From 70945cb645402a4bb1d3dc46a07928caeb954c1f Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 26 Jun 2014 11:58:57 +0200 Subject: [PATCH 01/11] Temporary integrated OSPF commit. --- doc/bird.sgml | 12 +- lib/birdlib.h | 6 + lib/slists.h | 8 +- proto/ospf/config.Y | 94 +- proto/ospf/dbdes.c | 608 ++++++----- proto/ospf/dbdes.h | 17 - proto/ospf/hello.c | 541 ++++----- proto/ospf/hello.h | 21 - proto/ospf/iface.c | 615 ++++++----- proto/ospf/iface.h | 36 - proto/ospf/lsack.c | 227 ++-- proto/ospf/lsack.h | 25 - proto/ospf/lsalib.c | 570 ++++++---- proto/ospf/lsalib.h | 61 +- proto/ospf/lsreq.c | 178 +-- proto/ospf/lsreq.h | 17 - proto/ospf/lsupd.c | 985 +++++++---------- proto/ospf/lsupd.h | 25 - proto/ospf/neighbor.c | 345 +++--- proto/ospf/neighbor.h | 22 - proto/ospf/ospf.c | 985 ++++++++--------- proto/ospf/ospf.h | 746 +++++++------ proto/ospf/packet.c | 616 +++++------ proto/ospf/packet.h | 28 - proto/ospf/rt.c | 1009 ++++++++--------- proto/ospf/rt.h | 27 +- proto/ospf/topology.c | 2413 ++++++++++++++++++++++------------------- proto/ospf/topology.h | 111 +- 28 files changed, 5077 insertions(+), 5271 deletions(-) delete mode 100644 proto/ospf/dbdes.h delete mode 100644 proto/ospf/hello.h delete mode 100644 proto/ospf/iface.h delete mode 100644 proto/ospf/lsack.h delete mode 100644 proto/ospf/lsreq.h delete mode 100644 proto/ospf/lsupd.h delete mode 100644 proto/ospf/neighbor.h delete mode 100644 proto/ospf/packet.h diff --git a/doc/bird.sgml b/doc/bird.sgml index fa4c777f..04b6a845 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2332,6 +2332,7 @@ protocol ospf <name> { tx length <num>; type [broadcast|bcast|pointopoint|ptp| nonbroadcast|nbma|pointomultipoint|ptmp]; + link lsa suppression <switch>; strict nonbroadcast <switch>; real broadcast <switch>; ptp netmask <switch>; @@ -2596,9 +2597,16 @@ protocol ospf <name> { communication, or if the NBMA network is used as an (possibly unnumbered) PtP link. - strict nonbroadcast switch + link lsa suppression + In OSPFv3, link LSAs are generated for each link, announcing link-local + IPv6 address of the router to its local neighbors. These are useless on + PtP or PtMP networks and this option allows to suppress the link LSA + origination for such interfaces. The option is ignored on other than PtP + or PtMP interfaces. Default value is no. + + strict nonbroadcast If set, don't send hello to any undefined neighbor. This switch is - ignored on other than NBMA or PtMP networks. Default value is no. + ignored on other than NBMA or PtMP interfaces. Default value is no. real broadcast In =0 ? (a) : -(a)) #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) +#define BIT32_VAL(p) (((u32) 1) << ((p) % 32)) +#define BIT32_TEST(b,p) ((b)[(p)/32] & BIT32_VAL(p)) +#define BIT32_SET(b,p) ((b)[(p)/32] |= BIT32_VAL(p)) +#define BIT32_CLR(b,p) ((b)[(p)/32] &= ~BIT32_VAL(p)) +#define BIT32_ZERO(b,l) memset((b), 0, (l)/8) + #ifndef NULL #define NULL ((void *) 0) #endif diff --git a/lib/slists.h b/lib/slists.h index 2334e36a..d98d02d2 100644 --- a/lib/slists.h +++ b/lib/slists.h @@ -68,10 +68,12 @@ typedef struct siterator { #define SNODE (snode *) #define SHEAD(list) ((void *)((list).head)) #define STAIL(list) ((void *)((list).tail)) -#define WALK_SLIST(n,list) for(n=SHEAD(list);(SNODE (n))->next; \ - n=(void *)((SNODE (n))->next)) +#define SNODE_NEXT(n) ((void *)((SNODE (n))->next)) +#define SNODE_VALID(n) ((SNODE (n))->next) + +#define WALK_SLIST(n,list) for(n=SHEAD(list); SNODE_VALID(n); n=SNODE_NEXT(n)) #define WALK_SLIST_DELSAFE(n,nxt,list) \ - for(n=SHEAD(list); nxt=(void *)((SNODE (n))->next); n=(void *) nxt) + for(n=SHEAD(list); nxt=SNODE_NEXT(n); n=(void *) nxt) #define EMPTY_SLIST(list) (!(list).head->next) void s_add_tail(slist *, snode *); diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 478529bc..9cbfa9a3 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -21,7 +21,9 @@ static list *this_nets; static struct area_net_config *this_pref; static struct ospf_stubnet_config *this_stubnet; -#ifdef OSPFv2 +static inline int ospf_cfg_is_v2(void) { return OSPF_CFG->ospf2; } +static inline int ospf_cfg_is_v3(void) { return ! OSPF_CFG->ospf2; } + static void ospf_iface_finish(void) { @@ -38,21 +40,6 @@ ospf_iface_finish(void) if ((ip->autype == OSPF_AUTH_NONE) && (ip->passwords != NULL)) log(L_WARN "Password option without authentication option does not make sense"); } -#endif - -#ifdef OSPFv3 -static void -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) @@ -61,12 +48,12 @@ ospf_area_finish(void) cf_error("Backbone area cannot be stub/NSSA"); if (this_area->summary && (this_area->type == OPT_E)) - cf_error("Only Stub/NSSA areas can use summary propagation"); + cf_error("Only stub/NSSA areas can use summary propagation"); if (this_area->default_nssa && ((this_area->type != OPT_N) || ! this_area->summary)) cf_error("Only NSSA areas with summary propagation can use NSSA default route"); - if ((this_area->default_cost & LSA_EXT_EBIT) && ! this_area->default_nssa) + if ((this_area->default_cost & LSA_EXT3_EBIT) && ! this_area->default_nssa) cf_error("Only NSSA default route can use type 2 metric"); } @@ -80,15 +67,22 @@ ospf_proto_finish(void) int areano = 0; int backbone = 0; + int nssa = 0; struct ospf_area_config *ac; WALK_LIST(ac, cf->area_list) { areano++; if (ac->areaid == 0) - backbone = 1; + backbone = 1; + if (ac->type == OPT_N) + nssa = 1; } + cf->abr = areano > 1; + /* Route export or NSSA translation (RFC 3101 3.1) */ + cf->asbr = (this_proto->out_filter != FILTER_REJECT) || (nssa && cf->abr); + if (cf->abr && !backbone) { struct ospf_area_config *ac = cfg_allocz(sizeof(struct ospf_area_config)); @@ -101,26 +95,27 @@ ospf_proto_finish(void) } if (!cf->abr && !EMPTY_LIST(cf->vlink_list)) - cf_error( "Vlinks cannot be used on single area router"); + cf_error("Vlinks cannot be used on single area router"); + + if (cf->asbr && (areano == 1) && (this_area->type == 0)) + cf_error("ASBR must be in non-stub area"); } static inline void -check_defcost(int cost) +ospf_check_defcost(int cost) { if ((cost <= 0) || (cost >= LSINFINITY)) cf_error("Default cost must be in range 1-%d", LSINFINITY-1); } static inline void -set_instance_id(unsigned id) +ospf_check_auth(void) { -#ifdef OSPFv3 - OSPF_PATT->instance_id = id; -#else - cf_error("Instance ID requires OSPFv3"); -#endif + if (ospf_cfg_is_v3()) + cf_error("Authentication not supported in OSPFv3"); } + CF_DECLS CF_KEYWORDS(OSPF, AREA, OSPF_METRIC1, OSPF_METRIC2, OSPF_TAG, OSPF_ROUTER_ID) @@ -132,7 +127,7 @@ CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, CHECK, LINK, ONLY, BFD) CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY, TAG, EXTERNAL) CF_KEYWORDS(WAIT, DELAY, LSADB, ECMP, LIMIT, WEIGHT, NSSA, TRANSLATOR, STABILITY) CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL, NETMASK, TX, PRIORITY, LENGTH) -CF_KEYWORDS(SECONDARY, MERGE) +CF_KEYWORDS(SECONDARY, MERGE, LSA, SUPPRESSION) %type opttext %type lsadb_args @@ -146,8 +141,8 @@ ospf_proto_start: proto_start OSPF { this_proto = proto_config_new(&proto_ospf, sizeof(struct ospf_config), $1); init_list(&OSPF_CFG->area_list); init_list(&OSPF_CFG->vlink_list); - OSPF_CFG->rfc1583 = DEFAULT_RFC1583; - OSPF_CFG->tick = DEFAULT_OSPFTICK; + OSPF_CFG->tick = OSPF_DEFAULT_TICK; + OSPF_CFG->ospf2 = OSPF_IS_V2; } ; @@ -160,7 +155,7 @@ ospf_proto_item: proto_item | RFC1583COMPAT bool { OSPF_CFG->rfc1583 = $2; } | STUB ROUTER bool { OSPF_CFG->stub_router = $3; } - | ECMP bool { OSPF_CFG->ecmp = $2 ? DEFAULT_ECMP_LIMIT : 0; } + | ECMP bool { OSPF_CFG->ecmp = $2 ? OSPF_DEFAULT_ECMP_LIMIT : 0; } | ECMP bool LIMIT expr { OSPF_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); } | MERGE EXTERNAL bool { OSPF_CFG->merge_external = $3; } | TICK expr { OSPF_CFG->tick = $2; if($2<=0) cf_error("Tick must be greater than zero"); } @@ -171,9 +166,9 @@ 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->default_cost = DEFAULT_STUB_COST; + this_area->default_cost = OSPF_DEFAULT_STUB_COST; this_area->type = OPT_E; - this_area->transint = DEFAULT_TRANSINT; + this_area->transint = OSPF_DEFAULT_TRANSINT; init_list(&this_area->patt_list); init_list(&this_area->net_list); @@ -195,9 +190,9 @@ ospf_area_item: | NSSA { this_area->type = OPT_N; } | SUMMARY bool { this_area->summary = $2; } | DEFAULT NSSA bool { this_area->default_nssa = $3; } - | DEFAULT COST expr { this_area->default_cost = $3; check_defcost($3); } - | DEFAULT COST2 expr { this_area->default_cost = $3 | LSA_EXT_EBIT; check_defcost($3); } - | STUB COST expr { this_area->default_cost = $3; check_defcost($3); } + | DEFAULT COST expr { this_area->default_cost = $3; ospf_check_defcost($3); } + | DEFAULT COST2 expr { this_area->default_cost = $3 | LSA_EXT3_EBIT; ospf_check_defcost($3); } + | STUB COST expr { this_area->default_cost = $3; ospf_check_defcost($3); } | TRANSLATOR bool { this_area->translator = $2; } | TRANSLATOR STABILITY expr { this_area->transint = $3; } | NETWORKS { this_nets = &this_area->net_list; } '{' pref_list '}' @@ -249,10 +244,10 @@ ospf_vlink_item: | WAIT expr { OSPF_PATT->waitint = $2 ; } | 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 ; } - | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT ; } - | password_list + | AUTHENTICATION NONE { OSPF_PATT->autype = OSPF_AUTH_NONE; } + | AUTHENTICATION SIMPLE { OSPF_PATT->autype = OSPF_AUTH_SIMPLE; ospf_check_auth(); } + | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT; ospf_check_auth(); } + | password_list { ospf_check_auth(); } ; ospf_vlink_start: VIRTUAL LINK idval @@ -292,18 +287,19 @@ 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 ; } - | REAL BROADCAST bool { OSPF_PATT->real_bcast = $3; if (OSPF_VERSION != 2) cf_error("Real broadcast option requires OSPFv2"); } - | PTP NETMASK bool { OSPF_PATT->ptp_netmask = $3; if (OSPF_VERSION != 2) cf_error("Real netmask option requires OSPFv2"); } + | REAL BROADCAST bool { OSPF_PATT->real_bcast = $3; if (!ospf_cfg_is_v2()) cf_error("Real broadcast option requires OSPFv2"); } + | PTP NETMASK bool { OSPF_PATT->ptp_netmask = $3; if (!ospf_cfg_is_v2()) cf_error("PtP netmask option requires OSPFv2"); } | 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; } | ECMP WEIGHT expr { OSPF_PATT->ecmp_weight = $3 - 1; if (($3<1) || ($3>256)) cf_error("ECMP weight must be in range 1-256"); } + | LINK LSA SUPPRESSION bool { OSPF_PATT->link_lsa_suppression = $4; if (!ospf_cfg_is_v3()) cf_error("Link LSA suppression option requires OSPFv3"); } | NEIGHBORS '{' nbma_list '}' - | AUTHENTICATION NONE { OSPF_PATT->autype = OSPF_AUTH_NONE ; } - | AUTHENTICATION SIMPLE { OSPF_PATT->autype = OSPF_AUTH_SIMPLE ; } - | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT ; } + | AUTHENTICATION NONE { OSPF_PATT->autype = OSPF_AUTH_NONE; } + | AUTHENTICATION SIMPLE { OSPF_PATT->autype = OSPF_AUTH_SIMPLE; ospf_check_auth(); } + | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT; ospf_check_auth(); } | RX BUFFER NORMAL { OSPF_PATT->rx_buffer = 0; } | RX BUFFER LARGE { OSPF_PATT->rx_buffer = OSPF_MAX_PKT_SIZE; } | RX BUFFER expr { OSPF_PATT->rx_buffer = $3; if (($3 < OSPF_MIN_PKT_SIZE) || ($3 > OSPF_MAX_PKT_SIZE)) cf_error("Buffer size must be in range 256-65535"); } @@ -314,7 +310,7 @@ ospf_iface_item: | TTL SECURITY TX ONLY { OSPF_PATT->ttl_security = 2; } | BFD bool { OSPF_PATT->bfd = $2; cf_check_bfd($2); } | SECONDARY bool { OSPF_PATT->bsd_secondary = $2; } - | password_list + | password_list { ospf_check_auth(); } ; pref_list: @@ -349,7 +345,7 @@ nbma_eligible: | ELIGIBLE { $$ = 1; } ; -nbma_item: IPA nbma_eligible ';' +nbma_item: ipa nbma_eligible ';' { this_nbma = cfg_allocz(sizeof(struct nbma_node)); add_tail(&OSPF_PATT->nbma_list, NODE this_nbma); @@ -384,11 +380,11 @@ ospf_iface_start: ospf_instance_id: /* empty */ - | INSTANCE expr { set_instance_id($2); } + | INSTANCE expr { OSPF_PATT->instance_id = $2; } ; ospf_iface_patt_list: - iface_patt_list { if (OSPF_VERSION == 3) iface_patt_check(); } ospf_instance_id + iface_patt_list { if (ospf_cfg_is_v3()) iface_patt_check(); } ospf_instance_id ; ospf_iface_opts: diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c index 6b291344..1f37965c 100644 --- a/proto/ospf/dbdes.c +++ b/proto/ospf/dbdes.c @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -9,62 +11,185 @@ #include "ospf.h" -#ifdef OSPFv2 -struct ospf_dbdes_packet +struct ospf_dbdes2_packet { - struct ospf_packet ospf_packet; + struct ospf_packet hdr; + union ospf_auth auth; + u16 iface_mtu; u8 options; - union imms imms; /* I, M, MS bits */ + u8 imms; /* I, M, MS bits */ u32 ddseq; + + struct ospf_lsa_header lsas[]; }; -#define hton_opt(X) X -#define ntoh_opt(X) X -#endif - - -#ifdef OSPFv3 -struct ospf_dbdes_packet +struct ospf_dbdes3_packet { - struct ospf_packet ospf_packet; + struct ospf_packet hdr; + u32 options; u16 iface_mtu; u8 padding; - union imms imms; /* I, M, MS bits */ + u8 imms; /* I, M, MS bits */ u32 ddseq; + + struct ospf_lsa_header lsas[]; }; -#define hton_opt(X) htonl(X) -#define ntoh_opt(X) ntohl(X) -#endif - -static void ospf_dump_dbdes(struct proto *p, struct ospf_dbdes_packet *pkt) +static inline uint +ospf_dbdes_hdrlen(struct ospf_proto *p) { - struct ospf_packet *op = &pkt->ospf_packet; - - ASSERT(op->type == DBDES_P); - ospf_dump_common(p, op); - log(L_TRACE "%s: imms %s%s%s", - p->name, pkt->imms.bit.ms ? "MS " : "", - pkt->imms.bit.m ? "M " : "", - pkt->imms.bit.i ? "I " : "" ); - log(L_TRACE "%s: ddseq %u", p->name, ntohl(pkt->ddseq)); - - struct ospf_lsa_header *plsa = (void *) (pkt + 1); - unsigned int i, j; - - j = (ntohs(op->length) - sizeof(struct ospf_dbdes_packet)) / - sizeof(struct ospf_lsa_header); - - for (i = 0; i < j; i++) - ospf_dump_lsahdr(p, plsa + i); + return ospf_is_v2(p) ? + sizeof(struct ospf_dbdes2_packet) : sizeof(struct ospf_dbdes3_packet); } +static void +ospf_dbdes_body(struct ospf_proto *p, struct ospf_packet *pkt, + struct ospf_lsa_header **body, uint *count) +{ + uint plen = ntohs(pkt->length); + uint hlen = ospf_dbdes_hdrlen(p); + + *body = ((void *) pkt) + hlen; + *count = (plen - hlen) / sizeof(struct ospf_lsa_header); +} + +static void +ospf_dump_dbdes(struct ospf_proto *p, struct ospf_packet *pkt) +{ + struct ospf_lsa_header *lsas; + uint i, lsa_count; + u32 pkt_ddseq; + u16 pkt_iface_mtu; + u8 pkt_imms; + + ASSERT(pkt->type == DBDES_P); + ospf_dump_common(p, pkt); + + if (ospf_is_v2(p)) + { + struct ospf_dbdes2_packet *ps = (void *) pkt; + pkt_iface_mtu = ntohs(ps->iface_mtu); + pkt_imms = ps->imms; + pkt_ddseq = ntohl(ps->ddseq); + } + else /* OSPFv3 */ + { + struct ospf_dbdes3_packet *ps = (void *) pkt; + pkt_iface_mtu = ntohs(ps->iface_mtu); + pkt_imms = ps->imms; + pkt_ddseq = ntohl(ps->ddseq); + } + + log(L_TRACE "%s: mtu %u", p->p.name, pkt_iface_mtu); + log(L_TRACE "%s: imms %s%s%s", p->p.name, + (pkt_imms & DBDES_I) ? "I " : "", + (pkt_imms & DBDES_M) ? "M " : "", + (pkt_imms & DBDES_MS) ? "MS" : ""); + log(L_TRACE "%s: ddseq %u", p->p.name, pkt_ddseq); + + ospf_dbdes_body(p, pkt, &lsas, &lsa_count); + for (i = 0; i < lsa_count; i++) + ospf_dump_lsahdr(p, lsas + i); +} + + +static void +ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n, int body) +{ + struct ospf_iface *ifa = n->ifa; + struct ospf_packet *pkt; + uint length; + + u16 iface_mtu = (ifa->type == OSPF_IT_VLINK) ? 0 : ifa->iface->mtu; + + if (n->ldd_bsize != ifa->tx_length) + { + mb_free(n->ldd_buffer); + n->ldd_buffer = mb_allocz(n->pool, ifa->tx_length); + n->ldd_bsize = ifa->tx_length; + } + + pkt = n->ldd_buffer; + ospf_pkt_fill_hdr(ifa, pkt, DBDES_P); + + if (ospf_is_v2(p)) + { + struct ospf_dbdes2_packet *ps = (void *) pkt; + ps->iface_mtu = htons(iface_mtu); + ps->options = ifa->oa->options; + ps->imms = 0; /* Will be set later */ + ps->ddseq = htonl(n->dds); + length = sizeof(struct ospf_dbdes2_packet); + } + else /* OSPFv3 */ + { + struct ospf_dbdes3_packet *ps = (void *) pkt; + ps->options = htonl(ifa->oa->options); + ps->iface_mtu = htons(iface_mtu); + ps->padding = 0; + ps->imms = 0; /* Will be set later */ + ps->ddseq = htonl(n->dds); + length = sizeof(struct ospf_dbdes3_packet); + } + + if (body && (n->myimms & DBDES_M)) + { + struct ospf_lsa_header *lsas; + struct top_hash_entry *en; + uint i = 0, lsa_max; + + ospf_dbdes_body(p, pkt, &lsas, &lsa_max); + en = (void *) s_get(&(n->dbsi)); + + while (i < lsa_max) + { + if (!SNODE_VALID(en)) + { + n->myimms &= ~DBDES_M; /* Unset More bit */ + break; + } + + if ((en->lsa.age < LSA_MAXAGE) && + lsa_flooding_allowed(en->lsa_type, en->domain, ifa)) + { + lsa_hton_hdr(&(en->lsa), lsas + i); + i++; + } + + en = SNODE_NEXT(en); + } + + s_put(&(n->dbsi), SNODE en); + + length += i * sizeof(struct ospf_lsa_header); + } + + if (ospf_is_v2(p)) + ((struct ospf_dbdes2_packet *) pkt)->imms = n->myimms; + else + ((struct ospf_dbdes3_packet *) pkt)->imms = n->myimms; + + pkt->length = htons(length); +} + +static void +ospf_do_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n) +{ + struct ospf_iface *ifa = n->ifa; + + OSPF_PACKET(ospf_dump_dbdes, n->ldd_buffer, + "DBDES packet sent to %I via %s", n->ip, ifa->ifname); + sk_set_tbuf(ifa->sk, n->ldd_buffer); + ospf_send_to(ifa, n->ip); + sk_set_tbuf(ifa->sk, NULL); +} + /** - * ospf_dbdes_send - transmit database description packet + * ospf_send_dbdes - transmit database description packet * @n: neighbor * @next: whether to send a next packet in a sequence (1) or to retransmit the old one (0) * @@ -75,355 +200,322 @@ static void ospf_dump_dbdes(struct proto *p, struct ospf_dbdes_packet *pkt) * of the buffer. */ void -ospf_dbdes_send(struct ospf_neighbor *n, int next) +ospf_send_dbdes(struct ospf_neighbor *n, int next) { - struct ospf_dbdes_packet *pkt; - struct ospf_packet *op; struct ospf_iface *ifa = n->ifa; struct ospf_area *oa = ifa->oa; - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; - u16 length, i, j; + struct ospf_proto *p = oa->po; - /* FIXME ??? */ - if ((oa->rt == NULL) || (EMPTY_LIST(po->lsal))) - update_rt_lsa(oa); + /* RFC 2328 10.8 */ + + if (oa->rt == NULL) + return; switch (n->state) { - case NEIGHBOR_EXSTART: /* Send empty packets */ - n->myimms.bit.i = 1; - pkt = ospf_tx_buffer(ifa); - op = &pkt->ospf_packet; - ospf_pkt_fill_hdr(ifa, pkt, DBDES_P); - pkt->iface_mtu = (ifa->type == OSPF_IT_VLINK) ? 0 : htons(ifa->iface->mtu); - pkt->options = hton_opt(oa->options); - pkt->imms = n->myimms; - pkt->ddseq = htonl(n->dds); - length = sizeof(struct ospf_dbdes_packet); - op->length = htons(length); + case NEIGHBOR_EXSTART: + n->myimms |= DBDES_I; - OSPF_PACKET(ospf_dump_dbdes, pkt, "DBDES packet sent to %I via %s", n->ip, ifa->ifname); - ospf_send_to(ifa, n->ip); + /* Send empty packets */ + ospf_prepare_dbdes(p, n, 0); + ospf_do_send_dbdes(p, n); break; case NEIGHBOR_EXCHANGE: - n->myimms.bit.i = 0; + n->myimms &= ~DBDES_I; if (next) - { - snode *sn; - struct ospf_lsa_header *lsa; + ospf_prepare_dbdes(p, n, 1); - if (n->ldd_bsize != ifa->tx_length) - { - mb_free(n->ldd_buffer); - n->ldd_buffer = mb_allocz(n->pool, ifa->tx_length); - n->ldd_bsize = ifa->tx_length; - } + /* Send prepared packet */ + ospf_do_send_dbdes(p, n); - pkt = n->ldd_buffer; - op = (struct ospf_packet *) pkt; + /* Master should restart RXMT timer for each DBDES exchange */ + if (n->myimms & DBDES_MS) + tm_start(n->rxmt_timer, n->ifa->rxmtint); - ospf_pkt_fill_hdr(ifa, pkt, DBDES_P); - pkt->iface_mtu = (ifa->type == OSPF_IT_VLINK) ? 0 : htons(ifa->iface->mtu); - pkt->ddseq = htonl(n->dds); - pkt->options = hton_opt(oa->options); - - j = i = (ospf_pkt_maxsize(ifa) - sizeof(struct ospf_dbdes_packet)) / sizeof(struct ospf_lsa_header); /* Number of possible lsaheaders to send */ - lsa = (n->ldd_buffer + sizeof(struct ospf_dbdes_packet)); - - if (n->myimms.bit.m) - { - sn = s_get(&(n->dbsi)); - - DBG("Number of LSA: %d\n", j); - for (; i > 0; i--) - { - struct top_hash_entry *en= (struct top_hash_entry *) sn; - - if (ospf_lsa_flooding_allowed(&en->lsa, en->domain, ifa)) - { - htonlsah(&(en->lsa), lsa); - DBG("Working on: %d\n", i); - DBG("\tX%01x %-1R %-1R %p\n", en->lsa.type, en->lsa.id, en->lsa.rt, en->lsa_body); - - lsa++; - } - else i++; /* No lsa added */ - - if (sn == STAIL(po->lsal)) - { - i--; - break; - } - - sn = sn->next; - } - - if (sn == STAIL(po->lsal)) - { - DBG("Number of LSA NOT sent: %d\n", i); - DBG("M bit unset.\n"); - n->myimms.bit.m = 0; /* Unset more bit */ - } - - s_put(&(n->dbsi), sn); - } - - pkt->imms.byte = n->myimms.byte; - - length = (j - i) * sizeof(struct ospf_lsa_header) + - sizeof(struct ospf_dbdes_packet); - op->length = htons(length); - - DBG("%s: DB_DES (M) prepared for %I.\n", p->name, n->ip); - } + if (!(n->myimms & DBDES_MS)) + if (!(n->myimms & DBDES_M) && + !(n->imms & DBDES_M)) + ospf_neigh_sm(n, INM_EXDONE); + break; case NEIGHBOR_LOADING: case NEIGHBOR_FULL: - length = n->ldd_buffer ? ntohs(((struct ospf_packet *) n->ldd_buffer)->length) : 0; - if (!length) + if (!n->ldd_buffer) { - OSPF_TRACE(D_PACKETS, "No packet in my buffer for repeating"); + OSPF_TRACE(D_PACKETS, "No DBDES packet for repeating"); ospf_neigh_sm(n, INM_KILLNBR); return; } - /* Send last packet from ldd buffer */ - - OSPF_PACKET(ospf_dump_dbdes, n->ldd_buffer, "DBDES packet sent to %I via %s", n->ip, ifa->ifname); - - sk_set_tbuf(ifa->sk, n->ldd_buffer); - ospf_send_to(ifa, n->ip); - sk_set_tbuf(ifa->sk, NULL); - - if(n->myimms.bit.ms) tm_start(n->rxmt_timer, n->ifa->rxmtint); /* Restart timer */ - - if (!n->myimms.bit.ms) - { - if ((n->myimms.bit.m == 0) && (n->imms.bit.m == 0) && - (n->state == NEIGHBOR_EXCHANGE)) - { - ospf_neigh_sm(n, INM_EXDONE); - } - } - break; - - default: /* Ignore it */ + /* Send last packet */ + ospf_do_send_dbdes(p, n); break; } } -static void -ospf_dbdes_reqladd(struct ospf_dbdes_packet *ps, struct ospf_neighbor *n) + +static int +ospf_process_dbdes(struct ospf_proto *p, struct ospf_packet *pkt, struct ospf_neighbor *n) { - struct ospf_lsa_header *plsa, lsa; - struct top_hash_entry *he, *sn; - struct ospf_area *oa = n->ifa->oa; - struct top_graph *gr = oa->po->gr; - struct ospf_packet *op; - int i, j; + struct ospf_iface *ifa = n->ifa; + struct ospf_lsa_header *lsas; + uint i, lsa_count; - op = (struct ospf_packet *) ps; + ospf_dbdes_body(p, pkt, &lsas, &lsa_count); - plsa = (void *) (ps + 1); - - j = (ntohs(op->length) - sizeof(struct ospf_dbdes_packet)) / - sizeof(struct ospf_lsa_header); - - for (i = 0; i < j; i++) + for (i = 0; i < lsa_count; i++) { - ntohlsah(plsa + i, &lsa); - u32 dom = ospf_lsa_domain(lsa.type, n->ifa); - if (((he = ospf_hash_find_header(gr, dom, &lsa)) == NULL) || - (lsa_comp(&lsa, &(he->lsa)) == 1)) + struct top_hash_entry *en, *req; + struct ospf_lsa_header lsa; + u32 lsa_type, lsa_domain; + + lsa_ntoh_hdr(lsas + i, &lsa); + lsa_get_type_domain(&lsa, ifa, &lsa_type, &lsa_domain); + + /* RFC 2328 10.6 and RFC 5340 4.2.2 */ + + if (!lsa_type) { - /* Is this condition necessary? */ - if (ospf_hash_find_header(n->lsrqh, dom, &lsa) == NULL) - { - sn = ospf_hash_get_header(n->lsrqh, dom, &lsa); - ntohlsah(plsa + i, &(sn->lsa)); - s_add_tail(&(n->lsrql), SNODE sn); - } + log(L_WARN "%s: Bad DBDES from %I - LSA of unknown type", p->p.name, n->ip); + goto err; + } + + if (!oa_is_ext(ifa->oa) && (LSA_SCOPE(lsa_type) == LSA_SCOPE_AS)) + { + log(L_WARN "%s: Bad DBDES from %I - LSA with AS scope in stub area", p->p.name, n->ip); + goto err; + } + + /* Errata 3746 to RFC 2328 - rt-summary-LSAs forbidden in stub areas */ + if (!oa_is_ext(ifa->oa) && (lsa_type == LSA_T_SUM_RT)) + { + log(L_WARN "%s: Bad DBDES from %I - rt-summary-LSA in stub area", p->p.name, n->ip); + goto err; + } + + /* Not explicitly mentioned in RFC 5340 4.2.2 but makes sense */ + if (LSA_SCOPE(lsa_type) == LSA_SCOPE_RES) + { + log(L_WARN "%s: Bad DBDES from %I - LSA with invalid scope", p->p.name, n->ip); + goto err; + } + + en = ospf_hash_find(p->gr, lsa_domain, lsa.id, lsa.rt, lsa_type); + if (!en || (lsa_comp(&lsa, &(en->lsa)) == CMP_NEWER)) + { + req = ospf_hash_get(n->lsrqh, lsa_domain, lsa.id, lsa.rt, lsa_type); + + if (!SNODE_VALID(req)) + s_add_tail(&n->lsrql, SNODE req); + + req->lsa = lsa; } } + + return 0; + + err: + ospf_neigh_sm(n, INM_SEQMIS); + return -1; } void -ospf_dbdes_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, +ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n) { - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; + struct ospf_proto *p = ifa->oa->po; + u32 rcv_ddseq, rcv_options; + u16 rcv_iface_mtu; + u8 rcv_imms; + uint plen; - unsigned int size = ntohs(ps_i->length); - if (size < sizeof(struct ospf_dbdes_packet)) + /* RFC 2328 10.6 */ + + plen = ntohs(pkt->length); + if (plen < ospf_dbdes_hdrlen(p)) { - log(L_ERR "Bad OSPF DBDES packet from %I - too short (%u B)", n->ip, size); + log(L_ERR "OSPF: Bad DBDES packet from %I - too short (%u B)", n->ip, plen); return; } - struct ospf_dbdes_packet *ps = (void *) ps_i; - u32 ps_ddseq = ntohl(ps->ddseq); - u32 ps_options = ntoh_opt(ps->options); - u16 ps_iface_mtu = ntohs(ps->iface_mtu); - - OSPF_PACKET(ospf_dump_dbdes, ps, "DBDES packet received from %I via %s", n->ip, ifa->ifname); + OSPF_PACKET(ospf_dump_dbdes, pkt, "DBDES packet received from %I via %s", n->ip, ifa->ifname); ospf_neigh_sm(n, INM_HELLOREC); + if (ospf_is_v2(p)) + { + struct ospf_dbdes2_packet *ps = (void *) pkt; + rcv_iface_mtu = ntohs(ps->iface_mtu); + rcv_options = ps->options; + rcv_imms = ps->imms; + rcv_ddseq = ntohl(ps->ddseq); + } + else /* OSPFv3 */ + { + struct ospf_dbdes3_packet *ps = (void *) pkt; + rcv_options = ntohl(ps->options); + rcv_iface_mtu = ntohs(ps->iface_mtu); + rcv_imms = ps->imms; + rcv_ddseq = ntohl(ps->ddseq); + } + switch (n->state) { case NEIGHBOR_DOWN: case NEIGHBOR_ATTEMPT: case NEIGHBOR_2WAY: return; - break; + case NEIGHBOR_INIT: ospf_neigh_sm(n, INM_2WAYREC); if (n->state != NEIGHBOR_EXSTART) return; + case NEIGHBOR_EXSTART: + if ((ifa->type != OSPF_IT_VLINK) && + (rcv_iface_mtu != ifa->iface->mtu) && + (rcv_iface_mtu != 0) && + (ifa->iface->mtu != 0)) + log(L_WARN "OSPF: MTU mismatch with neighbor %I on interface %s (remote %d, local %d)", + n->ip, ifa->ifname, rcv_iface_mtu, ifa->iface->mtu); - if ((ifa->type != OSPF_IT_VLINK) && (ps_iface_mtu != ifa->iface->mtu) - && (ps_iface_mtu != 0) && (ifa->iface->mtu != 0)) - log(L_WARN "OSPF: MTU mismatch with neighbour %I on interface %s (remote %d, local %d)", - n->ip, ifa->ifname, ps_iface_mtu, ifa->iface->mtu); - - if ((ps->imms.bit.m && ps->imms.bit.ms && ps->imms.bit.i) - && (n->rid > po->router_id) && (size == sizeof(struct ospf_dbdes_packet))) + if ((rcv_imms == DBDES_IMMS) && + (n->rid > p->router_id) && + (plen == ospf_dbdes_hdrlen(p))) { /* I'm slave! */ - n->dds = ps_ddseq; - n->ddr = ps_ddseq; - n->options = ps_options; - n->myimms.bit.ms = 0; - n->imms.byte = ps->imms.byte; - OSPF_TRACE(D_PACKETS, "I'm slave to %I.", n->ip); + n->dds = rcv_ddseq; + n->ddr = rcv_ddseq; + n->options = rcv_options; + n->myimms &= ~DBDES_MS; + n->imms = rcv_imms; + OSPF_TRACE(D_PACKETS, "I'm slave to %I", n->ip); ospf_neigh_sm(n, INM_NEGDONE); - ospf_dbdes_send(n, 1); + ospf_send_dbdes(n, 1); break; } - if (((ps->imms.bit.i == 0) && (ps->imms.bit.ms == 0)) && - (n->rid < po->router_id) && (n->dds == ps_ddseq)) + if (!(rcv_imms & DBDES_I) && + !(rcv_imms & DBDES_MS) && + (n->rid < p->router_id) && + (n->dds == rcv_ddseq)) { /* I'm master! */ - n->options = ps_options; - n->ddr = ps_ddseq - 1; /* It will be set corectly a few lines down */ - n->imms.byte = ps->imms.byte; - OSPF_TRACE(D_PACKETS, "I'm master to %I.", n->ip); + n->options = rcv_options; + n->ddr = rcv_ddseq - 1; /* It will be set corectly a few lines down */ + n->imms = rcv_imms; + OSPF_TRACE(D_PACKETS, "I'm master to %I", n->ip); ospf_neigh_sm(n, INM_NEGDONE); } else { - DBG("%s: Nothing happend to %I (imms=%u)\n", p->name, n->ip, - ps->imms.byte); + DBG("%s: Nothing happend to %I (imms=%d)\n", p->name, n->ip, rcv_imms); break; } + case NEIGHBOR_EXCHANGE: - if ((ps->imms.byte == n->imms.byte) && (ps_options == n->options) && - (ps_ddseq == n->ddr)) + if ((rcv_imms == n->imms) && + (rcv_options == n->options) && + (rcv_ddseq == n->ddr)) { /* Duplicate packet */ - OSPF_TRACE(D_PACKETS, "Received duplicate dbdes from %I.", n->ip); - if (n->myimms.bit.ms == 0) + OSPF_TRACE(D_PACKETS, "Received duplicate dbdes from %I", n->ip); + if (!(n->myimms & DBDES_MS)) { /* Slave should retransmit dbdes packet */ - ospf_dbdes_send(n, 0); + ospf_send_dbdes(n, 0); } return; } - n->ddr = ps_ddseq; - - if (ps->imms.bit.ms != n->imms.bit.ms) /* M/S bit differs */ + if ((rcv_imms & DBDES_MS) != (n->imms & DBDES_MS)) /* M/S bit differs */ { - OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (bit MS)", - n->ip); + OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (bit MS)", n->ip); ospf_neigh_sm(n, INM_SEQMIS); break; } - if (ps->imms.bit.i) /* I bit is set */ + if (rcv_imms & DBDES_I) /* I bit is set */ { - OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (bit I)", - n->ip); + OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (bit I)", n->ip); ospf_neigh_sm(n, INM_SEQMIS); break; } - n->imms.byte = ps->imms.byte; - - if (ps_options != n->options) /* Options differs */ + if (rcv_options != n->options) /* Options differs */ { - OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (options)", - n->ip); + OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (options)", n->ip); ospf_neigh_sm(n, INM_SEQMIS); break; } - if (n->myimms.bit.ms) + n->ddr = rcv_ddseq; + n->imms = rcv_imms; + + if (n->myimms & DBDES_MS) { - if (ps_ddseq != n->dds) /* MASTER */ + if (rcv_ddseq != n->dds) /* MASTER */ { - OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (master)", - n->ip); + OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (master)", n->ip); ospf_neigh_sm(n, INM_SEQMIS); break; } - n->dds++; - DBG("Incrementing dds\n"); - ospf_dbdes_reqladd(ps, n); - if ((n->myimms.bit.m == 0) && (ps->imms.bit.m == 0)) - { - ospf_neigh_sm(n, INM_EXDONE); - } - else - { - ospf_dbdes_send(n, 1); - } + n->dds++; + + if (ospf_process_dbdes(p, pkt, n) < 0) + return; + + if (!(n->myimms & DBDES_M) && !(n->imms & DBDES_M)) + ospf_neigh_sm(n, INM_EXDONE); + else + ospf_send_dbdes(n, 1); } else { - if (ps_ddseq != (n->dds + 1)) /* SLAVE */ + if (rcv_ddseq != (n->dds + 1)) /* SLAVE */ { OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (slave)", n->ip); ospf_neigh_sm(n, INM_SEQMIS); break; } - n->ddr = ps_ddseq; - n->dds = ps_ddseq; - ospf_dbdes_reqladd(ps, n); - ospf_dbdes_send(n, 1); - } + n->ddr = rcv_ddseq; + n->dds = rcv_ddseq; + + if (ospf_process_dbdes(p, pkt, n) < 0) + return; + + ospf_send_dbdes(n, 1); + } break; + case NEIGHBOR_LOADING: case NEIGHBOR_FULL: - if ((ps->imms.byte == n->imms.byte) && (ps_options == n->options) - && (ps_ddseq == n->ddr)) - /* Only duplicate are accepted */ + if ((rcv_imms == n->imms) && + (rcv_options == n->options) && + (rcv_ddseq == n->ddr)) { - OSPF_TRACE(D_PACKETS, "Received duplicate dbdes from %I.", n->ip); - if (n->myimms.bit.ms == 0) + /* Duplicate packet */ + OSPF_TRACE(D_PACKETS, "Received duplicate dbdes from %I", n->ip); + if (!(n->myimms & DBDES_MS)) { /* Slave should retransmit dbdes packet */ - ospf_dbdes_send(n, 0); + ospf_send_dbdes(n, 0); } return; } else { - OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (full)", - n->ip); - DBG("PS=%u, DDR=%u, DDS=%u\n", ps_ddseq, n->ddr, n->dds); + OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (full)", n->ip); + DBG("PS=%u, DDR=%u, DDS=%u\n", rcv_ddseq, n->ddr, n->dds); ospf_neigh_sm(n, INM_SEQMIS); } break; + default: bug("Received dbdes from %I in undefined state.", n->ip); } diff --git a/proto/ospf/dbdes.h b/proto/ospf/dbdes.h deleted file mode 100644 index 63cca0a2..00000000 --- a/proto/ospf/dbdes.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * BIRD -- OSPF - * - * (c) 1999 - 2004 Ondrej Filip - * - * Can be freely distributed and used under the terms of the GNU GPL. - * - */ - -#ifndef _BIRD_OSPF_DBDES_H_ -#define _BIRD_OSPF_DBDES_H_ - -void ospf_dbdes_send(struct ospf_neighbor *n, int next); -void ospf_dbdes_receive(struct ospf_packet *ps, struct ospf_iface *ifa, - struct ospf_neighbor *n); - -#endif /* _BIRD_OSPF_DBDES_H_ */ diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index e8bce09f..376eac3c 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -9,25 +11,26 @@ #include "ospf.h" -#ifdef OSPFv2 -struct ospf_hello_packet +struct ospf_hello2_packet { - struct ospf_packet ospf_packet; - ip_addr netmask; + struct ospf_packet hdr; + union ospf_auth auth; + + u32 netmask; u16 helloint; u8 options; u8 priority; u32 deadint; u32 dr; u32 bdr; + + u32 neighbors[]; }; -#endif - -#ifdef OSPFv3 -struct ospf_hello_packet +struct ospf_hello3_packet { - struct ospf_packet ospf_packet; + struct ospf_packet hdr; + u32 iface_id; u8 priority; u8 options3; @@ -37,286 +40,92 @@ struct ospf_hello_packet u16 deadint; u32 dr; u32 bdr; + + u32 neighbors[]; }; -#endif void -ospf_hello_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, - struct ospf_neighbor *n, ip_addr faddr) +ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) { - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; - char *beg = "OSPF: Bad HELLO packet from "; - unsigned int size, i, twoway, peers; - u32 tmp; - u32 *pnrid; - - size = ntohs(ps_i->length); - if (size < sizeof(struct ospf_hello_packet)) - { - log(L_ERR "%s%I - too short (%u B)", beg, faddr, size); - return; - } - - struct ospf_hello_packet *ps = (void *) ps_i; - - OSPF_TRACE(D_PACKETS, "HELLO packet received from %I via %s", faddr, ifa->ifname); - -#ifdef OSPFv2 - ip_addr mask = ps->netmask; - ipa_ntoh(mask); - if ((ifa->type != OSPF_IT_VLINK) && - (ifa->type != OSPF_IT_PTP) && - !ipa_equal(mask, ipa_mkmask(ifa->addr->pxlen))) - { - log(L_ERR "%s%I - netmask mismatch (%I)", beg, faddr, mask); - return; - } -#endif - - tmp = ntohs(ps->helloint); - if (tmp != ifa->helloint) - { - log(L_ERR "%s%I - hello interval mismatch (%d)", beg, faddr, tmp); - return; - } - -#ifdef OSPFv2 - tmp = ntohl(ps->deadint); -#else /* OSPFv3 */ - tmp = ntohs(ps->deadint); -#endif - if (tmp != ifa->deadint) - { - log(L_ERR "%s%I - dead interval mismatch (%d)", beg, faddr, tmp); - return; - } - - /* Check whether bits E, N match */ - if ((ps->options ^ ifa->oa->options) & (OPT_E | OPT_N)) - { - log(L_ERR "%s%I - area type mismatch (%x)", beg, faddr, ps->options); - return; - } - -#ifdef OSPFv2 - if (n && (n->rid != ntohl(ps_i->routerid))) - { - OSPF_TRACE(D_EVENTS, - "Neighbor %I has changed router id from %R to %R.", - n->ip, n->rid, ntohl(ps_i->routerid)); - ospf_neigh_remove(n); - n = NULL; - } -#endif - - if (!n) - { - if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP)) - { - struct nbma_node *nn = find_nbma_node(ifa, faddr); - - if (!nn && ifa->strictnbma) - { - log(L_WARN "Ignoring new neighbor: %I on %s", faddr, ifa->ifname); - return; - } - - if (nn && (ifa->type == OSPF_IT_NBMA) && - (((ps->priority == 0) && nn->eligible) || - ((ps->priority > 0) && !nn->eligible))) - { - log(L_ERR "Eligibility mismatch for neighbor: %I on %s", faddr, ifa->ifname); - return; - } - - if (nn) - nn->found = 1; - } - - OSPF_TRACE(D_EVENTS, "New neighbor found: %I on %s", faddr, ifa->ifname); - - n = ospf_neighbor_new(ifa); - - n->rid = ntohl(ps_i->routerid); - n->ip = faddr; - n->dr = ntohl(ps->dr); - n->bdr = ntohl(ps->bdr); - n->priority = ps->priority; -#ifdef OSPFv3 - n->iface_id = ntohl(ps->iface_id); -#endif - - if (n->ifa->cf->bfd) - ospf_neigh_update_bfd(n, n->ifa->bfd); - } -#ifdef OSPFv3 /* NOTE: this could also be relevant for OSPFv2 on PtP ifaces */ - else if (!ipa_equal(faddr, n->ip)) - { - OSPF_TRACE(D_EVENTS, "Neighbor address changed from %I to %I", n->ip, faddr); - n->ip = faddr; - } -#endif - - ospf_neigh_sm(n, INM_HELLOREC); - - pnrid = (u32 *) ((struct ospf_hello_packet *) (ps + 1)); - - peers = (size - sizeof(struct ospf_hello_packet))/ sizeof(u32); - - twoway = 0; - for (i = 0; i < peers; i++) - { - if (ntohl(pnrid[i]) == po->router_id) - { - DBG("%s: Twoway received from %I\n", p->name, faddr); - ospf_neigh_sm(n, INM_2WAYREC); - twoway = 1; - break; - } - } - - if (!twoway) - ospf_neigh_sm(n, INM_1WAYREC); - - u32 olddr = n->dr; - u32 oldbdr = n->bdr; - u32 oldpriority = n->priority; -#ifdef OSPFv3 - u32 oldiface_id = n->iface_id; -#endif - - n->dr = ntohl(ps->dr); - n->bdr = ntohl(ps->bdr); - n->priority = ps->priority; -#ifdef OSPFv3 - n->iface_id = ntohl(ps->iface_id); -#endif - - - /* Check priority change */ - if (n->state >= NEIGHBOR_2WAY) - { -#ifdef OSPFv2 - u32 neigh = ipa_to_u32(n->ip); -#else /* OSPFv3 */ - u32 neigh = n->rid; -#endif - - if (n->priority != oldpriority) - ospf_iface_sm(ifa, ISM_NEICH); - -#ifdef OSPFv3 - if (n->iface_id != oldiface_id) - ospf_iface_sm(ifa, ISM_NEICH); -#endif - - /* Neighbor is declaring itself ad DR and there is no BDR */ - if ((n->dr == neigh) && (n->bdr == 0) - && (n->state != NEIGHBOR_FULL)) - ospf_iface_sm(ifa, ISM_BACKS); - - /* Neighbor is declaring itself as BDR */ - if ((n->bdr == neigh) && (n->state != NEIGHBOR_FULL)) - ospf_iface_sm(ifa, ISM_BACKS); - - /* Neighbor is newly declaring itself as DR or BDR */ - if (((n->dr == neigh) && (n->dr != olddr)) - || ((n->bdr == neigh) && (n->bdr != oldbdr))) - ospf_iface_sm(ifa, ISM_NEICH); - - /* Neighbor is no more declaring itself as DR or BDR */ - if (((olddr == neigh) && (n->dr != olddr)) - || ((oldbdr == neigh) && (n->bdr != oldbdr))) - ospf_iface_sm(ifa, ISM_NEICH); - } - - if (ifa->type == OSPF_IT_NBMA) - { - if ((ifa->priority == 0) && (n->priority > 0)) - ospf_hello_send(n->ifa, OHS_HELLO, n); - } - ospf_neigh_sm(n, INM_HELLOREC); -} - -void -ospf_hello_send(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) -{ - struct ospf_hello_packet *pkt; - struct ospf_packet *op; - struct proto *p; + struct ospf_proto *p = ifa->oa->po; + struct ospf_packet *pkt; struct ospf_neighbor *neigh, *n1; - u16 length; - int i; struct nbma_node *nb; + u32 *neighbors; + uint length; + int i, max; if (ifa->state <= OSPF_IS_LOOP) return; if (ifa->stub) - return; /* Don't send any packet on stub iface */ + return; - p = (struct proto *) (ifa->oa->po); - DBG("%s: Hello/Poll timer fired on interface %s with IP %I\n", - p->name, ifa->ifname, ifa->addr->ip); - /* Now we should send a hello packet */ pkt = ospf_tx_buffer(ifa); - op = &pkt->ospf_packet; - - /* Now fill ospf_hello header */ ospf_pkt_fill_hdr(ifa, pkt, HELLO_P); -#ifdef OSPFv2 - pkt->netmask = ipa_mkmask(ifa->addr->pxlen); - ipa_hton(pkt->netmask); - if ((ifa->type == OSPF_IT_VLINK) || - ((ifa->type == OSPF_IT_PTP) && !ifa->ptp_netmask)) - pkt->netmask = IPA_NONE; -#endif + if (ospf_is_v2(p)) + { + struct ospf_hello2_packet *ps = (void *) pkt; - pkt->helloint = ntohs(ifa->helloint); - pkt->priority = ifa->priority; + if ((ifa->type == OSPF_IT_VLINK) || + ((ifa->type == OSPF_IT_PTP) && !ifa->ptp_netmask)) + ps->netmask = 0; + else + ps->netmask = htonl(u32_mkmask(ifa->addr->pxlen)); -#ifdef OSPFv3 - pkt->iface_id = htonl(ifa->iface_id); + ps->helloint = ntohs(ifa->helloint); + ps->options = ifa->oa->options; + ps->priority = ifa->priority; + ps->deadint = htonl(ifa->deadint); + ps->dr = htonl(ipa_to_u32(ifa->drip)); + ps->bdr = htonl(ipa_to_u32(ifa->bdrip)); - pkt->options3 = ifa->oa->options >> 16; - pkt->options2 = ifa->oa->options >> 8; -#endif - pkt->options = ifa->oa->options; + length = sizeof(struct ospf_hello2_packet); + neighbors = ps->neighbors; + } + else + { + struct ospf_hello3_packet *ps = (void *) pkt; -#ifdef OSPFv2 - 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->deadint); - pkt->dr = htonl(ifa->drid); - pkt->bdr = htonl(ifa->bdrid); -#endif + ps->iface_id = htonl(ifa->iface_id); + ps->priority = ifa->priority; + ps->options3 = ifa->oa->options >> 16; + ps->options2 = ifa->oa->options >> 8; + ps->options = ifa->oa->options; + ps->helloint = ntohs(ifa->helloint); + ps->deadint = htons(ifa->deadint); + ps->dr = htonl(ifa->drid); + ps->bdr = htonl(ifa->bdrid); + + length = sizeof(struct ospf_hello3_packet); + neighbors = ps->neighbors; + } + + i = 0; + max = (ospf_pkt_maxsize(ifa) - length) / sizeof(u32); /* Fill all neighbors */ - i = 0; - if (kind != OHS_SHUTDOWN) { - u32 *pp = (u32 *) (((u8 *) pkt) + sizeof(struct ospf_hello_packet)); WALK_LIST(neigh, ifa->neigh_list) { - if ((i+1) * sizeof(u32) + sizeof(struct ospf_hello_packet) > ospf_pkt_maxsize(ifa)) + if (i == max) { - log(L_WARN "%s: Too many neighbors on interface %s", p->name, ifa->ifname); + log(L_WARN "%s: Too many neighbors on interface %s", p->p.name, ifa->ifname); break; } - *(pp + i) = htonl(neigh->rid); + neighbors[i] = htonl(neigh->rid); i++; } } - length = sizeof(struct ospf_hello_packet) + i * sizeof(u32); - op->length = htons(length); + length += i * sizeof(u32); + pkt->length = htons(length); + + OSPF_TRACE(D_PACKETS, "HELLO packet sent via %s", ifa->ifname); switch(ifa->type) { @@ -369,8 +178,226 @@ ospf_hello_send(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) break; default: - bug("Bug in ospf_hello_send()"); + bug("Bug in ospf_send_hello()"); + } +} + + +void +ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, + struct ospf_neighbor *n, ip_addr faddr) +{ + struct ospf_proto *p = ifa->oa->po; + char *beg = "OSPF: Bad HELLO packet from "; + u32 rcv_iface_id, rcv_helloint, rcv_deadint, rcv_dr, rcv_bdr; + u8 rcv_options, rcv_priority; + u32 *neighbors; + u32 neigh_count; + uint plen, i; + + /* RFC 2328 10.5 */ + + OSPF_TRACE(D_PACKETS, "HELLO packet received from %I via %s", faddr, ifa->ifname); + + plen = ntohs(pkt->length); + + if (ospf_is_v2(p)) + { + struct ospf_hello2_packet *ps = (void *) pkt; + + if (plen < sizeof(struct ospf_hello2_packet)) + { + log(L_ERR "%s%I - too short (%u B)", beg, faddr, plen); + return; + } + + rcv_iface_id = 0; + rcv_helloint = ntohs(ps->helloint); + rcv_deadint = ntohl(ps->deadint); + rcv_dr = ntohl(ps->dr); + rcv_bdr = ntohl(ps->bdr); + rcv_options = ps->options; + rcv_priority = ps->priority; + + int pxlen = u32_masklen(ntohl(ps->netmask)); + if ((ifa->type != OSPF_IT_VLINK) && + (ifa->type != OSPF_IT_PTP) && + (pxlen != ifa->addr->pxlen)) + { + log(L_ERR "%s%I - prefix length mismatch (%d)", beg, faddr, pxlen); + return; + } + + neighbors = ps->neighbors; + neigh_count = (plen - sizeof(struct ospf_hello2_packet)) / sizeof(u32); + } + else /* OSPFv3 */ + { + struct ospf_hello3_packet *ps = (void *) pkt; + + if (plen < sizeof(struct ospf_hello3_packet)) + { + log(L_ERR "%s%I - too short (%u B)", beg, faddr, plen); + return; + } + + rcv_iface_id = ntohl(ps->iface_id); + rcv_helloint = ntohs(ps->helloint); + rcv_deadint = ntohs(ps->deadint); + rcv_dr = ntohl(ps->dr); + rcv_bdr = ntohl(ps->bdr); + rcv_options = ps->options; + rcv_priority = ps->priority; + + neighbors = ps->neighbors; + neigh_count = (plen - sizeof(struct ospf_hello3_packet)) / sizeof(u32); } - OSPF_TRACE(D_PACKETS, "HELLO packet sent via %s", ifa->ifname); + if (rcv_helloint != ifa->helloint) + { + log(L_ERR "%s%I - hello interval mismatch (%d)", beg, faddr, rcv_helloint); + return; + } + + if (rcv_deadint != ifa->deadint) + { + log(L_ERR "%s%I - dead interval mismatch (%d)", beg, faddr, rcv_deadint); + return; + } + + /* Check whether bits E, N match */ + if ((rcv_options ^ ifa->oa->options) & (OPT_E | OPT_N)) + { + log(L_ERR "%s%I - area type mismatch (%x)", beg, faddr, rcv_options); + return; + } + + /* Check consistency of existing neighbor entry */ + if (n) + { + unsigned t = ifa->type; + if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP))) + { + /* Neighbor identified by IP address; Router ID may change */ + if (n->rid != ntohl(pkt->routerid)) + { + OSPF_TRACE(D_EVENTS, "Neighbor %I has changed Router ID from %R to %R", + n->ip, n->rid, ntohl(pkt->routerid)); + ospf_neigh_remove(n); + n = NULL; + } + } + else /* OSPFv3 or OSPFv2/PtP */ + { + /* Neighbor identified by Router ID; IP address may change */ + if (!ipa_equal(faddr, n->ip)) + { + OSPF_TRACE(D_EVENTS, "Neighbor address changed from %I to %I", n->ip, faddr); + n->ip = faddr; + } + } + } + + if (!n) + { + if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP)) + { + struct nbma_node *nn = find_nbma_node(ifa, faddr); + + if (!nn && ifa->strictnbma) + { + log(L_WARN "Ignoring new neighbor: %I on %s", faddr, ifa->ifname); + return; + } + + if (nn && (ifa->type == OSPF_IT_NBMA) && + (((rcv_priority == 0) && nn->eligible) || + ((rcv_priority > 0) && !nn->eligible))) + { + log(L_ERR "Eligibility mismatch for neighbor: %I on %s", faddr, ifa->ifname); + return; + } + + if (nn) + nn->found = 1; + } + + OSPF_TRACE(D_EVENTS, "New neighbor found: %I on %s", faddr, ifa->ifname); + + n = ospf_neighbor_new(ifa); + + n->rid = ntohl(pkt->routerid); + n->ip = faddr; + n->dr = rcv_dr; + n->bdr = rcv_bdr; + n->priority = rcv_priority; + n->iface_id = rcv_iface_id; + + if (n->ifa->cf->bfd) + ospf_neigh_update_bfd(n, n->ifa->bfd); + } + + u32 n_id = ospf_is_v2(p) ? ipa_to_u32(n->ip) : n->rid; + + u32 old_dr = n->dr; + u32 old_bdr = n->bdr; + u32 old_priority = n->priority; + u32 old_iface_id = n->iface_id; + + n->dr = rcv_dr; + n->bdr = rcv_bdr; + n->priority = rcv_priority; + n->iface_id = rcv_iface_id; + + + /* Update inactivity timer */ + ospf_neigh_sm(n, INM_HELLOREC); + + /* RFC 2328 9.5.1 - non-eligible routers reply to hello on NBMA nets */ + if (ifa->type == OSPF_IT_NBMA) + if ((ifa->priority == 0) && (n->priority > 0)) + ospf_send_hello(n->ifa, OHS_HELLO, n); + + + /* Examine list of neighbors */ + for (i = 0; i < neigh_count; i++) + if (neighbors[i] == htonl(p->router_id)) + goto found_self; + + ospf_neigh_sm(n, INM_1WAYREC); + return; + + found_self: + ospf_neigh_sm(n, INM_2WAYREC); + + + if (n->iface_id != old_iface_id) + { + /* If neighbor is DR, also update cached DR interface ID */ + if (ifa->drid == n->rid) + ifa->dr_iface_id = n->iface_id; + + /* RFC 5340 4.4.3 Event 4 - change of neighbor's interface ID */ + ospf_notify_rt_lsa(ifa->oa); + + /* Missed in RFC 5340 4.4.3 Event 4 - (Px-)Net-LSA uses iface_id to ref Link-LSAs */ + ospf_notify_net_lsa(ifa); + } + + if (ifa->state == OSPF_IS_WAITING) + { + /* Neighbor is declaring itself DR (and there is no BDR) or as BDR */ + if (((n->dr == n_id) && (n->bdr == 0)) || (n->bdr == n_id)) + ospf_iface_sm(ifa, ISM_BACKS); + } + else if (ifa->state >= OSPF_IS_DROTHER) + { + /* Neighbor changed priority or started/stopped declaring itself as DR/BDR */ + if ((n->priority != old_priority) || + ((n->dr == n_id) && (old_dr != n_id)) || + ((n->dr != n_id) && (old_dr == n_id)) || + ((n->bdr == n_id) && (old_bdr != n_id)) || + ((n->bdr != n_id) && (old_bdr == n_id))) + ospf_iface_sm(ifa, ISM_NEICH); + } } diff --git a/proto/ospf/hello.h b/proto/ospf/hello.h deleted file mode 100644 index 0e476692..00000000 --- a/proto/ospf/hello.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * BIRD -- OSPF - * - * (c) 1999 - 2004 Ondrej Filip - * - * Can be freely distributed and used under the terms of the GNU GPL. - * - */ - -#ifndef _BIRD_OSPF_HELLO_H_ -#define _BIRD_OSPF_HELLO_H_ - -void ospf_hello_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, - struct ospf_neighbor *n, ip_addr faddr); -void ospf_hello_send(struct ospf_iface *ifa, 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 50cf15e2..5e17371d 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -2,12 +2,15 @@ * BIRD -- OSPF * * (c) 1999--2005 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ #include "ospf.h" + char *ospf_is[] = { "down", "loop", "waiting", "ptp", "drother", "backup", "dr" }; @@ -18,25 +21,26 @@ char *ospf_ism[] = { "interface up", "wait timer fired", "backup seen", char *ospf_it[] = { "broadcast", "nbma", "ptp", "ptmp", "virtual link" }; + static void poll_timer_hook(timer * timer) { - ospf_hello_send(timer->data, OHS_POLL, NULL); + ospf_send_hello(timer->data, OHS_POLL, NULL); } static void hello_timer_hook(timer * timer) { - ospf_hello_send(timer->data, OHS_HELLO, NULL); + ospf_send_hello(timer->data, OHS_HELLO, NULL); } static void wait_timer_hook(timer * timer) { struct ospf_iface *ifa = (struct ospf_iface *) timer->data; - struct proto *p = &ifa->oa->po->proto; + struct ospf_proto *p = ifa->oa->po; - OSPF_TRACE(D_EVENTS, "Wait timer fired on interface %s.", ifa->ifname); + OSPF_TRACE(D_EVENTS, "Wait timer fired on interface %s", ifa->ifname); ospf_iface_sm(ifa, ISM_WAITF); } @@ -58,10 +62,9 @@ ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen) { plen += SIZE_OF_IP_HEADER; -#ifdef OSPFv2 + /* This is relevant just for OSPFv2 */ if (ifa->autype == OSPF_AUTH_CRYPT) plen += OSPF_AUTH_CRYPT_SIZE; -#endif if (plen <= ifa->sk->tbsize) return 0; @@ -77,12 +80,14 @@ ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen) struct nbma_node * -find_nbma_node_in(list *nnl, ip_addr ip) +find_nbma_node_(list *nnl, ip_addr ip) { struct nbma_node *nn; + WALK_LIST(nn, *nnl) if (ipa_equal(nn->ip, ip)) return nn; + return NULL; } @@ -90,7 +95,7 @@ find_nbma_node_in(list *nnl, ip_addr ip) static int ospf_sk_open(struct ospf_iface *ifa) { - struct proto_ospf *po = ifa->oa->po; + struct ospf_proto *p = ifa->oa->po; sock *sk = sk_new(ifa->pool); sk->type = SK_IP; @@ -111,24 +116,25 @@ ospf_sk_open(struct ospf_iface *ifa) if (sk_open(sk) < 0) goto err; -#ifdef OSPFv3 - /* 12 is an offset of the checksum in an OSPF packet */ - if (sk_set_ipv6_checksum(sk, 12) < 0) - goto err; -#endif + /* 12 is an offset of the checksum in an OSPFv3 packet */ + if (ospf_is_v3(p)) + if (sk_set_ipv6_checksum(sk, 12) < 0) + goto err; if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_PTP)) { if (ifa->cf->real_bcast) { ifa->all_routers = ifa->addr->brd; + ifa->des_routers = IPA_NONE; if (sk_setup_broadcast(sk) < 0) goto err; } else { - ifa->all_routers = AllSPFRouters; + ifa->all_routers = ospf_is_v2(p) ? IP4_OSPF_ALL_ROUTERS : IP6_OSPF_ALL_ROUTERS; + ifa->des_routers = ospf_is_v2(p) ? IP4_OSPF_DES_ROUTERS : IP6_OSPF_DES_ROUTERS; if (sk_setup_multicast(sk) < 0) goto err; @@ -143,7 +149,7 @@ ospf_sk_open(struct ospf_iface *ifa) return 1; err: - sk_log_error(sk, po->proto.name); + sk_log_error(sk, p->p.name); rfree(sk); return 0; } @@ -154,8 +160,8 @@ ospf_sk_join_dr(struct ospf_iface *ifa) if (ifa->sk_dr) return; - if (sk_join_group(ifa->sk, AllDRouters) < 0) - sk_log_error(ifa->sk, ifa->oa->po->proto.name); + if (sk_join_group(ifa->sk, ifa->des_routers) < 0) + sk_log_error(ifa->sk, ifa->oa->po->p.name); ifa->sk_dr = 1; } @@ -166,16 +172,16 @@ ospf_sk_leave_dr(struct ospf_iface *ifa) if (!ifa->sk_dr) return; - if (sk_leave_group(ifa->sk, AllDRouters) < 0) - sk_log_error(ifa->sk, ifa->oa->po->proto.name); + if (sk_leave_group(ifa->sk, ifa->des_routers) < 0) + sk_log_error(ifa->sk, ifa->oa->po->p.name); ifa->sk_dr = 0; } void -ospf_open_vlink_sk(struct proto_ospf *po) +ospf_open_vlink_sk(struct ospf_proto *p) { - sock *sk = sk_new(po->proto.pool); + sock *sk = sk_new(p->p.pool); sk->type = SK_IP; sk->dport = OSPF_PROTO; @@ -185,48 +191,48 @@ ospf_open_vlink_sk(struct proto_ospf *po) sk->err_hook = ospf_verr_hook; sk->rbsize = 0; - sk->tbsize = OSPF_VLINK_MTU; - sk->data = (void *) po; + sk->tbsize = ospf_is_v2(p) ? IP4_MIN_MTU : IP6_MIN_MTU; + sk->data = (void *) p; sk->flags = 0; if (sk_open(sk) < 0) goto err; -#ifdef OSPFv3 - /* 12 is an offset of the checksum in an OSPF packet */ - if (sk_set_ipv6_checksum(sk, 12) < 0) - goto err; -#endif + /* 12 is an offset of the checksum in an OSPFv3 packet */ + if (ospf_is_v3(p)) + if (sk_set_ipv6_checksum(sk, 12) < 0) + goto err; - po->vlink_sk = sk; + p->vlink_sk = sk; return; err: - sk_log_error(sk, po->proto.name); - log(L_ERR "%s: Cannot open virtual link socket", po->proto.name); + sk_log_error(sk, p->p.name); + log(L_ERR "%s: Cannot open virtual link socket", p->p.name); rfree(sk); } static void ospf_iface_down(struct ospf_iface *ifa) { + struct ospf_proto *p = ifa->oa->po; struct ospf_neighbor *n, *nx; - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; struct ospf_iface *iff; if (ifa->type != OSPF_IT_VLINK) { -#ifdef OSPFv2 - OSPF_TRACE(D_EVENTS, "Removing interface %s (%I/%d) from area %R", - ifa->ifname, ifa->addr->prefix, ifa->addr->pxlen, ifa->oa->areaid); -#else - OSPF_TRACE(D_EVENTS, "Removing interface %s (IID %d) from area %R", - ifa->ifname, ifa->instance_id, ifa->oa->areaid); -#endif + if (ospf_is_v3(ifa->oa->po)) + OSPF_TRACE(D_EVENTS, "Removing interface %s (IID %d) from area %R", + ifa->ifname, ifa->instance_id, ifa->oa->areaid); + else if (ifa->addr->flags & IA_PEER) + OSPF_TRACE(D_EVENTS, "Removing interface %s (peer %I) from area %R", + ifa->ifname, ifa->addr->opposite, ifa->oa->areaid); + else + OSPF_TRACE(D_EVENTS, "Removing interface %s (%I/%d) from area %R", + ifa->ifname, ifa->addr->prefix, ifa->addr->pxlen, ifa->oa->areaid); /* First of all kill all the related vlinks */ - WALK_LIST(iff, po->iface_list) + WALK_LIST(iff, p->iface_list) { if ((iff->type == OSPF_IT_VLINK) && (iff->vifa == ifa)) ospf_iface_sm(iff, ISM_DOWN); @@ -248,6 +254,10 @@ ospf_iface_down(struct ospf_iface *ifa) if (ifa->wait_timer) tm_stop(ifa->wait_timer); + ospf_flush2_lsa(p, &ifa->link_lsa); + ospf_flush2_lsa(p, &ifa->net_lsa); + ospf_flush2_lsa(p, &ifa->pxn_lsa); + if (ifa->type == OSPF_IT_VLINK) { ifa->vifa = NULL; @@ -258,17 +268,16 @@ ospf_iface_down(struct ospf_iface *ifa) ifa->rt_pos_beg = 0; ifa->rt_pos_end = 0; -#ifdef OSPFv3 ifa->px_pos_beg = 0; ifa->px_pos_end = 0; -#endif } void ospf_iface_remove(struct ospf_iface *ifa) { - struct proto *p = &ifa->oa->po->proto; + struct ospf_proto *p = ifa->oa->po; + if (ifa->type == OSPF_IT_VLINK) OSPF_TRACE(D_EVENTS, "Removing vlink to %R via area %R", ifa->vid, ifa->voa->areaid); @@ -281,7 +290,7 @@ void ospf_iface_shutdown(struct ospf_iface *ifa) { if (ifa->state > OSPF_IS_DOWN) - ospf_hello_send(ifa, OHS_SHUTDOWN, NULL); + ospf_send_hello(ifa, OHS_SHUTDOWN, NULL); } /** @@ -296,23 +305,18 @@ ospf_iface_shutdown(struct ospf_iface *ifa) void ospf_iface_chstate(struct ospf_iface *ifa, u8 state) { - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; + struct ospf_proto *p = ifa->oa->po; u8 oldstate = ifa->state; - if (oldstate == state) + if (state == oldstate) return; + OSPF_TRACE(D_EVENTS, "Changing state of iface %s from %s to %s", + ifa->ifname, ospf_is[oldstate], ospf_is[state]); + ifa->state = state; - if (ifa->type == OSPF_IT_VLINK) - OSPF_TRACE(D_EVENTS, "Changing state of virtual link %R from %s to %s", - ifa->vid, ospf_is[oldstate], ospf_is[state]); - else - OSPF_TRACE(D_EVENTS, "Changing state of iface %s from %s to %s", - ifa->ifname, ospf_is[oldstate], ospf_is[state]); - - if ((ifa->type == OSPF_IT_BCAST) && !ifa->cf->real_bcast && ifa->sk) + if ((ifa->type == OSPF_IT_BCAST) && ipa_nonzero(ifa->des_routers) && ifa->sk) { if ((state == OSPF_IS_BACKUP) || (state == OSPF_IS_DR)) ospf_sk_join_dr(ifa); @@ -320,22 +324,17 @@ ospf_iface_chstate(struct ospf_iface *ifa, u8 state) ospf_sk_leave_dr(ifa); } - if ((oldstate == OSPF_IS_DR) && (ifa->net_lsa != NULL)) - { - ifa->net_lsa->lsa.age = LSA_MAXAGE; - if (state >= OSPF_IS_WAITING) - ospf_lsupd_flush_nlsa(po, ifa->net_lsa); - - if (can_flush_lsa(po)) - flush_lsa(ifa->net_lsa, po); - ifa->net_lsa = NULL; - } - if ((oldstate > OSPF_IS_LOOP) && (state <= OSPF_IS_LOOP)) ospf_iface_down(ifa); - schedule_rt_lsa(ifa->oa); - // FIXME flushling of link LSA + /* RFC 2328 12.4 Event 2 - iface state change */ + ospf_notify_rt_lsa(ifa->oa); + + /* RFC 5340 4.4.3 Event 1 - iface state change */ + ospf_notify_link_lsa(ifa); + + /* RFC 2328 12.4 Event 3 - iface enters/leaves DR state */ + ospf_notify_net_lsa(ifa); } /** @@ -352,7 +351,7 @@ ospf_iface_chstate(struct ospf_iface *ifa, u8 state) void ospf_iface_sm(struct ospf_iface *ifa, int event) { - DBG("SM on iface %s. Event is '%s'\n", ifa->ifname, ospf_ism[event]); + DBG("SM on %s. Event is '%s'\n", ifa->ifname, ospf_ism[event]); switch (event) { @@ -360,7 +359,9 @@ ospf_iface_sm(struct ospf_iface *ifa, int event) if (ifa->state <= OSPF_IS_LOOP) { /* Now, nothing should be adjacent */ - if ((ifa->type == OSPF_IT_PTP) || (ifa->type == OSPF_IT_PTMP) || (ifa->type == OSPF_IT_VLINK)) + if ((ifa->type == OSPF_IT_PTP) || + (ifa->type == OSPF_IT_PTMP) || + (ifa->type == OSPF_IT_VLINK)) { ospf_iface_chstate(ifa, OSPF_IS_PTP); } @@ -382,26 +383,19 @@ ospf_iface_sm(struct ospf_iface *ifa, int event) if (ifa->poll_timer) tm_start(ifa->poll_timer, ifa->pollint); - ospf_hello_send(ifa, OHS_HELLO, NULL); - schedule_link_lsa(ifa); + ospf_send_hello(ifa, OHS_HELLO, NULL); } break; case ISM_BACKS: case ISM_WAITF: if (ifa->state == OSPF_IS_WAITING) - { - bdr_election(ifa); - } + ospf_dr_election(ifa); break; case ISM_NEICH: - if ((ifa->state == OSPF_IS_DROTHER) || (ifa->state == OSPF_IS_DR) || - (ifa->state == OSPF_IS_BACKUP)) - { - bdr_election(ifa); - schedule_rt_lsa(ifa->oa); - } + if (ifa->state >= OSPF_IS_DROTHER) + ospf_dr_election(ifa); break; case ISM_LOOP: @@ -427,7 +421,7 @@ ospf_iface_sm(struct ospf_iface *ifa, int event) } static u8 -ospf_iface_classify_int(struct iface *ifa, struct ifa *addr) +ospf_iface_classify_(struct iface *ifa, struct ifa *addr) { if (ipa_nonzero(addr->opposite)) return (ifa->flags & IF_MULTICAST) ? OSPF_IT_PTP : OSPF_IT_PTMP; @@ -445,17 +439,19 @@ ospf_iface_classify_int(struct iface *ifa, struct ifa *addr) static inline u8 ospf_iface_classify(u8 type, struct ifa *addr) { - return (type != OSPF_IT_UNDEF) ? type : ospf_iface_classify_int(addr->iface, addr); + return (type != OSPF_IT_UNDEF) ? type : ospf_iface_classify_(addr->iface, addr); } struct ospf_iface * -ospf_iface_find(struct proto_ospf *p, struct iface *what) +ospf_iface_find(struct ospf_proto *p, struct iface *what) { - struct ospf_iface *i; + struct ospf_iface *ifa; + + WALK_LIST(ifa, p->iface_list) + if ((ifa->iface == what) && (ifa->type != OSPF_IT_VLINK)) + return ifa; - WALK_LIST(i, p->iface_list) if ((i->iface == what) && (i->type != OSPF_IT_VLINK)) - return i; return NULL; } @@ -463,13 +459,12 @@ static void ospf_iface_add(struct object_lock *lock) { struct ospf_iface *ifa = lock->data; - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; + struct ospf_proto *p = ifa->oa->po; /* Open socket if interface is not stub */ if (! ifa->stub && ! ospf_sk_open(ifa)) { - log(L_ERR "%s: Cannot open socket for %s, declaring as stub", p->name, ifa->ifname); + log(L_ERR "%s: Cannot open socket for %s, declaring as stub", p->p.name, ifa->ifname); ifa->ioprob = OSPF_I_SK; ifa->stub = 1; } @@ -525,20 +520,22 @@ ospf_iface_stubby(struct ospf_iface_patt *ip, struct ifa *addr) void ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *ip) { - struct proto *p = &oa->po->proto; + struct ospf_proto *p = oa->po; struct iface *iface = addr->iface; struct ospf_iface *ifa; struct pool *pool; -#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 + if (ospf_is_v3(p)) + OSPF_TRACE(D_EVENTS, "Adding interface %s (IID %d) to area %R", + iface->name, ip->instance_id, oa->areaid); + else if (addr->flags & IA_PEER) + OSPF_TRACE(D_EVENTS, "Adding interface %s (peer %I) to area %R", + iface->name, addr->opposite, oa->areaid); + else + OSPF_TRACE(D_EVENTS, "Adding interface %s (%I/%d) to area %R", + iface->name, addr->prefix, addr->pxlen, oa->areaid); - pool = rp_new(p->pool, "OSPF Interface"); + pool = rp_new(p->p.pool, "OSPF Interface"); ifa = mb_allocz(pool, sizeof(struct ospf_iface)); ifa->iface = iface; ifa->addr = addr; @@ -560,24 +557,18 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i ifa->deadint = ip->deadint; ifa->stub = ospf_iface_stubby(ip, addr); ifa->ioprob = OSPF_I_OK; - ifa->tx_length = ifa_tx_length(ifa); ifa->check_link = ip->check_link; ifa->ecmp_weight = ip->ecmp_weight; ifa->check_ttl = (ip->ttl_security == 1); ifa->bfd = ip->bfd; - -#ifdef OSPFv2 ifa->autype = ip->autype; ifa->passwords = ip->passwords; + ifa->instance_id = ip->instance_id; + ifa->ptp_netmask = !(addr->flags & IA_PEER); if (ip->ptp_netmask < 2) ifa->ptp_netmask = ip->ptp_netmask; -#endif - -#ifdef OSPFv3 - ifa->instance_id = ip->instance_id; -#endif ifa->type = ospf_iface_classify(ip->type, addr); @@ -586,13 +577,11 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i int old_type = ifa->type; u32 if_multi_flag = ip->real_bcast ? IF_BROADCAST : IF_MULTICAST; -#ifdef OSPFv2 - if ((ifa->type == OSPF_IT_BCAST) && (addr->flags & IA_PEER)) + if (ospf_is_v2(p) && (ifa->type == OSPF_IT_BCAST) && (addr->flags & IA_PEER)) ifa->type = OSPF_IT_PTP; - if ((ifa->type == OSPF_IT_NBMA) && (addr->flags & IA_PEER)) + if (ospf_is_v2(p) && (ifa->type == OSPF_IT_NBMA) && (addr->flags & IA_PEER)) ifa->type = OSPF_IT_PTMP; -#endif if ((ifa->type == OSPF_IT_BCAST) && !(iface->flags & if_multi_flag)) ifa->type = OSPF_IT_NBMA; @@ -602,9 +591,12 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i if (ifa->type != old_type) log(L_WARN "%s: Cannot use interface %s as %s, forcing %s", - p->name, iface->name, ospf_it[old_type], ospf_it[ifa->type]); + p->p.name, iface->name, ospf_it[old_type], ospf_it[ifa->type]); + if ((ifa->type == OSPF_IT_PTP) || (ifa->type == OSPF_IT_PTMP)) + ifa->link_lsa_suppression = ip->link_lsa_suppression; + ifa->state = OSPF_IS_DOWN; init_list(&ifa->neigh_list); init_list(&ifa->nbma_list); @@ -617,13 +609,11 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i should be used). Because OSPFv3 iface is not subnet-specific, there is no need for ipa_in_net() check */ -#ifdef OSPFv2 - if (!ipa_in_net(nb->ip, addr->prefix, addr->pxlen)) + if (ospf_is_v2(p) && !ipa_in_net(nb->ip, addr->prefix, addr->pxlen)) continue; -#else - if (!ipa_has_link_scope(nb->ip)) + + if (ospf_is_v3(p) && !ipa_is_link_local(nb->ip)) log(L_WARN "In OSPFv3, configured neighbor address (%I) should be link-local", nb->ip); -#endif add_nbma_node(ifa, nb, 0); } @@ -637,12 +627,9 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i * Therefore, we store such info to lock->addr field. */ + // XXXX review struct object_lock *lock = olock_new(pool); -#ifdef OSPFv2 - lock->addr = ifa->addr->prefix; -#else /* OSPFv3 */ - lock->addr = _MI(0,0,0,ifa->instance_id); -#endif + lock->addr = ospf_is_v2(p) ? ifa->addr->prefix : _MI6(0,0,0,ifa->instance_id); lock->type = OBJLOCK_IP; lock->port = OSPF_PROTO; lock->iface = iface; @@ -653,50 +640,43 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i } void -ospf_iface_new_vlink(struct proto_ospf *po, struct ospf_iface_patt *ip) +ospf_iface_new_vlink(struct ospf_proto *p, struct ospf_iface_patt *ip) { - struct proto *p = &po->proto; struct ospf_iface *ifa; struct pool *pool; - if (!po->vlink_sk) + if (!p->vlink_sk) return; OSPF_TRACE(D_EVENTS, "Adding vlink to %R via area %R", ip->vid, ip->voa); /* Vlink ifname is stored just after the ospf_iface structure */ - pool = rp_new(p->pool, "OSPF Vlink"); + pool = rp_new(p->p.pool, "OSPF Vlink"); ifa = mb_allocz(pool, sizeof(struct ospf_iface) + 16); - ifa->oa = po->backbone; + ifa->oa = p->backbone; ifa->cf = ip; ifa->pool = pool; /* Assign iface ID, for vlinks, this is ugly hack */ - u32 vlink_id = po->last_vlink_id++; + u32 vlink_id = p->last_vlink_id++; ifa->iface_id = vlink_id + OSPF_VLINK_ID_OFFSET; ifa->ifname = (void *) (ifa + 1); bsprintf(ifa->ifname, "vlink%d", vlink_id); - ifa->voa = ospf_find_area(po, ip->voa); + ifa->voa = ospf_find_area(p, ip->voa); ifa->vid = ip->vid; - ifa->sk = po->vlink_sk; + ifa->sk = p->vlink_sk; ifa->helloint = ip->helloint; ifa->rxmtint = ip->rxmtint; ifa->waitint = ip->waitint; ifa->deadint = ip->deadint; ifa->inftransdelay = ip->inftransdelay; - ifa->tx_length = OSPF_VLINK_MTU; - -#ifdef OSPFv2 + ifa->tx_length = ospf_is_v2(p) ? IP4_MIN_MTU : IP6_MIN_MTU; ifa->autype = ip->autype; ifa->passwords = ip->passwords; -#endif - -#ifdef OSPFv3 ifa->instance_id = ip->instance_id; -#endif ifa->type = OSPF_IT_VLINK; @@ -704,13 +684,13 @@ ospf_iface_new_vlink(struct proto_ospf *po, struct ospf_iface_patt *ip) init_list(&ifa->neigh_list); init_list(&ifa->nbma_list); - add_tail(&po->iface_list, NODE ifa); + add_tail(&p->iface_list, NODE ifa); ifa->hello_timer = tm_new_set(ifa->pool, hello_timer_hook, ifa, 0, ifa->helloint); } static void -ospf_iface_change_timer(timer *tm, unsigned val) +ospf_iface_change_timer(timer *tm, uint val) { if (!tm) return; @@ -724,13 +704,13 @@ ospf_iface_change_timer(timer *tm, unsigned val) int ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) { - struct proto *p = &ifa->oa->po->proto; + struct ospf_proto *p = ifa->oa->po; struct ospf_iface_patt *old = ifa->cf; char *ifname = ifa->ifname; /* 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(old->type, ifa->addr); + int old_type = ospf_iface_classify(old->type, ifa->addr); int new_type = ospf_iface_classify(new->type, ifa->addr); if (old_type != new_type) return 0; @@ -806,7 +786,6 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) ifa->inftransdelay = new->inftransdelay; } -#ifdef OSPFv2 /* AUTHENTICATION */ if (ifa->autype != new->autype) { @@ -816,7 +795,6 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* Update passwords */ ifa->passwords = new->passwords; -#endif /* Remaining options are just for proper interfaces */ if (ifa->type == OSPF_IT_VLINK) @@ -837,7 +815,9 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) { OSPF_TRACE(D_EVENTS, "Changing priority on interface %s from %d to %d", ifname, ifa->priority, new->priority); + ifa->priority = new->priority; + ospf_notify_link_lsa(ifa); } /* STRICT NBMA */ @@ -852,7 +832,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* 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); + struct nbma_node *nb2 = find_nbma_node_(&new->nbma_list, nb->ip); if (nb2) { if (nb->eligible != nb2->eligible) @@ -875,13 +855,11 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) WALK_LIST(nb, new->nbma_list) { /* See related note in ospf_iface_new() */ -#ifdef OSPFv2 - if (!ipa_in_net(nb->ip, ifa->addr->prefix, ifa->addr->pxlen)) + if (ospf_is_v2(p) && !ipa_in_net(nb->ip, ifa->addr->prefix, ifa->addr->pxlen)) continue; -#else - if (!ipa_has_link_scope(nb->ip)) + + if (ospf_is_v3(p) && !ipa_is_link_local(nb->ip)) log(L_WARN "In OSPFv3, configured neighbor address (%I) should be link-local", nb->ip); -#endif if (! find_nbma_node(ifa, nb->ip)) { @@ -942,6 +920,17 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) ifa->ecmp_weight = new->ecmp_weight; } + /* Link LSA suppression */ + if (((ifa->type == OSPF_IT_PTP) || (ifa->type == OSPF_IT_PTMP)) && + (ifa->link_lsa_suppression != new->link_lsa_suppression)) + { + OSPF_TRACE(D_EVENTS, "Changing link LSA suppression of %s from %d to %d", + ifname, ifa->link_lsa_suppression, new->link_lsa_suppression); + + ifa->link_lsa_suppression = new->link_lsa_suppression; + ospf_notify_link_lsa(ifa); + } + /* BFD */ if (ifa->bfd != new->bfd) { @@ -961,18 +950,90 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) } -#ifdef OSPFv2 +/* + * State for matching iface pattterns walk + * + * This is significantly different in OSPFv2 and OSPFv3. + * In OSPFv2, OSPF ifaces are created for each IP prefix (struct ifa) + * In OSPFv3, OSPF ifaces are created based on real iface (struct iface) + * We support instance_id for both OSPFv2 (RFC 6549) and OSPFv3. + * + * We process one ifa/iface and match it for all configured instance IDs. We + * maintain bitfields to track whether given instance ID was already matched. + * We have two bitfields, one global (active) and one per area (ignore), to + * detect misconfigured cases where one iface with one instance ID matches in + * multiple areas. + */ -static inline struct ospf_iface_patt * -ospf_iface_patt_find(struct ospf_area_config *ac, struct ifa *a) +struct ospf_mip_walk { + u32 active[8]; /* Bitfield of active instance IDs */ + u32 ignore[8]; /* Bitfield of instance IDs matched in current area */ + struct ospf_area *oa; /* Current area */ + struct ospf_iface_patt *ip; /* Current iface pattern */ + struct iface *iface; /* Specified iface (input) */ + struct ifa *a; /* Specified ifa (input) */ + int warn; /* Whether iface matched in multiple areas */ +}; + +static int +ospf_walk_matching_iface_patts(struct ospf_proto *p, struct ospf_mip_walk *s) { - return (struct ospf_iface_patt *) iface_patt_find(&ac->patt_list, a->iface, a); + int id; + + if (s->ip) + goto step; + + WALK_LIST(s->oa, p->area_list) + { + WALK_LIST(s->ip, s->oa->ac->patt_list) + { + id = s->ip->instance_id; + if (BIT32_TEST(s->ignore, id)) + continue; + + if (iface_patt_match(&s->ip->i, s->iface, s->a)) + { + /* Now we matched ifa/iface/instance_id for the first time in current area */ + BIT32_SET(s->ignore, id); + + /* If we already found it in previous areas, ignore it and add warning */ + if (!BIT32_TEST(s->active, id)) + { s->warn = 1; continue; } + + BIT32_SET(s->active, id); + return 1; + step: + ; + } + } + BIT32_ZERO(s->ignore, 256); + } + + if (s->warn) + log(L_WARN "%s: Interface %s matches for multiple areas", p->p.name, s->iface->name); + + return 0; } -void -ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) + +static struct ospf_iface * +ospf_iface_find_by_key(struct ospf_proto *p, struct ifa *a, int instance_id) { - struct proto_ospf *po = (struct proto_ospf *) p; + struct ospf_iface *ifa; + + WALK_LIST(ifa, p->iface_list) + if ((ifa->addr == a) && (ifa->instance_id == instance_id) && + (ifa->type != OSPF_IT_VLINK)) + return ifa; + + return NULL; +} + + +void +ospf_ifa_notify2(struct proto *P, uint flags, struct ifa *a) +{ + struct ospf_proto *p = (struct ospf_proto *) P; if (a->flags & IA_SECONDARY) return; @@ -983,51 +1044,68 @@ ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) /* In OSPFv2, we create OSPF iface for each address. */ if (flags & IF_CHANGE_UP) { - int done = 0; - struct ospf_area *oa; - WALK_LIST(oa, po->area_list) - { - struct ospf_iface_patt *ip; - if (ip = ospf_iface_patt_find(oa->ac, a)) - { - if (!done) - ospf_iface_new(oa, a, ip); - done++; - } - } - - if (done > 1) - log(L_WARN "%s: Interface %s (IP %I) matches for multiple areas", p->name, a->iface->name, a->ip); + struct ospf_mip_walk s = { .iface = a->iface, .a = a }; + while (ospf_walk_matching_iface_patts(p, &s)) + ospf_iface_new(s.oa, s.a, s.ip); } if (flags & IF_CHANGE_DOWN) { struct ospf_iface *ifa, *ifx; - WALK_LIST_DELSAFE(ifa, ifx, po->iface_list) - { + WALK_LIST_DELSAFE(ifa, ifx, p->iface_list) if ((ifa->type != OSPF_IT_VLINK) && (ifa->addr == a)) ospf_iface_remove(ifa); - /* See a note in ospf_iface_notify() */ - } + /* See a note in ospf_iface_notify() */ } } -static struct ospf_iface * -ospf_iface_find_by_key(struct ospf_area *oa, struct ifa *a) +void +ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a) { - 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; + struct ospf_proto *p = (struct ospf_proto *) P; - return NULL; + if (a->flags & IA_SECONDARY) + return; + + if (a->scope < SCOPE_LINK) + return; + + /* In OSPFv3, we create OSPF iface for link-local address, + other addresses are used for link-LSA. */ + if (a->scope == SCOPE_LINK) + { + if (flags & IF_CHANGE_UP) + { + struct ospf_mip_walk s = { .iface = a->iface }; + while (ospf_walk_matching_iface_patts(p, &s)) + ospf_iface_new(s.oa, s.a, s.ip); + } + + if (flags & IF_CHANGE_DOWN) + { + struct ospf_iface *ifa, *ifx; + WALK_LIST_DELSAFE(ifa, ifx, p->iface_list) + if ((ifa->addr == a) && (ifa->type != OSPF_IT_VLINK)) + ospf_iface_remove(ifa); + } + } + else + { + struct ospf_iface *ifa; + WALK_LIST(ifa, p->iface_list) + if (ifa->iface == a->iface) + { + /* RFC 5340 4.4.3 Event 5 - prefix added/deleted */ + ospf_notify_link_lsa(ifa); + ospf_notify_rt_lsa(ifa->oa); + } + } } -void -ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) + +static void +ospf_reconfigure_ifaces2(struct ospf_proto *p) { - struct proto *p = &oa->po->proto; - struct ospf_iface_patt *ip; struct iface *iface; struct ifa *a; @@ -1044,126 +1122,33 @@ ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) if (a->scope <= SCOPE_LINK) continue; - if (ip = ospf_iface_patt_find(oa->ac, a)) + struct ospf_mip_walk s = { .iface = iface, .a = a }; + while (ospf_walk_matching_iface_patts(p, &s)) { /* Main inner loop */ - struct ospf_iface *ifa = ospf_iface_find_by_key(oa, a); + struct ospf_iface *ifa = ospf_iface_find_by_key(p, a, s.ip->instance_id); if (ifa) { - if (ospf_iface_reconfigure(ifa, ip)) + if ((ifa->oa == s.oa) && (ifa->marked < 2) && + ospf_iface_reconfigure(ifa, s.ip)) continue; /* Hard restart */ log(L_INFO "%s: Restarting interface %s (%I/%d) in area %R", - p->name, ifa->ifname, a->prefix, a->pxlen, oa->areaid); + p->p.name, ifa->ifname, a->prefix, a->pxlen, s.oa->areaid); ospf_iface_shutdown(ifa); ospf_iface_remove(ifa); } - ospf_iface_new(oa, a, ip); + ospf_iface_new(s.oa, a, s.ip); } } } } - -#else /* OSPFv3 */ - -struct ospf_iface_patt * -ospf_iface_patt_find(struct ospf_area_config *ac, struct iface *iface, int iid) +static void +ospf_reconfigure_ifaces3(struct ospf_proto *p) { - 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; - - if (a->flags & IA_SECONDARY) - return; - - if (a->scope < SCOPE_LINK) - return; - - /* In OSPFv3, we create OSPF iface for link-local address, - other addresses are used for link-LSA. */ - if (a->scope == SCOPE_LINK) - { - if (flags & IF_CHANGE_UP) - { - int done0 = 0; - struct ospf_area *oa; - - WALK_LIST(oa, po->area_list) - { - int iid = 0; - - struct ospf_iface_patt *ip; - while (ip = ospf_iface_patt_find(oa->ac, a->iface, iid)) - { - 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) - { - struct ospf_iface *ifa, *ifx; - WALK_LIST_DELSAFE(ifa, ifx, po->iface_list) - { - if ((ifa->type != OSPF_IT_VLINK) && (ifa->addr == a)) - ospf_iface_remove(ifa); - /* See a note in ospf_iface_notify() */ - } - } - } - else - { - struct ospf_iface *ifa; - WALK_LIST(ifa, po->iface_list) - { - if (ifa->iface == a->iface) - { - schedule_rt_lsa(ifa->oa); - /* Event 5 from RFC5340 4.4.3. */ - schedule_link_lsa(ifa); - return; - } - } - } -} - -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 proto *p = &oa->po->proto; - struct ospf_iface_patt *ip; struct iface *iface; struct ifa *a; @@ -1180,38 +1165,43 @@ ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) if (a->scope != SCOPE_LINK) continue; - int iid = 0; - while (ip = ospf_iface_patt_find(nac, iface, iid)) + struct ospf_mip_walk s = { .iface = iface }; + while (ospf_walk_matching_iface_patts(p, &s)) { - iid = ip->instance_id + 1; - /* Main inner loop */ - struct ospf_iface *ifa = ospf_iface_find_by_key(oa, a, ip->instance_id); + struct ospf_iface *ifa = ospf_iface_find_by_key(p, a, s.ip->instance_id); if (ifa) { - if (ospf_iface_reconfigure(ifa, ip)) + if ((ifa->oa == s.oa) && (ifa->marked < 2) && + ospf_iface_reconfigure(ifa, s.ip)) continue; /* Hard restart */ log(L_INFO "%s: Restarting interface %s (IID %d) in area %R", - p->name, ifa->ifname, ifa->instance_id, oa->areaid); + p->p.name, ifa->ifname, ifa->instance_id, s.oa->areaid); ospf_iface_shutdown(ifa); ospf_iface_remove(ifa); } - ospf_iface_new(oa, a, ip); + ospf_iface_new(s.oa, a, s.ip); } } } } -#endif +void +ospf_reconfigure_ifaces(struct ospf_proto *p) +{ + if (ospf_is_v2(p)) + ospf_reconfigure_ifaces2(p); + else + ospf_reconfigure_ifaces3(p); +} + static void -ospf_iface_change_mtu(struct proto_ospf *po, struct ospf_iface *ifa) +ospf_iface_change_mtu(struct ospf_proto *p, struct ospf_iface *ifa) { - struct proto *p = &po->proto; - /* ifa is not vlink */ OSPF_TRACE(D_EVENTS, "Changing MTU on interface %s", ifa->ifname); @@ -1230,7 +1220,7 @@ ospf_iface_change_mtu(struct proto_ospf *po, struct ospf_iface *ifa) } static void -ospf_iface_notify(struct proto_ospf *po, unsigned flags, struct ospf_iface *ifa) +ospf_iface_notify(struct ospf_proto *p, uint flags, struct ospf_iface *ifa) { /* ifa is not vlink */ @@ -1244,13 +1234,13 @@ ospf_iface_notify(struct proto_ospf *po, unsigned flags, struct ospf_iface *ifa) ospf_iface_sm(ifa, (ifa->iface->flags & IF_LINK_UP) ? ISM_UNLOOP : ISM_LOOP); if (flags & IF_CHANGE_MTU) - ospf_iface_change_mtu(po, ifa); + ospf_iface_change_mtu(p, ifa); } void -ospf_if_notify(struct proto *p, unsigned flags, struct iface *iface) +ospf_if_notify(struct proto *P, uint flags, struct iface *iface) { - struct proto_ospf *po = (struct proto_ospf *) p; + struct ospf_proto *p = (struct ospf_proto *) P; /* if (iface->flags & IF_IGNORE) @@ -1262,9 +1252,9 @@ ospf_if_notify(struct proto *p, unsigned flags, struct iface *iface) return; struct ospf_iface *ifa, *ifx; - WALK_LIST_DELSAFE(ifa, ifx, po->iface_list) + WALK_LIST_DELSAFE(ifa, ifx, p->iface_list) if (ifa->iface == iface) - ospf_iface_notify(po, flags, ifa); + ospf_iface_notify(p, flags, ifa); /* We use here that even shutting down iface also shuts down the vlinks, but vlinks are not freed and stays in the @@ -1286,20 +1276,19 @@ ospf_iface_info(struct ospf_iface *ifa) if (ifa->type == OSPF_IT_VLINK) { - cli_msg(-1015, "Virtual link %s to %R:", ifa->ifname, ifa->vid); + cli_msg(-1015, "Virtual link %s to %R", ifa->ifname, ifa->vid); cli_msg(-1015, "\tPeer IP: %I", ifa->vip); cli_msg(-1015, "\tTransit area: %R (%u)", ifa->voa->areaid, ifa->voa->areaid); } else { -#ifdef OSPFv2 - if (ifa->addr->flags & IA_PEER) + if (ospf_is_v3(ifa->oa->po)) + cli_msg(-1015, "Interface %s (IID %d)", ifa->ifname, ifa->instance_id); + else if (ifa->addr->flags & IA_PEER) cli_msg(-1015, "Interface %s (peer %I)", ifa->ifname, ifa->addr->opposite); else cli_msg(-1015, "Interface %s (%I/%d)", ifa->ifname, ifa->addr->prefix, ifa->addr->pxlen); -#else /* OSPFv3 */ - cli_msg(-1015, "Interface %s (IID %d)", ifa->ifname, ifa->instance_id); -#endif + cli_msg(-1015, "\tType: %s%s", ospf_it[ifa->type], more); cli_msg(-1015, "\tArea: %R (%u)", ifa->oa->areaid, ifa->oa->areaid); } diff --git a/proto/ospf/iface.h b/proto/ospf/iface.h deleted file mode 100644 index 5a250e0a..00000000 --- a/proto/ospf/iface.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * BIRD -- OSPF - * - * (c) 1999--2005 Ondrej Filip - * - * Can be freely distributed and used under the terms of the GNU GPL. - * - */ - -#ifndef _BIRD_OSPF_IFACE_H_ -#define _BIRD_OSPF_IFACE_H_ - -void ospf_iface_chstate(struct ospf_iface *ifa, u8 state); -void ospf_iface_sm(struct ospf_iface *ifa, int event); -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_new_vlink(struct proto_ospf *po, struct ospf_iface_patt *ip); -void ospf_iface_remove(struct ospf_iface *ifa); -void ospf_iface_shutdown(struct ospf_iface *ifa); -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); - -int ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen); - -void ospf_open_vlink_sk(struct proto_ospf *po); - -struct nbma_node *find_nbma_node_in(list *nnl, ip_addr ip); - -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/lsack.c b/proto/ospf/lsack.c index fd8ead01..aefddfb8 100644 --- a/proto/ospf/lsack.c +++ b/proto/ospf/lsack.c @@ -1,7 +1,9 @@ /* * BIRD -- OSPF * - * (c) 2000-2004 Ondrej Filip + * (c) 2000--2004 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -9,184 +11,175 @@ #include "ospf.h" +/* struct ospf_lsack_packet { - struct ospf_packet ospf_packet; - struct ospf_lsa_header lsh[]; + struct ospf_packet hdr; + // union ospf_auth auth; + + struct ospf_lsa_header lsas[]; +}; +*/ + +struct lsa_node +{ + node n; + struct ospf_lsa_header lsa; }; -char *s_queue[] = { "direct", "delayed" }; - - -static void ospf_dump_lsack(struct proto *p, struct ospf_lsack_packet *pkt) +static inline void +ospf_lsack_body(struct ospf_proto *p, struct ospf_packet *pkt, + struct ospf_lsa_header **body, uint *count) { - struct ospf_packet *op = &pkt->ospf_packet; + uint plen = ntohs(pkt->length); + uint hlen = ospf_pkt_hdrlen(p); - ASSERT(op->type == LSACK_P); - ospf_dump_common(p, op); + *body = ((void *) pkt) + hlen; + *count = (plen - hlen) / sizeof(struct ospf_lsa_header); +} - unsigned int i, j; - j = (ntohs(op->length) - sizeof(struct ospf_lsack_packet)) / - sizeof(struct ospf_lsa_header); +static void +ospf_dump_lsack(struct ospf_proto *p, struct ospf_packet *pkt) +{ + struct ospf_lsa_header *lsas; + uint i, lsa_count; - for (i = 0; i < j; i++) - ospf_dump_lsahdr(p, pkt->lsh + i); + ASSERT(pkt->type == LSACK_P); + ospf_dump_common(p, pkt); + + ospf_lsack_body(p, pkt, &lsas, &lsa_count); + for (i = 0; i < lsa_count; i++) + ospf_dump_lsahdr(p, lsas + i); } -/* - * ===================================== - * Note, that h is in network endianity! - * ===================================== - */ - void -ospf_lsack_enqueue(struct ospf_neighbor *n, struct ospf_lsa_header *h, - int queue) +ospf_enqueue_lsack(struct ospf_neighbor *n, struct ospf_lsa_header *h_n, int queue) { - struct lsah_n *no = mb_alloc(n->pool, sizeof(struct lsah_n)); - memcpy(&no->lsa, h, sizeof(struct ospf_lsa_header)); + /* Note that h_n is in network endianity */ + struct lsa_node *no = mb_alloc(n->pool, sizeof(struct lsa_node)); + memcpy(&no->lsa, h_n, sizeof(struct ospf_lsa_header)); add_tail(&n->ackl[queue], NODE no); - DBG("Adding (%s) ack for %R, ID: %R, RT: %R, Type: %u\n", s_queue[queue], - n->rid, ntohl(h->id), ntohl(h->rt), h->type); + DBG("Adding %s ack for %R, ID: %R, RT: %R, Type: %u\n", + (queue == ACKL_DIRECT) ? "direct" : "delayed", + n->rid, ntohl(h_n->id), ntohl(h_n->rt), h_n->type); } void -ospf_lsack_send(struct ospf_neighbor *n, int queue) +ospf_reset_lsack_queue(struct ospf_neighbor *n) { - struct ospf_packet *op; - struct ospf_lsack_packet *pk; - u16 len, i = 0; - struct ospf_lsa_header *h; - struct lsah_n *no; - struct ospf_iface *ifa = n->ifa; - struct proto *p = &n->ifa->oa->po->proto; + struct lsa_node *no; - if (EMPTY_LIST(n->ackl[queue])) - return; - - pk = ospf_tx_buffer(ifa); - op = &pk->ospf_packet; - - ospf_pkt_fill_hdr(n->ifa, pk, LSACK_P); - h = pk->lsh; - - while (!EMPTY_LIST(n->ackl[queue])) + WALK_LIST_FIRST(no, n->ackl[ACKL_DELAY]) { - no = (struct lsah_n *) HEAD(n->ackl[queue]); - memcpy(h + i, &no->lsa, sizeof(struct ospf_lsa_header)); - DBG("Iter %u ID: %R, RT: %R, Type: %04x\n", i, ntohl((h + i)->id), - ntohl((h + i)->rt), (h + i)->type); - i++; rem_node(NODE no); mb_free(no); - if ((i * sizeof(struct ospf_lsa_header) + - sizeof(struct ospf_lsack_packet)) > ospf_pkt_maxsize(n->ifa)) - { - if (!EMPTY_LIST(n->ackl[queue])) - { - len = - sizeof(struct ospf_lsack_packet) + - i * sizeof(struct ospf_lsa_header); - op->length = htons(len); - DBG("Sending and continuing! Len=%u\n", len); + } +} - OSPF_PACKET(ospf_dump_lsack, pk, "LSACK packet sent via %s", ifa->ifname); +static inline void +ospf_send_lsack(struct ospf_neighbor *n, int queue) +{ + struct ospf_iface *ifa = n->ifa; + struct ospf_proto *p = ifa->oa->po; + struct ospf_lsa_header *lsas; + struct ospf_packet *pkt; + struct lsa_node *no; + uint i, lsa_max, length; - if (ifa->type == OSPF_IT_BCAST) - { - if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP)) - ospf_send_to_all(ifa); - else if (ifa->cf->real_bcast) - ospf_send_to_bdr(ifa); - else - ospf_send_to(ifa, AllDRouters); - } - else - { - if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP)) - ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE); - else - ospf_send_to_bdr(ifa); - } + /* RFC 2328 13.5 */ - ospf_pkt_fill_hdr(n->ifa, pk, LSACK_P); - i = 0; - } - } + pkt = ospf_tx_buffer(ifa); + ospf_pkt_fill_hdr(ifa, pkt, LSACK_P); + ospf_lsack_body(p, pkt, &lsas, &lsa_max); + + for (i = 0; i < lsa_max && !EMPTY_LIST(n->ackl[queue]); i++) + { + no = (struct lsa_node *) HEAD(n->ackl[queue]); + memcpy(&lsas[i], &no->lsa, sizeof(struct ospf_lsa_header)); + DBG("Iter %u ID: %R, RT: %R, Type: %04x\n", + i, ntohl(lsas[i].id), ntohl(lsas[i].rt), lsas[i].type); + rem_node(NODE no); + mb_free(no); } - len = sizeof(struct ospf_lsack_packet) + i * sizeof(struct ospf_lsa_header); - op->length = htons(len); - DBG("Sending! Len=%u\n", len); + length = ospf_pkt_hdrlen(p) + i * sizeof(struct ospf_lsa_header); + pkt->length = htons(length); - OSPF_PACKET(ospf_dump_lsack, pk, "LSACK packet sent via %s", ifa->ifname); + OSPF_PACKET(ospf_dump_lsack, pkt, "LSACK packet sent via %s", ifa->ifname); if (ifa->type == OSPF_IT_BCAST) { if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP)) ospf_send_to_all(ifa); - else if (ifa->cf->real_bcast) - ospf_send_to_bdr(ifa); else - ospf_send_to(ifa, AllDRouters); + ospf_send_to_des(ifa); } else ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE); } void -ospf_lsack_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, +ospf_lsack_send(struct ospf_neighbor *n, int queue) +{ + while (!EMPTY_LIST(n->ackl[queue])) + ospf_send_lsack(n, queue); +} + +void +ospf_receive_lsack(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n) { - struct proto *p = &ifa->oa->po->proto; - struct ospf_lsa_header lsa; - struct top_hash_entry *en; - unsigned int i, lsano; + struct ospf_proto *p = ifa->oa->po; + struct ospf_lsa_header lsa, *lsas; + struct top_hash_entry *ret, *en; + uint i, lsa_count; + u32 lsa_type, lsa_domain; - unsigned int size = ntohs(ps_i->length); - if (size < sizeof(struct ospf_lsack_packet)) - { - log(L_ERR "Bad OSPF LSACK packet from %I - too short (%u B)", n->ip, size); - return; - } + /* RFC 2328 13.7 */ - struct ospf_lsack_packet *ps = (void *) ps_i; - OSPF_PACKET(ospf_dump_lsack, ps, "LSACK packet received from %I via %s", n->ip, ifa->ifname); + /* No need to check length, lsack has only basic header */ - ospf_neigh_sm(n, INM_HELLOREC); + OSPF_PACKET(ospf_dump_lsack, pkt, "LSACK packet received from %I via %s", n->ip, ifa->ifname); if (n->state < NEIGHBOR_EXCHANGE) return; - lsano = (size - sizeof(struct ospf_lsack_packet)) / - sizeof(struct ospf_lsa_header); - for (i = 0; i < lsano; i++) - { - ntohlsah(ps->lsh + i, &lsa); - u32 dom = ospf_lsa_domain(lsa.type, n->ifa); - if ((en = ospf_hash_find_header(n->lsrth, dom, &lsa)) == NULL) - continue; /* pg 155 */ + ospf_neigh_sm(n, INM_HELLOREC); /* Not in RFC */ - if (lsa_comp(&lsa, &en->lsa) != CMP_SAME) /* pg 156 */ + ospf_lsack_body(p, pkt, &lsas, &lsa_count); + for (i = 0; i < lsa_count; i++) + { + lsa_ntoh_hdr(&lsas[i], &lsa); + lsa_get_type_domain(&lsa, n->ifa, &lsa_type, &lsa_domain); + + ret = ospf_hash_find(n->lsrth, lsa_domain, lsa.id, lsa.rt, lsa_type); + if (!ret) + continue; + + if (lsa_comp(&lsa, &ret->lsa) != CMP_SAME) { if ((lsa.sn == LSA_MAXSEQNO) && (lsa.age == LSA_MAXAGE)) continue; OSPF_TRACE(D_PACKETS, "Strange LSACK from %I", n->ip); OSPF_TRACE(D_PACKETS, "Type: %04x, Id: %R, Rt: %R", - lsa.type, lsa.id, lsa.rt); + lsa_type, lsa.id, lsa.rt); OSPF_TRACE(D_PACKETS, "I have: Age: %4u, Seq: %08x, Sum: %04x", - en->lsa.age, en->lsa.sn, en->lsa.checksum); + ret->lsa.age, ret->lsa.sn, ret->lsa.checksum); OSPF_TRACE(D_PACKETS, "He has: Age: %4u, Seq: %08x, Sum: %04x", lsa.age, lsa.sn, lsa.checksum); continue; } - DBG("Deleting LS Id: %R RT: %R Type: %u from LS Retl for neighbor %R\n", - lsa.id, lsa.rt, lsa.type, n->rid); - s_rem_node(SNODE en); - ospf_hash_delete(n->lsrth, en); + en = ospf_hash_find_entry(p->gr, ret); + if (en) + en->ret_count--; + + DBG("Deleting LSA (Type: %04x Id: %R Rt: %R) from lsrtl for neighbor %R\n", + lsa_type, lsa.id, lsa.rt, n->rid); + s_rem_node(SNODE ret); + ospf_hash_delete(n->lsrth, ret); } } diff --git a/proto/ospf/lsack.h b/proto/ospf/lsack.h deleted file mode 100644 index 63a436d6..00000000 --- a/proto/ospf/lsack.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * BIRD -- OSPF - * - * (c) 2000--2004 Ondrej Filip - * - * Can be freely distributed and used under the terms of the GNU GPL. - * - */ - -#ifndef _BIRD_OSPF_LSACK_H_ -#define _BIRD_OSPF_LSACK_H_ - -struct lsah_n -{ - node n; - struct ospf_lsa_header lsa; -}; - -void ospf_lsack_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, - struct ospf_neighbor *n); -void ospf_lsack_send(struct ospf_neighbor *n, int queue); -void ospf_lsack_enqueue(struct ospf_neighbor *n, struct ospf_lsa_header *h, - int queue); - -#endif /* _BIRD_OSPF_LSACK_H_ */ diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c index bcf7bcdd..e1af9f46 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -2,103 +2,21 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ #include "ospf.h" -void -flush_lsa(struct top_hash_entry *en, struct proto_ospf *po) -{ - struct proto *p = &po->proto; - - OSPF_TRACE(D_EVENTS, - "Going to remove LSA Type: %04x, Id: %R, Rt: %R, Age: %u, Seqno: 0x%x", - en->lsa.type, en->lsa.id, en->lsa.rt, en->lsa.age, en->lsa.sn); - s_rem_node(SNODE en); - if (en->lsa_body != NULL) - mb_free(en->lsa_body); - en->lsa_body = NULL; - 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 - * - * This function is periodicaly invoked from ospf_disp(). It computes the new - * age of all LSAs and old (@age is higher than %LSA_MAXAGE) LSAs are flushed - * whenever possible. If an LSA originated by the router itself is older - * than %LSREFRESHTIME a new instance is originated. - * - * The RFC says that a router should check the checksum of every LSA to detect - * hardware problems. BIRD does not do this to minimalize CPU utilization. - * - * If routing table calculation is scheduled, it also invalidates the old routing - * table calculation results. - */ -void -ospf_age(struct proto_ospf *po) -{ - struct proto *p = &po->proto; - struct top_hash_entry *en, *nxt; - int flush = can_flush_lsa(po); - - WALK_SLIST_DELSAFE(en, nxt, po->lsal) - { - if (en->lsa.age == LSA_MAXAGE) - { - if (flush) - flush_lsa(en, po); - continue; - } - if ((en->lsa.rt == po->router_id) && (en->lsa.age >= LSREFRESHTIME)) - { - OSPF_TRACE(D_EVENTS, "Refreshing my LSA: Type: %u, Id: %R, Rt: %R", - en->lsa.type, en->lsa.id, en->lsa.rt); - en->lsa.sn++; - en->lsa.age = 0; - en->inst_t = now; - en->ini_age = 0; - lsasum_calculate(&en->lsa, en->lsa_body); - ospf_lsupd_flood(po, NULL, NULL, &en->lsa, en->domain, 1); - continue; - } - if ((en->lsa.age = (en->ini_age + (now - en->inst_t))) >= LSA_MAXAGE) - { - if (flush) - { - flush_lsa(en, po); - schedule_rtcalc(po); - } - else - en->lsa.age = LSA_MAXAGE; - } - } -} #ifndef CPU_BIG_ENDIAN void -htonlsah(struct ospf_lsa_header *h, struct ospf_lsa_header *n) +lsa_hton_hdr(struct ospf_lsa_header *h, struct ospf_lsa_header *n) { n->age = htons(h->age); -#ifdef OSPFv2 - n->options = h->options; -#endif - n->type = htont(h->type); + n->type_raw = htons(h->type_raw); n->id = htonl(h->id); n->rt = htonl(h->rt); n->sn = htonl(h->sn); @@ -107,13 +25,10 @@ htonlsah(struct ospf_lsa_header *h, struct ospf_lsa_header *n) } void -ntohlsah(struct ospf_lsa_header *n, struct ospf_lsa_header *h) +lsa_ntoh_hdr(struct ospf_lsa_header *n, struct ospf_lsa_header *h) { h->age = ntohs(n->age); -#ifdef OSPFv2 - h->options = n->options; -#endif - h->type = ntoht(n->type); + h->type_raw = ntohs(n->type_raw); h->id = ntohl(n->id); h->rt = ntohl(n->rt); h->sn = ntohl(n->sn); @@ -122,28 +37,120 @@ ntohlsah(struct ospf_lsa_header *n, struct ospf_lsa_header *h) } void -htonlsab(void *h, void *n, u16 len) +lsa_hton_body(void *h, void *n, u16 len) { u32 *hid = h; u32 *nid = n; - unsigned i; + uint i; for (i = 0; i < (len / sizeof(u32)); i++) nid[i] = htonl(hid[i]); } void -ntohlsab(void *n, void *h, u16 len) +lsa_ntoh_body(void *n, void *h, u16 len) { u32 *nid = n; u32 *hid = h; - unsigned i; + uint i; for (i = 0; i < (len / sizeof(u32)); i++) hid[i] = ntohl(nid[i]); } #endif /* little endian */ + + +int +lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa) +{ + /* Handle inactive vlinks */ + if (ifa->state == OSPF_IS_DOWN) + return 0; + + /* 4.5.2 (Case 2) */ + switch (LSA_SCOPE(type)) + { + case LSA_SCOPE_LINK: + return ifa->iface_id == domain; + + case LSA_SCOPE_AREA: + return ifa->oa->areaid == domain; + + case LSA_SCOPE_AS: + if (ifa->type == OSPF_IT_VLINK) + return 0; + if (!oa_is_ext(ifa->oa)) + return 0; + return 1; + + default: + log(L_ERR "OSPF: LSA with invalid scope"); + return 0; + } +} + + +static int +unknown_lsa_type(u32 type) +{ + switch (type) + { + case LSA_T_RT: + case LSA_T_NET: + case LSA_T_SUM_NET: + case LSA_T_SUM_RT: + case LSA_T_EXT: + case LSA_T_NSSA: + case LSA_T_LINK: + case LSA_T_PREFIX: + return 0; + + default: + return 1; + } +} + +#define LSA_V2_TMAX 8 +static const u16 lsa_v2_types[LSA_V2_TMAX] = + {0, LSA_T_RT, LSA_T_NET, LSA_T_SUM_NET, LSA_T_SUM_RT, LSA_T_EXT, 0, LSA_T_NSSA}; + +void +lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain) +{ + if (ospf_is_v2(ifa->oa->po)) + { + itype = itype & LSA_T_V2_MASK; + itype = (itype < LSA_V2_TMAX) ? lsa_v2_types[itype] : 0; + } + else + { + /* For unkown LSAs without U-bit change scope to LSA_SCOPE_LINK */ + if (unknown_lsa_type(itype) && !(itype & LSA_UBIT)) + itype = itype & ~LSA_SCOPE_MASK; + } + + *otype = itype; + + switch (LSA_SCOPE(itype)) + { + case LSA_SCOPE_LINK: + *domain = ifa->iface_id; + return; + + case LSA_SCOPE_AREA: + *domain = ifa->oa->areaid; + return; + + case LSA_SCOPE_AS: + default: + *domain = 0; + return; + } +} + + + /* void buf_dump(const char *hdr, const byte *buf, int blen) @@ -188,8 +195,8 @@ lsasum_calculate(struct ospf_lsa_header *h, void *body) u16 length = h->length; // log(L_WARN "Checksum %R %R %d start (len %d)", h->id, h->rt, h->type, length); - htonlsah(h, h); - htonlsab1(body, length - sizeof(struct ospf_lsa_header)); + lsa_hton_hdr(h, h); + lsa_hton_body1(body, length - sizeof(struct ospf_lsa_header)); /* char buf[1024]; @@ -202,8 +209,8 @@ lsasum_calculate(struct ospf_lsa_header *h, void *body) // log(L_WARN "Checksum result %4x", h->checksum); - ntohlsah(h, h); - ntohlsab1(body, length - sizeof(struct ospf_lsa_header)); + lsa_ntoh_hdr(h, h); + lsa_ntoh_body1(body, length - sizeof(struct ospf_lsa_header)); } /* @@ -292,33 +299,204 @@ lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2) return CMP_SAME; } + +static inline int +lsa_walk_rt2(struct ospf_lsa_rt_walk *rt) +{ + if (rt->buf >= rt->bufend) + return 0; + + struct ospf_lsa_rt2_link *l = rt->buf; + rt->buf += sizeof(struct ospf_lsa_rt2_link) + l->no_tos * sizeof(struct ospf_lsa_rt2_tos); + + rt->type = l->type; + rt->metric = l->metric; + rt->id = l->id; + rt->data = l->data; + return 1; +} + +static inline int +lsa_walk_rt3(struct ospf_lsa_rt_walk *rt) +{ + while (rt->buf >= rt->bufend) + { + rt->en = ospf_hash_find_rt3_next(rt->en); + if (!rt->en) + return 0; + + rt->buf = rt->en->lsa_body; + rt->bufend = rt->buf + rt->en->lsa.length - sizeof(struct ospf_lsa_header); + rt->buf += sizeof(struct ospf_lsa_rt); + } + + struct ospf_lsa_rt3_link *l = rt->buf; + rt->buf += sizeof(struct ospf_lsa_rt3_link); + + rt->type = l->type; + rt->metric = l->metric; + rt->lif = l->lif; + rt->nif = l->nif; + rt->id = l->id; + return 1; +} + +void +lsa_walk_rt_init(struct ospf_proto *p, struct top_hash_entry *act, struct ospf_lsa_rt_walk *rt) +{ + rt->ospf2 = ospf_is_v2(p); + rt->id = rt->data = rt->lif = rt->nif = 0; + + if (rt->ospf2) + rt->en = act; + else + rt->en = ospf_hash_find_rt3_first(p->gr, act->domain, act->lsa.rt); + + rt->buf = rt->en->lsa_body; + rt->bufend = rt->buf + rt->en->lsa.length - sizeof(struct ospf_lsa_header); + rt->buf += sizeof(struct ospf_lsa_rt); +} + +int +lsa_walk_rt(struct ospf_lsa_rt_walk *rt) +{ + return rt->ospf2 ? lsa_walk_rt2(rt) : lsa_walk_rt3(rt); +} + + +void +lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, ip_addr *ip, int *pxlen, u8 *pxopts, u32 *metric) +{ + if (ospf2) + { + struct ospf_lsa_sum2 *ls = en->lsa_body; + *ip = ipa_from_u32(en->lsa.id & ls->netmask); + *pxlen = u32_masklen(ls->netmask); + *pxopts = 0; + *metric = ls->metric & LSA_METRIC_MASK; + } + else + { + struct ospf_lsa_sum3_net *ls = en->lsa_body; + u16 rest; + lsa_get_ipv6_prefix(ls->prefix, ip, pxlen, pxopts, &rest); + *metric = ls->metric & LSA_METRIC_MASK; + } +} + +void +lsa_parse_sum_rt(struct top_hash_entry *en, int ospf2, u32 *drid, u32 *metric, u32 *options) +{ + if (ospf2) + { + struct ospf_lsa_sum2 *ls = en->lsa_body; + *drid = en->lsa.id; + *metric = ls->metric & LSA_METRIC_MASK; + *options = 0; + } + else + { + struct ospf_lsa_sum3_rt *ls = en->lsa_body; + *drid = ls->drid; + *metric = ls->metric & LSA_METRIC_MASK; + *options = ls->options & LSA_OPTIONS_MASK; + } +} + +void +lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *rt) +{ + if (ospf2) + { + struct ospf_lsa_ext2 *ext = en->lsa_body; + rt->ip = ipa_from_u32(en->lsa.id & ext->netmask); + rt->pxlen = u32_masklen(ext->netmask); + rt->pxopts = 0; + rt->metric = ext->metric & LSA_METRIC_MASK; + rt->ebit = ext->metric & LSA_EXT2_EBIT; + + rt->fbit = ext->fwaddr; + rt->fwaddr = ipa_from_u32(ext->fwaddr); + + rt->tag = ext->tag; + rt->propagate = lsa_get_options(&en->lsa) & OPT_P; + } + else + { + struct ospf_lsa_ext3 *ext = en->lsa_body; + u16 rest; + u32 *buf = lsa_get_ipv6_prefix(ext->rest, &rt->ip, &rt->pxlen, &rt->pxopts, &rest); + rt->metric = ext->metric & LSA_METRIC_MASK; + rt->ebit = ext->metric & LSA_EXT3_EBIT; + + rt->fbit = ext->metric & LSA_EXT3_FBIT; + if (rt->fbit) + buf = lsa_get_ipv6_addr(buf, &rt->fwaddr); + else + rt->fwaddr = IPA_NONE; + + rt->tag = (ext->metric & LSA_EXT3_TBIT) ? *buf++ : 0; + rt->propagate = rt->pxopts & OPT_PX_P; + } +} + #define HDRLEN sizeof(struct ospf_lsa_header) static int -lsa_validate_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body) +lsa_validate_rt2(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body) { - unsigned int i, max; - if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_rt))) return 0; - struct ospf_lsa_rt_link *rtl = (struct ospf_lsa_rt_link *) (body + 1); - max = lsa_rt_count(lsa); + uint i = 0; + void *buf = body; + void *bufend = buf + lsa->length - HDRLEN; + buf += sizeof(struct ospf_lsa_rt); -#ifdef OSPFv2 - if (body->links != max) - return 0; -#endif - - for (i = 0; i < max; i++) + while (buf < bufend) { - u8 type = rtl[i].type; - if (!((type == LSART_PTP) || - (type == LSART_NET) || -#ifdef OSPFv2 - (type == LSART_STUB) || -#endif - (type == LSART_VLNK))) + struct ospf_lsa_rt2_link *l = buf; + buf += sizeof(struct ospf_lsa_rt2_link) + l->no_tos * sizeof(struct ospf_lsa_rt2_tos); + i++; + + if (buf > bufend) + return 0; + + if (!((l->type == LSART_PTP) || + (l->type == LSART_NET) || + (l->type == LSART_STUB) || + (l->type == LSART_VLNK))) + return 0; + } + + if ((body->options & LSA_RT2_LINKS) != i) + return 0; + + return 1; +} + + +static int +lsa_validate_rt3(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body) +{ + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_rt))) + return 0; + + void *buf = body; + void *bufend = buf + lsa->length - HDRLEN; + buf += sizeof(struct ospf_lsa_rt); + + while (buf < bufend) + { + struct ospf_lsa_rt3_link *l = buf; + buf += sizeof(struct ospf_lsa_rt3_link); + + if (buf > bufend) + return 0; + + if (!((l->type == LSART_PTP) || + (l->type == LSART_NET) || + (l->type == LSART_VLNK))) return 0; } return 1; @@ -333,37 +511,18 @@ lsa_validate_net(struct ospf_lsa_header *lsa, struct ospf_lsa_net *body UNUSED) return 1; } -#ifdef OSPFv2 - static int -lsa_validate_sum(struct ospf_lsa_header *lsa, struct ospf_lsa_sum *body) +lsa_validate_sum2(struct ospf_lsa_header *lsa, struct ospf_lsa_sum2 *body) { - if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum))) + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum2))) return 0; /* First field should have TOS = 0, we ignore other TOS fields */ - if ((body->metric & LSA_SUM_TOS) != 0) + if ((body->metric & LSA_SUM2_TOS) != 0) return 0; return 1; } -#define lsa_validate_sum_net(A,B) lsa_validate_sum(A,B) -#define lsa_validate_sum_rt(A,B) lsa_validate_sum(A,B) - -static int -lsa_validate_ext(struct ospf_lsa_header *lsa, struct ospf_lsa_ext *body) -{ - if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext))) - return 0; - - /* First field should have TOS = 0, we ignore other TOS fields */ - if ((body->metric & LSA_EXT_TOS) != 0) - return 0; - - return 1; -} - -#else /* OSPFv3 */ static inline int pxlen(u32 *buf) @@ -372,36 +531,48 @@ pxlen(u32 *buf) } static int -lsa_validate_sum_net(struct ospf_lsa_header *lsa, struct ospf_lsa_sum_net *body) +lsa_validate_sum3_net(struct ospf_lsa_header *lsa, struct ospf_lsa_sum3_net *body) { - if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum_net) + 4)) + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum3_net) + 4)) return 0; u8 pxl = pxlen(body->prefix); if (pxl > MAX_PREFIX_LENGTH) return 0; - if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum_net) + + if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum3_net) + IPV6_PREFIX_SPACE(pxl))) return 0; return 1; } - static int -lsa_validate_sum_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_sum_rt *body) +lsa_validate_sum3_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_sum3_rt *body) { - if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum_rt))) + if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum3_rt))) return 0; return 1; } static int -lsa_validate_ext(struct ospf_lsa_header *lsa, struct ospf_lsa_ext *body) +lsa_validate_ext2(struct ospf_lsa_header *lsa, struct ospf_lsa_ext2 *body) { - if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext) + 4)) + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext2))) + return 0; + + /* First field should have TOS = 0, we ignore other TOS fields */ + if ((body->metric & LSA_EXT2_TOS) != 0) + return 0; + + return 1; +} + +static int +lsa_validate_ext3(struct ospf_lsa_header *lsa, struct ospf_lsa_ext3 *body) +{ + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext3) + 4)) return 0; u8 pxl = pxlen(body->rest); @@ -409,23 +580,23 @@ lsa_validate_ext(struct ospf_lsa_header *lsa, struct ospf_lsa_ext *body) return 0; int len = IPV6_PREFIX_SPACE(pxl); - if (body->metric & LSA_EXT_FBIT) // forwardinf address + if (body->metric & LSA_EXT3_FBIT) // forwardinf address len += 16; - if (body->metric & LSA_EXT_TBIT) // route tag + if (body->metric & LSA_EXT3_TBIT) // route tag len += 4; if (*body->rest & 0xFFFF) // referenced LS type field len += 4; - if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_ext) + len)) + if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_ext3) + len)) return 0; return 1; } static int -lsa_validate_pxlist(struct ospf_lsa_header *lsa, u32 pxcount, unsigned int offset, u8 *pbuf) +lsa_validate_pxlist(struct ospf_lsa_header *lsa, u32 pxcount, uint offset, u8 *pbuf) { - unsigned int bound = lsa->length - HDRLEN - 4; + uint bound = lsa->length - HDRLEN - 4; u32 i; for (i = 0; i < pxcount; i++) @@ -464,8 +635,6 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body) return lsa_validate_pxlist(lsa, body->pxcount, sizeof(struct ospf_lsa_prefix), (u8 *) body); } -#endif - /** * lsa_validate - check whether given LSA is valid @@ -477,85 +646,48 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body) */ int -lsa_validate(struct ospf_lsa_header *lsa, void *body) +lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body) { - switch (lsa->type) + if (ospf2) + { + switch (lsa_type) { case LSA_T_RT: - return lsa_validate_rt(lsa, body); + return lsa_validate_rt2(lsa, body); case LSA_T_NET: return lsa_validate_net(lsa, body); case LSA_T_SUM_NET: - return lsa_validate_sum_net(lsa, body); + return lsa_validate_sum2(lsa, body); case LSA_T_SUM_RT: - return lsa_validate_sum_rt(lsa, body); + return lsa_validate_sum2(lsa, body); case LSA_T_EXT: case LSA_T_NSSA: - return lsa_validate_ext(lsa, body); -#ifdef OSPFv3 + return lsa_validate_ext2(lsa, body); + default: + return 0; /* Should not happen, unknown LSAs are already rejected */ + } + } + else + { + switch (lsa_type) + { + case LSA_T_RT: + return lsa_validate_rt3(lsa, body); + case LSA_T_NET: + return lsa_validate_net(lsa, body); + case LSA_T_SUM_NET: + return lsa_validate_sum3_net(lsa, body); + case LSA_T_SUM_RT: + return lsa_validate_sum3_rt(lsa, body); + case LSA_T_EXT: + case LSA_T_NSSA: + return lsa_validate_ext3(lsa, body); case LSA_T_LINK: return lsa_validate_link(lsa, body); case LSA_T_PREFIX: return lsa_validate_prefix(lsa, body); -#endif default: - /* In OSPFv3, unknown LSAs are OK, - In OSPFv2, unknown LSAs are already rejected - */ - return 1; + return 1; /* Unknown LSAs are OK in OSPFv3 */ } -} - -/** - * lsa_install_new - install new LSA into database - * @po: OSPF protocol - * @lsa: LSA header - * @domain: domain of LSA - * @body: pointer to LSA body - * - * This function ensures installing new LSA into LSA database. Old instance is - * replaced. Several actions are taken to detect if new routing table - * calculation is necessary. This is described in 13.2 of RFC 2328. - */ -struct top_hash_entry * -lsa_install_new(struct proto_ospf *po, struct ospf_lsa_header *lsa, u32 domain, void *body) -{ - /* LSA can be temporarrily, but body must be mb_allocated. */ - int change = 0; - struct top_hash_entry *en; - - if ((en = ospf_hash_find_header(po->gr, domain, lsa)) == NULL) - { - en = ospf_hash_get_header(po->gr, domain, lsa); - change = 1; } - else - { - if ((en->lsa.length != lsa->length) -#ifdef OSPFv2 - || (en->lsa.options != lsa->options) -#endif - || (en->lsa.age == LSA_MAXAGE) - || (lsa->age == LSA_MAXAGE) - || memcmp(en->lsa_body, body, lsa->length - sizeof(struct ospf_lsa_header))) - change = 1; - - s_rem_node(SNODE en); - } - - DBG("Inst lsa: Id: %R, Rt: %R, Type: %u, Age: %u, Sum: %u, Sn: 0x%x\n", - lsa->id, lsa->rt, lsa->type, lsa->age, lsa->checksum, lsa->sn); - - s_add_tail(&po->lsal, SNODE en); - en->inst_t = now; - if (en->lsa_body != NULL) - mb_free(en->lsa_body); - en->lsa_body = body; - memcpy(&en->lsa, lsa, sizeof(struct ospf_lsa_header)); - en->ini_age = en->lsa.age; - - if (change) - schedule_rtcalc(po); - - return en; } diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h index 0b556ec5..d9e1a610 100644 --- a/proto/ospf/lsalib.h +++ b/proto/ospf/lsalib.h @@ -1,42 +1,63 @@ /* * BIRD -- OSPF * - * (c) 1999 - 2000 Ondrej Filip + * (c) 1999--2000 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. - * */ #ifndef _BIRD_OSPF_LSALIB_H_ #define _BIRD_OSPF_LSALIB_H_ #ifdef CPU_BIG_ENDIAN -static inline void htonlsah(struct ospf_lsa_header *h, struct ospf_lsa_header *n) { *n = *h; }; -static inline void ntohlsah(struct ospf_lsa_header *n, struct ospf_lsa_header *h) { *h = *n; }; -static inline void htonlsab(void *h, void *n, u16 len) { ASSERT(h != n); memcpy(n, h, len); }; -static inline void ntohlsab(void *n, void *h, u16 len) { ASSERT(n != h); memcpy(h, n, len); }; -static inline void htonlsab1(void *h, u16 len) { }; -static inline void ntohlsab1(void *n, u16 len) { }; +static inline void lsa_hton_hdr(struct ospf_lsa_header *h, struct ospf_lsa_header *n) { *n = *h; }; +static inline void lsa_ntoh_hdr(struct ospf_lsa_header *n, struct ospf_lsa_header *h) { *h = *n; }; +static inline void lsa_hton_body(void *h, void *n, u16 len) { ASSERT(h != n); memcpy(n, h, len); }; +static inline void lsa_ntoh_body(void *n, void *h, u16 len) { ASSERT(n != h); memcpy(h, n, len); }; +static inline void lsa_hton_body1(void *h, u16 len) { }; +static inline void lsa_ntoh_body1(void *n, u16 len) { }; #else -void htonlsah(struct ospf_lsa_header *h, struct ospf_lsa_header *n); -void ntohlsah(struct ospf_lsa_header *n, struct ospf_lsa_header *h); -void htonlsab(void *h, void *n, u16 len); -void ntohlsab(void *n, void *h, u16 len); -static inline void htonlsab1(void *h, u16 len) { htonlsab(h, h, len); }; -static inline void ntohlsab1(void *n, u16 len) { ntohlsab(n, n, len); }; +void lsa_hton_hdr(struct ospf_lsa_header *h, struct ospf_lsa_header *n); +void lsa_ntoh_hdr(struct ospf_lsa_header *n, struct ospf_lsa_header *h); +void lsa_hton_body(void *h, void *n, u16 len); +void lsa_ntoh_body(void *n, void *h, u16 len); +static inline void lsa_hton_body1(void *h, u16 len) { lsa_hton_body(h, h, len); }; +static inline void lsa_ntoh_body1(void *n, u16 len) { lsa_ntoh_body(n, n, len); }; #endif +struct ospf_lsa_rt_walk { + struct top_hash_entry *en; + void *buf, *bufend; + int ospf2; + u16 type, metric; + u32 id, data, lif, nif; +}; + + +void lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain); + +static inline void lsa_get_type_domain(struct ospf_lsa_header *lsa, struct ospf_iface *ifa, u32 *otype, u32 *domain) +{ lsa_get_type_domain_(lsa->type_raw, ifa, otype, domain); } + +static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p) +{ return ospf_is_v2(p) ? (h->type_raw & LSA_T_V2_MASK) : h->type_raw; } + + +int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa); + void lsasum_calculate(struct ospf_lsa_header *header, void *body); u16 lsasum_check(struct ospf_lsa_header *h, void *body); #define CMP_NEWER 1 #define CMP_SAME 0 #define CMP_OLDER -1 int lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2); -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); - +void lsa_walk_rt_init(struct ospf_proto *po, struct top_hash_entry *act, struct ospf_lsa_rt_walk *rt); +int lsa_walk_rt(struct ospf_lsa_rt_walk *rt); +void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, ip_addr *ip, int *pxlen, u8 *pxopts, u32 *metric); +void lsa_parse_sum_rt(struct top_hash_entry *en, int ospf2, u32 *drid, u32 *metric, u32 *options); +void lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *rt); +int lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body); #endif /* _BIRD_OSPF_LSALIB_H_ */ diff --git a/proto/ospf/lsreq.c b/proto/ospf/lsreq.c index 15854ce7..8888f88e 100644 --- a/proto/ospf/lsreq.c +++ b/proto/ospf/lsreq.c @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 2000--2004 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -9,47 +11,55 @@ #include "ospf.h" +/* struct ospf_lsreq_packet { - struct ospf_packet ospf_packet; - struct ospf_lsreq_header lsh[]; + struct ospf_packet hdr; + // union ospf_auth auth; + + struct ospf_lsreq_header lsrs[]; }; +*/ -static void ospf_dump_lsreq(struct proto *p, struct ospf_lsreq_packet *pkt) +static inline void +ospf_lsreq_body(struct ospf_proto *p, struct ospf_packet *pkt, + struct ospf_lsreq_header **body, uint *count) { - struct ospf_packet *op = &pkt->ospf_packet; + uint plen = ntohs(pkt->length); + uint hlen = ospf_pkt_hdrlen(p); - ASSERT(op->type == LSREQ_P); - ospf_dump_common(p, op); - - unsigned int i, j; - j = (ntohs(op->length) - sizeof(struct ospf_lsreq_packet)) / - sizeof(struct ospf_lsreq_header); - - for (i = 0; i < j; i++) - log(L_TRACE "%s: LSR Type: %04x, Id: %R, Rt: %R", p->name, - htonl(pkt->lsh[i].type), htonl(pkt->lsh[i].id), htonl(pkt->lsh[i].rt)); + *body = ((void *) pkt) + hlen; + *count = (plen - hlen) / sizeof(struct ospf_lsreq_header); } -void -ospf_lsreq_send(struct ospf_neighbor *n) +static void +ospf_dump_lsreq(struct ospf_proto *p, struct ospf_packet *pkt) { - snode *sn; + struct ospf_lsreq_header *lsrs; + uint i, lsr_count; + + ASSERT(pkt->type == LSREQ_P); + ospf_dump_common(p, pkt); + + ospf_lsreq_body(p, pkt, &lsrs, &lsr_count); + for (i = 0; i < lsr_count; i++) + log(L_TRACE "%s: LSR Type: %04x, Id: %R, Rt: %R", p->p.name, + ntohl(lsrs[i].type), ntohl(lsrs[i].id), ntohl(lsrs[i].rt)); +} + + +void +ospf_send_lsreq(struct ospf_proto *p, struct ospf_neighbor *n) +{ + struct ospf_iface *ifa = n->ifa; + struct ospf_lsreq_header *lsrs; struct top_hash_entry *en; - struct ospf_lsreq_packet *pk; - struct ospf_packet *op; - struct ospf_lsreq_header *lsh; - u16 length; - int i, j; - struct proto *p = &n->ifa->oa->po->proto; + struct ospf_packet *pkt; + uint i, lsr_max, length; - pk = ospf_tx_buffer(n->ifa); - op = &pk->ospf_packet; + /* RFC 2328 10.9 */ - ospf_pkt_fill_hdr(n->ifa, pk, LSREQ_P); - - sn = SHEAD(n->lsrql); if (EMPTY_SLIST(n->lsrql)) { if (n->state == NEIGHBOR_LOADING) @@ -57,90 +67,80 @@ ospf_lsreq_send(struct ospf_neighbor *n) return; } - i = j = (ospf_pkt_maxsize(n->ifa) - sizeof(struct ospf_lsreq_packet)) / - sizeof(struct ospf_lsreq_header); - lsh = pk->lsh; + pkt = ospf_tx_buffer(ifa); + ospf_pkt_fill_hdr(ifa, pkt, LSREQ_P); + ospf_lsreq_body(p, pkt, &lsrs, &lsr_max); - for (; i > 0; i--) + // for (i = 0; i < lsr_max; i++) + + i = 0; + WALK_SLIST(en, n->lsrql) { - en = (struct top_hash_entry *) sn; - lsh->type = htonl(en->lsa.type); - lsh->rt = htonl(en->lsa.rt); - lsh->id = htonl(en->lsa.id); - DBG("Requesting %uth LSA: Type: %u, ID: %R, RT: %R, SN: 0x%x, Age %u\n", - i, en->lsa.type, en->lsa.id, en->lsa.rt, en->lsa.sn, en->lsa.age); - lsh++; - if (sn == STAIL(n->lsrql)) + if (i == lsr_max) break; - sn = sn->next; + + DBG("Requesting %uth LSA: Type: %04u, ID: %R, RT: %R, SN: 0x%x, Age %u\n", + i, en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn, en->lsa.age); + + u32 etype = lsa_get_etype(&en->lsa, p); + lsrs[i].type = htonl(etype); + lsrs[i].rt = htonl(en->lsa.rt); + lsrs[i].id = htonl(en->lsa.id); + i++; } - if (i != 0) - i--; - length = - sizeof(struct ospf_lsreq_packet) + (j - - i) * sizeof(struct ospf_lsreq_header); - op->length = htons(length); + length = ospf_pkt_hdrlen(p) + i * sizeof(struct ospf_lsreq_header); + pkt->length = htons(length); - OSPF_PACKET(ospf_dump_lsreq, pk, "LSREQ packet sent to %I via %s", n->ip, n->ifa->ifname); - ospf_send_to(n->ifa, n->ip); + OSPF_PACKET(ospf_dump_lsreq, pkt, "LSREQ packet sent to %I via %s", n->ip, ifa->ifname); + ospf_send_to(ifa, n->ip); } + void -ospf_lsreq_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, +ospf_receive_lsreq(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n) { - struct ospf_area *oa = ifa->oa; - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; - struct ospf_lsreq_header *lsh; - struct l_lsr_head *llsh; - list uplist; - slab *upslab; - int i, lsano; + struct ospf_proto *p = ifa->oa->po; + struct ospf_lsreq_header *lsrs; + uint i, lsr_count; - unsigned int size = ntohs(ps_i->length); - if (size < sizeof(struct ospf_lsreq_packet)) - { - log(L_ERR "Bad OSPF LSREQ packet from %I - too short (%u B)", n->ip, size); - return; - } + /* RFC 2328 10.7 */ - struct ospf_lsreq_packet *ps = (void *) ps_i; - OSPF_PACKET(ospf_dump_lsreq, ps, "LSREQ packet received from %I via %s", n->ip, ifa->ifname); + /* No need to check length, lsreq has only basic header */ + + OSPF_PACKET(ospf_dump_lsreq, pkt, "LSREQ packet received from %I via %s", n->ip, ifa->ifname); if (n->state < NEIGHBOR_EXCHANGE) return; - ospf_neigh_sm(n, INM_HELLOREC); + ospf_neigh_sm(n, INM_HELLOREC); /* Not in RFC */ - lsh = ps->lsh; - init_list(&uplist); - upslab = sl_new(n->pool, sizeof(struct l_lsr_head)); + ospf_lsreq_body(p, pkt, &lsrs, &lsr_count); - lsano = (size - sizeof(struct ospf_lsreq_packet)) / - sizeof(struct ospf_lsreq_header); - for (i = 0; i < lsano; lsh++, i++) + struct top_hash_entry *en, *entries[lsr_count]; + + for (i = 0; i < lsr_count; i++) { - u32 hid = ntohl(lsh->id); - u32 hrt = ntohl(lsh->rt); - u32 htype = ntohl(lsh->type); - u32 dom = ospf_lsa_domain(htype, ifa); - DBG("Processing requested LSA: Type: %u, ID: %R, RT: %R\n", lsh->type, hid, hrt); - llsh = sl_alloc(upslab); - llsh->lsh.id = hid; - llsh->lsh.rt = hrt; - llsh->lsh.type = htype; - add_tail(&uplist, NODE llsh); - if (ospf_hash_find(po->gr, dom, hid, hrt, htype) == NULL) + u32 id, rt, type, domain; + + id = ntohl(lsrs[i].id); + rt = ntohl(lsrs[i].rt); + lsa_get_type_domain_(ntohl(lsrs[i].type), ifa, &type, &domain); + + DBG("Processing requested LSA: Type: %04x, Id: %R, Rt: %R\n", type, id, rt); + + en = ospf_hash_find(p->gr, domain, id, rt, type); + if (!en) { - log(L_WARN "Received bad LSREQ from %I: Type: %04x, Id: %R, Rt: %R", - n->ip, htype, hid, hrt); + log(L_WARN "%s: Received LSREQ from %I for missing LSA (Type: %04x, Id: %R, Rt: %R)", + p->p.name, n->ip, type, id, rt); ospf_neigh_sm(n, INM_BADLSREQ); - rfree(upslab); return; } + + entries[i] = en; } - ospf_lsupd_send_list(n, &uplist); - rfree(upslab); + + ospf_send_lsupd(p, entries, lsr_count, n); } diff --git a/proto/ospf/lsreq.h b/proto/ospf/lsreq.h deleted file mode 100644 index a12edde2..00000000 --- a/proto/ospf/lsreq.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * BIRD -- OSPF - * - * (c) 2000--2004 Ondrej Filip - * - * Can be freely distributed and used under the terms of the GNU GPL. - * - */ - -#ifndef _BIRD_OSPF_LSREQ_H_ -#define _BIRD_OSPF_LSREQ_H_ - -void ospf_lsreq_send(struct ospf_neighbor *n); -void ospf_lsreq_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, - struct ospf_neighbor *n); - -#endif /* _BIRD_OSPF_LSREQ_H_ */ diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 8f65c532..b0bf21cd 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 2000--2004 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -9,456 +11,389 @@ #include "ospf.h" +/* struct ospf_lsupd_packet { - struct ospf_packet ospf_packet; - u32 lsano; /* Number of LSA's */ + struct ospf_packet hdr; + // union ospf_auth auth; + + u32 lsa_count; + void lsas[]; }; +*/ -/* Beware of unaligned access */ -void ospf_dump_lsahdr(struct proto *p, struct ospf_lsa_header *lsa_n) +void +ospf_dump_lsahdr(struct ospf_proto *p, struct ospf_lsa_header *lsa_n) { struct ospf_lsa_header lsa; - ntohlsah(lsa_n, &lsa); + u32 lsa_etype; + + lsa_ntoh_hdr(lsa_n, &lsa); + lsa_etype = lsa_get_etype(&lsa, p); log(L_TRACE "%s: LSA Type: %04x, Id: %R, Rt: %R, Age: %u, Seq: %08x, Sum: %04x", - p->name, lsa.type, lsa.id, lsa.rt, lsa.age, lsa.sn, lsa.checksum); + p->p.name, lsa_etype, lsa.id, lsa.rt, lsa.age, lsa.sn, lsa.checksum); } -void ospf_dump_common(struct proto *p, struct ospf_packet *op) +void +ospf_dump_common(struct ospf_proto *p, struct ospf_packet *pkt) { - log(L_TRACE "%s: length %d", p->name, ntohs(op->length)); - log(L_TRACE "%s: router %R", p->name, ntohl(op->routerid)); + log(L_TRACE "%s: length %d", p->p.name, ntohs(pkt->length)); + log(L_TRACE "%s: router %R", p->p.name, ntohl(pkt->routerid)); } -static void ospf_dump_lsupd(struct proto *p, struct ospf_lsupd_packet *pkt) +static inline uint +ospf_lsupd_hdrlen(struct ospf_proto *p) { - struct ospf_packet *op = &pkt->ospf_packet; - - ASSERT(op->type == LSUPD_P); - ospf_dump_common(p, op); - - /* We know that ntohs(op->length) >= sizeof(struct ospf_lsa_header) */ - u8 *pbuf= (u8 *) pkt; - unsigned int offset = sizeof(struct ospf_lsupd_packet); - unsigned int bound = ntohs(op->length) - sizeof(struct ospf_lsa_header); - unsigned int i, j, lsalen; - - j = ntohl(pkt->lsano); - for (i = 0; i < j; i++) - { - if (offset > bound) - { - log(L_TRACE "%s: LSA invalid", p->name); - return; - } - - struct ospf_lsa_header *lsa = (void *) (pbuf + offset); - ospf_dump_lsahdr(p, lsa); - lsalen = ntohs(lsa->length); - offset += lsalen; - - if (((lsalen % 4) != 0) || (lsalen <= sizeof(struct ospf_lsa_header))) - { - log(L_TRACE "%s: LSA invalid", p->name); - return; - } - } + return ospf_pkt_hdrlen(p) + 4; /* + u32 lsa count field */ } - -#ifdef OSPFv2 - -int -ospf_lsa_flooding_allowed(struct ospf_lsa_header *lsa, u32 domain, struct ospf_iface *ifa) +static inline u32 +ospf_lsupd_get_lsa_count(struct ospf_packet *pkt, uint hdrlen) { - if (lsa->type == LSA_T_EXT) - { - if (ifa->type == OSPF_IT_VLINK) - return 0; - if (!oa_is_ext(ifa->oa)) - return 0; - return 1; - } - else - return ifa->oa->areaid == domain; + u32 *c = ((void *) pkt) + hdrlen - 4; + return ntohl(*c); } -#else /* OSPFv3 */ - -static int -unknown_lsa_type(struct ospf_lsa_header *lsa) +static inline void +ospf_lsupd_set_lsa_count(struct ospf_packet *pkt, uint hdrlen, u32 val) { - switch (lsa->type) - { - case LSA_T_RT: - case LSA_T_NET: - case LSA_T_SUM_NET: - case LSA_T_SUM_RT: - case LSA_T_EXT: - case LSA_T_NSSA: - case LSA_T_LINK: - case LSA_T_PREFIX: - return 0; - - default: - return 1; - } + u32 *c = ((void *) pkt) + hdrlen - 4; + *c = htonl(val); } -int -ospf_lsa_flooding_allowed(struct ospf_lsa_header *lsa, u32 domain, struct ospf_iface *ifa) -{ - u32 scope = LSA_SCOPE(lsa); - - /* Handle inactive vlinks */ - if (ifa->state == OSPF_IS_DOWN) - return 0; - - /* 4.5.2 (Case 2) */ - if (unknown_lsa_type(lsa) && !(lsa->type & LSA_UBIT)) - scope = LSA_SCOPE_LINK; - - switch (scope) - { - case LSA_SCOPE_LINK: - return ifa->iface_id == domain; - - case LSA_SCOPE_AREA: - return ifa->oa->areaid == domain; - - case LSA_SCOPE_AS: - if (ifa->type == OSPF_IT_VLINK) - return 0; - if (!oa_is_ext(ifa->oa)) - return 0; - return 1; - - default: - log(L_ERR "LSA with invalid scope"); - return 0; - } +static inline void +ospf_lsupd_body(struct ospf_proto *p, struct ospf_packet *pkt, + uint *offset, uint *bound, uint *lsa_count) +{ + uint hlen = ospf_lsupd_hdrlen(p); + *offset = hlen; + *bound = ntohs(pkt->length) - sizeof(struct ospf_lsa_header); + *lsa_count = ospf_lsupd_get_lsa_count(pkt, hlen); } -#endif +static void +ospf_dump_lsupd(struct ospf_proto *p, struct ospf_packet *pkt) +{ + uint offset, bound, i, lsa_count, lsalen; + + ASSERT(pkt->type == LSUPD_P); + ospf_dump_common(p, pkt); + + ospf_lsupd_body(p, pkt, &offset, &bound, &lsa_count); + for (i = 0; i < lsa_count; i++) + { + if (offset > bound) + { + log(L_TRACE "%s: LSA invalid", p->p.name); + return; + } + + struct ospf_lsa_header *lsa = ((void *) pkt) + offset; + ospf_dump_lsahdr(p, lsa); + lsalen = ntohs(lsa->length); + offset += lsalen; + + if (((lsalen % 4) != 0) || (lsalen <= sizeof(struct ospf_lsa_header))) + { + log(L_TRACE "%s: LSA invalid", p->p.name); + return; + } + } +} + + +static inline void +ospf_lsa_lsrt_up(struct top_hash_entry *en, struct ospf_neighbor *n) +{ + struct top_hash_entry *ret = ospf_hash_get_entry(n->lsrth, en); + + if (!SNODE_VALID(ret)) + { + en->ret_count++; + s_add_tail(&n->lsrtl, SNODE ret); + } + + memcpy(&ret->lsa, &en->lsa, sizeof(struct ospf_lsa_header)); +} + +static inline int +ospf_lsa_lsrt_down(struct top_hash_entry *en, struct ospf_neighbor *n) +{ + struct top_hash_entry *ret = ospf_hash_find_entry(n->lsrth, en); + + if (ret) + { + en->ret_count--; + s_rem_node(SNODE ret); + ospf_hash_delete(n->lsrth, ret); + return 1; + } + + return 0; +} + + +static void ospf_lsupd_flood_ifa(struct ospf_proto *p, struct ospf_iface *ifa, struct top_hash_entry *en); + /** - * ospf_lsupd_flood - send received or generated lsa to the neighbors - * @po: OSPF protocol - * @n: neighbor than sent this lsa (or NULL if generated) - * @hn: LSA header followed by lsa body in network endianity (may be NULL) - * @hh: LSA header in host endianity (must be filled) - * @domain: domain of LSA (must be filled) - * @rtl: add this LSA into retransmission list - * + * ospf_lsupd_flood - send received or generated LSA to the neighbors + * @p: OSPF protocol + * @en: LSA entry + * @from: neighbor than sent this LSA (or NULL if LSA is local) * * return value - was the LSA flooded back? */ int -ospf_lsupd_flood(struct proto_ospf *po, - struct ospf_neighbor *n, struct ospf_lsa_header *hn, - struct ospf_lsa_header *hh, u32 domain, int rtl) +ospf_lsupd_flood(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neighbor *from) { struct ospf_iface *ifa; - struct ospf_neighbor *nn; - struct top_hash_entry *en; - struct proto *p = &po->proto; - int ret, retval = 0; + struct ospf_neighbor *n; - /* pg 148 */ - WALK_LIST(ifa, po->iface_list) + int back = 0; + WALK_LIST(ifa, p->iface_list) { if (ifa->stub) continue; - if (! ospf_lsa_flooding_allowed(hh, domain, ifa)) + if (! lsa_flooding_allowed(en->lsa_type, en->domain, ifa)) continue; DBG("Wanted to flood LSA: Type: %u, ID: %R, RT: %R, SN: 0x%x, Age %u\n", hh->type, hh->id, hh->rt, hh->sn, hh->age); - ret = 0; - WALK_LIST(nn, ifa->neigh_list) + int used = 0; + WALK_LIST(n, ifa->neigh_list) { /* 13.3 (1a) */ - if (nn->state < NEIGHBOR_EXCHANGE) + if (n->state < NEIGHBOR_EXCHANGE) continue; /* 13.3 (1b) */ - if (nn->state < NEIGHBOR_FULL) + if (n->state < NEIGHBOR_FULL) { - if ((en = ospf_hash_find_header(nn->lsrqh, domain, hh)) != NULL) + struct top_hash_entry *req = ospf_hash_find_entry(n->lsrqh, en); + if (req != NULL) { - DBG("That LSA found in lsreq list for neigh %R\n", nn->rid); + int cmp = lsa_comp(&en->lsa, &req->lsa); - switch (lsa_comp(hh, &en->lsa)) + /* If same or newer, remove LSA from the link state request list */ + if (cmp > CMP_OLDER) { - case CMP_OLDER: - continue; - break; - case CMP_SAME: - s_rem_node(SNODE en); - if (en->lsa_body != NULL) - mb_free(en->lsa_body); - en->lsa_body = NULL; - DBG("Removing from lsreq list for neigh %R\n", nn->rid); - ospf_hash_delete(nn->lsrqh, en); - if ((EMPTY_SLIST(nn->lsrql)) && (nn->state == NEIGHBOR_LOADING)) - ospf_neigh_sm(nn, INM_LOADDONE); - continue; - break; - case CMP_NEWER: - s_rem_node(SNODE en); - if (en->lsa_body != NULL) - mb_free(en->lsa_body); - en->lsa_body = NULL; - DBG("Removing from lsreq list for neigh %R\n", nn->rid); - ospf_hash_delete(nn->lsrqh, en); - if ((EMPTY_SLIST(nn->lsrql)) && (nn->state == NEIGHBOR_LOADING)) - ospf_neigh_sm(nn, INM_LOADDONE); - break; - default: - bug("Bug in lsa_comp?"); + s_rem_node(SNODE req); + ospf_hash_delete(n->lsrqh, req); + if ((EMPTY_SLIST(n->lsrql)) && (n->state == NEIGHBOR_LOADING)) + ospf_neigh_sm(n, INM_LOADDONE); } + + /* If older or same, skip processing of this neighbor */ + if (cmp < CMP_NEWER) + continue; } } /* 13.3 (1c) */ - if (nn == n) + if (n == from) continue; - /* 13.3 (1d) */ - if (rtl) - { - /* In OSPFv3, there should be check whether receiving router understand - that type of LSA (for LSA types with U-bit == 0). But as we does not support - any optional LSA types, this is not needed yet */ + /* In OSPFv3, there should be check whether receiving router understand + that type of LSA (for LSA types with U-bit == 0). But as we do not support + any optional LSA types, this is not needed yet */ - if ((en = ospf_hash_find_header(nn->lsrth, domain, hh)) == NULL) - { - en = ospf_hash_get_header(nn->lsrth, domain, hh); - } - else - { - s_rem_node(SNODE en); - } - s_add_tail(&nn->lsrtl, SNODE en); - memcpy(&en->lsa, hh, sizeof(struct ospf_lsa_header)); - DBG("Adding that LSA for flood to %I\n", nn->ip); - } - else - { - if ((en = ospf_hash_find_header(nn->lsrth, domain, hh)) != NULL) - { - s_rem_node(SNODE en); - ospf_hash_delete(nn->lsrth, en); - } - } + /* 13.3 (1d) - add LSA to the link state retransmission list */ + ospf_lsa_lsrt_up(en, n); - ret = 1; + used = 1; } - if (ret == 0) - continue; /* pg 150 (2) */ + /* 13.3 (2) */ + if (!used) + continue; - if (n && (n->ifa == ifa)) + if (from && (from->ifa == ifa)) { - if ((n->rid == ifa->drid) || n->rid == ifa->bdrid) - continue; /* pg 150 (3) */ + /* 13.3 (3) */ + if ((from->rid == ifa->drid) || (from->rid == ifa->bdrid)) + continue; + + /* 13.3 (4) */ if (ifa->state == OSPF_IS_BACKUP) - continue; /* pg 150 (4) */ - retval = 1; - } - - { - u16 len, age; - struct ospf_lsupd_packet *pk; - struct ospf_packet *op; - struct ospf_lsa_header *lh; - - /* Check iface buffer size */ - uint len2 = sizeof(struct ospf_lsupd_packet) + (hn ? ntohs(hn->length) : hh->length); - if (ospf_iface_assure_bufsize(ifa, len2) < 0) - { - /* Cannot fit in a tx buffer, skip that iface */ - log(L_ERR "OSPF: LSA too large to flood on %s (Type: %04x, Id: %R, Rt: %R)", - ifa->ifname, hh->type, hh->id, hh->rt); continue; - } - pk = ospf_tx_buffer(ifa); - op = &pk->ospf_packet; - - ospf_pkt_fill_hdr(ifa, pk, LSUPD_P); - pk->lsano = htonl(1); - - lh = (struct ospf_lsa_header *) (pk + 1); - - /* Copy LSA into the packet */ - if (hn) - { - memcpy(lh, hn, ntohs(hn->length)); - } - else - { - u8 *help; - struct top_hash_entry *en; - - htonlsah(hh, lh); - help = (u8 *) (lh + 1); - en = ospf_hash_find_header(po->gr, domain, hh); - htonlsab(en->lsa_body, help, hh->length - sizeof(struct ospf_lsa_header)); - } - - len = sizeof(struct ospf_lsupd_packet) + ntohs(lh->length); - - age = ntohs(lh->age); - age += ifa->inftransdelay; - if (age > LSA_MAXAGE) - age = LSA_MAXAGE; - lh->age = htons(age); - - op->length = htons(len); - - OSPF_PACKET(ospf_dump_lsupd, pk, "LSUPD packet flooded via %s", ifa->ifname); - - switch (ifa->type) - { - case OSPF_IT_BCAST: - if ((ifa->state == OSPF_IS_BACKUP) || (ifa->state == OSPF_IS_DR)) - ospf_send_to_all(ifa); - else if (ifa->cf->real_bcast) - ospf_send_to_bdr(ifa); - else - ospf_send_to(ifa, AllDRouters); - break; - - case OSPF_IT_NBMA: - if ((ifa->state == OSPF_IS_BACKUP) || (ifa->state == OSPF_IS_DR)) - ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE); - else - ospf_send_to_bdr(ifa); - break; - - case OSPF_IT_PTP: - ospf_send_to_all(ifa); - break; - - case OSPF_IT_PTMP: - ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE); - break; - - case OSPF_IT_VLINK: - ospf_send_to(ifa, ifa->vip); - break; - - default: - bug("Bug in ospf_lsupd_flood()"); - } + back = 1; } + + /* 13.3 (5) - finally flood the packet */ + ospf_lsupd_flood_ifa(p, ifa, en); } - return retval; + + return back; } -void /* I send all I received in LSREQ */ -ospf_lsupd_send_list(struct ospf_neighbor *n, list * l) +static uint +ospf_prepare_lsupd(struct ospf_proto *p, struct ospf_iface *ifa, + struct top_hash_entry **lsa_list, uint lsa_count) { - struct ospf_area *oa = n->ifa->oa; - struct proto *p = &oa->po->proto; - struct l_lsr_head *lsr; - struct top_hash_entry *en; - struct ospf_lsupd_packet *pkt; - u32 len, len2, lsano; - char *buf; + struct ospf_packet *pkt; + uint hlen, pos, i, maxsize; - pkt = ospf_tx_buffer(n->ifa); - buf = (void *) pkt; + pkt = ospf_tx_buffer(ifa); + hlen = ospf_lsupd_hdrlen(p); + maxsize = ospf_pkt_maxsize(ifa); - lsr = HEAD(*l); - while(NODE_NEXT(lsr)) + ospf_pkt_fill_hdr(ifa, pkt, LSUPD_P); + pos = hlen; + + for (i = 0; i < lsa_count; i++) { - /* Prepare the packet */ - ospf_pkt_fill_hdr(n->ifa, pkt, LSUPD_P); - len = sizeof(struct ospf_lsupd_packet); - lsano = 0; + struct top_hash_entry *en = lsa_list[i]; + uint len = en->lsa.length; - /* Fill the packet with LSAs */ - while(NODE_NEXT(lsr)) + if ((pos + len) > maxsize) { - u32 domain = ospf_lsa_domain(lsr->lsh.type, n->ifa); - en = ospf_hash_find(oa->po->gr, domain, lsr->lsh.id, lsr->lsh.rt, lsr->lsh.type); - if (en == NULL) + /* The packet if full, stop adding LSAs and sent it */ + if (i > 0) + break; + + /* LSA is larger than MTU, check buffer size */ + if (ospf_iface_assure_bufsize(ifa, pos + len) < 0) { - /* Probably flushed LSA, this should not happen */ - // log(L_WARN "OSPF: LSA disappeared (Type: %04x, Id: %R, Rt: %R)", lsr->lsh.type, lsr->lsh.id, lsr->lsh.rt); - lsr = NODE_NEXT(lsr); - continue; + /* Cannot fit in a tx buffer, skip that */ + log(L_ERR "%s: LSA too large to send on %s (Type: %04x, Id: %R, Rt: %R)", + p->p.name, ifa->ifname, en->lsa_type, en->lsa.id, en->lsa.rt); + break; } - len2 = len + en->lsa.length; - if (len2 > ospf_pkt_maxsize(n->ifa)) - { - /* The packet if full, stop adding LSAs and sent it */ - if (lsano > 0) - break; - - /* LSA is larger than MTU, check buffer size */ - if (ospf_iface_assure_bufsize(n->ifa, len2) < 0) - { - /* Cannot fit in a tx buffer, skip that */ - log(L_ERR "OSPF: LSA too large to send (Type: %04x, Id: %R, Rt: %R)", - lsr->lsh.type, lsr->lsh.id, lsr->lsh.rt); - lsr = NODE_NEXT(lsr); - continue; - } - - /* TX buffer could be reallocated */ - pkt = ospf_tx_buffer(n->ifa); - buf = (void *) pkt; - } - - /* Copy the LSA to the packet */ - htonlsah(&(en->lsa), (struct ospf_lsa_header *) (buf + len)); - htonlsab(en->lsa_body, buf + len + sizeof(struct ospf_lsa_header), - en->lsa.length - sizeof(struct ospf_lsa_header)); - len = len2; - lsano++; - lsr = NODE_NEXT(lsr); + /* TX buffer could be reallocated */ + pkt = ospf_tx_buffer(ifa); } - if (lsano == 0) - break; + struct ospf_lsa_header *buf = ((void *) pkt) + pos; + lsa_hton_hdr(&en->lsa, buf); + lsa_hton_body(en->lsa_body, ((void *) buf) + sizeof(struct ospf_lsa_header), + len - sizeof(struct ospf_lsa_header)); + buf->age = htons(MIN(en->lsa.age + ifa->inftransdelay, LSA_MAXAGE)); - /* Send the packet */ - pkt->lsano = htonl(lsano); - pkt->ospf_packet.length = htons(len); - OSPF_PACKET(ospf_dump_lsupd, pkt, "LSUPD packet sent to %I via %s", - n->ip, n->ifa->ifname); - ospf_send_to(n->ifa, n->ip); + pos += len; } + + ospf_lsupd_set_lsa_count(pkt, hlen, i); + pkt->length = htons(pos); + + return i; +} + + +static void +ospf_lsupd_flood_ifa(struct ospf_proto *p, struct ospf_iface *ifa, struct top_hash_entry *en) +{ + uint c = ospf_prepare_lsupd(p, ifa, &en, 1); + + if (!c) /* Too large LSA */ + return; + + OSPF_PACKET(ospf_dump_lsupd, ospf_tx_buffer(ifa), + "LSUPD packet flooded via %s", ifa->ifname); + + if (ifa->type == OSPF_IT_BCAST) + { + if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP)) + ospf_send_to_all(ifa); + else + ospf_send_to_des(ifa); + } + else + ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE); +} + +int +ospf_send_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_list, uint lsa_count, struct ospf_neighbor *n) +{ + struct ospf_iface *ifa = n->ifa; + uint i, c; + + for (i = 0; i < lsa_count; i += c) + { + c = ospf_prepare_lsupd(p, ifa, lsa_list + i, lsa_count - i); + + if (!c) /* Too large LSA */ + { i++; continue; } + + OSPF_PACKET(ospf_dump_lsupd, ospf_tx_buffer(ifa), + "LSUPD packet sent to %I via %s", n->ip, ifa->ifname); + + ospf_send_to(ifa, n->ip); + } + + return lsa_count; } void -ospf_lsupd_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, +ospf_rxmt_lsupd(struct ospf_proto *p, struct ospf_neighbor *n) +{ + const uint max = 128; + struct top_hash_entry *entries[max]; + struct top_hash_entry *ret, *nxt, *en; + uint i = 0; + + WALK_SLIST_DELSAFE(ret, nxt, n->lsrtl) + { + if (i == max) + break; + + en = ospf_hash_find_entry(p->gr, ret); + if (!en) + { + /* Probably flushed LSA, this should not happen */ + log(L_WARN "%s: LSA disappeared (Type: %04x, Id: %R, Rt: %R)", + p->p.name, ret->lsa_type, ret->lsa.id, ret->lsa.rt); + + s_rem_node(SNODE ret); + ospf_hash_delete(n->lsrth, ret); + + continue; + } + + entries[i] = en; + i++; + } + + ospf_send_lsupd(p, entries, i, n); +} + + +static inline int +ospf_addr_is_local(struct ospf_proto *p, struct ospf_area *oa, ip_addr ip) +{ + struct ospf_iface *ifa; + WALK_LIST(ifa, p->iface_list) + if ((ifa->oa == oa) && ifa->addr && ipa_equal(ifa->addr->ip, ip)) + return 1; + + return 0; +} + +void +ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n) { + struct ospf_proto *p = ifa->oa->po; - struct ospf_neighbor *ntmp; - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; - unsigned int i, max, sendreq = 1; + /* RFC 2328 13. */ - unsigned int size = ntohs(ps_i->length); - if (size < (sizeof(struct ospf_lsupd_packet) + sizeof(struct ospf_lsa_header))) + int sendreq = 1; /* XXXX: review sendreq */ + + uint plen = ntohs(pkt->length); + if (plen < (ospf_lsupd_hdrlen(p) + sizeof(struct ospf_lsa_header))) { - log(L_ERR "OSPF: Bad LSUPD packet from %I - too short (%u B)", n->ip, size); + log(L_ERR "OSPF: Bad LSUPD packet from %I - too short (%u B)", n->ip, plen); return; } - struct ospf_lsupd_packet *ps = (void *) ps_i; - OSPF_PACKET(ospf_dump_lsupd, ps, "LSUPD packet received from %I via %s", n->ip, ifa->ifname); + OSPF_PACKET(ospf_dump_lsupd, pkt, "LSUPD packet received from %I via %s", n->ip, ifa->ifname); if (n->state < NEIGHBOR_EXCHANGE) { @@ -468,296 +403,206 @@ ospf_lsupd_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, ospf_neigh_sm(n, INM_HELLOREC); /* Questionable */ - unsigned int offset = sizeof(struct ospf_lsupd_packet); - unsigned int bound = size - sizeof(struct ospf_lsa_header); + uint offset, bound, i, lsa_count; + ospf_lsupd_body(p, pkt, &offset, &bound, &lsa_count); - max = ntohl(ps->lsano); - for (i = 0; i < max; i++) + for (i = 0; i < lsa_count; i++) { - struct ospf_lsa_header lsatmp; - struct top_hash_entry *lsadb; + struct ospf_lsa_header lsa, *lsa_n; + struct top_hash_entry *en; + u32 lsa_len, lsa_type, lsa_domain; if (offset > bound) { - log(L_WARN "Received lsupd from %I is too short!", n->ip); + log(L_WARN "%s: Received LSUPD from %I is too short", p->p.name, n->ip); ospf_neigh_sm(n, INM_BADLSREQ); return; } - struct ospf_lsa_header *lsa = (void *) (((u8 *) ps) + offset); - unsigned int lsalen = ntohs(lsa->length); - offset += lsalen; + /* LSA header in network order */ + lsa_n = ((void *) pkt) + offset; + lsa_len = ntohs(lsa_n->length); + offset += lsa_len; - if ((offset > size) || ((lsalen % 4) != 0) || - (lsalen <= sizeof(struct ospf_lsa_header))) + if ((offset > plen) || ((lsa_len % 4) != 0) || + (lsa_len <= sizeof(struct ospf_lsa_header))) { - log(L_WARN "Received LSA from %I with bad length", n->ip); + log(L_WARN "%s: Received LSA from %I with bad length", p->p.name, n->ip); ospf_neigh_sm(n, INM_BADLSREQ); - break; + return; } - /* pg 143 (1) */ - u16 chsum = lsa->checksum; - if (chsum != lsasum_check(lsa, NULL)) + /* RFC 2328 13. (1) - validate LSA checksum */ + u16 chsum = lsa_n->checksum; + if (chsum != lsasum_check(lsa_n, NULL)) { - log(L_WARN "Received bad lsa checksum from %I: %x %x", n->ip, chsum, lsa->checksum); + log(L_WARN "%s: Received LSA from %I with bad checskum: %x %x", + p->p.name, n->ip, chsum, lsa_n->checksum); continue; } -#ifdef OSPFv2 - /* pg 143 (2) */ - if ((lsa->type == 0) || (lsa->type == 6) || (lsa->type > LSA_T_NSSA)) + /* LSA header in host order */ + lsa_ntoh_hdr(lsa_n, &lsa); + lsa_get_type_domain(&lsa, ifa, &lsa_type, &lsa_domain); + + DBG("Update Type: %04x, Id: %R, Rt: %R, Sn: 0x%08x, Age: %u, Sum: %u\n", + lsa_type, lsa.id, lsa.rt, lsa.sn, lsa.age, lsa.checksum); + + /* RFC 2328 13. (2) */ + if (!lsa_type) { - log(L_WARN "Unknown LSA type from %I", n->ip); + log(L_WARN "%s: Received unknown LSA type from %I", p->p.name, n->ip); continue; } - /* pg 143 (3) */ - if ((lsa->type == LSA_T_EXT) && !oa_is_ext(ifa->oa)) + /* RFC 5340 4.5.1 (2) and RFC 2328 13. (3) */ + if (!oa_is_ext(ifa->oa) && (LSA_SCOPE(lsa_type) == LSA_SCOPE_AS)) { - log(L_WARN "Received External LSA in stub area from %I", n->ip); - continue; - } -#else /* OSPFv3 */ - u16 scope = ntoht(lsa->type) & LSA_SCOPE_MASK; - - /* 4.5.1 (2) */ - if ((scope == LSA_SCOPE_AS) && !oa_is_ext(ifa->oa)) - { - log(L_WARN "Received LSA with AS scope in stub area from %I", n->ip); + log(L_WARN "%s: Received LSA with AS scope in stub area from %I", p->p.name, n->ip); continue; } - /* 4.5.1 (3) */ - if (scope == LSA_SCOPE_RES) + /* Errata 3746 to RFC 2328 - rt-summary-LSAs forbidden in stub areas */ + if (!oa_is_ext(ifa->oa) && (lsa_type == LSA_T_SUM_RT)) { - log(L_WARN "Received LSA with invalid scope from %I", n->ip); + log(L_WARN "%s: Received rt-summary-LSA in stub area from %I", p->p.name, n->ip); continue; } -#endif - ntohlsah(lsa, &lsatmp); + /* RFC 5340 4.5.1 (3) */ + if (LSA_SCOPE(lsa_type) == LSA_SCOPE_RES) + { + log(L_WARN "%s: Received LSA with invalid scope from %I", p->p.name, n->ip); + continue; + } - DBG("Update Type: %u ID: %R RT: %R, Sn: 0x%08x Age: %u, Sum: %u\n", - lsatmp.type, lsatmp.id, lsatmp.rt, lsatmp.sn, lsatmp.age, lsatmp.checksum); - - /* FIXME domain should be link id for unknown LSA types with zero Ubit */ - u32 domain = ospf_lsa_domain(lsatmp.type, ifa); - lsadb = ospf_hash_find_header(po->gr, domain, &lsatmp); + /* Find local copy of LSA in link state database */ + en = ospf_hash_find(p->gr, lsa_domain, lsa.id, lsa.rt, lsa_type); #ifdef LOCAL_DEBUG - if (lsadb) - DBG("I have Type: %u ID: %R RT: %R, Sn: 0x%08x Age: %u, Sum: %u\n", - lsadb->lsa.type, lsadb->lsa.id, lsadb->lsa.rt, - lsadb->lsa.sn, lsadb->lsa.age, lsadb->lsa.checksum); + if (en) + DBG("I have Type: %04x, Id: %R, Rt: %R, Sn: 0x%08x, Age: %u, Sum: %u\n", + en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn, en->lsa.age, en->lsa.checksum); #endif - /* pg 143 (4) */ - if ((lsatmp.age == LSA_MAXAGE) && (lsadb == NULL) && can_flush_lsa(po)) + /* 13. (4) - ignore maxage LSA if i have no local copy */ + if ((lsa.age == LSA_MAXAGE) && !en && (p->padj == 0)) { - ospf_lsack_enqueue(n, lsa, ACKL_DIRECT); + /* 13.5. - schedule ACKs (tbl 19, case 5) */ + ospf_enqueue_lsack(n, lsa_n, ACKL_DIRECT); continue; } - /* pg 144 (5) */ - if ((lsadb == NULL) || (lsa_comp(&lsatmp, &lsadb->lsa) == CMP_NEWER)) + /* 13. (5) - received LSA is newer (or no local copy) */ + if (!en || (lsa_comp(&lsa, &en->lsa) == CMP_NEWER)) { - struct ospf_iface *ift = NULL; - int self = (lsatmp.rt == po->router_id); - - DBG("PG143(5): Received LSA is newer\n"); - -#ifdef OSPFv2 - /* 13.4 - check self-originated LSAs of NET type */ - if ((!self) && (lsatmp.type == LSA_T_NET)) + /* 13. (5a) - enforce minimum time between updates for received LSAs */ + /* We also use this to ratelimit reactions to received self-originated LSAs */ + if (en && ((now - en->inst_time) < MINLSARRIVAL)) { - struct ospf_iface *nifa; - WALK_LIST(nifa, po->iface_list) - { - if (!nifa->iface) - continue; - if (ipa_equal(nifa->addr->ip, ipa_from_u32(lsatmp.id))) - { - self = 1; - break; - } - } - } -#endif - - /* pg 145 (5f) - premature aging of self originated lsa */ - if (self) - { - if ((lsatmp.age == LSA_MAXAGE) && (lsatmp.sn == LSA_MAXSEQNO)) - { - ospf_lsack_enqueue(n, lsa, ACKL_DIRECT); - continue; - } - - OSPF_TRACE(D_EVENTS, "Received old self-originated LSA (Type: %04x, Id: %R, Rt: %R)", - lsatmp.type, lsatmp.id, lsatmp.rt); - - if (lsadb) - { - OSPF_TRACE(D_EVENTS, "Reflooding new self-originated LSA with newer sequence number"); - lsadb->lsa.sn = lsatmp.sn + 1; - lsadb->lsa.age = 0; - lsadb->inst_t = now; - lsadb->ini_age = 0; - lsasum_calculate(&lsadb->lsa, lsadb->lsa_body); - ospf_lsupd_flood(po, NULL, NULL, &lsadb->lsa, domain, 1); - } - else - { - OSPF_TRACE(D_EVENTS, "Premature aging it"); - lsatmp.age = LSA_MAXAGE; - lsatmp.sn = LSA_MAXSEQNO; - lsa->age = htons(LSA_MAXAGE); - lsa->sn = htonl(LSA_MAXSEQNO); - lsasum_check(lsa, (lsa + 1)); /* It also calculates chsum! */ - lsatmp.checksum = ntohs(lsa->checksum); - ospf_lsupd_flood(po, NULL, lsa, &lsatmp, domain, 0); - } - continue; - } - - /* pg 144 (5a) */ - if (lsadb && ((now - lsadb->inst_t) <= MINLSARRIVAL)) /* FIXME: test for flooding? */ - { - OSPF_TRACE(D_EVENTS, "Skipping LSA received in less that MINLSARRIVAL"); + OSPF_TRACE(D_EVENTS, "Skipping LSA received in less that MinLSArrival"); sendreq = 0; continue; } - /* Remove old from all ret lists */ - /* pg 144 (5c) */ - /* Must be done before (5b), otherwise it also removes the new entries from (5b) */ - if (lsadb) - WALK_LIST(ift, po->iface_list) - WALK_LIST(ntmp, ift->neigh_list) - { - struct top_hash_entry *en; - if (ntmp->state > NEIGHBOR_EXSTART) - if ((en = ospf_hash_find_header(ntmp->lsrth, domain, &lsadb->lsa)) != NULL) - { - s_rem_node(SNODE en); - ospf_hash_delete(ntmp->lsrth, en); - } - } + /* Copy and validate LSA body */ + int blen = lsa.length - sizeof(struct ospf_lsa_header); + void *body = mb_alloc(p->p.pool, blen); + lsa_ntoh_body(lsa_n + 1, body, blen); - /* pg 144 (5b) */ - if (ospf_lsupd_flood(po, n, lsa, &lsatmp, domain, 1) == 0) + if (lsa_validate(&lsa, lsa_type, ospf_is_v2(p), body) == 0) { - DBG("Wasn't flooded back\n"); /* ps 144(5e), pg 153 */ - if (ifa->state == OSPF_IS_BACKUP) - { - if (ifa->drid == n->rid) - ospf_lsack_enqueue(n, lsa, ACKL_DELAY); - } - else - ospf_lsack_enqueue(n, lsa, ACKL_DELAY); - } - - if ((lsatmp.age == LSA_MAXAGE) && (lsatmp.sn == LSA_MAXSEQNO) - && lsadb && can_flush_lsa(po)) - { - flush_lsa(lsadb, po); - schedule_rtcalc(po); - continue; - } /* FIXME lsack? */ - - /* pg 144 (5d) */ - void *body = mb_alloc(p->pool, lsatmp.length - sizeof(struct ospf_lsa_header)); - ntohlsab(lsa + 1, body, lsatmp.length - sizeof(struct ospf_lsa_header)); - - /* We will do validation check after flooding and - acknowledging given LSA to minimize problems - when communicating with non-validating peer */ - if (lsa_validate(&lsatmp, body) == 0) - { - log(L_WARN "Received invalid LSA from %I", n->ip); + log(L_WARN "%s: Received invalid LSA from %I", p->p.name, n->ip); mb_free(body); - continue; + sendreq = 0; + continue; } - lsadb = lsa_install_new(po, &lsatmp, domain, body); - DBG("New LSA installed in DB\n"); + /* 13. (5f) - handle self-originated LSAs, see also 13.4. */ + if ((lsa.rt == p->router_id) || + (ospf_is_v2(p) && (lsa_type == LSA_T_NET) && ospf_addr_is_local(p, ifa->oa, ipa_from_u32(lsa.id)))) + { + OSPF_TRACE(D_EVENTS, "Received unexpected self-originated LSA"); + ospf_advance_lsa(p, en, &lsa, lsa_type, lsa_domain, body); + continue; + } -#ifdef OSPFv3 - /* Events 6,7 from RFC5340 4.4.3. */ - if ((lsatmp.type == LSA_T_LINK) && - (ifa->state == OSPF_IS_DR)) - schedule_net_lsa(ifa); -#endif + /* 13. (5c) - remove old LSA from all retransmission lists */ + /* Must be done before (5b), otherwise it also removes the new entries from (5b) */ + + if (en) + ospf_lsa_lsrt_down(en, n); + + /* + { + struct ospf_iface *ifi; + struct ospf_neighbor *ni; + + WALK_LIST(ifi, p->iface_list) + WALK_LIST(ni, ifi->neigh_list) + if (ni->state > NEIGHBOR_EXSTART) + ospf_lsa_lsrt_down(en, ni); + } + */ + + /* 13. (5d) - install new LSA into database */ + en = ospf_install_lsa(p, &lsa, lsa_type, lsa_domain, body); + + /* RFC 5340 4.4.3 Events 6+7 - new Link LSA received */ + if (lsa_type == LSA_T_LINK) + ospf_notify_net_lsa(ifa); + + /* 13. (5b) - flood new LSA */ + int flood_back = ospf_lsupd_flood(p, en, n); + + /* 13.5. - schedule ACKs (tbl 19, cases 1+2) */ + if (! flood_back) + if ((ifa->state != OSPF_IS_BACKUP) || (n->rid == ifa->drid)) + ospf_enqueue_lsack(n, lsa_n, ACKL_DELAY); + + /* FIXME: remove LSA entry if it is LSA_MAXAGE and it is possible? */ continue; } /* FIXME pg145 (6) */ - /* pg145 (7) */ - if (lsa_comp(&lsatmp, &lsadb->lsa) == CMP_SAME) + /* 13. (7) - received LSA is same */ + if (lsa_comp(&lsa, &en->lsa) == CMP_SAME) { - struct top_hash_entry *en; - DBG("PG145(7) Got the same LSA\n"); - if ((en = ospf_hash_find_header(n->lsrth, lsadb->domain, &lsadb->lsa)) != NULL) - { - /* pg145 (7a) */ - s_rem_node(SNODE en); - ospf_hash_delete(n->lsrth, en); + /* Duplicate LSA, treat as implicit ACK */ + int implicit_ack = ospf_lsa_lsrt_down(en, n); - if (ifa->state == OSPF_IS_BACKUP) - { - if (n->rid == ifa->drid) - ospf_lsack_enqueue(n, lsa, ACKL_DELAY); - } + /* 13.5. - schedule ACKs (tbl 19, cases 3+4) */ + if (implicit_ack) + { + if ((ifa->state == OSPF_IS_BACKUP) && (n->rid == ifa->drid)) + ospf_enqueue_lsack(n, lsa_n, ACKL_DELAY); } else - { - /* pg145 (7b) */ - ospf_lsack_enqueue(n, lsa, ACKL_DIRECT); - } + ospf_enqueue_lsack(n, lsa_n, ACKL_DIRECT); + sendreq = 0; continue; } - /* pg145 (8) */ - if ((lsadb->lsa.age == LSA_MAXAGE) && (lsadb->lsa.sn == LSA_MAXSEQNO)) + /* 13. (8) - received LSA is older */ { - continue; - } + /* Seqnum is wrapping, wait until it is flushed */ + if ((en->lsa.age == LSA_MAXAGE) && (en->lsa.sn == LSA_MAXSEQNO)) + continue; - { - list l; - struct l_lsr_head ll; - init_list(&l); - ll.lsh.id = lsadb->lsa.id; - ll.lsh.rt = lsadb->lsa.rt; - ll.lsh.type = lsadb->lsa.type; - add_tail(&l, NODE & ll); - ospf_lsupd_send_list(n, &l); + /* Send newer local copy back to neighbor */ + /* FIXME - check for MinLSArrival ? */ + ospf_send_lsupd(p, &en, 1, n); } } /* Send direct LSAs */ ospf_lsack_send(n, ACKL_DIRECT); + /* If loading, ask for another part of neighbor's database */ if (sendreq && (n->state == NEIGHBOR_LOADING)) - { - ospf_lsreq_send(n); /* Ask for another part of neighbor's database */ - } + ospf_send_lsreq(p, n); } -void -ospf_lsupd_flush_nlsa(struct proto_ospf *po, struct top_hash_entry *en) -{ - struct ospf_lsa_header *lsa = &en->lsa; - struct proto *p = &po->proto; - - lsa->age = LSA_MAXAGE; - lsa->sn = LSA_MAXSEQNO; - lsasum_calculate(lsa, en->lsa_body); - OSPF_TRACE(D_EVENTS, "Premature aging self originated lsa!"); - OSPF_TRACE(D_EVENTS, "Type: %04x, Id: %R, Rt: %R", lsa->type, lsa->id, lsa->rt); - ospf_lsupd_flood(po, NULL, NULL, lsa, en->domain, 0); -} diff --git a/proto/ospf/lsupd.h b/proto/ospf/lsupd.h deleted file mode 100644 index 8bacfe65..00000000 --- a/proto/ospf/lsupd.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * BIRD -- OSPF - * - * (c) 2000--2004 Ondrej Filip - * - * Can be freely distributed and used under the terms of the GNU GPL. - * - */ - -#ifndef _BIRD_OSPF_LSUPD_H_ -#define _BIRD_OSPF_LSUPD_H_ - -void ospf_dump_lsahdr(struct proto *p, struct ospf_lsa_header *lsa_n); -void ospf_dump_common(struct proto *p, struct ospf_packet *op); -void ospf_lsupd_send_list(struct ospf_neighbor *n, list * l); -void ospf_lsupd_receive(struct ospf_packet *ps_i, - struct ospf_iface *ifa, struct ospf_neighbor *n); -int ospf_lsupd_flood(struct proto_ospf *po, - struct ospf_neighbor *n, struct ospf_lsa_header *hn, - struct ospf_lsa_header *hh, u32 domain, int rtl); -void ospf_lsupd_flush_nlsa(struct proto_ospf *po, struct top_hash_entry *en); -int ospf_lsa_flooding_allowed(struct ospf_lsa_header *lsa, u32 domain, struct ospf_iface *ifa); - - -#endif /* _BIRD_OSPF_LSUPD_H_ */ diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index faaaf232..392f1d64 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -1,7 +1,9 @@ /* * BIRD -- OSPF * - * (c) 1999 - 2004 Ondrej Filip + * (c) 1999--2004 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -26,8 +28,6 @@ const char *ospf_inm[] = }; static void neigh_chstate(struct ospf_neighbor *n, u8 state); -static struct ospf_neighbor *electbdr(list nl); -static struct ospf_neighbor *electdr(list nl); static void neighbor_timer_hook(timer * timer); static void rxmt_timer_hook(timer * timer); static void ackd_timer_hook(timer * t); @@ -37,11 +37,9 @@ init_lists(struct ospf_neighbor *n) { s_init_list(&(n->lsrql)); n->lsrqh = ospf_top_new(n->pool); - s_init(&(n->lsrqi), &(n->lsrql)); s_init_list(&(n->lsrtl)); n->lsrth = ospf_top_new(n->pool); - s_init(&(n->lsrti), &(n->lsrtl)); } /* Resets LSA request and retransmit lists. @@ -59,9 +57,8 @@ reset_lists(struct ospf_neighbor *n) struct ospf_neighbor * ospf_neighbor_new(struct ospf_iface *ifa) { - struct proto *p = (struct proto *) (ifa->oa->po); - struct proto_ospf *po = ifa->oa->po; - struct pool *pool = rp_new(p->pool, "OSPF Neighbor"); + struct ospf_proto *p = ifa->oa->po; + struct pool *pool = rp_new(p->p.pool, "OSPF Neighbor"); struct ospf_neighbor *n = mb_allocz(pool, sizeof(struct ospf_neighbor)); n->pool = pool; @@ -72,14 +69,14 @@ ospf_neighbor_new(struct ospf_iface *ifa) n->state = NEIGHBOR_DOWN; init_lists(n); - s_init(&(n->dbsi), &(po->lsal)); + s_init(&(n->dbsi), &(p->lsal)); n->inactim = tm_new(pool); n->inactim->data = n; n->inactim->randomize = 0; n->inactim->hook = neighbor_timer_hook; n->inactim->recurrent = 0; - DBG("%s: Installing inactivity timer.\n", p->name); + DBG("%s: Installing inactivity timer.\n", p->p.name); n->rxmt_timer = tm_new(pool); n->rxmt_timer->data = n; @@ -87,7 +84,7 @@ ospf_neighbor_new(struct ospf_iface *ifa) n->rxmt_timer->hook = rxmt_timer_hook; n->rxmt_timer->recurrent = ifa->rxmtint; tm_start(n->rxmt_timer, n->ifa->rxmtint); - DBG("%s: Installing rxmt timer.\n", p->name); + DBG("%s: Installing rxmt timer.\n", p->p.name); n->ackd_timer = tm_new(pool); n->ackd_timer->data = n; @@ -97,7 +94,7 @@ ospf_neighbor_new(struct ospf_iface *ifa) init_list(&n->ackl[ACKL_DIRECT]); init_list(&n->ackl[ACKL_DELAY]); tm_start(n->ackd_timer, n->ifa->rxmtint / 2); - DBG("%s: Installing ackd timer.\n", p->name); + DBG("%s: Installing ackd timer.\n", p->p.name); return (n); } @@ -110,64 +107,73 @@ ospf_neighbor_new(struct ospf_iface *ifa) * Many actions have to be taken acording to a change of state of a neighbor. It * starts rxmt timers, call interface state machine etc. */ - static void neigh_chstate(struct ospf_neighbor *n, u8 state) { - u8 oldstate; + struct ospf_iface *ifa = n->ifa; + struct ospf_proto *p = ifa->oa->po; + u8 old_state = n->state; + int old_fadj = ifa->fadj; - oldstate = n->state; + if (state == old_state) + return; - if (oldstate != state) + OSPF_TRACE(D_EVENTS, "Neighbor %I changes state from %s to %s", + n->ip, ospf_ns[old_state], ospf_ns[state]); + + n->state = state; + + if ((state == NEIGHBOR_2WAY) && (old_state < NEIGHBOR_2WAY)) + ospf_iface_sm(ifa, ISM_NEICH); + if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY)) + ospf_iface_sm(ifa, ISM_NEICH); + + /* Increase number of partial adjacencies */ + if ((state == NEIGHBOR_EXCHANGE) || (state == NEIGHBOR_LOADING)) + p->padj++; + + /* Decrease number of partial adjacencies */ + if ((old_state == NEIGHBOR_EXCHANGE) || (old_state == NEIGHBOR_LOADING)) + p->padj--; + + /* Increase number of full adjacencies */ + if (state == NEIGHBOR_FULL) + ifa->fadj++; + + /* Decrease number of full adjacencies */ + if (old_state == NEIGHBOR_FULL) + ifa->fadj--; + + if (ifa->fadj != old_fadj) { - struct ospf_iface *ifa = n->ifa; - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; + /* RFC 2328 12.4 Event 4 - neighbor enters/leaves Full state */ + ospf_notify_rt_lsa(ifa->oa); + ospf_notify_net_lsa(ifa); - n->state = state; - - OSPF_TRACE(D_EVENTS, "Neighbor %I changes state from \"%s\" to \"%s\".", - n->ip, ospf_ns[oldstate], ospf_ns[state]); - - if ((state == NEIGHBOR_2WAY) && (oldstate < NEIGHBOR_2WAY)) - ospf_iface_sm(ifa, ISM_NEICH); - if ((state < NEIGHBOR_2WAY) && (oldstate >= NEIGHBOR_2WAY)) - ospf_iface_sm(ifa, ISM_NEICH); - - if (oldstate == NEIGHBOR_FULL) /* Decrease number of adjacencies */ - { - ifa->fadj--; - schedule_rt_lsa(ifa->oa); - if (ifa->type == OSPF_IT_VLINK) schedule_rt_lsa(ifa->voa); - schedule_net_lsa(ifa); - } - - if (state == NEIGHBOR_FULL) /* Increase number of adjacencies */ - { - ifa->fadj++; - schedule_rt_lsa(ifa->oa); - if (ifa->type == OSPF_IT_VLINK) schedule_rt_lsa(ifa->voa); - schedule_net_lsa(ifa); - } - if (state == NEIGHBOR_EXSTART) - { - if (n->adj == 0) /* First time adjacency */ - { - n->dds = random_u32(); - } - n->dds++; - n->myimms.byte = 0; - n->myimms.bit.ms = 1; - n->myimms.bit.m = 1; - n->myimms.bit.i = 1; - } - if (state > NEIGHBOR_EXSTART) - n->myimms.bit.i = 0; + /* RFC 2328 12.4 Event 8 - vlink state change */ + if (ifa->type == OSPF_IT_VLINK) + ospf_notify_rt_lsa(ifa->voa); } + + if (state == NEIGHBOR_EXSTART) + { + /* First time adjacency */ + if (n->adj == 0) + n->dds = random_u32(); + + n->dds++; + n->myimms = DBDES_IMMS; + } + + if (state > NEIGHBOR_EXSTART) + n->myimms &= ~DBDES_I; } +static inline u32 neigh_get_id(struct ospf_proto *p, struct ospf_neighbor *n) +{ return ospf_is_v2(p) ? ipa_to_u32(n->ip) : n->rid; } + static struct ospf_neighbor * -electbdr(list nl) +elect_bdr(struct ospf_proto *p, list nl) { struct ospf_neighbor *neigh, *n1, *n2; u32 nid; @@ -176,11 +182,7 @@ electbdr(list nl) n2 = NULL; WALK_LIST(neigh, nl) /* First try those decl. themselves */ { -#ifdef OSPFv2 - nid = ipa_to_u32(neigh->ip); -#else /* OSPFv3 */ - nid = neigh->rid; -#endif + nid = neigh_get_id(p, neigh); if (neigh->state >= NEIGHBOR_2WAY) /* Higher than 2WAY */ if (neigh->priority > 0) /* Eligible */ @@ -225,7 +227,7 @@ electbdr(list nl) } static struct ospf_neighbor * -electdr(list nl) +elect_dr(struct ospf_proto *p, list nl) { struct ospf_neighbor *neigh, *n; u32 nid; @@ -233,11 +235,7 @@ electdr(list nl) n = NULL; WALK_LIST(neigh, nl) /* And now DR */ { -#ifdef OSPFv2 - nid = ipa_to_u32(neigh->ip); -#else /* OSPFv3 */ - nid = neigh->rid; -#endif + nid = neigh_get_id(p, neigh); if (neigh->state >= NEIGHBOR_2WAY) /* Higher than 2WAY */ if (neigh->priority > 0) /* Eligible */ @@ -264,13 +262,9 @@ electdr(list nl) static int can_do_adj(struct ospf_neighbor *n) { - struct ospf_iface *ifa; - struct proto *p; - int i; - - ifa = n->ifa; - p = (struct proto *) (ifa->oa->po); - i = 0; + struct ospf_iface *ifa = n->ifa; + struct ospf_proto *p = ifa->oa->po; + int i = 0; switch (ifa->type) { @@ -285,10 +279,10 @@ can_do_adj(struct ospf_neighbor *n) { case OSPF_IS_DOWN: case OSPF_IS_LOOP: - bug("%s: Iface %s in down state?", p->name, ifa->ifname); + bug("%s: Iface %s in down state?", p->p.name, ifa->ifname); break; case OSPF_IS_WAITING: - DBG("%s: Neighbor? on iface %s\n", p->name, ifa->ifname); + DBG("%s: Neighbor? on iface %s\n", p->p.name, ifa->ifname); break; case OSPF_IS_DROTHER: if (((n->rid == ifa->drid) || (n->rid == ifa->bdrid)) @@ -302,15 +296,15 @@ can_do_adj(struct ospf_neighbor *n) i = 1; break; default: - bug("%s: Iface %s in unknown state?", p->name, ifa->ifname); + bug("%s: Iface %s in unknown state?", p->p.name, ifa->ifname); break; } break; default: - bug("%s: Iface %s is unknown type?", p->name, ifa->ifname); + bug("%s: Iface %s is unknown type?", p->p.name, ifa->ifname); break; } - DBG("%s: Iface %s can_do_adj=%d\n", p->name, ifa->ifname, i); + DBG("%s: Iface %s can_do_adj=%d\n", p->p.name, ifa->ifname, i); return i; } @@ -329,8 +323,7 @@ can_do_adj(struct ospf_neighbor *n) void ospf_neigh_sm(struct ospf_neighbor *n, int event) { - struct proto_ospf *po = n->ifa->oa->po; - struct proto *p = &po->proto; + struct ospf_proto *p = n->ifa->oa->po; DBG("Neighbor state machine for neighbor %I, event '%s'\n", n->ip, ospf_inm[event]); @@ -341,23 +334,23 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) neigh_chstate(n, NEIGHBOR_ATTEMPT); /* NBMA are used different way */ break; + case INM_HELLOREC: - switch (n->state) - { - case NEIGHBOR_ATTEMPT: - case NEIGHBOR_DOWN: + if ((n->state == NEIGHBOR_DOWN) || + (n->state == NEIGHBOR_ATTEMPT)) neigh_chstate(n, NEIGHBOR_INIT); - default: - tm_start(n->inactim, n->ifa->deadint); /* Restart inactivity timer */ - break; - } + + /* Restart inactivity timer */ + tm_start(n->inactim, n->ifa->deadint); break; + case INM_2WAYREC: if (n->state < NEIGHBOR_2WAY) neigh_chstate(n, NEIGHBOR_2WAY); if ((n->state == NEIGHBOR_2WAY) && can_do_adj(n)) neigh_chstate(n, NEIGHBOR_EXSTART); break; + case INM_NEGDONE: if (n->state == NEIGHBOR_EXSTART) { @@ -365,25 +358,22 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) /* Reset DB summary list iterator */ s_get(&(n->dbsi)); - s_init(&(n->dbsi), &po->lsal); + s_init(&(n->dbsi), &p->lsal); - while (!EMPTY_LIST(n->ackl[ACKL_DELAY])) - { - struct lsah_n *no; - no = (struct lsah_n *) HEAD(n->ackl[ACKL_DELAY]); - rem_node(NODE no); - mb_free(no); - } + ospf_reset_lsack_queue(n); } else bug("NEGDONE and I'm not in EXSTART?"); break; + case INM_EXDONE: neigh_chstate(n, NEIGHBOR_LOADING); break; + case INM_LOADDONE: neigh_chstate(n, NEIGHBOR_FULL); break; + case INM_ADJOK: switch (n->state) { @@ -404,6 +394,7 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) break; } break; + case INM_SEQMIS: case INM_BADLSREQ: if (n->state >= NEIGHBOR_EXCHANGE) @@ -412,24 +403,27 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) neigh_chstate(n, NEIGHBOR_EXSTART); } break; + case INM_KILLNBR: case INM_LLDOWN: case INM_INACTTIM: reset_lists(n); neigh_chstate(n, NEIGHBOR_DOWN); break; + case INM_1WAYREC: reset_lists(n); neigh_chstate(n, NEIGHBOR_INIT); break; + default: - bug("%s: INM - Unknown event?", p->name); + bug("%s: INM - Unknown event?", p->p.name); break; } } /** - * bdr_election - (Backup) Designed Router election + * ospf_dr_election - (Backup) Designed Router election * @ifa: actual interface * * When the wait timer fires, it is time to elect (Backup) Designated Router. @@ -438,12 +432,11 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) * Router. This process is described in 9.4 of RFC 2328. */ void -bdr_election(struct ospf_iface *ifa) +ospf_dr_election(struct ospf_iface *ifa) { - struct proto_ospf *po = ifa->oa->po; - u32 myid = po->router_id; + struct ospf_proto *p = ifa->oa->po; struct ospf_neighbor *neigh, *ndr, *nbdr, me; - int doadj; + u32 myid = p->router_id; DBG("(B)DR election.\n"); @@ -452,19 +445,14 @@ bdr_election(struct ospf_iface *ifa) me.priority = ifa->priority; me.ip = ifa->addr->ip; -#ifdef OSPFv2 - me.dr = ipa_to_u32(ifa->drip); - me.bdr = ipa_to_u32(ifa->bdrip); -#else /* OSPFv3 */ - me.dr = ifa->drid; - me.bdr = ifa->bdrid; + me.dr = ospf_is_v2(p) ? ipa_to_u32(ifa->drip) : ifa->drid; + me.bdr = ospf_is_v2(p) ? ipa_to_u32(ifa->bdrip) : ifa->bdrid; me.iface_id = ifa->iface_id; -#endif add_tail(&ifa->neigh_list, NODE & me); - nbdr = electbdr(ifa->neigh_list); - ndr = electdr(ifa->neigh_list); + nbdr = elect_bdr(p, ifa->neigh_list); + ndr = elect_dr(p, ifa->neigh_list); if (ndr == NULL) ndr = nbdr; @@ -475,56 +463,47 @@ bdr_election(struct ospf_iface *ifa) || ((ifa->bdrid == myid) && (nbdr != &me)) || ((ifa->bdrid != myid) && (nbdr == &me))) { -#ifdef OSPFv2 - me.dr = ndr ? ipa_to_u32(ndr->ip) : 0; - me.bdr = nbdr ? ipa_to_u32(nbdr->ip) : 0; -#else /* OSPFv3 */ - me.dr = ndr ? ndr->rid : 0; - me.bdr = nbdr ? nbdr->rid : 0; -#endif + me.dr = ndr ? neigh_get_id(p, ndr) : 0; + me.bdr = nbdr ? neigh_get_id(p, nbdr) : 0; - nbdr = electbdr(ifa->neigh_list); - ndr = electdr(ifa->neigh_list); + nbdr = elect_bdr(p, ifa->neigh_list); + ndr = elect_dr(p, ifa->neigh_list); if (ndr == NULL) ndr = nbdr; } - u32 odrid = ifa->drid; - u32 obdrid = ifa->bdrid; + rem_node(NODE & me); + + + u32 old_drid = ifa->drid; + u32 old_bdrid = ifa->bdrid; ifa->drid = ndr ? ndr->rid : 0; ifa->drip = ndr ? ndr->ip : IPA_NONE; + ifa->dr_iface_id = ndr ? ndr->iface_id : 0; + ifa->bdrid = nbdr ? nbdr->rid : 0; ifa->bdrip = nbdr ? nbdr->ip : IPA_NONE; -#ifdef OSPFv3 - ifa->dr_iface_id = ndr ? ndr->iface_id : 0; -#endif - DBG("DR=%R, BDR=%R\n", ifa->drid, ifa->bdrid); - doadj = ((ifa->drid != odrid) || (ifa->bdrid != obdrid)); - - if (myid == ifa->drid) + if (ifa->drid == myid) ospf_iface_chstate(ifa, OSPF_IS_DR); + else if (ifa->bdrid == myid) + ospf_iface_chstate(ifa, OSPF_IS_BACKUP); else - { - if (myid == ifa->bdrid) - ospf_iface_chstate(ifa, OSPF_IS_BACKUP); - else - ospf_iface_chstate(ifa, OSPF_IS_DROTHER); - } + ospf_iface_chstate(ifa, OSPF_IS_DROTHER); - rem_node(NODE & me); - - if (doadj) - { + /* Review neighbor adjacencies if DR or BDR changed */ + if ((ifa->drid != old_drid) || (ifa->bdrid != old_bdrid)) WALK_LIST(neigh, ifa->neigh_list) - { - ospf_neigh_sm(neigh, INM_ADJOK); - } - } + if (neigh->state >= NEIGHBOR_2WAY) + ospf_neigh_sm(neigh, INM_ADJOK); + + /* RFC 2328 12.4 Event 3 - DR change */ + if (ifa->drid != old_drid) + ospf_notify_rt_lsa(ifa->oa); } struct ospf_neighbor * @@ -553,9 +532,9 @@ neighbor_timer_hook(timer * timer) { struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data; struct ospf_iface *ifa = n->ifa; - struct proto *p = &ifa->oa->po->proto; + struct ospf_proto *p = ifa->oa->po; - OSPF_TRACE(D_EVENTS, "Inactivity timer fired on interface %s for neighbor %I.", + OSPF_TRACE(D_EVENTS, "Inactivity timer fired on interface %s for neighbor %I", ifa->ifname, n->ip); ospf_neigh_remove(n); } @@ -564,7 +543,7 @@ void ospf_neigh_remove(struct ospf_neighbor *n) { struct ospf_iface *ifa = n->ifa; - struct proto *p = &ifa->oa->po->proto; + struct ospf_proto *p = ifa->oa->po; if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP)) { @@ -577,20 +556,18 @@ ospf_neigh_remove(struct ospf_neighbor *n) neigh_chstate(n, NEIGHBOR_DOWN); rem_node(NODE n); rfree(n->pool); - OSPF_TRACE(D_EVENTS, "Deleting neigbor."); + OSPF_TRACE(D_EVENTS, "Deleting neigbor %R", n->rid); } static void ospf_neigh_bfd_hook(struct bfd_request *req) { struct ospf_neighbor *n = req->data; - struct proto *p = &n->ifa->oa->po->proto; + struct ospf_proto *p = n->ifa->oa->po; if (req->down) { - OSPF_TRACE(D_EVENTS, "BFD session down for %I on %s", - n->ip, n->ifa->ifname); - + OSPF_TRACE(D_EVENTS, "BFD session down for %I on %s", n->ip, n->ifa->ifname); ospf_neigh_remove(n); } } @@ -646,52 +623,32 @@ static void rxmt_timer_hook(timer * timer) { struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data; - // struct proto *p = &n->ifa->oa->po->proto; - struct top_hash_entry *en; + struct ospf_proto *p = n->ifa->oa->po; - DBG("%s: RXMT timer fired on interface %s for neigh: %I.\n", - p->name, n->ifa->ifname, n->ip); + DBG("%s: RXMT timer fired on interface %s for neigh %I\n", + p->p.name, n->ifa->ifname, n->ip); - if(n->state < NEIGHBOR_EXSTART) return; - - if (n->state == NEIGHBOR_EXSTART) + switch (n->state) { - ospf_dbdes_send(n, 1); + case NEIGHBOR_EXSTART: + ospf_send_dbdes(n, 1); return; - } - if ((n->state == NEIGHBOR_EXCHANGE) && n->myimms.bit.ms) /* I'm master */ - ospf_dbdes_send(n, 0); + case NEIGHBOR_EXCHANGE: + if (n->myimms & DBDES_MS) + ospf_send_dbdes(n, 0); + case NEIGHBOR_LOADING: + ospf_send_lsreq(p, n); + return; + case NEIGHBOR_FULL: + /* LSA retransmissions */ + if (!EMPTY_SLIST(n->lsrtl)) + ospf_rxmt_lsupd(p, n); + return; - if (n->state < NEIGHBOR_FULL) - ospf_lsreq_send(n); /* EXCHANGE or LOADING */ - else - { - if (!EMPTY_SLIST(n->lsrtl)) /* FULL */ - { - list uplist; - slab *upslab; - struct l_lsr_head *llsh; - - init_list(&uplist); - upslab = sl_new(n->pool, sizeof(struct l_lsr_head)); - - WALK_SLIST(en, n->lsrtl) - { - if ((SNODE en)->next == (SNODE en)) - bug("RTList is cycled"); - llsh = sl_alloc(upslab); - llsh->lsh.id = en->lsa.id; - llsh->lsh.rt = en->lsa.rt; - llsh->lsh.type = en->lsa.type; - DBG("Working on ID: %R, RT: %R, Type: %u\n", - en->lsa.id, en->lsa.rt, en->lsa.type); - add_tail(&uplist, NODE llsh); - } - ospf_lsupd_send_list(n, &uplist); - rfree(upslab); - } + default: + return; } } diff --git a/proto/ospf/neighbor.h b/proto/ospf/neighbor.h deleted file mode 100644 index e674927d..00000000 --- a/proto/ospf/neighbor.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * BIRD -- OSPF - * - * (c) 1999 - 2004 Ondrej Filip - * - * Can be freely distributed and used under the terms of the GNU GPL. - * - */ - -#ifndef _BIRD_OSPF_NEIGHBOR_H_ -#define _BIRD_OSPF_NEIGHBOR_H_ - -struct ospf_neighbor *ospf_neighbor_new(struct ospf_iface *ifa); -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); -void ospf_neigh_remove(struct ospf_neighbor *n); -void ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd); -void ospf_sh_neigh_info(struct ospf_neighbor *n); - -#endif /* _BIRD_OSPF_NEIGHBOR_H_ */ diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index 6756ff49..abcd527a 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -35,7 +37,7 @@ * One instance of the protocol is able to hold LSA databases for * multiple OSPF areas, to exchange routing information between * multiple neighbors and to calculate the routing tables. The core - * structure is &proto_ospf to which multiple &ospf_area and + * structure is &ospf_proto to which multiple &ospf_area and * &ospf_iface structures are connected. &ospf_area is also connected to * &top_hash_graph which is a dynamic hashing structure that * describes the link-state database. It allows fast search, addition @@ -69,7 +71,7 @@ * ospf_iface_down()). * * The heart beat of ospf is ospf_disp(). It is called at regular intervals - * (&proto_ospf->tick). It is responsible for aging and flushing of LSAs in + * (&ospf_proto->tick). It is responsible for aging and flushing of LSAs in * the database, for routing table calculaction and it call area_disp() of every * ospf_area. * @@ -102,9 +104,10 @@ #include #include "ospf.h" - -static int ospf_reload_routes(struct proto *p); -static void ospf_rt_notify(struct proto *p, struct rtable *table UNUSED, net * n, rte * new, rte * old UNUSED, ea_list * attrs); +static int ospf_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool); +static struct ea_list *ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool); +static void ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs); +static int ospf_reload_routes(struct proto *P); static int ospf_rte_better(struct rte *new, struct rte *old); static int ospf_rte_same(struct rte *new, struct rte *old); static void ospf_disp(timer *timer); @@ -114,82 +117,78 @@ ospf_area_initfib(struct fib_node *fn) { struct area_net *an = (struct area_net *) fn; an->hidden = 0; - an->active = -1; /* Force to regenerate summary lsa */ - /* ac->oldactive will be rewritten by ospf_rt_spf() */ + an->active = 0; } static void add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac) { - struct proto_ospf *po = oa->po; - struct area_net_config *anc; - struct area_net *an; + struct ospf_proto *p = oa->po; + struct area_net_config *anc; + struct area_net *an; - fib_init(&oa->net_fib, po->proto.pool, sizeof(struct area_net), 0, ospf_area_initfib); - fib_init(&oa->enet_fib, po->proto.pool, sizeof(struct area_net), 0, ospf_area_initfib); + fib_init(&oa->net_fib, p->p.pool, sizeof(struct area_net), 0, ospf_area_initfib); + fib_init(&oa->enet_fib, p->p.pool, sizeof(struct area_net), 0, ospf_area_initfib); - WALK_LIST(anc, ac->net_list) - { - an = (struct area_net *) fib_get(&oa->net_fib, &anc->px.addr, anc->px.len); - an->hidden = anc->hidden; - } + WALK_LIST(anc, ac->net_list) + { + an = (struct area_net *) fib_get(&oa->net_fib, &anc->px.addr, anc->px.len); + an->hidden = anc->hidden; + } - WALK_LIST(anc, ac->enet_list) - { - an = (struct area_net *) fib_get(&oa->enet_fib, &anc->px.addr, anc->px.len); - an->hidden = anc->hidden; - an->tag = anc->tag; - } + WALK_LIST(anc, ac->enet_list) + { + an = (struct area_net *) fib_get(&oa->enet_fib, &anc->px.addr, anc->px.len); + an->hidden = anc->hidden; + an->tag = anc->tag; + } } static void -ospf_area_add(struct proto_ospf *po, struct ospf_area_config *ac, int reconf) +ospf_area_add(struct ospf_proto *p, 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 = mb_allocz(p->p.pool, sizeof(struct ospf_area)); + add_tail(&p->area_list, NODE oa); + p->areano++; oa->ac = ac; oa->areaid = ac->areaid; oa->rt = NULL; - oa->po = po; - fib_init(&oa->rtr, p->pool, sizeof(ort), 0, ospf_rt_initort); + oa->po = p; + fib_init(&oa->rtr, p->p.pool, sizeof(ort), 0, ospf_rt_initort); add_area_nets(oa, ac); if (oa->areaid == 0) - po->backbone = oa; + p->backbone = oa; -#ifdef OSPFv2 - oa->options = ac->type; -#else /* OSPFv3 */ - oa->options = ac->type | OPT_V6 | (po->stub_router ? 0 : OPT_R); -#endif + if (ospf_is_v2(p)) + oa->options = ac->type; + else + oa->options = ac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R); +} - /* - * Set E-bit for NSSA ABR routers. No need to explicitly call - * schedule_rt_lsa() for other areas, will be done anyway. - * We use cf->abr because po->areano is not yet complete. - */ - if (oa_is_nssa(oa) && ((struct ospf_config *) (p->cf))->abr) - po->ebit = 1; +static void +ospf_flush_area(struct ospf_proto *p, u32 areaid) +{ + struct top_hash_entry *en; - if (reconf) - ospf_ifaces_reconfigure(oa, ac); + WALK_SLIST(en, p->lsal) + if ((LSA_SCOPE(en->lsa_type) == LSA_SCOPE_AREA) && (en->domain == areaid)) + ospf_flush_lsa(p, en); } static void ospf_area_remove(struct ospf_area *oa) { - struct proto *p = &oa->po->proto; + struct ospf_proto *p = oa->po; OSPF_TRACE(D_EVENTS, "Removing area %R", oa->areaid); /* We suppose that interfaces are already removed */ - ospf_flush_area(oa->po, oa->areaid); + ospf_flush_area(p, oa->areaid); fib_free(&oa->rtr); fib_free(&oa->net_fib); @@ -198,87 +197,88 @@ ospf_area_remove(struct ospf_area *oa) if (oa->translator_timer) rfree(oa->translator_timer); - oa->po->areano--; + p->areano--; rem_node(NODE oa); mb_free(oa); } struct ospf_area * -ospf_find_area(struct proto_ospf *po, u32 aid) +ospf_find_area(struct ospf_proto *p, u32 aid) { struct ospf_area *oa; - WALK_LIST(oa, po->area_list) + WALK_LIST(oa, p->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) +ospf_find_vlink(struct ospf_proto *p, u32 voa, u32 vid) { struct ospf_iface *ifa; - WALK_LIST(ifa, po->iface_list) + WALK_LIST(ifa, p->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) +ospf_start(struct proto *P) { - struct proto_ospf *po = (struct proto_ospf *) p; - struct ospf_config *c = (struct ospf_config *) (p->cf); + struct ospf_proto *p = (struct ospf_proto *) P; + struct ospf_config *c = (struct ospf_config *) (P->cf); struct ospf_area_config *ac; - po->router_id = proto_get_router_id(p->cf); - po->rfc1583 = c->rfc1583; - po->stub_router = c->stub_router; - po->merge_external = c->merge_external; - po->ebit = 0; - po->ecmp = c->ecmp; - po->tick = c->tick; - po->disp_timer = tm_new(p->pool); - po->disp_timer->data = po; - po->disp_timer->randomize = 0; - po->disp_timer->hook = ospf_disp; - po->disp_timer->recurrent = po->tick; - tm_start(po->disp_timer, 1); - po->lsab_size = 256; - po->lsab_used = 0; - po->lsab = mb_alloc(p->pool, po->lsab_size); - po->nhpool = lp_new(p->pool, 12*sizeof(struct mpnh)); - init_list(&(po->iface_list)); - init_list(&(po->area_list)); - fib_init(&po->rtf, p->pool, sizeof(ort), 0, ospf_rt_initort); - po->areano = 0; - po->gr = ospf_top_new(p->pool); - s_init_list(&(po->lsal)); + p->router_id = proto_get_router_id(P->cf); + p->ospf2 = c->ospf2; + p->rfc1583 = c->rfc1583; + p->stub_router = c->stub_router; + p->merge_external = c->merge_external; + p->asbr = c->asbr; + p->ecmp = c->ecmp; + p->tick = c->tick; + p->disp_timer = tm_new(P->pool); + p->disp_timer->data = p; + p->disp_timer->randomize = 0; + p->disp_timer->hook = ospf_disp; + p->disp_timer->recurrent = p->tick; + tm_start(p->disp_timer, 1); + p->lsab_size = 256; + p->lsab_used = 0; + p->lsab = mb_alloc(P->pool, p->lsab_size); + p->nhpool = lp_new(P->pool, 12*sizeof(struct mpnh)); + init_list(&(p->iface_list)); + init_list(&(p->area_list)); + fib_init(&p->rtf, P->pool, sizeof(ort), 0, ospf_rt_initort); + p->areano = 0; + p->gr = ospf_top_new(P->pool); + s_init_list(&(p->lsal)); WALK_LIST(ac, c->area_list) - ospf_area_add(po, ac, 0); + ospf_area_add(p, ac, 0); if (c->abr) - ospf_open_vlink_sk(po); + ospf_open_vlink_sk(p); /* Add all virtual links */ struct ospf_iface_patt *ic; WALK_LIST(ic, c->vlink_list) - ospf_iface_new_vlink(po, ic); + ospf_iface_new_vlink(p, ic); return PS_UP; } static void -ospf_dump(struct proto *p) +ospf_dump(struct proto *P) { + struct ospf_proto *p = (struct ospf_proto *) P; struct ospf_iface *ifa; struct ospf_neighbor *n; - struct proto_ospf *po = (struct proto_ospf *) p; - OSPF_TRACE(D_EVENTS, "Area number: %d", po->areano); + OSPF_TRACE(D_EVENTS, "Area number: %d", p->areano); - WALK_LIST(ifa, po->iface_list) + WALK_LIST(ifa, p->iface_list) { OSPF_TRACE(D_EVENTS, "Interface: %s", ifa->ifname); OSPF_TRACE(D_EVENTS, "state: %u", ifa->state); @@ -292,7 +292,7 @@ ospf_dump(struct proto *p) /* OSPF_TRACE(D_EVENTS, "LSA graph dump start:"); - ospf_top_dump(po->gr, p); + ospf_top_dump(p->gr, p); OSPF_TRACE(D_EVENTS, "LSA graph dump finished"); */ neigh_dump_all(); @@ -301,20 +301,21 @@ ospf_dump(struct proto *p) static struct proto * ospf_init(struct proto_config *c) { - struct proto *p = proto_new(c, sizeof(struct proto_ospf)); + struct ospf_config *oc = (struct ospf_config *) c; + struct proto *P = proto_new(c, sizeof(struct ospf_proto)); - p->accept_ra_types = RA_OPTIMAL; - p->rt_notify = ospf_rt_notify; - p->if_notify = ospf_if_notify; - p->ifa_notify = ospf_ifa_notify; - p->import_control = ospf_import_control; - p->reload_routes = ospf_reload_routes; - p->make_tmp_attrs = ospf_make_tmp_attrs; - p->store_tmp_attrs = ospf_store_tmp_attrs; - p->rte_better = ospf_rte_better; - p->rte_same = ospf_rte_same; + P->accept_ra_types = RA_OPTIMAL; + P->rt_notify = ospf_rt_notify; + P->if_notify = ospf_if_notify; + P->ifa_notify = oc->ospf2 ? ospf_ifa_notify2 : ospf_ifa_notify3; + P->import_control = ospf_import_control; + P->reload_routes = ospf_reload_routes; + P->make_tmp_attrs = ospf_make_tmp_attrs; + P->store_tmp_attrs = ospf_store_tmp_attrs; + P->rte_better = ospf_rte_better; + P->rte_same = ospf_rte_same; - return p; + return P; } /* If new is better return 1 */ @@ -379,116 +380,53 @@ ospf_build_attrs(ea_list * next, struct linpool *pool, u32 m1, u32 m2, return l; } -void -schedule_net_lsa(struct ospf_iface *ifa) -{ - struct proto *p = &ifa->oa->po->proto; - - OSPF_TRACE(D_EVENTS, "Scheduling network-LSA origination for iface %s", ifa->ifname); - ifa->orignet = 1; -} - -#ifdef OSPFv3 -void -schedule_link_lsa(struct ospf_iface *ifa) -{ - struct proto *p = &ifa->oa->po->proto; - - OSPF_TRACE(D_EVENTS, "Scheduling link-LSA origination for iface %s", ifa->ifname); - ifa->origlink = 1; -} -#endif void -schedule_rt_lsa(struct ospf_area *oa) +schedule_rtcalc(struct ospf_proto *p) { - struct proto *p = &oa->po->proto; - - OSPF_TRACE(D_EVENTS, "Scheduling router-LSA origination for area %R", oa->areaid); - oa->origrt = 1; -} - -void -schedule_rtcalc(struct proto_ospf *po) -{ - struct proto *p = &po->proto; - - if (po->calcrt) + if (p->calcrt) return; OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation"); - po->calcrt = 1; + p->calcrt = 1; } static int -ospf_reload_routes(struct proto *p) +ospf_reload_routes(struct proto *P) { - struct proto_ospf *po = (struct proto_ospf *) p; + struct ospf_proto *p = (struct ospf_proto *) P; - if (po->calcrt != 2) + if (p->calcrt != 2) OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation with route reload"); - po->calcrt = 2; + p->calcrt = 2; return 1; } -/** - * area_disp - invokes origination of - * router LSA and routing table cleanup - * @oa: ospf area - * - * It invokes aging and when @ospf_area->origrt is set to 1, start - * function for origination of router, network LSAs. - */ -void -area_disp(struct ospf_area *oa) -{ - struct proto_ospf *po = oa->po; - struct ospf_iface *ifa; - - /* Now try to originage rt_lsa */ - if (oa->origrt) - update_rt_lsa(oa); - - /* Now try to originate network LSA's */ - WALK_LIST(ifa, po->iface_list) - { -#ifdef OSPFv3 - /* Link LSA should be originated before Network LSA */ - if (ifa->origlink && (ifa->oa == oa)) - update_link_lsa(ifa); -#endif - - if (ifa->orignet && (ifa->oa == oa)) - update_net_lsa(ifa); - } -} /** * ospf_disp - invokes routing table calculation, aging and also area_disp() - * @timer: timer usually called every @proto_ospf->tick second, @timer->data - * point to @proto_ospf + * @timer: timer usually called every @ospf_proto->tick second, @timer->data + * point to @ospf_proto */ -void +static void ospf_disp(timer * timer) { - struct proto_ospf *po = timer->data; - struct ospf_area *oa; + struct ospf_proto *p = timer->data; - WALK_LIST(oa, po->area_list) - area_disp(oa); + /* Originate or flush local topology LSAs */ + ospf_update_topology(p); /* Age LSA DB */ - ospf_age(po); + ospf_update_lsadb(p); /* Calculate routing table */ - if (po->calcrt) - ospf_rt_spf(po); + if (p->calcrt) + ospf_rt_spf(p); } - /** * ospf_import_control - accept or reject new route from nest's routing table * @p: current instance of protocol @@ -499,15 +437,14 @@ ospf_disp(timer * timer) * Its quite simple. It does not accept our own routes and leaves the decision on * import to the filters. */ - -int -ospf_import_control(struct proto *p, rte ** new, ea_list ** attrs, - struct linpool *pool) +static int +ospf_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool) { - struct ospf_area *oa = ospf_main_area((struct proto_ospf *) p); + struct ospf_proto *p = (struct ospf_proto *) P; + struct ospf_area *oa = ospf_main_area(p); rte *e = *new; - if (e->attrs->src->proto == p) + if (e->attrs->src->proto == P) return -1; /* Reject our own routes */ if (oa_is_stub(oa)) @@ -520,14 +457,14 @@ ospf_import_control(struct proto *p, rte ** new, ea_list ** attrs, return 0; /* Leave decision to the filters */ } -struct ea_list * +static struct ea_list * ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool) { return ospf_build_attrs(NULL, pool, rt->u.ospf.metric1, rt->u.ospf.metric2, rt->u.ospf.tag, rt->u.ospf.router_id); } -void +static void ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs) { rt->u.ospf.metric1 = ea_get_int(attrs, EA_OSPF_METRIC1, LSINFINITY); @@ -545,20 +482,20 @@ ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs) * them hello packet with empty neighbor list. They should start * their neighbor state machine with event %NEIGHBOR_1WAY. */ - static int -ospf_shutdown(struct proto *p) +ospf_shutdown(struct proto *P) { - struct proto_ospf *po = (struct proto_ospf *) p; + struct ospf_proto *p = (struct ospf_proto *) P; struct ospf_iface *ifa; + OSPF_TRACE(D_EVENTS, "Shutdown requested"); /* And send to all my neighbors 1WAY */ - WALK_LIST(ifa, po->iface_list) + WALK_LIST(ifa, p->iface_list) ospf_iface_shutdown(ifa); /* Cleanup locked rta entries */ - FIB_WALK(&po->rtf, nftmp) + FIB_WALK(&p->rtf, nftmp) { rta_free(((ort *) nftmp)->old_rta); } @@ -568,49 +505,11 @@ ospf_shutdown(struct proto *p) } static void -ospf_rt_notify(struct proto *p, rtable *tbl UNUSED, net * n, rte * new, rte * old UNUSED, ea_list * attrs) +ospf_get_status(struct proto *P, byte * buf) { - struct proto_ospf *po = (struct proto_ospf *) p; - struct ospf_area *oa = ospf_main_area(po); - ort *nf = (ort *) fib_get(&po->rtf, &n->n.prefix, n->n.pxlen); - struct fib_node *fn = &nf->fn; + struct ospf_proto *p = (struct ospf_proto *) P; - if (!new) - { - if (fn->x1 != EXT_EXPORT) - return; - - flush_ext_lsa(oa, fn, oa_is_nssa(oa)); - - /* Old external route might blocked some NSSA translation */ - if (po->areano > 1) - schedule_rtcalc(po); - - return; - } - - /* Get route attributes */ - u32 m1 = ea_get_int(attrs, EA_OSPF_METRIC1, LSINFINITY); - u32 m2 = ea_get_int(attrs, EA_OSPF_METRIC2, 10000); - u32 metric = (m1 != LSINFINITY) ? m1 : (m2 | LSA_EXT_EBIT); - u32 tag = ea_get_int(attrs, EA_OSPF_TAG, 0); - ip_addr gw = IPA_NONE; - // FIXME check for gw should be per ifa, not per iface - if ((new->attrs->dest == RTD_ROUTER) && - ipa_nonzero(new->attrs->gw) && - !ipa_has_link_scope(new->attrs->gw) && - (ospf_iface_find((struct proto_ospf *) p, new->attrs->iface) != NULL)) - gw = new->attrs->gw; - - originate_ext_lsa(oa, fn, EXT_EXPORT, metric, gw, tag, 1); -} - -static void -ospf_get_status(struct proto *p, byte * buf) -{ - struct proto_ospf *po = (struct proto_ospf *) p; - - if (p->proto_state == PS_DOWN) + if (p->p.proto_state == PS_DOWN) buf[0] = 0; else { @@ -618,7 +517,7 @@ ospf_get_status(struct proto *p, byte * buf) struct ospf_neighbor *n; int adj = 0; - WALK_LIST(ifa, po->iface_list) + WALK_LIST(ifa, p->iface_list) WALK_LIST(n, ifa->neigh_list) if (n->state == NEIGHBOR_FULL) adj = 1; @@ -636,18 +535,18 @@ ospf_get_route_info(rte * rte, byte * buf, ea_list * attrs UNUSED) switch (rte->attrs->source) { - case RTS_OSPF: - type = "I"; - break; - case RTS_OSPF_IA: - type = "IA"; - break; - case RTS_OSPF_EXT1: - type = "E1"; - break; - case RTS_OSPF_EXT2: - type = "E2"; - break; + case RTS_OSPF: + type = "I"; + break; + case RTS_OSPF_IA: + type = "IA"; + break; + case RTS_OSPF_EXT1: + type = "E1"; + break; + case RTS_OSPF_EXT2: + type = "E2"; + break; } buf += bsprintf(buf, " %s", type); @@ -688,18 +587,24 @@ ospf_get_attr(eattr * a, byte * buf, int buflen UNUSED) static void ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) { + struct ospf_proto *p = oa->po; + struct ospf_area_config *oac = oa->ac; + struct ospf_iface *ifa; + oa->ac = nac; - // FIXME better area type reconfiguration -#ifdef OSPFv2 - oa->options = nac->type; -#else /* OSPFv3 */ - oa->options = nac->type | OPT_V6 | (oa->po->stub_router ? 0 : OPT_R); -#endif - if (oa_is_nssa(oa) && (oa->po->areano > 1)) - oa->po->ebit = 1; + if (ospf_is_v2(p)) + oa->options = nac->type; + else + oa->options = nac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R); - ospf_ifaces_reconfigure(oa, nac); + if (nac->type != oac->type) + { + /* Force restart of area interfaces */ + WALK_LIST(ifa, p->iface_list) + if (ifa->oa == oa) + ifa->marked = 2; + } /* Handle net_list */ fib_free(&oa->net_fib); @@ -709,7 +614,7 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) /* No need to handle stubnet_list */ oa->marked = 0; - schedule_rt_lsa(oa); + ospf_notify_rt_lsa(oa); } /** @@ -723,95 +628,99 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) * nonbroadcast network, cost of interface, etc. */ static int -ospf_reconfigure(struct proto *p, struct proto_config *c) +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_proto *p = (struct ospf_proto *) P; + struct ospf_config *old = (struct ospf_config *) (P->cf); struct ospf_config *new = (struct ospf_config *) c; struct ospf_area_config *nac; struct ospf_area *oa, *oax; struct ospf_iface *ifa, *ifx; struct ospf_iface_patt *ip; - if (proto_get_router_id(c) != po->router_id) + if (proto_get_router_id(c) != p->router_id) return 0; - if (po->rfc1583 != new->rfc1583) + if (p->rfc1583 != new->rfc1583) return 0; if (old->abr != new->abr) return 0; - po->stub_router = new->stub_router; - po->merge_external = new->merge_external; - po->ecmp = new->ecmp; - po->tick = new->tick; - po->disp_timer->recurrent = po->tick; - tm_start(po->disp_timer, 1); + p->stub_router = new->stub_router; + p->merge_external = new->merge_external; + p->asbr = new->asbr; + p->ecmp = new->ecmp; + p->tick = new->tick; + p->disp_timer->recurrent = p->tick; + tm_start(p->disp_timer, 1); /* Mark all areas and ifaces */ - WALK_LIST(oa, po->area_list) + WALK_LIST(oa, p->area_list) oa->marked = 1; - WALK_LIST(ifa, po->iface_list) + WALK_LIST(ifa, p->iface_list) ifa->marked = 1; /* Add and update areas */ WALK_LIST(nac, new->area_list) { - oa = ospf_find_area(po, nac->areaid); + oa = ospf_find_area(p, nac->areaid); if (oa) ospf_area_reconfigure(oa, nac); else - ospf_area_add(po, nac, 1); + ospf_area_add(p, nac, 1); } + /* Add and update interfaces */ + ospf_reconfigure_ifaces(p); + /* Add and update vlinks */ WALK_LIST(ip, new->vlink_list) { - ifa = ospf_find_vlink(po, ip->voa, ip->vid); + ifa = ospf_find_vlink(p, ip->voa, ip->vid); if (ifa) ospf_iface_reconfigure(ifa, ip); else - ospf_iface_new_vlink(po, ip); + ospf_iface_new_vlink(p, ip); } /* Delete remaining ifaces and areas */ - WALK_LIST_DELSAFE(ifa, ifx, po->iface_list) + WALK_LIST_DELSAFE(ifa, ifx, p->iface_list) if (ifa->marked) { ospf_iface_shutdown(ifa); ospf_iface_remove(ifa); } - WALK_LIST_DELSAFE(oa, oax, po->area_list) + WALK_LIST_DELSAFE(oa, oax, p->area_list) if (oa->marked) ospf_area_remove(oa); - schedule_rtcalc(po); + schedule_rtcalc(p); return 1; } void -ospf_sh_neigh(struct proto *p, char *iff) +ospf_sh_neigh(struct proto *P, char *iff) { + struct ospf_proto *p = (struct ospf_proto *) P; struct ospf_iface *ifa = NULL; struct ospf_neighbor *n; - struct proto_ospf *po = (struct proto_ospf *) p; - if (p->proto_state != PS_UP) + if (p->p.proto_state != PS_UP) { - cli_msg(-1013, "%s: is not up", p->name); + cli_msg(-1013, "%s: is not up", p->p.name); cli_msg(0, ""); return; } - cli_msg(-1013, "%s:", p->name); + cli_msg(-1013, "%s:", p->p.name); cli_msg(-1013, "%-12s\t%3s\t%-15s\t%-5s\t%-10s %-12s", "Router ID", "Pri", " State", "DTime", "Interface", "Router IP"); - WALK_LIST(ifa, po->iface_list) + WALK_LIST(ifa, p->iface_list) if ((iff == NULL) || patmatch(iff, ifa->ifname)) WALK_LIST(n, ifa->neigh_list) ospf_sh_neigh_info(n); @@ -819,37 +728,37 @@ ospf_sh_neigh(struct proto *p, char *iff) } void -ospf_sh(struct proto *p) +ospf_sh(struct proto *P) { + struct ospf_proto *p = (struct ospf_proto *) P; struct ospf_area *oa; - struct proto_ospf *po = (struct proto_ospf *) p; struct ospf_iface *ifa; struct ospf_neighbor *n; int ifano, nno, adjno, firstfib; struct area_net *anet; - if (p->proto_state != PS_UP) + if (p->p.proto_state != PS_UP) { - cli_msg(-1014, "%s: is not up", p->name); + cli_msg(-1014, "%s: is not up", p->p.name); cli_msg(0, ""); return; } - cli_msg(-1014, "%s:", p->name); - cli_msg(-1014, "RFC1583 compatibility: %s", (po->rfc1583 ? "enable" : "disabled")); - cli_msg(-1014, "Stub router: %s", (po->stub_router ? "Yes" : "No")); - cli_msg(-1014, "RT scheduler tick: %d", po->tick); - cli_msg(-1014, "Number of areas: %u", po->areano); - cli_msg(-1014, "Number of LSAs in DB:\t%u", po->gr->hash_entries); + cli_msg(-1014, "%s:", p->p.name); + cli_msg(-1014, "RFC1583 compatibility: %s", (p->rfc1583 ? "enable" : "disabled")); + cli_msg(-1014, "Stub router: %s", (p->stub_router ? "Yes" : "No")); + cli_msg(-1014, "RT scheduler tick: %d", p->tick); + cli_msg(-1014, "Number of areas: %u", p->areano); + cli_msg(-1014, "Number of LSAs in DB:\t%u", p->gr->hash_entries); - WALK_LIST(oa, po->area_list) + WALK_LIST(oa, p->area_list) { cli_msg(-1014, "\tArea: %R (%u) %s", oa->areaid, oa->areaid, oa->areaid == 0 ? "[BACKBONE]" : ""); ifano = 0; nno = 0; adjno = 0; - WALK_LIST(ifa, po->iface_list) + WALK_LIST(ifa, p->iface_list) { if (oa == ifa->oa) { @@ -907,20 +816,20 @@ ospf_sh(struct proto *p) } void -ospf_sh_iface(struct proto *p, char *iff) +ospf_sh_iface(struct proto *P, char *iff) { - struct proto_ospf *po = (struct proto_ospf *) p; + struct ospf_proto *p = (struct ospf_proto *) P; struct ospf_iface *ifa = NULL; - if (p->proto_state != PS_UP) + if (p->p.proto_state != PS_UP) { - cli_msg(-1015, "%s: is not up", p->name); + cli_msg(-1015, "%s: is not up", p->p.name); cli_msg(0, ""); return; } - cli_msg(-1015, "%s:", p->name); - WALK_LIST(ifa, po->iface_list) + cli_msg(-1015, "%s:", p->p.name); + WALK_LIST(ifa, p->iface_list) if ((iff == NULL) || patmatch(iff, ifa->ifname)) ospf_iface_info(ifa); cli_msg(0, ""); @@ -939,14 +848,12 @@ ospf_sh_iface(struct proto *p, char *iff) * values */ -#ifdef OSPFv3 - static struct ospf_lsa_header * fake_lsa_from_prefix_lsa(struct ospf_lsa_header *dst, struct ospf_lsa_header *src, struct ospf_lsa_prefix *px) { dst->age = src->age; - dst->type = px->ref_type; + dst->type_raw = px->ref_type; dst->id = px->ref_id; dst->rt = px->ref_rt; dst->sn = src->sn; @@ -954,49 +861,59 @@ fake_lsa_from_prefix_lsa(struct ospf_lsa_header *dst, struct ospf_lsa_header *sr return dst; } -#endif + +static int lsa_compare_ospf3; static int lsa_compare_for_state(const void *p1, const void *p2) { - struct top_hash_entry * he1 = * (struct top_hash_entry **) p1; - struct top_hash_entry * he2 = * (struct top_hash_entry **) p2; + struct top_hash_entry *he1 = * (struct top_hash_entry **) p1; + struct top_hash_entry *he2 = * (struct top_hash_entry **) p2; struct ospf_lsa_header *lsa1 = &(he1->lsa); struct ospf_lsa_header *lsa2 = &(he2->lsa); + struct ospf_lsa_header lsatmp1, lsatmp2; + u16 lsa1_type = he1->lsa_type; + u16 lsa2_type = he2->lsa_type; if (he1->domain < he2->domain) return -1; if (he1->domain > he2->domain) return 1; -#ifdef OSPFv3 - struct ospf_lsa_header lsatmp1, lsatmp2; - int px1 = (lsa1->type == LSA_T_PREFIX); - int px2 = (lsa2->type == LSA_T_PREFIX); + /* px1 or px2 assumes OSPFv3 */ + int px1 = (lsa1_type == LSA_T_PREFIX); + int px2 = (lsa2_type == LSA_T_PREFIX); if (px1) + { lsa1 = fake_lsa_from_prefix_lsa(&lsatmp1, lsa1, he1->lsa_body); + lsa1_type = lsa1->type_raw; /* FIXME: handle unknown ref_type */ + } if (px2) + { lsa2 = fake_lsa_from_prefix_lsa(&lsatmp2, lsa2, he2->lsa_body); -#endif + lsa2_type = lsa2->type_raw; + } - int nt1 = (lsa1->type == LSA_T_NET); - int nt2 = (lsa2->type == LSA_T_NET); + + int nt1 = (lsa1_type == LSA_T_NET); + int nt2 = (lsa2_type == LSA_T_NET); if (nt1 != nt2) return nt1 - nt2; if (nt1) { -#ifdef OSPFv3 - /* In OSPFv3, neworks are named base on ID of DR */ - if (lsa1->rt < lsa2->rt) - return -1; - if (lsa1->rt > lsa2->rt) - return 1; -#endif + /* In OSPFv3, networks are named based on ID of DR */ + if (lsa_compare_ospf3) + { + if (lsa1->rt < lsa2->rt) + return -1; + if (lsa1->rt > lsa2->rt) + return 1; + } /* For OSPFv2, this is IP of the network, for OSPFv3, this is interface ID */ @@ -1005,10 +922,8 @@ lsa_compare_for_state(const void *p1, const void *p2) if (lsa1->id > lsa2->id) return 1; -#ifdef OSPFv3 if (px1 != px2) return px1 - px2; -#endif return lsa1->sn - lsa2->sn; } @@ -1019,9 +934,9 @@ lsa_compare_for_state(const void *p1, const void *p2) if (lsa1->rt > lsa2->rt) return 1; - if (lsa1->type < lsa2->type) + if (lsa1_type < lsa2_type) return -1; - if (lsa1->type > lsa2->type) + if (lsa1_type > lsa2_type) return 1; if (lsa1->id < lsa2->id) @@ -1029,10 +944,8 @@ lsa_compare_for_state(const void *p1, const void *p2) if (lsa1->id > lsa2->id) return 1; -#ifdef OSPFv3 if (px1 != px2) return px1 - px2; -#endif return lsa1->sn - lsa2->sn; } @@ -1069,79 +982,77 @@ show_lsa_distance(struct top_hash_entry *he) } static inline void -show_lsa_router(struct proto_ospf *po, struct top_hash_entry *he, int first, int verbose) +show_lsa_router(struct ospf_proto *p, struct top_hash_entry *he, int verbose) { - struct ospf_lsa_header *lsa = &(he->lsa); - struct ospf_lsa_rt *rt = he->lsa_body; - struct ospf_lsa_rt_link *rr = (struct ospf_lsa_rt_link *) (rt + 1); - int max = lsa_rt_count(lsa); - int i; + struct ospf_lsa_rt_walk rtl; - if (first) - { - cli_msg(-1016, ""); - cli_msg(-1016, "\trouter %R", he->lsa.rt); - show_lsa_distance(he); - } + cli_msg(-1016, ""); + cli_msg(-1016, "\trouter %R", he->lsa.rt); + show_lsa_distance(he); + lsa_walk_rt_init(p, he, &rtl); + while (lsa_walk_rt(&rtl)) + if (rtl.type == LSART_VLNK) + cli_msg(-1016, "\t\tvlink %R metric %u", rtl.id, rtl.metric); - for (i = 0; i < max; i++) - if (rr[i].type == LSART_VLNK) - cli_msg(-1016, "\t\tvlink %R metric %u", rr[i].id, rr[i].metric); + lsa_walk_rt_init(p, he, &rtl); + while (lsa_walk_rt(&rtl)) + if (rtl.type == LSART_PTP) + cli_msg(-1016, "\t\trouter %R metric %u", rtl.id, rtl.metric); - for (i = 0; i < max; i++) - if (rr[i].type == LSART_PTP) - cli_msg(-1016, "\t\trouter %R metric %u", rr[i].id, rr[i].metric); - - for (i = 0; i < max; i++) - if (rr[i].type == LSART_NET) + lsa_walk_rt_init(p, he, &rtl); + while (lsa_walk_rt(&rtl)) + if (rtl.type == LSART_NET) { -#ifdef OSPFv2 - struct top_hash_entry *net_he = ospf_hash_find_net(po->gr, he->domain, rr[i].id); - - if (net_he) + if (ospf_is_v2(p)) { - struct ospf_lsa_header *net_lsa = &(net_he->lsa); - struct ospf_lsa_net *net_ln = net_he->lsa_body; + /* In OSPFv2, we try to find network-LSA to get prefix/pxlen */ + struct top_hash_entry *net_he = ospf_hash_find_net2(p->gr, he->domain, rtl.id); - cli_msg(-1016, "\t\tnetwork %I/%d metric %u", - ipa_and(ipa_from_u32(net_lsa->id), net_ln->netmask), - ipa_mklen(net_ln->netmask), rr[i].metric); + if (net_he) + { + struct ospf_lsa_header *net_lsa = &(net_he->lsa); + struct ospf_lsa_net *net_ln = net_he->lsa_body; + + cli_msg(-1016, "\t\tnetwork %I/%d metric %u", + ipa_from_u32(net_lsa->id & net_ln->optx), + u32_masklen(net_ln->optx), rtl.metric); + } + else + cli_msg(-1016, "\t\tnetwork [%R] metric %u", rtl.id, rtl.metric); } else - cli_msg(-1016, "\t\tnetwork [%R] metric %u", rr[i].id, rr[i].metric); - -#else /* OSPFv3 */ - cli_msg(-1016, "\t\tnetwork [%R-%u] metric %u", rr[i].id, rr[i].nif, rr[i].metric); -#endif + cli_msg(-1016, "\t\tnetwork [%R-%u] metric %u", rtl.id, rtl.nif, rtl.metric); } -#ifdef OSPFv2 - if (!verbose) - return; - - for (i = 0; i < max; i++) - if (rr[i].type == LSART_STUB) - cli_msg(-1016, "\t\tstubnet %I/%d metric %u", ipa_from_u32(rr[i].id), - ipa_mklen(ipa_from_u32(rr[i].data)), rr[i].metric); -#endif + if (ospf_is_v2(p) && verbose) + { + lsa_walk_rt_init(p, he, &rtl); + while (lsa_walk_rt(&rtl)) + if (rtl.type == LSART_STUB) + cli_msg(-1016, "\t\tstubnet %I/%d metric %u", + ipa_from_u32(rtl.id), u32_masklen(rtl.data), rtl.metric); + } } static inline void -show_lsa_network(struct top_hash_entry *he) +show_lsa_network(struct top_hash_entry *he, int ospf2) { struct ospf_lsa_header *lsa = &(he->lsa); struct ospf_lsa_net *ln = he->lsa_body; u32 i; -#ifdef OSPFv2 - cli_msg(-1016, ""); - cli_msg(-1016, "\tnetwork %I/%d", ipa_and(ipa_from_u32(lsa->id), ln->netmask), ipa_mklen(ln->netmask)); - cli_msg(-1016, "\t\tdr %R", lsa->rt); -#else /* OSPFv3 */ - cli_msg(-1016, ""); - cli_msg(-1016, "\tnetwork [%R-%u]", lsa->rt, lsa->id); -#endif + if (ospf2) + { + cli_msg(-1016, ""); + cli_msg(-1016, "\tnetwork %I/%d", ipa_from_u32(lsa->id & ln->optx), u32_masklen(ln->optx)); + cli_msg(-1016, "\t\tdr %R", lsa->rt); + } + else + { + cli_msg(-1016, ""); + cli_msg(-1016, "\tnetwork [%R-%u]", lsa->rt, lsa->id); + } show_lsa_distance(he); @@ -1150,97 +1061,54 @@ show_lsa_network(struct top_hash_entry *he) } static inline void -show_lsa_sum_net(struct top_hash_entry *he) +show_lsa_sum_net(struct top_hash_entry *he, int ospf2) { ip_addr ip; int pxlen; - -#ifdef OSPFv2 - struct ospf_lsa_sum *ls = he->lsa_body; - pxlen = ipa_mklen(ls->netmask); - ip = ipa_and(ipa_from_u32(he->lsa.id), ls->netmask); -#else /* OSPFv3 */ u8 pxopts; - u16 rest; - struct ospf_lsa_sum_net *ls = he->lsa_body; - lsa_get_ipv6_prefix(ls->prefix, &ip, &pxlen, &pxopts, &rest); -#endif + u32 metric; - cli_msg(-1016, "\t\txnetwork %I/%d metric %u", ip, pxlen, ls->metric); + lsa_parse_sum_net(he, ospf2, &ip, &pxlen, &pxopts, &metric); + cli_msg(-1016, "\t\txnetwork %I/%d metric %u", ip, pxlen, metric); } static inline void -show_lsa_sum_rt(struct top_hash_entry *he) +show_lsa_sum_rt(struct top_hash_entry *he, int ospf2) { + u32 metric; u32 dst_rid; + u32 options; -#ifdef OSPFv2 - struct ospf_lsa_sum *ls = he->lsa_body; - dst_rid = he->lsa.id; - // options = 0; -#else /* OSPFv3 */ - struct ospf_lsa_sum_rt *ls = he->lsa_body; - dst_rid = ls->drid; - // options = ls->options & OPTIONS_MASK; -#endif - - cli_msg(-1016, "\t\txrouter %R metric %u", dst_rid, ls->metric); + lsa_parse_sum_rt(he, ospf2, &dst_rid, &metric, &options); + cli_msg(-1016, "\t\txrouter %R metric %u", dst_rid, metric); } static inline void -show_lsa_external(struct top_hash_entry *he) +show_lsa_external(struct top_hash_entry *he, int ospf2) { - struct ospf_lsa_ext *ext = he->lsa_body; + struct ospf_lsa_ext_local rt; char str_via[STD_ADDRESS_P_LENGTH + 8] = ""; char str_tag[16] = ""; - ip_addr ip, rt_fwaddr; - int pxlen, ebit, rt_fwaddr_valid; - u32 rt_tag, rt_metric; - if (he->lsa.type == LSA_T_EXT) + if (he->lsa_type == LSA_T_EXT) he->domain = 0; /* Unmark the LSA */ - rt_metric = ext->metric & METRIC_MASK; - ebit = ext->metric & LSA_EXT_EBIT; -#ifdef OSPFv2 - ip = ipa_and(ipa_from_u32(he->lsa.id), ext->netmask); - pxlen = ipa_mklen(ext->netmask); - rt_fwaddr = ext->fwaddr; - rt_fwaddr_valid = !ipa_equal(rt_fwaddr, IPA_NONE); - rt_tag = ext->tag; -#else /* OSPFv3 */ - u8 pxopts; - u16 rest; - u32 *buf = ext->rest; - buf = lsa_get_ipv6_prefix(buf, &ip, &pxlen, &pxopts, &rest); - - rt_fwaddr_valid = ext->metric & LSA_EXT_FBIT; - if (rt_fwaddr_valid) - buf = lsa_get_ipv6_addr(buf, &rt_fwaddr); - else - rt_fwaddr = IPA_NONE; - - if (ext->metric & LSA_EXT_TBIT) - rt_tag = *buf++; - else - rt_tag = 0; -#endif + lsa_parse_ext(he, ospf2, &rt); - if (rt_fwaddr_valid) - bsprintf(str_via, " via %I", rt_fwaddr); + if (rt.fbit) + bsprintf(str_via, " via %I", rt.fwaddr); - if (rt_tag) - bsprintf(str_tag, " tag %08x", rt_tag); + if (rt.tag) + bsprintf(str_tag, " tag %08x", rt.tag); cli_msg(-1016, "\t\t%s %I/%d metric%s %u%s%s", - (he->lsa.type == LSA_T_NSSA) ? "nssa-ext" : "external", - ip, pxlen, ebit ? "2" : "", rt_metric, str_via, str_tag); + (he->lsa_type == LSA_T_NSSA) ? "nssa-ext" : "external", + rt.ip, rt.pxlen, rt.ebit ? "2" : "", rt.metric, str_via, str_tag); } -#ifdef OSPFv3 static inline void -show_lsa_prefix(struct top_hash_entry *he, struct ospf_lsa_header *cnode) +show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode) { struct ospf_lsa_prefix *px = he->lsa_body; ip_addr pxa; @@ -1251,13 +1119,13 @@ show_lsa_prefix(struct top_hash_entry *he, struct ospf_lsa_header *cnode) int i; /* We check whether given prefix-LSA is related to the current node */ - if ((px->ref_type != cnode->type) || (px->ref_rt != cnode->rt)) + if ((px->ref_type != cnode->lsa.type_raw) || (px->ref_rt != cnode->lsa.rt)) return; if ((px->ref_type == LSA_T_RT) && (px->ref_id != 0)) return; - if ((px->ref_type == LSA_T_NET) && (px->ref_id != cnode->id)) + if ((px->ref_type == LSA_T_NET) && (px->ref_id != cnode->lsa.id)) return; buf = px->rest; @@ -1271,19 +1139,18 @@ show_lsa_prefix(struct top_hash_entry *he, struct ospf_lsa_header *cnode) cli_msg(-1016, "\t\taddress %I/%d", pxa, pxlen); } } -#endif void -ospf_sh_state(struct proto *p, int verbose, int reachable) +ospf_sh_state(struct proto *P, int verbose, int reachable) { - struct proto_ospf *po = (struct proto_ospf *) p; - struct ospf_lsa_header *cnode = NULL; - unsigned int i, ix, j1, j2, jx; + struct ospf_proto *p = (struct ospf_proto *) P; + int ospf2 = ospf_is_v2(p); + uint i, ix, j1, jx; u32 last_area = 0xFFFFFFFF; - if (p->proto_state != PS_UP) + if (p->p.proto_state != PS_UP) { - cli_msg(-1016, "%s: is not up", p->name); + cli_msg(-1016, "%s: is not up", p->p.name); cli_msg(0, ""); return; } @@ -1291,51 +1158,51 @@ ospf_sh_state(struct proto *p, int verbose, int reachable) /* We store interesting area-scoped LSAs in array hea and global-scoped (LSA_T_EXT) LSAs in array hex */ - int num = po->gr->hash_entries; + int num = p->gr->hash_entries; struct top_hash_entry *hea[num]; struct top_hash_entry *hex[verbose ? num : 0]; struct top_hash_entry *he; + struct top_hash_entry *cnode = NULL; - j1 = j2 = jx = 0; - WALK_SLIST(he, po->lsal) + j1 = jx = 0; + WALK_SLIST(he, p->lsal) { int accept; - switch (he->lsa.type) + if (he->lsa.age == LSA_MAXAGE) + continue; + + switch (he->lsa_type) + { + case LSA_T_RT: + case LSA_T_NET: + accept = 1; + break; + + case LSA_T_SUM_NET: + case LSA_T_SUM_RT: + case LSA_T_NSSA: + case LSA_T_PREFIX: + accept = verbose; + break; + + case LSA_T_EXT: + if (verbose) { - case LSA_T_RT: - case LSA_T_NET: - accept = 1; - break; - - case LSA_T_SUM_NET: - case LSA_T_SUM_RT: - case LSA_T_NSSA: -#ifdef OSPFv3 - case LSA_T_PREFIX: -#endif - accept = verbose; - break; - - case LSA_T_EXT: - if (verbose) - { - he->domain = 1; /* Abuse domain field to mark the LSA */ - hex[jx++] = he; - } - default: - accept = 0; + he->domain = 1; /* Abuse domain field to mark the LSA */ + hex[jx++] = he; } + default: + accept = 0; + } if (accept) hea[j1++] = he; - else - j2++; } - if ((j1 + j2) != num) - die("Fatal mismatch"); + ASSERT(j1 <= num && jx <= num); + lsa_compare_ospf3 = !ospf2; qsort(hea, j1, sizeof(struct top_hash_entry *), lsa_compare_for_state); qsort(hex, jx, sizeof(struct top_hash_entry *), ext_compare_for_state); @@ -1366,10 +1233,10 @@ ospf_sh_state(struct proto *p, int verbose, int reachable) /* If there is no opened node, we open the LSA (if appropriate) or skip to the next one */ if (!cnode) { - if (((he->lsa.type == LSA_T_RT) || (he->lsa.type == LSA_T_NET)) + if (((he->lsa_type == LSA_T_RT) || (he->lsa_type == LSA_T_NET)) && ((he->color == INSPF) || !reachable)) { - cnode = &(he->lsa); + cnode = he; if (he->domain != last_area) { @@ -1383,51 +1250,50 @@ ospf_sh_state(struct proto *p, int verbose, int reachable) continue; } - ASSERT(cnode && (he->domain == last_area) && (he->lsa.rt == cnode->rt)); + ASSERT(cnode && (he->domain == last_area) && (he->lsa.rt == cnode->lsa.rt)); - switch (he->lsa.type) + switch (he->lsa_type) { - case LSA_T_RT: - show_lsa_router(po, he, he->lsa.id == cnode->id, verbose); - break; + case LSA_T_RT: + if (he->lsa.id == cnode->lsa.id) + show_lsa_router(p, he, verbose); + break; - case LSA_T_NET: - show_lsa_network(he); - break; + case LSA_T_NET: + show_lsa_network(he, ospf2); + break; - case LSA_T_SUM_NET: - if (cnode->type == LSA_T_RT) - show_lsa_sum_net(he); - break; + case LSA_T_SUM_NET: + if (cnode->lsa_type == LSA_T_RT) + show_lsa_sum_net(he, ospf2); + break; - case LSA_T_SUM_RT: - if (cnode->type == LSA_T_RT) - show_lsa_sum_rt(he); - break; + case LSA_T_SUM_RT: + if (cnode->lsa_type == LSA_T_RT) + show_lsa_sum_rt(he, ospf2); + break; -#ifdef OSPFv3 - case LSA_T_PREFIX: - show_lsa_prefix(he, cnode); - break; -#endif + case LSA_T_EXT: + case LSA_T_NSSA: + show_lsa_external(he, ospf2); + break; - case LSA_T_EXT: - case LSA_T_NSSA: - show_lsa_external(he); - break; + case LSA_T_PREFIX: + show_lsa_prefix(he, cnode); + break; } /* In these cases, we close the current node */ if ((i+1 == j1) || (hea[i+1]->domain != last_area) - || (hea[i+1]->lsa.rt != cnode->rt) - || (hea[i+1]->lsa.type == LSA_T_NET)) + || (hea[i+1]->lsa.rt != cnode->lsa.rt) + || (hea[i+1]->lsa_type == LSA_T_NET)) { - while ((ix < jx) && (hex[ix]->lsa.rt < cnode->rt)) + while ((ix < jx) && (hex[ix]->lsa.rt < cnode->lsa.rt)) ix++; - while ((ix < jx) && (hex[ix]->lsa.rt == cnode->rt)) - show_lsa_external(hex[ix++]); + while ((ix < jx) && (hex[ix]->lsa.rt == cnode->lsa.rt)) + show_lsa_external(hex[ix++], ospf2); cnode = NULL; } @@ -1461,7 +1327,7 @@ ospf_sh_state(struct proto *p, int verbose, int reachable) last_rt = he->lsa.rt; } - show_lsa_external(he); + show_lsa_external(he, ospf2); } } @@ -1476,8 +1342,8 @@ lsa_compare_for_lsadb(const void *p1, const void *p2) struct top_hash_entry * he2 = * (struct top_hash_entry **) p2; struct ospf_lsa_header *lsa1 = &(he1->lsa); struct ospf_lsa_header *lsa2 = &(he2->lsa); - int sc1 = LSA_SCOPE(lsa1); - int sc2 = LSA_SCOPE(lsa2); + int sc1 = LSA_SCOPE(he1->lsa_type); + int sc2 = LSA_SCOPE(he2->lsa_type); if (sc1 != sc2) return sc2 - sc1; @@ -1491,8 +1357,8 @@ lsa_compare_for_lsadb(const void *p1, const void *p2) if (lsa1->id != lsa2->id) return lsa1->id - lsa2->id; - if (lsa1->type != lsa2->type) - return lsa1->type - lsa2->type; + if (he1->lsa_type != he2->lsa_type) + return he1->lsa_type - he2->lsa_type; return lsa1->sn - lsa2->sn; } @@ -1500,48 +1366,50 @@ lsa_compare_for_lsadb(const void *p1, const void *p2) void ospf_sh_lsadb(struct lsadb_show_data *ld) { - struct proto *p = proto_get_named(ld->name, &proto_ospf); - struct proto_ospf *po = (struct proto_ospf *) p; - int num = po->gr->hash_entries; + struct ospf_proto *p = (struct ospf_proto *) proto_get_named(ld->name, &proto_ospf); + int num = p->gr->hash_entries; unsigned int i, j; int last_dscope = -1; u32 last_domain = 0; + u16 type_mask = ospf_is_v2(p) ? 0x00ff : 0xffff; /* see lsa_etype() */ - if (p->proto_state != PS_UP) + if (p->p.proto_state != PS_UP) { - cli_msg(-1017, "%s: is not up", p->name); + cli_msg(-1017, "%s: is not up", p->p.name); cli_msg(0, ""); return; } if (ld->router == SH_ROUTER_SELF) - ld->router = po->router_id; + ld->router = p->router_id; struct top_hash_entry *hea[num]; struct top_hash_entry *he; j = 0; - WALK_SLIST(he, po->lsal) - hea[j++] = he; + WALK_SLIST(he, p->lsal) + if (he->lsa_body) + hea[j++] = he; - if (j != num) - die("Fatal mismatch"); + ASSERT(j <= num); qsort(hea, j, sizeof(struct top_hash_entry *), lsa_compare_for_lsadb); for (i = 0; i < j; i++) { struct ospf_lsa_header *lsa = &(hea[i]->lsa); - int dscope = LSA_SCOPE(lsa); - + u16 lsa_type = lsa->type_raw & type_mask; + u16 dscope = LSA_SCOPE(hea[i]->lsa_type); + + /* Hack: 1 is used for LSA_SCOPE_LINK, fixed by & 0xf000 */ if (ld->scope && (dscope != (ld->scope & 0xf000))) continue; if ((ld->scope == LSA_SCOPE_AREA) && (hea[i]->domain != ld->area)) continue; - /* Ignore high nibble */ - if (ld->type && ((lsa->type & 0x0fff) != (ld->type & 0x0fff))) + /* For user convenience ignore high nibble */ + if (ld->type && ((lsa_type & 0x0fff) != (ld->type & 0x0fff))) continue; if (ld->lsid && (lsa->id != ld->lsid)) @@ -1555,20 +1423,20 @@ ospf_sh_lsadb(struct lsadb_show_data *ld) cli_msg(-1017, ""); switch (dscope) { - case LSA_SCOPE_AS: - cli_msg(-1017, "Global"); - break; - case LSA_SCOPE_AREA: - cli_msg(-1017, "Area %R", hea[i]->domain); - break; -#ifdef OSPFv3 - case LSA_SCOPE_LINK: - { - struct iface *ifa = if_find_by_index(hea[i]->domain); - cli_msg(-1017, "Link %s", (ifa != NULL) ? ifa->name : "?"); - } - break; -#endif + case LSA_SCOPE_AS: + cli_msg(-1017, "Global"); + break; + + case LSA_SCOPE_AREA: + cli_msg(-1017, "Area %R", hea[i]->domain); + break; + + case LSA_SCOPE_LINK: + { + struct iface *ifa = if_find_by_index(hea[i]->domain); + cli_msg(-1017, "Link %s", (ifa != NULL) ? ifa->name : "?"); + } + break; } cli_msg(-1017, ""); cli_msg(-1017," Type LS ID Router Age Sequence Checksum"); @@ -1577,26 +1445,25 @@ ospf_sh_lsadb(struct lsadb_show_data *ld) last_domain = hea[i]->domain; } - cli_msg(-1017," %04x %-15R %-15R %5u %08x %04x", - lsa->type, lsa->id, lsa->rt, lsa->age, lsa->sn, lsa->checksum); + lsa_type, lsa->id, lsa->rt, lsa->age, lsa->sn, lsa->checksum); } cli_msg(0, ""); } struct protocol proto_ospf = { - name: "OSPF", - template: "ospf%d", - attr_class: EAP_OSPF, - preference: DEF_PREF_OSPF, - init: ospf_init, - dump: ospf_dump, - start: ospf_start, - shutdown: ospf_shutdown, - reconfigure: ospf_reconfigure, - get_status: ospf_get_status, - get_attr: ospf_get_attr, - get_route_info: ospf_get_route_info - // show_proto_info: ospf_sh + .name = "OSPF", + .template = "ospf%d", + .attr_class = EAP_OSPF, + .preference = DEF_PREF_OSPF, + .init = ospf_init, + .dump = ospf_dump, + .start = ospf_start, + .shutdown = ospf_shutdown, + .reconfigure = ospf_reconfigure, + .get_status = ospf_get_status, + .get_attr = ospf_get_attr, + .get_route_info = ospf_get_route_info }; + diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index e705b88b..34c26b47 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 1999--2005 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -9,23 +11,6 @@ #ifndef _BIRD_OSPF_H_ #define _BIRD_OSPF_H_ -#define MAXNETS 10 -#define OSPF_MIN_PKT_SIZE 256 -#define OSPF_MAX_PKT_SIZE 65535 - -#ifdef LOCAL_DEBUG -#define OSPF_FORCE_DEBUG 1 -#else -#define OSPF_FORCE_DEBUG 0 -#endif -#define OSPF_TRACE(flags, msg, args...) do { if ((p->debug & flags) || OSPF_FORCE_DEBUG) \ - log(L_TRACE "%s: " msg, p->name , ## args ); } while(0) - -#define OSPF_PACKET(dumpfn, buffer, msg, args...) \ -do { if ((p->debug & D_PACKETS) || OSPF_FORCE_DEBUG) \ -{ log(L_TRACE "%s: " msg, p->name, ## args ); dumpfn(p, buffer); } } while(0) - - #include "nest/bird.h" #include "lib/checksum.h" @@ -44,33 +29,72 @@ do { if ((p->debug & D_PACKETS) || OSPF_FORCE_DEBUG) \ #include "conf/conf.h" #include "lib/string.h" -#define OSPF_PROTO 89 -#ifndef IPV6 -#define OSPFv2 1 -#define OSPF_VERSION 2 -#define OSPF_VLINK_MTU 576 /* RFC 2328 A.1 */ -#define AllSPFRouters ipa_from_u32(0xe0000005) /* 224.0.0.5 */ -#define AllDRouters ipa_from_u32(0xe0000006) /* 224.0.0.6 */ +#ifdef LOCAL_DEBUG +#define OSPF_FORCE_DEBUG 1 #else -#define OSPFv3 1 -#define OSPF_VERSION 3 -#define OSPF_VLINK_MTU 1280 /* RFC 5340 A.1 */ -#define AllSPFRouters _MI(0xFF020000, 0, 0, 5) /* FF02::5 */ -#define AllDRouters _MI(0xFF020000, 0, 0, 6) /* FF02::6 */ +#define OSPF_FORCE_DEBUG 0 #endif +#define IP4_MIN_MTU 576 +#define IP6_MIN_MTU 1280 + +#define IP4_OSPF_ALL_ROUTERS ipa_build4(224, 0, 0, 5) +#define IP4_OSPF_DES_ROUTERS ipa_build4(224, 0, 0, 6) + +#define IP6_OSPF_ALL_ROUTERS ipa_build6(0xFF020000, 0, 0, 5) +#define IP6_OSPF_DES_ROUTERS ipa_build6(0xFF020000, 0, 0, 6) + +#ifdef IPV6 +#define ip4_addr u32 +#define ip6_addr ip_addr +#define _MI6(x1,x2,x3,x4) _MI(x1, x2, x3, x4) +#define ipa_is_link_local(x) ipa_has_link_scope(x) +#define ipa_from_u32(x) _MI6(0,0,0xffff,x) +#define ipa_to_u32(x) _I3(x) +#define ipa_build4(a,b,c,d) IPA_NONE +#define ipa_build6(a,b,c,d) _MI6(a,b,c,d) +#define OSPF_IS_V2 0 +#else +#define ip4_addr u32 +#define ip6_addr ip_addr +#define _I0(X) 0 +#define _I1(X) 0 +#define _I2(X) 0 +#define _I3(X) 0 +#define _MI6(x1,x2,x3,x4) IPA_NONE +#define ipa_is_link_local(x) 0 +#define ipa_build4(a,b,c,d) _MI(((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) +#define ipa_build6(a,b,c,d) IPA_NONE +#define OSPF_IS_V2 1 +#endif + +// FIXME: MAX_PREFIX_LENGTH + +#define OSPF_TRACE(flags, msg, args...) \ +do { if ((p->p.debug & flags) || OSPF_FORCE_DEBUG) \ + log(L_TRACE "%s: " msg, p->p.name , ## args ); } while(0) + +#define OSPF_PACKET(dumpfn, buffer, msg, args...) \ +do { if ((p->p.debug & D_PACKETS) || OSPF_FORCE_DEBUG) \ + { log(L_TRACE "%s: " msg, p->p.name, ## args ); dumpfn(p, buffer); } } while(0) + + +#define OSPF_PROTO 89 + #define LSREFRESHTIME 1800 /* 30 minutes */ #define MINLSINTERVAL 5 #define MINLSARRIVAL 1 #define LSINFINITY 0xffffff -#define DEFAULT_OSPFTICK 1 -#define DEFAULT_RFC1583 0 /* compatibility with rfc1583 */ -#define DEFAULT_STUB_COST 1000 -#define DEFAULT_ECMP_LIMIT 16 -#define DEFAULT_TRANSINT 40 +#define OSPF_DEFAULT_TICK 1 +#define OSPF_DEFAULT_STUB_COST 1000 +#define OSPF_DEFAULT_ECMP_LIMIT 16 +#define OSPF_DEFAULT_TRANSINT 40 + +#define OSPF_MIN_PKT_SIZE 256 +#define OSPF_MAX_PKT_SIZE 65535 #define OSPF_VLINK_ID_OFFSET 0x80000000 @@ -79,10 +103,12 @@ struct ospf_config { struct proto_config c; unsigned tick; + byte ospf2; byte rfc1583; byte stub_router; byte merge_external; byte abr; + byte asbr; int ecmp; list area_list; /* list of struct ospf_area_config */ list vlink_list; /* list of struct ospf_iface_patt */ @@ -100,32 +126,34 @@ struct area_net_config { node n; struct prefix px; - int hidden; u32 tag; + u8 hidden; }; struct area_net { struct fib_node fn; - int hidden; - int active; - u32 metric; + u32 metric; /* With possible LSA_EXT3_EBIT for NSSA area nets */ u32 tag; + u8 hidden; + u8 active; }; struct ospf_stubnet_config { node n; struct prefix px; - int hidden, summary; u32 cost; + u8 hidden; + u8 summary; }; struct ospf_area_config { node n; u32 areaid; - u32 default_cost; /* Cost of default route for stub areas */ + u32 default_cost; /* Cost of default route for stub areas + (With possible LSA_EXT3_EBIT for NSSA areas) */ u8 type; /* Area type (standard, stub, NSSA), represented by option flags (OPT_E, OPT_N) */ u8 summary; /* Import summaries to this stub/NSSA area, valid for ABR */ @@ -139,38 +167,57 @@ struct ospf_area_config }; -/* Option flags */ +/* Generic option flags */ +#define OPT_V6 0x01 /* OSPFv3, LSA relevant for IPv6 routing calculation */ +#define OPT_E 0x02 /* Related to AS-external LSAs */ +#define OPT_MC 0x04 /* Related to MOSPF, not used and obsolete */ +#define OPT_N 0x08 /* Related to NSSA */ +#define OPT_P 0x08 /* OSPFv2, flags P and N share position, see NSSA RFC */ +#define OPT_EA 0x10 /* OSPFv2, external attributes, not used and obsolete */ +#define OPT_R 0x10 /* OSPFv3, originator is active router */ +#define OPT_DC 0x20 /* Related to demand circuits, not used */ -#define OPT_E 0x02 -#define OPT_N 0x08 -#define OPT_DC 0x20 - -#ifdef OSPFv2 -#define OPT_P 0x08 /* flags P and N share position, see NSSA RFC */ -#define OPT_EA 0x10 - -/* VEB flags are are stored independently in 'u16 options' */ -#define OPT_RT_B (0x01 << 8) -#define OPT_RT_E (0x02 << 8) -#define OPT_RT_V (0x04 << 8) -#define OPT_RT_NT (0x10 << 8) -#endif - -#ifdef OSPFv3 -#define OPT_V6 0x01 -#define OPT_R 0x10 - -/* VEB flags are are stored together with options in 'u32 options' */ +/* Router-LSA VEB flags are are stored together with links (OSPFv2) or options (OSPFv3) */ #define OPT_RT_B (0x01 << 24) #define OPT_RT_E (0x02 << 24) #define OPT_RT_V (0x04 << 24) #define OPT_RT_NT (0x10 << 24) +/* Prefix flags, specific for OSPFv3 */ #define OPT_PX_NU 0x01 #define OPT_PX_LA 0x02 #define OPT_PX_P 0x08 #define OPT_PX_DN 0x10 -#endif + + +/* OSPF interface types */ +#define OSPF_IT_BCAST 0 +#define OSPF_IT_NBMA 1 +#define OSPF_IT_PTP 2 +#define OSPF_IT_PTMP 3 +#define OSPF_IT_VLINK 4 +#define OSPF_IT_UNDEF 5 + +/* OSPF interface states */ +#define OSPF_IS_DOWN 0 /* Not active */ +#define OSPF_IS_LOOP 1 /* Iface with no link */ +#define OSPF_IS_WAITING 2 /* Waiting for Wait timer */ +#define OSPF_IS_PTP 3 /* PTP operational */ +#define OSPF_IS_DROTHER 4 /* I'm on BCAST or NBMA and I'm not DR */ +#define OSPF_IS_BACKUP 5 /* I'm BDR */ +#define OSPF_IS_DR 6 /* I'm DR */ + + +/* Default values for interface parameters */ +#define COST_D 10 +#define RXMTINT_D 5 +#define INFTRANSDELAY_D 1 +#define PRIORITY_D 1 +#define HELLOINT_D 10 +#define POLLINT_D 20 +#define DEADC_D 4 +#define WAIT_DMH 4 + /* Value of Wait timer - not found it in RFC * - using 4*HELLO */ struct ospf_iface @@ -199,82 +246,50 @@ struct ospf_iface transmit a Link State Update Packet over this interface. LSAs contained in the update */ u16 helloint; /* number of seconds between hello sending */ - -#ifdef OSPFv2 list *passwords; - u16 autype; u32 csn; /* Last used crypt seq number */ bird_clock_t csn_use; /* Last time when packet with that CSN was sent */ -#endif - - ip_addr all_routers; /* */ - ip_addr drip; /* Designated router */ - ip_addr bdrip; /* Backup DR */ - u32 drid; - u32 bdrid; + ip_addr all_routers; /* Multicast (or broadcast) address for all routers */ + ip_addr des_routers; /* Multicast (or NULL) address for designated routers */ + ip_addr drip; /* Designated router IP */ + ip_addr bdrip; /* Backup DR IP */ + u32 drid; /* DR Router ID */ + u32 bdrid; /* BDR Router ID */ s16 rt_pos_beg; /* Position of iface in Router-LSA, begin, inclusive */ s16 rt_pos_end; /* Position of iface in Router-LSA, end, exclusive */ - -#ifdef OSPFv3 s16 px_pos_beg; /* Position of iface in Rt Prefix-LSA, begin, inclusive */ s16 px_pos_end; /* Position of iface in Rt Prefix-LSA, end, exclusive */ - u32 dr_iface_id; /* if drid is valid, this is iface_id of DR (for connecting network) */ u8 instance_id; /* Used to differentiate between more OSPF instances on one interface */ -#endif - - u8 type; /* OSPF view of type */ -#define OSPF_IT_BCAST 0 -#define OSPF_IT_NBMA 1 -#define OSPF_IT_PTP 2 -#define OSPF_IT_PTMP 3 -#define OSPF_IT_VLINK 4 -#define OSPF_IT_UNDEF 5 + u8 autype; /* Authentication type (OSPF_AUTH_*) */ + u8 type; /* OSPF view of type (OSPF_IT_*) */ u8 strictnbma; /* Can I talk with unknown neighbors? */ u8 stub; /* Inactive interface */ - u8 state; /* Interface state machine */ -#define OSPF_IS_DOWN 0 /* Not working */ -#define OSPF_IS_LOOP 1 /* Iface with no link */ -#define OSPF_IS_WAITING 2 /* Waiting for Wait timer */ -#define OSPF_IS_PTP 3 /* PTP operational */ -#define OSPF_IS_DROTHER 4 /* I'm on BCAST or NBMA and I'm not DR */ -#define OSPF_IS_BACKUP 5 /* I'm BDR */ -#define OSPF_IS_DR 6 /* I'm DR */ + u8 state; /* Interface state machine (OSPF_IS_*) */ timer *wait_timer; /* WAIT timer */ timer *hello_timer; /* HELLOINT timer */ timer *poll_timer; /* Poll Interval - for NBMA */ -/* Default values for interface parameters */ -#define COST_D 10 -#define RXMTINT_D 5 -#define INFTRANSDELAY_D 1 -#define PRIORITY_D 1 -#define HELLOINT_D 10 -#define POLLINT_D 20 -#define DEADC_D 4 -#define WAIT_DMH 4 - /* Value of Wait timer - not found it in RFC * - using 4*HELLO */ - struct top_hash_entry *net_lsa; /* Originated network LSA */ - int orignet; /* Schedule network LSA origination */ -#ifdef OSPFv3 - int origlink; /* Schedule link LSA origination */ struct top_hash_entry *link_lsa; /* Originated link LSA */ + struct top_hash_entry *net_lsa; /* Originated network LSA */ struct top_hash_entry *pxn_lsa; /* Originated prefix LSA */ -#endif - int fadj; /* Number of full adjacent neigh */ + u8 update_link_lsa; + u8 update_net_lsa; + int fadj; /* Number of fully adjacent neighbors */ list nbma_list; u8 priority; /* A router priority for DR election */ u8 ioprob; #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_dr; /* Socket is a member of DRouters group */ - u8 marked; /* Used in OSPF reconfigure */ + u8 sk_dr; /* Socket is a member of designated routers group */ + u8 marked; /* Used in OSPF reconfigure, 2 for force restart */ u16 rxbuf; /* Buffer size */ u16 tx_length; /* Soft TX packet length limit, usually MTU */ u8 check_link; /* Whether iface link change is used */ u8 ecmp_weight; /* Weight used for ECMP */ + u8 link_lsa_suppression; /* Suppression of Link-LSA origination */ u8 ptp_netmask; /* Send real netmask for P2P */ u8 check_ttl; /* Check incoming packets for TTL 255 */ u8 bfd; /* Use BFD on iface */ @@ -296,42 +311,20 @@ union ospf_auth /* Packet types */ -#define HELLO_P 1 /* Hello */ -#define DBDES_P 2 /* Database description */ -#define LSREQ_P 3 /* Link state request */ -#define LSUPD_P 4 /* Link state update */ -#define LSACK_P 5 /* Link state acknowledgement */ +#define HELLO_P 1 /* Hello */ +#define DBDES_P 2 /* Database description */ +#define LSREQ_P 3 /* Link state request */ +#define LSUPD_P 4 /* Link state update */ +#define LSACK_P 5 /* Link state acknowledgement */ /* Area IDs */ -#define BACKBONE 0 +#define BACKBONE 0 +#define DBDES_I 4 /* Init bit */ +#define DBDES_M 2 /* More bit */ +#define DBDES_MS 1 /* Master/Slave bit */ +#define DBDES_IMMS (DBDES_I | DBDES_M | DBDES_MS) -struct immsb -{ -#ifdef CPU_BIG_ENDIAN - u8 padding:5; - u8 i:1; - u8 m:1; - u8 ms:1; -#else - u8 ms:1; - u8 m:1; - u8 i:1; - u8 padding:5; -#endif -}; - -union imms -{ - u8 byte; - struct immsb bit; -}; -#define DBDES_MS 1 -#define DBDES_M 2 -#define DBDES_I 4 - - -#ifdef OSPFv2 struct ospf_packet { @@ -341,57 +334,11 @@ struct ospf_packet u32 routerid; u32 areaid; u16 checksum; - u16 autype; - union ospf_auth u; + u8 instance_id; /* See RFC 6549 */ + u8 autype; /* Undefined for OSPFv3 */ }; -#else /* OSPFv3 packet descriptions */ - -struct ospf_packet -{ - u8 version; - u8 type; - u16 length; - u32 routerid; - u32 areaid; - u16 checksum; - u8 instance_id; - u8 zero; -}; - - -#endif - - - - -struct ospf_lsa_header -{ - u16 age; /* LS Age */ -#define LSA_MAXAGE 3600 /* 1 hour */ -#define LSA_CHECKAGE 300 /* 5 minutes */ -#define LSA_MAXAGEDIFF 900 /* 15 minutes */ - -#ifdef OSPFv2 - u8 options; - u8 type; - -#define LSA_T_RT 1 -#define LSA_T_NET 2 -#define LSA_T_SUM_NET 3 -#define LSA_T_SUM_RT 4 -#define LSA_T_EXT 5 -#define LSA_T_NSSA 7 - -#define LSA_SCOPE_AREA 0x2000 -#define LSA_SCOPE_AS 0x4000 - -#define LSA_SCOPE(lsa) (((lsa)->type == LSA_T_EXT) ? LSA_SCOPE_AS : LSA_SCOPE_AREA) - -#else /* OSPFv3 */ - u16 type; - #define LSA_T_RT 0x2001 #define LSA_T_NET 0x2002 #define LSA_T_SUM_NET 0x2003 @@ -401,6 +348,8 @@ struct ospf_lsa_header #define LSA_T_LINK 0x0008 #define LSA_T_PREFIX 0x2009 +#define LSA_T_V2_MASK 0x00ff + #define LSA_UBIT 0x8000 #define LSA_SCOPE_LINK 0x0000 @@ -408,91 +357,93 @@ struct ospf_lsa_header #define LSA_SCOPE_AS 0x4000 #define LSA_SCOPE_RES 0x6000 #define LSA_SCOPE_MASK 0x6000 +#define LSA_SCOPE(type) ((type) & LSA_SCOPE_MASK) -#define LSA_SCOPE(lsa) ((lsa)->type & LSA_SCOPE_MASK) -#endif + +#define LSA_MAXAGE 3600 /* 1 hour */ +#define LSA_CHECKAGE 300 /* 5 minutes */ +#define LSA_MAXAGEDIFF 900 /* 15 minutes */ + +#define LSA_ZEROSEQNO ((s32) 0x80000000) +#define LSA_INITSEQNO ((s32) 0x80000001) +#define LSA_MAXSEQNO ((s32) 0x7fffffff) + +#define LSA_METRIC_MASK 0x00FFFFFF +#define LSA_OPTIONS_MASK 0x00FFFFFF + + +#define LSART_PTP 1 +#define LSART_NET 2 +#define LSART_STUB 3 +#define LSART_VLNK 4 + +#define LSA_RT2_LINKS 0x0000FFFF + +#define LSA_SUM2_TOS 0xFF000000 + +#define LSA_EXT2_TOS 0x7F000000 +#define LSA_EXT2_EBIT 0x80000000 + +#define LSA_EXT3_EBIT 0x4000000 +#define LSA_EXT3_FBIT 0x2000000 +#define LSA_EXT3_TBIT 0x1000000 + + +struct ospf_lsa_header +{ + u16 age; /* LS Age */ + u16 type_raw; /* Type, mixed with options on OSPFv2 */ u32 id; u32 rt; /* Advertising router */ s32 sn; /* LS Sequence number */ -#define LSA_INITSEQNO ((s32) 0x80000001) -#define LSA_MAXSEQNO ((s32) 0x7fffffff) u16 checksum; u16 length; }; -#define LSART_PTP 1 -#define LSART_NET 2 -#define LSART_STUB 3 -#define LSART_VLNK 4 +/* In OSPFv2, options are embedded in higher half of type_raw */ +static inline u8 lsa_get_options(struct ospf_lsa_header *lsa) +{ return lsa->type_raw >> 8; } +static inline void lsa_set_options(struct ospf_lsa_header *lsa, u16 options) +{ lsa->type_raw = (lsa->type_raw & 0xff) | (options << 8); } -#ifdef OSPFv2 struct ospf_lsa_rt { -#ifdef CPU_BIG_ENDIAN - u16 options; /* VEB flags only */ - u16 links; -#else - u16 links; - u16 options; /* VEB flags only */ -#endif + u32 options; /* VEB flags, mixed with link count for OSPFv2 and options for OSPFv3 */ }; -struct ospf_lsa_rt_link +struct ospf_lsa_rt2_link { u32 id; u32 data; #ifdef CPU_BIG_ENDIAN u8 type; + u8 no_tos; + u16 metric; +#else + u16 metric; + u8 no_tos; + u8 type; +#endif +}; + +struct ospf_lsa_rt2_tos +{ +#ifdef CPU_BIG_ENDIAN + u8 tos; u8 padding; u16 metric; #else u16 metric; u8 padding; - u8 type; + u8 tos; #endif }; -struct ospf_lsa_net -{ - ip_addr netmask; - u32 routers[]; -}; - -struct ospf_lsa_sum -{ - ip_addr netmask; - u32 metric; -}; - -struct ospf_lsa_ext -{ - ip_addr netmask; - u32 metric; - ip_addr fwaddr; - u32 tag; -}; - -#define LSA_SUM_TOS 0xFF000000 -#define LSA_EXT_TOS 0x7F000000 -#define LSA_EXT_EBIT 0x80000000 - -/* Endianity swap for lsa->type */ -#define ntoht(x) x -#define htont(x) x - - -#else /* OSPFv3 */ - -struct ospf_lsa_rt -{ - u32 options; -}; - -struct ospf_lsa_rt_link +struct ospf_lsa_rt3_link { #ifdef CPU_BIG_ENDIAN u8 type; @@ -508,35 +459,58 @@ struct ospf_lsa_rt_link u32 id; /* Neighbor router ID */ }; + struct ospf_lsa_net { - u32 options; + u32 optx; /* Netmask for OSPFv2, options for OSPFv3 */ u32 routers[]; }; -struct ospf_lsa_sum_net +struct ospf_lsa_sum2 +{ + u32 netmask; + u32 metric; +}; + +struct ospf_lsa_sum3_net { u32 metric; u32 prefix[]; }; -struct ospf_lsa_sum_rt +struct ospf_lsa_sum3_rt { u32 options; u32 metric; u32 drid; }; -struct ospf_lsa_ext +struct ospf_lsa_ext2 +{ + u32 netmask; + u32 metric; + u32 fwaddr; + u32 tag; +}; + +struct ospf_lsa_ext3 { u32 metric; u32 rest[]; }; +struct ospf_lsa_ext_local +{ + ip_addr ip, fwaddr; + int pxlen; + u32 metric, ebit, fbit, tag, propagate; + u8 pxopts; +}; + struct ospf_lsa_link { u32 options; - ip_addr lladdr; + ip6_addr lladdr; u32 pxcount; u32 rest[]; }; @@ -555,25 +529,6 @@ struct ospf_lsa_prefix u32 rest[]; }; -#define LSA_EXT_EBIT 0x4000000 -#define LSA_EXT_FBIT 0x2000000 -#define LSA_EXT_TBIT 0x1000000 - -/* Endianity swap for lsa->type */ -#define ntoht(x) ntohs(x) -#define htont(x) htons(x) - -#endif - -#define METRIC_MASK 0x00FFFFFF -#define OPTIONS_MASK 0x00FFFFFF - -static inline unsigned -lsa_rt_count(struct ospf_lsa_header *lsa) -{ - return (lsa->length - sizeof(struct ospf_lsa_header) - sizeof(struct ospf_lsa_rt)) - / sizeof(struct ospf_lsa_rt_link); -} static inline unsigned lsa_net_count(struct ospf_lsa_header *lsa) @@ -582,8 +537,12 @@ lsa_net_count(struct ospf_lsa_header *lsa) / sizeof(u32); } +/* In ospf_area->rtr we store paths to routers, but we use RID (and not IP address) + as index, so we need to encapsulate RID to IP address */ + +#define ipa_from_rid(x) ipa_from_u32(x) +#define ipa_to_rid(x) ipa_to_u32(x) -#ifdef OSPFv3 #define IPV6_PREFIX_SPACE(x) ((((x) + 63) / 32) * 4) #define IPV6_PREFIX_WORDS(x) (((x) + 63) / 32) @@ -645,21 +604,12 @@ put_ipv6_addr(u32 *buf, ip_addr addr) return buf + 4; } -#endif - - struct ospf_lsreq_header { u32 type; u32 id; - u32 rt; /* Advertising router */ -}; - -struct l_lsr_head -{ - node n; - struct ospf_lsreq_header lsh; + u32 rt; }; @@ -678,31 +628,38 @@ struct ospf_neighbor #define NEIGHBOR_LOADING 6 #define NEIGHBOR_FULL 7 timer *inactim; /* Inactivity timer */ - union imms imms; /* I, M, Master/slave received */ + u8 imms; /* I, M, Master/slave received */ + u8 myimms; /* I, M Master/slave */ u32 dds; /* DD Sequence number being sent */ u32 ddr; /* last Dat Des packet received */ - union imms myimms; /* I, M Master/slave */ + u32 rid; /* Router ID */ ip_addr ip; /* IP of it's interface */ u8 priority; /* Priority */ u8 adj; /* built adjacency? */ u32 options; /* Options received */ - /* dr and bdr store IP address in OSPFv2 and router ID in OSPFv3, - we use the same type to simplify handling */ + /* Entries dr and bdr store IP addresses in OSPFv2 and router IDs in + OSPFv3, we use the same type to simplify handling */ u32 dr; /* Neigbour's idea of DR */ u32 bdr; /* Neigbour's idea of BDR */ - -#ifdef OSPFv3 u32 iface_id; /* ID of Neighbour's iface connected to common network */ -#endif - siterator dbsi; /* Database summary list iterator */ - slist lsrql; /* Link state request */ - struct top_graph *lsrqh; /* LSA graph */ - siterator lsrqi; - slist lsrtl; /* Link state retransmission list */ - siterator lsrti; + /* Database summary list iterator, controls initial dbdes exchange. + * Advances in the LSA list as dbdes packets are sent. + */ + siterator dbsi; /* iterator of po->lsal */ + + /* Link state request list, controls initial LSA exchange. + * Entries added when received in dbdes packets, removed as sent in lsreq packets. + */ + slist lsrql; /* slist of struct top_hash_entry from n->lsrqh */ + struct top_graph *lsrqh; + + /* Link state retransmission list, controls LSA retransmission during flood. + * Entries added as sent in lsupd packets, removed when received in lsack packets. + */ + slist lsrtl; /* slist of struct top_hash_entry from n->lsrth */ struct top_graph *lsrth; timer *rxmt_timer; /* RXMT timer */ list ackl[2]; @@ -750,12 +707,12 @@ struct ospf_area struct fib net_fib; /* Networks to advertise or not */ struct fib enet_fib; /* External networks for NSSAs */ u32 options; /* Optional features */ - byte origrt; /* Rt lsa origination scheduled? */ - byte trcap; /* Transit capability? */ - byte marked; /* Used in OSPF reconfigure */ - byte translate; /* Translator state (TRANS_*), for NSSA ABR */ + u8 update_rt_lsa; /* Rt lsa origination scheduled? */ + u8 trcap; /* Transit capability? */ + u8 marked; /* Used in OSPF reconfigure */ + u8 translate; /* Translator state (TRANS_*), for NSSA ABR */ timer *translator_timer; /* For NSSA translator switch */ - struct proto_ospf *po; + struct ospf_proto *po; struct fib rtr; /* Routing tables for routers */ }; @@ -763,9 +720,9 @@ struct ospf_area #define TRANS_ON 1 #define TRANS_WAIT 2 /* Waiting before the end of translation */ -struct proto_ospf +struct ospf_proto { - struct proto proto; + struct proto p; timer *disp_timer; /* OSPF proto dispatcher */ unsigned tick; struct top_graph *gr; /* LSA graph */ @@ -775,11 +732,13 @@ struct proto_ospf list iface_list; /* Interfaces we really use */ list area_list; int areano; /* Number of area I belong to */ + int padj; /* Number of neighbors in Exchange or Loading state */ struct fib rtf; /* Routing table */ + byte ospf2; /* OSPF v2 or v3 */ byte rfc1583; /* RFC1583 compatibility */ byte stub_router; /* Do not forward transit traffic */ byte merge_external; /* Should i merge external routes? */ - byte ebit; /* Did I originate any ext lsa? */ + byte asbr; /* May i originate any ext/NSSA lsa? */ byte ecmp; /* Maximal number of nexthops in ECMP route, or 0 */ struct ospf_area *backbone; /* If exists */ void *lsab; /* LSA buffer used when originating router LSAs */ @@ -813,7 +772,8 @@ struct ospf_iface_patt u16 rx_buffer; #define OSPF_RXBUF_MINSIZE 256 /* Minimal allowed size */ - u16 autype; /* Not really used in OSPFv3 */ + u8 instance_id; + u8 autype; /* Not really used in OSPFv3 */ #define OSPF_AUTH_NONE 0 #define OSPF_AUTH_SIMPLE 1 #define OSPF_AUTH_CRYPT 2 @@ -821,51 +781,15 @@ struct ospf_iface_patt u8 strictnbma; u8 check_link; u8 ecmp_weight; + u8 link_lsa_suppression; u8 real_bcast; /* Not really used in OSPFv3 */ u8 ptp_netmask; /* bool + 2 for unspecified */ u8 ttl_security; /* bool + 2 for TX only */ u8 bfd; u8 bsd_secondary; - -#ifdef OSPFv2 list *passwords; -#endif - -#ifdef OSPFv3 - u8 instance_id; -#endif }; -int ospf_import_control(struct proto *p, rte **new, ea_list **attrs, - struct linpool *pool); -struct ea_list *ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool); -void ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs); -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); -static inline struct ospf_area *ospf_main_area(struct proto_ospf *po) -{ return (po->areano == 1) ? HEAD(po->area_list) : po->backbone; } - -static inline int oa_is_stub(struct ospf_area *oa) -{ return (oa->options & (OPT_E | OPT_N)) == 0; } -static inline int oa_is_ext(struct ospf_area *oa) -{ return oa->options & OPT_E; } -static inline int oa_is_nssa(struct ospf_area *oa) -{ return oa->options & OPT_N; } - - -#ifdef OSPFv3 -void schedule_link_lsa(struct ospf_iface *ifa); -#else -static inline void schedule_link_lsa(struct ospf_iface *ifa UNUSED) {} -#endif - -void ospf_sh_neigh(struct proto *p, char *iff); -void ospf_sh(struct proto *p); -void ospf_sh_iface(struct proto *p, char *iff); -void ospf_sh_state(struct proto *p, int verbose, int reachable); #define SH_ROUTER_SELF 0xffffffff @@ -878,24 +802,148 @@ struct lsadb_show_data { u32 router; /* Advertising router, 0 -> all */ }; -void ospf_sh_lsadb(struct lsadb_show_data *ld); - #define EA_OSPF_METRIC1 EA_CODE(EAP_OSPF, 0) #define EA_OSPF_METRIC2 EA_CODE(EAP_OSPF, 1) #define EA_OSPF_TAG EA_CODE(EAP_OSPF, 2) #define EA_OSPF_ROUTER_ID EA_CODE(EAP_OSPF, 3) + +/* ospf.c */ +void schedule_rtcalc(struct ospf_proto *p); + +static inline void ospf_notify_rt_lsa(struct ospf_area *oa) +{ oa->update_rt_lsa = 1; } + +static inline void ospf_notify_net_lsa(struct ospf_iface *ifa) +{ ifa->update_net_lsa = 1; } + +static inline void ospf_notify_link_lsa(struct ospf_iface *ifa) +{ ifa->update_link_lsa = 1; } + + +static inline int ospf_is_v2(struct ospf_proto *p) +{ return p->ospf2; } + +static inline int ospf_is_v3(struct ospf_proto *p) +{ return ! p->ospf2; } + +static inline int ospf_get_version(struct ospf_proto *p) +{ return ospf_is_v2(p) ? 2 : 3; } + +struct ospf_area *ospf_find_area(struct ospf_proto *p, u32 aid); + +static inline struct ospf_area *ospf_main_area(struct ospf_proto *p) +{ return (p->areano == 1) ? HEAD(p->area_list) : p->backbone; } + +static inline int oa_is_stub(struct ospf_area *oa) +{ return (oa->options & (OPT_E | OPT_N)) == 0; } + +static inline int oa_is_ext(struct ospf_area *oa) +{ return oa->options & OPT_E; } + +static inline int oa_is_nssa(struct ospf_area *oa) +{ return oa->options & OPT_N; } + +void ospf_sh_neigh(struct proto *P, char *iff); +void ospf_sh(struct proto *P); +void ospf_sh_iface(struct proto *P, char *iff); +void ospf_sh_state(struct proto *P, int verbose, int reachable); + +void ospf_sh_lsadb(struct lsadb_show_data *ld); + +/* iface.c */ +void ospf_iface_chstate(struct ospf_iface *ifa, u8 state); +void ospf_iface_sm(struct ospf_iface *ifa, int event); +struct ospf_iface *ospf_iface_find(struct ospf_proto *p, struct iface *what); +void ospf_if_notify(struct proto *P, uint flags, struct iface *iface); +void ospf_ifa_notify2(struct proto *P, uint flags, struct ifa *a); +void ospf_ifa_notify3(struct proto *P, uint 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_new_vlink(struct ospf_proto *p, struct ospf_iface_patt *ip); +void ospf_iface_remove(struct ospf_iface *ifa); +void ospf_iface_shutdown(struct ospf_iface *ifa); +int ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen); +int ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new); +void ospf_reconfigure_ifaces(struct ospf_proto *p); +void ospf_open_vlink_sk(struct ospf_proto *p); +struct nbma_node *find_nbma_node_(list *nnl, ip_addr ip); + +static inline struct nbma_node * find_nbma_node(struct ospf_iface *ifa, ip_addr ip) +{ return find_nbma_node_(&ifa->nbma_list, ip); } + +/* neighbor.c */ +struct ospf_neighbor *ospf_neighbor_new(struct ospf_iface *ifa); +void ospf_neigh_sm(struct ospf_neighbor *n, int event); +void ospf_dr_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); +void ospf_neigh_remove(struct ospf_neighbor *n); +void ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd); +void ospf_sh_neigh_info(struct ospf_neighbor *n); + +/* packet.c */ +void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type); +uint ospf_pkt_maxsize(struct ospf_iface *ifa); +int ospf_rx_hook(sock * sk, int size); +// void ospf_tx_hook(sock * sk); +void ospf_err_hook(sock * sk, int err); +void ospf_verr_hook(sock *sk, int err); +void ospf_send_to(struct ospf_iface *ifa, ip_addr ip); +void ospf_send_to_agt(struct ospf_iface *ifa, u8 state); +void ospf_send_to_bdr(struct ospf_iface *ifa); + +static inline void ospf_send_to_all(struct ospf_iface *ifa) +{ ospf_send_to(ifa, ifa->all_routers); } + +static inline void ospf_send_to_des(struct ospf_iface *ifa) +{ + if (ipa_nonzero(ifa->des_routers)) + ospf_send_to(ifa, ifa->des_routers); + else + ospf_send_to_bdr(ifa); +} + +static inline uint ospf_pkt_hdrlen(struct ospf_proto *p) +{ return ospf_is_v2(p) ? (sizeof(struct ospf_packet) + sizeof(union ospf_auth)) : sizeof(struct ospf_packet); } + +static inline void * ospf_tx_buffer(struct ospf_iface *ifa) +{ return ifa->sk->tbuf; } + +/* hello.c */ +#define OHS_HELLO 0 +#define OHS_POLL 1 +#define OHS_SHUTDOWN 2 + +void ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn); +void ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n, ip_addr faddr); + +/* dbdes.c */ +void ospf_send_dbdes(struct ospf_neighbor *n, int next); +void ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n); + +/* lsreq.c */ +void ospf_send_lsreq(struct ospf_proto *p, struct ospf_neighbor *n); +void ospf_receive_lsreq(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n); + +/* lsupd.c */ +void ospf_dump_lsahdr(struct ospf_proto *p, struct ospf_lsa_header *lsa_n); +void ospf_dump_common(struct ospf_proto *p, struct ospf_packet *pkt); +int ospf_lsupd_flood(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neighbor *from); +int ospf_send_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_list, uint lsa_count, struct ospf_neighbor *n); +void ospf_rxmt_lsupd(struct ospf_proto *p, struct ospf_neighbor *n); +void ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n); + +/* lsack.c */ +void ospf_enqueue_lsack(struct ospf_neighbor *n, struct ospf_lsa_header *h_n, int queue); +void ospf_reset_lsack_queue(struct ospf_neighbor *n); +void ospf_lsack_send(struct ospf_neighbor *n, int queue); +void ospf_receive_lsack(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n); + + #include "proto/ospf/rt.h" -#include "proto/ospf/hello.h" -#include "proto/ospf/packet.h" -#include "proto/ospf/iface.h" -#include "proto/ospf/neighbor.h" #include "proto/ospf/topology.h" -#include "proto/ospf/dbdes.h" -#include "proto/ospf/lsreq.h" -#include "proto/ospf/lsupd.h" -#include "proto/ospf/lsack.h" #include "proto/ospf/lsalib.h" #endif /* _BIRD_OSPF_H_ */ diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index 1240b05c..96351178 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 1999--2005 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -13,233 +15,216 @@ void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) { - struct proto_ospf *po = ifa->oa->po; + struct ospf_proto *p = ifa->oa->po; struct ospf_packet *pkt; pkt = (struct ospf_packet *) buf; - pkt->version = OSPF_VERSION; - + pkt->version = ospf_get_version(p); pkt->type = h_type; - - pkt->routerid = htonl(po->router_id); + pkt->length = htons(ospf_pkt_maxsize(ifa)); + pkt->routerid = htonl(p->router_id); pkt->areaid = htonl(ifa->oa->areaid); - -#ifdef OSPFv3 - pkt->instance_id = ifa->instance_id; -#endif - -#ifdef OSPFv2 - pkt->autype = htons(ifa->autype); -#endif - pkt->checksum = 0; + pkt->instance_id = ifa->instance_id; + pkt->autype = ifa->autype; } -unsigned +uint ospf_pkt_maxsize(struct ospf_iface *ifa) { - unsigned headers = SIZE_OF_IP_HEADER; + uint headers = SIZE_OF_IP_HEADER; -#ifdef OSPFv2 + /* Relevant just for OSPFv2 */ if (ifa->autype == OSPF_AUTH_CRYPT) headers += OSPF_AUTH_CRYPT_SIZE; -#endif return ifa->tx_length - headers; } -#ifdef OSPFv2 - +/* We assume OSPFv2 in ospf_pkt_finalize() */ static void ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) { struct password_item *passwd = NULL; - void *tail; - struct MD5Context ctxt; - char password[OSPF_AUTH_CRYPT_SIZE]; + union ospf_auth *auth = (void *) (pkt + 1); + uint plen = ntohs(pkt->length); pkt->checksum = 0; - pkt->autype = htons(ifa->autype); - bzero(&pkt->u, sizeof(union ospf_auth)); + pkt->autype = ifa->autype; + bzero(auth, sizeof(union ospf_auth)); - /* Compatibility note: pkt->u may contain anything if autype is + /* Compatibility note: auth may contain anything if autype is none, but nonzero values do not work with Mikrotik OSPF */ - switch(ifa->autype) + switch (ifa->autype) { - case OSPF_AUTH_SIMPLE: - passwd = password_find(ifa->passwords, 1); - if (!passwd) - { - log( L_ERR "No suitable password found for authentication" ); - return; - } - password_cpy(pkt->u.password, passwd->password, sizeof(union ospf_auth)); - case OSPF_AUTH_NONE: - pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet) - - sizeof(union ospf_auth), (pkt + 1), - ntohs(pkt->length) - - sizeof(struct ospf_packet), NULL); - break; - case OSPF_AUTH_CRYPT: - passwd = password_find(ifa->passwords, 0); - if (!passwd) - { - log( L_ERR "No suitable password found for authentication" ); - return; - } + case OSPF_AUTH_SIMPLE: + passwd = password_find(ifa->passwords, 1); + if (!passwd) + { + log(L_ERR "No suitable password found for authentication"); + return; + } + password_cpy(auth->password, passwd->password, sizeof(union ospf_auth)); - /* Perhaps use random value to prevent replay attacks after - reboot when system does not have independent RTC? */ - if (!ifa->csn) - { - ifa->csn = (u32) now; - ifa->csn_use = now; - } + case OSPF_AUTH_NONE: + { + void *body = (void *) (auth + 1); + uint blen = plen - sizeof(struct ospf_packet) - sizeof(union ospf_auth); + pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet), body, blen, NULL); + } + break; - /* We must have sufficient delay between sending a packet and increasing - CSN to prevent reordering of packets (in a network) with different CSNs */ - if ((now - ifa->csn_use) > 1) - ifa->csn++; + case OSPF_AUTH_CRYPT: + passwd = password_find(ifa->passwords, 0); + if (!passwd) + { + log(L_ERR "No suitable password found for authentication"); + return; + } + /* Perhaps use random value to prevent replay attacks after + reboot when system does not have independent RTC? */ + if (!ifa->csn) + { + ifa->csn = (u32) now; ifa->csn_use = now; + } - pkt->u.md5.keyid = passwd->id; - pkt->u.md5.len = OSPF_AUTH_CRYPT_SIZE; - pkt->u.md5.zero = 0; - pkt->u.md5.csn = htonl(ifa->csn); - tail = ((void *)pkt) + ntohs(pkt->length); - MD5Init(&ctxt); - MD5Update(&ctxt, (char *) pkt, ntohs(pkt->length)); - password_cpy(password, passwd->password, OSPF_AUTH_CRYPT_SIZE); - MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); - MD5Final(tail, &ctxt); - break; - default: - bug("Unknown authentication type"); + /* We must have sufficient delay between sending a packet and increasing + CSN to prevent reordering of packets (in a network) with different CSNs */ + if ((now - ifa->csn_use) > 1) + ifa->csn++; + + ifa->csn_use = now; + + auth->md5.zero = 0; + auth->md5.keyid = passwd->id; + auth->md5.len = OSPF_AUTH_CRYPT_SIZE; + auth->md5.csn = htonl(ifa->csn); + + void *tail = ((void *) pkt) + plen; + char password[OSPF_AUTH_CRYPT_SIZE]; + password_cpy(password, passwd->password, OSPF_AUTH_CRYPT_SIZE); + + struct MD5Context ctxt; + MD5Init(&ctxt); + MD5Update(&ctxt, (char *) pkt, plen); + MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); + MD5Final(tail, &ctxt); + break; + + default: + bug("Unknown authentication type"); } } +/* We assume OSPFv2 in ospf_pkt_checkauth() */ static int ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, int size) { - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; + struct ospf_proto *p = ifa->oa->po; + union ospf_auth *auth = (void *) (pkt + 1); struct password_item *pass = NULL, *ptmp; - void *tail; - char md5sum[OSPF_AUTH_CRYPT_SIZE]; char password[OSPF_AUTH_CRYPT_SIZE]; - struct MD5Context ctxt; + uint plen = ntohs(pkt->length); + u8 autype = pkt->autype; - if (pkt->autype != htons(ifa->autype)) + if (autype != ifa->autype) { - OSPF_TRACE(D_PACKETS, "OSPF_auth: Method differs (%d)", ntohs(pkt->autype)); + OSPF_TRACE(D_PACKETS, "OSPF_auth: Method differs (%d)", autype); return 0; } - switch(ifa->autype) + switch (autype) { - case OSPF_AUTH_NONE: - return 1; - break; - case OSPF_AUTH_SIMPLE: - pass = password_find(ifa->passwords, 1); - if (!pass) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: no password found"); - return 0; - } - password_cpy(password, pass->password, sizeof(union ospf_auth)); + case OSPF_AUTH_NONE: + return 1; - if (memcmp(pkt->u.password, password, sizeof(union ospf_auth))) - { - char ppass[sizeof(union ospf_auth) + 1]; - bzero(ppass, (sizeof(union ospf_auth) + 1)); - memcpy(ppass, pkt->u.password, sizeof(union ospf_auth)); - OSPF_TRACE(D_PACKETS, "OSPF_auth: different passwords (%s)", ppass); - return 0; - } - return 1; - break; - case OSPF_AUTH_CRYPT: - if (pkt->u.md5.len != OSPF_AUTH_CRYPT_SIZE) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong size of md5 digest"); - return 0; - } - - if (ntohs(pkt->length) + OSPF_AUTH_CRYPT_SIZE > size) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: size mismatch (%d vs %d)", - ntohs(pkt->length) + OSPF_AUTH_CRYPT_SIZE, size); - return 0; - } - - tail = ((void *)pkt) + ntohs(pkt->length); - - if (ifa->passwords) - { - WALK_LIST(ptmp, *(ifa->passwords)) - { - if (pkt->u.md5.keyid != ptmp->id) continue; - if ((ptmp->accfrom > now_real) || (ptmp->accto < now_real)) continue; - pass = ptmp; - break; - } - } - - if (!pass) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: no suitable md5 password found"); - return 0; - } - - if (n) - { - u32 rcv_csn = ntohl(pkt->u.md5.csn); - if(rcv_csn < n->csn) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: lower sequence number (rcv %d, old %d)", rcv_csn, n->csn); - return 0; - } - - n->csn = rcv_csn; - } - - MD5Init(&ctxt); - MD5Update(&ctxt, (char *) pkt, ntohs(pkt->length)); - password_cpy(password, pass->password, OSPF_AUTH_CRYPT_SIZE); - MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); - MD5Final(md5sum, &ctxt); - if (memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE)) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong md5 digest"); - return 0; - } - return 1; - break; - default: - OSPF_TRACE(D_PACKETS, "OSPF_auth: unknown auth type"); + case OSPF_AUTH_SIMPLE: + pass = password_find(ifa->passwords, 1); + if (!pass) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: no password found"); return 0; + } + + password_cpy(password, pass->password, sizeof(union ospf_auth)); + if (memcmp(auth->password, password, sizeof(union ospf_auth))) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: different passwords"); + return 0; + } + return 1; + + case OSPF_AUTH_CRYPT: + if (auth->md5.len != OSPF_AUTH_CRYPT_SIZE) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong size of md5 digest"); + return 0; + } + + if (plen + OSPF_AUTH_CRYPT_SIZE > size) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: size mismatch (%d vs %d)", + plen + OSPF_AUTH_CRYPT_SIZE, size); + return 0; + } + + if (n) + { + u32 rcv_csn = ntohl(auth->md5.csn); + if(rcv_csn < n->csn) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: lower sequence number (rcv %d, old %d)", rcv_csn, n->csn); + return 0; + } + + n->csn = rcv_csn; + } + + if (ifa->passwords) + { + WALK_LIST(ptmp, *(ifa->passwords)) + { + if (auth->md5.keyid != ptmp->id) continue; + if ((ptmp->accfrom > now_real) || (ptmp->accto < now_real)) continue; + pass = ptmp; + break; + } + } + + if (!pass) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: no suitable md5 password found"); + return 0; + } + + void *tail = ((void *) pkt) + plen; + char md5sum[OSPF_AUTH_CRYPT_SIZE]; + password_cpy(password, pass->password, OSPF_AUTH_CRYPT_SIZE); + + struct MD5Context ctxt; + MD5Init(&ctxt); + MD5Update(&ctxt, (char *) pkt, plen); + MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); + MD5Final(md5sum, &ctxt); + + if (memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE)) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong md5 digest"); + return 0; + } + return 1; + + default: + OSPF_TRACE(D_PACKETS, "OSPF_auth: unknown auth type"); + return 0; } } -#else - -/* OSPFv3 authentication not yet supported */ - -static inline void -ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) -{ } - -static int -ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, int size) -{ return 1; } - -#endif - /** * ospf_rx_hook @@ -266,43 +251,44 @@ ospf_rx_hook(sock *sk, int size) /* Initially, the packet is associated with the 'master' iface */ struct ospf_iface *ifa = sk->data; - struct proto_ospf *po = ifa->oa->po; - // struct proto *p = &po->proto; + struct ospf_proto *p = ifa->oa->po; - int src_local, dst_local UNUSED, dst_mcast; + int src_local, dst_local, dst_mcast; src_local = ipa_in_net(sk->faddr, ifa->addr->prefix, ifa->addr->pxlen); dst_local = ipa_equal(sk->laddr, ifa->addr->ip); - dst_mcast = ipa_equal(sk->laddr, ifa->all_routers) || ipa_equal(sk->laddr, AllDRouters); + dst_mcast = ipa_equal(sk->laddr, ifa->all_routers) || ipa_equal(sk->laddr, ifa->des_routers); -#ifdef OSPFv2 - /* First, we eliminate packets with strange address combinations. - * In OSPFv2, they might be for other ospf_ifaces (with different IP - * prefix) on the same real iface, so we don't log it. We enforce - * that (src_local || dst_local), therefore we are eliminating all - * such cases. - */ - if (dst_mcast && !src_local) - return 1; - if (!dst_mcast && !dst_local) - return 1; + if (ospf_is_v2(p)) + { + /* First, we eliminate packets with strange address combinations. + * In OSPFv2, they might be for other ospf_ifaces (with different IP + * prefix) on the same real iface, so we don't log it. We enforce + * that (src_local || dst_local), therefore we are eliminating all + * such cases. + */ + if (dst_mcast && !src_local) + return 1; + if (!dst_mcast && !dst_local) + return 1; - /* Ignore my own broadcast packets */ - if (ifa->cf->real_bcast && ipa_equal(sk->faddr, ifa->addr->ip)) - return 1; -#else /* OSPFv3 */ - - /* In OSPFv3, src_local and dst_local mean link-local. - * RFC 5340 says that local (non-vlink) packets use - * link-local src address, but does not enforce it. Strange. - */ - if (dst_mcast && !src_local) - log(L_WARN "OSPF: Received multicast packet from %I (not link-local)", sk->faddr); -#endif + /* Ignore my own broadcast packets */ + if (ifa->cf->real_bcast && ipa_equal(sk->faddr, ifa->addr->ip)) + return 1; + } + else + { + /* In OSPFv3, src_local and dst_local mean link-local. + * RFC 5340 says that local (non-vlink) packets use + * link-local src address, but does not enforce it. Strange. + */ + if (dst_mcast && !src_local) + log(L_WARN "OSPF: Received multicast packet from %I (not link-local)", sk->faddr); + } /* Second, we check packet size, checksum, and the protocol version */ - struct ospf_packet *ps = (struct ospf_packet *) ip_skip_header(sk->rbuf, &size); + struct ospf_packet *pkt = (struct ospf_packet *) ip_skip_header(sk->rbuf, &size); - if (ps == NULL) + if (pkt == NULL) { log(L_ERR "%s%I - bad IP header", mesg, sk->faddr); return 1; @@ -314,13 +300,13 @@ ospf_rx_hook(sock *sk, int size) return 1; } - if ((unsigned) size < sizeof(struct ospf_packet)) + if ((uint) size < sizeof(struct ospf_packet)) { log(L_ERR "%s%I - too short (%u bytes)", mesg, sk->faddr, size); return 1; } - uint plen = ntohs(ps->length); + uint plen = ntohs(pkt->length); if ((plen < sizeof(struct ospf_packet)) || ((plen % 4) != 0)) { log(L_ERR "%s%I - invalid length (%u)", mesg, sk->faddr, plen); @@ -348,89 +334,101 @@ ospf_rx_hook(sock *sk, int size) return 1; } - if (ps->version != OSPF_VERSION) + if (pkt->version != ospf_get_version(p)) { - log(L_ERR "%s%I - version %u", mesg, sk->faddr, ps->version); + log(L_ERR "%s%I - version %u", mesg, sk->faddr, pkt->version); return 1; } -#ifdef OSPFv2 - if ((ps->autype != htons(OSPF_AUTH_CRYPT)) && - (!ipsum_verify(ps, 16, (void *) ps + sizeof(struct ospf_packet), - plen - sizeof(struct ospf_packet), NULL))) + if (ospf_is_v2(p) && (pkt->autype != OSPF_AUTH_CRYPT)) { - log(L_ERR "%s%I - bad checksum", mesg, sk->faddr); - return 1; - } -#endif + uint hlen = sizeof(struct ospf_packet) + sizeof(union ospf_auth); + uint blen = plen - hlen; + void *body = ((void *) pkt) + hlen; + if (! ipsum_verify(pkt, sizeof(struct ospf_packet), body, blen, NULL)) + { + log(L_ERR "%s%I - bad checksum", mesg, sk->faddr); + return 1; + } + } /* Third, we resolve associated iface and handle vlinks. */ - u32 areaid = ntohl(ps->areaid); - u32 rid = ntohl(ps->routerid); + u32 areaid = ntohl(pkt->areaid); + u32 rid = ntohl(pkt->routerid); + u8 instance_id = pkt->instance_id; - if ((areaid == ifa->oa->areaid) -#ifdef OSPFv3 - && (ps->instance_id == ifa->instance_id) -#endif - ) + if (areaid == ifa->oa->areaid) { - /* It is real iface, source should be local (in OSPFv2) */ -#ifdef OSPFv2 - if (!src_local) + /* Matching area ID */ + + if (instance_id != ifa->instance_id) return 1; -#endif - } - else if (dst_mcast || (areaid != 0)) - { - /* Obvious mismatch */ -#ifdef OSPFv2 - /* We ignore mismatch in OSPFv3, because there might be - other instance with different instance ID */ - log(L_ERR "%s%I - area does not match (%R vs %R)", - mesg, sk->faddr, areaid, ifa->oa->areaid); -#endif + /* It is real iface, source should be local (in OSPFv2) */ + if (ospf_is_v2(p) && !src_local) + { + log(L_ERR "%s%I - strange source address for %s", mesg, sk->faddr, ifa->ifname); + return 1; + } + + goto found; + } + else if ((areaid == 0) && !dst_mcast) + { + /* Backbone area ID and possible vlink packet */ + + if ((p->areano == 1) || !oa_is_ext(ifa->oa)) + return 1; + + struct ospf_iface *iff = NULL; + WALK_LIST(iff, p->iface_list) + { + if ((iff->type == OSPF_IT_VLINK) && + (iff->voa == ifa->oa) && + (iff->instance_id == instance_id) && + (iff->vid == rid)) + { + /* Vlink should be UP */ + if (iff->state != OSPF_IS_PTP) + return 1; + + ifa = iff; + goto found; + } + } + + /* + * Cannot find matching vlink. It is either misconfigured vlink; NBMA or + * PtMP with misconfigured area ID, or packet for some other instance (that + * is possible even if instance_id == ifa->instance_id, because it may be + * also vlink packet in the other instance, which is different namespace). + */ + return 1; } else { - /* Some vlink? */ - struct ospf_iface *iff = NULL; + /* Non-matching area ID but cannot be vlink packet */ - WALK_LIST(iff, po->iface_list) - { - if ((iff->type == OSPF_IT_VLINK) && - (iff->voa == ifa->oa) && -#ifdef OSPFv3 - (iff->instance_id == ps->instance_id) && -#endif - (iff->vid == rid)) - { - /* Vlink should be UP */ - if (iff->state != OSPF_IS_PTP) - return 1; - - ifa = iff; - goto found; - } - } + if (instance_id != ifa->instance_id) + return 1; -#ifdef OSPFv2 - log(L_WARN "OSPF: Received packet for unknown vlink (ID %R, IP %I)", rid, sk->faddr); -#endif + log(L_ERR "%s%I - area does not match (%R vs %R)", + mesg, sk->faddr, areaid, ifa->oa->areaid); return 1; } + found: if (ifa->stub) /* This shouldn't happen */ return 1; - if (ipa_equal(sk->laddr, AllDRouters) && (ifa->sk_dr == 0)) + if (ipa_equal(sk->laddr, ifa->des_routers) && (ifa->sk_dr == 0)) return 1; - if (rid == po->router_id) + if (rid == p->router_id) { log(L_ERR "%s%I - received my own router ID!", mesg, sk->faddr); return 1; @@ -442,62 +440,51 @@ ospf_rx_hook(sock *sk, int size) return 1; } -#ifdef OSPFv2 /* In OSPFv2, neighbors are identified by either IP or Router ID, base on network type */ + uint t = ifa->type; struct ospf_neighbor *n; - if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP)) + if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP))) n = find_neigh_by_ip(ifa, sk->faddr); else n = find_neigh(ifa, rid); -#else - struct ospf_neighbor *n = find_neigh(ifa, rid); -#endif - if(!n && (ps->type != HELLO_P)) + if (!n && (pkt->type != HELLO_P)) { log(L_WARN "OSPF: Received non-hello packet from unknown neighbor (src %I, iface %s)", sk->faddr, ifa->ifname); return 1; } - if (!ospf_pkt_checkauth(n, ifa, ps, size)) + if (ospf_is_v2(p) && !ospf_pkt_checkauth(n, ifa, pkt, size)) { log(L_ERR "%s%I - authentication failed", mesg, sk->faddr); return 1; } - /* Dump packet - pu8=(u8 *)(sk->rbuf+5*4); - for(i=0;ilength);i+=4) - DBG("%s: received %u,%u,%u,%u\n",p->name, pu8[i+0], pu8[i+1], pu8[i+2], - pu8[i+3]); - DBG("%s: received size: %u\n",p->name,size); - */ - - switch (ps->type) + switch (pkt->type) { case HELLO_P: - DBG("%s: Hello received.\n", p->name); - ospf_hello_receive(ps, ifa, n, sk->faddr); + ospf_receive_hello(pkt, ifa, n, sk->faddr); break; + case DBDES_P: - DBG("%s: Database description received.\n", p->name); - ospf_dbdes_receive(ps, ifa, n); + ospf_receive_dbdes(pkt, ifa, n); break; + case LSREQ_P: - DBG("%s: Link state request received.\n", p->name); - ospf_lsreq_receive(ps, ifa, n); + ospf_receive_lsreq(pkt, ifa, n); break; + case LSUPD_P: - DBG("%s: Link state update received.\n", p->name); - ospf_lsupd_receive(ps, ifa, n); + ospf_receive_lsupd(pkt, ifa, n); break; + case LSACK_P: - DBG("%s: Link state ack received.\n", p->name); - ospf_lsack_receive(ps, ifa, n); + ospf_receive_lsack(pkt, ifa, n); break; + default: - log(L_ERR "%s%I - wrong type %u", mesg, sk->faddr, ps->type); + log(L_ERR "%s%I - wrong type %u", mesg, sk->faddr, pkt->type); return 1; }; return 1; @@ -508,7 +495,7 @@ void ospf_tx_hook(sock * sk) { struct ospf_iface *ifa= (struct ospf_iface *) (sk->data); -// struct proto *p = (struct proto *) (ifa->oa->po); +// struct proto *p = (struct proto *) (ifa->oa->p); log(L_ERR "OSPF: TX hook called on %s", ifa->ifname); } */ @@ -517,16 +504,35 @@ void ospf_err_hook(sock * sk, int err) { struct ospf_iface *ifa= (struct ospf_iface *) (sk->data); - struct proto *p = &(ifa->oa->po->proto); - log(L_ERR "%s: Socket error on %s: %M", p->name, ifa->ifname, err); + struct ospf_proto *p = ifa->oa->po; + log(L_ERR "%s: Socket error on %s: %M", p->p.name, ifa->ifname, err); } void ospf_verr_hook(sock *sk, int err) { - struct proto_ospf *po = (struct proto_ospf *) (sk->data); - struct proto *p = &po->proto; - log(L_ERR "%s: Vlink socket error: %M", p->name, err); + struct ospf_proto *p = (struct ospf_proto *) (sk->data); + log(L_ERR "%s: Vlink socket error: %M", p->p.name, err); +} + +void +ospf_send_to(struct ospf_iface *ifa, ip_addr dst) +{ + sock *sk = ifa->sk; + struct ospf_packet *pkt = (struct ospf_packet *) sk->tbuf; + int plen = ntohs(pkt->length); + + if (ospf_is_v2(ifa->oa->po)) + { + if (ifa->autype == OSPF_AUTH_CRYPT) + plen += OSPF_AUTH_CRYPT_SIZE; + + ospf_pkt_finalize(ifa, pkt); + } + + int done = sk_send_to(sk, plen, dst, 0); + if (!done) + log(L_WARN "OSPF: TX queue full on %s", ifa->ifname); } void @@ -542,28 +548,8 @@ ospf_send_to_agt(struct ospf_iface *ifa, u8 state) void ospf_send_to_bdr(struct ospf_iface *ifa) { - if (!ipa_equal(ifa->drip, IPA_NONE)) + if (ipa_nonzero(ifa->drip)) ospf_send_to(ifa, ifa->drip); - if (!ipa_equal(ifa->bdrip, IPA_NONE)) + if (ipa_nonzero(ifa->bdrip)) ospf_send_to(ifa, ifa->bdrip); } - -void -ospf_send_to(struct ospf_iface *ifa, ip_addr dst) -{ - sock *sk = ifa->sk; - struct ospf_packet *pkt = (struct ospf_packet *) sk->tbuf; - int len = ntohs(pkt->length); - -#ifdef OSPFv2 - if (ifa->autype == OSPF_AUTH_CRYPT) - len += OSPF_AUTH_CRYPT_SIZE; -#endif - - ospf_pkt_finalize(ifa, pkt); - - int done = sk_send_to(sk, len, dst, 0); - if (!done) - log(L_WARN "OSPF: TX queue full on %s", ifa->ifname); -} - diff --git a/proto/ospf/packet.h b/proto/ospf/packet.h deleted file mode 100644 index 4ba1f08c..00000000 --- a/proto/ospf/packet.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * BIRD -- OSPF - * - * (c) 1999--2004 Ondrej Filip - * - * Can be freely distributed and used under the terms of the GNU GPL. - * - */ - -#ifndef _BIRD_OSPF_PACKET_H_ -#define _BIRD_OSPF_PACKET_H_ - -void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type); -uint ospf_pkt_maxsize(struct ospf_iface *ifa); -int ospf_rx_hook(sock * sk, int size); -// void ospf_tx_hook(sock * sk); -void ospf_err_hook(sock * sk, int err); -void ospf_verr_hook(sock *sk, int err); -void ospf_send_to_agt(struct ospf_iface *ifa, u8 state); -void ospf_send_to_bdr(struct ospf_iface *ifa); -void ospf_send_to(struct ospf_iface *ifa, ip_addr ip); - -static inline void ospf_send_to_all(struct ospf_iface *ifa) { ospf_send_to(ifa, ifa->all_routers); } - -static inline void * ospf_tx_buffer(struct ospf_iface *ifa) { return ifa->sk->tbuf; } - - -#endif /* _BIRD_OSPF_PACKET_H_ */ diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 2a879c05..c4340ee5 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -1,9 +1,11 @@ /* - * BIRD -- OSPF + * BIRD -- OSPF * - * (c) 2000--2004 Ondrej Filip + * (c) 2000--2004 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * - * Can be freely distributed and used under the terms of the GNU GPL. + * Can be freely distributed and used under the terms of the GNU GPL. */ #include "ospf.h" @@ -11,15 +13,7 @@ static void add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, u32 dist, struct ospf_area *oa, int i); -static void rt_sync(struct proto_ospf *po); - -/* In ospf_area->rtr we store paths to routers, but we use RID (and not IP address) - as index, so we need to encapsulate RID to IP address */ -#ifdef OSPFv2 -#define ipa_from_rid(x) _MI(x) -#else /* OSPFv3 */ -#define ipa_from_rid(x) _MI(0,0,0,x) -#endif +static void rt_sync(struct ospf_proto *p); static inline void reset_ri(ort *ort) @@ -33,7 +27,7 @@ ospf_rt_initort(struct fib_node *fn) ort *ri = (ort *) fn; reset_ri(ri); ri->old_rta = NULL; - ri->fn.x0 = ri->fn.x1 = 0; + ri->fn.flags = 0; } static inline int @@ -49,9 +43,9 @@ unresolved_vlink(ort *ort) } static inline struct mpnh * -new_nexthop(struct proto_ospf *po, ip_addr gw, struct iface *iface, unsigned char weight) +new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, unsigned char weight) { - struct mpnh *nh = lp_alloc(po->nhpool, sizeof(struct mpnh)); + struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh)); nh->gw = gw; nh->iface = iface; nh->next = NULL; @@ -60,9 +54,9 @@ new_nexthop(struct proto_ospf *po, ip_addr gw, struct iface *iface, unsigned cha } static inline struct mpnh * -copy_nexthop(struct proto_ospf *po, const struct mpnh *src) +copy_nexthop(struct ospf_proto *p, const struct mpnh *src) { - struct mpnh *nh = lp_alloc(po->nhpool, sizeof(struct mpnh)); + struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh)); nh->gw = src->gw; nh->iface = src->iface; nh->next = NULL; @@ -95,11 +89,11 @@ cmp_nhs(struct mpnh *s1, struct mpnh *s2) } static struct mpnh * -merge_nexthops(struct proto_ospf *po, struct mpnh *s1, struct mpnh *s2, int r1, int r2) +merge_nexthops(struct ospf_proto *p, struct mpnh *s1, struct mpnh *s2, int r1, int r2) { struct mpnh *root = NULL; struct mpnh **n = &root; - int count = po->ecmp; + int count = p->ecmp; /* * r1, r2 signalize whether we can reuse nexthops from s1, s2. @@ -118,17 +112,17 @@ merge_nexthops(struct proto_ospf *po, struct mpnh *s1, struct mpnh *s2, int r1, int cmp = cmp_nhs(s1, s2); if (cmp < 0) { - *n = r1 ? s1 : copy_nexthop(po, s1); + *n = r1 ? s1 : copy_nexthop(p, s1); s1 = s1->next; } else if (cmp > 0) { - *n = r2 ? s2 : copy_nexthop(po, s2); + *n = r2 ? s2 : copy_nexthop(p, s2); s2 = s2->next; } else { - *n = r1 ? s1 : (r2 ? s2 : copy_nexthop(po, s1)); + *n = r1 ? s1 : (r2 ? s2 : copy_nexthop(p, s1)); s1 = s1->next; s2 = s2->next; } @@ -152,7 +146,7 @@ has_device_nexthops(const struct mpnh *n) /* Replace device nexthops with nexthops to gw */ static struct mpnh * -fix_device_nexthops(struct proto_ospf *po, const struct mpnh *n, ip_addr gw) +fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw) { struct mpnh *root1 = NULL; struct mpnh *root2 = NULL; @@ -165,7 +159,7 @@ fix_device_nexthops(struct proto_ospf *po, const struct mpnh *n, ip_addr gw) for (; n; n = n->next) { - struct mpnh *nn = new_nexthop(po, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight); + struct mpnh *nn = new_nexthop(p, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight); if (ipa_zero(n->gw)) { @@ -179,7 +173,7 @@ fix_device_nexthops(struct proto_ospf *po, const struct mpnh *n, ip_addr gw) } } - return merge_nexthops(po, root1, root2, 1, 1); + return merge_nexthops(p, root1, root2, 1, 1); } @@ -235,7 +229,7 @@ orta_prefer_lsa(const orta *new, const orta *old) * the old orta. */ static int -orta_compare(const struct proto_ospf *po, const orta *new, const orta *old) +orta_compare(const struct ospf_proto *p, const orta *new, const orta *old) { int r; @@ -270,7 +264,7 @@ orta_compare(const struct proto_ospf *po, const orta *new, const orta *old) return 1; - if (po->ecmp) + if (p->ecmp) return 0; /* Prefer routes with higher Router ID, just to be more deterministic */ @@ -286,14 +280,14 @@ orta_compare(const struct proto_ospf *po, const orta *new, const orta *old) * than 0 if the new ASBR is less or more preferred than the old ASBR. */ static int -orta_compare_asbr(const struct proto_ospf *po, const orta *new, const orta *old) +orta_compare_asbr(const struct ospf_proto *p, const orta *new, const orta *old) { int r; if (old->type == RTS_DUMMY) return 1; - if (!po->rfc1583) + if (!p->rfc1583) { r = epath_preferred(new) - epath_preferred(old); if (r) return r; @@ -316,7 +310,7 @@ orta_compare_asbr(const struct proto_ospf *po, const orta *new, const orta *old) * than 0 if the new orta is less, equal or more preferred than the old orta. */ static int -orta_compare_ext(const struct proto_ospf *po, const orta *new, const orta *old) +orta_compare_ext(const struct ospf_proto *p, const orta *new, const orta *old) { int r; @@ -335,7 +329,7 @@ orta_compare_ext(const struct proto_ospf *po, const orta *new, const orta *old) } /* 16.4 (6c) - if not RFC1583, prefer routes with preferred ASBR/next_hop */ - if (!po->rfc1583) + if (!p->rfc1583) { r = orta_pref(new) - orta_pref(old); if (r) return r; @@ -346,7 +340,7 @@ orta_compare_ext(const struct proto_ospf *po, const orta *new, const orta *old) if (r) return r; - if (po->ecmp && po->merge_external) + if (p->ecmp && p->merge_external) return 0; /* @@ -369,13 +363,13 @@ ort_replace(ort *o, const orta *new) } static void -ort_merge(struct proto_ospf *po, ort *o, const orta *new) +ort_merge(struct ospf_proto *p, ort *o, const orta *new) { orta *old = &o->n; if (old->nhs != new->nhs) { - old->nhs = merge_nexthops(po, old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse); + old->nhs = merge_nexthops(p, old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse); old->nhs_reuse = 1; } @@ -384,13 +378,13 @@ ort_merge(struct proto_ospf *po, ort *o, const orta *new) } static void -ort_merge_ext(struct proto_ospf *po, ort *o, const orta *new) +ort_merge_ext(struct ospf_proto *p, ort *o, const orta *new) { orta *old = &o->n; if (old->nhs != new->nhs) { - old->nhs = merge_nexthops(po, old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse); + old->nhs = merge_nexthops(p, old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse); old->nhs_reuse = 1; } @@ -415,15 +409,15 @@ ort_merge_ext(struct proto_ospf *po, ort *o, const orta *new) static inline void -ri_install_net(struct proto_ospf *po, ip_addr prefix, int pxlen, const orta *new) +ri_install_net(struct ospf_proto *p, ip_addr prefix, int pxlen, const orta *new) { - ort *old = (ort *) fib_get(&po->rtf, &prefix, pxlen); - int cmp = orta_compare(po, new, &old->n); + ort *old = (ort *) fib_get(&p->rtf, &prefix, pxlen); + int cmp = orta_compare(p, new, &old->n); if (cmp > 0) ort_replace(old, new); else if (cmp == 0) - ort_merge(po, old, new); + ort_merge(p, old, new); } static inline void @@ -440,51 +434,55 @@ ri_install_rt(struct ospf_area *oa, u32 rid, const orta *new) } static inline void -ri_install_asbr(struct proto_ospf *po, ip_addr *addr, const orta *new) +ri_install_asbr(struct ospf_proto *p, ip_addr *addr, const orta *new) { - ort *old = (ort *) fib_get(&po->backbone->rtr, addr, MAX_PREFIX_LENGTH); - if (orta_compare_asbr(po, new, &old->n) > 0) + ort *old = (ort *) fib_get(&p->backbone->rtr, addr, MAX_PREFIX_LENGTH); + if (orta_compare_asbr(p, new, &old->n) > 0) ort_replace(old, new); } static inline void -ri_install_ext(struct proto_ospf *po, ip_addr prefix, int pxlen, const orta *new) +ri_install_ext(struct ospf_proto *p, ip_addr prefix, int pxlen, const orta *new) { - ort *old = (ort *) fib_get(&po->rtf, &prefix, pxlen); - int cmp = orta_compare_ext(po, new, &old->n); + ort *old = (ort *) fib_get(&p->rtf, &prefix, pxlen); + int cmp = orta_compare_ext(p, new, &old->n); if (cmp > 0) ort_replace(old, new); else if (cmp == 0) - ort_merge_ext(po, old, new); + ort_merge_ext(p, old, new); } static inline struct ospf_iface * rt_pos_to_ifa(struct ospf_area *oa, int pos) { struct ospf_iface *ifa; + WALK_LIST(ifa, oa->po->iface_list) if (ifa->oa == oa && pos >= ifa->rt_pos_beg && pos < ifa->rt_pos_end) return ifa; + return NULL; } -#ifdef OSPFv3 static inline struct ospf_iface * px_pos_to_ifa(struct ospf_area *oa, int pos) { struct ospf_iface *ifa; + WALK_LIST(ifa, oa->po->iface_list) if (ifa->oa == oa && pos >= ifa->px_pos_beg && pos < ifa->px_pos_end) return ifa; + return NULL; } -#endif static void add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_hash_entry *en, int pos) { + struct ospf_proto *p = oa->po; + orta nf = { .type = RTS_OSPF, .options = 0, @@ -499,7 +497,7 @@ add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_ if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH) { log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)", - oa->po->proto.name, en->lsa.type, en->lsa.id, en->lsa.rt); + p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt); return; } @@ -514,24 +512,117 @@ add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_ */ struct ospf_iface *ifa; -#ifdef OSPFv2 - ifa = rt_pos_to_ifa(oa, pos); -#else /* OSPFv3 */ - ifa = px_pos_to_ifa(oa, pos); -#endif - - nf.nhs = ifa ? new_nexthop(oa->po, IPA_NONE, ifa->iface, ifa->ecmp_weight) : NULL; + ifa = ospf_is_v2(p) ? rt_pos_to_ifa(oa, pos) : px_pos_to_ifa(oa, pos); + nf.nhs = ifa ? new_nexthop(p, IPA_NONE, ifa->iface, ifa->ecmp_weight) : NULL; } - ri_install_net(oa->po, px, pxlen, &nf); + ri_install_net(p, px, pxlen, &nf); } -#ifdef OSPFv3 -static void -process_prefixes(struct ospf_area *oa) + + +static inline void +spfa_process_rt(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entry *act) +{ + struct ospf_lsa_rt *rt = act->lsa_body; + struct ospf_lsa_rt_walk rtl; + struct top_hash_entry *tmp; + ip_addr prefix; + int pxlen, i; + + if (rt->options & OPT_RT_V) + oa->trcap = 1; + + /* + * In OSPFv3, all routers are added to per-area routing + * tables. But we use it just for ASBRs and ABRs. For the + * purpose of the last step in SPF - prefix-LSA processing in + * spfa_process_prefixes(), we use information stored in LSA db. + */ + if (((rt->options & OPT_RT_E) || (rt->options & OPT_RT_B)) + && (act->lsa.rt != p->router_id)) + { + orta nf = { + .type = RTS_OSPF, + .options = rt->options, + .metric1 = act->dist, + .metric2 = LSINFINITY, + .tag = 0, + .rid = act->lsa.rt, + .oa = oa, + .nhs = act->nhs + }; + ri_install_rt(oa, act->lsa.rt, &nf); + } + + /* Errata 2078 to RFC 5340 4.8.1 - skip links from non-routing nodes */ + if (ospf_is_v3(p) && (act != oa->rt) && !(rt->options & OPT_R)) + break; + + /* Now process Rt links */ + for (lsa_walk_rt_init(p, act, &rtl), i = 0; lsa_walk_rt(&rtl); i++) + { + tmp = NULL; + + switch (rtl.type) + { + case LSART_STUB: + + /* Should not happen, LSART_STUB is not defined in OSPFv3 */ + if (ospf_is_v3(p)) + break; + + /* + * RFC 2328 in 16.1. (2a) says to handle stub networks in an + * second phase after the SPF for an area is calculated. We get + * the same result by handing them here because add_network() + * will keep the best (not the first) found route. + */ + prefix = ipa_from_u32(rtl.id & rtl.data); + pxlen = u32_masklen(rtl.data); + add_network(oa, prefix, pxlen, act->dist + rtl.metric, act, i); + break; + + case LSART_NET: + tmp = ospf_hash_find_net(p->gr, oa->areaid, rtl.id, rtl.nif); + break; + + case LSART_VLNK: + case LSART_PTP: + tmp = ospf_hash_find_rt(p->gr, oa->areaid, rtl.id); + break; + } + + add_cand(&oa->cand, tmp, act, act->dist + rtl.metric, oa, i); + } +} + +static inline void +spfa_process_net(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entry *act) +{ + struct ospf_lsa_net *ln = act->lsa_body; + struct top_hash_entry *tmp; + ip_addr prefix; + int pxlen, i, cnt; + + if (ospf_is_v2(p)) + { + prefix = ipa_from_u32(act->lsa.id & ln->optx); + pxlen = u32_masklen(ln->optx); + add_network(oa, prefix, pxlen, act->dist, act, -1); + } + + cnt = lsa_net_count(&act->lsa); + for (i = 0; i < cnt; i++) + { + tmp = ospf_hash_find_rt(p->gr, oa->areaid, ln->routers[i]); + add_cand(&oa->cand, tmp, act, act->dist, oa, -1); + } +} + +static inline void +spfa_process_prefixes(struct ospf_proto *p, struct ospf_area *oa) { - struct proto_ospf *po = oa->po; - // struct proto *p = &po->proto; struct top_hash_entry *en, *src; struct ospf_lsa_prefix *px; ip_addr pxa; @@ -541,9 +632,9 @@ process_prefixes(struct ospf_area *oa) u32 *buf; int i; - WALK_SLIST(en, po->lsal) + WALK_SLIST(en, p->lsal) { - if (en->lsa.type != LSA_T_PREFIX) + if (en->lsa_type != LSA_T_PREFIX) continue; if (en->domain != oa->areaid) @@ -556,9 +647,9 @@ process_prefixes(struct ospf_area *oa) /* For router prefix-LSA, we would like to find the first router-LSA */ if (px->ref_type == LSA_T_RT) - src = ospf_hash_find_rt(po->gr, oa->areaid, px->ref_rt); + src = ospf_hash_find_rt(p->gr, oa->areaid, px->ref_rt); else - src = ospf_hash_find(po->gr, oa->areaid, px->ref_id, px->ref_rt, px->ref_type); + src = ospf_hash_find(p->gr, oa->areaid, px->ref_id, px->ref_rt, px->ref_type); if (!src) continue; @@ -567,7 +658,7 @@ process_prefixes(struct ospf_area *oa) if (src->color != INSPF) continue; - if ((src->lsa.type != LSA_T_RT) && (src->lsa.type != LSA_T_NET)) + if ((src->lsa_type != LSA_T_RT) && (src->lsa_type != LSA_T_NET)) continue; buf = px->rest; @@ -586,80 +677,13 @@ process_prefixes(struct ospf_area *oa) } } } -#endif - - -static void -ospf_rt_spfa_rtlinks(struct ospf_area *oa, struct top_hash_entry *act, struct top_hash_entry *en) -{ - // struct proto *p = &oa->po->proto; - struct proto_ospf *po = oa->po; - ip_addr prefix UNUSED; - int pxlen UNUSED, i; - - struct ospf_lsa_rt *rt = en->lsa_body; - struct ospf_lsa_rt_link *rr = (struct ospf_lsa_rt_link *) (rt + 1); - - for (i = 0; i < lsa_rt_count(&en->lsa); i++) - { - struct ospf_lsa_rt_link *rtl = rr + i; - struct top_hash_entry *tmp = NULL; - - DBG(" Working on link: %R (type: %u) ", rtl->id, rtl->type); - switch (rtl->type) - { -#ifdef OSPFv2 - case LSART_STUB: - /* - * RFC 2328 in 16.1. (2a) says to handle stub networks in an - * second phase after the SPF for an area is calculated. We get - * the same result by handing them here because add_network() - * will keep the best (not the first) found route. - */ - prefix = ipa_from_u32(rtl->id & rtl->data); - pxlen = ipa_mklen(ipa_from_u32(rtl->data)); - add_network(oa, prefix, pxlen, act->dist + rtl->metric, act, i); - break; -#endif - - case LSART_NET: -#ifdef OSPFv2 - /* In OSPFv2, rtl->id is IP addres of DR, Router ID is not known */ - tmp = ospf_hash_find_net(po->gr, oa->areaid, rtl->id); -#else /* OSPFv3 */ - tmp = ospf_hash_find(po->gr, oa->areaid, rtl->nif, rtl->id, LSA_T_NET); -#endif - break; - - case LSART_VLNK: - case LSART_PTP: - tmp = ospf_hash_find_rt(po->gr, oa->areaid, rtl->id); - break; - - default: - log("Unknown link type in router lsa. (rid = %R)", act->lsa.id); - break; - } - - if (tmp) - DBG("Going to add cand, Mydist: %u, Req: %u\n", - tmp->dist, act->dist + rtl->metric); - add_cand(&oa->cand, tmp, act, act->dist + rtl->metric, oa, i); - } -} /* RFC 2328 16.1. calculating shortest paths for an area */ static void ospf_rt_spfa(struct ospf_area *oa) { - struct proto *p = &oa->po->proto; - struct proto_ospf *po = oa->po; - struct ospf_lsa_rt *rt; - struct ospf_lsa_net *ln; - struct top_hash_entry *act, *tmp; - ip_addr prefix UNUSED; - int pxlen UNUSED; - u32 i, *rts; + struct ospf_proto *p = oa->po; + struct top_hash_entry *act; node *n; if (oa->rt == NULL) @@ -677,7 +701,7 @@ ospf_rt_spfa(struct ospf_area *oa) oa->rt->color = CANDIDATE; add_head(&oa->cand, &oa->rt->cn); DBG("RT LSA: rt: %R, id: %R, type: %u\n", - oa->rt->lsa.rt, oa->rt->lsa.id, oa->rt->lsa.type); + oa->rt->lsa.rt, oa->rt->lsa.id, oa->rt->lsa_type); while (!EMPTY_LIST(oa->cand)) { @@ -686,89 +710,36 @@ ospf_rt_spfa(struct ospf_area *oa) rem_node(n); DBG("Working on LSA: rt: %R, id: %R, type: %u\n", - act->lsa.rt, act->lsa.id, act->lsa.type); + act->lsa.rt, act->lsa.id, act->lsa_type); act->color = INSPF; - switch (act->lsa.type) + switch (act->lsa_type) { case LSA_T_RT: - rt = (struct ospf_lsa_rt *) act->lsa_body; - if (rt->options & OPT_RT_V) - oa->trcap = 1; - - /* - * In OSPFv3, all routers are added to per-area routing - * tables. But we use it just for ASBRs and ABRs. For the - * purpose of the last step in SPF - prefix-LSA processing in - * process_prefixes(), we use information stored in LSA db. - */ - if (((rt->options & OPT_RT_E) || (rt->options & OPT_RT_B)) - && (act->lsa.rt != po->router_id)) - { - orta nf = { - .type = RTS_OSPF, - .options = rt->options, - .metric1 = act->dist, - .metric2 = LSINFINITY, - .tag = 0, - .rid = act->lsa.rt, - .oa = oa, - .nhs = act->nhs - }; - ri_install_rt(oa, act->lsa.rt, &nf); - } - -#ifdef OSPFv2 - ospf_rt_spfa_rtlinks(oa, act, act); -#else /* OSPFv3 */ - /* Errata 2078 to RFC 5340 4.8.1 - skip links from non-routing nodes */ - if ((act != oa->rt) && !(rt->options & OPT_R)) - break; - - for (tmp = ospf_hash_find_rt_first(po->gr, act->domain, act->lsa.rt); - tmp; tmp = ospf_hash_find_rt_next(tmp)) - ospf_rt_spfa_rtlinks(oa, act, tmp); -#endif - + spfa_process_rt(p, oa, act); break; + case LSA_T_NET: - ln = act->lsa_body; - -#ifdef OSPFv2 - prefix = ipa_and(ipa_from_u32(act->lsa.id), ln->netmask); - pxlen = ipa_mklen(ln->netmask); - add_network(oa, prefix, pxlen, act->dist, act, -1); -#endif - - rts = (u32 *) (ln + 1); - for (i = 0; i < lsa_net_count(&act->lsa); i++) - { - DBG(" Working on router %R ", rts[i]); - tmp = ospf_hash_find_rt(po->gr, oa->areaid, rts[i]); - if (tmp != NULL) - DBG("Found :-)\n"); - else - DBG("Not found!\n"); - add_cand(&oa->cand, tmp, act, act->dist, oa, -1); - } + spfa_process_net(p, oa, act); break; + + default: + log(L_WARN "%s: Unknown LSA type in SPF: %d", p->p.name, act->lsa_type); } } -#ifdef OSPFv3 - process_prefixes(oa); -#endif + if (ospf_is_v3(p)) + spfa_process_prefixes(p, oa); } static int link_back(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par) { - u32 i, *rts; - struct ospf_lsa_net *ln; - struct ospf_lsa_rt *rt; - struct ospf_lsa_rt_link *rtl, *rr; + struct ospf_proto *p = oa->po; + struct ospf_lsa_rt_walk rtl; struct top_hash_entry *tmp; - struct proto_ospf *po = oa->po; + struct ospf_lsa_net *ln; + u32 i, cnt; if (!en || !par) return 0; @@ -783,65 +754,56 @@ link_back(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry it is set in process_prefixes() to any global addres in the area */ en->lb = IPA_NONE; -#ifdef OSPFv3 en->lb_id = 0; -#endif - switch (en->lsa.type) + + switch (en->lsa_type) { - case LSA_T_RT: - rt = (struct ospf_lsa_rt *) en->lsa_body; - rr = (struct ospf_lsa_rt_link *) (rt + 1); - for (i = 0; i < lsa_rt_count(&en->lsa); i++) + case LSA_T_RT: + lsa_walk_rt_init(p, en, &rtl); + while (lsa_walk_rt(&rtl)) + { + switch (rtl.type) { - rtl = (rr + i); - switch (rtl->type) - { - case LSART_STUB: - break; - case LSART_NET: -#ifdef OSPFv2 - /* In OSPFv2, rtl->id is IP addres of DR, Router ID is not known */ - tmp = ospf_hash_find_net(po->gr, oa->areaid, rtl->id); -#else /* OSPFv3 */ - tmp = ospf_hash_find(po->gr, oa->areaid, rtl->nif, rtl->id, LSA_T_NET); -#endif - if (tmp == par) - { -#ifdef OSPFv2 - en->lb = ipa_from_u32(rtl->data); -#else /* OSPFv3 */ - en->lb_id = rtl->lif; -#endif - return 1; - } + case LSART_STUB: + break; - break; - case LSART_VLNK: - case LSART_PTP: - /* Not necessary the same link, see RFC 2328 [23] */ - tmp = ospf_hash_find_rt(po->gr, oa->areaid, rtl->id); - if (tmp == par) - return 1; - - break; - default: - log(L_WARN "Unknown link type in router lsa. (rid = %R)", en->lsa.rt); - break; - } - } - break; - case LSA_T_NET: - ln = en->lsa_body; - rts = (u32 *) (ln + 1); - for (i = 0; i < lsa_net_count(&en->lsa); i++) - { - tmp = ospf_hash_find_rt(po->gr, oa->areaid, rts[i]); + case LSART_NET: + tmp = ospf_hash_find_net(p->gr, oa->areaid, rtl.id, rtl.nif); if (tmp == par) - return 1; + { + if (ospf_is_v2(p)) + en->lb = ipa_from_u32(rtl.data); + else + en->lb_id = rtl.lif; + + return 1; + } + break; + + case LSART_VLNK: + case LSART_PTP: + /* Not necessary the same link, see RFC 2328 [23] */ + tmp = ospf_hash_find_rt(p->gr, oa->areaid, rtl.id); + if (tmp == par) + return 1; + break; } - break; - default: - bug("Unknown lsa type %x.", en->lsa.type); + } + break; + + case LSA_T_NET: + ln = en->lsa_body; + cnt = lsa_net_count(&en->lsa); + for (i = 0; i < cnt; i++) + { + tmp = ospf_hash_find_rt(p->gr, oa->areaid, ln->routers[i]); + if (tmp == par) + return 1; + } + break; + + default: + log(L_WARN "%s: Unknown LSA type in SPF: %d", p->p.name, en->lsa_type); } return 0; } @@ -851,20 +813,20 @@ link_back(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry static void ospf_rt_sum(struct ospf_area *oa) { - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; + struct ospf_proto *p = oa->po; struct top_hash_entry *en; - ip_addr ip = IPA_NONE; - u32 dst_rid = 0; - u32 metric, options; + ip_addr ip, abrip; + u32 dst_rid, metric, options; ort *abr; int pxlen = -1, type = -1; + u8 pxopts; + OSPF_TRACE(D_EVENTS, "Starting routing table calculation for inter-area (area %R)", oa->areaid); - WALK_SLIST(en, po->lsal) + WALK_SLIST(en, p->lsal) { - if ((en->lsa.type != LSA_T_SUM_RT) && (en->lsa.type != LSA_T_SUM_NET)) + if ((en->lsa_type != LSA_T_SUM_RT) && (en->lsa_type != LSA_T_SUM_NET)) continue; if (en->domain != oa->areaid) @@ -875,55 +837,36 @@ ospf_rt_sum(struct ospf_area *oa) continue; /* 16.2. (2) */ - if (en->lsa.rt == po->router_id) + if (en->lsa.rt == p->router_id) continue; /* 16.2. (3) is handled later in ospf_rt_abr() by resetting such rt entry */ - if (en->lsa.type == LSA_T_SUM_NET) + if (en->lsa_type == LSA_T_SUM_NET) { -#ifdef OSPFv2 - struct ospf_lsa_sum *ls = en->lsa_body; - ip = ipa_and(ipa_from_u32(en->lsa.id), ls->netmask); - pxlen = ipa_mklen(ls->netmask); -#else /* OSPFv3 */ - u8 pxopts; - u16 rest; - struct ospf_lsa_sum_net *ls = en->lsa_body; - lsa_get_ipv6_prefix(ls->prefix, &ip, &pxlen, &pxopts, &rest); + lsa_parse_sum_net(en, ospf_is_v2(p), &ip, &pxlen, &pxopts, &metric); if (pxopts & OPT_PX_NU) continue; -#endif if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH) { log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)", - p->name, en->lsa.type, en->lsa.id, en->lsa.rt); + p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt); continue; } - metric = ls->metric & METRIC_MASK; options = 0; type = ORT_NET; } else /* LSA_T_SUM_RT */ { -#ifdef OSPFv2 - struct ospf_lsa_sum *ls = en->lsa_body; - dst_rid = en->lsa.id; - options = 0; -#else /* OSPFv3 */ - struct ospf_lsa_sum_rt *ls = en->lsa_body; - dst_rid = ls->drid; - options = ls->options & OPTIONS_MASK; -#endif + lsa_parse_sum_rt(en, ospf_is_v2(p), &dst_rid, &metric, &options); /* We don't want local router in ASBR routing table */ - if (dst_rid == po->router_id) + if (dst_rid == p->router_id) continue; - metric = ls->metric & METRIC_MASK; options |= ORTA_ASBR; type = ORT_ROUTER; } @@ -933,7 +876,7 @@ ospf_rt_sum(struct ospf_area *oa) continue; /* 16.2. (4) */ - ip_addr abrip = ipa_from_rid(en->lsa.rt); + abrip = ipa_from_rid(en->lsa.rt); abr = (ort *) fib_find(&oa->rtr, &abrip, MAX_PREFIX_LENGTH); if (!abr || !abr->n.type) continue; @@ -958,7 +901,7 @@ ospf_rt_sum(struct ospf_area *oa) }; if (type == ORT_NET) - ri_install_net(po, ip, pxlen, &nf); + ri_install_net(p, ip, pxlen, &nf); else ri_install_rt(oa, dst_rid, &nf); } @@ -968,20 +911,22 @@ ospf_rt_sum(struct ospf_area *oa) static void ospf_rt_sum_tr(struct ospf_area *oa) { - struct proto *p = &oa->po->proto; - struct proto_ospf *po = oa->po; - struct ospf_area *bb = po->backbone; - ip_addr abrip; + struct ospf_proto *p = oa->po; + struct ospf_area *bb = p->backbone; struct top_hash_entry *en; - u32 dst_rid, metric; - ort *re = NULL, *abr; + ort *re, *abr; + ip_addr ip, abrip; + u32 dst_rid, metric, options; + int pxlen; + u8 pxopts; - if (!bb) return; + if (!bb) + return; - WALK_SLIST(en, po->lsal) + WALK_SLIST(en, p->lsal) { - if ((en->lsa.type != LSA_T_SUM_RT) && (en->lsa.type != LSA_T_SUM_NET)) + if ((en->lsa_type != LSA_T_SUM_RT) && (en->lsa_type != LSA_T_SUM_NET)) continue; if (en->domain != oa->areaid) @@ -992,49 +937,30 @@ ospf_rt_sum_tr(struct ospf_area *oa) continue; /* 16.3 (2) */ - if (en->lsa.rt == po->router_id) + if (en->lsa.rt == p->router_id) continue; - if (en->lsa.type == LSA_T_SUM_NET) + if (en->lsa_type == LSA_T_SUM_NET) { - ip_addr ip; - int pxlen; -#ifdef OSPFv2 - struct ospf_lsa_sum *ls = en->lsa_body; - ip = ipa_and(ipa_from_u32(en->lsa.id), ls->netmask); - pxlen = ipa_mklen(ls->netmask); -#else /* OSPFv3 */ - u8 pxopts; - u16 rest; - struct ospf_lsa_sum_net *ls = en->lsa_body; - lsa_get_ipv6_prefix(ls->prefix, &ip, &pxlen, &pxopts, &rest); + lsa_parse_sum_net(en, ospf_is_v2(p), &ip, &pxlen, &pxopts, &metric); if (pxopts & OPT_PX_NU) continue; -#endif if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH) { log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)", - p->name, en->lsa.type, en->lsa.id, en->lsa.rt); + p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt); continue; } - metric = ls->metric & METRIC_MASK; - re = fib_find(&po->rtf, &ip, pxlen); + re = fib_find(&p->rtf, &ip, pxlen); } - else // en->lsa.type == LSA_T_SUM_RT + else // en->lsa_type == LSA_T_SUM_RT { -#ifdef OSPFv2 - struct ospf_lsa_sum *ls = en->lsa_body; - dst_rid = en->lsa.id; -#else /* OSPFv3 */ - struct ospf_lsa_sum_rt *ls = en->lsa_body; - dst_rid = ls->drid; -#endif + lsa_parse_sum_rt(en, ospf_is_v2(p), &dst_rid, &metric, &options); - metric = ls->metric & METRIC_MASK; - ip_addr ip = ipa_from_rid(dst_rid); + ip = ipa_from_rid(dst_rid); re = fib_find(&bb->rtr, &ip, MAX_PREFIX_LENGTH); } @@ -1156,20 +1082,20 @@ decide_sum_lsa(struct ospf_area *oa, ort *nf, int dest) /* RFC 2328 16.7. p1 - originate or flush summary LSAs */ static inline void -check_sum_net_lsa(struct proto_ospf *po, ort *nf) +check_sum_net_lsa(struct ospf_proto *p, ort *nf) { struct area_net *anet = NULL; struct ospf_area *anet_oa = NULL; /* RT entry marked as area network */ - if (nf->fn.x0) + if (nf->fn.flags & OSPF_RT_PERSISTENT) { /* It is a default route for stub areas, handled entirely in ospf_rt_abr() */ if (nf->fn.pxlen == 0) return; /* Find that area network */ - WALK_LIST(anet_oa, po->area_list) + WALK_LIST(anet_oa, p->area_list) { anet = (struct area_net *) fib_find(&anet_oa->net_fib, &nf->fn.prefix, nf->fn.pxlen); if (anet) @@ -1178,36 +1104,29 @@ check_sum_net_lsa(struct proto_ospf *po, ort *nf) } struct ospf_area *oa; - WALK_LIST(oa, po->area_list) + WALK_LIST(oa, p->area_list) { if (anet && decide_anet_lsa(oa, anet, anet_oa)) - originate_sum_net_lsa(oa, &nf->fn, anet->metric); + ospf_originate_sum_net_lsa(p, oa, nf, anet->metric); else if (decide_sum_lsa(oa, nf, ORT_NET)) - originate_sum_net_lsa(oa, &nf->fn, nf->n.metric1); - else - flush_sum_lsa(oa, &nf->fn, ORT_NET); + ospf_originate_sum_net_lsa(p, oa, nf, nf->n.metric1); } } static inline void -check_sum_rt_lsa(struct proto_ospf *po, ort *nf) +check_sum_rt_lsa(struct ospf_proto *p, ort *nf) { struct ospf_area *oa; - WALK_LIST(oa, po->area_list) - { + WALK_LIST(oa, p->area_list) if (decide_sum_lsa(oa, nf, ORT_ROUTER)) - originate_sum_rt_lsa(oa, &nf->fn, nf->n.metric1, nf->n.options); - else - flush_sum_lsa(oa, &nf->fn, ORT_ROUTER); - } + ospf_originate_sum_rt_lsa(p, oa, nf, nf->n.metric1, nf->n.options); } static inline int -decide_nssa_lsa(ort *nf, u32 *rt_metric, ip_addr *rt_fwaddr, u32 *rt_tag) +decide_nssa_lsa(struct ospf_proto *p, ort *nf, struct ospf_lsa_ext_local *rt) { struct ospf_area *oa = nf->n.oa; struct top_hash_entry *en = nf->n.en; - int propagate; if (!rt_is_nssa(nf) || !oa->translate) return 0; @@ -1216,66 +1135,40 @@ decide_nssa_lsa(ort *nf, u32 *rt_metric, ip_addr *rt_fwaddr, u32 *rt_tag) if (fib_route(&oa->enet_fib, nf->fn.prefix, nf->fn.pxlen)) return 0; - if (!en || (en->lsa.type != LSA_T_NSSA)) + if (!en || (en->lsa_type != LSA_T_NSSA)) return 0; /* We do not store needed data in struct orta, we have to parse the LSA */ - struct ospf_lsa_ext *le = en->lsa_body; - -#ifdef OSPFv2 - *rt_fwaddr = le->fwaddr; - *rt_tag = le->tag; - propagate = en->lsa.options & OPT_P; -#else /* OSPFv3 */ - u32 *buf = le->rest; - u8 pxlen = (*buf >> 24); - u8 pxopts = (*buf >> 16); - buf += IPV6_PREFIX_WORDS(pxlen); /* Skip the IP prefix */ - - if (pxopts & OPT_PX_NU) + lsa_parse_ext(en, ospf_is_v2(p), rt); + + if (rt->pxopts & OPT_PX_NU) return 0; - if (le->metric & LSA_EXT_FBIT) - buf = lsa_get_ipv6_addr(buf, rt_fwaddr); - else - *rt_fwaddr = IPA_NONE; - - if (le->metric & LSA_EXT_TBIT) - *rt_tag = *buf++; - else - *rt_tag = 0; - - propagate = pxopts & OPT_PX_P; -#endif - - if (!propagate || ipa_zero(*rt_fwaddr)) + if (!rt->propagate || ipa_zero(rt->fwaddr)) return 0; - *rt_metric = le->metric & (METRIC_MASK | LSA_EXT_EBIT); return 1; } /* RFC 3103 3.2 - translating Type-7 LSAs into Type-5 LSAs */ static inline void -check_nssa_lsa(struct proto_ospf *po, ort *nf) +check_nssa_lsa(struct ospf_proto *p, ort *nf) { - struct fib_node *fn = &nf->fn; struct area_net *anet = NULL; struct ospf_area *oa = NULL; - u32 rt_metric, rt_tag; - ip_addr rt_fwaddr; + struct ospf_lsa_ext_local rt; /* Do not translate LSA if there is already the external LSA from route export */ - if (fn->x1 == EXT_EXPORT) + if (nf->external_rte) return; /* RT entry marked as area network */ - if (fn->x0) + if (nf->fn.flags & OSPF_RT_PERSISTENT) { /* Find that area network */ - WALK_LIST(oa, po->area_list) + WALK_LIST(oa, p->area_list) { - anet = (struct area_net *) fib_find(&oa->enet_fib, &fn->prefix, fn->pxlen); + anet = (struct area_net *) fib_find(&oa->enet_fib, &nf->fn.prefix, nf->fn.pxlen); if (anet) break; } @@ -1283,33 +1176,29 @@ check_nssa_lsa(struct proto_ospf *po, ort *nf) /* RFC 3103 3.2 (3) - originate the aggregated address range */ if (anet && anet->active && !anet->hidden && oa->translate) - originate_ext_lsa(po->backbone, fn, EXT_NSSA, anet->metric, IPA_NONE, anet->tag, 0); + ospf_originate_ext_lsa(p, NULL, nf, LSA_RTCALC, anet->metric, + (anet->metric & LSA_EXT3_EBIT), IPA_NONE, anet->tag, 0); /* RFC 3103 3.2 (2) - originate the same network */ - else if (decide_nssa_lsa(nf, &rt_metric, &rt_fwaddr, &rt_tag)) - originate_ext_lsa(po->backbone, fn, EXT_NSSA, rt_metric, rt_fwaddr, rt_tag, 0); - - else if (fn->x1 == EXT_NSSA) - flush_ext_lsa(po->backbone, fn, 0); + else if (decide_nssa_lsa(p, nf, &rt)) + ospf_originate_ext_lsa(p, NULL, nf, LSA_RTCALC, rt.metric, rt.ebit, rt.fwaddr, rt.tag, 0); } /* RFC 2328 16.7. p2 - find new/lost vlink endpoints */ static void -ospf_check_vlinks(struct proto_ospf *po) +ospf_check_vlinks(struct ospf_proto *p) { - struct proto *p = &po->proto; - struct ospf_iface *ifa; - WALK_LIST(ifa, po->iface_list) + WALK_LIST(ifa, p->iface_list) { if (ifa->type == OSPF_IT_VLINK) { struct top_hash_entry *tmp; - tmp = ospf_hash_find_rt(po->gr, ifa->voa->areaid, ifa->vid); + tmp = ospf_hash_find_rt(p->gr, ifa->voa->areaid, ifa->vid); if (tmp && (tmp->color == INSPF) && ipa_nonzero(tmp->lb) && tmp->nhs) { - struct ospf_iface *nhi = ospf_iface_find(po, tmp->nhs->iface); + struct ospf_iface *nhi = ospf_iface_find(p, tmp->nhs->iface); if ((ifa->state != OSPF_IS_PTP) || (ifa->vifa != nhi) @@ -1326,7 +1215,9 @@ ospf_check_vlinks(struct proto_ospf *po) else if ((ifa->state == OSPF_IS_PTP) && (ifa->cost != tmp->dist)) { ifa->cost = tmp->dist; - schedule_rt_lsa(po->backbone); + + /* RFC 2328 12.4 Event 8 - vlink state change */ + ospf_notify_rt_lsa(ifa->oa); } } else @@ -1344,13 +1235,13 @@ ospf_check_vlinks(struct proto_ospf *po) /* Miscellaneous route processing that needs to be done by ABRs */ static void -ospf_rt_abr1(struct proto_ospf *po) +ospf_rt_abr1(struct ospf_proto *p) { struct area_net *anet; ort *nf, *default_nf; /* RFC 2328 G.3 - incomplete resolution of virtual next hops - routers */ - FIB_WALK(&po->backbone->rtr, nftmp) + FIB_WALK(&p->backbone->rtr, nftmp) { nf = (ort *) nftmp; @@ -1360,7 +1251,7 @@ ospf_rt_abr1(struct proto_ospf *po) FIB_WALK_END; - FIB_WALK(&po->rtf, nftmp) + FIB_WALK(&p->rtf, nftmp) { nf = (ort *) nftmp; @@ -1381,8 +1272,8 @@ ospf_rt_abr1(struct proto_ospf *po) anet->active = 1; /* Get a RT entry and mark it to know that it is an area network */ - ort *nfi = (ort *) fib_get(&po->rtf, &anet->fn.prefix, anet->fn.pxlen); - nfi->fn.x0 = 1; /* mark and keep persistent, to have stable UID */ + ort *nfi = (ort *) fib_get(&p->rtf, &anet->fn.prefix, anet->fn.pxlen); + nfi->fn.flags |= OSPF_RT_PERSISTENT; /* mark persistent, to have stable UID */ /* 16.2. (3) */ if (nfi->n.type == RTS_OSPF_IA) @@ -1397,18 +1288,16 @@ ospf_rt_abr1(struct proto_ospf *po) FIB_WALK_END; ip_addr addr = IPA_NONE; - default_nf = (ort *) fib_get(&po->rtf, &addr, 0); - default_nf->fn.x0 = 1; /* keep persistent */ + default_nf = (ort *) fib_get(&p->rtf, &addr, 0); + default_nf->fn.flags |= OSPF_RT_PERSISTENT; /* keep persistent */ struct ospf_area *oa; - WALK_LIST(oa, po->area_list) + WALK_LIST(oa, p->area_list) { /* 12.4.3.1. - originate or flush default route for stub/NSSA areas */ if (oa_is_stub(oa) || (oa_is_nssa(oa) && !oa->ac->summary)) - originate_sum_net_lsa(oa, &default_nf->fn, oa->ac->default_cost); - else - flush_sum_lsa(oa, &default_nf->fn, ORT_NET); + ospf_originate_sum_net_lsa(p, oa, default_nf, oa->ac->default_cost); /* * Originate type-7 default route for NSSA areas @@ -1420,10 +1309,8 @@ ospf_rt_abr1(struct proto_ospf *po) */ if (oa_is_nssa(oa) && oa->ac->default_nssa) - originate_ext_lsa(oa, &default_nf->fn, 0, oa->ac->default_cost, IPA_NONE, 0, 0); - else - flush_ext_lsa(oa, &default_nf->fn, 1); - + ospf_originate_ext_lsa(p, oa, default_nf, LSA_RTCALC, oa->ac->default_cost, + (oa->ac->default_cost & LSA_EXT3_EBIT), IPA_NONE, 0, 0); /* RFC 2328 16.4. (3) - precompute preferred ASBR entries */ if (oa_is_ext(oa)) @@ -1432,7 +1319,7 @@ ospf_rt_abr1(struct proto_ospf *po) { nf = (ort *) nftmp; if (nf->n.options & ORTA_ASBR) - ri_install_asbr(po, &nf->fn.prefix, &nf->n); + ri_install_asbr(p, &nf->fn.prefix, &nf->n); } FIB_WALK_END; } @@ -1440,15 +1327,15 @@ ospf_rt_abr1(struct proto_ospf *po) /* Originate or flush ASBR summary LSAs */ - FIB_WALK(&po->backbone->rtr, nftmp) + FIB_WALK(&p->backbone->rtr, nftmp) { - check_sum_rt_lsa(po, (ort *) nftmp); + check_sum_rt_lsa(p, (ort *) nftmp); } FIB_WALK_END; /* RFC 2328 16.7. p2 - find new/lost vlink endpoints */ - ospf_check_vlinks(po); + ospf_check_vlinks(p); } @@ -1465,7 +1352,7 @@ translator_timer_hook(timer *timer) } static void -ospf_rt_abr2(struct proto_ospf *po) +ospf_rt_abr2(struct ospf_proto *p) { struct ospf_area *oa; struct top_hash_entry *en; @@ -1473,8 +1360,8 @@ ospf_rt_abr2(struct proto_ospf *po) /* RFC 3103 3.1 - type-7 translator election */ - struct ospf_area *bb = po->backbone; - WALK_LIST(oa, po->area_list) + struct ospf_area *bb = p->backbone; + WALK_LIST(oa, p->area_list) if (oa_is_nssa(oa)) { int translate = 1; @@ -1492,13 +1379,13 @@ ospf_rt_abr2(struct proto_ospf *po) if (!nf2 || !nf2->n.type || !(nf2->n.options & ORTA_ABR)) continue; - en = ospf_hash_find_rt(po->gr, oa->areaid, nf->n.rid); + en = ospf_hash_find_rt(p->gr, oa->areaid, nf->n.rid); if (!en || (en->color != INSPF)) continue; struct ospf_lsa_rt *rt = en->lsa_body; /* There is better candidate - Nt-bit or higher Router ID */ - if ((rt->options & OPT_RT_NT) || (po->router_id < nf->n.rid)) + if ((rt->options & OPT_RT_NT) || (p->router_id < nf->n.rid)) { translate = 0; goto decided; @@ -1518,7 +1405,7 @@ ospf_rt_abr2(struct proto_ospf *po) if (!translate && (oa->translate == TRANS_ON)) { if (oa->translator_timer == NULL) - oa->translator_timer = tm_new_set(po->proto.pool, translator_timer_hook, oa, 0, 0); + oa->translator_timer = tm_new_set(p->p.pool, translator_timer_hook, oa, 0, 0); /* Schedule the end of translation */ tm_start(oa->translator_timer, oa->ac->transint); @@ -1528,7 +1415,7 @@ ospf_rt_abr2(struct proto_ospf *po) /* Compute condensed external networks */ - FIB_WALK(&po->rtf, nftmp) + FIB_WALK(&p->rtf, nftmp) { nf = (ort *) nftmp; if (rt_is_nssa(nf) && (nf->n.options & ORTA_PROP)) @@ -1543,12 +1430,12 @@ ospf_rt_abr2(struct proto_ospf *po) anet->active = 1; /* Get a RT entry and mark it to know that it is an area network */ - nf2 = (ort *) fib_get(&po->rtf, &anet->fn.prefix, anet->fn.pxlen); - nf2->fn.x0 = 1; + nf2 = (ort *) fib_get(&p->rtf, &anet->fn.prefix, anet->fn.pxlen); + nf2->fn.flags |= OSPF_RT_PERSISTENT; /* keep persistent */ } u32 metric = (nf->n.type == RTS_OSPF_EXT1) ? - nf->n.metric1 : ((nf->n.metric2 + 1) | LSA_EXT_EBIT); + nf->n.metric1 : ((nf->n.metric2 + 1) | LSA_EXT3_EBIT); if (anet->metric < metric) anet->metric = metric; @@ -1558,12 +1445,12 @@ ospf_rt_abr2(struct proto_ospf *po) FIB_WALK_END; - FIB_WALK(&po->rtf, nftmp) + FIB_WALK(&p->rtf, nftmp) { nf = (ort *) nftmp; - check_sum_net_lsa(po, nf); - check_nssa_lsa(po, nf); + check_sum_net_lsa(p, nf); + check_nssa_lsa(p, nf); } FIB_WALK_END; } @@ -1589,78 +1476,46 @@ ospf_fib_route(struct fib *f, ip_addr a, int len) /* RFC 2328 16.4. calculating external routes */ static void -ospf_ext_spf(struct proto_ospf *po) +ospf_ext_spf(struct ospf_proto *p) { + struct top_hash_entry *en; + struct ospf_lsa_ext_local rt; ort *nf1, *nf2; orta nfa = {}; - struct top_hash_entry *en; - struct proto *p = &po->proto; - struct ospf_lsa_ext *le; - int pxlen, ebit, rt_fwaddr_valid, rt_propagate; - ip_addr ip, rtid, rt_fwaddr; - u32 br_metric, rt_metric, rt_tag; + ip_addr rtid; + u32 br_metric; struct ospf_area *atmp; OSPF_TRACE(D_EVENTS, "Starting routing table calculation for ext routes"); - WALK_SLIST(en, po->lsal) + WALK_SLIST(en, p->lsal) { /* 16.4. (1) */ - if ((en->lsa.type != LSA_T_EXT) && (en->lsa.type != LSA_T_NSSA)) + if ((en->lsa_type != LSA_T_EXT) && (en->lsa_type != LSA_T_NSSA)) continue; if (en->lsa.age == LSA_MAXAGE) continue; /* 16.4. (2) */ - if (en->lsa.rt == po->router_id) + if (en->lsa.rt == p->router_id) continue; DBG("%s: Working on LSA. ID: %R, RT: %R, Type: %u\n", - p->name, en->lsa.id, en->lsa.rt, en->lsa.type); + p->p.name, en->lsa.id, en->lsa.rt, en->lsa_type); - le = en->lsa_body; + lsa_parse_ext(en, ospf_is_v2(p), &rt); - rt_metric = le->metric & METRIC_MASK; - ebit = le->metric & LSA_EXT_EBIT; - - if (rt_metric == LSINFINITY) + if (rt.metric == LSINFINITY) continue; -#ifdef OSPFv2 - ip = ipa_and(ipa_from_u32(en->lsa.id), le->netmask); - pxlen = ipa_mklen(le->netmask); - rt_fwaddr = le->fwaddr; - rt_fwaddr_valid = !ipa_equal(rt_fwaddr, IPA_NONE); - rt_tag = le->tag; - rt_propagate = en->lsa.options & OPT_P; -#else /* OSPFv3 */ - u8 pxopts; - u16 rest; - u32 *buf = le->rest; - buf = lsa_get_ipv6_prefix(buf, &ip, &pxlen, &pxopts, &rest); - - if (pxopts & OPT_PX_NU) + if (rt.pxopts & OPT_PX_NU) continue; - rt_fwaddr_valid = le->metric & LSA_EXT_FBIT; - if (rt_fwaddr_valid) - buf = lsa_get_ipv6_addr(buf, &rt_fwaddr); - else - rt_fwaddr = IPA_NONE; - - if (le->metric & LSA_EXT_TBIT) - rt_tag = *buf++; - else - rt_tag = 0; - - rt_propagate = pxopts & OPT_PX_P; -#endif - - if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH) + if (rt.pxlen < 0 || rt.pxlen > MAX_PREFIX_LENGTH) { log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)", - p->name, en->lsa.type, en->lsa.id, en->lsa.rt); + p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt); continue; } @@ -1669,10 +1524,10 @@ ospf_ext_spf(struct proto_ospf *po) /* If there are more areas, we already precomputed preferred ASBR entries in ospf_rt_abr1() and stored them in the backbone table. For NSSA, we examine the area to which the LSA is assigned */ - if (en->lsa.type == LSA_T_EXT) - atmp = ospf_main_area(po); + if (en->lsa_type == LSA_T_EXT) + atmp = ospf_main_area(p); else /* NSSA */ - atmp = ospf_find_area(po, en->domain); + atmp = ospf_find_area(p, en->domain); if (!atmp) continue; /* Should not happen */ @@ -1688,11 +1543,11 @@ ospf_ext_spf(struct proto_ospf *po) /* 16.4. (3) NSSA - special rule for default routes */ /* ABR should use default only if P-bit is set and summaries are active */ - if ((en->lsa.type == LSA_T_NSSA) && ipa_zero(ip) && (pxlen == 0) && - (po->areano > 1) && !(rt_propagate && atmp->ac->summary)) + if ((en->lsa_type == LSA_T_NSSA) && ipa_zero(rt.ip) && (rt.pxlen == 0) && + (p->areano > 1) && !(rt.propagate && atmp->ac->summary)) continue; - if (!rt_fwaddr_valid) + if (!rt.fbit) { nf2 = nf1; nfa.nhs = nf1->n.nhs; @@ -1700,11 +1555,11 @@ ospf_ext_spf(struct proto_ospf *po) } else { - nf2 = ospf_fib_route(&po->rtf, rt_fwaddr, MAX_PREFIX_LENGTH); + nf2 = ospf_fib_route(&p->rtf, rt.fwaddr, MAX_PREFIX_LENGTH); if (!nf2) continue; - if (en->lsa.type == LSA_T_EXT) + if (en->lsa_type == LSA_T_EXT) { /* For ext routes, we accept intra-area or inter-area routes */ if ((nf2->n.type != RTS_OSPF) && (nf2->n.type != RTS_OSPF_IA)) @@ -1727,21 +1582,21 @@ ospf_ext_spf(struct proto_ospf *po) /* Replace device nexthops with nexthops to forwarding address from LSA */ if (has_device_nexthops(nfa.nhs)) { - nfa.nhs = fix_device_nexthops(po, nfa.nhs, rt_fwaddr); + nfa.nhs = fix_device_nexthops(p, nfa.nhs, rt.fwaddr); nfa.nhs_reuse = 1; } } - if (ebit) + if (rt.ebit) { nfa.type = RTS_OSPF_EXT2; nfa.metric1 = br_metric; - nfa.metric2 = rt_metric; + nfa.metric2 = rt.metric; } else { nfa.type = RTS_OSPF_EXT1; - nfa.metric1 = br_metric + rt_metric; + nfa.metric1 = br_metric + rt.metric; nfa.metric2 = LSINFINITY; } @@ -1750,25 +1605,25 @@ ospf_ext_spf(struct proto_ospf *po) /* Whether the route is preferred in route selection according to 16.4.1 */ nfa.options = epath_preferred(&nf2->n) ? ORTA_PREF : 0; - if (en->lsa.type == LSA_T_NSSA) + if (en->lsa_type == LSA_T_NSSA) { nfa.options |= ORTA_NSSA; - if (rt_propagate) + if (rt.propagate) nfa.options |= ORTA_PROP; } - nfa.tag = rt_tag; + nfa.tag = rt.tag; nfa.rid = en->lsa.rt; nfa.oa = atmp; /* undefined in RFC 2328 */ nfa.en = en; /* store LSA for later (NSSA processing) */ - ri_install_ext(po, ip, pxlen, &nfa); + ri_install_ext(p, rt.ip, rt.pxlen, &nfa); } } /* Cleanup of routing tables and data */ void -ospf_rt_reset(struct proto_ospf *po) +ospf_rt_reset(struct ospf_proto *p) { struct ospf_area *oa; struct top_hash_entry *en; @@ -1776,24 +1631,27 @@ ospf_rt_reset(struct proto_ospf *po) ort *ri; /* Reset old routing table */ - FIB_WALK(&po->rtf, nftmp) + FIB_WALK(&p->rtf, nftmp) { ri = (ort *) nftmp; - ri->fn.x0 = 0; + ri->fn.flags &= ~OSPF_RT_PERSISTENT; reset_ri(ri); } FIB_WALK_END; /* Reset SPF data in LSA db */ - WALK_SLIST(en, po->lsal) + WALK_SLIST(en, p->lsal) { en->color = OUTSPF; en->dist = LSINFINITY; en->nhs = NULL; en->lb = IPA_NONE; + + if (en->rtcalc == LSA_RTCALC) + en->rtcalc = LSA_STALE; } - WALK_LIST(oa, po->area_list) + WALK_LIST(oa, p->area_list) { /* Reset ASBR routing tables */ FIB_WALK(&oa->rtr, nftmp) @@ -1804,7 +1662,7 @@ ospf_rt_reset(struct proto_ospf *po) FIB_WALK_END; /* Reset condensed area networks */ - if (po->areano > 1) + if (p->areano > 1) { FIB_WALK(&oa->net_fib, nftmp) { @@ -1825,53 +1683,57 @@ ospf_rt_reset(struct proto_ospf *po) } } +static void +ospf_flush_stale(struct ospf_proto *p) +{ +} + /** * ospf_rt_spf - calculate internal routes - * @po: OSPF protocol + * @p: OSPF protocol * * Calculation of internal paths in an area is described in 16.1 of RFC 2328. * It's based on Dijkstra's shortest path tree algorithms. * This function is invoked from ospf_disp(). */ void -ospf_rt_spf(struct proto_ospf *po) +ospf_rt_spf(struct ospf_proto *p) { - struct proto *p = &po->proto; struct ospf_area *oa; - if (po->areano == 0) + if (p->areano == 0) return; OSPF_TRACE(D_EVENTS, "Starting routing table calculation"); /* 16. (1) */ - ospf_rt_reset(po); + ospf_rt_reset(p); /* 16. (2) */ - WALK_LIST(oa, po->area_list) + WALK_LIST(oa, p->area_list) ospf_rt_spfa(oa); /* 16. (3) */ - ospf_rt_sum(ospf_main_area(po)); + ospf_rt_sum(ospf_main_area(p)); /* 16. (4) */ - WALK_LIST(oa, po->area_list) + WALK_LIST(oa, p->area_list) if (oa->trcap && (oa->areaid != 0)) ospf_rt_sum_tr(oa); - if (po->areano > 1) - ospf_rt_abr1(po); + if (p->areano > 1) + ospf_rt_abr1(p); /* 16. (5) */ - ospf_ext_spf(po); + ospf_ext_spf(p); - if (po->areano > 1) - ospf_rt_abr2(po); + if (p->areano > 1) + ospf_rt_abr2(p); - rt_sync(po); - lp_flush(po->nhpool); + rt_sync(p); + lp_flush(p->nhpool); - po->calcrt = 0; + p->calcrt = 0; } @@ -1886,15 +1748,14 @@ static struct mpnh * calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par, int pos) { - // struct proto *p = &oa->po->proto; - struct proto_ospf *po = oa->po; + struct ospf_proto *p = oa->po; struct mpnh *pn = par->nhs; struct ospf_iface *ifa; u32 rid = en->lsa.rt; /* 16.1.1. The next hop calculation */ DBG(" Next hop calculating for id: %R rt: %R type: %u\n", - en->lsa.id, en->lsa.rt, en->lsa.type); + en->lsa.id, en->lsa.rt, en->lsa_type); /* Usually, we inherit parent nexthops */ if (inherit_nexthops(pn)) @@ -1908,67 +1769,69 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, */ /* The first case - local network */ - if ((en->lsa.type == LSA_T_NET) && (par == oa->rt)) + if ((en->lsa_type == LSA_T_NET) && (par == oa->rt)) { ifa = rt_pos_to_ifa(oa, pos); if (!ifa) return NULL; - return new_nexthop(po, IPA_NONE, ifa->iface, ifa->ecmp_weight); + return new_nexthop(p, IPA_NONE, ifa->iface, ifa->ecmp_weight); } /* The second case - ptp or ptmp neighbor */ - if ((en->lsa.type == LSA_T_RT) && (par == oa->rt)) + if ((en->lsa_type == LSA_T_RT) && (par == oa->rt)) { ifa = rt_pos_to_ifa(oa, pos); if (!ifa) return NULL; if (ifa->type == OSPF_IT_VLINK) - return new_nexthop(po, IPA_NONE, NULL, 0); + return new_nexthop(p, IPA_NONE, NULL, 0); struct ospf_neighbor *m = find_neigh(ifa, rid); if (!m || (m->state != NEIGHBOR_FULL)) return NULL; - return new_nexthop(po, m->ip, ifa->iface, ifa->ecmp_weight); + return new_nexthop(p, m->ip, ifa->iface, ifa->ecmp_weight); } /* The third case - bcast or nbma neighbor */ - if ((en->lsa.type == LSA_T_RT) && (par->lsa.type == LSA_T_NET)) + if ((en->lsa_type == LSA_T_RT) && (par->lsa_type == LSA_T_NET)) { /* par->nhi should be defined from parent's calc_next_hop() */ if (!pn) goto bad; -#ifdef OSPFv2 - /* - * In this case, next-hop is the same as link-back, which is - * already computed in link_back(). - */ - if (ipa_zero(en->lb)) - goto bad; + if (ospf_is_v2(p)) + { + /* + * In this case, next-hop is the same as link-back, which is + * already computed in link_back(). + */ + if (ipa_zero(en->lb)) + goto bad; - return new_nexthop(po, en->lb, pn->iface, pn->weight); + return new_nexthop(p, en->lb, pn->iface, pn->weight); + } + else /* OSPFv3 */ + { + /* + * Next-hop is taken from lladdr field of Link-LSA, en->lb_id + * is computed in link_back(). + */ + struct top_hash_entry *lhe; + lhe = ospf_hash_find(p->gr, pn->iface->index, en->lb_id, rid, LSA_T_LINK); -#else /* OSPFv3 */ - /* - * Next-hop is taken from lladdr field of Link-LSA, en->lb_id - * is computed in link_back(). - */ - struct top_hash_entry *lhe; - lhe = ospf_hash_find(po->gr, pn->iface->index, en->lb_id, rid, LSA_T_LINK); + if (!lhe) + return NULL; - if (!lhe) - return NULL; - - struct ospf_lsa_link *llsa = lhe->lsa_body; + struct ospf_lsa_link *llsa = lhe->lsa_body; - if (ipa_zero(llsa->lladdr)) - return NULL; + if (ipa_zero(llsa->lladdr)) + return NULL; - return new_nexthop(po, llsa->lladdr, pn->iface, pn->weight); -#endif + return new_nexthop(p, llsa->lladdr, pn->iface, pn->weight); + } } bad: @@ -1983,7 +1846,7 @@ static void add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, u32 dist, struct ospf_area *oa, int pos) { - struct proto_ospf *po = oa->po; + struct ospf_proto *p = oa->po; node *prev, *n; int added = 0; struct top_hash_entry *act; @@ -1994,14 +1857,13 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, if (en->lsa.age == LSA_MAXAGE) return; -#ifdef OSPFv3 - if (en->lsa.type == LSA_T_RT) - { - struct ospf_lsa_rt *rt = en->lsa_body; - if (!(rt->options & OPT_V6)) - return; - } -#endif + if (ospf_is_v3(p) && (en->lsa_type == LSA_T_RT)) + { + /* In OSPFv3, check V6 flag */ + struct ospf_lsa_rt *rt = en->lsa_body; + if (!(rt->options & OPT_V6)) + return; + } /* 16.1. (2c) */ if (en->color == INSPF) @@ -2019,7 +1881,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, if (!nhs) { log(L_WARN "Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)", - en->lsa.type, en->lsa.id, en->lsa.rt); + en->lsa_type, en->lsa.id, en->lsa.rt); return; } @@ -2042,18 +1904,18 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, */ /* Keep old ones */ - if (!po->ecmp || nh_is_vlink(nhs) || (nhs == en->nhs)) + if (!p->ecmp || nh_is_vlink(nhs) || (nhs == en->nhs)) return; /* Merge old and new */ int new_reuse = (par->nhs != nhs); - en->nhs = merge_nexthops(po, en->nhs, nhs, en->nhs_reuse, new_reuse); + en->nhs = merge_nexthops(p, en->nhs, nhs, en->nhs_reuse, new_reuse); en->nhs_reuse = 1; return; } DBG(" Adding candidate: rt: %R, id: %R, type: %u\n", - en->lsa.rt, en->lsa.id, en->lsa.type); + en->lsa.rt, en->lsa.id, en->lsa_type); if (en->color == CANDIDATE) { /* We found a shorter path */ @@ -2076,7 +1938,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, { act = SKIP_BACK(struct top_hash_entry, cn, n); if ((act->dist > dist) || - ((act->dist == dist) && (act->lsa.type == LSA_T_RT))) + ((act->dist == dist) && (act->lsa_type == LSA_T_RT))) { if (prev == NULL) add_head(l, &en->cn); @@ -2108,16 +1970,16 @@ ort_changed(ort *nf, rta *nr) } static void -rt_sync(struct proto_ospf *po) +rt_sync(struct ospf_proto *p) { - struct proto *p = &po->proto; + struct top_hash_entry *en; struct fib_iterator fit; - struct fib *fib = &po->rtf; + struct fib *fib = &p->rtf; ort *nf; struct ospf_area *oa; /* This is used for forced reload of routes */ - int reload = (po->calcrt == 2); + int reload = (p->calcrt == 2); OSPF_TRACE(D_EVENTS, "Starting routing table synchronisation"); @@ -2135,7 +1997,7 @@ again1: for (nh = nf->n.nhs; nh; nh = nh->next) if (ipa_nonzero(nh->gw)) { - neighbor *ng = neigh_find2(p, &nh->gw, nh->iface, 0); + neighbor *ng = neigh_find2(&p->p, &nh->gw, nh->iface, 0); if (!ng || (ng->scope == SCOPE_HOST)) { reset_ri(nf); break; } } @@ -2148,7 +2010,7 @@ again1: if (nf->n.type) /* Add the route */ { rta a0 = { - .src = p->main_source, + .src = p->p.main_source, .source = nf->n.type, .scope = SCOPE_UNIVERSE, .cast = RTC_UNICAST @@ -2173,7 +2035,7 @@ again1: if (reload || ort_changed(nf, &a0)) { - net *ne = net_get(p->table, nf->fn.prefix, nf->fn.pxlen); + net *ne = net_get(p->p.table, nf->fn.prefix, nf->fn.pxlen); rta *a = rta_lookup(&a0); rte *e = rte_get_temp(a); @@ -2185,11 +2047,11 @@ again1: e->u.ospf.router_id = nf->old_rid = nf->n.rid; e->pflags = 0; e->net = ne; - e->pref = p->preference; + e->pref = p->p.preference; DBG("Mod rte type %d - %I/%d via %I on iface %s, met %d\n", a0.source, nf->fn.prefix, nf->fn.pxlen, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1); - rte_update(p, ne, e); + rte_update(&p->p, ne, e); } } else if (nf->old_rta) @@ -2198,12 +2060,12 @@ again1: rta_free(nf->old_rta); nf->old_rta = NULL; - net *ne = net_get(p->table, nf->fn.prefix, nf->fn.pxlen); - rte_update(p, ne, NULL); + net *ne = net_get(p->p.table, nf->fn.prefix, nf->fn.pxlen); + rte_update(&p->p, ne, NULL); } - /* Remove unused rt entry. Entries with fn.x0 == 1 are persistent. */ - if (!nf->n.type && !nf->fn.x0 && !nf->fn.x1) + /* Remove unused rt entry. Entries with any flags are persistent. */ + if (!nf->n.type && !nf->external_rte) // XXXX { FIB_ITERATE_PUT(&fit, nftmp); fib_delete(fib, nftmp); @@ -2213,7 +2075,7 @@ again1: FIB_ITERATE_END(nftmp); - WALK_LIST(oa, po->area_list) + WALK_LIST(oa, p->area_list) { /* Cleanup ASBR hash tables */ FIB_ITERATE_INIT(&fit, &oa->rtr); @@ -2231,4 +2093,9 @@ again2: } FIB_ITERATE_END(nftmp); } + + /* Cleanup stale LSAs */ + WALK_SLIST(en, p->lsal) + if (en->rtcalc == LSA_STALE) + ospf_flush_lsa(p, en); } diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h index a11748fc..77d7080b 100644 --- a/proto/ospf/rt.h +++ b/proto/ospf/rt.h @@ -10,9 +10,9 @@ #ifndef _BIRD_OSPF_RT_H_ #define _BIRD_OSPF_RT_H_ -#define ORT_UNDEF -1 -#define ORT_ROUTER 1 + #define ORT_NET 0 +#define ORT_ROUTER 1 typedef struct orta { @@ -51,26 +51,33 @@ typedef struct orta } orta; + +/* Values for fn.flags in struct ort */ +#define OSPF_RT_PERSISTENT 0x01 + typedef struct ort { /* - * We use fn.x0 to mark persistent rt entries, that are needed for summary - * LSAs that don't have 'proper' rt entry (area networks + default to stubs) - * to keep uid stable (used for LSA ID in OSPFv3 - see fibnode_to_lsaid()). + * We use OSPF_RT_PERSISTENT to mark persistent rt entries, that are + * needed for summary LSAs that don't have 'proper' rt entry (area + * networks + default to stubs) to keep uid stable (used for LSA ID + * in OSPFv3 - see fibnode_to_lsaid()). * - * We use fn.x1 to note whether the external route was originated - * from the route export (in ospf_rt_notify()) or from the NSSA - * route translation (in check_nssa_lsa()). + * We use ORT_RT_EXPORT and ORT_RT_NSSA to note whether the + * external/NSSA route was originated from the route export (in + * ospf_rt_notify()) or from the NSSA route translation (in + * check_nssa_lsa()). * * old_* values are here to represent the last route update. old_rta * is cached (we keep reference), mainly for multipath nexthops. - * old_rta == NULL means route wasn not in the last update, in that + * old_rta == NULL means route was not in the last update, in that * case other old_* values are not valid. */ struct fib_node fn; orta n; u32 old_metric1, old_metric2, old_tag, old_rid; rta *old_rta; + u8 external_rte; } ort; @@ -114,7 +121,7 @@ static inline int rt_is_nssa(ort *nf) * appear in ASBR pre-selection and external routes processing. */ -void ospf_rt_spf(struct proto_ospf *po); +void ospf_rt_spf(struct ospf_proto *p); void ospf_rt_initort(struct fib_node *fn); diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 4af5afa5..5ed3cf7a 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -3,6 +3,8 @@ * * (c) 1999 Martin Mares * (c) 1999--2004 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -12,6 +14,7 @@ #include "ospf.h" + #define HASH_DEF_ORDER 6 #define HASH_HI_MARK *4 #define HASH_HI_STEP 2 @@ -20,166 +23,570 @@ #define HASH_LO_STEP 2 #define HASH_LO_MIN 8 -void originate_prefix_rt_lsa(struct ospf_area *oa); -void originate_prefix_net_lsa(struct ospf_iface *ifa); -void flush_prefix_net_lsa(struct ospf_iface *ifa); - -#ifdef OSPFv2 -#define ipa_to_rid(x) _I(x) -#else /* OSPFv3 */ -#define ipa_to_rid(x) _I3(x) -#endif +static inline void * lsab_flush(struct ospf_proto *p); +static inline void lsab_reset(struct ospf_proto *p); -#ifdef OSPFv2 -static inline u32 -fibnode_to_lsaid(struct proto_ospf *po, struct fib_node *fn) +/** + * ospf_install_lsa - install new LSA into database + * @p: OSPF protocol instance + * @lsa: LSA header + * @type: type of LSA + * @domain: domain of LSA + * @body: pointer to LSA body + * + * This function ensures installing new LSA received in LS update into LSA + * database. Old instance is replaced. Several actions are taken to detect if + * new routing table calculation is necessary. This is described in 13.2 of RFC + * 2328. This function is for received LSA only, locally originated LSAs are + * installed by ospf_originate_lsa(). + */ +struct top_hash_entry * +ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body) { - /* We have to map IP prefixes to u32 in such manner that resulting - u32 interpreted as IP address is a member of given - prefix. Therefore, /32 prefix have to be mapped on itself. - All received prefixes have to be mapped on different u32s. + /* LSA can be temporary, but body must be mb_allocated. */ + struct top_hash_entry *en; + int change = 0; - We have an assumption that if there is nontrivial (non-/32) - network prefix, then there is not /32 prefix for the first - and the last IP address of the network (these are usually - reserved, therefore it is not an important restriction). - The network prefix is mapped to the first or the last - IP address in the manner that disallow collisions - we - use IP address that cannot be used by parent prefix. + en = ospf_hash_get(p->gr, domain, lsa->id, lsa->rt, type); - For example: - 192.168.0.0/24 maps to 192.168.0.255 - 192.168.1.0/24 maps to 192.168.1.0 - because 192.168.0.0 and 192.168.1.255 might be used by - 192.168.0.0/23 . + if (!SNODE_VALID(en)) + s_add_tail(&p->lsal, SNODE en); - This is not compatible with older RFC 1583, so we have an option - to the RFC 1583 compatible assignment (that always uses the first - address) which disallows subnetting. + if ((en->lsa_body == NULL) || /* No old LSA */ + (en->lsa.length != lsa->length) || + (en->lsa.type_raw != lsa->type_raw) || /* Check for OSPFv2 options */ + (en->lsa.age == LSA_MAXAGE) || + (lsa->age == LSA_MAXAGE) || + memcmp(en->lsa_body, body, lsa->length - sizeof(struct ospf_lsa_header))) + change = 1; - Appendig E of RFC 2328 suggests different algorithm, that tries - to maximize both compatibility and subnetting. But as it is not - possible to have both reliably and the suggested algorithm was - unnecessary complicated and it does crazy things like changing - LSA ID for a network because different network appeared, we - choose a different way. */ + DBG("Inst lsa: Id: %R, Rt: %R, Type: %u, Age: %u, Sum: %u, Sn: 0x%x\n", + lsa->id, lsa->rt, lsa->type, lsa->age, lsa->checksum, lsa->sn); - u32 id = _I(fn->prefix); + mb_free(en->lsa_body); + en->lsa_body = body; + en->lsa = *lsa; + en->init_age = en->lsa.age; + en->inst_time = now; - if ((po->rfc1583) || (fn->pxlen == 0) || (fn->pxlen == 32)) - return id; + if (change) + schedule_rtcalc(p); - if (id & (1 << (32 - fn->pxlen))) - return id; - else - return id | ~u32_mkmask(fn->pxlen); + return en; } -#else /* OSPFv3 */ +void +ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body) +{ + // OSPF_TRACE(D_EVENTS, "Reflooding new self-originated LSA with newer sequence number"); -static inline u32 -fibnode_to_lsaid(struct proto_ospf *po, struct fib_node *fn) + if (en && (en->lsa.age < LSA_MAXAGE)) + { + if (lsa->sn != LSA_MAXSEQNO) + { + /* + * We simply advance current LSA to have higher seqnum than received LSA. + * The received LSA is ignored and the advanced LSA is propagated instead. + * + * Although this is an origination of distinct LSA instance and therefore + * should be limited by MinLSInterval, we do not enforce it here. Fast + * reaction is needed and we are already limited by MinLSArrival. + */ + + mb_free(body); + + en->lsa.sn = lsa->sn + 1; + en->lsa.age = 0; + en->init_age = 0; + en->inst_time = now; + lsasum_calculate(&en->lsa, en->lsa_body); + } + else + { + /* + * Received LSA has maximal sequence number, so we cannot simply override + * it. We have to install it to the database, immediately flush it to + * implement sequence number wrapping, and schedule our current LSA to be + * originated after the received instance is flushed. + */ + + if (en->next_lsa_body == NULL) + { + /* Schedule current LSA */ + en->next_lsa_blen = en->lsa.length - sizeof(struct ospf_lsa_header); + en->next_lsa_body = en->lsa_body; + en->next_lsa_opts = ospf_is_v2(p) ? lsa_get_options(&en->lsa) : 0; + } + else + { + /* There is already scheduled LSA, so we just free current one */ + mb_free(en->lsa_body); + } + + en->lsa_body = body; + en->lsa = *lsa; + en->lsa.age = LSA_MAXAGE; + en->init_age = lsa->age; + en->inst_time = now; + } + } + else + { + /* + * We do not have received LSA in the database. We have to flush the + * received LSA. It has to be installed in the database to secure + * retransmissions. Note that the received LSA may already be MaxAge. + */ + + lsa->age = LSA_MAXAGE; + en = ospf_install_lsa(p, lsa, type, domain, body); + } + + /* + * We flood the updated LSA. Although in some cases the to-be-flooded LSA is + * the same as the received LSA, and therefore we should propagate it as + * regular received LSA (send the acknowledgement instead of the update to + * the neighbor we received it from), we cheat a bit here. + */ + + ospf_lsupd_flood(p, en, NULL); +} + + +static int +ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa_body, u16 lsa_blen, u16 lsa_opts) +{ + /* Enforce MinLSInterval */ + if ((en->init_age == 0) && en->inst_time && ((en->inst_time + MINLSINTERVAL) > now)) + return 0; + + /* Handle wrapping sequence number */ + if (en->lsa.sn == LSA_MAXSEQNO) + { + /* Prepare to flush old LSA */ + if (en->lsa.age != LSA_MAXAGE) + { + OSPF_TRACE(D_EVENTS, "Resetting LSA: Type: %04x, Id: %R, Rt: %R", + en->lsa_type, en->lsa.id, en->lsa.rt); + + en->lsa.age = LSA_MAXAGE; + ospf_lsupd_flood(p, en, NULL); + return 0; + } + + /* Already flushing */ + if ((p->padj != 0) || (en->ret_count != 0)) + return 0; + + /* Flush done, just clean up seqnum, lsa_body is freed below */ + en->lsa.sn = LSA_ZEROSEQNO; + } + + OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R", + en->lsa_type, en->lsa.id, en->lsa.rt); + + /* + * lsa.type_raw is initialized by ospf_hash_get() to OSPFv3 LSA type. + * lsa_set_options() implicitly converts it to OSPFv2 LSA type, assuming that + * old type is just new type masked by 0xff. That is not universally true, + * but it holds for all OSPFv2 types currently supported by BIRD. + */ + + if (ospf_is_v2(p)) + lsa_set_options(&en->lsa, lsa_opts); + + mb_free(en->lsa_body); + en->lsa_body = lsa_body; + en->lsa.length = sizeof(struct ospf_lsa_header) + lsa_blen; + en->lsa.sn++; + en->lsa.age = 0; + en->init_age = 0; + en->inst_time = now; + lsasum_calculate(&en->lsa, en->lsa_body); + + ospf_lsupd_flood(p, en, NULL); + + return 1; +} + +/** + * ospf_originate_lsa - originate new LSA + * @p: OSPF protocol instance + * @lsa: New LSA specification + * + * This function prepares a new LSA, installs it into the LSA database and + * floods it. If the new LSA cannot be originated now (because the old instance + * was originated within MinLSInterval, or because the LSA seqnum is currently + * wrapping), the origination is instead scheduled for later. If the new LSA is + * equivalent to the current LSA, the origination is skipped. In all cases, the + * corresponding LSA entry is returned. The new LSA is based on the LSA + * specification (@lsa) and the LSA body from lsab buffer of @p, which is + * emptied after the call. The opposite of this function is ospf_flush_lsa(). + */ +struct top_hash_entry * +ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa) +{ + struct top_hash_entry *en; + void *lsa_body = p->lsab; + u16 lsa_blen = p->lsab_used; + u16 lsa_length = sizeof(struct ospf_lsa_header) + lsa_blen; + + en = ospf_hash_get(p->gr, lsa->dom, lsa->id, p->router_id, lsa->type); + + if (!SNODE_VALID(en)) + s_add_tail(&p->lsal, SNODE en); + + if (en->lsa_body == NULL) + en->nf = lsa->nf; + + if (en->nf != lsa->nf) + { + log(L_ERR "%s: LSA ID collision for %I/%d", + p->p.name, lsa->nf->fn.prefix, lsa->nf->fn.pxlen); + goto drop; + } + + /* XXXX check for maxage or opts change */ + + if (en->next_lsa_body) + { + /* Ignore the new LSA if it is the same as the scheduled one */ + if ((lsa_blen == en->next_lsa_blen) && !memcmp(lsa_body, en->next_lsa_body, lsa_blen)) + goto drop; + + /* Free scheduled LSA */ + mb_free(en->next_lsa_body); + en->next_lsa_body = NULL; + en->next_lsa_blen = 0; + en->next_lsa_opts = 0; + } + + /* Ignore the the new LSA if is the same as the current one */ + if ((lsa_length == en->lsa.length) && !memcmp(lsa_body, en->lsa_body, lsa_blen)) + goto drop; + + lsa_body = lsab_flush(p); + + if (! ospf_do_originate_lsa(p, en, lsa_body, lsa_blen, lsa->opts)) + { + en->next_lsa_body = lsa_body; + en->next_lsa_blen = lsa_blen; + en->next_lsa_opts = lsa->opts; + } + + return en; + + drop: + lsab_reset(p); + return en; +} + +static void +ospf_originate_next_lsa(struct ospf_proto *p, struct top_hash_entry *en) +{ + /* Called by ospf_update_lsadb() to handle scheduled origination */ + + if (! ospf_do_originate_lsa(p, en, en->next_lsa_body, en->next_lsa_blen, en->next_lsa_opts)) + return; + + en->next_lsa_body = NULL; + en->next_lsa_blen = 0; + en->next_lsa_opts = 0; + + // XXXX: schedule_rtcalc(p); +} + +static void +ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en) { /* - * In OSPFv3, it is simpler. There is not a requirement for - * membership of the result in the input network, so we just use a - * hash-based unique ID of a routing table entry for a route that - * originated given LSA. For ext-LSA, it is an imported route in the - * nest's routing table (p->table). For summary-LSA, it is a - * 'source' route in the protocol internal routing table (po->rtf). + * Called by ospf_update_lsadb() for periodic LSA refresh. + * + * We know that lsa.age < LSA_MAXAGE and lsa.rt is our router ID. We can also + * assume that there is no scheduled LSA, because inst_time is deep in past, + * therefore ospf_originate_next_lsa() called before would either succeed or + * switched lsa.age to LSA_MAXAGE. */ - return fn->uid; + + OSPF_TRACE(D_EVENTS, "Refreshing LSA: Type: %04x, Id: %R, Rt: %R", + en->lsa_type, en->lsa.id, en->lsa.rt); + + ASSERT(en->next_lsa_body == NULL); + + /* Handle wrapping sequence number */ + if (en->lsa.sn == LSA_MAXSEQNO) + { + /* Copy LSA body as next LSA to get automatic origination after flush is finished */ + en->next_lsa_blen = en->lsa.length - sizeof(struct ospf_lsa_header); + en->next_lsa_body = mb_alloc(p->p.pool, en->next_lsa_blen); + memcpy(en->next_lsa_body, en->lsa_body, en->next_lsa_blen); + en->next_lsa_opts = ospf_is_v2(p) ? lsa_get_options(&en->lsa) : 0; + + en->lsa.age = LSA_MAXAGE; + ospf_lsupd_flood(p, en, NULL); + return; + } + + en->lsa.sn++; + en->lsa.age = 0; + en->init_age = 0; + en->inst_time = now; + lsasum_calculate(&en->lsa, en->lsa_body); + ospf_lsupd_flood(p, en, NULL); } -#endif +/** + * ospf_flush_lsa - flush LSA from OSPF domain + * @p: OSPF protocol instance + * @en: LSA entry to flush + * + * This function flushes @en from the OSPF domain by setting its age to + * %LSA_MAXAGE and flooding it. That also triggers subsequent events in LSA + * lifecycle leading to removal of the LSA from the LSA database (e.g. the LSA + * content is freed when flushing is acknowledged by neighbors). The function + * does nothing if the LSA is already being flushed. LSA entries are not + * immediately removed when being flushed, the caller may assume that @en still + * exists after the call. The function is the opposite of ospf_originate_lsa() + * and is supposed to do the right thing even in cases of postponed + * origination. Note that this function do not schedule routing table + * calculation, the caller is responsible to do it if necessary. + */ +void +ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en) +{ + OSPF_TRACE(D_EVENTS, "Flushing LSA: Type: %04x, Id: %R, Rt: %R", + en->lsa_type, en->lsa.id, en->lsa.rt); + + en->rtcalc = 0; + + if (en->next_lsa_body) + { + mb_free(en->next_lsa_body); + en->next_lsa_body = NULL; + en->next_lsa_blen = 0; + en->next_lsa_opts = 0; + } + + if (en->lsa.age == LSA_MAXAGE) + return; + + en->lsa.age = LSA_MAXAGE; + ospf_lsupd_flood(p, en, NULL); +} + +static void +ospf_clear_lsa(struct ospf_proto *p, struct top_hash_entry *en) +{ + /* + * Called by ospf_update_lsadb() as part of LSA flushing process. + * Flushed LSA was acknowledged by neighbors and we can free its content. + */ + + if (en->lsa.sn == LSA_MAXSEQNO) + en->lsa.sn = LSA_ZEROSEQNO; + + mb_free(en->lsa_body); + en->lsa_body = NULL; +} + +static void +ospf_remove_lsa(struct ospf_proto *p, struct top_hash_entry *en) +{ + /* + * Called by ospf_update_lsadb() as part of LSA flushing process. + * Both lsa_body and next_lsa_body are NULL. + */ + + s_rem_node(SNODE en); + ospf_hash_delete(p->gr, en); +} + +/** + * ospf_update_lsadb - update LSA database + * @p: OSPF protocol instance + * + * This function is periodicaly invoked from ospf_disp(). It does some periodic + * or postponed processing related to LSA entries. It originates postponed LSAs + * scheduled by ospf_originate_lsa(), It continues in flushing processes started + * by ospf_flush_lsa(). It also periodically refreshs locally originated LSAs -- + * when the current instance is older %LSREFRESHTIME, a new instance is originated. + * Finally, it also ages stored LSAs and flushes ones that reached %LSA_MAXAGE. + * + * The RFC 2328 says that a router should periodically check checksums of all + * stored LSAs to detect hardware problems. This is not implemented. + */ +void +ospf_update_lsadb(struct ospf_proto *p) +{ + struct top_hash_entry *en, *nxt; + bird_clock_t real_age; + + WALK_SLIST_DELSAFE(en, nxt, p->lsal) + { + if (en->next_lsa_body) + ospf_originate_next_lsa(p, en); + + real_age = en->init_age + (now - en->inst_time); + + if (en->lsa.age == LSA_MAXAGE) + { + if (en->lsa_body && (p->padj == 0) && (en->ret_count == 0)) + ospf_clear_lsa(p, en); + + if ((en->lsa_body == NULL) && (en->next_lsa_body == NULL) && + ((en->lsa.rt != p->router_id) || (real_age >= LSA_MAXAGE))) + ospf_remove_lsa(p, en); + + continue; + } + + if ((en->lsa.rt == p->router_id) && (real_age >= LSREFRESHTIME)) + { + ospf_refresh_lsa(p, en); + continue; + } + + if (real_age >= LSA_MAXAGE) + { + ospf_flush_lsa(p, en); + schedule_rtcalc(p); + continue; + } + + en->lsa.age = real_age; + } +} + + +static inline u32 +ort_to_lsaid(struct ospf_proto *p, ort *nf) +{ + /* + * In OSPFv2, We have to map IP prefixes to u32 in such manner that resulting + * u32 interpreted as IP address is a member of given prefix. Therefore, /32 + * prefix have to be mapped on itself. All received prefixes have to be + * mapped on different u32s. + * + * We have an assumption that if there is nontrivial (non-/32) network prefix, + * then there is not /32 prefix for the first and the last IP address of the + * network (these are usually reserved, therefore it is not an important + * restriction). The network prefix is mapped to the first or the last IP + * address in the manner that disallow collisions - we use the IP address that + * cannot be used by the parent prefix. + * + * For example: + * 192.168.0.0/24 maps to 192.168.0.255 + * 192.168.1.0/24 maps to 192.168.1.0 + * because 192.168.0.0 and 192.168.1.255 might be used by 192.168.0.0/23 . + * + * Appendig E of RFC 2328 suggests different algorithm, that tries to maximize + * both compatibility and subnetting. But as it is not possible to have both + * reliably and the suggested algorithm was unnecessary complicated and it + * does crazy things like changing LSA ID for a network because different + * network appeared, we choose a different way. + * + * In OSPFv3, it is simpler. There is not a requirement for membership of the + * result in the input network, so we just use a hash-based unique ID of a + * routing table entry for a route that originated given LSA. For ext-LSA, it + * is an imported route in the nest's routing table (p->table). For summary-LSA, + * it is a 'source' route in the protocol internal routing table (p->rtf). + */ + + if (ospf_is_v3(p)) + return nf->fn.uid; + + u32 id = ipa_to_u32(nf->fn.prefix); + int pxlen = nf->fn.pxlen; + + if ((pxlen == 0) || (pxlen == 32)) + return id; + + if (id & (1 << (32 - pxlen))) + return id; + else + return id | ~u32_mkmask(pxlen); +} static void * -lsab_alloc(struct proto_ospf *po, unsigned size) +lsab_alloc(struct ospf_proto *p, unsigned size) { - unsigned offset = po->lsab_used; - po->lsab_used += size; - if (po->lsab_used > po->lsab_size) - { - po->lsab_size = MAX(po->lsab_used, 2 * po->lsab_size); - po->lsab = po->lsab ? mb_realloc(po->lsab, po->lsab_size): - mb_alloc(po->proto.pool, po->lsab_size); - } - return ((byte *) po->lsab) + offset; + unsigned offset = p->lsab_used; + p->lsab_used += size; + if (p->lsab_used > p->lsab_size) + { + p->lsab_size = MAX(p->lsab_used, 2 * p->lsab_size); + p->lsab = p->lsab ? mb_realloc(p->lsab, p->lsab_size): + mb_alloc(p->p.pool, p->lsab_size); + } + return ((byte *) p->lsab) + offset; } static inline void * -lsab_allocz(struct proto_ospf *po, unsigned size) +lsab_allocz(struct ospf_proto *p, unsigned size) { - void *r = lsab_alloc(po, size); + void *r = lsab_alloc(p, size); bzero(r, size); return r; } static inline void * -lsab_flush(struct proto_ospf *po) +lsab_flush(struct ospf_proto *p) { - void *r = mb_alloc(po->proto.pool, po->lsab_used); - memcpy(r, po->lsab, po->lsab_used); - po->lsab_used = 0; + void *r = mb_alloc(p->p.pool, p->lsab_used); + memcpy(r, p->lsab, p->lsab_used); + p->lsab_used = 0; return r; } -static inline void * -lsab_offset(struct proto_ospf *po, unsigned offset) +static inline void +lsab_reset(struct ospf_proto *p) { - return ((byte *) po->lsab) + offset; + p->lsab_used = 0; } static inline void * -lsab_end(struct proto_ospf *po) +lsab_offset(struct ospf_proto *p, unsigned offset) { - return ((byte *) po->lsab) + po->lsab_used; + return ((byte *) p->lsab) + offset; } -static s32 -get_seqnum(struct top_hash_entry *en) +static inline void * +lsab_end(struct ospf_proto *p) { - if (!en) - return LSA_INITSEQNO; - - if (en->lsa.sn == LSA_MAXSEQNO) - { - log(L_WARN "OSPF: Premature origination of LSA (Type: %04x, Id: %R, Rt: %R)", - en->lsa.type, en->lsa.id, en->lsa.rt); - return LSA_INITSEQNO; - } - - return en->lsa.sn + 1; + return ((byte *) p->lsab) + p->lsab_used; } +/* + * Router-LSA handling + * Type = LSA_T_RT + */ + static int configured_stubnet(struct ospf_area *oa, struct ifa *a) { - if (!oa->ac) - return 0; - /* Does not work for IA_PEER addresses, but it is not called on these */ struct ospf_stubnet_config *sn; WALK_LIST(sn, oa->ac->stubnet_list) + { + if (sn->summary) { - if (sn->summary) - { - if (ipa_in_net(a->prefix, sn->px.addr, sn->px.len) && (a->pxlen >= sn->px.len)) - return 1; - } - else - { - if (ipa_equal(a->prefix, sn->px.addr) && (a->pxlen == sn->px.len)) - return 1; - } + if (ipa_in_net(a->prefix, sn->px.addr, sn->px.len) && (a->pxlen >= sn->px.len)) + return 1; } + else + { + if (ipa_equal(a->prefix, sn->px.addr) && (a->pxlen == sn->px.len)) + return 1; + } + } + return 0; } -int +static int bcast_net_active(struct ospf_iface *ifa) { struct ospf_neighbor *neigh; @@ -188,53 +595,66 @@ bcast_net_active(struct ospf_iface *ifa) return 0; WALK_LIST(neigh, ifa->neigh_list) + { + if (neigh->state == NEIGHBOR_FULL) { - if (neigh->state == NEIGHBOR_FULL) - { - if (neigh->rid == ifa->drid) - return 1; + if (neigh->rid == ifa->drid) + return 1; - if (ifa->state == OSPF_IS_DR) - return 1; - } + if (ifa->state == OSPF_IS_DR) + return 1; } + } return 0; } - -#ifdef OSPFv2 - -static void * -originate_rt_lsa_body(struct ospf_area *oa, u16 *length) +static inline u32 +get_rt_options(struct ospf_proto *p, struct ospf_area *oa, int bitv) +{ + u32 opts = 0; + + if (p->areano > 1) + opts |= OPT_RT_B; + + if ((p->areano > 1) && oa_is_nssa(oa) && oa->ac->translator) + opts |= OPT_RT_NT; + + if (p->asbr && !oa_is_stub(oa)) + opts |= OPT_RT_E; + + if (bitv) + opts |= OPT_RT_V; + + return opts; +} + +static inline void +add_rt2_lsa_link(struct ospf_proto *p, u8 type, u32 id, u32 data, u16 metric) +{ + struct ospf_lsa_rt2_link *ln = lsab_alloc(p, sizeof(struct ospf_lsa_rt2_link)); + ln->type = type; + ln->id = id; + ln->data = data; + ln->metric = metric; + ln->no_tos = 0; +} + +static void +prepare_rt2_lsa_body(struct ospf_proto *p, struct ospf_area *oa) { - struct proto_ospf *po = oa->po; struct ospf_iface *ifa; int i = 0, bitv = 0; - struct ospf_lsa_rt *rt; - struct ospf_lsa_rt_link *ln; struct ospf_neighbor *neigh; - ASSERT(po->lsab_used == 0); - rt = lsab_allocz(po, sizeof(struct ospf_lsa_rt)); + ASSERT(p->lsab_used == 0); + lsab_allocz(p, sizeof(struct ospf_lsa_rt)); + /* ospf_lsa_rt header will be filled later */ - rt->options = 0; - - if (po->areano > 1) - rt->options |= OPT_RT_B; - - if ((po->areano > 1) && oa_is_nssa(oa) && oa->ac->translator) - rt->options |= OPT_RT_NT; - - if (po->ebit && !oa_is_stub(oa)) - rt->options |= OPT_RT_E; - - rt = NULL; /* buffer might be reallocated later */ - - WALK_LIST(ifa, po->iface_list) + WALK_LIST(ifa, p->iface_list) { int net_lsa = 0; - u32 link_cost = po->stub_router ? 0xffff : ifa->cost; + u32 link_cost = p->stub_router ? 0xffff : ifa->cost; if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) && (!EMPTY_LIST(ifa->neigh_list))) @@ -249,66 +669,47 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length) ifa->rt_pos_beg = i; - /* RFC2328 - 12.4.1.1-4 */ + /* RFC 2328 - 12.4.1.1-4 */ switch (ifa->type) - { - case OSPF_IT_PTP: - case OSPF_IT_PTMP: - WALK_LIST(neigh, ifa->neigh_list) - if (neigh->state == NEIGHBOR_FULL) - { - ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); - ln->type = LSART_PTP; - ln->id = neigh->rid; - - /* - * ln->data should be ifa->iface_id in case of no/ptp - * address (ifa->addr->flags & IA_PEER) on PTP link (see - * RFC 2328 12.4.1.1.), but the iface ID value has no use, - * while using IP address even in this case is here for - * compatibility with some broken implementations that use - * this address as a next-hop. - */ - ln->data = ipa_to_u32(ifa->addr->ip); - ln->metric = link_cost; - ln->padding = 0; - i++; - } - break; - - case OSPF_IT_BCAST: - case OSPF_IT_NBMA: - if (bcast_net_active(ifa)) - { - ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); - ln->type = LSART_NET; - ln->id = ipa_to_u32(ifa->drip); - ln->data = ipa_to_u32(ifa->addr->ip); - ln->metric = link_cost; - ln->padding = 0; - i++; - net_lsa = 1; - } - break; - - case OSPF_IT_VLINK: - neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list); - if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff)) + { + case OSPF_IT_PTP: + case OSPF_IT_PTMP: + WALK_LIST(neigh, ifa->neigh_list) + if (neigh->state == NEIGHBOR_FULL) { - ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); - ln->type = LSART_VLNK; - ln->id = neigh->rid; - ln->data = ipa_to_u32(ifa->addr->ip); - ln->metric = link_cost; - ln->padding = 0; + /* + * ln->data should be ifa->iface_id in case of no/ptp + * address (ifa->addr->flags & IA_PEER) on PTP link (see + * RFC 2328 12.4.1.1.), but the iface ID value has no use, + * while using IP address even in this case is here for + * compatibility with some broken implementations that use + * this address as a next-hop. + */ + add_rt2_lsa_link(p, LSART_PTP, neigh->rid, ipa_to_u32(ifa->addr->ip), link_cost); i++; - } - break; + } + break; - default: - log("Unknown interface type %s", ifa->ifname); - break; + case OSPF_IT_BCAST: + case OSPF_IT_NBMA: + if (bcast_net_active(ifa)) + { + add_rt2_lsa_link(p, LSART_NET, ipa_to_u32(ifa->drip), ipa_to_u32(ifa->addr->ip), link_cost); + i++; + net_lsa = 1; } + break; + + case OSPF_IT_VLINK: + neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list); + if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff)) + add_rt2_lsa_link(p, LSART_VLNK, neigh->rid, ipa_to_u32(ifa->addr->ip), link_cost), i++; + break; + + default: + log("Unknown interface type %s", ifa->ifname); + break; + } ifa->rt_pos_end = i; @@ -319,62 +720,32 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length) configured_stubnet(oa, ifa->addr)) continue; - ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); + /* Host or network stub entry */ if ((ifa->addr->flags & IA_HOST) || (ifa->state == OSPF_IS_LOOP) || (ifa->type == OSPF_IT_PTMP)) - { - /* Host stub entry */ - ln->type = LSART_STUB; - ln->id = ipa_to_u32(ifa->addr->ip); - ln->data = 0xffffffff; - ln->metric = 0; - ln->padding = 0; - } + add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(ifa->addr->ip), 0xffffffff, 0); else - { - /* Network stub entry */ - ln->type = LSART_STUB; - ln->id = ipa_to_u32(ifa->addr->prefix); - ln->data = ipa_to_u32(ipa_mkmask(ifa->addr->pxlen)); - ln->metric = ifa->cost; - ln->padding = 0; - } + add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(ifa->addr->prefix), u32_mkmask(ifa->addr->pxlen), ifa->cost); i++; ifa->rt_pos_end = i; } struct ospf_stubnet_config *sn; - if (oa->ac) - WALK_LIST(sn, oa->ac->stubnet_list) - if (!sn->hidden) - { - ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); - ln->type = LSART_STUB; - ln->id = ipa_to_u32(sn->px.addr); - ln->data = ipa_to_u32(ipa_mkmask(sn->px.len)); - ln->metric = sn->cost; - ln->padding = 0; - i++; - } + WALK_LIST(sn, oa->ac->stubnet_list) + if (!sn->hidden) + add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(sn->px.addr), u32_mkmask(sn->px.len), sn->cost), i++; - rt = po->lsab; - rt->links = i; - - if (bitv) - rt->options |= OPT_RT_V; - - *length = po->lsab_used + sizeof(struct ospf_lsa_header); - return lsab_flush(po); + struct ospf_lsa_rt *rt = p->lsab; + /* Store number of links in lower half of options */ + rt->options = get_rt_options(p, oa, bitv) | (u16) i; } -#else /* OSPFv3 */ - -static void -add_lsa_rt_link(struct proto_ospf *po, struct ospf_iface *ifa, u8 type, u32 nif, u32 id) +static inline void +add_rt3_lsa_link(struct ospf_proto *p, u8 type, struct ospf_iface *ifa, u32 nif, u32 id) { - struct ospf_lsa_rt_link *ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); + struct ospf_lsa_rt3_link *ln = lsab_alloc(p, sizeof(struct ospf_lsa_rt3_link)); ln->type = type; ln->padding = 0; ln->metric = ifa->cost; @@ -383,33 +754,19 @@ add_lsa_rt_link(struct proto_ospf *po, struct ospf_iface *ifa, u8 type, u32 nif, ln->id = id; } -static void * -originate_rt_lsa_body(struct ospf_area *oa, u16 *length) +static void +prepare_rt3_lsa_body(struct ospf_proto *p, struct ospf_area *oa) { - struct proto_ospf *po = oa->po; struct ospf_iface *ifa; + struct ospf_neighbor *neigh; int bitv = 0; int i = 0; - struct ospf_lsa_rt *rt; - struct ospf_neighbor *neigh; - ASSERT(po->lsab_used == 0); - rt = lsab_allocz(po, sizeof(struct ospf_lsa_rt)); + ASSERT(p->lsab_used == 0); + lsab_allocz(p, sizeof(struct ospf_lsa_rt)); + /* ospf_lsa_rt header will be filled later */ - rt->options = oa->options & OPTIONS_MASK; - - if (po->areano > 1) - rt->options |= OPT_RT_B; - - if ((po->areano > 1) && oa_is_nssa(oa) && oa->ac->translator) - rt->options |= OPT_RT_NT; - - if (po->ebit && !oa_is_stub(oa)) - rt->options |= OPT_RT_E; - - rt = NULL; /* buffer might be reallocated later */ - - WALK_LIST(ifa, po->iface_list) + WALK_LIST(ifa, p->iface_list) { if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) && (!EMPTY_LIST(ifa->neigh_list))) @@ -424,50 +781,42 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length) ifa->rt_pos_beg = i; - /* RFC5340 - 4.4.3.2 */ + /* RFC 5340 - 4.4.3.2 */ switch (ifa->type) - { - case OSPF_IT_PTP: - case OSPF_IT_PTMP: - WALK_LIST(neigh, ifa->neigh_list) - if (neigh->state == NEIGHBOR_FULL) - add_lsa_rt_link(po, ifa, LSART_PTP, neigh->iface_id, neigh->rid), i++; - break; + { + case OSPF_IT_PTP: + case OSPF_IT_PTMP: + WALK_LIST(neigh, ifa->neigh_list) + if (neigh->state == NEIGHBOR_FULL) + add_rt3_lsa_link(p, LSART_PTP, ifa, neigh->iface_id, neigh->rid), i++; + break; - case OSPF_IT_BCAST: - case OSPF_IT_NBMA: - if (bcast_net_active(ifa)) - add_lsa_rt_link(po, ifa, LSART_NET, ifa->dr_iface_id, ifa->drid), i++; - break; + case OSPF_IT_BCAST: + case OSPF_IT_NBMA: + if (bcast_net_active(ifa)) + add_rt3_lsa_link(p, LSART_NET, ifa, ifa->dr_iface_id, ifa->drid), i++; + break; - case OSPF_IT_VLINK: - neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list); - if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff)) - add_lsa_rt_link(po, ifa, LSART_VLNK, neigh->iface_id, neigh->rid), i++; - break; + case OSPF_IT_VLINK: + neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list); + if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff)) + add_rt3_lsa_link(p, LSART_VLNK, ifa, neigh->iface_id, neigh->rid), i++; + break; - default: - log("Unknown interface type %s", ifa->ifname); - break; - } + default: + log("Unknown interface type %s", ifa->ifname); + break; + } ifa->rt_pos_end = i; } - if (bitv) - { - rt = po->lsab; - rt->options |= OPT_RT_V; - } - - *length = po->lsab_used + sizeof(struct ospf_lsa_header); - return lsab_flush(po); + struct ospf_lsa_rt *rt = p->lsab; + rt->options = get_rt_options(p, oa, bitv) | (oa->options & LSA_OPTIONS_MASK); } -#endif - /** - * originate_rt_lsa - build new instance of router LSA + * ospf_originate_rt_lsa - build new instance of router LSA * @oa: ospf_area which is LSA built to * * It builds router LSA walking through all OSPF interfaces in @@ -475,91 +824,80 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length) * area_disp(). Builds new LSA, increases sequence number (if old * instance exists) and sets age of LSA to zero. */ -void -originate_rt_lsa(struct ospf_area *oa) +static void +ospf_originate_rt_lsa(struct ospf_proto *p, struct ospf_area *oa) { - struct ospf_lsa_header lsa; - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; - void *body; + struct ospf_new_lsa lsa = { + .type = LSA_T_RT, + .dom = oa->areaid, + .id = ospf_is_v2(p) ? p->router_id : 0, + .opts = oa->options + }; - OSPF_TRACE(D_EVENTS, "Originating router-LSA for area %R", oa->areaid); + if (ospf_is_v2(p)) + prepare_rt2_lsa_body(p, oa); + else + prepare_rt3_lsa_body(p, oa); - lsa.age = 0; - lsa.type = LSA_T_RT; - -#ifdef OSPFv2 - lsa.options = oa->options; - lsa.id = po->router_id; -#else /* OSPFv3 */ - lsa.id = 0; -#endif - - lsa.rt = po->router_id; - lsa.sn = get_seqnum(oa->rt); - u32 dom = oa->areaid; - - body = originate_rt_lsa_body(oa, &lsa.length); - lsasum_calculate(&lsa, body); - oa->rt = lsa_install_new(po, &lsa, dom, body); - ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); + oa->rt = ospf_originate_lsa(p, &lsa); } -void -update_rt_lsa(struct ospf_area *oa) + +/* + * Net-LSA handling + * Type = LSA_T_NET + */ + +static void +prepare_net2_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa) { - struct proto_ospf *po = oa->po; - - if ((oa->rt) && ((oa->rt->inst_t + MINLSINTERVAL)) > now) - return; - /* - * Tick is probably set to very low value. We cannot - * originate new LSA before MINLSINTERVAL. We will - * try to do it next tick. - */ - - originate_rt_lsa(oa); -#ifdef OSPFv3 - originate_prefix_rt_lsa(oa); -#endif - - schedule_rtcalc(po); - oa->origrt = 0; -} - -static void * -originate_net_lsa_body(struct ospf_iface *ifa, u16 *length, - struct proto_ospf *po) -{ - u16 i = 1; - struct ospf_neighbor *n; struct ospf_lsa_net *net; + struct ospf_neighbor *n; int nodes = ifa->fadj + 1; + u16 i = 1; - net = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_net) - + nodes * sizeof(u32)); + ASSERT(p->lsab_used == 0); + net = lsab_alloc(p, sizeof(struct ospf_lsa_net) + 4 * nodes); -#ifdef OSPFv2 - net->netmask = ipa_mkmask(ifa->addr->pxlen); -#endif - -#ifdef OSPFv3 - /* In OSPFv3, we would like to merge options from Link LSAs of added neighbors */ - struct top_hash_entry *en; - u32 options = 0; -#endif - - net->routers[0] = po->router_id; + net->optx = u32_mkmask(ifa->addr->pxlen); + net->routers[0] = p->router_id; WALK_LIST(n, ifa->neigh_list) { if (n->state == NEIGHBOR_FULL) { -#ifdef OSPFv3 - en = ospf_hash_find(po->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK); + net->routers[i] = n->rid; + i++; + } + } + ASSERT(i == nodes); +} + +static void +prepare_net3_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa) +{ + struct ospf_lsa_net *net; + int nodes = ifa->fadj + 1; + u32 options = 0; + u16 i = 1; + + ASSERT(p->lsab_used == 0); + net = lsab_alloc(p, sizeof(struct ospf_lsa_net) + 4 * nodes); + + net->routers[0] = p->router_id; + + struct ospf_neighbor *n; + WALK_LIST(n, ifa->neigh_list) + { + if (n->state == NEIGHBOR_FULL) + { + /* In OSPFv3, we would like to merge options from Link LSAs of added neighbors */ + + struct top_hash_entry *en = + ospf_hash_find(p->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK); + if (en) options |= ((struct ospf_lsa_link *) en->lsa_body)->options; -#endif net->routers[i] = n->rid; i++; @@ -567,18 +905,11 @@ originate_net_lsa_body(struct ospf_iface *ifa, u16 *length, } ASSERT(i == nodes); -#ifdef OSPFv3 - net->options = options & OPTIONS_MASK; -#endif - - *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_net) - + nodes * sizeof(u32); - return net; + net->optx = options & LSA_OPTIONS_MASK; } - /** - * originate_net_lsa - originates of deletes network LSA + * ospf_originate_net_lsa - originates of deletes network LSA * @ifa: interface which is LSA originated for * * Interface counts number of adjacent neighbors. If this number is @@ -586,475 +917,169 @@ originate_net_lsa_body(struct ospf_iface *ifa, u16 *length, * and premature ages instance of network LSA for specified interface. * In other case, new instance of network LSA is originated. */ -void -originate_net_lsa(struct ospf_iface *ifa) +static void +ospf_originate_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa) { - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; - struct ospf_lsa_header lsa; - u32 dom = ifa->oa->areaid; - - void *body; + struct ospf_new_lsa lsa = { + .type = LSA_T_NET, + .dom = ifa->oa->areaid, + .id = ospf_is_v2(p) ? ipa_to_u32(ifa->addr->ip) : ifa->iface_id, + .opts = ifa->oa->options, + .ifa = ifa + }; - OSPF_TRACE(D_EVENTS, "Originating network-LSA for iface %s", ifa->ifname); - - lsa.age = 0; - lsa.type = LSA_T_NET; - -#ifdef OSPFv2 - lsa.options = ifa->oa->options; - lsa.id = ipa_to_u32(ifa->addr->ip); -#else /* OSPFv3 */ - lsa.id = ifa->iface_id; -#endif - - lsa.rt = po->router_id; - lsa.sn = get_seqnum(ifa->net_lsa); - - body = originate_net_lsa_body(ifa, &lsa.length, po); - lsasum_calculate(&lsa, body); - ifa->net_lsa = lsa_install_new(po, &lsa, dom, body); - ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); -} - -void -flush_net_lsa(struct ospf_iface *ifa) -{ - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; - u32 dom = ifa->oa->areaid; - - if (ifa->net_lsa == NULL) - return; - - OSPF_TRACE(D_EVENTS, "Flushing network-LSA for iface %s", ifa->ifname); - ifa->net_lsa->lsa.sn += 1; - ifa->net_lsa->lsa.age = LSA_MAXAGE; - lsasum_calculate(&ifa->net_lsa->lsa, ifa->net_lsa->lsa_body); - ospf_lsupd_flood(po, NULL, NULL, &ifa->net_lsa->lsa, dom, 0); - flush_lsa(ifa->net_lsa, po); - ifa->net_lsa = NULL; -} - -void -update_net_lsa(struct ospf_iface *ifa) -{ - struct proto_ospf *po = ifa->oa->po; - - if (ifa->net_lsa && ((ifa->net_lsa->inst_t + MINLSINTERVAL) > now)) - return; - /* - * It's too early to originate new network LSA. We will - * try to do it next tick - */ - - if ((ifa->state != OSPF_IS_DR) || (ifa->fadj == 0)) - { - flush_net_lsa(ifa); -#ifdef OSPFv3 - flush_prefix_net_lsa(ifa); -#endif - } + if (ospf_is_v2(p)) + prepare_net2_lsa_body(p, ifa); else - { - originate_net_lsa(ifa); -#ifdef OSPFv3 - originate_prefix_net_lsa(ifa); -#endif - } + prepare_net3_lsa_body(p, ifa); - schedule_rtcalc(po); - ifa->orignet = 0; + ifa->net_lsa = ospf_originate_lsa(p, &lsa); } -#ifdef OSPFv2 -static inline void * -originate_sum_lsa_body(struct proto_ospf *po, u16 *length, u32 mlen, u32 metric) +/* + * (Net|Rt)-summary-LSA handling + * (a.k.a. Inter-Area-(Prefix|Router)-LSA) + * Type = LSA_T_SUM_NET, LSA_T_SUM_RT + */ + +static inline void +prepare_sum2_lsa_body(struct ospf_proto *p, uint pxlen, u32 metric) { - struct ospf_lsa_sum *sum = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_sum)); - *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_sum); + struct ospf_lsa_sum2 *sum; - sum->netmask = ipa_mkmask(mlen); + sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum2)); + sum->netmask = u32_mkmask(pxlen); sum->metric = metric; - - return sum; } -#define originate_sum_net_lsa_body(po,length,fn,metric) \ - originate_sum_lsa_body(po, length, (fn)->pxlen, metric) - -#define originate_sum_rt_lsa_body(po,length,drid,metric,options) \ - originate_sum_lsa_body(po, length, 0, metric) - -static inline int -check_sum_net_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en) +static inline void +prepare_sum3_net_lsa_body(struct ospf_proto *p, ort *nf, u32 metric) { - struct ospf_lsa_sum *sum = en->lsa_body; - return fn->pxlen != ipa_mklen(sum->netmask); -} - -static inline int -check_sum_lsa_same(struct top_hash_entry *en, u32 metric) -{ - /* Netmask already checked in check_sum_net_lsaid_collision() */ - struct ospf_lsa_sum *sum = en->lsa_body; - return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric); -} - -#define check_sum_net_lsa_same(en,metric) \ - check_sum_lsa_same(en, metric) - -#define check_sum_rt_lsa_same(en,drid,metric,options) \ - check_sum_lsa_same(en, metric) - - -#else /* OSPFv3 */ - -static inline void * -originate_sum_net_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn, u32 metric) -{ - int size = sizeof(struct ospf_lsa_sum_net) + IPV6_PREFIX_SPACE(fn->pxlen); - struct ospf_lsa_sum_net *sum = mb_alloc(po->proto.pool, size); - *length = sizeof(struct ospf_lsa_header) + size; + struct ospf_lsa_sum3_net *sum; + sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_net) + IPV6_PREFIX_SPACE(nf->fn.pxlen)); sum->metric = metric; - put_ipv6_prefix(sum->prefix, fn->prefix, fn->pxlen, 0, 0); - - return sum; + put_ipv6_prefix(sum->prefix, nf->fn.prefix, nf->fn.pxlen, 0, 0); } -static inline int -check_sum_net_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en) +static inline void +prepare_sum3_rt_lsa_body(struct ospf_proto *p, u32 drid, u32 metric, u32 options) { - struct ospf_lsa_sum_net *sum = en->lsa_body; - ip_addr prefix; - int pxlen; - u8 pxopts; - u16 rest; - - lsa_get_ipv6_prefix(sum->prefix, &prefix, &pxlen, &pxopts, &rest); - return (fn->pxlen != pxlen) || !ipa_equal(fn->prefix, prefix); -} - -static inline int -check_sum_net_lsa_same(struct top_hash_entry *en, u32 metric) -{ - /* Prefix already checked in check_sum_net_lsaid_collision() */ - struct ospf_lsa_sum_net *sum = en->lsa_body; - return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric); -} - -static inline void * -originate_sum_rt_lsa_body(struct proto_ospf *po, u16 *length, u32 drid, u32 metric, u32 options) -{ - struct ospf_lsa_sum_rt *sum = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_sum_rt)); - *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_sum_rt); + struct ospf_lsa_sum3_rt *sum; + sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_rt)); sum->options = options; sum->metric = metric; sum->drid = drid; - - return sum; -} - -static inline int -check_sum_rt_lsa_same(struct top_hash_entry *en, u32 drid, u32 metric, u32 options) -{ - struct ospf_lsa_sum_rt *sum = en->lsa_body; - return (en->lsa.sn != LSA_MAXSEQNO) && (sum->options == options) && - (sum->metric == metric) && (sum->drid == drid); -} - -#endif - -void -originate_sum_net_lsa(struct ospf_area *oa, struct fib_node *fn, int metric) -{ - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; - struct top_hash_entry *en; - u32 dom = oa->areaid; - struct ospf_lsa_header lsa; - void *body; - - OSPF_TRACE(D_EVENTS, "Originating net-summary-LSA for %I/%d (metric %d)", - fn->prefix, fn->pxlen, metric); - - /* options argument is used in ORT_NET and OSPFv3 only */ - lsa.age = 0; -#ifdef OSPFv2 - lsa.options = oa->options; -#endif - lsa.type = LSA_T_SUM_NET; - lsa.id = fibnode_to_lsaid(po, fn); - lsa.rt = po->router_id; - - if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL) - { - if (check_sum_net_lsaid_collision(fn, en)) - { - log(L_ERR "%s: LSAID collision for %I/%d", - p->name, fn->prefix, fn->pxlen); - return; - } - - if (check_sum_net_lsa_same(en, metric)) - return; - } - lsa.sn = get_seqnum(en); - - body = originate_sum_net_lsa_body(po, &lsa.length, fn, metric); - lsasum_calculate(&lsa, body); - lsa_install_new(po, &lsa, dom, body); - ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); } void -originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32 options UNUSED) +ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric) { - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; struct top_hash_entry *en; - u32 dom = oa->areaid; - u32 rid = ipa_to_rid(fn->prefix); - struct ospf_lsa_header lsa; - void *body; - OSPF_TRACE(D_EVENTS, "Originating rt-summary-LSA for %R (metric %d)", - rid, metric); + struct ospf_new_lsa lsa = { + .type = LSA_T_SUM_NET, + .dom = oa->areaid, + .id = ort_to_lsaid(p, nf), + .opts = oa->options, + .nf = nf + }; - lsa.age = 0; -#ifdef OSPFv2 - lsa.options = oa->options; -#endif - lsa.type = LSA_T_SUM_RT; - /* In OSPFv3, LSA ID is meaningless, but we still use Router ID of ASBR */ - lsa.id = rid; - lsa.rt = po->router_id; - - options &= OPTIONS_MASK; - if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL) - { - if (check_sum_rt_lsa_same(en, lsa.id, metric, options)) - return; - } - lsa.sn = get_seqnum(en); - - body = originate_sum_rt_lsa_body(po, &lsa.length, lsa.id, metric, options); - lsasum_calculate(&lsa, body); - lsa_install_new(po, &lsa, dom, body); - ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); -} - -void -flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type) -{ - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; - struct top_hash_entry *en; - struct ospf_lsa_header lsa; - - lsa.rt = po->router_id; - if (type == ORT_NET) - { - lsa.id = fibnode_to_lsaid(po, fn); - lsa.type = LSA_T_SUM_NET; - } + if (ospf_is_v2(p)) + prepare_sum2_lsa_body(p, nf->fn.pxlen, metric); else - { - /* In OSPFv3, LSA ID is meaningless, but we still use Router ID of ASBR */ - lsa.id = ipa_to_rid(fn->prefix); - lsa.type = LSA_T_SUM_RT; - } + prepare_sum3_net_lsa_body(p, nf, metric); - if ((en = ospf_hash_find_header(po->gr, oa->areaid, &lsa)) != NULL) - { - OSPF_TRACE(D_EVENTS, "Flushing summary-LSA (id=%R, type=%d)", - en->lsa.id, en->lsa.type); - - if ((type == ORT_NET) && check_sum_net_lsaid_collision(fn, en)) - { - log(L_ERR "%s: LSAID collision for %I/%d", - p->name, fn->prefix, fn->pxlen); - return; - } - - struct ospf_lsa_sum *sum = en->lsa_body; - en->lsa.age = LSA_MAXAGE; - en->lsa.sn = LSA_MAXSEQNO; - lsasum_calculate(&en->lsa, sum); - ospf_lsupd_flood(po, NULL, NULL, &en->lsa, oa->areaid, 1); - if (can_flush_lsa(po)) flush_lsa(en, po); - } + en = ospf_originate_lsa(p, &lsa); + en->rtcalc = LSA_RTCALC; } -#ifdef OSPFv2 - -static inline void * -originate_ext_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn, - u32 metric, ip_addr fwaddr, u32 tag, int pbit UNUSED) +void +ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric, u32 options) { - struct ospf_lsa_ext *ext = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_ext)); - *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_ext); + struct top_hash_entry *en; + u32 rid = ipa_to_rid(nf->fn.prefix); - ext->metric = metric; - ext->netmask = ipa_mkmask(fn->pxlen); - ext->fwaddr = fwaddr; - ext->tag = tag; + /* In OSPFv3, LSA ID is meaningless, but we still use Router ID of ASBR */ - return ext; + struct ospf_new_lsa lsa = { + .type = LSA_T_SUM_RT, + .dom = oa->areaid, + .id = rid, + .opts = oa->options + }; + + if (ospf_is_v2(p)) + prepare_sum2_lsa_body(p, 0, metric); + else + prepare_sum3_rt_lsa_body(p, rid, metric, options & LSA_OPTIONS_MASK); + + en = ospf_originate_lsa(p, &lsa); + en->rtcalc = LSA_RTCALC; } + /* - * check_ext_lsa() combines functions of check_*_lsaid_collision() and - * check_*_lsa_same(). 'en' is existing ext LSA, and rest parameters - * are parameters of new ext route. Function returns -1 if there is - * LSAID collision, returns 1 if the existing LSA is the same and - * returns 0 otherwise (in that case, we need to originate a new LSA). - * - * Really, checking for the same parameters is not as important as in - * summary LSA origination, because in most cases the duplicate - * external route propagation would be stopped by the nest. But there - * are still some cases (route reload, the same route propagated through - * different protocol) so it is also done here. + * AS-external-LSA and NSSA-LSA handling + * Type = LSA_T_EXT, LSA_T_NSSA */ -static inline int -check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_addr fwaddr, u32 tag) +static inline void +prepare_ext2_lsa_body(struct ospf_proto *p, uint pxlen, + u32 metric, u32 ebit, ip_addr fwaddr, u32 tag) { - struct ospf_lsa_ext *ext = en->lsa_body; + struct ospf_lsa_ext2 *ext; - /* LSAID collision */ - if (fn->pxlen != ipa_mklen(ext->netmask)) - return -1; + ext = lsab_allocz(p, sizeof(struct ospf_lsa_ext2)); + ext->metric = metric & LSA_METRIC_MASK; + ext->netmask = u32_mkmask(pxlen); + ext->fwaddr = ipa_to_u32(fwaddr); + ext->tag = tag; - return (en->lsa.sn != LSA_MAXSEQNO) && (ext->metric == metric) && - (ext->tag == tag) && ipa_equal(ext->fwaddr,fwaddr); + if (ebit) + ext->metric |= LSA_EXT2_EBIT; } -#else /* OSPFv3 */ - -static inline void * -originate_ext_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn, - u32 metric, ip_addr fwaddr, u32 tag, int pbit) +static inline void +prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf, + u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit) { - int size = sizeof(struct ospf_lsa_ext) - + IPV6_PREFIX_SPACE(fn->pxlen) + struct ospf_lsa_ext3 *ext; + int bsize = sizeof(struct ospf_lsa_ext3) + + IPV6_PREFIX_SPACE(nf->fn.pxlen) + (ipa_nonzero(fwaddr) ? 16 : 0) + (tag ? 4 : 0); - struct ospf_lsa_ext *ext = mb_alloc(po->proto.pool, size); - *length = sizeof(struct ospf_lsa_header) + size; - - ext->metric = metric; - + ext = lsab_allocz(p, bsize); + ext->metric = metric & LSA_METRIC_MASK; u32 *buf = ext->rest; - buf = put_ipv6_prefix(buf, fn->prefix, fn->pxlen, pbit ? OPT_PX_P : 0, 0); + + buf = put_ipv6_prefix(buf, nf->fn.prefix, nf->fn.pxlen, pbit ? OPT_PX_P : 0, 0); + + if (ebit) + ext->metric |= LSA_EXT3_EBIT; if (ipa_nonzero(fwaddr)) { - ext->metric |= LSA_EXT_FBIT; + ext->metric |= LSA_EXT3_FBIT; buf = put_ipv6_addr(buf, fwaddr); } if (tag) { - ext->metric |= LSA_EXT_TBIT; + ext->metric |= LSA_EXT3_TBIT; *buf++ = tag; } - - return ext; } -static inline int -check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_addr fwaddr, u32 tag) -{ - struct ospf_lsa_ext *ext = en->lsa_body; - ip_addr prefix; - int pxlen; - u8 pxopts; - u16 rest; - - u32 *buf = lsa_get_ipv6_prefix(ext->rest, &prefix, &pxlen, &pxopts, &rest); - - /* LSAID collision */ - if ((fn->pxlen != pxlen) || !ipa_equal(fn->prefix, prefix)) - return -1; - - if (en->lsa.sn == LSA_MAXSEQNO) - return 0; - - u32 rt_metric = ext->metric & METRIC_MASK; - ip_addr rt_fwaddr = IPA_NONE; - u32 rt_tag = 0; - - if (ext->metric & LSA_EXT_FBIT) - buf = lsa_get_ipv6_addr(buf, &rt_fwaddr); - - if (ext->metric & LSA_EXT_TBIT) - rt_tag = *buf++; - - return (rt_metric == metric) && ipa_equal(rt_fwaddr, fwaddr) && (rt_tag == tag); -} - - -#endif - -static inline ip_addr -find_surrogate_fwaddr(struct ospf_area *oa) -{ - struct proto_ospf *po = oa->po; - struct ospf_iface *ifa; - struct ifa *a, *cur_addr = NULL; - int np, cur_np = 0; - - WALK_LIST(ifa, po->iface_list) - { - if ((ifa->oa != oa) || - (ifa->type == OSPF_IT_VLINK)) - continue; - -#ifdef OSPFv2 - a = ifa->addr; - if (a->flags & IA_PEER) - continue; - - np = ((a->flags & IA_HOST) || ifa->stub) ? 2 : 1; - if (np > cur_np) - { - cur_addr = a; - cur_np = np; - } - -#else /* OSPFv3 */ - WALK_LIST(a, ifa->iface->addrs) - { - if ((a->flags & IA_SECONDARY) || - (a->flags & IA_PEER) || - (a->scope <= SCOPE_LINK)) - continue; - - np = ((a->flags & IA_HOST) || ifa->stub) ? 2 : 1; - if (np > cur_np) - { - cur_addr = a; - cur_np = np; - } - } -#endif - } - - return cur_addr ? cur_addr->ip : IPA_NONE; -} - - /** * originate_ext_lsa - new route received from nest and filters * @oa: ospf_area for which LSA is originated - * @fn: network prefix and mask + * @nf: network prefix and mask * @src: the source of origination of the LSA (EXT_EXPORT/EXT_NSSA) - * @metric: the metric of a route + * @metric: the metric of a route (possibly with appropriate E-bit) * @fwaddr: the forwarding address * @tag: the route tag * @pbit: P-bit for NSSA LSAs, ignored for external LSAs @@ -1064,214 +1089,266 @@ find_surrogate_fwaddr(struct ospf_area *oa) * @oa should not be a stub area. @src does not specify whether the LSA * is external or NSSA, but it specifies the source of origination - * the export from ospf_rt_notify(), or the NSSA-EXT translation. - * - * The function also sets flag ebit. If it's the first time, the new router lsa - * origination is necessary. */ void -originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src, - u32 metric, ip_addr fwaddr, u32 tag, int pbit) +ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 rtcalc, + u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit) { - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; - struct ospf_lsa_header lsa; - struct top_hash_entry *en = NULL; - void *body; - int nssa = oa_is_nssa(oa); - u32 dom = nssa ? oa->areaid : 0; + struct top_hash_entry *en; - OSPF_TRACE(D_EVENTS, "Originating %s-LSA for %I/%d", - nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen); + struct ospf_new_lsa lsa = { + .type = oa ? LSA_T_NSSA : LSA_T_EXT, + .dom = oa ? oa->areaid : 0, + .id = ort_to_lsaid(p, nf), + .opts = oa ? (pbit ? OPT_P : 0) : OPT_E, + .nf = nf + }; - lsa.age = 0; -#ifdef OSPFv2 - lsa.options = nssa ? (pbit ? OPT_P : 0) : OPT_E; -#endif - lsa.type = nssa ? LSA_T_NSSA : LSA_T_EXT; - lsa.id = fibnode_to_lsaid(po, fn); - lsa.rt = po->router_id; + if (ospf_is_v2(p)) + prepare_ext2_lsa_body(p, nf->fn.pxlen, metric, ebit, fwaddr, tag); + else + prepare_ext3_lsa_body(p, nf, metric, ebit, fwaddr, tag, oa && pbit); - if (nssa && pbit && ipa_zero(fwaddr)) + en = ospf_originate_lsa(p, &lsa); + en->rtcalc = rtcalc; +} + +static void +ospf_flush_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf) +{ + struct top_hash_entry *en; + + u32 type = oa ? LSA_T_NSSA : LSA_T_EXT; + u32 dom = oa ? oa->areaid : 0; + u32 id = ort_to_lsaid(p, nf); + + en = ospf_hash_find(p->gr, dom, id, p->router_id, type); + + if (!en || (en->nf != nf)) + return; + + ospf_flush_lsa(p, en); +} + +static inline int +use_gw_for_fwaddr(struct ospf_proto *p, ip_addr gw, struct iface *iface) +{ + struct ospf_iface *ifa; + + if (ipa_zero(gw) || ipa_is_link_local(gw)) + return 0; + + WALK_LIST(ifa, p->iface_list) + if ((ifa->iface == iface) && + ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA)) && + (!ospf_is_v2(p) || ipa_in_net(gw, ifa->addr->prefix, ifa->addr->pxlen)) && + (!ifa->cf->stub)) + return 1; + + return 0; +} + +static inline ip_addr +find_surrogate_fwaddr(struct ospf_proto *p, struct ospf_area *oa) +{ + struct ospf_iface *ifa; + struct ifa *a, *cur_addr = NULL; + int np, cur_np = 0; + + /* RFC 3101 2.3 - surrogate forwarding address selection */ + + WALK_LIST(ifa, p->iface_list) { - /* NSSA-LSA with P-bit set must have non-zero forwarding address */ + if ((ifa->oa != oa) || + (ifa->type == OSPF_IT_VLINK)) + continue; - fwaddr = find_surrogate_fwaddr(oa); - if (ipa_zero(fwaddr)) + if (ospf_is_v2(p)) { - log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %I/%d", - p->name, fn->prefix, fn->pxlen); - return; + a = ifa->addr; + if (a->flags & IA_PEER) + continue; + + np = (a->flags & IA_HOST) ? 3 : (ifa->stub ? 2 : 1); + if (np > cur_np) + { + cur_addr = a; + cur_np = np; + } + } + else /* OSPFv3 */ + { + WALK_LIST(a, ifa->iface->addrs) + { + if ((a->flags & IA_SECONDARY) || + (a->flags & IA_PEER) || + (a->scope <= SCOPE_LINK)) + continue; + + np = (a->flags & IA_HOST) ? 3 : (ifa->stub ? 2 : 1); + if (np > cur_np) + { + cur_addr = a; + cur_np = np; + } + } } } - if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL) - { - int rv = check_ext_lsa(en, fn, metric, fwaddr, tag); - if (rv < 0) - { - log(L_ERR "%s: LSAID collision for %I/%d", - p->name, fn->prefix, fn->pxlen); - return; - } - - if (rv > 0) - return; - } - lsa.sn = get_seqnum(en); - - body = originate_ext_lsa_body(po, &lsa.length, fn, metric, fwaddr, tag, pbit); - lsasum_calculate(&lsa, body); - - if (src) - fn->x1 = src; - - lsa_install_new(po, &lsa, dom, body); - ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); - - if (po->ebit == 0) - { - po->ebit = 1; - WALK_LIST(oa, po->area_list) - { - schedule_rt_lsa(oa); - } - } + return cur_addr ? cur_addr->ip : IPA_NONE; } void -flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int nssa) +ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *ea) { - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; - struct top_hash_entry *en; + struct ospf_proto *p = (struct ospf_proto *) P; + struct ospf_area *oa = NULL; /* non-NULL for NSSA-LSA */ + ort *nf; - u32 dom = nssa ? oa->areaid : 0; - u32 type = nssa ? LSA_T_NSSA : LSA_T_EXT; - u32 lsaid = fibnode_to_lsaid(po, fn); + /* + * There are several posibilities: + * 1) router in regular area - originate external LSA with global scope + * 2) router in NSSA area - originate area-specific NSSA-LSA + * 3) router in stub area - cannot export routes + * 4) area border router - same as (1), it is attached to backbone + */ - if (en = ospf_hash_find(po->gr, dom, lsaid, po->router_id, type)) + if ((p->areano == 1) && oa_is_nssa(HEAD(p->area_list))) + oa = HEAD(p->area_list); + + if (!new) + { + nf = (ort *) fib_find(&p->rtf, &n->n.prefix, n->n.pxlen); + + if (!nf || !nf->external_rte) + return; + + ospf_flush_ext_lsa(p, oa, nf); + nf->external_rte = 0; + + /* Old external route might blocked some NSSA translation */ + if ((p->areano > 1) && rt_is_nssa(nf) && nf->n.oa->translate) + schedule_rtcalc(p); + + return; + } + + ASSERT(p->asbr); + + /* Get route attributes */ + rta *a = new->attrs; + u32 m1 = ea_get_int(ea, EA_OSPF_METRIC1, LSINFINITY); + u32 m2 = ea_get_int(ea, EA_OSPF_METRIC2, 10000); + int ebit = (m1 == LSINFINITY); + u32 metric = ebit ? m2 : m1; + u32 tag = ea_get_int(ea, EA_OSPF_TAG, 0); + ip_addr fwd = IPA_NONE; + + + if ((a->dest == RTD_ROUTER) && use_gw_for_fwaddr(p, a->gw, a->iface)) + fwd = a->gw; + + /* NSSA-LSA with P-bit set must have non-zero forwarding address */ + if (oa && ipa_zero(fwd)) + { + fwd = find_surrogate_fwaddr(p, oa); + + if (ipa_zero(fwd)) { - OSPF_TRACE(D_EVENTS, "Flushing %s-LSA for %I/%d", - nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen); - - if (check_ext_lsa(en, fn, 0, IPA_NONE, 0) < 0) - { - log(L_ERR "%s: LSAID collision for %I/%d", - p->name, fn->prefix, fn->pxlen); - return; - } - - fn->x1 = 0; - ospf_lsupd_flush_nlsa(po, en); + log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %I/%d", + p->p.name, n->n.prefix, n->n.pxlen); + return; } + } + + nf = (ort *) fib_get(&p->rtf, &n->n.prefix, n->n.pxlen); + ospf_originate_ext_lsa(p, oa, nf, 0, metric, ebit, fwd, tag, 1); + nf->external_rte = 1; } -#ifdef OSPFv3 +/* + * Link-LSA handling (assume OSPFv3) + * Type = LSA_T_LINK + */ -static void * -originate_link_lsa_body(struct ospf_iface *ifa, u16 *length) +static inline void +lsab_put_prefix(struct ospf_proto *p, ip_addr prefix, u32 pxlen, u32 cost) +{ + void *buf = lsab_alloc(p, IPV6_PREFIX_SPACE(pxlen)); + u8 flags = (pxlen < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA; + put_ipv6_prefix(buf, prefix, pxlen, flags, cost); +} + +static void +prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa) { - struct proto_ospf *po = ifa->oa->po; struct ospf_lsa_link *ll; int i = 0; - u8 flags; - ASSERT(po->lsab_used == 0); - ll = lsab_allocz(po, sizeof(struct ospf_lsa_link)); + ASSERT(p->lsab_used == 0); + ll = lsab_allocz(p, sizeof(struct ospf_lsa_link)); ll->options = ifa->oa->options | (ifa->priority << 24); ll->lladdr = ifa->addr->ip; ll = NULL; /* buffer might be reallocated later */ struct ifa *a; WALK_LIST(a, ifa->iface->addrs) - { - if ((a->flags & IA_SECONDARY) || - (a->scope < SCOPE_SITE)) - continue; + { + if ((a->flags & IA_SECONDARY) || + (a->scope < SCOPE_SITE)) + continue; - flags = (a->pxlen < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA; - put_ipv6_prefix(lsab_alloc(po, IPV6_PREFIX_SPACE(a->pxlen)), - a->ip, a->pxlen, flags, 0); - i++; - } + lsab_put_prefix(p, a->prefix, a->pxlen, 0); + i++; + } - ll = po->lsab; + ll = p->lsab; ll->pxcount = i; - *length = po->lsab_used + sizeof(struct ospf_lsa_header); - return lsab_flush(po); } -void -originate_link_lsa(struct ospf_iface *ifa) +static void +ospf_originate_link_lsa(struct ospf_proto *p, struct ospf_iface *ifa) { - struct ospf_lsa_header lsa; - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; - void *body; - - /* Vlinks do not have link-LSAs */ - if (ifa->type == OSPF_IT_VLINK) + if (ospf_is_v2(p)) return; - OSPF_TRACE(D_EVENTS, "Originating link-LSA for iface %s", ifa->ifname); + struct ospf_new_lsa lsa = { + .type = LSA_T_LINK, + .dom = ifa->iface_id, + .id = ifa->iface_id, + .ifa = ifa + }; - lsa.age = 0; - lsa.type = LSA_T_LINK; - lsa.id = ifa->iface_id; - lsa.rt = po->router_id; - lsa.sn = get_seqnum(ifa->link_lsa); - u32 dom = ifa->iface_id; + prepare_link_lsa_body(p, ifa); - body = originate_link_lsa_body(ifa, &lsa.length); - lsasum_calculate(&lsa, body); - ifa->link_lsa = lsa_install_new(po, &lsa, dom, body); - ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); - - /* Just to be sure to not forget on our link LSA */ - if (ifa->state == OSPF_IS_DR) - schedule_net_lsa(ifa); + ifa->link_lsa = ospf_originate_lsa(p, &lsa); } -void -update_link_lsa(struct ospf_iface *ifa) -{ - if (ifa->link_lsa && ((ifa->link_lsa->inst_t + MINLSINTERVAL) > now)) - return; - /* - * It's too early to originate new link LSA. We will - * try to do it next tick - */ - originate_link_lsa(ifa); - ifa->origlink = 0; -} -static inline void -lsa_put_prefix(struct proto_ospf *po, ip_addr prefix, u32 pxlen, u32 cost) -{ - put_ipv6_prefix(lsab_alloc(po, IPV6_PREFIX_SPACE(pxlen)), prefix, pxlen, - (pxlen < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA, cost); -} +/* + * Prefix-Rt-LSA handling (assume OSPFv3) + * Type = LSA_T_PREFIX, referred type = LSA_T_RT + */ -static void * -originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length) +static void +prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa) { - struct proto_ospf *po = oa->po; - struct ospf_config *cf = (struct ospf_config *) (po->proto.cf); + struct ospf_config *cf = (struct ospf_config *) (p->p.cf); struct ospf_iface *ifa; struct ospf_lsa_prefix *lp; int host_addr = 0; int net_lsa; int i = 0; - ASSERT(po->lsab_used == 0); - lp = lsab_allocz(po, sizeof(struct ospf_lsa_prefix)); + ASSERT(p->lsab_used == 0); + lp = lsab_allocz(p, sizeof(struct ospf_lsa_prefix)); lp->ref_type = LSA_T_RT; lp->ref_id = 0; - lp->ref_rt = po->router_id; + lp->ref_rt = p->router_id; lp = NULL; /* buffer might be reallocated later */ - WALK_LIST(ifa, po->iface_list) + WALK_LIST(ifa, p->iface_list) { if ((ifa->oa != oa) || (ifa->type == OSPF_IT_VLINK) || (ifa->state == OSPF_IS_DOWN)) continue; @@ -1286,47 +1363,46 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length) struct ifa *a; WALK_LIST(a, ifa->iface->addrs) + { + if ((a->flags & IA_SECONDARY) || + (a->flags & IA_PEER) || + (a->scope <= SCOPE_LINK)) + continue; + + if (((a->pxlen < MAX_PREFIX_LENGTH) && net_lsa) || + configured_stubnet(oa, a)) + continue; + + if ((a->flags & IA_HOST) || + (ifa->state == OSPF_IS_LOOP) || + (ifa->type == OSPF_IT_PTMP)) { - if ((a->flags & IA_SECONDARY) || - (a->flags & IA_PEER) || - (a->scope <= SCOPE_LINK)) - continue; - - if (((a->pxlen < MAX_PREFIX_LENGTH) && net_lsa) || - configured_stubnet(oa, a)) - continue; - - if ((a->flags & IA_HOST) || - (ifa->state == OSPF_IS_LOOP) || - (ifa->type == OSPF_IT_PTMP)) - { - lsa_put_prefix(po, a->ip, MAX_PREFIX_LENGTH, 0); - host_addr = 1; - } - else - lsa_put_prefix(po, a->prefix, a->pxlen, ifa->cost); - i++; + lsab_put_prefix(p, a->ip, MAX_PREFIX_LENGTH, 0); + host_addr = 1; } + else + lsab_put_prefix(p, a->prefix, a->pxlen, ifa->cost); + i++; + } ifa->px_pos_end = i; } struct ospf_stubnet_config *sn; - if (oa->ac) - WALK_LIST(sn, oa->ac->stubnet_list) - if (!sn->hidden) - { - lsa_put_prefix(po, sn->px.addr, sn->px.len, sn->cost); - if (sn->px.len == MAX_PREFIX_LENGTH) - host_addr = 1; - i++; - } + WALK_LIST(sn, oa->ac->stubnet_list) + if (!sn->hidden) + { + lsab_put_prefix(p, sn->px.addr, sn->px.len, sn->cost); + if (sn->px.len == MAX_PREFIX_LENGTH) + host_addr = 1; + i++; + } /* If there are some configured vlinks, find some global address (even from another area), which will be used as a vlink endpoint. */ if (!EMPTY_LIST(cf->vlink_list) && !host_addr) { - WALK_LIST(ifa, po->iface_list) + WALK_LIST(ifa, p->iface_list) { if ((ifa->type == OSPF_IT_VLINK) || (ifa->state == OSPF_IS_DOWN)) continue; @@ -1338,7 +1414,7 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length) continue; /* Found some IP */ - lsa_put_prefix(po, a->ip, MAX_PREFIX_LENGTH, 0); + lsab_put_prefix(p, a->ip, MAX_PREFIX_LENGTH, 0); i++; goto done; } @@ -1346,36 +1422,33 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length) } done: - lp = po->lsab; + lp = p->lsab; lp->pxcount = i; - *length = po->lsab_used + sizeof(struct ospf_lsa_header); - return lsab_flush(po); } -void -originate_prefix_rt_lsa(struct ospf_area *oa) +static void +ospf_originate_prefix_rt_lsa(struct ospf_proto *p, struct ospf_area *oa) { - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; - struct ospf_lsa_header lsa; - void *body; + if (ospf_is_v2(p)) + return; - OSPF_TRACE(D_EVENTS, "Originating router prefix-LSA for area %R", oa->areaid); + struct ospf_new_lsa lsa = { + .type = LSA_T_PREFIX, + .dom = oa->areaid, + .id = 0 + }; - lsa.age = 0; - lsa.type = LSA_T_PREFIX; - lsa.id = 0; - lsa.rt = po->router_id; - lsa.sn = get_seqnum(oa->pxr_lsa); - u32 dom = oa->areaid; + prepare_prefix_rt_lsa_body(p, oa); - body = originate_prefix_rt_lsa_body(oa, &lsa.length); - lsasum_calculate(&lsa, body); - oa->pxr_lsa = lsa_install_new(po, &lsa, dom, body); - ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); + ospf_originate_lsa(p, &lsa); } +/* + * Prefix-Net-LSA handling (assume OSPFv3) + * Type = LSA_T_PREFIX, referred type = LSA_T_NET + */ + static inline int prefix_space(u32 *buf) { @@ -1410,9 +1483,9 @@ prefix_advance(u32 *buf) /* FIXME eliminate items with LA bit set? see 4.4.3.9 */ static void -add_prefix(struct proto_ospf *po, u32 *px, int offset, int *pxc) +add_prefix(struct ospf_proto *p, u32 *px, int offset, int *pxc) { - u32 *pxl = lsab_offset(po, offset); + u32 *pxl = lsab_offset(p, offset); int i; for (i = 0; i < *pxc; pxl = prefix_advance(pxl), i++) if (prefix_same(px, pxl)) @@ -1422,19 +1495,18 @@ add_prefix(struct proto_ospf *po, u32 *px, int offset, int *pxc) return; } - ASSERT(pxl == lsab_end(po)); + ASSERT(pxl == lsab_end(p)); int pxspace = prefix_space(px); - pxl = lsab_alloc(po, pxspace); + pxl = lsab_alloc(p, pxspace); memcpy(pxl, px, pxspace); *pxl &= 0xFFFF0000; /* Set metric to zero */ (*pxc)++; } static void -add_link_lsa(struct proto_ospf *po, struct top_hash_entry *en, int offset, int *pxc) +add_link_lsa(struct ospf_proto *p, struct ospf_lsa_link *ll, int offset, int *pxc) { - struct ospf_lsa_link *ll = en->lsa_body; u32 *pxb = ll->rest; int j; @@ -1451,92 +1523,110 @@ add_link_lsa(struct proto_ospf *po, struct top_hash_entry *en, int offset, int * if ((pxlen >= 10) && ((pxb[1] & 0xffc00000) == 0xfe800000)) continue; - add_prefix(po, pxb, offset, pxc); + add_prefix(p, pxb, offset, pxc); } } - - -static void * -originate_prefix_net_lsa_body(struct ospf_iface *ifa, u16 *length) +static void +prepare_prefix_net_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa) { - struct proto_ospf *po = ifa->oa->po; struct ospf_lsa_prefix *lp; struct ospf_neighbor *n; struct top_hash_entry *en; int pxc, offset; - ASSERT(po->lsab_used == 0); - lp = lsab_allocz(po, sizeof(struct ospf_lsa_prefix)); + ASSERT(p->lsab_used == 0); + lp = lsab_allocz(p, sizeof(struct ospf_lsa_prefix)); lp->ref_type = LSA_T_NET; lp->ref_id = ifa->net_lsa->lsa.id; - lp->ref_rt = po->router_id; + lp->ref_rt = p->router_id; lp = NULL; /* buffer might be reallocated later */ pxc = 0; - offset = po->lsab_used; + offset = p->lsab_used; /* Find all Link LSAs associated with the link and merge their prefixes */ - if (ifa->link_lsa) - add_link_lsa(po, ifa->link_lsa, offset, &pxc); + if (en = ifa->link_lsa) + add_link_lsa(p, en->next_lsa_body ?: en->lsa_body, offset, &pxc); WALK_LIST(n, ifa->neigh_list) if ((n->state == NEIGHBOR_FULL) && - (en = ospf_hash_find(po->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK))) - add_link_lsa(po, en, offset, &pxc); + (en = ospf_hash_find(p->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK))) + add_link_lsa(p, en->lsa_body, offset, &pxc); - lp = po->lsab; + lp = p->lsab; lp->pxcount = pxc; - *length = po->lsab_used + sizeof(struct ospf_lsa_header); - return lsab_flush(po); } -void -originate_prefix_net_lsa(struct ospf_iface *ifa) +static void +ospf_originate_prefix_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa) { - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; - struct ospf_lsa_header lsa; - void *body; - - OSPF_TRACE(D_EVENTS, "Originating network prefix-LSA for iface %s", ifa->ifname); - - lsa.age = 0; - lsa.type = LSA_T_PREFIX; - lsa.id = ifa->iface_id; - lsa.rt = po->router_id; - lsa.sn = get_seqnum(ifa->pxn_lsa); - u32 dom = ifa->oa->areaid; - - body = originate_prefix_net_lsa_body(ifa, &lsa.length); - lsasum_calculate(&lsa, body); - ifa->pxn_lsa = lsa_install_new(po, &lsa, dom, body); - ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); -} - -void -flush_prefix_net_lsa(struct ospf_iface *ifa) -{ - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; - struct top_hash_entry *en = ifa->pxn_lsa; - u32 dom = ifa->oa->areaid; - - if (en == NULL) + if (ospf_is_v2(p)) return; - OSPF_TRACE(D_EVENTS, "Flushing network prefix-LSA for iface %s", ifa->ifname); + struct ospf_new_lsa lsa = { + .type = LSA_T_PREFIX, + .dom = ifa->oa->areaid, + .id = ifa->iface_id, + .ifa = ifa + }; - en->lsa.sn += 1; - en->lsa.age = LSA_MAXAGE; - lsasum_calculate(&en->lsa, en->lsa_body); - ospf_lsupd_flood(po, NULL, NULL, &en->lsa, dom, 0); - flush_lsa(en, po); - ifa->pxn_lsa = NULL; + prepare_prefix_net_lsa_body(p, ifa); + + ifa->pxn_lsa = ospf_originate_lsa(p, &lsa); } -#endif +void +ospf_update_topology(struct ospf_proto *p) +{ + struct ospf_area *oa; + struct ospf_iface *ifa; + + WALK_LIST(oa, p->area_list) + { + if (oa->update_rt_lsa) + { + ospf_originate_rt_lsa(p, oa); + ospf_originate_prefix_rt_lsa(p, oa); + oa->update_rt_lsa = 0; + } + } + + WALK_LIST(ifa, p->iface_list) + { + if (ifa->type == OSPF_IT_VLINK) + continue; + + if (ifa->update_link_lsa) + { + if ((ifa->state > OSPF_IS_LOOP) && !ifa->link_lsa_suppression) + ospf_originate_link_lsa(p, ifa); + else + ospf_flush2_lsa(p, &ifa->link_lsa); + + ifa->update_link_lsa = 0; + } + + if (ifa->update_net_lsa) + { + if ((ifa->state == OSPF_IS_DR) && (ifa->fadj > 0)) + { + ospf_originate_net_lsa(p, ifa); + ospf_originate_prefix_net_lsa(p, ifa); + } + else + { + ospf_flush2_lsa(p, &ifa->net_lsa); + ospf_flush2_lsa(p, &ifa->pxn_lsa); + } + + ifa->update_net_lsa = 0; + } + } + + // XXXX schedule_rtcalc(p); +} static void @@ -1574,7 +1664,7 @@ ospf_top_hash_u32(u32 a) return a; } -static inline unsigned +static unsigned ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type) { /* In OSPFv2, we don't know Router ID when looking for network LSAs. @@ -1582,14 +1672,8 @@ ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type) In both cases, there is (usually) just one (or small number) appropriate LSA, so we just clear unknown part of key. */ - return ( -#ifdef OSPFv2 - ((type == LSA_T_NET) ? 0 : ospf_top_hash_u32(rtrid)) + - ospf_top_hash_u32(lsaid) + -#else /* OSPFv3 */ - ospf_top_hash_u32(rtrid) + - ((type == LSA_T_RT) ? 0 : ospf_top_hash_u32(lsaid)) + -#endif + return (((f->ospf2 && (type == LSA_T_NET)) ? 0 : ospf_top_hash_u32(rtrid)) + + ((!f->ospf2 && (type == LSA_T_RT)) ? 0 : ospf_top_hash_u32(lsaid)) + type + domain) & f->hash_mask; /* @@ -1600,7 +1684,7 @@ ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type) /** * ospf_top_new - allocated new topology database - * @p: current instance of ospf + * @p: OSPF protocol instance * * this dynamically hashed structure is often used for keeping lsas. mainly * its used in @ospf_area structure. @@ -1648,7 +1732,7 @@ ospf_top_rehash(struct top_graph *f, int step) while (e) { x = e->next; - n = newt + ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa.type); + n = newt + ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa_type); e->next = *n; *n = e; e = x; @@ -1657,124 +1741,82 @@ ospf_top_rehash(struct top_graph *f, int step) ospf_top_ht_free(oldt); } -#ifdef OSPFv2 - -u32 -ospf_lsa_domain(u32 type, struct ospf_iface *ifa) -{ - return (type == LSA_T_EXT) ? 0 : ifa->oa->areaid; -} - -#else /* OSPFv3 */ - -u32 -ospf_lsa_domain(u32 type, struct ospf_iface *ifa) -{ - switch (type & LSA_SCOPE_MASK) - { - case LSA_SCOPE_LINK: - return ifa->iface_id; - - case LSA_SCOPE_AREA: - return ifa->oa->areaid; - - case LSA_SCOPE_AS: - default: - return 0; - } -} - -#endif - -struct top_hash_entry * -ospf_hash_find_header(struct top_graph *f, u32 domain, struct ospf_lsa_header *h) -{ - return ospf_hash_find(f, domain, h->id, h->rt, h->type); -} - -struct top_hash_entry * -ospf_hash_get_header(struct top_graph *f, u32 domain, struct ospf_lsa_header *h) -{ - return ospf_hash_get(f, domain, h->id, h->rt, h->type); -} - struct top_hash_entry * ospf_hash_find(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type) { struct top_hash_entry *e; e = f->hash_table[ospf_top_hash(f, domain, lsa, rtr, type)]; - while (e && (e->lsa.id != lsa || e->lsa.type != type || e->lsa.rt != rtr || e->domain != domain)) + while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || + e->lsa_type != type || e->domain != domain)) e = e->next; - return e; + /* Hide hash entry with empty lsa_body */ + return e->lsa_body ? e : NULL; } - -#ifdef OSPFv2 - -/* In OSPFv2, sometimes we don't know Router ID when looking for network LSAs. - There should be just one, so we find any match. */ -struct top_hash_entry * -ospf_hash_find_net(struct top_graph *f, u32 domain, u32 lsa) -{ - struct top_hash_entry *e; - e = f->hash_table[ospf_top_hash(f, domain, lsa, 0, LSA_T_NET)]; - - while (e && (e->lsa.id != lsa || e->lsa.type != LSA_T_NET || e->domain != domain)) - e = e->next; - - return e; -} - -#endif - - -#ifdef OSPFv3 - -/* In OSPFv3, usually we don't know LSA ID when looking for router - LSAs. We return matching LSA with smallest LSA ID. */ +/* In OSPFv2, lsa.id is the same as lsa.rt for router LSA. In OSPFv3, we don't know + lsa.id when looking for router LSAs. We return matching LSA with smallest lsa.id. */ struct top_hash_entry * ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr) { struct top_hash_entry *rv = NULL; struct top_hash_entry *e; - e = f->hash_table[ospf_top_hash(f, domain, 0, rtr, LSA_T_RT)]; - + /* We can put rtr for lsa.id to hash fn, it is ignored in OSPFv3 */ + e = f->hash_table[ospf_top_hash(f, domain, rtr, rtr, LSA_T_RT)]; + while (e) + { + if (e->lsa.rt == rtr && e->lsa_type == LSA_T_RT && e->domain == domain && e->lsa_body) { - if (e->lsa.rt == rtr && e->lsa.type == LSA_T_RT && e->domain == domain) - if (!rv || e->lsa.id < rv->lsa.id) - rv = e; - e = e->next; + if (f->ospf2 && (e->lsa.id == rtr)) + return e; + if (!f->ospf2 && (!rv || e->lsa.id < rv->lsa.id)) + rv = e; } + e = e->next; + } return rv; } static inline struct top_hash_entry * -find_matching_rt(struct top_hash_entry *e, u32 domain, u32 rtr) +find_matching_rt3(struct top_hash_entry *e, u32 domain, u32 rtr) { - while (e && (e->lsa.rt != rtr || e->lsa.type != LSA_T_RT || e->domain != domain)) + while (e && (e->lsa.rt != rtr || e->lsa_type != LSA_T_RT || + e->domain != domain || e->lsa_body == NULL)) e = e->next; return e; } struct top_hash_entry * -ospf_hash_find_rt_first(struct top_graph *f, u32 domain, u32 rtr) +ospf_hash_find_rt3_first(struct top_graph *f, u32 domain, u32 rtr) { struct top_hash_entry *e; e = f->hash_table[ospf_top_hash(f, domain, 0, rtr, LSA_T_RT)]; - return find_matching_rt(e, domain, rtr); + return find_matching_rt3(e, domain, rtr); } struct top_hash_entry * -ospf_hash_find_rt_next(struct top_hash_entry *e) +ospf_hash_find_rt3_next(struct top_hash_entry *e) { - return find_matching_rt(e->next, e->domain, e->lsa.rt); + return find_matching_rt3(e->next, e->domain, e->lsa.rt); } -#endif +/* In OSPFv2, we don't know Router ID when looking for network LSAs. + There should be just one, so we find any match. */ +struct top_hash_entry * +ospf_hash_find_net2(struct top_graph *f, u32 domain, u32 id) +{ + struct top_hash_entry *e; + e = f->hash_table[ospf_top_hash(f, domain, id, 0, LSA_T_NET)]; + + while (e && (e->lsa.id != id || e->lsa_type != LSA_T_NET || + e->domain != domain || e->lsa_body == NULL)) + e = e->next; + + return e; +} struct top_hash_entry * @@ -1786,21 +1828,23 @@ ospf_hash_get(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type) ee = f->hash_table + ospf_top_hash(f, domain, lsa, rtr, type); e = *ee; - while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || e->lsa.type != type || e->domain != domain)) + while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || + e->lsa_type != type || e->domain != domain)) e = e->next; if (e) return e; e = sl_alloc(f->hash_slab); + bzero(e, sizeof(struct top_hash_entry)); + e->color = OUTSPF; e->dist = LSINFINITY; - e->nhs = NULL; - e->lb = IPA_NONE; + e->lsa.type_raw = type; e->lsa.id = lsa; e->lsa.rt = rtr; - e->lsa.type = type; - e->lsa_body = NULL; + e->lsa.sn = LSA_ZEROSEQNO; + e->lsa_type = type; e->domain = domain; e->next = *ee; *ee = e; @@ -1813,7 +1857,7 @@ void ospf_hash_delete(struct top_graph *f, struct top_hash_entry *e) { struct top_hash_entry **ee = f->hash_table + - ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa.type); + ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa_type); while (*ee) { @@ -1885,29 +1929,114 @@ ospf_top_dump(struct top_graph *f, struct proto *p) } */ -/* This is very inefficient, please don't call it often */ -/* I should also test for every LSA if it's in some link state - * retransmission list for every neighbor. I will not test it. - * It could happen that I'll receive some strange ls ack's. - */ -int -can_flush_lsa(struct proto_ospf *po) +#if 0 + +void +update_rt_lsa(struct ospf_area *oa) { - struct ospf_iface *ifa; - struct ospf_neighbor *n; + struct ospf_proto *po = oa->po; - WALK_LIST(ifa, po->iface_list) - { - WALK_LIST(n, ifa->neigh_list) - { - if ((n->state == NEIGHBOR_EXCHANGE) || (n->state == NEIGHBOR_LOADING)) - return 0; + if ((oa->rt) && ((oa->rt->inst_t + MINLSINTERVAL)) > now) + return; - break; - } - } + originate_rt_lsa(oa); + if (ospf_is_v3(p)) + originate_prefix_rt_lsa(oa); - return 1; + schedule_rtcalc(p); + oa->origrt = 0; } + + + + +static inline int +check_sum2_net_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric) +{ + struct ospf_lsa_sum2 *sum = en->lsa_body; + + if (fn->pxlen != ip4_masklen(sum->netmask)) + return -1; + + return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric); +} + +static inline int +check_sum3_net_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric) +{ + struct ospf_lsa_sum3_net *sum = en->lsa_body; + ip6_addr prefix; + int pxlen; + u8 pxopts; + u16 rest; + lsa_get_ipv6_prefix(sum->prefix, &prefix, &pxlen, &pxopts, &rest); + + + if ((fn->pxlen != pxlen) || !ip6_equal(fn->prefix, prefix)) + return -1; + + return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric); +} + + +static int +check_sum_net_lsa(struct ospf_proto *po, struct top_hash_entry *en, struct fib_node *fn, u32 metric) +{ + int rv = ospf_is_v2(po) ? + check_sum2_net_lsa(en, fn, metric) : + check_sum3_net_lsa(en, fn, metric); + + if (rv < 0) + log(L_ERR "%s: LSAID collision for %I/%d", p->p.name, fn->prefix, fn->pxlen); + + return rv; +} + +static int +check_sum_rt_lsa(struct ospf_proto *po, struct top_hash_entry *en, u32 drid, u32 metric, u32 options) +{ + if (en->lsa.sn == LSA_MAXSEQNO) + return 0; + + if (ospf_is_v2(po)) + { + struct ospf_lsa_sum2 *sum = en->lsa_body; + return (sum->metric == metric); + } + else + { + struct ospf_lsa_sum3_rt *sum = en->lsa_body; + return (sum->options == options) && (sum->metric == metric) && (sum->drid == drid); + } +} + + + + + + + + OSPF_TRACE(D_EVENTS, "Originating router-LSA for area %R", oa->areaid); + OSPF_TRACE(D_EVENTS, "Originating network-LSA for iface %s", ifa->ifname); + OSPF_TRACE(D_EVENTS, "Originating net-summary-LSA for %I/%d (metric %d)", fn->prefix, fn->pxlen, metric); + OSPF_TRACE(D_EVENTS, "Originating rt-summary-LSA for %R (metric %d)", rid, metric); + OSPF_TRACE(D_EVENTS, "Originating %s-LSA for %I/%d", + nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen); + OSPF_TRACE(D_EVENTS, "Originating link-LSA for iface %s", ifa->ifname); + OSPF_TRACE(D_EVENTS, "Originating router prefix-LSA for area %R", oa->areaid); + OSPF_TRACE(D_EVENTS, "Originating network prefix-LSA for iface %s", ifa->ifname); + + + en = ospf_hash_find(po->gr, lsa.dom, lsa.id, po->router_id, lsa.type); + if (en && check_ext_lsa(po, en, fn, metric, fwaddr, tag)) + return; + + *length = sizeof(struct ospf_lsa_header) + po->lsab_used; + return lsab_flush(po); + + *length = po->lsab_used + sizeof(struct ospf_lsa_header); + return lsab_flush(po); + +#endif diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h index cb876487..e4ea79f7 100644 --- a/proto/ospf/topology.h +++ b/proto/ospf/topology.h @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -16,79 +18,102 @@ struct top_hash_entry in intra-area routing table calculation */ struct top_hash_entry *next; /* Next in hash chain */ struct ospf_lsa_header lsa; + u16 lsa_type; /* lsa.type processed and converted to common values */ + u16 init_age; /* Initial value for lsa.age during inst_time */ u32 domain; /* Area ID for area-wide LSAs, Iface ID for link-wide LSAs */ // struct ospf_area *oa; - void *lsa_body; - bird_clock_t inst_t; /* Time of installation into DB */ + void *lsa_body; /* May be NULL if LSA was flushed but hash entry was kept */ + void *next_lsa_body; /* For postponed LSA origination */ + u16 next_lsa_blen; /* For postponed LSA origination */ + u16 next_lsa_opts; /* For postponed LSA origination */ + bird_clock_t inst_time; /* Time of installation into DB */ + struct ort *nf; /* Reference fibnode for sum and ext LSAs, NULL for otherwise */ struct mpnh *nhs; /* Computed nexthops - valid only in ospf_rt_spf() */ ip_addr lb; /* In OSPFv2, link back address. In OSPFv3, any global address in the area useful for vlinks */ -#ifdef OSPFv3 u32 lb_id; /* Interface ID of link back iface (for bcast or NBMA networks) */ -#endif u32 dist; /* Distance from the root */ - u16 ini_age; + int ret_count; /* Number of retransmission lists referencing the entry */ u8 color; #define OUTSPF 0 #define CANDIDATE 1 #define INSPF 2 + u8 rtcalc; /* LSA generated during RT calculation (LSA_RTCALC or LSA_STALE)*/ u8 nhs_reuse; /* Whether nhs nodes can be reused during merging. See a note in rt.c:merge_nexthops() */ }; +#define LSA_RTCALC 1 +#define LSA_STALE 2 + struct top_graph { pool *pool; /* Pool we allocate from */ slab *hash_slab; /* Slab for hash entries */ struct top_hash_entry **hash_table; /* Hashing (modelled a`la fib) */ - unsigned int hash_size; - unsigned int hash_order; - unsigned int hash_mask; - unsigned int hash_entries; - unsigned int hash_entries_min, hash_entries_max; + uint ospf2; /* Whether it is for OSPFv2 or OSPFv3 */ + uint hash_size; + uint hash_order; + uint hash_mask; + uint hash_entries; + uint hash_entries_min, hash_entries_max; +}; + +struct ospf_new_lsa +{ + u16 type; + u32 dom; + u32 id; + u16 opts; + u16 length; + struct ospf_iface *ifa; + struct ort *nf; }; struct top_graph *ospf_top_new(pool *); void ospf_top_free(struct top_graph *); void ospf_top_dump(struct top_graph *, struct proto *); -u32 ospf_lsa_domain(u32 type, struct ospf_iface *ifa); -struct top_hash_entry *ospf_hash_find_header(struct top_graph *f, u32 domain, - struct ospf_lsa_header *h); -struct top_hash_entry *ospf_hash_get_header(struct top_graph *f, u32 domain, - struct ospf_lsa_header *h); -struct top_hash_entry *ospf_hash_find(struct top_graph *, u32 domain, u32 lsa, u32 rtr, - u32 type); -struct top_hash_entry *ospf_hash_get(struct top_graph *, u32 domain, u32 lsa, u32 rtr, - u32 type); +struct top_hash_entry * ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body); +struct top_hash_entry * ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa); +void ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body); +void ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en); +void ospf_update_lsadb(struct ospf_proto *p); + +static inline void ospf_flush2_lsa(struct ospf_proto *p, struct top_hash_entry **en) +{ if (*en) { ospf_flush_lsa(p, *en); *en = NULL; } } + +void ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric); +void ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric, u32 options); +void ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 rtcalc, u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit); + +void ospf_rt_notify(struct proto *P, rtable *tbl, net *n, rte *new, rte *old, ea_list *attrs); +void ospf_update_topology(struct ospf_proto *p); + +struct top_hash_entry *ospf_hash_find(struct top_graph *, u32 domain, u32 lsa, u32 rtr, u32 type); +struct top_hash_entry *ospf_hash_get(struct top_graph *, u32 domain, u32 lsa, u32 rtr, u32 type); void ospf_hash_delete(struct top_graph *, struct top_hash_entry *); -void originate_rt_lsa(struct ospf_area *oa); -void update_rt_lsa(struct ospf_area *oa); -void originate_net_lsa(struct ospf_iface *ifa); -void update_net_lsa(struct ospf_iface *ifa); -void update_link_lsa(struct ospf_iface *ifa); -int can_flush_lsa(struct proto_ospf *po); -void originate_sum_net_lsa(struct ospf_area *oa, struct fib_node *fn, int metric); -void originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32 options UNUSED); -void flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type); -void originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src, u32 metric, ip_addr fwaddr, u32 tag, int pbit); -void flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int nssa); +static inline struct top_hash_entry * ospf_hash_find_entry(struct top_graph *f, struct top_hash_entry *en) +{ return ospf_hash_find(f, en->domain, en->lsa.id, en->lsa.rt, en->lsa_type); } +static inline struct top_hash_entry * ospf_hash_get_entry(struct top_graph *f, struct top_hash_entry *en) +{ return ospf_hash_get(f, en->domain, en->lsa.id, en->lsa.rt, en->lsa_type); } -#ifdef OSPFv2 -struct top_hash_entry * ospf_hash_find_net(struct top_graph *f, u32 domain, u32 lsa); - -static inline struct top_hash_entry * -ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr) -{ - return ospf_hash_find(f, domain, rtr, rtr, LSA_T_RT); -} - -#else /* OSPFv3 */ struct top_hash_entry * ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr); -struct top_hash_entry * ospf_hash_find_rt_first(struct top_graph *f, u32 domain, u32 rtr); -struct top_hash_entry * ospf_hash_find_rt_next(struct top_hash_entry *e); -#endif +struct top_hash_entry * ospf_hash_find_rt3_first(struct top_graph *f, u32 domain, u32 rtr); +struct top_hash_entry * ospf_hash_find_rt3_next(struct top_hash_entry *e); + +struct top_hash_entry * ospf_hash_find_net2(struct top_graph *f, u32 domain, u32 id); + +/* In OSPFv2, id is network IP prefix (lsa.id) while lsa.rt field is unknown + In OSPFv3, id is lsa.rt of DR while nif is neighbor iface id (lsa.id) */ +static inline struct top_hash_entry * +ospf_hash_find_net(struct top_graph *f, u32 domain, u32 id, u32 nif) +{ + return f->ospf2 ? + ospf_hash_find_net2(f, domain, id) : + ospf_hash_find(f, domain, nif, id, LSA_T_NET); +} #endif /* _BIRD_OSPF_TOPOLOGY_H_ */ From a7a7372aa7c527619ee527e3b37013f9fb87d618 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 18 Jul 2014 18:24:12 +0200 Subject: [PATCH 02/11] Temporary integrated OSPF commit. --- nest/locks.c | 10 +- nest/locks.h | 5 +- proto/ospf/dbdes.c | 19 +- proto/ospf/hello.c | 2 +- proto/ospf/iface.c | 17 +- proto/ospf/lsack.c | 10 +- proto/ospf/lsreq.c | 3 +- proto/ospf/lsupd.c | 84 ++++--- proto/ospf/neighbor.c | 59 +++-- proto/ospf/ospf.c | 170 +++++++------- proto/ospf/ospf.h | 533 +++++++++++++++++++++--------------------- proto/ospf/rt.c | 43 ++-- proto/ospf/rt.h | 57 ++--- proto/ospf/topology.c | 340 ++++++++++++--------------- proto/ospf/topology.h | 118 +++++++++- 15 files changed, 779 insertions(+), 691 deletions(-) diff --git a/nest/locks.c b/nest/locks.c index 7044d6a9..c74f2f45 100644 --- a/nest/locks.c +++ b/nest/locks.c @@ -22,10 +22,11 @@ * or some other non-shareable resource, it asks the core to lock it and it doesn't * use the resource until it's notified that it has acquired the lock. * - * Object locks are represented by &object_lock structures which are in turn a kind of - * resource. Lockable resources are uniquely determined by resource type + * Object locks are represented by &object_lock structures which are in turn a + * kind of resource. Lockable resources are uniquely determined by resource type * (%OBJLOCK_UDP for a UDP port etc.), IP address (usually a broadcast or - * multicast address the port is bound to), port number and interface. + * multicast address the port is bound to), port number, interface and optional + * instance ID. */ #undef LOCAL_DEBUG @@ -45,6 +46,7 @@ olock_same(struct object_lock *x, struct object_lock *y) x->type == y->type && x->iface == y->iface && x->port == y->port && + x->inst == y->inst && ipa_equal(x->addr, y->addr); } @@ -88,7 +90,7 @@ olock_dump(resource *r) struct object_lock *l = (struct object_lock *) r; static char *olock_states[] = { "free", "locked", "waiting", "event" }; - debug("(%d:%s:%I:%d) [%s]\n", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->port, olock_states[l->state]); + debug("(%d:%s:%I:%d:%d) [%s]\n", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->port, l->inst, olock_states[l->state]); if (!EMPTY_LIST(l->waiters)) debug(" [wanted]\n"); } diff --git a/nest/locks.h b/nest/locks.h index 892d3c6b..3d58c8ed 100644 --- a/nest/locks.h +++ b/nest/locks.h @@ -26,9 +26,10 @@ struct object_lock { resource r; ip_addr addr; /* Identification of a object: IP address */ - unsigned int type; /* ... object type (OBJLOCK_xxx) */ + uint type; /* ... object type (OBJLOCK_xxx) */ + uint port; /* ... port number */ + uint inst; /* ... instance ID */ struct iface *iface; /* ... interface */ - unsigned int port; /* ... port number */ void (*hook)(struct object_lock *); /* Called when the lock succeeds */ void *data; /* User data */ /* ... internal to lock manager, don't touch ... */ diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c index 1f37965c..62b330ed 100644 --- a/proto/ospf/dbdes.c +++ b/proto/ospf/dbdes.c @@ -200,15 +200,11 @@ ospf_do_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n) * of the buffer. */ void -ospf_send_dbdes(struct ospf_neighbor *n, int next) +ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n, int next) { - struct ospf_iface *ifa = n->ifa; - struct ospf_area *oa = ifa->oa; - struct ospf_proto *p = oa->po; - /* RFC 2328 10.8 */ - if (oa->rt == NULL) + if (n->ifa->oa->rt == NULL) return; switch (n->state) @@ -312,6 +308,7 @@ ospf_process_dbdes(struct ospf_proto *p, struct ospf_packet *pkt, struct ospf_ne s_add_tail(&n->lsrql, SNODE req); req->lsa = lsa; + req->lsa_body = LSA_BODY_DUMMY; } } @@ -394,7 +391,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, n->imms = rcv_imms; OSPF_TRACE(D_PACKETS, "I'm slave to %I", n->ip); ospf_neigh_sm(n, INM_NEGDONE); - ospf_send_dbdes(n, 1); + ospf_send_dbdes(p, n, 1); break; } @@ -426,7 +423,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, if (!(n->myimms & DBDES_MS)) { /* Slave should retransmit dbdes packet */ - ospf_send_dbdes(n, 0); + ospf_send_dbdes(p, n, 0); } return; } @@ -472,7 +469,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, if (!(n->myimms & DBDES_M) && !(n->imms & DBDES_M)) ospf_neigh_sm(n, INM_EXDONE); else - ospf_send_dbdes(n, 1); + ospf_send_dbdes(p, n, 1); } else { @@ -489,7 +486,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, if (ospf_process_dbdes(p, pkt, n) < 0) return; - ospf_send_dbdes(n, 1); + ospf_send_dbdes(p, n, 1); } break; @@ -504,7 +501,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, if (!(n->myimms & DBDES_MS)) { /* Slave should retransmit dbdes packet */ - ospf_send_dbdes(n, 0); + ospf_send_dbdes(p, n, 0); } return; } diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index 376eac3c..50cd8609 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -275,7 +275,7 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, /* Check consistency of existing neighbor entry */ if (n) { - unsigned t = ifa->type; + uint t = ifa->type; if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP))) { /* Neighbor identified by IP address; Router ID may change */ diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 5e17371d..312e626a 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -620,18 +620,11 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i add_tail(&oa->po->iface_list, NODE ifa); - /* - * In some cases we allow more ospf_ifaces on one physical iface. - * In OSPFv2, if they use different IP address prefix. - * In OSPFv3, if they use different instance_id. - * Therefore, we store such info to lock->addr field. - */ - - // XXXX review struct object_lock *lock = olock_new(pool); - lock->addr = ospf_is_v2(p) ? ifa->addr->prefix : _MI6(0,0,0,ifa->instance_id); + lock->addr = ospf_is_v2(p) ? ifa->addr->prefix : IPA_NONE; lock->type = OBJLOCK_IP; lock->port = OSPF_PROTO; + lock->inst = ifa->instance_id; lock->iface = iface; lock->data = ifa; lock->hook = ospf_iface_add; @@ -997,7 +990,7 @@ ospf_walk_matching_iface_patts(struct ospf_proto *p, struct ospf_mip_walk *s) BIT32_SET(s->ignore, id); /* If we already found it in previous areas, ignore it and add warning */ - if (!BIT32_TEST(s->active, id)) + if (BIT32_TEST(s->active, id)) { s->warn = 1; continue; } BIT32_SET(s->active, id); @@ -1046,7 +1039,7 @@ ospf_ifa_notify2(struct proto *P, uint flags, struct ifa *a) { struct ospf_mip_walk s = { .iface = a->iface, .a = a }; while (ospf_walk_matching_iface_patts(p, &s)) - ospf_iface_new(s.oa, s.a, s.ip); + ospf_iface_new(s.oa, a, s.ip); } if (flags & IF_CHANGE_DOWN) @@ -1078,7 +1071,7 @@ ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a) { struct ospf_mip_walk s = { .iface = a->iface }; while (ospf_walk_matching_iface_patts(p, &s)) - ospf_iface_new(s.oa, s.a, s.ip); + ospf_iface_new(s.oa, a, s.ip); } if (flags & IF_CHANGE_DOWN) diff --git a/proto/ospf/lsack.c b/proto/ospf/lsack.c index aefddfb8..5cac3f69 100644 --- a/proto/ospf/lsack.c +++ b/proto/ospf/lsack.c @@ -79,10 +79,9 @@ ospf_reset_lsack_queue(struct ospf_neighbor *n) } static inline void -ospf_send_lsack(struct ospf_neighbor *n, int queue) +ospf_send_lsack_(struct ospf_proto *p, struct ospf_neighbor *n, int queue) { struct ospf_iface *ifa = n->ifa; - struct ospf_proto *p = ifa->oa->po; struct ospf_lsa_header *lsas; struct ospf_packet *pkt; struct lsa_node *no; @@ -121,10 +120,10 @@ ospf_send_lsack(struct ospf_neighbor *n, int queue) } void -ospf_lsack_send(struct ospf_neighbor *n, int queue) +ospf_send_lsack(struct ospf_proto *p, struct ospf_neighbor *n, int queue) { while (!EMPTY_LIST(n->ackl[queue])) - ospf_send_lsack(n, queue); + ospf_send_lsack_(p, n, queue); } void @@ -160,9 +159,6 @@ ospf_receive_lsack(struct ospf_packet *pkt, struct ospf_iface *ifa, if (lsa_comp(&lsa, &ret->lsa) != CMP_SAME) { - if ((lsa.sn == LSA_MAXSEQNO) && (lsa.age == LSA_MAXAGE)) - continue; - OSPF_TRACE(D_PACKETS, "Strange LSACK from %I", n->ip); OSPF_TRACE(D_PACKETS, "Type: %04x, Id: %R, Rt: %R", lsa_type, lsa.id, lsa.rt); diff --git a/proto/ospf/lsreq.c b/proto/ospf/lsreq.c index 8888f88e..1685ef13 100644 --- a/proto/ospf/lsreq.c +++ b/proto/ospf/lsreq.c @@ -71,7 +71,8 @@ ospf_send_lsreq(struct ospf_proto *p, struct ospf_neighbor *n) ospf_pkt_fill_hdr(ifa, pkt, LSREQ_P); ospf_lsreq_body(p, pkt, &lsrs, &lsr_max); - // for (i = 0; i < lsr_max; i++) + /* We send smaller LSREQ to prevent multiple LSACKs as answer */ + lsr_max = lsr_max / 4; i = 0; WALK_SLIST(en, n->lsrql) diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index b0bf21cd..37c89a24 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -115,7 +115,8 @@ ospf_lsa_lsrt_up(struct top_hash_entry *en, struct ospf_neighbor *n) s_add_tail(&n->lsrtl, SNODE ret); } - memcpy(&ret->lsa, &en->lsa, sizeof(struct ospf_lsa_header)); + ret->lsa = en->lsa; + ret->lsa_body = LSA_BODY_DUMMY; } static inline int @@ -134,25 +135,37 @@ ospf_lsa_lsrt_down(struct top_hash_entry *en, struct ospf_neighbor *n) return 0; } +void +ospf_add_flushed_to_lsrt(struct ospf_proto *p, struct ospf_neighbor *n) +{ + struct top_hash_entry *en; -static void ospf_lsupd_flood_ifa(struct ospf_proto *p, struct ospf_iface *ifa, struct top_hash_entry *en); + WALK_SLIST(en, p->lsal) + if ((en->lsa.age == LSA_MAXAGE) && (en->lsa_body != NULL) && + lsa_flooding_allowed(en->lsa_type, en->domain, n->ifa)) + ospf_lsa_lsrt_up(en, n); +} + + +static void ospf_send_lsupd_to_ifa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_iface *ifa); /** - * ospf_lsupd_flood - send received or generated LSA to the neighbors - * @p: OSPF protocol + * ospf_flood_lsa - send LSA to the neighbors + * @p: OSPF protocol instance * @en: LSA entry * @from: neighbor than sent this LSA (or NULL if LSA is local) * * return value - was the LSA flooded back? */ - int -ospf_lsupd_flood(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neighbor *from) +ospf_flood_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neighbor *from) { struct ospf_iface *ifa; struct ospf_neighbor *n; + /* RFC 2328 13.3 */ + int back = 0; WALK_LIST(ifa, p->iface_list) { @@ -185,6 +198,8 @@ ospf_lsupd_flood(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ne { s_rem_node(SNODE req); ospf_hash_delete(n->lsrqh, req); + n->want_lsreq = 1; + if ((EMPTY_SLIST(n->lsrql)) && (n->state == NEIGHBOR_LOADING)) ospf_neigh_sm(n, INM_LOADDONE); } @@ -227,7 +242,7 @@ ospf_lsupd_flood(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ne } /* 13.3 (5) - finally flood the packet */ - ospf_lsupd_flood_ifa(p, ifa, en); + ospf_send_lsupd_to_ifa(p, en, ifa); } return back; @@ -288,7 +303,7 @@ ospf_prepare_lsupd(struct ospf_proto *p, struct ospf_iface *ifa, static void -ospf_lsupd_flood_ifa(struct ospf_proto *p, struct ospf_iface *ifa, struct top_hash_entry *en) +ospf_send_lsupd_to_ifa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_iface *ifa) { uint c = ospf_prepare_lsupd(p, ifa, &en, 1); @@ -384,7 +399,8 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, /* RFC 2328 13. */ - int sendreq = 1; /* XXXX: review sendreq */ + int skip_lsreq = 0; + n->want_lsreq = 0; uint plen = ntohs(pkt->length); if (plen < (ospf_lsupd_hdrlen(p) + sizeof(struct ospf_lsa_header))) @@ -436,7 +452,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, u16 chsum = lsa_n->checksum; if (chsum != lsasum_check(lsa_n, NULL)) { - log(L_WARN "%s: Received LSA from %I with bad checskum: %x %x", + log(L_WARN "%s: Received LSA from %I with bad checksum: %x %x", p->p.name, n->ip, chsum, lsa_n->checksum); continue; } @@ -501,7 +517,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, if (en && ((now - en->inst_time) < MINLSARRIVAL)) { OSPF_TRACE(D_EVENTS, "Skipping LSA received in less that MinLSArrival"); - sendreq = 0; + skip_lsreq = 1; continue; } @@ -514,7 +530,6 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, { log(L_WARN "%s: Received invalid LSA from %I", p->p.name, n->ip); mb_free(body); - sendreq = 0; continue; } @@ -528,22 +543,27 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, } /* 13. (5c) - remove old LSA from all retransmission lists */ - /* Must be done before (5b), otherwise it also removes the new entries from (5b) */ - + /* + * We only need to remove it from the retransmission list of the neighbor + * that send us the new LSA. The old LSA is automatically replaced in + * retransmission lists by the new LSA. + */ if (en) ospf_lsa_lsrt_down(en, n); +#if 0 /* - { - struct ospf_iface *ifi; - struct ospf_neighbor *ni; + * Old code for removing LSA from all retransmission lists. Must be done + * before (5b), otherwise it also removes the new entries from (5b). + */ + struct ospf_iface *ifi; + struct ospf_neighbor *ni; - WALK_LIST(ifi, p->iface_list) - WALK_LIST(ni, ifi->neigh_list) - if (ni->state > NEIGHBOR_EXSTART) - ospf_lsa_lsrt_down(en, ni); - } - */ + WALK_LIST(ifi, p->iface_list) + WALK_LIST(ni, ifi->neigh_list) + if (ni->state > NEIGHBOR_EXSTART) + ospf_lsa_lsrt_down(en, ni); +#endif /* 13. (5d) - install new LSA into database */ en = ospf_install_lsa(p, &lsa, lsa_type, lsa_domain, body); @@ -553,7 +573,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, ospf_notify_net_lsa(ifa); /* 13. (5b) - flood new LSA */ - int flood_back = ospf_lsupd_flood(p, en, n); + int flood_back = ospf_flood_lsa(p, en, n); /* 13.5. - schedule ACKs (tbl 19, cases 1+2) */ if (! flood_back) @@ -582,7 +602,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, else ospf_enqueue_lsack(n, lsa_n, ACKL_DIRECT); - sendreq = 0; + skip_lsreq = 1; continue; } @@ -598,11 +618,17 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, } } - /* Send direct LSAs */ - ospf_lsack_send(n, ACKL_DIRECT); + /* Send direct LSACKs */ + ospf_send_lsack(p, n, ACKL_DIRECT); - /* If loading, ask for another part of neighbor's database */ - if (sendreq && (n->state == NEIGHBOR_LOADING)) + /* + * In loading state, we should ask for another batch of LSAs. This is only + * vaguely mentioned in RFC 2328. We send a new LSREQ only if the current + * LSUPD actually removed some entries from LSA request list (want_lsreq) and + * did not contain duplicate or early LSAs (skip_lsreq). The first condition + * prevents endless floods, the second condition helps with flow control. + */ + if ((n->state == NEIGHBOR_LOADING) && n->want_lsreq && !skip_lsreq) ospf_send_lsreq(p, n); } diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index 392f1d64..c182f0d2 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -33,13 +33,26 @@ static void rxmt_timer_hook(timer * timer); static void ackd_timer_hook(timer * t); static void -init_lists(struct ospf_neighbor *n) +init_lists(struct ospf_proto *p, struct ospf_neighbor *n) { s_init_list(&(n->lsrql)); - n->lsrqh = ospf_top_new(n->pool); + n->lsrqh = ospf_top_new(p, n->pool); s_init_list(&(n->lsrtl)); - n->lsrth = ospf_top_new(n->pool); + n->lsrth = ospf_top_new(p, n->pool); +} + +static void +release_lsrtl(struct ospf_proto *p, struct ospf_neighbor *n) +{ + struct top_hash_entry *ret, *en; + + WALK_SLIST(ret, n->lsrtl) + { + en = ospf_hash_find_entry(p->gr, ret); + if (en) + en->ret_count--; + } } /* Resets LSA request and retransmit lists. @@ -47,11 +60,12 @@ init_lists(struct ospf_neighbor *n) * it is reset during entering EXCHANGE state. */ static void -reset_lists(struct ospf_neighbor *n) +reset_lists(struct ospf_proto *p, struct ospf_neighbor *n) { + release_lsrtl(p,n); ospf_top_free(n->lsrqh); ospf_top_free(n->lsrth); - init_lists(n); + init_lists(p, n); } struct ospf_neighbor * @@ -68,7 +82,7 @@ ospf_neighbor_new(struct ospf_iface *ifa) n->csn = 0; n->state = NEIGHBOR_DOWN; - init_lists(n); + init_lists(p, n); s_init(&(n->dbsi), &(p->lsal)); n->inactim = tm_new(pool); @@ -360,6 +374,10 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) s_get(&(n->dbsi)); s_init(&(n->dbsi), &p->lsal); + /* Add MaxAge LSA entries to retransmission list */ + ospf_add_flushed_to_lsrt(p, n); + + /* FIXME: Why is this here ? */ ospf_reset_lsack_queue(n); } else @@ -388,7 +406,7 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) if (n->state >= NEIGHBOR_EXSTART) if (!can_do_adj(n)) { - reset_lists(n); + reset_lists(p,n); neigh_chstate(n, NEIGHBOR_2WAY); } break; @@ -399,7 +417,7 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) case INM_BADLSREQ: if (n->state >= NEIGHBOR_EXCHANGE) { - reset_lists(n); + reset_lists(p, n); neigh_chstate(n, NEIGHBOR_EXSTART); } break; @@ -407,12 +425,12 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) case INM_KILLNBR: case INM_LLDOWN: case INM_INACTTIM: - reset_lists(n); + reset_lists(p, n); neigh_chstate(n, NEIGHBOR_DOWN); break; case INM_1WAYREC: - reset_lists(n); + reset_lists(p, n); neigh_chstate(n, NEIGHBOR_INIT); break; @@ -552,8 +570,10 @@ ospf_neigh_remove(struct ospf_neighbor *n) nn->found = 0; } - s_get(&(n->dbsi)); neigh_chstate(n, NEIGHBOR_DOWN); + + s_get(&(n->dbsi)); + release_lsrtl(p, n); rem_node(NODE n); rfree(n->pool); OSPF_TRACE(D_EVENTS, "Deleting neigbor %R", n->rid); @@ -620,9 +640,9 @@ ospf_sh_neigh_info(struct ospf_neighbor *n) } static void -rxmt_timer_hook(timer * timer) +rxmt_timer_hook(timer *t) { - struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data; + struct ospf_neighbor *n = t->data; struct ospf_proto *p = n->ifa->oa->po; DBG("%s: RXMT timer fired on interface %s for neigh %I\n", @@ -631,12 +651,12 @@ rxmt_timer_hook(timer * timer) switch (n->state) { case NEIGHBOR_EXSTART: - ospf_send_dbdes(n, 1); + ospf_send_dbdes(p, n, 1); return; case NEIGHBOR_EXCHANGE: if (n->myimms & DBDES_MS) - ospf_send_dbdes(n, 0); + ospf_send_dbdes(p, n, 0); case NEIGHBOR_LOADING: ospf_send_lsreq(p, n); return; @@ -653,8 +673,13 @@ rxmt_timer_hook(timer * timer) } static void -ackd_timer_hook(timer * t) +ackd_timer_hook(timer *t) { struct ospf_neighbor *n = t->data; - ospf_lsack_send(n, ACKL_DELAY); + struct ospf_proto *p = n->ifa->oa->po; + + DBG("%s: ACKD timer fired on interface %s for neigh %I\n", + p->p.name, n->ifa->ifname, n->ip); + + ospf_send_lsack(p, n, ACKL_DELAY); } diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index abcd527a..df5fe472 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -11,93 +11,81 @@ /** * DOC: Open Shortest Path First (OSPF) * - * The OSPF protocol is quite complicated and its complex implemenation is - * split to many files. In |ospf.c|, you will find mainly the interface - * for communication with the core (e.g., reconfiguration hooks, shutdown - * and initialisation and so on). In |packet.c|, you will find various - * functions for sending and receiving generic OSPF packets. There are - * also routines for authentication and checksumming. File |iface.c| contains - * the interface state machine and functions for allocation and deallocation of OSPF's - * interface data structures. Source |neighbor.c| includes the neighbor state - * machine and functions for election of Designated Router and Backup - * Designated router. In |hello.c|, there are routines for sending - * and receiving of hello packets as well as functions for maintaining - * wait times and the inactivity timer. Files |lsreq.c|, |lsack.c|, |dbdes.c| - * contain functions for sending and receiving of link-state requests, - * link-state acknowledgements and database descriptions respectively. - * In |lsupd.c|, there are functions for sending and receiving - * of link-state updates and also the flooding algorithm. Source |topology.c| is - * a place where routines for searching LSAs in the link-state database, - * adding and deleting them reside, there also are functions for originating - * of various types of LSAs (router LSA, net LSA, external LSA). File |rt.c| - * contains routines for calculating the routing table. |lsalib.c| is a set - * of various functions for working with the LSAs (endianity conversions, - * calculation of checksum etc.). + * The OSPF protocol is quite complicated and its complex implemenation is split + * to many files. In |ospf.c|, you will find mainly the interface for + * communication with the core (e.g., reconfiguration hooks, shutdown and + * initialisation and so on). File |iface.c| contains the interface state + * machine and functions for allocation and deallocation of OSPF's interface + * data structures. Source |neighbor.c| includes the neighbor state machine and + * functions for election of Designated Router and Backup Designated router. In + * |packet.c|, you will find various functions for sending and receiving generic + * OSPF packets. There are also routines for authentication and checksumming. + * In |hello.c|, there are routines for sending and receiving of hello packets + * as well as functions for maintaining wait times and the inactivity timer. + * Files |lsreq.c|, |lsack.c|, |dbdes.c| contain functions for sending and + * receiving of link-state requests, link-state acknowledgements and database + * descriptions respectively. In |lsupd.c|, there are functions for sending and + * receiving of link-state updates and also the flooding algorithm. Source + * |topology.c| is a place where routines for searching LSAs in the link-state + * database, adding and deleting them reside, there also are functions for + * originating of various types of LSAs (router LSA, net LSA, external LSA). + * File |rt.c| contains routines for calculating the routing table. |lsalib.c| + * is a set of various functions for working with the LSAs (endianity + * conversions, calculation of checksum etc.). * - * One instance of the protocol is able to hold LSA databases for - * multiple OSPF areas, to exchange routing information between - * multiple neighbors and to calculate the routing tables. The core - * structure is &ospf_proto to which multiple &ospf_area and - * &ospf_iface structures are connected. &ospf_area is also connected to - * &top_hash_graph which is a dynamic hashing structure that - * describes the link-state database. It allows fast search, addition - * and deletion. Each LSA is kept in two pieces: header and body. Both of them are + * One instance of the protocol is able to hold LSA databases for multiple OSPF + * areas, to exchange routing information between multiple neighbors and to + * calculate the routing tables. The core structure is &ospf_proto to which + * multiple &ospf_area and &ospf_iface structures are connected. &ospf_proto is + * also connected to &top_hash_graph which is a dynamic hashing structure that + * describes the link-state database. It allows fast search, addition and + * deletion. Each LSA is kept in two pieces: header and body. Both of them are * kept in the endianity of the CPU. * - * In OSPFv2 specification, it is implied that there is one IP prefix - * for each physical network/interface (unless it is an ptp link). But - * in modern systems, there might be more independent IP prefixes - * associated with an interface. To handle this situation, we have - * one &ospf_iface for each active IP prefix (instead for each active - * iface); This behaves like virtual interface for the purpose of OSPF. - * If we receive packet, we associate it with a proper virtual interface - * mainly according to its source address. + * In OSPFv2 specification, it is implied that there is one IP prefix for each + * physical network/interface (unless it is an ptp link). But in modern systems, + * there might be more independent IP prefixes associated with an interface. To + * handle this situation, we have one &ospf_iface for each active IP prefix + * (instead for each active iface); This behaves like virtual interface for the + * purpose of OSPF. If we receive packet, we associate it with a proper virtual + * interface mainly according to its source address. * - * OSPF keeps one socket per &ospf_iface. This allows us (compared to - * one socket approach) to evade problems with a limit of multicast - * groups per socket and with sending multicast packets to appropriate - * interface in a portable way. The socket is associated with - * underlying physical iface and should not receive packets received - * on other ifaces (unfortunately, this is not true on - * BSD). Generally, one packet can be received by more sockets (for - * example, if there are more &ospf_iface on one physical iface), - * therefore we explicitly filter received packets according to - * src/dst IP address and received iface. + * OSPF keeps one socket per &ospf_iface. This allows us (compared to one socket + * approach) to evade problems with a limit of multicast groups per socket and + * with sending multicast packets to appropriate interface in a portable way. + * The socket is associated with underlying physical iface and should not + * receive packets received on other ifaces (unfortunately, this is not true on + * BSD). Generally, one packet can be received by more sockets (for example, if + * there are more &ospf_iface on one physical iface), therefore we explicitly + * filter received packets according to src/dst IP address and received iface. * - * Vlinks are implemented using particularly degenerate form of - * &ospf_iface, which has several exceptions: it does not have its - * iface or socket (it copies these from 'parent' &ospf_iface) and it - * is present in iface list even when down (it is not freed in - * ospf_iface_down()). + * Vlinks are implemented using particularly degenerate form of &ospf_iface, + * which has several exceptions: it does not have its iface or socket (it copies + * these from 'parent' &ospf_iface) and it is present in iface list even when + * down (it is not freed in ospf_iface_down()). * * The heart beat of ospf is ospf_disp(). It is called at regular intervals - * (&ospf_proto->tick). It is responsible for aging and flushing of LSAs in - * the database, for routing table calculaction and it call area_disp() of every - * ospf_area. + * (&ospf_proto->tick). It is responsible for aging and flushing of LSAs in the + * database, updating topology information in LSAs and for routing table + * calculation. * - * The function area_disp() is - * responsible for late originating of router LSA and network LSA - * and for cleanup before routing table calculation process in - * the area. - * To every &ospf_iface, we connect one or more - * &ospf_neighbor's -- a structure containing many timers and queues - * for building adjacency and for exchange of routing messages. + * To every &ospf_iface, we connect one or more &ospf_neighbor's -- a structure + * containing many timers and queues for building adjacency and for exchange of + * routing messages. * - * BIRD's OSPF implementation respects RFC2328 in every detail, but - * some of internal algorithms do differ. The RFC recommends making a snapshot - * of the link-state database when a new adjacency is forming and sending - * the database description packets based on the information in this - * snapshot. The database can be quite large in some networks, so - * rather we walk through a &slist structure which allows us to - * continue even if the actual LSA we were working with is deleted. New - * LSAs are added at the tail of this &slist. + * BIRD's OSPF implementation respects RFC2328 in every detail, but some of + * internal algorithms do differ. The RFC recommends making a snapshot of the + * link-state database when a new adjacency is forming and sending the database + * description packets based on the information in this snapshot. The database + * can be quite large in some networks, so rather we walk through a &slist + * structure which allows us to continue even if the actual LSA we were working + * with is deleted. New LSAs are added at the tail of this &slist. * - * We also don't keep a separate OSPF routing table, because the core - * helps us by being able to recognize when a route is updated - * to an identical one and it suppresses the update automatically. - * Due to this, we can flush all the routes we've recalculated and - * also those we've deleted to the core's routing table and the - * core will take care of the rest. This simplifies the process + * We also do not keep a separate OSPF routing table, because the core helps us + * by being able to recognize when a route is updated to an identical one and it + * suppresses the update automatically. Due to this, we can flush all the routes + * we have recalculated and also those we have deleted to the core's routing + * table and the core will take care of the rest. This simplifies the process * and conserves memory. */ @@ -145,7 +133,7 @@ add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac) } static void -ospf_area_add(struct ospf_proto *p, struct ospf_area_config *ac, int reconf) +ospf_area_add(struct ospf_proto *p, struct ospf_area_config *ac) { struct ospf_area *oa; @@ -169,6 +157,8 @@ ospf_area_add(struct ospf_proto *p, struct ospf_area_config *ac, int reconf) oa->options = ac->type; else oa->options = ac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R); + + ospf_notify_rt_lsa(oa); } static void @@ -252,11 +242,11 @@ ospf_start(struct proto *P) init_list(&(p->area_list)); fib_init(&p->rtf, P->pool, sizeof(ort), 0, ospf_rt_initort); p->areano = 0; - p->gr = ospf_top_new(P->pool); + p->gr = ospf_top_new(p, P->pool); s_init_list(&(p->lsal)); WALK_LIST(ac, c->area_list) - ospf_area_add(p, ac, 0); + ospf_area_add(p, ac); if (c->abr) ospf_open_vlink_sk(p); @@ -382,7 +372,7 @@ ospf_build_attrs(ea_list * next, struct linpool *pool, u32 m1, u32 m2, void -schedule_rtcalc(struct ospf_proto *p) +ospf_schedule_rtcalc(struct ospf_proto *p) { if (p->calcrt) return; @@ -418,7 +408,7 @@ ospf_disp(timer * timer) /* Originate or flush local topology LSAs */ ospf_update_topology(p); - /* Age LSA DB */ + /* Process LSA DB */ ospf_update_lsadb(p); /* Calculate routing table */ @@ -429,7 +419,7 @@ ospf_disp(timer * timer) /** * ospf_import_control - accept or reject new route from nest's routing table - * @p: current instance of protocol + * @P: OSPF protocol instance * @new: the new route * @attrs: list of attributes * @pool: pool for allocation of attributes @@ -475,7 +465,7 @@ ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs) /** * ospf_shutdown - Finish of OSPF instance - * @p: current instance of protocol + * @P: OSPF protocol instance * * RFC does not define any action that should be taken before router * shutdown. To make my neighbors react as fast as possible, I send @@ -619,7 +609,7 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) /** * ospf_reconfigure - reconfiguration hook - * @p: current instance of protocol (with old configuration) + * @P: current instance of protocol (with old configuration) * @c: new configuration requested by user * * This hook tries to be a little bit intelligent. Instance of OSPF @@ -669,7 +659,7 @@ ospf_reconfigure(struct proto *P, struct proto_config *c) if (oa) ospf_area_reconfigure(oa, nac); else - ospf_area_add(p, nac, 1); + ospf_area_add(p, nac); } /* Add and update interfaces */ @@ -697,7 +687,7 @@ ospf_reconfigure(struct proto *P, struct proto_config *c) if (oa->marked) ospf_area_remove(oa); - schedule_rtcalc(p); + ospf_schedule_rtcalc(p); return 1; } @@ -1009,7 +999,7 @@ show_lsa_router(struct ospf_proto *p, struct top_hash_entry *he, int verbose) /* In OSPFv2, we try to find network-LSA to get prefix/pxlen */ struct top_hash_entry *net_he = ospf_hash_find_net2(p->gr, he->domain, rtl.id); - if (net_he) + if (net_he && (net_he->lsa.age < LSA_MAXAGE)) { struct ospf_lsa_header *net_lsa = &(net_he->lsa); struct ospf_lsa_net *net_ln = net_he->lsa_body; @@ -1367,8 +1357,8 @@ void ospf_sh_lsadb(struct lsadb_show_data *ld) { struct ospf_proto *p = (struct ospf_proto *) proto_get_named(ld->name, &proto_ospf); - int num = p->gr->hash_entries; - unsigned int i, j; + uint num = p->gr->hash_entries; + uint i, j; int last_dscope = -1; u32 last_domain = 0; u16 type_mask = ospf_is_v2(p) ? 0x00ff : 0xffff; /* see lsa_etype() */ diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 34c26b47..e5713628 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -102,7 +102,7 @@ do { if ((p->p.debug & D_PACKETS) || OSPF_FORCE_DEBUG) \ struct ospf_config { struct proto_config c; - unsigned tick; + uint tick; byte ospf2; byte rfc1583; byte stub_router; @@ -110,16 +110,26 @@ struct ospf_config byte abr; byte asbr; int ecmp; - list area_list; /* list of struct ospf_area_config */ - list vlink_list; /* list of struct ospf_iface_patt */ + list area_list; /* list of area configs (struct ospf_area_config) */ + list vlink_list; /* list of configured vlinks (struct ospf_iface_patt) */ }; -struct nbma_node +struct ospf_area_config { node n; - ip_addr ip; - byte eligible; - byte found; + u32 areaid; + u32 default_cost; /* Cost of default route for stub areas + (With possible LSA_EXT3_EBIT for NSSA areas) */ + u8 type; /* Area type (standard, stub, NSSA), represented + by option flags (OPT_E, OPT_N) */ + u8 summary; /* Import summaries to this stub/NSSA area, valid for ABR */ + u8 default_nssa; /* Generate default NSSA route for NSSA+summary area */ + u8 translator; /* Translator role, for NSSA ABR */ + u32 transint; /* Translator stability interval */ + list patt_list; /* List of iface configs (struct ospf_iface_patt) */ + list net_list; /* List of aggregate networks for that area */ + list enet_list; /* List of aggregate external (NSSA) networks */ + list stubnet_list; /* List of stub networks added to Router LSA */ }; struct area_net_config @@ -148,65 +158,54 @@ struct ospf_stubnet_config u8 summary; }; -struct ospf_area_config +struct nbma_node { node n; - u32 areaid; - u32 default_cost; /* Cost of default route for stub areas - (With possible LSA_EXT3_EBIT for NSSA areas) */ - u8 type; /* Area type (standard, stub, NSSA), represented - by option flags (OPT_E, OPT_N) */ - u8 summary; /* Import summaries to this stub/NSSA area, valid for ABR */ - u8 default_nssa; /* Generate default NSSA route for NSSA+summary area */ - u8 translator; /* Translator role, for NSSA ABR */ - u32 transint; /* Translator stability interval */ - list patt_list; - list net_list; /* List of aggregate networks for that area */ - list enet_list; /* List of aggregate external (NSSA) networks */ - list stubnet_list; /* List of stub networks added to Router LSA */ + ip_addr ip; + byte eligible; + byte found; }; +struct ospf_iface_patt +{ + struct iface_patt i; + u32 type; + u32 stub; + u32 cost; + u32 helloint; + u32 rxmtint; + u32 pollint; + u32 waitint; + u32 deadc; + u32 deadint; + u32 inftransdelay; + list nbma_list; + u32 priority; + u32 voa; + u32 vid; + int tx_tos; + int tx_priority; + u16 tx_length; + u16 rx_buffer; -/* Generic option flags */ -#define OPT_V6 0x01 /* OSPFv3, LSA relevant for IPv6 routing calculation */ -#define OPT_E 0x02 /* Related to AS-external LSAs */ -#define OPT_MC 0x04 /* Related to MOSPF, not used and obsolete */ -#define OPT_N 0x08 /* Related to NSSA */ -#define OPT_P 0x08 /* OSPFv2, flags P and N share position, see NSSA RFC */ -#define OPT_EA 0x10 /* OSPFv2, external attributes, not used and obsolete */ -#define OPT_R 0x10 /* OSPFv3, originator is active router */ -#define OPT_DC 0x20 /* Related to demand circuits, not used */ - -/* Router-LSA VEB flags are are stored together with links (OSPFv2) or options (OSPFv3) */ -#define OPT_RT_B (0x01 << 24) -#define OPT_RT_E (0x02 << 24) -#define OPT_RT_V (0x04 << 24) -#define OPT_RT_NT (0x10 << 24) - -/* Prefix flags, specific for OSPFv3 */ -#define OPT_PX_NU 0x01 -#define OPT_PX_LA 0x02 -#define OPT_PX_P 0x08 -#define OPT_PX_DN 0x10 - - -/* OSPF interface types */ -#define OSPF_IT_BCAST 0 -#define OSPF_IT_NBMA 1 -#define OSPF_IT_PTP 2 -#define OSPF_IT_PTMP 3 -#define OSPF_IT_VLINK 4 -#define OSPF_IT_UNDEF 5 - -/* OSPF interface states */ -#define OSPF_IS_DOWN 0 /* Not active */ -#define OSPF_IS_LOOP 1 /* Iface with no link */ -#define OSPF_IS_WAITING 2 /* Waiting for Wait timer */ -#define OSPF_IS_PTP 3 /* PTP operational */ -#define OSPF_IS_DROTHER 4 /* I'm on BCAST or NBMA and I'm not DR */ -#define OSPF_IS_BACKUP 5 /* I'm BDR */ -#define OSPF_IS_DR 6 /* I'm DR */ - +#define OSPF_RXBUF_MINSIZE 256 /* Minimal allowed size */ + u8 instance_id; + u8 autype; /* Not really used in OSPFv3 */ +#define OSPF_AUTH_NONE 0 +#define OSPF_AUTH_SIMPLE 1 +#define OSPF_AUTH_CRYPT 2 +#define OSPF_AUTH_CRYPT_SIZE 16 + u8 strictnbma; + u8 check_link; + u8 ecmp_weight; + u8 link_lsa_suppression; + u8 real_bcast; /* Not really used in OSPFv3 */ + u8 ptp_netmask; /* bool + 2 for unspecified */ + u8 ttl_security; /* bool + 2 for TX only */ + u8 bfd; + u8 bsd_secondary; + list *passwords; +}; /* Default values for interface parameters */ #define COST_D 10 @@ -220,6 +219,58 @@ struct ospf_area_config /* Value of Wait timer - not found it in RFC * - using 4*HELLO */ + +struct ospf_proto +{ + struct proto p; + timer *disp_timer; /* OSPF proto dispatcher */ + uint tick; + struct top_graph *gr; /* LSA graph */ + slist lsal; /* List of all LSA's */ + int calcrt; /* Routing table calculation scheduled? + 0=no, 1=normal, 2=forced reload */ + list iface_list; /* List of OSPF interfaces (struct ospf_iface) */ + list area_list; /* List of OSPF areas (struct ospf_area) */ + int areano; /* Number of area I belong to */ + int padj; /* Number of neighbors in Exchange or Loading state */ + struct fib rtf; /* Routing table */ + byte ospf2; /* OSPF v2 or v3 */ + byte rfc1583; /* RFC1583 compatibility */ + byte stub_router; /* Do not forward transit traffic */ + byte merge_external; /* Should i merge external routes? */ + byte asbr; /* May i originate any ext/NSSA lsa? */ + byte ecmp; /* Maximal number of nexthops in ECMP route, or 0 */ + struct ospf_area *backbone; /* If exists */ + void *lsab; /* LSA buffer used when originating router LSAs */ + int lsab_size, lsab_used; + linpool *nhpool; /* Linpool used for next hops computed in SPF */ + sock *vlink_sk; /* IP socket used for vlink TX */ + u32 router_id; + u32 last_vlink_id; /* Interface IDs for vlinks (starts at 0x80000000) */ +}; + +struct ospf_area +{ + node n; + u32 areaid; + struct ospf_area_config *ac; /* Related area config */ + 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 */ + struct fib enet_fib; /* External networks for NSSAs */ + u32 options; /* Optional features */ + u8 update_rt_lsa; /* Rt lsa origination scheduled? */ + u8 trcap; /* Transit capability? */ + u8 marked; /* Used in OSPF reconfigure */ + u8 translate; /* Translator state (TRANS_*), for NSSA ABR */ + timer *translator_timer; /* For NSSA translator switch */ + struct ospf_proto *po; + struct fib rtr; /* Routing tables for routers */ +}; + + + struct ospf_iface { node n; @@ -231,7 +282,7 @@ struct ospf_iface pool *pool; sock *sk; /* IP socket */ - list neigh_list; /* List of neigbours */ + list neigh_list; /* List of neigbours (struct ospf_neighbor) */ u32 cost; /* Cost of iface */ u32 waitint; /* number of sec before changing state from wait */ u32 rxmtint; /* number of seconds between LSA retransmissions */ @@ -295,6 +346,153 @@ struct ospf_iface u8 bfd; /* Use BFD on iface */ }; +struct ospf_neighbor +{ + node n; + pool *pool; + struct ospf_iface *ifa; + u8 state; + timer *inactim; /* Inactivity timer */ + u8 imms; /* I, M, Master/slave received */ + u8 myimms; /* I, M Master/slave */ + u32 dds; /* DD Sequence number being sent */ + u32 ddr; /* last Dat Des packet received */ + + u32 rid; /* Router ID */ + ip_addr ip; /* IP of it's interface */ + u8 priority; /* Priority */ + u8 adj; /* built adjacency? */ + u8 want_lsreq; /* Set to 1 when lsrql was shortened during LSUPD */ + u32 options; /* Options received */ + + /* Entries dr and bdr store IP addresses in OSPFv2 and router IDs in + OSPFv3, we use the same type to simplify handling */ + u32 dr; /* Neigbour's idea of DR */ + u32 bdr; /* Neigbour's idea of BDR */ + u32 iface_id; /* ID of Neighbour's iface connected to common network */ + + /* Database summary list iterator, controls initial dbdes exchange. + * Advances in the LSA list as dbdes packets are sent. + */ + siterator dbsi; /* iterator of po->lsal */ + + /* Link state request list, controls initial LSA exchange. + * Entries added when received in dbdes packets, removed as sent in lsreq packets. + */ + slist lsrql; /* slist of struct top_hash_entry from n->lsrqh */ + struct top_graph *lsrqh; + + /* Link state retransmission list, controls LSA retransmission during flood. + * Entries added as sent in lsupd packets, removed when received in lsack packets. + * These entries hold ret_count in appropriate LSA entries. + */ + slist lsrtl; /* slist of struct top_hash_entry from n->lsrth */ + struct top_graph *lsrth; + timer *rxmt_timer; /* RXMT timer */ + list ackl[2]; +#define ACKL_DIRECT 0 +#define ACKL_DELAY 1 + timer *ackd_timer; /* Delayed ack timer */ + struct bfd_request *bfd_req; /* BFD request, if BFD is used */ + void *ldd_buffer; /* Last database description packet */ + u32 ldd_bsize; /* Buffer size for ldd_buffer */ + u32 csn; /* Last received crypt seq number (for MD5) */ +}; + + +/* OSPF interface types */ +#define OSPF_IT_BCAST 0 +#define OSPF_IT_NBMA 1 +#define OSPF_IT_PTP 2 +#define OSPF_IT_PTMP 3 +#define OSPF_IT_VLINK 4 +#define OSPF_IT_UNDEF 5 + +/* OSPF interface states */ +#define OSPF_IS_DOWN 0 /* Not active */ +#define OSPF_IS_LOOP 1 /* Iface with no link */ +#define OSPF_IS_WAITING 2 /* Waiting for Wait timer */ +#define OSPF_IS_PTP 3 /* PTP operational */ +#define OSPF_IS_DROTHER 4 /* I'm on BCAST or NBMA and I'm not DR */ +#define OSPF_IS_BACKUP 5 /* I'm BDR */ +#define OSPF_IS_DR 6 /* I'm DR */ + +/* Definitions for interface state machine */ +#define ISM_UP 0 /* Interface Up */ +#define ISM_WAITF 1 /* Wait timer fired */ +#define ISM_BACKS 2 /* Backup seen */ +#define ISM_NEICH 3 /* Neighbor change */ +#define ISM_LOOP 4 /* Link down */ +#define ISM_UNLOOP 5 /* Link up */ +#define ISM_DOWN 6 /* Interface down */ + + +/* OSPF neighbor states */ +#define NEIGHBOR_DOWN 0 +#define NEIGHBOR_ATTEMPT 1 +#define NEIGHBOR_INIT 2 +#define NEIGHBOR_2WAY 3 +#define NEIGHBOR_EXSTART 4 +#define NEIGHBOR_EXCHANGE 5 +#define NEIGHBOR_LOADING 6 +#define NEIGHBOR_FULL 7 + +/* Definitions for neighbor state machine */ +#define INM_HELLOREC 0 /* Hello Received */ +#define INM_START 1 /* Neighbor start - for NBMA */ +#define INM_2WAYREC 2 /* 2-Way received */ +#define INM_NEGDONE 3 /* Negotiation done */ +#define INM_EXDONE 4 /* Exchange done */ +#define INM_BADLSREQ 5 /* Bad LS Request */ +#define INM_LOADDONE 6 /* Load done */ +#define INM_ADJOK 7 /* AdjOK? */ +#define INM_SEQMIS 8 /* Sequence number mismatch */ +#define INM_1WAYREC 9 /* 1-Way */ +#define INM_KILLNBR 10 /* Kill Neighbor */ +#define INM_INACTTIM 11 /* Inactivity timer */ +#define INM_LLDOWN 12 /* Line down */ + +#define TRANS_OFF 0 +#define TRANS_ON 1 +#define TRANS_WAIT 2 /* Waiting before the end of translation */ + + + +/* Generic option flags */ +#define OPT_V6 0x01 /* OSPFv3, LSA relevant for IPv6 routing calculation */ +#define OPT_E 0x02 /* Related to AS-external LSAs */ +#define OPT_MC 0x04 /* Related to MOSPF, not used and obsolete */ +#define OPT_N 0x08 /* Related to NSSA */ +#define OPT_P 0x08 /* OSPFv2, flags P and N share position, see NSSA RFC */ +#define OPT_EA 0x10 /* OSPFv2, external attributes, not used and obsolete */ +#define OPT_R 0x10 /* OSPFv3, originator is active router */ +#define OPT_DC 0x20 /* Related to demand circuits, not used */ + +/* Router-LSA VEB flags are are stored together with links (OSPFv2) or options (OSPFv3) */ +#define OPT_RT_B (0x01 << 24) +#define OPT_RT_E (0x02 << 24) +#define OPT_RT_V (0x04 << 24) +#define OPT_RT_NT (0x10 << 24) + +/* Prefix flags, specific for OSPFv3 */ +#define OPT_PX_NU 0x01 +#define OPT_PX_LA 0x02 +#define OPT_PX_P 0x08 +#define OPT_PX_DN 0x10 + + +struct ospf_packet +{ + u8 version; + u8 type; + u16 length; + u32 routerid; + u32 areaid; + u16 checksum; + u8 instance_id; /* See RFC 6549 */ + u8 autype; /* Undefined for OSPFv3 */ +}; + struct ospf_md5 { u16 zero; @@ -309,7 +507,6 @@ union ospf_auth struct ospf_md5 md5; }; - /* Packet types */ #define HELLO_P 1 /* Hello */ #define DBDES_P 2 /* Database description */ @@ -317,8 +514,6 @@ union ospf_auth #define LSUPD_P 4 /* Link state update */ #define LSACK_P 5 /* Link state acknowledgement */ -/* Area IDs */ -#define BACKBONE 0 #define DBDES_I 4 /* Init bit */ #define DBDES_M 2 /* More bit */ @@ -326,19 +521,6 @@ union ospf_auth #define DBDES_IMMS (DBDES_I | DBDES_M | DBDES_MS) -struct ospf_packet -{ - u8 version; - u8 type; - u16 length; - u32 routerid; - u32 areaid; - u16 checksum; - u8 instance_id; /* See RFC 6549 */ - u8 autype; /* Undefined for OSPFv3 */ -}; - - #define LSA_T_RT 0x2001 #define LSA_T_NET 0x2002 #define LSA_T_SUM_NET 0x2003 @@ -530,7 +712,7 @@ struct ospf_lsa_prefix }; -static inline unsigned +static inline uint lsa_net_count(struct ospf_lsa_header *lsa) { return (lsa->length - sizeof(struct ospf_lsa_header) - sizeof(struct ospf_lsa_net)) @@ -558,6 +740,7 @@ lsa_get_ipv6_prefix(u32 *buf, ip_addr *addr, int *pxlen, u8 *pxopts, u16 *rest) *addr = IPA_NONE; +#ifdef IPV6 if (pxl > 0) _I0(*addr) = *buf++; if (pxl > 32) @@ -570,6 +753,7 @@ lsa_get_ipv6_prefix(u32 *buf, ip_addr *addr, int *pxlen, u8 *pxopts, u16 *rest) /* Clean up remaining bits */ if (pxl < 128) addr->addr[pxl / 32] &= u32_mkmask(pxl % 32); +#endif return buf; } @@ -613,183 +797,6 @@ struct ospf_lsreq_header }; -struct ospf_neighbor -{ - node n; - pool *pool; - struct ospf_iface *ifa; - u8 state; -#define NEIGHBOR_DOWN 0 -#define NEIGHBOR_ATTEMPT 1 -#define NEIGHBOR_INIT 2 -#define NEIGHBOR_2WAY 3 -#define NEIGHBOR_EXSTART 4 -#define NEIGHBOR_EXCHANGE 5 -#define NEIGHBOR_LOADING 6 -#define NEIGHBOR_FULL 7 - timer *inactim; /* Inactivity timer */ - u8 imms; /* I, M, Master/slave received */ - u8 myimms; /* I, M Master/slave */ - u32 dds; /* DD Sequence number being sent */ - u32 ddr; /* last Dat Des packet received */ - - u32 rid; /* Router ID */ - ip_addr ip; /* IP of it's interface */ - u8 priority; /* Priority */ - u8 adj; /* built adjacency? */ - u32 options; /* Options received */ - - /* Entries dr and bdr store IP addresses in OSPFv2 and router IDs in - OSPFv3, we use the same type to simplify handling */ - u32 dr; /* Neigbour's idea of DR */ - u32 bdr; /* Neigbour's idea of BDR */ - u32 iface_id; /* ID of Neighbour's iface connected to common network */ - - /* Database summary list iterator, controls initial dbdes exchange. - * Advances in the LSA list as dbdes packets are sent. - */ - siterator dbsi; /* iterator of po->lsal */ - - /* Link state request list, controls initial LSA exchange. - * Entries added when received in dbdes packets, removed as sent in lsreq packets. - */ - slist lsrql; /* slist of struct top_hash_entry from n->lsrqh */ - struct top_graph *lsrqh; - - /* Link state retransmission list, controls LSA retransmission during flood. - * Entries added as sent in lsupd packets, removed when received in lsack packets. - */ - slist lsrtl; /* slist of struct top_hash_entry from n->lsrth */ - struct top_graph *lsrth; - timer *rxmt_timer; /* RXMT timer */ - list ackl[2]; -#define ACKL_DIRECT 0 -#define ACKL_DELAY 1 - timer *ackd_timer; /* Delayed ack timer */ - struct bfd_request *bfd_req; /* BFD request, if BFD is used */ - void *ldd_buffer; /* Last database description packet */ - u32 ldd_bsize; /* Buffer size for ldd_buffer */ - u32 csn; /* Last received crypt seq number (for MD5) */ -}; - -/* Definitions for interface state machine */ -#define ISM_UP 0 /* Interface Up */ -#define ISM_WAITF 1 /* Wait timer fired */ -#define ISM_BACKS 2 /* Backup seen */ -#define ISM_NEICH 3 /* Neighbor change */ -#define ISM_LOOP 4 /* Link down */ -#define ISM_UNLOOP 5 /* Link up */ -#define ISM_DOWN 6 /* Interface down */ - -/* Definitions for neighbor state machine */ -#define INM_HELLOREC 0 /* Hello Received */ -#define INM_START 1 /* Neighbor start - for NBMA */ -#define INM_2WAYREC 2 /* 2-Way received */ -#define INM_NEGDONE 3 /* Negotiation done */ -#define INM_EXDONE 4 /* Exchange done */ -#define INM_BADLSREQ 5 /* Bad LS Request */ -#define INM_LOADDONE 6 /* Load done */ -#define INM_ADJOK 7 /* AdjOK? */ -#define INM_SEQMIS 8 /* Sequence number mismatch */ -#define INM_1WAYREC 9 /* 1-Way */ -#define INM_KILLNBR 10 /* Kill Neighbor */ -#define INM_INACTTIM 11 /* Inactivity timer */ -#define INM_LLDOWN 12 /* Line down */ - -struct ospf_area -{ - node n; - u32 areaid; - struct ospf_area_config *ac; /* Related area config */ - 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 */ - struct fib enet_fib; /* External networks for NSSAs */ - u32 options; /* Optional features */ - u8 update_rt_lsa; /* Rt lsa origination scheduled? */ - u8 trcap; /* Transit capability? */ - u8 marked; /* Used in OSPF reconfigure */ - u8 translate; /* Translator state (TRANS_*), for NSSA ABR */ - timer *translator_timer; /* For NSSA translator switch */ - struct ospf_proto *po; - struct fib rtr; /* Routing tables for routers */ -}; - -#define TRANS_OFF 0 -#define TRANS_ON 1 -#define TRANS_WAIT 2 /* Waiting before the end of translation */ - -struct ospf_proto -{ - struct proto p; - timer *disp_timer; /* OSPF proto dispatcher */ - unsigned tick; - struct top_graph *gr; /* LSA graph */ - slist lsal; /* List of all LSA's */ - int calcrt; /* Routing table calculation scheduled? - 0=no, 1=normal, 2=forced reload */ - list iface_list; /* Interfaces we really use */ - list area_list; - int areano; /* Number of area I belong to */ - int padj; /* Number of neighbors in Exchange or Loading state */ - struct fib rtf; /* Routing table */ - byte ospf2; /* OSPF v2 or v3 */ - byte rfc1583; /* RFC1583 compatibility */ - byte stub_router; /* Do not forward transit traffic */ - byte merge_external; /* Should i merge external routes? */ - byte asbr; /* May i originate any ext/NSSA lsa? */ - byte ecmp; /* Maximal number of nexthops in ECMP route, or 0 */ - struct ospf_area *backbone; /* If exists */ - void *lsab; /* LSA buffer used when originating router LSAs */ - int lsab_size, lsab_used; - linpool *nhpool; /* Linpool used for next hops computed in SPF */ - sock *vlink_sk; /* IP socket used for vlink TX */ - u32 router_id; - u32 last_vlink_id; /* Interface IDs for vlinks (starts at 0x80000000) */ -}; - -struct ospf_iface_patt -{ - struct iface_patt i; - u32 type; - u32 stub; - u32 cost; - u32 helloint; - u32 rxmtint; - u32 pollint; - u32 waitint; - u32 deadc; - u32 deadint; - u32 inftransdelay; - list nbma_list; - u32 priority; - u32 voa; - u32 vid; - int tx_tos; - int tx_priority; - u16 tx_length; - u16 rx_buffer; - -#define OSPF_RXBUF_MINSIZE 256 /* Minimal allowed size */ - u8 instance_id; - u8 autype; /* Not really used in OSPFv3 */ -#define OSPF_AUTH_NONE 0 -#define OSPF_AUTH_SIMPLE 1 -#define OSPF_AUTH_CRYPT 2 -#define OSPF_AUTH_CRYPT_SIZE 16 - u8 strictnbma; - u8 check_link; - u8 ecmp_weight; - u8 link_lsa_suppression; - u8 real_bcast; /* Not really used in OSPFv3 */ - u8 ptp_netmask; /* bool + 2 for unspecified */ - u8 ttl_security; /* bool + 2 for TX only */ - u8 bfd; - u8 bsd_secondary; - list *passwords; -}; - #define SH_ROUTER_SELF 0xffffffff @@ -810,7 +817,7 @@ struct lsadb_show_data { /* ospf.c */ -void schedule_rtcalc(struct ospf_proto *p); +void ospf_schedule_rtcalc(struct ospf_proto *p); static inline void ospf_notify_rt_lsa(struct ospf_area *oa) { oa->update_rt_lsa = 1; } @@ -822,12 +829,15 @@ static inline void ospf_notify_link_lsa(struct ospf_iface *ifa) { ifa->update_link_lsa = 1; } +#define ospf_is_v2(X) OSPF_IS_V2 +#define ospf_is_v3(X) (!OSPF_IS_V2) +/* static inline int ospf_is_v2(struct ospf_proto *p) { return p->ospf2; } static inline int ospf_is_v3(struct ospf_proto *p) { return ! p->ospf2; } - +*/ static inline int ospf_get_version(struct ospf_proto *p) { return ospf_is_v2(p) ? 2 : 3; } @@ -920,7 +930,7 @@ void ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dir void ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n, ip_addr faddr); /* dbdes.c */ -void ospf_send_dbdes(struct ospf_neighbor *n, int next); +void ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n, int next); void ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n); /* lsreq.c */ @@ -930,7 +940,8 @@ void ospf_receive_lsreq(struct ospf_packet *pkt, struct ospf_iface *ifa, struct /* lsupd.c */ void ospf_dump_lsahdr(struct ospf_proto *p, struct ospf_lsa_header *lsa_n); void ospf_dump_common(struct ospf_proto *p, struct ospf_packet *pkt); -int ospf_lsupd_flood(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neighbor *from); +void ospf_add_flushed_to_lsrt(struct ospf_proto *p, struct ospf_neighbor *n); +int ospf_flood_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neighbor *from); int ospf_send_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_list, uint lsa_count, struct ospf_neighbor *n); void ospf_rxmt_lsupd(struct ospf_proto *p, struct ospf_neighbor *n); void ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n); @@ -938,7 +949,7 @@ void ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, struct /* lsack.c */ void ospf_enqueue_lsack(struct ospf_neighbor *n, struct ospf_lsa_header *h_n, int queue); void ospf_reset_lsack_queue(struct ospf_neighbor *n); -void ospf_lsack_send(struct ospf_neighbor *n, int queue); +void ospf_send_lsack(struct ospf_proto *p, struct ospf_neighbor *n, int queue); void ospf_receive_lsack(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n); diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index c4340ee5..9d146ce2 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -557,7 +557,7 @@ spfa_process_rt(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entr /* Errata 2078 to RFC 5340 4.8.1 - skip links from non-routing nodes */ if (ospf_is_v3(p) && (act != oa->rt) && !(rt->options & OPT_R)) - break; + return; /* Now process Rt links */ for (lsa_walk_rt_init(p, act, &rtl), i = 0; lsa_walk_rt(&rtl); i++) @@ -688,6 +688,8 @@ ospf_rt_spfa(struct ospf_area *oa) if (oa->rt == NULL) return; + if (oa->rt->lsa.age == LSA_MAXAGE) + return; OSPF_TRACE(D_EVENTS, "Starting routing table calculation for area %R", oa->areaid); @@ -1087,8 +1089,7 @@ check_sum_net_lsa(struct ospf_proto *p, ort *nf) struct area_net *anet = NULL; struct ospf_area *anet_oa = NULL; - /* RT entry marked as area network */ - if (nf->fn.flags & OSPF_RT_PERSISTENT) + if (nf->area_net) { /* It is a default route for stub areas, handled entirely in ospf_rt_abr() */ if (nf->fn.pxlen == 0) @@ -1162,8 +1163,7 @@ check_nssa_lsa(struct ospf_proto *p, ort *nf) if (nf->external_rte) return; - /* RT entry marked as area network */ - if (nf->fn.flags & OSPF_RT_PERSISTENT) + if (nf->area_net) { /* Find that area network */ WALK_LIST(oa, p->area_list) @@ -1176,12 +1176,12 @@ check_nssa_lsa(struct ospf_proto *p, ort *nf) /* RFC 3103 3.2 (3) - originate the aggregated address range */ if (anet && anet->active && !anet->hidden && oa->translate) - ospf_originate_ext_lsa(p, NULL, nf, LSA_RTCALC, anet->metric, + ospf_originate_ext_lsa(p, NULL, nf, LSA_M_RTCALC, anet->metric, (anet->metric & LSA_EXT3_EBIT), IPA_NONE, anet->tag, 0); /* RFC 3103 3.2 (2) - originate the same network */ else if (decide_nssa_lsa(p, nf, &rt)) - ospf_originate_ext_lsa(p, NULL, nf, LSA_RTCALC, rt.metric, rt.ebit, rt.fwaddr, rt.tag, 0); + ospf_originate_ext_lsa(p, NULL, nf, LSA_M_RTCALC, rt.metric, rt.ebit, rt.fwaddr, rt.tag, 0); } /* RFC 2328 16.7. p2 - find new/lost vlink endpoints */ @@ -1273,7 +1273,7 @@ ospf_rt_abr1(struct ospf_proto *p) /* Get a RT entry and mark it to know that it is an area network */ ort *nfi = (ort *) fib_get(&p->rtf, &anet->fn.prefix, anet->fn.pxlen); - nfi->fn.flags |= OSPF_RT_PERSISTENT; /* mark persistent, to have stable UID */ + nfi->area_net = 1; /* 16.2. (3) */ if (nfi->n.type == RTS_OSPF_IA) @@ -1289,7 +1289,7 @@ ospf_rt_abr1(struct ospf_proto *p) ip_addr addr = IPA_NONE; default_nf = (ort *) fib_get(&p->rtf, &addr, 0); - default_nf->fn.flags |= OSPF_RT_PERSISTENT; /* keep persistent */ + default_nf->area_net = 1; struct ospf_area *oa; WALK_LIST(oa, p->area_list) @@ -1309,7 +1309,7 @@ ospf_rt_abr1(struct ospf_proto *p) */ if (oa_is_nssa(oa) && oa->ac->default_nssa) - ospf_originate_ext_lsa(p, oa, default_nf, LSA_RTCALC, oa->ac->default_cost, + ospf_originate_ext_lsa(p, oa, default_nf, LSA_M_RTCALC, oa->ac->default_cost, (oa->ac->default_cost & LSA_EXT3_EBIT), IPA_NONE, 0, 0); /* RFC 2328 16.4. (3) - precompute preferred ASBR entries */ @@ -1348,7 +1348,7 @@ translator_timer_hook(timer *timer) return; oa->translate = TRANS_OFF; - schedule_rtcalc(oa->po); + ospf_schedule_rtcalc(oa->po); } static void @@ -1431,7 +1431,7 @@ ospf_rt_abr2(struct ospf_proto *p) /* Get a RT entry and mark it to know that it is an area network */ nf2 = (ort *) fib_get(&p->rtf, &anet->fn.prefix, anet->fn.pxlen); - nf2->fn.flags |= OSPF_RT_PERSISTENT; /* keep persistent */ + nf2->area_net = 1; } u32 metric = (nf->n.type == RTS_OSPF_EXT1) ? @@ -1634,7 +1634,7 @@ ospf_rt_reset(struct ospf_proto *p) FIB_WALK(&p->rtf, nftmp) { ri = (ort *) nftmp; - ri->fn.flags &= ~OSPF_RT_PERSISTENT; + ri->area_net = 0; reset_ri(ri); } FIB_WALK_END; @@ -1647,8 +1647,8 @@ ospf_rt_reset(struct ospf_proto *p) en->nhs = NULL; en->lb = IPA_NONE; - if (en->rtcalc == LSA_RTCALC) - en->rtcalc = LSA_STALE; + if (en->mode == LSA_M_RTCALC) + en->mode = LSA_M_STALE; } WALK_LIST(oa, p->area_list) @@ -1683,14 +1683,9 @@ ospf_rt_reset(struct ospf_proto *p) } } -static void -ospf_flush_stale(struct ospf_proto *p) -{ -} - /** * ospf_rt_spf - calculate internal routes - * @p: OSPF protocol + * @p: OSPF protocol instance * * Calculation of internal paths in an area is described in 16.1 of RFC 2328. * It's based on Dijkstra's shortest path tree algorithms. @@ -2064,8 +2059,8 @@ again1: rte_update(&p->p, ne, NULL); } - /* Remove unused rt entry. Entries with any flags are persistent. */ - if (!nf->n.type && !nf->external_rte) // XXXX + /* Remove unused rt entry, some special entries are persistent */ + if (!nf->n.type && !nf->external_rte && !nf->area_net) { FIB_ITERATE_PUT(&fit, nftmp); fib_delete(fib, nftmp); @@ -2096,6 +2091,6 @@ again2: /* Cleanup stale LSAs */ WALK_SLIST(en, p->lsal) - if (en->rtcalc == LSA_STALE) + if (en->mode == LSA_M_STALE) ospf_flush_lsa(p, en); } diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h index 77d7080b..61936f3c 100644 --- a/proto/ospf/rt.h +++ b/proto/ospf/rt.h @@ -2,9 +2,10 @@ * BIRD -- OSPF * * (c) 2000--2004 Ondrej Filip + * (c) 2009--2014 Ondrej Zajicek + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. - * */ #ifndef _BIRD_OSPF_RT_H_ @@ -28,12 +29,17 @@ typedef struct orta #define ORTA_ASBR OPT_RT_E #define ORTA_ABR OPT_RT_B /* - * For ORT_NET routes, the field is almost unused with one - * exception: ORTA_PREF for external routes means that the route is - * preferred in AS external route selection according to 16.4.1. - - * it is intra-area path using non-backbone area. In other words, - * the forwarding address (or ASBR if forwarding address is zero) is - * intra-area (type == RTS_OSPF) and its area is not a backbone. + * For ORT_NET routes, there are just several flags for external routes: + * + * ORTA_PREF for external routes means that the route is preferred in AS + * external route selection according to 16.4.1. - it is intra-area path using + * non-backbone area. In other words, the forwarding address (or ASBR if + * forwarding address is zero) is intra-area (type == RTS_OSPF) and its area + * is not a backbone. + * + * ORTA_NSSA means that the entry represents an NSSA route, and ORTA_PROP + * means that the NSSA route has propagate-bit set. These flags are used in + * NSSA translation. */ #define ORTA_PREF 0x80000000 #define ORTA_NSSA 0x40000000 @@ -51,41 +57,38 @@ typedef struct orta } orta; - -/* Values for fn.flags in struct ort */ -#define OSPF_RT_PERSISTENT 0x01 - typedef struct ort { /* - * We use OSPF_RT_PERSISTENT to mark persistent rt entries, that are - * needed for summary LSAs that don't have 'proper' rt entry (area - * networks + default to stubs) to keep uid stable (used for LSA ID - * in OSPFv3 - see fibnode_to_lsaid()). + * Most OSPF routing table entries are for computed OSPF routes, these have + * defined n.type. There are also few other cases: entries for configured area + * networks (these have area_net field set) and entries for external routes + * exported to OSPF (these have external_rte field set). These entries are + * kept even if they do not contain 'proper' rt entry. That is needed to keep + * allocated stable UID numbers (fn.uid), which are used as LSA IDs in OSPFv3 + * (see fibnode_to_lsaid()) for related LSAs (network summary LSAs in the + * first case, external or NSSA LSAs in the second case). Entries for external + * routes also have a second purpose - to prevent NSSA translation of received + * NSSA routes if regular external routes were already originated for the same + * network (see check_nssa_lsa()). * - * We use ORT_RT_EXPORT and ORT_RT_NSSA to note whether the - * external/NSSA route was originated from the route export (in - * ospf_rt_notify()) or from the NSSA route translation (in - * check_nssa_lsa()). - * - * old_* values are here to represent the last route update. old_rta - * is cached (we keep reference), mainly for multipath nexthops. - * old_rta == NULL means route was not in the last update, in that - * case other old_* values are not valid. + * old_* values are here to represent the last route update. old_rta is cached + * (we keep reference), mainly for multipath nexthops. old_rta == NULL means + * route was not in the last update, in that case other old_* values are not + * valid. */ struct fib_node fn; orta n; u32 old_metric1, old_metric2, old_tag, old_rid; rta *old_rta; u8 external_rte; + u8 area_net; } ort; static inline int rt_is_nssa(ort *nf) { return nf->n.options & ORTA_NSSA; } -#define EXT_EXPORT 1 -#define EXT_NSSA 2 /* * Invariants for structs top_hash_entry (nodes of LSA db) @@ -97,7 +100,7 @@ static inline int rt_is_nssa(ort *nf) * - beware, nhs is not valid after SPF calculation * * Invariants for structs orta nodes of fib tables po->rtf, oa->rtr: - * - nodes may be invalid (fn.type == 0), in that case other invariants don't hold + * - nodes may be invalid (n.type == 0), in that case other invariants don't hold * - n.metric1 may be at most a small multiple of LSINFINITY, * therefore sums do not overflow * - n.oa is always non-NULL diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 5ed3cf7a..bc2de79f 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -40,11 +40,13 @@ static inline void lsab_reset(struct ospf_proto *p); * new routing table calculation is necessary. This is described in 13.2 of RFC * 2328. This function is for received LSA only, locally originated LSAs are * installed by ospf_originate_lsa(). + * + * The LSA body in @body is expected to be mb_allocated by the caller and its + * ownership is transferred to the LSA entry structure. */ struct top_hash_entry * ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body) { - /* LSA can be temporary, but body must be mb_allocated. */ struct top_hash_entry *en; int change = 0; @@ -61,8 +63,8 @@ ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u3 memcmp(en->lsa_body, body, lsa->length - sizeof(struct ospf_lsa_header))) change = 1; - DBG("Inst lsa: Id: %R, Rt: %R, Type: %u, Age: %u, Sum: %u, Sn: 0x%x\n", - lsa->id, lsa->rt, lsa->type, lsa->age, lsa->checksum, lsa->sn); + if ((en->lsa.age == LSA_MAXAGE) && (lsa->age == LSA_MAXAGE)) + change = 0; mb_free(en->lsa_body); en->lsa_body = body; @@ -70,16 +72,43 @@ ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u3 en->init_age = en->lsa.age; en->inst_time = now; + /* + * We do not set en->mode. It is either default LSA_M_BASIC, or in a special + * case when en is local but flushed, there is postponed LSA, self-originated + * LSA is received and ospf_install_lsa() is called from ospf_advance_lse(), + * then we have en->mode from the postponed LSA origination. + */ + + OSPF_TRACE(D_EVENTS, "Installing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x, Age: %u", + en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn, en->lsa.age); + if (change) - schedule_rtcalc(p); + ospf_schedule_rtcalc(p); return en; } +/** + * ospf_advance_lsa - handle received unexpected self-originated LSA + * @p: OSPF protocol instance + * @en: current LSA entry or NULL + * @lsa: new LSA header + * @type: type of LSA + * @domain: domain of LSA + * @body: pointer to LSA body + * + * This function handles received unexpected self-originated LSA (@lsa, @body) + * by either advancing sequence number of the local LSA instance (@en) and + * propagating it, or installing the received LSA and immediately flushing it + * (if there is no local LSA; i.e., @en is NULL or MaxAge). + * + * The LSA body in @body is expected to be mb_allocated by the caller and its + * ownership is transferred to the LSA entry structure or it is freed. + */ void ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body) { - // OSPF_TRACE(D_EVENTS, "Reflooding new self-originated LSA with newer sequence number"); + /* RFC 2328 13.4 */ if (en && (en->lsa.age < LSA_MAXAGE)) { @@ -101,6 +130,9 @@ ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ls en->init_age = 0; en->inst_time = now; lsasum_calculate(&en->lsa, en->lsa_body); + + OSPF_TRACE(D_EVENTS, "Advancing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x", + en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn); } else { @@ -129,6 +161,11 @@ ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ls en->lsa.age = LSA_MAXAGE; en->init_age = lsa->age; en->inst_time = now; + + OSPF_TRACE(D_EVENTS, "Resetting LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x", + en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn); + OSPF_TRACE(D_EVENTS, "Postponing LSA: Type: %04x, Id: %R, Rt: %R", + en->lsa_type, en->lsa.id, en->lsa.rt); } } else @@ -137,6 +174,7 @@ ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ls * We do not have received LSA in the database. We have to flush the * received LSA. It has to be installed in the database to secure * retransmissions. Note that the received LSA may already be MaxAge. + * Also note that en->next_lsa_* may be defined. */ lsa->age = LSA_MAXAGE; @@ -150,7 +188,7 @@ ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ls * the neighbor we received it from), we cheat a bit here. */ - ospf_lsupd_flood(p, en, NULL); + ospf_flood_lsa(p, en, NULL); } @@ -167,11 +205,11 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa /* Prepare to flush old LSA */ if (en->lsa.age != LSA_MAXAGE) { - OSPF_TRACE(D_EVENTS, "Resetting LSA: Type: %04x, Id: %R, Rt: %R", - en->lsa_type, en->lsa.id, en->lsa.rt); + OSPF_TRACE(D_EVENTS, "Resetting LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x", + en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn); en->lsa.age = LSA_MAXAGE; - ospf_lsupd_flood(p, en, NULL); + ospf_flood_lsa(p, en, NULL); return 0; } @@ -183,9 +221,6 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa en->lsa.sn = LSA_ZEROSEQNO; } - OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R", - en->lsa_type, en->lsa.id, en->lsa.rt); - /* * lsa.type_raw is initialized by ospf_hash_get() to OSPFv3 LSA type. * lsa_set_options() implicitly converts it to OSPFv2 LSA type, assuming that @@ -205,7 +240,13 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa en->inst_time = now; lsasum_calculate(&en->lsa, en->lsa_body); - ospf_lsupd_flood(p, en, NULL); + OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x", + en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn); + + ospf_flood_lsa(p, en, NULL); + + if (en->mode == LSA_M_BASIC) + ospf_schedule_rtcalc(p); return 1; } @@ -244,15 +285,20 @@ ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa) { log(L_ERR "%s: LSA ID collision for %I/%d", p->p.name, lsa->nf->fn.prefix, lsa->nf->fn.pxlen); + + en = NULL; goto drop; } - /* XXXX check for maxage or opts change */ + if (en->mode != lsa->mode) + en->mode = lsa->mode; if (en->next_lsa_body) { /* Ignore the new LSA if it is the same as the scheduled one */ - if ((lsa_blen == en->next_lsa_blen) && !memcmp(lsa_body, en->next_lsa_body, lsa_blen)) + if ((lsa_blen == en->next_lsa_blen) && + !memcmp(lsa_body, en->next_lsa_body, lsa_blen) && + (!ospf_is_v2(p) || (lsa->opts == en->next_lsa_opts))) goto drop; /* Free scheduled LSA */ @@ -263,13 +309,19 @@ ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa) } /* Ignore the the new LSA if is the same as the current one */ - if ((lsa_length == en->lsa.length) && !memcmp(lsa_body, en->lsa_body, lsa_blen)) + if ((en->lsa.age < LSA_MAXAGE) && + (lsa_length == en->lsa.length) && + !memcmp(lsa_body, en->lsa_body, lsa_blen) && + (!ospf_is_v2(p) || (lsa->opts == lsa_get_options(&en->lsa)))) goto drop; lsa_body = lsab_flush(p); if (! ospf_do_originate_lsa(p, en, lsa_body, lsa_blen, lsa->opts)) { + OSPF_TRACE(D_EVENTS, "Postponing LSA: Type: %04x, Id: %R, Rt: %R", + en->lsa_type, en->lsa.id, en->lsa.rt); + en->next_lsa_body = lsa_body; en->next_lsa_blen = lsa_blen; en->next_lsa_opts = lsa->opts; @@ -293,8 +345,6 @@ ospf_originate_next_lsa(struct ospf_proto *p, struct top_hash_entry *en) en->next_lsa_body = NULL; en->next_lsa_blen = 0; en->next_lsa_opts = 0; - - // XXXX: schedule_rtcalc(p); } static void @@ -309,8 +359,8 @@ ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en) * switched lsa.age to LSA_MAXAGE. */ - OSPF_TRACE(D_EVENTS, "Refreshing LSA: Type: %04x, Id: %R, Rt: %R", - en->lsa_type, en->lsa.id, en->lsa.rt); + OSPF_TRACE(D_EVENTS, "Refreshing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x", + en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn); ASSERT(en->next_lsa_body == NULL); @@ -324,7 +374,7 @@ ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en) en->next_lsa_opts = ospf_is_v2(p) ? lsa_get_options(&en->lsa) : 0; en->lsa.age = LSA_MAXAGE; - ospf_lsupd_flood(p, en, NULL); + ospf_flood_lsa(p, en, NULL); return; } @@ -333,7 +383,7 @@ ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en) en->init_age = 0; en->inst_time = now; lsasum_calculate(&en->lsa, en->lsa_body); - ospf_lsupd_flood(p, en, NULL); + ospf_flood_lsa(p, en, NULL); } /** @@ -349,16 +399,13 @@ ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en) * immediately removed when being flushed, the caller may assume that @en still * exists after the call. The function is the opposite of ospf_originate_lsa() * and is supposed to do the right thing even in cases of postponed - * origination. Note that this function do not schedule routing table - * calculation, the caller is responsible to do it if necessary. + * origination. */ void ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en) { - OSPF_TRACE(D_EVENTS, "Flushing LSA: Type: %04x, Id: %R, Rt: %R", - en->lsa_type, en->lsa.id, en->lsa.rt); - - en->rtcalc = 0; + OSPF_TRACE(D_EVENTS, "Flushing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x", + en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn); if (en->next_lsa_body) { @@ -372,7 +419,12 @@ ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en) return; en->lsa.age = LSA_MAXAGE; - ospf_lsupd_flood(p, en, NULL); + ospf_flood_lsa(p, en, NULL); + + if (en->mode == LSA_M_BASIC) + ospf_schedule_rtcalc(p); + + en->mode = LSA_M_BASIC; } static void @@ -450,7 +502,6 @@ ospf_update_lsadb(struct ospf_proto *p) if (real_age >= LSA_MAXAGE) { ospf_flush_lsa(p, en); - schedule_rtcalc(p); continue; } @@ -510,9 +561,9 @@ ort_to_lsaid(struct ospf_proto *p, ort *nf) static void * -lsab_alloc(struct ospf_proto *p, unsigned size) +lsab_alloc(struct ospf_proto *p, uint size) { - unsigned offset = p->lsab_used; + uint offset = p->lsab_used; p->lsab_used += size; if (p->lsab_used > p->lsab_size) { @@ -524,7 +575,7 @@ lsab_alloc(struct ospf_proto *p, unsigned size) } static inline void * -lsab_allocz(struct ospf_proto *p, unsigned size) +lsab_allocz(struct ospf_proto *p, uint size) { void *r = lsab_alloc(p, size); bzero(r, size); @@ -547,7 +598,7 @@ lsab_reset(struct ospf_proto *p) } static inline void * -lsab_offset(struct ospf_proto *p, unsigned offset) +lsab_offset(struct ospf_proto *p, uint offset) { return ((byte *) p->lsab) + offset; } @@ -815,15 +866,6 @@ prepare_rt3_lsa_body(struct ospf_proto *p, struct ospf_area *oa) rt->options = get_rt_options(p, oa, bitv) | (oa->options & LSA_OPTIONS_MASK); } -/** - * ospf_originate_rt_lsa - build new instance of router LSA - * @oa: ospf_area which is LSA built to - * - * It builds router LSA walking through all OSPF interfaces in - * specified OSPF area. This function is mostly called from - * area_disp(). Builds new LSA, increases sequence number (if old - * instance exists) and sets age of LSA to zero. - */ static void ospf_originate_rt_lsa(struct ospf_proto *p, struct ospf_area *oa) { @@ -834,6 +876,8 @@ ospf_originate_rt_lsa(struct ospf_proto *p, struct ospf_area *oa) .opts = oa->options }; + OSPF_TRACE(D_EVENTS, "Updating router state for area %R", oa->areaid); + if (ospf_is_v2(p)) prepare_rt2_lsa_body(p, oa); else @@ -908,15 +952,6 @@ prepare_net3_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa) net->optx = options & LSA_OPTIONS_MASK; } -/** - * ospf_originate_net_lsa - originates of deletes network LSA - * @ifa: interface which is LSA originated for - * - * Interface counts number of adjacent neighbors. If this number is - * lower than one or interface is not in state %OSPF_IS_DR it deletes - * and premature ages instance of network LSA for specified interface. - * In other case, new instance of network LSA is originated. - */ static void ospf_originate_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa) { @@ -928,6 +963,8 @@ ospf_originate_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa) .ifa = ifa }; + OSPF_TRACE(D_EVENTS, "Updating network state for %s (Id: %R)", ifa->ifname, lsa.id); + if (ospf_is_v2(p)) prepare_net2_lsa_body(p, ifa); else @@ -977,10 +1014,9 @@ prepare_sum3_rt_lsa_body(struct ospf_proto *p, u32 drid, u32 metric, u32 options void ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric) { - struct top_hash_entry *en; - struct ospf_new_lsa lsa = { .type = LSA_T_SUM_NET, + .mode = LSA_M_RTCALC, .dom = oa->areaid, .id = ort_to_lsaid(p, nf), .opts = oa->options, @@ -992,32 +1028,26 @@ ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, else prepare_sum3_net_lsa_body(p, nf, metric); - en = ospf_originate_lsa(p, &lsa); - en->rtcalc = LSA_RTCALC; + ospf_originate_lsa(p, &lsa); } void ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric, u32 options) { - struct top_hash_entry *en; - u32 rid = ipa_to_rid(nf->fn.prefix); - - /* In OSPFv3, LSA ID is meaningless, but we still use Router ID of ASBR */ - struct ospf_new_lsa lsa = { .type = LSA_T_SUM_RT, + .mode = LSA_M_RTCALC, .dom = oa->areaid, - .id = rid, + .id = ipa_to_rid(nf->fn.prefix), /* Router ID of ASBR, irrelevant for OSPFv3 */ .opts = oa->options }; if (ospf_is_v2(p)) prepare_sum2_lsa_body(p, 0, metric); else - prepare_sum3_rt_lsa_body(p, rid, metric, options & LSA_OPTIONS_MASK); + prepare_sum3_rt_lsa_body(p, lsa.id, metric, options & LSA_OPTIONS_MASK); - en = ospf_originate_lsa(p, &lsa); - en->rtcalc = LSA_RTCALC; + ospf_originate_lsa(p, &lsa); } @@ -1076,13 +1106,15 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf, /** * originate_ext_lsa - new route received from nest and filters + * @p: OSPF protocol instance * @oa: ospf_area for which LSA is originated * @nf: network prefix and mask - * @src: the source of origination of the LSA (EXT_EXPORT/EXT_NSSA) - * @metric: the metric of a route (possibly with appropriate E-bit) + * @mode: the mode of the LSA (LSA_M_EXPORT or LSA_M_RTCALC) + * @metric: the metric of a route + * @ebit: E-bit for route metric (bool) * @fwaddr: the forwarding address * @tag: the route tag - * @pbit: P-bit for NSSA LSAs, ignored for external LSAs + * @pbit: P-bit for NSSA LSAs (bool), ignored for external LSAs * * If I receive a message that new route is installed, I try to originate an * external LSA. If @oa is an NSSA area, NSSA-LSA is originated instead. @@ -1091,13 +1123,12 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf, * the export from ospf_rt_notify(), or the NSSA-EXT translation. */ void -ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 rtcalc, +ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 mode, u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit) { - struct top_hash_entry *en; - struct ospf_new_lsa lsa = { .type = oa ? LSA_T_NSSA : LSA_T_EXT, + .mode = mode, /* LSA_M_EXPORT or LSA_M_RTCALC */ .dom = oa ? oa->areaid : 0, .id = ort_to_lsaid(p, nf), .opts = oa ? (pbit ? OPT_P : 0) : OPT_E, @@ -1109,8 +1140,7 @@ ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 r else prepare_ext3_lsa_body(p, nf, metric, ebit, fwaddr, tag, oa && pbit); - en = ospf_originate_lsa(p, &lsa); - en->rtcalc = rtcalc; + ospf_originate_lsa(p, &lsa); } static void @@ -1228,7 +1258,7 @@ ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U /* Old external route might blocked some NSSA translation */ if ((p->areano > 1) && rt_is_nssa(nf) && nf->n.oa->translate) - schedule_rtcalc(p); + ospf_schedule_rtcalc(p); return; } @@ -1262,7 +1292,7 @@ ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U } nf = (ort *) fib_get(&p->rtf, &n->n.prefix, n->n.pxlen); - ospf_originate_ext_lsa(p, oa, nf, 0, metric, ebit, fwd, tag, 1); + ospf_originate_ext_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1); nf->external_rte = 1; } @@ -1320,6 +1350,8 @@ ospf_originate_link_lsa(struct ospf_proto *p, struct ospf_iface *ifa) .ifa = ifa }; + OSPF_TRACE(D_EVENTS, "Updating link state for %s (Id: %R)", ifa->ifname, lsa.id); + prepare_link_lsa_body(p, ifa); ifa->link_lsa = ospf_originate_lsa(p, &lsa); @@ -1576,6 +1608,8 @@ ospf_originate_prefix_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa) ifa->pxn_lsa = ospf_originate_lsa(p, &lsa); } +static inline int breaks_minlsinterval(struct top_hash_entry *en) +{ return en && (en->lsa.age < LSA_MAXAGE) && ((en->inst_time + MINLSINTERVAL) > now); } void ospf_update_topology(struct ospf_proto *p) @@ -1587,6 +1621,25 @@ ospf_update_topology(struct ospf_proto *p) { if (oa->update_rt_lsa) { + /* + * Generally, MinLSInterval is enforced in ospf_do_originate_lsa(), but + * origination of (prefix) router LSA is a special case. We do not want to + * prepare a new router LSA body and then postpone it in en->next_lsa_body + * for later origination, because there are side effects (updates of + * rt_pos_* and px_pos_* in ospf_iface structures) during that, which may + * confuse routing table calculation if executed after LSA body + * preparation but before final LSA origination (as rtcalc would use + * current rt_pos_* indexes but the old router LSA body). + * + * Here, we ensure that MinLSInterval is observed and we do not even try + * to originate these LSAs if it is not. Therefore, origination, when + * requested, will succeed unless there is also a seqnum wrapping, which + * is not a problem because in that case rtcalc is blocked by MaxAge. + */ + + if (breaks_minlsinterval(oa->rt) || breaks_minlsinterval(oa->pxr_lsa)) + continue; + ospf_originate_rt_lsa(p, oa); ospf_originate_prefix_rt_lsa(p, oa); oa->update_rt_lsa = 0; @@ -1624,8 +1677,6 @@ ospf_update_topology(struct ospf_proto *p) ifa->update_net_lsa = 0; } } - - // XXXX schedule_rtcalc(p); } @@ -1664,7 +1715,7 @@ ospf_top_hash_u32(u32 a) return a; } -static unsigned +static uint ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type) { /* In OSPFv2, we don't know Router ID when looking for network LSAs. @@ -1685,12 +1736,14 @@ ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type) /** * ospf_top_new - allocated new topology database * @p: OSPF protocol instance + * @pool: pool for allocation * - * this dynamically hashed structure is often used for keeping lsas. mainly - * its used in @ospf_area structure. + * This dynamically hashed structure is used for keeping LSAs. Mainly it is used + * for the LSA database of the OSPF protocol, but also for LSA retransmission + * and request lists of OSPF neighbors. */ struct top_graph * -ospf_top_new(pool *pool) +ospf_top_new(struct ospf_proto *p, pool *pool) { struct top_graph *f; @@ -1701,6 +1754,7 @@ ospf_top_new(pool *pool) ospf_top_ht_alloc(f); f->hash_entries = 0; f->hash_entries_min = 0; + f->ospf2 = ospf_is_v2(p); return f; } @@ -1715,8 +1769,8 @@ ospf_top_free(struct top_graph *f) static void ospf_top_rehash(struct top_graph *f, int step) { - unsigned int oldn, oldh; struct top_hash_entry **n, **oldt, **newt, *e, *x; + uint oldn, oldh; oldn = f->hash_size; oldt = f->hash_table; @@ -1752,7 +1806,7 @@ ospf_hash_find(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type) e = e->next; /* Hide hash entry with empty lsa_body */ - return e->lsa_body ? e : NULL; + return (e && e->lsa_body) ? e : NULL; } /* In OSPFv2, lsa.id is the same as lsa.rt for router LSA. In OSPFv3, we don't know @@ -1780,11 +1834,15 @@ ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr) return rv; } +/* + * ospf_hash_find_rt3_first() and ospf_hash_find_rt3_next() are used exclusively + * for lsa_walk_rt_init(), lsa_walk_rt(), therefore they skip MaxAge entries. + */ static inline struct top_hash_entry * find_matching_rt3(struct top_hash_entry *e, u32 domain, u32 rtr) { while (e && (e->lsa.rt != rtr || e->lsa_type != LSA_T_RT || - e->domain != domain || e->lsa_body == NULL)) + e->domain != domain || e->lsa.age == LSA_MAXAGE)) e = e->next; return e; } @@ -1917,7 +1975,7 @@ ospf_dump_lsa(struct top_hash_entry *he, struct proto *p) void ospf_top_dump(struct top_graph *f, struct proto *p) { - unsigned int i; + uint i; OSPF_TRACE(D_EVENTS, "Hash entries: %d", f->hash_entries); for (i = 0; i < f->hash_size; i++) @@ -1928,115 +1986,3 @@ ospf_top_dump(struct top_graph *f, struct proto *p) } } */ - - - -#if 0 - -void -update_rt_lsa(struct ospf_area *oa) -{ - struct ospf_proto *po = oa->po; - - if ((oa->rt) && ((oa->rt->inst_t + MINLSINTERVAL)) > now) - return; - - originate_rt_lsa(oa); - if (ospf_is_v3(p)) - originate_prefix_rt_lsa(oa); - - schedule_rtcalc(p); - oa->origrt = 0; -} - - - - -static inline int -check_sum2_net_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric) -{ - struct ospf_lsa_sum2 *sum = en->lsa_body; - - if (fn->pxlen != ip4_masklen(sum->netmask)) - return -1; - - return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric); -} - -static inline int -check_sum3_net_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric) -{ - struct ospf_lsa_sum3_net *sum = en->lsa_body; - ip6_addr prefix; - int pxlen; - u8 pxopts; - u16 rest; - lsa_get_ipv6_prefix(sum->prefix, &prefix, &pxlen, &pxopts, &rest); - - - if ((fn->pxlen != pxlen) || !ip6_equal(fn->prefix, prefix)) - return -1; - - return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric); -} - - -static int -check_sum_net_lsa(struct ospf_proto *po, struct top_hash_entry *en, struct fib_node *fn, u32 metric) -{ - int rv = ospf_is_v2(po) ? - check_sum2_net_lsa(en, fn, metric) : - check_sum3_net_lsa(en, fn, metric); - - if (rv < 0) - log(L_ERR "%s: LSAID collision for %I/%d", p->p.name, fn->prefix, fn->pxlen); - - return rv; -} - -static int -check_sum_rt_lsa(struct ospf_proto *po, struct top_hash_entry *en, u32 drid, u32 metric, u32 options) -{ - if (en->lsa.sn == LSA_MAXSEQNO) - return 0; - - if (ospf_is_v2(po)) - { - struct ospf_lsa_sum2 *sum = en->lsa_body; - return (sum->metric == metric); - } - else - { - struct ospf_lsa_sum3_rt *sum = en->lsa_body; - return (sum->options == options) && (sum->metric == metric) && (sum->drid == drid); - } -} - - - - - - - - OSPF_TRACE(D_EVENTS, "Originating router-LSA for area %R", oa->areaid); - OSPF_TRACE(D_EVENTS, "Originating network-LSA for iface %s", ifa->ifname); - OSPF_TRACE(D_EVENTS, "Originating net-summary-LSA for %I/%d (metric %d)", fn->prefix, fn->pxlen, metric); - OSPF_TRACE(D_EVENTS, "Originating rt-summary-LSA for %R (metric %d)", rid, metric); - OSPF_TRACE(D_EVENTS, "Originating %s-LSA for %I/%d", - nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen); - OSPF_TRACE(D_EVENTS, "Originating link-LSA for iface %s", ifa->ifname); - OSPF_TRACE(D_EVENTS, "Originating router prefix-LSA for area %R", oa->areaid); - OSPF_TRACE(D_EVENTS, "Originating network prefix-LSA for iface %s", ifa->ifname); - - - en = ospf_hash_find(po->gr, lsa.dom, lsa.id, po->router_id, lsa.type); - if (en && check_ext_lsa(po, en, fn, metric, fwaddr, tag)) - return; - - *length = sizeof(struct ospf_lsa_header) + po->lsab_used; - return lsab_flush(po); - - *length = po->lsab_used + sizeof(struct ospf_lsa_header); - return lsab_flush(po); - -#endif diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h index e4ea79f7..b34689e2 100644 --- a/proto/ospf/topology.h +++ b/proto/ospf/topology.h @@ -18,7 +18,7 @@ struct top_hash_entry in intra-area routing table calculation */ struct top_hash_entry *next; /* Next in hash chain */ struct ospf_lsa_header lsa; - u16 lsa_type; /* lsa.type processed and converted to common values */ + u16 lsa_type; /* lsa.type processed and converted to common values (LSA_T_*) */ u16 init_age; /* Initial value for lsa.age during inst_time */ u32 domain; /* Area ID for area-wide LSAs, Iface ID for link-wide LSAs */ // struct ospf_area *oa; @@ -37,13 +37,115 @@ struct top_hash_entry #define OUTSPF 0 #define CANDIDATE 1 #define INSPF 2 - u8 rtcalc; /* LSA generated during RT calculation (LSA_RTCALC or LSA_STALE)*/ + u8 mode; /* LSA generated during RT calculation (LSA_RTCALC or LSA_STALE)*/ u8 nhs_reuse; /* Whether nhs nodes can be reused during merging. See a note in rt.c:merge_nexthops() */ }; -#define LSA_RTCALC 1 -#define LSA_STALE 2 + +/* Prevents ospf_hash_find() to ignore the entry, for p->lsrqh and p->lsrth */ +#define LSA_BODY_DUMMY ((void *) 1) + +/* + * LSA entry life cycle + * + * LSA entries are created by ospf_originate_lsa() (for locally originated LSAs) + * or ospf_install_lsa() (for LSAs received from neighbors). A regular (like + * newly originated) LSA entry has defined lsa_body nad lsa.age < %LSA_MAXAGE. + * When the LSA is requested to be flushed by ospf_flush_lsa(), the lsa.age is + * set to %LSA_MAXAGE and flooded. Flush process is finished asynchronously, + * when (at least) flooding is acknowledged by neighbors. This is detected in + * ospf_update_lsadb(), then ospf_clear_lsa() is called to free the LSA body but + * the LSA entry is kept. Such LSA does not formally exist, we keep an empty + * entry (until regular timeout) to know inst_time and lsa.sn in the case of + * later reorigination. After the timeout, LSA is removed by ospf_remove_lsa(). + * + * When LSA origination is requested (by ospf_originate_lsa()). but it is not + * possible to do that immediately (because of MinLSInterval or because the + * sequence number is wrapping), The new LSA is scheduled for later origination + * in next_lsa_* fields of the LSA entry. The later origination is handled by + * ospf_originate_next_lsa() called from ospf_update_lsadb(). We can see that + * both real origination and final flush is asynchronous to ospf_originate_lsa() + * and ospf_flush_lsa(). + * + * LSA entry therefore could be in three basic states: + * R - regular (lsa.age < %LSA_MAXAGE, lsa_body != NULL) + * F - flushing (lsa.age == %LSA_MAXAGE, lsa_body != NULL) + * E - empty (lsa.age == %LSA_MAXAGE, lsa_body == NULL) + * + * And these states are doubled based on whether the next LSA is scheduled + * (next_lsa_body != NULL, -n suffix) or not (next_lsa_body == NULL). We also + * use X for a state of non-existentce. We have this basic state graph + * (transitions from any state to R are omitted for clarity): + * + * X --> R ---> F ---> E --> X + * | \ / | | + * | \/ | | + * | /\ | | + * | / \ | | + * Rn --> Fn --> En + * + * The transitions are: + * + * any state -> R - new LSA origination requested and executed + * R -> Rn, F -> Fn, E -> En - new LSA origination requested and postponed + * R -> Fn - new LSA origination requested, seqnum wrapping + * Rn,Fn,En -> R - postponed LSA finally originated + * R -> R - LSA refresh done + * R -> Fn - LSA refresh with seqnum wrapping + * R -> F, Rn -> Fn - LSA age timeout + * R,Rn,Fn -> F, En -> E - LSA flush requested + * F -> E, Fn -> En - LSA flush done (acknowledged) + * E -> X - LSA real age timeout (or immediate for received LSA) + * + * The 'origination requested' and 'flush requested' transitions are triggered + * and done by ospf_originate_lsa() and ospf_flush_lsa(), the rest is handled + * asynchronously by ospf_update_lsadb(). + * + * The situation is significantly simpler for non-local (received) LSAs - there + * is no postponed origination and after flushing is done, LSAs are immediately + * removed, so it is just X -> R -> F -> X, or X -> F -> X (when MaxAge LSA is + * received). + * + * There are also some special cases related to handling of received unknown + * self-originated LSAs in ospf_advance_lsa(): + * X -> F - LSA is received and immediately flushed + * R,Rn -> Fn - LSA with MaxSeqNo received and flushed, current LSA scheduled + */ + + +#define LSA_M_BASIC 0 +#define LSA_M_EXPORT 1 +#define LSA_M_RTCALC 2 +#define LSA_M_STALE 3 + +/* + * LSA entry modes: + * + * LSA_M_BASIC - The LSA is explicitly originated using ospf_originate_lsa() and + * explicitly flushed using ospf_flush_lsa(). When the LSA is changed, the + * routing table calculation is scheduled. This is also the mode used for LSAs + * received from neighbors. Example: Router-LSAs, Network-LSAs. + * + * LSA_M_EXPORT - like LSA_M_BASIC, but the routing table calculation does not + * depend on the LSA. Therefore, the calculation is not scheduled when the LSA + * is changed. Example: AS-external-LSAs for exported routes. + * + * LSA_M_RTCALC - The LSA has to be requested using ospf_originate_lsa() during + * each routing table calculation, otherwise it is flushed automatically at the + * end of the calculation. The LSA is a result of the calculation and not a + * source for it. Therefore, the calculation is not scheduled when the LSA is + * changed. Example: Summary-LSAs. + * + * LSA_M_STALE - Temporary state for LSA_M_RTCALC that is not requested during + * the current routing table calculation. + * + * + * Note that we do not schedule the routing table calculation when the age of + * LSA_M_BASIC LSA is changed to MaxAge because of the sequence number wrapping, + * As it will be switched back to a regular one ASAP. + */ + struct top_graph { @@ -61,6 +163,7 @@ struct top_graph struct ospf_new_lsa { u16 type; + u8 mode; u32 dom; u32 id; u16 opts; @@ -69,9 +172,8 @@ struct ospf_new_lsa struct ort *nf; }; -struct top_graph *ospf_top_new(pool *); -void ospf_top_free(struct top_graph *); -void ospf_top_dump(struct top_graph *, struct proto *); +struct top_graph *ospf_top_new(struct ospf_proto *p, pool *pool); +void ospf_top_free(struct top_graph *f); struct top_hash_entry * ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body); struct top_hash_entry * ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa); @@ -84,7 +186,7 @@ static inline void ospf_flush2_lsa(struct ospf_proto *p, struct top_hash_entry * void ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric); void ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric, u32 options); -void ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 rtcalc, u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit); +void ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 mode, u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit); void ospf_rt_notify(struct proto *P, rtable *tbl, net *n, rte *new, rte *old, ea_list *attrs); void ospf_update_topology(struct ospf_proto *p); From 742029eb782f19c05decbd443d245f12360d5e78 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 19 Jul 2014 17:28:38 +0200 Subject: [PATCH 03/11] Whitespace cleanup in OSPF. --- proto/ospf/dbdes.c | 8 +++--- proto/ospf/hello.c | 6 ++--- proto/ospf/iface.c | 13 +++++----- proto/ospf/lsalib.c | 12 ++++----- proto/ospf/lsreq.c | 2 +- proto/ospf/lsupd.c | 11 ++++---- proto/ospf/neighbor.c | 4 +-- proto/ospf/ospf.c | 43 +++++++++++++++---------------- proto/ospf/ospf.h | 10 ++++---- proto/ospf/packet.c | 10 ++++---- proto/ospf/rt.c | 60 +++++++++++++++++++++---------------------- proto/ospf/topology.c | 30 +++++++++++----------- proto/ospf/topology.h | 2 +- 13 files changed, 104 insertions(+), 107 deletions(-) diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c index 62b330ed..2b076157 100644 --- a/proto/ospf/dbdes.c +++ b/proto/ospf/dbdes.c @@ -56,7 +56,7 @@ ospf_dbdes_body(struct ospf_proto *p, struct ospf_packet *pkt, *body = ((void *) pkt) + hlen; *count = (plen - hlen) / sizeof(struct ospf_lsa_header); } - + static void ospf_dump_dbdes(struct ospf_proto *p, struct ospf_packet *pkt) { @@ -231,7 +231,7 @@ ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n, int next) tm_start(n->rxmt_timer, n->ifa->rxmtint); if (!(n->myimms & DBDES_MS)) - if (!(n->myimms & DBDES_M) && + if (!(n->myimms & DBDES_M) && !(n->imms & DBDES_M)) ospf_neigh_sm(n, INM_EXDONE); break; @@ -358,7 +358,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, rcv_imms = ps->imms; rcv_ddseq = ntohl(ps->ddseq); } - + switch (n->state) { case NEIGHBOR_DOWN: @@ -397,7 +397,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, if (!(rcv_imms & DBDES_I) && !(rcv_imms & DBDES_MS) && - (n->rid < p->router_id) && + (n->rid < p->router_id) && (n->dds == rcv_ddseq)) { /* I'm master! */ diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index 50cd8609..3aeb9f9a 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -114,7 +114,7 @@ ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) { if (i == max) { - log(L_WARN "%s: Too many neighbors on interface %s", p->p.name, ifa->ifname); + log(L_WARN "%s: Too many neighbors on interface %s", p->p.name, ifa->ifname); break; } neighbors[i] = htonl(neigh->rid); @@ -143,7 +143,7 @@ ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) int to_all = ifa->state > OSPF_IS_DROTHER; int me_elig = ifa->priority > 0; - + if (kind == OHS_POLL) /* Poll timer */ { WALK_LIST(nb, ifa->nbma_list) @@ -357,7 +357,7 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, if (ifa->type == OSPF_IT_NBMA) if ((ifa->priority == 0) && (n->priority > 0)) ospf_send_hello(n->ifa, OHS_HELLO, n); - + /* Examine list of neighbors */ for (i = 0; i < neigh_count; i++) diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 312e626a..892e8a77 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -47,7 +47,7 @@ wait_timer_hook(timer * timer) static inline uint ifa_tx_length(struct ospf_iface *ifa) { - return ifa->cf->tx_length ?: ifa->iface->mtu; + return ifa->cf->tx_length ?: ifa->iface->mtu; } static inline uint @@ -129,7 +129,7 @@ ospf_sk_open(struct ospf_iface *ifa) ifa->des_routers = IPA_NONE; if (sk_setup_broadcast(sk) < 0) - goto err; + goto err; } else { @@ -137,10 +137,10 @@ ospf_sk_open(struct ospf_iface *ifa) ifa->des_routers = ospf_is_v2(p) ? IP4_OSPF_DES_ROUTERS : IP6_OSPF_DES_ROUTERS; if (sk_setup_multicast(sk) < 0) - goto err; + goto err; if (sk_join_group(sk, ifa->all_routers) < 0) - goto err; + goto err; } } @@ -703,7 +703,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* 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(old->type, ifa->addr); + int old_type = ospf_iface_classify(old->type, ifa->addr); int new_type = ospf_iface_classify(new->type, ifa->addr); if (old_type != new_type) return 0; @@ -1132,7 +1132,7 @@ ospf_reconfigure_ifaces2(struct ospf_proto *p) ospf_iface_shutdown(ifa); ospf_iface_remove(ifa); } - + ospf_iface_new(s.oa, a, s.ip); } } @@ -1307,4 +1307,3 @@ ospf_iface_info(struct ospf_iface *ifa) cli_msg(-1015, "\tBackup designed router (IP): %I", ifa->bdrip); } } - diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c index e1af9f46..579d13e8 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -63,11 +63,11 @@ lsa_ntoh_body(void *n, void *h, u16 len) int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa) -{ +{ /* Handle inactive vlinks */ if (ifa->state == OSPF_IS_DOWN) return 0; - + /* 4.5.2 (Case 2) */ switch (LSA_SCOPE(type)) { @@ -238,7 +238,7 @@ lsasum_check(struct ospf_lsa_header *h, void *body) q = ep; for (p = sp; p < q; p++) { - /* + /* * I count with bytes from header and than from body * but if there is no body, it's appended to header * (probably checksum in update receiving) and I go on @@ -432,7 +432,7 @@ lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *r rt->fbit = ext->metric & LSA_EXT3_FBIT; if (rt->fbit) buf = lsa_get_ipv6_addr(buf, &rt->fwaddr); - else + else rt->fwaddr = IPA_NONE; rt->tag = (ext->metric & LSA_EXT3_TBIT) ? *buf++ : 0; @@ -540,7 +540,7 @@ lsa_validate_sum3_net(struct ospf_lsa_header *lsa, struct ospf_lsa_sum3_net *bod if (pxl > MAX_PREFIX_LENGTH) return 0; - if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum3_net) + + if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum3_net) + IPV6_PREFIX_SPACE(pxl))) return 0; @@ -607,7 +607,7 @@ lsa_validate_pxlist(struct ospf_lsa_header *lsa, u32 pxcount, uint offset, u8 *p u8 pxl = pxlen((u32 *) (pbuf + offset)); if (pxl > MAX_PREFIX_LENGTH) return 0; - + offset += IPV6_PREFIX_SPACE(pxl); } diff --git a/proto/ospf/lsreq.c b/proto/ospf/lsreq.c index 1685ef13..a6c0cf24 100644 --- a/proto/ospf/lsreq.c +++ b/proto/ospf/lsreq.c @@ -131,7 +131,7 @@ ospf_receive_lsreq(struct ospf_packet *pkt, struct ospf_iface *ifa, DBG("Processing requested LSA: Type: %04x, Id: %R, Rt: %R\n", type, id, rt); - en = ospf_hash_find(p->gr, domain, id, rt, type); + en = ospf_hash_find(p->gr, domain, id, rt, type); if (!en) { log(L_WARN "%s: Received LSREQ from %I for missing LSA (Type: %04x, Id: %R, Rt: %R)", diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 37c89a24..13ae54d1 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -277,7 +277,7 @@ ospf_prepare_lsupd(struct ospf_proto *p, struct ospf_iface *ifa, if (ospf_iface_assure_bufsize(ifa, pos + len) < 0) { /* Cannot fit in a tx buffer, skip that */ - log(L_ERR "%s: LSA too large to send on %s (Type: %04x, Id: %R, Rt: %R)", + log(L_ERR "%s: LSA too large to send on %s (Type: %04x, Id: %R, Rt: %R)", p->p.name, ifa->ifname, en->lsa_type, en->lsa.id, en->lsa.rt); break; } @@ -439,7 +439,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, lsa_n = ((void *) pkt) + offset; lsa_len = ntohs(lsa_n->length); offset += lsa_len; - + if ((offset > plen) || ((lsa_len % 4) != 0) || (lsa_len <= sizeof(struct ospf_lsa_header))) { @@ -504,7 +504,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, /* 13. (4) - ignore maxage LSA if i have no local copy */ if ((lsa.age == LSA_MAXAGE) && !en && (p->padj == 0)) { - /* 13.5. - schedule ACKs (tbl 19, case 5) */ + /* 13.5. - schedule ACKs (tbl 19, case 5) */ ospf_enqueue_lsack(n, lsa_n, ACKL_DIRECT); continue; } @@ -575,7 +575,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, /* 13. (5b) - flood new LSA */ int flood_back = ospf_flood_lsa(p, en, n); - /* 13.5. - schedule ACKs (tbl 19, cases 1+2) */ + /* 13.5. - schedule ACKs (tbl 19, cases 1+2) */ if (! flood_back) if ((ifa->state != OSPF_IS_BACKUP) || (n->rid == ifa->drid)) ospf_enqueue_lsack(n, lsa_n, ACKL_DELAY); @@ -593,7 +593,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, /* Duplicate LSA, treat as implicit ACK */ int implicit_ack = ospf_lsa_lsrt_down(en, n); - /* 13.5. - schedule ACKs (tbl 19, cases 3+4) */ + /* 13.5. - schedule ACKs (tbl 19, cases 3+4) */ if (implicit_ack) { if ((ifa->state == OSPF_IS_BACKUP) && (n->rid == ifa->drid)) @@ -631,4 +631,3 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, if ((n->state == NEIGHBOR_LOADING) && n->want_lsreq && !skip_lsreq) ospf_send_lsreq(p, n); } - diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index c182f0d2..ee1e8d0f 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -56,7 +56,7 @@ release_lsrtl(struct ospf_proto *p, struct ospf_neighbor *n) } /* Resets LSA request and retransmit lists. - * We do not reset DB summary list iterator here, + * We do not reset DB summary list iterator here, * it is reset during entering EXCHANGE state. */ static void @@ -496,7 +496,7 @@ ospf_dr_election(struct ospf_iface *ifa) u32 old_drid = ifa->drid; u32 old_bdrid = ifa->bdrid; - + ifa->drid = ndr ? ndr->rid : 0; ifa->drip = ndr ? ndr->ip : IPA_NONE; ifa->dr_iface_id = ndr ? ndr->iface_id : 0; diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index df5fe472..d65dba3b 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -10,7 +10,7 @@ /** * DOC: Open Shortest Path First (OSPF) - * + * * The OSPF protocol is quite complicated and its complex implemenation is split * to many files. In |ospf.c|, you will find mainly the interface for * communication with the core (e.g., reconfiguration hooks, shutdown and @@ -68,7 +68,7 @@ * (&ospf_proto->tick). It is responsible for aging and flushing of LSAs in the * database, updating topology information in LSAs and for routing table * calculation. - * + * * To every &ospf_iface, we connect one or more &ospf_neighbor's -- a structure * containing many timers and queues for building adjacency and for exchange of * routing messages. @@ -179,7 +179,7 @@ ospf_area_remove(struct ospf_area *oa) /* We suppose that interfaces are already removed */ ospf_flush_area(p, oa->areaid); - + fib_free(&oa->rtr); fib_free(&oa->net_fib); fib_free(&oa->enet_fib); @@ -207,7 +207,7 @@ static struct ospf_iface * ospf_find_vlink(struct ospf_proto *p, u32 voa, u32 vid) { struct ospf_iface *ifa; - WALK_LIST(ifa, p->iface_list) + WALK_LIST(ifa, p->iface_list) if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa->areaid == voa) && (ifa->vid == vid)) return ifa; return NULL; @@ -291,7 +291,7 @@ ospf_dump(struct proto *P) static struct proto * ospf_init(struct proto_config *c) { - struct ospf_config *oc = (struct ospf_config *) c; + struct ospf_config *oc = (struct ospf_config *) c; struct proto *P = proto_new(c, sizeof(struct ospf_proto)); P->accept_ra_types = RA_OPTIMAL; @@ -688,7 +688,7 @@ ospf_reconfigure(struct proto *P, struct proto_config *c) ospf_area_remove(oa); ospf_schedule_rtcalc(p); - + return 1; } @@ -753,12 +753,12 @@ ospf_sh(struct proto *P) if (oa == ifa->oa) { ifano++; - WALK_LIST(n, ifa->neigh_list) - { + WALK_LIST(n, ifa->neigh_list) + { nno++; if (n->state == NEIGHBOR_FULL) adjno++; - } + } } } @@ -779,8 +779,8 @@ ospf_sh(struct proto *P) anet = (struct area_net *) nftmp; if(firstfib) { - cli_msg(-1014, "\t\tArea networks:"); - firstfib = 0; + cli_msg(-1014, "\t\tArea networks:"); + firstfib = 0; } cli_msg(-1014, "\t\t\t%1I/%u\t%s\t%s", anet->fn.prefix, anet->fn.pxlen, anet->hidden ? "Hidden" : "Advertise", anet->active ? "Active" : ""); @@ -793,8 +793,8 @@ ospf_sh(struct proto *P) anet = (struct area_net *) nftmp; if(firstfib) { - cli_msg(-1014, "\t\tArea external networks:"); - firstfib = 0; + cli_msg(-1014, "\t\tArea external networks:"); + firstfib = 0; } cli_msg(-1014, "\t\t\t%1I/%u\t%s\t%s", anet->fn.prefix, anet->fn.pxlen, anet->hidden ? "Hidden" : "Advertise", anet->active ? "Active" : ""); @@ -917,7 +917,7 @@ lsa_compare_for_state(const void *p1, const void *p2) return lsa1->sn - lsa2->sn; } - else + else { if (lsa1->rt < lsa2->rt) return -1; @@ -936,7 +936,7 @@ lsa_compare_for_state(const void *p1, const void *p2) if (px1 != px2) return px1 - px2; - + return lsa1->sn - lsa2->sn; } } @@ -1004,7 +1004,7 @@ show_lsa_router(struct ospf_proto *p, struct top_hash_entry *he, int verbose) struct ospf_lsa_header *net_lsa = &(net_he->lsa); struct ospf_lsa_net *net_ln = net_he->lsa_body; - cli_msg(-1016, "\t\tnetwork %I/%d metric %u", + cli_msg(-1016, "\t\tnetwork %I/%d metric %u", ipa_from_u32(net_lsa->id & net_ln->optx), u32_masklen(net_ln->optx), rtl.metric); } @@ -1085,7 +1085,7 @@ show_lsa_external(struct top_hash_entry *he, int ospf2) he->domain = 0; /* Unmark the LSA */ lsa_parse_ext(he, ospf2, &rt); - + if (rt.fbit) bsprintf(str_via, " via %I", rt.fwaddr); @@ -1145,7 +1145,7 @@ ospf_sh_state(struct proto *P, int verbose, int reachable) return; } - /* We store interesting area-scoped LSAs in array hea and + /* We store interesting area-scoped LSAs in array hea and global-scoped (LSA_T_EXT) LSAs in array hex */ int num = p->gr->hash_entries; @@ -1343,7 +1343,7 @@ lsa_compare_for_lsadb(const void *p1, const void *p2) if (lsa1->rt != lsa2->rt) return lsa1->rt - lsa2->rt; - + if (lsa1->id != lsa2->id) return lsa1->id - lsa2->id; @@ -1390,7 +1390,7 @@ ospf_sh_lsadb(struct lsadb_show_data *ld) struct ospf_lsa_header *lsa = &(hea[i]->lsa); u16 lsa_type = lsa->type_raw & type_mask; u16 dscope = LSA_SCOPE(hea[i]->lsa_type); - + /* Hack: 1 is used for LSA_SCOPE_LINK, fixed by & 0xf000 */ if (ld->scope && (dscope != (ld->scope & 0xf000))) continue; @@ -1407,7 +1407,7 @@ ospf_sh_lsadb(struct lsadb_show_data *ld) if (ld->router && (lsa->rt != ld->router)) continue; - + if ((dscope != last_dscope) || (hea[i]->domain != last_domain)) { cli_msg(-1017, ""); @@ -1456,4 +1456,3 @@ struct protocol proto_ospf = { .get_attr = ospf_get_attr, .get_route_info = ospf_get_route_info }; - diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index e5713628..7e5996f6 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -127,8 +127,8 @@ struct ospf_area_config u8 translator; /* Translator role, for NSSA ABR */ u32 transint; /* Translator stability interval */ list patt_list; /* List of iface configs (struct ospf_iface_patt) */ - list net_list; /* List of aggregate networks for that area */ - list enet_list; /* List of aggregate external (NSSA) networks */ + list net_list; /* List of aggregate networks for that area */ + list enet_list; /* List of aggregate external (NSSA) networks */ list stubnet_list; /* List of stub networks added to Router LSA */ }; @@ -163,7 +163,7 @@ struct nbma_node node n; ip_addr ip; byte eligible; - byte found; + byte found; }; struct ospf_iface_patt @@ -215,7 +215,7 @@ struct ospf_iface_patt #define HELLOINT_D 10 #define POLLINT_D 20 #define DEADC_D 4 -#define WAIT_DMH 4 +#define WAIT_DMH 4 /* Value of Wait timer - not found it in RFC * - using 4*HELLO */ @@ -334,7 +334,7 @@ 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_dr; /* Socket is a member of designated routers group */ + u8 sk_dr; /* Socket is a member of designated routers group */ u8 marked; /* Used in OSPF reconfigure, 2 for force restart */ u16 rxbuf; /* Buffer size */ u16 tx_length; /* Soft TX packet length limit, usually MTU */ diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index 96351178..d64d7d6b 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -92,7 +92,7 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) ifa->csn_use = now; } - /* We must have sufficient delay between sending a packet and increasing + /* We must have sufficient delay between sending a packet and increasing CSN to prevent reordering of packets (in a network) with different CSNs */ if ((now - ifa->csn_use) > 1) ifa->csn++; @@ -253,7 +253,7 @@ ospf_rx_hook(sock *sk, int size) struct ospf_iface *ifa = sk->data; struct ospf_proto *p = ifa->oa->po; - int src_local, dst_local, dst_mcast; + int src_local, dst_local, dst_mcast; src_local = ipa_in_net(sk->faddr, ifa->addr->prefix, ifa->addr->pxlen); dst_local = ipa_equal(sk->laddr, ifa->addr->ip); dst_mcast = ipa_equal(sk->laddr, ifa->all_routers) || ipa_equal(sk->laddr, ifa->des_routers); @@ -264,7 +264,7 @@ ospf_rx_hook(sock *sk, int size) * In OSPFv2, they might be for other ospf_ifaces (with different IP * prefix) on the same real iface, so we don't log it. We enforce * that (src_local || dst_local), therefore we are eliminating all - * such cases. + * such cases. */ if (dst_mcast && !src_local) return 1; @@ -277,7 +277,7 @@ ospf_rx_hook(sock *sk, int size) } else { - /* In OSPFv3, src_local and dst_local mean link-local. + /* In OSPFv3, src_local and dst_local mean link-local. * RFC 5340 says that local (non-vlink) packets use * link-local src address, but does not enforce it. Strange. */ @@ -385,7 +385,7 @@ ospf_rx_hook(sock *sk, int size) struct ospf_iface *iff = NULL; WALK_LIST(iff, p->iface_list) { - if ((iff->type == OSPF_IT_VLINK) && + if ((iff->type == OSPF_IT_VLINK) && (iff->voa == ifa->oa) && (iff->instance_id == instance_id) && (iff->vid == rid)) diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 9d146ce2..16453b87 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -1,16 +1,16 @@ /* * BIRD -- OSPF - * + * * (c) 2000--2004 Ondrej Filip * (c) 2009--2014 Ondrej Zajicek * (c) 2009--2014 CZ.NIC z.s.p.o. - * + * * Can be freely distributed and used under the terms of the GNU GPL. */ #include "ospf.h" -static void add_cand(list * l, struct top_hash_entry *en, +static void add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, u32 dist, struct ospf_area *oa, int i); static void rt_sync(struct ospf_proto *p); @@ -270,7 +270,7 @@ orta_compare(const struct ospf_proto *p, const orta *new, const orta *old) /* Prefer routes with higher Router ID, just to be more deterministic */ if (new->rid > old->rid) return 1; - + return -1; } @@ -503,7 +503,7 @@ add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_ if (en == oa->rt) { - /* + /* * Local stub networks does not have proper iface in en->nhi * (because they all have common top_hash_entry en). * We have to find iface responsible for that stub network. @@ -810,7 +810,7 @@ link_back(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry return 0; } - + /* RFC 2328 16.2. calculating inter-area routes */ static void ospf_rt_sum(struct ospf_area *oa) @@ -822,7 +822,7 @@ ospf_rt_sum(struct ospf_area *oa) ort *abr; int pxlen = -1, type = -1; u8 pxopts; - + OSPF_TRACE(D_EVENTS, "Starting routing table calculation for inter-area (area %R)", oa->areaid); @@ -864,7 +864,7 @@ ospf_rt_sum(struct ospf_area *oa) else /* LSA_T_SUM_RT */ { lsa_parse_sum_rt(en, ospf_is_v2(p), &dst_rid, &metric, &options); - + /* We don't want local router in ASBR routing table */ if (dst_rid == p->router_id) continue; @@ -966,9 +966,9 @@ ospf_rt_sum_tr(struct ospf_area *oa) re = fib_find(&bb->rtr, &ip, MAX_PREFIX_LENGTH); } - /* 16.3 (1b) */ - if (metric == LSINFINITY) - continue; + /* 16.3 (1b) */ + if (metric == LSINFINITY) + continue; /* 16.3 (3) */ if (!re || !re->n.type) @@ -989,7 +989,7 @@ ospf_rt_sum_tr(struct ospf_area *oa) metric = abr->n.metric1 + metric; /* IAC */ /* 16.3. (5) */ - if ((metric < re->n.metric1) || + if ((metric < re->n.metric1) || ((metric == re->n.metric1) && unresolved_vlink(re))) { /* We want to replace the next-hop even if the metric is equal @@ -1075,7 +1075,7 @@ decide_sum_lsa(struct ospf_area *oa, ort *nf, int dest) struct area_net *anet = (struct area_net *) fib_route(&nf->n.oa->net_fib, nf->fn.prefix, nf->fn.pxlen); - /* Condensed area network found */ + /* Condensed area network found */ if (anet) return 0; @@ -1132,7 +1132,7 @@ decide_nssa_lsa(struct ospf_proto *p, ort *nf, struct ospf_lsa_ext_local *rt) if (!rt_is_nssa(nf) || !oa->translate) return 0; - /* Condensed area network found */ + /* Condensed area network found */ if (fib_route(&oa->enet_fib, nf->fn.prefix, nf->fn.pxlen)) return 0; @@ -1141,7 +1141,7 @@ decide_nssa_lsa(struct ospf_proto *p, ort *nf, struct ospf_lsa_ext_local *rt) /* We do not store needed data in struct orta, we have to parse the LSA */ lsa_parse_ext(en, ospf_is_v2(p), rt); - + if (rt->pxopts & OPT_PX_NU) return 0; @@ -1200,18 +1200,18 @@ ospf_check_vlinks(struct ospf_proto *p) { struct ospf_iface *nhi = ospf_iface_find(p, tmp->nhs->iface); - if ((ifa->state != OSPF_IS_PTP) + if ((ifa->state != OSPF_IS_PTP) || (ifa->vifa != nhi) || !ipa_equal(ifa->vip, tmp->lb)) - { - OSPF_TRACE(D_EVENTS, "Vlink peer %R found", tmp->lsa.id); - ospf_iface_sm(ifa, ISM_DOWN); + { + OSPF_TRACE(D_EVENTS, "Vlink peer %R found", tmp->lsa.id); + ospf_iface_sm(ifa, ISM_DOWN); ifa->vifa = nhi; ifa->addr = nhi->addr; ifa->cost = tmp->dist; - ifa->vip = tmp->lb; - ospf_iface_sm(ifa, ISM_UP); - } + ifa->vip = tmp->lb; + ospf_iface_sm(ifa, ISM_UP); + } else if ((ifa->state == OSPF_IS_PTP) && (ifa->cost != tmp->dist)) { ifa->cost = tmp->dist; @@ -1222,11 +1222,11 @@ ospf_check_vlinks(struct ospf_proto *p) } else { - if (ifa->state > OSPF_IS_DOWN) - { - OSPF_TRACE(D_EVENTS, "Vlink peer %R lost", ifa->vid); + if (ifa->state > OSPF_IS_DOWN) + { + OSPF_TRACE(D_EVENTS, "Vlink peer %R lost", ifa->vid); ospf_iface_sm(ifa, ISM_DOWN); - } + } } } } @@ -1343,7 +1343,7 @@ static void translator_timer_hook(timer *timer) { struct ospf_area *oa = timer->data; - + if (oa->translate != TRANS_WAIT) return; @@ -1727,7 +1727,7 @@ ospf_rt_spf(struct ospf_proto *p) rt_sync(p); lp_flush(p->nhpool); - + p->calcrt = 0; } @@ -1756,7 +1756,7 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, if (inherit_nexthops(pn)) return pn; - /* + /* * There are three cases: * 1) en is a local network (and par is root) * 2) en is a ptp or ptmp neighbor (and par is root) @@ -1821,7 +1821,7 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, return NULL; struct ospf_lsa_link *llsa = lhe->lsa_body; - + if (ipa_zero(llsa->lladdr)) return NULL; diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index bc2de79f..4e3da0de 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -31,7 +31,7 @@ static inline void lsab_reset(struct ospf_proto *p); * ospf_install_lsa - install new LSA into database * @p: OSPF protocol instance * @lsa: LSA header - * @type: type of LSA + * @type: type of LSA * @domain: domain of LSA * @body: pointer to LSA body * @@ -93,7 +93,7 @@ ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u3 * @p: OSPF protocol instance * @en: current LSA entry or NULL * @lsa: new LSA header - * @type: type of LSA + * @type: type of LSA * @domain: domain of LSA * @body: pointer to LSA body * @@ -136,7 +136,7 @@ ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ls } else { - /* + /* * Received LSA has maximal sequence number, so we cannot simply override * it. We have to install it to the database, immediately flush it to * implement sequence number wrapping, and schedule our current LSA to be @@ -181,10 +181,10 @@ ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ls en = ospf_install_lsa(p, lsa, type, domain, body); } - /* + /* * We flood the updated LSA. Although in some cases the to-be-flooded LSA is * the same as the received LSA, and therefore we should propagate it as - * regular received LSA (send the acknowledgement instead of the update to + * regular received LSA (send the acknowledgement instead of the update to * the neighbor we received it from), we cheat a bit here. */ @@ -341,7 +341,7 @@ ospf_originate_next_lsa(struct ospf_proto *p, struct top_hash_entry *en) if (! ospf_do_originate_lsa(p, en, en->next_lsa_body, en->next_lsa_blen, en->next_lsa_opts)) return; - + en->next_lsa_body = NULL; en->next_lsa_blen = 0; en->next_lsa_opts = 0; @@ -776,7 +776,7 @@ prepare_rt2_lsa_body(struct ospf_proto *p, struct ospf_area *oa) (ifa->state == OSPF_IS_LOOP) || (ifa->type == OSPF_IT_PTMP)) add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(ifa->addr->ip), 0xffffffff, 0); - else + else add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(ifa->addr->prefix), u32_mkmask(ifa->addr->pxlen), ifa->cost); i++; @@ -789,7 +789,7 @@ prepare_rt2_lsa_body(struct ospf_proto *p, struct ospf_area *oa) add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(sn->px.addr), u32_mkmask(sn->px.len), sn->cost), i++; struct ospf_lsa_rt *rt = p->lsab; - /* Store number of links in lower half of options */ + /* Store number of links in lower half of options */ rt->options = get_rt_options(p, oa, bitv) | (u16) i; } @@ -1119,7 +1119,7 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf, * If I receive a message that new route is installed, I try to originate an * external LSA. If @oa is an NSSA area, NSSA-LSA is originated instead. * @oa should not be a stub area. @src does not specify whether the LSA - * is external or NSSA, but it specifies the source of origination - + * is external or NSSA, but it specifies the source of origination - * the export from ospf_rt_notify(), or the NSSA-EXT translation. */ void @@ -1494,7 +1494,7 @@ prefix_same(u32 *b1, u32 *b2) int pxl1 = *b1 >> 24; int pxl2 = *b2 >> 24; int pxs, i; - + if (pxl1 != pxl2) return 0; @@ -1583,7 +1583,7 @@ prepare_prefix_net_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa) WALK_LIST(n, ifa->neigh_list) if ((n->state == NEIGHBOR_FULL) && - (en = ospf_hash_find(p->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK))) + (en = ospf_hash_find(p->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK))) add_link_lsa(p, en->lsa_body, offset, &pxc); lp = p->lsab; @@ -1886,7 +1886,7 @@ ospf_hash_get(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type) ee = f->hash_table + ospf_top_hash(f, domain, lsa, rtr, type); e = *ee; - while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || + while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || e->lsa_type != type || e->domain != domain)) e = e->next; @@ -1914,7 +1914,7 @@ ospf_hash_get(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type) void ospf_hash_delete(struct top_graph *f, struct top_hash_entry *e) { - struct top_hash_entry **ee = f->hash_table + + struct top_hash_entry **ee = f->hash_table + ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa_type); while (*ee) @@ -1955,7 +1955,7 @@ ospf_dump_lsa(struct top_hash_entry *he, struct proto *p) rr = (struct ospf_lsa_rt_link *) (rt + 1); for (i = 0; i < lsa_rt_items(&he->lsa); i++) - OSPF_TRACE(D_EVENTS, " - %1x %-1R %-1R %5u", + OSPF_TRACE(D_EVENTS, " - %1x %-1R %-1R %5u", rr[i].type, rr[i].id, rr[i].data, rr[i].metric); break; @@ -1964,7 +1964,7 @@ ospf_dump_lsa(struct top_hash_entry *he, struct proto *p) rts = (u32 *) (ln + 1); for (i = 0; i < lsa_net_items(&he->lsa); i++) - OSPF_TRACE(D_EVENTS, " - %-1R", rts[i]); + OSPF_TRACE(D_EVENTS, " - %-1R", rts[i]); break; default: diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h index b34689e2..e2d6c773 100644 --- a/proto/ospf/topology.h +++ b/proto/ospf/topology.h @@ -18,7 +18,7 @@ struct top_hash_entry in intra-area routing table calculation */ struct top_hash_entry *next; /* Next in hash chain */ struct ospf_lsa_header lsa; - u16 lsa_type; /* lsa.type processed and converted to common values (LSA_T_*) */ + u16 lsa_type; /* lsa.type processed and converted to common values (LSA_T_*) */ u16 init_age; /* Initial value for lsa.age during inst_time */ u32 domain; /* Area ID for area-wide LSAs, Iface ID for link-wide LSAs */ // struct ospf_area *oa; From 178a197afb77770d8a90765e39065679936a45d1 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 21 Jul 2014 21:50:56 +0200 Subject: [PATCH 04/11] OSPF instance id option and documentation update. --- doc/bird.sgml | 43 +++++++++++++++++++++++++++++-------------- proto/ospf/config.Y | 36 ++++++++++++++++++------------------ proto/ospf/ospf.c | 7 +++++++ proto/ospf/ospf.h | 13 +++++++------ 4 files changed, 61 insertions(+), 38 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 04b6a845..46900227 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2288,6 +2288,7 @@ networks. protocol ospf <name> { rfc1583compat <switch>; + instance id <num>; stub router <switch>; tick <num>; ecmp <switch> [limit <num>]; @@ -2375,14 +2376,24 @@ protocol ospf <name> { RFC 1583 . Default value is no. + instance id + When multiple OSPF protocol instances are active on the same links, they + should use different instance IDs to distinguish their packets. Although + it could be done on per-interface basis, it is often preferred to set + one instance ID to whole OSPF domain/topology (e.g., when multiple + instances are used to represent separate logical topologies on the same + physical network). This option specifies the default instance ID for all + interfaces of the OSPF instance. Note that this option, if used, must + precede interface definitions. Default value is 0. + stub router switch This option configures the router to be a stub router, i.e., a router that participates in the OSPF topology but does not allow transit traffic. In OSPFv2, this is implemented by advertising maximum metric - for outgoing links, as suggested by - RFC 3137 . - In OSPFv3, the stub router behavior is announced by clearing the R-bit - in the router LSA. Default value is no. + for outgoing links. In OSPFv3, the stub router behavior is announced by + clearing the R-bit in the router LSA. See RFC 6987 + for + details. Default value is no. tick num The routing table calculation and clean-up of areas' databases is not @@ -2484,22 +2495,26 @@ protocol ospf <name> { prefix. When option interface pattern [instance Defines that the specified interfaces belong to the area being defined. See common option for detailed description. In OSPFv2, extended interface clauses are used, because - OSPFv2 handles each network prefix as a separate virtual interface. In - OSPFv3, you can specify instance ID for that interface description, so - it is possible to have several instances of that interface with - different options or even in different areas. + each network prefix is handled as a separate virtual interface. + + You can specify alternative instance ID for the interface definition, + therefore it is possible to have several instances of that interface + with different options or even in different areas. For OSPFv2, + instance ID support is an extension (RFC 6549 + ) and is + supposed to be set per-protocol. For OSPFv3, it is an integral feature. virtual link id [instance Virtual link to router with the router id. Virtual link acts as a point-to-point interface belonging to backbone. The actual area is used - as transport area. This item cannot be in the backbone. In OSPFv3, you - could also use several virtual links to one destination with different - instance IDs. + as a transport area. This item cannot be in the backbone. Like with + cost num Specifies output cost (metric) of an interface. Default value is 10. @@ -2530,8 +2545,8 @@ protocol ospf <name> { wait num After start, router waits for the specified number of seconds between - starting election and building adjacency. Default value is 40. - + starting election and building adjacency. Default value is 4*dead count num When the router does not receive any messages from a neighbor in ospf2; } static inline int ospf_cfg_is_v3(void) { return ! OSPF_CFG->ospf2; } @@ -32,6 +32,9 @@ ospf_iface_finish(void) if (ip->deadint == 0) ip->deadint = ip->deadc * ip->helloint; + if (ip->waitint == 0) + ip->waitint = ip->deadc * ip->helloint; + ip->passwords = get_passwords(); if ((ip->autype == OSPF_AUTH_CRYPT) && (ip->helloint < 5)) @@ -159,6 +162,7 @@ ospf_proto_item: | ECMP bool LIMIT expr { OSPF_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); } | MERGE EXTERNAL bool { OSPF_CFG->merge_external = $3; } | TICK expr { OSPF_CFG->tick = $2; if($2<=0) cf_error("Tick must be greater than zero"); } + | INSTANCE ID expr { OSPF_CFG->instance_id = $3; if (($3<0) || ($3>255)) cf_error("Instance ID must be in range 0-255"); } | ospf_area ; @@ -213,7 +217,7 @@ ospf_stubnet_start: add_tail(&this_area->stubnet_list, NODE this_stubnet); this_stubnet->px = $1; this_stubnet->cost = COST_D; - } + } ; ospf_stubnet_opts: @@ -239,9 +243,9 @@ ospf_vlink_opts: ospf_vlink_item: | HELLO expr { OSPF_PATT->helloint = $2 ; if (($2<=0) || ($2>65535)) cf_error("Hello interval must be in range 1-65535"); } - | RETRANSMIT expr { OSPF_PATT->rxmtint = $2 ; if ($2<=0) cf_error("Retransmit int must be greater than zero"); } + | RETRANSMIT expr { OSPF_PATT->rxmtint = $2 ; if ($2<=1) cf_error("Retransmit int must be greater than one"); } | 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 ; } + | WAIT expr { OSPF_PATT->waitint = $2 ; if ($2<=1) cf_error("Wait 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; } @@ -261,12 +265,10 @@ ospf_vlink_start: VIRTUAL LINK idval 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->deadint = 0; OSPF_PATT->type = OSPF_IT_VLINK; + OSPF_PATT->instance_id = OSPF_CFG->instance_id; init_list(&OSPF_PATT->nbma_list); - OSPF_PATT->autype = OSPF_AUTH_NONE; reset_passwords(); } ; @@ -275,8 +277,8 @@ ospf_iface_item: COST expr { OSPF_PATT->cost = $2 ; if (($2<=0) || ($2>65535)) cf_error("Cost must be in range 1-65535"); } | 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"); } - | WAIT expr { OSPF_PATT->waitint = $2 ; } + | RETRANSMIT expr { OSPF_PATT->rxmtint = $2 ; if ($2<=1) cf_error("Retransmit int must be greater than one"); } + | WAIT expr { OSPF_PATT->waitint = $2 ; if ($2<=1) cf_error("Wait 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 ; } @@ -300,8 +302,8 @@ ospf_iface_item: | AUTHENTICATION NONE { OSPF_PATT->autype = OSPF_AUTH_NONE; } | AUTHENTICATION SIMPLE { OSPF_PATT->autype = OSPF_AUTH_SIMPLE; ospf_check_auth(); } | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT; ospf_check_auth(); } - | RX BUFFER NORMAL { OSPF_PATT->rx_buffer = 0; } - | RX BUFFER LARGE { OSPF_PATT->rx_buffer = OSPF_MAX_PKT_SIZE; } + | RX BUFFER NORMAL { OSPF_PATT->rx_buffer = 0; } + | RX BUFFER LARGE { OSPF_PATT->rx_buffer = OSPF_MAX_PKT_SIZE; } | RX BUFFER expr { OSPF_PATT->rx_buffer = $3; if (($3 < OSPF_MIN_PKT_SIZE) || ($3 > OSPF_MAX_PKT_SIZE)) cf_error("Buffer size must be in range 256-65535"); } | TX tos { OSPF_PATT->tx_tos = $2; } | TX PRIORITY expr { OSPF_PATT->tx_priority = $3; } @@ -344,7 +346,7 @@ nbma_eligible: /* empty */ { $$ = 0; } | ELIGIBLE { $$ = 1; } ; - + nbma_item: ipa nbma_eligible ';' { this_nbma = cfg_allocz(sizeof(struct nbma_node)); @@ -353,7 +355,7 @@ nbma_item: ipa nbma_eligible ';' this_nbma->eligible=$2; } ; - + ospf_iface_start: { this_ipatt = cfg_allocz(sizeof(struct ospf_iface_patt)); @@ -365,12 +367,10 @@ ospf_iface_start: OSPF_PATT->rxmtint = RXMTINT_D; OSPF_PATT->inftransdelay = INFTRANSDELAY_D; OSPF_PATT->priority = PRIORITY_D; - OSPF_PATT->waitint = WAIT_DMH*HELLOINT_D; OSPF_PATT->deadc = DEADC_D; - OSPF_PATT->deadint = 0; OSPF_PATT->type = OSPF_IT_UNDEF; + OSPF_PATT->instance_id = OSPF_CFG->instance_id; init_list(&OSPF_PATT->nbma_list); - OSPF_PATT->autype = OSPF_AUTH_NONE; OSPF_PATT->ptp_netmask = 2; /* not specified */ OSPF_PATT->tx_tos = IP_PREC_INTERNET_CONTROL; OSPF_PATT->tx_priority = sk_priority_control; @@ -380,12 +380,12 @@ ospf_iface_start: ospf_instance_id: /* empty */ - | INSTANCE expr { OSPF_PATT->instance_id = $2; } + | INSTANCE expr { OSPF_PATT->instance_id = $2; if (($2<0) || ($2>255)) cf_error("Instance ID must be in range 0-255"); } ; ospf_iface_patt_list: iface_patt_list { if (ospf_cfg_is_v3()) iface_patt_check(); } ospf_instance_id - ; + ; ospf_iface_opts: /* empty */ diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index d65dba3b..470a8633 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -87,6 +87,13 @@ * we have recalculated and also those we have deleted to the core's routing * table and the core will take care of the rest. This simplifies the process * and conserves memory. + * + * Supported standards: + * - RFC 2328 - main OSPFv2 standard + * - RFC 5340 - main OSPFv3 standard + * - RFC 3101 - OSPFv2 NSSA areas + * - RFC 6549 - OSPFv2 multi-instance extensions + * - RFC 6987 - OSPF stub router advertisement */ #include diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 7e5996f6..f464a3ed 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -103,12 +103,13 @@ struct ospf_config { struct proto_config c; uint tick; - byte ospf2; - byte rfc1583; - byte stub_router; - byte merge_external; - byte abr; - byte asbr; + u8 ospf2; + u8 rfc1583; + u8 stub_router; + u8 merge_external; + u8 instance_id; + u8 abr; + u8 asbr; int ecmp; list area_list; /* list of area configs (struct ospf_area_config) */ list vlink_list; /* list of configured vlinks (struct ospf_iface_patt) */ From f8fefde318c6248ad94e7b6d60155deed9ab8eed Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 24 Oct 2014 10:27:21 +0200 Subject: [PATCH 05/11] Refactoring of OSPF messages. --- nest/password.c | 15 +- nest/password.h | 9 +- proto/ospf/dbdes.c | 234 +++++++++------------ proto/ospf/hello.c | 78 ++++--- proto/ospf/iface.c | 71 +++---- proto/ospf/lsack.c | 5 +- proto/ospf/lsreq.c | 12 +- proto/ospf/lsupd.c | 98 ++++----- proto/ospf/neighbor.c | 477 +++++++++++++++++++++--------------------- proto/ospf/ospf.c | 5 +- proto/ospf/ospf.h | 40 +++- proto/ospf/packet.c | 215 ++++++++----------- proto/ospf/rt.c | 8 +- proto/ospf/topology.c | 4 +- proto/rip/auth.c | 6 +- 15 files changed, 606 insertions(+), 671 deletions(-) diff --git a/nest/password.c b/nest/password.c index 179939e2..21e42e0e 100644 --- a/nest/password.c +++ b/nest/password.c @@ -36,9 +36,18 @@ password_find(list *l, int first_fit) return pf; } -void password_cpy(char *dst, char *src, int size) +struct password_item * +password_find_by_id(list *l, int id) { - bzero(dst, size); - memcpy(dst, src, (strlen(src) < (unsigned) size ? strlen(src) : (unsigned) size)); + struct password_item *pi; + + if (!l) + return NULL; + + WALK_LIST(pi, *l) + if ((pi->id == id) && (pi->accfrom <= now_real) && (now_real < pi->accto)) + return pi; + + return NULL; } diff --git a/nest/password.h b/nest/password.h index 726af733..cd120d70 100644 --- a/nest/password.h +++ b/nest/password.h @@ -23,6 +23,13 @@ struct password_item { extern struct password_item *last_password_item; struct password_item *password_find(list *l, int first_fit); -void password_cpy(char *dst, char *src, int size); +struct password_item *password_find_by_id(list *l, int id); + +static inline int password_verify(struct password_item *p1, char *p2, uint size) +{ + char buf[size]; + strncpy(buf, p1->password, size); + return !memcmp(buf, p2, size); +} #endif diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c index 2b076157..88c87164 100644 --- a/proto/ospf/dbdes.c +++ b/proto/ospf/dbdes.c @@ -98,7 +98,7 @@ ospf_dump_dbdes(struct ospf_proto *p, struct ospf_packet *pkt) static void -ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n, int body) +ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n) { struct ospf_iface *ifa = n->ifa; struct ospf_packet *pkt; @@ -106,6 +106,7 @@ ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n, int body) u16 iface_mtu = (ifa->type == OSPF_IT_VLINK) ? 0 : ifa->iface->mtu; + /* Update DBDES buffer */ if (n->ldd_bsize != ifa->tx_length) { mb_free(n->ldd_buffer); @@ -136,7 +137,8 @@ ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n, int body) length = sizeof(struct ospf_dbdes3_packet); } - if (body && (n->myimms & DBDES_M)) + /* Prepare DBDES body */ + if (!(n->myimms & DBDES_I) && (n->myimms & DBDES_M)) { struct ospf_lsa_header *lsas; struct top_hash_entry *en; @@ -182,7 +184,7 @@ ospf_do_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n) struct ospf_iface *ifa = n->ifa; OSPF_PACKET(ospf_dump_dbdes, n->ldd_buffer, - "DBDES packet sent to %I via %s", n->ip, ifa->ifname); + "DBDES packet sent to nbr %R on %s", n->rid, ifa->ifname); sk_set_tbuf(ifa->sk, n->ldd_buffer); ospf_send_to(ifa, n->ip); sk_set_tbuf(ifa->sk, NULL); @@ -191,7 +193,6 @@ ospf_do_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n) /** * ospf_send_dbdes - transmit database description packet * @n: neighbor - * @next: whether to send a next packet in a sequence (1) or to retransmit the old one (0) * * Sending of a database description packet is described in 10.8 of RFC 2328. * Reception of each packet is acknowledged in the sequence number of another. @@ -200,104 +201,78 @@ ospf_do_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n) * of the buffer. */ void -ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n, int next) +ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n) { /* RFC 2328 10.8 */ + ASSERT((n->state == NEIGHBOR_EXSTART) || (n->state == NEIGHBOR_EXCHANGE)); + if (n->ifa->oa->rt == NULL) return; - switch (n->state) - { - case NEIGHBOR_EXSTART: - n->myimms |= DBDES_I; + ospf_prepare_dbdes(p, n); + ospf_do_send_dbdes(p, n); - /* Send empty packets */ - ospf_prepare_dbdes(p, n, 0); - ospf_do_send_dbdes(p, n); - break; + if (n->state == NEIGHBOR_EXSTART) + return; - case NEIGHBOR_EXCHANGE: - n->myimms &= ~DBDES_I; + /* Master should restart RXMT timer for each DBDES exchange */ + if (n->myimms & DBDES_MS) + tm_start(n->rxmt_timer, n->ifa->rxmtint); - if (next) - ospf_prepare_dbdes(p, n, 1); - - /* Send prepared packet */ - ospf_do_send_dbdes(p, n); - - /* Master should restart RXMT timer for each DBDES exchange */ - if (n->myimms & DBDES_MS) - tm_start(n->rxmt_timer, n->ifa->rxmtint); - - if (!(n->myimms & DBDES_MS)) - if (!(n->myimms & DBDES_M) && - !(n->imms & DBDES_M)) - ospf_neigh_sm(n, INM_EXDONE); - break; - - case NEIGHBOR_LOADING: - case NEIGHBOR_FULL: - - if (!n->ldd_buffer) - { - OSPF_TRACE(D_PACKETS, "No DBDES packet for repeating"); - ospf_neigh_sm(n, INM_KILLNBR); - return; - } - - /* Send last packet */ - ospf_do_send_dbdes(p, n); - break; - } + if (!(n->myimms & DBDES_MS)) + if (!(n->myimms & DBDES_M) && !(n->imms & DBDES_M)) + ospf_neigh_sm(n, INM_EXDONE); } +void +ospf_rxmt_dbdes(struct ospf_proto *p, struct ospf_neighbor *n) +{ + ASSERT(n->state > NEIGHBOR_EXSTART); + + if (!n->ldd_buffer) + { + log(L_WARN "%s: No DBDES packet for retransmit", p->p.name); + ospf_neigh_sm(n, INM_SEQMIS); + return; + } + + /* Send last packet */ + ospf_do_send_dbdes(p, n); +} static int ospf_process_dbdes(struct ospf_proto *p, struct ospf_packet *pkt, struct ospf_neighbor *n) { struct ospf_iface *ifa = n->ifa; - struct ospf_lsa_header *lsas; + struct ospf_lsa_header *lsas, lsa; + struct top_hash_entry *en, *req; + const char *err_dsc = NULL; + u32 lsa_type, lsa_domain; uint i, lsa_count; ospf_dbdes_body(p, pkt, &lsas, &lsa_count); for (i = 0; i < lsa_count; i++) { - struct top_hash_entry *en, *req; - struct ospf_lsa_header lsa; - u32 lsa_type, lsa_domain; - lsa_ntoh_hdr(lsas + i, &lsa); lsa_get_type_domain(&lsa, ifa, &lsa_type, &lsa_domain); /* RFC 2328 10.6 and RFC 5340 4.2.2 */ if (!lsa_type) - { - log(L_WARN "%s: Bad DBDES from %I - LSA of unknown type", p->p.name, n->ip); - goto err; - } + DROP1("LSA of unknown type"); if (!oa_is_ext(ifa->oa) && (LSA_SCOPE(lsa_type) == LSA_SCOPE_AS)) - { - log(L_WARN "%s: Bad DBDES from %I - LSA with AS scope in stub area", p->p.name, n->ip); - goto err; - } + DROP1("LSA with AS scope in stub area"); /* Errata 3746 to RFC 2328 - rt-summary-LSAs forbidden in stub areas */ if (!oa_is_ext(ifa->oa) && (lsa_type == LSA_T_SUM_RT)) - { - log(L_WARN "%s: Bad DBDES from %I - rt-summary-LSA in stub area", p->p.name, n->ip); - goto err; - } + DROP1("rt-summary-LSA in stub area"); /* Not explicitly mentioned in RFC 5340 4.2.2 but makes sense */ if (LSA_SCOPE(lsa_type) == LSA_SCOPE_RES) - { - log(L_WARN "%s: Bad DBDES from %I - LSA with invalid scope", p->p.name, n->ip); - goto err; - } + DROP1("LSA with invalid scope"); en = ospf_hash_find(p->gr, lsa_domain, lsa.id, lsa.rt, lsa_type); if (!en || (lsa_comp(&lsa, &(en->lsa)) == CMP_NEWER)) @@ -314,7 +289,10 @@ ospf_process_dbdes(struct ospf_proto *p, struct ospf_packet *pkt, struct ospf_ne return 0; - err: +drop: + LOG_LSA1("Bad LSA (Type: %04x, Id: %R, Rt: %R) in DBDES", lsa_type, lsa.id, lsa.rt); + LOG_LSA2(" received from nbr %R on %s - %s", n->rid, ifa->ifname, err_dsc); + ospf_neigh_sm(n, INM_SEQMIS); return -1; } @@ -324,21 +302,19 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n) { struct ospf_proto *p = ifa->oa->po; + const char *err_dsc = NULL; u32 rcv_ddseq, rcv_options; u16 rcv_iface_mtu; u8 rcv_imms; - uint plen; + uint plen, err_val = 0, err_seqmis = 0; /* RFC 2328 10.6 */ plen = ntohs(pkt->length); if (plen < ospf_dbdes_hdrlen(p)) - { - log(L_ERR "OSPF: Bad DBDES packet from %I - too short (%u B)", n->ip, plen); - return; - } + DROP("too short", plen); - OSPF_PACKET(ospf_dump_dbdes, pkt, "DBDES packet received from %I via %s", n->ip, ifa->ifname); + OSPF_PACKET(ospf_dump_dbdes, pkt, "DBDES packet received from nbr %R on %s", n->rid, ifa->ifname); ospf_neigh_sm(n, INM_HELLOREC); @@ -364,6 +340,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, case NEIGHBOR_DOWN: case NEIGHBOR_ATTEMPT: case NEIGHBOR_2WAY: + OSPF_TRACE(D_PACKETS, "DBDES packet ignored - lesser state than ExStart"); return; case NEIGHBOR_INIT: @@ -376,8 +353,8 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, (rcv_iface_mtu != ifa->iface->mtu) && (rcv_iface_mtu != 0) && (ifa->iface->mtu != 0)) - log(L_WARN "OSPF: MTU mismatch with neighbor %I on interface %s (remote %d, local %d)", - n->ip, ifa->ifname, rcv_iface_mtu, ifa->iface->mtu); + LOG_PKT_WARN("MTU mismatch with nbr %R on %s (remote %d, local %d)", + n->rid, ifa->ifname, rcv_iface_mtu, ifa->iface->mtu); if ((rcv_imms == DBDES_IMMS) && (n->rid > p->router_id) && @@ -389,9 +366,8 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, n->options = rcv_options; n->myimms &= ~DBDES_MS; n->imms = rcv_imms; - OSPF_TRACE(D_PACKETS, "I'm slave to %I", n->ip); ospf_neigh_sm(n, INM_NEGDONE); - ospf_send_dbdes(p, n, 1); + ospf_send_dbdes(p, n); break; } @@ -404,7 +380,6 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, n->options = rcv_options; n->ddr = rcv_ddseq - 1; /* It will be set corectly a few lines down */ n->imms = rcv_imms; - OSPF_TRACE(D_PACKETS, "I'm master to %I", n->ip); ospf_neigh_sm(n, INM_NEGDONE); } else @@ -417,49 +392,29 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, if ((rcv_imms == n->imms) && (rcv_options == n->options) && (rcv_ddseq == n->ddr)) - { - /* Duplicate packet */ - OSPF_TRACE(D_PACKETS, "Received duplicate dbdes from %I", n->ip); - if (!(n->myimms & DBDES_MS)) - { - /* Slave should retransmit dbdes packet */ - ospf_send_dbdes(p, n, 0); - } - return; - } + goto duplicate; - if ((rcv_imms & DBDES_MS) != (n->imms & DBDES_MS)) /* M/S bit differs */ - { - OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (bit MS)", n->ip); - ospf_neigh_sm(n, INM_SEQMIS); - break; - } + /* Do INM_SEQMIS during packet error */ + err_seqmis = 1; - if (rcv_imms & DBDES_I) /* I bit is set */ - { - OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (bit I)", n->ip); - ospf_neigh_sm(n, INM_SEQMIS); - break; - } + if ((rcv_imms & DBDES_MS) != (n->imms & DBDES_MS)) + DROP("MS-bit mismatch", rcv_imms); - if (rcv_options != n->options) /* Options differs */ - { - OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (options)", n->ip); - ospf_neigh_sm(n, INM_SEQMIS); - break; - } + if (rcv_imms & DBDES_I) + DROP("I-bit mismatch", rcv_imms); + + if (rcv_options != n->options) + DROP("options mismatch", rcv_options); n->ddr = rcv_ddseq; n->imms = rcv_imms; if (n->myimms & DBDES_MS) { - if (rcv_ddseq != n->dds) /* MASTER */ - { - OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (master)", n->ip); - ospf_neigh_sm(n, INM_SEQMIS); - break; - } + /* MASTER */ + + if (rcv_ddseq != n->dds) + DROP("DD sequence number mismatch", rcv_ddseq); n->dds++; @@ -469,16 +424,14 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, if (!(n->myimms & DBDES_M) && !(n->imms & DBDES_M)) ospf_neigh_sm(n, INM_EXDONE); else - ospf_send_dbdes(p, n, 1); + ospf_send_dbdes(p, n); } else { - if (rcv_ddseq != (n->dds + 1)) /* SLAVE */ - { - OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (slave)", n->ip); - ospf_neigh_sm(n, INM_SEQMIS); - break; - } + /* SLAVE */ + + if (rcv_ddseq != (n->dds + 1)) + DROP("DD sequence number mismatch", rcv_ddseq); n->ddr = rcv_ddseq; n->dds = rcv_ddseq; @@ -486,7 +439,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, if (ospf_process_dbdes(p, pkt, n) < 0) return; - ospf_send_dbdes(p, n, 1); + ospf_send_dbdes(p, n); } break; @@ -495,25 +448,30 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, if ((rcv_imms == n->imms) && (rcv_options == n->options) && (rcv_ddseq == n->ddr)) - { - /* Duplicate packet */ - OSPF_TRACE(D_PACKETS, "Received duplicate dbdes from %I", n->ip); - if (!(n->myimms & DBDES_MS)) - { - /* Slave should retransmit dbdes packet */ - ospf_send_dbdes(p, n, 0); - } - return; - } - else - { - OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (full)", n->ip); - DBG("PS=%u, DDR=%u, DDS=%u\n", rcv_ddseq, n->ddr, n->dds); - ospf_neigh_sm(n, INM_SEQMIS); - } - break; + goto duplicate; + + err_seqmis = 1; + + DROP("too late for DD exchange", n->state); default: - bug("Received dbdes from %I in undefined state.", n->ip); + bug("Undefined interface state"); } + return; + +duplicate: + OSPF_TRACE(D_PACKETS, "DBDES packet is duplicate"); + + /* Slave should retransmit DBDES packet */ + if (!(n->myimms & DBDES_MS)) + ospf_rxmt_dbdes(p, n); + return; + +drop: + LOG_PKT("Bad DBDES packet from nbr %R on %s - %s (%u)", + n->rid, ifa->ifname, err_dsc, err_val); + + if (err_seqmis) + ospf_neigh_sm(n, INM_SEQMIS); + return; } diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index 3aeb9f9a..e8b1c702 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -114,7 +114,7 @@ ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) { if (i == max) { - log(L_WARN "%s: Too many neighbors on interface %s", p->p.name, ifa->ifname); + log(L_WARN "%s: Too many neighbors on %s", p->p.name, ifa->ifname); break; } neighbors[i] = htonl(neigh->rid); @@ -188,16 +188,21 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n, ip_addr faddr) { struct ospf_proto *p = ifa->oa->po; - char *beg = "OSPF: Bad HELLO packet from "; + const char *err_dsc = NULL; u32 rcv_iface_id, rcv_helloint, rcv_deadint, rcv_dr, rcv_bdr; u8 rcv_options, rcv_priority; u32 *neighbors; u32 neigh_count; - uint plen, i; + uint plen, i, err_val = 0; /* RFC 2328 10.5 */ - OSPF_TRACE(D_PACKETS, "HELLO packet received from %I via %s", faddr, ifa->ifname); + /* + * We may not yet havethe associate neighbor, so we use Router ID from the + * packet instead of one from the neighbor structure for log messages. + */ + u32 rcv_rid = ntohl(pkt->routerid); + OSPF_TRACE(D_PACKETS, "HELLO packet received from nbr %R on %s", rcv_rid, ifa->ifname); plen = ntohs(pkt->length); @@ -206,10 +211,7 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_hello2_packet *ps = (void *) pkt; if (plen < sizeof(struct ospf_hello2_packet)) - { - log(L_ERR "%s%I - too short (%u B)", beg, faddr, plen); - return; - } + DROP("too short", plen); rcv_iface_id = 0; rcv_helloint = ntohs(ps->helloint); @@ -223,10 +225,7 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, if ((ifa->type != OSPF_IT_VLINK) && (ifa->type != OSPF_IT_PTP) && (pxlen != ifa->addr->pxlen)) - { - log(L_ERR "%s%I - prefix length mismatch (%d)", beg, faddr, pxlen); - return; - } + DROP("prefix length mismatch", pxlen); neighbors = ps->neighbors; neigh_count = (plen - sizeof(struct ospf_hello2_packet)) / sizeof(u32); @@ -236,10 +235,7 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_hello3_packet *ps = (void *) pkt; if (plen < sizeof(struct ospf_hello3_packet)) - { - log(L_ERR "%s%I - too short (%u B)", beg, faddr, plen); - return; - } + DROP("too short", plen); rcv_iface_id = ntohl(ps->iface_id); rcv_helloint = ntohs(ps->helloint); @@ -254,23 +250,14 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, } if (rcv_helloint != ifa->helloint) - { - log(L_ERR "%s%I - hello interval mismatch (%d)", beg, faddr, rcv_helloint); - return; - } + DROP("hello interval mismatch", rcv_helloint); if (rcv_deadint != ifa->deadint) - { - log(L_ERR "%s%I - dead interval mismatch (%d)", beg, faddr, rcv_deadint); - return; - } + DROP("dead interval mismatch", rcv_deadint); /* Check whether bits E, N match */ if ((rcv_options ^ ifa->oa->options) & (OPT_E | OPT_N)) - { - log(L_ERR "%s%I - area type mismatch (%x)", beg, faddr, rcv_options); - return; - } + DROP("area type mismatch", rcv_options); /* Check consistency of existing neighbor entry */ if (n) @@ -279,11 +266,11 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP))) { /* Neighbor identified by IP address; Router ID may change */ - if (n->rid != ntohl(pkt->routerid)) + if (n->rid != rcv_rid) { - OSPF_TRACE(D_EVENTS, "Neighbor %I has changed Router ID from %R to %R", - n->ip, n->rid, ntohl(pkt->routerid)); - ospf_neigh_remove(n); + OSPF_TRACE(D_EVENTS, "Neighbor %R on %s changed Router ID to %R", + n->rid, ifa->ifname, rcv_rid); + ospf_neigh_sm(n, INM_KILLNBR); n = NULL; } } @@ -292,7 +279,8 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, /* Neighbor identified by Router ID; IP address may change */ if (!ipa_equal(faddr, n->ip)) { - OSPF_TRACE(D_EVENTS, "Neighbor address changed from %I to %I", n->ip, faddr); + OSPF_TRACE(D_EVENTS, "Neighbor %R on %s changed IP address to %I", + n->rid, ifa->ifname, n->ip, faddr); n->ip = faddr; } } @@ -305,28 +293,26 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, struct nbma_node *nn = find_nbma_node(ifa, faddr); if (!nn && ifa->strictnbma) - { - log(L_WARN "Ignoring new neighbor: %I on %s", faddr, ifa->ifname); - return; - } + DROP1("new neighbor denied"); if (nn && (ifa->type == OSPF_IT_NBMA) && (((rcv_priority == 0) && nn->eligible) || ((rcv_priority > 0) && !nn->eligible))) - { - log(L_ERR "Eligibility mismatch for neighbor: %I on %s", faddr, ifa->ifname); - return; - } + DROP("eligibility mismatch", rcv_priority); if (nn) nn->found = 1; } + // XXXX format + // "ospf1: New neighbor found: 192.168.1.1/fe80:1234:1234:1234:1234 on eth0"; + // "ospf1: New neighbor found: 192.168.1.1 on eth0 at fe80:1234:1234:1234:1234"; + // "ospf1: Neighbor 192.168.1.1 on eth0 found, IP adress fe80:1234:1234:1234:1234"; OSPF_TRACE(D_EVENTS, "New neighbor found: %I on %s", faddr, ifa->ifname); n = ospf_neighbor_new(ifa); - n->rid = ntohl(pkt->routerid); + n->rid = rcv_rid; n->ip = faddr; n->dr = rcv_dr; n->bdr = rcv_bdr; @@ -367,7 +353,7 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, ospf_neigh_sm(n, INM_1WAYREC); return; - found_self: +found_self: ospf_neigh_sm(n, INM_2WAYREC); @@ -400,4 +386,10 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, ((n->bdr != n_id) && (old_bdr == n_id))) ospf_iface_sm(ifa, ISM_NEICH); } + + return; + +drop: + LOG_PKT("Bad HELLO packet from nbr %R on %s - %s (%u)", + rcv_rid, ifa->ifname, err_dsc, err_val); } diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 892e8a77..e14f09cb 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -11,15 +11,16 @@ #include "ospf.h" -char *ospf_is[] = { "down", "loop", "waiting", "ptp", "drother", - "backup", "dr" +const char *ospf_is_names[] = { + "Down", "Loopback", "Waiting", "PtP", "DROther", "Backup", "DR" }; -char *ospf_ism[] = { "interface up", "wait timer fired", "backup seen", - "neighbor change", "loop indicated", "unloop indicated", "interface down" +const char *ospf_ism_names[] = { + "InterfaceUp", "WaitTimer", "BackupSeen", "NeighborChange", + "LoopInd", "UnloopInd", "InterfaceDown" }; -char *ospf_it[] = { "broadcast", "nbma", "ptp", "ptmp", "virtual link" }; +const char *ospf_it[] = { "broadcast", "nbma", "ptp", "ptmp", "virtual link" }; static void @@ -40,7 +41,7 @@ wait_timer_hook(timer * timer) struct ospf_iface *ifa = (struct ospf_iface *) timer->data; struct ospf_proto *p = ifa->oa->po; - OSPF_TRACE(D_EVENTS, "Wait timer fired on interface %s", ifa->ifname); + OSPF_TRACE(D_EVENTS, "Wait timer fired on %s", ifa->ifname); ospf_iface_sm(ifa, ISM_WAITF); } @@ -240,10 +241,7 @@ ospf_iface_down(struct ospf_iface *ifa) } WALK_LIST_DELSAFE(n, nx, ifa->neigh_list) - { - OSPF_TRACE(D_EVENTS, "Removing neighbor %I", n->ip); - ospf_neigh_remove(n); - } + ospf_neigh_sm(n, INM_KILLNBR); if (ifa->hello_timer) tm_stop(ifa->hello_timer); @@ -311,8 +309,8 @@ ospf_iface_chstate(struct ospf_iface *ifa, u8 state) if (state == oldstate) return; - OSPF_TRACE(D_EVENTS, "Changing state of iface %s from %s to %s", - ifa->ifname, ospf_is[oldstate], ospf_is[state]); + OSPF_TRACE(D_EVENTS, "Interface %s changed state from %s to %s", + ifa->ifname, ospf_is_names[oldstate], ospf_is_names[state]); ifa->state = state; @@ -351,7 +349,7 @@ ospf_iface_chstate(struct ospf_iface *ifa, u8 state) void ospf_iface_sm(struct ospf_iface *ifa, int event) { - DBG("SM on %s. Event is '%s'\n", ifa->ifname, ospf_ism[event]); + DBG("SM on %s. Event is '%s'\n", ifa->ifname, ospf_ism_names[event]); switch (event) { @@ -613,7 +611,8 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i continue; if (ospf_is_v3(p) && !ipa_is_link_local(nb->ip)) - log(L_WARN "In OSPFv3, configured neighbor address (%I) should be link-local", nb->ip); + log(L_WARN "%s: Configured neighbor address (%I) should be link-local", + p->p.name, nb->ip); add_nbma_node(ifa, nb, 0); } @@ -726,7 +725,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* HELLO TIMER */ if (ifa->helloint != new->helloint) { - OSPF_TRACE(D_EVENTS, "Changing hello interval on interface %s from %d to %d", + OSPF_TRACE(D_EVENTS, "Changing hello interval of %s from %d to %d", ifname, ifa->helloint, new->helloint); ifa->helloint = new->helloint; @@ -736,7 +735,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* RXMT TIMER */ if (ifa->rxmtint != new->rxmtint) { - OSPF_TRACE(D_EVENTS, "Changing retransmit interval on interface %s from %d to %d", + OSPF_TRACE(D_EVENTS, "Changing retransmit interval of %s from %d to %d", ifname, ifa->rxmtint, new->rxmtint); ifa->rxmtint = new->rxmtint; @@ -745,7 +744,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* POLL TIMER */ if (ifa->pollint != new->pollint) { - OSPF_TRACE(D_EVENTS, "Changing poll interval on interface %s from %d to %d", + OSPF_TRACE(D_EVENTS, "Changing poll interval of %s from %d to %d", ifname, ifa->pollint, new->pollint); ifa->pollint = new->pollint; @@ -755,7 +754,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* WAIT TIMER */ if (ifa->waitint != new->waitint) { - OSPF_TRACE(D_EVENTS, "Changing wait interval on interface %s from %d to %d", + OSPF_TRACE(D_EVENTS, "Changing wait interval of %s from %d to %d", ifname, ifa->waitint, new->waitint); ifa->waitint = new->waitint; @@ -766,7 +765,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* DEAD TIMER */ if (ifa->deadint != new->deadint) { - OSPF_TRACE(D_EVENTS, "Changing dead interval on interface %s from %d to %d", + OSPF_TRACE(D_EVENTS, "Changing dead interval of %s from %d to %d", ifname, ifa->deadint, new->deadint); ifa->deadint = new->deadint; } @@ -774,7 +773,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* INFTRANS */ if (ifa->inftransdelay != new->inftransdelay) { - OSPF_TRACE(D_EVENTS, "Changing transmit delay on interface %s from %d to %d", + OSPF_TRACE(D_EVENTS, "Changing transmit delay of %s from %d to %d", ifname, ifa->inftransdelay, new->inftransdelay); ifa->inftransdelay = new->inftransdelay; } @@ -782,7 +781,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* AUTHENTICATION */ if (ifa->autype != new->autype) { - OSPF_TRACE(D_EVENTS, "Changing authentication type on interface %s", ifname); + OSPF_TRACE(D_EVENTS, "Changing authentication type of %s", ifname); ifa->autype = new->autype; } @@ -797,7 +796,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* COST */ if (ifa->cost != new->cost) { - OSPF_TRACE(D_EVENTS, "Changing cost on interface %s from %d to %d", + OSPF_TRACE(D_EVENTS, "Changing cost of %s from %d to %d", ifname, ifa->cost, new->cost); ifa->cost = new->cost; @@ -806,7 +805,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* PRIORITY */ if (ifa->priority != new->priority) { - OSPF_TRACE(D_EVENTS, "Changing priority on interface %s from %d to %d", + OSPF_TRACE(D_EVENTS, "Changing priority of %s from %d to %d", ifname, ifa->priority, new->priority); ifa->priority = new->priority; @@ -816,7 +815,8 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* STRICT NBMA */ if (ifa->strictnbma != new->strictnbma) { - OSPF_TRACE(D_EVENTS, "Changing NBMA strictness on interface %s", ifname); + OSPF_TRACE(D_EVENTS, "Changing NBMA strictness of %s from %d to %d", + ifname, ifa->strictnbma, new->strictnbma); ifa->strictnbma = new->strictnbma; } @@ -830,14 +830,14 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) { if (nb->eligible != nb2->eligible) { - OSPF_TRACE(D_EVENTS, "Changing eligibility of neighbor %I on interface %s", + OSPF_TRACE(D_EVENTS, "Changing eligibility of NBMA neighbor %I on %s", nb->ip, ifname); nb->eligible = nb2->eligible; } } else { - OSPF_TRACE(D_EVENTS, "Removing NBMA neighbor %I on interface %s", + OSPF_TRACE(D_EVENTS, "Removing NBMA neighbor %I on %s", nb->ip, ifname); rem_node(NODE nb); mb_free(nb); @@ -852,11 +852,12 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) continue; if (ospf_is_v3(p) && !ipa_is_link_local(nb->ip)) - log(L_WARN "In OSPFv3, configured neighbor address (%I) should be link-local", nb->ip); + log(L_WARN "%s: Configured neighbor address (%I) should be link-local", + p->p.name, nb->ip); if (! find_nbma_node(ifa, nb->ip)) { - OSPF_TRACE(D_EVENTS, "Adding NBMA neighbor %I on interface %s", + OSPF_TRACE(D_EVENTS, "Adding NBMA neighbor %I on %s", nb->ip, ifname); add_nbma_node(ifa, nb, !!find_neigh_by_ip(ifa, nb->ip)); } @@ -867,7 +868,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* TX LENGTH */ if (old->tx_length != new->tx_length) { - OSPF_TRACE(D_EVENTS, "Changing TX length on interface %s from %d to %d", + OSPF_TRACE(D_EVENTS, "Changing TX length of %s from %d to %d", ifname, old->tx_length, new->tx_length); /* ifa cannot be vlink */ @@ -878,7 +879,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* RX BUFFER */ if (old->rx_buffer != new->rx_buffer) { - OSPF_TRACE(D_EVENTS, "Changing buffer size on interface %s from %d to %d", + OSPF_TRACE(D_EVENTS, "Changing buffer size of %s from %d to %d", ifname, old->rx_buffer, new->rx_buffer); /* ifa cannot be vlink */ @@ -896,7 +897,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* LINK */ if (ifa->check_link != new->check_link) { - OSPF_TRACE(D_EVENTS, "%s link check on interface %s", + OSPF_TRACE(D_EVENTS, "%s link check for %s", new->check_link ? "Enabling" : "Disabling", ifname); ifa->check_link = new->check_link; @@ -908,7 +909,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* ECMP weight */ if (ifa->ecmp_weight != new->ecmp_weight) { - OSPF_TRACE(D_EVENTS, "Changing ECMP weight of interface %s from %d to %d", + OSPF_TRACE(D_EVENTS, "Changing ECMP weight of %s from %d to %d", ifname, (int)ifa->ecmp_weight + 1, (int)new->ecmp_weight + 1); ifa->ecmp_weight = new->ecmp_weight; } @@ -927,7 +928,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* BFD */ if (ifa->bfd != new->bfd) { - OSPF_TRACE(D_EVENTS, "%s BFD on interface %s", + OSPF_TRACE(D_EVENTS, "%s BFD for %s", new->bfd ? "Enabling" : "Disabling", ifname); ifa->bfd = new->bfd; @@ -1197,7 +1198,7 @@ ospf_iface_change_mtu(struct ospf_proto *p, struct ospf_iface *ifa) { /* ifa is not vlink */ - OSPF_TRACE(D_EVENTS, "Changing MTU on interface %s", ifa->ifname); + OSPF_TRACE(D_EVENTS, "Interface %s changed MTU to %d", ifa->iface->mtu); ifa->tx_length = ifa_tx_length(ifa); @@ -1285,7 +1286,7 @@ ospf_iface_info(struct ospf_iface *ifa) cli_msg(-1015, "\tType: %s%s", ospf_it[ifa->type], more); cli_msg(-1015, "\tArea: %R (%u)", ifa->oa->areaid, ifa->oa->areaid); } - cli_msg(-1015, "\tState: %s%s", ospf_is[ifa->state], ifa->stub ? " (stub)" : ""); + cli_msg(-1015, "\tState: %s%s", ospf_is_names[ifa->state], ifa->stub ? " (stub)" : ""); cli_msg(-1015, "\tPriority: %u", ifa->priority); cli_msg(-1015, "\tCost: %u", ifa->cost); if (ifa->oa->po->ecmp) diff --git a/proto/ospf/lsack.c b/proto/ospf/lsack.c index 5cac3f69..e590817e 100644 --- a/proto/ospf/lsack.c +++ b/proto/ospf/lsack.c @@ -140,10 +140,13 @@ ospf_receive_lsack(struct ospf_packet *pkt, struct ospf_iface *ifa, /* No need to check length, lsack has only basic header */ - OSPF_PACKET(ospf_dump_lsack, pkt, "LSACK packet received from %I via %s", n->ip, ifa->ifname); + OSPF_PACKET(ospf_dump_lsack, pkt, "LSACK packet received from nbr %R on %s", n->rid, ifa->ifname); if (n->state < NEIGHBOR_EXCHANGE) + { + OSPF_TRACE(D_PACKETS, "LSACK packet ignored - lesser state than Exchange"); return; + } ospf_neigh_sm(n, INM_HELLOREC); /* Not in RFC */ diff --git a/proto/ospf/lsreq.c b/proto/ospf/lsreq.c index a6c0cf24..067ad79b 100644 --- a/proto/ospf/lsreq.c +++ b/proto/ospf/lsreq.c @@ -93,7 +93,7 @@ ospf_send_lsreq(struct ospf_proto *p, struct ospf_neighbor *n) length = ospf_pkt_hdrlen(p) + i * sizeof(struct ospf_lsreq_header); pkt->length = htons(length); - OSPF_PACKET(ospf_dump_lsreq, pkt, "LSREQ packet sent to %I via %s", n->ip, ifa->ifname); + OSPF_PACKET(ospf_dump_lsreq, pkt, "LSREQ packet sent to nbr %R on %s", n->rid, ifa->ifname); ospf_send_to(ifa, n->ip); } @@ -110,10 +110,13 @@ ospf_receive_lsreq(struct ospf_packet *pkt, struct ospf_iface *ifa, /* No need to check length, lsreq has only basic header */ - OSPF_PACKET(ospf_dump_lsreq, pkt, "LSREQ packet received from %I via %s", n->ip, ifa->ifname); + OSPF_PACKET(ospf_dump_lsreq, pkt, "LSREQ packet received from nbr %R on %s", n->rid, ifa->ifname); if (n->state < NEIGHBOR_EXCHANGE) + { + OSPF_TRACE(D_PACKETS, "LSREQ packet ignored - lesser state than Exchange"); return; + } ospf_neigh_sm(n, INM_HELLOREC); /* Not in RFC */ @@ -134,8 +137,9 @@ ospf_receive_lsreq(struct ospf_packet *pkt, struct ospf_iface *ifa, en = ospf_hash_find(p->gr, domain, id, rt, type); if (!en) { - log(L_WARN "%s: Received LSREQ from %I for missing LSA (Type: %04x, Id: %R, Rt: %R)", - p->p.name, n->ip, type, id, rt); + LOG_LSA1("Bad LSR (Type: %04x, Id: %R, Rt: %R) in LSREQ", type, id, rt); + LOG_LSA2(" received from nbr %R on %s - LSA is missing", n->rid, ifa->ifname); + ospf_neigh_sm(n, INM_BADLSREQ); return; } diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 13ae54d1..0aa45f6a 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -338,7 +338,7 @@ ospf_send_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_list, uint lsa { i++; continue; } OSPF_PACKET(ospf_dump_lsupd, ospf_tx_buffer(ifa), - "LSUPD packet sent to %I via %s", n->ip, ifa->ifname); + "LSUPD packet sent to nbr %R on %s", n->rid, ifa->ifname); ospf_send_to(ifa, n->ip); } @@ -396,24 +396,22 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n) { struct ospf_proto *p = ifa->oa->po; - - /* RFC 2328 13. */ - + const char *err_dsc = NULL; + uint plen, err_val = 0; int skip_lsreq = 0; n->want_lsreq = 0; - uint plen = ntohs(pkt->length); - if (plen < (ospf_lsupd_hdrlen(p) + sizeof(struct ospf_lsa_header))) - { - log(L_ERR "OSPF: Bad LSUPD packet from %I - too short (%u B)", n->ip, plen); - return; - } + /* RFC 2328 13. */ - OSPF_PACKET(ospf_dump_lsupd, pkt, "LSUPD packet received from %I via %s", n->ip, ifa->ifname); + plen = ntohs(pkt->length); + if (plen < (ospf_lsupd_hdrlen(p) + sizeof(struct ospf_lsa_header))) + DROP("too short", plen); + + OSPF_PACKET(ospf_dump_lsupd, pkt, "LSUPD packet received from nbr %R on %s", n->rid, ifa->ifname); if (n->state < NEIGHBOR_EXCHANGE) { - OSPF_TRACE(D_PACKETS, "Received lsupd in lesser state than EXCHANGE from (%I)", n->ip); + OSPF_TRACE(D_PACKETS, "LSUPD packet ignored - lesser state than Exchange"); return; } @@ -429,33 +427,18 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, u32 lsa_len, lsa_type, lsa_domain; if (offset > bound) - { - log(L_WARN "%s: Received LSUPD from %I is too short", p->p.name, n->ip); - ospf_neigh_sm(n, INM_BADLSREQ); - return; - } + DROP("too short", plen); /* LSA header in network order */ lsa_n = ((void *) pkt) + offset; lsa_len = ntohs(lsa_n->length); offset += lsa_len; - if ((offset > plen) || ((lsa_len % 4) != 0) || - (lsa_len <= sizeof(struct ospf_lsa_header))) - { - log(L_WARN "%s: Received LSA from %I with bad length", p->p.name, n->ip); - ospf_neigh_sm(n, INM_BADLSREQ); - return; - } + if (offset > plen) + DROP("too short", plen); - /* RFC 2328 13. (1) - validate LSA checksum */ - u16 chsum = lsa_n->checksum; - if (chsum != lsasum_check(lsa_n, NULL)) - { - log(L_WARN "%s: Received LSA from %I with bad checksum: %x %x", - p->p.name, n->ip, chsum, lsa_n->checksum); - continue; - } + if (((lsa_len % 4) != 0) || (lsa_len <= sizeof(struct ospf_lsa_header))) + DROP("invalid LSA length", lsa_len); /* LSA header in host order */ lsa_ntoh_hdr(lsa_n, &lsa); @@ -464,33 +447,25 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, DBG("Update Type: %04x, Id: %R, Rt: %R, Sn: 0x%08x, Age: %u, Sum: %u\n", lsa_type, lsa.id, lsa.rt, lsa.sn, lsa.age, lsa.checksum); + /* RFC 2328 13. (1) - validate LSA checksum */ + if (lsa_n->checksum != lsasum_check(lsa_n, NULL)) + SKIP("invalid checksum"); + /* RFC 2328 13. (2) */ if (!lsa_type) - { - log(L_WARN "%s: Received unknown LSA type from %I", p->p.name, n->ip); - continue; - } + SKIP("unknown type"); /* RFC 5340 4.5.1 (2) and RFC 2328 13. (3) */ if (!oa_is_ext(ifa->oa) && (LSA_SCOPE(lsa_type) == LSA_SCOPE_AS)) - { - log(L_WARN "%s: Received LSA with AS scope in stub area from %I", p->p.name, n->ip); - continue; - } + SKIP("AS scope in stub area"); /* Errata 3746 to RFC 2328 - rt-summary-LSAs forbidden in stub areas */ if (!oa_is_ext(ifa->oa) && (lsa_type == LSA_T_SUM_RT)) - { - log(L_WARN "%s: Received rt-summary-LSA in stub area from %I", p->p.name, n->ip); - continue; - } + SKIP("rt-summary-LSA in stub area"); /* RFC 5340 4.5.1 (3) */ if (LSA_SCOPE(lsa_type) == LSA_SCOPE_RES) - { - log(L_WARN "%s: Received LSA with invalid scope from %I", p->p.name, n->ip); - continue; - } + SKIP("invalid scope"); /* Find local copy of LSA in link state database */ en = ospf_hash_find(p->gr, lsa_domain, lsa.id, lsa.rt, lsa_type); @@ -528,9 +503,8 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, if (lsa_validate(&lsa, lsa_type, ospf_is_v2(p), body) == 0) { - log(L_WARN "%s: Received invalid LSA from %I", p->p.name, n->ip); mb_free(body); - continue; + SKIP("invalid body"); } /* 13. (5f) - handle self-originated LSAs, see also 13.4. */ @@ -542,8 +516,8 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, continue; } - /* 13. (5c) - remove old LSA from all retransmission lists */ - /* + /* 13. (5c) - remove old LSA from all retransmission lists + * * We only need to remove it from the retransmission list of the neighbor * that send us the new LSA. The old LSA is automatically replaced in * retransmission lists by the new LSA. @@ -561,8 +535,8 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, WALK_LIST(ifi, p->iface_list) WALK_LIST(ni, ifi->neigh_list) - if (ni->state > NEIGHBOR_EXSTART) - ospf_lsa_lsrt_down(en, ni); + if (ni->state > NEIGHBOR_EXSTART) + ospf_lsa_lsrt_down(en, ni); #endif /* 13. (5d) - install new LSA into database */ @@ -615,7 +589,13 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, /* Send newer local copy back to neighbor */ /* FIXME - check for MinLSArrival ? */ ospf_send_lsupd(p, &en, 1, n); + + continue; } + + skip: + LOG_LSA1("Bad LSA (Type: %04x, Id: %R, Rt: %R) in LSUPD", lsa_type, lsa.id, lsa.rt); + LOG_LSA2(" received from nbr %R on %s - %s", n->rid, ifa->ifname, err_dsc); } /* Send direct LSACKs */ @@ -630,4 +610,14 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, */ if ((n->state == NEIGHBOR_LOADING) && n->want_lsreq && !skip_lsreq) ospf_send_lsreq(p, n); + + return; + +drop: + LOG_PKT("Bad LSUPD packet from nbr %R on %s - %s (%u)", + n->rid, ifa->ifname, err_dsc, err_val); + + // XXXX realy? + ospf_neigh_sm(n, INM_SEQMIS); + return; } diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index ee1e8d0f..c75b1b93 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -10,24 +10,19 @@ #include "ospf.h" -char *ospf_ns[] = { " down", - " attempt", - " init", - " 2way", - " exstart", - "exchange", - " loading", - " full" + +const char *ospf_ns_names[] = { + "Down", "Attempt", "Init", "2-Way", "ExStart", "Exchange", "Loading", "Full" }; -const char *ospf_inm[] = - { "hello received", "neighbor start", "2-way received", - "negotiation done", "exstart done", "bad ls request", "load done", - "adjacency ok?", "sequence mismatch", "1-way received", "kill neighbor", - "inactivity timer", "line down" +const char *ospf_inm_names[] = { + "HelloReceived", "Start", "2-WayReceived", "NegotiationDone", "ExchangeDone", + "BadLSReq", "LoadingDone", "AdjOK?", "SeqNumberMismatch", "1-WayReceived", + "KillNbr", "InactivityTimer", "LLDown" }; -static void neigh_chstate(struct ospf_neighbor *n, u8 state); + +static int can_do_adj(struct ospf_neighbor *n); static void neighbor_timer_hook(timer * timer); static void rxmt_timer_hook(timer * timer); static void ackd_timer_hook(timer * t); @@ -113,8 +108,29 @@ ospf_neighbor_new(struct ospf_iface *ifa) return (n); } +static void +ospf_neigh_down(struct ospf_neighbor *n) +{ + struct ospf_iface *ifa = n->ifa; + struct ospf_proto *p = ifa->oa->po; + + if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP)) + { + struct nbma_node *nn = find_nbma_node(ifa, n->ip); + if (nn) + nn->found = 0; + } + + s_get(&(n->dbsi)); + release_lsrtl(p, n); + rem_node(NODE n); + rfree(n->pool); + + OSPF_TRACE(D_EVENTS, "Neighbor %R on %s removed", n->rid, ifa->ifname); +} + /** - * neigh_chstate - handles changes related to new or lod state of neighbor + * ospf_neigh_chstate - handles changes related to new or lod state of neighbor * @n: OSPF neighbor * @state: new state * @@ -122,7 +138,7 @@ ospf_neighbor_new(struct ospf_iface *ifa) * starts rxmt timers, call interface state machine etc. */ static void -neigh_chstate(struct ospf_neighbor *n, u8 state) +ospf_neigh_chstate(struct ospf_neighbor *n, u8 state) { struct ospf_iface *ifa = n->ifa; struct ospf_proto *p = ifa->oa->po; @@ -132,16 +148,11 @@ neigh_chstate(struct ospf_neighbor *n, u8 state) if (state == old_state) return; - OSPF_TRACE(D_EVENTS, "Neighbor %I changes state from %s to %s", - n->ip, ospf_ns[old_state], ospf_ns[state]); + OSPF_TRACE(D_EVENTS, "Neighbor %R on %s changed state from %s to %s", + n->rid, ifa->ifname, ospf_ns_names[old_state], ospf_ns_names[state]); n->state = state; - if ((state == NEIGHBOR_2WAY) && (old_state < NEIGHBOR_2WAY)) - ospf_iface_sm(ifa, ISM_NEICH); - if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY)) - ospf_iface_sm(ifa, ISM_NEICH); - /* Increase number of partial adjacencies */ if ((state == NEIGHBOR_EXCHANGE) || (state == NEIGHBOR_LOADING)) p->padj++; @@ -181,8 +192,182 @@ neigh_chstate(struct ospf_neighbor *n, u8 state) if (state > NEIGHBOR_EXSTART) n->myimms &= ~DBDES_I; + + /* Generate NeighborChange event if needed, see RFC 2328 9.2 */ + if ((state == NEIGHBOR_2WAY) && (old_state < NEIGHBOR_2WAY)) + ospf_iface_sm(ifa, ISM_NEICH); + if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY)) + ospf_iface_sm(ifa, ISM_NEICH); } +/** + * ospf_neigh_sm - ospf neighbor state machine + * @n: neighor + * @event: actual event + * + * This part implements the neighbor state machine as described in 10.3 of + * RFC 2328. The only difference is that state %NEIGHBOR_ATTEMPT is not + * used. We discover neighbors on nonbroadcast networks in the + * same way as on broadcast networks. The only difference is in + * sending hello packets. These are sent to IPs listed in + * @ospf_iface->nbma_list . + */ +void +ospf_neigh_sm(struct ospf_neighbor *n, int event) +{ + struct ospf_proto *p = n->ifa->oa->po; + + DBG("Neighbor state machine for %R on %s, event %s\n", + n->rid, n->ifa->ifname, ospf_inm_names[event]); + + switch (event) + { + case INM_START: + ospf_neigh_chstate(n, NEIGHBOR_ATTEMPT); + /* NBMA are used different way */ + break; + + case INM_HELLOREC: + if (n->state < NEIGHBOR_INIT) + ospf_neigh_chstate(n, NEIGHBOR_INIT); + + /* Restart inactivity timer */ + tm_start(n->inactim, n->ifa->deadint); + break; + + case INM_2WAYREC: + if (n->state < NEIGHBOR_2WAY) + ospf_neigh_chstate(n, NEIGHBOR_2WAY); + if ((n->state == NEIGHBOR_2WAY) && can_do_adj(n)) + ospf_neigh_chstate(n, NEIGHBOR_EXSTART); + break; + + case INM_NEGDONE: + if (n->state == NEIGHBOR_EXSTART) + { + ospf_neigh_chstate(n, NEIGHBOR_EXCHANGE); + + /* Reset DB summary list iterator */ + s_get(&(n->dbsi)); + s_init(&(n->dbsi), &p->lsal); + + /* Add MaxAge LSA entries to retransmission list */ + ospf_add_flushed_to_lsrt(p, n); + + /* FIXME: Why is this here ? */ + ospf_reset_lsack_queue(n); + } + else + bug("NEGDONE and I'm not in EXSTART?"); + break; + + case INM_EXDONE: + ospf_neigh_chstate(n, NEIGHBOR_LOADING); + break; + + case INM_LOADDONE: + ospf_neigh_chstate(n, NEIGHBOR_FULL); + break; + + case INM_ADJOK: + switch (n->state) + { + case NEIGHBOR_2WAY: + /* Can In build adjacency? */ + if (can_do_adj(n)) + { + ospf_neigh_chstate(n, NEIGHBOR_EXSTART); + } + break; + default: + if (n->state >= NEIGHBOR_EXSTART) + if (!can_do_adj(n)) + { + reset_lists(p, n); + ospf_neigh_chstate(n, NEIGHBOR_2WAY); + } + break; + } + break; + + case INM_SEQMIS: + case INM_BADLSREQ: + if (n->state >= NEIGHBOR_EXCHANGE) + { + reset_lists(p, n); + ospf_neigh_chstate(n, NEIGHBOR_EXSTART); + } + break; + + case INM_KILLNBR: + case INM_LLDOWN: + case INM_INACTTIM: + /* No need for reset_lists() */ + ospf_neigh_chstate(n, NEIGHBOR_DOWN); + ospf_neigh_down(n); + break; + + case INM_1WAYREC: + reset_lists(p, n); + ospf_neigh_chstate(n, NEIGHBOR_INIT); + break; + + default: + bug("%s: INM - Unknown event?", p->p.name); + break; + } +} + +static int +can_do_adj(struct ospf_neighbor *n) +{ + struct ospf_iface *ifa = n->ifa; + struct ospf_proto *p = ifa->oa->po; + int i = 0; + + switch (ifa->type) + { + case OSPF_IT_PTP: + case OSPF_IT_PTMP: + case OSPF_IT_VLINK: + i = 1; + break; + case OSPF_IT_BCAST: + case OSPF_IT_NBMA: + switch (ifa->state) + { + case OSPF_IS_DOWN: + case OSPF_IS_LOOP: + bug("%s: Iface %s in down state?", p->p.name, ifa->ifname); + break; + case OSPF_IS_WAITING: + DBG("%s: Neighbor? on iface %s\n", p->p.name, ifa->ifname); + break; + case OSPF_IS_DROTHER: + if (((n->rid == ifa->drid) || (n->rid == ifa->bdrid)) + && (n->state >= NEIGHBOR_2WAY)) + i = 1; + break; + case OSPF_IS_PTP: + case OSPF_IS_BACKUP: + case OSPF_IS_DR: + if (n->state >= NEIGHBOR_2WAY) + i = 1; + break; + default: + bug("%s: Iface %s in unknown state?", p->p.name, ifa->ifname); + break; + } + break; + default: + bug("%s: Iface %s is unknown type?", p->p.name, ifa->ifname); + break; + } + DBG("%s: Iface %s can_do_adj=%d\n", p->p.name, ifa->ifname, i); + return i; +} + + static inline u32 neigh_get_id(struct ospf_proto *p, struct ospf_neighbor *n) { return ospf_is_v2(p) ? ipa_to_u32(n->ip) : n->rid; } @@ -273,181 +458,16 @@ elect_dr(struct ospf_proto *p, list nl) return (n); } -static int -can_do_adj(struct ospf_neighbor *n) -{ - struct ospf_iface *ifa = n->ifa; - struct ospf_proto *p = ifa->oa->po; - int i = 0; - - switch (ifa->type) - { - case OSPF_IT_PTP: - case OSPF_IT_PTMP: - case OSPF_IT_VLINK: - i = 1; - break; - case OSPF_IT_BCAST: - case OSPF_IT_NBMA: - switch (ifa->state) - { - case OSPF_IS_DOWN: - case OSPF_IS_LOOP: - bug("%s: Iface %s in down state?", p->p.name, ifa->ifname); - break; - case OSPF_IS_WAITING: - DBG("%s: Neighbor? on iface %s\n", p->p.name, ifa->ifname); - break; - case OSPF_IS_DROTHER: - if (((n->rid == ifa->drid) || (n->rid == ifa->bdrid)) - && (n->state >= NEIGHBOR_2WAY)) - i = 1; - break; - case OSPF_IS_PTP: - case OSPF_IS_BACKUP: - case OSPF_IS_DR: - if (n->state >= NEIGHBOR_2WAY) - i = 1; - break; - default: - bug("%s: Iface %s in unknown state?", p->p.name, ifa->ifname); - break; - } - break; - default: - bug("%s: Iface %s is unknown type?", p->p.name, ifa->ifname); - break; - } - DBG("%s: Iface %s can_do_adj=%d\n", p->p.name, ifa->ifname, i); - return i; -} - -/** - * ospf_neigh_sm - ospf neighbor state machine - * @n: neighor - * @event: actual event - * - * This part implements the neighbor state machine as described in 10.3 of - * RFC 2328. The only difference is that state %NEIGHBOR_ATTEMPT is not - * used. We discover neighbors on nonbroadcast networks in the - * same way as on broadcast networks. The only difference is in - * sending hello packets. These are sent to IPs listed in - * @ospf_iface->nbma_list . - */ -void -ospf_neigh_sm(struct ospf_neighbor *n, int event) -{ - struct ospf_proto *p = n->ifa->oa->po; - - DBG("Neighbor state machine for neighbor %I, event '%s'\n", n->ip, - ospf_inm[event]); - - switch (event) - { - case INM_START: - neigh_chstate(n, NEIGHBOR_ATTEMPT); - /* NBMA are used different way */ - break; - - case INM_HELLOREC: - if ((n->state == NEIGHBOR_DOWN) || - (n->state == NEIGHBOR_ATTEMPT)) - neigh_chstate(n, NEIGHBOR_INIT); - - /* Restart inactivity timer */ - tm_start(n->inactim, n->ifa->deadint); - break; - - case INM_2WAYREC: - if (n->state < NEIGHBOR_2WAY) - neigh_chstate(n, NEIGHBOR_2WAY); - if ((n->state == NEIGHBOR_2WAY) && can_do_adj(n)) - neigh_chstate(n, NEIGHBOR_EXSTART); - break; - - case INM_NEGDONE: - if (n->state == NEIGHBOR_EXSTART) - { - neigh_chstate(n, NEIGHBOR_EXCHANGE); - - /* Reset DB summary list iterator */ - s_get(&(n->dbsi)); - s_init(&(n->dbsi), &p->lsal); - - /* Add MaxAge LSA entries to retransmission list */ - ospf_add_flushed_to_lsrt(p, n); - - /* FIXME: Why is this here ? */ - ospf_reset_lsack_queue(n); - } - else - bug("NEGDONE and I'm not in EXSTART?"); - break; - - case INM_EXDONE: - neigh_chstate(n, NEIGHBOR_LOADING); - break; - - case INM_LOADDONE: - neigh_chstate(n, NEIGHBOR_FULL); - break; - - case INM_ADJOK: - switch (n->state) - { - case NEIGHBOR_2WAY: - /* Can In build adjacency? */ - if (can_do_adj(n)) - { - neigh_chstate(n, NEIGHBOR_EXSTART); - } - break; - default: - if (n->state >= NEIGHBOR_EXSTART) - if (!can_do_adj(n)) - { - reset_lists(p,n); - neigh_chstate(n, NEIGHBOR_2WAY); - } - break; - } - break; - - case INM_SEQMIS: - case INM_BADLSREQ: - if (n->state >= NEIGHBOR_EXCHANGE) - { - reset_lists(p, n); - neigh_chstate(n, NEIGHBOR_EXSTART); - } - break; - - case INM_KILLNBR: - case INM_LLDOWN: - case INM_INACTTIM: - reset_lists(p, n); - neigh_chstate(n, NEIGHBOR_DOWN); - break; - - case INM_1WAYREC: - reset_lists(p, n); - neigh_chstate(n, NEIGHBOR_INIT); - break; - - default: - bug("%s: INM - Unknown event?", p->p.name); - break; - } -} - /** * ospf_dr_election - (Backup) Designed Router election * @ifa: actual interface * * When the wait timer fires, it is time to elect (Backup) Designated Router. - * Structure describing me is added to this list so every electing router - * has the same list. Backup Designated Router is elected before Designated - * Router. This process is described in 9.4 of RFC 2328. + * Structure describing me is added to this list so every electing router has + * the same list. Backup Designated Router is elected before Designated + * Router. This process is described in 9.4 of RFC 2328. The function is + * supposed to be called only from ospf_iface_sm() as a part of the interface + * state machine. */ void ospf_dr_election(struct ospf_iface *ifa) @@ -506,6 +526,7 @@ ospf_dr_election(struct ospf_iface *ifa) DBG("DR=%R, BDR=%R\n", ifa->drid, ifa->bdrid); + /* We are part of the interface state machine */ if (ifa->drid == myid) ospf_iface_chstate(ifa, OSPF_IS_DR); else if (ifa->bdrid == myid) @@ -544,39 +565,15 @@ find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip) return NULL; } -/* Neighbor is inactive for a long time. Remove it. */ static void neighbor_timer_hook(timer * timer) { struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data; - struct ospf_iface *ifa = n->ifa; - struct ospf_proto *p = ifa->oa->po; + struct ospf_proto *p = n->ifa->oa->po; - OSPF_TRACE(D_EVENTS, "Inactivity timer fired on interface %s for neighbor %I", - ifa->ifname, n->ip); - ospf_neigh_remove(n); -} - -void -ospf_neigh_remove(struct ospf_neighbor *n) -{ - struct ospf_iface *ifa = n->ifa; - struct ospf_proto *p = ifa->oa->po; - - if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP)) - { - struct nbma_node *nn = find_nbma_node(ifa, n->ip); - if (nn) - nn->found = 0; - } - - neigh_chstate(n, NEIGHBOR_DOWN); - - s_get(&(n->dbsi)); - release_lsrtl(p, n); - rem_node(NODE n); - rfree(n->pool); - OSPF_TRACE(D_EVENTS, "Deleting neigbor %R", n->rid); + OSPF_TRACE(D_EVENTS, "Inactivity timer expired for neighbor %R on %s", + n->rid, n->ifa->ifname); + ospf_neigh_sm(n, INM_INACTTIM); } static void @@ -587,8 +584,9 @@ ospf_neigh_bfd_hook(struct bfd_request *req) if (req->down) { - OSPF_TRACE(D_EVENTS, "BFD session down for %I on %s", n->ip, n->ifa->ifname); - ospf_neigh_remove(n); + OSPF_TRACE(D_EVENTS, "BFD session down for neighbor %R on %s", + n->rid, n->ifa->ifname); + ospf_neigh_sm(n, INM_INACTTIM); } } @@ -611,7 +609,7 @@ void ospf_sh_neigh_info(struct ospf_neighbor *n) { struct ospf_iface *ifa = n->ifa; - char *pos = "other"; + char *pos = "ptp "; char etime[6]; int exp, sec, min; @@ -627,16 +625,18 @@ ospf_sh_neigh_info(struct ospf_neighbor *n) bsprintf(etime, "%02u:%02u", min, sec); } - if (n->rid == ifa->drid) - pos = "dr "; - else if (n->rid == ifa->bdrid) - pos = "bdr "; - else if ((n->ifa->type == OSPF_IT_PTP) || (n->ifa->type == OSPF_IT_PTMP) || - (n->ifa->type == OSPF_IT_VLINK)) - pos = "ptp "; + if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA)) + { + if (n->rid == ifa->drid) + pos = "dr "; + else if (n->rid == ifa->bdrid) + pos = "bdr "; + else + pos = "other"; + } cli_msg(-1013, "%-1R\t%3u\t%s/%s\t%-5s\t%-10s %-1I", n->rid, n->priority, - ospf_ns[n->state], pos, etime, ifa->ifname, n->ip); + ospf_ns_names[n->state], pos, etime, ifa->ifname, n->ip); } static void @@ -645,18 +645,18 @@ rxmt_timer_hook(timer *t) struct ospf_neighbor *n = t->data; struct ospf_proto *p = n->ifa->oa->po; - DBG("%s: RXMT timer fired on interface %s for neigh %I\n", + DBG("%s: RXMT timer fired on %s for neigh %I\n", p->p.name, n->ifa->ifname, n->ip); switch (n->state) { case NEIGHBOR_EXSTART: - ospf_send_dbdes(p, n, 1); + ospf_send_dbdes(p, n); return; case NEIGHBOR_EXCHANGE: - if (n->myimms & DBDES_MS) - ospf_send_dbdes(p, n, 0); + if (n->myimms & DBDES_MS) + ospf_rxmt_dbdes(p, n); case NEIGHBOR_LOADING: ospf_send_lsreq(p, n); return; @@ -666,9 +666,6 @@ rxmt_timer_hook(timer *t) if (!EMPTY_SLIST(n->lsrtl)) ospf_rxmt_lsupd(p, n); return; - - default: - return; } } @@ -678,7 +675,7 @@ ackd_timer_hook(timer *t) struct ospf_neighbor *n = t->data; struct ospf_proto *p = n->ifa->oa->po; - DBG("%s: ACKD timer fired on interface %s for neigh %I\n", + DBG("%s: ACKD timer fired on %s for neigh %I\n", p->p.name, n->ifa->ifname, n->ip); ospf_send_lsack(p, n, ACKL_DELAY); diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index 470a8633..01e53922 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -252,6 +252,9 @@ ospf_start(struct proto *P) p->gr = ospf_top_new(p, P->pool); s_init_list(&(p->lsal)); + p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 }; + p->log_lsa_tbf = (struct tbf){ .rate = 4, .burst = 20 }; + WALK_LIST(ac, c->area_list) ospf_area_add(p, ac); @@ -742,7 +745,7 @@ ospf_sh(struct proto *P) } cli_msg(-1014, "%s:", p->p.name); - cli_msg(-1014, "RFC1583 compatibility: %s", (p->rfc1583 ? "enable" : "disabled")); + cli_msg(-1014, "RFC1583 compatibility: %s", (p->rfc1583 ? "enabled" : "disabled")); cli_msg(-1014, "Stub router: %s", (p->stub_router ? "Yes" : "No")); cli_msg(-1014, "RT scheduler tick: %d", p->tick); cli_msg(-1014, "Number of areas: %u", p->areano); diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index f464a3ed..6df5df08 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -73,12 +73,28 @@ // FIXME: MAX_PREFIX_LENGTH #define OSPF_TRACE(flags, msg, args...) \ -do { if ((p->p.debug & flags) || OSPF_FORCE_DEBUG) \ - log(L_TRACE "%s: " msg, p->p.name , ## args ); } while(0) + do { if ((p->p.debug & flags) || OSPF_FORCE_DEBUG) \ + log(L_TRACE "%s: " msg, p->p.name , ## args ); } while(0) #define OSPF_PACKET(dumpfn, buffer, msg, args...) \ -do { if ((p->p.debug & D_PACKETS) || OSPF_FORCE_DEBUG) \ - { log(L_TRACE "%s: " msg, p->p.name, ## args ); dumpfn(p, buffer); } } while(0) + do { if ((p->p.debug & D_PACKETS) || OSPF_FORCE_DEBUG) \ + { log(L_TRACE "%s: " msg, p->p.name, ## args ); dumpfn(p, buffer); } } while(0) + +#define LOG_PKT(msg, args...) \ + log_rl(&p->log_pkt_tbf, L_REMOTE "%s: " msg, p->p.name, args) + +#define LOG_PKT_AUTH(msg, args...) \ + log_rl(&p->log_pkt_tbf, L_AUTH "%s: " msg, p->p.name, args) + +#define LOG_PKT_WARN(msg, args...) \ + log_rl(&p->log_pkt_tbf, L_WARN "%s: " msg, p->p.name, args) + +#define LOG_LSA1(msg, args...) \ + log_rl(&p->log_lsa_tbf, L_REMOTE "%s: " msg, p->p.name, args) + +#define LOG_LSA2(msg, args...) \ + do { if (! p->log_lsa_tbf.mark) \ + log(L_REMOTE "%s: " msg, p->p.name, args); } while(0) #define OSPF_PROTO 89 @@ -248,6 +264,8 @@ struct ospf_proto sock *vlink_sk; /* IP socket used for vlink TX */ u32 router_id; u32 last_vlink_id; /* Interface IDs for vlinks (starts at 0x80000000) */ + struct tbf log_pkt_tbf; /* TBF for packet messages */ + struct tbf log_lsa_tbf; /* TBF for LSA messages */ }; struct ospf_area @@ -283,7 +301,7 @@ struct ospf_iface pool *pool; sock *sk; /* IP socket */ - list neigh_list; /* List of neigbours (struct ospf_neighbor) */ + list neigh_list; /* List of neighbors (struct ospf_neighbor) */ u32 cost; /* Cost of iface */ u32 waitint; /* number of sec before changing state from wait */ u32 rxmtint; /* number of seconds between LSA retransmissions */ @@ -368,8 +386,8 @@ struct ospf_neighbor /* Entries dr and bdr store IP addresses in OSPFv2 and router IDs in OSPFv3, we use the same type to simplify handling */ - u32 dr; /* Neigbour's idea of DR */ - u32 bdr; /* Neigbour's idea of BDR */ + u32 dr; /* Neighbor's idea of DR */ + u32 bdr; /* Neighbor's idea of BDR */ u32 iface_id; /* ID of Neighbour's iface connected to common network */ /* Database summary list iterator, controls initial dbdes exchange. @@ -890,7 +908,6 @@ void ospf_neigh_sm(struct ospf_neighbor *n, int event); void ospf_dr_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); -void ospf_neigh_remove(struct ospf_neighbor *n); void ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd); void ospf_sh_neigh_info(struct ospf_neighbor *n); @@ -916,6 +933,10 @@ static inline void ospf_send_to_des(struct ospf_iface *ifa) ospf_send_to_bdr(ifa); } +#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0) +#define DROP1(DSC) do { err_dsc = DSC; goto drop; } while(0) +#define SKIP(DSC) do { err_dsc = DSC; goto skip; } while(0) + static inline uint ospf_pkt_hdrlen(struct ospf_proto *p) { return ospf_is_v2(p) ? (sizeof(struct ospf_packet) + sizeof(union ospf_auth)) : sizeof(struct ospf_packet); } @@ -931,7 +952,8 @@ void ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dir void ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n, ip_addr faddr); /* dbdes.c */ -void ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n, int next); +void ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n); +void ospf_rxmt_dbdes(struct ospf_proto *p, struct ospf_neighbor *n); void ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n); /* lsreq.c */ diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index d64d7d6b..2814712d 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -66,7 +66,7 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) log(L_ERR "No suitable password found for authentication"); return; } - password_cpy(auth->password, passwd->password, sizeof(union ospf_auth)); + strncpy(auth->password, passwd->password, sizeof(auth->password)); case OSPF_AUTH_NONE: { @@ -106,7 +106,7 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) void *tail = ((void *) pkt) + plen; char password[OSPF_AUTH_CRYPT_SIZE]; - password_cpy(password, passwd->password, OSPF_AUTH_CRYPT_SIZE); + strncpy(password, passwd->password, sizeof(password)); struct MD5Context ctxt; MD5Init(&ctxt); @@ -120,23 +120,22 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) } } + /* We assume OSPFv2 in ospf_pkt_checkauth() */ static int -ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, int size) +ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, int len) { struct ospf_proto *p = ifa->oa->po; union ospf_auth *auth = (void *) (pkt + 1); - struct password_item *pass = NULL, *ptmp; - char password[OSPF_AUTH_CRYPT_SIZE]; + struct password_item *pass = NULL; + const char *err_dsc = NULL; + uint err_val = 0; uint plen = ntohs(pkt->length); u8 autype = pkt->autype; if (autype != ifa->autype) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: Method differs (%d)", autype); - return 0; - } + DROP("authentication method mismatch", autype); switch (autype) { @@ -146,85 +145,65 @@ ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_ case OSPF_AUTH_SIMPLE: pass = password_find(ifa->passwords, 1); if (!pass) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: no password found"); - return 0; - } + DROP1("no password found"); + + if (!password_verify(pass, auth->password, sizeof(auth->password))) + DROP("wrong password", pass->id); - password_cpy(password, pass->password, sizeof(union ospf_auth)); - if (memcmp(auth->password, password, sizeof(union ospf_auth))) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: different passwords"); - return 0; - } return 1; case OSPF_AUTH_CRYPT: if (auth->md5.len != OSPF_AUTH_CRYPT_SIZE) + DROP("invalid MD5 digest length", auth->md5.len); + + if (plen + OSPF_AUTH_CRYPT_SIZE > len) + DROP("length mismatch", len); + + u32 rcv_csn = ntohl(auth->md5.csn); + if (n && (rcv_csn < n->csn)) + // DROP("lower sequence number", rcv_csn); { - OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong size of md5 digest"); + /* We want to report both new and old CSN */ + LOG_PKT_AUTH("Authentication failed for nbr %R on %s - " + "lower sequence number (rcv %u, old %u)", + n->rid, ifa->ifname, rcv_csn, n->csn); return 0; } - if (plen + OSPF_AUTH_CRYPT_SIZE > size) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: size mismatch (%d vs %d)", - plen + OSPF_AUTH_CRYPT_SIZE, size); - return 0; - } - - if (n) - { - u32 rcv_csn = ntohl(auth->md5.csn); - if(rcv_csn < n->csn) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: lower sequence number (rcv %d, old %d)", rcv_csn, n->csn); - return 0; - } - - n->csn = rcv_csn; - } - - if (ifa->passwords) - { - WALK_LIST(ptmp, *(ifa->passwords)) - { - if (auth->md5.keyid != ptmp->id) continue; - if ((ptmp->accfrom > now_real) || (ptmp->accto < now_real)) continue; - pass = ptmp; - break; - } - } - + pass = password_find_by_id(ifa->passwords, auth->md5.keyid); if (!pass) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: no suitable md5 password found"); - return 0; - } + DROP("no suitable password found", auth->md5.keyid); void *tail = ((void *) pkt) + plen; + char passwd[OSPF_AUTH_CRYPT_SIZE]; char md5sum[OSPF_AUTH_CRYPT_SIZE]; - password_cpy(password, pass->password, OSPF_AUTH_CRYPT_SIZE); + + strncpy(passwd, pass->password, OSPF_AUTH_CRYPT_SIZE); struct MD5Context ctxt; MD5Init(&ctxt); MD5Update(&ctxt, (char *) pkt, plen); - MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); + MD5Update(&ctxt, passwd, OSPF_AUTH_CRYPT_SIZE); MD5Final(md5sum, &ctxt); if (memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE)) - { - OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong md5 digest"); - return 0; - } + DROP("wrong MD5 digest", pass->id); + + if (n) + n->csn = rcv_csn; + return 1; default: - OSPF_TRACE(D_PACKETS, "OSPF_auth: unknown auth type"); - return 0; + bug("Unknown authentication type"); } -} +drop: + LOG_PKT_AUTH("Authentication failed for nbr %R on %s - %s (%u)", + (n ? n->rid : ntohl(pkt->routerid)), ifa->ifname, err_dsc, err_val); + + return 0; +} /** * ospf_rx_hook @@ -236,13 +215,10 @@ ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_ * non generic functions. */ int -ospf_rx_hook(sock *sk, int size) +ospf_rx_hook(sock *sk, int len) { - char *mesg = "OSPF: Bad packet from "; - - /* We want just packets from sk->iface. Unfortunately, on BSD we - cannot filter out other packets at kernel level and we receive - all packets on all sockets */ + /* We want just packets from sk->iface. Unfortunately, on BSD we cannot filter + out other packets at kernel level and we receive all packets on all sockets */ if (sk->lifindex != sk->iface->index) return 1; @@ -252,6 +228,8 @@ ospf_rx_hook(sock *sk, int size) /* Initially, the packet is associated with the 'master' iface */ struct ospf_iface *ifa = sk->data; struct ospf_proto *p = ifa->oa->po; + const char *err_dsc = NULL; + uint err_val = 0; int src_local, dst_local, dst_mcast; src_local = ipa_in_net(sk->faddr, ifa->addr->prefix, ifa->addr->pxlen); @@ -282,41 +260,31 @@ ospf_rx_hook(sock *sk, int size) * link-local src address, but does not enforce it. Strange. */ if (dst_mcast && !src_local) - log(L_WARN "OSPF: Received multicast packet from %I (not link-local)", sk->faddr); + LOG_PKT_WARN("Multicast packet received from not-link-local %I via %s", + sk->faddr, ifa->ifname); } - /* Second, we check packet size, checksum, and the protocol version */ - struct ospf_packet *pkt = (struct ospf_packet *) ip_skip_header(sk->rbuf, &size); + /* Second, we check packet length, checksum, and the protocol version */ + struct ospf_packet *pkt = (struct ospf_packet *) ip_skip_header(sk->rbuf, &len); if (pkt == NULL) - { - log(L_ERR "%s%I - bad IP header", mesg, sk->faddr); - return 1; - } + DROP("bad IP header", len); if (ifa->check_ttl && (sk->rcv_ttl < 255)) - { - log(L_ERR "%s%I - TTL %d (< 255)", mesg, sk->faddr, sk->rcv_ttl); - return 1; - } + DROP("wrong TTL", sk->rcv_ttl); - if ((uint) size < sizeof(struct ospf_packet)) - { - log(L_ERR "%s%I - too short (%u bytes)", mesg, sk->faddr, size); - return 1; - } + if (len < sizeof(struct ospf_packet)) + DROP("too short", len); + + if (pkt->version != ospf_get_version(p)) + DROP("version mismatch", pkt->version); uint plen = ntohs(pkt->length); if ((plen < sizeof(struct ospf_packet)) || ((plen % 4) != 0)) - { - log(L_ERR "%s%I - invalid length (%u)", mesg, sk->faddr, plen); - return 1; - } + DROP("invalid length", plen); if (sk->flags & SKF_TRUNCATED) { - log(L_WARN "%s%I - too large (%d/%d)", mesg, sk->faddr, plen, size); - /* If we have dynamic buffers and received truncated message, we expand RX buffer */ uint bs = plen + 256; @@ -325,20 +293,11 @@ ospf_rx_hook(sock *sk, int size) if (!ifa->cf->rx_buffer && (bs > sk->rbsize)) sk_set_rbsize(sk, bs); - return 1; + DROP("truncated", plen); } - if (plen > size) - { - log(L_ERR "%s%I - size field does not match (%d/%d)", mesg, sk->faddr, plen, size); - return 1; - } - - if (pkt->version != ospf_get_version(p)) - { - log(L_ERR "%s%I - version %u", mesg, sk->faddr, pkt->version); - return 1; - } + if (plen > len) + DROP("length mismatch", plen); if (ospf_is_v2(p) && (pkt->autype != OSPF_AUTH_CRYPT)) { @@ -346,11 +305,8 @@ ospf_rx_hook(sock *sk, int size) uint blen = plen - hlen; void *body = ((void *) pkt) + hlen; - if (! ipsum_verify(pkt, sizeof(struct ospf_packet), body, blen, NULL)) - { - log(L_ERR "%s%I - bad checksum", mesg, sk->faddr); - return 1; - } + if (!ipsum_verify(pkt, sizeof(struct ospf_packet), body, blen, NULL)) + DROP1("invalid checksum"); } /* Third, we resolve associated iface and handle vlinks. */ @@ -368,10 +324,7 @@ ospf_rx_hook(sock *sk, int size) /* It is real iface, source should be local (in OSPFv2) */ if (ospf_is_v2(p) && !src_local) - { - log(L_ERR "%s%I - strange source address for %s", mesg, sk->faddr, ifa->ifname); - return 1; - } + DROP1("strange source address"); goto found; } @@ -415,13 +368,11 @@ ospf_rx_hook(sock *sk, int size) if (instance_id != ifa->instance_id) return 1; - log(L_ERR "%s%I - area does not match (%R vs %R)", - mesg, sk->faddr, areaid, ifa->oa->areaid); - return 1; + DROP("area mismatch", areaid); } - found: +found: if (ifa->stub) /* This shouldn't happen */ return 1; @@ -429,18 +380,12 @@ ospf_rx_hook(sock *sk, int size) return 1; if (rid == p->router_id) - { - log(L_ERR "%s%I - received my own router ID!", mesg, sk->faddr); - return 1; - } + DROP1("my own router ID"); if (rid == 0) - { - log(L_ERR "%s%I - router id = 0.0.0.0", mesg, sk->faddr); - return 1; - } + DROP1("zero router ID"); - /* In OSPFv2, neighbors are identified by either IP or Router ID, base on network type */ + /* In OSPFv2, neighbors are identified by either IP or Router ID, based on network type */ uint t = ifa->type; struct ospf_neighbor *n; if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP))) @@ -450,16 +395,15 @@ ospf_rx_hook(sock *sk, int size) if (!n && (pkt->type != HELLO_P)) { - log(L_WARN "OSPF: Received non-hello packet from unknown neighbor (src %I, iface %s)", - sk->faddr, ifa->ifname); + // XXXX format + OSPF_TRACE(D_PACKETS, "Non-HELLO packet received from unknown neighbor %R on %s (%I)", + rid, ifa->ifname, sk->faddr); return 1; } - if (ospf_is_v2(p) && !ospf_pkt_checkauth(n, ifa, pkt, size)) - { - log(L_ERR "%s%I - authentication failed", mesg, sk->faddr); + /* ospf_pkt_checkauth() has its own error logging */ + if (ospf_is_v2(p) && !ospf_pkt_checkauth(n, ifa, pkt, len)) return 1; - } switch (pkt->type) { @@ -484,10 +428,15 @@ ospf_rx_hook(sock *sk, int size) break; default: - log(L_ERR "%s%I - wrong type %u", mesg, sk->faddr, pkt->type); - return 1; + DROP("invalid packet type", pkt->type); }; return 1; + +drop: + LOG_PKT("Bad packet from %I via %s - %s (%u)", + sk->faddr, ifa->ifname, err_dsc, err_val); + + return 1; } /* diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 16453b87..08f90b49 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -1204,7 +1204,7 @@ ospf_check_vlinks(struct ospf_proto *p) || (ifa->vifa != nhi) || !ipa_equal(ifa->vip, tmp->lb)) { - OSPF_TRACE(D_EVENTS, "Vlink peer %R found", tmp->lsa.id); + OSPF_TRACE(D_EVENTS, "Vlink peer %R found", ifa->vid); ospf_iface_sm(ifa, ISM_DOWN); ifa->vifa = nhi; ifa->addr = nhi->addr; @@ -1831,7 +1831,7 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, bad: /* Probably bug or some race condition, we log it */ - log(L_ERR "Unexpected case in next hop calculation"); + log(L_ERR "%s: Unexpected case in next hop calculation", p->p.name); return NULL; } @@ -1875,8 +1875,8 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, struct mpnh *nhs = calc_next_hop(oa, en, par, pos); if (!nhs) { - log(L_WARN "Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)", - en->lsa_type, en->lsa.id, en->lsa.rt); + log(L_WARN "%s: Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)", + p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt); return; } diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 4e3da0de..15ba013a 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -758,7 +758,7 @@ prepare_rt2_lsa_body(struct ospf_proto *p, struct ospf_area *oa) break; default: - log("Unknown interface type %s", ifa->ifname); + log(L_BUG "OSPF: Unknown interface type"); break; } @@ -855,7 +855,7 @@ prepare_rt3_lsa_body(struct ospf_proto *p, struct ospf_area *oa) break; default: - log("Unknown interface type %s", ifa->ifname); + log(L_BUG "OSPF: Unknown interface type"); break; } diff --git a/proto/rip/auth.c b/proto/rip/auth.c index b7b0611e..5634547a 100644 --- a/proto/rip/auth.c +++ b/proto/rip/auth.c @@ -95,7 +95,7 @@ rip_incoming_authentication( struct proto *p, struct rip_block_auth *block, stru } memcpy(md5sum_packet, tail->md5, 16); - password_cpy(tail->md5, pass->password, 16); + strncpy(tail->md5, pass->password, 16); MD5Init(&ctxt); MD5Update(&ctxt, (char *) packet, ntohs(block->packetlen) + sizeof(struct rip_block_auth) ); @@ -131,7 +131,7 @@ rip_outgoing_authentication( struct proto *p, struct rip_block_auth *block, stru block->mustbeFFFF = 0xffff; switch (P_CF->authtype) { case AT_PLAINTEXT: - password_cpy( (char *) (&block->packetlen), passwd->password, 16); + strncpy( (char *) (&block->packetlen), passwd->password, 16); return PACKETLEN(num); case AT_MD5: { @@ -156,7 +156,7 @@ rip_outgoing_authentication( struct proto *p, struct rip_block_auth *block, stru tail->mustbeFFFF = 0xffff; tail->mustbe0001 = 0x0100; - password_cpy(tail->md5, passwd->password, 16); + strncpy(tail->md5, passwd->password, 16); MD5Init(&ctxt); MD5Update(&ctxt, (char *) packet, PACKETLEN(num) + sizeof(struct rip_md5_tail)); MD5Final(tail->md5, &ctxt); From 88a183c6c9a2b86b52f67e87bbc8b7edd32670c6 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 24 Oct 2014 11:11:43 +0200 Subject: [PATCH 06/11] Integrated IP functions. --- conf/cf-lex.l | 18 +- conf/confbase.Y | 2 +- lib/Modules | 7 - lib/ip.c | 376 +++++++++++++++++++++++++++++++-- lib/ip.h | 475 +++++++++++++++++++++++++++++++++++++++--- lib/ipv4.c | 110 ---------- lib/ipv4.h | 116 ----------- lib/ipv6.c | 384 ---------------------------------- lib/ipv6.h | 141 ------------- lib/printf.c | 4 +- lib/socket.h | 2 + proto/bgp/attrs.c | 2 +- proto/bgp/bgp.c | 12 +- proto/bgp/config.Y | 2 +- proto/bgp/packets.c | 8 +- proto/ospf/ospf.h | 33 +-- proto/ospf/packet.c | 3 +- proto/ospf/rt.c | 4 +- proto/ospf/topology.c | 2 +- proto/radv/packets.c | 4 +- proto/radv/radv.h | 3 - proto/rip/rip.c | 10 +- sysdep/bsd/krt-sock.c | 4 +- sysdep/unix/io.c | 23 ++ sysdep/unix/unix.h | 18 +- 25 files changed, 874 insertions(+), 889 deletions(-) delete mode 100644 lib/ipv4.c delete mode 100644 lib/ipv4.h delete mode 100644 lib/ipv6.c delete mode 100644 lib/ipv6.h diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 35b590bb..b3e13311 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -124,22 +124,24 @@ include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*; } {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ { + ip4_addr a; + if (!ip4_pton(yytext, &a)) + cf_error("Invalid IPv4 address %s", yytext); + #ifdef IPV6 - if (ipv4_pton_u32(yytext, &cf_lval.i32)) - return RTRID; - cf_error("Invalid IPv4 address %s", yytext); + cf_lval.i32 = ip4_to_u32(a); + return RTRID; #else - if (ip_pton(yytext, &cf_lval.a)) - return IPA; - cf_error("Invalid IP address %s", yytext); + cf_lval.a = ipa_from_ip4(a); + return IPA; #endif } ({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) { #ifdef IPV6 - if (ip_pton(yytext, &cf_lval.a)) + if (ipa_pton(yytext, &cf_lval.a)) return IPA; - cf_error("Invalid IP address %s", yytext); + cf_error("Invalid IPv6 address %s", yytext); #else cf_error("This is an IPv4 router, therefore IPv6 addresses are not supported"); #endif diff --git a/conf/confbase.Y b/conf/confbase.Y index 49831b1a..16a493e9 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -187,7 +187,7 @@ pxlen: $$ = $2; } | ':' ipa { - $$ = ipa_mklen($2); + $$ = ipa_masklen($2); if ($$ < 0) cf_error("Invalid netmask %I", $2); } ; diff --git a/lib/Modules b/lib/Modules index 7131f0b2..7254df2d 100644 --- a/lib/Modules +++ b/lib/Modules @@ -3,13 +3,6 @@ bitops.c bitops.h ip.h ip.c -#ifdef IPV6 -ipv6.c -ipv6.h -#else -ipv4.c -ipv4.h -#endif lists.c lists.h md5.c diff --git a/lib/ip.c b/lib/ip.c index aa61553e..01edf0d5 100644 --- a/lib/ip.c +++ b/lib/ip.c @@ -1,14 +1,11 @@ /* - * BIRD Library -- IP address routines common for IPv4 and IPv6 + * BIRD Library -- IP address functions * * (c) 1998--2000 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ -#include "nest/bird.h" -#include "lib/ip.h" - /** * DOC: IP addresses * @@ -18,6 +15,333 @@ * they must be manipulated using the following functions and macros. */ +#include + +#include "nest/bird.h" +#include "lib/ip.h" + + +int +ip6_compare(ip6_addr a, ip6_addr b) +{ + int i; + for (i=0; i<4; i++) + if (a.addr[i] > b.addr[i]) + return 1; + else if (a.addr[i] < b.addr[i]) + return -1; + return 0; +} + +ip6_addr +ip6_mkmask(uint n) +{ + ip6_addr a; + int i; + + for (i=0; i<4; i++) + { + if (!n) + a.addr[i] = 0; + else if (n >= 32) + { + a.addr[i] = ~0; + n -= 32; + } + else + { + a.addr[i] = u32_mkmask(n); + n = 0; + } + } + + return a; +} + +int +ip6_masklen(ip6_addr *a) +{ + int i, j, n; + + for (i=0, n=0; i<4; i++, n+=32) + if (a->addr[i] != ~0U) + { + j = u32_masklen(a->addr[i]); + if (j < 0) + return j; + n += j; + while (++i < 4) + if (a->addr[i]) + return -1; + break; + } + + return n; +} + +int +ip4_classify(ip4_addr ad) +{ + u32 a = _I(ad); + u32 b = a >> 24U; + + if (b && b <= 0xdf) + { + if (b == 0x7f) + return IADDR_HOST | SCOPE_HOST; + else if ((b == 0x0a) || + ((a & 0xffff0000) == 0xc0a80000) || + ((a & 0xfff00000) == 0xac100000)) + return IADDR_HOST | SCOPE_SITE; + else + return IADDR_HOST | SCOPE_UNIVERSE; + } + + if (b >= 0xe0 && b <= 0xef) + return IADDR_MULTICAST | SCOPE_UNIVERSE; + + if (a == 0xffffffff) + return IADDR_BROADCAST | SCOPE_LINK; + + return IADDR_INVALID; +} + +int +ip6_classify(ip6_addr *a) +{ + u32 x = a->addr[0]; + + if ((x & 0xe0000000) == 0x20000000) /* 2000::/3 Aggregatable Global Unicast Address */ + return IADDR_HOST | SCOPE_UNIVERSE; + if ((x & 0xffc00000) == 0xfe800000) /* fe80::/10 Link-Local Address */ + return IADDR_HOST | SCOPE_LINK; + if ((x & 0xffc00000) == 0xfec00000) /* fec0::/10 Site-Local Address */ + return IADDR_HOST | SCOPE_SITE; + if ((x & 0xfe000000) == 0xfc000000) /* fc00::/7 Unique Local Unicast Address (RFC 4193) */ + return IADDR_HOST | SCOPE_SITE; + if ((x & 0xff000000) == 0xff000000) /* ff00::/8 Multicast Address */ + { + uint scope = (x >> 16) & 0x0f; + switch (scope) + { + case 1: return IADDR_MULTICAST | SCOPE_HOST; + case 2: return IADDR_MULTICAST | SCOPE_LINK; + case 5: return IADDR_MULTICAST | SCOPE_SITE; + case 8: return IADDR_MULTICAST | SCOPE_ORGANIZATION; + case 14: return IADDR_MULTICAST | SCOPE_UNIVERSE; + default: return IADDR_MULTICAST | SCOPE_UNDEFINED; + } + } + + if (!x && !a->addr[1]) + { + u32 a2 = a->addr[2]; + u32 a3 = a->addr[3]; + + if (a2 == 0 && a3 == 1) + return IADDR_HOST | SCOPE_HOST; /* Loopback address */ + if (a2 == 0) + return ip4_classify(_MI4(a3)); /* IPv4 compatible addresses */ + if (a2 == 0xffff) + return ip4_classify(_MI4(a3)); /* IPv4 mapped addresses */ + + return IADDR_INVALID; + } + + return IADDR_HOST | SCOPE_UNDEFINED; +} + + + +/* + * Conversion of IPv6 address to presentation format and vice versa. + * Heavily inspired by routines written by Paul Vixie for the BIND project + * and of course by RFC 2373. + */ + + +char * +ip4_ntop(ip4_addr a, char *b) +{ + u32 x = _I(a); + return b + bsprintf(b, "%d.%d.%d.%d", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); +} + + +char * +ip6_ntop(ip6_addr a, char *b) +{ + u16 words[8]; + int bestpos, bestlen, curpos, curlen, i; + + /* First of all, preprocess the address and find the longest run of zeros */ + bestlen = bestpos = curpos = curlen = 0; + for (i=0; i<8; i++) + { + u32 x = a.addr[i/2]; + words[i] = ((i%2) ? x : (x >> 16)) & 0xffff; + if (words[i]) + curlen = 0; + else + { + if (!curlen) + curpos = i; + curlen++; + if (curlen > bestlen) + { + bestpos = curpos; + bestlen = curlen; + } + } + } + + if (bestlen < 2) + bestpos = -1; + + /* Is it an encapsulated IPv4 address? */ + if (!bestpos && ((bestlen == 5 && a.addr[2] == 0xffff) || (bestlen == 6))) + { + u32 x = a.addr[3]; + b += bsprintf(b, "::%s%d.%d.%d.%d", + a.addr[2] ? "ffff:" : "", + (x >> 24) & 0xff, + (x >> 16) & 0xff, + (x >> 8) & 0xff, + x & 0xff); + return b; + } + + /* Normal IPv6 formatting, compress the largest sequence of zeros */ + for (i=0; i<8; i++) + { + if (i == bestpos) + { + i += bestlen - 1; + *b++ = ':'; + if (i == 7) + *b++ = ':'; + } + else + { + if (i) + *b++ = ':'; + b += bsprintf(b, "%x", words[i]); + } + } + *b = 0; + return b; +} + +int +ip4_pton(char *a, ip4_addr *o) +{ + int i; + unsigned long int l; + u32 ia = 0; + + i=4; + while (i--) + { + char *d, *c = strchr(a, '.'); + if (!c != !i) + return 0; + l = strtoul(a, &d, 10); + if (d != c && *d || l > 255) + return 0; + ia = (ia << 8) | l; + if (c) + c++; + a = c; + } + *o = ip4_from_u32(ia); + return 1; +} + +int +ip6_pton(char *a, ip6_addr *o) +{ + u16 words[8]; + int i, j, k, l, hfil; + char *start; + + if (a[0] == ':') /* Leading :: */ + { + if (a[1] != ':') + return 0; + a++; + } + + hfil = -1; + i = 0; + while (*a) + { + if (*a == ':') /* :: */ + { + if (hfil >= 0) + return 0; + + hfil = i; + a++; + continue; + } + + j = 0; + l = 0; + start = a; + for (;;) + { + if (*a >= '0' && *a <= '9') + k = *a++ - '0'; + else if (*a >= 'A' && *a <= 'F') + k = *a++ - 'A' + 10; + else if (*a >= 'a' && *a <= 'f') + k = *a++ - 'a' + 10; + else + break; + + j = (j << 4) + k; + if (j >= 0x10000 || ++l > 4) + return 0; + } + + if (*a == ':' && a[1]) + a++; + else if (*a == '.' && (i == 6 || i < 6 && hfil >= 0)) + { /* Embedded IPv4 address */ + ip4_addr x; + if (!ip4_pton(start, &x)) + return 0; + words[i++] = _I(x) >> 16; + words[i++] = _I(x); + break; + } + else if (*a) + return 0; + + if (i >= 8) + return 0; + + words[i++] = j; + } + + /* Replace :: with an appropriate number of zeros */ + if (hfil >= 0) + { + j = 8 - i; + for (i=7; i-j >= hfil; i--) + words[i] = words[i-j]; + for (; i>=hfil; i--) + words[i] = 0; + } + + /* Convert the address to ip6_addr format */ + for (i=0; i<4; i++) + o->addr[i] = (words[2*i] << 16) | words[2*i+1]; + + return 1; +} + + /** * ip_scope_text - get textual representation of address scope * @scope: scope (%SCOPE_xxx) @@ -25,7 +349,7 @@ * Returns a pointer to a textual name of the scope given. */ char * -ip_scope_text(unsigned scope) +ip_scope_text(uint scope) { static char *scope_table[] = { "host", "link", "site", "org", "univ", "undef" }; @@ -35,6 +359,23 @@ ip_scope_text(unsigned scope) return scope_table[scope]; } +ip4_addr +ip4_class_mask(ip4_addr ad) +{ + u32 m, a = _I(ad); + + if (a < 0x80000000) + m = 0xff000000; + else if (a < 0xc0000000) + m = 0xffff0000; + else + m = 0xffffff00; + if (a & ~m) + m = 0xffffffff; + + return _MI4(m); +} + #if 0 /** * ipa_equal - compare two IP addresses for equality @@ -102,14 +443,14 @@ ip_addr ipa_not(ip_addr x) { DUMMY } ip_addr ipa_mkmask(int x) { DUMMY } /** - * ipa_mkmask - calculate netmask length + * ipa_masklen - calculate netmask length * @x: IP address * * This function checks whether @x represents a valid netmask and * returns the size of the associate network prefix or -1 for invalid * mask. */ -int ipa_mklen(ip_addr x) { DUMMY } +int ipa_masklen(ip_addr x) { DUMMY } /** * ipa_hash - hash IP addresses @@ -151,8 +492,8 @@ void ipa_ntoh(ip_addr x) { DUMMY } int ipa_classify(ip_addr x) { DUMMY } /** - * ipa_class_mask - guess netmask according to address class - * @x: IP address + * ip4_class_mask - guess netmask according to address class + * @x: IPv4 address * * This function (available in IPv4 version only) returns a * network mask according to the address class of @x. Although @@ -160,7 +501,7 @@ int ipa_classify(ip_addr x) { DUMMY } * routing protocols transferring no prefix lengths nor netmasks * and this function could be useful to them. */ -ip_addr ipa_class_mask(ip_addr x) { DUMMY } +ip4_addr ip4_class_mask(ip4_addr x) { DUMMY } /** * ipa_from_u32 - convert IPv4 address to an integer @@ -193,7 +534,7 @@ ip_addr ipa_to_u32(u32 x) { DUMMY } int ipa_compare(ip_addr x, ip_addr y) { DUMMY } /** - * ipa_build - build an IPv6 address from parts + * ipa_build6 - build an IPv6 address from parts * @a1: part #1 * @a2: part #2 * @a3: part #3 @@ -203,18 +544,7 @@ int ipa_compare(ip_addr x, ip_addr y) { DUMMY } * address. It's used for example when a protocol wants to bind its * socket to a hard-wired multicast address. */ -ip_addr ipa_build(u32 a1, u32 a2, u32 a3, u32 a4) { DUMMY } - -/** - * ipa_absolutize - convert link scope IPv6 address to universe scope - * @x: link scope IPv6 address - * @y: universe scope IPv6 prefix of the interface - * - * This function combines a link-scope IPv6 address @x with the universe - * scope prefix @x of the network assigned to an interface to get a - * universe scope form of @x. - */ -ip_addr ipa_absolutize(ip_addr x, ip_addr y) { DUMMY } +ip_addr ipa_build6(u32 a1, u32 a2, u32 a3, u32 a4) { DUMMY } /** * ip_ntop - convert IP address to textual representation diff --git a/lib/ip.h b/lib/ip.h index 023c1064..45e073d9 100644 --- a/lib/ip.h +++ b/lib/ip.h @@ -9,59 +9,474 @@ #ifndef _BIRD_IP_H_ #define _BIRD_IP_H_ -#ifndef IPV6 -#include "ipv4.h" +#include "lib/endian.h" +#include "lib/string.h" +#include "lib/bitops.h" +#include "lib/unaligned.h" + + +#define IP4_OSPF_ALL_ROUTERS ipa_build4(224, 0, 0, 5) +#define IP4_OSPF_DES_ROUTERS ipa_build4(224, 0, 0, 6) + +#define IP6_ALL_NODES ipa_build6(0xFF020000, 0, 0, 1) +#define IP6_ALL_ROUTERS ipa_build6(0xFF020000, 0, 0, 2) +#define IP6_OSPF_ALL_ROUTERS ipa_build6(0xFF020000, 0, 0, 5) +#define IP6_OSPF_DES_ROUTERS ipa_build6(0xFF020000, 0, 0, 6) +#define IP6_RIP_ROUTERS ipa_build6(0xFF020000, 0, 0, 9) + +#define IP4_NONE _MI4(0) +#define IP6_NONE _MI6(0,0,0,0) + +#define IP4_MIN_MTU 576 +#define IP6_MIN_MTU 1280 + +#define IP_PREC_INTERNET_CONTROL 0xc0 + + +#ifdef IPV6 +#define MAX_PREFIX_LENGTH 128 +#define BITS_PER_IP_ADDRESS 128 +#define STD_ADDRESS_P_LENGTH 39 +#define SIZE_OF_IP_HEADER 40 #else -#include "ipv6.h" +#define MAX_PREFIX_LENGTH 32 +#define BITS_PER_IP_ADDRESS 32 +#define STD_ADDRESS_P_LENGTH 15 +#define SIZE_OF_IP_HEADER 24 +#endif + + +#ifdef DEBUGGING + +typedef struct ip4_addr { + u32 addr; +} ip4_addr; + +#define _MI4(x) ((struct ip4_addr) { x }) +#define _I(x) (x).addr + +#else + +typedef u32 ip4_addr; + +#define _MI4(x) (x) +#define _I(x) (x) + +#endif + + +typedef struct ip6_addr { + u32 addr[4]; +} ip6_addr; + +#define _MI6(a,b,c,d) ((struct ip6_addr) {{ a, b, c, d }}) +#define _I0(a) ((a).addr[0]) +#define _I1(a) ((a).addr[1]) +#define _I2(a) ((a).addr[2]) +#define _I3(a) ((a).addr[3]) + + +#ifdef IPV6 + +/* Structure ip_addr may contain both IPv4 and IPv6 addresses */ +typedef ip6_addr ip_addr; +#define IPA_NONE IP6_NONE + +#define ipa_from_ip4(x) _MI6(0,0,0xffff,_I(x)) +#define ipa_from_ip6(x) x +#define ipa_from_u32(x) ipa_from_ip4(ip4_from_u32(x)) + +#define ipa_to_ip4(x) _MI4(_I3(x)) +#define ipa_to_ip6(x) x +#define ipa_to_u32(x) ip4_to_u32(ipa_to_ip4(x)) + +#define ipa_is_ip4(a) ip6_is_v4mapped(a) + +#else + +/* Provisionary ip_addr definition same as ip4_addr */ +typedef ip4_addr ip_addr; +#define IPA_NONE IP4_NONE + +#define ipa_from_ip4(x) x +#define ipa_from_ip6(x) IPA_NONE +#define ipa_from_u32(x) ipa_from_ip4(ip4_from_u32(x)) + +#define ipa_to_ip4(x) x +#define ipa_to_ip6(x) IP6_NONE +#define ipa_to_u32(x) ip4_to_u32(ipa_to_ip4(x)) + +#define ipa_is_ip4(a) 1 + #endif -#define ipa_zero(x) (!ipa_nonzero(x)) -#define ip_is_prefix(a,l) (!ipa_nonzero(ipa_and(a, ipa_not(ipa_mkmask(l))))) -#define ipa_in_net(x,n,p) (ipa_zero(ipa_and(ipa_xor((n),(x)),ipa_mkmask(p)))) -#define net_in_net(n1,l1,n2,l2) (((l1) >= (l2)) && (ipa_zero(ipa_and(ipa_xor((n1),(n2)),ipa_mkmask(l2))))) /* - * ip_classify() returns either a negative number for invalid addresses - * or scope OR'ed together with address type. + * Public constructors */ +#define ip4_from_u32(x) _MI4(x) +#define ip4_to_u32(x) _I(x) + +#define ip4_build(a,b,c,d) _MI4(((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) +#define ip6_build(a,b,c,d) _MI6(a,b,c,d) + +#define ipa_build4(a,b,c,d) ipa_from_ip4(ip4_build(a,b,c,d)) +#define ipa_build6(a,b,c,d) ipa_from_ip6(ip6_build(a,b,c,d)) + + +/* + * Basic algebraic functions + */ + +static inline int ip4_equal(ip4_addr a, ip4_addr b) +{ return _I(a) == _I(b); } + +static inline int ip4_zero(ip4_addr a) +{ return _I(a) == 0; } + +static inline int ip4_nonzero(ip4_addr a) +{ return _I(a) != 0; } + +static inline ip4_addr ip4_and(ip4_addr a, ip4_addr b) +{ return _MI4(_I(a) & _I(b)); } + +static inline ip4_addr ip4_or(ip4_addr a, ip4_addr b) +{ return _MI4(_I(a) | _I(b)); } + +static inline ip4_addr ip4_xor(ip4_addr a, ip4_addr b) +{ return _MI4(_I(a) ^ _I(b)); } + +static inline ip4_addr ip4_not(ip4_addr a) +{ return _MI4(~_I(a)); } + + +static inline int ip6_equal(ip6_addr a, ip6_addr b) +{ return _I0(a) == _I0(b) && _I1(a) == _I1(b) && _I2(a) == _I2(b) && _I3(a) == _I3(b); } + +static inline int ip6_zero(ip6_addr a) +{ return !_I0(a) && !_I1(a) && !_I2(a) && !_I3(a); } + +static inline int ip6_nonzero(ip6_addr a) +{ return _I0(a) || _I1(a) || _I2(a) || _I3(a); } + +static inline ip6_addr ip6_and(ip6_addr a, ip6_addr b) +{ return _MI6(_I0(a) & _I0(b), _I1(a) & _I1(b), _I2(a) & _I2(b), _I3(a) & _I3(b)); } + +static inline ip6_addr ip6_or(ip6_addr a, ip6_addr b) +{ return _MI6(_I0(a) | _I0(b), _I1(a) | _I1(b), _I2(a) | _I2(b), _I3(a) | _I3(b)); } + +static inline ip6_addr ip6_xor(ip6_addr a, ip6_addr b) +{ return _MI6(_I0(a) ^ _I0(b), _I1(a) ^ _I1(b), _I2(a) ^ _I2(b), _I3(a) ^ _I3(b)); } + +static inline ip6_addr ip6_not(ip6_addr a) +{ return _MI6(~_I0(a), ~_I1(a), ~_I2(a), ~_I3(a)); } + + +#ifdef IPV6 +#define ipa_equal(x,y) ip6_equal(x,y) +#define ipa_zero(x) ip6_zero(x) +#define ipa_nonzero(x) ip6_nonzero(x) +#define ipa_and(x,y) ip6_and(x,y) +#define ipa_or(x,y) ip6_or(x,y) +#define ipa_xor(x,y) ip6_xor(x,y) +#define ipa_not(x) ip6_not(x) +#else +#define ipa_equal(x,y) ip4_equal(x,y) +#define ipa_zero(x) ip4_zero(x) +#define ipa_nonzero(x) ip4_nonzero(x) +#define ipa_and(x,y) ip4_and(x,y) +#define ipa_or(x,y) ip4_or(x,y) +#define ipa_xor(x,y) ip4_xor(x,y) +#define ipa_not(x) ip4_not(x) +#endif + + + +#ifdef IPV6 +/* + * A zero address is either a token for invalid/unused, or the prefix of default + * routes. These functions should be used in the second case, where both IPv4 + * and IPv6 zero addresses should be checked. + */ + +static inline int ipa_zero2(ip_addr a) +{ return !_I0(a) && !_I1(a) && ((_I2(a) == 0) || (_I2(a) == 0xffff)) && !_I3(a); } + +static inline int ipa_nonzero2(ip_addr a) +{ return _I0(a) || _I1(a) || ((_I2(a) != 0) && (_I2(a) != 0xffff)) || _I3(a); } + +#else +#define ipa_zero2(x) ip4_zero(x) +#define ipa_nonzero2(x) ip4_nonzero(x) +#endif + + +/* + * Hash and compare functions + */ + +static inline uint ip4_hash(ip4_addr a) +{ + /* Returns a 16-bit value */ + u32 x = _I(a); + x ^= x >> 16; + x ^= x << 10; + return x & 0xffff; +} + +static inline u32 ip4_hash32(ip4_addr a) +{ + /* Returns a 32-bit value, although low-order bits are not mixed */ + u32 x = _I(a); + x ^= x << 16; + x ^= x << 12; + return x; +} + +static inline uint ip6_hash(ip6_addr a) +{ + /* Returns a 16-bit hash key */ + u32 x = _I0(a) ^ _I1(a) ^ _I2(a) ^ _I3(a); + return (x ^ (x >> 16) ^ (x >> 8)) & 0xffff; +} + +static inline u32 ip6_hash32(ip6_addr a) +{ + /* Returns a 32-bit hash key, although low-order bits are not mixed */ + u32 x = _I0(a) ^ _I1(a) ^ _I2(a) ^ _I3(a); + return x ^ (x << 16) ^ (x << 24); +} + +static inline int ip4_compare(ip4_addr a, ip4_addr b) +{ return (_I(a) > _I(b)) - (_I(a) < _I(b)); } + +int ip6_compare(ip6_addr a, ip6_addr b); + + +#ifdef IPV6 +#define ipa_hash(x) ip6_hash(x) +#define ipa_hash32(x) ip6_hash32(x) +#define ipa_compare(x,y) ip6_compare(x,y) +#else +#define ipa_hash(x) ip4_hash(x) +#define ipa_hash32(x) ip4_hash32(x) +#define ipa_compare(x,y) ip4_compare(x,y) +#endif + + +/* + * IP address classification + */ + +/* Address class */ #define IADDR_INVALID -1 #define IADDR_SCOPE_MASK 0xfff #define IADDR_HOST 0x1000 #define IADDR_BROADCAST 0x2000 #define IADDR_MULTICAST 0x4000 +/* Address scope */ +#define SCOPE_HOST 0 +#define SCOPE_LINK 1 +#define SCOPE_SITE 2 +#define SCOPE_ORGANIZATION 3 +#define SCOPE_UNIVERSE 4 +#define SCOPE_UNDEFINED 5 + +int ip4_classify(ip4_addr ad); +int ip6_classify(ip6_addr *a); + +static inline int ip6_is_link_local(ip6_addr a) +{ return (_I0(a) & 0xffc00000) == 0xfe800000; } + +static inline int ip6_is_v4mapped(ip6_addr a) +{ return _I0(a) == 0 && _I1(a) == 0 && _I2(a) == 0xffff; } + +#ifdef IPV6 +#define ipa_classify(x) ip6_classify(&(x)) +#define ipa_is_link_local(x) ip6_is_link_local(x) +#else +#define ipa_classify(x) ip4_classify(x) +#define ipa_is_link_local(x) 0 +#endif + +static inline int ipa_classify_net(ip_addr a) +{ return ipa_zero2(a) ? (IADDR_HOST | SCOPE_UNIVERSE) : ipa_classify(a); } + + /* - * Address scope + * Miscellaneous IP prefix manipulation */ -#define SCOPE_HOST 0 -#define SCOPE_LINK 1 -#define SCOPE_SITE 2 -#define SCOPE_ORGANIZATION 3 -#define SCOPE_UNIVERSE 4 -#define SCOPE_UNDEFINED 5 +static inline ip4_addr ip4_mkmask(uint n) +{ return _MI4(u32_mkmask(n)); } + +static inline int ip4_masklen(ip4_addr a) +{ return u32_masklen(_I(a)); } + +ip6_addr ip6_mkmask(uint n); +int ip6_masklen(ip6_addr *a); + +/* ipX_pxlen() requires that x != y */ +static inline uint ip4_pxlen(ip4_addr a, ip4_addr b) +{ return 31 - u32_log2(_I(a) ^ _I(b)); } + +static inline uint ip6_pxlen(ip6_addr a, ip6_addr b) +{ + int i = 0; + i += (a.addr[i] == b.addr[i]); + i += (a.addr[i] == b.addr[i]); + i += (a.addr[i] == b.addr[i]); + i += (a.addr[i] == b.addr[i]); + return 32 * i + 31 - u32_log2(a.addr[i] ^ b.addr[i]); +} + +static inline u32 ip4_getbit(ip4_addr a, uint pos) +{ return _I(a) & (0x80000000 >> pos); } + +static inline u32 ip6_getbit(ip6_addr a, uint pos) +{ return a.addr[pos / 32] & (0x80000000 >> (pos % 32)); } + +static inline ip4_addr ip4_opposite_m1(ip4_addr a) +{ return _MI4(_I(a) ^ 1); } + +static inline ip4_addr ip4_opposite_m2(ip4_addr a) +{ return _MI4(_I(a) ^ 3); } + +static inline ip6_addr ip6_opposite_m1(ip6_addr a) +{ return _MI6(_I0(a), _I1(a), _I2(a), _I3(a) ^ 1); } + +static inline ip6_addr ip6_opposite_m2(ip6_addr a) +{ return _MI6(_I0(a), _I1(a), _I2(a), _I3(a) ^ 3); } + +ip4_addr ip4_class_mask(ip4_addr ad); + +#ifdef IPV6 +#define ipa_mkmask(x) ip6_mkmask(x) +#define ipa_masklen(x) ip6_masklen(&x) +#define ipa_pxlen(x,y) ip6_pxlen(x,y) +#define ipa_getbit(x,n) ip6_getbit(x,n) +#define ipa_opposite_m1(x) ip6_opposite_m1(x) +#define ipa_opposite_m2(x) ip6_opposite_m2(x) +#else +#define ipa_mkmask(x) ip4_mkmask(x) +#define ipa_masklen(x) ip4_masklen(x) +#define ipa_pxlen(x,y) ip4_pxlen(x,y) +#define ipa_getbit(x,n) ip4_getbit(x,n) +#define ipa_opposite_m1(x) ip4_opposite_m1(x) +#define ipa_opposite_m2(x) ip4_opposite_m2(x) +#endif + + +/* + * Host/network order conversions + */ + +static inline ip4_addr ip4_hton(ip4_addr a) +{ return _MI4(htonl(_I(a))); } + +static inline ip4_addr ip4_ntoh(ip4_addr a) +{ return _MI4(ntohl(_I(a))); } + +static inline ip6_addr ip6_hton(ip6_addr a) +{ return _MI6(htonl(_I0(a)), htonl(_I1(a)), htonl(_I2(a)), htonl(_I3(a))); } + +static inline ip6_addr ip6_ntoh(ip6_addr a) +{ return _MI6(ntohl(_I0(a)), ntohl(_I1(a)), ntohl(_I2(a)), ntohl(_I3(a))); } + +#ifdef IPV6 +#define ipa_hton(x) x = ip6_hton(x) +#define ipa_ntoh(x) x = ip6_ntoh(x) +#else +#define ipa_hton(x) x = ip4_hton(x) +#define ipa_ntoh(x) x = ip4_ntoh(x) +#endif + + +/* + * Unaligned data access (in network order) + */ + +static inline ip4_addr get_ip4(void *buf) +{ + return _MI4(get_u32(buf)); +} + +static inline ip6_addr get_ip6(void *buf) +{ + ip6_addr a; + memcpy(&a, buf, 16); + return ip6_ntoh(a); +} + +static inline void * put_ip4(void *buf, ip4_addr a) +{ + put_u32(buf, _I(a)); + return buf+4; +} + +static inline void * put_ip6(void *buf, ip6_addr a) +{ + a = ip6_hton(a); + memcpy(buf, &a, 16); + return buf+16; +} + +// XXXX these functions must be redesigned or removed +#ifdef IPV6 +#define get_ipa(x) get_ip6(x) +#define put_ipa(x,y) put_ip6(x,y) +#else +#define get_ipa(x) get_ip4(x) +#define put_ipa(x,y) put_ip4(x,y) +#endif + + +/* + * Binary/text form conversions + */ + +char *ip4_ntop(ip4_addr a, char *b); +char *ip6_ntop(ip6_addr a, char *b); + +static inline char * ip4_ntox(ip4_addr a, char *b) +{ return b + bsprintf(b, "%08x", _I(a)); } + +static inline char * ip6_ntox(ip6_addr a, char *b) +{ return b + bsprintf(b, "%08x.%08x.%08x.%08x", _I0(a), _I1(a), _I2(a), _I3(a)); } + +int ip4_pton(char *a, ip4_addr *o); +int ip6_pton(char *a, ip6_addr *o); + +// XXXX these functions must be redesigned or removed +#ifdef IPV6 +#define ipa_ntop(x,y) ip6_ntop(x,y) +#define ipa_ntox(x,y) ip6_ntox(x,y) +#define ipa_pton(x,y) ip6_pton(x,y) +#else +#define ipa_ntop(x,y) ip4_ntop(x,y) +#define ipa_ntox(x,y) ip4_ntox(x,y) +#define ipa_pton(x,y) ip4_pton(x,y) +#endif + + +/* + * Miscellaneous + */ + +// XXXX review this + +#define ip_is_prefix(a,l) (!ipa_nonzero(ipa_and(a, ipa_not(ipa_mkmask(l))))) +#define ipa_in_net(x,n,p) (ipa_zero(ipa_and(ipa_xor((n),(x)),ipa_mkmask(p)))) +#define net_in_net(n1,l1,n2,l2) (((l1) >= (l2)) && (ipa_zero(ipa_and(ipa_xor((n1),(n2)),ipa_mkmask(l2))))) char *ip_scope_text(unsigned); -/* - * Network prefixes - */ - struct prefix { ip_addr addr; unsigned int len; }; -static inline int ipa_classify_net(ip_addr a) -{ return ipa_zero(a) ? (IADDR_HOST | SCOPE_UNIVERSE) : ipa_classify(a); } - -/* - * Conversions between internal and string representation - */ - -char *ip_ntop(ip_addr a, char *); -char *ip_ntox(ip_addr a, char *); -int ip_pton(char *a, ip_addr *o); #endif diff --git a/lib/ipv4.c b/lib/ipv4.c deleted file mode 100644 index 751351ca..00000000 --- a/lib/ipv4.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * BIRD Library -- IPv4 Address Manipulation Functions - * - * (c) 1998 Martin Mares - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include - -#include "nest/bird.h" -#include "lib/ip.h" -#include "lib/string.h" - -int -ipv4_classify(u32 a) -{ - u32 b = a >> 24U; - - if (b && b <= 0xdf) - { - if (b == 0x7f) - return IADDR_HOST | SCOPE_HOST; - else if (b == 0x0a || - (a & 0xffff0000) == 0xc0a80000 || - (a & 0xfff00000) == 0xac100000) - return IADDR_HOST | SCOPE_SITE; - else - return IADDR_HOST | SCOPE_UNIVERSE; - } - if (b >= 0xe0 && b <= 0xef) - return IADDR_MULTICAST | SCOPE_UNIVERSE; - if (a == 0xffffffff) - return IADDR_BROADCAST | SCOPE_LINK; - return IADDR_INVALID; -} - -char * -ip_ntop(ip_addr a, char *b) -{ - u32 x = _I(a); - - return b + bsprintf(b, "%d.%d.%d.%d", - ((x >> 24) & 0xff), - ((x >> 16) & 0xff), - ((x >> 8) & 0xff), - (x & 0xff)); -} - -char * -ip_ntox(ip_addr a, char *b) -{ - return b + bsprintf(b, "%08x", _I(a)); -} - -u32 -ipv4_class_mask(u32 a) -{ - u32 m; - - if (a < 0x80000000) - m = 0xff000000; - else if (a < 0xc0000000) - m = 0xffff0000; - else - m = 0xffffff00; - while (a & ~m) - m |= m >> 1; - return m; -} - -int -ip_pton(char *a, ip_addr *o) -{ - int i; - unsigned long int l; - u32 ia = 0; - - i=4; - while (i--) - { - char *d, *c = strchr(a, '.'); - if (!c != !i) - return 0; - l = strtoul(a, &d, 10); - if (d != c && *d || l > 255) - return 0; - ia = (ia << 8) | l; - if (c) - c++; - a = c; - } - *o = ipa_from_u32(ia); - return 1; -} - -byte * -ipv4_skip_header(byte *pkt, int *len) -{ - int l = *len; - int q; - - if (l < 20 || (*pkt & 0xf0) != 0x40) - return NULL; - q = (*pkt & 0x0f) * 4; - if (q > l) - return NULL; - *len -= q; - return pkt + q; -} diff --git a/lib/ipv4.h b/lib/ipv4.h deleted file mode 100644 index 7fbdf2eb..00000000 --- a/lib/ipv4.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * BIRD -- IP Addresses et Cetera for IPv4 - * - * (c) 1998--1999 Martin Mares - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#ifndef _BIRD_IPV4_H_ -#define _BIRD_IPV4_H_ - -#include "lib/endian.h" -#include "lib/bitops.h" -#include "lib/unaligned.h" - -#ifdef DEBUGGING - -/* - * Use the structural representation when you want to make sure - * nobody unauthorized attempts to handle ip_addr as number. - */ - -typedef struct ipv4_addr { - u32 addr; -} ip_addr; - -#define _I(x) (x).addr -#define _MI(x) ((struct ipv4_addr) { x }) - -#else - -typedef u32 ip_addr; - -#define _I(x) (x) -#define _MI(x) (x) - -#endif - -#define MAX_PREFIX_LENGTH 32 -#define BITS_PER_IP_ADDRESS 32 -#define STD_ADDRESS_P_LENGTH 15 -#define SIZE_OF_IP_HEADER 24 - -#define IPA_NONE (_MI(0)) - -#define ipa_equal(x,y) (_I(x) == _I(y)) -#define ipa_nonzero(x) _I(x) -#define ipa_and(x,y) _MI(_I(x) & _I(y)) -#define ipa_or(x,y) _MI(_I(x) | _I(y)) -#define ipa_xor(x,y) _MI(_I(x) ^ _I(y)) -#define ipa_not(x) _MI(~_I(x)) -#define ipa_mkmask(x) _MI(u32_mkmask(x)) -#define ipa_mklen(x) u32_masklen(_I(x)) -#define ipa_hash(x) ipv4_hash(_I(x)) -#define ipa_hash32(x) ipv4_hash32(_I(x)) -#define ipa_hton(x) x = _MI(htonl(_I(x))) -#define ipa_ntoh(x) x = _MI(ntohl(_I(x))) -#define ipa_classify(x) ipv4_classify(_I(x)) -#define ipa_has_link_scope(x) ipv4_has_link_scope(_I(x)) -#define ipa_opposite_m1(x) _MI(_I(x) ^ 1) -#define ipa_opposite_m2(x) _MI(_I(x) ^ 3) -#define ipa_class_mask(x) _MI(ipv4_class_mask(_I(x))) -#define ipa_from_u32(x) _MI(x) -#define ipa_to_u32(x) _I(x) -#define ipa_compare(x,y) ipv4_compare(_I(x),_I(y)) -/* ipa_pxlen() requires that x != y */ -#define ipa_pxlen(x, y) ipv4_pxlen(_I(x), _I(y)) -#define ipa_getbit(x, y) (_I(x) & (0x80000000 >> (y))) -#define ipa_put_addr(x, y) ipv4_put_addr(x, y) - -#define ip_skip_header(x, y) ipv4_skip_header(x, y) - -int ipv4_classify(u32); -u32 ipv4_class_mask(u32); -byte *ipv4_skip_header(byte *, int *); - -static inline int ipv4_has_link_scope(u32 a UNUSED) -{ - return 0; -} - -static inline unsigned ipv4_hash(u32 a) -{ - /* Returns a 16-bit value */ - a ^= a >> 16; - a ^= a << 10; - return a & 0xffff; -} - -static inline u32 ipv4_hash32(u32 a) -{ - /* Returns a 32-bit value, although low-order bits are not mixed */ - a ^= a << 16; - a ^= a << 12; - return a; -} - -static inline int ipv4_compare(u32 x, u32 y) -{ - return (x > y) - (x < y); -} - -static inline u32 ipv4_pxlen(u32 a, u32 b) -{ - return 31 - u32_log2(a ^ b); -} - -static inline byte * ipv4_put_addr(byte *buf, ip_addr a) -{ - put_u32(buf, _I(a)); - return buf+4; -} - -#define IP_PREC_INTERNET_CONTROL 0xc0 - -#endif diff --git a/lib/ipv6.c b/lib/ipv6.c deleted file mode 100644 index 623f6328..00000000 --- a/lib/ipv6.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - * BIRD Library -- IPv6 Address Manipulation Functions - * - * (c) 1999 Martin Mares - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include - -#include "nest/bird.h" -#include "lib/ip.h" -#include "lib/bitops.h" -#include "lib/endian.h" -#include "lib/string.h" - -/* - * See RFC 2373 for explanation of IPv6 addressing issues. - */ - -ip_addr -ipv6_mkmask(unsigned n) -{ - ip_addr a; - int i; - - for(i=0; i<4; i++) - { - if (!n) - a.addr[i] = 0; - else if (n >= 32) - { - a.addr[i] = ~0; - n -= 32; - } - else - { - a.addr[i] = u32_mkmask(n); - n = 0; - } - } - return a; -} - -unsigned -ipv6_mklen(ip_addr *a) -{ - int i, j, n; - - for(i=0, n=0; i<4; i++, n+=32) - if (a->addr[i] != ~0U) - { - j = u32_masklen(a->addr[i]); - if (j < 0) - return j; - n += j; - while (++i < 4) - if (a->addr[i]) - return -1; - break; - } - return n; -} - -int -ipv6_classify(ip_addr *a) -{ - u32 x = a->addr[0]; - - if ((x & 0xe0000000) == 0x20000000) /* 2000::/3 Aggregatable Global Unicast Address */ - return IADDR_HOST | SCOPE_UNIVERSE; - if ((x & 0xffc00000) == 0xfe800000) /* fe80::/10 Link-Local Address */ - return IADDR_HOST | SCOPE_LINK; - if ((x & 0xffc00000) == 0xfec00000) /* fec0::/10 Site-Local Address */ - return IADDR_HOST | SCOPE_SITE; - if ((x & 0xfe000000) == 0xfc000000) /* fc00::/7 Unique Local Unicast Address (RFC 4193) */ - return IADDR_HOST | SCOPE_SITE; - if ((x & 0xff000000) == 0xff000000) /* ff00::/8 Multicast Address */ - { - unsigned int scope = (x >> 16) & 0x0f; - switch (scope) - { - case 1: return IADDR_MULTICAST | SCOPE_HOST; - case 2: return IADDR_MULTICAST | SCOPE_LINK; - case 5: return IADDR_MULTICAST | SCOPE_SITE; - case 8: return IADDR_MULTICAST | SCOPE_ORGANIZATION; - case 14: return IADDR_MULTICAST | SCOPE_UNIVERSE; - default: return IADDR_MULTICAST | SCOPE_UNDEFINED; - } - } - if (!x && !a->addr[1] && !a->addr[2]) - { - u32 y = a->addr[3]; - if (y == 1) - return IADDR_HOST | SCOPE_HOST; /* Loopback address */ - /* IPv4 compatible addresses */ - if (y >= 0x7f000000 && y < 0x80000000) - return IADDR_HOST | SCOPE_HOST; - if ((y & 0xff000000) == 0x0a000000 || - (y & 0xffff0000) == 0xc0a80000 || - (y & 0xfff00000) == 0xac100000) - return IADDR_HOST | SCOPE_SITE; - if (y >= 0x01000000 && y < 0xe0000000) - return IADDR_HOST | SCOPE_UNIVERSE; - } - return IADDR_HOST | SCOPE_UNDEFINED; -} - -void -ipv6_hton(ip_addr *a) -{ - int i; - - for(i=0; i<4; i++) - a->addr[i] = htonl(a->addr[i]); -} - -void -ipv6_ntoh(ip_addr *a) -{ - int i; - - for(i=0; i<4; i++) - a->addr[i] = ntohl(a->addr[i]); -} - -int -ipv6_compare(ip_addr X, ip_addr Y) -{ - int i; - ip_addr *x = &X; - ip_addr *y = &Y; - - for(i=0; i<4; i++) - if (x->addr[i] > y->addr[i]) - return 1; - else if (x->addr[i] < y->addr[i]) - return -1; - return 0; -} - -/* - * Conversion of IPv6 address to presentation format and vice versa. - * Heavily inspired by routines written by Paul Vixie for the BIND project - * and of course by RFC 2373. - */ - -char * -ip_ntop(ip_addr a, char *b) -{ - u16 words[8]; - int bestpos, bestlen, curpos, curlen, i; - - /* First of all, preprocess the address and find the longest run of zeros */ - bestlen = bestpos = curpos = curlen = 0; - for(i=0; i<8; i++) - { - u32 x = a.addr[i/2]; - words[i] = ((i%2) ? x : (x >> 16)) & 0xffff; - if (words[i]) - curlen = 0; - else - { - if (!curlen) - curpos = i; - curlen++; - if (curlen > bestlen) - { - bestpos = curpos; - bestlen = curlen; - } - } - } - if (bestlen < 2) - bestpos = -1; - - /* Is it an encapsulated IPv4 address? */ - if (!bestpos && - (bestlen == 5 && a.addr[2] == 0xffff || - bestlen == 6)) - { - u32 x = a.addr[3]; - b += bsprintf(b, "::%s%d.%d.%d.%d", - a.addr[2] ? "ffff:" : "", - ((x >> 24) & 0xff), - ((x >> 16) & 0xff), - ((x >> 8) & 0xff), - (x & 0xff)); - return b; - } - - /* Normal IPv6 formatting, compress the largest sequence of zeros */ - for(i=0; i<8; i++) - { - if (i == bestpos) - { - i += bestlen - 1; - *b++ = ':'; - if (i == 7) - *b++ = ':'; - } - else - { - if (i) - *b++ = ':'; - b += bsprintf(b, "%x", words[i]); - } - } - *b = 0; - return b; -} - -char * -ip_ntox(ip_addr a, char *b) -{ - int i; - - for(i=0; i<4; i++) - { - if (i) - *b++ = '.'; - b += bsprintf(b, "%08x", a.addr[i]); - } - return b; -} - -int -ipv4_pton_u32(char *a, u32 *o) -{ - int i; - unsigned long int l; - u32 ia = 0; - - i=4; - while (i--) - { - char *d, *c = strchr(a, '.'); - if (!c != !i) - return 0; - l = strtoul(a, &d, 10); - if (d != c && *d || l > 255) - return 0; - ia = (ia << 8) | l; - if (c) - c++; - a = c; - } - *o = ia; - return 1; -} - -int -ip_pton(char *a, ip_addr *o) -{ - u16 words[8]; - int i, j, k, l, hfil; - char *start; - - if (a[0] == ':') /* Leading :: */ - { - if (a[1] != ':') - return 0; - a++; - } - hfil = -1; - i = 0; - while (*a) - { - if (*a == ':') /* :: */ - { - if (hfil >= 0) - return 0; - hfil = i; - a++; - continue; - } - j = 0; - l = 0; - start = a; - for(;;) - { - if (*a >= '0' && *a <= '9') - k = *a++ - '0'; - else if (*a >= 'A' && *a <= 'F') - k = *a++ - 'A' + 10; - else if (*a >= 'a' && *a <= 'f') - k = *a++ - 'a' + 10; - else - break; - j = (j << 4) + k; - if (j >= 0x10000 || ++l > 4) - return 0; - } - if (*a == ':' && a[1]) - a++; - else if (*a == '.' && (i == 6 || i < 6 && hfil >= 0)) - { /* Embedded IPv4 address */ - u32 x; - if (!ipv4_pton_u32(start, &x)) - return 0; - words[i++] = x >> 16; - words[i++] = x; - break; - } - else if (*a) - return 0; - if (i >= 8) - return 0; - words[i++] = j; - } - - /* Replace :: with an appropriate number of zeros */ - if (hfil >= 0) - { - j = 8 - i; - for(i=7; i-j >= hfil; i--) - words[i] = words[i-j]; - for(; i>=hfil; i--) - words[i] = 0; - } - - /* Convert the address to ip_addr format */ - for(i=0; i<4; i++) - o->addr[i] = (words[2*i] << 16) | words[2*i+1]; - return 1; -} - -void ipv6_absolutize(ip_addr *a, ip_addr *ifa) -{ - if ((a->addr[0] & 0xffc00000) == 0xfe800000 && /* a is link-scope */ - ((ifa->addr[0] & 0xe0000000) == 0x20000000 | /* ifa is AGU ... */ - (ifa->addr[0] & 0xffc00000) == 0xfec00000)) /* ... or site-scope */ - { - a->addr[0] = ifa->addr[0]; /* Copy the prefix, leave interface ID */ - a->addr[1] = ifa->addr[1]; - } -} - -#ifdef TEST - -#include "bitops.c" - -static void test(char *x) -{ - ip_addr a; - char c[STD_ADDRESS_P_LENGTH+1]; - - printf("%-40s ", x); - if (!ip_pton(x, &a)) - { - puts("BAD"); - return; - } - ip_ntop(a, c); - printf("%-40s %04x\n", c, ipv6_classify(&a)); -} - -int main(void) -{ - puts("Positive tests:"); - test("1:2:3:4:5:6:7:8"); - test("dead:beef:DEAD:BEEF::f00d"); - test("::"); - test("::1"); - test("1::"); - test("::1.234.5.6"); - test("::ffff:1.234.5.6"); - test("::fffe:1.234.5.6"); - test("1:2:3:4:5:6:7::8"); - test("2080::8:800:200c:417a"); - test("ff01::101"); - - puts("Negative tests:"); - test(":::"); - test("1:2:3:4:5:6:7:8:"); - test("1::2::3"); - test("::12345"); - test("::1.2.3.4:5"); - test(":1:2:3:4:5:6:7:8"); - test("g:1:2:3:4:5:6:7"); - return 0; -} - -#endif diff --git a/lib/ipv6.h b/lib/ipv6.h deleted file mode 100644 index b935a6ef..00000000 --- a/lib/ipv6.h +++ /dev/null @@ -1,141 +0,0 @@ - -/* - * BIRD -- IP Addresses et Cetera for IPv6 - * - * (c) 1999--2000 Martin Mares - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#ifndef _BIRD_IPV6_H_ -#define _BIRD_IPV6_H_ - -#include -#include -#include "lib/string.h" -#include "lib/bitops.h" -#include "lib/unaligned.h" - -typedef struct ipv6_addr { - u32 addr[4]; -} ip_addr; - -#define _MI(a,b,c,d) ((struct ipv6_addr) {{ a, b, c, d }}) -#define _I0(a) ((a).addr[0]) -#define _I1(a) ((a).addr[1]) -#define _I2(a) ((a).addr[2]) -#define _I3(a) ((a).addr[3]) - -#define MAX_PREFIX_LENGTH 128 -#define BITS_PER_IP_ADDRESS 128 -#define STD_ADDRESS_P_LENGTH 39 -#define SIZE_OF_IP_HEADER 40 - -#define IPA_NONE _MI(0,0,0,0) - -#define ipa_equal(x,y) ({ ip_addr _a=(x), _b=(y); \ - _I0(_a) == _I0(_b) && \ - _I1(_a) == _I1(_b) && \ - _I2(_a) == _I2(_b) && \ - _I3(_a) == _I3(_b); }) -#define ipa_nonzero(x) ({ ip_addr _a=(x); (_I0(_a) || _I1(_a) || _I2(_a) || _I3(_a)); }) -#define ipa_and(x,y) ({ ip_addr _a=(x), _b=(y); \ - _MI(_I0(_a) & _I0(_b), \ - _I1(_a) & _I1(_b), \ - _I2(_a) & _I2(_b), \ - _I3(_a) & _I3(_b)); }) -#define ipa_or(x,y) ({ ip_addr _a=(x), _b=(y); \ - _MI(_I0(_a) | _I0(_b), \ - _I1(_a) | _I1(_b), \ - _I2(_a) | _I2(_b), \ - _I3(_a) | _I3(_b)); }) -#define ipa_xor(x,y) ({ ip_addr _a=(x), _b=(y); \ - _MI(_I0(_a) ^ _I0(_b), \ - _I1(_a) ^ _I1(_b), \ - _I2(_a) ^ _I2(_b), \ - _I3(_a) ^ _I3(_b)); }) -#define ipa_not(x) ({ ip_addr _a=(x); _MI(~_I0(_a),~_I1(_a),~_I2(_a),~_I3(_a)); }) -#define ipa_mkmask(x) ipv6_mkmask(x) -#define ipa_mklen(x) ipv6_mklen(&(x)) -#define ipa_hash(x) ipv6_hash(&(x)) -#define ipa_hash32(x) ipv6_hash32(&(x)) -#define ipa_hton(x) ipv6_hton(&(x)) -#define ipa_ntoh(x) ipv6_ntoh(&(x)) -#define ipa_classify(x) ipv6_classify(&(x)) -#define ipa_has_link_scope(x) ipv6_has_link_scope(&(x)) -#define ipa_opposite_m1(x) ({ ip_addr _a=(x); _MI(_I0(_a),_I1(_a),_I2(_a),_I3(_a) ^ 1); }) -#define ipa_opposite_m2(x) ({ ip_addr _a=(x); _MI(_I0(_a),_I1(_a),_I2(_a),_I3(_a) ^ 3); }) -/* ipa_class_mask don't make sense with IPv6 */ -/* ipa_from_u32 and ipa_to_u32 replaced by ipa_build */ -#define ipa_build(a,b,c,d) _MI(a,b,c,d) -#define ipa_compare(x,y) ipv6_compare(x,y) -/* ipa_pxlen() requires that x != y */ -#define ipa_pxlen(x, y) ipv6_pxlen(x, y) -#define ipa_getbit(x, y) ipv6_getbit(x, y) -#define ipa_put_addr(x, y) ipv6_put_addr(x, y) -#define ipa_absolutize(x,y) ipv6_absolutize(x,y) - -/* In IPv6, SOCK_RAW does not return packet header */ -#define ip_skip_header(x, y) x - -ip_addr ipv6_mkmask(unsigned); -unsigned ipv6_mklen(ip_addr *); -int ipv6_classify(ip_addr *); -void ipv6_hton(ip_addr *); -void ipv6_ntoh(ip_addr *); -int ipv6_compare(ip_addr, ip_addr); -int ipv4_pton_u32(char *, u32 *); -void ipv6_absolutize(ip_addr *, ip_addr *); - -static inline int ipv6_has_link_scope(ip_addr *a) -{ - return ((a->addr[0] & 0xffc00000) == 0xfe800000); -} - -/* - * This hash function looks well, but once IPv6 enters - * mainstream use, we need to check that it has good - * distribution properties on real routing tables. - */ - -static inline unsigned ipv6_hash(ip_addr *a) -{ - /* Returns a 16-bit hash key */ - u32 x = _I0(*a) ^ _I1(*a) ^ _I2(*a) ^ _I3(*a); - return (x ^ (x >> 16) ^ (x >> 8)) & 0xffff; -} - -static inline u32 ipv6_hash32(ip_addr *a) -{ - /* Returns a 32-bit hash key, although low-order bits are not ixed */ - u32 x = _I0(*a) ^ _I1(*a) ^ _I2(*a) ^ _I3(*a); - return x ^ (x << 16) ^ (x << 24); -} - -static inline u32 ipv6_getbit(ip_addr a, u32 y) -{ - return a.addr[y / 32] & (0x80000000 >> (y % 32)); -} - -static inline u32 ipv6_pxlen(ip_addr a, ip_addr b) -{ - int i = 0; - i+= (a.addr[i] == b.addr[i]); - i+= (a.addr[i] == b.addr[i]); - i+= (a.addr[i] == b.addr[i]); - i+= (a.addr[i] == b.addr[i]); - return 32 * i + 31 - u32_log2(a.addr[i] ^ b.addr[i]); -} - -static inline byte * ipv6_put_addr(byte *buf, ip_addr a) -{ - put_u32(buf+0, _I0(a)); - put_u32(buf+4, _I1(a)); - put_u32(buf+8, _I2(a)); - put_u32(buf+12, _I3(a)); - return buf+16; -} - -#define IP_PREC_INTERNET_CONTROL 0xc0 - -#endif diff --git a/lib/printf.c b/lib/printf.c index ebecc140..3eb988fa 100644 --- a/lib/printf.c +++ b/lib/printf.c @@ -283,9 +283,9 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) /* IP address */ case 'I': if (flags & SPECIAL) - ip_ntox(va_arg(args, ip_addr), ipbuf); + ipa_ntox(va_arg(args, ip_addr), ipbuf); else { - ip_ntop(va_arg(args, ip_addr), ipbuf); + ipa_ntop(va_arg(args, ip_addr), ipbuf); if (field_width == 1) field_width = STD_ADDRESS_P_LENGTH; } diff --git a/lib/socket.h b/lib/socket.h index f1fffa94..a5b85aa2 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -91,6 +91,8 @@ int sk_set_ipv6_checksum(sock *s, int offset); int sk_set_icmp6_filter(sock *s, int p1, int p2); void sk_log_error(sock *s, const char *p); +byte * sk_rx_buffer(sock *s, int *len); /* Temporary */ + extern int sk_priority_control; /* Suggested priority for control traffic, should be sysdep define */ diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 1d37bfd7..a091ed1e 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -991,7 +991,7 @@ bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p if (p->cf->next_hop_self || rta->dest != RTD_ROUTER || ipa_equal(rta->gw, IPA_NONE) || - ipa_has_link_scope(rta->gw) || + ipa_is_link_local(rta->gw) || (!p->is_internal && !p->cf->next_hop_keep && (!p->neigh || (rta->iface != p->neigh->iface)))) set_next_hop(z, p->source_addr); diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index e2339112..27825d7f 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -689,7 +689,7 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c s->password = p->cf->password; s->tx_hook = bgp_connected; BGP_TRACE(D_EVENTS, "Connecting to %I%J from local address %I%J", s->daddr, p->cf->iface, - s->saddr, ipa_has_link_scope(s->saddr) ? s->iface : NULL); + s->saddr, ipa_is_link_local(s->saddr) ? s->iface : NULL); bgp_setup_conn(p, conn); bgp_setup_sk(conn, s); bgp_conn_set_state(conn, BS_CONNECT); @@ -735,7 +735,7 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED) { struct bgp_proto *p = (struct bgp_proto *) pc->proto; if (ipa_equal(p->cf->remote_ip, sk->daddr) && - (!ipa_has_link_scope(sk->daddr) || (p->cf->iface == sk->iface))) + (!ipa_is_link_local(sk->daddr) || (p->cf->iface == sk->iface))) { /* We are in proper state and there is no other incoming connection */ int acc = (p->p.proto_state == PS_START || p->p.proto_state == PS_UP) && @@ -750,7 +750,7 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED) } BGP_TRACE(D_EVENTS, "Incoming connection from %I%J (port %d) %s", - sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL, + sk->daddr, ipa_is_link_local(sk->daddr) ? sk->iface : NULL, sk->dport, acc ? "accepted" : "rejected"); if (!acc) @@ -779,7 +779,7 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED) } log(L_WARN "BGP: Unexpected connect from unknown address %I%J (port %d)", - sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL, sk->dport); + sk->daddr, ipa_is_link_local(sk->daddr) ? sk->iface : NULL, sk->dport); reject: rfree(sk); return 0; @@ -1169,8 +1169,8 @@ bgp_check_config(struct bgp_config *c) if (c->multihop && (c->gw_mode == GW_DIRECT)) cf_error("Multihop BGP cannot use direct gateway mode"); - if (c->multihop && (ipa_has_link_scope(c->remote_ip) || - ipa_has_link_scope(c->source_addr))) + if (c->multihop && (ipa_is_link_local(c->remote_ip) || + ipa_is_link_local(c->source_addr))) cf_error("Multihop BGP cannot be used with link-local addresses"); if (c->multihop && c->bfd && ipa_zero(c->source_addr)) diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 8e0b2412..c8345530 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -63,7 +63,7 @@ bgp_proto: | bgp_proto NEIGHBOR ipa ipa_scope ipa_port AS expr ';' { if (ipa_nonzero(BGP_CFG->remote_ip)) cf_error("Only one neighbor per BGP instance is allowed"); - if (!ipa_has_link_scope($3) != !$4) + if (!ipa_is_link_local($3) != !$4) cf_error("Link-local address and interface scope must be used together"); BGP_CFG->remote_ip = $3; diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 0b9de8c1..69646c7d 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -69,8 +69,8 @@ mrt_put_bgp4_hdr(byte *buf, struct bgp_conn *conn, int as4) put_u16(buf+0, (p->neigh && p->neigh->iface) ? p->neigh->iface->index : 0); put_u16(buf+2, BGP_AF); buf+=4; - buf = ipa_put_addr(buf, conn->sk ? conn->sk->daddr : IPA_NONE); - buf = ipa_put_addr(buf, conn->sk ? conn->sk->saddr : IPA_NONE); + buf = put_ipa(buf, conn->sk ? conn->sk->daddr : IPA_NONE); + buf = put_ipa(buf, conn->sk ? conn->sk->saddr : IPA_NONE); return buf; } @@ -522,7 +522,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) *tmp++ = BGP_AF_IPV6; *tmp++ = 1; - if (ipa_has_link_scope(ip)) + if (ipa_is_link_local(ip)) ip = IPA_NONE; if (ipa_nonzero(ip_ll)) @@ -1034,7 +1034,7 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a) int second = (nh->u.ptr->length == NEXT_HOP_LENGTH) && ipa_nonzero(nexthop[1]); /* First address should not be link-local, but may be zero in direct mode */ - if (ipa_has_link_scope(*nexthop)) + if (ipa_is_link_local(*nexthop)) *nexthop = IPA_NONE; #else int second = 0; diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 6df5df08..e535287c 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -37,36 +37,9 @@ #endif -#define IP4_MIN_MTU 576 -#define IP6_MIN_MTU 1280 - -#define IP4_OSPF_ALL_ROUTERS ipa_build4(224, 0, 0, 5) -#define IP4_OSPF_DES_ROUTERS ipa_build4(224, 0, 0, 6) - -#define IP6_OSPF_ALL_ROUTERS ipa_build6(0xFF020000, 0, 0, 5) -#define IP6_OSPF_DES_ROUTERS ipa_build6(0xFF020000, 0, 0, 6) - #ifdef IPV6 -#define ip4_addr u32 -#define ip6_addr ip_addr -#define _MI6(x1,x2,x3,x4) _MI(x1, x2, x3, x4) -#define ipa_is_link_local(x) ipa_has_link_scope(x) -#define ipa_from_u32(x) _MI6(0,0,0xffff,x) -#define ipa_to_u32(x) _I3(x) -#define ipa_build4(a,b,c,d) IPA_NONE -#define ipa_build6(a,b,c,d) _MI6(a,b,c,d) #define OSPF_IS_V2 0 #else -#define ip4_addr u32 -#define ip6_addr ip_addr -#define _I0(X) 0 -#define _I1(X) 0 -#define _I2(X) 0 -#define _I3(X) 0 -#define _MI6(x1,x2,x3,x4) IPA_NONE -#define ipa_is_link_local(x) 0 -#define ipa_build4(a,b,c,d) _MI(((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) -#define ipa_build6(a,b,c,d) IPA_NONE #define OSPF_IS_V2 1 #endif @@ -744,10 +717,12 @@ lsa_net_count(struct ospf_lsa_header *lsa) #define ipa_from_rid(x) ipa_from_u32(x) #define ipa_to_rid(x) ipa_to_u32(x) - #define IPV6_PREFIX_SPACE(x) ((((x) + 63) / 32) * 4) #define IPV6_PREFIX_WORDS(x) (((x) + 63) / 32) +/* FIXME: these four functions should be significantly redesigned w.r.t. integration, + also should be named as ospf3_* instead of *_ipv6_* */ + static inline u32 * lsa_get_ipv6_prefix(u32 *buf, ip_addr *addr, int *pxlen, u8 *pxopts, u16 *rest) { @@ -787,6 +762,7 @@ lsa_get_ipv6_addr(u32 *buf, ip_addr *addr) static inline u32 * put_ipv6_prefix(u32 *buf, ip_addr addr, u8 pxlen, u8 pxopts, u16 lh) { +#ifdef IPV6 *buf++ = ((pxlen << 24) | (pxopts << 16) | lh); if (pxlen > 0) @@ -797,6 +773,7 @@ put_ipv6_prefix(u32 *buf, ip_addr addr, u8 pxlen, u8 pxopts, u16 lh) *buf++ = _I2(addr); if (pxlen > 96) *buf++ = _I3(addr); +#endif return buf; } diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index 2814712d..a545c7fd 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -265,7 +265,8 @@ ospf_rx_hook(sock *sk, int len) } /* Second, we check packet length, checksum, and the protocol version */ - struct ospf_packet *pkt = (struct ospf_packet *) ip_skip_header(sk->rbuf, &len); + struct ospf_packet *pkt = (void *) sk_rx_buffer(sk, &len); + if (pkt == NULL) DROP("bad IP header", len); diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 08f90b49..b616c0d1 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -1822,10 +1822,10 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, struct ospf_lsa_link *llsa = lhe->lsa_body; - if (ipa_zero(llsa->lladdr)) + if (ip6_zero(llsa->lladdr)) return NULL; - return new_nexthop(p, llsa->lladdr, pn->iface, pn->weight); + return new_nexthop(p, ipa_from_ip6(llsa->lladdr), pn->iface, pn->weight); } } diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 15ba013a..42bf9c45 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -1319,7 +1319,7 @@ prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa) ASSERT(p->lsab_used == 0); ll = lsab_allocz(p, sizeof(struct ospf_lsa_link)); ll->options = ifa->oa->options | (ifa->priority << 24); - ll->lladdr = ifa->addr->ip; + ll->lladdr = ipa_to_ip6(ifa->addr->ip); ll = NULL; /* buffer might be reallocated later */ struct ifa *a; diff --git a/proto/radv/packets.c b/proto/radv/packets.c index ef869722..3862af8d 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -343,7 +343,7 @@ radv_send_ra(struct radv_iface *ifa, int shutdown) } RADV_TRACE(D_PACKETS, "Sending RA via %s", ifa->iface->name); - sk_send_to(ifa->sk, ifa->plen, AllNodes, 0); + sk_send_to(ifa->sk, ifa->plen, IP6_ALL_NODES, 0); } @@ -432,7 +432,7 @@ radv_sk_open(struct radv_iface *ifa) if (sk_setup_multicast(sk) < 0) goto err; - if (sk_join_group(sk, AllRouters) < 0) + if (sk_join_group(sk, IP6_ALL_ROUTERS) < 0) goto err; ifa->sk = sk; diff --git a/proto/radv/radv.h b/proto/radv/radv.h index bb80d65f..48ba9c1a 100644 --- a/proto/radv/radv.h +++ b/proto/radv/radv.h @@ -26,9 +26,6 @@ #define ICMPV6_PROTO 58 -#define AllNodes _MI(0xFF020000, 0, 0, 1) /* FF02::1 */ -#define AllRouters _MI(0xFF020000, 0, 0, 2) /* FF02::2 */ - #define ICMPV6_RS 133 #define ICMPV6_RA 134 diff --git a/proto/rip/rip.c b/proto/rip/rip.c index bc9ffc5f..a0c28e72 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -305,7 +305,7 @@ advertise_entry( struct proto *p, struct rip_block *b, ip_addr whotoldme, struct A.flags = 0; #ifndef IPV6 A.gw = ipa_nonzero(b->nexthop) ? b->nexthop : whotoldme; - pxlen = ipa_mklen(b->netmask); + pxlen = ipa_masklen(b->netmask); #else /* FIXME: next hop is in other packet for v6 */ A.gw = whotoldme; @@ -365,7 +365,7 @@ process_block( struct proto *p, struct rip_block *block, ip_addr whotoldme, stru #ifndef IPV6 metric = ntohl( block->metric ); - pxlen = ipa_mklen(block->netmask); + pxlen = ipa_masklen(block->netmask); #else metric = block->metric; pxlen = block->pxlen; @@ -447,7 +447,7 @@ rip_process_packet( struct proto *p, struct rip_packet *packet, int num, ip_addr ipa_ntoh( block->netmask ); ipa_ntoh( block->nexthop ); if (packet->heading.version == RIP_V1) /* FIXME (nonurgent): switch to disable this? */ - block->netmask = ipa_class_mask(block->network); + block->netmask = ip4_class_mask(ipa_to_ip4(block->network)); #endif process_block( p, block, whotoldme, iface ); } @@ -721,7 +721,7 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ #ifndef IPV6 rif->sock->daddr = ipa_from_u32(0xe0000009); #else - rif->sock->daddr = ipa_build(0xff020000, 0, 0, 9); + rif->sock->daddr = IP6_RIP_ROUTERS; #endif } else { rif->sock->daddr = new->addr->brd; @@ -812,7 +812,7 @@ rip_if_notify(struct proto *p, unsigned c, struct iface *iface) #ifndef IPV6 lock->addr = ipa_from_u32(0xe0000009); #else - ip_pton("FF02::9", &lock->addr); + lock->addr = IP6_RIP_ROUTERS; #endif else lock->addr = iface->addr->brd; diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index 621f7309..0e65c51c 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -382,7 +382,7 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan) if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) SKIP("strange class/scope\n"); - int pxlen = (flags & RTF_HOST) ? MAX_PREFIX_LENGTH : ipa_mklen(imask); + int pxlen = (flags & RTF_HOST) ? MAX_PREFIX_LENGTH : ipa_masklen(imask); if (pxlen < 0) { log(L_ERR "%s (%I) - netmask %I", errmsg, idst, imask); return; } @@ -653,7 +653,7 @@ krt_read_addr(struct ks_msg *msg, int scan) ibrd = ipa_from_sa(&brd); - if ((masklen = ipa_mklen(imask)) < 0) + if ((masklen = ipa_masklen(imask)) < 0) { log(L_ERR "KIF: Invalid masklen %I for %s", imask, iface->name); return; diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 164038ec..04f0fe50 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -764,6 +764,29 @@ sk_set_tos6(sock *s, int tos) return 0; } +static inline byte * +sk_skip_ip_header(byte *pkt, int *len) +{ + if ((*len < 20) || ((*pkt & 0xf0) != 0x40)) + return NULL; + + int hlen = (*pkt & 0x0f) * 4; + if ((hlen < 20) || (hlen > *len)) + return NULL; + + *len -= hlen; + return pkt + hlen; +} + +byte * +sk_rx_buffer(sock *s, int *len) +{ + if (sk_is_ipv4(s) && (s->type == SK_IP)) + return sk_skip_ip_header(s->rbuf, len); + else + return s->rbuf; +} + /* * Public socket functions diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index 518713bc..3cee96b4 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -47,19 +47,9 @@ typedef struct sockaddr_bird { #ifdef IPV6 #define BIRD_AF AF_INET6 -#define _MI6(x1,x2,x3,x4) _MI(x1, x2, x3, x4) -#define ipa_is_link_local(x) ipa_has_link_scope(x) #define ipa_from_sa(x) ipa_from_sa6(x) -#define ipa_from_u32(x) _MI6(0,0,0xffff,x) -#define ipa_to_u32(x) _I3(x) #else #define BIRD_AF AF_INET -#define _I0(X) 0 -#define _I1(X) 0 -#define _I2(X) 0 -#define _I3(X) 0 -#define _MI6(x1,x2,x3,x4) IPA_NONE -#define ipa_is_link_local(x) 0 #define ipa_from_sa(x) ipa_from_sa4(x) #endif @@ -75,7 +65,7 @@ static inline ip_addr ipa_from_in4(struct in_addr a) { return ipa_from_u32(ntohl(a.s_addr)); } static inline ip_addr ipa_from_in6(struct in6_addr a) -{ return _MI6(ntohl(a.s6_addr32[0]), ntohl(a.s6_addr32[1]), ntohl(a.s6_addr32[2]), ntohl(a.s6_addr32[3])); } +{ return ipa_build6(ntohl(a.s6_addr32[0]), ntohl(a.s6_addr32[1]), ntohl(a.s6_addr32[2]), ntohl(a.s6_addr32[3])); } static inline ip_addr ipa_from_sa4(sockaddr *sa) { return ipa_from_in4(((struct sockaddr_in *) sa)->sin_addr); } @@ -86,8 +76,14 @@ static inline ip_addr ipa_from_sa6(sockaddr *sa) static inline struct in_addr ipa_to_in4(ip_addr a) { return (struct in_addr) { htonl(ipa_to_u32(a)) }; } +#ifdef IPV6 static inline struct in6_addr ipa_to_in6(ip_addr a) { return (struct in6_addr) { .s6_addr32 = { htonl(_I0(a)), htonl(_I1(a)), htonl(_I2(a)), htonl(_I3(a)) } }; } +#else +/* Temporary dummy */ +static inline struct in6_addr ipa_to_in6(ip_addr a) +{ return (struct in6_addr) { .s6_addr32 = { 0, 0, 0, 0 } }; } +#endif void sockaddr_fill(sockaddr *sa, int af, ip_addr a, struct iface *ifa, uint port); int sockaddr_read(sockaddr *sa, int af, ip_addr *a, struct iface **ifa, uint *port); From 6f8bbaa10bbd21729d0b62a5878febcbee2c0811 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 3 Nov 2014 10:42:55 +0100 Subject: [PATCH 07/11] Fininshing integrated OSPF. --- lib/event.c | 2 +- lib/event.h | 7 ++ proto/ospf/dbdes.c | 47 +++++---- proto/ospf/hello.c | 7 +- proto/ospf/iface.c | 33 ++++++ proto/ospf/lsack.c | 21 ++-- proto/ospf/lsreq.c | 25 ++--- proto/ospf/lsupd.c | 234 +++++++++++++++++++++++++++++------------- proto/ospf/neighbor.c | 187 +++++++++++++++++---------------- proto/ospf/ospf.c | 16 +-- proto/ospf/ospf.h | 12 ++- proto/ospf/packet.c | 5 +- proto/ospf/topology.c | 27 +++-- sysdep/unix/timer.h | 6 ++ 14 files changed, 389 insertions(+), 240 deletions(-) diff --git a/lib/event.c b/lib/event.c index 916cf55c..b429c205 100644 --- a/lib/event.c +++ b/lib/event.c @@ -27,7 +27,7 @@ event_list global_event_list; inline void ev_postpone(event *e) { - if (e->n.next) + if (ev_active(e)) { rem_node(&e->n); e->n.next = NULL; diff --git a/lib/event.h b/lib/event.h index d8500413..d5975222 100644 --- a/lib/event.h +++ b/lib/event.h @@ -30,4 +30,11 @@ void ev_schedule(event *); void ev_postpone(event *); int ev_run_list(event_list *); +static inline int +ev_active(event *e) +{ + return e->n.next != NULL; +} + + #endif diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c index 88c87164..65bdb3ec 100644 --- a/proto/ospf/dbdes.c +++ b/proto/ospf/dbdes.c @@ -212,17 +212,6 @@ ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n) ospf_prepare_dbdes(p, n); ospf_do_send_dbdes(p, n); - - if (n->state == NEIGHBOR_EXSTART) - return; - - /* Master should restart RXMT timer for each DBDES exchange */ - if (n->myimms & DBDES_MS) - tm_start(n->rxmt_timer, n->ifa->rxmtint); - - if (!(n->myimms & DBDES_MS)) - if (!(n->myimms & DBDES_M) && !(n->imms & DBDES_M)) - ospf_neigh_sm(n, INM_EXDONE); } void @@ -277,13 +266,20 @@ ospf_process_dbdes(struct ospf_proto *p, struct ospf_packet *pkt, struct ospf_ne en = ospf_hash_find(p->gr, lsa_domain, lsa.id, lsa.rt, lsa_type); if (!en || (lsa_comp(&lsa, &(en->lsa)) == CMP_NEWER)) { + /* This should be splitted to ospf_lsa_lsrq_up() */ req = ospf_hash_get(n->lsrqh, lsa_domain, lsa.id, lsa.rt, lsa_type); if (!SNODE_VALID(req)) s_add_tail(&n->lsrql, SNODE req); + if (!SNODE_VALID(n->lsrqi)) + n->lsrqi = req; + req->lsa = lsa; req->lsa_body = LSA_BODY_DUMMY; + + if (!tm_active(n->lsrq_timer)) + tm_start(n->lsrq_timer, 0); } } @@ -306,13 +302,16 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, u32 rcv_ddseq, rcv_options; u16 rcv_iface_mtu; u8 rcv_imms; - uint plen, err_val = 0, err_seqmis = 0; + uint plen, err_val = 0; /* RFC 2328 10.6 */ plen = ntohs(pkt->length); if (plen < ospf_dbdes_hdrlen(p)) - DROP("too short", plen); + { + LOG_PKT("Bad DBDES packet from nbr %R on %s - %s (%u)", n->rid, ifa->ifname, "too short", plen); + return; + } OSPF_PACKET(ospf_dump_dbdes, pkt, "DBDES packet received from nbr %R on %s", n->rid, ifa->ifname); @@ -366,6 +365,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, n->options = rcv_options; n->myimms &= ~DBDES_MS; n->imms = rcv_imms; + tm_stop(n->dbdes_timer); ospf_neigh_sm(n, INM_NEGDONE); ospf_send_dbdes(p, n); break; @@ -381,6 +381,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, n->ddr = rcv_ddseq - 1; /* It will be set corectly a few lines down */ n->imms = rcv_imms; ospf_neigh_sm(n, INM_NEGDONE); + /* Continue to the NEIGHBOR_EXCHANGE case */ } else { @@ -394,9 +395,6 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, (rcv_ddseq == n->ddr)) goto duplicate; - /* Do INM_SEQMIS during packet error */ - err_seqmis = 1; - if ((rcv_imms & DBDES_MS) != (n->imms & DBDES_MS)) DROP("MS-bit mismatch", rcv_imms); @@ -422,9 +420,14 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, return; if (!(n->myimms & DBDES_M) && !(n->imms & DBDES_M)) + { + tm_stop(n->dbdes_timer); ospf_neigh_sm(n, INM_EXDONE); - else - ospf_send_dbdes(p, n); + break; + } + + ospf_send_dbdes(p, n); + tm_start(n->dbdes_timer, n->ifa->rxmtint); } else { @@ -440,6 +443,9 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, return; ospf_send_dbdes(p, n); + + if (!(n->myimms & DBDES_M) && !(n->imms & DBDES_M)) + ospf_neigh_sm(n, INM_EXDONE); } break; @@ -450,8 +456,6 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, (rcv_ddseq == n->ddr)) goto duplicate; - err_seqmis = 1; - DROP("too late for DD exchange", n->state); default: @@ -471,7 +475,6 @@ drop: LOG_PKT("Bad DBDES packet from nbr %R on %s - %s (%u)", n->rid, ifa->ifname, err_dsc, err_val); - if (err_seqmis) - ospf_neigh_sm(n, INM_SEQMIS); + ospf_neigh_sm(n, INM_SEQMIS); return; } diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index e8b1c702..50cf1407 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -304,11 +304,8 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, nn->found = 1; } - // XXXX format - // "ospf1: New neighbor found: 192.168.1.1/fe80:1234:1234:1234:1234 on eth0"; - // "ospf1: New neighbor found: 192.168.1.1 on eth0 at fe80:1234:1234:1234:1234"; - // "ospf1: Neighbor 192.168.1.1 on eth0 found, IP adress fe80:1234:1234:1234:1234"; - OSPF_TRACE(D_EVENTS, "New neighbor found: %I on %s", faddr, ifa->ifname); + OSPF_TRACE(D_EVENTS, "New neighbor %R on %s, IP address %I", + rcv_rid, ifa->ifname, faddr); n = ospf_neighbor_new(ifa); diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index e14f09cb..4dfb74fb 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -58,6 +58,12 @@ ifa_bufsize(struct ospf_iface *ifa) return MAX(bsize, ifa->tx_length); } +static inline uint +ifa_flood_queue_size(struct ospf_iface *ifa) +{ + return ifa->tx_length / 24; +} + int ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen) { @@ -476,6 +482,9 @@ ospf_iface_add(struct object_lock *lock) if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA)) ifa->wait_timer = tm_new_set(ifa->pool, wait_timer_hook, ifa, 0, 0); + + ifa->flood_queue_size = ifa_flood_queue_size(ifa); + ifa->flood_queue = mb_allocz(ifa->pool, ifa->flood_queue_size * sizeof(void *)); } /* Do iface UP, unless there is no link and we use link detection */ @@ -679,6 +688,9 @@ ospf_iface_new_vlink(struct ospf_proto *p, struct ospf_iface_patt *ip) add_tail(&p->iface_list, NODE ifa); ifa->hello_timer = tm_new_set(ifa->pool, hello_timer_hook, ifa, 0, ifa->helloint); + + ifa->flood_queue_size = ifa_flood_queue_size(ifa); + ifa->flood_queue = mb_allocz(ifa->pool, ifa->flood_queue_size * sizeof(void *)); } static void @@ -693,6 +705,20 @@ ospf_iface_change_timer(timer *tm, uint val) tm_start(tm, val); } +static inline void +ospf_iface_update_flood_queue_size(struct ospf_iface *ifa) +{ + uint old_size = ifa->flood_queue_size; + uint new_size = ifa_flood_queue_size(ifa); + + if (new_size <= old_size) + return; + + ifa->flood_queue_size = new_size; + ifa->flood_queue = mb_realloc(ifa->flood_queue, new_size * sizeof(void *)); + bzero(ifa->flood_queue + old_size, (new_size - old_size) * sizeof(void *)); +} + int ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) { @@ -739,6 +765,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) ifname, ifa->rxmtint, new->rxmtint); ifa->rxmtint = new->rxmtint; + /* FIXME: Update neighbors' timers */ } /* POLL TIMER */ @@ -874,6 +901,9 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* ifa cannot be vlink */ ifa->tx_length = ifa_tx_length(ifa); update_buffers = 1; + + if (!ifa->stub) + ospf_iface_update_flood_queue_size(ifa); } /* RX BUFFER */ @@ -1211,6 +1241,9 @@ ospf_iface_change_mtu(struct ospf_proto *p, struct ospf_iface *ifa) sk_set_rbsize(ifa->sk, bsize); if (bsize > ifa->sk->tbsize) sk_set_tbsize(ifa->sk, bsize); + + if (!ifa->stub) + ospf_iface_update_flood_queue_size(ifa); } static void diff --git a/proto/ospf/lsack.c b/proto/ospf/lsack.c index e590817e..251b5e47 100644 --- a/proto/ospf/lsack.c +++ b/proto/ospf/lsack.c @@ -162,23 +162,20 @@ ospf_receive_lsack(struct ospf_packet *pkt, struct ospf_iface *ifa, if (lsa_comp(&lsa, &ret->lsa) != CMP_SAME) { - OSPF_TRACE(D_PACKETS, "Strange LSACK from %I", n->ip); - OSPF_TRACE(D_PACKETS, "Type: %04x, Id: %R, Rt: %R", + OSPF_TRACE(D_PACKETS, "Strange LSACK from nbr %R on %s", n->rid, ifa->ifname); + OSPF_TRACE(D_PACKETS, " Type: %04x, Id: %R, Rt: %R", lsa_type, lsa.id, lsa.rt); - OSPF_TRACE(D_PACKETS, "I have: Age: %4u, Seq: %08x, Sum: %04x", - ret->lsa.age, ret->lsa.sn, ret->lsa.checksum); - OSPF_TRACE(D_PACKETS, "He has: Age: %4u, Seq: %08x, Sum: %04x", - lsa.age, lsa.sn, lsa.checksum); + OSPF_TRACE(D_PACKETS, " I have: Seq: %08x, Age: %4u, Sum: %04x", + ret->lsa.sn, ret->lsa.age, ret->lsa.checksum); + OSPF_TRACE(D_PACKETS, " It has: Seq: %08x, Age: %4u, Sum: %04x", + lsa.sn, lsa.age, lsa.checksum); continue; } - en = ospf_hash_find_entry(p->gr, ret); - if (en) - en->ret_count--; - DBG("Deleting LSA (Type: %04x Id: %R Rt: %R) from lsrtl for neighbor %R\n", lsa_type, lsa.id, lsa.rt, n->rid); - s_rem_node(SNODE ret); - ospf_hash_delete(n->lsrth, ret); + + en = ospf_hash_find_entry(p->gr, ret); + ospf_lsa_lsrt_down_(en, n, ret); } } diff --git a/proto/ospf/lsreq.c b/proto/ospf/lsreq.c index 067ad79b..657c0247 100644 --- a/proto/ospf/lsreq.c +++ b/proto/ospf/lsreq.c @@ -54,42 +54,37 @@ ospf_send_lsreq(struct ospf_proto *p, struct ospf_neighbor *n) { struct ospf_iface *ifa = n->ifa; struct ospf_lsreq_header *lsrs; - struct top_hash_entry *en; + struct top_hash_entry *req; struct ospf_packet *pkt; uint i, lsr_max, length; /* RFC 2328 10.9 */ - if (EMPTY_SLIST(n->lsrql)) - { - if (n->state == NEIGHBOR_LOADING) - ospf_neigh_sm(n, INM_LOADDONE); - return; - } + /* ASSERT((n->state >= NEIGHBOR_EXCHANGE) && !EMPTY_SLIST(n->lsrql)); */ pkt = ospf_tx_buffer(ifa); ospf_pkt_fill_hdr(ifa, pkt, LSREQ_P); ospf_lsreq_body(p, pkt, &lsrs, &lsr_max); - /* We send smaller LSREQ to prevent multiple LSACKs as answer */ - lsr_max = lsr_max / 4; - i = 0; - WALK_SLIST(en, n->lsrql) + WALK_SLIST(req, n->lsrql) { if (i == lsr_max) break; DBG("Requesting %uth LSA: Type: %04u, ID: %R, RT: %R, SN: 0x%x, Age %u\n", - i, en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn, en->lsa.age); + i, req->lsa_type, req->lsa.id, req->lsa.rt, req->lsa.sn, req->lsa.age); - u32 etype = lsa_get_etype(&en->lsa, p); + u32 etype = lsa_get_etype(&req->lsa, p); lsrs[i].type = htonl(etype); - lsrs[i].rt = htonl(en->lsa.rt); - lsrs[i].id = htonl(en->lsa.id); + lsrs[i].rt = htonl(req->lsa.rt); + lsrs[i].id = htonl(req->lsa.id); i++; } + /* We store the position to see whether requested LSAs have been received */ + n->lsrqi = req; + length = ospf_pkt_hdrlen(p) + i * sizeof(struct ospf_lsreq_header); pkt->length = htons(length); diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 0aa45f6a..ea32923a 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -32,8 +32,8 @@ ospf_dump_lsahdr(struct ospf_proto *p, struct ospf_lsa_header *lsa_n) lsa_ntoh_hdr(lsa_n, &lsa); lsa_etype = lsa_get_etype(&lsa, p); - log(L_TRACE "%s: LSA Type: %04x, Id: %R, Rt: %R, Age: %u, Seq: %08x, Sum: %04x", - p->p.name, lsa_etype, lsa.id, lsa.rt, lsa.age, lsa.sn, lsa.checksum); + log(L_TRACE "%s: LSA Type: %04x, Id: %R, Rt: %R, Seq: %08x, Age: %u, Sum: %04x", + p->p.name, lsa_etype, lsa.id, lsa.rt, lsa.sn, lsa.age, lsa.checksum); } void @@ -65,45 +65,63 @@ ospf_lsupd_set_lsa_count(struct ospf_packet *pkt, uint hdrlen, u32 val) static inline void ospf_lsupd_body(struct ospf_proto *p, struct ospf_packet *pkt, - uint *offset, uint *bound, uint *lsa_count) + uint *offset, uint *lsa_count) { uint hlen = ospf_lsupd_hdrlen(p); *offset = hlen; - *bound = ntohs(pkt->length) - sizeof(struct ospf_lsa_header); *lsa_count = ospf_lsupd_get_lsa_count(pkt, hlen); } static void ospf_dump_lsupd(struct ospf_proto *p, struct ospf_packet *pkt) { - uint offset, bound, i, lsa_count, lsalen; + uint offset, plen, i, lsa_count, lsa_len; ASSERT(pkt->type == LSUPD_P); ospf_dump_common(p, pkt); - ospf_lsupd_body(p, pkt, &offset, &bound, &lsa_count); + plen = ntohs(pkt->length); + ospf_lsupd_body(p, pkt, &offset, &lsa_count); for (i = 0; i < lsa_count; i++) { - if (offset > bound) - { - log(L_TRACE "%s: LSA invalid", p->p.name); - return; - } + if ((offset + sizeof(struct ospf_lsa_header)) > plen) + goto invalid; struct ospf_lsa_header *lsa = ((void *) pkt) + offset; - ospf_dump_lsahdr(p, lsa); - lsalen = ntohs(lsa->length); - offset += lsalen; + lsa_len = ntohs(lsa->length); - if (((lsalen % 4) != 0) || (lsalen <= sizeof(struct ospf_lsa_header))) - { - log(L_TRACE "%s: LSA invalid", p->p.name); - return; - } + if (((lsa_len % 4) != 0) || (lsa_len <= sizeof(struct ospf_lsa_header))) + goto invalid; + + ospf_dump_lsahdr(p, lsa); + offset += lsa_len; } + return; + +invalid: + log(L_TRACE "%s: LSA invalid", p->p.name); + return; } +static inline void +ospf_lsa_lsrq_down(struct top_hash_entry *req, struct ospf_neighbor *n, struct ospf_neighbor *from) +{ + if (req == n->lsrqi) + n->lsrqi = SNODE_NEXT(req); + + s_rem_node(SNODE req); + ospf_hash_delete(n->lsrqh, req); + + if (EMPTY_SLIST(n->lsrql)) + { + tm_stop(n->lsrq_timer); + + if (n->state == NEIGHBOR_LOADING) + ospf_neigh_sm(n, INM_LOADDONE); + } +} + static inline void ospf_lsa_lsrt_up(struct top_hash_entry *en, struct ospf_neighbor *n) { @@ -117,6 +135,22 @@ ospf_lsa_lsrt_up(struct top_hash_entry *en, struct ospf_neighbor *n) ret->lsa = en->lsa; ret->lsa_body = LSA_BODY_DUMMY; + + if (!tm_active(n->lsrt_timer)) + tm_start(n->lsrt_timer, n->ifa->rxmtint); +} + +void +ospf_lsa_lsrt_down_(struct top_hash_entry *en, struct ospf_neighbor *n, struct top_hash_entry *ret) +{ + if (en) + en->ret_count--; + + s_rem_node(SNODE ret); + ospf_hash_delete(n->lsrth, ret); + + if (EMPTY_SLIST(n->lsrtl)) + tm_stop(n->lsrt_timer); } static inline int @@ -125,14 +159,9 @@ ospf_lsa_lsrt_down(struct top_hash_entry *en, struct ospf_neighbor *n) struct top_hash_entry *ret = ospf_hash_find_entry(n->lsrth, en); if (ret) - { - en->ret_count--; - s_rem_node(SNODE ret); - ospf_hash_delete(n->lsrth, ret); - return 1; - } + ospf_lsa_lsrt_down_(en, n, ret); - return 0; + return ret != NULL; } void @@ -144,10 +173,61 @@ ospf_add_flushed_to_lsrt(struct ospf_proto *p, struct ospf_neighbor *n) if ((en->lsa.age == LSA_MAXAGE) && (en->lsa_body != NULL) && lsa_flooding_allowed(en->lsa_type, en->domain, n->ifa)) ospf_lsa_lsrt_up(en, n); + + /* If we found any flushed LSA, we send them ASAP */ + if (tm_active(n->lsrt_timer)) + tm_start(n->lsrt_timer, 0); } +static int ospf_flood_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_list, uint lsa_count, uint lsa_min_count, struct ospf_iface *ifa); -static void ospf_send_lsupd_to_ifa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_iface *ifa); +static void +ospf_enqueue_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_iface *ifa) +{ + if (ifa->flood_queue_used == ifa->flood_queue_size) + { + /* If we already have full queue, we send some packets */ + uint sent = ospf_flood_lsupd(p, ifa->flood_queue, ifa->flood_queue_used, ifa->flood_queue_used / 2, ifa); + int i; + + for (i = 0; i < sent; i++) + ifa->flood_queue[i]->ret_count--; + + ifa->flood_queue_used -= sent; + memmove(ifa->flood_queue, ifa->flood_queue + sent, ifa->flood_queue_used * sizeof(void *)); + bzero(ifa->flood_queue + ifa->flood_queue_used, sent * sizeof(void *)); + } + + en->ret_count++; + ifa->flood_queue[ifa->flood_queue_used] = en; + ifa->flood_queue_used++; + + if (!ev_active(p->flood_event)) + ev_schedule(p->flood_event); +} + +void +ospf_flood_event(void *ptr) +{ + struct ospf_proto *p = ptr; + struct ospf_iface *ifa; + int i, count; + + WALK_LIST(ifa, p->iface_list) + { + if (ifa->flood_queue_used == 0) + continue; + + count = ifa->flood_queue_used; + ospf_flood_lsupd(p, ifa->flood_queue, count, count, ifa); + + for (i = 0; i < count; i++) + ifa->flood_queue[i]->ret_count--; + + ifa->flood_queue_used = 0; + bzero(ifa->flood_queue, count * sizeof(void *)); + } +} /** @@ -195,14 +275,7 @@ ospf_flood_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neig /* If same or newer, remove LSA from the link state request list */ if (cmp > CMP_OLDER) - { - s_rem_node(SNODE req); - ospf_hash_delete(n->lsrqh, req); - n->want_lsreq = 1; - - if ((EMPTY_SLIST(n->lsrql)) && (n->state == NEIGHBOR_LOADING)) - ospf_neigh_sm(n, INM_LOADDONE); - } + ospf_lsa_lsrq_down(req, n, from); /* If older or same, skip processing of this neighbor */ if (cmp < CMP_NEWER) @@ -242,7 +315,7 @@ ospf_flood_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neig } /* 13.3 (5) - finally flood the packet */ - ospf_send_lsupd_to_ifa(p, en, ifa); + ospf_enqueue_lsa(p, en, ifa); } return back; @@ -302,26 +375,33 @@ ospf_prepare_lsupd(struct ospf_proto *p, struct ospf_iface *ifa, } -static void -ospf_send_lsupd_to_ifa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_iface *ifa) +static int +ospf_flood_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_list, uint lsa_count, uint lsa_min_count, struct ospf_iface *ifa) { - uint c = ospf_prepare_lsupd(p, ifa, &en, 1); + uint i, c; - if (!c) /* Too large LSA */ - return; - - OSPF_PACKET(ospf_dump_lsupd, ospf_tx_buffer(ifa), - "LSUPD packet flooded via %s", ifa->ifname); - - if (ifa->type == OSPF_IT_BCAST) + for (i = 0; i < lsa_min_count; i += c) { - if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP)) - ospf_send_to_all(ifa); + c = ospf_prepare_lsupd(p, ifa, lsa_list + i, lsa_count - i); + + if (!c) /* Too large LSA */ + { i++; continue; } + + OSPF_PACKET(ospf_dump_lsupd, ospf_tx_buffer(ifa), + "LSUPD packet flooded via %s", ifa->ifname); + + if (ifa->type == OSPF_IT_BCAST) + { + if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP)) + ospf_send_to_all(ifa); + else + ospf_send_to_des(ifa); + } else - ospf_send_to_des(ifa); + ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE); } - else - ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE); + + return i; } int @@ -343,17 +423,19 @@ ospf_send_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_list, uint lsa ospf_send_to(ifa, n->ip); } - return lsa_count; + return i; } void ospf_rxmt_lsupd(struct ospf_proto *p, struct ospf_neighbor *n) { - const uint max = 128; + uint max = 2 * n->ifa->flood_queue_size; struct top_hash_entry *entries[max]; struct top_hash_entry *ret, *nxt, *en; uint i = 0; + /* ASSERT((n->state >= NEIGHBOR_EXCHANGE) && !EMPTY_SLIST(n->lsrtl)); */ + WALK_SLIST_DELSAFE(ret, nxt, n->lsrtl) { if (i == max) @@ -398,14 +480,15 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_proto *p = ifa->oa->po; const char *err_dsc = NULL; uint plen, err_val = 0; - int skip_lsreq = 0; - n->want_lsreq = 0; /* RFC 2328 13. */ plen = ntohs(pkt->length); - if (plen < (ospf_lsupd_hdrlen(p) + sizeof(struct ospf_lsa_header))) - DROP("too short", plen); + if (plen < ospf_lsupd_hdrlen(p)) + { + LOG_PKT("Bad LSUPD packet from nbr %R on %s - %s (%u)", n->rid, ifa->ifname, "too short", plen); + return; + } OSPF_PACKET(ospf_dump_lsupd, pkt, "LSUPD packet received from nbr %R on %s", n->rid, ifa->ifname); @@ -417,8 +500,8 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, ospf_neigh_sm(n, INM_HELLOREC); /* Questionable */ - uint offset, bound, i, lsa_count; - ospf_lsupd_body(p, pkt, &offset, &bound, &lsa_count); + uint offset, i, lsa_count; + ospf_lsupd_body(p, pkt, &offset, &lsa_count); for (i = 0; i < lsa_count; i++) { @@ -426,7 +509,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, struct top_hash_entry *en; u32 lsa_len, lsa_type, lsa_domain; - if (offset > bound) + if ((offset + sizeof(struct ospf_lsa_header)) > plen) DROP("too short", plen); /* LSA header in network order */ @@ -492,7 +575,6 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, if (en && ((now - en->inst_time) < MINLSARRIVAL)) { OSPF_TRACE(D_EVENTS, "Skipping LSA received in less that MinLSArrival"); - skip_lsreq = 1; continue; } @@ -559,7 +641,9 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, continue; } - /* FIXME pg145 (6) */ + /* 13. (6) - received LSA is in Link state request list (but not newer) */ + if (ospf_hash_find_entry(n->lsrqh, en) != NULL) + DROP1("error in LSA database exchange"); /* 13. (7) - received LSA is same */ if (lsa_comp(&lsa, &en->lsa) == CMP_SAME) @@ -576,7 +660,6 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, else ospf_enqueue_lsack(n, lsa_n, ACKL_DIRECT); - skip_lsreq = 1; continue; } @@ -601,15 +684,24 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, /* Send direct LSACKs */ ospf_send_lsack(p, n, ACKL_DIRECT); + /* Send enqueued LSAs immediately, do not wait for flood_event */ + if (ev_active(p->flood_event)) + { + ev_postpone(p->flood_event); + ospf_flood_event(p); + } + /* - * In loading state, we should ask for another batch of LSAs. This is only - * vaguely mentioned in RFC 2328. We send a new LSREQ only if the current - * LSUPD actually removed some entries from LSA request list (want_lsreq) and - * did not contain duplicate or early LSAs (skip_lsreq). The first condition - * prevents endless floods, the second condition helps with flow control. + * During loading, we should ask for another batch of LSAs. This is only + * vaguely mentioned in RFC 2328. We send a new LSREQ if all requests sent in + * the last packet were already answered and/or removed from the LS request + * list and therefore lsrqi is pointing to the first node of the list. */ - if ((n->state == NEIGHBOR_LOADING) && n->want_lsreq && !skip_lsreq) + if (!EMPTY_SLIST(n->lsrql) && (n->lsrqi == SHEAD(n->lsrql))) + { ospf_send_lsreq(p, n); + tm_start(n->lsrq_timer, n->ifa->rxmtint); + } return; @@ -617,7 +709,7 @@ drop: LOG_PKT("Bad LSUPD packet from nbr %R on %s - %s (%u)", n->rid, ifa->ifname, err_dsc, err_val); - // XXXX realy? - ospf_neigh_sm(n, INM_SEQMIS); + /* Malformed LSUPD - there is no defined error event, we abuse BadLSReq */ + ospf_neigh_sm(n, INM_BADLSREQ); return; } diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index c75b1b93..c5d44dec 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -23,14 +23,18 @@ const char *ospf_inm_names[] = { static int can_do_adj(struct ospf_neighbor *n); -static void neighbor_timer_hook(timer * timer); -static void rxmt_timer_hook(timer * timer); -static void ackd_timer_hook(timer * t); +static void inactivity_timer_hook(timer * timer); +static void dbdes_timer_hook(timer *t); +static void lsrq_timer_hook(timer *t); +static void lsrt_timer_hook(timer *t); +static void ackd_timer_hook(timer *t); + static void init_lists(struct ospf_proto *p, struct ospf_neighbor *n) { s_init_list(&(n->lsrql)); + n->lsrqi = SHEAD(n->lsrql); n->lsrqh = ospf_top_new(p, n->pool); s_init_list(&(n->lsrtl)); @@ -57,9 +61,16 @@ release_lsrtl(struct ospf_proto *p, struct ospf_neighbor *n) static void reset_lists(struct ospf_proto *p, struct ospf_neighbor *n) { - release_lsrtl(p,n); + release_lsrtl(p, n); ospf_top_free(n->lsrqh); ospf_top_free(n->lsrth); + ospf_reset_lsack_queue(n); + + tm_stop(n->dbdes_timer); + tm_stop(n->lsrq_timer); + tm_stop(n->lsrt_timer); + tm_stop(n->ackd_timer); + init_lists(p, n); } @@ -80,30 +91,14 @@ ospf_neighbor_new(struct ospf_iface *ifa) init_lists(p, n); s_init(&(n->dbsi), &(p->lsal)); - n->inactim = tm_new(pool); - n->inactim->data = n; - n->inactim->randomize = 0; - n->inactim->hook = neighbor_timer_hook; - n->inactim->recurrent = 0; - DBG("%s: Installing inactivity timer.\n", p->p.name); - - n->rxmt_timer = tm_new(pool); - n->rxmt_timer->data = n; - n->rxmt_timer->randomize = 0; - n->rxmt_timer->hook = rxmt_timer_hook; - n->rxmt_timer->recurrent = ifa->rxmtint; - tm_start(n->rxmt_timer, n->ifa->rxmtint); - DBG("%s: Installing rxmt timer.\n", p->p.name); - - n->ackd_timer = tm_new(pool); - n->ackd_timer->data = n; - n->ackd_timer->randomize = 0; - n->ackd_timer->hook = ackd_timer_hook; - n->ackd_timer->recurrent = ifa->rxmtint / 2; init_list(&n->ackl[ACKL_DIRECT]); init_list(&n->ackl[ACKL_DELAY]); - tm_start(n->ackd_timer, n->ifa->rxmtint / 2); - DBG("%s: Installing ackd timer.\n", p->p.name); + + n->inactim = tm_new_set(pool, inactivity_timer_hook, n, 0, 0); + n->dbdes_timer = tm_new_set(pool, dbdes_timer_hook, n, 0, ifa->rxmtint); + n->lsrq_timer = tm_new_set(pool, lsrq_timer_hook, n, 0, ifa->rxmtint); + n->lsrt_timer = tm_new_set(pool, lsrt_timer_hook, n, 0, ifa->rxmtint); + n->ackd_timer = tm_new_set(pool, ackd_timer_hook, n, 0, ifa->rxmtint / 2); return (n); } @@ -188,6 +183,9 @@ ospf_neigh_chstate(struct ospf_neighbor *n, u8 state) n->dds++; n->myimms = DBDES_IMMS; + + tm_start(n->dbdes_timer, 0); + tm_start(n->ackd_timer, ifa->rxmtint / 2); } if (state > NEIGHBOR_EXSTART) @@ -253,16 +251,16 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) /* Add MaxAge LSA entries to retransmission list */ ospf_add_flushed_to_lsrt(p, n); - - /* FIXME: Why is this here ? */ - ospf_reset_lsack_queue(n); } else bug("NEGDONE and I'm not in EXSTART?"); break; case INM_EXDONE: - ospf_neigh_chstate(n, NEIGHBOR_LOADING); + if (!EMPTY_SLIST(n->lsrql)) + ospf_neigh_chstate(n, NEIGHBOR_LOADING); + else + ospf_neigh_chstate(n, NEIGHBOR_FULL); break; case INM_LOADDONE: @@ -270,23 +268,15 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) break; case INM_ADJOK: - switch (n->state) + /* Can In build adjacency? */ + if ((n->state == NEIGHBOR_2WAY) && can_do_adj(n)) { - case NEIGHBOR_2WAY: - /* Can In build adjacency? */ - if (can_do_adj(n)) - { - ospf_neigh_chstate(n, NEIGHBOR_EXSTART); - } - break; - default: - if (n->state >= NEIGHBOR_EXSTART) - if (!can_do_adj(n)) - { - reset_lists(p, n); - ospf_neigh_chstate(n, NEIGHBOR_2WAY); - } - break; + ospf_neigh_chstate(n, NEIGHBOR_EXSTART); + } + else if ((n->state >= NEIGHBOR_EXSTART) && !can_do_adj(n)) + { + reset_lists(p, n); + ospf_neigh_chstate(n, NEIGHBOR_2WAY); } break; @@ -566,12 +556,12 @@ find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip) } static void -neighbor_timer_hook(timer * timer) +inactivity_timer_hook(timer * timer) { struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data; struct ospf_proto *p = n->ifa->oa->po; - OSPF_TRACE(D_EVENTS, "Inactivity timer expired for neighbor %R on %s", + OSPF_TRACE(D_EVENTS, "Inactivity timer expired for nbr %R on %s", n->rid, n->ifa->ifname); ospf_neigh_sm(n, INM_INACTTIM); } @@ -584,7 +574,7 @@ ospf_neigh_bfd_hook(struct bfd_request *req) if (req->down) { - OSPF_TRACE(D_EVENTS, "BFD session down for neighbor %R on %s", + OSPF_TRACE(D_EVENTS, "BFD session down for nbr %R on %s", n->rid, n->ifa->ifname); ospf_neigh_sm(n, INM_INACTTIM); } @@ -605,11 +595,60 @@ ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd) } +static void +dbdes_timer_hook(timer *t) +{ + struct ospf_neighbor *n = t->data; + struct ospf_proto *p = n->ifa->oa->po; + + // OSPF_TRACE(D_EVENTS, "DBDES timer expired for nbr %R on %s", n->rid, n->ifa->ifname); + + if (n->state == NEIGHBOR_EXSTART) + ospf_send_dbdes(p, n); + + if ((n->state == NEIGHBOR_EXCHANGE) && (n->myimms & DBDES_MS)) + ospf_rxmt_dbdes(p, n); +} + +static void +lsrq_timer_hook(timer *t) +{ + struct ospf_neighbor *n = t->data; + struct ospf_proto *p = n->ifa->oa->po; + + // OSPF_TRACE(D_EVENTS, "LSRQ timer expired for nbr %R on %s", n->rid, n->ifa->ifname); + + if ((n->state >= NEIGHBOR_EXCHANGE) && !EMPTY_SLIST(n->lsrql)) + ospf_send_lsreq(p, n); +} + +static void +lsrt_timer_hook(timer *t) +{ + struct ospf_neighbor *n = t->data; + struct ospf_proto *p = n->ifa->oa->po; + + // OSPF_TRACE(D_EVENTS, "LSRT timer expired for nbr %R on %s", n->rid, n->ifa->ifname); + + if ((n->state >= NEIGHBOR_EXCHANGE) && !EMPTY_SLIST(n->lsrtl)) + ospf_rxmt_lsupd(p, n); +} + +static void +ackd_timer_hook(timer *t) +{ + struct ospf_neighbor *n = t->data; + struct ospf_proto *p = n->ifa->oa->po; + + ospf_send_lsack(p, n, ACKL_DELAY); +} + + void ospf_sh_neigh_info(struct ospf_neighbor *n) { struct ospf_iface *ifa = n->ifa; - char *pos = "ptp "; + char *pos = "PtP "; char etime[6]; int exp, sec, min; @@ -628,55 +667,13 @@ ospf_sh_neigh_info(struct ospf_neighbor *n) if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA)) { if (n->rid == ifa->drid) - pos = "dr "; + pos = "DR "; else if (n->rid == ifa->bdrid) - pos = "bdr "; + pos = "BDR "; else - pos = "other"; + pos = "Other"; } cli_msg(-1013, "%-1R\t%3u\t%s/%s\t%-5s\t%-10s %-1I", n->rid, n->priority, ospf_ns_names[n->state], pos, etime, ifa->ifname, n->ip); } - -static void -rxmt_timer_hook(timer *t) -{ - struct ospf_neighbor *n = t->data; - struct ospf_proto *p = n->ifa->oa->po; - - DBG("%s: RXMT timer fired on %s for neigh %I\n", - p->p.name, n->ifa->ifname, n->ip); - - switch (n->state) - { - case NEIGHBOR_EXSTART: - ospf_send_dbdes(p, n); - return; - - case NEIGHBOR_EXCHANGE: - if (n->myimms & DBDES_MS) - ospf_rxmt_dbdes(p, n); - case NEIGHBOR_LOADING: - ospf_send_lsreq(p, n); - return; - - case NEIGHBOR_FULL: - /* LSA retransmissions */ - if (!EMPTY_SLIST(n->lsrtl)) - ospf_rxmt_lsupd(p, n); - return; - } -} - -static void -ackd_timer_hook(timer *t) -{ - struct ospf_neighbor *n = t->data; - struct ospf_proto *p = n->ifa->oa->po; - - DBG("%s: ACKD timer fired on %s for neigh %I\n", - p->p.name, n->ifa->ifname, n->ip); - - ospf_send_lsack(p, n, ACKL_DELAY); -} diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index 01e53922..dab7aab8 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -235,11 +235,7 @@ ospf_start(struct proto *P) p->asbr = c->asbr; p->ecmp = c->ecmp; p->tick = c->tick; - p->disp_timer = tm_new(P->pool); - p->disp_timer->data = p; - p->disp_timer->randomize = 0; - p->disp_timer->hook = ospf_disp; - p->disp_timer->recurrent = p->tick; + p->disp_timer = tm_new_set(P->pool, ospf_disp, p, 0, p->tick); tm_start(p->disp_timer, 1); p->lsab_size = 256; p->lsab_used = 0; @@ -252,6 +248,10 @@ ospf_start(struct proto *P) p->gr = ospf_top_new(p, P->pool); s_init_list(&(p->lsal)); + p->flood_event = ev_new(P->pool); + p->flood_event->hook = ospf_flood_event; + p->flood_event->data = p; + p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 }; p->log_lsa_tbf = (struct tbf){ .rate = 4, .burst = 20 }; @@ -1439,14 +1439,14 @@ ospf_sh_lsadb(struct lsadb_show_data *ld) break; } cli_msg(-1017, ""); - cli_msg(-1017," Type LS ID Router Age Sequence Checksum"); + cli_msg(-1017," Type LS ID Router Sequence Age Checksum"); last_dscope = dscope; last_domain = hea[i]->domain; } - cli_msg(-1017," %04x %-15R %-15R %5u %08x %04x", - lsa_type, lsa->id, lsa->rt, lsa->age, lsa->sn, lsa->checksum); + cli_msg(-1017," %04x %-15R %-15R %08x %5u %04x", + lsa_type, lsa->id, lsa->rt, lsa->sn, lsa->age, lsa->checksum); } cli_msg(0, ""); } diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index e535287c..c324f431 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -231,6 +231,7 @@ struct ospf_proto byte asbr; /* May i originate any ext/NSSA lsa? */ byte ecmp; /* Maximal number of nexthops in ECMP route, or 0 */ struct ospf_area *backbone; /* If exists */ + event *flood_event; /* Event for flooding LS updates */ void *lsab; /* LSA buffer used when originating router LSAs */ int lsab_size, lsab_used; linpool *nhpool; /* Linpool used for next hops computed in SPF */ @@ -317,8 +318,11 @@ struct ospf_iface struct top_hash_entry *link_lsa; /* Originated link LSA */ struct top_hash_entry *net_lsa; /* Originated network LSA */ struct top_hash_entry *pxn_lsa; /* Originated prefix LSA */ + struct top_hash_entry **flood_queue; /* LSAs queued for LSUPD */ u8 update_link_lsa; u8 update_net_lsa; + u16 flood_queue_used; /* The current number of LSAs in flood_queue */ + u16 flood_queue_size; /* The maximum number of LSAs in flood_queue */ int fadj; /* Number of fully adjacent neighbors */ list nbma_list; u8 priority; /* A router priority for DR election */ @@ -354,7 +358,6 @@ struct ospf_neighbor ip_addr ip; /* IP of it's interface */ u8 priority; /* Priority */ u8 adj; /* built adjacency? */ - u8 want_lsreq; /* Set to 1 when lsrql was shortened during LSUPD */ u32 options; /* Options received */ /* Entries dr and bdr store IP addresses in OSPFv2 and router IDs in @@ -373,6 +376,7 @@ struct ospf_neighbor */ slist lsrql; /* slist of struct top_hash_entry from n->lsrqh */ struct top_graph *lsrqh; + struct top_hash_entry *lsrqi; /* Pointer to the first unsent node in lsrql */ /* Link state retransmission list, controls LSA retransmission during flood. * Entries added as sent in lsupd packets, removed when received in lsack packets. @@ -380,7 +384,9 @@ struct ospf_neighbor */ slist lsrtl; /* slist of struct top_hash_entry from n->lsrth */ struct top_graph *lsrth; - timer *rxmt_timer; /* RXMT timer */ + timer *dbdes_timer; /* DBDES exchange timer */ + timer *lsrq_timer; /* LSA request timer */ + timer *lsrt_timer; /* LSA retransmission timer */ list ackl[2]; #define ACKL_DIRECT 0 #define ACKL_DELAY 1 @@ -940,7 +946,9 @@ void ospf_receive_lsreq(struct ospf_packet *pkt, struct ospf_iface *ifa, struct /* lsupd.c */ void ospf_dump_lsahdr(struct ospf_proto *p, struct ospf_lsa_header *lsa_n); void ospf_dump_common(struct ospf_proto *p, struct ospf_packet *pkt); +void ospf_lsa_lsrt_down_(struct top_hash_entry *en, struct ospf_neighbor *n, struct top_hash_entry *ret); void ospf_add_flushed_to_lsrt(struct ospf_proto *p, struct ospf_neighbor *n); +void ospf_flood_event(void *ptr); int ospf_flood_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neighbor *from); int ospf_send_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_list, uint lsa_count, struct ospf_neighbor *n); void ospf_rxmt_lsupd(struct ospf_proto *p, struct ospf_neighbor *n); diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index a545c7fd..fb63e61c 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -260,7 +260,7 @@ ospf_rx_hook(sock *sk, int len) * link-local src address, but does not enforce it. Strange. */ if (dst_mcast && !src_local) - LOG_PKT_WARN("Multicast packet received from not-link-local %I via %s", + LOG_PKT_WARN("Multicast packet received from non-link-local %I via %s", sk->faddr, ifa->ifname); } @@ -396,8 +396,7 @@ found: if (!n && (pkt->type != HELLO_P)) { - // XXXX format - OSPF_TRACE(D_PACKETS, "Non-HELLO packet received from unknown neighbor %R on %s (%I)", + OSPF_TRACE(D_PACKETS, "Non-HELLO packet received from unknown nbr %R on %s, src %I", rid, ifa->ifname, sk->faddr); return 1; } diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 42bf9c45..c21c23d5 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -404,9 +404,6 @@ ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en) void ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en) { - OSPF_TRACE(D_EVENTS, "Flushing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x", - en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn); - if (en->next_lsa_body) { mb_free(en->next_lsa_body); @@ -418,6 +415,9 @@ ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en) if (en->lsa.age == LSA_MAXAGE) return; + OSPF_TRACE(D_EVENTS, "Flushing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x", + en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn); + en->lsa.age = LSA_MAXAGE; ospf_flood_lsa(p, en, NULL); @@ -433,8 +433,12 @@ ospf_clear_lsa(struct ospf_proto *p, struct top_hash_entry *en) /* * Called by ospf_update_lsadb() as part of LSA flushing process. * Flushed LSA was acknowledged by neighbors and we can free its content. + * The log message is for 'remove' - we hide empty LSAs from users. */ + OSPF_TRACE(D_EVENTS, "Removing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x", + en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn); + if (en->lsa.sn == LSA_MAXSEQNO) en->lsa.sn = LSA_ZEROSEQNO; @@ -1143,6 +1147,9 @@ ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 m ospf_originate_lsa(p, &lsa); } +static struct top_hash_entry * +ospf_hash_find_(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type); + static void ospf_flush_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf) { @@ -1152,7 +1159,7 @@ ospf_flush_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf) u32 dom = oa ? oa->areaid : 0; u32 id = ort_to_lsaid(p, nf); - en = ospf_hash_find(p->gr, dom, id, p->router_id, type); + en = ospf_hash_find_(p->gr, dom, id, p->router_id, type); if (!en || (en->nf != nf)) return; @@ -1795,8 +1802,8 @@ ospf_top_rehash(struct top_graph *f, int step) ospf_top_ht_free(oldt); } -struct top_hash_entry * -ospf_hash_find(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type) +static struct top_hash_entry * +ospf_hash_find_(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type) { struct top_hash_entry *e; e = f->hash_table[ospf_top_hash(f, domain, lsa, rtr, type)]; @@ -1805,6 +1812,14 @@ ospf_hash_find(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type) e->lsa_type != type || e->domain != domain)) e = e->next; + return e; +} + +struct top_hash_entry * +ospf_hash_find(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type) +{ + struct top_hash_entry *e = ospf_hash_find_(f, domain, lsa, rtr, type); + /* Hide hash entry with empty lsa_body */ return (e && e->lsa_body) ? e : NULL; } diff --git a/sysdep/unix/timer.h b/sysdep/unix/timer.h index 17450322..99d43932 100644 --- a/sysdep/unix/timer.h +++ b/sysdep/unix/timer.h @@ -34,6 +34,12 @@ extern bird_clock_t now; /* Relative, monotonic time in seconds */ extern bird_clock_t now_real; /* Time in seconds since fixed known epoch */ extern bird_clock_t boot_time; +static inline int +tm_active(timer *t) +{ + return t->expires != 0; +} + static inline bird_clock_t tm_remains(timer *t) { From cd3b700393144796c9dead54b6ac2fc31aaa5272 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 3 Nov 2014 14:53:59 +0100 Subject: [PATCH 08/11] Bugfix to OSPF reconfiguration. --- proto/ospf/iface.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 4dfb74fb..dfc892aa 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -1009,6 +1009,9 @@ ospf_walk_matching_iface_patts(struct ospf_proto *p, struct ospf_mip_walk *s) WALK_LIST(s->oa, p->area_list) { + if (s->oa->marked) + continue; + WALK_LIST(s->ip, s->oa->ac->patt_list) { id = s->ip->instance_id; From ac9dc669d80cbfeea3d0cdba5ec7354c804b1ae9 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 3 Nov 2014 20:35:25 +0100 Subject: [PATCH 09/11] Bugfix in latest OSPF changes. --- proto/ospf/iface.c | 6 ++++++ proto/ospf/topology.c | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index dfc892aa..656184c6 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -281,10 +281,16 @@ void ospf_iface_remove(struct ospf_iface *ifa) { struct ospf_proto *p = ifa->oa->po; + int i; if (ifa->type == OSPF_IT_VLINK) OSPF_TRACE(D_EVENTS, "Removing vlink to %R via area %R", ifa->vid, ifa->voa->areaid); + /* Release LSAs from flood queue */ + if (!ifa->stub) + for (i = 0; i < ifa->flood_queue_used; i++) + ifa->flood_queue[i]->ret_count--; + ospf_iface_sm(ifa, ISM_DOWN); rem_node(NODE ifa); rfree(ifa->pool); diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index c21c23d5..0613d34d 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -1177,9 +1177,7 @@ use_gw_for_fwaddr(struct ospf_proto *p, ip_addr gw, struct iface *iface) WALK_LIST(ifa, p->iface_list) if ((ifa->iface == iface) && - ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA)) && - (!ospf_is_v2(p) || ipa_in_net(gw, ifa->addr->prefix, ifa->addr->pxlen)) && - (!ifa->cf->stub)) + (!ospf_is_v2(p) || ipa_in_net(gw, ifa->addr->prefix, ifa->addr->pxlen))) return 1; return 0; From f92e6ab364d50900bc9104af4539f6ea42b25c43 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 3 Nov 2014 20:35:58 +0100 Subject: [PATCH 10/11] Changes order of iface/addr/neigh event hooks. Now the order is: Up -> iface, addr, neigh Down -> neigh, addr, iface It fixes the case when an iface appears, related static routes are activated and exported to OSPF before the iface notification and therefore forwarding addresses are not encoded in generated external LSAs. --- nest/iface.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/nest/iface.c b/nest/iface.c index 298698a7..4d73c2a4 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -150,7 +150,7 @@ ifa_send_notify(struct proto *p, unsigned c, struct ifa *a) } static void -ifa_notify_change_dep(unsigned c, struct ifa *a) +ifa_notify_change_(unsigned c, struct ifa *a) { struct proto *p; @@ -163,8 +163,13 @@ ifa_notify_change_dep(unsigned c, struct ifa *a) static inline void ifa_notify_change(unsigned c, struct ifa *a) { - neigh_ifa_update(a); - ifa_notify_change_dep(c, a); + if (c & IF_CHANGE_DOWN) + neigh_ifa_update(a); + + ifa_notify_change_(c, a); + + if (c & IF_CHANGE_UP) + neigh_ifa_update(a); } static inline void @@ -201,14 +206,14 @@ if_notify_change(unsigned c, struct iface *i) if_dump(i); #endif - if (c & IF_CHANGE_UP) - neigh_if_up(i); + if (c & IF_CHANGE_DOWN) + neigh_if_down(i); if (c & IF_CHANGE_DOWN) WALK_LIST(a, i->addrs) { a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS); - ifa_notify_change_dep(IF_CHANGE_DOWN, a); + ifa_notify_change_(IF_CHANGE_DOWN, a); } WALK_LIST(p, active_proto_list) @@ -218,14 +223,14 @@ if_notify_change(unsigned c, struct iface *i) WALK_LIST(a, i->addrs) { a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS); - ifa_notify_change_dep(IF_CHANGE_UP, a); + ifa_notify_change_(IF_CHANGE_UP, a); } + if (c & IF_CHANGE_UP) + neigh_if_up(i); + if ((c & (IF_CHANGE_UP | IF_CHANGE_DOWN | IF_CHANGE_LINK)) == IF_CHANGE_LINK) neigh_if_link(i); - - if (c & IF_CHANGE_DOWN) - neigh_if_down(i); } static unsigned @@ -251,8 +256,8 @@ if_change_flags(struct iface *i, unsigned flags) } /** - * if_delete - remove interface - * @old: interface + * if_delete - remove interface + * @old: interface * * This function is called by the low-level platform dependent code * whenever it notices an interface disappears. It is just a shorthand @@ -676,7 +681,7 @@ iface_patt_match(struct iface_patt *ifp, struct iface *i, struct ifa *a) if ((a->flags & IA_PEER) && ipa_in_net(a->opposite, p->prefix, p->pxlen)) return pos; - + continue; } From ec2194fa7a20a2768ca0027b5f3c024f0a251866 Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Wed, 5 Nov 2014 11:07:25 +0100 Subject: [PATCH 11/11] Version 1.5.0pre --- NEWS | 4 ++++ sysdep/config.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index cd3e2b81..117f4d7b 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +Version 1.5.0pre (2014-11-05) - Not for production + o Major OSPF protocol redesign + o RFC 6549 - OSPFv2 multi-instance extension + Version 1.4.5 (2014-10-06) o New 'show route noexport' command option. o Port option for BGP sessions. diff --git a/sysdep/config.h b/sysdep/config.h index 02f62762..36cf8391 100644 --- a/sysdep/config.h +++ b/sysdep/config.h @@ -7,7 +7,7 @@ #define _BIRD_CONFIG_H_ /* BIRD version */ -#define BIRD_VERSION "1.4.5" +#define BIRD_VERSION "1.5.0pre" /* Include parameters determined by configure script */ #include "sysdep/autoconf.h"