1 // SPDX-License-Identifier: GPL-2.0-only
3 * This code is taken from the Android Open Source Project and the author
4 * (Maciej Żenczykowski) has gave permission to relicense it under the
5 * GPLv2. Therefore this program is free software;
6 * You can redistribute it and/or modify it under the terms of the GNU
7 * General Public License version 2 as published by the Free Software
10 * The original headers, including the original license headers, are
11 * included below for completeness.
13 * Copyright (C) 2019 The Android Open Source Project
15 * Licensed under the Apache License, Version 2.0 (the "License");
16 * you may not use this file except in compliance with the License.
17 * You may obtain a copy of the License at
19 * http://www.apache.org/licenses/LICENSE-2.0
21 * Unless required by applicable law or agreed to in writing, software
22 * distributed under the License is distributed on an "AS IS" BASIS,
23 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 * See the License for the specific language governing permissions and
25 * limitations under the License.
27 #include <linux/bpf.h>
29 #include <linux/if_ether.h>
30 #include <linux/if_packet.h>
32 #include <linux/in6.h>
34 #include <linux/ipv6.h>
35 #include <linux/pkt_cls.h>
36 #include <linux/swab.h>
41 #include <linux/udp.h>
43 #include <bpf/bpf_helpers.h>
44 #include <bpf/bpf_endian.h>
46 #define IP_DF 0x4000 // Flag: "Don't Fragment"
48 SEC("schedcls/ingress6/nat_6")
49 int sched_cls_ingress6_nat_6_prog(struct __sk_buff
*skb
)
51 const int l2_header_size
= sizeof(struct ethhdr
);
52 void *data
= (void *)(long)skb
->data
;
53 const void *data_end
= (void *)(long)skb
->data_end
;
54 const struct ethhdr
* const eth
= data
; // used iff is_ethernet
55 const struct ipv6hdr
* const ip6
= (void *)(eth
+ 1);
57 // Require ethernet dst mac address to be our unicast address.
58 if (skb
->pkt_type
!= PACKET_HOST
)
61 // Must be meta-ethernet IPv6 frame
62 if (skb
->protocol
!= bpf_htons(ETH_P_IPV6
))
65 // Must have (ethernet and) ipv6 header
66 if (data
+ l2_header_size
+ sizeof(*ip6
) > data_end
)
69 // Ethertype - if present - must be IPv6
70 if (eth
->h_proto
!= bpf_htons(ETH_P_IPV6
))
73 // IP version must be 6
74 if (ip6
->version
!= 6)
76 // Maximum IPv6 payload length that can be translated to IPv4
77 if (bpf_ntohs(ip6
->payload_len
) > 0xFFFF - sizeof(struct iphdr
))
79 switch (ip6
->nexthdr
) {
80 case IPPROTO_TCP
: // For TCP & UDP the checksum neutrality of the chosen IPv6
81 case IPPROTO_UDP
: // address means there is no need to update their checksums.
82 case IPPROTO_GRE
: // We do not need to bother looking at GRE/ESP headers,
83 case IPPROTO_ESP
: // since there is never a checksum to update.
85 default: // do not know how to handle anything else
89 struct ethhdr eth2
; // used iff is_ethernet
91 eth2
= *eth
; // Copy over the ethernet header (src/dst mac)
92 eth2
.h_proto
= bpf_htons(ETH_P_IP
); // But replace the ethertype
96 .ihl
= sizeof(struct iphdr
) / sizeof(__u32
), // u4
97 .tos
= (ip6
->priority
<< 4) + (ip6
->flow_lbl
[0] >> 4), // u8
98 .tot_len
= bpf_htons(bpf_ntohs(ip6
->payload_len
) + sizeof(struct iphdr
)), // u16
100 .frag_off
= bpf_htons(IP_DF
), // u16
101 .ttl
= ip6
->hop_limit
, // u8
102 .protocol
= ip6
->nexthdr
, // u8
104 .saddr
= 0x0201a8c0, // u32
105 .daddr
= 0x0101a8c0, // u32
108 // Calculate the IPv4 one's complement checksum of the IPv4 header.
111 for (int i
= 0; i
< sizeof(ip
) / sizeof(__u16
); ++i
)
112 sum4
+= ((__u16
*)&ip
)[i
];
114 // Note that sum4 is guaranteed to be non-zero by virtue of ip.version == 4
115 sum4
= (sum4
& 0xFFFF) + (sum4
>> 16); // collapse u32 into range 1 .. 0x1FFFE
116 sum4
= (sum4
& 0xFFFF) + (sum4
>> 16); // collapse any potential carry into u16
117 ip
.check
= (__u16
)~sum4
; // sum4 cannot be zero, so this is never 0xFFFF
119 // Calculate the *negative* IPv6 16-bit one's complement checksum of the IPv6 header.
121 // We'll end up with a non-zero sum due to ip6->version == 6 (which has '0' bits)
122 for (int i
= 0; i
< sizeof(*ip6
) / sizeof(__u16
); ++i
)
123 sum6
+= ~((__u16
*)ip6
)[i
]; // note the bitwise negation
125 // Note that there is no L4 checksum update: we are relying on the checksum neutrality
126 // of the ipv6 address chosen by netd's ClatdController.
128 // Packet mutations begin - point of no return, but if this first modification fails
129 // the packet is probably still pristine, so let clatd handle it.
130 if (bpf_skb_change_proto(skb
, bpf_htons(ETH_P_IP
), 0))
132 bpf_csum_update(skb
, sum6
);
134 data
= (void *)(long)skb
->data
;
135 data_end
= (void *)(long)skb
->data_end
;
136 if (data
+ l2_header_size
+ sizeof(struct iphdr
) > data_end
)
139 struct ethhdr
*new_eth
= data
;
141 // Copy over the updated ethernet header
144 // Copy over the new ipv4 header.
145 *(struct iphdr
*)(new_eth
+ 1) = ip
;
146 return bpf_redirect(skb
->ifindex
, BPF_F_INGRESS
);
149 SEC("schedcls/egress4/snat4")
150 int sched_cls_egress4_snat4_prog(struct __sk_buff
*skb
)
152 const int l2_header_size
= sizeof(struct ethhdr
);
153 void *data
= (void *)(long)skb
->data
;
154 const void *data_end
= (void *)(long)skb
->data_end
;
155 const struct ethhdr
*const eth
= data
; // used iff is_ethernet
156 const struct iphdr
*const ip4
= (void *)(eth
+ 1);
158 // Must be meta-ethernet IPv4 frame
159 if (skb
->protocol
!= bpf_htons(ETH_P_IP
))
162 // Must have ipv4 header
163 if (data
+ l2_header_size
+ sizeof(struct ipv6hdr
) > data_end
)
166 // Ethertype - if present - must be IPv4
167 if (eth
->h_proto
!= bpf_htons(ETH_P_IP
))
170 // IP version must be 4
171 if (ip4
->version
!= 4)
174 // We cannot handle IP options, just standard 20 byte == 5 dword minimal IPv4 header
178 // Maximum IPv6 payload length that can be translated to IPv4
179 if (bpf_htons(ip4
->tot_len
) > 0xFFFF - sizeof(struct ipv6hdr
))
182 // Calculate the IPv4 one's complement checksum of the IPv4 header.
185 for (int i
= 0; i
< sizeof(*ip4
) / sizeof(__u16
); ++i
)
186 sum4
+= ((__u16
*)ip4
)[i
];
188 // Note that sum4 is guaranteed to be non-zero by virtue of ip4->version == 4
189 sum4
= (sum4
& 0xFFFF) + (sum4
>> 16); // collapse u32 into range 1 .. 0x1FFFE
190 sum4
= (sum4
& 0xFFFF) + (sum4
>> 16); // collapse any potential carry into u16
191 // for a correct checksum we should get *a* zero, but sum4 must be positive, ie 0xFFFF
195 // Minimum IPv4 total length is the size of the header
196 if (bpf_ntohs(ip4
->tot_len
) < sizeof(*ip4
))
199 // We are incapable of dealing with IPv4 fragments
200 if (ip4
->frag_off
& ~bpf_htons(IP_DF
))
203 switch (ip4
->protocol
) {
204 case IPPROTO_TCP
: // For TCP & UDP the checksum neutrality of the chosen IPv6
205 case IPPROTO_GRE
: // address means there is no need to update their checksums.
206 case IPPROTO_ESP
: // We do not need to bother looking at GRE/ESP headers,
207 break; // since there is never a checksum to update.
209 case IPPROTO_UDP
: // See above comment, but must also have UDP header...
210 if (data
+ sizeof(*ip4
) + sizeof(struct udphdr
) > data_end
)
212 const struct udphdr
*uh
= (const struct udphdr
*)(ip4
+ 1);
213 // If IPv4/UDP checksum is 0 then fallback to clatd so it can calculate the
214 // checksum. Otherwise the network or more likely the NAT64 gateway might
215 // drop the packet because in most cases IPv6/UDP packets with a zero checksum
216 // are invalid. See RFC 6935. TODO: calculate checksum via bpf_csum_diff()
221 default: // do not know how to handle anything else
224 struct ethhdr eth2
; // used iff is_ethernet
226 eth2
= *eth
; // Copy over the ethernet header (src/dst mac)
227 eth2
.h_proto
= bpf_htons(ETH_P_IPV6
); // But replace the ethertype
229 struct ipv6hdr ip6
= {
230 .version
= 6, // __u8:4
231 .priority
= ip4
->tos
>> 4, // __u8:4
232 .flow_lbl
= {(ip4
->tos
& 0xF) << 4, 0, 0}, // __u8[3]
233 .payload_len
= bpf_htons(bpf_ntohs(ip4
->tot_len
) - 20), // __be16
234 .nexthdr
= ip4
->protocol
, // __u8
235 .hop_limit
= ip4
->ttl
, // __u8
237 ip6
.saddr
.in6_u
.u6_addr32
[0] = bpf_htonl(0x20010db8);
238 ip6
.saddr
.in6_u
.u6_addr32
[1] = 0;
239 ip6
.saddr
.in6_u
.u6_addr32
[2] = 0;
240 ip6
.saddr
.in6_u
.u6_addr32
[3] = bpf_htonl(1);
241 ip6
.daddr
.in6_u
.u6_addr32
[0] = bpf_htonl(0x20010db8);
242 ip6
.daddr
.in6_u
.u6_addr32
[1] = 0;
243 ip6
.daddr
.in6_u
.u6_addr32
[2] = 0;
244 ip6
.daddr
.in6_u
.u6_addr32
[3] = bpf_htonl(2);
246 // Calculate the IPv6 16-bit one's complement checksum of the IPv6 header.
248 // We'll end up with a non-zero sum due to ip6.version == 6
249 for (int i
= 0; i
< sizeof(ip6
) / sizeof(__u16
); ++i
)
250 sum6
+= ((__u16
*)&ip6
)[i
];
252 // Packet mutations begin - point of no return, but if this first modification fails
253 // the packet is probably still pristine, so let clatd handle it.
254 if (bpf_skb_change_proto(skb
, bpf_htons(ETH_P_IPV6
), 0))
257 // This takes care of updating the skb->csum field for a CHECKSUM_COMPLETE packet.
258 // In such a case, skb->csum is a 16-bit one's complement sum of the entire payload,
259 // thus we need to subtract out the ipv4 header's sum, and add in the ipv6 header's sum.
260 // However, we've already verified the ipv4 checksum is correct and thus 0.
261 // Thus we only need to add the ipv6 header's sum.
263 // bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error
264 // (-ENOTSUPP) if it isn't. So we just ignore the return code (see above for more details).
265 bpf_csum_update(skb
, sum6
);
267 // bpf_skb_change_proto() invalidates all pointers - reload them.
268 data
= (void *)(long)skb
->data
;
269 data_end
= (void *)(long)skb
->data_end
;
271 // I cannot think of any valid way for this error condition to trigger, however I do
272 // believe the explicit check is required to keep the in kernel ebpf verifier happy.
273 if (data
+ l2_header_size
+ sizeof(ip6
) > data_end
)
276 struct ethhdr
*new_eth
= data
;
278 // Copy over the updated ethernet header
280 // Copy over the new ipv4 header.
281 *(struct ipv6hdr
*)(new_eth
+ 1) = ip6
;
285 char _license
[] SEC("license") = ("GPL");