Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 242608
b: refs/heads/master
c: e6abbaa
h: refs/heads/master
v: v3
  • Loading branch information
Julian Anastasov authored and David S. Miller committed Mar 22, 2011
1 parent fc788a4 commit 35eee6c
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 14 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 74cb3c108bc0f599a4eb40980db8580cfba725c9
refs/heads/master: e6abbaa2725a43cf5d26c4c2a5dc6c0f6029ea19
1 change: 1 addition & 0 deletions trunk/include/net/route.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ extern int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb);

struct in_ifaddr;
extern void fib_add_ifaddr(struct in_ifaddr *);
extern void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *);

static inline void ip_rt_put(struct rtable * rt)
{
Expand Down
101 changes: 88 additions & 13 deletions trunk/net/ipv4/fib_frontend.c
Original file line number Diff line number Diff line change
Expand Up @@ -722,30 +722,44 @@ void fib_add_ifaddr(struct in_ifaddr *ifa)
}
}

static void fib_del_ifaddr(struct in_ifaddr *ifa)
/* Delete primary or secondary address.
* Optionally, on secondary address promotion consider the addresses
* from subnet iprim as deleted, even if they are in device list.
* In this case the secondary ifa can be in device list.
*/
void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
{
struct in_device *in_dev = ifa->ifa_dev;
struct net_device *dev = in_dev->dev;
struct in_ifaddr *ifa1;
struct in_ifaddr *prim = ifa;
struct in_ifaddr *prim = ifa, *prim1 = NULL;
__be32 brd = ifa->ifa_address | ~ifa->ifa_mask;
__be32 any = ifa->ifa_address & ifa->ifa_mask;
#define LOCAL_OK 1
#define BRD_OK 2
#define BRD0_OK 4
#define BRD1_OK 8
unsigned ok = 0;
int subnet = 0; /* Primary network */
int gone = 1; /* Address is missing */
int same_prefsrc = 0; /* Another primary with same IP */

if (!(ifa->ifa_flags & IFA_F_SECONDARY))
fib_magic(RTM_DELROUTE,
dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
any, ifa->ifa_prefixlen, prim);
else {
if (ifa->ifa_flags & IFA_F_SECONDARY) {
prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask);
if (prim == NULL) {
printk(KERN_WARNING "fib_del_ifaddr: bug: prim == NULL\n");
return;
}
if (iprim && iprim != prim) {
printk(KERN_WARNING "fib_del_ifaddr: bug: iprim != prim\n");
return;
}
} else if (!ipv4_is_zeronet(any) &&
(any != ifa->ifa_local || ifa->ifa_prefixlen < 32)) {
fib_magic(RTM_DELROUTE,
dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
any, ifa->ifa_prefixlen, prim);
subnet = 1;
}

/* Deletion is more complicated than add.
Expand All @@ -755,6 +769,49 @@ static void fib_del_ifaddr(struct in_ifaddr *ifa)
*/

for (ifa1 = in_dev->ifa_list; ifa1; ifa1 = ifa1->ifa_next) {
if (ifa1 == ifa) {
/* promotion, keep the IP */
gone = 0;
continue;
}
/* Ignore IFAs from our subnet */
if (iprim && ifa1->ifa_mask == iprim->ifa_mask &&
inet_ifa_match(ifa1->ifa_address, iprim))
continue;

/* Ignore ifa1 if it uses different primary IP (prefsrc) */
if (ifa1->ifa_flags & IFA_F_SECONDARY) {
/* Another address from our subnet? */
if (ifa1->ifa_mask == prim->ifa_mask &&
inet_ifa_match(ifa1->ifa_address, prim))
prim1 = prim;
else {
/* We reached the secondaries, so
* same_prefsrc should be determined.
*/
if (!same_prefsrc)
continue;
/* Search new prim1 if ifa1 is not
* using the current prim1
*/
if (!prim1 ||
ifa1->ifa_mask != prim1->ifa_mask ||
!inet_ifa_match(ifa1->ifa_address, prim1))
prim1 = inet_ifa_byprefix(in_dev,
ifa1->ifa_address,
ifa1->ifa_mask);
if (!prim1)
continue;
if (prim1->ifa_local != prim->ifa_local)
continue;
}
} else {
if (prim->ifa_local != ifa1->ifa_local)
continue;
prim1 = ifa1;
if (prim != prim1)
same_prefsrc = 1;
}
if (ifa->ifa_local == ifa1->ifa_local)
ok |= LOCAL_OK;
if (ifa->ifa_broadcast == ifa1->ifa_broadcast)
Expand All @@ -763,19 +820,37 @@ static void fib_del_ifaddr(struct in_ifaddr *ifa)
ok |= BRD1_OK;
if (any == ifa1->ifa_broadcast)
ok |= BRD0_OK;
/* primary has network specific broadcasts */
if (prim1 == ifa1 && ifa1->ifa_prefixlen < 31) {
__be32 brd1 = ifa1->ifa_address | ~ifa1->ifa_mask;
__be32 any1 = ifa1->ifa_address & ifa1->ifa_mask;

if (!ipv4_is_zeronet(any1)) {
if (ifa->ifa_broadcast == brd1 ||
ifa->ifa_broadcast == any1)
ok |= BRD_OK;
if (brd == brd1 || brd == any1)
ok |= BRD1_OK;
if (any == brd1 || any == any1)
ok |= BRD0_OK;
}
}
}

if (!(ok & BRD_OK))
fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
if (!(ok & BRD1_OK))
fib_magic(RTM_DELROUTE, RTN_BROADCAST, brd, 32, prim);
if (!(ok & BRD0_OK))
fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32, prim);
if (subnet && ifa->ifa_prefixlen < 31) {
if (!(ok & BRD1_OK))
fib_magic(RTM_DELROUTE, RTN_BROADCAST, brd, 32, prim);
if (!(ok & BRD0_OK))
fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32, prim);
}
if (!(ok & LOCAL_OK)) {
fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim);

/* Check, that this local address finally disappeared. */
if (inet_addr_type(dev_net(dev), ifa->ifa_local) != RTN_LOCAL) {
if (gone &&
inet_addr_type(dev_net(dev), ifa->ifa_local) != RTN_LOCAL) {
/* And the last, but not the least thing.
* We must flush stray FIB entries.
*
Expand Down Expand Up @@ -896,7 +971,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
rt_cache_flush(dev_net(dev), -1);
break;
case NETDEV_DOWN:
fib_del_ifaddr(ifa);
fib_del_ifaddr(ifa, NULL);
fib_update_nh_saddrs(dev);
if (ifa->ifa_dev->ifa_list == NULL) {
/* Last address was deleted from this interface.
Expand Down

0 comments on commit 35eee6c

Please sign in to comment.