diff mbox series

[net-next,v7,1/3] net: flow_dissector: extend bpf flow dissector support with vnet hdr

Message ID 20210616203448.995314-2-tannerlove.kernel@gmail.com
State New
Headers show
Series [net-next,v7,1/3] net: flow_dissector: extend bpf flow dissector support with vnet hdr | expand

Commit Message

Tanner Love June 16, 2021, 8:34 p.m. UTC
From: Tanner Love <tannerlove@google.com>

Amend the bpf flow dissector program type to be able to process
virtio-net headers. Do this to enable bpf flow dissector programs to
perform virtio-net header validation. The next patch in this series
will add a flow dissection hook in virtio_net_hdr_to_skb and make use
of this extended functionality. That commit message has more
background on the use case.

Add a pointer to struct virtio_net_hdr as a new member in struct
bpf_flow_keys. When machine is big and vnet hdr is little endian, the
kernel converts the vnet hdr endianness before passing the vnet hdr
pointer to the bpf program; otherwise, the kernel just passes along
the pointer to the unaltered vnet header to the bpf program. This
handles the case of a v1+ header on a big endian machine.

Changes
v7:
  - Remove vhdr_is_little_endian, instead copy vhdr fields only in
    case where machine is big and vhdr is little endian
  - Rename check_flow_keys_access bpf_flow_dissector_is_valid_access
  - Move bpf_flow_dissector_is_valid_access() under CONFIG_NET
  - Make bpf_flow_dissector_is_valid_access() populate info.btf and
    info.reg_type in addition to info.btf_id
v6:
  - Move bpf_flow_dissector_btf_ids, check_flow_keys_access() to
    filter.c
  - Verify (off % size == 0) in check_flow_keys_access()
  - Check bpf_flow_dissector_btf_ids[0] is nonzero in
    check_flow_keys_access()
v5:
  - Use PTR_TO_BTF_ID_OR_NULL instead of defining new
    PTR_TO_VNET_HDR_OR_NULL
  - Make check_flow_keys_access() disallow writes to keys->vhdr
  - Make check_flow_keys_access() check loading keys->vhdr is in
    sizeof(__u64)
  - Use BPF_REG_AX instead of BPF_REG_TMP as scratch reg
  - Describe parameter vhdr_is_little_endian in __skb_flow_dissect
    documentation
v4:
  - Add virtio_net_hdr pointer to struct bpf_flow_keys
  - Add vhdr_is_little_endian to struct bpf_flow_keys
v2:
  - Describe parameter vhdr in __skb_flow_dissect documentation

Signed-off-by: Tanner Love <tannerlove@google.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Reviewed-by: Petar Penkov <ppenkov@google.com>
Reviewed-by: Stanislav Fomichev <sdf@google.com>
---
 drivers/net/bonding/bond_main.c |  2 +-
 include/linux/bpf.h             |  8 +++++
 include/linux/skbuff.h          | 35 +++++++++++++++++-----
 include/uapi/linux/bpf.h        |  1 +
 kernel/bpf/verifier.c           | 33 ++++++++++----------
 net/bpf/test_run.c              |  2 +-
 net/core/filter.c               | 53 +++++++++++++++++++++++++++++++++
 net/core/flow_dissector.c       | 36 +++++++++++++++++++---
 tools/include/uapi/linux/bpf.h  |  1 +
 9 files changed, 143 insertions(+), 28 deletions(-)

Comments

kernel test robot June 17, 2021, 7:43 a.m. UTC | #1
Hi Tanner,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Tanner-Love/virtio_net-add-optional-flow-dissection-in-virtio_net_hdr_to_skb/20210617-082208
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 0c33795231bff5df410bd405b569c66851e92d4b
config: m68k-randconfig-r023-20210617 (attached as .config)
compiler: m68k-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/b03a1eb684b925a09ae011d0e620d98ebf3b0abd
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Tanner-Love/virtio_net-add-optional-flow-dissection-in-virtio_net_hdr_to_skb/20210617-082208
        git checkout b03a1eb684b925a09ae011d0e620d98ebf3b0abd
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=m68k 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All error/warnings (new ones prefixed by >>):

   net/core/filter.c: In function 'bpf_flow_keys_is_valid_access':
