--- zzzz-none-000/linux-5.4.213/net/ipv6/route.c 2022-09-15 10:04:56.000000000 +0000 +++ miami-7690-761/linux-5.4.213/net/ipv6/route.c 2024-05-29 11:20:02.000000000 +0000 @@ -94,6 +94,8 @@ static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); static int ip6_pkt_prohibit(struct sk_buff *skb); static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb); +static int ip6_pkt_policy_failed(struct sk_buff *skb); +static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb); static void ip6_link_failure(struct sk_buff *skb); static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb, u32 mtu, @@ -187,6 +189,9 @@ } } +/* Define route change notification chain. */ +ATOMIC_NOTIFIER_HEAD(ip6route_chain); + static inline const void *choose_neigh_daddr(const struct in6_addr *p, struct sk_buff *skb, const void *daddr) @@ -327,6 +332,18 @@ .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), }; +static const struct rt6_info ip6_policy_failed_entry_template = { + .dst = { + .__refcnt = ATOMIC_INIT(1), + .__use = 1, + .obsolete = DST_OBSOLETE_FORCE_CHK, + .error = -EACCES, + .input = ip6_pkt_policy_failed, + .output = ip6_pkt_policy_failed_out, + }, + .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), +}; + static const struct rt6_info ip6_blk_hole_entry_template = { .dst = { .__refcnt = ATOMIC_INIT(1), @@ -1048,6 +1065,7 @@ [RTN_BLACKHOLE] = -EINVAL, [RTN_UNREACHABLE] = -EHOSTUNREACH, [RTN_PROHIBIT] = -EACCES, + [RTN_POLICY_FAILED] = -EACCES, [RTN_THROW] = -EAGAIN, [RTN_NAT] = -EINVAL, [RTN_XRESOLVE] = -EINVAL, @@ -1085,6 +1103,10 @@ rt->dst.output = ip6_pkt_prohibit_out; rt->dst.input = ip6_pkt_prohibit; break; + case RTN_POLICY_FAILED: + rt->dst.output = ip6_pkt_policy_failed_out; + rt->dst.input = ip6_pkt_policy_failed; + break; case RTN_THROW: case RTN_UNREACHABLE: default: @@ -4386,6 +4408,10 @@ } rtnl_unlock(); + if (!err) + atomic_notifier_call_chain(&ip6route_chain, + SIOCADDRT ? RTM_NEWROUTE:RTM_DELROUTE, &cfg); + return err; } @@ -4453,6 +4479,17 @@ return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); } +static int ip6_pkt_policy_failed(struct sk_buff *skb) +{ + return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_INNOROUTES); +} + +static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb) +{ + skb->dev = skb_dst(skb)->dev; + return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_OUTNOROUTES); +} + /* * Allocate a dst for local (unicast / anycast) address. */ @@ -4940,7 +4977,8 @@ if (rtm->rtm_type == RTN_UNREACHABLE || rtm->rtm_type == RTN_BLACKHOLE || rtm->rtm_type == RTN_PROHIBIT || - rtm->rtm_type == RTN_THROW) + rtm->rtm_type == RTN_THROW || + rtm->rtm_type == RTN_POLICY_FAILED) cfg->fc_flags |= RTF_REJECT; if (rtm->rtm_type == RTN_LOCAL) @@ -5354,11 +5392,17 @@ } if (cfg.fc_mp) - return ip6_route_multipath_del(&cfg, extack); + err = ip6_route_multipath_del(&cfg, extack); else { cfg.fc_delete_all_nh = 1; - return ip6_route_del(&cfg, extack); + err = ip6_route_del(&cfg, extack); } + + if (!err) + atomic_notifier_call_chain(&ip6route_chain, + RTM_DELROUTE, &cfg); + + return err; } static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -5375,9 +5419,15 @@ cfg.fc_metric = IP6_RT_PRIO_USER; if (cfg.fc_mp) - return ip6_route_multipath_add(&cfg, extack); + err = ip6_route_multipath_add(&cfg, extack); else - return ip6_route_add(&cfg, GFP_KERNEL, extack); + err = ip6_route_add(&cfg, GFP_KERNEL, extack); + + if (!err) + atomic_notifier_call_chain(&ip6route_chain, + RTM_NEWROUTE, &cfg); + + return err; } /* add the overhead of this fib6_nh to nexthop_len */ @@ -6091,6 +6141,8 @@ #ifdef CONFIG_IPV6_MULTIPLE_TABLES net->ipv6.ip6_prohibit_entry->dst.dev = dev; net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); + net->ipv6.ip6_policy_failed_entry->dst.dev = dev; + net->ipv6.ip6_policy_failed_entry->rt6i_idev = in6_dev_get(dev); net->ipv6.ip6_blk_hole_entry->dst.dev = dev; net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); #endif @@ -6102,6 +6154,7 @@ in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev); #ifdef CONFIG_IPV6_MULTIPLE_TABLES in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev); + in6_dev_put_clear(&net->ipv6.ip6_policy_failed_entry->rt6i_idev); in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev); #endif } @@ -6109,6 +6162,18 @@ return NOTIFY_OK; } +int rt6_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ip6route_chain, nb); +} +EXPORT_SYMBOL(rt6_register_notifier); + +int rt6_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ip6route_chain, nb); +} +EXPORT_SYMBOL(rt6_unregister_notifier); + /* * /proc */ @@ -6294,6 +6359,8 @@ #ifdef CONFIG_IPV6_MULTIPLE_TABLES net->ipv6.fib6_has_custom_rules = false; + + net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, sizeof(*net->ipv6.ip6_prohibit_entry), GFP_KERNEL); @@ -6304,11 +6371,21 @@ ip6_template_metrics, true); INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached); + net->ipv6.ip6_policy_failed_entry = + kmemdup(&ip6_policy_failed_entry_template, + sizeof(*net->ipv6.ip6_policy_failed_entry), GFP_KERNEL); + if (!net->ipv6.ip6_policy_failed_entry) + goto out_ip6_prohibit_entry; + net->ipv6.ip6_policy_failed_entry->dst.ops = &net->ipv6.ip6_dst_ops; + dst_init_metrics(&net->ipv6.ip6_policy_failed_entry->dst, + ip6_template_metrics, true); + INIT_LIST_HEAD(&net->ipv6.ip6_policy_failed_entry->rt6i_uncached); + net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, sizeof(*net->ipv6.ip6_blk_hole_entry), GFP_KERNEL); if (!net->ipv6.ip6_blk_hole_entry) - goto out_ip6_prohibit_entry; + goto out_ip6_policy_failed_entry; net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, ip6_template_metrics, true); @@ -6332,6 +6409,8 @@ return ret; #ifdef CONFIG_IPV6_MULTIPLE_TABLES +out_ip6_policy_failed_entry: + kfree(net->ipv6.ip6_policy_failed_entry); out_ip6_prohibit_entry: kfree(net->ipv6.ip6_prohibit_entry); out_ip6_null_entry: @@ -6351,6 +6430,7 @@ kfree(net->ipv6.ip6_null_entry); #ifdef CONFIG_IPV6_MULTIPLE_TABLES kfree(net->ipv6.ip6_prohibit_entry); + kfree(net->ipv6.ip6_policy_failed_entry); kfree(net->ipv6.ip6_blk_hole_entry); #endif dst_entries_destroy(&net->ipv6.ip6_dst_ops); @@ -6428,6 +6508,9 @@ init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); + init_net.ipv6.ip6_policy_failed_entry->dst.dev = init_net.loopback_dev; + init_net.ipv6.ip6_policy_failed_entry->rt6i_idev = + in6_dev_get(init_net.loopback_dev); #endif }