blob: 6052a30235f6b744e24c42cdb47df932421310c1 [file] [log] [blame]
Bernhard Reutner-Fischerd1d23a62006-01-12 12:08:46 +00001/* vi: set sw=4 ts=4: */
Glenn L McGrath9a2d2722002-11-10 01:33:55 +00002/*
3 * iproute.c "ip route".
4 *
Bernhard Reutner-Fischerab187822005-10-26 10:47:26 +00005 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Glenn L McGrath9a2d2722002-11-10 01:33:55 +00006 *
7 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
8 *
9 *
10 * Changes:
11 *
12 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
13 * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
14 */
15
Rob Landleyecae66a2006-06-02 20:53:38 +000016#include "libbb.h"
17
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000018#include <sys/socket.h>
Glenn L McGrath275be872002-12-16 07:37:21 +000019
Glenn L McGrath275be872002-12-16 07:37:21 +000020#include <string.h>
Glenn L McGrath4a4c6772003-02-15 11:50:33 +000021#include <fcntl.h>
22#include <unistd.h>
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000023
24#include "rt_names.h"
25#include "utils.h"
Bernhard Reutner-Fischer9a67ca32006-04-02 21:14:19 +000026#include "ip_common.h"
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000027
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000028#ifndef RTAX_RTTVAR
29#define RTAX_RTTVAR RTAX_HOPS
30#endif
31
32
33static struct
34{
35 int tb;
Glenn L McGrath4a4c6772003-02-15 11:50:33 +000036 int flushed;
37 char *flushb;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000038 int flushp;
39 int flushe;
40 struct rtnl_handle *rth;
41 int protocol, protocolmask;
42 int scope, scopemask;
43 int type, typemask;
44 int tos, tosmask;
45 int iif, iifmask;
46 int oif, oifmask;
47 int realm, realmmask;
48 inet_prefix rprefsrc;
49 inet_prefix rvia;
50 inet_prefix rdst;
51 inet_prefix mdst;
52 inet_prefix rsrc;
53 inet_prefix msrc;
54} filter;
55
Glenn L McGrath4a4c6772003-02-15 11:50:33 +000056static int flush_update(void)
57{
58 if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
59 perror("Failed to send flush request\n");
60 return -1;
61 }
62 filter.flushp = 0;
63 return 0;
64}
65
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000066static int print_route(struct sockaddr_nl *who ATTRIBUTE_UNUSED,
Bernhard Reutner-Fischerd1d23a62006-01-12 12:08:46 +000067 struct nlmsghdr *n, void *arg)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000068{
69 FILE *fp = (FILE*)arg;
70 struct rtmsg *r = NLMSG_DATA(n);
71 int len = n->nlmsg_len;
72 struct rtattr * tb[RTA_MAX+1];
73 char abuf[256];
Glenn L McGrathfbf0b8a2003-04-26 02:22:19 +000074 inet_prefix dst;
75 inet_prefix src;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000076 int host_len = -1;
77 SPRINT_BUF(b1);
Eric Andersenc7bda1c2004-03-15 08:29:22 +000078
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000079
80 if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
81 fprintf(stderr, "Not a route: %08x %08x %08x\n",
82 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
83 return 0;
84 }
Glenn L McGrath4a4c6772003-02-15 11:50:33 +000085 if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
86 return 0;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000087 len -= NLMSG_LENGTH(sizeof(*r));
88 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +000089 bb_error_msg("wrong nlmsg len %d", len);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000090 return -1;
91 }
92
93 if (r->rtm_family == AF_INET6)
94 host_len = 128;
95 else if (r->rtm_family == AF_INET)
96 host_len = 32;
97
98 if (r->rtm_family == AF_INET6) {
99 if (filter.tb) {
100 if (filter.tb < 0) {
Glenn L McGrath16528552002-11-28 11:17:19 +0000101 if (!(r->rtm_flags&RTM_F_CLONED)) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000102 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000103 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000104 } else {
Glenn L McGrath16528552002-11-28 11:17:19 +0000105 if (r->rtm_flags&RTM_F_CLONED) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000106 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000107 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000108 if (filter.tb == RT_TABLE_LOCAL) {
Glenn L McGrath16528552002-11-28 11:17:19 +0000109 if (r->rtm_type != RTN_LOCAL) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000110 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000111 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000112 } else if (filter.tb == RT_TABLE_MAIN) {
Glenn L McGrath16528552002-11-28 11:17:19 +0000113 if (r->rtm_type == RTN_LOCAL) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000114 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000115 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000116 } else {
117 return 0;
118 }
119 }
120 }
121 } else {
Glenn L McGrath16528552002-11-28 11:17:19 +0000122 if (filter.tb > 0 && filter.tb != r->rtm_table) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000123 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000124 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000125 }
126 if (filter.rdst.family &&
Glenn L McGrath16528552002-11-28 11:17:19 +0000127 (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000128 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000129 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000130 if (filter.mdst.family &&
131 (r->rtm_family != filter.mdst.family ||
Glenn L McGrath16528552002-11-28 11:17:19 +0000132 (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000133 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000134 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000135 if (filter.rsrc.family &&
Glenn L McGrath16528552002-11-28 11:17:19 +0000136 (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000137 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000138 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000139 if (filter.msrc.family &&
140 (r->rtm_family != filter.msrc.family ||
Glenn L McGrath16528552002-11-28 11:17:19 +0000141 (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000142 return 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000143 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000144
145 memset(tb, 0, sizeof(tb));
146 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
147
Glenn L McGrathfbf0b8a2003-04-26 02:22:19 +0000148 if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen))
149 return 0;
150 if (filter.mdst.family && filter.mdst.bitlen >= 0 &&
151 inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len))
152 return 0;
153
154 if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen))
155 return 0;
156 if (filter.msrc.family && filter.msrc.bitlen >= 0 &&
157 inet_addr_match(&src, &filter.msrc, r->rtm_src_len))
158 return 0;
159
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000160 if (filter.flushb &&
161 r->rtm_family == AF_INET6 &&
162 r->rtm_dst_len == 0 &&
163 r->rtm_type == RTN_UNREACHABLE &&
164 tb[RTA_PRIORITY] &&
165 *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1)
166 return 0;
167
168 if (filter.flushb) {
169 struct nlmsghdr *fn;
170 if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
171 if (flush_update())
172 return -1;
173 }
174 fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
175 memcpy(fn, n, n->nlmsg_len);
176 fn->nlmsg_type = RTM_DELROUTE;
177 fn->nlmsg_flags = NLM_F_REQUEST;
178 fn->nlmsg_seq = ++filter.rth->seq;
179 filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
180 filter.flushed++;
181 return 0;
182 }
183
Glenn L McGrath16528552002-11-28 11:17:19 +0000184 if (n->nlmsg_type == RTM_DELROUTE) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000185 fprintf(fp, "Deleted ");
Glenn L McGrath16528552002-11-28 11:17:19 +0000186 }
187 if (r->rtm_type != RTN_UNICAST && !filter.type) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000188 fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
Glenn L McGrath16528552002-11-28 11:17:19 +0000189 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000190
191 if (tb[RTA_DST]) {
192 if (r->rtm_dst_len != host_len) {
193 fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family,
194 RTA_PAYLOAD(tb[RTA_DST]),
195 RTA_DATA(tb[RTA_DST]),
196 abuf, sizeof(abuf)),
197 r->rtm_dst_len
198 );
199 } else {
200 fprintf(fp, "%s ", format_host(r->rtm_family,
201 RTA_PAYLOAD(tb[RTA_DST]),
202 RTA_DATA(tb[RTA_DST]),
203 abuf, sizeof(abuf))
204 );
205 }
206 } else if (r->rtm_dst_len) {
207 fprintf(fp, "0/%d ", r->rtm_dst_len);
208 } else {
209 fprintf(fp, "default ");
210 }
211 if (tb[RTA_SRC]) {
212 if (r->rtm_src_len != host_len) {
213 fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family,
214 RTA_PAYLOAD(tb[RTA_SRC]),
215 RTA_DATA(tb[RTA_SRC]),
216 abuf, sizeof(abuf)),
217 r->rtm_src_len
218 );
219 } else {
220 fprintf(fp, "from %s ", format_host(r->rtm_family,
221 RTA_PAYLOAD(tb[RTA_SRC]),
222 RTA_DATA(tb[RTA_SRC]),
223 abuf, sizeof(abuf))
224 );
225 }
226 } else if (r->rtm_src_len) {
227 fprintf(fp, "from 0/%u ", r->rtm_src_len);
228 }
229 if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000230 fprintf(fp, "via %s ",
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000231 format_host(r->rtm_family,
232 RTA_PAYLOAD(tb[RTA_GATEWAY]),
233 RTA_DATA(tb[RTA_GATEWAY]),
234 abuf, sizeof(abuf)));
235 }
Glenn L McGrath16528552002-11-28 11:17:19 +0000236 if (tb[RTA_OIF] && filter.oifmask != -1) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000237 fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
Glenn L McGrath16528552002-11-28 11:17:19 +0000238 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000239
240 if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
241 /* Do not use format_host(). It is our local addr
242 and symbolic name will not be useful.
243 */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000244 fprintf(fp, " src %s ",
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000245 rt_addr_n2a(r->rtm_family,
246 RTA_PAYLOAD(tb[RTA_PREFSRC]),
247 RTA_DATA(tb[RTA_PREFSRC]),
248 abuf, sizeof(abuf)));
249 }
Glenn L McGrath16528552002-11-28 11:17:19 +0000250 if (tb[RTA_PRIORITY]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000251 fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY]));
Glenn L McGrath16528552002-11-28 11:17:19 +0000252 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000253 if (r->rtm_family == AF_INET6) {
254 struct rta_cacheinfo *ci = NULL;
Glenn L McGrath16528552002-11-28 11:17:19 +0000255 if (tb[RTA_CACHEINFO]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000256 ci = RTA_DATA(tb[RTA_CACHEINFO]);
Glenn L McGrath16528552002-11-28 11:17:19 +0000257 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000258 if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
259 static int hz;
Glenn L McGrath16528552002-11-28 11:17:19 +0000260 if (!hz) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000261 hz = get_hz();
Glenn L McGrath16528552002-11-28 11:17:19 +0000262 }
263 if (r->rtm_flags & RTM_F_CLONED) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000264 fprintf(fp, "%s cache ", _SL_);
Glenn L McGrath16528552002-11-28 11:17:19 +0000265 }
266 if (ci->rta_expires) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000267 fprintf(fp, " expires %dsec", ci->rta_expires/hz);
Glenn L McGrath16528552002-11-28 11:17:19 +0000268 }
269 if (ci->rta_error != 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000270 fprintf(fp, " error %d", ci->rta_error);
Glenn L McGrath16528552002-11-28 11:17:19 +0000271 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000272 } else if (ci) {
273 if (ci->rta_error != 0)
274 fprintf(fp, " error %d", ci->rta_error);
275 }
276 }
277 if (tb[RTA_IIF] && filter.iifmask != -1) {
278 fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
279 }
280 fprintf(fp, "\n");
281 fflush(fp);
282 return 0;
283}
284
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000285static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000286{
287 struct rtnl_handle rth;
288 struct {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000289 struct nlmsghdr n;
290 struct rtmsg r;
291 char buf[1024];
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000292 } req;
293 char mxbuf[256];
294 struct rtattr * mxrta = (void*)mxbuf;
295 unsigned mxlock = 0;
296 char *d = NULL;
297 int gw_ok = 0;
298 int dst_ok = 0;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000299 int proto_ok = 0;
300 int type_ok = 0;
301
302 memset(&req, 0, sizeof(req));
303
304 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
305 req.n.nlmsg_flags = NLM_F_REQUEST|flags;
306 req.n.nlmsg_type = cmd;
307 req.r.rtm_family = preferred_family;
308 req.r.rtm_table = RT_TABLE_MAIN;
309 req.r.rtm_scope = RT_SCOPE_NOWHERE;
310
311 if (cmd != RTM_DELROUTE) {
312 req.r.rtm_protocol = RTPROT_BOOT;
313 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
314 req.r.rtm_type = RTN_UNICAST;
315 }
316
317 mxrta->rta_type = RTA_METRICS;
318 mxrta->rta_len = RTA_LENGTH(0);
319
320 while (argc > 0) {
321 if (strcmp(*argv, "src") == 0) {
322 inet_prefix addr;
323 NEXT_ARG();
324 get_addr(&addr, *argv, req.r.rtm_family);
Glenn L McGrath16528552002-11-28 11:17:19 +0000325 if (req.r.rtm_family == AF_UNSPEC) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000326 req.r.rtm_family = addr.family;
Glenn L McGrath16528552002-11-28 11:17:19 +0000327 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000328 addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
329 } else if (strcmp(*argv, "via") == 0) {
330 inet_prefix addr;
331 gw_ok = 1;
332 NEXT_ARG();
333 get_addr(&addr, *argv, req.r.rtm_family);
Glenn L McGrath16528552002-11-28 11:17:19 +0000334 if (req.r.rtm_family == AF_UNSPEC) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000335 req.r.rtm_family = addr.family;
Glenn L McGrath16528552002-11-28 11:17:19 +0000336 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000337 addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
338 } else if (strcmp(*argv, "mtu") == 0) {
339 unsigned mtu;
340 NEXT_ARG();
341 if (strcmp(*argv, "lock") == 0) {
342 mxlock |= (1<<RTAX_MTU);
343 NEXT_ARG();
344 }
Glenn L McGrath16528552002-11-28 11:17:19 +0000345 if (get_unsigned(&mtu, *argv, 0)) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000346 invarg("\"mtu\" value is invalid\n", *argv);
Glenn L McGrath16528552002-11-28 11:17:19 +0000347 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000348 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
349 } else if (matches(*argv, "protocol") == 0) {
Eric Andersend78aea82006-01-30 18:00:02 +0000350 uint32_t prot;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000351 NEXT_ARG();
352 if (rtnl_rtprot_a2n(&prot, *argv))
353 invarg("\"protocol\" value is invalid\n", *argv);
354 req.r.rtm_protocol = prot;
355 proto_ok =1;
356 } else if (strcmp(*argv, "dev") == 0 ||
357 strcmp(*argv, "oif") == 0) {
358 NEXT_ARG();
359 d = *argv;
360 } else {
361 int type;
362 inet_prefix dst;
363
364 if (strcmp(*argv, "to") == 0) {
365 NEXT_ARG();
366 }
367 if ((**argv < '0' || **argv > '9') &&
368 rtnl_rtntype_a2n(&type, *argv) == 0) {
369 NEXT_ARG();
370 req.r.rtm_type = type;
371 type_ok = 1;
372 }
373
Glenn L McGrath16528552002-11-28 11:17:19 +0000374 if (dst_ok) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000375 duparg2("to", *argv);
Glenn L McGrath16528552002-11-28 11:17:19 +0000376 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000377 get_prefix(&dst, *argv, req.r.rtm_family);
Glenn L McGrath16528552002-11-28 11:17:19 +0000378 if (req.r.rtm_family == AF_UNSPEC) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000379 req.r.rtm_family = dst.family;
Glenn L McGrath16528552002-11-28 11:17:19 +0000380 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000381 req.r.rtm_dst_len = dst.bitlen;
382 dst_ok = 1;
Glenn L McGrath16528552002-11-28 11:17:19 +0000383 if (dst.bytelen) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000384 addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
Glenn L McGrath16528552002-11-28 11:17:19 +0000385 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000386 }
387 argc--; argv++;
388 }
389
Glenn L McGrath16528552002-11-28 11:17:19 +0000390 if (rtnl_open(&rth, 0) < 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000391 exit(1);
Glenn L McGrath16528552002-11-28 11:17:19 +0000392 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000393
Eric Andersen5780adb2002-11-15 09:12:47 +0000394 if (d) {
395 int idx;
396
397 ll_init_map(&rth);
398
399 if (d) {
400 if ((idx = ll_name_to_index(d)) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000401 bb_error_msg("Cannot find device \"%s\"", d);
Eric Andersen5780adb2002-11-15 09:12:47 +0000402 return -1;
403 }
404 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
405 }
406 }
407
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000408 if (mxrta->rta_len > RTA_LENGTH(0)) {
Glenn L McGrath16528552002-11-28 11:17:19 +0000409 if (mxlock) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000410 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
Glenn L McGrath16528552002-11-28 11:17:19 +0000411 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000412 addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
413 }
414
Glenn L McGrath16528552002-11-28 11:17:19 +0000415 if (req.r.rtm_family == AF_UNSPEC) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000416 req.r.rtm_family = AF_INET;
Glenn L McGrath16528552002-11-28 11:17:19 +0000417 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000418
Glenn L McGrath16528552002-11-28 11:17:19 +0000419 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000420 exit(2);
Glenn L McGrath16528552002-11-28 11:17:19 +0000421 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000422
423 return 0;
424}
425
426static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
427{
428 struct {
429 struct nlmsghdr nlh;
430 struct rtmsg rtm;
431 } req;
432 struct sockaddr_nl nladdr;
433
434 memset(&nladdr, 0, sizeof(nladdr));
435 memset(&req, 0, sizeof(req));
436 nladdr.nl_family = AF_NETLINK;
437
438 req.nlh.nlmsg_len = sizeof(req);
439 req.nlh.nlmsg_type = RTM_GETROUTE;
440 req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
441 req.nlh.nlmsg_pid = 0;
442 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
443 req.rtm.rtm_family = family;
444 req.rtm.rtm_flags |= RTM_F_CLONED;
445
446 return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
447}
448
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000449static int iproute_flush_cache(void)
450{
451#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush"
452
453 int len;
454 int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY);
455 char *buffer = "-1";
456
457 if (flush_fd < 0) {
458 fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH);
459 return -1;
460 }
461
462 len = strlen (buffer);
463
464 if ((write (flush_fd, (void *)buffer, len)) < len) {
465 fprintf (stderr, "Cannot flush routing cache\n");
466 return -1;
467 }
468 close(flush_fd);
469 return 0;
470}
471
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000472static void iproute_reset_filter(void)
473{
474 memset(&filter, 0, sizeof(filter));
475 filter.mdst.bitlen = -1;
476 filter.msrc.bitlen = -1;
477}
478
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000479static int iproute_list_or_flush(int argc, char **argv, int flush)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000480{
481 int do_ipv6 = preferred_family;
482 struct rtnl_handle rth;
483 char *id = NULL;
484 char *od = NULL;
485
486 iproute_reset_filter();
487 filter.tb = RT_TABLE_MAIN;
488
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000489 if (flush && argc <= 0) {
490 fprintf(stderr, "\"ip route flush\" requires arguments.\n");
491 return -1;
492 }
493
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000494 while (argc > 0) {
495 if (matches(*argv, "protocol") == 0) {
Eric Andersend78aea82006-01-30 18:00:02 +0000496 uint32_t prot = 0;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000497 NEXT_ARG();
498 filter.protocolmask = -1;
499 if (rtnl_rtprot_a2n(&prot, *argv)) {
Glenn L McGrath16528552002-11-28 11:17:19 +0000500 if (strcmp(*argv, "all") != 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000501 invarg("invalid \"protocol\"\n", *argv);
Glenn L McGrath16528552002-11-28 11:17:19 +0000502 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000503 prot = 0;
504 filter.protocolmask = 0;
505 }
506 filter.protocol = prot;
507 } else if (strcmp(*argv, "dev") == 0 ||
508 strcmp(*argv, "oif") == 0) {
509 NEXT_ARG();
510 od = *argv;
511 } else if (strcmp(*argv, "iif") == 0) {
512 NEXT_ARG();
513 id = *argv;
514 } else if (matches(*argv, "from") == 0) {
515 NEXT_ARG();
516 if (matches(*argv, "root") == 0) {
517 NEXT_ARG();
518 get_prefix(&filter.rsrc, *argv, do_ipv6);
519 } else if (matches(*argv, "match") == 0) {
520 NEXT_ARG();
521 get_prefix(&filter.msrc, *argv, do_ipv6);
522 } else {
523 if (matches(*argv, "exact") == 0) {
524 NEXT_ARG();
525 }
526 get_prefix(&filter.msrc, *argv, do_ipv6);
527 filter.rsrc = filter.msrc;
528 }
529 } else {
530 if (matches(*argv, "to") == 0) {
531 NEXT_ARG();
532 }
533 if (matches(*argv, "root") == 0) {
534 NEXT_ARG();
535 get_prefix(&filter.rdst, *argv, do_ipv6);
536 } else if (matches(*argv, "match") == 0) {
537 NEXT_ARG();
538 get_prefix(&filter.mdst, *argv, do_ipv6);
Paul Fox5dc0cee2005-07-20 19:01:05 +0000539 } else if (matches(*argv, "table") == 0) {
540 NEXT_ARG();
541 if (matches(*argv, "cache") == 0) {
542 filter.tb = -1;
543 } else if (matches(*argv, "main") != 0) {
544 invarg("invalid \"table\"", *argv);
545 }
546 } else if (matches(*argv, "cache") == 0) {
547 filter.tb = -1;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000548 } else {
549 if (matches(*argv, "exact") == 0) {
550 NEXT_ARG();
551 }
552 get_prefix(&filter.mdst, *argv, do_ipv6);
553 filter.rdst = filter.mdst;
554 }
555 }
556 argc--; argv++;
557 }
558
Glenn L McGrath16528552002-11-28 11:17:19 +0000559 if (do_ipv6 == AF_UNSPEC && filter.tb) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000560 do_ipv6 = AF_INET;
Glenn L McGrath16528552002-11-28 11:17:19 +0000561 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000562
Glenn L McGrath16528552002-11-28 11:17:19 +0000563 if (rtnl_open(&rth, 0) < 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000564 exit(1);
Glenn L McGrath16528552002-11-28 11:17:19 +0000565 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000566
567 ll_init_map(&rth);
568
569 if (id || od) {
570 int idx;
571
572 if (id) {
573 if ((idx = ll_name_to_index(id)) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000574 bb_error_msg("Cannot find device \"%s\"", id);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000575 return -1;
576 }
577 filter.iif = idx;
578 filter.iifmask = -1;
579 }
580 if (od) {
581 if ((idx = ll_name_to_index(od)) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000582 bb_error_msg("Cannot find device \"%s\"", od);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000583 }
584 filter.oif = idx;
585 filter.oifmask = -1;
586 }
587 }
588
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000589 if (flush) {
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000590 char flushb[4096-512];
591
592 if (filter.tb == -1) {
593 if (do_ipv6 != AF_INET6)
594 iproute_flush_cache();
595 if (do_ipv6 == AF_INET)
596 return 0;
597 }
598
599 filter.flushb = flushb;
600 filter.flushp = 0;
601 filter.flushe = sizeof(flushb);
602 filter.rth = &rth;
603
604 for (;;) {
605 if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
606 perror("Cannot send dump request");
607 return -1;
608 }
609 filter.flushed = 0;
610 if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000611 bb_error_msg("Flush terminated\n");
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000612 return -1;
613 }
614 if (filter.flushed == 0) {
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000615 fflush(stdout);
616 return 0;
617 }
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000618 if (flush_update() < 0)
619 exit(1);
620 }
621 }
622
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000623 if (filter.tb != -1) {
624 if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000625 bb_perror_msg_and_die("Cannot send dump request");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000626 }
627 } else {
628 if (rtnl_rtcache_request(&rth, do_ipv6) < 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 }
632
633 if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000634 bb_error_msg_and_die("Dump terminated");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000635 }
636
637 exit(0);
638}
639
640
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000641static int iproute_get(int argc, char **argv)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000642{
643 struct rtnl_handle rth;
644 struct {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000645 struct nlmsghdr n;
646 struct rtmsg r;
647 char buf[1024];
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000648 } req;
649 char *idev = NULL;
650 char *odev = NULL;
651 int connected = 0;
652 int from_ok = 0;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000653 static const char * const options[] =
"Vladimir N. Oleynik"2f0a5f92005-12-06 12:00:39 +0000654 { "from", "iif", "oif", "dev", "notify", "connected", "to", 0 };
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000655
656 memset(&req, 0, sizeof(req));
657
658 iproute_reset_filter();
659
660 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
661 req.n.nlmsg_flags = NLM_F_REQUEST;
662 req.n.nlmsg_type = RTM_GETROUTE;
663 req.r.rtm_family = preferred_family;
664 req.r.rtm_table = 0;
665 req.r.rtm_protocol = 0;
666 req.r.rtm_scope = 0;
667 req.r.rtm_type = 0;
668 req.r.rtm_src_len = 0;
669 req.r.rtm_dst_len = 0;
670 req.r.rtm_tos = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000671
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000672 while (argc > 0) {
Glenn L McGrath18eae002002-12-02 00:54:10 +0000673 switch (compare_string_array(options, *argv)) {
674 case 0: /* from */
675 {
676 inet_prefix addr;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000677 NEXT_ARG();
Glenn L McGrath18eae002002-12-02 00:54:10 +0000678 from_ok = 1;
679 get_prefix(&addr, *argv, req.r.rtm_family);
680 if (req.r.rtm_family == AF_UNSPEC) {
681 req.r.rtm_family = addr.family;
682 }
683 if (addr.bytelen) {
684 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
685 }
686 req.r.rtm_src_len = addr.bitlen;
687 break;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000688 }
Glenn L McGrath18eae002002-12-02 00:54:10 +0000689 case 1: /* iif */
690 NEXT_ARG();
691 idev = *argv;
692 break;
693 case 2: /* oif */
694 case 3: /* dev */
695 NEXT_ARG();
696 odev = *argv;
697 break;
698 case 4: /* notify */
699 req.r.rtm_flags |= RTM_F_NOTIFY;
700 break;
701 case 5: /* connected */
702 connected = 1;
703 break;
704 case 6: /* to */
705 NEXT_ARG();
706 default:
707 {
708 inet_prefix addr;
709 get_prefix(&addr, *argv, req.r.rtm_family);
710 if (req.r.rtm_family == AF_UNSPEC) {
711 req.r.rtm_family = addr.family;
712 }
713 if (addr.bytelen) {
714 addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
715 }
716 req.r.rtm_dst_len = addr.bitlen;
717 }
718 argc--; argv++;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000719 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000720 }
721
722 if (req.r.rtm_dst_len == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000723 bb_error_msg_and_die("need at least destination address");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000724 }
725
726 if (rtnl_open(&rth, 0) < 0)
727 exit(1);
728
729 ll_init_map(&rth);
730
731 if (idev || odev) {
732 int idx;
733
734 if (idev) {
735 if ((idx = ll_name_to_index(idev)) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000736 bb_error_msg("Cannot find device \"%s\"", idev);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000737 return -1;
738 }
739 addattr32(&req.n, sizeof(req), RTA_IIF, idx);
740 }
741 if (odev) {
742 if ((idx = ll_name_to_index(odev)) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000743 bb_error_msg("Cannot find device \"%s\"", odev);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000744 return -1;
745 }
746 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
747 }
748 }
749
Glenn L McGrath16528552002-11-28 11:17:19 +0000750 if (req.r.rtm_family == AF_UNSPEC) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000751 req.r.rtm_family = AF_INET;
Glenn L McGrath16528552002-11-28 11:17:19 +0000752 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000753
Glenn L McGrath16528552002-11-28 11:17:19 +0000754 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000755 exit(2);
Glenn L McGrath16528552002-11-28 11:17:19 +0000756 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000757
758 if (connected && !from_ok) {
759 struct rtmsg *r = NLMSG_DATA(&req.n);
760 int len = req.n.nlmsg_len;
761 struct rtattr * tb[RTA_MAX+1];
762
763 if (print_route(NULL, &req.n, (void*)stdout) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000764 bb_error_msg_and_die("An error :-)");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000765 }
766
767 if (req.n.nlmsg_type != RTM_NEWROUTE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000768 bb_error_msg("Not a route?");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000769 return -1;
770 }
771 len -= NLMSG_LENGTH(sizeof(*r));
772 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000773 bb_error_msg("Wrong len %d", len);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000774 return -1;
775 }
776
777 memset(tb, 0, sizeof(tb));
778 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
779
780 if (tb[RTA_PREFSRC]) {
781 tb[RTA_PREFSRC]->rta_type = RTA_SRC;
782 r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
783 } else if (!tb[RTA_SRC]) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000784 bb_error_msg("Failed to connect the route");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000785 return -1;
786 }
Glenn L McGrath16528552002-11-28 11:17:19 +0000787 if (!odev && tb[RTA_OIF]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000788 tb[RTA_OIF]->rta_type = 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000789 }
790 if (tb[RTA_GATEWAY]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000791 tb[RTA_GATEWAY]->rta_type = 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000792 }
793 if (!idev && tb[RTA_IIF]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000794 tb[RTA_IIF]->rta_type = 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000795 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000796 req.n.nlmsg_flags = NLM_F_REQUEST;
797 req.n.nlmsg_type = RTM_GETROUTE;
798
Glenn L McGrath16528552002-11-28 11:17:19 +0000799 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000800 exit(2);
Glenn L McGrath16528552002-11-28 11:17:19 +0000801 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000802 }
803
804 if (print_route(NULL, &req.n, (void*)stdout) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000805 bb_error_msg_and_die("An error :-)");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000806 }
807
808 exit(0);
809}
810
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000811int do_iproute(int argc, char **argv)
812{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000813 static const char * const ip_route_commands[] =
814 { "add", "append", "change", "chg", "delete", "del", "get",
"Vladimir N. Oleynik"2f0a5f92005-12-06 12:00:39 +0000815 "list", "show", "prepend", "replace", "test", "flush", 0 };
816 int command_num = 7;
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000817 unsigned int flags = 0;
818 int cmd = RTM_NEWROUTE;
819
820 if (*argv) {
821 command_num = compare_string_array(ip_route_commands, *argv);
Glenn L McGrath16528552002-11-28 11:17:19 +0000822 }
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000823 switch(command_num) {
824 case 0: /* add*/
825 flags = NLM_F_CREATE|NLM_F_EXCL;
826 break;
827 case 1: /* append */
828 flags = NLM_F_CREATE|NLM_F_APPEND;
829 break;
830 case 2: /* change */
831 case 3: /* chg */
832 flags = NLM_F_REPLACE;
833 break;
834 case 4: /* delete */
Eric Andersen1a773a62003-12-19 10:47:40 +0000835 case 5: /* del */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000836 cmd = RTM_DELROUTE;
837 break;
Eric Andersen1a773a62003-12-19 10:47:40 +0000838 case 6: /* get */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000839 return iproute_get(argc-1, argv+1);
Eric Andersen1a773a62003-12-19 10:47:40 +0000840 case 7: /* list */
841 case 8: /* show */
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000842 return iproute_list_or_flush(argc-1, argv+1, 0);
Eric Andersen1a773a62003-12-19 10:47:40 +0000843 case 9: /* prepend */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000844 flags = NLM_F_CREATE;
Eric Andersen1a773a62003-12-19 10:47:40 +0000845 case 10: /* replace */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000846 flags = NLM_F_CREATE|NLM_F_REPLACE;
Eric Andersen1a773a62003-12-19 10:47:40 +0000847 case 11: /* test */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000848 flags = NLM_F_EXCL;
Eric Andersen1a773a62003-12-19 10:47:40 +0000849 case 12: /* flush */
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000850 return iproute_list_or_flush(argc-1, argv+1, 1);
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000851 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000852 bb_error_msg_and_die("Unknown command %s", *argv);
Glenn L McGrath16528552002-11-28 11:17:19 +0000853 }
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000854
855 return iproute_modify(cmd, flags, argc-1, argv+1);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000856}