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