def _buildAcls(self): for aclname, aclrules in self.config.match.iteritems(): key = eossdk.AclKey(aclname, eossdk.ACL_TYPE_IPV4) # todo support ipv6 also for i, rule in enumerate(aclrules): aclRule = eossdk.AclRuleIp() if rule.proto: pr = PROTOCOLS.get(rule.proto.lower()) if pr: aclRule.ip_protocol_is(pr) else: sys.stderr.write('Invalid protocol name "%s"', rule.proto) if rule.src_ip: try: srcPfx = eossdk.IpPrefix(rule.src_ip) addr = eossdk.IpAddrMask(srcPfx.network(), srcPfx.prefix_length()) aclRule.source_addr_is(addr) except eossdk.Error: sys.stderr.write('bad IP address: %s\n', rule.src_ip) continue if rule.dst_ip: try: dstPfx = eossdk.IpPrefix(rule.dst_ip) addr = eossdk.IpAddrMask(dstPfx.network(), dstPfx.prefix_length()) aclRule.destination_addr_is(addr) except eossdk.Error: sys.stderr.write('bad IP address: %s\n', rule.dst_ip) continue if rule.sport: try: spec = self._buildPortSpec(rule.sport) except ConfigError as e: sys.stderr.write('Invalid port spec %r: %s\n', spec, e) else: aclRule.source_port_is(spec) if rule.dport: try: spec = self._buildPortSpec(rule.dport) except ConfigError as e: sys.stderr.write('Invalid port spec %r: %s\n', spec, e) else: aclRule.destination_port_is(spec) self.acl_mgr.acl_rule_set(key, i, aclRule) self.acl_mgr.acl_commit()
def insert_routes(self, start, end, newApi): self.tracer.trace1("Starting to insert {} routes, \ starting at route# {}".format(end, start)) for i in range(start, end): first = 200 + (i / 65536) second = (i / 256) % 256 third = i % 256 addrStr = "%d.%d.%d.0" % (first, second, third) prefix = eossdk.IpPrefix(eossdk.IpAddr(addrStr), 24) if i % 2550 == 0: self.tracer.trace3("Adding prefix " + prefix.to_string()) rkey = eossdk.IpRouteKey(prefix) route = eossdk.IpRoute(rkey) route.tag_is(self.tag) via = eossdk.IpRouteVia(rkey) via.nexthop_group_is("mpls_nhg") if not newApi: self.ipMgr.ip_route_set(route) else: self.ipMgr.ip_route_set(route, eossdk.IP_ROUTE_ACTION_NEXTHOP_GROUP) self.ipMgr.ip_route_via_set(via) self.tracer.trace1("Finished inserting {} routes, \ starting at route# {}".format(end, start))
def create_ip_v6_route_key(self, i): first = (i / 65536) % 65536 second = i % 65536 addrStr = "%04x:%04x::" % (first, second) ip = eossdk.IpAddr(addrStr) prefix6 = eossdk.IpPrefix(ip, 64) rkey6 = eossdk.IpRouteKey(prefix6) return rkey6
def create_ip_route_key(self, i): first = 200 + (i / 65536) second = (i / 256) % 256 third = i % 256 addrStr = "%d.%d.%d.0" % (first, second, third) prefix = eossdk.IpPrefix(eossdk.IpAddr(addrStr), 24) if i % 2550 == 0: self.tracer.trace3("Adding prefix %s" % prefix.to_string()) rkey = eossdk.IpRouteKey(prefix) return rkey
def update_acl(self, prefix): global ACLENTRY # Configure V4 ACL syslog.syslog("inside Update ACL, ACLENTRY No: %s" % ACLENTRY) aclRule = eossdk.AclRuleIp() aclRule.ip_protocol_is(4) aclRule.action_is(eossdk.ACL_PERMIT) syslog.syslog("ACL Rule Created") srcPfx = eossdk.IpPrefix(prefix) addr = eossdk.IpAddrMask(srcPfx.network(), srcPfx.prefix_length()) aclRule.source_addr_is(addr) dstPfx = eossdk.IpPrefix("0.0.0.0/0") addr = eossdk.IpAddrMask(dstPfx.network(), dstPfx.prefix_length()) aclRule.destination_addr_is(addr) self.aclMgr_.acl_rule_set(self.aclKey, ACLENTRY, aclRule) ACLENTRY += 10 syslog.syslog("ACL Updated: %s prefix added" % prefix)
def _set_route(self, prefix, nexthop_group): """ { "command": "set", "type": "route", "prefix": "5.3.0.0/24", "nexthop_group": "CYGNUS_NHG_1" } """ prefix = eossdk.IpPrefix(str(prefix)) route_key = eossdk.IpRouteKey(prefix) route = eossdk.IpRoute(route_key) via = eossdk.IpRouteVia(route_key) via.nexthop_group_is(str(nexthop_group)) self.ip_route_mgr.ip_route_set(route) self.ip_route_mgr.ip_route_via_set(via)
def _del_route(self, prefix, nexthop_group=None): """ { "command": "del", "type": "route", "prefix": "5.3.0.0/24", "nexthop_group": "CYGNUS_NHG_1" // optional } """ prefix = eossdk.IpPrefix(str(prefix)) route_key = eossdk.IpRouteKey(prefix) route = eossdk.IpRoute(route_key) if nexthop_group: via = eossdk.IpRouteVia(route_key) via.nexthop_group_is(str(nexthop_group)) self.ip_route_mgr.ip_route_via_del(via) else: self.ip_route_mgr.ip_route_del(route_key)
def insert_v6_routes(self, start, end, newApi): self.tracer.trace1("Starting to insert {} v6_routes, \ starting at route# {}".format(end, start)) for i in range(start, end): first = (i / 65536) % 65536 second = i % 65536 addrStr = "%04x:%04x::" % (first, second) ip = eossdk.IpAddr(addrStr) prefix6 = eossdk.IpPrefix(ip, 64) rkey6 = eossdk.IpRouteKey(prefix6) route6 = eossdk.IpRoute(rkey6) route6.tag_is(self.tag) via6 = eossdk.IpRouteVia(rkey6) via6.nexthop_group_is("mpls_nhg") if not newApi: self.ipMgr.ip_route_set(route6) else: self.ipMgr.ip_route_set(route6, eossdk.IP_ROUTE_ACTION_NEXTHOP_GROUP) self.ipMgr.ip_route_via_set(via6) self.tracer.trace1("Finished inserting {} routes, \ starting at route# {}".format(end, start))
def process_config(self): """Critical function; processes configuration and rules description files. Called upon initialization and then subsequently whenever inotify indicates the configuration file has changed on disk.""" self.tracer.trace0("Processing config") syslog.syslog("Attempting to process configuration file(s)") # Time stamp for performance evaluation start_time = time.time() self.start_time = start_time # Attempt to parse ACLerate_config_file # Initially, attempt to acquire the lock to ensure file not modified # by another entity while it is being processed here. syslog.syslog("Attempting to open, lock and parse %s" % self.config_file) try: with open(self.config_file) as acl_config_file: for i in xrange(file_lock_attempt): try: fcntl.flock(acl_config_file, fcntl.LOCK_EX | fcntl.LOCK_NB) break except IOError: sys.stderr.write("Attempt %s to lock %s " "failed\n" % (str(i+1), self.config_file)) time.sleep(file_lock_interval) else: syslog.syslog("All %s attempts to lock %s " "failed" % (str(file_lock_attempt), self.config_file)) sys.stderr.write("All %s attempts to lock %s " "failed\n" % (str(file_lock_attempt), self.config_file)) return syslog.syslog("%s opened & locked successfully. Now parse" % self.config_file) acl_config_list = json.load(acl_config_file) except IOError: syslog.syslog("Cannot open %s" % self.config_file) sys.stderr.write("Cannot open %s\n" % self.config_file) return for acl_config in acl_config_list: command = acl_config.get("command") name = acl_config.get("name") acl_type = acl_config.get("type") interface = acl_config.get("interface") operation = acl_config.get("operation") direction = acl_config.get("direction") rules_file = acl_config.get("rules") counting = acl_config.get("counting") # Sanity check ACL parameters (before potentially # iterating over thousands of rules!). If invalid, skip # processing rest of this ACL and continue to next ACL if not acl_validate(command, name, acl_type, direction, counting): syslog.syslog("Invalid ACL input data") sys.stderr.write("Invalid ACL input data\n") continue syslog.syslog("Processing %s command for %s ACL %s" % (command, acl_type, name)) # Convert external ACL type to SDK's type sdk_type = acl_type_convert(acl_type) if sdk_type is None: syslog.syslog("Invalid ACL type") sys.stderr.write("Invalid ACL type specified\n") continue # Get handle to ACL. acl_key = eossdk.AclKey(str(name), sdk_type) # If input command is to delete the ACL, simply call the appropriate # SDK API and continue onto next ACL in the list. i.e. no need to be # concerned with interfaces, rules etc. if command.lower() == "delete-acl": syslog.syslog("About to delete %s ACL %s" % (acl_type, name)) self.acl_mgr.acl_del(acl_key) # Now call commit to actually push changes to HW. self.acl_mgr.acl_commit() continue intf_id = None # Next, if an interface is specified, verify it actually exists # and correct parameters have been specified if interface: intf_id = self.interface_validate(interface, operation, direction) if intf_id: # Convert external direction to SDK's type sdk_direction = direction_convert(direction) if sdk_direction is None: syslog.syslog("Invalid direction %s specified" % direction) sys.stderr.write("Invalid direction %s specified\n" % direction) continue else: syslog.syslog("Invalid interface %s specified" % interface) sys.stderr.write("Invalid interface %s specified\n" % interface) continue # Set ACL counting behaviour if specified. If not, will # simply fallback to default ACL behaviour. if counting: if counting.lower() == "true": self.acl_mgr.acl_counters_enabled_set(acl_key, True) if counting.lower() == "false": self.acl_mgr.acl_counters_enabled_set(acl_key, False) # Rules files is needed. Does it actually exist? # Is a comprehensive unwind needed in the error case? if rules_file is None: syslog.syslog("Need to add/remove rules but no rule info file specified") sys.stderr.write("Need to add/remove rules but no rule info file specified\n") continue # Attempt to parse rules_file # Initially, attempt to acquire the lock to ensure file not modified # by another entity while it is being processed here. syslog.syslog("Attempting to open, lock and parse %s" % rules_file) try: with open(rules_file) as rule_listing_file: for i in xrange(file_lock_attempt): try: fcntl.flock(rule_listing_file, fcntl.LOCK_EX | fcntl.LOCK_NB) break except IOError: sys.stderr.write("Attempt %s to lock %s " "failed\n" % (str(i+1), rule_file)) time.sleep(file_lock_interval) else: syslog.syslog("All %s attempts to lock %s " "failed" % (str(file_lock_attempt), rules_file)) sys.stderr.write("All %s attempts to lock %s " "failed\n" % (str(file_lock_attempt), rules_file)) return syslog.syslog("%s opened & locked successfully. Now parse" % rules_file) rule_list = json.load(rule_listing_file) except IOError: syslog.syslog("Cannot open %s" % rules_file) sys.stderr.write("Cannot open %s\n" % rules_file) continue # Now iterate over all rules for index, rule in enumerate(rule_list): number = rule.get("number") source = rule.get("source") destination = rule.get("destination") protocol = rule.get("protocol") action = rule.get("action") log = rule.get("log") # Rule must have a sequence number. if number is None: syslog.syslog("Rule must have a number") sys.stderr.write("Rule must have a number\n") continue # If input command is to delete the rule, simply call the SDK API # and continue to next rule in the list. # i.e. no need to be concerned with addresses, protocols etc. if command.lower() == "delete-rule": self.acl_mgr.acl_rule_del(acl_key, int(number)) continue # If here, then the command must be to add rule. # So verify if rule data supplied is valid. if rule_validate(number, source, destination, action) == False: syslog.syslog("Invalid rule input data") sys.stderr.write("Invalid rule input data\n") continue # Create IP ACL rule object. Used for IPv4 and IPv6 ACLs but # different object needed for Ethernet ACLs. # (Perhaps create object with all rule info and invoke # functions to process for ACL, IPv4, IPv6 etc? # Needs more thought on optimal way to handle different ACL type) acl_rule = eossdk.AclRuleIp() # Now parse the rule data and invoke appropriate SDK # APIs to create requisite data structures. if source: try: # How is "any" handled? 0.0.0.0/0? if source == "any": source = "0.0.0.0/0" # String to IP address conversion of source? sdk_prefix = eossdk.IpPrefix(str(source)) sdk_addr = eossdk.IpAddrMask(sdk_prefix.network(), sdk_prefix.prefix_length()) acl_rule.source_addr_is(sdk_addr) except eossdk.Error: syslog.syslog("Error processing source address %s" % source) sys.stderr.write("Error processing source address %s\n" % source) continue if destination: try: # How is "any" handled? 0.0.0.0/0? if destination == "any": destination = "0.0.0.0/0" # String to IP address conversion of destination? sdk_prefix = eossdk.IpPrefix(str(destination)) sdk_addr = eossdk.IpAddrMask(sdk_prefix.network(), sdk_prefix.prefix_length()) acl_rule.destination_addr_is(sdk_addr) except eossdk.Error: syslog.syslog("Error processing destination address %s" % destination) sys.stderr.write("Error processing destination address %s\n" % destination) continue if protocol: protocol_number = protocol_validate(protocol, sdk_type) if protocol_number: acl_rule.ip_protocol_is(protocol_number) else: syslog.syslog("Error processing protocol %s" % protocol) sys.stderr.write("Error processing protocol %s\n" % protocol) continue # Previously verified that action is 'permit' or 'deny' if action: try: if action.lower() == "permit": acl_rule.action_is(eossdk.ACL_PERMIT) else: acl_rule.action_is(eossdk.ACL_DENY) except: syslog.syslog("Error processing action %s" % action) sys.stderr.write("Error processing action %s\n" % action) continue if log: try: if log.lower() == "true": acl_rule.log_is(True) else: acl_rule.log_is(False) except: syslog.syslog("Error processing log %s" % log) sys.stderr.write("Error processing log %s\n" % log) continue # Now add this rule to ACL, with appropriate sequence number # Should only ever be here when adding rules.... paranoid check. if command.lower() == "add-rule": self.acl_mgr.acl_rule_set(acl_key, int(number), acl_rule) parsing_time = time.time() self.parsing_time = parsing_time self.parsing_duration = parsing_time - start_time syslog.syslog("Time to parse config files for ACL %s " "is %ss" % (name, self.parsing_duration)) self.rule_count = index+1 syslog.syslog("Processing %s rules complete. " "Now commit ACL %s to HW" % (index+1, name)) # Now call commit to actually push changes to HW. self.acl_mgr.acl_commit() # Should ACL be attached or detached from interface? if intf_id: if operation.lower() == "attach": syslog.syslog("Attaching ACL %s to interface %s " "%sbound" % (name, interface, direction.lower())) self.acl_mgr.acl_apply(acl_key, intf_id, sdk_direction, True) if operation.lower() == "detach": syslog.syslog("Detaching ACL %s from interface %s " "%sbound" % (name, interface, direction.lower())) self.acl_mgr.acl_apply(acl_key, intf_id, sdk_direction, False)