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