1 /* $Id: getroute.c,v 1.6 2015/04/26 14:43:28 nanard Exp $ */
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 */
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"
30 get_src_for_route_to(const struct sockaddr
* dst
,
31 void * src
, size_t * src_len
,
42 struct sockaddr_nl nladdr
;
44 .iov_base
= (void*) &req
.n
,
48 .msg_namelen
= sizeof(nladdr
),
52 const struct sockaddr_in
* dst4
;
53 const struct sockaddr_in6
* dst6
;
54 #ifndef USE_LIBNFNETLINK
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
;
64 req
.r
.rtm_protocol
= 0;
67 req
.r
.rtm_src_len
= 0;
68 req
.r
.rtm_dst_len
= 0;
73 sockaddr_to_string(dst
, dst_str
, sizeof(dst_str
));
74 syslog(LOG_DEBUG
, "get_src_for_route_to (%s)", dst_str
);
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);
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;
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);
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
);
106 syslog(LOG_ERR
, "socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) : %m");
110 memset(&nladdr
, 0, sizeof(nladdr
));
111 nladdr
.nl_family
= AF_NETLINK
;
114 iov
.iov_len
= req
.n
.nlmsg_len
;
116 status
= sendmsg(fd
, &msg
, 0);
119 syslog(LOG_ERR
, "sendmsg(rtnetlink) : %m");
123 memset(&req
, 0, sizeof(req
));
126 iov
.iov_len
= sizeof(req
);
127 status
= recvmsg(fd
, &msg
, 0);
129 if (errno
== EINTR
|| errno
== EAGAIN
)
131 syslog(LOG_ERR
, "recvmsg(rtnetlink) %m");
135 syslog(LOG_ERR
, "recvmsg(rtnetlink) EOF");
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
);
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
));
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
));
163 if(h
->nlmsg_type
== RTM_NEWROUTE
) {
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
) {
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
));
176 *src_len
= RTA_PAYLOAD(rta
);
177 memcpy(src
, data
, RTA_PAYLOAD(rta
));
179 } else if(rta
->rta_type
== RTA_OIF
) {
181 memcpy(index
, data
, sizeof(int));
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");