summaryrefslogtreecommitdiff
path: root/src/dhcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dhcp.c')
-rw-r--r--src/dhcp.c179
1 files changed, 117 insertions, 62 deletions
diff --git a/src/dhcp.c b/src/dhcp.c
index e500bc2..42d819f 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
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
@@ -20,8 +20,6 @@
struct iface_param {
struct dhcp_context *current;
- struct dhcp_relay *relay;
- struct in_addr relay_local;
int ind;
};
@@ -34,7 +32,7 @@ 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 int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index);
+static int 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)
@@ -177,11 +175,15 @@ void dhcp_packet(time_t now, int pxe_fd)
if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
(sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
return;
-
- #if defined (HAVE_LINUX_NETWORK)
+
+#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;
-
+
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
@@ -302,12 +304,7 @@ void dhcp_packet(time_t now, int pxe_fd)
for (context = daemon->dhcp; context; context = context->next)
context->current = context;
- for (relay = daemon->relay4; relay; relay = relay->next)
- relay->current = relay;
-
parm.current = NULL;
- parm.relay = NULL;
- parm.relay_local.s_addr = 0;
parm.ind = iface_index;
if (!iface_check(AF_INET, (union all_addr *)&iface_addr, ifr.ifr_name, NULL))
@@ -329,15 +326,19 @@ void dhcp_packet(time_t now, int pxe_fd)
there is more than one address on the interface in the same subnet */
complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
}
+
+ if (relay_upstream4(iface_index, mess, (size_t)sz))
+ return;
if (!iface_enumerate(AF_INET, &parm, complete_context))
return;
- /* We're relaying this request */
- if (parm.relay_local.s_addr != 0 &&
- relay_upstream4(parm.relay, mess, (size_t)sz, iface_index))
+ /* Check for a relay again after iface_enumerate/complete_context has had
+ chance to fill in relay->iface_index fields. This handles first time through
+ and any changes in interface config. */
+ if (relay_upstream4(iface_index, mess, (size_t)sz))
return;
-
+
/* May have configured relay, but not DHCP server */
if (!daemon->dhcp)
return;
@@ -455,6 +456,17 @@ void dhcp_packet(time_t now, int pxe_fd)
#elif defined(HAVE_BSD_NETWORK)
else
{
+#ifdef HAVE_DUMPFILE
+ if (ntohs(mess->flags) & 0x8000)
+ dest.sin_addr.s_addr = INADDR_BROADCAST;
+ else
+ dest.sin_addr = mess->yiaddr;
+ dest.sin_port = htons(daemon->dhcp_client_port);
+
+ dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
+ (union mysockaddr *)&dest, fd);
+#endif
+
send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
return;
}
@@ -463,6 +475,11 @@ void dhcp_packet(time_t now, int pxe_fd)
#ifdef HAVE_SOLARIS_NETWORK
setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
#endif
+
+#ifdef HAVE_DUMPFILE
+ dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
+ (union mysockaddr *)&dest, fd);
+#endif
while(retry_send(sendmsg(fd, &msg, 0)));
@@ -613,14 +630,9 @@ static int complete_context(struct in_addr local, int if_index, char *label,
}
for (relay = daemon->relay4; relay; relay = relay->next)
- if (if_index == param->ind && relay->local.addr4.s_addr == local.s_addr && relay->current == relay &&
- (param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))
- {
- relay->current = param->relay;
- param->relay = relay;
- param->relay_local = local;
- }
-
+ if (relay->local.addr4.s_addr == local.s_addr)
+ relay->iface_index = if_index;
+
return 1;
}
@@ -1061,53 +1073,96 @@ char *host_from_dns(struct in_addr addr)
return NULL;
}
-static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)
+static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz)
{
- /* ->local is same value for all relays on ->current chain */
- union all_addr from;
-
+ struct in_addr giaddr = mess->giaddr;
+ u8 hops = mess->hops;
+ struct dhcp_relay *relay;
+
if (mess->op != BOOTREQUEST)
return 0;
- /* source address == relay address */
- from.addr4 = relay->local.addr4;
+ 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 0;
- /* already gatewayed ? */
- if (mess->giaddr.s_addr)
- {
- /* if so check if by us, to stomp on loops. */
- if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
- return 1;
- }
- else
- {
- /* plug in our address */
- mess->giaddr.s_addr = relay->local.addr4.s_addr;
- }
+ for (; relay; relay = relay->next)
+ if (relay->iface_index != 0 && relay->iface_index == iface_index)
+ {
+ union mysockaddr to;
+ union all_addr from;
- if ((mess->hops++) > 20)
- return 1;
+ mess->hops = hops;
+ mess->giaddr = giaddr;
+
+ if ((mess->hops++) > 20)
+ continue;
+
+ /* source address == relay address */
+ from.addr4 = relay->local.addr4;
- for (; relay; relay = relay->current)
- {
- union mysockaddr to;
-
- to.sa.sa_family = AF_INET;
- to.in.sin_addr = relay->server.addr4;
- to.in.sin_port = htons(daemon->dhcp_server_port);
-
- send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
-
- if (option_bool(OPT_LOG_OPTS))
+ /* 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);
+
+ /* 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
{
- 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);
+ 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);
}
-
- /* Save this for replies */
- relay->iface_index = iface_index;
- }
+#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);
+ }
+ }
return 1;
}