diff options
Diffstat (limited to 'net/ipv4/devinet.c')
-rw-r--r-- | net/ipv4/devinet.c | 30 |
1 files changed, 29 insertions, 1 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 6d85800daeb7..5345b0bee6df 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
@@ -64,6 +64,8 @@ | |||
64 | #include <net/rtnetlink.h> | 64 | #include <net/rtnetlink.h> |
65 | #include <net/net_namespace.h> | 65 | #include <net/net_namespace.h> |
66 | 66 | ||
67 | #include "fib_lookup.h" | ||
68 | |||
67 | static struct ipv4_devconf ipv4_devconf = { | 69 | static struct ipv4_devconf ipv4_devconf = { |
68 | .data = { | 70 | .data = { |
69 | [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, | 71 | [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, |
@@ -151,6 +153,20 @@ struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) | |||
151 | break; | 153 | break; |
152 | } | 154 | } |
153 | } | 155 | } |
156 | if (!result) { | ||
157 | struct flowi4 fl4 = { .daddr = addr }; | ||
158 | struct fib_result res = { 0 }; | ||
159 | struct fib_table *local; | ||
160 | |||
161 | /* Fallback to FIB local table so that communication | ||
162 | * over loopback subnets work. | ||
163 | */ | ||
164 | local = fib_get_table(net, RT_TABLE_LOCAL); | ||
165 | if (local && | ||
166 | !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && | ||
167 | res.type == RTN_LOCAL) | ||
168 | result = FIB_RES_DEV(res); | ||
169 | } | ||
154 | if (result && devref) | 170 | if (result && devref) |
155 | dev_hold(result); | 171 | dev_hold(result); |
156 | rcu_read_unlock(); | 172 | rcu_read_unlock(); |
@@ -345,6 +361,17 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
345 | } | 361 | } |
346 | } | 362 | } |
347 | 363 | ||
364 | /* On promotion all secondaries from subnet are changing | ||
365 | * the primary IP, we must remove all their routes silently | ||
366 | * and later to add them back with new prefsrc. Do this | ||
367 | * while all addresses are on the device list. | ||
368 | */ | ||
369 | for (ifa = promote; ifa; ifa = ifa->ifa_next) { | ||
370 | if (ifa1->ifa_mask == ifa->ifa_mask && | ||
371 | inet_ifa_match(ifa1->ifa_address, ifa)) | ||
372 | fib_del_ifaddr(ifa, ifa1); | ||
373 | } | ||
374 | |||
348 | /* 2. Unlink it */ | 375 | /* 2. Unlink it */ |
349 | 376 | ||
350 | *ifap = ifa1->ifa_next; | 377 | *ifap = ifa1->ifa_next; |
@@ -364,6 +391,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
364 | blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); | 391 | blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); |
365 | 392 | ||
366 | if (promote) { | 393 | if (promote) { |
394 | struct in_ifaddr *next_sec = promote->ifa_next; | ||
367 | 395 | ||
368 | if (prev_prom) { | 396 | if (prev_prom) { |
369 | prev_prom->ifa_next = promote->ifa_next; | 397 | prev_prom->ifa_next = promote->ifa_next; |
@@ -375,7 +403,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
375 | rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid); | 403 | rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid); |
376 | blocking_notifier_call_chain(&inetaddr_chain, | 404 | blocking_notifier_call_chain(&inetaddr_chain, |
377 | NETDEV_UP, promote); | 405 | NETDEV_UP, promote); |
378 | for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) { | 406 | for (ifa = next_sec; ifa; ifa = ifa->ifa_next) { |
379 | if (ifa1->ifa_mask != ifa->ifa_mask || | 407 | if (ifa1->ifa_mask != ifa->ifa_mask || |
380 | !inet_ifa_match(ifa1->ifa_address, ifa)) | 408 | !inet_ifa_match(ifa1->ifa_address, ifa)) |
381 | continue; | 409 | continue; |