miniupnpd 1.9 (20160113)
[tomato.git] / release / src / router / miniupnpd / linux / getroute.c
blob1b29804e049023ae93b00cc1f5e54b3855c1ef54
1 /* $Id: getroute.c,v 1.6 2015/04/26 14:43:28 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 */
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 */
26 #include "../getroute.h"
27 #include "../upnputils.h"
29 int
30 get_src_for_route_to(const struct sockaddr * dst,
31 void * src, size_t * src_len,
32 int * index)
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,
46 struct msghdr msg = {
47 .msg_name = &nladdr,
48 .msg_namelen = sizeof(nladdr),
49 .msg_iov = &iov,
50 .msg_iovlen = 1,
52 const struct sockaddr_in * dst4;
53 const struct sockaddr_in6 * dst6;
54 #ifndef USE_LIBNFNETLINK
55 struct rtattr * rta;
56 #endif /* USE_LIBNFNETLINK */
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;
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);
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;
100 #ifndef USE_LIBNFNETLINK
101 req.n.nlmsg_len += rta->rta_len;
102 #endif /* USE_LIBNFNETLINK */
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;
110 memset(&nladdr, 0, sizeof(nladdr));
111 nladdr.nl_family = AF_NETLINK;
113 req.n.nlmsg_seq = 1;
114 iov.iov_len = req.n.nlmsg_len;
116 status = sendmsg(fd, &msg, 0);
118 if (status < 0) {
119 syslog(LOG_ERR, "sendmsg(rtnetlink) : %m");
120 goto error;
123 memset(&req, 0, sizeof(req));
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;
134 if(status == 0) {
135 syslog(LOG_ERR, "recvmsg(rtnetlink) EOF");
136 goto error;
138 for (h = (struct nlmsghdr*)&req.n; status >= (int)sizeof(*h); ) {
139 int len = h->nlmsg_len;
140 int l = len - sizeof(*h);
142 if (l<0 || len>status) {
143 if (msg.msg_flags & MSG_TRUNC) {
144 syslog(LOG_ERR, "Truncated message");
146 syslog(LOG_ERR, "malformed message: len=%d", len);
147 goto error;
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;
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;
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;
176 *src_len = RTA_PAYLOAD(rta);
177 memcpy(src, data, RTA_PAYLOAD(rta));
179 } else if(rta->rta_type == RTA_OIF) {
180 if(index)
181 memcpy(index, data, sizeof(int));
184 close(fd);
185 return 0;
187 status -= NLMSG_ALIGN(len);
188 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
191 syslog(LOG_WARNING, "get_src_for_route_to() : src not found");
192 error:
193 if(fd >= 0)
194 close(fd);
195 return -1;