2 * dhcp.c DHCP processing. Done poorly for now.
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.
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.
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
20 * Copyright 2008 The FreeRADIUS server project
21 * Copyright 2008 Alan DeKok <aland@deployingradius.com>
27 * Same layout, etc. as listen_socket_t.
29 typedef struct dhcp_socket_t {
35 #ifdef SO_BINDTODEVICE
36 const char *interface;
38 int suppress_responses;
39 RADCLIENT dhcp_client;
42 static int dhcp_process(REQUEST *request)
47 vp = pairfind(request->packet->vps, DHCP2ATTR(53)); /* DHCP-Message-Type */
49 DICT_VALUE *dv = dict_valbyattr(DHCP2ATTR(53), vp->vp_integer);
50 DEBUG("Trying sub-section dhcp %s {...}",
51 dv->name ? dv->name : "<unknown>");
52 rcode = module_post_auth(vp->vp_integer, request);
54 DEBUG("DHCP: Failed to find DHCP-Message-Type in packet!");
55 rcode = RLM_MODULE_FAIL;
59 * Look for Relay attribute, and forward it if necessary.
61 vp = pairfind(request->config_items, DHCP2ATTR(270));
64 RADIUS_PACKET relayed;
66 request->reply->code = 0; /* don't reply to the client */
69 * Find the original giaddr.
70 * FIXME: Maybe look in the original packet?
72 * It's invalid to have giaddr=0 AND a relay option
74 giaddr = pairfind(request->packet->vps, 266);
75 if (giaddr && (giaddr->vp_ipaddr == htonl(INADDR_ANY))) {
76 if (pairfind(request->packet->vps, DHCP2ATTR(82))) {
77 RDEBUG("DHCP: Received packet with giaddr = 0 and containing relay option: Discarding packet");
82 * FIXME: Add cache by XID.
84 RDEBUG("DHCP: Cannot yet relay packets with giaddr = 0");
88 if (request->packet->data[3] > 10) {
89 RDEBUG("DHCP: Number of hops is greater than 10: not relaying");
94 * Forward it VERBATIM to the next server, rather
97 memcpy(&relayed, packet, sizeof(relayed));
99 relayed.dst_ipaddr.af = AF_INET;
100 relayed.dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
101 relayed.dst_port = request->packet->dst_port;
103 relayed.src_ipaddr = request->packet->dst_ipaddr;
104 relayed.src_port = request->packet->dst_port;
106 relayed.data = rad_malloc(relayed.data_len);
107 memcpy(relayed.data, request->packet->data, request->packet->data_len);
111 * The only field that changes is the number of hops
113 relayed.data[3]++; /* number of hops */
116 * Forward the relayed packet VERBATIM, don't
117 * respond to the client, and forget completely
118 * about this request.
120 fr_dhcp_send(&relayed);
125 vp = pairfind(request->reply->vps, DHCP2ATTR(53)); /* DHCP-Message-Type */
127 request->reply->code = vp->vp_integer;
128 if (request->reply->code < PW_DHCP_OFFSET) {
129 request->reply->code += PW_DHCP_OFFSET;
132 else switch (rcode) {
134 case RLM_MODULE_UPDATED:
135 if (request->packet->code == PW_DHCP_DISCOVER) {
136 request->reply->code = PW_DHCP_OFFER;
139 } else if (request->packet->code == PW_DHCP_REQUEST) {
140 request->reply->code = PW_DHCP_ACK;
147 case RLM_MODULE_REJECT:
148 case RLM_MODULE_FAIL:
149 case RLM_MODULE_INVALID:
150 case RLM_MODULE_NOOP:
151 case RLM_MODULE_NOTFOUND:
152 request->reply->code = PW_DHCP_NAK;
155 case RLM_MODULE_HANDLED:
162 static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
170 rcode = common_socket_parse(cs, this);
171 if (rcode != 0) return rcode;
176 * FIXME: Parse config file option for "do broadast = yes/no"
178 if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
179 radlog(L_ERR, "Can't set broadcast option: %s\n",
184 if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
185 radlog(L_ERR, "Can't set re-use addres option: %s\n",
191 * Undocumented extension for testing without
192 * destroying your network!
194 sock->suppress_responses = FALSE;
195 cp = cf_pair_find(cs, "suppress_responses");
199 value = cf_pair_value(cp);
201 if (value && (strcmp(value, "yes") == 0)) {
202 sock->suppress_responses = TRUE;
207 * Initialize the fake client.
209 client = &sock->dhcp_client;
210 memset(client, 0, sizeof(*client));
211 client->ipaddr.af = AF_INET;
212 client->ipaddr.ipaddr.ip4addr.s_addr = INADDR_NONE;
214 client->longname = client->shortname = "dhcp";
215 client->secret = client->shortname;
216 client->nastype = strdup("none");
223 * Check if an incoming request is "ok"
225 * It takes packets, not requests. It sees if the packet looks
226 * OK. If so, it does a number of sanity checks on it.
228 static int dhcp_socket_recv(rad_listen_t *listener,
229 RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
231 RADIUS_PACKET *packet;
234 packet = fr_dhcp_recv(listener->fd);
236 radlog(L_ERR, "%s", fr_strerror());
240 sock = listener->data;
241 if (!received_request(listener, packet, prequest, &sock->dhcp_client)) {
246 *pfun = dhcp_process;
253 * Send an authentication response packet
255 static int dhcp_socket_send(rad_listen_t *listener, REQUEST *request)
259 rad_assert(request->listener == listener);
260 rad_assert(listener->send == dhcp_socket_send);
262 if (request->reply->code == 0) return 0; /* don't reply */
264 if (fr_dhcp_encode(request->reply, request->packet) < 0) {
268 sock = listener->data;
269 if (sock->suppress_responses) return 0;
272 * Don't send anything
274 return fr_dhcp_send(request->reply);
278 static int dhcp_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request)
280 DEBUG2("NO ENCODE!");
282 return fr_dhcp_encode(request->reply, request->packet);
286 static int dhcp_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
288 return fr_dhcp_decode(request->packet);
290 #endif /* WITH_DCHP */