*             * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP **:
  *               Use with ENCAP_L3 flags to further specify the tunnel type.
  *
+ *             * **BPF_F_ADJ_ROOM_ENCAP_L2(len) **:
+ *               Use with ENCAP_L3/L4 flags to further specify the tunnel
+ *               type; **len** is the length of the inner MAC header.
+ *
  *             A call to this helper is susceptible to change the underlaying
  *             packet buffer. Therefore, at load time, all checks on pointers
  *             previously done by the verifier are invalidated and must be
 /* BPF_FUNC_skb_adjust_room flags. */
 #define BPF_F_ADJ_ROOM_FIXED_GSO       (1ULL << 0)
 
+#define        BPF_ADJ_ROOM_ENCAP_L2_MASK      0xff
+#define        BPF_ADJ_ROOM_ENCAP_L2_SHIFT     56
+
 #define BPF_F_ADJ_ROOM_ENCAP_L3_IPV4   (1ULL << 1)
 #define BPF_F_ADJ_ROOM_ENCAP_L3_IPV6   (1ULL << 2)
 #define BPF_F_ADJ_ROOM_ENCAP_L4_GRE    (1ULL << 3)
 #define BPF_F_ADJ_ROOM_ENCAP_L4_UDP    (1ULL << 4)
+#define        BPF_F_ADJ_ROOM_ENCAP_L2(len)    (((__u64)len & \
+                                         BPF_ADJ_ROOM_ENCAP_L2_MASK) \
+                                        << BPF_ADJ_ROOM_ENCAP_L2_SHIFT)
 
 /* Mode for BPF_FUNC_skb_adjust_room helper. */
 enum bpf_adj_room_mode {
 
 #define BPF_F_ADJ_ROOM_MASK            (BPF_F_ADJ_ROOM_FIXED_GSO | \
                                         BPF_F_ADJ_ROOM_ENCAP_L3_MASK | \
                                         BPF_F_ADJ_ROOM_ENCAP_L4_GRE | \
-                                        BPF_F_ADJ_ROOM_ENCAP_L4_UDP)
+                                        BPF_F_ADJ_ROOM_ENCAP_L4_UDP | \
+                                        BPF_F_ADJ_ROOM_ENCAP_L2( \
+                                         BPF_ADJ_ROOM_ENCAP_L2_MASK))
 
 static int bpf_skb_net_grow(struct sk_buff *skb, u32 off, u32 len_diff,
                            u64 flags)
 {
+       u8 inner_mac_len = flags >> BPF_ADJ_ROOM_ENCAP_L2_SHIFT;
        bool encap = flags & BPF_F_ADJ_ROOM_ENCAP_L3_MASK;
        u16 mac_len = 0, inner_net = 0, inner_trans = 0;
        unsigned int gso_type = SKB_GSO_DODGY;
 
                mac_len = skb->network_header - skb->mac_header;
                inner_net = skb->network_header;
+               if (inner_mac_len > len_diff)
+                       return -EINVAL;
                inner_trans = skb->transport_header;
        }
 
                return ret;
 
        if (encap) {
-               /* inner mac == inner_net on l3 encap */
-               skb->inner_mac_header = inner_net;
+               skb->inner_mac_header = inner_net - inner_mac_len;
                skb->inner_network_header = inner_net;
                skb->inner_transport_header = inner_trans;
                skb_set_inner_protocol(skb, skb->protocol);
                        gso_type |= SKB_GSO_GRE;
                else if (flags & BPF_F_ADJ_ROOM_ENCAP_L3_IPV6)
                        gso_type |= SKB_GSO_IPXIP6;
-               else
+               else if (flags & BPF_F_ADJ_ROOM_ENCAP_L3_IPV4)
                        gso_type |= SKB_GSO_IPXIP4;
 
                if (flags & BPF_F_ADJ_ROOM_ENCAP_L4_GRE ||