diff options
Diffstat (limited to 'proto')
-rw-r--r-- | proto/ospf/config.Y | 128 | ||||
-rw-r--r-- | proto/ospf/dbdes.c | 595 | ||||
-rw-r--r-- | proto/ospf/dbdes.h | 17 | ||||
-rw-r--r-- | proto/ospf/hello.c | 549 | ||||
-rw-r--r-- | proto/ospf/hello.h | 21 | ||||
-rw-r--r-- | proto/ospf/iface.c | 621 | ||||
-rw-r--r-- | proto/ospf/iface.h | 36 | ||||
-rw-r--r-- | proto/ospf/lsack.c | 225 | ||||
-rw-r--r-- | proto/ospf/lsack.h | 25 | ||||
-rw-r--r-- | proto/ospf/lsalib.c | 560 | ||||
-rw-r--r-- | proto/ospf/lsalib.h | 61 | ||||
-rw-r--r-- | proto/ospf/lsreq.c | 175 | ||||
-rw-r--r-- | proto/ospf/lsreq.h | 17 | ||||
-rw-r--r-- | proto/ospf/lsupd.c | 954 | ||||
-rw-r--r-- | proto/ospf/lsupd.h | 25 | ||||
-rw-r--r-- | proto/ospf/neighbor.c | 398 | ||||
-rw-r--r-- | proto/ospf/neighbor.h | 22 | ||||
-rw-r--r-- | proto/ospf/ospf.c | 1175 | ||||
-rw-r--r-- | proto/ospf/ospf.h | 1076 | ||||
-rw-r--r-- | proto/ospf/packet.c | 586 | ||||
-rw-r--r-- | proto/ospf/packet.h | 28 | ||||
-rw-r--r-- | proto/ospf/rt.c | 1062 | ||||
-rw-r--r-- | proto/ospf/rt.h | 58 | ||||
-rw-r--r-- | proto/ospf/topology.c | 2365 | ||||
-rw-r--r-- | proto/ospf/topology.h | 215 |
25 files changed, 5437 insertions, 5557 deletions
diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 478529bc..268b9539 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -19,9 +19,11 @@ static struct ospf_area_config *this_area; static struct nbma_node *this_nbma; static list *this_nets; static struct area_net_config *this_pref; -static struct ospf_stubnet_config *this_stubnet; +static struct ospf_stubnet_config *this_stubnet; + +static inline int ospf_cfg_is_v2(void) { return OSPF_CFG->ospf2; } +static inline int ospf_cfg_is_v3(void) { return ! OSPF_CFG->ospf2; } -#ifdef OSPFv2 static void ospf_iface_finish(void) { @@ -30,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)) @@ -38,21 +43,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 +51,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 +70,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 +98,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 +130,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 <t> opttext %type <ld> lsadb_args @@ -146,8 +144,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,10 +158,11 @@ 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"); } + | 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 ; @@ -171,9 +170,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 +194,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 '}' @@ -218,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: @@ -244,15 +243,15 @@ 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 ; } - | 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 @@ -266,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(); } ; @@ -280,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 ; } @@ -292,20 +289,21 @@ 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 ; } - | RX BUFFER NORMAL { OSPF_PATT->rx_buffer = 0; } - | RX BUFFER LARGE { OSPF_PATT->rx_buffer = OSPF_MAX_PKT_SIZE; } + | 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"); } | TX tos { OSPF_PATT->tx_tos = $2; } | TX PRIORITY expr { OSPF_PATT->tx_priority = $3; } @@ -314,7 +312,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: @@ -348,8 +346,8 @@ nbma_eligible: /* empty */ { $$ = 0; } | 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); @@ -357,7 +355,7 @@ nbma_item: IPA nbma_eligible ';' this_nbma->eligible=$2; } ; - + ospf_iface_start: { this_ipatt = cfg_allocz(sizeof(struct ospf_iface_patt)); @@ -369,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; @@ -384,12 +380,12 @@ ospf_iface_start: ospf_instance_id: /* empty */ - | INSTANCE expr { set_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_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: /* empty */ diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c index 6b291344..2b076157 100644 --- a/proto/ospf/dbdes.c +++ b/proto/ospf/dbdes.c @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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; -}; - -#define hton_opt(X) X -#define ntoh_opt(X) X -#endif + struct ospf_lsa_header lsas[]; +}; -#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) +{ + 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) { - struct ospf_packet *op = &pkt->ospf_packet; + uint plen = ntohs(pkt->length); + uint hlen = ospf_dbdes_hdrlen(p); - 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)); + *body = ((void *) pkt) + hlen; + *count = (plen - hlen) / sizeof(struct ospf_lsa_header); +} - struct ospf_lsa_header *plsa = (void *) (pkt + 1); - unsigned int i, j; +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; - j = (ntohs(op->length) - sizeof(struct ospf_dbdes_packet)) / - sizeof(struct ospf_lsa_header); + ASSERT(pkt->type == DBDES_P); + ospf_dump_common(p, pkt); - for (i = 0; i < j; i++) - ospf_dump_lsahdr(p, plsa + i); + 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,319 @@ 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_proto *p, 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; + /* RFC 2328 10.8 */ - /* FIXME ??? */ - if ((oa->rt == NULL) || (EMPTY_LIST(po->lsal))) - update_rt_lsa(oa); + if (n->ifa->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); - - OSPF_PACKET(ospf_dump_dbdes, pkt, "DBDES packet sent to %I via %s", n->ip, ifa->ifname); - ospf_send_to(ifa, n->ip); + case NEIGHBOR_EXSTART: + n->myimms |= DBDES_I; + + /* 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; - - 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; - } + ospf_prepare_dbdes(p, n, 1); - pkt = n->ldd_buffer; - op = (struct ospf_packet *) pkt; + /* Send prepared packet */ + ospf_do_send_dbdes(p, n); - 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); + /* Master should restart RXMT timer for each DBDES exchange */ + if (n->myimms & DBDES_MS) + tm_start(n->rxmt_timer, n->ifa->rxmtint); - 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 */ + /* Send last packet */ + ospf_do_send_dbdes(p, n); + break; + } +} + + +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; + uint i, lsa_count; + + ospf_dbdes_body(p, pkt, &lsas, &lsa_count); - OSPF_PACKET(ospf_dump_dbdes, n->ldd_buffer, "DBDES packet sent to %I via %s", n->ip, ifa->ifname); + for (i = 0; i < lsa_count; i++) + { + struct top_hash_entry *en, *req; + struct ospf_lsa_header lsa; + u32 lsa_type, lsa_domain; - sk_set_tbuf(ifa->sk, n->ldd_buffer); - ospf_send_to(ifa, n->ip); - sk_set_tbuf(ifa->sk, NULL); + lsa_ntoh_hdr(lsas + i, &lsa); + lsa_get_type_domain(&lsa, ifa, &lsa_type, &lsa_domain); - if(n->myimms.bit.ms) tm_start(n->rxmt_timer, n->ifa->rxmtint); /* Restart timer */ + /* RFC 2328 10.6 and RFC 5340 4.2.2 */ - if (!n->myimms.bit.ms) + if (!lsa_type) { - if ((n->myimms.bit.m == 0) && (n->imms.bit.m == 0) && - (n->state == NEIGHBOR_EXCHANGE)) - { - ospf_neigh_sm(n, INM_EXDONE); - } + log(L_WARN "%s: Bad DBDES from %I - LSA of unknown type", p->p.name, n->ip); + goto err; } - break; - default: /* Ignore it */ - break; - } -} + 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; + } -static void -ospf_dbdes_reqladd(struct ospf_dbdes_packet *ps, 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; + /* 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; + } - op = (struct ospf_packet *) ps; + /* 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; + } - plsa = (void *) (ps + 1); + 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); - j = (ntohs(op->length) - sizeof(struct ospf_dbdes_packet)) / - sizeof(struct ospf_lsa_header); + if (!SNODE_VALID(req)) + s_add_tail(&n->lsrql, SNODE req); - for (i = 0; i < j; 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)) - { - /* 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); - } + req->lsa = lsa; + req->lsa_body = LSA_BODY_DUMMY; } } + + 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; + + /* RFC 2328 10.6 */ - unsigned int size = ntohs(ps_i->length); - if (size < sizeof(struct ospf_dbdes_packet)) + 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) && (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))) + 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 ((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(p, 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(p, 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)) - { + + 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_dbdes_send(n, 1); - } - + ospf_send_dbdes(p, 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(p, 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(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", 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 <feela@network.cz> - * - * 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..3aeb9f9a 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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; -}; -#endif + u32 neighbors[]; +}; -#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; -}; -#endif - - -void -ospf_hello_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, - struct ospf_neighbor *n, ip_addr faddr) -{ - 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); - } + u32 neighbors[]; +}; - 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) +ospf_send_hello(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 - - pkt->helloint = ntohs(ifa->helloint); - pkt->priority = ifa->priority; - -#ifdef OSPFv3 - pkt->iface_id = htonl(ifa->iface_id); - - pkt->options3 = ifa->oa->options >> 16; - pkt->options2 = ifa->oa->options >> 8; -#endif - pkt->options = ifa->oa->options; - -#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 + if (ospf_is_v2(p)) + { + struct ospf_hello2_packet *ps = (void *) pkt; + + 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)); + + 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)); + + length = sizeof(struct ospf_hello2_packet); + neighbors = ps->neighbors; + } + else + { + struct ospf_hello3_packet *ps = (void *) pkt; + + 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; + } - /* Fill all neighbors */ i = 0; + max = (ospf_pkt_maxsize(ifa) - length) / sizeof(u32); + /* Fill all neighbors */ 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) { @@ -334,7 +143,7 @@ ospf_hello_send(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) @@ -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()"); } +} - OSPF_TRACE(D_PACKETS, "HELLO packet sent via %s", ifa->ifname); + +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); + } + + 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) + { + 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 */ + 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 <feela@network.cz> - * - * 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..892e8a77 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -2,12 +2,15 @@ * BIRD -- OSPF * * (c) 1999--2005 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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,32 +21,33 @@ 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); } 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 @@ -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,30 +116,31 @@ 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; + 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; + goto err; if (sk_join_group(sk, ifa->all_routers) < 0) - goto err; + 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; - ifa->state = state; + 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_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]); + ifa->state = 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,8 +591,11 @@ 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); @@ -617,34 +609,22 @@ 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); } 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. - */ - 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 : 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; @@ -653,50 +633,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 +677,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,7 +697,7 @@ 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; @@ -806,7 +779,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 +788,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 +808,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 +825,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 +848,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 +913,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,131 +943,119 @@ 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) -{ - return (struct ospf_iface_patt *) iface_patt_find(&ac->patt_list, a->iface, 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 */ +}; -void -ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) +static int +ospf_walk_matching_iface_patts(struct ospf_proto *p, struct ospf_mip_walk *s) { - struct proto_ospf *po = (struct proto_ospf *) p; - - if (a->flags & IA_SECONDARY) - return; + int id; - if (a->scope <= SCOPE_LINK) - return; + if (s->ip) + goto step; - /* In OSPFv2, we create OSPF iface for each address. */ - if (flags & IF_CHANGE_UP) + WALK_LIST(s->oa, p->area_list) { - int done = 0; - struct ospf_area *oa; - WALK_LIST(oa, po->area_list) + WALK_LIST(s->ip, s->oa->ac->patt_list) { - struct ospf_iface_patt *ip; - if (ip = ospf_iface_patt_find(oa->ac, a)) + id = s->ip->instance_id; + if (BIT32_TEST(s->ignore, id)) + continue; + + if (iface_patt_match(&s->ip->i, s->iface, s->a)) { - if (!done) - ospf_iface_new(oa, a, ip); - done++; - } - } + /* Now we matched ifa/iface/instance_id for the first time in current area */ + BIT32_SET(s->ignore, id); - if (done > 1) - log(L_WARN "%s: Interface %s (IP %I) matches for multiple areas", p->name, a->iface->name, a->ip); - } + /* If we already found it in previous areas, ignore it and add warning */ + if (BIT32_TEST(s->active, id)) + { s->warn = 1; continue; } - 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() */ + 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; } + static struct ospf_iface * -ospf_iface_find_by_key(struct ospf_area *oa, struct ifa *a) +ospf_iface_find_by_key(struct ospf_proto *p, struct ifa *a, int instance_id) { struct ospf_iface *ifa; - WALK_LIST(ifa, oa->po->iface_list) - if ((ifa->addr == a) && (ifa->oa == oa) && (ifa->type != OSPF_IT_VLINK)) + + 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_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) +ospf_ifa_notify2(struct proto *P, uint flags, struct ifa *a) { - struct proto *p = &oa->po->proto; - struct ospf_iface_patt *ip; - struct iface *iface; - struct ifa *a; - - WALK_LIST(iface, iface_list) - { - if (! (iface->flags & IF_UP)) - continue; - - WALK_LIST(a, iface->addrs) - { - if (a->flags & IA_SECONDARY) - continue; + struct ospf_proto *p = (struct ospf_proto *) P; - if (a->scope <= SCOPE_LINK) - continue; + if (a->flags & IA_SECONDARY) + return; - if (ip = ospf_iface_patt_find(oa->ac, a)) - { - /* Main inner loop */ - struct ospf_iface *ifa = ospf_iface_find_by_key(oa, a); - if (ifa) - { - if (ospf_iface_reconfigure(ifa, ip)) - continue; + if (a->scope <= SCOPE_LINK) + return; - /* Hard restart */ - log(L_INFO "%s: Restarting interface %s (%I/%d) in area %R", - p->name, ifa->ifname, a->prefix, a->pxlen, oa->areaid); - ospf_iface_shutdown(ifa); - ospf_iface_remove(ifa); - } - - ospf_iface_new(oa, a, ip); - } - } + /* In OSPFv2, we create OSPF iface for each address. */ + if (flags & IF_CHANGE_UP) + { + struct ospf_mip_walk s = { .iface = a->iface, .a = a }; + while (ospf_walk_matching_iface_patts(p, &s)) + 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) -{ - 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; + if (flags & IF_CHANGE_DOWN) + { + struct ospf_iface *ifa, *ifx; + 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() */ + } } void -ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) +ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a) { - struct proto_ospf *po = (struct proto_ospf *) p; + struct ospf_proto *p = (struct ospf_proto *) P; if (a->flags & IA_SECONDARY) return; @@ -1099,71 +1069,79 @@ ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) { 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); + struct ospf_mip_walk s = { .iface = a->iface }; + while (ospf_walk_matching_iface_patts(p, &s)) + ospf_iface_new(s.oa, a, s.ip); } 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)) + WALK_LIST_DELSAFE(ifa, ifx, p->iface_list) + if ((ifa->addr == a) && (ifa->type != OSPF_IT_VLINK)) ospf_iface_remove(ifa); - /* See a note in ospf_iface_notify() */ - } } } else { struct ospf_iface *ifa; - WALK_LIST(ifa, po->iface_list) - { + WALK_LIST(ifa, p->iface_list) if (ifa->iface == a->iface) { - schedule_rt_lsa(ifa->oa); - /* Event 5 from RFC5340 4.4.3. */ - schedule_link_lsa(ifa); - return; + /* RFC 5340 4.4.3 Event 5 - prefix added/deleted */ + ospf_notify_link_lsa(ifa); + ospf_notify_rt_lsa(ifa->oa); } - } } } -static struct ospf_iface * -ospf_iface_find_by_key(struct ospf_area *oa, struct ifa *a, int iid) + +static void +ospf_reconfigure_ifaces2(struct ospf_proto *p) { - 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; + struct iface *iface; + struct ifa *a; - return NULL; + WALK_LIST(iface, iface_list) + { + if (! (iface->flags & IF_UP)) + continue; + + WALK_LIST(a, iface->addrs) + { + if (a->flags & IA_SECONDARY) + continue; + + if (a->scope <= SCOPE_LINK) + continue; + + 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(p, a, s.ip->instance_id); + if (ifa) + { + 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->p.name, ifa->ifname, a->prefix, a->pxlen, s.oa->areaid); + ospf_iface_shutdown(ifa); + ospf_iface_remove(ifa); + } + + ospf_iface_new(s.oa, a, s.ip); + } + } + } } -void -ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) +static void +ospf_reconfigure_ifaces3(struct ospf_proto *p) { - struct proto *p = &oa->po->proto; - struct ospf_iface_patt *ip; struct iface *iface; struct ifa *a; @@ -1180,38 +1158,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 +1213,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 +1227,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 +1245,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 +1269,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); } @@ -1325,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/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 <feela@network.cz> - * - * 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..5cac3f69 100644 --- a/proto/ospf/lsack.c +++ b/proto/ospf/lsack.c @@ -1,7 +1,9 @@ /* * BIRD -- OSPF * - * (c) 2000-2004 Ondrej Filip <feela@network.cz> + * (c) 2000--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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,171 @@ #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[]; +}; +*/ -char *s_queue[] = { "direct", "delayed" }; +struct lsa_node +{ + node n; + struct ospf_lsa_header lsa; +}; -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; + WALK_LIST_FIRST(no, n->ackl[ACKL_DELAY]) + { + rem_node(NODE no); + mb_free(no); + } +} - pk = ospf_tx_buffer(ifa); - op = &pk->ospf_packet; +static inline void +ospf_send_lsack_(struct ospf_proto *p, struct ospf_neighbor *n, int queue) +{ + struct ospf_iface *ifa = n->ifa; + struct ospf_lsa_header *lsas; + struct ospf_packet *pkt; + struct lsa_node *no; + uint i, lsa_max, length; - ospf_pkt_fill_hdr(n->ifa, pk, LSACK_P); - h = pk->lsh; + /* RFC 2328 13.5 */ - while (!EMPTY_LIST(n->ackl[queue])) + 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 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++; + 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); - 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); - - 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); - } - - ospf_pkt_fill_hdr(n->ifa, pk, LSACK_P); - i = 0; - } - } } - 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_send_lsack(struct ospf_proto *p, struct ospf_neighbor *n, int queue) +{ + while (!EMPTY_LIST(n->ackl[queue])) + ospf_send_lsack_(p, 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++) + ospf_neigh_sm(n, INM_HELLOREC); /* Not in RFC */ + + ospf_lsack_body(p, pkt, &lsas, &lsa_count); + for (i = 0; i < lsa_count; 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 */ + lsa_ntoh_hdr(&lsas[i], &lsa); + lsa_get_type_domain(&lsa, n->ifa, &lsa_type, &lsa_domain); - if (lsa_comp(&lsa, &en->lsa) != CMP_SAME) /* pg 156 */ - { - if ((lsa.sn == LSA_MAXSEQNO) && (lsa.age == LSA_MAXAGE)) - continue; + 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) + { 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 <feela@network.cz> - * - * 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..579d13e8 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -2,103 +2,21 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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)); } /* @@ -231,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 @@ -292,79 +299,231 @@ lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2) return CMP_SAME; } -#define HDRLEN sizeof(struct ospf_lsa_header) -static int -lsa_validate_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body) +static inline int +lsa_walk_rt2(struct ospf_lsa_rt_walk *rt) { - unsigned int i, max; - - if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_rt))) + if (rt->buf >= rt->bufend) return 0; - struct ospf_lsa_rt_link *rtl = (struct ospf_lsa_rt_link *) (body + 1); - max = lsa_rt_count(lsa); + 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); -#ifdef OSPFv2 - if (body->links != max) - return 0; -#endif + rt->type = l->type; + rt->metric = l->metric; + rt->id = l->id; + rt->data = l->data; + return 1; +} - for (i = 0; i < max; i++) +static inline int +lsa_walk_rt3(struct ospf_lsa_rt_walk *rt) +{ + while (rt->buf >= rt->bufend) { - u8 type = rtl[i].type; - if (!((type == LSART_PTP) || - (type == LSART_NET) || -#ifdef OSPFv2 - (type == LSART_STUB) || -#endif - (type == LSART_VLNK))) + 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_net(struct ospf_lsa_header *lsa, struct ospf_lsa_net *body UNUSED) +lsa_validate_rt2(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body) { - if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_net))) + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_rt))) + return 0; + + uint i = 0; + void *buf = body; + void *bufend = buf + lsa->length - HDRLEN; + buf += sizeof(struct ospf_lsa_rt); + + while (buf < bufend) + { + 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; } -#ifdef OSPFv2 static int -lsa_validate_sum(struct ospf_lsa_header *lsa, struct ospf_lsa_sum *body) +lsa_validate_rt3(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body) { - if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum))) + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_rt))) return 0; - /* First field should have TOS = 0, we ignore other TOS fields */ - if ((body->metric & LSA_SUM_TOS) != 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; +} + +static int +lsa_validate_net(struct ospf_lsa_header *lsa, struct ospf_lsa_net *body UNUSED) +{ + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_net))) 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) +lsa_validate_sum2(struct ospf_lsa_header *lsa, struct ospf_lsa_sum2 *body) { - if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext))) + 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_EXT_TOS) != 0) + if ((body->metric & LSA_SUM2_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_sum3_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_sum3_rt *body) +{ + if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum3_rt))) + return 0; + + return 1; +} static int -lsa_validate_sum_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_sum_rt *body) +lsa_validate_ext2(struct ospf_lsa_header *lsa, struct ospf_lsa_ext2 *body) { - if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum_rt))) + 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_ext(struct ospf_lsa_header *lsa, struct ospf_lsa_ext *body) +lsa_validate_ext3(struct ospf_lsa_header *lsa, struct ospf_lsa_ext3 *body) { - if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext) + 4)) + 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++) @@ -436,7 +607,7 @@ lsa_validate_pxlist(struct ospf_lsa_header *lsa, u32 pxcount, unsigned int offse u8 pxl = pxlen((u32 *) (pbuf + offset)); if (pxl > MAX_PREFIX_LENGTH) return 0; - + offset += IPV6_PREFIX_SPACE(pxl); } @@ -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 <feela@network.cz> + * (c) 1999--2000 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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..a6c0cf24 100644 --- a/proto/ospf/lsreq.c +++ b/proto/ospf/lsreq.c @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 2000--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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); + + *body = ((void *) pkt) + hlen; + *count = (plen - hlen) / sizeof(struct ospf_lsreq_header); +} - ASSERT(op->type == LSREQ_P); - ospf_dump_common(p, op); +static void +ospf_dump_lsreq(struct ospf_proto *p, struct ospf_packet *pkt) +{ + struct ospf_lsreq_header *lsrs; + uint i, lsr_count; - unsigned int i, j; - j = (ntohs(op->length) - sizeof(struct ospf_lsreq_packet)) / - sizeof(struct ospf_lsreq_header); + ASSERT(pkt->type == LSREQ_P); + ospf_dump_common(p, pkt); - 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)); + 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_lsreq_send(struct ospf_neighbor *n) +ospf_send_lsreq(struct ospf_proto *p, struct ospf_neighbor *n) { - snode *sn; + 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,81 @@ 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); + + /* We send smaller LSREQ to prevent multiple LSACKs as answer */ + lsr_max = lsr_max / 4; - for (; i > 0; 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; - - 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; - } + struct ospf_proto *p = ifa->oa->po; + struct ospf_lsreq_header *lsrs; + uint i, lsr_count; - 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); + /* RFC 2328 10.7 */ + + /* 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 */ + + ospf_lsreq_body(p, pkt, &lsrs, &lsr_count); - lsh = ps->lsh; - init_list(&uplist); - upslab = sl_new(n->pool, sizeof(struct l_lsr_head)); + struct top_hash_entry *en, *entries[lsr_count]; - lsano = (size - sizeof(struct ospf_lsreq_packet)) / - sizeof(struct ospf_lsreq_header); - for (i = 0; i < lsano; lsh++, i++) + 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 <feela@network.cz> - * - * 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..13ae54d1 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 2000--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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,405 @@ #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; + return ospf_pkt_hdrlen(p) + 4; /* + u32 lsa count field */ +} - j = ntohl(pkt->lsano); - for (i = 0; i < j; i++) - { - if (offset > bound) - { - log(L_TRACE "%s: LSA invalid", p->name); - return; - } +static inline u32 +ospf_lsupd_get_lsa_count(struct ospf_packet *pkt, uint hdrlen) +{ + u32 *c = ((void *) pkt) + hdrlen - 4; + return ntohl(*c); +} - struct ospf_lsa_header *lsa = (void *) (pbuf + offset); - ospf_dump_lsahdr(p, lsa); - lsalen = ntohs(lsa->length); - offset += lsalen; +static inline void +ospf_lsupd_set_lsa_count(struct ospf_packet *pkt, uint hdrlen, u32 val) +{ + u32 *c = ((void *) pkt) + hdrlen - 4; + *c = htonl(val); +} - if (((lsalen % 4) != 0) || (lsalen <= sizeof(struct ospf_lsa_header))) - { - log(L_TRACE "%s: LSA invalid", p->name); - return; - } - } +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); } +static void +ospf_dump_lsupd(struct ospf_proto *p, struct ospf_packet *pkt) +{ + uint offset, bound, i, lsa_count, lsalen; -#ifdef OSPFv2 + ASSERT(pkt->type == LSUPD_P); + ospf_dump_common(p, pkt); -int -ospf_lsa_flooding_allowed(struct ospf_lsa_header *lsa, u32 domain, struct ospf_iface *ifa) -{ - if (lsa->type == LSA_T_EXT) + ospf_lsupd_body(p, pkt, &offset, &bound, &lsa_count); + for (i = 0; i < lsa_count; i++) + { + if (offset > bound) { - if (ifa->type == OSPF_IT_VLINK) - return 0; - if (!oa_is_ext(ifa->oa)) - return 0; - return 1; + log(L_TRACE "%s: LSA invalid", p->p.name); + return; } - else - return ifa->oa->areaid == domain; -} -#else /* OSPFv3 */ + struct ospf_lsa_header *lsa = ((void *) pkt) + offset; + ospf_dump_lsahdr(p, lsa); + lsalen = ntohs(lsa->length); + offset += lsalen; -static int -unknown_lsa_type(struct ospf_lsa_header *lsa) -{ - switch (lsa->type) + if (((lsalen % 4) != 0) || (lsalen <= sizeof(struct ospf_lsa_header))) { - 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; + log(L_TRACE "%s: LSA invalid", p->p.name); + return; } + } } -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; +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); - /* 4.5.2 (Case 2) */ - if (unknown_lsa_type(lsa) && !(lsa->type & LSA_UBIT)) - scope = LSA_SCOPE_LINK; + if (!SNODE_VALID(ret)) + { + en->ret_count++; + s_add_tail(&n->lsrtl, SNODE ret); + } - switch (scope) - { - case LSA_SCOPE_LINK: - return ifa->iface_id == domain; + ret->lsa = en->lsa; + ret->lsa_body = LSA_BODY_DUMMY; +} - case LSA_SCOPE_AREA: - return ifa->oa->areaid == domain; +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); - case LSA_SCOPE_AS: - if (ifa->type == OSPF_IT_VLINK) - return 0; - if (!oa_is_ext(ifa->oa)) - return 0; - return 1; + if (ret) + { + en->ret_count--; + s_rem_node(SNODE ret); + ospf_hash_delete(n->lsrth, ret); + return 1; + } - default: - log(L_ERR "LSA with invalid scope"); - return 0; - } + return 0; +} + +void +ospf_add_flushed_to_lsrt(struct ospf_proto *p, struct ospf_neighbor *n) +{ + 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); } -#endif + +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 - * @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_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 proto_ospf *po, - struct ospf_neighbor *n, struct ospf_lsa_header *hn, - struct ospf_lsa_header *hh, u32 domain, int rtl) +ospf_flood_lsa(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; + + /* RFC 2328 13.3 */ - /* 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); + n->want_lsreq = 1; + + 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; + continue; + + back = 1; } + /* 13.3 (5) - finally flood the packet */ + ospf_send_lsupd_to_ifa(p, en, ifa); + } + + return back; +} + +static uint +ospf_prepare_lsupd(struct ospf_proto *p, struct ospf_iface *ifa, + struct top_hash_entry **lsa_list, uint lsa_count) +{ + struct ospf_packet *pkt; + uint hlen, pos, i, maxsize; + + pkt = ospf_tx_buffer(ifa); + hlen = ospf_lsupd_hdrlen(p); + maxsize = ospf_pkt_maxsize(ifa); + + ospf_pkt_fill_hdr(ifa, pkt, LSUPD_P); + pos = hlen; + + for (i = 0; i < lsa_count; i++) + { + struct top_hash_entry *en = lsa_list[i]; + uint len = en->lsa.length; + + if ((pos + len) > maxsize) { - 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) + /* 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) { - /* 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; + /* 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; } - pk = ospf_tx_buffer(ifa); - op = &pk->ospf_packet; + /* TX buffer could be reallocated */ + pkt = ospf_tx_buffer(ifa); + } - ospf_pkt_fill_hdr(ifa, pk, LSUPD_P); - pk->lsano = htonl(1); + 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)); - lh = (struct ospf_lsa_header *) (pk + 1); + pos += len; + } - /* Copy LSA into the packet */ - if (hn) - { - memcpy(lh, hn, ntohs(hn->length)); - } - else - { - u8 *help; - struct top_hash_entry *en; + ospf_lsupd_set_lsa_count(pkt, hlen, i); + pkt->length = htons(pos); - 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)); - } + return i; +} - 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); +static void +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); - op->length = htons(len); + if (!c) /* Too large LSA */ + return; - OSPF_PACKET(ospf_dump_lsupd, pk, "LSUPD packet flooded via %s", ifa->ifname); + OSPF_PACKET(ospf_dump_lsupd, ospf_tx_buffer(ifa), + "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; + 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); +} - 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; +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; - case OSPF_IT_PTP: - ospf_send_to_all(ifa); - break; + for (i = 0; i < lsa_count; i += c) + { + c = ospf_prepare_lsupd(p, ifa, lsa_list + i, lsa_count - i); - case OSPF_IT_PTMP: - ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE); - break; + if (!c) /* Too large LSA */ + { i++; continue; } - case OSPF_IT_VLINK: - ospf_send_to(ifa, ifa->vip); - break; + OSPF_PACKET(ospf_dump_lsupd, ospf_tx_buffer(ifa), + "LSUPD packet sent to %I via %s", n->ip, ifa->ifname); - default: - bug("Bug in ospf_lsupd_flood()"); - } - } + ospf_send_to(ifa, n->ip); } - return retval; + + return lsa_count; } -void /* I send all I received in LSREQ */ -ospf_lsupd_send_list(struct ospf_neighbor *n, list * l) +void +ospf_rxmt_lsupd(struct ospf_proto *p, struct ospf_neighbor *n) { - 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; - - pkt = ospf_tx_buffer(n->ifa); - buf = (void *) pkt; + const uint max = 128; + struct top_hash_entry *entries[max]; + struct top_hash_entry *ret, *nxt, *en; + uint i = 0; - lsr = HEAD(*l); - while(NODE_NEXT(lsr)) + WALK_SLIST_DELSAFE(ret, nxt, n->lsrtl) { - /* Prepare the packet */ - ospf_pkt_fill_hdr(n->ifa, pkt, LSUPD_P); - len = sizeof(struct ospf_lsupd_packet); - lsano = 0; + if (i == max) + break; - /* Fill the packet with LSAs */ - while(NODE_NEXT(lsr)) + en = ospf_hash_find_entry(p->gr, ret); + if (!en) { - 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) - { - /* 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; - } + /* 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); - 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; + s_rem_node(SNODE ret); + ospf_hash_delete(n->lsrth, ret); - /* 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; - } + continue; + } - /* TX buffer could be reallocated */ - pkt = ospf_tx_buffer(n->ifa); - buf = (void *) pkt; - } + entries[i] = en; + i++; + } - /* 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); - } + ospf_send_lsupd(p, entries, i, n); +} - if (lsano == 0) - break; - /* 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); - } +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_lsupd_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, +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. */ - struct ospf_neighbor *ntmp; - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; - unsigned int i, max, sendreq = 1; + int skip_lsreq = 0; + n->want_lsreq = 0; - unsigned int size = ntohs(ps_i->length); - if (size < (sizeof(struct ospf_lsupd_packet) + sizeof(struct ospf_lsa_header))) + 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 +419,215 @@ 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; - - if ((offset > size) || ((lsalen % 4) != 0) || - (lsalen <= sizeof(struct ospf_lsa_header))) + /* 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 "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 checksum: %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); + log(L_WARN "%s: Received LSA with AS scope in stub area from %I", p->p.name, 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)) + /* 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 AS scope in stub area from %I", n->ip); + log(L_WARN "%s: Received rt-summary-LSA in stub area from %I", p->p.name, n->ip); continue; } - /* 4.5.1 (3) */ - if (scope == LSA_SCOPE_RES) + /* RFC 5340 4.5.1 (3) */ + if (LSA_SCOPE(lsa_type) == LSA_SCOPE_RES) { - log(L_WARN "Received LSA with invalid scope from %I", n->ip); + log(L_WARN "%s: Received LSA with invalid scope from %I", p->p.name, n->ip); continue; } -#endif - - ntohlsah(lsa, &lsatmp); - - 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); - } + OSPF_TRACE(D_EVENTS, "Skipping LSA received in less that MinLSArrival"); + skip_lsreq = 1; continue; } - /* pg 144 (5a) */ - if (lsadb && ((now - lsadb->inst_t) <= MINLSARRIVAL)) /* FIXME: test for flooding? */ + /* 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); + + if (lsa_validate(&lsa, lsa_type, ospf_is_v2(p), body) == 0) { - OSPF_TRACE(D_EVENTS, "Skipping LSA received in less that MINLSARRIVAL"); - sendreq = 0; + log(L_WARN "%s: Received invalid LSA from %I", p->p.name, n->ip); + mb_free(body); 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) + /* 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)))) { - 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); - } + OSPF_TRACE(D_EVENTS, "Received unexpected self-originated LSA"); + ospf_advance_lsa(p, en, &lsa, lsa_type, lsa_domain, body); + continue; } - /* pg 144 (5b) */ - if (ospf_lsupd_flood(po, n, lsa, &lsatmp, domain, 1) == 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); - } + /* 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. + */ + if (en) + ospf_lsa_lsrt_down(en, n); + +#if 0 + /* + * 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); +#endif - if ((lsatmp.age == LSA_MAXAGE) && (lsatmp.sn == LSA_MAXSEQNO) - && lsadb && can_flush_lsa(po)) - { - flush_lsa(lsadb, po); - schedule_rtcalc(po); - continue; - } /* FIXME lsack? */ + /* 13. (5d) - install new LSA into database */ + en = ospf_install_lsa(p, &lsa, lsa_type, lsa_domain, body); - /* 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)); + /* RFC 5340 4.4.3 Events 6+7 - new Link LSA received */ + if (lsa_type == LSA_T_LINK) + ospf_notify_net_lsa(ifa); - /* 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); - mb_free(body); - continue; - } + /* 13. (5b) - flood new LSA */ + int flood_back = ospf_flood_lsa(p, en, n); - lsadb = lsa_install_new(po, &lsatmp, domain, body); - DBG("New LSA installed in DB\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); -#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 + /* 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); - } - } - else + /* 13.5. - schedule ACKs (tbl 19, cases 3+4) */ + if (implicit_ack) { - /* pg145 (7b) */ - ospf_lsack_enqueue(n, lsa, ACKL_DIRECT); + if ((ifa->state == OSPF_IS_BACKUP) && (n->rid == ifa->drid)) + ospf_enqueue_lsack(n, lsa_n, ACKL_DELAY); } - sendreq = 0; - continue; - } + else + ospf_enqueue_lsack(n, lsa_n, ACKL_DIRECT); - /* pg145 (8) */ - if ((lsadb->lsa.age == LSA_MAXAGE) && (lsadb->lsa.sn == LSA_MAXSEQNO)) - { + skip_lsreq = 1; continue; } + /* 13. (8) - received LSA is older */ { - 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 direct LSAs */ - ospf_lsack_send(n, ACKL_DIRECT); + /* Seqnum is wrapping, wait until it is flushed */ + if ((en->lsa.age == LSA_MAXAGE) && (en->lsa.sn == LSA_MAXSEQNO)) + continue; - if (sendreq && (n->state == NEIGHBOR_LOADING)) - { - ospf_lsreq_send(n); /* Ask for another part of neighbor's database */ + /* Send newer local copy back to neighbor */ + /* FIXME - check for MinLSArrival ? */ + ospf_send_lsupd(p, &en, 1, 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); + /* Send direct LSACKs */ + ospf_send_lsack(p, n, ACKL_DIRECT); + + /* + * 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/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 <feela@network.cz> - * - * 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..ee1e8d0f 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -1,7 +1,9 @@ /* * BIRD -- OSPF * - * (c) 1999 - 2004 Ondrej Filip <feela@network.cz> + * (c) 1999--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -26,42 +28,51 @@ 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); 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); - s_init(&(n->lsrqi), &(n->lsrql)); + n->lsrqh = ospf_top_new(p, n->pool); s_init_list(&(n->lsrtl)); - n->lsrth = ospf_top_new(n->pool); - s_init(&(n->lsrti), &(n->lsrtl)); + 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. - * 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 -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 * 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; @@ -71,15 +82,15 @@ ospf_neighbor_new(struct ospf_iface *ifa) n->csn = 0; n->state = NEIGHBOR_DOWN; - init_lists(n); - s_init(&(n->dbsi), &(po->lsal)); + 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->name); + DBG("%s: Installing inactivity timer.\n", p->p.name); n->rxmt_timer = tm_new(pool); n->rxmt_timer->data = n; @@ -87,7 +98,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 +108,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 +121,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) - { - struct ospf_iface *ifa = n->ifa; - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; + OSPF_TRACE(D_EVENTS, "Neighbor %I changes state from %s to %s", + n->ip, ospf_ns[old_state], ospf_ns[state]); - n->state = state; + 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) && (old_state < NEIGHBOR_2WAY)) + ospf_iface_sm(ifa, ISM_NEICH); + if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY)) + ospf_iface_sm(ifa, ISM_NEICH); - 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); + /* Increase number of partial adjacencies */ + if ((state == NEIGHBOR_EXCHANGE) || (state == NEIGHBOR_LOADING)) + p->padj++; - 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); - } + /* Decrease number of partial adjacencies */ + if ((old_state == NEIGHBOR_EXCHANGE) || (old_state == NEIGHBOR_LOADING)) + p->padj--; - 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; + /* 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) + { + /* RFC 2328 12.4 Event 4 - neighbor enters/leaves Full state */ + ospf_notify_rt_lsa(ifa->oa); + ospf_notify_net_lsa(ifa); + + /* 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 +196,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 +241,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 +249,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 +276,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 +293,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 +310,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 +337,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 +348,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 +372,26 @@ 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); - } + /* 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) { @@ -398,38 +406,42 @@ 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; } break; + case INM_SEQMIS: case INM_BADLSREQ: if (n->state >= NEIGHBOR_EXCHANGE) { - reset_lists(n); + reset_lists(p, n); neigh_chstate(n, NEIGHBOR_EXSTART); } break; + 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; + 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 +450,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 +463,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 +481,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); - } - - rem_node(NODE & me); + ospf_iface_chstate(ifa, OSPF_IS_DROTHER); - 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 +550,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 +561,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)) { @@ -573,24 +570,24 @@ 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."); + 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); } } @@ -643,61 +640,46 @@ 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 proto *p = &n->ifa->oa->po->proto; - struct top_hash_entry *en; - - DBG("%s: RXMT timer fired on interface %s for neigh: %I.\n", - p->name, n->ifa->ifname, n->ip); + struct ospf_neighbor *n = t->data; + struct ospf_proto *p = n->ifa->oa->po; - if(n->state < NEIGHBOR_EXSTART) return; + 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) + switch (n->state) { - ospf_dbdes_send(n, 1); + case NEIGHBOR_EXSTART: + ospf_send_dbdes(p, 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(p, n, 0); + case NEIGHBOR_LOADING: + ospf_send_lsreq(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)); + case NEIGHBOR_FULL: + /* LSA retransmissions */ + if (!EMPTY_SLIST(n->lsrtl)) + ospf_rxmt_lsupd(p, n); + return; - 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; } } 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/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 <feela@network.cz> - * - * 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..470a8633 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -2,109 +2,107 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ /** * 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.). * - * 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 - * &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 + * 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_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 - * (&proto_ospf->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. - * - * 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. + * (&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. * - * 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. + * + * 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 <stdlib.h> #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,83 +112,81 @@ 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) { - 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; + ospf_notify_rt_lsa(oa); +} - if (reconf) - ospf_ifaces_reconfigure(oa, ac); +static void +ospf_flush_area(struct ospf_proto *p, u32 areaid) +{ + struct top_hash_entry *en; + + 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); fib_free(&oa->enet_fib); @@ -198,87 +194,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, P->pool); + s_init_list(&(p->lsal)); WALK_LIST(ac, c->area_list) - ospf_area_add(po, ac, 0); + ospf_area_add(p, ac); 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 +289,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 +298,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)); - - 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; - - return p; + 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 = 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; } /* If new is better return 1 */ @@ -379,119 +377,56 @@ 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) +ospf_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); + /* Process LSA DB */ + 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 + * @P: OSPF protocol instance * @new: the new route * @attrs: list of attributes * @pool: pool for allocation of attributes @@ -499,15 +434,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 +454,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); @@ -538,27 +472,27 @@ 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 * 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 +502,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; - - 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; - } + struct ospf_proto *p = (struct ospf_proto *) P; - /* 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 +514,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 +532,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 +584,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,12 +611,12 @@ 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); } /** * 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 @@ -723,95 +625,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); } + /* 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); - + ospf_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,47 +725,47 @@ 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) { ifano++; - WALK_LIST(n, ifa->neigh_list) - { + WALK_LIST(n, ifa->neigh_list) + { nno++; if (n->state == NEIGHBOR_FULL) adjno++; - } + } } } @@ -880,8 +786,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" : ""); @@ -894,8 +800,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" : ""); @@ -907,20 +813,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 +845,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 +858,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,23 +919,21 @@ 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; } - else + else { if (lsa1->rt < lsa2->rt) return -1; 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,11 +941,9 @@ 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 +979,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; - - if (first) - { - cli_msg(-1016, ""); - cli_msg(-1016, "\trouter %R", he->lsa.rt); - show_lsa_distance(he); - } + struct ospf_lsa_rt_walk rtl; + cli_msg(-1016, ""); + cli_msg(-1016, "\trouter %R", he->lsa.rt); + show_lsa_distance(he); - 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_VLNK) + cli_msg(-1016, "\t\tvlink %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); + 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_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 && (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; + + 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 +1058,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 - - if (rt_fwaddr_valid) - bsprintf(str_via, " via %I", rt_fwaddr); + lsa_parse_ext(he, ospf2, &rt); + + 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 +1116,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,71 +1136,70 @@ 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; } - /* 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 = 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) - { - case LSA_T_RT: - case LSA_T_NET: - accept = 1; - break; + if (he->lsa.age == LSA_MAXAGE) + continue; - case LSA_T_SUM_NET: - case LSA_T_SUM_RT: - case LSA_T_NSSA: -#ifdef OSPFv3 - case LSA_T_PREFIX: -#endif - accept = verbose; - break; + switch (he->lsa_type) + { + case LSA_T_RT: + case LSA_T_NET: + accept = 1; + break; - case LSA_T_EXT: - if (verbose) - { - he->domain = 1; /* Abuse domain field to mark the LSA */ - hex[jx++] = he; - } - default: - accept = 0; + 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) + { + 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 +1230,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 +1247,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 +1324,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 +1339,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; @@ -1487,12 +1350,12 @@ 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; - 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 +1363,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; - unsigned int i, j; + struct ospf_proto *p = (struct ospf_proto *) proto_get_named(ld->name, &proto_ospf); + 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() */ - 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)) @@ -1549,26 +1414,26 @@ 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, ""); 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 +1442,24 @@ 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..f464a3ed 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 1999--2005 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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 @@ -78,99 +102,174 @@ do { if ((p->debug & D_PACKETS) || OSPF_FORCE_DEBUG) \ struct ospf_config { struct proto_config c; - unsigned tick; - byte rfc1583; - byte stub_router; - byte merge_external; - byte abr; + uint tick; + u8 ospf2; + u8 rfc1583; + u8 stub_router; + u8 merge_external; + u8 instance_id; + u8 abr; + u8 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 { 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 +struct nbma_node { node n; - u32 areaid; - u32 default_cost; /* Cost of default route for stub 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; -/* Option flags */ +#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 OPT_E 0x02 -#define OPT_N 0x08 -#define OPT_DC 0x20 +/* 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 */ -#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 +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) */ +}; -/* VEB flags are are stored together with options in 'u32 options' */ -#define OPT_RT_B (0x01 << 24) -#define OPT_RT_E (0x02 << 24) -#define OPT_RT_V (0x04 << 24) -#define OPT_RT_NT (0x10 << 24) +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 OPT_PX_NU 0x01 -#define OPT_PX_LA 0x02 -#define OPT_PX_P 0x08 -#define OPT_PX_DN 0x10 -#endif struct ospf_iface @@ -184,7 +283,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 */ @@ -199,154 +298,189 @@ 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 */ }; -struct ospf_md5 +struct ospf_neighbor { - u16 zero; - u8 keyid; - u8 len; - u32 csn; -}; + 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 */ -union ospf_auth -{ - u8 password[8]; - struct ospf_md5 md5; + 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) */ }; -/* 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 */ +/* 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 -/* Area IDs */ -#define BACKBONE 0 +/* 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 */ -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 +/* 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 */ -#ifdef OSPFv2 +#define TRANS_OFF 0 +#define TRANS_ON 1 +#define TRANS_WAIT 2 /* Waiting before the end of translation */ -struct ospf_packet -{ - u8 version; - u8 type; - u16 length; - u32 routerid; - u32 areaid; - u16 checksum; - u16 autype; - union ospf_auth u; -}; -#else /* OSPFv3 packet descriptions */ +/* 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 { @@ -356,41 +490,37 @@ struct ospf_packet u32 routerid; u32 areaid; u16 checksum; - u8 instance_id; - u8 zero; + u8 instance_id; /* See RFC 6549 */ + u8 autype; /* Undefined for OSPFv3 */ }; - -#endif - - - - -struct ospf_lsa_header +struct ospf_md5 { - u16 age; /* LS Age */ -#define LSA_MAXAGE 3600 /* 1 hour */ -#define LSA_CHECKAGE 300 /* 5 minutes */ -#define LSA_MAXAGEDIFF 900 /* 15 minutes */ + u16 zero; + u8 keyid; + u8 len; + u32 csn; +}; -#ifdef OSPFv2 - u8 options; - u8 type; +union ospf_auth +{ + u8 password[8]; + struct ospf_md5 md5; +}; -#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 +/* 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 LSA_SCOPE_AREA 0x2000 -#define LSA_SCOPE_AS 0x4000 -#define LSA_SCOPE(lsa) (((lsa)->type == LSA_T_EXT) ? LSA_SCOPE_AS : LSA_SCOPE_AREA) +#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) -#else /* OSPFv3 */ - u16 type; #define LSA_T_RT 0x2001 #define LSA_T_NET 0x2002 @@ -401,6 +531,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 +540,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 padding; + u8 no_tos; u16 metric; #else u16 metric; - u8 padding; + u8 no_tos; u8 type; #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 +struct ospf_lsa_rt2_tos { - u32 options; +#ifdef CPU_BIG_ENDIAN + u8 tos; + u8 padding; + u16 metric; +#else + u16 metric; + u8 padding; + u8 tos; +#endif }; -struct ospf_lsa_rt_link +struct ospf_lsa_rt3_link { #ifdef CPU_BIG_ENDIAN u8 type; @@ -508,35 +642,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,35 +712,20 @@ 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 +static inline uint lsa_net_count(struct ospf_lsa_header *lsa) { return (lsa->length - sizeof(struct ospf_lsa_header) - sizeof(struct ospf_lsa_net)) / 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) @@ -599,6 +741,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) @@ -611,6 +754,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; } @@ -645,257 +789,173 @@ 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; }; -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 */ - union imms imms; /* I, M, Master/slave received */ - 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 */ - 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 +#define SH_ROUTER_SELF 0xffffffff - 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; - 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) */ +struct lsadb_show_data { + struct symbol *name; /* Protocol to request data from */ + u16 type; /* LSA Type, 0 -> all */ + u16 scope; /* Scope, 0 -> all, hack to handle link scope as 1 */ + u32 area; /* Specified for area scope */ + u32 lsid; /* LSA ID, 0 -> all */ + u32 router; /* Advertising router, 0 -> all */ }; -/* 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 */ +#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) -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 */ - byte origrt; /* Rt lsa origination scheduled? */ - byte trcap; /* Transit capability? */ - byte marked; /* Used in OSPF reconfigure */ - byte translate; /* Translator state (TRANS_*), for NSSA ABR */ - timer *translator_timer; /* For NSSA translator switch */ - struct proto_ospf *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 */ +/* ospf.c */ +void ospf_schedule_rtcalc(struct ospf_proto *p); -struct proto_ospf -{ - struct proto proto; - 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 */ - struct fib rtf; /* Routing table */ - 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 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) */ -}; +static inline void ospf_notify_rt_lsa(struct ospf_area *oa) +{ oa->update_rt_lsa = 1; } -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; +static inline void ospf_notify_net_lsa(struct ospf_iface *ifa) +{ ifa->update_net_lsa = 1; } -#define OSPF_RXBUF_MINSIZE 256 /* Minimal allowed size */ - u16 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 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; +static inline void ospf_notify_link_lsa(struct ospf_iface *ifa) +{ ifa->update_link_lsa = 1; } -#ifdef OSPFv2 - list *passwords; -#endif -#ifdef OSPFv3 - u8 instance_id; -#endif -}; +#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; } -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 ospf_proto *p, u32 aid); -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 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); -#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_lsadb(struct lsadb_show_data *ld); -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); +/* 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); +} -#define SH_ROUTER_SELF 0xffffffff +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); } -struct lsadb_show_data { - struct symbol *name; /* Protocol to request data from */ - u16 type; /* LSA Type, 0 -> all */ - u16 scope; /* Scope, 0 -> all, hack to handle link scope as 1 */ - u32 area; /* Specified for area scope */ - u32 lsid; /* LSA ID, 0 -> all */ - u32 router; /* Advertising router, 0 -> all */ -}; +static inline void * ospf_tx_buffer(struct ospf_iface *ifa) +{ return ifa->sk->tbuf; } -void ospf_sh_lsadb(struct lsadb_show_data *ld); +/* 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_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 */ +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); +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); + +/* 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_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); -#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) #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..d64d7d6b 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 1999--2005 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -13,232 +15,215 @@ 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; + } + + /* 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); - 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"); + 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; - } + case OSPF_AUTH_SIMPLE: + pass = password_find(ifa->passwords, 1); + if (!pass) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: no password found"); + 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; - } + 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; - tail = ((void *)pkt) + ntohs(pkt->length); + 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 (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 (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 (!pass) + if (n) + { + u32 rcv_csn = ntohl(auth->md5.csn); + if(rcv_csn < n->csn) { - OSPF_TRACE(D_PACKETS, "OSPF_auth: no suitable md5 password found"); - return 0; + OSPF_TRACE(D_PACKETS, "OSPF_auth: lower sequence number (rcv %d, old %d)", rcv_csn, n->csn); + 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; - } + 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)) + if (ifa->passwords) + { + WALK_LIST(ptmp, *(ifa->passwords)) { - OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong md5 digest"); - return 0; + if (auth->md5.keyid != ptmp->id) continue; + if ((ptmp->accfrom > now_real) || (ptmp->accto < now_real)) continue; + pass = ptmp; + break; } - return 1; - break; - default: - OSPF_TRACE(D_PACKETS, "OSPF_auth: unknown auth type"); + } + + if (!pass) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: no suitable md5 password found"); return 0; - } -} + } -#else + void *tail = ((void *) pkt) + plen; + char md5sum[OSPF_AUTH_CRYPT_SIZE]; + password_cpy(password, pass->password, OSPF_AUTH_CRYPT_SIZE); -/* OSPFv3 authentication not yet supported */ + struct MD5Context ctxt; + MD5Init(&ctxt); + MD5Update(&ctxt, (char *) pkt, plen); + MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); + MD5Final(md5sum, &ctxt); -static inline void -ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) -{ } + if (memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE)) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong md5 digest"); + return 0; + } + return 1; -static int -ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, int size) -{ return 1; } - -#endif + default: + OSPF_TRACE(D_PACKETS, "OSPF_auth: unknown auth type"); + return 0; + } +} /** @@ -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); - -#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; + dst_mcast = ipa_equal(sk->laddr, ifa->all_routers) || ipa_equal(sk->laddr, ifa->des_routers); - /* Ignore my own broadcast packets */ - if (ifa->cf->real_bcast && ipa_equal(sk->faddr, ifa->addr->ip)) - return 1; -#else /* OSPFv3 */ + 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; - /* 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) { + /* Matching area ID */ + + if (instance_id != ifa->instance_id) + return 1; + /* It is real iface, source should be local (in OSPFv2) */ -#ifdef OSPFv2 - if (!src_local) + if (ospf_is_v2(p) && !src_local) + { + log(L_ERR "%s%I - strange source address for %s", mesg, sk->faddr, ifa->ifname); 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 - return 1; + goto found; } - else + else if ((areaid == 0) && !dst_mcast) { - /* Some vlink? */ - struct ospf_iface *iff = NULL; + /* Backbone area ID and possible vlink packet */ + + if ((p->areano == 1) || !oa_is_ext(ifa->oa)) + return 1; - WALK_LIST(iff, po->iface_list) + 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) && -#ifdef OSPFv3 - (iff->instance_id == ps->instance_id) && -#endif + (iff->instance_id == instance_id) && (iff->vid == rid)) - { - /* Vlink should be UP */ - if (iff->state != OSPF_IS_PTP) - return 1; - - ifa = iff; - goto found; - } + { + /* Vlink should be UP */ + if (iff->state != OSPF_IS_PTP) + return 1; + + ifa = iff; + goto found; + } } -#ifdef OSPFv2 - log(L_WARN "OSPF: Received packet for unknown vlink (ID %R, IP %I)", rid, sk->faddr); -#endif + /* + * 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 + { + /* Non-matching area ID but cannot be vlink packet */ + + 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; } + 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;i<ntohs(ps->length);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 <feela@network.cz> - * - * 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..16453b87 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -1,25 +1,19 @@ /* - * BIRD -- OSPF - * - * (c) 2000--2004 Ondrej Filip <feela@network.cz> - * - * Can be freely distributed and used under the terms of the GNU GPL. + * BIRD -- OSPF + * + * (c) 2000--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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 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,13 +264,13 @@ 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 */ if (new->rid > old->rid) return 1; - + return -1; } @@ -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,13 +497,13 @@ 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; } 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. @@ -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 + 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; + } - nf.nhs = ifa ? new_nexthop(oa->po, IPA_NONE, ifa->iface, ifa->ecmp_weight) : NULL; + ri_install_net(p, px, pxlen, &nf); +} + + + +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); } - ri_install_net(oa->po, px, pxlen, &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)) + return; + + /* 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); + } } -#ifdef OSPFv3 -static void -process_prefixes(struct ospf_area *oa) +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,84 +677,19 @@ 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) return; + if (oa->rt->lsa.age == LSA_MAXAGE) + return; OSPF_TRACE(D_EVENTS, "Starting routing table calculation for area %R", oa->areaid); @@ -677,7 +703,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 +712,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); - } + case LSA_T_NET: + 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,88 +756,79 @@ 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: + tmp = ospf_hash_find_net(p->gr, oa->areaid, rtl.id, rtl.nif); + if (tmp == par) { - 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; - } - - 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; + if (ospf_is_v2(p)) + en->lb = ipa_from_u32(rtl.data); + else + en->lb_id = rtl.lif; + + return 1; } - } - 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]); + 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; + 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; } - + /* RFC 2328 16.2. calculating inter-area routes */ 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 +839,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 +878,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 +903,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 +913,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,55 +939,36 @@ 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 - - metric = ls->metric & METRIC_MASK; - ip_addr ip = ipa_from_rid(dst_rid); + lsa_parse_sum_rt(en, ospf_is_v2(p), &dst_rid, &metric, &options); + + ip = ipa_from_rid(dst_rid); 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) @@ -1061,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 @@ -1147,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; @@ -1156,20 +1084,19 @@ 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->area_net) { /* 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,104 +1105,70 @@ 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; - /* Condensed area network found */ + /* Condensed area network found */ 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) - 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; + lsa_parse_ext(en, ospf_is_v2(p), rt); - propagate = pxopts & OPT_PX_P; -#endif + if (rt->pxopts & OPT_PX_NU) + return 0; - 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->area_net) { /* 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,59 +1176,57 @@ 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_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(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_M_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) + 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; - schedule_rt_lsa(po->backbone); + + /* RFC 2328 12.4 Event 8 - vlink state change */ + ospf_notify_rt_lsa(ifa->oa); } } 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); - } + } } } } @@ -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->area_net = 1; /* 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->area_net = 1; 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_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 */ 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); } @@ -1456,16 +1343,16 @@ static void translator_timer_hook(timer *timer) { struct ospf_area *oa = timer->data; - + if (oa->translate != TRANS_WAIT) return; oa->translate = TRANS_OFF; - schedule_rtcalc(oa->po); + ospf_schedule_rtcalc(oa->po); } 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->area_net = 1; } 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->area_net = 0; 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->mode == LSA_M_RTCALC) + en->mode = LSA_M_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) { @@ -1827,51 +1685,50 @@ ospf_rt_reset(struct proto_ospf *po) /** * ospf_rt_spf - calculate internal routes - * @po: 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. * 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); - - po->calcrt = 0; + rt_sync(p); + lp_flush(p->nhpool); + + p->calcrt = 0; } @@ -1886,21 +1743,20 @@ 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)) 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) @@ -1908,67 +1764,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 +1841,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 +1852,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 +1876,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 +1899,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 +1933,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 +1965,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 +1992,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 +2005,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 +2030,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 +2042,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 +2055,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, some special entries are persistent */ + if (!nf->n.type && !nf->external_rte && !nf->area_net) { FIB_ITERATE_PUT(&fit, nftmp); fib_delete(fib, nftmp); @@ -2213,7 +2070,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 +2088,9 @@ again2: } FIB_ITERATE_END(nftmp); } + + /* Cleanup stale LSAs */ + WALK_SLIST(en, p->lsal) + if (en->mode == LSA_M_STALE) + ospf_flush_lsa(p, en); } diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h index a11748fc..61936f3c 100644 --- a/proto/ospf/rt.h +++ b/proto/ospf/rt.h @@ -2,17 +2,18 @@ * BIRD -- OSPF * * (c) 2000--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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_ #define _BIRD_OSPF_RT_H_ -#define ORT_UNDEF -1 -#define ORT_ROUTER 1 + #define ORT_NET 0 +#define ORT_ROUTER 1 typedef struct orta { @@ -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 @@ -54,31 +60,35 @@ orta; 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 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()). + * 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()). * - * 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 - * 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) @@ -90,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 @@ -114,7 +124,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..4e3da0de 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -3,6 +3,8 @@ * * (c) 1999 Martin Mares <mj@ucw.cz> * (c) 1999--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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,621 @@ #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); +static inline void * lsab_flush(struct ospf_proto *p); +static inline void lsab_reset(struct ospf_proto *p); + -#ifdef OSPFv2 -#define ipa_to_rid(x) _I(x) -#else /* OSPFv3 */ -#define ipa_to_rid(x) _I3(x) -#endif +/** + * 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(). + * + * 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) +{ + struct top_hash_entry *en; + int change = 0; + en = ospf_hash_get(p->gr, domain, lsa->id, lsa->rt, type); -#ifdef OSPFv2 -static inline u32 -fibnode_to_lsaid(struct proto_ospf *po, struct fib_node *fn) -{ - /* 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 IP address that cannot be used by 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 . - - 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. - - 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. */ - - u32 id = _I(fn->prefix); - - if ((po->rfc1583) || (fn->pxlen == 0) || (fn->pxlen == 32)) - return id; + if (!SNODE_VALID(en)) + s_add_tail(&p->lsal, SNODE en); - if (id & (1 << (32 - fn->pxlen))) - return id; + 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; + + if ((en->lsa.age == LSA_MAXAGE) && (lsa->age == LSA_MAXAGE)) + change = 0; + + mb_free(en->lsa_body); + en->lsa_body = body; + en->lsa = *lsa; + 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) + 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) +{ + /* RFC 2328 13.4 */ + + 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); + + 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 + { + /* + * 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; + + 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 - return id | ~u32_mkmask(fn->pxlen); + { + /* + * 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; + 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_flood_lsa(p, en, NULL); } -#else /* OSPFv3 */ -static inline u32 -fibnode_to_lsaid(struct proto_ospf *po, struct fib_node *fn) +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, 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); + 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; + } + /* - * 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). + * 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. */ - return fn->uid; + + 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_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; } -#endif +/** + * 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); -static void * -lsab_alloc(struct proto_ospf *po, unsigned size) + 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); + + en = NULL; + goto drop; + } + + 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) && + (!ospf_is_v2(p) || (lsa->opts == en->next_lsa_opts))) + 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 ((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; + } + + 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; +} + +static void +ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en) +{ + /* + * 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. + */ + + 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); + + /* 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_flood_lsa(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_flood_lsa(p, en, NULL); +} + +/** + * 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. + */ +void +ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en) { - unsigned offset = po->lsab_used; - po->lsab_used += size; - if (po->lsab_used > po->lsab_size) + 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); + 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_flood_lsa(p, en, NULL); + + if (en->mode == LSA_M_BASIC) + ospf_schedule_rtcalc(p); + + en->mode = LSA_M_BASIC; +} + +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) { - 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); + ospf_flush_lsa(p, en); + continue; } - return ((byte *) po->lsab) + offset; + + 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 ospf_proto *p, uint size) +{ + uint 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, uint 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, uint 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 +646,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; } +static inline u32 +get_rt_options(struct ospf_proto *p, struct ospf_area *oa, int bitv) +{ + u32 opts = 0; -#ifdef OSPFv2 + if (p->areano > 1) + opts |= OPT_RT_B; -static void * -originate_rt_lsa_body(struct ospf_area *oa, u16 *length) -{ - 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; + if ((p->areano > 1) && oa_is_nssa(oa) && oa->ac->translator) + opts |= OPT_RT_NT; - ASSERT(po->lsab_used == 0); - rt = lsab_allocz(po, sizeof(struct ospf_lsa_rt)); + if (p->asbr && !oa_is_stub(oa)) + opts |= OPT_RT_E; - rt->options = 0; + if (bitv) + opts |= OPT_RT_V; - if (po->areano > 1) - rt->options |= OPT_RT_B; + return opts; +} - if ((po->areano > 1) && oa_is_nssa(oa) && oa->ac->translator) - rt->options |= OPT_RT_NT; +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; +} - if (po->ebit && !oa_is_stub(oa)) - rt->options |= OPT_RT_E; +static void +prepare_rt2_lsa_body(struct ospf_proto *p, struct ospf_area *oa) +{ + struct ospf_iface *ifa; + int i = 0, bitv = 0; + struct ospf_neighbor *neigh; - rt = NULL; /* buffer might be reallocated later */ + ASSERT(p->lsab_used == 0); + lsab_allocz(p, sizeof(struct ospf_lsa_rt)); + /* ospf_lsa_rt header will be filled 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 +720,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 +771,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; - } - 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->ip), 0xffffffff, 0); + else + 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++; - } - - rt = po->lsab; - rt->links = i; - - if (bitv) - rt->options |= OPT_RT_V; + 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++; - *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 +805,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)); - - rt->options = oa->options & OPTIONS_MASK; - if (po->areano > 1) - rt->options |= OPT_RT_B; + ASSERT(p->lsab_used == 0); + lsab_allocz(p, sizeof(struct ospf_lsa_rt)); + /* ospf_lsa_rt header will be filled later */ - 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,854 +832,555 @@ 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_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_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; - - 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); -} - -#endif + 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; -/** - * 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. - */ -void -originate_rt_lsa(struct ospf_area *oa) -{ - struct ospf_lsa_header lsa; - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; - void *body; + 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; - OSPF_TRACE(D_EVENTS, "Originating router-LSA for area %R", oa->areaid); + 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; - 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 + default: + log("Unknown interface type %s", ifa->ifname); + break; + } - lsa.rt = po->router_id; - lsa.sn = get_seqnum(oa->rt); - u32 dom = oa->areaid; + ifa->rt_pos_end = i; + } - 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); + struct ospf_lsa_rt *rt = p->lsab; + rt->options = get_rt_options(p, oa, bitv) | (oa->options & LSA_OPTIONS_MASK); } -void -update_rt_lsa(struct ospf_area *oa) +static void +ospf_originate_rt_lsa(struct ospf_proto *p, struct ospf_area *oa) { - struct proto_ospf *po = oa->po; + struct ospf_new_lsa lsa = { + .type = LSA_T_RT, + .dom = oa->areaid, + .id = ospf_is_v2(p) ? p->router_id : 0, + .opts = oa->options + }; - 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. - */ + OSPF_TRACE(D_EVENTS, "Updating router state for area %R", oa->areaid); - originate_rt_lsa(oa); -#ifdef OSPFv3 - originate_prefix_rt_lsa(oa); -#endif + if (ospf_is_v2(p)) + prepare_rt2_lsa_body(p, oa); + else + prepare_rt3_lsa_body(p, oa); - schedule_rtcalc(po); - oa->origrt = 0; + oa->rt = ospf_originate_lsa(p, &lsa); } -static void * -originate_net_lsa_body(struct ospf_iface *ifa, u16 *length, - struct proto_ospf *po) + +/* + * Net-LSA handling + * Type = LSA_T_NET + */ + +static void +prepare_net2_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa) { - 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)); - -#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 + ASSERT(p->lsab_used == 0); + net = lsab_alloc(p, sizeof(struct ospf_lsa_net) + 4 * nodes); - 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); - if (en) - options |= ((struct ospf_lsa_link *) en->lsa_body)->options; -#endif - net->routers[i] = n->rid; i++; } } 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; } - -/** - * 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. - */ -void -originate_net_lsa(struct ospf_iface *ifa) +static void +prepare_net3_lsa_body(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; - - 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); + struct ospf_lsa_net *net; + int nodes = ifa->fadj + 1; + u32 options = 0; + u16 i = 1; - 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); -} + ASSERT(p->lsab_used == 0); + net = lsab_alloc(p, sizeof(struct ospf_lsa_net) + 4 * nodes); -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; + net->routers[0] = p->router_id; - if (ifa->net_lsa == NULL) - return; + 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 */ - 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; -} + struct top_hash_entry *en = + ospf_hash_find(p->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK); -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 (en) + options |= ((struct ospf_lsa_link *) en->lsa_body)->options; - if ((ifa->state != OSPF_IS_DR) || (ifa->fadj == 0)) - { - flush_net_lsa(ifa); -#ifdef OSPFv3 - flush_prefix_net_lsa(ifa); -#endif - } - else - { - originate_net_lsa(ifa); -#ifdef OSPFv3 - originate_prefix_net_lsa(ifa); -#endif + net->routers[i] = n->rid; + i++; } + } + ASSERT(i == nodes); - schedule_rtcalc(po); - ifa->orignet = 0; + net->optx = options & LSA_OPTIONS_MASK; } -#ifdef OSPFv2 - -static inline void * -originate_sum_lsa_body(struct proto_ospf *po, u16 *length, u32 mlen, u32 metric) +static void +ospf_originate_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa) { - 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_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 + }; - sum->netmask = ipa_mkmask(mlen); - sum->metric = metric; + OSPF_TRACE(D_EVENTS, "Updating network state for %s (Id: %R)", ifa->ifname, lsa.id); - 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) -{ - struct ospf_lsa_sum *sum = en->lsa_body; - return fn->pxlen != ipa_mklen(sum->netmask); -} + if (ospf_is_v2(p)) + prepare_net2_lsa_body(p, ifa); + else + prepare_net3_lsa_body(p, ifa); -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); + ifa->net_lsa = ospf_originate_lsa(p, &lsa); } -#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 */ +/* + * (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 * -originate_sum_net_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn, u32 metric) +static inline void +prepare_sum2_lsa_body(struct ospf_proto *p, uint pxlen, 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_sum2 *sum; + sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum2)); + sum->netmask = u32_mkmask(pxlen); sum->metric = metric; - put_ipv6_prefix(sum->prefix, fn->prefix, fn->pxlen, 0, 0); - - return sum; } -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_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); -} + struct ospf_lsa_sum3_net *sum; -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); + sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_net) + IPV6_PREFIX_SPACE(nf->fn.pxlen)); + sum->metric = metric; + put_ipv6_prefix(sum->prefix, nf->fn.prefix, nf->fn.pxlen, 0, 0); } -static inline void * -originate_sum_rt_lsa_body(struct proto_ospf *po, u16 *length, u32 drid, u32 metric, u32 options) +static inline void +prepare_sum3_rt_lsa_body(struct ospf_proto *p, 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) -{ - 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); - - 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); +ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric) +{ + 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, + .nf = nf + }; + + if (ospf_is_v2(p)) + prepare_sum2_lsa_body(p, nf->fn.pxlen, metric); + else + prepare_sum3_net_lsa_body(p, nf, metric); - 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); + ospf_originate_lsa(p, &lsa); } 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; - } +ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric, u32 options) +{ + struct ospf_new_lsa lsa = { + .type = LSA_T_SUM_RT, + .mode = LSA_M_RTCALC, + .dom = oa->areaid, + .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 - { - /* 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_rt_lsa_body(p, lsa.id, metric, options & LSA_OPTIONS_MASK); - 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); - } + ospf_originate_lsa(p, &lsa); } -#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) -{ - 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); - - ext->metric = metric; - ext->netmask = ipa_mkmask(fn->pxlen); - ext->fwaddr = fwaddr; - ext->tag = tag; - - return ext; -} /* - * 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 = lsab_allocz(p, bsize); + ext->metric = metric & LSA_METRIC_MASK; + u32 *buf = ext->rest; - ext->metric = metric; + buf = put_ipv6_prefix(buf, nf->fn.prefix, nf->fn.pxlen, pbit ? OPT_PX_P : 0, 0); - u32 *buf = ext->rest; - buf = put_ipv6_prefix(buf, fn->prefix, 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; +/** + * 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 + * @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 (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. + * @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. + */ +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) +{ + 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, + .nf = nf + }; + + 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); + + ospf_originate_lsa(p, &lsa); } -static inline int -check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_addr fwaddr, u32 tag) +static void +ospf_flush_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf) { - 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); + struct top_hash_entry *en; - /* LSAID collision */ - if ((fn->pxlen != pxlen) || !ipa_equal(fn->prefix, prefix)) - return -1; + u32 type = oa ? LSA_T_NSSA : LSA_T_EXT; + u32 dom = oa ? oa->areaid : 0; + u32 id = ort_to_lsaid(p, nf); - if (en->lsa.sn == LSA_MAXSEQNO) - return 0; + en = ospf_hash_find(p->gr, dom, id, p->router_id, type); - u32 rt_metric = ext->metric & METRIC_MASK; - ip_addr rt_fwaddr = IPA_NONE; - u32 rt_tag = 0; + if (!en || (en->nf != nf)) + return; - if (ext->metric & LSA_EXT_FBIT) - buf = lsa_get_ipv6_addr(buf, &rt_fwaddr); + ospf_flush_lsa(p, en); +} - if (ext->metric & LSA_EXT_TBIT) - rt_tag = *buf++; +static inline int +use_gw_for_fwaddr(struct ospf_proto *p, ip_addr gw, struct iface *iface) +{ + struct ospf_iface *ifa; - return (rt_metric == metric) && ipa_equal(rt_fwaddr, fwaddr) && (rt_tag == tag); -} + 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; -#endif + return 0; +} static inline ip_addr -find_surrogate_fwaddr(struct ospf_area *oa) +find_surrogate_fwaddr(struct ospf_proto *p, 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) + /* RFC 3101 2.3 - surrogate forwarding address selection */ + + WALK_LIST(ifa, p->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) + if (ospf_is_v2(p)) { - 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)) + a = ifa->addr; + if (a->flags & IA_PEER) continue; - np = ((a->flags & IA_HOST) || ifa->stub) ? 2 : 1; + np = (a->flags & IA_HOST) ? 3 : (ifa->stub ? 2 : 1); if (np > cur_np) { cur_addr = a; cur_np = np; } } -#endif + 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; + } + } + } } 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 - * @src: the source of origination of the LSA (EXT_EXPORT/EXT_NSSA) - * @metric: the metric of a route - * @fwaddr: the forwarding address - * @tag: the route tag - * @pbit: P-bit for NSSA LSAs, 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. - * @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) -{ - 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; - - OSPF_TRACE(D_EVENTS, "Originating %s-LSA for %I/%d", - nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen); - - 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 (nssa && pbit && ipa_zero(fwaddr)) - { - /* NSSA-LSA with P-bit set must have non-zero forwarding address */ +ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *ea) +{ + struct ospf_proto *p = (struct ospf_proto *) P; + struct ospf_area *oa = NULL; /* non-NULL for NSSA-LSA */ + ort *nf; - fwaddr = find_surrogate_fwaddr(oa); - if (ipa_zero(fwaddr)) - { - log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %I/%d", - p->name, fn->prefix, fn->pxlen); - return; - } - } + /* + * 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 ((p->areano == 1) && oa_is_nssa(HEAD(p->area_list))) + oa = HEAD(p->area_list); - if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL) + if (!new) { - 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; - } + nf = (ort *) fib_find(&p->rtf, &n->n.prefix, n->n.pxlen); - if (rv > 0) + 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) + ospf_schedule_rtcalc(p); + + return; } - lsa.sn = get_seqnum(en); - body = originate_ext_lsa_body(po, &lsa.length, fn, metric, fwaddr, tag, pbit); - lsasum_calculate(&lsa, body); + 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 (src) - fn->x1 = src; - lsa_install_new(po, &lsa, dom, body); - ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); + if ((a->dest == RTD_ROUTER) && use_gw_for_fwaddr(p, a->gw, a->iface)) + fwd = a->gw; - if (po->ebit == 0) + /* NSSA-LSA with P-bit set must have non-zero forwarding address */ + if (oa && ipa_zero(fwd)) { - po->ebit = 1; - WALK_LIST(oa, po->area_list) + fwd = find_surrogate_fwaddr(p, oa); + + if (ipa_zero(fwd)) { - schedule_rt_lsa(oa); + log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %I/%d", + p->p.name, n->n.prefix, n->n.pxlen); + return; } } -} -void -flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int nssa) -{ - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; - struct top_hash_entry *en; - - u32 dom = nssa ? oa->areaid : 0; - u32 type = nssa ? LSA_T_NSSA : LSA_T_EXT; - u32 lsaid = fibnode_to_lsaid(po, fn); + nf = (ort *) fib_get(&p->rtf, &n->n.prefix, n->n.pxlen); + ospf_originate_ext_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1); + nf->external_rte = 1; +} - if (en = ospf_hash_find(po->gr, dom, lsaid, po->router_id, type)) - { - 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; - } +/* + * Link-LSA handling (assume OSPFv3) + * Type = LSA_T_LINK + */ - fn->x1 = 0; - ospf_lsupd_flush_nlsa(po, en); - } +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); } - -#ifdef OSPFv3 - -static void * -originate_link_lsa_body(struct ospf_iface *ifa, u16 *length) +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; + OSPF_TRACE(D_EVENTS, "Updating link state for %s (Id: %R)", ifa->ifname, lsa.id); - 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); + prepare_link_lsa_body(p, ifa); - /* 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 +1395,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->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->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++; + if ((a->flags & IA_HOST) || + (ifa->state == OSPF_IS_LOOP) || + (ifa->type == OSPF_IT_PTMP)) + { + 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 +1446,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 +1454,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) { @@ -1389,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; @@ -1410,9 +1515,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 +1527,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 +1555,129 @@ 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; + if (ospf_is_v2(p)) + return; - OSPF_TRACE(D_EVENTS, "Originating 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 + }; - 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; + prepare_prefix_net_lsa_body(p, ifa); - 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); + 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 -flush_prefix_net_lsa(struct ospf_iface *ifa) +ospf_update_topology(struct ospf_proto *p) { - 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; + struct ospf_area *oa; + struct ospf_iface *ifa; - if (en == NULL) - return; + WALK_LIST(oa, p->area_list) + { + 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_TRACE(D_EVENTS, "Flushing network prefix-LSA for iface %s", ifa->ifname); + ospf_originate_rt_lsa(p, oa); + ospf_originate_prefix_rt_lsa(p, oa); + oa->update_rt_lsa = 0; + } + } - 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; -} + 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); -#endif + 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; + } + } +} static void @@ -1574,7 +1715,7 @@ ospf_top_hash_u32(u32 a) return a; } -static inline 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. @@ -1582,14 +1723,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,13 +1735,15 @@ 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 + * @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; @@ -1617,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; } @@ -1631,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; @@ -1648,7 +1786,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 +1795,86 @@ 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)) - e = e->next; - - return e; -} - - -#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)) + 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 && e->lsa_body) ? e : NULL; } -#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; } +/* + * 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_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.age == LSA_MAXAGE)) 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 +1886,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; @@ -1812,8 +1914,8 @@ 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 + - ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa.type); + 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) { @@ -1853,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; @@ -1862,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: @@ -1873,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++) @@ -1884,30 +1986,3 @@ 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) -{ - struct ospf_iface *ifa; - struct ospf_neighbor *n; - - WALK_LIST(ifa, po->iface_list) - { - WALK_LIST(n, ifa->neigh_list) - { - if ((n->state == NEIGHBOR_EXCHANGE) || (n->state == NEIGHBOR_LOADING)) - return 0; - - break; - } - } - - return 1; -} diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h index cb876487..e2d6c773 100644 --- a/proto/ospf/topology.h +++ b/proto/ospf/topology.h @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (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,204 @@ 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 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 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() */ }; + +/* 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 { 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; + u8 mode; + 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_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); +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 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); + +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); +struct top_hash_entry * ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr); +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_rt(struct top_graph *f, u32 domain, u32 rtr) +ospf_hash_find_net(struct top_graph *f, u32 domain, u32 id, u32 nif) { - return ospf_hash_find(f, domain, rtr, rtr, LSA_T_RT); + return f->ospf2 ? + ospf_hash_find_net2(f, domain, id) : + ospf_hash_find(f, domain, nif, id, LSA_T_NET); } -#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 - #endif /* _BIRD_OSPF_TOPOLOGY_H_ */ |