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 dbe79ca67d31..e3e5436f8017 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 4ec4b2ca6ab1..04a6fe3e95a2 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 2267c1fad879..882f88f6d13b 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; |
