diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 33ddf0d07855..a03af9750573 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -296,14 +296,18 @@ static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr, return err; path = &stack.path[stack.num_paths - 1]; - if (path->type != DEV_PATH_MTK_WDMA) - return -EINVAL; + if (path->type != DEV_PATH_MTK_WDMA) { + err = -EINVAL; + goto err_out; + } info->idx = path->mtk_wdma.wdma_idx; info->bss = path->mtk_wdma.bss; info->wcid = path->mtk_wdma.wcid; +err_out: + dev_fill_forward_path_release(&stack); - return 0; + return err; } static int airoha_get_dsa_port(struct net_device **dev) diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c index cc8c4ef8038f..771d9118f94a 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c @@ -108,16 +108,20 @@ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_i return err; path = &stack.path[stack.num_paths - 1]; - if (path->type != DEV_PATH_MTK_WDMA) - return -1; + if (path->type != DEV_PATH_MTK_WDMA) { + err = -EINVAL; + goto err_out; + } info->wdma_idx = path->mtk_wdma.wdma_idx; info->queue = path->mtk_wdma.queue; info->bss = path->mtk_wdma.bss; info->wcid = path->mtk_wdma.wcid; info->amsdu = path->mtk_wdma.amsdu; +err_out: + dev_fill_forward_path_release(&stack); - return 0; + return err; } diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8db25b79573e..62cfad7e6b79 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -892,6 +892,7 @@ struct net_device_path { u8 h_dest[ETH_ALEN]; } encap; struct { + struct dst_entry *dst; union { struct in_addr src_v4; struct in6_addr src_v6; @@ -3427,6 +3428,7 @@ int dev_get_iflink(const struct net_device *dev); int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb); int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr, struct net_device_path_stack *stack); +void dev_fill_forward_path_release(struct net_device_path_stack *stack); struct net_device *dev_get_by_name(struct net *net, const char *name); struct net_device *dev_get_by_name_rcu(struct net *net, const char *name); struct net_device *__dev_get_by_name(struct net *net, const char *name); diff --git a/net/core/dev.c b/net/core/dev.c index c1c1be1a6962..e50ed677de72 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -742,14 +742,33 @@ EXPORT_SYMBOL_GPL(dev_fill_metadata_dst); static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack) { - int k = stack->num_paths++; - - if (k >= NET_DEVICE_PATH_STACK_MAX) + if (stack->num_paths + 1 > NET_DEVICE_PATH_STACK_MAX) return NULL; - return &stack->path[k]; + return &stack->path[stack->num_paths]; } +void dev_fill_forward_path_release(struct net_device_path_stack *stack) +{ + struct net_device_path *path; + int k; + + if (stack->num_paths == 0) + return; + + for (k = stack->num_paths - 1; k >= 0; k--) { + path = &stack->path[k]; + switch (path->type) { + case DEV_PATH_TUN: + dst_release(path->tun.dst); + break; + default: + break; + } + } +} +EXPORT_SYMBOL_GPL(dev_fill_forward_path_release); + int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr, struct net_device_path_stack *stack) { @@ -766,15 +785,16 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr, last_dev = ctx.dev; path = dev_fwd_path(stack); if (!path) - return -1; + goto err_out; memset(path, 0, sizeof(struct net_device_path)); ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path); if (ret < 0) - return -1; + goto err_out; + stack->num_paths++; if (WARN_ON_ONCE(last_dev == ctx.dev)) - return -1; + goto err_out; } if (!ctx.dev) @@ -782,11 +802,17 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr, path = dev_fwd_path(stack); if (!path) - return -1; + goto err_out; + path->type = DEV_PATH_ETHERNET; path->dev = ctx.dev; + stack->num_paths++; - return ret; + return 0; +err_out: + dev_fill_forward_path_release(stack); + + return -1; } EXPORT_SYMBOL_GPL(dev_fill_forward_path); diff --git a/net/dsa/user.c b/net/dsa/user.c index 03c7af6abe18..4065c6ee6fc6 100644 --- a/net/dsa/user.c +++ b/net/dsa/user.c @@ -2547,14 +2547,13 @@ static int dsa_user_fill_forward_path(struct net_device_path_ctx *ctx, struct net_device_path *path) { struct dsa_port *dp = dsa_user_to_port(ctx->dev); - struct net_device *conduit = dsa_port_to_conduit(dp); struct dsa_port *cpu_dp = dp->cpu_dp; path->dev = ctx->dev; path->type = DEV_PATH_DSA; path->dsa.proto = cpu_dp->tag_ops->proto; path->dsa.port = dp->index; - ctx->dev = conduit; + ctx->dev = NULL; return 0; } diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 0831f6b81717..fb7d96f99b06 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -376,10 +376,10 @@ static int ipip_fill_forward_path(struct net_device_path_ctx *ctx, path->tun.src_v4.s_addr = tiph->saddr; path->tun.dst_v4.s_addr = tiph->daddr; path->tun.l3_proto = IPPROTO_IPIP; + path->tun.dst = &rt->dst; path->dev = ctx->dev; ctx->dev = rt->dst.dev; - ip_rt_put(rt); return 0; } diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index 4626dc46808f..59ec465a9df9 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -263,6 +264,7 @@ static int nf_reject_fill_skb_dst(struct sk_buff *skb_in) if (!dst) return -1; + skb_dst_drop(skb_in); skb_dst_set(skb_in, dst); return 0; } @@ -279,7 +281,7 @@ void nf_send_reset(struct net *net, struct sock *sk, struct sk_buff *oldskb, if (!oth) return; - if (!skb_dst(oldskb) && nf_reject_fill_skb_dst(oldskb) < 0) + if (!skb_valid_dst(oldskb) && nf_reject_fill_skb_dst(oldskb) < 0) return; if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) @@ -352,7 +354,7 @@ void nf_send_unreach(struct sk_buff *skb_in, int code, int hook) if (iph->frag_off & htons(IP_OFFSET)) return; - if (!skb_dst(skb_in) && nf_reject_fill_skb_dst(skb_in) < 0) + if (!skb_valid_dst(skb_in) && nf_reject_fill_skb_dst(skb_in) < 0) return; if (skb_csum_unnecessary(skb_in) || diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 97c3f61d627b..d80020bc2620 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1870,12 +1870,14 @@ static int ip6_tnl_fill_forward_path(struct net_device_path_ctx *ctx, path->tun.src_v6 = fl6.saddr; path->tun.dst_v6 = fl6.daddr; path->tun.l3_proto = IPPROTO_IPV6; + path->tun.dst = dst; path->dev = ctx->dev; ctx->dev = dst->dev; } err = dst->error; - dst_release(dst); + if (err) + dst_release(dst); return err; } diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c index ef5b7e85cffa..07cdaa10da0d 100644 --- a/net/ipv6/netfilter/nf_reject_ipv6.c +++ b/net/ipv6/netfilter/nf_reject_ipv6.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -304,6 +305,7 @@ static int nf_reject6_fill_skb_dst(struct sk_buff *skb_in) if (!dst) return -1; + skb_dst_drop(skb_in); skb_dst_set(skb_in, dst); return 0; } @@ -336,10 +338,12 @@ void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb, fl6.fl6_sport = otcph->dest; fl6.fl6_dport = otcph->source; - if (!skb_dst(oldskb)) { + if (!skb_valid_dst(oldskb)) { nf_ip6_route(net, &dst, flowi6_to_flowi(&fl6), false); if (!dst) return; + + skb_dst_drop(oldskb); skb_dst_set(oldskb, dst); } @@ -440,7 +444,7 @@ void nf_send_unreach6(struct net *net, struct sk_buff *skb_in, if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL) skb_in->dev = net->loopback_dev; - if (!skb_dst(skb_in) && nf_reject6_fill_skb_dst(skb_in) < 0) + if (!skb_valid_dst(skb_in) && nf_reject6_fill_skb_dst(skb_in) < 0) return; icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0); diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c index e9ea6d9466e7..85487f92af50 100644 --- a/net/netfilter/nf_conncount.c +++ b/net/netfilter/nf_conncount.c @@ -158,6 +158,8 @@ static bool get_ct_or_tuple_from_skb(struct net *net, return true; found_ct = nf_ct_tuplehash_to_ctrack(h); + *tuple = found_ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + *zone = nf_ct_zone(found_ct); *refcounted = true; *ct = found_ct; diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index ceeed3d7fe52..723e946a78f4 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -1281,8 +1281,9 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, if (ct->proto.tcp.retrans >= tn->tcp_max_retrans && timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS]) timeout = timeouts[TCP_CONNTRACK_RETRANS]; - else if (unlikely(index == TCP_RST_SET)) - timeout = timeouts[TCP_CONNTRACK_CLOSE]; + else if (unlikely(index == TCP_RST_SET && + new_state == TCP_CONNTRACK_ESTABLISHED)) + timeout = timeouts[TCP_CONNTRACK_UNACK]; else if ((ct->proto.tcp.seen[0].flags | ct->proto.tcp.seen[1].flags) & IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED && timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK]) diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c index 98c03b487f52..56219b02e122 100644 --- a/net/netfilter/nf_flow_table_path.c +++ b/net/netfilter/nf_flow_table_path.c @@ -42,8 +42,7 @@ static bool nft_is_valid_ether_device(const struct net_device *dev) return true; } -static int nft_dev_fill_forward_path(const struct nf_flow_route *route, - const struct dst_entry *dst_cache, +static int nft_dev_fill_forward_path(const struct dst_entry *dst_cache, const struct nf_conn *ct, enum ip_conntrack_dir dir, u8 *ha, struct net_device_path_stack *stack) @@ -76,14 +75,14 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route, } struct nft_forward_info { - const struct net_device *indev; - const struct net_device *outdev; + const struct net_device *dev; struct id { __u16 id; __be16 proto; } encap[NF_FLOW_TABLE_ENCAP_MAX]; u8 num_encaps; struct flow_offload_tunnel tun; + struct dst_entry *tun_dst; u8 num_tuns; u8 ingress_vlans; u8 h_source[ETH_ALEN]; @@ -92,9 +91,12 @@ struct nft_forward_info { enum flow_offload_xmit_type xmit_type; }; -static int nft_dev_path_info(const struct net_device_path_stack *stack, +static bool nft_flowtable_find_dev(const struct net_device *dev, + struct nft_flowtable *ft); + +static int nft_dev_path_info(struct net_device_path_stack *stack, struct nft_forward_info *info, - unsigned char *ha, struct nf_flowtable *flowtable) + unsigned char *ha, struct nft_flowtable *ft) { const struct net_device_path *path; int i; @@ -109,29 +111,27 @@ static int nft_dev_path_info(const struct net_device_path_stack *stack, case DEV_PATH_VLAN: case DEV_PATH_PPPOE: case DEV_PATH_TUN: - info->indev = path->dev; + info->dev = path->dev; if (is_zero_ether_addr(info->h_source)) memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); - if (path->type == DEV_PATH_ETHERNET) + if (path->type == DEV_PATH_ETHERNET || + path->type == DEV_PATH_DSA) break; - if (path->type == DEV_PATH_DSA) { - i = stack->num_paths; - break; - } /* DEV_PATH_VLAN, DEV_PATH_PPPOE and DEV_PATH_TUN */ if (path->type == DEV_PATH_TUN) { if (info->num_tuns) - return -1; + goto err_out; info->tun.src_v6 = path->tun.src_v6; info->tun.dst_v6 = path->tun.dst_v6; info->tun.l3_proto = path->tun.l3_proto; + info->tun_dst = path->tun.dst; info->num_tuns++; } else { if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) - return -1; + goto err_out; info->encap[info->num_encaps].id = path->encap.id; @@ -152,13 +152,13 @@ static int nft_dev_path_info(const struct net_device_path_stack *stack, switch (path->bridge.vlan_mode) { case DEV_PATH_BR_VLAN_UNTAG_HW: if (info->num_encaps == 0) - return -1; + goto err_out; info->ingress_vlans |= BIT(info->num_encaps - 1); break; case DEV_PATH_BR_VLAN_TAG: if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) - return -1; + goto err_out; info->encap[info->num_encaps].id = path->bridge.vlan_id; info->encap[info->num_encaps].proto = path->bridge.vlan_proto; @@ -166,7 +166,7 @@ static int nft_dev_path_info(const struct net_device_path_stack *stack, break; case DEV_PATH_BR_VLAN_UNTAG: if (info->num_encaps == 0) - return -1; + goto err_out; info->num_encaps--; break; @@ -176,16 +176,22 @@ static int nft_dev_path_info(const struct net_device_path_stack *stack, info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; break; default: - return -1; + goto err_out; } } - info->outdev = info->indev; - if (nf_flowtable_hw_offload(flowtable) && - nft_is_valid_ether_device(info->indev)) + if (nf_flowtable_hw_offload(&ft->data) && + nft_is_valid_ether_device(info->dev)) info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; + if (!nft_flowtable_find_dev(info->dev, ft)) + goto err_out; + return 0; +err_out: + dev_fill_forward_path_release(stack); + + return -1; } static bool nft_flowtable_find_dev(const struct net_device *dev, @@ -205,44 +211,6 @@ static bool nft_flowtable_find_dev(const struct net_device *dev, return found; } -static int nft_flow_tunnel_update_route(const struct nft_pktinfo *pkt, - struct flow_offload_tunnel *tun, - struct nf_flow_route *route, - enum ip_conntrack_dir dir) -{ - struct dst_entry *cur_dst = route->tuple[dir].dst; - struct dst_entry *tun_dst = NULL; - struct flowi fl = {}; - - switch (nft_pf(pkt)) { - case NFPROTO_IPV4: - fl.u.ip4.daddr = tun->dst_v4.s_addr; - fl.u.ip4.saddr = tun->src_v4.s_addr; - fl.u.ip4.flowi4_iif = nft_in(pkt)->ifindex; - fl.u.ip4.flowi4_dscp = ip4h_dscp(ip_hdr(pkt->skb)); - fl.u.ip4.flowi4_mark = pkt->skb->mark; - fl.u.ip4.flowi4_flags = FLOWI_FLAG_ANYSRC; - break; - case NFPROTO_IPV6: - fl.u.ip6.daddr = tun->dst_v6; - fl.u.ip6.saddr = tun->src_v6; - fl.u.ip6.flowi6_iif = nft_in(pkt)->ifindex; - fl.u.ip6.flowlabel = ip6_flowinfo(ipv6_hdr(pkt->skb)); - fl.u.ip6.flowi6_mark = pkt->skb->mark; - fl.u.ip6.flowi6_flags = FLOWI_FLAG_ANYSRC; - break; - } - - nf_route(nft_net(pkt), &tun_dst, &fl, false, nft_pf(pkt)); - if (!tun_dst) - return -ENOENT; - - route->tuple[dir].dst = tun_dst; - dst_release(cur_dst); - - return 0; -} - static int nft_dev_forward_path(const struct nft_pktinfo *pkt, struct nf_flow_route *route, const struct nf_conn *ct, @@ -255,28 +223,25 @@ static int nft_dev_forward_path(const struct nft_pktinfo *pkt, unsigned char ha[ETH_ALEN]; int i; - if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) < 0 || - nft_dev_path_info(&stack, &info, ha, &ft->data) < 0) + if (nft_dev_fill_forward_path(dst, ct, dir, ha, &stack) < 0 || + nft_dev_path_info(&stack, &info, ha, ft) < 0) return -ENOENT; - if (!nft_flowtable_find_dev(info.indev, ft)) - return -ENOENT; + route->tuple[!dir].in.ifindex = info.dev->ifindex; + route->tuple[dir].out.ifindex = info.dev->ifindex; - if (info.outdev) - route->tuple[dir].out.ifindex = info.outdev->ifindex; - - route->tuple[!dir].in.ifindex = info.indev->ifindex; for (i = 0; i < info.num_encaps; i++) { route->tuple[!dir].in.encap[i].id = info.encap[i].id; route->tuple[!dir].in.encap[i].proto = info.encap[i].proto; } - if (info.num_tuns && - !nft_flow_tunnel_update_route(pkt, &info.tun, route, dir)) { + if (info.num_tuns) { route->tuple[!dir].in.tun.src_v6 = info.tun.dst_v6; route->tuple[!dir].in.tun.dst_v6 = info.tun.src_v6; route->tuple[!dir].in.tun.l3_proto = info.tun.l3_proto; route->tuple[!dir].in.num_tuns = info.num_tuns; + dst_release(route->tuple[dir].dst); + route->tuple[dir].dst = info.tun_dst; } route->tuple[!dir].in.num_encaps = info.num_encaps; diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 0a43e0787a68..01cfbaa36525 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -20,6 +20,7 @@ #include #include #include +#include #include /* for TCP_TIME_WAIT */ #include #include @@ -279,11 +280,12 @@ static bool nft_meta_get_eval_ifname(enum nft_meta_keys key, u32 *dest, static noinline bool nft_meta_get_eval_rtclassid(const struct sk_buff *skb, u32 *dest) { - const struct dst_entry *dst = skb_dst(skb); + const struct dst_entry *dst; - if (!dst) + if (!skb_valid_dst(skb)) return false; + dst = skb_dst(skb); *dest = dst->tclassid; return true; } diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c index aeb0094eafd8..841c863a08db 100644 --- a/net/netfilter/nft_rt.c +++ b/net/netfilter/nft_rt.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -59,10 +60,11 @@ void nft_rt_get_eval(const struct nft_expr *expr, u32 *dest = ®s->data[priv->dreg]; const struct dst_entry *dst; - dst = skb_dst(skb); - if (!dst) + if (!skb_valid_dst(skb)) goto err; + dst = skb_dst(skb); + switch (priv->key) { #ifdef CONFIG_IP_ROUTE_CLASSID case NFT_RT_CLASSID: diff --git a/net/netfilter/nft_xfrm.c b/net/netfilter/nft_xfrm.c index 8cec43064319..c8bba697f993 100644 --- a/net/netfilter/nft_xfrm.c +++ b/net/netfilter/nft_xfrm.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -177,9 +178,15 @@ static void nft_xfrm_get_eval_out(const struct nft_xfrm *priv, struct nft_regs *regs, const struct nft_pktinfo *pkt) { - const struct dst_entry *dst = skb_dst(pkt->skb); + const struct dst_entry *dst; int i; + if (!skb_valid_dst(pkt->skb)) { + regs->verdict.code = NFT_BREAK; + return; + } + + dst = skb_dst(pkt->skb); for (i = 0; dst && dst->xfrm; dst = ((const struct xfrm_dst *)dst)->child, i++) { if (i < priv->spnum)