iproute.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  4. *
  5. * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  6. *
  7. * Changes:
  8. *
  9. * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
  10. * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
  11. */
  12. #include "ip_common.h" /* #include "libbb.h" is inside */
  13. #include "common_bufsiz.h"
  14. #include "rt_names.h"
  15. #include "utils.h"
  16. #include <linux/version.h>
  17. /* RTA_TABLE is not a define, can't test with ifdef. */
  18. /* As a proxy, test which kernels toolchain expects: */
  19. #define HAVE_RTA_TABLE (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19))
  20. #ifndef RTAX_RTTVAR
  21. #define RTAX_RTTVAR RTAX_HOPS
  22. #endif
  23. struct filter_t {
  24. int tb;
  25. /* Misnomer. Does not mean "flushed something" */
  26. /* More like "flush commands were constructed by print_route()" */
  27. smallint flushed;
  28. /* Flush cmd buf. If !NULL, print_route() constructs flush commands in it */
  29. char *flushb;
  30. int flushp;
  31. int flushe;
  32. struct rtnl_handle *rth;
  33. //int protocol, protocolmask; - write-only fields?!
  34. int scope, scopemask;
  35. //int type; - read-only
  36. //int typemask; - unused
  37. //int tos, tosmask; - unused
  38. int iif;
  39. int oif;
  40. //int realm, realmmask; - unused
  41. //inet_prefix rprefsrc; - read-only
  42. inet_prefix rvia;
  43. inet_prefix rdst;
  44. inet_prefix mdst;
  45. inet_prefix rsrc;
  46. inet_prefix msrc;
  47. } FIX_ALIASING;
  48. typedef struct filter_t filter_t;
  49. #define G_filter (*(filter_t*)bb_common_bufsiz1)
  50. #define INIT_G() do { setup_common_bufsiz(); } while (0)
  51. static int flush_update(void)
  52. {
  53. if (rtnl_send_check(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) {
  54. bb_simple_perror_msg("can't send flush request");
  55. return -1;
  56. }
  57. G_filter.flushp = 0;
  58. return 0;
  59. }
  60. static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
  61. struct nlmsghdr *n, void *arg UNUSED_PARAM)
  62. {
  63. struct rtmsg *r = NLMSG_DATA(n);
  64. int len = n->nlmsg_len;
  65. struct rtattr *tb[RTA_MAX+1];
  66. inet_prefix dst;
  67. inet_prefix src;
  68. int host_len = -1;
  69. uint32_t tid;
  70. if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
  71. fprintf(stderr, "Not a route: %08x %08x %08x\n",
  72. n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
  73. return 0;
  74. }
  75. if (G_filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
  76. return 0;
  77. len -= NLMSG_LENGTH(sizeof(*r));
  78. if (len < 0)
  79. bb_error_msg_and_die("wrong nlmsg len %d", len);
  80. //memset(tb, 0, sizeof(tb)); - parse_rtattr does this
  81. parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
  82. #if HAVE_RTA_TABLE
  83. if (tb[RTA_TABLE])
  84. tid = *(uint32_t *)RTA_DATA(tb[RTA_TABLE]);
  85. else
  86. #endif
  87. tid = r->rtm_table;
  88. if (r->rtm_family == AF_INET6)
  89. host_len = 128;
  90. else if (r->rtm_family == AF_INET)
  91. host_len = 32;
  92. if (r->rtm_family == AF_INET6) {
  93. if (G_filter.tb) {
  94. if (G_filter.tb < 0) {
  95. if (!(r->rtm_flags & RTM_F_CLONED)) {
  96. return 0;
  97. }
  98. } else {
  99. if (r->rtm_flags & RTM_F_CLONED) {
  100. return 0;
  101. }
  102. if (G_filter.tb != tid) {
  103. return 0;
  104. }
  105. }
  106. }
  107. } else {
  108. if (G_filter.tb > 0 && G_filter.tb != tid) {
  109. return 0;
  110. }
  111. }
  112. if ((G_filter.scope ^ r->rtm_scope) & G_filter.scopemask)
  113. return 0;
  114. if (G_filter.rdst.family
  115. && (r->rtm_family != G_filter.rdst.family || G_filter.rdst.bitlen > r->rtm_dst_len)
  116. ) {
  117. return 0;
  118. }
  119. if (G_filter.mdst.family
  120. && (r->rtm_family != G_filter.mdst.family
  121. || (G_filter.mdst.bitlen >= 0 && G_filter.mdst.bitlen < r->rtm_dst_len)
  122. )
  123. ) {
  124. return 0;
  125. }
  126. if (G_filter.rsrc.family
  127. && (r->rtm_family != G_filter.rsrc.family || G_filter.rsrc.bitlen > r->rtm_src_len)
  128. ) {
  129. return 0;
  130. }
  131. if (G_filter.msrc.family
  132. && (r->rtm_family != G_filter.msrc.family
  133. || (G_filter.msrc.bitlen >= 0 && G_filter.msrc.bitlen < r->rtm_src_len)
  134. )
  135. ) {
  136. return 0;
  137. }
  138. memset(&src, 0, sizeof(src));
  139. memset(&dst, 0, sizeof(dst));
  140. if (tb[RTA_SRC]) {
  141. src.bitlen = r->rtm_src_len;
  142. src.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
  143. memcpy(src.data, RTA_DATA(tb[RTA_SRC]), src.bytelen);
  144. }
  145. if (tb[RTA_DST]) {
  146. dst.bitlen = r->rtm_dst_len;
  147. dst.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
  148. memcpy(dst.data, RTA_DATA(tb[RTA_DST]), dst.bytelen);
  149. }
  150. if (G_filter.rdst.family
  151. && inet_addr_match(&dst, &G_filter.rdst, G_filter.rdst.bitlen)
  152. ) {
  153. return 0;
  154. }
  155. if (G_filter.mdst.family
  156. && G_filter.mdst.bitlen >= 0
  157. && inet_addr_match(&dst, &G_filter.mdst, r->rtm_dst_len)
  158. ) {
  159. return 0;
  160. }
  161. if (G_filter.rsrc.family
  162. && inet_addr_match(&src, &G_filter.rsrc, G_filter.rsrc.bitlen)
  163. ) {
  164. return 0;
  165. }
  166. if (G_filter.msrc.family && G_filter.msrc.bitlen >= 0
  167. && inet_addr_match(&src, &G_filter.msrc, r->rtm_src_len)
  168. ) {
  169. return 0;
  170. }
  171. if (G_filter.oif != 0) {
  172. if (!tb[RTA_OIF])
  173. return 0;
  174. if (G_filter.oif != *(int*)RTA_DATA(tb[RTA_OIF]))
  175. return 0;
  176. }
  177. if (G_filter.flushb) {
  178. struct nlmsghdr *fn;
  179. /* We are creating route flush commands */
  180. if (r->rtm_family == AF_INET6
  181. && r->rtm_dst_len == 0
  182. && r->rtm_type == RTN_UNREACHABLE
  183. && tb[RTA_PRIORITY]
  184. && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
  185. ) {
  186. return 0;
  187. }
  188. if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
  189. if (flush_update())
  190. xfunc_die();
  191. }
  192. fn = (void*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
  193. memcpy(fn, n, n->nlmsg_len);
  194. fn->nlmsg_type = RTM_DELROUTE;
  195. fn->nlmsg_flags = NLM_F_REQUEST;
  196. fn->nlmsg_seq = ++G_filter.rth->seq;
  197. G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb;
  198. G_filter.flushed = 1;
  199. return 0;
  200. }
  201. /* We are printing routes */
  202. if (n->nlmsg_type == RTM_DELROUTE) {
  203. printf("Deleted ");
  204. }
  205. if (r->rtm_type != RTN_UNICAST /* && !G_filter.type - always 0 */) {
  206. printf("%s ", rtnl_rtntype_n2a(r->rtm_type));
  207. }
  208. if (tb[RTA_DST]) {
  209. if (r->rtm_dst_len != host_len) {
  210. printf("%s/%u ",
  211. rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_DST])),
  212. r->rtm_dst_len
  213. );
  214. } else {
  215. printf("%s ", format_host(r->rtm_family,
  216. RTA_PAYLOAD(tb[RTA_DST]),
  217. RTA_DATA(tb[RTA_DST]))
  218. );
  219. }
  220. } else if (r->rtm_dst_len) {
  221. printf("0/%d ", r->rtm_dst_len);
  222. } else {
  223. printf("default ");
  224. }
  225. if (tb[RTA_SRC]) {
  226. if (r->rtm_src_len != host_len) {
  227. printf("from %s/%u ",
  228. rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_SRC])),
  229. r->rtm_src_len
  230. );
  231. } else {
  232. printf("from %s ", format_host(r->rtm_family,
  233. RTA_PAYLOAD(tb[RTA_SRC]),
  234. RTA_DATA(tb[RTA_SRC]))
  235. );
  236. }
  237. } else if (r->rtm_src_len) {
  238. printf("from 0/%u ", r->rtm_src_len);
  239. }
  240. if (tb[RTA_GATEWAY] && G_filter.rvia.bitlen != host_len) {
  241. printf("via %s ", format_host(r->rtm_family,
  242. RTA_PAYLOAD(tb[RTA_GATEWAY]),
  243. RTA_DATA(tb[RTA_GATEWAY]))
  244. );
  245. }
  246. if (tb[RTA_OIF]) {
  247. printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
  248. }
  249. #if ENABLE_FEATURE_IP_RULE
  250. if (tid && tid != RT_TABLE_MAIN && !G_filter.tb)
  251. printf("table %s ", rtnl_rttable_n2a(tid));
  252. #endif
  253. /* Todo: parse & show "proto kernel" here */
  254. if (!(r->rtm_flags & RTM_F_CLONED)) {
  255. if ((r->rtm_scope != RT_SCOPE_UNIVERSE) && G_filter.scopemask != -1)
  256. printf("scope %s ", rtnl_rtscope_n2a(r->rtm_scope));
  257. }
  258. if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) {
  259. /* Do not use format_host(). It is our local addr
  260. and symbolic name will not be useful.
  261. */
  262. printf(" src %s ", rt_addr_n2a(r->rtm_family,
  263. RTA_DATA(tb[RTA_PREFSRC])));
  264. }
  265. if (tb[RTA_PRIORITY]) {
  266. printf(" metric %d ", *(uint32_t*)RTA_DATA(tb[RTA_PRIORITY]));
  267. }
  268. if (r->rtm_flags & RTNH_F_DEAD) {
  269. printf("dead ");
  270. }
  271. if (r->rtm_flags & RTNH_F_ONLINK) {
  272. printf("onlink ");
  273. }
  274. if (r->rtm_flags & RTNH_F_PERVASIVE) {
  275. printf("pervasive ");
  276. }
  277. if (r->rtm_flags & RTM_F_NOTIFY) {
  278. printf("notify ");
  279. }
  280. if (r->rtm_family == AF_INET6) {
  281. struct rta_cacheinfo *ci = NULL;
  282. if (tb[RTA_CACHEINFO]) {
  283. ci = RTA_DATA(tb[RTA_CACHEINFO]);
  284. }
  285. if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
  286. if (r->rtm_flags & RTM_F_CLONED) {
  287. printf("%c cache ", _SL_);
  288. }
  289. if (ci->rta_expires) {
  290. printf(" expires %dsec", ci->rta_expires / get_hz());
  291. }
  292. if (ci->rta_error != 0) {
  293. printf(" error %d", ci->rta_error);
  294. }
  295. } else if (ci) {
  296. if (ci->rta_error != 0)
  297. printf(" error %d", ci->rta_error);
  298. }
  299. }
  300. if (tb[RTA_IIF] && G_filter.iif == 0) {
  301. printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
  302. }
  303. bb_putchar('\n');
  304. return 0;
  305. }
  306. static int str_is_lock(const char *str)
  307. {
  308. return strcmp(str, "lock") == 0;
  309. }
  310. /* Return value becomes exitcode. It's okay to not return at all */
  311. static int iproute_modify(int cmd, unsigned flags, char **argv)
  312. {
  313. /* If you add stuff here, update iproute_full_usage */
  314. static const char keywords[] ALIGN1 =
  315. "src\0""via\0"
  316. "mtu\0""advmss\0"
  317. "scope\0""protocol\0"IF_FEATURE_IP_RULE("table\0")
  318. "dev\0""oif\0""to\0""metric\0""onlink\0";
  319. #define keyword_via (keywords + sizeof("src"))
  320. #define keyword_mtu (keyword_via + sizeof("via"))
  321. #define keyword_advmss (keyword_mtu + sizeof("mtu"))
  322. #define keyword_scope (keyword_advmss + sizeof("advmss"))
  323. #define keyword_proto (keyword_scope + sizeof("scope"))
  324. #define keyword_table (keyword_proto + sizeof("protocol"))
  325. enum {
  326. ARG_src,
  327. ARG_via,
  328. ARG_mtu,
  329. ARG_advmss,
  330. ARG_scope,
  331. ARG_protocol,
  332. IF_FEATURE_IP_RULE(ARG_table,)
  333. ARG_dev,
  334. ARG_oif,
  335. ARG_to,
  336. ARG_metric,
  337. ARG_onlink,
  338. };
  339. enum {
  340. gw_ok = 1 << 0,
  341. dst_ok = 1 << 1,
  342. proto_ok = 1 << 2,
  343. type_ok = 1 << 3
  344. };
  345. struct rtnl_handle rth;
  346. struct {
  347. struct nlmsghdr n;
  348. struct rtmsg r;
  349. char buf[1024];
  350. } req;
  351. char mxbuf[256];
  352. struct rtattr * mxrta = (void*)mxbuf;
  353. unsigned mxlock = 0;
  354. char *d = NULL;
  355. smalluint ok = 0;
  356. smalluint scope_ok = 0;
  357. int arg;
  358. memset(&req, 0, sizeof(req));
  359. req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
  360. req.n.nlmsg_flags = NLM_F_REQUEST | flags;
  361. req.n.nlmsg_type = cmd;
  362. req.r.rtm_family = preferred_family;
  363. if (RT_TABLE_MAIN != 0) /* if it is zero, memset already did it */
  364. req.r.rtm_table = RT_TABLE_MAIN;
  365. if (RT_SCOPE_NOWHERE != 0)
  366. req.r.rtm_scope = RT_SCOPE_NOWHERE;
  367. if (cmd != RTM_DELROUTE) {
  368. req.r.rtm_scope = RT_SCOPE_UNIVERSE;
  369. if (RTPROT_BOOT != 0)
  370. req.r.rtm_protocol = RTPROT_BOOT;
  371. if (RTN_UNICAST != 0)
  372. req.r.rtm_type = RTN_UNICAST;
  373. }
  374. mxrta->rta_type = RTA_METRICS;
  375. mxrta->rta_len = RTA_LENGTH(0);
  376. while (*argv) {
  377. arg = index_in_substrings(keywords, *argv);
  378. if (arg == ARG_src) {
  379. inet_prefix addr;
  380. NEXT_ARG();
  381. get_addr(&addr, *argv, req.r.rtm_family);
  382. if (req.r.rtm_family == AF_UNSPEC)
  383. req.r.rtm_family = addr.family;
  384. addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
  385. } else if (arg == ARG_via) {
  386. inet_prefix addr;
  387. ok |= gw_ok;
  388. NEXT_ARG();
  389. get_addr(&addr, *argv, req.r.rtm_family);
  390. if (req.r.rtm_family == AF_UNSPEC) {
  391. req.r.rtm_family = addr.family;
  392. }
  393. addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
  394. } else if (arg == ARG_mtu) {
  395. unsigned mtu;
  396. NEXT_ARG();
  397. if (str_is_lock(*argv)) {
  398. mxlock |= (1 << RTAX_MTU);
  399. NEXT_ARG();
  400. }
  401. mtu = get_unsigned(*argv, keyword_mtu);
  402. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
  403. } else if (arg == ARG_advmss) {
  404. unsigned mss;
  405. NEXT_ARG();
  406. if (str_is_lock(*argv)) {
  407. mxlock |= (1 << RTAX_ADVMSS);
  408. NEXT_ARG();
  409. }
  410. mss = get_unsigned(*argv, keyword_advmss);
  411. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss);
  412. } else if (arg == ARG_scope) {
  413. uint32_t scope;
  414. NEXT_ARG();
  415. if (rtnl_rtscope_a2n(&scope, *argv))
  416. invarg_1_to_2(*argv, keyword_scope);
  417. req.r.rtm_scope = scope;
  418. scope_ok = 1;
  419. } else if (arg == ARG_protocol) {
  420. uint32_t prot;
  421. NEXT_ARG();
  422. if (rtnl_rtprot_a2n(&prot, *argv))
  423. invarg_1_to_2(*argv, keyword_proto);
  424. req.r.rtm_protocol = prot;
  425. ok |= proto_ok;
  426. #if ENABLE_FEATURE_IP_RULE
  427. } else if (arg == ARG_table) {
  428. uint32_t tid;
  429. NEXT_ARG();
  430. if (rtnl_rttable_a2n(&tid, *argv))
  431. invarg_1_to_2(*argv, keyword_table);
  432. #if HAVE_RTA_TABLE
  433. if (tid > 255) {
  434. req.r.rtm_table = RT_TABLE_UNSPEC;
  435. addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
  436. } else
  437. #endif
  438. req.r.rtm_table = tid;
  439. #endif
  440. } else if (arg == ARG_dev || arg == ARG_oif) {
  441. NEXT_ARG();
  442. d = *argv;
  443. } else if (arg == ARG_metric) {
  444. //TODO: "metric", "priority" and "preference" are synonyms
  445. uint32_t metric;
  446. NEXT_ARG();
  447. metric = get_u32(*argv, "metric");
  448. addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
  449. } else if (arg == ARG_onlink) {
  450. req.r.rtm_flags |= RTNH_F_ONLINK;
  451. } else {
  452. int type;
  453. inet_prefix dst;
  454. if (arg == ARG_to) {
  455. NEXT_ARG();
  456. }
  457. if ((**argv < '0' || **argv > '9')
  458. && rtnl_rtntype_a2n(&type, *argv) == 0
  459. ) {
  460. NEXT_ARG();
  461. req.r.rtm_type = type;
  462. ok |= type_ok;
  463. }
  464. if (ok & dst_ok) {
  465. duparg2("to", *argv);
  466. }
  467. get_prefix(&dst, *argv, req.r.rtm_family);
  468. if (req.r.rtm_family == AF_UNSPEC) {
  469. req.r.rtm_family = dst.family;
  470. }
  471. req.r.rtm_dst_len = dst.bitlen;
  472. ok |= dst_ok;
  473. if (dst.bytelen) {
  474. addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
  475. }
  476. }
  477. /* Other keywords recognized by iproute2-3.19.0: */
  478. #if 0
  479. } else if (strcmp(*argv, "from") == 0) {
  480. inet_prefix addr;
  481. NEXT_ARG();
  482. get_prefix(&addr, *argv, req.r.rtm_family);
  483. if (req.r.rtm_family == AF_UNSPEC)
  484. req.r.rtm_family = addr.family;
  485. if (addr.bytelen)
  486. addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
  487. req.r.rtm_src_len = addr.bitlen;
  488. } else if (strcmp(*argv, "tos") == 0 ||
  489. matches(*argv, "dsfield") == 0) {
  490. __u32 tos;
  491. NEXT_ARG();
  492. if (rtnl_dsfield_a2n(&tos, *argv))
  493. invarg("\"tos\" value is invalid\n", *argv);
  494. req.r.rtm_tos = tos;
  495. } else if (strcmp(*argv, "hoplimit") == 0) {
  496. unsigned hoplimit;
  497. NEXT_ARG();
  498. if (strcmp(*argv, "lock") == 0) {
  499. mxlock |= (1<<RTAX_HOPLIMIT);
  500. NEXT_ARG();
  501. }
  502. if (get_unsigned(&hoplimit, *argv, 0))
  503. invarg("\"hoplimit\" value is invalid\n", *argv);
  504. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_HOPLIMIT, hoplimit);
  505. } else if (matches(*argv, "reordering") == 0) {
  506. unsigned reord;
  507. NEXT_ARG();
  508. if (strcmp(*argv, "lock") == 0) {
  509. mxlock |= (1<<RTAX_REORDERING);
  510. NEXT_ARG();
  511. }
  512. if (get_unsigned(&reord, *argv, 0))
  513. invarg("\"reordering\" value is invalid\n", *argv);
  514. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord);
  515. } else if (strcmp(*argv, "rtt") == 0) {
  516. unsigned rtt;
  517. NEXT_ARG();
  518. if (strcmp(*argv, "lock") == 0) {
  519. mxlock |= (1<<RTAX_RTT);
  520. NEXT_ARG();
  521. }
  522. if (get_time_rtt(&rtt, *argv, &raw))
  523. invarg("\"rtt\" value is invalid\n", *argv);
  524. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT,
  525. (raw) ? rtt : rtt * 8);
  526. } else if (strcmp(*argv, "rto_min") == 0) {
  527. unsigned rto_min;
  528. NEXT_ARG();
  529. mxlock |= (1<<RTAX_RTO_MIN);
  530. if (get_time_rtt(&rto_min, *argv, &raw))
  531. invarg("\"rto_min\" value is invalid\n",
  532. *argv);
  533. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTO_MIN,
  534. rto_min);
  535. } else if (matches(*argv, "window") == 0) {
  536. unsigned win;
  537. NEXT_ARG();
  538. if (strcmp(*argv, "lock") == 0) {
  539. mxlock |= (1<<RTAX_WINDOW);
  540. NEXT_ARG();
  541. }
  542. if (get_unsigned(&win, *argv, 0))
  543. invarg("\"window\" value is invalid\n", *argv);
  544. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win);
  545. } else if (matches(*argv, "cwnd") == 0) {
  546. unsigned win;
  547. NEXT_ARG();
  548. if (strcmp(*argv, "lock") == 0) {
  549. mxlock |= (1<<RTAX_CWND);
  550. NEXT_ARG();
  551. }
  552. if (get_unsigned(&win, *argv, 0))
  553. invarg("\"cwnd\" value is invalid\n", *argv);
  554. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win);
  555. } else if (matches(*argv, "initcwnd") == 0) {
  556. unsigned win;
  557. NEXT_ARG();
  558. if (strcmp(*argv, "lock") == 0) {
  559. mxlock |= (1<<RTAX_INITCWND);
  560. NEXT_ARG();
  561. }
  562. if (get_unsigned(&win, *argv, 0))
  563. invarg("\"initcwnd\" value is invalid\n", *argv);
  564. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITCWND, win);
  565. } else if (matches(*argv, "initrwnd") == 0) {
  566. unsigned win;
  567. NEXT_ARG();
  568. if (strcmp(*argv, "lock") == 0) {
  569. mxlock |= (1<<RTAX_INITRWND);
  570. NEXT_ARG();
  571. }
  572. if (get_unsigned(&win, *argv, 0))
  573. invarg("\"initrwnd\" value is invalid\n", *argv);
  574. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITRWND, win);
  575. } else if (matches(*argv, "features") == 0) {
  576. unsigned int features = 0;
  577. while (argc > 0) {
  578. NEXT_ARG();
  579. if (strcmp(*argv, "ecn") == 0)
  580. features |= RTAX_FEATURE_ECN;
  581. else
  582. invarg("\"features\" value not valid\n", *argv);
  583. break;
  584. }
  585. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_FEATURES, features);
  586. } else if (matches(*argv, "quickack") == 0) {
  587. unsigned quickack;
  588. NEXT_ARG();
  589. if (get_unsigned(&quickack, *argv, 0))
  590. invarg("\"quickack\" value is invalid\n", *argv);
  591. if (quickack != 1 && quickack != 0)
  592. invarg("\"quickack\" value should be 0 or 1\n", *argv);
  593. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_QUICKACK, quickack);
  594. } else if (matches(*argv, "rttvar") == 0) {
  595. unsigned win;
  596. NEXT_ARG();
  597. if (strcmp(*argv, "lock") == 0) {
  598. mxlock |= (1<<RTAX_RTTVAR);
  599. NEXT_ARG();
  600. }
  601. if (get_time_rtt(&win, *argv, &raw))
  602. invarg("\"rttvar\" value is invalid\n", *argv);
  603. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR,
  604. (raw) ? win : win * 4);
  605. } else if (matches(*argv, "ssthresh") == 0) {
  606. unsigned win;
  607. NEXT_ARG();
  608. if (strcmp(*argv, "lock") == 0) {
  609. mxlock |= (1<<RTAX_SSTHRESH);
  610. NEXT_ARG();
  611. }
  612. if (get_unsigned(&win, *argv, 0))
  613. invarg("\"ssthresh\" value is invalid\n", *argv);
  614. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win);
  615. } else if (matches(*argv, "realms") == 0) {
  616. __u32 realm;
  617. NEXT_ARG();
  618. if (get_rt_realms(&realm, *argv))
  619. invarg("\"realm\" value is invalid\n", *argv);
  620. addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
  621. } else if (strcmp(*argv, "nexthop") == 0) {
  622. nhs_ok = 1;
  623. break;
  624. }
  625. #endif
  626. argv++;
  627. }
  628. xrtnl_open(&rth);
  629. if (d) {
  630. int idx;
  631. ll_init_map(&rth);
  632. if (d) {
  633. idx = xll_name_to_index(d);
  634. addattr32(&req.n, sizeof(req), RTA_OIF, idx);
  635. }
  636. }
  637. if (mxrta->rta_len > RTA_LENGTH(0)) {
  638. if (mxlock) {
  639. rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
  640. }
  641. addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
  642. }
  643. if (!scope_ok) {
  644. if (req.r.rtm_type == RTN_LOCAL || req.r.rtm_type == RTN_NAT)
  645. req.r.rtm_scope = RT_SCOPE_HOST;
  646. else
  647. if (req.r.rtm_type == RTN_BROADCAST
  648. || req.r.rtm_type == RTN_MULTICAST
  649. || req.r.rtm_type == RTN_ANYCAST
  650. ) {
  651. req.r.rtm_scope = RT_SCOPE_LINK;
  652. }
  653. else if (req.r.rtm_type == RTN_UNICAST || req.r.rtm_type == RTN_UNSPEC) {
  654. if (cmd == RTM_DELROUTE)
  655. req.r.rtm_scope = RT_SCOPE_NOWHERE;
  656. else if (!(ok & gw_ok))
  657. req.r.rtm_scope = RT_SCOPE_LINK;
  658. }
  659. }
  660. if (req.r.rtm_family == AF_UNSPEC) {
  661. req.r.rtm_family = AF_INET;
  662. }
  663. if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
  664. return 2;
  665. }
  666. return 0;
  667. }
  668. static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
  669. {
  670. struct {
  671. struct nlmsghdr nlh;
  672. struct rtmsg rtm;
  673. } req;
  674. struct sockaddr_nl nladdr;
  675. memset(&nladdr, 0, sizeof(nladdr));
  676. memset(&req, 0, sizeof(req));
  677. nladdr.nl_family = AF_NETLINK;
  678. req.nlh.nlmsg_len = sizeof(req);
  679. if (RTM_GETROUTE)
  680. req.nlh.nlmsg_type = RTM_GETROUTE;
  681. if (NLM_F_ROOT | NLM_F_REQUEST)
  682. req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
  683. /*req.nlh.nlmsg_pid = 0; - memset did it already */
  684. req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
  685. req.rtm.rtm_family = family;
  686. if (RTM_F_CLONED)
  687. req.rtm.rtm_flags = RTM_F_CLONED;
  688. return xsendto(rth->fd, (void*)&req, sizeof(req), (struct sockaddr*)&nladdr, sizeof(nladdr));
  689. }
  690. static void iproute_flush_cache(void)
  691. {
  692. static const char fn[] ALIGN1 = "/proc/sys/net/ipv4/route/flush";
  693. int flush_fd = open_or_warn(fn, O_WRONLY);
  694. if (flush_fd < 0) {
  695. return;
  696. }
  697. if (write(flush_fd, "-1", 2) < 2) {
  698. bb_simple_perror_msg("can't flush routing cache");
  699. return;
  700. }
  701. close(flush_fd);
  702. }
  703. static void iproute_reset_filter(void)
  704. {
  705. memset(&G_filter, 0, sizeof(G_filter));
  706. G_filter.mdst.bitlen = -1;
  707. G_filter.msrc.bitlen = -1;
  708. }
  709. /* Return value becomes exitcode. It's okay to not return at all */
  710. static int iproute_list_or_flush(char **argv, int flush)
  711. {
  712. int do_ipv6 = preferred_family;
  713. struct rtnl_handle rth;
  714. char *id = NULL;
  715. char *od = NULL;
  716. static const char keywords[] ALIGN1 =
  717. /* If you add stuff here, update iproute_full_usage */
  718. /* "ip route list/flush" parameters: */
  719. "protocol\0" "dev\0" "oif\0" "iif\0"
  720. "via\0" "table\0" "cache\0"
  721. "from\0" "to\0" "scope\0"
  722. /* and possible further keywords */
  723. "all\0"
  724. "root\0"
  725. "match\0"
  726. "exact\0"
  727. "main\0"
  728. ;
  729. enum {
  730. KW_proto, KW_dev, KW_oif, KW_iif,
  731. KW_via, KW_table, KW_cache,
  732. KW_from, KW_to, KW_scope,
  733. /* */
  734. KW_all,
  735. KW_root,
  736. KW_match,
  737. KW_exact,
  738. KW_main,
  739. };
  740. int arg, parm;
  741. iproute_reset_filter();
  742. G_filter.tb = RT_TABLE_MAIN;
  743. if (flush && !*argv)
  744. bb_error_msg_and_die(bb_msg_requires_arg, "\"ip route flush\"");
  745. while (*argv) {
  746. arg = index_in_substrings(keywords, *argv);
  747. if (arg == KW_proto) {
  748. uint32_t prot = 0;
  749. NEXT_ARG();
  750. //G_filter.protocolmask = -1;
  751. if (rtnl_rtprot_a2n(&prot, *argv)) {
  752. if (index_in_strings(keywords, *argv) != KW_all)
  753. invarg_1_to_2(*argv, "protocol");
  754. prot = 0;
  755. //G_filter.protocolmask = 0;
  756. }
  757. //G_filter.protocol = prot;
  758. } else if (arg == KW_dev || arg == KW_oif) {
  759. NEXT_ARG();
  760. od = *argv;
  761. } else if (arg == KW_iif) {
  762. NEXT_ARG();
  763. id = *argv;
  764. } else if (arg == KW_via) {
  765. NEXT_ARG();
  766. get_prefix(&G_filter.rvia, *argv, do_ipv6);
  767. } else if (arg == KW_table) { /* table all/cache/main */
  768. NEXT_ARG();
  769. parm = index_in_substrings(keywords, *argv);
  770. if (parm == KW_cache)
  771. G_filter.tb = -1;
  772. else if (parm == KW_all)
  773. G_filter.tb = 0;
  774. else if (parm != KW_main) {
  775. #if ENABLE_FEATURE_IP_RULE
  776. uint32_t tid;
  777. if (rtnl_rttable_a2n(&tid, *argv))
  778. invarg_1_to_2(*argv, "table");
  779. G_filter.tb = tid;
  780. #else
  781. invarg_1_to_2(*argv, "table");
  782. #endif
  783. }
  784. } else if (arg == KW_cache) {
  785. /* The command 'ip route flush cache' is used by OpenSWAN.
  786. * Assuming it's a synonym for 'ip route flush table cache' */
  787. G_filter.tb = -1;
  788. } else if (arg == KW_scope) {
  789. uint32_t scope;
  790. NEXT_ARG();
  791. G_filter.scopemask = -1;
  792. if (rtnl_rtscope_a2n(&scope, *argv)) {
  793. if (strcmp(*argv, "all") != 0)
  794. invarg_1_to_2(*argv, "scope");
  795. scope = RT_SCOPE_NOWHERE;
  796. G_filter.scopemask = 0;
  797. }
  798. G_filter.scope = scope;
  799. } else if (arg == KW_from) {
  800. NEXT_ARG();
  801. parm = index_in_substrings(keywords, *argv);
  802. if (parm == KW_root) {
  803. NEXT_ARG();
  804. get_prefix(&G_filter.rsrc, *argv, do_ipv6);
  805. } else if (parm == KW_match) {
  806. NEXT_ARG();
  807. get_prefix(&G_filter.msrc, *argv, do_ipv6);
  808. } else {
  809. if (parm == KW_exact)
  810. NEXT_ARG();
  811. get_prefix(&G_filter.msrc, *argv, do_ipv6);
  812. G_filter.rsrc = G_filter.msrc;
  813. }
  814. } else { /* "to" is the default parameter */
  815. if (arg == KW_to) {
  816. NEXT_ARG();
  817. arg = index_in_substrings(keywords, *argv);
  818. }
  819. /* parm = arg; - would be more plausible, but we reuse 'arg' here */
  820. if (arg == KW_root) {
  821. NEXT_ARG();
  822. get_prefix(&G_filter.rdst, *argv, do_ipv6);
  823. } else if (arg == KW_match) {
  824. NEXT_ARG();
  825. get_prefix(&G_filter.mdst, *argv, do_ipv6);
  826. } else { /* "to exact" is the default */
  827. if (arg == KW_exact)
  828. NEXT_ARG();
  829. get_prefix(&G_filter.mdst, *argv, do_ipv6);
  830. G_filter.rdst = G_filter.mdst;
  831. }
  832. }
  833. argv++;
  834. }
  835. if (do_ipv6 == AF_UNSPEC && G_filter.tb) {
  836. do_ipv6 = AF_INET;
  837. }
  838. xrtnl_open(&rth);
  839. ll_init_map(&rth);
  840. if (id || od) {
  841. int idx;
  842. if (id) {
  843. idx = xll_name_to_index(id);
  844. G_filter.iif = idx;
  845. }
  846. if (od) {
  847. idx = xll_name_to_index(od);
  848. G_filter.oif = idx;
  849. }
  850. }
  851. if (flush) {
  852. char flushb[4096-512];
  853. if (G_filter.tb == -1) { /* "flush table cache" */
  854. if (do_ipv6 != AF_INET6)
  855. iproute_flush_cache();
  856. if (do_ipv6 == AF_INET)
  857. return 0;
  858. }
  859. G_filter.flushb = flushb;
  860. G_filter.flushp = 0;
  861. G_filter.flushe = sizeof(flushb);
  862. G_filter.rth = &rth;
  863. for (;;) {
  864. xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
  865. G_filter.flushed = 0;
  866. xrtnl_dump_filter(&rth, print_route, NULL);
  867. if (G_filter.flushed == 0)
  868. return 0;
  869. if (flush_update())
  870. return 1;
  871. }
  872. }
  873. if (G_filter.tb != -1) {
  874. xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
  875. } else if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
  876. bb_simple_perror_msg_and_die("can't send dump request");
  877. }
  878. xrtnl_dump_filter(&rth, print_route, NULL);
  879. return 0;
  880. }
  881. /* Return value becomes exitcode. It's okay to not return at all */
  882. static int iproute_get(char **argv)
  883. {
  884. struct rtnl_handle rth;
  885. struct {
  886. struct nlmsghdr n;
  887. struct rtmsg r;
  888. char buf[1024];
  889. } req;
  890. char *idev = NULL;
  891. char *odev = NULL;
  892. bool connected = 0;
  893. bool from_ok = 0;
  894. static const char options[] ALIGN1 =
  895. "from\0""iif\0""oif\0""dev\0""notify\0""connected\0""to\0";
  896. memset(&req, 0, sizeof(req));
  897. iproute_reset_filter();
  898. req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
  899. if (NLM_F_REQUEST)
  900. req.n.nlmsg_flags = NLM_F_REQUEST;
  901. if (RTM_GETROUTE)
  902. req.n.nlmsg_type = RTM_GETROUTE;
  903. req.r.rtm_family = preferred_family;
  904. /*req.r.rtm_table = 0; - memset did this already */
  905. /*req.r.rtm_protocol = 0;*/
  906. /*req.r.rtm_scope = 0;*/
  907. /*req.r.rtm_type = 0;*/
  908. /*req.r.rtm_src_len = 0;*/
  909. /*req.r.rtm_dst_len = 0;*/
  910. /*req.r.rtm_tos = 0;*/
  911. while (*argv) {
  912. switch (index_in_strings(options, *argv)) {
  913. case 0: /* from */
  914. {
  915. inet_prefix addr;
  916. NEXT_ARG();
  917. from_ok = 1;
  918. get_prefix(&addr, *argv, req.r.rtm_family);
  919. if (req.r.rtm_family == AF_UNSPEC) {
  920. req.r.rtm_family = addr.family;
  921. }
  922. if (addr.bytelen) {
  923. addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
  924. }
  925. req.r.rtm_src_len = addr.bitlen;
  926. break;
  927. }
  928. case 1: /* iif */
  929. NEXT_ARG();
  930. idev = *argv;
  931. break;
  932. case 2: /* oif */
  933. case 3: /* dev */
  934. NEXT_ARG();
  935. odev = *argv;
  936. break;
  937. case 4: /* notify */
  938. req.r.rtm_flags |= RTM_F_NOTIFY;
  939. break;
  940. case 5: /* connected */
  941. connected = 1;
  942. break;
  943. case 6: /* to */
  944. NEXT_ARG();
  945. default:
  946. {
  947. inet_prefix addr;
  948. get_prefix(&addr, *argv, req.r.rtm_family);
  949. if (req.r.rtm_family == AF_UNSPEC) {
  950. req.r.rtm_family = addr.family;
  951. }
  952. if (addr.bytelen) {
  953. addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
  954. }
  955. req.r.rtm_dst_len = addr.bitlen;
  956. }
  957. }
  958. argv++;
  959. }
  960. if (req.r.rtm_dst_len == 0) {
  961. bb_simple_error_msg_and_die("need at least destination address");
  962. }
  963. xrtnl_open(&rth);
  964. ll_init_map(&rth);
  965. if (idev || odev) {
  966. int idx;
  967. if (idev) {
  968. idx = xll_name_to_index(idev);
  969. addattr32(&req.n, sizeof(req), RTA_IIF, idx);
  970. }
  971. if (odev) {
  972. idx = xll_name_to_index(odev);
  973. addattr32(&req.n, sizeof(req), RTA_OIF, idx);
  974. }
  975. }
  976. if (req.r.rtm_family == AF_UNSPEC) {
  977. req.r.rtm_family = AF_INET;
  978. }
  979. if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
  980. return 2;
  981. }
  982. if (connected && !from_ok) {
  983. struct rtmsg *r = NLMSG_DATA(&req.n);
  984. int len = req.n.nlmsg_len;
  985. struct rtattr * tb[RTA_MAX+1];
  986. print_route(NULL, &req.n, NULL);
  987. if (req.n.nlmsg_type != RTM_NEWROUTE) {
  988. bb_simple_error_msg_and_die("not a route?");
  989. }
  990. len -= NLMSG_LENGTH(sizeof(*r));
  991. if (len < 0) {
  992. bb_error_msg_and_die("wrong len %d", len);
  993. }
  994. //memset(tb, 0, sizeof(tb)); - parse_rtattr does this
  995. parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
  996. if (tb[RTA_PREFSRC]) {
  997. tb[RTA_PREFSRC]->rta_type = RTA_SRC;
  998. r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
  999. } else if (!tb[RTA_SRC]) {
  1000. bb_simple_error_msg_and_die("can't connect the route");
  1001. }
  1002. if (!odev && tb[RTA_OIF]) {
  1003. tb[RTA_OIF]->rta_type = 0;
  1004. }
  1005. if (tb[RTA_GATEWAY]) {
  1006. tb[RTA_GATEWAY]->rta_type = 0;
  1007. }
  1008. if (!idev && tb[RTA_IIF]) {
  1009. tb[RTA_IIF]->rta_type = 0;
  1010. }
  1011. req.n.nlmsg_flags = NLM_F_REQUEST;
  1012. req.n.nlmsg_type = RTM_GETROUTE;
  1013. if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
  1014. return 2;
  1015. }
  1016. }
  1017. print_route(NULL, &req.n, NULL);
  1018. return 0;
  1019. }
  1020. /* Return value becomes exitcode. It's okay to not return at all */
  1021. int FAST_FUNC do_iproute(char **argv)
  1022. {
  1023. static const char ip_route_commands[] ALIGN1 =
  1024. "a\0""add\0""append\0""change\0""chg\0"
  1025. "delete\0""get\0""list\0""show\0"
  1026. "prepend\0""replace\0""test\0""flush\0"
  1027. ;
  1028. enum {
  1029. CMD_a = 0, CMD_add, CMD_append, CMD_change, CMD_chg,
  1030. CMD_delete, CMD_get, CMD_list, CMD_show,
  1031. CMD_prepend, CMD_replace, CMD_test, CMD_flush,
  1032. };
  1033. int command_num;
  1034. unsigned flags = 0;
  1035. int cmd = RTM_NEWROUTE;
  1036. INIT_G();
  1037. if (!*argv)
  1038. return iproute_list_or_flush(argv, 0);
  1039. /* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */
  1040. /* It probably means that it is using "first match" rule */
  1041. command_num = index_in_substrings(ip_route_commands, *argv);
  1042. switch (command_num) {
  1043. case CMD_a:
  1044. case CMD_add:
  1045. flags = NLM_F_CREATE|NLM_F_EXCL;
  1046. break;
  1047. case CMD_append:
  1048. flags = NLM_F_CREATE|NLM_F_APPEND;
  1049. break;
  1050. case CMD_change:
  1051. case CMD_chg:
  1052. flags = NLM_F_REPLACE;
  1053. break;
  1054. case CMD_delete:
  1055. cmd = RTM_DELROUTE;
  1056. break;
  1057. case CMD_get:
  1058. return iproute_get(argv + 1);
  1059. case CMD_list:
  1060. case CMD_show:
  1061. return iproute_list_or_flush(argv + 1, 0);
  1062. case CMD_prepend:
  1063. flags = NLM_F_CREATE;
  1064. break;
  1065. case CMD_replace:
  1066. flags = NLM_F_CREATE|NLM_F_REPLACE;
  1067. break;
  1068. case CMD_test:
  1069. flags = NLM_F_EXCL;
  1070. break;
  1071. case CMD_flush:
  1072. return iproute_list_or_flush(argv + 1, 1);
  1073. default:
  1074. invarg_1_to_2(*argv, applet_name);
  1075. }
  1076. return iproute_modify(cmd, flags, argv + 1);
  1077. }