From d5356072ac18d5b0eb12f14afca6bfbea702dda2 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 4 Nov 2010 17:22:43 +0100 Subject: [PATCH] Fixes a bug in LSA update of large LSAs. --- lib/lists.h | 6 +-- proto/ospf/hello.c | 2 +- proto/ospf/lsupd.c | 109 +++++++++++++++++++++++--------------------- proto/ospf/ospf.h | 3 +- proto/ospf/packet.c | 10 ++-- proto/ospf/packet.h | 13 ++++++ 6 files changed, 81 insertions(+), 62 deletions(-) diff --git a/lib/lists.h b/lib/lists.h index 342dfd62..6fab12c4 100644 --- a/lib/lists.h +++ b/lib/lists.h @@ -33,10 +33,10 @@ typedef struct list { /* In fact two overlayed nodes */ #define NODE (node *) #define HEAD(list) ((void *)((list).head)) #define TAIL(list) ((void *)((list).tail)) -#define WALK_LIST(n,list) for(n=HEAD(list);(NODE (n))->next; \ - n=(void *)((NODE (n))->next)) +#define NODE_NEXT(n) ((void *)((NODE (n))->next)) +#define WALK_LIST(n,list) for(n=HEAD(list);(NODE (n))->next; n=NODE_NEXT(n)) #define WALK_LIST_DELSAFE(n,nxt,list) \ - for(n=HEAD(list); nxt=(void *)((NODE (n))->next); n=(void *) nxt) + for(n=HEAD(list); nxt=NODE_NEXT(n); n=(void *) nxt) /* WALK_LIST_FIRST supposes that called code removes each processed node */ #define WALK_LIST_FIRST(n,list) \ while(n=HEAD(list), (NODE (n))->next) diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index 70fb6548..3d7d8de3 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -297,7 +297,7 @@ ospf_hello_send(timer *timer, int poll, struct ospf_neighbor *dirn) 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+1) * sizeof(u32) + sizeof(struct ospf_hello_packet) > ospf_pkt_bufsize(ifa)) { OSPF_TRACE(D_PACKETS, "Too many neighbors on the interface!"); break; diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 62e7eac1..fb757d08 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -330,67 +330,74 @@ ospf_lsupd_flood(struct proto_ospf *po, void /* I send all I received in LSREQ */ ospf_lsupd_send_list(struct ospf_neighbor *n, list * l) { - struct l_lsr_head *llsh; - u16 len; - u32 lsano; - struct top_hash_entry *en; - struct ospf_lsupd_packet *pk; - struct ospf_packet *op; struct ospf_area *oa = n->ifa->oa; - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; - void *pktpos; + 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; - if (EMPTY_LIST(*l)) - return; + pkt = ospf_tx_buffer(n->ifa); + buf = (void *) pkt; - DBG("LSupd: 1st packet\n"); - - pk= ospf_tx_buffer(n->ifa); - op = &pk->ospf_packet; - - ospf_pkt_fill_hdr(n->ifa, pk, LSUPD_P); - len = sizeof(struct ospf_lsupd_packet); - lsano = 0; - pktpos = (pk + 1); - - WALK_LIST(llsh, *l) + lsr = HEAD(*l); + while(NODE_NEXT(lsr)) { - 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 */ + /* Prepare the packet */ + ospf_pkt_fill_hdr(n->ifa, pkt, LSUPD_P); + len = sizeof(struct ospf_lsupd_packet); + lsano = 0; - DBG("Sending LSA: Type=%u, ID=%R, RT=%R, SN: 0x%x, Age: %u\n", - llsh->lsh.type, llsh->lsh.id, llsh->lsh.rt, en->lsa.sn, en->lsa.age); - if (((u32) (len + en->lsa.length)) > ospf_pkt_maxsize(n->ifa)) + /* Fill the packet with LSAs */ + while(NODE_NEXT(lsr)) { - pk->lsano = htonl(lsano); - op->length = htons(len); + 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; + } - OSPF_PACKET(ospf_dump_lsupd, pk, "LSUPD packet sent to %I via %s", n->ip, n->ifa->iface->name); - ospf_send_to(n->ifa, n->ip); + 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; - DBG("LSupd: next packet\n"); - ospf_pkt_fill_hdr(n->ifa, pk, LSUPD_P); - len = sizeof(struct ospf_lsupd_packet); - lsano = 0; - pktpos = (pk + 1); + /* LSA is larger than MTU, check buffer size */ + if (len2 > ospf_pkt_bufsize(n->ifa)) + { + /* Cannot fit in a tx buffer, skip that */ + log(L_WARN "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; + } + } + + /* 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); } - htonlsah(&(en->lsa), pktpos); - pktpos = pktpos + sizeof(struct ospf_lsa_header); - htonlsab(en->lsa_body, pktpos, en->lsa.length - sizeof(struct ospf_lsa_header)); - pktpos = pktpos + en->lsa.length - sizeof(struct ospf_lsa_header); - len += en->lsa.length; - lsano++; - } - if (lsano > 0) - { - pk->lsano = htonl(lsano); - op->length = htons(len); - OSPF_PACKET(ospf_dump_lsupd, pk, "LSUPD packet sent to %I via %s", n->ip, n->ifa->iface->name); + 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->iface->name); ospf_send_to(n->ifa, n->ip); } } diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 103ca550..1ae83c1c 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -10,7 +10,6 @@ #define _BIRD_OSPF_H_ #define MAXNETS 10 -#define OSPF_VLINK_MTU 576 /* RFC2328 - A.1 */ #define OSPF_MAX_PKT_SIZE 65536 /* * RFC 2328 says, maximum packet size is 65535 @@ -53,11 +52,13 @@ do { if ((p->debug & D_PACKETS) || OSPF_FORCE_DEBUG) \ #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 */ #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 */ #endif diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index 6ae29a88..bfc17d76 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -39,19 +39,17 @@ ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) unsigned ospf_pkt_maxsize(struct ospf_iface *ifa) { - /* For virtual links use mtu=576, can be mtu < 576? */ unsigned mtu = (ifa->type == OSPF_IT_VLINK) ? OSPF_VLINK_MTU : ifa->iface->mtu; - unsigned add = 0; + unsigned headers = SIZE_OF_IP_HEADER; #ifdef OSPFv2 - add = ((ifa->autype == OSPF_AUTH_CRYPT) ? OSPF_AUTH_CRYPT_SIZE : 0); + if (ifa->autype == OSPF_AUTH_CRYPT) + headers += OSPF_AUTH_CRYPT_SIZE; #endif - return ((mtu <= ifa->iface->mtu) ? mtu : ifa->iface->mtu) - - SIZE_OF_IP_HEADER - add; + return mtu - headers; } - #ifdef OSPFv2 static void diff --git a/proto/ospf/packet.h b/proto/ospf/packet.h index 1c74a703..c0185b9c 100644 --- a/proto/ospf/packet.h +++ b/proto/ospf/packet.h @@ -21,4 +21,17 @@ void ospf_send_to(struct ospf_iface *ifa, ip_addr ip); static inline void * ospf_tx_buffer(struct ospf_iface *ifa) { return ifa->sk->tbuf; } +static inline unsigned +ospf_pkt_bufsize(struct ospf_iface *ifa) +{ +#ifdef OSPFv2 + unsigned headers = (ifa->autype == OSPF_AUTH_CRYPT) ? OSPF_AUTH_CRYPT_SIZE : 0; +#else + unsigned headers = 0; +#endif + + return ifa->sk->tbsize - headers; +} + + #endif /* _BIRD_OSPF_PACKET_H_ */