diff options
author | Jamal Hadi Salim <hadi@cyberus.ca> | 2005-11-22 17:47:37 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2005-11-22 17:47:37 -0500 |
commit | 0ff60a45678e67b2547256a636fd00c1667ce4fa (patch) | |
tree | 664625450d2821c2d22e35e3f9f22192bf41d7d4 | |
parent | c27bd492fd84c590767a3c0f9f74e637b17af138 (diff) |
[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>
-rw-r--r-- | include/net/route.h | 3 | ||||
-rw-r--r-- | net/ipv4/devinet.c | 40 | ||||
-rw-r--r-- | net/ipv4/fib_frontend.c | 2 |
3 files changed, 34 insertions, 11 deletions
diff --git a/include/net/route.h b/include/net/route.h index dbe79ca67d3..e3e5436f801 100644 --- a/include/net/route.h +++ b/include/net/route.h | |||
@@ -126,6 +126,9 @@ extern int ip_rt_ioctl(unsigned int cmd, void __user *arg); | |||
126 | extern void ip_rt_get_source(u8 *src, struct rtable *rt); | 126 | extern void ip_rt_get_source(u8 *src, struct rtable *rt); |
127 | extern int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb); | 127 | extern int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb); |
128 | 128 | ||
129 | struct in_ifaddr; | ||
130 | extern void fib_add_ifaddr(struct in_ifaddr *); | ||
131 | |||
129 | static inline void ip_rt_put(struct rtable * rt) | 132 | static inline void ip_rt_put(struct rtable * rt) |
130 | { | 133 | { |
131 | if (rt) | 134 | if (rt) |
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 4ec4b2ca6ab..04a6fe3e95a 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
@@ -234,7 +234,10 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
234 | int destroy) | 234 | int destroy) |
235 | { | 235 | { |
236 | struct in_ifaddr *promote = NULL; | 236 | struct in_ifaddr *promote = NULL; |
237 | struct in_ifaddr *ifa1 = *ifap; | 237 | struct in_ifaddr *ifa, *ifa1 = *ifap; |
238 | struct in_ifaddr *last_prim = in_dev->ifa_list; | ||
239 | struct in_ifaddr *prev_prom = NULL; | ||
240 | int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); | ||
238 | 241 | ||
239 | ASSERT_RTNL(); | 242 | ASSERT_RTNL(); |
240 | 243 | ||
@@ -243,18 +246,22 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
243 | **/ | 246 | **/ |
244 | 247 | ||
245 | if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { | 248 | if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { |
246 | struct in_ifaddr *ifa; | ||
247 | struct in_ifaddr **ifap1 = &ifa1->ifa_next; | 249 | struct in_ifaddr **ifap1 = &ifa1->ifa_next; |
248 | 250 | ||
249 | while ((ifa = *ifap1) != NULL) { | 251 | while ((ifa = *ifap1) != NULL) { |
252 | if (!(ifa->ifa_flags & IFA_F_SECONDARY) && | ||
253 | ifa1->ifa_scope <= ifa->ifa_scope) | ||
254 | last_prim = ifa; | ||
255 | |||
250 | if (!(ifa->ifa_flags & IFA_F_SECONDARY) || | 256 | if (!(ifa->ifa_flags & IFA_F_SECONDARY) || |
251 | ifa1->ifa_mask != ifa->ifa_mask || | 257 | ifa1->ifa_mask != ifa->ifa_mask || |
252 | !inet_ifa_match(ifa1->ifa_address, ifa)) { | 258 | !inet_ifa_match(ifa1->ifa_address, ifa)) { |
253 | ifap1 = &ifa->ifa_next; | 259 | ifap1 = &ifa->ifa_next; |
260 | prev_prom = ifa; | ||
254 | continue; | 261 | continue; |
255 | } | 262 | } |
256 | 263 | ||
257 | if (!IN_DEV_PROMOTE_SECONDARIES(in_dev)) { | 264 | if (!do_promote) { |
258 | *ifap1 = ifa->ifa_next; | 265 | *ifap1 = ifa->ifa_next; |
259 | 266 | ||
260 | rtmsg_ifa(RTM_DELADDR, ifa); | 267 | rtmsg_ifa(RTM_DELADDR, ifa); |
@@ -283,18 +290,31 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
283 | */ | 290 | */ |
284 | rtmsg_ifa(RTM_DELADDR, ifa1); | 291 | rtmsg_ifa(RTM_DELADDR, ifa1); |
285 | notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); | 292 | notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); |
286 | if (destroy) { | ||
287 | inet_free_ifa(ifa1); | ||
288 | 293 | ||
289 | if (!in_dev->ifa_list) | 294 | if (promote) { |
290 | inetdev_destroy(in_dev); | 295 | |
291 | } | 296 | if (prev_prom) { |
297 | prev_prom->ifa_next = promote->ifa_next; | ||
298 | promote->ifa_next = last_prim->ifa_next; | ||
299 | last_prim->ifa_next = promote; | ||
300 | } | ||
292 | 301 | ||
293 | if (promote && IN_DEV_PROMOTE_SECONDARIES(in_dev)) { | ||
294 | /* not sure if we should send a delete notify first? */ | ||
295 | promote->ifa_flags &= ~IFA_F_SECONDARY; | 302 | promote->ifa_flags &= ~IFA_F_SECONDARY; |
296 | rtmsg_ifa(RTM_NEWADDR, promote); | 303 | rtmsg_ifa(RTM_NEWADDR, promote); |
297 | notifier_call_chain(&inetaddr_chain, NETDEV_UP, promote); | 304 | notifier_call_chain(&inetaddr_chain, NETDEV_UP, promote); |
305 | for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) { | ||
306 | if (ifa1->ifa_mask != ifa->ifa_mask || | ||
307 | !inet_ifa_match(ifa1->ifa_address, ifa)) | ||
308 | continue; | ||
309 | fib_add_ifaddr(ifa); | ||
310 | } | ||
311 | |||
312 | } | ||
313 | if (destroy) { | ||
314 | inet_free_ifa(ifa1); | ||
315 | |||
316 | if (!in_dev->ifa_list) | ||
317 | inetdev_destroy(in_dev); | ||
298 | } | 318 | } |
299 | } | 319 | } |
300 | 320 | ||
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 2267c1fad87..882f88f6d13 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c | |||
@@ -407,7 +407,7 @@ static void fib_magic(int cmd, int type, u32 dst, int dst_len, struct in_ifaddr | |||
407 | tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL); | 407 | tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL); |
408 | } | 408 | } |
409 | 409 | ||
410 | static void fib_add_ifaddr(struct in_ifaddr *ifa) | 410 | void fib_add_ifaddr(struct in_ifaddr *ifa) |
411 | { | 411 | { |
412 | struct in_device *in_dev = ifa->ifa_dev; | 412 | struct in_device *in_dev = ifa->ifa_dev; |
413 | struct net_device *dev = in_dev->dev; | 413 | struct net_device *dev = in_dev->dev; |