github.com/datadog/cilium@v1.6.12/bpf/bpf_lb.c (about)

     1  /*
     2   *  Copyright (C) 2016-2019 Authors of Cilium
     3   *
     4   *  This program is free software; you can redistribute it and/or modify
     5   *  it under the terms of the GNU General Public License as published by
     6   *  the Free Software Foundation; either version 2 of the License, or
     7   *  (at your option) any later version.
     8   *
     9   *  This program is distributed in the hope that it will be useful,
    10   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   *  GNU General Public License for more details.
    13   *
    14   *  You should have received a copy of the GNU General Public License
    15   *  along with this program; if not, write to the Free Software
    16   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    17   */
    18  
    19  /**
    20   * Description: Standalone loadbalancer that can be attached to any
    21   *              net_device. Will perform a map lookup on the destination
    22   *              IP and optional destination port for every IPv4 and
    23   *              IPv6 packet received. If a matching entry is found, the
    24   *              destination address will be written to one of the
    25   *              configures slaves. Optionally the destination port can be
    26   *              mapped to a slave specific port as well. The packet is
    27   *              then passed back to the stack.
    28   *
    29   * Configuration:
    30   *  - LB_REDIRECT     - Redirect to an ifindex
    31   *  - LB_L4           - Enable L4 matching and mapping
    32   */
    33  
    34  #define DISABLE_LOOPBACK_LB
    35  
    36  #include <node_config.h>
    37  #include <netdev_config.h>
    38  
    39  #include <bpf/api.h>
    40  
    41  #include <stdint.h>
    42  #include <stdio.h>
    43  
    44  #include "lib/utils.h"
    45  #include "lib/common.h"
    46  #include "lib/maps.h"
    47  #include "lib/ipv6.h"
    48  #include "lib/ipv4.h"
    49  #include "lib/l4.h"
    50  #include "lib/eth.h"
    51  #include "lib/dbg.h"
    52  #include "lib/drop.h"
    53  #include "lib/lb.h"
    54  
    55  #ifdef ENABLE_IPV6
    56  static inline int handle_ipv6(struct __sk_buff *skb)
    57  {
    58  	void *data, *data_end;
    59  	struct lb6_key_v2 key = {};
    60  	struct lb6_service_v2 *svc;
    61  	struct lb6_backend *backend;
    62  	struct ipv6hdr *ip6;
    63  	struct csum_offset csum_off = {};
    64  	int l3_off, l4_off, ret, hdrlen;
    65  	union v6addr new_dst;
    66  	__u8 nexthdr;
    67  	__u16 slave;
    68  
    69  	if (!revalidate_data(skb, &data, &data_end, &ip6))
    70  		return DROP_INVALID;
    71  
    72  	cilium_dbg_capture(skb, DBG_CAPTURE_FROM_LB, skb->ingress_ifindex);
    73  
    74  	nexthdr = ip6->nexthdr;
    75  	ipv6_addr_copy(&key.address, (union v6addr *) &ip6->daddr);
    76  	l3_off = ETH_HLEN;
    77  	hdrlen = ipv6_hdrlen(skb, ETH_HLEN, &nexthdr);
    78  	if (hdrlen < 0)
    79  		return hdrlen;
    80  
    81  	l4_off = ETH_HLEN + hdrlen;
    82  	csum_l4_offset_and_flags(nexthdr, &csum_off);
    83  
    84  #ifdef LB_L4
    85  	ret = extract_l4_port(skb, nexthdr, l4_off, &key.dport);
    86  	if (IS_ERR(ret)) {
    87  		if (ret == DROP_UNKNOWN_L4) {
    88  			/* Pass unknown L4 to stack */
    89  			return TC_ACT_OK;
    90  		} else
    91  			return ret;
    92  	}
    93  #endif
    94  
    95  	svc = lb6_lookup_service_v2(skb, &key);
    96  	if (svc == NULL) {
    97  		/* Pass packets to the stack which should not be loadbalanced */
    98  		return TC_ACT_OK;
    99  	}
   100  
   101  	slave = lb6_select_slave(svc->count);
   102  	if (!(svc = lb6_lookup_slave_v2(skb, &key, slave)))
   103  		return DROP_NO_SERVICE;
   104  	if (!(backend = lb6_lookup_backend(skb, svc->backend_id)))
   105  		return DROP_NO_SERVICE;
   106  
   107  	ipv6_addr_copy(&new_dst, &backend->address);
   108  	if (svc->rev_nat_index)
   109  		new_dst.p4 |= svc->rev_nat_index;
   110  
   111  	ret = lb6_xlate_v2(skb, &new_dst, nexthdr, l3_off, l4_off, &csum_off, &key,
   112  						svc, backend);
   113  	if (IS_ERR(ret))
   114  		return ret;
   115  
   116  	return TC_ACT_REDIRECT;
   117  }
   118  #endif /* ENABLE_IPV6 */
   119  
   120  #ifdef ENABLE_IPV4
   121  static inline int handle_ipv4(struct __sk_buff *skb)
   122  {
   123  	void *data;
   124  	void *data_end;
   125  	struct lb4_key_v2 key = {};
   126  	struct lb4_service_v2 *svc;
   127  	struct lb4_backend *backend;
   128  	struct iphdr *ip;
   129  	struct csum_offset csum_off = {};
   130  	int l3_off, l4_off, ret;
   131  	__be32 new_dst;
   132  	__u8 nexthdr;
   133  	__u16 slave;
   134  
   135  	if (!revalidate_data(skb, &data, &data_end, &ip))
   136  		return DROP_INVALID;
   137  
   138  	cilium_dbg_capture(skb, DBG_CAPTURE_FROM_LB, skb->ingress_ifindex);
   139  
   140  	nexthdr = ip->protocol;
   141  	key.address = ip->daddr;
   142  	l3_off = ETH_HLEN;
   143  	l4_off = ETH_HLEN + ipv4_hdrlen(ip);
   144  	csum_l4_offset_and_flags(nexthdr, &csum_off);
   145  
   146  #ifdef LB_L4
   147  	ret = extract_l4_port(skb, nexthdr, l4_off, &key.dport);
   148  	if (IS_ERR(ret)) {
   149  		if (ret == DROP_UNKNOWN_L4) {
   150  			/* Pass unknown L4 to stack */
   151  			return TC_ACT_OK;
   152  		} else
   153  			return ret;
   154  	}
   155  #endif
   156  
   157  	svc = lb4_lookup_service_v2(skb, &key);
   158  	if (svc == NULL) {
   159  		/* Pass packets to the stack which should not be loadbalanced */
   160  		return TC_ACT_OK;
   161  	}
   162  
   163  	slave = lb4_select_slave(svc->count);
   164  	if (!(svc = lb4_lookup_slave_v2(skb, &key, slave)))
   165  		return DROP_NO_SERVICE;
   166  	if (!(backend = lb4_lookup_backend(skb, svc->backend_id)))
   167  		return DROP_NO_SERVICE;
   168  
   169  	new_dst = backend->address;
   170  	ret = lb4_xlate_v2(skb, &new_dst, NULL, NULL, nexthdr, l3_off, l4_off,
   171  						&csum_off, &key, svc, backend);
   172  	if (IS_ERR(ret))
   173  		return ret;
   174  
   175  	return TC_ACT_REDIRECT;
   176  }
   177  #endif /* ENABLE_IPV4 */
   178  
   179  __section("from-netdev")
   180  int from_netdev(struct __sk_buff *skb)
   181  {
   182  	__u16 proto;
   183  	int ret;
   184  
   185  	bpf_clear_cb(skb);
   186  
   187  	if (!validate_ethertype(skb, &proto))
   188  		/* Pass unknown traffic to the stack */
   189  		return TC_ACT_OK;
   190  
   191  	switch (proto) {
   192  #ifdef ENABLE_IPV6
   193  	case bpf_htons(ETH_P_IPV6):
   194  		ret = handle_ipv6(skb);
   195  		break;
   196  #endif
   197  
   198  #ifdef ENABLE_IPV4
   199  	case bpf_htons(ETH_P_IP):
   200  		ret = handle_ipv4(skb);
   201  		break;
   202  #endif
   203  
   204  	default:
   205  		/* Pass unknown traffic to the stack */
   206  		return TC_ACT_OK;
   207  	}
   208  
   209  	if (IS_ERR(ret))
   210  		return send_drop_notify_error(skb, 0, ret, TC_ACT_SHOT, METRIC_INGRESS);
   211  
   212  #ifdef LB_REDIRECT
   213  	if (ret == TC_ACT_REDIRECT) {
   214  		int ifindex = LB_REDIRECT;
   215  #ifdef LB_DSTMAC
   216  		union macaddr mac = LB_DSTMAC;
   217  
   218  		if (eth_store_daddr(skb, (__u8 *) &mac.addr, 0) < 0)
   219  			ret = DROP_WRITE_ERROR;
   220  #endif
   221  		cilium_dbg_capture(skb, DBG_CAPTURE_DELIVERY, ifindex);
   222  		return redirect(ifindex, 0);
   223  	}
   224  #endif
   225  	cilium_dbg_capture(skb, DBG_CAPTURE_DELIVERY, 0);
   226  	return TC_ACT_OK;
   227  }
   228  
   229  BPF_LICENSE("GPL");