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