blob: 2e1c7660d763ed156f415a51b16f2a41f459eb7c [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
Simon Kelleyc72daea2012-01-05 21:33:27 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17
18#include "dnsmasq.h"
19
20#ifdef HAVE_DHCP6
21
Simon Kelleyceae00d2012-02-09 21:28:14 +000022static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, struct dhcp_context *context,
Simon Kelleye44ddca2012-02-18 17:08:50 +000023 int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now);
Simon Kelley07933802012-02-14 20:55:25 +000024static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dhcp_netid *tags, struct dhcp_context *context,
Simon Kelleye44ddca2012-02-18 17:08:50 +000025 int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now);
Simon Kelley6c8f21e2012-03-12 15:06:55 +000026static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts);
Simon Kelley4cb1b322012-02-06 14:30:41 +000027static void log6_packet(char *type, unsigned char *clid, int clid_len, struct in6_addr *addr, int xid, char *iface, char *string);
Simon Kelleyc72daea2012-01-05 21:33:27 +000028
Simon Kelley4cb1b322012-02-06 14:30:41 +000029static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize);
30static void *opt6_next(void *opts, void *end);
31static unsigned int opt6_uint(unsigned char *opt, int offset, int size);
Simon Kelley62779782012-02-10 21:19:25 +000032
Simon Kelley4cb1b322012-02-06 14:30:41 +000033#define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2)))
34#define opt6_type(opt) (opt6_uint(opt, -4, 2))
35#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)]))
36
37
Simon Kelley1d0f91c2012-03-12 11:56:22 +000038unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
39 struct in6_addr *fallback, size_t sz, int is_unicast, time_t now)
Simon Kelleyc72daea2012-01-05 21:33:27 +000040{
Simon Kelley4cb1b322012-02-06 14:30:41 +000041 struct dhcp_netid *relay_tags = NULL;
42 struct dhcp_vendor *vendor;
Simon Kelley1d0f91c2012-03-12 11:56:22 +000043 int msg_type;
44
45 if (sz <= 4)
46 return 0;
47
48 msg_type = *((unsigned char *)daemon->dhcp_packet.iov_base);
49
Simon Kelley4cb1b322012-02-06 14:30:41 +000050 /* Mark these so we only match each at most once, to avoid tangled linked lists */
51 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
52 vendor->netid.next = &vendor->netid;
Simon Kelleyc72daea2012-01-05 21:33:27 +000053
Simon Kelleyc5ad4e72012-02-24 16:06:20 +000054 save_counter(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +000055
Simon Kelleye44ddca2012-02-18 17:08:50 +000056 if (dhcp6_maybe_relay(NULL, &relay_tags, context, interface, iface_name, fallback, daemon->dhcp_packet.iov_base, sz, is_unicast, now))
Simon Kelley1d0f91c2012-03-12 11:56:22 +000057 return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
Simon Kelleyc72daea2012-01-05 21:33:27 +000058
59 return 0;
60}
61
Simon Kelley4cb1b322012-02-06 14:30:41 +000062/* This cost me blood to write, it will probably cost you blood to understand - srk. */
Simon Kelleyceae00d2012-02-09 21:28:14 +000063static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, struct dhcp_context *context,
Simon Kelleye44ddca2012-02-18 17:08:50 +000064 int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now)
Simon Kelley4cb1b322012-02-06 14:30:41 +000065{
66 void *end = inbuff + sz;
67 void *opts = inbuff + 34;
68 int msg_type = *((unsigned char *)inbuff);
69 unsigned char *outmsgtypep;
70 void *opt;
71 struct dhcp_vendor *vendor;
72
73 /* if not an encaplsulated relayed message, just do the stuff */
74 if (msg_type != DHCP6RELAYFORW)
75 {
76 /* if link_address != NULL if points to the link address field of the
77 innermost nested RELAYFORW message, which is where we find the
78 address of the network on which we can allocate an address.
79 Recalculate the available contexts using that information. */
80
81 if (link_address)
82 {
83 struct dhcp_context *c;
84 context = NULL;
Simon Kelleybaeb3ad2013-01-10 11:47:38 +000085
86 if (!IN6_IS_ADDR_LOOPBACK(link_address) &&
87 !IN6_IS_ADDR_LINKLOCAL(link_address) &&
88 !IN6_IS_ADDR_MULTICAST(link_address))
89 for (c = daemon->dhcp6; c; c = c->next)
90 if ((c->flags & CONTEXT_DHCP) &&
91 !(c->flags & CONTEXT_TEMPLATE) &&
92 is_same_net6(link_address, &c->start6, c->prefix) &&
93 is_same_net6(link_address, &c->end6, c->prefix))
94 {
95 c->current = context;
96 context = c;
97 }
Simon Kelley4cb1b322012-02-06 14:30:41 +000098
Simon Kelley4cb1b322012-02-06 14:30:41 +000099 if (!context)
100 {
101 inet_ntop(AF_INET6, link_address, daemon->addrbuff, ADDRSTRLEN);
102 my_syslog(MS_DHCP | LOG_WARNING,
103 _("no address range available for DHCPv6 request from relay at %s"),
104 daemon->addrbuff);
105 return 0;
106 }
107 }
108
109 if (!context)
110 {
111 my_syslog(MS_DHCP | LOG_WARNING,
112 _("no address range available for DHCPv6 request via %s"), iface_name);
113 return 0;
114 }
115
Simon Kelleye44ddca2012-02-18 17:08:50 +0000116 return dhcp6_no_relay(msg_type, link_address, *relay_tagsp, context, interface, iface_name, fallback, inbuff, sz, is_unicast, now);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000117 }
118
119 /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
120 which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */
121 if (sz < 38)
122 return 0;
123
124 /* copy header stuff into reply message and set type to reply */
125 outmsgtypep = put_opt6(inbuff, 34);
126 *outmsgtypep = DHCP6RELAYREPL;
127
128 /* look for relay options and set tags if found. */
129 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
130 {
131 int mopt;
132
133 if (vendor->match_type == MATCH_SUBSCRIBER)
134 mopt = OPTION6_SUBSCRIBER_ID;
135 else if (vendor->match_type == MATCH_REMOTE)
136 mopt = OPTION6_REMOTE_ID;
137 else
138 continue;
139
140 if ((opt = opt6_find(opts, end, mopt, 1)) &&
141 vendor->len == opt6_len(opt) &&
142 memcmp(vendor->data, opt6_ptr(opt, 0), vendor->len) == 0 &&
143 vendor->netid.next != &vendor->netid)
144 {
145 vendor->netid.next = *relay_tagsp;
146 *relay_tagsp = &vendor->netid;
147 break;
148 }
149 }
150
151 for (opt = opts; opt; opt = opt6_next(opt, end))
152 {
153 int o = new_opt6(opt6_type(opt));
154 if (opt6_type(opt) == OPTION6_RELAY_MSG)
155 {
156 struct in6_addr link_address;
157 /* the packet data is unaligned, copy to aligned storage */
158 memcpy(&link_address, inbuff + 2, IN6ADDRSZ);
159 /* Not, zero is_unicast since that is now known to refer to the
160 relayed packet, not the original sent by the client */
Simon Kelleye44ddca2012-02-18 17:08:50 +0000161 if (!dhcp6_maybe_relay(&link_address, relay_tagsp, context, interface, iface_name, fallback, opt6_ptr(opt, 0), opt6_len(opt), 0, now))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000162 return 0;
163 }
164 else
165 put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
166 end_opt6(o);
167 }
168
169 return 1;
170}
171
Simon Kelley07933802012-02-14 20:55:25 +0000172static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dhcp_netid *tags, struct dhcp_context *context,
Simon Kelleye44ddca2012-02-18 17:08:50 +0000173 int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000174{
175 void *packet_options = inbuff + 4;
176 void *end = inbuff + sz;
177 void *opt, *oro;
178 int i, o, o1;
179 unsigned char *clid = NULL;
180 int clid_len = 0, start_opts;
181 struct dhcp_netid *tagif, *context_tags = NULL;
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000182 char *client_hostname= NULL, *hostname = NULL;
183 char *domain = NULL, *send_domain = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000184 struct dhcp_config *config = NULL;
Simon Kelley0d28af82012-09-20 21:24:06 +0100185 struct dhcp_netid known_id, iface_id, v6_id;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000186 int done_dns = 0, hostname_auth = 0, do_encap = 0;
187 unsigned char *outmsgtypep;
188 struct dhcp_opt *opt_cfg;
189 struct dhcp_vendor *vendor;
190 struct dhcp_context *context_tmp;
Simon Kelley3634c542012-02-08 14:22:37 +0000191 unsigned int xid, ignore = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000192 unsigned int fqdn_flags = 0x01; /* default to send if we recieve no FQDN option */
193
Simon Kelleyceae00d2012-02-09 21:28:14 +0000194 /* set tag with name == interface */
195 iface_id.net = iface_name;
196 iface_id.next = tags;
197 tags = &iface_id;
198
Simon Kelley23780dd2012-10-23 17:04:37 +0100199 /* set tag "dhcpv6" */
200 v6_id.net = "dhcpv6";
Simon Kelley0d28af82012-09-20 21:24:06 +0100201 v6_id.next = tags;
202 tags = &v6_id;
203
Simon Kelley4cb1b322012-02-06 14:30:41 +0000204 /* copy over transaction-id, and save pointer to message type */
205 outmsgtypep = put_opt6(inbuff, 4);
206 start_opts = save_counter(-1);
207 xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16;
208
209 /* We're going to be linking tags from all context we use.
210 mark them as unused so we don't link one twice and break the list */
211 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
212 {
213 context->netid.next = &context->netid;
214
215 if (option_bool(OPT_LOG_OPTS))
216 {
217 inet_ntop(AF_INET6, &context_tmp->start6, daemon->dhcp_buff, ADDRSTRLEN);
218 inet_ntop(AF_INET6, &context_tmp->end6, daemon->dhcp_buff2, ADDRSTRLEN);
219 if (context_tmp->flags & (CONTEXT_STATIC))
220 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCPv6 subnet: %s/%d"),
221 xid, daemon->dhcp_buff, context_tmp->prefix);
222 else
223 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
224 xid, daemon->dhcp_buff, daemon->dhcp_buff2);
225 }
226 }
227
228 if ((opt = opt6_find(packet_options, end, OPTION6_CLIENT_ID, 1)))
229 {
230 clid = opt6_ptr(opt, 0);
231 clid_len = opt6_len(opt);
232 o = new_opt6(OPTION6_CLIENT_ID);
233 put_opt6(clid, clid_len);
234 end_opt6(o);
235 }
236 else if (msg_type != DHCP6IREQ)
237 return 0;
238
239 /* server-id must match except for SOLICIT and CONFIRM messages */
240 if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ &&
241 (!(opt = opt6_find(packet_options, end, OPTION6_SERVER_ID, 1)) ||
242 opt6_len(opt) != daemon->duid_len ||
243 memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
244 return 0;
245
246 o = new_opt6(OPTION6_SERVER_ID);
247 put_opt6(daemon->duid, daemon->duid_len);
248 end_opt6(o);
249
250 if (is_unicast &&
251 (msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE))
252
253 {
254 o1 = new_opt6(OPTION6_STATUS_CODE);
255 put_opt6_short(DHCP6USEMULTI);
256 put_opt6_string("Use multicast");
257 end_opt6(o1);
258 return 1;
259 }
260
261 /* match vendor and user class options */
262 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
263 {
264 int mopt;
265
266 if (vendor->match_type == MATCH_VENDOR)
267 mopt = OPTION6_VENDOR_CLASS;
268 else if (vendor->match_type == MATCH_USER)
269 mopt = OPTION6_USER_CLASS;
270 else
271 continue;
272
273 if ((opt = opt6_find(packet_options, end, mopt, 2)))
274 {
275 void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000276 int offset = 0;
277
278 if (mopt == OPTION6_VENDOR_CLASS)
279 {
280 if (opt6_len(opt) < 4)
281 continue;
282
283 if (vendor->enterprise != opt6_uint(opt, 0, 4))
284 continue;
285
286 offset = 4;
287 }
288
289 for (enc_opt = opt6_ptr(opt, offset); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000290 for (i = 0; i <= (opt6_len(enc_opt) - vendor->len); i++)
291 if (memcmp(vendor->data, opt6_ptr(enc_opt, i), vendor->len) == 0)
292 {
293 vendor->netid.next = tags;
294 tags = &vendor->netid;
295 break;
296 }
297 }
298 }
Simon Kelley3634c542012-02-08 14:22:37 +0000299
Simon Kelley1567fea2012-03-12 22:15:35 +0000300 if (option_bool(OPT_LOG_OPTS) && (opt = opt6_find(packet_options, end, OPTION6_VENDOR_CLASS, 4)))
301 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %u"), xid, opt6_uint(opt, 0, 4));
302
Simon Kelley3634c542012-02-08 14:22:37 +0000303 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
304 Otherwise assume the option is an array, and look for a matching element.
305 If no data given, existance of the option is enough. This code handles
306 V-I opts too. */
307 for (opt_cfg = daemon->dhcp_match6; opt_cfg; opt_cfg = opt_cfg->next)
308 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000309 int match = 0;
310
Simon Kelley3634c542012-02-08 14:22:37 +0000311 if (opt_cfg->flags & DHOPT_RFC3925)
312 {
313 for (opt = opt6_find(packet_options, end, OPTION6_VENDOR_OPTS, 4);
314 opt;
315 opt = opt6_find(opt6_next(opt, end), end, OPTION6_VENDOR_OPTS, 4))
316 {
317 void *vopt;
318 void *vend = opt6_ptr(opt, opt6_len(opt));
319
320 for (vopt = opt6_find(opt6_ptr(opt, 4), vend, opt_cfg->opt, 0);
321 vopt;
322 vopt = opt6_find(opt6_next(vopt, vend), vend, opt_cfg->opt, 0))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000323 if ((match = match_bytes(opt_cfg, opt6_ptr(vopt, 0), opt6_len(vopt))))
Simon Kelley3634c542012-02-08 14:22:37 +0000324 break;
325 }
326 if (match)
327 break;
328 }
329 else
330 {
331 if (!(opt = opt6_find(packet_options, end, opt_cfg->opt, 1)))
332 continue;
333
334 match = match_bytes(opt_cfg, opt6_ptr(opt, 0), opt6_len(opt));
335 }
336
337 if (match)
338 {
339 opt_cfg->netid->next = tags;
340 tags = opt_cfg->netid;
341 }
342 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000343
344 if ((opt = opt6_find(packet_options, end, OPTION6_FQDN, 1)))
345 {
346 /* RFC4704 refers */
347 int len = opt6_len(opt) - 1;
348
349 fqdn_flags = opt6_uint(opt, 0, 1);
350
351 /* Always force update, since the client has no way to do it itself. */
352 if (!option_bool(OPT_FQDN_UPDATE) && !(fqdn_flags & 0x01))
353 fqdn_flags |= 0x03;
354
355 fqdn_flags &= ~0x04;
356
357 if (len != 0 && len < 255)
358 {
Simon Kelleyfbbc1452012-03-30 20:48:20 +0100359 unsigned char *pp, *op = opt6_ptr(opt, 1);
360 char *pq = daemon->dhcp_buff;
361
362 pp = op;
363 while (*op != 0 && ((op + (*op)) - pp) < len)
364 {
365 memcpy(pq, op+1, *op);
366 pq += *op;
367 op += (*op)+1;
368 *(pq++) = '.';
369 }
370
371 if (pq != daemon->dhcp_buff)
372 pq--;
373 *pq = 0;
374
375 if (legal_hostname(daemon->dhcp_buff))
376 {
377 client_hostname = daemon->dhcp_buff;
378 if (option_bool(OPT_LOG_OPTS))
379 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), xid, client_hostname);
380 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000381 }
382 }
383
384 if (clid)
385 {
386 config = find_config6(daemon->dhcp_conf, context, clid, clid_len, NULL);
387
388 if (have_config(config, CONFIG_NAME))
389 {
390 hostname = config->hostname;
391 domain = config->domain;
392 hostname_auth = 1;
393 }
394 else if (client_hostname)
395 {
396 domain = strip_hostname(client_hostname);
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000397
Simon Kelley4cb1b322012-02-06 14:30:41 +0000398 if (strlen(client_hostname) != 0)
399 {
400 hostname = client_hostname;
401 if (!config)
402 {
403 /* Search again now we have a hostname.
Simon Kelley00e9ad52012-02-16 21:53:11 +0000404 Only accept configs without CLID here, (it won't match)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000405 to avoid impersonation by name. */
406 struct dhcp_config *new = find_config6(daemon->dhcp_conf, context, NULL, 0, hostname);
407 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
408 config = new;
409 }
410 }
411 }
412 }
413
414 if (config)
415 {
416 struct dhcp_netid_list *list;
417
418 for (list = config->netid; list; list = list->next)
419 {
420 list->list->next = tags;
421 tags = list->list;
422 }
423
424 /* set "known" tag for known hosts */
425 known_id.net = "known";
426 known_id.next = tags;
427 tags = &known_id;
Simon Kelley3634c542012-02-08 14:22:37 +0000428
429 if (have_config(config, CONFIG_DISABLE))
430 ignore = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000431 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000432
433 tagif = run_tag_if(tags);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000434
Simon Kelley3634c542012-02-08 14:22:37 +0000435 /* if all the netids in the ignore list are present, ignore this client */
436 if (daemon->dhcp_ignore)
437 {
438 struct dhcp_netid_list *id_list;
439
Simon Kelley3634c542012-02-08 14:22:37 +0000440 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
441 if (match_netid(id_list->list, tagif, 0))
442 ignore = 1;
443 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000444
445 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
446 if (!hostname_auth)
447 {
448 struct dhcp_netid_list *id_list;
449
450 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
451 if ((!id_list->list) || match_netid(id_list->list, tagif, 0))
452 break;
453 if (id_list)
454 hostname = NULL;
455 }
456
Simon Kelley4cb1b322012-02-06 14:30:41 +0000457
458 switch (msg_type)
459 {
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000460 default:
461 return 0;
462
Simon Kelley4cb1b322012-02-06 14:30:41 +0000463 case DHCP6SOLICIT:
464 case DHCP6REQUEST:
465 {
466 void *rapid_commit = opt6_find(packet_options, end, OPTION6_RAPID_COMMIT, 0);
467 int make_lease = (msg_type == DHCP6REQUEST || rapid_commit);
468 int serial = 0, used_config = 0;
469
470 if (rapid_commit)
471 {
472 o = new_opt6(OPTION6_RAPID_COMMIT);
473 end_opt6(o);
474 }
475
476 /* set reply message type */
477 *outmsgtypep = make_lease ? DHCP6REPLY : DHCP6ADVERTISE;
478
479 log6_packet(msg_type == DHCP6SOLICIT ? "DHCPSOLICIT" : "DHCPREQUEST",
Simon Kelley3634c542012-02-08 14:22:37 +0000480 clid, clid_len, NULL, xid, iface_name, ignore ? "ignored" : NULL);
481
482 if (ignore)
483 return 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000484
485 for (opt = packet_options; opt; opt = opt6_next(opt, end))
486 {
487 int iaid, ia_type = opt6_type(opt);
488 void *ia_option, *ia_end;
489 unsigned int min_time = 0xffffffff;
Simon Kelley98d76a02012-02-10 22:16:45 +0000490 int t1cntr = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000491 int address_assigned = 0;
492
493 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
494 continue;
495
496 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
497 continue;
498
499 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
500 continue;
501
502 iaid = opt6_uint(opt, 0, 4);
503 ia_end = opt6_ptr(opt, opt6_len(opt));
504 ia_option = opt6_find(opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4), ia_end, OPTION6_IAADDR, 24);
505
506 /* reset "USED" flags on leases */
Simon Kelley8b460612012-09-08 21:47:28 +0100507 lease6_filter(ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, context);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000508
509 o = new_opt6(ia_type);
510 put_opt6_long(iaid);
511 if (ia_type == OPTION6_IA_NA)
512 {
513 /* save pointer */
514 t1cntr = save_counter(-1);
515 /* so we can fill these in later */
516 put_opt6_long(0);
517 put_opt6_long(0);
518 }
519
520 while (1)
521 {
522 struct in6_addr alloced_addr, *addrp = NULL;
Simon Kelleyc8257542012-03-28 21:15:41 +0100523 u32 requested_time = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000524 struct dhcp_lease *lease = NULL;
525
526 if (ia_option)
527 {
528 struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
Simon Kelleyc8257542012-03-28 21:15:41 +0100529 requested_time = opt6_uint(ia_option, 16, 4);
Simon Kelley37c9cce2013-01-09 19:51:04 +0000530 struct dhcp_context *dynamic;
531
532 if ((dynamic = address6_available(context, req_addr, tags)) || address6_valid(context, req_addr, tags))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000533 {
Simon Kelley37c9cce2013-01-09 19:51:04 +0000534 if (!dynamic && !(have_config(config, CONFIG_ADDR6) && memcmp(&config->addr6, req_addr, IN6ADDRSZ) == 0))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000535 {
Simon Kelley37c9cce2013-01-09 19:51:04 +0000536 /* Static range, not configured. */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000537 o1 = new_opt6(OPTION6_STATUS_CODE);
Simon Kelley37c9cce2013-01-09 19:51:04 +0000538 put_opt6_short(DHCP6UNSPEC);
539 put_opt6_string("Address unavailable");
Simon Kelley4cb1b322012-02-06 14:30:41 +0000540 end_opt6(o1);
541 }
Simon Kelley37c9cce2013-01-09 19:51:04 +0000542 else if (lease6_find_by_addr(req_addr, 128, 0) &&
543 !(lease = lease6_find(clid, clid_len, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, req_addr)))
544 {
545 /* Address leased to another DUID/IAID */
546 o1 = new_opt6(OPTION6_STATUS_CODE);
547 put_opt6_short(DHCP6UNSPEC);
548 put_opt6_string("Address in use");
549 end_opt6(o1);
550 }
551 else
552 addrp = req_addr;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000553 }
Simon Kelley37c9cce2013-01-09 19:51:04 +0000554 else if (msg_type == DHCP6REQUEST)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000555 {
Simon Kelley37c9cce2013-01-09 19:51:04 +0000556 /* requested address not on the correct link */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000557 o1 = new_opt6(OPTION6_STATUS_CODE);
Simon Kelley37c9cce2013-01-09 19:51:04 +0000558 put_opt6_short(DHCP6NOTONLINK);
559 put_opt6_string("Not on link");
Simon Kelley4cb1b322012-02-06 14:30:41 +0000560 end_opt6(o1);
Simon Kelley37c9cce2013-01-09 19:51:04 +0000561 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000562 }
563 else
564 {
565 /* must have an address to CONFIRM */
566 if (msg_type == DHCP6REQUEST && ia_type == OPTION6_IA_NA)
567 return 0;
568
569 /* Don't used configured addresses for temporary leases. */
570 if (have_config(config, CONFIG_ADDR6) && !used_config && ia_type == OPTION6_IA_NA)
571 {
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000572 struct dhcp_lease *ltmp = lease6_find_by_addr(&config->addr6, 128, 0);
573
Simon Kelley4cb1b322012-02-06 14:30:41 +0000574 used_config = 1;
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000575 inet_ntop(AF_INET6, &config->addr6, daemon->addrbuff, ADDRSTRLEN);
576
577 if (ltmp && ltmp->clid &&
Simon Kelley21bac1b2013-01-14 21:35:05 +0000578 (ltmp->clid_len != clid_len || memcmp(ltmp->clid, clid, clid_len) != 0 || ltmp->hwaddr_type != iaid))
Simon Kelley45938092013-01-15 21:57:42 +0000579 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s#%d"),
580 daemon->addrbuff, print_mac(daemon->namebuff, ltmp->clid, ltmp->clid_len), ltmp->hwaddr_type);
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000581 else if (have_config(config, CONFIG_DECLINED) &&
582 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
583 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"),
584 daemon->addrbuff);
585 else
Simon Kelley8b460612012-09-08 21:47:28 +0100586 {
587 addrp = &config->addr6;
588 /* may have existing lease for this address */
589 lease = lease6_find(clid, clid_len,
590 ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, addrp);
591 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000592 }
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000593
Simon Kelley4cb1b322012-02-06 14:30:41 +0000594 /* existing lease */
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000595 if (!addrp &&
596 (lease = lease6_find(clid, clid_len,
597 ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL)) &&
Simon Kelleyceae00d2012-02-09 21:28:14 +0000598 !config_find_by_address6(daemon->dhcp_conf, (struct in6_addr *)&lease->hwaddr, 128, 0))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000599 addrp = (struct in6_addr *)&lease->hwaddr;
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000600
601 if (!addrp && address6_allocate(context, clid, clid_len, serial++, tags, &alloced_addr))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000602 addrp = &alloced_addr;
603 }
604
605 if (addrp)
606 {
607 unsigned int lease_time;
608 struct dhcp_context *this_context;
609 struct dhcp_config *valid_config = config;
610
611 /* don't use a config to set lease time if it specifies an address which isn't this. */
612 if (have_config(config, CONFIG_ADDR6) && memcmp(&config->addr6, addrp, IN6ADDRSZ) != 0)
613 valid_config = NULL;
614
615 address_assigned = 1;
616
617 /* shouldn't ever fail */
Simon Kelley00e9ad52012-02-16 21:53:11 +0000618 if ((this_context = narrow_context6(context, addrp, tagif)))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000619 {
620 /* get tags from context if we've not used it before */
Simon Kelley00e9ad52012-02-16 21:53:11 +0000621 if (this_context->netid.next == &this_context->netid && this_context->netid.net)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000622 {
623 this_context->netid.next = context_tags;
624 context_tags = &this_context->netid;
Simon Kelley00e9ad52012-02-16 21:53:11 +0000625 if (!hostname_auth)
626 {
627 struct dhcp_netid_list *id_list;
628
629 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
630 if ((!id_list->list) || match_netid(id_list->list, &this_context->netid, 0))
631 break;
632 if (id_list)
633 hostname = NULL;
634 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000635 }
Simon Kelley3bc0d932012-12-28 11:31:44 +0000636
637 if (have_config(valid_config, CONFIG_TIME))
638 lease_time = valid_config->lease_time;
639 else
640 lease_time = this_context->lease_time;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000641
Simon Kelley3bc0d932012-12-28 11:31:44 +0000642 if (this_context->valid < lease_time)
643 lease_time = this_context->valid;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000644
Simon Kelley3bc0d932012-12-28 11:31:44 +0000645 if (ia_option)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000646 {
Simon Kelleyc8257542012-03-28 21:15:41 +0100647 if (requested_time < 120u )
648 requested_time = 120u; /* sanity */
649 if (lease_time == 0xffffffff || (requested_time != 0xffffffff && requested_time < lease_time))
650 lease_time = requested_time;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000651 }
652
653 if (lease_time < min_time)
654 min_time = lease_time;
655
656 /* May fail to create lease */
657 if (!lease && make_lease)
658 lease = lease6_allocate(addrp, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
659
660 if (lease)
661 {
662 lease_set_expires(lease, lease_time, now);
Simon Kelleya9ab7322012-04-28 11:29:37 +0100663 lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len, now, 0);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000664 lease_set_interface(lease, interface, now);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000665 if (hostname && ia_type == OPTION6_IA_NA)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000666 {
667 char *addr_domain = get_domain6(addrp);
668 if (!send_domain)
669 send_domain = addr_domain;
670 lease_set_hostname(lease, hostname, hostname_auth, addr_domain, domain);
671 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000672
673#ifdef HAVE_SCRIPT
674 if (daemon->lease_change_command)
675 {
676 void *class_opt;
677 lease->flags |= LEASE_CHANGED;
678 free(lease->extradata);
679 lease->extradata = NULL;
680 lease->extradata_size = lease->extradata_len = 0;
681 lease->hwaddr_len = 0; /* surrogate for no of vendor classes */
682
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000683 if ((class_opt = opt6_find(packet_options, end, OPTION6_VENDOR_CLASS, 4)))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000684 {
685 void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000686 lease->hwaddr_len++;
687 /* send enterprise number first */
688 sprintf(daemon->dhcp_buff2, "%u", opt6_uint(class_opt, 0, 4));
689 lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0);
690
691 if (opt6_len(class_opt) >= 6)
692 for (enc_opt = opt6_ptr(class_opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
693 {
694 lease->hwaddr_len++;
695 lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
696 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000697 }
698
699 lease_add_extradata(lease, (unsigned char *)client_hostname,
700 client_hostname ? strlen(client_hostname) : 0, 0);
701
702 /* space-concat tag set */
Simon Kelley00e9ad52012-02-16 21:53:11 +0000703 if (!tagif && !context_tags)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000704 lease_add_extradata(lease, NULL, 0, 0);
705 else
706 {
Simon Kelley87b8ecb2012-02-18 21:20:43 +0000707 struct dhcp_netid *n, *l, *tmp = tags;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000708
Simon Kelley00e9ad52012-02-16 21:53:11 +0000709 /* link temporarily */
710 for (n = context_tags; n && n->next; n = n->next);
Simon Kelleye44ddca2012-02-18 17:08:50 +0000711 if ((l = n))
Simon Kelley87b8ecb2012-02-18 21:20:43 +0000712 {
713 l->next = tags;
714 tmp = context_tags;
715 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000716
Simon Kelley87b8ecb2012-02-18 21:20:43 +0000717 for (n = run_tag_if(tmp); n; n = n->next)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000718 {
719 struct dhcp_netid *n1;
720 /* kill dupes */
721 for (n1 = n->next; n1; n1 = n1->next)
722 if (strcmp(n->net, n1->net) == 0)
723 break;
724 if (!n1)
725 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
726 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000727
728 /* unlink again */
729 if (l)
730 l->next = NULL;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000731 }
Simon Kelley07933802012-02-14 20:55:25 +0000732
733 if (link_address)
734 inet_ntop(AF_INET6, link_address, daemon->addrbuff, ADDRSTRLEN);
735
736 lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, link_address ? strlen(daemon->addrbuff) : 0, 0);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000737
738 if ((class_opt = opt6_find(packet_options, end, OPTION6_USER_CLASS, 2)))
739 {
740 void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
741 for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
742 lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
743 }
744 }
745#endif
746
Simon Kelley4cb1b322012-02-06 14:30:41 +0000747 }
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000748 else if (!send_domain)
749 send_domain = get_domain6(addrp);
750
Simon Kelley4cb1b322012-02-06 14:30:41 +0000751
752 if (lease || !make_lease)
753 {
754 o1 = new_opt6(OPTION6_IAADDR);
755 put_opt6(addrp, sizeof(*addrp));
Simon Kelleyc8257542012-03-28 21:15:41 +0100756 /* preferred lifetime */
Simon Kelley3bc0d932012-12-28 11:31:44 +0000757 put_opt6_long(this_context && (this_context->preferred < lease_time) ?
758 this_context->preferred : lease_time);
Simon Kelleyc8257542012-03-28 21:15:41 +0100759 put_opt6_long(lease_time); /* valid lifetime */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000760 end_opt6(o1);
761
762 log6_packet( make_lease ? "DHCPREPLY" : "DHCPADVERTISE",
763 clid, clid_len, addrp, xid, iface_name, hostname);
764 }
765
766 }
767 }
768
769
770 if (!ia_option ||
771 !(ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)))
772 {
773 if (address_assigned)
774 {
Simon Kelleydd88c172012-03-10 20:46:57 +0000775 o1 = new_opt6(OPTION6_STATUS_CODE);
776 put_opt6_short(DHCP6SUCCESS);
777 put_opt6_string("Oh hai from dnsmasq");
778 end_opt6(o1);
779
Simon Kelley4cb1b322012-02-06 14:30:41 +0000780 if (ia_type == OPTION6_IA_NA)
781 {
782 /* go back an fill in fields in IA_NA option */
783 unsigned int t1 = min_time == 0xffffffff ? 0xffffffff : min_time/2;
784 unsigned int t2 = min_time == 0xffffffff ? 0xffffffff : (min_time/8) * 7;
785 int sav = save_counter(t1cntr);
786 put_opt6_long(t1);
787 put_opt6_long(t2);
788 save_counter(sav);
789 }
790 }
791 else
792 {
Simon Kelleydd88c172012-03-10 20:46:57 +0000793 /* no address, return error */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000794 o1 = new_opt6(OPTION6_STATUS_CODE);
795 put_opt6_short(DHCP6NOADDRS);
796 put_opt6_string("No addresses available");
797 end_opt6(o1);
798 }
799
800 end_opt6(o);
Simon Kelley2a82db42012-03-10 21:40:10 +0000801
Simon Kelley1d0f91c2012-03-12 11:56:22 +0000802 if (address_assigned)
803 {
804 /* If --dhcp-authoritative is set, we can tell client not to wait for
805 other possible servers */
806 o = new_opt6(OPTION6_PREFERENCE);
807 put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);
808 end_opt6(o);
809 }
810
Simon Kelley4cb1b322012-02-06 14:30:41 +0000811 break;
812 }
813 }
814 }
815
816 break;
817 }
818
819 case DHCP6RENEW:
820 {
821 /* set reply message type */
822 *outmsgtypep = DHCP6REPLY;
823
824 log6_packet("DHCPRENEW", clid, clid_len, NULL, xid, iface_name, NULL);
825
826 for (opt = packet_options; opt; opt = opt6_next(opt, end))
827 {
828 int ia_type = opt6_type(opt);
829 void *ia_option, *ia_end;
830 unsigned int min_time = 0xffffffff;
831 int t1cntr = 0, iacntr;
832 unsigned int iaid;
833
834 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
835 continue;
836
837 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
838 continue;
839
840 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
841 continue;
842
843 iaid = opt6_uint(opt, 0, 4);
844
845 o = new_opt6(ia_type);
846 put_opt6_long(iaid);
847 if (ia_type == OPTION6_IA_NA)
848 {
849 /* save pointer */
850 t1cntr = save_counter(-1);
851 /* so we can fill these in later */
852 put_opt6_long(0);
853 put_opt6_long(0);
854 }
855
856 iacntr = save_counter(-1);
857
858 /* reset "USED" flags on leases */
Simon Kelley8b460612012-09-08 21:47:28 +0100859 lease6_filter(ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, context);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000860
861 ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
862 ia_end = opt6_ptr(opt, opt6_len(opt));
863
864 for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
865 ia_option;
866 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
867 {
868 struct dhcp_lease *lease = NULL;
869 struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
Simon Kelleyc8257542012-03-28 21:15:41 +0100870 u32 requested_time = opt6_uint(ia_option, 16, 4);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000871 unsigned int lease_time;
872 struct dhcp_context *this_context;
873 struct dhcp_config *valid_config = config;
874
875 /* don't use a config to set lease time if it specifies an address which isn't this. */
876 if (have_config(config, CONFIG_ADDR6) && memcmp(&config->addr6, req_addr, IN6ADDRSZ) != 0)
877 valid_config = NULL;
878
879 if (!(lease = lease6_find(clid, clid_len,
880 ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
881 iaid, req_addr)))
882 {
883 /* If the server cannot find a client entry for the IA the server
884 returns the IA containing no addresses with a Status Code option set
885 to NoBinding in the Reply message. */
886 save_counter(iacntr);
887 t1cntr = 0;
888
889 log6_packet("DHCPREPLY", clid, clid_len, req_addr, xid, iface_name, "lease not found");
890
891 o1 = new_opt6(OPTION6_STATUS_CODE);
892 put_opt6_short(DHCP6NOBINDING);
893 put_opt6_string("No binding found");
894 end_opt6(o1);
895 break;
896 }
897
Simon Kelley00e9ad52012-02-16 21:53:11 +0000898
Simon Kelleyceae00d2012-02-09 21:28:14 +0000899 if (!address6_available(context, req_addr, tagif) ||
900 !(this_context = narrow_context6(context, req_addr, tagif)))
Simon Kelleyc8257542012-03-28 21:15:41 +0100901 {
902 lease_time = 0;
903 this_context = NULL;
904 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000905 else
906 {
907 /* get tags from context if we've not used it before */
Simon Kelley00e9ad52012-02-16 21:53:11 +0000908 if (this_context->netid.next == &this_context->netid && this_context->netid.net)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000909 {
910 this_context->netid.next = context_tags;
911 context_tags = &this_context->netid;
Simon Kelley00e9ad52012-02-16 21:53:11 +0000912 if (!hostname_auth)
913 {
914 struct dhcp_netid_list *id_list;
915
916 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
917 if ((!id_list->list) || match_netid(id_list->list, &this_context->netid, 0))
918 break;
919 if (id_list)
920 hostname = NULL;
921 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000922 }
923
924 lease_time = have_config(valid_config, CONFIG_TIME) ? valid_config->lease_time : this_context->lease_time;
925
Simon Kelleyc8257542012-03-28 21:15:41 +0100926 if (requested_time < 120u )
927 requested_time = 120u; /* sanity */
928 if (lease_time == 0xffffffff || (requested_time != 0xffffffff && requested_time < lease_time))
929 lease_time = requested_time;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000930
931 lease_set_expires(lease, lease_time, now);
932 if (ia_type == OPTION6_IA_NA && hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000933 {
934 char *addr_domain = get_domain6(req_addr);
935 if (!send_domain)
936 send_domain = addr_domain;
937 lease_set_hostname(lease, hostname, hostname_auth, addr_domain, domain);
938 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000939
940 if (lease_time < min_time)
941 min_time = lease_time;
942 }
943
944 log6_packet("DHCPREPLY", clid, clid_len, req_addr, xid, iface_name, hostname);
945
946 o1 = new_opt6(OPTION6_IAADDR);
947 put_opt6(req_addr, sizeof(*req_addr));
Simon Kelleyc8257542012-03-28 21:15:41 +0100948 /* preferred lifetime */
949 put_opt6_long(this_context && (this_context->flags & CONTEXT_DEPRECATE) ? 0 : lease_time);
950 put_opt6_long(lease_time); /* valid lifetime */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000951 end_opt6(o1);
952 }
953
954 if (t1cntr != 0)
955 {
956 /* go back an fill in fields in IA_NA option */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000957 int sav = save_counter(t1cntr);
Simon Kelleydaf061c2012-03-12 21:57:18 +0000958 unsigned int t1, t2, fuzz = rand16();
959
960 while (fuzz > (min_time/16))
961 fuzz = fuzz/2;
962 t1 = min_time == 0xffffffff ? 0xffffffff : min_time/2 - fuzz;
963 t2 = min_time == 0xffffffff ? 0xffffffff : ((min_time/8)*7) - fuzz;
964
Simon Kelley4cb1b322012-02-06 14:30:41 +0000965 put_opt6_long(t1);
966 put_opt6_long(t2);
967 save_counter(sav);
968 }
969
970 end_opt6(o);
971 }
972 break;
973
974 }
975
976 case DHCP6CONFIRM:
977 {
978 /* set reply message type */
979 *outmsgtypep = DHCP6REPLY;
980
981 log6_packet("DHCPCONFIRM", clid, clid_len, NULL, xid, iface_name, NULL);
982
983 for (opt = packet_options; opt; opt = opt6_next(opt, end))
984 {
985 int ia_type = opt6_type(opt);
986 void *ia_option, *ia_end;
987
988 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
989 continue;
990
991 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
992 continue;
993
994 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
995 continue;
996
997 ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
998 ia_end = opt6_ptr(opt, opt6_len(opt));
999
1000 for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
1001 ia_option;
1002 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
1003 {
1004 struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
1005
Simon Kelleyceae00d2012-02-09 21:28:14 +00001006 if (!address6_available(context, req_addr, run_tag_if(tags)))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001007 {
1008 o1 = new_opt6(OPTION6_STATUS_CODE);
1009 put_opt6_short(DHCP6NOTONLINK);
1010 put_opt6_string("Confirm failed");
1011 end_opt6(o1);
1012 return 1;
1013 }
1014
1015 log6_packet("DHCPREPLY", clid, clid_len, req_addr, xid, iface_name, hostname);
1016 }
1017 }
1018
1019 o1 = new_opt6(OPTION6_STATUS_CODE);
1020 put_opt6_short(DHCP6SUCCESS );
1021 put_opt6_string("All addresses still on link");
1022 end_opt6(o1);
1023 return 1;
1024 }
1025
1026 case DHCP6IREQ:
1027 {
Simon Kelleyd1e9a582012-10-23 17:00:57 +01001028 /* We can't discriminate contexts based on address, as we don't know it.
1029 If there is only one possible context, we can use its tags */
1030 if (context && !context->current)
1031 {
1032 context->netid.next = NULL;
1033 context_tags = &context->netid;
1034 }
Simon Kelley42698cb2012-09-20 21:19:35 +01001035 log6_packet("DHCPINFORMATION-REQUEST", clid, clid_len, NULL, xid, iface_name, ignore ? "ignored" : hostname);
Simon Kelley3634c542012-02-08 14:22:37 +00001036 if (ignore)
1037 return 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001038 *outmsgtypep = DHCP6REPLY;
1039 break;
1040 }
1041
1042
1043 case DHCP6RELEASE:
1044 {
1045 /* set reply message type */
1046 *outmsgtypep = DHCP6REPLY;
1047
1048 log6_packet("DHCPRELEASE", clid, clid_len, NULL, xid, iface_name, NULL);
1049
1050 for (opt = packet_options; opt; opt = opt6_next(opt, end))
1051 {
1052 int iaid, ia_type = opt6_type(opt);
1053 void *ia_option, *ia_end;
1054 int made_ia = 0;
1055
1056 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
1057 continue;
1058
1059 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
1060 continue;
1061
1062 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
1063 continue;
1064
1065 iaid = opt6_uint(opt, 0, 4);
1066 ia_end = opt6_ptr(opt, opt6_len(opt));
1067 ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
1068
1069 /* reset "USED" flags on leases */
Simon Kelley8b460612012-09-08 21:47:28 +01001070 lease6_filter(ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, context);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001071
1072 for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
1073 ia_option;
1074 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
1075 {
1076 struct dhcp_lease *lease;
1077
1078 if ((lease = lease6_find(clid, clid_len, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
1079 iaid, opt6_ptr(ia_option, 0))))
1080 lease_prune(lease, now);
1081 else
1082 {
1083 if (!made_ia)
1084 {
1085 o = new_opt6(ia_type);
1086 put_opt6_long(iaid);
1087 if (ia_type == OPTION6_IA_NA)
1088 {
1089 put_opt6_long(0);
1090 put_opt6_long(0);
1091 }
1092 made_ia = 1;
1093 }
1094
1095 o1 = new_opt6(OPTION6_IAADDR);
1096 put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
1097 put_opt6_long(0);
1098 put_opt6_long(0);
1099 end_opt6(o1);
1100 }
1101 }
1102
1103 if (made_ia)
1104 {
1105 o1 = new_opt6(OPTION6_STATUS_CODE);
1106 put_opt6_short(DHCP6NOBINDING);
1107 put_opt6_string("No binding found");
1108 end_opt6(o1);
1109
1110 end_opt6(o);
1111 }
1112 }
1113
1114 o1 = new_opt6(OPTION6_STATUS_CODE);
1115 put_opt6_short(DHCP6SUCCESS);
1116 put_opt6_string("Release received");
1117 end_opt6(o1);
1118
1119 return 1;
1120 }
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001121
1122 case DHCP6DECLINE:
1123 {
1124 /* set reply message type */
1125 *outmsgtypep = DHCP6REPLY;
1126
1127 log6_packet("DHCPDECLINE", clid, clid_len, NULL, xid, iface_name, NULL);
1128
1129 for (opt = packet_options; opt; opt = opt6_next(opt, end))
1130 {
1131 int iaid, ia_type = opt6_type(opt);
1132 void *ia_option, *ia_end;
1133 int made_ia = 0;
1134
1135 if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA)
1136 continue;
1137
1138 if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
1139 continue;
1140
1141 if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
1142 continue;
1143
1144 iaid = opt6_uint(opt, 0, 4);
1145 ia_end = opt6_ptr(opt, opt6_len(opt));
1146 ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4);
1147
1148 /* reset "USED" flags on leases */
Simon Kelley8b460612012-09-08 21:47:28 +01001149 lease6_filter(ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, context);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001150
1151 for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24);
1152 ia_option;
1153 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
1154 {
1155 struct dhcp_lease *lease;
1156 struct in6_addr *addrp = opt6_ptr(ia_option, 0);
1157
1158 if (have_config(config, CONFIG_ADDR6) &&
1159 memcmp(&config->addr6, addrp, IN6ADDRSZ) == 0)
1160 {
Simon Kelleyceae00d2012-02-09 21:28:14 +00001161 prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001162 inet_ntop(AF_INET6, addrp, daemon->addrbuff, ADDRSTRLEN);
1163 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyceae00d2012-02-09 21:28:14 +00001164 daemon->addrbuff, daemon->dhcp_buff3);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001165 config->flags |= CONFIG_DECLINED;
1166 config->decline_time = now;
1167 }
1168 else
1169 /* make sure this host gets a different address next time. */
1170 for (; context; context = context->current)
1171 context->addr_epoch++;
1172
1173 if ((lease = lease6_find(clid, clid_len, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
1174 iaid, opt6_ptr(ia_option, 0))))
1175 lease_prune(lease, now);
1176 else
1177 {
1178 if (!made_ia)
1179 {
1180 o = new_opt6(ia_type);
1181 put_opt6_long(iaid);
1182 if (ia_type == OPTION6_IA_NA)
1183 {
1184 put_opt6_long(0);
1185 put_opt6_long(0);
1186 }
1187 made_ia = 1;
1188 }
1189
1190 o1 = new_opt6(OPTION6_IAADDR);
1191 put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
1192 put_opt6_long(0);
1193 put_opt6_long(0);
1194 end_opt6(o1);
1195 }
1196 }
1197
1198 if (made_ia)
1199 {
1200 o1 = new_opt6(OPTION6_STATUS_CODE);
1201 put_opt6_short(DHCP6NOBINDING);
1202 put_opt6_string("No binding found");
1203 end_opt6(o1);
1204
1205 end_opt6(o);
1206 }
1207
1208 }
1209 return 1;
1210 }
1211
Simon Kelley4cb1b322012-02-06 14:30:41 +00001212 }
1213
1214
1215 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
1216 tagif = option_filter(tags, context_tags, daemon->dhcp_opts6);
1217
1218 oro = opt6_find(packet_options, end, OPTION6_ORO, 0);
1219
1220 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
1221 {
1222 /* netids match and not encapsulated? */
1223 if (!(opt_cfg->flags & DHOPT_TAGOK))
1224 continue;
1225
1226 if (!(opt_cfg->flags & DHOPT_FORCE) && oro)
1227 {
1228 for (i = 0; i < opt6_len(oro) - 1; i += 2)
1229 if (opt6_uint(oro, i, 2) == (unsigned)opt_cfg->opt)
1230 break;
1231
1232 /* option not requested */
1233 if (i >= opt6_len(oro) - 1)
1234 continue;
1235 }
1236
1237 if (opt_cfg->opt == OPTION6_DNS_SERVER)
1238 {
1239 done_dns = 1;
1240 if (opt_cfg->len == 0)
1241 continue;
1242 }
1243
1244 o = new_opt6(opt_cfg->opt);
Simon Kelley4b86b652012-02-29 11:45:37 +00001245 if (opt_cfg->flags & DHOPT_ADDR6)
1246 {
1247 int j;
1248 struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
1249 for (j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
1250 {
1251 /* zero means "self" (but not in vendorclass options.) */
1252 if (IN6_IS_ADDR_UNSPECIFIED(a))
1253 {
1254 if (IN6_IS_ADDR_UNSPECIFIED(&context->local6))
1255 put_opt6(fallback, IN6ADDRSZ);
1256 else
1257 put_opt6(&context->local6, IN6ADDRSZ);
1258 }
1259 else
1260 put_opt6(a, IN6ADDRSZ);
1261 }
1262 }
1263 else if (opt_cfg->val)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001264 put_opt6(opt_cfg->val, opt_cfg->len);
1265 end_opt6(o);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001266 }
1267
Simon Kelleye44ddca2012-02-18 17:08:50 +00001268 if (!done_dns &&
1269 (!IN6_IS_ADDR_UNSPECIFIED(&context->local6) ||
1270 !IN6_IS_ADDR_UNSPECIFIED(fallback)))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001271 {
1272 o = new_opt6(OPTION6_DNS_SERVER);
Simon Kelleye44ddca2012-02-18 17:08:50 +00001273 if (IN6_IS_ADDR_UNSPECIFIED(&context->local6))
1274 put_opt6(fallback, IN6ADDRSZ);
1275 else
1276 put_opt6(&context->local6, IN6ADDRSZ);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001277 end_opt6(o);
1278 }
1279
1280 /* handle vendor-identifying vendor-encapsulated options,
1281 dhcp-option = vi-encap:13,17,....... */
1282 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
1283 opt_cfg->flags &= ~DHOPT_ENCAP_DONE;
Simon Kelley4b86b652012-02-29 11:45:37 +00001284
Simon Kelley4cb1b322012-02-06 14:30:41 +00001285 if (oro)
1286 for (i = 0; i < opt6_len(oro) - 1; i += 2)
1287 if (opt6_uint(oro, i, 2) == OPTION6_VENDOR_OPTS)
1288 do_encap = 1;
1289
1290 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
1291 {
1292 if (opt_cfg->flags & DHOPT_RFC3925)
1293 {
1294 int found = 0;
1295 struct dhcp_opt *oc;
1296
1297 if (opt_cfg->flags & DHOPT_ENCAP_DONE)
1298 continue;
1299
1300 for (oc = daemon->dhcp_opts6; oc; oc = oc->next)
1301 {
1302 oc->flags &= ~DHOPT_ENCAP_MATCH;
1303
1304 if (!(oc->flags & DHOPT_RFC3925) || opt_cfg->u.encap != oc->u.encap)
1305 continue;
1306
1307 oc->flags |= DHOPT_ENCAP_DONE;
1308 if (match_netid(oc->netid, tagif, 1))
1309 {
1310 /* option requested/forced? */
1311 if (!oro || do_encap || (oc->flags & DHOPT_FORCE))
1312 {
1313 oc->flags |= DHOPT_ENCAP_MATCH;
1314 found = 1;
1315 }
1316 }
1317 }
1318
1319 if (found)
1320 {
1321 o = new_opt6(OPTION6_VENDOR_OPTS);
1322 put_opt6_long(opt_cfg->u.encap);
1323
1324 for (oc = daemon->dhcp_opts6; oc; oc = oc->next)
1325 if (oc->flags & DHOPT_ENCAP_MATCH)
1326 {
1327 o1 = new_opt6(oc->opt);
1328 put_opt6(oc->val, oc->len);
1329 end_opt6(o1);
1330 }
1331 end_opt6(o);
1332 }
1333 }
1334 }
Simon Kelley07933802012-02-14 20:55:25 +00001335
1336
Simon Kelley4cb1b322012-02-06 14:30:41 +00001337 if (hostname)
1338 {
1339 unsigned char *p;
1340 size_t len = strlen(hostname);
1341
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001342 if (send_domain)
1343 len += strlen(send_domain) + 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001344
1345 o = new_opt6(OPTION6_FQDN);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +00001346 if ((p = expand(len + 3)))
1347 {
1348 *(p++) = fqdn_flags;
1349 p = do_rfc1035_name(p, hostname);
1350 if (send_domain)
1351 p = do_rfc1035_name(p, send_domain);
1352 *p = 0;
1353 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001354 end_opt6(o);
1355 }
1356
1357
1358 /* logging */
1359 if (option_bool(OPT_LOG_OPTS) && oro)
1360 {
1361 char *q = daemon->namebuff;
1362 for (i = 0; i < opt6_len(oro) - 1; i += 2)
1363 {
1364 char *s = option_string(AF_INET6, opt6_uint(oro, i, 2), NULL, 0, NULL, 0);
1365 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
1366 "%d%s%s%s",
1367 opt6_uint(oro, i, 2),
1368 strlen(s) != 0 ? ":" : "",
1369 s,
1370 (i > opt6_len(oro) - 3) ? "" : ", ");
1371 if ( i > opt6_len(oro) - 3 || (q - daemon->namebuff) > 40)
1372 {
1373 q = daemon->namebuff;
1374 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), xid, daemon->namebuff);
1375 }
1376 }
1377 }
1378
1379 log_tags(tagif, xid);
1380
1381 if (option_bool(OPT_LOG_OPTS))
Simon Kelley6c8f21e2012-03-12 15:06:55 +00001382 log6_opts(0, xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1));
1383
1384 return 1;
1385}
1386
1387static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts)
1388{
1389 void *opt;
1390 char *desc = nest ? "nest" : "sent";
Simon Kelley5cfea3d2012-03-12 17:28:27 +00001391
1392 if (start_opts == end_opts)
1393 return;
1394
1395 for (opt = start_opts; opt; opt = opt6_next(opt, end_opts))
1396 {
1397 int type = opt6_type(opt);
1398 void *ia_options = NULL;
1399 char *optname;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001400
Simon Kelley5cfea3d2012-03-12 17:28:27 +00001401 if (type == OPTION6_IA_NA)
1402 {
1403 sprintf(daemon->namebuff, "IAID=%u T1=%u T2=%u",
1404 opt6_uint(opt, 0, 4), opt6_uint(opt, 4, 4), opt6_uint(opt, 8, 4));
1405 optname = "ia-na";
1406 ia_options = opt6_ptr(opt, 12);
1407 }
1408 else if (type == OPTION6_IA_TA)
1409 {
1410 sprintf(daemon->namebuff, "IAID=%u", opt6_uint(opt, 0, 4));
1411 optname = "ia-ta";
1412 ia_options = opt6_ptr(opt, 4);
1413 }
1414 else if (type == OPTION6_IAADDR)
1415 {
1416 inet_ntop(AF_INET6, opt6_ptr(opt, 0), daemon->addrbuff, ADDRSTRLEN);
1417 sprintf(daemon->namebuff, "%s PL=%u VL=%u",
1418 daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4));
1419 optname = "iaaddr";
1420 ia_options = opt6_ptr(opt, 24);
1421 }
1422 else if (type == OPTION6_STATUS_CODE)
1423 {
1424 int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2));
1425 memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), opt6_len(opt)-2);
1426 daemon->namebuff[len + opt6_len(opt) - 2] = 0;
1427 optname = "status";
1428 }
1429 else
1430 {
1431 /* account for flag byte on FQDN */
1432 int offset = type == OPTION6_FQDN ? 1 : 0;
1433 optname = option_string(AF_INET6, type, opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAME);
1434 }
1435
1436 my_syslog(MS_DHCP | LOG_INFO, "%u %s size:%3d option:%3d %s %s",
1437 xid, desc, opt6_len(opt), type, optname, daemon->namebuff);
1438
1439 if (ia_options)
1440 log6_opts(1, xid, ia_options, opt6_ptr(opt, opt6_len(opt)));
1441 }
Simon Kelley6c8f21e2012-03-12 15:06:55 +00001442}
1443
Simon Kelley4cb1b322012-02-06 14:30:41 +00001444static void log6_packet(char *type, unsigned char *clid, int clid_len, struct in6_addr *addr, int xid, char *iface, char *string)
1445{
1446 /* avoid buffer overflow */
1447 if (clid_len > 100)
1448 clid_len = 100;
1449
1450 print_mac(daemon->namebuff, clid, clid_len);
1451
1452 if (addr)
1453 {
1454 inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, 255);
1455 strcat(daemon->dhcp_buff2, " ");
1456 }
1457 else
1458 daemon->dhcp_buff2[0] = 0;
1459
1460 if(option_bool(OPT_LOG_OPTS))
Simon Kelley58dc02e2012-02-27 11:49:37 +00001461 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s %s",
Simon Kelley4cb1b322012-02-06 14:30:41 +00001462 xid,
1463 type,
1464 iface,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001465 daemon->dhcp_buff2,
Simon Kelley58dc02e2012-02-27 11:49:37 +00001466 daemon->namebuff,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001467 string ? string : "");
1468 else
Simon Kelley58dc02e2012-02-27 11:49:37 +00001469 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s %s",
Simon Kelley4cb1b322012-02-06 14:30:41 +00001470 type,
1471 iface,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001472 daemon->dhcp_buff2,
Simon Kelley58dc02e2012-02-27 11:49:37 +00001473 daemon->namebuff,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001474 string ? string : "");
1475}
1476
1477static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize)
Simon Kelleyc72daea2012-01-05 21:33:27 +00001478{
1479 u16 opt, opt_len;
1480 void *start;
1481
1482 if (!opts)
1483 return NULL;
1484
1485 while (1)
1486 {
1487 if (end - opts < 4)
1488 return NULL;
1489
1490 start = opts;
1491 GETSHORT(opt, opts);
1492 GETSHORT(opt_len, opts);
1493
1494 if (opt_len > (end - opts))
1495 return NULL;
1496
1497 if (opt == search && (opt_len >= minsize))
1498 return start;
1499
1500 opts += opt_len;
1501 }
1502}
1503
Simon Kelley4cb1b322012-02-06 14:30:41 +00001504static void *opt6_next(void *opts, void *end)
Simon Kelleyc72daea2012-01-05 21:33:27 +00001505{
1506 u16 opt_len;
1507
1508 if (end - opts < 4)
1509 return NULL;
1510
1511 opts += 2;
1512 GETSHORT(opt_len, opts);
1513
1514 if (opt_len >= (end - opts))
1515 return NULL;
1516
1517 return opts + opt_len;
1518}
Simon Kelleyc72daea2012-01-05 21:33:27 +00001519
1520static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
1521{
1522 /* this worries about unaligned data and byte order */
1523 unsigned int ret = 0;
1524 int i;
1525 unsigned char *p = opt6_ptr(opt, offset);
1526
1527 for (i = 0; i < size; i++)
1528 ret = (ret << 8) | *p++;
1529
1530 return ret;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001531}
Simon Kelleyc72daea2012-01-05 21:33:27 +00001532
Simon Kelleyc72daea2012-01-05 21:33:27 +00001533#endif