2 * dhcp.c DHCP processing.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2008 The FreeRADIUS server project
21 * Copyright 2008,2011 Alan DeKok <aland@deployingradius.com>
26 * INADDR_ANY : 68 -> INADDR_BROADCAST : 67 DISCOVER
27 * CLIENT_IP : 68 <- DHCP_SERVER_IP : 67 OFFER
28 * INADDR_ANY : 68 -> INADDR_BROADCAST : 67 REQUEST
29 * CLIENT_IP : 68 <- DHCP_SERVER_IP : 67 ACK
32 * INADDR_ANY : 68 -> INADDR_BROADCAST : 67 DISCOVER
33 * RELAY_IP : 67 -> NEXT_SERVER_IP : 67 DISCOVER
34 * (NEXT_SERVER_IP can be a relay itself)
35 * FIRST_RELAY_IP : 67 <- DHCP_SERVER_IP : 67 OFFER
36 * CLIENT_IP : 68 <- FIRST_RELAY_IP : 67 OFFER
37 * INADDR_ANY : 68 -> INADDR_BROADCAST : 67 REQUEST
38 * RELAY_IP : 67 -> NEXT_SERVER_IP : 67 REQUEST
39 * (NEXT_SERVER_IP can be a relay itself)
40 * FIRST_RELAY_IP : 67 <- DHCP_SERVER_IP : 67 ACK
41 * CLIENT_IP : 68 <- FIRST_RELAY_IP : 67 ACK
43 * Note: NACK are broadcasted, rest is unicast, unless client asked
48 #include <freeradius-devel/radiusd.h>
49 #include <freeradius-devel/modules.h>
50 #include <freeradius-devel/protocol.h>
51 #include <freeradius-devel/process.h>
52 #include <freeradius-devel/dhcp.h>
53 #include <freeradius-devel/rad_assert.h>
56 extern int check_config; /* @todo globals are bad, m'kay? */
59 * Same contents as listen_socket_t.
61 typedef struct dhcp_socket_t {
67 const char *interface;
68 RADCLIENT_LIST *clients;
71 * DHCP-specific additions.
73 int suppress_responses;
74 RADCLIENT dhcp_client;
75 const char *src_interface;
76 fr_ipaddr_t src_ipaddr;
80 static int dhcprelay_process_client_request(REQUEST *request)
83 VALUE_PAIR *vp, *giaddrvp;
86 rad_assert(request->packet->data[0] == 1);
89 * Do the forward by ourselves, do not rely on dhcp_socket_send()
91 request->reply->code = 0;
94 * It's invalid to have giaddr=0 AND a relay option
96 giaddrvp = vp = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
97 if (vp && (vp->vp_ipaddr == htonl(INADDR_ANY)) &&
98 pairfind(request->packet->vps, 82, DHCP_MAGIC_VENDOR, TAG_ANY)) { /* DHCP-Relay-Agent-Information */
99 DEBUG("DHCP: Received packet with giaddr = 0 and containing relay option: Discarding packet\n");
104 * RFC 1542 (BOOTP), page 15
106 * Drop requests if hop-count > 16 or admin specified another value
108 if ((vp = pairfind(request->config_items, 271, DHCP_MAGIC_VENDOR, TAG_ANY))) { /* DHCP-Relay-Max-Hop-Count */
109 maxhops = vp->vp_integer;
111 vp = pairfind(request->packet->vps, 259, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Hop-Count */
112 rad_assert(vp != NULL);
113 if (vp->vp_integer > maxhops) {
114 DEBUG("DHCP: Number of hops is greater than %d: not relaying\n", maxhops);
117 /* Increment hop count */
121 sock = request->listener->data;
124 * Forward the request to the next server using the
125 * incoming request as a template.
127 /* set SRC ipaddr/port to the listener ipaddr/port */
128 request->packet->src_ipaddr.af = AF_INET;
129 request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = giaddrvp->vp_ipaddr;
130 request->packet->src_port = sock->port;
132 vp = pairfind(request->config_items, 270, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-To-IP-Address */
133 rad_assert(vp != NULL);
135 /* set DEST ipaddr/port to the next server ipaddr/port */
136 request->packet->dst_ipaddr.af = AF_INET;
137 request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
139 if (fr_dhcp_encode(request->packet) < 0) {
140 DEBUG("dhcprelay_process_client_request: ERROR in fr_dhcp_encode\n");
144 return fr_dhcp_send(request->packet);
147 static int dhcprelay_process_server_reply(REQUEST *request)
149 VALUE_PAIR *vp, *giaddrvp;
152 rad_assert(request->packet->data[0] == 2);
155 * Do the forward by ourselves, do not rely on dhcp_socket_send()
157 request->reply->code = 0;
159 sock = request->listener->data;
162 * Check that packet is for us.
164 giaddrvp = vp = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
165 rad_assert(vp != NULL);
167 /* --with-udpfromto is needed just for the following test */
168 if (!vp || vp->vp_ipaddr != request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr) {
169 DEBUG("DHCP: Packet received from server was not for us (was for 0x%x). Discarding packet",
170 ntohl(request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr));
174 /* set SRC ipaddr/port to the listener ipaddr/port */
175 request->packet->src_ipaddr.af = AF_INET;
176 request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = giaddrvp->vp_ipaddr;
177 request->packet->src_port = sock->port;
179 /* set DEST ipaddr/port to clientip/68 or broadcast in specific cases */
180 request->packet->dst_ipaddr.af = AF_INET;
181 request->packet->dst_port = request->packet->dst_port + 1; /* Port 68 */
183 if ((request->packet->code == PW_DHCP_NAK) ||
184 ((vp = pairfind(request->packet->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Flags */ &&
185 (vp->vp_integer & 0x8000) &&
186 ((vp = pairfind(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
187 (vp->vp_ipaddr == htonl(INADDR_ANY))))) {
194 * - Broadcast flag is set up and ciaddr == NULL
196 RDEBUG("DHCP: response will be broadcast");
197 request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
203 * - ciaddr if present
204 * otherwise to yiaddr
206 if ((vp = pairfind(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
207 (vp->vp_ipaddr != htonl(INADDR_ANY))) {
208 request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
210 vp = pairfind(request->packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
212 DEBUG("DHCP: Failed to find IP Address for request.");
216 RDEBUG("DHCP: response will be unicast to your-ip-address");
217 request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
220 * When sending a DHCP_OFFER, make sure our ARP table
221 * contains an entry for the client IP address, or else
222 * packet may not be forwarded if it was the first time
223 * the client was requesting an IP address.
225 if (request->packet->code == PW_DHCP_OFFER) {
226 VALUE_PAIR *hwvp = pairfind(request->packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */
228 DEBUG("DHCP: DHCP_OFFER packet received with "
229 "no Client Hardware Address. Discarding packet");
232 if (fr_dhcp_add_arp_entry(request->packet->sockfd, sock->src_interface, hwvp, vp) < 0) {
233 DEBUG("%s", fr_strerror());
240 if (fr_dhcp_encode(request->packet) < 0) {
241 DEBUG("dhcprelay_process_server_reply: ERROR in fr_dhcp_encode\n");
245 return fr_dhcp_send(request->packet);
247 #else /* WITH_UDPFROMTO */
248 static int dhcprelay_process_server_reply(UNUSED REQUEST *request)
250 DEBUG("WARNING: DHCP Relaying requires the server to be configured with UDPFROMTO");
254 static int dhcprelay_process_client_request(UNUSED REQUEST *request)
256 DEBUG("WARNING: DHCP Relaying requires the server to be configured with UDPFROMTO");
260 #endif /* WITH_UDPFROMTO */
262 static const uint32_t attrnums[] = {
263 57, /* DHCP-DHCP-Maximum-Msg-Size */
264 256, /* DHCP-Opcode */
265 257, /* DHCP-Hardware-Type */
266 258, /* DHCP-Hardware-Address-Length */
267 259, /* DHCP-Hop-Count */
268 260, /* DHCP-Transaction-Id */
269 262, /* DHCP-Flags */
270 263, /* DHCP-Client-IP-Address */
271 266, /* DHCP-Gateway-IP-Address */
272 267 /* DHCP-Client-Hardware-Address */
275 static int dhcp_process(REQUEST *request)
283 * If there's a giaddr, save it as the Relay-IP-Address
284 * in the response. That way the later code knows where
287 vp = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
288 if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) {
291 /* DHCP-Relay-IP-Address */
292 relay = radius_paircreate(request, &request->reply->vps,
293 272, DHCP_MAGIC_VENDOR);
294 if (relay) relay->vp_ipaddr = vp->vp_ipaddr;
297 vp = pairfind(request->packet->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */
299 DICT_VALUE *dv = dict_valbyattr(53, DHCP_MAGIC_VENDOR, vp->vp_integer);
300 DEBUG("Trying sub-section dhcp %s {...}",
301 dv->name ? dv->name : "<unknown>");
302 rcode = process_post_auth(vp->vp_integer, request);
304 DEBUG("DHCP: Failed to find DHCP-Message-Type in packet!");
305 rcode = RLM_MODULE_FAIL;
308 vp = pairfind(request->reply->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */
310 request->reply->code = vp->vp_integer;
311 if ((request->reply->code != 0) &&
312 (request->reply->code < PW_DHCP_OFFSET)) {
313 request->reply->code += PW_DHCP_OFFSET;
316 else switch (rcode) {
318 case RLM_MODULE_UPDATED:
319 if (request->packet->code == PW_DHCP_DISCOVER) {
320 request->reply->code = PW_DHCP_OFFER;
323 } else if (request->packet->code == PW_DHCP_REQUEST) {
324 request->reply->code = PW_DHCP_ACK;
327 request->reply->code = PW_DHCP_NAK;
331 case RLM_MODULE_REJECT:
332 case RLM_MODULE_FAIL:
333 case RLM_MODULE_INVALID:
334 case RLM_MODULE_NOOP:
335 case RLM_MODULE_NOTFOUND:
336 if (request->packet->code == PW_DHCP_DISCOVER) {
337 request->reply->code = 0; /* ignore the packet */
339 request->reply->code = PW_DHCP_NAK;
343 case RLM_MODULE_HANDLED:
344 request->reply->code = 0; /* ignore the packet */
349 * TODO: Handle 'output' of RLM_MODULE when acting as a
350 * DHCP relay We may want to not forward packets in
351 * certain circumstances.
355 * Handle requests when acting as a DHCP relay
357 vp = pairfind(request->packet->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */
359 RDEBUG("FAILURE: Someone deleted the DHCP-Opcode!");
363 /* BOOTREPLY received on port 67 (i.e. from a server) */
364 if (vp->vp_integer == 2) {
365 return dhcprelay_process_server_reply(request);
368 /* Packet from client, and we have DHCP-Relay-To-IP-Address */
369 if (pairfind(request->config_items, 270, DHCP_MAGIC_VENDOR, TAG_ANY)) {
370 return dhcprelay_process_client_request(request);
373 /* else it's a packet from a client, without relaying */
374 rad_assert(vp->vp_integer == 1); /* BOOTREQUEST */
376 sock = request->listener->data;
379 * Handle requests when acting as a DHCP server
383 * Releases don't get replies.
385 if (request->packet->code == PW_DHCP_RELEASE) {
386 request->reply->code = 0;
389 if (request->reply->code == 0) {
393 request->reply->sockfd = request->packet->sockfd;
396 * Copy specific fields from packet to reply, if they
397 * don't already exist
399 for (i = 0; i < sizeof(attrnums) / sizeof(attrnums[0]); i++) {
400 uint32_t attr = attrnums[i];
402 if (pairfind(request->reply->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY)) continue;
404 vp = pairfind(request->packet->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY);
406 pairadd(&request->reply->vps, paircopyvp(request->reply, vp));
410 vp = pairfind(request->reply->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */
411 rad_assert(vp != NULL);
412 vp->vp_integer = 2; /* BOOTREPLY */
415 * Prepare the reply packet for sending through dhcp_socket_send()
417 request->reply->dst_ipaddr.af = AF_INET;
418 request->reply->src_ipaddr.af = AF_INET;
419 request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = sock->src_ipaddr.ipaddr.ip4addr.s_addr;
421 request->reply->dst_port = request->packet->src_port;
422 request->reply->src_port = request->packet->dst_port;
425 * Answer to client's nearest DHCP relay.
427 * Which may be different than the giaddr given in the
428 * packet to the client. i.e. the relay may have a
429 * public IP, but the gateway a private one.
431 vp = pairfind(request->reply->vps, 272, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-IP-Address */
433 RDEBUG("DHCP: Reply will be unicast to giaddr from original packet");
434 request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
439 * Answer to client's nearest DHCP gateway. In this
440 * case, the client can reach the gateway, as can the
443 vp = pairfind(request->reply->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
444 if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) {
445 RDEBUG("DHCP: Reply will be unicast to giaddr");
446 request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
451 * If it's a NAK, or the broadcast flag was set, ond
452 * there's no client-ip-address, send a broadcast.
454 if ((request->reply->code == PW_DHCP_NAK) ||
455 ((vp = pairfind(request->reply->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Flags */
456 (vp->vp_integer & 0x8000) &&
457 ((vp = pairfind(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */
458 (vp->vp_ipaddr == htonl(INADDR_ANY))))) {
465 * - Broadcast flag is set up and ciaddr == NULL
467 RDEBUG("DHCP: Reply will be broadcast");
468 request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
475 * Unicast to ciaddr if present, otherwise to yiaddr.
477 if ((vp = pairfind(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */
478 (vp->vp_ipaddr != htonl(INADDR_ANY))) {
479 RDEBUG("DHCP: Reply will be sent unicast to client-ip-address");
480 request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
484 vp = pairfind(request->reply->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
486 DEBUG("DHCP: Failed to find DHCP-Your-IP-Address for request.");
490 RDEBUG("DHCP: Reply will be sent unicast to your-ip-address");
491 request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
494 * When sending a DHCP_OFFER, make sure our ARP table
495 * contains an entry for the client IP address.
496 * Otherwise the packet may not be sent to the client, as
497 * the OS has no ARP entry for it.
499 * This is a cute hack to avoid us having to create a raw
500 * socket to send DHCP packets.
502 if (request->reply->code == PW_DHCP_OFFER) {
503 VALUE_PAIR *hwvp = pairfind(request->reply->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */
505 if (!hwvp) return -1;
507 if (fr_dhcp_add_arp_entry(request->reply->sockfd, sock->src_interface, hwvp, vp) < 0) {
515 static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
517 int rcode, broadcast = 1;
523 rcode = common_socket_parse(cs, this);
524 if (rcode != 0) return rcode;
526 if (check_config) return 0;
530 if (!sock->interface) {
531 DEBUG("WARNING: No \"interface\" setting is defined. Only unicast DHCP will work.");
535 * See whether or not we enable broadcast packets.
537 cp = cf_pair_find(cs, "broadcast");
539 const char *value = cf_pair_value(cp);
540 if (value && (strcmp(value, "no") == 0)) {
546 if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
547 DEBUGE("Can't set broadcast option: %s\n",
553 if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
554 DEBUGE("Can't set re-use addres option: %s\n",
560 * Undocumented extension for testing without
561 * destroying your network!
563 sock->suppress_responses = FALSE;
564 cp = cf_pair_find(cs, "suppress_responses");
566 cf_item_parse(cs, "suppress_responses", PW_TYPE_BOOLEAN,
567 &sock->suppress_responses, NULL);
570 cp = cf_pair_find(cs, "src_interface");
572 cf_item_parse(cs, "src_interface", PW_TYPE_STRING_PTR,
573 &sock->src_interface, NULL);
575 sock->src_interface = sock->interface;
578 if (!sock->src_interface && sock->interface) {
579 sock->src_interface = talloc_strdup(sock, sock->interface);
582 cp = cf_pair_find(cs, "src_ipaddr");
584 memset(&sock->src_ipaddr, 0, sizeof(sock->src_ipaddr));
585 sock->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
586 rcode = cf_item_parse(cs, "src_ipaddr", PW_TYPE_IPADDR,
587 &sock->src_ipaddr.ipaddr.ip4addr, NULL);
588 if (rcode < 0) return -1;
590 sock->src_ipaddr.af = AF_INET;
592 memcpy(&sock->src_ipaddr, &sock->ipaddr, sizeof(sock->src_ipaddr));
596 * Initialize the fake client.
598 client = &sock->dhcp_client;
599 memset(client, 0, sizeof(*client));
600 client->ipaddr.af = AF_INET;
601 client->ipaddr.ipaddr.ip4addr.s_addr = INADDR_NONE;
603 client->longname = client->shortname = "dhcp";
604 client->secret = client->shortname;
605 client->nastype = talloc_strdup(sock, "none");
612 * Check if an incoming request is "ok"
614 * It takes packets, not requests. It sees if the packet looks
615 * OK. If so, it does a number of sanity checks on it.
617 static int dhcp_socket_recv(rad_listen_t *listener)
619 RADIUS_PACKET *packet;
622 packet = fr_dhcp_recv(listener->fd);
624 DEBUGE("%s", fr_strerror());
628 sock = listener->data;
629 if (!request_receive(listener, packet, &sock->dhcp_client, dhcp_process)) {
639 * Send an authentication response packet
641 static int dhcp_socket_send(rad_listen_t *listener, REQUEST *request)
645 rad_assert(request->listener == listener);
646 rad_assert(listener->send == dhcp_socket_send);
648 if (request->reply->code == 0) return 0; /* don't reply */
650 if (fr_dhcp_encode(request->packet) < 0) {
651 DEBUG("dhcp_socket_send: ERROR\n");
654 sock = listener->data;
655 if (sock->suppress_responses) return 0;
657 return fr_dhcp_send(request->reply);
661 static int dhcp_socket_encode(UNUSED rad_listen_t *listener, UNUSED REQUEST *request)
663 DEBUG2("NO ENCODE!");
668 static int dhcp_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
670 return fr_dhcp_decode(request->packet);
673 fr_protocol_t proto_dhcp = {
676 sizeof(dhcp_socket_t),
678 dhcp_socket_parse, NULL,
679 dhcp_socket_recv, dhcp_socket_send,
680 common_socket_print, dhcp_socket_encode, dhcp_socket_decode