summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2009-05-29 22:49:30 +0200
committerOndrej Zajicek <santiago@crfreenet.org>2009-05-29 22:49:30 +0200
commit874b868544c3a6ba45ace062091cc3aee007b719 (patch)
treedc9558c4e5e1537ea11469bb4be93bf941b9ee28
parent51f4469f03759642870a45634d9b53054e3deb92 (diff)
Implements primary address selection base on 'primary' option.
-rw-r--r--conf/confbase.Y6
-rw-r--r--nest/iface.c42
-rw-r--r--nest/iface.h1
-rw-r--r--sysdep/unix/krt.Y2
-rw-r--r--sysdep/unix/krt.c56
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;
}