From 1d0f91c4a98f0fdfea181e74736f2471eeab2132 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 12 Mar 2012 11:56:22 +0000 Subject: [PATCH] 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. --- src/dhcp6.c | 19 ++++++++++++++----- src/dnsmasq.h | 4 ++-- src/rfc3315.c | 27 +++++++++++++++++++-------- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/dhcp6.c b/src/dhcp6.c index 154ff76..70c8dcd 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -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, diff --git a/src/dnsmasq.h b/src/dnsmasq.h index d6179be..1f9166c 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -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 */ diff --git a/src/rfc3315.c b/src/rfc3315.c index b0c31ad..73f799a 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -34,12 +34,18 @@ 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) vendor->netid.next = &vendor->netid; @@ -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,10 +770,15 @@ 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; } }