diff mbox series

[bpf-next,V1,2/6] bpf: bpf_fib_lookup return MTU value as output when looked up

Message ID 160200017655.719143.17344942455389603664.stgit@firesoul (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series bpf: New approach for BPF MTU handling and enforcement | expand

Commit Message

Jesper Dangaard Brouer Oct. 6, 2020, 4:02 p.m. UTC
The BPF-helpers for FIB lookup (bpf_xdp_fib_lookup and bpf_skb_fib_lookup)
can perform MTU check and return BPF_FIB_LKUP_RET_FRAG_NEEDED.  The BPF-prog
don't know the MTU value that caused this rejection.

If the BPF-prog wants to implement PMTU (Path MTU Discovery) (rfc1191) it
need to know this MTU value for the ICMP packet.

Patch change lookup and result struct bpf_fib_lookup, to contain this MTU
value as output via a union with 'tot_len' as this is the value used for
the MTU lookup.

Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
---
 include/uapi/linux/bpf.h |   11 +++++++++--
 net/core/filter.c        |   17 ++++++++++++-----
 2 files changed, 21 insertions(+), 7 deletions(-)

Comments

Maciej Żenczykowski Oct. 7, 2020, 1:34 a.m. UTC | #1
On Tue, Oct 6, 2020 at 9:03 AM Jesper Dangaard Brouer <brouer@redhat.com> wrote:
>
> The BPF-helpers for FIB lookup (bpf_xdp_fib_lookup and bpf_skb_fib_lookup)
> can perform MTU check and return BPF_FIB_LKUP_RET_FRAG_NEEDED.  The BPF-prog
> don't know the MTU value that caused this rejection.
>
> If the BPF-prog wants to implement PMTU (Path MTU Discovery) (rfc1191) it
> need to know this MTU value for the ICMP packet.
>
> Patch change lookup and result struct bpf_fib_lookup, to contain this MTU
> value as output via a union with 'tot_len' as this is the value used for
> the MTU lookup.
>
> Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
> ---
>  include/uapi/linux/bpf.h |   11 +++++++++--
>  net/core/filter.c        |   17 ++++++++++++-----
>  2 files changed, 21 insertions(+), 7 deletions(-)
>
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index c446394135be..50ce65e37b16 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -2216,6 +2216,9 @@ union bpf_attr {
>   *             * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the
>   *               packet is not forwarded or needs assist from full stack
>   *
> + *             If lookup fails with BPF_FIB_LKUP_RET_FRAG_NEEDED, then the MTU
> + *             was exceeded and result params->mtu contains the MTU.
> + *
>   * long bpf_sock_hash_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags)
>   *     Description
>   *             Add an entry to, or update a sockhash *map* referencing sockets.
> @@ -4844,9 +4847,13 @@ struct bpf_fib_lookup {
>         __be16  sport;
>         __be16  dport;
>
> -       /* total length of packet from network header - used for MTU check */
> -       __u16   tot_len;
> +       union { /* used for MTU check */
> +               /* input to lookup */
> +               __u16   tot_len; /* total length of packet from network hdr */
>
> +               /* output: MTU value (if requested check_mtu) */
> +               __u16   mtu;
> +       };
>         /* input: L3 device index for lookup
>          * output: device index from FIB lookup
>          */
> diff --git a/net/core/filter.c b/net/core/filter.c
> index fed239e77bdc..d84723f347c0 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
> @@ -5185,13 +5185,14 @@ static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = {
>  #if IS_ENABLED(CONFIG_INET) || IS_ENABLED(CONFIG_IPV6)
>  static int bpf_fib_set_fwd_params(struct bpf_fib_lookup *params,
>                                   const struct neighbour *neigh,
> -                                 const struct net_device *dev)
> +                                 const struct net_device *dev, u32 mtu)
>  {
>         memcpy(params->dmac, neigh->ha, ETH_ALEN);
>         memcpy(params->smac, dev->dev_addr, ETH_ALEN);
>         params->h_vlan_TCI = 0;
>         params->h_vlan_proto = 0;
>         params->ifindex = dev->ifindex;
> +       params->mtu = mtu;
>
>         return 0;
>  }
> @@ -5275,8 +5276,10 @@ static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
>
>         if (check_mtu) {
>                 mtu = ip_mtu_from_fib_result(&res, params->ipv4_dst);
> -               if (params->tot_len > mtu)
> +               if (params->tot_len > mtu) {
> +                       params->mtu = mtu; /* union with tot_len */
>                         return BPF_FIB_LKUP_RET_FRAG_NEEDED;
> +               }
>         }
>
>         nhc = res.nhc;
> @@ -5309,7 +5312,7 @@ static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
>         if (!neigh)
>                 return BPF_FIB_LKUP_RET_NO_NEIGH;
>
> -       return bpf_fib_set_fwd_params(params, neigh, dev);
> +       return bpf_fib_set_fwd_params(params, neigh, dev, mtu);
>  }
>  #endif
>
> @@ -5401,8 +5404,10 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
>
>         if (check_mtu) {
>                 mtu = ipv6_stub->ip6_mtu_from_fib6(&res, dst, src);
> -               if (params->tot_len > mtu)
> +               if (params->tot_len > mtu) {
> +                       params->mtu = mtu; /* union with tot_len */
>                         return BPF_FIB_LKUP_RET_FRAG_NEEDED;
> +               }
>         }
>
>         if (res.nh->fib_nh_lws)
> @@ -5421,7 +5426,7 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
>         if (!neigh)
>                 return BPF_FIB_LKUP_RET_NO_NEIGH;
>
> -       return bpf_fib_set_fwd_params(params, neigh, dev);
> +       return bpf_fib_set_fwd_params(params, neigh, dev, mtu);
>  }
>  #endif
>
> @@ -5490,6 +5495,8 @@ BPF_CALL_4(bpf_skb_fib_lookup, struct sk_buff *, skb,
>                 dev = dev_get_by_index_rcu(net, params->ifindex);
>                 if (!is_skb_forwardable(dev, skb))
>                         rc = BPF_FIB_LKUP_RET_FRAG_NEEDED;
> +
> +               params->mtu = dev->mtu; /* union with tot_len */
>         }
>
>         return rc;
>
>

