Skip to content

Commit

Permalink
switch: Add initial support to MAC Address learning + cosmetics
Browse files Browse the repository at this point in the history
Some code refactor and eBPF extensions to support MAC Address learning
  • Loading branch information
acassen committed Apr 14, 2024
1 parent 7c44901 commit 8de4150
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 109 deletions.
8 changes: 4 additions & 4 deletions src/bpf/gtp.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ struct gtp_teid_rule {
/* Some stats */
__u64 packets;
__u64 bytes;

__u8 direction;
__u8 flags;
} __attribute__ ((__aligned__(8)));
#define GTP_TEID_DIRECTION_INGRESS 0
#define GTP_TEID_DIRECTION_EGRESS 1
#define GTP_FWD_FL_INGRESS (1 << 0)
#define GTP_FWD_FL_EGRESS (1 << 1)
#define GTP_FWD_FL_DIRECT_TX (1 << 2)

/* IP Fragmentation handling */
#define IP_CE 0x8000 /* Flag: "Congestion" */
Expand Down
121 changes: 90 additions & 31 deletions src/bpf/gtp_fwd.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,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");


/* Packet rewrite */
static __always_inline void swap_src_dst_mac(struct ethhdr *eth)
Expand Down Expand Up @@ -421,24 +428,13 @@ gtpu_xlat(struct parse_pkt *pkt, struct ethhdr *ethh, struct iphdr *iph, struct
{
struct gtp_iptnl_rule *iptnl_rule;

/* That is a nice feature of XDP here:
* punt to linux kernel stack path-management message.
* We get it back into userland where things are easier
*/
if (gtph->type != 0xff)
return XDP_PASS;

/* Prevent GTP-U to flood stack */
if (!rule)
return XDP_DROP;

/* Phase 0 : IPIP tunneling if needed
* Traffic selector is based on original IP header
* destination address */
iptnl_rule = bpf_map_lookup_elem(&iptnl_info, &iph->daddr);
if (iptnl_rule && !(iptnl_rule->flags & IPTNL_FL_DEAD) &&
iptnl_rule->flags & IPTNL_FL_TRANSPARENT_EGRESS_ENCAP &&
rule->direction == GTP_TEID_DIRECTION_INGRESS)
rule->flags & GTP_FWD_FL_INGRESS)
return gtpu_ipencap(pkt, iptnl_rule);

gtpu_xlat_header(rule, iph, gtph);
Expand Down Expand Up @@ -506,6 +502,56 @@ gtpu_ip_frag_timer_set(const struct ip_frag_key *frag_key)
return 0;
}

/*
* MAC Address learning
*/
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)
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)
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;
}


/*
* GTP-U traffic selector
*/
Expand Down Expand Up @@ -564,26 +610,39 @@ gtpu_traffic_selector(struct parse_pkt *pkt)
gtph = data + offset + sizeof(struct udphdr);
if (gtph + 1 > data_end)
return XDP_DROP;


/* That is a nice feature of XDP here:
* punt to linux kernel stack path-management message.
* We get it back into userland where things are easier
*/
if (gtph->type != 0xff)
return XDP_PASS;

rule = bpf_map_lookup_elem(&teid_xlat, &gtph->teid);
if (rule) {
/* First fragment detected and handled only if related
* to an existing F-TEID */
if ((ipfl & IP_MF) && (frag_off == 0)) {
__builtin_memset(&frag_key, 0, sizeof(struct ip_frag_key));
frag_key.saddr = iph->saddr;
frag_key.daddr = iph->daddr;
frag_key.id = iph->id;
frag_key.protocol = iph->protocol;

__builtin_memset(&frag, 0, sizeof(struct gtp_teid_frag));
frag.dst_addr = rule->dst_addr;
ret = bpf_map_update_elem(&ip_frag, &frag_key, &frag, BPF_NOEXIST);
if (ret < 0)
return XDP_DROP;

gtpu_ip_frag_timer_set(&frag_key);
}
/* Prevent from GTP-U netstack flooding */
if (!rule)
return XDP_DROP;

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

/* First fragment detected and handled only if related
* to an existing F-TEID */
if ((ipfl & IP_MF) && (frag_off == 0)) {
__builtin_memset(&frag_key, 0, sizeof(struct ip_frag_key));
frag_key.saddr = iph->saddr;
frag_key.daddr = iph->daddr;
frag_key.id = iph->id;
frag_key.protocol = iph->protocol;

__builtin_memset(&frag, 0, sizeof(struct gtp_teid_frag));
frag.dst_addr = rule->dst_addr;
ret = bpf_map_update_elem(&ip_frag, &frag_key, &frag, BPF_NOEXIST);
if (ret < 0)
return XDP_DROP;

gtpu_ip_frag_timer_set(&frag_key);
}

return gtpu_xlat(pkt, ethh, iph, udph, gtph, rule);
Expand Down
8 changes: 2 additions & 6 deletions src/bpf/gtp_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -368,10 +368,8 @@ gtp_port_mac_update(__u32 index, struct eth_percpu_ctx *ctx)
__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);
if (!pma)
return 0;
}

return gtp_port_mac_set(ctx, pma);
}
Expand All @@ -384,10 +382,8 @@ gtp_port_mac_learning(struct ethhdr *ethh)
__u32 key = 0;

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

if (pma->state)
return 0;
Expand Down
50 changes: 50 additions & 0 deletions src/gtp_xdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,56 @@ extern data_t *daemon_data;
static const char *pin_basedir = "/sys/fs/bpf";


/*
* XDP common helpers
*/
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;
}

int
gtp_xdp_mac_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;
}


/*
* XDP related
*/
Expand Down
Loading

0 comments on commit 8de4150

Please sign in to comment.