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 elif parse_starts_block(actstr, "trunc(", False): 867 parencount += 1 868 actstr, val = parse_extract_field( 869 actstr, 870 "trunc(", 871 r"([0-9]+)", 872 int, 873 False, 874 None, 875 ) 876 self["attrs"].append(["OVS_ACTION_ATTR_TRUNC", val]) 877 parsed = True 878 879 actstr = actstr[strspn(actstr, ", ") :] 880 while parencount > 0: 881 parencount -= 1 882 actstr = actstr[strspn(actstr, " "):] 883 if len(actstr) and actstr[0] != ")": 884 raise ValueError("Action str: '%s' unbalanced" % actstr) 885 actstr = actstr[1:] 886 887 if len(actstr) and actstr[0] == ")": 888 return (totallen - len(actstr)) 889 890 actstr = actstr[strspn(actstr, ", ") :] 891 892 if not parsed: 893 raise ValueError("Action str: '%s' not supported" % actstr) 894 895 return (totallen - len(actstr)) 896 897 898class ovskey(nla): 899 nla_flags = NLA_F_NESTED 900 nla_map = ( 901 ("OVS_KEY_ATTR_UNSPEC", "none"), 902 ("OVS_KEY_ATTR_ENCAP", "none"), 903 ("OVS_KEY_ATTR_PRIORITY", "uint32"), 904 ("OVS_KEY_ATTR_IN_PORT", "uint32"), 905 ("OVS_KEY_ATTR_ETHERNET", "ethaddr"), 906 ("OVS_KEY_ATTR_VLAN", "uint16"), 907 ("OVS_KEY_ATTR_ETHERTYPE", "be16"), 908 ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"), 909 ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"), 910 ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"), 911 ("OVS_KEY_ATTR_UDP", "ovs_key_udp"), 912 ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"), 913 ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"), 914 ("OVS_KEY_ATTR_ARP", "ovs_key_arp"), 915 ("OVS_KEY_ATTR_ND", "ovs_key_nd"), 916 ("OVS_KEY_ATTR_SKB_MARK", "uint32"), 917 ("OVS_KEY_ATTR_TUNNEL", "ovs_key_tunnel"), 918 ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"), 919 ("OVS_KEY_ATTR_TCP_FLAGS", "be16"), 920 ("OVS_KEY_ATTR_DP_HASH", "uint32"), 921 ("OVS_KEY_ATTR_RECIRC_ID", "uint32"), 922 ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"), 923 ("OVS_KEY_ATTR_CT_STATE", "uint32"), 924 ("OVS_KEY_ATTR_CT_ZONE", "uint16"), 925 ("OVS_KEY_ATTR_CT_MARK", "uint32"), 926 ("OVS_KEY_ATTR_CT_LABELS", "none"), 927 ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", "ovs_key_ct_tuple_ipv4"), 928 ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", "ovs_key_ct_tuple_ipv6"), 929 ("OVS_KEY_ATTR_NSH", "none"), 930 ("OVS_KEY_ATTR_PACKET_TYPE", "none"), 931 ("OVS_KEY_ATTR_ND_EXTENSIONS", "none"), 932 ("OVS_KEY_ATTR_TUNNEL_INFO", "none"), 933 ("OVS_KEY_ATTR_IPV6_EXTENSIONS", "none"), 934 ) 935 936 class ovs_key_proto(nla): 937 fields = ( 938 ("src", "!H"), 939 ("dst", "!H"), 940 ) 941 942 fields_map = ( 943 ("src", "src", "%d", lambda x: int(x) if x else 0, 944 convert_int(16)), 945 ("dst", "dst", "%d", lambda x: int(x) if x else 0, 946 convert_int(16)), 947 ) 948 949 def __init__( 950 self, 951 protostr, 952 data=None, 953 offset=None, 954 parent=None, 955 length=None, 956 init=None, 957 ): 958 self.proto_str = protostr 959 nla.__init__( 960 self, 961 data=data, 962 offset=offset, 963 parent=parent, 964 length=length, 965 init=init, 966 ) 967 968 def parse(self, flowstr, typeInst): 969 if not flowstr.startswith(self.proto_str): 970 return None, None 971 972 k = typeInst() 973 m = typeInst() 974 975 flowstr = flowstr[len(self.proto_str) :] 976 if flowstr.startswith("("): 977 flowstr = flowstr[1:] 978 979 keybits = b"" 980 maskbits = b"" 981 for f in self.fields_map: 982 if flowstr.startswith(f[1]): 983 # the following assumes that the field looks 984 # something like 'field.' where '.' is a 985 # character that we don't exactly care about. 986 flowstr = flowstr[len(f[1]) + 1 :] 987 splitchar = 0 988 for c in flowstr: 989 if c == "," or c == ")": 990 break 991 splitchar += 1 992 data = flowstr[:splitchar] 993 flowstr = flowstr[splitchar:] 994 else: 995 data = "" 996 997 if len(f) > 4: 998 k[f[0]], m[f[0]] = f[4](data) 999 else: 1000 k[f[0]] = f[3](data) 1001 m[f[0]] = f[3](data) 1002 1003 flowstr = flowstr[strspn(flowstr, ", ") :] 1004 if len(flowstr) == 0: 1005 return flowstr, k, m 1006 1007 flowstr = flowstr[strspn(flowstr, "), ") :] 1008 1009 return flowstr, k, m 1010 1011 def dpstr(self, masked=None, more=False): 1012 outstr = self.proto_str + "(" 1013 first = False 1014 for f in self.fields_map: 1015 if first: 1016 outstr += "," 1017 if masked is None: 1018 outstr += "%s=" % f[0] 1019 if isinstance(f[2], str): 1020 outstr += f[2] % self[f[1]] 1021 else: 1022 outstr += f[2](self[f[1]]) 1023 first = True 1024 elif more or f[3](masked[f[1]]) != 0: 1025 outstr += "%s=" % f[0] 1026 if isinstance(f[2], str): 1027 outstr += f[2] % self[f[1]] 1028 else: 1029 outstr += f[2](self[f[1]]) 1030 outstr += "/" 1031 if isinstance(f[2], str): 1032 outstr += f[2] % masked[f[1]] 1033 else: 1034 outstr += f[2](masked[f[1]]) 1035 first = True 1036 outstr += ")" 1037 return outstr 1038 1039 class ethaddr(ovs_key_proto): 1040 fields = ( 1041 ("src", "!6s"), 1042 ("dst", "!6s"), 1043 ) 1044 1045 fields_map = ( 1046 ( 1047 "src", 1048 "src", 1049 macstr, 1050 lambda x: int.from_bytes(x, "big"), 1051 convert_mac, 1052 ), 1053 ( 1054 "dst", 1055 "dst", 1056 macstr, 1057 lambda x: int.from_bytes(x, "big"), 1058 convert_mac, 1059 ), 1060 ) 1061 1062 def __init__( 1063 self, 1064 data=None, 1065 offset=None, 1066 parent=None, 1067 length=None, 1068 init=None, 1069 ): 1070 ovskey.ovs_key_proto.__init__( 1071 self, 1072 "eth", 1073 data=data, 1074 offset=offset, 1075 parent=parent, 1076 length=length, 1077 init=init, 1078 ) 1079 1080 class ovs_key_ipv4(ovs_key_proto): 1081 fields = ( 1082 ("src", "!I"), 1083 ("dst", "!I"), 1084 ("proto", "B"), 1085 ("tos", "B"), 1086 ("ttl", "B"), 1087 ("frag", "B"), 1088 ) 1089 1090 fields_map = ( 1091 ( 1092 "src", 1093 "src", 1094 lambda x: str(ipaddress.IPv4Address(x)), 1095 int, 1096 convert_ipv4, 1097 ), 1098 ( 1099 "dst", 1100 "dst", 1101 lambda x: str(ipaddress.IPv4Address(x)), 1102 int, 1103 convert_ipv4, 1104 ), 1105 ("proto", "proto", "%d", lambda x: int(x) if x else 0, 1106 convert_int(8)), 1107 ("tos", "tos", "%d", lambda x: int(x) if x else 0, 1108 convert_int(8)), 1109 ("ttl", "ttl", "%d", lambda x: int(x) if x else 0, 1110 convert_int(8)), 1111 ("frag", "frag", "%d", lambda x: int(x) if x else 0, 1112 convert_int(8)), 1113 ) 1114 1115 def __init__( 1116 self, 1117 data=None, 1118 offset=None, 1119 parent=None, 1120 length=None, 1121 init=None, 1122 ): 1123 ovskey.ovs_key_proto.__init__( 1124 self, 1125 "ipv4", 1126 data=data, 1127 offset=offset, 1128 parent=parent, 1129 length=length, 1130 init=init, 1131 ) 1132 1133 class ovs_key_ipv6(ovs_key_proto): 1134 fields = ( 1135 ("src", "!16s"), 1136 ("dst", "!16s"), 1137 ("label", "!I"), 1138 ("proto", "B"), 1139 ("tclass", "B"), 1140 ("hlimit", "B"), 1141 ("frag", "B"), 1142 ) 1143 1144 fields_map = ( 1145 ( 1146 "src", 1147 "src", 1148 lambda x: str(ipaddress.IPv6Address(x)), 1149 lambda x: ipaddress.IPv6Address(x).packed if x else 0, 1150 convert_ipv6, 1151 ), 1152 ( 1153 "dst", 1154 "dst", 1155 lambda x: str(ipaddress.IPv6Address(x)), 1156 lambda x: ipaddress.IPv6Address(x).packed if x else 0, 1157 convert_ipv6, 1158 ), 1159 ("label", "label", "%d", lambda x: int(x) if x else 0), 1160 ("proto", "proto", "%d", lambda x: int(x) if x else 0), 1161 ("tclass", "tclass", "%d", lambda x: int(x) if x else 0), 1162 ("hlimit", "hlimit", "%d", lambda x: int(x) if x else 0), 1163 ("frag", "frag", "%d", lambda x: int(x) if x else 0), 1164 ) 1165 1166 def __init__( 1167 self, 1168 data=None, 1169 offset=None, 1170 parent=None, 1171 length=None, 1172 init=None, 1173 ): 1174 ovskey.ovs_key_proto.__init__( 1175 self, 1176 "ipv6", 1177 data=data, 1178 offset=offset, 1179 parent=parent, 1180 length=length, 1181 init=init, 1182 ) 1183 1184 class ovs_key_tcp(ovs_key_proto): 1185 def __init__( 1186 self, 1187 data=None, 1188 offset=None, 1189 parent=None, 1190 length=None, 1191 init=None, 1192 ): 1193 ovskey.ovs_key_proto.__init__( 1194 self, 1195 "tcp", 1196 data=data, 1197 offset=offset, 1198 parent=parent, 1199 length=length, 1200 init=init, 1201 ) 1202 1203 class ovs_key_udp(ovs_key_proto): 1204 def __init__( 1205 self, 1206 data=None, 1207 offset=None, 1208 parent=None, 1209 length=None, 1210 init=None, 1211 ): 1212 ovskey.ovs_key_proto.__init__( 1213 self, 1214 "udp", 1215 data=data, 1216 offset=offset, 1217 parent=parent, 1218 length=length, 1219 init=init, 1220 ) 1221 1222 class ovs_key_sctp(ovs_key_proto): 1223 def __init__( 1224 self, 1225 data=None, 1226 offset=None, 1227 parent=None, 1228 length=None, 1229 init=None, 1230 ): 1231 ovskey.ovs_key_proto.__init__( 1232 self, 1233 "sctp", 1234 data=data, 1235 offset=offset, 1236 parent=parent, 1237 length=length, 1238 init=init, 1239 ) 1240 1241 class ovs_key_icmp(ovs_key_proto): 1242 fields = ( 1243 ("type", "B"), 1244 ("code", "B"), 1245 ) 1246 1247 fields_map = ( 1248 ("type", "type", "%d", lambda x: int(x) if x else 0), 1249 ("code", "code", "%d", lambda x: int(x) if x else 0), 1250 ) 1251 1252 def __init__( 1253 self, 1254 data=None, 1255 offset=None, 1256 parent=None, 1257 length=None, 1258 init=None, 1259 ): 1260 ovskey.ovs_key_proto.__init__( 1261 self, 1262 "icmp", 1263 data=data, 1264 offset=offset, 1265 parent=parent, 1266 length=length, 1267 init=init, 1268 ) 1269 1270 class ovs_key_icmpv6(ovs_key_icmp): 1271 def __init__( 1272 self, 1273 data=None, 1274 offset=None, 1275 parent=None, 1276 length=None, 1277 init=None, 1278 ): 1279 ovskey.ovs_key_proto.__init__( 1280 self, 1281 "icmpv6", 1282 data=data, 1283 offset=offset, 1284 parent=parent, 1285 length=length, 1286 init=init, 1287 ) 1288 1289 class ovs_key_arp(ovs_key_proto): 1290 fields = ( 1291 ("sip", "!I"), 1292 ("tip", "!I"), 1293 ("op", "!H"), 1294 ("sha", "!6s"), 1295 ("tha", "!6s"), 1296 ("pad", "xx"), 1297 ) 1298 1299 fields_map = ( 1300 ( 1301 "sip", 1302 "sip", 1303 lambda x: str(ipaddress.IPv4Address(x)), 1304 int, 1305 convert_ipv4, 1306 ), 1307 ( 1308 "tip", 1309 "tip", 1310 lambda x: str(ipaddress.IPv4Address(x)), 1311 int, 1312 convert_ipv4, 1313 ), 1314 ("op", "op", "%d", lambda x: int(x) if x else 0), 1315 ( 1316 "sha", 1317 "sha", 1318 macstr, 1319 lambda x: int.from_bytes(x, "big"), 1320 convert_mac, 1321 ), 1322 ( 1323 "tha", 1324 "tha", 1325 macstr, 1326 lambda x: int.from_bytes(x, "big"), 1327 convert_mac, 1328 ), 1329 ) 1330 1331 def __init__( 1332 self, 1333 data=None, 1334 offset=None, 1335 parent=None, 1336 length=None, 1337 init=None, 1338 ): 1339 ovskey.ovs_key_proto.__init__( 1340 self, 1341 "arp", 1342 data=data, 1343 offset=offset, 1344 parent=parent, 1345 length=length, 1346 init=init, 1347 ) 1348 1349 class ovs_key_nd(ovs_key_proto): 1350 fields = ( 1351 ("target", "!16s"), 1352 ("sll", "!6s"), 1353 ("tll", "!6s"), 1354 ) 1355 1356 fields_map = ( 1357 ( 1358 "target", 1359 "target", 1360 lambda x: str(ipaddress.IPv6Address(x)), 1361 convert_ipv6, 1362 ), 1363 ("sll", "sll", macstr, lambda x: int.from_bytes(x, "big")), 1364 ("tll", "tll", macstr, lambda x: int.from_bytes(x, "big")), 1365 ) 1366 1367 def __init__( 1368 self, 1369 data=None, 1370 offset=None, 1371 parent=None, 1372 length=None, 1373 init=None, 1374 ): 1375 ovskey.ovs_key_proto.__init__( 1376 self, 1377 "nd", 1378 data=data, 1379 offset=offset, 1380 parent=parent, 1381 length=length, 1382 init=init, 1383 ) 1384 1385 class ovs_key_ct_tuple_ipv4(ovs_key_proto): 1386 fields = ( 1387 ("src", "!I"), 1388 ("dst", "!I"), 1389 ("tp_src", "!H"), 1390 ("tp_dst", "!H"), 1391 ("proto", "B"), 1392 ) 1393 1394 fields_map = ( 1395 ( 1396 "src", 1397 "src", 1398 lambda x: str(ipaddress.IPv4Address(x)), 1399 int, 1400 convert_ipv4, 1401 ), 1402 ( 1403 "dst", 1404 "dst", 1405 lambda x: str(ipaddress.IPv4Address(x)), 1406 int, 1407 convert_ipv4, 1408 ), 1409 ("tp_src", "tp_src", "%d", int), 1410 ("tp_dst", "tp_dst", "%d", int), 1411 ("proto", "proto", "%d", int), 1412 ) 1413 1414 def __init__( 1415 self, 1416 data=None, 1417 offset=None, 1418 parent=None, 1419 length=None, 1420 init=None, 1421 ): 1422 ovskey.ovs_key_proto.__init__( 1423 self, 1424 "ct_tuple4", 1425 data=data, 1426 offset=offset, 1427 parent=parent, 1428 length=length, 1429 init=init, 1430 ) 1431 1432 class ovs_key_ct_tuple_ipv6(nla): 1433 fields = ( 1434 ("src", "!16s"), 1435 ("dst", "!16s"), 1436 ("tp_src", "!H"), 1437 ("tp_dst", "!H"), 1438 ("proto", "B"), 1439 ) 1440 1441 fields_map = ( 1442 ( 1443 "src", 1444 "src", 1445 lambda x: str(ipaddress.IPv6Address(x)), 1446 convert_ipv6, 1447 ), 1448 ( 1449 "dst", 1450 "dst", 1451 lambda x: str(ipaddress.IPv6Address(x)), 1452 convert_ipv6, 1453 ), 1454 ("tp_src", "tp_src", "%d", int), 1455 ("tp_dst", "tp_dst", "%d", int), 1456 ("proto", "proto", "%d", int), 1457 ) 1458 1459 def __init__( 1460 self, 1461 data=None, 1462 offset=None, 1463 parent=None, 1464 length=None, 1465 init=None, 1466 ): 1467 ovskey.ovs_key_proto.__init__( 1468 self, 1469 "ct_tuple6", 1470 data=data, 1471 offset=offset, 1472 parent=parent, 1473 length=length, 1474 init=init, 1475 ) 1476 1477 class ovs_key_tunnel(nla): 1478 nla_flags = NLA_F_NESTED 1479 1480 nla_map = ( 1481 ("OVS_TUNNEL_KEY_ATTR_ID", "be64"), 1482 ("OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "ipaddr"), 1483 ("OVS_TUNNEL_KEY_ATTR_IPV4_DST", "ipaddr"), 1484 ("OVS_TUNNEL_KEY_ATTR_TOS", "uint8"), 1485 ("OVS_TUNNEL_KEY_ATTR_TTL", "uint8"), 1486 ("OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", "flag"), 1487 ("OVS_TUNNEL_KEY_ATTR_CSUM", "flag"), 1488 ("OVS_TUNNEL_KEY_ATTR_OAM", "flag"), 1489 ("OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS", "array(uint32)"), 1490 ("OVS_TUNNEL_KEY_ATTR_TP_SRC", "be16"), 1491 ("OVS_TUNNEL_KEY_ATTR_TP_DST", "be16"), 1492 ("OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS", "none"), 1493 ("OVS_TUNNEL_KEY_ATTR_IPV6_SRC", "ipaddr"), 1494 ("OVS_TUNNEL_KEY_ATTR_IPV6_DST", "ipaddr"), 1495 ("OVS_TUNNEL_KEY_ATTR_PAD", "none"), 1496 ("OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS", "none"), 1497 ("OVS_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE", "flag"), 1498 ) 1499 1500 def parse(self, flowstr, mask=None): 1501 if not flowstr.startswith("tunnel("): 1502 return None, None 1503 1504 k = ovskey.ovs_key_tunnel() 1505 if mask is not None: 1506 mask = ovskey.ovs_key_tunnel() 1507 1508 flowstr = flowstr[len("tunnel("):] 1509 1510 v6_address = None 1511 1512 fields = [ 1513 ("tun_id=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_ID", 1514 0xffffffffffffffff, None, None), 1515 1516 ("src=", r"([0-9a-fA-F\.]+)", str, 1517 "OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "255.255.255.255", "0.0.0.0", 1518 False), 1519 ("dst=", r"([0-9a-fA-F\.]+)", str, 1520 "OVS_TUNNEL_KEY_ATTR_IPV4_DST", "255.255.255.255", "0.0.0.0", 1521 False), 1522 1523 ("ipv6_src=", r"([0-9a-fA-F:]+)", str, 1524 "OVS_TUNNEL_KEY_ATTR_IPV6_SRC", 1525 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True), 1526 ("ipv6_dst=", r"([0-9a-fA-F:]+)", str, 1527 "OVS_TUNNEL_KEY_ATTR_IPV6_DST", 1528 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True), 1529 1530 ("tos=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TOS", 255, 0, 1531 None), 1532 ("ttl=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TTL", 255, 0, 1533 None), 1534 1535 ("tp_src=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_SRC", 1536 65535, 0, None), 1537 ("tp_dst=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_DST", 1538 65535, 0, None), 1539 ] 1540 1541 forced_include = ["OVS_TUNNEL_KEY_ATTR_TTL"] 1542 1543 for prefix, regex, typ, attr_name, mask_val, default_val, v46_flag in fields: 1544 flowstr, value = parse_extract_field(flowstr, prefix, regex, typ, False) 1545 if not attr_name: 1546 raise Exception("Bad list value in tunnel fields") 1547 1548 if value is None and attr_name in forced_include: 1549 value = default_val 1550 mask_val = default_val 1551 1552 if value is not None: 1553 if v46_flag is not None: 1554 if v6_address is None: 1555 v6_address = v46_flag 1556 if v46_flag != v6_address: 1557 raise ValueError("Cannot mix v6 and v4 addresses") 1558 k["attrs"].append([attr_name, value]) 1559 if mask is not None: 1560 mask["attrs"].append([attr_name, mask_val]) 1561 else: 1562 if v46_flag is not None: 1563 if v6_address is None or v46_flag != v6_address: 1564 continue 1565 if mask is not None: 1566 mask["attrs"].append([attr_name, default_val]) 1567 1568 if k["attrs"][0][0] != "OVS_TUNNEL_KEY_ATTR_ID": 1569 raise ValueError("Needs a tunid set") 1570 1571 if flowstr.startswith("flags("): 1572 flowstr = flowstr[len("flags("):] 1573 flagspos = flowstr.find(")") 1574 flags = flowstr[:flagspos] 1575 flowstr = flowstr[flagspos + 1:] 1576 1577 flag_attrs = { 1578 "df": "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", 1579 "csum": "OVS_TUNNEL_KEY_ATTR_CSUM", 1580 "oam": "OVS_TUNNEL_KEY_ATTR_OAM" 1581 } 1582 1583 for flag in flags.split("|"): 1584 if flag in flag_attrs: 1585 k["attrs"].append([flag_attrs[flag], True]) 1586 if mask is not None: 1587 mask["attrs"].append([flag_attrs[flag], True]) 1588 1589 flowstr = flowstr[strspn(flowstr, ", ") :] 1590 return flowstr, k, mask 1591 1592 def dpstr(self, mask=None, more=False): 1593 print_str = "tunnel(" 1594 1595 flagsattrs = [] 1596 for k in self["attrs"]: 1597 noprint = False 1598 if k[0] == "OVS_TUNNEL_KEY_ATTR_ID": 1599 print_str += "tun_id=%d" % k[1] 1600 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_SRC": 1601 print_str += "src=%s" % k[1] 1602 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_DST": 1603 print_str += "dst=%s" % k[1] 1604 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_SRC": 1605 print_str += "ipv6_src=%s" % k[1] 1606 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_DST": 1607 print_str += "ipv6_dst=%s" % k[1] 1608 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TOS": 1609 print_str += "tos=%d" % k[1] 1610 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TTL": 1611 print_str += "ttl=%d" % k[1] 1612 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_SRC": 1613 print_str += "tp_src=%d" % k[1] 1614 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_DST": 1615 print_str += "tp_dst=%d" % k[1] 1616 elif k[0] == "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT": 1617 noprint = True 1618 flagsattrs.append("df") 1619 elif k[0] == "OVS_TUNNEL_KEY_ATTR_CSUM": 1620 noprint = True 1621 flagsattrs.append("csum") 1622 elif k[0] == "OVS_TUNNEL_KEY_ATTR_OAM": 1623 noprint = True 1624 flagsattrs.append("oam") 1625 1626 if not noprint: 1627 print_str += "," 1628 1629 if len(flagsattrs): 1630 print_str += "flags(" + "|".join(flagsattrs) + ")" 1631 print_str += ")" 1632 return print_str 1633 1634 class ovs_key_mpls(nla): 1635 fields = (("lse", ">I"),) 1636 1637 def parse(self, flowstr, mask=None): 1638 for field in ( 1639 ("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse), 1640 ("OVS_KEY_ATTR_SKB_MARK", "skb_mark", intparse), 1641 ("OVS_KEY_ATTR_RECIRC_ID", "recirc_id", intparse), 1642 ("OVS_KEY_ATTR_TUNNEL", "tunnel", ovskey.ovs_key_tunnel), 1643 ("OVS_KEY_ATTR_DP_HASH", "dp_hash", intparse), 1644 ("OVS_KEY_ATTR_CT_STATE", "ct_state", parse_ct_state), 1645 ("OVS_KEY_ATTR_CT_ZONE", "ct_zone", intparse), 1646 ("OVS_KEY_ATTR_CT_MARK", "ct_mark", intparse), 1647 ("OVS_KEY_ATTR_IN_PORT", "in_port", intparse), 1648 ( 1649 "OVS_KEY_ATTR_ETHERNET", 1650 "eth", 1651 ovskey.ethaddr, 1652 ), 1653 ( 1654 "OVS_KEY_ATTR_ETHERTYPE", 1655 "eth_type", 1656 lambda x: intparse(x, "0xffff"), 1657 ), 1658 ( 1659 "OVS_KEY_ATTR_IPV4", 1660 "ipv4", 1661 ovskey.ovs_key_ipv4, 1662 ), 1663 ( 1664 "OVS_KEY_ATTR_IPV6", 1665 "ipv6", 1666 ovskey.ovs_key_ipv6, 1667 ), 1668 ( 1669 "OVS_KEY_ATTR_ARP", 1670 "arp", 1671 ovskey.ovs_key_arp, 1672 ), 1673 ( 1674 "OVS_KEY_ATTR_TCP", 1675 "tcp", 1676 ovskey.ovs_key_tcp, 1677 ), 1678 ( 1679 "OVS_KEY_ATTR_UDP", 1680 "udp", 1681 ovskey.ovs_key_udp, 1682 ), 1683 ( 1684 "OVS_KEY_ATTR_ICMP", 1685 "icmp", 1686 ovskey.ovs_key_icmp, 1687 ), 1688 ( 1689 "OVS_KEY_ATTR_TCP_FLAGS", 1690 "tcp_flags", 1691 lambda x: parse_flags(x, None), 1692 ), 1693 ): 1694 fld = field[1] + "(" 1695 if not flowstr.startswith(fld): 1696 continue 1697 1698 if not isinstance(field[2], types.FunctionType): 1699 nk = field[2]() 1700 flowstr, k, m = nk.parse(flowstr, field[2]) 1701 else: 1702 flowstr = flowstr[len(fld) :] 1703 flowstr, k, m = field[2](flowstr) 1704 1705 if m and mask is not None: 1706 mask["attrs"].append([field[0], m]) 1707 self["attrs"].append([field[0], k]) 1708 1709 flowstr = flowstr[strspn(flowstr, "), ") :] 1710 1711 return flowstr 1712 1713 def dpstr(self, mask=None, more=False): 1714 print_str = "" 1715 1716 for field in ( 1717 ( 1718 "OVS_KEY_ATTR_PRIORITY", 1719 "skb_priority", 1720 "%d", 1721 lambda x: False, 1722 True, 1723 ), 1724 ( 1725 "OVS_KEY_ATTR_SKB_MARK", 1726 "skb_mark", 1727 "%d", 1728 lambda x: False, 1729 True, 1730 ), 1731 ( 1732 "OVS_KEY_ATTR_RECIRC_ID", 1733 "recirc_id", 1734 "0x%08X", 1735 lambda x: False, 1736 True, 1737 ), 1738 ( 1739 "OVS_KEY_ATTR_DP_HASH", 1740 "dp_hash", 1741 "0x%08X", 1742 lambda x: False, 1743 True, 1744 ), 1745 ( 1746 "OVS_KEY_ATTR_TUNNEL", 1747 "tunnel", 1748 None, 1749 False, 1750 False, 1751 ), 1752 ( 1753 "OVS_KEY_ATTR_CT_STATE", 1754 "ct_state", 1755 "0x%04x", 1756 lambda x: False, 1757 True, 1758 ), 1759 ( 1760 "OVS_KEY_ATTR_CT_ZONE", 1761 "ct_zone", 1762 "0x%04x", 1763 lambda x: False, 1764 True, 1765 ), 1766 ( 1767 "OVS_KEY_ATTR_CT_MARK", 1768 "ct_mark", 1769 "0x%08x", 1770 lambda x: False, 1771 True, 1772 ), 1773 ( 1774 "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", 1775 None, 1776 None, 1777 False, 1778 False, 1779 ), 1780 ( 1781 "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", 1782 None, 1783 None, 1784 False, 1785 False, 1786 ), 1787 ( 1788 "OVS_KEY_ATTR_IN_PORT", 1789 "in_port", 1790 "%d", 1791 lambda x: True, 1792 True, 1793 ), 1794 ("OVS_KEY_ATTR_ETHERNET", None, None, False, False), 1795 ( 1796 "OVS_KEY_ATTR_ETHERTYPE", 1797 "eth_type", 1798 "0x%04x", 1799 lambda x: int(x) == 0xFFFF, 1800 True, 1801 ), 1802 ("OVS_KEY_ATTR_IPV4", None, None, False, False), 1803 ("OVS_KEY_ATTR_IPV6", None, None, False, False), 1804 ("OVS_KEY_ATTR_ARP", None, None, False, False), 1805 ("OVS_KEY_ATTR_TCP", None, None, False, False), 1806 ( 1807 "OVS_KEY_ATTR_TCP_FLAGS", 1808 "tcp_flags", 1809 "0x%04x", 1810 lambda x: False, 1811 True, 1812 ), 1813 ("OVS_KEY_ATTR_UDP", None, None, False, False), 1814 ("OVS_KEY_ATTR_SCTP", None, None, False, False), 1815 ("OVS_KEY_ATTR_ICMP", None, None, False, False), 1816 ("OVS_KEY_ATTR_ICMPV6", None, None, False, False), 1817 ("OVS_KEY_ATTR_ND", None, None, False, False), 1818 ): 1819 v = self.get_attr(field[0]) 1820 if v is not None: 1821 m = None if mask is None else mask.get_attr(field[0]) 1822 if field[4] is False: 1823 print_str += v.dpstr(m, more) 1824 print_str += "," 1825 else: 1826 if m is None or field[3](m): 1827 print_str += field[1] + "(" 1828 print_str += field[2] % v 1829 print_str += ")," 1830 elif more or m != 0: 1831 print_str += field[1] + "(" 1832 print_str += (field[2] % v) + "/" + (field[2] % m) 1833 print_str += ")," 1834 1835 return print_str 1836 1837 1838class OvsPacket(GenericNetlinkSocket): 1839 OVS_PACKET_CMD_MISS = 1 # Flow table miss 1840 OVS_PACKET_CMD_ACTION = 2 # USERSPACE action 1841 OVS_PACKET_CMD_EXECUTE = 3 # Apply actions to packet 1842 1843 class ovs_packet_msg(ovs_dp_msg): 1844 nla_map = ( 1845 ("OVS_PACKET_ATTR_UNSPEC", "none"), 1846 ("OVS_PACKET_ATTR_PACKET", "array(uint8)"), 1847 ("OVS_PACKET_ATTR_KEY", "ovskey"), 1848 ("OVS_PACKET_ATTR_ACTIONS", "ovsactions"), 1849 ("OVS_PACKET_ATTR_USERDATA", "none"), 1850 ("OVS_PACKET_ATTR_EGRESS_TUN_KEY", "none"), 1851 ("OVS_PACKET_ATTR_UNUSED1", "none"), 1852 ("OVS_PACKET_ATTR_UNUSED2", "none"), 1853 ("OVS_PACKET_ATTR_PROBE", "none"), 1854 ("OVS_PACKET_ATTR_MRU", "uint16"), 1855 ("OVS_PACKET_ATTR_LEN", "uint32"), 1856 ("OVS_PACKET_ATTR_HASH", "uint64"), 1857 ) 1858 1859 def __init__(self): 1860 GenericNetlinkSocket.__init__(self) 1861 self.bind(OVS_PACKET_FAMILY, OvsPacket.ovs_packet_msg) 1862 1863 def upcall_handler(self, up=None): 1864 print("listening on upcall packet handler:", self.epid) 1865 while True: 1866 try: 1867 msgs = self.get() 1868 for msg in msgs: 1869 if not up: 1870 continue 1871 if msg["cmd"] == OvsPacket.OVS_PACKET_CMD_MISS: 1872 up.miss(msg) 1873 elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_ACTION: 1874 up.action(msg) 1875 elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_EXECUTE: 1876 up.execute(msg) 1877 else: 1878 print("Unkonwn cmd: %d" % msg["cmd"]) 1879 except NetlinkError as ne: 1880 raise ne 1881 1882 1883class OvsDatapath(GenericNetlinkSocket): 1884 OVS_DP_F_VPORT_PIDS = 1 << 1 1885 OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3 1886 1887 class dp_cmd_msg(ovs_dp_msg): 1888 """ 1889 Message class that will be used to communicate with the kernel module. 1890 """ 1891 1892 nla_map = ( 1893 ("OVS_DP_ATTR_UNSPEC", "none"), 1894 ("OVS_DP_ATTR_NAME", "asciiz"), 1895 ("OVS_DP_ATTR_UPCALL_PID", "array(uint32)"), 1896 ("OVS_DP_ATTR_STATS", "dpstats"), 1897 ("OVS_DP_ATTR_MEGAFLOW_STATS", "megaflowstats"), 1898 ("OVS_DP_ATTR_USER_FEATURES", "uint32"), 1899 ("OVS_DP_ATTR_PAD", "none"), 1900 ("OVS_DP_ATTR_MASKS_CACHE_SIZE", "uint32"), 1901 ("OVS_DP_ATTR_PER_CPU_PIDS", "array(uint32)"), 1902 ) 1903 1904 class dpstats(nla): 1905 fields = ( 1906 ("hit", "=Q"), 1907 ("missed", "=Q"), 1908 ("lost", "=Q"), 1909 ("flows", "=Q"), 1910 ) 1911 1912 class megaflowstats(nla): 1913 fields = ( 1914 ("mask_hit", "=Q"), 1915 ("masks", "=I"), 1916 ("padding", "=I"), 1917 ("cache_hits", "=Q"), 1918 ("pad1", "=Q"), 1919 ) 1920 1921 def __init__(self): 1922 GenericNetlinkSocket.__init__(self) 1923 self.bind(OVS_DATAPATH_FAMILY, OvsDatapath.dp_cmd_msg) 1924 1925 def info(self, dpname, ifindex=0): 1926 msg = OvsDatapath.dp_cmd_msg() 1927 msg["cmd"] = OVS_DP_CMD_GET 1928 msg["version"] = OVS_DATAPATH_VERSION 1929 msg["reserved"] = 0 1930 msg["dpifindex"] = ifindex 1931 msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname]) 1932 1933 try: 1934 reply = self.nlm_request( 1935 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST 1936 ) 1937 reply = reply[0] 1938 except NetlinkError as ne: 1939 if ne.code == errno.ENODEV: 1940 reply = None 1941 else: 1942 raise ne 1943 1944 return reply 1945 1946 def create( 1947 self, dpname, shouldUpcall=False, versionStr=None, p=OvsPacket() 1948 ): 1949 msg = OvsDatapath.dp_cmd_msg() 1950 msg["cmd"] = OVS_DP_CMD_NEW 1951 if versionStr is None: 1952 msg["version"] = OVS_DATAPATH_VERSION 1953 else: 1954 msg["version"] = int(versionStr.split(":")[0], 0) 1955 msg["reserved"] = 0 1956 msg["dpifindex"] = 0 1957 msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname]) 1958 1959 dpfeatures = 0 1960 if versionStr is not None and versionStr.find(":") != -1: 1961 dpfeatures = int(versionStr.split(":")[1], 0) 1962 else: 1963 if versionStr is None or versionStr.find(":") == -1: 1964 dpfeatures |= OvsDatapath.OVS_DP_F_DISPATCH_UPCALL_PER_CPU 1965 dpfeatures &= ~OvsDatapath.OVS_DP_F_VPORT_PIDS 1966 1967 nproc = multiprocessing.cpu_count() 1968 procarray = [] 1969 for i in range(1, nproc): 1970 procarray += [int(p.epid)] 1971 msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", procarray]) 1972 msg["attrs"].append(["OVS_DP_ATTR_USER_FEATURES", dpfeatures]) 1973 if not shouldUpcall: 1974 msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", [0]]) 1975 1976 try: 1977 reply = self.nlm_request( 1978 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 1979 ) 1980 reply = reply[0] 1981 except NetlinkError as ne: 1982 if ne.code == errno.EEXIST: 1983 reply = None 1984 else: 1985 raise ne 1986 1987 return reply 1988 1989 def destroy(self, dpname): 1990 msg = OvsDatapath.dp_cmd_msg() 1991 msg["cmd"] = OVS_DP_CMD_DEL 1992 msg["version"] = OVS_DATAPATH_VERSION 1993 msg["reserved"] = 0 1994 msg["dpifindex"] = 0 1995 msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname]) 1996 1997 try: 1998 reply = self.nlm_request( 1999 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2000 ) 2001 reply = reply[0] 2002 except NetlinkError as ne: 2003 if ne.code == errno.ENODEV: 2004 reply = None 2005 else: 2006 raise ne 2007 2008 return reply 2009 2010 2011class OvsVport(GenericNetlinkSocket): 2012 OVS_VPORT_TYPE_NETDEV = 1 2013 OVS_VPORT_TYPE_INTERNAL = 2 2014 OVS_VPORT_TYPE_GRE = 3 2015 OVS_VPORT_TYPE_VXLAN = 4 2016 OVS_VPORT_TYPE_GENEVE = 5 2017 2018 class ovs_vport_msg(ovs_dp_msg): 2019 nla_map = ( 2020 ("OVS_VPORT_ATTR_UNSPEC", "none"), 2021 ("OVS_VPORT_ATTR_PORT_NO", "uint32"), 2022 ("OVS_VPORT_ATTR_TYPE", "uint32"), 2023 ("OVS_VPORT_ATTR_NAME", "asciiz"), 2024 ("OVS_VPORT_ATTR_OPTIONS", "vportopts"), 2025 ("OVS_VPORT_ATTR_UPCALL_PID", "array(uint32)"), 2026 ("OVS_VPORT_ATTR_STATS", "vportstats"), 2027 ("OVS_VPORT_ATTR_PAD", "none"), 2028 ("OVS_VPORT_ATTR_IFINDEX", "uint32"), 2029 ("OVS_VPORT_ATTR_NETNSID", "uint32"), 2030 ) 2031 2032 class vportopts(nla): 2033 nla_map = ( 2034 ("OVS_TUNNEL_ATTR_UNSPEC", "none"), 2035 ("OVS_TUNNEL_ATTR_DST_PORT", "uint16"), 2036 ("OVS_TUNNEL_ATTR_EXTENSION", "none"), 2037 ) 2038 2039 class vportstats(nla): 2040 fields = ( 2041 ("rx_packets", "=Q"), 2042 ("tx_packets", "=Q"), 2043 ("rx_bytes", "=Q"), 2044 ("tx_bytes", "=Q"), 2045 ("rx_errors", "=Q"), 2046 ("tx_errors", "=Q"), 2047 ("rx_dropped", "=Q"), 2048 ("tx_dropped", "=Q"), 2049 ) 2050 2051 def type_to_str(vport_type): 2052 if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV: 2053 return "netdev" 2054 elif vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL: 2055 return "internal" 2056 elif vport_type == OvsVport.OVS_VPORT_TYPE_GRE: 2057 return "gre" 2058 elif vport_type == OvsVport.OVS_VPORT_TYPE_VXLAN: 2059 return "vxlan" 2060 elif vport_type == OvsVport.OVS_VPORT_TYPE_GENEVE: 2061 return "geneve" 2062 raise ValueError("Unknown vport type:%d" % vport_type) 2063 2064 def str_to_type(vport_type): 2065 if vport_type == "netdev": 2066 return OvsVport.OVS_VPORT_TYPE_NETDEV 2067 elif vport_type == "internal": 2068 return OvsVport.OVS_VPORT_TYPE_INTERNAL 2069 elif vport_type == "gre": 2070 return OvsVport.OVS_VPORT_TYPE_INTERNAL 2071 elif vport_type == "vxlan": 2072 return OvsVport.OVS_VPORT_TYPE_VXLAN 2073 elif vport_type == "geneve": 2074 return OvsVport.OVS_VPORT_TYPE_GENEVE 2075 raise ValueError("Unknown vport type: '%s'" % vport_type) 2076 2077 def __init__(self, packet=OvsPacket()): 2078 GenericNetlinkSocket.__init__(self) 2079 self.bind(OVS_VPORT_FAMILY, OvsVport.ovs_vport_msg) 2080 self.upcall_packet = packet 2081 2082 def info(self, vport_name, dpifindex=0, portno=None): 2083 msg = OvsVport.ovs_vport_msg() 2084 2085 msg["cmd"] = OVS_VPORT_CMD_GET 2086 msg["version"] = OVS_DATAPATH_VERSION 2087 msg["reserved"] = 0 2088 msg["dpifindex"] = dpifindex 2089 2090 if portno is None: 2091 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_name]) 2092 else: 2093 msg["attrs"].append(["OVS_VPORT_ATTR_PORT_NO", portno]) 2094 2095 try: 2096 reply = self.nlm_request( 2097 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST 2098 ) 2099 reply = reply[0] 2100 except NetlinkError as ne: 2101 if ne.code == errno.ENODEV: 2102 reply = None 2103 else: 2104 raise ne 2105 return reply 2106 2107 def attach(self, dpindex, vport_ifname, ptype, dport, lwt): 2108 msg = OvsVport.ovs_vport_msg() 2109 2110 msg["cmd"] = OVS_VPORT_CMD_NEW 2111 msg["version"] = OVS_DATAPATH_VERSION 2112 msg["reserved"] = 0 2113 msg["dpifindex"] = dpindex 2114 port_type = OvsVport.str_to_type(ptype) 2115 2116 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname]) 2117 msg["attrs"].append( 2118 ["OVS_VPORT_ATTR_UPCALL_PID", [self.upcall_packet.epid]] 2119 ) 2120 2121 TUNNEL_DEFAULTS = [("geneve", 6081), 2122 ("vxlan", 4789)] 2123 2124 for tnl in TUNNEL_DEFAULTS: 2125 if ptype == tnl[0]: 2126 if not dport: 2127 dport = tnl[1] 2128 2129 if not lwt: 2130 vportopt = OvsVport.ovs_vport_msg.vportopts() 2131 vportopt["attrs"].append( 2132 ["OVS_TUNNEL_ATTR_DST_PORT", socket.htons(dport)] 2133 ) 2134 msg["attrs"].append( 2135 ["OVS_VPORT_ATTR_OPTIONS", vportopt] 2136 ) 2137 else: 2138 port_type = OvsVport.OVS_VPORT_TYPE_NETDEV 2139 ipr = pyroute2.iproute.IPRoute() 2140 2141 if tnl[0] == "geneve": 2142 ipr.link("add", ifname=vport_ifname, kind=tnl[0], 2143 geneve_port=dport, 2144 geneve_collect_metadata=True, 2145 geneve_udp_zero_csum6_rx=1) 2146 elif tnl[0] == "vxlan": 2147 ipr.link("add", ifname=vport_ifname, kind=tnl[0], 2148 vxlan_learning=0, vxlan_collect_metadata=1, 2149 vxlan_udp_zero_csum6_rx=1, vxlan_port=dport) 2150 break 2151 msg["attrs"].append(["OVS_VPORT_ATTR_TYPE", port_type]) 2152 2153 try: 2154 reply = self.nlm_request( 2155 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2156 ) 2157 reply = reply[0] 2158 except NetlinkError as ne: 2159 if ne.code == errno.EEXIST: 2160 reply = None 2161 else: 2162 raise ne 2163 return reply 2164 2165 def reset_upcall(self, dpindex, vport_ifname, p=None): 2166 msg = OvsVport.ovs_vport_msg() 2167 2168 msg["cmd"] = OVS_VPORT_CMD_SET 2169 msg["version"] = OVS_DATAPATH_VERSION 2170 msg["reserved"] = 0 2171 msg["dpifindex"] = dpindex 2172 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname]) 2173 2174 if p == None: 2175 p = self.upcall_packet 2176 else: 2177 self.upcall_packet = p 2178 2179 msg["attrs"].append(["OVS_VPORT_ATTR_UPCALL_PID", [p.epid]]) 2180 2181 try: 2182 reply = self.nlm_request( 2183 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2184 ) 2185 reply = reply[0] 2186 except NetlinkError as ne: 2187 raise ne 2188 return reply 2189 2190 def detach(self, dpindex, vport_ifname): 2191 msg = OvsVport.ovs_vport_msg() 2192 2193 msg["cmd"] = OVS_VPORT_CMD_DEL 2194 msg["version"] = OVS_DATAPATH_VERSION 2195 msg["reserved"] = 0 2196 msg["dpifindex"] = dpindex 2197 msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname]) 2198 2199 try: 2200 reply = self.nlm_request( 2201 msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK 2202 ) 2203 reply = reply[0] 2204 except NetlinkError as ne: 2205 if ne.code == errno.ENODEV: 2206 reply = None 2207 else: 2208 raise ne 2209 return reply 2210 2211 def upcall_handler(self, handler=None): 2212 self.upcall_packet.upcall_handler(handler) 2213 2214 2215class OvsFlow(GenericNetlinkSocket): 2216 class ovs_flow_msg(ovs_dp_msg): 2217 nla_map = ( 2218 ("OVS_FLOW_ATTR_UNSPEC", "none"), 2219 ("OVS_FLOW_ATTR_KEY", "ovskey"), 2220 ("OVS_FLOW_ATTR_ACTIONS", "ovsactions"), 2221 ("OVS_FLOW_ATTR_STATS", "flowstats"), 2222 ("OVS_FLOW_ATTR_TCP_FLAGS", "uint8"), 2223 ("OVS_FLOW_ATTR_USED", "uint64"), 2224 ("OVS_FLOW_ATTR_CLEAR", "none"), 2225 ("OVS_FLOW_ATTR_MASK", "ovskey"), 2226 ("OVS_FLOW_ATTR_PROBE", "none"), 2227 ("OVS_FLOW_ATTR_UFID", "array(uint32)"), 2228 ("OVS_FLOW_ATTR_UFID_FLAGS", "uint32"), 2229 ) 2230 2231 class flowstats(nla): 2232 fields = ( 2233 ("packets", "=Q"), 2234 ("bytes", "=Q"), 2235 ) 2236 2237 def dpstr(self, more=False): 2238 ufid = self.get_attr("OVS_FLOW_ATTR_UFID") 2239 ufid_str = "" 2240 if ufid is not None: 2241 ufid_str = ( 2242 "ufid:{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format( 2243 ufid[0], 2244 ufid[1] >> 16, 2245 ufid[1] & 0xFFFF, 2246 ufid[2] >> 16, 2247 ufid[2] & 0, 2248 ufid[3], 2249 ) 2250 ) 2251 2252 key_field = self.get_attr("OVS_FLOW_ATTR_KEY") 2253 keymsg = None 2254 if key_field is not None: 2255 keymsg = key_field 2256 2257 mask_field = self.get_attr("OVS_FLOW_ATTR_MASK") 2258 maskmsg = None 2259 if mask_field is not None: 2260 maskmsg = mask_field 2261 2262 acts_field = self.get_attr("OVS_FLOW_ATTR_ACTIONS") 2263 actsmsg = None 2264 if acts_field is not None: 2265 actsmsg = acts_field 2266 2267 print_str = "" 2268 2269 if more: 2270 print_str += ufid_str + "," 2271 2272 if keymsg is not None: 2273 print_str += keymsg.dpstr(maskmsg, more) 2274 2275 stats = self.get_attr("OVS_FLOW_ATTR_STATS") 2276 if stats is None: 2277 print_str += " packets:0, bytes:0," 2278 else: 2279 print_str += " packets:%d, bytes:%d," % ( 2280 stats["packets"], 2281 stats["bytes"], 2282 ) 2283 2284 used = self.get_attr("OVS_FLOW_ATTR_USED") 2285 print_str += " used:" 2286 if used is None: 2287 print_str += "never," 2288 else: 2289 used_time = int(used) 2290 cur_time_sec = time.clock_gettime(time.CLOCK_MONOTONIC) 2291 used_time = (cur_time_sec * 1000) - used_time 2292 print_str += "{}s,".format(used_time / 1000) 2293 2294 print_str += " actions:" 2295 if ( 2296 actsmsg is None 2297 or "attrs" not in actsmsg 2298 or len(actsmsg["attrs"]) == 0 2299 ): 2300 print_str += "drop" 2301 else: 2302 print_str += actsmsg.dpstr(more) 2303 2304 return print_str 2305 2306 def parse(self, flowstr, actstr, dpidx=0): 2307 OVS_UFID_F_OMIT_KEY = 1 << 0 2308 OVS_UFID_F_OMIT_MASK = 1 << 1 2309 OVS_UFID_F_OMIT_ACTIONS = 1 << 2 2310 2311 self["cmd"] = 0 2312 self["version"] = 0 2313 self["reserved"] = 0 2314 self["dpifindex"] = 0 2315 2316 if flowstr.startswith("ufid:"): 2317 count = 5 2318 while flowstr[count] != ",": 2319 count += 1 2320 ufidstr = flowstr[5:count] 2321 flowstr = flowstr[count + 1 :] 2322 else: 2323 ufidstr = str(uuid.uuid4()) 2324 uuidRawObj = uuid.UUID(ufidstr).fields 2325 2326 self["attrs"].append( 2327 [ 2328 "OVS_FLOW_ATTR_UFID", 2329 [ 2330 uuidRawObj[0], 2331 uuidRawObj[1] << 16 | uuidRawObj[2], 2332 uuidRawObj[3] << 24 2333 | uuidRawObj[4] << 16 2334 | uuidRawObj[5] & (0xFF << 32) >> 32, 2335 uuidRawObj[5] & (0xFFFFFFFF), 2336 ], 2337 ] 2338 ) 2339 self["attrs"].append( 2340 [ 2341 "OVS_FLOW_ATTR_UFID_FLAGS", 2342 int( 2343 OVS_UFID_F_OMIT_KEY 2344 | OVS_UFID_F_OMIT_MASK 2345 | OVS_UFID_F_OMIT_ACTIONS 2346 ), 2347 ] 2348 ) 2349 2350 k = ovskey() 2351 m = ovskey() 2352 k.parse(flowstr, m) 2353 self["attrs"].append(["OVS_FLOW_ATTR_KEY", k]) 2354 self["attrs"].append(["OVS_FLOW_ATTR_MASK", m]) 2355 2356 a = ovsactions() 2357 a.parse(actstr) 2358 self["attrs"].append(["OVS_FLOW_ATTR_ACTIONS", a]) 2359 2360 def __init__(self): 2361 GenericNetlinkSocket.__init__(self) 2362 2363 self.bind(OVS_FLOW_FAMILY, OvsFlow.ovs_flow_msg) 2364 2365 def add_flow(self, dpifindex, flowmsg): 2366 """ 2367 Send a new flow message to the kernel. 2368 2369 dpifindex should be a valid datapath obtained by calling 2370 into the OvsDatapath lookup 2371 2372 flowmsg is a flow object obtained by calling a dpparse 2373 """ 2374 2375 flowmsg["cmd"] = OVS_FLOW_CMD_NEW 2376 flowmsg["version"] = OVS_DATAPATH_VERSION 2377 flowmsg["reserved"] = 0 2378 flowmsg["dpifindex"] = dpifindex 2379 2380 try: 2381 reply = self.nlm_request( 2382 flowmsg, 2383 msg_type=self.prid, 2384 msg_flags=NLM_F_REQUEST | NLM_F_ACK, 2385 ) 2386 reply = reply[0] 2387 except NetlinkError as ne: 2388 print(flowmsg) 2389 raise ne 2390 return reply 2391 2392 def del_flows(self, dpifindex): 2393 """ 2394 Send a del message to the kernel that will drop all flows. 2395 2396 dpifindex should be a valid datapath obtained by calling 2397 into the OvsDatapath lookup 2398 """ 2399 2400 flowmsg = OvsFlow.ovs_flow_msg() 2401 flowmsg["cmd"] = OVS_FLOW_CMD_DEL 2402 flowmsg["version"] = OVS_DATAPATH_VERSION 2403 flowmsg["reserved"] = 0 2404 flowmsg["dpifindex"] = dpifindex 2405 2406 try: 2407 reply = self.nlm_request( 2408 flowmsg, 2409 msg_type=self.prid, 2410 msg_flags=NLM_F_REQUEST | NLM_F_ACK, 2411 ) 2412 reply = reply[0] 2413 except NetlinkError as ne: 2414 print(flowmsg) 2415 raise ne 2416 return reply 2417 2418 def dump(self, dpifindex, flowspec=None): 2419 """ 2420 Returns a list of messages containing flows. 2421 2422 dpifindex should be a valid datapath obtained by calling 2423 into the OvsDatapath lookup 2424 2425 flowpsec is a string which represents a flow in the dpctl 2426 format. 2427 """ 2428 msg = OvsFlow.ovs_flow_msg() 2429 2430 msg["cmd"] = OVS_FLOW_CMD_GET 2431 msg["version"] = OVS_DATAPATH_VERSION 2432 msg["reserved"] = 0 2433 msg["dpifindex"] = dpifindex 2434 2435 msg_flags = NLM_F_REQUEST | NLM_F_ACK 2436 if flowspec is None: 2437 msg_flags |= NLM_F_DUMP 2438 rep = None 2439 2440 try: 2441 rep = self.nlm_request( 2442 msg, 2443 msg_type=self.prid, 2444 msg_flags=msg_flags, 2445 ) 2446 except NetlinkError as ne: 2447 raise ne 2448 return rep 2449 2450 def miss(self, packetmsg): 2451 seq = packetmsg["header"]["sequence_number"] 2452 keystr = "(none)" 2453 key_field = packetmsg.get_attr("OVS_PACKET_ATTR_KEY") 2454 if key_field is not None: 2455 keystr = key_field.dpstr(None, True) 2456 2457 pktdata = packetmsg.get_attr("OVS_PACKET_ATTR_PACKET") 2458 pktpres = "yes" if pktdata is not None else "no" 2459 2460 print("MISS upcall[%d/%s]: %s" % (seq, pktpres, keystr), flush=True) 2461 2462 def execute(self, packetmsg): 2463 print("userspace execute command") 2464 2465 def action(self, packetmsg): 2466 print("userspace action command") 2467 2468 2469def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()): 2470 dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME") 2471 base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS") 2472 megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS") 2473 user_features = dp_lookup_rep.get_attr("OVS_DP_ATTR_USER_FEATURES") 2474 masks_cache_size = dp_lookup_rep.get_attr("OVS_DP_ATTR_MASKS_CACHE_SIZE") 2475 2476 print("%s:" % dp_name) 2477 print( 2478 " lookups: hit:%d missed:%d lost:%d" 2479 % (base_stats["hit"], base_stats["missed"], base_stats["lost"]) 2480 ) 2481 print(" flows:%d" % base_stats["flows"]) 2482 pkts = base_stats["hit"] + base_stats["missed"] 2483 avg = (megaflow_stats["mask_hit"] / pkts) if pkts != 0 else 0.0 2484 print( 2485 " masks: hit:%d total:%d hit/pkt:%f" 2486 % (megaflow_stats["mask_hit"], megaflow_stats["masks"], avg) 2487 ) 2488 print(" caches:") 2489 print(" masks-cache: size:%d" % masks_cache_size) 2490 2491 if user_features is not None: 2492 print(" features: 0x%X" % user_features) 2493 2494 # port print out 2495 for iface in ndb.interfaces: 2496 rep = vpl.info(iface.ifname, ifindex) 2497 if rep is not None: 2498 opts = "" 2499 vpo = rep.get_attr("OVS_VPORT_ATTR_OPTIONS") 2500 if vpo: 2501 dpo = vpo.get_attr("OVS_TUNNEL_ATTR_DST_PORT") 2502 if dpo: 2503 opts += " tnl-dport:%s" % socket.ntohs(dpo) 2504 print( 2505 " port %d: %s (%s%s)" 2506 % ( 2507 rep.get_attr("OVS_VPORT_ATTR_PORT_NO"), 2508 rep.get_attr("OVS_VPORT_ATTR_NAME"), 2509 OvsVport.type_to_str(rep.get_attr("OVS_VPORT_ATTR_TYPE")), 2510 opts, 2511 ) 2512 ) 2513 2514 2515def main(argv): 2516 nlmsg_atoms.ovskey = ovskey 2517 nlmsg_atoms.ovsactions = ovsactions 2518 2519 # version check for pyroute2 2520 prverscheck = pyroute2.__version__.split(".") 2521 if int(prverscheck[0]) == 0 and int(prverscheck[1]) < 6: 2522 print("Need to upgrade the python pyroute2 package to >= 0.6.") 2523 sys.exit(0) 2524 2525 parser = argparse.ArgumentParser() 2526 parser.add_argument( 2527 "-v", 2528 "--verbose", 2529 action="count", 2530 help="Increment 'verbose' output counter.", 2531 default=0, 2532 ) 2533 subparsers = parser.add_subparsers() 2534 2535 showdpcmd = subparsers.add_parser("show") 2536 showdpcmd.add_argument( 2537 "showdp", metavar="N", type=str, nargs="?", help="Datapath Name" 2538 ) 2539 2540 adddpcmd = subparsers.add_parser("add-dp") 2541 adddpcmd.add_argument("adddp", help="Datapath Name") 2542 adddpcmd.add_argument( 2543 "-u", 2544 "--upcall", 2545 action="store_true", 2546 help="Leave open a reader for upcalls", 2547 ) 2548 adddpcmd.add_argument( 2549 "-V", 2550 "--versioning", 2551 required=False, 2552 help="Specify a custom version / feature string", 2553 ) 2554 2555 deldpcmd = subparsers.add_parser("del-dp") 2556 deldpcmd.add_argument("deldp", help="Datapath Name") 2557 2558 addifcmd = subparsers.add_parser("add-if") 2559 addifcmd.add_argument("dpname", help="Datapath Name") 2560 addifcmd.add_argument("addif", help="Interface name for adding") 2561 addifcmd.add_argument( 2562 "-u", 2563 "--upcall", 2564 action="store_true", 2565 help="Leave open a reader for upcalls", 2566 ) 2567 addifcmd.add_argument( 2568 "-t", 2569 "--ptype", 2570 type=str, 2571 default="netdev", 2572 choices=["netdev", "internal", "geneve", "vxlan"], 2573 help="Interface type (default netdev)", 2574 ) 2575 addifcmd.add_argument( 2576 "-p", 2577 "--dport", 2578 type=int, 2579 default=0, 2580 help="Destination port (0 for default)" 2581 ) 2582 addifcmd.add_argument( 2583 "-l", 2584 "--lwt", 2585 type=bool, 2586 default=True, 2587 help="Use LWT infrastructure instead of vport (default true)." 2588 ) 2589 delifcmd = subparsers.add_parser("del-if") 2590 delifcmd.add_argument("dpname", help="Datapath Name") 2591 delifcmd.add_argument("delif", help="Interface name for adding") 2592 delifcmd.add_argument("-d", 2593 "--dellink", 2594 type=bool, default=False, 2595 help="Delete the link as well.") 2596 2597 dumpflcmd = subparsers.add_parser("dump-flows") 2598 dumpflcmd.add_argument("dumpdp", help="Datapath Name") 2599 2600 addflcmd = subparsers.add_parser("add-flow") 2601 addflcmd.add_argument("flbr", help="Datapath name") 2602 addflcmd.add_argument("flow", help="Flow specification") 2603 addflcmd.add_argument("acts", help="Flow actions") 2604 2605 delfscmd = subparsers.add_parser("del-flows") 2606 delfscmd.add_argument("flsbr", help="Datapath name") 2607 2608 args = parser.parse_args() 2609 2610 if args.verbose > 0: 2611 if args.verbose > 1: 2612 logging.basicConfig(level=logging.DEBUG) 2613 2614 ovspk = OvsPacket() 2615 ovsdp = OvsDatapath() 2616 ovsvp = OvsVport(ovspk) 2617 ovsflow = OvsFlow() 2618 ndb = NDB() 2619 2620 sys.setrecursionlimit(100000) 2621 2622 if hasattr(args, "showdp"): 2623 found = False 2624 for iface in ndb.interfaces: 2625 rep = None 2626 if args.showdp is None: 2627 rep = ovsdp.info(iface.ifname, 0) 2628 elif args.showdp == iface.ifname: 2629 rep = ovsdp.info(iface.ifname, 0) 2630 2631 if rep is not None: 2632 found = True 2633 print_ovsdp_full(rep, iface.index, ndb, ovsvp) 2634 2635 if not found: 2636 msg = "No DP found" 2637 if args.showdp is not None: 2638 msg += ":'%s'" % args.showdp 2639 print(msg) 2640 elif hasattr(args, "adddp"): 2641 rep = ovsdp.create(args.adddp, args.upcall, args.versioning, ovspk) 2642 if rep is None: 2643 print("DP '%s' already exists" % args.adddp) 2644 else: 2645 print("DP '%s' added" % args.adddp) 2646 if args.upcall: 2647 ovspk.upcall_handler(ovsflow) 2648 elif hasattr(args, "deldp"): 2649 ovsdp.destroy(args.deldp) 2650 elif hasattr(args, "addif"): 2651 rep = ovsdp.info(args.dpname, 0) 2652 if rep is None: 2653 print("DP '%s' not found." % args.dpname) 2654 return 1 2655 dpindex = rep["dpifindex"] 2656 rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype, 2657 args.dport, args.lwt) 2658 msg = "vport '%s'" % args.addif 2659 if rep and rep["header"]["error"] is None: 2660 msg += " added." 2661 else: 2662 msg += " failed to add." 2663 if args.upcall: 2664 if rep is None: 2665 rep = ovsvp.reset_upcall(dpindex, args.addif, ovspk) 2666 ovsvp.upcall_handler(ovsflow) 2667 elif hasattr(args, "delif"): 2668 rep = ovsdp.info(args.dpname, 0) 2669 if rep is None: 2670 print("DP '%s' not found." % args.dpname) 2671 return 1 2672 rep = ovsvp.detach(rep["dpifindex"], args.delif) 2673 msg = "vport '%s'" % args.delif 2674 if rep and rep["header"]["error"] is None: 2675 msg += " removed." 2676 else: 2677 msg += " failed to remove." 2678 if args.dellink: 2679 ipr = pyroute2.iproute.IPRoute() 2680 ipr.link("del", index=ipr.link_lookup(ifname=args.delif)[0]) 2681 elif hasattr(args, "dumpdp"): 2682 rep = ovsdp.info(args.dumpdp, 0) 2683 if rep is None: 2684 print("DP '%s' not found." % args.dumpdp) 2685 return 1 2686 rep = ovsflow.dump(rep["dpifindex"]) 2687 for flow in rep: 2688 print(flow.dpstr(True if args.verbose > 0 else False)) 2689 elif hasattr(args, "flbr"): 2690 rep = ovsdp.info(args.flbr, 0) 2691 if rep is None: 2692 print("DP '%s' not found." % args.flbr) 2693 return 1 2694 flow = OvsFlow.ovs_flow_msg() 2695 flow.parse(args.flow, args.acts, rep["dpifindex"]) 2696 ovsflow.add_flow(rep["dpifindex"], flow) 2697 elif hasattr(args, "flsbr"): 2698 rep = ovsdp.info(args.flsbr, 0) 2699 if rep is None: 2700 print("DP '%s' not found." % args.flsbr) 2701 ovsflow.del_flows(rep["dpifindex"]) 2702 2703 return 0 2704 2705 2706if __name__ == "__main__": 2707 sys.exit(main(sys.argv)) 2708