/* * We've seen a reply from a server. * i.e. we're a relay. */ static int dhcprelay_process_server_reply(REQUEST *request) { VALUE_PAIR *vp, *giaddr; dhcp_socket_t *sock; 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 = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */ /* --with-udpfromto is needed just for the following test */ if (!giaddr || giaddr->vp_ipaddr != request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr) { DEBUG("DHCP: Packet received from server was not for us (was for 0x%x). Discarding packet", ntohl(request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr)); return 1; } /* set SRC ipaddr/port to the listener ipaddr/port */ request->packet->src_ipaddr.af = AF_INET; request->packet->src_port = sock->lsock.my_port; /* set DEST ipaddr/port to clientip/68 or broadcast in specific cases */ request->packet->dst_ipaddr.af = AF_INET; /* * We're a relay, and send the reply to giaddr. */ request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = giaddr->vp_ipaddr; request->reply->dst_port = request->packet->dst_port; /* server port */ if ((request->packet->code == PW_DHCP_NAK) || !sock->src_interface || ((vp = pairfind(request->packet->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Flags */ && (vp->vp_integer & 0x8000) && ((vp = pairfind(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ && (vp->vp_ipaddr == htonl(INADDR_ANY))))) { /* * RFC 2131, page 23 * * Broadcast on * - DHCPNAK * or * - Broadcast flag is set up and ciaddr == NULL */ RDEBUG("DHCP: response will be broadcast"); request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST); } else { /* * RFC 2131, page 23 * * Unicast to * - ciaddr if present * otherwise to yiaddr */ if ((vp = pairfind(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ && (vp->vp_ipaddr != htonl(INADDR_ANY))) { request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; } else { vp = pairfind(request->packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */ if (!vp) { DEBUG("DHCP: Failed to find IP Address for request"); return -1; } RDEBUG("DHCP: response will be unicast to your-ip-address"); request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; /* * 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 == PW_DHCP_OFFER) { VALUE_PAIR *hwvp = pairfind(request->packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */ if (hwvp == NULL) { DEBUG("DHCP: DHCP_OFFER packet received with " "no Client Hardware Address. Discarding packet"); return 1; } if (fr_dhcp_add_arp_entry(request->packet->sockfd, sock->src_interface, hwvp, vp) < 0) { DEBUG("%s", fr_strerror()); return -1; } } } } if (fr_dhcp_encode(request->packet) < 0) { DEBUG("dhcprelay_process_server_reply: ERROR in fr_dhcp_encode\n"); return -1; } return fr_dhcp_send(request->packet); }
static int dhcp_process(REQUEST *request) { int 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 = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */ if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) { VALUE_PAIR *relay; /* DHCP-Relay-IP-Address */ relay = radius_paircreate(request->reply, &request->reply->vps, 272, DHCP_MAGIC_VENDOR); if (relay) relay->vp_ipaddr = vp->vp_ipaddr; } vp = pairfind(request->packet->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */ if (vp) { DICT_VALUE *dv = dict_valbyattr(53, DHCP_MAGIC_VENDOR, vp->vp_integer); DEBUG("Trying sub-section dhcp %s {...}", dv->name ? dv->name : "<unknown>"); rcode = process_post_auth(vp->vp_integer, request); } else { DEBUG("DHCP: Failed to find DHCP-Message-Type in packet!"); rcode = RLM_MODULE_FAIL; } vp = pairfind(request->reply->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */ if (vp) { request->reply->code = vp->vp_integer; if ((request->reply->code != 0) && (request->reply->code < PW_DHCP_OFFSET)) { request->reply->code += PW_DHCP_OFFSET; } } else switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: if (request->packet->code == PW_DHCP_DISCOVER) { request->reply->code = PW_DHCP_OFFER; break; } else if (request->packet->code == PW_DHCP_REQUEST) { request->reply->code = PW_DHCP_ACK; break; } request->reply->code = PW_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 == PW_DHCP_DISCOVER) { request->reply->code = 0; /* ignore the packet */ } else { request->reply->code = PW_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 = pairfind(request->packet->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */ if (!vp) { RDEBUG("FAILURE: Someone deleted the DHCP-Opcode!"); return 1; } /* BOOTREPLY received on port 67 (i.e. from a server) */ if (vp->vp_integer == 2) { return dhcprelay_process_server_reply(request); } /* Packet from client, and we have DHCP-Relay-To-IP-Address */ if (pairfind(request->config_items, 270, DHCP_MAGIC_VENDOR, TAG_ANY)) { return dhcprelay_process_client_request(request); } /* else it's a packet from a client, without relaying */ rad_assert(vp->vp_integer == 1); /* BOOTREQUEST */ sock = request->listener->data; /* * Handle requests when acting as a DHCP server */ /* * Releases don't get replies. */ if (request->packet->code == PW_DHCP_RELEASE) { request->reply->code = 0; } if (request->reply->code == 0) { return 1; } 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 (pairfind(request->reply->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY)) continue; vp = pairfind(request->packet->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY); if (vp) { pairadd(&request->reply->vps, paircopyvp(request->reply, vp)); } } vp = pairfind(request->reply->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */ rad_assert(vp != NULL); vp->vp_integer = 2; /* BOOTREPLY */ /* * Allow NAKs to be delayed for a short period of time. */ if (request->reply->code == PW_DHCP_NAK) { vp = pairfind(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY, 0, TAG_ANY); if (vp) { if (vp->vp_integer <= 10) { request->response_delay = vp->vp_integer; } else { request->response_delay = 10; } } } /* * 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.ipaddr.ip4addr.s_addr = sock->src_ipaddr.ipaddr.ip4addr.s_addr; /* * They didn't set a proper src_ipaddr, but we want to * send the packet with a source IP. If there's a server * identifier, use it. */ if (request->reply->src_ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY) { vp = pairfind(request->reply->vps, 265, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Server-IP-Address */ if (!vp) vp = pairfind(request->reply->vps, 54, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-DHCP-Server-Identifier */ if (vp) { request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; } } 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 = pairfind(request->reply->vps, 272, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-IP-Address */ if (vp && (vp->vp_ipaddr != ntohl(INADDR_ANY))) { RDEBUG("DHCP: Reply will be unicast to giaddr from original packet"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; return 1; } /* * 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 = pairfind(request->reply->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */ if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) { RDEBUG("DHCP: Reply will be unicast to giaddr"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; request->reply->dst_port = request->packet->dst_port; return 1; } /* * 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 == PW_DHCP_NAK) || ((vp = pairfind(request->reply->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Flags */ (vp->vp_integer & 0x8000) && ((vp = pairfind(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */ (vp->vp_ipaddr == htonl(INADDR_ANY))))) { /* * RFC 2131, page 23 * * Broadcast on * - DHCPNAK * or * - Broadcast flag is set up and ciaddr == NULL */ RDEBUG("DHCP: Reply will be broadcast"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST); return 1; } /* * RFC 2131, page 23 * * Unicast to ciaddr if present, otherwise to yiaddr. */ if ((vp = pairfind(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */ (vp->vp_ipaddr != htonl(INADDR_ANY))) { RDEBUG("DHCP: Reply will be sent unicast to client-ip-address"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; return 1; } vp = pairfind(request->reply->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */ if (!vp) { RDEBUG("DHCP: Failed to find DHCP-Client-IP-Address or DHCP-Your-IP-Address for request; " "not responding."); /* * There is nowhere to send the response to, so don't bother. */ request->reply->code = 0; return -1; } #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\""); RDEBUG("DHCP: Reply will be broadcast as no interface was defined"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST); return 1; } RDEBUG("DHCP: Reply will be unicast to your-ip-address"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; /* * 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 == PW_DHCP_OFFER) { VALUE_PAIR *hwvp = pairfind(request->reply->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */ if (!hwvp) return -1; if (fr_dhcp_add_arp_entry(request->reply->sockfd, sock->src_interface, hwvp, vp) < 0) { RDEBUG("Failed adding arp entry: %s", fr_strerror()); return -1; } } #else if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr != ntohl(INADDR_NONE)) { RDEBUG("DHCP: Request will be unicast to the unicast source IP address"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr; } else { RDEBUG("DHCP: Reply will be broadcast as this system does not support ARP updates"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST); } #endif return 1; }
static int dhcp_process(REQUEST *request) { int rcode; unsigned int i; VALUE_PAIR *vp; dhcp_socket_t *sock; vp = pairfind(request->packet->vps, DHCP2ATTR(53)); /* DHCP-Message-Type */ if (vp) { DICT_VALUE *dv = dict_valbyattr(DHCP2ATTR(53), vp->vp_integer); DEBUG("Trying sub-section dhcp %s {...}", dv->name ? dv->name : "<unknown>"); rcode = module_post_auth(vp->vp_integer, request); } else { DEBUG("DHCP: Failed to find DHCP-Message-Type in packet!"); rcode = RLM_MODULE_FAIL; } vp = pairfind(request->reply->vps, DHCP2ATTR(53)); /* DHCP-Message-Type */ if (vp) { request->reply->code = vp->vp_integer; if ((request->reply->code != 0) && (request->reply->code < PW_DHCP_OFFSET)) { request->reply->code += PW_DHCP_OFFSET; } } else switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: if (request->packet->code == PW_DHCP_DISCOVER) { request->reply->code = PW_DHCP_OFFER; break; } else if (request->packet->code == PW_DHCP_REQUEST) { request->reply->code = PW_DHCP_ACK; break; } request->reply->code = PW_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 == PW_DHCP_DISCOVER) { request->reply->code = 0; /* ignore the packet */ } else { request->reply->code = PW_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 = pairfind(request->packet->vps, DHCP2ATTR(256)); /* DHCP-Opcode */ if (!vp) { RDEBUG("FAILURE: Someone deleted the DHCP-Opcode!"); return 1; } /* BOOTREPLY received on port 67 (i.e. from a server) */ if (vp->vp_integer == 2) { return dhcprelay_process_server_reply(request); } /* Packet from client, and we have DHCP-Relay-To-IP-Address */ if (pairfind(request->config_items, DHCP2ATTR(270))) { return dhcprelay_process_client_request(request); } /* else it's a packet from a client, without relaying */ rad_assert(vp->vp_integer == 1); /* BOOTREQUEST */ sock = request->listener->data; /* * Handle requests when acting as a DHCP server */ /* * Releases don't get replies. */ if (request->packet->code == PW_DHCP_RELEASE) { request->reply->code = 0; } if (request->reply->code == 0) { return 1; } 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 (pairfind(request->reply->vps, DHCP2ATTR(attr))) continue; if ((vp = pairfind(request->packet->vps, DHCP2ATTR(attr)))) { pairadd(&request->reply->vps, paircopyvp(vp)); } } vp = pairfind(request->reply->vps, DHCP2ATTR(256)); /* DHCP-Opcode */ rad_assert(vp != NULL); vp->vp_integer = 2; /* BOOTREPLY */ /* * 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.ipaddr.ip4addr.s_addr = sock->src_ipaddr.ipaddr.ip4addr.s_addr; request->reply->dst_port = request->packet->src_port; request->reply->src_port = request->packet->dst_port; vp = pairfind(request->reply->vps, DHCP2ATTR(266)); /* DHCP-Gateway-IP-Address */ if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) { /* Answer to client's nearest DHCP relay */ request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; } else if ((request->reply->code == PW_DHCP_NAK) || ((vp = pairfind(request->reply->vps, DHCP2ATTR(262))) /* DHCP-Flags */ && (vp->vp_integer & 0x8000) && ((vp = pairfind(request->reply->vps, DHCP2ATTR(263))) /* DHCP-Client-IP-Address */ && (vp->vp_ipaddr == htonl(INADDR_ANY))))) { /* * RFC 2131, page 23 * * Broadcast on * - DHCPNAK * or * - Broadcast flag is set up and ciaddr == NULL */ request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST); } else { /* * RFC 2131, page 23 * * Unicast to * - ciaddr if present * otherwise to yiaddr */ if ((vp = pairfind(request->reply->vps, DHCP2ATTR(263))) /* DHCP-Client-IP-Address */ && (vp->vp_ipaddr != htonl(INADDR_ANY))) { request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; } else { vp = pairfind(request->reply->vps, DHCP2ATTR(264)); /* DHCP-Your-IP-Address */ if (!vp) { DEBUG("DHCP: Failed to find IP Address for request."); return -1; } request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; /* * 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->reply->code == PW_DHCP_OFFER) { VALUE_PAIR *hwvp = pairfind(request->reply->vps, DHCP2ATTR(267)); /* DHCP-Client-Hardware-Address */ if (!hwvp) return -1; if (fr_dhcp_add_arp_entry(request->reply->sockfd, sock->src_interface, hwvp, vp) < 0) { return -1; } } } } return 1; }