>> net/core/filter.c:7900:15: error: implicit declaration of function 'bpf_get_btf_vmlinux' [-Werror=implicit-function-declaration]

    7900 |   info->btf = bpf_get_btf_vmlinux();
         |               ^~~~~~~~~~~~~~~~~~~
>> net/core/filter.c:7900:13: warning: assignment to 'struct btf *' from 'int' makes pointer from integer without a cast [-Wint-conversion]

    7900 |   info->btf = bpf_get_btf_vmlinux();
         |             ^
   cc1: some warnings being treated as errors


vim +/bpf_get_btf_vmlinux +7900 net/core/filter.c

  7884	
  7885	int bpf_flow_keys_is_valid_access(int off, int size, enum bpf_access_type t,
  7886					  struct bpf_insn_access_aux *info)
  7887	{
  7888		if (off < 0 ||
  7889		    (u64)off + size > offsetofend(struct bpf_flow_keys, vhdr))
  7890			return -EACCES;
  7891	
  7892		switch (off) {
  7893		case bpf_ctx_range_ptr(struct bpf_flow_keys, vhdr):
  7894			if (t == BPF_WRITE || off % size != 0 || size != sizeof(__u64))
  7895				return -EACCES;
  7896	
  7897			if (!bpf_flow_dissector_btf_ids[0])
  7898				return -EINVAL;
  7899	
> 7900			info->btf = bpf_get_btf_vmlinux();

  7901			info->reg_type = PTR_TO_BTF_ID_OR_NULL;
  7902			info->btf_id = bpf_flow_dissector_btf_ids[0];
  7903	
  7904			break;
  7905		}
  7906	
  7907		return 0;
  7908	}
  7909	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
kernel test robot June 17, 2021, 1:32 p.m. UTC | #2
Hi Tanner,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Tanner-Love/virtio_net-add-optional-flow-dissection-in-virtio_net_hdr_to_skb/20210617-082208
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 0c33795231bff5df410bd405b569c66851e92d4b
config: x86_64-randconfig-a014-20210617 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 64720f57bea6a6bf033feef4a5751ab9c0c3b401)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # https://github.com/0day-ci/linux/commit/b03a1eb684b925a09ae011d0e620d98ebf3b0abd
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Tanner-Love/virtio_net-add-optional-flow-dissection-in-virtio_net_hdr_to_skb/20210617-082208
        git checkout b03a1eb684b925a09ae011d0e620d98ebf3b0abd
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   net/core/filter.c:7900:15: error: implicit declaration of function 'bpf_get_btf_vmlinux' [-Werror,-Wimplicit-function-declaration]
                   info->btf = bpf_get_btf_vmlinux();
                               ^
>> net/core/filter.c:7900:13: warning: incompatible integer to pointer conversion assigning to 'struct btf *' from 'int' [-Wint-conversion]

                   info->btf = bpf_get_btf_vmlinux();
                             ^ ~~~~~~~~~~~~~~~~~~~~~
   1 warning and 1 error generated.


vim +7900 net/core/filter.c

  7884	
  7885	int bpf_flow_keys_is_valid_access(int off, int size, enum bpf_access_type t,
  7886					  struct bpf_insn_access_aux *info)
  7887	{
  7888		if (off < 0 ||
  7889		    (u64)off + size > offsetofend(struct bpf_flow_keys, vhdr))
  7890			return -EACCES;
  7891	
  7892		switch (off) {
  7893		case bpf_ctx_range_ptr(struct bpf_flow_keys, vhdr):
  7894			if (t == BPF_WRITE || off % size != 0 || size != sizeof(__u64))
  7895				return -EACCES;
  7896	
  7897			if (!bpf_flow_dissector_btf_ids[0])
  7898				return -EINVAL;
  7899	
> 7900			info->btf = bpf_get_btf_vmlinux();

  7901			info->reg_type = PTR_TO_BTF_ID_OR_NULL;
  7902			info->btf_id = bpf_flow_dissector_btf_ids[0];
  7903	
  7904			break;
  7905		}
  7906	
  7907		return 0;
  7908	}
  7909	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
