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