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