kernel test robot June 17, 2021, 5:07 p.m. UTC | #3
Hi Tanner,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Tanner-Love/virtio_net-add-optional-flow-dissection-in-virtio_net_hdr_to_skb/20210617-082208
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 0c33795231bff5df410bd405b569c66851e92d4b
config: s390-randconfig-s031-20210617 (attached as .config)
compiler: s390-linux-gcc (GCC) 9.3.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # apt-get install sparse
        # sparse version: v0.6.3-341-g8af24329-dirty
        # https://github.com/0day-ci/linux/commit/b03a1eb684b925a09ae011d0e620d98ebf3b0abd
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Tanner-Love/virtio_net-add-optional-flow-dissection-in-virtio_net_hdr_to_skb/20210617-082208
        git checkout b03a1eb684b925a09ae011d0e620d98ebf3b0abd
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' W=1 ARCH=s390 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>


sparse warnings: (new ones prefixed by >>)
>> net/core/flow_dissector.c:882:40: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __virtio16 [assigned] [usertype] hdr_len @@     got unsigned short @@

   net/core/flow_dissector.c:882:40: sparse:     expected restricted __virtio16 [assigned] [usertype] hdr_len
   net/core/flow_dissector.c:882:40: sparse:     got unsigned short
>> net/core/flow_dissector.c:884:41: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __virtio16 [assigned] [usertype] gso_size @@     got unsigned short @@

   net/core/flow_dissector.c:884:41: sparse:     expected restricted __virtio16 [assigned] [usertype] gso_size
   net/core/flow_dissector.c:884:41: sparse:     got unsigned short
>> net/core/flow_dissector.c:886:43: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __virtio16 [assigned] [usertype] csum_start @@     got unsigned short @@

   net/core/flow_dissector.c:886:43: sparse:     expected restricted __virtio16 [assigned] [usertype] csum_start
   net/core/flow_dissector.c:886:43: sparse:     got unsigned short
>> net/core/flow_dissector.c:888:44: sparse: sparse: incorrect type in assignment (different base types) @@     expected restricted __virtio16 [assigned] [usertype] csum_offset @@     got unsigned short @@

   net/core/flow_dissector.c:888:44: sparse:     expected restricted __virtio16 [assigned] [usertype] csum_offset
   net/core/flow_dissector.c:888:44: sparse:     got unsigned short

vim +882 net/core/flow_dissector.c

   866	
   867	bool bpf_flow_dissect(struct bpf_prog *prog, struct bpf_flow_dissector *ctx,
   868			      __be16 proto, int nhoff, int hlen, unsigned int flags,
   869			      const struct virtio_net_hdr *vhdr,
   870			      bool vhdr_is_little_endian)
   871	{
   872		struct bpf_flow_keys *flow_keys = ctx->flow_keys;
   873		u32 result;
   874	
   875	/* vnet hdr is either machine endian (virtio spec < v1) or le (>= v1) */
   876	#if defined(__BIG_ENDIAN_BITFIELD)
   877		struct virtio_net_hdr vnet_hdr_local;
   878	
   879		if (vhdr && vhdr_is_little_endian) {
   880			vnet_hdr_local.flags = vhdr->flags;
   881			vnet_hdr_local.gso_type = vhdr->gso_type;
 > 882			vnet_hdr_local.hdr_len = __virtio16_to_cpu(false,

   883								   vhdr->hdr_len);
 > 884			vnet_hdr_local.gso_size = __virtio16_to_cpu(false,

   885								    vhdr->gso_size);
 > 886			vnet_hdr_local.csum_start = __virtio16_to_cpu(false,

   887								      vhdr->csum_start);
 > 888			vnet_hdr_local.csum_offset = __virtio16_to_cpu(false,

   889								       vhdr->csum_offset);
   890			vhdr = &vnet_hdr_local;
   891		}
   892	#endif
   893	
   894		/* Pass parameters to the BPF program */
   895		memset(flow_keys, 0, sizeof(*flow_keys));
   896		flow_keys->n_proto = proto;
   897		flow_keys->nhoff = nhoff;
   898		flow_keys->thoff = flow_keys->nhoff;
   899		flow_keys->vhdr = vhdr;
   900	
   901		BUILD_BUG_ON((int)BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG !=
   902			     (int)FLOW_DISSECTOR_F_PARSE_1ST_FRAG);
   903		BUILD_BUG_ON((int)BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL !=
   904			     (int)FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);
   905		BUILD_BUG_ON((int)BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP !=
   906			     (int)FLOW_DISSECTOR_F_STOP_AT_ENCAP);
   907		flow_keys->flags = flags;
   908	
   909		result = bpf_prog_run_pin_on_cpu(prog, ctx);
   910	
   911		flow_keys->nhoff = clamp_t(u16, flow_keys->nhoff, nhoff, hlen);
   912		flow_keys->thoff = clamp_t(u16, flow_keys->thoff,
   913					   flow_keys->nhoff, hlen);
   914	
   915		return result == BPF_OK;
   916	}
   917	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index eb79a9f05914..36993636d56d 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3554,7 +3554,7 @@  static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
 	case BOND_XMIT_POLICY_ENCAP34:
 		memset(fk, 0, sizeof(*fk));
 		return __skb_flow_dissect(NULL, skb, &flow_keys_bonding,
-					  fk, NULL, 0, 0, 0, 0);
+					  fk, NULL, 0, 0, 0, 0, NULL, false);
 	default:
 		break;
 	}
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 9dc44ba97584..e6980da0b469 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1998,6 +1998,8 @@  u32 bpf_sock_convert_ctx_access(enum bpf_access_type type,
 				struct bpf_insn *insn_buf,
 				struct bpf_prog *prog,
 				u32 *target_size);
