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