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