+int bpf_flow_keys_is_valid_access(int off, int size, enum bpf_access_type t,
+				  struct bpf_insn_access_aux *info);
 #else
 static inline bool bpf_sock_common_is_valid_access(int off, int size,
 						   enum bpf_access_type type,
@@ -2019,6 +2021,12 @@  static inline u32 bpf_sock_convert_ctx_access(enum bpf_access_type type,
 {
 	return 0;
 }
+static inline int bpf_flow_keys_is_valid_access(int off, int size,
+						enum bpf_access_type t,
+						struct bpf_insn_access_aux *info)
+{
+	return -EINVAL;
+}
 #endif
 
 #ifdef CONFIG_INET
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index b2db9cd9a73f..4e390cd8f72a 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -1314,21 +1314,27 @@  void skb_flow_dissector_init(struct flow_dissector *flow_dissector,
 			     unsigned int key_count);
 
 struct bpf_flow_dissector;
+struct virtio_net_hdr;
 bool bpf_flow_dissect(struct bpf_prog *prog, struct bpf_flow_dissector *ctx,
-		      __be16 proto, int nhoff, int hlen, unsigned int flags);
+		      __be16 proto, int nhoff, int hlen, unsigned int flags,
+		      const struct virtio_net_hdr *vhdr,
+		      bool vhdr_is_little_endian);
 
 bool __skb_flow_dissect(const struct net *net,
 			const struct sk_buff *skb,
 			struct flow_dissector *flow_dissector,
 			void *target_container, const void *data,
-			__be16 proto, int nhoff, int hlen, unsigned int flags);
+			__be16 proto, int nhoff, int hlen, unsigned int flags,
+			const struct virtio_net_hdr *vhdr,
+			bool vhdr_is_little_endian);
 
 static inline bool skb_flow_dissect(const struct sk_buff *skb,
 				    struct flow_dissector *flow_dissector,
 				    void *target_container, unsigned int flags)
 {
 	return __skb_flow_dissect(NULL, skb, flow_dissector,
-				  target_container, NULL, 0, 0, 0, flags);
+				  target_container, NULL, 0, 0, 0, flags, NULL,
+				  false);
 }
 
 static inline bool skb_flow_dissect_flow_keys(const struct sk_buff *skb,
@@ -1337,7 +1343,22 @@  static inline bool skb_flow_dissect_flow_keys(const struct sk_buff *skb,
 {
 	memset(flow, 0, sizeof(*flow));
 	return __skb_flow_dissect(NULL, skb, &flow_keys_dissector,
-				  flow, NULL, 0, 0, 0, flags);
+				  flow, NULL, 0, 0, 0, flags, NULL, false);
+}
+
+static inline bool
+__skb_flow_dissect_flow_keys_basic(const struct net *net,
+				   const struct sk_buff *skb,
+				   struct flow_keys_basic *flow,
+				   const void *data, __be16 proto,
+				   int nhoff, int hlen, unsigned int flags,
+				   const struct virtio_net_hdr *vhdr,
+				   bool vhdr_is_little_endian)
+{
+	memset(flow, 0, sizeof(*flow));
+	return __skb_flow_dissect(net, skb, &flow_keys_basic_dissector, flow,
+				  data, proto, nhoff, hlen, flags, vhdr,
+				  vhdr_is_little_endian);
 }
 
 static inline bool
@@ -1347,9 +1368,9 @@  skb_flow_dissect_flow_keys_basic(const struct net *net,
 				 const void *data, __be16 proto,
 				 int nhoff, int hlen, unsigned int flags)
 {
-	memset(flow, 0, sizeof(*flow));
-	return __skb_flow_dissect(net, skb, &flow_keys_basic_dissector, flow,
-				  data, proto, nhoff, hlen, flags);
+	return __skb_flow_dissect_flow_keys_basic(net, skb, flow, data, proto,
+						  nhoff, hlen, flags, NULL,
+						  false);
 }
 
 void skb_flow_dissect_meta(const struct sk_buff *skb,
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 418b9b813d65..0524dec15c6d 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -6017,6 +6017,7 @@  struct bpf_flow_keys {
 	};
 	__u32	flags;
 	__be32	flow_label;
+	__bpf_md_ptr(const struct virtio_net_hdr *, vhdr);
 };
 
 struct bpf_func_info {
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 331b170d9fcc..d4876d5e8959 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3372,18 +3372,6 @@  static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,
 	return -EACCES;
 }
 
-static int check_flow_keys_access(struct bpf_verifier_env *env, int off,
-				  int size)
-{
-	if (size < 0 || off < 0 ||
-	    (u64)off + size > sizeof(struct bpf_flow_keys)) {
-		verbose(env, "invalid access to flow keys off=%d size=%d\n",
-			off, size);
-		return -EACCES;
-	}
-	return 0;
-}
-
 static int check_sock_access(struct bpf_verifier_env *env, int insn_idx,
 			     u32 regno, int off, int size,
 			     enum bpf_access_type t)
@@ -4210,6 +4198,8 @@  static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
 		if (!err && t == BPF_READ && value_regno >= 0)
 			mark_reg_unknown(env, regs, value_regno);
 	} else if (reg->type == PTR_TO_FLOW_KEYS) {
+		struct bpf_insn_access_aux info = {};
+
 		if (t == BPF_WRITE && value_regno >= 0 &&
 		    is_pointer_value(env, value_regno)) {
 			verbose(env, "R%d leaks addr into flow keys\n",
@@ -4217,9 +4207,22 @@  static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
 			return -EACCES;
 		}
 
-		err = check_flow_keys_access(env, off, size);
-		if (!err && t == BPF_READ && value_regno >= 0)
-			mark_reg_unknown(env, regs, value_regno);
+		err = bpf_flow_keys_is_valid_access(off, size, t, &info);
+		if (err) {
+			verbose(env,
+				"invalid access to flow keys off=%d size=%d\n",
+				off, size);
+		} else if (t == BPF_READ && value_regno >= 0) {
+			if (info.reg_type == PTR_TO_BTF_ID_OR_NULL) {
+				mark_reg_known_zero(env, regs, value_regno);
+				regs[value_regno].type = info.reg_type;
+				regs[value_regno].btf = info.btf;
+				regs[value_regno].btf_id = info.btf_id;
+				regs[value_regno].id = ++env->id_gen;
+			} else {
+				mark_reg_unknown(env, regs, value_regno);
+			}
+		}
 	} else if (type_is_sk_pointer(reg->type)) {
 		if (t == BPF_WRITE) {
 			verbose(env, "R%d cannot write into %s\n",
diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c
index aa47af349ba8..a11c5ce99ccb 100644
--- a/net/bpf/test_run.c
+++ b/net/bpf/test_run.c
@@ -797,7 +797,7 @@  int bpf_prog_test_run_flow_dissector(struct bpf_prog *prog,
 	bpf_test_timer_enter(&t);
 	do {
 		retval = bpf_flow_dissect(prog, &ctx, eth->h_proto, ETH_HLEN,
-					  size, flags);
+					  size, flags, NULL, false);
 	} while (bpf_test_timer_continue(&t, repeat, &ret, &duration));
 	bpf_test_timer_leave(&t);
 
diff --git a/net/core/filter.c b/net/core/filter.c
index 239de1306de9..c3964ef8f387 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -7880,6 +7880,33 @@  static bool sock_filter_is_valid_access(int off, int size,
 					       prog->expected_attach_type);
 }
 
+BTF_ID_LIST_SINGLE(bpf_flow_dissector_btf_ids, struct, virtio_net_hdr);
+
+int bpf_flow_keys_is_valid_access(int off, int size, enum bpf_access_type t,
+				  struct bpf_insn_access_aux *info)
+{
+	if (off < 0 ||
+	    (u64)off + size > offsetofend(struct bpf_flow_keys, vhdr))
+		return -EACCES;
+
+	switch (off) {
+	case bpf_ctx_range_ptr(struct bpf_flow_keys, vhdr):
+		if (t == BPF_WRITE || off % size != 0 || size != sizeof(__u64))
+			return -EACCES;
+
+		if (!bpf_flow_dissector_btf_ids[0])
+			return -EINVAL;
+
+		info->btf = bpf_get_btf_vmlinux();
+		info->reg_type = PTR_TO_BTF_ID_OR_NULL;
+		info->btf_id = bpf_flow_dissector_btf_ids[0];
+
+		break;
+	}
+
+	return 0;
+}
+
 static int bpf_noop_prologue(struct bpf_insn *insn_buf, bool direct_write,
 			     const struct bpf_prog *prog)
 {
@@ -8358,6 +8385,8 @@  static bool flow_dissector_is_valid_access(int off, int size,
 			return false;
 		info->reg_type = PTR_TO_FLOW_KEYS;
 		return true;
+	case bpf_ctx_range(struct __sk_buff, len):
+		return size == size_default;
 	default:
 		return false;
 	}
@@ -8390,6 +8419,30 @@  static u32 flow_dissector_convert_ctx_access(enum bpf_access_type type,
 				      si->dst_reg, si->src_reg,
 				      offsetof(struct bpf_flow_dissector, flow_keys));
 		break;
+
+	case offsetof(struct __sk_buff, len):
+		*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_flow_dissector, skb),
+				      si->dst_reg, si->src_reg,
+				      offsetof(struct bpf_flow_dissector, skb));
+		*insn++ = BPF_JMP_IMM(BPF_JNE, si->dst_reg, 0, 4);
+		/* bpf_flow_dissector->skb == NULL */
+		/* dst_reg = bpf_flow_dissector->data_end */
+		*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_flow_dissector, data_end),
+				      si->dst_reg, si->src_reg,
+				      offsetof(struct bpf_flow_dissector, data_end));
+		/* AX = bpf_flow_dissector->data */
+		*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_flow_dissector, data),
+				      BPF_REG_AX, si->src_reg,
+				      offsetof(struct bpf_flow_dissector, data));
+		/* dst_reg -= bpf_flow_dissector->data */
+		*insn++ = BPF_ALU64_REG(BPF_SUB, si->dst_reg, BPF_REG_AX);
+		*insn++ = BPF_JMP_A(1);
+		/* bpf_flow_dissector->skb != NULL */
+		/* bpf_flow_dissector->skb->len */
+		*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, len),
+				      si->dst_reg, si->dst_reg,
+				      offsetof(struct sk_buff, len));
+		break;
 	}
 
 	return insn - insn_buf;
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 2aadbfc5193b..609e24ba98ea 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -28,6 +28,7 @@ 
 #include <scsi/fc/fc_fcoe.h>
 #include <uapi/linux/batadv_packet.h>
 #include <linux/bpf.h>
