1 /* $Id: ifacewatch.c,v 1.16 2015/09/03 18:31:25 nanard Exp $ */
2 /* MiniUPnP project
3  * (c) 2011-2018 Thomas Bernard
4  * website : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
5  * This software is subject to the conditions detailed
6  * in the LICENCE file provided within the distribution */
7 #include "config.h"
8 
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #include <arpa/inet.h>
17 #include <net/if.h>
18 #ifdef __linux__
19 #include <linux/netlink.h>
20 #include <linux/rtnetlink.h>
21 #else	/* __linux__ */
22 #include <net/route.h>
23 #ifdef AF_LINK
24 #include <net/if_dl.h>
25 #endif
26 #endif	/* __linux__ */
27 #include <syslog.h>
28 #include <inttypes.h>
29 
30 #include "config.h"
31 #include "openssdpsocket.h"
32 #include "upnputils.h"
33 #include "minissdpdtypes.h"
34 
35 extern struct lan_addr_list lan_addrs;
36 
37 #ifndef __linux__
38 #if defined(__OpenBSD__) || defined(__FreeBSD__)
39 #define SALIGN (sizeof(long) - 1)
40 #else
41 #define SALIGN (sizeof(int32_t) - 1)
42 #endif
43 #define SA_RLEN(sa) (SA_LEN(sa) ? ((SA_LEN(sa) + SALIGN) & ~SALIGN) : (SALIGN + 1))
44 #endif
45 
46 int
OpenAndConfInterfaceWatchSocket(void)47 OpenAndConfInterfaceWatchSocket(void)
48 {
49 	int s;
50 #ifdef __linux__
51 	struct sockaddr_nl addr;
52 
53 	s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
54 #else	/* __linux__*/
55 	/*s = socket(PF_ROUTE, SOCK_RAW, AF_INET);*/
56 	s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
57 /* The family parameter may be AF_UNSPEC which will provide routing informa-
58  * tion for all address families, or can be restricted to a specific address
59  * family by specifying which one is desired.  There can be more than one
60  * routing socket open per system. */
61 #endif
62 	if(s < 0) {
63 		syslog(LOG_ERR, "%s socket: %m",
64 		       "OpenAndConfInterfaceWatchSocket");
65 		return -1;
66 	}
67 	if(!set_non_blocking(s)) {
68 		syslog(LOG_WARNING, "%s failed to set socket non blocking : %m",
69 		       "OpenAndConfInterfaceWatchSocket");
70 	}
71 #ifdef __linux__
72 	memset(&addr, 0, sizeof(addr));
73 	addr.nl_family = AF_NETLINK;
74 	addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
75 
76 	if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
77 		syslog(LOG_ERR, "bind(netlink): %m");
78 		close(s);
79 		return -1;
80 	}
81 #endif
82 	return s;
83 }
84 
85 /**
86  * Process the message and add/drop multicast membership if needed
87  */
88 int
ProcessInterfaceWatch(int s,int s_ssdp,int s_ssdp6)89 ProcessInterfaceWatch(int s, int s_ssdp, int s_ssdp6)
90 {
91 	struct lan_addr_s * lan_addr;
92 	ssize_t len;
93 	char buffer[4096];
94 #ifdef __linux__
95 	struct iovec iov;
96 	struct msghdr hdr;
97 	struct nlmsghdr *nlhdr;
98 	struct ifaddrmsg *ifa;
99 	struct rtattr *rta;
100 	int ifa_len;
101 
102 #ifndef ENABLE_IPV6
103 	(void)s_ssdp6;
104 #endif
105 	iov.iov_base = buffer;
106 	iov.iov_len = sizeof(buffer);
107 
108 	memset(&hdr, 0, sizeof(hdr));
109 	hdr.msg_iov = &iov;
110 	hdr.msg_iovlen = 1;
111 
112 	len = recvmsg(s, &hdr, 0);
113 	if(len < 0) {
114 		syslog(LOG_ERR, "recvmsg(s, &hdr, 0): %m");
115 		return -1;
116 	}
117 
118 	for(nlhdr = (struct nlmsghdr *)buffer;
119 		NLMSG_OK(nlhdr, len);
120 		nlhdr = NLMSG_NEXT(nlhdr, len)) {
121 		int is_del = 0;
122 		char address[48];
123 		char ifname[IFNAMSIZ];
124 		address[0] = '\0';
125 		ifname[0] = '\0';
126 		if(nlhdr->nlmsg_type == NLMSG_DONE)
127 			break;
128 		switch(nlhdr->nlmsg_type) {
129 		/* case RTM_NEWLINK: */
130 		/* case RTM_DELLINK: */
131 		case RTM_DELADDR:
132 			is_del = 1;
133 		case RTM_NEWADDR:
134 			/* http://linux-hacks.blogspot.fr/2009/01/sample-code-to-learn-netlink.html */
135 			ifa = (struct ifaddrmsg *)NLMSG_DATA(nlhdr);
136 			rta = (struct rtattr *)IFA_RTA(ifa);
137 			ifa_len = IFA_PAYLOAD(nlhdr);
138 			syslog(LOG_DEBUG, "%s %s index=%d fam=%d prefixlen=%d flags=%d scope=%d",
139 			       "ProcessInterfaceWatchNotify", is_del ? "RTM_DELADDR" : "RTM_NEWADDR",
140 			       ifa->ifa_index, ifa->ifa_family, ifa->ifa_prefixlen,
141 			       ifa->ifa_flags, ifa->ifa_scope);
142 			for(;RTA_OK(rta, ifa_len); rta = RTA_NEXT(rta, ifa_len)) {
143 				/*RTA_DATA(rta)*/
144 				/*rta_type : IFA_ADDRESS, IFA_LOCAL, etc. */
145 				char tmp[128];
146 				memset(tmp, 0, sizeof(tmp));
147 				switch(rta->rta_type) {
148 				case IFA_ADDRESS:
149 				case IFA_LOCAL:
150 				case IFA_BROADCAST:
151 				case IFA_ANYCAST:
152 					inet_ntop(ifa->ifa_family, RTA_DATA(rta), tmp, sizeof(tmp));
153 					if(rta->rta_type == IFA_ADDRESS)
154 						strncpy(address, tmp, sizeof(address));
155 					break;
156 				case IFA_LABEL:
157 					strncpy(tmp, RTA_DATA(rta), sizeof(tmp));
158 					strncpy(ifname, tmp, sizeof(ifname));
159 					break;
160 				case IFA_CACHEINFO:
161 					{
162 						struct ifa_cacheinfo *cache_info;
163 						cache_info = RTA_DATA(rta);
164 						snprintf(tmp, sizeof(tmp), "valid=%u preferred=%u",
165 						         cache_info->ifa_valid, cache_info->ifa_prefered);
166 					}
167 					break;
168 				default:
169 					strncpy(tmp, "*unknown*", sizeof(tmp));
170 				}
171 				syslog(LOG_DEBUG, " rta_len=%d rta_type=%d '%s'", rta->rta_len, rta->rta_type, tmp);
172 			}
173 			syslog(LOG_INFO, "%s: %s/%d %s",
174 			       is_del ? "RTM_DELADDR" : "RTM_NEWADDR",
175 			       address, ifa->ifa_prefixlen, ifname);
176 			for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) {
177 #ifdef ENABLE_IPV6
178 				if((0 == strcmp(address, lan_addr->str)) ||
179 				   (0 == strcmp(ifname, lan_addr->ifname)) ||
180 				   (ifa->ifa_index == lan_addr->index)) {
181 #else
182 				if((0 == strcmp(address, lan_addr->str)) ||
183 				   (0 == strcmp(ifname, lan_addr->ifname))) {
184 #endif
185 					if(ifa->ifa_family == AF_INET)
186 						AddDropMulticastMembership(s_ssdp, lan_addr, 0, is_del);
187 #ifdef ENABLE_IPV6
188 					else if(ifa->ifa_family == AF_INET6)
189 						AddDropMulticastMembership(s_ssdp6, lan_addr, 1, is_del);
190 #endif
191 					break;
192 				}
193 			}
194 			break;
195 		default:
196 			syslog(LOG_DEBUG, "unknown nlmsg_type=%d", nlhdr->nlmsg_type);
197 		}
198 	}
199 #else /* __linux__ */
200 	struct rt_msghdr * rtm;
201 	struct ifa_msghdr * ifam;
202 	int is_del = 0;
203 	char tmp[64];
204 	char * p;
205 	struct sockaddr * sa;
206 	int addr;
207 	char address[48];
208 	char ifname[IFNAMSIZ];
209 	int family = AF_UNSPEC;
210 	int prefixlen = 0;
211 
212 #ifndef ENABLE_IPV6
213 	(void)s_ssdp6;
214 #endif
215 	address[0] = '\0';
216 	ifname[0] = '\0';
217 
218 	len = recv(s, buffer, sizeof(buffer), 0);
219 	if(len < 0) {
220 		syslog(LOG_ERR, "%s recv: %m", "ProcessInterfaceWatchNotify");
221 		return -1;
222 	}
223 	rtm = (struct rt_msghdr *)buffer;
224 	switch(rtm->rtm_type) {
225 	case RTM_DELADDR:
226 		is_del = 1;
227 	case RTM_NEWADDR:
228 		ifam = (struct ifa_msghdr *)buffer;
229 		syslog(LOG_DEBUG, "%s %s len=%d/%hu index=%hu addrs=%x flags=%x",
230 		       "ProcessInterfaceWatchNotify", is_del?"RTM_DELADDR":"RTM_NEWADDR",
231 		       (int)len, ifam->ifam_msglen,
232 		       ifam->ifam_index, ifam->ifam_addrs, ifam->ifam_flags);
233 		p = buffer + sizeof(struct ifa_msghdr);
234 		addr = 1;
235 		while(p < buffer + len) {
236 			sa = (struct sockaddr *)p;
237 			while(!(addr & ifam->ifam_addrs) && (addr <= ifam->ifam_addrs))
238 				addr = addr << 1;
239 			sockaddr_to_string(sa, tmp, sizeof(tmp));
240 			syslog(LOG_DEBUG, " %s", tmp);
241 			switch(addr) {
242 			case RTA_DST:
243 			case RTA_GATEWAY:
244 				break;
245 			case RTA_NETMASK:
246 				if(sa->sa_family == AF_INET
247 #if defined(__OpenBSD__)
248 				   || (sa->sa_family == 0 &&
249 				       sa->sa_len <= sizeof(struct sockaddr_in))
250 #endif
251 				   ) {
252 					uint32_t sin_addr = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
253 					while((prefixlen < 32) &&
254 					      ((sin_addr & (1 << (31 - prefixlen))) != 0))
255 						prefixlen++;
256 				} else if(sa->sa_family == AF_INET6
257 #if defined(__OpenBSD__)
258 				          || (sa->sa_family == 0 &&
259 				              sa->sa_len == sizeof(struct sockaddr_in6))
260 #endif
261 				          ) {
262 					int i = 0;
263 					uint8_t * q =  ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr;
264 					while((*q == 0xff) && (i < 16)) {
265 						prefixlen += 8;
266 						q++; i++;
267 					}
268 					if(i < 16) {
269 						i = 0;
270 						while((i < 8) &&
271 						      ((*q & (1 << (7 - i))) != 0))
272 							i++;
273 						prefixlen += i;
274 					}
275 				}
276 				break;
277 			case RTA_GENMASK:
278 				break;
279 			case RTA_IFP:
280 #ifdef AF_LINK
281 				if(sa->sa_family == AF_LINK) {
282 					struct sockaddr_dl * sdl = (struct sockaddr_dl *)sa;
283 					memset(ifname, 0, sizeof(ifname));
284 					memcpy(ifname, sdl->sdl_data, sdl->sdl_nlen);
285 				}
286 #endif
287 				break;
288 			case RTA_IFA:
289 				family = sa->sa_family;
290 				if(sa->sa_family == AF_INET) {
291 					inet_ntop(sa->sa_family,
292 					          &((struct sockaddr_in *)sa)->sin_addr,
293 					          address, sizeof(address));
294 				} else if(sa->sa_family == AF_INET6) {
295 					inet_ntop(sa->sa_family,
296 					          &((struct sockaddr_in6 *)sa)->sin6_addr,
297 					          address, sizeof(address));
298 				}
299 				break;
300 			case RTA_AUTHOR:
301 				break;
302 			case RTA_BRD:
303 				break;
304 			}
305 #if 0
306 			syslog(LOG_DEBUG, " %d.%d.%d.%d %02x%02x%02x%02x",
307 			       (uint8_t)p[0], (uint8_t)p[1], (uint8_t)p[2], (uint8_t)p[3],
308 			       (uint8_t)p[0], (uint8_t)p[1], (uint8_t)p[2], (uint8_t)p[3]);
309 			syslog(LOG_DEBUG, " %d.%d.%d.%d %02x%02x%02x%02x",
310 			       (uint8_t)p[4], (uint8_t)p[5], (uint8_t)p[6], (uint8_t)p[7],
311 			       (uint8_t)p[4], (uint8_t)p[5], (uint8_t)p[6], (uint8_t)p[7]);
312 #endif
313 			p += SA_RLEN(sa);
314 			addr = addr << 1;
315 		}
316 		syslog(LOG_INFO, "%s: %s/%d %s",
317 		       is_del ? "RTM_DELADDR" : "RTM_NEWADDR",
318 		       address, prefixlen, ifname);
319 		for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) {
320 #ifdef ENABLE_IPV6
321 			if((0 == strcmp(address, lan_addr->str)) ||
322 			   (0 == strcmp(ifname, lan_addr->ifname)) ||
323 			   (ifam->ifam_index == lan_addr->index)) {
324 #else
325 			if((0 == strcmp(address, lan_addr->str)) ||
326 			   (0 == strcmp(ifname, lan_addr->ifname))) {
327 #endif
328 				if(family == AF_INET)
329 					AddDropMulticastMembership(s_ssdp, lan_addr, 0, is_del);
330 #ifdef ENABLE_IPV6
331 				else if(family == AF_INET6)
332 					AddDropMulticastMembership(s_ssdp6, lan_addr, 1, is_del);
333 #endif
334 				break;
335 			}
336 		}
337 		break;
338 	default:
339 		syslog(LOG_DEBUG, "Unknown RTM message : rtm->rtm_type=%d len=%d",
340 		       rtm->rtm_type, (int)len);
341 	}
342 #endif
343 	return 0;
344 }
345 
346