1/* $Id: getroute.c,v 1.4 2014/03/31 12:27:14 nanard Exp $ */ 2/* MiniUPnP project 3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 * (c) 2006-2013 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 <syslog.h> 10#include <unistd.h> 11#include <string.h> 12#include <sys/socket.h> 13#include <net/if.h> 14#include <net/route.h> 15#include <netinet/in.h> 16#ifdef AF_LINK 17#include <net/if_dl.h> 18#endif 19 20#include "../config.h" 21#include "../upnputils.h" 22 23 24int 25get_src_for_route_to(const struct sockaddr * dst, 26 void * src, size_t * src_len, 27 int * index) 28{ 29 int found = 0; 30 int s; 31 int l, i; 32 char * p; 33 struct sockaddr * sa; 34 struct { 35 struct rt_msghdr m_rtm; 36 char m_space[512]; 37 } m_rtmsg; 38#define rtm m_rtmsg.m_rtm 39 40 if(dst == NULL) 41 return -1; 42#ifdef __APPLE__ 43 if(dst->sa_family == AF_INET6) { 44 syslog(LOG_ERR, "Sorry, get_src_for_route_to() is known to fail with IPV6 on OS X..."); 45 return -1; 46 } 47#endif 48 s = socket(PF_ROUTE, SOCK_RAW, dst->sa_family); 49 if(s < 0) { 50 syslog(LOG_ERR, "socket(PF_ROUTE) failed : %m"); 51 return -1; 52 } 53 memset(&rtm, 0, sizeof(rtm)); 54 rtm.rtm_type = RTM_GET; 55 rtm.rtm_flags = RTF_UP; 56 rtm.rtm_version = RTM_VERSION; 57 rtm.rtm_seq = 1; 58 rtm.rtm_addrs = RTA_DST; /* destination address */ 59 memcpy(m_rtmsg.m_space, dst, sizeof(struct sockaddr)); 60 rtm.rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr); 61 if(write(s, &m_rtmsg, rtm.rtm_msglen) < 0) { 62 syslog(LOG_ERR, "write: %m"); 63 close(s); 64 return -1; 65 } 66 67 do { 68 l = read(s, &m_rtmsg, sizeof(m_rtmsg)); 69 if(l<0) { 70 syslog(LOG_ERR, "read: %m"); 71 close(s); 72 return -1; 73 } 74 syslog(LOG_DEBUG, "read l=%d seq=%d pid=%d", 75 l, rtm.rtm_seq, rtm.rtm_pid); 76 } while(l > 0 && (rtm.rtm_pid != getpid() || rtm.rtm_seq != 1)); 77 close(s); 78 p = m_rtmsg.m_space; 79 if(rtm.rtm_addrs) { 80 for(i=1; i<0x8000; i <<= 1) { 81 if(i & rtm.rtm_addrs) { 82 char tmp[256] = { 0 }; 83 sa = (struct sockaddr *)p; 84 sockaddr_to_string(sa, tmp, sizeof(tmp)); 85 syslog(LOG_DEBUG, "type=%d sa_len=%d sa_family=%d %s", 86 i, SA_LEN(sa), sa->sa_family, tmp); 87 if((i == RTA_DST || i == RTA_GATEWAY) && 88 (src_len && src)) { 89 size_t len = 0; 90 void * paddr = NULL; 91 if(sa->sa_family == AF_INET) { 92 paddr = &((struct sockaddr_in *)sa)->sin_addr; 93 len = sizeof(struct in_addr); 94 } else if(sa->sa_family == AF_INET6) { 95 paddr = &((struct sockaddr_in6 *)sa)->sin6_addr; 96 len = sizeof(struct in6_addr); 97 } 98 if(paddr) { 99 if(*src_len < len) { 100 syslog(LOG_WARNING, "cannot copy src. %u<%u", 101 (unsigned)*src_len, (unsigned)len); 102 return -1; 103 } 104 memcpy(src, paddr, len); 105 *src_len = len; 106 found = 1; 107 } 108 } 109#ifdef AF_LINK 110 if(sa->sa_family == AF_LINK) { 111 struct sockaddr_dl * sdl = (struct sockaddr_dl *)sa; 112 if(index) 113 *index = sdl->sdl_index; 114 } 115#endif 116 p += SA_LEN(sa); 117 } 118 } 119 } 120 return found ? 0 : -1; 121} 122 123