X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/786a8c4546c4a28f21d8995567e6f8f28f2eb053..b3f40f41f7de7a383c6f1170e546d0b09cc57338:/dhcp6.c diff --git a/dhcp6.c b/dhcp6.c new file mode 100644 index 0000000..e422df5 --- /dev/null +++ b/dhcp6.c @@ -0,0 +1,544 @@ +/* + * Fernando ALVES 2014 + * Add functionality DHCPv6 to l2tpns. + * GPL licenced + */ + +#include +#include +#include + +#include "dhcp6.h" +#include "l2tpns.h" +#include "ipv6_u.h" + +struct dhcp6_in_option +{ + struct dhcp6_mess_hdr *p_mess_hdr; + struct dhcp6_opt_h *p_opt_clientid; + struct dhcp6_opt_h *p_opt_serverid; + struct dhcp6_opt_h *p_opt_ia_na; + struct dhcp6_opt_h *p_opt_ia_ta; + struct dhcp6_opt_h *p_opt_ia_pd; + struct dhcp6_opt_h *p_opt_oro; + struct dhcp6_opt_h *p_opt_rapidcommit; +}; + +static struct dhcp6_opt_serverid dhcp6_local_serverid; +static struct dhcp6_in_option list_option; + +static int dhcpv6_format_dns_search_name(const char *strdns, uint8_t *buffer); + +static void dhcp6_send_reply(sessionidt s, tunnelidt t, struct in6_addr *ip6_src) +{ + struct ip6_hdr *p_ip6_hdr; + struct udphdr *p_udp; + struct dhcp6_mess_hdr *p_mess_hdr; + struct dhcp6_opt_h *p_opt; + struct ipv6_pseudo_hdr pseudo_hdr; + uint8_t b[MAXETHER + 20]; + int len; + + memset(b, 0, sizeof(b)); + p_ip6_hdr = (struct ip6_hdr *) makeppp(b, sizeof(b), 0, 0, s, t, PPPIPV6, 0, 0, 0); + + // IPv6 Header + p_ip6_hdr->ip6_vfc = 0x60; // IPv6 + p_ip6_hdr->ip6_plen = 0; // Length of payload (not header) (calculation below) + p_ip6_hdr->ip6_nxt = IPPROTO_UDP; // icmp6 is next + p_ip6_hdr->ip6_hlim = 1; // Hop limit + // IPv6 Src FE02::1:2 + inet_pton(AF_INET6, "FE02::1:2", &p_ip6_hdr->ip6_src.s6_addr); + // IPv6 Dest + memcpy(&p_ip6_hdr->ip6_dst.s6_addr, ip6_src, sizeof(p_ip6_hdr->ip6_dst.s6_addr)); + + // UDP Header + p_udp = (struct udphdr *) &p_ip6_hdr[1]; + p_udp->source = htons(547); + p_udp->dest = htons(546); + p_udp->len = 0; // Length udp size_udp_header + data (calculation below) + p_udp->check = 0; // checksum (calculation below with ip pseudo header) + + // DHCPv6 msg header + p_mess_hdr = (struct dhcp6_mess_hdr *) &p_udp[1]; + if (list_option.p_mess_hdr->type == DHCP6_SOLICIT) + p_mess_hdr->type = list_option.p_opt_rapidcommit ? DHCP6_REPLY : DHCP6_ADVERTISE; + else + p_mess_hdr->type = DHCP6_REPLY; + + p_mess_hdr->trans_id = list_option.p_mess_hdr->trans_id; + + // DHCPv6 options header + p_opt = (struct dhcp6_opt_h *) &p_mess_hdr[1]; + memcpy(p_opt, &dhcp6_local_serverid, ntohs(dhcp6_local_serverid.opt_hdr.len) + sizeof(dhcp6_local_serverid.opt_hdr)); // ServerID + p_opt = (struct dhcp6_opt_h *) (((uint8_t *) p_opt) + ntohs(p_opt->len) + sizeof(*p_opt)); // next option + + if (list_option.p_opt_clientid) + { + memcpy(p_opt, list_option.p_opt_clientid, ntohs(list_option.p_opt_clientid->len) + sizeof(*p_opt)); // ClientID + p_opt = (struct dhcp6_opt_h *) (((uint8_t *) p_opt) + ntohs(p_opt->len) + sizeof(*p_opt)); // next option + } + + if (list_option.p_opt_ia_pd && (list_option.p_mess_hdr->type != DHCP6_INFORMATION_REQUEST)) + { + p_opt->code = htons(D6_OPT_IA_PD); // D6_OPT_IA_PD + ((struct dhcp6_opt_ia_pd *)p_opt)->iaid = ((struct dhcp6_opt_ia_pd *)list_option.p_opt_ia_pd)->iaid; + ((struct dhcp6_opt_ia_pd *)p_opt)->T1 = (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime/2) : 0xFFFFFFFF; + ((struct dhcp6_opt_ia_pd *)p_opt)->T2 = (config->dhcp6_preferred_lifetime > 0) ? htonl((config->dhcp6_preferred_lifetime*4)/5) : 0xFFFFFFFF; + + 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) + { + p_opt->len = htons(sizeof(struct dhcp6_opt_ia_pd) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_status)); + p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_pd *)p_opt)[1]; + + ((struct dhcp6_opt_status *)p_opt)->hdr.code = htons(D6_OPT_STATUS_CODE); + ((struct dhcp6_opt_status *)p_opt)->hdr.len = htons(2); + ((struct dhcp6_opt_status *)p_opt)->code = htons(D6_STATUS_NoBinding); + p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_status *)p_opt)[1]; // next option + } + else + { + if (list_option.p_mess_hdr->type == DHCP6_REQUEST || list_option.p_opt_rapidcommit) + { + session[s].dhcpv6_prefix_iaid = ((struct dhcp6_opt_ia_pd *)list_option.p_opt_ia_pd)->iaid; + } + + p_opt->len = htons(sizeof(struct dhcp6_opt_ia_pd) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_ia_prefix)); + p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_pd *)p_opt)[1]; + + ((struct dhcp6_opt_ia_prefix *)p_opt)->hdr.code = htons(D6_OPT_IAPREFIX); + ((struct dhcp6_opt_ia_prefix *)p_opt)->hdr.len = htons(sizeof(struct dhcp6_opt_ia_prefix) - sizeof(*p_opt)); + ((struct dhcp6_opt_ia_prefix *)p_opt)->pref_lifetime= (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime) : 0xFFFFFFFF; + ((struct dhcp6_opt_ia_prefix *)p_opt)->valid_lifetime= (config->dhcp6_valid_lifetime > 0) ? htonl(config->dhcp6_valid_lifetime) : 0xFFFFFFFF; + ((struct dhcp6_opt_ia_prefix *)p_opt)->prefix_len = session[s].ipv6prefixlen; + ((struct dhcp6_opt_ia_prefix *)p_opt)->prefix = session[s].ipv6route; + p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_prefix *)p_opt)[1]; // next option + } + } + + if (list_option.p_opt_ia_na && (list_option.p_mess_hdr->type != DHCP6_INFORMATION_REQUEST)) + { + p_opt->code = htons(D6_OPT_IA_NA); // D6_OPT_IA_NA + ((struct dhcp6_opt_ia_na *)p_opt)->iaid = ((struct dhcp6_opt_ia_na *)list_option.p_opt_ia_na)->iaid; + ((struct dhcp6_opt_ia_na *)p_opt)->T1 = (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime/2) : 0xFFFFFFFF; + ((struct dhcp6_opt_ia_na *)p_opt)->T2 = (config->dhcp6_preferred_lifetime > 0) ? htonl((config->dhcp6_preferred_lifetime*4)/5) : 0xFFFFFFFF; + + 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) + { + p_opt->len = htons(sizeof(struct dhcp6_opt_ia_na) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_status)); + p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_na *)p_opt)[1]; + + ((struct dhcp6_opt_status *)p_opt)->hdr.code = htons(D6_OPT_STATUS_CODE); + ((struct dhcp6_opt_status *)p_opt)->hdr.len = htons(2); + ((struct dhcp6_opt_status *)p_opt)->code = htons(D6_STATUS_NoBinding); + p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_status *)p_opt)[1]; // next option + } + else + { + in_addr_t addr_ipv4; + + if (list_option.p_mess_hdr->type == DHCP6_REQUEST || list_option.p_opt_rapidcommit) + { + session[s].dhcpv6_iana_iaid = ((struct dhcp6_opt_ia_na *)list_option.p_opt_ia_na)->iaid; + } + + p_opt->len = htons(sizeof(struct dhcp6_opt_ia_na) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_ia_addr)); + p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_na *)p_opt)[1]; + + ((struct dhcp6_opt_ia_addr *)p_opt)->hdr.code = htons(D6_OPT_IAADDR); + ((struct dhcp6_opt_ia_addr *)p_opt)->hdr.len = htons(sizeof(struct dhcp6_opt_ia_addr) - sizeof(*p_opt)); + + if (session[s].ipv6address.s6_addr[0]) + { + memcpy(&((struct dhcp6_opt_ia_addr *)p_opt)->addr, &session[s].ipv6address, 16); // copy ipv6 prefix + } + else + { + memcpy(&((struct dhcp6_opt_ia_addr *)p_opt)->addr, &config->ipv6_prefix, 8); // copy prefix 64 + addr_ipv4 = htonl(session[s].ip); + memcpy(&((struct dhcp6_opt_ia_addr *)p_opt)->addr.s6_addr[8], &addr_ipv4, 4); // copy ipv4 + } + + ((struct dhcp6_opt_ia_addr *)p_opt)->pref_lifetime= (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime) : 0xFFFFFFFF; + ((struct dhcp6_opt_ia_addr *)p_opt)->valid_lifetime= (config->dhcp6_valid_lifetime > 0) ? htonl(config->dhcp6_valid_lifetime) : 0xFFFFFFFF; + + p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_addr *)p_opt)[1]; // next option + } + } + + if (list_option.p_opt_ia_ta && (list_option.p_mess_hdr->type != DHCP6_INFORMATION_REQUEST)) + { + p_opt->code = htons(D6_OPT_IA_TA); // D6_OPT_IA_TA + p_opt->len = htons(sizeof(struct dhcp6_opt_ia_ta) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_status)); + ((struct dhcp6_opt_ia_ta *)p_opt)->iaid = ((struct dhcp6_opt_ia_ta *)list_option.p_opt_ia_ta)->iaid; + p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_ta *)p_opt)[1]; + ((struct dhcp6_opt_status *)p_opt)->hdr.code = htons(D6_OPT_STATUS_CODE); + ((struct dhcp6_opt_status *)p_opt)->hdr.len = htons(2); + ((struct dhcp6_opt_status *)p_opt)->code = htons(D6_STATUS_UnspecFail); + p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_status *)p_opt)[1]; // next option + } + + if (list_option.p_opt_oro) + { + int countopt; + uint16_t *ptrw; + struct in6_addr *ptr_in6_addr; + + 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++) + { + if (ntohs(*ptrw) == D6_OPT_DNS_SERVERS) + { + if (config->default_ipv6_dns1.s6_addr[0]) + { + p_opt->code = htons(D6_OPT_DNS_SERVERS); // D6_OPT_DNS_SERVERS + p_opt->len = htons(sizeof(*ptr_in6_addr)); + ptr_in6_addr = (struct in6_addr *) &p_opt[1]; + memcpy(ptr_in6_addr, &config->default_ipv6_dns1, sizeof(*ptr_in6_addr)); + + if (config->default_ipv6_dns2.s6_addr[0]) + { + p_opt->len = htons(2*sizeof(*ptr_in6_addr)); + ptr_in6_addr = &ptr_in6_addr[1]; + memcpy(ptr_in6_addr, &config->default_ipv6_dns2, sizeof(*ptr_in6_addr)); + } + + p_opt = (struct dhcp6_opt_h *) &ptr_in6_addr[1]; // next option + } + } + + if (ntohs(*ptrw) == D6_OPT_DOMAIN_LIST) + { + if (*config->default_ipv6_domain_list) + { + uint8_t buffer[255]; + int len = dhcpv6_format_dns_search_name(config->default_ipv6_domain_list, buffer); + + if (len > 0) + { + p_opt->code = htons(D6_OPT_DOMAIN_LIST); // D6_OPT_DOMAIN_LIST + p_opt->len = htons(len); + memcpy((char *)&p_opt[1], buffer, len); + + p_opt = (struct dhcp6_opt_h *) (((uint8_t *) &p_opt[1]) + len); // next option + } + } + } + } + } + + if (list_option.p_opt_rapidcommit && (list_option.p_mess_hdr->type == DHCP6_SOLICIT)) + { + p_opt->code = htons(D6_OPT_RAPID_COMMIT); // D6_OPT_RAPID_COMMIT + p_opt->len = 0; + p_opt = &p_opt[1]; // next option + } + + p_opt->code = htons(D6_OPT_PREFERENCE); // D6_OPT_PREFERENCE + p_opt->len = htons(1); + ((struct dhcp6_opt_preference *)p_opt)->pref = 255; + p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_preference *)p_opt)[1]; // next option + + // calculation of lenght + len = ((uint8_t *) p_opt) - ((uint8_t *) p_udp); + p_ip6_hdr->ip6_plen = p_udp->len = htons(len); + + /* Use pseudo hearder for checksum calculation */ + memset(&pseudo_hdr, 0, sizeof(pseudo_hdr)); + memcpy(&pseudo_hdr.src, &p_ip6_hdr->ip6_src, 16); + memcpy(&pseudo_hdr.dest, &p_ip6_hdr->ip6_dst, 16); + pseudo_hdr.ulp_length = htonl(len); // Lenght whitout Ipv6 header + pseudo_hdr.nexthdr = IPPROTO_UDP; + // Checksum is over the udp payload plus the pseudo header + p_udp->check = ipv6_checksum(&pseudo_hdr, (uint8_t *) p_udp, len); + + // Add ipv6 header to length + len += sizeof(*p_ip6_hdr); + LOG(3, s, t, "Send DHCPv6 message %s\n", (p_mess_hdr->type == DHCP6_REPLY) ? "REPLY" : "ADVERTISE"); + tunnelsend(b, len + (((uint8_t *) p_ip6_hdr)-b), t); // send it... +} + +void dhcpv6_process(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) +{ + struct ip6_hdr *p_ip6_hdr_in; + struct dhcp6_mess_hdr *p_mess_hdr; + struct dhcp6_opt_h *p_opt; + uint8_t *p_end; + uint16_t len; + + CSTAT(dhcpv6_process); + + p_ip6_hdr_in = (struct ip6_hdr *) p; + p_mess_hdr = (struct dhcp6_mess_hdr *) (p + 48); + + LOG(3, s, t, "Got DHCPv6 message Type: %d\n", p_mess_hdr->type); + + if (!session[s].ipv6route.s6_addr[0] || !session[s].ipv6prefixlen) + return; + + p_opt = (struct dhcp6_opt_h *) &p_mess_hdr[1]; + p_end = ((uint8_t *)p_ip6_hdr_in) + ntohs(p_ip6_hdr_in->ip6_plen) + sizeof(*p_ip6_hdr_in); + memset(&list_option, 0, sizeof(list_option)); + list_option.p_mess_hdr = p_mess_hdr; + while (((uint8_t *)p_opt) < p_end) + { + switch(ntohs(p_opt->code)) + { + case D6_OPT_CLIENTID: + list_option.p_opt_clientid = p_opt; + LOG(3, s, t, "......Option D6_OPT_CLIENTID\n"); + break; + case D6_OPT_SERVERID: + list_option.p_opt_serverid = p_opt; + LOG(3, s, t, "......Option D6_OPT_SERVERID\n"); + break; + case D6_OPT_RAPID_COMMIT: + list_option.p_opt_rapidcommit = p_opt; + LOG(3, s, t, "......Option D6_OPT_RAPID_COMMIT\n"); + break; + case D6_OPT_IA_NA: + list_option.p_opt_ia_na = p_opt; + LOG(3, s, t, "......Option D6_OPT_IA_NA\n"); + break; + case D6_OPT_IA_TA: + list_option.p_opt_ia_ta = p_opt; + LOG(3, s, t, "......Option D6_OPT_IA_TA\n"); + break; + case D6_OPT_ORO: + list_option.p_opt_oro = p_opt; + LOG(3, s, t, "......Option D6_OPT_ORO\n"); + break; + case D6_OPT_IA_PD: + list_option.p_opt_ia_pd = p_opt; + LOG(3, s, t, "......Option D6_OPT_IA_PD\n"); + break; + case D6_OPT_ELAPSED_TIME: + LOG(3, s, t, "......Option D6_OPT_ELAPSED_TIME\n"); + break; + + default: + LOG(3, s, t, "......DHCPv6 option: %d\n", ntohs(p_opt->code)); + break; + } + p_opt = (struct dhcp6_opt_h *)(((uint8_t *) p_opt) + ntohs(p_opt->len) + sizeof(*p_opt)); + } + + switch(p_mess_hdr->type) + { + case DHCP6_SOLICIT: + { + LOG(3, s, t, "..(Type %d = Solicit)\n", p_mess_hdr->type); + + if (!list_option.p_opt_clientid) + { + LOG(3, s, t, "DHCPv6: error no Client-ID\n"); + return; + } + else if (list_option.p_opt_rapidcommit) + { + if (!session[s].dhcpv6_client_id.opt_hdr.len) + { + len = ntohs(list_option.p_opt_clientid->len); + + if ((len > 0) && (len <= sizeof(struct dhcp6_duid))) + { + memcpy(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + len); + } + else + { + LOG(3, s, t, "DHCPv6: Error malformed Client-ID option\n"); + return; + } + } + else if (session[s].dhcpv6_client_id.opt_hdr.len != list_option.p_opt_clientid->len || + memcmp(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_clientid->len))) + { + LOG(3, s, t, "DHCPv6: Error unmatched Client-ID option\n"); + return; + } + } + + if (list_option.p_opt_serverid) + { + LOG(3, s, t, "DHCPv6: Error unexpected Server-ID option on Solicit\n"); + return; + } + + dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src); + } + break; + + case DHCP6_REQUEST: + { + LOG(3, s, t, "..(Type %d = Request)\n", p_mess_hdr->type); + + if (!list_option.p_opt_clientid) + { + LOG(3, s, t, "DHCPv6: error no Client-ID\n"); + return; + } + + if (!list_option.p_opt_serverid) + { + LOG(3, s, t, "DHCPv6: error no Server-ID\n"); + return; + } + else if (dhcp6_local_serverid.opt_hdr.len != list_option.p_opt_serverid->len || + memcmp(&dhcp6_local_serverid, list_option.p_opt_serverid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_serverid->len))) + { + LOG(3, s, t, "DHCPv6: Error unmatched Server-ID option\n"); + return; + } + + if (!session[s].dhcpv6_client_id.opt_hdr.len) + { + len = ntohs(list_option.p_opt_clientid->len); + + if ((len > 0) && (len <= sizeof(struct dhcp6_duid))) + { + memcpy(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + len); + } + else + { + LOG(3, s, t, "DHCPv6: Error malformed Client-ID option\n"); + return; + } + } + else if ( session[s].dhcpv6_client_id.opt_hdr.len != list_option.p_opt_clientid->len || + memcmp(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_clientid->len))) + { + LOG(3, s, t, "DHCPv6: Error unmatched Client-ID option\n"); + return; + } + + dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src); + } + break; + + case DHCP6_RENEW: + { + LOG(3, s, t, "..(Type %d = Renew)\n", p_mess_hdr->type); + + if (!list_option.p_opt_clientid) + { + LOG(3, s, t, "DHCPv6: error no Client-ID\n"); + return; + } + else if ( session[s].dhcpv6_client_id.opt_hdr.len != list_option.p_opt_clientid->len || + memcmp(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_clientid->len))) + { + LOG(3, s, t, "DHCPv6: Error unmatched Client-ID option\n"); + return; + } + + if (!list_option.p_opt_serverid) + { + LOG(3, s, t, "DHCPv6: error no Server-ID\n"); + return; + } + else if (dhcp6_local_serverid.opt_hdr.len != list_option.p_opt_serverid->len || + memcmp(&dhcp6_local_serverid, list_option.p_opt_serverid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_serverid->len))) + { + LOG(3, s, t, "DHCPv6: Error unmatched Server-ID option\n"); + return; + } + + dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src); + } + break; + + case DHCP6_INFORMATION_REQUEST: + { + LOG(3, s, t, "..(Type %d = Information Request)\n", p_mess_hdr->type); + + if (!list_option.p_opt_clientid) + { + LOG(3, s, t, "DHCPv6: error no Client-ID\n"); + return; + } + + dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src); + } + break; + + case DHCP6_REBIND: + { + LOG(3, s, t, "..(Type %d = Rebind)\n", p_mess_hdr->type); + } + break; + + case DHCP6_RELEASE: + { + LOG(3, s, t, "..(Type %d = Release)\n", p_mess_hdr->type); + } + break; + + case DHCP6_DECLINE: + { + LOG(3, s, t, "..(Type %d = Decline)\n", p_mess_hdr->type); + } + break; + + default: + LOG(3, s, t, "Got unknown DHCPv6 Type: %d\n", *(p + 38)); + break; + } + + return; +} + +static int dhcpv6_format_dns_search_name(const char *strdns, uint8_t *buffer) +{ + int n = strlen(strdns); + const char *ptr; + + if (strdns[n - 1] == '.') n++; + else n += 2; + + if (n > 255) { + LOG(3, 0, 0, "DHCPv6: DNS search '%s' is too long\n", strdns); + return 0; + } + + while (1) + { + ptr = strchr(strdns, '.'); + + if (!ptr) ptr = strchr(strdns, 0); + + if (ptr - strdns > 63) + { + LOG(3, 0, 0, "DHCPv6: DNS search '%s' is invalid\n", strdns); + return 0; + } + + *buffer = ptr - strdns; + memcpy(buffer + 1, strdns, ptr - strdns); + buffer += 1 + (ptr - strdns); + strdns = ptr + 1; + + if (!*ptr || !*strdns) + { + *buffer = 0; + break; + } + } + + return n; +} + +void dhcpv6_init(void) +{ + uint32_t id; + + dhcp6_local_serverid.opt_hdr.code = htons(D6_OPT_SERVERID); + dhcp6_local_serverid.opt_hdr.len = htons(4 + sizeof(id)); + dhcp6_local_serverid.duid.type = htons(DUID_LL); + dhcp6_local_serverid.duid.u.ll.htype = htons(27); + + if (config->dhcp6_server_duid) + id = htobe32(config->dhcp6_server_duid); + else + id = htobe32(0xFDFDFAFA); + + memcpy(dhcp6_local_serverid.duid.u.ll.addr, &id, sizeof(id)); +}