diff --git a/package/gluon-radv-filterd/luasrc/lib/gluon/ebtables/400-radv-filterd b/package/gluon-radv-filterd/luasrc/lib/gluon/ebtables/400-radv-filterd index 178084d41f83fed9fa8167c004b24287fdd712ea..0761038d0c7a0009475d3eb022cbd142333d40ba 100644 --- a/package/gluon-radv-filterd/luasrc/lib/gluon/ebtables/400-radv-filterd +++ b/package/gluon-radv-filterd/luasrc/lib/gluon/ebtables/400-radv-filterd @@ -1,3 +1,7 @@ chain('RADV_FILTER', 'DROP') rule 'FORWARD -p IPv6 -i bat0 --ip6-protocol ipv6-icmp --ip6-icmp-type router-advertisement -j RADV_FILTER' rule 'RADV_FILTER -j ACCEPT' + +chain('REDIRECT', 'RETURN', 'nat') +rule('PREROUTING -p IPv6 --logical-in br-client --ip6-destination 2000::/3 -j REDIRECT', 'nat') +rule('OUTPUT -p IPv6 --logical-out --ip6-destination 2000::/3 -j REDIRECT', 'nat') diff --git a/package/gluon-radv-filterd/src/gluon-radv-filterd.c b/package/gluon-radv-filterd/src/gluon-radv-filterd.c index b1620cae8b4a07b0a199e699a990b60271201862..be9bfa56b20a911a9f5853e15f1ab973b1bce176 100644 --- a/package/gluon-radv-filterd/src/gluon-radv-filterd.c +++ b/package/gluon-radv-filterd/src/gluon-radv-filterd.c @@ -24,6 +24,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// #define DEBUG + #include <errno.h> #include <signal.h> #include <stdarg.h> @@ -52,6 +54,8 @@ #include <netinet/in.h> #include <netinet/ip6.h> +#include <arpa/inet.h> + #include <netlink/netlink.h> #include <netlink/genl/genl.h> #include <netlink/genl/ctrl.h> @@ -109,6 +113,9 @@ struct router { struct timespec eol; struct ether_addr originator; uint16_t tq; + bool redirected; + struct in6_addr lladdr; + struct in6_addr prefix; }; static struct global { @@ -178,6 +185,10 @@ static void cleanup(void) { if (fork_execvp_timeout(&timeout, "ebtables-tiny", (const char *[]) { "ebtables-tiny", "-A", G.chain, "-j", "ACCEPT", NULL })) DEBUG_MSG("warning: adding new rule to ebtables chain %s failed", G.chain); + + if (fork_execvp_timeout(&timeout, "ebtables-tiny", (const char *[]) + { "ebtables-tiny", "-t", "nat", "-F", "REDIRECT", NULL})) + DEBUG_MSG("warning: flushing ebtables nat chain REDIRECT failed", G.chain); } } @@ -254,7 +265,7 @@ static int init_packet_socket(unsigned int ifindex) { struct sockaddr_ll bind_iface = { .sll_family = AF_PACKET, - .sll_protocol = htons(ETH_P_IPV6), + .sll_protocol = htons(ETH_P_ALL), /* seems needed to recieve packets on bat0 */ .sll_ifindex = ifindex, }; ret = bind(sock, (struct sockaddr *)&bind_iface, sizeof(bind_iface)); @@ -342,40 +353,85 @@ static struct router *router_add(const struct ether_addr *mac) { return router; } -static void router_update(const struct ether_addr *mac, uint16_t timeout) { - struct router *router; - - router = router_find_src(mac); - if (!router) - router = router_add(mac); - if (!router) - return; - - clock_gettime(CLOCK_MONOTONIC, &router->eol); - router->eol.tv_sec += timeout; -} - static void handle_ra(int sock) { struct sockaddr_ll src; struct ether_addr mac; socklen_t addr_size = sizeof(src); ssize_t len; + uint8_t *ptr; struct { - struct ip6_hdr ip6; - struct nd_router_advert ra; + struct { + struct ip6_hdr ip6; + struct nd_router_advert ra; + } hdr; + uint8_t options[128]; } pkt; + struct router *router; + char addr_str[INET6_ADDRSTRLEN]; len = recvfrom(sock, &pkt, sizeof(pkt), 0, (struct sockaddr *)&src, &addr_size); CHECK(len >= 0); // BPF already checked that this is an ICMPv6 RA of a default router - CHECK((size_t)len >= sizeof(pkt)); - CHECK(ntohs(pkt.ip6.ip6_plen) + sizeof(struct ip6_hdr) >= sizeof(pkt)); + CHECK((size_t)len >= sizeof(pkt.hdr)); + CHECK(ntohs(pkt.hdr.ip6.ip6_plen) + sizeof(struct ip6_hdr) >= sizeof(pkt.hdr)); memcpy(&mac, src.sll_addr, sizeof(mac)); DEBUG_MSG("received valid RA from " F_MAC, F_MAC_VAR(mac)); - router_update(&mac, ntohs(pkt.ra.nd_ra_router_lifetime)); + router = router_find_src(&mac); + if (!router) + router = router_add(&mac); + if (!router) + return; + + clock_gettime(CLOCK_MONOTONIC, &router->eol); + router->eol.tv_sec += ntohs(pkt.hdr.ra.nd_ra_router_lifetime); + + memcpy(&router->lladdr, &pkt.hdr.ip6.ip6_src, sizeof(router->lladdr)); + + DEBUG_MSG("%d bytes in packet", len); + + // find prefix option + len -= sizeof(pkt.hdr); + ptr = (uint8_t*)&pkt + sizeof(pkt.hdr); + + while (len >= 8) { + unsigned int o_type = ptr[0]; + unsigned int o_len = (unsigned int)ptr[1] << 3; + struct nd_opt_prefix_info *o_pi; + + if (o_type != 3) { + ptr += o_len; + len -= o_len; + DEBUG_MSG("skipping option %d (size %d)", o_type, o_len); + continue; + } + CHECK(len >= o_len); + DEBUG_MSG("found option option %d (size %d)", o_type, o_len); + + o_pi = (struct nd_opt_prefix_info*)ptr; + memcpy(&router->prefix, &o_pi->nd_opt_pi_prefix, sizeof(router->prefix)); + + + ptr += o_len; + len -= o_len; + break; + } + + DEBUG_MSG("%d bytes remaining", len); + + if (inet_ntop(AF_INET6, &router->lladdr, addr_str, sizeof(addr_str))) { + DEBUG_MSG("lladdr: %s", addr_str); + } else { + DEBUG_MSG("lladdr: error"); + } + + if (inet_ntop(AF_INET6, &router->prefix, addr_str, sizeof(addr_str))) { + DEBUG_MSG("prefix: %s", addr_str); + } else { + DEBUG_MSG("prefix: error"); + } check_failed: return; @@ -620,6 +676,43 @@ static void update_tqs(void) { } } +static void update_redirect(void) { + struct router *router; + struct timespec timeout = { + .tv_nsec = EBTABLES_TIMEOUT, + }; + + foreach(router, G.routers) { + char mac[F_MAC_LEN + 1]; + char addr[INET6_ADDRSTRLEN]; + char prefix[INET6_ADDRSTRLEN]; + + if (router->redirected) + continue; + router->redirected = true; + + snprintf(mac, sizeof(mac), F_MAC, F_MAC_VAR(router->src)); + + if (inet_ntop(AF_INET6, &router->prefix, addr, sizeof(addr)) == NULL) { + error_message(0, 0, "warning: failed to format prefix"); + continue; + } + snprintf(prefix, sizeof(prefix), "%s/64", addr); + + if (fork_execvp_timeout(&timeout, "ebtables-tiny", (const char *[]) + { "ebtables-tiny", "-t", "nat", "-A", "REDIRECT", + "-p", "IPv6", + "--ip6-source", prefix, + "--ip6-destination", "!", prefix, + "-d", "!", mac, + "-j", "dnat", + "--to-destination", mac, + NULL })) + error_message(0, 0, "warning: adding new rule to ebtables chain REDIRECT failed"); + } +} + + static int fork_execvp_timeout(struct timespec *timeout, const char *file, const char *const argv[]) { int ret; pid_t child; @@ -795,6 +888,8 @@ int main(int argc, char *argv[]) { timespec_diff(&now, &next_update, &diff)) { expire_routers(); + update_redirect(); + // all routers could have expired, check again if (G.routers != NULL) { if(timespec_diff(&now, &next_invalidation, &diff)) { diff --git a/package/gluon-radv-filterd/src/respondd.c b/package/gluon-radv-filterd/src/respondd.c index 8c2c7eb428f13180182f8ee2542f5778411a8c27..bfa9257aebf0c25e9b4379a5707fa50f2a23dfc7 100644 --- a/package/gluon-radv-filterd/src/respondd.c +++ b/package/gluon-radv-filterd/src/respondd.c @@ -4,6 +4,7 @@ #include <libgluonutil.h> #include <net/ethernet.h> #include <stdio.h> +#include <string.h> #include "mac.h"