diff options
author | Martin Mares <mj@ucw.cz> | 1999-05-06 21:38:11 +0000 |
---|---|---|
committer | Martin Mares <mj@ucw.cz> | 1999-05-06 21:38:11 +0000 |
commit | 9a158361da249e0eab1e0f7bd2c7dbe9f32eddfa (patch) | |
tree | 4d9dff14fd053aaa2d45be859144148eb8851a4d /nest/iface.c | |
parent | ec8b579e9c9703601bf745745b620103fe2e2477 (diff) |
I rewrote the interface handling code, so that it supports multiple
addresses per interface (needed for example for IPv6 support).
Visible changes:
o struct iface now contains a list of all interface addresses (represented
by struct ifa), iface->addr points to the primary address (if any).
o Interface has IF_UP set iff it's up and it has a primary address.
o IF_UP is now independent on IF_IGNORED (i.e., you need to test IF_IGNORED
in the protocols; I've added this, but please check).
o The if_notify_change hook has been simplified (only one interface pointer
etc.).
o Introduced a ifa_notify_change hook. (For now, only the Direct protocol
does use it -- it's wise to just listen to device routes in all other
protocols.)
o Removed IF_CHANGE_FLAGS notifier flag (it was meaningless anyway).
o Updated all the code except netlink (I'll look at it tomorrow) to match
the new semantics (please look at your code to ensure I did it right).
Things to fix:
o Netlink.
o Make krt-iface interpret "eth0:1"-type aliases as secondary addresses.
Diffstat (limited to 'nest/iface.c')
-rw-r--r-- | nest/iface.c | 333 |
1 files changed, 247 insertions, 86 deletions
diff --git a/nest/iface.c b/nest/iface.c index 002646ba..069b740c 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -31,17 +31,22 @@ static list neigh_list; static int if_connected(ip_addr *a, struct iface *i) /* -1=error, 1=match, 0=no match */ { + struct ifa *b; + if (!(i->flags & IF_UP)) return 0; - if ((i->flags & IF_UNNUMBERED) && ipa_equal(*a, i->opposite)) + if ((i->flags & IF_UNNUMBERED) && ipa_equal(*a, i->addr->opposite)) return 1; - if (!ipa_in_net(*a, i->prefix, i->pxlen)) - return 0; - if (ipa_equal(*a, i->prefix) || /* Network address */ - ipa_equal(*a, i->brd) || /* Broadcast */ - ipa_equal(*a, i->ip)) /* Our own address */ - return -1; - return 1; + WALK_LIST(b, i->addrs) + if (ipa_in_net(*a, b->prefix, b->pxlen)) + { + if (ipa_equal(*a, b->prefix) || /* Network address */ + ipa_equal(*a, b->brd) || /* Broadcast */ + ipa_equal(*a, b->ip)) /* Our own address */ + return -1; + return 1; + } + return 0; } neighbor * @@ -69,7 +74,7 @@ neigh_find(struct proto *p, ip_addr *a, unsigned flags) case -1: return NULL; case 1: - if (!j || j->pxlen > i->pxlen) + if (!j) /* FIXME: Search for _optimal_ iface route? */ j = i; /* Fall-thru */ } @@ -188,8 +193,18 @@ neigh_prune(void) list iface_list; void +ifa_dump(struct ifa *a) +{ + debug("\t%I, net %I/%-2d bc %I -> %I%s%s\n", a->ip, a->prefix, a->pxlen, a->brd, a->opposite, + (a->flags & IF_UP) ? "" : " DOWN", + (a->flags & IA_PRIMARY) ? "" : " SEC"); +} + +void if_dump(struct iface *i) { + struct ifa *a; + debug("IF%d: %s", i->index, i->name); if (i->flags & IF_ADMIN_DOWN) debug(" ADMIN-DOWN"); @@ -213,8 +228,14 @@ if_dump(struct iface *i) debug(" LOOP"); if (i->flags & IF_IGNORE) debug(" IGN"); + if (i->flags & IF_TMP_DOWN) + debug(" TDOWN"); debug(" MTU=%d\n", i->mtu); - debug("\t%I, net %I/%-2d bc %I -> %I\n", i->ip, i->prefix, i->pxlen, i->brd, i->opposite); + WALK_LIST(a, i->addrs) + { + ifa_dump(a); + ASSERT((a != i->addr) == !(a->flags & IA_PRIMARY)); + } } void @@ -228,151 +249,200 @@ if_dump_all(void) debug("Router ID: %08x\n", config->router_id); } -static inline int -if_change_too_big_p(struct iface *i, struct iface *j) +static inline unsigned +if_what_changed(struct iface *i, struct iface *j) { - if (!ipa_equal(i->ip, j->ip) || /* Address change isn't */ - !ipa_equal(i->prefix, j->prefix) || - i->pxlen != j->pxlen || - !ipa_equal(i->brd, j->brd) || - !ipa_equal(i->opposite, j->opposite)) - return 1; - if ((i->flags ^ j->flags) & ~(IF_UP | IF_ADMIN_DOWN | IF_UPDATED | IF_LINK_UP)) - return 1; /* Interface type change isn't as well */ - return 0; + unsigned c; + + if (((i->flags ^ j->flags) & ~(IF_UP | IF_ADMIN_DOWN | IF_UPDATED | IF_LINK_UP | IF_TMP_DOWN | IF_JUST_CREATED)) + || i->index != j->index) + return IF_CHANGE_TOO_MUCH; + c = 0; + if ((i->flags ^ j->flags) & IF_UP) + c |= (i->flags & IF_UP) ? IF_CHANGE_DOWN : IF_CHANGE_UP; + if (i->mtu != j->mtu) + c |= IF_CHANGE_MTU; + return c; } static inline void if_copy(struct iface *to, struct iface *from) { - to->flags = from->flags; + to->flags = from->flags | (to->flags & IF_TMP_DOWN); to->mtu = from->mtu; - to->index = from->index; - to->neigh = from->neigh; } -static unsigned -if_changed(struct iface *i, struct iface *j) +static void +ifa_notify_change(unsigned c, struct ifa *a) { - unsigned f = 0; + struct proto *p; - if (i->mtu != j->mtu) - f |= IF_CHANGE_MTU; - if ((i->flags ^ j->flags) & ~IF_UPDATED) - { - f |= IF_CHANGE_FLAGS; - if ((i->flags ^ j->flags) & IF_UP) - if (i->flags & IF_UP) - f |= IF_CHANGE_DOWN; - else - f |= IF_CHANGE_UP; - } - return f; + debug("IFA change notification (%x) for %s:%I\n", c, a->iface->name, a->ip); + WALK_LIST(p, proto_list) + if (p->ifa_notify) + p->ifa_notify(p, c, a); } static void -if_notify_change(unsigned c, struct iface *old, struct iface *new, struct iface *real) +if_notify_change(unsigned c, struct iface *i) { struct proto *p; + struct ifa *a; - debug("Interface change notification (%x) for %s\n", c, new->name); - if (old) - if_dump(old); - if (new) - if_dump(new); + if (i->flags & IF_JUST_CREATED) + { + i->flags &= ~IF_JUST_CREATED; + c |= IF_CHANGE_CREATE | IF_CHANGE_MTU; + } + + debug("Interface change notification (%x) for %s\n", c, i->name); + if_dump(i); if (c & IF_CHANGE_UP) - neigh_if_up(real); + neigh_if_up(i); + if (c & IF_CHANGE_DOWN) + WALK_LIST(a, i->addrs) + { + a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS); + ifa_notify_change(IF_CHANGE_DOWN, a); + } WALK_LIST(p, proto_list) if (p->if_notify) - p->if_notify(p, c, new, old); + p->if_notify(p, c, i); + if (c & IF_CHANGE_UP) + WALK_LIST(a, i->addrs) + { + a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS); + ifa_notify_change(IF_CHANGE_UP, a); + } if (c & IF_CHANGE_DOWN) - neigh_if_down(real); + neigh_if_down(i); } -void +static unsigned +if_recalc_flags(struct iface *i, unsigned flags) +{ + if ((flags & (IF_ADMIN_DOWN | IF_TMP_DOWN)) || + !(flags & IF_LINK_UP) || + !i->addr) + flags &= ~IF_UP; + else + flags |= IF_UP; + return flags; +} + +static void +if_change_flags(struct iface *i, unsigned flags) +{ + unsigned of = i->flags; + + i->flags = if_recalc_flags(i, flags); + if ((i->flags ^ of) & IF_UP) + if_notify_change((i->flags & IF_UP) ? IF_CHANGE_UP : IF_CHANGE_DOWN, i); +} + +struct iface * if_update(struct iface *new) { struct iface *i; + struct ifa *a, *b; unsigned c; - if ((new->flags & IF_LINK_UP) && !(new->flags & IF_ADMIN_DOWN) && ipa_nonzero(new->ip)) - new->flags |= IF_UP; - else - new->flags &= ~IF_UP; - WALK_LIST(i, iface_list) if (!strcmp(new->name, i->name)) { - if (if_change_too_big_p(i, new)) /* Changed a lot, convert it to down/up */ + new->addr = i->addr; + new->flags = if_recalc_flags(new, new->flags); + c = if_what_changed(i, new); + if (c & IF_CHANGE_TOO_MUCH) /* Changed a lot, convert it to down/up */ { DBG("Interface %s changed too much -- forcing down/up transition\n", i->name); - if (i->flags & IF_UP) - { - struct iface j; - memcpy(&j, i, sizeof(struct iface)); - i->flags &= ~IF_UP; - if_notify_change(IF_CHANGE_DOWN | IF_CHANGE_FLAGS, &j, i, i); - } + if_change_flags(i, i->flags | IF_TMP_DOWN); rem_node(&i->n); + WALK_LIST_DELSAFE(a, b, i->addrs) + ifa_delete(a); goto newif; } - c = if_changed(i, new); - if (c) - if_notify_change(c, i, new, i); - if_copy(i, new); /* Even if c==0 as we might need to update i->index et al. */ + else if (c) + { + if_copy(i, new); + if_notify_change(c, i); + } i->flags |= IF_UPDATED; - return; + return i; } - i = mb_alloc(if_pool, sizeof(struct iface)); newif: memcpy(i, new, sizeof(*i)); - i->flags |= IF_UPDATED; + init_list(&i->addrs); + i->flags |= IF_UPDATED | IF_TMP_DOWN; /* Tmp down as we don't have addresses yet */ add_tail(&iface_list, &i->n); - if_notify_change(IF_CHANGE_CREATE | ((i->flags & IF_UP) ? IF_CHANGE_UP : 0) - | IF_CHANGE_FLAGS | IF_CHANGE_MTU, NULL, i, i); + return i; } void if_start_update(void) { struct iface *i; + struct ifa *a; WALK_LIST(i, iface_list) - i->flags &= ~IF_UPDATED; + { + i->flags &= ~IF_UPDATED; + WALK_LIST(a, i->addrs) + a->flags &= ~IF_UPDATED; + } +} + +void +if_end_partial_update(struct iface *i) +{ + if (i->flags & IF_TMP_DOWN) + if_change_flags(i, i->flags & ~IF_TMP_DOWN); } void if_end_update(void) { struct iface *i, j; + struct ifa *a, *b; if (!config->router_id) auto_router_id(); WALK_LIST(i, iface_list) - if (!(i->flags & IF_UPDATED)) - { - memcpy(&j, i, sizeof(struct iface)); - i->flags = (i->flags & ~(IF_LINK_UP | IF_UP)) | IF_ADMIN_DOWN; - if (i->flags != j.flags) - if_notify_change(IF_CHANGE_DOWN | IF_CHANGE_FLAGS, &j, i, i); - } + { + if (!(i->flags & IF_UPDATED)) + if_change_flags(i, (i->flags & ~IF_LINK_UP) | IF_ADMIN_DOWN); + else + { + WALK_LIST_DELSAFE(a, b, i->addrs) + if (!(a->flags & IF_UPDATED)) + ifa_delete(a); + if_end_partial_update(i); + } + } } void if_feed_baby(struct proto *p) { struct iface *i; + struct ifa *a; - if (!p->if_notify) + if (!p->if_notify && !p->ifa_notify) return; debug("Announcing interfaces to new protocol %s\n", p->name); WALK_LIST(i, iface_list) - p->if_notify(p, IF_CHANGE_CREATE | ((i->flags & IF_UP) ? IF_CHANGE_UP : 0), i, NULL); + { + if (p->if_notify) + p->if_notify(p, IF_CHANGE_CREATE | ((i->flags & IF_UP) ? IF_CHANGE_UP : 0), i); + if (p->ifa_notify && (i->flags & IF_UP)) + WALK_LIST(a, i->addrs) + p->ifa_notify(p, IF_CHANGE_CREATE | IF_CHANGE_UP, a); + } } struct iface * @@ -386,6 +456,96 @@ if_find_by_index(unsigned idx) return NULL; } +struct iface * +if_find_by_name(char *name) +{ + struct iface *i; + + WALK_LIST(i, iface_list) + if (!strcmp(i->name, name)) + return i; + return NULL; +} + +static int +ifa_recalc_primary(struct iface *i) +{ + struct ifa *a, *b = NULL; + int res; + + WALK_LIST(a, i->addrs) + { + if (!(a->flags & IA_SECONDARY) && (!b || a->scope > b->scope)) + b = a; + a->flags &= ~IA_PRIMARY; + } + res = (b != i->addr); + i->addr = b; + if (b) + { + b->flags |= IA_PRIMARY; + rem_node(&b->n); + add_head(&i->addrs, &b->n); + } + return res; +} + +struct ifa * +ifa_update(struct ifa *a) +{ + struct iface *i = a->iface; + struct ifa *b; + + WALK_LIST(b, i->addrs) + if (ipa_equal(b->ip, a->ip)) + { + if (ipa_equal(b->prefix, a->prefix) && + b->pxlen == a->pxlen && + ipa_equal(b->brd, a->brd) && + ipa_equal(b->opposite, a->opposite) && + b->scope == a->scope) + { + b->flags |= IF_UPDATED; + return b; + } + ifa_delete(b); + break; + } + b = mb_alloc(if_pool, sizeof(struct ifa)); + memcpy(b, a, sizeof(struct ifa)); + add_tail(&i->addrs, &b->n); + b->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS); + if ((!i->addr || i->addr->scope < b->scope) && ifa_recalc_primary(i)) + if_change_flags(i, i->flags | IF_TMP_DOWN); + if (b->flags & IF_UP) + ifa_notify_change(IF_CHANGE_CREATE | IF_CHANGE_UP, b); + return b; +} + +void +ifa_delete(struct ifa *a) +{ + struct iface *i = a->iface; + struct ifa *b; + + WALK_LIST(b, i->addrs) + if (ipa_equal(b->ip, a->ip)) + { + rem_node(&b->n); + if (b->flags & IF_UP) + { + b->flags &= ~IF_UP; + ifa_notify_change(IF_CHANGE_DOWN, b); + } + if (b->flags & IA_PRIMARY) + { + if_change_flags(i, i->flags | IF_TMP_DOWN); + ifa_recalc_primary(i); + } + mb_free(b); + } +} + static void auto_router_id(void) /* FIXME: What if we run IPv6??? */ { @@ -393,14 +553,15 @@ auto_router_id(void) /* FIXME: What if we run IPv6??? */ j = NULL; WALK_LIST(i, iface_list) - if ((i->flags & IF_UP) && - !(i->flags & (IF_UNNUMBERED | IF_IGNORE)) && - (!j || ipa_to_u32(i->ip) < ipa_to_u32(j->ip))) + if ((i->flags & IF_LINK_UP) && + !(i->flags & (IF_UNNUMBERED | IF_IGNORE | IF_ADMIN_DOWN)) && + i->addr && + (!j || ipa_to_u32(i->addr->ip) < ipa_to_u32(j->addr->ip))) j = i; if (!j) die("Cannot determine router ID (no suitable network interface found), please configure it manually"); - debug("Guessed router ID %I (%s)\n", j->ip, j->name); - config->router_id = ipa_to_u32(j->ip); + debug("Guessed router ID %I (%s)\n", j->addr->ip, j->name); + config->router_id = ipa_to_u32(j->addr->ip); } void |