summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2021-12-20 16:40:41 +0000
committerSimon Kelley <simon@thekelleys.org.uk>2021-12-20 16:40:41 +0000
commit18b1d1424e3b5e36589321caf569fdf6fe0b474a (patch)
tree7671f2f96ae883f77029b825b72805c454bbf6f7
parent1176cd58c90fc37bf98a6f774b26fc1adc8fd8e9 (diff)
downloaddnsmasq-18b1d1424e3b5e36589321caf569fdf6fe0b474a.tar.gz
Generalise --dhcp-relay.
Sending via broadcast/multicast is now supported for both IPv4 and IPv6 and the configuration syntax made easier (but backwards compatible).
-rw-r--r--CHANGELOG4
-rw-r--r--man/dnsmasq.89
-rw-r--r--src/dhcp-common.c18
-rw-r--r--src/dhcp.c27
-rw-r--r--src/option.c56
-rw-r--r--src/rfc3315.c12
6 files changed, 101 insertions, 25 deletions
diff --git a/CHANGELOG b/CHANGELOG
index f202bbf..4a012de 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -15,6 +15,10 @@ version 2.87
to disable the DNS server. Thanks to Drexl Johannes
for the bug report.
+ Generalise --dhcp-relay. Sending via broadcast/multicast is
+ now supported for both IPv4 and IPv6 and the configuration
+ syntax made easier (but backwards compatible).
+
version 2.86
Handle DHCPREBIND requests in the DHCPv6 server code.
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 19559a0..8bdcaa3 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -1326,7 +1326,7 @@ DHCP options. This make extra space available in the DHCP packet for
options but can, rarely, confuse old or broken clients. This flag
forces "simple and safe" behaviour to avoid problems in such a case.
.TP
-.B --dhcp-relay=<local address>,<server address>[,<interface]
+.B --dhcp-relay=<local address>[,<server address>][,<interface]
Configure dnsmasq to do DHCP relay. The local address is an address
allocated to an interface on the host running dnsmasq. All DHCP
requests arriving on that interface will we relayed to a remote DHCP
@@ -1334,10 +1334,9 @@ server at the server address. It is possible to relay from a single local
address to multiple remote servers by using multiple \fB--dhcp-relay\fP
configs with the same local address and different server
addresses. A server address must be an IP literal address, not a
-domain name. In the case of DHCPv6, the server address may be the
-ALL_SERVERS multicast address, ff05::1:3. In this case the interface
-must be given, not be wildcard, and is used to direct the multicast to the
-correct interface to reach the DHCP server.
+domain name. If the server address is ommitted, the request will be
+forwarded by broadcast (IPv4) or multicast (IPv6). In this case the interface
+must be given and not be wildcard.
Access control for DHCP clients has the same rules as for the DHCP
server, see \fB--interface\fP, \fB--except-interface\fP, etc. The optional
diff --git a/src/dhcp-common.c b/src/dhcp-common.c
index 69e8d38..291e82b 100644
--- a/src/dhcp-common.c
+++ b/src/dhcp-common.c
@@ -985,11 +985,27 @@ void log_context(int family, struct dhcp_context *context)
void log_relay(int family, struct dhcp_relay *relay)
{
+ int broadcast = relay->server.addr4.s_addr == 0;
inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN);
inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN);
+#ifdef HAVE_DHCP6
+ struct in6_addr multicast;
+
+ inet_pton(AF_INET6, ALL_SERVERS, &multicast);
+
+ if (family == AF_INET6)
+ broadcast = IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast);
+#endif
+
+
if (relay->interface)
- my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
+ {
+ if (broadcast)
+ my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s via %s"), daemon->addrbuff, relay->interface);
+ else
+ my_syslog(MS_DHCP | LOG_INFO, _("DHCP 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"), daemon->addrbuff, daemon->namebuff);
}
diff --git a/src/dhcp.c b/src/dhcp.c
index e500bc2..2c1272f 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -1095,14 +1095,35 @@ static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess,
to.sa.sa_family = AF_INET;
to.in.sin_addr = relay->server.addr4;
to.in.sin_port = htons(daemon->dhcp_server_port);
-
+
+ /* 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);
+ return 1;
+ }
+
+ to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+ }
+
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);
- inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
- my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
+ 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);
}
/* Save this for replies */
diff --git a/src/option.c b/src/option.c
index 9e315a5..7e7b9fb 100644
--- a/src/option.c
+++ b/src/option.c
@@ -4281,26 +4281,56 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
}
break;
-
+
case LOPT_RELAY: /* --dhcp-relay */
{
struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
- comma = split(arg);
- new->interface = opt_string_alloc(split(comma));
+ char *two = split(arg);
+ char *three = split(two);
+
new->iface_index = 0;
- if (comma && inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
+
+ if (two)
{
- new->next = daemon->relay4;
- daemon->relay4 = new;
- }
+ if (inet_pton(AF_INET, arg, &new->local))
+ {
+ if (!inet_pton(AF_INET, two, &new->server))
+ {
+ new->server.addr4.s_addr = 0;
+
+ /* Fail for three arg version where there are not two addresses.
+ Also fail when broadcasting to wildcard address. */
+ if (three || strchr(two, '*'))
+ two = NULL;
+ else
+ three = two;
+ }
+
+ new->next = daemon->relay4;
+ daemon->relay4 = new;
+ }
#ifdef HAVE_DHCP6
- else if (comma && inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
- {
- new->next = daemon->relay6;
- daemon->relay6 = new;
- }
+ else if (inet_pton(AF_INET6, arg, &new->local))
+ {
+ if (!inet_pton(AF_INET6, two, &new->server))
+ {
+ inet_pton(AF_INET6, ALL_SERVERS, &new->server.addr6);
+ /* Fail for three arg version where there are not two addresses.
+ Also fail when multicasting to wildcard address. */
+ if (three || strchr(two, '*'))
+ two = NULL;
+ else
+ three = two;
+ }
+ new->next = daemon->relay6;
+ daemon->relay6 = new;
+ }
#endif
- else
+
+ new->interface = opt_string_alloc(three);
+ }
+
+ if (!two)
{
free(new->interface);
ret_err_free(_("Bad dhcp-relay"), new);
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 5c2ff97..f54fb78 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -2170,7 +2170,10 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz,
if (!relay->interface || strchr(relay->interface, '*') ||
(multicast_iface = if_nametoindex(relay->interface)) == 0 ||
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1)
- my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast to DHCPv6 server without correct interface"));
+ {
+ my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast DHCP relay via interface %s"), relay->interface);
+ return;
+ }
}
send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(-1), &to, &from, 0);
@@ -2178,8 +2181,11 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz,
if (option_bool(OPT_LOG_OPTS))
{
inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN);
- inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN);
- my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->namebuff);
+ if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast))
+ snprintf(daemon->namebuff, MAXDNAME, _("multicast via %s"), relay->interface);
+ else
+ inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN);
+ my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->namebuff);
}
/* Save this for replies */