--- zzzz-none-000/linux-4.4.60/net/ipv6/route.c 2017-04-08 07:53:53.000000000 +0000 +++ hawkeye-5590-729/linux-4.4.60/net/ipv6/route.c 2022-03-30 14:21:53.000000000 +0000 @@ -90,6 +90,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); @@ -175,6 +177,9 @@ return dst_metrics_write_ptr(rt->dst.from); } +/* Define route change notification chain. */ +ATOMIC_NOTIFIER_HEAD(ip6route_chain); + static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) { struct rt6_info *rt = (struct rt6_info *)dst; @@ -297,6 +302,21 @@ .rt6i_ref = ATOMIC_INIT(1), }; +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), + .rt6i_protocol = RTPROT_KERNEL, + .rt6i_metric = ~(u32) 0, + .rt6i_ref = ATOMIC_INIT(1), +}; + static const struct rt6_info ip6_blk_hole_entry_template = { .dst = { .__refcnt = ATOMIC_INIT(1), @@ -337,9 +357,9 @@ return rt; } -static struct rt6_info *ip6_dst_alloc(struct net *net, - struct net_device *dev, - int flags) +struct rt6_info *ip6_dst_alloc(struct net *net, + struct net_device *dev, + int flags) { struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags); @@ -363,6 +383,7 @@ return rt; } +EXPORT_SYMBOL(ip6_dst_alloc); static void ip6_dst_destroy(struct dst_entry *dst) { @@ -392,14 +413,12 @@ struct net_device *loopback_dev = dev_net(dev)->loopback_dev; - if (dev != loopback_dev) { - if (idev && idev->dev == dev) { - struct inet6_dev *loopback_idev = - in6_dev_get(loopback_dev); - if (loopback_idev) { - rt->rt6i_idev = loopback_idev; - in6_dev_put(idev); - } + if (idev && idev->dev != loopback_dev) { + struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev); + + if (loopback_idev) { + rt->rt6i_idev = loopback_idev; + in6_dev_put(idev); } } } @@ -1386,7 +1405,7 @@ } void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, - int oif, u32 mark) + int oif, u32 mark, kuid_t uid) { const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; struct dst_entry *dst; @@ -1398,6 +1417,7 @@ fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; fl6.flowlabel = ip6_flowinfo(iph); + fl6.flowi6_uid = uid; dst = ip6_route_output(net, NULL, &fl6); if (!dst->error) @@ -1409,7 +1429,7 @@ void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) { ip6_update_pmtu(skb, sock_net(sk), mtu, - sk->sk_bound_dev_if, sk->sk_mark); + sk->sk_bound_dev_if, sk->sk_mark, sk->sk_uid); } EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); @@ -1490,7 +1510,8 @@ flags, __ip6_route_redirect); } -void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) +void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark, + kuid_t uid) { const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; struct dst_entry *dst; @@ -1503,6 +1524,7 @@ fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; fl6.flowlabel = ip6_flowinfo(iph); + fl6.flowi6_uid = uid; dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); rt6_do_redirect(dst, NULL, skb); @@ -1524,6 +1546,7 @@ fl6.flowi6_mark = mark; fl6.daddr = msg->dest; fl6.saddr = iph->daddr; + fl6.flowi6_uid = sock_net_uid(net, NULL); dst = ip6_route_redirect(net, &fl6, &iph->saddr); rt6_do_redirect(dst, NULL, skb); @@ -1532,7 +1555,8 @@ void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) { - ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); + ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark, + sk->sk_uid); } EXPORT_SYMBOL_GPL(ip6_sk_redirect); @@ -1885,6 +1909,11 @@ rt->dst.output = ip6_pkt_prohibit_out; rt->dst.input = ip6_pkt_prohibit; break; + case RTN_POLICY_FAILED: + rt->dst.error = -EACCES; + rt->dst.output = ip6_pkt_policy_failed_out; + rt->dst.input = ip6_pkt_policy_failed; + break; case RTN_THROW: case RTN_UNREACHABLE: default: @@ -2039,7 +2068,6 @@ write_lock_bh(&table->tb6_lock); err = fib6_del(rt, info); write_unlock_bh(&table->tb6_lock); - out: ip6_rt_put(rt); return err; @@ -2431,6 +2459,10 @@ } rtnl_unlock(); + if (!err) + atomic_notifier_call_chain(&ip6route_chain, + SIOCADDRT ? RTM_NEWROUTE:RTM_DELROUTE, &cfg); + return err; } @@ -2486,6 +2518,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. */ @@ -2700,6 +2743,7 @@ [RTA_PREF] = { .type = NLA_U8 }, [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, [RTA_ENCAP] = { .type = NLA_NESTED }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -2728,7 +2772,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) @@ -3006,9 +3051,15 @@ return err; if (cfg.fc_mp) - return ip6_route_multipath_del(&cfg); + err = ip6_route_multipath_del(&cfg); else - return ip6_route_del(&cfg); + err = ip6_route_del(&cfg); + + 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) @@ -3021,9 +3072,15 @@ return err; if (cfg.fc_mp) - return ip6_route_multipath_add(&cfg); + err = ip6_route_multipath_add(&cfg); else - return ip6_route_add(&cfg); + err = ip6_route_add(&cfg); + + if (!err) + atomic_notifier_call_chain(&ip6route_chain, + RTM_NEWROUTE, &cfg); + + return err; } static inline size_t rt6_nlmsg_size(struct rt6_info *rt) @@ -3087,6 +3144,9 @@ case -EACCES: rtm->rtm_type = RTN_PROHIBIT; break; + case -EPERM: + rtm->rtm_type = RTN_POLICY_FAILED; + break; case -EAGAIN: rtm->rtm_type = RTN_THROW; break; @@ -3264,6 +3324,12 @@ if (tb[RTA_MARK]) fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); + if (tb[RTA_UID]) + fl6.flowi6_uid = make_kuid(current_user_ns(), + nla_get_u32(tb[RTA_UID])); + else + fl6.flowi6_uid = iif ? INVALID_UID : current_uid(); + if (iif) { struct net_device *dev; int flags = 0; @@ -3363,6 +3429,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 @@ -3371,6 +3439,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 */ @@ -3579,6 +3659,17 @@ 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); + + 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_blk_hole_entry; + net->ipv6.ip6_policy_failed_entry->dst.path = + (struct dst_entry *)net->ipv6.ip6_policy_failed_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); #endif net->ipv6.sysctl.flush_delay = 0; @@ -3597,6 +3688,8 @@ return ret; #ifdef CONFIG_IPV6_MULTIPLE_TABLES +out_ip6_blk_hole_entry: + kfree(net->ipv6.ip6_blk_hole_entry); out_ip6_prohibit_entry: kfree(net->ipv6.ip6_prohibit_entry); out_ip6_null_entry: @@ -3614,6 +3707,7 @@ #ifdef CONFIG_IPV6_MULTIPLE_TABLES kfree(net->ipv6.ip6_prohibit_entry); kfree(net->ipv6.ip6_blk_hole_entry); + kfree(net->ipv6.ip6_policy_failed_entry); #endif dst_entries_destroy(&net->ipv6.ip6_dst_ops); } @@ -3711,6 +3805,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 ret = fib6_init(); if (ret)