X-Git-Url: https://git.decadent.org.uk/gitweb/?p=odhcp6c.git;a=blobdiff_plain;f=src%2Fdhcpv6.c;h=4b1ba957b21fcbc3d18c0030a0bc2acb915ec07b;hp=973ca8f03502a8830158316a063ae73ff221683f;hb=c291def1cadf58c0aa10c18a53c2fc4d2dee1ad6;hpb=b146f9adc80cc2c2cdf2b04bfeec4c861a2a0e23 diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 973ca8f..4b1ba95 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2012-2013 Steven Barth + * Copyright (C) 2012-2014 Steven Barth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License v2 as published by @@ -105,8 +105,13 @@ static bool accept_reconfig = false; // Reconfigure key static uint8_t reconf_key[16]; -int init_dhcpv6(const char *ifname, bool strict_options, int sol_timeout) +// client options +static unsigned int client_options = 0; + + +int init_dhcpv6(const char *ifname, unsigned int options, int sol_timeout) { + client_options = options; dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = sol_timeout; sock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP); @@ -156,7 +161,7 @@ int init_dhcpv6(const char *ifname, bool strict_options, int sol_timeout) } // Create ORO - if (!strict_options) { + if (!(client_options & DHCPV6_STRICT_OPTIONS)) { uint16_t oro[] = { htons(DHCPV6_OPT_SIP_SERVER_D), htons(DHCPV6_OPT_SIP_SERVER_A), @@ -363,6 +368,21 @@ static void dhcpv6_send(enum dhcpv6_msg type, uint8_t trid[3], uint32_t ecs) // Request Information Refresh uint16_t oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH); + // Build vendor-class option + size_t vendor_class_len, user_class_len; + struct dhcpv6_vendorclass *vendor_class = odhcp6c_get_state(STATE_VENDORCLASS, &vendor_class_len); + void *user_class = odhcp6c_get_state(STATE_USERCLASS, &user_class_len); + + struct { + uint16_t type; + uint16_t length; + } vendor_class_hdr = {htons(DHCPV6_OPT_VENDOR_CLASS), htons(vendor_class_len)}; + + struct { + uint16_t type; + uint16_t length; + } user_class_hdr = {htons(DHCPV6_OPT_USER_CLASS), htons(user_class_len)}; + // Prepare Header size_t oro_len; void *oro = odhcp6c_get_state(STATE_ORO, &oro_len); @@ -387,6 +407,10 @@ static void dhcpv6_send(enum dhcpv6_msg type, uint8_t trid[3], uint32_t ecs) {&oro_refresh, 0}, {cl_id, cl_id_len}, {srv_id, srv_id_len}, + {&vendor_class_hdr, vendor_class_len ? sizeof(vendor_class_hdr) : 0}, + {vendor_class, vendor_class_len}, + {&user_class_hdr, user_class_len ? sizeof(user_class_hdr) : 0}, + {user_class, user_class_len}, {&reconf_accept, sizeof(reconf_accept)}, {&fqdn, fqdn_len}, {&hdr_ia_na, sizeof(hdr_ia_na)}, @@ -396,23 +420,29 @@ static void dhcpv6_send(enum dhcpv6_msg type, uint8_t trid[3], uint32_t ecs) size_t cnt = ARRAY_SIZE(iov); if (type == DHCPV6_MSG_INFO_REQ) { - cnt = 5; + cnt = 9; iov[2].iov_len = sizeof(oro_refresh); hdr.oro_len = htons(oro_len + sizeof(oro_refresh)); } else if (!request_prefix) { - cnt = 9; + cnt = 13; } // Disable IAs if not used if (type != DHCPV6_MSG_SOLICIT) { - iov[5].iov_len = 0; + iov[7].iov_len = 0; if (ia_na_len == 0) - iov[7].iov_len = 0; + iov[9].iov_len = 0; } if (na_mode == IA_MODE_NONE) + iov[9].iov_len = 0; + + if (!(client_options & DHCPV6_ACCEPT_RECONFIGURE)) iov[7].iov_len = 0; + if (!(client_options & DHCPV6_CLIENT_FQDN)) + iov[8].iov_len = 0; + struct sockaddr_in6 srv = {AF_INET6, htons(DHCPV6_SERVER_PORT), 0, ALL_DHCPV6_RELAYS, ifindex}; struct msghdr msg = {&srv, sizeof(srv), iov, cnt, NULL, 0, 0}; @@ -444,9 +474,9 @@ int dhcpv6_request(enum dhcpv6_msg type) if (type == DHCPV6_MSG_UNKNOWN) timeout = t1; else if (type == DHCPV6_MSG_RENEW) - timeout = (t2 > t1) ? t2 - t1 : 0; + timeout = (t2 > t1) ? t2 - t1 : ((t1 == UINT32_MAX) ? UINT32_MAX : 0); else if (type == DHCPV6_MSG_REBIND) - timeout = (t3 > t2) ? t3 - t2 : 0; + timeout = (t3 > t2) ? t3 - t2 : ((t2 == UINT32_MAX) ? UINT32_MAX : 0); if (timeout == 0) return -1; @@ -484,8 +514,8 @@ int dhcpv6_request(enum dhcpv6_msg type) uint64_t round_end = round_start + rto; elapsed = round_start - start; - // Don't wait too long - if (round_end - start > timeout * 1000) + // Don't wait too long if timeout differs from infinite + if ((timeout != UINT32_MAX) && (round_end - start > timeout * 1000)) round_end = timeout * 1000 + start; // Built and send package @@ -512,9 +542,9 @@ int dhcpv6_request(enum dhcpv6_msg type) // Set timeout for receiving uint64_t t = round_end - round_start; - struct timeval timeout = {t / 1000, (t % 1000) * 1000}; + struct timeval tv = {t / 1000, (t % 1000) * 1000}; setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, - &timeout, sizeof(timeout)); + &tv, sizeof(tv)); // Receive cycle len = recvmsg(sock, &msg, 0); @@ -559,8 +589,8 @@ int dhcpv6_request(enum dhcpv6_msg type) // Allow if (retx->handler_finish) len = retx->handler_finish(); - } while (len < 0 && ((elapsed / 1000 < timeout) && (!retx->max_rc || rc < retx->max_rc))); - + } while (len < 0 && ((timeout == UINT32_MAX) || (elapsed / 1000 < timeout)) && + (!retx->max_rc || rc < retx->max_rc)); return len; } @@ -831,9 +861,14 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc, uint32_t elapsed = (last_update > 0) ? now - last_update : 0; last_update = now; - t1 -= elapsed; - t2 -= elapsed; - t3 -= elapsed; + if (t1 != UINT32_MAX) + t1 -= elapsed; + + if (t2 != UINT32_MAX) + t2 -= elapsed; + + if (t3 != UINT32_MAX) + t3 -= elapsed; if (t1 < 0) t1 = 0;