blob: 1c4f75520fd68195a4159751629cc0369550c411 [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
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000016#include <sys/socket.h>
Glenn L McGrath275be872002-12-16 07:37:21 +000017
18#include <stdlib.h>
19#include <string.h>
Glenn L McGrath4a4c6772003-02-15 11:50:33 +000020#include <fcntl.h>
21#include <unistd.h>
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000022
23#include "rt_names.h"
24#include "utils.h"
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000025
Glenn L McGrath275be872002-12-16 07:37:21 +000026#include "libbb.h"
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000027
28#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) {
350 int prot;
351 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) {
496 int prot = 0;
497 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) {
Bernhard Reutner-Fischerab187822005-10-26 10:47:26 +0000590 int _round = 0;
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000591 char flushb[4096-512];
592
593 if (filter.tb == -1) {
594 if (do_ipv6 != AF_INET6)
595 iproute_flush_cache();
596 if (do_ipv6 == AF_INET)
597 return 0;
598 }
599
600 filter.flushb = flushb;
601 filter.flushp = 0;
602 filter.flushe = sizeof(flushb);
603 filter.rth = &rth;
604
605 for (;;) {
606 if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
607 perror("Cannot send dump request");
608 return -1;
609 }
610 filter.flushed = 0;
611 if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000612 bb_error_msg("Flush terminated\n");
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000613 return -1;
614 }
615 if (filter.flushed == 0) {
Bernhard Reutner-Fischerab187822005-10-26 10:47:26 +0000616 if (_round == 0) {
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000617 if (filter.tb != -1 || do_ipv6 == AF_INET6)
618 fprintf(stderr, "Nothing to flush.\n");
619 }
620 fflush(stdout);
621 return 0;
622 }
Bernhard Reutner-Fischerab187822005-10-26 10:47:26 +0000623 _round++;
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000624 if (flush_update() < 0)
625 exit(1);
626 }
627 }
628
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000629 if (filter.tb != -1) {
630 if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000631 bb_perror_msg_and_die("Cannot send dump request");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000632 }
633 } else {
634 if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000635 bb_perror_msg_and_die("Cannot send dump request");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000636 }
637 }
638
639 if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000640 bb_error_msg_and_die("Dump terminated");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000641 }
642
643 exit(0);
644}
645
646
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000647static int iproute_get(int argc, char **argv)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000648{
649 struct rtnl_handle rth;
650 struct {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000651 struct nlmsghdr n;
652 struct rtmsg r;
653 char buf[1024];
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000654 } req;
655 char *idev = NULL;
656 char *odev = NULL;
657 int connected = 0;
658 int from_ok = 0;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000659 static const char * const options[] =
"Vladimir N. Oleynik"2f0a5f92005-12-06 12:00:39 +0000660 { "from", "iif", "oif", "dev", "notify", "connected", "to", 0 };
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000661
662 memset(&req, 0, sizeof(req));
663
664 iproute_reset_filter();
665
666 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
667 req.n.nlmsg_flags = NLM_F_REQUEST;
668 req.n.nlmsg_type = RTM_GETROUTE;
669 req.r.rtm_family = preferred_family;
670 req.r.rtm_table = 0;
671 req.r.rtm_protocol = 0;
672 req.r.rtm_scope = 0;
673 req.r.rtm_type = 0;
674 req.r.rtm_src_len = 0;
675 req.r.rtm_dst_len = 0;
676 req.r.rtm_tos = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000677
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000678 while (argc > 0) {
Glenn L McGrath18eae002002-12-02 00:54:10 +0000679 switch (compare_string_array(options, *argv)) {
680 case 0: /* from */
681 {
682 inet_prefix addr;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000683 NEXT_ARG();
Glenn L McGrath18eae002002-12-02 00:54:10 +0000684 from_ok = 1;
685 get_prefix(&addr, *argv, req.r.rtm_family);
686 if (req.r.rtm_family == AF_UNSPEC) {
687 req.r.rtm_family = addr.family;
688 }
689 if (addr.bytelen) {
690 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
691 }
692 req.r.rtm_src_len = addr.bitlen;
693 break;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000694 }
Glenn L McGrath18eae002002-12-02 00:54:10 +0000695 case 1: /* iif */
696 NEXT_ARG();
697 idev = *argv;
698 break;
699 case 2: /* oif */
700 case 3: /* dev */
701 NEXT_ARG();
702 odev = *argv;
703 break;
704 case 4: /* notify */
705 req.r.rtm_flags |= RTM_F_NOTIFY;
706 break;
707 case 5: /* connected */
708 connected = 1;
709 break;
710 case 6: /* to */
711 NEXT_ARG();
712 default:
713 {
714 inet_prefix addr;
715 get_prefix(&addr, *argv, req.r.rtm_family);
716 if (req.r.rtm_family == AF_UNSPEC) {
717 req.r.rtm_family = addr.family;
718 }
719 if (addr.bytelen) {
720 addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
721 }
722 req.r.rtm_dst_len = addr.bitlen;
723 }
724 argc--; argv++;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000725 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000726 }
727
728 if (req.r.rtm_dst_len == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000729 bb_error_msg_and_die("need at least destination address");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000730 }
731
732 if (rtnl_open(&rth, 0) < 0)
733 exit(1);
734
735 ll_init_map(&rth);
736
737 if (idev || odev) {
738 int idx;
739
740 if (idev) {
741 if ((idx = ll_name_to_index(idev)) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000742 bb_error_msg("Cannot find device \"%s\"", idev);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000743 return -1;
744 }
745 addattr32(&req.n, sizeof(req), RTA_IIF, idx);
746 }
747 if (odev) {
748 if ((idx = ll_name_to_index(odev)) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000749 bb_error_msg("Cannot find device \"%s\"", odev);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000750 return -1;
751 }
752 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
753 }
754 }
755
Glenn L McGrath16528552002-11-28 11:17:19 +0000756 if (req.r.rtm_family == AF_UNSPEC) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000757 req.r.rtm_family = AF_INET;
Glenn L McGrath16528552002-11-28 11:17:19 +0000758 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000759
Glenn L McGrath16528552002-11-28 11:17:19 +0000760 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000761 exit(2);
Glenn L McGrath16528552002-11-28 11:17:19 +0000762 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000763
764 if (connected && !from_ok) {
765 struct rtmsg *r = NLMSG_DATA(&req.n);
766 int len = req.n.nlmsg_len;
767 struct rtattr * tb[RTA_MAX+1];
768
769 if (print_route(NULL, &req.n, (void*)stdout) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000770 bb_error_msg_and_die("An error :-)");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000771 }
772
773 if (req.n.nlmsg_type != RTM_NEWROUTE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000774 bb_error_msg("Not a route?");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000775 return -1;
776 }
777 len -= NLMSG_LENGTH(sizeof(*r));
778 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000779 bb_error_msg("Wrong len %d", len);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000780 return -1;
781 }
782
783 memset(tb, 0, sizeof(tb));
784 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
785
786 if (tb[RTA_PREFSRC]) {
787 tb[RTA_PREFSRC]->rta_type = RTA_SRC;
788 r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
789 } else if (!tb[RTA_SRC]) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000790 bb_error_msg("Failed to connect the route");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000791 return -1;
792 }
Glenn L McGrath16528552002-11-28 11:17:19 +0000793 if (!odev && tb[RTA_OIF]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000794 tb[RTA_OIF]->rta_type = 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000795 }
796 if (tb[RTA_GATEWAY]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000797 tb[RTA_GATEWAY]->rta_type = 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000798 }
799 if (!idev && tb[RTA_IIF]) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000800 tb[RTA_IIF]->rta_type = 0;
Glenn L McGrath16528552002-11-28 11:17:19 +0000801 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000802 req.n.nlmsg_flags = NLM_F_REQUEST;
803 req.n.nlmsg_type = RTM_GETROUTE;
804
Glenn L McGrath16528552002-11-28 11:17:19 +0000805 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000806 exit(2);
Glenn L McGrath16528552002-11-28 11:17:19 +0000807 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000808 }
809
810 if (print_route(NULL, &req.n, (void*)stdout) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000811 bb_error_msg_and_die("An error :-)");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000812 }
813
814 exit(0);
815}
816
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000817int do_iproute(int argc, char **argv)
818{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000819 static const char * const ip_route_commands[] =
820 { "add", "append", "change", "chg", "delete", "del", "get",
"Vladimir N. Oleynik"2f0a5f92005-12-06 12:00:39 +0000821 "list", "show", "prepend", "replace", "test", "flush", 0 };
822 int command_num = 7;
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000823 unsigned int flags = 0;
824 int cmd = RTM_NEWROUTE;
825
826 if (*argv) {
827 command_num = compare_string_array(ip_route_commands, *argv);
Glenn L McGrath16528552002-11-28 11:17:19 +0000828 }
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000829 switch(command_num) {
830 case 0: /* add*/
831 flags = NLM_F_CREATE|NLM_F_EXCL;
832 break;
833 case 1: /* append */
834 flags = NLM_F_CREATE|NLM_F_APPEND;
835 break;
836 case 2: /* change */
837 case 3: /* chg */
838 flags = NLM_F_REPLACE;
839 break;
840 case 4: /* delete */
Eric Andersen1a773a62003-12-19 10:47:40 +0000841 case 5: /* del */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000842 cmd = RTM_DELROUTE;
843 break;
Eric Andersen1a773a62003-12-19 10:47:40 +0000844 case 6: /* get */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000845 return iproute_get(argc-1, argv+1);
Eric Andersen1a773a62003-12-19 10:47:40 +0000846 case 7: /* list */
847 case 8: /* show */
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000848 return iproute_list_or_flush(argc-1, argv+1, 0);
Eric Andersen1a773a62003-12-19 10:47:40 +0000849 case 9: /* prepend */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000850 flags = NLM_F_CREATE;
Eric Andersen1a773a62003-12-19 10:47:40 +0000851 case 10: /* replace */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000852 flags = NLM_F_CREATE|NLM_F_REPLACE;
Eric Andersen1a773a62003-12-19 10:47:40 +0000853 case 11: /* test */
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000854 flags = NLM_F_EXCL;
Eric Andersen1a773a62003-12-19 10:47:40 +0000855 case 12: /* flush */
Glenn L McGrath4a4c6772003-02-15 11:50:33 +0000856 return iproute_list_or_flush(argc-1, argv+1, 1);
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000857 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000858 bb_error_msg_and_die("Unknown command %s", *argv);
Glenn L McGrath16528552002-11-28 11:17:19 +0000859 }
Glenn L McGrathc82f2322002-12-02 00:35:23 +0000860
861 return iproute_modify(cmd, flags, argc-1, argv+1);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000862}