Ejemplo n.º 1
0
    def setUp(self):
        self._br = "t_up_br0"
        try:
            subprocess.check_call(["pkill", "dnsmasq"])
        except subprocess.CalledProcessError:
            pass

        setup_dhcp_server = SCRIPT_PATH + "scripts/setup-test-dhcp-srv.sh"
        subprocess.check_call([setup_dhcp_server, "t0"])

        setup_uplink_br = [SCRIPT_PATH + "scripts/setup-uplink-br.sh",
                           self._br,
                           DHCP_IFACE,
                           "8A:00:00:00:00:01"]
        subprocess.check_call(setup_uplink_br)

        self.dhcp_wait = threading.Condition()
        self.dhcp_store = {}
        self.gw_info_map = {}
        self.gw_info = UplinkGatewayInfo(self.gw_info_map)
        self._dhcp_client = DHCPClient(dhcp_wait=self.dhcp_wait,
                                       dhcp_store=self.dhcp_store,
                                       gw_info=self.gw_info,
                                       iface="t_dhcp0",
                                       lease_renew_wait_min=1)
        self._dhcp_client.run()
Ejemplo n.º 2
0
 def _setup_dhclp_client(self):
     self._dhcp_client = DHCPClient(dhcp_wait=self.dhcp_wait,
                                    dhcp_store=self.dhcp_store,
                                    gw_info=self.gw_info,
                                    iface=DHCP_IFACE,
                                    lease_renew_wait_min=1)
     self._dhcp_client.run()
