diff options
author | Ting Xu <ting.xu@intel.com> | 2022-04-24 06:14:25 +0000 |
---|---|---|
committer | Dave Wallace <dwallacelf@gmail.com> | 2022-09-20 20:44:42 +0000 |
commit | ce4b6451787389c5b0ebfac413c350ef3a424b8b (patch) | |
tree | aa777368e14fca9b6613f747817331336ca2b11b /extras/packetforge/packetforge.py | |
parent | f5e0a17c9cca09822296a0ed3196fde36c1ca5f8 (diff) |
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
Diffstat (limited to 'extras/packetforge/packetforge.py')
-rw-r--r-- | extras/packetforge/packetforge.py | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/extras/packetforge/packetforge.py b/extras/packetforge/packetforge.py new file mode 100644 index 00000000000..6a419bc7c4f --- /dev/null +++ b/extras/packetforge/packetforge.py @@ -0,0 +1,200 @@ +# 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 vpp_papi.vpp_papi import VppEnum +from ParseGraph import * +from Path import * +import json +import re +import os + +parsegraph_path = os.getcwd() + "/parsegraph" + + +def Forge(pattern, actions, file_flag): + pg = ParseGraph.Create(parsegraph_path) + if pg == None: + print("error: create parsegraph failed") + return None + + if not file_flag: + token = ParsePattern(pattern) + if token == None: + return None + else: + if not os.path.exists(pattern): + print("error: file not exist '%s' " % (pattern)) + return + f = open(pattern, "r", encoding="utf-8") + token = json.load(f) + if "actions" in token: + actions = token["actions"] + + path = Path.Create(token) + if path == None: + print("error: path not exit") + return None + + result = pg.Forge(path) + if result == None: + print("error: result not available") + return None + + spec, mask = GetBinary(result.ToJSON()) + + # create generic flow + my_flow = { + "type": VppEnum.vl_api_flow_type_v2_t.FLOW_TYPE_GENERIC_V2, + "flow": { + "generic": { + "pattern": {"spec": bytes(spec.encode()), "mask": bytes(mask.encode())} + } + }, + } + + # update actions entry + my_flow = GetAction(actions, my_flow) + + return my_flow + + +def GetAction(actions, flow): + if len(actions.split(" ")) > 1: + type = actions.split(" ")[0] + else: + type = actions + + if type == "mark": + flow.update( + { + "actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_MARK_V2, + "mark_flow_id": int(actions.split(" ")[1]), + } + ) + elif type == "next-node": + flow.update( + { + "actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_REDIRECT_TO_NODE_V2, + "redirect_node_index": int(actions.split(" ")[1]), + } + ) + elif type == "buffer-advance": + flow.update( + { + "actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_BUFFER_ADVANCE_V2, + "buffer_advance": int(actions.split(" ")[1]), + } + ) + elif type == "redirect-to-queue": + flow.update( + { + "actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_REDIRECT_TO_QUEUE_V2, + "redirect_queue": int(actions.split(" ")[1]), + } + ) + elif type == "rss": + flow.update({"actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_RSS_V2}) + elif type == "rss-queues": + queue_end = int(actions.split(" ")[-1]) + queue_start = int(actions.split(" ")[-3]) + flow.update( + { + "actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_RSS_V2, + "queue_index": queue_start, + "queue_num": queue_end - queue_start + 1, + } + ) + elif type == "drop": + flow.update({"actions": VppEnum.vl_api_flow_action_v2_t.FLOW_ACTION_DROP_V2}) + + return flow + + +def GetBinary(flow_info): + spec = "".join(flow_info["Packet"]) + mask = "".join(flow_info["Mask"]) + return spec, mask + + +def ParseFields(item): + # get protocol name + prot = item.split("(")[0] + stack = {"header": prot} + # get fields contents + fields = re.findall(r"[(](.*?)[)]", item) + if not fields: + print("error: invalid pattern") + return None + if fields == [""]: + return stack + stack.update({"fields": []}) + return ParseStack(stack, fields[0].split(",")) + + +def GetMask(item): + if "format" in item: + format = item["format"] + if format == "mac": + mask = "ff.ff.ff.ff.ff.ff" + elif format == "ipv4": + mask = "255.255.255.255" + elif format == "ipv6": + mask = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" + return mask + if "size" in item: + mask = str((1 << int(item["size"])) - 1) + else: + print("mask error") + return mask + + +# parse protocol headers and its fields. Available fields are defined in corresponding nodes. +def ParseStack(stack, fields): + prot = stack["header"] + node_path = parsegraph_path + "/nodes/" + prot + ".json" + if not os.path.exists(node_path): + print("error file not exist '%s' " % (node_path)) + return None + f = open(node_path, "r", encoding="utf-8") + nodeinfo = json.load(f) + for field in fields: + fld_name = field.split("=")[0].strip() + fld_value = ( + field.split("=")[-1].strip() if (len(field.split("=")) >= 2) else None + ) + for item in nodeinfo["layout"]: + if fld_name == item["name"]: + mask = GetMask(item) + stack["fields"].append( + {"name": fld_name, "value": fld_value, "mask": mask} + ) + break + if not stack["fields"]: + print("warning: invalid field '%s'" % (fld_name)) + return None + + return stack + + +def ParsePattern(pattern): + # create json template + json_tmp = {"type": "path", "stack": []} + + items = pattern.split("/") + for item in items: + stack = ParseFields(item) + if stack == None: + return None + json_tmp["stack"].append(stack) + + return json_tmp |