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