]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: ipv4: ipmr: Split ipmr_queue_xmit() in two
authorPetr Machata <petrm@nvidia.com>
Mon, 16 Jun 2025 22:44:11 +0000 (00:44 +0200)
committerJakub Kicinski <kuba@kernel.org>
Wed, 18 Jun 2025 01:18:45 +0000 (18:18 -0700)
Some of the work of ipmr_queue_xmit() is specific to IPMR forwarding, and
should not take place on the output path. In order to allow reuse of the
common parts, split the function into two: the ipmr_prepare_xmit() helper
that takes care of the common bits, and the ipmr_queue_fwd_xmit(), which
invokes the former and encapsulates the whole forwarding algorithm.

Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
Link: https://patch.msgid.link/4e8db165572a4f8bd29a723a801e854e9d20df4d.1750113335.git.petrm@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv4/ipmr.c

index d2ac630bea3a8b40f19773620a34409ce61e8d44..74d45fd5d11ea3ad94cf6657f666e03b18ddc086 100644 (file)
@@ -1853,8 +1853,8 @@ static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt,
 
 /* Processing handlers for ipmr_forward, under rcu_read_lock() */
 
-static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
-                           int in_vifi, struct sk_buff *skb, int vifi)
+static int ipmr_prepare_xmit(struct net *net, struct mr_table *mrt,
+                            struct sk_buff *skb, int vifi)
 {
        const struct iphdr *iph = ip_hdr(skb);
        struct vif_device *vif = &mrt->vif_table[vifi];
@@ -1865,7 +1865,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
 
        vif_dev = vif_dev_read(vif);
        if (!vif_dev)
-               goto out_free;
+               return -1;
 
        if (vif->flags & VIFF_REGISTER) {
                WRITE_ONCE(vif->pkt_out, vif->pkt_out + 1);
@@ -1873,12 +1873,9 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
                DEV_STATS_ADD(vif_dev, tx_bytes, skb->len);
                DEV_STATS_INC(vif_dev, tx_packets);
                ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT);
-               goto out_free;
+               return -1;
        }
 
-       if (ipmr_forward_offloaded(skb, mrt, in_vifi, vifi))
-               goto out_free;
-
        if (vif->flags & VIFF_TUNNEL) {
                rt = ip_route_output_ports(net, &fl4, NULL,
                                           vif->remote, vif->local,
@@ -1886,7 +1883,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
                                           IPPROTO_IPIP,
                                           iph->tos & INET_DSCP_MASK, vif->link);
                if (IS_ERR(rt))
-                       goto out_free;
+                       return -1;
                encap = sizeof(struct iphdr);
        } else {
                rt = ip_route_output_ports(net, &fl4, NULL, iph->daddr, 0,
@@ -1894,7 +1891,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
                                           IPPROTO_IPIP,
                                           iph->tos & INET_DSCP_MASK, vif->link);
                if (IS_ERR(rt))
-                       goto out_free;
+                       return -1;
        }
 
        if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) {
@@ -1904,14 +1901,14 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
                 */
                IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
                ip_rt_put(rt);
-               goto out_free;
+               return -1;
        }
 
        encap += LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len;
 
        if (skb_cow(skb, encap)) {
                ip_rt_put(rt);
-               goto out_free;
+               return -1;
        }
 
        WRITE_ONCE(vif->pkt_out, vif->pkt_out + 1);
@@ -1931,6 +1928,22 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
                DEV_STATS_ADD(vif_dev, tx_bytes, skb->len);
        }
 
+       return 0;
+}
+
+static void ipmr_queue_fwd_xmit(struct net *net, struct mr_table *mrt,
+                               int in_vifi, struct sk_buff *skb, int vifi)
+{
+       struct rtable *rt;
+
+       if (ipmr_forward_offloaded(skb, mrt, in_vifi, vifi))
+               goto out_free;
+
+       if (ipmr_prepare_xmit(net, mrt, skb, vifi))
+               goto out_free;
+
+       rt = skb_rtable(skb);
+
        IPCB(skb)->flags |= IPSKB_FORWARDED;
 
        /* RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
@@ -2062,8 +2075,8 @@ forward:
                                struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 
                                if (skb2)
-                                       ipmr_queue_xmit(net, mrt, true_vifi,
-                                                       skb2, psend);
+                                       ipmr_queue_fwd_xmit(net, mrt, true_vifi,
+                                                           skb2, psend);
                        }
                        psend = ct;
                }
@@ -2074,10 +2087,10 @@ last_forward:
                        struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 
                        if (skb2)
-                               ipmr_queue_xmit(net, mrt, true_vifi, skb2,
-                                               psend);
+                               ipmr_queue_fwd_xmit(net, mrt, true_vifi, skb2,
+                                                   psend);
                } else {
-                       ipmr_queue_xmit(net, mrt, true_vifi, skb, psend);
+                       ipmr_queue_fwd_xmit(net, mrt, true_vifi, skb, psend);
                        return;
                }
        }