Esempio n. 1
0
    def _apply_endpoint_update(self):
        pending_endpoint = self._pending_endpoint
        if pending_endpoint == self.endpoint:
            _log.debug("Endpoint hasn't changed, nothing to do")
            return

        if pending_endpoint:
            # Update/create.
            if pending_endpoint['mac'] != self._mac:
                # Either we have not seen this MAC before, or it has changed.
                _log.debug("Endpoint MAC changed to %s",
                           pending_endpoint["mac"])
                self._mac = pending_endpoint['mac']
                self._mac_changed = True
                # MAC change requires refresh of iptables rules and ARP table.
                self._iptables_in_sync = False
                self._device_in_sync = False

            if self.endpoint is None:
                # This is the first time we have seen the endpoint, so extract
                # the interface name and endpoint ID.
                self._iface_name = pending_endpoint["name"]
                self._suffix = interface_to_suffix(self.config,
                                                   self._iface_name)
                _log.debug("Learned interface name/suffix: %s/%s",
                           self._iface_name, self._suffix)
                # First time through, need to program everything.
                self._iptables_in_sync = False
                self._device_in_sync = False

            # Check if the profile ID or IP addresses have changed, requiring
            # a refresh of the dataplane.
            profile_ids = set(pending_endpoint.get("profile_ids", []))
            if profile_ids != self.rules_ref_helper.required_refs:
                # Profile ID update required iptables update but not device
                # update.
                _log.debug("Profile IDs changed, need to update iptables")
                self._iptables_in_sync = False
            if (self.endpoint and
                    (self.endpoint[self.nets_key] !=
                     pending_endpoint[self.nets_key])):
                # IP addresses have changed, need to update the routing table.
                _log.debug("IP addresses changed, need to update routing")
                self._device_in_sync = False
        else:
            # Delete of the endpoint.  Need to resync everything.
            profile_ids = set()
            self._iptables_in_sync = False
            self._device_in_sync = False

        # Note: we don't actually need to wait for the activation to finish
        # due to the dependency management in the iptables layer.
        self.rules_ref_helper.replace_all(profile_ids)

        self.endpoint = pending_endpoint
        self._endpoint_update_pending = False
        self._pending_endpoint = None
Esempio n. 2
0
    def on_endpoint_update(self, endpoint):
        """
        Called when this endpoint has received an update.
        :param dict[str] endpoint: endpoint parameter dictionary.
        """
        _log.info("%s updated: %s", self, endpoint)
        mac_changed = False

        if not endpoint and not self.endpoint:
            # First time we have been called, but it's a delete! Maybe some
            # odd timing window, but we have nothing to tidy up.
            return

        if endpoint and endpoint['mac'] != self._mac:
            # Either we have not seen this MAC before, or it has changed.
            self._mac = endpoint['mac']
            mac_changed = True

        if endpoint and not self.endpoint:
            # This is the first time we have seen the endpoint, so extract the
            # interface name and endpoint ID.
            self._iface_name = endpoint["name"]
            self._suffix = interface_to_suffix(self.config,
                                               self._iface_name)

        was_ready = self._ready

        # Activate the required profile IDs (and deactivate any that we no
        # longer need).
        if endpoint:
            new_profile_ids = set(endpoint["profile_ids"])
        else:
            new_profile_ids = set()
        # Note: we don't actually need to wait for the activation to finish
        # due to the dependency management in the iptables layer.
        self.rules_ref_helper.replace_all(new_profile_ids)

        if endpoint != self.endpoint:
            self._dirty = True

        # Store off the endpoint we were passed.
        self.endpoint = endpoint

        if endpoint:
            # Configure the network interface; may fail if not there yet (in
            # which case we'll just do it when the interface comes up).
            self._configure_interface(mac_changed)
        else:
            # Remove the network programming.
            self._deconfigure_interface()

        self._maybe_update(was_ready)
        _log.debug("%s finished processing update", self)
Esempio n. 3
0
    def on_endpoint_update(self, endpoint, force_reprogram=False):
        """
        Called when this endpoint has received an update.
        :param dict[str] endpoint: endpoint parameter dictionary.
        """
        _log.info("%s updated: %s", self, endpoint)
        mac_changed = False

        if not endpoint and not self.endpoint:
            # First time we have been called, but it's a delete! Maybe some
            # odd timing window, but we have nothing to tidy up.
            return

        if endpoint and endpoint['mac'] != self._mac:
            # Either we have not seen this MAC before, or it has changed.
            self._mac = endpoint['mac']
            mac_changed = True

        if endpoint and not self.endpoint:
            # This is the first time we have seen the endpoint, so extract the
            # interface name and endpoint ID.
            self._iface_name = endpoint["name"]
            self._suffix = interface_to_suffix(self.config, self._iface_name)

        was_ready = self._ready

        # Activate the required profile IDs (and deactivate any that we no
        # longer need).
        if endpoint:
            new_profile_ids = set(endpoint["profile_ids"])
        else:
            new_profile_ids = set()
        # Note: we don't actually need to wait for the activation to finish
        # due to the dependency management in the iptables layer.
        self.rules_ref_helper.replace_all(new_profile_ids)

        if endpoint != self.endpoint or force_reprogram:
            self._dirty = True

        # Store off the endpoint we were passed.
        self.endpoint = endpoint

        if endpoint:
            # Configure the network interface; may fail if not there yet (in
            # which case we'll just do it when the interface comes up).
            self._configure_interface(mac_changed)
        else:
            # Remove the network programming.
            self._deconfigure_interface()

        self._maybe_update(was_ready)
        _log.debug("%s finished processing update", self)
