diff --git a/src/config.c b/src/config.c index 29384482..13b8ba84 100644 --- a/src/config.c +++ b/src/config.c @@ -139,6 +139,7 @@ enum { IFACE_ATTR_NTP, IFACE_ATTR_CAPTIVE_PORTAL_URI, IFACE_ATTR_IPV6_ONLY_PREFERRED, + IFACE_ATTR_DHCPV6_RELAY_SERVERS, IFACE_ATTR_MAX }; @@ -193,6 +194,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = { [IFACE_ATTR_NTP] = { .name = "ntp", .type = BLOBMSG_TYPE_ARRAY }, [IFACE_ATTR_CAPTIVE_PORTAL_URI] = { .name = "captive_portal_uri", .type = BLOBMSG_TYPE_STRING }, [IFACE_ATTR_IPV6_ONLY_PREFERRED] = { .name = "ipv6_only_preferred", .type = BLOBMSG_TYPE_INT32 }, + [IFACE_ATTR_DHCPV6_RELAY_SERVERS] = { .name = "dhcpv6_relay_servers", .type = BLOBMSG_TYPE_ARRAY }, }; const struct uci_blob_param_list interface_attr_list = { @@ -1710,6 +1712,39 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr &iface->pio_filter_addr, &iface->pio_filter_length); + if ((c = tb[IFACE_ATTR_DHCPV6_RELAY_SERVERS])) { + struct blob_attr *cur; + unsigned rem; + + blobmsg_for_each_attr(cur, c, rem) { + struct in6_addr addr6, *tmp6; + + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, false)) + continue; + + if (inet_pton(AF_INET6, blobmsg_get_string(cur), &addr6) == 1) { + if (IN6_IS_ADDR_UNSPECIFIED(&addr6)) { + error("Invalid %s value configured for interface '%s'", + iface_attrs[IFACE_ATTR_DHCPV6_RELAY_SERVERS].name, iface->name); + continue; + } + + tmp6 = realloc(iface->dhcpv6_relay_server_addrs6, + (iface->dhcpv6_relay_server_addrs6_cnt + 1) * + sizeof(*iface->dhcpv6_relay_server_addrs6)); + + if (!tmp6) + goto err; + + iface->dhcpv6_relay_server_addrs6 = tmp6; + iface->dhcpv6_relay_server_addrs6[iface->dhcpv6_relay_server_addrs6_cnt++] = addr6; + } else { + error("Invalid %s value configured for interface '%s'", + iface_attrs[IFACE_ATTR_DHCPV6_RELAY_SERVERS].name, iface->name); + } + } + } + if (overwrite && (c = tb[IFACE_ATTR_NTP])) { struct blob_attr *cur; unsigned rem; diff --git a/src/dhcpv6.c b/src/dhcpv6.c index a211b259..483e95b2 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -28,7 +28,8 @@ #endif static void relay_client_request(struct sockaddr_in6 *source, - const void *data, size_t len, struct interface *iface); + const void *data, size_t len, struct interface *iface, + struct in6_addr *dest); static void relay_server_response(uint8_t *data, size_t len); static void handle_dhcpv6(void *addr, void *data, size_t len, @@ -806,10 +807,15 @@ static void handle_dhcpv6(void *addr, void *data, size_t len, if (iface->dhcpv6 == MODE_SERVER) { handle_client_request(addr, data, len, iface, dest_addr); } else if (iface->dhcpv6 == MODE_RELAY) { - if (iface->master) + if (iface->master) { relay_server_response(data, len); - else - relay_client_request(addr, data, len, iface); + } else if (iface->dhcpv6_relay_server_addrs6_cnt > 0) { + for (size_t i = 0; i < iface->dhcpv6_relay_server_addrs6_cnt; i++) { + relay_client_request(addr, data, len, iface, &iface->dhcpv6_relay_server_addrs6[i]); + } + } else { + relay_client_request(addr, data, len, iface, NULL); + } } } @@ -926,7 +932,8 @@ static struct odhcpd_ipaddr *relay_link_address(struct interface *iface) /* Relay client request (regular DHCPv6-relay) */ static void relay_client_request(struct sockaddr_in6 *source, - const void *data, size_t len, struct interface *iface) + const void *data, size_t len, struct interface *iface, + struct in6_addr *dest) { const struct dhcpv6_relay_header *h = data; /* Construct our forwarding envelope */ @@ -988,7 +995,11 @@ static void relay_client_request(struct sockaddr_in6 *source, memset(&s, 0, sizeof(s)); s.sin6_family = AF_INET6; s.sin6_port = htons(DHCPV6_SERVER_PORT); - inet_pton(AF_INET6, ALL_DHCPV6_SERVERS, &s.sin6_addr); + + if (dest) + s.sin6_addr = *dest; + else + inet_pton(AF_INET6, ALL_DHCPV6_SERVERS, &s.sin6_addr); avl_for_each_element(&interfaces, c, avl) { if (!c->master || c->dhcpv6 != MODE_RELAY) diff --git a/src/odhcpd.h b/src/odhcpd.h index 9298d62a..d59facf1 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -468,6 +468,8 @@ struct interface { bool dhcpv6_na; uint32_t dhcpv6_hostid_len; uint32_t dhcpv6_pd_min_len; // minimum delegated prefix length + struct in6_addr *dhcpv6_relay_server_addrs6; + size_t dhcpv6_relay_server_addrs6_cnt; char *upstream; size_t upstream_len;