393 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2022 Meta3 4#include <stddef.h>5#include <stdint.h>6#include <stdbool.h>7#include <linux/bpf.h>8#include <linux/stddef.h>9#include <linux/pkt_cls.h>10#include <linux/if_ether.h>11#include <linux/in.h>12#include <linux/ip.h>13#include <linux/ipv6.h>14#include <linux/tcp.h>15#include <linux/udp.h>16#include <bpf/bpf_helpers.h>17#include <bpf/bpf_endian.h>18 19/* veth_src --- veth_src_fwd --- veth_det_fwd --- veth_dst20 * | |21 * ns_src | ns_fwd | ns_dst22 *23 * ns_src and ns_dst: ENDHOST namespace24 * ns_fwd: Fowarding namespace25 */26 27#define ctx_ptr(field) (void *)(long)(field)28 29#define ip4_src __bpf_htonl(0xac100164) /* 172.16.1.100 */30#define ip4_dst __bpf_htonl(0xac100264) /* 172.16.2.100 */31 32#define ip6_src { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \33 0x00, 0x01, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe }34#define ip6_dst { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \35 0x00, 0x02, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe }36 37#define v6_equal(a, b) (a.s6_addr32[0] == b.s6_addr32[0] && \38 a.s6_addr32[1] == b.s6_addr32[1] && \39 a.s6_addr32[2] == b.s6_addr32[2] && \40 a.s6_addr32[3] == b.s6_addr32[3])41 42volatile const __u32 IFINDEX_SRC;43volatile const __u32 IFINDEX_DST;44 45#define EGRESS_ENDHOST_MAGIC 0x0b9fbeef46#define INGRESS_FWDNS_MAGIC 0x1b9fbeef47#define EGRESS_FWDNS_MAGIC 0x2b9fbeef48 49enum {50 INGRESS_FWDNS_P100,51 INGRESS_FWDNS_P101,52 EGRESS_FWDNS_P100,53 EGRESS_FWDNS_P101,54 INGRESS_ENDHOST,55 EGRESS_ENDHOST,56 SET_DTIME,57 __MAX_CNT,58};59 60enum {61 TCP_IP6_CLEAR_DTIME,62 TCP_IP4,63 TCP_IP6,64 UDP_IP4,65 UDP_IP6,66 TCP_IP4_RT_FWD,67 TCP_IP6_RT_FWD,68 UDP_IP4_RT_FWD,69 UDP_IP6_RT_FWD,70 UKN_TEST,71 __NR_TESTS,72};73 74enum {75 SRC_NS = 1,76 DST_NS,77};78 79__u32 dtimes[__NR_TESTS][__MAX_CNT] = {};80__u32 errs[__NR_TESTS][__MAX_CNT] = {};81__u32 test = 0;82 83static void inc_dtimes(__u32 idx)84{85 if (test < __NR_TESTS)86 dtimes[test][idx]++;87 else88 dtimes[UKN_TEST][idx]++;89}90 91static void inc_errs(__u32 idx)92{93 if (test < __NR_TESTS)94 errs[test][idx]++;95 else96 errs[UKN_TEST][idx]++;97}98 99static int skb_proto(int type)100{101 return type & 0xff;102}103 104static int skb_ns(int type)105{106 return (type >> 8) & 0xff;107}108 109static bool fwdns_clear_dtime(void)110{111 return test == TCP_IP6_CLEAR_DTIME;112}113 114static bool bpf_fwd(void)115{116 return test < TCP_IP4_RT_FWD;117}118 119static __u8 get_proto(void)120{121 switch (test) {122 case UDP_IP4:123 case UDP_IP6:124 case UDP_IP4_RT_FWD:125 case UDP_IP6_RT_FWD:126 return IPPROTO_UDP;127 default:128 return IPPROTO_TCP;129 }130}131 132/* -1: parse error: TC_ACT_SHOT133 * 0: not testing traffic: TC_ACT_OK134 * >0: first byte is the inet_proto, second byte has the netns135 * of the sender136 */137static int skb_get_type(struct __sk_buff *skb)138{139 __u16 dst_ns_port = __bpf_htons(50000 + test);140 void *data_end = ctx_ptr(skb->data_end);141 void *data = ctx_ptr(skb->data);142 __u8 inet_proto = 0, ns = 0;143 struct ipv6hdr *ip6h;144 __u16 sport, dport;145 struct iphdr *iph;146 struct tcphdr *th;147 struct udphdr *uh;148 void *trans;149 150 switch (skb->protocol) {151 case __bpf_htons(ETH_P_IP):152 iph = data + sizeof(struct ethhdr);153 if (iph + 1 > data_end)154 return -1;155 if (iph->saddr == ip4_src)156 ns = SRC_NS;157 else if (iph->saddr == ip4_dst)158 ns = DST_NS;159 inet_proto = iph->protocol;160 trans = iph + 1;161 break;162 case __bpf_htons(ETH_P_IPV6):163 ip6h = data + sizeof(struct ethhdr);164 if (ip6h + 1 > data_end)165 return -1;166 if (v6_equal(ip6h->saddr, (struct in6_addr){{ip6_src}}))167 ns = SRC_NS;168 else if (v6_equal(ip6h->saddr, (struct in6_addr){{ip6_dst}}))169 ns = DST_NS;170 inet_proto = ip6h->nexthdr;171 trans = ip6h + 1;172 break;173 default:174 return 0;175 }176 177 /* skb is not from src_ns or dst_ns.178 * skb is not the testing IPPROTO.179 */180 if (!ns || inet_proto != get_proto())181 return 0;182 183 switch (inet_proto) {184 case IPPROTO_TCP:185 th = trans;186 if (th + 1 > data_end)187 return -1;188 sport = th->source;189 dport = th->dest;190 break;191 case IPPROTO_UDP:192 uh = trans;193 if (uh + 1 > data_end)194 return -1;195 sport = uh->source;196 dport = uh->dest;197 break;198 default:199 return 0;200 }201 202 /* The skb is the testing traffic */203 if ((ns == SRC_NS && dport == dst_ns_port) ||204 (ns == DST_NS && sport == dst_ns_port))205 return (ns << 8 | inet_proto);206 207 return 0;208}209 210/* format: direction@iface@netns211 * egress@veth_(src|dst)@ns_(src|dst)212 */213SEC("tc")214int egress_host(struct __sk_buff *skb)215{216 int skb_type;217 218 skb_type = skb_get_type(skb);219 if (skb_type == -1)220 return TC_ACT_SHOT;221 if (!skb_type)222 return TC_ACT_OK;223 224 if (skb_proto(skb_type) == IPPROTO_TCP) {225 if (skb->tstamp_type == BPF_SKB_CLOCK_MONOTONIC &&226 skb->tstamp)227 inc_dtimes(EGRESS_ENDHOST);228 else229 inc_errs(EGRESS_ENDHOST);230 } else if (skb_proto(skb_type) == IPPROTO_UDP) {231 if (skb->tstamp_type == BPF_SKB_CLOCK_TAI &&232 skb->tstamp)233 inc_dtimes(EGRESS_ENDHOST);234 else235 inc_errs(EGRESS_ENDHOST);236 } else {237 if (skb->tstamp_type == BPF_SKB_CLOCK_REALTIME &&238 skb->tstamp)239 inc_errs(EGRESS_ENDHOST);240 }241 242 skb->tstamp = EGRESS_ENDHOST_MAGIC;243 244 return TC_ACT_OK;245}246 247/* ingress@veth_(src|dst)@ns_(src|dst) */248SEC("tc")249int ingress_host(struct __sk_buff *skb)250{251 int skb_type;252 253 skb_type = skb_get_type(skb);254 if (skb_type == -1)255 return TC_ACT_SHOT;256 if (!skb_type)257 return TC_ACT_OK;258 259 if (skb->tstamp_type == BPF_SKB_CLOCK_MONOTONIC &&260 skb->tstamp == EGRESS_FWDNS_MAGIC)261 inc_dtimes(INGRESS_ENDHOST);262 else263 inc_errs(INGRESS_ENDHOST);264 265 return TC_ACT_OK;266}267 268/* ingress@veth_(src|dst)_fwd@ns_fwd priority 100 */269SEC("tc")270int ingress_fwdns_prio100(struct __sk_buff *skb)271{272 int skb_type;273 274 skb_type = skb_get_type(skb);275 if (skb_type == -1)276 return TC_ACT_SHOT;277 if (!skb_type)278 return TC_ACT_OK;279 280 /* delivery_time is only available to the ingress281 * if the tc-bpf checks the skb->tstamp_type.282 */283 if (skb->tstamp == EGRESS_ENDHOST_MAGIC)284 inc_errs(INGRESS_FWDNS_P100);285 286 if (fwdns_clear_dtime())287 skb->tstamp = 0;288 289 return TC_ACT_UNSPEC;290}291 292/* egress@veth_(src|dst)_fwd@ns_fwd priority 100 */293SEC("tc")294int egress_fwdns_prio100(struct __sk_buff *skb)295{296 int skb_type;297 298 skb_type = skb_get_type(skb);299 if (skb_type == -1)300 return TC_ACT_SHOT;301 if (!skb_type)302 return TC_ACT_OK;303 304 /* delivery_time is always available to egress even305 * the tc-bpf did not use the tstamp_type.306 */307 if (skb->tstamp == INGRESS_FWDNS_MAGIC)308 inc_dtimes(EGRESS_FWDNS_P100);309 else310 inc_errs(EGRESS_FWDNS_P100);311 312 if (fwdns_clear_dtime())313 skb->tstamp = 0;314 315 return TC_ACT_UNSPEC;316}317 318/* ingress@veth_(src|dst)_fwd@ns_fwd priority 101 */319SEC("tc")320int ingress_fwdns_prio101(struct __sk_buff *skb)321{322 int skb_type;323 324 skb_type = skb_get_type(skb);325 if (skb_type == -1 || !skb_type)326 /* Should have handled in prio100 */327 return TC_ACT_SHOT;328 329 if (skb->tstamp_type) {330 if (fwdns_clear_dtime() ||331 (skb->tstamp_type != BPF_SKB_CLOCK_MONOTONIC &&332 skb->tstamp_type != BPF_SKB_CLOCK_TAI) ||333 skb->tstamp != EGRESS_ENDHOST_MAGIC)334 inc_errs(INGRESS_FWDNS_P101);335 else336 inc_dtimes(INGRESS_FWDNS_P101);337 } else {338 if (!fwdns_clear_dtime())339 inc_errs(INGRESS_FWDNS_P101);340 }341 342 if (skb->tstamp_type == BPF_SKB_CLOCK_MONOTONIC) {343 skb->tstamp = INGRESS_FWDNS_MAGIC;344 } else {345 if (bpf_skb_set_tstamp(skb, INGRESS_FWDNS_MAGIC,346 BPF_SKB_CLOCK_MONOTONIC))347 inc_errs(SET_DTIME);348 }349 350 if (skb_ns(skb_type) == SRC_NS)351 return bpf_fwd() ?352 bpf_redirect_neigh(IFINDEX_DST, NULL, 0, 0) : TC_ACT_OK;353 else354 return bpf_fwd() ?355 bpf_redirect_neigh(IFINDEX_SRC, NULL, 0, 0) : TC_ACT_OK;356}357 358/* egress@veth_(src|dst)_fwd@ns_fwd priority 101 */359SEC("tc")360int egress_fwdns_prio101(struct __sk_buff *skb)361{362 int skb_type;363 364 skb_type = skb_get_type(skb);365 if (skb_type == -1 || !skb_type)366 /* Should have handled in prio100 */367 return TC_ACT_SHOT;368 369 if (skb->tstamp_type) {370 if (fwdns_clear_dtime() ||371 skb->tstamp_type != BPF_SKB_CLOCK_MONOTONIC ||372 skb->tstamp != INGRESS_FWDNS_MAGIC)373 inc_errs(EGRESS_FWDNS_P101);374 else375 inc_dtimes(EGRESS_FWDNS_P101);376 } else {377 if (!fwdns_clear_dtime())378 inc_errs(EGRESS_FWDNS_P101);379 }380 381 if (skb->tstamp_type == BPF_SKB_CLOCK_MONOTONIC) {382 skb->tstamp = EGRESS_FWDNS_MAGIC;383 } else {384 if (bpf_skb_set_tstamp(skb, EGRESS_FWDNS_MAGIC,385 BPF_SKB_CLOCK_MONOTONIC))386 inc_errs(SET_DTIME);387 }388 389 return TC_ACT_OK;390}391 392char __license[] SEC("license") = "GPL";393