Ejemplo n.º 3
0
class DhcpClient(unittest.TestCase):
    def setUp(self):
        self._br = "dh_br0"
        self.vlan_sw = "vlan_sw"
        self.up_link_port = ""

        try:
            subprocess.check_call(["pkill", "dnsmasq"])
        except subprocess.CalledProcessError:
            pass

        self.dhcp_wait = threading.Condition()
        self.pkt_list_lock = threading.Condition()
        self.dhcp_store = {}
        self.gw_info_map = {}
        self.gw_info = UplinkGatewayInfo(self.gw_info_map)
        self._sniffer = None
        self._last_xid = -1

    def tearDown(self):
        self._dhcp_client.stop()
        BridgeTools.destroy_bridge(self._br)

    @unittest.skipIf(os.getuid(), reason="needs root user")
    def test_dhcp_lease1(self):
        self._setup_dhcp_vlan_off()

        mac1 = MacAddress("11:22:33:44:55:66")
        self._validate_dhcp_alloc_renew(mac1, None)

    """
    Network diagram of vlan test setup                                    VETH pair        +---------------------+
    ==================================                                                     |  Namespace 1 with   |
                                                                      +--------------------+  DHCP server        |
                                                                      |                    |                     |
                                                                      |                    +---------------------+
                                                                      |
    +----------------+  Patch   +--------------+  uplink-    +--------+-------+            +---------------------+
    |                |  Port    |              |  iface      |                | VETH Pair  |  Namespace 2 with   |
    |  GTP_BR0       +----------+  UPLINK_BR0  +-------------+    VLAN_SW     +------------+  DHCP server        |
    |                |          |              |             |                |            |                     |
    +----------------+          +--------------+             +----------------+            +---------------------+
    """

    @unittest.skipIf(os.getuid(), reason="needs root user")
    def test_dhcp_vlan(self):
        vlan1: int = 2
        mac1 = MacAddress("11:22:33:44:55:66")

        self._setup_vlan_network()
        self._setup_dhcp_on_vlan(vlan1)

        self._validate_dhcp_alloc_renew(mac1, vlan1)
        self._validate_ip_subnet(mac1, vlan1)
        self._release_ip(mac1, vlan1)

    @unittest.skip("needs more investigation.")
    def test_dhcp_vlan_multi(self):
        self._setup_vlan_network()

        vlan1 = 51
        mac1 = MacAddress("11:22:33:44:55:66")
        vlan2 = 52
        mac2 = MacAddress("22:22:33:44:55:66")
        vlan3 = 53
        mac3 = MacAddress("11:22:33:44:55:66")

        self._setup_dhcp_on_vlan(vlan1)
        self._setup_dhcp_on_vlan(vlan2)
        self._setup_dhcp_on_vlan(vlan3)

        self._validate_dhcp_alloc_renew(mac1, vlan1)
        self._validate_dhcp_alloc_renew(mac2, vlan2)
        self._validate_dhcp_alloc_renew(mac3, vlan3)

        self._validate_ip_subnet(mac1, vlan1)
        self._validate_ip_subnet(mac2, vlan2)
        self._validate_ip_subnet(mac3, vlan3)

        self._release_ip(mac1, vlan1)
        self._release_ip(mac2, vlan2)
        self._release_ip(mac3, vlan3)

    def _validate_dhcp_alloc_renew(self, mac1: MacAddress, vlan: int):
        self._alloc_ip_address_from_dhcp(mac1, vlan)
        self._validate_req_state(mac1, DHCPState.REQUEST, vlan)
        self._validate_state_as_current(mac1, vlan)

        # trigger lease reneval before deadline
        self._last_xid = self._get_state_xid(mac1, vlan)
        LOG.debug("time: %s", datetime.datetime.now())
        time1 = datetime.datetime.now() + datetime.timedelta(seconds=100)
        self._start_sniffer()
        with freeze_time(time1):
            LOG.debug("check req packets time: %s", datetime.datetime.now())
            self._stop_sniffer_and_check(DHCPState.REQUEST, mac1, vlan)
            self._validate_req_state(mac1, DHCPState.REQUEST, vlan)
            self._validate_state_as_current(mac1, vlan)

            # trigger lease after deadline
            self._last_xid = self._get_state_xid(mac1, vlan)
            time2 = datetime.datetime.now() + datetime.timedelta(seconds=2000)
            self._start_sniffer()
            LOG.debug(
                "check discover packets time: %s",
                datetime.datetime.now(),
            )
            with freeze_time(time2):
                LOG.debug("check discover after lease loss")
                self._stop_sniffer_and_check(DHCPState.DISCOVER, mac1, vlan)
                self._validate_req_state(mac1, DHCPState.REQUEST, vlan)
                self._validate_state_as_current(mac1, vlan)

    def _setup_dhcp_vlan_off(self):
        self.up_link_port = "cl1uplink_p0"
        setup_dhcp_server = SCRIPT_PATH + "scripts/setup-test-dhcp-srv.sh"
        subprocess.check_call([setup_dhcp_server, "cl1"])

        setup_uplink_br = [
            SCRIPT_PATH + "scripts/setup-uplink-br.sh",
            self._br,
            self.up_link_port,
            DHCP_IFACE,
        ]
        subprocess.check_call(setup_uplink_br)
        self._setup_dhclp_client()

    def _setup_vlan_network(self):
        self.up_link_port = "v_ul_0"
        setup_vlan_switch = SCRIPT_PATH + "scripts/setup-uplink-vlan-sw.sh"
        subprocess.check_call([setup_vlan_switch, self.vlan_sw, "v"])

        setup_uplink_br = [
            SCRIPT_PATH + "scripts/setup-uplink-br.sh",
            self._br,
            self.up_link_port,
            DHCP_IFACE,
        ]
        subprocess.check_call(setup_uplink_br)
        self._setup_dhclp_client()

    def _setup_dhclp_client(self):
        self._dhcp_client = DHCPClient(
            dhcp_wait=self.dhcp_wait,
            dhcp_store=self.dhcp_store,
            gw_info=self.gw_info,
            iface=DHCP_IFACE,
            lease_renew_wait_min=1,
        )
        self._dhcp_client.run()

    def _setup_dhcp_on_vlan(self, vlan: int):
        setup_vlan_switch = SCRIPT_PATH + "scripts/setup-uplink-vlan-srv.sh"
        subprocess.check_call([setup_vlan_switch, self.vlan_sw, str(vlan)])

    def _validate_req_state(
        self,
        mac: MacAddress,
        state: DHCPState,
        vlan: int,
    ):
        for x in range(RETRY_LIMIT):
            LOG.debug("wait for state: %d" % x)
            with self.dhcp_wait:
                dhcp1 = self.dhcp_store.get(mac.as_redis_key(vlan))
                if state == DHCPState.RELEASE and dhcp1 is None:
                    return
                if dhcp1.state_requested == state:
                    return
            time.sleep(PKT_CAPTURE_WAIT)

        assert 0

    def _get_state_xid(self, mac: MacAddress, vlan: int):
        with self.dhcp_wait:
            dhcp1 = self.dhcp_store.get(mac.as_redis_key(vlan))
            return dhcp1.xid

    def _validate_state_as_current(self, mac: MacAddress, vlan: int):
        with self.dhcp_wait:
            dhcp1 = self.dhcp_store.get(mac.as_redis_key(vlan))
            self.assert_(
                dhcp1.state == DHCPState.OFFER
                or dhcp1.state == DHCPState.ACK, )

    def _alloc_ip_address_from_dhcp(
        self,
        mac: MacAddress,
        vlan: int,
    ) -> DHCPDescriptor:
        retry_count = 0
        with self.dhcp_wait:
            dhcp_desc = None
            while (retry_count < 60
                   and (dhcp_desc is None
                        or dhcp_desc.ip_is_allocated() is not True)):
                if retry_count % 5 == 0:
                    self._dhcp_client.send_dhcp_packet(
                        mac,
                        vlan,
                        DHCPState.DISCOVER,
                    )

                self.dhcp_wait.wait(timeout=1)
                dhcp_desc = self._dhcp_client.get_dhcp_desc(mac, vlan)
                retry_count = retry_count + 1

            return dhcp_desc

    def _validate_ip_subnet(self, mac: MacAddress, vlan: int):
        # vlan is configured with subnet : 10.200.x.1
        # router IP is 10.200.x.211
        # x is vlan id
        exptected_subnet = ipaddress.ip_network("10.200.%s.0/24" % vlan)
        exptected_router_ip = ipaddress.ip_address("10.200.%s.211" % vlan)
        with self.dhcp_wait:
            dhcp1 = self.dhcp_store.get(mac.as_redis_key(vlan))
            self.assertEqual(dhcp1.subnet, str(exptected_subnet))
            self.assertEqual(dhcp1.router_ip, exptected_router_ip)
            self.assert_(ipaddress.ip_address(dhcp1.ip) in exptected_subnet)

    def _release_ip(self, mac: MacAddress, vlan: int):
        self._dhcp_client.release_ip_address(mac, vlan)
        time.sleep(PKT_CAPTURE_WAIT)
        self._validate_req_state(mac, DHCPState.RELEASE, vlan)

    def _handle_dhcp_req_packet(self, packet):
        if DHCP not in packet:
            return
        with self.pkt_list_lock:
            self.pkt_list.append(packet)

    def _start_sniffer(self):
        # drop dhclient requests, this would avoid lease
        # renewal after freezing time.
        subprocess.check_call([
            "ovs-ofctl",
            "add-flow",
            self._br,
            "priority=100,in_port=" + self.up_link_port + ",action=drop",
        ])
        time.sleep(PKT_CAPTURE_WAIT)

        self._sniffer = AsyncSniffer(
            iface=self._br,
            filter="udp and (port 67 or 68)",
            store=False,
            prn=self._handle_dhcp_req_packet,
        )

        self.pkt_list = []
        self._sniffer.start()
        LOG.debug("sniffer started")
        time.sleep(PKT_CAPTURE_WAIT)

    def _stop_sniffer_and_check(self, state: DHCPState, mac: MacAddress,
                                vlan: int):
        LOG.debug("delete drop flow")
        subprocess.check_call([
            "ovs-ofctl",
            "del-flows",
            self._br,
            "in_port=" + self.up_link_port,
        ])

        for x in range(RETRY_LIMIT):
            LOG.debug("wait for pkt: %d" % x)
            time.sleep(PKT_CAPTURE_WAIT)

            with self.pkt_list_lock:
                for pkt in self.pkt_list:
                    if DHCP in pkt:
                        if vlan and (Dot1Q not in pkt
                                     or vlan != pkt[Dot1Q].vlan):
                            continue

                        if pkt[DHCP].options[0][1] == int(state) and \
                                pkt[Ether].src == str(mac):
                            self._sniffer.stop()
                            return

        LOG.debug("Failed check for dhcp packet: %s", state)
        with self.pkt_list_lock:
            for pkt in self.pkt_list:
                LOG.debug("DHCP pkt %s", pkt.show(dump=True))

        # validate if any dhcp packet was sent.
        if state == DHCPState.DISCOVER:
            self.assertNotEqual(
                self._last_xid,
                self._get_state_xid(mac, vlan),
            )
