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