From 3ceea9e75584bb526ba63430e6a6b47d6cc7cb70 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Tue, 22 Jul 2025 20:43:52 +0100 Subject: [PATCH] Fix packet validation in DHCPv6. Some messages must be sent as multicast. --- src/dhcp6.c | 22 +++++++++++++++------- src/dnsmasq.h | 2 +- src/rfc3315.c | 31 ++++++++++++++++++------------- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/dhcp6.c b/src/dhcp6.c index 06285d6..ae19d0e 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -176,6 +176,7 @@ void dhcp6_packet(time_t now) else { struct dhcp_bridge *bridge, *alias; + int multicast_dest = 0; for (tmp = daemon->if_except; tmp; tmp = tmp->next) if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) @@ -231,13 +232,20 @@ void dhcp6_packet(time_t now) memset(&context->local6, 0, IN6ADDRSZ); } - /* Ignore requests sent to the ALL_SERVERS multicast address for relay when - we're listening there for DHCPv6 server reasons. */ - inet_pton(AF_INET6, ALL_SERVERS, &all_servers); + inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &all_servers); + if (IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers)) + multicast_dest = 1; - if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) && - relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now)) - return; + inet_pton(AF_INET6, ALL_SERVERS, &all_servers); + if (IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers)) + multicast_dest = 1; + else + { + /* Ignore requests sent to the ALL_SERVERS multicast address for relay when + we're listening there for DHCPv6 server reasons. */ + if (relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now)) + return; + } if (!iface_enumerate(AF_INET6, &parm, (callback_t){.af_inet6=complete_context6})) return; @@ -266,7 +274,7 @@ void dhcp6_packet(time_t now) lease_prune(NULL, now); /* lose any expired leases */ - port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback, + port = dhcp6_reply(parm.current, multicast_dest, if_index, ifr.ifr_name, &parm.fallback, &parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now); /* The port in the source address of the original request should diff --git a/src/dnsmasq.h b/src/dnsmasq.h index d1abed6..d0faf06 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1801,7 +1801,7 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, /* rfc3315.c */ #ifdef HAVE_DHCP6 -unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, +unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int interface, char *iface_name, struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr, size_t sz, struct in6_addr *client_addr, time_t now); int relay_upstream6(int iface_index, ssize_t sz, struct in6_addr *peer_address, diff --git a/src/rfc3315.c b/src/rfc3315.c index 701bd9a..4e7a012 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -21,7 +21,7 @@ struct state { unsigned char *clid; - int clid_len, ia_type, interface, hostname_auth, lease_allocate; + int multicast_dest, clid_len, ia_type, interface, hostname_auth, lease_allocate; char *client_hostname, *hostname, *domain, *send_domain; struct dhcp_context *context; struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr; @@ -68,7 +68,7 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time #define opt6_user_vendor_next(opt, end) (opt6_next(((uint8_t *) opt) - 2, end)) -unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, +unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int interface, char *iface_name, struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr, size_t sz, struct in6_addr *client_addr, time_t now) { @@ -87,6 +87,7 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if reset_counter(); state.context = context; + state.multicast_dest = multicast_dest; state.interface = interface; state.iface_name = iface_name; state.fallback = fallback; @@ -333,20 +334,24 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu else if (msg_type != DHCP6IREQ) return 0; - /* server-id must match except for SOLICIT, CONFIRM and REBIND messages, which MUST NOT - have a server-id. 3315 para 15.x */ opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1); - - if (msg_type == DHCP6SOLICIT || msg_type == DHCP6CONFIRM || msg_type == DHCP6REBIND) + + if (msg_type == DHCP6SOLICIT || msg_type == DHCP6CONFIRM || msg_type == DHCP6REBIND || msg_type == DHCP6IREQ) { - if (opt) + /* Above message types must be multicast 3315 Section 15. */ + if (!state->multicast_dest) return 0; - } - else if (msg_type == DHCP6IREQ) - { - /* If server-id provided, it must match. */ - if (opt && (opt6_len(opt) != daemon->duid_len || - memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0)) + + /* server-id must match except for SOLICIT, CONFIRM and REBIND messages, which MUST NOT + have a server-id. 3315 para 15.x */ + if (msg_type == DHCP6IREQ) + { + /* If server-id provided in IREQ, it must match. */ + if (opt && (opt6_len(opt) != daemon->duid_len || + memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0)) + return 0; + } + else if (opt) return 0; } else