Example #1
0
    def test_list_interface_ips(self):
        type = futils.IPV4
        tap = "tap" + str(uuid.uuid4())[:11]

        retcode = futils.CommandOutput("", "")
        with mock.patch('calico.felix.futils.check_call',
                        return_value=retcode):
            ips = devices.list_interface_ips(type, tap)
            futils.check_call.assert_called_once_with(
                ["ip", "route", "list", "dev", tap])
            self.assertFalse(ips)

        stdout = "10.11.9.90  scope link"
        retcode = futils.CommandOutput(stdout, "")
        with mock.patch('calico.felix.futils.check_call',
                        return_value=retcode):
            ips = devices.list_interface_ips(type, tap)
            futils.check_call.assert_called_once_with(
                ["ip", "route", "list", "dev", tap])
            self.assertEqual(ips, set(["10.11.9.90"]))

        stdout = "10.11.9.90  scope link\nblah-di-blah not valid\nx\n"
        retcode = futils.CommandOutput(stdout, "")
        with mock.patch('calico.felix.futils.check_call',
                        return_value=retcode):
            ips = devices.list_interface_ips(type, tap)
            futils.check_call.assert_called_once_with(
                ["ip", "route", "list", "dev", tap])
            self.assertEqual(ips, set(["10.11.9.90"]))

        type = futils.IPV6
        stdout = "2001:: scope link\n"
        retcode = futils.CommandOutput(stdout, "")
        with mock.patch('calico.felix.futils.check_call',
                        return_value=retcode):
            ips = devices.list_interface_ips(type, tap)
            futils.check_call.assert_called_once_with(
                ["ip", "-6", "route", "list", "dev", tap])
            self.assertEqual(ips, set(["2001::"]))

        stdout = "2001:: scope link\n\n"
        retcode = futils.CommandOutput(stdout, "")
        with mock.patch('calico.felix.futils.check_call',
                        return_value=retcode):
            ips = devices.list_interface_ips(type, tap)
            futils.check_call.assert_called_once_with(
                ["ip", "-6", "route", "list", "dev", tap])
            self.assertEqual(ips, set(["2001::"]))
Example #2
0
 def test_list_interface_no_ips(self):
     retcode = futils.CommandOutput(
         "7: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue "
         "state UNKNOWN group default\n"
         "    link/ipip 0.0.0.0 brd 0.0.0.0\n", "")
     with mock.patch('calico.felix.futils.check_call',
                     return_value=retcode) as m_check_call:
         ips = devices.list_interface_ips(futils.IPV4, "tunl0")
         self.assertEqual(
             m_check_call.mock_calls,
             [mock.call(["ip", "addr", "list", "dev", "tunl0"])])
         self.assertEqual(ips, set())
Example #3
0
    def test_list_interface_ips(self):
        type = futils.IPV4
        tap = "tap" + str(uuid.uuid4())[:11]

        retcode = futils.CommandOutput("", "")
        with mock.patch('calico.felix.futils.check_call', return_value=retcode):
            ips = devices.list_interface_ips(type, tap)
            futils.check_call.assert_called_once_with(["ip", "route", "list", "dev", tap])
            self.assertFalse(ips)

        stdout = "10.11.9.90  scope link"
        retcode = futils.CommandOutput(stdout, "")
        with mock.patch('calico.felix.futils.check_call', return_value=retcode):
            ips = devices.list_interface_ips(type, tap)
            futils.check_call.assert_called_once_with(["ip", "route", "list", "dev", tap])
            self.assertEqual(ips, set(["10.11.9.90"]))

        stdout = "10.11.9.90  scope link\nblah-di-blah not valid\nx\n"
        retcode = futils.CommandOutput(stdout, "")
        with mock.patch('calico.felix.futils.check_call', return_value=retcode):
            ips = devices.list_interface_ips(type, tap)
            futils.check_call.assert_called_once_with(["ip", "route", "list", "dev", tap])
            self.assertEqual(ips, set(["10.11.9.90"]))

        type = futils.IPV6
        stdout = "2001:: scope link\n"
        retcode = futils.CommandOutput(stdout, "")
        with mock.patch('calico.felix.futils.check_call', return_value=retcode):
            ips = devices.list_interface_ips(type, tap)
            futils.check_call.assert_called_once_with(["ip", "-6", "route", "list", "dev", tap])
            self.assertEqual(ips, set(["2001::"]))

        stdout = "2001:: scope link\n\n"
        retcode = futils.CommandOutput(stdout, "")
        with mock.patch('calico.felix.futils.check_call', return_value=retcode):
            ips = devices.list_interface_ips(type, tap)
            futils.check_call.assert_called_once_with(["ip", "-6", "route", "list", "dev", tap])
            self.assertEqual(ips, set(["2001::"]))