It would be beneficial to be able to fetch the route advmss, initcwnd,
etc as well...
But I take it the struct can't be extended?
kernel test robot Oct. 7, 2020, 7:28 a.m. UTC | #2
Hi Jesper,

url:    https://github.com/0day-ci/linux/commits/Jesper-Dangaard-Brouer/bpf-New-approach-for-BPF-MTU-handling-and-enforcement/20201007-000903
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
config: s390-randconfig-m031-20201002 (attached as .config)
compiler: s390-linux-gcc (GCC) 9.3.0

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

smatch warnings:
net/core/filter.c:5315 bpf_ipv4_fib_lookup() error: uninitialized symbol 'mtu'.

vim +/mtu +5315 net/core/filter.c

87f5fc7e48dd31 David Ahern            2018-05-09  5202  static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
4f74fede40df8d David Ahern            2018-05-21  5203  			       u32 flags, bool check_mtu)
87f5fc7e48dd31 David Ahern            2018-05-09  5204  {
eba618abacade7 David Ahern            2019-04-02  5205  	struct fib_nh_common *nhc;
87f5fc7e48dd31 David Ahern            2018-05-09  5206  	struct in_device *in_dev;
87f5fc7e48dd31 David Ahern            2018-05-09  5207  	struct neighbour *neigh;
87f5fc7e48dd31 David Ahern            2018-05-09  5208  	struct net_device *dev;
87f5fc7e48dd31 David Ahern            2018-05-09  5209  	struct fib_result res;
87f5fc7e48dd31 David Ahern            2018-05-09  5210  	struct flowi4 fl4;
87f5fc7e48dd31 David Ahern            2018-05-09  5211  	int err;
4f74fede40df8d David Ahern            2018-05-21  5212  	u32 mtu;
87f5fc7e48dd31 David Ahern            2018-05-09  5213  
87f5fc7e48dd31 David Ahern            2018-05-09  5214  	dev = dev_get_by_index_rcu(net, params->ifindex);
87f5fc7e48dd31 David Ahern            2018-05-09  5215  	if (unlikely(!dev))
87f5fc7e48dd31 David Ahern            2018-05-09  5216  		return -ENODEV;
87f5fc7e48dd31 David Ahern            2018-05-09  5217  
87f5fc7e48dd31 David Ahern            2018-05-09  5218  	/* verify forwarding is enabled on this interface */
87f5fc7e48dd31 David Ahern            2018-05-09  5219  	in_dev = __in_dev_get_rcu(dev);
87f5fc7e48dd31 David Ahern            2018-05-09  5220  	if (unlikely(!in_dev || !IN_DEV_FORWARD(in_dev)))
4c79579b44b187 David Ahern            2018-06-26  5221  		return BPF_FIB_LKUP_RET_FWD_DISABLED;
87f5fc7e48dd31 David Ahern            2018-05-09  5222  
87f5fc7e48dd31 David Ahern            2018-05-09  5223  	if (flags & BPF_FIB_LOOKUP_OUTPUT) {
87f5fc7e48dd31 David Ahern            2018-05-09  5224  		fl4.flowi4_iif = 1;
87f5fc7e48dd31 David Ahern            2018-05-09  5225  		fl4.flowi4_oif = params->ifindex;
87f5fc7e48dd31 David Ahern            2018-05-09  5226  	} else {
87f5fc7e48dd31 David Ahern            2018-05-09  5227  		fl4.flowi4_iif = params->ifindex;
87f5fc7e48dd31 David Ahern            2018-05-09  5228  		fl4.flowi4_oif = 0;
87f5fc7e48dd31 David Ahern            2018-05-09  5229  	}
87f5fc7e48dd31 David Ahern            2018-05-09  5230  	fl4.flowi4_tos = params->tos & IPTOS_RT_MASK;
87f5fc7e48dd31 David Ahern            2018-05-09  5231  	fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
87f5fc7e48dd31 David Ahern            2018-05-09  5232  	fl4.flowi4_flags = 0;
87f5fc7e48dd31 David Ahern            2018-05-09  5233  
87f5fc7e48dd31 David Ahern            2018-05-09  5234  	fl4.flowi4_proto = params->l4_protocol;
87f5fc7e48dd31 David Ahern            2018-05-09  5235  	fl4.daddr = params->ipv4_dst;
87f5fc7e48dd31 David Ahern            2018-05-09  5236  	fl4.saddr = params->ipv4_src;
87f5fc7e48dd31 David Ahern            2018-05-09  5237  	fl4.fl4_sport = params->sport;
87f5fc7e48dd31 David Ahern            2018-05-09  5238  	fl4.fl4_dport = params->dport;
1869e226a7b3ef David Ahern            2020-09-13  5239  	fl4.flowi4_multipath_hash = 0;
87f5fc7e48dd31 David Ahern            2018-05-09  5240  
87f5fc7e48dd31 David Ahern            2018-05-09  5241  	if (flags & BPF_FIB_LOOKUP_DIRECT) {
87f5fc7e48dd31 David Ahern            2018-05-09  5242  		u32 tbid = l3mdev_fib_table_rcu(dev) ? : RT_TABLE_MAIN;
87f5fc7e48dd31 David Ahern            2018-05-09  5243  		struct fib_table *tb;
87f5fc7e48dd31 David Ahern            2018-05-09  5244  
87f5fc7e48dd31 David Ahern            2018-05-09  5245  		tb = fib_get_table(net, tbid);
87f5fc7e48dd31 David Ahern            2018-05-09  5246  		if (unlikely(!tb))
4c79579b44b187 David Ahern            2018-06-26  5247  			return BPF_FIB_LKUP_RET_NOT_FWDED;
87f5fc7e48dd31 David Ahern            2018-05-09  5248  
87f5fc7e48dd31 David Ahern            2018-05-09  5249  		err = fib_table_lookup(tb, &fl4, &res, FIB_LOOKUP_NOREF);
87f5fc7e48dd31 David Ahern            2018-05-09  5250  	} else {
87f5fc7e48dd31 David Ahern            2018-05-09  5251  		fl4.flowi4_mark = 0;
87f5fc7e48dd31 David Ahern            2018-05-09  5252  		fl4.flowi4_secid = 0;
87f5fc7e48dd31 David Ahern            2018-05-09  5253  		fl4.flowi4_tun_key.tun_id = 0;
87f5fc7e48dd31 David Ahern            2018-05-09  5254  		fl4.flowi4_uid = sock_net_uid(net, NULL);
87f5fc7e48dd31 David Ahern            2018-05-09  5255  
87f5fc7e48dd31 David Ahern            2018-05-09  5256  		err = fib_lookup(net, &fl4, &res, FIB_LOOKUP_NOREF);
87f5fc7e48dd31 David Ahern            2018-05-09  5257  	}
87f5fc7e48dd31 David Ahern            2018-05-09  5258  
4c79579b44b187 David Ahern            2018-06-26  5259  	if (err) {
4c79579b44b187 David Ahern            2018-06-26  5260  		/* map fib lookup errors to RTN_ type */
4c79579b44b187 David Ahern            2018-06-26  5261  		if (err == -EINVAL)
4c79579b44b187 David Ahern            2018-06-26  5262  			return BPF_FIB_LKUP_RET_BLACKHOLE;
4c79579b44b187 David Ahern            2018-06-26  5263  		if (err == -EHOSTUNREACH)
4c79579b44b187 David Ahern            2018-06-26  5264  			return BPF_FIB_LKUP_RET_UNREACHABLE;
4c79579b44b187 David Ahern            2018-06-26  5265  		if (err == -EACCES)
4c79579b44b187 David Ahern            2018-06-26  5266  			return BPF_FIB_LKUP_RET_PROHIBIT;
4c79579b44b187 David Ahern            2018-06-26  5267  
4c79579b44b187 David Ahern            2018-06-26  5268  		return BPF_FIB_LKUP_RET_NOT_FWDED;
4c79579b44b187 David Ahern            2018-06-26  5269  	}
4c79579b44b187 David Ahern            2018-06-26  5270  
4c79579b44b187 David Ahern            2018-06-26  5271  	if (res.type != RTN_UNICAST)
4c79579b44b187 David Ahern            2018-06-26  5272  		return BPF_FIB_LKUP_RET_NOT_FWDED;
87f5fc7e48dd31 David Ahern            2018-05-09  5273  
5481d73f81549e David Ahern            2019-06-03  5274  	if (fib_info_num_path(res.fi) > 1)
87f5fc7e48dd31 David Ahern            2018-05-09  5275  		fib_select_path(net, &res, &fl4, NULL);
87f5fc7e48dd31 David Ahern            2018-05-09  5276  
4f74fede40df8d David Ahern            2018-05-21  5277  	if (check_mtu) {
4f74fede40df8d David Ahern            2018-05-21  5278  		mtu = ip_mtu_from_fib_result(&res, params->ipv4_dst);
ab61fc7ee5c482 Jesper Dangaard Brouer 2020-10-06  5279  		if (params->tot_len > mtu) {
ab61fc7ee5c482 Jesper Dangaard Brouer 2020-10-06  5280  			params->mtu = mtu; /* union with tot_len */
4c79579b44b187 David Ahern            2018-06-26  5281  			return BPF_FIB_LKUP_RET_FRAG_NEEDED;
4f74fede40df8d David Ahern            2018-05-21  5282  		}
ab61fc7ee5c482 Jesper Dangaard Brouer 2020-10-06  5283  	}

"mtu" is not initialized on else path.

4f74fede40df8d David Ahern            2018-05-21  5284  
eba618abacade7 David Ahern            2019-04-02  5285  	nhc = res.nhc;
87f5fc7e48dd31 David Ahern            2018-05-09  5286  
87f5fc7e48dd31 David Ahern            2018-05-09  5287  	/* do not handle lwt encaps right now */
eba618abacade7 David Ahern            2019-04-02  5288  	if (nhc->nhc_lwtstate)
4c79579b44b187 David Ahern            2018-06-26  5289  		return BPF_FIB_LKUP_RET_UNSUPP_LWT;
87f5fc7e48dd31 David Ahern            2018-05-09  5290  
eba618abacade7 David Ahern            2019-04-02  5291  	dev = nhc->nhc_dev;
87f5fc7e48dd31 David Ahern            2018-05-09  5292  
87f5fc7e48dd31 David Ahern            2018-05-09  5293  	params->rt_metric = res.fi->fib_priority;
87f5fc7e48dd31 David Ahern            2018-05-09  5294  
87f5fc7e48dd31 David Ahern            2018-05-09  5295  	/* xdp and cls_bpf programs are run in RCU-bh so
87f5fc7e48dd31 David Ahern            2018-05-09  5296  	 * rcu_read_lock_bh is not needed here
87f5fc7e48dd31 David Ahern            2018-05-09  5297  	 */
6f5f68d05ec0f6 David Ahern            2019-04-05  5298  	if (likely(nhc->nhc_gw_family != AF_INET6)) {
6f5f68d05ec0f6 David Ahern            2019-04-05  5299  		if (nhc->nhc_gw_family)
6f5f68d05ec0f6 David Ahern            2019-04-05  5300  			params->ipv4_dst = nhc->nhc_gw.ipv4;
6f5f68d05ec0f6 David Ahern            2019-04-05  5301  
6f5f68d05ec0f6 David Ahern            2019-04-05  5302  		neigh = __ipv4_neigh_lookup_noref(dev,
6f5f68d05ec0f6 David Ahern            2019-04-05  5303  						 (__force u32)params->ipv4_dst);
6f5f68d05ec0f6 David Ahern            2019-04-05  5304  	} else {
6f5f68d05ec0f6 David Ahern            2019-04-05  5305  		struct in6_addr *dst = (struct in6_addr *)params->ipv6_dst;
6f5f68d05ec0f6 David Ahern            2019-04-05  5306  
6f5f68d05ec0f6 David Ahern            2019-04-05  5307  		params->family = AF_INET6;
6f5f68d05ec0f6 David Ahern            2019-04-05  5308  		*dst = nhc->nhc_gw.ipv6;
6f5f68d05ec0f6 David Ahern            2019-04-05  5309  		neigh = __ipv6_neigh_lookup_noref_stub(dev, dst);
6f5f68d05ec0f6 David Ahern            2019-04-05  5310  	}
6f5f68d05ec0f6 David Ahern            2019-04-05  5311  
4c79579b44b187 David Ahern            2018-06-26  5312  	if (!neigh)
4c79579b44b187 David Ahern            2018-06-26  5313  		return BPF_FIB_LKUP_RET_NO_NEIGH;
87f5fc7e48dd31 David Ahern            2018-05-09  5314  
ab61fc7ee5c482 Jesper Dangaard Brouer 2020-10-06 @5315  	return bpf_fib_set_fwd_params(params, neigh, dev, mtu);
                                                                                                                  ^^^
Uninitialized.

87f5fc7e48dd31 David Ahern            2018-05-09  5316  }

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Jesper Dangaard Brouer Oct. 7, 2020, 7:42 a.m. UTC | #3
On Tue, 6 Oct 2020 18:34:50 -0700
Maciej Żenczykowski <maze@google.com> wrote:

