1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3 4# Controls the openvswitch module. Part of the kselftest suite, but 5# can be used for some diagnostic purpose as well. 6 7import argparse 8import errno 9import ipaddress 10import logging 11import math 12import multiprocessing 13import re 14import struct 15import sys 16import time 17import types 18import uuid 19 20try: 21 from pyroute2 import NDB 22 23 from pyroute2.netlink import NLA_F_NESTED 24 from pyroute2.netlink import NLM_F_ACK 25 from pyroute2.netlink import NLM_F_DUMP 26 from pyroute2.netlink import NLM_F_REQUEST 27 from pyroute2.netlink import genlmsg 28 from pyroute2.netlink import nla 29 from pyroute2.netlink import nlmsg_atoms 30 from pyroute2.netlink.event import EventSocket 31 from pyroute2.netlink.exceptions import NetlinkError 32 from pyroute2.netlink.generic import GenericNetlinkSocket 33 from pyroute2.netlink.nlsocket import Marshal 34 import pyroute2 35 import pyroute2.iproute 36 37except ModuleNotFoundError: 38 print("Need to install the python pyroute2 package >= 0.6.") 39 sys.exit(1) 40 41 42OVS_DATAPATH_FAMILY = "ovs_datapath" 43OVS_VPORT_FAMILY = "ovs_vport" 44OVS_FLOW_FAMILY = "ovs_flow" 45OVS_PACKET_FAMILY = "ovs_packet" 46OVS_METER_FAMILY = "ovs_meter" 47OVS_CT_LIMIT_FAMILY = "ovs_ct_limit" 48 49OVS_DATAPATH_VERSION = 2 50OVS_DP_CMD_NEW = 1 51OVS_DP_CMD_DEL = 2 52OVS_DP_CMD_GET = 3 53OVS_DP_CMD_SET = 4 54 55OVS_VPORT_CMD_NEW = 1 56OVS_VPORT_CMD_DEL = 2 57OVS_VPORT_CMD_GET = 3 58OVS_VPORT_CMD_SET = 4 59 60OVS_FLOW_CMD_NEW = 1 61OVS_FLOW_CMD_DEL = 2 62OVS_FLOW_CMD_GET = 3 63OVS_FLOW_CMD_SET = 4 64 65UINT32_MAX = 0xFFFFFFFF 66 67def macstr(mac): 68 outstr = ":".join(["%02X" % i for i in mac]) 69 return outstr 70 71 72def strcspn(str1, str2): 73 tot = 0 74 for char in str1: 75 if str2.find(char) != -1: 76 return tot 77 tot += 1 78 return tot 79 80 81def strspn(str1, str2): 82 tot = 0 83 for char in str1: 84 if str2.find(char) == -1: 85 return tot 86 tot += 1 87 return tot 88 89 90def intparse(statestr, defmask="0xffffffff"): 91 totalparse = strspn(statestr, "0123456789abcdefABCDEFx/") 92 # scan until "/" 93 count = strspn(statestr, "x0123456789abcdefABCDEF") 94 95 firstnum = statestr[:count] 96 if firstnum[-1] == "/": 97 firstnum = firstnum[:-1] 98 k = int(firstnum, 0) 99 100 m = None 101 if defmask is not None: 102 secondnum = defmask 103 if statestr[count] == "/": 104 secondnum = statestr[count + 1 :] # this is wrong... 105 m = int(secondnum, 0) 106 107 return statestr[totalparse + 1 :], k, m 108 109 110def parse_flags(flag_str, flag_vals): 111 bitResult = 0 112 maskResult = 0 113 114 if len(flag_str) == 0: 115 return flag_str, bitResult, maskResult 116 117 if flag_str[0].isdigit(): 118 idx = 0 119 while flag_str[idx].isdigit() or flag_str[idx] == "x": 120 idx += 1 121 digits = flag_str[:idx] 122 flag_str = flag_str[idx:] 123 124 bitResult = int(digits, 0) 125 maskResult = int(digits, 0) 126 127 while len(flag_str) > 0 and (flag_str[0] == "+" or flag_str[0] == "-"): 128 if flag_str[0] == "+": 129 setFlag = True 130 elif flag_str[0] == "-": 131 setFlag = False 132 133 flag_str = flag_str[1:] 134 135 flag_len = 0 136 while ( 137 flag_str[flag_len] != "+" 138 and flag_str[flag_len] != "-" 139 and flag_str[flag_len] != "," 140 and flag_str[flag_len] != ")" 141 ): 142 flag_len += 1 143 144 flag = flag_str[0:flag_len] 145 146 if flag in flag_vals: 147 if maskResult & flag_vals[flag]: 148 raise KeyError( 149 "Flag %s set once, cannot be set in multiples" % flag 150 ) 151 152 if setFlag: 153 bitResult |= flag_vals[flag] 154 155 maskResult |= flag_vals[flag] 156 else: 157 raise KeyError("Missing flag value: %s" % flag) 158 159 flag_str = flag_str[flag_len:] 160 161 return flag_str, bitResult, maskResult 162 163 164def parse_ct_state(statestr): 165 ct_flags = { 166 "new": 1 << 0, 167 "est": 1 << 1, 168 "rel": 1 << 2, 169 "rpl": 1 << 3, 170 "inv": 1 << 4, 171 "trk": 1 << 5, 172 "snat": 1 << 6, 173 "dnat": 1 << 7, 174 } 175 176 return parse_flags(statestr, ct_flags) 177 178 179def convert_mac(data): 180 def to_bytes(mac): 181 mac_split = mac.split(":") 182 ret = bytearray([int(i, 16) for i in mac_split]) 183 return bytes(ret) 184 185 mac_str, _, mask_str = data.partition('/') 186 187 if not mac_str: 188 mac_str = mask_str = "00:00:00:00:00:00" 189 elif not mask_str: 190 mask_str = "FF:FF:FF:FF:FF:FF" 191 192 return to_bytes(mac_str), to_bytes(mask_str) 193 194def convert_ipv4(data): 195 ip, _, mask = data.partition('/') 196 197 if not ip: 198 ip = mask = 0 199 elif not mask: 200 mask = 0xFFFFFFFF 201 elif mask.isdigit(): 202 mask = (0xFFFFFFFF << (32 - int(mask))) & 0xFFFFFFFF 203 204 return int(ipaddress.IPv4Address(ip)), int(ipaddress.IPv4Address(mask)) 205 206def convert_ipv6(data): 207 ip, _, mask = data.partition('/') 208 209 if not ip: 210 ip = mask = 0 211 elif not mask: 212 mask = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' 213 elif mask.isdigit(): 214 mask = ipaddress.IPv6Network("::/" + mask).hostmask 215 216 return ipaddress.IPv6Address(ip).packed, ipaddress.IPv6Address(mask).packed 217 218def convert_int(size): 219 def convert_int_sized(data): 220 value, _, mask = data.partition('/') 221 222 if not value: 223 return 0, 0 224 elif not mask: 225 return int(value, 0), pow(2, size) - 1 226 else: 227 return int(value, 0), int(mask, 0) 228 229 return convert_int_sized 230 231def parse_starts_block(block_str, scanstr, returnskipped, scanregex=False): 232 if scanregex: 233 m = re.search(scanstr, block_str) 234 if m is None: 235 if returnskipped: 236 return block_str 237 return False 238 if returnskipped: 239 block_str = block_str[len(m.group(0)) :] 240 return block_str 241 return True 242 243 if block_str.startswith(scanstr): 244 if returnskipped: 245 block_str = block_str[len(scanstr) :] 246 else: 247 return True 248 249 if returnskipped: 250 return block_str 251 252 return False 253 254 255def parse_extract_field( 256 block_str, fieldstr, scanfmt, convert, masked=False, defval=None 257): 258 if fieldstr and not block_str.startswith(fieldstr): 259 return block_str, defval 260 261 if fieldstr: 262 str_skiplen = len(fieldstr) 263 str_skipped = block_str[str_skiplen:] 264 if str_skiplen == 0: 265 return str_skipped, defval 266 else: 267 str_skiplen = 0 268 str_skipped = block_str 269 270 m = re.search(scanfmt, str_skipped) 271 if m is None: 272 raise ValueError("Bad fmt string") 273 274 data = m.group(0) 275 if convert: 276 data = convert(m.group(0)) 277 278 str_skipped = str_skipped[len(m.group(0)) :] 279 if masked: 280 if str_skipped[0] == "/": 281 raise ValueError("Masking support TBD...") 282 283 str_skipped = str_skipped[strspn(str_skipped, ", ") :] 284 return str_skipped, data 285 286 287def parse_attrs(actstr, attr_desc): 288 """Parses the given action string and returns a list of netlink 289 attributes based on a list of attribute descriptions. 290 291 Each element in the attribute description list is a tuple such as: 292 (name, attr_name, parse_func) 293 where: 294 name: is the string representing the attribute 295 attr_name: is the name of the attribute as defined in the uAPI. 296 parse_func: is a callable accepting a string and returning either 297 a single object (the parsed attribute value) or a tuple of 298 two values (the parsed attribute value and the remaining string) 299 300 Returns a list of attributes and the remaining string. 301 """ 302 def parse_attr(actstr, key, func): 303 actstr = actstr[len(key) :] 304 305 if not func: 306 return None, actstr 307 308 delim = actstr[0] 309 actstr = actstr[1:] 310 311 if delim == "=": 312 pos = strcspn(actstr, ",)") 313 ret = func(actstr[:pos]) 314 else: 315 ret = func(actstr) 316 317 if isinstance(ret, tuple): 318 (datum, actstr) = ret 319 else: 320 datum = ret 321 actstr = actstr[strcspn(actstr, ",)"):] 322 323 if delim == "(": 324 if not actstr or actstr[0] != ")": 325 raise ValueError("Action contains unbalanced parentheses") 326 327 actstr = actstr[1:] 328 329 actstr = actstr[strspn(actstr, ", ") :] 330 331 return datum, actstr 332 333 attrs = [] 334 attr_desc = list(attr_desc) 335 while actstr and actstr[0] != ")" and attr_desc: 336 found = False 337 for i, (key, attr, func) in enumerate(attr_desc): 338 if actstr.startswith(key): 339 datum, actstr = parse_attr(actstr, key, func) 340 attrs.append([attr, datum]) 341 found = True 342 del attr_desc[i] 343 344 if not found: 345 raise ValueError("Unknown attribute: '%s'" % actstr) 346 347 actstr = actstr[strspn(actstr, ", ") :] 348 349 if actstr[0] != ")": 350 raise ValueError("Action string contains extra garbage or has " 351 "unbalanced parenthesis: '%s'" % actstr) 352 353 return attrs, actstr[1:] 354 355 356class ovs_dp_msg(genlmsg): 357 # include the OVS version 358 # We need a custom header rather than just being able to rely on 359 # genlmsg because fields ends up not expressing everything correctly 360 # if we use the canonical example of setting fields = (('customfield',),) 361 fields = genlmsg.fields + (("dpifindex", "I"),) 362 363 364class ovsactions(nla): 365 nla_flags = NLA_F_NESTED 366 367 nla_map = ( 368 ("OVS_ACTION_ATTR_UNSPEC", "none"), 369 ("OVS_ACTION_ATTR_OUTPUT", "uint32"), 370 ("OVS_ACTION_ATTR_USERSPACE", "userspace"), 371 ("OVS_ACTION_ATTR_SET", "ovskey"), 372 ("OVS_ACTION_ATTR_PUSH_VLAN", "push_vlan"), 373 ("OVS_ACTION_ATTR_POP_VLAN", "flag"), 374 ("OVS_ACTION_ATTR_SAMPLE", "sample"), 375 ("OVS_ACTION_ATTR_RECIRC", "uint32"), 376 ("OVS_ACTION_ATTR_HASH", "none"), 377 ("OVS_ACTION_ATTR_PUSH_MPLS", "none"), 378 ("OVS_ACTION_ATTR_POP_MPLS", "flag"), 379 ("OVS_ACTION_ATTR_SET_MASKED", "ovskey"), 380 ("OVS_ACTION_ATTR_CT", "ctact"), 381 ("OVS_ACTION_ATTR_TRUNC", "uint32"), 382 ("OVS_ACTION_ATTR_PUSH_ETH", "none"), 383 ("OVS_ACTION_ATTR_POP_ETH", "flag"), 384 ("OVS_ACTION_ATTR_CT_CLEAR", "flag"), 385 ("OVS_ACTION_ATTR_PUSH_NSH", "none"), 386 ("OVS_ACTION_ATTR_POP_NSH", "flag"), 387 ("OVS_ACTION_ATTR_METER", "none"), 388 ("OVS_ACTION_ATTR_CLONE", "recursive"), 389 ("OVS_ACTION_ATTR_CHECK_PKT_LEN", "none"), 390 ("OVS_ACTION_ATTR_ADD_MPLS", "none"), 391 ("OVS_ACTION_ATTR_DEC_TTL", "dec_ttl"), 392 ("OVS_ACTION_ATTR_DROP", "uint32"), 393 ("OVS_ACTION_ATTR_PSAMPLE", "psample"), 394 ) 395 396 class dec_ttl(nla): # pylint: disable=invalid-name 397 """Nested OVS_DEC_TTL_ATTR_* sub-attributes.""" 398 399 nla_flags = NLA_F_NESTED 400 401 nla_map = ( 402 ("OVS_DEC_TTL_ATTR_UNSPEC", "none"), 403 ("OVS_DEC_TTL_ATTR_ACTION", "actions"), 404 ) 405 406 class psample(nla): 407 nla_flags = NLA_F_NESTED 408 409 nla_map = ( 410 ("OVS_PSAMPLE_ATTR_UNSPEC", "none"), 411 ("OVS_PSAMPLE_ATTR_GROUP", "uint32"), 412 ("OVS_PSAMPLE_ATTR_COOKIE", "array(uint8)"), 413 ) 414 415 def dpstr(self, more=False): 416 args = "group=%d" % self.get_attr("OVS_PSAMPLE_ATTR_GROUP") 417 418 cookie = self.get_attr("OVS_PSAMPLE_ATTR_COOKIE") 419 if cookie: 420 args += ",cookie(%s)" % \ 421 "".join(format(x, "02x") for x in cookie) 422 423 return "psample(%s)" % args 424 425 def parse(self, actstr): 426 desc = ( 427 ("group", "OVS_PSAMPLE_ATTR_GROUP", int), 428 ("cookie", "OVS_PSAMPLE_ATTR_COOKIE", 429 lambda x: list(bytearray.fromhex(x))) 430 ) 431 432 attrs, actstr = parse_attrs(actstr, desc) 433 434 for attr in attrs: 435 self["attrs"].append(attr) 436 437 return actstr 438 439 class push_vlan(nla): 440 fields = (("vlan_tpid", "!H"), ("vlan_tci", "!H")) 441 442 class sample(nla): 443 nla_flags = NLA_F_NESTED 444 445 nla_map = ( 446 ("OVS_SAMPLE_ATTR_UNSPEC", "none"), 447 ("OVS_SAMPLE_ATTR_PROBABILITY", "uint32"), 448 ("OVS_SAMPLE_ATTR_ACTIONS", "ovsactions"), 449 ) 450 451 def dpstr(self, more=False): 452 args = [] 453 454 args.append("sample={:.2f}%".format( 455 100 * self.get_attr("OVS_SAMPLE_ATTR_PROBABILITY") / 456 UINT32_MAX)) 457 458 actions = self.get_attr("OVS_SAMPLE_ATTR_ACTIONS") 459 if actions: 460 args.append("actions(%s)" % actions.dpstr(more)) 461 462 return "sample(%s)" % ",".join(args) 463 464 def parse(self, actstr): 465 def parse_nested_actions(actstr): 466 subacts = ovsactions() 467 parsed_len = subacts.parse(actstr) 468 return subacts, actstr[parsed_len :] 469 470 def percent_to_rate(percent): 471 percent = float(percent.strip('%')) 472 return int(math.floor(UINT32_MAX * (percent / 100.0) + .5)) 473 474 desc = ( 475 ("sample", "OVS_SAMPLE_ATTR_PROBABILITY", percent_to_rate), 476 ("actions", "OVS_SAMPLE_ATTR_ACTIONS", parse_nested_actions), 477 ) 478 attrs, actstr = parse_attrs(actstr, desc) 479 480 for attr in attrs: 481 self["attrs"].append(attr) 482 483 return actstr 484 485 class ctact(nla): 486 nla_flags = NLA_F_NESTED 487 488 nla_map = ( 489 ("OVS_CT_ATTR_NONE", "none"), 490 ("OVS_CT_ATTR_COMMIT", "flag"), 491 ("OVS_CT_ATTR_ZONE", "uint16"), 492 ("OVS_CT_ATTR_MARK", "none"), 493 ("OVS_CT_ATTR_LABELS", "none"), 494 ("OVS_CT_ATTR_HELPER", "asciiz"), 495 ("OVS_CT_ATTR_NAT", "natattr"), 496 ("OVS_CT_ATTR_FORCE_COMMIT", "flag"), 497 ("OVS_CT_ATTR_EVENTMASK", "uint32"), 498 ("OVS_CT_ATTR_TIMEOUT", "asciiz"), 499 ) 500 501 class natattr(nla): 502 nla_flags = NLA_F_NESTED 503 504 nla_map = ( 505 ("OVS_NAT_ATTR_NONE", "none"), 506 ("OVS_NAT_ATTR_SRC", "flag"), 507 ("OVS_NAT_ATTR_DST", "flag"), 508 ("OVS_NAT_ATTR_IP_MIN", "ipaddr"), 509 ("OVS_NAT_ATTR_IP_MAX", "ipaddr"), 510 ("OVS_NAT_ATTR_PROTO_MIN", "uint16"), 511 ("OVS_NAT_ATTR_PROTO_MAX", "uint16"), 512 ("OVS_NAT_ATTR_PERSISTENT", "flag"), 513 ("OVS_NAT_ATTR_PROTO_HASH", "flag"), 514 ("OVS_NAT_ATTR_PROTO_RANDOM", "flag"), 515 ) 516 517 def dpstr(self, more=False): 518 print_str = "nat(" 519 520 if self.get_attr("OVS_NAT_ATTR_SRC"): 521 print_str += "src" 522 elif self.get_attr("OVS_NAT_ATTR_DST"): 523 print_str += "dst" 524 else: 525 print_str += "XXX-unknown-nat" 526 527 if self.get_attr("OVS_NAT_ATTR_IP_MIN") or self.get_attr( 528 "OVS_NAT_ATTR_IP_MAX" 529 ): 530 if self.get_attr("OVS_NAT_ATTR_IP_MIN"): 531 print_str += "=%s," % str( 532 self.get_attr("OVS_NAT_ATTR_IP_MIN") 533 ) 534 535 if self.get_attr("OVS_NAT_ATTR_IP_MAX"): 536 print_str += "-%s," % str( 537 self.get_attr("OVS_NAT_ATTR_IP_MAX") 538 ) 539 else: 540 print_str += "," 541 542 if self.get_attr("OVS_NAT_ATTR_PROTO_MIN"): 543 print_str += "proto_min=%d," % self.get_attr( 544 "OVS_NAT_ATTR_PROTO_MIN" 545 ) 546 547 if self.get_attr("OVS_NAT_ATTR_PROTO_MAX"): 548 print_str += "proto_max=%d," % self.get_attr( 549 "OVS_NAT_ATTR_PROTO_MAX" 550 ) 551 552 if self.get_attr("OVS_NAT_ATTR_PERSISTENT"): 553 print_str += "persistent," 554 if self.get_attr("OVS_NAT_ATTR_HASH"): 555 print_str += "hash," 556 if self.get_attr("OVS_NAT_ATTR_RANDOM"): 557 print_str += "random" 558 print_str += ")" 559 return print_str 560 561 def dpstr(self, more=False): 562 print_str = "ct(" 563 564 if self.get_attr("OVS_CT_ATTR_COMMIT") is not None: 565 print_str += "commit," 566 if self.get_attr("OVS_CT_ATTR_ZONE") is not None: 567 print_str += "zone=%d," % self.get_attr("OVS_CT_ATTR_ZONE") 568 if self.get_attr("OVS_CT_ATTR_HELPER") is not None: 569 print_str += "helper=%s," % self.get_attr("OVS_CT_ATTR_HELPER") 570 if self.get_attr("OVS_CT_ATTR_NAT") is not None: 571 print_str += self.get_attr("OVS_CT_ATTR_NAT").dpstr(more) 572 print_str += "," 573 if self.get_attr("OVS_CT_ATTR_FORCE_COMMIT") is not None: 574 print_str += "force," 575 if self.get_attr("OVS_CT_ATTR_EVENTMASK") is not None: 576 print_str += "emask=0x%X," % self.get_attr( 577 "OVS_CT_ATTR_EVENTMASK" 578 ) 579 if self.get_attr("OVS_CT_ATTR_TIMEOUT") is not None: 580 print_str += "timeout=%s" % self.get_attr( 581 "OVS_CT_ATTR_TIMEOUT" 582 ) 583 print_str += ")" 584 return print_str 585 586 class userspace(nla): 587 nla_flags = NLA_F_NESTED 588 589 nla_map = ( 590 ("OVS_USERSPACE_ATTR_UNUSED", "none"), 591 ("OVS_USERSPACE_ATTR_PID", "uint32"), 592 ("OVS_USERSPACE_ATTR_USERDATA", "array(uint8)"), 593 ("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", "uint32"), 594 ) 595 596 def dpstr(self, more=False): 597 print_str = "userspace(" 598 if self.get_attr("OVS_USERSPACE_ATTR_PID") is not None: 599 print_str += "pid=%d," % self.get_attr( 600 "OVS_USERSPACE_ATTR_PID" 601 ) 602 if self.get_attr("OVS_USERSPACE_ATTR_USERDATA") is not None: 603 print_str += "userdata=" 604 for f in self.get_attr("OVS_USERSPACE_ATTR_USERDATA"): 605 print_str += "%x." % f 606 if self.get_attr("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT") is not None: 607 print_str += "egress_tun_port=%d" % self.get_attr( 608 "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT" 609 ) 610 print_str += ")" 611 return print_str 612 613 def parse(self, actstr): 614 attrs_desc = ( 615 ("pid", "OVS_USERSPACE_ATTR_PID", int), 616 ("userdata", "OVS_USERSPACE_ATTR_USERDATA", 617 lambda x: list(bytearray.fromhex(x))), 618 ("egress_tun_port", "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", int) 619 ) 620 621 attrs, actstr = parse_attrs(actstr, attrs_desc) 622 for attr in attrs: 623 self["attrs"].append(attr) 624 625 return actstr 626 627 def dpstr(self, more=False): 628 print_str = "" 629 630 for field in self["attrs"]: 631 if field[1] == "none" or self.get_attr(field[0]) is None: 632 continue 633 if print_str != "": 634 print_str += "," 635 636 if field[0] == "OVS_ACTION_ATTR_OUTPUT": 637 print_str += "%d" % int(self.get_attr(field[0])) 638 elif field[0] == "OVS_ACTION_ATTR_RECIRC": 639 print_str += "recirc(0x%x)" % int(self.get_attr(field[0])) 640 elif field[0] == "OVS_ACTION_ATTR_TRUNC": 641 print_str += "trunc(%d)" % int(self.get_attr(field[0])) 642 elif field[0] == "OVS_ACTION_ATTR_DROP": 643 print_str += "drop(%d)" % int(self.get_attr(field[0])) 644 elif field[0] == "OVS_ACTION_ATTR_CT_CLEAR": 645 print_str += "ct_clear" 646 elif field[0] == "OVS_ACTION_ATTR_POP_VLAN": 647 print_str += "pop_vlan" 648 elif field[0] == "OVS_ACTION_ATTR_DEC_TTL": 649 datum = self.get_attr(field[0]) 650 print_str += "dec_ttl(le_1(" 651 subacts = datum.get_attr("OVS_DEC_TTL_ATTR_ACTION") 652 if subacts and subacts.get("attrs"): 653 print_str += subacts.dpstr(more) 654 print_str += "))" 655 elif field[0] == "OVS_ACTION_ATTR_PUSH_VLAN": 656 datum = self.get_attr(field[0]) 657 tpid = datum["vlan_tpid"] 658 tci = datum["vlan_tci"] 659 vid = tci & 0x0FFF 660 pcp = (tci >> 13) & 0x7 661 print_str += "push_vlan(vid=%d,pcp=%d" \ 662 ",tpid=0x%04x)" % (vid, pcp, tpid) 663 elif field[0] == "OVS_ACTION_ATTR_POP_ETH": 664 print_str += "pop_eth" 665 elif field[0] == "OVS_ACTION_ATTR_POP_NSH": 666 print_str += "pop_nsh" 667 elif field[0] == "OVS_ACTION_ATTR_POP_MPLS": 668 print_str += "pop_mpls" 669 else: 670 datum = self.get_attr(field[0]) 671 if field[0] == "OVS_ACTION_ATTR_CLONE": 672 print_str += "clone(" 673 print_str += datum.dpstr(more) 674 print_str += ")" 675 elif field[0] == "OVS_ACTION_ATTR_SET" or \ 676 field[0] == "OVS_ACTION_ATTR_SET_MASKED": 677 print_str += "set" 678 field = datum 679 mask = None 680 if field[0] == "OVS_ACTION_ATTR_SET_MASKED": 681 print_str += "_masked" 682 field = datum[0] 683 mask = datum[1] 684 print_str += "(" 685 print_str += field.dpstr(mask, more) 686 print_str += ")" 687 else: 688 try: 689 print_str += datum.dpstr(more) 690 except: 691 print_str += "{ATTR: %s not decoded}" % field[0] 692 693 return print_str 694 695 def parse(self, actstr): 696 totallen = len(actstr) 697 while len(actstr) != 0: 698 parsed = False 699 parencount = 0 700 if actstr.startswith("drop"): 701 # If no reason is provided, the implicit drop is used (i.e no 702 # action). If some reason is given, an explicit action is used. 703 reason = None 704 if actstr.startswith("drop("): 705 parencount += 1 706 707 actstr, reason = parse_extract_field( 708 actstr, 709 "drop(", 710 r"([0-9]+)", 711 lambda x: int(x, 0), 712 False, 713 None, 714 ) 715 716 if reason is not None: 717 self["attrs"].append(["OVS_ACTION_ATTR_DROP", reason]) 718 parsed = True 719 else: 720 actstr = actstr[len("drop"): ] 721 return (totallen - len(actstr)) 722 723 elif parse_starts_block(actstr, r"^(\d+)", False, True): 724 actstr, output = parse_extract_field( 725 actstr, None, r"(\d+)", lambda x: int(x), False, "0" 726 ) 727 self["attrs"].append(["OVS_ACTION_ATTR_OUTPUT", output]) 728 parsed = True 729 elif parse_starts_block(actstr, "recirc(", False): 730 actstr, recircid = parse_extract_field( 731 actstr, 732 "recirc(", 733 r"([0-9a-fA-Fx]+)", 734 lambda x: int(x, 0), 735 False, 736 0, 737 ) 738 parencount += 1 739 self["attrs"].append(["OVS_ACTION_ATTR_RECIRC", recircid]) 740 parsed = True 741 742 parse_flat_map = ( 743 ("ct_clear", "OVS_ACTION_ATTR_CT_CLEAR"), 744 ("pop_vlan", "OVS_ACTION_ATTR_POP_VLAN"), 745 ("pop_eth", "OVS_ACTION_ATTR_POP_ETH"), 746 ("pop_nsh", "OVS_ACTION_ATTR_POP_NSH"), 747 ) 748 749 for flat_act in parse_flat_map: 750 if parse_starts_block(actstr, flat_act[0], False): 751 actstr = actstr[len(flat_act[0]):] 752 self["attrs"].append([flat_act[1], True]) 753 actstr = actstr[strspn(actstr, ", ") :] 754 parsed = True 755 756 if parse_starts_block(actstr, "push_vlan(", False): 757 actstr = actstr[len("push_vlan("):] 758 vid = 0 759 pcp = 0 760 tpid = 0x8100 761 if ")" not in actstr: 762 raise ValueError( 763 "push_vlan(): missing ')'") 764 paren = actstr.index(")") 765 if not actstr[:paren].strip(): 766 raise ValueError("push_vlan(): no fields") 767 for kv in actstr[:paren].split(","): 768 if "=" not in kv: 769 raise ValueError( 770 "push_vlan(): bad field '%s'" 771 % kv.strip()) 772 k = kv[:kv.index("=")].strip() 773 v = kv[kv.index("=") + 1:].strip() 774 if k == "vid": 775 vid = int(v, 0) 776 if vid < 0 or vid > 0xFFF: 777 raise ValueError( 778 "push_vlan(): vid=%d out of " 779 "range (0-4095)" % vid) 780 elif k == "pcp": 781 pcp = int(v, 0) 782 if pcp < 0 or pcp > 7: 783 raise ValueError( 784 "push_vlan(): pcp=%d out of " 785 "range (0-7)" % pcp) 786 elif k == "tpid": 787 tpid = int(v, 0) 788 if tpid < 0 or tpid > 0xFFFF: 789 raise ValueError( 790 "push_vlan(): tpid=0x%x out " 791 "of range (0-0xffff)" % tpid) 792 else: 793 raise ValueError( 794 "push_vlan(): unknown key '%s'" 795 % k) 796 tci = (vid & 0x0FFF) | ((pcp & 0x7) << 13) \ 797 | 0x1000 798 pvact = self.push_vlan() 799 pvact["vlan_tpid"] = tpid 800 pvact["vlan_tci"] = tci 801 self["attrs"].append( 802 ["OVS_ACTION_ATTR_PUSH_VLAN", pvact]) 803 actstr = actstr[paren + 1:] 804 parsed = True 805 806 elif parse_starts_block(actstr, "dec_ttl(le_1(", False): 807 parencount += 2 808 subacts = ovsactions() 809 actstr = actstr[len("dec_ttl(le_1("):] 810 parsed_len = subacts.parse(actstr) 811 decttl = ovsactions.dec_ttl() 812 decttl["attrs"].append( 813 ("OVS_DEC_TTL_ATTR_ACTION", subacts) 814 ) 815 self["attrs"].append( 816 ("OVS_ACTION_ATTR_DEC_TTL", decttl) 817 ) 818 actstr = actstr[parsed_len:] 819 parsed = True 820 elif parse_starts_block(actstr, "clone(", False): 821 parencount += 1 822 subacts = ovsactions() 823 actstr = actstr[len("clone("):] 824 parsedLen = subacts.parse(actstr) 825 lst = [] 826 self["attrs"].append(("OVS_ACTION_ATTR_CLONE", subacts)) 827 actstr = actstr[parsedLen:] 828 parsed = True 829 elif parse_starts_block(actstr, "set(", False): 830 parencount += 1 831 k = ovskey() 832 actstr = actstr[len("set("):] 833 actstr = k.parse(actstr, None) 834 self["attrs"].append(("OVS_ACTION_ATTR_SET", k)) 835 if not actstr.startswith(")"): 836 actstr = ")" + actstr 837 parsed = True 838 elif parse_starts_block(actstr, "set_masked(", False): 839 parencount += 1 840 k = ovskey() 841 m = ovskey() 842 actstr = actstr[len("set_masked("):] 843 actstr = k.parse(actstr, m) 844 self["attrs"].append(("OVS_ACTION_ATTR_SET_MASKED", [k, m])) 845 if not actstr.startswith(")"): 846 actstr = ")" + actstr 847 parsed = True 848 elif parse_starts_block(actstr, "ct(", False): 849 parencount += 1 850 actstr = actstr[len("ct(") :] 851 ctact = ovsactions.ctact() 852 853 for scan in ( 854 ("commit", "OVS_CT_ATTR_COMMIT", None), 855 ("force_commit", "OVS_CT_ATTR_FORCE_COMMIT", None), 856 ("zone", "OVS_CT_ATTR_ZONE", int), 857 ("mark", "OVS_CT_ATTR_MARK", int), 858 ("helper", "OVS_CT_ATTR_HELPER", lambda x, y: str(x)), 859 ("timeout", "OVS_CT_ATTR_TIMEOUT", lambda x, y: str(x)), 860 ): 861 if actstr.startswith(scan[0]): 862 actstr = actstr[len(scan[0]) :] 863 if scan[2] is not None: 864 if actstr[0] != "=": 865 raise ValueError("Invalid ct attr") 866 actstr = actstr[1:] 867 pos = strcspn(actstr, ",)") 868 datum = scan[2](actstr[:pos], 0) 869 ctact["attrs"].append([scan[1], datum]) 870 actstr = actstr[pos:] 871 else: 872 ctact["attrs"].append([scan[1], None]) 873 actstr = actstr[strspn(actstr, ", ") :] 874 # it seems strange to put this here, but nat() is a complex 875 # sub-action and this lets it sit anywhere in the ct() action 876 if actstr.startswith("nat"): 877 actstr = actstr[3:] 878 natact = ovsactions.ctact.natattr() 879 880 if actstr.startswith("("): 881 parencount += 1 882 t = None 883 actstr = actstr[1:] 884 if actstr.startswith("src"): 885 t = "OVS_NAT_ATTR_SRC" 886 actstr = actstr[3:] 887 elif actstr.startswith("dst"): 888 t = "OVS_NAT_ATTR_DST" 889 actstr = actstr[3:] 890 891 actstr, ip_block_min = parse_extract_field( 892 actstr, "=", r"([0-9a-fA-F\.]+)", str, False 893 ) 894 actstr, ip_block_max = parse_extract_field( 895 actstr, "-", r"([0-9a-fA-F\.]+)", str, False 896 ) 897 898 actstr, proto_min = parse_extract_field( 899 actstr, ":", r"(\d+)", int, False 900 ) 901 actstr, proto_max = parse_extract_field( 902 actstr, "-", r"(\d+)", int, False 903 ) 904 905 if t is not None: 906 natact["attrs"].append([t, None]) 907 908 if ip_block_min is not None: 909 natact["attrs"].append( 910 ["OVS_NAT_ATTR_IP_MIN", ip_block_min] 911 ) 912 if ip_block_max is not None: 913 natact["attrs"].append( 914 ["OVS_NAT_ATTR_IP_MAX", ip_block_max] 915 ) 916 if proto_min is not None: 917 natact["attrs"].append( 918 ["OVS_NAT_ATTR_PROTO_MIN", proto_min] 919 ) 920 if proto_max is not None: 921 natact["attrs"].append( 922 ["OVS_NAT_ATTR_PROTO_MAX", proto_max] 923 ) 924 925 for natscan in ( 926 ("persistent", "OVS_NAT_ATTR_PERSISTENT"), 927 ("hash", "OVS_NAT_ATTR_PROTO_HASH"), 928 ("random", "OVS_NAT_ATTR_PROTO_RANDOM"), 929 ): 930 if actstr.startswith(natscan[0]): 931 actstr = actstr[len(natscan[0]) :] 932 natact["attrs"].append([natscan[1], None]) 933 actstr = actstr[strspn(actstr, ", ") :] 934 935 ctact["attrs"].append(["OVS_CT_ATTR_NAT", natact]) 936 actstr = actstr[strspn(actstr, ", ") :] 937 938 self["attrs"].append(["OVS_ACTION_ATTR_CT", ctact]) 939 parsed = True 940 941 elif parse_starts_block(actstr, "sample(", False): 942 sampleact = self.sample() 943 actstr = sampleact.parse(actstr[len("sample(") : ]) 944 self["attrs"].append(["OVS_ACTION_ATTR_SAMPLE", sampleact]) 945 parsed = True 946 947 elif parse_starts_block(actstr, "psample(", False): 948 psampleact = self.psample() 949 actstr = psampleact.parse(actstr[len("psample(") : ]) 950 self["attrs"].append(["OVS_ACTION_ATTR_PSAMPLE", psampleact]) 951 parsed = True 952 953 elif parse_starts_block(actstr, "userspace(", False): 954 uact = self.userspace() 955 actstr = uact.parse(actstr[len("userspace(") : ]) 956 self["attrs"].append(["OVS_ACTION_ATTR_USERSPACE", uact]) 957 parsed = True 958 959 elif parse_starts_block(actstr, "trunc(", False): 960 parencount += 1 961 actstr, val = parse_extract_field( 962 actstr, 963 "trunc(", 964 r"([0-9]+)", 965 int, 966 False, 967 None, 968 ) 969 self["attrs"].append(["OVS_ACTION_ATTR_TRUNC", val]) 970 parsed = True 971 972 actstr = actstr[strspn(actstr, ", ") :] 973 while parencount > 0: 974 parencount -= 1 975 actstr = actstr[strspn(actstr, " "):] 976 if len(actstr) and actstr[0] != ")": 977 raise ValueError("Action str: '%s' unbalanced" % actstr) 978 actstr = actstr[1:] 979 980 if len(actstr) and actstr[0] == ")": 981 return (totallen - len(actstr)) 982 983 actstr = actstr[strspn(actstr, ", ") :] 984 985 if not parsed: 986 raise ValueError("Action str: '%s' not supported" % actstr) 987 988 return (totallen - len(actstr)) 989 990 991# pyroute2 resolves nla_map types via getattr(self, name). 992# dec_ttl needs "actions" to resolve to ovsactions, but 993# ovsactions is not defined when dec_ttl class body runs. 994ovsactions.dec_ttl.actions = ovsactions 995 996 997class ovskey(nla): 998 nla_flags = NLA_F_NESTED 999 nla_map = ( 1000 ("OVS_KEY_ATTR_UNSPEC", "none"), 1001 ("OVS_KEY_ATTR_ENCAP", "encap_ovskey"), 1002 ("OVS_KEY_ATTR_PRIORITY", "uint32"), 1003 ("OVS_KEY_ATTR_IN_PORT", "uint32"), 1004 ("OVS_KEY_ATTR_ETHERNET", "ethaddr"), 1005 ("OVS_KEY_ATTR_VLAN", "be16"), 1006 ("OVS_KEY_ATTR_ETHERTYPE", "be16"), 1007 ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"), 1008 ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"), 1009 ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"), 1010 ("OVS_KEY_ATTR_UDP", "ovs_key_udp"), 1011 ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"), 1012 ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"), 1013 ("OVS_KEY_ATTR_ARP", "ovs_key_arp"), 1014 ("OVS_KEY_ATTR_ND", "ovs_key_nd"), 1015 ("OVS_KEY_ATTR_SKB_MARK", "uint32"), 1016 ("OVS_KEY_ATTR_TUNNEL", "ovs_key_tunnel"), 1017 ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"), 1018 ("OVS_KEY_ATTR_TCP_FLAGS", "be16"), 1019 ("OVS_KEY_ATTR_DP_HASH", "uint32"), 1020 ("OVS_KEY_ATTR_RECIRC_ID", "uint32"), 1021 ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"), 1022 ("OVS_KEY_ATTR_CT_STATE", "uint32"), 1023 ("OVS_KEY_ATTR_CT_ZONE", "uint16"), 1024 ("OVS_KEY_ATTR_CT_MARK", "uint32"), 1025 ("OVS_KEY_ATTR_CT_LABELS", "none"), 1026 ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", "ovs_key_ct_tuple_ipv4"), 1027 ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", "ovs_key_ct_tuple_ipv6"), 1028 ("OVS_KEY_ATTR_NSH", "none"), 1029 ("OVS_KEY_ATTR_PACKET_TYPE", "none"), 1030 ("OVS_KEY_ATTR_ND_EXTENSIONS", "none"), 1031 ("OVS_KEY_ATTR_TUNNEL_INFO", "none"), 1032 ("OVS_KEY_ATTR_IPV6_EXTENSIONS", "none"), 1033 ) 1034 1035 class ovs_key_proto(nla): 1036 fields = ( 1037 ("src", "!H"), 1038 ("dst", "!H"), 1039 ) 1040 1041 fields_map = ( 1042 ("src", "src", "%d", lambda x: int(x) if x else 0, 1043 convert_int(16)), 1044 ("dst", "dst", "%d", lambda x: int(x) if x else 0, 1045 convert_int(16)), 1046 ) 1047 1048 def __init__( 1049 self, 1050 protostr, 1051 data=None, 1052 offset=None, 1053 parent=None, 1054 length=None, 1055 init=None, 1056 ): 1057 self.proto_str = protostr 1058 nla.__init__( 1059 self, 1060 data=data, 1061 offset=offset, 1062 parent=parent, 1063 length=length, 1064 init=init, 1065 ) 1066 1067 def parse(self, flowstr, typeInst): 1068 if not flowstr.startswith(self.proto_str): 1069 return None, None 1070 1071 k = typeInst() 1072 m = typeInst() 1073 1074 flowstr = flowstr[len(self.proto_str) :] 1075 if flowstr.startswith("("): 1076 flowstr = flowstr[1:] 1077 1078 keybits = b"" 1079 maskbits = b"" 1080 for f in self.fields_map: 1081 if flowstr.startswith(f[1]): 1082 # the following assumes that the field looks 1083 # something like 'field.' where '.' is a 1084 # character that we don't exactly care about. 1085 flowstr = flowstr[len(f[1]) + 1 :] 1086 splitchar = 0 1087 for c in flowstr: 1088 if c == "," or c == ")": 1089 break 1090 splitchar += 1 1091 data = flowstr[:splitchar] 1092 flowstr = flowstr[splitchar:] 1093 else: 1094 data = "" 1095 1096 if len(f) > 4: 1097 k[f[0]], m[f[0]] = f[4](data) 1098 else: 1099 k[f[0]] = f[3](data) 1100 m[f[0]] = f[3](data) 1101 1102 flowstr = flowstr[strspn(flowstr, ", ") :] 1103 if len(flowstr) == 0: 1104 return flowstr, k, m 1105 1106 flowstr = flowstr[strspn(flowstr, "), ") :] 1107 1108 return flowstr, k, m 1109 1110 def dpstr(self, masked=None, more=False): 1111 outstr = self.proto_str + "(" 1112 first = False 1113 for f in self.fields_map: 1114 if first: 1115 outstr += "," 1116 if masked is None: 1117 outstr += "%s=" % f[0] 1118 if isinstance(f[2], str): 1119 outstr += f[2] % self[f[1]] 1120 else: 1121 outstr += f[2](self[f[1]]) 1122 first = True 1123 elif more or f[3](masked[f[1]]) != 0: 1124 outstr += "%s=" % f[0] 1125 if isinstance(f[2], str): 1126 outstr += f[2] % self[f[1]] 1127 else: 1128 outstr += f[2](self[f[1]]) 1129 outstr += "/" 1130 if isinstance(f[2], str): 1131 outstr += f[2] % masked[f[1]] 1132 else: 1133 outstr += f[2](masked[f[1]]) 1134 first = True 1135 outstr += ")" 1136 return outstr 1137 1138 class ethaddr(ovs_key_proto): 1139 fields = ( 1140 ("src", "!6s"), 1141 ("dst", "!6s"), 1142 ) 1143 1144 fields_map = ( 1145 ( 1146 "src", 1147 "src", 1148 macstr, 1149 lambda x: int.from_bytes(x, "big"), 1150 convert_mac, 1151 ), 1152 ( 1153 "dst", 1154 "dst", 1155 macstr, 1156 lambda x: int.from_bytes(x, "big"), 1157 convert_mac, 1158 ), 1159 ) 1160 1161 def __init__( 1162 self, 1163 data=None, 1164 offset=None, 1165 parent=None, 1166 length=None, 1167 init=None, 1168 ): 1169 ovskey.ovs_key_proto.__init__( 1170 self, 1171 "eth", 1172 data=data, 1173 offset=offset, 1174 parent=parent, 1175 length=length, 1176 init=init, 1177 ) 1178 1179 class ovs_key_ipv4(ovs_key_proto): 1180 fields = ( 1181 ("src", "!I"), 1182 ("dst", "!I"), 1183 ("proto", "B"), 1184 ("tos", "B"), 1185 ("ttl", "B"), 1186 ("frag", "B"), 1187 ) 1188 1189 fields_map = ( 1190 ( 1191 "src", 1192 "src", 1193 lambda x: str(ipaddress.IPv4Address(x)), 1194 int, 1195 convert_ipv4, 1196 ), 1197 ( 1198 "dst", 1199 "dst", 1200 lambda x: str(ipaddress.IPv4Address(x)), 1201 int, 1202 convert_ipv4, 1203 ), 1204 ("proto", "proto", "%d", lambda x: int(x) if x else 0, 1205 convert_int(8)), 1206 ("tos", "tos", "%d", lambda x: int(x) if x else 0, 1207 convert_int(8)), 1208 ("ttl", "ttl", "%d", lambda x: int(x) if x else 0, 1209 convert_int(8)), 1210 ("frag", "frag", "%d", lambda x: int(x) if x else 0, 1211 convert_int(8)), 1212 ) 1213 1214 def __init__( 1215 self, 1216 data=None, 1217 offset=None, 1218 parent=None, 1219 length=None, 1220 init=None, 1221 ): 1222 ovskey.ovs_key_proto.__init__( 1223 self, 1224 "ipv4", 1225 data=data, 1226 offset=offset, 1227 parent=parent, 1228 length=length, 1229 init=init, 1230 ) 1231 1232 class ovs_key_ipv6(ovs_key_proto): 1233 fields = ( 1234 ("src", "!16s"), 1235 ("dst", "!16s"), 1236 ("label", "!I"), 1237 ("proto", "B"), 1238 ("tclass", "B"), 1239 ("hlimit", "B"), 1240 ("frag", "B"), 1241 ) 1242 1243 fields_map = ( 1244 ( 1245 "src", 1246 "src", 1247 lambda x: str(ipaddress.IPv6Address(x)), 1248 lambda x: ipaddress.IPv6Address(x).packed if x else 0, 1249 convert_ipv6, 1250 ), 1251 ( 1252 "dst", 1253 "dst", 1254 lambda x: str(ipaddress.IPv6Address(x)), 1255 lambda x: ipaddress.IPv6Address(x).packed if x else 0, 1256 convert_ipv6, 1257 ), 1258 ("label", "label", "%d", lambda x: int(x) if x else 0, 1259 convert_int(20)), 1260 ("proto", "proto", "%d", lambda x: int(x) if x else 0, 1261 convert_int(8)), 1262 ("tclass", "tclass", "%d", lambda x: int(x) if x else 0, 1263 convert_int(8)), 1264 ("hlimit", "hlimit", "%d", lambda x: int(x) if x else 0, 1265 convert_int(8)), 1266 ("frag", "frag", "%d", lambda x: int(x) if x else 0, 1267 convert_int(8)), 1268 ) 1269 1270 def __init__( 1271 self, 1272 data=None, 1273 offset=None, 1274 parent=None, 1275 length=None, 1276 init=None, 1277 ): 1278 ovskey.ovs_key_proto.__init__( 1279 self, 1280 "ipv6", 1281 data=data, 1282 offset=offset, 1283 parent=parent, 1284 length=length, 1285 init=init, 1286 ) 1287 1288 class ovs_key_tcp(ovs_key_proto): 1289 def __init__( 1290 self, 1291 data=None, 1292 offset=None, 1293 parent=None, 1294 length=None, 1295 init=None, 1296 ): 1297 ovskey.ovs_key_proto.__init__( 1298 self, 1299 "tcp", 1300 data=data, 1301 offset=offset, 1302 parent=parent, 1303 length=length, 1304 init=init, 1305 ) 1306 1307 class ovs_key_udp(ovs_key_proto): 1308 def __init__( 1309 self, 1310 data=None, 1311 offset=None, 1312 parent=None, 1313 length=None, 1314 init=None, 1315 ): 1316 ovskey.ovs_key_proto.__init__( 1317 self, 1318 "udp", 1319 data=data, 1320 offset=offset, 1321 parent=parent, 1322 length=length, 1323 init=init, 1324 ) 1325 1326 class ovs_key_sctp(ovs_key_proto): 1327 def __init__( 1328 self, 1329 data=None, 1330 offset=None, 1331 parent=None, 1332 length=None, 1333 init=None, 1334 ): 1335 ovskey.ovs_key_proto.__init__( 1336 self, 1337 "sctp", 1338 data=data, 1339 offset=offset, 1340 parent=parent, 1341 length=length, 1342 init=init, 1343 ) 1344 1345 class ovs_key_icmp(ovs_key_proto): 1346 fields = ( 1347 ("type", "B"), 1348 ("code", "B"), 1349 ) 1350 1351 fields_map = ( 1352 ("type", "type", "%d", lambda x: int(x) if x else 0, 1353 convert_int(8)), 1354 ("code", "code", "%d", lambda x: int(x) if x else 0, 1355 convert_int(8)), 1356 ) 1357 1358 def __init__( 1359 self, 1360 data=None, 1361 offset=None, 1362 parent=None, 1363 length=None, 1364 init=None, 1365 ): 1366 ovskey.ovs_key_proto.__init__( 1367 self, 1368 "icmp", 1369 data=data, 1370 offset=offset, 1371 parent=parent, 1372 length=length, 1373 init=init, 1374 ) 1375 1376 class ovs_key_icmpv6(ovs_key_icmp): 1377 def __init__( 1378 self, 1379 data=None, 1380 offset=None, 1381 parent=None, 1382 length=None, 1383 init=None, 1384 ): 1385 ovskey.ovs_key_proto.__init__( 1386 self, 1387 "icmpv6", 1388 data=data, 1389 offset=offset, 1390 parent=parent, 1391 length=length, 1392 init=init, 1393 ) 1394 1395 class ovs_key_arp(ovs_key_proto): 1396 fields = ( 1397 ("sip", "!I"), 1398 ("tip", "!I"), 1399 ("op", "!H"), 1400 ("sha", "!6s"), 1401 ("tha", "!6s"), 1402 ("pad", "xx"), 1403 ) 1404 1405 fields_map = ( 1406 ( 1407 "sip", 1408 "sip", 1409 lambda x: str(ipaddress.IPv4Address(x)), 1410 int, 1411 convert_ipv4, 1412 ), 1413 ( 1414 "tip", 1415 "tip", 1416 lambda x: str(ipaddress.IPv4Address(x)), 1417 int, 1418 convert_ipv4, 1419 ), 1420 ("op", "op", "%d", lambda x: int(x) if x else 0), 1421 ( 1422 "sha", 1423 "sha", 1424 macstr, 1425 lambda x: int.from_bytes(x, "big"), 1426 convert_mac, 1427 ), 1428 ( 1429 "tha", 1430 "tha", 1431 macstr, 1432 lambda x: int.from_bytes(x, "big"), 1433 convert_mac, 1434 ), 1435 ) 1436 1437 def __init__( 1438 self, 1439 data=None, 1440 offset=None, 1441 parent=None, 1442 length=None, 1443 init=None, 1444 ): 1445 ovskey.ovs_key_proto.__init__( 1446 self, 1447 "arp", 1448 data=data, 1449 offset=offset, 1450 parent=parent, 1451 length=length, 1452 init=init, 1453 ) 1454 1455 class ovs_key_nd(ovs_key_proto): 1456 fields = ( 1457 ("target", "!16s"), 1458 ("sll", "!6s"), 1459 ("tll", "!6s"), 1460 ) 1461 1462 fields_map = ( 1463 ( 1464 "target", 1465 "target", 1466 lambda x: str(ipaddress.IPv6Address(x)), 1467 convert_ipv6, 1468 ), 1469 ("sll", "sll", macstr, lambda x: int.from_bytes(x, "big")), 1470 ("tll", "tll", macstr, lambda x: int.from_bytes(x, "big")), 1471 ) 1472 1473 def __init__( 1474 self, 1475 data=None, 1476 offset=None, 1477 parent=None, 1478 length=None, 1479 init=None, 1480 ): 1481 ovskey.ovs_key_proto.__init__( 1482 self, 1483 "nd", 1484 data=data, 1485 offset=offset, 1486 parent=parent, 1487 length=length, 1488 init=init, 1489 ) 1490 1491 class ovs_key_ct_tuple_ipv4(ovs_key_proto): 1492 fields = ( 1493 ("src", "!I"), 1494 ("dst", "!I"), 1495 ("tp_src", "!H"), 1496 ("tp_dst", "!H"), 1497 ("proto", "B"), 1498 ) 1499 1500 fields_map = ( 1501 ( 1502 "src", 1503 "src", 1504 lambda x: str(ipaddress.IPv4Address(x)), 1505 int, 1506 convert_ipv4, 1507 ), 1508 ( 1509 "dst", 1510 "dst", 1511 lambda x: str(ipaddress.IPv4Address(x)), 1512 int, 1513 convert_ipv4, 1514 ), 1515 ("tp_src", "tp_src", "%d", int), 1516 ("tp_dst", "tp_dst", "%d", int), 1517 ("proto", "proto", "%d", int), 1518 ) 1519 1520 def __init__( 1521 self, 1522 data=None, 1523 offset=None, 1524 parent=None, 1525 length=None, 1526 init=None, 1527 ): 1528 ovskey.ovs_key_proto.__init__( 1529 self, 1530 "ct_tuple4", 1531 data=data, 1532 offset=offset, 1533 parent=parent, 1534 length=length, 1535 init=init, 1536 ) 1537 1538 class ovs_key_ct_tuple_ipv6(nla): 1539 fields = ( 1540 ("src", "!16s"), 1541 ("dst", "!16s"), 1542 ("tp_src", "!H"), 1543 ("tp_dst", "!H"), 1544 ("proto", "B"), 1545 ) 1546 1547 fields_map = ( 1548 ( 1549 "src", 1550 "src", 1551 lambda x: str(ipaddress.IPv6Address(x)), 1552 convert_ipv6, 1553 ), 1554 ( 1555 "dst", 1556 "dst", 1557 lambda x: str(ipaddress.IPv6Address(x)), 1558 convert_ipv6, 1559 ), 1560 ("tp_src", "tp_src", "%d", int), 1561 ("tp_dst", "tp_dst", "%d", int), 1562 ("proto", "proto", "%d", int), 1563 ) 1564 1565 def __init__( 1566 self, 1567 data=None, 1568 offset=None, 1569 parent=None, 1570 length=None, 1571 init=None, 1572 ): 1573 ovskey.ovs_key_proto.__init__( 1574 self, 1575 "ct_tuple6", 1576 data=data, 1577 offset=offset, 1578 parent=parent, 1579 length=length, 1580 init=init, 1581 ) 1582 1583 class ovs_key_tunnel(nla): 1584 nla_flags = NLA_F_NESTED 1585 1586 nla_map = ( 1587 ("OVS_TUNNEL_KEY_ATTR_ID", "be64"), 1588 ("OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "ipaddr"), 1589 ("OVS_TUNNEL_KEY_ATTR_IPV4_DST", "ipaddr"), 1590 ("OVS_TUNNEL_KEY_ATTR_TOS", "uint8"), 1591 ("OVS_TUNNEL_KEY_ATTR_TTL", "uint8"), 1592 ("OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", "flag"), 1593 ("OVS_TUNNEL_KEY_ATTR_CSUM", "flag"), 1594 ("OVS_TUNNEL_KEY_ATTR_OAM", "flag"), 1595 ("OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS", "array(uint32)"), 1596 ("OVS_TUNNEL_KEY_ATTR_TP_SRC", "be16"), 1597 ("OVS_TUNNEL_KEY_ATTR_TP_DST", "be16"), 1598 ("OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS", "none"), 1599 ("OVS_TUNNEL_KEY_ATTR_IPV6_SRC", "ipaddr"), 1600 ("OVS_TUNNEL_KEY_ATTR_IPV6_DST", "ipaddr"), 1601 ("OVS_TUNNEL_KEY_ATTR_PAD", "none"), 1602 ("OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS", "none"), 1603 ("OVS_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE", "flag"), 1604 ) 1605 1606 def parse(self, flowstr, mask=None): 1607 if not flowstr.startswith("tunnel("): 1608 return None, None 1609 1610 k = ovskey.ovs_key_tunnel() 1611 if mask is not None: 1612 mask = ovskey.ovs_key_tunnel() 1613 1614 flowstr = flowstr[len("tunnel("):] 1615 1616 v6_address = None 1617 1618 fields = [ 1619 ("tun_id=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_ID", 1620 0xffffffffffffffff, None, None), 1621 1622 ("src=", r"([0-9a-fA-F\.]+)", str, 1623 "OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "255.255.255.255", "0.0.0.0", 1624 False), 1625 ("dst=", r"([0-9a-fA-F\.]+)", str, 1626 "OVS_TUNNEL_KEY_ATTR_IPV4_DST", "255.255.255.255", "0.0.0.0", 1627 False), 1628 1629 ("ipv6_src=", r"([0-9a-fA-F:]+)", str, 1630 "OVS_TUNNEL_KEY_ATTR_IPV6_SRC", 1631 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True), 1632 ("ipv6_dst=", r"([0-9a-fA-F:]+)", str, 1633 "OVS_TUNNEL_KEY_ATTR_IPV6_DST", 1634 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True), 1635 1636 ("tos=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TOS", 255, 0, 1637 None), 1638 ("ttl=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TTL", 255, 0, 1639 None), 1640 1641 ("tp_src=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_SRC", 1642 65535, 0, None), 1643 ("tp_dst=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_DST", 1644 65535, 0, None), 1645 ] 1646 1647 forced_include = ["OVS_TUNNEL_KEY_ATTR_TTL"] 1648 1649 for prefix, regex, typ, attr_name, mask_val, default_val, v46_flag in fields: 1650 flowstr, value = parse_extract_field(flowstr, prefix, regex, typ, False) 1651 if not attr_name: 1652 raise Exception("Bad list value in tunnel fields") 1653 1654 if value is None and attr_name in forced_include: 1655 value = default_val 1656 mask_val = default_val 1657 1658 if value is not None: 1659 if v46_flag is not None: 1660 if v6_address is None: 1661 v6_address = v46_flag 1662 if v46_flag != v6_address: 1663 raise ValueError("Cannot mix v6 and v4 addresses") 1664 k["attrs"].append([attr_name, value]) 1665 if mask is not None: 1666 mask["attrs"].append([attr_name, mask_val]) 1667 else: 1668 if v46_flag is not None: 1669 if v6_address is None or v46_flag != v6_address: 1670 continue 1671 if mask is not None: 1672 mask["attrs"].append([attr_name, default_val]) 1673 1674 if k["attrs"][0][0] != "OVS_TUNNEL_KEY_ATTR_ID": 1675 raise ValueError("Needs a tunid set") 1676 1677 if flowstr.startswith("flags("): 1678 flowstr = flowstr[len("flags("):] 1679 flagspos = flowstr.find(")") 1680 flags = flowstr[:flagspos] 1681 flowstr = flowstr[flagspos + 1:] 1682 1683 flag_attrs = { 1684 "df": "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", 1685 "csum": "OVS_TUNNEL_KEY_ATTR_CSUM", 1686 "oam": "OVS_TUNNEL_KEY_ATTR_OAM" 1687 } 1688 1689 for flag in flags.split("|"): 1690 if flag in flag_attrs: 1691 k["attrs"].append([flag_attrs[flag], True]) 1692 if mask is not None: 1693 mask["attrs"].append([flag_attrs[flag], True]) 1694 1695 flowstr = flowstr[strspn(flowstr, ", ") :] 1696 return flowstr, k, mask 1697 1698 def dpstr(self, mask=None, more=False): 1699 print_str = "tunnel(" 1700 1701 flagsattrs = [] 1702 for k in self["attrs"]: 1703 noprint = False 1704 if k[0] == "OVS_TUNNEL_KEY_ATTR_ID": 1705 print_str += "tun_id=%d" % k[1] 1706 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_SRC": 1707 print_str += "src=%s" % k[1] 1708 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_DST": 1709 print_str += "dst=%s" % k[1] 1710 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_SRC": 1711 print_str += "ipv6_src=%s" % k[1] 1712 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_DST": 1713 print_str += "ipv6_dst=%s" % k[1] 1714 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TOS": 1715 print_str += "tos=%d" % k[1] 1716 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TTL": 1717 print_str += "ttl=%d" % k[1] 1718 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_SRC": 1719 print_str += "tp_src=%d" % k[1] 1720 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_DST": 1721 print_str += "tp_dst=%d" % k[1] 1722 elif k[0] == "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT": 1723 noprint = True 1724 flagsattrs.append("df") 1725 elif k[0] == "OVS_TUNNEL_KEY_ATTR_CSUM": 1726 noprint = True 1727 flagsattrs.append("csum") 1728 elif k[0] == "OVS_TUNNEL_KEY_ATTR_OAM": 1729 noprint = True 1730 flagsattrs.append("oam") 1731 1732 if not noprint: 1733 print_str += "," 1734 1735 if len(flagsattrs): 1736 print_str += "flags(" + "|".join(flagsattrs) + ")" 1737 print_str += ")" 1738 return print_str 1739 1740 class ovs_key_mpls(nla): 1741 fields = (("lse", ">I"),) 1742 1743 # 802.1Q CFI (Canonical Format Indicator) bit, always set for Ethernet 1744 _VLAN_CFI_MASK = 0x1000 1745 1746 @staticmethod 1747 def _vlan_dpstr(tci): 1748 """Format VLAN TCI as vid=X,pcp=Y,cfi=Z or tci=0xNNNN. 1749 1750 When cfi=1 (standard Ethernet VLAN), outputs decomposed 1751 vid/pcp/cfi fields. When cfi=0 (truncated VLAN header), 1752 falls back to raw tci=0x%04x to ensure round-trip 1753 correctness: the parser auto-adds cfi=1 for vid/pcp 1754 format, so cfi=0 would be lost on re-parse.""" 1755 vid = tci & 0x0FFF 1756 pcp = (tci >> 13) & 0x7 1757 cfi = (tci >> 12) & 0x1 1758 if cfi: 1759 return "vid=%d,pcp=%d,cfi=%d" % (vid, pcp, cfi) 1760 return "tci=0x%04x" % tci 1761 1762 @staticmethod 1763 def _parse_vlan_from_flowstr(flowstr): 1764 """Parse vlan(tci=X) or vlan(vid=X[,pcp=Y,cfi=Z]) from flowstr. 1765 1766 Returns (remaining_flowstr, key_tci, mask_tci). 1767 TCI values use standard bit layout (VID bits 0-11, 1768 CFI bit 12, PCP bits 13-15); byte order conversion to 1769 big-endian happens in pyroute2 be16 NLA serialization. 1770 The mask covers only the fields the caller specified: 1771 vid -> 0x0FFF, pcp -> 0xE000, cfi -> 0x1000, tci -> 0xFFFF. 1772 1773 The tci= key sets the raw TCI bitfield (no CFI validation) to allow 1774 non-Ethernet use cases. Use cfi=1 for standard Ethernet VLAN matching. 1775 """ 1776 tci = 0 1777 mask = 0 1778 has_tci = False 1779 has_vid = has_pcp = has_cfi = False 1780 _tci_mix_err = "vlan(): 'tci' cannot be mixed " \ 1781 "with 'vid'/'pcp'/'cfi'" 1782 first = True 1783 while True: 1784 flowstr = flowstr.lstrip() 1785 if not flowstr: 1786 raise ValueError("vlan(): missing ')'") 1787 if flowstr[0] == ')': 1788 break 1789 if not first: 1790 flowstr = flowstr[1:] # skip ',' 1791 if not flowstr: 1792 raise ValueError("vlan(): missing ')' after trailing comma") 1793 flowstr = flowstr.lstrip() 1794 if flowstr and flowstr[0] == ')': 1795 break 1796 if flowstr and flowstr[0] == ',': 1797 raise ValueError( 1798 "vlan(): empty or extra comma in field list") 1799 first = False 1800 1801 eq = flowstr.find('=') 1802 if eq == -1: 1803 raise ValueError( 1804 "vlan(): expected key=value, got '%s'" % flowstr) 1805 key = flowstr[:eq].strip() 1806 flowstr = flowstr[eq + 1:] 1807 1808 end = flowstr.find(',') 1809 end2 = flowstr.find(')') 1810 if end == -1 and end2 == -1: 1811 raise ValueError("vlan(): missing ')'") 1812 if end == -1 or (end2 != -1 and end2 < end): 1813 end = end2 1814 val = flowstr[:end].strip() 1815 flowstr = flowstr[end:] 1816 1817 if not val: 1818 raise ValueError("vlan(): empty value for key '%s'" % key) 1819 try: 1820 v = int(val, 0) 1821 except ValueError as exc: 1822 raise ValueError( 1823 "vlan(): invalid value '%s' for key '%s'" 1824 % (val, key)) from exc 1825 1826 if key == 'tci': 1827 if has_tci: 1828 raise ValueError("vlan(): duplicate 'tci'") 1829 if has_vid or has_pcp or has_cfi: 1830 raise ValueError(_tci_mix_err) 1831 if v > 0xFFFF or v < 0: 1832 raise ValueError("vlan(): tci=0x%x out of range" % v) 1833 tci = v 1834 mask = 0xFFFF 1835 has_tci = True 1836 elif key == 'vid': 1837 if has_tci: 1838 raise ValueError(_tci_mix_err) 1839 if has_vid: 1840 raise ValueError("vlan(): duplicate 'vid'") 1841 if v < 0 or v > 0xFFF: 1842 raise ValueError("vlan(): vid=%d out of range (0-4095)" % v) 1843 tci |= v 1844 mask |= 0x0FFF 1845 has_vid = True 1846 elif key == 'pcp': 1847 if has_tci: 1848 raise ValueError(_tci_mix_err) 1849 if has_pcp: 1850 raise ValueError("vlan(): duplicate 'pcp'") 1851 if v < 0 or v > 7: 1852 raise ValueError("vlan(): pcp=%d out of range (0-7)" % v) 1853 tci |= (v & 0x7) << 13 1854 mask |= 0xE000 1855 has_pcp = True 1856 elif key == 'cfi': 1857 if has_tci: 1858 raise ValueError(_tci_mix_err) 1859 if has_cfi: 1860 raise ValueError("vlan(): duplicate 'cfi'") 1861 if v != 1: 1862 raise ValueError("vlan(): cfi must be 1 for Ethernet") 1863 tci |= ovskey._VLAN_CFI_MASK 1864 mask |= ovskey._VLAN_CFI_MASK 1865 has_cfi = True 1866 else: 1867 raise ValueError("vlan(): unknown key '%s'" % key) 1868 1869 flowstr = flowstr[1:] # skip ')' 1870 # Catch immediate '))' (user error). A ')' after ',' is consumed 1871 # by parse()'s strspn(flowstr, "), ") inter-field separator stripping. 1872 if flowstr.lstrip().startswith(')'): 1873 raise ValueError("vlan(): unmatched ')'") 1874 # parse() strips trailing ',', ')', ' ' as inter-field separators, 1875 # so we do not need to call strspn here. 1876 1877 if mask == 0: 1878 raise ValueError("vlan(): no fields specified, " 1879 "use vlan(vid=X[,pcp=Y,cfi=Z]) or vlan(tci=X)") 1880 if not has_tci: 1881 tci |= ovskey._VLAN_CFI_MASK 1882 mask |= ovskey._VLAN_CFI_MASK 1883 return flowstr, tci, mask 1884 1885 @staticmethod 1886 def _parse_encap_from_flowstr(flowstr): 1887 """Parse encap(inner_flow) from flowstr. 1888 1889 Returns (remaining_flowstr, inner_key_dict, inner_mask_dict) 1890 where each dict has an 'attrs' key for recursive NLA encoding. 1891 Parenthesis-depth tracking handles nested encap() calls but not 1892 quoted strings containing literal parentheses. 1893 """ 1894 depth = 1 1895 end = -1 1896 for i, c in enumerate(flowstr): 1897 if c == '(': 1898 depth += 1 1899 elif c == ')': 1900 depth -= 1 1901 if depth < 0: 1902 raise ValueError( 1903 "encap(): unmatched ')' at position %d" % i) 1904 if depth == 0: 1905 end = i 1906 break 1907 1908 if end == -1: 1909 if depth > 1: 1910 raise ValueError("encap(): missing ')' in nested encap") 1911 raise ValueError("encap(): missing ')'") 1912 1913 inner_str = flowstr[:end].strip() 1914 if not inner_str: 1915 raise ValueError("encap(): empty inner flow") 1916 1917 flowstr = flowstr[end + 1:] 1918 if flowstr.lstrip().startswith(')'): 1919 raise ValueError("encap(): unmatched ')' after encap()") 1920 1921 inner_key = encap_ovskey() 1922 inner_mask = encap_ovskey() 1923 remaining = inner_key.parse(inner_str, inner_mask) 1924 if remaining and re.search(r'[^\s,)]', remaining): 1925 raise ValueError( 1926 "encap(): unrecognized trailing " 1927 "content '%s'" % remaining.strip()) 1928 1929 return flowstr, inner_key, inner_mask 1930 1931 def parse(self, flowstr, mask=None): 1932 for field in ( 1933 ("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse), 1934 ("OVS_KEY_ATTR_SKB_MARK", "skb_mark", intparse), 1935 ("OVS_KEY_ATTR_RECIRC_ID", "recirc_id", intparse), 1936 ("OVS_KEY_ATTR_TUNNEL", "tunnel", ovskey.ovs_key_tunnel), 1937 ("OVS_KEY_ATTR_DP_HASH", "dp_hash", intparse), 1938 ("OVS_KEY_ATTR_CT_STATE", "ct_state", parse_ct_state), 1939 ("OVS_KEY_ATTR_CT_ZONE", "ct_zone", intparse), 1940 ("OVS_KEY_ATTR_CT_MARK", "ct_mark", intparse), 1941 ("OVS_KEY_ATTR_IN_PORT", "in_port", intparse), 1942 ( 1943 "OVS_KEY_ATTR_ETHERNET", 1944 "eth", 1945 ovskey.ethaddr, 1946 ), 1947 ( 1948 "OVS_KEY_ATTR_ETHERTYPE", 1949 "eth_type", 1950 lambda x: intparse(x, "0xffff"), 1951 ), 1952 ( 1953 "OVS_KEY_ATTR_VLAN", 1954 "vlan", 1955 ovskey._parse_vlan_from_flowstr, 1956 ), 1957 ( 1958 "OVS_KEY_ATTR_ENCAP", 1959 "encap", 1960 ovskey._parse_encap_from_flowstr, 1961 ), 1962 ( 1963 "OVS_KEY_ATTR_IPV4", 1964 "ipv4", 1965 ovskey.ovs_key_ipv4, 1966 ), 1967 ( 1968 "OVS_KEY_ATTR_IPV6", 1969 "ipv6", 1970 ovskey.ovs_key_ipv6, 1971 ), 1972 ( 1973 "OVS_KEY_ATTR_ARP", 1974 "arp", 1975 ovskey.ovs_key_arp, 1976 ), 1977 ( 1978 "OVS_KEY_ATTR_TCP", 1979 "tcp", 1980 ovskey.ovs_key_tcp, 1981 ), 1982 ( 1983 "OVS_KEY_ATTR_UDP", 1984 "udp", 1985 ovskey.ovs_key_udp, 1986 ), 1987 ( 1988 "OVS_KEY_ATTR_ICMP", 1989 "icmp", 1990 ovskey.ovs_key_icmp, 1991 ), 1992 ( 1993 "OVS_KEY_ATTR_ICMPV6", 1994 "icmpv6", 1995 ovskey.ovs_key_icmpv6, 1996 ), 1997 ( 1998 "OVS_KEY_ATTR_TCP_FLAGS", 1999 "tcp_flags", 2000 lambda x: parse_flags(x, None), 2001 ), 2002 ): 2003 fld = field[1] + "(" 2004 if not flowstr.startswith(fld): 2005 continue 2006 2007 if not isinstance(field[2], types.FunctionType): 2008 nk = field[2]() 2009 flowstr, k, m = nk.parse(flowstr, field[2]) 2010 else: 2011 flowstr = flowstr[len(fld) :] 2012 flowstr, k, m = field[2](flowstr) 2013 2014 if m and mask is not None: 2015 mask["attrs"].append([field[0], m]) 2016 self["attrs"].append([field[0], k]) 2017 2018 flowstr = flowstr[strspn(flowstr, "), ") :] 2019 2020 return flowstr 2021 2022 def dpstr(self, mask=None, more=False): 2023 print_str = "" 2024 2025 for field in ( 2026 ( 2027 "OVS_KEY_ATTR_PRIORITY", 2028 "skb_priority", 2029 "%d", 2030 lambda x: False, 2031 True, 2032 ), 2033 ( 2034 "OVS_KEY_ATTR_SKB_MARK", 2035 "skb_mark", 2036 "%d", 2037 lambda x: False, 2038 True, 2039 ), 2040 ( 2041 "OVS_KEY_ATTR_RECIRC_ID", 2042 "recirc_id", 2043 "0x%08X", 2044 lambda x: False, 2045 True, 2046 ), 2047 ( 2048 "OVS_KEY_ATTR_DP_HASH", 2049 "dp_hash", 2050 "0x%08X", 2051 lambda x: False, 2052 True, 2053 ), 2054 ( 2055 "OVS_KEY_ATTR_TUNNEL", 2056 "tunnel", 2057 None, 2058 False, 2059 False, 2060 ), 2061 ( 2062 "OVS_KEY_ATTR_CT_STATE", 2063 "ct_state", 2064 "0x%04x", 2065 lambda x: False, 2066 True, 2067 ), 2068 ( 2069 "OVS_KEY_ATTR_CT_ZONE", 2070 "ct_zone", 2071 "0x%04x", 2072 lambda x: False, 2073 True, 2074 ), 2075 ( 2076 "OVS_KEY_ATTR_CT_MARK", 2077 "ct_mark", 2078 "0x%08x", 2079 lambda x: False, 2080 True, 2081 ), 2082 ( 2083 "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", 2084 None, 2085 None, 2086 False, 2087 False, 2088 ), 2089 ( 2090 "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", 2091 None, 2092 None, 2093 False, 2094 False, 2095 ), 2096 ( 2097 "OVS_KEY_ATTR_IN_PORT", 2098 "in_port", 2099 "%d", 2100 lambda x: True, 2101 True, 2102 ), 2103 ("OVS_KEY_ATTR_ETHERNET", None, None, False, False), 2104 ("OVS_KEY_ATTR_VLAN", "vlan", ovskey._vlan_dpstr, 2105 lambda x: False, True), 2106 ("OVS_KEY_ATTR_ENCAP", None, None, False, False), 2107 ( 2108 "OVS_KEY_ATTR_ETHERTYPE", 2109 "eth_type", 2110 "0x%04x", 2111 lambda x: int(x) == 0xFFFF, 2112 True, 2113 ), 2114 ("OVS_KEY_ATTR_IPV4", None, None, False, False), 2115 ("OVS_KEY_ATTR_IPV6", None, None, False, False), 2116 ("OVS_KEY_ATTR_ARP", None, None, False, False), 2117 ("OVS_KEY_ATTR_TCP", None, None, False, False), 2118 ( 2119 "OVS_KEY_ATTR_TCP_FLAGS", 2120 "tcp_flags", 2121 "0x%04x", 2122 lambda x: False, 2123 True, 2124 ), 2125 ("OVS_KEY_ATTR_UDP", None, None, False, False), 2126 ("OVS_KEY_ATTR_SCTP", None, None, False, False), 2127 ("OVS_KEY_ATTR_ICMP", None, None, False, False), 2128 ("OVS_KEY_ATTR_ICMPV6", None, None, False, False), 2129 ("OVS_KEY_ATTR_ND", None, None, False, False), 2130 ): 2131 v = self.get_attr(field[0]) 2132 if v is not None: 2133 m = None if mask is None else mask.get_attr(field[0]) 2134 fmt = field[2] # str format or callable 2135 if field[4] is False: 2136 print_str += v.dpstr(m, more) 2137 print_str += "," 2138 else: 2139 if m is None or field[3](m): 2140 val = fmt(v) if callable(fmt) else fmt % v 2141 print_str += field[1] + "(" + val + ")," 2142 elif more or m != 0: 2143 if field[0] == "OVS_KEY_ATTR_VLAN": 2144 val = "tci=0x%04x/0x%04x" % (v, m) 2145 elif callable(fmt): 2146 val = fmt(v) + "/" + fmt(m) 2147 else: 2148 val = (fmt % v) + "/" + (fmt % m) 2149 print_str += field[1] + "(" + val + ")," 2150 2151 return print_str 2152 2153 2154class encap_ovskey(ovskey): 2155 """Inner flow key attributes valid inside 802.1Q ENCAP. 2156 2157 Only L2-L4 key attributes (slots 0-21) appear inside ENCAP. 2158 Metadata-only attributes (SKB_MARK, DP_HASH, RECIRC_ID, etc.) 2159 are set to "none" -- they never appear inside ENCAP per 2160 ovs_nla_put_vlan() in net/openvswitch/flow_netlink.c. 2161 2162 nla_map indexes must match OVS_KEY_ATTR_* enum values in 2163 include/uapi/linux/openvswitch.h. 2164 """ 2165 nla_map = ( 2166 ("OVS_KEY_ATTR_UNSPEC", "none"), 2167 ("OVS_KEY_ATTR_ENCAP", "none"), # placeholder, parsed by ovskey 2168 ("OVS_KEY_ATTR_PRIORITY", "none"), # skb metadata, not in ENCAP 2169 ("OVS_KEY_ATTR_IN_PORT", "none"), # skb metadata, not in ENCAP 2170 ("OVS_KEY_ATTR_ETHERNET", "ethaddr"), 2171 ("OVS_KEY_ATTR_VLAN", "be16"), 2172 ("OVS_KEY_ATTR_ETHERTYPE", "be16"), 2173 ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"), 2174 ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"), 2175 ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"), 2176 ("OVS_KEY_ATTR_UDP", "ovs_key_udp"), 2177 ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"), 2178 ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"), 2179 ("OVS_KEY_ATTR_ARP", "ovs_key_arp"), 2180 ("OVS_KEY_ATTR_ND", "ovs_key_nd"), 2181 ("OVS_KEY_ATTR_SKB_MARK", "none"), # metadata, not in ENCAP 2182 ("OVS_KEY_ATTR_TUNNEL", "none"), # tunnel metadata, not in ENCAP 2183 ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"), 2184 ("OVS_KEY_ATTR_TCP_FLAGS", "be16"), 2185 ("OVS_KEY_ATTR_DP_HASH", "none"), # metadata, not in ENCAP 2186 ("OVS_KEY_ATTR_RECIRC_ID", "none"), # metadata, not in ENCAP 2187 ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"), 2188 ) 2189 2190 2191class OvsPacket(GenericNetlinkSocket): 2192 OVS_PACKET_CMD_MISS = 1 # Flow table miss 2193 OVS_PACKET_CMD_ACTION = 2 # USERSPACE action 2194 OVS_PACKET_CMD_EXECUTE = 3 # Apply actions to packet 2195 2196 class ovs_packet_msg(ovs_dp_msg): 2197 nla_map = ( 2198 ("OVS_PACKET_ATTR_UNSPEC", "none"), 2199 ("OVS_PACKET_ATTR_PACKET", "array(uint8)"), 2200 ("OVS_PACKET_ATTR_KEY", "ovskey"), 2201 ("OVS_PACKET_ATTR_ACTIONS", "ovsactions"), 2202 ("OVS_PACKET_ATTR_USERDATA", "none"), 2203 ("OVS_PACKET_ATTR_EGRESS_TUN_KEY", "none"), 2204 ("OVS_PACKET_ATTR_UNUSED1", "none"), 2205 ("OVS_PACKET_ATTR_UNUSED2", "none"), 2206 ("OVS_PACKET_ATTR_PROBE", "none"), 2207 ("OVS_PACKET_ATTR_MRU", "uint16"), 2208 ("OVS_PACKET_ATTR_LEN", "uint32"), 2209 ("OVS_PACKET_ATTR_HASH", "uint64"), 2210 ) 2211 2212 def __init__(self): 2213 GenericNetlinkSocket.__init__(self) 2214 self.bind(OVS_PACKET_FAMILY, OvsPacket.ovs_packet_msg) 2215 2216 def upcall_handler(self, up=None): 2217 print("listening on upcall packet handler:", self.epid) 2218 while True: 2219 try: 2220 msgs = self.get() 2221 for msg in msgs: 2222 if not up: 2223 continue 2224 if msg["cmd"] == OvsPacket.OVS_PACKET_CMD_MISS: 2225 up.miss(msg) 2226 elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_ACTION: 2227 up.action(msg) 2228 elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_EXECUTE: 2229 up.execute(msg) 2230 else: 2231 print("Unknown cmd: %d" % msg["cmd"]) 2232 except NetlinkError as ne: 2233 raise ne 2234 2235 2236class OvsDatapath(GenericNetlinkSocket): 2237 OVS_DP_F_VPORT_PIDS = 1 << 1 2238 OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3 2239 2240 class dp_cmd_msg(ovs_dp_msg): 2241 """ 2242 Message class that will be used to communicate with the kernel module. 2243 """ 2244 2245 nla_map = ( 2246 ("OVS_DP_ATTR_UNSPEC", "none"), 2247 ("OVS_DP_ATTR_NAME", "asciiz"), 2248 ("OVS_DP_ATTR_UPCALL_PID", "array(uint32)"), 2249 ("OVS_DP_ATTR_STATS", "dpstats"), 2250 ("OVS_DP_ATTR_MEGAFLOW_STATS", "megaflowstats"), 2251 ("OVS_DP_ATTR_USER_FEATURES", "uint32"), 2252 ("OVS_DP_ATTR_PAD", "none"), 2253 ("OVS_DP_ATTR_MASKS_CACHE_SIZE", "uint32"), 2254 ("OVS_DP_ATTR_PER_CPU_PIDS", "array(uint32)"), 2255 ) 2256 2257 class dpstats(nla): 2258 fields = ( 2259 ("hit", "=Q"), 2260 ("missed", "=Q"), 2261 ("lost", "=Q"), 2262 ("flows", "=Q"), 2263 ) 2264 2265 class megaflowstats(nla): 2266 fields = ( 2267 ("mask_hit", "=Q"), 2268 ("masks", "=I"), 2269 ("padding", "=I"), 2270 ("cache_hits", "=Q"), 2271 ("pad1", "=Q"), 2272 ) 2273 2274 def __init__(self): 2275 GenericNetlinkSocket.__init__(self) 2276 self.bind(OVS_DATAPATH_FAMILY, OvsDatapath.dp_cmd_msg) 2277 2278 def info(self, dpname, ifindex=0): 2279 msg = OvsDatapath.dp_cmd_msg() 2280 msg["cmd"] = OVS_DP_CMD_GET 2281 msg["version"] = OVS_DATAPATH_VERSION 2282 msg["reserved"] = 0 2283 msg["dpifindex"] = ifindex 2284 msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname]) 2285 2286 try: 2287 reply = self.nlm_request( 2288 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST 2289 ) 2290 reply = reply[0] 2291 except NetlinkError as ne: 2292 if ne.code == errno.ENODEV: 2293 reply = None 2294 else: 2295 raise ne 2296 2297 return reply 2298 2299 def create( 2300 self, dpname, shouldUpcall=False, versionStr=None, p=OvsPacket() 2301 ): 2302 msg = OvsDatapath.dp_cmd_msg() 2303 msg["cmd"] = OVS_DP_CMD_NEW 2304 if versionStr is None: 2305 msg["version"] = OVS_DATAPATH_VERSION 2306 else: 2307 msg["version"] = int(versionStr.split(":")[0], 0) 2308 msg["reserved"] = 0 2309 msg["dpifindex"] = 0 2310 msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname]) 2311 2312 dpfeatures = 0 2313 if versionStr is not None and versionStr.find(":") != -1: 2314 dpfeatures = int(versionStr.split(":")[1], 0) 2315 else: 2316 if versionStr is None or versionStr.find(":") == -1: 2317 dpfeatures |= OvsDatapath.OVS_DP_F_DISPATCH_UPCALL_PER_CPU 2318 dpfeatures &= ~OvsDatapath.OVS_DP_F_VPORT_PIDS 2319 2320 nproc = multiprocessing.cpu_count() 2321 procarray = [] 2322 for i in range(1, nproc): 2323 procarray += [int(p.epid)] 2324 msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", procarray]) 2325 msg["attrs"].append(["OVS_DP_ATTR_USER_FEATURES", dpfeatures]) 2326 if not shouldUpcall: 2327 msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", [0]]) 2328 2329 try: 2330 reply = self.nlm_request( 2331 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2332 ) 2333 reply = reply[0] 2334 except NetlinkError as ne: 2335 if ne.code == errno.EEXIST: 2336 reply = None 2337 else: 2338 raise ne 2339 2340 return reply 2341 2342 def destroy(self, dpname): 2343 msg = OvsDatapath.dp_cmd_msg() 2344 msg["cmd"] = OVS_DP_CMD_DEL 2345 msg["version"] = OVS_DATAPATH_VERSION 2346 msg["reserved"] = 0 2347 msg["dpifindex"] = 0 2348 msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname]) 2349 2350 try: 2351 reply = self.nlm_request( 2352 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2353 ) 2354 reply = reply[0] 2355 except NetlinkError as ne: 2356 if ne.code == errno.ENODEV: 2357 reply = None 2358 else: 2359 raise ne 2360 2361 return reply 2362 2363 2364class OvsVport(GenericNetlinkSocket): 2365 OVS_VPORT_TYPE_NETDEV = 1 2366 OVS_VPORT_TYPE_INTERNAL = 2 2367 OVS_VPORT_TYPE_GRE = 3 2368 OVS_VPORT_TYPE_VXLAN = 4 2369 OVS_VPORT_TYPE_GENEVE = 5 2370 2371 class ovs_vport_msg(ovs_dp_msg): 2372 nla_map = ( 2373 ("OVS_VPORT_ATTR_UNSPEC", "none"), 2374 ("OVS_VPORT_ATTR_PORT_NO", "uint32"), 2375 ("OVS_VPORT_ATTR_TYPE", "uint32"), 2376 ("OVS_VPORT_ATTR_NAME", "asciiz"), 2377 ("OVS_VPORT_ATTR_OPTIONS", "vportopts"), 2378 ("OVS_VPORT_ATTR_UPCALL_PID", "array(uint32)"), 2379 ("OVS_VPORT_ATTR_STATS", "vportstats"), 2380 ("OVS_VPORT_ATTR_PAD", "none"), 2381 ("OVS_VPORT_ATTR_IFINDEX", "uint32"), 2382 ("OVS_VPORT_ATTR_NETNSID", "uint32"), 2383 ) 2384 2385 class vportopts(nla): 2386 nla_map = ( 2387 ("OVS_TUNNEL_ATTR_UNSPEC", "none"), 2388 ("OVS_TUNNEL_ATTR_DST_PORT", "uint16"), 2389 ("OVS_TUNNEL_ATTR_EXTENSION", "none"), 2390 ) 2391 2392 class vportstats(nla): 2393 fields = ( 2394 ("rx_packets", "=Q"), 2395 ("tx_packets", "=Q"), 2396 ("rx_bytes", "=Q"), 2397 ("tx_bytes", "=Q"), 2398 ("rx_errors", "=Q"), 2399 ("tx_errors", "=Q"), 2400 ("rx_dropped", "=Q"), 2401 ("tx_dropped", "=Q"), 2402 ) 2403 2404 def type_to_str(vport_type): 2405 if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV: 2406 return "netdev" 2407 elif vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL: 2408 return "internal" 2409 elif vport_type == OvsVport.OVS_VPORT_TYPE_GRE: 2410 return "gre" 2411 elif vport_type == OvsVport.OVS_VPORT_TYPE_VXLAN: 2412 return "vxlan" 2413 elif vport_type == OvsVport.OVS_VPORT_TYPE_GENEVE: 2414 return "geneve" 2415 raise ValueError("Unknown vport type:%d" % vport_type) 2416 2417 def str_to_type(vport_type): 2418 if vport_type == "netdev": 2419 return OvsVport.OVS_VPORT_TYPE_NETDEV 2420 elif vport_type == "internal": 2421 return OvsVport.OVS_VPORT_TYPE_INTERNAL 2422 elif vport_type == "gre": 2423 return OvsVport.OVS_VPORT_TYPE_GRE 2424 elif vport_type == "vxlan": 2425 return OvsVport.OVS_VPORT_TYPE_VXLAN 2426 elif vport_type == "geneve": 2427 return OvsVport.OVS_VPORT_TYPE_GENEVE 2428 raise ValueError("Unknown vport type: '%s'" % vport_type) 2429 2430 def __init__(self, packet=OvsPacket()): 2431 GenericNetlinkSocket.__init__(self) 2432 self.bind(OVS_VPORT_FAMILY, OvsVport.ovs_vport_msg) 2433 self.upcall_packet = packet 2434 2435 def info(self, vport_name, dpifindex=0, portno=None): 2436 msg = OvsVport.ovs_vport_msg() 2437 2438 msg["cmd"] = OVS_VPORT_CMD_GET 2439 msg["version"] = OVS_DATAPATH_VERSION 2440 msg["reserved"] = 0 2441 msg["dpifindex"] = dpifindex 2442 2443 if portno is None: 2444 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_name]) 2445 else: 2446 msg["attrs"].append(["OVS_VPORT_ATTR_PORT_NO", portno]) 2447 2448 try: 2449 reply = self.nlm_request( 2450 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST 2451 ) 2452 reply = reply[0] 2453 except NetlinkError as ne: 2454 if ne.code == errno.ENODEV: 2455 reply = None 2456 else: 2457 raise ne 2458 return reply 2459 2460 def attach(self, dpindex, vport_ifname, ptype, dport, lwt): 2461 msg = OvsVport.ovs_vport_msg() 2462 2463 msg["cmd"] = OVS_VPORT_CMD_NEW 2464 msg["version"] = OVS_DATAPATH_VERSION 2465 msg["reserved"] = 0 2466 msg["dpifindex"] = dpindex 2467 port_type = OvsVport.str_to_type(ptype) 2468 2469 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname]) 2470 msg["attrs"].append( 2471 ["OVS_VPORT_ATTR_UPCALL_PID", [self.upcall_packet.epid]] 2472 ) 2473 2474 TUNNEL_DEFAULTS = [("geneve", 6081), 2475 ("gre", 0), 2476 ("vxlan", 4789)] 2477 2478 for tnl in TUNNEL_DEFAULTS: 2479 if ptype == tnl[0]: 2480 if not dport: 2481 dport = tnl[1] 2482 2483 if not lwt: 2484 if tnl[0] == "gre": 2485 # GRE tunnels have no options. 2486 break 2487 2488 vportopt = OvsVport.ovs_vport_msg.vportopts() 2489 vportopt["attrs"].append( 2490 ["OVS_TUNNEL_ATTR_DST_PORT", dport] 2491 ) 2492 msg["attrs"].append( 2493 ["OVS_VPORT_ATTR_OPTIONS", vportopt] 2494 ) 2495 else: 2496 port_type = OvsVport.OVS_VPORT_TYPE_NETDEV 2497 ipr = pyroute2.iproute.IPRoute() 2498 2499 if tnl[0] == "geneve": 2500 ipr.link("add", ifname=vport_ifname, kind=tnl[0], 2501 geneve_port=dport, 2502 geneve_collect_metadata=True, 2503 geneve_udp_zero_csum6_rx=1) 2504 elif tnl[0] == "gre": 2505 ipr.link("add", ifname=vport_ifname, kind="gretap", 2506 gre_collect_metadata=True) 2507 elif tnl[0] == "vxlan": 2508 ipr.link("add", ifname=vport_ifname, kind=tnl[0], 2509 vxlan_learning=0, vxlan_collect_metadata=1, 2510 vxlan_udp_zero_csum6_rx=1, vxlan_port=dport) 2511 break 2512 msg["attrs"].append(["OVS_VPORT_ATTR_TYPE", port_type]) 2513 2514 try: 2515 reply = self.nlm_request( 2516 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2517 ) 2518 reply = reply[0] 2519 except NetlinkError as ne: 2520 if ne.code == errno.EEXIST: 2521 reply = None 2522 else: 2523 raise ne 2524 return reply 2525 2526 def reset_upcall(self, dpindex, vport_ifname, p=None): 2527 msg = OvsVport.ovs_vport_msg() 2528 2529 msg["cmd"] = OVS_VPORT_CMD_SET 2530 msg["version"] = OVS_DATAPATH_VERSION 2531 msg["reserved"] = 0 2532 msg["dpifindex"] = dpindex 2533 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname]) 2534 2535 if p == None: 2536 p = self.upcall_packet 2537 else: 2538 self.upcall_packet = p 2539 2540 msg["attrs"].append(["OVS_VPORT_ATTR_UPCALL_PID", [p.epid]]) 2541 2542 try: 2543 reply = self.nlm_request( 2544 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2545 ) 2546 reply = reply[0] 2547 except NetlinkError as ne: 2548 raise ne 2549 return reply 2550 2551 def detach(self, dpindex, vport_ifname): 2552 msg = OvsVport.ovs_vport_msg() 2553 2554 msg["cmd"] = OVS_VPORT_CMD_DEL 2555 msg["version"] = OVS_DATAPATH_VERSION 2556 msg["reserved"] = 0 2557 msg["dpifindex"] = dpindex 2558 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname]) 2559 2560 try: 2561 reply = self.nlm_request( 2562 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2563 ) 2564 reply = reply[0] 2565 except NetlinkError as ne: 2566 if ne.code == errno.ENODEV: 2567 reply = None 2568 else: 2569 raise ne 2570 return reply 2571 2572 def upcall_handler(self, handler=None): 2573 self.upcall_packet.upcall_handler(handler) 2574 2575 2576class OvsFlow(GenericNetlinkSocket): 2577 class ovs_flow_msg(ovs_dp_msg): 2578 nla_map = ( 2579 ("OVS_FLOW_ATTR_UNSPEC", "none"), 2580 ("OVS_FLOW_ATTR_KEY", "ovskey"), 2581 ("OVS_FLOW_ATTR_ACTIONS", "ovsactions"), 2582 ("OVS_FLOW_ATTR_STATS", "flowstats"), 2583 ("OVS_FLOW_ATTR_TCP_FLAGS", "uint8"), 2584 ("OVS_FLOW_ATTR_USED", "uint64"), 2585 ("OVS_FLOW_ATTR_CLEAR", "none"), 2586 ("OVS_FLOW_ATTR_MASK", "ovskey"), 2587 ("OVS_FLOW_ATTR_PROBE", "none"), 2588 ("OVS_FLOW_ATTR_UFID", "array(uint32)"), 2589 ("OVS_FLOW_ATTR_UFID_FLAGS", "uint32"), 2590 ) 2591 2592 class flowstats(nla): 2593 fields = ( 2594 ("packets", "=Q"), 2595 ("bytes", "=Q"), 2596 ) 2597 2598 def dpstr(self, more=False): 2599 ufid = self.get_attr("OVS_FLOW_ATTR_UFID") 2600 ufid_str = "" 2601 if ufid is not None: 2602 ufid_str = ( 2603 "ufid:{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format( 2604 ufid[0], 2605 ufid[1] >> 16, 2606 ufid[1] & 0xFFFF, 2607 ufid[2] >> 16, 2608 ufid[2] & 0, 2609 ufid[3], 2610 ) 2611 ) 2612 2613 key_field = self.get_attr("OVS_FLOW_ATTR_KEY") 2614 keymsg = None 2615 if key_field is not None: 2616 keymsg = key_field 2617 2618 mask_field = self.get_attr("OVS_FLOW_ATTR_MASK") 2619 maskmsg = None 2620 if mask_field is not None: 2621 maskmsg = mask_field 2622 2623 acts_field = self.get_attr("OVS_FLOW_ATTR_ACTIONS") 2624 actsmsg = None 2625 if acts_field is not None: 2626 actsmsg = acts_field 2627 2628 print_str = "" 2629 2630 if more: 2631 print_str += ufid_str + "," 2632 2633 if keymsg is not None: 2634 print_str += keymsg.dpstr(maskmsg, more) 2635 2636 stats = self.get_attr("OVS_FLOW_ATTR_STATS") 2637 if stats is None: 2638 print_str += " packets:0, bytes:0," 2639 else: 2640 print_str += " packets:%d, bytes:%d," % ( 2641 stats["packets"], 2642 stats["bytes"], 2643 ) 2644 2645 used = self.get_attr("OVS_FLOW_ATTR_USED") 2646 print_str += " used:" 2647 if used is None: 2648 print_str += "never," 2649 else: 2650 used_time = int(used) 2651 cur_time_sec = time.clock_gettime(time.CLOCK_MONOTONIC) 2652 used_time = (cur_time_sec * 1000) - used_time 2653 print_str += "{}s,".format(used_time / 1000) 2654 2655 print_str += " actions:" 2656 if ( 2657 actsmsg is None 2658 or "attrs" not in actsmsg 2659 or len(actsmsg["attrs"]) == 0 2660 ): 2661 print_str += "drop" 2662 else: 2663 print_str += actsmsg.dpstr(more) 2664 2665 return print_str 2666 2667 def parse(self, flowstr, actstr, dpidx=0): 2668 OVS_UFID_F_OMIT_KEY = 1 << 0 2669 OVS_UFID_F_OMIT_MASK = 1 << 1 2670 OVS_UFID_F_OMIT_ACTIONS = 1 << 2 2671 2672 self["cmd"] = 0 2673 self["version"] = 0 2674 self["reserved"] = 0 2675 self["dpifindex"] = 0 2676 2677 if flowstr.startswith("ufid:"): 2678 count = 5 2679 while flowstr[count] != ",": 2680 count += 1 2681 ufidstr = flowstr[5:count] 2682 flowstr = flowstr[count + 1 :] 2683 else: 2684 ufidstr = str(uuid.uuid4()) 2685 uuidRawObj = uuid.UUID(ufidstr).fields 2686 2687 self["attrs"].append( 2688 [ 2689 "OVS_FLOW_ATTR_UFID", 2690 [ 2691 uuidRawObj[0], 2692 uuidRawObj[1] << 16 | uuidRawObj[2], 2693 uuidRawObj[3] << 24 2694 | uuidRawObj[4] << 16 2695 | uuidRawObj[5] & (0xFF << 32) >> 32, 2696 uuidRawObj[5] & (0xFFFFFFFF), 2697 ], 2698 ] 2699 ) 2700 self["attrs"].append( 2701 [ 2702 "OVS_FLOW_ATTR_UFID_FLAGS", 2703 int( 2704 OVS_UFID_F_OMIT_KEY 2705 | OVS_UFID_F_OMIT_MASK 2706 | OVS_UFID_F_OMIT_ACTIONS 2707 ), 2708 ] 2709 ) 2710 2711 k = ovskey() 2712 m = ovskey() 2713 k.parse(flowstr, m) 2714 self["attrs"].append(["OVS_FLOW_ATTR_KEY", k]) 2715 self["attrs"].append(["OVS_FLOW_ATTR_MASK", m]) 2716 2717 if actstr is not None: 2718 a = ovsactions() 2719 a.parse(actstr) 2720 self["attrs"].append(["OVS_FLOW_ATTR_ACTIONS", a]) 2721 2722 def __init__(self): 2723 GenericNetlinkSocket.__init__(self) 2724 2725 self.bind(OVS_FLOW_FAMILY, OvsFlow.ovs_flow_msg) 2726 2727 def add_flow(self, dpifindex, flowmsg): 2728 """ 2729 Send a new flow message to the kernel. 2730 2731 dpifindex should be a valid datapath obtained by calling 2732 into the OvsDatapath lookup 2733 2734 flowmsg is a flow object obtained by calling a dpparse 2735 """ 2736 2737 flowmsg["cmd"] = OVS_FLOW_CMD_NEW 2738 flowmsg["version"] = OVS_DATAPATH_VERSION 2739 flowmsg["reserved"] = 0 2740 flowmsg["dpifindex"] = dpifindex 2741 2742 try: 2743 reply = self.nlm_request( 2744 flowmsg, 2745 msg_type=self.prid, 2746 msg_flags=NLM_F_REQUEST | NLM_F_ACK, 2747 ) 2748 reply = reply[0] 2749 except NetlinkError as ne: 2750 print(flowmsg) 2751 raise ne 2752 return reply 2753 2754 def mod_flow(self, dpifindex, flowmsg): 2755 """Modify an existing flow in the kernel.""" 2756 flowmsg["cmd"] = OVS_FLOW_CMD_SET 2757 flowmsg["version"] = OVS_DATAPATH_VERSION 2758 flowmsg["reserved"] = 0 2759 flowmsg["dpifindex"] = dpifindex 2760 2761 try: 2762 reply = self.nlm_request( 2763 flowmsg, 2764 msg_type=self.prid, 2765 msg_flags=NLM_F_REQUEST | NLM_F_ACK, 2766 ) 2767 reply = reply[0] 2768 except NetlinkError as ne: 2769 print(flowmsg) 2770 raise ne 2771 return reply 2772 2773 def del_flows(self, dpifindex): 2774 """ 2775 Send a del message to the kernel that will drop all flows. 2776 2777 dpifindex should be a valid datapath obtained by calling 2778 into the OvsDatapath lookup 2779 """ 2780 2781 flowmsg = OvsFlow.ovs_flow_msg() 2782 flowmsg["cmd"] = OVS_FLOW_CMD_DEL 2783 flowmsg["version"] = OVS_DATAPATH_VERSION 2784 flowmsg["reserved"] = 0 2785 flowmsg["dpifindex"] = dpifindex 2786 2787 try: 2788 reply = self.nlm_request( 2789 flowmsg, 2790 msg_type=self.prid, 2791 msg_flags=NLM_F_REQUEST | NLM_F_ACK, 2792 ) 2793 reply = reply[0] 2794 except NetlinkError as ne: 2795 print(flowmsg) 2796 raise ne 2797 return reply 2798 2799 def dump(self, dpifindex, flowspec=None): 2800 """ 2801 Returns a list of messages containing flows. 2802 2803 dpifindex should be a valid datapath obtained by calling 2804 into the OvsDatapath lookup 2805 2806 flowpsec is a string which represents a flow in the dpctl 2807 format. 2808 """ 2809 msg = OvsFlow.ovs_flow_msg() 2810 2811 msg["cmd"] = OVS_FLOW_CMD_GET 2812 msg["version"] = OVS_DATAPATH_VERSION 2813 msg["reserved"] = 0 2814 msg["dpifindex"] = dpifindex 2815 2816 msg_flags = NLM_F_REQUEST | NLM_F_ACK 2817 if flowspec is None: 2818 msg_flags |= NLM_F_DUMP 2819 rep = None 2820 2821 try: 2822 rep = self.nlm_request( 2823 msg, 2824 msg_type=self.prid, 2825 msg_flags=msg_flags, 2826 ) 2827 except NetlinkError as ne: 2828 raise ne 2829 return rep 2830 2831 def miss(self, packetmsg): 2832 seq = packetmsg["header"]["sequence_number"] 2833 keystr = "(none)" 2834 key_field = packetmsg.get_attr("OVS_PACKET_ATTR_KEY") 2835 if key_field is not None: 2836 keystr = key_field.dpstr(None, True) 2837 2838 pktdata = packetmsg.get_attr("OVS_PACKET_ATTR_PACKET") 2839 pktpres = "yes" if pktdata is not None else "no" 2840 2841 print("MISS upcall[%d/%s]: %s" % (seq, pktpres, keystr), flush=True) 2842 2843 def execute(self, packetmsg): 2844 print("userspace execute command", flush=True) 2845 2846 def action(self, packetmsg): 2847 print("userspace action command", flush=True) 2848 2849 2850class psample_sample(genlmsg): 2851 nla_map = ( 2852 ("PSAMPLE_ATTR_IIFINDEX", "none"), 2853 ("PSAMPLE_ATTR_OIFINDEX", "none"), 2854 ("PSAMPLE_ATTR_ORIGSIZE", "none"), 2855 ("PSAMPLE_ATTR_SAMPLE_GROUP", "uint32"), 2856 ("PSAMPLE_ATTR_GROUP_SEQ", "none"), 2857 ("PSAMPLE_ATTR_SAMPLE_RATE", "uint32"), 2858 ("PSAMPLE_ATTR_DATA", "array(uint8)"), 2859 ("PSAMPLE_ATTR_GROUP_REFCOUNT", "none"), 2860 ("PSAMPLE_ATTR_TUNNEL", "none"), 2861 ("PSAMPLE_ATTR_PAD", "none"), 2862 ("PSAMPLE_ATTR_OUT_TC", "none"), 2863 ("PSAMPLE_ATTR_OUT_TC_OCC", "none"), 2864 ("PSAMPLE_ATTR_LATENCY", "none"), 2865 ("PSAMPLE_ATTR_TIMESTAMP", "none"), 2866 ("PSAMPLE_ATTR_PROTO", "none"), 2867 ("PSAMPLE_ATTR_USER_COOKIE", "array(uint8)"), 2868 ) 2869 2870 def dpstr(self): 2871 fields = [] 2872 data = "" 2873 for (attr, value) in self["attrs"]: 2874 if attr == "PSAMPLE_ATTR_SAMPLE_GROUP": 2875 fields.append("group:%d" % value) 2876 if attr == "PSAMPLE_ATTR_SAMPLE_RATE": 2877 fields.append("rate:%d" % value) 2878 if attr == "PSAMPLE_ATTR_USER_COOKIE": 2879 value = "".join(format(x, "02x") for x in value) 2880 fields.append("cookie:%s" % value) 2881 if attr == "PSAMPLE_ATTR_DATA" and len(value) > 0: 2882 data = "data:%s" % "".join(format(x, "02x") for x in value) 2883 2884 return ("%s %s" % (",".join(fields), data)).strip() 2885 2886 2887class psample_msg(Marshal): 2888 PSAMPLE_CMD_SAMPLE = 0 2889 PSAMPLE_CMD_GET_GROUP = 1 2890 PSAMPLE_CMD_NEW_GROUP = 2 2891 PSAMPLE_CMD_DEL_GROUP = 3 2892 PSAMPLE_CMD_SET_FILTER = 4 2893 msg_map = {PSAMPLE_CMD_SAMPLE: psample_sample} 2894 2895 2896class PsampleEvent(EventSocket): 2897 genl_family = "psample" 2898 mcast_groups = ["packets"] 2899 marshal_class = psample_msg 2900 2901 def read_samples(self): 2902 print("listening for psample events", flush=True) 2903 while True: 2904 try: 2905 for msg in self.get(): 2906 print(msg.dpstr(), flush=True) 2907 except NetlinkError as ne: 2908 raise ne 2909 2910 2911def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()): 2912 dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME") 2913 base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS") 2914 megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS") 2915 user_features = dp_lookup_rep.get_attr("OVS_DP_ATTR_USER_FEATURES") 2916 masks_cache_size = dp_lookup_rep.get_attr("OVS_DP_ATTR_MASKS_CACHE_SIZE") 2917 2918 print("%s:" % dp_name) 2919 print( 2920 " lookups: hit:%d missed:%d lost:%d" 2921 % (base_stats["hit"], base_stats["missed"], base_stats["lost"]) 2922 ) 2923 print(" flows:%d" % base_stats["flows"]) 2924 pkts = base_stats["hit"] + base_stats["missed"] 2925 avg = (megaflow_stats["mask_hit"] / pkts) if pkts != 0 else 0.0 2926 print( 2927 " masks: hit:%d total:%d hit/pkt:%f" 2928 % (megaflow_stats["mask_hit"], megaflow_stats["masks"], avg) 2929 ) 2930 print(" caches:") 2931 print(" masks-cache: size:%d" % masks_cache_size) 2932 2933 if user_features is not None: 2934 print(" features: 0x%X" % user_features) 2935 2936 # port print out 2937 for iface in ndb.interfaces: 2938 rep = vpl.info(iface.ifname, ifindex) 2939 if rep is not None: 2940 opts = "" 2941 vpo = rep.get_attr("OVS_VPORT_ATTR_OPTIONS") 2942 if vpo: 2943 dpo = vpo.get_attr("OVS_TUNNEL_ATTR_DST_PORT") 2944 if dpo: 2945 opts += " tnl-dport:%s" % dpo 2946 print( 2947 " port %d: %s (%s%s)" 2948 % ( 2949 rep.get_attr("OVS_VPORT_ATTR_PORT_NO"), 2950 rep.get_attr("OVS_VPORT_ATTR_NAME"), 2951 OvsVport.type_to_str(rep.get_attr("OVS_VPORT_ATTR_TYPE")), 2952 opts, 2953 ) 2954 ) 2955 2956 2957def main(argv): 2958 nlmsg_atoms.encap_ovskey = encap_ovskey 2959 nlmsg_atoms.ovskey = ovskey 2960 nlmsg_atoms.ovsactions = ovsactions 2961 2962 # version check for pyroute2 2963 prverscheck = pyroute2.__version__.split(".") 2964 if int(prverscheck[0]) == 0 and int(prverscheck[1]) < 6: 2965 print("Need to upgrade the python pyroute2 package to >= 0.6.") 2966 sys.exit(1) 2967 2968 parser = argparse.ArgumentParser() 2969 parser.add_argument( 2970 "-v", 2971 "--verbose", 2972 action="count", 2973 help="Increment 'verbose' output counter.", 2974 default=0, 2975 ) 2976 subparsers = parser.add_subparsers(dest="subcommand") 2977 2978 showdpcmd = subparsers.add_parser("show") 2979 showdpcmd.add_argument( 2980 "showdp", metavar="N", type=str, nargs="?", help="Datapath Name" 2981 ) 2982 2983 adddpcmd = subparsers.add_parser("add-dp") 2984 adddpcmd.add_argument("adddp", help="Datapath Name") 2985 adddpcmd.add_argument( 2986 "-u", 2987 "--upcall", 2988 action="store_true", 2989 help="Leave open a reader for upcalls", 2990 ) 2991 adddpcmd.add_argument( 2992 "-V", 2993 "--versioning", 2994 required=False, 2995 help="Specify a custom version / feature string", 2996 ) 2997 2998 deldpcmd = subparsers.add_parser("del-dp") 2999 deldpcmd.add_argument("deldp", help="Datapath Name") 3000 3001 addifcmd = subparsers.add_parser("add-if") 3002 addifcmd.add_argument("dpname", help="Datapath Name") 3003 addifcmd.add_argument("addif", help="Interface name for adding") 3004 addifcmd.add_argument( 3005 "-u", 3006 "--upcall", 3007 action="store_true", 3008 help="Leave open a reader for upcalls", 3009 ) 3010 addifcmd.add_argument( 3011 "-t", 3012 "--ptype", 3013 type=str, 3014 default="netdev", 3015 choices=["netdev", "internal", "gre", "geneve", "vxlan"], 3016 help="Interface type (default netdev)", 3017 ) 3018 addifcmd.add_argument( 3019 "-p", 3020 "--dport", 3021 type=int, 3022 default=0, 3023 help="Destination port (0 for default)" 3024 ) 3025 addifcmd.add_argument( 3026 "-l", 3027 "--lwt", 3028 action=argparse.BooleanOptionalAction, 3029 default=True, 3030 help="Use LWT infrastructure instead of vport (default true)." 3031 ) 3032 delifcmd = subparsers.add_parser("del-if") 3033 delifcmd.add_argument("dpname", help="Datapath Name") 3034 delifcmd.add_argument("delif", help="Interface name for adding") 3035 delifcmd.add_argument("-d", 3036 "--dellink", 3037 type=bool, default=False, 3038 help="Delete the link as well.") 3039 3040 dumpflcmd = subparsers.add_parser("dump-flows") 3041 dumpflcmd.add_argument("dumpdp", help="Datapath Name") 3042 3043 addflcmd = subparsers.add_parser("add-flow") 3044 addflcmd.add_argument("flbr", help="Datapath name") 3045 addflcmd.add_argument("flow", help="Flow specification") 3046 addflcmd.add_argument("acts", help="Flow actions") 3047 3048 modflcmd = subparsers.add_parser("mod-flow") 3049 modflcmd.add_argument("modbr", help="Datapath name") 3050 modflcmd.add_argument("modflow", help="Flow specification") 3051 modflcmd.add_argument("modacts", help="Flow actions", 3052 nargs="?", default=None) 3053 3054 delfscmd = subparsers.add_parser("del-flows") 3055 delfscmd.add_argument("flsbr", help="Datapath name") 3056 3057 subparsers.add_parser("psample-events") 3058 3059 args = parser.parse_args() 3060 3061 if args.verbose > 0: 3062 if args.verbose > 1: 3063 logging.basicConfig(level=logging.DEBUG) 3064 3065 ovspk = OvsPacket() 3066 ovsdp = OvsDatapath() 3067 ovsvp = OvsVport(ovspk) 3068 ovsflow = OvsFlow() 3069 ndb = NDB() 3070 3071 sys.setrecursionlimit(100000) 3072 3073 if args.subcommand == "psample-events": 3074 PsampleEvent().read_samples() 3075 3076 if hasattr(args, "showdp"): 3077 found = False 3078 for iface in ndb.interfaces: 3079 rep = None 3080 if args.showdp is None: 3081 rep = ovsdp.info(iface.ifname, 0) 3082 elif args.showdp == iface.ifname: 3083 rep = ovsdp.info(iface.ifname, 0) 3084 3085 if rep is not None: 3086 found = True 3087 print_ovsdp_full(rep, iface.index, ndb, ovsvp) 3088 3089 if not found: 3090 msg = "No DP found" 3091 if args.showdp is not None: 3092 msg += ":'%s'" % args.showdp 3093 print(msg) 3094 elif hasattr(args, "adddp"): 3095 rep = ovsdp.create(args.adddp, args.upcall, args.versioning, ovspk) 3096 if rep is None: 3097 print("DP '%s' already exists" % args.adddp) 3098 else: 3099 print("DP '%s' added" % args.adddp) 3100 if args.upcall: 3101 ovspk.upcall_handler(ovsflow) 3102 elif hasattr(args, "deldp"): 3103 ovsdp.destroy(args.deldp) 3104 elif hasattr(args, "addif"): 3105 rep = ovsdp.info(args.dpname, 0) 3106 if rep is None: 3107 print("DP '%s' not found." % args.dpname) 3108 return 1 3109 dpindex = rep["dpifindex"] 3110 rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype, 3111 args.dport, args.lwt) 3112 msg = "vport '%s'" % args.addif 3113 if rep and rep["header"]["error"] is None: 3114 msg += " added." 3115 else: 3116 msg += " failed to add." 3117 if args.upcall: 3118 if rep is None: 3119 rep = ovsvp.reset_upcall(dpindex, args.addif, ovspk) 3120 ovsvp.upcall_handler(ovsflow) 3121 elif hasattr(args, "delif"): 3122 rep = ovsdp.info(args.dpname, 0) 3123 if rep is None: 3124 print("DP '%s' not found." % args.dpname) 3125 return 1 3126 rep = ovsvp.detach(rep["dpifindex"], args.delif) 3127 msg = "vport '%s'" % args.delif 3128 if rep and rep["header"]["error"] is None: 3129 msg += " removed." 3130 else: 3131 msg += " failed to remove." 3132 if args.dellink: 3133 ipr = pyroute2.iproute.IPRoute() 3134 ipr.link("del", index=ipr.link_lookup(ifname=args.delif)[0]) 3135 elif hasattr(args, "dumpdp"): 3136 rep = ovsdp.info(args.dumpdp, 0) 3137 if rep is None: 3138 print("DP '%s' not found." % args.dumpdp) 3139 return 1 3140 rep = ovsflow.dump(rep["dpifindex"]) 3141 for flow in rep: 3142 print(flow.dpstr(True if args.verbose > 0 else False)) 3143 elif hasattr(args, "flbr"): 3144 rep = ovsdp.info(args.flbr, 0) 3145 if rep is None: 3146 print("DP '%s' not found." % args.flbr) 3147 return 1 3148 flow = OvsFlow.ovs_flow_msg() 3149 flow.parse(args.flow, args.acts, rep["dpifindex"]) 3150 ovsflow.add_flow(rep["dpifindex"], flow) 3151 elif hasattr(args, "modbr"): 3152 rep = ovsdp.info(args.modbr, 0) 3153 if rep is None: 3154 print(f"DP '{args.modbr}' not found.") 3155 return 1 3156 flow = OvsFlow.ovs_flow_msg() 3157 flow.parse(args.modflow, args.modacts, rep["dpifindex"]) 3158 ovsflow.mod_flow(rep["dpifindex"], flow) 3159 elif hasattr(args, "flsbr"): 3160 rep = ovsdp.info(args.flsbr, 0) 3161 if rep is None: 3162 print("DP '%s' not found." % args.flsbr) 3163 ovsflow.del_flows(rep["dpifindex"]) 3164 3165 return 0 3166 3167 3168if __name__ == "__main__": 3169 sys.exit(main(sys.argv)) 3170