Example #4
0
 def test_list_interface_no_ips(self):
     retcode = futils.CommandOutput(
         "7: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue "
         "state UNKNOWN group default\n"
         "    link/ipip 0.0.0.0 brd 0.0.0.0\n",
         ""
     )
     with mock.patch('calico.felix.futils.check_call',
                     return_value=retcode) as m_check_call:
         ips = devices.list_interface_ips(futils.IPV4, "tunl0")
         self.assertEqual(
             m_check_call.mock_calls,
             [mock.call(["ip", "addr", "list", "dev", "tunl0"])]
         )
         self.assertEqual(ips, set())
Example #5
0
    def remove(self, iptables_state):
        """
        Delete a programmed endpoint. Remove the routes, then the rules.
        """
        if devices.interface_exists(self.interface):
            for type in (futils.IPV4, futils.IPV6):
                try:
                    ips = devices.list_interface_ips(type, self.interface)
                    for ip in ips:
                        devices.del_route(type, ip, self.interface)
                except futils.FailedSystemCall:
                    # There is a window where the interface gets deleted under
                    # our feet. If it has gone now, ignore the error, otherwise
                    # rethrow it.
                    if devices.interface_exists(self.interface):
                        raise
                    break

        frules.del_rules(iptables_state, self.suffix, futils.IPV4)
        frules.del_rules(iptables_state, self.suffix, futils.IPV6)
Example #6
0
 def test_list_interface_v6_with_ips(self):
     retcode = futils.CommandOutput(
         "7: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue "
         "state UNKNOWN group default\n"
         "    link/ipip 0.0.0.0 brd 0.0.0.0\n"
         "    inet6 5678::/64 brd foobar scope global tunl0\n"
         "    inet6 ABcd::/64 brd foobar scope global tunl0\n"
         # Allow for dotted quad translated v4 addresses, just in case.
         "    inet6 ::ffff:192.0.2.128/128 brd foobar scope global tunl0\n",
         ""
     )
     with mock.patch('calico.felix.futils.check_call',
                     return_value=retcode) as m_check_call:
         ips = devices.list_interface_ips(futils.IPV6, "tunl0")
         self.assertEqual(
             m_check_call.mock_calls,
             [mock.call(["ip", "-6", "addr", "list", "dev", "tunl0"])]
         )
         self.assertEqual(ips, set([IPAddress("5678::"),
                                    IPAddress("abcd::"),
                                    IPAddress("::ffff:c000:0280")]))
Example #7
0
 def test_list_interface_v6_with_ips(self):
     retcode = futils.CommandOutput(
         "7: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue "
         "state UNKNOWN group default\n"
         "    link/ipip 0.0.0.0 brd 0.0.0.0\n"
         "    inet6 5678::/64 brd foobar scope global tunl0\n"
         "    inet6 ABcd::/64 brd foobar scope global tunl0\n"
         # Allow for dotted quad translated v4 addresses, just in case.
         "    inet6 ::ffff:192.0.2.128/128 brd foobar scope global tunl0\n",
         ""
     )
     with mock.patch('calico.felix.futils.check_call',
                     return_value=retcode) as m_check_call:
         ips = devices.list_interface_ips(futils.IPV6, "tunl0")
         self.assertEqual(
             m_check_call.mock_calls,
             [mock.call(["ip", "-6", "addr", "list", "dev", "tunl0"])]
         )
         self.assertEqual(ips, set([IPAddress("5678::"),
                                    IPAddress("abcd::"),
                                    IPAddress("::ffff:c000:0280")]))
