iprule.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * iprule.c "ip rule".
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License
  7. * as published by the Free Software Foundation; either version
  8. * 2 of the License, or (at your option) any later version.
  9. *
  10. * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  11. *
  12. *
  13. * Changes:
  14. *
  15. * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
  16. */
  17. #include "libbb.h"
  18. #include <syslog.h>
  19. #include <sys/socket.h>
  20. #include <netinet/in.h>
  21. #include <netinet/ip.h>
  22. #include <arpa/inet.h>
  23. #include "rt_names.h"
  24. #include "utils.h"
  25. /*
  26. static void usage(void) __attribute__((noreturn));
  27. static void usage(void)
  28. {
  29. fprintf(stderr, "Usage: ip rule [ list | add | del ] SELECTOR ACTION\n");
  30. fprintf(stderr, "SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n");
  31. fprintf(stderr, " [ dev STRING ] [ pref NUMBER ]\n");
  32. fprintf(stderr, "ACTION := [ table TABLE_ID ] [ nat ADDRESS ]\n");
  33. fprintf(stderr, " [ prohibit | reject | unreachable ]\n");
  34. fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n");
  35. fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n");
  36. exit(-1);
  37. }
  38. */
  39. static int print_rule(struct sockaddr_nl *who ATTRIBUTE_UNUSED,
  40. struct nlmsghdr *n, void *arg)
  41. {
  42. FILE *fp = (FILE*)arg;
  43. struct rtmsg *r = NLMSG_DATA(n);
  44. int len = n->nlmsg_len;
  45. int host_len = -1;
  46. struct rtattr * tb[RTA_MAX+1];
  47. char abuf[256];
  48. SPRINT_BUF(b1);
  49. if (n->nlmsg_type != RTM_NEWRULE)
  50. return 0;
  51. len -= NLMSG_LENGTH(sizeof(*r));
  52. if (len < 0)
  53. return -1;
  54. memset(tb, 0, sizeof(tb));
  55. parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
  56. if (r->rtm_family == AF_INET)
  57. host_len = 32;
  58. else if (r->rtm_family == AF_INET6)
  59. host_len = 128;
  60. /* else if (r->rtm_family == AF_DECnet)
  61. host_len = 16;
  62. else if (r->rtm_family == AF_IPX)
  63. host_len = 80;
  64. */
  65. if (tb[RTA_PRIORITY])
  66. fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[RTA_PRIORITY]));
  67. else
  68. fprintf(fp, "0:\t");
  69. fprintf(fp, "from ");
  70. if (tb[RTA_SRC]) {
  71. if (r->rtm_src_len != host_len) {
  72. fprintf(fp, "%s/%u", rt_addr_n2a(r->rtm_family,
  73. RTA_PAYLOAD(tb[RTA_SRC]),
  74. RTA_DATA(tb[RTA_SRC]),
  75. abuf, sizeof(abuf)),
  76. r->rtm_src_len
  77. );
  78. } else {
  79. fprintf(fp, "%s", format_host(r->rtm_family,
  80. RTA_PAYLOAD(tb[RTA_SRC]),
  81. RTA_DATA(tb[RTA_SRC]),
  82. abuf, sizeof(abuf))
  83. );
  84. }
  85. } else if (r->rtm_src_len) {
  86. fprintf(fp, "0/%d", r->rtm_src_len);
  87. } else {
  88. fprintf(fp, "all");
  89. }
  90. fprintf(fp, " ");
  91. if (tb[RTA_DST]) {
  92. if (r->rtm_dst_len != host_len) {
  93. fprintf(fp, "to %s/%u ", rt_addr_n2a(r->rtm_family,
  94. RTA_PAYLOAD(tb[RTA_DST]),
  95. RTA_DATA(tb[RTA_DST]),
  96. abuf, sizeof(abuf)),
  97. r->rtm_dst_len
  98. );
  99. } else {
  100. fprintf(fp, "to %s ", format_host(r->rtm_family,
  101. RTA_PAYLOAD(tb[RTA_DST]),
  102. RTA_DATA(tb[RTA_DST]),
  103. abuf, sizeof(abuf)));
  104. }
  105. } else if (r->rtm_dst_len) {
  106. fprintf(fp, "to 0/%d ", r->rtm_dst_len);
  107. }
  108. if (r->rtm_tos) {
  109. fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
  110. }
  111. if (tb[RTA_PROTOINFO]) {
  112. fprintf(fp, "fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO]));
  113. }
  114. if (tb[RTA_IIF]) {
  115. fprintf(fp, "iif %s ", (char*)RTA_DATA(tb[RTA_IIF]));
  116. }
  117. if (r->rtm_table)
  118. fprintf(fp, "lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1)));
  119. if (tb[RTA_FLOW]) {
  120. uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]);
  121. uint32_t from = to>>16;
  122. to &= 0xFFFF;
  123. if (from) {
  124. fprintf(fp, "realms %s/",
  125. rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
  126. }
  127. fprintf(fp, "%s ",
  128. rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
  129. }
  130. if (r->rtm_type == RTN_NAT) {
  131. if (tb[RTA_GATEWAY]) {
  132. fprintf(fp, "map-to %s ",
  133. format_host(r->rtm_family,
  134. RTA_PAYLOAD(tb[RTA_GATEWAY]),
  135. RTA_DATA(tb[RTA_GATEWAY]),
  136. abuf, sizeof(abuf)));
  137. } else
  138. fprintf(fp, "masquerade");
  139. } else if (r->rtm_type != RTN_UNICAST)
  140. fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
  141. fprintf(fp, "\n");
  142. fflush(fp);
  143. return 0;
  144. }
  145. int iprule_list(int argc, char **argv)
  146. {
  147. struct rtnl_handle rth;
  148. int af = preferred_family;
  149. if (af == AF_UNSPEC)
  150. af = AF_INET;
  151. if (argc > 0) {
  152. bb_error_msg("\"rule show\" needs no arguments");
  153. return -1;
  154. }
  155. if (rtnl_open(&rth, 0) < 0)
  156. return 1;
  157. if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) {
  158. bb_perror_msg("Cannot send dump request");
  159. return 1;
  160. }
  161. if (rtnl_dump_filter(&rth, print_rule, stdout, NULL, NULL) < 0) {
  162. bb_error_msg("Dump terminated");
  163. return 1;
  164. }
  165. return 0;
  166. }
  167. int iprule_modify(int cmd, int argc, char **argv)
  168. {
  169. int table_ok = 0;
  170. struct rtnl_handle rth;
  171. struct {
  172. struct nlmsghdr n;
  173. struct rtmsg r;
  174. char buf[1024];
  175. } req;
  176. memset(&req, 0, sizeof(req));
  177. req.n.nlmsg_type = cmd;
  178. req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
  179. req.n.nlmsg_flags = NLM_F_REQUEST;
  180. req.r.rtm_family = preferred_family;
  181. req.r.rtm_protocol = RTPROT_BOOT;
  182. req.r.rtm_scope = RT_SCOPE_UNIVERSE;
  183. req.r.rtm_table = 0;
  184. req.r.rtm_type = RTN_UNSPEC;
  185. if (cmd == RTM_NEWRULE) {
  186. req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
  187. req.r.rtm_type = RTN_UNICAST;
  188. }
  189. while (argc > 0) {
  190. if (strcmp(*argv, "from") == 0) {
  191. inet_prefix dst;
  192. NEXT_ARG();
  193. get_prefix(&dst, *argv, req.r.rtm_family);
  194. req.r.rtm_src_len = dst.bitlen;
  195. addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen);
  196. } else if (strcmp(*argv, "to") == 0) {
  197. inet_prefix dst;
  198. NEXT_ARG();
  199. get_prefix(&dst, *argv, req.r.rtm_family);
  200. req.r.rtm_dst_len = dst.bitlen;
  201. addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
  202. } else if (matches(*argv, "preference") == 0 ||
  203. matches(*argv, "order") == 0 ||
  204. matches(*argv, "priority") == 0) {
  205. uint32_t pref;
  206. NEXT_ARG();
  207. if (get_u32(&pref, *argv, 0))
  208. invarg("preference value", *argv);
  209. addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref);
  210. } else if (strcmp(*argv, "tos") == 0) {
  211. uint32_t tos;
  212. NEXT_ARG();
  213. if (rtnl_dsfield_a2n(&tos, *argv))
  214. invarg("TOS value", *argv);
  215. req.r.rtm_tos = tos;
  216. } else if (strcmp(*argv, "fwmark") == 0) {
  217. uint32_t fwmark;
  218. NEXT_ARG();
  219. if (get_u32(&fwmark, *argv, 0))
  220. invarg("fwmark value", *argv);
  221. addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark);
  222. } else if (matches(*argv, "realms") == 0) {
  223. uint32_t realm;
  224. NEXT_ARG();
  225. if (get_rt_realms(&realm, *argv))
  226. invarg("realms", *argv);
  227. addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
  228. } else if (matches(*argv, "table") == 0 ||
  229. strcmp(*argv, "lookup") == 0) {
  230. int tid;
  231. NEXT_ARG();
  232. if (rtnl_rttable_a2n(&tid, *argv))
  233. invarg("table ID", *argv);
  234. req.r.rtm_table = tid;
  235. table_ok = 1;
  236. } else if (strcmp(*argv, "dev") == 0 ||
  237. strcmp(*argv, "iif") == 0) {
  238. NEXT_ARG();
  239. addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1);
  240. } else if (strcmp(*argv, "nat") == 0 ||
  241. matches(*argv, "map-to") == 0) {
  242. NEXT_ARG();
  243. addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv));
  244. req.r.rtm_type = RTN_NAT;
  245. } else {
  246. int type;
  247. if (strcmp(*argv, "type") == 0) {
  248. NEXT_ARG();
  249. }
  250. if (matches(*argv, "help") == 0)
  251. bb_show_usage();
  252. if (rtnl_rtntype_a2n(&type, *argv))
  253. invarg("Failed to parse rule type", *argv);
  254. req.r.rtm_type = type;
  255. }
  256. argc--;
  257. argv++;
  258. }
  259. if (req.r.rtm_family == AF_UNSPEC)
  260. req.r.rtm_family = AF_INET;
  261. if (!table_ok && cmd == RTM_NEWRULE)
  262. req.r.rtm_table = RT_TABLE_MAIN;
  263. if (rtnl_open(&rth, 0) < 0)
  264. return 1;
  265. if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
  266. return 2;
  267. return 0;
  268. }
  269. int do_iprule(int argc, char **argv)
  270. {
  271. static const char * const ip_rule_commands[] =
  272. {"add", "delete", "list", "show", 0};
  273. int command_num = 2;
  274. int cmd;
  275. if (argc < 1)
  276. return iprule_list(0, NULL);
  277. if (*argv)
  278. command_num = index_in_substr_array(ip_rule_commands, *argv);
  279. switch (command_num) {
  280. case 0: /* add */
  281. cmd = RTM_NEWRULE;
  282. break;
  283. case 1: /* delete */
  284. cmd = RTM_DELRULE;
  285. break;
  286. case 2: /* list */
  287. case 3: /* show */
  288. return iprule_list(argc-1, argv+1);
  289. break;
  290. default:
  291. bb_error_msg_and_die("unknown command %s", *argv);
  292. }
  293. return iprule_modify(cmd, argc-1, argv+1);
  294. }