The previous patch made the ip6_gre helpers return the drop reason of ip6_tnl_xmit() and their own, but ip6gre_tunnel_xmit() and ip6erspan_tunnel_xmit() still free the packet with a plain kfree_skb(). Free it with the reason instead, and give the drops of the handlers themselves one, from the same set: - the length reason from pskb_inet_may_pull_reason(), as in ip_gre, - SKB_DROP_REASON_NO_TX_TARGET for a device without a remote address that is not collect_md, every packet of which ip6_tnl_xmit_ctl() refuses, as the handlers pass it the addresses of the device; ip6_tnl_xmit() reports an NBMA tunnel that found no endpoint the same way, - SKB_DROP_REASON_DEV_READY for the other refusals of ip6_tnl_xmit_ctl(), which, as the previous patch explains, include a routing loop, - SKB_DROP_REASON_NOMEM for the offload setup, the trim of a packet longer than the MTU and the headroom, - SKB_DROP_REASON_TUNNEL_TXINFO for the collect_md metadata checks of ip6erspan, and for a packet without IPv6 metadata in ip6gre (see below), - SKB_DROP_REASON_UNHANDLED_PROTO for an ERSPAN version that is not implemented, - SKB_DROP_REASON_RECURSION_LIMIT and SKB_DROP_REASON_IPV6_BAD_EXTHDR for the checks that ip6erspan_tunnel_xmit() does itself, the same as in ip6gre_xmit_ipv6(). ip6gre_tunnel_xmit() looks up the metadata of a collect_md device before it passes the packet to a helper, but leaves the drop of a packet without usable metadata, missing or not of the IPv6 family, to the helpers, and ip6gre_xmit_ipv6() checks the source address first. The raddr of such a device is ::, so such a packet sent from ::, like the device's own DAD probe, would be reported as SKB_DROP_REASON_RECURSION_LIMIT, and other such packets as SKB_DROP_REASON_TUNNEL_TXINFO. Drop it right after the lookup instead, as SKB_DROP_REASON_TUNNEL_TXINFO, with the test ip6erspan_tunnel_xmit() uses; the IPv4 handlers also test presence and family together. The helpers dropped such a packet before ip6_tnl_xmit() in any case, and tx_err counts it by the result of the lookup, so only the reason changes; the test in __gre6_xmit() stays as a safeguard. A packet from :: that carries IPv6 metadata still meets the check in ip6gre_xmit_ipv6(). Such an ip6gre device is an NBMA tunnel: ip6gre_header() puts the endpoint of the packet in its outer header, and __gre6_xmit() would take it from there, but ip6gre_tunnel_xmit() refuses the packet before that, so the device sends nothing. This patch does not change that; it reports these drops like those of an ip6gretap or ip6erspan device without a remote address, which has no endpoint at all. Assisted-by: LLM Signed-off-by: Anton Danilov --- net/ipv6/ip6_gre.c | 73 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 9e94ea6b6c20..dbca78230815 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -889,14 +889,28 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb, enum skb_drop_reason reason; __be16 payload_protocol; - if (!pskb_inet_may_pull(skb)) + reason = pskb_inet_may_pull_reason(skb); + if (reason) goto tx_err; - if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr)) + if (!t->parms.collect_md && ipv6_addr_any(&t->parms.raddr)) { + reason = SKB_DROP_REASON_NO_TX_TARGET; goto tx_err; + } - if (t->parms.collect_md) + if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr)) { + reason = SKB_DROP_REASON_DEV_READY; + goto tx_err; + } + + if (t->parms.collect_md) { tun_info = skb_tunnel_info_txcheck(skb); + if (IS_ERR(tun_info) || + unlikely(ip_tunnel_info_af(tun_info) != AF_INET6)) { + reason = SKB_DROP_REASON_TUNNEL_TXINFO; + goto tx_err; + } + } payload_protocol = skb_protocol(skb, true); switch (payload_protocol) { @@ -917,10 +931,11 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb, return NETDEV_TX_OK; tx_err: + reason = reason ?: SKB_DROP_REASON_NOT_SPECIFIED; if (!IS_ERR(tun_info)) DEV_STATS_INC(dev, tx_errors); DEV_STATS_INC(dev, tx_dropped); - kfree_skb(skb); + kfree_skb_reason(skb, reason); return NETDEV_TX_OK; } @@ -940,18 +955,30 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, __u32 mtu; int nhoff; - if (!pskb_inet_may_pull(skb)) + reason = pskb_inet_may_pull_reason(skb); + if (reason) goto tx_err; - if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr)) + if (!t->parms.collect_md && ipv6_addr_any(&t->parms.raddr)) { + reason = SKB_DROP_REASON_NO_TX_TARGET; goto tx_err; + } - if (gre_handle_offloads(skb, false)) + if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr)) { + reason = SKB_DROP_REASON_DEV_READY; goto tx_err; + } + + if (gre_handle_offloads(skb, false)) { + reason = SKB_DROP_REASON_NOMEM; + goto tx_err; + } if (skb->len > dev->mtu + dev->hard_header_len) { - if (pskb_trim(skb, dev->mtu + dev->hard_header_len)) + if (pskb_trim(skb, dev->mtu + dev->hard_header_len)) { + reason = SKB_DROP_REASON_NOMEM; goto tx_err; + } truncate = true; } @@ -971,8 +998,10 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, truncate = true; } - if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen)) + if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen)) { + reason = SKB_DROP_REASON_NOMEM; goto tx_err; + } IPCB(skb)->flags = 0; @@ -986,8 +1015,10 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, tun_info = skb_tunnel_info_txcheck(skb); if (IS_ERR(tun_info) || - unlikely(ip_tunnel_info_af(tun_info) != AF_INET6)) + unlikely(ip_tunnel_info_af(tun_info) != AF_INET6)) { + reason = SKB_DROP_REASON_TUNNEL_TXINFO; goto tx_err; + } key = &tun_info->key; memset(&fl6, 0, sizeof(fl6)); @@ -999,10 +1030,14 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, dsfield = key->tos; if (!test_bit(IP_TUNNEL_ERSPAN_OPT_BIT, - tun_info->key.tun_flags)) + tun_info->key.tun_flags)) { + reason = SKB_DROP_REASON_TUNNEL_TXINFO; goto tx_err; - if (tun_info->options_len < sizeof(*md)) + } + if (tun_info->options_len < sizeof(*md)) { + reason = SKB_DROP_REASON_TUNNEL_TXINFO; goto tx_err; + } md = ip_tunnel_info_opts(tun_info); tun_id = tunnel_id_to_key32(key->tun_id); @@ -1020,6 +1055,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, truncate, false); proto = htons(ETH_P_ERSPAN2); } else { + reason = SKB_DROP_REASON_UNHANDLED_PROTO; goto tx_err; } } else { @@ -1030,11 +1066,16 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, &dsfield, &encap_limit); break; case htons(ETH_P_IPV6): - if (ipv6_addr_equal(&t->parms.raddr, &ipv6_hdr(skb)->saddr)) + if (ipv6_addr_equal(&t->parms.raddr, + &ipv6_hdr(skb)->saddr)) { + reason = SKB_DROP_REASON_RECURSION_LIMIT; goto tx_err; + } if (prepare_ip6gre_xmit_ipv6(skb, dev, &fl6, - &dsfield, &encap_limit)) + &dsfield, &encap_limit)) { + reason = SKB_DROP_REASON_IPV6_BAD_EXTHDR; goto tx_err; + } break; default: memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6)); @@ -1053,6 +1094,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, truncate, false); proto = htons(ETH_P_ERSPAN2); } else { + reason = SKB_DROP_REASON_UNHANDLED_PROTO; goto tx_err; } @@ -1087,10 +1129,11 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, return NETDEV_TX_OK; tx_err: + reason = reason ?: SKB_DROP_REASON_NOT_SPECIFIED; if (!IS_ERR(tun_info)) DEV_STATS_INC(dev, tx_errors); DEV_STATS_INC(dev, tx_dropped); - kfree_skb(skb); + kfree_skb_reason(skb, reason); return NETDEV_TX_OK; } -- 2.47.3