Example #8
0
    def program_endpoint(self, iptables_state):
        """
        Given an endpoint, make the programmed state match the desired state,
        setting up rules and creating chains and ipsets, but not putting
        content into the ipsets (leaving that for frules.update_acls).

        Note that if acl_data is none, we have not received any ACLs, and so
        we just leave the ACLs in place until we do. If there are none because
        this is a new endpoint, then we leave the endpoint with all routing
        disabled until we know better.

        The logic here is that we should create the routes and basic rules, but
        not the ACLs - leaving the ACLs as they were or with no access
        permitted if none. That is because we have the information for the
        former (routes and IP addresses for the endpoint) but not the latter
        (ACLs). However this split only makes sense at the point where the ACLs
        must have a default rule of "deny", so when issue39 is fully resolved
        this method should only be called when the ACLs are available too.

        Returns True if the endpoint needs to be retried (because the tap
        interface does not exist yet).
        """

        # Declare some utility functions
        def add_routes(routes, type):
            for route in routes:
                log.info("Add route to %s address %s for interface %s", type,
                         route, self.interface)
                devices.add_route(type, route, self.interface, self.mac)

        def remove_routes(routes, type):
            for route in routes:
                log.info(
                    "Remove extra %s route to address %s for interface %s",
                    type, route, self.interface)
                devices.del_route(type, route, self.interface)

        if not devices.interface_exists(self.interface):
            if self.state == Endpoint.STATE_ENABLED:
                log.error("Unable to configure non-existent interface %s",
                          self.interface)
                return True
            else:
                # No interface, but disabled. This is not an error, and there
                # is nothing to do.
                log.debug("Interface missing when disabling endpoint %s",
                          self.uuid)
                return False

        # If the interface is down, we can't configure it.
        if not devices.interface_up(self.interface):
            log.error("Unable to configure interface %s: interface is down.",
                      self.interface)
            return True

        # Configure the interface.
        if self.state == Endpoint.STATE_ENABLED:
            devices.configure_interface(self.interface)

            # Build up list of addresses that should be present
            ipv4_intended = set([
                addr.ip.encode('ascii') for addr in self.addresses
                if addr.type is futils.IPV4
            ])
            ipv6_intended = set([
                addr.ip.encode('ascii') for addr in self.addresses
                if addr.type is futils.IPV6
            ])
        else:
            # Disabled endpoint; we should remove all the routes.
            ipv4_intended = set()
            ipv6_intended = set()

        ipv4_existing = devices.list_interface_ips(futils.IPV4, self.interface)
        ipv6_existing = devices.list_interface_ips(futils.IPV6, self.interface)

        # Determine the addresses that won't be changed.
        unchanged = ((ipv4_intended & ipv4_existing) |
                     (ipv6_intended & ipv6_existing))

        log.debug("Already got routes for %s for interface %s", unchanged,
                  self.interface)

        #*********************************************************************#
        #* Add and remove routes. Add any route we need but don't have, and  *#
        #* remove any route we have but don't need. These operations are     *#
        #* fast because they operate on sets.                                *#
        #*********************************************************************#
        add_routes(ipv4_intended - ipv4_existing, futils.IPV4)
        add_routes(ipv6_intended - ipv6_existing, futils.IPV6)
        remove_routes(ipv4_existing - ipv4_intended, futils.IPV4)
        remove_routes(ipv6_existing - ipv6_intended, futils.IPV6)

        #*********************************************************************#
        #* Set up the rules for this endpoint, not including ACLs. Note that *#
        #* if the endpoint is disabled, then it has no permitted addresses,  *#
        #* so it cannot send any data.                                       *#
        #*********************************************************************#
        frules.set_ep_specific_rules(iptables_state, self.suffix,
                                     self.interface, futils.IPV4,
                                     ipv4_intended, self.mac)
        frules.set_ep_specific_rules(iptables_state, self.suffix,
                                     self.interface, futils.IPV6,
                                     ipv6_intended, self.mac)

        #*********************************************************************#
        #* If we have just disabled / enabled an endpoint, we may need to    *#
        #* enable / disable incoming traffic. update_acls makes this         *#
        #* decision.                                                         *#
        #*********************************************************************#
        self.update_acls()

        return False