diff options
Diffstat (limited to 'proto')
-rw-r--r-- | proto/ospf/topology.c | 230 |
1 files changed, 157 insertions, 73 deletions
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 8f7563b2..ebcc31d2 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -637,12 +637,20 @@ check_sum_net_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en) } static inline int -check_ext_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en) +check_sum_lsa_same(struct top_hash_entry *en, u32 metric) { - struct ospf_lsa_ext *ext = en->lsa_body; - return fn->pxlen != ipa_mklen(ext->netmask); + /* Netmask already checked in check_sum_net_lsaid_collision() */ + struct ospf_lsa_sum *sum = en->lsa_body; + return (sum->metric == metric); } +#define check_sum_net_lsa_same(en,metric) \ + check_sum_lsa_same(en, metric) + +#define check_sum_rt_lsa_same(en,drid,metric,options) \ + check_sum_lsa_same(en, metric) + + #else /* OSPFv3 */ static inline void * @@ -672,16 +680,11 @@ check_sum_net_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en) } static inline int -check_ext_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en) +check_sum_net_lsa_same(struct top_hash_entry *en, u32 metric) { - 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); + /* Prefix already checked in check_sum_net_lsaid_collision() */ + struct ospf_lsa_sum_net *sum = en->lsa_body; + return (sum->metric == metric); } static inline void * @@ -690,13 +693,20 @@ originate_sum_rt_lsa_body(struct proto_ospf *po, u16 *length, u32 drid, u32 metr 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->options = options; sum->metric = metric; sum->drid = drid; return sum; } +static inline int +check_sum_rt_lsa_same(struct top_hash_entry *en, u32 drid, u32 metric, u32 options) +{ + struct ospf_lsa_sum_rt *sum = en->lsa_body; + return (sum->options == options) && (sum->metric == metric) && (sum->drid == drid); +} + #endif void @@ -725,11 +735,14 @@ originate_sum_net_lsa(struct ospf_area *oa, struct fib_node *fn, int metric) if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL) { if (check_sum_net_lsaid_collision(fn, en)) - { - log(L_ERR, "%s: LSAID collision for %I/%d", - p->name, fn->prefix, fn->pxlen); - return; - } + { + log(L_ERR, "%s: LSAID collision for %I/%d", + p->name, fn->prefix, fn->pxlen); + return; + } + + if (check_sum_net_lsa_same(en, metric)) + return; lsa.sn = en->lsa.sn + 1; } @@ -764,8 +777,15 @@ originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32 lsa.rt = po->router_id; lsa.sn = LSA_INITSEQNO; + options &= OPTIONS_MASK; + if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL) + { + if (check_sum_rt_lsa_same(en, lsa.id, metric, options)) + return; + lsa.sn = en->lsa.sn + 1; + } body = originate_sum_rt_lsa_body(po, &lsa.length, lsa.id, metric, options); lsasum_calculate(&lsa, body); @@ -815,65 +835,114 @@ flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type) } } +#ifdef OSPFv2 -static void * -originate_ext_lsa_body(net *n, rte *e, u16 *length, struct proto_ospf *po, - struct ea_list *attrs) +static inline void * +originate_ext_lsa_body(struct proto_ospf *po, u16 *length, net *n, + u32 metric, ip_addr fwaddr, u32 tag) { - 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); + struct ospf_lsa_ext *ext = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_ext)); + *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_ext); - // FIXME check for gw should be per ifa, not per iface - if ((e->attrs->dest == RTD_ROUTER) && - !ipa_equal(e->attrs->gw, IPA_NONE) && - !ipa_has_link_scope(e->attrs->gw) && - (ospf_iface_find((struct proto_ospf *) p, e->attrs->iface) != NULL)) - gw = 1; + ext->metric = metric; + ext->netmask = ipa_mkmask(n->n.pxlen); + ext->fwaddr = fwaddr; + ext->tag = tag; -#ifdef OSPFv3 - size += IPV6_PREFIX_SPACE(n->n.pxlen); + return ext; +} - if (gw) - size += 16; - - if (tag) - size += 4; -#endif - - ext = mb_alloc(p->pool, size); - *length = sizeof(struct ospf_lsa_header) + size; +/* + * check_ext_lsa() combines functions of check_*_lsaid_collision() and + * check_*_lsa_same(). 'en' is existing ext LSA, and rest parameters + * are parameters of new ext route. Function returns -1 if there is + * LSAID collision, returns 1 if the existing LSA is the same and + * returns 0 otherwise (in that case, we need to originate a new LSA). + * + * Really, checking for the same parameters is not as important as in + * summary LSA origination, because in most cases the duplicate + * external route propagation would be stopped by the nest. But there + * are still some cases (route reload, the same route propagated through + * different protocol) so it is also done here. + */ - ext->metric = (m1 != LSINFINITY) ? m1 : (m2 | LSA_EXT_EBIT); +static inline int +check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_addr fwaddr, u32 tag) +{ + struct ospf_lsa_ext *ext = en->lsa_body; + + /* LSAID collision */ + if (fn->pxlen != ipa_mklen(ext->netmask)) + return -1; + + return (ext->metric == metric) && (ext->tag == tag) && (ext->fwaddr == fwaddr); +} -#ifdef OSPFv2 - ext->netmask = ipa_mkmask(n->n.pxlen); - ext->fwaddr = gw ? e->attrs->gw : IPA_NONE; - ext->tag = tag; #else /* OSPFv3 */ + +static inline void * +originate_ext_lsa_body(struct proto_ospf *po, u16 *length, net *n, + u32 metric, ip_addr fwaddr, u32 tag) +{ + int size = sizeof(struct ospf_lsa_ext) + + IPV6_PREFIX_SPACE(n->n.pxlen) + + (ipa_nonzero(fwaddr) ? 16 : 0) + + (tag ? 4 : 0); + + struct ospf_lsa_ext *ext = mb_alloc(po->proto.pool, size); + *length = sizeof(struct ospf_lsa_header) + size; + + ext->metric = metric; + u32 *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 (ipa_nonzero(fwaddr)) + { + ext->metric |= LSA_EXT_FBIT; + buf = put_ipv6_addr(buf, fwaddr); + } if (tag) - { - ext->metric |= LSA_EXT_TBIT; - *buf++ = tag; - } -#endif + { + ext->metric |= LSA_EXT_TBIT; + *buf++ = tag; + } return ext; } +static inline int +check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_addr fwaddr, u32 tag) +{ + struct ospf_lsa_ext *ext = en->lsa_body; + ip_addr prefix; + int pxlen; + u8 pxopts; + u16 rest; + + u32 *buf = lsa_get_ipv6_prefix(ext->rest, &prefix, &pxlen, &pxopts, &rest); + + /* LSAID collision */ + if ((fn->pxlen != pxlen) || !ipa_equal(fn->prefix, prefix)) + return -1; + + u32 rt_metric = ext->metric & METRIC_MASK; + ip_addr rt_fwaddr = IPA_NONE; + u32 rt_tag = 0; + + if (ext->metric & LSA_EXT_FBIT) + buf = lsa_get_ipv6_addr(buf, &rt_fwaddr); + + if (ext->metric & LSA_EXT_TBIT) + rt_tag = *buf++; + + return (rt_metric == metric) && ipa_equal(rt_fwaddr, fwaddr) && (rt_tag == tag); +} + + +#endif + /** * originate_ext_lsa - new route received from nest and filters * @n: network prefix and mask @@ -882,9 +951,7 @@ originate_ext_lsa_body(net *n, rte *e, u16 *length, struct proto_ospf *po, * @attrs: list of extended attributes * * If I receive a message that new route is installed, I try to originate an - * external LSA. The LSA header of such LSA does not contain information about - * prefix length, so if I have to originate multiple LSAs for route with - * different prefixes I try to increment prefix id to find a "free" one. + * external LSA. * * The function also sets flag ebit. If it's the first time, the new router lsa * origination is necessary. @@ -912,19 +979,36 @@ originate_ext_lsa(net * n, rte * e, struct proto_ospf *po, lsa.rt = po->router_id; lsa.sn = LSA_INITSEQNO; + /* Compute LSA content */ + u32 m1 = ea_get_int(attrs, EA_OSPF_METRIC1, LSINFINITY); + u32 m2 = ea_get_int(attrs, EA_OSPF_METRIC2, 10000); + u32 metric = (m1 != LSINFINITY) ? m1 : (m2 | LSA_EXT_EBIT); + u32 tag = ea_get_int(attrs, EA_OSPF_TAG, 0); + ip_addr gw = IPA_NONE; + // FIXME check for gw should be per ifa, not per iface + if ((e->attrs->dest == RTD_ROUTER) && + ipa_nonzero(e->attrs->gw) && + !ipa_has_link_scope(e->attrs->gw) && + (ospf_iface_find((struct proto_ospf *) p, e->attrs->iface) != NULL)) + gw = e->attrs->gw; + if ((en = ospf_hash_find_header(po->gr, 0, &lsa)) != NULL) + { + int rv = check_ext_lsa(en, fn, metric, gw, tag); + if (rv < 0) { - 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; + log(L_ERR, "%s: LSAID collision for %I/%d", + p->name, fn->prefix, fn->pxlen); + return; } - body = originate_ext_lsa_body(n, e, &lsa.length, po, attrs); + if (rv > 0) + return; + + lsa.sn = en->lsa.sn + 1; + } + + body = originate_ext_lsa_body(po, &lsa.length, n, metric, gw, tag); lsasum_calculate(&lsa, body); en = lsa_install_new(po, &lsa, 0, body); @@ -954,7 +1038,7 @@ flush_ext_lsa(net *n, struct proto_ospf *po) if (en = ospf_hash_find(po->gr, 0, lsaid, po->router_id, LSA_T_EXT)) { - if (check_ext_lsaid_collision(fn, en)) + if (check_ext_lsa(en, fn, 0, IPA_NONE, 0) < 0) { log(L_ERR, "%s: LSAID collision for %I/%d", p->name, fn->prefix, fn->pxlen); |