2776 lines · python
1#!/usr/bin/env python32# SPDX-License-Identifier: GPL-2.03 4# Controls the openvswitch module. Part of the kselftest suite, but5# can be used for some diagnostic purpose as well.6 7import argparse8import errno9import ipaddress10import logging11import math12import multiprocessing13import re14import socket15import struct16import sys17import time18import types19import uuid20 21try:22 from pyroute2 import NDB23 24 from pyroute2.netlink import NLA_F_NESTED25 from pyroute2.netlink import NLM_F_ACK26 from pyroute2.netlink import NLM_F_DUMP27 from pyroute2.netlink import NLM_F_REQUEST28 from pyroute2.netlink import genlmsg29 from pyroute2.netlink import nla30 from pyroute2.netlink import nlmsg_atoms31 from pyroute2.netlink.event import EventSocket32 from pyroute2.netlink.exceptions import NetlinkError33 from pyroute2.netlink.generic import GenericNetlinkSocket34 from pyroute2.netlink.nlsocket import Marshal35 import pyroute236 import pyroute2.iproute37 38except ModuleNotFoundError:39 print("Need to install the python pyroute2 package >= 0.6.")40 sys.exit(1)41 42 43OVS_DATAPATH_FAMILY = "ovs_datapath"44OVS_VPORT_FAMILY = "ovs_vport"45OVS_FLOW_FAMILY = "ovs_flow"46OVS_PACKET_FAMILY = "ovs_packet"47OVS_METER_FAMILY = "ovs_meter"48OVS_CT_LIMIT_FAMILY = "ovs_ct_limit"49 50OVS_DATAPATH_VERSION = 251OVS_DP_CMD_NEW = 152OVS_DP_CMD_DEL = 253OVS_DP_CMD_GET = 354OVS_DP_CMD_SET = 455 56OVS_VPORT_CMD_NEW = 157OVS_VPORT_CMD_DEL = 258OVS_VPORT_CMD_GET = 359OVS_VPORT_CMD_SET = 460 61OVS_FLOW_CMD_NEW = 162OVS_FLOW_CMD_DEL = 263OVS_FLOW_CMD_GET = 364OVS_FLOW_CMD_SET = 465 66UINT32_MAX = 0xFFFFFFFF67 68def macstr(mac):69 outstr = ":".join(["%02X" % i for i in mac])70 return outstr71 72 73def strcspn(str1, str2):74 tot = 075 for char in str1:76 if str2.find(char) != -1:77 return tot78 tot += 179 return tot80 81 82def strspn(str1, str2):83 tot = 084 for char in str1:85 if str2.find(char) == -1:86 return tot87 tot += 188 return tot89 90 91def intparse(statestr, defmask="0xffffffff"):92 totalparse = strspn(statestr, "0123456789abcdefABCDEFx/")93 # scan until "/"94 count = strspn(statestr, "x0123456789abcdefABCDEF")95 96 firstnum = statestr[:count]97 if firstnum[-1] == "/":98 firstnum = firstnum[:-1]99 k = int(firstnum, 0)100 101 m = None102 if defmask is not None:103 secondnum = defmask104 if statestr[count] == "/":105 secondnum = statestr[count + 1 :] # this is wrong...106 m = int(secondnum, 0)107 108 return statestr[totalparse + 1 :], k, m109 110 111def parse_flags(flag_str, flag_vals):112 bitResult = 0113 maskResult = 0114 115 if len(flag_str) == 0:116 return flag_str, bitResult, maskResult117 118 if flag_str[0].isdigit():119 idx = 0120 while flag_str[idx].isdigit() or flag_str[idx] == "x":121 idx += 1122 digits = flag_str[:idx]123 flag_str = flag_str[idx:]124 125 bitResult = int(digits, 0)126 maskResult = int(digits, 0)127 128 while len(flag_str) > 0 and (flag_str[0] == "+" or flag_str[0] == "-"):129 if flag_str[0] == "+":130 setFlag = True131 elif flag_str[0] == "-":132 setFlag = False133 134 flag_str = flag_str[1:]135 136 flag_len = 0137 while (138 flag_str[flag_len] != "+"139 and flag_str[flag_len] != "-"140 and flag_str[flag_len] != ","141 and flag_str[flag_len] != ")"142 ):143 flag_len += 1144 145 flag = flag_str[0:flag_len]146 147 if flag in flag_vals:148 if maskResult & flag_vals[flag]:149 raise KeyError(150 "Flag %s set once, cannot be set in multiples" % flag151 )152 153 if setFlag:154 bitResult |= flag_vals[flag]155 156 maskResult |= flag_vals[flag]157 else:158 raise KeyError("Missing flag value: %s" % flag)159 160 flag_str = flag_str[flag_len:]161 162 return flag_str, bitResult, maskResult163 164 165def parse_ct_state(statestr):166 ct_flags = {167 "new": 1 << 0,168 "est": 1 << 1,169 "rel": 1 << 2,170 "rpl": 1 << 3,171 "inv": 1 << 4,172 "trk": 1 << 5,173 "snat": 1 << 6,174 "dnat": 1 << 7,175 }176 177 return parse_flags(statestr, ct_flags)178 179 180def convert_mac(data):181 def to_bytes(mac):182 mac_split = mac.split(":")183 ret = bytearray([int(i, 16) for i in mac_split])184 return bytes(ret)185 186 mac_str, _, mask_str = data.partition('/')187 188 if not mac_str:189 mac_str = mask_str = "00:00:00:00:00:00"190 elif not mask_str:191 mask_str = "FF:FF:FF:FF:FF:FF"192 193 return to_bytes(mac_str), to_bytes(mask_str)194 195def convert_ipv4(data):196 ip, _, mask = data.partition('/')197 198 if not ip:199 ip = mask = 0200 elif not mask:201 mask = 0xFFFFFFFF202 elif mask.isdigit():203 mask = (0xFFFFFFFF << (32 - int(mask))) & 0xFFFFFFFF204 205 return int(ipaddress.IPv4Address(ip)), int(ipaddress.IPv4Address(mask))206 207def convert_ipv6(data):208 ip, _, mask = data.partition('/')209 210 if not ip:211 ip = mask = 0212 elif not mask:213 mask = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'214 elif mask.isdigit():215 mask = ipaddress.IPv6Network("::/" + mask).hostmask216 217 return ipaddress.IPv6Address(ip).packed, ipaddress.IPv6Address(mask).packed218 219def convert_int(size):220 def convert_int_sized(data):221 value, _, mask = data.partition('/')222 223 if not value:224 return 0, 0225 elif not mask:226 return int(value, 0), pow(2, size) - 1227 else:228 return int(value, 0), int(mask, 0)229 230 return convert_int_sized231 232def parse_starts_block(block_str, scanstr, returnskipped, scanregex=False):233 if scanregex:234 m = re.search(scanstr, block_str)235 if m is None:236 if returnskipped:237 return block_str238 return False239 if returnskipped:240 block_str = block_str[len(m.group(0)) :]241 return block_str242 return True243 244 if block_str.startswith(scanstr):245 if returnskipped:246 block_str = block_str[len(scanstr) :]247 else:248 return True249 250 if returnskipped:251 return block_str252 253 return False254 255 256def parse_extract_field(257 block_str, fieldstr, scanfmt, convert, masked=False, defval=None258):259 if fieldstr and not block_str.startswith(fieldstr):260 return block_str, defval261 262 if fieldstr:263 str_skiplen = len(fieldstr)264 str_skipped = block_str[str_skiplen:]265 if str_skiplen == 0:266 return str_skipped, defval267 else:268 str_skiplen = 0269 str_skipped = block_str270 271 m = re.search(scanfmt, str_skipped)272 if m is None:273 raise ValueError("Bad fmt string")274 275 data = m.group(0)276 if convert:277 data = convert(m.group(0))278 279 str_skipped = str_skipped[len(m.group(0)) :]280 if masked:281 if str_skipped[0] == "/":282 raise ValueError("Masking support TBD...")283 284 str_skipped = str_skipped[strspn(str_skipped, ", ") :]285 return str_skipped, data286 287 288def parse_attrs(actstr, attr_desc):289 """Parses the given action string and returns a list of netlink290 attributes based on a list of attribute descriptions.291 292 Each element in the attribute description list is a tuple such as:293 (name, attr_name, parse_func)294 where:295 name: is the string representing the attribute296 attr_name: is the name of the attribute as defined in the uAPI.297 parse_func: is a callable accepting a string and returning either298 a single object (the parsed attribute value) or a tuple of299 two values (the parsed attribute value and the remaining string)300 301 Returns a list of attributes and the remaining string.302 """303 def parse_attr(actstr, key, func):304 actstr = actstr[len(key) :]305 306 if not func:307 return None, actstr308 309 delim = actstr[0]310 actstr = actstr[1:]311 312 if delim == "=":313 pos = strcspn(actstr, ",)")314 ret = func(actstr[:pos])315 else:316 ret = func(actstr)317 318 if isinstance(ret, tuple):319 (datum, actstr) = ret320 else:321 datum = ret322 actstr = actstr[strcspn(actstr, ",)"):]323 324 if delim == "(":325 if not actstr or actstr[0] != ")":326 raise ValueError("Action contains unbalanced parentheses")327 328 actstr = actstr[1:]329 330 actstr = actstr[strspn(actstr, ", ") :]331 332 return datum, actstr333 334 attrs = []335 attr_desc = list(attr_desc)336 while actstr and actstr[0] != ")" and attr_desc:337 found = False338 for i, (key, attr, func) in enumerate(attr_desc):339 if actstr.startswith(key):340 datum, actstr = parse_attr(actstr, key, func)341 attrs.append([attr, datum])342 found = True343 del attr_desc[i]344 345 if not found:346 raise ValueError("Unknown attribute: '%s'" % actstr)347 348 actstr = actstr[strspn(actstr, ", ") :]349 350 if actstr[0] != ")":351 raise ValueError("Action string contains extra garbage or has "352 "unbalanced parenthesis: '%s'" % actstr)353 354 return attrs, actstr[1:]355 356 357class ovs_dp_msg(genlmsg):358 # include the OVS version359 # We need a custom header rather than just being able to rely on360 # genlmsg because fields ends up not expressing everything correctly361 # if we use the canonical example of setting fields = (('customfield',),)362 fields = genlmsg.fields + (("dpifindex", "I"),)363 364 365class ovsactions(nla):366 nla_flags = NLA_F_NESTED367 368 nla_map = (369 ("OVS_ACTION_ATTR_UNSPEC", "none"),370 ("OVS_ACTION_ATTR_OUTPUT", "uint32"),371 ("OVS_ACTION_ATTR_USERSPACE", "userspace"),372 ("OVS_ACTION_ATTR_SET", "ovskey"),373 ("OVS_ACTION_ATTR_PUSH_VLAN", "none"),374 ("OVS_ACTION_ATTR_POP_VLAN", "flag"),375 ("OVS_ACTION_ATTR_SAMPLE", "sample"),376 ("OVS_ACTION_ATTR_RECIRC", "uint32"),377 ("OVS_ACTION_ATTR_HASH", "none"),378 ("OVS_ACTION_ATTR_PUSH_MPLS", "none"),379 ("OVS_ACTION_ATTR_POP_MPLS", "flag"),380 ("OVS_ACTION_ATTR_SET_MASKED", "ovskey"),381 ("OVS_ACTION_ATTR_CT", "ctact"),382 ("OVS_ACTION_ATTR_TRUNC", "uint32"),383 ("OVS_ACTION_ATTR_PUSH_ETH", "none"),384 ("OVS_ACTION_ATTR_POP_ETH", "flag"),385 ("OVS_ACTION_ATTR_CT_CLEAR", "flag"),386 ("OVS_ACTION_ATTR_PUSH_NSH", "none"),387 ("OVS_ACTION_ATTR_POP_NSH", "flag"),388 ("OVS_ACTION_ATTR_METER", "none"),389 ("OVS_ACTION_ATTR_CLONE", "recursive"),390 ("OVS_ACTION_ATTR_CHECK_PKT_LEN", "none"),391 ("OVS_ACTION_ATTR_ADD_MPLS", "none"),392 ("OVS_ACTION_ATTR_DEC_TTL", "none"),393 ("OVS_ACTION_ATTR_DROP", "uint32"),394 ("OVS_ACTION_ATTR_PSAMPLE", "psample"),395 )396 397 class psample(nla):398 nla_flags = NLA_F_NESTED399 400 nla_map = (401 ("OVS_PSAMPLE_ATTR_UNSPEC", "none"),402 ("OVS_PSAMPLE_ATTR_GROUP", "uint32"),403 ("OVS_PSAMPLE_ATTR_COOKIE", "array(uint8)"),404 )405 406 def dpstr(self, more=False):407 args = "group=%d" % self.get_attr("OVS_PSAMPLE_ATTR_GROUP")408 409 cookie = self.get_attr("OVS_PSAMPLE_ATTR_COOKIE")410 if cookie:411 args += ",cookie(%s)" % \412 "".join(format(x, "02x") for x in cookie)413 414 return "psample(%s)" % args415 416 def parse(self, actstr):417 desc = (418 ("group", "OVS_PSAMPLE_ATTR_GROUP", int),419 ("cookie", "OVS_PSAMPLE_ATTR_COOKIE",420 lambda x: list(bytearray.fromhex(x)))421 )422 423 attrs, actstr = parse_attrs(actstr, desc)424 425 for attr in attrs:426 self["attrs"].append(attr)427 428 return actstr429 430 class sample(nla):431 nla_flags = NLA_F_NESTED432 433 nla_map = (434 ("OVS_SAMPLE_ATTR_UNSPEC", "none"),435 ("OVS_SAMPLE_ATTR_PROBABILITY", "uint32"),436 ("OVS_SAMPLE_ATTR_ACTIONS", "ovsactions"),437 )438 439 def dpstr(self, more=False):440 args = []441 442 args.append("sample={:.2f}%".format(443 100 * self.get_attr("OVS_SAMPLE_ATTR_PROBABILITY") /444 UINT32_MAX))445 446 actions = self.get_attr("OVS_SAMPLE_ATTR_ACTIONS")447 if actions:448 args.append("actions(%s)" % actions.dpstr(more))449 450 return "sample(%s)" % ",".join(args)451 452 def parse(self, actstr):453 def parse_nested_actions(actstr):454 subacts = ovsactions()455 parsed_len = subacts.parse(actstr)456 return subacts, actstr[parsed_len :]457 458 def percent_to_rate(percent):459 percent = float(percent.strip('%'))460 return int(math.floor(UINT32_MAX * (percent / 100.0) + .5))461 462 desc = (463 ("sample", "OVS_SAMPLE_ATTR_PROBABILITY", percent_to_rate),464 ("actions", "OVS_SAMPLE_ATTR_ACTIONS", parse_nested_actions),465 )466 attrs, actstr = parse_attrs(actstr, desc)467 468 for attr in attrs:469 self["attrs"].append(attr)470 471 return actstr472 473 class ctact(nla):474 nla_flags = NLA_F_NESTED475 476 nla_map = (477 ("OVS_CT_ATTR_NONE", "none"),478 ("OVS_CT_ATTR_COMMIT", "flag"),479 ("OVS_CT_ATTR_ZONE", "uint16"),480 ("OVS_CT_ATTR_MARK", "none"),481 ("OVS_CT_ATTR_LABELS", "none"),482 ("OVS_CT_ATTR_HELPER", "asciiz"),483 ("OVS_CT_ATTR_NAT", "natattr"),484 ("OVS_CT_ATTR_FORCE_COMMIT", "flag"),485 ("OVS_CT_ATTR_EVENTMASK", "uint32"),486 ("OVS_CT_ATTR_TIMEOUT", "asciiz"),487 )488 489 class natattr(nla):490 nla_flags = NLA_F_NESTED491 492 nla_map = (493 ("OVS_NAT_ATTR_NONE", "none"),494 ("OVS_NAT_ATTR_SRC", "flag"),495 ("OVS_NAT_ATTR_DST", "flag"),496 ("OVS_NAT_ATTR_IP_MIN", "ipaddr"),497 ("OVS_NAT_ATTR_IP_MAX", "ipaddr"),498 ("OVS_NAT_ATTR_PROTO_MIN", "uint16"),499 ("OVS_NAT_ATTR_PROTO_MAX", "uint16"),500 ("OVS_NAT_ATTR_PERSISTENT", "flag"),501 ("OVS_NAT_ATTR_PROTO_HASH", "flag"),502 ("OVS_NAT_ATTR_PROTO_RANDOM", "flag"),503 )504 505 def dpstr(self, more=False):506 print_str = "nat("507 508 if self.get_attr("OVS_NAT_ATTR_SRC"):509 print_str += "src"510 elif self.get_attr("OVS_NAT_ATTR_DST"):511 print_str += "dst"512 else:513 print_str += "XXX-unknown-nat"514 515 if self.get_attr("OVS_NAT_ATTR_IP_MIN") or self.get_attr(516 "OVS_NAT_ATTR_IP_MAX"517 ):518 if self.get_attr("OVS_NAT_ATTR_IP_MIN"):519 print_str += "=%s," % str(520 self.get_attr("OVS_NAT_ATTR_IP_MIN")521 )522 523 if self.get_attr("OVS_NAT_ATTR_IP_MAX"):524 print_str += "-%s," % str(525 self.get_attr("OVS_NAT_ATTR_IP_MAX")526 )527 else:528 print_str += ","529 530 if self.get_attr("OVS_NAT_ATTR_PROTO_MIN"):531 print_str += "proto_min=%d," % self.get_attr(532 "OVS_NAT_ATTR_PROTO_MIN"533 )534 535 if self.get_attr("OVS_NAT_ATTR_PROTO_MAX"):536 print_str += "proto_max=%d," % self.get_attr(537 "OVS_NAT_ATTR_PROTO_MAX"538 )539 540 if self.get_attr("OVS_NAT_ATTR_PERSISTENT"):541 print_str += "persistent,"542 if self.get_attr("OVS_NAT_ATTR_HASH"):543 print_str += "hash,"544 if self.get_attr("OVS_NAT_ATTR_RANDOM"):545 print_str += "random"546 print_str += ")"547 return print_str548 549 def dpstr(self, more=False):550 print_str = "ct("551 552 if self.get_attr("OVS_CT_ATTR_COMMIT") is not None:553 print_str += "commit,"554 if self.get_attr("OVS_CT_ATTR_ZONE") is not None:555 print_str += "zone=%d," % self.get_attr("OVS_CT_ATTR_ZONE")556 if self.get_attr("OVS_CT_ATTR_HELPER") is not None:557 print_str += "helper=%s," % self.get_attr("OVS_CT_ATTR_HELPER")558 if self.get_attr("OVS_CT_ATTR_NAT") is not None:559 print_str += self.get_attr("OVS_CT_ATTR_NAT").dpstr(more)560 print_str += ","561 if self.get_attr("OVS_CT_ATTR_FORCE_COMMIT") is not None:562 print_str += "force,"563 if self.get_attr("OVS_CT_ATTR_EVENTMASK") is not None:564 print_str += "emask=0x%X," % self.get_attr(565 "OVS_CT_ATTR_EVENTMASK"566 )567 if self.get_attr("OVS_CT_ATTR_TIMEOUT") is not None:568 print_str += "timeout=%s" % self.get_attr(569 "OVS_CT_ATTR_TIMEOUT"570 )571 print_str += ")"572 return print_str573 574 class userspace(nla):575 nla_flags = NLA_F_NESTED576 577 nla_map = (578 ("OVS_USERSPACE_ATTR_UNUSED", "none"),579 ("OVS_USERSPACE_ATTR_PID", "uint32"),580 ("OVS_USERSPACE_ATTR_USERDATA", "array(uint8)"),581 ("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", "uint32"),582 )583 584 def dpstr(self, more=False):585 print_str = "userspace("586 if self.get_attr("OVS_USERSPACE_ATTR_PID") is not None:587 print_str += "pid=%d," % self.get_attr(588 "OVS_USERSPACE_ATTR_PID"589 )590 if self.get_attr("OVS_USERSPACE_ATTR_USERDATA") is not None:591 print_str += "userdata="592 for f in self.get_attr("OVS_USERSPACE_ATTR_USERDATA"):593 print_str += "%x." % f594 if self.get_attr("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT") is not None:595 print_str += "egress_tun_port=%d" % self.get_attr(596 "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT"597 )598 print_str += ")"599 return print_str600 601 def parse(self, actstr):602 attrs_desc = (603 ("pid", "OVS_USERSPACE_ATTR_PID", int),604 ("userdata", "OVS_USERSPACE_ATTR_USERDATA",605 lambda x: list(bytearray.fromhex(x))),606 ("egress_tun_port", "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", int)607 )608 609 attrs, actstr = parse_attrs(actstr, attrs_desc)610 for attr in attrs:611 self["attrs"].append(attr)612 613 return actstr614 615 def dpstr(self, more=False):616 print_str = ""617 618 for field in self["attrs"]:619 if field[1] == "none" or self.get_attr(field[0]) is None:620 continue621 if print_str != "":622 print_str += ","623 624 if field[0] == "OVS_ACTION_ATTR_OUTPUT":625 print_str += "%d" % int(self.get_attr(field[0]))626 elif field[0] == "OVS_ACTION_ATTR_RECIRC":627 print_str += "recirc(0x%x)" % int(self.get_attr(field[0]))628 elif field[0] == "OVS_ACTION_ATTR_TRUNC":629 print_str += "trunc(%d)" % int(self.get_attr(field[0]))630 elif field[0] == "OVS_ACTION_ATTR_DROP":631 print_str += "drop(%d)" % int(self.get_attr(field[0]))632 elif field[0] == "OVS_ACTION_ATTR_CT_CLEAR":633 print_str += "ct_clear"634 elif field[0] == "OVS_ACTION_ATTR_POP_VLAN":635 print_str += "pop_vlan"636 elif field[0] == "OVS_ACTION_ATTR_POP_ETH":637 print_str += "pop_eth"638 elif field[0] == "OVS_ACTION_ATTR_POP_NSH":639 print_str += "pop_nsh"640 elif field[0] == "OVS_ACTION_ATTR_POP_MPLS":641 print_str += "pop_mpls"642 else:643 datum = self.get_attr(field[0])644 if field[0] == "OVS_ACTION_ATTR_CLONE":645 print_str += "clone("646 print_str += datum.dpstr(more)647 print_str += ")"648 elif field[0] == "OVS_ACTION_ATTR_SET" or \649 field[0] == "OVS_ACTION_ATTR_SET_MASKED":650 print_str += "set"651 field = datum652 mask = None653 if field[0] == "OVS_ACTION_ATTR_SET_MASKED":654 print_str += "_masked"655 field = datum[0]656 mask = datum[1]657 print_str += "("658 print_str += field.dpstr(mask, more)659 print_str += ")"660 else:661 try:662 print_str += datum.dpstr(more)663 except:664 print_str += "{ATTR: %s not decoded}" % field[0]665 666 return print_str667 668 def parse(self, actstr):669 totallen = len(actstr)670 while len(actstr) != 0:671 parsed = False672 parencount = 0673 if actstr.startswith("drop"):674 # If no reason is provided, the implicit drop is used (i.e no675 # action). If some reason is given, an explicit action is used.676 reason = None677 if actstr.startswith("drop("):678 parencount += 1679 680 actstr, reason = parse_extract_field(681 actstr,682 "drop(",683 r"([0-9]+)",684 lambda x: int(x, 0),685 False,686 None,687 )688 689 if reason is not None:690 self["attrs"].append(["OVS_ACTION_ATTR_DROP", reason])691 parsed = True692 else:693 actstr = actstr[len("drop"): ]694 return (totallen - len(actstr))695 696 elif parse_starts_block(actstr, r"^(\d+)", False, True):697 actstr, output = parse_extract_field(698 actstr, None, r"(\d+)", lambda x: int(x), False, "0"699 )700 self["attrs"].append(["OVS_ACTION_ATTR_OUTPUT", output])701 parsed = True702 elif parse_starts_block(actstr, "recirc(", False):703 actstr, recircid = parse_extract_field(704 actstr,705 "recirc(",706 r"([0-9a-fA-Fx]+)",707 lambda x: int(x, 0),708 False,709 0,710 )711 parencount += 1712 self["attrs"].append(["OVS_ACTION_ATTR_RECIRC", recircid])713 parsed = True714 715 parse_flat_map = (716 ("ct_clear", "OVS_ACTION_ATTR_CT_CLEAR"),717 ("pop_vlan", "OVS_ACTION_ATTR_POP_VLAN"),718 ("pop_eth", "OVS_ACTION_ATTR_POP_ETH"),719 ("pop_nsh", "OVS_ACTION_ATTR_POP_NSH"),720 )721 722 for flat_act in parse_flat_map:723 if parse_starts_block(actstr, flat_act[0], False):724 actstr = actstr[len(flat_act[0]):]725 self["attrs"].append([flat_act[1], True])726 actstr = actstr[strspn(actstr, ", ") :]727 parsed = True728 729 if parse_starts_block(actstr, "clone(", False):730 parencount += 1731 subacts = ovsactions()732 actstr = actstr[len("clone("):]733 parsedLen = subacts.parse(actstr)734 lst = []735 self["attrs"].append(("OVS_ACTION_ATTR_CLONE", subacts))736 actstr = actstr[parsedLen:]737 parsed = True738 elif parse_starts_block(actstr, "set(", False):739 parencount += 1740 k = ovskey()741 actstr = actstr[len("set("):]742 actstr = k.parse(actstr, None)743 self["attrs"].append(("OVS_ACTION_ATTR_SET", k))744 if not actstr.startswith(")"):745 actstr = ")" + actstr746 parsed = True747 elif parse_starts_block(actstr, "set_masked(", False):748 parencount += 1749 k = ovskey()750 m = ovskey()751 actstr = actstr[len("set_masked("):]752 actstr = k.parse(actstr, m)753 self["attrs"].append(("OVS_ACTION_ATTR_SET_MASKED", [k, m]))754 if not actstr.startswith(")"):755 actstr = ")" + actstr756 parsed = True757 elif parse_starts_block(actstr, "ct(", False):758 parencount += 1759 actstr = actstr[len("ct(") :]760 ctact = ovsactions.ctact()761 762 for scan in (763 ("commit", "OVS_CT_ATTR_COMMIT", None),764 ("force_commit", "OVS_CT_ATTR_FORCE_COMMIT", None),765 ("zone", "OVS_CT_ATTR_ZONE", int),766 ("mark", "OVS_CT_ATTR_MARK", int),767 ("helper", "OVS_CT_ATTR_HELPER", lambda x, y: str(x)),768 ("timeout", "OVS_CT_ATTR_TIMEOUT", lambda x, y: str(x)),769 ):770 if actstr.startswith(scan[0]):771 actstr = actstr[len(scan[0]) :]772 if scan[2] is not None:773 if actstr[0] != "=":774 raise ValueError("Invalid ct attr")775 actstr = actstr[1:]776 pos = strcspn(actstr, ",)")777 datum = scan[2](actstr[:pos], 0)778 ctact["attrs"].append([scan[1], datum])779 actstr = actstr[pos:]780 else:781 ctact["attrs"].append([scan[1], None])782 actstr = actstr[strspn(actstr, ", ") :]783 # it seems strange to put this here, but nat() is a complex784 # sub-action and this lets it sit anywhere in the ct() action785 if actstr.startswith("nat"):786 actstr = actstr[3:]787 natact = ovsactions.ctact.natattr()788 789 if actstr.startswith("("):790 parencount += 1791 t = None792 actstr = actstr[1:]793 if actstr.startswith("src"):794 t = "OVS_NAT_ATTR_SRC"795 actstr = actstr[3:]796 elif actstr.startswith("dst"):797 t = "OVS_NAT_ATTR_DST"798 actstr = actstr[3:]799 800 actstr, ip_block_min = parse_extract_field(801 actstr, "=", r"([0-9a-fA-F\.]+)", str, False802 )803 actstr, ip_block_max = parse_extract_field(804 actstr, "-", r"([0-9a-fA-F\.]+)", str, False805 )806 807 actstr, proto_min = parse_extract_field(808 actstr, ":", r"(\d+)", int, False809 )810 actstr, proto_max = parse_extract_field(811 actstr, "-", r"(\d+)", int, False812 )813 814 if t is not None:815 natact["attrs"].append([t, None])816 817 if ip_block_min is not None:818 natact["attrs"].append(819 ["OVS_NAT_ATTR_IP_MIN", ip_block_min]820 )821 if ip_block_max is not None:822 natact["attrs"].append(823 ["OVS_NAT_ATTR_IP_MAX", ip_block_max]824 )825 if proto_min is not None:826 natact["attrs"].append(827 ["OVS_NAT_ATTR_PROTO_MIN", proto_min]828 )829 if proto_max is not None:830 natact["attrs"].append(831 ["OVS_NAT_ATTR_PROTO_MAX", proto_max]832 )833 834 for natscan in (835 ("persistent", "OVS_NAT_ATTR_PERSISTENT"),836 ("hash", "OVS_NAT_ATTR_PROTO_HASH"),837 ("random", "OVS_NAT_ATTR_PROTO_RANDOM"),838 ):839 if actstr.startswith(natscan[0]):840 actstr = actstr[len(natscan[0]) :]841 natact["attrs"].append([natscan[1], None])842 actstr = actstr[strspn(actstr, ", ") :]843 844 ctact["attrs"].append(["OVS_CT_ATTR_NAT", natact])845 actstr = actstr[strspn(actstr, ", ") :]846 847 self["attrs"].append(["OVS_ACTION_ATTR_CT", ctact])848 parsed = True849 850 elif parse_starts_block(actstr, "sample(", False):851 sampleact = self.sample()852 actstr = sampleact.parse(actstr[len("sample(") : ])853 self["attrs"].append(["OVS_ACTION_ATTR_SAMPLE", sampleact])854 parsed = True855 856 elif parse_starts_block(actstr, "psample(", False):857 psampleact = self.psample()858 actstr = psampleact.parse(actstr[len("psample(") : ])859 self["attrs"].append(["OVS_ACTION_ATTR_PSAMPLE", psampleact])860 parsed = True861 862 elif parse_starts_block(actstr, "userspace(", False):863 uact = self.userspace()864 actstr = uact.parse(actstr[len("userspace(") : ])865 self["attrs"].append(["OVS_ACTION_ATTR_USERSPACE", uact])866 parsed = True867 868 elif parse_starts_block(actstr, "trunc(", False):869 parencount += 1870 actstr, val = parse_extract_field(871 actstr,872 "trunc(",873 r"([0-9]+)",874 int,875 False,876 None,877 )878 self["attrs"].append(["OVS_ACTION_ATTR_TRUNC", val])879 parsed = True880 881 actstr = actstr[strspn(actstr, ", ") :]882 while parencount > 0:883 parencount -= 1884 actstr = actstr[strspn(actstr, " "):]885 if len(actstr) and actstr[0] != ")":886 raise ValueError("Action str: '%s' unbalanced" % actstr)887 actstr = actstr[1:]888 889 if len(actstr) and actstr[0] == ")":890 return (totallen - len(actstr))891 892 actstr = actstr[strspn(actstr, ", ") :]893 894 if not parsed:895 raise ValueError("Action str: '%s' not supported" % actstr)896 897 return (totallen - len(actstr))898 899 900class ovskey(nla):901 nla_flags = NLA_F_NESTED902 nla_map = (903 ("OVS_KEY_ATTR_UNSPEC", "none"),904 ("OVS_KEY_ATTR_ENCAP", "none"),905 ("OVS_KEY_ATTR_PRIORITY", "uint32"),906 ("OVS_KEY_ATTR_IN_PORT", "uint32"),907 ("OVS_KEY_ATTR_ETHERNET", "ethaddr"),908 ("OVS_KEY_ATTR_VLAN", "uint16"),909 ("OVS_KEY_ATTR_ETHERTYPE", "be16"),910 ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"),911 ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"),912 ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"),913 ("OVS_KEY_ATTR_UDP", "ovs_key_udp"),914 ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"),915 ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"),916 ("OVS_KEY_ATTR_ARP", "ovs_key_arp"),917 ("OVS_KEY_ATTR_ND", "ovs_key_nd"),918 ("OVS_KEY_ATTR_SKB_MARK", "uint32"),919 ("OVS_KEY_ATTR_TUNNEL", "ovs_key_tunnel"),920 ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"),921 ("OVS_KEY_ATTR_TCP_FLAGS", "be16"),922 ("OVS_KEY_ATTR_DP_HASH", "uint32"),923 ("OVS_KEY_ATTR_RECIRC_ID", "uint32"),924 ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"),925 ("OVS_KEY_ATTR_CT_STATE", "uint32"),926 ("OVS_KEY_ATTR_CT_ZONE", "uint16"),927 ("OVS_KEY_ATTR_CT_MARK", "uint32"),928 ("OVS_KEY_ATTR_CT_LABELS", "none"),929 ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", "ovs_key_ct_tuple_ipv4"),930 ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", "ovs_key_ct_tuple_ipv6"),931 ("OVS_KEY_ATTR_NSH", "none"),932 ("OVS_KEY_ATTR_PACKET_TYPE", "none"),933 ("OVS_KEY_ATTR_ND_EXTENSIONS", "none"),934 ("OVS_KEY_ATTR_TUNNEL_INFO", "none"),935 ("OVS_KEY_ATTR_IPV6_EXTENSIONS", "none"),936 )937 938 class ovs_key_proto(nla):939 fields = (940 ("src", "!H"),941 ("dst", "!H"),942 )943 944 fields_map = (945 ("src", "src", "%d", lambda x: int(x) if x else 0,946 convert_int(16)),947 ("dst", "dst", "%d", lambda x: int(x) if x else 0,948 convert_int(16)),949 )950 951 def __init__(952 self,953 protostr,954 data=None,955 offset=None,956 parent=None,957 length=None,958 init=None,959 ):960 self.proto_str = protostr961 nla.__init__(962 self,963 data=data,964 offset=offset,965 parent=parent,966 length=length,967 init=init,968 )969 970 def parse(self, flowstr, typeInst):971 if not flowstr.startswith(self.proto_str):972 return None, None973 974 k = typeInst()975 m = typeInst()976 977 flowstr = flowstr[len(self.proto_str) :]978 if flowstr.startswith("("):979 flowstr = flowstr[1:]980 981 keybits = b""982 maskbits = b""983 for f in self.fields_map:984 if flowstr.startswith(f[1]):985 # the following assumes that the field looks986 # something like 'field.' where '.' is a987 # character that we don't exactly care about.988 flowstr = flowstr[len(f[1]) + 1 :]989 splitchar = 0990 for c in flowstr:991 if c == "," or c == ")":992 break993 splitchar += 1994 data = flowstr[:splitchar]995 flowstr = flowstr[splitchar:]996 else:997 data = ""998 999 if len(f) > 4:1000 k[f[0]], m[f[0]] = f[4](data)1001 else:1002 k[f[0]] = f[3](data)1003 m[f[0]] = f[3](data)1004 1005 flowstr = flowstr[strspn(flowstr, ", ") :]1006 if len(flowstr) == 0:1007 return flowstr, k, m1008 1009 flowstr = flowstr[strspn(flowstr, "), ") :]1010 1011 return flowstr, k, m1012 1013 def dpstr(self, masked=None, more=False):1014 outstr = self.proto_str + "("1015 first = False1016 for f in self.fields_map:1017 if first:1018 outstr += ","1019 if masked is None:1020 outstr += "%s=" % f[0]1021 if isinstance(f[2], str):1022 outstr += f[2] % self[f[1]]1023 else:1024 outstr += f[2](self[f[1]])1025 first = True1026 elif more or f[3](masked[f[1]]) != 0:1027 outstr += "%s=" % f[0]1028 if isinstance(f[2], str):1029 outstr += f[2] % self[f[1]]1030 else:1031 outstr += f[2](self[f[1]])1032 outstr += "/"1033 if isinstance(f[2], str):1034 outstr += f[2] % masked[f[1]]1035 else:1036 outstr += f[2](masked[f[1]])1037 first = True1038 outstr += ")"1039 return outstr1040 1041 class ethaddr(ovs_key_proto):1042 fields = (1043 ("src", "!6s"),1044 ("dst", "!6s"),1045 )1046 1047 fields_map = (1048 (1049 "src",1050 "src",1051 macstr,1052 lambda x: int.from_bytes(x, "big"),1053 convert_mac,1054 ),1055 (1056 "dst",1057 "dst",1058 macstr,1059 lambda x: int.from_bytes(x, "big"),1060 convert_mac,1061 ),1062 )1063 1064 def __init__(1065 self,1066 data=None,1067 offset=None,1068 parent=None,1069 length=None,1070 init=None,1071 ):1072 ovskey.ovs_key_proto.__init__(1073 self,1074 "eth",1075 data=data,1076 offset=offset,1077 parent=parent,1078 length=length,1079 init=init,1080 )1081 1082 class ovs_key_ipv4(ovs_key_proto):1083 fields = (1084 ("src", "!I"),1085 ("dst", "!I"),1086 ("proto", "B"),1087 ("tos", "B"),1088 ("ttl", "B"),1089 ("frag", "B"),1090 )1091 1092 fields_map = (1093 (1094 "src",1095 "src",1096 lambda x: str(ipaddress.IPv4Address(x)),1097 int,1098 convert_ipv4,1099 ),1100 (1101 "dst",1102 "dst",1103 lambda x: str(ipaddress.IPv4Address(x)),1104 int,1105 convert_ipv4,1106 ),1107 ("proto", "proto", "%d", lambda x: int(x) if x else 0,1108 convert_int(8)),1109 ("tos", "tos", "%d", lambda x: int(x) if x else 0,1110 convert_int(8)),1111 ("ttl", "ttl", "%d", lambda x: int(x) if x else 0,1112 convert_int(8)),1113 ("frag", "frag", "%d", lambda x: int(x) if x else 0,1114 convert_int(8)),1115 )1116 1117 def __init__(1118 self,1119 data=None,1120 offset=None,1121 parent=None,1122 length=None,1123 init=None,1124 ):1125 ovskey.ovs_key_proto.__init__(1126 self,1127 "ipv4",1128 data=data,1129 offset=offset,1130 parent=parent,1131 length=length,1132 init=init,1133 )1134 1135 class ovs_key_ipv6(ovs_key_proto):1136 fields = (1137 ("src", "!16s"),1138 ("dst", "!16s"),1139 ("label", "!I"),1140 ("proto", "B"),1141 ("tclass", "B"),1142 ("hlimit", "B"),1143 ("frag", "B"),1144 )1145 1146 fields_map = (1147 (1148 "src",1149 "src",1150 lambda x: str(ipaddress.IPv6Address(x)),1151 lambda x: ipaddress.IPv6Address(x).packed if x else 0,1152 convert_ipv6,1153 ),1154 (1155 "dst",1156 "dst",1157 lambda x: str(ipaddress.IPv6Address(x)),1158 lambda x: ipaddress.IPv6Address(x).packed if x else 0,1159 convert_ipv6,1160 ),1161 ("label", "label", "%d", lambda x: int(x) if x else 0),1162 ("proto", "proto", "%d", lambda x: int(x) if x else 0),1163 ("tclass", "tclass", "%d", lambda x: int(x) if x else 0),1164 ("hlimit", "hlimit", "%d", lambda x: int(x) if x else 0),1165 ("frag", "frag", "%d", lambda x: int(x) if x else 0),1166 )1167 1168 def __init__(1169 self,1170 data=None,1171 offset=None,1172 parent=None,1173 length=None,1174 init=None,1175 ):1176 ovskey.ovs_key_proto.__init__(1177 self,1178 "ipv6",1179 data=data,1180 offset=offset,1181 parent=parent,1182 length=length,1183 init=init,1184 )1185 1186 class ovs_key_tcp(ovs_key_proto):1187 def __init__(1188 self,1189 data=None,1190 offset=None,1191 parent=None,1192 length=None,1193 init=None,1194 ):1195 ovskey.ovs_key_proto.__init__(1196 self,1197 "tcp",1198 data=data,1199 offset=offset,1200 parent=parent,1201 length=length,1202 init=init,1203 )1204 1205 class ovs_key_udp(ovs_key_proto):1206 def __init__(1207 self,1208 data=None,1209 offset=None,1210 parent=None,1211 length=None,1212 init=None,1213 ):1214 ovskey.ovs_key_proto.__init__(1215 self,1216 "udp",1217 data=data,1218 offset=offset,1219 parent=parent,1220 length=length,1221 init=init,1222 )1223 1224 class ovs_key_sctp(ovs_key_proto):1225 def __init__(1226 self,1227 data=None,1228 offset=None,1229 parent=None,1230 length=None,1231 init=None,1232 ):1233 ovskey.ovs_key_proto.__init__(1234 self,1235 "sctp",1236 data=data,1237 offset=offset,1238 parent=parent,1239 length=length,1240 init=init,1241 )1242 1243 class ovs_key_icmp(ovs_key_proto):1244 fields = (1245 ("type", "B"),1246 ("code", "B"),1247 )1248 1249 fields_map = (1250 ("type", "type", "%d", lambda x: int(x) if x else 0),1251 ("code", "code", "%d", lambda x: int(x) if x else 0),1252 )1253 1254 def __init__(1255 self,1256 data=None,1257 offset=None,1258 parent=None,1259 length=None,1260 init=None,1261 ):1262 ovskey.ovs_key_proto.__init__(1263 self,1264 "icmp",1265 data=data,1266 offset=offset,1267 parent=parent,1268 length=length,1269 init=init,1270 )1271 1272 class ovs_key_icmpv6(ovs_key_icmp):1273 def __init__(1274 self,1275 data=None,1276 offset=None,1277 parent=None,1278 length=None,1279 init=None,1280 ):1281 ovskey.ovs_key_proto.__init__(1282 self,1283 "icmpv6",1284 data=data,1285 offset=offset,1286 parent=parent,1287 length=length,1288 init=init,1289 )1290 1291 class ovs_key_arp(ovs_key_proto):1292 fields = (1293 ("sip", "!I"),1294 ("tip", "!I"),1295 ("op", "!H"),1296 ("sha", "!6s"),1297 ("tha", "!6s"),1298 ("pad", "xx"),1299 )1300 1301 fields_map = (1302 (1303 "sip",1304 "sip",1305 lambda x: str(ipaddress.IPv4Address(x)),1306 int,1307 convert_ipv4,1308 ),1309 (1310 "tip",1311 "tip",1312 lambda x: str(ipaddress.IPv4Address(x)),1313 int,1314 convert_ipv4,1315 ),1316 ("op", "op", "%d", lambda x: int(x) if x else 0),1317 (1318 "sha",1319 "sha",1320 macstr,1321 lambda x: int.from_bytes(x, "big"),1322 convert_mac,1323 ),1324 (1325 "tha",1326 "tha",1327 macstr,1328 lambda x: int.from_bytes(x, "big"),1329 convert_mac,1330 ),1331 )1332 1333 def __init__(1334 self,1335 data=None,1336 offset=None,1337 parent=None,1338 length=None,1339 init=None,1340 ):1341 ovskey.ovs_key_proto.__init__(1342 self,1343 "arp",1344 data=data,1345 offset=offset,1346 parent=parent,1347 length=length,1348 init=init,1349 )1350 1351 class ovs_key_nd(ovs_key_proto):1352 fields = (1353 ("target", "!16s"),1354 ("sll", "!6s"),1355 ("tll", "!6s"),1356 )1357 1358 fields_map = (1359 (1360 "target",1361 "target",1362 lambda x: str(ipaddress.IPv6Address(x)),1363 convert_ipv6,1364 ),1365 ("sll", "sll", macstr, lambda x: int.from_bytes(x, "big")),1366 ("tll", "tll", macstr, lambda x: int.from_bytes(x, "big")),1367 )1368 1369 def __init__(1370 self,1371 data=None,1372 offset=None,1373 parent=None,1374 length=None,1375 init=None,1376 ):1377 ovskey.ovs_key_proto.__init__(1378 self,1379 "nd",1380 data=data,1381 offset=offset,1382 parent=parent,1383 length=length,1384 init=init,1385 )1386 1387 class ovs_key_ct_tuple_ipv4(ovs_key_proto):1388 fields = (1389 ("src", "!I"),1390 ("dst", "!I"),1391 ("tp_src", "!H"),1392 ("tp_dst", "!H"),1393 ("proto", "B"),1394 )1395 1396 fields_map = (1397 (1398 "src",1399 "src",1400 lambda x: str(ipaddress.IPv4Address(x)),1401 int,1402 convert_ipv4,1403 ),1404 (1405 "dst",1406 "dst",1407 lambda x: str(ipaddress.IPv4Address(x)),1408 int,1409 convert_ipv4,1410 ),1411 ("tp_src", "tp_src", "%d", int),1412 ("tp_dst", "tp_dst", "%d", int),1413 ("proto", "proto", "%d", int),1414 )1415 1416 def __init__(1417 self,1418 data=None,1419 offset=None,1420 parent=None,1421 length=None,1422 init=None,1423 ):1424 ovskey.ovs_key_proto.__init__(1425 self,1426 "ct_tuple4",1427 data=data,1428 offset=offset,1429 parent=parent,1430 length=length,1431 init=init,1432 )1433 1434 class ovs_key_ct_tuple_ipv6(nla):1435 fields = (1436 ("src", "!16s"),1437 ("dst", "!16s"),1438 ("tp_src", "!H"),1439 ("tp_dst", "!H"),1440 ("proto", "B"),1441 )1442 1443 fields_map = (1444 (1445 "src",1446 "src",1447 lambda x: str(ipaddress.IPv6Address(x)),1448 convert_ipv6,1449 ),1450 (1451 "dst",1452 "dst",1453 lambda x: str(ipaddress.IPv6Address(x)),1454 convert_ipv6,1455 ),1456 ("tp_src", "tp_src", "%d", int),1457 ("tp_dst", "tp_dst", "%d", int),1458 ("proto", "proto", "%d", int),1459 )1460 1461 def __init__(1462 self,1463 data=None,1464 offset=None,1465 parent=None,1466 length=None,1467 init=None,1468 ):1469 ovskey.ovs_key_proto.__init__(1470 self,1471 "ct_tuple6",1472 data=data,1473 offset=offset,1474 parent=parent,1475 length=length,1476 init=init,1477 )1478 1479 class ovs_key_tunnel(nla):1480 nla_flags = NLA_F_NESTED1481 1482 nla_map = (1483 ("OVS_TUNNEL_KEY_ATTR_ID", "be64"),1484 ("OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "ipaddr"),1485 ("OVS_TUNNEL_KEY_ATTR_IPV4_DST", "ipaddr"),1486 ("OVS_TUNNEL_KEY_ATTR_TOS", "uint8"),1487 ("OVS_TUNNEL_KEY_ATTR_TTL", "uint8"),1488 ("OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", "flag"),1489 ("OVS_TUNNEL_KEY_ATTR_CSUM", "flag"),1490 ("OVS_TUNNEL_KEY_ATTR_OAM", "flag"),1491 ("OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS", "array(uint32)"),1492 ("OVS_TUNNEL_KEY_ATTR_TP_SRC", "be16"),1493 ("OVS_TUNNEL_KEY_ATTR_TP_DST", "be16"),1494 ("OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS", "none"),1495 ("OVS_TUNNEL_KEY_ATTR_IPV6_SRC", "ipaddr"),1496 ("OVS_TUNNEL_KEY_ATTR_IPV6_DST", "ipaddr"),1497 ("OVS_TUNNEL_KEY_ATTR_PAD", "none"),1498 ("OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS", "none"),1499 ("OVS_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE", "flag"),1500 )1501 1502 def parse(self, flowstr, mask=None):1503 if not flowstr.startswith("tunnel("):1504 return None, None1505 1506 k = ovskey.ovs_key_tunnel()1507 if mask is not None:1508 mask = ovskey.ovs_key_tunnel()1509 1510 flowstr = flowstr[len("tunnel("):]1511 1512 v6_address = None1513 1514 fields = [1515 ("tun_id=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_ID",1516 0xffffffffffffffff, None, None),1517 1518 ("src=", r"([0-9a-fA-F\.]+)", str,1519 "OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "255.255.255.255", "0.0.0.0",1520 False),1521 ("dst=", r"([0-9a-fA-F\.]+)", str,1522 "OVS_TUNNEL_KEY_ATTR_IPV4_DST", "255.255.255.255", "0.0.0.0",1523 False),1524 1525 ("ipv6_src=", r"([0-9a-fA-F:]+)", str,1526 "OVS_TUNNEL_KEY_ATTR_IPV6_SRC",1527 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True),1528 ("ipv6_dst=", r"([0-9a-fA-F:]+)", str,1529 "OVS_TUNNEL_KEY_ATTR_IPV6_DST",1530 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True),1531 1532 ("tos=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TOS", 255, 0,1533 None),1534 ("ttl=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TTL", 255, 0,1535 None),1536 1537 ("tp_src=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_SRC",1538 65535, 0, None),1539 ("tp_dst=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_DST",1540 65535, 0, None),1541 ]1542 1543 forced_include = ["OVS_TUNNEL_KEY_ATTR_TTL"]1544 1545 for prefix, regex, typ, attr_name, mask_val, default_val, v46_flag in fields:1546 flowstr, value = parse_extract_field(flowstr, prefix, regex, typ, False)1547 if not attr_name:1548 raise Exception("Bad list value in tunnel fields")1549 1550 if value is None and attr_name in forced_include:1551 value = default_val1552 mask_val = default_val1553 1554 if value is not None:1555 if v46_flag is not None:1556 if v6_address is None:1557 v6_address = v46_flag1558 if v46_flag != v6_address:1559 raise ValueError("Cannot mix v6 and v4 addresses")1560 k["attrs"].append([attr_name, value])1561 if mask is not None:1562 mask["attrs"].append([attr_name, mask_val])1563 else:1564 if v46_flag is not None:1565 if v6_address is None or v46_flag != v6_address:1566 continue1567 if mask is not None:1568 mask["attrs"].append([attr_name, default_val])1569 1570 if k["attrs"][0][0] != "OVS_TUNNEL_KEY_ATTR_ID":1571 raise ValueError("Needs a tunid set")1572 1573 if flowstr.startswith("flags("):1574 flowstr = flowstr[len("flags("):]1575 flagspos = flowstr.find(")")1576 flags = flowstr[:flagspos]1577 flowstr = flowstr[flagspos + 1:]1578 1579 flag_attrs = {1580 "df": "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT",1581 "csum": "OVS_TUNNEL_KEY_ATTR_CSUM",1582 "oam": "OVS_TUNNEL_KEY_ATTR_OAM"1583 }1584 1585 for flag in flags.split("|"):1586 if flag in flag_attrs:1587 k["attrs"].append([flag_attrs[flag], True])1588 if mask is not None:1589 mask["attrs"].append([flag_attrs[flag], True])1590 1591 flowstr = flowstr[strspn(flowstr, ", ") :]1592 return flowstr, k, mask1593 1594 def dpstr(self, mask=None, more=False):1595 print_str = "tunnel("1596 1597 flagsattrs = []1598 for k in self["attrs"]:1599 noprint = False1600 if k[0] == "OVS_TUNNEL_KEY_ATTR_ID":1601 print_str += "tun_id=%d" % k[1]1602 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_SRC":1603 print_str += "src=%s" % k[1]1604 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_DST":1605 print_str += "dst=%s" % k[1]1606 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_SRC":1607 print_str += "ipv6_src=%s" % k[1]1608 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_DST":1609 print_str += "ipv6_dst=%s" % k[1]1610 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TOS":1611 print_str += "tos=%d" % k[1]1612 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TTL":1613 print_str += "ttl=%d" % k[1]1614 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_SRC":1615 print_str += "tp_src=%d" % k[1]1616 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_DST":1617 print_str += "tp_dst=%d" % k[1]1618 elif k[0] == "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT":1619 noprint = True1620 flagsattrs.append("df")1621 elif k[0] == "OVS_TUNNEL_KEY_ATTR_CSUM":1622 noprint = True1623 flagsattrs.append("csum")1624 elif k[0] == "OVS_TUNNEL_KEY_ATTR_OAM":1625 noprint = True1626 flagsattrs.append("oam")1627 1628 if not noprint:1629 print_str += ","1630 1631 if len(flagsattrs):1632 print_str += "flags(" + "|".join(flagsattrs) + ")"1633 print_str += ")"1634 return print_str1635 1636 class ovs_key_mpls(nla):1637 fields = (("lse", ">I"),)1638 1639 def parse(self, flowstr, mask=None):1640 for field in (1641 ("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse),1642 ("OVS_KEY_ATTR_SKB_MARK", "skb_mark", intparse),1643 ("OVS_KEY_ATTR_RECIRC_ID", "recirc_id", intparse),1644 ("OVS_KEY_ATTR_TUNNEL", "tunnel", ovskey.ovs_key_tunnel),1645 ("OVS_KEY_ATTR_DP_HASH", "dp_hash", intparse),1646 ("OVS_KEY_ATTR_CT_STATE", "ct_state", parse_ct_state),1647 ("OVS_KEY_ATTR_CT_ZONE", "ct_zone", intparse),1648 ("OVS_KEY_ATTR_CT_MARK", "ct_mark", intparse),1649 ("OVS_KEY_ATTR_IN_PORT", "in_port", intparse),1650 (1651 "OVS_KEY_ATTR_ETHERNET",1652 "eth",1653 ovskey.ethaddr,1654 ),1655 (1656 "OVS_KEY_ATTR_ETHERTYPE",1657 "eth_type",1658 lambda x: intparse(x, "0xffff"),1659 ),1660 (1661 "OVS_KEY_ATTR_IPV4",1662 "ipv4",1663 ovskey.ovs_key_ipv4,1664 ),1665 (1666 "OVS_KEY_ATTR_IPV6",1667 "ipv6",1668 ovskey.ovs_key_ipv6,1669 ),1670 (1671 "OVS_KEY_ATTR_ARP",1672 "arp",1673 ovskey.ovs_key_arp,1674 ),1675 (1676 "OVS_KEY_ATTR_TCP",1677 "tcp",1678 ovskey.ovs_key_tcp,1679 ),1680 (1681 "OVS_KEY_ATTR_UDP",1682 "udp",1683 ovskey.ovs_key_udp,1684 ),1685 (1686 "OVS_KEY_ATTR_ICMP",1687 "icmp",1688 ovskey.ovs_key_icmp,1689 ),1690 (1691 "OVS_KEY_ATTR_TCP_FLAGS",1692 "tcp_flags",1693 lambda x: parse_flags(x, None),1694 ),1695 ):1696 fld = field[1] + "("1697 if not flowstr.startswith(fld):1698 continue1699 1700 if not isinstance(field[2], types.FunctionType):1701 nk = field[2]()1702 flowstr, k, m = nk.parse(flowstr, field[2])1703 else:1704 flowstr = flowstr[len(fld) :]1705 flowstr, k, m = field[2](flowstr)1706 1707 if m and mask is not None:1708 mask["attrs"].append([field[0], m])1709 self["attrs"].append([field[0], k])1710 1711 flowstr = flowstr[strspn(flowstr, "), ") :]1712 1713 return flowstr1714 1715 def dpstr(self, mask=None, more=False):1716 print_str = ""1717 1718 for field in (1719 (1720 "OVS_KEY_ATTR_PRIORITY",1721 "skb_priority",1722 "%d",1723 lambda x: False,1724 True,1725 ),1726 (1727 "OVS_KEY_ATTR_SKB_MARK",1728 "skb_mark",1729 "%d",1730 lambda x: False,1731 True,1732 ),1733 (1734 "OVS_KEY_ATTR_RECIRC_ID",1735 "recirc_id",1736 "0x%08X",1737 lambda x: False,1738 True,1739 ),1740 (1741 "OVS_KEY_ATTR_DP_HASH",1742 "dp_hash",1743 "0x%08X",1744 lambda x: False,1745 True,1746 ),1747 (1748 "OVS_KEY_ATTR_TUNNEL",1749 "tunnel",1750 None,1751 False,1752 False,1753 ),1754 (1755 "OVS_KEY_ATTR_CT_STATE",1756 "ct_state",1757 "0x%04x",1758 lambda x: False,1759 True,1760 ),1761 (1762 "OVS_KEY_ATTR_CT_ZONE",1763 "ct_zone",1764 "0x%04x",1765 lambda x: False,1766 True,1767 ),1768 (1769 "OVS_KEY_ATTR_CT_MARK",1770 "ct_mark",1771 "0x%08x",1772 lambda x: False,1773 True,1774 ),1775 (1776 "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4",1777 None,1778 None,1779 False,1780 False,1781 ),1782 (1783 "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6",1784 None,1785 None,1786 False,1787 False,1788 ),1789 (1790 "OVS_KEY_ATTR_IN_PORT",1791 "in_port",1792 "%d",1793 lambda x: True,1794 True,1795 ),1796 ("OVS_KEY_ATTR_ETHERNET", None, None, False, False),1797 (1798 "OVS_KEY_ATTR_ETHERTYPE",1799 "eth_type",1800 "0x%04x",1801 lambda x: int(x) == 0xFFFF,1802 True,1803 ),1804 ("OVS_KEY_ATTR_IPV4", None, None, False, False),1805 ("OVS_KEY_ATTR_IPV6", None, None, False, False),1806 ("OVS_KEY_ATTR_ARP", None, None, False, False),1807 ("OVS_KEY_ATTR_TCP", None, None, False, False),1808 (1809 "OVS_KEY_ATTR_TCP_FLAGS",1810 "tcp_flags",1811 "0x%04x",1812 lambda x: False,1813 True,1814 ),1815 ("OVS_KEY_ATTR_UDP", None, None, False, False),1816 ("OVS_KEY_ATTR_SCTP", None, None, False, False),1817 ("OVS_KEY_ATTR_ICMP", None, None, False, False),1818 ("OVS_KEY_ATTR_ICMPV6", None, None, False, False),1819 ("OVS_KEY_ATTR_ND", None, None, False, False),1820 ):1821 v = self.get_attr(field[0])1822 if v is not None:1823 m = None if mask is None else mask.get_attr(field[0])1824 if field[4] is False:1825 print_str += v.dpstr(m, more)1826 print_str += ","1827 else:1828 if m is None or field[3](m):1829 print_str += field[1] + "("1830 print_str += field[2] % v1831 print_str += "),"1832 elif more or m != 0:1833 print_str += field[1] + "("1834 print_str += (field[2] % v) + "/" + (field[2] % m)1835 print_str += "),"1836 1837 return print_str1838 1839 1840class OvsPacket(GenericNetlinkSocket):1841 OVS_PACKET_CMD_MISS = 1 # Flow table miss1842 OVS_PACKET_CMD_ACTION = 2 # USERSPACE action1843 OVS_PACKET_CMD_EXECUTE = 3 # Apply actions to packet1844 1845 class ovs_packet_msg(ovs_dp_msg):1846 nla_map = (1847 ("OVS_PACKET_ATTR_UNSPEC", "none"),1848 ("OVS_PACKET_ATTR_PACKET", "array(uint8)"),1849 ("OVS_PACKET_ATTR_KEY", "ovskey"),1850 ("OVS_PACKET_ATTR_ACTIONS", "ovsactions"),1851 ("OVS_PACKET_ATTR_USERDATA", "none"),1852 ("OVS_PACKET_ATTR_EGRESS_TUN_KEY", "none"),1853 ("OVS_PACKET_ATTR_UNUSED1", "none"),1854 ("OVS_PACKET_ATTR_UNUSED2", "none"),1855 ("OVS_PACKET_ATTR_PROBE", "none"),1856 ("OVS_PACKET_ATTR_MRU", "uint16"),1857 ("OVS_PACKET_ATTR_LEN", "uint32"),1858 ("OVS_PACKET_ATTR_HASH", "uint64"),1859 )1860 1861 def __init__(self):1862 GenericNetlinkSocket.__init__(self)1863 self.bind(OVS_PACKET_FAMILY, OvsPacket.ovs_packet_msg)1864 1865 def upcall_handler(self, up=None):1866 print("listening on upcall packet handler:", self.epid)1867 while True:1868 try:1869 msgs = self.get()1870 for msg in msgs:1871 if not up:1872 continue1873 if msg["cmd"] == OvsPacket.OVS_PACKET_CMD_MISS:1874 up.miss(msg)1875 elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_ACTION:1876 up.action(msg)1877 elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_EXECUTE:1878 up.execute(msg)1879 else:1880 print("Unkonwn cmd: %d" % msg["cmd"])1881 except NetlinkError as ne:1882 raise ne1883 1884 1885class OvsDatapath(GenericNetlinkSocket):1886 OVS_DP_F_VPORT_PIDS = 1 << 11887 OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 31888 1889 class dp_cmd_msg(ovs_dp_msg):1890 """1891 Message class that will be used to communicate with the kernel module.1892 """1893 1894 nla_map = (1895 ("OVS_DP_ATTR_UNSPEC", "none"),1896 ("OVS_DP_ATTR_NAME", "asciiz"),1897 ("OVS_DP_ATTR_UPCALL_PID", "array(uint32)"),1898 ("OVS_DP_ATTR_STATS", "dpstats"),1899 ("OVS_DP_ATTR_MEGAFLOW_STATS", "megaflowstats"),1900 ("OVS_DP_ATTR_USER_FEATURES", "uint32"),1901 ("OVS_DP_ATTR_PAD", "none"),1902 ("OVS_DP_ATTR_MASKS_CACHE_SIZE", "uint32"),1903 ("OVS_DP_ATTR_PER_CPU_PIDS", "array(uint32)"),1904 )1905 1906 class dpstats(nla):1907 fields = (1908 ("hit", "=Q"),1909 ("missed", "=Q"),1910 ("lost", "=Q"),1911 ("flows", "=Q"),1912 )1913 1914 class megaflowstats(nla):1915 fields = (1916 ("mask_hit", "=Q"),1917 ("masks", "=I"),1918 ("padding", "=I"),1919 ("cache_hits", "=Q"),1920 ("pad1", "=Q"),1921 )1922 1923 def __init__(self):1924 GenericNetlinkSocket.__init__(self)1925 self.bind(OVS_DATAPATH_FAMILY, OvsDatapath.dp_cmd_msg)1926 1927 def info(self, dpname, ifindex=0):1928 msg = OvsDatapath.dp_cmd_msg()1929 msg["cmd"] = OVS_DP_CMD_GET1930 msg["version"] = OVS_DATAPATH_VERSION1931 msg["reserved"] = 01932 msg["dpifindex"] = ifindex1933 msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])1934 1935 try:1936 reply = self.nlm_request(1937 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST1938 )1939 reply = reply[0]1940 except NetlinkError as ne:1941 if ne.code == errno.ENODEV:1942 reply = None1943 else:1944 raise ne1945 1946 return reply1947 1948 def create(1949 self, dpname, shouldUpcall=False, versionStr=None, p=OvsPacket()1950 ):1951 msg = OvsDatapath.dp_cmd_msg()1952 msg["cmd"] = OVS_DP_CMD_NEW1953 if versionStr is None:1954 msg["version"] = OVS_DATAPATH_VERSION1955 else:1956 msg["version"] = int(versionStr.split(":")[0], 0)1957 msg["reserved"] = 01958 msg["dpifindex"] = 01959 msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])1960 1961 dpfeatures = 01962 if versionStr is not None and versionStr.find(":") != -1:1963 dpfeatures = int(versionStr.split(":")[1], 0)1964 else:1965 if versionStr is None or versionStr.find(":") == -1:1966 dpfeatures |= OvsDatapath.OVS_DP_F_DISPATCH_UPCALL_PER_CPU1967 dpfeatures &= ~OvsDatapath.OVS_DP_F_VPORT_PIDS1968 1969 nproc = multiprocessing.cpu_count()1970 procarray = []1971 for i in range(1, nproc):1972 procarray += [int(p.epid)]1973 msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", procarray])1974 msg["attrs"].append(["OVS_DP_ATTR_USER_FEATURES", dpfeatures])1975 if not shouldUpcall:1976 msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", [0]])1977 1978 try:1979 reply = self.nlm_request(1980 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK1981 )1982 reply = reply[0]1983 except NetlinkError as ne:1984 if ne.code == errno.EEXIST:1985 reply = None1986 else:1987 raise ne1988 1989 return reply1990 1991 def destroy(self, dpname):1992 msg = OvsDatapath.dp_cmd_msg()1993 msg["cmd"] = OVS_DP_CMD_DEL1994 msg["version"] = OVS_DATAPATH_VERSION1995 msg["reserved"] = 01996 msg["dpifindex"] = 01997 msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])1998 1999 try:2000 reply = self.nlm_request(2001 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK2002 )2003 reply = reply[0]2004 except NetlinkError as ne:2005 if ne.code == errno.ENODEV:2006 reply = None2007 else:2008 raise ne2009 2010 return reply2011 2012 2013class OvsVport(GenericNetlinkSocket):2014 OVS_VPORT_TYPE_NETDEV = 12015 OVS_VPORT_TYPE_INTERNAL = 22016 OVS_VPORT_TYPE_GRE = 32017 OVS_VPORT_TYPE_VXLAN = 42018 OVS_VPORT_TYPE_GENEVE = 52019 2020 class ovs_vport_msg(ovs_dp_msg):2021 nla_map = (2022 ("OVS_VPORT_ATTR_UNSPEC", "none"),2023 ("OVS_VPORT_ATTR_PORT_NO", "uint32"),2024 ("OVS_VPORT_ATTR_TYPE", "uint32"),2025 ("OVS_VPORT_ATTR_NAME", "asciiz"),2026 ("OVS_VPORT_ATTR_OPTIONS", "vportopts"),2027 ("OVS_VPORT_ATTR_UPCALL_PID", "array(uint32)"),2028 ("OVS_VPORT_ATTR_STATS", "vportstats"),2029 ("OVS_VPORT_ATTR_PAD", "none"),2030 ("OVS_VPORT_ATTR_IFINDEX", "uint32"),2031 ("OVS_VPORT_ATTR_NETNSID", "uint32"),2032 )2033 2034 class vportopts(nla):2035 nla_map = (2036 ("OVS_TUNNEL_ATTR_UNSPEC", "none"),2037 ("OVS_TUNNEL_ATTR_DST_PORT", "uint16"),2038 ("OVS_TUNNEL_ATTR_EXTENSION", "none"),2039 )2040 2041 class vportstats(nla):2042 fields = (2043 ("rx_packets", "=Q"),2044 ("tx_packets", "=Q"),2045 ("rx_bytes", "=Q"),2046 ("tx_bytes", "=Q"),2047 ("rx_errors", "=Q"),2048 ("tx_errors", "=Q"),2049 ("rx_dropped", "=Q"),2050 ("tx_dropped", "=Q"),2051 )2052 2053 def type_to_str(vport_type):2054 if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV:2055 return "netdev"2056 elif vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL:2057 return "internal"2058 elif vport_type == OvsVport.OVS_VPORT_TYPE_GRE:2059 return "gre"2060 elif vport_type == OvsVport.OVS_VPORT_TYPE_VXLAN:2061 return "vxlan"2062 elif vport_type == OvsVport.OVS_VPORT_TYPE_GENEVE:2063 return "geneve"2064 raise ValueError("Unknown vport type:%d" % vport_type)2065 2066 def str_to_type(vport_type):2067 if vport_type == "netdev":2068 return OvsVport.OVS_VPORT_TYPE_NETDEV2069 elif vport_type == "internal":2070 return OvsVport.OVS_VPORT_TYPE_INTERNAL2071 elif vport_type == "gre":2072 return OvsVport.OVS_VPORT_TYPE_INTERNAL2073 elif vport_type == "vxlan":2074 return OvsVport.OVS_VPORT_TYPE_VXLAN2075 elif vport_type == "geneve":2076 return OvsVport.OVS_VPORT_TYPE_GENEVE2077 raise ValueError("Unknown vport type: '%s'" % vport_type)2078 2079 def __init__(self, packet=OvsPacket()):2080 GenericNetlinkSocket.__init__(self)2081 self.bind(OVS_VPORT_FAMILY, OvsVport.ovs_vport_msg)2082 self.upcall_packet = packet2083 2084 def info(self, vport_name, dpifindex=0, portno=None):2085 msg = OvsVport.ovs_vport_msg()2086 2087 msg["cmd"] = OVS_VPORT_CMD_GET2088 msg["version"] = OVS_DATAPATH_VERSION2089 msg["reserved"] = 02090 msg["dpifindex"] = dpifindex2091 2092 if portno is None:2093 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_name])2094 else:2095 msg["attrs"].append(["OVS_VPORT_ATTR_PORT_NO", portno])2096 2097 try:2098 reply = self.nlm_request(2099 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST2100 )2101 reply = reply[0]2102 except NetlinkError as ne:2103 if ne.code == errno.ENODEV:2104 reply = None2105 else:2106 raise ne2107 return reply2108 2109 def attach(self, dpindex, vport_ifname, ptype, dport, lwt):2110 msg = OvsVport.ovs_vport_msg()2111 2112 msg["cmd"] = OVS_VPORT_CMD_NEW2113 msg["version"] = OVS_DATAPATH_VERSION2114 msg["reserved"] = 02115 msg["dpifindex"] = dpindex2116 port_type = OvsVport.str_to_type(ptype)2117 2118 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])2119 msg["attrs"].append(2120 ["OVS_VPORT_ATTR_UPCALL_PID", [self.upcall_packet.epid]]2121 )2122 2123 TUNNEL_DEFAULTS = [("geneve", 6081),2124 ("vxlan", 4789)]2125 2126 for tnl in TUNNEL_DEFAULTS:2127 if ptype == tnl[0]:2128 if not dport:2129 dport = tnl[1]2130 2131 if not lwt:2132 vportopt = OvsVport.ovs_vport_msg.vportopts()2133 vportopt["attrs"].append(2134 ["OVS_TUNNEL_ATTR_DST_PORT", socket.htons(dport)]2135 )2136 msg["attrs"].append(2137 ["OVS_VPORT_ATTR_OPTIONS", vportopt]2138 )2139 else:2140 port_type = OvsVport.OVS_VPORT_TYPE_NETDEV2141 ipr = pyroute2.iproute.IPRoute()2142 2143 if tnl[0] == "geneve":2144 ipr.link("add", ifname=vport_ifname, kind=tnl[0],2145 geneve_port=dport,2146 geneve_collect_metadata=True,2147 geneve_udp_zero_csum6_rx=1)2148 elif tnl[0] == "vxlan":2149 ipr.link("add", ifname=vport_ifname, kind=tnl[0],2150 vxlan_learning=0, vxlan_collect_metadata=1,2151 vxlan_udp_zero_csum6_rx=1, vxlan_port=dport)2152 break2153 msg["attrs"].append(["OVS_VPORT_ATTR_TYPE", port_type])2154 2155 try:2156 reply = self.nlm_request(2157 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK2158 )2159 reply = reply[0]2160 except NetlinkError as ne:2161 if ne.code == errno.EEXIST:2162 reply = None2163 else:2164 raise ne2165 return reply2166 2167 def reset_upcall(self, dpindex, vport_ifname, p=None):2168 msg = OvsVport.ovs_vport_msg()2169 2170 msg["cmd"] = OVS_VPORT_CMD_SET2171 msg["version"] = OVS_DATAPATH_VERSION2172 msg["reserved"] = 02173 msg["dpifindex"] = dpindex2174 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])2175 2176 if p == None:2177 p = self.upcall_packet2178 else:2179 self.upcall_packet = p2180 2181 msg["attrs"].append(["OVS_VPORT_ATTR_UPCALL_PID", [p.epid]])2182 2183 try:2184 reply = self.nlm_request(2185 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK2186 )2187 reply = reply[0]2188 except NetlinkError as ne:2189 raise ne2190 return reply2191 2192 def detach(self, dpindex, vport_ifname):2193 msg = OvsVport.ovs_vport_msg()2194 2195 msg["cmd"] = OVS_VPORT_CMD_DEL2196 msg["version"] = OVS_DATAPATH_VERSION2197 msg["reserved"] = 02198 msg["dpifindex"] = dpindex2199 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])2200 2201 try:2202 reply = self.nlm_request(2203 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK2204 )2205 reply = reply[0]2206 except NetlinkError as ne:2207 if ne.code == errno.ENODEV:2208 reply = None2209 else:2210 raise ne2211 return reply2212 2213 def upcall_handler(self, handler=None):2214 self.upcall_packet.upcall_handler(handler)2215 2216 2217class OvsFlow(GenericNetlinkSocket):2218 class ovs_flow_msg(ovs_dp_msg):2219 nla_map = (2220 ("OVS_FLOW_ATTR_UNSPEC", "none"),2221 ("OVS_FLOW_ATTR_KEY", "ovskey"),2222 ("OVS_FLOW_ATTR_ACTIONS", "ovsactions"),2223 ("OVS_FLOW_ATTR_STATS", "flowstats"),2224 ("OVS_FLOW_ATTR_TCP_FLAGS", "uint8"),2225 ("OVS_FLOW_ATTR_USED", "uint64"),2226 ("OVS_FLOW_ATTR_CLEAR", "none"),2227 ("OVS_FLOW_ATTR_MASK", "ovskey"),2228 ("OVS_FLOW_ATTR_PROBE", "none"),2229 ("OVS_FLOW_ATTR_UFID", "array(uint32)"),2230 ("OVS_FLOW_ATTR_UFID_FLAGS", "uint32"),2231 )2232 2233 class flowstats(nla):2234 fields = (2235 ("packets", "=Q"),2236 ("bytes", "=Q"),2237 )2238 2239 def dpstr(self, more=False):2240 ufid = self.get_attr("OVS_FLOW_ATTR_UFID")2241 ufid_str = ""2242 if ufid is not None:2243 ufid_str = (2244 "ufid:{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format(2245 ufid[0],2246 ufid[1] >> 16,2247 ufid[1] & 0xFFFF,2248 ufid[2] >> 16,2249 ufid[2] & 0,2250 ufid[3],2251 )2252 )2253 2254 key_field = self.get_attr("OVS_FLOW_ATTR_KEY")2255 keymsg = None2256 if key_field is not None:2257 keymsg = key_field2258 2259 mask_field = self.get_attr("OVS_FLOW_ATTR_MASK")2260 maskmsg = None2261 if mask_field is not None:2262 maskmsg = mask_field2263 2264 acts_field = self.get_attr("OVS_FLOW_ATTR_ACTIONS")2265 actsmsg = None2266 if acts_field is not None:2267 actsmsg = acts_field2268 2269 print_str = ""2270 2271 if more:2272 print_str += ufid_str + ","2273 2274 if keymsg is not None:2275 print_str += keymsg.dpstr(maskmsg, more)2276 2277 stats = self.get_attr("OVS_FLOW_ATTR_STATS")2278 if stats is None:2279 print_str += " packets:0, bytes:0,"2280 else:2281 print_str += " packets:%d, bytes:%d," % (2282 stats["packets"],2283 stats["bytes"],2284 )2285 2286 used = self.get_attr("OVS_FLOW_ATTR_USED")2287 print_str += " used:"2288 if used is None:2289 print_str += "never,"2290 else:2291 used_time = int(used)2292 cur_time_sec = time.clock_gettime(time.CLOCK_MONOTONIC)2293 used_time = (cur_time_sec * 1000) - used_time2294 print_str += "{}s,".format(used_time / 1000)2295 2296 print_str += " actions:"2297 if (2298 actsmsg is None2299 or "attrs" not in actsmsg2300 or len(actsmsg["attrs"]) == 02301 ):2302 print_str += "drop"2303 else:2304 print_str += actsmsg.dpstr(more)2305 2306 return print_str2307 2308 def parse(self, flowstr, actstr, dpidx=0):2309 OVS_UFID_F_OMIT_KEY = 1 << 02310 OVS_UFID_F_OMIT_MASK = 1 << 12311 OVS_UFID_F_OMIT_ACTIONS = 1 << 22312 2313 self["cmd"] = 02314 self["version"] = 02315 self["reserved"] = 02316 self["dpifindex"] = 02317 2318 if flowstr.startswith("ufid:"):2319 count = 52320 while flowstr[count] != ",":2321 count += 12322 ufidstr = flowstr[5:count]2323 flowstr = flowstr[count + 1 :]2324 else:2325 ufidstr = str(uuid.uuid4())2326 uuidRawObj = uuid.UUID(ufidstr).fields2327 2328 self["attrs"].append(2329 [2330 "OVS_FLOW_ATTR_UFID",2331 [2332 uuidRawObj[0],2333 uuidRawObj[1] << 16 | uuidRawObj[2],2334 uuidRawObj[3] << 242335 | uuidRawObj[4] << 162336 | uuidRawObj[5] & (0xFF << 32) >> 32,2337 uuidRawObj[5] & (0xFFFFFFFF),2338 ],2339 ]2340 )2341 self["attrs"].append(2342 [2343 "OVS_FLOW_ATTR_UFID_FLAGS",2344 int(2345 OVS_UFID_F_OMIT_KEY2346 | OVS_UFID_F_OMIT_MASK2347 | OVS_UFID_F_OMIT_ACTIONS2348 ),2349 ]2350 )2351 2352 k = ovskey()2353 m = ovskey()2354 k.parse(flowstr, m)2355 self["attrs"].append(["OVS_FLOW_ATTR_KEY", k])2356 self["attrs"].append(["OVS_FLOW_ATTR_MASK", m])2357 2358 a = ovsactions()2359 a.parse(actstr)2360 self["attrs"].append(["OVS_FLOW_ATTR_ACTIONS", a])2361 2362 def __init__(self):2363 GenericNetlinkSocket.__init__(self)2364 2365 self.bind(OVS_FLOW_FAMILY, OvsFlow.ovs_flow_msg)2366 2367 def add_flow(self, dpifindex, flowmsg):2368 """2369 Send a new flow message to the kernel.2370 2371 dpifindex should be a valid datapath obtained by calling2372 into the OvsDatapath lookup2373 2374 flowmsg is a flow object obtained by calling a dpparse2375 """2376 2377 flowmsg["cmd"] = OVS_FLOW_CMD_NEW2378 flowmsg["version"] = OVS_DATAPATH_VERSION2379 flowmsg["reserved"] = 02380 flowmsg["dpifindex"] = dpifindex2381 2382 try:2383 reply = self.nlm_request(2384 flowmsg,2385 msg_type=self.prid,2386 msg_flags=NLM_F_REQUEST | NLM_F_ACK,2387 )2388 reply = reply[0]2389 except NetlinkError as ne:2390 print(flowmsg)2391 raise ne2392 return reply2393 2394 def del_flows(self, dpifindex):2395 """2396 Send a del message to the kernel that will drop all flows.2397 2398 dpifindex should be a valid datapath obtained by calling2399 into the OvsDatapath lookup2400 """2401 2402 flowmsg = OvsFlow.ovs_flow_msg()2403 flowmsg["cmd"] = OVS_FLOW_CMD_DEL2404 flowmsg["version"] = OVS_DATAPATH_VERSION2405 flowmsg["reserved"] = 02406 flowmsg["dpifindex"] = dpifindex2407 2408 try:2409 reply = self.nlm_request(2410 flowmsg,2411 msg_type=self.prid,2412 msg_flags=NLM_F_REQUEST | NLM_F_ACK,2413 )2414 reply = reply[0]2415 except NetlinkError as ne:2416 print(flowmsg)2417 raise ne2418 return reply2419 2420 def dump(self, dpifindex, flowspec=None):2421 """2422 Returns a list of messages containing flows.2423 2424 dpifindex should be a valid datapath obtained by calling2425 into the OvsDatapath lookup2426 2427 flowpsec is a string which represents a flow in the dpctl2428 format.2429 """2430 msg = OvsFlow.ovs_flow_msg()2431 2432 msg["cmd"] = OVS_FLOW_CMD_GET2433 msg["version"] = OVS_DATAPATH_VERSION2434 msg["reserved"] = 02435 msg["dpifindex"] = dpifindex2436 2437 msg_flags = NLM_F_REQUEST | NLM_F_ACK2438 if flowspec is None:2439 msg_flags |= NLM_F_DUMP2440 rep = None2441 2442 try:2443 rep = self.nlm_request(2444 msg,2445 msg_type=self.prid,2446 msg_flags=msg_flags,2447 )2448 except NetlinkError as ne:2449 raise ne2450 return rep2451 2452 def miss(self, packetmsg):2453 seq = packetmsg["header"]["sequence_number"]2454 keystr = "(none)"2455 key_field = packetmsg.get_attr("OVS_PACKET_ATTR_KEY")2456 if key_field is not None:2457 keystr = key_field.dpstr(None, True)2458 2459 pktdata = packetmsg.get_attr("OVS_PACKET_ATTR_PACKET")2460 pktpres = "yes" if pktdata is not None else "no"2461 2462 print("MISS upcall[%d/%s]: %s" % (seq, pktpres, keystr), flush=True)2463 2464 def execute(self, packetmsg):2465 print("userspace execute command", flush=True)2466 2467 def action(self, packetmsg):2468 print("userspace action command", flush=True)2469 2470 2471class psample_sample(genlmsg):2472 nla_map = (2473 ("PSAMPLE_ATTR_IIFINDEX", "none"),2474 ("PSAMPLE_ATTR_OIFINDEX", "none"),2475 ("PSAMPLE_ATTR_ORIGSIZE", "none"),2476 ("PSAMPLE_ATTR_SAMPLE_GROUP", "uint32"),2477 ("PSAMPLE_ATTR_GROUP_SEQ", "none"),2478 ("PSAMPLE_ATTR_SAMPLE_RATE", "uint32"),2479 ("PSAMPLE_ATTR_DATA", "array(uint8)"),2480 ("PSAMPLE_ATTR_GROUP_REFCOUNT", "none"),2481 ("PSAMPLE_ATTR_TUNNEL", "none"),2482 ("PSAMPLE_ATTR_PAD", "none"),2483 ("PSAMPLE_ATTR_OUT_TC", "none"),2484 ("PSAMPLE_ATTR_OUT_TC_OCC", "none"),2485 ("PSAMPLE_ATTR_LATENCY", "none"),2486 ("PSAMPLE_ATTR_TIMESTAMP", "none"),2487 ("PSAMPLE_ATTR_PROTO", "none"),2488 ("PSAMPLE_ATTR_USER_COOKIE", "array(uint8)"),2489 )2490 2491 def dpstr(self):2492 fields = []2493 data = ""2494 for (attr, value) in self["attrs"]:2495 if attr == "PSAMPLE_ATTR_SAMPLE_GROUP":2496 fields.append("group:%d" % value)2497 if attr == "PSAMPLE_ATTR_SAMPLE_RATE":2498 fields.append("rate:%d" % value)2499 if attr == "PSAMPLE_ATTR_USER_COOKIE":2500 value = "".join(format(x, "02x") for x in value)2501 fields.append("cookie:%s" % value)2502 if attr == "PSAMPLE_ATTR_DATA" and len(value) > 0:2503 data = "data:%s" % "".join(format(x, "02x") for x in value)2504 2505 return ("%s %s" % (",".join(fields), data)).strip()2506 2507 2508class psample_msg(Marshal):2509 PSAMPLE_CMD_SAMPLE = 02510 PSAMPLE_CMD_GET_GROUP = 12511 PSAMPLE_CMD_NEW_GROUP = 22512 PSAMPLE_CMD_DEL_GROUP = 32513 PSAMPLE_CMD_SET_FILTER = 42514 msg_map = {PSAMPLE_CMD_SAMPLE: psample_sample}2515 2516 2517class PsampleEvent(EventSocket):2518 genl_family = "psample"2519 mcast_groups = ["packets"]2520 marshal_class = psample_msg2521 2522 def read_samples(self):2523 print("listening for psample events", flush=True)2524 while True:2525 try:2526 for msg in self.get():2527 print(msg.dpstr(), flush=True)2528 except NetlinkError as ne:2529 raise ne2530 2531 2532def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):2533 dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME")2534 base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS")2535 megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS")2536 user_features = dp_lookup_rep.get_attr("OVS_DP_ATTR_USER_FEATURES")2537 masks_cache_size = dp_lookup_rep.get_attr("OVS_DP_ATTR_MASKS_CACHE_SIZE")2538 2539 print("%s:" % dp_name)2540 print(2541 " lookups: hit:%d missed:%d lost:%d"2542 % (base_stats["hit"], base_stats["missed"], base_stats["lost"])2543 )2544 print(" flows:%d" % base_stats["flows"])2545 pkts = base_stats["hit"] + base_stats["missed"]2546 avg = (megaflow_stats["mask_hit"] / pkts) if pkts != 0 else 0.02547 print(2548 " masks: hit:%d total:%d hit/pkt:%f"2549 % (megaflow_stats["mask_hit"], megaflow_stats["masks"], avg)2550 )2551 print(" caches:")2552 print(" masks-cache: size:%d" % masks_cache_size)2553 2554 if user_features is not None:2555 print(" features: 0x%X" % user_features)2556 2557 # port print out2558 for iface in ndb.interfaces:2559 rep = vpl.info(iface.ifname, ifindex)2560 if rep is not None:2561 opts = ""2562 vpo = rep.get_attr("OVS_VPORT_ATTR_OPTIONS")2563 if vpo:2564 dpo = vpo.get_attr("OVS_TUNNEL_ATTR_DST_PORT")2565 if dpo:2566 opts += " tnl-dport:%s" % socket.ntohs(dpo)2567 print(2568 " port %d: %s (%s%s)"2569 % (2570 rep.get_attr("OVS_VPORT_ATTR_PORT_NO"),2571 rep.get_attr("OVS_VPORT_ATTR_NAME"),2572 OvsVport.type_to_str(rep.get_attr("OVS_VPORT_ATTR_TYPE")),2573 opts,2574 )2575 )2576 2577 2578def main(argv):2579 nlmsg_atoms.ovskey = ovskey2580 nlmsg_atoms.ovsactions = ovsactions2581 2582 # version check for pyroute22583 prverscheck = pyroute2.__version__.split(".")2584 if int(prverscheck[0]) == 0 and int(prverscheck[1]) < 6:2585 print("Need to upgrade the python pyroute2 package to >= 0.6.")2586 sys.exit(0)2587 2588 parser = argparse.ArgumentParser()2589 parser.add_argument(2590 "-v",2591 "--verbose",2592 action="count",2593 help="Increment 'verbose' output counter.",2594 default=0,2595 )2596 subparsers = parser.add_subparsers(dest="subcommand")2597 2598 showdpcmd = subparsers.add_parser("show")2599 showdpcmd.add_argument(2600 "showdp", metavar="N", type=str, nargs="?", help="Datapath Name"2601 )2602 2603 adddpcmd = subparsers.add_parser("add-dp")2604 adddpcmd.add_argument("adddp", help="Datapath Name")2605 adddpcmd.add_argument(2606 "-u",2607 "--upcall",2608 action="store_true",2609 help="Leave open a reader for upcalls",2610 )2611 adddpcmd.add_argument(2612 "-V",2613 "--versioning",2614 required=False,2615 help="Specify a custom version / feature string",2616 )2617 2618 deldpcmd = subparsers.add_parser("del-dp")2619 deldpcmd.add_argument("deldp", help="Datapath Name")2620 2621 addifcmd = subparsers.add_parser("add-if")2622 addifcmd.add_argument("dpname", help="Datapath Name")2623 addifcmd.add_argument("addif", help="Interface name for adding")2624 addifcmd.add_argument(2625 "-u",2626 "--upcall",2627 action="store_true",2628 help="Leave open a reader for upcalls",2629 )2630 addifcmd.add_argument(2631 "-t",2632 "--ptype",2633 type=str,2634 default="netdev",2635 choices=["netdev", "internal", "geneve", "vxlan"],2636 help="Interface type (default netdev)",2637 )2638 addifcmd.add_argument(2639 "-p",2640 "--dport",2641 type=int,2642 default=0,2643 help="Destination port (0 for default)"2644 )2645 addifcmd.add_argument(2646 "-l",2647 "--lwt",2648 type=bool,2649 default=True,2650 help="Use LWT infrastructure instead of vport (default true)."2651 )2652 delifcmd = subparsers.add_parser("del-if")2653 delifcmd.add_argument("dpname", help="Datapath Name")2654 delifcmd.add_argument("delif", help="Interface name for adding")2655 delifcmd.add_argument("-d",2656 "--dellink",2657 type=bool, default=False,2658 help="Delete the link as well.")2659 2660 dumpflcmd = subparsers.add_parser("dump-flows")2661 dumpflcmd.add_argument("dumpdp", help="Datapath Name")2662 2663 addflcmd = subparsers.add_parser("add-flow")2664 addflcmd.add_argument("flbr", help="Datapath name")2665 addflcmd.add_argument("flow", help="Flow specification")2666 addflcmd.add_argument("acts", help="Flow actions")2667 2668 delfscmd = subparsers.add_parser("del-flows")2669 delfscmd.add_argument("flsbr", help="Datapath name")2670 2671 subparsers.add_parser("psample-events")2672 2673 args = parser.parse_args()2674 2675 if args.verbose > 0:2676 if args.verbose > 1:2677 logging.basicConfig(level=logging.DEBUG)2678 2679 ovspk = OvsPacket()2680 ovsdp = OvsDatapath()2681 ovsvp = OvsVport(ovspk)2682 ovsflow = OvsFlow()2683 ndb = NDB()2684 2685 sys.setrecursionlimit(100000)2686 2687 if args.subcommand == "psample-events":2688 PsampleEvent().read_samples()2689 2690 if hasattr(args, "showdp"):2691 found = False2692 for iface in ndb.interfaces:2693 rep = None2694 if args.showdp is None:2695 rep = ovsdp.info(iface.ifname, 0)2696 elif args.showdp == iface.ifname:2697 rep = ovsdp.info(iface.ifname, 0)2698 2699 if rep is not None:2700 found = True2701 print_ovsdp_full(rep, iface.index, ndb, ovsvp)2702 2703 if not found:2704 msg = "No DP found"2705 if args.showdp is not None:2706 msg += ":'%s'" % args.showdp2707 print(msg)2708 elif hasattr(args, "adddp"):2709 rep = ovsdp.create(args.adddp, args.upcall, args.versioning, ovspk)2710 if rep is None:2711 print("DP '%s' already exists" % args.adddp)2712 else:2713 print("DP '%s' added" % args.adddp)2714 if args.upcall:2715 ovspk.upcall_handler(ovsflow)2716 elif hasattr(args, "deldp"):2717 ovsdp.destroy(args.deldp)2718 elif hasattr(args, "addif"):2719 rep = ovsdp.info(args.dpname, 0)2720 if rep is None:2721 print("DP '%s' not found." % args.dpname)2722 return 12723 dpindex = rep["dpifindex"]2724 rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype,2725 args.dport, args.lwt)2726 msg = "vport '%s'" % args.addif2727 if rep and rep["header"]["error"] is None:2728 msg += " added."2729 else:2730 msg += " failed to add."2731 if args.upcall:2732 if rep is None:2733 rep = ovsvp.reset_upcall(dpindex, args.addif, ovspk)2734 ovsvp.upcall_handler(ovsflow)2735 elif hasattr(args, "delif"):2736 rep = ovsdp.info(args.dpname, 0)2737 if rep is None:2738 print("DP '%s' not found." % args.dpname)2739 return 12740 rep = ovsvp.detach(rep["dpifindex"], args.delif)2741 msg = "vport '%s'" % args.delif2742 if rep and rep["header"]["error"] is None:2743 msg += " removed."2744 else:2745 msg += " failed to remove."2746 if args.dellink:2747 ipr = pyroute2.iproute.IPRoute()2748 ipr.link("del", index=ipr.link_lookup(ifname=args.delif)[0])2749 elif hasattr(args, "dumpdp"):2750 rep = ovsdp.info(args.dumpdp, 0)2751 if rep is None:2752 print("DP '%s' not found." % args.dumpdp)2753 return 12754 rep = ovsflow.dump(rep["dpifindex"])2755 for flow in rep:2756 print(flow.dpstr(True if args.verbose > 0 else False))2757 elif hasattr(args, "flbr"):2758 rep = ovsdp.info(args.flbr, 0)2759 if rep is None:2760 print("DP '%s' not found." % args.flbr)2761 return 12762 flow = OvsFlow.ovs_flow_msg()2763 flow.parse(args.flow, args.acts, rep["dpifindex"])2764 ovsflow.add_flow(rep["dpifindex"], flow)2765 elif hasattr(args, "flsbr"):2766 rep = ovsdp.info(args.flsbr, 0)2767 if rep is None:2768 print("DP '%s' not found." % args.flsbr)2769 ovsflow.del_flows(rep["dpifindex"])2770 2771 return 02772 2773 2774if __name__ == "__main__":2775 sys.exit(main(sys.argv))2776