Compare commits

...
13 Commits
Author SHA1 Message Date
Simon Kelley c91c66ee63 Get destination address of received DHCPv4 packets right in dumpfile. 2025-07-27 16:23:59 +01:00
Simon Kelley fc9f6985ab Add --dhcp-split-relay option.
This makes a DHCPv4 relay which is functional when
client and server networks aren't mutually route-able.
2025-07-27 15:27:40 +01:00
Simon Kelley ea5b0e64e2 Make DHCP6ADVERTISE messages RFC 8415 compliant.
Include IA_* options even if no addresses are allocated, and
always return options specified in ORO.
2025-07-23 21:05:40 +01:00
Simon Kelley e775264539 Fix wrong status in reply to DHCP6REQUEST.
If we jump to label request_no_address, address_assigned
has an undefined value, and maybe the wrong status is returned.
2025-07-23 21:04:06 +01:00
Simon Kelley d976d94e3d Implement RFC 6221 para 4.
If the link address in a relayed DHCPv6 message is zero, don't
use it for addresss selection.
2025-07-22 22:09:49 +01:00
Simon Kelley 3034746748 Allow --dhcp-authoritative to control recreation of missing leases in DHCPv6.
Change the behaviour of the DHVPv6 server when a REBIND message
is received but no lease exists. Under these circumstances a new
lease is created _only_ when the --dhcp-authoritative option is
set. This matches the behavior of the DHCPv4 server.
2025-07-22 21:51:47 +01:00
Simon Kelley 3ceea9e755 Fix packet validation in DHCPv6.
Some messages must be sent as multicast.
2025-07-22 20:43:52 +01:00
Geert Stappers dfcef19fb4 Added a tail to .hmtl files
HTML-files `doc.html` and `setup.html` were missing closing tags.
Also some empty lines removed.

Signed-off-by: Geert Stappers <stappers@stappers.nl>
2025-07-20 15:29:43 +01:00
Simon Kelley f74b74e521 Refactor relay_reply6().
Traverse the nested data structures more efficiently,
more understandably, and with watertight checking of
buffer boundaries.
2025-07-15 15:17:46 +01:00
Simon Kelley c9342cb556 Check for constructed name in --srv-host exceeding length limit.
Thanks to Mikhail Dmitrichenko for spotting this.
2025-07-15 15:15:32 +01:00
Simon Kelley 2b844b8c83 Fix BNF from --srv-host in manpage. 2025-07-14 15:15:26 +01:00
Simon Kelley 65e9a8957c Fix order of reads dhcp-hostsfile and /etc/ethers.
Reading /etc/ethers assumes that dhcp-host cofig has already
been read, and that is the case for dhcp-host, but for unknown
reasons, the analogous congiguration is dhcp-hostfile was being
read after /etc/ethers.

Swap the order.

Thanks to Andreas Kuropka for spotting the problem.
2025-07-10 10:58:14 +01:00
Simon Kelley da868a2fbe Restore bit-rotted warning about non-recursive upstreams. 2025-07-08 16:18:03 +01:00
14 changed files with 393 additions and 238 deletions

No files matched your search

