1 /* $Id: getroute.c,v 1.4 2013/02/06 10:50:04 nanard Exp $ */
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2013 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
13 #include <linux/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 #include <libnfnetlink/libnfnetlink.h>
21 #include "../getroute.h"
22 #include "../upnputils.h"
25 get_src_for_route_to(const struct sockaddr
* dst
,
26 void * src
, size_t * src_len
,
37 struct sockaddr_nl nladdr
;
39 .iov_base
= (void*) &req
.n
,
43 .msg_namelen
= sizeof(nladdr
),
47 const struct sockaddr_in
* dst4
;
48 const struct sockaddr_in6
* dst6
;
50 memset(&req
, 0, sizeof(req
));
51 req
.n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct rtmsg
));
52 req
.n
.nlmsg_flags
= NLM_F_REQUEST
;
53 req
.n
.nlmsg_type
= RTM_GETROUTE
;
54 req
.r
.rtm_family
= dst
->sa_family
;
56 req
.r
.rtm_protocol
= 0;
59 req
.r
.rtm_src_len
= 0;
60 req
.r
.rtm_dst_len
= 0;
65 sockaddr_to_string(dst
, dst_str
, sizeof(dst_str
));
66 syslog(LOG_DEBUG
, "get_src_for_route_to (%s)", dst_str
);
69 if(dst
->sa_family
== AF_INET
) {
70 dst4
= (const struct sockaddr_in
*)dst
;
71 nfnl_addattr_l(&req
.n
, sizeof(req
), RTA_DST
, &dst4
->sin_addr
, 4);
72 req
.r
.rtm_dst_len
= 32;
74 dst6
= (const struct sockaddr_in6
*)dst
;
75 nfnl_addattr_l(&req
.n
, sizeof(req
), RTA_DST
, &dst6
->sin6_addr
, 16);
76 req
.r
.rtm_dst_len
= 128;
79 fd
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
81 syslog(LOG_ERR
, "socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) : %m");
85 memset(&nladdr
, 0, sizeof(nladdr
));
86 nladdr
.nl_family
= AF_NETLINK
;
89 iov
.iov_len
= req
.n
.nlmsg_len
;
91 status
= sendmsg(fd
, &msg
, 0);
94 syslog(LOG_ERR
, "sendmsg(rtnetlink) : %m");
98 memset(&req
, 0, sizeof(req
));
101 iov
.iov_len
= sizeof(req
);
102 status
= recvmsg(fd
, &msg
, 0);
104 if (errno
== EINTR
|| errno
== EAGAIN
)
106 syslog(LOG_ERR
, "recvmsg(rtnetlink) %m");
110 syslog(LOG_ERR
, "recvmsg(rtnetlink) EOF");
113 for (h
= (struct nlmsghdr
*)&req
.n
; status
>= (int)sizeof(*h
); ) {
114 int len
= h
->nlmsg_len
;
115 int l
= len
- sizeof(*h
);
117 if (l
<0 || len
>status
) {
118 if (msg
.msg_flags
& MSG_TRUNC
) {
119 syslog(LOG_ERR
, "Truncated message");
121 syslog(LOG_ERR
, "malformed message: len=%d", len
);
125 if(nladdr
.nl_pid
!= 0 || h
->nlmsg_seq
!= 1/*seq*/) {
126 syslog(LOG_ERR
, "wrong seq = %d\n", h
->nlmsg_seq
);
127 /* Don't forget to skip that message. */
128 status
-= NLMSG_ALIGN(len
);
129 h
= (struct nlmsghdr
*)((char*)h
+ NLMSG_ALIGN(len
));
133 if(h
->nlmsg_type
== NLMSG_ERROR
) {
134 struct nlmsgerr
*err
= (struct nlmsgerr
*)NLMSG_DATA(h
);
135 syslog(LOG_ERR
, "NLMSG_ERROR %d : %s", err
->error
, strerror(-err
->error
));
138 if(h
->nlmsg_type
== RTM_NEWROUTE
) {
140 int len
= h
->nlmsg_len
;
141 len
-= NLMSG_LENGTH(sizeof(struct rtmsg
));
142 for(rta
= RTM_RTA(NLMSG_DATA((h
))); RTA_OK(rta
, len
); rta
= RTA_NEXT(rta
,len
)) {
143 unsigned char * data
= RTA_DATA(rta
);
144 if(rta
->rta_type
== RTA_PREFSRC
) {
146 if(*src_len
< RTA_PAYLOAD(rta
)) {
147 syslog(LOG_WARNING
, "cannot copy src: %u<%lu",
148 (unsigned)*src_len
, RTA_PAYLOAD(rta
));
151 *src_len
= RTA_PAYLOAD(rta
);
152 memcpy(src
, data
, RTA_PAYLOAD(rta
));
154 } else if(rta
->rta_type
== RTA_OIF
) {
156 memcpy(index
, data
, sizeof(int));
162 status
-= NLMSG_ALIGN(len
);
163 h
= (struct nlmsghdr
*)((char*)h
+ NLMSG_ALIGN(len
));
166 syslog(LOG_WARNING
, "get_src_for_route_to() : src not found");