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