Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 73a8bd74 authored by David S. Miller's avatar David S. Miller
Browse files

ipv6: Revert 'administrative down' address handling changes.



This reverts the following set of commits:

d1ed113f ("ipv6: remove duplicate neigh_ifdown")
29ba5fed ("ipv6: don't flush routes when setting loopback down")
9d82ca98 ("ipv6: fix missing in6_ifa_put in addrconf")
2de79570 ("ipv6: addrconf: don't remove address state on ifdown if the address is being kept")
8595805a ("IPv6: only notify protocols if address is compeletely gone")
27bdb2ab ("IPv6: keep tentative addresses in hash table")
93fa159a ("IPv6: keep route for tentative address")
8f37ada5 ("IPv6: fix race between cleanup and add/delete address")
84e8b803 ("IPv6: addrconf notify when address is unavailable")
dc2b99f7 ("IPv6: keep permanent addresses on admin down")

because the core semantic change to ipv6 address handling on ifdown
has broken some things, in particular "disable_ipv6" sysctl handling.

Stephen has made several attempts to get things back in working order,
but nothing has restored disable_ipv6 fully yet.

Reported-by: default avatarEric W. Biederman <ebiederm@xmission.com>
Tested-by: default avatarEric W. Biederman <ebiederm@xmission.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent de0368d5
Loading
Loading
Loading
Loading
+33 −48
Original line number Diff line number Diff line
@@ -2661,14 +2661,12 @@ static int addrconf_ifdown(struct net_device *dev, int how)
	struct net *net = dev_net(dev);
	struct inet6_dev *idev;
	struct inet6_ifaddr *ifa;
	LIST_HEAD(keep_list);
	int state;
	int state, i;

	ASSERT_RTNL();

	/* Flush routes if device is being removed or it is not loopback */
	if (how || !(dev->flags & IFF_LOOPBACK))
	rt6_ifdown(net, dev);
	neigh_ifdown(&nd_tbl, dev);

	idev = __in6_dev_get(dev);
	if (idev == NULL)
@@ -2689,6 +2687,23 @@ static int addrconf_ifdown(struct net_device *dev, int how)

	}

	/* Step 2: clear hash table */
	for (i = 0; i < IN6_ADDR_HSIZE; i++) {
		struct hlist_head *h = &inet6_addr_lst[i];
		struct hlist_node *n;

		spin_lock_bh(&addrconf_hash_lock);
	restart:
		hlist_for_each_entry_rcu(ifa, n, h, addr_lst) {
			if (ifa->idev == idev) {
				hlist_del_init_rcu(&ifa->addr_lst);
				addrconf_del_timer(ifa);
				goto restart;
			}
		}
		spin_unlock_bh(&addrconf_hash_lock);
	}

	write_lock_bh(&idev->lock);

	/* Step 2: clear flags for stateless addrconf */
@@ -2722,35 +2737,10 @@ static int addrconf_ifdown(struct net_device *dev, int how)
				       struct inet6_ifaddr, if_list);
		addrconf_del_timer(ifa);

		/* If just doing link down, and address is permanent
		   and not link-local, then retain it. */
		if (!how &&
		    (ifa->flags&IFA_F_PERMANENT) &&
		    !(ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
			list_move_tail(&ifa->if_list, &keep_list);

			/* If not doing DAD on this address, just keep it. */
			if ((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) ||
			    idev->cnf.accept_dad <= 0 ||
			    (ifa->flags & IFA_F_NODAD))
				continue;

			/* If it was tentative already, no need to notify */
			if (ifa->flags & IFA_F_TENTATIVE)
				continue;

			/* Flag it for later restoration when link comes up */
			ifa->flags |= IFA_F_TENTATIVE;
			ifa->state = INET6_IFADDR_STATE_DAD;
		} else {
		list_del(&ifa->if_list);

			/* clear hash table */
			spin_lock_bh(&addrconf_hash_lock);
			hlist_del_init_rcu(&ifa->addr_lst);
			spin_unlock_bh(&addrconf_hash_lock);

		write_unlock_bh(&idev->lock);

		spin_lock_bh(&ifa->state_lock);
		state = ifa->state;
		ifa->state = INET6_IFADDR_STATE_DEAD;
@@ -2758,16 +2748,12 @@ static int addrconf_ifdown(struct net_device *dev, int how)

		if (state != INET6_IFADDR_STATE_DEAD) {
			__ipv6_ifa_notify(RTM_DELADDR, ifa);
				atomic_notifier_call_chain(&inet6addr_chain,
							   NETDEV_DOWN, ifa);
			atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
		}

		in6_ifa_put(ifa);

		write_lock_bh(&idev->lock);
	}
	}

	list_splice(&keep_list, &idev->addr_list);

	write_unlock_bh(&idev->lock);

@@ -4156,8 +4142,7 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
		addrconf_leave_solict(ifp->idev, &ifp->addr);
		dst_hold(&ifp->rt->dst);

		if (ifp->state == INET6_IFADDR_STATE_DEAD &&
		    ip6_del_rt(ifp->rt))
		if (ip6_del_rt(ifp->rt))
			dst_free(&ifp->rt->dst);
		break;
	}