1/* $Id: getroute.c,v 1.4 2013/02/06 10:50:04 nanard Exp $ */ 2/* MiniUPnP project 3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 * (c) 2006-2015 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/*#include <linux/in_route.h>*/ 17#include <linux/netlink.h> 18#include <linux/rtnetlink.h> 19#ifdef USE_LIBNFNETLINK 20/* define USE_LIBNFNETLINK in order to use libnfnetlink 21 * instead of custom code 22 * see https://github.com/miniupnp/miniupnp/issues/110 */ 23#include <libnfnetlink/libnfnetlink.h> 24#endif /* USE_LIBNFNETLINK */ 25 26#include "../getroute.h" 27#include "../upnputils.h" 28 29int 30get_src_for_route_to(const struct sockaddr * dst, 31 void * src, size_t * src_len, 32 int * index) 33{ 34 int fd = -1; 35 struct nlmsghdr *h; 36 int status; 37 struct { 38 struct nlmsghdr n; 39 struct rtmsg r; 40 char buf[1024]; 41 } req; 42 struct sockaddr_nl nladdr; 43 struct iovec iov = { 44 .iov_base = (void*) &req.n, 45 }; 46 struct msghdr msg = { 47 .msg_name = &nladdr, 48 .msg_namelen = sizeof(nladdr), 49 .msg_iov = &iov, 50 .msg_iovlen = 1, 51 }; 52 const struct sockaddr_in * dst4; 53 const struct sockaddr_in6 * dst6; 54#ifndef USE_LIBNFNETLINK 55 struct rtattr * rta; 56#endif /* USE_LIBNFNETLINK */ 57 58 memset(&req, 0, sizeof(req)); 59 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); 60 req.n.nlmsg_flags = NLM_F_REQUEST; 61 req.n.nlmsg_type = RTM_GETROUTE; 62 req.r.rtm_family = dst->sa_family; 63 req.r.rtm_table = 0; 64 req.r.rtm_protocol = 0; 65 req.r.rtm_scope = 0; 66 req.r.rtm_type = 0; 67 req.r.rtm_src_len = 0; 68 req.r.rtm_dst_len = 0; 69 req.r.rtm_tos = 0; 70 71 { 72 char dst_str[128]; 73 sockaddr_to_string(dst, dst_str, sizeof(dst_str)); 74 syslog(LOG_DEBUG, "get_src_for_route_to (%s)", dst_str); 75 } 76 /* add address */ 77#ifndef USE_LIBNFNETLINK 78 rta = (struct rtattr *)(((char*)&req) + NLMSG_ALIGN(req.n.nlmsg_len)); 79 rta->rta_type = RTA_DST; 80#endif /* USE_LIBNFNETLINK */ 81 if(dst->sa_family == AF_INET) { 82 dst4 = (const struct sockaddr_in *)dst; 83#ifdef USE_LIBNFNETLINK 84 nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst4->sin_addr, 4); 85#else 86 rta->rta_len = RTA_SPACE(sizeof(dst4->sin_addr)); 87 memcpy(RTA_DATA(rta), &dst4->sin_addr, sizeof(dst4->sin_addr)); 88#endif /* USE_LIBNFNETLINK */ 89 req.r.rtm_dst_len = 32; 90 } else { 91 dst6 = (const struct sockaddr_in6 *)dst; 92#ifdef USE_LIBNFNETLINK 93 nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst6->sin6_addr, 16); 94#else 95 rta->rta_len = RTA_SPACE(sizeof(dst6->sin6_addr)); 96 memcpy(RTA_DATA(rta), &dst6->sin6_addr, sizeof(dst6->sin6_addr)); 97#endif /* USE_LIBNFNETLINK */ 98 req.r.rtm_dst_len = 128; 99 } 100#ifndef USE_LIBNFNETLINK 101 req.n.nlmsg_len += rta->rta_len; 102#endif /* USE_LIBNFNETLINK */ 103 104 fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 105 if (fd < 0) { 106 syslog(LOG_ERR, "socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) : %m"); 107 return -1; 108 } 109 110 memset(&nladdr, 0, sizeof(nladdr)); 111 nladdr.nl_family = AF_NETLINK; 112 113 req.n.nlmsg_seq = 1; 114 iov.iov_len = req.n.nlmsg_len; 115 116 status = sendmsg(fd, &msg, 0); 117 118 if (status < 0) { 119 syslog(LOG_ERR, "sendmsg(rtnetlink) : %m"); 120 goto error; 121 } 122 123 memset(&req, 0, sizeof(req)); 124 125 for(;;) { 126 iov.iov_len = sizeof(req); 127 status = recvmsg(fd, &msg, 0); 128 if(status < 0) { 129 if (errno == EINTR || errno == EAGAIN) 130 continue; 131 syslog(LOG_ERR, "recvmsg(rtnetlink) %m"); 132 goto error; 133 } 134 if(status == 0) { 135 syslog(LOG_ERR, "recvmsg(rtnetlink) EOF"); 136 goto error; 137 } 138 for (h = (struct nlmsghdr*)&req.n; status >= (int)sizeof(*h); ) { 139 int len = h->nlmsg_len; 140 int l = len - sizeof(*h); 141 142 if (l<0 || len>status) { 143 if (msg.msg_flags & MSG_TRUNC) { 144 syslog(LOG_ERR, "Truncated message"); 145 } 146 syslog(LOG_ERR, "malformed message: len=%d", len); 147 goto error; 148 } 149 150 if(nladdr.nl_pid != 0 || h->nlmsg_seq != 1/*seq*/) { 151 syslog(LOG_ERR, "wrong seq = %d\n", h->nlmsg_seq); 152 /* Don't forget to skip that message. */ 153 status -= NLMSG_ALIGN(len); 154 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); 155 continue; 156 } 157 158 if(h->nlmsg_type == NLMSG_ERROR) { 159 struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); 160 syslog(LOG_ERR, "NLMSG_ERROR %d : %s", err->error, strerror(-err->error)); 161 goto error; 162 } 163 if(h->nlmsg_type == RTM_NEWROUTE) { 164 struct rtattr * rta; 165 int len = h->nlmsg_len; 166 len -= NLMSG_LENGTH(sizeof(struct rtmsg)); 167 for(rta = RTM_RTA(NLMSG_DATA((h))); RTA_OK(rta, len); rta = RTA_NEXT(rta,len)) { 168 unsigned char * data = RTA_DATA(rta); 169 if(rta->rta_type == RTA_PREFSRC) { 170 if(src_len && src) { 171 if(*src_len < RTA_PAYLOAD(rta)) { 172 syslog(LOG_WARNING, "cannot copy src: %u<%lu", 173 (unsigned)*src_len, (unsigned long)RTA_PAYLOAD(rta)); 174 goto error; 175 } 176 *src_len = RTA_PAYLOAD(rta); 177 memcpy(src, data, RTA_PAYLOAD(rta)); 178 } 179 } else if(rta->rta_type == RTA_OIF) { 180 if(index) 181 memcpy(index, data, sizeof(int)); 182 } 183 } 184 close(fd); 185 return 0; 186 } 187 status -= NLMSG_ALIGN(len); 188 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); 189 } 190 } 191 syslog(LOG_WARNING, "get_src_for_route_to() : src not found"); 192error: 193 if(fd >= 0) 194 close(fd); 195 return -1; 196} 197 198