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 */
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"
32 get_src_for_route_to(const struct sockaddr
* dst
,
33 void * src
, size_t * src_len
,
44 struct sockaddr_nl nladdr
;
46 .iov_base
= (void*) &req
.n
,
50 .msg_namelen
= sizeof(nladdr
),
54 const struct sockaddr_in
* dst4
;
55 const struct sockaddr_in6
* dst6
;
56 #ifndef USE_LIBNFNETLINK
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
;
66 req
.r
.rtm_protocol
= 0;
69 req
.r
.rtm_src_len
= 0;
70 req
.r
.rtm_dst_len
= 0;
75 sockaddr_to_string(dst
, dst_str
, sizeof(dst_str
));
76 syslog(LOG_DEBUG
, "get_src_for_route_to (%s)", dst_str
);
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);
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;
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);
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
);
108 syslog(LOG_ERR
, "socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) : %m");
112 memset(&nladdr
, 0, sizeof(nladdr
));
113 nladdr
.nl_family
= AF_NETLINK
;
116 iov
.iov_len
= req
.n
.nlmsg_len
;
118 status
= sendmsg(fd
, &msg
, 0);
121 syslog(LOG_ERR
, "sendmsg(rtnetlink) : %m");
125 memset(&req
, 0, sizeof(req
));
128 iov
.iov_len
= sizeof(req
);
129 status
= recvmsg(fd
, &msg
, 0);
131 if (errno
== EINTR
|| errno
== EAGAIN
)
133 syslog(LOG_ERR
, "recvmsg(rtnetlink) %m");
137 syslog(LOG_ERR
, "recvmsg(rtnetlink) EOF");
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
);
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
));
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
));
165 if(h
->nlmsg_type
== RTM_NEWROUTE
) {
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
) {
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
));
178 *src_len
= RTA_PAYLOAD(rta
);
179 memcpy(src
, data
, RTA_PAYLOAD(rta
));
181 } else if(rta
->rta_type
== RTA_OIF
) {
183 memcpy(index
, data
, sizeof(int));
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");
200 #endif /* UPNP_STRICT */