2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-30 05:47:45 +00:00

- The BSD socket code will now permit multiple DHCPv6 server instances to

operate on different interfaces. [ISC-Bugs #17610]
This commit is contained in:
David Hankins 2008-08-08 20:26:57 +00:00
parent 535485df4e
commit 08c4e7ce2b
4 changed files with 143 additions and 58 deletions

View File

@ -48,7 +48,7 @@ work on other platforms. Please report any problems and suggested fixes to
<dhcp-users@isc.org>.
Changes since 4.1.0a2
Changes since 4.1.0a1
- Corrected list of failover state values in dhcpd man page.
@ -60,8 +60,6 @@ work on other platforms. Please report any problems and suggested fixes to
- The server wasn't always sending the FQDN option when it should.
Changes since 4.1.0a1
- Fixed a coredump when adding a class via OMAPI.
- Check whether files are zero length before trying to parse them.
@ -111,6 +109,9 @@ work on other platforms. Please report any problems and suggested fixes to
- Fix handling of -A and -a flags in dhcrelay; it was failing to expand
packet size as needed to add relay agent options.
- The BSD socket code will now permit multiple DHCPv6 server instances to
operate on different interfaces.
Changes since 4.0.0 (new features)
- Added DHCPv6 rapid commit support.

View File

@ -1265,6 +1265,10 @@ discover_interfaces(int state) {
if (status != ISC_R_SUCCESS)
log_fatal ("Can't register I/O handle for %s: %s",
tmp -> name, isc_result_totext (status));
#if defined(DHCPv6)
/* Only register the first interface with OMAPI for select. */
break;
#endif
}
if (state == DISCOVER_SERVER && wifcount == 0) {
@ -1395,6 +1399,7 @@ got_one_v6(omapi_object_t *h) {
char buf[65536]; /* maximum size for a UDP packet is 65536 */
struct interface_info *ip;
int is_unicast;
unsigned int if_idx = 0;
if (h->type != dhcp_type_interface) {
return ISC_R_INVALIDARG;
@ -1402,12 +1407,16 @@ got_one_v6(omapi_object_t *h) {
ip = (struct interface_info *)h;
result = receive_packet6(ip, (unsigned char *)buf, sizeof(buf),
&from, &to);
&from, &to, &if_idx);
if (result < 0) {
log_error("receive_packet6() failed on %s: %m", ip->name);
return ISC_R_UNEXPECTED;
}
/* 0 is 'any' interface. */
if (if_idx == 0)
return ISC_R_NOTFOUND;
if (dhcpv6_packet_handler != NULL) {
/*
* If a packet is not multicast, we assume it is unicast.
@ -1421,6 +1430,13 @@ got_one_v6(omapi_object_t *h) {
ifrom.len = 16;
memcpy(ifrom.iabuf, &from.sin6_addr, ifrom.len);
/* Seek forward to find the matching source interface. */
while ((ip != NULL) && (if_nametoindex(ip->name) != if_idx))
ip = ip->next;
if (ip == NULL)
return ISC_R_NOTFOUND;
(*dhcpv6_packet_handler)(ip, buf,
result, from.sin6_port,
&ifrom, is_unicast);

View File

@ -54,6 +54,17 @@
# endif
#endif
#if defined(DHCPv6)
/*
* XXX: this is gross. we need to go back and overhaul the API for socket
* handling and make an additional layer for v6 sockets.
*/
static unsigned int global_v6_socket_references = 0;
static int global_v6_socket = -1;
static void if_register_multicast(struct interface_info *info);
#endif
/*
* If we can't bind() to a specific interface, then we can only have
* a single socket. This variable insures that we don't try to listen
@ -131,7 +142,7 @@ get_ifaddr6(struct interface_info *info, struct in6_addr *ifaddr6) {
/* Generic interface registration routine... */
int
if_register_socket(struct interface_info *info, int family, int do_multicast) {
if_register_socket(struct interface_info *info, int family) {
struct sockaddr_storage name;
int name_len;
int sock;
@ -159,6 +170,7 @@ if_register_socket(struct interface_info *info, int family, int do_multicast) {
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&name;
addr->sin6_family = AF_INET6;
addr->sin6_port = local_port;
/* XXX: what will happen to multicasts if this is nonzero? */
memcpy(&addr->sin6_addr,
&local_address6,
sizeof(addr->sin6_addr));
@ -217,6 +229,24 @@ if_register_socket(struct interface_info *info, int family, int do_multicast) {
log_fatal("Can't set SO_BROADCAST option on dhcp socket: %m");
}
#if defined(DHCPv6) && defined(SO_REUSEPORT)
/*
* We only set SO_REUSEPORT on AF_INET6 sockets, so that multiple
* 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.
*/
if (local_family == AF_INET6) {
flag = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
(char *)&flag, sizeof(flag)) < 0) {
log_fatal("Can't set SO_REUSEPORT option on dhcp "
"socket: %m");
}
}
#endif
/* Bind the socket to this interface's IP address. */
if (bind(sock, (struct sockaddr *)&name, name_len) < 0) {
log_error("Can't bind to dhcp address: %m");
@ -229,7 +259,7 @@ if_register_socket(struct interface_info *info, int family, int do_multicast) {
#if defined(SO_BINDTODEVICE)
/* Bind this socket to this interface. */
if (info->ifp &&
if ((local_family != AF_INET6) && (info->ifp != NULL) &&
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
(char *)(info -> ifp), sizeof(*(info -> ifp))) < 0) {
log_fatal("setsockopt: SO_BINDTODEVICE: %m");
@ -272,36 +302,6 @@ if_register_socket(struct interface_info *info, int family, int do_multicast) {
}
#endif
}
if ((family == AF_INET6) && do_multicast) {
struct ipv6_mreq mreq;
/*
* Join the DHCPv6 multicast groups so we will receive
* multicast messages.
*/
if (inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers,
&mreq.ipv6mr_multiaddr) <= 0) {
log_fatal("inet_pton: unable to convert '%s'",
All_DHCP_Relay_Agents_and_Servers);
}
mreq.ipv6mr_interface = if_nametoindex(info->name);
if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
&mreq, sizeof(mreq)) < 0) {
log_fatal("setsockopt: IPV6_JOIN_GROUP: %m");
}
if (inet_pton(AF_INET6, All_DHCP_Servers,
&mreq.ipv6mr_multiaddr) <= 0) {
log_fatal("inet_pton: unable to convert '%s'",
All_DHCP_Servers);
}
mreq.ipv6mr_interface = if_nametoindex(info->name);
if (((info->flags & INTERFACE_DOWNSTREAM) == 0) &&
(setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
&mreq, sizeof(mreq)) < 0)) {
log_fatal("setsockopt: IPV6_JOIN_GROUP: %m");
}
}
if ((family == AF_INET6) &&
((info->flags & INTERFACE_UPSTREAM) != 0)) {
@ -313,7 +313,8 @@ if_register_socket(struct interface_info *info, int family, int do_multicast) {
}
#endif /* DHCPv6 */
if (strcmp(info->name, "fallback") != 0)
/* If this is a normal IPv4 address, get the hardware address. */
if ((local_family == AF_INET) && (strcmp(info->name, "fallback") != 0))
get_hw_addr(info->name, &info->hw_address);
return sock;
@ -325,7 +326,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);
#if defined (USE_SOCKET_FALLBACK)
/* Fallback only registers for send, but may need to receive as
well. */
@ -393,15 +394,70 @@ void if_deregister_receive (info)
#ifdef DHCPv6
/*
* This function joines the interface to DHCPv6 multicast groups so we will
* receive multicast messages.
*/
static void
if_register_multicast(struct interface_info *info) {
int sock = info->rfdesc;
struct ipv6_mreq mreq;
if (inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers,
&mreq.ipv6mr_multiaddr) <= 0) {
log_fatal("inet_pton: unable to convert '%s'",
All_DHCP_Relay_Agents_and_Servers);
}
mreq.ipv6mr_interface = if_nametoindex(info->name);
if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
&mreq, sizeof(mreq)) < 0) {
log_fatal("setsockopt: IPV6_JOIN_GROUP: %m");
}
if (inet_pton(AF_INET6, All_DHCP_Servers,
&mreq.ipv6mr_multiaddr) <= 0) {
log_fatal("inet_pton: unable to convert '%s'",
All_DHCP_Servers);
}
mreq.ipv6mr_interface = if_nametoindex(info->name);
if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
&mreq, sizeof(mreq)) < 0) {
log_fatal("setsockopt: IPV6_JOIN_GROUP: %m");
}
}
void
if_register6(struct interface_info *info, int do_multicast) {
info->rfdesc = if_register_socket(info, AF_INET6, do_multicast);
info->wfdesc = info->rfdesc;
if (global_v6_socket_references == 0) {
global_v6_socket = if_register_socket(info, AF_INET6);
if (global_v6_socket < 0) {
/*
* if_register_socket() fatally logs if it fails
* to create a socket, so this is just a sanity
* check.
*/
log_fatal("Impossible condition at %s:%d", MDL);
} else {
log_info("Bound to *:%d", ntohs(local_port));
}
}
/* Reference the global v6 socket. */
info->rfdesc = global_v6_socket;
info->wfdesc = global_v6_socket;
global_v6_socket_references++;
if (do_multicast)
if_register_multicast(info);
get_hw_addr(info->name, &info->hw_address);
if (!quiet_interface_discovery) {
if (info->shared_network != NULL) {
log_info("Listening on Socket/%s/%s", info->name,
log_info("Listening on Socket/%d/%s/%s",
global_v6_socket, info->name,
info->shared_network->name);
log_info("Sending on Socket/%s/%s", info->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);
@ -412,14 +468,16 @@ if_register6(struct interface_info *info, int do_multicast) {
void
if_deregister6(struct interface_info *info) {
/*
* XXX: it would be nice to check for >= 0, but we need to change
* interface_allocate() to set the file descriptors for that.
*/
close(info->rfdesc);
info->rfdesc = -1;
close(info->wfdesc);
info->wfdesc = -1;
/* Dereference the global v6 socket. */
if ((info->rfdesc == global_v6_socket) &&
(info->wfdesc == global_v6_socket) &&
(global_v6_socket_references > 0)) {
global_v6_socket_references--;
info->rfdesc = -1;
info->wfdesc = -1;
} else {
log_fatal("Impossible condition at %s:%d", MDL);
}
if (!quiet_interface_discovery) {
if (info->shared_network != NULL) {
@ -432,6 +490,13 @@ if_deregister6(struct interface_info *info) {
log_info("Disabling output on Socket/%s", info->name);
}
}
if (global_v6_socket_references == 0) {
close(global_v6_socket);
global_v6_socket = -1;
log_info("Unbound from *:%d", ntohs(local_port));
}
}
#endif /* DHCPv6 */
@ -640,13 +705,14 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
ssize_t
receive_packet6(struct interface_info *interface,
unsigned char *buf, size_t len,
struct sockaddr_in6 *from, struct in6_addr *to_addr) {
struct sockaddr_in6 *from, struct in6_addr *to_addr,
unsigned int *if_idx) {
struct msghdr m;
struct iovec v;
int result;
struct cmsghdr *cmsg;
struct in6_pktinfo *pktinfo;
int found_to_addr;
int found_pktinfo;
union {
struct cmsghdr cmsg_sizer;
u_int8_t pktinfo_sizer[CMSG_SPACE(sizeof(struct in6_pktinfo))];
@ -695,18 +761,19 @@ receive_packet6(struct interface_info *interface,
* We also keep a flag to see if we found it. If we
* didn't, then we consider this to be an error.
*/
found_to_addr = 0;
found_pktinfo = 0;
cmsg = CMSG_FIRSTHDR(&m);
while (cmsg != NULL) {
if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
(cmsg->cmsg_type == IPV6_PKTINFO)) {
pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
*to_addr = pktinfo->ipi6_addr;
found_to_addr = 1;
*if_idx = pktinfo->ipi6_ifindex;
found_pktinfo = 1;
}
cmsg = CMSG_NXTHDR(&m, cmsg);
}
if (!found_to_addr) {
if (!found_pktinfo) {
result = -1;
errno = EIO;
}

View File

@ -1124,7 +1124,7 @@ struct interface_info {
unsigned remote_id_len; /* Length of Remote ID. */
char name [IFNAMSIZ]; /* Its name... */
int index; /* Its index. */
int index; /* Its index in the registry. */
int rfdesc; /* Its read file descriptor. */
int wfdesc; /* Its write file descriptor, if
different. */
@ -2135,7 +2135,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);
#endif
#if defined (USE_SOCKET_FALLBACK) && !defined (USE_SOCKET_SEND)
@ -2184,7 +2184,8 @@ void maybe_setup_fallback PROTO ((void));
void if_register6(struct interface_info *info, int do_multicast);
ssize_t receive_packet6(struct interface_info *interface,
unsigned char *buf, size_t len,
struct sockaddr_in6 *from, struct in6_addr *to_addr);
struct sockaddr_in6 *from, struct in6_addr *to_addr,
unsigned int *if_index);
void if_deregister6(struct interface_info *info);