tc_l2_redirect_kern.c (6142B)
1/* Copyright (c) 2016 Facebook 2 * 3 * This program is free software; you can redistribute it and/or 4 * modify it under the terms of version 2 of the GNU General Public 5 * License as published by the Free Software Foundation. 6 */ 7#define KBUILD_MODNAME "foo" 8#include <uapi/linux/bpf.h> 9#include <uapi/linux/if_ether.h> 10#include <uapi/linux/if_packet.h> 11#include <uapi/linux/ip.h> 12#include <uapi/linux/ipv6.h> 13#include <uapi/linux/in.h> 14#include <uapi/linux/tcp.h> 15#include <uapi/linux/filter.h> 16#include <uapi/linux/pkt_cls.h> 17#include <net/ipv6.h> 18#include <bpf/bpf_helpers.h> 19 20#define _htonl __builtin_bswap32 21 22#define PIN_GLOBAL_NS 2 23struct bpf_elf_map { 24 __u32 type; 25 __u32 size_key; 26 __u32 size_value; 27 __u32 max_elem; 28 __u32 flags; 29 __u32 id; 30 __u32 pinning; 31}; 32 33/* copy of 'struct ethhdr' without __packed */ 34struct eth_hdr { 35 unsigned char h_dest[ETH_ALEN]; 36 unsigned char h_source[ETH_ALEN]; 37 unsigned short h_proto; 38}; 39 40struct bpf_elf_map SEC("maps") tun_iface = { 41 .type = BPF_MAP_TYPE_ARRAY, 42 .size_key = sizeof(int), 43 .size_value = sizeof(int), 44 .pinning = PIN_GLOBAL_NS, 45 .max_elem = 1, 46}; 47 48static __always_inline bool is_vip_addr(__be16 eth_proto, __be32 daddr) 49{ 50 if (eth_proto == htons(ETH_P_IP)) 51 return (_htonl(0xffffff00) & daddr) == _htonl(0x0a0a0100); 52 else if (eth_proto == htons(ETH_P_IPV6)) 53 return (daddr == _htonl(0x2401face)); 54 55 return false; 56} 57 58SEC("l2_to_iptun_ingress_forward") 59int _l2_to_iptun_ingress_forward(struct __sk_buff *skb) 60{ 61 struct bpf_tunnel_key tkey = {}; 62 void *data = (void *)(long)skb->data; 63 struct eth_hdr *eth = data; 64 void *data_end = (void *)(long)skb->data_end; 65 int key = 0, *ifindex; 66 67 int ret; 68 69 if (data + sizeof(*eth) > data_end) 70 return TC_ACT_OK; 71 72 ifindex = bpf_map_lookup_elem(&tun_iface, &key); 73 if (!ifindex) 74 return TC_ACT_OK; 75 76 if (eth->h_proto == htons(ETH_P_IP)) { 77 char fmt4[] = "ingress forward to ifindex:%d daddr4:%x\n"; 78 struct iphdr *iph = data + sizeof(*eth); 79 80 if (data + sizeof(*eth) + sizeof(*iph) > data_end) 81 return TC_ACT_OK; 82 83 if (iph->protocol != IPPROTO_IPIP) 84 return TC_ACT_OK; 85 86 bpf_trace_printk(fmt4, sizeof(fmt4), *ifindex, 87 _htonl(iph->daddr)); 88 return bpf_redirect(*ifindex, BPF_F_INGRESS); 89 } else if (eth->h_proto == htons(ETH_P_IPV6)) { 90 char fmt6[] = "ingress forward to ifindex:%d daddr6:%x::%x\n"; 91 struct ipv6hdr *ip6h = data + sizeof(*eth); 92 93 if (data + sizeof(*eth) + sizeof(*ip6h) > data_end) 94 return TC_ACT_OK; 95 96 if (ip6h->nexthdr != IPPROTO_IPIP && 97 ip6h->nexthdr != IPPROTO_IPV6) 98 return TC_ACT_OK; 99 100 bpf_trace_printk(fmt6, sizeof(fmt6), *ifindex, 101 _htonl(ip6h->daddr.s6_addr32[0]), 102 _htonl(ip6h->daddr.s6_addr32[3])); 103 return bpf_redirect(*ifindex, BPF_F_INGRESS); 104 } 105 106 return TC_ACT_OK; 107} 108 109SEC("l2_to_iptun_ingress_redirect") 110int _l2_to_iptun_ingress_redirect(struct __sk_buff *skb) 111{ 112 struct bpf_tunnel_key tkey = {}; 113 void *data = (void *)(long)skb->data; 114 struct eth_hdr *eth = data; 115 void *data_end = (void *)(long)skb->data_end; 116 int key = 0, *ifindex; 117 118 int ret; 119 120 if (data + sizeof(*eth) > data_end) 121 return TC_ACT_OK; 122 123 ifindex = bpf_map_lookup_elem(&tun_iface, &key); 124 if (!ifindex) 125 return TC_ACT_OK; 126 127 if (eth->h_proto == htons(ETH_P_IP)) { 128 char fmt4[] = "e/ingress redirect daddr4:%x to ifindex:%d\n"; 129 struct iphdr *iph = data + sizeof(*eth); 130 __be32 daddr = iph->daddr; 131 132 if (data + sizeof(*eth) + sizeof(*iph) > data_end) 133 return TC_ACT_OK; 134 135 if (!is_vip_addr(eth->h_proto, daddr)) 136 return TC_ACT_OK; 137 138 bpf_trace_printk(fmt4, sizeof(fmt4), _htonl(daddr), *ifindex); 139 } else { 140 return TC_ACT_OK; 141 } 142 143 tkey.tunnel_id = 10000; 144 tkey.tunnel_ttl = 64; 145 tkey.remote_ipv4 = 0x0a020166; /* 10.2.1.102 */ 146 bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), 0); 147 return bpf_redirect(*ifindex, 0); 148} 149 150SEC("l2_to_ip6tun_ingress_redirect") 151int _l2_to_ip6tun_ingress_redirect(struct __sk_buff *skb) 152{ 153 struct bpf_tunnel_key tkey = {}; 154 void *data = (void *)(long)skb->data; 155 struct eth_hdr *eth = data; 156 void *data_end = (void *)(long)skb->data_end; 157 int key = 0, *ifindex; 158 159 if (data + sizeof(*eth) > data_end) 160 return TC_ACT_OK; 161 162 ifindex = bpf_map_lookup_elem(&tun_iface, &key); 163 if (!ifindex) 164 return TC_ACT_OK; 165 166 if (eth->h_proto == htons(ETH_P_IP)) { 167 char fmt4[] = "e/ingress redirect daddr4:%x to ifindex:%d\n"; 168 struct iphdr *iph = data + sizeof(*eth); 169 170 if (data + sizeof(*eth) + sizeof(*iph) > data_end) 171 return TC_ACT_OK; 172 173 if (!is_vip_addr(eth->h_proto, iph->daddr)) 174 return TC_ACT_OK; 175 176 bpf_trace_printk(fmt4, sizeof(fmt4), _htonl(iph->daddr), 177 *ifindex); 178 } else if (eth->h_proto == htons(ETH_P_IPV6)) { 179 char fmt6[] = "e/ingress redirect daddr6:%x to ifindex:%d\n"; 180 struct ipv6hdr *ip6h = data + sizeof(*eth); 181 182 if (data + sizeof(*eth) + sizeof(*ip6h) > data_end) 183 return TC_ACT_OK; 184 185 if (!is_vip_addr(eth->h_proto, ip6h->daddr.s6_addr32[0])) 186 return TC_ACT_OK; 187 188 bpf_trace_printk(fmt6, sizeof(fmt6), 189 _htonl(ip6h->daddr.s6_addr32[0]), *ifindex); 190 } else { 191 return TC_ACT_OK; 192 } 193 194 tkey.tunnel_id = 10000; 195 tkey.tunnel_ttl = 64; 196 /* 2401:db02:0:0:0:0:0:66 */ 197 tkey.remote_ipv6[0] = _htonl(0x2401db02); 198 tkey.remote_ipv6[1] = 0; 199 tkey.remote_ipv6[2] = 0; 200 tkey.remote_ipv6[3] = _htonl(0x00000066); 201 bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), BPF_F_TUNINFO_IPV6); 202 return bpf_redirect(*ifindex, 0); 203} 204 205SEC("drop_non_tun_vip") 206int _drop_non_tun_vip(struct __sk_buff *skb) 207{ 208 struct bpf_tunnel_key tkey = {}; 209 void *data = (void *)(long)skb->data; 210 struct eth_hdr *eth = data; 211 void *data_end = (void *)(long)skb->data_end; 212 213 if (data + sizeof(*eth) > data_end) 214 return TC_ACT_OK; 215 216 if (eth->h_proto == htons(ETH_P_IP)) { 217 struct iphdr *iph = data + sizeof(*eth); 218 219 if (data + sizeof(*eth) + sizeof(*iph) > data_end) 220 return TC_ACT_OK; 221 222 if (is_vip_addr(eth->h_proto, iph->daddr)) 223 return TC_ACT_SHOT; 224 } else if (eth->h_proto == htons(ETH_P_IPV6)) { 225 struct ipv6hdr *ip6h = data + sizeof(*eth); 226 227 if (data + sizeof(*eth) + sizeof(*ip6h) > data_end) 228 return TC_ACT_OK; 229 230 if (is_vip_addr(eth->h_proto, ip6h->daddr.s6_addr32[0])) 231 return TC_ACT_SHOT; 232 } 233 234 return TC_ACT_OK; 235} 236 237char _license[] SEC("license") = "GPL";