~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/net/openvswitch/ovs-dpctl.py

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php