From d3f4f92b0ece0ce4031087a25735e6cbf0d741e2 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 9 Oct 2017 01:16:29 +0200 Subject: [PATCH] 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. --- proto/ospf/config.Y | 89 +++++++++++++++++++++++++---- proto/ospf/dbdes.c | 2 +- proto/ospf/hello.c | 23 ++++---- proto/ospf/iface.c | 9 ++- proto/ospf/lsalib.c | 10 ++-- proto/ospf/lsalib.h | 4 +- proto/ospf/ospf.c | 62 ++++++++++++--------- proto/ospf/ospf.h | 127 +++++++++++++++++++++++++++--------------- proto/ospf/rt.c | 28 ++++++---- proto/ospf/topology.c | 44 +++++++++------ 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 lsadb_args -%type ospf_variant nbma_eligible +%type ospf_variant ospf_af_mc nbma_eligible +%type 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 @@ -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); + + if (af == NET_IP4) + net_fill_ip4(n, ospf3_6to4(px), pxlen); + else + net_fill_ip6(n, px, pxlen); return buf; } static inline u32 * -ospf_get_ipv6_addr(u32 *buf, ip_addr *addr) +ospf3_put_prefix(u32 *buf, net_addr *n, u8 pxopts, u16 rest) { - *addr = ipa_from_ip6(*(ip6_addr *) buf); - return buf + 4; -} - -static inline u32 * -ospf_put_ipv6_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 *) buf = ipa_to_ip6(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 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);