--- dnsmasq-2.57/work/dnsmasq-2.57/src/dnsmasq.h 2011-02-17 15:30:15.000000000 +0000 +++ dnsmasq-2.57_patched_alternate_ip/work/dnsmasq-2.57/src/dnsmasq.h 2011-11-20 07:15:07.000000000 +0000 @@ -14,6 +14,8 @@ along with this program. If not, see . */ +// USE_LINUX_GETIFADDRS patch by Martin Suefke 2011 + #define COPYRIGHT "Copyright (c) 2000-2011 Simon Kelley" #ifndef NO_LARGEFILE @@ -109,6 +111,12 @@ #if defined(HAVE_LINUX_NETWORK) #include + +//TODO: Find a better place to define USE_LINUX_GETIFADDRS +#define USE_LINUX_GETIFADDRS +#if defined(USE_LINUX_GETIFADDRS) +# include +#endif /* There doesn't seem to be a universally-available userpace header for these. */ extern int capset(cap_user_header_t header, cap_user_data_t data); --- dnsmasq-2.57/work/dnsmasq-2.57/src/dhcp.c 2011-02-17 15:30:15.000000000 +0000 +++ dnsmasq-2.57_patched_alternate_ip/work/dnsmasq-2.57/src/dhcp.c 2011-11-20 07:14:47.000000000 +0000 @@ -14,6 +14,8 @@ along with this program. If not, see . */ +// USE_LINUX_GETIFADDRS patch by Martin Suefke 2011 + #include "dnsmasq.h" #ifdef HAVE_DHCP @@ -129,6 +131,14 @@ struct iface_param parm; #ifdef HAVE_LINUX_NETWORK struct arpreq arp_req; + struct in_addr recvd_addr4; // need one for ipv6, too ? + struct ifaddrs *ifa, *ifEntry; + int rc; + char addressBuffer[INET6_ADDRSTRLEN]; + struct all_addr srcaddr; // DHCP server source address as determined during interface examination + int srcaddrAF; //Set address family if special src address available + memset(&srcaddr, 0, sizeof(srcaddr)); + srcaddrAF=0; #endif union { @@ -153,6 +163,7 @@ { msg.msg_flags = 0; while ((sz = recvmsg(fd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR); + my_syslog(MS_DHCP | LOG_DEBUG, _("dhcp.c +156 size %d"), sz); if (sz == -1) return; @@ -166,14 +177,18 @@ { if (!expand_buf(&daemon->dhcp_packet, sz + 100)) return; + my_syslog(MS_DHCP | LOG_DEBUG, _("DHCP packet received: %d bytes or less"), sz+100); } else { expand_buf(&daemon->dhcp_packet, sz); + my_syslog(MS_DHCP | LOG_DEBUG, _("DHCP packet received: %d bytes"), sz); break; } } + my_syslog(MS_DHCP | LOG_DEBUG, _("dhcp.c +180 size %d"), sz); + /* expand_buf may have moved buffer */ mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base; msg.msg_controllen = sizeof(control_u); @@ -184,10 +199,13 @@ while ((sz = recvmsg(fd, &msg, 0)) == -1 && errno == EINTR); + my_syslog(MS_DHCP | LOG_DEBUG, _("dhcp.c +192 size %d"), sz); + if ((msg.msg_flags & MSG_TRUNC) || sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))) return; #if defined (HAVE_LINUX_NETWORK) + recvd_addr4.s_addr = 0; if (msg.msg_controllen >= sizeof(struct cmsghdr)) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) @@ -198,6 +216,8 @@ } p; p.c = CMSG_DATA(cmptr); iface_index = p.p->ipi_ifindex; + my_syslog(MS_DHCP | LOG_DEBUG, _("dhcp.c +215 ipi_saddr = %s"), inet_ntoa(p.p->ipi_addr)); + recvd_addr4 = p.p->ipi_addr; //TODO: Do this for BSD, Solaris, too (?) if (p.p->ipi_addr.s_addr != INADDR_BROADCAST) unicast_dest = 1; } @@ -229,9 +249,13 @@ } #endif + my_syslog(MS_DHCP | LOG_DEBUG, _("dhcp.c +239 indextoname( %d )" ), iface_index); + if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name)) return; + my_syslog(MS_DHCP | LOG_DEBUG, _("dhcp.c +244 IFNAME=%s"), ifr.ifr_name); + #ifdef HAVE_LINUX_NETWORK /* ARP fiddling uses original interface even if we pretend to use a different one. */ strncpy(arp_req.arp_dev, ifr.ifr_name, 16); @@ -243,15 +267,75 @@ unicast_dest = 1; #endif +#ifdef USE_LINUX_GETIFADDRS + /* Check all IPs assigned to the interface named ifr.ifr_name */ + ifa = NULL; + ifEntry = NULL; + rc = getifaddrs(&ifa); + if (rc == 0) { + my_syslog(MS_DHCP | LOG_DEBUG, _("getifaddrs() ok") ); + for(ifEntry=ifa; ifEntry!=NULL; ifEntry=ifEntry->ifa_next) { + my_syslog(MS_DHCP | LOG_DEBUG, _("got entry for %s, flags 0x%x"), ifEntry->ifa_name, ifEntry->ifa_flags ); + //TODO: may check ifEntry->ifa_flags for values in net/if.h ; e.g. IFF_UP, IFF_RUNNING + if (strcmp(ifEntry->ifa_name,ifr.ifr_name) == 0 ) { + my_syslog(MS_DHCP | LOG_DEBUG, _("entry matches ifr_name") ); + } else continue; + if(ifEntry->ifa_addr == NULL) { + continue; + } + if(ifEntry->ifa_addr->sa_data == NULL) { + continue; + } + if(ifEntry->ifa_addr->sa_family==AF_INET) { + addrp = &((struct sockaddr_in *)ifEntry->ifa_addr)->sin_addr; + srcaddr.addr.addr4 = *addrp; + srcaddrAF = AF_INET; + const char *a = inet_ntop(ifEntry->ifa_addr->sa_family, addrp, addressBuffer, sizeof(addressBuffer)); + my_syslog(MS_DHCP | LOG_DEBUG, _("got AF_INET: %s") , (a!=NULL) ? a : "" ); + if ((recvd_addr4.s_addr == srcaddr.addr.addr4.s_addr ) || (recvd_addr4.s_addr == INADDR_BROADCAST) ) { + my_syslog(MS_DHCP | LOG_DEBUG, _("recvd unicast to this IF or broadcast, checking interface")); + if (iface_check(AF_INET, (struct all_addr *)addrp, ifr.ifr_name, &iface_index)) { + rc=1; + my_syslog(MS_DHCP | LOG_DEBUG, _("iface_check ok") ); + break; + } + } + } else if(ifEntry->ifa_addr->sa_family==AF_INET6) { + const char *a = inet_ntop(ifEntry->ifa_addr->sa_family, addrp, addressBuffer, sizeof(addressBuffer)); + my_syslog(MS_DHCP | LOG_DEBUG, _("got AF_INET6: %s"), (a!=NULL) ? a : "" ); + addrp = (struct in_addr *) &((struct sockaddr_in6 *)ifEntry->ifa_addr)->sin6_addr; + srcaddr.addr.addr6 = ((struct sockaddr_in6 *)ifEntry->ifa_addr)->sin6_addr; + srcaddrAF=AF_INET6; + if (iface_check(AF_INET6, (struct all_addr *)addrp, ifr.ifr_name, &iface_index)) { + rc=1; + my_syslog(MS_DHCP | LOG_DEBUG, _("iface_check ok") ); + break; + } + } else { + //It isn't IPv4 or IPv6 + continue; + } + } + freeifaddrs(ifa); + if ( rc != 1 ) return; // no suitable interface found + } else return; // cannot determine any addresses + +#else + /* This simple version gets only the interface's 1st address. May fail iface_check later. */ ifr.ifr_addr.sa_family = AF_INET; if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 ) { addrp = &iface_addr; iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; + my_syslog(MS_DHCP | LOG_DEBUG, _("dhcp.c +262 got iface_addr %08x"),iface_addr); } + + my_syslog(MS_DHCP | LOG_DEBUG, _("dhcp.c +265 before iface_check")); if (!iface_check(AF_INET, (struct all_addr *)addrp, ifr.ifr_name, &iface_index)) return; + my_syslog(MS_DHCP | LOG_DEBUG, _("dhcp.c +268 after iface_check")); +#endif for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0)) @@ -320,6 +404,9 @@ dest.sin_len = sizeof(struct sockaddr_in); #endif + my_syslog(MS_DHCP | LOG_DEBUG, _("dhcp.c +398") ); + struct in_pktinfo *pkt = NULL; + if (pxe_fd) { if (mess->ciaddr.s_addr != 0) @@ -337,6 +424,7 @@ the source address in the request packet, we believe the source port too, and send back to that. If we're replying to a DHCPINFORM, trust the source address always. */ + my_syslog(MS_DHCP | LOG_DEBUG, _("dhcp.c +417 src addr tallys") ); if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) || dest.sin_port == 0 || dest.sin_addr.s_addr == 0) { @@ -349,7 +437,7 @@ mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0) { /* broadcast to 255.255.255.255 (or mac address invalid) */ - struct in_pktinfo *pkt; + my_syslog(MS_DHCP | LOG_DEBUG, _("answer with broadcast") ); msg.msg_control = control_u.control; msg.msg_controllen = sizeof(control_u); cmptr = CMSG_FIRSTHDR(&msg); @@ -366,6 +454,7 @@ { /* unicast to unconfigured client. Inject mac address direct into ARP cache. struct sockaddr limits size to 14 bytes. */ + my_syslog(MS_DHCP | LOG_DEBUG, _("answer with unicast") ); dest.sin_addr = mess->yiaddr; dest.sin_port = htons(daemon->dhcp_client_port); memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in)); @@ -411,6 +500,27 @@ setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index)); #endif +#ifdef USE_LINUX_GETIFADDRS + // Set specific sender address if available + if (srcaddrAF == AF_INET) { + my_syslog(MS_DHCP | LOG_DEBUG, _("adding IPv4 source") ); + if (! (pkt) ) { // make pktinfo if not yet available + my_syslog(MS_DHCP | LOG_DEBUG, _("making pktinfo structure") ); + 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; + msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + cmptr->cmsg_level = SOL_IP; + cmptr->cmsg_type = IP_PKTINFO; + } + pkt->ipi_spec_dst = srcaddr.addr.addr4; + } else if (srcaddrAF == AF_INET6) { + my_syslog(MS_DHCP | LOG_WARNING, _("TODO: set correct source address for IPv6. Btw, what are you doing with DHCP on IPv6 ?!?") ); + } +#endif + while(sendmsg(fd, &msg, 0) == -1 && retry_send()); }