2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-22 18:07:25 +00:00

-n [master]

Fix the socket handling for DHCPv6 clients to allow multiple instances
of a clinet on a single machine to work properly.
[ISC-Bugs #34784]
This commit is contained in:
Shawn Routhier 2013-10-21 14:59:41 -07:00
parent d7d9c0c7c3
commit 4b8251a0c0
4 changed files with 112 additions and 33 deletions

View File

@ -123,6 +123,12 @@ work on other platforms. Please report any problems and suggested fixes to
- Update client script for use with openwrt. - Update client script for use with openwrt.
[ISC-Bugs #29843] [ISC-Bugs #29843]
- Fix the socket handling for DHCPv6 clients to allow multiple instances
of a client on a single machine to work properly. Previously only
one client would receive the packets. Thanks to Jiri Popelka at Red Hat
for the bug report and a potential patch.
[ISC-Bugs #34784]
Changes since 4.2.4 Changes since 4.2.4
- Correct code to calculate timing values in client to compare - Correct code to calculate timing values in client to compare

View File

@ -58,10 +58,6 @@ struct in_addr limited_broadcast;
int local_family = AF_INET; int local_family = AF_INET;
struct in_addr local_address; struct in_addr local_address;
#ifdef DHCPv6
struct in6_addr local_address6;
#endif /* DHCPv6 */
void (*bootp_packet_handler) (struct interface_info *, void (*bootp_packet_handler) (struct interface_info *,
struct dhcp_packet *, unsigned, struct dhcp_packet *, unsigned,
unsigned int, unsigned int,
@ -1242,7 +1238,7 @@ discover_interfaces(int state) {
(state == DISCOVER_RELAY)) { (state == DISCOVER_RELAY)) {
if_register6(tmp, 1); if_register6(tmp, 1);
} else { } else {
if_register6(tmp, 0); if_register_linklocal6(tmp);
} }
#endif /* DHCPv6 */ #endif /* DHCPv6 */
} }
@ -1298,13 +1294,14 @@ discover_interfaces(int state) {
tmp -> name, isc_result_totext (status)); tmp -> name, isc_result_totext (status));
#if defined(DHCPv6) #if defined(DHCPv6)
/* Only register the first interface for V6, since they all /* Only register the first interface for V6, since
* use the same socket. XXX: This has some messy side * servers and relays all use the same socket.
* effects if we start dynamically adding and removing * XXX: This has some messy side effects if we start
* interfaces, but we're well beyond that point in terms of * dynamically adding and removing interfaces, but
* mess. * we're well beyond that point in terms of mess.
*/ */
if (local_family == AF_INET6) if (((state == DISCOVER_SERVER) || (state == DISCOVER_RELAY)) &&
(local_family == AF_INET6))
break; break;
#endif #endif
} /* for (tmp = interfaces; ... */ } /* for (tmp = interfaces; ... */

View File

