1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2017 Facebook
6 #include <linux/pkt_cls.h>
9 #include <linux/if_ether.h>
11 #include <linux/ipv6.h>
12 #include <linux/icmp.h>
13 #include <linux/icmpv6.h>
14 #include <linux/tcp.h>
15 #include <linux/udp.h>
16 #include "bpf_helpers.h"
18 #define bpf_printk(fmt, ...) \
20 char ____fmt[] = fmt; \
21 bpf_trace_printk(____fmt, sizeof(____fmt), \
25 static __u32
rol32(__u32 word
, unsigned int shift
)
27 return (word
<< shift
) | (word
>> ((-shift
) & 31));
30 /* copy paste of jhash from kernel sources to make sure llvm
31 * can compile it into valid sequence of bpf instructions
33 #define __jhash_mix(a, b, c) \
35 a -= c; a ^= rol32(c, 4); c += b; \
36 b -= a; b ^= rol32(a, 6); a += c; \
37 c -= b; c ^= rol32(b, 8); b += a; \
38 a -= c; a ^= rol32(c, 16); c += b; \
39 b -= a; b ^= rol32(a, 19); a += c; \
40 c -= b; c ^= rol32(b, 4); b += a; \
43 #define __jhash_final(a, b, c) \
45 c ^= b; c -= rol32(b, 14); \
46 a ^= c; a -= rol32(c, 11); \
47 b ^= a; b -= rol32(a, 25); \
48 c ^= b; c -= rol32(b, 16); \
49 a ^= c; a -= rol32(c, 4); \
50 b ^= a; b -= rol32(a, 14); \
51 c ^= b; c -= rol32(b, 24); \
54 #define JHASH_INITVAL 0xdeadbeef
56 typedef unsigned int u32
;
58 static __attribute__ ((noinline
))
59 u32
jhash(const void *key
, u32 length
, u32 initval
)
62 const unsigned char *k
= key
;
64 a
= b
= c
= JHASH_INITVAL
+ length
+ initval
;
75 case 12: c
+= (u32
)k
[11]<<24;
76 case 11: c
+= (u32
)k
[10]<<16;
77 case 10: c
+= (u32
)k
[9]<<8;
79 case 8: b
+= (u32
)k
[7]<<24;
80 case 7: b
+= (u32
)k
[6]<<16;
81 case 6: b
+= (u32
)k
[5]<<8;
83 case 4: a
+= (u32
)k
[3]<<24;
84 case 3: a
+= (u32
)k
[2]<<16;
85 case 2: a
+= (u32
)k
[1]<<8;
87 __jhash_final(a
, b
, c
);
88 case 0: /* Nothing left to add */
95 static __attribute__ ((noinline
))
96 u32
__jhash_nwords(u32 a
, u32 b
, u32 c
, u32 initval
)
101 __jhash_final(a
, b
, c
);
105 static __attribute__ ((noinline
))
106 u32
jhash_2words(u32 a
, u32 b
, u32 initval
)
108 return __jhash_nwords(a
, b
, 0, initval
+ JHASH_INITVAL
+ (2 << 2));
127 struct packet_description
{
128 struct flow_key flow
;
140 struct vip_definition
{
155 struct real_pos_lru
{
160 struct real_definition
{
173 struct bpf_map_def
__attribute__ ((section("maps"), used
)) vip_map
= {
174 .type
= BPF_MAP_TYPE_HASH
,
175 .key_size
= sizeof(struct vip_definition
),
176 .value_size
= sizeof(struct vip_meta
),
181 struct bpf_map_def
__attribute__ ((section("maps"), used
)) lru_cache
= {
182 .type
= BPF_MAP_TYPE_LRU_HASH
,
183 .key_size
= sizeof(struct flow_key
),
184 .value_size
= sizeof(struct real_pos_lru
),
186 .map_flags
= 1U << 1,
189 struct bpf_map_def
__attribute__ ((section("maps"), used
)) ch_rings
= {
190 .type
= BPF_MAP_TYPE_ARRAY
,
191 .key_size
= sizeof(__u32
),
192 .value_size
= sizeof(__u32
),
193 .max_entries
= 12 * 655,
197 struct bpf_map_def
__attribute__ ((section("maps"), used
)) reals
= {
198 .type
= BPF_MAP_TYPE_ARRAY
,
199 .key_size
= sizeof(__u32
),
200 .value_size
= sizeof(struct real_definition
),
205 struct bpf_map_def
__attribute__ ((section("maps"), used
)) stats
= {
206 .type
= BPF_MAP_TYPE_PERCPU_ARRAY
,
207 .key_size
= sizeof(__u32
),
208 .value_size
= sizeof(struct lb_stats
),
213 struct bpf_map_def
__attribute__ ((section("maps"), used
)) ctl_array
= {
214 .type
= BPF_MAP_TYPE_ARRAY
,
215 .key_size
= sizeof(__u32
),
216 .value_size
= sizeof(struct ctl_value
),
222 unsigned char eth_dest
[6];
223 unsigned char eth_source
[6];
224 unsigned short eth_proto
;
227 static inline __u64
calc_offset(bool is_ipv6
, bool is_icmp
)
229 __u64 off
= sizeof(struct eth_hdr
);
231 off
+= sizeof(struct ipv6hdr
);
233 off
+= sizeof(struct icmp6hdr
) + sizeof(struct ipv6hdr
);
235 off
+= sizeof(struct iphdr
);
237 off
+= sizeof(struct icmphdr
) + sizeof(struct iphdr
);
242 static __attribute__ ((noinline
))
243 bool parse_udp(void *data
, void *data_end
,
244 bool is_ipv6
, struct packet_description
*pckt
)
247 bool is_icmp
= !((pckt
->flags
& (1 << 0)) == 0);
248 __u64 off
= calc_offset(is_ipv6
, is_icmp
);
252 if (udp
+ 1 > data_end
)
255 pckt
->flow
.port16
[0] = udp
->source
;
256 pckt
->flow
.port16
[1] = udp
->dest
;
258 pckt
->flow
.port16
[0] = udp
->dest
;
259 pckt
->flow
.port16
[1] = udp
->source
;
264 static __attribute__ ((noinline
))
265 bool parse_tcp(void *data
, void *data_end
,
266 bool is_ipv6
, struct packet_description
*pckt
)
269 bool is_icmp
= !((pckt
->flags
& (1 << 0)) == 0);
270 __u64 off
= calc_offset(is_ipv6
, is_icmp
);
274 if (tcp
+ 1 > data_end
)
277 pckt
->flags
|= (1 << 1);
279 pckt
->flow
.port16
[0] = tcp
->source
;
280 pckt
->flow
.port16
[1] = tcp
->dest
;
282 pckt
->flow
.port16
[0] = tcp
->dest
;
283 pckt
->flow
.port16
[1] = tcp
->source
;
288 static __attribute__ ((noinline
))
289 bool encap_v6(struct xdp_md
*xdp
, struct ctl_value
*cval
,
290 struct packet_description
*pckt
,
291 struct real_definition
*dst
, __u32 pkt_bytes
)
293 struct eth_hdr
*new_eth
;
294 struct eth_hdr
*old_eth
;
295 struct ipv6hdr
*ip6h
;
300 if (bpf_xdp_adjust_head(xdp
, 0 - (int)sizeof(struct ipv6hdr
)))
302 data
= (void *)(long)xdp
->data
;
303 data_end
= (void *)(long)xdp
->data_end
;
305 ip6h
= data
+ sizeof(struct eth_hdr
);
306 old_eth
= data
+ sizeof(struct ipv6hdr
);
307 if (new_eth
+ 1 > data_end
||
308 old_eth
+ 1 > data_end
|| ip6h
+ 1 > data_end
)
310 memcpy(new_eth
->eth_dest
, cval
->mac
, 6);
311 memcpy(new_eth
->eth_source
, old_eth
->eth_dest
, 6);
312 new_eth
->eth_proto
= 56710;
315 memset(ip6h
->flow_lbl
, 0, sizeof(ip6h
->flow_lbl
));
317 ip6h
->nexthdr
= IPPROTO_IPV6
;
318 ip_suffix
= pckt
->flow
.srcv6
[3] ^ pckt
->flow
.port16
[0];
320 __builtin_bswap16(pkt_bytes
+ sizeof(struct ipv6hdr
));
323 ip6h
->saddr
.in6_u
.u6_addr32
[0] = 1;
324 ip6h
->saddr
.in6_u
.u6_addr32
[1] = 2;
325 ip6h
->saddr
.in6_u
.u6_addr32
[2] = 3;
326 ip6h
->saddr
.in6_u
.u6_addr32
[3] = ip_suffix
;
327 memcpy(ip6h
->daddr
.in6_u
.u6_addr32
, dst
->dstv6
, 16);
331 static __attribute__ ((noinline
))
332 bool encap_v4(struct xdp_md
*xdp
, struct ctl_value
*cval
,
333 struct packet_description
*pckt
,
334 struct real_definition
*dst
, __u32 pkt_bytes
)
337 __u32 ip_suffix
= __builtin_bswap16(pckt
->flow
.port16
[0]);
338 struct eth_hdr
*new_eth
;
339 struct eth_hdr
*old_eth
;
347 ip_suffix
^= pckt
->flow
.src
;
348 if (bpf_xdp_adjust_head(xdp
, 0 - (int)sizeof(struct iphdr
)))
350 data
= (void *)(long)xdp
->data
;
351 data_end
= (void *)(long)xdp
->data_end
;
353 iph
= data
+ sizeof(struct eth_hdr
);
354 old_eth
= data
+ sizeof(struct iphdr
);
355 if (new_eth
+ 1 > data_end
||
356 old_eth
+ 1 > data_end
|| iph
+ 1 > data_end
)
358 memcpy(new_eth
->eth_dest
, cval
->mac
, 6);
359 memcpy(new_eth
->eth_source
, old_eth
->eth_dest
, 6);
360 new_eth
->eth_proto
= 8;
364 iph
->protocol
= IPPROTO_IPIP
;
367 iph
->tot_len
= __builtin_bswap16(pkt_bytes
+ sizeof(struct iphdr
));
368 /* don't update iph->daddr, since it will overwrite old eth_proto
369 * and multiple iterations of bpf_prog_run() will fail
372 iph
->saddr
= ((0xFFFF0000 & ip_suffix
) | 4268) ^ dst
->dst
;
375 next_iph_u16
= (__u16
*) iph
;
376 #pragma clang loop unroll(full)
377 for (int i
= 0; i
< sizeof(struct iphdr
) >> 1; i
++)
378 csum
+= *next_iph_u16
++;
379 iph
->check
= ~((csum
& 0xffff) + (csum
>> 16));
380 if (bpf_xdp_adjust_head(xdp
, (int)sizeof(struct iphdr
)))
385 static __attribute__ ((noinline
))
386 bool decap_v6(struct xdp_md
*xdp
, void **data
, void **data_end
, bool inner_v4
)
388 struct eth_hdr
*new_eth
;
389 struct eth_hdr
*old_eth
;
392 new_eth
= *data
+ sizeof(struct ipv6hdr
);
393 memcpy(new_eth
->eth_source
, old_eth
->eth_source
, 6);
394 memcpy(new_eth
->eth_dest
, old_eth
->eth_dest
, 6);
396 new_eth
->eth_proto
= 8;
398 new_eth
->eth_proto
= 56710;
399 if (bpf_xdp_adjust_head(xdp
, (int)sizeof(struct ipv6hdr
)))
401 *data
= (void *)(long)xdp
->data
;
402 *data_end
= (void *)(long)xdp
->data_end
;
406 static __attribute__ ((noinline
))
407 bool decap_v4(struct xdp_md
*xdp
, void **data
, void **data_end
)
409 struct eth_hdr
*new_eth
;
410 struct eth_hdr
*old_eth
;
413 new_eth
= *data
+ sizeof(struct iphdr
);
414 memcpy(new_eth
->eth_source
, old_eth
->eth_source
, 6);
415 memcpy(new_eth
->eth_dest
, old_eth
->eth_dest
, 6);
416 new_eth
->eth_proto
= 8;
417 if (bpf_xdp_adjust_head(xdp
, (int)sizeof(struct iphdr
)))
419 *data
= (void *)(long)xdp
->data
;
420 *data_end
= (void *)(long)xdp
->data_end
;
424 static __attribute__ ((noinline
))
425 int swap_mac_and_send(void *data
, void *data_end
)
427 unsigned char tmp_mac
[6];
431 memcpy(tmp_mac
, eth
->eth_source
, 6);
432 memcpy(eth
->eth_source
, eth
->eth_dest
, 6);
433 memcpy(eth
->eth_dest
, tmp_mac
, 6);
437 static __attribute__ ((noinline
))
438 int send_icmp_reply(void *data
, void *data_end
)
440 struct icmphdr
*icmp_hdr
;
448 if (data
+ sizeof(struct eth_hdr
)
449 + sizeof(struct iphdr
) + sizeof(struct icmphdr
) > data_end
)
451 off
+= sizeof(struct eth_hdr
);
453 off
+= sizeof(struct iphdr
);
454 icmp_hdr
= data
+ off
;
456 icmp_hdr
->checksum
+= 0x0007;
458 tmp_addr
= iph
->daddr
;
459 iph
->daddr
= iph
->saddr
;
460 iph
->saddr
= tmp_addr
;
462 next_iph_u16
= (__u16
*) iph
;
463 #pragma clang loop unroll(full)
464 for (int i
= 0; i
< sizeof(struct iphdr
) >> 1; i
++)
465 csum
+= *next_iph_u16
++;
466 iph
->check
= ~((csum
& 0xffff) + (csum
>> 16));
467 return swap_mac_and_send(data
, data_end
);
470 static __attribute__ ((noinline
))
471 int send_icmp6_reply(void *data
, void *data_end
)
473 struct icmp6hdr
*icmp_hdr
;
474 struct ipv6hdr
*ip6h
;
478 if (data
+ sizeof(struct eth_hdr
)
479 + sizeof(struct ipv6hdr
) + sizeof(struct icmp6hdr
) > data_end
)
481 off
+= sizeof(struct eth_hdr
);
483 off
+= sizeof(struct ipv6hdr
);
484 icmp_hdr
= data
+ off
;
485 icmp_hdr
->icmp6_type
= 129;
486 icmp_hdr
->icmp6_cksum
-= 0x0001;
488 memcpy(tmp_addr
, ip6h
->saddr
.in6_u
.u6_addr32
, 16);
489 memcpy(ip6h
->saddr
.in6_u
.u6_addr32
, ip6h
->daddr
.in6_u
.u6_addr32
, 16);
490 memcpy(ip6h
->daddr
.in6_u
.u6_addr32
, tmp_addr
, 16);
491 return swap_mac_and_send(data
, data_end
);
494 static __attribute__ ((noinline
))
495 int parse_icmpv6(void *data
, void *data_end
, __u64 off
,
496 struct packet_description
*pckt
)
498 struct icmp6hdr
*icmp_hdr
;
499 struct ipv6hdr
*ip6h
;
501 icmp_hdr
= data
+ off
;
502 if (icmp_hdr
+ 1 > data_end
)
504 if (icmp_hdr
->icmp6_type
== 128)
505 return send_icmp6_reply(data
, data_end
);
506 if (icmp_hdr
->icmp6_type
!= 3)
508 off
+= sizeof(struct icmp6hdr
);
510 if (ip6h
+ 1 > data_end
)
512 pckt
->flow
.proto
= ip6h
->nexthdr
;
513 pckt
->flags
|= (1 << 0);
514 memcpy(pckt
->flow
.srcv6
, ip6h
->daddr
.in6_u
.u6_addr32
, 16);
515 memcpy(pckt
->flow
.dstv6
, ip6h
->saddr
.in6_u
.u6_addr32
, 16);
519 static __attribute__ ((noinline
))
520 int parse_icmp(void *data
, void *data_end
, __u64 off
,
521 struct packet_description
*pckt
)
523 struct icmphdr
*icmp_hdr
;
526 icmp_hdr
= data
+ off
;
527 if (icmp_hdr
+ 1 > data_end
)
529 if (icmp_hdr
->type
== 8)
530 return send_icmp_reply(data
, data_end
);
531 if ((icmp_hdr
->type
!= 3) || (icmp_hdr
->code
!= 4))
533 off
+= sizeof(struct icmphdr
);
535 if (iph
+ 1 > data_end
)
539 pckt
->flow
.proto
= iph
->protocol
;
540 pckt
->flags
|= (1 << 0);
541 pckt
->flow
.src
= iph
->daddr
;
542 pckt
->flow
.dst
= iph
->saddr
;
546 static __attribute__ ((noinline
))
547 __u32
get_packet_hash(struct packet_description
*pckt
,
551 return jhash_2words(jhash(pckt
->flow
.srcv6
, 16, 12),
552 pckt
->flow
.ports
, 24);
554 return jhash_2words(pckt
->flow
.src
, pckt
->flow
.ports
,
558 __attribute__ ((noinline
))
559 static bool get_packet_dst(struct real_definition
**real
,
560 struct packet_description
*pckt
,
561 struct vip_meta
*vip_info
,
562 bool is_ipv6
, void *lru_map
)
564 struct real_pos_lru new_dst_lru
= { };
565 bool hash_16bytes
= is_ipv6
;
566 __u32
*real_pos
, hash
, key
;
569 if (vip_info
->flags
& (1 << 2))
571 if (vip_info
->flags
& (1 << 3)) {
572 pckt
->flow
.port16
[0] = pckt
->flow
.port16
[1];
573 memset(pckt
->flow
.srcv6
, 0, 16);
575 hash
= get_packet_hash(pckt
, hash_16bytes
);
576 if (hash
!= 0x358459b7 /* jhash of ipv4 packet */ &&
577 hash
!= 0x2f4bc6bb /* jhash of ipv6 packet */)
579 key
= 2 * vip_info
->vip_num
+ hash
% 2;
580 real_pos
= bpf_map_lookup_elem(&ch_rings
, &key
);
584 *real
= bpf_map_lookup_elem(&reals
, &key
);
587 if (!(vip_info
->flags
& (1 << 1))) {
588 __u32 conn_rate_key
= 512 + 2;
589 struct lb_stats
*conn_rate_stats
=
590 bpf_map_lookup_elem(&stats
, &conn_rate_key
);
592 if (!conn_rate_stats
)
594 cur_time
= bpf_ktime_get_ns();
595 if ((cur_time
- conn_rate_stats
->v2
) >> 32 > 0xffFFFF) {
596 conn_rate_stats
->v1
= 1;
597 conn_rate_stats
->v2
= cur_time
;
599 conn_rate_stats
->v1
+= 1;
600 if (conn_rate_stats
->v1
>= 1)
603 if (pckt
->flow
.proto
== IPPROTO_UDP
)
604 new_dst_lru
.atime
= cur_time
;
605 new_dst_lru
.pos
= key
;
606 bpf_map_update_elem(lru_map
, &pckt
->flow
, &new_dst_lru
, 0);
611 __attribute__ ((noinline
))
612 static void connection_table_lookup(struct real_definition
**real
,
613 struct packet_description
*pckt
,
617 struct real_pos_lru
*dst_lru
;
621 dst_lru
= bpf_map_lookup_elem(lru_map
, &pckt
->flow
);
624 if (pckt
->flow
.proto
== IPPROTO_UDP
) {
625 cur_time
= bpf_ktime_get_ns();
626 if (cur_time
- dst_lru
->atime
> 300000)
628 dst_lru
->atime
= cur_time
;
631 *real
= bpf_map_lookup_elem(&reals
, &key
);
634 /* don't believe your eyes!
635 * below function has 6 arguments whereas bpf and llvm allow maximum of 5
636 * but since it's _static_ llvm can optimize one argument away
638 __attribute__ ((noinline
))
639 static int process_l3_headers_v6(struct packet_description
*pckt
,
640 __u8
*protocol
, __u64 off
,
641 __u16
*pkt_bytes
, void *data
,
644 struct ipv6hdr
*ip6h
;
649 if (ip6h
+ 1 > data_end
)
651 iph_len
= sizeof(struct ipv6hdr
);
652 *protocol
= ip6h
->nexthdr
;
653 pckt
->flow
.proto
= *protocol
;
654 *pkt_bytes
= __builtin_bswap16(ip6h
->payload_len
);
656 if (*protocol
== 45) {
658 } else if (*protocol
== 59) {
659 action
= parse_icmpv6(data
, data_end
, off
, pckt
);
663 memcpy(pckt
->flow
.srcv6
, ip6h
->saddr
.in6_u
.u6_addr32
, 16);
664 memcpy(pckt
->flow
.dstv6
, ip6h
->daddr
.in6_u
.u6_addr32
, 16);
669 __attribute__ ((noinline
))
670 static int process_l3_headers_v4(struct packet_description
*pckt
,
671 __u8
*protocol
, __u64 off
,
672 __u16
*pkt_bytes
, void *data
,
680 if (iph
+ 1 > data_end
)
684 *protocol
= iph
->protocol
;
685 pckt
->flow
.proto
= *protocol
;
686 *pkt_bytes
= __builtin_bswap16(iph
->tot_len
);
688 if (iph
->frag_off
& 65343)
690 if (*protocol
== IPPROTO_ICMP
) {
691 action
= parse_icmp(data
, data_end
, off
, pckt
);
695 pckt
->flow
.src
= iph
->saddr
;
696 pckt
->flow
.dst
= iph
->daddr
;
701 __attribute__ ((noinline
))
702 static int process_packet(void *data
, __u64 off
, void *data_end
,
703 bool is_ipv6
, struct xdp_md
*xdp
)
706 struct real_definition
*dst
= NULL
;
707 struct packet_description pckt
= { };
708 struct vip_definition vip
= { };
709 struct lb_stats
*data_stats
;
710 struct eth_hdr
*eth
= data
;
711 void *lru_map
= &lru_cache
;
712 struct vip_meta
*vip_info
;
713 __u32 lru_stats_key
= 513;
714 __u32 mac_addr_pos
= 0;
715 __u32 stats_key
= 512;
716 struct ctl_value
*cval
;
724 action
= process_l3_headers_v6(&pckt
, &protocol
, off
,
725 &pkt_bytes
, data
, data_end
);
727 action
= process_l3_headers_v4(&pckt
, &protocol
, off
,
728 &pkt_bytes
, data
, data_end
);
731 protocol
= pckt
.flow
.proto
;
732 if (protocol
== IPPROTO_TCP
) {
733 if (!parse_tcp(data
, data_end
, is_ipv6
, &pckt
))
735 } else if (protocol
== IPPROTO_UDP
) {
736 if (!parse_udp(data
, data_end
, is_ipv6
, &pckt
))
743 memcpy(vip
.vipv6
, pckt
.flow
.dstv6
, 16);
745 vip
.vip
= pckt
.flow
.dst
;
746 vip
.port
= pckt
.flow
.port16
[1];
747 vip
.proto
= pckt
.flow
.proto
;
748 vip_info
= bpf_map_lookup_elem(&vip_map
, &vip
);
751 vip_info
= bpf_map_lookup_elem(&vip_map
, &vip
);
754 if (!(vip_info
->flags
& (1 << 4)))
755 pckt
.flow
.port16
[1] = 0;
757 if (data_end
- data
> 1400)
759 data_stats
= bpf_map_lookup_elem(&stats
, &stats_key
);
764 if (vip_info
->flags
& (1 << 0))
765 pckt
.flow
.port16
[0] = 0;
766 if (!(pckt
.flags
& (1 << 1)) && !(vip_info
->flags
& (1 << 1)))
767 connection_table_lookup(&dst
, &pckt
, lru_map
);
770 if (pckt
.flow
.proto
== IPPROTO_TCP
) {
771 struct lb_stats
*lru_stats
=
772 bpf_map_lookup_elem(&stats
, &lru_stats_key
);
776 if (pckt
.flags
& (1 << 1))
781 if (!get_packet_dst(&dst
, &pckt
, vip_info
, is_ipv6
, lru_map
))
786 cval
= bpf_map_lookup_elem(&ctl_array
, &mac_addr_pos
);
789 if (dst
->flags
& (1 << 0)) {
790 if (!encap_v6(xdp
, cval
, &pckt
, dst
, pkt_bytes
))
793 if (!encap_v4(xdp
, cval
, &pckt
, dst
, pkt_bytes
))
796 vip_num
= vip_info
->vip_num
;
797 data_stats
= bpf_map_lookup_elem(&stats
, &vip_num
);
801 data_stats
->v2
+= pkt_bytes
;
803 data
= (void *)(long)xdp
->data
;
804 data_end
= (void *)(long)xdp
->data_end
;
805 if (data
+ 4 > data_end
)
807 *(u32
*)data
= dst
->dst
;
811 __attribute__ ((section("xdp-test"), used
))
812 int balancer_ingress(struct xdp_md
*ctx
)
814 void *data
= (void *)(long)ctx
->data
;
815 void *data_end
= (void *)(long)ctx
->data_end
;
816 struct eth_hdr
*eth
= data
;
820 nh_off
= sizeof(struct eth_hdr
);
821 if (data
+ nh_off
> data_end
)
823 eth_proto
= eth
->eth_proto
;
825 return process_packet(data
, nh_off
, data_end
, 0, ctx
);
826 else if (eth_proto
== 56710)
827 return process_packet(data
, nh_off
, data_end
, 1, ctx
);
832 char _license
[] __attribute__ ((section("license"), used
)) = "GPL";
833 int _version
__attribute__ ((section("version"), used
)) = 1;