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