forked from mirrors/linux
		
	[IPV4]: Fix secondary IP addresses after promotion
This patch fixes the problem with promoting aliases when: a) a single primary and > 1 secondary addresses b) multiple primary addresses each with at least one secondary address Based on earlier efforts from Brian Pomerantz <bapper@piratehaven.org>, Patrick McHardy <kaber@trash.net> and Thomas Graf <tgraf@suug.ch> Signed-off-by: Jamal Hadi Salim <hadi@cyberus.ca> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									c27bd492fd
								
							
						
					
					
						commit
						0ff60a4567
					
				
					 3 changed files with 34 additions and 11 deletions
				
			
		|  | @ -126,6 +126,9 @@ extern int		ip_rt_ioctl(unsigned int cmd, void __user *arg); | ||||||
| extern void		ip_rt_get_source(u8 *src, struct rtable *rt); | extern void		ip_rt_get_source(u8 *src, struct rtable *rt); | ||||||
| extern int		ip_rt_dump(struct sk_buff *skb,  struct netlink_callback *cb); | extern int		ip_rt_dump(struct sk_buff *skb,  struct netlink_callback *cb); | ||||||
| 
 | 
 | ||||||
|  | struct in_ifaddr; | ||||||
|  | extern void fib_add_ifaddr(struct in_ifaddr *); | ||||||
|  | 
 | ||||||
| static inline void ip_rt_put(struct rtable * rt) | static inline void ip_rt_put(struct rtable * rt) | ||||||
| { | { | ||||||
| 	if (rt) | 	if (rt) | ||||||
|  |  | ||||||
|  | @ -234,7 +234,10 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | ||||||
| 			 int destroy) | 			 int destroy) | ||||||
| { | { | ||||||
| 	struct in_ifaddr *promote = NULL; | 	struct in_ifaddr *promote = NULL; | ||||||
| 	struct in_ifaddr *ifa1 = *ifap; | 	struct in_ifaddr *ifa, *ifa1 = *ifap; | ||||||
|  | 	struct in_ifaddr *last_prim = in_dev->ifa_list; | ||||||
|  | 	struct in_ifaddr *prev_prom = NULL; | ||||||
|  | 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); | ||||||
| 
 | 
 | ||||||
| 	ASSERT_RTNL(); | 	ASSERT_RTNL(); | ||||||
| 
 | 
 | ||||||
|  | @ -243,18 +246,22 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | ||||||
| 	 **/ | 	 **/ | ||||||
| 
 | 
 | ||||||
| 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { | 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { | ||||||
| 		struct in_ifaddr *ifa; |  | ||||||
| 		struct in_ifaddr **ifap1 = &ifa1->ifa_next; | 		struct in_ifaddr **ifap1 = &ifa1->ifa_next; | ||||||
| 
 | 
 | ||||||
| 		while ((ifa = *ifap1) != NULL) { | 		while ((ifa = *ifap1) != NULL) { | ||||||
|  | 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&  | ||||||
|  | 			    ifa1->ifa_scope <= ifa->ifa_scope) | ||||||
|  | 				last_prim = ifa; | ||||||
|  | 
 | ||||||
| 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) || | 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) || | ||||||
| 			    ifa1->ifa_mask != ifa->ifa_mask || | 			    ifa1->ifa_mask != ifa->ifa_mask || | ||||||
| 			    !inet_ifa_match(ifa1->ifa_address, ifa)) { | 			    !inet_ifa_match(ifa1->ifa_address, ifa)) { | ||||||
| 				ifap1 = &ifa->ifa_next; | 				ifap1 = &ifa->ifa_next; | ||||||
|  | 				prev_prom = ifa; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (!IN_DEV_PROMOTE_SECONDARIES(in_dev)) { | 			if (!do_promote) { | ||||||
| 				*ifap1 = ifa->ifa_next; | 				*ifap1 = ifa->ifa_next; | ||||||
| 
 | 
 | ||||||
| 				rtmsg_ifa(RTM_DELADDR, ifa); | 				rtmsg_ifa(RTM_DELADDR, ifa); | ||||||
|  | @ -283,19 +290,32 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | ||||||
| 	 */ | 	 */ | ||||||
| 	rtmsg_ifa(RTM_DELADDR, ifa1); | 	rtmsg_ifa(RTM_DELADDR, ifa1); | ||||||
| 	notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); | 	notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); | ||||||
|  | 
 | ||||||
|  | 	if (promote) { | ||||||
|  | 
 | ||||||
|  | 		if (prev_prom) { | ||||||
|  | 			prev_prom->ifa_next = promote->ifa_next; | ||||||
|  | 			promote->ifa_next = last_prim->ifa_next; | ||||||
|  | 			last_prim->ifa_next = promote; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		promote->ifa_flags &= ~IFA_F_SECONDARY; | ||||||
|  | 		rtmsg_ifa(RTM_NEWADDR, promote); | ||||||
|  | 		notifier_call_chain(&inetaddr_chain, NETDEV_UP, promote); | ||||||
|  | 		for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) { | ||||||
|  | 			if (ifa1->ifa_mask != ifa->ifa_mask || | ||||||
|  | 			    !inet_ifa_match(ifa1->ifa_address, ifa)) | ||||||
|  | 					continue; | ||||||
|  | 			fib_add_ifaddr(ifa); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
| 	if (destroy) { | 	if (destroy) { | ||||||
| 		inet_free_ifa(ifa1); | 		inet_free_ifa(ifa1); | ||||||
| 
 | 
 | ||||||
| 		if (!in_dev->ifa_list) | 		if (!in_dev->ifa_list) | ||||||
| 			inetdev_destroy(in_dev); | 			inetdev_destroy(in_dev); | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	if (promote && IN_DEV_PROMOTE_SECONDARIES(in_dev)) { |  | ||||||
| 		/* not sure if we should send a delete notify first? */ |  | ||||||
| 		promote->ifa_flags &= ~IFA_F_SECONDARY; |  | ||||||
| 		rtmsg_ifa(RTM_NEWADDR, promote); |  | ||||||
| 		notifier_call_chain(&inetaddr_chain, NETDEV_UP, promote); |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int inet_insert_ifa(struct in_ifaddr *ifa) | static int inet_insert_ifa(struct in_ifaddr *ifa) | ||||||
|  |  | ||||||
|  | @ -407,7 +407,7 @@ static void fib_magic(int cmd, int type, u32 dst, int dst_len, struct in_ifaddr | ||||||
| 		tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL); | 		tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void fib_add_ifaddr(struct in_ifaddr *ifa) | void fib_add_ifaddr(struct in_ifaddr *ifa) | ||||||
| { | { | ||||||
| 	struct in_device *in_dev = ifa->ifa_dev; | 	struct in_device *in_dev = ifa->ifa_dev; | ||||||
| 	struct net_device *dev = in_dev->dev; | 	struct net_device *dev = in_dev->dev; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jamal Hadi Salim
						Jamal Hadi Salim