diff options
40 files changed, 3856 insertions, 1790 deletions
diff --git a/configure.in b/configure.in index 309cb83..ec2417b 100644 --- a/configure.in +++ b/configure.in @@ -43,15 +43,13 @@ AC_SUBST(srcdir_rel_mf) if test "$enable_ipv6" = yes ; then ip=ipv6 SUFFIX6=6 - if test "$with_protocols" = all ; then - with_protocols=bgp,pipe,rip,static - fi else ip=ipv4 SUFFIX6="" - if test "$with_protocols" = all ; then - with_protocols=bgp,ospf,pipe,rip,static - fi +fi + +if test "$with_protocols" = all ; then + with_protocols=bgp,ospf,pipe,rip,static fi AC_SEARCH_LIBS(clock_gettime,[c rt posix4]) diff --git a/doc/bird.sgml b/doc/bird.sgml index a6278a1..81ef79b 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -302,8 +302,7 @@ to zero to disable it. An empty <cf><m/switch/</cf> is equivalent to <cf/on/ <cf/packets/ for packets sent and received by the protocol. Default: off. <tag>router id <m/IPv4 address/</tag> This option can be used to override global - router id for a given protocol. This option is not yet implemented for OSPF - protocol. Default: uses global router id. + router id for a given protocol. Default: uses global router id. <tag>import all | none | filter <m/name/ | filter { <m/filter commands/ } | where <m/filter expression/</tag> Specify a filter to be used for filtering routes coming from the protocol to the routing table. <cf/all/ is shorthand for <cf/where true/ and <cf/none/ is shorthand for <cf/where false/. Default: <cf/all/. diff --git a/doc/reply_codes b/doc/reply_codes index 4588182..db760fb 100644 --- a/doc/reply_codes +++ b/doc/reply_codes @@ -42,6 +42,7 @@ Reply codes of BIRD command-line interface 1014 Show ospf 1015 Show ospf interface 1016 Show ospf state/topology +1017 Show ospf lsadb 8000 Reply too long 8001 Route not found diff --git a/filter/filter.h b/filter/filter.h index 62bad84..e526a79 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -89,6 +89,7 @@ int filter_same(struct filter *new, struct filter *old); int i_same(struct f_inst *f1, struct f_inst *f2); void f_prefix_get_bounds(struct f_prefix *px, int *l, int *h); +void f_prefix_get_bounds(struct f_prefix *px, int *l, int *h); int val_compare(struct f_val v1, struct f_val v2); void val_print(struct f_val v); @@ -54,6 +54,7 @@ typedef u32 ip_addr; #define ipa_hton(x) x = _MI(htonl(_I(x))) #define ipa_ntoh(x) x = _MI(ntohl(_I(x))) #define ipa_classify(x) ipv4_classify(_I(x)) +#define ipa_has_link_scope(x) ipv4_has_link_scope(_I(x)) #define ipa_opposite(x,len) _MI(_I(x) ^ (len == 30 ? 3 : 1)) #define ipa_class_mask(x) _MI(ipv4_class_mask(_I(x))) #define ipa_from_u32(x) _MI(x) @@ -69,6 +70,11 @@ int ipv4_classify(u32); u32 ipv4_class_mask(u32); byte *ipv4_skip_header(byte *, int *); +static inline int ipv4_has_link_scope(u32 a) +{ + return 0; +} + static inline unsigned ipv4_hash(u32 a) { /* Returns a 16-bit value */ @@ -1,3 +1,4 @@ + /* * BIRD -- IP Addresses et Cetera for IPv6 * @@ -59,6 +60,7 @@ typedef struct ipv6_addr { #define ipa_hton(x) ipv6_hton(&(x)) #define ipa_ntoh(x) ipv6_ntoh(&(x)) #define ipa_classify(x) ipv6_classify(&(x)) +#define ipa_has_link_scope(x) ipv6_has_link_scope(&(x)) /* ipa_opposite and ipa_class_mask don't make sense with IPv6 */ /* ipa_from_u32 and ipa_to_u32 replaced by ipa_build */ #define ipa_build(a,b,c,d) _MI(a,b,c,d) @@ -68,6 +70,9 @@ typedef struct ipv6_addr { #define ipa_getbit(x, y) ipv6_getbit(x, y) #define ipa_absolutize(x,y) ipv6_absolutize(x,y) +/* In IPv6, SOCK_RAW does not return packet header */ +#define ip_skip_header(x, y) x + ip_addr ipv6_mkmask(unsigned); unsigned ipv6_mklen(ip_addr *); int ipv6_classify(ip_addr *); @@ -77,6 +82,11 @@ int ipv6_compare(ip_addr, ip_addr); int ipv4_pton_u32(char *, u32 *); void ipv6_absolutize(ip_addr *, ip_addr *); +static inline int ipv6_has_link_scope(ip_addr *a) +{ + return ((a->addr[0] & 0xffc00000) == 0xfe800000); +} + /* * This hash function looks well, but once IPv6 enters * mainstream use, we need to check that it has good diff --git a/lib/socket.h b/lib/socket.h index 53e89ed..c642bdf 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -50,9 +50,20 @@ int sk_send_to(sock *, unsigned len, ip_addr to, unsigned port); /* sk_send to g void sk_reallocate(sock *); /* Free and allocate tbuf & rbuf */ void sk_dump_all(void); int sk_set_ttl(sock *s, int ttl); /* Set TTL for given socket */ -int sk_set_md5_auth(sock *s, ip_addr a, char *passwd); /* Add or remove security associations for given passive socket */ + +/* Add or remove security associations for given passive socket */ +int sk_set_md5_auth(sock *s, ip_addr a, char *passwd); int sk_rx_ready(sock *s); +/* Prepare UDP or IP socket to multicasting. s->iface and s->ttl must be set */ +int sk_setup_multicast(sock *s); +int sk_join_group(sock *s, ip_addr maddr); +int sk_leave_group(sock *s, ip_addr maddr); + +#ifdef IPV6 +int sk_set_ipv6_checksum(sock *s, int offset); +#endif + static inline int sk_send_buffer_empty(sock *sk) { @@ -73,9 +84,7 @@ sk_send_buffer_empty(sock *sk) #define SK_TCP_ACTIVE 1 /* ? ? * * - ? - */ #define SK_TCP 2 #define SK_UDP 3 /* ? ? - - - ? ? */ -#define SK_UDP_MC 4 /* ? ? * * * * - */ #define SK_IP 5 /* ? - - * - ? ? */ -#define SK_IP_MC 6 /* ? - * * * * - */ #define SK_MAGIC 7 /* Internal use by sysdep code */ #define SK_UNIX_PASSIVE 8 #define SK_UNIX 9 diff --git a/nest/iface.h b/nest/iface.h index af98a76..a982c17 100644 --- a/nest/iface.h +++ b/nest/iface.h @@ -99,6 +99,7 @@ typedef struct neighbor { #define NEF_STICKY 1 neighbor *neigh_find(struct proto *, ip_addr *, unsigned flags); +neighbor *neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags); static inline int neigh_connected_to(struct proto *p, ip_addr *a, struct iface *i) { diff --git a/nest/neighbor.c b/nest/neighbor.c index 2c5af6a..a44667f 100644 --- a/nest/neighbor.c +++ b/nest/neighbor.c @@ -100,13 +100,21 @@ if_connected(ip_addr *a, struct iface *i) /* -1=error, 1=match, 0=no match */ * IP address, neigh_find() returns %NULL. */ + neighbor * neigh_find(struct proto *p, ip_addr *a, unsigned flags) { + return neigh_find2(p, a, NULL, flags); +} + + +neighbor * +neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags) +{ neighbor *n; int class, scope = SCOPE_HOST; unsigned int h = neigh_hash(p, a); - struct iface *i, *j; + struct iface *i; WALK_LIST(n, neigh_hash_table[h]) /* Search the cache */ if (n->proto == p && ipa_equal(*a, n->addr)) @@ -115,27 +123,31 @@ neigh_find(struct proto *p, ip_addr *a, unsigned flags) class = ipa_classify(*a); if (class < 0) /* Invalid address */ return NULL; - if ((class & IADDR_SCOPE_MASK) < SCOPE_SITE || + if (((class & IADDR_SCOPE_MASK) == SCOPE_HOST) || + (((class & IADDR_SCOPE_MASK) == SCOPE_LINK) && (ifa == NULL)) || !(class & IADDR_HOST)) return NULL; /* Bad scope or a somecast */ - j = NULL; - WALK_LIST(i, iface_list) - if ((scope = if_connected(a, i)) >= 0) - { - j = i; - break; - } - if (!j && !(flags & NEF_STICKY)) + if (ifa) + scope = if_connected(a, ifa); + else + WALK_LIST(i, iface_list) + if ((scope = if_connected(a, i)) >= 0) + { + ifa = i; + break; + } + + if (!ifa && !(flags & NEF_STICKY)) return NULL; n = sl_alloc(neigh_slab); n->addr = *a; - n->iface = j; - if (j) + n->iface = ifa; + if (ifa) { add_tail(&neigh_hash_table[h], &n->n); - add_tail(&j->neighbors, &n->if_n); + add_tail(&ifa->neighbors, &n->if_n); } else { diff --git a/nest/route.h b/nest/route.h index 1bd23a6..e45a8c6 100644 --- a/nest/route.h +++ b/nest/route.h @@ -37,6 +37,7 @@ struct fib_node { byte pxlen; byte flags; /* User-defined */ byte x0, x1; /* User-defined */ + u32 uid; /* Unique ID based on hash */ ip_addr prefix; /* In host order */ }; diff --git a/nest/rt-fib.c b/nest/rt-fib.c index 8d76f26..510aa76 100644 --- a/nest/rt-fib.c +++ b/nest/rt-fib.c @@ -172,6 +172,28 @@ fib_find(struct fib *f, ip_addr *a, int len) return e; } +/* +int +fib_histogram(struct fib *f) +{ + log(L_WARN "Histogram dump start %d %d", f->hash_size, f->entries); + + int i, j; + struct fib_node *e; + + for (i = 0; i < f->hash_size; i++) + { + j = 0; + for (e = f->hash_table[i]; e != NULL; e = e->next) + j++; + if (j > 0) + log(L_WARN "Histogram line %d: %d", i, j); + } + + log(L_WARN "Histogram dump end"); +} +*/ + /** * fib_get - find or create a FIB node * @f: FIB to work with @@ -187,6 +209,7 @@ fib_get(struct fib *f, ip_addr *a, int len) unsigned int h = ipa_hash(*a); struct fib_node **ee = f->hash_table + (h >> f->hash_shift); struct fib_node *g, *e = *ee; + u32 uid = h << 16; while (e && (e->pxlen != len || !ipa_equal(*a, e->prefix))) e = e->next; @@ -196,17 +219,31 @@ fib_get(struct fib *f, ip_addr *a, int len) if (len < 0 || len > BITS_PER_IP_ADDRESS || !ip_is_prefix(*a,len)) bug("fib_get() called for invalid address"); #endif + + while ((g = *ee) && g->uid < uid) + ee = &g->next; + while ((g = *ee) && g->uid == uid) + { + ee = &g->next; + uid++; + } + + if ((uid >> 16) != h) + log(L_ERR "FIB hash table chains are too long"); + + // log (L_WARN "FIB_GET %I %x %x", *a, h, uid); + e = sl_alloc(f->fib_slab); e->prefix = *a; e->pxlen = len; - while ((g = *ee) && ipa_hash(g->prefix) < h) - ee = &g->next; e->next = *ee; + e->uid = uid; *ee = e; e->readers = NULL; f->init(e); if (f->entries++ > f->entries_max) fib_rehash(f, HASH_HI_STEP); + return e; } diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 7b49bdf..801fe10 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -877,10 +877,12 @@ bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p z = bgp_set_attr_wa(ea->attrs+2, pool, BA_NEXT_HOP, NEXT_HOP_LENGTH); if (p->cf->next_hop_self || rta->dest != RTD_ROUTER || - (!p->is_internal && (e->attrs->iface != p->neigh->iface))) + ipa_equal(e->attrs->gw, IPA_NONE) || + ipa_has_link_scope(rta->gw) || + (!p->is_internal && (rta->iface != p->neigh->iface))) set_next_hop(z, p->source_addr); else - set_next_hop(z, e->attrs->gw); + set_next_hop(z, rta->gw); bgp_set_attr(ea->attrs+3, BA_LOCAL_PREF, 0); diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 9529f89..e809baf 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -20,6 +20,7 @@ static struct nbma_node *this_nbma; static struct area_net_config *this_pref; static struct ospf_stubnet_config *this_stubnet; +#ifdef OSPFv2 static void finish_iface_config(struct ospf_iface_patt *ip) { @@ -31,6 +32,16 @@ finish_iface_config(struct ospf_iface_patt *ip) 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 +finish_iface_config(struct ospf_iface_patt *ip) +{ + if ((ip->autype != OSPF_AUTH_NONE) || (get_passwords() != NULL)) + cf_error("Authentication not supported in OSPFv3"); +} +#endif CF_DECLS @@ -40,7 +51,7 @@ CF_KEYWORDS(HELLO, TRANSMIT, PRIORITY, DEAD, NONBROADCAST, POINTOPOINT, TYPE) CF_KEYWORDS(NONE, SIMPLE, AUTHENTICATION, STRICT, CRYPTOGRAPHIC) CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, LINK) CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY) -CF_KEYWORDS(WAIT, DELAY) +CF_KEYWORDS(WAIT, DELAY, LSADB) %type <t> opttext @@ -309,6 +320,9 @@ CF_CLI(SHOW OSPF TOPOLOGY, optsym opttext, [<name>], [[Show information about OS CF_CLI(SHOW OSPF STATE, optsym opttext, [<name>], [[Show information about OSPF network state]]) { ospf_sh_state(proto_get_named($4, &proto_ospf), 1); }; +CF_CLI(SHOW OSPF LSADB, optsym opttext, [<name>], [[Show content of OSPF LSA database]]) +{ ospf_sh_lsadb(proto_get_named($4, &proto_ospf)); }; + CF_CODE CF_END diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c index adc0276..a249d75 100644 --- a/proto/ospf/dbdes.c +++ b/proto/ospf/dbdes.c @@ -8,6 +8,37 @@ #include "ospf.h" + +#ifdef OSPFv2 +struct ospf_dbdes_packet +{ + struct ospf_packet ospf_packet; + u16 iface_mtu; + u8 options; + union imms imms; /* I, M, MS bits */ + u32 ddseq; +}; + +#define hton_opt(X) X +#define ntoh_opt(X) X +#endif + + +#ifdef OSPFv3 +struct ospf_dbdes_packet +{ + struct ospf_packet ospf_packet; + u32 options; + u16 iface_mtu; + u8 padding; + union imms imms; /* I, M, MS bits */ + u32 ddseq; +}; + +#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) { @@ -22,7 +53,7 @@ static void ospf_dump_dbdes(struct proto *p, struct ospf_dbdes_packet *pkt) log(L_TRACE "%s: ddseq %u", p->name, ntohl(pkt->ddseq)); struct ospf_lsa_header *plsa = (void *) (pkt + 1); - int i, j; + unsigned int i, j; j = (ntohs(op->length) - sizeof(struct ospf_dbdes_packet)) / sizeof(struct ospf_lsa_header); @@ -37,7 +68,7 @@ static void ospf_dump_dbdes(struct proto *p, struct ospf_dbdes_packet *pkt) * @n: neighbor * @next: whether to send a next packet in a sequence (1) or to retransmit the old one (0) * - * Sending of a database description packet is described in 10.6 of RFC 2328. + * Sending of a database description packet is described in 10.8 of RFC 2328. * Reception of each packet is acknowledged in the sequence number of another. * When I send a packet to a neighbor I keep a copy in a buffer. If the neighbor * does not reply, I don't create a new packet but just send the content @@ -54,25 +85,26 @@ ospf_dbdes_send(struct ospf_neighbor *n, int next) struct proto *p = &po->proto; u16 length, i, j; + /* FIXME ??? */ if ((oa->rt == NULL) || (EMPTY_LIST(po->lsal))) - originate_rt_lsa(oa); + update_rt_lsa(oa); switch (n->state) { case NEIGHBOR_EXSTART: /* Send empty packets */ n->myimms.bit.i = 1; - pkt = (struct ospf_dbdes_packet *) (ifa->ip_sk->tbuf); + pkt = (struct ospf_dbdes_packet *) (ifa->sk->tbuf); op = (struct ospf_packet *) pkt; ospf_pkt_fill_hdr(ifa, pkt, DBDES_P); pkt->iface_mtu = htons(ifa->iface->mtu); - pkt->options = oa->opt.byte; + 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->iface->name); - ospf_send_to(ifa->ip_sk, n->ip, ifa); + ospf_send_to(ifa, n->ip); break; case NEIGHBOR_EXCHANGE: @@ -88,8 +120,8 @@ ospf_dbdes_send(struct ospf_neighbor *n, int next) ospf_pkt_fill_hdr(ifa, pkt, DBDES_P); pkt->iface_mtu = htons(ifa->iface->mtu); - pkt->options = oa->opt.byte; pkt->ddseq = htonl(n->dds); + pkt->options = hton_opt(oa->options); j = i = (ospf_pkt_maxsize(ifa) - sizeof(struct ospf_dbdes_packet)) / sizeof(struct ospf_lsa_header); /* Number of possible lsaheaders to send */ lsa = (n->ldbdes + sizeof(struct ospf_dbdes_packet)); @@ -102,16 +134,8 @@ ospf_dbdes_send(struct ospf_neighbor *n, int next) for (; i > 0; i--) { struct top_hash_entry *en= (struct top_hash_entry *) sn; - int send = 1; - /* Don't send ext LSA into stub areas */ - if (oa->stub && (en->lsa.type == LSA_T_EXT)) send = 0; - /* Don't send ext LSAs through VLINK */ - if ((ifa->type == OSPF_IT_VLINK) && (en->lsa.type == LSA_T_EXT)) send = 0;; - /* Don't send LSA of other areas */ - if ((en->lsa.type != LSA_T_EXT) && (en->oa != oa)) send = 0; - - if (send) + if (ospf_lsa_flooding_allowed(&en->lsa, en->domain, ifa)) { htonlsah(&(en->lsa), lsa); DBG("Working on: %d\n", i); @@ -161,11 +185,11 @@ ospf_dbdes_send(struct ospf_neighbor *n, int next) } /* Copy last sent packet again */ - memcpy(ifa->ip_sk->tbuf, n->ldbdes, length); + memcpy(ifa->sk->tbuf, n->ldbdes, length); - OSPF_PACKET(ospf_dump_dbdes, (struct ospf_dbdes_packet *) ifa->ip_sk->tbuf, + OSPF_PACKET(ospf_dump_dbdes, (struct ospf_dbdes_packet *) ifa->sk->tbuf, "DBDES packet sent to %I via %s", n->ip, ifa->iface->name); - ospf_send_to(ifa->ip_sk, n->ip, n->ifa); + ospf_send_to(ifa, n->ip); if(n->myimms.bit.ms) tm_start(n->rxmt_timer, n->ifa->rxmtint); /* Restart timer */ @@ -204,13 +228,14 @@ ospf_dbdes_reqladd(struct ospf_dbdes_packet *ps, struct ospf_neighbor *n) for (i = 0; i < j; i++) { ntohlsah(plsa + i, &lsa); - if (((he = ospf_hash_find(gr, oa->areaid, lsa.id, lsa.rt, lsa.type)) == NULL) || + 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(n->lsrqh, oa->areaid, lsa.id, lsa.rt, lsa.type) == NULL) + if (ospf_hash_find_header(n->lsrqh, dom, &lsa) == NULL) { - sn = ospf_hash_get(n->lsrqh, oa, lsa.id, lsa.rt, lsa.type); + sn = ospf_hash_get_header(n->lsrqh, dom, &lsa); ntohlsah(plsa + i, &(sn->lsa)); s_add_tail(&(n->lsrql), SNODE sn); } @@ -219,13 +244,23 @@ ospf_dbdes_reqladd(struct ospf_dbdes_packet *ps, struct ospf_neighbor *n) } void -ospf_dbdes_receive(struct ospf_dbdes_packet *ps, - struct ospf_iface *ifa, struct ospf_neighbor *n) +ospf_dbdes_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, + struct ospf_neighbor *n) { - struct proto *p = &ifa->oa->po->proto; - u32 myrid = p->cf->global->router_id; - unsigned int size = ntohs(ps->ospf_packet.length); + struct proto_ospf *po = ifa->oa->po; + struct proto *p = &po->proto; + unsigned int size = ntohs(ps_i->length); + if (size < sizeof(struct ospf_dbdes_packet)) + { + log(L_ERR "Bad OSPF DBDES packet from %I - too short (%u B)", n->ip, size); + return; + } + + struct ospf_dbdes_packet *ps = (void *) ps_i; + u32 ps_ddseq = ntohl(ps->ddseq); + u32 ps_options = ntoh_opt(ps->options); + OSPF_PACKET(ospf_dump_dbdes, ps, "DBDES packet received from %I via %s", n->ip, ifa->iface->name); ospf_neigh_sm(n, INM_HELLOREC); @@ -243,12 +278,12 @@ ospf_dbdes_receive(struct ospf_dbdes_packet *ps, return; case NEIGHBOR_EXSTART: if ((ps->imms.bit.m && ps->imms.bit.ms && ps->imms.bit.i) - && (n->rid > myrid) && (size == sizeof(struct ospf_dbdes_packet))) + && (n->rid > po->router_id) && (size == sizeof(struct ospf_dbdes_packet))) { /* I'm slave! */ - n->dds = ntohl(ps->ddseq); - n->ddr = ntohl(ps->ddseq); - n->options = ps->options; + 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); @@ -258,11 +293,11 @@ ospf_dbdes_receive(struct ospf_dbdes_packet *ps, } if (((ps->imms.bit.i == 0) && (ps->imms.bit.ms == 0)) && - (n->rid < myrid) && (n->dds == ntohl(ps->ddseq))) + (n->rid < po->router_id) && (n->dds == ps_ddseq)) { /* I'm master! */ - n->options = ps->options; - n->ddr = ntohl(ps->ddseq) - 1; /* It will be set corectly a few lines down */ + 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); ospf_neigh_sm(n, INM_NEGDONE); @@ -274,8 +309,8 @@ ospf_dbdes_receive(struct ospf_dbdes_packet *ps, break; } case NEIGHBOR_EXCHANGE: - if ((ps->imms.byte == n->imms.byte) && (ps->options == n->options) && - (ntohl(ps->ddseq) == n->ddr)) + if ((ps->imms.byte == n->imms.byte) && (ps_options == n->options) && + (ps_ddseq == n->ddr)) { /* Duplicate packet */ OSPF_TRACE(D_PACKETS, "Received duplicate dbdes from %I.", n->ip); @@ -287,7 +322,7 @@ ospf_dbdes_receive(struct ospf_dbdes_packet *ps, return; } - n->ddr = ntohl(ps->ddseq); + n->ddr = ps_ddseq; if (ps->imms.bit.ms != n->imms.bit.ms) /* M/S bit differs */ { @@ -307,7 +342,7 @@ ospf_dbdes_receive(struct ospf_dbdes_packet *ps, n->imms.byte = ps->imms.byte; - if (ps->options != n->options) /* Options differs */ + if (ps_options != n->options) /* Options differs */ { OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (options)", n->ip); @@ -317,7 +352,7 @@ ospf_dbdes_receive(struct ospf_dbdes_packet *ps, if (n->myimms.bit.ms) { - if (ntohl(ps->ddseq) != n->dds) /* MASTER */ + if (ps_ddseq != n->dds) /* MASTER */ { OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (master)", n->ip); @@ -339,15 +374,15 @@ ospf_dbdes_receive(struct ospf_dbdes_packet *ps, } else { - if (ntohl(ps->ddseq) != (n->dds + 1)) /* SLAVE */ + if (ps_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 = ntohl(ps->ddseq); - n->dds = ntohl(ps->ddseq); + n->ddr = ps_ddseq; + n->dds = ps_ddseq; ospf_dbdes_reqladd(ps, n); ospf_dbdes_send(n, 1); } @@ -355,8 +390,8 @@ ospf_dbdes_receive(struct ospf_dbdes_packet *ps, break; case NEIGHBOR_LOADING: case NEIGHBOR_FULL: - if ((ps->imms.byte == n->imms.byte) && (ps->options == n->options) - && (ntohl(ps->ddseq) == n->ddr)) + if ((ps->imms.byte == n->imms.byte) && (ps_options == n->options) + && (ps_ddseq == n->ddr)) /* Only duplicate are accepted */ { OSPF_TRACE(D_PACKETS, "Received duplicate dbdes from %I.", n->ip); @@ -371,7 +406,7 @@ ospf_dbdes_receive(struct ospf_dbdes_packet *ps, { OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (full)", n->ip); - DBG("PS=%u, DDR=%u, DDS=%u\n", ntohl(ps->ddseq), n->ddr, n->dds); + DBG("PS=%u, DDR=%u, DDS=%u\n", ps_ddseq, n->ddr, n->dds); ospf_neigh_sm(n, INM_SEQMIS); } break; diff --git a/proto/ospf/dbdes.h b/proto/ospf/dbdes.h index af96292..63cca0a 100644 --- a/proto/ospf/dbdes.h +++ b/proto/ospf/dbdes.h @@ -11,7 +11,7 @@ #define _BIRD_OSPF_DBDES_H_ void ospf_dbdes_send(struct ospf_neighbor *n, int next); -void ospf_dbdes_receive(struct ospf_dbdes_packet *ps, - struct ospf_iface *ifa, struct ospf_neighbor *n); +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 45b6b61..738748d 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -8,22 +8,65 @@ #include "ospf.h" + +#ifdef OSPFv2 +struct ospf_hello_packet +{ + struct ospf_packet ospf_packet; + ip_addr netmask; + u16 helloint; + u8 options; + u8 priority; + u32 deadint; + u32 dr; + u32 bdr; +}; +#endif + + +#ifdef OSPFv3 +struct ospf_hello_packet +{ + struct ospf_packet ospf_packet; + u32 iface_id; + u8 priority; + u8 options3; + u8 options2; + u8 options; + u16 helloint; + u16 deadint; + u32 dr; + u32 bdr; +}; +#endif + + void -ospf_hello_receive(struct ospf_hello_packet *ps, - struct ospf_iface *ifa, struct ospf_neighbor *n, ip_addr faddr) +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 = "Bad OSPF HELLO packet from ", *rec = " received: "; + unsigned int size, i, twoway, oldpriority, eligible, peers; + u32 olddr, oldbdr, oldiface_id, tmp; u32 *pnrid; - ip_addr olddr, oldbdr; - ip_addr mask; - char *beg = "Bad OSPF hello packet from ", *rec = " received: "; - struct proto *p = (struct proto *) ifa->oa->po; - unsigned int size = ntohs(ps->ospf_packet.length), i, twoway, oldpriority, eligible = 0, peers; + + 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%s", faddr, (ifa->type == OSPF_IT_VLINK ? "vlink-" : ""), ifa->iface->name); - mask = ps->netmask; - ipa_ntoh(mask); +#ifdef OSPFv2 + ip_addr mask = ps->netmask; + ipa_ntoh(mask); if (ifa->type != OSPF_IT_VLINK) { char *msg = L_WARN "Received HELLO packet %s (%I) is inconsistent " @@ -50,24 +93,30 @@ ospf_hello_receive(struct ospf_hello_packet *ps, return; } } +#endif - if (ntohs(ps->helloint) != ifa->helloint) + tmp = ntohs(ps->helloint); + if (tmp != ifa->helloint) { - log(L_ERR "%s%I%shello interval mismatch (%d).", beg, faddr, rec, - ntohs(ps->helloint)); + log(L_ERR "%s%I%shello interval mismatch (%d).", beg, faddr, rec, tmp); return; } - if (ntohl(ps->deadint) != ifa->dead) +#ifdef OSPFv2 + tmp = ntohl(ps->deadint); +#else /* OSPFv3 */ + tmp = ntohs(ps->deadint); +#endif + if (tmp != ifa->dead) { - log(L_ERR "%s%I%sdead interval mismatch (%d).", beg, faddr, rec, - ntohl(ps->deadint)); + log(L_ERR "%s%I%sdead interval mismatch (%d).", beg, faddr, rec, tmp); return; } - if (ps->options != ifa->oa->opt.byte) + tmp = !(ps->options & OPT_E); + if (tmp != ifa->oa->stub) { - log(L_ERR "%s%I%soptions mismatch (0x%x).", beg, faddr, rec, ps->options); + log(L_ERR "%s%I%sstub area flag mismatch (%d).", beg, faddr, rec, tmp); return; } @@ -111,12 +160,12 @@ ospf_hello_receive(struct ospf_hello_packet *ps, n->rid = ntohl(((struct ospf_packet *) ps)->routerid); n->ip = faddr; - n->dr = ps->dr; - ipa_ntoh(n->dr); - n->bdr = ps->bdr; - ipa_ntoh(n->bdr); + n->dr = ntohl(ps->dr); + n->bdr = ntohl(ps->bdr); n->priority = ps->priority; - n->options = ps->options; +#ifdef OSPFv3 + n->iface_id = ntohl(ps->iface_id); +#endif } ospf_neigh_sm(n, INM_HELLOREC); @@ -127,7 +176,7 @@ ospf_hello_receive(struct ospf_hello_packet *ps, twoway = 0; for (i = 0; i < peers; i++) { - if (ntohl(*(pnrid + i)) == p->cf->global->router_id) + if (ntohl(pnrid[i]) == po->router_id) { DBG("%s: Twoway received from %I\n", p->name, faddr); ospf_neigh_sm(n, INM_2WAYREC); @@ -140,35 +189,54 @@ ospf_hello_receive(struct ospf_hello_packet *ps, ospf_neigh_sm(n, INM_1WAYREC); olddr = n->dr; - n->dr = ipa_ntoh(ps->dr); oldbdr = n->bdr; - n->bdr = ipa_ntoh(ps->bdr); oldpriority = n->priority; +#ifdef OSPFv3 + 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); - /* Router is declaring itself ad DR and there is no BDR */ - if (ipa_equal(n->ip, n->dr) && (ipa_to_u32(n->bdr) == 0) +#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 (ipa_equal(n->ip, n->bdr) && (n->state != NEIGHBOR_FULL)) + if ((n->bdr == neigh) && (n->state != NEIGHBOR_FULL)) ospf_iface_sm(ifa, ISM_BACKS); /* Neighbor is newly declaring itself as DR or BDR */ - if ((ipa_equal(n->ip, n->dr) && (!ipa_equal(n->dr, olddr))) - || (ipa_equal(n->ip, n->bdr) && (!ipa_equal(n->bdr, oldbdr)))) + 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 ((ipa_equal(n->ip, olddr) && (!ipa_equal(n->dr, olddr))) - || (ipa_equal(n->ip, oldbdr) && (!ipa_equal(n->bdr, oldbdr)))) + if (((olddr == neigh) && (n->dr != olddr)) + || ((oldbdr == neigh) && (n->bdr != oldbdr))) ospf_iface_sm(ifa, ISM_NEICH); } @@ -181,7 +249,7 @@ ospf_hello_receive(struct ospf_hello_packet *ps, } void -ospf_hello_send(timer * timer, int poll, struct ospf_neighbor *dirn) +ospf_hello_send(timer *timer, int poll, struct ospf_neighbor *dirn) { struct ospf_iface *ifa; struct ospf_hello_packet *pkt; @@ -207,34 +275,41 @@ ospf_hello_send(timer * timer, int poll, struct ospf_neighbor *dirn) p = (struct proto *) (ifa->oa->po); DBG("%s: Hello/Poll timer fired on interface %s.\n", p->name, ifa->iface->name); - /* Now we should send a hello packet */ - /* First a common packet header */ - if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_VLINK)) - { - pkt = (struct ospf_hello_packet *) (ifa->ip_sk->tbuf); - } - else - { - pkt = (struct ospf_hello_packet *) (ifa->hello_sk->tbuf); - } - /* Now fill ospf_hello header */ + /* Now we should send a hello packet */ + pkt = (struct ospf_hello_packet *) (ifa->sk->tbuf); op = (struct ospf_packet *) pkt; + /* Now fill ospf_hello header */ ospf_pkt_fill_hdr(ifa, pkt, HELLO_P); +#ifdef OSPFv2 pkt->netmask = ipa_mkmask(ifa->iface->addr->pxlen); ipa_hton(pkt->netmask); if ((ifa->type == OSPF_IT_VLINK) || (ifa->type == OSPF_IT_PTP)) pkt->netmask = IPA_NONE; +#endif + pkt->helloint = ntohs(ifa->helloint); - pkt->options = ifa->oa->opt.byte; pkt->priority = ifa->priority; + +#ifdef OSPFv3 + pkt->iface_id = htonl(ifa->iface->index); + + pkt->options3 = ifa->oa->options >> 16; + pkt->options2 = ifa->oa->options >> 8; +#endif + pkt->options = ifa->oa->options; + +#ifdef OSPFv2 pkt->deadint = htonl(ifa->dead); - pkt->dr = ifa->drip; - ipa_hton(pkt->dr); - pkt->bdr = ifa->bdrip; - ipa_hton(pkt->bdr); + pkt->dr = htonl(ipa_to_u32(ifa->drip)); + pkt->bdr = htonl(ipa_to_u32(ifa->bdrip)); +#else /* OSPFv3 */ + pkt->deadint = htons(ifa->dead); + pkt->dr = htonl(ifa->drid); + pkt->bdr = htonl(ifa->bdrid); +#endif /* Fill all neighbors */ i = 0; @@ -258,7 +333,7 @@ ospf_hello_send(timer * timer, int poll, struct ospf_neighbor *dirn) case OSPF_IT_NBMA: if (timer == NULL) /* Response to received hello */ { - ospf_send_to(ifa->ip_sk, dirn->ip, ifa); + ospf_send_to(ifa, dirn->ip); } else { @@ -283,7 +358,7 @@ ospf_hello_send(timer * timer, int poll, struct ospf_neighbor *dirn) if ((poll == 1) && (send)) { if (toall || (meeli && nb->eligible)) - ospf_send_to(ifa->ip_sk, nb->ip, ifa); + ospf_send_to(ifa, nb->ip); } } if (poll == 0) @@ -292,16 +367,16 @@ ospf_hello_send(timer * timer, int poll, struct ospf_neighbor *dirn) { if (toall || (n1->rid == ifa->drid) || (n1->rid == ifa->bdrid) || (meeli && (n1->priority > 0))) - ospf_send_to(ifa->ip_sk, n1->ip, ifa); + ospf_send_to(ifa, n1->ip); } } } break; case OSPF_IT_VLINK: - ospf_send_to(ifa->ip_sk, ifa->vip, ifa); + ospf_send_to(ifa, ifa->vip); break; default: - ospf_send_to(ifa->hello_sk, IPA_NONE, ifa); + ospf_send_to(ifa, AllSPFRouters); } OSPF_TRACE(D_PACKETS, "HELLO packet sent via %s%s", diff --git a/proto/ospf/hello.h b/proto/ospf/hello.h index d535935..5d73c09 100644 --- a/proto/ospf/hello.h +++ b/proto/ospf/hello.h @@ -10,8 +10,8 @@ #ifndef _BIRD_OSPF_HELLO_H_ #define _BIRD_OSPF_HELLO_H_ -void ospf_hello_receive(struct ospf_hello_packet *ps, - struct ospf_iface *ifa, struct ospf_neighbor *n, ip_addr faddr); +void ospf_hello_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, + struct ospf_neighbor *n, ip_addr faddr); void ospf_hello_send(timer *timer, int poll, struct ospf_neighbor *dirn); #endif /* _BIRD_OSPF_HELLO_H_ */ diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index ea38461..8db086e 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -59,7 +59,7 @@ rxbufsize(struct ospf_iface *ifa) } static sock * -ospf_open_ip_socket(struct ospf_iface *ifa) +ospf_open_socket(struct ospf_iface *ifa, int mc) { sock *ipsk; struct proto *p = &ifa->oa->po->proto; @@ -67,7 +67,14 @@ ospf_open_ip_socket(struct ospf_iface *ifa) ipsk = sk_new(p->pool); ipsk->type = SK_IP; ipsk->dport = OSPF_PROTO; - ipsk->saddr = ifa->iface->addr->ip; + +#ifdef OSPFv2 + // ipsk->saddr = ifa->iface->addr->ip; + ipsk->saddr = IPA_NONE; +#else /* OSPFv3 */ + ipsk->saddr = ifa->lladdr; +#endif + ipsk->tos = IP_PREC_INTERNET_CONTROL; ipsk->ttl = 1; if (ifa->type == OSPF_IT_VLINK) @@ -80,12 +87,28 @@ ospf_open_ip_socket(struct ospf_iface *ifa) ipsk->tbsize = ifa->iface->mtu; ipsk->data = (void *) ifa; if (sk_open(ipsk) != 0) + goto err; + +#ifdef OSPFv3 + /* 12 is an offset of the checksum in an OSPF packet */ + if (sk_set_ipv6_checksum(ipsk, 12) < 0) + goto err; +#endif + + if (mc) { - DBG("%s: SK_OPEN: ip open failed.\n", p->name); - return (NULL); + if (sk_setup_multicast(ipsk) < 0) + goto err; + + if (sk_join_group(ipsk, AllSPFRouters) < 0) + goto err; } - DBG("%s: SK_OPEN: ip opened.\n", p->name); - return (ipsk); + + return ipsk; + + err: + rfree(ipsk); + return NULL; } @@ -116,7 +139,7 @@ ospf_iface_chstate(struct ospf_iface *ifa, u8 state) ifa->vid, ospf_is[oldstate], ospf_is[state]); if (state == OSPF_IS_PTP) { - ifa->ip_sk = ospf_open_ip_socket(ifa); + ifa->sk = ospf_open_socket(ifa, 0); } } else @@ -126,47 +149,28 @@ ospf_iface_chstate(struct ospf_iface *ifa, u8 state) ifa->iface->name, ospf_is[oldstate], ospf_is[state]); if (ifa->iface->flags & IF_MULTICAST) { - if ((state == OSPF_IS_BACKUP) || (state == OSPF_IS_DR)) + if ((ifa->type != OSPF_IT_NBMA) && (ifa->ioprob == OSPF_I_OK) && + ((state == OSPF_IS_BACKUP) || (state == OSPF_IS_DR))) { - if ((ifa->dr_sk == NULL) && (ifa->type != OSPF_IT_NBMA)) - { - DBG("%s: Adding new multicast socket for (B)DR\n", p->name); - ifa->dr_sk = sk_new(p->pool); - ifa->dr_sk->type = SK_IP_MC; - ifa->dr_sk->sport = 0; - ifa->dr_sk->dport = OSPF_PROTO; - ifa->dr_sk->saddr = AllDRouters; - ifa->dr_sk->daddr = AllDRouters; - ifa->dr_sk->tos = IP_PREC_INTERNET_CONTROL; - ifa->dr_sk->ttl = 1; - ifa->dr_sk->rx_hook = ospf_rx_hook; - ifa->dr_sk->tx_hook = ospf_tx_hook; - ifa->dr_sk->err_hook = ospf_err_hook; - ifa->dr_sk->iface = ifa->iface; - ifa->dr_sk->rbsize = rxbufsize(ifa); - ifa->dr_sk->tbsize = ifa->iface->mtu; - ifa->dr_sk->data = (void *) ifa; - if (sk_open(ifa->dr_sk) != 0) - { - DBG("%s: SK_OPEN: new? mc open failed.\n", p->name); - } - } + /* FIXME some error handing ? */ + sk_join_group(ifa->sk, AllDRouters); + ifa->dr_up = 1; } - else + else if (ifa->dr_up) { - rfree(ifa->dr_sk); - ifa->dr_sk = NULL; + sk_leave_group(ifa->sk, AllDRouters); + ifa->dr_up = 0; } - if ((oldstate == OSPF_IS_DR) && (ifa->nlsa != NULL)) + if ((oldstate == OSPF_IS_DR) && (ifa->net_lsa != NULL)) { - ifa->nlsa->lsa.age = LSA_MAXAGE; + ifa->net_lsa->lsa.age = LSA_MAXAGE; if (state >= OSPF_IS_WAITING) { - ospf_lsupd_flush_nlsa(ifa->nlsa, ifa->oa); + ospf_lsupd_flush_nlsa(po, ifa->net_lsa); } if (can_flush_lsa(po)) - flush_lsa(ifa->nlsa, po); - ifa->nlsa = NULL; + flush_lsa(ifa->net_lsa, po); + ifa->net_lsa = NULL; } } } @@ -196,13 +200,12 @@ ospf_iface_down(struct ospf_iface *ifa) OSPF_TRACE(D_EVENTS, "Removing neighbor %I", n->ip); ospf_neigh_remove(n); } - rfree(ifa->hello_sk); - rfree(ifa->dr_sk); - rfree(ifa->ip_sk); + + rfree(ifa->sk); + ifa->sk = NULL; if (ifa->type == OSPF_IT_VLINK) { - ifa->ip_sk = NULL; ifa->iface = NULL; return; } @@ -260,6 +263,7 @@ ospf_iface_sm(struct ospf_iface *ifa, int event) hello_timer_hook(ifa->hello_timer); } + schedule_link_lsa(ifa); schedule_rt_lsa(ifa->oa); break; case ISM_BACKS: @@ -280,6 +284,7 @@ ospf_iface_sm(struct ospf_iface *ifa, int event) case ISM_DOWN: ospf_iface_chstate(ifa, OSPF_IS_DOWN); ospf_iface_down(ifa); + schedule_link_lsa(ifa); schedule_rt_lsa(oa); break; case ISM_LOOP: /* Useless? */ @@ -298,6 +303,7 @@ ospf_iface_sm(struct ospf_iface *ifa, int event) } +#if 0 static sock * ospf_open_mc_socket(struct ospf_iface *ifa) { @@ -308,7 +314,14 @@ ospf_open_mc_socket(struct ospf_iface *ifa) mcsk->type = SK_IP_MC; mcsk->sport = 0; mcsk->dport = OSPF_PROTO; + +#ifdef OSPFv2 mcsk->saddr = AllSPFRouters; +#else /* OSPFv3 */ + // mcsk->saddr = AllSPFRouters; + mcsk->saddr = ifa->lladdr; +#endif + mcsk->daddr = AllSPFRouters; mcsk->tos = IP_PREC_INTERNET_CONTROL; mcsk->ttl = 1; @@ -327,6 +340,7 @@ ospf_open_mc_socket(struct ospf_iface *ifa) DBG("%s: SK_OPEN: mc opened.\n", p->name); return (mcsk); } +#endif u8 ospf_iface_clasify(struct iface * ifa) @@ -363,20 +377,8 @@ ospf_iface_add(struct object_lock *lock) ifa->ioprob = OSPF_I_OK; - if (ifa->type != OSPF_IT_NBMA) - { - if ((ifa->hello_sk = ospf_open_mc_socket(ifa)) == NULL) - { - log("%s: Huh? could not open mc socket on interface %s?", p->name, - iface->name); - log("%s: Declaring as stub.", p->name); - ifa->stub = 1; - ifa->ioprob += OSPF_I_MC; - } - ifa->dr_sk = NULL; - } - - if ((ifa->ip_sk = ospf_open_ip_socket(ifa)) == NULL) + ifa->sk = ospf_open_socket(ifa, ifa->type != OSPF_IT_NBMA); + if (ifa->sk == NULL) { log("%s: Huh? could not open ip socket on interface %s?", p->name, iface->name); @@ -412,8 +414,33 @@ ospf_iface_new(struct proto_ospf *po, struct iface *iface, ifa->waitint = ip->waitint; ifa->dead = (ip->dead == 0) ? ip->deadc * ifa->helloint : ip->dead; ifa->stub = ip->stub; + +#ifdef OSPFv2 ifa->autype = ip->autype; ifa->passwords = ip->passwords; +#endif + +#ifdef OSPFv3 + ifa->instance_id = ip->instance_id; + + ifa->lladdr = IPA_NONE; + + /* Find link-local address */ + if (ifa->type != OSPF_IT_VLINK) + { + struct ifa *a; + WALK_LIST(a, iface->addrs) + if (a->scope == SCOPE_LINK) + { + ifa->lladdr = a->ip; + break; + } + + if (! ipa_nonzero(ifa->lladdr)) + log(L_WARN "%s: Missing link local address on interface %s", p->name, iface->name); + } +#endif + ifa->rxbuf = ip->rxbuf; if (ip->type == OSPF_IT_UNDEF) @@ -501,23 +528,12 @@ ospf_iface_change_mtu(struct proto_ospf *po, struct ospf_iface *ifa) struct ospf_packet *op; struct ospf_neighbor *n; OSPF_TRACE(D_EVENTS, "Changing MTU on interface %s.", ifa->iface->name); - if (ifa->hello_sk) - { - ifa->hello_sk->rbsize = rxbufsize(ifa); - ifa->hello_sk->tbsize = ifa->iface->mtu; - sk_reallocate(ifa->hello_sk); - } - if (ifa->dr_sk) - { - ifa->dr_sk->rbsize = rxbufsize(ifa); - ifa->dr_sk->tbsize = ifa->iface->mtu; - sk_reallocate(ifa->dr_sk); - } - if (ifa->ip_sk) + + if (ifa->sk) { - ifa->ip_sk->rbsize = rxbufsize(ifa); - ifa->ip_sk->tbsize = ifa->iface->mtu; - sk_reallocate(ifa->ip_sk); + ifa->sk->rbsize = rxbufsize(ifa); + ifa->sk->tbsize = ifa->iface->mtu; + sk_reallocate(ifa->sk); } WALK_LIST(n, ifa->neigh_list) diff --git a/proto/ospf/lsack.c b/proto/ospf/lsack.c index 824767a..c740ef6 100644 --- a/proto/ospf/lsack.c +++ b/proto/ospf/lsack.c @@ -8,6 +8,14 @@ #include "ospf.h" + +struct ospf_lsack_packet +{ + struct ospf_packet ospf_packet; + struct ospf_lsa_header lsh[]; +}; + + char *s_queue[] = { "direct", "delayed" }; @@ -18,14 +26,12 @@ static void ospf_dump_lsack(struct proto *p, struct ospf_lsack_packet *pkt) ASSERT(op->type == LSACK_P); ospf_dump_common(p, op); - struct ospf_lsa_header *plsa = (void *) (pkt + 1); - int i, j; - + unsigned int i, j; j = (ntohs(op->length) - sizeof(struct ospf_lsack_packet)) / sizeof(struct ospf_lsa_header); for (i = 0; i < j; i++) - ospf_dump_lsahdr(p, plsa + i); + ospf_dump_lsahdr(p, pkt->lsh + i); } @@ -51,7 +57,6 @@ ospf_lsack_send(struct ospf_neighbor *n, int queue) { struct ospf_packet *op; struct ospf_lsack_packet *pk; - sock *sk; u16 len, i = 0; struct ospf_lsa_header *h; struct lsah_n *no; @@ -61,24 +66,19 @@ ospf_lsack_send(struct ospf_neighbor *n, int queue) if (EMPTY_LIST(n->ackl[queue])) return; - if (ifa->type == OSPF_IT_BCAST) - sk = ifa->hello_sk; - else - sk = ifa->ip_sk; - - pk = (struct ospf_lsack_packet *) sk->tbuf; - op = (struct ospf_packet *) sk->tbuf; + pk = (struct ospf_lsack_packet *) ifa->sk->tbuf; + op = (struct ospf_packet *) ifa->sk->tbuf; ospf_pkt_fill_hdr(n->ifa, pk, LSACK_P); - h = (struct ospf_lsa_header *) (pk + 1); + h = pk->lsh; while (!EMPTY_LIST(n->ackl[queue])) { no = (struct lsah_n *) HEAD(n->ackl[queue]); memcpy(h + i, &no->lsa, sizeof(struct ospf_lsa_header)); - i++; - DBG("Iter %u ID: %R, RT: %R, Type: %u\n", i, ntohl((h + i)->id), + DBG("Iter %u ID: %R, RT: %R, Type: %04x\n", i, ntohl((h + i)->id), ntohl((h + i)->rt), (h + i)->type); + i++; rem_node(NODE no); mb_free(no); if ((i * sizeof(struct ospf_lsa_header) + @@ -92,22 +92,22 @@ ospf_lsack_send(struct ospf_neighbor *n, int queue) op->length = htons(len); DBG("Sending and continuing! Len=%u\n", len); - OSPF_PACKET(ospf_dump_lsack, (struct ospf_lsack_packet *) sk->tbuf, + OSPF_PACKET(ospf_dump_lsack, (struct ospf_lsack_packet *) ifa->sk->tbuf, "LSACK packet sent via %s", ifa->iface->name); if (ifa->type == OSPF_IT_BCAST) { if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP)) - ospf_send_to(sk, AllSPFRouters, ifa); + ospf_send_to(ifa, AllSPFRouters); else - ospf_send_to(sk, AllDRouters, ifa); + ospf_send_to(ifa, AllDRouters); } else { if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP)) - 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); } ospf_pkt_fill_hdr(n->ifa, pk, LSACK_P); @@ -120,36 +120,37 @@ ospf_lsack_send(struct ospf_neighbor *n, int queue) op->length = htons(len); DBG("Sending! Len=%u\n", len); - OSPF_PACKET(ospf_dump_lsack, (struct ospf_lsack_packet *) sk->tbuf, + OSPF_PACKET(ospf_dump_lsack, (struct ospf_lsack_packet *) ifa->sk->tbuf, "LSACK packet sent via %s", ifa->iface->name); if (ifa->type == OSPF_IT_BCAST) { if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP)) - { - ospf_send_to(sk, AllSPFRouters, ifa); - } + ospf_send_to(ifa, AllSPFRouters); else - { - ospf_send_to(sk, AllDRouters, ifa); - } + ospf_send_to(ifa, AllDRouters); } else - { - ospf_send_to_agt(sk, ifa, NEIGHBOR_EXCHANGE); - } + ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE); } void -ospf_lsack_receive(struct ospf_lsack_packet *ps, - struct ospf_iface *ifa, struct ospf_neighbor *n) +ospf_lsack_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, + struct ospf_neighbor *n) { - struct ospf_lsa_header lsa, *plsa; - u16 nolsa; - struct top_hash_entry *en; struct proto *p = &ifa->oa->po->proto; - unsigned int size = ntohs(ps->ospf_packet.length), i; + struct ospf_lsa_header lsa; + struct top_hash_entry *en; + unsigned int i, lsano; + + 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; + } + struct ospf_lsack_packet *ps = (void *) ps_i; OSPF_PACKET(ospf_dump_lsack, ps, "LSACK packet received from %I via %s", n->ip, ifa->iface->name); ospf_neigh_sm(n, INM_HELLOREC); @@ -157,22 +158,13 @@ ospf_lsack_receive(struct ospf_lsack_packet *ps, if (n->state < NEIGHBOR_EXCHANGE) return; - nolsa = (size - sizeof(struct ospf_lsack_packet)) / + lsano = (size - sizeof(struct ospf_lsack_packet)) / sizeof(struct ospf_lsa_header); - - if ((nolsa < 1) || ((size - sizeof(struct ospf_lsack_packet)) != - (nolsa * sizeof(struct ospf_lsa_header)))) - { - log(L_ERR "Received corrupted LS ack from %I", n->ip); - return; - } - - plsa = (struct ospf_lsa_header *) (ps + 1); - - for (i = 0; i < nolsa; i++) + for (i = 0; i < lsano; i++) { - ntohlsah(plsa + i, &lsa); - if ((en = ospf_hash_find_header(n->lsrth, n->ifa->oa->areaid, &lsa)) == NULL) + 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 */ if (lsa_comp(&lsa, &en->lsa) != CMP_SAME) /* pg 156 */ @@ -180,12 +172,12 @@ ospf_lsack_receive(struct ospf_lsack_packet *ps, if ((lsa.sn == LSA_MAXSEQNO) && (lsa.age == LSA_MAXAGE)) continue; - OSPF_TRACE(D_PACKETS, "Strange LS acknoledgement from %I", n->ip); - OSPF_TRACE(D_PACKETS, "Id: %R, Rt: %R, Type: %u", - lsa.id, lsa.rt, lsa.type); - OSPF_TRACE(D_PACKETS, "I have: Age: %4u, Seqno: 0x%08x, Sum: %u", + 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); + OSPF_TRACE(D_PACKETS, "I have: Age: %4u, Seq: %08x, Sum: %04x", en->lsa.age, en->lsa.sn, en->lsa.checksum); - OSPF_TRACE(D_PACKETS, "He has: Age: %4u, Seqno: 0x%08x, Sum: %u", + OSPF_TRACE(D_PACKETS, "He has: Age: %4u, Seq: %08x, Sum: %04x", lsa.age, lsa.sn, lsa.checksum); continue; } diff --git a/proto/ospf/lsack.h b/proto/ospf/lsack.h index 05cc22f..63a436d 100644 --- a/proto/ospf/lsack.h +++ b/proto/ospf/lsack.h @@ -16,8 +16,8 @@ struct lsah_n struct ospf_lsa_header lsa; }; -void ospf_lsack_receive(struct ospf_lsack_packet *ps, - struct ospf_iface *ifa, struct ospf_neighbor *n); +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); diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c index e624b6c..ab63398 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -14,7 +14,7 @@ flush_lsa(struct top_hash_entry *en, struct proto_ospf *po) struct proto *p = &po->proto; OSPF_TRACE(D_EVENTS, - "Going to remove node Type: %u, Id: %R, Rt: %R, Age: %u, SN: 0x%x", + "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) @@ -65,8 +65,7 @@ ospf_age(struct proto_ospf *po) flush_lsa(en, po); continue; } - if ((en->lsa.rt == p->cf->global->router_id) &&(en->lsa.age >= - LSREFRESHTIME)) + 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); @@ -75,7 +74,7 @@ ospf_age(struct proto_ospf *po) en->inst_t = now; en->ini_age = 0; lsasum_calculate(&en->lsa, en->lsa_body); - ospf_lsupd_flood(NULL, NULL, &en->lsa, NULL, en->oa, 1); + 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) @@ -96,216 +95,84 @@ void htonlsah(struct ospf_lsa_header *h, struct ospf_lsa_header *n) { n->age = htons(h->age); +#ifdef OSPFv2 n->options = h->options; - n->type = h->type; +#endif + n->type = htont(h->type); n->id = htonl(h->id); n->rt = htonl(h->rt); n->sn = htonl(h->sn); n->checksum = htons(h->checksum); n->length = htons(h->length); -}; +} void ntohlsah(struct ospf_lsa_header *n, struct ospf_lsa_header *h) { h->age = ntohs(n->age); +#ifdef OSPFv2 h->options = n->options; - h->type = n->type; +#endif + h->type = ntoht(n->type); h->id = ntohl(n->id); h->rt = ntohl(n->rt); h->sn = ntohl(n->sn); h->checksum = ntohs(n->checksum); h->length = ntohs(n->length); -}; +} void -htonlsab(void *h, void *n, u8 type, u16 len) +htonlsab(void *h, void *n, u16 type, u16 len) { - unsigned int i; - switch (type) - { - case LSA_T_RT: - { - struct ospf_lsa_rt *hrt, *nrt; - struct ospf_lsa_rt_link *hrtl, *nrtl; - u16 links; - - nrt = n; - hrt = h; - links = hrt->links; - - nrt->veb.byte = hrt->veb.byte; - nrt->padding = 0; - nrt->links = htons(hrt->links); - nrtl = (struct ospf_lsa_rt_link *) (nrt + 1); - hrtl = (struct ospf_lsa_rt_link *) (hrt + 1); - for (i = 0; i < links; i++) - { - (nrtl + i)->id = htonl((hrtl + i)->id); - (nrtl + i)->data = htonl((hrtl + i)->data); - (nrtl + i)->type = (hrtl + i)->type; - (nrtl + i)->notos = (hrtl + i)->notos; - (nrtl + i)->metric = htons((hrtl + i)->metric); - } - break; - } - case LSA_T_NET: - { - u32 *hid, *nid; + u32 *hid = h; + u32 *nid = n; + int i; - nid = n; - hid = h; - - for (i = 0; i < (len / sizeof(u32)); i++) - { - *(nid + i) = htonl(*(hid + i)); - } - break; - } - case LSA_T_SUM_NET: - case LSA_T_SUM_RT: - { - struct ospf_lsa_sum *hs, *ns; - union ospf_lsa_sum_tm *hn, *nn; - - hs = h; - ns = n; - - ns->netmask = hs->netmask; - ipa_hton(ns->netmask); - - hn = (union ospf_lsa_sum_tm *) (hs + 1); - nn = (union ospf_lsa_sum_tm *) (ns + 1); - - for (i = 0; i < ((len - sizeof(struct ospf_lsa_sum)) / - sizeof(union ospf_lsa_sum_tm)); i++) - { - (nn + i)->metric = htonl((hn + i)->metric); - } - break; - } - case LSA_T_EXT: - { - struct ospf_lsa_ext *he, *ne; - struct ospf_lsa_ext_tos *ht, *nt; - - he = h; - ne = n; - - ne->netmask = he->netmask; - ipa_hton(ne->netmask); + for (i = 0; i < (len / sizeof(u32)); i++) + nid[i] = htonl(hid[i]); +} - ht = (struct ospf_lsa_ext_tos *) (he + 1); - nt = (struct ospf_lsa_ext_tos *) (ne + 1); +void +ntohlsab(void *n, void *h, u16 type, u16 len) +{ + u32 *nid = n; + u32 *hid = h; + int i; - for (i = 0; i < ((len - sizeof(struct ospf_lsa_ext)) / - sizeof(struct ospf_lsa_ext_tos)); i++) - { - (nt + i)->etm.metric = htonl((ht + i)->etm.metric); - (nt + i)->fwaddr = (ht + i)->fwaddr; - ipa_hton((nt + i)->fwaddr); - (nt + i)->tag = htonl((ht + i)->tag); - } - break; - } - default: - bug("(hton): Unknown LSA"); - } -}; + for (i = 0; i < (len / sizeof(u32)); i++) + hid[i] = ntohl(nid[i]); +} +/* void -ntohlsab(void *n, void *h, u8 type, u16 len) +buf_dump(const char *hdr, const byte *buf, int blen) { - unsigned int i; - switch (type) - { - case LSA_T_RT: - { - struct ospf_lsa_rt *hrt, *nrt; - struct ospf_lsa_rt_link *hrtl, *nrtl; - u16 links; - - nrt = n; - hrt = h; - - hrt->veb.byte = nrt->veb.byte; - hrt->padding = 0; - links = hrt->links = ntohs(nrt->links); - nrtl = (struct ospf_lsa_rt_link *) (nrt + 1); - hrtl = (struct ospf_lsa_rt_link *) (hrt + 1); - for (i = 0; i < links; i++) - { - (hrtl + i)->id = ntohl((nrtl + i)->id); - (hrtl + i)->data = ntohl((nrtl + i)->data); - (hrtl + i)->type = (nrtl + i)->type; - (hrtl + i)->notos = (nrtl + i)->notos; - (hrtl + i)->metric = ntohs((nrtl + i)->metric); - } - break; - } - case LSA_T_NET: - { - u32 *hid, *nid; + char b2[1024]; + char *bp; + int first = 1; + int i; - hid = h; - nid = n; + const char *lhdr = hdr; - for (i = 0; i < (len / sizeof(u32)); i++) - { - *(hid + i) = ntohl(*(nid + i)); - } - break; - } - case LSA_T_SUM_NET: - case LSA_T_SUM_RT: + bp = b2; + for(i = 0; i < blen; i++) { - struct ospf_lsa_sum *hs, *ns; - union ospf_lsa_sum_tm *hn, *nn; - - hs = h; - ns = n; - - hs->netmask = ns->netmask; - ipa_ntoh(hs->netmask); + if ((i > 0) && ((i % 16) == 0)) + { + *bp = 0; + log(L_WARN "%s\t%s", lhdr, b2); + lhdr = ""; + bp = b2; + } - hn = (union ospf_lsa_sum_tm *) (hs + 1); - nn = (union ospf_lsa_sum_tm *) (ns + 1); + bp += snprintf(bp, 1022, "%02x ", buf[i]); - for (i = 0; i < ((len - sizeof(struct ospf_lsa_sum)) / - sizeof(union ospf_lsa_sum_tm)); i++) - { - (hn + i)->metric = ntohl((nn + i)->metric); - } - break; } - case LSA_T_EXT: - { - struct ospf_lsa_ext *he, *ne; - struct ospf_lsa_ext_tos *ht, *nt; - - he = h; - ne = n; - - he->netmask = ne->netmask; - ipa_ntoh(he->netmask); - ht = (struct ospf_lsa_ext_tos *) (he + 1); - nt = (struct ospf_lsa_ext_tos *) (ne + 1); - - for (i = 0; i < ((len - sizeof(struct ospf_lsa_ext)) / - sizeof(struct ospf_lsa_ext_tos)); i++) - { - (ht + i)->etm.metric = ntohl((nt + i)->etm.metric); - (ht + i)->fwaddr = (nt + i)->fwaddr; - ipa_ntoh((ht + i)->fwaddr); - (ht + i)->tag = ntohl((nt + i)->tag); - } - break; - } - default: - bug("(ntoh): Unknown LSA"); - } -}; + *bp = 0; + log(L_WARN "%s\t%s", lhdr, b2); +} +*/ #define MODX 4102 /* larges signed value without overflow */ @@ -317,17 +184,26 @@ ntohlsab(void *n, void *h, u8 type, u16 len) void lsasum_calculate(struct ospf_lsa_header *h, void *body) { - u16 length; - - length = h->length; + u16 length = h->length; + u16 type = h->type; + // log(L_WARN "Checksum %R %R %d start (len %d)", h->id, h->rt, h->type, length); htonlsah(h, h); - htonlsab(body, body, h->type, length - sizeof(struct ospf_lsa_header)); + htonlsab(body, body, type, length - sizeof(struct ospf_lsa_header)); + + /* + char buf[1024]; + memcpy(buf, h, sizeof(struct ospf_lsa_header)); + memcpy(buf + sizeof(struct ospf_lsa_header), body, length - sizeof(struct ospf_lsa_header)); + buf_dump("CALC", buf, length); + */ (void) lsasum_check(h, body); + // log(L_WARN "Checksum result %4x", h->checksum); + ntohlsah(h, h); - ntohlsab(body, body, h->type, length - sizeof(struct ospf_lsa_header)); + ntohlsab(body, body, type, length - sizeof(struct ospf_lsa_header)); } /* @@ -343,7 +219,8 @@ lsasum_check(struct ospf_lsa_header *h, void *body) u16 length; b = body; - sp = (char *) &h->options; + sp = (char *) h; + sp += 2; /* Skip Age field */ length = ntohs(h->length) - 2; h->checksum = 0; @@ -415,47 +292,254 @@ 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) +{ + unsigned int i, max; + + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_rt))) + return 0; + + struct ospf_lsa_rt_link *rtl = (struct ospf_lsa_rt_link *) (body + 1); + max = lsa_rt_count(lsa); + +#ifdef OSPFv2 + if (body->links != max) + return 0; +#endif + + for (i = 0; i < max; i++) + { + u8 type = rtl[i].type; + if (!((type == LSART_PTP) || + (type == LSART_NET) || +#ifdef OSPFv2 + (type == LSART_STUB) || +#endif + (type == LSART_VLNK))) + return 0; + } + return 1; +} + +static int +lsa_validate_net(struct ospf_lsa_header *lsa, struct ospf_lsa_net *body) +{ + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_net))) + return 0; + + return 1; +} + +#ifdef OSPFv2 + +static int +lsa_validate_sum(struct ospf_lsa_header *lsa, struct ospf_lsa_sum *body) +{ + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum))) + return 0; + + /* First field should have TOS = 0, we ignore other TOS fields */ + if ((body->metric & LSA_SUM_TOS) != 0) + return 0; + + return 1; +} +#define lsa_validate_sum_net(A,B) lsa_validate_sum(A,B) +#define lsa_validate_sum_rt(A,B) lsa_validate_sum(A,B) + +static int +lsa_validate_ext(struct ospf_lsa_header *lsa, struct ospf_lsa_ext *body) +{ + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext))) + return 0; + + /* First field should have TOS = 0, we ignore other TOS fields */ + if ((body->metric & LSA_EXT_TOS) != 0) + return 0; + + return 1; +} + +#else /* OSPFv3 */ + +static inline int +pxlen(u32 *buf) +{ + return *buf >> 24; +} + +static int +lsa_validate_sum_net(struct ospf_lsa_header *lsa, struct ospf_lsa_sum_net *body) +{ + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum_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) + + IPV6_PREFIX_SPACE(pxl))) + return 0; + + return 1; +} + + +static int +lsa_validate_sum_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_sum_rt *body) +{ + if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum_rt))) + return 0; + + return 1; +} + +static int +lsa_validate_ext(struct ospf_lsa_header *lsa, struct ospf_lsa_ext *body) +{ + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext) + 4)) + return 0; + + u8 pxl = pxlen(body->rest); + if (pxl > MAX_PREFIX_LENGTH) + return 0; + + int len = IPV6_PREFIX_SPACE(pxl); + if (body->metric & LSA_EXT_FBIT) // forwardinf address + len += 16; + if (body->metric & LSA_EXT_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)) + return 0; + + return 1; +} + +static int +lsa_validate_pxlist(struct ospf_lsa_header *lsa, u32 pxcount, unsigned int offset, u8 *pbuf) +{ + unsigned int bound = lsa->length - HDRLEN - 4; + u32 i; + + for (i = 0; i < pxcount; i++) + { + if (offset > bound) + return 0; + + u8 pxl = pxlen((u32 *) (pbuf + offset)); + if (pxl > MAX_PREFIX_LENGTH) + return 0; + + offset += IPV6_PREFIX_SPACE(pxl); + } + + if (lsa->length != (HDRLEN + offset)) + return 0; + + return 1; +} + +static int +lsa_validate_link(struct ospf_lsa_header *lsa, struct ospf_lsa_link *body) +{ + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_link))) + return 0; + + return lsa_validate_pxlist(lsa, body->pxcount, sizeof(struct ospf_lsa_link), (u8 *) body); +} + +static int +lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body) +{ + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_prefix))) + return 0; + + return lsa_validate_pxlist(lsa, body->pxcount, sizeof(struct ospf_lsa_prefix), (u8 *) body); +} + +#endif + + +/** + * lsa_validate - check whether given LSA is valid + * @lsa: LSA header + * @body: pointer to LSA body + * + * Checks internal structure of given LSA body (minimal length, + * consistency). Returns true if valid. + */ + +int +lsa_validate(struct ospf_lsa_header *lsa, void *body) +{ + switch (lsa->type) + { + case LSA_T_RT: + return lsa_validate_rt(lsa, body); + case LSA_T_NET: + return lsa_validate_net(lsa, body); + case LSA_T_SUM_NET: + return lsa_validate_sum_net(lsa, body); + case LSA_T_SUM_RT: + return lsa_validate_sum_rt(lsa, body); + case LSA_T_EXT: + return lsa_validate_ext(lsa, body); +#ifdef OSPFv3 + 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; + } +} + /** * lsa_install_new - install new LSA into database + * @po: OSPF protocol * @lsa: LSA header + * @domain: domain of LSA * @body: pointer to LSA body - * @oa: current ospf_area + * * 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 ospf_lsa_header *lsa, void *body, struct ospf_area *oa) +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; - unsigned i; struct top_hash_entry *en; - struct proto_ospf *po = oa->po; - if ((en = ospf_hash_find_header(po->gr, oa->areaid, lsa)) == NULL) + if ((en = ospf_hash_find_header(po->gr, domain, lsa)) == NULL) { - en = ospf_hash_get_header(po->gr, oa, lsa); + en = ospf_hash_get_header(po->gr, domain, lsa); change = 1; } else { - if ((en->lsa.length != lsa->length) || (en->lsa.options != lsa->options) - || ((en->lsa.age == LSA_MAXAGE) || (lsa->age == LSA_MAXAGE))) + 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; - else - { - u8 *k = en->lsa_body, *l = body; - for (i = 0; i < (lsa->length - sizeof(struct ospf_lsa_header)); i++) - { - if (*(k + i) != *(l + i)) - { - change = 1; - break; - } - } - } + s_rem_node(SNODE en); } @@ -471,9 +555,7 @@ lsa_install_new(struct ospf_lsa_header *lsa, void *body, struct ospf_area *oa) 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 c7f16d5..ed929be 100644 --- a/proto/ospf/lsalib.h +++ b/proto/ospf/lsalib.h @@ -12,16 +12,16 @@ 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, u8 type, u16 len); -void ntohlsab(void *n, void *h, u8 type, u16 len); +void htonlsab(void *h, void *n, u16 type, u16 len); +void ntohlsab(void *n, void *h, u16 type, u16 len); 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); -struct top_hash_entry *lsa_install_new(struct ospf_lsa_header *lsa, - void *body, struct ospf_area *oa); +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); diff --git a/proto/ospf/lsreq.c b/proto/ospf/lsreq.c index e7d7af2..0e360d7 100644 --- a/proto/ospf/lsreq.c +++ b/proto/ospf/lsreq.c @@ -8,6 +8,14 @@ #include "ospf.h" + +struct ospf_lsreq_packet +{ + struct ospf_packet ospf_packet; + struct ospf_lsreq_header lsh[]; +}; + + static void ospf_dump_lsreq(struct proto *p, struct ospf_lsreq_packet *pkt) { struct ospf_packet *op = &pkt->ospf_packet; @@ -15,15 +23,13 @@ static void ospf_dump_lsreq(struct proto *p, struct ospf_lsreq_packet *pkt) ASSERT(op->type == LSREQ_P); ospf_dump_common(p, op); - struct ospf_lsreq_header *plsr = (void *) (pkt + 1); - int i, j; - + unsigned int i, j; j = (ntohs(op->length) - sizeof(struct ospf_lsreq_packet)) / sizeof(struct ospf_lsreq_header); for (i = 0; i < j; i++) - log(L_TRACE "%s: LSR Id: %R, Rt: %R, Type: %u", - p->name, htonl(plsr[i].id), htonl(plsr[i].rt), plsr[i].type); + 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)); } void @@ -38,8 +44,8 @@ ospf_lsreq_send(struct ospf_neighbor *n) int i, j; struct proto *p = &n->ifa->oa->po->proto; - pk = (struct ospf_lsreq_packet *) n->ifa->ip_sk->tbuf; - op = (struct ospf_packet *) n->ifa->ip_sk->tbuf; + pk = (struct ospf_lsreq_packet *) n->ifa->sk->tbuf; + op = (struct ospf_packet *) n->ifa->sk->tbuf; ospf_pkt_fill_hdr(n->ifa, pk, LSREQ_P); @@ -53,14 +59,12 @@ ospf_lsreq_send(struct ospf_neighbor *n) i = j = (ospf_pkt_maxsize(n->ifa) - sizeof(struct ospf_lsreq_packet)) / sizeof(struct ospf_lsreq_header); - lsh = (struct ospf_lsreq_header *) (pk + 1); + lsh = pk->lsh; for (; i > 0; i--) { en = (struct top_hash_entry *) sn; - lsh->padd1 = 0; - lsh->padd2 = 0; - lsh->type = en->lsa.type; + 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", @@ -78,25 +82,32 @@ ospf_lsreq_send(struct ospf_neighbor *n) i) * sizeof(struct ospf_lsreq_header); op->length = htons(length); - OSPF_PACKET(ospf_dump_lsreq, (struct ospf_lsreq_packet *) n->ifa->ip_sk->tbuf, + OSPF_PACKET(ospf_dump_lsreq, (struct ospf_lsreq_packet *) n->ifa->sk->tbuf, "LSREQ 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_lsreq_receive(struct ospf_lsreq_packet *ps, - struct ospf_iface *ifa, struct ospf_neighbor *n) +ospf_lsreq_receive(struct ospf_packet *ps_i, 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; - unsigned int size = ntohs(ps->ospf_packet.length); int i, lsano; - struct ospf_area *oa = ifa->oa; - struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; + 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_lsreq_packet *ps = (void *) ps_i; OSPF_PACKET(ospf_dump_lsreq, ps, "LSREQ packet received from %I via %s", n->ip, ifa->iface->name); if (n->state < NEIGHBOR_EXCHANGE) @@ -104,7 +115,7 @@ ospf_lsreq_receive(struct ospf_lsreq_packet *ps, ospf_neigh_sm(n, INM_HELLOREC); - lsh = (void *) (ps + 1); + lsh = ps->lsh; init_list(&uplist); upslab = sl_new(n->pool, sizeof(struct l_lsr_head)); @@ -114,18 +125,18 @@ ospf_lsreq_receive(struct ospf_lsreq_packet *ps, { 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 = lsh->type; + llsh->lsh.type = htype; add_tail(&uplist, NODE llsh); - if (ospf_hash_find(po->gr, oa->areaid, llsh->lsh.id, llsh->lsh.rt, - llsh->lsh.type) == NULL) + if (ospf_hash_find(po->gr, dom, hid, hrt, htype) == NULL) { - log(L_WARN - "Received bad LS req from: %I looking: Type: %u, ID: %R, RT: %R", - n->ip, lsh->type, hid, hrt); + log(L_WARN "Received bad LSREQ from %I: Type: %04x, Id: %R, Rt: %R", + n->ip, htype, hid, hrt); ospf_neigh_sm(n, INM_BADLSREQ); rfree(upslab); return; diff --git a/proto/ospf/lsreq.h b/proto/ospf/lsreq.h index f917f05..a12edde 100644 --- a/proto/ospf/lsreq.h +++ b/proto/ospf/lsreq.h @@ -11,7 +11,7 @@ #define _BIRD_OSPF_LSREQ_H_ void ospf_lsreq_send(struct ospf_neighbor *n); -void ospf_lsreq_receive(struct ospf_lsreq_packet *ps, - struct ospf_iface *ifa, 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 f8195dc..9bed374 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -9,13 +9,21 @@ #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", - p->name, lsa.id, lsa.rt, lsa.type, lsa.age, lsa.sn, lsa.checksum); + 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); } void ospf_dump_common(struct proto *p, struct ospf_packet *op) @@ -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,62 @@ 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); - - OSPF_PACKET(ospf_dump_lsupd, ps, "LSUPD packet received from %I via %s", n->ip, ifa->iface->name); + unsigned int i, max, sendreq = 1; - 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); + offset += lsalen; + + if ((offset > 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 +460,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 +476,35 @@ 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); + /* 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); #ifdef LOCAL_DEBUG if (lsadb) @@ -433,15 +524,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); + int self = (lsatmp.rt == po->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 +544,9 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, } } } +#endif + /* pg 145 (5f) - premature aging of self originated lsa */ if (self) { if ((lsatmp.age == LSA_MAXAGE) && (lsatmp.sn == LSA_MAXSEQNO)) @@ -462,17 +555,18 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, continue; } - OSPF_TRACE(D_EVENTS, "Received old self-originated LSA (Type: %04x, Id: %R, Rt: %R)", lsatmp.type, lsatmp.id, lsatmp.rt); + 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 SN"); + 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(NULL, NULL, &lsadb->lsa, NULL, oa, 1); + ospf_lsupd_flood(po, NULL, NULL, &lsadb->lsa, domain, 1); } else { @@ -483,7 +577,7 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, lsa->sn = htonl(LSA_MAXSEQNO); lsasum_check(lsa, (lsa + 1)); /* It also calculates chsum! */ lsatmp.checksum = ntohs(lsa->checksum); - ospf_lsupd_flood(NULL, lsa, &lsatmp, NULL, oa, 0); + ospf_lsupd_flood(po, NULL, lsa, &lsatmp, domain, 0); } continue; } @@ -496,7 +590,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) @@ -516,7 +610,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) @@ -535,13 +629,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 RFC5340 4.4.3. */ + if ((lsatmp.type == LSA_T_LINK) && + (ifa->state == OSPF_IS_DR)) + schedule_net_lsa(ifa); +#endif + continue; } @@ -552,7 +663,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); @@ -603,16 +714,15 @@ 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; lsa->sn = LSA_MAXSEQNO; 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_TRACE(D_EVENTS, "Type: %04x, Id: %R, Rt: %R", lsa->type, lsa->id, lsa->rt); + ospf_lsupd_flood(po, NULL, NULL, lsa, en->domain, 0); } diff --git a/proto/ospf/lsupd.h b/proto/ospf/lsupd.h index 524a0a2..8bacfe6 100644 --- a/proto/ospf/lsupd.h +++ b/proto/ospf/lsupd.h @@ -13,11 +13,13 @@ 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_lsupd_packet *ps, +void ospf_lsupd_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, struct ospf_neighbor *n); -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); -void ospf_lsupd_flush_nlsa(struct top_hash_entry *en, struct ospf_area *oa); +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 bb681c2..ba8d7b9 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -171,16 +171,23 @@ static struct ospf_neighbor * electbdr(list nl) { struct ospf_neighbor *neigh, *n1, *n2; + u32 nid; n1 = NULL; n2 = NULL; - WALK_LIST(neigh, nl) /* First try those decl. themselves */ + WALK_LIST(neigh, nl) /* First try those decl. themselves */ { +#ifdef OSPFv2 + nid = ipa_to_u32(neigh->ip); +#else /* OSPFv3 */ + nid = neigh->rid; +#endif + if (neigh->state >= NEIGHBOR_2WAY) /* Higher than 2WAY */ - if (neigh->priority > 0) /* Eligible */ - if (ipa_compare(neigh->ip, neigh->dr) != 0) /* And not decl. itself DR */ + if (neigh->priority > 0) /* Eligible */ + if (neigh->dr != nid) /* And not decl. itself DR */ { - if (ipa_compare(neigh->ip, neigh->bdr) == 0) /* Declaring BDR */ + if (neigh->bdr == nid) /* Declaring BDR */ { if (n1 != NULL) { @@ -222,13 +229,20 @@ static struct ospf_neighbor * electdr(list nl) { struct ospf_neighbor *neigh, *n; + u32 nid; n = NULL; - WALK_LIST(neigh, nl) /* And now DR */ + WALK_LIST(neigh, nl) /* And now DR */ { +#ifdef OSPFv2 + nid = ipa_to_u32(neigh->ip); +#else /* OSPFv3 */ + nid = neigh->rid; +#endif + if (neigh->state >= NEIGHBOR_2WAY) /* Higher than 2WAY */ - if (neigh->priority > 0) /* Eligible */ - if (ipa_compare(neigh->ip, neigh->dr) == 0) /* And declaring itself DR */ + if (neigh->priority > 0) /* Eligible */ + if (neigh->dr == nid) /* And declaring itself DR */ { if (n != NULL) { @@ -425,22 +439,28 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event) void bdr_election(struct ospf_iface *ifa) { + struct proto_ospf *po = ifa->oa->po; + struct proto *p = &po->proto; + u32 myid = po->router_id; struct ospf_neighbor *neigh, *ndr, *nbdr, me; - u32 myid; - ip_addr ndrip, nbdrip; int doadj; - struct proto *p = &ifa->oa->po->proto; DBG("(B)DR election.\n"); - myid = p->cf->global->router_id; - me.state = NEIGHBOR_2WAY; me.rid = myid; me.priority = ifa->priority; - me.dr = ifa->drip; - me.bdr = ifa->bdrip; + +#ifdef OSPFv2 me.ip = ifa->iface->addr->ip; + me.dr = ipa_to_u32(ifa->drip); + me.bdr = ipa_to_u32(ifa->bdrip); +#else /* OSPFv3 */ + me.ip = ifa->lladdr; + me.dr = ifa->drid; + me.bdr = ifa->bdrid; + me.iface_id = ifa->iface->index; +#endif add_tail(&ifa->neigh_list, NODE & me); @@ -450,64 +470,43 @@ bdr_election(struct ospf_iface *ifa) if (ndr == NULL) ndr = nbdr; + /* 9.4. (4) */ if (((ifa->drid == myid) && (ndr != &me)) || ((ifa->drid != myid) && (ndr == &me)) || ((ifa->bdrid == myid) && (nbdr != &me)) || ((ifa->bdrid != myid) && (nbdr == &me))) { - if (ndr == NULL) - ifa->drip = me.dr = IPA_NONE; - else - ifa->drip = me.dr = ndr->ip; - - if (nbdr == NULL) - ifa->bdrip = me.bdr = IPA_NONE; - else - ifa->bdrip = me.bdr = nbdr->ip; +#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 nbdr = electbdr(ifa->neigh_list); ndr = electdr(ifa->neigh_list); - } - if (ndr == NULL) - ndrip = IPA_NONE; - else - ndrip = ndr->ip; - - if (nbdr == NULL) - nbdrip = IPA_NONE; - else - nbdrip = nbdr->ip; - - doadj = 0; - if ((ipa_compare(ifa->drip, ndrip) != 0) - || (ipa_compare(ifa->bdrip, nbdrip) != 0)) - doadj = 1; - - if (ndr == NULL) - { - ifa->drid = 0; - ifa->drip = IPA_NONE; - } - else - { - ifa->drid = ndr->rid; - ifa->drip = ndr->ip; + if (ndr == NULL) + ndr = nbdr; } - if (nbdr == NULL) - { - ifa->bdrid = 0; - ifa->bdrip = IPA_NONE; - } - else - { - ifa->bdrid = nbdr->rid; - ifa->bdrip = nbdr->ip; - } + u32 odrid = ifa->drid; + u32 obdrid = ifa->bdrid; + + ifa->drid = ndr ? ndr->rid : 0; + ifa->drip = ndr ? ndr->ip : IPA_NONE; + 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) ospf_iface_chstate(ifa, OSPF_IS_DR); else diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index d4bcbed..920f84f 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -77,6 +77,7 @@ #include "ospf.h" +static int ospf_reload_routes(struct proto *p); static void ospf_rt_notify(struct proto *p, net * n, rte * new, rte * old UNUSED, ea_list * attrs); static void ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a); static int ospf_rte_better(struct rte *new, struct rte *old); @@ -117,9 +118,9 @@ ospf_start(struct proto *p) struct ospf_area_config *ac; struct ospf_area *oa; + po->router_id = proto_get_router_id(p->cf); po->rfc1583 = c->rfc1583; po->ebit = 0; - po->tick = c->tick; po->disp_timer = tm_new(p->pool); po->disp_timer->data = po; @@ -163,8 +164,11 @@ ospf_start(struct proto *p) oa->stub = 0; } - oa->opt.byte = 0; - if(!oa->stub) oa->opt.bit.e = 1; +#ifdef OSPFv2 + oa->options = (oa->stub ? 0 : OPT_E); +#else /* OSPFv3 */ + oa->options = OPT_R | (oa->stub ? 0 : OPT_E) | OPT_V6; +#endif } /* Add all virtual links as interfaces */ @@ -186,8 +190,11 @@ ospf_start(struct proto *p) fib_init(&oa->net_fib, p->pool, sizeof(struct area_net), 16, ospf_area_initfib); fib_init(&oa->rtr, p->pool, sizeof(ort), 16, ospf_rt_initort); po->backbone = oa; - oa->opt.byte = 0; - oa->opt.bit.e = 1; +#ifdef OSPFv2 + oa->options = OPT_E; +#else /* OSPFv3 */ + oa->options = OPT_R | OPT_E | OPT_V6; +#endif } ospf_iface_new(po, NULL, ac, ipatt); } @@ -228,9 +235,10 @@ ospf_init(struct proto_config *c) { struct proto *p = proto_new(c, sizeof(struct proto_ospf)); - p->import_control = ospf_import_control; p->make_tmp_attrs = ospf_make_tmp_attrs; p->store_tmp_attrs = ospf_store_tmp_attrs; + p->import_control = ospf_import_control; + p->reload_routes = ospf_reload_routes; p->accept_ra_types = RA_OPTIMAL; p->rt_notify = ospf_rt_notify; p->if_notify = ospf_iface_notify; @@ -301,15 +309,29 @@ ospf_build_attrs(ea_list * next, struct linpool *pool, u32 m1, u32 m2, 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->iface->name); 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->iface->name); + ifa->origlink = 1; +} +#endif + void schedule_rt_lsa(struct ospf_area *oa) { struct proto *p = &oa->po->proto; - OSPF_TRACE(D_EVENTS, "Scheduling RT lsa origination for area %R.", oa->areaid); + OSPF_TRACE(D_EVENTS, "Scheduling router-LSA origination for area %R", oa->areaid); oa->origrt = 1; } @@ -321,17 +343,30 @@ schedule_rtcalc(struct proto_ospf *po) if (po->calcrt) return; - OSPF_TRACE(D_EVENTS, "Scheduling RT calculation."); + OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation"); po->calcrt = 1; } +static int +ospf_reload_routes(struct proto *p) +{ + struct proto_ospf *po = (struct proto_ospf *) p; + + if (po->calcrt != 2) + OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation with route reload"); + + po->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 LSA and network LSAs. + * function for origination of router, network LSAs. */ void area_disp(struct ospf_area *oa) @@ -341,13 +376,19 @@ area_disp(struct ospf_area *oa) /* Now try to originage rt_lsa */ if (oa->origrt) - originate_rt_lsa(oa); + 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)) - originate_net_lsa(ifa); + update_net_lsa(ifa); } } @@ -370,7 +411,7 @@ ospf_disp(timer * timer) /* Calculate routing table */ if (po->calcrt) - ospf_rt_spf (po); + ospf_rt_spf(po); } @@ -448,36 +489,9 @@ ospf_rt_notify(struct proto *p, net * n, rte * new, rte * old UNUSED, */ if (new) /* Got some new route */ - { originate_ext_lsa(n, new, po, attrs); - } else - { - u32 rtid = po->proto.cf->global->router_id; - struct ospf_area *oa; - struct top_hash_entry *en; - u32 pr = ipa_to_u32(n->n.prefix); - struct ospf_lsa_ext *ext; - int i; - int max = max_ext_lsa(n->n.pxlen); - - /* Flush old external LSA */ - for (i = 0; i < max; i++, pr++) - { - if (en = ospf_hash_find(po->gr, 0, pr, rtid, LSA_T_EXT)) - { - ext = en->lsa_body; - if (ipa_compare(ext->netmask, ipa_mkmask(n->n.pxlen)) == 0) - { - WALK_LIST(oa, po->area_list) - { - ospf_lsupd_flush_nlsa(en, oa); - } - } - break; - } - } - } + flush_ext_lsa(n, po); } static void @@ -494,6 +508,8 @@ ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) if (ifa->iface == a->iface) { schedule_rt_lsa(ifa->oa); + /* Event 5 from RFC5340 4.4.3. */ + schedule_link_lsa(ifa); return; } } @@ -605,7 +621,9 @@ ospf_reconfigure(struct proto *p, struct proto_config *c) struct area_net_config *anc; struct area_net *an; - po->rfc1583 = new->rfc1583; + if (po->rfc1583 != new->rfc1583) + return 0; + schedule_rtcalc(po); po->tick = new->tick; @@ -762,6 +780,7 @@ ospf_reconfigure(struct proto *p, struct proto_config *c) "Interface %s is no longer stub.", ifa->iface->name); } +#ifdef OSPFv2 /* AUTHENTICATION */ if (oldip->autype != newip->autype) { @@ -772,6 +791,7 @@ ospf_reconfigure(struct proto *p, struct proto_config *c) } /* Add *passwords */ ifa->passwords = newip->passwords; +#endif /* priority */ if (oldip->priority != newip->priority) @@ -1032,85 +1052,152 @@ ospf_sh_iface(struct proto *p, char *iff) cli_msg(0, ""); } -/* First we want to separate network-LSAs and other LSAs (because network-LSAs +/* lsa_compare_for_state() - Compare function for 'show ospf state' + * + * First we want to separate network-LSAs and other LSAs (because network-LSAs * will be presented as network nodes and other LSAs together as router nodes) * Network-LSAs are sorted according to network prefix, other LSAs are sorted * according to originating router id (to get all LSA needed to represent one * router node together). Then, according to LSA type, ID and age. + * + * For OSPFv3, we have to handle also Prefix-LSAs. We would like to put each + * immediately after the referenced LSA. We will make faked LSA based on ref_ + * 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->id = px->ref_id; + dst->rt = px->ref_rt; + dst->sn = src->sn; + + return dst; +} + +#endif + static int -he_compare(const void *p1, const void *p2) +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 ospf_lsa_header *lsa1 = &(he1->lsa); struct ospf_lsa_header *lsa2 = &(he2->lsa); + + if (he1->domain != he2->domain) + return he1->domain - he2->domain; + +#ifdef OSPFv3 + struct ospf_lsa_header lsatmp1, lsatmp2; + + 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); + + if (px2) + lsa2 = fake_lsa_from_prefix_lsa(&lsatmp2, lsa2, he2->lsa_body); +#endif + int nt1 = (lsa1->type == LSA_T_NET); int nt2 = (lsa2->type == LSA_T_NET); - if (he1->oa->areaid != he2->oa->areaid) - return he1->oa->areaid - he2->oa->areaid; - if (nt1 != nt2) return nt1 - nt2; if (nt1) { - // we are cheating for now +#ifdef OSPFv3 + /* In OSPFv3, neworks are named base on ID of DR */ + if (lsa1->rt != lsa2->rt) + return lsa1->rt - lsa2->rt; +#endif + + /* For OSPFv2, this is IP of the network, + for OSPFv3, this is interface ID */ if (lsa1->id != lsa2->id) return lsa1->id - lsa2->id; - - return lsa1->age - lsa2->age; + +#ifdef OSPFv3 + if (px1 != px2) + return px1 - px2; +#endif + + return lsa1->sn - lsa2->sn; } else - { - if (lsa1->rt != lsa2->rt) - return lsa1->rt - lsa2->rt; - - if (lsa1->type != lsa2->type) - return lsa1->type - lsa2->type; + { + if (lsa1->rt != lsa2->rt) + return lsa1->rt - lsa2->rt; + + if (lsa1->type != lsa2->type) + return lsa1->type - lsa2->type; - if (lsa1->id != lsa2->id) - return lsa1->id - lsa2->id; + if (lsa1->id != lsa2->id) + return lsa1->id - lsa2->id; + +#ifdef OSPFv3 + if (px1 != px2) + return px1 - px2; +#endif - return lsa1->age - lsa2->age; - } + return lsa1->sn - lsa2->sn; + } } static inline void -show_lsa_router(struct top_hash_entry *he) +show_lsa_router(struct proto_ospf *po, struct top_hash_entry *he) { 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); - u32 i; + int max = lsa_rt_count(lsa); + int i; + + 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); - for (i = 0; i < rt->links; i++) + 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); + cli_msg(-1016, "\t\trouter %R metric %u", rr[i].id, rr[i].metric); - for (i = 0; i < rt->links; i++) + for (i = 0; i < max; i++) if (rr[i].type == LSART_NET) { - struct proto_ospf *po = he->oa->po; - struct top_hash_entry *net_he = ospf_hash_find(po->gr, he->oa->areaid, rr[i].id, rr[i].id, LSA_T_NET); +#ifdef OSPFv2 + struct top_hash_entry *net_he = ospf_hash_find_net(po->gr, he->domain, rr[i].id); + if (net_he) { struct ospf_lsa_header *net_lsa = &(net_he->lsa); struct ospf_lsa_net *net_ln = net_he->lsa_body; - cli_msg(-1016, "\t\tnetwork %I/%d metric %u ", ipa_and(ipa_from_u32(net_lsa->id), net_ln->netmask), ipa_mklen(net_ln->netmask), rr[i].metric); + + 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); } else - cli_msg(-1016, "\t\tnetwork ??? metric %u ", rr[i].metric); + 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 } - for (i = 0; i < rt->links; i++) +#ifdef OSPFv2 + 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); - - for (i = 0; i < rt->links; i++) - if (rr[i].type == LSART_VLNK) - cli_msg(-1016, "\t\tvlink %I metric %u ", ipa_from_u32(rr[i].id), rr[i].metric); + 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 } static inline void @@ -1118,31 +1205,58 @@ show_lsa_network(struct top_hash_entry *he) { struct ospf_lsa_header *lsa = &(he->lsa); struct ospf_lsa_net *ln = he->lsa_body; - u32 *rts = (u32 *) (ln + 1); - u32 max = (lsa->length - sizeof(struct ospf_lsa_header) - sizeof(struct ospf_lsa_net)) / sizeof(u32); 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 - for (i = 0; i < max; i++) - cli_msg(-1016, "\t\trouter %R", rts[i]); + for (i = 0; i < lsa_net_count(lsa); i++) + cli_msg(-1016, "\t\trouter %R", ln->routers[i]); } static inline void show_lsa_sum_net(struct top_hash_entry *he) { struct ospf_lsa_header *lsa = &(he->lsa); - struct ospf_lsa_sum *sm = he->lsa_body; - - cli_msg(-1016, "\t\txnetwork %I/%d", ipa_and(ipa_from_u32(lsa->id), sm->netmask), ipa_mklen(sm->netmask)); + 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 + + cli_msg(-1016, "\t\txnetwork %I/%d", ip, pxlen); } static inline void show_lsa_sum_rt(struct top_hash_entry *he) { - cli_msg(-1016, "\t\txrouter %R", he->lsa.id); + u32 dst_rid, 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", dst_rid); } @@ -1152,32 +1266,95 @@ show_lsa_external(struct top_hash_entry *he) struct ospf_lsa_header *lsa = &(he->lsa); struct ospf_lsa_ext *ext = he->lsa_body; struct ospf_lsa_ext_tos *et = (struct ospf_lsa_ext_tos *) (ext + 1); - 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; + + rt_metric = ext->metric & METRIC_MASK; + ebit = ext->metric & LSA_EXT_EBIT; +#ifdef OSPFv2 + ip = ipa_and(ipa_from_u32(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 (ipa_nonzero(et->fwaddr)) - bsprintf(str_via, " via %I", et->fwaddr); + if (rt_fwaddr_valid) + bsprintf(str_via, " via %I", rt_fwaddr); - if (et->tag) - bsprintf(str_tag, " tag %08x", et->tag); + if (rt_tag) + bsprintf(str_tag, " tag %08x", rt_tag); - cli_msg(-1016, "\t\texternal %I/%d metric%s %u%s%s", - ipa_and(ipa_from_u32(lsa->id), ext->netmask), - ipa_mklen(ext->netmask), et->etm.etos.ebit ? "2" : "", - et->etm.metric & METRIC_MASK, str_via, str_tag); + cli_msg(-1016, "\t\texternal %I/%d metric%s %u%s%s", ip, pxlen, + 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 *olsa) +{ + struct ospf_lsa_header *lsa = &(he->lsa); + struct ospf_lsa_prefix *px = he->lsa_body; + struct ospf_lsa_ext *ext = he->lsa_body; + char *msg; + ip_addr pxa; + int pxlen; + u8 pxopts; + u16 metric; + u32 *buf; + int i; + + /* We check whether given prefix-LSA is related to the last non-prefix-LSA */ + if ((olsa == NULL) || (olsa->type != px->ref_type) || (olsa->rt != px->ref_rt) || + !(((px->ref_type == LSA_T_RT) && (px->ref_id == 0)) || + ((px->ref_type == LSA_T_NET) && (px->ref_id == olsa->id)))) + return; + + buf = px->rest; + for (i = 0; i < px->pxcount; i++) + { + buf = lsa_get_ipv6_prefix(buf, &pxa, &pxlen, &pxopts, &metric); + + if (px->ref_type == LSA_T_RT) + cli_msg(-1016, "\t\tstubnet %I/%d metric %u", pxa, pxlen, metric); + else + cli_msg(-1016, "\t\taddress %I/%d", pxa, pxlen); + } +} +#endif void ospf_sh_state(struct proto *p, int verbose) { struct proto_ospf *po = (struct proto_ospf *) p; struct top_graph *f = po->gr; - unsigned int i, j; + unsigned int i, j1, j2; u32 last_rt = 0xFFFFFFFF; u32 last_area = 0xFFFFFFFF; +#ifdef OSPFv3 + struct ospf_lsa_header *olsa = NULL; +#endif + if (p->proto_state != PS_UP) { cli_msg(-1016, "%s: is not up", p->name); @@ -1188,30 +1365,57 @@ ospf_sh_state(struct proto *p, int verbose) struct top_hash_entry *hea[f->hash_entries]; struct top_hash_entry *he; - j = 0; - for (i = 0; i < f->hash_size; i++) - for (he = f->hash_table[i]; he != NULL; he = he->next) - hea[j++] = he; + j1 = j2 = 0; + WALK_SLIST(he, po->lsal) + { + int accept; + + switch (he->lsa.type) + { + case LSA_T_RT: + case LSA_T_NET: + accept = 1; + break; + + case LSA_T_SUM_NET: + case LSA_T_SUM_RT: + case LSA_T_EXT: +#ifdef OSPFv3 + case LSA_T_PREFIX: +#endif + accept = verbose; + break; + + default: + accept = 0; + } - if (j == f->hash_size) + if (accept) + hea[j1++] = he; + else + j2++; + } + + if ((j1 + j2) != f->hash_entries) die("Fatal mismatch"); - qsort(hea, j, sizeof(struct top_hash_entry *), he_compare); + qsort(hea, j1, sizeof(struct top_hash_entry *), lsa_compare_for_state); - for (i = 0; i < j; i++) + for (i = 0; i < j1; i++) { - if ((verbose == 0) && (hea[i]->lsa.type > LSA_T_NET)) - continue; - - if (last_area != hea[i]->oa->areaid) + if (last_area != hea[i]->domain) { cli_msg(-1016, ""); - cli_msg(-1016, "area %R", hea[i]->oa->areaid); - last_area = hea[i]->oa->areaid; + cli_msg(-1016, "area %R", hea[i]->domain); + last_area = hea[i]->domain; last_rt = 0xFFFFFFFF; } - if ((hea[i]->lsa.rt != last_rt) && (hea[i]->lsa.type != LSA_T_NET)) + if ((hea[i]->lsa.rt != last_rt) && (hea[i]->lsa.type != LSA_T_NET) +#ifdef OSPFv3 + && (hea[i]->lsa.type != LSA_T_PREFIX) +#endif + ) { cli_msg(-1016, ""); cli_msg(-1016, (hea[i]->lsa.type != LSA_T_EXT) ? "\trouter %R" : "\txrouter %R", hea[i]->lsa.rt); @@ -1221,7 +1425,7 @@ ospf_sh_state(struct proto *p, int verbose) switch (hea[i]->lsa.type) { case LSA_T_RT: - show_lsa_router(hea[i]); + show_lsa_router(po, hea[i]); break; case LSA_T_NET: @@ -1235,15 +1439,123 @@ ospf_sh_state(struct proto *p, int verbose) case LSA_T_SUM_RT: show_lsa_sum_rt(hea[i]); break; + case LSA_T_EXT: show_lsa_external(hea[i]); break; + +#ifdef OSPFv3 + case LSA_T_PREFIX: + show_lsa_prefix(hea[i], olsa); + break; +#endif } + +#ifdef OSPFv3 + if (hea[i]->lsa.type != LSA_T_PREFIX) + olsa = &(hea[i]->lsa); +#endif } + cli_msg(0, ""); +} + + +static int +lsa_compare_for_lsadb(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 ospf_lsa_header *lsa1 = &(he1->lsa); + struct ospf_lsa_header *lsa2 = &(he2->lsa); + int sc1 = LSA_SCOPE(lsa1); + int sc2 = LSA_SCOPE(lsa2); + + if (sc1 != sc2) + return sc2 - sc1; + + if (he1->domain != he2->domain) + return he1->domain - he2->domain; + 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; + + return lsa1->sn - lsa2->sn; +} + +void +ospf_sh_lsadb(struct proto *p) +{ + struct proto_ospf *po = (struct proto_ospf *) p; + struct top_graph *f = po->gr; + unsigned int i, j; + int last_dscope = -1; + u32 last_domain = 0; + + if (p->proto_state != PS_UP) + { + cli_msg(-1017, "%s: is not up", p->name); + cli_msg(0, ""); + return; + } + + struct top_hash_entry *hea[f->hash_entries]; + struct top_hash_entry *he; + + j = 0; + WALK_SLIST(he, po->lsal) + hea[j++] = he; + + if (j != f->hash_entries) + die("Fatal mismatch"); + + 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); + + if ((dscope != last_dscope) || (hea[i]->domain != last_domain)) + { + struct iface *ifa; + + 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: + ifa = if_find_by_index(hea[i]->domain); + cli_msg(-1017, "Link %s", (ifa != NULL) ? ifa->name : "?"); + break; +#endif + } + cli_msg(-1017, ""); + cli_msg(-1017," Type LS ID Router Age Sequence Checksum"); + + last_dscope = dscope; + last_domain = hea[i]->domain; + } + + + cli_msg(-1017," %04x %-15R %-15R %5u %08x %04x", + lsa->type, lsa->id, lsa->rt, lsa->age, lsa->sn, lsa->checksum); + } cli_msg(0, ""); } + struct protocol proto_ospf = { name:"OSPF", template:"ospf%d", diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 7f0ee54..0b25ded 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -18,6 +18,7 @@ * normally allocate 2*mtu - (I found one cisco * sending packets mtu+16) */ +#define LOCAL_DEBUG 1 #ifdef LOCAL_DEBUG #define OSPF_FORCE_DEBUG 1 #else @@ -49,13 +50,17 @@ do { if ((p->debug & D_PACKETS) || OSPF_FORCE_DEBUG) \ #include "lib/string.h" #define OSPF_PROTO 89 + #ifndef IPV6 +#define OSPFv2 1 #define OSPF_VERSION 2 #define AllSPFRouters ipa_from_u32(0xe0000005) /* 224.0.0.5 */ #define AllDRouters ipa_from_u32(0xe0000006) /* 224.0.0.6 */ -#define DEFAULTDES ipa_from_u32(0) #else -#error OSPF for IPv6 is not implemented (mail to Feela <feela@network.cz>) +#define OSPFv3 1 +#define OSPF_VERSION 3 +#define AllSPFRouters _MI(0xFF020000, 0, 0, 5) /* FF02::5 */ +#define AllDRouters _MI(0xFF020000, 0, 0, 6) /* FF02::6 */ #endif @@ -118,32 +123,37 @@ struct ospf_area_config list stubnet_list; }; -struct obits -{ -#ifdef CPU_BIG_ENDIAN - u8 unused2:2; - u8 dc:1; - u8 ea:1; - u8 np:1; - u8 mc:1; - u8 e:1; - u8 unused1:1; -#else - u8 unused1:1; - u8 e:1; - u8 mc:1; - u8 np:1; - u8 ea:1; - u8 dc:1; - u8 unused2:2; + +/* Option flags */ + +#define OPT_E 0x02 +#define OPT_N 0x08 +#define OPT_DC 0x20 + +#ifdef OSPFv2 +#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) #endif -}; -union options -{ - u8 byte; - struct obits bit; -}; +#ifdef OSPFv3 +#define OPT_V6 0x01 +#define OPT_R 0x10 + +/* 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) + +#define OPT_PX_NU 0x01 +#define OPT_PX_LA 0x02 +#define OPT_PX_P 0x08 +#define OPT_PX_DN 0x10 +#endif struct ospf_iface @@ -152,9 +162,7 @@ struct ospf_iface struct iface *iface; /* Nest's iface */ struct ospf_area *oa; struct object_lock *lock; - sock *hello_sk; /* Hello socket */ - sock *dr_sk; /* For states DR or BACKUP */ - sock *ip_sk; /* IP socket (for DD ...) */ + sock *sk; /* IP socket (for DD ...) */ list neigh_list; /* List of neigbours */ u32 cost; /* Cost of iface */ u32 waitint; /* number of sec before changing state from wait */ @@ -167,15 +175,27 @@ struct ospf_iface u16 inftransdelay; /* The estimated number of seconds it takes to transmit a Link State Update Packet over this interface. LSAs contained in the update */ - u16 autype; 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 drip; /* Designated router */ u32 drid; ip_addr bdrip; /* Backup DR */ u32 bdrid; + +#ifdef OSPFv3 + 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 */ + ip_addr lladdr; /* Used link-local addr */ +#endif + u8 type; /* OSPF view of type */ #define OSPF_IT_BCAST 0 #define OSPF_IT_NBMA 1 @@ -206,15 +226,21 @@ struct ospf_iface #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 *nlsa; /* Originated net lsa */ - int orignet; /* Schedule network LSA origination */ - int fadj; /* Number of full adjacent neigh */ +#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 *pxn_lsa; /* Originated prefix LSA */ +#endif + int fadj; /* Number of full adjacent neigh */ list nbma_list; - u8 priority; /* A router priority for DR election */ + u8 priority; /* A router priority for DR election */ u8 ioprob; + u8 dr_up; /* Socket is a member of DRouters group */ u32 rxbuf; }; @@ -232,35 +258,17 @@ union ospf_auth struct ospf_md5 md5; }; -struct ospf_packet -{ - u8 version; - u8 type; + +/* 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 */ - u16 length; - u32 routerid; - u32 areaid; + +/* Area IDs */ #define BACKBONE 0 - u16 checksum; - u16 autype; - union ospf_auth u; -}; -struct ospf_hello_packet -{ - struct ospf_packet ospf_packet; - ip_addr netmask; - u16 helloint; - u8 options; - u8 priority; - u32 deadint; - ip_addr dr; - ip_addr bdr; -}; struct immsb { @@ -282,164 +290,326 @@ union imms u8 byte; struct immsb bit; }; - -struct ospf_dbdes_packet -{ - struct ospf_packet ospf_packet; - u16 iface_mtu; - u8 options; - union imms imms; /* I, M, MS bits */ #define DBDES_MS 1 #define DBDES_M 2 #define DBDES_I 4 - u32 ddseq; + + +#ifdef OSPFv2 + +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 */ + +struct ospf_packet +{ + u8 version; + u8 type; + u16 length; + u32 routerid; + u32 areaid; + u16 checksum; + u8 instance_id; + u8 zero; +}; + + +#endif + + + + struct ospf_lsa_header { u16 age; /* LS Age */ #define LSA_MAXAGE 3600 /* 1 hour */ #define LSA_CHECKAGE 300 /* 5 minutes */ #define LSA_MAXAGEDIFF 900 /* 15 minutes */ + +#ifdef OSPFv2 u8 options; u8 type; + +#define LSA_T_RT 1 +#define LSA_T_NET 2 +#define LSA_T_SUM_NET 3 +#define LSA_T_SUM_RT 4 +#define LSA_T_EXT 5 + +#define LSA_SCOPE_AREA 0x2000 +#define LSA_SCOPE_AS 0x4000 + +#define LSA_SCOPE(lsa) (((lsa)->type == LSA_T_EXT) ? LSA_SCOPE_AS : LSA_SCOPE_AREA) + +#else /* OSPFv3 */ + u16 type; + +#define LSA_T_RT 0x2001 +#define LSA_T_NET 0x2002 +#define LSA_T_SUM_NET 0x2003 +#define LSA_T_SUM_RT 0x2004 +#define LSA_T_EXT 0x4005 +#define LSA_T_LINK 0x0008 +#define LSA_T_PREFIX 0x2009 + +#define LSA_UBIT 0x8000 + +#define LSA_SCOPE_LINK 0x0000 +#define LSA_SCOPE_AREA 0x2000 +#define LSA_SCOPE_AS 0x4000 +#define LSA_SCOPE_RES 0x6000 +#define LSA_SCOPE_MASK 0x6000 + +#define LSA_SCOPE(lsa) ((lsa)->type & LSA_SCOPE_MASK) +#endif + u32 id; -#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 u32 rt; /* Advertising router */ s32 sn; /* LS Sequence number */ -#define LSA_INITSEQNO 0x80000001 -#define LSA_MAXSEQNO 0x7fffffff +#define LSA_INITSEQNO ((s32) 0x80000001) +#define LSA_MAXSEQNO ((s32) 0x7fffffff) u16 checksum; u16 length; }; -struct vebb -{ -#ifdef CPU_BIG_ENDIAN - u8 padding:5; - u8 v:1; - u8 e:1; - u8 b:1; -#else - u8 b:1; - u8 e:1; - u8 v:1; - u8 padding:5; -#endif -}; -union veb -{ - u8 byte; - struct vebb bit; -}; +#define LSART_PTP 1 +#define LSART_NET 2 +#define LSART_STUB 3 +#define LSART_VLNK 4 + + +#ifdef OSPFv2 struct ospf_lsa_rt { - union veb veb; - u8 padding; +#ifdef CPU_BIG_ENDIAN + u16 options; /* VEB flags only */ + u16 links; +#else u16 links; + u16 options; /* VEB flags only */ +#endif }; struct ospf_lsa_rt_link { u32 id; u32 data; +#ifdef CPU_BIG_ENDIAN u8 type; -#define LSART_PTP 1 -#define LSART_NET 2 -#define LSART_STUB 3 -#define LSART_VLNK 4 - u8 notos; - u16 metric; -}; - -struct ospf_lsa_rt_link_tos -{ /* Actually we ignore TOS. This is useless */ - u8 tos; u8 padding; u16 metric; +#else + u16 metric; + u8 padding; + 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; }; -struct ospf_lsa_ext_etos +#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 { -#ifdef CPU_BIG_ENDIAN - u8 ebit:1; - u8 tos:7; - u8 padding1; - u16 padding2; -#else - u16 padding2; - u8 padding1; - u8 tos:7; - u8 ebit:1; -#endif + u32 options; }; -#define METRIC_MASK 0x00FFFFFF -struct ospf_lsa_sum_tos +struct ospf_lsa_rt_link { #ifdef CPU_BIG_ENDIAN - u8 tos; - u8 padding1; - u16 padding2; + u8 type; + u8 padding; + u16 metric; #else - u16 padding2; - u8 padding1; - u8 tos; + u16 metric; + u8 padding; + u8 type; #endif + u32 lif; /* Local interface ID */ + u32 nif; /* Neighbor interface ID */ + u32 id; /* Neighbor router ID */ }; -union ospf_lsa_sum_tm +struct ospf_lsa_net +{ + u32 options; + u32 routers[]; +}; + +struct ospf_lsa_sum_net { - struct ospf_lsa_sum_tos tos; u32 metric; + u32 prefix[]; }; -union ospf_lsa_ext_etm +struct ospf_lsa_sum_rt { - struct ospf_lsa_ext_etos etos; + u32 options; u32 metric; + u32 drid; }; -struct ospf_lsa_ext_tos +struct ospf_lsa_ext { - union ospf_lsa_ext_etm etm; - ip_addr fwaddr; - u32 tag; + u32 metric; + u32 rest[]; +}; + +struct ospf_lsa_link +{ + u32 options; + ip_addr lladdr; + u32 pxcount; + u32 rest[]; }; -struct ospf_lsreq_packet +struct ospf_lsa_prefix { - struct ospf_packet ospf_packet; +#ifdef CPU_BIG_ENDIAN + u16 pxcount; + u16 ref_type; +#else + u16 ref_type; + u16 pxcount; +#endif + u32 ref_id; + u32 ref_rt; + u32 rest[]; }; +#define LSA_EXT_EBIT 0x4000000 +#define LSA_EXT_FBIT 0x2000000 +#define LSA_EXT_TBIT 0x1000000 + +/* Endianity swap for lsa->type */ +#define ntoht(x) ntohs(x) +#define htont(x) htons(x) + +#endif + +#define METRIC_MASK 0x00FFFFFF +#define OPTIONS_MASK 0x00FFFFFF + +static inline unsigned +lsa_rt_count(struct ospf_lsa_header *lsa) +{ + return (lsa->length - sizeof(struct ospf_lsa_header) - sizeof(struct ospf_lsa_rt)) + / sizeof(struct ospf_lsa_rt_link); +} + +static inline unsigned +lsa_net_count(struct ospf_lsa_header *lsa) +{ + return (lsa->length - sizeof(struct ospf_lsa_header) - sizeof(struct ospf_lsa_net)) + / sizeof(u32); +} + + +#ifdef OSPFv3 + +#define IPV6_PREFIX_SPACE(x) ((((x) + 63) / 32) * 4) +#define IPV6_PREFIX_WORDS(x) (((x) + 63) / 32) + +static inline u32 * +lsa_get_ipv6_prefix(u32 *buf, ip_addr *addr, int *pxlen, u8 *pxopts, u16 *rest) +{ + u8 pxl = (*buf >> 24); + *pxopts = (*buf >> 16); + *rest = *buf; + *pxlen = pxl; + buf++; + + *addr = IPA_NONE; + + if (pxl > 0) + _I0(*addr) = *buf++; + if (pxl > 32) + _I1(*addr) = *buf++; + if (pxl > 64) + _I2(*addr) = *buf++; + if (pxl > 96) + _I3(*addr) = *buf++; + + return buf; +} + +static inline u32 * +lsa_get_ipv6_addr(u32 *buf, ip_addr *addr) +{ + *addr = *(ip_addr *) buf; + return buf + 4; +} + +static inline u32 * +put_ipv6_prefix(u32 *buf, ip_addr addr, u8 pxlen, u8 pxopts, u16 lh) +{ + *buf++ = ((pxlen << 24) | (pxopts << 16) | lh); + + if (pxlen > 0) + *buf++ = _I0(addr); + if (pxlen > 32) + *buf++ = _I1(addr); + if (pxlen > 64) + *buf++ = _I2(addr); + if (pxlen > 96) + *buf++ = _I3(addr); + return buf; +} + +static inline u32 * +put_ipv6_addr(u32 *buf, ip_addr addr) +{ + *(ip_addr *) buf = addr; + return buf + 4; +} + +#endif + + + struct ospf_lsreq_header { - u16 padd1; - u8 padd2; - u8 type; + u32 type; u32 id; u32 rt; /* Advertising router */ }; @@ -450,17 +620,6 @@ struct l_lsr_head struct ospf_lsreq_header lsh; }; -struct ospf_lsupd_packet -{ - struct ospf_packet ospf_packet; - u32 lsano; /* Number of LSA's */ -}; - -struct ospf_lsack_packet -{ - struct ospf_packet ospf_packet; -}; - struct ospf_neighbor { @@ -484,10 +643,18 @@ struct ospf_neighbor u32 rid; /* Router ID */ ip_addr ip; /* IP of it's interface */ u8 priority; /* Priority */ - u8 options; /* Options received */ - ip_addr dr; /* Neigbour's idea of DR */ - ip_addr bdr; /* Neigbour's idea of BDR */ 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 + siterator dbsi; /* Database summary list iterator */ slist lsrql; /* Link state request */ struct top_graph *lsrqh; /* LSA graph */ @@ -535,13 +702,14 @@ struct ospf_area struct ospf_area_config *ac; /* Related area config */ int origrt; /* Rt lsa origination scheduled? */ struct top_hash_entry *rt; /* My own router LSA */ + struct top_hash_entry *pxr_lsa; /* Originated prefix LSA */ list cand; /* List of candidates for RT calc. */ struct fib net_fib; /* Networks to advertise or not */ int stub; int trcap; /* Transit capability? */ + u32 options; /* Optional features */ struct proto_ospf *po; struct fib rtr; /* Routing tables for routers */ - union options opt; /* RFC2328 - A.2 */ }; struct proto_ospf @@ -551,7 +719,8 @@ struct proto_ospf unsigned tick; struct top_graph *gr; /* LSA graph */ slist lsal; /* List of all LSA's */ - int calcrt; /* Routing table calculation scheduled? */ + int calcrt; /* Routing table calculation scheduled? + 0=no, 1=normal, 2=forced reload */ int cleanup; /* Should I cleanup after RT calculation? */ list iface_list; /* Interfaces we really use */ list area_list; @@ -562,6 +731,7 @@ struct proto_ospf struct ospf_area *backbone; /* If exists */ void *lsab; /* LSA buffer used when originating router LSAs */ int lsab_size, lsab_used; + u32 router_id; }; struct ospf_iface_patt @@ -577,20 +747,28 @@ struct ospf_iface_patt u32 deadc; u32 dead; u32 type; - u32 autype; u32 strictnbma; u32 stub; u32 vid; -#define OSPF_AUTH_NONE 0 -#define OSPF_AUTH_SIMPLE 1 -#define OSPF_AUTH_CRYPT 2 -#define OSPF_AUTH_CRYPT_SIZE 16 u32 rxbuf; #define OSPF_RXBUF_NORMAL 0 #define OSPF_RXBUF_LARGE 1 #define OSPF_RXBUF_MINSIZE 256 /* Minimal allowed size */ - list *passwords; list nbma_list; + + u32 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 + +#ifdef OSPFv2 + list *passwords; +#endif + +#ifdef OSPFv3 + u8 instance_id; +#endif }; int ospf_import_control(struct proto *p, rte **new, ea_list **attrs, @@ -600,6 +778,13 @@ 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); + +#ifdef OSPFv3 +void schedule_link_lsa(struct ospf_iface *ifa); +#else +static inline void schedule_link_lsa(struct ospf_iface *ifa) {} +#endif + void ospf_sh_neigh(struct proto *p, char *iff); void ospf_sh(struct proto *p); void ospf_sh_iface(struct proto *p, char *iff); diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index 783d28e..1a02c00 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -13,8 +13,9 @@ void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) { + struct proto_ospf *po = ifa->oa->po; + struct proto *p = &po->proto; struct ospf_packet *pkt; - struct proto *p = (struct proto *) (ifa->oa->po); pkt = (struct ospf_packet *) buf; @@ -22,23 +23,39 @@ ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) pkt->type = h_type; - pkt->routerid = htonl(p->cf->global->router_id); + pkt->routerid = htonl(po->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; } 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; - /* Can be mtu < 576? */ + unsigned add = 0; + +#ifdef OSPFv2 + add = ((ifa->autype == OSPF_AUTH_CRYPT) ? OSPF_AUTH_CRYPT_SIZE : 0); +#endif + return ((mtu <= ifa->iface->mtu) ? mtu : ifa->iface->mtu) - - SIZE_OF_IP_HEADER - ((ifa->autype == OSPF_AUTH_CRYPT) ? OSPF_AUTH_CRYPT_SIZE : 0); - /* For virtual links use mtu=576 */ + SIZE_OF_IP_HEADER - add; } -void + +#ifdef OSPFv2 + +static void ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) { struct password_item *passwd = NULL; @@ -224,6 +241,20 @@ ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_ } } +#else + +/* OSPFv3 authentication not yet supported */ + +static inline void +ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) +{ } + +static int +ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, int size) +{ return 1; } + +#endif + /** * ospf_rx_hook * @sk: socket we received the packet. Its ignored. @@ -248,7 +279,7 @@ ospf_rx_hook(sock * sk, int size) if (ifa->stub) return (1); - ps = (struct ospf_packet *) ipv4_skip_header(sk->rbuf, &size); + ps = (struct ospf_packet *) ip_skip_header(sk->rbuf, &size); if (ps == NULL) { @@ -256,6 +287,7 @@ ospf_rx_hook(sock * sk, int size) return 1; } + /* This is strange! */ if ((ifa->oa->areaid != 0) && (ntohl(ps->areaid) == 0)) { WALK_LIST(iff, po->iface_list) @@ -270,17 +302,16 @@ ospf_rx_hook(sock * sk, int size) DBG("%s: RX_Hook called on interface %s.\n", p->name, sk->iface->name); - osize = ntohs(ps->length); - if ((unsigned) size < sizeof(struct ospf_packet)) { log(L_ERR "%s%I - too short (%u bytes)", mesg, sk->faddr, size); return 1; } - if ((osize > size) || (osize != (4 * (osize / 4)))) + osize = ntohs(ps->length); + if ((osize > size) || ((osize % 4) != 0)) { - log(L_ERR "%s%I - size field does not match (%d/%d)", mesg, sk->faddr, ntohs(ps->length), size ); + log(L_ERR "%s%I - size field does not match (%d/%d)", mesg, sk->faddr, osize, size); return 1; } @@ -290,21 +321,32 @@ ospf_rx_hook(sock * sk, int size) return 1; } +#ifdef OSPFv2 if ((ps->autype != htons(OSPF_AUTH_CRYPT)) && (!ipsum_verify(ps, 16, (void *) ps + sizeof(struct ospf_packet), - ntohs(ps->length) - sizeof(struct ospf_packet), NULL))) + osize - sizeof(struct ospf_packet), NULL))) { log(L_ERR "%s%I - bad checksum", mesg, sk->faddr); return 1; } +#endif if (ntohl(ps->areaid) != ifa->oa->areaid) { - log(L_ERR "%s%I - different area %ld", mesg, sk->faddr, ntohl(ps->areaid)); + log(L_ERR "%s%I - different area (%u)", mesg, sk->faddr, ntohl(ps->areaid)); return 1; } - if (ntohl(ps->routerid) == p->cf->global->router_id) + /* FIXME - handling of instance id should be better */ +#ifdef OSPFv3 + if (ps->instance_id != ifa->instance_id) + { + log(L_ERR "%s%I - different instance (%u)", mesg, sk->faddr, ps->instance_id); + return 1; + } +#endif + + if (ntohl(ps->routerid) == po->router_id) { log(L_ERR "%s%I - received my own router ID!", mesg, sk->faddr); return 1; @@ -316,17 +358,17 @@ ospf_rx_hook(sock * sk, int size) return 1; } - if (((unsigned) size > sk->rbsize) || (ntohs(ps->length) > sk->rbsize)) + if ((unsigned) size > sk->rbsize) { - log(L_ERR "%s%I - packet is too large (%d-%d vs %d)", - mesg, sk->faddr, size, ntohs(ps->length), sk->rbsize); + log(L_ERR "%s%I - packet is too large (%d vs %d)", + mesg, sk->faddr, size, sk->rbsize); return 1; } /* This is deviation from RFC 2328 - neighbours should be identified by * IP address on broadcast and NBMA networks. */ - n = find_neigh(ifa, ntohl(((struct ospf_packet *) ps)->routerid)); + n = find_neigh(ifa, ntohl(ps->routerid)); if(!n && (ps->type != HELLO_P)) { @@ -352,23 +394,23 @@ ospf_rx_hook(sock * sk, int size) { case HELLO_P: DBG("%s: Hello received.\n", p->name); - ospf_hello_receive((struct ospf_hello_packet *) ps, ifa, n, sk->faddr); + ospf_hello_receive(ps, ifa, n, sk->faddr); break; case DBDES_P: DBG("%s: Database description received.\n", p->name); - ospf_dbdes_receive((struct ospf_dbdes_packet *) ps, ifa, n); + ospf_dbdes_receive(ps, ifa, n); break; case LSREQ_P: DBG("%s: Link state request received.\n", p->name); - ospf_lsreq_receive((struct ospf_lsreq_packet *) ps, ifa, n); + ospf_lsreq_receive(ps, ifa, n); break; case LSUPD_P: DBG("%s: Link state update received.\n", p->name); - ospf_lsupd_receive((struct ospf_lsupd_packet *) ps, ifa, n); + ospf_lsupd_receive(ps, ifa, n); break; case LSACK_P: DBG("%s: Link state ack received.\n", p->name); - ospf_lsack_receive((struct ospf_lsack_packet *) ps, ifa, n); + ospf_lsack_receive(ps, ifa, n); break; default: log(L_ERR "%s%I - wrong type %u", mesg, sk->faddr, ps->type); @@ -395,30 +437,36 @@ ospf_err_hook(sock * sk, int err) } void -ospf_send_to_agt(sock * sk, struct ospf_iface *ifa, u8 state) +ospf_send_to_agt(struct ospf_iface *ifa, u8 state) { struct ospf_neighbor *n; WALK_LIST(n, ifa->neigh_list) if (n->state >= state) - ospf_send_to(sk, n->ip, ifa); + ospf_send_to(ifa, n->ip); } void -ospf_send_to_bdr(sock * sk, struct ospf_iface *ifa) +ospf_send_to_bdr(struct ospf_iface *ifa) { if (!ipa_equal(ifa->drip, IPA_NONE)) - ospf_send_to(sk, ifa->drip, ifa); + ospf_send_to(ifa, ifa->drip); if (!ipa_equal(ifa->bdrip, IPA_NONE)) - ospf_send_to(sk, ifa->bdrip, ifa); + ospf_send_to(ifa, ifa->bdrip); } void -ospf_send_to(sock *sk, ip_addr ip, struct ospf_iface *ifa) +ospf_send_to(struct ospf_iface *ifa, ip_addr ip) { + sock *sk = ifa->sk; struct ospf_packet *pkt = (struct ospf_packet *) sk->tbuf; - int len = ntohs(pkt->length) + ((ifa->autype == OSPF_AUTH_CRYPT) ? OSPF_AUTH_CRYPT_SIZE : 0); - ospf_pkt_finalize(ifa, pkt); + int len = ntohs(pkt->length); + +#ifdef OSPFv2 + if (ifa->autype == OSPF_AUTH_CRYPT) + len += OSPF_AUTH_CRYPT_SIZE; +#endif + ospf_pkt_finalize(ifa, pkt); if (sk->tbuf != sk->tpos) log(L_ERR "Aiee, old packet was overwritted in TX buffer"); diff --git a/proto/ospf/packet.h b/proto/ospf/packet.h index b85b608..4d5612f 100644 --- a/proto/ospf/packet.h +++ b/proto/ospf/packet.h @@ -15,9 +15,9 @@ unsigned 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_send_to_agt(sock * sk, struct ospf_iface *ifa, u8 state); -void ospf_send_to_bdr(sock * sk, struct ospf_iface *ifa); -void ospf_send_to(sock *sk, ip_addr ip, struct ospf_iface *ifa); +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); #endif /* _BIRD_OSPF_PACKET_H_ */ diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 2e1de44..2928326 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -8,20 +8,29 @@ #include "ospf.h" -static void -add_cand(list * l, struct top_hash_entry *en, - struct top_hash_entry *par, u16 dist, struct ospf_area *oa); -static void -calc_next_hop(struct top_hash_entry *en, - struct top_hash_entry *par, struct ospf_area *oa); +static void add_cand(list * l, struct top_hash_entry *en, + struct top_hash_entry *par, u32 dist, + struct ospf_area *oa); +static int calc_next_hop(struct ospf_area *oa, + struct top_hash_entry *en, + struct top_hash_entry *par); static void ospf_ext_spf(struct proto_ospf *po); 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 fill_ri(orta * orta) { orta->type = RTS_DUMMY; - orta->capa = 0; + orta->options = 0; orta->oa = NULL; orta->metric1 = LSINFINITY; orta->metric2 = LSINFINITY; @@ -137,88 +146,109 @@ ri_install(struct proto_ospf *po, ip_addr prefix, int pxlen, int dest, } static void -ospf_rt_spfa(struct ospf_area *oa) +add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_hash_entry *en) { - u32 i, *rts; - struct ospf_lsa_rt *rt; - struct ospf_lsa_rt_link *rtl, *rr; - struct proto *p = &oa->po->proto; - struct proto_ospf *po = oa->po; - struct ospf_lsa_net *ln; orta nf; - struct ospf_iface *iface; - struct top_hash_entry *act, *tmp; - node *n; + nf.type = RTS_OSPF; + nf.options = 0; + nf.metric1 = metric; + nf.metric2 = LSINFINITY; + nf.tag = 0; + nf.oa = oa; + nf.ar = en; + nf.nh = en->nh; + nf.ifa = en->nhi; + + /* FIXME check nf.ifa on stubs */ + ri_install(oa->po, px, pxlen, ORT_NET, &nf, NULL); +} +#ifdef OSPFv3 +static void +process_prefixes(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; + int pxlen; + u8 pxopts; + u16 metric; + u32 *buf; + int i; - if (oa->rt == NULL) - return; + WALK_SLIST(en, po->lsal) + { + if (en->lsa.type != LSA_T_PREFIX) + continue; - OSPF_TRACE(D_EVENTS, "Starting routing table calculation for area %R", oa->areaid); + if (en->domain != oa->areaid) + continue; - if (oa->rt->dist != LSINFINITY) - bug("Aging was not processed."); + if (en->lsa.age == LSA_MAXAGE) + continue; - init_list(&oa->cand); /* Empty list of candidates */ - oa->trcap = 0; + px = en->lsa_body; - DBG("LSA db prepared, adding me into candidate list.\n"); + /* 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); + else + src = ospf_hash_find(po->gr, oa->areaid, px->ref_id, px->ref_rt, px->ref_type); - oa->rt->dist = 0; - 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); + if (!src) + continue; - while (!EMPTY_LIST(oa->cand)) - { - n = HEAD(oa->cand); - act = SKIP_BACK(struct top_hash_entry, cn, n); - rem_node(n); + if (src->lsa.age == LSA_MAXAGE) + continue; - DBG("Working on LSA: rt: %R, id: %R, type: %u\n", - act->lsa.rt, act->lsa.id, act->lsa.type); + if ((src->lsa.type != LSA_T_RT) && (src->lsa.type != LSA_T_NET)) + continue; - act->color = INSPF; - switch (act->lsa.type) - { - case LSA_T_RT: - rt = (struct ospf_lsa_rt *) act->lsa_body; - if (rt->veb.bit.v) - oa->trcap = 1; - if (rt->veb.bit.b || rt->veb.bit.e) + buf = px->rest; + for (i = 0; i < px->pxcount; i++) { - nf.type = RTS_OSPF; - nf.capa = 0; - if (rt->veb.bit.b) nf.capa |= ORTA_ABR; - if (rt->veb.bit.e) nf.capa |= ORTA_ASBR; - nf.metric1 = act->dist; - nf.metric2 = LSINFINITY; - nf.tag = 0; - nf.oa = oa; - nf.ar = act; - nf.nh = act->nh; - nf.ifa = act->nhi; - ri_install(po, ipa_from_u32(act->lsa.id), 32, ORT_ROUTER, &nf, NULL); + buf = lsa_get_ipv6_prefix(buf, &pxa, &pxlen, &pxopts, &metric); + + if (pxopts & OPT_PX_NU) + continue; + + add_network(oa, pxa, pxlen, src->dist + metric, src); } - rr = (struct ospf_lsa_rt_link *) (rt + 1); - DBG(" Number of links: %u\n", rt->links); - for (i = 0; i < rt->links; i++) - { - tmp = NULL; - rtl = (rr + i); - DBG(" Working on link: %R (type: %u) ", rtl->id, rtl->type); - switch (rtl->type) + } +} +#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; + orta nf; + u32 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: /* - * This violates rfc2328! but I hope - * it's also correct. + * This violates rfc2328! But it is mostly harmless. */ DBG("\n"); nf.type = RTS_OSPF; - nf.capa = 0; + nf.options = 0; nf.metric1 = act->dist + rtl->metric; nf.metric2 = LSINFINITY; nf.tag = 0; @@ -228,20 +258,20 @@ ospf_rt_spfa(struct ospf_area *oa) nf.ifa = act->nhi; if (act == oa->rt) - { - struct ospf_iface *iff; - - WALK_LIST(iff, po->iface_list) /* Try to find corresponding interface */ { - if (iff->iface && (iff->type != OSPF_IT_VLINK) && - (rtl->id == (ipa_to_u32(ipa_mkmask(iff->iface->addr->pxlen)) - & ipa_to_u32(iff->iface->addr->prefix)))) /* No VLINK and IP must match */ - { - nf.ifa = iff; - break; - } - } - } + struct ospf_iface *iff; + + WALK_LIST(iff, po->iface_list) /* Try to find corresponding interface */ + { + if (iff->iface && (iff->type != OSPF_IT_VLINK) && + (rtl->id == (ipa_to_u32(ipa_mkmask(iff->iface->addr->pxlen)) + & ipa_to_u32(iff->iface->addr->prefix)))) /* No VLINK and IP must match */ + { + nf.ifa = iff; + break; + } + } + } if (!nf.ifa) continue; @@ -249,9 +279,15 @@ ospf_rt_spfa(struct ospf_area *oa) ri_install(po, ipa_from_u32(rtl->id), ipa_mklen(ipa_from_u32(rtl->data)), ORT_NET, &nf, NULL); break; +#endif case LSART_NET: - tmp = ospf_hash_find(po->gr, oa->areaid, rtl->id, rtl->id, LSA_T_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 == NULL) DBG("Not found!\n"); else @@ -260,23 +296,75 @@ ospf_rt_spfa(struct ospf_area *oa) case LSART_VLNK: case LSART_PTP: - tmp = ospf_hash_find(po->gr, oa->areaid, rtl->id, rtl->id, LSA_T_RT); + tmp = ospf_hash_find_rt(po->gr, oa->areaid, rtl->id); DBG("PTP found.\n"); 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); - } - break; - case LSA_T_NET: - ln = act->lsa_body; + 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); + } +} + +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 ospf_iface *iface; + struct top_hash_entry *act, *tmp; + u32 i, *rts; + orta nf; + node *n; + + if (oa->rt == NULL) + return; + + OSPF_TRACE(D_EVENTS, "Starting routing table calculation for area %R", oa->areaid); + + if (oa->rt->dist != LSINFINITY) + bug("Aging was not processed."); + + /* 16.1. (1) */ + init_list(&oa->cand); /* Empty list of candidates */ + oa->trcap = 0; + + DBG("LSA db prepared, adding me into candidate list.\n"); + + oa->rt->dist = 0; + 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); + + while (!EMPTY_LIST(oa->cand)) + { + n = HEAD(oa->cand); + act = SKIP_BACK(struct top_hash_entry, cn, n); + rem_node(n); + + DBG("Working on LSA: rt: %R, id: %R, type: %u\n", + act->lsa.rt, act->lsa.id, act->lsa.type); + + act->color = INSPF; + 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 OSPFv2, just ASBRs and ABRs are needed to add to oa->rtr table */ + // ((rt->options & OPT_RT_V) || (rt->options & OPT_RT_E)) + nf.type = RTS_OSPF; - nf.capa = 0; + nf.options = rt->options; nf.metric1 = act->dist; nf.metric2 = LSINFINITY; nf.tag = 0; @@ -284,15 +372,30 @@ ospf_rt_spfa(struct ospf_area *oa) nf.ar = act; nf.nh = act->nh; nf.ifa = act->nhi; - ri_install(po, ipa_and(ipa_from_u32(act->lsa.id), ln->netmask), - ipa_mklen(ln->netmask), ORT_NET, &nf, NULL); + ri_install(po, ipa_from_rid(act->lsa.rt), MAX_PREFIX_LENGTH, ORT_ROUTER, &nf, NULL); + +#ifdef OSPFv2 + ospf_rt_spfa_rtlinks(oa, act, act); +#else /* OSPFv3 */ + 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 + + break; + case LSA_T_NET: + ln = act->lsa_body; + +#ifdef OSPFv2 + add_network(oa, ipa_and(ipa_from_u32(act->lsa.id), ln->netmask), + ipa_mklen(ln->netmask), act->dist, act); +#endif rts = (u32 *) (ln + 1); - for (i = 0; i < (act->lsa.length - sizeof(struct ospf_lsa_header) - - sizeof(struct ospf_lsa_net)) / sizeof(u32); i++) + for (i = 0; i < lsa_net_count(&act->lsa); i++) { DBG(" Working on router %R ", rts[i]); - tmp = ospf_hash_find(po->gr, oa->areaid, rts[i], rts[i], LSA_T_RT); + tmp = ospf_hash_find_rt(po->gr, oa->areaid, rts[i]); if (tmp != NULL) DBG("Found :-)\n"); else @@ -303,13 +406,17 @@ ospf_rt_spfa(struct ospf_area *oa) } } +#ifdef OSPFv3 + process_prefixes(oa); +#endif + /* Find new/lost VLINK peers */ WALK_LIST(iface, po->iface_list) { if ((iface->type == OSPF_IT_VLINK) && (iface->voa == oa)) { - if ((tmp = ospf_hash_find(po->gr, oa->areaid, iface->vid, iface->vid, LSA_T_RT)) && - (!ipa_equal(tmp->lb, IPA_NONE))) + if ((tmp = ospf_hash_find_rt(po->gr, oa->areaid, iface->vid)) && + (!ipa_equal(tmp->lb, IPA_NONE))) { if ((iface->state != OSPF_IS_PTP) || (iface->iface != tmp->nhi->iface) || (!ipa_equal(iface->vip, tmp->lb))) { @@ -333,22 +440,25 @@ ospf_rt_spfa(struct ospf_area *oa) } static int -link_back(struct ospf_area *oa, struct top_hash_entry *fol, struct top_hash_entry *pre) +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 top_hash_entry *tmp; struct proto_ospf *po = oa->po; - if (!pre) return 0; - if (!fol) return 0; - switch (fol->lsa.type) + if (!en || !par) return 0; + + // FIXME lb should be properly set for vlinks */ + en->lb = IPA_NONE; + switch (en->lsa.type) { case LSA_T_RT: - rt = (struct ospf_lsa_rt *) fol->lsa_body; + rt = (struct ospf_lsa_rt *) en->lsa_body; rr = (struct ospf_lsa_rt_link *) (rt + 1); - for (i = 0; i < rt->links; i++) + for (i = 0; i < lsa_rt_count(&en->lsa); i++) { rtl = (rr + i); switch (rtl->type) @@ -356,40 +466,41 @@ link_back(struct ospf_area *oa, struct top_hash_entry *fol, struct top_hash_entr case LSART_STUB: break; case LSART_NET: - if (ospf_hash_find(po->gr, oa->areaid, rtl->id, rtl->id, LSA_T_NET) == pre) - { - fol->lb = ipa_from_u32(rtl->data); +#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) return 1; - } + break; case LSART_VLNK: case LSART_PTP: - if (ospf_hash_find(po->gr, oa->areaid, rtl->id, rtl->id, LSA_T_RT) == pre) - { - fol->lb = ipa_from_u32(rtl->data); + tmp = ospf_hash_find_rt(po->gr, oa->areaid, rtl->id); + if (tmp == par) return 1; - } + break; default: - log("Unknown link type in router lsa. (rid = %R)", fol->lsa.id); + log(L_WARN "Unknown link type in router lsa. (rid = %R)", en->lsa.rt); break; } } break; case LSA_T_NET: - ln = fol->lsa_body; + ln = en->lsa_body; rts = (u32 *) (ln + 1); - for (i = 0; i < (fol->lsa.length - sizeof(struct ospf_lsa_header) - - sizeof(struct ospf_lsa_net)) / sizeof(u32); i++) + for (i = 0; i < lsa_net_count(&en->lsa); i++) { - if (ospf_hash_find(po->gr, oa->areaid, *(rts + i), *(rts + i), LSA_T_RT) == pre) - { + tmp = ospf_hash_find_rt(po->gr, oa->areaid, rts[i]); + if (tmp == par) return 1; - } } break; default: - bug("Unknown lsa type. (id = %R)", fol->lsa.id); + bug("Unknown lsa type %x.", en->lsa.type); } return 0; } @@ -400,10 +511,10 @@ 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 *mask, ip, abrip; + ip_addr ip, abrip; struct top_hash_entry *en; - int mlen = -1, type = -1; - union ospf_lsa_sum_tm *tm; + u32 dst_rid, metric, options; + int pxlen = -1, type = -1; ort *re = NULL, *abr; orta nf; @@ -411,57 +522,81 @@ ospf_rt_sum_tr(struct ospf_area *oa) WALK_SLIST(en, po->lsal) { - if (en->oa != oa) - continue; - if (en->lsa.age == LSA_MAXAGE) + if ((en->lsa.type != LSA_T_SUM_RT) && (en->lsa.type != LSA_T_SUM_NET)) continue; - if (en->dist == LSINFINITY) + + if (en->domain != oa->areaid) continue; - if (en->lsa.rt == p->cf->global->router_id) + if (en->lsa.age == LSA_MAXAGE) continue; - if ((en->lsa.type != LSA_T_SUM_RT) && (en->lsa.type != LSA_T_SUM_NET)) + if (en->dist == LSINFINITY) continue; - mask = (ip_addr *)en->lsa_body; + if (en->lsa.rt == po->router_id) + continue; if (en->lsa.type == LSA_T_SUM_NET) { - mlen = ipa_mklen(*mask); - ip = ipa_and(ipa_from_u32(en->lsa.id), *mask); +#ifdef OSPFv2 + struct ospf_lsa_sum *ls = en->lsa_body; + pxlen = ipa_mklen(ls->netmask); + ip = ipa_and(ipa_from_u32(en->lsa.id), 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); + + if (pxopts & OPT_PX_NU) + continue; +#endif + + metric = ls->metric & METRIC_MASK; + options = 0; type = ORT_NET; - re = (ort *) fib_find(&po->rtf, &ip, 32); + re = (ort *) fib_find(&po->rtf, &ip, pxlen); } - - if (en->lsa.type == LSA_T_SUM_RT) + else if (en->lsa.type == LSA_T_SUM_RT) { - ip = ipa_from_u32(en->lsa.id); - mlen = 32; +#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 + + ip = ipa_from_rid(dst_rid); + pxlen = MAX_PREFIX_LENGTH; + metric = ls->metric & METRIC_MASK; + options |= ORTA_ASBR; type = ORT_ROUTER; - re = (ort *) fib_find(&bb->rtr, &ip, 32); + re = (ort *) fib_find(&bb->rtr, &ip, pxlen); } + if (!re) continue; if (re->n.oa->areaid != 0) continue; if ((re->n.type != RTS_OSPF) && (re->n.type != RTS_OSPF_IA)) continue; - abrip = ipa_from_u32(en->lsa.rt); + abrip = ipa_from_rid(en->lsa.rt); - abr = fib_find(&oa->rtr, &abrip, 32); + abr = fib_find(&oa->rtr, &abrip, MAX_PREFIX_LENGTH); if (!abr) continue; - tm = (union ospf_lsa_sum_tm *)(mask + 1); - nf.type = re->n.type; - nf.capa = ORTA_ASBR; - nf.metric1 = abr->n.metric1 + (tm->metric & METRIC_MASK); + nf.options = options; + nf.metric1 = abr->n.metric1 + metric; nf.metric2 = LSINFINITY; nf.tag = 0; nf.oa = oa; nf.ar = abr->n.ar; nf.nh = abr->n.nh; nf.ifa = abr->n.ifa; - ri_install(po, ip, mlen, type, &nf, NULL); + ri_install(po, ip, pxlen, type, &nf, NULL); } } @@ -472,76 +607,105 @@ ospf_rt_sum(struct ospf_area *oa) struct proto_ospf *po = oa->po; struct proto *p = &po->proto; struct top_hash_entry *en; - ip_addr *mask, ip, abrip; /* abrIP is actually ID */ + ip_addr ip, abrip; /* abrIP is actually ID */ + u32 dst_rid, metric, options; struct area_net *anet; orta nf; ort *abr; - int mlen = -1, type = -1; - union ospf_lsa_sum_tm *tm; + int pxlen = -1, type = -1; OSPF_TRACE(D_EVENTS, "Starting routing table calculation for inter-area (area %R)", oa->areaid); WALK_SLIST(en, po->lsal) { - if (en->oa != oa) + if ((en->lsa.type != LSA_T_SUM_RT) && (en->lsa.type != LSA_T_SUM_NET)) continue; + + if (en->domain != oa->areaid) + continue; + /* Page 169 (1) */ if (en->lsa.age == LSA_MAXAGE) continue; - /* Page 169 (2) */ - if (en->lsa.rt == p->cf->global->router_id) - continue; - if ((en->lsa.type != LSA_T_SUM_RT) && (en->lsa.type != LSA_T_SUM_NET)) + /* Page 169 (2) */ + if (en->lsa.rt == po->router_id) continue; - mask = (ip_addr *)en->lsa_body; - tm = (union ospf_lsa_sum_tm *)(mask + 1); - - if ((tm->metric & METRIC_MASK) == LSINFINITY) - continue; if (en->lsa.type == LSA_T_SUM_NET) { struct ospf_area *oaa; int skip = 0; - mlen = ipa_mklen(*mask); - ip = ipa_and(ipa_from_u32(en->lsa.id), *mask); + +#ifdef OSPFv2 + struct ospf_lsa_sum *ls = en->lsa_body; + pxlen = ipa_mklen(ls->netmask); + ip = ipa_and(ipa_from_u32(en->lsa.id), 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); + + if (pxopts & OPT_PX_NU) + continue; +#endif + + metric = ls->metric & METRIC_MASK; + options = 0; + type = ORT_NET; + /* Page 169 (3) */ WALK_LIST(oaa, po->area_list) { - if ((anet = fib_find(&oaa->net_fib, &ip, mlen)) && anet->active) + if ((anet = fib_find(&oaa->net_fib, &ip, pxlen)) && anet->active) { skip = 1; break; } } if (skip) continue; - - type = ORT_NET; } else { - ip = ipa_from_u32(en->lsa.id); - mlen = 32; +#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 + + ip = ipa_from_rid(dst_rid); + pxlen = MAX_PREFIX_LENGTH; + metric = ls->metric & METRIC_MASK; + options |= ORTA_ASBR; type = ORT_ROUTER; } - abrip = ipa_from_u32(en->lsa.rt); - if (!(abr = (ort *) fib_find(&oa->rtr, &abrip, 32))) continue; + /* Page 169 (1) */ + if (metric == LSINFINITY) + continue; + + /* Page 169 (4) */ + abrip = ipa_from_rid(en->lsa.rt); + if (!(abr = (ort *) fib_find(&oa->rtr, &abrip, MAX_PREFIX_LENGTH))) continue; if (abr->n.metric1 == LSINFINITY) continue; - if (!(abr->n.capa & ORTA_ABR)) continue; + if (!(abr->n.options & ORTA_ABR)) continue; nf.type = RTS_OSPF_IA; - nf.capa = ORTA_ASBR; - nf.metric1 = abr->n.metric1 + (tm->metric & METRIC_MASK); + nf.options = options; + nf.metric1 = abr->n.metric1 + metric; nf.metric2 = LSINFINITY; nf.tag = 0; nf.oa = oa; nf.ar = abr->n.ar; nf.nh = abr->n.nh; nf.ifa = abr->n.ifa; - ri_install(po, ip, mlen, type, &nf, NULL); + ri_install(po, ip, pxlen, type, &nf, NULL); } } @@ -567,7 +731,7 @@ ospf_rt_spf(struct proto_ospf *po) OSPF_TRACE(D_EVENTS, "Starting routing table calculation"); - /* Invalidate old routing table */ + /* 16. (1) - Invalidate old routing table */ FIB_WALK(&po->rtf, nftmp) { ri = (ort *) nftmp; @@ -594,9 +758,12 @@ ospf_rt_spf(struct proto_ospf *po) anet->metric = 1; } FIB_WALK_END; + + /* 16. (2) */ ospf_rt_spfa(oa); } + /* 16. (3) */ if ((po->areano == 1) || (!po->backbone)) { ospf_rt_sum(HEAD(po->area_list)); @@ -606,6 +773,7 @@ ospf_rt_spf(struct proto_ospf *po) ospf_rt_sum(po->backbone); } + /* 16. (4) */ WALK_LIST(oa, po->area_list) { if (oa->trcap && (oa->areaid != 0)) @@ -615,6 +783,7 @@ ospf_rt_spf(struct proto_ospf *po) } } + /* 16. (5) */ ospf_ext_spf(po); rt_sync(po); @@ -622,13 +791,12 @@ ospf_rt_spf(struct proto_ospf *po) po->calcrt = 0; } - /** * ospf_ext_spf - calculate external paths * @po: protocol * * After routing table for any area is calculated, calculation of external - * path is invoked. This process is described in 16.6 of RFC 2328. + * path is invoked. This process is described in 16.4 of RFC 2328. * Inter- and Intra-area paths are always prefered over externals. */ static void @@ -639,54 +807,80 @@ ospf_ext_spf(struct proto_ospf *po) struct top_hash_entry *en; struct proto *p = &po->proto; struct ospf_lsa_ext *le; - struct ospf_lsa_ext_tos *lt; - int mlen; - ip_addr ip, nh, rtid; + int pxlen, ebit, rt_fwaddr_valid; + ip_addr ip, nh, rtid, rt_fwaddr; struct ospf_iface *nhi = NULL; - int met1, met2; + u32 br_metric, rt_metric, rt_tag; neighbor *nn; struct ospf_area *atmp; - OSPF_TRACE(D_EVENTS, "Starting routing table calculation for ext routes"); WALK_SLIST(en, po->lsal) { + /* 16.4. (1) */ if (en->lsa.type != LSA_T_EXT) continue; if (en->lsa.age == LSA_MAXAGE) continue; - if (en->lsa.rt == p->cf->global->router_id) + + /* 16.4. (2) */ + if (en->lsa.rt == po->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); + le = en->lsa_body; - lt = (struct ospf_lsa_ext_tos *) (le + 1); - DBG("%s: Working on LSA. ID: %R, RT: %R, Type: %u, Mask %I\n", - p->name, en->lsa.id, en->lsa.rt, en->lsa.type, le->netmask); + rt_metric = le->metric & METRIC_MASK; + ebit = le->metric & LSA_EXT_EBIT; - if ((lt->etm.metric & METRIC_MASK) == LSINFINITY) + if (rt_metric == LSINFINITY) continue; + +#ifdef OSPFv2 ip = ipa_and(ipa_from_u32(en->lsa.id), le->netmask); - mlen = ipa_mklen(le->netmask); - if ((mlen < 0) || (mlen > 32)) + pxlen = ipa_mklen(le->netmask); + rt_fwaddr = le->fwaddr; + rt_fwaddr_valid = !ipa_equal(rt_fwaddr, IPA_NONE); + rt_tag = le->tag; +#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) + 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; +#endif + + if (pxlen < 0) { - log("%s: Invalid mask in LSA. ID: %R, RT: %R, Type: %u, Mask %I", - p->name, en->lsa.id, en->lsa.rt, en->lsa.type, le->netmask); + log(L_WARN "%s: Invalid mask in LSA (Type: %04x, Id: %R, Rt: %R)", + p->name, en->lsa.type, en->lsa.id, en->lsa.rt); continue; } nhi = NULL; nh = IPA_NONE; - met1 = LSINFINITY; - met2 = LSINFINITY; - - rtid = ipa_from_u32(en->lsa.rt); - + /* 16.4. (3) */ + rtid = ipa_from_rid(en->lsa.rt); nf1 = NULL; WALK_LIST(atmp, po->area_list) { - nfh = fib_find(&atmp->rtr, &rtid, 32); + nfh = fib_find(&atmp->rtr, &rtid, MAX_PREFIX_LENGTH); if (nfh == NULL) continue; if (nf1 == NULL) nf1 = nfh; else if (ri_better(po, &nfh->n, NULL, &nf1->n, NULL, po->rfc1583)) nf1 = nfh; @@ -698,50 +892,29 @@ ospf_ext_spf(struct proto_ospf *po) if (nf1->n.metric1 == LSINFINITY) continue; /* distance is INF */ - if (!(nf1->n.capa & ORTA_ASBR)) + if (!(nf1->n.options & ORTA_ASBR)) continue; /* It is not ASBR */ - if (ipa_equal(lt->fwaddr, IPA_NONE)) + if (!rt_fwaddr_valid) { - if (lt->etm.etos.ebit) - { /* FW address == 0 */ - met1 = nf1->n.metric1; - met2 = (lt->etm.metric & METRIC_MASK); - } - else - { - met1 = nf1->n.metric1 + (lt->etm.metric & METRIC_MASK); - met2 = LSINFINITY; - } - nh = nf1->n.nh; nhi = nf1->n.ifa; nfh = nf1; + br_metric = nf1->n.metric1; } else - { /* FW address !=0 */ - nf2 = fib_route(&po->rtf, lt->fwaddr, 32); + { + nf2 = fib_route(&po->rtf, rt_fwaddr, MAX_PREFIX_LENGTH); if (!nf2) { - DBG("Cannot find network route (GW=%I)\n", lt->fwaddr); + DBG("Cannot find network route (GW=%I)\n", rt_fwaddr); continue; } - if (lt->etm.etos.ebit) - { - met1 = nf2->n.metric1; - met2 = (lt->etm.metric & METRIC_MASK); - } - else - { - met1 = nf2->n.metric1 + (lt->etm.metric & METRIC_MASK); - met2 = LSINFINITY; - } - - if ((nn = neigh_find(p, <->fwaddr, 0)) != NULL) + if ((nn = neigh_find(p, &rt_fwaddr, 0)) != NULL) { - nh = lt->fwaddr; + nh = rt_fwaddr; nhi = ospf_iface_find(po, nn->iface); } else @@ -750,20 +923,31 @@ ospf_ext_spf(struct proto_ospf *po) nhi = nf2->n.ifa; } - if (nf2->n.metric1 == LSINFINITY) + br_metric = nf2->n.metric1; + if (br_metric == LSINFINITY) continue; /* distance is INF */ } - nfa.type = (met2 == LSINFINITY) ? RTS_OSPF_EXT1 : RTS_OSPF_EXT2; - nfa.capa = 0; - nfa.metric1 = met1; - nfa.metric2 = met2; - nfa.tag = lt->tag; + if (ebit) + { + nfa.type = RTS_OSPF_EXT2; + nfa.metric1 = br_metric; + nfa.metric2 = rt_metric; + } + else + { + nfa.type = RTS_OSPF_EXT1; + nfa.metric1 = br_metric + rt_metric; + nfa.metric2 = LSINFINITY; + } + + nfa.options = 0; + nfa.tag = rt_tag; nfa.oa = (po->backbone == NULL) ? HEAD(po->area_list) : po->backbone; nfa.ar = nf1->n.ar; nfa.nh = nh; nfa.ifa = nhi; - ri_install(po, ip, mlen, ORT_NET, &nfa, nfh); + ri_install(po, ip, pxlen, ORT_NET, &nfa, nfh); } } @@ -771,22 +955,32 @@ ospf_ext_spf(struct proto_ospf *po) /* Add LSA into list of candidates in Dijkstra's algorithm */ static void add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, - u16 dist, struct ospf_area *oa) + u32 dist, struct ospf_area *oa) { node *prev, *n; int added = 0; struct top_hash_entry *act; - ip_addr old_nh; - struct ospf_iface *old_nhi; + /* 16.1. (2b) */ if (en == NULL) return; 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) || !(rt->options & OPT_R)) + return; + } +#endif + + /* 16.1. (2c) */ if (en->color == INSPF) return; + /* 16.1. (2d) */ if (dist >= en->dist) return; /* @@ -794,27 +988,20 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, * next hops. I'll start as soon as nest will */ + /* We should check whether there is a reverse link from en to par, */ if (!link_back(oa, en, par)) return; + if (!calc_next_hop(oa, en, par)) + { + log(L_WARN "Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)", + en->lsa.type, en->lsa.id, en->lsa.rt); + return; + } + DBG(" Adding candidate: rt: %R, id: %R, type: %u\n", en->lsa.rt, en->lsa.id, en->lsa.type); - old_nhi = en->nhi; - old_nh = en->nh; - - en->nhi = NULL; - en->nh = IPA_NONE; - calc_next_hop(en, par, oa); - - if (!en->nhi) - { - /* No next hop found, we undo changes and return */ - en->nhi = old_nhi; - en->nh = old_nh; - return; - } - if (en->color == CANDIDATE) { /* We found a shorter path */ rem_node(&en->cn); @@ -835,6 +1022,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_NET))) + /* FIXME - shouldn't be here LSA_T_RT ??? */ { if (prev == NULL) add_head(l, &en->cn); @@ -853,84 +1041,85 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, } } -static void -calc_next_hop(struct top_hash_entry *en, struct top_hash_entry *par, - struct ospf_area *oa) + +static inline int +match_dr(struct ospf_iface *ifa, struct top_hash_entry *en) +{ +#ifdef OSPFv2 + return (ifa->drid == en->lsa.rt) && (ipa_to_u32(ifa->drip) == en->lsa.id); +#else /* OSPFv3 */ + return (ifa->drid == en->lsa.rt) && (ifa->dr_iface_id == en->lsa.id); +#endif +} + +static int +calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, + struct top_hash_entry *par) { struct ospf_neighbor *neigh; struct proto *p = &oa->po->proto; struct proto_ospf *po = oa->po; struct ospf_iface *ifa; - u32 myrid = p->cf->global->router_id; + /* 16.1.1. The next hop calculation */ DBG(" Next hop called.\n"); if (ipa_equal(par->nh, IPA_NONE)) { - neighbor *nn; DBG(" Next hop calculating for id: %R rt: %R type: %u\n", en->lsa.id, en->lsa.rt, en->lsa.type); - if (par == oa->rt) + /* + * 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) + * 3) en is a bcast or nbma neighbor (and par is local network) + */ + + /* The first case - local network */ + if ((en->lsa.type == LSA_T_NET) && (par == oa->rt)) { - if (en->lsa.type == LSA_T_NET) - { - if (en->lsa.rt == myrid) - { - WALK_LIST(ifa, po->iface_list) - if (ifa->iface && (ipa_compare - (ifa->iface->addr->ip, ipa_from_u32(en->lsa.id)) == 0)) + WALK_LIST(ifa, po->iface_list) + if (match_dr(ifa, en)) { + en->nh = IPA_NONE; en->nhi = ifa; - return; + return 1; } - log(L_ERR "I didn't find interface for my self originated LSA!\n"); - /* This could sometimes happen */ - return; - } - else - { - ip_addr ip = ipa_from_u32(en->lsa.id); - nn = neigh_find(p, &ip, 0); - if (nn) - en->nhi = ospf_iface_find(po, nn->iface); - return; - } - } - else - { - if ((neigh = find_neigh_noifa(po, en->lsa.rt)) == NULL) - return; - en->nhi = neigh->ifa; - if (ipa_equal(en->nh, IPA_NONE)) - en->nh = neigh->ip; /* Yes, neighbor is it's - * own next hop */ - return; - } + return 0; } - if (par->lsa.type == LSA_T_NET) + + /* + * Remaining cases - local neighbours. + * There are two problems with this code: + * 1) we use IP address from HELLO packet + * and not the one from LSA (router or link). + * This may break NBMA networks + * 2) we use find_neigh_noifa() and does not + * take into account associated iface. + * This breaks neighbors connected by more links. + */ + + if ((en->lsa.type == LSA_T_RT) && + ((par == oa->rt) || (par->lsa.type == LSA_T_NET))) { - if (en->lsa.type == LSA_T_NET) - bug("Parent for net is net?"); - if ((en->nhi = par->nhi) == NULL) - bug("Did not find next hop interface for INSPF lsa!"); - if ((neigh = find_neigh_noifa(po, en->lsa.rt)) == NULL) - return; - en->nhi = neigh->ifa; - en->nh = neigh->ip; /* Yes, neighbor is it's own - * next hop */ - return; - } - else - { /* Parent is some RT neighbor */ - log(L_ERR "Router's parent has no next hop. (EN=%R, PAR=%R)", - en->lsa.id, par->lsa.id); - /* I hope this would never happen */ - return; + if ((neigh = find_neigh_noifa(po, en->lsa.rt)) != NULL) + { + en->nh = neigh->ip; + en->nhi = neigh->ifa; + return 1; + } + return 0; } + + /* Probably bug or some race condition, we log it */ + log(L_ERR "Unexpected case in next hop calculation"); + + return 0; } + en->nh = par->nh; en->nhi = par->nhi; - DBG(" Next hop calculated: %I.\n", en->nh); + return 1; } static void @@ -944,6 +1133,9 @@ rt_sync(struct proto_ospf *po) struct area_net *anet; int flush; + /* This is used for forced reload of routes */ + int reload = (po->calcrt == 2); + OSPF_TRACE(D_EVENTS, "Starting routing table synchronisation"); DBG("Now syncing my rt table with nest's\n"); @@ -953,7 +1145,7 @@ again1: { nf = (ort *) nftmp; check_sum_lsa(po, nf, ORT_NET); - if (memcmp(&nf->n, &nf->o, sizeof(orta))) + if (reload || memcmp(&nf->n, &nf->o, sizeof(orta))) { /* Some difference */ net *ne; rta a0; @@ -972,18 +1164,18 @@ again1: if (nf->n.ifa) a0.iface = nf->n.ifa->iface; a0.gw = nf->n.nh; - if ((!ipa_equal(nf->n.nh, IPA_NONE)) && (!neigh_find(p, &nf->n.nh, 0))) + if (ipa_nonzero(nf->n.nh) && (!neigh_find2(p, &nf->n.nh, nf->n.ifa->iface, 0))) { int found = 0; struct ospf_iface *ifa; struct top_hash_entry *en; - OSPF_TRACE(D_EVENTS, "Trying to find correct next hop"); + OSPF_TRACE(D_EVENTS, "Trying to find correct next hop %I/%d via %I", nf->fn.prefix, nf->fn.pxlen, nf->n.nh); WALK_LIST(ifa, po->iface_list) { if ((ifa->type == OSPF_IT_VLINK) && ipa_equal(ifa->vip, nf->n.nh)) { - if ((en = ospf_hash_find(po->gr, ifa->voa->areaid, ifa->vid, ifa->vid, LSA_T_RT)) - && (!ipa_equal(en->nh, IPA_NONE))) + if ((en = ospf_hash_find_rt(po->gr, ifa->voa->areaid, ifa->vid)) + && (!ipa_equal(en->nh, IPA_NONE))) { a0.gw = en->nh; found = 1; @@ -1063,7 +1255,7 @@ again2: if (oaa->stub) fl = 1; if (fl) flush_sum_lsa(oaa, &anet->fn, ORT_NET); - else originate_sum_lsa(oaa, &anet->fn, ORT_NET, anet->metric); + else originate_sum_lsa(oaa, &anet->fn, ORT_NET, anet->metric, 0); } } FIB_WALK_END; @@ -1076,7 +1268,7 @@ again2: fnn.prefix = IPA_NONE; fnn.pxlen = 0; - if (oa->stub) originate_sum_lsa(oa, &fnn, ORT_NET, oa->stub); + if (oa->stub) originate_sum_lsa(oa, &fnn, ORT_NET, oa->stub, 0); else flush_sum_lsa(oa, &fnn, ORT_NET); } } diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h index acb0a1a..d4b85ae 100644 --- a/proto/ospf/rt.h +++ b/proto/ospf/rt.h @@ -17,9 +17,12 @@ typedef struct orta { int type; - int capa; -#define ORTA_ASBR 1 -#define ORTA_ABR 2 + u32 options; + /* router-LSA style options (for ORT_ROUTER), with V,E,B bits. + In OSPFv2, ASBRs from another areas (that we know from rt-summary-lsa), + have just ORTA_ASBR in options, their real options are unknown */ +#define ORTA_ASBR OPT_RT_E +#define ORTA_ABR OPT_RT_V struct ospf_area *oa; u32 metric1; u32 metric2; diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 5d8c7a9..3ca5e77 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -20,7 +20,76 @@ #define HASH_LO_STEP 2 #define HASH_LO_MIN 8 -int ptp_unnumbered_stub_lsa = 0; +void originate_prefix_rt_lsa(struct ospf_area *oa); +void originate_prefix_net_lsa(struct ospf_iface *ifa); +void flush_prefix_net_lsa(struct ospf_iface *ifa); + +#ifdef OSPFv2 +#define ipa_to_rid(x) _I(x) +#else /* OSPFv3 */ +#define ipa_to_rid(x) _I3(x) +#endif + + +#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 (id & (1 << (32 - fn->pxlen))) + return id; + else + return id | ~u32_mkmask(fn->pxlen); +} + +#else /* OSPFv3 */ + +static inline u32 +fibnode_to_lsaid(struct proto_ospf *po, struct fib_node *fn) +{ + /* In OSPFv3, it is simpler. There is not a requirement + for membership of the result in input network, so we + just use hash-based unique ID. */ + + return fn->uid; +} + +#endif + static void * lsab_alloc(struct proto_ospf *po, unsigned size) @@ -52,6 +121,19 @@ lsab_flush(struct proto_ospf *po) return r; } +static inline void * +lsab_offset(struct proto_ospf *po, unsigned offset) +{ + return ((byte *) po->lsab) + offset; +} + +static inline void * +lsab_end(struct proto_ospf *po) +{ + return ((byte *) po->lsab) + po->lsab_used; +} + + static int configured_stubnet(struct ospf_area *oa, struct ifa *a) { @@ -72,25 +154,53 @@ configured_stubnet(struct ospf_area *oa, struct ifa *a) return 0; } +int +bcast_net_active(struct ospf_iface *ifa) +{ + struct ospf_neighbor *neigh; + + if (ifa->state == OSPF_IS_WAITING) + return 0; + + WALK_LIST(neigh, ifa->neigh_list) + { + if (neigh->state == NEIGHBOR_FULL) + { + if (neigh->rid == ifa->drid) + return 1; + + if (ifa->state == OSPF_IS_DR) + return 1; + } + } + + return 0; +} + + +#ifdef OSPFv2 + static void * -originate_rt_lsa_body(struct ospf_area *oa, u16 * length) +originate_rt_lsa_body(struct ospf_area *oa, u16 *length) { struct proto_ospf *po = oa->po; struct ospf_iface *ifa; - int i = 0, j = 0, k = 0, bitv = 0; + int i = 0, bitv = 0; struct ospf_lsa_rt *rt; struct ospf_lsa_rt_link *ln; struct ospf_neighbor *neigh; - DBG("%s: Originating RT_lsa body for area %R.\n", po->proto.name, - oa->areaid); - ASSERT(po->lsab_used == 0); rt = lsab_allocz(po, sizeof(struct ospf_lsa_rt)); + + rt->options = 0; + if (po->areano > 1) - rt->veb.bit.b = 1; + rt->options |= OPT_RT_B; + if ((po->ebit) && (!oa->stub)) - rt->veb.bit.e = 1; + rt->options |= OPT_RT_E; + rt = NULL; /* buffer might be reallocated later */ WALK_LIST(ifa, po->iface_list) @@ -123,7 +233,7 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 * length) ln->data = (ifa->iface->addr->flags & IA_UNNUMBERED) ? ifa->iface->index : ipa_to_u32(ifa->iface->addr->ip); ln->metric = ifa->cost; - ln->notos = 0; + ln->padding = 0; i++; master = 1; } @@ -131,26 +241,14 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 * length) case OSPF_IT_BCAST: /* RFC2328 - 12.4.1.2 */ case OSPF_IT_NBMA: - if (ifa->state == OSPF_IS_WAITING) - break; - - j = 0, k = 0; - WALK_LIST(neigh, ifa->neigh_list) - { - if ((neigh->rid == ifa->drid) && (neigh->state == NEIGHBOR_FULL)) - k = 1; - if (neigh->state == NEIGHBOR_FULL) - j = 1; - } - - if (((ifa->state == OSPF_IS_DR) && (j == 1)) || (k == 1)) + 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->iface->addr->ip); ln->metric = ifa->cost; - ln->notos = 0; + ln->padding = 0; i++; master = 1; } @@ -165,7 +263,7 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 * length) ln->id = neigh->rid; ln->data = ipa_to_u32(ifa->iface->addr->ip); ln->metric = ifa->cost; - ln->notos = 0; + ln->padding = 0; i++; master = 1; } @@ -192,7 +290,7 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 * length) ln->id = ipa_to_u32(a->prefix); ln->data = ipa_to_u32(ipa_mkmask(a->pxlen)); ln->metric = ifa->cost; - ln->notos = 0; + ln->padding = 0; i++; } } @@ -206,17 +304,111 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 * length) ln->id = ipa_to_u32(sn->px.addr); ln->data = ipa_to_u32(ipa_mkmask(sn->px.len)); ln->metric = sn->cost; - ln->notos = 0; + ln->padding = 0; i++; } rt = po->lsab; rt->links = i; - rt->veb.bit.v = bitv; + + if (bitv) + rt->options |= OPT_RT_V; + + *length = po->lsab_used + sizeof(struct ospf_lsa_header); + return lsab_flush(po); +} + +#else /* OSPFv3 */ + +static void +add_lsa_rt_link(struct proto_ospf *po, struct ospf_iface *ifa, u8 type, u32 nif, u32 id) +{ + struct ospf_lsa_rt_link *ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); + ln->type = type; + ln->padding = 0; + ln->metric = ifa->cost; + ln->lif = ifa->iface->index; + ln->nif = nif; + ln->id = id; +} + +static void * +originate_rt_lsa_body(struct ospf_area *oa, u16 *length) +{ + struct proto_ospf *po = oa->po; + struct ospf_iface *ifa; + int bitv = 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; + + if ((po->ebit) && (!oa->stub)) + rt->options |= OPT_RT_E; + + rt = NULL; /* buffer might be reallocated later */ + + WALK_LIST(ifa, po->iface_list) + { + if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) && + (!EMPTY_LIST(ifa->neigh_list))) + { + neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list); + if ((neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff)) + bitv = 1; + } + + if ((ifa->oa != oa) || (ifa->state == OSPF_IS_DOWN)) + continue; + + /* BIRD does not support interface loops */ + ASSERT(ifa->state != OSPF_IS_LOOP); + + /* RFC5340 - 4.4.3.2 */ + switch (ifa->type) + { + case OSPF_IT_PTP: + neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list); + if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL)) + add_lsa_rt_link(po, ifa, LSART_PTP, neigh->iface_id, neigh->rid); + 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); + 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); + break; + + default: + log("Unknown interface type %s", ifa->iface->name); + break; + } + } + + if (bitv) + { + rt = po->lsab; + rt->options |= OPT_RT_V; + } + *length = po->lsab_used + sizeof(struct ospf_lsa_header); return lsab_flush(po); } +#endif + /** * originate_rt_lsa - build new instance of router LSA * @oa: ospf_area which is LSA built to @@ -232,10 +424,33 @@ originate_rt_lsa(struct ospf_area *oa) struct ospf_lsa_header lsa; struct proto_ospf *po = oa->po; struct proto *p = &po->proto; - u32 rtid = po->proto.cf->global->router_id; - struct top_hash_entry *en; void *body; + OSPF_TRACE(D_EVENTS, "Originating router-LSA for area %R", oa->areaid); + + lsa.age = 0; + lsa.type = LSA_T_RT; + +#ifdef OSPFv2 + lsa.options = oa->options; +#endif + + lsa.id = po->router_id; + lsa.rt = po->router_id; + lsa.sn = oa->rt ? (oa->rt->lsa.sn + 1) : LSA_INITSEQNO; + u32 dom = oa->areaid; + + body = originate_rt_lsa_body(oa, &lsa.length); + lsasum_calculate(&lsa, body); + oa->rt = lsa_install_new(po, &lsa, dom, body); + ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); +} + +void +update_rt_lsa(struct ospf_area *oa) +{ + struct proto_ospf *po = oa->po; + if ((oa->rt) && ((oa->rt->inst_t + MINLSINTERVAL)) > now) return; /* @@ -244,59 +459,65 @@ originate_rt_lsa(struct ospf_area *oa) * try to do it next tick. */ - OSPF_TRACE(D_EVENTS, "Originating RT_lsa for area %R.", oa->areaid); + originate_rt_lsa(oa); +#ifdef OSPFv3 + originate_prefix_rt_lsa(oa); +#endif - lsa.age = 0; - lsa.id = rtid; - lsa.type = LSA_T_RT; - lsa.rt = rtid; - lsa.options = oa->opt.byte; - if (oa->rt == NULL) - { - lsa.sn = LSA_INITSEQNO; - } - else - { - lsa.sn = oa->rt->lsa.sn + 1; - } - body = originate_rt_lsa_body(oa, &lsa.length); - lsasum_calculate(&lsa, body); - en = lsa_install_new(&lsa, body, oa); - oa->rt = en; - ospf_lsupd_flood(NULL, NULL, &oa->rt->lsa, NULL, oa, 1); schedule_rtcalc(po); oa->origrt = 0; } static void * -originate_net_lsa_body(struct ospf_iface *ifa, u16 * length, +originate_net_lsa_body(struct ospf_iface *ifa, u16 *length, struct proto_ospf *po) { u16 i = 1; struct ospf_neighbor *n; - u32 *body; struct ospf_lsa_net *net; + int nodes = ifa->fadj + 1; - net = mb_alloc(po->proto.pool, sizeof(u32) * (ifa->fadj + 1) + - sizeof(struct ospf_lsa_net)); + net = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_net) + + nodes * sizeof(u32)); + +#ifdef OSPFv2 net->netmask = ipa_mkmask(ifa->iface->addr->pxlen); +#endif + +#ifdef OSPFv3 + /* In OSPFv3, we would like to merge options from Link LSAs of added neighbors */ + struct top_hash_entry *en; + u32 options = 0; +#endif + + net->routers[0] = po->router_id; - body = (u32 *) (net + 1); - i = 1; - *body = po->proto.cf->global->router_id; WALK_LIST(n, ifa->neigh_list) { if (n->state == NEIGHBOR_FULL) { - *(body + i) = n->rid; +#ifdef OSPFv3 + en = ospf_hash_find(po->gr, ifa->iface->index, 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++; } } - *length = i * sizeof(u32) + sizeof(struct ospf_lsa_header) + - sizeof(struct ospf_lsa_net); + 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 @@ -310,12 +531,60 @@ void originate_net_lsa(struct ospf_iface *ifa) { struct proto_ospf *po = ifa->oa->po; - struct ospf_lsa_header lsa; - u32 rtid = po->proto.cf->global->router_id; struct proto *p = &po->proto; + struct ospf_lsa_header lsa; + u32 dom = ifa->oa->areaid; + void *body; - if (ifa->nlsa && ((ifa->nlsa->inst_t + MINLSINTERVAL) > now)) + OSPF_TRACE(D_EVENTS, "Originating network-LSA for iface %s", + ifa->iface->name); + + lsa.age = 0; + lsa.type = LSA_T_NET; + +#ifdef OSPFv2 + lsa.options = ifa->oa->options; + lsa.id = ipa_to_u32(ifa->iface->addr->ip); +#else /* OSPFv3 */ + lsa.id = ifa->iface->index; +#endif + + lsa.rt = po->router_id; + lsa.sn = ifa->net_lsa ? (ifa->net_lsa->lsa.sn + 1) : LSA_INITSEQNO; + + body = originate_net_lsa_body(ifa, &lsa.length, po); + lsasum_calculate(&lsa, body); + ifa->net_lsa = lsa_install_new(po, &lsa, dom, body); + ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); +} + +void +flush_net_lsa(struct ospf_iface *ifa) +{ + struct proto_ospf *po = ifa->oa->po; + struct proto *p = &po->proto; + u32 dom = ifa->oa->areaid; + + if (ifa->net_lsa == NULL) + return; + + OSPF_TRACE(D_EVENTS, "Flushing network-LSA for iface %s", + ifa->iface->name); + ifa->net_lsa->lsa.sn += 1; + ifa->net_lsa->lsa.age = LSA_MAXAGE; + lsasum_calculate(&ifa->net_lsa->lsa, ifa->net_lsa->lsa_body); + ospf_lsupd_flood(po, NULL, NULL, &ifa->net_lsa->lsa, dom, 0); + flush_lsa(ifa->net_lsa, po); + ifa->net_lsa = NULL; +} + +void +update_net_lsa(struct ospf_iface *ifa) +{ + struct proto_ospf *po = ifa->oa->po; + + if (ifa->net_lsa && ((ifa->net_lsa->inst_t + MINLSINTERVAL) > now)) return; /* * It's too early to originate new network LSA. We will @@ -323,233 +592,241 @@ originate_net_lsa(struct ospf_iface *ifa) */ if ((ifa->state != OSPF_IS_DR) || (ifa->fadj == 0)) - { - if (ifa->nlsa == NULL) - return; + { + 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 + } - OSPF_TRACE(D_EVENTS, "Deleting Net lsa for iface \"%s\".", - ifa->iface->name); - ifa->nlsa->lsa.sn += 1; - ifa->nlsa->lsa.age = LSA_MAXAGE; - lsasum_calculate(&ifa->nlsa->lsa, ifa->nlsa->lsa_body); - ospf_lsupd_flood(NULL, NULL, &ifa->nlsa->lsa, NULL, ifa->oa, 0); - s_rem_node(SNODE ifa->nlsa); - if (ifa->nlsa->lsa_body != NULL) - mb_free(ifa->nlsa->lsa_body); - ifa->nlsa->lsa_body = NULL; - ospf_hash_delete(po->gr, ifa->nlsa); - schedule_rtcalc(po); - ifa->nlsa = NULL; - return; - } + schedule_rtcalc(po); + ifa->orignet = 0; +} - OSPF_TRACE(D_EVENTS, "Originating Net lsa for iface \"%s\".", - ifa->iface->name); +#ifdef OSPFv2 - lsa.age = 0; - lsa.id = ipa_to_u32(ifa->iface->addr->ip); - lsa.type = LSA_T_NET; - lsa.rt = rtid; - lsa.options = ifa->oa->opt.byte; - if (ifa->nlsa == NULL) - { - lsa.sn = LSA_INITSEQNO; - } - else - { - lsa.sn = ifa->nlsa->lsa.sn + 1; - } +static inline void * +originate_sum_lsa_body(struct proto_ospf *po, u16 *length, u32 mlen, u32 metric) +{ + struct ospf_lsa_sum *sum = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_sum)); + *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_sum); - body = originate_net_lsa_body(ifa, &lsa.length, po); - lsasum_calculate(&lsa, body); - ifa->nlsa = lsa_install_new(&lsa, body, ifa->oa); - ospf_lsupd_flood(NULL, NULL, &ifa->nlsa->lsa, NULL, ifa->oa, 1); - ifa->orignet = 0; + sum->netmask = ipa_mkmask(mlen); + sum->metric = metric; + + return sum; } +#define originate_sum_net_lsa_body(po,length,fn,metric) \ + originate_sum_lsa_body(po, length, (fn)->pxlen, metric) -static void * -originate_ext_lsa_body(net * n, rte * e, struct proto_ospf *po, - struct ea_list *attrs) +#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 proto *p = &po->proto; - struct ospf_lsa_ext *ext; - struct ospf_lsa_ext_tos *et; - u32 m1 = ea_get_int(attrs, EA_OSPF_METRIC1, LSINFINITY); - u32 m2 = ea_get_int(attrs, EA_OSPF_METRIC2, 10000); - u32 tag = ea_get_int(attrs, EA_OSPF_TAG, 0); - int inas = 0; + struct ospf_lsa_sum *sum = en->lsa_body; + return fn->pxlen != ipa_mklen(sum->netmask); +} - ext = mb_alloc(p->pool, sizeof(struct ospf_lsa_ext) + - sizeof(struct ospf_lsa_ext_tos)); - ext->netmask = ipa_mkmask(n->n.pxlen); +static inline int +check_ext_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en) +{ + struct ospf_lsa_ext *ext = en->lsa_body; + return fn->pxlen != ipa_mklen(ext->netmask); +} - et = (struct ospf_lsa_ext_tos *) (ext + 1); +#else /* OSPFv3 */ - if (m1 != LSINFINITY) - { - et->etm.metric = m1; - et->etm.etos.tos = 0; - et->etm.etos.ebit = 0; - } - else - { - et->etm.metric = m2; - et->etm.etos.tos = 0; - et->etm.etos.ebit = 1; - } - et->tag = tag; - if (!ipa_equal(e->attrs->gw, IPA_NONE)) - { - if (ospf_iface_find((struct proto_ospf *) p, e->attrs->iface) != NULL) - inas = 1; - } +static inline void * +originate_sum_net_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn, u32 metric) +{ + int size = sizeof(struct ospf_lsa_sum_net) + IPV6_PREFIX_SPACE(fn->pxlen); + struct ospf_lsa_sum_net *sum = mb_alloc(po->proto.pool, size); + *length = sizeof(struct ospf_lsa_header) + size; - if (!inas) - et->fwaddr = IPA_NONE; - else - et->fwaddr = e->attrs->gw; - return ext; + sum->metric = metric; + put_ipv6_prefix(sum->prefix, fn->prefix, fn->pxlen, 0, 0); + + return sum; } -/** - * max_ext_lsa - calculate the maximum amount of external networks - * possible for the given prefix length. - * @pxlen: network prefix length - * - * This is a fix for the previous static use of MAXNETS which did - * only work correct if MAXNETS < possible IPs for given prefix. - * This solution is kind of a hack as there can now only be one - * route for /32 type entries but this is better than the crashes - * I did experience whith close together /32 routes originating - * on different hosts. - */ +static inline int +check_sum_net_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en) +{ + 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); +} -int -max_ext_lsa(unsigned pxlen) +static inline int +check_ext_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en) { - int i; - for (i = 1; pxlen < BITS_PER_IP_ADDRESS; pxlen++, i <<= 1) - if (i >= MAXNETS) - return MAXNETS; - return i; + struct ospf_lsa_ext *ext = en->lsa_body; + ip_addr prefix; + int pxlen; + u8 pxopts; + u16 rest; + + lsa_get_ipv6_prefix(ext->rest, &prefix, &pxlen, &pxopts, &rest); + return (fn->pxlen != pxlen) || !ipa_equal(fn->prefix, prefix); } +static inline void * +originate_sum_rt_lsa_body(struct proto_ospf *po, u16 *length, u32 drid, u32 metric, u32 options) +{ + struct ospf_lsa_sum_rt *sum = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_sum_rt)); + *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_sum_rt); + + sum->options = options & OPTIONS_MASK; + sum->metric = metric; + sum->drid = drid; + + return sum; +} + +#endif + void -flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type) +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 rtid = po->proto.cf->global->router_id; + u32 dom = oa->areaid; struct ospf_lsa_header lsa; - int max, i; - struct ospf_lsa_sum *sum = NULL; + void *body; - lsa.rt = rtid; - lsa.type = LSA_T_SUM_NET; - if (type == ORT_ROUTER) - lsa.type = LSA_T_SUM_RT; + OSPF_TRACE(D_EVENTS, "Originating net-summary-LSA for %I/%d (metric %d)", + fn->prefix, fn->pxlen, metric); - max = max_ext_lsa(fn->pxlen); + /* 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; + lsa.sn = LSA_INITSEQNO; - for (i = 0; i < max; i++) + if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL) { - lsa.id = ipa_to_u32(fn->prefix) + i; - if ((en = ospf_hash_find_header(po->gr, oa->areaid, &lsa)) != NULL) - { - sum = en->lsa_body; - if ((type == ORT_ROUTER) || (fn->pxlen == ipa_mklen(sum->netmask))) + if (check_sum_net_lsaid_collision(fn, en)) { - en->lsa.age = LSA_MAXAGE; - en->lsa.sn = LSA_MAXSEQNO; - lsasum_calculate(&en->lsa, sum); - OSPF_TRACE(D_EVENTS, "Flushing summary lsa. (id=%R, type=%d)", - en->lsa.id, en->lsa.type); - ospf_lsupd_flood(NULL, NULL, &en->lsa, NULL, oa, 1); - if (can_flush_lsa(po)) flush_lsa(en, po); - break; + log(L_ERR, "%s: LSAID collision for %I/%d", + p->name, fn->prefix, fn->pxlen); + return; } - } + + lsa.sn = en->lsa.sn + 1; } + + body = originate_sum_net_lsa_body(po, &lsa.length, fn, metric); + lsasum_calculate(&lsa, body); + en = lsa_install_new(po, &lsa, dom, body); + ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); } void -originate_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type, int metric) +originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32 options) { struct proto_ospf *po = oa->po; struct proto *p = &po->proto; struct top_hash_entry *en; - u32 rtid = po->proto.cf->global->router_id; + u32 dom = oa->areaid; struct ospf_lsa_header lsa; - int i, max, mlen = fn->pxlen, free = 0; - u32 freeid = 0xFFFF; - struct ospf_lsa_sum *sum = NULL; - union ospf_lsa_sum_tm *tm; - lsa.type = LSA_T_SUM_NET; + void *body; - if (type == ORT_ROUTER) - { - lsa.type = LSA_T_SUM_RT; - mlen = 0; - } + OSPF_TRACE(D_EVENTS, "Originating rt-summary-LSA for %R (metric %d)", + lsa.id, metric); lsa.age = 0; - lsa.rt = rtid; +#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 = ipa_to_rid(fn->prefix); + lsa.rt = po->router_id; lsa.sn = LSA_INITSEQNO; - lsa.length = sizeof(struct ospf_lsa_sum) + sizeof(union ospf_lsa_sum_tm) + - sizeof(struct ospf_lsa_header); - lsa.options = oa->opt.byte; - max = max_ext_lsa(fn->pxlen); - for (i = 0; i < max; i++) - { - lsa.id = ipa_to_u32(fn->prefix) + i; - if ((en = ospf_hash_find_header(po->gr, oa->areaid, &lsa)) == NULL) + if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL) + lsa.sn = en->lsa.sn + 1; + + body = originate_sum_rt_lsa_body(po, &lsa.length, lsa.id, metric, options); + lsasum_calculate(&lsa, body); + en = lsa_install_new(po, &lsa, dom, body); + ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); +} + + +void +originate_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type, int metric, u32 options) +{ + if (type == ORT_NET) + originate_sum_net_lsa(oa, fn, metric); + else + originate_sum_rt_lsa(oa, fn, metric, options); +} + + +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) { - if (!free) - { - freeid = lsa.id; - free = 1; - } + lsa.id = fibnode_to_lsaid(po, fn); + lsa.type = LSA_T_SUM_NET; } - else + else { - sum = en->lsa_body; - if (mlen == ipa_mklen(sum->netmask)) - { - tm = (union ospf_lsa_sum_tm *) (sum + 1); - if (tm->metric == (unsigned) metric) return; /* No reason for origination */ - lsa.sn = en->lsa.sn + 1; - freeid = en->lsa.id; - free = 1; - break; - } + /* 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; } - } - if(!free) - { - log("%s: got more routes for one /%d network then %d, ignoring", p->name, - fn->pxlen, max); - return; - } - lsa.id = freeid; - - OSPF_TRACE(D_EVENTS, "Originating summary (type %d) lsa for %I/%d (met %d).", lsa.type, fn->prefix, - fn->pxlen, metric); + if ((en = ospf_hash_find_header(po->gr, oa->areaid, &lsa)) != NULL) + { + 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; + } - sum = mb_alloc(p->pool, sizeof(struct ospf_lsa_sum) + sizeof(union ospf_lsa_sum_tm)); - sum->netmask = ipa_mkmask(mlen); - tm = (union ospf_lsa_sum_tm *) (sum + 1); - tm->metric = metric; - tm->tos.tos = 0; + struct ospf_lsa_sum *sum = en->lsa_body; + en->lsa.age = LSA_MAXAGE; + en->lsa.sn = LSA_MAXSEQNO; + lsasum_calculate(&en->lsa, sum); - lsasum_calculate(&lsa, sum); - en = lsa_install_new(&lsa, sum, oa); - ospf_lsupd_flood(NULL, NULL, &en->lsa, NULL, oa, 1); + OSPF_TRACE(D_EVENTS, "Flushing summary-LSA (id=%R, type=%d)", + en->lsa.id, en->lsa.type); + ospf_lsupd_flood(po, NULL, NULL, &en->lsa, oa->areaid, 1); + if (can_flush_lsa(po)) flush_lsa(en, po); + } } + void check_sum_lsa(struct proto_ospf *po, ort *nf, int dest) { @@ -572,7 +849,7 @@ check_sum_lsa(struct proto_ospf *po, ort *nf, int dest) flush = 0; if ((nf->n.metric1 >= LSINFINITY) || (nf->n.type > RTS_OSPF_IA)) flush = 1; - if ((dest == ORT_ROUTER) && (!(nf->n.capa & ORTA_ASBR))) + if ((dest == ORT_ROUTER) && (!(nf->n.options & ORTA_ASBR))) flush = 1; if ((!nf->n.oa) || (nf->n.oa->areaid == oa->areaid)) flush = 1; @@ -591,21 +868,82 @@ check_sum_lsa(struct proto_ospf *po, ort *nf, int dest) mlen = nf->fn.pxlen; ip = ipa_and(nf->fn.prefix, ipa_mkmask(mlen)); - if ((oa == po->backbone) && (nf->n.type == RTS_OSPF_IA)) flush = 1; /* Only intra-area can go to the backbone */ + if ((oa == po->backbone) && (nf->n.type == RTS_OSPF_IA)) + flush = 1; /* Only intra-area can go to the backbone */ - if ((!flush) && (dest == ORT_NET) && fib_route(&nf->n.oa->net_fib, ip, mlen)) /* The route fits into area networks */ + if ((!flush) && (dest == ORT_NET) && fib_route(&nf->n.oa->net_fib, ip, mlen)) { + /* The route fits into area networks */ flush = 1; if ((nf->n.oa == po->backbone) && (oa->trcap)) flush = 0; } - if(flush) + if (flush) flush_sum_lsa(oa, &nf->fn, dest); else - originate_sum_lsa(oa, &nf->fn, dest, nf->n.metric1); + originate_sum_lsa(oa, &nf->fn, dest, nf->n.metric1, nf->n.options); } } + +static void * +originate_ext_lsa_body(net *n, rte *e, u16 *length, struct proto_ospf *po, + struct ea_list *attrs) +{ + struct proto *p = &po->proto; + struct ospf_lsa_ext *ext; + u32 m1 = ea_get_int(attrs, EA_OSPF_METRIC1, LSINFINITY); + u32 m2 = ea_get_int(attrs, EA_OSPF_METRIC2, 10000); + u32 tag = ea_get_int(attrs, EA_OSPF_TAG, 0); + int gw = 0; + int size = sizeof(struct ospf_lsa_ext); + u32 *buf; + + if ((e->attrs->dest == RTD_ROUTER) && + !ipa_equal(e->attrs->gw, IPA_NONE) && + !ipa_has_link_scope(e->attrs->gw) && + (ospf_iface_find((struct proto_ospf *) p, e->attrs->iface) != NULL)) + gw = 1; + +#ifdef OSPFv3 + size += IPV6_PREFIX_SPACE(n->n.pxlen); + + if (gw) + size += 16; + + if (tag) + size += 4; +#endif + + ext = mb_alloc(p->pool, size); + *length = sizeof(struct ospf_lsa_header) + size; + + ext->metric = (m1 != LSINFINITY) ? m1 : (m2 | LSA_EXT_EBIT); + +#ifdef OSPFv2 + ext->netmask = ipa_mkmask(n->n.pxlen); + ext->fwaddr = gw ? e->attrs->gw : IPA_NONE; + ext->tag = tag; +#else /* OSPFv3 */ + buf = ext->rest; + buf = put_ipv6_prefix(buf, n->n.prefix, n->n.pxlen, 0, 0); + + if (gw) + { + ext->metric |= LSA_EXT_FBIT; + buf = put_ipv6_addr(buf, e->attrs->gw); + } + + if (tag) + { + ext->metric |= LSA_EXT_TBIT; + *buf++ = tag; + } +#endif + + return ext; +} + /** * originate_ext_lsa - new route received from nest and filters * @n: network prefix and mask @@ -625,75 +963,399 @@ void originate_ext_lsa(net * n, rte * e, struct proto_ospf *po, struct ea_list *attrs) { + struct proto *p = &po->proto; + struct fib_node *fn = &n->n; struct ospf_lsa_header lsa; - u32 rtid = po->proto.cf->global->router_id; struct top_hash_entry *en = NULL; - void *body = NULL; - struct proto *p = &po->proto; + void *body; struct ospf_area *oa; - struct ospf_lsa_ext *ext1, *ext2; - int i, max; - OSPF_TRACE(D_EVENTS, "Originating Ext lsa for %I/%d.", n->n.prefix, - n->n.pxlen); + OSPF_TRACE(D_EVENTS, "Originating AS-external-LSA for %I/%d", + fn->prefix, fn->pxlen); lsa.age = 0; - lsa.id = ipa_to_u32(n->n.prefix); +#ifdef OSPFv2 + lsa.options = 0; /* or oa->options ? */ +#endif lsa.type = LSA_T_EXT; - lsa.rt = rtid; + lsa.id = fibnode_to_lsaid(po, fn); + lsa.rt = po->router_id; lsa.sn = LSA_INITSEQNO; - lsa.options = 0; - body = originate_ext_lsa_body(n, e, po, attrs); - lsa.length = sizeof(struct ospf_lsa_ext) + sizeof(struct ospf_lsa_ext_tos) + - sizeof(struct ospf_lsa_header); - ext1 = body; - max = max_ext_lsa(n->n.pxlen); + if ((en = ospf_hash_find_header(po->gr, 0, &lsa)) != NULL) + { + if (check_ext_lsaid_collision(fn, en)) + { + log(L_ERR, "%s: LSAID collision for %I/%d", + p->name, fn->prefix, fn->pxlen); + return; + } + + lsa.sn = en->lsa.sn + 1; + } + + body = originate_ext_lsa_body(n, e, &lsa.length, po, attrs); + lsasum_calculate(&lsa, body); - for (i = 0; i < max; i++) + en = lsa_install_new(po, &lsa, 0, body); + ospf_lsupd_flood(po, NULL, NULL, &lsa, 0, 1); + + if (po->ebit == 0) { - if ((en = ospf_hash_find_header(po->gr, 0 , &lsa)) != NULL) + po->ebit = 1; + WALK_LIST(oa, po->area_list) { - ext2 = en->lsa_body; - if (ipa_compare(ext1->netmask, ext2->netmask) != 0) - lsa.id++; - else - break; + schedule_rt_lsa(oa); } - else - break; } +} - if (i == max) - { - log("%s: got more routes for one /%d network then %d, ignoring", p->name, - n->n.pxlen, max); - mb_free(body); +void +flush_ext_lsa(net *n, struct proto_ospf *po) +{ + struct proto *p = &po->proto; + struct fib_node *fn = &n->n; + struct ospf_area *oa; + struct top_hash_entry *en; + + OSPF_TRACE(D_EVENTS, "Flushing AS-external-LSA for %I/%d", + fn->prefix, fn->pxlen); + + u32 lsaid = fibnode_to_lsaid(po, fn); + + if (en = ospf_hash_find(po->gr, 0, lsaid, po->router_id, LSA_T_EXT)) + { + if (check_ext_lsaid_collision(fn, en)) + { + log(L_ERR, "%s: LSAID collision for %I/%d", + p->name, fn->prefix, fn->pxlen); + return; + } + + ospf_lsupd_flush_nlsa(po, en); + } +} + + +#ifdef OSPFv3 + +static void * +originate_link_lsa_body(struct ospf_iface *ifa, u16 *length) +{ + 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)); + ll->options = ifa->oa->options | (ifa->priority << 24); + ll->lladdr = ifa->lladdr; + 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; + + 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++; + } + + ll = po->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) +{ + struct ospf_lsa_header lsa; + struct proto_ospf *po = ifa->oa->po; + struct proto *p = &po->proto; + void *body; + + /* FIXME check for vlink and skip that? */ + OSPF_TRACE(D_EVENTS, "Originating link-LSA for iface %s", ifa->iface->name); + + lsa.age = 0; + lsa.type = LSA_T_LINK; + lsa.id = ifa->iface->index; + lsa.rt = po->router_id; + lsa.sn = ifa->link_lsa ? (ifa->link_lsa->lsa.sn + 1) : LSA_INITSEQNO; + u32 dom = ifa->iface->index; + + body = originate_link_lsa_body(ifa, &lsa.length); + lsasum_calculate(&lsa, body); + ifa->link_lsa = lsa_install_new(po, &lsa, dom, body); + ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); + + /* Just to be sure to not forget on our link LSA */ + if (ifa->state == OSPF_IS_DR) + schedule_net_lsa(ifa); +} + +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 void * +originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length) +{ + struct proto_ospf *po = oa->po; + struct ospf_iface *ifa; + struct ospf_lsa_prefix *lp; + int net_lsa; + int i = 0; + u8 flags; + + ASSERT(po->lsab_used == 0); + lp = lsab_allocz(po, sizeof(struct ospf_lsa_prefix)); + lp->ref_type = LSA_T_RT; + lp->ref_id = 0; + lp->ref_rt = po->router_id; + lp = NULL; /* buffer might be reallocated later */ + + WALK_LIST(ifa, po->iface_list) + { + if ((ifa->oa != oa) || (ifa->state == OSPF_IS_DOWN)) + continue; + + if ((ifa->type == OSPF_IT_BCAST) || + (ifa->type == OSPF_IT_NBMA)) + net_lsa = bcast_net_active(ifa); + else + net_lsa = 0; + + struct ifa *a; + WALK_LIST(a, ifa->iface->addrs) + { + if (((a->pxlen < MAX_PREFIX_LENGTH) && net_lsa) || + (a->flags & IA_SECONDARY) || + (a->flags & IA_UNNUMBERED) || + (a->scope <= SCOPE_LINK) || + configured_stubnet(oa, a)) + 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, ifa->cost); + i++; + } } + + /* FIXME Handle vlinks? see RFC5340, page 38 */ + + struct ospf_stubnet_config *sn; + WALK_LIST(sn, oa->ac->stubnet_list) + if (!sn->hidden) + { + flags = (sn->px.len < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA; + put_ipv6_prefix(lsab_alloc(po, IPV6_PREFIX_SPACE(sn->px.len)), + sn->px.addr, sn->px.len, flags, sn->cost); + i++; + } + + lp = po->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) +{ + struct proto_ospf *po = oa->po; + struct proto *p = &po->proto; + struct ospf_lsa_header lsa; + void *body; + + OSPF_TRACE(D_EVENTS, "Originating router prefix-LSA for area %R", oa->areaid); + + lsa.age = 0; + lsa.type = LSA_T_PREFIX; + lsa.id = 0; + lsa.rt = po->router_id; + lsa.sn = oa->pxr_lsa ? (oa->pxr_lsa->lsa.sn + 1) : LSA_INITSEQNO; + u32 dom = oa->areaid; + + body = originate_prefix_rt_lsa_body(oa, &lsa.length); lsasum_calculate(&lsa, body); - WALK_LIST(oa, po->area_list) - { - if (!oa->stub) + oa->pxr_lsa = lsa_install_new(po, &lsa, dom, body); + ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); +} + + +static inline int +prefix_space(u32 *buf) +{ + int pxl = *buf >> 24; + return IPV6_PREFIX_SPACE(pxl); +} + +static inline int +prefix_same(u32 *b1, u32 *b2) +{ + int pxl1 = *b1 >> 24; + int pxl2 = *b2 >> 24; + int pxs, i; + + if (pxl1 != pxl2) + return 0; + + pxs = IPV6_PREFIX_WORDS(pxl1); + for (i = 1; i < pxs; i++) + if (b1[i] != b2[i]) + return 0; + + return 1; +} + +static inline u32 * +prefix_advance(u32 *buf) +{ + int pxl = *buf >> 24; + return buf + IPV6_PREFIX_WORDS(pxl); +} + +static void +add_prefix(struct proto_ospf *po, u32 *px, int offset, int *pxc) +{ + u32 *pxl = lsab_offset(po, offset); + int i; + for (i = 0; i < *pxc; i++) { - en = lsa_install_new(&lsa, body, oa); - ospf_lsupd_flood(NULL, NULL, &en->lsa, NULL, oa, 1); - body = originate_ext_lsa_body(n, e, po, attrs); + if (prefix_same(px, pxl)) + { + /* Options should be logically OR'ed together */ + *pxl |= *px; + return; + } + pxl = prefix_advance(pxl); } - } - mb_free(body); - if (po->ebit == 0) - { - po->ebit = 1; - WALK_LIST(oa, po->area_list) + ASSERT(pxl == lsab_end(po)); + + int pxspace = prefix_space(px); + pxl = lsab_alloc(po, pxspace); + memcpy(pxl, px, pxspace); + (*pxc)++; +} + +static void +add_link_lsa(struct proto_ospf *po, struct top_hash_entry *en, int offset, int *pxc) +{ + struct ospf_lsa_link *ll = en->lsa_body; + u32 *pxb = ll->rest; + int j; + + for (j = 0; j < ll->pxcount; j++) { - schedule_rt_lsa(oa); + add_prefix(po, pxb, offset, pxc); + pxb = prefix_advance(pxb); } - } } + +static void * +originate_prefix_net_lsa_body(struct ospf_iface *ifa, u16 *length) +{ + 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)); + lp->ref_type = LSA_T_NET; + lp->ref_id = ifa->net_lsa->lsa.id; + lp->ref_rt = po->router_id; + lp = NULL; /* buffer might be reallocated later */ + + pxc = 0; + offset = po->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); + + WALK_LIST(n, ifa->neigh_list) + if ((n->state == NEIGHBOR_FULL) && + (en = ospf_hash_find(po->gr, ifa->iface->index, n->iface_id, n->rid, LSA_T_LINK))) + add_link_lsa(po, en, offset, &pxc); + + lp = po->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) +{ + struct proto_ospf *po = ifa->oa->po; + struct proto *p = &po->proto; + struct ospf_lsa_header lsa; + void *body; + + OSPF_TRACE(D_EVENTS, "Originating network prefix-LSA for iface %s", + ifa->iface->name); + + lsa.age = 0; + lsa.type = LSA_T_PREFIX; + lsa.id = ifa->iface->index; + lsa.rt = po->router_id; + lsa.sn = ifa->pxn_lsa ? (ifa->pxn_lsa->lsa.sn + 1) : LSA_INITSEQNO; + u32 dom = ifa->oa->areaid; + + body = originate_prefix_net_lsa_body(ifa, &lsa.length); + lsasum_calculate(&lsa, body); + ifa->pxn_lsa = lsa_install_new(po, &lsa, dom, body); + ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1); +} + +void +flush_prefix_net_lsa(struct ospf_iface *ifa) +{ + struct proto_ospf *po = ifa->oa->po; + struct proto *p = &po->proto; + struct top_hash_entry *en = ifa->pxn_lsa; + u32 dom = ifa->oa->areaid; + + if (en == NULL) + return; + + OSPF_TRACE(D_EVENTS, "Flushing network prefix-LSA for iface %s", + ifa->iface->name); + + 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; +} + + +#endif + + static void ospf_top_ht_alloc(struct top_graph *f) { @@ -730,24 +1392,34 @@ ospf_top_hash_u32(u32 a) } static inline unsigned -ospf_top_hash(struct top_graph *f, u32 areaid, u32 lsaid, u32 rtrid, u32 type) -{ -#if 1 /* Dirty patch to make rt table calculation work. */ - return (ospf_top_hash_u32(lsaid) + - ospf_top_hash_u32((type == - LSA_T_NET) ? lsaid : rtrid) + type + - (type == LSA_T_EXT ? 0 : areaid)) & f->hash_mask; -#else +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. + In OSPFv3, we don't know LSA ID when looking for router LSAs. + 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 + type + domain) & f->hash_mask; + + /* return (ospf_top_hash_u32(lsaid) + ospf_top_hash_u32(rtrid) + type + areaid) & f->hash_mask; -#endif + */ } /** * ospf_top_new - allocated new topology database - * @p: current instance of OSPF + * @p: current instance of ospf * - * This dynamically hashed structure is often used for keeping LSAs. Mainly + * this dynamically hashed structure is often used for keeping lsas. mainly * its used in @ospf_area structure. */ struct top_graph * @@ -781,7 +1453,7 @@ ospf_top_rehash(struct top_graph *f, int step) oldn = f->hash_size; oldt = f->hash_table; - DBG("Re-hashing topology hash from order %d to %d\n", f->hash_order, + DBG("re-hashing topology hash from order %d to %d\n", f->hash_order, f->hash_order + step); f->hash_order += step; ospf_top_ht_alloc(f); @@ -793,7 +1465,7 @@ ospf_top_rehash(struct top_graph *f, int step) while (e) { x = e->next; - n = newt + ospf_top_hash(f, e->oa->areaid, 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; @@ -802,65 +1474,137 @@ 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->index; + + 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 areaid, struct ospf_lsa_header *h) +ospf_hash_find_header(struct top_graph *f, u32 domain, struct ospf_lsa_header *h) { - return ospf_hash_find(f, areaid, h->id, h->rt, h->type); + return ospf_hash_find(f, domain, h->id, h->rt, h->type); } struct top_hash_entry * -ospf_hash_get_header(struct top_graph *f, struct ospf_area *oa, struct ospf_lsa_header *h) +ospf_hash_get_header(struct top_graph *f, u32 domain, struct ospf_lsa_header *h) { - return ospf_hash_get(f, oa, h->id, h->rt, h->type); + return ospf_hash_get(f, domain, h->id, h->rt, h->type); } struct top_hash_entry * -ospf_hash_find(struct top_graph *f, u32 areaid, u32 lsa, u32 rtr, u32 type) +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)]; - e = f->hash_table[ospf_top_hash(f, areaid, lsa, rtr, type)]; + while (e && (e->lsa.id != lsa || e->lsa.type != type || e->lsa.rt != rtr || e->domain != domain)) + e = e->next; - /* Dirty patch to make rt table calculation work. */ - if (type == LSA_T_NET) - { - while (e && (e->lsa.id != lsa || e->lsa.type != LSA_T_NET || e->oa->areaid != areaid)) - e = e->next; - } - else if (type == LSA_T_EXT) - { - while (e && (e->lsa.id != lsa || e->lsa.type != type || e->lsa.rt != rtr)) - e = e->next; - } - else - { - while (e && (e->lsa.id != lsa || e->lsa.type != type || e->lsa.rt != rtr || e->oa->areaid != areaid)) + 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)) + e = e->next; + + return e; +} + +#endif + + +#ifdef OSPFv3 + +/* In OSPFv3, usually we don't know LSA ID when looking for router + LSAs. We return matching LSA with smallest LSA ID. */ +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)]; + + while (e) + { + 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; - } + } + return rv; +} + +static inline struct top_hash_entry * +find_matching_rt(struct top_hash_entry *e, u32 domain, u32 rtr) +{ + while (e && (e->lsa.rt != rtr || e->lsa.type != LSA_T_RT || e->domain != domain)) + e = e->next; return e; } struct top_hash_entry * -ospf_hash_get(struct top_graph *f, struct ospf_area *oa, u32 lsa, u32 rtr, u32 type) +ospf_hash_find_rt_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); +} + +struct top_hash_entry * +ospf_hash_find_rt_next(struct top_hash_entry *e) +{ + return find_matching_rt(e->next, e->domain, e->lsa.rt); +} + +#endif + + +struct top_hash_entry * +ospf_hash_get(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type) { struct top_hash_entry **ee; struct top_hash_entry *e; - u32 nareaid = (type == LSA_T_EXT ? 0 : oa->areaid); - ee = f->hash_table + ospf_top_hash(f, nareaid, lsa, rtr, type); + ee = f->hash_table + ospf_top_hash(f, domain, lsa, rtr, type); e = *ee; - if (type == LSA_T_EXT) - { - while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || e->lsa.type != type)) - e = e->next; - } - else - { - while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || e->lsa.type != type || e->oa->areaid != nareaid)) - e = e->next; - } + while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || e->lsa.type != type || e->domain != domain)) + e = e->next; if (e) return e; @@ -876,7 +1620,7 @@ ospf_hash_get(struct top_graph *f, struct ospf_area *oa, u32 lsa, u32 rtr, u32 t e->lsa.type = type; e->lsa_body = NULL; e->nhi = NULL; - e->oa = oa; + e->domain = domain; e->next = *ee; *ee = e; if (f->hash_entries++ > f->hash_entries_max) @@ -888,7 +1632,7 @@ void ospf_hash_delete(struct top_graph *f, struct top_hash_entry *e) { struct top_hash_entry **ee = f->hash_table + - ospf_top_hash(f, e->oa->areaid, e->lsa.id, e->lsa.rt, e->lsa.type); + ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa.type); while (*ee) { @@ -908,6 +1652,7 @@ ospf_hash_delete(struct top_graph *f, struct top_hash_entry *e) static void ospf_dump_lsa(struct top_hash_entry *he, struct proto *p) { + /* struct ospf_lsa_rt *rt = NULL; struct ospf_lsa_rt_link *rr = NULL; struct ospf_lsa_net *ln = NULL; @@ -916,7 +1661,8 @@ ospf_dump_lsa(struct top_hash_entry *he, struct proto *p) OSPF_TRACE(D_EVENTS, "- %1x %-1R %-1R %4u 0x%08x 0x%04x %-1R", he->lsa.type, he->lsa.id, he->lsa.rt, he->lsa.age, he->lsa.sn, - he->lsa.checksum, he->oa ? he->oa->areaid : 0 ); + he->lsa.checksum, he->domain); + switch (he->lsa.type) { @@ -924,7 +1670,7 @@ ospf_dump_lsa(struct top_hash_entry *he, struct proto *p) rt = he->lsa_body; rr = (struct ospf_lsa_rt_link *) (rt + 1); - for (i = 0; i < rt->links; i++) + for (i = 0; i < lsa_rt_items(&he->lsa); i++) OSPF_TRACE(D_EVENTS, " - %1x %-1R %-1R %5u", rr[i].type, rr[i].id, rr[i].data, rr[i].metric); break; @@ -932,16 +1678,15 @@ ospf_dump_lsa(struct top_hash_entry *he, struct proto *p) case LSA_T_NET: ln = he->lsa_body; rts = (u32 *) (ln + 1); - max = (he->lsa.length - sizeof(struct ospf_lsa_header) - - sizeof(struct ospf_lsa_net)) / sizeof(u32); - for (i = 0; i < max; i++) + for (i = 0; i < lsa_net_items(&he->lsa); i++) OSPF_TRACE(D_EVENTS, " - %-1R", rts[i]); break; default: break; } + */ } void diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h index 21e545e..f6ba019 100644 --- a/proto/ospf/topology.h +++ b/proto/ospf/topology.h @@ -13,12 +13,11 @@ struct top_hash_entry { /* Index for fast mapping (type,rtrid,LSid)->vertex */ snode n; node cn; /* For adding into list of candidates - * in intra-area routing table - * calculation - */ + in intra-area routing table calculation */ struct top_hash_entry *next; /* Next in hash chain */ struct ospf_lsa_header lsa; - struct ospf_area *oa; + 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 */ ip_addr nh; /* Next hop */ @@ -48,26 +47,45 @@ struct top_graph struct top_graph *ospf_top_new(pool *); void ospf_top_free(struct top_graph *); void ospf_top_dump(struct top_graph *, struct proto *); -struct top_hash_entry *ospf_hash_find_header(struct top_graph *f, u32 areaid, +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, struct ospf_area *oa, +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 areaid, u32 lsa, u32 rtr, + +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 *, struct ospf_area *oa, u32 lsa, u32 rtr, +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); int max_ext_lsa(unsigned pxlen); void originate_ext_lsa(net * n, rte * e, struct proto_ospf *po, struct ea_list *attrs); +void flush_ext_lsa(net *n, struct proto_ospf *po); void check_sum_lsa(struct proto_ospf *po, ort *nf, int); -void originate_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type, int metric); +void originate_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type, int metric, u32 options); void flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type); +#ifdef OSPFv2 +struct top_hash_entry * ospf_hash_find_net(struct top_graph *f, u32 domain, u32 lsa); + +static inline struct top_hash_entry * +ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr) +{ + return ospf_hash_find(f, domain, rtr, rtr, LSA_T_RT); +} +#else /* OSPFv3 */ +struct top_hash_entry * ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr); +struct top_hash_entry * ospf_hash_find_rt_first(struct top_graph *f, u32 domain, u32 rtr); +struct top_hash_entry * ospf_hash_find_rt_next(struct top_hash_entry *e); +#endif #endif /* _BIRD_OSPF_TOPOLOGY_H_ */ diff --git a/proto/rip/rip.c b/proto/rip/rip.c index c655cc3..f9a160e 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -684,7 +684,7 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ DBG( "Doing multicasts!\n" ); rif->sock = sk_new( p->pool ); - rif->sock->type = rif->multicast?SK_UDP_MC:SK_UDP; + rif->sock->type = SK_UDP; rif->sock->sport = P_CF->port; rif->sock->rx_hook = rip_rx; rif->sock->data = rif; @@ -721,19 +721,37 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ if (!ipa_nonzero(rif->sock->daddr)) { if (rif->iface) log( L_WARN "%s: interface %s is too strange for me", p->name, rif->iface->name ); - } else if (sk_open(rif->sock)<0) { - log( L_ERR "%s: could not create socket for %s", p->name, rif->iface ? rif->iface->name : "(dummy)" ); - if (rif->iface) { - rfree(rif->sock); - mb_free(rif); - return NULL; - } - /* On dummy, we just return non-working socket, so that user gets error every time anyone requests table */ + } else { + + if (sk_open(rif->sock)<0) + goto err; + + if (rif->multicast) + { + if (sk_setup_multicast(rif->sock) < 0) + goto err; + if (sk_join_group(rif->sock, rif->sock->daddr) < 0) + goto err; + } + else + { + if (sk_set_broadcast(rif->sock, 1) < 0) + goto err; + } } TRACE(D_EVENTS, "Listening on %s, port %d, mode %s (%I)", rif->iface ? rif->iface->name : "(dummy)", P_CF->port, rif->multicast ? "multicast" : "broadcast", rif->sock->daddr ); return rif; + + err: + log( L_ERR "%s: could not create socket for %s", p->name, rif->iface ? rif->iface->name : "(dummy)" ); + if (rif->iface) { + rfree(rif->sock); + mb_free(rif); + return NULL; + } + /* On dummy, we just return non-working socket, so that user gets error every time anyone requests table */ } static void diff --git a/sysdep/bsd/sysio.h b/sysdep/bsd/sysio.h index b0ec456..80f8f94 100644 --- a/sysdep/bsd/sysio.h +++ b/sysdep/bsd/sysio.h @@ -27,48 +27,55 @@ set_inaddr(struct in_addr * ia, ip_addr a) } static inline char * -sysio_mcast_setup(sock * s) +sysio_setup_multicast(sock *s) { - u8 zero = 0; - u8 one = 1; + struct in_addr m; + u8 zero = 0; + u8 ttl = s->ttl; - if (ipa_nonzero(s->daddr)) { + if (setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) + return "IP_MULTICAST_LOOP"; - if (setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) - return "IP_MULTICAST_LOOP"; + if (setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) + return "IP_MULTICAST_TTL"; + + /* This defines where should we send _outgoing_ multicasts */ + set_inaddr(&m, s->iface->addr->ip); + if (setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_IF, &m, sizeof(m)) < 0) + return "IP_MULTICAST_IF"; - if (setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_TTL, &one, sizeof(one)) < 0) - return "IP_MULTICAST_TTL"; - } return NULL; } static inline char * -sysio_mcast_join(sock * s) +sysio_join_group(sock *s, ip_addr maddr) { - struct in_addr m; struct ip_mreq mreq; - char *err; - - set_inaddr(&m, s->iface->addr->ip ); bzero(&mreq, sizeof(mreq)); set_inaddr(&mreq.imr_interface, s->iface->addr->ip); - set_inaddr(&mreq.imr_multiaddr, s->daddr); + set_inaddr(&mreq.imr_multiaddr, maddr); /* And this one sets interface for _receiving_ multicasts from */ - if (ipa_nonzero(s->daddr) && - setsockopt(s->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + if (setsockopt(s->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) return "IP_ADD_MEMBERSHIP"; + return NULL; +} - /* This defines where should we send _outgoing_ multicasts */ - if (ipa_nonzero(s->daddr) && setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_IF, &m, sizeof(m)) < 0) - return "IP_MULTICAST_IF"; +static inline char * +sysio_leave_group(sock *s, ip_addr maddr) +{ + struct ip_mreq mreq; - if (err = sysio_mcast_setup(s)) - return err; + bzero(&mreq, sizeof(mreq)); + set_inaddr(&mreq.imr_interface, s->iface->addr->ip); + set_inaddr(&mreq.imr_multiaddr, maddr); + + /* And this one sets interface for _receiving_ multicasts from */ + if (setsockopt(s->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + return "IP_DROP_MEMBERSHIP"; return NULL; } diff --git a/sysdep/linux/netlink/netlink.c b/sysdep/linux/netlink/netlink.c index 03d4388..f45fe15 100644 --- a/sysdep/linux/netlink/netlink.c +++ b/sysdep/linux/netlink/netlink.c @@ -390,7 +390,6 @@ nl_parse_addr(struct nlmsghdr *h) else { ip_addr netmask = ipa_mkmask(ifa.pxlen); - ip_addr xbrd; ifa.prefix = ipa_and(ifa.ip, netmask); ifa.brd = ipa_or(ifa.ip, ipa_not(netmask)); #ifndef IPV6 @@ -398,6 +397,7 @@ nl_parse_addr(struct nlmsghdr *h) ifa.opposite = ipa_opposite(ifa.ip, i->ifa_prefixlen); if ((ifi->flags & IF_BROADCAST) && a[IFA_BROADCAST]) { + ip_addr xbrd; memcpy(&xbrd, RTA_DATA(a[IFA_BROADCAST]), sizeof(xbrd)); ipa_ntoh(xbrd); if (ipa_equal(xbrd, ifa.prefix) || ipa_equal(xbrd, ifa.brd)) @@ -472,6 +472,9 @@ krt_capable(rte *e) switch (a->dest) { case RTD_ROUTER: + if (ipa_has_link_scope(a->gw) && (a->iface == NULL)) + return 0; + case RTD_DEVICE: case RTD_BLACKHOLE: case RTD_UNREACHABLE: @@ -514,6 +517,11 @@ nl_send_route(struct krt_proto *p, rte *e, int new) case RTD_ROUTER: r.r.rtm_type = RTN_UNICAST; nl_add_attr_ipa(&r.h, sizeof(r), RTA_GATEWAY, a->gw); + + /* a->iface != NULL checked in krt_capable() */ + if (ipa_has_link_scope(a->gw)) + nl_add_attr_u32(&r.h, sizeof(r), RTA_OIF, a->iface->index); + break; case RTD_DEVICE: if (!a->iface) @@ -678,6 +686,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) } if (a[RTA_GATEWAY]) { + struct iface *ifa = if_find_by_index(oif); neighbor *ng; ra.dest = RTD_ROUTER; memcpy(&ra.gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ra.gw)); @@ -696,15 +705,19 @@ nl_parse_route(struct nlmsghdr *h, int scan) } else { - /* standard route */ - ng = neigh_find(&p->p, &ra.gw, 0); + ng = neigh_find2(&p->p, &ra.gw, ifa, 0); if (ng && ng->scope) - ra.iface = ng->iface; + { + if (ng->iface != ifa) + log(L_WARN "KRT: Route with unexpected iface for %I/%d", net->n.prefix, net->n.pxlen); + ra.iface = ng->iface; + } else { log(L_WARN "Kernel told us to use non-neighbor %I for %I/%d", ra.gw, net->n.prefix, net->n.pxlen); return; } + } } else diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index 2fa5f0a..70d35cc 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -38,27 +38,33 @@ set_inaddr(struct in_addr *ia, ip_addr a) * ways. Horrible. */ -static inline char *sysio_mcast_setup(sock *s) + +#if defined(CONFIG_LINUX_MC_MREQ) || defined(CONFIG_LINUX_MC_MREQ_BIND) +/* + * Older kernels support only struct mreq which matches interfaces by their + * addresses and thus fails on unnumbered devices. On newer 2.0 kernels + * we can use SO_BINDTODEVICE to circumvent this problem. + */ + +#define MREQ_IFA struct in_addr +#define MREQ_GRP struct ip_mreq +static inline void fill_mreq_ifa(struct in_addr *m, struct iface *ifa, UNUSED ip_addr maddr) { - int zero = 0; + set_inaddr(m, ifa->addr->ip); +} - if (ipa_nonzero(s->daddr)) - { - if ( -#ifdef IP_DEFAULT_MULTICAST_TTL - s->ttl != IP_DEFAULT_MULTICAST_TTL && -#endif - setsockopt(s->fd, SOL_IP, IP_MULTICAST_TTL, &s->ttl, sizeof(s->ttl)) < 0) - return "IP_MULTICAST_TTL"; - if ( -#ifdef IP_DEFAULT_MULTICAST_LOOP - IP_DEFAULT_MULTICAST_LOOP && +static inline void fill_mreq_grp(struct ip_mreq *m, struct iface *ifa, ip_addr maddr) +{ + bzero(m, sizeof(*m)); +#ifdef CONFIG_LINUX_MC_MREQ_BIND + m->imr_interface.s_addr = INADDR_ANY; +#else + set_inaddr(&m->imr_interface, ifa->addr->ip); #endif - setsockopt(s->fd, SOL_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) - return "IP_MULTICAST_LOOP"; - } - return NULL; + set_inaddr(&m->imr_multiaddr, maddr); } +#endif + #ifdef CONFIG_LINUX_MC_MREQN /* @@ -76,70 +82,78 @@ struct ip_mreqn }; #endif -static inline char *sysio_mcast_join(sock *s) +#define MREQ_IFA struct ip_mreqn +#define MREQ_GRP struct ip_mreqn +#define fill_mreq_ifa fill_mreq +#define fill_mreq_grp fill_mreq + +static inline fill_mreq(struct ip_mreqn *m, struct iface *ifa, ip_addr maddr) { - struct ip_mreqn mreq; - char *err; - struct ifreq ifr; - - if (err = sysio_mcast_setup(s)) - return err; - strcpy(ifr.ifr_name, s->iface->name); - if (setsockopt(s->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) - return "SO_BINDTODEVICE"; - mreq.imr_ifindex = s->iface->index; - set_inaddr(&mreq.imr_address, s->iface->addr->ip); - set_inaddr(&mreq.imr_multiaddr, s->daddr); - /* This defines where should we send _outgoing_ multicasts */ - if (ipa_nonzero(s->daddr) && setsockopt(s->fd, SOL_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0) - return "IP_MULTICAST_IF"; - /* And this one sets interface for _receiving_ multicasts from */ - if (ipa_nonzero(s->saddr) && setsockopt(s->fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) - return "IP_ADD_MEMBERSHIP"; - return NULL; + bzero(m, sizeof(*m)); + m->imr_ifindex = ifa->index; + set_inaddr(&m->imr_address, ifa->addr->ip); + set_inaddr(&m->imr_multiaddr, maddr); } #endif -#if defined(CONFIG_LINUX_MC_MREQ) || defined(CONFIG_LINUX_MC_MREQ_BIND) -/* - * Older kernels support only struct mreq which matches interfaces by their - * addresses and thus fails on unnumbered devices. On newer 2.0 kernels - * we can use SO_BINDTODEVICE to circumvent this problem. - */ - -static inline char *sysio_mcast_join(sock *s) +static inline char * +sysio_setup_multicast(sock *s) { - struct in_addr mreq; - struct ip_mreq mreq_add; - char *err; + MREQ_IFA m; + int zero = 0; - if (err = sysio_mcast_setup(s)) - return err; - set_inaddr(&mreq, s->iface->addr->ip); -#ifdef CONFIG_LINUX_MC_MREQ_BIND + if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) + return "IP_MULTICAST_LOOP"; + + if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_TTL, &s->ttl, sizeof(s->ttl)) < 0) + return "IP_MULTICAST_TTL"; + + /* This defines where should we send _outgoing_ multicasts */ + fill_mreq_ifa(&m, s->iface, IPA_NONE); + if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_IF, &m, sizeof(m)) < 0) + return "IP_MULTICAST_IF"; + +#if defined(CONFIG_LINUX_MC_MREQ_BIND) || defined(CONFIG_LINUX_MC_MREQN) { struct ifreq ifr; strcpy(ifr.ifr_name, s->iface->name); if (setsockopt(s->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) return "SO_BINDTODEVICE"; - mreq_add.imr_interface.s_addr = INADDR_ANY; } -#else - mreq_add.imr_interface = mreq; #endif - set_inaddr(&mreq_add.imr_multiaddr, s->daddr); - /* This defines where should we send _outgoing_ multicasts */ - if (ipa_nonzero(s->daddr) && setsockopt(s->fd, SOL_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0) - return "IP_MULTICAST_IF"; + + return NULL; +} + +static inline char * +sysio_join_group(sock *s, ip_addr maddr) +{ + MREQ_GRP m; + /* And this one sets interface for _receiving_ multicasts from */ - if (ipa_nonzero(s->saddr) && setsockopt(s->fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq_add, sizeof(mreq_add)) < 0) + fill_mreq_grp(&m, s->iface, maddr); + if (setsockopt(s->fd, SOL_IP, IP_ADD_MEMBERSHIP, &m, sizeof(m)) < 0) return "IP_ADD_MEMBERSHIP"; + + return NULL; +} + +static inline char * +sysio_leave_group(sock *s, ip_addr maddr) +{ + MREQ_GRP m; + + /* And this one sets interface for _receiving_ multicasts from */ + fill_mreq_grp(&m, s->iface, maddr); + if (setsockopt(s->fd, SOL_IP, IP_DROP_MEMBERSHIP, &m, sizeof(m)) < 0) + return "IP_DROP_MEMBERSHIP"; + return NULL; } -#endif #endif + #include <linux/socket.h> #include <linux/tcp.h> diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 330aea4..cd5c5db 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -627,6 +627,12 @@ fill_in_sockaddr(sockaddr *sa, ip_addr a, unsigned port) set_inaddr(&sa->sin6_addr, a); } +static inline void +fill_in_sockifa(sockaddr *sa, struct iface *ifa) +{ + sa->sin6_scope_id = ifa ? ifa->index : 0; +} + void get_sockaddr(struct sockaddr_in6 *sa, ip_addr *a, unsigned *port, int check) { @@ -652,6 +658,11 @@ fill_in_sockaddr(sockaddr *sa, ip_addr a, unsigned port) set_inaddr(&sa->sin_addr, a); } +static inline void +fill_in_sockifa(sockaddr *sa, struct iface *ifa) +{ +} + void get_sockaddr(struct sockaddr_in *sa, ip_addr *a, unsigned *port, int check) { @@ -670,8 +681,7 @@ sk_set_ttl_int(sock *s) { int one = 1; #ifdef IPV6 - if (s->type != SK_UDP_MC && s->type != SK_IP_MC && - setsockopt(s->fd, SOL_IPV6, IPV6_UNICAST_HOPS, &s->ttl, sizeof(s->ttl)) < 0) + if (setsockopt(s->fd, SOL_IPV6, IPV6_UNICAST_HOPS, &s->ttl, sizeof(s->ttl)) < 0) return "IPV6_UNICAST_HOPS"; #else if (setsockopt(s->fd, SOL_IP, IP_TTL, &s->ttl, sizeof(s->ttl)) < 0) @@ -768,6 +778,151 @@ sk_set_md5_auth(sock *s, ip_addr a, char *passwd) return sk_set_md5_auth_int(s, &sa, passwd); } +int +sk_set_broadcast(sock *s, int enable) +{ + if (setsockopt(s->fd, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(enable)) < 0) + { + log(L_ERR "sk_set_broadcast: SO_BROADCAST: %m"); + return -1; + } + + return 0; +} + + +#ifdef IPV6 + +int +sk_set_ipv6_checksum(sock *s, int offset) +{ + if (setsockopt(s->fd, IPPROTO_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset)) < 0) + { + log(L_ERR "sk_set_ipv6_checksum: IPV6_CHECKSUM: %m"); + return -1; + } + + return 0; +} + +int +sk_setup_multicast(sock *s) +{ + char *err; + int zero = 0; + int index; + + ASSERT(s->iface && s->iface->addr); + + index = s->iface->index; + if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &s->ttl, sizeof(s->ttl)) < 0) + ERR("IPV6_MULTICAST_HOPS"); + if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) + ERR("IPV6_MULTICAST_LOOP"); + if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index)) < 0) + ERR("IPV6_MULTICAST_IF"); + + return 0; + +bad: + log(L_ERR "sk_setup_multicast: %s: %m", err); + return -1; +} + +int +sk_join_group(sock *s, ip_addr maddr) +{ + struct ipv6_mreq mreq; + + set_inaddr(&mreq.ipv6mr_multiaddr, maddr); + +#ifdef CONFIG_IPV6_GLIBC_20 + mreq.ipv6mr_ifindex = s->iface->index; +#else + mreq.ipv6mr_interface = s->iface->index; +#endif + + /* RFC 2553 says IPV6_JOIN_GROUP */ + if (setsockopt(s->fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + log(L_ERR "sk_join_group: IPV6_ADD_MEMBERSHIP: %m"); + return -1; + } + + return 0; +} + +int +sk_leave_group(sock *s, ip_addr maddr) +{ + struct ipv6_mreq mreq; + + set_inaddr(&mreq.ipv6mr_multiaddr, maddr); + +#ifdef CONFIG_IPV6_GLIBC_20 + mreq.ipv6mr_ifindex = s->iface->index; +#else + mreq.ipv6mr_interface = s->iface->index; +#endif + + /* RFC 2553 says IPV6_LEAVE_GROUP */ + if (setsockopt(s->fd, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + log(L_ERR "sk_leave_group: IPV6_DROP_MEMBERSHIP: %m"); + return -1; + } + + return 0; +} + +#else /* IPV4 */ + +int +sk_setup_multicast(sock *s) +{ + char *err; + + ASSERT(s->iface && s->iface->addr); + + if (err = sysio_setup_multicast(s)) + { + log(L_ERR "sk_setup_multicast: %s: %m", err); + return -1; + } + + return 0; +} + +int +sk_join_group(sock *s, ip_addr maddr) +{ + char *err; + + if (err = sysio_join_group(s, maddr)) + { + log(L_ERR "sk_join_group: %s: %m", err); + return -1; + } + + return 0; +} + +int +sk_leave_group(sock *s, ip_addr maddr) +{ + char *err; + + if (err = sysio_leave_group(s, maddr)) + { + log(L_ERR "sk_leave_group: %s: %m", err); + return -1; + } + + return 0; +} + +#endif + static void sk_tcp_connected(sock *s) @@ -841,11 +996,9 @@ sk_open(sock *s) fd = socket(BIRD_PF, SOCK_STREAM, IPPROTO_TCP); break; case SK_UDP: - case SK_UDP_MC: fd = socket(BIRD_PF, SOCK_DGRAM, IPPROTO_UDP); break; case SK_IP: - case SK_IP_MC: fd = socket(BIRD_PF, SOCK_RAW, s->dport); break; case SK_MAGIC: @@ -861,61 +1014,11 @@ sk_open(sock *s) if (err = sk_setup(s)) goto bad; - switch (type) - { - case SK_UDP: - case SK_IP: - if (s->iface) /* It's a broadcast socket */ -#ifdef IPV6 - bug("IPv6 has no broadcasts"); -#else - if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0) - ERR("SO_BROADCAST"); -#endif - break; - case SK_UDP_MC: - case SK_IP_MC: - { -#ifdef IPV6 - /* Fortunately, IPv6 socket interface is recent enough and therefore standardized */ - ASSERT(s->iface && s->iface->addr); - if (ipa_nonzero(s->daddr)) - { - int t = s->iface->index; - int zero = 0; - if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &s->ttl, sizeof(s->ttl)) < 0) - ERR("IPV6_MULTICAST_HOPS"); - if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) - ERR("IPV6_MULTICAST_LOOP"); - if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_IF, &t, sizeof(t)) < 0) - ERR("IPV6_MULTICAST_IF"); - } - if (has_src) - { - struct ipv6_mreq mreq; - set_inaddr(&mreq.ipv6mr_multiaddr, s->daddr); -#ifdef CONFIG_IPV6_GLIBC_20 - mreq.ipv6mr_ifindex = s->iface->index; -#else - mreq.ipv6mr_interface = s->iface->index; -#endif /* CONFIG_IPV6_GLIBC_20 */ - if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) - ERR("IPV6_ADD_MEMBERSHIP"); - } -#else - /* With IPv4 there are zillions of different socket interface variants. Ugh. */ - ASSERT(s->iface && s->iface->addr); - if (err = sysio_mcast_join(s)) - goto bad; -#endif /* IPV6 */ - break; - } - } if (has_src) { int port; - if (type == SK_IP || type == SK_IP_MC) + if (type == SK_IP) port = 0; else { @@ -924,12 +1027,8 @@ sk_open(sock *s) ERR("SO_REUSEADDR"); } fill_in_sockaddr(&sa, s->saddr, port); -#ifdef CONFIG_SKIP_MC_BIND - if ((type != SK_UDP_MC) && (type != SK_IP_MC) && - bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) -#else + fill_in_sockifa(&sa, s->iface); if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) -#endif ERR("bind"); } fill_in_sockaddr(&sa, s->daddr, s->dport); @@ -1050,16 +1149,15 @@ sk_maybe_write(sock *s) s->ttx = s->tpos = s->tbuf; return 1; case SK_UDP: - case SK_UDP_MC: case SK_IP: - case SK_IP_MC: { sockaddr sa; if (s->tbuf == s->tpos) return 1; - fill_in_sockaddr(&sa, s->faddr, s->fport); + fill_in_sockaddr(&sa, s->faddr, s->fport); + fill_in_sockifa(&sa, s->iface); e = sendto(s->fd, s->tbuf, s->tpos - s->tbuf, 0, (struct sockaddr *) &sa, sizeof(sa)); if (e < 0) { |