> On Tue, Oct 6, 2020 at 9:03 AM Jesper Dangaard Brouer <brouer@redhat.com> wrote:
> >
> > The BPF-helpers for FIB lookup (bpf_xdp_fib_lookup and bpf_skb_fib_lookup)
> > can perform MTU check and return BPF_FIB_LKUP_RET_FRAG_NEEDED.  The BPF-prog
> > don't know the MTU value that caused this rejection.
> >
> > If the BPF-prog wants to implement PMTU (Path MTU Discovery) (rfc1191) it
> > need to know this MTU value for the ICMP packet.
> >
> > Patch change lookup and result struct bpf_fib_lookup, to contain this MTU
> > value as output via a union with 'tot_len' as this is the value used for
> > the MTU lookup.
> >
> > Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
> > ---
> >  include/uapi/linux/bpf.h |   11 +++++++++--
> >  net/core/filter.c        |   17 ++++++++++++-----
> >  2 files changed, 21 insertions(+), 7 deletions(-)
> >
> > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> > index c446394135be..50ce65e37b16 100644
> > --- a/include/uapi/linux/bpf.h
> > +++ b/include/uapi/linux/bpf.h
[...]
> > @@ -4844,9 +4847,13 @@ struct bpf_fib_lookup {
> >         __be16  sport;
> >         __be16  dport;
> >
> > -       /* total length of packet from network header - used for MTU check */
> > -       __u16   tot_len;
> > +       union { /* used for MTU check */
> > +               /* input to lookup */
> > +               __u16   tot_len; /* total length of packet from network hdr */
> >
> > +               /* output: MTU value (if requested check_mtu) */
> > +               __u16   mtu;
> > +       };
> >         /* input: L3 device index for lookup
> >          * output: device index from FIB lookup
> >          */
[...]
> 
> It would be beneficial to be able to fetch the route advmss, initcwnd,
> etc as well...
> But I take it the struct can't be extended?

The struct bpf_fib_lookup is exactly 1 cache-line (64 bytes) for
performance reasons.  I do believe that it can be extended, as Ahern
designed the BPF-helper API cleverly via a plen (detail below signature).

For accessing other route metric information like advmss and initcwnd,
I would expect Daniel to suggest to use BTF to access info from
dst_entry, or actually dst->_metrics. But looking at the details for
accessing dst->_metrics is complicated by macros, thus I expect BTF
would have a hard time.
David Ahern Oct. 7, 2020, 4:38 p.m. UTC | #4
On 10/7/20 12:42 AM, Jesper Dangaard Brouer wrote:
> 
> The struct bpf_fib_lookup is exactly 1 cache-line (64 bytes) for
> performance reasons.  I do believe that it can be extended, as Ahern
> designed the BPF-helper API cleverly via a plen (detail below signature).

Yes, I kept it to 64B for performance reasons which is why most fields
have 1 value on input and another on output.

Technically it can be extended, but any cost in doing so should be
abosrbed by the new feature(s). Meaning, users just doing a fib lookup
based on current API should not take a hit with the extra size.
diff mbox series

Patch

diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index c446394135be..50ce65e37b16 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -2216,6 +2216,9 @@  union bpf_attr {
  *		* > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the
  *		  packet is not forwarded or needs assist from full stack
  *
+ *		If lookup fails with BPF_FIB_LKUP_RET_FRAG_NEEDED, then the MTU
+ *		was exceeded and result params->mtu contains the MTU.
+ *
  * long bpf_sock_hash_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags)
  *	Description
  *		Add an entry to, or update a sockhash *map* referencing sockets.
@@ -4844,9 +4847,13 @@  struct bpf_fib_lookup {
 	__be16	sport;
 	__be16	dport;
 
-	/* total length of packet from network header - used for MTU check */
-	__u16	tot_len;
+	union {	/* used for MTU check */
+		/* input to lookup */
+		__u16	tot_len; /* total length of packet from network hdr */
 
+		/* output: MTU value (if requested check_mtu) */
+		__u16	mtu;
+	};
 	/* input: L3 device index for lookup
 	 * output: device index from FIB lookup
 	 */
diff --git a/net/core/filter.c b/net/core/filter.c
index fed239e77bdc..d84723f347c0 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -5185,13 +5185,14 @@  static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = {
 #if IS_ENABLED(CONFIG_INET) || IS_ENABLED(CONFIG_IPV6)
 static int bpf_fib_set_fwd_params(struct bpf_fib_lookup *params,
 				  const struct neighbour *neigh,
-				  const struct net_device *dev)
+				  const struct net_device *dev, u32 mtu)
 {
 	memcpy(params->dmac, neigh->ha, ETH_ALEN);
 	memcpy(params->smac, dev->dev_addr, ETH_ALEN);
 	params->h_vlan_TCI = 0;
 	params->h_vlan_proto = 0;
 	params->ifindex = dev->ifindex;
+	params->mtu = mtu;
 
 	return 0;
 }
@@ -5275,8 +5276,10 @@  static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
 
 	if (check_mtu) {
 		mtu = ip_mtu_from_fib_result(&res, params->ipv4_dst);
-		if (params->tot_len > mtu)
+		if (params->tot_len > mtu) {
+			params->mtu = mtu; /* union with tot_len */
 			return BPF_FIB_LKUP_RET_FRAG_NEEDED;
+		}
 	}
 
 	nhc = res.nhc;
@@ -5309,7 +5312,7 @@  static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
 	if (!neigh)
 		return BPF_FIB_LKUP_RET_NO_NEIGH;
 
-	return bpf_fib_set_fwd_params(params, neigh, dev);
+	return bpf_fib_set_fwd_params(params, neigh, dev, mtu);
 }
 #endif
 
@@ -5401,8 +5404,10 @@  static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
 
 	if (check_mtu) {
 		mtu = ipv6_stub->ip6_mtu_from_fib6(&res, dst, src);
-		if (params->tot_len > mtu)
+		if (params->tot_len > mtu) {
+			params->mtu = mtu; /* union with tot_len */
 			return BPF_FIB_LKUP_RET_FRAG_NEEDED;
+		}
 	}
 
 	if (res.nh->fib_nh_lws)
@@ -5421,7 +5426,7 @@  static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
 	if (!neigh)
 		return BPF_FIB_LKUP_RET_NO_NEIGH;
 
-	return bpf_fib_set_fwd_params(params, neigh, dev);
+	return bpf_fib_set_fwd_params(params, neigh, dev, mtu);
 }
 #endif
 
@@ -5490,6 +5495,8 @@  BPF_CALL_4(bpf_skb_fib_lookup, struct sk_buff *, skb,
 		dev = dev_get_by_index_rcu(net, params->ifindex);
 		if (!is_skb_forwardable(dev, skb))
 			rc = BPF_FIB_LKUP_RET_FRAG_NEEDED;
+
+		params->mtu = dev->mtu; /* union with tot_len */
 	}
 
 	return rc;