Wrap up version 1.3.3.
[minidlna.git] / getifaddr.c
blob2d9474c2d7111394e12205955a38220b3dde85be
1 /* MiniUPnP project
2 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * Copyright (c) 2006, Thomas Bernard
5 * All rights reserved.
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.
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.
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
45 #include "config.h"
46 #include "event.h"
47 #if HAVE_GETIFADDRS
48 # include <ifaddrs.h>
49 # ifdef __linux__
50 # ifndef AF_LINK
51 # define AF_LINK AF_INET
52 # endif
53 # else
54 # include <net/if_dl.h>
55 # endif
56 # ifndef IFF_SLAVE
57 # define IFF_SLAVE 0
58 # endif
59 #endif
60 #ifdef HAVE_NETLINK
61 # include <linux/rtnetlink.h>
62 # include <linux/netlink.h>
63 #endif
64 #include "upnpglobalvars.h"
65 #include "getifaddr.h"
66 #include "minissdp.h"
67 #include "utils.h"
68 #include "log.h"
70 static int
71 getifaddr(const char *ifname)
73 #if HAVE_GETIFADDRS
74 struct ifaddrs *ifap, *p;
75 struct sockaddr_in *addr_in;
77 if (getifaddrs(&ifap) != 0)
79 DPRINTF(E_ERROR, L_GENERAL, "getifaddrs(): %s\n", strerror(errno));
80 return -1;
83 for (p = ifap; p != NULL; p = p->ifa_next)
85 if (!p->ifa_addr || p->ifa_addr->sa_family != AF_INET)
86 continue;
87 if (ifname && strcmp(p->ifa_name, ifname) != 0)
88 continue;
89 addr_in = (struct sockaddr_in *)p->ifa_addr;
90 if (!ifname && (p->ifa_flags & (IFF_LOOPBACK | IFF_SLAVE)))
91 continue;
92 memcpy(&lan_addr[n_lan_addr].addr, &addr_in->sin_addr, sizeof(lan_addr[n_lan_addr].addr));
93 if (!inet_ntop(AF_INET, &addr_in->sin_addr, lan_addr[n_lan_addr].str, sizeof(lan_addr[0].str)) )
95 DPRINTF(E_ERROR, L_GENERAL, "inet_ntop(): %s\n", strerror(errno));
96 continue;
98 addr_in = (struct sockaddr_in *)p->ifa_netmask;
99 memcpy(&lan_addr[n_lan_addr].mask, &addr_in->sin_addr, sizeof(lan_addr[n_lan_addr].mask));
100 lan_addr[n_lan_addr].ifindex = if_nametoindex(p->ifa_name);
101 lan_addr[n_lan_addr].snotify = OpenAndConfSSDPNotifySocket(&lan_addr[n_lan_addr]);
102 if (lan_addr[n_lan_addr].snotify >= 0)
103 n_lan_addr++;
104 if (ifname || n_lan_addr >= MAX_LAN_ADDR)
105 break;
107 freeifaddrs(ifap);
108 if (ifname && !p)
110 DPRINTF(E_ERROR, L_GENERAL, "Network interface %s not found\n", ifname);
111 return -1;
113 #else
114 int s = socket(PF_INET, SOCK_STREAM, 0);
115 struct sockaddr_in addr;
116 struct ifconf ifc;
117 struct ifreq *ifr;
118 char buf[8192];
119 int i, n;
121 memset(&ifc, '\0', sizeof(ifc));
122 ifc.ifc_buf = buf;
123 ifc.ifc_len = sizeof(buf);
125 if (ioctl(s, SIOCGIFCONF, &ifc) < 0)
127 DPRINTF(E_ERROR, L_GENERAL, "SIOCGIFCONF: %s\n", strerror(errno));
128 close(s);
129 return -1;
132 n = ifc.ifc_len / sizeof(struct ifreq);
133 for (i = 0; i < n; i++)
135 ifr = &ifc.ifc_req[i];
136 if (ifname && strcmp(ifr->ifr_name, ifname) != 0)
137 continue;
138 if (!ifname &&
139 (ioctl(s, SIOCGIFFLAGS, ifr) < 0 || ifr->ifr_ifru.ifru_flags & IFF_LOOPBACK))
140 continue;
141 if (ioctl(s, SIOCGIFADDR, ifr) < 0)
142 continue;
143 memcpy(&addr, &(ifr->ifr_addr), sizeof(addr));
144 memcpy(&lan_addr[n_lan_addr].addr, &addr.sin_addr, sizeof(lan_addr[n_lan_addr].addr));
145 if (!inet_ntop(AF_INET, &addr.sin_addr, lan_addr[n_lan_addr].str, sizeof(lan_addr[0].str)))
147 DPRINTF(E_ERROR, L_GENERAL, "inet_ntop(): %s\n", strerror(errno));
148 close(s);
149 continue;
151 if (ioctl(s, SIOCGIFNETMASK, ifr) < 0)
152 continue;
153 memcpy(&addr, &(ifr->ifr_addr), sizeof(addr));
154 memcpy(&lan_addr[n_lan_addr].mask, &addr.sin_addr, sizeof(addr));
155 lan_addr[n_lan_addr].ifindex = if_nametoindex(ifr->ifr_name);
156 lan_addr[n_lan_addr].snotify = OpenAndConfSSDPNotifySocket(&lan_addr[n_lan_addr]);
157 if (lan_addr[n_lan_addr].snotify >= 0)
158 n_lan_addr++;
159 if (ifname || n_lan_addr >= MAX_LAN_ADDR)
160 break;
162 close(s);
163 if (ifname && i == n)
165 DPRINTF(E_ERROR, L_GENERAL, "Network interface %s not found\n", ifname);
166 return -1;
168 #endif
169 return n_lan_addr;
173 getsyshwaddr(char *buf, int len)
175 unsigned char mac[6];
176 int ret = -1;
177 #if defined(HAVE_GETIFADDRS) && !defined (__linux__) && !defined (__sun__)
178 struct ifaddrs *ifap, *p;
179 struct sockaddr_in *addr_in;
180 uint8_t a;
182 if (getifaddrs(&ifap) != 0)
184 DPRINTF(E_ERROR, L_GENERAL, "getifaddrs(): %s\n", strerror(errno));
185 return -1;
187 for (p = ifap; p != NULL; p = p->ifa_next)
189 if (p->ifa_addr && p->ifa_addr->sa_family == AF_LINK)
191 addr_in = (struct sockaddr_in *)p->ifa_addr;
192 a = (htonl(addr_in->sin_addr.s_addr) >> 0x18) & 0xFF;
193 if (a == 127)
194 continue;
195 #if defined(__linux__)
196 struct ifreq ifr;
197 int fd;
198 fd = socket(AF_INET, SOCK_DGRAM, 0);
199 if (fd < 0)
200 continue;
201 strncpy(ifr.ifr_name, p->ifa_name, IFNAMSIZ);
202 ret = ioctl(fd, SIOCGIFHWADDR, &ifr);
203 close(fd);
204 if (ret < 0)
205 continue;
206 memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
207 #else
208 if (p->ifa_addr->sa_family != AF_LINK)
209 continue;
210 struct sockaddr_dl *sdl;
211 sdl = (struct sockaddr_dl*)p->ifa_addr;
212 if (sdl->sdl_alen != 6)
213 continue;
214 memcpy(mac, LLADDR(sdl), 6);
215 #endif
216 if (MACADDR_IS_ZERO(mac))
217 continue;
218 ret = 0;
219 break;
222 freeifaddrs(ifap);
223 #else
224 struct if_nameindex *ifaces, *if_idx;
225 struct ifreq ifr;
226 int fd;
228 memset(&mac, '\0', sizeof(mac));
229 /* Get the spatially unique node identifier */
230 fd = socket(AF_INET, SOCK_DGRAM, 0);
231 if (fd < 0)
232 return ret;
234 ifaces = if_nameindex();
235 if (!ifaces)
237 close(fd);
238 return ret;
241 for (if_idx = ifaces; if_idx->if_index; if_idx++)
243 strncpyt(ifr.ifr_name, if_idx->if_name, IFNAMSIZ);
244 if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)
245 continue;
246 if (ifr.ifr_ifru.ifru_flags & IFF_LOOPBACK)
247 continue;
248 if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0)
249 continue;
250 #ifdef __sun__
251 if (MACADDR_IS_ZERO(ifr.ifr_addr.sa_data))
252 continue;
253 memcpy(mac, ifr.ifr_addr.sa_data, 6);
254 #else
255 if (MACADDR_IS_ZERO(ifr.ifr_hwaddr.sa_data))
256 continue;
257 memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
258 #endif
259 ret = 0;
260 break;
262 if_freenameindex(ifaces);
263 close(fd);
264 #endif
265 if (ret == 0)
267 if (len > 12)
268 sprintf(buf, "%02x%02x%02x%02x%02x%02x",
269 mac[0]&0xFF, mac[1]&0xFF, mac[2]&0xFF,
270 mac[3]&0xFF, mac[4]&0xFF, mac[5]&0xFF);
271 else if (len == 6)
272 memmove(buf, mac, 6);
274 return ret;
278 get_remote_mac(struct in_addr ip_addr, unsigned char *mac)
280 struct in_addr arp_ent;
281 FILE * arp;
282 char remote_ip[16];
283 int matches, hwtype, flags;
284 memset(mac, 0xFF, 6);
286 arp = fopen("/proc/net/arp", "r");
287 if (!arp)
288 return 1;
289 while (!feof(arp))
291 matches = fscanf(arp, "%15s 0x%8X 0x%8X %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx",
292 remote_ip, &hwtype, &flags,
293 &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
294 if (matches != 9)
295 continue;
296 inet_pton(AF_INET, remote_ip, &arp_ent);
297 if (ip_addr.s_addr == arp_ent.s_addr)
298 break;
299 mac[0] = 0xFF;
301 fclose(arp);
303 if (mac[0] == 0xFF)
305 memset(mac, 0xFF, 6);
306 return 1;
309 return 0;
312 void
313 reload_ifaces(int force_notify)
315 struct in_addr old_addr[MAX_LAN_ADDR];
316 int i, j;
318 memset(&old_addr, 0xFF, sizeof(old_addr));
319 for (i = 0; i < n_lan_addr; i++)
321 memcpy(&old_addr[i], &lan_addr[i].addr, sizeof(struct in_addr));
322 close(lan_addr[i].snotify);
324 n_lan_addr = 0;
326 i = 0;
327 do {
328 getifaddr(runtime_vars.ifaces[i]);
329 i++;
330 } while (i < MAX_LAN_ADDR && runtime_vars.ifaces[i]);
332 for (i = 0; i < n_lan_addr; i++)
334 for (j = 0; j < MAX_LAN_ADDR; j++)
336 if (memcmp(&lan_addr[i].addr, &old_addr[j], sizeof(struct in_addr)) == 0)
337 break;
339 /* Send out startup notifies if it's a new interface, or on SIGHUP */
340 if (force_notify || j == MAX_LAN_ADDR)
342 DPRINTF(E_INFO, L_GENERAL, "Enabling interface %s/%s\n",
343 lan_addr[i].str, inet_ntoa(lan_addr[i].mask));
344 SendSSDPGoodbyes(lan_addr[i].snotify);
345 SendSSDPNotifies(lan_addr[i].snotify, lan_addr[i].str,
346 runtime_vars.port, runtime_vars.notify_interval);
352 OpenAndConfMonitorSocket(void)
354 #ifdef HAVE_NETLINK
355 struct sockaddr_nl addr;
356 int s;
357 int ret;
359 s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
360 if (s < 0)
362 perror("couldn't open NETLINK_ROUTE socket");
363 return -1;
366 memset(&addr, 0, sizeof(addr));
367 addr.nl_family = AF_NETLINK;
368 addr.nl_groups = RTMGRP_IPV4_IFADDR;
370 ret = bind(s, (struct sockaddr*)&addr, sizeof(addr));
371 if (ret < 0)
373 perror("couldn't bind");
374 close(s);
375 return -1;
378 return s;
379 #else
380 return -1;
381 #endif
384 void
385 ProcessMonitorEvent(struct event *ev)
387 #ifdef HAVE_NETLINK
388 int s = ev->fd;
389 int len;
390 char buf[4096];
391 struct nlmsghdr *nlh;
392 int changed = 0;
394 nlh = (struct nlmsghdr*)buf;
396 len = recv(s, nlh, sizeof(buf), 0);
397 if (len <= 0)
398 return;
399 while ((NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE))
401 if (nlh->nlmsg_type == RTM_NEWADDR ||
402 nlh->nlmsg_type == RTM_DELADDR)
404 changed = 1;
406 nlh = NLMSG_NEXT(nlh, len);
408 if (changed)
409 reload_ifaces(0);
410 #endif