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 ("proto", "proto", "%d", lambda x: int(x) if x else 0), 1260 ("tclass", "tclass", "%d", lambda x: int(x) if x else 0), 1261 ("hlimit", "hlimit", "%d", lambda x: int(x) if x else 0), 1262 ("frag", "frag", "%d", lambda x: int(x) if x else 0), 1263 ) 1264 1265 def __init__( 1266 self, 1267 data=None, 1268 offset=None, 1269 parent=None, 1270 length=None, 1271 init=None, 1272 ): 1273 ovskey.ovs_key_proto.__init__( 1274 self, 1275 "ipv6", 1276 data=data, 1277 offset=offset, 1278 parent=parent, 1279 length=length, 1280 init=init, 1281 ) 1282 1283 class ovs_key_tcp(ovs_key_proto): 1284 def __init__( 1285 self, 1286 data=None, 1287 offset=None, 1288 parent=None, 1289 length=None, 1290 init=None, 1291 ): 1292 ovskey.ovs_key_proto.__init__( 1293 self, 1294 "tcp", 1295 data=data, 1296 offset=offset, 1297 parent=parent, 1298 length=length, 1299 init=init, 1300 ) 1301 1302 class ovs_key_udp(ovs_key_proto): 1303 def __init__( 1304 self, 1305 data=None, 1306 offset=None, 1307 parent=None, 1308 length=None, 1309 init=None, 1310 ): 1311 ovskey.ovs_key_proto.__init__( 1312 self, 1313 "udp", 1314 data=data, 1315 offset=offset, 1316 parent=parent, 1317 length=length, 1318 init=init, 1319 ) 1320 1321 class ovs_key_sctp(ovs_key_proto): 1322 def __init__( 1323 self, 1324 data=None, 1325 offset=None, 1326 parent=None, 1327 length=None, 1328 init=None, 1329 ): 1330 ovskey.ovs_key_proto.__init__( 1331 self, 1332 "sctp", 1333 data=data, 1334 offset=offset, 1335 parent=parent, 1336 length=length, 1337 init=init, 1338 ) 1339 1340 class ovs_key_icmp(ovs_key_proto): 1341 fields = ( 1342 ("type", "B"), 1343 ("code", "B"), 1344 ) 1345 1346 fields_map = ( 1347 ("type", "type", "%d", lambda x: int(x) if x else 0), 1348 ("code", "code", "%d", lambda x: int(x) if x else 0), 1349 ) 1350 1351 def __init__( 1352 self, 1353 data=None, 1354 offset=None, 1355 parent=None, 1356 length=None, 1357 init=None, 1358 ): 1359 ovskey.ovs_key_proto.__init__( 1360 self, 1361 "icmp", 1362 data=data, 1363 offset=offset, 1364 parent=parent, 1365 length=length, 1366 init=init, 1367 ) 1368 1369 class ovs_key_icmpv6(ovs_key_icmp): 1370 def __init__( 1371 self, 1372 data=None, 1373 offset=None, 1374 parent=None, 1375 length=None, 1376 init=None, 1377 ): 1378 ovskey.ovs_key_proto.__init__( 1379 self, 1380 "icmpv6", 1381 data=data, 1382 offset=offset, 1383 parent=parent, 1384 length=length, 1385 init=init, 1386 ) 1387 1388 class ovs_key_arp(ovs_key_proto): 1389 fields = ( 1390 ("sip", "!I"), 1391 ("tip", "!I"), 1392 ("op", "!H"), 1393 ("sha", "!6s"), 1394 ("tha", "!6s"), 1395 ("pad", "xx"), 1396 ) 1397 1398 fields_map = ( 1399 ( 1400 "sip", 1401 "sip", 1402 lambda x: str(ipaddress.IPv4Address(x)), 1403 int, 1404 convert_ipv4, 1405 ), 1406 ( 1407 "tip", 1408 "tip", 1409 lambda x: str(ipaddress.IPv4Address(x)), 1410 int, 1411 convert_ipv4, 1412 ), 1413 ("op", "op", "%d", lambda x: int(x) if x else 0), 1414 ( 1415 "sha", 1416 "sha", 1417 macstr, 1418 lambda x: int.from_bytes(x, "big"), 1419 convert_mac, 1420 ), 1421 ( 1422 "tha", 1423 "tha", 1424 macstr, 1425 lambda x: int.from_bytes(x, "big"), 1426 convert_mac, 1427 ), 1428 ) 1429 1430 def __init__( 1431 self, 1432 data=None, 1433 offset=None, 1434 parent=None, 1435 length=None, 1436 init=None, 1437 ): 1438 ovskey.ovs_key_proto.__init__( 1439 self, 1440 "arp", 1441 data=data, 1442 offset=offset, 1443 parent=parent, 1444 length=length, 1445 init=init, 1446 ) 1447 1448 class ovs_key_nd(ovs_key_proto): 1449 fields = ( 1450 ("target", "!16s"), 1451 ("sll", "!6s"), 1452 ("tll", "!6s"), 1453 ) 1454 1455 fields_map = ( 1456 ( 1457 "target", 1458 "target", 1459 lambda x: str(ipaddress.IPv6Address(x)), 1460 convert_ipv6, 1461 ), 1462 ("sll", "sll", macstr, lambda x: int.from_bytes(x, "big")), 1463 ("tll", "tll", macstr, lambda x: int.from_bytes(x, "big")), 1464 ) 1465 1466 def __init__( 1467 self, 1468 data=None, 1469 offset=None, 1470 parent=None, 1471 length=None, 1472 init=None, 1473 ): 1474 ovskey.ovs_key_proto.__init__( 1475 self, 1476 "nd", 1477 data=data, 1478 offset=offset, 1479 parent=parent, 1480 length=length, 1481 init=init, 1482 ) 1483 1484 class ovs_key_ct_tuple_ipv4(ovs_key_proto): 1485 fields = ( 1486 ("src", "!I"), 1487 ("dst", "!I"), 1488 ("tp_src", "!H"), 1489 ("tp_dst", "!H"), 1490 ("proto", "B"), 1491 ) 1492 1493 fields_map = ( 1494 ( 1495 "src", 1496 "src", 1497 lambda x: str(ipaddress.IPv4Address(x)), 1498 int, 1499 convert_ipv4, 1500 ), 1501 ( 1502 "dst", 1503 "dst", 1504 lambda x: str(ipaddress.IPv4Address(x)), 1505 int, 1506 convert_ipv4, 1507 ), 1508 ("tp_src", "tp_src", "%d", int), 1509 ("tp_dst", "tp_dst", "%d", int), 1510 ("proto", "proto", "%d", int), 1511 ) 1512 1513 def __init__( 1514 self, 1515 data=None, 1516 offset=None, 1517 parent=None, 1518 length=None, 1519 init=None, 1520 ): 1521 ovskey.ovs_key_proto.__init__( 1522 self, 1523 "ct_tuple4", 1524 data=data, 1525 offset=offset, 1526 parent=parent, 1527 length=length, 1528 init=init, 1529 ) 1530 1531 class ovs_key_ct_tuple_ipv6(nla): 1532 fields = ( 1533 ("src", "!16s"), 1534 ("dst", "!16s"), 1535 ("tp_src", "!H"), 1536 ("tp_dst", "!H"), 1537 ("proto", "B"), 1538 ) 1539 1540 fields_map = ( 1541 ( 1542 "src", 1543 "src", 1544 lambda x: str(ipaddress.IPv6Address(x)), 1545 convert_ipv6, 1546 ), 1547 ( 1548 "dst", 1549 "dst", 1550 lambda x: str(ipaddress.IPv6Address(x)), 1551 convert_ipv6, 1552 ), 1553 ("tp_src", "tp_src", "%d", int), 1554 ("tp_dst", "tp_dst", "%d", int), 1555 ("proto", "proto", "%d", int), 1556 ) 1557 1558 def __init__( 1559 self, 1560 data=None, 1561 offset=None, 1562 parent=None, 1563 length=None, 1564 init=None, 1565 ): 1566 ovskey.ovs_key_proto.__init__( 1567 self, 1568 "ct_tuple6", 1569 data=data, 1570 offset=offset, 1571 parent=parent, 1572 length=length, 1573 init=init, 1574 ) 1575 1576 class ovs_key_tunnel(nla): 1577 nla_flags = NLA_F_NESTED 1578 1579 nla_map = ( 1580 ("OVS_TUNNEL_KEY_ATTR_ID", "be64"), 1581 ("OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "ipaddr"), 1582 ("OVS_TUNNEL_KEY_ATTR_IPV4_DST", "ipaddr"), 1583 ("OVS_TUNNEL_KEY_ATTR_TOS", "uint8"), 1584 ("OVS_TUNNEL_KEY_ATTR_TTL", "uint8"), 1585 ("OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", "flag"), 1586 ("OVS_TUNNEL_KEY_ATTR_CSUM", "flag"), 1587 ("OVS_TUNNEL_KEY_ATTR_OAM", "flag"), 1588 ("OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS", "array(uint32)"), 1589 ("OVS_TUNNEL_KEY_ATTR_TP_SRC", "be16"), 1590 ("OVS_TUNNEL_KEY_ATTR_TP_DST", "be16"), 1591 ("OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS", "none"), 1592 ("OVS_TUNNEL_KEY_ATTR_IPV6_SRC", "ipaddr"), 1593 ("OVS_TUNNEL_KEY_ATTR_IPV6_DST", "ipaddr"), 1594 ("OVS_TUNNEL_KEY_ATTR_PAD", "none"), 1595 ("OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS", "none"), 1596 ("OVS_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE", "flag"), 1597 ) 1598 1599 def parse(self, flowstr, mask=None): 1600 if not flowstr.startswith("tunnel("): 1601 return None, None 1602 1603 k = ovskey.ovs_key_tunnel() 1604 if mask is not None: 1605 mask = ovskey.ovs_key_tunnel() 1606 1607 flowstr = flowstr[len("tunnel("):] 1608 1609 v6_address = None 1610 1611 fields = [ 1612 ("tun_id=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_ID", 1613 0xffffffffffffffff, None, None), 1614 1615 ("src=", r"([0-9a-fA-F\.]+)", str, 1616 "OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "255.255.255.255", "0.0.0.0", 1617 False), 1618 ("dst=", r"([0-9a-fA-F\.]+)", str, 1619 "OVS_TUNNEL_KEY_ATTR_IPV4_DST", "255.255.255.255", "0.0.0.0", 1620 False), 1621 1622 ("ipv6_src=", r"([0-9a-fA-F:]+)", str, 1623 "OVS_TUNNEL_KEY_ATTR_IPV6_SRC", 1624 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True), 1625 ("ipv6_dst=", r"([0-9a-fA-F:]+)", str, 1626 "OVS_TUNNEL_KEY_ATTR_IPV6_DST", 1627 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True), 1628 1629 ("tos=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TOS", 255, 0, 1630 None), 1631 ("ttl=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TTL", 255, 0, 1632 None), 1633 1634 ("tp_src=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_SRC", 1635 65535, 0, None), 1636 ("tp_dst=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_DST", 1637 65535, 0, None), 1638 ] 1639 1640 forced_include = ["OVS_TUNNEL_KEY_ATTR_TTL"] 1641 1642 for prefix, regex, typ, attr_name, mask_val, default_val, v46_flag in fields: 1643 flowstr, value = parse_extract_field(flowstr, prefix, regex, typ, False) 1644 if not attr_name: 1645 raise Exception("Bad list value in tunnel fields") 1646 1647 if value is None and attr_name in forced_include: 1648 value = default_val 1649 mask_val = default_val 1650 1651 if value is not None: 1652 if v46_flag is not None: 1653 if v6_address is None: 1654 v6_address = v46_flag 1655 if v46_flag != v6_address: 1656 raise ValueError("Cannot mix v6 and v4 addresses") 1657 k["attrs"].append([attr_name, value]) 1658 if mask is not None: 1659 mask["attrs"].append([attr_name, mask_val]) 1660 else: 1661 if v46_flag is not None: 1662 if v6_address is None or v46_flag != v6_address: 1663 continue 1664 if mask is not None: 1665 mask["attrs"].append([attr_name, default_val]) 1666 1667 if k["attrs"][0][0] != "OVS_TUNNEL_KEY_ATTR_ID": 1668 raise ValueError("Needs a tunid set") 1669 1670 if flowstr.startswith("flags("): 1671 flowstr = flowstr[len("flags("):] 1672 flagspos = flowstr.find(")") 1673 flags = flowstr[:flagspos] 1674 flowstr = flowstr[flagspos + 1:] 1675 1676 flag_attrs = { 1677 "df": "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", 1678 "csum": "OVS_TUNNEL_KEY_ATTR_CSUM", 1679 "oam": "OVS_TUNNEL_KEY_ATTR_OAM" 1680 } 1681 1682 for flag in flags.split("|"): 1683 if flag in flag_attrs: 1684 k["attrs"].append([flag_attrs[flag], True]) 1685 if mask is not None: 1686 mask["attrs"].append([flag_attrs[flag], True]) 1687 1688 flowstr = flowstr[strspn(flowstr, ", ") :] 1689 return flowstr, k, mask 1690 1691 def dpstr(self, mask=None, more=False): 1692 print_str = "tunnel(" 1693 1694 flagsattrs = [] 1695 for k in self["attrs"]: 1696 noprint = False 1697 if k[0] == "OVS_TUNNEL_KEY_ATTR_ID": 1698 print_str += "tun_id=%d" % k[1] 1699 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_SRC": 1700 print_str += "src=%s" % k[1] 1701 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_DST": 1702 print_str += "dst=%s" % k[1] 1703 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_SRC": 1704 print_str += "ipv6_src=%s" % k[1] 1705 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_DST": 1706 print_str += "ipv6_dst=%s" % k[1] 1707 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TOS": 1708 print_str += "tos=%d" % k[1] 1709 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TTL": 1710 print_str += "ttl=%d" % k[1] 1711 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_SRC": 1712 print_str += "tp_src=%d" % k[1] 1713 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_DST": 1714 print_str += "tp_dst=%d" % k[1] 1715 elif k[0] == "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT": 1716 noprint = True 1717 flagsattrs.append("df") 1718 elif k[0] == "OVS_TUNNEL_KEY_ATTR_CSUM": 1719 noprint = True 1720 flagsattrs.append("csum") 1721 elif k[0] == "OVS_TUNNEL_KEY_ATTR_OAM": 1722 noprint = True 1723 flagsattrs.append("oam") 1724 1725 if not noprint: 1726 print_str += "," 1727 1728 if len(flagsattrs): 1729 print_str += "flags(" + "|".join(flagsattrs) + ")" 1730 print_str += ")" 1731 return print_str 1732 1733 class ovs_key_mpls(nla): 1734 fields = (("lse", ">I"),) 1735 1736 # 802.1Q CFI (Canonical Format Indicator) bit, always set for Ethernet 1737 _VLAN_CFI_MASK = 0x1000 1738 1739 @staticmethod 1740 def _vlan_dpstr(tci): 1741 """Format VLAN TCI as vid=X,pcp=Y,cfi=Z or tci=0xNNNN. 1742 1743 When cfi=1 (standard Ethernet VLAN), outputs decomposed 1744 vid/pcp/cfi fields. When cfi=0 (truncated VLAN header), 1745 falls back to raw tci=0x%04x to ensure round-trip 1746 correctness: the parser auto-adds cfi=1 for vid/pcp 1747 format, so cfi=0 would be lost on re-parse.""" 1748 vid = tci & 0x0FFF 1749 pcp = (tci >> 13) & 0x7 1750 cfi = (tci >> 12) & 0x1 1751 if cfi: 1752 return "vid=%d,pcp=%d,cfi=%d" % (vid, pcp, cfi) 1753 return "tci=0x%04x" % tci 1754 1755 @staticmethod 1756 def _parse_vlan_from_flowstr(flowstr): 1757 """Parse vlan(tci=X) or vlan(vid=X[,pcp=Y,cfi=Z]) from flowstr. 1758 1759 Returns (remaining_flowstr, key_tci, mask_tci). 1760 TCI values use standard bit layout (VID bits 0-11, 1761 CFI bit 12, PCP bits 13-15); byte order conversion to 1762 big-endian happens in pyroute2 be16 NLA serialization. 1763 The mask covers only the fields the caller specified: 1764 vid -> 0x0FFF, pcp -> 0xE000, cfi -> 0x1000, tci -> 0xFFFF. 1765 1766 The tci= key sets the raw TCI bitfield (no CFI validation) to allow 1767 non-Ethernet use cases. Use cfi=1 for standard Ethernet VLAN matching. 1768 """ 1769 tci = 0 1770 mask = 0 1771 has_tci = False 1772 has_vid = has_pcp = has_cfi = False 1773 _tci_mix_err = "vlan(): 'tci' cannot be mixed " \ 1774 "with 'vid'/'pcp'/'cfi'" 1775 first = True 1776 while True: 1777 flowstr = flowstr.lstrip() 1778 if not flowstr: 1779 raise ValueError("vlan(): missing ')'") 1780 if flowstr[0] == ')': 1781 break 1782 if not first: 1783 flowstr = flowstr[1:] # skip ',' 1784 if not flowstr: 1785 raise ValueError("vlan(): missing ')' after trailing comma") 1786 flowstr = flowstr.lstrip() 1787 if flowstr and flowstr[0] == ')': 1788 break 1789 if flowstr and flowstr[0] == ',': 1790 raise ValueError( 1791 "vlan(): empty or extra comma in field list") 1792 first = False 1793 1794 eq = flowstr.find('=') 1795 if eq == -1: 1796 raise ValueError( 1797 "vlan(): expected key=value, got '%s'" % flowstr) 1798 key = flowstr[:eq].strip() 1799 flowstr = flowstr[eq + 1:] 1800 1801 end = flowstr.find(',') 1802 end2 = flowstr.find(')') 1803 if end == -1 and end2 == -1: 1804 raise ValueError("vlan(): missing ')'") 1805 if end == -1 or (end2 != -1 and end2 < end): 1806 end = end2 1807 val = flowstr[:end].strip() 1808 flowstr = flowstr[end:] 1809 1810 if not val: 1811 raise ValueError("vlan(): empty value for key '%s'" % key) 1812 try: 1813 v = int(val, 0) 1814 except ValueError as exc: 1815 raise ValueError( 1816 "vlan(): invalid value '%s' for key '%s'" 1817 % (val, key)) from exc 1818 1819 if key == 'tci': 1820 if has_tci: 1821 raise ValueError("vlan(): duplicate 'tci'") 1822 if has_vid or has_pcp or has_cfi: 1823 raise ValueError(_tci_mix_err) 1824 if v > 0xFFFF or v < 0: 1825 raise ValueError("vlan(): tci=0x%x out of range" % v) 1826 tci = v 1827 mask = 0xFFFF 1828 has_tci = True 1829 elif key == 'vid': 1830 if has_tci: 1831 raise ValueError(_tci_mix_err) 1832 if has_vid: 1833 raise ValueError("vlan(): duplicate 'vid'") 1834 if v < 0 or v > 0xFFF: 1835 raise ValueError("vlan(): vid=%d out of range (0-4095)" % v) 1836 tci |= v 1837 mask |= 0x0FFF 1838 has_vid = True 1839 elif key == 'pcp': 1840 if has_tci: 1841 raise ValueError(_tci_mix_err) 1842 if has_pcp: 1843 raise ValueError("vlan(): duplicate 'pcp'") 1844 if v < 0 or v > 7: 1845 raise ValueError("vlan(): pcp=%d out of range (0-7)" % v) 1846 tci |= (v & 0x7) << 13 1847 mask |= 0xE000 1848 has_pcp = True 1849 elif key == 'cfi': 1850 if has_tci: 1851 raise ValueError(_tci_mix_err) 1852 if has_cfi: 1853 raise ValueError("vlan(): duplicate 'cfi'") 1854 if v != 1: 1855 raise ValueError("vlan(): cfi must be 1 for Ethernet") 1856 tci |= ovskey._VLAN_CFI_MASK 1857 mask |= ovskey._VLAN_CFI_MASK 1858 has_cfi = True 1859 else: 1860 raise ValueError("vlan(): unknown key '%s'" % key) 1861 1862 flowstr = flowstr[1:] # skip ')' 1863 # Catch immediate '))' (user error). A ')' after ',' is consumed 1864 # by parse()'s strspn(flowstr, "), ") inter-field separator stripping. 1865 if flowstr.lstrip().startswith(')'): 1866 raise ValueError("vlan(): unmatched ')'") 1867 # parse() strips trailing ',', ')', ' ' as inter-field separators, 1868 # so we do not need to call strspn here. 1869 1870 if mask == 0: 1871 raise ValueError("vlan(): no fields specified, " 1872 "use vlan(vid=X[,pcp=Y,cfi=Z]) or vlan(tci=X)") 1873 if not has_tci: 1874 tci |= ovskey._VLAN_CFI_MASK 1875 mask |= ovskey._VLAN_CFI_MASK 1876 return flowstr, tci, mask 1877 1878 @staticmethod 1879 def _parse_encap_from_flowstr(flowstr): 1880 """Parse encap(inner_flow) from flowstr. 1881 1882 Returns (remaining_flowstr, inner_key_dict, inner_mask_dict) 1883 where each dict has an 'attrs' key for recursive NLA encoding. 1884 Parenthesis-depth tracking handles nested encap() calls but not 1885 quoted strings containing literal parentheses. 1886 """ 1887 depth = 1 1888 end = -1 1889 for i, c in enumerate(flowstr): 1890 if c == '(': 1891 depth += 1 1892 elif c == ')': 1893 depth -= 1 1894 if depth < 0: 1895 raise ValueError( 1896 "encap(): unmatched ')' at position %d" % i) 1897 if depth == 0: 1898 end = i 1899 break 1900 1901 if end == -1: 1902 if depth > 1: 1903 raise ValueError("encap(): missing ')' in nested encap") 1904 raise ValueError("encap(): missing ')'") 1905 1906 inner_str = flowstr[:end].strip() 1907 if not inner_str: 1908 raise ValueError("encap(): empty inner flow") 1909 1910 flowstr = flowstr[end + 1:] 1911 if flowstr.lstrip().startswith(')'): 1912 raise ValueError("encap(): unmatched ')' after encap()") 1913 1914 inner_key = encap_ovskey() 1915 inner_mask = encap_ovskey() 1916 remaining = inner_key.parse(inner_str, inner_mask) 1917 if remaining and re.search(r'[^\s,)]', remaining): 1918 raise ValueError( 1919 "encap(): unrecognized trailing " 1920 "content '%s'" % remaining.strip()) 1921 1922 return flowstr, inner_key, inner_mask 1923 1924 def parse(self, flowstr, mask=None): 1925 for field in ( 1926 ("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse), 1927 ("OVS_KEY_ATTR_SKB_MARK", "skb_mark", intparse), 1928 ("OVS_KEY_ATTR_RECIRC_ID", "recirc_id", intparse), 1929 ("OVS_KEY_ATTR_TUNNEL", "tunnel", ovskey.ovs_key_tunnel), 1930 ("OVS_KEY_ATTR_DP_HASH", "dp_hash", intparse), 1931 ("OVS_KEY_ATTR_CT_STATE", "ct_state", parse_ct_state), 1932 ("OVS_KEY_ATTR_CT_ZONE", "ct_zone", intparse), 1933 ("OVS_KEY_ATTR_CT_MARK", "ct_mark", intparse), 1934 ("OVS_KEY_ATTR_IN_PORT", "in_port", intparse), 1935 ( 1936 "OVS_KEY_ATTR_ETHERNET", 1937 "eth", 1938 ovskey.ethaddr, 1939 ), 1940 ( 1941 "OVS_KEY_ATTR_ETHERTYPE", 1942 "eth_type", 1943 lambda x: intparse(x, "0xffff"), 1944 ), 1945 ( 1946 "OVS_KEY_ATTR_VLAN", 1947 "vlan", 1948 ovskey._parse_vlan_from_flowstr, 1949 ), 1950 ( 1951 "OVS_KEY_ATTR_ENCAP", 1952 "encap", 1953 ovskey._parse_encap_from_flowstr, 1954 ), 1955 ( 1956 "OVS_KEY_ATTR_IPV4", 1957 "ipv4", 1958 ovskey.ovs_key_ipv4, 1959 ), 1960 ( 1961 "OVS_KEY_ATTR_IPV6", 1962 "ipv6", 1963 ovskey.ovs_key_ipv6, 1964 ), 1965 ( 1966 "OVS_KEY_ATTR_ARP", 1967 "arp", 1968 ovskey.ovs_key_arp, 1969 ), 1970 ( 1971 "OVS_KEY_ATTR_TCP", 1972 "tcp", 1973 ovskey.ovs_key_tcp, 1974 ), 1975 ( 1976 "OVS_KEY_ATTR_UDP", 1977 "udp", 1978 ovskey.ovs_key_udp, 1979 ), 1980 ( 1981 "OVS_KEY_ATTR_ICMP", 1982 "icmp", 1983 ovskey.ovs_key_icmp, 1984 ), 1985 ( 1986 "OVS_KEY_ATTR_TCP_FLAGS", 1987 "tcp_flags", 1988 lambda x: parse_flags(x, None), 1989 ), 1990 ): 1991 fld = field[1] + "(" 1992 if not flowstr.startswith(fld): 1993 continue 1994 1995 if not isinstance(field[2], types.FunctionType): 1996 nk = field[2]() 1997 flowstr, k, m = nk.parse(flowstr, field[2]) 1998 else: 1999 flowstr = flowstr[len(fld) :] 2000 flowstr, k, m = field[2](flowstr) 2001 2002 if m and mask is not None: 2003 mask["attrs"].append([field[0], m]) 2004 self["attrs"].append([field[0], k]) 2005 2006 flowstr = flowstr[strspn(flowstr, "), ") :] 2007 2008 return flowstr 2009 2010 def dpstr(self, mask=None, more=False): 2011 print_str = "" 2012 2013 for field in ( 2014 ( 2015 "OVS_KEY_ATTR_PRIORITY", 2016 "skb_priority", 2017 "%d", 2018 lambda x: False, 2019 True, 2020 ), 2021 ( 2022 "OVS_KEY_ATTR_SKB_MARK", 2023 "skb_mark", 2024 "%d", 2025 lambda x: False, 2026 True, 2027 ), 2028 ( 2029 "OVS_KEY_ATTR_RECIRC_ID", 2030 "recirc_id", 2031 "0x%08X", 2032 lambda x: False, 2033 True, 2034 ), 2035 ( 2036 "OVS_KEY_ATTR_DP_HASH", 2037 "dp_hash", 2038 "0x%08X", 2039 lambda x: False, 2040 True, 2041 ), 2042 ( 2043 "OVS_KEY_ATTR_TUNNEL", 2044 "tunnel", 2045 None, 2046 False, 2047 False, 2048 ), 2049 ( 2050 "OVS_KEY_ATTR_CT_STATE", 2051 "ct_state", 2052 "0x%04x", 2053 lambda x: False, 2054 True, 2055 ), 2056 ( 2057 "OVS_KEY_ATTR_CT_ZONE", 2058 "ct_zone", 2059 "0x%04x", 2060 lambda x: False, 2061 True, 2062 ), 2063 ( 2064 "OVS_KEY_ATTR_CT_MARK", 2065 "ct_mark", 2066 "0x%08x", 2067 lambda x: False, 2068 True, 2069 ), 2070 ( 2071 "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", 2072 None, 2073 None, 2074 False, 2075 False, 2076 ), 2077 ( 2078 "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", 2079 None, 2080 None, 2081 False, 2082 False, 2083 ), 2084 ( 2085 "OVS_KEY_ATTR_IN_PORT", 2086 "in_port", 2087 "%d", 2088 lambda x: True, 2089 True, 2090 ), 2091 ("OVS_KEY_ATTR_ETHERNET", None, None, False, False), 2092 ("OVS_KEY_ATTR_VLAN", "vlan", ovskey._vlan_dpstr, 2093 lambda x: False, True), 2094 ("OVS_KEY_ATTR_ENCAP", None, None, False, False), 2095 ( 2096 "OVS_KEY_ATTR_ETHERTYPE", 2097 "eth_type", 2098 "0x%04x", 2099 lambda x: int(x) == 0xFFFF, 2100 True, 2101 ), 2102 ("OVS_KEY_ATTR_IPV4", None, None, False, False), 2103 ("OVS_KEY_ATTR_IPV6", None, None, False, False), 2104 ("OVS_KEY_ATTR_ARP", None, None, False, False), 2105 ("OVS_KEY_ATTR_TCP", None, None, False, False), 2106 ( 2107 "OVS_KEY_ATTR_TCP_FLAGS", 2108 "tcp_flags", 2109 "0x%04x", 2110 lambda x: False, 2111 True, 2112 ), 2113 ("OVS_KEY_ATTR_UDP", None, None, False, False), 2114 ("OVS_KEY_ATTR_SCTP", None, None, False, False), 2115 ("OVS_KEY_ATTR_ICMP", None, None, False, False), 2116 ("OVS_KEY_ATTR_ICMPV6", None, None, False, False), 2117 ("OVS_KEY_ATTR_ND", None, None, False, False), 2118 ): 2119 v = self.get_attr(field[0]) 2120 if v is not None: 2121 m = None if mask is None else mask.get_attr(field[0]) 2122 fmt = field[2] # str format or callable 2123 if field[4] is False: 2124 print_str += v.dpstr(m, more) 2125 print_str += "," 2126 else: 2127 if m is None or field[3](m): 2128 val = fmt(v) if callable(fmt) else fmt % v 2129 print_str += field[1] + "(" + val + ")," 2130 elif more or m != 0: 2131 if field[0] == "OVS_KEY_ATTR_VLAN": 2132 val = "tci=0x%04x/0x%04x" % (v, m) 2133 elif callable(fmt): 2134 val = fmt(v) + "/" + fmt(m) 2135 else: 2136 val = (fmt % v) + "/" + (fmt % m) 2137 print_str += field[1] + "(" + val + ")," 2138 2139 return print_str 2140 2141 2142class encap_ovskey(ovskey): 2143 """Inner flow key attributes valid inside 802.1Q ENCAP. 2144 2145 Only L2-L4 key attributes (slots 0-21) appear inside ENCAP. 2146 Metadata-only attributes (SKB_MARK, DP_HASH, RECIRC_ID, etc.) 2147 are set to "none" -- they never appear inside ENCAP per 2148 ovs_nla_put_vlan() in net/openvswitch/flow_netlink.c. 2149 2150 nla_map indexes must match OVS_KEY_ATTR_* enum values in 2151 include/uapi/linux/openvswitch.h. 2152 """ 2153 nla_map = ( 2154 ("OVS_KEY_ATTR_UNSPEC", "none"), 2155 ("OVS_KEY_ATTR_ENCAP", "none"), # placeholder, parsed by ovskey 2156 ("OVS_KEY_ATTR_PRIORITY", "none"), # skb metadata, not in ENCAP 2157 ("OVS_KEY_ATTR_IN_PORT", "none"), # skb metadata, not in ENCAP 2158 ("OVS_KEY_ATTR_ETHERNET", "ethaddr"), 2159 ("OVS_KEY_ATTR_VLAN", "be16"), 2160 ("OVS_KEY_ATTR_ETHERTYPE", "be16"), 2161 ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"), 2162 ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"), 2163 ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"), 2164 ("OVS_KEY_ATTR_UDP", "ovs_key_udp"), 2165 ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"), 2166 ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"), 2167 ("OVS_KEY_ATTR_ARP", "ovs_key_arp"), 2168 ("OVS_KEY_ATTR_ND", "ovs_key_nd"), 2169 ("OVS_KEY_ATTR_SKB_MARK", "none"), # metadata, not in ENCAP 2170 ("OVS_KEY_ATTR_TUNNEL", "none"), # tunnel metadata, not in ENCAP 2171 ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"), 2172 ("OVS_KEY_ATTR_TCP_FLAGS", "be16"), 2173 ("OVS_KEY_ATTR_DP_HASH", "none"), # metadata, not in ENCAP 2174 ("OVS_KEY_ATTR_RECIRC_ID", "none"), # metadata, not in ENCAP 2175 ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"), 2176 ) 2177 2178 2179class OvsPacket(GenericNetlinkSocket): 2180 OVS_PACKET_CMD_MISS = 1 # Flow table miss 2181 OVS_PACKET_CMD_ACTION = 2 # USERSPACE action 2182 OVS_PACKET_CMD_EXECUTE = 3 # Apply actions to packet 2183 2184 class ovs_packet_msg(ovs_dp_msg): 2185 nla_map = ( 2186 ("OVS_PACKET_ATTR_UNSPEC", "none"), 2187 ("OVS_PACKET_ATTR_PACKET", "array(uint8)"), 2188 ("OVS_PACKET_ATTR_KEY", "ovskey"), 2189 ("OVS_PACKET_ATTR_ACTIONS", "ovsactions"), 2190 ("OVS_PACKET_ATTR_USERDATA", "none"), 2191 ("OVS_PACKET_ATTR_EGRESS_TUN_KEY", "none"), 2192 ("OVS_PACKET_ATTR_UNUSED1", "none"), 2193 ("OVS_PACKET_ATTR_UNUSED2", "none"), 2194 ("OVS_PACKET_ATTR_PROBE", "none"), 2195 ("OVS_PACKET_ATTR_MRU", "uint16"), 2196 ("OVS_PACKET_ATTR_LEN", "uint32"), 2197 ("OVS_PACKET_ATTR_HASH", "uint64"), 2198 ) 2199 2200 def __init__(self): 2201 GenericNetlinkSocket.__init__(self) 2202 self.bind(OVS_PACKET_FAMILY, OvsPacket.ovs_packet_msg) 2203 2204 def upcall_handler(self, up=None): 2205 print("listening on upcall packet handler:", self.epid) 2206 while True: 2207 try: 2208 msgs = self.get() 2209 for msg in msgs: 2210 if not up: 2211 continue 2212 if msg["cmd"] == OvsPacket.OVS_PACKET_CMD_MISS: 2213 up.miss(msg) 2214 elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_ACTION: 2215 up.action(msg) 2216 elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_EXECUTE: 2217 up.execute(msg) 2218 else: 2219 print("Unknown cmd: %d" % msg["cmd"]) 2220 except NetlinkError as ne: 2221 raise ne 2222 2223 2224class OvsDatapath(GenericNetlinkSocket): 2225 OVS_DP_F_VPORT_PIDS = 1 << 1 2226 OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3 2227 2228 class dp_cmd_msg(ovs_dp_msg): 2229 """ 2230 Message class that will be used to communicate with the kernel module. 2231 """ 2232 2233 nla_map = ( 2234 ("OVS_DP_ATTR_UNSPEC", "none"), 2235 ("OVS_DP_ATTR_NAME", "asciiz"), 2236 ("OVS_DP_ATTR_UPCALL_PID", "array(uint32)"), 2237 ("OVS_DP_ATTR_STATS", "dpstats"), 2238 ("OVS_DP_ATTR_MEGAFLOW_STATS", "megaflowstats"), 2239 ("OVS_DP_ATTR_USER_FEATURES", "uint32"), 2240 ("OVS_DP_ATTR_PAD", "none"), 2241 ("OVS_DP_ATTR_MASKS_CACHE_SIZE", "uint32"), 2242 ("OVS_DP_ATTR_PER_CPU_PIDS", "array(uint32)"), 2243 ) 2244 2245 class dpstats(nla): 2246 fields = ( 2247 ("hit", "=Q"), 2248 ("missed", "=Q"), 2249 ("lost", "=Q"), 2250 ("flows", "=Q"), 2251 ) 2252 2253 class megaflowstats(nla): 2254 fields = ( 2255 ("mask_hit", "=Q"), 2256 ("masks", "=I"), 2257 ("padding", "=I"), 2258 ("cache_hits", "=Q"), 2259 ("pad1", "=Q"), 2260 ) 2261 2262 def __init__(self): 2263 GenericNetlinkSocket.__init__(self) 2264 self.bind(OVS_DATAPATH_FAMILY, OvsDatapath.dp_cmd_msg) 2265 2266 def info(self, dpname, ifindex=0): 2267 msg = OvsDatapath.dp_cmd_msg() 2268 msg["cmd"] = OVS_DP_CMD_GET 2269 msg["version"] = OVS_DATAPATH_VERSION 2270 msg["reserved"] = 0 2271 msg["dpifindex"] = ifindex 2272 msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname]) 2273 2274 try: 2275 reply = self.nlm_request( 2276 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST 2277 ) 2278 reply = reply[0] 2279 except NetlinkError as ne: 2280 if ne.code == errno.ENODEV: 2281 reply = None 2282 else: 2283 raise ne 2284 2285 return reply 2286 2287 def create( 2288 self, dpname, shouldUpcall=False, versionStr=None, p=OvsPacket() 2289 ): 2290 msg = OvsDatapath.dp_cmd_msg() 2291 msg["cmd"] = OVS_DP_CMD_NEW 2292 if versionStr is None: 2293 msg["version"] = OVS_DATAPATH_VERSION 2294 else: 2295 msg["version"] = int(versionStr.split(":")[0], 0) 2296 msg["reserved"] = 0 2297 msg["dpifindex"] = 0 2298 msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname]) 2299 2300 dpfeatures = 0 2301 if versionStr is not None and versionStr.find(":") != -1: 2302 dpfeatures = int(versionStr.split(":")[1], 0) 2303 else: 2304 if versionStr is None or versionStr.find(":") == -1: 2305 dpfeatures |= OvsDatapath.OVS_DP_F_DISPATCH_UPCALL_PER_CPU 2306 dpfeatures &= ~OvsDatapath.OVS_DP_F_VPORT_PIDS 2307 2308 nproc = multiprocessing.cpu_count() 2309 procarray = [] 2310 for i in range(1, nproc): 2311 procarray += [int(p.epid)] 2312 msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", procarray]) 2313 msg["attrs"].append(["OVS_DP_ATTR_USER_FEATURES", dpfeatures]) 2314 if not shouldUpcall: 2315 msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", [0]]) 2316 2317 try: 2318 reply = self.nlm_request( 2319 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2320 ) 2321 reply = reply[0] 2322 except NetlinkError as ne: 2323 if ne.code == errno.EEXIST: 2324 reply = None 2325 else: 2326 raise ne 2327 2328 return reply 2329 2330 def destroy(self, dpname): 2331 msg = OvsDatapath.dp_cmd_msg() 2332 msg["cmd"] = OVS_DP_CMD_DEL 2333 msg["version"] = OVS_DATAPATH_VERSION 2334 msg["reserved"] = 0 2335 msg["dpifindex"] = 0 2336 msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname]) 2337 2338 try: 2339 reply = self.nlm_request( 2340 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2341 ) 2342 reply = reply[0] 2343 except NetlinkError as ne: 2344 if ne.code == errno.ENODEV: 2345 reply = None 2346 else: 2347 raise ne 2348 2349 return reply 2350 2351 2352class OvsVport(GenericNetlinkSocket): 2353 OVS_VPORT_TYPE_NETDEV = 1 2354 OVS_VPORT_TYPE_INTERNAL = 2 2355 OVS_VPORT_TYPE_GRE = 3 2356 OVS_VPORT_TYPE_VXLAN = 4 2357 OVS_VPORT_TYPE_GENEVE = 5 2358 2359 class ovs_vport_msg(ovs_dp_msg): 2360 nla_map = ( 2361 ("OVS_VPORT_ATTR_UNSPEC", "none"), 2362 ("OVS_VPORT_ATTR_PORT_NO", "uint32"), 2363 ("OVS_VPORT_ATTR_TYPE", "uint32"), 2364 ("OVS_VPORT_ATTR_NAME", "asciiz"), 2365 ("OVS_VPORT_ATTR_OPTIONS", "vportopts"), 2366 ("OVS_VPORT_ATTR_UPCALL_PID", "array(uint32)"), 2367 ("OVS_VPORT_ATTR_STATS", "vportstats"), 2368 ("OVS_VPORT_ATTR_PAD", "none"), 2369 ("OVS_VPORT_ATTR_IFINDEX", "uint32"), 2370 ("OVS_VPORT_ATTR_NETNSID", "uint32"), 2371 ) 2372 2373 class vportopts(nla): 2374 nla_map = ( 2375 ("OVS_TUNNEL_ATTR_UNSPEC", "none"), 2376 ("OVS_TUNNEL_ATTR_DST_PORT", "uint16"), 2377 ("OVS_TUNNEL_ATTR_EXTENSION", "none"), 2378 ) 2379 2380 class vportstats(nla): 2381 fields = ( 2382 ("rx_packets", "=Q"), 2383 ("tx_packets", "=Q"), 2384 ("rx_bytes", "=Q"), 2385 ("tx_bytes", "=Q"), 2386 ("rx_errors", "=Q"), 2387 ("tx_errors", "=Q"), 2388 ("rx_dropped", "=Q"), 2389 ("tx_dropped", "=Q"), 2390 ) 2391 2392 def type_to_str(vport_type): 2393 if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV: 2394 return "netdev" 2395 elif vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL: 2396 return "internal" 2397 elif vport_type == OvsVport.OVS_VPORT_TYPE_GRE: 2398 return "gre" 2399 elif vport_type == OvsVport.OVS_VPORT_TYPE_VXLAN: 2400 return "vxlan" 2401 elif vport_type == OvsVport.OVS_VPORT_TYPE_GENEVE: 2402 return "geneve" 2403 raise ValueError("Unknown vport type:%d" % vport_type) 2404 2405 def str_to_type(vport_type): 2406 if vport_type == "netdev": 2407 return OvsVport.OVS_VPORT_TYPE_NETDEV 2408 elif vport_type == "internal": 2409 return OvsVport.OVS_VPORT_TYPE_INTERNAL 2410 elif vport_type == "gre": 2411 return OvsVport.OVS_VPORT_TYPE_GRE 2412 elif vport_type == "vxlan": 2413 return OvsVport.OVS_VPORT_TYPE_VXLAN 2414 elif vport_type == "geneve": 2415 return OvsVport.OVS_VPORT_TYPE_GENEVE 2416 raise ValueError("Unknown vport type: '%s'" % vport_type) 2417 2418 def __init__(self, packet=OvsPacket()): 2419 GenericNetlinkSocket.__init__(self) 2420 self.bind(OVS_VPORT_FAMILY, OvsVport.ovs_vport_msg) 2421 self.upcall_packet = packet 2422 2423 def info(self, vport_name, dpifindex=0, portno=None): 2424 msg = OvsVport.ovs_vport_msg() 2425 2426 msg["cmd"] = OVS_VPORT_CMD_GET 2427 msg["version"] = OVS_DATAPATH_VERSION 2428 msg["reserved"] = 0 2429 msg["dpifindex"] = dpifindex 2430 2431 if portno is None: 2432 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_name]) 2433 else: 2434 msg["attrs"].append(["OVS_VPORT_ATTR_PORT_NO", portno]) 2435 2436 try: 2437 reply = self.nlm_request( 2438 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST 2439 ) 2440 reply = reply[0] 2441 except NetlinkError as ne: 2442 if ne.code == errno.ENODEV: 2443 reply = None 2444 else: 2445 raise ne 2446 return reply 2447 2448 def attach(self, dpindex, vport_ifname, ptype, dport, lwt): 2449 msg = OvsVport.ovs_vport_msg() 2450 2451 msg["cmd"] = OVS_VPORT_CMD_NEW 2452 msg["version"] = OVS_DATAPATH_VERSION 2453 msg["reserved"] = 0 2454 msg["dpifindex"] = dpindex 2455 port_type = OvsVport.str_to_type(ptype) 2456 2457 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname]) 2458 msg["attrs"].append( 2459 ["OVS_VPORT_ATTR_UPCALL_PID", [self.upcall_packet.epid]] 2460 ) 2461 2462 TUNNEL_DEFAULTS = [("geneve", 6081), 2463 ("gre", 0), 2464 ("vxlan", 4789)] 2465 2466 for tnl in TUNNEL_DEFAULTS: 2467 if ptype == tnl[0]: 2468 if not dport: 2469 dport = tnl[1] 2470 2471 if not lwt: 2472 if tnl[0] == "gre": 2473 # GRE tunnels have no options. 2474 break 2475 2476 vportopt = OvsVport.ovs_vport_msg.vportopts() 2477 vportopt["attrs"].append( 2478 ["OVS_TUNNEL_ATTR_DST_PORT", dport] 2479 ) 2480 msg["attrs"].append( 2481 ["OVS_VPORT_ATTR_OPTIONS", vportopt] 2482 ) 2483 else: 2484 port_type = OvsVport.OVS_VPORT_TYPE_NETDEV 2485 ipr = pyroute2.iproute.IPRoute() 2486 2487 if tnl[0] == "geneve": 2488 ipr.link("add", ifname=vport_ifname, kind=tnl[0], 2489 geneve_port=dport, 2490 geneve_collect_metadata=True, 2491 geneve_udp_zero_csum6_rx=1) 2492 elif tnl[0] == "gre": 2493 ipr.link("add", ifname=vport_ifname, kind="gretap", 2494 gre_collect_metadata=True) 2495 elif tnl[0] == "vxlan": 2496 ipr.link("add", ifname=vport_ifname, kind=tnl[0], 2497 vxlan_learning=0, vxlan_collect_metadata=1, 2498 vxlan_udp_zero_csum6_rx=1, vxlan_port=dport) 2499 break 2500 msg["attrs"].append(["OVS_VPORT_ATTR_TYPE", port_type]) 2501 2502 try: 2503 reply = self.nlm_request( 2504 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2505 ) 2506 reply = reply[0] 2507 except NetlinkError as ne: 2508 if ne.code == errno.EEXIST: 2509 reply = None 2510 else: 2511 raise ne 2512 return reply 2513 2514 def reset_upcall(self, dpindex, vport_ifname, p=None): 2515 msg = OvsVport.ovs_vport_msg() 2516 2517 msg["cmd"] = OVS_VPORT_CMD_SET 2518 msg["version"] = OVS_DATAPATH_VERSION 2519 msg["reserved"] = 0 2520 msg["dpifindex"] = dpindex 2521 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname]) 2522 2523 if p == None: 2524 p = self.upcall_packet 2525 else: 2526 self.upcall_packet = p 2527 2528 msg["attrs"].append(["OVS_VPORT_ATTR_UPCALL_PID", [p.epid]]) 2529 2530 try: 2531 reply = self.nlm_request( 2532 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2533 ) 2534 reply = reply[0] 2535 except NetlinkError as ne: 2536 raise ne 2537 return reply 2538 2539 def detach(self, dpindex, vport_ifname): 2540 msg = OvsVport.ovs_vport_msg() 2541 2542 msg["cmd"] = OVS_VPORT_CMD_DEL 2543 msg["version"] = OVS_DATAPATH_VERSION 2544 msg["reserved"] = 0 2545 msg["dpifindex"] = dpindex 2546 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname]) 2547 2548 try: 2549 reply = self.nlm_request( 2550 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2551 ) 2552 reply = reply[0] 2553 except NetlinkError as ne: 2554 if ne.code == errno.ENODEV: 2555 reply = None 2556 else: 2557 raise ne 2558 return reply 2559 2560 def upcall_handler(self, handler=None): 2561 self.upcall_packet.upcall_handler(handler) 2562 2563 2564class OvsFlow(GenericNetlinkSocket): 2565 class ovs_flow_msg(ovs_dp_msg): 2566 nla_map = ( 2567 ("OVS_FLOW_ATTR_UNSPEC", "none"), 2568 ("OVS_FLOW_ATTR_KEY", "ovskey"), 2569 ("OVS_FLOW_ATTR_ACTIONS", "ovsactions"), 2570 ("OVS_FLOW_ATTR_STATS", "flowstats"), 2571 ("OVS_FLOW_ATTR_TCP_FLAGS", "uint8"), 2572 ("OVS_FLOW_ATTR_USED", "uint64"), 2573 ("OVS_FLOW_ATTR_CLEAR", "none"), 2574 ("OVS_FLOW_ATTR_MASK", "ovskey"), 2575 ("OVS_FLOW_ATTR_PROBE", "none"), 2576 ("OVS_FLOW_ATTR_UFID", "array(uint32)"), 2577 ("OVS_FLOW_ATTR_UFID_FLAGS", "uint32"), 2578 ) 2579 2580 class flowstats(nla): 2581 fields = ( 2582 ("packets", "=Q"), 2583 ("bytes", "=Q"), 2584 ) 2585 2586 def dpstr(self, more=False): 2587 ufid = self.get_attr("OVS_FLOW_ATTR_UFID") 2588 ufid_str = "" 2589 if ufid is not None: 2590 ufid_str = ( 2591 "ufid:{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format( 2592 ufid[0], 2593 ufid[1] >> 16, 2594 ufid[1] & 0xFFFF, 2595 ufid[2] >> 16, 2596 ufid[2] & 0, 2597 ufid[3], 2598 ) 2599 ) 2600 2601 key_field = self.get_attr("OVS_FLOW_ATTR_KEY") 2602 keymsg = None 2603 if key_field is not None: 2604 keymsg = key_field 2605 2606 mask_field = self.get_attr("OVS_FLOW_ATTR_MASK") 2607 maskmsg = None 2608 if mask_field is not None: 2609 maskmsg = mask_field 2610 2611 acts_field = self.get_attr("OVS_FLOW_ATTR_ACTIONS") 2612 actsmsg = None 2613 if acts_field is not None: 2614 actsmsg = acts_field 2615 2616 print_str = "" 2617 2618 if more: 2619 print_str += ufid_str + "," 2620 2621 if keymsg is not None: 2622 print_str += keymsg.dpstr(maskmsg, more) 2623 2624 stats = self.get_attr("OVS_FLOW_ATTR_STATS") 2625 if stats is None: 2626 print_str += " packets:0, bytes:0," 2627 else: 2628 print_str += " packets:%d, bytes:%d," % ( 2629 stats["packets"], 2630 stats["bytes"], 2631 ) 2632 2633 used = self.get_attr("OVS_FLOW_ATTR_USED") 2634 print_str += " used:" 2635 if used is None: 2636 print_str += "never," 2637 else: 2638 used_time = int(used) 2639 cur_time_sec = time.clock_gettime(time.CLOCK_MONOTONIC) 2640 used_time = (cur_time_sec * 1000) - used_time 2641 print_str += "{}s,".format(used_time / 1000) 2642 2643 print_str += " actions:" 2644 if ( 2645 actsmsg is None 2646 or "attrs" not in actsmsg 2647 or len(actsmsg["attrs"]) == 0 2648 ): 2649 print_str += "drop" 2650 else: 2651 print_str += actsmsg.dpstr(more) 2652 2653 return print_str 2654 2655 def parse(self, flowstr, actstr, dpidx=0): 2656 OVS_UFID_F_OMIT_KEY = 1 << 0 2657 OVS_UFID_F_OMIT_MASK = 1 << 1 2658 OVS_UFID_F_OMIT_ACTIONS = 1 << 2 2659 2660 self["cmd"] = 0 2661 self["version"] = 0 2662 self["reserved"] = 0 2663 self["dpifindex"] = 0 2664 2665 if flowstr.startswith("ufid:"): 2666 count = 5 2667 while flowstr[count] != ",": 2668 count += 1 2669 ufidstr = flowstr[5:count] 2670 flowstr = flowstr[count + 1 :] 2671 else: 2672 ufidstr = str(uuid.uuid4()) 2673 uuidRawObj = uuid.UUID(ufidstr).fields 2674 2675 self["attrs"].append( 2676 [ 2677 "OVS_FLOW_ATTR_UFID", 2678 [ 2679 uuidRawObj[0], 2680 uuidRawObj[1] << 16 | uuidRawObj[2], 2681 uuidRawObj[3] << 24 2682 | uuidRawObj[4] << 16 2683 | uuidRawObj[5] & (0xFF << 32) >> 32, 2684 uuidRawObj[5] & (0xFFFFFFFF), 2685 ], 2686 ] 2687 ) 2688 self["attrs"].append( 2689 [ 2690 "OVS_FLOW_ATTR_UFID_FLAGS", 2691 int( 2692 OVS_UFID_F_OMIT_KEY 2693 | OVS_UFID_F_OMIT_MASK 2694 | OVS_UFID_F_OMIT_ACTIONS 2695 ), 2696 ] 2697 ) 2698 2699 k = ovskey() 2700 m = ovskey() 2701 k.parse(flowstr, m) 2702 self["attrs"].append(["OVS_FLOW_ATTR_KEY", k]) 2703 self["attrs"].append(["OVS_FLOW_ATTR_MASK", m]) 2704 2705 if actstr is not None: 2706 a = ovsactions() 2707 a.parse(actstr) 2708 self["attrs"].append(["OVS_FLOW_ATTR_ACTIONS", a]) 2709 2710 def __init__(self): 2711 GenericNetlinkSocket.__init__(self) 2712 2713 self.bind(OVS_FLOW_FAMILY, OvsFlow.ovs_flow_msg) 2714 2715 def add_flow(self, dpifindex, flowmsg): 2716 """ 2717 Send a new flow message to the kernel. 2718 2719 dpifindex should be a valid datapath obtained by calling 2720 into the OvsDatapath lookup 2721 2722 flowmsg is a flow object obtained by calling a dpparse 2723 """ 2724 2725 flowmsg["cmd"] = OVS_FLOW_CMD_NEW 2726 flowmsg["version"] = OVS_DATAPATH_VERSION 2727 flowmsg["reserved"] = 0 2728 flowmsg["dpifindex"] = dpifindex 2729 2730 try: 2731 reply = self.nlm_request( 2732 flowmsg, 2733 msg_type=self.prid, 2734 msg_flags=NLM_F_REQUEST | NLM_F_ACK, 2735 ) 2736 reply = reply[0] 2737 except NetlinkError as ne: 2738 print(flowmsg) 2739 raise ne 2740 return reply 2741 2742 def mod_flow(self, dpifindex, flowmsg): 2743 """Modify an existing flow in the kernel.""" 2744 flowmsg["cmd"] = OVS_FLOW_CMD_SET 2745 flowmsg["version"] = OVS_DATAPATH_VERSION 2746 flowmsg["reserved"] = 0 2747 flowmsg["dpifindex"] = dpifindex 2748 2749 try: 2750 reply = self.nlm_request( 2751 flowmsg, 2752 msg_type=self.prid, 2753 msg_flags=NLM_F_REQUEST | NLM_F_ACK, 2754 ) 2755 reply = reply[0] 2756 except NetlinkError as ne: 2757 print(flowmsg) 2758 raise ne 2759 return reply 2760 2761 def del_flows(self, dpifindex): 2762 """ 2763 Send a del message to the kernel that will drop all flows. 2764 2765 dpifindex should be a valid datapath obtained by calling 2766 into the OvsDatapath lookup 2767 """ 2768 2769 flowmsg = OvsFlow.ovs_flow_msg() 2770 flowmsg["cmd"] = OVS_FLOW_CMD_DEL 2771 flowmsg["version"] = OVS_DATAPATH_VERSION 2772 flowmsg["reserved"] = 0 2773 flowmsg["dpifindex"] = dpifindex 2774 2775 try: 2776 reply = self.nlm_request( 2777 flowmsg, 2778 msg_type=self.prid, 2779 msg_flags=NLM_F_REQUEST | NLM_F_ACK, 2780 ) 2781 reply = reply[0] 2782 except NetlinkError as ne: 2783 print(flowmsg) 2784 raise ne 2785 return reply 2786 2787 def dump(self, dpifindex, flowspec=None): 2788 """ 2789 Returns a list of messages containing flows. 2790 2791 dpifindex should be a valid datapath obtained by calling 2792 into the OvsDatapath lookup 2793 2794 flowpsec is a string which represents a flow in the dpctl 2795 format. 2796 """ 2797 msg = OvsFlow.ovs_flow_msg() 2798 2799 msg["cmd"] = OVS_FLOW_CMD_GET 2800 msg["version"] = OVS_DATAPATH_VERSION 2801 msg["reserved"] = 0 2802 msg["dpifindex"] = dpifindex 2803 2804 msg_flags = NLM_F_REQUEST | NLM_F_ACK 2805 if flowspec is None: 2806 msg_flags |= NLM_F_DUMP 2807 rep = None 2808 2809 try: 2810 rep = self.nlm_request( 2811 msg, 2812 msg_type=self.prid, 2813 msg_flags=msg_flags, 2814 ) 2815 except NetlinkError as ne: 2816 raise ne 2817 return rep 2818 2819 def miss(self, packetmsg): 2820 seq = packetmsg["header"]["sequence_number"] 2821 keystr = "(none)" 2822 key_field = packetmsg.get_attr("OVS_PACKET_ATTR_KEY") 2823 if key_field is not None: 2824 keystr = key_field.dpstr(None, True) 2825 2826 pktdata = packetmsg.get_attr("OVS_PACKET_ATTR_PACKET") 2827 pktpres = "yes" if pktdata is not None else "no" 2828 2829 print("MISS upcall[%d/%s]: %s" % (seq, pktpres, keystr), flush=True) 2830 2831 def execute(self, packetmsg): 2832 print("userspace execute command", flush=True) 2833 2834 def action(self, packetmsg): 2835 print("userspace action command", flush=True) 2836 2837 2838class psample_sample(genlmsg): 2839 nla_map = ( 2840 ("PSAMPLE_ATTR_IIFINDEX", "none"), 2841 ("PSAMPLE_ATTR_OIFINDEX", "none"), 2842 ("PSAMPLE_ATTR_ORIGSIZE", "none"), 2843 ("PSAMPLE_ATTR_SAMPLE_GROUP", "uint32"), 2844 ("PSAMPLE_ATTR_GROUP_SEQ", "none"), 2845 ("PSAMPLE_ATTR_SAMPLE_RATE", "uint32"), 2846 ("PSAMPLE_ATTR_DATA", "array(uint8)"), 2847 ("PSAMPLE_ATTR_GROUP_REFCOUNT", "none"), 2848 ("PSAMPLE_ATTR_TUNNEL", "none"), 2849 ("PSAMPLE_ATTR_PAD", "none"), 2850 ("PSAMPLE_ATTR_OUT_TC", "none"), 2851 ("PSAMPLE_ATTR_OUT_TC_OCC", "none"), 2852 ("PSAMPLE_ATTR_LATENCY", "none"), 2853 ("PSAMPLE_ATTR_TIMESTAMP", "none"), 2854 ("PSAMPLE_ATTR_PROTO", "none"), 2855 ("PSAMPLE_ATTR_USER_COOKIE", "array(uint8)"), 2856 ) 2857 2858 def dpstr(self): 2859 fields = [] 2860 data = "" 2861 for (attr, value) in self["attrs"]: 2862 if attr == "PSAMPLE_ATTR_SAMPLE_GROUP": 2863 fields.append("group:%d" % value) 2864 if attr == "PSAMPLE_ATTR_SAMPLE_RATE": 2865 fields.append("rate:%d" % value) 2866 if attr == "PSAMPLE_ATTR_USER_COOKIE": 2867 value = "".join(format(x, "02x") for x in value) 2868 fields.append("cookie:%s" % value) 2869 if attr == "PSAMPLE_ATTR_DATA" and len(value) > 0: 2870 data = "data:%s" % "".join(format(x, "02x") for x in value) 2871 2872 return ("%s %s" % (",".join(fields), data)).strip() 2873 2874 2875class psample_msg(Marshal): 2876 PSAMPLE_CMD_SAMPLE = 0 2877 PSAMPLE_CMD_GET_GROUP = 1 2878 PSAMPLE_CMD_NEW_GROUP = 2 2879 PSAMPLE_CMD_DEL_GROUP = 3 2880 PSAMPLE_CMD_SET_FILTER = 4 2881 msg_map = {PSAMPLE_CMD_SAMPLE: psample_sample} 2882 2883 2884class PsampleEvent(EventSocket): 2885 genl_family = "psample" 2886 mcast_groups = ["packets"] 2887 marshal_class = psample_msg 2888 2889 def read_samples(self): 2890 print("listening for psample events", flush=True) 2891 while True: 2892 try: 2893 for msg in self.get(): 2894 print(msg.dpstr(), flush=True) 2895 except NetlinkError as ne: 2896 raise ne 2897 2898 2899def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()): 2900 dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME") 2901 base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS") 2902 megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS") 2903 user_features = dp_lookup_rep.get_attr("OVS_DP_ATTR_USER_FEATURES") 2904 masks_cache_size = dp_lookup_rep.get_attr("OVS_DP_ATTR_MASKS_CACHE_SIZE") 2905 2906 print("%s:" % dp_name) 2907 print( 2908 " lookups: hit:%d missed:%d lost:%d" 2909 % (base_stats["hit"], base_stats["missed"], base_stats["lost"]) 2910 ) 2911 print(" flows:%d" % base_stats["flows"]) 2912 pkts = base_stats["hit"] + base_stats["missed"] 2913 avg = (megaflow_stats["mask_hit"] / pkts) if pkts != 0 else 0.0 2914 print( 2915 " masks: hit:%d total:%d hit/pkt:%f" 2916 % (megaflow_stats["mask_hit"], megaflow_stats["masks"], avg) 2917 ) 2918 print(" caches:") 2919 print(" masks-cache: size:%d" % masks_cache_size) 2920 2921 if user_features is not None: 2922 print(" features: 0x%X" % user_features) 2923 2924 # port print out 2925 for iface in ndb.interfaces: 2926 rep = vpl.info(iface.ifname, ifindex) 2927 if rep is not None: 2928 opts = "" 2929 vpo = rep.get_attr("OVS_VPORT_ATTR_OPTIONS") 2930 if vpo: 2931 dpo = vpo.get_attr("OVS_TUNNEL_ATTR_DST_PORT") 2932 if dpo: 2933 opts += " tnl-dport:%s" % dpo 2934 print( 2935 " port %d: %s (%s%s)" 2936 % ( 2937 rep.get_attr("OVS_VPORT_ATTR_PORT_NO"), 2938 rep.get_attr("OVS_VPORT_ATTR_NAME"), 2939 OvsVport.type_to_str(rep.get_attr("OVS_VPORT_ATTR_TYPE")), 2940 opts, 2941 ) 2942 ) 2943 2944 2945def main(argv): 2946 nlmsg_atoms.encap_ovskey = encap_ovskey 2947 nlmsg_atoms.ovskey = ovskey 2948 nlmsg_atoms.ovsactions = ovsactions 2949 2950 # version check for pyroute2 2951 prverscheck = pyroute2.__version__.split(".") 2952 if int(prverscheck[0]) == 0 and int(prverscheck[1]) < 6: 2953 print("Need to upgrade the python pyroute2 package to >= 0.6.") 2954 sys.exit(1) 2955 2956 parser = argparse.ArgumentParser() 2957 parser.add_argument( 2958 "-v", 2959 "--verbose", 2960 action="count", 2961 help="Increment 'verbose' output counter.", 2962 default=0, 2963 ) 2964 subparsers = parser.add_subparsers(dest="subcommand") 2965 2966 showdpcmd = subparsers.add_parser("show") 2967 showdpcmd.add_argument( 2968 "showdp", metavar="N", type=str, nargs="?", help="Datapath Name" 2969 ) 2970 2971 adddpcmd = subparsers.add_parser("add-dp") 2972 adddpcmd.add_argument("adddp", help="Datapath Name") 2973 adddpcmd.add_argument( 2974 "-u", 2975 "--upcall", 2976 action="store_true", 2977 help="Leave open a reader for upcalls", 2978 ) 2979 adddpcmd.add_argument( 2980 "-V", 2981 "--versioning", 2982 required=False, 2983 help="Specify a custom version / feature string", 2984 ) 2985 2986 deldpcmd = subparsers.add_parser("del-dp") 2987 deldpcmd.add_argument("deldp", help="Datapath Name") 2988 2989 addifcmd = subparsers.add_parser("add-if") 2990 addifcmd.add_argument("dpname", help="Datapath Name") 2991 addifcmd.add_argument("addif", help="Interface name for adding") 2992 addifcmd.add_argument( 2993 "-u", 2994 "--upcall", 2995 action="store_true", 2996 help="Leave open a reader for upcalls", 2997 ) 2998 addifcmd.add_argument( 2999 "-t", 3000 "--ptype", 3001 type=str, 3002 default="netdev", 3003 choices=["netdev", "internal", "gre", "geneve", "vxlan"], 3004 help="Interface type (default netdev)", 3005 ) 3006 addifcmd.add_argument( 3007 "-p", 3008 "--dport", 3009 type=int, 3010 default=0, 3011 help="Destination port (0 for default)" 3012 ) 3013 addifcmd.add_argument( 3014 "-l", 3015 "--lwt", 3016 action=argparse.BooleanOptionalAction, 3017 default=True, 3018 help="Use LWT infrastructure instead of vport (default true)." 3019 ) 3020 delifcmd = subparsers.add_parser("del-if") 3021 delifcmd.add_argument("dpname", help="Datapath Name") 3022 delifcmd.add_argument("delif", help="Interface name for adding") 3023 delifcmd.add_argument("-d", 3024 "--dellink", 3025 type=bool, default=False, 3026 help="Delete the link as well.") 3027 3028 dumpflcmd = subparsers.add_parser("dump-flows") 3029 dumpflcmd.add_argument("dumpdp", help="Datapath Name") 3030 3031 addflcmd = subparsers.add_parser("add-flow") 3032 addflcmd.add_argument("flbr", help="Datapath name") 3033 addflcmd.add_argument("flow", help="Flow specification") 3034 addflcmd.add_argument("acts", help="Flow actions") 3035 3036 modflcmd = subparsers.add_parser("mod-flow") 3037 modflcmd.add_argument("modbr", help="Datapath name") 3038 modflcmd.add_argument("modflow", help="Flow specification") 3039 modflcmd.add_argument("modacts", help="Flow actions", 3040 nargs="?", default=None) 3041 3042 delfscmd = subparsers.add_parser("del-flows") 3043 delfscmd.add_argument("flsbr", help="Datapath name") 3044 3045 subparsers.add_parser("psample-events") 3046 3047 args = parser.parse_args() 3048 3049 if args.verbose > 0: 3050 if args.verbose > 1: 3051 logging.basicConfig(level=logging.DEBUG) 3052 3053 ovspk = OvsPacket() 3054 ovsdp = OvsDatapath() 3055 ovsvp = OvsVport(ovspk) 3056 ovsflow = OvsFlow() 3057 ndb = NDB() 3058 3059 sys.setrecursionlimit(100000) 3060 3061 if args.subcommand == "psample-events": 3062 PsampleEvent().read_samples() 3063 3064 if hasattr(args, "showdp"): 3065 found = False 3066 for iface in ndb.interfaces: 3067 rep = None 3068 if args.showdp is None: 3069 rep = ovsdp.info(iface.ifname, 0) 3070 elif args.showdp == iface.ifname: 3071 rep = ovsdp.info(iface.ifname, 0) 3072 3073 if rep is not None: 3074 found = True 3075 print_ovsdp_full(rep, iface.index, ndb, ovsvp) 3076 3077 if not found: 3078 msg = "No DP found" 3079 if args.showdp is not None: 3080 msg += ":'%s'" % args.showdp 3081 print(msg) 3082 elif hasattr(args, "adddp"): 3083 rep = ovsdp.create(args.adddp, args.upcall, args.versioning, ovspk) 3084 if rep is None: 3085 print("DP '%s' already exists" % args.adddp) 3086 else: 3087 print("DP '%s' added" % args.adddp) 3088 if args.upcall: 3089 ovspk.upcall_handler(ovsflow) 3090 elif hasattr(args, "deldp"): 3091 ovsdp.destroy(args.deldp) 3092 elif hasattr(args, "addif"): 3093 rep = ovsdp.info(args.dpname, 0) 3094 if rep is None: 3095 print("DP '%s' not found." % args.dpname) 3096 return 1 3097 dpindex = rep["dpifindex"] 3098 rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype, 3099 args.dport, args.lwt) 3100 msg = "vport '%s'" % args.addif 3101 if rep and rep["header"]["error"] is None: 3102 msg += " added." 3103 else: 3104 msg += " failed to add." 3105 if args.upcall: 3106 if rep is None: 3107 rep = ovsvp.reset_upcall(dpindex, args.addif, ovspk) 3108 ovsvp.upcall_handler(ovsflow) 3109 elif hasattr(args, "delif"): 3110 rep = ovsdp.info(args.dpname, 0) 3111 if rep is None: 3112 print("DP '%s' not found." % args.dpname) 3113 return 1 3114 rep = ovsvp.detach(rep["dpifindex"], args.delif) 3115 msg = "vport '%s'" % args.delif 3116 if rep and rep["header"]["error"] is None: 3117 msg += " removed." 3118 else: 3119 msg += " failed to remove." 3120 if args.dellink: 3121 ipr = pyroute2.iproute.IPRoute() 3122 ipr.link("del", index=ipr.link_lookup(ifname=args.delif)[0]) 3123 elif hasattr(args, "dumpdp"): 3124 rep = ovsdp.info(args.dumpdp, 0) 3125 if rep is None: 3126 print("DP '%s' not found." % args.dumpdp) 3127 return 1 3128 rep = ovsflow.dump(rep["dpifindex"]) 3129 for flow in rep: 3130 print(flow.dpstr(True if args.verbose > 0 else False)) 3131 elif hasattr(args, "flbr"): 3132 rep = ovsdp.info(args.flbr, 0) 3133 if rep is None: 3134 print("DP '%s' not found." % args.flbr) 3135 return 1 3136 flow = OvsFlow.ovs_flow_msg() 3137 flow.parse(args.flow, args.acts, rep["dpifindex"]) 3138 ovsflow.add_flow(rep["dpifindex"], flow) 3139 elif hasattr(args, "modbr"): 3140 rep = ovsdp.info(args.modbr, 0) 3141 if rep is None: 3142 print(f"DP '{args.modbr}' not found.") 3143 return 1 3144 flow = OvsFlow.ovs_flow_msg() 3145 flow.parse(args.modflow, args.modacts, rep["dpifindex"]) 3146 ovsflow.mod_flow(rep["dpifindex"], flow) 3147 elif hasattr(args, "flsbr"): 3148 rep = ovsdp.info(args.flsbr, 0) 3149 if rep is None: 3150 print("DP '%s' not found." % args.flsbr) 3151 ovsflow.del_flows(rep["dpifindex"]) 3152 3153 return 0 3154 3155 3156if __name__ == "__main__": 3157 sys.exit(main(sys.argv)) 3158