Annotation of embedaddon/miniupnpd/minissdpd/ifacewatch.c, revision 1.1
1.1 ! misho 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
! 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>