2727 from pyroute2 .netlink import genlmsg
2828 from pyroute2 .netlink import nla
2929 from pyroute2 .netlink import nlmsg_atoms
30- from pyroute2 .netlink .exceptions import NetlinkError
30+ from pyroute2 .netlink .event import EventSocket
3131 from pyroute2 .netlink .generic import GenericNetlinkSocket
32+ from pyroute2 .netlink .nlsocket import Marshal
33+ from pyroute2 .netlink .exceptions import NetlinkError
3234 import pyroute2
3335
3436except ModuleNotFoundError :
@@ -269,6 +271,47 @@ def parse_extract_field(
269271 return str_skipped , data
270272
271273
274+ def parse_attributes (actstr , attributes ):
275+ """Parses actstr according to attribute description. attributes must be
276+ a list of tuples (name, attribute, parse_func), e.g:
277+ ("pid", OVS_USERSPACE_ATTR_PID, int)
278+
279+ Returns a list of parsed attributes followed by the remaining string.
280+ """
281+ attrs = []
282+ for (key , attr , func ) in attributes :
283+ if not actstr .startswith (key ):
284+ continue
285+
286+ actstr = actstr [len (key ) :]
287+
288+ if not func :
289+ attrs .append ([attr ])
290+ continue
291+
292+ # The length of complex attributes cannot be determined
293+ # beforehand and must be reported by the parsing func.
294+ delim = actstr [0 ]
295+ actstr = actstr [1 :]
296+ if delim == "=" :
297+ pos = strcspn (actstr , ",)" )
298+ datum = func (actstr [:pos ])
299+ elif delim == "(" :
300+ datum , pos = func (actstr )
301+
302+ attrs .append ([attr , datum ])
303+ actstr = actstr [pos :]
304+
305+ if delim == "(" :
306+ actstr = actstr [1 :]
307+
308+ actstr = actstr [strspn (actstr , ", " ) :]
309+
310+ if actstr [0 ] != ")" :
311+ raise ValueError ("Action str: '%s' unbalanced" % actstr )
312+
313+ return attrs , actstr [1 :]
314+
272315class ovs_dp_msg (genlmsg ):
273316 # include the OVS version
274317 # We need a custom header rather than just being able to rely on
@@ -357,41 +400,19 @@ def percent_to_rate(percent):
357400 percent = float (percent .strip ('%' ))
358401 return int (math .floor (UINT32_MAX * (percent / 100.0 ) + .5 ))
359402
360- for ( key , attr , func ) in (
403+ attrs_desc = (
361404 ("sample" , "OVS_SAMPLE_ATTR_PROBABILITY" , percent_to_rate ),
362405 ("group_id" , "OVS_SAMPLE_ATTR_PSAMPLE_GROUP" , int ),
363406 ("cookie" , "OVS_SAMPLE_ATTR_PSAMPLE_COOKIE" ,
364407 lambda x : list (bytearray .fromhex (x ))),
365408 ("actions" , "OVS_SAMPLE_ATTR_ACTIONS" , parse_nested_actions ),
366- ):
367- if not actstr .startswith (key ):
368- continue
369-
370- actstr = actstr [len (key ) :]
371-
372- if not func :
373- self ["attrs" ].append ([attr , None ])
374- continue
375-
376- # The length of complex attributes cannot be determined
377- # beforehand and must be reported by the parsing func.
378- delim = actstr [0 ]
379- actstr = actstr [1 :]
380- if delim == "=" :
381- pos = strcspn (actstr , ",)" )
382- datum = func (actstr [:pos ])
383- elif delim == "(" :
384- datum , pos = func (actstr )
385-
386- self ["attrs" ].append ([attr , datum ])
387- actstr = actstr [pos :]
388- actstr = actstr [strspn (actstr , ", " ) :]
389-
390- if actstr [0 ] != ")" :
391- raise ValueError ("Action str: '%s' unbalanced" % actstr )
409+ )
392410
393- return actstr [1 :]
411+ attrs , actstr = parse_attributes (actstr , attrs_desc )
412+ for attr in attrs :
413+ self ["attrs" ].append (attr )
394414
415+ return actstr
395416
396417 class ctact (nla ):
397418 nla_flags = NLA_F_NESTED
@@ -521,6 +542,18 @@ def dpstr(self, more=False):
521542 print_str += ")"
522543 return print_str
523544
545+ def parse (self , actstr ):
546+ attrs_desc = (
547+ ("pid" , "OVS_USERSPACE_ATTR_PID" , int ),
548+ ("userdata" , "OVS_USERSPACE_ATTR_USERDATA" ,
549+ lambda x : list (bytearray .fromhex (x ))),
550+ ("egress_tun_port" , "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT" , int )
551+ )
552+ attrs , actstr = parse_attributes (actstr , attrs_desc )
553+ for attr in attrs :
554+ self ["attrs" ].append (attr )
555+ return actstr
556+
524557 def dpstr (self , more = False ):
525558 print_str = ""
526559
@@ -730,6 +763,11 @@ def parse(self, actstr):
730763 self ["attrs" ].append (["OVS_ACTION_ATTR_SAMPLE" , sampleact ])
731764 parsed = True
732765
766+ elif parse_starts_block (actstr , "userspace(" , False ):
767+ uact = self .userspace ()
768+ actstr = uact .parse (actstr [len ("userpsace(" ) : ])
769+ self ["attrs" ].append (["OVS_ACTION_ATTR_USERSPACE" , uact ])
770+ parsed = True
733771
734772 actstr = actstr [strspn (actstr , ", " ) :]
735773 while parencount > 0 :
@@ -2112,10 +2150,70 @@ def miss(self, packetmsg):
21122150 print ("MISS upcall[%d/%s]: %s" % (seq , pktpres , keystr ), flush = True )
21132151
21142152 def execute (self , packetmsg ):
2115- print ("userspace execute command" )
2153+ print ("userspace execute command" , flush = True )
21162154
21172155 def action (self , packetmsg ):
2118- print ("userspace action command" )
2156+ print ("userspace action command" , flush = True )
2157+
2158+
2159+ class psample_sample (genlmsg ):
2160+ nla_map = (
2161+ ("PSAMPLE_ATTR_IIFINDEX" , "none" ),
2162+ ("PSAMPLE_ATTR_OIFINDEX" , "none" ),
2163+ ("PSAMPLE_ATTR_ORIGSIZE" , "none" ),
2164+ ("PSAMPLE_ATTR_SAMPLE_GROUP" , "uint32" ),
2165+ ("PSAMPLE_ATTR_GROUP_SEQ" , "none" ),
2166+ ("PSAMPLE_ATTR_SAMPLE_RATE" , "uint32" ),
2167+ ("PSAMPLE_ATTR_DATA" , "array(uint8)" ),
2168+ ("PSAMPLE_ATTR_GROUP_REFCOUNT" , "none" ),
2169+ ("PSAMPLE_ATTR_TUNNEL" , "none" ),
2170+ ("PSAMPLE_ATTR_PAD" , "none" ),
2171+ ("PSAMPLE_ATTR_OUT_TC" , "none" ),
2172+ ("PSAMPLE_ATTR_OUT_TC_OCC" , "none" ),
2173+ ("PSAMPLE_ATTR_LATENCY" , "none" ),
2174+ ("PSAMPLE_ATTR_TIMESTAMP" , "none" ),
2175+ ("PSAMPLE_ATTR_PROTO" , "none" ),
2176+ ("PSAMPLE_ATTR_USER_COOKIE" , "array(uint8)" ),
2177+ )
2178+
2179+ def dpstr (self ):
2180+ fields = []
2181+ data = ""
2182+ for (attr , value ) in self ["attrs" ]:
2183+ if attr == "PSAMPLE_ATTR_SAMPLE_GROUP" :
2184+ fields .append ("group:%d" % value )
2185+ if attr == "PSAMPLE_ATTR_SAMPLE_RATE" :
2186+ fields .append ("rate:%d" % value )
2187+ if attr == "PSAMPLE_ATTR_USER_COOKIE" :
2188+ value = "" .join (format (x , "02x" ) for x in value )
2189+ fields .append ("cookie:%s" % value )
2190+ if attr == "PSAMPLE_ATTR_DATA" and len (value ) > 0 :
2191+ data = "data:%s" % "" .join (format (x , "02x" ) for x in value )
2192+
2193+ return ("%s %s" % ("," .join (fields ), data )).strip ()
2194+
2195+
2196+ class psample_msg (Marshal ):
2197+ PSAMPLE_CMD_SAMPLE = 0
2198+ PSAMPLE_CMD_GET_GROUP = 1
2199+ PSAMPLE_CMD_NEW_GROUP = 2
2200+ PSAMPLE_CMD_DEL_GROUP = 3
2201+ PSAMPLE_CMD_SET_FILTER = 4
2202+ msg_map = {PSAMPLE_CMD_SAMPLE : psample_sample }
2203+
2204+
2205+ class Psample (EventSocket ):
2206+ genl_family = "psample"
2207+ mcast_groups = ["packets" ]
2208+ marshal_class = psample_msg
2209+
2210+ def read_samples (self ):
2211+ while True :
2212+ try :
2213+ for msg in self .get ():
2214+ print (msg .dpstr (), flush = True )
2215+ except NetlinkError as ne :
2216+ raise ne
21192217
21202218
21212219def print_ovsdp_full (dp_lookup_rep , ifindex , ndb = NDB (), vpl = OvsVport ()):
@@ -2175,7 +2273,7 @@ def main(argv):
21752273 help = "Increment 'verbose' output counter." ,
21762274 default = 0 ,
21772275 )
2178- subparsers = parser .add_subparsers ()
2276+ subparsers = parser .add_subparsers (dest = "subcommand" )
21792277
21802278 showdpcmd = subparsers .add_parser ("show" )
21812279 showdpcmd .add_argument (
@@ -2232,6 +2330,8 @@ def main(argv):
22322330 delfscmd = subparsers .add_parser ("del-flows" )
22332331 delfscmd .add_argument ("flsbr" , help = "Datapath name" )
22342332
2333+ subparsers .add_parser ("psample" )
2334+
22352335 args = parser .parse_args ()
22362336
22372337 if args .verbose > 0 :
@@ -2246,6 +2346,9 @@ def main(argv):
22462346
22472347 sys .setrecursionlimit (100000 )
22482348
2349+ if args .subcommand == "psample" :
2350+ Psample ().read_samples ()
2351+
22492352 if hasattr (args , "showdp" ):
22502353 found = False
22512354 for iface in ndb .interfaces :
0 commit comments