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