1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Check if we can migrate child sockets.
4 *
5 * 1. If reuse_md->migrating_sk is NULL (SYN packet),
6 * return SK_PASS without selecting a listener.
7 * 2. If reuse_md->migrating_sk is not NULL (socket migration),
8 * select a listener (reuseport_map[migrate_map[cookie]])
9 *
10 * Author: Kuniyuki Iwashima <kuniyu@amazon.co.jp>
11 */
12
13#include <stddef.h>
14#include <string.h>
15#include <linux/bpf.h>
16#include <linux/if_ether.h>
17#include <linux/ip.h>
18#include <linux/ipv6.h>
19#include <linux/tcp.h>
20#include <linux/in.h>
21#include <bpf/bpf_endian.h>
22#include <bpf/bpf_helpers.h>
23
24struct {
25 __uint(type, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY);
26 __uint(max_entries, 256);
27 __type(key, int);
28 __type(value, __u64);
29} reuseport_map SEC(".maps");
30
31struct {
32 __uint(type, BPF_MAP_TYPE_HASH);
33 __uint(max_entries, 256);
34 __type(key, __u64);
35 __type(value, int);
36} migrate_map SEC(".maps");
37
38int migrated_at_close = 0;
39int migrated_at_close_fastopen = 0;
40int migrated_at_send_synack = 0;
41int migrated_at_recv_ack = 0;
42__be16 server_port;
43
44SEC("xdp")
45int drop_ack(struct xdp_md *xdp)
46{
47 void *data_end = (void *)(long)xdp->data_end;
48 void *data = (void *)(long)xdp->data;
49 struct ethhdr *eth = data;
50 struct tcphdr *tcp = NULL;
51
52 if (eth + 1 > data_end)
53 goto pass;
54
55 switch (bpf_ntohs(eth->h_proto)) {
56 case ETH_P_IP: {
57 struct iphdr *ip = (struct iphdr *)(eth + 1);
58
59 if (ip + 1 > data_end)
60 goto pass;
61
62 if (ip->protocol != IPPROTO_TCP)
63 goto pass;
64
65 tcp = (struct tcphdr *)((void *)ip + ip->ihl * 4);
66 break;
67 }
68 case ETH_P_IPV6: {
69 struct ipv6hdr *ipv6 = (struct ipv6hdr *)(eth + 1);
70
71 if (ipv6 + 1 > data_end)
72 goto pass;
73
74 if (ipv6->nexthdr != IPPROTO_TCP)
75 goto pass;
76
77 tcp = (struct tcphdr *)(ipv6 + 1);
78 break;
79 }
80 default:
81 goto pass;
82 }
83
84 if (tcp + 1 > data_end)
85 goto pass;
86
87 if (tcp->dest != server_port)
88 goto pass;
89
90 if (!tcp->syn && tcp->ack)
91 return XDP_DROP;
92
93pass:
94 return XDP_PASS;
95}
96
97SEC("sk_reuseport/migrate")
98int migrate_reuseport(struct sk_reuseport_md *reuse_md)
99{
100 int *key, flags = 0, state, err;
101 __u64 cookie;
102
103 if (!reuse_md->migrating_sk)
104 return SK_PASS;
105
106 state = reuse_md->migrating_sk->state;
107 cookie = bpf_get_socket_cookie(reuse_md->sk);
108
109 key = bpf_map_lookup_elem(&migrate_map, &cookie);
110 if (!key)
111 return SK_DROP;
112
113 err = bpf_sk_select_reuseport(reuse_md, &reuseport_map, key, flags);
114 if (err)
115 return SK_PASS;
116
117 switch (state) {
118 case BPF_TCP_ESTABLISHED:
119 __sync_fetch_and_add(&migrated_at_close, 1);
120 break;
121 case BPF_TCP_SYN_RECV:
122 __sync_fetch_and_add(&migrated_at_close_fastopen, 1);
123 break;
124 case BPF_TCP_NEW_SYN_RECV:
125 if (!reuse_md->len)
126 __sync_fetch_and_add(&migrated_at_send_synack, 1);
127 else
128 __sync_fetch_and_add(&migrated_at_recv_ack, 1);
129 break;
130 }
131
132 return SK_PASS;
133}
134
135char _license[] SEC("license") = "GPL";
136

source code of linux/tools/testing/selftests/bpf/progs/test_migrate_reuseport.c