From 8e48831a970a784a979446813191628790d477f1 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 17 Mar 2011 15:53:36 +0100 Subject: [PATCH] Vastly improved OSPF reconfiguration. Now it can handle a change in iface pattern structure. It can add, remove and reconfigure interfaces, vlinks and areas. --- proto/ospf/config.Y | 82 +++++-- proto/ospf/hello.c | 31 +-- proto/ospf/hello.h | 6 +- proto/ospf/iface.c | 523 +++++++++++++++++++++++++++++++++--------- proto/ospf/iface.h | 8 +- proto/ospf/lsalib.c | 12 + proto/ospf/lsalib.h | 2 + proto/ospf/neighbor.c | 12 +- proto/ospf/neighbor.h | 1 - proto/ospf/ospf.c | 489 ++++++++++++--------------------------- proto/ospf/ospf.h | 59 ++--- proto/ospf/topology.c | 3 +- 12 files changed, 695 insertions(+), 533 deletions(-) diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 321bd8d4..f3a87d66 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -22,8 +22,13 @@ static struct ospf_stubnet_config *this_stubnet; #ifdef OSPFv2 static void -finish_iface_config(struct ospf_iface_patt *ip) +ospf_iface_finish(void) { + struct ospf_iface_patt *ip = OSPF_PATT; + + if (ip->deadint == 0) + ip->deadint = ip->deadc * ip->helloint; + ip->passwords = get_passwords(); if ((ip->autype == OSPF_AUTH_CRYPT) && (ip->helloint < 5)) @@ -36,13 +41,57 @@ finish_iface_config(struct ospf_iface_patt *ip) #ifdef OSPFv3 static void -finish_iface_config(struct ospf_iface_patt *ip) +ospf_iface_finish(void) { + struct ospf_iface_patt *ip = OSPF_PATT; + + if (ip->deadint == 0) + ip->deadint = ip->deadc * ip->helloint; + if ((ip->autype != OSPF_AUTH_NONE) || (get_passwords() != NULL)) cf_error("Authentication not supported in OSPFv3"); } #endif +static void +ospf_area_finish(void) +{ + if ((this_area->areaid == 0) && (this_area->stub != 0)) + cf_error( "Backbone area cannot be stub"); +} + +static void +ospf_proto_finish(void) +{ + struct ospf_config *cf = OSPF_CFG; + + if (EMPTY_LIST(cf->area_list)) + cf_error( "No configured areas in OSPF"); + + int areano = 0; + int backbone = 0; + struct ospf_area_config *ac; + WALK_LIST(ac, cf->area_list) + { + areano++; + if (ac->areaid == 0) + backbone = 1; + } + cf->abr = areano > 1; + + if (cf->abr && !backbone) + { + struct ospf_area_config *ac = cfg_allocz(sizeof(struct ospf_area_config)); + add_head(&cf->area_list, NODE ac); + init_list(&ac->patt_list); + init_list(&ac->net_list); + init_list(&ac->stubnet_list); + } + + if (!cf->abr && !EMPTY_LIST(cf->vlink_list)) + cf_error( "No configured areas in OSPF"); +} + CF_DECLS CF_KEYWORDS(OSPF, AREA, OSPF_METRIC1, OSPF_METRIC2, OSPF_TAG, OSPF_ROUTER_ID) @@ -58,12 +107,13 @@ CF_KEYWORDS(WAIT, DELAY, LSADB, ECMP, LIMIT, WEIGHT) CF_GRAMMAR -CF_ADDTO(proto, ospf_proto '}') +CF_ADDTO(proto, ospf_proto '}' { ospf_proto_finish(); } ) ospf_proto_start: proto_start OSPF { this_proto = proto_config_new(&proto_ospf, sizeof(struct ospf_config)); this_proto->preference = DEF_PREF_OSPF; init_list(&OSPF_CFG->area_list); + init_list(&OSPF_CFG->vlink_list); OSPF_CFG->rfc1583 = DEFAULT_RFC1583; OSPF_CFG->tick = DEFAULT_OSPFTICK; } @@ -80,22 +130,21 @@ ospf_proto_item: | ECMP bool { OSPF_CFG->ecmp = $2 ? DEFAULT_ECMP_LIMIT : 0; } | ECMP bool LIMIT expr { OSPF_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); } | TICK expr { OSPF_CFG->tick = $2; if($2<=0) cf_error("Tick must be greater than zero"); } - | ospf_area '}' + | ospf_area ; -ospf_area_start: AREA idval '{' { +ospf_area_start: AREA idval { this_area = cfg_allocz(sizeof(struct ospf_area_config)); add_tail(&OSPF_CFG->area_list, NODE this_area); this_area->areaid = $2; this_area->stub = 0; init_list(&this_area->patt_list); - init_list(&this_area->vlink_list); init_list(&this_area->net_list); init_list(&this_area->stubnet_list); } ; -ospf_area: ospf_area_start ospf_area_opts +ospf_area: ospf_area_start '{' ospf_area_opts '}' { ospf_area_finish(); } ; ospf_area_opts: @@ -138,7 +187,7 @@ ospf_stubnet_item: ; ospf_vlink: - ospf_vlink_start '{' ospf_vlink_opts '}' { finish_iface_config(OSPF_PATT); } + ospf_vlink_start '{' ospf_vlink_opts '}' { ospf_iface_finish(); } | ospf_vlink_start ; @@ -152,7 +201,7 @@ ospf_vlink_item: | RETRANSMIT expr { OSPF_PATT->rxmtint = $2 ; if ($2<=0) cf_error("Retransmit int must be greater than zero"); } | TRANSMIT DELAY expr { OSPF_PATT->inftransdelay = $3 ; if (($3<=0) || ($3>65535)) cf_error("Transmit delay must be in range 1-65535"); } | WAIT expr { OSPF_PATT->waitint = $2 ; } - | DEAD expr { OSPF_PATT->dead = $2 ; if ($2<=1) cf_error("Dead interval must be greater than one"); } + | DEAD expr { OSPF_PATT->deadint = $2 ; if ($2<=1) cf_error("Dead interval must be greater than one"); } | DEAD COUNT expr { OSPF_PATT->deadc = $3 ; if ($3<=1) cf_error("Dead count must be greater than one"); } | AUTHENTICATION NONE { OSPF_PATT->autype = OSPF_AUTH_NONE ; } | AUTHENTICATION SIMPLE { OSPF_PATT->autype = OSPF_AUTH_SIMPLE ; } @@ -164,15 +213,16 @@ ospf_vlink_start: VIRTUAL LINK idval { if (this_area->areaid == 0) cf_error("Virtual link cannot be in backbone"); this_ipatt = cfg_allocz(sizeof(struct ospf_iface_patt)); - add_tail(&this_area->vlink_list, NODE this_ipatt); + add_tail(&OSPF_CFG->vlink_list, NODE this_ipatt); init_list(&this_ipatt->ipn_list); + OSPF_PATT->voa = this_area->areaid; OSPF_PATT->vid = $3; OSPF_PATT->helloint = HELLOINT_D; OSPF_PATT->rxmtint = RXMTINT_D; OSPF_PATT->inftransdelay = INFTRANSDELAY_D; OSPF_PATT->waitint = WAIT_DMH*HELLOINT_D; OSPF_PATT->deadc = DEADC_D; - OSPF_PATT->dead = 0; + OSPF_PATT->deadint = 0; OSPF_PATT->type = OSPF_IT_VLINK; init_list(&OSPF_PATT->nbma_list); OSPF_PATT->autype = OSPF_AUTH_NONE; @@ -185,10 +235,8 @@ ospf_iface_item: | HELLO expr { OSPF_PATT->helloint = $2 ; if (($2<=0) || ($2>65535)) cf_error("Hello interval must be in range 1-65535"); } | POLL expr { OSPF_PATT->pollint = $2 ; if ($2<=0) cf_error("Poll int must be greater than zero"); } | RETRANSMIT expr { OSPF_PATT->rxmtint = $2 ; if ($2<=0) cf_error("Retransmit int must be greater than zero"); } - | TRANSMIT DELAY expr { OSPF_PATT->inftransdelay = $3 ; if (($3<=0) || ($3>65535)) cf_error("Transmit delay must be in range 1-65535"); } - | PRIORITY expr { OSPF_PATT->priority = $2 ; if (($2<0) || ($2>255)) cf_error("Priority must be in range 0-255"); } | WAIT expr { OSPF_PATT->waitint = $2 ; } - | DEAD expr { OSPF_PATT->dead = $2 ; if ($2<=1) cf_error("Dead interval must be greater than one"); } + | DEAD expr { OSPF_PATT->deadint = $2 ; if ($2<=1) cf_error("Dead interval must be greater than one"); } | DEAD COUNT expr { OSPF_PATT->deadc = $3 ; if ($3<=1) cf_error("Dead count must be greater than one"); } | TYPE BROADCAST { OSPF_PATT->type = OSPF_IT_BCAST ; } | TYPE BCAST { OSPF_PATT->type = OSPF_IT_BCAST ; } @@ -198,6 +246,8 @@ ospf_iface_item: | TYPE PTP { OSPF_PATT->type = OSPF_IT_PTP ; } | TYPE POINTOMULTIPOINT { OSPF_PATT->type = OSPF_IT_PTMP ; } | TYPE PTMP { OSPF_PATT->type = OSPF_IT_PTMP ; } + | TRANSMIT DELAY expr { OSPF_PATT->inftransdelay = $3 ; if (($3<=0) || ($3>65535)) cf_error("Transmit delay must be in range 1-65535"); } + | PRIORITY expr { OSPF_PATT->priority = $2 ; if (($2<0) || ($2>255)) cf_error("Priority must be in range 0-255"); } | STRICT NONBROADCAST bool { OSPF_PATT->strictnbma = $3 ; } | STUB bool { OSPF_PATT->stub = $2 ; } | CHECK LINK bool { OSPF_PATT->check_link = $3; } @@ -281,7 +331,7 @@ ospf_iface_start: OSPF_PATT->priority = PRIORITY_D; OSPF_PATT->waitint = WAIT_DMH*HELLOINT_D; OSPF_PATT->deadc = DEADC_D; - OSPF_PATT->dead = 0; + OSPF_PATT->deadint = 0; OSPF_PATT->type = OSPF_IT_UNDEF; init_list(&OSPF_PATT->nbma_list); OSPF_PATT->autype = OSPF_AUTH_NONE; @@ -300,7 +350,7 @@ ospf_iface_opt_list: ; ospf_iface: - ospf_iface_start iface_patt_list ospf_iface_opt_list { finish_iface_config(OSPF_PATT); } + ospf_iface_start iface_patt_list ospf_iface_opt_list { ospf_iface_finish(); } ; opttext: diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index 47b84a4d..76c95c4f 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -88,7 +88,7 @@ ospf_hello_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, #else /* OSPFv3 */ tmp = ntohs(ps->deadint); #endif - if (tmp != ifa->dead) + if (tmp != ifa->deadint) { log(L_ERR "%s%I - dead interval mismatch (%d)", beg, faddr, tmp); return; @@ -217,13 +217,13 @@ ospf_hello_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, if (ifa->type == OSPF_IT_NBMA) { if ((ifa->priority == 0) && (n->priority > 0)) - ospf_hello_send(NULL, 0, n); + ospf_hello_send(NULL, OHS_HELLO, n); } ospf_neigh_sm(n, INM_HELLOREC); } void -ospf_hello_send(timer *timer, int poll, struct ospf_neighbor *dirn) +ospf_hello_send(timer *timer, int kind, struct ospf_neighbor *dirn) { struct ospf_iface *ifa; struct ospf_hello_packet *pkt; @@ -231,7 +231,6 @@ ospf_hello_send(timer *timer, int poll, struct ospf_neighbor *dirn) struct proto *p; struct ospf_neighbor *neigh, *n1; u16 length; - u32 *pp; int i; struct nbma_node *nb; @@ -276,27 +275,31 @@ ospf_hello_send(timer *timer, int poll, struct ospf_neighbor *dirn) pkt->options = ifa->oa->options; #ifdef OSPFv2 - pkt->deadint = htonl(ifa->dead); + pkt->deadint = htonl(ifa->deadint); pkt->dr = htonl(ipa_to_u32(ifa->drip)); pkt->bdr = htonl(ipa_to_u32(ifa->bdrip)); #else /* OSPFv3 */ - pkt->deadint = htons(ifa->dead); + pkt->deadint = htons(ifa->deadint); pkt->dr = htonl(ifa->drid); pkt->bdr = htonl(ifa->bdrid); #endif /* Fill all neighbors */ i = 0; - pp = (u32 *) (((u8 *) pkt) + sizeof(struct ospf_hello_packet)); - WALK_LIST(neigh, ifa->neigh_list) + + if (kind != OHS_SHUTDOWN) { - if ((i+1) * sizeof(u32) + sizeof(struct ospf_hello_packet) > ospf_pkt_bufsize(ifa)) + u32 *pp = (u32 *) (((u8 *) pkt) + sizeof(struct ospf_hello_packet)); + WALK_LIST(neigh, ifa->neigh_list) { - OSPF_TRACE(D_PACKETS, "Too many neighbors on the interface!"); - break; + if ((i+1) * sizeof(u32) + sizeof(struct ospf_hello_packet) > ospf_pkt_bufsize(ifa)) + { + log(L_WARN "%s: Too many neighbors on interface %s", p->name, ifa->iface->name); + break; + } + *(pp + i) = htonl(neigh->rid); + i++; } - *(pp + i) = htonl(neigh->rid); - i++; } length = sizeof(struct ospf_hello_packet) + i * sizeof(u32); @@ -319,7 +322,7 @@ ospf_hello_send(timer *timer, int poll, struct ospf_neighbor *dirn) int to_all = ifa->state > OSPF_IS_DROTHER; int me_elig = ifa->priority > 0; - if (poll) /* Poll timer */ + if (kind == OHS_POLL) /* Poll timer */ { WALK_LIST(nb, ifa->nbma_list) if (!nb->found && (to_all || (me_elig && nb->eligible))) diff --git a/proto/ospf/hello.h b/proto/ospf/hello.h index 5d73c09e..3675c052 100644 --- a/proto/ospf/hello.h +++ b/proto/ospf/hello.h @@ -12,6 +12,10 @@ void ospf_hello_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, struct ospf_neighbor *n, ip_addr faddr); -void ospf_hello_send(timer *timer, int poll, struct ospf_neighbor *dirn); +void ospf_hello_send(timer *timer, int kind, struct ospf_neighbor *dirn); + +#define OHS_HELLO 0 +#define OHS_POLL 1 +#define OHS_SHUTDOWN 2 #endif /* _BIRD_OSPF_HELLO_H_ */ diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index e7c9e72d..faee634a 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -21,13 +21,13 @@ char *ospf_it[] = { "broadcast", "nbma", "ptp", "ptmp", "virtual link" }; static void poll_timer_hook(timer * timer) { - ospf_hello_send(timer, 1, NULL); + ospf_hello_send(timer, OHS_POLL, NULL); } static void hello_timer_hook(timer * timer) { - ospf_hello_send(timer, 0, NULL); + ospf_hello_send(timer, OHS_HELLO, NULL); } static void @@ -40,6 +40,8 @@ wait_timer_hook(timer * timer) ospf_iface_sm(ifa, ISM_WAITF); } +static void ospf_iface_change_mtu(struct proto_ospf *po, struct ospf_iface *ifa); + u32 rxbufsize(struct ospf_iface *ifa) { @@ -163,7 +165,13 @@ ospf_iface_down(struct ospf_iface *ifa) if (ifa->type != OSPF_IT_VLINK) { - OSPF_TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name); +#ifdef OSPFv2 + OSPF_TRACE(D_EVENTS, "Removing interface %s (%I/%d) from area %R", + ifa->iface->name, ifa->addr->prefix, ifa->addr->pxlen, ifa->oa->areaid); +#else + OSPF_TRACE(D_EVENTS, "Removing interface %s (IID %d) from area %R", + ifa->iface->name, ifa->instance_id, ifa->oa->areaid); +#endif /* First of all kill all the related vlinks */ WALK_LIST(iff, po->iface_list) @@ -207,14 +215,25 @@ ospf_iface_down(struct ospf_iface *ifa) } -static void +void ospf_iface_remove(struct ospf_iface *ifa) { + struct proto *p = &ifa->oa->po->proto; + if (ifa->type == OSPF_IT_VLINK) + OSPF_TRACE(D_EVENTS, "Removing vlink to %R via area %R", ifa->vid, ifa->voa->areaid); + ospf_iface_sm(ifa, ISM_DOWN); rem_node(NODE ifa); rfree(ifa->pool); } +void +ospf_iface_shutdown(struct ospf_iface *ifa) +{ + if (ifa->state > OSPF_IS_DOWN) + ospf_hello_send(ifa->hello_timer, OHS_SHUTDOWN, NULL); +} + /** * ospf_iface_chstate - handle changes of interface state * @ifa: OSPF interface @@ -357,7 +376,7 @@ ospf_iface_sm(struct ospf_iface *ifa, int event) } static u8 -ospf_iface_classify(struct iface *ifa, struct ifa *addr) +ospf_iface_classify_int(struct iface *ifa, struct ifa *addr) { if (ipa_nonzero(addr->opposite)) return (ifa->flags & IF_MULTICAST) ? OSPF_IT_PTP : OSPF_IT_PTMP; @@ -372,6 +391,13 @@ ospf_iface_classify(struct iface *ifa, struct ifa *addr) return OSPF_IT_PTP; } +static inline u8 +ospf_iface_classify(u8 type, struct ifa *addr) +{ + return (type != OSPF_IT_UNDEF) ? type : ospf_iface_classify_int(addr->iface, addr); +} + + struct ospf_iface * ospf_iface_find(struct proto_ospf *p, struct iface *what) { @@ -401,23 +427,69 @@ ospf_iface_add(struct object_lock *lock) ospf_iface_sm(ifa, (ifa->check_link && !(ifa->iface->flags & IF_LINK_UP)) ? ISM_LOOP : ISM_UP); } -void -ospf_iface_new(struct proto_ospf *po, struct iface *iface, struct ifa *addr, - struct ospf_area_config *ac, struct ospf_iface_patt *ip) +static inline void +add_nbma_node(struct ospf_iface *ifa, struct nbma_node *src, int found) { - struct proto *p = &po->proto; - struct pool *pool = rp_new(p->pool, "OSPF Interface"); + struct nbma_node *n = mb_alloc(ifa->pool, sizeof(struct nbma_node)); + add_tail(&ifa->nbma_list, NODE n); + n->ip = src->ip; + n->eligible = src->eligible; + n->found = found; +} + +static int +ospf_iface_stubby(struct ospf_iface_patt *ip, struct ifa *addr) +{ + if (! addr) + return 0; + + /* + * We cannot properly support multiple OSPF ifaces on real iface + * with multiple prefixes, therefore we force OSPF ifaces with + * non-primary IP prefixes to be stub. + */ +#if defined(OSPFv2) && !defined(CONFIG_MC_PROPER_SRC) + if (! (addr->flags & IA_PRIMARY)) + return 1; +#endif + + /* a loopback/dummy address */ + if ((addr->pxlen == MAX_PREFIX_LENGTH) && ipa_zero(addr->opposite)) + return 1; + + return ip->stub; +} + +void +ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *ip) +{ + struct proto *p = &oa->po->proto; + struct iface *iface = addr ? addr->iface : NULL; + struct pool *pool; + struct ospf_iface *ifa; - struct nbma_node *nbma, *nb; + struct nbma_node *nb; struct object_lock *lock; - struct ospf_area *oa; - if (ip->type != OSPF_IT_VLINK) - OSPF_TRACE(D_EVENTS, "Adding interface %s", iface->name); + if (ip->type == OSPF_IT_VLINK) + OSPF_TRACE(D_EVENTS, "Adding vlink to %R via area %R", ip->vid, ip->voa); + else + { +#ifdef OSPFv2 + OSPF_TRACE(D_EVENTS, "Adding interface %s (%I/%d) to area %R", + iface->name, addr->prefix, addr->pxlen, oa->areaid); +#else + OSPF_TRACE(D_EVENTS, "Adding interface %s (IID %d) to area %R", + iface->name, ip->instance_id, oa->areaid); +#endif + } + pool = rp_new(p->pool, "OSPF Interface"); ifa = mb_allocz(pool, sizeof(struct ospf_iface)); ifa->iface = iface; ifa->addr = addr; + ifa->oa = oa; + ifa->cf = ip; ifa->pool = pool; ifa->cost = ip->cost; @@ -428,7 +500,7 @@ ospf_iface_new(struct proto_ospf *po, struct iface *iface, struct ifa *addr, ifa->pollint = ip->pollint; ifa->strictnbma = ip->strictnbma; ifa->waitint = ip->waitint; - ifa->dead = (ip->dead == 0) ? ip->deadc * ifa->helloint : ip->dead; + ifa->deadint = ip->deadint; ifa->stub = ospf_iface_stubby(ip, addr); ifa->ioprob = OSPF_I_OK; ifa->rxbuf = ip->rxbuf; @@ -444,14 +516,7 @@ ospf_iface_new(struct proto_ospf *po, struct iface *iface, struct ifa *addr, ifa->instance_id = ip->instance_id; #endif - if (ip->type == OSPF_IT_UNDEF) - ifa->type = ospf_iface_classify(iface, addr); - else - ifa->type = ip->type; - - /* a loopback/dummy address */ - if ((addr->pxlen == MAX_PREFIX_LENGTH) && ipa_zero(addr->opposite)) - ifa->stub = 1; + ifa->type = ospf_iface_classify(ip->type, addr); /* Check validity of interface type */ int old_type = ifa->type; @@ -479,16 +544,8 @@ ospf_iface_new(struct proto_ospf *po, struct iface *iface, struct ifa *addr, init_list(&ifa->nbma_list); WALK_LIST(nb, ip->nbma_list) - { - if (!ipa_in_net(nb->ip, addr->prefix, addr->pxlen)) - continue; - - nbma = mb_alloc(pool, sizeof(struct nbma_node)); - nbma->ip = nb->ip; - nbma->eligible = nb->eligible; - nbma->found = 0; - add_tail(&ifa->nbma_list, NODE nbma); - } + if (ipa_in_net(nb->ip, addr->prefix, addr->pxlen)) + add_nbma_node(ifa, nb, 0); DBG("%s: Installing hello timer. (%u)\n", p->name, ifa->helloint); ifa->hello_timer = tm_new(pool); @@ -518,26 +575,11 @@ ospf_iface_new(struct proto_ospf *po, struct iface *iface, struct ifa *addr, } ifa->state = OSPF_IS_DOWN; - add_tail(&po->iface_list, NODE ifa); - - ifa->oa = NULL; - WALK_LIST(oa, po->area_list) - { - if (oa->areaid == ac->areaid) - { - ifa->oa = oa; - break; - } - } - - if (!ifa->oa) - bug("Cannot add any area to accepted Interface"); - else + add_tail(&oa->po->iface_list, NODE ifa); if (ifa->type == OSPF_IT_VLINK) { - ifa->oa = po->backbone; - ifa->voa = oa; + ifa->voa = ospf_find_area(oa->po, ip->voa); ifa->vid = ip->vid; return; /* Don't lock, don't add sockets */ } @@ -564,14 +606,209 @@ ospf_iface_new(struct proto_ospf *po, struct iface *iface, struct ifa *addr, olock_acquire(lock); } +int +ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) +{ + struct proto *p = &ifa->oa->po->proto; + struct nbma_node *nb, *nbx; + char *ifname = (ifa->type != OSPF_IT_VLINK) ? ifa->iface->name : "vlink"; + + /* Type could be changed in ospf_iface_new(), + but if config values are same then also results are same */ + int old_type = ospf_iface_classify(ifa->cf->type, ifa->addr); + int new_type = ospf_iface_classify(new->type, ifa->addr); + if (old_type != new_type) + return 0; + + int new_stub = ospf_iface_stubby(new, ifa->addr); + if (ifa->stub != new_stub) + return 0; + + ifa->cf = new; + ifa->marked = 0; + + + /* HELLO TIMER */ + if (ifa->helloint != new->helloint) + { + OSPF_TRACE(D_EVENTS, "Changing hello interval on interface %s from %d to %d", + ifname, ifa->helloint, new->helloint); + + ifa->helloint = new->helloint; + ifa->hello_timer->recurrent = ifa->helloint; + tm_start(ifa->hello_timer, ifa->helloint); + } + + /* RXMT TIMER */ + if (ifa->rxmtint != new->rxmtint) + { + OSPF_TRACE(D_EVENTS, "Changing retransmit interval on interface %s from %d to %d", + ifname, ifa->rxmtint, new->rxmtint); + + ifa->rxmtint = new->rxmtint; + } + + /* POLL TIMER */ + if (ifa->pollint != new->pollint) + { + OSPF_TRACE(D_EVENTS, "Changing poll interval on interface %s from %d to %d", + ifname, ifa->pollint, new->pollint); + + ifa->pollint = new->helloint; + ifa->poll_timer->recurrent = ifa->pollint; + tm_start(ifa->poll_timer, ifa->pollint); + } + + /* WAIT TIMER */ + if (ifa->waitint != new->waitint) + { + OSPF_TRACE(D_EVENTS, "Changing wait interval on interface %s from %d to %d", + ifname, ifa->waitint, new->waitint); + + ifa->waitint = new->waitint; + if (ifa->wait_timer->expires != 0) + tm_start(ifa->wait_timer, ifa->waitint); + } + + /* DEAD TIMER */ + if (ifa->deadint != new->deadint) + { + OSPF_TRACE(D_EVENTS, "Changing dead interval on interface %s from %d to %d", + ifname, ifa->deadint, new->deadint); + ifa->deadint = new->deadint; + } + + /* INFTRANS */ + if (ifa->inftransdelay != new->inftransdelay) + { + OSPF_TRACE(D_EVENTS, "Changing transmit delay on interface %s from %d to %d", + ifname, ifa->inftransdelay, new->inftransdelay); + ifa->inftransdelay = new->inftransdelay; + } + +#ifdef OSPFv2 + /* AUTHENTICATION */ + if (ifa->autype != new->autype) + { + OSPF_TRACE(D_EVENTS, "Changing authentication type on interface %s", ifname); + ifa->autype = new->autype; + } + + /* Update passwords */ + ifa->passwords = new->passwords; +#endif + + /* Remaining options are just for proper interfaces */ + if (ifa->type == OSPF_IT_VLINK) + return 1; + + + /* COST */ + if (ifa->cost != new->cost) + { + OSPF_TRACE(D_EVENTS, "Changing cost on interface %s from %d to %d", + ifname, ifa->cost, new->cost); + + ifa->cost = new->cost; + } + + /* PRIORITY */ + if (ifa->priority != new->priority) + { + OSPF_TRACE(D_EVENTS, "Changing priority on interface %s from %d to %d", + ifname, ifa->priority, new->priority); + ifa->priority = new->priority; + } + + /* STRICT NBMA */ + if (ifa->strictnbma != new->strictnbma) + { + OSPF_TRACE(D_EVENTS, "Changing NBMA strictness on interface %s", ifname); + ifa->strictnbma = new->strictnbma; + } + + /* NBMA LIST - remove or update old */ + WALK_LIST_DELSAFE(nb, nbx, ifa->nbma_list) + { + struct nbma_node *nb2 = find_nbma_node_in(&new->nbma_list, nb->ip); + if (nb2) + { + if (nb->eligible != nb2->eligible) + { + OSPF_TRACE(D_EVENTS, "Changing eligibility of neighbor %I on interface %s", + nb->ip, ifname); + nb->eligible = nb2->eligible; + } + } + else + { + OSPF_TRACE(D_EVENTS, "Removing NBMA neighbor %I on interface %s", + nb->ip, ifname); + rem_node(NODE nb); + mb_free(nb); + } + } + + /* NBMA LIST - add new */ + WALK_LIST(nb, new->nbma_list) + { + if (!ipa_in_net(nb->ip, ifa->addr->prefix, ifa->addr->pxlen)) + continue; + + if (! find_nbma_node(ifa, nb->ip)) + { + OSPF_TRACE(D_EVENTS, "Adding NBMA neighbor %I on interface %s", + nb->ip, ifname); + add_nbma_node(ifa, nb, !!find_neigh_by_ip(ifa, nb->ip)); + } + } + + /* RX BUFF */ + if (ifa->rxbuf != new->rxbuf) + { + OSPF_TRACE(D_EVENTS, "Changing rxbuf interface %s from %d to %d", + ifname, ifa->rxbuf, new->rxbuf); + ifa->rxbuf = new->rxbuf; + ospf_iface_change_mtu(ifa->oa->po, ifa); + } + + /* LINK */ + if (ifa->check_link != new->check_link) + { + OSPF_TRACE(D_EVENTS, "%s link check on interface %s", + new->check_link ? "Enabling" : "Disabling", ifname); + ifa->check_link = new->check_link; + + if (!(ifa->iface->flags & IF_LINK_UP)) + ospf_iface_sm(ifa, ifa->check_link ? ISM_LOOP : ISM_UNLOOP); + } + + /* ECMP weight */ + if (ifa->ecmp_weight != new->ecmp_weight) + { + OSPF_TRACE(D_EVENTS, "Changing ECMP weight of interface %s from %d to %d", + ifname, (int)ifa->ecmp_weight + 1, (int)new->ecmp_weight + 1); + ifa->ecmp_weight = new->ecmp_weight; + } + + /* instance_id is not updated - it is part of key */ + + return 1; +} + #ifdef OSPFv2 +static inline struct ospf_iface_patt * +ospf_iface_patt_find(struct ospf_area_config *ac, struct ifa *a) +{ + return (struct ospf_iface_patt *) iface_patt_find(&ac->patt_list, a->iface, a); +} + void ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) { struct proto_ospf *po = (struct proto_ospf *) p; - struct ospf_config *cf = (struct ospf_config *) (p->cf); if (a->flags & IA_SECONDARY) return; @@ -583,16 +820,14 @@ ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) if (flags & IF_CHANGE_UP) { int done = 0; - struct ospf_area_config *ac; - WALK_LIST(ac, cf->area_list) + struct ospf_area *oa; + WALK_LIST(oa, po->area_list) { - struct ospf_iface_patt *ip = (struct ospf_iface_patt *) - iface_patt_find(&ac->patt_list, a->iface, a); - - if (ip) + struct ospf_iface_patt *ip; + if (ip = ospf_iface_patt_find(oa->ac, a)) { if (!done) - ospf_iface_new(po, a->iface, a, ac, ip); + ospf_iface_new(oa, a, ip); done++; } } @@ -613,23 +848,72 @@ ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) } } -#else /* OSPFv3 */ - -static inline int iflag_test(u32 *a, u8 i) +static struct ospf_iface * +ospf_iface_find_by_key(struct ospf_area *oa, struct ifa *a) { - return a[i / 32] & (1u << (i % 32)); + struct ospf_iface *ifa; + WALK_LIST(ifa, oa->po->iface_list) + if ((ifa->addr == a) && (ifa->oa == oa) && (ifa->type != OSPF_IT_VLINK)) + return ifa; + + return NULL; } -static inline void iflag_set(u32 *a, u8 i) +void +ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) { - a[i / 32] |= (1u << (i % 32)); + struct ospf_iface_patt *ip; + struct iface *iface; + struct ifa *a; + + WALK_LIST(iface, iface_list) + WALK_LIST(a, iface->addrs) + { + if (a->flags & IA_SECONDARY) + continue; + + if (a->scope <= SCOPE_LINK) + continue; + + if (ip = ospf_iface_patt_find(oa->ac, a)) + { + /* Main inner loop */ + struct ospf_iface *ifa = ospf_iface_find_by_key(oa, a); + if (ifa) + { + if (ospf_iface_reconfigure(ifa, ip)) + continue; + + /* Hard restart */ + ospf_iface_shutdown(ifa); + ospf_iface_remove(ifa); + } + + ospf_iface_new(oa, a, ip); + } + } +} + + +#else /* OSPFv3 */ + +struct ospf_iface_patt * +ospf_iface_patt_find(struct ospf_area_config *ac, struct iface *iface, int iid) +{ + struct ospf_iface_patt *pt, *res = NULL; + + WALK_LIST(pt, ac->patt_list) + if ((pt->instance_id >= iid) && (iface_patt_match(&pt->i, iface, NULL)) && + (!res || (pt->instance_id < res->instance_id))) + res = pt; + + return res; } void ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) { struct proto_ospf *po = (struct proto_ospf *) p; - struct ospf_config *cf = (struct ospf_config *) (p->cf); if (a->flags & IA_SECONDARY) return; @@ -643,40 +927,26 @@ ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) { if (flags & IF_CHANGE_UP) { - u32 found_all[8] = {}; - struct ospf_area_config *ac; + int done0 = 0; + struct ospf_area *oa; - WALK_LIST(ac, cf->area_list) + WALK_LIST(oa, po->area_list) { - u32 found_new[8] = {}; - struct iface_patt *pt; + int iid = 0; - WALK_LIST(pt, ac->patt_list) + struct ospf_iface_patt *ip; + while (ip = ospf_iface_patt_find(oa->ac, a->iface, iid)) { - if (iface_patt_match(pt, a->iface, a)) - { - struct ospf_iface_patt *ipt = (struct ospf_iface_patt *) pt; - - /* If true, we already assigned that IID and we skip - this to implement first-match behavior */ - if (iflag_test(found_new, ipt->instance_id)) - continue; - - /* If true, we already assigned that in a different area, - we log collision */ - if (iflag_test(found_all, ipt->instance_id)) - { - log(L_WARN "%s: Interface %s (IID %d) matches for multiple areas", - p->name, a->iface->name, ipt->instance_id); - continue; - } - - iflag_set(found_all, ipt->instance_id); - iflag_set(found_new, ipt->instance_id); - ospf_iface_new(po, a->iface, a, ac, ipt); - } + ospf_iface_new(oa, a, ip); + if (ip->instance_id == 0) + done0++; + iid = ip->instance_id + 1; } } + + if (done0 > 1) + log(L_WARN "%s: Interface %s matches for multiple areas", + p->name, a->iface->name); } if (flags & IF_CHANGE_DOWN) @@ -706,15 +976,64 @@ ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) } } -#endif +static struct ospf_iface * +ospf_iface_find_by_key(struct ospf_area *oa, struct ifa *a, int iid) +{ + struct ospf_iface *ifa; + WALK_LIST(ifa, oa->po->iface_list) + if ((ifa->addr == a) && (ifa->oa == oa) && (ifa->instance_id == iid) && (ifa->type != OSPF_IT_VLINK)) + return ifa; + + return NULL; +} void +ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) +{ + struct ospf_iface_patt *ip; + struct iface *iface; + struct ifa *a; + + WALK_LIST(iface, iface_list) + WALK_LIST(a, iface->addrs) + { + if (a->flags & IA_SECONDARY) + continue; + + if (a->scope != SCOPE_LINK) + continue; + + int iid = 0; + while (ip = ospf_iface_patt_find(nac, iface, iid)) + { + iid = ip->instance_id + 1; + + /* Main inner loop */ + struct ospf_iface *ifa = ospf_iface_find_by_key(oa, a, ip->instance_id); + if (ifa) + { + if (ospf_iface_reconfigure(ifa, ip)) + continue; + + /* Hard restart */ + ospf_iface_shutdown(ifa); + ospf_iface_remove(ifa); + } + + ospf_iface_new(oa, a, ip); + } + } +} + +#endif + +static void ospf_iface_change_mtu(struct proto_ospf *po, struct ospf_iface *ifa) { struct proto *p = &po->proto; struct ospf_packet *op; struct ospf_neighbor *n; - OSPF_TRACE(D_EVENTS, "Changing MTU on interface %s.", ifa->iface->name); + OSPF_TRACE(D_EVENTS, "Changing MTU on interface %s", ifa->iface->name); if (ifa->sk) { @@ -755,11 +1074,13 @@ void ospf_if_notify(struct proto *p, unsigned flags, struct iface *iface) { struct proto_ospf *po = (struct proto_ospf *) p; - + + /* if (iface->flags & IF_IGNORE) return; + */ - /* Going up means that there are no such ifaces yet */ + /* Going up means that there are no such ifaces yet */ if (flags & IF_CHANGE_UP) return; @@ -817,7 +1138,7 @@ ospf_iface_info(struct ospf_iface *ifa) cli_msg(-1015, "\tPoll timer: %u", ifa->pollint); } cli_msg(-1015, "\tWait timer: %u", ifa->waitint); - cli_msg(-1015, "\tDead timer: %u", ifa->dead); + cli_msg(-1015, "\tDead timer: %u", ifa->deadint); cli_msg(-1015, "\tRetransmit timer: %u", ifa->rxmtint); if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA)) { @@ -828,9 +1149,3 @@ ospf_iface_info(struct ospf_iface *ifa) } } -void -ospf_iface_shutdown(struct ospf_iface *ifa) -{ - init_list(&ifa->neigh_list); - hello_timer_hook(ifa->hello_timer); -} diff --git a/proto/ospf/iface.h b/proto/ospf/iface.h index f9631eb3..3f887728 100644 --- a/proto/ospf/iface.h +++ b/proto/ospf/iface.h @@ -16,10 +16,11 @@ struct ospf_iface *ospf_iface_find(struct proto_ospf *p, struct iface *what); void ospf_if_notify(struct proto *p, unsigned flags, struct iface *iface); void ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a); void ospf_iface_info(struct ospf_iface *ifa); +void ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *ip); +void ospf_iface_remove(struct ospf_iface *ifa); void ospf_iface_shutdown(struct ospf_iface *ifa); -void ospf_iface_new(struct proto_ospf *po, struct iface *iface, struct ifa *addr, struct ospf_area_config *ac, struct ospf_iface_patt *ip); -void ospf_iface_change_mtu(struct proto_ospf *po, struct ospf_iface *ifa); -void ospf_set_rxbuf_size(struct ospf_iface *ifa, u32 rxbuf); +int ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new); +void ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac); struct nbma_node *find_nbma_node_in(list *nnl, ip_addr ip); @@ -27,5 +28,4 @@ static inline struct nbma_node * find_nbma_node(struct ospf_iface *ifa, ip_addr ip) { return find_nbma_node_in(&ifa->nbma_list, ip); } - #endif /* _BIRD_OSPF_IFACE_H_ */ diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c index 2f52fe9a..224c345e 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -23,6 +23,18 @@ flush_lsa(struct top_hash_entry *en, struct proto_ospf *po) ospf_hash_delete(po->gr, en); } +void +ospf_flush_area(struct proto_ospf *po, u32 areaid) +{ + struct top_hash_entry *en, *nxt; + + WALK_SLIST_DELSAFE(en, nxt, po->lsal) + { + if ((LSA_SCOPE(&en->lsa) == LSA_SCOPE_AREA) && (en->domain == areaid)) + flush_lsa(en, po); + } +} + /** * ospf_age * @po: ospf protocol diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h index 8a949d86..0b556ec5 100644 --- a/proto/ospf/lsalib.h +++ b/proto/ospf/lsalib.h @@ -36,5 +36,7 @@ int lsa_validate(struct ospf_lsa_header *lsa, void *body); struct top_hash_entry * lsa_install_new(struct proto_ospf *po, struct ospf_lsa_header *lsa, u32 domain, void *body); void ospf_age(struct proto_ospf *po); void flush_lsa(struct top_hash_entry *en, struct proto_ospf *po); +void ospf_flush_area(struct proto_ospf *po, u32 areaid); + #endif /* _BIRD_OSPF_LSALIB_H_ */ diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index 98c9de50..642365b3 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -349,7 +349,7 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) case NEIGHBOR_DOWN: neigh_chstate(n, NEIGHBOR_INIT); default: - tm_start(n->inactim, n->ifa->dead); /* Restart inactivity timer */ + tm_start(n->inactim, n->ifa->deadint); /* Restart inactivity timer */ break; } break; @@ -548,16 +548,6 @@ find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip) return NULL; } -struct ospf_area * -ospf_find_area(struct proto_ospf *po, u32 aid) -{ - struct ospf_area *oa; - WALK_LIST(oa, po->area_list) - if (((struct ospf_area *) oa)->areaid == aid) - return oa; - return NULL; -} - /* Neighbor is inactive for a long time. Remove it. */ static void neighbor_timer_hook(timer * timer) diff --git a/proto/ospf/neighbor.h b/proto/ospf/neighbor.h index dcaa12df..f593faed 100644 --- a/proto/ospf/neighbor.h +++ b/proto/ospf/neighbor.h @@ -15,7 +15,6 @@ void ospf_neigh_sm(struct ospf_neighbor *n, int event); void bdr_election(struct ospf_iface *ifa); struct ospf_neighbor *find_neigh(struct ospf_iface *ifa, u32 rid); struct ospf_neighbor *find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip); -struct ospf_area *ospf_find_area(struct proto_ospf *po, u32 aid); void ospf_neigh_remove(struct ospf_neighbor *n); void ospf_sh_neigh_info(struct ospf_neighbor *n); diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index e10a405c..ce913f7b 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -122,27 +122,95 @@ static void add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac) { struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; - struct area_net_config *anet; - struct area_net *antmp; + struct area_net_config *anc; + struct area_net *an; - fib_init(&oa->net_fib, p->pool, sizeof(struct area_net), 0, ospf_area_initfib); + fib_init(&oa->net_fib, po->proto.pool, sizeof(struct area_net), 0, ospf_area_initfib); - WALK_LIST(anet, ac->net_list) + WALK_LIST(anc, ac->net_list) { - antmp = (struct area_net *) fib_get(&oa->net_fib, &anet->px.addr, anet->px.len); - antmp->hidden = anet->hidden; + an = (struct area_net *) fib_get(&oa->net_fib, &anc->px.addr, anc->px.len); + an->hidden = an->hidden; } } +static void +ospf_area_add(struct proto_ospf *po, struct ospf_area_config *ac, int reconf) +{ + struct proto *p = &po->proto; + struct ospf_area *oa; + + OSPF_TRACE(D_EVENTS, "Adding area %R", ac->areaid); + + oa = mb_allocz(p->pool, sizeof(struct ospf_area)); + add_tail(&po->area_list, NODE oa); + po->areano++; + + oa->ac = ac; + oa->stub = ac->stub; + oa->areaid = ac->areaid; + oa->rt = NULL; + oa->po = po; + fib_init(&oa->rtr, p->pool, sizeof(ort), 0, ospf_rt_initort); + add_area_nets(oa, ac); + + if (oa->areaid == 0) + po->backbone = oa; + +#ifdef OSPFv2 + oa->options = (oa->stub ? 0 : OPT_E); +#else /* OSPFv3 */ + oa->options = OPT_R | (oa->stub ? 0 : OPT_E) | OPT_V6; +#endif + + if (reconf) + ospf_ifaces_reconfigure(oa, ac); +} + +static void +ospf_area_remove(struct ospf_area *oa) +{ + struct proto *p = &oa->po->proto; + OSPF_TRACE(D_EVENTS, "Removing area %R", oa->areaid); + + /* We suppose that interfaces are already removed */ + ospf_flush_area(oa->po, oa->areaid); + + fib_free(&oa->rtr); + fib_free(&oa->net_fib); + + oa->po->areano--; + rem_node(NODE oa); + mb_free(oa); +} + + +struct ospf_area * +ospf_find_area(struct proto_ospf *po, u32 aid) +{ + struct ospf_area *oa; + WALK_LIST(oa, po->area_list) + if (((struct ospf_area *) oa)->areaid == aid) + return oa; + return NULL; +} + +static struct ospf_iface * +ospf_find_vlink(struct proto_ospf *po, u32 voa, u32 vid) +{ + struct ospf_iface *ifa; + WALK_LIST(ifa, po->iface_list) + if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa->areaid == voa) && (ifa->vid == vid)) + return ifa; + return NULL; +} + static int ospf_start(struct proto *p) { struct proto_ospf *po = (struct proto_ospf *) p; struct ospf_config *c = (struct ospf_config *) (p->cf); struct ospf_area_config *ac; - struct ospf_area *oa; - int vlinks = 0; po->router_id = proto_get_router_id(p->cf); po->rfc1583 = c->rfc1583; @@ -165,68 +233,14 @@ ospf_start(struct proto *p) po->areano = 0; po->gr = ospf_top_new(p->pool); s_init_list(&(po->lsal)); - if (EMPTY_LIST(c->area_list)) - { - log(L_ERR "Cannot start, no OSPF areas configured!"); - return PS_DOWN; - } WALK_LIST(ac, c->area_list) - { - oa = mb_allocz(p->pool, sizeof(struct ospf_area)); - add_tail(&po->area_list, NODE oa); - po->areano++; - oa->ac = ac; - oa->stub = ac->stub; - oa->areaid = ac->areaid; - oa->rt = NULL; - oa->po = po; - add_area_nets(oa, ac); - fib_init(&oa->rtr, p->pool, sizeof(ort), 0, ospf_rt_initort); + ospf_area_add(po, ac, 0); - if (oa->areaid == 0) - { - po->backbone = oa; - if (oa->stub) log(L_ERR "Backbone cannot be stub. Ignoring!"); - oa->stub = 0; - } - - if (!EMPTY_LIST(ac->vlink_list)) - vlinks = 1; - -#ifdef OSPFv2 - oa->options = (oa->stub ? 0 : OPT_E); -#else /* OSPFv3 */ - oa->options = OPT_R | (oa->stub ? 0 : OPT_E) | OPT_V6; -#endif - } - - /* ABR is always in the backbone */ - if (((po->areano > 1) || vlinks) && !po->backbone) - { - oa = mb_allocz(p->pool, sizeof(struct ospf_area)); - add_tail(&po->area_list, NODE oa); - po->areano++; - oa->ac = NULL; - oa->stub = 0; - oa->areaid = 0; - oa->rt = NULL; - oa->po = po; - fib_init(&oa->net_fib, p->pool, sizeof(struct area_net), 0, ospf_area_initfib); - fib_init(&oa->rtr, p->pool, sizeof(ort), 0, ospf_rt_initort); - po->backbone = oa; -#ifdef OSPFv2 - oa->options = OPT_E; -#else /* OSPFv3 */ - oa->options = OPT_R | OPT_E | OPT_V6; -#endif - } - - /* Add all virtual links as interfaces */ - struct ospf_iface_patt *ipatt; - WALK_LIST(ac, c->area_list) - WALK_LIST(ipatt, ac->vlink_list) - ospf_iface_new(po, NULL, NULL, ac, ipatt); + /* Add all virtual links */ + struct ospf_iface_patt *ic; + WALK_LIST(ic, c->vlink_list) + ospf_iface_new(po->backbone, NULL, ic); return PS_UP; } @@ -513,8 +527,7 @@ ospf_shutdown(struct proto *p) /* And send to all my neighbors 1WAY */ WALK_LIST(ifa, po->iface_list) - if (ifa->state > OSPF_IS_DOWN) - ospf_iface_shutdown(ifa); + ospf_iface_shutdown(ifa); /* Cleanup locked rta entries */ FIB_WALK(&po->rtf, nftmp) @@ -622,10 +635,22 @@ ospf_get_attr(eattr * a, byte * buf, int buflen UNUSED) } } -static int -ospf_patt_compare(struct ospf_iface_patt *a, struct ospf_iface_patt *b) +static void +ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) { - return (a->type == b->type); + oa->ac = nac; + oa->stub = nac->stub; + + ospf_ifaces_reconfigure(oa, nac); + + /* Handle net_list */ + fib_free(&oa->net_fib); + add_area_nets(oa, nac); + + /* No need to handle stubnet_list */ + + oa->marked = 0; + schedule_rt_lsa(oa); } /** @@ -641,296 +666,70 @@ ospf_patt_compare(struct ospf_iface_patt *a, struct ospf_iface_patt *b) static int ospf_reconfigure(struct proto *p, struct proto_config *c) { + struct proto_ospf *po = (struct proto_ospf *) p; struct ospf_config *old = (struct ospf_config *) (p->cf); struct ospf_config *new = (struct ospf_config *) c; - struct ospf_area_config *oldac, *newac; - struct proto_ospf *po = (struct proto_ospf *) p; - struct ospf_iface_patt *oldip, *newip; - struct ospf_iface *ifa; - struct nbma_node *nb1, *nb2, *nbnx; - struct ospf_area *oa = NULL; - int olddead, newdead; - + struct ospf_area_config *nac; + struct ospf_area *oa, *oax; + struct ospf_iface *ifa, *ifx; + struct ospf_iface_patt *ip; + if (po->rfc1583 != new->rfc1583) return 0; - schedule_rtcalc(po); + if (old->abr != new->abr) + return 0; - po->tick = new->tick; po->ecmp = new->ecmp; + po->tick = new->tick; po->disp_timer->recurrent = po->tick; tm_start(po->disp_timer, 1); - oldac = HEAD(old->area_list); - newac = HEAD(new->area_list); + /* Mark all areas and ifaces */ + WALK_LIST(oa, po->area_list) + oa->marked = 1; - /* I should get it in the same order */ + WALK_LIST(ifa, po->iface_list) + ifa->marked = 1; - while (((NODE(oldac))->next != NULL) && ((NODE(newac))->next != NULL)) + /* Add and update areas */ + WALK_LIST(nac, new->area_list) { - if (oldac->areaid != newac->areaid) - return 0; - - WALK_LIST(oa, po->area_list) - if (oa->areaid == newac->areaid) - break; - - if (!oa) - return 0; - - oa->ac = newac; - oa->stub = newac->stub; - if (newac->stub && (oa->areaid == 0)) oa->stub = 0; - - /* Check stubnet_list */ - struct ospf_stubnet_config *oldsn = HEAD(oldac->stubnet_list); - struct ospf_stubnet_config *newsn = HEAD(newac->stubnet_list); - - while (((NODE(oldsn))->next != NULL) && ((NODE(newsn))->next != NULL)) - { - if (!ipa_equal(oldsn->px.addr, newsn->px.addr) || - (oldsn->px.len != newsn->px.len) || - (oldsn->hidden != newsn->hidden) || - (oldsn->summary != newsn->summary) || - (oldsn->cost != newsn->cost)) - break; - - oldsn = (struct ospf_stubnet_config *)(NODE(oldsn))->next; - newsn = (struct ospf_stubnet_config *)(NODE(newsn))->next; - } - - /* If there is no change, both pointers should be NULL */ - if (((NODE(oldsn))->next) != ((NODE(newsn))->next)) - schedule_rt_lsa(oa); - - /* Change net_list */ - fib_free(&oa->net_fib); - add_area_nets(oa, newac); - - if (!iface_patts_equal(&oldac->patt_list, &newac->patt_list, - (void *) ospf_patt_compare)) - return 0; - - WALK_LIST(ifa, po->iface_list) - { - /* FIXME: better handling of vlinks */ - if (ifa->iface == NULL) - continue; - - /* FIXME: better matching of interface_id in OSPFv3 */ - if (oldip = (struct ospf_iface_patt *) - iface_patt_find(&oldac->patt_list, ifa->iface, ifa->addr)) - { - /* Now reconfigure interface */ - if (!(newip = (struct ospf_iface_patt *) - iface_patt_find(&newac->patt_list, ifa->iface, ifa->addr))) - return 0; - - /* HELLO TIMER */ - if (oldip->helloint != newip->helloint) - { - ifa->helloint = newip->helloint; - ifa->hello_timer->recurrent = ifa->helloint; - tm_start(ifa->hello_timer, ifa->helloint); - OSPF_TRACE(D_EVENTS, - "Changing hello interval on interface %s from %d to %d", - ifa->iface->name, oldip->helloint, newip->helloint); - } - - /* POLL TIMER */ - if ((oldip->pollint != newip->pollint) && ifa->poll_timer) - { - ifa->pollint = newip->helloint; - ifa->poll_timer->recurrent = ifa->pollint; - tm_start(ifa->poll_timer, ifa->pollint); - OSPF_TRACE(D_EVENTS, - "Changing poll interval on interface %s from %d to %d", - ifa->iface->name, oldip->pollint, newip->pollint); - } - - /* COST */ - if (oldip->cost != newip->cost) - { - ifa->cost = newip->cost; - OSPF_TRACE(D_EVENTS, - "Changing cost interface %s from %d to %d", - ifa->iface->name, oldip->cost, newip->cost); - schedule_rt_lsa(ifa->oa); - } - - /* RX BUFF */ - if (oldip->rxbuf != newip->rxbuf) - { - ifa->rxbuf = newip->rxbuf; - OSPF_TRACE(D_EVENTS, - "Changing rxbuf interface %s from %d to %d", - ifa->iface->name, oldip->rxbuf, newip->rxbuf); - ospf_iface_change_mtu(po, ifa); - } - - /* LINK */ - if (oldip->check_link != newip->check_link) - { - ifa->check_link = newip->check_link; - - if (!(ifa->iface->flags & IF_LINK_UP)) - ospf_iface_sm(ifa, ifa->check_link ? ISM_LOOP : ISM_UNLOOP); - } - - /* ECMP weight */ - if (oldip->ecmp_weight != newip->ecmp_weight) - { - ifa->ecmp_weight = newip->ecmp_weight; - OSPF_TRACE(D_EVENTS, "Changing ECMP weight of interface %s from %d to %d", - ifa->iface->name, (int)oldip->ecmp_weight + 1, (int)newip->ecmp_weight + 1); - } - - /* strict nbma */ - if ((oldip->strictnbma == 0) && (newip->strictnbma != 0)) - { - ifa->strictnbma = newip->strictnbma; - OSPF_TRACE(D_EVENTS, - "Interface %s is now strict NBMA.", ifa->iface->name); - } - if ((oldip->strictnbma != 0) && (newip->strictnbma == 0)) - { - ifa->strictnbma = newip->strictnbma; - OSPF_TRACE(D_EVENTS, - "Interface %s is no longer strict NBMA.", - ifa->iface->name); - } - - /* stub */ - int old_stub = ospf_iface_stubby(oldip, ifa->addr); - int new_stub = ospf_iface_stubby(newip, ifa->addr); - if (!old_stub && new_stub) - { - ifa->stub = 1; - OSPF_TRACE(D_EVENTS, "Interface %s is now stub.", ifa->iface->name); - } - if (old_stub && !new_stub && (ifa->ioprob == OSPF_I_OK)) - { - ifa->stub = 0; - OSPF_TRACE(D_EVENTS, "Interface %s is no longer stub.", ifa->iface->name); - } - -#ifdef OSPFv2 - /* AUTHENTICATION */ - if (oldip->autype != newip->autype) - { - ifa->autype = newip->autype; - OSPF_TRACE(D_EVENTS, - "Changing authentication type on interface %s", - ifa->iface->name); - } - /* Add *passwords */ - ifa->passwords = newip->passwords; -#endif - - /* priority */ - if (oldip->priority != newip->priority) - { - ifa->priority = newip->priority; - OSPF_TRACE(D_EVENTS, - "Changing priority on interface %s from %d to %d", - ifa->iface->name, oldip->priority, newip->priority); - } - - /* RXMT */ - if (oldip->rxmtint != newip->rxmtint) - { - ifa->rxmtint = newip->rxmtint; - OSPF_TRACE(D_EVENTS, - "Changing retransmit interval on interface %s from %d to %d", - ifa->iface->name, oldip->rxmtint, newip->rxmtint); - } - - /* WAIT */ - if ((oldip->waitint != newip->waitint) && ifa->wait_timer) - { - ifa->waitint = newip->waitint; - if (ifa->wait_timer->expires != 0) - tm_start(ifa->wait_timer, ifa->waitint); - OSPF_TRACE(D_EVENTS, - "Changing wait interval on interface %s from %d to %d", - ifa->iface->name, oldip->waitint, newip->waitint); - } - - /* INFTRANS */ - if (oldip->inftransdelay != newip->inftransdelay) - { - ifa->inftransdelay = newip->inftransdelay; - OSPF_TRACE(D_EVENTS, - "Changing transmit delay on interface %s from %d to %d", - ifa->iface->name, oldip->inftransdelay, - newip->inftransdelay); - } - - /* DEAD */ - olddead = (oldip->dead == 0) ? oldip->deadc * oldip->helloint : oldip->dead; - newdead = (newip->dead == 0) ? newip->deadc * newip->helloint : newip->dead; - if (olddead != newdead) - { - ifa->dead = newdead; - OSPF_TRACE(D_EVENTS, - "Changing dead interval on interface %s from %d to %d", - ifa->iface->name, olddead, newdead); - } - - /* NBMA LIST */ - /* First remove old */ - WALK_LIST_DELSAFE(nb1, nbnx, ifa->nbma_list) - { - nb2 = find_nbma_node_in(&newip->nbma_list, nb1->ip); - if (nb2) - { - if (nb1->eligible != nb2->eligible) - { - nb1->eligible = nb2->eligible; - OSPF_TRACE(D_EVENTS, "Changing neighbor eligibility %I on interface %s", - nb1->ip, ifa->iface->name); - } - } - else - { - OSPF_TRACE(D_EVENTS, - "Removing NBMA neighbor %I on interface %s", - nb1->ip, ifa->iface->name); - rem_node(NODE nb1); - mb_free(nb1); - } - } - /* And then add new */ - WALK_LIST(nb2, newip->nbma_list) - { - if (!ipa_in_net(nb2->ip, ifa->addr->prefix, ifa->addr->pxlen)) - continue; - - if (find_nbma_node(ifa, nb2->ip) == NULL) - { - nb1 = mb_alloc(ifa->pool, sizeof(struct nbma_node)); - nb1->ip = nb2->ip; - nb1->eligible = nb2->eligible; - nb1->found = !!find_neigh_by_ip(ifa, nb1->ip); - add_tail(&ifa->nbma_list, NODE nb1); - OSPF_TRACE(D_EVENTS, - "Adding NBMA neighbor %I on interface %s", - nb1->ip, ifa->iface->name); - } - } - } - } - - oldac = (struct ospf_area_config *)(NODE(oldac))->next; - newac = (struct ospf_area_config *)(NODE(newac))->next; + oa = ospf_find_area(po, nac->areaid); + if (oa) + ospf_area_reconfigure(oa, nac); + else + ospf_area_add(po, nac, 1); } - if (((NODE(oldac))->next) != ((NODE(newac))->next)) - return 0; /* One is not null */ + /* Add and update vlinks */ + WALK_LIST(ip, new->vlink_list) + { + ifa = ospf_find_vlink(po, ip->voa, ip->vid); + if (ifa) + ospf_iface_reconfigure(ifa, ip); + else + ospf_iface_new(po->backbone, NULL, ip); + } - return 1; /* Everything OK :-) */ + /* Delete remaining ifaces and areas */ + WALK_LIST_DELSAFE(ifa, ifx, po->iface_list) + if (ifa->marked) + { + ospf_iface_shutdown(ifa); + ospf_iface_remove(ifa); + } + + WALK_LIST_DELSAFE(oa, oax, po->area_list) + if (oa->marked) + ospf_area_remove(oa); + + schedule_rtcalc(po); + + return 1; } + void ospf_sh_neigh(struct proto *p, char *iff) { diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 75ffb6de..74a8e316 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -81,9 +81,11 @@ struct ospf_config { struct proto_config c; unsigned tick; - int rfc1583; + byte rfc1583; + byte abr; int ecmp; list area_list; + list vlink_list; }; struct nbma_node @@ -121,11 +123,10 @@ struct ospf_area_config { node n; u32 areaid; - int stub; + u32 stub; list patt_list; - list vlink_list; - list net_list; - list stubnet_list; + list net_list; /* List of aggregate networks for that area */ + list stubnet_list; /* List of stub networks added to Router LSA */ }; @@ -167,6 +168,7 @@ struct ospf_iface struct iface *iface; /* Nest's iface */ struct ifa *addr; /* IP prefix associated with that OSPF iface */ struct ospf_area *oa; + struct ospf_iface_patt *cf; pool *pool; sock *sk; /* IP socket (for DD ...) */ list neigh_list; /* List of neigbours */ @@ -174,7 +176,7 @@ struct ospf_iface u32 waitint; /* number of sec before changing state from wait */ u32 rxmtint; /* number of seconds between LSA retransmissions */ u32 pollint; /* Poll interval */ - u32 dead; /* after "deadint" missing hellos is router dead */ + u32 deadint; /* after "deadint" missing hellos is router dead */ u32 vid; /* Id of peer of virtual link */ ip_addr vip; /* IP of peer of virtual link */ struct ospf_iface *vifa; /* OSPF iface which the vlink goes through */ @@ -252,8 +254,8 @@ struct ospf_iface #define OSPF_I_OK 0 /* Everything OK */ #define OSPF_I_SK 1 /* Socket open failed */ #define OSPF_I_LL 2 /* Missing link-local address (OSPFv3) */ - // u8 sk_spf; /* Socket is a member of SPFRouters group */ u8 sk_dr; /* Socket is a member of DRouters group */ + u8 marked; /* Used in OSPF reconfigure */ u16 rxbuf; /* Buffer size */ u8 check_link; /* Whether iface link change is used */ u8 ecmp_weight; /* Weight used for ECMP */ @@ -715,14 +717,15 @@ struct ospf_area node n; u32 areaid; struct ospf_area_config *ac; /* Related area config, might be NULL */ - int origrt; /* Rt lsa origination scheduled? */ struct top_hash_entry *rt; /* My own router LSA */ struct top_hash_entry *pxr_lsa; /* Originated prefix LSA */ list cand; /* List of candidates for RT calc. */ struct fib net_fib; /* Networks to advertise or not */ - unsigned stub; - int trcap; /* Transit capability? */ + u32 stub; /* 0 or stub area cost */ u32 options; /* Optional features */ + byte origrt; /* Rt lsa origination scheduled? */ + byte trcap; /* Transit capability? */ + byte marked; /* Used in OSPF reconfigure */ struct proto_ospf *po; struct fib rtr; /* Routing tables for routers */ }; @@ -753,18 +756,20 @@ struct proto_ospf struct ospf_iface_patt { struct iface_patt i; + u32 type; + u32 stub; u32 cost; u32 helloint; u32 rxmtint; u32 pollint; - u32 inftransdelay; - u32 priority; u32 waitint; u32 deadc; - u32 dead; - u32 type; + u32 deadint; + u32 inftransdelay; + u32 priority; u32 strictnbma; - u32 stub; + list nbma_list; + u32 voa; u32 vid; u16 rxbuf; u8 check_link; @@ -772,9 +777,7 @@ struct ospf_iface_patt #define OSPF_RXBUF_NORMAL 0 #define OSPF_RXBUF_LARGE 1 #define OSPF_RXBUF_MINSIZE 256 /* Minimal allowed size */ - list nbma_list; - - u32 autype; /* Not really used in OSPFv3 */ + u32 autype; /* Not really used in OSPFv3 */ #define OSPF_AUTH_NONE 0 #define OSPF_AUTH_SIMPLE 1 #define OSPF_AUTH_CRYPT 2 @@ -789,24 +792,6 @@ struct ospf_iface_patt #endif }; -#if defined(OSPFv2) && !defined(CONFIG_MC_PROPER_SRC) -static inline int -ospf_iface_stubby(struct ospf_iface_patt *ip, struct ifa *addr) -{ - /* - * We cannot properly support multiple OSPF ifaces on real iface - * with multiple prefixes, therefore we force OSPF ifaces with - * non-primary IP prefixes to be stub. - */ - return ip->stub || !(addr->flags & IA_PRIMARY); -} -#else -static inline int -ospf_iface_stubby(struct ospf_iface_patt *ip, struct ifa *addr UNUSED) -{ - return ip->stub; -} -#endif int ospf_import_control(struct proto *p, rte **new, ea_list **attrs, struct linpool *pool); @@ -816,6 +801,8 @@ void schedule_rt_lsa(struct ospf_area *oa); void schedule_rtcalc(struct proto_ospf *po); void schedule_net_lsa(struct ospf_iface *ifa); +struct ospf_area *ospf_find_area(struct proto_ospf *po, u32 aid); + #ifdef OSPFv3 void schedule_link_lsa(struct ospf_iface *ifa); #else diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 9e693e13..1403e72e 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -1176,6 +1176,7 @@ static void * originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length) { struct proto_ospf *po = oa->po; + struct ospf_config *cf = (struct ospf_config *) (po->proto.cf); struct ospf_iface *ifa; struct ospf_lsa_prefix *lp; struct ifa *vlink_addr = NULL; @@ -1234,7 +1235,7 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length) /* If there are some configured vlinks, add some global address, which will be used as a vlink endpoint. */ - if (oa->ac && !EMPTY_LIST(oa->ac->vlink_list) && !host_addr && vlink_addr) + if (!EMPTY_LIST(cf->vlink_list) && !host_addr && vlink_addr) { lsa_put_prefix(po, vlink_addr->ip, MAX_PREFIX_LENGTH, 0); i++;