Skip to content

Commit

Permalink
pppoe: Extend XDP gtp_route to support mac-learning for VRF direct-tx…
Browse files Browse the repository at this point in the history
… mode

When runing multiple PPPoE instance in a bundle and using direct-tx featrure, packet
can come at PPPoE ingress from multiple interface, while re-encap related traffic
to GTP we need learn remote router mac address in order to skip fib_lookup for this
use-case.

eBPF program has been augmented to support mac address learning via a PERCPU_ARRAY
directly set upon incoming traffic. This learning can be maintained via userland
by resetting entry via state. This is left for futur dev task :) (but can be done
easily via netlink reflection from neighbor table).
  • Loading branch information
acassen committed Apr 6, 2024
1 parent 5325966 commit ffdf752
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 13 deletions.
15 changes: 14 additions & 1 deletion src/bpf/gtp.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ struct gtp_rt_rule {
#define GTP_RT_FL_UDP_LEARNING (1 << 3)

/* FIXME: How can we fetch cpu_num in BPF context ? */
const volatile int nr_cpus = 12;
const volatile int nr_cpus = 32;

struct rt_percpu_ctx {
/* ingress */
Expand All @@ -173,10 +173,23 @@ struct rt_percpu_ctx {
__u16 dst_port;
};

/* MAC Address learning */
struct port_mac_address {
__u8 local[6];
__u8 remote[6];
__u8 state;
} __attribute__ ((__aligned__(8)));

struct eth_percpu_ctx {
__u8 source[6];
__u8 dest[6];
};

/* Receive Packet Steering related */
struct rps_opts {
__u16 id;
__u16 max_id;
} __attribute__ ((__aligned__(8)));


#endif
93 changes: 83 additions & 10 deletions src/bpf/gtp_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ struct {
__type(value, struct gtp_iptnl_rule);
} iptnl_info SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 8);
__type(key, __u32);
__type(value, struct port_mac_address);
} mac_learning SEC(".maps");


/*
* Checksum related
Expand All @@ -97,7 +104,7 @@ static __always_inline void ipv4_csum(void *data_start, int data_size, __u32 *cs
*/
static __always_inline int
gtp_route_fib_lookup(struct xdp_md *ctx, struct ethhdr *ethh, struct iphdr *iph,
struct bpf_fib_lookup *fib_params, bool direct_tx)
struct bpf_fib_lookup *fib_params)
{
int ret;

Expand All @@ -116,9 +123,6 @@ gtp_route_fib_lookup(struct xdp_md *ctx, struct ethhdr *ethh, struct iphdr *iph,
__builtin_memcpy(ethh->h_dest, fib_params->dmac, ETH_ALEN);
__builtin_memcpy(ethh->h_source, fib_params->smac, ETH_ALEN);

if (direct_tx)
return XDP_TX;

if (ctx->ingress_ifindex != fib_params->ifindex)
return bpf_redirect(fib_params->ifindex, 0);

Expand Down Expand Up @@ -218,7 +222,7 @@ gtp_route_ipip_encap(struct parse_pkt *pkt, struct gtp_rt_rule *rt_rule)
*/
__builtin_memset(&fib_params, 0, sizeof(fib_params));
fib_params.ifindex = ctx->ingress_ifindex;
return gtp_route_fib_lookup(ctx, new_eth, iph, &fib_params, false);
return gtp_route_fib_lookup(ctx, new_eth, iph, &fib_params);
}

static __always_inline int
Expand Down Expand Up @@ -341,18 +345,69 @@ gtp_route_ipip_decap(struct parse_pkt *pkt)
*/
__builtin_memset(&fib_params, 0, sizeof(fib_params));
fib_params.ifindex = ctx->ingress_ifindex;
return gtp_route_fib_lookup(ctx, new_eth, iph, &fib_params, false);
return gtp_route_fib_lookup(ctx, new_eth, iph, &fib_params);
}


/*
* PPPoE
*/
static __always_inline int
gtp_port_mac_set(struct eth_percpu_ctx *ctx, struct port_mac_address *pma)
{
__builtin_memcpy(pma->local, ctx->dest, ETH_ALEN);
__builtin_memcpy(pma->remote, ctx->source, ETH_ALEN);
pma->state = 1;
return 0;
}

static int
gtp_port_mac_update(__u32 index, struct eth_percpu_ctx *ctx)
{
struct port_mac_address *pma;
__u32 key = 0;

pma = bpf_map_lookup_percpu_elem(&mac_learning, &key, index);
if (!pma) {
bpf_printk("Unable to lookup mac_learning idx:0, cpu_id:%d", index);
return 0;
}

return gtp_port_mac_set(ctx, pma);
}

