mirror of
https://gitlab.isc.org/isc-projects/dhcp
synced 2025-08-22 01:49:35 +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:
parent
d7d9c0c7c3
commit
4b8251a0c0
6
RELNOTES
6
RELNOTES
@ -123,6 +123,12 @@ work on other platforms. Please report any problems and suggested fixes to
|
||||
- Update client script for use with openwrt.
|
||||
[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
|
||||
|
||||
- Correct code to calculate timing values in client to compare
|
||||
|
@ -58,10 +58,6 @@ struct in_addr limited_broadcast;
|
||||
int local_family = AF_INET;
|
||||
struct in_addr local_address;
|
||||
|
||||
#ifdef DHCPv6
|
||||
struct in6_addr local_address6;
|
||||
#endif /* DHCPv6 */
|
||||
|
||||
void (*bootp_packet_handler) (struct interface_info *,
|
||||
struct dhcp_packet *, unsigned,
|
||||
unsigned int,
|
||||
@ -1242,7 +1238,7 @@ discover_interfaces(int state) {
|
||||
(state == DISCOVER_RELAY)) {
|
||||
if_register6(tmp, 1);
|
||||
} else {
|
||||
if_register6(tmp, 0);
|
||||
if_register_linklocal6(tmp);
|
||||
}
|
||||
#endif /* DHCPv6 */
|
||||
}
|
||||
@ -1298,13 +1294,14 @@ discover_interfaces(int state) {
|
||||
tmp -> name, isc_result_totext (status));
|
||||
|
||||
#if defined(DHCPv6)
|
||||
/* Only register the first interface for V6, since they all
|
||||
* use the same socket. XXX: This has some messy side
|
||||
* effects if we start dynamically adding and removing
|
||||
* interfaces, but we're well beyond that point in terms of
|
||||
* mess.
|
||||
/* Only register the first interface for V6, since
|
||||
* servers and relays all use the same socket.
|
||||
* XXX: This has some messy side effects if we start
|
||||
* dynamically adding and removing interfaces, but
|
||||
* 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;
|
||||
#endif
|
||||
} /* for (tmp = interfaces; ... */
|
||||
|
114
common/socket.c
114
common/socket.c
@ -67,6 +67,7 @@
|
||||
* XXX: this is gross. we need to go back and overhaul the API for socket
|
||||
* handling.
|
||||
*/
|
||||
static int no_global_v6_socket = 0;
|
||||
static unsigned int global_v6_socket_references = 0;
|
||||
static int global_v6_socket = -1;
|
||||
|
||||
@ -127,7 +128,7 @@ void if_reinitialize_receive (info)
|
||||
/* Generic interface registration routine... */
|
||||
int
|
||||
if_register_socket(struct interface_info *info, int family,
|
||||
int *do_multicast)
|
||||
int *do_multicast, struct in6_addr *linklocal6)
|
||||
{
|
||||
struct sockaddr_storage name;
|
||||
int name_len;
|
||||
@ -161,10 +162,12 @@ if_register_socket(struct interface_info *info, int family,
|
||||
addr6 = (struct sockaddr_in6 *)&name;
|
||||
addr6->sin6_family = AF_INET6;
|
||||
addr6->sin6_port = local_port;
|
||||
/* XXX: What will happen to multicasts if this is nonzero? */
|
||||
memcpy(&addr6->sin6_addr,
|
||||
&local_address6,
|
||||
sizeof(addr6->sin6_addr));
|
||||
if (linklocal6) {
|
||||
memcpy(&addr6->sin6_addr,
|
||||
linklocal6,
|
||||
sizeof(addr6->sin6_addr));
|
||||
addr6->sin6_scope_id = if_nametoindex(info->name);
|
||||
}
|
||||
#ifdef HAVE_SA_LEN
|
||||
addr6->sin6_len = sizeof(*addr6);
|
||||
#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
|
||||
* respective interfaces. This does not (and should not) affect
|
||||
* 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) {
|
||||
flag = 1;
|
||||
@ -322,7 +325,7 @@ void if_register_send (info)
|
||||
struct interface_info *info;
|
||||
{
|
||||
#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 (strcmp(info->name, "fallback") != 0)
|
||||
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 (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_register_socket() fatally logs if it fails to
|
||||
@ -384,7 +387,7 @@ void if_register_receive (info)
|
||||
#else
|
||||
/* If we're using the socket API for sending and receiving,
|
||||
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... */
|
||||
/* If this is a normal IPv4 address, get the hardware address. */
|
||||
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. */
|
||||
int req_multi = do_multicast;
|
||||
|
||||
if (no_global_v6_socket) {
|
||||
log_fatal("Impossible condition at %s:%d", MDL);
|
||||
}
|
||||
|
||||
if (global_v6_socket_references == 0) {
|
||||
global_v6_socket = if_register_socket(info, AF_INET6,
|
||||
&req_multi);
|
||||
&req_multi, NULL);
|
||||
if (global_v6_socket < 0) {
|
||||
/*
|
||||
* 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
|
||||
if_deregister6(struct interface_info *info) {
|
||||
/* Dereference the global v6 socket. */
|
||||
if ((info->rfdesc == global_v6_socket) &&
|
||||
(info->wfdesc == global_v6_socket) &&
|
||||
(global_v6_socket_references > 0)) {
|
||||
/* client case */
|
||||
if (no_global_v6_socket) {
|
||||
close(info->rfdesc);
|
||||
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--;
|
||||
info->rfdesc = -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);
|
||||
global_v6_socket = -1;
|
||||
|
||||
@ -692,9 +761,11 @@ ssize_t send_packet6(struct interface_info *interface,
|
||||
struct sockaddr_in6 *to) {
|
||||
struct msghdr m;
|
||||
struct iovec v;
|
||||
struct sockaddr_in6 dst;
|
||||
int result;
|
||||
struct in6_pktinfo *pktinfo;
|
||||
struct cmsghdr *cmsg;
|
||||
unsigned int ifindex;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* Enforce the scope ID for bogus BSDs.
|
||||
*/
|
||||
m.msg_name = to;
|
||||
m.msg_namelen = sizeof(*to);
|
||||
memcpy(&dst, to, sizeof(dst));
|
||||
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
|
||||
@ -748,7 +824,7 @@ ssize_t send_packet6(struct interface_info *interface,
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
|
||||
pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
|
||||
memset(pktinfo, 0, sizeof(*pktinfo));
|
||||
pktinfo->ipi6_ifindex = if_nametoindex(interface->name);
|
||||
pktinfo->ipi6_ifindex = ifindex;
|
||||
m.msg_controllen = cmsg->cmsg_len;
|
||||
|
||||
result = sendmsg(interface->wfdesc, &m, 0);
|
||||
@ -1047,7 +1123,7 @@ void maybe_setup_fallback ()
|
||||
isc_result_t status;
|
||||
struct interface_info *fbi = (struct interface_info *)0;
|
||||
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;
|
||||
log_info ("Sending on Socket/%s%s%s",
|
||||
fbi -> name,
|
||||
|
@ -2414,7 +2414,7 @@ void get_hw_addr(const char *name, struct hardware *hw);
|
||||
/* socket.c */
|
||||
#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_RECEIVE) \
|
||||
|| 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
|
||||
|
||||
#if defined (USE_SOCKET_FALLBACK) && !defined (USE_SOCKET_SEND)
|
||||
@ -2425,7 +2425,7 @@ ssize_t send_fallback (struct interface_info *,
|
||||
struct in_addr,
|
||||
struct sockaddr_in *, struct hardware *);
|
||||
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 *);
|
||||
#endif
|
||||
|
||||
@ -2461,6 +2461,7 @@ void maybe_setup_fallback (void);
|
||||
#endif
|
||||
|
||||
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,
|
||||
unsigned char *buf, size_t len,
|
||||
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 int local_family;
|
||||
extern struct in_addr local_address;
|
||||
extern struct in6_addr local_address6;
|
||||
|
||||
extern u_int16_t local_port;
|
||||
extern u_int16_t remote_port;
|
||||
|
Loading…
x
Reference in New Issue
Block a user