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