+9
View File
@@ -61,6 +61,15 @@ version 2.92
Add TFTP options windowsize (RFC 7440) and timeout (RFC 2349).
Change the behaviour of the DHVPv6 server when a REBIND message
is received but no lease exists. Under these circumstances a new
lease is created _only_ when the --dhcp-authoritative option is
set. This matches the behavior of the DHCPv4 server.
Add --dhcp-split-relay option. This makes a DHCPv4 relay which
is functional when client and server networks aren't mutually
route-able.
version 2.91
Fix spurious "resource limit exceeded messages". Thanks to
+1 -3
View File
@@ -95,7 +95,5 @@ a contribution towards my expenses, please use the donation button below.
<input type="image" src="https://www.paypalobjects.com/en_US/GB/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal – The safer, easier way to pay online.">
<img alt="" border="0" src="https://www.paypalobjects.com/en_GB/i/scr/pixel.gif" width="1" height="1">
</form>
</BODY>
</HTML>
+26 -3
View File
@@ -673,7 +673,7 @@ machine on which dnsmasq is running) for each
local machine. Local machines are those in /etc/hosts or with DHCP
leases.
.TP
.B \-W, --srv-host=<_service>.<_prot>.[<domain>],[<target>[,<port>[,<priority>[,<weight>]]]]
.B \-W, --srv-host=<_service>.<_prot>[.<domain>],[<target>[,<port>[,<priority>[,<weight>]]]]
Return a SRV DNS record. See RFC2782 for details. If not supplied, the
domain defaults to that given by
.B --domain.
@@ -1487,7 +1487,28 @@ DHCPv4 to a DHCPv6 server or vice-versa.
The DHCP relay function for IPv6 includes the ability to snoop
prefix-delegation from relayed DHCP transactions. See
.B --dhcp-script
for details.
for details.
.TP
.B --dhcp-split-relay=<local address>,[<server address>[#<server port>]],<server-facing-interface>
A usefully enchanced version of DHCPv4 relay. IPv4 DHCP normally uses a single address
for two functions; it is used by the DHCP server to determine which network to allocate
an address on, and it is used as the address of the relay to which the server sends packets.
This version of DHCP relay splits these functions. It uses the address of the server-facing relay
interface as the address that the server talks to. The address of the client-facing interface
(the first item in the config) is used as to determine the client's subnet. The
local address is also used as server-ID override so that the client always sends requests
via the relay. The effect of this is that server doesn't require
a route to the client network and the clients don't require a route to the server.
The interface parameter is mandatory and a cannot be a wildcard.
If setting up a network where the client networks have limited routing, be careful
about configuring the DHCP server. Dnsmasq, as DHCP server, will set the default route to the
client-facing relay interface unless explicitly configured: that is a sensible default.
The normal default DNS server (the same address as the DHCP server)
will not be appropriate when there is no route bewteen the
two, so this will have to be explicitly configured.
.TP
.B \-U, --dhcp-vendorclass=set:<tag>,[enterprise:<IANA-enterprise number>,]<vendor-class>
Map from a vendor-class string to a tag. Most DHCP clients provide a
@@ -1739,7 +1760,9 @@ For DHCPv4, it changes the behaviour from strict RFC compliance so that DHCP req
unknown leases from unknown hosts are not ignored. This allows new hosts
to get a lease without a tedious timeout under all circumstances. It also
allows dnsmasq to rebuild its lease database without each client needing to
reacquire a lease, if the database is lost. For DHCPv6 it sets the
reacquire a lease, if the database is lost. For DHCPv6 it controls the same
behaviour as DHCPv4 with missing leases (except for the RFC uncompliance - the
DHCPv6 RFCs allow this behaviour if configured). It also sets the
priority in replies to 255 (the maximum) instead of 0 (the minimum).
.TP
.B --dhcp-rapid-commit
+2
View File
@@ -229,3 +229,5 @@ contents of the cache to the syslog.
<P>For a complete listing of options please take a look at the manpage
dnsmasq(8).
</BODY>
</HTML>
+3 -1
View File
@@ -1067,10 +1067,12 @@ void log_relay(int family, struct dhcp_relay *relay)
{
if (broadcast)
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s via %s"), daemon->addrbuff, relay->interface);
else if (relay->split_mode)
my_syslog(MS_DHCP | LOG_INFO, _("DHCP split-relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
else
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
}
else
else
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff);
}
+1
View File
@@ -73,6 +73,7 @@
#define SUBOPT_REMOTE_ID 2
#define SUBOPT_SUBNET_SELECT 5 /* RFC 3527 */
#define SUBOPT_SUBSCR_ID 6 /* RFC 3393 */
#define SUBOPT_FLAGS 10 /* RFC 5010 */
#define SUBOPT_SERVER_OR 11 /* RFC 5107 */
#define SUBOPT_PXE_BOOT_ITEM 71 /* PXE standard */
+25 -127
View File
@@ -32,8 +32,6 @@ static int complete_context(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
static void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz);
static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
static int make_fd(int port)
{
@@ -151,6 +149,7 @@ void dhcp_packet(time_t now, int pxe_fd)
#ifdef HAVE_LINUX_NETWORK
struct arpreq arp_req;
struct timeval tv;
struct in_addr dst_addr;
#endif
union {
@@ -176,13 +175,11 @@ void dhcp_packet(time_t now, int pxe_fd)
(sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
return;
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, NULL, fd);
#endif
#if defined (HAVE_LINUX_NETWORK)
if (ioctl(fd, SIOCGSTAMP, &tv) == 0)
recvtime = tv.tv_sec;
dst_addr.s_addr = 0;
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
@@ -194,7 +191,8 @@ void dhcp_packet(time_t now, int pxe_fd)
} p;
p.c = CMSG_DATA(cmptr);
iface_index = p.p->ipi_ifindex;
if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
dst_addr = p.p->ipi_addr;
if (dst_addr.s_addr != INADDR_BROADCAST)
unicast_dest = 1;
}
@@ -224,7 +222,25 @@ void dhcp_packet(time_t now, int pxe_fd)
iface_index = *(p.i);
}
#endif
#ifdef HAVE_DUMPFILE
union mysockaddr *sockp = NULL;
# ifdef HAVE_LINUX_NETWORK
union mysockaddr tosock;
sockp = &tosock;
tosock.in.sin_port = htons(daemon->dhcp_server_port);
tosock.in.sin_addr = dst_addr;
tosock.sa.sa_family = AF_INET;
# ifdef HAVE_SOCKADDR_SA_LEN
tosock.in.sin_len = sizeof(struct sockaddr_in);
# endif
# endif
dump_packet_udp(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, sockp, -1);
#endif
if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name) ||
ioctl(daemon->dhcpfd, SIOCGIFFLAGS, &ifr) != 0)
return;
@@ -344,7 +360,7 @@ void dhcp_packet(time_t now, int pxe_fd)
if (!iface_enumerate(AF_INET, &parm, (callback_t){.af_inet=complete_context}))
return;
relay_upstream4(iface_index, mess, (size_t)sz);
relay_upstream4(iface_index, mess, (size_t)sz, unicast_dest);
/* May have configured relay, but not DHCP server */
if (!daemon->dhcp)
@@ -1091,122 +1107,4 @@ char *host_from_dns(struct in_addr addr)
return NULL;
}
static void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz)
{
struct in_addr giaddr = mess->giaddr;
u8 hops = mess->hops;
struct dhcp_relay *relay;
if (mess->op != BOOTREQUEST)
return;
for (relay = daemon->relay4; relay; relay = relay->next)
if (relay->iface_index != 0 && relay->iface_index == iface_index)
break;
/* No relay config. */
if (!relay)
return;
for (; relay; relay = relay->next)
if (relay->iface_index != 0 && relay->iface_index == iface_index)
{
union mysockaddr to;
union all_addr from;
mess->hops = hops;
mess->giaddr = giaddr;
if ((mess->hops++) > 20)
continue;
/* source address == relay address */
from.addr4 = relay->local.addr4;
/* already gatewayed ? */
if (giaddr.s_addr)
{
/* if so check if by us, to stomp on loops. */
if (giaddr.s_addr == relay->local.addr4.s_addr)
continue;
}
else
{
/* plug in our address */
mess->giaddr.s_addr = relay->local.addr4.s_addr;
}
to.sa.sa_family = AF_INET;
to.in.sin_addr = relay->server.addr4;
to.in.sin_port = htons(relay->port);
#ifdef HAVE_SOCKADDR_SA_LEN
to.in.sin_len = sizeof(struct sockaddr_in);
#endif
/* Broadcasting to server. */
if (relay->server.addr4.s_addr == 0)
{
struct ifreq ifr;
if (relay->interface)
safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
if (!relay->interface || strchr(relay->interface, '*') ||
ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1)
{
my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface);
continue;
}
to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
#ifdef HAVE_DUMPFILE
{
union mysockaddr fromsock;
fromsock.in.sin_port = htons(daemon->dhcp_server_port);
fromsock.in.sin_addr = from.addr4;
fromsock.sa.sa_family = AF_INET;
dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1);
}
#endif
send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
if (option_bool(OPT_LOG_OPTS))
{
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
if (relay->server.addr4.s_addr == 0)
snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface);
else
inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
}
}
/* restore in case of a local reply. */
mess->giaddr = giaddr;
}
static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
{
struct dhcp_relay *relay;
if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
return NULL;
for (relay = daemon->relay4; relay; relay = relay->next)
{
if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
{
if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
return relay->iface_index != 0 ? relay : NULL;
}
}
return NULL;
}
#endif
+15 -7
View File
@@ -176,6 +176,7 @@ void dhcp6_packet(time_t now)
else
{
struct dhcp_bridge *bridge, *alias;
int multicast_dest = 0;
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
@@ -231,13 +232,20 @@ void dhcp6_packet(time_t now)
memset(&context->local6, 0, IN6ADDRSZ);
}
/* Ignore requests sent to the ALL_SERVERS multicast address for relay when
we're listening there for DHCPv6 server reasons. */
inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &all_servers);
if (IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
multicast_dest = 1;
if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) &&
relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
return;
inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
if (IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
multicast_dest = 1;
else
{
/* Ignore requests sent to the ALL_SERVERS multicast address for relay when
we're listening there for DHCPv6 server reasons. */
if (relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
return;
}
if (!iface_enumerate(AF_INET6, &parm, (callback_t){.af_inet6=complete_context6}))
return;
@@ -266,7 +274,7 @@ void dhcp6_packet(time_t now)
lease_prune(NULL, now); /* lose any expired leases */
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
port = dhcp6_reply(parm.current, multicast_dest, if_index, ifr.ifr_name, &parm.fallback,
&parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
/* The port in the source address of the original request should
+1 -1
View File
@@ -1777,9 +1777,9 @@ void clear_cache_and_reload(time_t now)
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6)
{
reread_dhcp();
if (option_bool(OPT_ETHERS))
dhcp_read_ethers();
reread_dhcp();
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
+4 -1
View File
@@ -1148,6 +1148,7 @@ struct dhcp_relay {
char *interface; /* Allowable interface for replies from server, and dest for IPv6 multicast */
int iface_index; /* working - interface in which requests arrived, for return */
int port; /* Port of relay we forward to. */
int split_mode; /* Split address allocation and relay address. */
#ifdef HAVE_SCRIPT
struct snoop_record {
struct in6_addr client, prefix;
@@ -1666,6 +1667,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
time_t recvtime, struct in_addr leasequery_source);
unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
int clid_len, unsigned char *clid, int *len_out);
void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz, int unicast);
struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
#endif
/* dnsmasq.c */
@@ -1801,7 +1804,7 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
/* rfc3315.c */
#ifdef HAVE_DHCP6
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int interface, char *iface_name,
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
size_t sz, struct in6_addr *client_addr, time_t now);
int relay_upstream6(int iface_index, ssize_t sz, struct in6_addr *peer_address,
+10 -10
View File
@@ -763,6 +763,16 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
header->hb4 &= ~HB4_AD;
/* Complain loudly if the upstream server is non-recursive. */
if (!(header->hb4 & HB4_RA) && rcode == NOERROR &&
server && !(server->flags & SERV_WARNED_RECURSIVE))
{
(void)prettyprint_addr(&server->addr, daemon->namebuff);
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
if (!option_bool(OPT_LOG))
server->flags |= SERV_WARNED_RECURSIVE;
}
header->hb4 |= HB4_RA; /* recursion if available */
if (OPCODE(header) != QUERY)
@@ -778,16 +788,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
return resize_packet(header, n, pheader, plen);
}
/* Complain loudly if the upstream server is non-recursive. */
if (!(header->hb4 & HB4_RA) && rcode == NOERROR &&
server && !(server->flags & SERV_WARNED_RECURSIVE))
{
(void)prettyprint_addr(&server->addr, daemon->namebuff);
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
if (!option_bool(OPT_LOG))
server->flags |= SERV_WARNED_RECURSIVE;
}
if (header->hb3 & HB3_TC)
log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
else if (!bogusanswer || (header->hb4 & HB4_CD))
+20 -3
View File
@@ -196,6 +196,7 @@ struct myoption {
#define LOPT_NO_ENCODE 387
#define LOPT_DO_ENCODE 388
#define LOPT_LEASEQUERY 389
#define LOPT_SPLIT_RELAY 390
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -374,6 +375,7 @@ static const struct myoption opts[] =
{ "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
{ "dnssec-limits", 1, 0, LOPT_DNSSEC_LIMITS },
{ "dhcp-relay", 1, 0, LOPT_RELAY },
{ "dhcp-split-relay", 1, 0, LOPT_SPLIT_RELAY },
{ "ra-param", 1, 0, LOPT_RA_PARAM },
{ "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
{ "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
@@ -542,6 +544,7 @@ static struct {
{ LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
{ LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
{ LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<iface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
{ LOPT_SPLIT_RELAY, ARG_DUP, "<local-addr>,<server>,<iface>", gettext_noop("Relay DHCP requests to a remote server"), NULL},
{ LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
{ LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
{ LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
@@ -4716,11 +4719,21 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break;
case LOPT_RELAY: /* --dhcp-relay */
case LOPT_SPLIT_RELAY: /* --dhcp-splt-relay */
{
struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
char *two = split(arg);
char *three = split(two);
if (option == LOPT_SPLIT_RELAY)
{
new->split_mode = 1;
/* split mode must have two addresses and a non-wildcard interface name. */
if (!three || strchr(three, '*'))
two = NULL;
}
new->iface_index = 0;
if (two)
@@ -4748,7 +4761,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->relay4 = new;
}
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, arg, &new->local))
else if (inet_pton(AF_INET6, arg, &new->local) && !new->split_mode)
{
char *hash = split_chr(two, '#');
@@ -4769,7 +4782,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->relay6 = new;
}
#endif
else
two = NULL;
new->interface = opt_string_alloc(three);
}
@@ -6262,6 +6277,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
strchr(srv->name, '.') &&
strchr(srv->name, '.') == strrchr(srv->name, '.'))
{
if (strlen(srv->name) + 1 + strlen(daemon->domain_suffix) > MAXDNAME)
die(_("srv-host name %s too long after domain appended"), srv->name, EC_MISC);
strcpy(buff, srv->name);
strcat(buff, ".");
strcat(buff, daemon->domain_suffix);
+195 -1
View File
@@ -204,6 +204,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
memcpy(agent_id, opt, total);
}
/* look for RFC5010 flags sub-option */
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_FLAGS, INADDRSZ)))
unicast_dest = !!(option_uint(opt, 0, 1) & 0x80);
/* look for RFC3527 Link selection sub-option */
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBNET_SELECT, INADDRSZ)))
subnet_addr = option_addr(sopt);
@@ -289,6 +293,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{
addr = subnet_addr;
force = 1;
if (mess->giaddr.s_addr)
via_relay = 1;
}
else if (mess->giaddr.s_addr)
{
@@ -357,7 +363,12 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (context_tmp->local.s_addr == 0)
context_tmp->local = fallback;
if (context_tmp->router.s_addr == 0 && !share)
context_tmp->router = mess->giaddr;
{
if (override.s_addr)
context_tmp->router = override;
else
context_tmp->router = mess->giaddr;
}
/* fill in missing broadcast addresses for relayed ranges */
if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
@@ -3044,4 +3055,187 @@ static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
}
}
void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz, int unicast)
{
struct in_addr giaddr = mess->giaddr;
u8 hops = mess->hops;
struct dhcp_relay *relay;
size_t orig_sz = sz;
unsigned char *endopt = NULL;
if (mess->op != BOOTREQUEST)
return;
for (relay = daemon->relay4; relay; relay = relay->next)
if (relay->iface_index != 0 && relay->iface_index == iface_index)
{
union mysockaddr to;
union all_addr from;
struct ifreq ifr;
/* restore orig packet */
mess->hops = hops;
mess->giaddr = giaddr;
if (endopt)
*endopt = OPTION_END;
sz = orig_sz;
if ((mess->hops++) > 20)
continue;
if (relay->interface)
{
safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
ifr.ifr_addr.sa_family = AF_INET;
}
if (!relay->split_mode)
{
/* already gatewayed ? */
if (giaddr.s_addr)
{
/* if so check if by us, to stomp on loops. */
if (giaddr.s_addr == relay->local.addr4.s_addr)
continue;
}
/* plug in our address */
from.addr4 = mess->giaddr = relay->local.addr4;
}
else
{
/* Split mode. We put our address on the server-facing interface
into giaddr for the server to talk back to us on.
Our address on client-facing interface goes into agent-id
subnet-selector subopt, so that the server allocates the correct address. */
/* get our address on the server-facing interface. */
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
continue;
/* already gatewayed ? */
if (giaddr.s_addr)
{
/* if so check if by us, to stomp on loops. */
if (giaddr.s_addr == ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr)
continue;
}
/* giaddr is our address on the outgoing interface in split mode. */
from.addr4 = mess->giaddr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (!endopt)
{
/* Add an RFC3026 relay agent information option (2 bytes) at the very end of the options.
Said option to contain a RFC 3527 link selection sub option (6 bytes) and
RFC 5017 serverid-override option (6 bytes) and RFC5010 (3 bytes).
New END option is a 18th byte, so we need 18 bytes free.
We only need to do this once, and poke the address into the same place each time. */
if (!(endopt = option_find1((&mess->options[0] + sizeof(u32)), ((unsigned char *)mess) + sz, OPTION_END, 0)) ||
(endopt + 18 > (unsigned char *)(mess + 1)))
continue;
endopt[1] = 15; /* length */
endopt[2] = SUBOPT_SUBNET_SELECT;
endopt[3] = 4; /* length */
endopt[8] = SUBOPT_SERVER_OR;
endopt[9] = 4;
endopt[14] = SUBOPT_FLAGS;
endopt[15] = 1; /* length */
endopt[17] = OPTION_END;
sz = (endopt - (unsigned char *)mess) + 18;
}
/* IP address is already in network byte order */
memcpy(&endopt[4], &relay->local.addr4.s_addr, INADDRSZ);
memcpy(&endopt[10], &relay->local.addr4.s_addr, INADDRSZ);
endopt[16] = unicast ? 0x80 : 0x00;
endopt[0] = OPTION_AGENT_ID;
}
to.sa.sa_family = AF_INET;
to.in.sin_addr = relay->server.addr4;
to.in.sin_port = htons(relay->port);
#ifdef HAVE_SOCKADDR_SA_LEN
to.in.sin_len = sizeof(struct sockaddr_in);
#endif
/* Broadcasting to server. */
if (relay->server.addr4.s_addr == 0)
{
if (!relay->interface || strchr(relay->interface, '*') ||
ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1)
{
my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface);
continue;
}
to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
#ifdef HAVE_DUMPFILE
{
union mysockaddr fromsock;
fromsock.in.sin_port = htons(daemon->dhcp_server_port);
fromsock.in.sin_addr = from.addr4;
fromsock.sa.sa_family = AF_INET;
dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1);
}
#endif
send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
if (option_bool(OPT_LOG_OPTS))
{
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
if (relay->server.addr4.s_addr == 0)
snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface);
else
inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
}
}
/* restore in case of a local reply. */
mess->hops = hops;
mess->giaddr = giaddr;
if (endopt)
*endopt = OPTION_END;
}
struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
{
struct dhcp_relay *relay;
if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
return NULL;
for (relay = daemon->relay4; relay; relay = relay->next)
{
if (relay->split_mode)
{
struct ifreq ifr;
safe_strncpy(ifr.ifr_name, arrival_interface, IF_NAMESIZE);
ifr.ifr_addr.sa_family = AF_INET;
/* giaddr is our address on the returning interface in split mode. */
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1 ||
mess->giaddr.s_addr != ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr)
continue;
}
else if (mess->giaddr.s_addr != relay->local.addr4.s_addr)
continue;
if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
return relay->iface_index != 0 ? relay : NULL;
}
return NULL;
}
#endif /* HAVE_DHCP */
+81 -81
View File
@@ -21,7 +21,7 @@
struct state {
unsigned char *clid;
int clid_len, ia_type, interface, hostname_auth, lease_allocate;
int multicast_dest, clid_len, ia_type, interface, hostname_auth, lease_allocate;
char *client_hostname, *hostname, *domain, *send_domain;
struct dhcp_context *context;
struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr;
@@ -68,7 +68,7 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time
#define opt6_user_vendor_next(opt, end) (opt6_next(((uint8_t *) opt) - 2, end))
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int interface, char *iface_name,
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
size_t sz, struct in6_addr *client_addr, time_t now)
{
@@ -87,6 +87,7 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if
reset_counter();
state.context = context;
state.multicast_dest = multicast_dest;
state.interface = interface;
state.iface_name = iface_name;
state.fallback = fallback;
@@ -244,7 +245,11 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
struct in6_addr align;
/* the packet data is unaligned, copy to aligned storage */
memcpy(&align, inbuff + 2, IN6ADDRSZ);
state->link_address = &align;
/* RFC6221 para 4 */
if (!IN6_IS_ADDR_UNSPECIFIED(&align))
state->link_address = &align;
/* zero is_unicast since that is now known to refer to the
relayed packet, not the original sent by the client */
if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr, 0, now))
@@ -333,20 +338,24 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
else if (msg_type != DHCP6IREQ)
return 0;
/* server-id must match except for SOLICIT, CONFIRM and REBIND messages, which MUST NOT
have a server-id. 3315 para 15.x */
opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1);
if (msg_type == DHCP6SOLICIT || msg_type == DHCP6CONFIRM || msg_type == DHCP6REBIND)
if (msg_type == DHCP6SOLICIT || msg_type == DHCP6CONFIRM || msg_type == DHCP6REBIND || msg_type == DHCP6IREQ)
{
if (opt)
/* Above message types must be multicast 3315 Section 15. */
if (!state->multicast_dest)
return 0;
}
else if (msg_type == DHCP6IREQ)
{
/* If server-id provided, it must match. */
if (opt && (opt6_len(opt) != daemon->duid_len ||
memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
/* server-id must match except for SOLICIT, CONFIRM and REBIND messages, which MUST NOT
have a server-id. 3315 para 15.x */
if (msg_type == DHCP6IREQ)
{
/* If server-id provided in IREQ, it must match. */
if (opt && (opt6_len(opt) != daemon->duid_len ||
memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
return 0;
}
else if (opt)
return 0;
}
else
@@ -634,7 +643,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
case DHCP6SOLICIT:
{
int address_assigned = 0;
int address_assigned;
/* tags without all prefix-class tags */
struct dhcp_netid *solicit_tags;
struct dhcp_context *c;
@@ -653,6 +662,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
request_no_address:
solicit_tags = tagif;
address_assigned = 0;
if (ignore)
return 0;
@@ -764,15 +774,6 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
if (address_assigned != 1)
{
/* If the server will not assign any addresses to any IAs in a
subsequent Request from the client, the server MUST send an Advertise
message to the client that doesn't include any IA options. */
if (!state->lease_allocate)
{
save_counter(o);
continue;
}
/* If the server cannot assign any addresses to an IA in the message
from the client, the server MUST include the IA in the Reply message
with no addresses in the IA and a Status Code option in the IA
@@ -799,7 +800,6 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
o = new_opt6(OPTION6_PREFERENCE);
put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);
end_opt6(o);
tagif = add_options(state, 0);
}
else
{
@@ -819,7 +819,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
break;
}
}
tagif = add_options(state, 0);
break;
}
@@ -977,8 +978,10 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
{
if (msg_type == DHCP6REBIND)
{
/* When rebinding, we can create a lease if it doesn't exist. */
lease = lease6_allocate(&req_addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
/* When rebinding, we can create a lease if it doesn't exist, as long
as --dhcp-authoritative is set. */
if (option_bool(OPT_AUTHORITATIVE))
lease = lease6_allocate(&req_addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
if (lease)
lease_set_iaid(lease, state->iaid);
else
@@ -2276,64 +2279,61 @@ int relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface)
{
void *opt, *opts = inbuff + 34;
void *end = inbuff + sz;
for (opt = opts; opt; opt = opt6_next(opt, end))
if (opt6_type(opt) == OPTION6_RELAY_MSG && opt6_len(opt) > 0)
{
int encap_type = *((unsigned char *)opt6_ptr(opt, 0));
put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ);
peer->sin6_scope_id = relay->iface_index;
if (encap_type == DHCP6RELAYREPL)
{
peer->sin6_port = ntohs(DHCPV6_SERVER_PORT);
return 1;
}
peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT);
#ifdef HAVE_SCRIPT
if (daemon->lease_change_command && encap_type == DHCP6REPLY)
{
/* decapsulate relayed message */
opts = opt6_ptr(opt, 4);
end = opt6_ptr(opt, opt6_len(opt));
for (opt = opts; opt; opt = opt6_next(opt, end))
if (opt6_type(opt) == OPTION6_IA_PD && opt6_len(opt) > 12)
{
void *ia_opts = opt6_ptr(opt, 12);
void *ia_end = opt6_ptr(opt, opt6_len(opt));
void *ia_opt;
for (ia_opt = ia_opts; ia_opt; ia_opt = opt6_next(ia_opt, ia_end))
/* valid lifetime must not be zero. */
if (opt6_type(ia_opt) == OPTION6_IAPREFIX && opt6_len(ia_opt) >= 25 && opt6_uint(ia_opt, 4, 4) != 0)
{
if (daemon->free_snoops ||
(daemon->free_snoops = whine_malloc(sizeof(struct snoop_record))))
{
struct snoop_record *snoop = daemon->free_snoops;
daemon->free_snoops = snoop->next;
snoop->client = peer->sin6_addr;
snoop->prefix_len = opt6_uint(ia_opt, 8, 1);
memcpy(&snoop->prefix, opt6_ptr(ia_opt, 9), IN6ADDRSZ);
snoop->next = relay->snoop_records;
relay->snoop_records = snoop;
}
}
}
}
#endif
return 1;
}
if ((opt = opt6_find(opts, end, OPTION6_RELAY_MSG, 4)))
{
int encap_type = opt6_uint(opt, 0, 1);
put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ);
peer->sin6_scope_id = relay->iface_index;
if (encap_type == DHCP6RELAYREPL)
{
peer->sin6_port = ntohs(DHCPV6_SERVER_PORT);
return 1;
}
peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT);
#ifdef HAVE_SCRIPT
if (daemon->lease_change_command && encap_type == DHCP6REPLY)
{
/* skip over message type and transaction-id. to get to options. */
opts = opt6_ptr(opt, 4);
end = opt6_ptr(opt, opt6_len(opt));
if ((opt = opt6_find(opts, end, OPTION6_IA_PD, 12)))
{
opts = opt6_ptr(opt, 12);
end = opt6_ptr(opt, opt6_len(opt));
for (opt = opt6_find(opts, end, OPTION6_IAPREFIX, 25); opt; opt = opt6_find(opt6_next(opt, end), end, OPTION6_IAPREFIX, 25))
/* valid lifetime must not be zero. */
if (opt6_uint(opt, 4, 4) != 0)
{
if (daemon->free_snoops ||
(daemon->free_snoops = whine_malloc(sizeof(struct snoop_record))))
{
struct snoop_record *snoop = daemon->free_snoops;
daemon->free_snoops = snoop->next;
snoop->client = peer->sin6_addr;
snoop->prefix_len = opt6_uint(opt, 8, 1);
memcpy(&snoop->prefix, opt6_ptr(opt, 9), IN6ADDRSZ);
snoop->next = relay->snoop_records;
relay->snoop_records = snoop;
}
}
}
}
#endif
return 1;
}
}
return 0;
}
#ifdef HAVE_SCRIPT
int do_snoop_script_run(void)
{