blob: d7900d62e41181e96148f7c0cb00aaad6d57c2b0 [file] [log] [blame]
Glenn L McGrath9a2d2722002-11-10 01:33:55 +00001/*
2 * iproute.c "ip route".
3 *
Bernhard Reutner-Fischerab187822005-10-26 10:47:26 +00004 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Glenn L McGrath9a2d2722002-11-10 01:33:55 +00005 *
6 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
7 *
8 *
9 * Changes:
10 *
11 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
12 * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
13 */
14
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000015#include <sys/socket.h>
Glenn L McGrath275be872002-12-16 07:37:21 +000016
17#include <stdlib.h>
18#include <string.h>
Glenn L McGrath4a4c6772003-02-15 11:50:33 +000019#include <fcntl.h>
20#include <unistd.h>
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000021
22#include "rt_names.h"
23#include "utils.h"
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000024
Glenn L McGrath275be872002-12-16 07:37:21 +000025#include "libbb.h"
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000026
27#ifndef RTAX_RTTVAR
28#define RTAX_RTTVAR RTAX_HOPS
29#endif
30
31
32static struct
33{
34 int tb;
Glenn L McGrath4a4c6772003-02-15 11:50:33 +000035 int flushed;
36 char *flushb;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000037 int flushp;
38 int flushe;
39 struct rtnl_handle *rth;
40 int protocol, protocolmask;
41 int scope, scopemask;
42 int type, typemask;
43 int tos, tosmask;
44 int iif, iifmask;
45 int oif, oifmask;
46 int realm, realmmask;
47 inet_prefix rprefsrc;
48 inet_prefix rvia;
49 inet_prefix rdst;
50 inet_prefix mdst;
51 inet_prefix rsrc;
52 inet_prefix msrc;
53} filter;
54
Glenn L McGrath4a4c6772003-02-15 11:50:33 +000055static int flush_update(void)
56{
57 if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
58 perror("Failed to send flush request\n");
59 return -1;
60 }
61 filter.flushp = 0;
62 return 0;
63}
64
Glenn L McGrathc82f2322002-12-02 00:35:23 +000065static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000066{
67 FILE *fp = (FILE*)arg;
68 struct rtmsg *r = NLMSG_DATA(n);
69 int len = n->nlmsg_len;
70 struct rtattr * tb[RTA_MAX+1];
71 char abuf[256];
Glenn L McGrathfbf0b8a2003-04-26 02:22:19 +000072 inet_prefix dst;
73 inet_prefix src;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000074 int host_len = -1;
75 SPRINT_BUF(b1);
Eric Andersenc7bda1c2004-03-15 08:29:22 +000076
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000077
78 if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
79 fprintf(stderr, "Not a route: %08x %08x %08x\n",
80 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
81 return 0;
82 }
Glenn L McGrath4a4c6772003-02-15 11:50:33 +000083 if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
84 return 0;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000085 len -= NLMSG_LENGTH(sizeof(*r));
86 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +000087 bb_error_msg("wrong nlmsg len %d", len);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000088 return -1;
89 }
90
91 if (r->rtm_family == AF_INET6)
92 host_len = 128;
93 else if (r->rtm_family == AF_INET)
94 host_len = 32;
95
96 if (r->rtm_family == AF_INET6) {
97 if (filter.tb) {
98 if (filter.tb < 0) {
Glenn L McGrath16528552002-11-28 11:17:19 +000099 if (!(r->rtm_flags&RTM_F_CLONED)) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000100 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000101 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000102 } else {
Glenn L McGrath16528552002-11-28 11:17:19 +0000103 if (r->rtm_flags&RTM_F_CLONED) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000104 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000105 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000106 if (filter.tb == RT_TABLE_LOCAL) {
Glenn L McGrath16528552002-11-28 11:17:19 +0000107 if (r->rtm_type != RTN_LOCAL) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000108 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000109 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000110 } else if (filter.tb == RT_TABLE_MAIN) {
Glenn L McGrath16528552002-11-28 11:17:19 +0000111 if (r->rtm_type == RTN_LOCAL) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000112 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000113 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000114 } else {
115 return 0;
116 }
117 }
118 }
119 } else {
Glenn L McGrath16528552002-11-28 11:17:19 +0000120 if (filter.tb > 0 && filter.tb != r->rtm_table) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000121 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000122 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000123 }
124 if (filter.rdst.family &&
Glenn L McGrath16528552002-11-28 11:17:19 +0000125 (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000126 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000127 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000128 if (filter.mdst.family &&
129 (r->rtm_family != filter.mdst.family ||
Glenn L McGrath16528552002-11-28 11:17:19 +0000130 (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000131 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000132 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000133 if (filter.rsrc.family &&
Glenn L McGrath16528552002-11-28 11:17:19 +0000134 (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000135 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000136 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000137 if (filter.msrc.family &&
138 (r->rtm_family != filter.msrc.family ||
Glenn L McGrath16528552002-11-28 11:17:19 +0000139 (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000140 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000141 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000142
143 memset(tb, 0, sizeof(tb));
144 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
145
Glenn L McGrathfbf0b8a2003-04-26 02:22:19 +0000146 if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen))
147 return 0;
148 if (filter.mdst.family && filter.mdst.bitlen >= 0 &&
149 inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len))
150 return 0;
151
152 if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen))
153 return 0;
154 if (filter.msrc.family && filter.msrc.bitlen >= 0 &&
155 inet_addr_match(&src, &filter.msrc, r->rtm_src_len))
156 return 0;
157
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000158 if (filter.flushb &&
159 r->rtm_family == AF_INET6 &&
160 r->rtm_dst_len == 0 &&
161 r->rtm_type == RTN_UNREACHABLE &&
162 tb[RTA_PRIORITY] &&
163 *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1)
164 return 0;
165
166 if (filter.flushb) {
167 struct nlmsghdr *fn;
168 if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
169 if (flush_update())
170 return -1;
171 }
172 fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
173 memcpy(fn, n, n->nlmsg_len);
174 fn->nlmsg_type = RTM_DELROUTE;
175 fn->nlmsg_flags = NLM_F_REQUEST;
176 fn->nlmsg_seq = ++filter.rth->seq;
177 filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
178 filter.flushed++;
179 return 0;
180 }
181
Glenn L McGrath16528552002-11-28 11:17:19 +0000182 if (n->nlmsg_type == RTM_DELROUTE) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000183 fprintf(fp, "Deleted ");
Glenn L McGrath16528552002-11-28 11:17:19 +0000184 }
185 if (r->rtm_type != RTN_UNICAST && !filter.type) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000186 fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
Glenn L McGrath16528552002-11-28 11:17:19 +0000187 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000188
189 if (tb[RTA_DST]) {
190 if (r->rtm_dst_len != host_len) {
191 fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family,
192 RTA_PAYLOAD(tb[RTA_DST]),
193 RTA_DATA(tb[RTA_DST]),
194 abuf, sizeof(abuf)),
195 r->rtm_dst_len
196 );
197 } else {
198 fprintf(fp, "%s ", format_host(r->rtm_family,
199 RTA_PAYLOAD(tb[RTA_DST]),
200 RTA_DATA(tb[RTA_DST]),
201 abuf, sizeof(abuf))
202 );
203 }
204 } else if (r->rtm_dst_len) {
205 fprintf(fp, "0/%d ", r->rtm_dst_len);
206 } else {
207 fprintf(fp, "default ");
208 }
209 if (tb[RTA_SRC]) {
210 if (r->rtm_src_len != host_len) {
211 fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family,
212 RTA_PAYLOAD(tb[RTA_SRC]),
213 RTA_DATA(tb[RTA_SRC]),
214 abuf, sizeof(abuf)),
215 r->rtm_src_len
216 );
217 } else {
218 fprintf(fp, "from %s ", format_host(r->rtm_family,
219 RTA_PAYLOAD(tb[RTA_SRC]),
220 RTA_DATA(tb[RTA_SRC]),
221 abuf, sizeof(abuf))
222 );
223 }
224 } else if (r->rtm_src_len) {
225 fprintf(fp, "from 0/%u ", r->rtm_src_len);
226 }
227 if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000228 fprintf(fp, "via %s ",
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000229 format_host(r->rtm_family,
230 RTA_PAYLOAD(tb[RTA_GATEWAY]),
231 RTA_DATA(tb[RTA_GATEWAY]),
232 abuf, sizeof(abuf)));
233 }
Glenn L McGrath16528552002-11-28 11:17:19 +0000234 if (tb[RTA_OIF] && filter.oifmask != -1) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000235 fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
Glenn L McGrath16528552002-11-28 11:17:19 +0000236 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000237
238 if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
239 /* Do not use format_host(). It is our local addr
240 and symbolic name will not be useful.
241 */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000242 fprintf(fp, " src %s ",
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000243 rt_addr_n2a(r->rtm_family,
244 RTA_PAYLOAD(tb[RTA_PREFSRC]),
245 RTA_DATA(tb[RTA_PREFSRC]),
246 abuf, sizeof(abuf)));
247 }
Glenn L McGrath16528552002-11-28 11:17:19 +0000248 if (tb[RTA_PRIORITY]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000249 fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY]));
Glenn L McGrath16528552002-11-28 11:17:19 +0000250 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000251 if (r->rtm_family == AF_INET6) {
252 struct rta_cacheinfo *ci = NULL;
Glenn L McGrath16528552002-11-28 11:17:19 +0000253 if (tb[RTA_CACHEINFO]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000254 ci = RTA_DATA(tb[RTA_CACHEINFO]);
Glenn L McGrath16528552002-11-28 11:17:19 +0000255 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000256 if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
257 static int hz;
Glenn L McGrath16528552002-11-28 11:17:19 +0000258 if (!hz) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000259 hz = get_hz();
Glenn L McGrath16528552002-11-28 11:17:19 +0000260 }
261 if (r->rtm_flags & RTM_F_CLONED) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000262 fprintf(fp, "%s cache ", _SL_);
Glenn L McGrath16528552002-11-28 11:17:19 +0000263 }
264 if (ci->rta_expires) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000265 fprintf(fp, " expires %dsec", ci->rta_expires/hz);
Glenn L McGrath16528552002-11-28 11:17:19 +0000266 }
267 if (ci->rta_error != 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000268 fprintf(fp, " error %d", ci->rta_error);
Glenn L McGrath16528552002-11-28 11:17:19 +0000269 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000270 } else if (ci) {
271 if (ci->rta_error != 0)
272 fprintf(fp, " error %d", ci->rta_error);
273 }
274 }
275 if (tb[RTA_IIF] && filter.iifmask != -1) {
276 fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
277 }
278 fprintf(fp, "\n");
279 fflush(fp);
280 return 0;
281}
282
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000283static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000284{
285 struct rtnl_handle rth;
286 struct {
287 struct nlmsghdr n;
288 struct rtmsg r;
289 char buf[1024];
290 } req;
291 char mxbuf[256];
292 struct rtattr * mxrta = (void*)mxbuf;
293 unsigned mxlock = 0;
294 char *d = NULL;
295 int gw_ok = 0;
296 int dst_ok = 0;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000297 int proto_ok = 0;
298 int type_ok = 0;
299
300 memset(&req, 0, sizeof(req));
301
302 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
303 req.n.nlmsg_flags = NLM_F_REQUEST|flags;
304 req.n.nlmsg_type = cmd;
305 req.r.rtm_family = preferred_family;
306 req.r.rtm_table = RT_TABLE_MAIN;
307 req.r.rtm_scope = RT_SCOPE_NOWHERE;
308
309 if (cmd != RTM_DELROUTE) {
310 req.r.rtm_protocol = RTPROT_BOOT;
311 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
312 req.r.rtm_type = RTN_UNICAST;
313 }
314
315 mxrta->rta_type = RTA_METRICS;
316 mxrta->rta_len = RTA_LENGTH(0);
317
318 while (argc > 0) {
319 if (strcmp(*argv, "src") == 0) {
320 inet_prefix addr;
321 NEXT_ARG();
322 get_addr(&addr, *argv, req.r.rtm_family);
Glenn L McGrath16528552002-11-28 11:17:19 +0000323 if (req.r.rtm_family == AF_UNSPEC) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000324 req.r.rtm_family = addr.family;
Glenn L McGrath16528552002-11-28 11:17:19 +0000325 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000326 addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
327 } else if (strcmp(*argv, "via") == 0) {
328 inet_prefix addr;
329 gw_ok = 1;
330 NEXT_ARG();
331 get_addr(&addr, *argv, req.r.rtm_family);
Glenn L McGrath16528552002-11-28 11:17:19 +0000332 if (req.r.rtm_family == AF_UNSPEC) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000333 req.r.rtm_family = addr.family;
Glenn L McGrath16528552002-11-28 11:17:19 +0000334 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000335 addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
336 } else if (strcmp(*argv, "mtu") == 0) {
337 unsigned mtu;
338 NEXT_ARG();
339 if (strcmp(*argv, "lock") == 0) {
340 mxlock |= (1<<RTAX_MTU);
341 NEXT_ARG();
342 }
Glenn L McGrath16528552002-11-28 11:17:19 +0000343 if (get_unsigned(&mtu, *argv, 0)) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000344 invarg("\"mtu\" value is invalid\n", *argv);
Glenn L McGrath16528552002-11-28 11:17:19 +0000345 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000346 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
347 } else if (matches(*argv, "protocol") == 0) {
348 int prot;
349 NEXT_ARG();
350 if (rtnl_rtprot_a2n(&prot, *argv))
351 invarg("\"protocol\" value is invalid\n", *argv);
352 req.r.rtm_protocol = prot;
353 proto_ok =1;
354 } else if (strcmp(*argv, "dev") == 0 ||
355 strcmp(*argv, "oif") == 0) {
356 NEXT_ARG();
357 d = *argv;
358 } else {
359 int type;
360 inet_prefix dst;
361
362 if (strcmp(*argv, "to") == 0) {
363 NEXT_ARG();
364 }
365 if ((**argv < '0' || **argv > '9') &&
366 rtnl_rtntype_a2n(&type, *argv) == 0) {
367 NEXT_ARG();
368 req.r.rtm_type = type;
369 type_ok = 1;
370 }
371
Glenn L McGrath16528552002-11-28 11:17:19 +0000372 if (dst_ok) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000373 duparg2("to", *argv);
Glenn L McGrath16528552002-11-28 11:17:19 +0000374 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000375 get_prefix(&dst, *argv, req.r.rtm_family);
Glenn L McGrath16528552002-11-28 11:17:19 +0000376 if (req.r.rtm_family == AF_UNSPEC) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000377 req.r.rtm_family = dst.family;
Glenn L McGrath16528552002-11-28 11:17:19 +0000378 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000379 req.r.rtm_dst_len = dst.bitlen;
380 dst_ok = 1;
Glenn L McGrath16528552002-11-28 11:17:19 +0000381 if (dst.bytelen) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000382 addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
Glenn L McGrath16528552002-11-28 11:17:19 +0000383 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000384 }
385 argc--; argv++;
386 }
387
Glenn L McGrath16528552002-11-28 11:17:19 +0000388 if (rtnl_open(&rth, 0) < 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000389 exit(1);
Glenn L McGrath16528552002-11-28 11:17:19 +0000390 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000391
Eric Andersen5780adb2002-11-15 09:12:47 +0000392 if (d) {
393 int idx;
394
395 ll_init_map(&rth);
396
397 if (d) {
398 if ((idx = ll_name_to_index(d)) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000399 bb_error_msg("Cannot find device \"%s\"", d);
Eric Andersen5780adb2002-11-15 09:12:47 +0000400 return -1;
401 }
402 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
403 }
404 }
405
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000406 if (mxrta->rta_len > RTA_LENGTH(0)) {
Glenn L McGrath16528552002-11-28 11:17:19 +0000407 if (mxlock) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000408 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
Glenn L McGrath16528552002-11-28 11:17:19 +0000409 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000410 addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
411 }
412
Glenn L McGrath16528552002-11-28 11:17:19 +0000413 if (req.r.rtm_family == AF_UNSPEC) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000414 req.r.rtm_family = AF_INET;
Glenn L McGrath16528552002-11-28 11:17:19 +0000415 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000416
Glenn L McGrath16528552002-11-28 11:17:19 +0000417 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000418 exit(2);
Glenn L McGrath16528552002-11-28 11:17:19 +0000419 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000420
421 return 0;
422}
423
424static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
425{
426 struct {
427 struct nlmsghdr nlh;
428 struct rtmsg rtm;
429 } req;
430 struct sockaddr_nl nladdr;
431
432 memset(&nladdr, 0, sizeof(nladdr));
433 memset(&req, 0, sizeof(req));
434 nladdr.nl_family = AF_NETLINK;
435
436 req.nlh.nlmsg_len = sizeof(req);
437 req.nlh.nlmsg_type = RTM_GETROUTE;
438 req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
439 req.nlh.nlmsg_pid = 0;
440 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
441 req.rtm.rtm_family = family;
442 req.rtm.rtm_flags |= RTM_F_CLONED;
443
444 return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
445}
446
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000447static int iproute_flush_cache(void)
448{
449#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush"
450
451 int len;
452 int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY);
453 char *buffer = "-1";
454
455 if (flush_fd < 0) {
456 fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH);
457 return -1;
458 }
459
460 len = strlen (buffer);
461
462 if ((write (flush_fd, (void *)buffer, len)) < len) {
463 fprintf (stderr, "Cannot flush routing cache\n");
464 return -1;
465 }
466 close(flush_fd);
467 return 0;
468}
469
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000470static void iproute_reset_filter(void)
471{
472 memset(&filter, 0, sizeof(filter));
473 filter.mdst.bitlen = -1;
474 filter.msrc.bitlen = -1;
475}
476
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000477static int iproute_list_or_flush(int argc, char **argv, int flush)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000478{
479 int do_ipv6 = preferred_family;
480 struct rtnl_handle rth;
481 char *id = NULL;
482 char *od = NULL;
483
484 iproute_reset_filter();
485 filter.tb = RT_TABLE_MAIN;
486
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000487 if (flush && argc <= 0) {
488 fprintf(stderr, "\"ip route flush\" requires arguments.\n");
489 return -1;
490 }
491
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000492 while (argc > 0) {
493 if (matches(*argv, "protocol") == 0) {
494 int prot = 0;
495 NEXT_ARG();
496 filter.protocolmask = -1;
497 if (rtnl_rtprot_a2n(&prot, *argv)) {
Glenn L McGrath16528552002-11-28 11:17:19 +0000498 if (strcmp(*argv, "all") != 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000499 invarg("invalid \"protocol\"\n", *argv);
Glenn L McGrath16528552002-11-28 11:17:19 +0000500 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000501 prot = 0;
502 filter.protocolmask = 0;
503 }
504 filter.protocol = prot;
505 } else if (strcmp(*argv, "dev") == 0 ||
506 strcmp(*argv, "oif") == 0) {
507 NEXT_ARG();
508 od = *argv;
509 } else if (strcmp(*argv, "iif") == 0) {
510 NEXT_ARG();
511 id = *argv;
512 } else if (matches(*argv, "from") == 0) {
513 NEXT_ARG();
514 if (matches(*argv, "root") == 0) {
515 NEXT_ARG();
516 get_prefix(&filter.rsrc, *argv, do_ipv6);
517 } else if (matches(*argv, "match") == 0) {
518 NEXT_ARG();
519 get_prefix(&filter.msrc, *argv, do_ipv6);
520 } else {
521 if (matches(*argv, "exact") == 0) {
522 NEXT_ARG();
523 }
524 get_prefix(&filter.msrc, *argv, do_ipv6);
525 filter.rsrc = filter.msrc;
526 }
527 } else {
528 if (matches(*argv, "to") == 0) {
529 NEXT_ARG();
530 }
531 if (matches(*argv, "root") == 0) {
532 NEXT_ARG();
533 get_prefix(&filter.rdst, *argv, do_ipv6);
534 } else if (matches(*argv, "match") == 0) {
535 NEXT_ARG();
536 get_prefix(&filter.mdst, *argv, do_ipv6);
Paul Fox5dc0cee2005-07-20 19:01:05 +0000537 } else if (matches(*argv, "table") == 0) {
538 NEXT_ARG();
539 if (matches(*argv, "cache") == 0) {
540 filter.tb = -1;
541 } else if (matches(*argv, "main") != 0) {
542 invarg("invalid \"table\"", *argv);
543 }
544 } else if (matches(*argv, "cache") == 0) {
545 filter.tb = -1;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000546 } else {
547 if (matches(*argv, "exact") == 0) {
548 NEXT_ARG();
549 }
550 get_prefix(&filter.mdst, *argv, do_ipv6);
551 filter.rdst = filter.mdst;
552 }
553 }
554 argc--; argv++;
555 }
556
Glenn L McGrath16528552002-11-28 11:17:19 +0000557 if (do_ipv6 == AF_UNSPEC && filter.tb) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000558 do_ipv6 = AF_INET;
Glenn L McGrath16528552002-11-28 11:17:19 +0000559 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000560
Glenn L McGrath16528552002-11-28 11:17:19 +0000561 if (rtnl_open(&rth, 0) < 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000562 exit(1);
Glenn L McGrath16528552002-11-28 11:17:19 +0000563 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000564
565 ll_init_map(&rth);
566
567 if (id || od) {
568 int idx;
569
570 if (id) {
571 if ((idx = ll_name_to_index(id)) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000572 bb_error_msg("Cannot find device \"%s\"", id);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000573 return -1;
574 }
575 filter.iif = idx;
576 filter.iifmask = -1;
577 }
578 if (od) {
579 if ((idx = ll_name_to_index(od)) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000580 bb_error_msg("Cannot find device \"%s\"", od);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000581 }
582 filter.oif = idx;
583 filter.oifmask = -1;
584 }
585 }
586
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000587 if (flush) {
Bernhard Reutner-Fischerab187822005-10-26 10:47:26 +0000588 int _round = 0;
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000589 char flushb[4096-512];
590
591 if (filter.tb == -1) {
592 if (do_ipv6 != AF_INET6)
593 iproute_flush_cache();
594 if (do_ipv6 == AF_INET)
595 return 0;
596 }
597
598 filter.flushb = flushb;
599 filter.flushp = 0;
600 filter.flushe = sizeof(flushb);
601 filter.rth = &rth;
602
603 for (;;) {
604 if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
605 perror("Cannot send dump request");
606 return -1;
607 }
608 filter.flushed = 0;
609 if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000610 bb_error_msg("Flush terminated\n");
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000611 return -1;
612 }
613 if (filter.flushed == 0) {
Bernhard Reutner-Fischerab187822005-10-26 10:47:26 +0000614 if (_round == 0) {
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000615 if (filter.tb != -1 || do_ipv6 == AF_INET6)
616 fprintf(stderr, "Nothing to flush.\n");
617 }
618 fflush(stdout);
619 return 0;
620 }
Bernhard Reutner-Fischerab187822005-10-26 10:47:26 +0000621 _round++;
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000622 if (flush_update() < 0)
623 exit(1);
624 }
625 }
626
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000627 if (filter.tb != -1) {
628 if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000629 bb_perror_msg_and_die("Cannot send dump request");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000630 }
631 } else {
632 if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000633 bb_perror_msg_and_die("Cannot send dump request");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000634 }
635 }
636
637 if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000638 bb_error_msg_and_die("Dump terminated");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000639 }
640
641 exit(0);
642}
643
644
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000645static int iproute_get(int argc, char **argv)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000646{
647 struct rtnl_handle rth;
648 struct {
649 struct nlmsghdr n;
650 struct rtmsg r;
651 char buf[1024];
652 } req;
653 char *idev = NULL;
654 char *odev = NULL;
655 int connected = 0;
656 int from_ok = 0;
"Vladimir N. Oleynik"2f0a5f92005-12-06 12:00:39 +0000657 static const char * const options[] =
658 { "from", "iif", "oif", "dev", "notify", "connected", "to", 0 };
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000659
660 memset(&req, 0, sizeof(req));
661
662 iproute_reset_filter();
663
664 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
665 req.n.nlmsg_flags = NLM_F_REQUEST;
666 req.n.nlmsg_type = RTM_GETROUTE;
667 req.r.rtm_family = preferred_family;
668 req.r.rtm_table = 0;
669 req.r.rtm_protocol = 0;
670 req.r.rtm_scope = 0;
671 req.r.rtm_type = 0;
672 req.r.rtm_src_len = 0;
673 req.r.rtm_dst_len = 0;
674 req.r.rtm_tos = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000675
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000676 while (argc > 0) {
Glenn L McGrath18eae002002-12-02 00:54:10 +0000677 switch (compare_string_array(options, *argv)) {
678 case 0: /* from */
679 {
680 inet_prefix addr;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000681 NEXT_ARG();
Glenn L McGrath18eae002002-12-02 00:54:10 +0000682 from_ok = 1;
683 get_prefix(&addr, *argv, req.r.rtm_family);
684 if (req.r.rtm_family == AF_UNSPEC) {
685 req.r.rtm_family = addr.family;
686 }
687 if (addr.bytelen) {
688 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
689 }
690 req.r.rtm_src_len = addr.bitlen;
691 break;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000692 }
Glenn L McGrath18eae002002-12-02 00:54:10 +0000693 case 1: /* iif */
694 NEXT_ARG();
695 idev = *argv;
696 break;
697 case 2: /* oif */
698 case 3: /* dev */
699 NEXT_ARG();
700 odev = *argv;
701 break;
702 case 4: /* notify */
703 req.r.rtm_flags |= RTM_F_NOTIFY;
704 break;
705 case 5: /* connected */
706 connected = 1;
707 break;
708 case 6: /* to */
709 NEXT_ARG();
710 default:
711 {
712 inet_prefix addr;
713 get_prefix(&addr, *argv, req.r.rtm_family);
714 if (req.r.rtm_family == AF_UNSPEC) {
715 req.r.rtm_family = addr.family;
716 }
717 if (addr.bytelen) {
718 addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
719 }
720 req.r.rtm_dst_len = addr.bitlen;
721 }
722 argc--; argv++;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000723 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000724 }
725
726 if (req.r.rtm_dst_len == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000727 bb_error_msg_and_die("need at least destination address");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000728 }
729
730 if (rtnl_open(&rth, 0) < 0)
731 exit(1);
732
733 ll_init_map(&rth);
734
735 if (idev || odev) {
736 int idx;
737
738 if (idev) {
739 if ((idx = ll_name_to_index(idev)) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000740 bb_error_msg("Cannot find device \"%s\"", idev);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000741 return -1;
742 }
743 addattr32(&req.n, sizeof(req), RTA_IIF, idx);
744 }
745 if (odev) {
746 if ((idx = ll_name_to_index(odev)) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000747 bb_error_msg("Cannot find device \"%s\"", odev);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000748 return -1;
749 }
750 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
751 }
752 }
753
Glenn L McGrath16528552002-11-28 11:17:19 +0000754 if (req.r.rtm_family == AF_UNSPEC) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000755 req.r.rtm_family = AF_INET;
Glenn L McGrath16528552002-11-28 11:17:19 +0000756 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000757
Glenn L McGrath16528552002-11-28 11:17:19 +0000758 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000759 exit(2);
Glenn L McGrath16528552002-11-28 11:17:19 +0000760 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000761
762 if (connected && !from_ok) {
763 struct rtmsg *r = NLMSG_DATA(&req.n);
764 int len = req.n.nlmsg_len;
765 struct rtattr * tb[RTA_MAX+1];
766
767 if (print_route(NULL, &req.n, (void*)stdout) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000768 bb_error_msg_and_die("An error :-)");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000769 }
770
771 if (req.n.nlmsg_type != RTM_NEWROUTE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000772 bb_error_msg("Not a route?");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000773 return -1;
774 }
775 len -= NLMSG_LENGTH(sizeof(*r));
776 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000777 bb_error_msg("Wrong len %d", len);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000778 return -1;
779 }
780
781 memset(tb, 0, sizeof(tb));
782 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
783
784 if (tb[RTA_PREFSRC]) {
785 tb[RTA_PREFSRC]->rta_type = RTA_SRC;
786 r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
787 } else if (!tb[RTA_SRC]) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000788 bb_error_msg("Failed to connect the route");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000789 return -1;
790 }
Glenn L McGrath16528552002-11-28 11:17:19 +0000791 if (!odev && tb[RTA_OIF]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000792 tb[RTA_OIF]->rta_type = 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000793 }
794 if (tb[RTA_GATEWAY]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000795 tb[RTA_GATEWAY]->rta_type = 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000796 }
797 if (!idev && tb[RTA_IIF]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000798 tb[RTA_IIF]->rta_type = 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000799 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000800 req.n.nlmsg_flags = NLM_F_REQUEST;
801 req.n.nlmsg_type = RTM_GETROUTE;
802
Glenn L McGrath16528552002-11-28 11:17:19 +0000803 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000804 exit(2);
Glenn L McGrath16528552002-11-28 11:17:19 +0000805 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000806 }
807
808 if (print_route(NULL, &req.n, (void*)stdout) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000809 bb_error_msg_and_die("An error :-)");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000810 }
811
812 exit(0);
813}
814
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000815int do_iproute(int argc, char **argv)
816{
"Vladimir N. Oleynik"2f0a5f92005-12-06 12:00:39 +0000817 static const char * const ip_route_commands[] =
818 { "add", "append", "change", "chg", "delete", "del", "get",
819 "list", "show", "prepend", "replace", "test", "flush", 0 };
820 int command_num = 7;
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000821 unsigned int flags = 0;
822 int cmd = RTM_NEWROUTE;
823
824 if (*argv) {
825 command_num = compare_string_array(ip_route_commands, *argv);
Glenn L McGrath16528552002-11-28 11:17:19 +0000826 }
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000827 switch(command_num) {
828 case 0: /* add*/
829 flags = NLM_F_CREATE|NLM_F_EXCL;
830 break;
831 case 1: /* append */
832 flags = NLM_F_CREATE|NLM_F_APPEND;
833 break;
834 case 2: /* change */
835 case 3: /* chg */
836 flags = NLM_F_REPLACE;
837 break;
838 case 4: /* delete */
Eric Andersen1a773a62003-12-19 10:47:40 +0000839 case 5: /* del */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000840 cmd = RTM_DELROUTE;
841 break;
Eric Andersen1a773a62003-12-19 10:47:40 +0000842 case 6: /* get */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000843 return iproute_get(argc-1, argv+1);
Eric Andersen1a773a62003-12-19 10:47:40 +0000844 case 7: /* list */
845 case 8: /* show */
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000846 return iproute_list_or_flush(argc-1, argv+1, 0);
Eric Andersen1a773a62003-12-19 10:47:40 +0000847 case 9: /* prepend */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000848 flags = NLM_F_CREATE;
Eric Andersen1a773a62003-12-19 10:47:40 +0000849 case 10: /* replace */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000850 flags = NLM_F_CREATE|NLM_F_REPLACE;
Eric Andersen1a773a62003-12-19 10:47:40 +0000851 case 11: /* test */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000852 flags = NLM_F_EXCL;
Eric Andersen1a773a62003-12-19 10:47:40 +0000853 case 12: /* flush */
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000854 return iproute_list_or_flush(argc-1, argv+1, 1);
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000855 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000856 bb_error_msg_and_die("Unknown command %s", *argv);
Glenn L McGrath16528552002-11-28 11:17:19 +0000857 }
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000858
859 return iproute_modify(cmd, flags, argc-1, argv+1);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000860}