# 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, show_result_only): 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 = { "flow": { "generic": { "pattern": {"spec": bytes(spec.encode()), "mask": bytes(mask.encode())} } }, } if show_result_only: return my_flow my_flow.update( { "type": VppEnum.vl_api_flow_type_v2_t.FLOW_TYPE_GENERIC_V2, } ) # 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