+ if (dhcp_client->debug_func) {
+ gchar *hex = convert_to_hex(duid, duid_len);
+ debug(dhcp_client, "DUID(%d) %s", duid_len, hex);
+ g_free(hex);
+ }
+
+ return 0;
+}
+
+int g_dhcpv6_client_set_pd(GDHCPClient *dhcp_client, uint32_t *T1,
+ uint32_t *T2, GSList *prefixes)
+{
+ uint8_t options[1452];
+ unsigned int max_buf = sizeof(options);
+ int len, count = g_slist_length(prefixes);
+
+ if (!dhcp_client || dhcp_client->type != G_DHCP_IPV6)
+ return -EINVAL;
+
+ g_dhcp_client_set_request(dhcp_client, G_DHCPV6_IA_PD);
+
+ memset(options, 0, sizeof(options));
+
+ options[0] = dhcp_client->iaid >> 24;
+ options[1] = dhcp_client->iaid >> 16;
+ options[2] = dhcp_client->iaid >> 8;
+ options[3] = dhcp_client->iaid;
+
+ if (T1) {
+ uint32_t t = htonl(*T1);
+ memcpy(&options[4], &t, 4);
+ }
+
+ if (T2) {
+ uint32_t t = htonl(*T2);
+ memcpy(&options[8], &t, 4);
+ }
+
+ len = 12;
+
+ if (count > 0) {
+ GSList *list;
+
+ for (list = prefixes; list; list = list->next) {
+ GDHCPIAPrefix *prefix = list->data;
+ uint8_t sub_option[4+4+1+16];
+
+ if ((len + 2 + 2 + sizeof(sub_option)) >= max_buf) {
+ debug(dhcp_client,
+ "Too long dhcpv6 message "
+ "when writing IA prefix option");
+ return -EINVAL;
+ }
+
+ memset(&sub_option, 0, sizeof(sub_option));
+
+ /* preferred and validity time are left zero */
+
+ sub_option[8] = prefix->prefixlen;
+ memcpy(&sub_option[9], &prefix->prefix, 16);
+
+ copy_option(&options[len], G_DHCPV6_IA_PREFIX,
+ sizeof(sub_option), sub_option);
+ len += 2 + 2 + sizeof(sub_option);
+ }
+ }
+
+ g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_PD,
+ options, len);
+
+ return 0;
+}
+
+uint32_t g_dhcpv6_client_get_iaid(GDHCPClient *dhcp_client)
+{
+ if (!dhcp_client || dhcp_client->type != G_DHCP_IPV6)
+ return 0;
+
+ return dhcp_client->iaid;
+}
+
+void g_dhcpv6_client_set_iaid(GDHCPClient *dhcp_client, uint32_t iaid)
+{
+ if (!dhcp_client || dhcp_client->type != G_DHCP_IPV6)
+ return;
+
+ dhcp_client->iaid = iaid;
+}
+
+void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index,
+ unsigned char *iaid)
+{
+ uint8_t buf[6];
+
+ __connman_inet_get_interface_mac_address(index, buf);
+
+ memcpy(iaid, &buf[2], 4);
+ dhcp_client->iaid = iaid[0] << 24 |
+ iaid[1] << 16 | iaid[2] << 8 | iaid[3];
+}
+
+int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
+ uint32_t *T1, uint32_t *T2,
+ time_t *started,
+ time_t *expire)
+{
+ if (!dhcp_client || dhcp_client->type != G_DHCP_IPV6)
+ return -EINVAL;
+
+ if (T1)
+ *T1 = (dhcp_client->expire == 0xffffffff) ? 0xffffffff:
+ dhcp_client->T1;
+
+ if (T2)
+ *T2 = (dhcp_client->expire == 0xffffffff) ? 0xffffffff:
+ dhcp_client->T2;
+
+ if (started)
+ *started = dhcp_client->last_request;
+
+ if (expire)
+ *expire = (dhcp_client->expire == 0xffffffff) ? 0xffffffff:
+ dhcp_client->last_request + dhcp_client->expire;
+
+ return 0;
+}
+
+static uint8_t *create_iaaddr(GDHCPClient *dhcp_client, uint8_t *buf,
+ uint16_t len)
+{
+ buf[0] = 0;
+ buf[1] = G_DHCPV6_IAADDR;
+ buf[2] = 0;
+ buf[3] = len;
+ memcpy(&buf[4], &dhcp_client->ia_na, 16);
+ memset(&buf[20], 0, 4); /* preferred */
+ memset(&buf[24], 0, 4); /* valid */
+ return buf;
+}
+
+static uint8_t *append_iaaddr(GDHCPClient *dhcp_client, uint8_t *buf,
+ const char *address)
+{
+ struct in6_addr addr;
+
+ if (inet_pton(AF_INET6, address, &addr) != 1)
+ return NULL;
+
+ buf[0] = 0;
+ buf[1] = G_DHCPV6_IAADDR;
+ buf[2] = 0;
+ buf[3] = 24;
+ memcpy(&buf[4], &addr, 16);
+ memset(&buf[20], 0, 4); /* preferred */
+ memset(&buf[24], 0, 4); /* valid */
+ return &buf[28];
+}
+
+static void put_iaid(GDHCPClient *dhcp_client, int index, uint8_t *buf)
+{
+ uint32_t iaid;
+
+ iaid = g_dhcpv6_client_get_iaid(dhcp_client);
+ if (iaid == 0) {
+ g_dhcpv6_client_create_iaid(dhcp_client, index, buf);
+ return;
+ }
+
+ buf[0] = iaid >> 24;
+ buf[1] = iaid >> 16;
+ buf[2] = iaid >> 8;
+ buf[3] = iaid;
+}
+
+int g_dhcpv6_client_set_ia(GDHCPClient *dhcp_client, int index,
+ int code, uint32_t *T1, uint32_t *T2,
+ bool add_iaaddr, const char *ia_na)
+{
+ if (code == G_DHCPV6_IA_TA) {
+ uint8_t ia_options[4];
+
+ put_iaid(dhcp_client, index, ia_options);
+
+ g_dhcp_client_set_request(dhcp_client, G_DHCPV6_IA_TA);
+ g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_TA,
+ ia_options, sizeof(ia_options));
+
+ } else if (code == G_DHCPV6_IA_NA) {
+ struct in6_addr addr;
+
+ g_dhcp_client_set_request(dhcp_client, G_DHCPV6_IA_NA);
+
+ /*
+ * If caller has specified the IPv6 address it wishes to
+ * to use (ia_na != NULL and address is valid), then send
+ * the address to server.
+ * If caller did not specify the address (ia_na == NULL) and
+ * if the current address is not set, then we should not send
+ * the address sub-option.
+ */
+ if (add_iaaddr && ((!ia_na &&
+ !IN6_IS_ADDR_UNSPECIFIED(&dhcp_client->ia_na))
+ || (ia_na &&
+ inet_pton(AF_INET6, ia_na, &addr) == 1))) {
+#define IAADDR_LEN (16+4+4)
+ uint8_t ia_options[4+4+4+2+2+IAADDR_LEN];
+
+ if (ia_na)
+ memcpy(&dhcp_client->ia_na, &addr,
+ sizeof(struct in6_addr));
+
+ put_iaid(dhcp_client, index, ia_options);
+
+ if (T1) {
+ ia_options[4] = *T1 >> 24;
+ ia_options[5] = *T1 >> 16;
+ ia_options[6] = *T1 >> 8;
+ ia_options[7] = *T1;
+ } else
+ memset(&ia_options[4], 0x00, 4);
+
+ if (T2) {
+ ia_options[8] = *T2 >> 24;
+ ia_options[9] = *T2 >> 16;
+ ia_options[10] = *T2 >> 8;
+ ia_options[11] = *T2;
+ } else
+ memset(&ia_options[8], 0x00, 4);
+
+ create_iaaddr(dhcp_client, &ia_options[12],
+ IAADDR_LEN);
+
+ g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_NA,
+ ia_options, sizeof(ia_options));
+ } else {
+ uint8_t ia_options[4+4+4];
+
+ put_iaid(dhcp_client, index, ia_options);
+
+ memset(&ia_options[4], 0x00, 4); /* T1 (4 bytes) */
+ memset(&ia_options[8], 0x00, 4); /* T2 (4 bytes) */
+
+ g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_NA,
+ ia_options, sizeof(ia_options));
+ }
+
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+int g_dhcpv6_client_set_ias(GDHCPClient *dhcp_client, int index,
+ int code, uint32_t *T1, uint32_t *T2,
+ GSList *addresses)
+{
+ GSList *list;
+ uint8_t *ia_options, *pos;
+ int len, count, total_len;
+
+ count = g_slist_length(addresses);
+ if (count == 0)
+ return -EINVAL;
+
+ g_dhcp_client_set_request(dhcp_client, code);
+
+ if (code == G_DHCPV6_IA_TA)
+ len = 4; /* IAID */
+ else if (code == G_DHCPV6_IA_NA)
+ len = 4 + 4 + 4; /* IAID + T1 + T2 */
+ else
+ return -EINVAL;
+
+ total_len = len + count * (2 + 2 + 16 + 4 + 4);
+ ia_options = g_try_malloc0(total_len);
+ if (!ia_options)
+ return -ENOMEM;
+
+ put_iaid(dhcp_client, index, ia_options);
+
+ pos = &ia_options[len]; /* skip the IA_NA or IA_TA */
+
+ for (list = addresses; list; list = list->next) {
+ pos = append_iaaddr(dhcp_client, pos, list->data);
+ if (!pos)
+ break;
+ }
+
+ if (code == G_DHCPV6_IA_NA) {
+ if (T1) {
+ ia_options[4] = *T1 >> 24;
+ ia_options[5] = *T1 >> 16;
+ ia_options[6] = *T1 >> 8;
+ ia_options[7] = *T1;
+ } else
+ memset(&ia_options[4], 0x00, 4);
+
+ if (T2) {
+ ia_options[8] = *T2 >> 24;
+ ia_options[9] = *T2 >> 16;
+ ia_options[10] = *T2 >> 8;
+ ia_options[11] = *T2;
+ } else
+ memset(&ia_options[8], 0x00, 4);
+ }
+
+ g_dhcpv6_client_set_send(dhcp_client, code, ia_options, total_len);
+
+ g_free(ia_options);
+