github.com/rohankumardubey/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");