static rlm_rcode_t dhcp_process(REQUEST *request) { rlm_rcode_t rcode; unsigned int i; VALUE_PAIR *vp; dhcp_socket_t *sock; /* * If there's a giaddr, save it as the Relay-IP-Address * in the response. That way the later code knows where * to send the reply. */ vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */ if (vp && (vp->vp_ipv4addr != htonl(INADDR_ANY))) { VALUE_PAIR *relay; /* DHCP-Relay-IP-Address */ MEM(relay = fr_pair_afrom_num(request->reply, DHCP_MAGIC_VENDOR, 222)); relay->vp_ipv4addr = vp->vp_ipv4addr; fr_pair_add(&request->reply->vps, relay); } vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 53, TAG_ANY); /* DHCP-Message-Type */ if (vp) { fr_dict_enum_t *dv = fr_dict_enum_by_value(vp->da, &vp->data); if (dv) { CONF_SECTION *server, *unlang; RDEBUG("Trying sub-section dhcp %s {...}", dv->alias); server = cf_item_to_section(cf_parent(request->listener->cs)); unlang = cf_section_find(server, "dhcp", dv->alias); rcode = unlang_interpret(request, unlang, RLM_MODULE_NOOP); } else { REDEBUG("Unknown DHCP-Message-Type %d", vp->vp_uint8); rcode = RLM_MODULE_FAIL; } } else { REDEBUG("Failed to find DHCP-Message-Type in packet!"); rcode = RLM_MODULE_FAIL; } vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 53, TAG_ANY); /* DHCP-Message-Type */ if (vp) { request->reply->code = vp->vp_uint8; } else switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: if (request->packet->code == FR_DHCP_DISCOVER) { request->reply->code = FR_DHCP_OFFER; break; } else if (request->packet->code == FR_DHCP_REQUEST) { request->reply->code = FR_DHCP_ACK; break; } request->reply->code = FR_DHCP_NAK; break; default: case RLM_MODULE_REJECT: case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: if (request->packet->code == FR_DHCP_DISCOVER) { request->reply->code = 0; /* ignore the packet */ } else { request->reply->code = FR_DHCP_NAK; } break; case RLM_MODULE_HANDLED: request->reply->code = 0; /* ignore the packet */ break; } /* * TODO: Handle 'output' of RLM_MODULE when acting as a * DHCP relay We may want to not forward packets in * certain circumstances. */ /* * Handle requests when acting as a DHCP relay */ vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 256, TAG_ANY); /* DHCP-Opcode */ if (!vp) { RPEDEBUG("Someone deleted the DHCP-Opcode!"); return RLM_MODULE_FAIL; } /* BOOTREPLY received on port 67 (i.e. from a server) */ if (vp->vp_uint8 == 2) { return dhcprelay_process_server_reply(request); } /* Packet from client, and we have DHCP-Relay-To-IP-Address */ if (fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 270, TAG_ANY)) { return dhcprelay_process_client_request(request); } /* else it's a packet from a client, without relaying */ rad_assert(vp->vp_uint8 == 1); /* BOOTREQUEST */ sock = request->listener->data; /* * Handle requests when acting as a DHCP server */ /* * Releases don't get replies. */ if (request->packet->code == FR_DHCP_RELEASE) { request->reply->code = 0; } if (request->reply->code == 0) { return RLM_MODULE_OK; } request->reply->sockfd = request->packet->sockfd; /* * Copy specific fields from packet to reply, if they * don't already exist */ for (i = 0; i < sizeof(attrnums) / sizeof(attrnums[0]); i++) { uint32_t attr = attrnums[i]; if (fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, attr, TAG_ANY)) continue; vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, attr, TAG_ANY); if (vp) { fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp)); } } vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 256, TAG_ANY); /* DHCP-Opcode */ rad_assert(vp != NULL); vp->vp_uint8 = 2; /* BOOTREPLY */ /* * Allow NAKs to be delayed for a short period of time. */ if (request->reply->code == FR_DHCP_NAK) { vp = fr_pair_find_by_num(request->reply->vps, 0, FR_FREERADIUS_RESPONSE_DELAY, TAG_ANY); if (vp) { if (vp->vp_uint32 <= 10) { request->response_delay.tv_sec = vp->vp_uint32; request->response_delay.tv_usec = 0; } else { request->response_delay.tv_sec = 10; request->response_delay.tv_usec = 0; } } else { #ifndef USEC #define USEC 1000000 #endif vp = fr_pair_find_by_num(request->reply->vps, 0, FR_FREERADIUS_RESPONSE_DELAY_USEC, TAG_ANY); if (vp) { if (vp->vp_uint32 <= 10 * USEC) { request->response_delay.tv_sec = vp->vp_uint32 / USEC; request->response_delay.tv_usec = vp->vp_uint32 % USEC; } else { request->response_delay.tv_sec = 10; request->response_delay.tv_usec = 0; } } } } /* * Prepare the reply packet for sending through dhcp_socket_send() */ request->reply->dst_ipaddr.af = AF_INET; request->reply->src_ipaddr.af = AF_INET; request->reply->src_ipaddr.prefix = 32; /* * Packet-Src-IP-Address has highest precedence */ vp = fr_pair_find_by_num(request->reply->vps, 0, FR_PACKET_SRC_IP_ADDRESS, TAG_ANY); if (vp) { request->reply->if_index = 0; /* Must be 0, we don't know the outbound if_index */ request->reply->src_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; /* * The request was unicast (via a relay) */ } else if (request->packet->dst_ipaddr.addr.v4.s_addr != htonl(INADDR_BROADCAST) && request->packet->dst_ipaddr.addr.v4.s_addr != htonl(INADDR_ANY)) { request->reply->src_ipaddr.addr.v4.s_addr = request->packet->dst_ipaddr.addr.v4.s_addr; request->reply->if_index = request->packet->if_index; /* * The listener was bound to an IP address, or we determined * the address automatically, as it was the only address bound * to the interface, and we bound to the interface. */ } else if (sock->src_ipaddr.addr.v4.s_addr != htonl(INADDR_ANY)) { request->reply->src_ipaddr.addr.v4.s_addr = sock->src_ipaddr.addr.v4.s_addr; #ifdef WITH_IFINDEX_IPADDR_RESOLUTION /* * We built with udpfromto and have the if_index of the receiving * interface, which we can now resolve to an IP address. */ } else if (request->packet->if_index > 0) { fr_ipaddr_t primary; if (fr_ipaddr_from_ifindex(&primary, request->packet->sockfd, request->packet->dst_ipaddr.af, request->packet->if_index) < 0) { RPEDEBUG("Failed determining src_ipaddr from if_index"); return RLM_MODULE_FAIL; } request->reply->src_ipaddr.addr.v4.s_addr = primary.addr.v4.s_addr; #endif /* * There's a Server-Identification attribute */ } else if ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 54, TAG_ANY))) { request->reply->src_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; } else { REDEBUG("Unable to determine correct src_ipaddr for response"); return RLM_MODULE_FAIL; } request->reply->dst_port = request->packet->src_port; request->reply->src_port = request->packet->dst_port; /* * Answer to client's nearest DHCP relay. * * Which may be different than the giaddr given in the * packet to the client. i.e. the relay may have a * public IP, but the gateway a private one. */ vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 272, TAG_ANY); /* DHCP-Relay-IP-Address */ if (vp && (vp->vp_ipv4addr != ntohl(INADDR_ANY))) { RDEBUG2("Reply will be unicast to giaddr from original packet"); request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; request->reply->dst_port = request->packet->dst_port; vp = fr_pair_find_by_num(request->reply->vps, 0, FR_PACKET_DST_PORT, TAG_ANY); if (vp) request->reply->dst_port = vp->vp_uint16; return RLM_MODULE_OK; } /* * Answer to client's nearest DHCP gateway. In this * case, the client can reach the gateway, as can the * server. * * We also use *our* source port as the destination port. * Gateways are servers, and listen on the server port, * not the client port. */ vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */ if (vp && (vp->vp_ipv4addr != htonl(INADDR_ANY))) { RDEBUG2("Reply will be unicast to giaddr"); request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; request->reply->dst_port = request->packet->dst_port; return RLM_MODULE_OK; } /* * If it's a NAK, or the broadcast flag was set, ond * there's no client-ip-address, send a broadcast. */ if ((request->reply->code == FR_DHCP_NAK) || ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 262, TAG_ANY)) && /* DHCP-Flags */ (vp->vp_uint32 & 0x8000) && ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) && /* DHCP-Client-IP-Address */ (vp->vp_ipv4addr == htonl(INADDR_ANY))))) { /* * RFC 2131, page 23 * * Broadcast on * - DHCPNAK * or * - Broadcast flag is set up and ciaddr == NULL */ RDEBUG2("Reply will be broadcast"); request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); return RLM_MODULE_OK; } /* * RFC 2131, page 23 * * Unicast to ciaddr if present, otherwise to yiaddr. */ if ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) && /* DHCP-Client-IP-Address */ (vp->vp_ipv4addr != htonl(INADDR_ANY))) { RDEBUG2("Reply will be sent unicast to &DHCP-Client-IP-Address"); request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; return RLM_MODULE_OK; } vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 264, TAG_ANY); /* DHCP-Your-IP-Address */ if (!vp) { REDEBUG("Can't assign address to client: Neither &reply:DHCP-Client-IP-Address nor " "&reply:DHCP-Your-IP-Address set"); /* * There is nowhere to send the response to, so don't bother. */ request->reply->code = 0; return RLM_MODULE_FAIL; } #ifdef SIOCSARP /* * The system is configured to listen for broadcast * packets, which means we'll need to send unicast * replies, to IPs which haven't yet been assigned. * Therefore, we need to update the ARP table. * * However, they haven't specified a interface. So we * can't update the ARP table. And we must send a * broadcast response. */ if (sock->lsock.broadcast && !sock->src_interface) { WARN("You MUST set \"interface\" if you have \"broadcast = yes\""); RDEBUG2("Reply will be broadcast as no interface was defined"); request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); return RLM_MODULE_OK; } RDEBUG2("Reply will be unicast to &DHCP-Your-IP-Address"); request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; /* * When sending a DHCP_OFFER, make sure our ARP table * contains an entry for the client IP address. * Otherwise the packet may not be sent to the client, as * the OS has no ARP entry for it. * * This is a cute hack to avoid us having to create a raw * socket to send DHCP packets. */ if (request->reply->code == FR_DHCP_OFFER) { VALUE_PAIR *hwvp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 267, TAG_ANY); /* DHCP-Client-Hardware-Address */ if (!hwvp) return RLM_MODULE_FAIL; if (fr_dhcpv4_udp_add_arp_entry(request->reply->sockfd, sock->src_interface, &vp->vp_ip, hwvp->vp_ether) < 0) { RPEDEBUG("Failed adding arp entry"); return RLM_MODULE_FAIL; } } #else if (request->packet->src_ipaddr.addr.v4.s_addr != ntohl(INADDR_NONE)) { RDEBUG2("Reply will be unicast to the unicast source IP address"); request->reply->dst_ipaddr.addr.v4.s_addr = request->packet->src_ipaddr.addr.v4.s_addr; } else { RDEBUG2("Reply will be broadcast as this system does not support ARP updates"); request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); } #endif return RLM_MODULE_OK; }
static ssize_t mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time, uint8_t *buffer, size_t buffer_len, UNUSED size_t written) { proto_dhcpv4_udp_t const *inst = talloc_get_type_abort_const(li->app_io_instance, proto_dhcpv4_udp_t); proto_dhcpv4_udp_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_dhcpv4_udp_thread_t); fr_io_track_t *track = talloc_get_type_abort(packet_ctx, fr_io_track_t); fr_io_address_t address; int flags; ssize_t data_size; /* * @todo - share a stats interface with the parent? or * put the stats in the listener, so that proto_dhcpv4 * can update them, too.. <sigh> */ thread->stats.total_responses++; flags = UDP_FLAGS_CONNECTED * (thread->connection != NULL); rad_assert(track->reply_len == 0); /* * Swap src/dst IP/port */ address.src_ipaddr = track->address->dst_ipaddr; address.src_port = track->address->dst_port; address.dst_ipaddr = track->address->src_ipaddr; address.dst_port = track->address->src_port; address.if_index = track->address->if_index; /* * Figure out which kind of packet we're sending. */ if (!thread->connection) { uint8_t const *code, *sid; uint32_t ipaddr; dhcp_packet_t *packet = (dhcp_packet_t *) buffer; dhcp_packet_t *request = (dhcp_packet_t *) track->packet; /* only 20 bytes tho! */ #ifdef WITH_IFINDEX_IPADDR_RESOLUTION fr_ipaddr_t primary; #endif /* * This isn't available in the packet header. */ code = fr_dhcpv4_packet_get_option(packet, buffer_len, attr_message_type); if (!code || (code[1] < 1) || (code[2] == 0) || (code[2] >= FR_DHCP_INFORM)) { DEBUG("WARNING - silently discarding reply due to invalid or missing message type"); return 0; } /* * Set the source IP of the packet. * * - if src_ipaddr is unicast, use that * - else if socket wasn't bound to *, then use that * - else if we have if_index, get main IP from that interface and use that. * - else for offer/ack, look at option 54, for Server Identification and use that * - else leave source IP as whatever is already in "address.src_ipaddr". */ if (inst->src_ipaddr.addr.v4.s_addr != INADDR_ANY) { address.src_ipaddr = inst->src_ipaddr; } else if (inst->ipaddr.addr.v4.s_addr != INADDR_ANY) { address.src_ipaddr = inst->ipaddr; #ifdef WITH_IFINDEX_IPADDR_RESOLUTION } else if ((address->if_index > 0) && (fr_ipaddr_from_ifindex(&primary, thread->sockfd, &address.dst_ipaddr.af, &address.if_index) == 0)) { address.src_ipaddr = primary; #endif } else if (((code[2] == FR_DHCP_OFFER) || (code[2] == FR_DHCP_ACK)) && ((sid = fr_dhcpv4_packet_get_option(packet, buffer_len, attr_dhcp_server_identifier)) != NULL) && (sid[1] == 4)) { memcpy(&address.src_ipaddr.addr.v4.s_addr, sid + 2, 4); } /* * We have GIADDR in the packet, so send it * there. The packet is FROM our IP address and * port, TO the destination IP address, at the * same (i.e. server) port. */ memcpy(&ipaddr, &packet->giaddr, 4); if (ipaddr != INADDR_ANY) { DEBUG("Reply will be sent to giaddr."); address.dst_ipaddr.addr.v4.s_addr = ipaddr; address.dst_port = inst->port; address.src_port = inst->port; /* * Increase the hop count for client * packets sent to the next gateway. */ if ((code[2] == FR_DHCP_DISCOVER) || (code[2] == FR_DHCP_REQUEST)) { packet->opcode = 1; /* client message */ packet->hops = request->hops + 1; } else { packet->opcode = 2; /* server message */ } goto send_reply; } /* * If there's no GIADDR, we don't know where to * send client packets. */ if ((code[2] == FR_DHCP_DISCOVER) || (code[2] == FR_DHCP_REQUEST)) { DEBUG("WARNING - silently discarding client reply, as there is no GIADDR to send it to."); return 0; } packet->opcode = 2; /* server message */ /* * The original packet requested a broadcast * reply, and CIADDR is empty, go broadcast the * reply. RFC 2131 page 23. */ if (((request->flags & FR_DHCP_FLAGS_VALUE_BROADCAST) != 0) && (request->ciaddr == INADDR_ANY)) { DEBUG("Reply will be broadcast due to client request."); address.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST; goto send_reply; } /* * The original packet has CIADDR, so we unicast * the reply there. RFC 2131 page 23. */ if (request->ciaddr != INADDR_ANY) { DEBUG("Reply will be unicast to CIADDR from original packet."); memcpy(&address.dst_ipaddr.addr.v4.s_addr, &request->ciaddr, 4); goto send_reply; } /* * The original packet was unicast to us, such as * via a relay. We have a unicast destination * address, so we just use that. */ if ((packet->yiaddr == htonl(INADDR_ANY)) && (address.dst_ipaddr.addr.v4.s_addr != htonl(INADDR_BROADCAST))) { DEBUG("Reply will be unicast to source IP from original packet."); goto send_reply; } switch (code[2]) { /* * Offers are sent to YIADDR if we * received a unicast packet from YIADDR. * Otherwise, they are unicast to YIADDR * (if we can update ARP), otherwise they * are broadcast. */ case FR_DHCP_OFFER: /* * If the packet was unicast from the * client, unicast it back. */ if (memcmp(&address.dst_ipaddr.addr.v4.s_addr, &packet->yiaddr, 4) == 0) { DEBUG("Reply will be unicast to YIADDR."); #ifdef SIOCSARP } else if (inst->broadcast && inst->interface) { if (fr_dhcpv4_udp_add_arp_entry(thread->sockfd, inst->interface, &packet->yiaddr, &packet->chaddr) < 0) { DEBUG("Failed adding ARP entry. Reply will be broadcast."); address.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST; } else { DEBUG("Reply will be unicast to YIADDR after ARP table updates."); } #endif } else { DEBUG("Reply will be broadcast due to OFFER."); address.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST; } break; /* * ACKs are unicast to YIADDR */ case FR_DHCP_ACK: DEBUG("Reply will be unicast to YIADDR."); memcpy(&address.dst_ipaddr.addr.v4.s_addr, &packet->yiaddr, 4); break; /* * NAKs are broadcast. */ case FR_DHCP_NAK: DEBUG("Reply will be broadcast due to NAK."); address.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST; break; default: DEBUG("WARNING - silently discarding reply due to invalid message type %d", code[2]); return 0; } } send_reply: /* * proto_dhcpv4 takes care of suppressing do-not-respond, etc. */ data_size = udp_send(thread->sockfd, buffer, buffer_len, flags, &address.src_ipaddr, address.src_port, address.if_index, &address.dst_ipaddr, address.dst_port); /* * This socket is dead. That's an error... */ if (data_size <= 0) return data_size; return data_size; }
/* * We've seen a reply from a server. * i.e. we're a relay. */ static int dhcprelay_process_server_reply(REQUEST *request) { int rcode; VALUE_PAIR *vp, *giaddr; dhcp_socket_t *sock; RADIUS_PACKET *packet; rad_assert(request->packet->data[0] == 2); /* * Do the forward by ourselves, do not rely on dhcp_socket_send() */ request->reply->code = 0; sock = request->listener->data; /* * Check that packet is for us. */ giaddr = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */ /* --with-udpfromto is needed just for the following test */ if (!giaddr || giaddr->vp_ipv4addr != request->packet->dst_ipaddr.addr.v4.s_addr) { RDEBUG2("Packet received from server was not for us (was for 0x%x). Discarding packet", ntohl(request->packet->dst_ipaddr.addr.v4.s_addr)); return 1; } /* * Don't muck with the original request packet. That's * bad form. Plus, dhcp_encode() does nothing if * packet->data is already set. */ packet = fr_radius_alloc(request, false); rcode = -1; /* * Forward the request to the next server using the * incoming request as a template. */ packet->code = request->packet->code; packet->sockfd = request->packet->sockfd; /* set SRC ipaddr/port to the listener ipaddr/port */ packet->src_ipaddr.af = AF_INET; packet->src_port = sock->lsock.my_port; /* set DEST ipaddr/port to clientip/68 or broadcast in specific cases */ packet->dst_ipaddr.af = AF_INET; /* * We're a relay, and send the reply to giaddr. */ packet->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); packet->dst_port = request->packet->dst_port; /* server port */ vp = fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 270, TAG_ANY); /* DHCP-Relay-To-IP-Address */ if (vp) { RDEBUG("DHCP: response will be relayed to previous gateway"); packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; giaddr->vp_ipv4addr = vp->vp_ipv4addr; } else if ((packet->code == FR_DHCP_NAK) || !sock->src_interface || ((vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 262, TAG_ANY)) /* DHCP-Flags */ && (vp->vp_uint32 & 0x8000) && ((vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) /* DHCP-Client-IP-Address */ && (vp->vp_ipv4addr == htonl(INADDR_ANY))))) { /* * RFC 2131, page 23 * * Broadcast on * - DHCPNAK * or * - Broadcast flag is set up and ciaddr == NULL */ RDEBUG2("Response will be broadcast"); packet->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); } else if ((vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) /* DHCP-Client-IP-Address */ && (vp->vp_ipv4addr != htonl(INADDR_ANY))) { /* * RFC 2131, page 23 * * Unicast to * - ciaddr if present * otherwise to yiaddr */ packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; } else { vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 264, TAG_ANY); /* DHCP-Your-IP-Address */ if (!vp) { RPEDEBUG("Failed to find IP Address for request"); goto error; } RDEBUG2("Response will be unicast to &DHCP-Your-IP-Address"); packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; /* * When sending a DHCP_OFFER, make sure our ARP table * contains an entry for the client IP address, or else * packet may not be forwarded if it was the first time * the client was requesting an IP address. */ if (request->packet->code == FR_DHCP_OFFER) { VALUE_PAIR *hwvp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 267, TAG_ANY); /* DHCP-Client-Hardware-Address */ if (hwvp == NULL) { RDEBUG2("DHCP_OFFER packet received with no Client Hardware Address. " "Discarding packet"); goto error; } if (fr_dhcpv4_udp_add_arp_entry(request->packet->sockfd, sock->src_interface, &vp->vp_ip, hwvp->vp_ether) < 0) { REDEBUG("Failed adding ARP entry"); goto error; } } } packet->vps = request->packet->vps; /* hackity hack */ /* * Our response doesn't go through process.c */ dhcp_packet_debug(request, packet, false); if (fr_dhcpv4_packet_encode(packet) < 0) { RPERROR("Failed encoding DHCP packet"); goto error; } rcode = fr_dhcpv4_udp_packet_send(request->packet); error: packet->vps = NULL; talloc_free(packet); return rcode; }