88import errno
99import ipaddress
1010import logging
11+ import math
1112import multiprocessing
1213import re
1314import socket
6061OVS_FLOW_CMD_GET = 3
6162OVS_FLOW_CMD_SET = 4
6263
64+ UINT32_MAX = 0xFFFFFFFF
6365
6466def macstr (mac ):
6567 outstr = ":" .join (["%02X" % i for i in mac ])
@@ -281,6 +283,75 @@ def parse_extract_field(
281283 return str_skipped , data
282284
283285
286+ def parse_attrs (actstr , attr_desc ):
287+ """Parses the given action string and returns a list of netlink
288+ attributes based on a list of attribute descriptions.
289+
290+ Each element in the attribute description list is a tuple such as:
291+ (name, attr_name, parse_func)
292+ where:
293+ name: is the string representing the attribute
294+ attr_name: is the name of the attribute as defined in the uAPI.
295+ parse_func: is a callable accepting a string and returning either
296+ a single object (the parsed attribute value) or a tuple of
297+ two values (the parsed attribute value and the remaining string)
298+
299+ Returns a list of attributes and the remaining string.
300+ """
301+ def parse_attr (actstr , key , func ):
302+ actstr = actstr [len (key ) :]
303+
304+ if not func :
305+ return None , actstr
306+
307+ delim = actstr [0 ]
308+ actstr = actstr [1 :]
309+
310+ if delim == "=" :
311+ pos = strcspn (actstr , ",)" )
312+ ret = func (actstr [:pos ])
313+ else :
314+ ret = func (actstr )
315+
316+ if isinstance (ret , tuple ):
317+ (datum , actstr ) = ret
318+ else :
319+ datum = ret
320+ actstr = actstr [strcspn (actstr , ",)" ):]
321+
322+ if delim == "(" :
323+ if not actstr or actstr [0 ] != ")" :
324+ raise ValueError ("Action contains unbalanced parentheses" )
325+
326+ actstr = actstr [1 :]
327+
328+ actstr = actstr [strspn (actstr , ", " ) :]
329+
330+ return datum , actstr
331+
332+ attrs = []
333+ attr_desc = list (attr_desc )
334+ while actstr and actstr [0 ] != ")" and attr_desc :
335+ found = False
336+ for i , (key , attr , func ) in enumerate (attr_desc ):
337+ if actstr .startswith (key ):
338+ datum , actstr = parse_attr (actstr , key , func )
339+ attrs .append ([attr , datum ])
340+ found = True
341+ del attr_desc [i ]
342+
343+ if not found :
344+ raise ValueError ("Unknown attribute: '%s'" % actstr )
345+
346+ actstr = actstr [strspn (actstr , ", " ) :]
347+
348+ if actstr [0 ] != ")" :
349+ raise ValueError ("Action string contains extra garbage or has "
350+ "unbalanced parenthesis: '%s'" % actstr )
351+
352+ return attrs , actstr [1 :]
353+
354+
284355class ovs_dp_msg (genlmsg ):
285356 # include the OVS version
286357 # We need a custom header rather than just being able to rely on
@@ -299,7 +370,7 @@ class ovsactions(nla):
299370 ("OVS_ACTION_ATTR_SET" , "ovskey" ),
300371 ("OVS_ACTION_ATTR_PUSH_VLAN" , "none" ),
301372 ("OVS_ACTION_ATTR_POP_VLAN" , "flag" ),
302- ("OVS_ACTION_ATTR_SAMPLE" , "none " ),
373+ ("OVS_ACTION_ATTR_SAMPLE" , "sample " ),
303374 ("OVS_ACTION_ATTR_RECIRC" , "uint32" ),
304375 ("OVS_ACTION_ATTR_HASH" , "none" ),
305376 ("OVS_ACTION_ATTR_PUSH_MPLS" , "none" ),
@@ -318,8 +389,85 @@ class ovsactions(nla):
318389 ("OVS_ACTION_ATTR_ADD_MPLS" , "none" ),
319390 ("OVS_ACTION_ATTR_DEC_TTL" , "none" ),
320391 ("OVS_ACTION_ATTR_DROP" , "uint32" ),
392+ ("OVS_ACTION_ATTR_PSAMPLE" , "psample" ),
321393 )
322394
395+ class psample (nla ):
396+ nla_flags = NLA_F_NESTED
397+
398+ nla_map = (
399+ ("OVS_PSAMPLE_ATTR_UNSPEC" , "none" ),
400+ ("OVS_PSAMPLE_ATTR_GROUP" , "uint32" ),
401+ ("OVS_PSAMPLE_ATTR_COOKIE" , "array(uint8)" ),
402+ )
403+
404+ def dpstr (self , more = False ):
405+ args = "group=%d" % self .get_attr ("OVS_PSAMPLE_ATTR_GROUP" )
406+
407+ cookie = self .get_attr ("OVS_PSAMPLE_ATTR_COOKIE" )
408+ if cookie :
409+ args += ",cookie(%s)" % \
410+ "" .join (format (x , "02x" ) for x in cookie )
411+
412+ return "psample(%s)" % args
413+
414+ def parse (self , actstr ):
415+ desc = (
416+ ("group" , "OVS_PSAMPLE_ATTR_GROUP" , int ),
417+ ("cookie" , "OVS_PSAMPLE_ATTR_COOKIE" ,
418+ lambda x : list (bytearray .fromhex (x )))
419+ )
420+
421+ attrs , actstr = parse_attrs (actstr , desc )
422+
423+ for attr in attrs :
424+ self ["attrs" ].append (attr )
425+
426+ return actstr
427+
428+ class sample (nla ):
429+ nla_flags = NLA_F_NESTED
430+
431+ nla_map = (
432+ ("OVS_SAMPLE_ATTR_UNSPEC" , "none" ),
433+ ("OVS_SAMPLE_ATTR_PROBABILITY" , "uint32" ),
434+ ("OVS_SAMPLE_ATTR_ACTIONS" , "ovsactions" ),
435+ )
436+
437+ def dpstr (self , more = False ):
438+ args = []
439+
440+ args .append ("sample={:.2f}%" .format (
441+ 100 * self .get_attr ("OVS_SAMPLE_ATTR_PROBABILITY" ) /
442+ UINT32_MAX ))
443+
444+ actions = self .get_attr ("OVS_SAMPLE_ATTR_ACTIONS" )
445+ if actions :
446+ args .append ("actions(%s)" % actions .dpstr (more ))
447+
448+ return "sample(%s)" % "," .join (args )
449+
450+ def parse (self , actstr ):
451+ def parse_nested_actions (actstr ):
452+ subacts = ovsactions ()
453+ parsed_len = subacts .parse (actstr )
454+ return subacts , actstr [parsed_len :]
455+
456+ def percent_to_rate (percent ):
457+ percent = float (percent .strip ('%' ))
458+ return int (math .floor (UINT32_MAX * (percent / 100.0 ) + .5 ))
459+
460+ desc = (
461+ ("sample" , "OVS_SAMPLE_ATTR_PROBABILITY" , percent_to_rate ),
462+ ("actions" , "OVS_SAMPLE_ATTR_ACTIONS" , parse_nested_actions ),
463+ )
464+ attrs , actstr = parse_attrs (actstr , desc )
465+
466+ for attr in attrs :
467+ self ["attrs" ].append (attr )
468+
469+ return actstr
470+
323471 class ctact (nla ):
324472 nla_flags = NLA_F_NESTED
325473
@@ -683,6 +831,18 @@ def parse(self, actstr):
683831 self ["attrs" ].append (["OVS_ACTION_ATTR_CT" , ctact ])
684832 parsed = True
685833
834+ elif parse_starts_block (actstr , "sample(" , False ):
835+ sampleact = self .sample ()
836+ actstr = sampleact .parse (actstr [len ("sample(" ) : ])
837+ self ["attrs" ].append (["OVS_ACTION_ATTR_SAMPLE" , sampleact ])
838+ parsed = True
839+
840+ elif parse_starts_block (actstr , "psample(" , False ):
841+ psampleact = self .psample ()
842+ actstr = psampleact .parse (actstr [len ("psample(" ) : ])
843+ self ["attrs" ].append (["OVS_ACTION_ATTR_PSAMPLE" , psampleact ])
844+ parsed = True
845+
686846 actstr = actstr [strspn (actstr , ", " ) :]
687847 while parencount > 0 :
688848 parencount -= 1
0 commit comments