+#include <linux/virtio_net.h>
 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
 #include <net/netfilter/nf_conntrack_core.h>
 #include <net/netfilter/nf_conntrack_labels.h>
@@ -864,16 +865,38 @@  static void __skb_flow_bpf_to_target(const struct bpf_flow_keys *flow_keys,
 }
 
 bool bpf_flow_dissect(struct bpf_prog *prog, struct bpf_flow_dissector *ctx,
-		      __be16 proto, int nhoff, int hlen, unsigned int flags)
+		      __be16 proto, int nhoff, int hlen, unsigned int flags,
+		      const struct virtio_net_hdr *vhdr,
+		      bool vhdr_is_little_endian)
 {
 	struct bpf_flow_keys *flow_keys = ctx->flow_keys;
 	u32 result;
 
+/* vnet hdr is either machine endian (virtio spec < v1) or le (>= v1) */
+#if defined(__BIG_ENDIAN_BITFIELD)
+	struct virtio_net_hdr vnet_hdr_local;
+
+	if (vhdr && vhdr_is_little_endian) {
+		vnet_hdr_local.flags = vhdr->flags;
+		vnet_hdr_local.gso_type = vhdr->gso_type;
+		vnet_hdr_local.hdr_len = __virtio16_to_cpu(false,
+							   vhdr->hdr_len);
+		vnet_hdr_local.gso_size = __virtio16_to_cpu(false,
+							    vhdr->gso_size);
+		vnet_hdr_local.csum_start = __virtio16_to_cpu(false,
+							      vhdr->csum_start);
+		vnet_hdr_local.csum_offset = __virtio16_to_cpu(false,
+							       vhdr->csum_offset);
+		vhdr = &vnet_hdr_local;
+	}
+#endif
+
 	/* Pass parameters to the BPF program */
 	memset(flow_keys, 0, sizeof(*flow_keys));
 	flow_keys->n_proto = proto;
 	flow_keys->nhoff = nhoff;
 	flow_keys->thoff = flow_keys->nhoff;
+	flow_keys->vhdr = vhdr;
 
 	BUILD_BUG_ON((int)BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG !=
 		     (int)FLOW_DISSECTOR_F_PARSE_1ST_FRAG);
@@ -904,6 +927,8 @@  bool bpf_flow_dissect(struct bpf_prog *prog, struct bpf_flow_dissector *ctx,
  * @hlen: packet header length, if @data is NULL use skb_headlen(skb)
  * @flags: flags that control the dissection process, e.g.
  *         FLOW_DISSECTOR_F_STOP_AT_ENCAP.
+ * @vhdr: virtio_net_header to include in kernel context for BPF flow dissector
+ * @vhdr_is_little_endian: whether virtio_net_hdr fields are little endian
  *
  * The function will try to retrieve individual keys into target specified
  * by flow_dissector from either the skbuff or a raw buffer specified by the
@@ -915,7 +940,9 @@  bool __skb_flow_dissect(const struct net *net,
 			const struct sk_buff *skb,
 			struct flow_dissector *flow_dissector,
 			void *target_container, const void *data,
-			__be16 proto, int nhoff, int hlen, unsigned int flags)
+			__be16 proto, int nhoff, int hlen, unsigned int flags,
+			const struct virtio_net_hdr *vhdr,
+			bool vhdr_is_little_endian)
 {
 	struct flow_dissector_key_control *key_control;
 	struct flow_dissector_key_basic *key_basic;
@@ -1012,7 +1039,8 @@  bool __skb_flow_dissect(const struct net *net,
 
 			prog = READ_ONCE(run_array->items[0].prog);
 			ret = bpf_flow_dissect(prog, &ctx, n_proto, nhoff,
-					       hlen, flags);
+					       hlen, flags, vhdr,
+					       vhdr_is_little_endian);
 			__skb_flow_bpf_to_target(&flow_keys, flow_dissector,
 						 target_container);
 			rcu_read_unlock();
@@ -1610,7 +1638,7 @@  u32 __skb_get_hash_symmetric(const struct sk_buff *skb)
 	memset(&keys, 0, sizeof(keys));
 	__skb_flow_dissect(NULL, skb, &flow_keys_dissector_symmetric,
 			   &keys, NULL, 0, 0, 0,
-			   FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);
+			   FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL, NULL, false);
 
 	return __flow_hash_from_keys(&keys, &hashrnd);
 }
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 418b9b813d65..0524dec15c6d 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -6017,6 +6017,7 @@  struct bpf_flow_keys {
 	};
 	__u32	flags;
 	__be32	flow_label;
+	__bpf_md_ptr(const struct virtio_net_hdr *, vhdr);
 };
 
 struct bpf_func_info {