@ -67,6 +67,7 @@
* XXX: this is gross. we need to go back and overhaul the API for socket * XXX: this is gross. we need to go back and overhaul the API for socket
* handling. * handling.
*/ */
static int no_global_v6_socket = 0;
static unsigned int global_v6_socket_references = 0; static unsigned int global_v6_socket_references = 0;
static int global_v6_socket = -1; static int global_v6_socket = -1;
@ -127,7 +128,7 @@ void if_reinitialize_receive (info)
/* Generic interface registration routine... */ /* Generic interface registration routine... */
int int
if_register_socket(struct interface_info *info, int family, if_register_socket(struct interface_info *info, int family,
int *do_multicast) int *do_multicast, struct in6_addr *linklocal6)
{ {
struct sockaddr_storage name; struct sockaddr_storage name;
int name_len; int name_len;
@ -161,10 +162,12 @@ if_register_socket(struct interface_info *info, int family,
addr6 = (struct sockaddr_in6 *)&name; addr6 = (struct sockaddr_in6 *)&name;
addr6->sin6_family = AF_INET6; addr6->sin6_family = AF_INET6;
addr6->sin6_port = local_port; addr6->sin6_port = local_port;
/* XXX: What will happen to multicasts if this is nonzero? */ if (linklocal6) {
memcpy(&addr6->sin6_addr, memcpy(&addr6->sin6_addr,
&local_address6, linklocal6,
sizeof(addr6->sin6_addr)); sizeof(addr6->sin6_addr));
addr6->sin6_scope_id = if_nametoindex(info->name);
}
#ifdef HAVE_SA_LEN #ifdef HAVE_SA_LEN
addr6->sin6_len = sizeof(*addr6); addr6->sin6_len = sizeof(*addr6);
#endif #endif
@ -221,7 +224,7 @@ if_register_socket(struct interface_info *info, int family,
* daemons can bind to their own sockets and get data for their * daemons can bind to their own sockets and get data for their
* respective interfaces. This does not (and should not) affect * respective interfaces. This does not (and should not) affect
* DHCPv4 sockets; we can't yet support BSD sockets well, much * DHCPv4 sockets; we can't yet support BSD sockets well, much
* less multiple sockets. * less multiple sockets. Make sense only with multicast.
*/ */
if ((local_family == AF_INET6) && *do_multicast) { if ((local_family == AF_INET6) && *do_multicast) {
flag = 1; flag = 1;
@ -322,7 +325,7 @@ void if_register_send (info)
struct interface_info *info; struct interface_info *info;
{ {
#ifndef USE_SOCKET_RECEIVE #ifndef USE_SOCKET_RECEIVE
info->wfdesc = if_register_socket(info, AF_INET, 0); info->wfdesc = if_register_socket(info, AF_INET, 0, NULL);
/* If this is a normal IPv4 address, get the hardware address. */ /* If this is a normal IPv4 address, get the hardware address. */
if (strcmp(info->name, "fallback") != 0) if (strcmp(info->name, "fallback") != 0)
get_hw_addr(info->name, &info->hw_address); get_hw_addr(info->name, &info->hw_address);
@ -368,7 +371,7 @@ void if_register_receive (info)
#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
if (global_v4_socket_references == 0) { if (global_v4_socket_references == 0) {
global_v4_socket = if_register_socket(info, AF_INET, 0); global_v4_socket = if_register_socket(info, AF_INET, 0, NULL);
if (global_v4_socket < 0) { if (global_v4_socket < 0) {
/* /*
* if_register_socket() fatally logs if it fails to * if_register_socket() fatally logs if it fails to
@ -384,7 +387,7 @@ void if_register_receive (info)
#else #else
/* If we're using the socket API for sending and receiving, /* If we're using the socket API for sending and receiving,
we don't need to register this interface twice. */ we don't need to register this interface twice. */
info->rfdesc = if_register_socket(info, AF_INET, 0); info->rfdesc = if_register_socket(info, AF_INET, 0, NULL);
#endif /* IP_PKTINFO... */ #endif /* IP_PKTINFO... */
/* If this is a normal IPv4 address, get the hardware address. */ /* If this is a normal IPv4 address, get the hardware address. */
if (strcmp(info->name, "fallback") != 0) if (strcmp(info->name, "fallback") != 0)
@ -477,9 +480,13 @@ if_register6(struct interface_info *info, int do_multicast) {
/* Bounce do_multicast to a stack variable because we may change it. */ /* Bounce do_multicast to a stack variable because we may change it. */
int req_multi = do_multicast; int req_multi = do_multicast;
if (no_global_v6_socket) {
log_fatal("Impossible condition at %s:%d", MDL);
}
if (global_v6_socket_references == 0) { if (global_v6_socket_references == 0) {
global_v6_socket = if_register_socket(info, AF_INET6, global_v6_socket = if_register_socket(info, AF_INET6,
&req_multi); &req_multi, NULL);
if (global_v6_socket < 0) { if (global_v6_socket < 0) {
/* /*
* if_register_socket() fatally logs if it fails to * if_register_socket() fatally logs if it fails to
@ -515,12 +522,73 @@ if_register6(struct interface_info *info, int do_multicast) {
} }
} }
/*
* Register an IPv6 socket bound to the link-local address of
* the argument interface (used by clients on a multiple interface box,
* vs. a server or a relay using the global IPv6 socket and running
* *only* in a single instance).
*/
void
if_register_linklocal6(struct interface_info *info) {
int sock;
int count;
struct in6_addr *addr6 = NULL;
int req_multi = 0;
if (global_v6_socket >= 0) {
log_fatal("Impossible condition at %s:%d", MDL);
}
no_global_v6_socket = 1;
/* get the (?) link-local address */
for (count = 0; count < info->v6address_count; count++) {
addr6 = &info->v6addresses[count];
if (IN6_IS_ADDR_LINKLOCAL(addr6))
break;
}
if (!addr6) {
log_fatal("no link-local IPv6 address for %s", info->name);
}
sock = if_register_socket(info, AF_INET6, &req_multi, addr6);
if (sock < 0) {
log_fatal("if_register_socket for %s fails", info->name);
}
info->rfdesc = sock;
info->wfdesc = sock;
get_hw_addr(info->name, &info->hw_address);
if (!quiet_interface_discovery) {
if (info->shared_network != NULL) {
log_info("Listening on Socket/%d/%s/%s",
global_v6_socket, info->name,
info->shared_network->name);
log_info("Sending on Socket/%d/%s/%s",
global_v6_socket, info->name,
info->shared_network->name);
} else {
log_info("Listening on Socket/%s", info->name);
log_info("Sending on Socket/%s", info->name);
}
}
}
void void
if_deregister6(struct interface_info *info) { if_deregister6(struct interface_info *info) {
/* Dereference the global v6 socket. */ /* client case */
if ((info->rfdesc == global_v6_socket) && if (no_global_v6_socket) {
(info->wfdesc == global_v6_socket) && close(info->rfdesc);
(global_v6_socket_references > 0)) { info->rfdesc = -1;
info->wfdesc = -1;
} else if ((info->rfdesc == global_v6_socket) &&
(info->wfdesc == global_v6_socket) &&
(global_v6_socket_references > 0)) {
/* Dereference the global v6 socket. */
global_v6_socket_references--; global_v6_socket_references--;
info->rfdesc = -1; info->rfdesc = -1;
info->wfdesc = -1; info->wfdesc = -1;
@ -540,7 +608,8 @@ if_deregister6(struct interface_info *info) {
} }
} }
if (global_v6_socket_references == 0) { if (!no_global_v6_socket &&
(global_v6_socket_references == 0)) {
close(global_v6_socket); close(global_v6_socket);
global_v6_socket = -1; global_v6_socket = -1;
@ -692,9 +761,11 @@ ssize_t send_packet6(struct interface_info *interface,
struct sockaddr_in6 *to) { struct sockaddr_in6 *to) {
struct msghdr m; struct msghdr m;
struct iovec v; struct iovec v;
struct sockaddr_in6 dst;
int result; int result;
struct in6_pktinfo *pktinfo; struct in6_pktinfo *pktinfo;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
unsigned int ifindex;
/* /*
* If necessary allocate space for the control message header. * If necessary allocate space for the control message header.
@ -717,9 +788,14 @@ ssize_t send_packet6(struct interface_info *interface,
/* /*
* Set the target address we're sending to. * Set the target address we're sending to.
* Enforce the scope ID for bogus BSDs.
*/ */
m.msg_name = to; memcpy(&dst, to, sizeof(dst));
m.msg_namelen = sizeof(*to); m.msg_name = &dst;
m.msg_namelen = sizeof(dst);
ifindex = if_nametoindex(interface->name);
if (no_global_v6_socket)
dst.sin6_scope_id = ifindex;
/* /*
* Set the data buffer we're sending. (Using this wacky * Set the data buffer we're sending. (Using this wacky
@ -748,7 +824,7 @@ ssize_t send_packet6(struct interface_info *interface,
cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo)); cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
memset(pktinfo, 0, sizeof(*pktinfo)); memset(pktinfo, 0, sizeof(*pktinfo));
pktinfo->ipi6_ifindex = if_nametoindex(interface->name); pktinfo->ipi6_ifindex = ifindex;
m.msg_controllen = cmsg->cmsg_len; m.msg_controllen = cmsg->cmsg_len;
result = sendmsg(interface->wfdesc, &m, 0); result = sendmsg(interface->wfdesc, &m, 0);
@ -1047,7 +1123,7 @@ void maybe_setup_fallback ()
isc_result_t status; isc_result_t status;
struct interface_info *fbi = (struct interface_info *)0; struct interface_info *fbi = (struct interface_info *)0;
if (setup_fallback (&fbi, MDL)) { if (setup_fallback (&fbi, MDL)) {
fbi -> wfdesc = if_register_socket (fbi, AF_INET, 0); fbi -> wfdesc = if_register_socket (fbi, AF_INET, 0, NULL);
fbi -> rfdesc = fbi -> wfdesc; fbi -> rfdesc = fbi -> wfdesc;
log_info ("Sending on Socket/%s%s%s", log_info ("Sending on Socket/%s%s%s",
fbi -> name, fbi -> name,

View File

@ -2414,7 +2414,7 @@ void get_hw_addr(const char *name, struct hardware *hw);
/* socket.c */ /* socket.c */
#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_RECEIVE) \ #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_RECEIVE) \
|| defined (USE_SOCKET_FALLBACK) || defined (USE_SOCKET_FALLBACK)
int if_register_socket(struct interface_info *, int, int *); int if_register_socket(struct interface_info *, int, int *, struct in6_addr *);
#endif #endif
#if defined (USE_SOCKET_FALLBACK) && !defined (USE_SOCKET_SEND) #if defined (USE_SOCKET_FALLBACK) && !defined (USE_SOCKET_SEND)
@ -2425,7 +2425,7 @@ ssize_t send_fallback (struct interface_info *,
struct in_addr, struct in_addr,
struct sockaddr_in *, struct hardware *); struct sockaddr_in *, struct hardware *);
ssize_t send_fallback6(struct interface_info *, struct packet *, ssize_t send_fallback6(struct interface_info *, struct packet *,
struct dhcp_packet *, size_t, struct in6_addr, struct dhcp_packet *, size_t, struct in6_addr *,
struct sockaddr_in6 *, struct hardware *); struct sockaddr_in6 *, struct hardware *);
#endif #endif
@ -2461,6 +2461,7 @@ void maybe_setup_fallback (void);
#endif #endif
void if_register6(struct interface_info *info, int do_multicast); void if_register6(struct interface_info *info, int do_multicast);
void if_register_linklocal6(struct interface_info *info);
ssize_t receive_packet6(struct interface_info *interface, ssize_t receive_packet6(struct interface_info *interface,
unsigned char *buf, size_t len, unsigned char *buf, size_t len,
struct sockaddr_in6 *from, struct in6_addr *to_addr, struct sockaddr_in6 *from, struct in6_addr *to_addr,
@ -2606,7 +2607,6 @@ void interface_trace_setup (void);
extern struct in_addr limited_broadcast; extern struct in_addr limited_broadcast;
extern int local_family; extern int local_family;
extern struct in_addr local_address; extern struct in_addr local_address;
extern struct in6_addr local_address6;
extern u_int16_t local_port; extern u_int16_t local_port;
extern u_int16_t remote_port; extern u_int16_t remote_port;