Esempio n. 4
0
    def on_endpoint_update(self, endpoint):
        """
        Called when this endpoint has received an update.
        :param dict[str] endpoint: endpoint parameter dictionary.
        """
        _log.info("%s updated: %s", self, endpoint)
        if endpoint and not self.endpoint:
            # This is the first time we have seen the endpoint, so extract the
            # interface name and endpoint ID.
            self._iface_name = endpoint["name"]
            self._suffix = interface_to_suffix(self.config,
                                               self._iface_name)
        was_ready = self._ready

        old_profile_id = self.endpoint and self.endpoint["profile_id"]
        new_profile_id = endpoint and endpoint["profile_id"]
        if old_profile_id != new_profile_id:
            if old_profile_id:
                # Clean up the old profile.
                _log.info("Profile changed, decreffing old profile %s",
                          old_profile_id)
                self.rules_mgr.decref(old_profile_id, async=True)
            if new_profile_id is not None:
                _log.info("Acquiring new profile %s", new_profile_id)
                self.rules_mgr.get_and_incref(new_profile_id, async=True)

        if endpoint != self.endpoint:
            self._dirty = True

        # Store off the endpoint we were passed.
        self.endpoint = endpoint

        if endpoint:
            # Configure the network interface; may fail if not there yet (in
            # which case we'll just do it when the interface comes up).
            self._configure_interface()
        else:
            # Remove the network programming.
            self._deconfigure_interface()

        self._maybe_update(was_ready)
        _log.debug("%s finished processing update", self)
Esempio n. 5
0
    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
Esempio n. 6
0
    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 = CHAIN_TO_PREFIX + ep_suffix
                from_chain_name = CHAIN_FROM_PREFIX + 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.
                # Add a default drop to the end of the leaf chain.
                from_upds.extend(
                    self.iptables_generator.drop_rules(
                        self.ip_version, disp_from_chain, None,
                        "From unknown endpoint"))
                to_upds.extend(
                    self.iptables_generator.drop_rules(self.ip_version,
                                                       disp_to_chain, None,
                                                       "To unknown endpoint"))

        # 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.extend(
            self.iptables_generator.drop_rules(self.ip_version,
                                               CHAIN_FROM_ENDPOINT, None,
                                               "From unknown endpoint"))
        root_to_upds.extend(
            self.iptables_generator.drop_rules(self.ip_version,
                                               CHAIN_TO_ENDPOINT, None,
                                               "To unknown endpoint"))

        chains_to_delete = self.programmed_leaf_chains - new_leaf_chains

        return chains_to_delete, dependencies, updates, new_leaf_chains
