Page MenuHomeVyOS Platform

xdp_prog_kern.c
No OneTemporary

Size
10 KB
Referenced Files
None
Subscribers
None

xdp_prog_kern.c

/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/bpf.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
// The parsing helper functions from the packet01 lesson have moved here
#include "common/parsing_helpers.h"
#include "common/rewrite_helpers.h"
/* Defines xdp_stats_map */
#include "common/xdp_stats_kern_user.h"
#include "common/xdp_stats_kern.h"
#ifndef memcpy
#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n))
#endif
struct {
__uint(type, BPF_MAP_TYPE_DEVMAP);
__type(key, int);
__type(value, int);
__uint(max_entries, 256);
} tx_port SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, ETH_ALEN);
__type(value, ETH_ALEN);
__uint(max_entries, 1);
} redirect_params SEC(".maps");
static __always_inline __u16 csum_fold_helper(__u32 csum)
{
return ~((csum & 0xffff) + (csum >> 16));
}
/*
* The icmp_checksum_diff function takes pointers to old and new structures and
* the old checksum and returns the new checksum. It uses the bpf_csum_diff
* helper to compute the checksum difference. Note that the sizes passed to the
* bpf_csum_diff helper should be multiples of 4, as it operates on 32-bit
* words.
*/
static __always_inline __u16 icmp_checksum_diff(
__u16 seed,
struct icmphdr_common *icmphdr_new,
struct icmphdr_common *icmphdr_old)
{
__u32 csum, size = sizeof(struct icmphdr_common);
csum = bpf_csum_diff((__be32 *)icmphdr_old, size, (__be32 *)icmphdr_new, size, seed);
return csum_fold_helper(csum);
}
/* Solution to packet03/assignment-1 */
SEC("xdp_icmp_echo")
int xdp_icmp_echo_func(struct xdp_md *ctx)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct hdr_cursor nh;
struct ethhdr *eth;
int eth_type;
int ip_type;
int icmp_type;
struct iphdr *iphdr;
struct ipv6hdr *ipv6hdr;
__u16 echo_reply, old_csum;
struct icmphdr_common *icmphdr;
struct icmphdr_common icmphdr_old;
__u32 action = XDP_PASS;
/* These keep track of the next header type and iterator pointer */
nh.pos = data;
/* Parse Ethernet and IP/IPv6 headers */
eth_type = parse_ethhdr(&nh, data_end, &eth);
if (eth_type == bpf_htons(ETH_P_IP)) {
ip_type = parse_iphdr(&nh, data_end, &iphdr);
if (ip_type != IPPROTO_ICMP)
goto out;
} else if (eth_type == bpf_htons(ETH_P_IPV6)) {
ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr);
if (ip_type != IPPROTO_ICMPV6)
goto out;
} else {
goto out;
}
/*
* We are using a special parser here which returns a stucture
* containing the "protocol-independent" part of an ICMP or ICMPv6
* header. For purposes of this Assignment we are not interested in
* the rest of the structure.
*/
icmp_type = parse_icmphdr_common(&nh, data_end, &icmphdr);
if (eth_type == bpf_htons(ETH_P_IP) && icmp_type == ICMP_ECHO) {
/* Swap IP source and destination */
swap_src_dst_ipv4(iphdr);
echo_reply = ICMP_ECHOREPLY;
} else if (eth_type == bpf_htons(ETH_P_IPV6)
&& icmp_type == ICMPV6_ECHO_REQUEST) {
/* Swap IPv6 source and destination */
swap_src_dst_ipv6(ipv6hdr);
echo_reply = ICMPV6_ECHO_REPLY;
} else {
goto out;
}
/* Swap Ethernet source and destination */
swap_src_dst_mac(eth);
/* Patch the packet and update the checksum.*/
old_csum = icmphdr->cksum;
icmphdr->cksum = 0;
icmphdr_old = *icmphdr;
icmphdr->type = echo_reply;
icmphdr->cksum = icmp_checksum_diff(~old_csum, icmphdr, &icmphdr_old);
/* Another, less generic, but a bit more efficient way to update the
* checksum is listed below. As only one 16-bit word changed, the sum
* can be patched using this formula: sum' = ~(~sum + ~m0 + m1), where
* sum' is a new sum, sum is an old sum, m0 and m1 are the old and new
* 16-bit words, correspondingly. In the formula above the + operation
* is defined as the following function:
*
* static __always_inline __u16 csum16_add(__u16 csum, __u16 addend)
* {
* csum += addend;
* return csum + (csum < addend);
* }
*
* So an alternative code to update the checksum might look like this:
*
* __u16 m0 = * (__u16 *) icmphdr;
* icmphdr->type = echo_reply;
* __u16 m1 = * (__u16 *) icmphdr;
* icmphdr->checksum = ~(csum16_add(csum16_add(~icmphdr->checksum, ~m0), m1));
*/
action = XDP_TX;
out:
return xdp_stats_record_action(ctx, action);
}
/* Solution to packet03/assignment-2 */
SEC("xdp_redirect")
int xdp_redirect_func(struct xdp_md *ctx)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct hdr_cursor nh;
struct ethhdr *eth;
int eth_type;
int action = XDP_PASS;
unsigned char dst[ETH_ALEN] = { /* TODO: put your values here */ };
unsigned ifindex = 0/* TODO: put your values here */;
/* These keep track of the next header type and iterator pointer */
nh.pos = data;
/* Parse Ethernet and IP/IPv6 headers */
eth_type = parse_ethhdr(&nh, data_end, &eth);
if (eth_type == -1)
goto out;
/* Set a proper destination address */
memcpy(eth->h_dest, dst, ETH_ALEN);
action = bpf_redirect(ifindex, 0);
out:
return xdp_stats_record_action(ctx, action);
}
/* Solution to packet03/assignment-3 */
SEC("xdp_redirect_map")
int xdp_redirect_map_func(struct xdp_md *ctx)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct hdr_cursor nh;
struct ethhdr *eth;
int eth_type;
int action = XDP_PASS;
unsigned char *dst;
/* These keep track of the next header type and iterator pointer */
nh.pos = data;
/* Parse Ethernet and IP/IPv6 headers */
eth_type = parse_ethhdr(&nh, data_end, &eth);
if (eth_type == -1)
goto out;
/* Do we know where to redirect this packet? */
dst = bpf_map_lookup_elem(&redirect_params, eth->h_source);
if (!dst)
goto out;
/* Set a proper destination address */
memcpy(eth->h_dest, dst, ETH_ALEN);
action = bpf_redirect_map(&tx_port, 0, 0);
out:
return xdp_stats_record_action(ctx, action);
}
#ifndef AF_INET
#define AF_INET 2
#endif
#ifndef AF_INET6
#define AF_INET6 10
#endif
#define IPV6_FLOWINFO_MASK bpf_htonl(0x0FFFFFFF)
/* from include/net/ip.h */
static __always_inline int ip_decrease_ttl(struct iphdr *iph)
{
__u32 check = iph->check;
check += bpf_htons(0x0100);
iph->check = (__u16)(check + (check >= 0xFFFF));
return --iph->ttl;
}
/* Solution to packet03/assignment-4 */
/* xdp_router is the name of the xdp program */
SEC("xdp_router")
int xdp_router_func(struct xdp_md *ctx)
{
/* this is the packet context*/
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct bpf_fib_lookup fib_params = {};
struct ethhdr *eth = data;
struct ipv6hdr *ip6h;
struct iphdr *iph;
__u16 h_proto;
__u64 nh_off;
int rc;
/* default action is to pass */
int action = XDP_PASS;
nh_off = sizeof(*eth);
if (data + nh_off > data_end) {
action = XDP_DROP;
goto out;
}
/* determine if this is IP4 or IPv6 by looking at the Ethernet protocol field */
h_proto = eth->h_proto;
if (h_proto == bpf_htons(ETH_P_IP)) {
/* IPv4 part of the code */
iph = data + nh_off;
if (iph + 1 > data_end) {
action = XDP_DROP;
goto out;
}
/* as a real router, we need to check the TTL to prevent never ending loops*/
if (iph->ttl <= 1)
goto out;
/* populate the fib_params fields to prepare for the lookup */
fib_params.family = AF_INET;
fib_params.tos = iph->tos;
fib_params.l4_protocol = iph->protocol;
fib_params.sport = 0;
fib_params.dport = 0;
fib_params.tot_len = bpf_ntohs(iph->tot_len);
fib_params.ipv4_src = iph->saddr;
fib_params.ipv4_dst = iph->daddr;
} else if (h_proto == bpf_htons(ETH_P_IPV6)) {
/* IPv6 part of the code */
struct in6_addr *src = (struct in6_addr *) fib_params.ipv6_src;
struct in6_addr *dst = (struct in6_addr *) fib_params.ipv6_dst;
ip6h = data + nh_off;
if (ip6h + 1 > data_end) {
action = XDP_DROP;
goto out;
}
/* as a real router, we need to check the TTL to prevent never ending loops*/
if (ip6h->hop_limit <= 1)
goto out;
/* populate the fib_params fields to prepare for the lookup */
fib_params.family = AF_INET6;
fib_params.flowinfo = *(__be32 *) ip6h & IPV6_FLOWINFO_MASK;
fib_params.l4_protocol = ip6h->nexthdr;
fib_params.sport = 0;
fib_params.dport = 0;
fib_params.tot_len = bpf_ntohs(ip6h->payload_len);
*src = ip6h->saddr;
*dst = ip6h->daddr;
} else {
goto out;
}
fib_params.ifindex = ctx->ingress_ifindex;
/* this is where the FIB lookup happens. If the lookup is successful */
/* it will populate the fib_params.ifindex with the egress interface index */
rc = bpf_fib_lookup(ctx, &fib_params, sizeof(fib_params), 0);
switch (rc) {
case BPF_FIB_LKUP_RET_SUCCESS: /* lookup successful */
/* we are a router, so we need to decrease the ttl */
if (h_proto == bpf_htons(ETH_P_IP))
ip_decrease_ttl(iph);
else if (h_proto == bpf_htons(ETH_P_IPV6))
ip6h->hop_limit--;
/* set the correct new source and destionation mac addresses */
/* can be found in fib_params.dmac and fib_params.smac */
memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN);
memcpy(eth->h_source, fib_params.smac, ETH_ALEN);
/* and done, now we set the action to bpf_redirect_map with fib_params.ifindex which is the egress port as paramater */
action = bpf_redirect_map(&tx_port, fib_params.ifindex, 0);
break;
case BPF_FIB_LKUP_RET_BLACKHOLE: /* dest is blackholed; can be dropped */
case BPF_FIB_LKUP_RET_UNREACHABLE: /* dest is unreachable; can be dropped */
case BPF_FIB_LKUP_RET_PROHIBIT: /* dest not allowed; can be dropped */
action = XDP_DROP;
break;
case BPF_FIB_LKUP_RET_NOT_FWDED: /* packet is not forwarded */
case BPF_FIB_LKUP_RET_FWD_DISABLED: /* fwding is not enabled on ingress */
case BPF_FIB_LKUP_RET_UNSUPP_LWT: /* fwd requires encapsulation */
case BPF_FIB_LKUP_RET_NO_NEIGH: /* no neighbor entry for nh */
case BPF_FIB_LKUP_RET_FRAG_NEEDED: /* fragmentation required to fwd */
/* PASS */
break;
}
out:
/* and done, update stats and return action */
return xdp_stats_record_action(ctx, action);
}
SEC("xdp_pass")
int xdp_pass_func(struct xdp_md *ctx)
{
return xdp_stats_record_action(ctx, XDP_PASS);
}
char _license[] SEC("license") = "GPL";

File Metadata

Mime Type
text/x-c
Expires
Fri, Jan 30, 11:45 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3308627
Default Alt Text
xdp_prog_kern.c (10 KB)

Event Timeline