fe42fdc2bca3681a883a0e4421b9a400d3baab9b
[freeradius.git] / src / modules / frs_dhcp / frs_dhcp.c
1 /*
2  * dhcp.c       DHCP processing.  Done poorly for now.
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 Alan DeKok <aland@deployingradius.com>
22  */
23
24 #include <freeradius-devel/radiusd.h>
25 #include <freeradius-devel/modules.h>
26 #include <freeradius-devel/conffile.h>
27 #include <freeradius-devel/rad_assert.h>
28 #include "dhcp.h"
29
30 #ifdef WITH_DHCP
31
32 /*
33  *      Same layout, etc. as listen_socket_t.
34  */
35 typedef struct dhcp_socket_t {
36         /*
37          *      For normal sockets.
38          */
39         fr_ipaddr_t     ipaddr;
40         int             port;
41 #ifdef SO_BINDTODEVICE
42         const char      *interface;
43 #endif
44         int             suppress_responses;
45         RADCLIENT       dhcp_client;
46 } dhcp_socket_t;
47
48 static int dhcp_process(REQUEST *request)
49 {
50         int rcode;
51         VALUE_PAIR *vp;
52
53         vp = pairfind(request->packet->vps, DHCP2ATTR(53)); /* DHCP-Message-Type */
54         if (vp) {
55                 DICT_VALUE *dv = dict_valbyattr(DHCP2ATTR(53), vp->vp_integer);
56                 DEBUG("Trying sub-section dhcp %s {...}",
57                       dv->name ? dv->name : "<unknown>");
58                 rcode = module_post_auth(vp->vp_integer, request);
59         } else {
60                 DEBUG("DHCP: Failed to find DHCP-Message-Type in packet!");
61                 rcode = RLM_MODULE_FAIL;
62         }
63
64         /*
65          *      Look for Relay attribute, and forward it if so...
66          */
67
68         vp = pairfind(request->reply->vps, DHCP2ATTR(53)); /* DHCP-Message-Type */
69         if (vp) {
70                 request->reply->code = vp->vp_integer;
71                 if (request->reply->code < PW_DHCP_OFFSET) {
72                         request->reply->code += PW_DHCP_OFFSET;
73                 }
74         }
75         else switch (rcode) {
76         case RLM_MODULE_OK:
77         case RLM_MODULE_UPDATED:
78                 if (request->packet->code == PW_DHCP_DISCOVER) {
79                         request->reply->code = PW_DHCP_OFFER;
80                         break;
81
82                 } else if (request->packet->code == PW_DHCP_REQUEST) {
83                         request->reply->code = PW_DHCP_ACK;
84                         break;
85                 }
86
87                 /* FALL-THROUGH */
88
89         default:
90         case RLM_MODULE_REJECT:
91         case RLM_MODULE_FAIL:
92         case RLM_MODULE_INVALID:
93         case RLM_MODULE_NOOP:
94         case RLM_MODULE_NOTFOUND:
95                 request->reply->code = PW_DHCP_NAK;
96                 break;
97
98         case RLM_MODULE_HANDLED:
99                 break;
100         }
101
102         return 1;
103 }
104
105 static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
106 {
107         int rcode;
108         int on = 1;
109         dhcp_socket_t *sock;
110         RADCLIENT *client;
111         CONF_PAIR *cp;
112
113         rcode = listen_socket_parse(cs, this);
114         if (rcode != 0) return rcode;
115
116         sock = this->data;
117
118         /*
119          *      FIXME: Parse config file option for "do broadast = yes/no"
120          */
121         if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
122                 radlog(L_ERR, "Can't set broadcast option: %s\n",
123                        strerror(errno));
124                 return -1;
125         }
126
127         if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
128                 radlog(L_ERR, "Can't set re-use addres option: %s\n",
129                        strerror(errno));
130                 return -1;
131         }
132
133         /*
134          *      Undocumented extension for testing without
135          *      destroying your network!
136          */
137         sock->suppress_responses = FALSE;
138         cp = cf_pair_find(cs, "suppress_responses");
139         if (cp) {
140                 const char *value;
141
142                 value = cf_pair_value(cp);
143
144                 if (value && (strcmp(value, "yes") == 0)) {
145                         sock->suppress_responses = TRUE;
146                 }
147         }
148
149         /*
150          *      Initialize the fake client.
151          */
152         client = &sock->dhcp_client;
153         memset(client, 0, sizeof(*client));
154         client->ipaddr.af = AF_INET;
155         client->ipaddr.ipaddr.ip4addr.s_addr = INADDR_NONE;
156         client->prefix = 0;
157         client->longname = client->shortname = "dhcp";
158         client->secret = client->shortname;
159         client->nastype = strdup("none");
160
161         return 0;
162 }
163
164
165 /*
166  *      Check if an incoming request is "ok"
167  *
168  *      It takes packets, not requests.  It sees if the packet looks
169  *      OK.  If so, it does a number of sanity checks on it.
170  */
171 static int dhcp_socket_recv(rad_listen_t *listener,
172                             RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
173 {
174         RADIUS_PACKET   *packet;
175         dhcp_socket_t   *sock;
176
177         packet = fr_dhcp_recv(listener->fd);
178         if (!packet) {
179                 radlog(L_ERR, "%s", fr_strerror());
180                 return 0;
181         }
182
183         sock = listener->data;
184         if (!received_request(listener, packet, prequest, &sock->dhcp_client)) {
185                 rad_free(&packet);
186                 return 0;
187         }
188
189         *pfun = dhcp_process;
190
191         return 1;
192 }
193
194
195 /*
196  *      Send an authentication response packet
197  */
198 static int dhcp_socket_send(rad_listen_t *listener, REQUEST *request)
199 {
200         dhcp_socket_t   *sock;
201
202         rad_assert(request->listener == listener);
203         rad_assert(listener->send == dhcp_socket_send);
204
205         if (request->reply->code == 0) return 0; /* don't reply */
206
207         if (fr_dhcp_encode(request->reply, request->packet) < 0) {
208                 return -1;
209         }
210
211         sock = listener->data;
212         if (sock->suppress_responses) return 0;
213
214         /*
215          *      Don't send anything
216          */
217         return fr_dhcp_send(request->reply);
218 }
219
220
221 static int dhcp_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request)
222 {
223         DEBUG2("NO ENCODE!");
224         return 0;
225         return fr_dhcp_encode(request->reply, request->packet);
226 }
227
228
229 static int dhcp_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
230 {
231         return fr_dhcp_decode(request->packet);
232 }
233
234
235 frs_module_t frs_dhcp = {
236   FRS_MODULE_INIT, RAD_LISTEN_DHCP, "dhcp",
237   dhcp_socket_parse, NULL,
238   dhcp_socket_recv, dhcp_socket_send,
239   listen_socket_print, dhcp_socket_encode, dhcp_socket_decode
240 };
241 #endif /* WITH_DHCP */