--- embedaddon/dnsmasq/src/dhcp.c 2014/06/15 16:31:38 1.1.1.2 +++ embedaddon/dnsmasq/src/dhcp.c 2016/11/02 09:57:01 1.1.1.3 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2016 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 @@ -146,6 +146,7 @@ void dhcp_packet(time_t now, int pxe_fd) struct iovec iov; ssize_t sz; int iface_index = 0, unicast_dest = 0, is_inform = 0; + int rcvd_iface_index; struct in_addr iface_addr; struct iface_param parm; #ifdef HAVE_LINUX_NETWORK @@ -225,18 +226,22 @@ void dhcp_packet(time_t now, int pxe_fd) strncpy(arp_req.arp_dev, ifr.ifr_name, 16); #endif - /* One form of bridging on BSD has the property that packets - can be recieved on bridge interfaces which do not have an IP address. - We allow these to be treated as aliases of another interface which does have - an IP address with --dhcp-bridge=interface,alias,alias */ + /* If the interface on which the DHCP request was received is an + alias of some other interface (as specified by the + --bridge-interface option), change ifr.ifr_name so that we look + for DHCP contexts associated with the aliased interface instead + of with the aliasing one. */ + rcvd_iface_index = iface_index; for (bridge = daemon->bridges; bridge; bridge = bridge->next) { for (alias = bridge->alias; alias; alias = alias->next) - if (strncmp(ifr.ifr_name, alias->iface, IF_NAMESIZE) == 0) + if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE)) { if (!(iface_index = if_nametoindex(bridge->iface))) { - my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name); + my_syslog(MS_DHCP | LOG_WARNING, + _("unknown interface %s in bridge-interface"), + bridge->iface); return; } else @@ -376,36 +381,42 @@ 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); cmptr = CMSG_FIRSTHDR(&msg); pkt = (struct in_pktinfo *)CMSG_DATA(cmptr); - pkt->ipi_ifindex = iface_index; + pkt->ipi_ifindex = rcvd_iface_index; 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); + 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)); + } } - 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; - ioctl(daemon->dhcpfd, SIOCSARP, &arp_req); - } #elif defined(HAVE_SOLARIS_NETWORK) else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER) { @@ -442,9 +453,14 @@ void dhcp_packet(time_t now, int pxe_fd) setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index)); #endif - while(sendmsg(fd, &msg, 0) == -1 && retry_send()); + while(retry_send(sendmsg(fd, &msg, 0))); + + /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */ + if (errno != 0) + my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"), + inet_ntoa(dest.sin_addr), strerror(errno)); } - + /* check against secondary interface addresses */ static int check_listen_addrs(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam) @@ -637,7 +653,7 @@ int address_allocate(struct dhcp_context *context, /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good dispersal even with similarly-valued "strings". */ for (j = 0, i = 0; i < hw_len; i++) - j += hwaddr[i] + (j << 6) + (j << 16) - j; + j = hwaddr[i] + (j << 6) + (j << 16) - j; for (pass = 0; pass <= 1; pass++) for (c = context; c; c = c->current)