def _remove_chains(self): try: self.iptables_updater.delete_chains(chain_names(self._suffix), async=True) except FailedSystemCall: _log.exception("Failed to delete chains for %s", self) self._failed = True
def _remove_chains(self): try: self.iptables_updater.delete_chains(chain_names(self._suffix), async=True) except CalledProcessError: _log.exception("Failed to delete chains for %s", self) self._failed = True
def _remove_chains(self): try: self.iptables_updater.delete_chains(chain_names(self._suffix), async=True) except FailedSystemCall: _log.exception("Failed to delete chains for %s", self) else: self._iptables_in_sync = True
def _remove_chains(self): try: self.iptables_updater.delete_chains(chain_names(self._suffix), async=True) except FailedSystemCall: _log.exception("Failed to delete chains for %s", self) else: self._iptables_in_sync = True self._chains_programmed = False
def _get_endpoint_rules(endpoint_id, suffix, mac, profile_ids): to_chain_name, from_chain_name = chain_names(suffix) to_chain, to_deps = _build_to_or_from_chain(endpoint_id, profile_ids, to_chain_name, "inbound") from_chain, from_deps = _build_to_or_from_chain( endpoint_id, profile_ids, from_chain_name, "outbound", expected_mac=mac ) updates = {to_chain_name: to_chain, from_chain_name: from_chain} deps = {to_chain_name: to_deps, from_chain_name: from_deps} return updates, deps
def _get_endpoint_rules(endpoint_id, suffix, mac, profile_ids): to_chain_name, from_chain_name = chain_names(suffix) to_chain, to_deps = _build_to_or_from_chain(endpoint_id, profile_ids, to_chain_name, "inbound") from_chain, from_deps = _build_to_or_from_chain( endpoint_id, profile_ids, from_chain_name, "outbound", expected_mac=mac, ) updates = {to_chain_name: to_chain, from_chain_name: from_chain} deps = {to_chain_name: to_deps, from_chain_name: from_deps} return updates, deps
def _update_chains(self): updates, deps = _get_endpoint_rules(self.combined_id.endpoint, self._suffix, self._mac, self.endpoint["profile_ids"]) try: self.iptables_updater.rewrite_chains(updates, deps, async=False) except FailedSystemCall: _log.exception("Failed to program chains for %s. Removing.", self) try: self.iptables_updater.delete_chains(chain_names(self._suffix), async=False) except FailedSystemCall: _log.exception("Failed to remove chains after original " "failure") else: self._iptables_in_sync = True
def _update_chains(self): updates, deps = _get_endpoint_rules(self.combined_id.endpoint, self._suffix, self._mac, self.endpoint["profile_ids"]) try: self.iptables_updater.rewrite_chains(updates, deps, async=False) except FailedSystemCall: _log.exception("Failed to program chains for %s. Removing.", self) try: self.iptables_updater.delete_chains(chain_names(self._suffix), async=False) except FailedSystemCall: _log.exception("Failed to remove chains after original " "failure") else: self._iptables_in_sync = True self._chains_programmed = True
def _calculate_update(self, ifaces): """ Calculates the iptables update to rewrite our chains. To avoid traversing lots of dispatch rules to find the right one, we build a tree of chains. Currently, the tree can only be two layers deep: a root chain and a layer of leaves. Interface names look like this: "prefix1234abc". The "prefix" part is always the same so we ignore it. We call "1234abc", the "suffix". The root chain contains two sorts of rules: * where there are multiple interfaces whose suffixes start with the same character, it contains a rule that matches on that prefix of the "suffix"(!) and directs the packet to a leaf chain for that prefix. * as an optimization, if there is only one interface whose suffix starts with a given character, it contains a dispatch rule for that exact interface name. For example, if we have interface names "tapA1" "tapB1" "tapB2", we'll get (in pseudo code): Root chain: if interface=="tapA1" then goto chain for endpoint tapA1 if interface.startswith("tapB") then goto leaf chain for prefix "tapB" tapB leaf chain: if interface=="tapB1" then goto chain for endpoint tapB1 if interface=="tapB2" then goto chain for endpoint tapB2 :param set[str] ifaces: The list of interfaces to generate a dispatch chain for. :returns Tuple: to_delete, deps, updates, new_leaf_chains: * set of leaf chains that are no longer needed for deletion * chain dependency dict. * chain updates dict. * complete set of leaf chains that are now required. """ # iptables update fragments/dependencies for the root chains. updates = defaultdict(list) root_to_upds = updates[CHAIN_TO_ENDPOINT] root_from_upds = updates[CHAIN_FROM_ENDPOINT] dependencies = defaultdict(set) root_to_deps = dependencies[CHAIN_TO_ENDPOINT] root_from_deps = dependencies[CHAIN_FROM_ENDPOINT] # Special case: allow the metadata IP through from all interfaces. if self.config.METADATA_IP is not None and self.ip_version == 4: # Need to allow outgoing Metadata requests. root_from_upds.append("--append %s " "--protocol tcp " "--in-interface %s+ " "--destination %s " "--dport %s " "--jump RETURN" % (CHAIN_FROM_ENDPOINT, self.config.IFACE_PREFIX, self.config.METADATA_IP, self.config.METADATA_PORT)) # Separate the interface names by their prefixes so we can count them # and decide whether to program a leaf chain or not. interfaces_by_prefix = defaultdict(set) for iface in ifaces: ep_suffix = interface_to_suffix(self.config, iface) prefix = ep_suffix[:1] interfaces_by_prefix[prefix].add(iface) # Spin through the interfaces by prefix. Either add them directly # to the root chain or create a leaf and add them there. new_leaf_chains = set() for prefix, interfaces in interfaces_by_prefix.iteritems(): use_root_chain = len(interfaces) == 1 if use_root_chain: # Optimization: there's only one interface with this prefix, # don't program a leaf chain. disp_to_chain = CHAIN_TO_ENDPOINT disp_from_chain = CHAIN_FROM_ENDPOINT to_deps = root_to_deps from_deps = root_from_deps to_upds = root_to_upds from_upds = root_from_upds else: # There's more than one interface with this prefix, program # a leaf chain. disp_to_chain = CHAIN_TO_LEAF + "-" + prefix disp_from_chain = CHAIN_FROM_LEAF + "-" + prefix to_upds = updates[disp_to_chain] from_upds = updates[disp_from_chain] to_deps = dependencies[disp_to_chain] from_deps = dependencies[disp_from_chain] new_leaf_chains.add(disp_from_chain) new_leaf_chains.add(disp_to_chain) # Root chain depends on its leaves. root_from_deps.add(disp_to_chain) root_to_deps.add(disp_from_chain) # Point root chain at prefix chain. iface_match = self.config.IFACE_PREFIX + prefix + "+" root_from_upds.append( "--append %s --in-interface %s --goto %s" % (CHAIN_FROM_ENDPOINT, iface_match, disp_from_chain) ) root_to_upds.append( "--append %s --out-interface %s --goto %s" % (CHAIN_TO_ENDPOINT, iface_match, disp_to_chain) ) # Common processing, add the per-endpoint rules to whichever # chain we decided above. for iface in interfaces: # Add rule to leaf or global chain to direct traffic to the # endpoint-specific one. Note that we use --goto, which means # that the endpoint-specific chain will return to our parent # rather than to this chain. ep_suffix = interface_to_suffix(self.config, iface) to_chain_name, from_chain_name = chain_names(ep_suffix) from_upds.append("--append %s --in-interface %s --goto %s" % (disp_from_chain, iface, from_chain_name)) from_deps.add(from_chain_name) to_upds.append("--append %s --out-interface %s --goto %s" % (disp_to_chain, iface, to_chain_name)) to_deps.add(to_chain_name) if not use_root_chain: # Add a default drop to the end of the leaf chain. from_upds.append("--append %s --jump DROP" % disp_from_chain) to_upds.append("--append %s --jump DROP" % disp_to_chain) # Both TO and FROM chains end with a DROP so that interfaces that # we don't know about yet can't bypass our rules. root_from_upds.append("--append %s --jump DROP" % CHAIN_FROM_ENDPOINT) root_to_upds.append("--append %s --jump DROP" % CHAIN_TO_ENDPOINT) chains_to_delete = self.programmed_leaf_chains - new_leaf_chains return chains_to_delete, dependencies, updates, new_leaf_chains
def _get_endpoint_rules(endpoint_id, suffix, ip_version, local_ips, mac, profile_ids): to_chain_name, from_chain_name = chain_names(suffix) # First build the chain that manages packets to the interface. # Common chain prefixes. Allow IPv6 ICMP and conntrack rules. to_chain = ["--flush %s" % to_chain_name] if ip_version == 6: # In ipv6 only, there are 6 rules that need to be created first. # RETURN ipv6-icmp anywhere anywhere ipv6-icmptype 130 # RETURN ipv6-icmp anywhere anywhere ipv6-icmptype 131 # RETURN ipv6-icmp anywhere anywhere ipv6-icmptype 132 # RETURN ipv6-icmp anywhere anywhere ipv6-icmp router-advertisement # RETURN ipv6-icmp anywhere anywhere ipv6-icmp neighbour-solicitation # RETURN ipv6-icmp anywhere anywhere ipv6-icmp neighbour-advertisement # # These rules are ICMP types 130, 131, 132, 134, 135 and 136. for icmp_type in ["130", "131", "132", "134", "135", "136"]: to_chain.append("--append %s --jump RETURN " "--protocol ipv6-icmp " "--icmpv6-type %s" % (to_chain_name, icmp_type)) to_chain.append("--append %s --match conntrack --ctstate INVALID " "--jump DROP" % to_chain_name) to_chain.append("--append %s --match conntrack " "--ctstate RELATED,ESTABLISHED --jump RETURN" % to_chain_name) # Jump to each profile in turn. The profile will do one of the # following: # * DROP the packet; in which case we won't see it again. # * RETURN without MARKing the packet; indicates that the packet was # accepted. # * RETURN with a mark on the packet, indicates reaching the end # of the chain. to_deps = set() for profile_id in profile_ids: profile_in_chain = profile_to_chain_name("inbound", profile_id) to_deps.add(profile_in_chain) to_chain.append("--append %s --jump MARK --set-mark 0" % to_chain_name) to_chain.append("--append %s --jump %s" % (to_chain_name, profile_in_chain)) to_chain.append('--append %s --match mark ! --mark 1/1 ' '--match comment --comment "No mark means profile ' 'accepted packet" --jump RETURN' % to_chain_name) # Default drop rule. to_chain.append(commented_drop_fragment(to_chain_name, "Endpoint %s:" % endpoint_id)) # Now the chain that manages packets from the interface... from_chain = ["--flush %s" % from_chain_name] if ip_version == 6: # In ipv6 only, allow all ICMP traffic from this endpoint to anywhere. from_chain.append("--append %s --protocol ipv6-icmp --jump RETURN" % from_chain_name) # Conntrack rules. from_chain.append("--append %s --match conntrack --ctstate INVALID " "--jump DROP" % from_chain_name) from_chain.append("--append %s --match conntrack " "--ctstate RELATED,ESTABLISHED --jump RETURN" % from_chain_name) if ip_version == 4: from_chain.append("--append %s --protocol udp --sport 68 --dport 67 " "--jump RETURN" % from_chain_name) else: assert ip_version == 6 from_chain.append("--append %s --protocol udp --sport 546 --dport 547 " "--jump RETURN" % from_chain_name) # Combined anti-spoofing and jump to profile rules. The only way to # get to a profile chain is to have the correct IP and MAC address. from_deps = set() for profile_id in profile_ids: profile_out_chain = profile_to_chain_name("outbound", profile_id) from_deps.add(profile_out_chain) from_chain.append("--append %s --jump MARK --set-mark 0" % from_chain_name) for ip in local_ips: if "/" in ip: cidr = ip else: cidr = "%s/32" % ip if ip_version == 4 else "%s/128" % ip # Jump to each profile in turn. The profile will do one of the # following: # * DROP the packet; in which case we won't see it again. # * RETURN without MARKing the packet; indicates that the packet # was accepted. # * RETURN with a mark on the packet, indicates reaching the end # of the chain. from_chain.append("--append %s --src %s --match mac " "--mac-source %s --jump %s" % (from_chain_name, cidr, mac.upper(), profile_out_chain)) from_chain.append('--append %s --match mark ! --mark 1/1 ' '--match comment --comment "No mark means profile ' 'accepted packet" --jump RETURN' % from_chain_name) # Final default DROP if no profile RETURNed or no MAC matched. drop_frag = commented_drop_fragment(from_chain_name, "Default DROP if no match (endpoint %s):" % endpoint_id) from_chain.append(drop_frag) updates = {to_chain_name: to_chain, from_chain_name: from_chain} deps = {to_chain_name: to_deps, from_chain_name: from_deps} return updates, deps
def _get_endpoint_rules(endpoint_id, suffix, ip_version, local_ips, mac, profile_ids): to_chain_name, from_chain_name = chain_names(suffix) # First build the chain that manages packets to the interface. # Common chain prefixes. to_chain = [] # Jump to each profile in turn. The profile will do one of the # following: # * DROP the packet; in which case we won't see it again. # * RETURN without MARKing the packet; indicates that the packet was # accepted. # * RETURN with a mark on the packet, indicates reaching the end # of the chain. to_deps = set() for profile_id in profile_ids: profile_in_chain = profile_to_chain_name("inbound", profile_id) to_deps.add(profile_in_chain) to_chain.append("--append %s --jump MARK --set-mark 0" % to_chain_name) to_chain.append("--append %s --jump %s" % (to_chain_name, profile_in_chain)) to_chain.append('--append %s --match mark ! --mark 1/1 ' '--match comment --comment "No mark means profile ' 'accepted packet" --jump RETURN' % to_chain_name) # Default drop rule. to_chain.append(commented_drop_fragment(to_chain_name, "Endpoint %s:" % endpoint_id)) # Now the chain that manages packets from the interface... from_chain = [] # Combined anti-spoofing and jump to profile rules. The only way to # get to a profile chain is to have the correct IP and MAC address. from_deps = set() for profile_id in profile_ids: profile_out_chain = profile_to_chain_name("outbound", profile_id) from_deps.add(profile_out_chain) from_chain.append("--append %s --jump MARK --set-mark 0" % from_chain_name) for ip in local_ips: if "/" in ip: cidr = ip else: cidr = "%s/32" % ip if ip_version == 4 else "%s/128" % ip # Jump to each profile in turn. The profile will do one of the # following: # * DROP the packet; in which case we won't see it again. # * RETURN without MARKing the packet; indicates that the packet # was accepted. # * RETURN with a mark on the packet, indicates reaching the end # of the chain. from_chain.append("--append %s --src %s --match mac " "--mac-source %s --jump %s" % (from_chain_name, cidr, mac.upper(), profile_out_chain)) from_chain.append('--append %s --match mark ! --mark 1/1 ' '--match comment --comment "No mark means profile ' 'accepted packet" --jump RETURN' % from_chain_name) # Final default DROP if no profile RETURNed or no MAC matched. drop_frag = commented_drop_fragment(from_chain_name, "Default DROP if no match (endpoint %s):" % endpoint_id) from_chain.append(drop_frag) updates = {to_chain_name: to_chain, from_chain_name: from_chain} deps = {to_chain_name: to_deps, from_chain_name: from_deps} return updates, deps
def _get_endpoint_rules(endpoint_id, suffix, ip_version, local_ips, mac, profile_ids): to_chain_name, from_chain_name = chain_names(suffix) # First build the chain that manages packets to the interface. # Common chain prefixes. to_chain = [] # Jump to each profile in turn. The profile will do one of the # following: # * DROP the packet; in which case we won't see it again. # * RETURN without MARKing the packet; indicates that the packet was # accepted. # * RETURN with a mark on the packet, indicates reaching the end # of the chain. to_deps = set() for profile_id in profile_ids: profile_in_chain = profile_to_chain_name("inbound", profile_id) to_deps.add(profile_in_chain) to_chain.append("--append %s --jump MARK --set-mark 0" % to_chain_name) to_chain.append("--append %s --jump %s" % (to_chain_name, profile_in_chain)) to_chain.append('--append %s --match mark ! --mark 1/1 ' '--match comment --comment "No mark means profile ' 'accepted packet" --jump RETURN' % to_chain_name) # Default drop rule. to_chain.append( commented_drop_fragment(to_chain_name, "Endpoint %s:" % endpoint_id)) # Now the chain that manages packets from the interface... from_chain = [] # Combined anti-spoofing and jump to profile rules. The only way to # get to a profile chain is to have the correct IP and MAC address. from_deps = set() for profile_id in profile_ids: profile_out_chain = profile_to_chain_name("outbound", profile_id) from_deps.add(profile_out_chain) from_chain.append("--append %s --jump MARK --set-mark 0" % from_chain_name) for ip in local_ips: if "/" in ip: cidr = ip else: cidr = "%s/32" % ip if ip_version == 4 else "%s/128" % ip # Jump to each profile in turn. The profile will do one of the # following: # * DROP the packet; in which case we won't see it again. # * RETURN without MARKing the packet; indicates that the packet # was accepted. # * RETURN with a mark on the packet, indicates reaching the end # of the chain. from_chain.append( "--append %s --src %s --match mac " "--mac-source %s --jump %s" % (from_chain_name, cidr, mac.upper(), profile_out_chain)) from_chain.append('--append %s --match mark ! --mark 1/1 ' '--match comment --comment "No mark means profile ' 'accepted packet" --jump RETURN' % from_chain_name) # Final default DROP if no profile RETURNed or no MAC matched. drop_frag = commented_drop_fragment( from_chain_name, "Default DROP if no match (endpoint %s):" % endpoint_id) from_chain.append(drop_frag) updates = {to_chain_name: to_chain, from_chain_name: from_chain} deps = {to_chain_name: to_deps, from_chain_name: from_deps} return updates, deps
def _get_endpoint_rules(endpoint_id, suffix, ip_version, local_ips, mac, profile_id): to_chain_name, from_chain_name = chain_names(suffix) to_chain = ["--flush %s" % to_chain_name] if ip_version == 6: # In ipv6 only, there are 6 rules that need to be created first. # RETURN ipv6-icmp anywhere anywhere ipv6-icmptype 130 # RETURN ipv6-icmp anywhere anywhere ipv6-icmptype 131 # RETURN ipv6-icmp anywhere anywhere ipv6-icmptype 132 # RETURN ipv6-icmp anywhere anywhere ipv6-icmp router-advertisement # RETURN ipv6-icmp anywhere anywhere ipv6-icmp neighbour-solicitation # RETURN ipv6-icmp anywhere anywhere ipv6-icmp neighbour-advertisement # # These rules are ICMP types 130, 131, 132, 134, 135 and 136, and can # be created on the command line with something like : # ip6tables -A plw -j RETURN --protocol ipv6-icmp --icmpv6-type 130 for icmp_type in ["130", "131", "132", "134", "135", "136"]: to_chain.append("--append %s --jump RETURN " "--protocol ipv6-icmp " "--icmpv6-type %s" % (to_chain_name, icmp_type)) to_chain.append("--append %s --match conntrack --ctstate INVALID " "--jump DROP" % to_chain_name) to_chain.append("--append %s --match conntrack " "--ctstate RELATED,ESTABLISHED --jump RETURN" % to_chain_name) assert profile_id, "Profile ID should be set, not %s" % profile_id profile_in_chain = profile_to_chain_name("inbound", profile_id) to_chain.append("--append %s --goto %s" % (to_chain_name, profile_in_chain)) # This drop rule is not hittable, but it gives us a place to stash the # comment with our ID. to_chain.append(commented_drop_fragment(to_chain_name, "Endpoint %s:" % endpoint_id)) to_deps = set([profile_in_chain]) # Now the chain that manages packets from the interface... from_chain = ["--flush %s" % from_chain_name] if ip_version == 6: # In ipv6 only, allows all ICMP traffic from this endpoint to anywhere. from_chain.append("--append %s --protocol ipv6-icmp --jump RETURN" % from_chain_name) # Conntrack rules. from_chain.append("--append %s --match conntrack --ctstate INVALID " "--jump DROP" % from_chain_name) from_chain.append("--append %s --match conntrack " "--ctstate RELATED,ESTABLISHED --jump RETURN" % from_chain_name) if ip_version == 4: from_chain.append("--append %s --protocol udp --sport 68 --dport 67 " "--jump RETURN" % from_chain_name) else: assert ip_version == 6 from_chain.append("--append %s --protocol udp --sport 546 --dport 547 " "--jump RETURN" % from_chain_name) # Anti-spoofing rules. Only allow traffic from known (IP, MAC) pairs to # get to the profile chain, drop other traffic. profile_out_chain = profile_to_chain_name("outbound", profile_id) from_deps = set([profile_out_chain]) for ip in local_ips: if "/" in ip: cidr = ip else: cidr = "%s/32" % ip if ip_version == 4 else "%s/128" % ip # Note use of --goto rather than --jump; this means that when the # profile chain returns, it will return the chain that called us, not # this chain. from_chain.append("--append %s --src %s --match mac --mac-source %s " "--goto %s" % (from_chain_name, cidr, mac.upper(), profile_out_chain)) drop_frag = commented_drop_fragment(from_chain_name, "Anti-spoof DROP (endpoint %s):" % endpoint_id) from_chain.append(drop_frag) updates = {to_chain_name: to_chain, from_chain_name: from_chain} deps = {to_chain_name: to_deps, from_chain_name: from_deps} return updates, deps
def _calculate_update(self, ifaces): """ Calculates the iptables update to rewrite our chains. To avoid traversing lots of dispatch rules to find the right one, we build a tree of chains. Currently, the tree can only be two layers deep: a root chain and a layer of leaves. Interface names look like this: "prefix1234abc". The "prefix" part is always the same so we ignore it. We call "1234abc", the "suffix". The root chain contains two sorts of rules: * where there are multiple interfaces whose suffixes start with the same character, it contains a rule that matches on that prefix of the "suffix"(!) and directs the packet to a leaf chain for that prefix. * as an optimization, if there is only one interface whose suffix starts with a given character, it contains a dispatch rule for that exact interface name. For example, if we have interface names "tapA1" "tapB1" "tapB2", we'll get (in pseudo code): Root chain: if interface=="tapA1" then goto chain for endpoint tapA1 if interface.startswith("tapB") then goto leaf chain for prefix "tapB" tapB leaf chain: if interface=="tapB1" then goto chain for endpoint tapB1 if interface=="tapB2" then goto chain for endpoint tapB2 :param set[str] ifaces: The list of interfaces to generate a dispatch chain for. :returns Tuple: to_delete, deps, updates, new_leaf_chains: * set of leaf chains that are no longer needed for deletion * chain dependency dict. * chain updates dict. * complete set of leaf chains that are now required. """ # iptables update fragments/dependencies for the root chains. updates = defaultdict(list) root_to_upds = updates[CHAIN_TO_ENDPOINT] root_from_upds = updates[CHAIN_FROM_ENDPOINT] dependencies = defaultdict(set) root_to_deps = dependencies[CHAIN_TO_ENDPOINT] root_from_deps = dependencies[CHAIN_FROM_ENDPOINT] # Separate the interface names by their prefixes so we can count them # and decide whether to program a leaf chain or not. interfaces_by_prefix = defaultdict(set) for iface in ifaces: ep_suffix = interface_to_suffix(self.config, iface) prefix = ep_suffix[:1] interfaces_by_prefix[prefix].add(iface) # Spin through the interfaces by prefix. Either add them directly # to the root chain or create a leaf and add them there. new_leaf_chains = set() for prefix, interfaces in interfaces_by_prefix.iteritems(): use_root_chain = len(interfaces) == 1 if use_root_chain: # Optimization: there's only one interface with this prefix, # don't program a leaf chain. disp_to_chain = CHAIN_TO_ENDPOINT disp_from_chain = CHAIN_FROM_ENDPOINT to_deps = root_to_deps from_deps = root_from_deps to_upds = root_to_upds from_upds = root_from_upds else: # There's more than one interface with this prefix, program # a leaf chain. disp_to_chain = CHAIN_TO_LEAF + "-" + prefix disp_from_chain = CHAIN_FROM_LEAF + "-" + prefix to_upds = updates[disp_to_chain] from_upds = updates[disp_from_chain] to_deps = dependencies[disp_to_chain] from_deps = dependencies[disp_from_chain] new_leaf_chains.add(disp_from_chain) new_leaf_chains.add(disp_to_chain) # Root chain depends on its leaves. root_from_deps.add(disp_to_chain) root_to_deps.add(disp_from_chain) # Point root chain at prefix chain. iface_match = self.config.IFACE_PREFIX + prefix + "+" root_from_upds.append( "--append %s --in-interface %s --goto %s" % (CHAIN_FROM_ENDPOINT, iface_match, disp_from_chain) ) root_to_upds.append( "--append %s --out-interface %s --goto %s" % (CHAIN_TO_ENDPOINT, iface_match, disp_to_chain) ) # Common processing, add the per-endpoint rules to whichever # chain we decided above. for iface in interfaces: # Add rule to leaf or global chain to direct traffic to the # endpoint-specific one. Note that we use --goto, which means # that the endpoint-specific chain will return to our parent # rather than to this chain. ep_suffix = interface_to_suffix(self.config, iface) to_chain_name, from_chain_name = chain_names(ep_suffix) from_upds.append("--append %s --in-interface %s --goto %s" % (disp_from_chain, iface, from_chain_name)) from_deps.add(from_chain_name) to_upds.append("--append %s --out-interface %s --goto %s" % (disp_to_chain, iface, to_chain_name)) to_deps.add(to_chain_name) if not use_root_chain: # Add a default drop to the end of the leaf chain. from_upds.append("--append %s --jump DROP" % disp_from_chain) to_upds.append("--append %s --jump DROP" % disp_to_chain) # Both TO and FROM chains end with a DROP so that interfaces that # we don't know about yet can't bypass our rules. root_from_upds.append("--append %s --jump DROP" % CHAIN_FROM_ENDPOINT) root_to_upds.append("--append %s --jump DROP" % CHAIN_TO_ENDPOINT) chains_to_delete = self.programmed_leaf_chains - new_leaf_chains return chains_to_delete, dependencies, updates, new_leaf_chains