Dst seems to be INADDR_ANY in some cases... Weird.
[freeradius.git] / src / modules / proto_dhcp / dhcpd.c
1 /*
2  * dhcp.c       DHCP processing.
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2008 The FreeRADIUS server project
21  * Copyright 2008,2011 Alan DeKok <aland@deployingradius.com>
22  */
23
24 /*
25  * Standard sequence:
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
30  *
31  * Relay sequence:
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
42  *
43  * Note: NACK are broadcasted, rest is unicast, unless client asked
44  * for a broadcast
45  */
46
47
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>
54
55 #ifndef __MINGW32__
56 #include <sys/ioctl.h>
57 #endif
58
59 /*
60  *      Same contents as listen_socket_t.
61  */
62 typedef struct dhcp_socket_t {
63         listen_socket_t lsock;
64
65         /*
66          *      DHCP-specific additions.
67          */
68         bool            suppress_responses;
69         RADCLIENT       dhcp_client;
70         char const      *src_interface;
71         fr_ipaddr_t     src_ipaddr;
72 } dhcp_socket_t;
73
74 #ifdef WITH_UDPFROMTO
75 static int dhcprelay_process_client_request(REQUEST *request)
76 {
77         uint8_t maxhops = 16;
78         VALUE_PAIR *vp, *giaddr;
79         dhcp_socket_t *sock;
80
81         rad_assert(request->packet->data[0] == 1);
82
83         /*
84          *      Do the forward by ourselves, do not rely on dhcp_socket_send()
85          */
86         request->reply->code = 0;
87
88         /*
89          * It's invalid to have giaddr=0 AND a relay option
90          */
91         giaddr = fr_pair_find_by_num(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
92         if (giaddr && (giaddr->vp_ipaddr == htonl(INADDR_ANY)) &&
93             fr_pair_find_by_num(request->packet->vps, 82, DHCP_MAGIC_VENDOR, TAG_ANY)) { /* DHCP-Relay-Agent-Information */
94                 DEBUG("DHCP: Received packet with giaddr = 0 and containing relay option: Discarding packet\n");
95                 return 1;
96         }
97
98         /*
99          * RFC 1542 (BOOTP), page 15
100          *
101          * Drop requests if hop-count > 16 or admin specified another value
102          */
103         if ((vp = fr_pair_find_by_num(request->config, 271, DHCP_MAGIC_VENDOR, TAG_ANY))) { /* DHCP-Relay-Max-Hop-Count */
104             maxhops = vp->vp_integer;
105         }
106         vp = fr_pair_find_by_num(request->packet->vps, 259, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Hop-Count */
107         rad_assert(vp != NULL);
108         if (vp->vp_integer > maxhops) {
109                 DEBUG("DHCP: Number of hops is greater than %d: not relaying\n", maxhops);
110                 return 1;
111         } else {
112             /* Increment hop count */
113             vp->vp_integer++;
114         }
115
116         sock = request->listener->data;
117
118         /*
119          *      Forward the request to the next server using the
120          *      incoming request as a template.
121          */
122         /* set SRC ipaddr/port to the listener ipaddr/port */
123         request->packet->src_ipaddr.af = AF_INET;
124         request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = sock->lsock.my_ipaddr.ipaddr.ip4addr.s_addr;
125         request->packet->src_port = sock->lsock.my_port;
126
127         vp = fr_pair_find_by_num(request->config, 270, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-To-IP-Address */
128         rad_assert(vp != NULL);
129
130         /* set DEST ipaddr/port to the next server ipaddr/port */
131         request->packet->dst_ipaddr.af = AF_INET;
132         request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
133         request->packet->dst_port = sock->lsock.my_port;
134
135         if (fr_dhcp_encode(request->packet) < 0) {
136                 DEBUG("dhcprelay_process_client_request: ERROR in fr_dhcp_encode\n");
137                 return -1;
138         }
139
140         return fr_dhcp_send(request->packet);
141 }
142
143
144 /*
145  *      We've seen a reply from a server.
146  *      i.e. we're a relay.
147  */
148 static int dhcprelay_process_server_reply(REQUEST *request)
149 {
150         VALUE_PAIR *vp, *giaddr;
151         dhcp_socket_t *sock;
152
153         rad_assert(request->packet->data[0] == 2);
154
155         /*
156          * Do the forward by ourselves, do not rely on dhcp_socket_send()
157          */
158         request->reply->code = 0;
159
160         sock = request->listener->data;
161
162         /*
163          * Check that packet is for us.
164          */
165         giaddr = fr_pair_find_by_num(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
166
167         /* --with-udpfromto is needed just for the following test */
168         if (!giaddr || giaddr->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));
171                 return 1;
172         }
173
174         /* set SRC ipaddr/port to the listener ipaddr/port */
175         request->packet->src_ipaddr.af = AF_INET;
176         request->packet->src_port = sock->lsock.my_port;
177
178         /* set DEST ipaddr/port to clientip/68 or broadcast in specific cases */
179         request->packet->dst_ipaddr.af = AF_INET;
180
181         /*
182          *      We're a relay, and send the reply to giaddr.
183          */
184         request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = giaddr->vp_ipaddr;
185         request->reply->dst_port = request->packet->dst_port;           /* server port */
186
187         if ((request->packet->code == PW_DHCP_NAK) ||
188             !sock->src_interface ||
189             ((vp = fr_pair_find_by_num(request->packet->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Flags */ &&
190              (vp->vp_integer & 0x8000) &&
191              ((vp = fr_pair_find_by_num(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
192               (vp->vp_ipaddr == htonl(INADDR_ANY))))) {
193                 /*
194                  * RFC 2131, page 23
195                  *
196                  * Broadcast on
197                  * - DHCPNAK
198                  * or
199                  * - Broadcast flag is set up and ciaddr == NULL
200                  */
201                 RDEBUG("DHCP: response will be  broadcast");
202                 request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
203         } else {
204                 /*
205                  * RFC 2131, page 23
206                  *
207                  * Unicast to
208                  * - ciaddr if present
209                  * otherwise to yiaddr
210                  */
211                 if ((vp = fr_pair_find_by_num(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
212                     (vp->vp_ipaddr != htonl(INADDR_ANY))) {
213                         request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
214                 } else {
215                         vp = fr_pair_find_by_num(request->packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
216                         if (!vp) {
217                                 DEBUG("DHCP: Failed to find IP Address for request");
218                                 return -1;
219                         }
220
221                         RDEBUG("DHCP: response will be unicast to your-ip-address");
222                         request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
223
224                         /*
225                          * When sending a DHCP_OFFER, make sure our ARP table
226                          * contains an entry for the client IP address, or else
227                          * packet may not be forwarded if it was the first time
228                          * the client was requesting an IP address.
229                          */
230                         if (request->packet->code == PW_DHCP_OFFER) {
231                                 VALUE_PAIR *hwvp = fr_pair_find_by_num(request->packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */
232                                 if (hwvp == NULL) {
233                                         DEBUG("DHCP: DHCP_OFFER packet received with "
234                                             "no Client Hardware Address. Discarding packet");
235                                         return 1;
236                                 }
237                                 if (fr_dhcp_add_arp_entry(request->packet->sockfd, sock->src_interface, hwvp, vp) < 0) {
238                                         DEBUG("%s", fr_strerror());
239                                         return -1;
240                                 }
241                         }
242                 }
243         }
244
245         if (fr_dhcp_encode(request->packet) < 0) {
246                 DEBUG("dhcprelay_process_server_reply: ERROR in fr_dhcp_encode\n");
247                 return -1;
248         }
249
250         return fr_dhcp_send(request->packet);
251 }
252 #else  /* WITH_UDPFROMTO */
253 static int dhcprelay_process_server_reply(UNUSED REQUEST *request)
254 {
255         WARN("DHCP Relaying requires the server to be configured with UDPFROMTO");
256         return -1;
257 }
258
259 static int dhcprelay_process_client_request(UNUSED REQUEST *request)
260 {
261         WARN("DHCP Relaying requires the server to be configured with UDPFROMTO");
262         return -1;
263 }
264
265 #endif  /* WITH_UDPFROMTO */
266
267 static const uint32_t attrnums[] = {
268         57,     /* DHCP-DHCP-Maximum-Msg-Size */
269         256,    /* DHCP-Opcode */
270         257,    /* DHCP-Hardware-Type */
271         258,    /* DHCP-Hardware-Address-Length */
272         259,    /* DHCP-Hop-Count */
273         260,    /* DHCP-Transaction-Id */
274         262,    /* DHCP-Flags */
275         263,    /* DHCP-Client-IP-Address */
276         266,    /* DHCP-Gateway-IP-Address */
277         267     /* DHCP-Client-Hardware-Address */
278 };
279
280 static int dhcp_process(REQUEST *request)
281 {
282         int rcode;
283         unsigned int i;
284         VALUE_PAIR *vp;
285         dhcp_socket_t *sock;
286
287         /*
288          *      If there's a giaddr, save it as the Relay-IP-Address
289          *      in the response.  That way the later code knows where
290          *      to send the reply.
291          */
292         vp = fr_pair_find_by_num(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
293         if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) {
294                 VALUE_PAIR *relay;
295
296                 /* DHCP-Relay-IP-Address */
297                 relay = radius_pair_create(request->reply, &request->reply->vps,
298                                           272, DHCP_MAGIC_VENDOR);
299                 if (relay) relay->vp_ipaddr = vp->vp_ipaddr;
300         }
301
302         vp = fr_pair_find_by_num(request->packet->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */
303         if (vp) {
304                 DICT_VALUE *dv = dict_valbyattr(53, DHCP_MAGIC_VENDOR, vp->vp_integer);
305                 DEBUG("Trying sub-section dhcp %s {...}",
306                       dv ? dv->name : "<unknown>");
307                 rcode = process_post_auth(vp->vp_integer, request);
308         } else {
309                 DEBUG("DHCP: Failed to find DHCP-Message-Type in packet!");
310                 rcode = RLM_MODULE_FAIL;
311         }
312
313         vp = fr_pair_find_by_num(request->reply->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */
314         if (vp) {
315                 request->reply->code = vp->vp_integer;
316                 if ((request->reply->code != 0) &&
317                     (request->reply->code < PW_DHCP_OFFSET)) {
318                         request->reply->code += PW_DHCP_OFFSET;
319                 }
320         }
321         else switch (rcode) {
322         case RLM_MODULE_OK:
323         case RLM_MODULE_UPDATED:
324                 if (request->packet->code == PW_DHCP_DISCOVER) {
325                         request->reply->code = PW_DHCP_OFFER;
326                         break;
327
328                 } else if (request->packet->code == PW_DHCP_REQUEST) {
329                         request->reply->code = PW_DHCP_ACK;
330                         break;
331                 }
332                 request->reply->code = PW_DHCP_NAK;
333                 break;
334
335         default:
336         case RLM_MODULE_REJECT:
337         case RLM_MODULE_FAIL:
338         case RLM_MODULE_INVALID:
339         case RLM_MODULE_NOOP:
340         case RLM_MODULE_NOTFOUND:
341                 if (request->packet->code == PW_DHCP_DISCOVER) {
342                         request->reply->code = 0; /* ignore the packet */
343                 } else {
344                         request->reply->code = PW_DHCP_NAK;
345                 }
346                 break;
347
348         case RLM_MODULE_HANDLED:
349                 request->reply->code = 0; /* ignore the packet */
350                 break;
351         }
352
353         /*
354          *      TODO: Handle 'output' of RLM_MODULE when acting as a
355          *      DHCP relay We may want to not forward packets in
356          *      certain circumstances.
357          */
358
359         /*
360          *      Handle requests when acting as a DHCP relay
361          */
362         vp = fr_pair_find_by_num(request->packet->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */
363         if (!vp) {
364                 RDEBUG("FAILURE: Someone deleted the DHCP-Opcode!");
365                 return 1;
366         }
367
368         /* BOOTREPLY received on port 67 (i.e. from a server) */
369         if (vp->vp_integer == 2) {
370                 return dhcprelay_process_server_reply(request);
371         }
372
373         /* Packet from client, and we have DHCP-Relay-To-IP-Address */
374         if (fr_pair_find_by_num(request->config, 270, DHCP_MAGIC_VENDOR, TAG_ANY)) {
375                 return dhcprelay_process_client_request(request);
376         }
377
378         /* else it's a packet from a client, without relaying */
379         rad_assert(vp->vp_integer == 1); /* BOOTREQUEST */
380
381         sock = request->listener->data;
382
383         /*
384          *      Handle requests when acting as a DHCP server
385          */
386
387         /*
388          *      Releases don't get replies.
389          */
390         if (request->packet->code == PW_DHCP_RELEASE) {
391                 request->reply->code = 0;
392         }
393
394         if (request->reply->code == 0) {
395                 return 1;
396         }
397
398         request->reply->sockfd = request->packet->sockfd;
399
400         /*
401          *      Copy specific fields from packet to reply, if they
402          *      don't already exist
403          */
404         for (i = 0; i < sizeof(attrnums) / sizeof(attrnums[0]); i++) {
405                 uint32_t attr = attrnums[i];
406
407                 if (fr_pair_find_by_num(request->reply->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY)) continue;
408
409                 vp = fr_pair_find_by_num(request->packet->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY);
410                 if (vp) {
411                         fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp));
412                 }
413         }
414
415         vp = fr_pair_find_by_num(request->reply->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */
416         rad_assert(vp != NULL);
417         vp->vp_integer = 2; /* BOOTREPLY */
418
419         /*
420          *      Allow NAKs to be delayed for a short period of time.
421          */
422         if (request->reply->code == PW_DHCP_NAK) {
423                 vp = fr_pair_find_by_num(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY, 0, TAG_ANY);
424                 if (vp) {
425                         if (vp->vp_integer <= 10) {
426                                 request->response_delay.tv_sec = vp->vp_integer;
427                                 request->response_delay.tv_usec = 0;
428                         } else {
429                                 request->response_delay.tv_sec = 10;
430                                 request->response_delay.tv_usec = 0;
431                         }
432                 } else {
433 #define USEC 1000000
434                         vp = fr_pair_find_by_num(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY_USEC, 0, TAG_ANY);
435                         if (vp) {
436                                 if (vp->vp_integer <= 10 * USEC) {
437                                         request->response_delay.tv_sec = vp->vp_integer / USEC;
438                                         request->response_delay.tv_usec = vp->vp_integer % USEC;
439                                 } else {
440                                         request->response_delay.tv_sec = 10;
441                                         request->response_delay.tv_usec = 0;
442                                 }
443                         }
444                 }
445         }
446
447         /*
448          *      Prepare the reply packet for sending through dhcp_socket_send()
449          */
450         request->reply->dst_ipaddr.af = AF_INET;
451         request->reply->src_ipaddr.af = AF_INET;
452         request->reply->src_ipaddr.prefix = 32;
453
454         /*
455          *      Packet-Src-IP-Address has highest precedence
456          */
457         vp = fr_pair_find_by_num(request->reply->vps, PW_PACKET_SRC_IP_ADDRESS, 0, TAG_ANY);
458         if (vp) {
459                 request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
460         /*
461          *      The request was unicast (via a relay)
462          */
463         } else if (request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_BROADCAST) &&
464                    request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_ANY)) {
465                 request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
466         /*
467          *      The listener was bound to an IP address, or we determined
468          *      the address automatically, as it was the only address bound
469          *      to the interface, and we bound to the interface.
470          */
471         } else if (sock->src_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_ANY)) {
472                 request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = sock->src_ipaddr.ipaddr.ip4addr.s_addr;
473         /*
474          *      There's a Server-Identification attribute
475          */
476         } else if ((vp = fr_pair_find_by_num(request->reply->vps, 54, DHCP_MAGIC_VENDOR, TAG_ANY))) {
477                 request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
478         } else {
479                 REDEBUG("Unable to determine correct src_ipaddr for response");
480                 return -1;
481         }
482         request->reply->dst_port = request->packet->src_port;
483         request->reply->src_port = request->packet->dst_port;
484
485         /*
486          *      Answer to client's nearest DHCP relay.
487          *
488          *      Which may be different than the giaddr given in the
489          *      packet to the client.  i.e. the relay may have a
490          *      public IP, but the gateway a private one.
491          */
492         vp = fr_pair_find_by_num(request->reply->vps, 272, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-IP-Address */
493         if (vp && (vp->vp_ipaddr != ntohl(INADDR_ANY))) {
494                 RDEBUG("DHCP: Reply will be unicast to giaddr from original packet");
495                 request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
496                 request->reply->dst_port = request->packet->dst_port;
497
498                 vp = fr_pair_find_by_num(request->reply->vps, PW_PACKET_DST_PORT, 0, TAG_ANY);
499                 if (vp) request->reply->dst_port = vp->vp_integer;
500
501                 return 1;
502         }
503
504         /*
505          *      Answer to client's nearest DHCP gateway.  In this
506          *      case, the client can reach the gateway, as can the
507          *      server.
508          *
509          *      We also use *our* source port as the destination port.
510          *      Gateways are servers, and listen on the server port,
511          *      not the client port.
512          */
513         vp = fr_pair_find_by_num(request->reply->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
514         if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) {
515                 RDEBUG("DHCP: Reply will be unicast to giaddr");
516                 request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
517                 request->reply->dst_port = request->packet->dst_port;
518                 return 1;
519         }
520
521         /*
522          *      If it's a NAK, or the broadcast flag was set, ond
523          *      there's no client-ip-address, send a broadcast.
524          */
525         if ((request->reply->code == PW_DHCP_NAK) ||
526             ((vp = fr_pair_find_by_num(request->reply->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Flags */
527              (vp->vp_integer & 0x8000) &&
528              ((vp = fr_pair_find_by_num(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */
529               (vp->vp_ipaddr == htonl(INADDR_ANY))))) {
530                 /*
531                  * RFC 2131, page 23
532                  *
533                  * Broadcast on
534                  * - DHCPNAK
535                  * or
536                  * - Broadcast flag is set up and ciaddr == NULL
537                  */
538                 RDEBUG("DHCP: Reply will be broadcast");
539                 request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
540                 return 1;
541         }
542
543         /*
544          *      RFC 2131, page 23
545          *
546          *      Unicast to ciaddr if present, otherwise to yiaddr.
547          */
548         if ((vp = fr_pair_find_by_num(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */
549             (vp->vp_ipaddr != htonl(INADDR_ANY))) {
550                 RDEBUG("DHCP: Reply will be sent unicast to client-ip-address");
551                 request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
552                 return 1;
553         }
554
555         vp = fr_pair_find_by_num(request->reply->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
556         if (!vp) {
557                 RDEBUG("DHCP: Failed to find DHCP-Client-IP-Address or DHCP-Your-IP-Address for request; "
558                        "not responding");
559                 /*
560                  *      There is nowhere to send the response to, so don't bother.
561                  */
562                 request->reply->code = 0;
563                 return -1;
564         }
565
566 #ifdef SIOCSARP
567         /*
568          *      The system is configured to listen for broadcast
569          *      packets, which means we'll need to send unicast
570          *      replies, to IPs which haven't yet been assigned.
571          *      Therefore, we need to update the ARP table.
572          *
573          *      However, they haven't specified a interface.  So we
574          *      can't update the ARP table.  And we must send a
575          *      broadcast response.
576          */
577         if (sock->lsock.broadcast && !sock->src_interface) {
578                 WARN("You MUST set \"interface\" if you have \"broadcast = yes\"");
579                 RDEBUG("DHCP: Reply will be broadcast as no interface was defined");
580                 request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
581                 return 1;
582         }
583
584         RDEBUG("DHCP: Reply will be unicast to your-ip-address");
585         request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
586
587         /*
588          *      When sending a DHCP_OFFER, make sure our ARP table
589          *      contains an entry for the client IP address.
590          *      Otherwise the packet may not be sent to the client, as
591          *      the OS has no ARP entry for it.
592          *
593          *      This is a cute hack to avoid us having to create a raw
594          *      socket to send DHCP packets.
595          */
596         if (request->reply->code == PW_DHCP_OFFER) {
597                 VALUE_PAIR *hwvp = fr_pair_find_by_num(request->reply->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */
598
599                 if (!hwvp) return -1;
600
601                 if (fr_dhcp_add_arp_entry(request->reply->sockfd, sock->src_interface, hwvp, vp) < 0) {
602                         RDEBUG("Failed adding arp entry: %s", fr_strerror());
603                         return -1;
604                 }
605         }
606 #else
607         if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr != ntohl(INADDR_NONE)) {
608                 RDEBUG("DHCP: Request will be unicast to the unicast source IP address");
609                 request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
610         } else {
611                 RDEBUG("DHCP: Reply will be broadcast as this system does not support ARP updates");
612                 request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
613         }
614 #endif
615
616         return 1;
617 }
618
619 static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
620 {
621         int rcode, broadcast = 1;
622         int on = 1;
623         dhcp_socket_t *sock;
624         RADCLIENT *client;
625         CONF_PAIR *cp;
626
627         /*
628          *      Set if before parsing, so the user can forcibly turn
629          *      it off later.
630          */
631         this->nodup = true;
632
633         rcode = common_socket_parse(cs, this);
634         if (rcode != 0) return rcode;
635
636         if (check_config) return 0;
637
638         sock = this->data;
639
640         if (!sock->lsock.interface) {
641                 WARN("No \"interface\" setting is defined.  Only unicast DHCP will work");
642         }
643
644         /*
645          *      See whether or not we enable broadcast packets.
646          */
647         cp = cf_pair_find(cs, "broadcast");
648         if (cp) {
649                 char const *value = cf_pair_value(cp);
650                 if (value && (strcmp(value, "no") == 0)) {
651                         broadcast = 0;
652                 }
653         }
654
655         if (broadcast) {
656                 if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
657                         ERROR("Can't set broadcast option: %s\n",
658                                fr_syserror(errno));
659                         return -1;
660                 }
661         }
662
663         if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
664                 ERROR("Can't set re-use addres option: %s\n",
665                        fr_syserror(errno));
666                 return -1;
667         }
668
669         /*
670          *      Undocumented extension for testing without
671          *      destroying your network!
672          */
673         sock->suppress_responses = false;
674         cp = cf_pair_find(cs, "suppress_responses");
675         if (cp) {
676                 rcode = cf_item_parse(cs, "suppress_responses", FR_ITEM_POINTER(PW_TYPE_BOOLEAN, &sock->suppress_responses), NULL);
677                 if (rcode < 0) return -1;
678         }
679
680         cp = cf_pair_find(cs, "src_interface");
681         if (cp) {
682                 rcode = cf_item_parse(cs, "src_interface", FR_ITEM_POINTER(PW_TYPE_STRING, &sock->src_interface), NULL);
683                 if (rcode < 0) return -1;
684         } else {
685                 sock->src_interface = sock->lsock.interface;
686         }
687
688         if (!sock->src_interface && sock->lsock.interface) {
689                 sock->src_interface = talloc_typed_strdup(sock, sock->lsock.interface);
690         }
691
692         cp = cf_pair_find(cs, "src_ipaddr");
693         if (cp) {
694                 memset(&sock->src_ipaddr, 0, sizeof(sock->src_ipaddr));
695                 sock->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
696                 rcode = cf_item_parse(cs, "src_ipaddr", FR_ITEM_POINTER(PW_TYPE_IPV4_ADDR, &sock->src_ipaddr), NULL);
697                 if (rcode < 0) return -1;
698
699                 sock->src_ipaddr.af = AF_INET;
700         } else {
701                 memcpy(&sock->src_ipaddr, &sock->lsock.my_ipaddr, sizeof(sock->src_ipaddr));
702         }
703
704         /*
705          *      Initialize the fake client.
706          */
707         client = &sock->dhcp_client;
708         memset(client, 0, sizeof(*client));
709         client->ipaddr.af = AF_INET;
710         client->ipaddr.ipaddr.ip4addr.s_addr = ntohl(INADDR_NONE);
711         client->ipaddr.prefix = 0;
712         client->longname = client->shortname = "dhcp";
713         client->secret = client->shortname;
714         client->nas_type = talloc_typed_strdup(sock, "none");
715
716         return 0;
717 }
718
719
720 /*
721  *      Check if an incoming request is "ok"
722  *
723  *      It takes packets, not requests.  It sees if the packet looks
724  *      OK.  If so, it does a number of sanity checks on it.
725  */
726 static int dhcp_socket_recv(rad_listen_t *listener)
727 {
728         RADIUS_PACKET   *packet;
729         dhcp_socket_t   *sock;
730
731         packet = fr_dhcp_recv(listener->fd);
732         if (!packet) {
733                 ERROR("%s", fr_strerror());
734                 return 0;
735         }
736
737         sock = listener->data;
738         if (!request_receive(NULL, listener, packet, &sock->dhcp_client, dhcp_process)) {
739                 rad_free(&packet);
740                 return 0;
741         }
742
743         return 1;
744 }
745
746
747 /*
748  *      Send an authentication response packet
749  */
750 static int dhcp_socket_send(rad_listen_t *listener, REQUEST *request)
751 {
752         dhcp_socket_t   *sock;
753
754         rad_assert(request->listener == listener);
755         rad_assert(listener->send == dhcp_socket_send);
756
757         if (request->reply->code == 0) return 0; /* don't reply */
758
759         if (fr_dhcp_encode(request->reply) < 0) {
760                 DEBUG("dhcp_socket_send: ERROR\n");
761                 return -1;
762         }
763
764         sock = listener->data;
765         if (sock->suppress_responses) return 0;
766
767         return fr_dhcp_send(request->reply);
768 }
769
770
771 static int dhcp_socket_encode(UNUSED rad_listen_t *listener, UNUSED REQUEST *request)
772 {
773         DEBUG2("NO ENCODE!");
774         return 0;
775 }
776
777
778 static int dhcp_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
779 {
780         return fr_dhcp_decode(request->packet);
781 }
782
783 extern fr_protocol_t proto_dhcp;
784 fr_protocol_t proto_dhcp = {
785         .magic          = RLM_MODULE_INIT,
786         .name           = "dhcp",
787         .inst_size      = sizeof(dhcp_socket_t),
788         .parse          = dhcp_socket_parse,
789         .recv           = dhcp_socket_recv,
790         .send           = dhcp_socket_send,
791         .print          = common_socket_print,
792         .encode         = dhcp_socket_encode,
793         .decode         = dhcp_socket_decode
794 };