https://github.com/cilium/cilium
Raw File
Tip revision: 2aab10ef515c3e614830369e926d9827d5cc82d5 authored by John Fastabend on 02 December 2018, 06:13:18 UTC
cilium: add ktls support
Tip revision: 2aab10e
bpf_xdp.c
/*
 *  Copyright (C) 2017 Authors of Cilium
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
#define SKIP_CALLS_MAP

#include <node_config.h>
#include <netdev_config.h>
#include <filter_config.h>

#include <bpf/api.h>

#include <stdint.h>
#include <stdio.h>

#include <linux/bpf.h>
#include <linux/if_ether.h>

#include "lib/utils.h"
#include "lib/common.h"
#include "lib/maps.h"
#include "lib/xdp.h"
#include "lib/eps.h"
#include "lib/events.h"

#ifndef HAVE_LPM_MAP_TYPE
# undef CIDR4_LPM_PREFILTER
# undef CIDR6_LPM_PREFILTER
#endif

#ifdef CIDR4_FILTER
struct bpf_elf_map __section_maps CIDR4_HMAP_NAME = {
	.type		= BPF_MAP_TYPE_HASH,
	.size_key	= sizeof(struct lpm_v4_key),
	.size_value	= sizeof(struct lpm_val),
	.flags		= BPF_F_NO_PREALLOC,
	.pinning	= PIN_GLOBAL_NS,
	.max_elem	= CIDR4_HMAP_ELEMS,
};

#ifdef CIDR4_LPM_PREFILTER
struct bpf_elf_map __section_maps CIDR4_LMAP_NAME = {
	.type		= BPF_MAP_TYPE_LPM_TRIE,
	.size_key	= sizeof(struct lpm_v4_key),
	.size_value	= sizeof(struct lpm_val),
	.flags		= BPF_F_NO_PREALLOC,
	.pinning	= PIN_GLOBAL_NS,
	.max_elem	= CIDR4_LMAP_ELEMS,
};
#endif /* CIDR4_LPM_PREFILTER */
#endif /* CIDR4_FILTER */

#ifdef CIDR6_FILTER
struct bpf_elf_map __section_maps CIDR6_HMAP_NAME = {
	.type		= BPF_MAP_TYPE_HASH,
	.size_key	= sizeof(struct lpm_v6_key),
	.size_value	= sizeof(struct lpm_val),
	.flags		= BPF_F_NO_PREALLOC,
	.pinning	= PIN_GLOBAL_NS,
	.max_elem	= CIDR4_HMAP_ELEMS,
};

#ifdef CIDR6_LPM_PREFILTER
struct bpf_elf_map __section_maps CIDR6_LMAP_NAME = {
	.type		= BPF_MAP_TYPE_LPM_TRIE,
	.size_key	= sizeof(struct lpm_v6_key),
	.size_value	= sizeof(struct lpm_val),
	.flags		= BPF_F_NO_PREALLOC,
	.pinning	= PIN_GLOBAL_NS,
	.max_elem	= CIDR4_LMAP_ELEMS,
};
#endif /* CIDR6_LPM_PREFILTER */
#endif /* CIDR6_FILTER */

static __always_inline int check_v4_endpoint(struct xdp_md *xdp,
					     struct iphdr *ipv4_hdr)
{
	if (lookup_ip4_endpoint(ipv4_hdr))
		return XDP_PASS;

	return XDP_DROP;
}

static __always_inline int check_v4(struct xdp_md *xdp)
{
	void *data_end = xdp_data_end(xdp);
	void *data = xdp_data(xdp);
	struct iphdr *ipv4_hdr = data + sizeof(struct ethhdr);
	struct lpm_v4_key pfx __maybe_unused;

	if (xdp_no_room(ipv4_hdr + 1, data_end))
		return XDP_DROP;

#ifdef CIDR4_FILTER
	__builtin_memcpy(pfx.lpm.data, &ipv4_hdr->saddr, sizeof(pfx.addr));
	pfx.lpm.prefixlen = 32;

#ifdef CIDR4_LPM_PREFILTER
	if (map_lookup_elem(&CIDR4_LMAP_NAME, &pfx))
		return XDP_DROP;
	else
#endif /* CIDR4_LPM_PREFILTER */
		return map_lookup_elem(&CIDR4_HMAP_NAME, &pfx) ?
		       XDP_DROP : check_v4_endpoint(xdp, ipv4_hdr);
#else
	return check_v4_endpoint(xdp, ipv4_hdr);
#endif /* CIDR4_FILTER */
}

static __always_inline int check_v6_endpoint(struct xdp_md *xdp,
					     struct ipv6hdr *ipv6_hdr)
{
	if (lookup_ip6_endpoint(ipv6_hdr))
		return XDP_PASS;

	return XDP_DROP;
}

static __always_inline int check_v6(struct xdp_md *xdp)
{
	void *data_end = xdp_data_end(xdp);
	void *data = xdp_data(xdp);
	struct ipv6hdr *ipv6_hdr = data + sizeof(struct ethhdr);
	struct lpm_v6_key pfx __maybe_unused;

	if (xdp_no_room(ipv6_hdr + 1, data_end))
		return XDP_DROP;

#ifdef CIDR6_FILTER
	__builtin_memcpy(pfx.lpm.data, &ipv6_hdr->saddr, sizeof(pfx.addr));
	pfx.lpm.prefixlen = 128;

#ifdef CIDR6_LPM_PREFILTER
	if (map_lookup_elem(&CIDR6_LMAP_NAME, &pfx))
		return XDP_DROP;
	else
#endif /* CIDR6_LPM_PREFILTER */
		return map_lookup_elem(&CIDR6_HMAP_NAME, &pfx) ?
		       XDP_DROP : check_v6_endpoint(xdp, ipv6_hdr);
#else
	return check_v6_endpoint(xdp, ipv6_hdr);
#endif /* CIDR6_FILTER */
}

static __always_inline int check_filters(struct xdp_md *xdp)
{
	void *data_end = xdp_data_end(xdp);
	void *data = xdp_data(xdp);
	struct ethhdr *eth = data;
	__u16 proto;

	if (xdp_no_room(eth + 1, data_end))
		return XDP_DROP;

	proto = eth->h_proto;
	if (proto == bpf_htons(ETH_P_IP))
		return check_v4(xdp);
	else if (proto == bpf_htons(ETH_P_IPV6))
		return check_v6(xdp);
	else
		/* Pass the rest to stack, we might later do more
		 * fine-grained filtering here.
		 */
		return XDP_PASS;
}

__section("from-netdev")
int xdp_start(struct xdp_md *xdp)
{
	return check_filters(xdp);
}

BPF_LICENSE("GPL");
back to top