Compare commits

...
7 changed files with 152 additions and 24 deletions

No files matched your search

+10 -1
View File
@@ -16,7 +16,16 @@ version 2.66
this idea.
Fix crash on startup on Solaris 11. Regression probably
introduced in 2.61. Thanks to Geoff Johnstone for the patch.
introduced in 2.61. Thanks to Geoff Johnstone for the
patch.
Add code to make behaviour for TCP DNS requests that same
as for UDP requests, when a request arrives for an allowed
address, but via a banned interface. This change is only
active on Linux, since the relevant API is missing (AFAIK)
on other platforms. Many thanks to Tomas Hozza for
spotting the problem, and doing invaluable discovery of
the obscure and undocumented API required for the solution.
version 2.65
+7 -1
View File
@@ -717,7 +717,13 @@ A single
.B dhcp-host
may contain an IPv4 address or an IPv6 address, or both. IPv6 addresses must be bracketed by square brackets thus:
.B --dhcp-host=laptop,[1234::56]
Note that in IPv6 DHCP, the hardware address is not normally available, so a client must be identified by client-id (called client DUID in IPv6-land) or hostname.
IPv6 addresses may contain only the host-identifier part:
.B --dhcp-host=laptop,[::56]
in which case thay act as wildcards in constructed dhcp ranges, with
the appropriate network part inserted.
Note that in IPv6 DHCP, the hardware address is not normally
available, so a client must be identified by client-id (called client
DUID in IPv6-land) or hostname.
The special option id:* means "ignore any client-id
and use MAC addresses only." This is useful when a client presents a client-id sometimes
+11 -3
View File
@@ -399,11 +399,19 @@ struct dhcp_context *narrow_context6(struct dhcp_context *context,
static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)
{
if (!context) /* called via find_config() from lease_update_from_configs() */
return 1;
/* expand wildcard on contructed contexts */
if ((config->flags & CONFIG_WILDCARD) &&
(context->flags & CONTEXT_CONSTRUCTED))
{
u64 addrpart = addr6part(&config->addr6);
config->addr6 = context->start6;
setaddr6part(&config->addr6, addrpart);
return 1;
}
if (!(config->flags & CONFIG_ADDR6) || is_addr_in_context6(context, &config->addr6))
return 1;
return 0;
}
+43 -19
View File
@@ -1337,7 +1337,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if (listener->tcpfd != -1 && FD_ISSET(listener->tcpfd, set))
{
int confd;
int confd, client_ok = 1;
struct irec *iface = NULL;
pid_t p;
union mysockaddr tcp_addr;
@@ -1348,25 +1348,49 @@ static void check_dns_listeners(fd_set *set, time_t now)
if (confd == -1 ||
getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
continue;
if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
if (option_bool(OPT_NOWILD))
iface = listener->iface; /* May be NULL */
else
{
/* Check for allowed interfaces when binding the wildcard address:
we do this by looking for an interface with the same address as
the local address of the TCP connection, then looking to see if that's
an allowed interface. As a side effect, we get the netmask of the
interface too, for localisation. */
/* interface may be new since startup */
if (enumerate_interfaces())
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, &tcp_addr))
break;
}
if (!iface && !(option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)))
else
{
int if_index;
/* In full wildcard mode, need to refresh interface list.
This happens automagically in CLEVERBIND */
if (!option_bool(OPT_CLEVERBIND))
enumerate_interfaces();
/* if we can find the arrival interface, check it's one that's allowed */
if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0)
{
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->index == if_index)
break;
if (!iface)
client_ok = 0;
}
if (option_bool(OPT_CLEVERBIND))
iface = listener->iface; /* May be NULL */
else
{
/* Check for allowed interfaces when binding the wildcard address:
we do this by looking for an interface with the same address as
the local address of the TCP connection, then looking to see if that's
an allowed interface. As a side effect, we get the netmask of the
interface too, for localisation. */
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, &tcp_addr))
break;
if (!iface)
client_ok = 0;
}
}
if (!client_ok)
{
shutdown(confd, SHUT_RDWR);
close(confd);
+2
View File
@@ -593,6 +593,7 @@ struct dhcp_config {
#define CONFIG_DECLINED 1024 /* address declined by client */
#define CONFIG_BANK 2048 /* from dhcp hosts file */
#define CONFIG_ADDR6 4096
#define CONFIG_WILDCARD 8192
struct dhcp_opt {
int opt, len, flags;
@@ -1002,6 +1003,7 @@ void create_bound_listeners(int die);
int is_dad_listeners(void);
int iface_check(int family, struct all_addr *addr, char *name, int *auth_dns);
int fix_fd(int fd);
int tcp_interface(int fd, int af);
struct in_addr get_ifaddr(char *intr);
#ifdef HAVE_IPV6
int set_ipv6pktinfo(int fd);
+71
View File
@@ -459,6 +459,77 @@ int set_ipv6pktinfo(int fd)
return 0;
}
#endif
/* Find the interface on which a TCP connection arrived, if possible, or zero otherwise. */
int tcp_interface(int fd, int af)
{
int if_index = 0;
#ifdef HAVE_LINUX_NETWORK
int opt = 1;
struct cmsghdr *cmptr;
struct msghdr msg;
/* use mshdr do that the CMSDG_* macros are available */
msg.msg_control = daemon->packet;
msg.msg_controllen = daemon->packet_buff_sz;
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if (af == AF_INET)
{
if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 &&
getsockopt(fd, IPPROTO_IP, IP_PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
{
union {
unsigned char *c;
struct in_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
if_index = p.p->ipi_ifindex;
}
}
#ifdef HAVE_IPV6
else
{
/* Only the RFC-2292 API has the ability to find the interface for TCP connections,
it was removed in RFC-3542 !!!!
Fortunately, Linux kept the 2292 ABI when it moved to 3542. The following code always
uses the old ABI, and should work with pre- and post-3542 kernel headers */
#ifdef IPV6_2292PKTOPTIONS
# define PKTOPTIONS IPV6_2292PKTOPTIONS
#else
# define PKTOPTIONS IPV6_PKTOPTIONS
#endif
if (set_ipv6pktinfo(fd) &&
getsockopt(fd, IPPROTO_IPV6, PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
union {
unsigned char *c;
struct in6_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
if_index = p.p->ipi6_ifindex;
}
}
}
#endif /* IPV6 */
#endif /* Linux */
return if_index;
}
static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, int dienow)
{
+8
View File
@@ -2468,6 +2468,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (!inet_pton(AF_INET6, arg, &new->addr6))
ret_err(_("bad IPv6 address"));
for (i= 0; i < 8; i++)
if (new->addr6.s6_addr[i] != 0)
break;
/* set WILDCARD if network part all zeros */
if (i == 8)
new->flags |= CONFIG_WILDCARD;
new->flags |= CONFIG_ADDR6;
}