https://github.com/cilium/cilium
Revision 85da38b0e126f44c355577282d7dba9a9bdbf19c authored by Michal Rostecki on 13 December 2018, 15:28:14 UTC, committed by Ian Vernon on 29 January 2019, 16:49:19 UTC
[ upstream commit 1e91684f22474b44ced8cd9ed8fc72aa1a86e05b ]

Signed-off-by: Michal Rostecki <mrostecki@suse.de>
Signed-off-by: Joe Stringer <joe@cilium.io>
1 parent 37ca613
Raw File
Tip revision: 85da38b0e126f44c355577282d7dba9a9bdbf19c authored by Michal Rostecki on 13 December 2018, 15:28:14 UTC
idpool: Use checker.DeepEquals in unit tests
Tip revision: 85da38b
bpf_lb.c
/*
 *  Copyright (C) 2016-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
 */

/**
 * Description: Standalone loadbalancer that can be attached to any
 *              net_device. Will perform a map lookup on the destination
 *              IP and optional destination port for every IPv4 and
 *              IPv6 packet received. If a matching entry is found, the
 *              destination address will be written to one of the
 *              configures slaves. Optionally the destination port can be
 *              mapped to a slave specific port as well. The packet is
 *              then passed back to the stack.
 *
 * Configuration:
 *  - LB_REDIRECT     - Redirect to an ifindex
 *  - LB_L4           - Enable L4 matching and mapping
 */

#define DISABLE_LOOPBACK_LB

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

#include <bpf/api.h>

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

#include "lib/utils.h"
#include "lib/common.h"
#include "lib/maps.h"
#include "lib/ipv6.h"
#include "lib/ipv4.h"
#include "lib/l4.h"
#include "lib/eth.h"
#include "lib/dbg.h"
#include "lib/drop.h"
#include "lib/lb.h"

#ifdef ENABLE_IPV6
static inline int handle_ipv6(struct __sk_buff *skb)
{
	void *data, *data_end;
	struct lb6_key key = {};
	struct lb6_service *svc;
	struct ipv6hdr *ip6;
	struct csum_offset csum_off = {};
	int l3_off, l4_off, ret, hdrlen;
	union v6addr new_dst;
	__u8 nexthdr;
	__u16 slave;

	if (!revalidate_data(skb, &data, &data_end, &ip6))
		return DROP_INVALID;

	cilium_dbg_capture(skb, DBG_CAPTURE_FROM_LB, skb->ingress_ifindex);

	nexthdr = ip6->nexthdr;
	ipv6_addr_copy(&key.address, (union v6addr *) &ip6->daddr);
	l3_off = ETH_HLEN;
	hdrlen = ipv6_hdrlen(skb, ETH_HLEN, &nexthdr);
	if (hdrlen < 0)
		return hdrlen;

	l4_off = ETH_HLEN + hdrlen;
	csum_l4_offset_and_flags(nexthdr, &csum_off);

#ifdef LB_L4
	ret = extract_l4_port(skb, nexthdr, l4_off, &key.dport);
	if (IS_ERR(ret)) {
		if (ret == DROP_UNKNOWN_L4) {
			/* Pass unknown L4 to stack */
			return TC_ACT_OK;
		} else
			return ret;
	}
#endif

	svc = lb6_lookup_service(skb, &key);
	if (svc == NULL) {
		/* Pass packets to the stack which should not be loadbalanced */
		return TC_ACT_OK;
	}

	slave = lb6_select_slave(skb, &key, svc->count, svc->weight);
	if (!(svc = lb6_lookup_slave(skb, &key, slave)))
		return DROP_NO_SERVICE;

	ipv6_addr_copy(&new_dst, &svc->target);
	if (svc->rev_nat_index)
		new_dst.p4 |= svc->rev_nat_index;

	ret = lb6_xlate(skb, &new_dst, nexthdr, l3_off, l4_off, &csum_off, &key, svc);
	if (IS_ERR(ret))
		return ret;

	return TC_ACT_REDIRECT;
}
#endif /* ENABLE_IPV6 */

#ifdef ENABLE_IPV4
static inline int handle_ipv4(struct __sk_buff *skb)
{
	void *data;
	void *data_end;
	struct lb4_key key = {};
	struct lb4_service *svc;
	struct iphdr *ip;
	struct csum_offset csum_off = {};
	int l3_off, l4_off, ret;
	__be32 new_dst;
	__u8 nexthdr;
	__u16 slave;

	if (!revalidate_data(skb, &data, &data_end, &ip))
		return DROP_INVALID;

	cilium_dbg_capture(skb, DBG_CAPTURE_FROM_LB, skb->ingress_ifindex);

	nexthdr = ip->protocol;
	key.address = ip->daddr;
	l3_off = ETH_HLEN;
	l4_off = ETH_HLEN + ipv4_hdrlen(ip);
	csum_l4_offset_and_flags(nexthdr, &csum_off);

#ifdef LB_L4
	ret = extract_l4_port(skb, nexthdr, l4_off, &key.dport);
	if (IS_ERR(ret)) {
		if (ret == DROP_UNKNOWN_L4) {
			/* Pass unknown L4 to stack */
			return TC_ACT_OK;
		} else
			return ret;
	}
#endif

	svc = lb4_lookup_service(skb, &key);
	if (svc == NULL) {
		/* Pass packets to the stack which should not be loadbalanced */
		return TC_ACT_OK;
	}

	slave = lb4_select_slave(skb, &key, svc->count, svc->weight);
	if (!(svc = lb4_lookup_slave(skb, &key, slave)))
		return DROP_NO_SERVICE;

	new_dst = svc->target;
	ret = lb4_xlate(skb, &new_dst, NULL, NULL, nexthdr, l3_off, l4_off, &csum_off, &key, svc);
	if (IS_ERR(ret))
		return ret;

	return TC_ACT_REDIRECT;
}
#endif /* ENABLE_IPV4 */

__section("from-netdev")
int from_netdev(struct __sk_buff *skb)
{
	int ret;

	bpf_clear_cb(skb);

	switch (skb->protocol) {
#ifdef ENABLE_IPV6
	case bpf_htons(ETH_P_IPV6):
		ret = handle_ipv6(skb);
		break;
#endif

#ifdef ENABLE_IPV4
	case bpf_htons(ETH_P_IP):
		ret = handle_ipv4(skb);
		break;
#endif

	default:
		/* Pass unknown traffic to the stack */
		return TC_ACT_OK;
	}

	if (IS_ERR(ret))
		return send_drop_notify_error(skb, ret, TC_ACT_SHOT, METRIC_INGRESS);

#ifdef LB_REDIRECT
	if (ret == TC_ACT_REDIRECT) {
		int ifindex = LB_REDIRECT;
#ifdef LB_DSTMAC
		union macaddr mac = LB_DSTMAC;

		if (eth_store_daddr(skb, (__u8 *) &mac.addr, 0) < 0)
			ret = DROP_WRITE_ERROR;
#endif
		cilium_dbg_capture(skb, DBG_CAPTURE_DELIVERY, ifindex);
		return redirect(ifindex, 0);
	}
#endif
	cilium_dbg_capture(skb, DBG_CAPTURE_DELIVERY, 0);
	return TC_ACT_OK;
}

BPF_LICENSE("GPL");
back to top