diff options
Diffstat (limited to 'proto/ospf/lsupd.c')
-rw-r--r-- | proto/ospf/lsupd.c | 303 |
1 files changed, 205 insertions, 98 deletions
diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index ba09fec0..7ae2f2a2 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -9,12 +9,20 @@ #include "ospf.h" +struct ospf_lsupd_packet +{ + struct ospf_packet ospf_packet; + u32 lsano; /* Number of LSA's */ +}; + + +/* Beware of unaligned access */ void ospf_dump_lsahdr(struct proto *p, struct ospf_lsa_header *lsa_n) { struct ospf_lsa_header lsa; ntohlsah(lsa_n, &lsa); - log(L_TRACE "%s: LSA Id: %R, Rt: %R, Type: %u, Age: %u, Seqno: 0x%08x, Sum: %u", + log(L_TRACE "%s: LSA Id: %R, Rt: %R, Type: 0x%04x, Age: %u, Seqno: 0x%08x, Sum: 0x%04x", p->name, lsa.id, lsa.rt, lsa.type, lsa.age, lsa.sn, lsa.checksum); } @@ -31,15 +39,16 @@ static void ospf_dump_lsupd(struct proto *p, struct ospf_lsupd_packet *pkt) 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; - int offset = sizeof(struct ospf_lsupd_packet); - int bound = ntohs(op->length) - sizeof(struct ospf_lsa_header); - int i, j; + unsigned int offset = sizeof(struct ospf_lsupd_packet); + unsigned int bound = ntohs(op->length) - sizeof(struct ospf_lsa_header); + unsigned int i, j; j = ntohl(pkt->lsano); for (i = 0; i < j; i++) { - if (offset > bound) + if ((offset > bound) || ((offset % 4) != 0)) { log(L_TRACE "%s: LSA invalid", p->name); return; @@ -51,27 +60,98 @@ static void ospf_dump_lsupd(struct proto *p, struct ospf_lsupd_packet *pkt) } } + +#ifdef OSPFv2 + +int +ospf_lsa_flooding_allowed(struct ospf_lsa_header *lsa, u32 domain, struct ospf_iface *ifa) +{ + if (lsa->type == LSA_T_EXT) + { + if (ifa->type == OSPF_IT_VLINK) + return 0; + if (ifa->oa->stub) + return 0; + return 1; + } + else + return ifa->oa->areaid == domain; +} + +#else /* OSPFv3 */ + +static int +unknown_lsa_type(struct ospf_lsa_header *lsa) +{ + switch (lsa->type) + { + case LSA_T_RT: + case LSA_T_NET: + case LSA_T_SUM_NET: + case LSA_T_SUM_RT: + case LSA_T_EXT: + case LSA_T_LINK: + case LSA_T_PREFIX: + return 0; + + default: + return 1; + } +} + +int +ospf_lsa_flooding_allowed(struct ospf_lsa_header *lsa, u32 domain, struct ospf_iface *ifa) +{ + u32 scope = LSA_SCOPE(lsa); + + /* 4.5.2 (Case 2) */ + if (unknown_lsa_type(lsa) && !(lsa->type & LSA_UBIT)) + scope = LSA_SCOPE_LINK; + + switch (scope) + { + case LSA_SCOPE_LINK: + return ifa->iface->index == domain; + + case LSA_SCOPE_AREA: + return ifa->oa->areaid == domain; + + case LSA_SCOPE_AS: + if (ifa->type == OSPF_IT_VLINK) + return 0; + if (ifa->oa->stub) + return 0; + return 1; + + default: + log(L_ERR "LSA with invalid scope"); + return 0; + } +} + +#endif + /** * 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) - * @iff: interface which received this LSA (or NULL if LSA is generated) - * @oa: ospf_area which is the LSA generated for + * @domain: domain of LSA (must be filled) * @rtl: add this LSA into retransmission list * + * * return value - was the LSA flooded back? */ int -ospf_lsupd_flood(struct ospf_neighbor *n, struct ospf_lsa_header *hn, - struct ospf_lsa_header *hh, struct ospf_iface *iff, - struct ospf_area *oa, int rtl) +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) { struct ospf_iface *ifa; struct ospf_neighbor *nn; struct top_hash_entry *en; - struct proto_ospf *po = oa->po; struct proto *p = &po->proto; int ret, retval = 0; @@ -81,18 +161,8 @@ ospf_lsupd_flood(struct ospf_neighbor *n, struct ospf_lsa_header *hn, if (ifa->stub) continue; - if (hh->type == LSA_T_EXT) - { - if (ifa->type == OSPF_IT_VLINK) - continue; - if (ifa->oa->stub) - continue; - } - else - { - if (ifa->oa != oa) - continue; - } + if (! ospf_lsa_flooding_allowed(hh, 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); @@ -100,11 +170,14 @@ ospf_lsupd_flood(struct ospf_neighbor *n, struct ospf_lsa_header *hn, ret = 0; WALK_LIST(nn, ifa->neigh_list) { + /* 13.3 (1a) */ if (nn->state < NEIGHBOR_EXCHANGE) continue; + + /* 13.3 (1b) */ if (nn->state < NEIGHBOR_FULL) { - if ((en = ospf_hash_find_header(nn->lsrqh, nn->ifa->oa->areaid, hh)) != NULL) + if ((en = ospf_hash_find_header(nn->lsrqh, domain, hh)) != NULL) { DBG("That LSA found in lsreq list for neigh %R\n", nn->rid); @@ -140,14 +213,20 @@ ospf_lsupd_flood(struct ospf_neighbor *n, struct ospf_lsa_header *hn, } } + /* 13.3 (1c) */ if (nn == n) continue; + /* 13.3 (1d) */ if (rtl) { - if ((en = ospf_hash_find_header(nn->lsrth, nn->ifa->oa->areaid, hh)) == NULL) + /* 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 */ + + if ((en = ospf_hash_find_header(nn->lsrth, domain, hh)) == NULL) { - en = ospf_hash_get_header(nn->lsrth, nn->ifa->oa, hh); + en = ospf_hash_get_header(nn->lsrth, domain, hh); } else { @@ -159,7 +238,7 @@ ospf_lsupd_flood(struct ospf_neighbor *n, struct ospf_lsa_header *hn, } else { - if ((en = ospf_hash_find_header(nn->lsrth, nn->ifa->oa->areaid, hh)) != NULL) + if ((en = ospf_hash_find_header(nn->lsrth, domain, hh)) != NULL) { s_rem_node(SNODE en); if (en->lsa_body != NULL) @@ -175,29 +254,23 @@ ospf_lsupd_flood(struct ospf_neighbor *n, struct ospf_lsa_header *hn, if (ret == 0) continue; /* pg 150 (2) */ - if (ifa == iff) + if (n && (n->ifa == ifa)) { - if ((n->rid == iff->drid) || n->rid == iff->bdrid) + if ((n->rid == ifa->drid) || n->rid == ifa->bdrid) continue; /* pg 150 (3) */ - if (iff->state == OSPF_IS_BACKUP) + if (ifa->state == OSPF_IS_BACKUP) continue; /* pg 150 (4) */ retval = 1; } { - sock *sk; u16 len, age; struct ospf_lsupd_packet *pk; struct ospf_packet *op; struct ospf_lsa_header *lh; - if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_VLINK)) - sk = ifa->ip_sk; - else - sk = ifa->hello_sk; - - pk = (struct ospf_lsupd_packet *) sk->tbuf; - op = (struct ospf_packet *) sk->tbuf; + pk = (struct ospf_lsupd_packet *) ifa->sk->tbuf; + op = (struct ospf_packet *) ifa->sk->tbuf; ospf_pkt_fill_hdr(ifa, pk, LSUPD_P); pk->lsano = htonl(1); @@ -216,7 +289,7 @@ ospf_lsupd_flood(struct ospf_neighbor *n, struct ospf_lsa_header *hn, htonlsah(hh, lh); help = (u8 *) (lh + 1); - en = ospf_hash_find_header(po->gr, oa->areaid, hh); + en = ospf_hash_find_header(po->gr, domain, hh); htonlsab(en->lsa_body, help, hh->type, hh->length - sizeof(struct ospf_lsa_header)); } @@ -231,28 +304,28 @@ ospf_lsupd_flood(struct ospf_neighbor *n, struct ospf_lsa_header *hn, op->length = htons(len); - OSPF_PACKET(ospf_dump_lsupd, (struct ospf_lsupd_packet *) sk->tbuf, + OSPF_PACKET(ospf_dump_lsupd, (struct ospf_lsupd_packet *) ifa->sk->tbuf, "LSUPD packet flooded via %s", ifa->iface->name); switch (ifa->type) { case OSPF_IT_NBMA: if ((ifa->state == OSPF_IS_BACKUP) || (ifa->state == OSPF_IS_DR)) - ospf_send_to_agt(sk, ifa, NEIGHBOR_EXCHANGE); + ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE); else - ospf_send_to_bdr(sk, ifa); + ospf_send_to_bdr(ifa); break; case OSPF_IT_VLINK: - ospf_send_to(sk, ifa->vip, ifa); + ospf_send_to(ifa, ifa->vip); break; default: if ((ifa->state == OSPF_IS_BACKUP) || (ifa->state == OSPF_IS_DR) || (ifa->type == OSPF_IT_PTP)) - ospf_send_to(sk, AllSPFRouters, ifa); + ospf_send_to(ifa, AllSPFRouters); else - ospf_send_to(sk, AllDRouters, ifa); + ospf_send_to(ifa, AllDRouters); } } } @@ -276,8 +349,8 @@ ospf_lsupd_send_list(struct ospf_neighbor *n, list * l) if (EMPTY_LIST(*l)) return; - pk = (struct ospf_lsupd_packet *) n->ifa->ip_sk->tbuf; - op = (struct ospf_packet *) n->ifa->ip_sk->tbuf; + pk = (struct ospf_lsupd_packet *) n->ifa->sk->tbuf; + op = (struct ospf_packet *) n->ifa->sk->tbuf; DBG("LSupd: 1st packet\n"); @@ -288,8 +361,9 @@ ospf_lsupd_send_list(struct ospf_neighbor *n, list * l) WALK_LIST(llsh, *l) { - if ((en = ospf_hash_find(po->gr, oa->areaid, llsh->lsh.id, llsh->lsh.rt, - llsh->lsh.type)) == NULL) + u32 domain = ospf_lsa_domain(llsh->lsh.type, n->ifa); + if ((en = ospf_hash_find(po->gr, domain, llsh->lsh.id, + llsh->lsh.rt, llsh->lsh.type)) == NULL) continue; /* Probably flushed LSA */ /* FIXME This is a bug! I cannot flush LSA that is in lsrt */ @@ -300,9 +374,9 @@ ospf_lsupd_send_list(struct ospf_neighbor *n, list * l) pk->lsano = htonl(lsano); op->length = htons(len); - OSPF_PACKET(ospf_dump_lsupd, (struct ospf_lsupd_packet *) n->ifa->ip_sk->tbuf, + OSPF_PACKET(ospf_dump_lsupd, (struct ospf_lsupd_packet *) n->ifa->sk->tbuf, "LSUPD packet sent to %I via %s", n->ip, n->ifa->iface->name); - ospf_send_to(n->ifa->ip_sk, n->ip, n->ifa); + ospf_send_to(n->ifa, n->ip); DBG("LSupd: next packet\n"); ospf_pkt_fill_hdr(n->ifa, pk, LSUPD_P); @@ -323,63 +397,61 @@ ospf_lsupd_send_list(struct ospf_neighbor *n, list * l) pk->lsano = htonl(lsano); op->length = htons(len); - OSPF_PACKET(ospf_dump_lsupd, (struct ospf_lsupd_packet *) n->ifa->ip_sk->tbuf, + OSPF_PACKET(ospf_dump_lsupd, (struct ospf_lsupd_packet *) n->ifa->sk->tbuf, "LSUPD packet sent to %I via %s", n->ip, n->ifa->iface->name); - ospf_send_to(n->ifa->ip_sk, n->ip, n->ifa); + ospf_send_to(n->ifa, n->ip); } } void -ospf_lsupd_receive(struct ospf_lsupd_packet *ps, - struct ospf_iface *ifa, struct ospf_neighbor *n) +ospf_lsupd_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, + struct ospf_neighbor *n) { - u32 area; + struct ospf_neighbor *ntmp; - struct ospf_lsa_header *lsa; - struct ospf_area *oa; struct proto_ospf *po = ifa->oa->po; struct proto *p = &po->proto; - unsigned int i, sendreq = 1, size = ntohs(ps->ospf_packet.length); + unsigned int i, max, sendreq = 1; - OSPF_PACKET(ospf_dump_lsupd, ps, "LSUPD packet received from %I via %s", n->ip, ifa->iface->name); - - if (n->state < NEIGHBOR_EXCHANGE) + unsigned int size = ntohs(ps_i->length); + if (size < (sizeof(struct ospf_lsupd_packet) + sizeof(struct ospf_lsa_header))) { - OSPF_TRACE(D_PACKETS, "Received lsupd in lesser state than EXCHANGE from (%I)", n->ip); + log(L_ERR "Bad OSPF LSUPD packet from %I - too short (%u B)", n->ip, size); return; } - if (size <= - (sizeof(struct ospf_lsupd_packet) + sizeof(struct ospf_lsa_header))) + struct ospf_lsupd_packet *ps = (void *) ps_i; + OSPF_PACKET(ospf_dump_lsupd, ps, "LSUPD packet received from %I via %s", n->ip, ifa->iface->name); + + if (n->state < NEIGHBOR_EXCHANGE) { - log(L_WARN "Received lsupd from %I is too short!", n->ip); + OSPF_TRACE(D_PACKETS, "Received lsupd in lesser state than EXCHANGE from (%I)", n->ip); return; } ospf_neigh_sm(n, INM_HELLOREC); /* Questionable */ - lsa = (struct ospf_lsa_header *) (ps + 1); - area = htonl(ps->ospf_packet.areaid); - oa = ospf_find_area((struct proto_ospf *) p, area); + unsigned int offset = sizeof(struct ospf_lsupd_packet); + unsigned int bound = size - sizeof(struct ospf_lsa_header); - for (i = 0; i < ntohl(ps->lsano); i++, - lsa = (struct ospf_lsa_header *) (((u8 *) lsa) + ntohs(lsa->length))) + max = ntohl(ps->lsano); + for (i = 0; i < max; i++) { struct ospf_lsa_header lsatmp; struct top_hash_entry *lsadb; - unsigned diff = ((u8 *) lsa) - ((u8 *) ps), lenn = ntohs(lsa->length); - u16 chsum; - if (((diff + sizeof(struct ospf_lsa_header)) >= size) - || ((lenn + diff) > size)) + if (offset > bound) { log(L_WARN "Received lsupd from %I is too short!", n->ip); ospf_neigh_sm(n, INM_BADLSREQ); - break; + return; } - if ((lenn <= sizeof(struct ospf_lsa_header)) - || (lenn != (4 * (lenn / 4)))) + struct ospf_lsa_header *lsa = (void *) (((u8 *) ps) + offset); + unsigned int lsalen = ntohs(lsa->length); + + if (((offset + lsalen) > size) || ((lsalen % 4) != 0) || + (lsalen <= sizeof(struct ospf_lsa_header))) { log(L_WARN "Received LSA from %I with bad length", n->ip); ospf_neigh_sm(n, INM_BADLSREQ); @@ -387,13 +459,14 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, } /* pg 143 (1) */ - chsum = lsa->checksum; + u16 chsum = lsa->checksum; if (chsum != lsasum_check(lsa, NULL)) { - log(L_WARN "Received bad lsa checksum from %I", n->ip); + log(L_WARN "Received bad lsa checksum from %I: %x %x", n->ip, chsum, lsa->checksum); continue; } +#ifdef OSPFv2 /* pg 143 (2) */ if ((lsa->type < LSA_T_RT) || (lsa->type > LSA_T_EXT)) { @@ -402,18 +475,34 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, } /* pg 143 (3) */ - if ((lsa->type == LSA_T_EXT) && oa->stub) + if ((lsa->type == LSA_T_EXT) && ifa->oa->stub) { log(L_WARN "Received External LSA in stub area from %I", n->ip); continue; } +#else /* OSPFv3 */ + /* 4.5.1 (2) */ + if ((LSA_SCOPE(lsa) == LSA_SCOPE_AS) && ifa->oa->stub) + { + log(L_WARN "Received LSA with AS scope in stub area from %I", n->ip); + continue; + } + + /* 4.5.1 (3) */ + if ((LSA_SCOPE(lsa) == LSA_SCOPE_RES)) + { + log(L_WARN "Received LSA with invalid scope from %I", 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); - lsadb = ospf_hash_find_header(po->gr, oa->areaid, &lsatmp); + u32 domain = ospf_lsa_domain(lsatmp.type, ifa); + lsadb = ospf_hash_find_header(po->gr, domain, &lsatmp); #ifdef LOCAL_DEBUG if (lsadb) @@ -433,15 +522,15 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, if ((lsadb == NULL) || (lsa_comp(&lsatmp, &lsadb->lsa) == CMP_NEWER)) { struct ospf_iface *ift = NULL; - void *body; - struct ospf_iface *nifa; int self = (lsatmp.rt == p->cf->global->router_id); DBG("PG143(5): Received LSA is newer\n"); - /* pg 145 (5f) - premature aging of self originated lsa */ +#ifdef OSPFv2 + /* 13.4 - check self-originated LSAs of NET type */ if ((!self) && (lsatmp.type == LSA_T_NET)) { + struct ospf_iface *nifa; WALK_LIST(nifa, po->iface_list) { if (!nifa->iface) @@ -453,7 +542,9 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, } } } +#endif + /* pg 145 (5f) - premature aging of self originated lsa */ if (self) { struct top_hash_entry *en; @@ -473,10 +564,10 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, lsatmp.type, lsatmp.id, lsatmp.rt); lsasum_check(lsa, (lsa + 1)); /* It also calculates chsum! */ lsatmp.checksum = ntohs(lsa->checksum); - ospf_lsupd_flood(NULL, lsa, &lsatmp, NULL, oa, 0); - if (en = ospf_hash_find_header(po->gr, oa->areaid, &lsatmp)) - { - ospf_lsupd_flood(NULL, NULL, &en->lsa, NULL, oa, 1); + ospf_lsupd_flood(po, NULL, lsa, &lsatmp, domain, 0); + if (en = ospf_hash_find_header(po->gr, domain, &lsatmp)) + { /* FIXME verify hacks */ + ospf_lsupd_flood(po, NULL, NULL, &en->lsa, domain, 1); } continue; } @@ -489,7 +580,7 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, continue; } - if (ospf_lsupd_flood(n, lsa, &lsatmp, ifa, ifa->oa, 1) == 0) + 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) @@ -509,7 +600,7 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, { struct top_hash_entry *en; if (ntmp->state > NEIGHBOR_EXSTART) - if ((en = ospf_hash_find_header(ntmp->lsrth, ntmp->ifa->oa->areaid, &lsadb->lsa)) != NULL) + if ((en = ospf_hash_find_header(ntmp->lsrth, domain, &lsadb->lsa)) != NULL) { s_rem_node(SNODE en); if (en->lsa_body != NULL) @@ -528,13 +619,30 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, } /* FIXME lsack? */ /* pg 144 (5d) */ - body = - mb_alloc(p->pool, lsatmp.length - sizeof(struct ospf_lsa_header)); + void *body = mb_alloc(p->pool, lsatmp.length - sizeof(struct ospf_lsa_header)); ntohlsab(lsa + 1, body, lsatmp.type, lsatmp.length - sizeof(struct ospf_lsa_header)); - lsadb = lsa_install_new(&lsatmp, body, oa); + + /* 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; + } + + lsadb = lsa_install_new(po, &lsatmp, domain, body); DBG("New LSA installed in DB\n"); +#ifdef OSPFv3 + /* Events 6,7 from 4.4.3. */ + if ((lsatmp.type == LSA_T_LINK) && + (ifa->state == OSPF_IS_DR)) + schedule_net_lsa(ifa); +#endif + continue; } @@ -545,7 +653,7 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, { struct top_hash_entry *en; DBG("PG145(7) Got the same LSA\n"); - if ((en = ospf_hash_find_header(n->lsrth, n->ifa->oa->areaid, &lsadb->lsa)) != NULL) + if ((en = ospf_hash_find_header(n->lsrth, lsadb->domain, &lsadb->lsa)) != NULL) { /* pg145 (7a) */ s_rem_node(SNODE en); @@ -596,10 +704,9 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, } void -ospf_lsupd_flush_nlsa(struct top_hash_entry *en, struct ospf_area *oa) +ospf_lsupd_flush_nlsa(struct proto_ospf *po, struct top_hash_entry *en) { struct ospf_lsa_header *lsa = &en->lsa; - struct proto_ospf *po = oa->po; struct proto *p = &po->proto; lsa->age = LSA_MAXAGE; @@ -607,5 +714,5 @@ ospf_lsupd_flush_nlsa(struct top_hash_entry *en, struct ospf_area *oa) lsasum_calculate(lsa, en->lsa_body); OSPF_TRACE(D_EVENTS, "Premature aging self originated lsa!"); OSPF_TRACE(D_EVENTS, "Type: %d, Id: %R, Rt: %R", lsa->type, lsa->id, lsa->rt); - ospf_lsupd_flood(NULL, NULL, lsa, NULL, oa, 0); + ospf_lsupd_flood(po, NULL, NULL, lsa, en->domain, 0); } |