route.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. /* route.c - Display/edit network routing table.
  2. *
  3. * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
  4. * Copyright 2013 Kyungwan Han <asura321@gmail.com>
  5. * Copyright 2020 Eric Molitor <eric@molitor.org>
  6. *
  7. * No Standard
  8. *
  9. * route add -net target 10.0.0.0 netmask 255.0.0.0 dev eth0
  10. * route del delete
  11. * delete net route, must match netmask, informative error message
  12. *
  13. * mod dyn reinstate metric netmask gw mss window irtt dev
  14. USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_SBIN))
  15. config ROUTE
  16. bool "route"
  17. default n
  18. help
  19. usage: route [-ne] [-A [inet|inet6]] [add|del TARGET [OPTIONS]]
  20. Display, add or delete network routes in the "Forwarding Information Base",
  21. which send packets out a network interface to an address.
  22. -n Show numerical addresses (no DNS lookups)
  23. -e display netstat fields
  24. Assigning an address to an interface automatically creates an appropriate
  25. network route ("ifconfig eth0 10.0.2.15/8" does "route add 10.0.0.0/8 eth0"
  26. for you), although some devices (such as loopback) won't show it in the
  27. table. For machines more than one hop away, you need to specify a gateway
  28. (ala "route add default gw 10.0.2.2").
  29. The address "default" is a wildcard address (0.0.0.0/0) matching all
  30. packets without a more specific route.
  31. Available OPTIONS include:
  32. reject - blocking route (force match failure)
  33. dev NAME - force matching packets out this interface (ala "eth0")
  34. netmask - old way of saying things like ADDR/24
  35. gw ADDR - forward packets to gateway ADDR
  36. */
  37. #define FOR_route
  38. #include "toys.h"
  39. #define _LINUX_SYSINFO_H // workaround for musl bug
  40. #include <linux/rtnetlink.h>
  41. GLOBALS(
  42. char *A;
  43. )
  44. struct _arglist {
  45. char *arg;
  46. int action;
  47. };
  48. static struct _arglist arglist1[] = {
  49. { "add", 1 }, { "del", 2 },
  50. { "delete", 2 }, { NULL, 0 }
  51. };
  52. static struct _arglist arglist2[] = {
  53. { "-net", 1 }, { "-host", 2 },
  54. { NULL, 0 }
  55. };
  56. void xsend(int sockfd, void *buf, size_t len)
  57. {
  58. if (send(sockfd, buf, len, 0) != len) perror_exit("xsend");
  59. }
  60. int xrecv(int sockfd, void *buf, size_t len)
  61. {
  62. int msg_len = recv(sockfd, buf, len, 0);
  63. if (msg_len < 0) perror_exit("xrecv");
  64. return msg_len;
  65. }
  66. void addAttr(struct nlmsghdr *nl, int maxlen, void *attr, int type, int len)
  67. {
  68. struct rtattr *rt;
  69. int rtlen = RTA_LENGTH(len);
  70. if (NLMSG_ALIGN(nl->nlmsg_len) + rtlen > maxlen) perror_exit("addAttr");
  71. rt = (struct rtattr*)((char *)nl + NLMSG_ALIGN(nl->nlmsg_len));
  72. rt->rta_type = type;
  73. rt->rta_len = rtlen;
  74. memcpy(RTA_DATA(rt), attr, len);
  75. nl->nlmsg_len = NLMSG_ALIGN(nl->nlmsg_len) + rtlen;
  76. }
  77. static void get_hostname(sa_family_t f, void *a, char *dst, size_t len) {
  78. size_t a_len = (AF_INET6 == f) ? sizeof(struct in6_addr) : sizeof(struct in_addr);
  79. struct hostent *host = gethostbyaddr(a, a_len, f);
  80. if (host) xstrncpy(dst, host->h_name, len);
  81. }
  82. static void display_routes(sa_family_t f)
  83. {
  84. int fd, msg_hdr_len, route_protocol;
  85. struct {
  86. struct nlmsghdr nl;
  87. struct rtmsg rt;
  88. } req;
  89. struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)];
  90. struct nlmsghdr *msg_hdr_ptr;
  91. struct rtmsg *route_entry;
  92. struct rtattr *rteattr;
  93. fd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  94. memset(&req, 0, sizeof(req));
  95. req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
  96. req.nl.nlmsg_type = RTM_GETROUTE;
  97. req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
  98. req.nl.nlmsg_pid = getpid();
  99. req.nl.nlmsg_seq = 1;
  100. req.rt.rtm_family = f;
  101. req.rt.rtm_table = RT_TABLE_MAIN;
  102. xsend(fd, &req, sizeof(req));
  103. if (f == AF_INET) {
  104. xprintf("Kernel IP routing table\n"
  105. "Destination Gateway Genmask Flags %s Iface\n",
  106. FLAG(e) ? " MSS Window irtt" : "Metric Ref Use");
  107. } else {
  108. xprintf("Kernel IPv6 routing table\n"
  109. "%-31s%-26s Flag Metric Ref Use If\n", "Destination", "Next Hop");
  110. }
  111. msg_hdr_len = xrecv(fd, buf, sizeof(buf));
  112. msg_hdr_ptr = buf;
  113. while (msg_hdr_ptr->nlmsg_type != NLMSG_DONE) {
  114. while (NLMSG_OK(msg_hdr_ptr, msg_hdr_len)) {
  115. route_entry = NLMSG_DATA(msg_hdr_ptr);
  116. route_protocol = route_entry->rtm_protocol;
  117. // Annoyingly NLM_F_MATCH is not yet implemented so even if we pass in
  118. // RT_TABLE_MAIN with RTM_GETROUTE it still returns everything so we
  119. // have to filter here.
  120. if (route_entry->rtm_table == RT_TABLE_MAIN) {
  121. int route_attribute_len;
  122. char dest[INET6_ADDRSTRLEN], gate[INET6_ADDRSTRLEN], netmask[32],
  123. flags[10] = "U", if_name[IF_NAMESIZE] = "-";
  124. unsigned priority = 0, mss = 0, win = 0, irtt = 0, ref = 0, use = 0,
  125. route_netmask, metric_len;
  126. struct in_addr netmask_addr;
  127. struct rtattr *metric;
  128. struct rta_cacheinfo *cache_info;
  129. if (f == AF_INET) {
  130. strcpy(dest, FLAG(n) ? "0.0.0.0" : "default");
  131. strcpy(gate, FLAG(n) ? "*" : "0.0.0.0");
  132. strcpy(netmask, "0.0.0.0");
  133. } else {
  134. strcpy(dest, "::");
  135. strcpy(gate, "::");
  136. }
  137. route_netmask = route_entry->rtm_dst_len;
  138. if (route_netmask == 0) netmask_addr.s_addr = ~((in_addr_t) -1);
  139. else netmask_addr.s_addr = htonl(~((1 << (32 - route_netmask)) - 1));
  140. inet_ntop(AF_INET, &netmask_addr, netmask, sizeof(netmask));
  141. rteattr = RTM_RTA(route_entry);
  142. route_attribute_len = RTM_PAYLOAD(msg_hdr_ptr);
  143. while (RTA_OK(rteattr, route_attribute_len)) {
  144. switch (rteattr->rta_type) {
  145. case RTA_DST:
  146. if (FLAG(n)) inet_ntop(f, RTA_DATA(rteattr), dest, sizeof(dest));
  147. else get_hostname(f, RTA_DATA(rteattr), dest, sizeof(dest));
  148. break;
  149. case RTA_GATEWAY:
  150. if (FLAG(n)) inet_ntop(f, RTA_DATA(rteattr), gate, sizeof(dest));
  151. else get_hostname(f, RTA_DATA(rteattr), gate, sizeof(dest));
  152. strcat(flags, "G");
  153. break;
  154. case RTA_PRIORITY:
  155. priority = *(unsigned *)RTA_DATA(rteattr);
  156. break;
  157. case RTA_OIF:
  158. if_indextoname(*(int *)RTA_DATA(rteattr), if_name);
  159. break;
  160. case RTA_METRICS:
  161. metric_len = RTA_PAYLOAD(rteattr);
  162. for (metric = RTA_DATA(rteattr); RTA_OK(metric, metric_len);
  163. metric = RTA_NEXT(metric, metric_len))
  164. if (metric->rta_type == RTAX_ADVMSS)
  165. mss = *(unsigned *)RTA_DATA(metric);
  166. else if (metric->rta_type == RTAX_WINDOW)
  167. win = *(unsigned *)RTA_DATA(metric);
  168. else if (metric->rta_type == RTAX_RTT)
  169. irtt = (*(unsigned *)RTA_DATA(metric))/8;
  170. break;
  171. case RTA_CACHEINFO:
  172. cache_info = RTA_DATA(rteattr);
  173. ref = cache_info->rta_clntref;
  174. use = cache_info->rta_used;
  175. break;
  176. }
  177. rteattr = RTA_NEXT(rteattr, route_attribute_len);
  178. }
  179. if (route_entry->rtm_type == RTN_UNREACHABLE) flags[0] = '!';
  180. if (route_netmask == 32) strcat(flags, "H");
  181. if (route_protocol == RTPROT_REDIRECT) strcat(flags, "D");
  182. if (f == AF_INET) {
  183. xprintf("%-15.15s %-15.15s %-16s%-6s", dest, gate, netmask, flags);
  184. if (FLAG(e)) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, if_name);
  185. else xprintf("%-6d %-2d %7d %s\n", priority, ref, use, if_name);
  186. } else {
  187. char *dest_with_mask = xmprintf("%s/%u", dest, route_netmask);
  188. xprintf("%-30s %-26s %-4s %-6d %-4d %2d %-8s\n",
  189. dest_with_mask, gate, flags, priority, ref, use, if_name);
  190. free(dest_with_mask);
  191. }
  192. }
  193. msg_hdr_ptr = NLMSG_NEXT(msg_hdr_ptr, msg_hdr_len);
  194. }
  195. msg_hdr_len = xrecv(fd, buf, sizeof(buf));
  196. msg_hdr_ptr = buf;
  197. }
  198. xclose(fd);
  199. }
  200. // find parameter (add/del/net/host) in list, return appropriate action or 0.
  201. static int get_action(char ***argv, struct _arglist *list)
  202. {
  203. struct _arglist *alist;
  204. if (!**argv) return 0;
  205. for (alist = list; alist->arg; alist++) { //find the given parameter in list
  206. if (!strcmp(**argv, alist->arg)) {
  207. *argv += 1;
  208. return alist->action;
  209. }
  210. }
  211. return 0;
  212. }
  213. // add/del a route.
  214. static void setroute(sa_family_t f, char **argv)
  215. {
  216. char *tgtip;
  217. int sockfd, arg2_action;
  218. int action = get_action(&argv, arglist1); //verify the arg for add/del.
  219. struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)];
  220. struct nlmsghdr *nlMsg;
  221. struct rtmsg *rtMsg;
  222. if (!action || !*argv) help_exit("setroute");
  223. arg2_action = get_action(&argv, arglist2); //verify the arg for -net or -host
  224. if (!*argv) help_exit("setroute");
  225. tgtip = *argv++;
  226. sockfd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  227. memset(buf, 0, sizeof(buf));
  228. nlMsg = (struct nlmsghdr *) buf;
  229. rtMsg = (struct rtmsg *) NLMSG_DATA(nlMsg);
  230. nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
  231. //TODO(emolitor): Improve action and arg2_action handling
  232. if (action == 1) { // Add
  233. nlMsg->nlmsg_type = RTM_NEWROUTE;
  234. nlMsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
  235. } else { // Delete
  236. nlMsg->nlmsg_type = RTM_DELROUTE;
  237. nlMsg->nlmsg_flags = NLM_F_REQUEST;
  238. }
  239. nlMsg->nlmsg_pid = getpid();
  240. nlMsg->nlmsg_seq = 1;
  241. rtMsg->rtm_family = f;
  242. rtMsg->rtm_table = RT_TABLE_UNSPEC;
  243. rtMsg->rtm_type = RTN_UNICAST;
  244. rtMsg->rtm_protocol = RTPROT_UNSPEC;
  245. rtMsg->rtm_flags = RTM_F_NOTIFY;
  246. rtMsg->rtm_dst_len = rtMsg->rtm_src_len = (f == AF_INET) ? 32 : 128;
  247. if (arg2_action == 2) rtMsg->rtm_scope = RT_SCOPE_HOST;
  248. size_t addr_len = sizeof(struct in_addr);
  249. if (f == AF_INET6) addr_len = sizeof(struct in6_addr);
  250. unsigned char addr[sizeof(struct in6_addr)] = {0,};
  251. for (; *argv; argv++) {
  252. if (!strcmp(*argv, "mod")) continue;
  253. else if (!strcmp(*argv, "dyn")) continue;
  254. else if (!strcmp(*argv, "reinstate")) continue;
  255. else if (!strcmp(*argv, "reject")) rtMsg->rtm_type = RTN_UNREACHABLE;
  256. else {
  257. if (!argv[1]) show_help(stdout, 1);
  258. if (!strcmp(*argv, "metric")) {
  259. unsigned int priority = atolx_range(argv[1], 0, UINT_MAX);
  260. addAttr(nlMsg, sizeof(toybuf), &priority, RTA_PRIORITY, sizeof(unsigned int));
  261. } else if (!strcmp(*argv, "netmask")) {
  262. uint32_t netmask;
  263. char *ptr;
  264. uint32_t naddr[4] = {0,};
  265. uint64_t plen;
  266. netmask = (f == AF_INET6) ? 128 : 32; // set default netmask
  267. plen = strtoul(argv[1], &ptr, 0);
  268. if (!ptr || ptr == argv[1] || *ptr || !plen || plen > netmask) {
  269. if (!inet_pton(f, argv[1], &naddr)) error_exit("invalid netmask");
  270. if (f == AF_INET) {
  271. uint32_t mask = htonl(*naddr), host = ~mask;
  272. if (host & (host + 1)) error_exit("invalid netmask");
  273. for (plen = 0; mask; mask <<= 1) ++plen;
  274. if (plen > 32) error_exit("invalid netmask");
  275. }
  276. }
  277. netmask = plen;
  278. rtMsg->rtm_dst_len = netmask;
  279. } else if (!strcmp(*argv, "gw")) {
  280. if (!inet_pton(f, argv[1], &addr)) error_exit("invalid gw");
  281. addAttr(nlMsg, sizeof(toybuf), &addr, RTA_GATEWAY, addr_len);
  282. } else if (!strcmp(*argv, "mss")) {
  283. // TODO(emolitor): Add RTA_METRICS support
  284. //set the TCP Maximum Segment Size for connections over this route.
  285. //rt->rt_mtu = atolx_range(argv[1], 64, 65536);
  286. //rt->rt_flags |= RTF_MSS;
  287. } else if (!strcmp(*argv, "window")) {
  288. // TODO(emolitor): Add RTA_METRICS support
  289. //set the TCP window size for connections over this route to W bytes.
  290. //rt->rt_window = atolx_range(argv[1], 128, INT_MAX); //win low
  291. //rt->rt_flags |= RTF_WINDOW;
  292. } else if (!strcmp(*argv, "irtt")) {
  293. // TODO(emolitor): Add RTA_METRICS support
  294. //rt->rt_irtt = atolx_range(argv[1], 0, INT_MAX);
  295. //rt->rt_flags |= RTF_IRTT;
  296. } else if (!strcmp(*argv, "dev")) {
  297. unsigned int if_idx = if_nametoindex(argv[1]);
  298. if (!if_idx) perror_exit("dev");
  299. addAttr(nlMsg, sizeof(toybuf), &if_idx, RTA_OIF, sizeof(unsigned int));
  300. } else help_exit("no '%s'", *argv);
  301. argv++;
  302. }
  303. }
  304. if (strcmp(tgtip, "default") != 0) {
  305. char *prefix = strtok(0, "/");
  306. if (prefix) rtMsg->rtm_dst_len = strtoul(prefix, &prefix, 0);
  307. if (!inet_pton(f, strtok(tgtip, "/"), &addr)) error_exit("invalid target");
  308. addAttr(nlMsg, sizeof(toybuf), &addr, RTA_DST, addr_len);
  309. } else rtMsg->rtm_dst_len = 0;
  310. xsend(sockfd, nlMsg, nlMsg->nlmsg_len);
  311. xclose(sockfd);
  312. }
  313. void route_main(void)
  314. {
  315. if (!*toys.optargs) {
  316. if (!TT.A || !strcmp(TT.A, "inet")) display_routes(AF_INET);
  317. else if (!strcmp(TT.A, "inet6")) display_routes(AF_INET6);
  318. else show_help(stdout, 1);
  319. } else {
  320. if (!TT.A) {
  321. if (toys.optc>1 && strchr(toys.optargs[1], ':')) {
  322. xprintf("WARNING: Implicit IPV6 address using -Ainet6\n");
  323. TT.A = "inet6";
  324. } else TT.A = "inet";
  325. }
  326. if (!strcmp(TT.A, "inet")) setroute(AF_INET, toys.optargs);
  327. else setroute(AF_INET6, toys.optargs);
  328. }
  329. }