mirror of
https://github.com/xdp-project/bpf-examples.git
synced 2024-05-06 15:54:53 +00:00
nat64: Insert route and neighbour entry on setup/teardown
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 <toke@redhat.com>
This commit is contained in:
@@ -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
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <getopt.h>
|
||||
@@ -16,6 +17,9 @@
|
||||
#include <bpf/libbpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
|
||||
#include <libmnl/libmnl.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#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;
|
||||
|
Reference in New Issue
Block a user