From 742924a0761fc9a94c1d4c565f21d358dcc733c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Mon, 4 Oct 2021 17:13:00 +0200 Subject: [PATCH] nat64: Insert route and neighbour entry on setup/teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We insert a v4-via-v6 route on the interface to direct packets to the v4 subnet to the right interface, where it will be rewritten by the BPF program. We also create a fake neighbour entry so the kernel won't do neighbour resolution when sending the pre-rewrite packet. The egress BPF program will use bpf_redirect_neigh() to do proper neighbour resolution for the actual destination after rewriting the packet. Signed-off-by: Toke Høiland-Jørgensen --- nat64-bpf/Makefile | 1 + nat64-bpf/nat64.c | 190 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 182 insertions(+), 9 deletions(-) diff --git a/nat64-bpf/Makefile b/nat64-bpf/Makefile index 9fad6d3..264ee5f 100644 --- a/nat64-bpf/Makefile +++ b/nat64-bpf/Makefile @@ -5,6 +5,7 @@ BPF_TARGETS := nat64_kern BPF_SKEL_OBJ := nat64_kern.o #LDLIBS += -pthread +USER_LIBS = -lmnl EXTRA_DEPS += nat64.h LIB_DIR = ../lib diff --git a/nat64-bpf/nat64.c b/nat64-bpf/nat64.c index 594e613..694b6b3 100644 --- a/nat64-bpf/nat64.c +++ b/nat64-bpf/nat64.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,9 @@ #include #include +#include +#include + #include "nat64.h" #include "nat64_kern.skel.h" @@ -39,6 +43,7 @@ struct nat64_user_config { char ifname[IF_NAMESIZE+1]; struct in6_addr v6_allow; __u32 v6_allow_pxlen; + __u32 v4_pxlen; bool unload; }; @@ -137,6 +142,7 @@ static int parse_arguments(int argc, char *argv[], struct nat64_user_config *con return -EINVAL; } config->c.v4_mask = 0xFFFFFFFF << (32 - pxlen); + config->v4_pxlen = pxlen; config->c.v4_prefix = ntohl(v4addr.s_addr); if (config->c.v4_prefix & ~config->c.v4_mask) { fprintf(stderr, "Not a network address: %s\n", optarg); @@ -175,6 +181,173 @@ static int parse_arguments(int argc, char *argv[], struct nat64_user_config *con return 0; } +static int do_v4_neigh(struct mnl_socket *nl, struct nat64_user_config *cfg, bool create) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t seq, portid; + struct rtmsg *rtm; + int ret, err = 0; + + struct { + __u16 family; + struct in6_addr addr; + } __attribute__((packed)) via = { + .family = AF_INET6, + .addr = cfg->c.v6_prefix + }; + + + nlh = mnl_nlmsg_put_header(buf); + if (create) { + nlh->nlmsg_type = RTM_NEWROUTE; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; + } else { + nlh->nlmsg_type = RTM_DELROUTE; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + } + nlh->nlmsg_seq = seq = time(NULL); + + rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); + rtm->rtm_family = AF_INET; + rtm->rtm_dst_len = cfg->v4_pxlen; + rtm->rtm_src_len = 0; + rtm->rtm_tos = 0; + rtm->rtm_protocol = RTPROT_STATIC; + rtm->rtm_table = RT_TABLE_MAIN; + rtm->rtm_type = RTN_UNICAST; + rtm->rtm_scope = RT_SCOPE_UNIVERSE; + rtm->rtm_flags = RTNH_F_ONLINK; + + mnl_attr_put_u32(nlh, RTA_DST, htonl(cfg->c.v4_prefix)); + mnl_attr_put_u32(nlh, RTA_OIF, cfg->ifindex); + mnl_attr_put(nlh, RTA_VIA, sizeof(via), &via); + + portid = mnl_socket_get_portid(nl); + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + err = -errno; + goto out; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret < 0) { + perror("mnl_socket_recvfrom"); + err = -errno; + goto out; + } + + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret < 0) { + if ((create && errno != EEXIST) || + !(create && errno != ENOENT && errno != ESRCH)) + err = -errno; + goto out; + } + +out: + return err; +} + +static int do_v4_route(struct mnl_socket *nl, struct nat64_user_config *cfg, bool create) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t seq, portid; + struct ndmsg *ndm; + int ret, err = 0; + + __u8 lladdr[6] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; + nlh = mnl_nlmsg_put_header(buf); + if (create) { + nlh->nlmsg_type = RTM_NEWNEIGH; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; + } else { + nlh->nlmsg_type = RTM_DELNEIGH; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + } + nlh->nlmsg_seq = seq = time(NULL); + + ndm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ndmsg)); + ndm->ndm_family = AF_INET6; + ndm->ndm_ifindex = cfg->ifindex; + ndm->ndm_state = NUD_PERMANENT; + ndm->ndm_type = 0; + + mnl_attr_put(nlh, NDA_LLADDR, sizeof(lladdr), &lladdr); + mnl_attr_put(nlh, NDA_DST, sizeof(struct in6_addr), &cfg->c.v6_prefix); + + portid = mnl_socket_get_portid(nl); + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + err = -errno; + goto out; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret < 0) { + perror("mnl_socket_recvfrom"); + err = -errno; + goto out; + } + + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret < 0) { + if ((create && errno != EEXIST) || + !(create && errno != ENOENT && errno != ESRCH)) + err = -errno; + goto out; + } + +out: + return err; +} + +static int do_netlink(struct nat64_user_config *cfg, bool create) +{ + struct mnl_socket *nl; + int err = 0; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + err = -errno; + goto out; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + err = -errno; + goto out; + } + + err = do_v4_route(nl, cfg, create); + err = err ?: do_v4_neigh(nl, cfg, create); + +out: + mnl_socket_close(nl); + return err; +} + + +int teardown(struct nat64_user_config *cfg) +{ + DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, + .attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS, + .ifindex = cfg->ifindex); + int err; + + err = bpf_tc_hook_destroy(&hook); + if (err) + fprintf(stderr, "Couldn't remove clsact qdisc on %s\n", cfg->ifname); + + err = do_netlink(cfg, false); + if (err) + fprintf(stderr, "Couldn't remove route on %s: %s\n", + cfg->ifname, strerror(-err)); + + return err; +} int main(int argc, char *argv[]) @@ -194,13 +367,8 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; hook.ifindex = cfg.ifindex; - if (cfg.unload) { - err = bpf_tc_hook_destroy(&hook); - if (err) - fprintf(stderr, "Couldn't remove clsact qdisc on %s\n", cfg.ifname); - - return err; - } + if (cfg.unload) + return teardown(&cfg); obj = nat64_kern__open(); err = libbpf_get_error(obj); @@ -212,8 +380,6 @@ int main(int argc, char *argv[]) num_addr = (cfg.c.v4_prefix | ~cfg.c.v4_mask) - cfg.c.v4_prefix - 2; - printf("num addr: %u\n", num_addr); - obj->bss->config = cfg.c; bpf_map__resize(obj->maps.v6_state_map, num_addr); bpf_map__resize(obj->maps.v4_reversemap, num_addr); @@ -276,6 +442,12 @@ int main(int argc, char *argv[]) goto out; } + err = do_netlink(&cfg, true); + if (err) { + fprintf(stderr, "Couldn't create route: %s\n", strerror(-err)); + err = teardown(&cfg); + } + out: nat64_kern__destroy(obj); return err;