def test_swap(self): retcode = futils.CommandOutput("", "") with mock.patch('calico.felix.futils.check_call', return_value=retcode): ipsets.swap("a", "b") futils.check_call.assert_called_with(["ipset", "swap", "a", "b"])
def update_ipsets(type, descr, suffix, rule_list, ipset_addr, ipset_port, ipset_icmp, tmp_ipset_addr, tmp_ipset_port, tmp_ipset_icmp): """ Update the ipsets with a given set of rules. If a rule is invalid we do not throw an exception or give up, but just log an error and continue. """ for rule in rule_list: if rule.get('cidr') is None: log.error("Invalid %s rule without cidr for %s : %s" % (descr, suffix, rule)) continue #*********************************************************************# #* The ipset format is something like "10.11.1.3,udp:0" *# #* Further valid examples include *# #* 10.11.1.0/24 *# #* 10.11.1.0/24,tcp *# #* 10.11.1.0/24,80 *# #* *# #*********************************************************************# if rule['cidr'].endswith("/0"): #*****************************************************************# #* We have to handle any CIDR with a "/0" specially, since we *# #* split it into two ipsets entries; ipsets cannot have zero *# #* CIDR length in bits. *# #*****************************************************************# if type == IPV4: cidrs = ["0.0.0.0/1", "128.0.0.0/1"] else: cidrs = ["::/1", "8000::/1"] else: cidrs = [rule['cidr']] #*********************************************************************# #* Now handle the protocol. There are three types of protocol. tcp / *# #* sctp /udp / udplite have an optional port. icmp / icmpv6 have an *# #* optional type and code. Anything else doesn't have ports. *# #* *# #* We build the value to insert without the CIDR, then prepend the *# #* CIDR later (since we may need to use two CIDRs). *# #*********************************************************************# protocol = rule.get('protocol') port = rule.get('port') icmp_type = rule.get('icmp_type') icmp_code = rule.get('icmp_code') if protocol is None: if rule.get('port') is not None: # No protocol, so port is not allowed. log.error( "Invalid %s rule with port but no protocol for %s : %s", descr, suffix, rule) continue ipset_value = "" ipset = tmp_ipset_addr elif protocol in ("tcp", "sctp", "udp", "udplite"): if port is None: # ipsets use port 0 to mean "any port" ipset_value = ",%s:0" % (protocol) ipset = tmp_ipset_port elif isinstance(port, list) and len(port) == 2: # List of two ports - port range if (not common.validate_port(str(port[0])) or not common.validate_port(str(port[1])) ): # Port range was not two valid ports. log.error( "Invalid port range in %s rule for %s : %s", descr, suffix, rule) continue ipset_value = ",%s:%s-%s" % (protocol, port[0], port[1]) ipset = tmp_ipset_port else: if not common.validate_port(str(port)): # Port was supplied but was not an integer or range. log.error( "Invalid port in %s rule for %s : %s", descr, suffix, rule) continue # An integer port was specified. ipset_value = ",%s:%s" % (protocol, port) ipset = tmp_ipset_port elif protocol in ("icmp", "icmpv6"): if rule.get('port') is not None: # No protocol, so port is not allowed. log.error( "Invalid %s rule for %s with port for protocol %s : %s", descr, suffix, protocol, rule) continue if (icmp_type is None and icmp_code is not None): # A code but no type - not allowed. log.error( "Invalid %s rule with ICMP code but no type for %s : %s", descr, suffix, rule) continue if icmp_type is None: # No type - all ICMP to / from the cidr, so use the ICMP ipset. ipset_value = "" ipset = tmp_ipset_icmp else: try: # Assume integer ICMP type first. int(icmp_type) if icmp_code is None: # Code defaults to 0 if not supplied. icmp_code = 0 ipset_value = ",%s:%s/%s" % (protocol, icmp_type, icmp_code) ipset = tmp_ipset_port except ValueError: # Not an integer ICMP type - must be a string code name. ipset_value = ",%s:%s" % (protocol, icmp_type) ipset = tmp_ipset_port else: if port is not None: # The supplied protocol does not allow ports. log.error( "Invalid %s rule with port but no protocol for %s : %s", descr, suffix, rule) continue # ipsets use port 0 to mean "any port" ipset_value = ",%s:0" % (protocol) ipset = tmp_ipset_port # Now add those values to the ipsets. for cidr in cidrs: try: ipsets.add(ipset, cidr + ipset_value) except FailedSystemCall: log.exception("Failed to add %s rule (%s) for %s", descr, cidr + ipset_value, suffix) # Now that we have filled the tmp ipset, swap it with the real one. ipsets.swap(tmp_ipset_addr, ipset_addr) ipsets.swap(tmp_ipset_port, ipset_port) ipsets.swap(tmp_ipset_icmp, ipset_icmp) # Get the temporary ipsets clean again - we leave them existing but empty. ipsets.flush(tmp_ipset_port) ipsets.flush(tmp_ipset_addr) ipsets.flush(tmp_ipset_icmp)