1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3 4# Controls the openvswitch module. Part of the kselftest suite, but 5# can be used for some diagnostic purpose as well. 6 7import argparse 8import errno 9import ipaddress 10import logging 11import math 12import multiprocessing 13import re 14import socket 15import struct 16import sys 17import time 18import types 19import uuid 20 21try: 22 from pyroute2 import NDB 23 24 from pyroute2.netlink import NLA_F_NESTED 25 from pyroute2.netlink import NLM_F_ACK 26 from pyroute2.netlink import NLM_F_DUMP 27 from pyroute2.netlink import NLM_F_REQUEST 28 from pyroute2.netlink import genlmsg 29 from pyroute2.netlink import nla 30 from pyroute2.netlink import nlmsg_atoms 31 from pyroute2.netlink.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 38except ModuleNotFoundError: 39 print("Need to install the python pyroute2 package >= 0.6.") 40 sys.exit(1) 41 42 43OVS_DATAPATH_FAMILY = "ovs_datapath" 44OVS_VPORT_FAMILY = "ovs_vport" 45OVS_FLOW_FAMILY = "ovs_flow" 46OVS_PACKET_FAMILY = "ovs_packet" 47OVS_METER_FAMILY = "ovs_meter" 48OVS_CT_LIMIT_FAMILY = "ovs_ct_limit" 49 50OVS_DATAPATH_VERSION = 2 51OVS_DP_CMD_NEW = 1 52OVS_DP_CMD_DEL = 2 53OVS_DP_CMD_GET = 3 54OVS_DP_CMD_SET = 4 55 56OVS_VPORT_CMD_NEW = 1 57OVS_VPORT_CMD_DEL = 2 58OVS_VPORT_CMD_GET = 3 59OVS_VPORT_CMD_SET = 4 60 61OVS_FLOW_CMD_NEW = 1 62OVS_FLOW_CMD_DEL = 2 63OVS_FLOW_CMD_GET = 3 64OVS_FLOW_CMD_SET = 4 65 66UINT32_MAX = 0xFFFFFFFF 67 68def macstr(mac): 69 outstr = ":".join(["%02X" % i for i in mac]) 70 return outstr 71 72 73def 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 82def 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 91def 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 111def 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 165def 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 180def 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 195def 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 207def 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 219def 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 232def 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 256def 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 288def 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 357class 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 365class 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 900class 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 1840class 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 1885class 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 2013class 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 2217class 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 2471class 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 2508class 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 2517class 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 2532def 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 2578def 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 2774if __name__ == "__main__": 2775 sys.exit(main(sys.argv)) 2776