dhcpv6: Fix REPLY messages with NotOnLink status code

The NotOnLink status code needs to be appended to the existing IA
content, because if we omit the requested addresses in the reply,
ISC's dhclient handles it as a NoAddrsAvail response.

Also fix length accounting (we would send a bunch of zeroes after
the IA otherwise), and print an informational message with the
requested address, if it's not appropriate for the link.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
Stefano Brivio 2021-04-21 17:15:23 +02:00
parent 61fa05c7c0
commit faff133629

View file

@ -299,6 +299,7 @@ static struct opt_hdr *dhcpv6_ia_notonlink(struct opt_hdr *o, size_t len,
struct in6_addr *addr) struct in6_addr *addr)
{ {
struct opt_hdr *ia, *ia_addr; struct opt_hdr *ia, *ia_addr;
char buf[INET6_ADDRSTRLEN];
struct in6_addr *req_addr; struct in6_addr *req_addr;
size_t __len; size_t __len;
int ia_type; int ia_type;
@ -309,7 +310,7 @@ ia_ta:
ia = o; ia = o;
while ((ia = dhcpv6_opt(ia, ia_type, &__len))) { while ((ia = dhcpv6_opt(ia, ia_type, &__len))) {
size_t ia_len = ntohs(ia->l) - sizeof(struct opt_hdr); size_t ia_len = ntohs(ia->l);
if (ia_len > __len) if (ia_len > __len)
return NULL; return NULL;
@ -328,8 +329,13 @@ ia_ta:
struct opt_ia_addr *next; struct opt_ia_addr *next;
req_addr = (struct in6_addr *)(ia_addr + 1); req_addr = (struct in6_addr *)(ia_addr + 1);
if (memcmp(addr, req_addr, sizeof(*addr)))
if (memcmp(addr, req_addr, sizeof(*addr))) {
info("DHCPv6: requested address %s not on link",
inet_ntop(AF_INET6, req_addr,
buf, sizeof(buf)));
return ia; return ia;
}
next = (struct opt_ia_addr *)ia_addr + 1; next = (struct opt_ia_addr *)ia_addr + 1;
ia_addr = (struct opt_hdr *)next; ia_addr = (struct opt_hdr *)next;
@ -412,17 +418,21 @@ int dhcpv6(struct ctx *c, struct ethhdr *eh, size_t len)
if ((bad_ia = dhcpv6_ia_notonlink((struct opt_hdr *)(mh + 1), if ((bad_ia = dhcpv6_ia_notonlink((struct opt_hdr *)(mh + 1),
mlen, &c->addr6))) { mlen, &c->addr6))) {
n = OPT_IA_NA ? sizeof(struct opt_ia_na) : info("DHCPv6: received CONFIRM with inappropriate IA,"
sizeof(struct opt_ia_ta); " sending NotOnLink status in REPLY");
memcpy(&resp_not_on_link.var, bad_ia, n);
memcpy(&resp_not_on_link.var + n, &sc_not_on_link, n = ntohs(bad_ia->l) + sizeof(struct opt_hdr);
bad_ia->l = htons(n - sizeof(struct opt_hdr) +
sizeof(sc_not_on_link));
memcpy(resp_not_on_link.var, bad_ia, n);
memcpy(resp_not_on_link.var + n, &sc_not_on_link,
sizeof(sc_not_on_link)); sizeof(sc_not_on_link));
n += sizeof(sc_not_on_link); n += sizeof(sc_not_on_link);
memcpy(&resp_not_on_link.var + n, client_id, memcpy(resp_not_on_link.var + n, client_id,
sizeof(struct opt_hdr) + client_id->l); sizeof(struct opt_hdr) + ntohs(client_id->l));
n += sizeof(struct opt_hdr) + client_id->l; n += sizeof(struct opt_hdr) + ntohs(client_id->l);
n = offsetof(struct resp_not_on_link_t, var) + n; n = offsetof(struct resp_not_on_link_t, var) + n;
resp_not_on_link.uh.len = htons(n); resp_not_on_link.uh.len = htons(n);