route.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. /*
  2. * Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License v2 as published by
  6. * the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  16. */
  17. #include <sys/socket.h>
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <errno.h>
  21. #include <unistd.h>
  22. #include <fcntl.h>
  23. #include <time.h>
  24. #include <linux/fib_rules.h>
  25. #include "relayd.h"
  26. #define NLMSG_ALIGNTO 4U
  27. #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
  28. static struct uloop_fd rtnl_sock;
  29. static unsigned int rtnl_seq, rtnl_dump_seq;
  30. int route_table = 16800;
  31. static void rtnl_flush(void)
  32. {
  33. int fd;
  34. fd = open("/proc/sys/net/ipv4/route/flush", O_WRONLY);
  35. if (fd < 0)
  36. return;
  37. write(fd, "-1", 2);
  38. close(fd);
  39. }
  40. enum {
  41. RULE_F_ADD = (1 << 0),
  42. RULE_F_DEFGW_WORKAROUND = (1 << 1),
  43. };
  44. static int get_route_table(struct relayd_interface *rif)
  45. {
  46. if (rif)
  47. return rif->rt_table;
  48. else
  49. return local_route_table;
  50. }
  51. static void
  52. rtnl_rule_request(struct relayd_interface *rif, int flags)
  53. {
  54. struct {
  55. struct nlmsghdr nl;
  56. struct rtmsg rt;
  57. struct {
  58. struct rtattr rta;
  59. int table;
  60. } __packed table;
  61. struct {
  62. struct rtattr rta;
  63. int prio;
  64. } __packed prio;
  65. struct {
  66. struct rtattr rta;
  67. char ifname[IFNAMSIZ + 1];
  68. } __packed dev;
  69. } __packed req = {
  70. .rt = {
  71. .rtm_family = AF_INET,
  72. .rtm_table = RT_TABLE_UNSPEC,
  73. .rtm_scope = RT_SCOPE_UNIVERSE,
  74. .rtm_protocol = RTPROT_BOOT,
  75. },
  76. .prio = {
  77. .rta.rta_type = FRA_PRIORITY,
  78. .rta.rta_len = sizeof(req.prio),
  79. .prio = 2,
  80. },
  81. .table.rta = {
  82. .rta_type = FRA_TABLE,
  83. .rta_len = sizeof(req.table),
  84. },
  85. };
  86. const char *ifname = "lo";
  87. int padding = sizeof(req.dev.ifname);
  88. if (rif)
  89. ifname = rif->ifname;
  90. if (!(flags & RULE_F_DEFGW_WORKAROUND)) {
  91. int len = strlen(ifname) + 1;
  92. req.dev.rta.rta_type = FRA_IFNAME;
  93. padding -= NLMSG_ALIGN(len);
  94. strcpy(req.dev.ifname, ifname);
  95. req.dev.rta.rta_len = sizeof(req.dev.rta) + len;
  96. } else {
  97. padding = sizeof(req.dev);
  98. req.prio.prio--;
  99. }
  100. req.table.table = get_route_table(rif);
  101. req.nl.nlmsg_len = sizeof(req) - padding;
  102. req.nl.nlmsg_flags = NLM_F_REQUEST;
  103. if (flags & RULE_F_ADD) {
  104. req.nl.nlmsg_type = RTM_NEWRULE;
  105. req.nl.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
  106. req.rt.rtm_type = RTN_UNICAST;
  107. } else {
  108. req.nl.nlmsg_type = RTM_DELRULE;
  109. req.rt.rtm_type = RTN_UNSPEC;
  110. }
  111. send(rtnl_sock.fd, &req, req.nl.nlmsg_len, 0);
  112. rtnl_flush();
  113. }
  114. struct rtnl_addr {
  115. struct rtattr rta;
  116. uint8_t ipaddr[4];
  117. } __packed;
  118. static struct rtnl_addr *
  119. rtnl_add_addr(struct rtnl_addr *addr, int *len, int type, const uint8_t *ipaddr)
  120. {
  121. addr->rta.rta_type = type;
  122. memcpy(addr->ipaddr, ipaddr, 4);
  123. *len += sizeof(*addr);
  124. return addr + 1;
  125. }
  126. static void
  127. rtnl_route_request(struct relayd_interface *rif, struct relayd_host *host,
  128. struct relayd_route *route, bool add)
  129. {
  130. static struct {
  131. struct nlmsghdr nl;
  132. struct rtmsg rt;
  133. struct {
  134. struct rtattr rta;
  135. int table;
  136. } __packed table;
  137. struct {
  138. struct rtattr rta;
  139. int ifindex;
  140. } __packed dev;
  141. struct rtnl_addr addr[3];
  142. } __packed req = {
  143. .rt = {
  144. .rtm_family = AF_INET,
  145. .rtm_dst_len = 32,
  146. .rtm_table = RT_TABLE_MAIN,
  147. },
  148. .table.rta = {
  149. .rta_type = RTA_TABLE,
  150. .rta_len = sizeof(req.table),
  151. },
  152. .dev.rta = {
  153. .rta_type = RTA_OIF,
  154. .rta_len = sizeof(req.dev),
  155. },
  156. .addr[0].rta.rta_len = sizeof(struct rtnl_addr),
  157. .addr[1].rta.rta_len = sizeof(struct rtnl_addr),
  158. .addr[2].rta.rta_len = sizeof(struct rtnl_addr),
  159. };
  160. int pktlen = sizeof(req) - sizeof(req.addr);
  161. struct rtnl_addr *addr = &req.addr[0];
  162. const char *ifname = "loopback";
  163. req.dev.ifindex = host->rif->sll.sll_ifindex;
  164. req.table.table = get_route_table(rif);
  165. req.nl.nlmsg_flags = NLM_F_REQUEST;
  166. if (add) {
  167. req.nl.nlmsg_type = RTM_NEWROUTE;
  168. req.nl.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
  169. req.rt.rtm_protocol = RTPROT_BOOT;
  170. if (route) {
  171. req.rt.rtm_scope = RT_SCOPE_UNIVERSE;
  172. } else {
  173. req.rt.rtm_scope = RT_SCOPE_LINK;
  174. }
  175. req.rt.rtm_type = RTN_UNICAST;
  176. } else {
  177. req.nl.nlmsg_type = RTM_DELROUTE;
  178. req.rt.rtm_scope = RT_SCOPE_NOWHERE;
  179. }
  180. if (rif)
  181. ifname = rif->ifname;
  182. if (route) {
  183. DPRINTF(2, "%s: add route to "IP_FMT"/%d via "IP_FMT" (%s)\n", ifname,
  184. IP_BUF(route->dest), route->mask, IP_BUF(host->ipaddr),
  185. host->rif->ifname);
  186. req.rt.rtm_dst_len = route->mask;
  187. if (route->mask)
  188. addr = rtnl_add_addr(addr, &pktlen, RTA_DST, route->dest);
  189. addr = rtnl_add_addr(addr, &pktlen, RTA_GATEWAY, host->ipaddr);
  190. } else {
  191. DPRINTF(2, "%s: add host route to "IP_FMT" (%s)\n", ifname,
  192. IP_BUF(host->ipaddr), host->rif->ifname);
  193. addr = rtnl_add_addr(addr, &pktlen, RTA_DST, host->ipaddr);
  194. req.rt.rtm_dst_len = 32;
  195. }
  196. /* local route */
  197. if (!rif)
  198. addr = rtnl_add_addr(addr, &pktlen, RTA_PREFSRC, local_addr);
  199. req.nl.nlmsg_len = pktlen;
  200. if (route)
  201. rtnl_rule_request(rif, RULE_F_DEFGW_WORKAROUND | RULE_F_ADD);
  202. send(rtnl_sock.fd, &req, pktlen, 0);
  203. if (route)
  204. rtnl_rule_request(rif, RULE_F_DEFGW_WORKAROUND);
  205. rtnl_flush();
  206. }
  207. void
  208. rtnl_route_set(struct relayd_host *host, struct relayd_route *route, bool add)
  209. {
  210. struct relayd_interface *rif;
  211. list_for_each_entry(rif, &interfaces, list) {
  212. if (rif == host->rif)
  213. continue;
  214. rtnl_route_request(rif, host, route, add);
  215. }
  216. if (local_route_table)
  217. rtnl_route_request(NULL, host, route, add);
  218. }
  219. void relayd_add_interface_routes(struct relayd_interface *rif)
  220. {
  221. rif->rt_table = route_table++;
  222. rtnl_rule_request(rif, RULE_F_ADD);
  223. }
  224. void relayd_del_interface_routes(struct relayd_interface *rif)
  225. {
  226. rtnl_rule_request(rif, 0);
  227. }
  228. #ifndef NDA_RTA
  229. #define NDA_RTA(r) \
  230. ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
  231. #endif
  232. static void rtnl_parse_newneigh(struct nlmsghdr *h)
  233. {
  234. struct relayd_interface *rif = NULL;
  235. struct ndmsg *r = NLMSG_DATA(h);
  236. const uint8_t *lladdr = NULL;
  237. const uint8_t *ipaddr = NULL;
  238. struct rtattr *rta;
  239. int len;
  240. if (r->ndm_family != AF_INET)
  241. return;
  242. list_for_each_entry(rif, &interfaces, list) {
  243. if (rif->sll.sll_ifindex == r->ndm_ifindex)
  244. goto found_interface;
  245. }
  246. return;
  247. found_interface:
  248. len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
  249. for (rta = NDA_RTA(r); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
  250. switch(rta->rta_type) {
  251. case NDA_LLADDR:
  252. lladdr = RTA_DATA(rta);
  253. break;
  254. case NDA_DST:
  255. ipaddr = RTA_DATA(rta);
  256. break;
  257. default:
  258. break;
  259. }
  260. }
  261. if (!lladdr || !ipaddr || (r->ndm_state & (NUD_INCOMPLETE|NUD_FAILED)))
  262. return;
  263. if (!memcmp(lladdr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN))
  264. return;
  265. DPRINTF(1, "%s: Found ARP cache entry for host "IP_FMT" ("MAC_FMT")\n",
  266. rif->ifname, IP_BUF(ipaddr), MAC_BUF(lladdr));
  267. relayd_refresh_host(rif, lladdr, ipaddr);
  268. }
  269. static void rtnl_parse_packet(void *data, int len)
  270. {
  271. struct nlmsghdr *h;
  272. for (h = data; NLMSG_OK(h, len); h = NLMSG_NEXT(h, len)) {
  273. if (h->nlmsg_type == NLMSG_DONE ||
  274. h->nlmsg_type == NLMSG_ERROR)
  275. return;
  276. if (h->nlmsg_seq != rtnl_dump_seq)
  277. continue;
  278. if (h->nlmsg_type == RTM_NEWNEIGH)
  279. rtnl_parse_newneigh(h);
  280. }
  281. }
  282. static void rtnl_cb(struct uloop_fd *fd, unsigned int events)
  283. {
  284. struct sockaddr_nl nladdr;
  285. static uint8_t buf[16384];
  286. struct iovec iov = {
  287. .iov_base = buf,
  288. .iov_len = sizeof(buf),
  289. };
  290. struct msghdr msg = {
  291. .msg_name = &nladdr,
  292. .msg_namelen = sizeof(nladdr),
  293. .msg_iov = &iov,
  294. .msg_iovlen = 1,
  295. };
  296. do {
  297. int len;
  298. len = recvmsg(rtnl_sock.fd, &msg, 0);
  299. if (len < 0) {
  300. if (errno == EINTR)
  301. continue;
  302. return;
  303. }
  304. if (!len)
  305. break;
  306. if (nladdr.nl_pid != 0)
  307. continue;
  308. rtnl_parse_packet(buf, len);
  309. } while (1);
  310. }
  311. static void rtnl_dump_request(int nlmsg_type)
  312. {
  313. static struct {
  314. struct nlmsghdr nlh;
  315. struct rtgenmsg g;
  316. } req = {
  317. .nlh = {
  318. .nlmsg_len = sizeof(req),
  319. .nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST,
  320. .nlmsg_pid = 0,
  321. },
  322. .g.rtgen_family = AF_INET,
  323. };
  324. req.nlh.nlmsg_type = nlmsg_type;
  325. req.nlh.nlmsg_seq = rtnl_seq;
  326. send(rtnl_sock.fd, &req, sizeof(req), 0);
  327. rtnl_seq++;
  328. }
  329. int relayd_rtnl_init(void)
  330. {
  331. struct sockaddr_nl snl_local = {};
  332. rtnl_sock.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  333. if (rtnl_sock.fd < 0) {
  334. perror("socket(AF_NETLINK)");
  335. return -1;
  336. }
  337. snl_local.nl_family = AF_NETLINK;
  338. if (bind(rtnl_sock.fd, (struct sockaddr *) &snl_local, sizeof(struct sockaddr_nl)) < 0) {
  339. perror("bind");
  340. close(rtnl_sock.fd);
  341. return -1;
  342. }
  343. rtnl_sock.cb = rtnl_cb;
  344. uloop_fd_add(&rtnl_sock, ULOOP_READ | ULOOP_EDGE_TRIGGER);
  345. rtnl_seq = time(NULL);
  346. rtnl_dump_seq = rtnl_seq;
  347. rtnl_dump_request(RTM_GETNEIGH);
  348. rtnl_rule_request(NULL, RULE_F_ADD);
  349. return 0;
  350. }
  351. void relayd_rtnl_done(void)
  352. {
  353. rtnl_rule_request(NULL, 0);
  354. uloop_fd_delete(&rtnl_sock);
  355. close(rtnl_sock.fd);
  356. }