1 /* dnsmasq is Copyright (c) 2000-2009 Simon Kelley 2 3 This program is free software; you can redistribute it and/or modify 4 it under the terms of the GNU General Public License as published by 5 the Free Software Foundation; version 2 dated June, 1991, or 6 (at your option) version 3 dated 29 June, 2007. 7 8 This program is distributed in the hope that it will be useful, 9 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License 14 along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 #include "dnsmasq.h" 18 19 #if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) 20 21 static struct iovec ifconf = { 22 .iov_base = NULL, 23 .iov_len = 0 24 }; 25 26 static struct iovec ifreq = { 27 .iov_base = NULL, 28 .iov_len = 0 29 }; 30 31 int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)()) 32 { 33 char *ptr; 34 struct ifreq *ifr; 35 struct ifconf ifc; 36 int fd, errsav, ret = 0; 37 int lastlen = 0; 38 size_t len = 0; 39 40 if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) 41 return 0; 42 43 while(1) 44 { 45 len += 10*sizeof(struct ifreq); 46 47 if (!expand_buf(&ifconf, len)) 48 goto err; 49 50 ifc.ifc_len = len; 51 ifc.ifc_buf = ifconf.iov_base; 52 53 if (ioctl(fd, SIOCGIFCONF, &ifc) == -1) 54 { 55 if (errno != EINVAL || lastlen != 0) 56 goto err; 57 } 58 else 59 { 60 if (ifc.ifc_len == lastlen) 61 break; /* got a big enough buffer now */ 62 lastlen = ifc.ifc_len; 63 } 64 } 65 66 for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len) 67 { 68 /* subsequent entries may not be aligned, so copy into 69 an aligned buffer to avoid nasty complaints about 70 unaligned accesses. */ 71 72 len = sizeof(struct ifreq); 73 74 #ifdef HAVE_SOCKADDR_SA_LEN 75 ifr = (struct ifreq *)ptr; 76 if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru)) 77 len = ifr->ifr_addr.sa_len + offsetof(struct ifreq, ifr_ifru); 78 #endif 79 80 if (!expand_buf(&ifreq, len)) 81 goto err; 82 83 ifr = (struct ifreq *)ifreq.iov_base; 84 memcpy(ifr, ptr, len); 85 86 if (ifr->ifr_addr.sa_family == AF_INET && ipv4_callback) 87 { 88 struct in_addr addr, netmask, broadcast; 89 broadcast.s_addr = 0; 90 addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr; 91 if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1) 92 continue; 93 netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr; 94 if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1) 95 broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr; 96 if (!((*ipv4_callback)(addr, 97 (int)if_nametoindex(ifr->ifr_name), 98 netmask, broadcast, 99 parm))) 100 goto err; 101 } 102 #ifdef HAVE_IPV6 103 else if (ifr->ifr_addr.sa_family == AF_INET6 && ipv6_callback) 104 { 105 struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr; 106 /* voodoo to clear interface field in address */ 107 if (!(daemon->options & OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr)) 108 { 109 addr->s6_addr[2] = 0; 110 addr->s6_addr[3] = 0; 111 } 112 if (!((*ipv6_callback)(addr, 113 (int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id, 114 (int)if_nametoindex(ifr->ifr_name), 115 parm))) 116 goto err; 117 } 118 #endif 119 } 120 121 ret = 1; 122 123 err: 124 errsav = errno; 125 close(fd); 126 errno = errsav; 127 128 return ret; 129 } 130 #endif 131 132 133 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) 134 #include <net/bpf.h> 135 136 void init_bpf(void) 137 { 138 int i = 0; 139 140 while (1) 141 { 142 /* useful size which happens to be sufficient */ 143 if (expand_buf(&ifreq, sizeof(struct ifreq))) 144 { 145 sprintf(ifreq.iov_base, "/dev/bpf%d", i++); 146 if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1) 147 return; 148 } 149 if (errno != EBUSY) 150 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET); 151 } 152 } 153 154 void send_via_bpf(struct dhcp_packet *mess, size_t len, 155 struct in_addr iface_addr, struct ifreq *ifr) 156 { 157 /* Hairy stuff, packet either has to go to the 158 net broadcast or the destination can't reply to ARP yet, 159 but we do know the physical address. 160 Build the packet by steam, and send directly, bypassing 161 the kernel IP stack */ 162 163 struct ether_header ether; 164 struct ip ip; 165 struct udphdr { 166 u16 uh_sport; /* source port */ 167 u16 uh_dport; /* destination port */ 168 u16 uh_ulen; /* udp length */ 169 u16 uh_sum; /* udp checksum */ 170 } udp; 171 172 u32 i, sum; 173 struct iovec iov[4]; 174 175 /* Only know how to do ethernet on *BSD */ 176 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN) 177 { 178 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"), 179 mess->htype, ifr->ifr_name); 180 return; 181 } 182 183 ifr->ifr_addr.sa_family = AF_LINK; 184 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0) 185 return; 186 187 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN); 188 ether.ether_type = htons(ETHERTYPE_IP); 189 190 if (ntohs(mess->flags) & 0x8000) 191 { 192 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN); 193 ip.ip_dst.s_addr = INADDR_BROADCAST; 194 } 195 else 196 { 197 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN); 198 ip.ip_dst.s_addr = mess->yiaddr.s_addr; 199 } 200 201 ip.ip_p = IPPROTO_UDP; 202 ip.ip_src.s_addr = iface_addr.s_addr; 203 ip.ip_len = htons(sizeof(struct ip) + 204 sizeof(struct udphdr) + 205 len) ; 206 ip.ip_hl = sizeof(struct ip) / 4; 207 ip.ip_v = IPVERSION; 208 ip.ip_tos = 0; 209 ip.ip_id = htons(0); 210 ip.ip_off = htons(0x4000); /* don't fragment */ 211 ip.ip_ttl = IPDEFTTL; 212 ip.ip_sum = 0; 213 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++) 214 sum += ((u16 *)&ip)[i]; 215 while (sum>>16) 216 sum = (sum & 0xffff) + (sum >> 16); 217 ip.ip_sum = (sum == 0xffff) ? sum : ~sum; 218 219 udp.uh_sport = htons(daemon->dhcp_server_port); 220 udp.uh_dport = htons(daemon->dhcp_client_port); 221 if (len & 1) 222 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */ 223 udp.uh_sum = 0; 224 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len); 225 sum += htons(IPPROTO_UDP); 226 sum += ip.ip_src.s_addr & 0xffff; 227 sum += (ip.ip_src.s_addr >> 16) & 0xffff; 228 sum += ip.ip_dst.s_addr & 0xffff; 229 sum += (ip.ip_dst.s_addr >> 16) & 0xffff; 230 for (i = 0; i < sizeof(struct udphdr)/2; i++) 231 sum += ((u16 *)&udp)[i]; 232 for (i = 0; i < (len + 1) / 2; i++) 233 sum += ((u16 *)mess)[i]; 234 while (sum>>16) 235 sum = (sum & 0xffff) + (sum >> 16); 236 udp.uh_sum = (sum == 0xffff) ? sum : ~sum; 237 238 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr); 239 240 iov[0].iov_base = ðer; 241 iov[0].iov_len = sizeof(ether); 242 iov[1].iov_base = &ip; 243 iov[1].iov_len = sizeof(ip); 244 iov[2].iov_base = &udp; 245 iov[2].iov_len = sizeof(udp); 246 iov[3].iov_base = mess; 247 iov[3].iov_len = len; 248 249 while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send()); 250 } 251 252 #endif 253 254 255