static __always_inline int
gtp_port_mac_learning(struct ethhdr *ethh)
{
struct port_mac_address *pma;
struct eth_percpu_ctx ctx;
__u32 key = 0;

pma = bpf_map_lookup_elem(&mac_learning, &key);
if (!pma) {
bpf_printk("Unable to lookup mac_learning idx:0");
return -1;
}

if (pma->state)
return 0;

__builtin_memset(&ctx, 0, sizeof(struct eth_percpu_ctx));
__builtin_memcpy(ctx.dest, ethh->h_dest, ETH_ALEN);
__builtin_memcpy(ctx.source, ethh->h_source, ETH_ALEN);

/* Verifier prevent passing ethh as arg so we need to replicate
* pieces of interest. */
bpf_loop(nr_cpus, gtp_port_mac_update, &ctx, 0);
return 0;
}

static __always_inline int
gtp_route_ppp_encap(struct parse_pkt *pkt, struct gtp_rt_rule *rt_rule, __u16 length)
{
struct xdp_md *ctx = pkt->ctx;
void *data, *data_end;
void *data = (void *) (long) ctx->data;
void *data_end = (void *) (long) ctx->data_end;
struct ethhdr *ethh;
struct _vlan_hdr *vlanh = NULL;
struct pppoehdr *pppoeh;
Expand All @@ -362,6 +417,14 @@ gtp_route_ppp_encap(struct parse_pkt *pkt, struct gtp_rt_rule *rt_rule, __u16 le
__u16 *ppph;
int headroom, payload_len;

ethh = data;
if (ethh + 1 > data_end)
return XDP_PASS;

/* In direct-tx mode we are enabling mac learning */
if (rt_rule->flags & GTP_RT_FL_DIRECT_TX)
gtp_port_mac_learning(ethh);

/* Phase 0 : Build payload len */
payload_len = length + 2;

Expand Down Expand Up @@ -452,14 +515,16 @@ gtp_route_ppp_decap(struct parse_pkt *pkt)
struct ppp_key ppp_k;
int offset = pkt->l3_offset;
struct _vlan_hdr *vlanh = NULL;
struct pppoehdr *pppoeh;
struct ethhdr *ethh;
struct port_mac_address *pma;
struct pppoehdr *pppoeh;
struct iphdr *iph;
struct udphdr *udph;
struct gtphdr *gtph;
__u16 *ppph;
__u16 payload_len;
__u32 csum = 0;
__u32 key = 0;
int headroom;

ethh = data;
Expand Down Expand Up @@ -564,14 +629,22 @@ gtp_route_ppp_decap(struct parse_pkt *pkt)
gtph->teid = rt_rule->teid;
gtph->length = bpf_htons(payload_len);

/* In direct-tx mode we are using mac learning */
if (rt_rule->flags & GTP_RT_FL_DIRECT_TX) {
pma = bpf_map_lookup_elem(&mac_learning, &key);
if (pma) {
__builtin_memcpy(ethh->h_dest, pma->remote, ETH_ALEN);
__builtin_memcpy(ethh->h_source, pma->local, ETH_ALEN);
return XDP_TX;
}
}

/* We need to perform a fib lookup to resolv {s,d}mac properly
* and not re-invent the wheel by storing it locally in ruleset
*/
__builtin_memset(&fib_params, 0, sizeof(fib_params));
fib_params.ifindex = ctx->ingress_ifindex;
return gtp_route_fib_lookup(ctx, ethh, iph, &fib_params,
rt_rule->flags & GTP_RT_FL_DIRECT_TX);
return gtp_route_fib_lookup(ctx, ethh, iph, &fib_params);
}

/*
Expand Down
26 changes: 26 additions & 0 deletions src/gtp_vty.c
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,30 @@ DEFUN(show_xdp_routing_iptnl,
return CMD_SUCCESS;
}

DEFUN(show_xdp_routing_mac_learning,
show_xdp_routing_mac_learning_cmd,
"show xdp routing mac-learning",
SHOW_STR
"GTP XDP Routing IPIP MAC Address Learning\n")
{
int err;

if (!__test_bit(GTP_FL_GTP_ROUTE_LOADED_BIT, &daemon_data->flags)) {
vty_out(vty, "%% XDP GTP-Route is not configured. Ignoring%s"
, VTY_NEWLINE);
return CMD_WARNING;
}

err = gtp_xdp_rt_mac_learning_vty(vty);
if (err) {
vty_out(vty, "%% Error displaying XDP ruleset%s"
, VTY_NEWLINE);
return CMD_WARNING;
}

return CMD_SUCCESS;
}

DEFUN(show_xdp_mirror,
show_xdp_mirror_cmd,
"show xdp mirror",
Expand Down Expand Up @@ -820,13 +844,15 @@ gtp_vty_init(void)
install_element(VIEW_NODE, &show_xdp_forwarding_iptnl_cmd);
install_element(VIEW_NODE, &show_xdp_routing_cmd);
install_element(VIEW_NODE, &show_xdp_routing_iptnl_cmd);
install_element(VIEW_NODE, &show_xdp_routing_mac_learning_cmd);
install_element(VIEW_NODE, &show_xdp_mirror_cmd);
install_element(VIEW_NODE, &gtp_send_echo_request_standard_cmd);
install_element(VIEW_NODE, &gtp_send_echo_request_extended_cmd);
install_element(ENABLE_NODE, &show_xdp_forwarding_cmd);
install_element(ENABLE_NODE, &show_xdp_forwarding_iptnl_cmd);
install_element(ENABLE_NODE, &show_xdp_routing_cmd);
install_element(ENABLE_NODE, &show_xdp_routing_iptnl_cmd);
install_element(ENABLE_NODE, &show_xdp_routing_mac_learning_cmd);
install_element(ENABLE_NODE, &show_xdp_mirror_cmd);
install_element(ENABLE_NODE, &gtp_send_echo_request_standard_cmd);
install_element(ENABLE_NODE, &gtp_send_echo_request_extended_cmd);
Expand Down
2 changes: 1 addition & 1 deletion src/gtp_xdp_ppp.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ gtp_xdp_ppp_rule_alloc(size_t *sz)
if (!new)
return NULL;

*sz = nr_cpus * sizeof(struct gtp_rt_rule);
*sz = nr_cpus * sizeof(*new);
return new;
}

Expand Down
75 changes: 74 additions & 1 deletion src/gtp_xdp_rt.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ gtp_xdp_rt_load(gtp_bpf_opts_t *opts)
}
opts->bpf_maps[XDP_RT_MAP_IPTNL].map = map;

map = gtp_bpf_load_map(opts->bpf_obj, "mac_learning");
if (!map) {
gtp_xdp_unload(opts);
return -1;
}
opts->bpf_maps[XDP_RT_MAP_MAC_LEARNING].map = map;

return 0;
}

Expand Down Expand Up @@ -460,4 +467,70 @@ gtp_xdp_rt_iptnl_vty(vty_t *vty)
}

return 0;
}
}

/*
* MAC learning related
*/
static struct port_mac_address *
gtp_xdp_port_mac_address_alloc(size_t *sz)
{
unsigned int nr_cpus = bpf_num_possible_cpus();
struct port_mac_address *new;

new = calloc(nr_cpus, sizeof(*new));
if (!new)
return NULL;

*sz = nr_cpus * sizeof(*new);
return new;
}

static int
gtp_xdp_rt_learning_vty(vty_t *vty, struct bpf_map *map)
{
struct port_mac_address *pma;
char errmsg[GTP_XDP_STRERR_BUFSIZE];
__u32 key = 0;
size_t sz;
int err;

pma = gtp_xdp_port_mac_address_alloc(&sz);
if (!pma) {
vty_out(vty, "%% Cant allocate temp port_mac_address%s", VTY_NEWLINE);
return -1;
}

err = bpf_map__lookup_elem(map, &key, sizeof(__u32), pma, sz, 0);
if (err) {
libbpf_strerror(err, errmsg, GTP_XDP_STRERR_BUFSIZE);
vty_out(vty, "%% error fetching mac-learning for key:%d (%s)%s"
, key, errmsg, VTY_NEWLINE);
free(pma);
return -1;
}

vty_out(vty, " local:" ETHER_FMT " remote:" ETHER_FMT "%s"
, ETHER_BYTES(pma[0].local)
, ETHER_BYTES(pma[0].remote)
, VTY_NEWLINE);
free(pma);
return 0;
}

int
gtp_xdp_rt_mac_learning_vty(vty_t *vty)
{
list_head_t *l = &daemon_data->xdp_gtp_route;
gtp_bpf_opts_t *opts;

if (!__test_bit(GTP_FL_GTP_ROUTE_LOADED_BIT, &daemon_data->flags))
return -1;

list_for_each_entry(opts, l, next) {
vty_out(vty, "XDP ruleset on ifindex:%d:%s", opts->ifindex, VTY_NEWLINE);
gtp_xdp_rt_learning_vty(vty, opts->bpf_maps[XDP_RT_MAP_MAC_LEARNING].map);
}

return 0;
}
8 changes: 8 additions & 0 deletions src/include/gtp_xdp_rt.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ enum {
XDP_RT_MAP_TEID_EGRESS,
XDP_RT_MAP_PPP_INGRESS,
XDP_RT_MAP_IPTNL,
XDP_RT_MAP_MAC_LEARNING,
XDP_RT_MAP_CNT
};

Expand Down Expand Up @@ -59,6 +60,12 @@ struct gtp_rt_rule {
__u8 flags;
} __attribute__ ((__aligned__(8)));

struct port_mac_address {
__u8 local[6];
__u8 remote[6];
__u8 state;
} __attribute__ ((__aligned__(8)));


/* Prototypes */
extern int gtp_xdp_rt_load(gtp_bpf_opts_t *);
Expand All @@ -69,5 +76,6 @@ extern int gtp_xdp_rt_teid_vty(vty_t *, gtp_teid_t *);
extern int gtp_xdp_rt_vty(vty_t *);
extern int gtp_xdp_rt_iptnl_action(int, gtp_iptnl_t *);
extern int gtp_xdp_rt_iptnl_vty(vty_t *);
extern int gtp_xdp_rt_mac_learning_vty(vty_t *);

#endif

0 comments on commit ffdf752

Please sign in to comment.