0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027 #include <linux/bpf.h>
0028 #include <linux/if.h>
0029 #include <linux/if_ether.h>
0030 #include <linux/if_packet.h>
0031 #include <linux/in.h>
0032 #include <linux/in6.h>
0033 #include <linux/ip.h>
0034 #include <linux/ipv6.h>
0035 #include <linux/pkt_cls.h>
0036 #include <linux/swab.h>
0037 #include <stdbool.h>
0038 #include <stdint.h>
0039
0040
0041 #include <linux/udp.h>
0042
0043 #include <bpf/bpf_helpers.h>
0044 #include <bpf/bpf_endian.h>
0045
0046 #define IP_DF 0x4000
0047
0048 SEC("schedcls/ingress6/nat_6")
0049 int sched_cls_ingress6_nat_6_prog(struct __sk_buff *skb)
0050 {
0051 const int l2_header_size = sizeof(struct ethhdr);
0052 void *data = (void *)(long)skb->data;
0053 const void *data_end = (void *)(long)skb->data_end;
0054 const struct ethhdr * const eth = data;
0055 const struct ipv6hdr * const ip6 = (void *)(eth + 1);
0056
0057
0058 if (skb->pkt_type != PACKET_HOST)
0059 return TC_ACT_OK;
0060
0061
0062 if (skb->protocol != bpf_htons(ETH_P_IPV6))
0063 return TC_ACT_OK;
0064
0065
0066 if (data + l2_header_size + sizeof(*ip6) > data_end)
0067 return TC_ACT_OK;
0068
0069
0070 if (eth->h_proto != bpf_htons(ETH_P_IPV6))
0071 return TC_ACT_OK;
0072
0073
0074 if (ip6->version != 6)
0075 return TC_ACT_OK;
0076
0077 if (bpf_ntohs(ip6->payload_len) > 0xFFFF - sizeof(struct iphdr))
0078 return TC_ACT_OK;
0079 switch (ip6->nexthdr) {
0080 case IPPROTO_TCP:
0081 case IPPROTO_UDP:
0082 case IPPROTO_GRE:
0083 case IPPROTO_ESP:
0084 break;
0085 default:
0086 return TC_ACT_OK;
0087 }
0088
0089 struct ethhdr eth2;
0090
0091 eth2 = *eth;
0092 eth2.h_proto = bpf_htons(ETH_P_IP);
0093
0094 struct iphdr ip = {
0095 .version = 4,
0096 .ihl = sizeof(struct iphdr) / sizeof(__u32),
0097 .tos = (ip6->priority << 4) + (ip6->flow_lbl[0] >> 4),
0098 .tot_len = bpf_htons(bpf_ntohs(ip6->payload_len) + sizeof(struct iphdr)),
0099 .id = 0,
0100 .frag_off = bpf_htons(IP_DF),
0101 .ttl = ip6->hop_limit,
0102 .protocol = ip6->nexthdr,
0103 .check = 0,
0104 .saddr = 0x0201a8c0,
0105 .daddr = 0x0101a8c0,
0106 };
0107
0108
0109 __wsum sum4 = 0;
0110
0111 for (int i = 0; i < sizeof(ip) / sizeof(__u16); ++i)
0112 sum4 += ((__u16 *)&ip)[i];
0113
0114
0115 sum4 = (sum4 & 0xFFFF) + (sum4 >> 16);
0116 sum4 = (sum4 & 0xFFFF) + (sum4 >> 16);
0117 ip.check = (__u16)~sum4;
0118
0119
0120 __wsum sum6 = 0;
0121
0122 for (int i = 0; i < sizeof(*ip6) / sizeof(__u16); ++i)
0123 sum6 += ~((__u16 *)ip6)[i];
0124
0125
0126
0127
0128
0129
0130 if (bpf_skb_change_proto(skb, bpf_htons(ETH_P_IP), 0))
0131 return TC_ACT_OK;
0132 bpf_csum_update(skb, sum6);
0133
0134 data = (void *)(long)skb->data;
0135 data_end = (void *)(long)skb->data_end;
0136 if (data + l2_header_size + sizeof(struct iphdr) > data_end)
0137 return TC_ACT_SHOT;
0138
0139 struct ethhdr *new_eth = data;
0140
0141
0142 *new_eth = eth2;
0143
0144
0145 *(struct iphdr *)(new_eth + 1) = ip;
0146 return bpf_redirect(skb->ifindex, BPF_F_INGRESS);
0147 }
0148
0149 SEC("schedcls/egress4/snat4")
0150 int sched_cls_egress4_snat4_prog(struct __sk_buff *skb)
0151 {
0152 const int l2_header_size = sizeof(struct ethhdr);
0153 void *data = (void *)(long)skb->data;
0154 const void *data_end = (void *)(long)skb->data_end;
0155 const struct ethhdr *const eth = data;
0156 const struct iphdr *const ip4 = (void *)(eth + 1);
0157
0158
0159 if (skb->protocol != bpf_htons(ETH_P_IP))
0160 return TC_ACT_OK;
0161
0162
0163 if (data + l2_header_size + sizeof(struct ipv6hdr) > data_end)
0164 return TC_ACT_OK;
0165
0166
0167 if (eth->h_proto != bpf_htons(ETH_P_IP))
0168 return TC_ACT_OK;
0169
0170
0171 if (ip4->version != 4)
0172 return TC_ACT_OK;
0173
0174
0175 if (ip4->ihl != 5)
0176 return TC_ACT_OK;
0177
0178
0179 if (bpf_htons(ip4->tot_len) > 0xFFFF - sizeof(struct ipv6hdr))
0180 return TC_ACT_OK;
0181
0182
0183 __wsum sum4 = 0;
0184
0185 for (int i = 0; i < sizeof(*ip4) / sizeof(__u16); ++i)
0186 sum4 += ((__u16 *)ip4)[i];
0187
0188
0189 sum4 = (sum4 & 0xFFFF) + (sum4 >> 16);
0190 sum4 = (sum4 & 0xFFFF) + (sum4 >> 16);
0191
0192 if (sum4 != 0xFFFF)
0193 return TC_ACT_OK;
0194
0195
0196 if (bpf_ntohs(ip4->tot_len) < sizeof(*ip4))
0197 return TC_ACT_OK;
0198
0199
0200 if (ip4->frag_off & ~bpf_htons(IP_DF))
0201 return TC_ACT_OK;
0202
0203 switch (ip4->protocol) {
0204 case IPPROTO_TCP:
0205 case IPPROTO_GRE:
0206 case IPPROTO_ESP:
0207 break;
0208
0209 case IPPROTO_UDP:
0210 if (data + sizeof(*ip4) + sizeof(struct udphdr) > data_end)
0211 return TC_ACT_OK;
0212 const struct udphdr *uh = (const struct udphdr *)(ip4 + 1);
0213
0214
0215
0216
0217 if (!uh->check)
0218 return TC_ACT_OK;
0219 break;
0220
0221 default:
0222 return TC_ACT_OK;
0223 }
0224 struct ethhdr eth2;
0225
0226 eth2 = *eth;
0227 eth2.h_proto = bpf_htons(ETH_P_IPV6);
0228
0229 struct ipv6hdr ip6 = {
0230 .version = 6,
0231 .priority = ip4->tos >> 4,
0232 .flow_lbl = {(ip4->tos & 0xF) << 4, 0, 0},
0233 .payload_len = bpf_htons(bpf_ntohs(ip4->tot_len) - 20),
0234 .nexthdr = ip4->protocol,
0235 .hop_limit = ip4->ttl,
0236 };
0237 ip6.saddr.in6_u.u6_addr32[0] = bpf_htonl(0x20010db8);
0238 ip6.saddr.in6_u.u6_addr32[1] = 0;
0239 ip6.saddr.in6_u.u6_addr32[2] = 0;
0240 ip6.saddr.in6_u.u6_addr32[3] = bpf_htonl(1);
0241 ip6.daddr.in6_u.u6_addr32[0] = bpf_htonl(0x20010db8);
0242 ip6.daddr.in6_u.u6_addr32[1] = 0;
0243 ip6.daddr.in6_u.u6_addr32[2] = 0;
0244 ip6.daddr.in6_u.u6_addr32[3] = bpf_htonl(2);
0245
0246
0247 __wsum sum6 = 0;
0248
0249 for (int i = 0; i < sizeof(ip6) / sizeof(__u16); ++i)
0250 sum6 += ((__u16 *)&ip6)[i];
0251
0252
0253
0254 if (bpf_skb_change_proto(skb, bpf_htons(ETH_P_IPV6), 0))
0255 return TC_ACT_OK;
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265 bpf_csum_update(skb, sum6);
0266
0267
0268 data = (void *)(long)skb->data;
0269 data_end = (void *)(long)skb->data_end;
0270
0271
0272
0273 if (data + l2_header_size + sizeof(ip6) > data_end)
0274 return TC_ACT_SHOT;
0275
0276 struct ethhdr *new_eth = data;
0277
0278
0279 *new_eth = eth2;
0280
0281 *(struct ipv6hdr *)(new_eth + 1) = ip6;
0282 return TC_ACT_OK;
0283 }
0284
0285 char _license[] SEC("license") = ("GPL");