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