1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 #include <linux/bpf.h> 3 #include <linux/if_ether.h> 4 #include <linux/in.h> 5 6 #include <bpf/bpf_helpers.h> 7 #include <bpf/bpf_endian.h> 8 9 #ifndef AF_INET 10 #define AF_INET 2 11 #endif 12 13 /* NOTICE: Re-defining VLAN header levels to parse */ 14 #define VLAN_MAX_DEPTH 10 15 #include "../common/parsing_helpers.h" 16 #include "../common/rewrite_helpers.h" 17 18 #define unlikely(e) __builtin_expect((e), 0) 19 20 struct ethhdr_dsa { 21 unsigned char h_dest[ETH_ALEN]; 22 unsigned char h_source[ETH_ALEN]; 23 struct dsa_tag { 24 __u8 dev : 5; 25 __u8 tagged : 1; 26 __u8 cmd : 2; 27 28 __u8 cfi : 1; 29 __u8 res1 : 1; 30 __u8 is_trunk : 1; 31 __u8 port : 5; 32 33 __u8 vid_h : 4; 34 __u8 res2 : 1; 35 __u8 pri : 3; 36 37 __u8 vid_l; 38 } __attribute__((packed)) dsa; 39 __be16 h_proto; 40 } __attribute__((packed)); 41 42 enum dsa_cmd { 43 DSA_CMD_TO_CPU = 0, 44 DSA_CMD_FROM_CPU = 1, 45 DSA_CMD_TO_SNIFFER = 2, 46 DSA_CMD_FORWARD = 3 47 }; 48 49 static __always_inline int ifindex_to_mv88e6xxx_port(int ifindex) 50 { 51 switch (ifindex) { 52 case 14: 53 case 24: 54 case 25: 55 return 1; 56 case 16: 57 case 26: 58 case 27: 59 return 3; 60 case 18: 61 case 28: 62 case 29: 63 return 5; 64 case 19: 65 case 34: 66 case 35: 67 return 6; 68 case 20: 69 case 30: 70 case 31: 71 return 7; 72 case 21: 73 case 32: 74 case 33: 75 return 8; 76 case 22: 77 case 36: 78 case 37: 79 case 42: 80 case 43: 81 return 9; 82 default: 83 return -1; 84 } 85 } 86 87 static __always_inline int ifindex_to_vid(int ifindex) 88 { 89 switch (ifindex) { 90 case 24: 91 return 10; 92 case 25: 93 return 11; 94 case 26: 95 return 12; 96 case 27: 97 return 13; 98 case 28: 99 return 14; 100 case 29: 101 return 15; 102 case 30: 103 return 16; 104 case 31: 105 return 17; 106 case 32: 107 return 18; 108 case 33: 109 return 19; 110 case 34: 111 return 20; 112 case 35: 113 return 21; 114 case 36: 115 return 22; 116 case 37: 117 return 23; 118 case 42: 119 return 100; 120 case 43: 121 return 101; 122 default: 123 return -1; 124 } 125 } 126 127 static __always_inline int ifindex_to_dsa_tag(int ifindex, struct dsa_tag *dsa) 128 { 129 int port, vid; 130 131 port = ifindex_to_mv88e6xxx_port(ifindex); 132 if (port < 0) 133 return -1; 134 135 dsa->port = port; 136 vid = ifindex_to_vid(ifindex); 137 138 if (vid >= 0) { 139 dsa->tagged = 1; 140 dsa->vid_h = (vid >> 8) & 0xf; 141 dsa->vid_l = vid & 0xff; 142 } else { 143 dsa->tagged = dsa->vid_h = dsa->vid_l = 0; 144 } 145 146 return 0; 147 } 148 149 static __always_inline int dsa_tag_to_ifindex(struct dsa_tag *dsa) 150 { 151 int vid; 152 153 if (dsa->tagged) 154 vid = ((int)dsa->vid_h << 8) | dsa->vid_l; 155 else 156 vid = -1; 157 158 switch (vid) { 159 case 10: 160 return 24; 161 case 11: 162 return 25; 163 case 12: 164 return 26; 165 case 13: 166 return 27; 167 case 14: 168 return 28; 169 case 15: 170 return 29; 171 case 16: 172 return 30; 173 case 17: 174 return 31; 175 case 18: 176 return 32; 177 case 19: 178 return 33; 179 case 20: 180 return 34; 181 case 21: 182 return 35; 183 case 22: 184 return 36; 185 case 23: 186 return 37; 187 case 100: 188 return 42; 189 case 101: 190 return 43; 191 case -1: 192 switch (dsa->port) { 193 case 1: 194 return 14; 195 case 3: 196 return 16; 197 case 5: 198 return 18; 199 case 6: 200 return 19; 201 case 7: 202 return 20; 203 case 8: 204 return 21; 205 case 9: 206 return 22; 207 default: 208 return -1; 209 } 210 default: 211 return -1; 212 } 213 } 214 215 SEC("xdp_dsa") 216 int xdp_dsa_func(struct xdp_md *ctx) 217 { 218 void *data_end = (void *)(long)ctx->data_end; 219 void *data = (void *)(long)ctx->data; 220 struct bpf_fib_lookup fib_params; 221 struct ethhdr_dsa *eth = data; 222 struct iphdr *iphdr; 223 struct dsa_tag dsa; 224 int source_ifindex; 225 __u64 nh_off; 226 int pop_vlan = 0; 227 __u16 h_proto; 228 int rc; 229 230 nh_off = sizeof(*eth); 231 if (data + nh_off > data_end) 232 return XDP_DROP; 233 234 __builtin_memcpy(&dsa, ð->dsa, sizeof(dsa)); 235 236 if (dsa.cmd != DSA_CMD_FORWARD) 237 return XDP_PASS; 238 239 h_proto = eth->h_proto; 240 241 if (!dsa.tagged && h_proto == bpf_htons(ETH_P_8021Q)) { 242 struct vlan_hdr *vhdr; 243 int vid; 244 245 vhdr = data + nh_off; 246 nh_off += sizeof(struct vlan_hdr); 247 if (data + nh_off > data_end) 248 return XDP_DROP; 249 250 dsa.tagged = 1; 251 dsa.pri = bpf_ntohs(vhdr->h_vlan_TCI) >> 13; 252 vid = bpf_ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; 253 dsa.vid_h = (vid >> 8) & 0xf; 254 dsa.vid_l = vid & 0xff; 255 256 h_proto = vhdr->h_vlan_encapsulated_proto; 257 pop_vlan = 1; 258 } 259 260 if (h_proto != bpf_htons(ETH_P_IP)) 261 return XDP_PASS; 262 263 iphdr = data + nh_off; 264 nh_off += sizeof(struct iphdr); 265 if (data + nh_off > data_end) 266 return XDP_DROP; 267 268 if (iphdr->ttl <= 1) 269 return XDP_PASS; 270 271 source_ifindex = dsa_tag_to_ifindex(&dsa); 272 if (source_ifindex < 0) 273 return XDP_PASS; 274 275 __builtin_memset(&fib_params, 0, sizeof(fib_params)); 276 277 fib_params.family = AF_INET; 278 fib_params.tos = iphdr->tos; 279 fib_params.l4_protocol = iphdr->protocol; 280 fib_params.sport = 0; 281 fib_params.dport = 0; 282 fib_params.tot_len = bpf_ntohs(iphdr->tot_len); 283 fib_params.ipv4_src = iphdr->saddr; 284 fib_params.ipv4_dst = iphdr->daddr; 285 fib_params.ifindex = source_ifindex; 286 287 rc = bpf_fib_lookup(ctx, &fib_params, sizeof(fib_params), 0); 288 289 if (rc == BPF_FIB_LKUP_RET_SUCCESS) { 290 __builtin_memcpy(eth->h_source, fib_params.smac, ETH_ALEN); 291 __builtin_memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN); 292 293 if (ifindex_to_dsa_tag(fib_params.ifindex, &dsa) < 0) { 294 /* tu by sa mohlo robit bpf_redirect_map */ 295 return XDP_PASS; 296 } 297 } else { 298 return XDP_PASS; 299 } 300 301 dsa.cmd = DSA_CMD_FROM_CPU; 302 dsa.res1 = 0; 303 dsa.res2 = 0; 304 dsa.pri = 0; 305 __builtin_memcpy(ð->dsa, &dsa, sizeof(dsa)); 306 307 if (pop_vlan) { 308 eth->h_proto = h_proto; 309 310 __builtin_memmove(data + sizeof(struct vlan_hdr), data, sizeof(*eth)); 311 if (bpf_xdp_adjust_head(ctx, (int)sizeof(struct vlan_hdr))) 312 return XDP_DROP; 313 314 data = (void *)(long)ctx->data; 315 data_end = (void *)(long)ctx->data_end; 316 eth = data; 317 nh_off = sizeof(*eth); 318 319 if (data + nh_off > data_end) 320 return XDP_DROP; 321 } 322 323 return XDP_TX; 324 } 325 326 char _license[] SEC("license") = "GPL";