summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLung-Pin Chang <changlp@cs.nctu.edu.tw>2015-03-19 23:22:21 +0000
committerSimon Kelley <simon@thekelleys.org.uk>2015-03-19 23:22:21 +0000
commit65c721200023ef0023114459a8d12f8b0a24cfd8 (patch)
treee4dbdcd3762c014c763c8e70eb84574b300c4acc
parent979fe86bc8693f660eddea232ae39cbbb50b294c (diff)
downloaddnsmasq-65c721200023ef0023114459a8d12f8b0a24cfd8.tar.gz
dhcp: set outbound interface via cmsg in unicast replyv2.73rc1
If multiple routes to the same network exist, Linux blindly picks the first interface (route) based on destination address, which might not be the one we're actually offering leases. Rather than relying on this, always set the interface for outgoing unicast DHCP packets.
-rw-r--r--src/dhcp.c45
1 files changed, 25 insertions, 20 deletions
diff --git a/src/dhcp.c b/src/dhcp.c
index 5c3089a..f1f43f8 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -376,10 +376,9 @@ void dhcp_packet(time_t now, int pxe_fd)
}
}
#if defined(HAVE_LINUX_NETWORK)
- else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
- mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
+ else
{
- /* broadcast to 255.255.255.255 (or mac address invalid) */
+ /* fill cmsg for outbound interface (both broadcast & unicast) */
struct in_pktinfo *pkt;
msg.msg_control = control_u.control;
msg.msg_controllen = sizeof(control_u);
@@ -389,23 +388,29 @@ void dhcp_packet(time_t now, int pxe_fd)
pkt->ipi_spec_dst.s_addr = 0;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = IPPROTO_IP;
- cmptr->cmsg_type = IP_PKTINFO;
- dest.sin_addr.s_addr = INADDR_BROADCAST;
- dest.sin_port = htons(daemon->dhcp_client_port);
- }
- else
- {
- /* unicast to unconfigured client. Inject mac address direct into ARP cache.
- struct sockaddr limits size to 14 bytes. */
- dest.sin_addr = mess->yiaddr;
- dest.sin_port = htons(daemon->dhcp_client_port);
- memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
- arp_req.arp_ha.sa_family = mess->htype;
- memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
- /* interface name already copied in */
- arp_req.arp_flags = ATF_COM;
- if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
- my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
+ cmptr->cmsg_type = IP_PKTINFO;
+
+ if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
+ mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
+ {
+ /* broadcast to 255.255.255.255 (or mac address invalid) */
+ dest.sin_addr.s_addr = INADDR_BROADCAST;
+ dest.sin_port = htons(daemon->dhcp_client_port);
+ }
+ else
+ {
+ /* unicast to unconfigured client. Inject mac address direct into ARP cache.
+ struct sockaddr limits size to 14 bytes. */
+ dest.sin_addr = mess->yiaddr;
+ dest.sin_port = htons(daemon->dhcp_client_port);
+ memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
+ arp_req.arp_ha.sa_family = mess->htype;
+ memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
+ /* interface name already copied in */
+ arp_req.arp_flags = ATF_COM;
+ if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
+ my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
+ }
}
#elif defined(HAVE_SOLARIS_NETWORK)
else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)