packetforge: add packetforge for generic flow to extras
Add a new tool packetforge to extras. This tool is to support generic flow.
Packetforge is a library to translate naming or json profile format flow
pattern to the required input of generic flow, i.e. spec and mask. Using
python script flow_create.py, it can add and enable a new flow rule for
an interface via flow VAPI, and can delete an existed flow rule as well.
Command examples are shown below. Json profile examples can be found in
./parsegraph/samples.
Naming format input:
python flow_create.py --add -p "mac()/ipv4(src=1.1.1.1,dst=2.2.2.2)/udp()"
-a "redirect-to-queue 3" -i 1
python flow_create.py --del -i 1 -I 0
Json profile format input:
python flow_create.py -f "./flow_rule_examples/mac_ipv4.json" -i 1
With this command, flow rule can be added or deleted, and the flow
entry can be listed with "show flow entry" command in VPP CLI.
Packetforge is based on a parsegraph. The parsegraph can be built by
users. A Spec can be found in ./parsegraph as guidance. More details
about packetforge are in README file.
Type: feature
Signed-off-by: Ting Xu <ting.xu@intel.com>
Change-Id: Ia9f539741c5dca27ff236f2bcc493c5dd48c0df1
diff --git a/extras/packetforge/ParseGraph.py b/extras/packetforge/ParseGraph.py
new file mode 100644
index 0000000..188b073
--- /dev/null
+++ b/extras/packetforge/ParseGraph.py
@@ -0,0 +1,179 @@
+# Copyright (c) 2022 Intel and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ProtocolHeader import *
+from ForgeResult import *
+from Node import *
+from Edge import *
+import os
+
+
+class ParseGraph:
+ def __init__(self):
+ self.nodeDict = {}
+ self.edgeDict = {}
+
+ def Create(folder):
+ try:
+ pg = ParseGraph()
+ if not os.path.exists(folder):
+ print("folder not exisit")
+ return None
+
+ if os.path.exists(folder + "/nodes"):
+ pg.LoadNodesFromDirectory(folder + "/nodes")
+ if os.path.exists(folder + "/edges"):
+ pg.LoadEdgesFromDirectory(folder + "/edges")
+ except:
+ print("Failed to create Parse Graph")
+ return None
+ else:
+ return pg
+
+ def Nodes(self):
+ nodes = []
+ nodes.extend(self.nodeDict.values)
+ return nodes
+
+ def Edges(self):
+ edges = []
+ edges.extend(self.edgeDict.values)
+ return edges
+
+ def LoadNodesFromDirectory(self, folder):
+ for root, dirs, files in os.walk(folder):
+ for f in files:
+ self.LoadNodeFromFile(os.path.join(root, f))
+
+ def LoadEdgesFromDirectory(self, folder):
+ for root, dirs, files in os.walk(folder):
+ for f in files:
+ self.LoadEdgeFromFile(os.path.join(root, f))
+
+ def LoadNodeFromFile(self, file):
+ try:
+ node = Node.Create(file)
+
+ if node == None:
+ print("No node created")
+ return None
+
+ self.AddNode(node)
+ except:
+ print("Failed to create node from " + file)
+
+ def LoadEdgeFromFile(self, file):
+ try:
+ edges = Edge.Create(file)
+
+ if edges == None:
+ print("No edge created")
+ return None
+
+ for edge in edges:
+ self.AddEdge(edge)
+ except:
+ print("Failed to create edge from " + file)
+
+ def createProtocolHeader(self, name):
+ if name in self.nodeDict:
+ return ProtocolHeader(self.nodeDict[name])
+ return None
+
+ def GetNode(self, name):
+ if self.nodeDict.has_key(name):
+ return self.nodeDict[name]
+ return None
+
+ def GetEdge(self, start, end):
+ key = start + "-" + end
+ if key in self.edgeDict:
+ return self.edgeDict[key]
+ return None
+
+ def AddNode(self, node):
+ if node.Name in self.nodeDict:
+ print("Warning: node {0} already exist", node.Name)
+
+ self.nodeDict[node.Name] = node
+
+ def AddEdge(self, edge):
+ key = edge.Start + "-" + edge.End
+ if key in self.edgeDict:
+ print("Warning: edge {0} already exist", key)
+ self.edgeDict[key] = edge
+
+ def Forge(self, path):
+ headerList = []
+
+ # set field value/mask
+ for headerConfig in path.stack:
+ header = self.createProtocolHeader(headerConfig.Header)
+
+ if header == None:
+ return None
+
+ for hcf in headerConfig.fields:
+ attr = False
+ if not header.SetField(hcf.Name, hcf.Value):
+ if not header.SetAttribute(hcf.Name, hcf.Value):
+ print("failed to set value of " + hcf.Name)
+ return None
+ else:
+ attr = True
+
+ if not attr and not header.SetMask(hcf.Name, hcf.Mask):
+ print("failed to set mask of " + hcf.Name)
+ return None
+
+ header.Adjust()
+
+ headerList.append(header)
+
+ # apply edge actions and length autoincrease
+ for i in range(1, len(headerList)):
+ start = headerList[i - 1]
+ end = headerList[i]
+
+ edge = self.GetEdge(start.Name(), end.Name())
+
+ if edge == None:
+ print("no edge exist for {0}, {1}", start.Name, end.Name)
+ return None
+
+ edge.Apply(start, end)
+
+ increase = end.GetSize()
+ for j in range(i):
+ headerList[j].AppendAuto(increase)
+
+ # resolve buffer
+ pktLen = 0
+ for header in headerList:
+ header.Resolve()
+ pktLen += len(header.Buffer)
+
+ # join buffer
+ pktbuf = []
+ mskbuf = []
+
+ offset = 0
+ for header in headerList:
+ pktbuf.extend(header.Buffer)
+ mskbuf.extend(header.Mask)
+
+ offset += len(header.Buffer)
+
+ result = ForgeResult(headerList, pktbuf, mskbuf)
+
+ return result