99import tabulate
1010import pyangbind .lib .pybindJSON as pybindJSON
1111from natsort import natsorted
12- from sonic_py_common import device_info
13- from swsssdk import ConfigDBConnector , SonicDBConfig
14- from swsscommon .swsscommon import SonicV2Connector
12+ from sonic_py_common import device_info , multi_asic
13+ from swsscommon .swsscommon import SonicV2Connector , ConfigDBConnector , SonicDBConfig
1514
1615
1716def info (msg ):
@@ -104,6 +103,7 @@ class AclLoader(object):
104103 "IP_RSVP" : 46 ,
105104 "IP_GRE" : 47 ,
106105 "IP_AUTH" : 51 ,
106+ "IP_ICMPV6" : 58 ,
107107 "IP_L2TP" : 115
108108 }
109109
@@ -115,8 +115,13 @@ def __init__(self):
115115 self .tables_db_info = {}
116116 self .rules_db_info = {}
117117 self .rules_info = {}
118- # Load global db config. This call is no-op in single npu platforms
119- SonicDBConfig .load_sonic_global_db_config ()
118+
119+ if multi_asic .is_multi_asic ():
120+ # Load global db config
121+ SonicDBConfig .load_sonic_global_db_config ()
122+ else :
123+ SonicDBConfig .initialize ()
124+
120125 self .sessions_db_info = {}
121126 self .configdb = ConfigDBConnector ()
122127 self .configdb .connect ()
@@ -286,6 +291,14 @@ def is_table_mirror(self, tname):
286291 """
287292 return self .tables_db_info [tname ]['type' ].upper ().startswith (self .ACL_TABLE_TYPE_MIRROR )
288293
294+ def is_table_ipv6 (self , tname ):
295+ """
296+ Check if ACL table type is IPv6 (L3V6 or MIRRORV6)
297+ :param tname: ACL table name
298+ :return: True if table type is IPv6 else False
299+ """
300+ return self .tables_db_info [tname ]["type" ].upper () in ("L3V6" , "MIRRORV6" )
301+
289302 def is_table_control_plane (self , tname ):
290303 """
291304 Check if ACL table type is ACL_TABLE_TYPE_CTRLPLANE
@@ -405,9 +418,18 @@ def convert_l2(self, table_name, rule_idx, rule):
405418 else :
406419 try :
407420 rule_props ["ETHER_TYPE" ] = int (rule .l2 .config .ethertype )
408- except :
409- raise AclLoaderException ("Failed to convert ethertype %s table %s rule %s" % (
410- rule .l2 .config .ethertype , table_name , rule_idx ))
421+ except Exception as e :
422+ raise AclLoaderException (
423+ "Failed to convert ethertype %s; table %s rule %s; exception=%s" %
424+ (rule .l2 .config .ethertype , table_name , rule_idx , str (e )))
425+
426+ if rule .l2 .config .vlan_id != "" and rule .l2 .config .vlan_id != "null" :
427+ vlan_id = rule .l2 .config .vlan_id
428+
429+ if vlan_id <= 0 or vlan_id >= 4096 :
430+ raise AclLoaderException ("VLAN ID %d is out of bounds (0, 4096)" % (vlan_id ))
431+
432+ rule_props ["VLAN_ID" ] = vlan_id
411433
412434 return rule_props
413435
@@ -418,7 +440,12 @@ def convert_ip(self, table_name, rule_idx, rule):
418440 # so there isn't currently a good way to check if the user defined proto=0 or not.
419441 if rule .ip .config .protocol :
420442 if rule .ip .config .protocol in self .ip_protocol_map :
421- rule_props ["IP_PROTOCOL" ] = self .ip_protocol_map [rule .ip .config .protocol ]
443+ # Special case: ICMP has different protocol numbers for IPv4 and IPv6, so if we receive
444+ # "IP_ICMP" we need to pick the correct protocol number for the IP version
445+ if rule .ip .config .protocol == "IP_ICMP" and self .is_table_ipv6 (table_name ):
446+ rule_props ["IP_PROTOCOL" ] = self .ip_protocol_map ["IP_ICMPV6" ]
447+ else :
448+ rule_props ["IP_PROTOCOL" ] = self .ip_protocol_map [rule .ip .config .protocol ]
422449 else :
423450 try :
424451 int (rule .ip .config .protocol )
@@ -449,6 +476,31 @@ def convert_ip(self, table_name, rule_idx, rule):
449476
450477 return rule_props
451478
479+ def convert_icmp (self , table_name , rule_idx , rule ):
480+ rule_props = {}
481+
482+ is_table_v6 = self .is_table_ipv6 (table_name )
483+ type_key = "ICMPV6_TYPE" if is_table_v6 else "ICMP_TYPE"
484+ code_key = "ICMPV6_CODE" if is_table_v6 else "ICMP_CODE"
485+
486+ if rule .icmp .config .type != "" and rule .icmp .config .type != "null" :
487+ icmp_type = rule .icmp .config .type
488+
489+ if icmp_type < 0 or icmp_type > 255 :
490+ raise AclLoaderException ("ICMP type %d is out of bounds [0, 255]" % (icmp_type ))
491+
492+ rule_props [type_key ] = icmp_type
493+
494+ if rule .icmp .config .code != "" and rule .icmp .config .code != "null" :
495+ icmp_code = rule .icmp .config .code
496+
497+ if icmp_code < 0 or icmp_code > 255 :
498+ raise AclLoaderException ("ICMP code %d is out of bounds [0, 255]" % (icmp_code ))
499+
500+ rule_props [code_key ] = icmp_code
501+
502+ return rule_props
503+
452504 def convert_port (self , port ):
453505 """
454506 Convert port field format from openconfig ACL to Config DB schema
@@ -507,6 +559,19 @@ def convert_input_interface(self, table_name, rule_idx, rule):
507559
508560 return rule_props
509561
562+ def validate_rule_fields (self , rule_props ):
563+ protocol = rule_props .get ("IP_PROTOCOL" )
564+
565+ if protocol :
566+ if "TCP_FLAGS" in rule_props and protocol != 6 :
567+ raise AclLoaderException ("IP_PROTOCOL={} is not TCP, but TCP flags were provided" .format (protocol ))
568+
569+ if ("ICMP_TYPE" in rule_props or "ICMP_CODE" in rule_props ) and protocol != 1 :
570+ raise AclLoaderException ("IP_PROTOCOL={} is not ICMP, but ICMP fields were provided" .format (protocol ))
571+
572+ if ("ICMPV6_TYPE" in rule_props or "ICMPV6_CODE" in rule_props ) and protocol != 58 :
573+ raise AclLoaderException ("IP_PROTOCOL={} is not ICMPV6, but ICMPV6 fields were provided" .format (protocol ))
574+
510575 def convert_rule_to_db_schema (self , table_name , rule ):
511576 """
512577 Convert rules format from openconfig ACL to Config DB schema
@@ -523,9 +588,12 @@ def convert_rule_to_db_schema(self, table_name, rule):
523588 deep_update (rule_props , self .convert_action (table_name , rule_idx , rule ))
524589 deep_update (rule_props , self .convert_l2 (table_name , rule_idx , rule ))
525590 deep_update (rule_props , self .convert_ip (table_name , rule_idx , rule ))
591+ deep_update (rule_props , self .convert_icmp (table_name , rule_idx , rule ))
526592 deep_update (rule_props , self .convert_transport (table_name , rule_idx , rule ))
527593 deep_update (rule_props , self .convert_input_interface (table_name , rule_idx , rule ))
528594
595+ self .validate_rule_fields (rule_props )
596+
529597 return rule_data
530598
531599 def deny_rule (self , table_name ):
@@ -538,7 +606,7 @@ def deny_rule(self, table_name):
538606 rule_data = {(table_name , "DEFAULT_RULE" ): rule_props }
539607 rule_props ["PRIORITY" ] = str (self .min_priority )
540608 rule_props ["PACKET_ACTION" ] = "DROP"
541- if 'v6' in table_name . lower ( ):
609+ if self . is_table_ipv6 ( table_name ):
542610 rule_props ["IP_TYPE" ] = "IPV6ANY" # ETHERTYPE is not supported for DATAACLV6
543611 else :
544612 rule_props ["ETHER_TYPE" ] = str (self .ethertype_map ["ETHERTYPE_IPV4" ])
0 commit comments