diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2009-05-29 22:49:30 +0200 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2009-05-29 22:49:30 +0200 |
commit | 874b868544c3a6ba45ace062091cc3aee007b719 (patch) | |
tree | dc9558c4e5e1537ea11469bb4be93bf941b9ee28 | |
parent | 51f4469f03759642870a45634d9b53054e3deb92 (diff) |
Implements primary address selection base on 'primary' option.
-rw-r--r-- | conf/confbase.Y | 6 | ||||
-rw-r--r-- | nest/iface.c | 42 | ||||
-rw-r--r-- | nest/iface.h | 1 | ||||
-rw-r--r-- | sysdep/unix/krt.Y | 2 | ||||
-rw-r--r-- | sysdep/unix/krt.c | 56 |
5 files changed, 92 insertions, 15 deletions
diff --git a/conf/confbase.Y b/conf/confbase.Y index 43854623..a2df85dc 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -57,6 +57,7 @@ CF_DECLS %type <time> datetime %type <a> ipa %type <px> prefix prefix_or_ipa +%type <t> text_or_none %nonassoc PREFIX_DUMMY %nonassoc '=' '<' '>' '~' '.' GEQ LEQ NEQ AND OR PO PC @@ -153,6 +154,11 @@ datetime: } ; +text_or_none: + TEXT { $$ = $1; } + | { $$ = NULL; } + ; + CF_CODE CF_END diff --git a/nest/iface.c b/nest/iface.c index 01f25810..5e88b21b 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -399,29 +399,43 @@ if_find_by_name(char *name) return NULL; } +struct ifa *kif_choose_primary(struct iface *i); + static int ifa_recalc_primary(struct iface *i) { - struct ifa *a, *b = NULL; - int res; + struct ifa *a = kif_choose_primary(i); - WALK_LIST(a, i->addrs) + if (a == i->addr) + return 0; + + if (i->addr) + i->addr->flags &= ~IA_PRIMARY; + + if (a) { - if (!(a->flags & IA_SECONDARY) && (!b || a->scope > b->scope)) - b = a; - a->flags &= ~IA_PRIMARY; + a->flags |= IA_PRIMARY; + rem_node(&a->n); + add_head(&i->addrs, &a->n); } - res = (b != i->addr); - i->addr = b; - if (b) + + i->addr = a; + return 1; +} + +void +ifa_recalc_all_primary_addresses(void) +{ + struct iface *i; + + WALK_LIST(i, iface_list) { - b->flags |= IA_PRIMARY; - rem_node(&b->n); - add_head(&i->addrs, &b->n); + if (ifa_recalc_primary(i)) + if_change_flags(i, i->flags | IF_TMP_DOWN); } - return res; } + /** * ifa_update - update interface address * @a: new interface address @@ -464,7 +478,7 @@ ifa_update(struct ifa *a) 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 (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); diff --git a/nest/iface.h b/nest/iface.h index f884dd90..af98a761 100644 --- a/nest/iface.h +++ b/nest/iface.h @@ -80,6 +80,7 @@ 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 *); +void ifa_recalc_all_primary_addresses(void); /* The Neighbor Cache */ diff --git a/sysdep/unix/krt.Y b/sysdep/unix/krt.Y index 24f2dd27..40f1af9f 100644 --- a/sysdep/unix/krt.Y +++ b/sysdep/unix/krt.Y @@ -82,7 +82,7 @@ kif_item: /* Scan time of 0 means scan on startup only */ THIS_KIF->scan_time = $3; } - | PRIMARY TEXT prefix_or_ipa { + | PRIMARY text_or_none prefix_or_ipa { struct kif_primary_item *kpi = cfg_alloc(sizeof (struct kif_primary_item)); kpi->pattern = $2; kpi->prefix = $3.addr; diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 6208f689..a60e7f91 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -151,6 +151,49 @@ kif_shutdown(struct proto *P) return PS_DOWN; } + +static inline int +prefer_scope(struct ifa *a, struct ifa *b) +{ return (a->scope > SCOPE_LINK) && (b->scope <= SCOPE_LINK); } + +static inline int +prefer_addr(struct ifa *a, struct ifa *b) +{ return ipa_compare(a->ip, b->ip) < 0; } + +static inline struct ifa * +find_preferred_ifa(struct iface *i, ip_addr prefix, ip_addr mask) +{ + struct ifa *a, *b = NULL; + + WALK_LIST(a, i->addrs) + { + if (!(a->flags & IA_SECONDARY) && + ipa_equal(ipa_and(a->ip, mask), prefix) && + (!b || prefer_scope(a, b) || prefer_addr(a, b))) + b = a; + } + + return b; +} + +struct ifa * +kif_choose_primary(struct iface *i) +{ + struct kif_config *cf = (struct kif_config *) (kif_proto->p.cf); + struct kif_primary_item *it; + struct ifa *a; + + WALK_LIST(it, cf->primary) + { + if (!it->pattern || patmatch(it->pattern, i->name)) + if (a = find_preferred_ifa(i, it->prefix, ipa_mkmask(it->pxlen))) + return a; + } + + return find_preferred_ifa(i, IPA_NONE, IPA_NONE); +} + + static int kif_reconfigure(struct proto *p, struct proto_config *new) { @@ -159,6 +202,7 @@ kif_reconfigure(struct proto *p, struct proto_config *new) if (!kif_params_same(&o->iface, &n->iface)) return 0; + if (o->scan_time != n->scan_time) { tm_stop(kif_scan_timer); @@ -166,6 +210,18 @@ kif_reconfigure(struct proto *p, struct proto_config *new) kif_scan(kif_scan_timer); tm_start(kif_scan_timer, n->scan_time); } + + if (!EMPTY_LIST(o->primary) || !EMPTY_LIST(n->primary)) + { + /* This is hack, we have to update a configuration + * to the new value just now, because it is used + * for recalculation of primary addresses. + */ + p->cf = new; + + ifa_recalc_all_primary_addresses(); + } + return 1; } |