mirror of
https://github.com/xdp-project/BNG-router.git
synced 2024-05-06 15:54:53 +00:00
Import build config and DHCP relay code from bpf-examples
The dhcp-relay utility was initially implemented as part of the bpf-examples repository, but really belongs here. So import it along with the build environment from bpf-examples. Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
This commit is contained in:
73
include/xdp/context_helpers.h
Normal file
73
include/xdp/context_helpers.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2020 Authors of Cilium */
|
||||
/* For now, only import the xdp_load_bytes() and xdp_store_bytes() helpers
|
||||
* from Cilium.
|
||||
*/
|
||||
|
||||
#ifndef __XDP_CONTEXT_HELPERS_H
|
||||
#define __XDP_CONTEXT_HELPERS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/bpf.h>
|
||||
|
||||
#include "../bpf/builtins.h"
|
||||
#include "../bpf/errno.h"
|
||||
|
||||
/* This must be a mask and all offsets guaranteed to be less than that. */
|
||||
#define __CTX_OFF_MAX 0x3fff
|
||||
|
||||
static __always_inline __maybe_unused int
|
||||
xdp_load_bytes(const struct xdp_md *ctx, __u64 off, void *to, const __u64 len)
|
||||
{
|
||||
void *from;
|
||||
int ret;
|
||||
/* LLVM tends to generate code that verifier doesn't understand,
|
||||
* so force it the way we want it in order to open up a range
|
||||
* on the reg.
|
||||
*/
|
||||
asm volatile("r1 = *(u32 *)(%[ctx] +0)\n\t"
|
||||
"r2 = *(u32 *)(%[ctx] +4)\n\t"
|
||||
"%[off] &= %[offmax]\n\t"
|
||||
"r1 += %[off]\n\t"
|
||||
"%[from] = r1\n\t"
|
||||
"r1 += %[len]\n\t"
|
||||
"if r1 > r2 goto +2\n\t"
|
||||
"%[ret] = 0\n\t"
|
||||
"goto +1\n\t"
|
||||
"%[ret] = %[errno]\n\t"
|
||||
: [ret]"=r"(ret), [from]"=r"(from)
|
||||
: [ctx]"r"(ctx), [off]"r"(off), [len]"ri"(len),
|
||||
[offmax]"i"(__CTX_OFF_MAX), [errno]"i"(-EINVAL)
|
||||
: "r1", "r2");
|
||||
if (!ret)
|
||||
memcpy(to, from, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __always_inline __maybe_unused int
|
||||
xdp_store_bytes(const struct xdp_md *ctx, __u64 off, const void *from,
|
||||
const __u64 len, __u64 flags __maybe_unused)
|
||||
{
|
||||
void *to;
|
||||
int ret;
|
||||
/* See xdp_load_bytes(). */
|
||||
asm volatile("r1 = *(u32 *)(%[ctx] +0)\n\t"
|
||||
"r2 = *(u32 *)(%[ctx] +4)\n\t"
|
||||
"%[off] &= %[offmax]\n\t"
|
||||
"r1 += %[off]\n\t"
|
||||
"%[to] = r1\n\t"
|
||||
"r1 += %[len]\n\t"
|
||||
"if r1 > r2 goto +2\n\t"
|
||||
"%[ret] = 0\n\t"
|
||||
"goto +1\n\t"
|
||||
"%[ret] = %[errno]\n\t"
|
||||
: [ret]"=r"(ret), [to]"=r"(to)
|
||||
: [ctx]"r"(ctx), [off]"r"(off), [len]"ri"(len),
|
||||
[offmax]"i"(__CTX_OFF_MAX), [errno]"i"(-EINVAL)
|
||||
: "r1", "r2");
|
||||
if (!ret)
|
||||
memcpy(to, from, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
320
include/xdp/parsing_helpers.h
Normal file
320
include/xdp/parsing_helpers.h
Normal file
@@ -0,0 +1,320 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-clause) */
|
||||
/*
|
||||
* This file contains parsing functions that are used in the packetXX XDP
|
||||
* programs. The functions are marked as __always_inline, and fully defined in
|
||||
* this header file to be included in the BPF program.
|
||||
*
|
||||
* Each helper parses a packet header, including doing bounds checking, and
|
||||
* returns the type of its contents if successful, and -1 otherwise.
|
||||
*
|
||||
* For Ethernet and IP headers, the content type is the type of the payload
|
||||
* (h_proto for Ethernet, nexthdr for IPv6), for ICMP it is the ICMP type field.
|
||||
* All return values are in host byte order.
|
||||
*
|
||||
* The versions of the functions included here are slightly expanded versions of
|
||||
* the functions in the packet01 lesson. For instance, the Ethernet header
|
||||
* parsing has support for parsing VLAN tags.
|
||||
*/
|
||||
|
||||
#ifndef __PARSING_HELPERS_H
|
||||
#define __PARSING_HELPERS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/in.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
|
||||
/* Header cursor to keep track of current parsing position */
|
||||
struct hdr_cursor {
|
||||
void *pos;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct vlan_hdr - vlan header
|
||||
* @h_vlan_TCI: priority and VLAN ID
|
||||
* @h_vlan_encapsulated_proto: packet type ID or len
|
||||
*/
|
||||
struct vlan_hdr {
|
||||
__be16 h_vlan_TCI;
|
||||
__be16 h_vlan_encapsulated_proto;
|
||||
};
|
||||
|
||||
/*
|
||||
* Struct icmphdr_common represents the common part of the icmphdr and icmp6hdr
|
||||
* structures.
|
||||
*/
|
||||
struct icmphdr_common {
|
||||
__u8 type;
|
||||
__u8 code;
|
||||
__sum16 cksum;
|
||||
};
|
||||
|
||||
/* Allow users of header file to redefine VLAN max depth */
|
||||
#ifndef VLAN_MAX_DEPTH
|
||||
#define VLAN_MAX_DEPTH 2
|
||||
#endif
|
||||
|
||||
/* Longest chain of IPv6 extension headers to resolve */
|
||||
#ifndef IPV6_EXT_MAX_CHAIN
|
||||
#define IPV6_EXT_MAX_CHAIN 6
|
||||
#endif
|
||||
|
||||
#define VLAN_VID_MASK 0x0fff /* VLAN Identifier */
|
||||
/* Struct for collecting VLANs after parsing via parse_ethhdr_vlan */
|
||||
struct collect_vlans {
|
||||
__u16 id[VLAN_MAX_DEPTH];
|
||||
};
|
||||
|
||||
static __always_inline int proto_is_vlan(__u16 h_proto)
|
||||
{
|
||||
return !!(h_proto == bpf_htons(ETH_P_8021Q) ||
|
||||
h_proto == bpf_htons(ETH_P_8021AD));
|
||||
}
|
||||
|
||||
/* Notice, parse_ethhdr() will skip VLAN tags, by advancing nh->pos and returns
|
||||
* next header EtherType, BUT the ethhdr pointer supplied still points to the
|
||||
* Ethernet header. Thus, caller can look at eth->h_proto to see if this was a
|
||||
* VLAN tagged packet.
|
||||
*/
|
||||
static __always_inline int parse_ethhdr_vlan(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct ethhdr **ethhdr,
|
||||
struct collect_vlans *vlans)
|
||||
{
|
||||
struct ethhdr *eth = nh->pos;
|
||||
int hdrsize = sizeof(*eth);
|
||||
struct vlan_hdr *vlh;
|
||||
__u16 h_proto;
|
||||
int i;
|
||||
|
||||
/* Byte-count bounds check; check if current pointer + size of header
|
||||
* is after data_end.
|
||||
*/
|
||||
if (nh->pos + hdrsize > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos += hdrsize;
|
||||
*ethhdr = eth;
|
||||
vlh = nh->pos;
|
||||
h_proto = eth->h_proto;
|
||||
|
||||
/* Use loop unrolling to avoid the verifier restriction on loops;
|
||||
* support up to VLAN_MAX_DEPTH layers of VLAN encapsulation.
|
||||
*/
|
||||
#pragma unroll
|
||||
for (i = 0; i < VLAN_MAX_DEPTH; i++) {
|
||||
if (!proto_is_vlan(h_proto))
|
||||
break;
|
||||
|
||||
if (vlh + 1 > data_end)
|
||||
break;
|
||||
|
||||
h_proto = vlh->h_vlan_encapsulated_proto;
|
||||
if (vlans) /* collect VLAN ids */
|
||||
vlans->id[i] =
|
||||
(bpf_ntohs(vlh->h_vlan_TCI) & VLAN_VID_MASK);
|
||||
|
||||
vlh++;
|
||||
}
|
||||
|
||||
nh->pos = vlh;
|
||||
return h_proto; /* network-byte-order */
|
||||
}
|
||||
|
||||
static __always_inline int parse_ethhdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct ethhdr **ethhdr)
|
||||
{
|
||||
/* Expect compiler removes the code that collects VLAN ids */
|
||||
return parse_ethhdr_vlan(nh, data_end, ethhdr, NULL);
|
||||
}
|
||||
|
||||
static __always_inline int skip_ip6hdrext(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
__u8 next_hdr_type)
|
||||
{
|
||||
for (int i = 0; i < IPV6_EXT_MAX_CHAIN; ++i) {
|
||||
struct ipv6_opt_hdr *hdr = nh->pos;
|
||||
|
||||
if (hdr + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
switch (next_hdr_type) {
|
||||
case IPPROTO_HOPOPTS:
|
||||
case IPPROTO_DSTOPTS:
|
||||
case IPPROTO_ROUTING:
|
||||
case IPPROTO_MH:
|
||||
nh->pos = (char *)hdr + (hdr->hdrlen + 1) * 8;
|
||||
next_hdr_type = hdr->nexthdr;
|
||||
break;
|
||||
case IPPROTO_AH:
|
||||
nh->pos = (char *)hdr + (hdr->hdrlen + 2) * 4;
|
||||
next_hdr_type = hdr->nexthdr;
|
||||
break;
|
||||
case IPPROTO_FRAGMENT:
|
||||
nh->pos = (char *)hdr + 8;
|
||||
next_hdr_type = hdr->nexthdr;
|
||||
break;
|
||||
default:
|
||||
/* Found a header that is not an IPv6 extension header */
|
||||
return next_hdr_type;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static __always_inline int parse_ip6hdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct ipv6hdr **ip6hdr)
|
||||
{
|
||||
struct ipv6hdr *ip6h = nh->pos;
|
||||
|
||||
/* Pointer-arithmetic bounds check; pointer +1 points to after end of
|
||||
* thing being pointed to. We will be using this style in the remainder
|
||||
* of the tutorial.
|
||||
*/
|
||||
if (ip6h + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
if (ip6h->version != 6)
|
||||
return -1;
|
||||
|
||||
nh->pos = ip6h + 1;
|
||||
*ip6hdr = ip6h;
|
||||
|
||||
return skip_ip6hdrext(nh, data_end, ip6h->nexthdr);
|
||||
}
|
||||
|
||||
static __always_inline int parse_iphdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct iphdr **iphdr)
|
||||
{
|
||||
struct iphdr *iph = nh->pos;
|
||||
int hdrsize;
|
||||
|
||||
if (iph + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
if (iph->version != 4)
|
||||
return -1;
|
||||
|
||||
hdrsize = iph->ihl * 4;
|
||||
/* Sanity check packet field is valid */
|
||||
if(hdrsize < sizeof(*iph))
|
||||
return -1;
|
||||
|
||||
/* Variable-length IPv4 header, need to use byte-based arithmetic */
|
||||
if (nh->pos + hdrsize > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos += hdrsize;
|
||||
*iphdr = iph;
|
||||
|
||||
return iph->protocol;
|
||||
}
|
||||
|
||||
static __always_inline int parse_icmp6hdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct icmp6hdr **icmp6hdr)
|
||||
{
|
||||
struct icmp6hdr *icmp6h = nh->pos;
|
||||
|
||||
if (icmp6h + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos = icmp6h + 1;
|
||||
*icmp6hdr = icmp6h;
|
||||
|
||||
return icmp6h->icmp6_type;
|
||||
}
|
||||
|
||||
static __always_inline int parse_icmphdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct icmphdr **icmphdr)
|
||||
{
|
||||
struct icmphdr *icmph = nh->pos;
|
||||
|
||||
if (icmph + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos = icmph + 1;
|
||||
*icmphdr = icmph;
|
||||
|
||||
return icmph->type;
|
||||
}
|
||||
|
||||
static __always_inline int parse_icmphdr_common(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct icmphdr_common **icmphdr)
|
||||
{
|
||||
struct icmphdr_common *h = nh->pos;
|
||||
|
||||
if (h + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos = h + 1;
|
||||
*icmphdr = h;
|
||||
|
||||
return h->type;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse_udphdr: parse the udp header and return the length of the udp payload
|
||||
*/
|
||||
static __always_inline int parse_udphdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct udphdr **udphdr)
|
||||
{
|
||||
int len;
|
||||
struct udphdr *h = nh->pos;
|
||||
|
||||
if (h + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos = h + 1;
|
||||
*udphdr = h;
|
||||
|
||||
len = bpf_ntohs(h->len) - sizeof(struct udphdr);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse_tcphdr: parse and return the length of the tcp header
|
||||
*/
|
||||
static __always_inline int parse_tcphdr(struct hdr_cursor *nh,
|
||||
void *data_end,
|
||||
struct tcphdr **tcphdr)
|
||||
{
|
||||
int len;
|
||||
struct tcphdr *h = nh->pos;
|
||||
|
||||
if (h + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
len = h->doff * 4;
|
||||
/* Sanity check packet field is valid */
|
||||
if(len < sizeof(*h))
|
||||
return -1;
|
||||
|
||||
/* Variable-length TCP header, need to use byte-based arithmetic */
|
||||
if (nh->pos + len > data_end)
|
||||
return -1;
|
||||
|
||||
nh->pos += len;
|
||||
*tcphdr = h;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#endif /* __PARSING_HELPERS_H */
|
Reference in New Issue
Block a user