1: /* $Id: ifacewatch.c,v 1.1.1.1 2023/09/27 11:25:11 misho 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
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
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:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>