Esempio n. 7
0
    def _apply_endpoint_update(self):
        pending_endpoint = self._pending_endpoint
        if pending_endpoint == self.endpoint:
            _log.debug("Endpoint hasn't changed, nothing to do")
            return

        # Calculate the set of IPs that we had before this update.  Needed on
        # the update and delete code paths below.
        if self.endpoint:
            old_ips = set(futils.net_to_ip(n) for n in
                          self.endpoint.get(self.nets_key, []))
            old_nat_mappings = self.endpoint.get(self.nat_key, [])
        else:
            old_ips = set()
            old_nat_mappings = []
        all_old_ips = old_ips | set([n["ext_ip"] for n in old_nat_mappings])

        if pending_endpoint:
            # Update/create.
            if pending_endpoint['mac'] != self._mac:
                # Either we have not seen this MAC before, or it has changed.
                _log.debug("Endpoint MAC changed to %s",
                           pending_endpoint["mac"])
                self._mac = pending_endpoint['mac']
                self._mac_changed = True
                # MAC change requires refresh of iptables rules and ARP table.
                self._iptables_in_sync = False
                self._device_in_sync = False

            if self.endpoint is None:
                # This is the first time we have seen the endpoint, so extract
                # the interface name and endpoint ID.
                self._iface_name = pending_endpoint["name"]
                self._suffix = interface_to_suffix(self.config,
                                                   self._iface_name)
                _log.debug("Learned interface name/suffix: %s/%s",
                           self._iface_name, self._suffix)
                # First time through, need to program everything.
                self._iptables_in_sync = False
                self._device_in_sync = False
                if self._device_is_up is None:
                    _log.debug("Learned interface name, checking if device "
                               "is up.")
                    self._device_is_up = (
                        devices.interface_exists(self._iface_name) and
                        devices.interface_up(self._iface_name)
                    )

            # Check if the profile ID or IP addresses have changed, requiring
            # a refresh of the dataplane.
            profile_ids = set(pending_endpoint.get("profile_ids", []))
            if profile_ids != self._explicit_profile_ids:
                # Profile ID update requires iptables update but not device
                # update.
                _log.debug("Profile IDs changed from %s to %s, need to update "
                           "iptables", self._rules_ref_helper.required_refs,
                           profile_ids)
                self._explicit_profile_ids = profile_ids
                self._iptables_in_sync = False
                self._profile_ids_dirty = True

            # Check for changes to values that require a device update.
            if self.endpoint:
                if self.endpoint.get("state") != pending_endpoint.get("state"):
                    _log.debug("Desired interface state updated.")
                    self._device_in_sync = False
                    self._iptables_in_sync = False
                new_ips = set(futils.net_to_ip(n) for n in
                              pending_endpoint.get(self.nets_key, []))
                if old_ips != new_ips:
                    # IP addresses have changed, need to update the routing
                    # table.
                    _log.debug("IP addresses changed, need to update routing")
                    self._device_in_sync = False
                new_nat_mappings = pending_endpoint.get(self.nat_key, [])
                if old_nat_mappings != new_nat_mappings:
                    _log.debug("NAT mappings have changed, refreshing.")
                    self._device_in_sync = False
                    self._iptables_in_sync = False
                all_new_ips = new_ips | set([n["ext_ip"] for n in
                                             new_nat_mappings])
                if all_old_ips != all_new_ips:
                    # Ensure we clean up any conntrack entries for IPs that
                    # have been removed.
                    _log.debug("Set of all IPs changed from %s to %s",
                               all_old_ips, all_new_ips)
                    self._removed_ips |= all_old_ips
                    self._removed_ips -= all_new_ips
        else:
            # Delete of the endpoint.  Need to resync everything.
            self._profile_ids_dirty = True
            self._iptables_in_sync = False
            self._device_in_sync = False
            self._removed_ips |= all_old_ips

        self.endpoint = pending_endpoint
        self._endpoint_update_pending = False
        self._pending_endpoint = None
Esempio n. 8
0
    def _apply_endpoint_update(self):
        pending_endpoint = self._pending_endpoint
        if pending_endpoint == self.endpoint:
            _log.debug("Endpoint hasn't changed, nothing to do")
            return

        if pending_endpoint:
            # Update/create.
            if pending_endpoint['mac'] != self._mac:
                # Either we have not seen this MAC before, or it has changed.
                _log.debug("Endpoint MAC changed to %s",
                           pending_endpoint["mac"])
                self._mac = pending_endpoint['mac']
                self._mac_changed = True
                # MAC change requires refresh of iptables rules and ARP table.
                self._iptables_in_sync = False
                self._device_in_sync = False

            if self.endpoint is None:
                # This is the first time we have seen the endpoint, so extract
                # the interface name and endpoint ID.
                self._iface_name = pending_endpoint["name"]
                self._suffix = interface_to_suffix(self.config,
                                                   self._iface_name)
                _log.debug("Learned interface name/suffix: %s/%s",
                           self._iface_name, self._suffix)
                # First time through, need to program everything.
                self._iptables_in_sync = False
                self._device_in_sync = False
                if self._device_is_up is None:
                    _log.debug("Learned interface name, checking if device "
                               "is up.")
                    self._device_is_up = (
                        devices.interface_exists(self._iface_name)
                        and devices.interface_up(self._iface_name))

            # Check if the profile ID or IP addresses have changed, requiring
            # a refresh of the dataplane.
            profile_ids = set(pending_endpoint.get("profile_ids", []))
            if profile_ids != self.rules_ref_helper.required_refs:
                # Profile ID update required iptables update but not device
                # update.
                _log.debug("Profile IDs changed, need to update iptables")
                self._iptables_in_sync = False

            # Check for changes to values that require a device update.
            if self.endpoint:
                if self.endpoint.get("state") != pending_endpoint.get("state"):
                    _log.debug("Desired interface state updated.")
                    self._device_in_sync = False
                    self._iptables_in_sync = False
                if (self.endpoint[self.nets_key] !=
                        pending_endpoint[self.nets_key]):
                    # IP addresses have changed, need to update the routing
                    # table.
                    _log.debug("IP addresses changed, need to update routing")
                    self._device_in_sync = False
                for key in "ipv4_nat", "ipv6_nat":
                    if (self.endpoint.get(key, None) != pending_endpoint.get(
                            key, None)):
                        _log.debug("NAT mappings have changed, refreshing.")
                        self._device_in_sync = False
                        self._iptables_in_sync = False
        else:
            # Delete of the endpoint.  Need to resync everything.
            profile_ids = set()
            self._iptables_in_sync = False
            self._device_in_sync = False

        # Note: we don't actually need to wait for the activation to finish
        # due to the dependency management in the iptables layer.
        self.rules_ref_helper.replace_all(profile_ids)

        self.endpoint = pending_endpoint
        self._endpoint_update_pending = False
        self._pending_endpoint = None
