Exemple #1
0
    def handle_socket(self) -> None:
        """Retrieves the next, waiting DHCP packet, parses it and calls the
        handler of the associated request.
        """
        try:
            data, source_address = self._socket.recvfrom(2048)
            if len(data) == 0:
                self._log.warning("unexpectedly received EOF!")
                return
            packet = DhcpPacket()
            packet.source_address = source_address
            packet.DecodePacket(data)

            if (not packet.IsDhcpPacket()) or (
                not packet.IsOption("dhcp_message_type")
            ):
                self._log.debug("Ignoring invalid packet")
                return

            dhcp_type = packet.GetOption("dhcp_message_type")[0]
            if dhcp_type not in self._DHCP_TYPE_HANDLERS:
                self._log.debug("Ignoring packet of unexpected DHCP type %d", dhcp_type)
                return

            xid = int.from_bytes(packet.GetOption('xid'), "big")
            if xid not in self._requests:
                self._log.debug("Ignoring answer with xid %r", xid)
                return

            request = self._requests[xid]
            clb_name = self._DHCP_TYPE_HANDLERS[dhcp_type]
            if not hasattr(request, clb_name):
                self._log.error("request has no callback '%s'", clb_name)
                return

            clb = getattr(request, clb_name)
            clb(packet)
        except Exception:
            self._log.exception('handling DHCP packet failed')
Exemple #2
0
    def handle_dhcp_ack(self, packet: DhcpPacket) -> None:
        """Called by the requestor as soon as a DHCP ACK packet is received for
        our XID.

        In case the packet matches what we currently expect, the packet is
        parsed and the success handler called.

        The request instance (self) is removed from the requestor and will
        therefore be destroyed soon.
        """
        if self._state != self.AR_REQUEST:
            return
        self._log.debug("Received ACK")
        if not self._valid_source_address(packet):
            return
        if self._timeout_obj:
            self._timeout_mgr.del_timeout_object(self._timeout_obj)
        self._requestor.del_request(self)
        result = {}  # type: Dict[str, Any]
        result['domain'] = packet.GetOption('domain_name').decode("ascii")

        translate_ips = {
            'yiaddr': 'ip_address',
            'subnet_mask': 'subnet_mask',
            'router': 'gateway',
        }
        for opt_name in translate_ips:
            if not packet.IsOption(opt_name):
                continue
            val = packet.GetOption(opt_name)
            if len(val) == 4:
                result[translate_ips[opt_name]] = str(IPv4Address(val))

        dns = []  # type: List[str]
        result['dns'] = dns
        dns_list = packet.GetOption('domain_name_server')
        while len(dns_list) >= 4:
            dns.append(str(IPv4Address(dns_list[:4])))
            dns_list = dns_list[4:]

        if packet.IsOption('classless_static_route'):
            static_routes = parse_classless_static_routes(
                list(packet.GetOption('classless_static_route'))
            )
            if static_routes is not None:
                if 'gateway' in result:
                    # We MUST ignore a regular default route if static routes
                    # are sent.
                    del result['gateway']
                # Find and filter out default route (if any).  And set it as
                # the new gateway parameter.
                result['static_routes'] = []
                for network, netmask, gateway in static_routes:
                    if network == '0.0.0.0' and netmask == '0.0.0.0':
                        result['gateway'] = gateway
                    else:
                        result['static_routes'].append((network, netmask, gateway))
            del static_routes

        # Calculate lease timeouts (with RFC T1/T2 if not found in packet)
        lease_delta = int.from_bytes(packet.GetOption('ip_address_lease_time'), "big")
        result['lease_timeout'] = self._start_time + lease_delta
        if packet.IsOption('renewal_time_value'):
            renewal_delta = int.from_bytes(
                packet.GetOption('renewal_time_value'), "big"
            )
        else:
            renewal_delta = int(lease_delta * 0.5) + random.randint(-5, 5)
        result['renewal_timeout'] = self._start_time + renewal_delta
        if packet.IsOption('rebinding_time_value'):
            rebinding_delta = int.from_bytes(
                packet.GetOption('rebinding_time_value'), "big"
            )
        else:
            rebinding_delta = int(lease_delta * 0.875) + random.randint(-5, 5)
        result['rebinding_timeout'] = self._start_time + rebinding_delta

        self._success_handler(result)