]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Implements primary address selection base on 'primary' option.
authorOndrej Zajicek <santiago@crfreenet.org>
Fri, 29 May 2009 20:49:30 +0000 (22:49 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Fri, 29 May 2009 20:49:30 +0000 (22:49 +0200)
conf/confbase.Y
nest/iface.c
nest/iface.h
sysdep/unix/krt.Y
sysdep/unix/krt.c

index 4385462353480ddf25499624b092b2889be55f67..a2df85dcf0f172e618356501b1cd02133f722757 100644 (file)
@@ -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
index 01f258105652a3cdccb7bb0196ef062929747845..5e88b21bf4187bbc904972d588307cfc5e5702d0 100644 (file)
@@ -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);
index f884dd907ecaae8881d3b6a7e42b7166448a99ee..af98a761dfdbf322da7b97135b23e1bbcd433947 100644 (file)
@@ -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 */
 
index 24f2dd278d19e8aaad1df5578f5c72b07d10fb46..40f1af9fd427949202f0fcaa8a5c58d9777b358d 100644 (file)
@@ -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;
index 6208f689d9989cb7b812d42749d25844dec8ba36..a60e7f917d02a35d4cada228c312e413256b2299 100644 (file)
@@ -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;
 }