Esempio n. 9
0
    def _apply_endpoint_update(self):
        pending_endpoint = self._pending_endpoint
        if pending_endpoint == self.endpoint:
            _log.debug("Endpoint hasn't changed, nothing to do")
            return

        # Calculate the set of IPs that we had before this update.  Needed on
        # the update and delete code paths below.
        if self.endpoint:
            old_ips = set(
                futils.net_to_ip(n)
                for n in self.endpoint.get(self.nets_key, []))
            old_nat_mappings = self.endpoint.get(self.nat_key, [])
        else:
            old_ips = set()
            old_nat_mappings = []
        all_old_ips = old_ips | set([n["ext_ip"] for n in old_nat_mappings])

        if pending_endpoint:
            # Update/create.
            if pending_endpoint['mac'] != self._mac:
                # Either we have not seen this MAC before, or it has changed.
                _log.debug("Endpoint MAC changed to %s",
                           pending_endpoint["mac"])
                self._mac = pending_endpoint['mac']
                self._mac_changed = True
                # MAC change requires refresh of iptables rules and ARP table.
                self._iptables_in_sync = False
                self._device_in_sync = False

            if self.endpoint is None:
                # This is the first time we have seen the endpoint, so extract
                # the interface name and endpoint ID.
                self._iface_name = pending_endpoint["name"]
                self._suffix = interface_to_suffix(self.config,
                                                   self._iface_name)
                _log.debug("Learned interface name/suffix: %s/%s",
                           self._iface_name, self._suffix)
                # First time through, need to program everything.
                self._iptables_in_sync = False
                self._device_in_sync = False
                if self._device_is_up is None:
                    _log.debug("Learned interface name, checking if device "
                               "is up.")
                    self._device_is_up = (
                        devices.interface_exists(self._iface_name)
                        and devices.interface_up(self._iface_name))

            # Check if the profile ID or IP addresses have changed, requiring
            # a refresh of the dataplane.
            profile_ids = set(pending_endpoint.get("profile_ids", []))
            if profile_ids != self._explicit_profile_ids:
                # Profile ID update requires iptables update but not device
                # update.
                _log.debug(
                    "Profile IDs changed from %s to %s, need to update "
                    "iptables", self._rules_ref_helper.required_refs,
                    profile_ids)
                self._explicit_profile_ids = profile_ids
                self._iptables_in_sync = False
                self._profile_ids_dirty = True

            # Check for changes to values that require a device update.
            if self.endpoint:
                if self.endpoint.get("state") != pending_endpoint.get("state"):
                    _log.debug("Desired interface state updated.")
                    self._device_in_sync = False
                    self._iptables_in_sync = False
                new_ips = set(
                    futils.net_to_ip(n)
                    for n in pending_endpoint.get(self.nets_key, []))
                if old_ips != new_ips:
                    # IP addresses have changed, need to update the routing
                    # table.
                    _log.debug("IP addresses changed, need to update routing")
                    self._device_in_sync = False
                new_nat_mappings = pending_endpoint.get(self.nat_key, [])
                if old_nat_mappings != new_nat_mappings:
                    _log.debug("NAT mappings have changed, refreshing.")
                    self._device_in_sync = False
                    self._iptables_in_sync = False
                all_new_ips = new_ips | set(
                    [n["ext_ip"] for n in new_nat_mappings])
                if all_old_ips != all_new_ips:
                    # Ensure we clean up any conntrack entries for IPs that
                    # have been removed.
                    _log.debug("Set of all IPs changed from %s to %s",
                               all_old_ips, all_new_ips)
                    self._removed_ips |= all_old_ips
                    self._removed_ips -= all_new_ips
        else:
            # Delete of the endpoint.  Need to resync everything.
            self._profile_ids_dirty = True
            self._iptables_in_sync = False
            self._device_in_sync = False
            self._removed_ips |= all_old_ips

        self.endpoint = pending_endpoint
        self._endpoint_update_pending = False
        self._pending_endpoint = None