iproute.c 29 KB

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