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>