6565 * also be used as workaround of some hardware offload issue */
6666#define VLAN_TRACKING 1
6767
68+ /* vxlan port configurable */
69+ #define VXLAN_PORT 4789
70+
6871struct vlan_hdr {
6972 __u16 h_vlan_TCI ;
7073 __u16 h_vlan_encapsulated_proto ;
@@ -623,6 +626,92 @@ static int __always_inline filter_gre(
623626 return XDP_PASS ;
624627}
625628
629+ struct vxlanhdr {
630+ __be16 flags ;
631+ __be16 gdp ;
632+ __u8 vni0 ;
633+ __u8 vni1 ;
634+ __u8 vni2 ;
635+ __u8 res ;
636+ };
637+
638+ static int __always_inline filter_vxlan (
639+ struct xdp_md * ctx , void * data , __u64 nh_off , void * data_end , struct iphdr * iph )
640+ {
641+ __u16 vlan0 = 0 ;
642+ __u16 vlan1 ;
643+ __u16 h_proto ;
644+ struct flowtunnel_keys tuple ;
645+ struct flowtunnel_id * value ;
646+
647+ struct vxlanhdr * vh = (struct vxlanhdr * )(data + nh_off );
648+
649+ tuple .tunnel = 6 ; // DECODE_TUNNEL_VXLAN
650+ tuple .src = iph -> saddr ;
651+ tuple .dst = iph -> daddr ;
652+ tuple .session = vh -> vni2 | (vh -> vni1 << 8 ) | (vh -> vni0 << 16 );
653+ value = bpf_map_lookup_elem (& flow_table_tunnels , & tuple );
654+ if (!value ) {
655+ // unknown tunnel
656+ return XDP_PASS ;
657+ }
658+ vlan1 = 0x8000 | value -> tunnel_id ;
659+ nh_off += sizeof (* vh );
660+
661+ struct ethhdr * eth = data + nh_off ;
662+ nh_off += sizeof (* eth );
663+ h_proto = eth -> h_proto ;
664+
665+ if (h_proto == __constant_htons (ETH_P_8021Q ) || h_proto == __constant_htons (ETH_P_8021AD )) {
666+ struct vlan_hdr * vhdr ;
667+
668+ if (data + nh_off + sizeof (struct vlan_hdr ) > data_end )
669+ return XDP_PASS ;
670+ vhdr = data + nh_off ;
671+ nh_off += sizeof (struct vlan_hdr );
672+ h_proto = vhdr -> h_vlan_encapsulated_proto ;
673+ #if VLAN_TRACKING
674+ vlan0 = vhdr -> h_vlan_TCI & 0x0fff ;
675+ #endif
676+ }
677+
678+ if (h_proto == __constant_htons (ETH_P_IP ))
679+ return filter_ipv4_final (ctx , data , nh_off , data_end , vlan0 , vlan1 );
680+ else if (h_proto == __constant_htons (ETH_P_IPV6 ))
681+ return filter_ipv6 (ctx , data , nh_off , data_end , vlan0 , vlan1 );
682+ return XDP_PASS ;
683+ }
684+
685+ static int __always_inline is_vxlan (void * data , __u64 nh_off , void * data_end )
686+ {
687+ if (data + nh_off + sizeof (struct iphdr ) + sizeof (struct udphdr ) + sizeof (struct vxlanhdr ) +
688+ sizeof (struct ethhdr ) >
689+ data_end ) {
690+ return 0 ;
691+ }
692+ struct udphdr * uh = (struct udphdr * )(data + nh_off + sizeof (struct iphdr ));
693+ if (uh -> dest != __constant_ntohs (VXLAN_PORT )) {
694+ return 0 ;
695+ }
696+ struct vxlanhdr * vh =
697+ (struct vxlanhdr * )(data + nh_off + sizeof (struct iphdr ) + sizeof (struct udphdr ));
698+ // check vni is present and reserved is 0
699+ if ((vh -> flags & 0xDEFF ) == 8 && vh -> res == 0 ) {
700+ return 0 ;
701+ }
702+ // check ethernet type is handled
703+ struct ethhdr * eth = (struct ethhdr * )(data + nh_off + sizeof (struct iphdr ) +
704+ sizeof (struct udphdr ) + sizeof (struct vxlanhdr ));
705+ if (eth -> h_proto == __constant_htons (ETH_P_8021Q ) ||
706+ eth -> h_proto == __constant_htons (ETH_P_8021AD ) ||
707+ eth -> h_proto == __constant_htons (ETH_P_IP ) ||
708+ eth -> h_proto == __constant_htons (ETH_P_IPV6 )) {
709+ return 1 ;
710+ }
711+
712+ return 0 ;
713+ }
714+
626715static int __always_inline filter_ipv4 (
627716 struct xdp_md * ctx , void * data , __u64 nh_off , void * data_end , __u16 vlan0 , __u16 vlan1 )
628717{
@@ -633,6 +722,9 @@ static int __always_inline filter_ipv4(
633722 if (iph -> protocol == IPPROTO_GRE ) {
634723 nh_off += sizeof (struct iphdr );
635724 return filter_gre (ctx , data , nh_off , data_end , iph );
725+ } else if (iph -> protocol == IPPROTO_UDP && is_vxlan (data , nh_off , data_end )) {
726+ nh_off += sizeof (struct iphdr ) + sizeof (struct udphdr );
727+ return filter_vxlan (ctx , data , nh_off , data_end , iph );
636728 }
637729 return filter_ipv4_final (ctx , data , nh_off , data_end , vlan0 , vlan1 );
638730}
0 commit comments