Fix: mismatch variable type (cluster_port).
[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 struct dhcp6_opt_h *p_opt_head;
102 int r;
103 uint16_t lenopt;
104
105 if (list_option.p_mess_hdr->type == DHCP6_REQUEST || list_option.p_opt_rapidcommit)
106 {
107 session[s].dhcpv6_prefix_iaid = ((struct dhcp6_opt_ia_pd *)list_option.p_opt_ia_pd)->iaid;
108 }
109
110 p_opt_head = p_opt;
111 lenopt = sizeof(struct dhcp6_opt_ia_pd) - sizeof(*p_opt);
112
113 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_pd *)p_opt)[1];
114
115 for (r = 0; r < MAXROUTE6 && session[s].route6[r].ipv6route.s6_addr[0] && session[s].route6[r].ipv6prefixlen; r++)
116 {
117 ((struct dhcp6_opt_ia_prefix *)p_opt)->hdr.code = htons(D6_OPT_IAPREFIX);
118 ((struct dhcp6_opt_ia_prefix *)p_opt)->hdr.len = htons(sizeof(struct dhcp6_opt_ia_prefix) - sizeof(*p_opt));
119 ((struct dhcp6_opt_ia_prefix *)p_opt)->pref_lifetime= (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime) : 0xFFFFFFFF;
120 ((struct dhcp6_opt_ia_prefix *)p_opt)->valid_lifetime= (config->dhcp6_valid_lifetime > 0) ? htonl(config->dhcp6_valid_lifetime) : 0xFFFFFFFF;
121 ((struct dhcp6_opt_ia_prefix *)p_opt)->prefix_len = session[s].route6[r].ipv6prefixlen;
122 ((struct dhcp6_opt_ia_prefix *)p_opt)->prefix = session[s].route6[r].ipv6route;
123
124 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_prefix *)p_opt)[1]; // next option
125 lenopt += sizeof(struct dhcp6_opt_ia_prefix);
126 }
127 p_opt_head->len = htons(lenopt);
128 }
129 }
130
131 if (list_option.p_opt_ia_na && (list_option.p_mess_hdr->type != DHCP6_INFORMATION_REQUEST))
132 {
133 p_opt->code = htons(D6_OPT_IA_NA); // D6_OPT_IA_NA
134 ((struct dhcp6_opt_ia_na *)p_opt)->iaid = ((struct dhcp6_opt_ia_na *)list_option.p_opt_ia_na)->iaid;
135 ((struct dhcp6_opt_ia_na *)p_opt)->T1 = (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime/2) : 0xFFFFFFFF;
136 ((struct dhcp6_opt_ia_na *)p_opt)->T2 = (config->dhcp6_preferred_lifetime > 0) ? htonl((config->dhcp6_preferred_lifetime*4)/5) : 0xFFFFFFFF;
137
138 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)
139 {
140 p_opt->len = htons(sizeof(struct dhcp6_opt_ia_na) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_status));
141 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_na *)p_opt)[1];
142
143 ((struct dhcp6_opt_status *)p_opt)->hdr.code = htons(D6_OPT_STATUS_CODE);
144 ((struct dhcp6_opt_status *)p_opt)->hdr.len = htons(2);
145 ((struct dhcp6_opt_status *)p_opt)->code = htons(D6_STATUS_NoBinding);
146 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_status *)p_opt)[1]; // next option
147 }
148 else
149 {
150 in_addr_t addr_ipv4;
151
152 if (list_option.p_mess_hdr->type == DHCP6_REQUEST || list_option.p_opt_rapidcommit)
153 {
154 session[s].dhcpv6_iana_iaid = ((struct dhcp6_opt_ia_na *)list_option.p_opt_ia_na)->iaid;
155 }
156
157 p_opt->len = htons(sizeof(struct dhcp6_opt_ia_na) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_ia_addr));
158 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_na *)p_opt)[1];
159
160 ((struct dhcp6_opt_ia_addr *)p_opt)->hdr.code = htons(D6_OPT_IAADDR);
161 ((struct dhcp6_opt_ia_addr *)p_opt)->hdr.len = htons(sizeof(struct dhcp6_opt_ia_addr) - sizeof(*p_opt));
162
163 if (session[s].ipv6address.s6_addr[0])
164 {
165 memcpy(&((struct dhcp6_opt_ia_addr *)p_opt)->addr, &session[s].ipv6address, 16); // copy ipv6 prefix
166 }
167 else
168 {
169 memcpy(&((struct dhcp6_opt_ia_addr *)p_opt)->addr, &config->ipv6_prefix, 8); // copy prefix 64
170 addr_ipv4 = htonl(session[s].ip);
171 memcpy(&((struct dhcp6_opt_ia_addr *)p_opt)->addr.s6_addr[8], &addr_ipv4, 4); // copy ipv4
172 }
173
174 ((struct dhcp6_opt_ia_addr *)p_opt)->pref_lifetime= (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime) : 0xFFFFFFFF;
175 ((struct dhcp6_opt_ia_addr *)p_opt)->valid_lifetime= (config->dhcp6_valid_lifetime > 0) ? htonl(config->dhcp6_valid_lifetime) : 0xFFFFFFFF;
176
177 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_addr *)p_opt)[1]; // next option
178 }
179 }
180
181 if (list_option.p_opt_ia_ta && (list_option.p_mess_hdr->type != DHCP6_INFORMATION_REQUEST))
182 {
183 p_opt->code = htons(D6_OPT_IA_TA); // D6_OPT_IA_TA
184 p_opt->len = htons(sizeof(struct dhcp6_opt_ia_ta) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_status));
185 ((struct dhcp6_opt_ia_ta *)p_opt)->iaid = ((struct dhcp6_opt_ia_ta *)list_option.p_opt_ia_ta)->iaid;
186 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_ta *)p_opt)[1];
187 ((struct dhcp6_opt_status *)p_opt)->hdr.code = htons(D6_OPT_STATUS_CODE);
188 ((struct dhcp6_opt_status *)p_opt)->hdr.len = htons(2);
189 ((struct dhcp6_opt_status *)p_opt)->code = htons(D6_STATUS_UnspecFail);
190 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_status *)p_opt)[1]; // next option
191 }
192
193 if (list_option.p_opt_oro)
194 {
195 int countopt;
196 uint16_t *ptrw;
197 struct in6_addr *ptr_in6_addr;
198
199 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++)
200 {
201 if (ntohs(*ptrw) == D6_OPT_DNS_SERVERS)
202 {
203 if (config->default_ipv6_dns1.s6_addr[0])
204 {
205 p_opt->code = htons(D6_OPT_DNS_SERVERS); // D6_OPT_DNS_SERVERS
206 p_opt->len = htons(sizeof(*ptr_in6_addr));
207 ptr_in6_addr = (struct in6_addr *) &p_opt[1];
208 memcpy(ptr_in6_addr, &config->default_ipv6_dns1, sizeof(*ptr_in6_addr));
209
210 if (config->default_ipv6_dns2.s6_addr[0])
211 {
212 p_opt->len = htons(2*sizeof(*ptr_in6_addr));
213 ptr_in6_addr = &ptr_in6_addr[1];
214 memcpy(ptr_in6_addr, &config->default_ipv6_dns2, sizeof(*ptr_in6_addr));
215 }
216
217 p_opt = (struct dhcp6_opt_h *) &ptr_in6_addr[1]; // next option
218 }
219 }
220
221 if (ntohs(*ptrw) == D6_OPT_DOMAIN_LIST)
222 {
223 if (*config->default_ipv6_domain_list)
224 {
225 uint8_t buffer[255];
226 int len = dhcpv6_format_dns_search_name(config->default_ipv6_domain_list, buffer);
227
228 if (len > 0)
229 {
230 p_opt->code = htons(D6_OPT_DOMAIN_LIST); // D6_OPT_DOMAIN_LIST
231 p_opt->len = htons(len);
232 memcpy((char *)&p_opt[1], buffer, len);
233
234 p_opt = (struct dhcp6_opt_h *) (((uint8_t *) &p_opt[1]) + len); // next option
235 }
236 }
237 }
238 }
239 }
240
241 if (list_option.p_opt_rapidcommit && (list_option.p_mess_hdr->type == DHCP6_SOLICIT))
242 {
243 p_opt->code = htons(D6_OPT_RAPID_COMMIT); // D6_OPT_RAPID_COMMIT
244 p_opt->len = 0;
245 p_opt = &p_opt[1]; // next option
246 }
247
248 p_opt->code = htons(D6_OPT_PREFERENCE); // D6_OPT_PREFERENCE
249 p_opt->len = htons(1);
250 ((struct dhcp6_opt_preference *)p_opt)->pref = 255;
251 p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_preference *)p_opt)[1]; // next option
252
253 // calculation of lenght
254 len = ((uint8_t *) p_opt) - ((uint8_t *) p_udp);
255 p_ip6_hdr->ip6_plen = p_udp->len = htons(len);
256
257 /* Use pseudo hearder for checksum calculation */
258 memset(&pseudo_hdr, 0, sizeof(pseudo_hdr));
259 memcpy(&pseudo_hdr.src, &p_ip6_hdr->ip6_src, 16);
260 memcpy(&pseudo_hdr.dest, &p_ip6_hdr->ip6_dst, 16);
261 pseudo_hdr.ulp_length = htonl(len); // Lenght whitout Ipv6 header
262 pseudo_hdr.nexthdr = IPPROTO_UDP;
263 // Checksum is over the udp payload plus the pseudo header
264 p_udp->check = ipv6_checksum(&pseudo_hdr, (uint8_t *) p_udp, len);
265
266 // Add ipv6 header to length
267 len += sizeof(*p_ip6_hdr);
268 LOG(3, s, t, "Send DHCPv6 message %s\n", (p_mess_hdr->type == DHCP6_REPLY) ? "REPLY" : "ADVERTISE");
269 tunnelsend(b, len + (((uint8_t *) p_ip6_hdr)-b), t); // send it...
270 }
271
272 static char * get_msg_type(uint8_t type)
273 {
274 switch(type)
275 {
276 case DHCP6_SOLICIT:
277 {
278 return "Solicit";
279 }
280 break;
281
282 case DHCP6_REQUEST:
283 return "Request";
284 break;
285
286 case DHCP6_RENEW:
287 return "Renew";
288 break;
289
290 case DHCP6_INFORMATION_REQUEST:
291 return "Information Request";
292 break;
293
294 case DHCP6_REBIND:
295 return "Rebind";
296 break;
297
298 case DHCP6_RELEASE:
299 return "Release";
300 break;
301
302 case DHCP6_DECLINE:
303 return "Decline";
304 break;
305
306 default:
307 return "Unknown";
308 break;
309 }
310 }
311
312 void dhcpv6_process(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
313 {
314 struct ip6_hdr *p_ip6_hdr_in;
315 struct dhcp6_mess_hdr *p_mess_hdr;
316 struct dhcp6_opt_h *p_opt;
317 uint8_t *p_end;
318 uint16_t len;
319
320 CSTAT(dhcpv6_process);
321
322 p_ip6_hdr_in = (struct ip6_hdr *) p;
323 p_mess_hdr = (struct dhcp6_mess_hdr *) (p + 48);
324
325 LOG(3, s, t, "Got DHCPv6 message Type: %s(%d)\n", get_msg_type(p_mess_hdr->type), p_mess_hdr->type);
326
327 if (!session[s].route6[0].ipv6route.s6_addr[0] || !session[s].route6[0].ipv6prefixlen)
328 return;
329
330 p_opt = (struct dhcp6_opt_h *) &p_mess_hdr[1];
331 p_end = ((uint8_t *)p_ip6_hdr_in) + ntohs(p_ip6_hdr_in->ip6_plen) + sizeof(*p_ip6_hdr_in);
332 memset(&list_option, 0, sizeof(list_option));
333 list_option.p_mess_hdr = p_mess_hdr;
334 while (((uint8_t *)p_opt) < p_end)
335 {
336 switch(ntohs(p_opt->code))
337 {
338 case D6_OPT_CLIENTID:
339 list_option.p_opt_clientid = p_opt;
340 LOG(3, s, t, "......Option D6_OPT_CLIENTID\n");
341 break;
342 case D6_OPT_SERVERID:
343 list_option.p_opt_serverid = p_opt;
344 LOG(3, s, t, "......Option D6_OPT_SERVERID\n");
345 break;
346 case D6_OPT_RAPID_COMMIT:
347 list_option.p_opt_rapidcommit = p_opt;
348 LOG(3, s, t, "......Option D6_OPT_RAPID_COMMIT\n");
349 break;
350 case D6_OPT_IA_NA:
351 list_option.p_opt_ia_na = p_opt;
352 LOG(3, s, t, "......Option D6_OPT_IA_NA\n");
353 break;
354 case D6_OPT_IA_TA:
355 list_option.p_opt_ia_ta = p_opt;
356 LOG(3, s, t, "......Option D6_OPT_IA_TA\n");
357 break;
358 case D6_OPT_ORO:
359 list_option.p_opt_oro = p_opt;
360 LOG(3, s, t, "......Option D6_OPT_ORO\n");
361 break;
362 case D6_OPT_IA_PD:
363 list_option.p_opt_ia_pd = p_opt;
364 LOG(3, s, t, "......Option D6_OPT_IA_PD\n");
365 break;
366 case D6_OPT_ELAPSED_TIME:
367 LOG(3, s, t, "......Option D6_OPT_ELAPSED_TIME\n");
368 break;
369
370 default:
371 LOG(3, s, t, "......DHCPv6 option: %d\n", ntohs(p_opt->code));
372 break;
373 }
374 p_opt = (struct dhcp6_opt_h *)(((uint8_t *) p_opt) + ntohs(p_opt->len) + sizeof(*p_opt));
375 }
376
377 switch(p_mess_hdr->type)
378 {
379 case DHCP6_SOLICIT:
380 {
381 if (!list_option.p_opt_clientid)
382 {
383 LOG(3, s, t, "DHCPv6: error no Client-ID\n");
384 return;
385 }
386 else if (list_option.p_opt_rapidcommit)
387 {
388 if (!session[s].dhcpv6_client_id.opt_hdr.len)
389 {
390 len = ntohs(list_option.p_opt_clientid->len);
391
392 if ((len > 0) && (len <= sizeof(struct dhcp6_duid)))
393 {
394 memcpy(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + len);
395 }
396 else
397 {
398 LOG(3, s, t, "DHCPv6: Error malformed Client-ID option\n");
399 return;
400 }
401 }
402 else if (session[s].dhcpv6_client_id.opt_hdr.len != list_option.p_opt_clientid->len ||
403 memcmp(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_clientid->len)))
404 {
405 LOG(3, s, t, "DHCPv6: Error unmatched Client-ID option\n");
406 return;
407 }
408 }
409
410 if (list_option.p_opt_serverid)
411 {
412 LOG(3, s, t, "DHCPv6: Error unexpected Server-ID option on Solicit\n");
413 return;
414 }
415
416 dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src);
417 }
418 break;
419
420 case DHCP6_REQUEST:
421 {
422 if (!list_option.p_opt_clientid)
423 {
424 LOG(3, s, t, "DHCPv6: error no Client-ID\n");
425 return;
426 }
427
428 if (!list_option.p_opt_serverid)
429 {
430 LOG(3, s, t, "DHCPv6: error no Server-ID\n");
431 return;
432 }
433 else if (dhcp6_local_serverid.opt_hdr.len != list_option.p_opt_serverid->len ||
434 memcmp(&dhcp6_local_serverid, list_option.p_opt_serverid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_serverid->len)))
435 {
436 LOG(3, s, t, "DHCPv6: Error unmatched Server-ID option\n");
437 return;
438 }
439
440 if (!session[s].dhcpv6_client_id.opt_hdr.len)
441 {
442 len = ntohs(list_option.p_opt_clientid->len);
443
444 if ((len > 0) && (len <= sizeof(struct dhcp6_duid)))
445 {
446 memcpy(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + len);
447 }
448 else
449 {
450 LOG(3, s, t, "DHCPv6: Error malformed Client-ID option\n");
451 return;
452 }
453 }
454 else if ( session[s].dhcpv6_client_id.opt_hdr.len != list_option.p_opt_clientid->len ||
455 memcmp(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_clientid->len)))
456 {
457 LOG(3, s, t, "DHCPv6: Error unmatched Client-ID option\n");
458 return;
459 }
460
461 dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src);
462 send_ipv6_ra(s, t, &p_ip6_hdr_in->ip6_src); // send a RA
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 }