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