summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.in10
-rw-r--r--doc/bird.sgml3
-rw-r--r--filter/filter.h1
-rw-r--r--lib/ipv4.h6
-rw-r--r--lib/ipv6.h10
-rw-r--r--lib/socket.h15
-rw-r--r--nest/iface.h1
-rw-r--r--nest/neighbor.c38
-rw-r--r--nest/route.h1
-rw-r--r--nest/rt-fib.c41
-rw-r--r--proto/ospf/config.Y16
-rw-r--r--proto/ospf/dbdes.c125
-rw-r--r--proto/ospf/dbdes.h4
-rw-r--r--proto/ospf/hello.c183
-rw-r--r--proto/ospf/hello.h4
-rw-r--r--proto/ospf/iface.c162
-rw-r--r--proto/ospf/lsack.c102
-rw-r--r--proto/ospf/lsack.h4
-rw-r--r--proto/ospf/lsalib.c500
-rw-r--r--proto/ospf/lsalib.h8
-rw-r--r--proto/ospf/lsreq.c63
-rw-r--r--proto/ospf/lsreq.h4
-rw-r--r--proto/ospf/lsupd.c312
-rw-r--r--proto/ospf/lsupd.h12
-rw-r--r--proto/ospf/neighbor.c115
-rw-r--r--proto/ospf/ospf.c509
-rw-r--r--proto/ospf/ospf.h506
-rw-r--r--proto/ospf/packet.c110
-rw-r--r--proto/ospf/packet.h6
-rw-r--r--proto/ospf/rt.c811
-rw-r--r--proto/ospf/rt.h9
-rw-r--r--proto/ospf/topology.c1420
-rw-r--r--proto/ospf/topology.h36
-rw-r--r--proto/rip/rip.c36
-rw-r--r--sysdep/bsd/sysio.h51
-rw-r--r--sysdep/linux/netlink/netlink.c21
-rw-r--r--sysdep/linux/sysio.h136
-rw-r--r--sysdep/unix/io.c224
38 files changed, 3830 insertions, 1785 deletions
diff --git a/configure.in b/configure.in
index 309cb836..ec2417b2 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 3905a29e..806b0e6d 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/filter/filter.h b/filter/filter.h
index 62bad84c..e526a79b 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);
diff --git a/lib/ipv4.h b/lib/ipv4.h
index b64d9b2a..5c8c3907 100644
--- a/lib/ipv4.h
+++ b/lib/ipv4.h
@@ -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 */
diff --git a/lib/ipv6.h b/lib/ipv6.h
index 9cec86e7..53888ff0 100644
--- a/lib/ipv6.h
+++ b/lib/ipv6.h
@@ -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 53e89edd..c642bdf9 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 af98a761..a982c17f 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 2c5af6a6..a44667f5 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 1bd23a6b..e45a8c62 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 8d76f26f..510aa76b 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/ospf/config.Y b/proto/ospf/config.Y
index 9529f89a..e809baf9 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 adc0276f..a249d75f 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 af962928..63cca0a2 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 45b6b613..738748d8 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 d5359352..5d73c09e 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 ea38461d..8db086ec 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 824767a6..c740ef69 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 05cc22f0..63a436d6 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 e624b6ce..ab633984 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 c7f16d51..ed929beb 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 e7d7af24..0e360d75 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 f917f05c..a12edde2 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 f8195dcd..9bed374e 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 524a0a2d..8bacfe65 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 bb681c22..ba8d7b98 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 d4bcbed7..4c2f1c2a 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -117,9 +117,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 +163,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 +189,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);
}
@@ -301,15 +307,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,7 +341,7 @@ 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;
}
@@ -331,7 +351,7 @@ schedule_rtcalc(struct proto_ospf *po)
* @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 +361,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 +396,7 @@ ospf_disp(timer * timer)
/* Calculate routing table */
if (po->calcrt)
- ospf_rt_spf (po);
+ ospf_rt_spf(po);
}
@@ -448,36 +474,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 +493,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 +606,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 +765,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 +776,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 +1037,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 < rt->links; 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 < 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 +1190,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 +1251,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 +1350,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 +1410,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 +1424,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 7f0ee54c..0930a065 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
@@ -562,6 +730,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 +746,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 +777,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 783d28ed..1a02c00e 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 b85b608b..4d5612ff 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 2e1de440..ae815b3c 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, &lt->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
@@ -972,18 +1161,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 +1252,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 +1265,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 acb0a1ac..d4b85ae2 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 5d8c7a9d..46656811 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(struct ospf_lsa_net)
+ + nodes * sizeof(u32));
- net = mb_alloc(po->proto.pool, sizeof(u32) * (ifa->fadj + 1) +
- sizeof(struct ospf_lsa_net));
+#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,19 +868,81 @@ 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 (!ipa_equal(e->attrs->gw, IPA_NONE))
+ {
+ /* FIXME: check for link-local in OSPFv3 ? */
+ if (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;
}
/**
@@ -625,75 +964,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 +1393,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 +1454,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 +1466,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 +1475,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 +1621,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 +1633,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 +1653,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 +1662,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 +1671,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 +1679,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 21e545e7..f6ba0198 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 c655cc36..f9a160e6 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 b0ec456e..80f8f942 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 03d43884..f45fe159 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 2fa5f0a9..70d35ccf 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 330aea4f..cd5c5db7 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)
{