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