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 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 send_dhcp_packet(self, mac: MacAddress, vlan: str, state: DHCPState, dhcp_desc: DHCPDescriptor = None): """ Send DHCP packet and record state in dhcp_client_state. Args: mac: MAC address of interface state: state of DHCP packet dhcp_desc: DHCP protocol state. Returns: """ ciaddr = None # generate DHCP request packet if state == DHCPState.DISCOVER: dhcp_opts = [("message-type", "discover")] dhcp_desc = DHCPDescriptor(mac=mac, ip="", vlan=vlan, state_requested=DHCPState.DISCOVER) self._msg_xid = self._msg_xid + 1 pkt_xid = self._msg_xid elif state == DHCPState.REQUEST: dhcp_opts = [("message-type", "request"), ("requested_addr", dhcp_desc.ip), ("server_id", dhcp_desc.server_ip)] dhcp_desc.state_requested = DHCPState.REQUEST pkt_xid = dhcp_desc.xid ciaddr = dhcp_desc.ip elif state == DHCPState.RELEASE: dhcp_opts = [("message-type", "release"), ("server_id", dhcp_desc.server_ip)] dhcp_desc.state_requested = DHCPState.RELEASE self._msg_xid = self._msg_xid + 1 pkt_xid = self._msg_xid ciaddr = dhcp_desc.ip else: LOG.warning("Unknown egress request mac %s state %s", str(mac), state) return dhcp_opts.append("end") dhcp_desc.xid = pkt_xid with self._dhcp_notify: self.dhcp_client_state[mac.as_redis_key(vlan)] = dhcp_desc pkt = Ether(src=str(mac), dst="ff:ff:ff:ff:ff:ff") if vlan and vlan != "0": pkt /= Dot1Q(vlan=int(vlan)) pkt /= IP(src="0.0.0.0", dst="255.255.255.255") pkt /= UDP(sport=68, dport=67) pkt /= BOOTP(op=1, chaddr=mac.as_hex(), xid=pkt_xid, ciaddr=ciaddr) pkt /= DHCP(options=dhcp_opts) LOG.debug("DHCP pkt xmit %s", pkt.show(dump=True)) sendp(pkt, iface=self._dhcp_interface, verbose=0)
def _process_dhcp_pkt(self, packet, state: DHCPState): LOG.debug("DHCP pkt recv %s", packet.show(dump=True)) mac_addr = MacAddress(hex_to_mac(packet[BOOTP].chaddr.hex()[0:12])) vlan = "" if Dot1Q in packet: vlan = str(packet[Dot1Q].vlan) mac_addr_key = mac_addr.as_redis_key(vlan) with self._dhcp_notify: if mac_addr_key in self.dhcp_client_state: state_requested = self.dhcp_client_state[ mac_addr_key].state_requested if BOOTP not in packet or packet[BOOTP].yiaddr is None: LOG.error("no ip offered") return ip_offered = packet[BOOTP].yiaddr subnet_mask = self._get_option(packet, "subnet_mask") if subnet_mask is not None: ip_subnet = IPv4Network(ip_offered + "/" + subnet_mask, strict=False) else: ip_subnet = IPv4Network(ip_offered + "/" + "32", strict=False) dhcp_router_opt = self._get_option(packet, "router") if dhcp_router_opt is not None: router_ip_addr = ip_address(dhcp_router_opt) self.dhcp_gw_info.update_ip(router_ip_addr, vlan) else: router_ip_addr = None lease_expiration_time = self._get_option(packet, "lease_time") dhcp_state = DHCPDescriptor( mac=mac_addr, ip=ip_offered, state=state, vlan=vlan, state_requested=state_requested, subnet=str(ip_subnet), server_ip=packet[IP].src, router_ip=router_ip_addr, lease_expiration_time=lease_expiration_time, xid=packet[BOOTP].xid) LOG.info("Record DHCP for: %s state: %s", mac_addr_key, dhcp_state) self.dhcp_client_state[mac_addr_key] = dhcp_state self._dhcp_notify.notifyAll() if state == DHCPState.OFFER: # let other thread work on fulfilling IP allocation request. threading.Event().wait(self.THREAD_YIELD_TIME) self.send_dhcp_packet(mac_addr, vlan, DHCPState.REQUEST, dhcp_state) else: LOG.debug("Unknown MAC: %s " % packet.summary()) return
def del_record(self, args): """ Delete DHCP state record from the redis map. Args: args: Mac address. Returns: None """ mac = MacAddress(args.mac) desc = self.dhcp_client_state[mac.as_redis_key()] print("Deleted mac %s with DHCP rec %s" % (str(mac), desc)) self.dhcp_client_state[mac.as_redis_key()] = None
def release_ip_address(self, mac: MacAddress): """ Release DHCP allocated IP. Args: mac: MAC address of the IP allocated. Returns: None """ if mac.as_redis_key() not in self.dhcp_client_state: LOG.error("Unallocated DHCP release for MAC: %s", str(mac)) return dhcp_desc = self.dhcp_client_state[mac.as_redis_key()] self.send_dhcp_packet(mac, DHCPState.RELEASE, dhcp_desc)
def test_dhcp_lease1(self): self.pkt_list_lock = threading.Condition() self._setup_sniffer() mac1 = MacAddress("11:22:33:44:55:66") self._alloc_ip_address_from_dhcp(mac1) self._validate_req_state(mac1, DHCPState.REQUEST) self._validate_state_as_current(mac1) # trigger lease reneval before deadline time1 = datetime.datetime.now() + datetime.timedelta(seconds=100) self._start_sniffer() with freeze_time(time1): self._stop_sniffer_and_check(DHCPState.REQUEST, mac1) self._validate_req_state(mac1, DHCPState.REQUEST) self._validate_state_as_current(mac1) # trigger lease after deadline time2 = datetime.datetime.now() + datetime.timedelta(seconds=200) self._start_sniffer() with freeze_time(time2): LOG.debug("check discover after lease loss") self._stop_sniffer_and_check(DHCPState.DISCOVER, mac1) self._validate_req_state(mac1, DHCPState.REQUEST) self._validate_state_as_current(mac1) self._dhcp_client.release_ip_address(mac1) time.sleep(PKT_CAPTURE_WAIT) self._validate_req_state(mac1, DHCPState.RELEASE)
def _validate_req_state(self, mac: MacAddress, state: DHCPState, vlan: str): 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 dhcp1.state_requested == state: return time.sleep(PKT_CAPTURE_WAIT) assert 0
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)
def add_record(self, args): """ Add DHCP record. Args: args: All data required for DHCP state. Returns: """ state = DHCPState(args.state) ipaddr = ip_address(args.ip) subnet = ip_network(args.subnet, strict=False) dhcp_ip = ip_address(args.dhcp) desc = DHCPDescriptor(args.mac, str(ipaddr), state, subnet, dhcp_ip, None, args.lease, random.randint(0, 50000)) mac = MacAddress(args.mac) self.dhcp_client_state[mac.as_redis_key()] = desc print("Added mac %s with DHCP rec %s" % str(mac), 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 get_dhcp_desc(self, mac: MacAddress) -> Optional[DHCPDescriptor]: """ Get DHCP description for given MAC. Args: mac: Mac address of the client Returns: Current DHCP info. """ key = mac.as_redis_key() if key in self.dhcp_client_state: return self.dhcp_client_state[key] LOG.debug("lookup error for %s", str(mac)) return None
def get_dhcp_desc(self, mac: MacAddress, vlan: str) -> Optional[DHCPDescriptor]: """ Get DHCP description for given MAC. Args: mac: Mac address of the client vlan: vlan id if the IP allocated in a VLAN Returns: Current DHCP info. """ key = mac.as_redis_key(vlan) if key in self.dhcp_client_state: return self.dhcp_client_state[key] LOG.debug("lookup error for %s", str(key)) return None
def release_ip_address(self, mac: MacAddress, vlan: str): """ Release DHCP allocated IP. Args: mac: MAC address of the IP allocated. vlan: vlan id if the IP allocated in a VLAN Returns: None """ key = mac.as_redis_key(vlan) if key not in self.dhcp_client_state: LOG.error("Unallocated DHCP release for MAC: %s", key) return dhcp_desc = self.dhcp_client_state[key] self.send_dhcp_packet(mac, dhcp_desc.vlan, DHCPState.RELEASE, dhcp_desc)
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)
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 _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): with self.dhcp_wait: dhcp1 = self.dhcp_store.get(mac.as_redis_key()) assert (dhcp1.state == DHCPState.OFFER or dhcp1.state == DHCPState.ACK)
def _validate_req_state(self, mac: MacAddress, state: DHCPState, vlan: str): with self.dhcp_wait: dhcp1 = self.dhcp_store.get(mac.as_redis_key(vlan)) self.assertEqual(dhcp1.state_requested, state)