gluon-radv-filterd.c 13 KB

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