gluon-radv-filterd.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. /*
  2. Copyright (c) 2016 Jan-Philipp Litza <janphilipp@litza.de>
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are met:
  6. 1. Redistributions of source code must retain the above copyright notice,
  7. this list of conditions and the following disclaimer.
  8. 2. Redistributions in binary form must reproduce the above copyright notice,
  9. this list of conditions and the following disclaimer in the documentation
  10. and/or other materials provided with the distribution.
  11. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  12. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  13. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  14. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  15. FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  16. DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  17. SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  18. CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  19. OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  20. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  21. */
  22. #define _GNU_SOURCE
  23. #include <error.h>
  24. #include <errno.h>
  25. #include <signal.h>
  26. #include <stdarg.h>
  27. #include <stdbool.h>
  28. #include <stdio.h>
  29. #include <stdint.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <time.h>
  33. #include <unistd.h>
  34. #include <sys/socket.h>
  35. #include <sys/select.h>
  36. #include <sys/types.h>
  37. #include <sys/wait.h>
  38. #include <net/if.h>
  39. #include <linux/filter.h>
  40. #include <linux/if_packet.h>
  41. #include <linux/if_ether.h>
  42. #include <linux/limits.h>
  43. #include <netinet/icmp6.h>
  44. #include <netinet/ip6.h>
  45. // Recheck TQs after this time even if no RA was received
  46. #define MAX_INTERVAL 60
  47. // Recheck TQs at most this often, even if new RAs were received (they won't
  48. // become the preferred routers until the TQs have been rechecked)
  49. // Also, the first update will take at least this long
  50. #define MIN_INTERVAL 5
  51. // max execution time of a single ebtables call in nanoseconds
  52. #define EBTABLES_TIMEOUT 1e8 // 100ms
  53. // TQ value assigned to local routers
  54. #define LOCAL_TQ 512
  55. #define BUFSIZE 1500
  56. #define DEBUGFS "/sys/kernel/debug/batman_adv/%s/"
  57. #define ORIGINATORS DEBUGFS "originators"
  58. #define TRANSTABLE_GLOBAL DEBUGFS "transtable_global"
  59. #define TRANSTABLE_LOCAL DEBUGFS "transtable_local"
  60. #define F_MAC "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
  61. #define F_MAC_IGN "%*2x:%*2x:%*2x:%*2x:%*2x:%*2x"
  62. #define F_MAC_VAR(var) var[0], var[1], var[2], var[3], var[4], var[5]
  63. #ifdef DEBUG
  64. #define CHECK(stmt) \
  65. if(!(stmt)) { \
  66. fprintf(stderr, "check failed: " #stmt "\n"); \
  67. goto check_failed; \
  68. }
  69. #define DEBUG_MSG(msg, ...) fprintf(stderr, msg "\n", ##__VA_ARGS__)
  70. #else
  71. #define CHECK(stmt) if(!(stmt)) goto check_failed;
  72. #define DEBUG_MSG(msg, ...) do {} while(0)
  73. #endif
  74. #ifndef ARRAY_SIZE
  75. #define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
  76. #endif
  77. typedef uint8_t macaddr_t[ETH_ALEN];
  78. struct list_item {
  79. struct list *next;
  80. };
  81. #define foreach(item, list) \
  82. for(item = list; item != NULL; item = item->next)
  83. struct router {
  84. struct router *next;
  85. macaddr_t src;
  86. time_t eol;
  87. macaddr_t originator;
  88. uint16_t tq;
  89. };
  90. struct global {
  91. int sock;
  92. struct router *routers;
  93. const char *mesh_iface;
  94. const char *chain;
  95. uint16_t max_tq;
  96. uint16_t hysteresis_thresh;
  97. struct router *best_router;
  98. } G = {
  99. .mesh_iface = "bat0",
  100. };
  101. static void cleanup() {
  102. struct router *router;
  103. close(G.sock);
  104. while (G.routers != NULL) {
  105. router = G.routers;
  106. G.routers = router->next;
  107. free(router);
  108. }
  109. }
  110. static void usage(const char *msg) {
  111. if (msg != NULL && *msg != '\0') {
  112. fprintf(stderr, "ERROR: %s\n\n", msg);
  113. }
  114. fprintf(stderr,
  115. "Usage: %s [-m <mesh_iface>] [-t <thresh>] -c <chain> -i <iface>\n\n"
  116. " -m <mesh_iface> B.A.T.M.A.N. advanced mesh interface used to get metric\n"
  117. " information (\"TQ\") for the available gateways. Default: bat0\n"
  118. " -t <thresh> Minimum TQ difference required to switch the gateway.\n"
  119. " Default: 0\n"
  120. " -c <chain> ebtables chain that should be managed by the daemon. The\n"
  121. " chain already has to exist on program invocation and should\n"
  122. " have a DROP policy. It will be flushed by the program!\n"
  123. " -i <iface> Interface to listen on for router advertisements. Should be\n"
  124. " <mesh_iface> or a bridge on top of it, as no metric\n"
  125. " information will be available for hosts on other interfaces.\n\n",
  126. program_invocation_short_name);
  127. cleanup();
  128. if (msg == NULL)
  129. exit(EXIT_SUCCESS);
  130. else
  131. exit(EXIT_FAILURE);
  132. }
  133. #define exit_errmsg(message, ...) { \
  134. fprintf(stderr, message "\n", ##__VA_ARGS__); \
  135. cleanup(); \
  136. exit(1); \
  137. }
  138. static inline void exit_errno(const char *message) {
  139. cleanup();
  140. error(1, errno, "error: %s", message);
  141. }
  142. static inline void warn_errno(const char *message) {
  143. error(0, errno, "warning: %s", message);
  144. }
  145. static int init_packet_socket(unsigned int ifindex) {
  146. // generated by tcpdump -i tun "icmp6 and ip6[40] = 134" -dd
  147. // Important: Generate on TUN interface (because the socket is SOCK_DGRAM)!
  148. struct sock_filter radv_filter_code[] = {
  149. { 0x30, 0, 0, 0x00000000 },
  150. { 0x54, 0, 0, 0x000000f0 },
  151. { 0x15, 0, 8, 0x00000060 },
  152. { 0x30, 0, 0, 0x00000006 },
  153. { 0x15, 3, 0, 0x0000003a },
  154. { 0x15, 0, 5, 0x0000002c },
  155. { 0x30, 0, 0, 0x00000028 },
  156. { 0x15, 0, 3, 0x0000003a },
  157. { 0x30, 0, 0, 0x00000028 },
  158. { 0x15, 0, 1, 0x00000086 },
  159. { 0x06, 0, 0, 0x0000ffff },
  160. { 0x06, 0, 0, 0x00000000 },
  161. };
  162. struct sock_fprog radv_filter = {
  163. .len = ARRAY_SIZE(radv_filter_code),
  164. .filter = radv_filter_code,
  165. };
  166. int sock = socket(AF_PACKET, SOCK_DGRAM|SOCK_CLOEXEC, ETH_P_IPV6);
  167. if (sock < 0)
  168. exit_errno("can't open packet socket");
  169. setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &radv_filter, sizeof(radv_filter));
  170. struct sockaddr_ll bind_iface = {
  171. .sll_family = AF_PACKET,
  172. .sll_protocol = ETH_P_IPV6,
  173. .sll_ifindex = ifindex,
  174. };
  175. bind(sock, (struct sockaddr *)&bind_iface, sizeof(bind_iface));
  176. return sock;
  177. }
  178. static void parse_cmdline(int argc, char *argv[]) {
  179. int c;
  180. unsigned int ifindex;
  181. unsigned long int threshold;
  182. char *endptr;
  183. while ((c = getopt(argc, argv, "c:hi:m:t:")) != -1) {
  184. switch (c) {
  185. case 'i':
  186. if (G.sock != 0)
  187. usage("-i given more than once");
  188. ifindex = if_nametoindex(optarg);
  189. if (ifindex == 0)
  190. exit_errmsg("Unknown interface: %s", optarg);
  191. G.sock = init_packet_socket(ifindex);
  192. break;
  193. case 'm':
  194. G.mesh_iface = optarg;
  195. break;
  196. case 'c':
  197. G.chain = optarg;
  198. break;
  199. case 't':
  200. threshold = strtoul(optarg, &endptr, 10);
  201. if (*endptr != '\0')
  202. exit_errmsg("Threshold must be a number: %s", optarg);
  203. if (threshold >= LOCAL_TQ)
  204. exit_errmsg("Threshold too large: %ld (max is %d)", threshold, LOCAL_TQ);
  205. G.hysteresis_thresh = (uint16_t) threshold;
  206. break;
  207. case 'h':
  208. usage(NULL);
  209. break;
  210. default:
  211. usage("");
  212. break;
  213. }
  214. }
  215. }
  216. static void handle_ra(int sock) {
  217. struct sockaddr_ll src;
  218. unsigned int addr_size = sizeof(src);
  219. size_t len;
  220. uint8_t buffer[BUFSIZE] __attribute__((aligned(8)));
  221. struct ip6_hdr *pkt;
  222. struct ip6_ext *ext;
  223. struct nd_router_advert *ra;
  224. uint8_t ext_type;
  225. len = recvfrom(sock, buffer, BUFSIZE, 0, (struct sockaddr *)&src, &addr_size);
  226. // skip IPv6 headers, ensuring packet is long enough
  227. CHECK(len > sizeof(struct ip6_hdr));
  228. pkt = (struct ip6_hdr *)buffer;
  229. CHECK(len >= ntohs(pkt->ip6_plen) + sizeof(struct ip6_hdr));
  230. ext_type = pkt->ip6_nxt;
  231. ext = (void*)pkt + sizeof(struct ip6_hdr);
  232. while (ext_type != IPPROTO_ICMPV6) {
  233. CHECK((void*)ext < (void*)pkt + sizeof(struct ip6_hdr) + len);
  234. CHECK(ext->ip6e_len > 0);
  235. ext_type = ext->ip6e_nxt;
  236. ext = (void*)ext + ext->ip6e_len;
  237. }
  238. // partially parse router advertisement
  239. CHECK((void*)ext + sizeof(struct nd_router_advert) <= (void*)pkt + sizeof(struct ip6_hdr) + len);
  240. ra = (struct nd_router_advert *) ext;
  241. CHECK(ra->nd_ra_type == ND_ROUTER_ADVERT);
  242. CHECK(ra->nd_ra_code == 0);
  243. // we only want default routers
  244. CHECK(ra->nd_ra_router_lifetime > 0);
  245. DEBUG_MSG("received valid RA from " F_MAC, F_MAC_VAR(src.sll_addr));
  246. // update list of known routers
  247. struct router *router;
  248. foreach(router, G.routers) {
  249. if (!memcmp(router->src, src.sll_addr, sizeof(macaddr_t))) {
  250. break;
  251. }
  252. }
  253. if (!router) {
  254. router = malloc(sizeof(struct router));
  255. memcpy(router->src, src.sll_addr, 8);
  256. router->next = G.routers;
  257. G.routers = router;
  258. }
  259. router->eol = time(NULL) + ra->nd_ra_router_lifetime;
  260. check_failed:
  261. return;
  262. }
  263. static void expire_routers() {
  264. struct router **prev_ptr = &G.routers;
  265. struct router *router;
  266. time_t now = time(NULL);
  267. foreach(router, G.routers) {
  268. if (router->eol < now) {
  269. DEBUG_MSG("router " F_MAC " expired", F_MAC_VAR(router->src));
  270. *prev_ptr = router->next;
  271. if (G.best_router == router)
  272. G.best_router = NULL;
  273. free(router);
  274. } else {
  275. prev_ptr = &router->next;
  276. }
  277. }
  278. }
  279. static void update_tqs() {
  280. FILE *f;
  281. struct router *router;
  282. char path[PATH_MAX];
  283. char *line = NULL;
  284. size_t len = 0;
  285. uint8_t tq;
  286. bool update_originators = false;
  287. int i;
  288. macaddr_t mac_a, mac_b;
  289. // reset TQs
  290. foreach(router, G.routers) {
  291. router->tq = 0;
  292. for (i = 0; i < 6; i++)
  293. if (router->originator[i] != 0)
  294. break;
  295. if (i >= 6)
  296. update_originators = true;
  297. }
  298. // TODO: Currently, we iterate over the whole list of routers all the
  299. // time. Maybe it would be a good idea to sort routers that already
  300. // have the current piece of information to the back. That way, we
  301. // could abort as soon as we hit the first router with the current
  302. // information filled in.
  303. if (update_originators) {
  304. // translate all router's MAC addresses to originators simultaneously
  305. snprintf(path, PATH_MAX, TRANSTABLE_GLOBAL, G.mesh_iface);
  306. f = fopen(path, "r");
  307. // skip header
  308. while (fgetc(f) != '\n');
  309. while (fgetc(f) != '\n');
  310. while (fscanf(f, " %*[*+] " F_MAC "%*[0-9 -] (%*3u) via " F_MAC " %*[^]]]\n",
  311. F_MAC_VAR(&mac_a), F_MAC_VAR(&mac_b)) == 12) {
  312. foreach(router, G.routers) {
  313. if (!memcmp(router->src, mac_a, sizeof(macaddr_t))) {
  314. DEBUG_MSG("Found originator for " F_MAC ", it's " F_MAC, F_MAC_VAR(router->src), F_MAC_VAR(mac_b));
  315. memcpy(router->originator, mac_b, sizeof(macaddr_t));
  316. break; // foreach
  317. }
  318. }
  319. }
  320. if (!feof(f)) {
  321. getline(&line, &len, f);
  322. fprintf(stderr, "Parsing transtable_global aborted at this line: %s\n", line);
  323. }
  324. fclose(f);
  325. }
  326. // look up TQs of originators
  327. G.max_tq = 0;
  328. snprintf(path, PATH_MAX, ORIGINATORS, G.mesh_iface);
  329. f = fopen(path, "r");
  330. // skip header
  331. while (fgetc(f) != '\n');
  332. while (fgetc(f) != '\n');
  333. while (fscanf(f, F_MAC " %*fs (%hhu) " F_MAC_IGN " [ %*[^]]]: " F_MAC_IGN " (%*3u)\n",
  334. F_MAC_VAR(&mac_a), &tq) == 7) {
  335. foreach(router, G.routers) {
  336. if (!memcmp(router->originator, mac_a, sizeof(macaddr_t))) {
  337. DEBUG_MSG("Found TQ for router " F_MAC " (originator " F_MAC "), it's %d", F_MAC_VAR(router->src), F_MAC_VAR(router->originator), tq);
  338. router->tq = tq;
  339. if (tq > G.max_tq)
  340. G.max_tq = tq;
  341. break; // foreach
  342. }
  343. }
  344. }
  345. if (!feof(f)) {
  346. getline(&line, &len, f);
  347. fprintf(stderr, "Parsing originators aborted at this line: %s\n", line);
  348. }
  349. fclose(f);
  350. // if all routers have a TQ value, we don't need to check translocal
  351. foreach(router, G.routers) {
  352. if (router->tq == 0)
  353. break;
  354. }
  355. if (router != NULL) {
  356. // rate local routers (if present) the highest
  357. snprintf(path, PATH_MAX, TRANSTABLE_LOCAL, G.mesh_iface);
  358. f = fopen(path, "r");
  359. // skip header
  360. while (fgetc(f) != '\n');
  361. while (fgetc(f) != '\n');
  362. while (fscanf(f, " * " F_MAC " [%*5s] %*f", F_MAC_VAR(&mac_a)) == 6) {
  363. foreach(router, G.routers) {
  364. if (!memcmp(router->src, mac_a, sizeof(macaddr_t))) {
  365. DEBUG_MSG("Found router " F_MAC " in transtable_local, assigning TQ %d", F_MAC_VAR(router->src), LOCAL_TQ);
  366. router->tq = LOCAL_TQ;
  367. G.max_tq = LOCAL_TQ;
  368. break; // foreach
  369. }
  370. }
  371. }
  372. if (!feof(f)) {
  373. getline(&line, &len, f);
  374. fprintf(stderr, "Parsing transtable_local aborted at this line: %s\n", line);
  375. }
  376. fclose(f);
  377. }
  378. foreach(router, G.routers) {
  379. if (router->tq == 0) {
  380. for (i = 0; i < 6; i++)
  381. if (router->originator[i] != 0)
  382. break;
  383. if (i >= 6)
  384. fprintf(stderr,
  385. "Unable to find router " F_MAC " in transtable_{global,local}\n",
  386. F_MAC_VAR(router->src));
  387. else
  388. fprintf(stderr,
  389. "Unable to find TQ for originator " F_MAC " (router " F_MAC ")\n",
  390. F_MAC_VAR(router->originator),
  391. F_MAC_VAR(router->src));
  392. }
  393. }
  394. free(line);
  395. }
  396. static int fork_execvp_timeout(struct timespec *timeout, const char *file, const char *const argv[]) {
  397. int ret;
  398. pid_t child;
  399. siginfo_t info;
  400. sigset_t signals;
  401. sigemptyset(&signals);
  402. sigaddset(&signals, SIGCHLD);
  403. child = fork();
  404. if (!child) {
  405. // casting discards const, but should be safe
  406. // (see http://stackoverflow.com/q/36925388)
  407. execvp(file, (char**) argv);
  408. error(1, errno, "can't execvp(\"%s\", ...)", file);
  409. }
  410. sigprocmask(SIG_BLOCK, &signals, NULL);
  411. ret = sigtimedwait(&signals, &info, timeout);
  412. sigprocmask(SIG_UNBLOCK, &signals, NULL);
  413. if (ret == SIGCHLD) {
  414. if (info.si_pid != child) {
  415. cleanup();
  416. error_at_line(1, 0, __FILE__, __LINE__,
  417. "BUG: We received a SIGCHLD from a child we didn't spawn (expected PID %d, got %d)",
  418. child, info.si_pid);
  419. }
  420. waitpid(child, NULL, 0);
  421. return info.si_status;
  422. }
  423. if (ret < 0 && errno == EAGAIN)
  424. error(0, 0, "warning: child %d took too long, killing", child);
  425. else if (ret < 0)
  426. warn_errno("sigtimedwait failed, killing child");
  427. else
  428. error_at_line(1, 0, __FILE__, __LINE__,
  429. "BUG: sigtimedwait() return some other signal than SIGCHLD: %d",
  430. ret);
  431. kill(child, SIGKILL);
  432. kill(child, SIGCONT);
  433. waitpid(child, NULL, 0);
  434. return -1;
  435. }
  436. static void update_ebtables() {
  437. struct timespec timeout = {
  438. .tv_nsec = EBTABLES_TIMEOUT,
  439. };
  440. char mac[18];
  441. struct router *router;
  442. if (G.best_router && G.best_router->tq >= G.max_tq - G.hysteresis_thresh) {
  443. DEBUG_MSG(F_MAC " is still good enough with TQ=%d (max_tq=%d), not executing ebtables",
  444. F_MAC_VAR(G.best_router->src),
  445. G.best_router->tq,
  446. G.max_tq);
  447. return;
  448. }
  449. foreach(router, G.routers) {
  450. if (router->tq == G.max_tq) {
  451. snprintf(mac, sizeof(mac), F_MAC, F_MAC_VAR(router->src));
  452. break;
  453. }
  454. }
  455. DEBUG_MSG("Determined %s as new best router with TQ=%d", mac, G.max_tq);
  456. G.best_router = router;
  457. if (fork_execvp_timeout(&timeout, "ebtables", (const char *[])
  458. { "ebtables", "-F", G.chain, NULL }))
  459. error(0, 0, "warning: flushing ebtables chain %s failed, not adding a new rule", G.chain);
  460. else if (fork_execvp_timeout(&timeout, "ebtables", (const char *[])
  461. { "ebtables", "-A", G.chain, "-s", mac, "-j", "ACCEPT", NULL }))
  462. error(0, 0, "warning: adding new rule to ebtables chain %s failed", G.chain);
  463. }
  464. int main(int argc, char *argv[]) {
  465. int retval;
  466. fd_set rfds;
  467. struct timeval tv;
  468. time_t last_update = time(NULL);
  469. parse_cmdline(argc, argv);
  470. if (G.sock == 0)
  471. usage("No interface set!");
  472. if (G.chain == NULL)
  473. usage("No chain set!");
  474. while (1) {
  475. FD_ZERO(&rfds);
  476. FD_SET(G.sock, &rfds);
  477. tv.tv_sec = MAX_INTERVAL;
  478. tv.tv_usec = 0;
  479. retval = select(G.sock + 1, &rfds, NULL, NULL, &tv);
  480. if (retval < 0)
  481. exit_errno("select() failed");
  482. else if (retval) {
  483. if (FD_ISSET(G.sock, &rfds)) {
  484. handle_ra(G.sock);
  485. }
  486. }
  487. else
  488. DEBUG_MSG("select() timeout expired");
  489. if (G.routers != NULL && last_update <= time(NULL) - MIN_INTERVAL) {
  490. expire_routers();
  491. // all routers could have expired, check again
  492. if (G.routers != NULL) {
  493. update_tqs();
  494. update_ebtables();
  495. last_update = time(NULL);
  496. }
  497. }
  498. }
  499. cleanup();
  500. return 0;
  501. }