Ejemplo n.º 4
0
class DhcpClient(unittest.TestCase):
    def setUp(self):
        self._br = "t_up_br0"
        try:
            subprocess.check_call(["pkill", "dnsmasq"])
        except subprocess.CalledProcessError:
            pass

        setup_dhcp_server = SCRIPT_PATH + "scripts/setup-test-dhcp-srv.sh"
        subprocess.check_call([setup_dhcp_server, "t0"])

        setup_uplink_br = [SCRIPT_PATH + "scripts/setup-uplink-br.sh",
                           self._br,
                           DHCP_IFACE,
                           "8A:00:00:00:00:01"]
        subprocess.check_call(setup_uplink_br)

        self.dhcp_wait = threading.Condition()
        self.dhcp_store = {}
        self.gw_info_map = {}
        self.gw_info = UplinkGatewayInfo(self.gw_info_map)
        self._dhcp_client = DHCPClient(dhcp_wait=self.dhcp_wait,
                                       dhcp_store=self.dhcp_store,
                                       gw_info=self.gw_info,
                                       iface="t_dhcp0",
                                       lease_renew_wait_min=1)
        self._dhcp_client.run()

    def tearDown(self):
        self._dhcp_client.stop()
        BridgeTools.destroy_bridge(self._br)

    @unittest.skipIf(os.getuid(), reason="needs root user")
    def test_dhcp_lease1(self):
        self._setup_sniffer()
        mac1 = MacAddress("11:22:33:44:55:66")
        dhcp1 = self._alloc_ip_address_from_dhcp(mac1)
        self.assertEqual(dhcp1.state_requested, DHCPState.REQUEST)
        assert (dhcp1.state == DHCPState.OFFER or dhcp1.state == DHCPState.ACK)

        # trigger lease reneval before deadline
        time1 = datetime.datetime.now() + datetime.timedelta(seconds=100)
        self._start_sniffer()
        with freeze_time(time1):
            time.sleep(PKT_CAPTURE_WAIT)
            self._stop_sniffer_and_check(DHCPState.REQUEST, mac1)
            self.assertEqual(dhcp1.state_requested, DHCPState.REQUEST)
            assert (dhcp1.state == DHCPState.OFFER or dhcp1.state == DHCPState.ACK)

            # trigger lease after deadline
            time2 = datetime.datetime.now() + datetime.timedelta(seconds=200)
            self._start_sniffer()
            with freeze_time(time2):
                time.sleep(PKT_CAPTURE_WAIT)
                self._stop_sniffer_and_check(DHCPState.DISCOVER, mac1)
                self.assertEqual(dhcp1.state_requested, DHCPState.REQUEST)
                assert (dhcp1.state == DHCPState.OFFER or dhcp1.state == DHCPState.ACK)

        self._dhcp_client.release_ip_address(mac1)

        dhcp1 = self.dhcp_store.get(mac1.as_redis_key())
        self.assertEqual(dhcp1.state_requested, DHCPState.RELEASE)

    def _alloc_ip_address_from_dhcp(self, mac: MacAddress) -> DHCPDescriptor:
        retry_count = 0
        with self.dhcp_wait:
            dhcp_desc = None
            while (retry_count < 60 and (dhcp_desc is None or
                                         dhcp_desc.ip_is_allocated() is not True)):
                if retry_count % 5 == 0:
                    self._dhcp_client.send_dhcp_packet(mac, DHCPState.DISCOVER)

                self.dhcp_wait.wait(timeout=1)
                dhcp_desc = self._dhcp_client.get_dhcp_desc(mac)
                retry_count = retry_count + 1

            return dhcp_desc

    def _handle_dhcp_req_packet(self, packet):
        if DHCP not in packet:
            return
        self.pkt_list.append(packet)

    def _setup_sniffer(self):
        self._sniffer = AsyncSniffer(iface=DHCP_IFACE,
                                     filter="udp and (port 67 or 68)",
                                     prn=self._handle_dhcp_req_packet)

    def _start_sniffer(self):
        self.pkt_list = []
        self._sniffer.start()
        time.sleep(.5)

    def _stop_sniffer_and_check(self, state: DHCPState, mac: MacAddress):
        self._sniffer.stop()
        for pkt in self.pkt_list:
            logging.debug("pkt: %s " % pkt.summary())
            if DHCP in pkt:
                if pkt[DHCP].options[0][1] == int(state) and \
                        pkt[Ether].src == str(mac):
                    return
        assert 0