summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndrej Zajicek (work) <santiago@crfreenet.org>2017-10-09 01:16:29 +0200
committerOndrej Zajicek (work) <santiago@crfreenet.org>2017-10-10 16:10:02 +0200
commitd3f4f92b0ece0ce4031087a25735e6cbf0d741e2 (patch)
treee19713ebf6be06e4933c6a56eb4e3260e01dcb0a
parent15a4421f9cb2c077cc484e3cda94e8710a1d68f5 (diff)
OSPF: Support of address families in OSPFv3
OSPFv3-AF can handle multiple topologies of diferent address families (IPv4, IPv6, both unicast and multicast) using separate instances distinguished by instance ID ranges.
-rw-r--r--proto/ospf/config.Y89
-rw-r--r--proto/ospf/dbdes.c2
-rw-r--r--proto/ospf/hello.c23
-rw-r--r--proto/ospf/iface.c9
-rw-r--r--proto/ospf/lsalib.c10
-rw-r--r--proto/ospf/lsalib.h4
-rw-r--r--proto/ospf/ospf.c62
-rw-r--r--proto/ospf/ospf.h127
-rw-r--r--proto/ospf/rt.c28
-rw-r--r--proto/ospf/topology.c44
10 files changed, 262 insertions, 136 deletions
diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y
index 1f379bf4..85149ea1 100644
--- a/proto/ospf/config.Y
+++ b/proto/ospf/config.Y
@@ -78,18 +78,66 @@ static void
ospf_proto_finish(void)
{
struct ospf_config *cf = OSPF_CFG;
-
- if (EMPTY_LIST(cf->area_list))
- cf_error( "No configured areas in OSPF");
+ struct ospf_area_config *ac;
+ struct ospf_iface_patt *ic;
/* Define default channel */
if (EMPTY_LIST(this_proto->channels))
+ {
+ this_proto->net_type = ospf_cfg_is_v2() ? NET_IP4 : NET_IP6;
channel_config_new(NULL, this_proto->net_type, this_proto);
+ }
+
+ /* Propagate global instance ID to interfaces */
+ if (cf->instance_id_set)
+ {
+ WALK_LIST(ac, cf->area_list)
+ WALK_LIST(ic, ac->patt_list)
+ if (!ic->instance_id_set)
+ { ic->instance_id = cf->instance_id; ic->instance_id_set = 1; }
+
+ WALK_LIST(ic, cf->vlink_list)
+ if (!ic->instance_id_set)
+ { ic->instance_id = cf->instance_id; ic->instance_id_set = 1; }
+ }
+
+ if (ospf_cfg_is_v3())
+ {
+ uint ipv4 = (this_proto->net_type == NET_IP4);
+ uint base = (ipv4 ? 64 : 0) + (cf->af_mc ? 32 : 0);
+
+ /* RFC 5838 - OSPFv3-AF */
+ if (cf->af_ext)
+ {
+ /* RFC 5838 2.1 - instance IDs based on AFs */
+ WALK_LIST(ac, cf->area_list)
+ WALK_LIST(ic, ac->patt_list)
+ {
+ if (!ic->instance_id_set)
+ ic->instance_id = base;
+ else if (ic->instance_id >= 128)
+ log(L_WARN "Instance ID %d from unassigned/private range", ic->instance_id);
+ else if ((ic->instance_id < base) || (ic->instance_id >= (base + 32)))
+ cf_error("Instance ID %d invalid for given channel type", ic->instance_id);
+ }
+
+ /* RFC 5838 2.8 - vlinks limited to IPv6 unicast */
+ if ((ipv4 || cf->af_mc) && !EMPTY_LIST(cf->vlink_list))
+ cf_error("Vlinks not supported in AFs other than IPv6 unicast");
+ }
+ else
+ {
+ if (ipv4 || cf->af_mc)
+ cf_error("Different channel type");
+ }
+ }
+
+ if (EMPTY_LIST(cf->area_list))
+ cf_error("No configured areas in OSPF");
int areano = 0;
int backbone = 0;
int nssa = 0;
- struct ospf_area_config *ac;
WALK_LIST(ac, cf->area_list)
{
areano++;
@@ -148,10 +196,11 @@ CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, CHECK, LINK, ONLY, BFD)
CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY, TAG, EXTERNAL)
CF_KEYWORDS(WAIT, DELAY, LSADB, ECMP, LIMIT, WEIGHT, NSSA, TRANSLATOR, STABILITY)
CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL, NETMASK, TX, PRIORITY, LENGTH)
-CF_KEYWORDS(SECONDARY, MERGE, LSA, SUPPRESSION)
+CF_KEYWORDS(SECONDARY, MERGE, LSA, SUPPRESSION, MULTICAST)
%type <ld> lsadb_args
-%type <i> ospf_variant nbma_eligible
+%type <i> ospf_variant ospf_af_mc nbma_eligible
+%type <cc> ospf_channel_start ospf_channel
CF_GRAMMAR
@@ -166,12 +215,13 @@ ospf_variant:
ospf_proto_start: proto_start ospf_variant
{
this_proto = proto_config_new(&proto_ospf, $1);
- this_proto->net_type = $2 ? NET_IP4 : NET_IP6;
+ this_proto->net_type = $2 ? NET_IP4 : 0;
init_list(&OSPF_CFG->area_list);
init_list(&OSPF_CFG->vlink_list);
OSPF_CFG->tick = OSPF_DEFAULT_TICK;
OSPF_CFG->ospf2 = $2;
+ OSPF_CFG->af_ext = !$2;
};
ospf_proto:
@@ -179,16 +229,33 @@ ospf_proto:
| ospf_proto ospf_proto_item ';'
;
+ospf_af_mc:
+ { $$ = 0; }
+ | MULTICAST { $$ = 1; }
+ ;
+
+/* We redefine proto_channel to add multicast flag */
+ospf_channel_start: net_type ospf_af_mc
+{
+ $$ = this_channel = channel_config_new(NULL, $1, this_proto);
+
+ /* Save the multicast flag */
+ if (this_channel == proto_cf_main_channel(this_proto))
+ OSPF_CFG->af_mc = $2;
+};
+
+ospf_channel: ospf_channel_start channel_opt_list channel_end;
+
ospf_proto_item:
proto_item
- | proto_channel
+ | ospf_channel { this_proto->net_type = $1->net_type; }
| RFC1583COMPAT bool { OSPF_CFG->rfc1583 = $2; }
| STUB ROUTER bool { OSPF_CFG->stub_router = $3; }
| ECMP bool { OSPF_CFG->ecmp = $2 ? OSPF_DEFAULT_ECMP_LIMIT : 0; }
| ECMP bool LIMIT expr { OSPF_CFG->ecmp = $2 ? $4 : 0; }
| MERGE EXTERNAL bool { OSPF_CFG->merge_external = $3; }
| TICK expr { OSPF_CFG->tick = $2; if($2 <= 0) cf_error("Tick must be greater than zero"); }
- | INSTANCE ID expr { OSPF_CFG->instance_id = $3; if ($3 > 255) cf_error("Instance ID must be in range 0-255"); }
+ | INSTANCE ID expr { OSPF_CFG->instance_id = $3; OSPF_CFG->instance_id_set = 1; if ($3 > 255) cf_error("Instance ID must be in range 0-255"); }
| ospf_area
;
@@ -293,7 +360,6 @@ ospf_vlink_start: VIRTUAL LINK idval
OSPF_PATT->inftransdelay = INFTRANSDELAY_D;
OSPF_PATT->deadc = DEADC_D;
OSPF_PATT->type = OSPF_IT_VLINK;
- OSPF_PATT->instance_id = OSPF_CFG->instance_id;
init_list(&OSPF_PATT->nbma_list);
reset_passwords();
}
@@ -393,7 +459,6 @@ ospf_iface_start:
OSPF_PATT->priority = PRIORITY_D;
OSPF_PATT->deadc = DEADC_D;
OSPF_PATT->type = OSPF_IT_UNDEF;
- OSPF_PATT->instance_id = OSPF_CFG->instance_id;
init_list(&OSPF_PATT->nbma_list);
OSPF_PATT->ptp_netmask = 2; /* not specified */
OSPF_PATT->tx_tos = IP_PREC_INTERNET_CONTROL;
@@ -404,7 +469,7 @@ ospf_iface_start:
ospf_instance_id:
/* empty */
- | INSTANCE expr { OSPF_PATT->instance_id = $2; if ($2 > 255) cf_error("Instance ID must be in range 0-255"); }
+ | INSTANCE expr { OSPF_PATT->instance_id = $2; OSPF_PATT->instance_id_set = 1; if ($2 > 255) cf_error("Instance ID must be in range 0-255"); }
;
ospf_iface_patt_list:
diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c
index 195b03ad..a4452cc8 100644
--- a/proto/ospf/dbdes.c
+++ b/proto/ospf/dbdes.c
@@ -356,7 +356,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa,
LOG_PKT_WARN("MTU mismatch with nbr %R on %s (remote %d, local %d)",
n->rid, ifa->ifname, rcv_iface_mtu, ifa->iface->mtu);
- if ((rcv_imms == DBDES_IMMS) &&
+ if (((rcv_imms & DBDES_IMMS) == DBDES_IMMS) &&
(n->rid > p->router_id) &&
(plen == ospf_dbdes_hdrlen(p)))
{
diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c
index 2c55155d..e706ea0f 100644
--- a/proto/ospf/hello.c
+++ b/proto/ospf/hello.c
@@ -32,10 +32,7 @@ struct ospf_hello3_packet
struct ospf_packet hdr;
u32 iface_id;
- u8 priority;
- u8 options3;
- u8 options2;
- u8 options;
+ u32 options;
u16 helloint;
u16 deadint;
u32 dr;
@@ -91,10 +88,7 @@ ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn)
struct ospf_hello3_packet *ps = (void *) pkt;
ps->iface_id = htonl(ifa->iface_id);
- ps->priority = ifa->priority;
- ps->options3 = ifa->oa->options >> 16;
- ps->options2 = ifa->oa->options >> 8;
- ps->options = ifa->oa->options;
+ ps->options = ntohl(ifa->oa->options | (ifa->priority << 24));
ps->helloint = ntohs(ifa->helloint);
ps->deadint = htons(ifa->deadint);
ps->dr = htonl(ifa->drid);
@@ -190,7 +184,8 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa,
struct ospf_proto *p = ifa->oa->po;
const char *err_dsc = NULL;
u32 rcv_iface_id, rcv_helloint, rcv_deadint, rcv_dr, rcv_bdr;
- u8 rcv_options, rcv_priority;
+ uint rcv_options, rcv_priority;
+ uint loc_options = ifa->oa->options;
u32 *neighbors;
u32 neigh_count;
uint plen, i, err_val = 0;
@@ -245,8 +240,8 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa,
rcv_deadint = ntohs(ps->deadint);
rcv_dr = ntohl(ps->dr);
rcv_bdr = ntohl(ps->bdr);
- rcv_options = ps->options;
- rcv_priority = ps->priority;
+ rcv_options = ntohl(ps->options) & 0x00FFFFFF;
+ rcv_priority = ntohl(ps->options) >> 24;
neighbors = ps->neighbors;
neigh_count = (plen - sizeof(struct ospf_hello3_packet)) / sizeof(u32);
@@ -259,9 +254,13 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa,
DROP("dead interval mismatch", rcv_deadint);
/* Check whether bits E, N match */
- if ((rcv_options ^ ifa->oa->options) & (OPT_E | OPT_N))
+ if ((rcv_options ^ loc_options) & (OPT_E | OPT_N))
DROP("area type mismatch", rcv_options);
+ /* RFC 5838 2.4 - AF-bit check unless on IPv6 unicast */
+ if ((loc_options & OPT_AF) && !(loc_options & OPT_V6) && !(rcv_options & OPT_AF))
+ DROP("AF-bit mismatch", rcv_options);
+
/* Check consistency of existing neighbor entry */
if (n)
{
diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c
index 675cf76d..98d48aa1 100644
--- a/proto/ospf/iface.c
+++ b/proto/ospf/iface.c
@@ -1113,9 +1113,6 @@ ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a)
{
struct ospf_proto *p = (struct ospf_proto *) P;
- if (a->prefix.type != NET_IP6)
- return;
-
if (a->flags & IA_SECONDARY)
return;
@@ -1126,6 +1123,9 @@ ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a)
other addresses are used for link-LSA. */
if (a->scope == SCOPE_LINK)
{
+ if (a->prefix.type != NET_IP6)
+ return;
+
if (flags & IF_CHANGE_UP)
{
struct ospf_mip_walk s = { .iface = a->iface };
@@ -1143,6 +1143,9 @@ ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a)
}
else
{
+ if (a->prefix.type != ospf_get_af(p))
+ return;
+
struct ospf_iface *ifa;
WALK_LIST(ifa, p->iface_list)
if (ifa->iface == a->iface)
diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c
index b88a114d..fbfd8d29 100644
--- a/proto/ospf/lsalib.c
+++ b/proto/ospf/lsalib.c
@@ -280,7 +280,7 @@ lsa_walk_rt(struct ospf_lsa_rt_walk *rt)
void
-lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, net_addr *net, u8 *pxopts, u32 *metric)
+lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, int af, net_addr *net, u8 *pxopts, u32 *metric)
{
if (ospf2)
{
@@ -292,7 +292,7 @@ lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, net_addr *net, u8 *pxopt
else
{
struct ospf_lsa_sum3_net *ls = en->lsa_body;
- ospf_get_ipv6_prefix(ls->prefix, net, pxopts, NULL);
+ ospf3_get_prefix(ls->prefix, af, net, pxopts, NULL);
*metric = ls->metric & LSA_METRIC_MASK;
}
}
@@ -317,7 +317,7 @@ lsa_parse_sum_rt(struct top_hash_entry *en, int ospf2, u32 *drid, u32 *metric, u
}
void
-lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *rt)
+lsa_parse_ext(struct top_hash_entry *en, int ospf2, int af, struct ospf_lsa_ext_local *rt)
{
if (ospf2)
{
@@ -338,13 +338,13 @@ lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *r
else
{
struct ospf_lsa_ext3 *ext = en->lsa_body;
- u32 *buf = ospf_get_ipv6_prefix(ext->rest, &rt->net, &rt->pxopts, NULL);
+ u32 *buf = ospf3_get_prefix(ext->rest, af, &rt->net, &rt->pxopts, NULL);
rt->metric = ext->metric & LSA_METRIC_MASK;
rt->ebit = ext->metric & LSA_EXT3_EBIT;
rt->fbit = ext->metric & LSA_EXT3_FBIT;
if (rt->fbit)
- buf = ospf_get_ipv6_addr(buf, &rt->fwaddr);
+ buf = ospf3_get_addr(buf, af, &rt->fwaddr);
else
rt->fwaddr = IPA_NONE;
diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h
index c93f0295..0d477f58 100644
--- a/proto/ospf/lsalib.h
+++ b/proto/ospf/lsalib.h
@@ -55,9 +55,9 @@ u16 lsa_verify_checksum(const void *lsa_n, int lsa_len);
int lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2);
void lsa_walk_rt_init(struct ospf_proto *po, struct top_hash_entry *act, struct ospf_lsa_rt_walk *rt);
int lsa_walk_rt(struct ospf_lsa_rt_walk *rt);
-void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, net_addr *net, u8 *pxopts, u32 *metric);
+void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, int af, net_addr *net, u8 *pxopts, u32 *metric);
void lsa_parse_sum_rt(struct top_hash_entry *en, int ospf2, u32 *drid, u32 *metric, u32 *options);
-void lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *rt);
+void lsa_parse_ext(struct top_hash_entry *en, int ospf2, int af, struct ospf_lsa_ext_local *rt);
int lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body);
#endif /* _BIRD_OSPF_LSALIB_H_ */
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index daf76ff2..7ce6698e 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -92,8 +92,10 @@
* - RFC 2328 - main OSPFv2 standard
* - RFC 5340 - main OSPFv3 standard
* - RFC 3101 - OSPFv2 NSSA areas
- * - RFC 6549 - OSPFv2 multi-instance extensions
- * - RFC 6987 - OSPF stub router advertisement
+ * - RFC 5709 - OSPFv2 HMAC-SHA Cryptographic Authentication
+ * - RFC 5838 - OSPFv3 Support of Address Families
+ * - RFC 6549 - OSPFv2 Multi-Instance Extensions
+ * - RFC 6987 - OSPF Stub Router Advertisement
*/
#include <stdlib.h>
@@ -115,9 +117,9 @@ add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac)
struct area_net_config *anc;
struct area_net *an;
- fib_init(&oa->net_fib, p->p.pool, ospf_is_v2(p) ? NET_IP4 : NET_IP6,
+ fib_init(&oa->net_fib, p->p.pool, ospf_get_af(p),
sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL);
- fib_init(&oa->enet_fib, p->p.pool, ospf_is_v2(p) ? NET_IP4 : NET_IP6,
+ fib_init(&oa->enet_fib, p->p.pool, ospf_get_af(p),
sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL);
WALK_LIST(anc, ac->net_list)
@@ -134,6 +136,16 @@ add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac)
}
}
+static inline uint
+ospf_opts(struct ospf_proto *p)
+{
+ if (ospf_is_v2(p))
+ return 0;
+
+ return ((ospf_is_ip6(p) && !p->af_mc) ? OPT_V6 : 0) |
+ (!p->stub_router ? OPT_R : 0) | (p->af_ext ? OPT_AF : 0);
+}
+
static void
ospf_area_add(struct ospf_proto *p, struct ospf_area_config *ac)
{
@@ -155,10 +167,7 @@ ospf_area_add(struct ospf_proto *p, struct ospf_area_config *ac)
if (oa->areaid == 0)
p->backbone = oa;
- if (ospf_is_v2(p))
- oa->options = ac->type;
- else
- oa->options = ac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R);
+ oa->options = ac->type | ospf_opts(p);
ospf_notify_rt_lsa(oa);
}
@@ -224,6 +233,8 @@ ospf_start(struct proto *P)
p->router_id = proto_get_router_id(P->cf);
p->ospf2 = c->ospf2;
+ p->af_ext = c->af_ext;
+ p->af_mc = c->af_mc;
p->rfc1583 = c->rfc1583;
p->stub_router = c->stub_router;
p->merge_external = c->merge_external;
@@ -238,8 +249,7 @@ ospf_start(struct proto *P)
p->nhpool = lp_new(P->pool, 12*sizeof(struct nexthop));
init_list(&(p->iface_list));
init_list(&(p->area_list));
- fib_init(&p->rtf, P->pool, p->ospf2 ? NET_IP4 : NET_IP6,
- sizeof(ort), OFFSETOF(ort, fn), 0, NULL);
+ fib_init(&p->rtf, P->pool, ospf_get_af(p), sizeof(ort), OFFSETOF(ort, fn), 0, NULL);
if (ospf_is_v3(p))
idm_init(&p->idm, P->pool, 16);
p->areano = 0;
@@ -601,11 +611,7 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)
struct ospf_iface *ifa;
oa->ac = nac;
-
- if (ospf_is_v2(p))
- oa->options = nac->type;
- else
- oa->options = nac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R);
+ oa->options = nac->type | ospf_opts(p);
if (nac->type != oac->type)
{
@@ -659,6 +665,9 @@ ospf_reconfigure(struct proto *P, struct proto_config *CF)
if (old->abr != new->abr)
return 0;
+ if ((p->af_ext != new->af_ext) || (p->af_mc != new->af_mc))
+ return 0;
+
if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
return 0;
@@ -1073,13 +1082,13 @@ show_lsa_network(struct top_hash_entry *he, int ospf2)
}
static inline void
-show_lsa_sum_net(struct top_hash_entry *he, int ospf2)
+show_lsa_sum_net(struct top_hash_entry *he, int ospf2, int af)
{
net_addr net;
u8 pxopts;
u32 metric;
- lsa_parse_sum_net(he, ospf2, &net, &pxopts, &metric);
+ lsa_parse_sum_net(he, ospf2, af, &net, &pxopts, &metric);
cli_msg(-1016, "\t\txnetwork %N metric %u", &net, metric);
}
@@ -1096,7 +1105,7 @@ show_lsa_sum_rt(struct top_hash_entry *he, int ospf2)
static inline void
-show_lsa_external(struct top_hash_entry *he, int ospf2)
+show_lsa_external(struct top_hash_entry *he, int ospf2, int af)
{
struct ospf_lsa_ext_local rt;
char str_via[IPA_MAX_TEXT_LENGTH + 8] = "";
@@ -1105,7 +1114,7 @@ show_lsa_external(struct top_hash_entry *he, int ospf2)
if (he->lsa_type == LSA_T_EXT)
he->domain = 0; /* Unmark the LSA */
- lsa_parse_ext(he, ospf2, &rt);
+ lsa_parse_ext(he, ospf2, af, &rt);
if (rt.fbit)
bsprintf(str_via, " via %I", rt.fwaddr);
@@ -1119,7 +1128,7 @@ show_lsa_external(struct top_hash_entry *he, int ospf2)
}
static inline void
-show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode)
+show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode, int af)
{
struct ospf_lsa_prefix *px = he->lsa_body;
u32 *buf;
@@ -1142,7 +1151,7 @@ show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode)
u8 pxopts;
u16 metric;
- buf = ospf_get_ipv6_prefix(buf, &net, &pxopts, &metric);
+ buf = ospf3_get_prefix(buf, af, &net, &pxopts, &metric);
if (px->ref_type == LSA_T_RT)
cli_msg(-1016, "\t\tstubnet %N metric %u", &net, metric);
@@ -1156,6 +1165,7 @@ ospf_sh_state(struct proto *P, int verbose, int reachable)
{
struct ospf_proto *p = (struct ospf_proto *) P;
int ospf2 = ospf_is_v2(p);
+ int af = ospf_get_af(p);
uint i, ix, j1, jx;
u32 last_area = 0xFFFFFFFF;
@@ -1276,7 +1286,7 @@ ospf_sh_state(struct proto *P, int verbose, int reachable)
case LSA_T_SUM_NET:
if (cnode->lsa_type == LSA_T_RT)
- show_lsa_sum_net(he, ospf2);
+ show_lsa_sum_net(he, ospf2, af);
break;
case LSA_T_SUM_RT:
@@ -1286,11 +1296,11 @@ ospf_sh_state(struct proto *P, int verbose, int reachable)
case LSA_T_EXT:
case LSA_T_NSSA:
- show_lsa_external(he, ospf2);
+ show_lsa_external(he, ospf2, af);
break;
case LSA_T_PREFIX:
- show_lsa_prefix(he, cnode);
+ show_lsa_prefix(he, cnode, af);
break;
}
@@ -1304,7 +1314,7 @@ ospf_sh_state(struct proto *P, int verbose, int reachable)
ix++;
while ((ix < jx) && (hex[ix]->lsa.rt == cnode->lsa.rt))
- show_lsa_external(hex[ix++], ospf2);
+ show_lsa_external(hex[ix++], ospf2, af);
cnode = NULL;
}
@@ -1338,7 +1348,7 @@ ospf_sh_state(struct proto *P, int verbose, int reachable)
last_rt = he->lsa.rt;
}
- show_lsa_external(he, ospf2);
+ show_lsa_external(he, ospf2, af);
}
}
diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h
index e3eae2b5..1530b26a 100644
--- a/proto/ospf/ospf.h
+++ b/proto/ospf/ospf.h
@@ -84,10 +84,13 @@ struct ospf_config
struct proto_config c;
uint tick;
u8 ospf2;
+ u8 af_ext;
+ u8 af_mc;
u8 rfc1583;
u8 stub_router;
u8 merge_external;
u8 instance_id;
+ u8 instance_id_set;
u8 abr;
u8 asbr;
int ecmp;
@@ -168,9 +171,9 @@ struct ospf_iface_patt
int tx_priority;
u16 tx_length;
u16 rx_buffer;
-
#define OSPF_RXBUF_MINSIZE 256 /* Minimal allowed size */
u8 instance_id;
+ u8 instance_id_set;
u8 autype; /* OSPF_AUTH_*, not really used in OSPFv3 */
u8 strictnbma;
u8 check_link;
@@ -211,12 +214,14 @@ struct ospf_proto
int padj; /* Number of neighbors in Exchange or Loading state */
struct fib rtf; /* Routing table */
struct idm idm; /* OSPFv3 LSA ID map */
- byte ospf2; /* OSPF v2 or v3 */
- byte rfc1583; /* RFC1583 compatibility */
- byte stub_router; /* Do not forward transit traffic */
- byte merge_external; /* Should i merge external routes? */
- byte asbr; /* May i originate any ext/NSSA lsa? */
- byte ecmp; /* Maximal number of nexthops in ECMP route, or 0 */
+ u8 ospf2; /* OSPF v2 or v3 */
+ u8 af_ext; /* OSPFv3-AF extension */
+ u8 af_mc; /* OSPFv3-AF multicast */
+ u8 rfc1583; /* RFC1583 compatibility */
+ u8 stub_router; /* Do not forward transit traffic */
+ u8 merge_external; /* Should i merge external routes? */
+ u8 asbr; /* May i originate any ext/NSSA lsa? */
+ u8 ecmp; /* Maximal number of nexthops in ECMP route, or 0 */
struct ospf_area *backbone; /* If exists */
event *flood_event; /* Event for flooding LS updates */
void *lsab; /* LSA buffer used when originating router LSAs */
@@ -449,14 +454,15 @@ struct ospf_neighbor
/* Generic option flags */
-#define OPT_V6 0x01 /* OSPFv3, LSA relevant for IPv6 routing calculation */
-#define OPT_E 0x02 /* Related to AS-external LSAs */
-#define OPT_MC 0x04 /* Related to MOSPF, not used and obsolete */
-#define OPT_N 0x08 /* Related to NSSA */
-#define OPT_P 0x08 /* OSPFv2, flags P and N share position, see NSSA RFC */
-#define OPT_EA 0x10 /* OSPFv2, external attributes, not used and obsolete */
-#define OPT_R 0x10 /* OSPFv3, originator is active router */
-#define OPT_DC 0x20 /* Related to demand circuits, not used */
+#define OPT_V6 0x0001 /* OSPFv3, LSA relevant for IPv6 routing calculation */
+#define OPT_E 0x0002 /* Related to AS-external LSAs */
+#define OPT_MC 0x0004 /* Related to MOSPF, not used and obsolete */
+#define OPT_N 0x0008 /* Related to NSSA */
+#define OPT_P 0x0008 /* OSPFv2, flags P and N share position, see NSSA RFC */
+#define OPT_EA 0x0010 /* OSPFv2, external attributes, not used and obsolete */
+#define OPT_R 0x0010 /* OSPFv3, originator is active router */
+#define OPT_DC 0x0020 /* Related to demand circuits, not used */
+#define OPT_AF 0x0100 /* OSPFv3 Address Families (RFC 5838) */
/* Router-LSA VEB flags are are stored together with links (OSPFv2) or options (OSPFv3) */
#define OPT_RT_B (0x01 << 24)
@@ -718,74 +724,96 @@ lsa_net_count(struct ospf_lsa_header *lsa)
#define IPV6_PREFIX_SPACE(x) ((((x) + 63) / 32) * 4)
#define IPV6_PREFIX_WORDS(x) (((x) + 63) / 32)
-/* FIXME: these functions should be significantly redesigned w.r.t. integration,
- also should be named as ospf3_* instead of *_ipv6_* */
static inline int
ospf_valid_prefix(net_addr *n)
{
- /* In OSPFv2, prefix is stored as netmask; ip4_masklen() returns 255 for invalid one */
- return n->pxlen <= IP6_MAX_PREFIX_LENGTH;
+ /*
+ * In OSPFv2, prefix is stored as netmask; ip4_masklen() returns 255 for
+ * invalid one. But OSPFv3-AF may receive IPv4 net with 32 < pxlen < 128.
+ */
+ uint max = (n->type == NET_IP4) ? IP4_MAX_PREFIX_LENGTH : IP6_MAX_PREFIX_LENGTH;
+ return n->pxlen <= max;
}
+/*
+ * In OSPFv3-AF (RFC 5835), IPv4 address is encoded by just placing it in the
+ * first 32 bits of IPv6 address and setting remaining bits to zero. Likewise
+ * for IPv4 prefix, where remaining bits do not matter. We use following
+ * functions to convert between IPv4 and IPv4-in-IPv6 representations:
+ */
+
+static inline ip4_addr ospf3_6to4(ip6_addr a)
+{ return _MI4(_I0(a)); }
+
+static inline ip6_addr ospf3_4to6(ip4_addr a)
+{ return _MI6(_I(a), 0, 0, 0); }
+
+
static inline u32 *
-ospf_get_ipv6_prefix(u32 *buf, net_addr *N, u8 *pxopts, u16 *rest)
+ospf3_get_prefix(u32 *buf, int af, net_addr *n, u8 *pxopts, u16 *rest)
{
- net_addr_ip6 *net = (void *) N;
- u8 pxlen = (*buf >> 24);
+ ip6_addr px = IP6_NONE;
+ uint pxlen = (*buf >> 24);
*pxopts = (*buf >> 16) & 0xff;
if (rest) *rest = *buf & 0xffff;
buf++;
- *net = NET_ADDR_IP6(IP6_NONE, pxlen);
-
if (pxlen > 0)
- _I0(net->prefix) = *buf++;
+ _I0(px) = *buf++;
if (pxlen > 32)
- _I1(net->prefix) = *buf++;
+ _I1(px) = *buf++;
if (pxlen > 64)
- _I2(net->prefix) = *buf++;
+ _I2(px) = *buf++;
if (pxlen > 96)
- _I3(net->prefix) = *buf++;
+ _I3(px) = *buf++;
/* Clean up remaining bits */
if (pxlen < 128)
- net->prefix.addr[pxlen / 32] &= u32_mkmask(pxlen % 32);
+ px.addr[pxlen / 32] &= u32_mkmask(pxlen % 32);
- return buf;
-}
+ if (af == NET_IP4)
+ net_fill_ip4(n, ospf3_6to4(px), pxlen);
+ else
+ net_fill_ip6(n, px, pxlen);
-static inline u32 *
-ospf_get_ipv6_addr(u32 *buf, ip_addr *addr)
-{
- *addr = ipa_from_ip6(*(ip6_addr *) buf);
- return buf + 4;
+ return buf;
}
static inline u32 *
-ospf_put_ipv6_prefix(u32 *buf, net_addr *N, u8 pxopts, u16 rest)
+ospf3_put_prefix(u32 *buf, net_addr *n, u8 pxopts, u16 rest)
{
- net_addr_ip6 *net = (void *) N;
- u32 pxlen = net->pxlen;
+ ip6_addr px = (n->type == NET_IP4) ? ospf3_4to6(net4_prefix(n)) : net6_prefix(n);
+ uint pxlen = n->pxlen;
*buf++ = ((pxlen << 24) | (pxopts << 16) | rest);
if (pxlen > 0)
- *buf++ = _I0(net->prefix);
+ *buf++ = _I0(px);
if (pxlen > 32)
- *buf++ = _I1(net->prefix);
+ *buf++ = _I1(px);
if (pxlen > 64)
- *buf++ = _I2(net->prefix);
+ *buf++ = _I2(px);
if (pxlen > 96)
- *buf++ = _I3(net->prefix);
+ *buf++ = _I3(px);
return buf;
}
static inline u32 *
-ospf_put_ipv6_addr(u32 *buf, ip_addr addr)
+ospf3_get_addr(u32 *buf, int af, ip_addr *addr)
+{
+ ip6_addr a;
+ memcpy(&a, buf, 16);
+ *addr = (af == NET_IP4) ? ipa_from_ip4(ospf3_6to4(a)) : ipa_from_ip6(a);
+ return buf + 4;
+}
+
+static inline u32 *
+ospf3_put_addr(u32 *buf, ip_addr addr)
{
- *(ip6_addr *) buf = ipa_to_ip6(addr);
+ ip6_addr a = ipa_is_ip4(addr) ? ospf3_4to6(ipa_to_ip4(addr)) : ipa_to_ip6(addr);
+ memcpy(buf, &a, 16);
return buf + 4;
}
@@ -838,6 +866,15 @@ static inline int ospf_is_v3(struct ospf_proto *p)
static inline int ospf_get_version(struct ospf_proto *p)
{ return ospf_is_v2(p) ? 2 : 3; }
+static inline int ospf_is_ip4(struct ospf_proto *p)
+{ return p->p.net_type == NET_IP4; }
+
+static inline int ospf_is_ip6(struct ospf_proto *p)
+{ return p->p.net_type == NET_IP6; }
+
+static inline int ospf_get_af(struct ospf_proto *p)
+{ return p->p.net_type; }
+
struct ospf_area *ospf_find_area(struct ospf_proto *p, u32 aid);
static inline struct ospf_area *ospf_main_area(struct ospf_proto *p)
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index df9eb75b..f57925c3 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -576,20 +576,20 @@ spfa_process_prefixes(struct ospf_proto *p, struct ospf_area *oa)
buf = px->rest;
for (i = 0; i < px->pxcount; i++)
{
- net_addr_ip6 net;
+ net_addr net;
u8 pxopts;
u16 metric;
- buf = ospf_get_ipv6_prefix(buf, (net_addr *) &net, &pxopts, &metric);
+ buf = ospf3_get_prefix(buf, ospf_get_af(p), &net, &pxopts, &metric);
if (pxopts & OPT_PX_NU)
continue;
/* Store the first global address to use it later as a vlink endpoint */
- if ((pxopts & OPT_PX_LA) && ipa_zero(src->lb))
- src->lb = ipa_from_ip6(net.prefix);
+ if ((pxopts & OPT_PX_LA) && (net.type == NET_IP6) && ipa_zero(src->lb))
+ src->lb = ipa_from_ip6(net6_prefix(&net));
- add_network(oa, (net_addr *) &net, src->dist + metric, src, i);
+ add_network(oa, &net, src->dist + metric, src, i);
}
}
}
@@ -761,7 +761,7 @@ ospf_rt_sum(struct ospf_area *oa)
if (en->lsa_type == LSA_T_SUM_NET)
{
- lsa_parse_sum_net(en, ospf_is_v2(p), &net, &pxopts, &metric);
+ lsa_parse_sum_net(en, ospf_is_v2(p), ospf_get_af(p), &net, &pxopts, &metric);
if (!ospf_valid_prefix(&net))
{
@@ -858,7 +858,7 @@ ospf_rt_sum_tr(struct ospf_area *oa)
net_addr net;
u8 pxopts;
- lsa_parse_sum_net(en, ospf_is_v2(p), &net, &pxopts, &metric);
+ lsa_parse_sum_net(en, ospf_is_v2(p), ospf_get_af(p), &net, &pxopts, &metric);
if (!ospf_valid_prefix(&net))
{
@@ -1058,7 +1058,7 @@ decide_nssa_lsa(struct ospf_proto *p, ort *nf, struct ospf_lsa_ext_local *rt)
return 0;
/* We do not store needed data in struct orta, we have to parse the LSA */
- lsa_parse_ext(en, ospf_is_v2(p), rt);
+ lsa_parse_ext(en, ospf_is_v2(p), ospf_get_af(p), rt);
if (rt->pxopts & OPT_PX_NU)
return 0;
@@ -1450,7 +1450,7 @@ ospf_ext_spf(struct ospf_proto *p)
DBG("%s: Working on LSA. ID: %R, RT: %R, Type: %u\n",
p->p.name, en->lsa.id, en->lsa.rt, en->lsa_type);
- lsa_parse_ext(en, ospf_is_v2(p), &rt);
+ lsa_parse_ext(en, ospf_is_v2(p), ospf_get_af(p), &rt);
if (!ospf_valid_prefix(&rt.net))
{
@@ -1765,7 +1765,11 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
if (ip6_zero(llsa->lladdr))
return NULL;
- return new_nexthop(p, ipa_from_ip6(llsa->lladdr), pn->iface, pn->weight);
+ ip_addr nh = ospf_is_ip4(p) ?
+ ipa_from_ip4(ospf3_6to4(llsa->lladdr)) :
+ ipa_from_ip6(llsa->lladdr);
+
+ return new_nexthop(p, nh, pn->iface, pn->weight);
}
}
@@ -1792,9 +1796,9 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
if (en->lsa.age == LSA_MAXAGE)
return;
- if (ospf_is_v3(p) && (en->lsa_type == LSA_T_RT))
+ if (ospf_is_v3(p) && (oa->options & OPT_V6) && (en->lsa_type == LSA_T_RT))
{
- /* In OSPFv3, check V6 flag */
+ /* In OSPFv3 IPv6 unicast, check V6 flag */
struct ospf_lsa_rt *rt = en->lsa_body;
if (!(rt->options & OPT_V6))
return;
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index ce77f57a..8dd91a40 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -1008,7 +1008,7 @@ prepare_sum3_net_lsa_body(struct ospf_proto *p, ort *nf, u32 metric)
sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_net) +
IPV6_PREFIX_SPACE(nf->fn.addr->pxlen));
sum->metric = metric;
- ospf_put_ipv6_prefix(sum->prefix, nf->fn.addr, 0, 0);
+ ospf3_put_prefix(sum->prefix, nf->fn.addr, 0, 0);
}
static inline void
@@ -1097,7 +1097,7 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf,
ext->metric = metric & LSA_METRIC_MASK;
u32 *buf = ext->rest;
- buf = ospf_put_ipv6_prefix(buf, nf->fn.addr, pbit ? OPT_PX_P : 0, 0);
+ buf = ospf3_put_prefix(buf, nf->fn.addr, pbit ? OPT_PX_P : 0, 0);
if (ebit)
ext->metric |= LSA_EXT3_EBIT;
@@ -1105,7 +1105,7 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf,
if (ipa_nonzero(fwaddr))
{
ext->metric |= LSA_EXT3_FBIT;
- buf = ospf_put_ipv6_addr(buf, fwaddr);
+ buf = ospf3_put_addr(buf, fwaddr);
}
if (tag)
@@ -1222,7 +1222,7 @@ find_surrogate_fwaddr(struct ospf_proto *p, struct ospf_area *oa)
{
WALK_LIST(a, ifa->iface->addrs)
{
- if ((a->prefix.type != NET_IP6) ||
+ if ((a->prefix.type != ospf_get_af(p)) ||
(a->flags & IA_SECONDARY) ||
(a->flags & IA_PEER) ||
(a->scope <= SCOPE_LINK))
@@ -1316,39 +1316,47 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
*/
static inline void
-lsab_put_prefix(struct ospf_proto *p, net_addr *net, u32 cost)
+lsab_put_prefix(struct ospf_proto *p, net_addr *n, u32 cost)
{
- void *buf = lsab_alloc(p, IPV6_PREFIX_SPACE(net6_pxlen(net)));
- u8 flags = (net6_pxlen(net) < IP6_MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA;
- ospf_put_ipv6_prefix(buf, net, flags, cost);
+ void *buf = lsab_alloc(p, IPV6_PREFIX_SPACE(net_pxlen(n)));
+ uint max = (n->type == NET_IP4) ? IP4_MAX_PREFIX_LENGTH : IP6_MAX_PREFIX_LENGTH;
+ u8 flags = (net_pxlen(n) < max) ? 0 : OPT_PX_LA;
+ ospf3_put_prefix(buf, n, flags, cost);
}
static void
prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
{
- struct ospf_lsa_link *ll;
+ ip_addr nh = ospf_is_ip4(p) ? IPA_NONE : ifa->addr->ip;
int i = 0;
+ /* Preallocating space for header */
ASSERT(p->lsab_used == 0);
- ll = lsab_allocz(p, sizeof(struct ospf_lsa_link));
- ll->options = ifa->oa->options | (ifa->priority << 24);
- ll->lladdr = ipa_to_ip6(ifa->addr->ip);
- ll = NULL; /* buffer might be reallocated later */
+ lsab_allocz(p, sizeof(struct ospf_lsa_link));
struct ifa *a;
WALK_LIST(a, ifa->iface->addrs)
{
- if ((a->prefix.type != NET_IP6) ||
+ if ((a->prefix.type != ospf_get_af(p)) ||
(a->flags & IA_SECONDARY) ||
(a->scope <= SCOPE_LINK))
continue;
+ if (ospf_is_ip4(p) && ipa_zero(nh))
+ nh = a->ip;
+
lsab_put_prefix(p, &a->prefix, 0);
i++;
}
- ll = p->lsab;
+ /* Filling the preallocated header */
+ struct ospf_lsa_link *ll = p->lsab;
+ ll->options = ifa->oa->options | (ifa->priority << 24);
+ ll->lladdr = ospf_is_ip4(p) ? ospf3_4to6(ipa_to_ip4(nh)) : ipa_to_ip6(nh);
ll->pxcount = i;
+
+ if (ipa_zero(nh))
+ log(L_ERR "%s: Cannot find next hop address for %s", p->p.name, ifa->ifname);
}
static void
@@ -1410,7 +1418,7 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
struct ifa *a;
WALK_LIST(a, ifa->iface->addrs)
{
- if ((a->prefix.type != NET_IP6) ||
+ if ((a->prefix.type != ospf_get_af(p)) ||
(a->flags & IA_SECONDARY) ||
(a->flags & IA_PEER) ||
(a->scope <= SCOPE_LINK))
@@ -1448,7 +1456,7 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
/* If there are some configured vlinks, find some global address
(even from another area), which will be used as a vlink endpoint. */
- if (!EMPTY_LIST(cf->vlink_list) && !host_addr)
+ if (!EMPTY_LIST(cf->vlink_list) && !host_addr && ospf_is_ip6(p))
{
WALK_LIST(ifa, p->iface_list)
{
@@ -1571,7 +1579,7 @@ add_link_lsa(struct ospf_proto *p, struct ospf_lsa_link *ll, int offset, int *px
continue;
/* Skip link-local prefixes */
- if ((pxlen >= 10) && ((pxb[1] & 0xffc00000) == 0xfe800000))
+ if (ospf_is_ip6(p) && (pxlen >= 10) && ((pxb[1] & 0xffc00000) == 0xfe800000))
continue;
add_prefix(p, pxb, offset, pxc);