1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (c) 2018 Facebook 3 4 #include <string.h> 5 6 #include <linux/stddef.h> 7 #include <linux/bpf.h> 8 #include <linux/in.h> 9 #include <linux/in6.h> 10 #include <sys/socket.h> 11 #include <netinet/tcp.h> 12 13 #include <bpf/bpf_helpers.h> 14 #include <bpf/bpf_endian.h> 15 16 #define SRC_REWRITE_IP4 0x7f000004U 17 #define DST_REWRITE_IP4 0x7f000001U 18 #define DST_REWRITE_PORT4 4444 19 20 #ifndef TCP_CA_NAME_MAX 21 #define TCP_CA_NAME_MAX 16 22 #endif 23 24 int _version SEC("version") = 1; 25 26 __attribute__ ((noinline)) 27 int do_bind(struct bpf_sock_addr *ctx) 28 { 29 struct sockaddr_in sa = {}; 30 31 sa.sin_family = AF_INET; 32 sa.sin_port = bpf_htons(0); 33 sa.sin_addr.s_addr = bpf_htonl(SRC_REWRITE_IP4); 34 35 if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0) 36 return 0; 37 38 return 1; 39 } 40 41 static __inline int verify_cc(struct bpf_sock_addr *ctx, 42 char expected[TCP_CA_NAME_MAX]) 43 { 44 char buf[TCP_CA_NAME_MAX]; 45 int i; 46 47 if (bpf_getsockopt(ctx, SOL_TCP, TCP_CONGESTION, &buf, sizeof(buf))) 48 return 1; 49 50 for (i = 0; i < TCP_CA_NAME_MAX; i++) { 51 if (buf[i] != expected[i]) 52 return 1; 53 if (buf[i] == 0) 54 break; 55 } 56 57 return 0; 58 } 59 60 static __inline int set_cc(struct bpf_sock_addr *ctx) 61 { 62 char reno[TCP_CA_NAME_MAX] = "reno"; 63 char cubic[TCP_CA_NAME_MAX] = "cubic"; 64 65 if (bpf_setsockopt(ctx, SOL_TCP, TCP_CONGESTION, &reno, sizeof(reno))) 66 return 1; 67 if (verify_cc(ctx, reno)) 68 return 1; 69 70 if (bpf_setsockopt(ctx, SOL_TCP, TCP_CONGESTION, &cubic, sizeof(cubic))) 71 return 1; 72 if (verify_cc(ctx, cubic)) 73 return 1; 74 75 return 0; 76 } 77 78 SEC("cgroup/connect4") 79 int connect_v4_prog(struct bpf_sock_addr *ctx) 80 { 81 struct bpf_sock_tuple tuple = {}; 82 struct bpf_sock *sk; 83 84 /* Verify that new destination is available. */ 85 memset(&tuple.ipv4.saddr, 0, sizeof(tuple.ipv4.saddr)); 86 memset(&tuple.ipv4.sport, 0, sizeof(tuple.ipv4.sport)); 87 88 tuple.ipv4.daddr = bpf_htonl(DST_REWRITE_IP4); 89 tuple.ipv4.dport = bpf_htons(DST_REWRITE_PORT4); 90 91 if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM) 92 return 0; 93 else if (ctx->type == SOCK_STREAM) 94 sk = bpf_sk_lookup_tcp(ctx, &tuple, sizeof(tuple.ipv4), 95 BPF_F_CURRENT_NETNS, 0); 96 else 97 sk = bpf_sk_lookup_udp(ctx, &tuple, sizeof(tuple.ipv4), 98 BPF_F_CURRENT_NETNS, 0); 99 100 if (!sk) 101 return 0; 102 103 if (sk->src_ip4 != tuple.ipv4.daddr || 104 sk->src_port != DST_REWRITE_PORT4) { 105 bpf_sk_release(sk); 106 return 0; 107 } 108 109 bpf_sk_release(sk); 110 111 /* Rewrite congestion control. */ 112 if (ctx->type == SOCK_STREAM && set_cc(ctx)) 113 return 0; 114 115 /* Rewrite destination. */ 116 ctx->user_ip4 = bpf_htonl(DST_REWRITE_IP4); 117 ctx->user_port = bpf_htons(DST_REWRITE_PORT4); 118 119 return do_bind(ctx) ? 1 : 0; 120 } 121 122 char _license[] SEC("license") = "GPL"; 123