1*cfa7b011SJoanne Koong // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2*cfa7b011SJoanne Koong // Copyright (c) 2019, 2020 Cloudflare
3*cfa7b011SJoanne Koong 
4*cfa7b011SJoanne Koong #include <stdbool.h>
5*cfa7b011SJoanne Koong #include <stddef.h>
6*cfa7b011SJoanne Koong #include <stdint.h>
7*cfa7b011SJoanne Koong #include <string.h>
8*cfa7b011SJoanne Koong 
9*cfa7b011SJoanne Koong #include <linux/bpf.h>
10*cfa7b011SJoanne Koong #include <linux/icmp.h>
11*cfa7b011SJoanne Koong #include <linux/icmpv6.h>
12*cfa7b011SJoanne Koong #include <linux/if_ether.h>
13*cfa7b011SJoanne Koong #include <linux/in.h>
14*cfa7b011SJoanne Koong #include <linux/ip.h>
15*cfa7b011SJoanne Koong #include <linux/ipv6.h>
16*cfa7b011SJoanne Koong #include <linux/pkt_cls.h>
17*cfa7b011SJoanne Koong #include <linux/tcp.h>
18*cfa7b011SJoanne Koong #include <linux/udp.h>
19*cfa7b011SJoanne Koong 
20*cfa7b011SJoanne Koong #include <bpf/bpf_helpers.h>
21*cfa7b011SJoanne Koong #include <bpf/bpf_endian.h>
22*cfa7b011SJoanne Koong 
23*cfa7b011SJoanne Koong #include "test_cls_redirect.h"
24*cfa7b011SJoanne Koong #include "bpf_kfuncs.h"
25*cfa7b011SJoanne Koong 
26*cfa7b011SJoanne Koong #define offsetofend(TYPE, MEMBER) \
27*cfa7b011SJoanne Koong 	(offsetof(TYPE, MEMBER) + sizeof((((TYPE *)0)->MEMBER)))
28*cfa7b011SJoanne Koong 
29*cfa7b011SJoanne Koong #define IP_OFFSET_MASK (0x1FFF)
30*cfa7b011SJoanne Koong #define IP_MF (0x2000)
31*cfa7b011SJoanne Koong 
32*cfa7b011SJoanne Koong char _license[] SEC("license") = "Dual BSD/GPL";
33*cfa7b011SJoanne Koong 
34*cfa7b011SJoanne Koong /**
35*cfa7b011SJoanne Koong  * Destination port and IP used for UDP encapsulation.
36*cfa7b011SJoanne Koong  */
37*cfa7b011SJoanne Koong volatile const __be16 ENCAPSULATION_PORT;
38*cfa7b011SJoanne Koong volatile const __be32 ENCAPSULATION_IP;
39*cfa7b011SJoanne Koong 
40*cfa7b011SJoanne Koong typedef struct {
41*cfa7b011SJoanne Koong 	uint64_t processed_packets_total;
42*cfa7b011SJoanne Koong 	uint64_t l3_protocol_packets_total_ipv4;
43*cfa7b011SJoanne Koong 	uint64_t l3_protocol_packets_total_ipv6;
44*cfa7b011SJoanne Koong 	uint64_t l4_protocol_packets_total_tcp;
45*cfa7b011SJoanne Koong 	uint64_t l4_protocol_packets_total_udp;
46*cfa7b011SJoanne Koong 	uint64_t accepted_packets_total_syn;
47*cfa7b011SJoanne Koong 	uint64_t accepted_packets_total_syn_cookies;
48*cfa7b011SJoanne Koong 	uint64_t accepted_packets_total_last_hop;
49*cfa7b011SJoanne Koong 	uint64_t accepted_packets_total_icmp_echo_request;
50*cfa7b011SJoanne Koong 	uint64_t accepted_packets_total_established;
51*cfa7b011SJoanne Koong 	uint64_t forwarded_packets_total_gue;
52*cfa7b011SJoanne Koong 	uint64_t forwarded_packets_total_gre;
53*cfa7b011SJoanne Koong 
54*cfa7b011SJoanne Koong 	uint64_t errors_total_unknown_l3_proto;
55*cfa7b011SJoanne Koong 	uint64_t errors_total_unknown_l4_proto;
56*cfa7b011SJoanne Koong 	uint64_t errors_total_malformed_ip;
57*cfa7b011SJoanne Koong 	uint64_t errors_total_fragmented_ip;
58*cfa7b011SJoanne Koong 	uint64_t errors_total_malformed_icmp;
59*cfa7b011SJoanne Koong 	uint64_t errors_total_unwanted_icmp;
60*cfa7b011SJoanne Koong 	uint64_t errors_total_malformed_icmp_pkt_too_big;
61*cfa7b011SJoanne Koong 	uint64_t errors_total_malformed_tcp;
62*cfa7b011SJoanne Koong 	uint64_t errors_total_malformed_udp;
63*cfa7b011SJoanne Koong 	uint64_t errors_total_icmp_echo_replies;
64*cfa7b011SJoanne Koong 	uint64_t errors_total_malformed_encapsulation;
65*cfa7b011SJoanne Koong 	uint64_t errors_total_encap_adjust_failed;
66*cfa7b011SJoanne Koong 	uint64_t errors_total_encap_buffer_too_small;
67*cfa7b011SJoanne Koong 	uint64_t errors_total_redirect_loop;
68*cfa7b011SJoanne Koong 	uint64_t errors_total_encap_mtu_violate;
69*cfa7b011SJoanne Koong } metrics_t;
70*cfa7b011SJoanne Koong 
71*cfa7b011SJoanne Koong typedef enum {
72*cfa7b011SJoanne Koong 	INVALID = 0,
73*cfa7b011SJoanne Koong 	UNKNOWN,
74*cfa7b011SJoanne Koong 	ECHO_REQUEST,
75*cfa7b011SJoanne Koong 	SYN,
76*cfa7b011SJoanne Koong 	SYN_COOKIE,
77*cfa7b011SJoanne Koong 	ESTABLISHED,
78*cfa7b011SJoanne Koong } verdict_t;
79*cfa7b011SJoanne Koong 
80*cfa7b011SJoanne Koong typedef struct {
81*cfa7b011SJoanne Koong 	uint16_t src, dst;
82*cfa7b011SJoanne Koong } flow_ports_t;
83*cfa7b011SJoanne Koong 
84*cfa7b011SJoanne Koong _Static_assert(
85*cfa7b011SJoanne Koong 	sizeof(flow_ports_t) !=
86*cfa7b011SJoanne Koong 		offsetofend(struct bpf_sock_tuple, ipv4.dport) -
87*cfa7b011SJoanne Koong 			offsetof(struct bpf_sock_tuple, ipv4.sport) - 1,
88*cfa7b011SJoanne Koong 	"flow_ports_t must match sport and dport in struct bpf_sock_tuple");
89*cfa7b011SJoanne Koong _Static_assert(
90*cfa7b011SJoanne Koong 	sizeof(flow_ports_t) !=
91*cfa7b011SJoanne Koong 		offsetofend(struct bpf_sock_tuple, ipv6.dport) -
92*cfa7b011SJoanne Koong 			offsetof(struct bpf_sock_tuple, ipv6.sport) - 1,
93*cfa7b011SJoanne Koong 	"flow_ports_t must match sport and dport in struct bpf_sock_tuple");
94*cfa7b011SJoanne Koong 
95*cfa7b011SJoanne Koong struct iphdr_info {
96*cfa7b011SJoanne Koong 	void *hdr;
97*cfa7b011SJoanne Koong 	__u64 len;
98*cfa7b011SJoanne Koong };
99*cfa7b011SJoanne Koong 
100*cfa7b011SJoanne Koong typedef int ret_t;
101*cfa7b011SJoanne Koong 
102*cfa7b011SJoanne Koong /* This is a bit of a hack. We need a return value which allows us to
103*cfa7b011SJoanne Koong  * indicate that the regular flow of the program should continue,
104*cfa7b011SJoanne Koong  * while allowing functions to use XDP_PASS and XDP_DROP, etc.
105*cfa7b011SJoanne Koong  */
106*cfa7b011SJoanne Koong static const ret_t CONTINUE_PROCESSING = -1;
107*cfa7b011SJoanne Koong 
108*cfa7b011SJoanne Koong /* Convenience macro to call functions which return ret_t.
109*cfa7b011SJoanne Koong  */
110*cfa7b011SJoanne Koong #define MAYBE_RETURN(x)                           \
111*cfa7b011SJoanne Koong 	do {                                      \
112*cfa7b011SJoanne Koong 		ret_t __ret = x;                  \
113*cfa7b011SJoanne Koong 		if (__ret != CONTINUE_PROCESSING) \
114*cfa7b011SJoanne Koong 			return __ret;             \
115*cfa7b011SJoanne Koong 	} while (0)
116*cfa7b011SJoanne Koong 
ipv4_is_fragment(const struct iphdr * ip)117*cfa7b011SJoanne Koong static bool ipv4_is_fragment(const struct iphdr *ip)
118*cfa7b011SJoanne Koong {
119*cfa7b011SJoanne Koong 	uint16_t frag_off = ip->frag_off & bpf_htons(IP_OFFSET_MASK);
120*cfa7b011SJoanne Koong 	return (ip->frag_off & bpf_htons(IP_MF)) != 0 || frag_off > 0;
121*cfa7b011SJoanne Koong }
122*cfa7b011SJoanne Koong 
pkt_parse_ipv4(struct bpf_dynptr * dynptr,__u64 * offset,struct iphdr * iphdr)123*cfa7b011SJoanne Koong static int pkt_parse_ipv4(struct bpf_dynptr *dynptr, __u64 *offset, struct iphdr *iphdr)
124*cfa7b011SJoanne Koong {
125*cfa7b011SJoanne Koong 	if (bpf_dynptr_read(iphdr, sizeof(*iphdr), dynptr, *offset, 0))
126*cfa7b011SJoanne Koong 		return -1;
127*cfa7b011SJoanne Koong 
128*cfa7b011SJoanne Koong 	*offset += sizeof(*iphdr);
129*cfa7b011SJoanne Koong 
130*cfa7b011SJoanne Koong 	if (iphdr->ihl < 5)
131*cfa7b011SJoanne Koong 		return -1;
132*cfa7b011SJoanne Koong 
133*cfa7b011SJoanne Koong 	/* skip ipv4 options */
134*cfa7b011SJoanne Koong 	*offset += (iphdr->ihl - 5) * 4;
135*cfa7b011SJoanne Koong 
136*cfa7b011SJoanne Koong 	return 0;
137*cfa7b011SJoanne Koong }
138*cfa7b011SJoanne Koong 
139*cfa7b011SJoanne Koong /* Parse the L4 ports from a packet, assuming a layout like TCP or UDP. */
pkt_parse_icmp_l4_ports(struct bpf_dynptr * dynptr,__u64 * offset,flow_ports_t * ports)140*cfa7b011SJoanne Koong static bool pkt_parse_icmp_l4_ports(struct bpf_dynptr *dynptr, __u64 *offset, flow_ports_t *ports)
141*cfa7b011SJoanne Koong {
142*cfa7b011SJoanne Koong 	if (bpf_dynptr_read(ports, sizeof(*ports), dynptr, *offset, 0))
143*cfa7b011SJoanne Koong 		return false;
144*cfa7b011SJoanne Koong 
145*cfa7b011SJoanne Koong 	*offset += sizeof(*ports);
146*cfa7b011SJoanne Koong 
147*cfa7b011SJoanne Koong 	/* Ports in the L4 headers are reversed, since we are parsing an ICMP
148*cfa7b011SJoanne Koong 	 * payload which is going towards the eyeball.
149*cfa7b011SJoanne Koong 	 */
150*cfa7b011SJoanne Koong 	uint16_t dst = ports->src;
151*cfa7b011SJoanne Koong 	ports->src = ports->dst;
152*cfa7b011SJoanne Koong 	ports->dst = dst;
153*cfa7b011SJoanne Koong 	return true;
154*cfa7b011SJoanne Koong }
155*cfa7b011SJoanne Koong 
pkt_checksum_fold(uint32_t csum)156*cfa7b011SJoanne Koong static uint16_t pkt_checksum_fold(uint32_t csum)
157*cfa7b011SJoanne Koong {
158*cfa7b011SJoanne Koong 	/* The highest reasonable value for an IPv4 header
159*cfa7b011SJoanne Koong 	 * checksum requires two folds, so we just do that always.
160*cfa7b011SJoanne Koong 	 */
161*cfa7b011SJoanne Koong 	csum = (csum & 0xffff) + (csum >> 16);
162*cfa7b011SJoanne Koong 	csum = (csum & 0xffff) + (csum >> 16);
163*cfa7b011SJoanne Koong 	return (uint16_t)~csum;
164*cfa7b011SJoanne Koong }
165*cfa7b011SJoanne Koong 
pkt_ipv4_checksum(struct iphdr * iph)166*cfa7b011SJoanne Koong static void pkt_ipv4_checksum(struct iphdr *iph)
167*cfa7b011SJoanne Koong {
168*cfa7b011SJoanne Koong 	iph->check = 0;
169*cfa7b011SJoanne Koong 
170*cfa7b011SJoanne Koong 	/* An IP header without options is 20 bytes. Two of those
171*cfa7b011SJoanne Koong 	 * are the checksum, which we always set to zero. Hence,
172*cfa7b011SJoanne Koong 	 * the maximum accumulated value is 18 / 2 * 0xffff = 0x8fff7,
173*cfa7b011SJoanne Koong 	 * which fits in 32 bit.
174*cfa7b011SJoanne Koong 	 */
175*cfa7b011SJoanne Koong 	_Static_assert(sizeof(struct iphdr) == 20, "iphdr must be 20 bytes");
176*cfa7b011SJoanne Koong 	uint32_t acc = 0;
177*cfa7b011SJoanne Koong 	uint16_t *ipw = (uint16_t *)iph;
178*cfa7b011SJoanne Koong 
179*cfa7b011SJoanne Koong 	for (size_t i = 0; i < sizeof(struct iphdr) / 2; i++)
180*cfa7b011SJoanne Koong 		acc += ipw[i];
181*cfa7b011SJoanne Koong 
182*cfa7b011SJoanne Koong 	iph->check = pkt_checksum_fold(acc);
183*cfa7b011SJoanne Koong }
184*cfa7b011SJoanne Koong 
pkt_skip_ipv6_extension_headers(struct bpf_dynptr * dynptr,__u64 * offset,const struct ipv6hdr * ipv6,uint8_t * upper_proto,bool * is_fragment)185*cfa7b011SJoanne Koong static bool pkt_skip_ipv6_extension_headers(struct bpf_dynptr *dynptr, __u64 *offset,
186*cfa7b011SJoanne Koong 					    const struct ipv6hdr *ipv6, uint8_t *upper_proto,
187*cfa7b011SJoanne Koong 					    bool *is_fragment)
188*cfa7b011SJoanne Koong {
189*cfa7b011SJoanne Koong 	/* We understand five extension headers.
190*cfa7b011SJoanne Koong 	 * https://tools.ietf.org/html/rfc8200#section-4.1 states that all
191*cfa7b011SJoanne Koong 	 * headers should occur once, except Destination Options, which may
192*cfa7b011SJoanne Koong 	 * occur twice. Hence we give up after 6 headers.
193*cfa7b011SJoanne Koong 	 */
194*cfa7b011SJoanne Koong 	struct {
195*cfa7b011SJoanne Koong 		uint8_t next;
196*cfa7b011SJoanne Koong 		uint8_t len;
197*cfa7b011SJoanne Koong 	} exthdr = {
198*cfa7b011SJoanne Koong 		.next = ipv6->nexthdr,
199*cfa7b011SJoanne Koong 	};
200*cfa7b011SJoanne Koong 	*is_fragment = false;
201*cfa7b011SJoanne Koong 
202*cfa7b011SJoanne Koong 	for (int i = 0; i < 6; i++) {
203*cfa7b011SJoanne Koong 		switch (exthdr.next) {
204*cfa7b011SJoanne Koong 		case IPPROTO_FRAGMENT:
205*cfa7b011SJoanne Koong 			*is_fragment = true;
206*cfa7b011SJoanne Koong 			/* NB: We don't check that hdrlen == 0 as per spec. */
207*cfa7b011SJoanne Koong 			/* fallthrough; */
208*cfa7b011SJoanne Koong 
209*cfa7b011SJoanne Koong 		case IPPROTO_HOPOPTS:
210*cfa7b011SJoanne Koong 		case IPPROTO_ROUTING:
211*cfa7b011SJoanne Koong 		case IPPROTO_DSTOPTS:
212*cfa7b011SJoanne Koong 		case IPPROTO_MH:
213*cfa7b011SJoanne Koong 			if (bpf_dynptr_read(&exthdr, sizeof(exthdr), dynptr, *offset, 0))
214*cfa7b011SJoanne Koong 				return false;
215*cfa7b011SJoanne Koong 
216*cfa7b011SJoanne Koong 			/* hdrlen is in 8-octet units, and excludes the first 8 octets. */
217*cfa7b011SJoanne Koong 			*offset += (exthdr.len + 1) * 8;
218*cfa7b011SJoanne Koong 
219*cfa7b011SJoanne Koong 			/* Decode next header */
220*cfa7b011SJoanne Koong 			break;
221*cfa7b011SJoanne Koong 
222*cfa7b011SJoanne Koong 		default:
223*cfa7b011SJoanne Koong 			/* The next header is not one of the known extension
224*cfa7b011SJoanne Koong 			 * headers, treat it as the upper layer header.
225*cfa7b011SJoanne Koong 			 *
226*cfa7b011SJoanne Koong 			 * This handles IPPROTO_NONE.
227*cfa7b011SJoanne Koong 			 *
228*cfa7b011SJoanne Koong 			 * Encapsulating Security Payload (50) and Authentication
229*cfa7b011SJoanne Koong 			 * Header (51) also end up here (and will trigger an
230*cfa7b011SJoanne Koong 			 * unknown proto error later). They have a custom header
231*cfa7b011SJoanne Koong 			 * format and seem too esoteric to care about.
232*cfa7b011SJoanne Koong 			 */
233*cfa7b011SJoanne Koong 			*upper_proto = exthdr.next;
234*cfa7b011SJoanne Koong 			return true;
235*cfa7b011SJoanne Koong 		}
236*cfa7b011SJoanne Koong 	}
237*cfa7b011SJoanne Koong 
238*cfa7b011SJoanne Koong 	/* We never found an upper layer header. */
239*cfa7b011SJoanne Koong 	return false;
240*cfa7b011SJoanne Koong }
241*cfa7b011SJoanne Koong 
pkt_parse_ipv6(struct bpf_dynptr * dynptr,__u64 * offset,struct ipv6hdr * ipv6,uint8_t * proto,bool * is_fragment)242*cfa7b011SJoanne Koong static int pkt_parse_ipv6(struct bpf_dynptr *dynptr, __u64 *offset, struct ipv6hdr *ipv6,
243*cfa7b011SJoanne Koong 			  uint8_t *proto, bool *is_fragment)
244*cfa7b011SJoanne Koong {
245*cfa7b011SJoanne Koong 	if (bpf_dynptr_read(ipv6, sizeof(*ipv6), dynptr, *offset, 0))
246*cfa7b011SJoanne Koong 		return -1;
247*cfa7b011SJoanne Koong 
248*cfa7b011SJoanne Koong 	*offset += sizeof(*ipv6);
249*cfa7b011SJoanne Koong 
250*cfa7b011SJoanne Koong 	if (!pkt_skip_ipv6_extension_headers(dynptr, offset, ipv6, proto, is_fragment))
251*cfa7b011SJoanne Koong 		return -1;
252*cfa7b011SJoanne Koong 
253*cfa7b011SJoanne Koong 	return 0;
254*cfa7b011SJoanne Koong }
255*cfa7b011SJoanne Koong 
256*cfa7b011SJoanne Koong /* Global metrics, per CPU
257*cfa7b011SJoanne Koong  */
258*cfa7b011SJoanne Koong struct {
259*cfa7b011SJoanne Koong 	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
260*cfa7b011SJoanne Koong 	__uint(max_entries, 1);
261*cfa7b011SJoanne Koong 	__type(key, unsigned int);
262*cfa7b011SJoanne Koong 	__type(value, metrics_t);
263*cfa7b011SJoanne Koong } metrics_map SEC(".maps");
264*cfa7b011SJoanne Koong 
get_global_metrics(void)265*cfa7b011SJoanne Koong static metrics_t *get_global_metrics(void)
266*cfa7b011SJoanne Koong {
267*cfa7b011SJoanne Koong 	uint64_t key = 0;
268*cfa7b011SJoanne Koong 	return bpf_map_lookup_elem(&metrics_map, &key);
269*cfa7b011SJoanne Koong }
270*cfa7b011SJoanne Koong 
accept_locally(struct __sk_buff * skb,encap_headers_t * encap)271*cfa7b011SJoanne Koong static ret_t accept_locally(struct __sk_buff *skb, encap_headers_t *encap)
272*cfa7b011SJoanne Koong {
273*cfa7b011SJoanne Koong 	const int payload_off =
274*cfa7b011SJoanne Koong 		sizeof(*encap) +
275*cfa7b011SJoanne Koong 		sizeof(struct in_addr) * encap->unigue.hop_count;
276*cfa7b011SJoanne Koong 	int32_t encap_overhead = payload_off - sizeof(struct ethhdr);
277*cfa7b011SJoanne Koong 
278*cfa7b011SJoanne Koong 	/* Changing the ethertype if the encapsulated packet is ipv6 */
279*cfa7b011SJoanne Koong 	if (encap->gue.proto_ctype == IPPROTO_IPV6)
280*cfa7b011SJoanne Koong 		encap->eth.h_proto = bpf_htons(ETH_P_IPV6);
281*cfa7b011SJoanne Koong 
282*cfa7b011SJoanne Koong 	if (bpf_skb_adjust_room(skb, -encap_overhead, BPF_ADJ_ROOM_MAC,
283*cfa7b011SJoanne Koong 				BPF_F_ADJ_ROOM_FIXED_GSO |
284*cfa7b011SJoanne Koong 				BPF_F_ADJ_ROOM_NO_CSUM_RESET) ||
285*cfa7b011SJoanne Koong 	    bpf_csum_level(skb, BPF_CSUM_LEVEL_DEC))
286*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
287*cfa7b011SJoanne Koong 
288*cfa7b011SJoanne Koong 	return bpf_redirect(skb->ifindex, BPF_F_INGRESS);
289*cfa7b011SJoanne Koong }
290*cfa7b011SJoanne Koong 
forward_with_gre(struct __sk_buff * skb,struct bpf_dynptr * dynptr,encap_headers_t * encap,struct in_addr * next_hop,metrics_t * metrics)291*cfa7b011SJoanne Koong static ret_t forward_with_gre(struct __sk_buff *skb, struct bpf_dynptr *dynptr,
292*cfa7b011SJoanne Koong 			      encap_headers_t *encap, struct in_addr *next_hop,
293*cfa7b011SJoanne Koong 			      metrics_t *metrics)
294*cfa7b011SJoanne Koong {
295*cfa7b011SJoanne Koong 	const int payload_off =
296*cfa7b011SJoanne Koong 		sizeof(*encap) +
297*cfa7b011SJoanne Koong 		sizeof(struct in_addr) * encap->unigue.hop_count;
298*cfa7b011SJoanne Koong 	int32_t encap_overhead =
299*cfa7b011SJoanne Koong 		payload_off - sizeof(struct ethhdr) - sizeof(struct iphdr);
300*cfa7b011SJoanne Koong 	int32_t delta = sizeof(struct gre_base_hdr) - encap_overhead;
301*cfa7b011SJoanne Koong 	__u8 encap_buffer[sizeof(encap_gre_t)] = {};
302*cfa7b011SJoanne Koong 	uint16_t proto = ETH_P_IP;
303*cfa7b011SJoanne Koong 	uint32_t mtu_len = 0;
304*cfa7b011SJoanne Koong 	encap_gre_t *encap_gre;
305*cfa7b011SJoanne Koong 
306*cfa7b011SJoanne Koong 	metrics->forwarded_packets_total_gre++;
307*cfa7b011SJoanne Koong 
308*cfa7b011SJoanne Koong 	/* Loop protection: the inner packet's TTL is decremented as a safeguard
309*cfa7b011SJoanne Koong 	 * against any forwarding loop. As the only interesting field is the TTL
310*cfa7b011SJoanne Koong 	 * hop limit for IPv6, it is easier to use bpf_skb_load_bytes/bpf_skb_store_bytes
311*cfa7b011SJoanne Koong 	 * as they handle the split packets if needed (no need for the data to be
312*cfa7b011SJoanne Koong 	 * in the linear section).
313*cfa7b011SJoanne Koong 	 */
314*cfa7b011SJoanne Koong 	if (encap->gue.proto_ctype == IPPROTO_IPV6) {
315*cfa7b011SJoanne Koong 		proto = ETH_P_IPV6;
316*cfa7b011SJoanne Koong 		uint8_t ttl;
317*cfa7b011SJoanne Koong 		int rc;
318*cfa7b011SJoanne Koong 
319*cfa7b011SJoanne Koong 		rc = bpf_skb_load_bytes(
320*cfa7b011SJoanne Koong 			skb, payload_off + offsetof(struct ipv6hdr, hop_limit),
321*cfa7b011SJoanne Koong 			&ttl, 1);
322*cfa7b011SJoanne Koong 		if (rc != 0) {
323*cfa7b011SJoanne Koong 			metrics->errors_total_malformed_encapsulation++;
324*cfa7b011SJoanne Koong 			return TC_ACT_SHOT;
325*cfa7b011SJoanne Koong 		}
326*cfa7b011SJoanne Koong 
327*cfa7b011SJoanne Koong 		if (ttl == 0) {
328*cfa7b011SJoanne Koong 			metrics->errors_total_redirect_loop++;
329*cfa7b011SJoanne Koong 			return TC_ACT_SHOT;
330*cfa7b011SJoanne Koong 		}
331*cfa7b011SJoanne Koong 
332*cfa7b011SJoanne Koong 		ttl--;
333*cfa7b011SJoanne Koong 		rc = bpf_skb_store_bytes(
334*cfa7b011SJoanne Koong 			skb, payload_off + offsetof(struct ipv6hdr, hop_limit),
335*cfa7b011SJoanne Koong 			&ttl, 1, 0);
336*cfa7b011SJoanne Koong 		if (rc != 0) {
337*cfa7b011SJoanne Koong 			metrics->errors_total_malformed_encapsulation++;
338*cfa7b011SJoanne Koong 			return TC_ACT_SHOT;
339*cfa7b011SJoanne Koong 		}
340*cfa7b011SJoanne Koong 	} else {
341*cfa7b011SJoanne Koong 		uint8_t ttl;
342*cfa7b011SJoanne Koong 		int rc;
343*cfa7b011SJoanne Koong 
344*cfa7b011SJoanne Koong 		rc = bpf_skb_load_bytes(
345*cfa7b011SJoanne Koong 			skb, payload_off + offsetof(struct iphdr, ttl), &ttl,
346*cfa7b011SJoanne Koong 			1);
347*cfa7b011SJoanne Koong 		if (rc != 0) {
348*cfa7b011SJoanne Koong 			metrics->errors_total_malformed_encapsulation++;
349*cfa7b011SJoanne Koong 			return TC_ACT_SHOT;
350*cfa7b011SJoanne Koong 		}
351*cfa7b011SJoanne Koong 
352*cfa7b011SJoanne Koong 		if (ttl == 0) {
353*cfa7b011SJoanne Koong 			metrics->errors_total_redirect_loop++;
354*cfa7b011SJoanne Koong 			return TC_ACT_SHOT;
355*cfa7b011SJoanne Koong 		}
356*cfa7b011SJoanne Koong 
357*cfa7b011SJoanne Koong 		/* IPv4 also has a checksum to patch. While the TTL is only one byte,
358*cfa7b011SJoanne Koong 		 * this function only works for 2 and 4 bytes arguments (the result is
359*cfa7b011SJoanne Koong 		 * the same).
360*cfa7b011SJoanne Koong 		 */
361*cfa7b011SJoanne Koong 		rc = bpf_l3_csum_replace(
362*cfa7b011SJoanne Koong 			skb, payload_off + offsetof(struct iphdr, check), ttl,
363*cfa7b011SJoanne Koong 			ttl - 1, 2);
364*cfa7b011SJoanne Koong 		if (rc != 0) {
365*cfa7b011SJoanne Koong 			metrics->errors_total_malformed_encapsulation++;
366*cfa7b011SJoanne Koong 			return TC_ACT_SHOT;
367*cfa7b011SJoanne Koong 		}
368*cfa7b011SJoanne Koong 
369*cfa7b011SJoanne Koong 		ttl--;
370*cfa7b011SJoanne Koong 		rc = bpf_skb_store_bytes(
371*cfa7b011SJoanne Koong 			skb, payload_off + offsetof(struct iphdr, ttl), &ttl, 1,
372*cfa7b011SJoanne Koong 			0);
373*cfa7b011SJoanne Koong 		if (rc != 0) {
374*cfa7b011SJoanne Koong 			metrics->errors_total_malformed_encapsulation++;
375*cfa7b011SJoanne Koong 			return TC_ACT_SHOT;
376*cfa7b011SJoanne Koong 		}
377*cfa7b011SJoanne Koong 	}
378*cfa7b011SJoanne Koong 
379*cfa7b011SJoanne Koong 	if (bpf_check_mtu(skb, skb->ifindex, &mtu_len, delta, 0)) {
380*cfa7b011SJoanne Koong 		metrics->errors_total_encap_mtu_violate++;
381*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
382*cfa7b011SJoanne Koong 	}
383*cfa7b011SJoanne Koong 
384*cfa7b011SJoanne Koong 	if (bpf_skb_adjust_room(skb, delta, BPF_ADJ_ROOM_NET,
385*cfa7b011SJoanne Koong 				BPF_F_ADJ_ROOM_FIXED_GSO |
386*cfa7b011SJoanne Koong 				BPF_F_ADJ_ROOM_NO_CSUM_RESET) ||
387*cfa7b011SJoanne Koong 	    bpf_csum_level(skb, BPF_CSUM_LEVEL_INC)) {
388*cfa7b011SJoanne Koong 		metrics->errors_total_encap_adjust_failed++;
389*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
390*cfa7b011SJoanne Koong 	}
391*cfa7b011SJoanne Koong 
392*cfa7b011SJoanne Koong 	if (bpf_skb_pull_data(skb, sizeof(encap_gre_t))) {
393*cfa7b011SJoanne Koong 		metrics->errors_total_encap_buffer_too_small++;
394*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
395*cfa7b011SJoanne Koong 	}
396*cfa7b011SJoanne Koong 
397*cfa7b011SJoanne Koong 	encap_gre = bpf_dynptr_slice_rdwr(dynptr, 0, encap_buffer, sizeof(encap_buffer));
398*cfa7b011SJoanne Koong 	if (!encap_gre) {
399*cfa7b011SJoanne Koong 		metrics->errors_total_encap_buffer_too_small++;
400*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
401*cfa7b011SJoanne Koong 	}
402*cfa7b011SJoanne Koong 
403*cfa7b011SJoanne Koong 	encap_gre->ip.protocol = IPPROTO_GRE;
404*cfa7b011SJoanne Koong 	encap_gre->ip.daddr = next_hop->s_addr;
405*cfa7b011SJoanne Koong 	encap_gre->ip.saddr = ENCAPSULATION_IP;
406*cfa7b011SJoanne Koong 	encap_gre->ip.tot_len =
407*cfa7b011SJoanne Koong 		bpf_htons(bpf_ntohs(encap_gre->ip.tot_len) + delta);
408*cfa7b011SJoanne Koong 	encap_gre->gre.flags = 0;
409*cfa7b011SJoanne Koong 	encap_gre->gre.protocol = bpf_htons(proto);
410*cfa7b011SJoanne Koong 	pkt_ipv4_checksum((void *)&encap_gre->ip);
411*cfa7b011SJoanne Koong 
412*cfa7b011SJoanne Koong 	if (encap_gre == encap_buffer)
413*cfa7b011SJoanne Koong 		bpf_dynptr_write(dynptr, 0, encap_buffer, sizeof(encap_buffer), 0);
414*cfa7b011SJoanne Koong 
415*cfa7b011SJoanne Koong 	return bpf_redirect(skb->ifindex, 0);
416*cfa7b011SJoanne Koong }
417*cfa7b011SJoanne Koong 
forward_to_next_hop(struct __sk_buff * skb,struct bpf_dynptr * dynptr,encap_headers_t * encap,struct in_addr * next_hop,metrics_t * metrics)418*cfa7b011SJoanne Koong static ret_t forward_to_next_hop(struct __sk_buff *skb, struct bpf_dynptr *dynptr,
419*cfa7b011SJoanne Koong 				 encap_headers_t *encap, struct in_addr *next_hop,
420*cfa7b011SJoanne Koong 				 metrics_t *metrics)
421*cfa7b011SJoanne Koong {
422*cfa7b011SJoanne Koong 	/* swap L2 addresses */
423*cfa7b011SJoanne Koong 	/* This assumes that packets are received from a router.
424*cfa7b011SJoanne Koong 	 * So just swapping the MAC addresses here will make the packet go back to
425*cfa7b011SJoanne Koong 	 * the router, which will send it to the appropriate machine.
426*cfa7b011SJoanne Koong 	 */
427*cfa7b011SJoanne Koong 	unsigned char temp[ETH_ALEN];
428*cfa7b011SJoanne Koong 	memcpy(temp, encap->eth.h_dest, sizeof(temp));
429*cfa7b011SJoanne Koong 	memcpy(encap->eth.h_dest, encap->eth.h_source,
430*cfa7b011SJoanne Koong 	       sizeof(encap->eth.h_dest));
431*cfa7b011SJoanne Koong 	memcpy(encap->eth.h_source, temp, sizeof(encap->eth.h_source));
432*cfa7b011SJoanne Koong 
433*cfa7b011SJoanne Koong 	if (encap->unigue.next_hop == encap->unigue.hop_count - 1 &&
434*cfa7b011SJoanne Koong 	    encap->unigue.last_hop_gre) {
435*cfa7b011SJoanne Koong 		return forward_with_gre(skb, dynptr, encap, next_hop, metrics);
436*cfa7b011SJoanne Koong 	}
437*cfa7b011SJoanne Koong 
438*cfa7b011SJoanne Koong 	metrics->forwarded_packets_total_gue++;
439*cfa7b011SJoanne Koong 	uint32_t old_saddr = encap->ip.saddr;
440*cfa7b011SJoanne Koong 	encap->ip.saddr = encap->ip.daddr;
441*cfa7b011SJoanne Koong 	encap->ip.daddr = next_hop->s_addr;
442*cfa7b011SJoanne Koong 	if (encap->unigue.next_hop < encap->unigue.hop_count) {
443*cfa7b011SJoanne Koong 		encap->unigue.next_hop++;
444*cfa7b011SJoanne Koong 	}
445*cfa7b011SJoanne Koong 
446*cfa7b011SJoanne Koong 	/* Remove ip->saddr, add next_hop->s_addr */
447*cfa7b011SJoanne Koong 	const uint64_t off = offsetof(typeof(*encap), ip.check);
448*cfa7b011SJoanne Koong 	int ret = bpf_l3_csum_replace(skb, off, old_saddr, next_hop->s_addr, 4);
449*cfa7b011SJoanne Koong 	if (ret < 0) {
450*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
451*cfa7b011SJoanne Koong 	}
452*cfa7b011SJoanne Koong 
453*cfa7b011SJoanne Koong 	return bpf_redirect(skb->ifindex, 0);
454*cfa7b011SJoanne Koong }
455*cfa7b011SJoanne Koong 
skip_next_hops(__u64 * offset,int n)456*cfa7b011SJoanne Koong static ret_t skip_next_hops(__u64 *offset, int n)
457*cfa7b011SJoanne Koong {
458*cfa7b011SJoanne Koong 	switch (n) {
459*cfa7b011SJoanne Koong 	case 1:
460*cfa7b011SJoanne Koong 		*offset += sizeof(struct in_addr);
461*cfa7b011SJoanne Koong 	case 0:
462*cfa7b011SJoanne Koong 		return CONTINUE_PROCESSING;
463*cfa7b011SJoanne Koong 
464*cfa7b011SJoanne Koong 	default:
465*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
466*cfa7b011SJoanne Koong 	}
467*cfa7b011SJoanne Koong }
468*cfa7b011SJoanne Koong 
469*cfa7b011SJoanne Koong /* Get the next hop from the GLB header.
470*cfa7b011SJoanne Koong  *
471*cfa7b011SJoanne Koong  * Sets next_hop->s_addr to 0 if there are no more hops left.
472*cfa7b011SJoanne Koong  * pkt is positioned just after the variable length GLB header
473*cfa7b011SJoanne Koong  * iff the call is successful.
474*cfa7b011SJoanne Koong  */
get_next_hop(struct bpf_dynptr * dynptr,__u64 * offset,encap_headers_t * encap,struct in_addr * next_hop)475*cfa7b011SJoanne Koong static ret_t get_next_hop(struct bpf_dynptr *dynptr, __u64 *offset, encap_headers_t *encap,
476*cfa7b011SJoanne Koong 			  struct in_addr *next_hop)
477*cfa7b011SJoanne Koong {
478*cfa7b011SJoanne Koong 	if (encap->unigue.next_hop > encap->unigue.hop_count)
479*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
480*cfa7b011SJoanne Koong 
481*cfa7b011SJoanne Koong 	/* Skip "used" next hops. */
482*cfa7b011SJoanne Koong 	MAYBE_RETURN(skip_next_hops(offset, encap->unigue.next_hop));
483*cfa7b011SJoanne Koong 
484*cfa7b011SJoanne Koong 	if (encap->unigue.next_hop == encap->unigue.hop_count) {
485*cfa7b011SJoanne Koong 		/* No more next hops, we are at the end of the GLB header. */
486*cfa7b011SJoanne Koong 		next_hop->s_addr = 0;
487*cfa7b011SJoanne Koong 		return CONTINUE_PROCESSING;
488*cfa7b011SJoanne Koong 	}
489*cfa7b011SJoanne Koong 
490*cfa7b011SJoanne Koong 	if (bpf_dynptr_read(next_hop, sizeof(*next_hop), dynptr, *offset, 0))
491*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
492*cfa7b011SJoanne Koong 
493*cfa7b011SJoanne Koong 	*offset += sizeof(*next_hop);
494*cfa7b011SJoanne Koong 
495*cfa7b011SJoanne Koong 	/* Skip the remainig next hops (may be zero). */
496*cfa7b011SJoanne Koong 	return skip_next_hops(offset, encap->unigue.hop_count - encap->unigue.next_hop - 1);
497*cfa7b011SJoanne Koong }
498*cfa7b011SJoanne Koong 
499*cfa7b011SJoanne Koong /* Fill a bpf_sock_tuple to be used with the socket lookup functions.
500*cfa7b011SJoanne Koong  * This is a kludge that let's us work around verifier limitations:
501*cfa7b011SJoanne Koong  *
502*cfa7b011SJoanne Koong  *    fill_tuple(&t, foo, sizeof(struct iphdr), 123, 321)
503*cfa7b011SJoanne Koong  *
504*cfa7b011SJoanne Koong  * clang will substitue a costant for sizeof, which allows the verifier
505*cfa7b011SJoanne Koong  * to track it's value. Based on this, it can figure out the constant
506*cfa7b011SJoanne Koong  * return value, and calling code works while still being "generic" to
507*cfa7b011SJoanne Koong  * IPv4 and IPv6.
508*cfa7b011SJoanne Koong  */
fill_tuple(struct bpf_sock_tuple * tuple,void * iph,uint64_t iphlen,uint16_t sport,uint16_t dport)509*cfa7b011SJoanne Koong static uint64_t fill_tuple(struct bpf_sock_tuple *tuple, void *iph,
510*cfa7b011SJoanne Koong 				    uint64_t iphlen, uint16_t sport, uint16_t dport)
511*cfa7b011SJoanne Koong {
512*cfa7b011SJoanne Koong 	switch (iphlen) {
513*cfa7b011SJoanne Koong 	case sizeof(struct iphdr): {
514*cfa7b011SJoanne Koong 		struct iphdr *ipv4 = (struct iphdr *)iph;
515*cfa7b011SJoanne Koong 		tuple->ipv4.daddr = ipv4->daddr;
516*cfa7b011SJoanne Koong 		tuple->ipv4.saddr = ipv4->saddr;
517*cfa7b011SJoanne Koong 		tuple->ipv4.sport = sport;
518*cfa7b011SJoanne Koong 		tuple->ipv4.dport = dport;
519*cfa7b011SJoanne Koong 		return sizeof(tuple->ipv4);
520*cfa7b011SJoanne Koong 	}
521*cfa7b011SJoanne Koong 
522*cfa7b011SJoanne Koong 	case sizeof(struct ipv6hdr): {
523*cfa7b011SJoanne Koong 		struct ipv6hdr *ipv6 = (struct ipv6hdr *)iph;
524*cfa7b011SJoanne Koong 		memcpy(&tuple->ipv6.daddr, &ipv6->daddr,
525*cfa7b011SJoanne Koong 		       sizeof(tuple->ipv6.daddr));
526*cfa7b011SJoanne Koong 		memcpy(&tuple->ipv6.saddr, &ipv6->saddr,
527*cfa7b011SJoanne Koong 		       sizeof(tuple->ipv6.saddr));
528*cfa7b011SJoanne Koong 		tuple->ipv6.sport = sport;
529*cfa7b011SJoanne Koong 		tuple->ipv6.dport = dport;
530*cfa7b011SJoanne Koong 		return sizeof(tuple->ipv6);
531*cfa7b011SJoanne Koong 	}
532*cfa7b011SJoanne Koong 
533*cfa7b011SJoanne Koong 	default:
534*cfa7b011SJoanne Koong 		return 0;
535*cfa7b011SJoanne Koong 	}
536*cfa7b011SJoanne Koong }
537*cfa7b011SJoanne Koong 
classify_tcp(struct __sk_buff * skb,struct bpf_sock_tuple * tuple,uint64_t tuplen,void * iph,struct tcphdr * tcp)538*cfa7b011SJoanne Koong static verdict_t classify_tcp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple,
539*cfa7b011SJoanne Koong 			      uint64_t tuplen, void *iph, struct tcphdr *tcp)
540*cfa7b011SJoanne Koong {
541*cfa7b011SJoanne Koong 	struct bpf_sock *sk =
542*cfa7b011SJoanne Koong 		bpf_skc_lookup_tcp(skb, tuple, tuplen, BPF_F_CURRENT_NETNS, 0);
543*cfa7b011SJoanne Koong 
544*cfa7b011SJoanne Koong 	if (sk == NULL)
545*cfa7b011SJoanne Koong 		return UNKNOWN;
546*cfa7b011SJoanne Koong 
547*cfa7b011SJoanne Koong 	if (sk->state != BPF_TCP_LISTEN) {
548*cfa7b011SJoanne Koong 		bpf_sk_release(sk);
549*cfa7b011SJoanne Koong 		return ESTABLISHED;
550*cfa7b011SJoanne Koong 	}
551*cfa7b011SJoanne Koong 
552*cfa7b011SJoanne Koong 	if (iph != NULL && tcp != NULL) {
553*cfa7b011SJoanne Koong 		/* Kludge: we've run out of arguments, but need the length of the ip header. */
554*cfa7b011SJoanne Koong 		uint64_t iphlen = sizeof(struct iphdr);
555*cfa7b011SJoanne Koong 
556*cfa7b011SJoanne Koong 		if (tuplen == sizeof(tuple->ipv6))
557*cfa7b011SJoanne Koong 			iphlen = sizeof(struct ipv6hdr);
558*cfa7b011SJoanne Koong 
559*cfa7b011SJoanne Koong 		if (bpf_tcp_check_syncookie(sk, iph, iphlen, tcp,
560*cfa7b011SJoanne Koong 					    sizeof(*tcp)) == 0) {
561*cfa7b011SJoanne Koong 			bpf_sk_release(sk);
562*cfa7b011SJoanne Koong 			return SYN_COOKIE;
563*cfa7b011SJoanne Koong 		}
564*cfa7b011SJoanne Koong 	}
565*cfa7b011SJoanne Koong 
566*cfa7b011SJoanne Koong 	bpf_sk_release(sk);
567*cfa7b011SJoanne Koong 	return UNKNOWN;
568*cfa7b011SJoanne Koong }
569*cfa7b011SJoanne Koong 
classify_udp(struct __sk_buff * skb,struct bpf_sock_tuple * tuple,uint64_t tuplen)570*cfa7b011SJoanne Koong static verdict_t classify_udp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple, uint64_t tuplen)
571*cfa7b011SJoanne Koong {
572*cfa7b011SJoanne Koong 	struct bpf_sock *sk =
573*cfa7b011SJoanne Koong 		bpf_sk_lookup_udp(skb, tuple, tuplen, BPF_F_CURRENT_NETNS, 0);
574*cfa7b011SJoanne Koong 
575*cfa7b011SJoanne Koong 	if (sk == NULL)
576*cfa7b011SJoanne Koong 		return UNKNOWN;
577*cfa7b011SJoanne Koong 
578*cfa7b011SJoanne Koong 	if (sk->state == BPF_TCP_ESTABLISHED) {
579*cfa7b011SJoanne Koong 		bpf_sk_release(sk);
580*cfa7b011SJoanne Koong 		return ESTABLISHED;
581*cfa7b011SJoanne Koong 	}
582*cfa7b011SJoanne Koong 
583*cfa7b011SJoanne Koong 	bpf_sk_release(sk);
584*cfa7b011SJoanne Koong 	return UNKNOWN;
585*cfa7b011SJoanne Koong }
586*cfa7b011SJoanne Koong 
classify_icmp(struct __sk_buff * skb,uint8_t proto,struct bpf_sock_tuple * tuple,uint64_t tuplen,metrics_t * metrics)587*cfa7b011SJoanne Koong static verdict_t classify_icmp(struct __sk_buff *skb, uint8_t proto, struct bpf_sock_tuple *tuple,
588*cfa7b011SJoanne Koong 			       uint64_t tuplen, metrics_t *metrics)
589*cfa7b011SJoanne Koong {
590*cfa7b011SJoanne Koong 	switch (proto) {
591*cfa7b011SJoanne Koong 	case IPPROTO_TCP:
592*cfa7b011SJoanne Koong 		return classify_tcp(skb, tuple, tuplen, NULL, NULL);
593*cfa7b011SJoanne Koong 
594*cfa7b011SJoanne Koong 	case IPPROTO_UDP:
595*cfa7b011SJoanne Koong 		return classify_udp(skb, tuple, tuplen);
596*cfa7b011SJoanne Koong 
597*cfa7b011SJoanne Koong 	default:
598*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_icmp++;
599*cfa7b011SJoanne Koong 		return INVALID;
600*cfa7b011SJoanne Koong 	}
601*cfa7b011SJoanne Koong }
602*cfa7b011SJoanne Koong 
process_icmpv4(struct __sk_buff * skb,struct bpf_dynptr * dynptr,__u64 * offset,metrics_t * metrics)603*cfa7b011SJoanne Koong static verdict_t process_icmpv4(struct __sk_buff *skb, struct bpf_dynptr *dynptr, __u64 *offset,
604*cfa7b011SJoanne Koong 				metrics_t *metrics)
605*cfa7b011SJoanne Koong {
606*cfa7b011SJoanne Koong 	struct icmphdr icmp;
607*cfa7b011SJoanne Koong 	struct iphdr ipv4;
608*cfa7b011SJoanne Koong 
609*cfa7b011SJoanne Koong 	if (bpf_dynptr_read(&icmp, sizeof(icmp), dynptr, *offset, 0)) {
610*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_icmp++;
611*cfa7b011SJoanne Koong 		return INVALID;
612*cfa7b011SJoanne Koong 	}
613*cfa7b011SJoanne Koong 
614*cfa7b011SJoanne Koong 	*offset += sizeof(icmp);
615*cfa7b011SJoanne Koong 
616*cfa7b011SJoanne Koong 	/* We should never receive encapsulated echo replies. */
617*cfa7b011SJoanne Koong 	if (icmp.type == ICMP_ECHOREPLY) {
618*cfa7b011SJoanne Koong 		metrics->errors_total_icmp_echo_replies++;
619*cfa7b011SJoanne Koong 		return INVALID;
620*cfa7b011SJoanne Koong 	}
621*cfa7b011SJoanne Koong 
622*cfa7b011SJoanne Koong 	if (icmp.type == ICMP_ECHO)
623*cfa7b011SJoanne Koong 		return ECHO_REQUEST;
624*cfa7b011SJoanne Koong 
625*cfa7b011SJoanne Koong 	if (icmp.type != ICMP_DEST_UNREACH || icmp.code != ICMP_FRAG_NEEDED) {
626*cfa7b011SJoanne Koong 		metrics->errors_total_unwanted_icmp++;
627*cfa7b011SJoanne Koong 		return INVALID;
628*cfa7b011SJoanne Koong 	}
629*cfa7b011SJoanne Koong 
630*cfa7b011SJoanne Koong 	if (pkt_parse_ipv4(dynptr, offset, &ipv4)) {
631*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_icmp_pkt_too_big++;
632*cfa7b011SJoanne Koong 		return INVALID;
633*cfa7b011SJoanne Koong 	}
634*cfa7b011SJoanne Koong 
635*cfa7b011SJoanne Koong 	/* The source address in the outer IP header is from the entity that
636*cfa7b011SJoanne Koong 	 * originated the ICMP message. Use the original IP header to restore
637*cfa7b011SJoanne Koong 	 * the correct flow tuple.
638*cfa7b011SJoanne Koong 	 */
639*cfa7b011SJoanne Koong 	struct bpf_sock_tuple tuple;
640*cfa7b011SJoanne Koong 	tuple.ipv4.saddr = ipv4.daddr;
641*cfa7b011SJoanne Koong 	tuple.ipv4.daddr = ipv4.saddr;
642*cfa7b011SJoanne Koong 
643*cfa7b011SJoanne Koong 	if (!pkt_parse_icmp_l4_ports(dynptr, offset, (flow_ports_t *)&tuple.ipv4.sport)) {
644*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_icmp_pkt_too_big++;
645*cfa7b011SJoanne Koong 		return INVALID;
646*cfa7b011SJoanne Koong 	}
647*cfa7b011SJoanne Koong 
648*cfa7b011SJoanne Koong 	return classify_icmp(skb, ipv4.protocol, &tuple,
649*cfa7b011SJoanne Koong 			     sizeof(tuple.ipv4), metrics);
650*cfa7b011SJoanne Koong }
651*cfa7b011SJoanne Koong 
process_icmpv6(struct bpf_dynptr * dynptr,__u64 * offset,struct __sk_buff * skb,metrics_t * metrics)652*cfa7b011SJoanne Koong static verdict_t process_icmpv6(struct bpf_dynptr *dynptr, __u64 *offset, struct __sk_buff *skb,
653*cfa7b011SJoanne Koong 				metrics_t *metrics)
654*cfa7b011SJoanne Koong {
655*cfa7b011SJoanne Koong 	struct bpf_sock_tuple tuple;
656*cfa7b011SJoanne Koong 	struct ipv6hdr ipv6;
657*cfa7b011SJoanne Koong 	struct icmp6hdr icmp6;
658*cfa7b011SJoanne Koong 	bool is_fragment;
659*cfa7b011SJoanne Koong 	uint8_t l4_proto;
660*cfa7b011SJoanne Koong 
661*cfa7b011SJoanne Koong 	if (bpf_dynptr_read(&icmp6, sizeof(icmp6), dynptr, *offset, 0)) {
662*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_icmp++;
663*cfa7b011SJoanne Koong 		return INVALID;
664*cfa7b011SJoanne Koong 	}
665*cfa7b011SJoanne Koong 
666*cfa7b011SJoanne Koong 	/* We should never receive encapsulated echo replies. */
667*cfa7b011SJoanne Koong 	if (icmp6.icmp6_type == ICMPV6_ECHO_REPLY) {
668*cfa7b011SJoanne Koong 		metrics->errors_total_icmp_echo_replies++;
669*cfa7b011SJoanne Koong 		return INVALID;
670*cfa7b011SJoanne Koong 	}
671*cfa7b011SJoanne Koong 
672*cfa7b011SJoanne Koong 	if (icmp6.icmp6_type == ICMPV6_ECHO_REQUEST) {
673*cfa7b011SJoanne Koong 		return ECHO_REQUEST;
674*cfa7b011SJoanne Koong 	}
675*cfa7b011SJoanne Koong 
676*cfa7b011SJoanne Koong 	if (icmp6.icmp6_type != ICMPV6_PKT_TOOBIG) {
677*cfa7b011SJoanne Koong 		metrics->errors_total_unwanted_icmp++;
678*cfa7b011SJoanne Koong 		return INVALID;
679*cfa7b011SJoanne Koong 	}
680*cfa7b011SJoanne Koong 
681*cfa7b011SJoanne Koong 	if (pkt_parse_ipv6(dynptr, offset, &ipv6, &l4_proto, &is_fragment)) {
682*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_icmp_pkt_too_big++;
683*cfa7b011SJoanne Koong 		return INVALID;
684*cfa7b011SJoanne Koong 	}
685*cfa7b011SJoanne Koong 
686*cfa7b011SJoanne Koong 	if (is_fragment) {
687*cfa7b011SJoanne Koong 		metrics->errors_total_fragmented_ip++;
688*cfa7b011SJoanne Koong 		return INVALID;
689*cfa7b011SJoanne Koong 	}
690*cfa7b011SJoanne Koong 
691*cfa7b011SJoanne Koong 	/* Swap source and dest addresses. */
692*cfa7b011SJoanne Koong 	memcpy(&tuple.ipv6.saddr, &ipv6.daddr, sizeof(tuple.ipv6.saddr));
693*cfa7b011SJoanne Koong 	memcpy(&tuple.ipv6.daddr, &ipv6.saddr, sizeof(tuple.ipv6.daddr));
694*cfa7b011SJoanne Koong 
695*cfa7b011SJoanne Koong 	if (!pkt_parse_icmp_l4_ports(dynptr, offset, (flow_ports_t *)&tuple.ipv6.sport)) {
696*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_icmp_pkt_too_big++;
697*cfa7b011SJoanne Koong 		return INVALID;
698*cfa7b011SJoanne Koong 	}
699*cfa7b011SJoanne Koong 
700*cfa7b011SJoanne Koong 	return classify_icmp(skb, l4_proto, &tuple, sizeof(tuple.ipv6),
701*cfa7b011SJoanne Koong 			     metrics);
702*cfa7b011SJoanne Koong }
703*cfa7b011SJoanne Koong 
process_tcp(struct bpf_dynptr * dynptr,__u64 * offset,struct __sk_buff * skb,struct iphdr_info * info,metrics_t * metrics)704*cfa7b011SJoanne Koong static verdict_t process_tcp(struct bpf_dynptr *dynptr, __u64 *offset, struct __sk_buff *skb,
705*cfa7b011SJoanne Koong 			     struct iphdr_info *info, metrics_t *metrics)
706*cfa7b011SJoanne Koong {
707*cfa7b011SJoanne Koong 	struct bpf_sock_tuple tuple;
708*cfa7b011SJoanne Koong 	struct tcphdr tcp;
709*cfa7b011SJoanne Koong 	uint64_t tuplen;
710*cfa7b011SJoanne Koong 
711*cfa7b011SJoanne Koong 	metrics->l4_protocol_packets_total_tcp++;
712*cfa7b011SJoanne Koong 
713*cfa7b011SJoanne Koong 	if (bpf_dynptr_read(&tcp, sizeof(tcp), dynptr, *offset, 0)) {
714*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_tcp++;
715*cfa7b011SJoanne Koong 		return INVALID;
716*cfa7b011SJoanne Koong 	}
717*cfa7b011SJoanne Koong 
718*cfa7b011SJoanne Koong 	*offset += sizeof(tcp);
719*cfa7b011SJoanne Koong 
720*cfa7b011SJoanne Koong 	if (tcp.syn)
721*cfa7b011SJoanne Koong 		return SYN;
722*cfa7b011SJoanne Koong 
723*cfa7b011SJoanne Koong 	tuplen = fill_tuple(&tuple, info->hdr, info->len, tcp.source, tcp.dest);
724*cfa7b011SJoanne Koong 	return classify_tcp(skb, &tuple, tuplen, info->hdr, &tcp);
725*cfa7b011SJoanne Koong }
726*cfa7b011SJoanne Koong 
process_udp(struct bpf_dynptr * dynptr,__u64 * offset,struct __sk_buff * skb,struct iphdr_info * info,metrics_t * metrics)727*cfa7b011SJoanne Koong static verdict_t process_udp(struct bpf_dynptr *dynptr, __u64 *offset, struct __sk_buff *skb,
728*cfa7b011SJoanne Koong 			     struct iphdr_info *info, metrics_t *metrics)
729*cfa7b011SJoanne Koong {
730*cfa7b011SJoanne Koong 	struct bpf_sock_tuple tuple;
731*cfa7b011SJoanne Koong 	struct udphdr udph;
732*cfa7b011SJoanne Koong 	uint64_t tuplen;
733*cfa7b011SJoanne Koong 
734*cfa7b011SJoanne Koong 	metrics->l4_protocol_packets_total_udp++;
735*cfa7b011SJoanne Koong 
736*cfa7b011SJoanne Koong 	if (bpf_dynptr_read(&udph, sizeof(udph), dynptr, *offset, 0)) {
737*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_udp++;
738*cfa7b011SJoanne Koong 		return INVALID;
739*cfa7b011SJoanne Koong 	}
740*cfa7b011SJoanne Koong 	*offset += sizeof(udph);
741*cfa7b011SJoanne Koong 
742*cfa7b011SJoanne Koong 	tuplen = fill_tuple(&tuple, info->hdr, info->len, udph.source, udph.dest);
743*cfa7b011SJoanne Koong 	return classify_udp(skb, &tuple, tuplen);
744*cfa7b011SJoanne Koong }
745*cfa7b011SJoanne Koong 
process_ipv4(struct __sk_buff * skb,struct bpf_dynptr * dynptr,__u64 * offset,metrics_t * metrics)746*cfa7b011SJoanne Koong static verdict_t process_ipv4(struct __sk_buff *skb, struct bpf_dynptr *dynptr,
747*cfa7b011SJoanne Koong 			      __u64 *offset, metrics_t *metrics)
748*cfa7b011SJoanne Koong {
749*cfa7b011SJoanne Koong 	struct iphdr ipv4;
750*cfa7b011SJoanne Koong 	struct iphdr_info info = {
751*cfa7b011SJoanne Koong 		.hdr = &ipv4,
752*cfa7b011SJoanne Koong 		.len = sizeof(ipv4),
753*cfa7b011SJoanne Koong 	};
754*cfa7b011SJoanne Koong 
755*cfa7b011SJoanne Koong 	metrics->l3_protocol_packets_total_ipv4++;
756*cfa7b011SJoanne Koong 
757*cfa7b011SJoanne Koong 	if (pkt_parse_ipv4(dynptr, offset, &ipv4)) {
758*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_ip++;
759*cfa7b011SJoanne Koong 		return INVALID;
760*cfa7b011SJoanne Koong 	}
761*cfa7b011SJoanne Koong 
762*cfa7b011SJoanne Koong 	if (ipv4.version != 4) {
763*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_ip++;
764*cfa7b011SJoanne Koong 		return INVALID;
765*cfa7b011SJoanne Koong 	}
766*cfa7b011SJoanne Koong 
767*cfa7b011SJoanne Koong 	if (ipv4_is_fragment(&ipv4)) {
768*cfa7b011SJoanne Koong 		metrics->errors_total_fragmented_ip++;
769*cfa7b011SJoanne Koong 		return INVALID;
770*cfa7b011SJoanne Koong 	}
771*cfa7b011SJoanne Koong 
772*cfa7b011SJoanne Koong 	switch (ipv4.protocol) {
773*cfa7b011SJoanne Koong 	case IPPROTO_ICMP:
774*cfa7b011SJoanne Koong 		return process_icmpv4(skb, dynptr, offset, metrics);
775*cfa7b011SJoanne Koong 
776*cfa7b011SJoanne Koong 	case IPPROTO_TCP:
777*cfa7b011SJoanne Koong 		return process_tcp(dynptr, offset, skb, &info, metrics);
778*cfa7b011SJoanne Koong 
779*cfa7b011SJoanne Koong 	case IPPROTO_UDP:
780*cfa7b011SJoanne Koong 		return process_udp(dynptr, offset, skb, &info, metrics);
781*cfa7b011SJoanne Koong 
782*cfa7b011SJoanne Koong 	default:
783*cfa7b011SJoanne Koong 		metrics->errors_total_unknown_l4_proto++;
784*cfa7b011SJoanne Koong 		return INVALID;
785*cfa7b011SJoanne Koong 	}
786*cfa7b011SJoanne Koong }
787*cfa7b011SJoanne Koong 
process_ipv6(struct __sk_buff * skb,struct bpf_dynptr * dynptr,__u64 * offset,metrics_t * metrics)788*cfa7b011SJoanne Koong static verdict_t process_ipv6(struct __sk_buff *skb, struct bpf_dynptr *dynptr,
789*cfa7b011SJoanne Koong 			      __u64 *offset, metrics_t *metrics)
790*cfa7b011SJoanne Koong {
791*cfa7b011SJoanne Koong 	struct ipv6hdr ipv6;
792*cfa7b011SJoanne Koong 	struct iphdr_info info = {
793*cfa7b011SJoanne Koong 		.hdr = &ipv6,
794*cfa7b011SJoanne Koong 		.len = sizeof(ipv6),
795*cfa7b011SJoanne Koong 	};
796*cfa7b011SJoanne Koong 	uint8_t l4_proto;
797*cfa7b011SJoanne Koong 	bool is_fragment;
798*cfa7b011SJoanne Koong 
799*cfa7b011SJoanne Koong 	metrics->l3_protocol_packets_total_ipv6++;
800*cfa7b011SJoanne Koong 
801*cfa7b011SJoanne Koong 	if (pkt_parse_ipv6(dynptr, offset, &ipv6, &l4_proto, &is_fragment)) {
802*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_ip++;
803*cfa7b011SJoanne Koong 		return INVALID;
804*cfa7b011SJoanne Koong 	}
805*cfa7b011SJoanne Koong 
806*cfa7b011SJoanne Koong 	if (ipv6.version != 6) {
807*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_ip++;
808*cfa7b011SJoanne Koong 		return INVALID;
809*cfa7b011SJoanne Koong 	}
810*cfa7b011SJoanne Koong 
811*cfa7b011SJoanne Koong 	if (is_fragment) {
812*cfa7b011SJoanne Koong 		metrics->errors_total_fragmented_ip++;
813*cfa7b011SJoanne Koong 		return INVALID;
814*cfa7b011SJoanne Koong 	}
815*cfa7b011SJoanne Koong 
816*cfa7b011SJoanne Koong 	switch (l4_proto) {
817*cfa7b011SJoanne Koong 	case IPPROTO_ICMPV6:
818*cfa7b011SJoanne Koong 		return process_icmpv6(dynptr, offset, skb, metrics);
819*cfa7b011SJoanne Koong 
820*cfa7b011SJoanne Koong 	case IPPROTO_TCP:
821*cfa7b011SJoanne Koong 		return process_tcp(dynptr, offset, skb, &info, metrics);
822*cfa7b011SJoanne Koong 
823*cfa7b011SJoanne Koong 	case IPPROTO_UDP:
824*cfa7b011SJoanne Koong 		return process_udp(dynptr, offset, skb, &info, metrics);
825*cfa7b011SJoanne Koong 
826*cfa7b011SJoanne Koong 	default:
827*cfa7b011SJoanne Koong 		metrics->errors_total_unknown_l4_proto++;
828*cfa7b011SJoanne Koong 		return INVALID;
829*cfa7b011SJoanne Koong 	}
830*cfa7b011SJoanne Koong }
831*cfa7b011SJoanne Koong 
832*cfa7b011SJoanne Koong SEC("tc")
cls_redirect(struct __sk_buff * skb)833*cfa7b011SJoanne Koong int cls_redirect(struct __sk_buff *skb)
834*cfa7b011SJoanne Koong {
835*cfa7b011SJoanne Koong 	__u8 encap_buffer[sizeof(encap_headers_t)] = {};
836*cfa7b011SJoanne Koong 	struct bpf_dynptr dynptr;
837*cfa7b011SJoanne Koong 	struct in_addr next_hop;
838*cfa7b011SJoanne Koong 	/* Tracks offset of the dynptr. This will be unnecessary once
839*cfa7b011SJoanne Koong 	 * bpf_dynptr_advance() is available.
840*cfa7b011SJoanne Koong 	 */
841*cfa7b011SJoanne Koong 	__u64 off = 0;
842*cfa7b011SJoanne Koong 	ret_t ret;
843*cfa7b011SJoanne Koong 
844*cfa7b011SJoanne Koong 	bpf_dynptr_from_skb(skb, 0, &dynptr);
845*cfa7b011SJoanne Koong 
846*cfa7b011SJoanne Koong 	metrics_t *metrics = get_global_metrics();
847*cfa7b011SJoanne Koong 	if (metrics == NULL)
848*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
849*cfa7b011SJoanne Koong 
850*cfa7b011SJoanne Koong 	metrics->processed_packets_total++;
851*cfa7b011SJoanne Koong 
852*cfa7b011SJoanne Koong 	/* Pass bogus packets as long as we're not sure they're
853*cfa7b011SJoanne Koong 	 * destined for us.
854*cfa7b011SJoanne Koong 	 */
855*cfa7b011SJoanne Koong 	if (skb->protocol != bpf_htons(ETH_P_IP))
856*cfa7b011SJoanne Koong 		return TC_ACT_OK;
857*cfa7b011SJoanne Koong 
858*cfa7b011SJoanne Koong 	encap_headers_t *encap;
859*cfa7b011SJoanne Koong 
860*cfa7b011SJoanne Koong 	/* Make sure that all encapsulation headers are available in
861*cfa7b011SJoanne Koong 	 * the linear portion of the skb. This makes it easy to manipulate them.
862*cfa7b011SJoanne Koong 	 */
863*cfa7b011SJoanne Koong 	if (bpf_skb_pull_data(skb, sizeof(*encap)))
864*cfa7b011SJoanne Koong 		return TC_ACT_OK;
865*cfa7b011SJoanne Koong 
866*cfa7b011SJoanne Koong 	encap = bpf_dynptr_slice_rdwr(&dynptr, 0, encap_buffer, sizeof(encap_buffer));
867*cfa7b011SJoanne Koong 	if (!encap)
868*cfa7b011SJoanne Koong 		return TC_ACT_OK;
869*cfa7b011SJoanne Koong 
870*cfa7b011SJoanne Koong 	off += sizeof(*encap);
871*cfa7b011SJoanne Koong 
872*cfa7b011SJoanne Koong 	if (encap->ip.ihl != 5)
873*cfa7b011SJoanne Koong 		/* We never have any options. */
874*cfa7b011SJoanne Koong 		return TC_ACT_OK;
875*cfa7b011SJoanne Koong 
876*cfa7b011SJoanne Koong 	if (encap->ip.daddr != ENCAPSULATION_IP ||
877*cfa7b011SJoanne Koong 	    encap->ip.protocol != IPPROTO_UDP)
878*cfa7b011SJoanne Koong 		return TC_ACT_OK;
879*cfa7b011SJoanne Koong 
880*cfa7b011SJoanne Koong 	/* TODO Check UDP length? */
881*cfa7b011SJoanne Koong 	if (encap->udp.dest != ENCAPSULATION_PORT)
882*cfa7b011SJoanne Koong 		return TC_ACT_OK;
883*cfa7b011SJoanne Koong 
884*cfa7b011SJoanne Koong 	/* We now know that the packet is destined to us, we can
885*cfa7b011SJoanne Koong 	 * drop bogus ones.
886*cfa7b011SJoanne Koong 	 */
887*cfa7b011SJoanne Koong 	if (ipv4_is_fragment((void *)&encap->ip)) {
888*cfa7b011SJoanne Koong 		metrics->errors_total_fragmented_ip++;
889*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
890*cfa7b011SJoanne Koong 	}
891*cfa7b011SJoanne Koong 
892*cfa7b011SJoanne Koong 	if (encap->gue.variant != 0) {
893*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_encapsulation++;
894*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
895*cfa7b011SJoanne Koong 	}
896*cfa7b011SJoanne Koong 
897*cfa7b011SJoanne Koong 	if (encap->gue.control != 0) {
898*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_encapsulation++;
899*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
900*cfa7b011SJoanne Koong 	}
901*cfa7b011SJoanne Koong 
902*cfa7b011SJoanne Koong 	if (encap->gue.flags != 0) {
903*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_encapsulation++;
904*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
905*cfa7b011SJoanne Koong 	}
906*cfa7b011SJoanne Koong 
907*cfa7b011SJoanne Koong 	if (encap->gue.hlen !=
908*cfa7b011SJoanne Koong 	    sizeof(encap->unigue) / 4 + encap->unigue.hop_count) {
909*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_encapsulation++;
910*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
911*cfa7b011SJoanne Koong 	}
912*cfa7b011SJoanne Koong 
913*cfa7b011SJoanne Koong 	if (encap->unigue.version != 0) {
914*cfa7b011SJoanne Koong 		metrics->errors_total_malformed_encapsulation++;
915*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
916*cfa7b011SJoanne Koong 	}
917*cfa7b011SJoanne Koong 
918*cfa7b011SJoanne Koong 	if (encap->unigue.reserved != 0)
919*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
920*cfa7b011SJoanne Koong 
921*cfa7b011SJoanne Koong 	MAYBE_RETURN(get_next_hop(&dynptr, &off, encap, &next_hop));
922*cfa7b011SJoanne Koong 
923*cfa7b011SJoanne Koong 	if (next_hop.s_addr == 0) {
924*cfa7b011SJoanne Koong 		metrics->accepted_packets_total_last_hop++;
925*cfa7b011SJoanne Koong 		return accept_locally(skb, encap);
926*cfa7b011SJoanne Koong 	}
927*cfa7b011SJoanne Koong 
928*cfa7b011SJoanne Koong 	verdict_t verdict;
929*cfa7b011SJoanne Koong 	switch (encap->gue.proto_ctype) {
930*cfa7b011SJoanne Koong 	case IPPROTO_IPIP:
931*cfa7b011SJoanne Koong 		verdict = process_ipv4(skb, &dynptr, &off, metrics);
932*cfa7b011SJoanne Koong 		break;
933*cfa7b011SJoanne Koong 
934*cfa7b011SJoanne Koong 	case IPPROTO_IPV6:
935*cfa7b011SJoanne Koong 		verdict = process_ipv6(skb, &dynptr, &off, metrics);
936*cfa7b011SJoanne Koong 		break;
937*cfa7b011SJoanne Koong 
938*cfa7b011SJoanne Koong 	default:
939*cfa7b011SJoanne Koong 		metrics->errors_total_unknown_l3_proto++;
940*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
941*cfa7b011SJoanne Koong 	}
942*cfa7b011SJoanne Koong 
943*cfa7b011SJoanne Koong 	switch (verdict) {
944*cfa7b011SJoanne Koong 	case INVALID:
945*cfa7b011SJoanne Koong 		/* metrics have already been bumped */
946*cfa7b011SJoanne Koong 		return TC_ACT_SHOT;
947*cfa7b011SJoanne Koong 
948*cfa7b011SJoanne Koong 	case UNKNOWN:
949*cfa7b011SJoanne Koong 		return forward_to_next_hop(skb, &dynptr, encap, &next_hop, metrics);
950*cfa7b011SJoanne Koong 
951*cfa7b011SJoanne Koong 	case ECHO_REQUEST:
952*cfa7b011SJoanne Koong 		metrics->accepted_packets_total_icmp_echo_request++;
953*cfa7b011SJoanne Koong 		break;
954*cfa7b011SJoanne Koong 
955*cfa7b011SJoanne Koong 	case SYN:
956*cfa7b011SJoanne Koong 		if (encap->unigue.forward_syn) {
957*cfa7b011SJoanne Koong 			return forward_to_next_hop(skb, &dynptr, encap, &next_hop,
958*cfa7b011SJoanne Koong 						   metrics);
959*cfa7b011SJoanne Koong 		}
960*cfa7b011SJoanne Koong 
961*cfa7b011SJoanne Koong 		metrics->accepted_packets_total_syn++;
962*cfa7b011SJoanne Koong 		break;
963*cfa7b011SJoanne Koong 
964*cfa7b011SJoanne Koong 	case SYN_COOKIE:
965*cfa7b011SJoanne Koong 		metrics->accepted_packets_total_syn_cookies++;
966*cfa7b011SJoanne Koong 		break;
967*cfa7b011SJoanne Koong 
968*cfa7b011SJoanne Koong 	case ESTABLISHED:
969*cfa7b011SJoanne Koong 		metrics->accepted_packets_total_established++;
970*cfa7b011SJoanne Koong 		break;
971*cfa7b011SJoanne Koong 	}
972*cfa7b011SJoanne Koong 
973*cfa7b011SJoanne Koong 	ret = accept_locally(skb, encap);
974*cfa7b011SJoanne Koong 
975*cfa7b011SJoanne Koong 	if (encap == encap_buffer)
976*cfa7b011SJoanne Koong 		bpf_dynptr_write(&dynptr, 0, encap_buffer, sizeof(encap_buffer), 0);
977*cfa7b011SJoanne Koong 
978*cfa7b011SJoanne Koong 	return ret;
979*cfa7b011SJoanne Koong }
980