mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-02-03 10:14:44 +08:00
42-xdp-loadbalancer: fix various bugs which prevented the example from working (#175)
* added stub XDP_PASS for receiving veth * added tcp csum to prevent packet dropping * sticky backend select (hash over connection tuple) --------- Co-authored-by: Benibr <1274814+benibr@users.noreply.github.com>
This commit is contained in:
@@ -445,8 +445,11 @@ sudo ./teardown.sh
|
|||||||
### Running the Load Balancer
|
### Running the Load Balancer
|
||||||
|
|
||||||
To run the XDP load balancer, execute the following command, specifying the interface and backends' IP and MAC addresses:
|
To run the XDP load balancer, execute the following command, specifying the interface and backends' IP and MAC addresses:
|
||||||
|
To make XDP work with veth interfaces we need to add a stub eBPF program to the other end of the pair.
|
||||||
|
|
||||||
```console
|
```console
|
||||||
|
clang -target bpf -o stub.bpf.o -c stub.bpf.c
|
||||||
|
sudo ip l set dev veth7 xdp obj ./stub.bpf.o sec .xdp # stub XDP_PASS on the receiving interface
|
||||||
sudo ip netns exec lb ./xdp_lb veth6 10.0.0.2 de:ad:be:ef:00:02 10.0.0.3 de:ad:be:ef:00:03
|
sudo ip netns exec lb ./xdp_lb veth6 10.0.0.2 de:ad:be:ef:00:02 10.0.0.3 de:ad:be:ef:00:03
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
7
src/42-xdp-loadbalancer/stub.bpf.c
Normal file
7
src/42-xdp-loadbalancer/stub.bpf.c
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#include <linux/bpf.h>
|
||||||
|
#include <bpf/bpf_helpers.h>
|
||||||
|
|
||||||
|
SEC(".xdp")
|
||||||
|
int main () {
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@
|
|||||||
#include <linux/tcp.h>
|
#include <linux/tcp.h>
|
||||||
#include "xx_hash.h"
|
#include "xx_hash.h"
|
||||||
|
|
||||||
|
#define MAX_TCP_CHECK_WORDS 750 // max 1500 bytes to check in TCP checksum. This is MTU dependent
|
||||||
|
|
||||||
struct backend_config {
|
struct backend_config {
|
||||||
__u32 ip;
|
__u32 ip;
|
||||||
unsigned char mac[ETH_ALEN];
|
unsigned char mac[ETH_ALEN];
|
||||||
@@ -38,6 +40,35 @@ csum_fold_helper(__u64 csum)
|
|||||||
return ~csum;
|
return ~csum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __always_inline __u16
|
||||||
|
tcph_csum(struct tcphdr *tcph, struct iphdr *iph, void *data_end)
|
||||||
|
{
|
||||||
|
// Clear checksum
|
||||||
|
tcph->check = 0;
|
||||||
|
|
||||||
|
// Pseudo header checksum calculation
|
||||||
|
__u32 sum = 0;
|
||||||
|
sum += (__u16)(iph->saddr >> 16) + (__u16)(iph->saddr & 0xFFFF);
|
||||||
|
sum += (__u16)(iph->daddr >> 16) + (__u16)(iph->daddr & 0xFFFF);
|
||||||
|
sum += __constant_htons(IPPROTO_TCP);
|
||||||
|
sum += __constant_htons((__u16)(data_end - (void *)tcph));
|
||||||
|
|
||||||
|
// TCP header and payload checksum
|
||||||
|
#pragma clang loop unroll_count(MAX_TCP_CHECK_WORDS)
|
||||||
|
for (int i = 0; i <= MAX_TCP_CHECK_WORDS; i++) {
|
||||||
|
__u16 *ptr = (__u16 *)tcph + i;
|
||||||
|
if ((void *)ptr + 2 > data_end)
|
||||||
|
break;
|
||||||
|
sum += *(__u16 *)ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fold into 16 bit
|
||||||
|
while (sum >> 16)
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
|
||||||
|
return ~sum;
|
||||||
|
}
|
||||||
|
|
||||||
static __always_inline __u16
|
static __always_inline __u16
|
||||||
iph_csum(struct iphdr *iph)
|
iph_csum(struct iphdr *iph)
|
||||||
{
|
{
|
||||||
@@ -70,6 +101,11 @@ int xdp_load_balancer(struct xdp_md *ctx) {
|
|||||||
// Check if the protocol is TCP or UDP
|
// Check if the protocol is TCP or UDP
|
||||||
if (iph->protocol != IPPROTO_TCP)
|
if (iph->protocol != IPPROTO_TCP)
|
||||||
return XDP_PASS;
|
return XDP_PASS;
|
||||||
|
|
||||||
|
// TCP header
|
||||||
|
struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
|
||||||
|
if ((void *)tcph + sizeof(*tcph) > data_end)
|
||||||
|
return XDP_PASS;
|
||||||
|
|
||||||
bpf_printk("Received Source IP: 0x%x", bpf_ntohl(iph->saddr));
|
bpf_printk("Received Source IP: 0x%x", bpf_ntohl(iph->saddr));
|
||||||
bpf_printk("Received Destination IP: 0x%x", bpf_ntohl(iph->daddr));
|
bpf_printk("Received Destination IP: 0x%x", bpf_ntohl(iph->daddr));
|
||||||
@@ -80,7 +116,19 @@ int xdp_load_balancer(struct xdp_md *ctx) {
|
|||||||
{
|
{
|
||||||
bpf_printk("Packet from client");
|
bpf_printk("Packet from client");
|
||||||
|
|
||||||
__u32 key = xxhash32((const char*)iph, sizeof(struct iphdr), 0) % 2;
|
struct {
|
||||||
|
__u32 src_ip;
|
||||||
|
__u32 dst_ip;
|
||||||
|
__u16 src_port;
|
||||||
|
__u16 dst_port;
|
||||||
|
} four_tuple = {iph->saddr,
|
||||||
|
iph->daddr,
|
||||||
|
bpf_ntohs(tcph->source),
|
||||||
|
bpf_ntohs(tcph->dest)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hash the 4-tuple for flow based backend decision
|
||||||
|
__u32 key = xxhash32((const char *)&four_tuple, sizeof(four_tuple), 0) % 2;
|
||||||
|
|
||||||
struct backend_config *backend = bpf_map_lookup_elem(&backends, &key);
|
struct backend_config *backend = bpf_map_lookup_elem(&backends, &key);
|
||||||
if (!backend)
|
if (!backend)
|
||||||
@@ -102,7 +150,10 @@ int xdp_load_balancer(struct xdp_md *ctx) {
|
|||||||
__builtin_memcpy(eth->h_source, load_balancer_mac, ETH_ALEN);
|
__builtin_memcpy(eth->h_source, load_balancer_mac, ETH_ALEN);
|
||||||
|
|
||||||
// Recalculate IP checksum
|
// Recalculate IP checksum
|
||||||
iph->check = iph_csum(iph);
|
iph->check = iph_csum(iph);
|
||||||
|
|
||||||
|
// Recalculate TCP checksum
|
||||||
|
tcph->check = tcph_csum(tcph, iph, data_end);
|
||||||
|
|
||||||
bpf_printk("Redirecting packet to new IP 0x%x from IP 0x%x",
|
bpf_printk("Redirecting packet to new IP 0x%x from IP 0x%x",
|
||||||
bpf_ntohl(iph->daddr),
|
bpf_ntohl(iph->daddr),
|
||||||
|
|||||||
Reference in New Issue
Block a user