File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / minissdpd / getroute.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: getroute.c,v 1.1.1.1 2023/09/27 11:25:11 misho Exp $ */
    2: /* MiniUPnP project
    3:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
    4:  * (c) 2006-2014 Thomas Bernard
    5:  * This software is subject to the conditions detailed
    6:  * in the LICENCE file provided within the distribution */
    7: 
    8: #include <stdio.h>
    9: #include <string.h>
   10: #include <unistd.h>
   11: #include <errno.h>
   12: #include <syslog.h>
   13: #include <sys/types.h>
   14: #include <sys/socket.h>
   15: #include <netinet/in.h>
   16: #ifdef __linux__
   17: /*#include <linux/in_route.h>*/
   18: #include <linux/netlink.h>
   19: #include <linux/rtnetlink.h>
   20: #include <libnfnetlink/libnfnetlink.h>
   21: #else /* __linux__ */
   22: #include <net/if.h>
   23: #include <net/route.h>
   24: #include <netinet/in.h>
   25: #ifdef AF_LINK
   26: #include <net/if_dl.h>
   27: #endif /* AF_LINK */
   28: #endif /* __linux__ */
   29: 
   30: #include "getroute.h"
   31: #include "upnputils.h"
   32: #include "config.h"
   33: 
   34: /* get_src_for_route_to() function is only called in code
   35:  * enabled with ENABLE_IPV6 */
   36: #ifdef ENABLE_IPV6
   37: 
   38: int
   39: get_src_for_route_to(const struct sockaddr * dst,
   40:                      void * src, size_t * src_len,
   41:                      int * index)
   42: {
   43: #if __linux__
   44: 	int fd = -1;
   45: 	struct nlmsghdr *h;
   46: 	int status;
   47: 	struct {
   48: 		struct nlmsghdr n;
   49: 		struct rtmsg r;
   50: 		char buf[1024];
   51: 	} req;
   52: 	struct sockaddr_nl nladdr;
   53: 	struct iovec iov = {
   54: 		.iov_base = (void*) &req.n,
   55: 	};
   56: 	struct msghdr msg = {
   57: 		.msg_name = &nladdr,
   58: 		.msg_namelen = sizeof(nladdr),
   59: 		.msg_iov = &iov,
   60: 		.msg_iovlen = 1,
   61: 	};
   62: 	const struct sockaddr_in * dst4;
   63: 	const struct sockaddr_in6 * dst6;
   64: 
   65: 	memset(&req, 0, sizeof(req));
   66: 	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
   67: 	req.n.nlmsg_flags = NLM_F_REQUEST;
   68: 	req.n.nlmsg_type = RTM_GETROUTE;
   69: 	req.r.rtm_family = dst->sa_family;
   70: 	req.r.rtm_table = 0;
   71: 	req.r.rtm_protocol = 0;
   72: 	req.r.rtm_scope = 0;
   73: 	req.r.rtm_type = 0;
   74: 	req.r.rtm_src_len = 0;
   75: 	req.r.rtm_dst_len = 0;
   76: 	req.r.rtm_tos = 0;
   77: 
   78: 	{
   79: 		char dst_str[128];
   80: 		sockaddr_to_string(dst, dst_str, sizeof(dst_str));
   81: 		syslog(LOG_DEBUG, "get_src_for_route_to (%s)", dst_str);
   82: 	}
   83: 	/* add address */
   84: 	if(dst->sa_family == AF_INET) {
   85: 		dst4 = (const struct sockaddr_in *)dst;
   86: 		nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst4->sin_addr, 4);
   87: 		req.r.rtm_dst_len = 32;
   88: 	} else {
   89: 		dst6 = (const struct sockaddr_in6 *)dst;
   90: 		nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst6->sin6_addr, 16);
   91: 		req.r.rtm_dst_len = 128;
   92: 	}
   93: 
   94: 	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
   95: 	if (fd < 0) {
   96: 		syslog(LOG_ERR, "socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) : %m");
   97: 		return -1;
   98: 	}
   99: 
  100: 	memset(&nladdr, 0, sizeof(nladdr));
  101: 	nladdr.nl_family = AF_NETLINK;
  102: 
  103: 	req.n.nlmsg_seq = 1;
  104: 	iov.iov_len = req.n.nlmsg_len;
  105: 
  106: 	status = sendmsg(fd, &msg, 0);
  107: 
  108: 	if (status < 0) {
  109: 		syslog(LOG_ERR, "sendmsg(rtnetlink) : %m");
  110: 		goto error;
  111: 	}
  112: 
  113: 	memset(&req, 0, sizeof(req));
  114: 
  115: 	for(;;) {
  116: 		iov.iov_len = sizeof(req);
  117: 		status = recvmsg(fd, &msg, 0);
  118: 		if(status < 0) {
  119: 			if (errno == EINTR || errno == EAGAIN)
  120: 				continue;
  121: 			syslog(LOG_ERR, "recvmsg(rtnetlink) %m");
  122: 			goto error;
  123: 		}
  124: 		if(status == 0) {
  125: 			syslog(LOG_ERR, "recvmsg(rtnetlink) EOF");
  126: 			goto error;
  127: 		}
  128: 		for (h = (struct nlmsghdr*)&req.n; status >= (int)sizeof(*h); ) {
  129: 			int len = h->nlmsg_len;
  130: 			int l = len - sizeof(*h);
  131: 
  132: 			if (l<0 || len>status) {
  133: 				if (msg.msg_flags & MSG_TRUNC) {
  134: 					syslog(LOG_ERR, "Truncated message");
  135: 				}
  136: 				syslog(LOG_ERR, "malformed message: len=%d", len);
  137: 				goto error;
  138: 			}
  139: 
  140: 			if(nladdr.nl_pid != 0 || h->nlmsg_seq != 1/*seq*/) {
  141: 				syslog(LOG_ERR, "wrong seq = %d\n", h->nlmsg_seq);
  142: 				/* Don't forget to skip that message. */
  143: 				status -= NLMSG_ALIGN(len);
  144: 				h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
  145: 				continue;
  146: 			}
  147: 
  148: 			if(h->nlmsg_type == NLMSG_ERROR) {
  149: 				struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
  150: 				syslog(LOG_ERR, "NLMSG_ERROR %d : %s", err->error, strerror(-err->error));
  151: 				goto error;
  152: 			}
  153: 			if(h->nlmsg_type == RTM_NEWROUTE) {
  154: 				struct rtattr * rta;
  155: 				int len = h->nlmsg_len;
  156: 				len -= NLMSG_LENGTH(sizeof(struct rtmsg));
  157: 				for(rta = RTM_RTA(NLMSG_DATA((h))); RTA_OK(rta, len); rta = RTA_NEXT(rta,len)) {
  158: 					unsigned char * data = RTA_DATA(rta);
  159: 					if(rta->rta_type == RTA_PREFSRC) {
  160: 						if(src_len && src) {
  161: 							if(*src_len < RTA_PAYLOAD(rta)) {
  162: 								syslog(LOG_WARNING, "cannot copy src: %u<%lu",
  163: 								       (unsigned)*src_len, (unsigned long)RTA_PAYLOAD(rta));
  164: 								goto error;
  165: 							}
  166: 							*src_len = RTA_PAYLOAD(rta);
  167: 							memcpy(src, data, RTA_PAYLOAD(rta));
  168: 						}
  169: 					} else if(rta->rta_type == RTA_OIF) {
  170: 						if(index)
  171: 							memcpy(index, data, sizeof(int));
  172: 					}
  173: 				}
  174: 				close(fd);
  175: 				return 0;
  176: 			}
  177: 			status -= NLMSG_ALIGN(len);
  178: 			h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
  179: 		}
  180: 	}
  181: 	syslog(LOG_WARNING, "get_src_for_route_to() : src not found");
  182: error:
  183: 	if(fd >= 0)
  184: 		close(fd);
  185: 	return -1;
  186: #else /* __linux__ */
  187: 	int found = 0;
  188: 	int s;
  189: 	int l, i;
  190: 	char * p;
  191: 	struct sockaddr * sa;
  192: 	struct {
  193: 	  struct rt_msghdr m_rtm;
  194: 	  char       m_space[512];
  195: 	} m_rtmsg;
  196: #define rtm m_rtmsg.m_rtm
  197: 
  198: 	if(dst == NULL)
  199: 		return -1;
  200: #ifdef __APPLE__
  201: 	if(dst->sa_family == AF_INET6) {
  202: 		syslog(LOG_ERR, "Sorry, get_src_for_route_to() is known to fail with IPV6 on OS X...");
  203: 		return -1;
  204: 	}
  205: #endif
  206: 	s = socket(PF_ROUTE, SOCK_RAW, dst->sa_family);
  207: 	if(s < 0) {
  208: 		syslog(LOG_ERR, "socket(PF_ROUTE) failed : %m");
  209: 		return -1;
  210: 	}
  211: 	memset(&rtm, 0, sizeof(rtm));
  212: 	rtm.rtm_type = RTM_GET;
  213: 	rtm.rtm_flags = RTF_UP;
  214: 	rtm.rtm_version = RTM_VERSION;
  215: 	rtm.rtm_seq = 1;
  216: 	rtm.rtm_addrs = RTA_DST;	/* destination address */
  217: 	memcpy(m_rtmsg.m_space, dst, sizeof(struct sockaddr));
  218: 	rtm.rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr);
  219: 	if(write(s, &m_rtmsg, rtm.rtm_msglen) < 0) {
  220: 		syslog(LOG_ERR, "write: %m");
  221: 		close(s);
  222: 		return -1;
  223: 	}
  224: 
  225: 	do {
  226: 		l = read(s, &m_rtmsg, sizeof(m_rtmsg));
  227: 		if(l<0) {
  228: 			syslog(LOG_ERR, "read: %m");
  229: 			close(s);
  230: 			return -1;
  231: 		}
  232: 		syslog(LOG_DEBUG, "read l=%d seq=%d pid=%d",
  233: 		       l, rtm.rtm_seq, rtm.rtm_pid);
  234: 	} while(l > 0 && (rtm.rtm_pid != getpid() || rtm.rtm_seq != 1));
  235: 	close(s);
  236: 	p = m_rtmsg.m_space;
  237: 	if(rtm.rtm_addrs) {
  238: 		for(i=1; i<0x8000; i <<= 1) {
  239: 			if(i & rtm.rtm_addrs) {
  240: 				char tmp[256] = { 0 };
  241: 				sa = (struct sockaddr *)p;
  242: 				sockaddr_to_string(sa, tmp, sizeof(tmp));
  243: 				syslog(LOG_DEBUG, "type=%d sa_len=%d sa_family=%d %s",
  244: 				       i, SA_LEN(sa), sa->sa_family, tmp);
  245: 				if((i == RTA_DST || i == RTA_GATEWAY) &&
  246: 				   (src_len && src)) {
  247: 					size_t len = 0;
  248: 					void * paddr = NULL;
  249: 					if(sa->sa_family == AF_INET) {
  250: 						paddr = &((struct sockaddr_in *)sa)->sin_addr;
  251: 						len = sizeof(struct in_addr);
  252: 					} else if(sa->sa_family == AF_INET6) {
  253: 						paddr = &((struct sockaddr_in6 *)sa)->sin6_addr;
  254: 						len = sizeof(struct in6_addr);
  255: 					}
  256: 					if(paddr) {
  257: 						if(*src_len < len) {
  258: 							syslog(LOG_WARNING, "cannot copy src. %u<%u",
  259: 							       (unsigned)*src_len, (unsigned)len);
  260: 							return -1;
  261: 						}
  262: 						memcpy(src, paddr, len);
  263: 						*src_len = len;
  264: 						found = 1;
  265: 					}
  266: 				}
  267: #ifdef AF_LINK
  268: 				if(sa->sa_family == AF_LINK) {
  269: 					struct sockaddr_dl * sdl = (struct sockaddr_dl *)sa;
  270: 					if(index)
  271: 						*index = sdl->sdl_index;
  272: 				}
  273: #endif
  274: 				p += SA_LEN(sa);
  275: 			}
  276: 		}
  277: 	}
  278: 	return found ? 0 : -1;
  279: #endif /* __linux__ */
  280: }
  281: 
  282: #endif /* ENABLE_IPV6 */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>