1/* MiniUPnP project 2 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 3 * 4 * Copyright (c) 2006, Thomas Bernard 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * * The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32#include <unistd.h> 33#include <sys/ioctl.h> 34#include <sys/types.h> 35#include <sys/socket.h> 36#include <net/if.h> 37#include <arpa/inet.h> 38#include <netinet/in.h> 39#include <netdb.h> 40#include <errno.h> 41#if defined(sun) 42#include <sys/sockio.h> 43#endif 44 45#include "config.h" 46#if HAVE_GETIFADDRS 47# include <ifaddrs.h> 48# ifdef __linux__ 49# ifndef AF_LINK 50# define AF_LINK AF_INET 51# endif 52# else 53# include <net/if_dl.h> 54# endif 55# ifndef IFF_SLAVE 56# define IFF_SLAVE 0 57# endif 58#endif 59#ifdef HAVE_NETLINK 60# include <linux/rtnetlink.h> 61# include <linux/netlink.h> 62#endif 63#include "upnpglobalvars.h" 64#include "getifaddr.h" 65#include "minissdp.h" 66#include "utils.h" 67#include "log.h" 68#ifdef BCMARM 69#include "ifaddrs.c" 70#endif 71 72static int 73getifaddr(const char *ifname) 74{ 75#if HAVE_GETIFADDRS 76 struct ifaddrs *ifap, *p; 77 struct sockaddr_in *addr_in; 78 79 if (getifaddrs(&ifap) != 0) 80 { 81 DPRINTF(E_ERROR, L_GENERAL, "getifaddrs(): %s\n", strerror(errno)); 82 return -1; 83 } 84 85 for (p = ifap; p != NULL; p = p->ifa_next) 86 { 87 if (!p->ifa_addr || p->ifa_addr->sa_family != AF_INET) 88 continue; 89 if (ifname && strcmp(p->ifa_name, ifname) != 0) 90 continue; 91 addr_in = (struct sockaddr_in *)p->ifa_addr; 92 if (!ifname && (p->ifa_flags & (IFF_LOOPBACK | IFF_SLAVE))) 93 continue; 94 memcpy(&lan_addr[n_lan_addr].addr, &addr_in->sin_addr, sizeof(lan_addr[n_lan_addr].addr)); 95 if (!inet_ntop(AF_INET, &addr_in->sin_addr, lan_addr[n_lan_addr].str, sizeof(lan_addr[0].str)) ) 96 { 97 DPRINTF(E_ERROR, L_GENERAL, "inet_ntop(): %s\n", strerror(errno)); 98 continue; 99 } 100 addr_in = (struct sockaddr_in *)p->ifa_netmask; 101 memcpy(&lan_addr[n_lan_addr].mask, &addr_in->sin_addr, sizeof(lan_addr[n_lan_addr].mask)); 102 lan_addr[n_lan_addr].ifindex = if_nametoindex(p->ifa_name); 103 lan_addr[n_lan_addr].snotify = OpenAndConfSSDPNotifySocket(&lan_addr[n_lan_addr]); 104 if (lan_addr[n_lan_addr].snotify >= 0) 105 n_lan_addr++; 106 if (ifname || n_lan_addr >= MAX_LAN_ADDR) 107 break; 108 } 109 freeifaddrs(ifap); 110 if (ifname && !p) 111 { 112 DPRINTF(E_ERROR, L_GENERAL, "Network interface %s not found\n", ifname); 113 return -1; 114 } 115#else 116 int s = socket(PF_INET, SOCK_STREAM, 0); 117 struct sockaddr_in addr; 118 struct ifconf ifc; 119 struct ifreq *ifr; 120 char buf[8192]; 121 int i, n; 122 123 memset(&ifc, '\0', sizeof(ifc)); 124 ifc.ifc_buf = buf; 125 ifc.ifc_len = sizeof(buf); 126 127 if (ioctl(s, SIOCGIFCONF, &ifc) < 0) 128 { 129 DPRINTF(E_ERROR, L_GENERAL, "SIOCGIFCONF: %s\n", strerror(errno)); 130 close(s); 131 return -1; 132 } 133 134 n = ifc.ifc_len / sizeof(struct ifreq); 135 for (i = 0; i < n; i++) 136 { 137 ifr = &ifc.ifc_req[i]; 138 if (ifname && strcmp(ifr->ifr_name, ifname) != 0) 139 continue; 140 if (!ifname && 141 (ioctl(s, SIOCGIFFLAGS, ifr) < 0 || ifr->ifr_ifru.ifru_flags & IFF_LOOPBACK)) 142 continue; 143 if (ioctl(s, SIOCGIFADDR, ifr) < 0) 144 continue; 145 memcpy(&addr, &(ifr->ifr_addr), sizeof(addr)); 146 memcpy(&lan_addr[n_lan_addr].addr, &addr.sin_addr, sizeof(lan_addr[n_lan_addr].addr)); 147 if (!inet_ntop(AF_INET, &addr.sin_addr, lan_addr[n_lan_addr].str, sizeof(lan_addr[0].str))) 148 { 149 DPRINTF(E_ERROR, L_GENERAL, "inet_ntop(): %s\n", strerror(errno)); 150 close(s); 151 continue; 152 } 153 if (ioctl(s, SIOCGIFNETMASK, ifr) < 0) 154 continue; 155 memcpy(&addr, &(ifr->ifr_addr), sizeof(addr)); 156 memcpy(&lan_addr[n_lan_addr].mask, &addr.sin_addr, sizeof(addr)); 157 lan_addr[n_lan_addr].ifindex = if_nametoindex(ifr->ifr_name); 158 lan_addr[n_lan_addr].snotify = OpenAndConfSSDPNotifySocket(&lan_addr[n_lan_addr]); 159 if (lan_addr[n_lan_addr].snotify >= 0) 160 n_lan_addr++; 161 if (ifname || n_lan_addr >= MAX_LAN_ADDR) 162 break; 163 } 164 close(s); 165 if (ifname && i == n) 166 { 167 DPRINTF(E_ERROR, L_GENERAL, "Network interface %s not found\n", ifname); 168 return -1; 169 } 170#endif 171 return n_lan_addr; 172} 173 174int 175getsyshwaddr(char *buf, int len) 176{ 177 unsigned char mac[6]; 178 int ret = -1; 179#if defined(HAVE_GETIFADDRS) && !defined (__linux__) && !defined (__sun__) 180 struct ifaddrs *ifap, *p; 181 struct sockaddr_in *addr_in; 182 uint8_t a; 183 184 if (getifaddrs(&ifap) != 0) 185 { 186 DPRINTF(E_ERROR, L_GENERAL, "getifaddrs(): %s\n", strerror(errno)); 187 return -1; 188 } 189 for (p = ifap; p != NULL; p = p->ifa_next) 190 { 191 if (p->ifa_addr && p->ifa_addr->sa_family == AF_LINK) 192 { 193 addr_in = (struct sockaddr_in *)p->ifa_addr; 194 a = (htonl(addr_in->sin_addr.s_addr) >> 0x18) & 0xFF; 195 if (a == 127) 196 continue; 197#if defined(__linux__) 198 struct ifreq ifr; 199 int fd; 200 fd = socket(AF_INET, SOCK_DGRAM, 0); 201 if (fd < 0) 202 continue; 203 strncpy(ifr.ifr_name, p->ifa_name, IFNAMSIZ); 204 ret = ioctl(fd, SIOCGIFHWADDR, &ifr); 205 close(fd); 206 if (ret < 0) 207 continue; 208 memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); 209#else 210 struct sockaddr_dl *sdl; 211 sdl = (struct sockaddr_dl*)p->ifa_addr; 212 memcpy(mac, LLADDR(sdl), sdl->sdl_alen); 213#endif 214 if (MACADDR_IS_ZERO(mac)) 215 continue; 216 ret = 0; 217 break; 218 } 219 } 220 freeifaddrs(ifap); 221#else 222 struct if_nameindex *ifaces, *if_idx; 223 struct ifreq ifr; 224 int fd; 225 226 memset(&mac, '\0', sizeof(mac)); 227 /* Get the spatially unique node identifier */ 228 fd = socket(AF_INET, SOCK_DGRAM, 0); 229 if (fd < 0) 230 return ret; 231 232 ifaces = if_nameindex(); 233 if (!ifaces) 234 { 235 close(fd); 236 return ret; 237 } 238 239 for (if_idx = ifaces; if_idx->if_index; if_idx++) 240 { 241 strncpyt(ifr.ifr_name, if_idx->if_name, IFNAMSIZ); 242 if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) 243 continue; 244 if (ifr.ifr_ifru.ifru_flags & IFF_LOOPBACK) 245 continue; 246 if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) 247 continue; 248#ifdef __sun__ 249 if (MACADDR_IS_ZERO(ifr.ifr_addr.sa_data)) 250 continue; 251 memcpy(mac, ifr.ifr_addr.sa_data, 6); 252#else 253 if (MACADDR_IS_ZERO(ifr.ifr_hwaddr.sa_data)) 254 continue; 255 memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); 256#endif 257 ret = 0; 258 break; 259 } 260 if_freenameindex(ifaces); 261 close(fd); 262#endif 263 if (ret == 0) 264 { 265 if (len > 12) 266 sprintf(buf, "%02x%02x%02x%02x%02x%02x", 267 mac[0]&0xFF, mac[1]&0xFF, mac[2]&0xFF, 268 mac[3]&0xFF, mac[4]&0xFF, mac[5]&0xFF); 269 else if (len == 6) 270 memmove(buf, mac, 6); 271 } 272 return ret; 273} 274 275int 276get_remote_mac(struct in_addr ip_addr, unsigned char *mac) 277{ 278 struct in_addr arp_ent; 279 FILE * arp; 280 char remote_ip[16]; 281 int matches, hwtype, flags; 282 memset(mac, 0xFF, 6); 283 284 arp = fopen("/proc/net/arp", "r"); 285 if (!arp) 286 return 1; 287 while (!feof(arp)) 288 { 289 matches = fscanf(arp, "%15s 0x%8X 0x%8X %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", 290 remote_ip, &hwtype, &flags, 291 &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); 292 if (matches != 9) 293 continue; 294 inet_pton(AF_INET, remote_ip, &arp_ent); 295 if (ip_addr.s_addr == arp_ent.s_addr) 296 break; 297 mac[0] = 0xFF; 298 } 299 fclose(arp); 300 301 if (mac[0] == 0xFF) 302 { 303 memset(mac, 0xFF, 6); 304 return 1; 305 } 306 307 return 0; 308} 309 310void 311reload_ifaces(int force_notify) 312{ 313 struct in_addr old_addr[MAX_LAN_ADDR]; 314 int i, j; 315 316 memset(&old_addr, 0xFF, sizeof(old_addr)); 317 for (i = 0; i < n_lan_addr; i++) 318 { 319 memcpy(&old_addr[i], &lan_addr[i].addr, sizeof(struct in_addr)); 320 close(lan_addr[i].snotify); 321 } 322 n_lan_addr = 0; 323 324 i = 0; 325 do { 326 getifaddr(runtime_vars.ifaces[i]); 327 i++; 328 } while (i < MAX_LAN_ADDR && runtime_vars.ifaces[i]); 329 330 for (i = 0; i < n_lan_addr; i++) 331 { 332 for (j = 0; j < MAX_LAN_ADDR; j++) 333 { 334 if (memcmp(&lan_addr[i].addr, &old_addr[j], sizeof(struct in_addr)) == 0) 335 break; 336 } 337 /* Send out startup notifies if it's a new interface, or on SIGHUP */ 338 if (force_notify || j == MAX_LAN_ADDR) 339 { 340 DPRINTF(E_INFO, L_GENERAL, "Enabling interface %s/%s\n", 341 lan_addr[i].str, inet_ntoa(lan_addr[i].mask)); 342 SendSSDPGoodbyes(lan_addr[i].snotify); 343 SendSSDPNotifies(lan_addr[i].snotify, lan_addr[i].str, 344 runtime_vars.port, runtime_vars.notify_interval); 345 } 346 } 347} 348 349int 350OpenAndConfMonitorSocket(void) 351{ 352#ifdef HAVE_NETLINK 353 struct sockaddr_nl addr; 354 int s; 355 int ret; 356 357 s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 358 if (s < 0) 359 { 360 perror("couldn't open NETLINK_ROUTE socket"); 361 return -1; 362 } 363 364 memset(&addr, 0, sizeof(addr)); 365 addr.nl_family = AF_NETLINK; 366 addr.nl_groups = RTMGRP_IPV4_IFADDR; 367 368 ret = bind(s, (struct sockaddr*)&addr, sizeof(addr)); 369 if (ret < 0) 370 { 371 perror("couldn't bind"); 372 close(s); 373 return -1; 374 } 375 376 return s; 377#else 378 return -1; 379#endif 380} 381 382void 383ProcessMonitorEvent(int s) 384{ 385#ifdef HAVE_NETLINK 386 int len; 387 char buf[4096]; 388 struct nlmsghdr *nlh; 389 int changed = 0; 390 391 nlh = (struct nlmsghdr*)buf; 392 393 len = recv(s, nlh, sizeof(buf), 0); 394 if (len <= 0) 395 return; 396 while ((NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE)) 397 { 398 if (nlh->nlmsg_type == RTM_NEWADDR || 399 nlh->nlmsg_type == RTM_DELADDR) 400 { 401 changed = 1; 402 } 403 nlh = NLMSG_NEXT(nlh, len); 404 } 405 if (changed) 406 reload_ifaces(0); 407#endif 408} 409