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