Don't trust the port in the source address of requests.

At least one client gets it wrong: always send to the client port for
clients, and the server port for relays.
This commit is contained in:
Simon Kelley
2012-03-12 11:56:22 +00:00
parent 2a82db4caf
commit 1d0f91c4a9
3 changed files with 35 additions and 15 deletions

View File

@@ -75,6 +75,7 @@ void dhcp6_packet(time_t now)
ssize_t sz; ssize_t sz;
struct ifreq ifr; struct ifreq ifr;
struct iname *tmp; struct iname *tmp;
unsigned short port;
msg.msg_control = control_u.control6; msg.msg_control = control_u.control6;
msg.msg_controllen = sizeof(control_u); msg.msg_controllen = sizeof(control_u);
@@ -84,7 +85,7 @@ void dhcp6_packet(time_t now)
msg.msg_iov = &daemon->dhcp_packet; msg.msg_iov = &daemon->dhcp_packet;
msg.msg_iovlen = 1; msg.msg_iovlen = 1;
if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1 || sz <= 4) if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
return; return;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
@@ -134,16 +135,24 @@ void dhcp6_packet(time_t now)
lease_prune(NULL, now); /* lose any expired leases */ lease_prune(NULL, now); /* lose any expired leases */
sz = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback, port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now); sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
lease_update_file(now); lease_update_file(now);
lease_update_dns(); lease_update_dns();
if (sz != 0) /* The port in the source address of the original request should
while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, sz, 0, (struct sockaddr *)&from, sizeof(from)) == -1 && be correct, but at least once client sends from the server port,
so we explicitly send to the client port to a client, and the
server port to a relay. */
if (port != 0)
{
from.sin6_port = htons(port);
while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
retry_send()); retry_send());
} }
}
static int complete_context6(struct in6_addr *local, int prefix, static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam) int scope, int if_index, int dad, void *vparam)

View File

@@ -1053,7 +1053,7 @@ void make_duid(time_t now);
/* rfc3315.c */ /* rfc3315.c */
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, size_t sz, int is_multicast, time_t now); struct in6_addr *fallback, size_t sz, int is_multicast, time_t now);
#endif #endif

View File

@@ -34,11 +34,17 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size);
#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)])) #define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)]))
size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, size_t sz, int is_unicast, time_t now) struct in6_addr *fallback, size_t sz, int is_unicast, time_t now)
{ {
struct dhcp_netid *relay_tags = NULL; struct dhcp_netid *relay_tags = NULL;
struct dhcp_vendor *vendor; struct dhcp_vendor *vendor;
int msg_type;
if (sz <= 4)
return 0;
msg_type = *((unsigned char *)daemon->dhcp_packet.iov_base);
/* Mark these so we only match each at most once, to avoid tangled linked lists */ /* Mark these so we only match each at most once, to avoid tangled linked lists */
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
@@ -47,7 +53,7 @@ size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name
save_counter(0); save_counter(0);
if (dhcp6_maybe_relay(NULL, &relay_tags, context, interface, iface_name, fallback, daemon->dhcp_packet.iov_base, sz, is_unicast, now)) if (dhcp6_maybe_relay(NULL, &relay_tags, context, interface, iface_name, fallback, daemon->dhcp_packet.iov_base, sz, is_unicast, now))
return save_counter(0); return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
return 0; return 0;
} }
@@ -764,9 +770,14 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
end_opt6(o); end_opt6(o);
if (address_assigned)
{
/* If --dhcp-authoritative is set, we can tell client not to wait for
other possible servers */
o = new_opt6(OPTION6_PREFERENCE); o = new_opt6(OPTION6_PREFERENCE);
put_opt6_char(0); put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);
end_opt6(o); end_opt6(o);
}
break; break;
} }