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; |
