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;
struct ifreq ifr;
struct iname *tmp;
unsigned short port;
msg.msg_control = control_u.control6;
msg.msg_controllen = sizeof(control_u);
@@ -84,7 +85,7 @@ void dhcp6_packet(time_t now)
msg.msg_iov = &daemon->dhcp_packet;
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;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
@@ -134,15 +135,23 @@ void dhcp6_packet(time_t now)
lease_prune(NULL, now); /* lose any expired leases */
sz = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
lease_update_file(now);
lease_update_dns();
if (sz != 0)
while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, sz, 0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
/* The port in the source address of the original request should
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());
}
}
static int complete_context6(struct in6_addr *local, int prefix,

View File

@@ -1053,8 +1053,8 @@ void make_duid(time_t now);
/* rfc3315.c */
#ifdef HAVE_DHCP6
size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, size_t sz, int is_multicast, time_t now);
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);
#endif
/* dhcp-common.c */

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)]))
size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, size_t sz, int is_unicast, time_t now)
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 dhcp_netid *relay_tags = NULL;
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 */
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);
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;
}
@@ -764,9 +770,14 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
end_opt6(o);
o = new_opt6(OPTION6_PREFERENCE);
put_opt6_char(0);
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);
put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);
end_opt6(o);
}
break;
}