From 9010054ea4ceee9105aa938f15b79a3a91ec5969 Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Fri, 21 May 2021 11:14:47 +0200 Subject: dhcp, ndp, dhcpv6: Support for multiple DNS servers, search list Add support for a variable amount of DNS servers, including zero, from /etc/resolv.conf, in DHCP, NDP and DHCPv6 implementations. Introduce support for domain search list for DHCP (RFC 3397), NDP (RFC 8106), and DHCPv6 (RFC 3646), also sourced from /etc/resolv.conf. Signed-off-by: Stefano Brivio --- dhcpv6.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 9 deletions(-) (limited to 'dhcpv6.c') diff --git a/dhcpv6.c b/dhcpv6.c index 6e006dd..4ce7a87 100644 --- a/dhcpv6.c +++ b/dhcpv6.c @@ -15,13 +15,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include "passt.h" #include "tap.h" @@ -43,6 +43,7 @@ struct opt_hdr { # define OPT_STATUS_CODE 13 # define STATUS_NOTONLINK 4 # define OPT_DNS_SERVERS 23 +# define OPT_DNS_SEARCH 24 #else # define OPT_CLIENTID __bswap_constant_16(1) # define OPT_SERVERID __bswap_constant_16(2) @@ -52,6 +53,7 @@ struct opt_hdr { # define OPT_STATUS_CODE __bswap_constant_16(13) # define STATUS_NOTONLINK __bswap_constant_16(4) # define OPT_DNS_SERVERS __bswap_constant_16(23) +# define OPT_DNS_SEARCH __bswap_constant_16(24) #endif #define STR_NOTONLINK "Prefix not appropriate for link." @@ -157,11 +159,21 @@ struct opt_status_code { /** * struct opt_dns_servers - DNS Recursive Name Server option (RFC 3646) * @hdr: Option header - * @addr: IPv6 DNS address + * @addr: IPv6 DNS addresses */ struct opt_dns_servers { struct opt_hdr hdr; - struct in6_addr addr; + struct in6_addr addr[MAXNS]; +}; + +/** + * struct opt_dns_servers - Domain Search List option (RFC 3646) + * @hdr: Option header + * @list: NULL-separated list of domain names + */ +struct opt_dns_search { + struct opt_hdr hdr; + char list[MAXDNSRCH * NS_MAXDNAME]; }; /** @@ -200,8 +212,9 @@ static const struct udphdr uh_resp = { * @server_id: Server Identifier option * @ia_na: Non-temporary Address option * @ia_addr: Address for IA_NA - * @dns_servers: DNS Recursive Name Server option - * @client_id: Client Identifier, variable length, must be at the end + * @client_id: Client Identifier, variable length + * @dns_servers: DNS Recursive Name Server, here just for storage size + * @dns_search: Domain Search List, here just for storage size */ static struct resp_t { struct udphdr uh; @@ -211,6 +224,8 @@ static struct resp_t { struct opt_ia_na ia_na; struct opt_ia_addr ia_addr; struct opt_client_id client_id; + struct opt_dns_servers dns_servers; + struct opt_dns_search dns_search; } __attribute__((__packed__)) resp = { uh_resp, { 0 }, @@ -226,10 +241,17 @@ static struct resp_t { IN6ADDR_ANY_INIT, (uint32_t)~0U, (uint32_t)~0U }, - { { OPT_CLIENTID, 0, }, { 0 } }, + + { { OPT_DNS_SERVERS, 0, }, + { IN6ADDR_ANY_INIT } + }, + + { { OPT_DNS_SEARCH, 0, }, + { 0 }, + }, }; static const struct opt_status_code sc_not_on_link = { @@ -352,6 +374,67 @@ ia_ta: return NULL; } +/** + * dhcpv6_dns_fill() - Fill in DNS Servers and Domain Search list options + * @c: Execution context + * @buf: Response message buffer where options will be appended + * @offset: Offset in message buffer for new options + * + * Return: updated length of response message buffer. + */ +static size_t dhcpv6_dns_fill(struct ctx *c, char *buf, int offset) +{ + struct opt_dns_servers *srv = NULL; + struct opt_dns_search *srch = NULL; + int i; + + for (i = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->dns6[i]); i++) { + if (!i) { + srv = (struct opt_dns_servers *)(buf + offset); + offset += sizeof(struct opt_hdr); + srv->hdr.t = OPT_DNS_SERVERS; + srv->hdr.l = 0; + } + + memcpy(&srv->addr[i], &c->dns6[i], sizeof(srv->addr[i])); + srv->hdr.l += sizeof(srv->addr[i]); + offset += sizeof(srv->addr[i]); + } + + if (srv) + srv->hdr.l = htons(srv->hdr.l); + + for (i = 0; *c->dns_search[i].n; i++) { + char *p; + + if (!i) { + srch = (struct opt_dns_search *)(buf + offset); + offset += sizeof(struct opt_hdr); + srch->hdr.t = OPT_DNS_SEARCH; + srch->hdr.l = 0; + p = srch->list; + *p = 0; + } + + p = stpcpy(p + 1, c->dns_search[i].n); + *(p++) = 0; + srch->hdr.l += strlen(c->dns_search[i].n) + 2; + offset += strlen(c->dns_search[i].n) + 2; + } + + if (srch) { + for (i = 0; i < srch->hdr.l; i++) { + if (srch->list[i] == '.' || !srch->list[i]) { + srch->list[i] = strcspn(srch->list + i + 1, + "."); + } + } + srch->hdr.l = htons(srch->hdr.l); + } + + return offset; +} + /** * dhcpv6() - Check if this is a DHCPv6 message, reply as needed * @c: Execution context @@ -478,10 +561,14 @@ int dhcpv6(struct ctx *c, struct ethhdr *eh, size_t len) memcpy(&resp.client_id, client_id, ntohs(client_id->l) + sizeof(struct opt_hdr)); - resp.uh.len = htons(n = offsetof(struct resp_t, client_id) + - sizeof(struct opt_hdr) + ntohs(client_id->l)); + + n = offsetof(struct resp_t, client_id) + + sizeof(struct opt_hdr) + ntohs(client_id->l); + n = dhcpv6_dns_fill(c, (char *)&resp, n); + resp.uh.len = htons(n); resp.hdr.xid = mh->xid; + tap_ip_send(c, &c->gw6, IPPROTO_UDP, (char *)&resp, n); c->addr6_seen = c->addr6; @@ -489,7 +576,7 @@ int dhcpv6(struct ctx *c, struct ethhdr *eh, size_t len) } /** - * dhcpv6() - Initialise DUID and addresses for DHCPv6 server + * dhcpv6_init() - Initialise DUID and addresses for DHCPv6 server * @c: Execution context */ void dhcpv6_init(struct ctx *c) -- cgit v1.2.3