File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / minissdpd / ifacewatch.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Sep 27 11:25:11 2023 UTC (14 months, 4 weeks ago) by misho
Branches: miniupnpd, MAIN
CVS tags: v2_3_3p0, HEAD
Version 2.3.3p0

    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>