From af2681828af5f2b42e12e8b16ba0cf113cf486c8 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 3 Apr 2008 12:52:19 -0700 Subject: [ICMP]: Ensure that ICMP relookup maintains status quo The ICMP relookup path is only meant to modify behaviour when appropriate IPsec policies are in place and marked as requiring relookups. It is certainly not meant to modify behaviour when IPsec policies don't exist at all. However, due to an oversight on the error paths existing behaviour may in fact change should one of the relookup steps fail. This patch corrects this by redirecting all errors on relookup failures to the previous code path. That is, if the initial xfrm_lookup let the packet pass, we will stand by that decision should the relookup fail due to an error. This should be safe from a security point-of-view because compliant systems must install a default deny policy so the packet would'nt have passed in that case. Many thanks to Julian Anastasov for pointing out this error. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- net/ipv4/icmp.c | 24 +++++++++++++----------- net/ipv6/icmp.c | 22 ++++++++++++---------- 2 files changed, 25 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index a944e8053e28..40508babad8c 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -591,7 +591,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) } if (xfrm_decode_session_reverse(skb_in, &fl, AF_INET)) - goto ende; + goto relookup_failed; if (inet_addr_type(net, fl.fl4_src) == RTN_LOCAL) err = __ip_route_output_key(net, &rt2, &fl); @@ -601,7 +601,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) fl2.fl4_dst = fl.fl4_src; if (ip_route_output_key(net, &rt2, &fl2)) - goto ende; + goto relookup_failed; /* Ugh! */ odst = skb_in->dst; @@ -614,21 +614,23 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) } if (err) - goto ende; + goto relookup_failed; err = xfrm_lookup((struct dst_entry **)&rt2, &fl, NULL, XFRM_LOOKUP_ICMP); - if (err == -ENOENT) { + switch (err) { + case 0: + dst_release(&rt->u.dst); + rt = rt2; + break; + case -EPERM: + goto ende; + default: +relookup_failed: if (!rt) goto out_unlock; - goto route_done; + break; } - - dst_release(&rt->u.dst); - rt = rt2; - - if (err) - goto out_unlock; } route_done: diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index f204a7275a0d..893287ecc628 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -436,24 +436,26 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, } if (xfrm_decode_session_reverse(skb, &fl2, AF_INET6)) - goto out_dst_release; + goto relookup_failed; if (ip6_dst_lookup(sk, &dst2, &fl)) - goto out_dst_release; + goto relookup_failed; err = xfrm_lookup(&dst2, &fl, sk, XFRM_LOOKUP_ICMP); - if (err == -ENOENT) { + switch (err) { + case 0: + dst_release(dst); + dst = dst2; + break; + case -EPERM: + goto out_dst_release; + default: +relookup_failed: if (!dst) goto out; - goto route_done; + break; } - dst_release(dst); - dst = dst2; - - if (err) - goto out; - route_done: if (ipv6_addr_is_multicast(&fl.fl6_dst)) hlimit = np->mcast_hops; -- cgit v1.2.1 From 439e23857a21c3a953826eed23c818697a97de1a Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 3 Apr 2008 13:30:17 -0700 Subject: [IPV6]: Event type in addrconf_ifdown is mis-used. addrconf_ifdown is broken in respect to the usage of how parameter. This function is called with (event != NETDEV_DOWN) and (2) on the IPv6 stop. It the latter case inet6_dev from loopback device should be destroyed. Signed-off-by: Denis V. Lunev Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index e7a1882db048..4fa9da0be19a 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2469,7 +2469,7 @@ static int addrconf_ifdown(struct net_device *dev, int how) /* Step 1: remove reference to ipv6 device from parent device. Do not dev_put! */ - if (how == 1) { + if (how) { idev->dead = 1; /* protected by rtnl_lock */ @@ -2501,12 +2501,12 @@ static int addrconf_ifdown(struct net_device *dev, int how) write_lock_bh(&idev->lock); /* Step 3: clear flags for stateless addrconf */ - if (how != 1) + if (!how) idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY); /* Step 4: clear address list */ #ifdef CONFIG_IPV6_PRIVACY - if (how == 1 && del_timer(&idev->regen_timer)) + if (how && del_timer(&idev->regen_timer)) in6_dev_put(idev); /* clear tempaddr list */ @@ -2543,7 +2543,7 @@ static int addrconf_ifdown(struct net_device *dev, int how) /* Step 5: Discard multicast list */ - if (how == 1) + if (how) ipv6_mc_destroy_dev(idev); else ipv6_mc_down(idev); @@ -2552,7 +2552,7 @@ static int addrconf_ifdown(struct net_device *dev, int how) /* Shot the device (if unregistered) */ - if (how == 1) { + if (how) { addrconf_sysctl_unregister(idev); neigh_parms_release(&nd_tbl, idev->nd_parms); neigh_ifdown(&nd_tbl, dev); -- cgit v1.2.1 From eb867579311a9c1e998d6911af056772c400122a Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 3 Apr 2008 13:31:53 -0700 Subject: [IPV6]: inet6_dev on loopback should be kept until namespace stop. In the other case it will be destroyed when last address will be removed from lo inside a namespace. This will break IPv6 in several places. The most obvious one is ip6_dst_ifdown. Signed-off-by: Denis V. Lunev Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 4fa9da0be19a..a65935a9afd9 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2456,7 +2456,7 @@ static int addrconf_ifdown(struct net_device *dev, int how) ASSERT_RTNL(); - if (dev == init_net.loopback_dev && how == 1) + if ((dev->flags & IFF_LOOPBACK) && how == 1) how = 0; rt6_ifdown(dev); -- cgit v1.2.1 From 84f59370c519449c70dcc813b050f5cbbf0098e7 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 3 Apr 2008 13:33:00 -0700 Subject: [IPV6]: Fix refcounting for anycast dst entries. Anycast DST entries allocated inside ipv6_dev_ac_inc are leaked when network device is stopped without removing IPv6 addresses from it. The bug has been observed in the reality on 2.6.18-rhel5 kernel. In the above case addrconf_ifdown marks all entries as obsolete and ip6_del_rt called from __ipv6_dev_ac_dec returns ENOENT. The referrence is not dropped. The fix is simple. DST entry should not keep referrence when stored in the FIB6 tree. Signed-off-by: Denis V. Lunev Signed-off-by: David S. Miller --- net/ipv6/anycast.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 9c7f83fbc3a1..e5f56c953b58 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -334,9 +334,7 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr) idev->ac_list = aca; write_unlock_bh(&idev->lock); - dst_hold(&rt->u.dst); - if (ip6_ins_rt(rt)) - dst_release(&rt->u.dst); + ip6_ins_rt(rt); addrconf_join_solict(dev, &aca->aca_addr); @@ -378,10 +376,7 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, struct in6_addr *addr) addrconf_leave_solict(idev, &aca->aca_addr); dst_hold(&aca->aca_rt->u.dst); - if (ip6_del_rt(aca->aca_rt)) - dst_free(&aca->aca_rt->u.dst); - else - dst_release(&aca->aca_rt->u.dst); + ip6_del_rt(aca->aca_rt); aca_put(aca); return 0; -- cgit v1.2.1 From 23556323b22fef35bdc36465b7e7439ba3748c9f Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 4 Apr 2008 12:45:12 -0700 Subject: [VLAN]: Fix egress priority mappings leak. These entries are allocated in vlan_dev_set_egress_priority, but are never released and leaks on vlan device removal. Drop these in vlan's ->uninit callback - after the device is brought down and everyone is notified about it is going to be unregistered. Found during testing vlan netnsization patchset. Signed-off-by: Pavel Emelyanov Acked-by: Patrick McHardy Signed-off-by: David S. Miller --- net/8021q/vlan_dev.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'net') diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 480ea90e7dcd..41a76a05e6fd 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -692,6 +692,20 @@ static int vlan_dev_init(struct net_device *dev) return 0; } +static void vlan_dev_uninit(struct net_device *dev) +{ + struct vlan_priority_tci_mapping *pm; + struct vlan_dev_info *vlan = vlan_dev_info(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) { + while ((pm = vlan->egress_priority_map[i]) != NULL) { + vlan->egress_priority_map[i] = pm->next; + kfree(pm); + } + } +} + void vlan_setup(struct net_device *dev) { ether_setup(dev); @@ -701,6 +715,7 @@ void vlan_setup(struct net_device *dev) dev->change_mtu = vlan_dev_change_mtu; dev->init = vlan_dev_init; + dev->uninit = vlan_dev_uninit; dev->open = vlan_dev_open; dev->stop = vlan_dev_stop; dev->set_mac_address = vlan_dev_set_mac_address; -- cgit v1.2.1