1: /* $Id: getroute.c,v 1.1.1.1 2013/07/22 00:32:35 misho Exp $ */
2: /* MiniUPnP project
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 */
7:
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: #include <libnfnetlink/libnfnetlink.h>
20:
21: #include "../getroute.h"
22: #include "../upnputils.h"
23:
24: int
25: get_src_for_route_to(const struct sockaddr * dst,
26: void * src, size_t * src_len,
27: int * index)
28: {
29: int fd = -1;
30: struct nlmsghdr *h;
31: int status;
32: struct {
33: struct nlmsghdr n;
34: struct rtmsg r;
35: char buf[1024];
36: } req;
37: struct sockaddr_nl nladdr;
38: struct iovec iov = {
39: .iov_base = (void*) &req.n,
40: };
41: struct msghdr msg = {
42: .msg_name = &nladdr,
43: .msg_namelen = sizeof(nladdr),
44: .msg_iov = &iov,
45: .msg_iovlen = 1,
46: };
47: const struct sockaddr_in * dst4;
48: const struct sockaddr_in6 * dst6;
49:
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;
55: req.r.rtm_table = 0;
56: req.r.rtm_protocol = 0;
57: req.r.rtm_scope = 0;
58: req.r.rtm_type = 0;
59: req.r.rtm_src_len = 0;
60: req.r.rtm_dst_len = 0;
61: req.r.rtm_tos = 0;
62:
63: {
64: char dst_str[128];
65: sockaddr_to_string(dst, dst_str, sizeof(dst_str));
66: syslog(LOG_DEBUG, "get_src_for_route_to (%s)", dst_str);
67: }
68: /* add address */
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;
73: } else {
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;
77: }
78:
79: fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
80: if (fd < 0) {
81: syslog(LOG_ERR, "socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) : %m");
82: return -1;
83: }
84:
85: memset(&nladdr, 0, sizeof(nladdr));
86: nladdr.nl_family = AF_NETLINK;
87:
88: req.n.nlmsg_seq = 1;
89: iov.iov_len = req.n.nlmsg_len;
90:
91: status = sendmsg(fd, &msg, 0);
92:
93: if (status < 0) {
94: syslog(LOG_ERR, "sendmsg(rtnetlink) : %m");
95: goto error;
96: }
97:
98: memset(&req, 0, sizeof(req));
99:
100: for(;;) {
101: iov.iov_len = sizeof(req);
102: status = recvmsg(fd, &msg, 0);
103: if(status < 0) {
104: if (errno == EINTR || errno == EAGAIN)
105: continue;
106: syslog(LOG_ERR, "recvmsg(rtnetlink) %m");
107: goto error;
108: }
109: if(status == 0) {
110: syslog(LOG_ERR, "recvmsg(rtnetlink) EOF");
111: goto error;
112: }
113: for (h = (struct nlmsghdr*)&req.n; status >= (int)sizeof(*h); ) {
114: int len = h->nlmsg_len;
115: int l = len - sizeof(*h);
116:
117: if (l<0 || len>status) {
118: if (msg.msg_flags & MSG_TRUNC) {
119: syslog(LOG_ERR, "Truncated message");
120: }
121: syslog(LOG_ERR, "malformed message: len=%d", len);
122: goto error;
123: }
124:
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));
130: continue;
131: }
132:
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));
136: goto error;
137: }
138: if(h->nlmsg_type == RTM_NEWROUTE) {
139: struct rtattr * rta;
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) {
145: if(src_len && src) {
146: if(*src_len < RTA_PAYLOAD(rta)) {
147: syslog(LOG_WARNING, "cannot copy src: %u<%lu",
148: (unsigned)*src_len, RTA_PAYLOAD(rta));
149: goto error;
150: }
151: *src_len = RTA_PAYLOAD(rta);
152: memcpy(src, data, RTA_PAYLOAD(rta));
153: }
154: } else if(rta->rta_type == RTA_OIF) {
155: if(index)
156: memcpy(index, data, sizeof(int));
157: }
158: }
159: close(fd);
160: return 0;
161: }
162: status -= NLMSG_ALIGN(len);
163: h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
164: }
165: }
166: syslog(LOG_WARNING, "get_src_for_route_to() : src not found");
167: error:
168: if(fd >= 0)
169: close(fd);
170: return -1;
171: }
172:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>