Add DHCPv6 functionality.
[l2tpns.git] / dhcp6.c
1 /*
2 * Fernando ALVES 2014
3 * Add functionality DHCPv6 to l2tpns.
4 * GPL licenced
5 */
6
7 #include <netinet/icmp6.h>
8 #include <netinet/ip6.h>
9 #include <netinet/udp.h>
10
11 #include "dhcp6.h"
12 #include "l2tpns.h"
13 #include "ipv6_u.h"
14
15 struct dhcp6_in_option
16 {
17 struct dhcp6_mess_hdr *p_mess_hdr;
18 struct dhcp6_opt_h *p_opt_clientid;
19 struct dhcp6_opt_h *p_opt_serverid;
20 struct dhcp6_opt_h *p_opt_ia_na;
21 struct dhcp6_opt_h *p_opt_ia_ta;
22 struct dhcp6_opt_h *p_opt_ia_pd;
23 struct dhcp6_opt_h *p_opt_oro;
24 struct dhcp6_opt_h *p_opt_rapidcommit;
25 };
26
27 static struct dhcp6_opt_serverid dhcp6_local_serverid;
28 static struct dhcp6_in_option list_option;
29
30 static int dhcpv6_format_dns_search_name(const char *strdns, uint8_t *buffer);
31
32 static void dhcp6_send_reply(sessionidt s, tunnelidt t, struct in6_addr *ip6_src)
33 {
34 struct ip6_hdr *p_ip6_hdr;
35 struct udphdr *p_udp;
36 struct dhcp6_mess_hdr *p_mess_hdr;
37 struct dhcp6_opt_h *p_opt;
38 struct ipv6_pseudo_hdr pseudo_hdr;
39 uint8_t b[MAXETHER + 20];
40 int len;
41
42 memset(b, 0, sizeof(b));
43 p_ip6_hdr = (struct ip6_hdr *) makeppp(b, sizeof(b), 0, 0, s, t, PPPIPV6, 0, 0, 0);
44
45 // IPv6 Header
46 p_ip6_hdr->ip6_vfc = 0x60; // IPv6
47 p_ip6_hdr->ip6_plen = 0; // Length of payload (not header) (calculation below)
48 p_ip6_hdr->ip6_nxt = IPPROTO_UDP; // icmp6 is next
49 p_ip6_hdr->ip6_hlim = 1; // Hop limit
50 // IPv6 Src FE02::1:2
51 inet_pton(AF_INET6, "FE02::1:2", &p_ip6_hdr->ip6_src.s6_addr);
52 // IPv6 Dest
53 memcpy(&p_ip6_hdr->ip6_dst.s6_addr, ip6_src, sizeof(p_ip6_hdr->ip6_dst.s6_addr));
54
55 // UDP Header
56 p_udp = (struct udphdr *) &p_ip6_hdr[1];
57 p_udp->source = htons(547);
58 p_udp->dest = htons(546);
59 p_udp->len = 0; // Length udp size_udp_header + data (calculation below)
60 p_udp->check = 0; // checksum (calculation below with ip pseudo header)
61
62 // DHCPv6 msg header
63 p_mess_hdr = (struct dhcp6_mess_hdr *) &p_udp[1];
64 if (list_option.p_mess_hdr->type == DHCP6_SOLICIT)
65 p_mess_hdr->type = list_option.p_opt_rapidcommit ? DHCP6_REPLY : DHCP6_ADVERTISE;
66 else
67 p_mess_hdr->type = DHCP6_REPLY;
68
69 p_mess_hdr->trans_id = list_option.p_mess_hdr->trans_id;
70
71 // DHCPv6 options header
72 p_opt = (struct dhcp6_opt_h *) &p_mess_hdr[1];
73 memcpy(p_opt, &dhcp6_local_serverid, ntohs(dhcp6_local_serverid.opt_hdr.len) + sizeof(dhcp6_local_serverid.opt_hdr)); // ServerID
74 p_opt = (struct dhcp6_opt_h *) (((uint8_t *) p_opt) + ntohs(p_opt->len) + sizeof(*p_opt)); // next option
75
76 if (list_option.p_opt_clientid)
77 {
78 memcpy(p_opt, list_option.p_opt_clientid, ntohs(list_option.p_opt_clientid->len) + sizeof(*p_opt)); // ClientID
79 p_opt = (struct dhcp6_opt_h *) (((uint8_t *) p_opt) + ntohs(p_opt->len) + sizeof(*p_opt)); // next option
80 }
81
82 if (list_option.p_opt_ia_pd && (list_option.p_mess_hdr->type != DHCP6_INFORMATION_REQUEST))
83 {
84 p_opt->code = htons(D6_OPT_IA_PD); // D6_OPT_IA_PD
85 ((struct dhcp6_opt_ia_pd *)p_opt)->iaid = ((struct dhcp6_opt_ia_pd *)list_option.p_opt_ia_pd)->iaid;
86 ((struct dhcp6_opt_ia_pd *)p_opt)->T1 = (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime/2) : 0xFFFFFFFF;
87 ((struct dhcp6_opt_ia_pd *)p_opt)->T2 = (config->dhcp6_preferred_lifetime > 0) ? htonl((config->dhcp6_preferred_lifetime*4)/5) : 0xFFFFFFFF;
88
89 if ((list_option.p_mess_hdr->type == DHCP6_RENEW) && session[s].dhcpv6_prefix_iaid != ((struct dhcp6_opt_ia_pd *)list_option.p_opt_ia_pd)->iaid)
90 {
91 p_opt->len = htons(sizeof(struct dhcp6_opt_ia_pd) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_status));
92 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_pd *)p_opt)[1];
93
94 ((struct dhcp6_opt_status *)p_opt)->hdr.code = htons(D6_OPT_STATUS_CODE);
95 ((struct dhcp6_opt_status *)p_opt)->hdr.len = htons(2);
96 ((struct dhcp6_opt_status *)p_opt)->code = htons(D6_STATUS_NoBinding);
97 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_status *)p_opt)[1]; // next option
98 }
99 else
100 {
101 if (list_option.p_mess_hdr->type == DHCP6_REQUEST || list_option.p_opt_rapidcommit)
102 {
103 session[s].dhcpv6_prefix_iaid = ((struct dhcp6_opt_ia_pd *)list_option.p_opt_ia_pd)->iaid;
104 }
105
106 p_opt->len = htons(sizeof(struct dhcp6_opt_ia_pd) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_ia_prefix));
107 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_pd *)p_opt)[1];
108
109 ((struct dhcp6_opt_ia_prefix *)p_opt)->hdr.code = htons(D6_OPT_IAPREFIX);
110 ((struct dhcp6_opt_ia_prefix *)p_opt)->hdr.len = htons(sizeof(struct dhcp6_opt_ia_prefix) - sizeof(*p_opt));
111 ((struct dhcp6_opt_ia_prefix *)p_opt)->pref_lifetime= (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime) : 0xFFFFFFFF;
112 ((struct dhcp6_opt_ia_prefix *)p_opt)->valid_lifetime= (config->dhcp6_valid_lifetime > 0) ? htonl(config->dhcp6_valid_lifetime) : 0xFFFFFFFF;
113 ((struct dhcp6_opt_ia_prefix *)p_opt)->prefix_len = session[s].ipv6prefixlen;
114 ((struct dhcp6_opt_ia_prefix *)p_opt)->prefix = session[s].ipv6route;
115 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_prefix *)p_opt)[1]; // next option
116 }
117 }
118
119 if (list_option.p_opt_ia_na && (list_option.p_mess_hdr->type != DHCP6_INFORMATION_REQUEST))
120 {
121 p_opt->code = htons(D6_OPT_IA_NA); // D6_OPT_IA_NA
122 ((struct dhcp6_opt_ia_na *)p_opt)->iaid = ((struct dhcp6_opt_ia_na *)list_option.p_opt_ia_na)->iaid;
123 ((struct dhcp6_opt_ia_na *)p_opt)->T1 = (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime/2) : 0xFFFFFFFF;
124 ((struct dhcp6_opt_ia_na *)p_opt)->T2 = (config->dhcp6_preferred_lifetime > 0) ? htonl((config->dhcp6_preferred_lifetime*4)/5) : 0xFFFFFFFF;
125
126 if ((list_option.p_mess_hdr->type == DHCP6_RENEW) && session[s].dhcpv6_iana_iaid != ((struct dhcp6_opt_ia_na *)list_option.p_opt_ia_na)->iaid)
127 {
128 p_opt->len = htons(sizeof(struct dhcp6_opt_ia_na) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_status));
129 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_na *)p_opt)[1];
130
131 ((struct dhcp6_opt_status *)p_opt)->hdr.code = htons(D6_OPT_STATUS_CODE);
132 ((struct dhcp6_opt_status *)p_opt)->hdr.len = htons(2);
133 ((struct dhcp6_opt_status *)p_opt)->code = htons(D6_STATUS_NoBinding);
134 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_status *)p_opt)[1]; // next option
135 }
136 else
137 {
138 in_addr_t addr_ipv4;
139
140 if (list_option.p_mess_hdr->type == DHCP6_REQUEST || list_option.p_opt_rapidcommit)
141 {
142 session[s].dhcpv6_iana_iaid = ((struct dhcp6_opt_ia_na *)list_option.p_opt_ia_na)->iaid;
143 }
144
145 p_opt->len = htons(sizeof(struct dhcp6_opt_ia_na) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_ia_addr));
146 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_na *)p_opt)[1];
147
148 ((struct dhcp6_opt_ia_addr *)p_opt)->hdr.code = htons(D6_OPT_IAADDR);
149 ((struct dhcp6_opt_ia_addr *)p_opt)->hdr.len = htons(sizeof(struct dhcp6_opt_ia_addr) - sizeof(*p_opt));
150
151 if (session[s].ipv6address.s6_addr[0])
152 {
153 memcpy(&((struct dhcp6_opt_ia_addr *)p_opt)->addr, &session[s].ipv6address, 16); // copy ipv6 prefix
154 }
155 else
156 {
157 memcpy(&((struct dhcp6_opt_ia_addr *)p_opt)->addr, &config->ipv6_prefix, 8); // copy prefix 64
158 addr_ipv4 = htonl(session[s].ip);
159 memcpy(&((struct dhcp6_opt_ia_addr *)p_opt)->addr.s6_addr[8], &addr_ipv4, 4); // copy ipv4
160 }
161
162 ((struct dhcp6_opt_ia_addr *)p_opt)->pref_lifetime= (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime) : 0xFFFFFFFF;
163 ((struct dhcp6_opt_ia_addr *)p_opt)->valid_lifetime= (config->dhcp6_valid_lifetime > 0) ? htonl(config->dhcp6_valid_lifetime) : 0xFFFFFFFF;
164
165 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_addr *)p_opt)[1]; // next option
166 }
167 }
168
169 if (list_option.p_opt_ia_ta && (list_option.p_mess_hdr->type != DHCP6_INFORMATION_REQUEST))
170 {
171 p_opt->code = htons(D6_OPT_IA_TA); // D6_OPT_IA_TA
172 p_opt->len = htons(sizeof(struct dhcp6_opt_ia_ta) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_status));
173 ((struct dhcp6_opt_ia_ta *)p_opt)->iaid = ((struct dhcp6_opt_ia_ta *)list_option.p_opt_ia_ta)->iaid;
174 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_ta *)p_opt)[1];
175 ((struct dhcp6_opt_status *)p_opt)->hdr.code = htons(D6_OPT_STATUS_CODE);
176 ((struct dhcp6_opt_status *)p_opt)->hdr.len = htons(2);
177 ((struct dhcp6_opt_status *)p_opt)->code = htons(D6_STATUS_UnspecFail);
178 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_status *)p_opt)[1]; // next option
179 }
180
181 if (list_option.p_opt_oro)
182 {
183 int countopt;
184 uint16_t *ptrw;
185 struct in6_addr *ptr_in6_addr;
186
187 for (countopt = ntohs(list_option.p_opt_oro->len)/2, ptrw = (uint16_t *)((struct dhcp6_opt_oro *)list_option.p_opt_oro)->opt_demand; countopt; countopt--, ptrw++)
188 {
189 if (ntohs(*ptrw) == D6_OPT_DNS_SERVERS)
190 {
191 if (config->default_ipv6_dns1.s6_addr[0])
192 {
193 p_opt->code = htons(D6_OPT_DNS_SERVERS); // D6_OPT_DNS_SERVERS
194 p_opt->len = htons(sizeof(*ptr_in6_addr));
195 ptr_in6_addr = (struct in6_addr *) &p_opt[1];
196 memcpy(ptr_in6_addr, &config->default_ipv6_dns1, sizeof(*ptr_in6_addr));
197
198 if (config->default_ipv6_dns2.s6_addr[0])
199 {
200 p_opt->len = htons(2*sizeof(*ptr_in6_addr));
201 ptr_in6_addr = &ptr_in6_addr[1];
202 memcpy(ptr_in6_addr, &config->default_ipv6_dns2, sizeof(*ptr_in6_addr));
203 }
204
205 p_opt = (struct dhcp6_opt_h *) &ptr_in6_addr[1]; // next option
206 }
207 }
208
209 if (ntohs(*ptrw) == D6_OPT_DOMAIN_LIST)
210 {
211 if (*config->default_ipv6_domain_list)
212 {
213 uint8_t buffer[255];
214 int len = dhcpv6_format_dns_search_name(config->default_ipv6_domain_list, buffer);
215
216 if (len > 0)
217 {
218 p_opt->code = htons(D6_OPT_DOMAIN_LIST); // D6_OPT_DOMAIN_LIST
219 p_opt->len = htons(len);
220 memcpy((char *)&p_opt[1], buffer, len);
221
222 p_opt = (struct dhcp6_opt_h *) (((uint8_t *) &p_opt[1]) + len); // next option
223 }
224 }
225 }
226 }
227 }
228
229 if (list_option.p_opt_rapidcommit && (list_option.p_mess_hdr->type == DHCP6_SOLICIT))
230 {
231 p_opt->code = htons(D6_OPT_RAPID_COMMIT); // D6_OPT_RAPID_COMMIT
232 p_opt->len = 0;
233 p_opt = &p_opt[1]; // next option
234 }
235
236 p_opt->code = htons(D6_OPT_PREFERENCE); // D6_OPT_PREFERENCE
237 p_opt->len = htons(1);
238 ((struct dhcp6_opt_preference *)p_opt)->pref = 255;
239 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_preference *)p_opt)[1]; // next option
240
241 // calculation of lenght
242 len = ((uint8_t *) p_opt) - ((uint8_t *) p_udp);
243 p_ip6_hdr->ip6_plen = p_udp->len = htons(len);
244
245 /* Use pseudo hearder for checksum calculation */
246 memset(&pseudo_hdr, 0, sizeof(pseudo_hdr));
247 memcpy(&pseudo_hdr.src, &p_ip6_hdr->ip6_src, 16);
248 memcpy(&pseudo_hdr.dest, &p_ip6_hdr->ip6_dst, 16);
249 pseudo_hdr.ulp_length = htonl(len); // Lenght whitout Ipv6 header
250 pseudo_hdr.nexthdr = IPPROTO_UDP;
251 // Checksum is over the udp payload plus the pseudo header
252 p_udp->check = ipv6_checksum(&pseudo_hdr, (uint8_t *) p_udp, len);
253
254 // Add ipv6 header to length
255 len += sizeof(*p_ip6_hdr);
256 LOG(3, s, t, "Send DHCPv6 message %s\n", (p_mess_hdr->type == DHCP6_REPLY) ? "REPLY" : "ADVERTISE");
257 tunnelsend(b, len + (((uint8_t *) p_ip6_hdr)-b), t); // send it...
258 }
259
260 void dhcpv6_process(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
261 {
262 struct ip6_hdr *p_ip6_hdr_in;
263 struct dhcp6_mess_hdr *p_mess_hdr;
264 struct dhcp6_opt_h *p_opt;
265 uint8_t *p_end;
266 uint16_t len;
267
268 CSTAT(dhcpv6_process);
269
270 p_ip6_hdr_in = (struct ip6_hdr *) p;
271 p_mess_hdr = (struct dhcp6_mess_hdr *) (p + 48);
272
273 LOG(3, s, t, "Got DHCPv6 message Type: %d\n", p_mess_hdr->type);
274
275 if (!session[s].ipv6route.s6_addr[0] || !session[s].ipv6prefixlen)
276 return;
277
278 p_opt = (struct dhcp6_opt_h *) &p_mess_hdr[1];
279 p_end = ((uint8_t *)p_ip6_hdr_in) + ntohs(p_ip6_hdr_in->ip6_plen) + sizeof(*p_ip6_hdr_in);
280 memset(&list_option, 0, sizeof(list_option));
281 list_option.p_mess_hdr = p_mess_hdr;
282 while (((uint8_t *)p_opt) < p_end)
283 {
284 switch(ntohs(p_opt->code))
285 {
286 case D6_OPT_CLIENTID:
287 list_option.p_opt_clientid = p_opt;
288 LOG(3, s, t, "......Option D6_OPT_CLIENTID\n");
289 break;
290 case D6_OPT_SERVERID:
291 list_option.p_opt_serverid = p_opt;
292 LOG(3, s, t, "......Option D6_OPT_SERVERID\n");
293 break;
294 case D6_OPT_RAPID_COMMIT:
295 list_option.p_opt_rapidcommit = p_opt;
296 LOG(3, s, t, "......Option D6_OPT_RAPID_COMMIT\n");
297 break;
298 case D6_OPT_IA_NA:
299 list_option.p_opt_ia_na = p_opt;
300 LOG(3, s, t, "......Option D6_OPT_IA_NA\n");
301 break;
302 case D6_OPT_IA_TA:
303 list_option.p_opt_ia_ta = p_opt;
304 LOG(3, s, t, "......Option D6_OPT_IA_TA\n");
305 break;
306 case D6_OPT_ORO:
307 list_option.p_opt_oro = p_opt;
308 LOG(3, s, t, "......Option D6_OPT_ORO\n");
309 break;
310 case D6_OPT_IA_PD:
311 list_option.p_opt_ia_pd = p_opt;
312 LOG(3, s, t, "......Option D6_OPT_IA_PD\n");
313 break;
314 case D6_OPT_ELAPSED_TIME:
315 LOG(3, s, t, "......Option D6_OPT_ELAPSED_TIME\n");
316 break;
317
318 default:
319 LOG(3, s, t, "......DHCPv6 option: %d\n", ntohs(p_opt->code));
320 break;
321 }
322 p_opt = (struct dhcp6_opt_h *)(((uint8_t *) p_opt) + ntohs(p_opt->len) + sizeof(*p_opt));
323 }
324
325 switch(p_mess_hdr->type)
326 {
327 case DHCP6_SOLICIT:
328 {
329 LOG(3, s, t, "..(Type %d = Solicit)\n", p_mess_hdr->type);
330
331 if (!list_option.p_opt_clientid)
332 {
333 LOG(3, s, t, "DHCPv6: error no Client-ID\n");
334 return;
335 }
336 else if (list_option.p_opt_rapidcommit)
337 {
338 if (!session[s].dhcpv6_client_id.opt_hdr.len)
339 {
340 len = ntohs(list_option.p_opt_clientid->len);
341
342 if ((len > 0) && (len <= sizeof(struct dhcp6_duid)))
343 {
344 memcpy(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + len);
345 }
346 else
347 {
348 LOG(3, s, t, "DHCPv6: Error malformed Client-ID option\n");
349 return;
350 }
351 }
352 else if (session[s].dhcpv6_client_id.opt_hdr.len != list_option.p_opt_clientid->len ||
353 memcmp(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_clientid->len)))
354 {
355 LOG(3, s, t, "DHCPv6: Error unmatched Client-ID option\n");
356 return;
357 }
358 }
359
360 if (list_option.p_opt_serverid)
361 {
362 LOG(3, s, t, "DHCPv6: Error unexpected Server-ID option on Solicit\n");
363 return;
364 }
365
366 dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src);
367 }
368 break;
369
370 case DHCP6_REQUEST:
371 {
372 LOG(3, s, t, "..(Type %d = Request)\n", p_mess_hdr->type);
373
374 if (!list_option.p_opt_clientid)
375 {
376 LOG(3, s, t, "DHCPv6: error no Client-ID\n");
377 return;
378 }
379
380 if (!list_option.p_opt_serverid)
381 {
382 LOG(3, s, t, "DHCPv6: error no Server-ID\n");
383 return;
384 }
385 else if (dhcp6_local_serverid.opt_hdr.len != list_option.p_opt_serverid->len ||
386 memcmp(&dhcp6_local_serverid, list_option.p_opt_serverid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_serverid->len)))
387 {
388 LOG(3, s, t, "DHCPv6: Error unmatched Server-ID option\n");
389 return;
390 }
391
392 if (!session[s].dhcpv6_client_id.opt_hdr.len)
393 {
394 len = ntohs(list_option.p_opt_clientid->len);
395
396 if ((len > 0) && (len <= sizeof(struct dhcp6_duid)))
397 {
398 memcpy(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + len);
399 }
400 else
401 {
402 LOG(3, s, t, "DHCPv6: Error malformed Client-ID option\n");
403 return;
404 }
405 }
406 else if ( session[s].dhcpv6_client_id.opt_hdr.len != list_option.p_opt_clientid->len ||
407 memcmp(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_clientid->len)))
408 {
409 LOG(3, s, t, "DHCPv6: Error unmatched Client-ID option\n");
410 return;
411 }
412
413 dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src);
414 }
415 break;
416
417 case DHCP6_RENEW:
418 {
419 LOG(3, s, t, "..(Type %d = Renew)\n", p_mess_hdr->type);
420
421 if (!list_option.p_opt_clientid)
422 {
423 LOG(3, s, t, "DHCPv6: error no Client-ID\n");
424 return;
425 }
426 else if ( session[s].dhcpv6_client_id.opt_hdr.len != list_option.p_opt_clientid->len ||
427 memcmp(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_clientid->len)))
428 {
429 LOG(3, s, t, "DHCPv6: Error unmatched Client-ID option\n");
430 return;
431 }
432
433 if (!list_option.p_opt_serverid)
434 {
435 LOG(3, s, t, "DHCPv6: error no Server-ID\n");
436 return;
437 }
438 else if (dhcp6_local_serverid.opt_hdr.len != list_option.p_opt_serverid->len ||
439 memcmp(&dhcp6_local_serverid, list_option.p_opt_serverid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_serverid->len)))
440 {
441 LOG(3, s, t, "DHCPv6: Error unmatched Server-ID option\n");
442 return;
443 }
444
445 dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src);
446 }
447 break;
448
449 case DHCP6_INFORMATION_REQUEST:
450 {
451 LOG(3, s, t, "..(Type %d = Information Request)\n", p_mess_hdr->type);
452
453 if (!list_option.p_opt_clientid)
454 {
455 LOG(3, s, t, "DHCPv6: error no Client-ID\n");
456 return;
457 }
458
459 dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src);
460 }
461 break;
462
463 case DHCP6_REBIND:
464 {
465 LOG(3, s, t, "..(Type %d = Rebind)\n", p_mess_hdr->type);
466 }
467 break;
468
469 case DHCP6_RELEASE:
470 {
471 LOG(3, s, t, "..(Type %d = Release)\n", p_mess_hdr->type);
472 }
473 break;
474
475 case DHCP6_DECLINE:
476 {
477 LOG(3, s, t, "..(Type %d = Decline)\n", p_mess_hdr->type);
478 }
479 break;
480
481 default:
482 LOG(3, s, t, "Got unknown DHCPv6 Type: %d\n", *(p + 38));
483 break;
484 }
485
486 return;
487 }
488
489 static int dhcpv6_format_dns_search_name(const char *strdns, uint8_t *buffer)
490 {
491 int n = strlen(strdns);
492 const char *ptr;
493
494 if (strdns[n - 1] == '.') n++;
495 else n += 2;
496
497 if (n > 255) {
498 LOG(3, 0, 0, "DHCPv6: DNS search '%s' is too long\n", strdns);
499 return 0;
500 }
501
502 while (1)
503 {
504 ptr = strchr(strdns, '.');
505
506 if (!ptr) ptr = strchr(strdns, 0);
507
508 if (ptr - strdns > 63)
509 {
510 LOG(3, 0, 0, "DHCPv6: DNS search '%s' is invalid\n", strdns);
511 return 0;
512 }
513
514 *buffer = ptr - strdns;
515 memcpy(buffer + 1, strdns, ptr - strdns);
516 buffer += 1 + (ptr - strdns);
517 strdns = ptr + 1;
518
519 if (!*ptr || !*strdns)
520 {
521 *buffer = 0;
522 break;
523 }
524 }
525
526 return n;
527 }
528
529 void dhcpv6_init(void)
530 {
531 uint32_t id;
532
533 dhcp6_local_serverid.opt_hdr.code = htons(D6_OPT_SERVERID);
534 dhcp6_local_serverid.opt_hdr.len = htons(4 + sizeof(id));
535 dhcp6_local_serverid.duid.type = htons(DUID_LL);
536 dhcp6_local_serverid.duid.u.ll.htype = htons(27);
537
538 if (config->dhcp6_server_duid)
539 id = htobe32(config->dhcp6_server_duid);
540 else
541 id = htobe32(0xFDFDFAFA);
542
543 memcpy(dhcp6_local_serverid.duid.u.ll.addr, &id, sizeof(id));
544 }