1 /*
2  * route-linux.c
3  *
4  * Copyright (c) 2000 Dug Song <dugsong@monkey.org>
5  *
6  * $Id: route-linux.c 619 2006-01-15 07:33:29Z dugsong $
7  */
8 
9 #include "config.h"
10 
11 #include <sys/types.h>
12 #include <sys/ioctl.h>
13 #include <sys/socket.h>
14 #include <sys/uio.h>
15 
16 #include <asm/types.h>
17 #include <net/if.h>
18 #include <netinet/in.h>
19 #include <linux/netlink.h>
20 #include <linux/rtnetlink.h>
21 
22 #include <net/route.h>
23 
24 #include <ctype.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include "dnet.h"
32 
33 #define ADDR_ISHOST(a)	(((a)->addr_type == ADDR_TYPE_IP &&	\
34 			  (a)->addr_bits == IP_ADDR_BITS) ||	\
35 			 ((a)->addr_type == ADDR_TYPE_IP6 &&	\
36 			  (a)->addr_bits == IP6_ADDR_BITS))
37 
38 #define PROC_ROUTE_FILE		"/proc/net/route"
39 #define PROC_IPV6_ROUTE_FILE	"/proc/net/ipv6_route"
40 
41 struct route_handle {
42 	int	 fd;
43 	int	 nlfd;
44 };
45 
46 route_t *
route_open(void)47 route_open(void)
48 {
49 	struct sockaddr_nl snl;
50 	route_t *r;
51 
52 	if ((r = calloc(1, sizeof(*r))) != NULL) {
53 		r->fd = r->nlfd = -1;
54 
55 		if ((r->fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
56 			return (route_close(r));
57 
58 		if ((r->nlfd = socket(AF_NETLINK, SOCK_RAW,
59 			 NETLINK_ROUTE)) < 0)
60 			return (route_close(r));
61 
62 		memset(&snl, 0, sizeof(snl));
63 		snl.nl_family = AF_NETLINK;
64 
65 		if (bind(r->nlfd, (struct sockaddr *)&snl, sizeof(snl)) < 0)
66 			return (route_close(r));
67 	}
68 	return (r);
69 }
70 
71 int
route_add(route_t * r,const struct route_entry * entry)72 route_add(route_t *r, const struct route_entry *entry)
73 {
74 	struct rtentry rt;
75 	struct addr dst;
76 
77 	memset(&rt, 0, sizeof(rt));
78 	rt.rt_flags = RTF_UP | RTF_GATEWAY;
79 
80 	if (ADDR_ISHOST(&entry->route_dst)) {
81 		rt.rt_flags |= RTF_HOST;
82 		memcpy(&dst, &entry->route_dst, sizeof(dst));
83 	} else
84 		addr_net(&entry->route_dst, &dst);
85 
86 	if (addr_ntos(&dst, &rt.rt_dst) < 0 ||
87 	    addr_ntos(&entry->route_gw, &rt.rt_gateway) < 0 ||
88 	    addr_btos(entry->route_dst.addr_bits, &rt.rt_genmask) < 0)
89 		return (-1);
90 
91 	return (ioctl(r->fd, SIOCADDRT, &rt));
92 }
93 
94 int
route_delete(route_t * r,const struct route_entry * entry)95 route_delete(route_t *r, const struct route_entry *entry)
96 {
97 	struct rtentry rt;
98 	struct addr dst;
99 
100 	memset(&rt, 0, sizeof(rt));
101 	rt.rt_flags = RTF_UP;
102 
103 	if (ADDR_ISHOST(&entry->route_dst)) {
104 		rt.rt_flags |= RTF_HOST;
105 		memcpy(&dst, &entry->route_dst, sizeof(dst));
106 	} else
107 		addr_net(&entry->route_dst, &dst);
108 
109 	if (addr_ntos(&dst, &rt.rt_dst) < 0 ||
110 	    addr_btos(entry->route_dst.addr_bits, &rt.rt_genmask) < 0)
111 		return (-1);
112 
113 	return (ioctl(r->fd, SIOCDELRT, &rt));
114 }
115 
116 int
route_get(route_t * r,struct route_entry * entry)117 route_get(route_t *r, struct route_entry *entry)
118 {
119 	static int seq;
120 	struct nlmsghdr *nmsg;
121 	struct rtmsg *rmsg;
122 	struct rtattr *rta;
123 	struct sockaddr_nl snl;
124 	struct iovec iov;
125 	struct msghdr msg;
126 	u_char buf[512];
127 	int i, af, alen;
128 
129 	switch (entry->route_dst.addr_type) {
130 	case ADDR_TYPE_IP:
131 		af = AF_INET;
132 		alen = IP_ADDR_LEN;
133 		break;
134 	case ADDR_TYPE_IP6:
135 		af = AF_INET6;
136 		alen = IP6_ADDR_LEN;
137 		break;
138 	default:
139 		errno = EINVAL;
140 		return (-1);
141 	}
142 	memset(buf, 0, sizeof(buf));
143 
144 	nmsg = (struct nlmsghdr *)buf;
145 	nmsg->nlmsg_len = NLMSG_LENGTH(sizeof(*nmsg)) + RTA_LENGTH(alen);
146 	nmsg->nlmsg_flags = NLM_F_REQUEST;
147 	nmsg->nlmsg_type = RTM_GETROUTE;
148 	nmsg->nlmsg_seq = ++seq;
149 
150 	rmsg = (struct rtmsg *)(nmsg + 1);
151 	rmsg->rtm_family = af;
152 	rmsg->rtm_dst_len = entry->route_dst.addr_bits;
153 
154 	rta = RTM_RTA(rmsg);
155 	rta->rta_type = RTA_DST;
156 	rta->rta_len = RTA_LENGTH(alen);
157 
158 	/* XXX - gross hack for default route */
159 	if (af == AF_INET && entry->route_dst.addr_ip == IP_ADDR_ANY) {
160 		i = htonl(0x60060606);
161 		memcpy(RTA_DATA(rta), &i, alen);
162 	} else
163 		memcpy(RTA_DATA(rta), entry->route_dst.addr_data8, alen);
164 
165 	memset(&snl, 0, sizeof(snl));
166 	snl.nl_family = AF_NETLINK;
167 
168 	iov.iov_base = nmsg;
169 	iov.iov_len = nmsg->nlmsg_len;
170 
171 	memset(&msg, 0, sizeof(msg));
172 	msg.msg_name = &snl;
173 	msg.msg_namelen = sizeof(snl);
174 	msg.msg_iov = &iov;
175 	msg.msg_iovlen = 1;
176 
177 	if (sendmsg(r->nlfd, &msg, 0) < 0)
178 		return (-1);
179 
180 	iov.iov_base = buf;
181 	iov.iov_len = sizeof(buf);
182 
183 	if ((i = recvmsg(r->nlfd, &msg, 0)) <= 0)
184 		return (-1);
185 
186 	if (nmsg->nlmsg_len < (int)sizeof(*nmsg) || nmsg->nlmsg_len > i ||
187 	    nmsg->nlmsg_seq != seq) {
188 		errno = EINVAL;
189 		return (-1);
190 	}
191 	if (nmsg->nlmsg_type == NLMSG_ERROR)
192 		return (-1);
193 
194 	i -= NLMSG_LENGTH(sizeof(*nmsg));
195 
196 	entry->route_gw.addr_type = ADDR_TYPE_NONE;
197 	entry->intf_name[0] = '\0';
198 	for (rta = RTM_RTA(rmsg); RTA_OK(rta, i); rta = RTA_NEXT(rta, i)) {
199 		if (rta->rta_type == RTA_GATEWAY) {
200 			entry->route_gw.addr_type = entry->route_dst.addr_type;
201 			memcpy(entry->route_gw.addr_data8, RTA_DATA(rta), alen);
202 			entry->route_gw.addr_bits = alen * 8;
203 		} else if (rta->rta_type == RTA_OIF) {
204 			char ifbuf[IFNAMSIZ];
205 			char *p;
206 			int intf_index;
207 
208 			intf_index = *(int *) RTA_DATA(rta);
209 			p = if_indextoname(intf_index, ifbuf);
210 			if (p == NULL)
211 				return (-1);
212 			strlcpy(entry->intf_name, ifbuf, sizeof(entry->intf_name));
213 		}
214 	}
215 	if (entry->route_gw.addr_type == ADDR_TYPE_NONE) {
216 		errno = ESRCH;
217 		return (-1);
218 	}
219 
220 	return (0);
221 }
222 
223 int
route_loop(route_t * r,route_handler callback,void * arg)224 route_loop(route_t *r, route_handler callback, void *arg)
225 {
226 	FILE *fp;
227 	struct route_entry entry;
228 	char buf[BUFSIZ];
229 	char ifbuf[16];
230 	int ret = 0;
231 
232 	if ((fp = fopen(PROC_ROUTE_FILE, "r")) != NULL) {
233 		int i, iflags, refcnt, use, metric, mss, win, irtt;
234 		uint32_t mask;
235 
236 		while (fgets(buf, sizeof(buf), fp) != NULL) {
237 			i = sscanf(buf, "%15s %X %X %X %d %d %d %X %d %d %d\n",
238 			    ifbuf, &entry.route_dst.addr_ip,
239 			    &entry.route_gw.addr_ip, &iflags, &refcnt, &use,
240 			    &metric, &mask, &mss, &win, &irtt);
241 
242 			if (i < 11 || !(iflags & RTF_UP))
243 				continue;
244 
245 			strlcpy(entry.intf_name, ifbuf, sizeof(entry.intf_name));
246 
247 			entry.route_dst.addr_type = entry.route_gw.addr_type =
248 			    ADDR_TYPE_IP;
249 
250 			if (addr_mtob(&mask, IP_ADDR_LEN,
251 				&entry.route_dst.addr_bits) < 0)
252 				continue;
253 
254 			entry.route_gw.addr_bits = IP_ADDR_BITS;
255 			entry.metric = metric;
256 
257 			if ((ret = callback(&entry, arg)) != 0)
258 				break;
259 		}
260 		fclose(fp);
261 	}
262 	if (ret == 0 && (fp = fopen(PROC_IPV6_ROUTE_FILE, "r")) != NULL) {
263 		char s[33], d[8][5], n[8][5];
264 		int i, iflags, metric;
265 		u_int slen, dlen;
266 
267 		while (fgets(buf, sizeof(buf), fp) != NULL) {
268 			i = sscanf(buf, "%04s%04s%04s%04s%04s%04s%04s%04s %02x "
269 			    "%32s %02x %04s%04s%04s%04s%04s%04s%04s%04s "
270 			    "%x %*x %*x %x %15s",
271 			    d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
272 			    &dlen, s, &slen,
273 			    n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7],
274 			    &metric, &iflags, ifbuf);
275 
276 			if (i < 21 || !(iflags & RTF_UP))
277 				continue;
278 
279 			strlcpy(entry.intf_name, ifbuf, sizeof(entry.intf_name));
280 
281 			snprintf(buf, sizeof(buf), "%s:%s:%s:%s:%s:%s:%s:%s/%d",
282 			    d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
283 			    dlen);
284 			addr_aton(buf, &entry.route_dst);
285 			snprintf(buf, sizeof(buf), "%s:%s:%s:%s:%s:%s:%s:%s/%d",
286 			    n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7],
287 			    IP6_ADDR_BITS);
288 			addr_aton(buf, &entry.route_gw);
289 			entry.metric = metric;
290 
291 			if ((ret = callback(&entry, arg)) != 0)
292 				break;
293 		}
294 		fclose(fp);
295 	}
296 	return (ret);
297 }
298 
299 route_t *
route_close(route_t * r)300 route_close(route_t *r)
301 {
302 	if (r != NULL) {
303 		if (r->fd >= 0)
304 			close(r->fd);
305 		if (r->nlfd >= 0)
306 			close(r->nlfd);
307 		free(r);
308 	}
309 	return (NULL);
310 }
311