Compare commits

...
21 changed files with 1133 additions and 667 deletions

No files matched your search

+19 -1
View File
@@ -30,7 +30,25 @@ version 2.66
Don't send the default DHCP option advertising dnsmasq as
the local DNS server if dnsmasq is configured to not act
as DNS server, or it's configured to a non-standard port.
Add DNSMASQ_CIRCUIT_ID, DNSMASQ_SUBCRIBER_ID,
DNSMASQ_REMOTE_ID variables to the environment of the
lease-change script (and the correponding Lua). These hold
information inserted into the DHCP request by a DHCP relay
agent. Thanks to Lakefield Communications for providing a
bounty for this addition.
Fixed crash, introduced in 2.64, whilst handling DHCPv6
information-requests with some common configurations.
Thanks to Robert M. Albrecht for the bug report and
chasing the problem.
Add --ipset option. Thanks to Jason A. Donenfeld for the
patch.
Don't erroneously reject some option names in --dhcp-match
options. Thnaks to Benedikt Hochstrasser for the bug report.
version 2.65
Fix regression which broke forwarding of queries sent via
+1 -1
View File
@@ -65,7 +65,7 @@ version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h
+1 -1
View File
@@ -8,7 +8,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
netlink.c network.c option.c rfc1035.c \
rfc2131.c tftp.c util.c conntrack.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
radv.c slaac.c auth.c
radv.c slaac.c auth.c ipset.c
LOCAL_MODULE := dnsmasq
+2 -1
View File
@@ -1,8 +1,9 @@
dnsmasq (2.66-1) unstable; urgency=low
* New upstream.
* Add support for noipset in DEB_BUILD_OPTIONS.
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 14 Dec 2012 11:58:41 +0000
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 22 Feb 2013 21:52:13 +0000
dnsmasq (2.65-1) unstable; urgency=low
+1
View File
@@ -59,6 +59,7 @@ Notes on configuring dnsmasq as packaged for Debian.
noipv6 : omit IPv6 support.
nodbus : omit DBus support.
noconntrack : omit connection tracking support.
noipset : omit IPset support.
nortc : compile alternate mode suitable for systems without an RTC.
noi18n : omit translations and internationalisation support.
noidn : omit international domain name support, must be
+6
View File
@@ -33,6 +33,12 @@ ifeq ($(DEB_BUILD_ARCH_OS),linux)
endif
endif
ifeq (,$(filter noipset,$(DEB_BUILD_OPTIONS)))
ifeq ($(DEB_BUILD_ARCH_OS),linux)
COPTS += -DHAVE_IPSET
endif
endif
ifneq (,$(filter nodhcp6,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_DHCP6
endif
+4
View File
@@ -69,6 +69,10 @@
# --address (and --server) work with IPv6 addresses too.
#address=/www.thekelleys.org.uk/fe80::20d:60ff:fe36:f83
# Add the IPs of all queries to yahoo.com, google.com, and their
# subdomains to the vpn and search ipsets:
#ipset=/yahoo.com/google.com/vpn,search
# You can control how dnsmasq talks to a server: this forces
# queries to 10.1.2.3 to be routed via eth1
# server=10.1.2.3@eth1
+9
View File
@@ -412,6 +412,12 @@ additional facility that /#/ matches any domain. Thus
answered from /etc/hosts or DHCP and not sent to an upstream
nameserver by a more specific --server directive.
.TP
.B --ipset=/<domain>/[domain/]<ipset>[,<ipset>]
Places the resolved IP addresses of queries for the specified domains
in the specified netfilter ip sets. Domains and subdomains are matched
in the same way as --address. These ip sets must already exist. See
ipset(8) for more details.
.TP
.B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
Return an MX record named <mx name> pointing to the given hostname (if
given), or
@@ -1230,6 +1236,9 @@ For IPv4 only:
DNSMASQ_CLIENT_ID if the host provided a client-id.
DNSMASQ_CIRCUIT_ID, DNSMASQ_SUBSCRIBER_ID, DNSMASQ_REMOTE_ID if a
DHCP relay-agent added any of these options.
If the client provides vendor-class, DNSMASQ_VENDOR_CLASS.
For IPv6 only:
+13 -1
View File
@@ -97,6 +97,10 @@ HAVE_CONNTRACK
a build-dependency on libnetfilter_conntrack, but the resulting binary will
still run happily on a kernel without conntrack support.
HAVE_IPSET
define this to include the ability to selectively add resolved ip addresses
to given ipsets.
HAVE_AUTH
define this to include the facility to act as an authoritative DNS
server for one or more zones.
@@ -136,7 +140,7 @@ RESOLVFILE
/* #define HAVE_DBUS */
/* #define HAVE_IDN */
/* #define HAVE_CONNTRACK */
/* #define HAVE_IPSET */
/* Default locations for important system files. */
@@ -323,6 +327,10 @@ HAVE_SOCKADDR_SA_LEN
#undef HAVE_AUTH
#endif
#ifndef HAVE_LINUX_NETWORK
#undef HAVE_IPSET
#endif
/* Define a string indicating which options are in use.
DNSMASQP_COMPILE_OPTS is only defined in dnsmasq.c */
@@ -381,6 +389,10 @@ static char *compile_opts =
"no-"
#endif
"conntrack "
#ifndef HAVE_IPSET
"no-"
#endif
"ipset "
#ifndef HAVE_AUTH
"no-"
#endif
+4 -10
View File
@@ -518,8 +518,7 @@ u16 lookup_dhcp_opt(int prot, char *name)
t = opttab;
for (i = 0; t[i].name; i++)
if (!(t[i].size & OT_INTERNAL) &&
strcasecmp(t[i].name, name) == 0)
if (strcasecmp(t[i].name, name) == 0)
return t[i].val;
return 0;
@@ -539,14 +538,9 @@ u16 lookup_dhcp_len(int prot, u16 val)
for (i = 0; t[i].name; i++)
if (val == t[i].val)
{
if (t[i].size & OT_INTERNAL)
return 0;
return t[i].size & ~OT_DEC;
}
return 0;
return t[i].size & ~OT_DEC;
return 0;
}
char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
+8 -36
View File
@@ -265,8 +265,8 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct
return NULL;
}
int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
int serial, struct dhcp_netid *netids, struct in6_addr *ans)
struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
int iaid, struct dhcp_netid *netids, struct in6_addr *ans)
{
/* Find a free address: exclude anything in use and anything allocated to
a particular hwaddr/clientid/hostname in our configuration.
@@ -283,12 +283,12 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl
/* hash hwaddr: use the SDBM hashing algorithm. This works
for MAC addresses, let's see how it manages with client-ids! */
for (j = 0, i = 0; i < clid_len; i++)
for (j = iaid, i = 0; i < clid_len; i++)
j += clid[i] + (j << 6) + (j << 16) - j;
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)
if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS))
if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
continue;
else if (!match_netid(c->filter, netids, pass))
continue;
@@ -296,9 +296,9 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl
{
if (option_bool(OPT_CONSEC_ADDR))
/* seed is largest extant lease addr in this context */
start = lease_find_max_addr6(c) + serial;
start = lease_find_max_addr6(c);
else
start = addr6part(&c->start6) + ((j + c->addr_epoch + serial) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
/* iterate until we find a free address. */
addr = start;
@@ -315,7 +315,7 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl
{
*ans = c->start6;
setaddr6part (ans, addr);
return 1;
return c;
}
addr++;
@@ -325,7 +325,7 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl
} while (addr != start);
}
return 0;
}
@@ -369,34 +369,6 @@ struct dhcp_context *address6_valid(struct dhcp_context *context,
return NULL;
}
struct dhcp_context *narrow_context6(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids)
{
/* We start of with a set of possible contexts, all on the current physical interface.
These are chained on ->current.
Here we have an address, and return the actual context correponding to that
address. Note that none may fit, if the address came a dhcp-host and is outside
any dhcp-range. In that case we return a static range if possible, or failing that,
any context on the correct subnet. (If there's more than one, this is a dodgy
configuration: maybe there should be a warning.) */
struct dhcp_context *tmp;
if (!(tmp = address6_available(context, taddr, netids)) &&
!(tmp = address6_valid(context, taddr, netids)))
for (tmp = context; tmp; tmp = tmp->current)
if (match_netid(tmp->filter, netids, 1) &&
is_same_net6(taddr, &tmp->start6, tmp->prefix))
break;
/* Only one context allowed now */
if (tmp)
tmp->current = NULL;
return tmp;
}
static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)
{
/* expand wildcard on contructed contexts */
+5
View File
@@ -213,6 +213,11 @@ int main (int argc, char **argv)
#endif
#ifdef HAVE_IPSET
if (daemon->ipsets)
ipset_init();
#endif
#ifdef HAVE_LINUX_NETWORK
netlink_init();
+28 -7
View File
@@ -430,6 +430,12 @@ struct server {
struct server *next;
};
struct ipsets {
char **sets;
char *domain;
struct ipsets *next;
};
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
@@ -675,6 +681,14 @@ struct cond_domain {
struct cond_domain *next;
};
#ifdef OPTION6_PREFIX_CLASS
struct prefix_class {
int class;
struct dhcp_netid netid;
struct prefix_class *next;
};
#endif
struct dhcp_context {
unsigned int lease_time, addr_epoch;
struct in_addr netmask, broadcast;
@@ -708,6 +722,7 @@ struct dhcp_context {
#define CONTEXT_GC 4096
#define CONTEXT_RA 8192
#define CONTEXT_WILDCARD 16384
#define CONTEXT_USED 32768
struct ping_result {
struct in_addr addr;
@@ -779,6 +794,7 @@ extern struct daemon {
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers;
struct bogus_addr *bogus_addr;
struct server *servers;
struct ipsets *ipsets;
int log_fac; /* log facility */
char *log_file; /* optional log file */
int max_logs; /* queue limit */
@@ -903,7 +919,8 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
struct all_addr *addrp, unsigned int flags,
unsigned long local_ttl);
int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
time_t now, int is_sign, int checkrebind, int checking_disabled);
time_t now, char **ipsets, int is_sign, int checkrebind,
int checking_disabled);
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask, time_t now);
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
@@ -1047,7 +1064,8 @@ struct dhcp_lease *lease4_allocate(struct in_addr addr);
struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type);
struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len,
int lease_type, int iaid, struct in6_addr *addr);
void lease6_filter(int lease_type, int iaid, struct dhcp_context *context);
void lease6_reset(void);
struct dhcp_lease *lease6_find_by_client(struct dhcp_lease *first, int lease_type, unsigned char *clid, int clid_len, int iaid);
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr);
u64 lease_find_max_addr6(struct dhcp_context *context);
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
@@ -1117,6 +1135,12 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
# endif
#endif
/* ipset.c */
#ifdef HAVE_IPSET
void ipset_init(void);
int add_to_ipset(const char *setname, const struct all_addr *ipaddr, int flags, int remove);
#endif
/* helper.c */
#if defined(HAVE_SCRIPT)
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
@@ -1146,8 +1170,8 @@ int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr,
#ifdef HAVE_DHCP6
void dhcp6_init(void);
void dhcp6_packet(time_t now);
int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
int serial, struct dhcp_netid *netids, struct in6_addr *ans);
struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
int iaid, struct dhcp_netid *netids, struct in6_addr *ans);
int is_addr_in_context6(struct dhcp_context *context, struct in6_addr *addr);
struct dhcp_context *address6_available(struct dhcp_context *context,
struct in6_addr *taddr,
@@ -1155,9 +1179,6 @@ struct dhcp_context *address6_available(struct dhcp_context *context,
struct dhcp_context *address6_valid(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids);
struct dhcp_context *narrow_context6(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids);
struct dhcp_config *find_config6(struct dhcp_config *configs,
struct dhcp_context *context,
unsigned char *duid, int duid_len,
+20 -1
View File
@@ -439,9 +439,28 @@ static size_t process_reply(struct dns_header *header, time_t now,
struct server *server, size_t n, int check_rebind, int checking_disabled)
{
unsigned char *pheader, *sizep;
char **sets = 0;
int munged = 0, is_sign;
size_t plen;
#ifdef HAVE_IPSET
/* Similar algorithm to search_servers. */
struct ipsets *ipset_pos;
unsigned int namelen = strlen(daemon->namebuff);
unsigned int matchlen = 0;
for (ipset_pos = daemon->ipsets; ipset_pos; ipset_pos = ipset_pos->next)
{
unsigned int domainlen = strlen(ipset_pos->domain);
char *matchstart = daemon->namebuff + namelen - domainlen;
if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) &&
(domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) &&
domainlen >= matchlen) {
matchlen = domainlen;
sets = ipset_pos->sets;
}
}
#endif
/* If upstream is advertising a larger UDP packet size
than we allow, trim it so that we don't get overlarge
requests for the client. We can't do this for signed packets. */
@@ -494,7 +513,7 @@ static size_t process_reply(struct dns_header *header, time_t now,
SET_RCODE(header, NOERROR);
}
if (extract_addresses(header, n, daemon->namebuff, now, is_sign, check_rebind, checking_disabled))
if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, checking_disabled))
{
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
+9 -8
View File
@@ -229,13 +229,6 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
}
}
/* expiry or length into dhcp_buff2 */
#ifdef HAVE_BROKEN_RTC
sprintf(daemon->dhcp_buff2, "%u", data.length);
#else
sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
#endif
/* supplied data may just exceed normal buffer (unlikely) */
if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
!(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
@@ -397,6 +390,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
buf = grab_extradata_lua(buf, end, "cpewan_oui");
buf = grab_extradata_lua(buf, end, "cpewan_serial");
buf = grab_extradata_lua(buf, end, "cpewan_class");
buf = grab_extradata_lua(buf, end, "circuit_id");
buf = grab_extradata_lua(buf, end, "subscriber_id");
buf = grab_extradata_lua(buf, end, "remote_id");
}
buf = grab_extradata_lua(buf, end, "tags");
@@ -493,8 +489,10 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
#ifdef HAVE_BROKEN_RTC
sprintf(daemon->dhcp_buff2, "%u", data.length);
my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
#else
sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
#endif
@@ -528,10 +526,13 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
}
buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
if (is6)
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
else if (data.giaddr.s_addr != 0)
+205
View File
@@ -0,0 +1,205 @@
/* ipset.c is Copyright (c) 2013 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_IPSET
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <arpa/inet.h>
#include <linux/version.h>
#include <linux/netlink.h>
#include <linux/netfilter/nfnetlink.h>
#ifndef NFNL_SUBSYS_IPSET
#define NFNL_SUBSYS_IPSET 6
#define IPSET_ATTR_DATA 7
#define IPSET_ATTR_IP 1
#define IPSET_ATTR_IPADDR_IPV4 1
#define IPSET_ATTR_IPADDR_IPV6 2
#define IPSET_ATTR_PROTOCOL 1
#define IPSET_ATTR_SETNAME 2
#define IPSET_CMD_ADD 9
#define IPSET_CMD_DEL 10
#define IPSET_MAXNAMELEN 32
#define IPSET_PROTOCOL 6
#else
#include <linux/netfilter/ipset/ip_set.h>
#endif
/* data structure size in here is fixed */
#define BUFF_SZ 256
#define NL_ALIGN(len) (((len)+3) & ~(3))
static const struct sockaddr_nl snl = { .nl_family = AF_NETLINK };
static int ipset_sock, old_kernel;
static char *buffer;
static inline void add_attr(struct nlmsghdr *nlh, uint16_t type, size_t len, const void *data)
{
struct nlattr *attr = (void *)nlh + NL_ALIGN(nlh->nlmsg_len);
uint16_t payload_len = NL_ALIGN(sizeof(struct nlattr)) + len;
attr->nla_type = type;
attr->nla_len = payload_len;
memcpy((void *)attr + NL_ALIGN(sizeof(struct nlattr)), data, len);
nlh->nlmsg_len += NL_ALIGN(payload_len);
}
void ipset_init(void)
{
struct utsname utsname;
int version;
char *split;
if (uname(&utsname) < 0)
die(_("failed to find kernel version: %s"), NULL, EC_MISC);
split = strtok(utsname.release, ".");
version = (split ? atoi(split) : 0);
split = strtok(NULL, ".");
version = version * 256 + (split ? atoi(split) : 0);
split = strtok(NULL, ".");
version = version * 256 + (split ? atoi(split) : 0);
old_kernel = (version < KERNEL_VERSION(2,6,32));
if (old_kernel && (ipset_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) != -1)
return;
if (!old_kernel &&
(buffer = safe_malloc(BUFF_SZ)) &&
(ipset_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER)) != -1 &&
(bind(ipset_sock, (struct sockaddr *)&snl, sizeof(snl)) != -1))
return;
die (_("failed to create IPset control socket: %s"), NULL, EC_MISC);
}
static int new_add_to_ipset(const char *setname, const struct all_addr *ipaddr, int af, int remove)
{
struct nlmsghdr *nlh;
struct nfgenmsg *nfg;
struct nlattr *nested[2];
uint8_t proto;
int addrsz = INADDRSZ;
ssize_t rc;
#ifdef HAVE_IPV6
if (af == AF_INET6)
addrsz = IN6ADDRSZ;
#endif
if (strlen(setname) >= IPSET_MAXNAMELEN)
{
errno = ENAMETOOLONG;
return -1;
}
memset(buffer, 0, sizeof(buffer));
nlh = (struct nlmsghdr *)buffer;
nlh->nlmsg_len = NL_ALIGN(sizeof(struct nlmsghdr));
nlh->nlmsg_type = (remove ? IPSET_CMD_DEL : IPSET_CMD_ADD) | (NFNL_SUBSYS_IPSET << 8);
nlh->nlmsg_flags = NLM_F_REQUEST;
nfg = (struct nfgenmsg *)(buffer + nlh->nlmsg_len);
nlh->nlmsg_len += NL_ALIGN(sizeof(struct nfgenmsg));
nfg->nfgen_family = af;
nfg->version = NFNETLINK_V0;
nfg->res_id = htons(0);
proto = IPSET_PROTOCOL;
add_attr(nlh, IPSET_ATTR_PROTOCOL, sizeof(proto), &proto);
add_attr(nlh, IPSET_ATTR_SETNAME, strlen(setname) + 1, setname);
nested[0] = (struct nlattr *)(buffer + NL_ALIGN(nlh->nlmsg_len));
nlh->nlmsg_len += NL_ALIGN(sizeof(struct nlattr));
nested[0]->nla_type = NLA_F_NESTED | IPSET_ATTR_DATA;
nested[1] = (struct nlattr *)(buffer + NL_ALIGN(nlh->nlmsg_len));
nlh->nlmsg_len += NL_ALIGN(sizeof(struct nlattr));
nested[1]->nla_type = NLA_F_NESTED | IPSET_ATTR_IP;
add_attr(nlh,
(af == AF_INET ? IPSET_ATTR_IPADDR_IPV4 : IPSET_ATTR_IPADDR_IPV6) | NLA_F_NET_BYTEORDER,
addrsz, &ipaddr->addr);
nested[1]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[1];
nested[0]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[0];
while ((rc = sendto(ipset_sock, buffer, nlh->nlmsg_len, 0,
(struct sockaddr *)&snl, sizeof(snl))) == -1 && retry_send());
return rc;
}
static int old_add_to_ipset(const char *setname, const struct all_addr *ipaddr, int remove)
{
socklen_t size;
struct ip_set_req_adt_get {
unsigned op;
unsigned version;
union {
char name[IPSET_MAXNAMELEN];
uint16_t index;
} set;
char typename[IPSET_MAXNAMELEN];
} req_adt_get;
struct ip_set_req_adt {
unsigned op;
uint16_t index;
uint32_t ip;
} req_adt;
if (strlen(setname) >= sizeof(req_adt_get.set.name))
{
errno = ENAMETOOLONG;
return -1;
}
req_adt_get.op = 0x10;
req_adt_get.version = 3;
strcpy(req_adt_get.set.name, setname);
size = sizeof(req_adt_get);
if (getsockopt(ipset_sock, SOL_IP, 83, &req_adt_get, &size) < 0)
return -1;
req_adt.op = remove ? 0x102 : 0x101;
req_adt.index = req_adt_get.set.index;
req_adt.ip = ntohl(ipaddr->addr.addr4.s_addr);
if (setsockopt(ipset_sock, SOL_IP, 83, &req_adt, sizeof(req_adt)) < 0)
return -1;
return 0;
}
int add_to_ipset(const char *setname, const struct all_addr *ipaddr, int flags, int remove)
{
int af = AF_INET;
#ifdef HAVE_IPV6
if (flags & F_IPV6)
{
af = AF_INET6;
/* old method only supports IPv4 */
if (old_kernel)
return -1;
}
#endif
return old_kernel ? old_add_to_ipset(setname, ipaddr, remove) : new_add_to_ipset(setname, ipaddr, af, remove);
}
#endif
+28 -17
View File
@@ -555,8 +555,7 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
}
#ifdef HAVE_DHCP6
/* addr or clid may be NULL for "don't care, both NULL resets "USED" flags both
set activates USED check */
/* find address for {CLID, IAID, address} */
struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len,
int lease_type, int iaid, struct in6_addr *addr)
{
@@ -567,40 +566,52 @@ struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len,
if (!(lease->flags & lease_type) || lease->hwaddr_type != iaid)
continue;
if (clid && addr && (lease->flags & LEASE_USED))
if (memcmp(lease->hwaddr, addr, IN6ADDRSZ) != 0)
continue;
if (addr && memcmp(lease->hwaddr, addr, IN6ADDRSZ) != 0)
continue;
if (clid &&
(clid_len != lease->clid_len ||
if ((clid_len != lease->clid_len ||
memcmp(clid, lease->clid, clid_len) != 0))
continue;
lease->flags |= LEASE_USED;
return lease;
}
return NULL;
}
void lease6_filter(int lease_type, int iaid, struct dhcp_context *context)
/* reset "USED flags */
void lease6_reset(void)
{
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
lease->flags &= ~LEASE_USED;
}
/* enumerate all leases belonging to {CLID, IAID} */
struct dhcp_lease *lease6_find_by_client(struct dhcp_lease *first, int lease_type, unsigned char *clid, int clid_len, int iaid)
{
struct dhcp_lease *lease;
if (!first)
first = leases;
for (lease = first; lease; lease = lease->next)
{
/* reset "USED flag */
lease->flags &= ~LEASE_USED;
if (lease->flags & LEASE_USED)
continue;
if (!(lease->flags & lease_type) || lease->hwaddr_type != iaid)
continue;
/* leases on the wrong interface get filtered out here */
if (!is_addr_in_context6(context, (struct in6_addr *)&lease->hwaddr))
lease->flags |= LEASE_USED;
if ((clid_len != lease->clid_len ||
memcmp(clid, lease->clid, clid_len) != 0))
continue;
return lease;
}
return NULL;
}
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr)
+75
View File
@@ -127,6 +127,7 @@ struct myoption {
#define LOPT_AUTHSOA 316
#define LOPT_AUTHSFS 317
#define LOPT_AUTHPEER 318
#define LOPT_IPSET 319
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -259,6 +260,7 @@ static const struct myoption opts[] =
{ "auth-soa", 1, 0, LOPT_AUTHSOA },
{ "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
{ "auth-peer", 1, 0, LOPT_AUTHPEER },
{ "ipset", 1, 0, LOPT_IPSET },
{ NULL, 0, 0, 0 }
};
@@ -397,6 +399,7 @@ static struct {
{ LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritive zone information"), NULL },
{ LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
{ LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -765,6 +768,8 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
new->opt = lookup_dhcp_opt(AF_INET, arg+7);
opt_len = lookup_dhcp_len(AF_INET, new->opt);
/* option:<optname> must follow tag and vendor string. */
if ((opt_len & OT_INTERNAL) && flags != DHOPT_MATCH)
new->opt = 0;
break;
}
#ifdef HAVE_DHCP6
@@ -783,6 +788,8 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
{
new->opt = lookup_dhcp_opt(AF_INET6, arg+8);
opt_len = lookup_dhcp_len(AF_INET6, new->opt);
if ((opt_len & OT_INTERNAL) && flags != DHOPT_MATCH)
new->opt = 0;
}
/* option6:<opt>|<optname> must follow tag and vendor string. */
is6 = 1;
@@ -2021,6 +2028,74 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->servers = newlist;
break;
}
case LOPT_IPSET: /* --ipset */
#ifndef HAVE_IPSET
ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
break;
#else
{
struct ipsets ipsets_head;
struct ipsets *ipsets = &ipsets_head;
int size;
char *end;
char **sets, **sets_pos;
memset(ipsets, 0, sizeof(struct ipsets));
unhide_metas(arg);
if (arg && *arg == '/')
{
arg++;
while ((end = split_chr(arg, '/')))
{
char *domain = NULL;
/* elide leading dots - they are implied in the search algorithm */
while (*arg == '.')
arg++;
/* # matches everything and becomes a zero length domain string */
if (strcmp(arg, "#") == 0 || !*arg)
domain = "";
else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
option = '?';
ipsets->next = opt_malloc(sizeof(struct ipsets));
ipsets = ipsets->next;
memset(ipsets, 0, sizeof(struct ipsets));
ipsets->domain = domain;
arg = end;
}
}
else
{
ipsets->next = opt_malloc(sizeof(struct ipsets));
ipsets = ipsets->next;
memset(ipsets, 0, sizeof(struct ipsets));
ipsets->domain = "";
}
if (!arg || !*arg)
{
option = '?';
break;
}
size = 2;
for (end = arg; *end; ++end)
if (*end == ',')
++size;
sets = sets_pos = opt_malloc(sizeof(char *) * size);
do {
end = split(arg);
*sets_pos++ = opt_string_alloc(arg);
arg = end;
} while (end);
*sets_pos = 0;
for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
ipsets->next->sets = sets;
ipsets->next = daemon->ipsets;
daemon->ipsets = ipsets_head.next;
break;
}
#endif
case 'c': /* --cache-size */
{
+16 -2
View File
@@ -777,13 +777,18 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
expired and cleaned out that way.
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
int is_sign, int check_rebind, int checking_disabled)
char **ipsets, int is_sign, int check_rebind, int checking_disabled)
{
unsigned char *p, *p1, *endrr, *namep;
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
unsigned long ttl = 0;
struct all_addr addr;
#ifdef HAVE_IPSET
char **ipsets_cur;
#else
(void)ipsets; /* unused */
#endif
cache_start_insert();
/* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
@@ -966,6 +971,15 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
(flags & F_IPV4) &&
private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
return 1;
#ifdef HAVE_IPSET
if (ipsets && (flags & (F_IPV4 | F_IPV6)))
{
ipsets_cur = ipsets;
while (*ipsets_cur)
add_to_ipset(*ipsets_cur++, &addr, flags, 0);
}
#endif
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
if (newc && cpp)
+14 -1
View File
@@ -1256,7 +1256,20 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
add_extradata_opt(lease, oui);
add_extradata_opt(lease, serial);
add_extradata_opt(lease, class);
if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
{
add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
}
else
{
add_extradata_opt(lease, NULL);
add_extradata_opt(lease, NULL);
add_extradata_opt(lease, NULL);
}
/* space-concat tag set */
if (!tagif_netid)
add_extradata_opt(lease, NULL);
+665 -580
View File
File diff suppressed because it is too large. Load diff