diff options
Diffstat (limited to 'nest')
-rw-r--r-- | nest/iface.c | 333 | ||||
-rw-r--r-- | nest/iface.h | 53 | ||||
-rw-r--r-- | nest/protocol.h | 5 | ||||
-rw-r--r-- | nest/rt-dev.c | 23 |
4 files changed, 299 insertions, 115 deletions
diff --git a/nest/iface.c b/nest/iface.c index 002646b..069b740 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 diff --git a/nest/iface.h b/nest/iface.h index 8677be0..1d26e7b 100644 --- a/nest/iface.h +++ b/nest/iface.h @@ -15,48 +15,69 @@ extern list iface_list; struct proto; +struct ifa { /* Interface address */ + node n; + struct iface *iface; /* Interface this address belongs to */ + ip_addr ip; /* IP address of this host */ + ip_addr prefix; /* Network prefix */ + unsigned pxlen; /* Prefix length */ + ip_addr brd; /* Broadcast address */ + ip_addr opposite; /* Opposite end of a point-to-point link */ + unsigned scope; /* Interface address scope */ + unsigned flags; /* Analogous to iface->flags */ +}; + struct iface { node n; char name[16]; unsigned flags; unsigned mtu; unsigned index; /* OS-dependent interface index */ - ip_addr ip; /* IP address of this host (0=unset) */ - ip_addr prefix; /* Network prefix */ - unsigned pxlen; /* Prefix length */ - ip_addr brd; /* Broadcast address */ - ip_addr opposite; /* Opposite end of a point-to-point link */ + list addrs; /* Addresses assigned to this interface */ + struct ifa *addr; /* Primary address */ struct neighbor *neigh; /* List of neighbors on this interface */ }; -#define IF_UP 1 /* IF_LINK_UP, not IF_IGNORE and IP address known */ +#define IF_UP 1 /* IF_LINK_UP and IP address known */ #define IF_MULTIACCESS 2 #define IF_UNNUMBERED 4 #define IF_BROADCAST 8 -#define IF_MULTICAST 16 -#define IF_TUNNEL 32 -#define IF_ADMIN_DOWN 64 -#define IF_LOOPBACK 128 -#define IF_IGNORE 256 -#define IF_LINK_UP 512 +#define IF_MULTICAST 0x10 +#define IF_TUNNEL 0x20 +#define IF_ADMIN_DOWN 0x40 +#define IF_LOOPBACK 0x80 +#define IF_IGNORE 0x100 /* Not to be used by routing protocols (loopbacks etc.) */ +#define IF_LINK_UP 0x200 + +#define IA_PRIMARY 0x10000 /* This address is primary */ +#define IA_SECONDARY 0x20000 /* This address has been reported as secondary by the kernel */ +#define IA_FLAGS 0xff0000 + +#define IF_JUST_CREATED 0x10000000 /* Send creation event as soon as possible */ +#define IF_TMP_DOWN 0x20000000 /* Temporary shutdown due to interface reconfiguration */ #define IF_UPDATED 0x40000000 /* Touched in last scan */ /* Interface change events */ #define IF_CHANGE_UP 1 #define IF_CHANGE_DOWN 2 -#define IF_CHANGE_FLAGS 4 /* Can be converted to down/up internally */ -#define IF_CHANGE_MTU 8 -#define IF_CHANGE_CREATE 16 /* Seen this interface for the first time */ +#define IF_CHANGE_MTU 4 +#define IF_CHANGE_CREATE 8 /* Seen this interface for the first time */ +#define IF_CHANGE_TOO_MUCH 0x40000000 /* Used internally */ void if_init(void); void if_dump(struct iface *); void if_dump_all(void); -void if_update(struct iface *); +void ifa_dump(struct ifa *); +struct iface *if_update(struct iface *); +struct ifa *ifa_update(struct ifa *); +void ifa_delete(struct ifa *); void if_start_update(void); void if_end_update(void); +void if_end_partial_update(struct iface *); void if_feed_baby(struct proto *); struct iface *if_find_by_index(unsigned); +struct iface *if_find_by_name(char *); /* * Neighbor Cache. We hold (direct neighbor, protocol) pairs we've seen diff --git a/nest/protocol.h b/nest/protocol.h index c203f25..5e1e2ce 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -13,6 +13,7 @@ #include "lib/resource.h" struct iface; +struct ifa; struct rte; struct neighbor; struct rta; @@ -96,6 +97,7 @@ struct proto { * General protocol hooks: * * if_notify Notify protocol about interface state changes. + * ifa_notify Notify protocol about interface address changes. * rt_notify Notify protocol about routing table updates. * neigh_notify Notify protocol about neighbor cache events. * make_tmp_attrs Construct ea_list from private attrs stored in rte. @@ -106,7 +108,8 @@ struct proto { * 0=process it through the import filter set by the user. */ - void (*if_notify)(struct proto *, unsigned flags, struct iface *new, struct iface *old); + void (*if_notify)(struct proto *, unsigned flags, struct iface *i); + void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a); void (*rt_notify)(struct proto *, struct network *net, struct rte *new, struct rte *old); void (*neigh_notify)(struct neighbor *neigh); struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool); diff --git a/nest/rt-dev.c b/nest/rt-dev.c index d6035f5..1225f1d 100644 --- a/nest/rt-dev.c +++ b/nest/rt-dev.c @@ -19,19 +19,18 @@ #include "lib/resource.h" static void -dev_if_notify(struct proto *p, unsigned c, struct iface *new, struct iface *old) +dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) { struct rt_dev_config *P = (void *) p->cf; - if (old && !iface_patt_match(&P->iface_list, old) || - new && !iface_patt_match(&P->iface_list, new)) + if (!iface_patt_match(&P->iface_list, ad->iface)) return; if (c & IF_CHANGE_DOWN) { net *n; - debug("dev_if_notify: %s going down\n", old->name); - n = net_find(p->table, old->prefix, old->pxlen); + DBG("dev_if_notify: %s:%I going down\n", ad->iface->name, ad->ip); + n = net_find(p->table, ad->prefix, ad->pxlen); if (!n) { debug("dev_if_notify: device shutdown: prefix not found\n"); @@ -45,20 +44,20 @@ dev_if_notify(struct proto *p, unsigned c, struct iface *new, struct iface *old) net *n; rte *e; - debug("dev_if_notify: %s going up\n", new->name); + debug("dev_if_notify: %s:%I going up\n", ad->iface->name, ad->ip); bzero(&A, sizeof(A)); A.proto = p; A.source = RTS_DEVICE; - A.scope = (new->flags & IF_LOOPBACK) ? SCOPE_HOST : SCOPE_UNIVERSE; + A.scope = ad->scope; A.cast = RTC_UNICAST; A.dest = RTD_DEVICE; - A.iface = new; + A.iface = ad->iface; A.attrs = NULL; a = rta_lookup(&A); - if (new->flags & IF_UNNUMBERED) - n = net_get(p->table, new->opposite, new->pxlen); + if (ad->flags & IF_UNNUMBERED) + n = net_get(p->table, ad->opposite, ad->pxlen); else - n = net_get(p->table, new->prefix, new->pxlen); + n = net_get(p->table, ad->prefix, ad->pxlen); e = rte_get_temp(a); e->net = n; e->pflags = 0; @@ -71,7 +70,7 @@ dev_init(struct proto_config *c) { struct proto *p = proto_new(c, sizeof(struct proto)); - p->if_notify = dev_if_notify; + p->ifa_notify = dev_ifa_notify; return p; } |