]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merge commit '950775f6fa3d569a9d7cd05e33538d35e895d688' into haugesund
authorMaria Matejka <mq@ucw.cz>
Wed, 8 Jun 2022 09:47:49 +0000 (11:47 +0200)
committerMaria Matejka <mq@ucw.cz>
Wed, 8 Jun 2022 09:47:49 +0000 (11:47 +0200)
There were quite a lot of conflicts in flowspec validation code which
ultimately led to some code being a bit rewritten, not only adapted from
this or that branch, yet it is still in a limit of a merge.

20 files changed:
1  2 
filter/f-inst.c
lib/route.h
nest/config.Y
nest/rt-attr.c
nest/rt-dev.c
nest/rt-show.c
nest/rt-table.c
nest/rt.h
proto/babel/babel.c
proto/bgp/attrs.c
proto/bgp/bgp.h
proto/bgp/packets.c
proto/ospf/rt.c
proto/ospf/topology.c
proto/perf/perf.c
proto/rip/rip.c
proto/rpki/rpki.c
proto/static/static.c
sysdep/linux/netlink.c
sysdep/unix/krt.c

diff --cc filter/f-inst.c
index b276387fe43c452de6b15a35218ef609c7c5711d,c2abd5aa4b68f30ffbe925c94bc395308756dae5..1690c9f64f441fe21b073bcd38a05a0eb27e9464
  
        switch (sa.sa_code)
        {
 -      case SA_NET:    RESULT(sa.type, net, (*fs->rte)->net->n.addr); break;
 -      case SA_PROTO:  RESULT(sa.type, s, (*fs->rte)->src->proto->name); break;
 +      case SA_NET:    RESULT(sa.type, net, fs->rte->net); break;
 +      case SA_PROTO:  RESULT(sa.type, s, fs->rte->src->proto->name); break;
-       case SA_DEST:   RESULT(sa.type, i, rta->dest); break;
        default:
        {
-         struct eattr *nh_ea = ea_find(*fs->eattrs, &ea_gen_nexthop);
-         struct nexthop *nh = nh_ea ? &((struct nexthop_adata *) nh_ea->u.ptr)->nh : NULL;
+         struct eattr *nhea = ea_find(*fs->eattrs, &ea_gen_nexthop);
+         struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL;
+         struct nexthop *nh = nhad ? &nhad->nh : NULL;
  
          switch (sa.sa_code)
          {
diff --cc lib/route.h
index 613df0c38f660ca9472b3e60586ce58c6d2337cd,1d8877c8008f1dfd088ff039e0022e2f5805b15f..283a3760db47c807de8ec1549332014982a59bb3
@@@ -330,9 -331,18 +333,25 @@@ extern struct ea_class ea_gen_from
  /* Source: An old method to devise the route source protocol and kind.
   * To be superseded in a near future by something more informative. */
  extern struct ea_class ea_gen_source;
 -static inline u32 rt_get_source_attr(rte *rt)
 +static inline u32 rt_get_source_attr(const rte *rt)
  { return ea_get_int(rt->attrs->eattrs, &ea_gen_source, 0); }
  
 -#define FLOWSPEC_UNKNOWN      0
 -#define FLOWSPEC_VALID                1
 -#define FLOWSPEC_INVALID      2
+ /* Flowspec validation result */
 -static inline u32 rt_get_flowspec_valid(rte *rt)
++enum flowspec_valid {
++  FLOWSPEC_UNKNOWN    = 0,
++  FLOWSPEC_VALID      = 1,
++  FLOWSPEC_INVALID    = 2,
++  FLOWSPEC__MAX,
++};
++
++extern const char * flowspec_valid_names[FLOWSPEC__MAX];
++static inline const char *flowspec_valid_name(enum flowspec_valid v)
++{ return (v < FLOWSPEC__MAX) ? flowspec_valid_names[v] : "???"; }
+ extern struct ea_class ea_gen_flowspec_valid;
++static inline enum flowspec_valid rt_get_flowspec_valid(rte *rt)
+ { return ea_get_int(rt->attrs->eattrs, &ea_gen_flowspec_valid, FLOWSPEC_UNKNOWN); }
  /* Next hop: For now, stored as adata */
  extern struct ea_class ea_gen_nexthop;
  
@@@ -361,7 -377,35 +386,35 @@@ struct nexthop_adata *nexthop_merge(str
  struct nexthop_adata *nexthop_sort(struct nexthop_adata *x, linpool *lp);
  int nexthop_is_sorted(struct nexthop_adata *x);
  
+ #define NEXTHOP_IS_REACHABLE(nhad)    ((nhad)->ad.length > NEXTHOP_DEST_SIZE)
  
 -static inline int rte_dest(rte *r)
+ /* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */
+ static inline int rte_is_reachable(rte *r)
+ {
+   eattr *nhea = ea_find(r->attrs->eattrs, &ea_gen_nexthop);
+   if (!nhea)
+     return 0;
+   struct nexthop_adata *nhad = (void *) nhea->u.ptr;
+   return NEXTHOP_IS_REACHABLE(nhad);
+ }
+ static inline int nhea_dest(eattr *nhea)
+ {
+   if (!nhea)
+     return RTD_NONE;
+   struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL;
+   if (NEXTHOP_IS_REACHABLE(nhad))
+     return RTD_UNICAST;
+   else
+     return nhad->dest;
+ }
++static inline int rte_dest(const rte *r)
+ {
+   return nhea_dest(ea_find(r->attrs->eattrs, &ea_gen_nexthop));
+ }
  
  void rta_init(void);
  #define rta_size(...) (sizeof(rta))
diff --cc nest/config.Y
Simple merge
diff --cc nest/rt-attr.c
index cf3ab659d85d3b99ef0b89a32a841a0ae0128525,b5be936ba043c26c0aebfdd15d9ddcff43578600..f548a575bb58b75467a9533c38e79fab325a95b3
@@@ -166,6 -166,12 +166,18 @@@ const char * rta_dest_names[RTD_MAX] = 
    [RTD_PROHIBIT]      = "prohibited",
  };
  
+ struct ea_class ea_gen_flowspec_valid = {
+   .name = "flowspec_valid",
+   .type = T_ENUM_FLOWSPEC_VALID,
+   .readonly = 1,
+ };
++const char * flowspec_valid_names[FLOWSPEC__MAX] = {
++  [FLOWSPEC_UNKNOWN]  = "unknown",
++  [FLOWSPEC_VALID]    = "",
++  [FLOWSPEC_INVALID]  = "invalid",
++};
++
  pool *rta_pool;
  
  static slab *rta_slab;
diff --cc nest/rt-dev.c
Simple merge
diff --cc nest/rt-show.c
index 6cfd99fcc5edd4c20a00f4b797bb02b2682ddd95,0ad8f5c645546caccdf9c17ea0b3fffd388bb373..cc5a9a101c8c57f084dad1bc6976823a55a74249
@@@ -45,8 -45,9 +45,11 @@@ rt_show_rte(struct cli *c, byte *ia, rt
    rta *a = e->attrs;
    int sync_error = d->kernel ? krt_get_sync_error(d->kernel, e) : 0;
    void (*get_route_info)(struct rte *, byte *buf);
--  eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop);
++  eattr *nhea = net_type_match(e->net, NB_DEST) ?
++    ea_find(a->eattrs, &ea_gen_nexthop) : NULL;
    struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL;
 -  int dest = NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest;
++  int dest = nhad ? (NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest) : RTD_NONE;
++  int flowspec_valid = net_is_flow(e->net) ? rt_get_flowspec_valid(e) : FLOWSPEC_UNKNOWN;
  
    tm_format_time(tm, &config->tf_route, e->lastmod);
    ip_addr a_from = ea_get_ip(a->eattrs, &ea_gen_from, IPA_NONE);
    if (d->last_table != d->tab)
      rt_show_table(c, d);
  
-   cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest),
 -  cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(dest),
--           e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
++  cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, 
++      net_is_flow(e->net) ? flowspec_valid_name(flowspec_valid) : rta_dest_name(dest),
++      e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
  
-   if (a->dest == RTD_UNICAST)
+   if (dest == RTD_UNICAST)
      NEXTHOP_WALK(nh, nhad)
      {
        char mpls[MPLS_MAX_LABEL_STACK*12 + 5], *lsp = mpls;
diff --cc nest/rt-table.c
index 491ae1d9ba3b812b263b37e02149c42d53abc715,539e04d01eec71ea033367c64e8b456bd9d7f063..946f4021c94cadebe403c7ed416e2786884ecc0b
@@@ -679,11 -695,11 +679,11 @@@ rte_mergable(rte *pri, rte *sec
  }
  
  static void
 -rte_trace(struct channel *c, rte *e, int dir, char *msg)
 +rte_trace(const char *name, const rte *e, int dir, const char *msg)
  {
 -  log(L_TRACE "%s.%s %c %s %N %uL %uG %s",
 -      c->proto->name, c->name ?: "?", dir, msg, e->net->n.addr, e->src->private_id, e->src->global_id,
 +  log(L_TRACE "%s %c %s %N %uL %uG %s",
 +      name, dir, msg, e->net, e->src->private_id, e->src->global_id,
-       rta_dest_name(e->attrs->dest));
+       rta_dest_name(rte_dest(e)));
  }
  
  static inline void
@@@ -1226,30 -1177,21 +1226,30 @@@ rte_validate(struct channel *ch, rte *e
      return 0;
    }
  
-   if (net_type_match(n, NB_DEST) == !e->attrs->dest)
 -  eattr *nhea = ea_find(e->attrs->eattrs, &ea_gen_nexthop);
 -  int dest = nhea_dest(nhea);
 -
 -  if (net_type_match(n->n.addr, NB_DEST) == !dest)
++  if (net_type_match(n, NB_DEST))
    {
-     /* Exception for flowspec that failed validation */
-     if (net_is_flow(n) && (e->attrs->dest == RTD_UNREACHABLE))
-       return 1;
 -    log(L_WARN "Ignoring route %N with invalid dest %d received via %s",
 -      n->n.addr, dest, e->sender->proto->name);
 -    return 0;
 -  }
++    eattr *nhea = ea_find(e->attrs->eattrs, &ea_gen_nexthop);
++    int dest = nhea_dest(nhea);
 +
-     log(L_WARN "Ignoring route %N with invalid dest %d received via %s",
-       n, e->attrs->dest, ch->proto->name);
-     return 0;
-   }
++    if (dest == RTD_NONE)
++    {
++      log(L_WARN "Ignoring route %N with no destination received via %s",
++        n, ch->proto->name);
++      return 0;
++    }
  
-   eattr *nhea = ea_find(e->attrs->eattrs, &ea_gen_nexthop);
-   if ((!nhea) != (e->attrs->dest != RTD_UNICAST))
-   {
-     log(L_WARN "Ignoring route %N with destination %d and %snexthop received via %s",
-       n, e->attrs->dest, (nhea ? "" : "no "), ch->proto->name);
-     return 0;
 -  if ((dest == RTD_UNICAST) &&
 -      !nexthop_is_sorted((struct nexthop_adata *) nhea->u.ptr))
++    if ((dest == RTD_UNICAST) &&
++      !nexthop_is_sorted((struct nexthop_adata *) nhea->u.ptr))
++    {
++      log(L_WARN "Ignoring unsorted multipath route %N received via %s",
++        n, ch->proto->name);
++      return 0;
++    }
 +  }
-   if ((e->attrs->dest == RTD_UNICAST) &&
-       !nexthop_is_sorted((struct nexthop_adata *) nhea->u.ptr))
++  else if (ea_find(e->attrs->eattrs, &ea_gen_nexthop))
    {
--    log(L_WARN "Ignoring unsorted multipath route %N received via %s",
 -      n->n.addr, e->sender->proto->name);
++    log(L_WARN "Ignoring route %N having a nexthop attribute received via %s",
 +      n, ch->proto->name);
      return 0;
    }
  
@@@ -2722,7 -2594,7 +2736,7 @@@ rta_get_first_asn(rta *a
    return (e && as_path_get_first_regular(e->u.ptr, &asn)) ? asn : 0;
  }
  
--int
++static inline enum flowspec_valid
  rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, int interior)
  {
    ASSERT(rt_is_ip(tab_ip));
  
    /* RFC 8955 6. a) Flowspec has defined dst prefix */
    if (!net_flow_has_dst_prefix(n))
--    return 0;
++    return FLOWSPEC_INVALID;
  
    /* RFC 9117 4.1. Accept  AS_PATH is empty (fr */
    if (interior && rta_as_path_is_empty(a))
--    return 1;
++    return FLOWSPEC_VALID;
  
  
    /* RFC 8955 6. b) Flowspec and its best-match route have the same originator */
  
    /* No best-match BGP route -> no flowspec */
    if (!rb || (rt_get_source_attr(rb) != RTS_BGP))
--    return 0;
++    return FLOWSPEC_INVALID;
  
    /* Find ORIGINATOR_ID values */
    u32 orig_a = ea_get_int(a->eattrs, "bgp_originator_id", 0);
          ea_get_ip(a->eattrs, &ea_gen_from, IPA_NONE),
          ea_get_ip(rb->attrs->eattrs, &ea_gen_from, IPA_NONE)
          )))
--    return 0;
++    return FLOWSPEC_INVALID;
  
  
    /* Find ASN of the best-match route, for use in next checks */
    u32 asn_b = rta_get_first_asn(rb->attrs);
    if (!asn_b)
--    return 0;
++    return FLOWSPEC_INVALID;
  
    /* RFC 9117 4.2. For EBGP, flowspec and its best-match route are from the same AS */
    if (!interior && (rta_get_first_asn(a) != asn_b))
--    return 0;
++    return FLOWSPEC_INVALID;
  
    /* RFC 8955 6. c) More-specific routes are from the same AS as the best-match route */
    TRIE_WALK(tab_ip->trie, subnet, &dst)
      if (!nc)
        continue;
  
 -    rte *rc = nc->routes;
 +    const rte *rc = &nc->routes->rte;
      if (rt_get_source_attr(rc) != RTS_BGP)
--      return 0;
++      return FLOWSPEC_INVALID;
  
      if (rta_get_first_asn(rc->attrs) != asn_b)
--      return 0;
++      return FLOWSPEC_INVALID;
    }
    TRIE_WALK_END;
  
--  return 1;
++  return FLOWSPEC_VALID;
  }
  
  #endif /* CONFIG_BGP */
@@@ -2812,57 -2684,29 +2826,67 @@@ rt_flowspec_update_rte(rtable *tab, ne
    if (!bc->base_table)
      return NULL;
  
 -  const net_addr *n = r->net->n.addr;
--  struct bgp_proto *p = (void *) r->src->proto;
-   int valid = rt_flowspec_check(bc->base_table, tab, n->n.addr, r->attrs, p->is_interior);
-   int dest = valid ? RTD_NONE : RTD_UNREACHABLE;
 -  int valid = rt_flowspec_check(bc->base_table, tab, n, r->attrs, p->is_interior);
 -  int old = rt_get_flowspec_valid(r);
++  struct bgp_proto *p = SKIP_BACK(struct bgp_proto, p, bc->c.proto);
++
++  enum flowspec_valid old = rt_get_flowspec_valid(r),
++                    valid = rt_flowspec_check(bc->base_table, tab, n->n.addr, r->attrs, p->is_interior);
 +
-   if (dest == r->attrs->dest)
+   if (old == valid)
      return NULL;
  
    rta *a = alloca(RTA_MAX_SIZE);
 -  memcpy(a, r->attrs, rta_size(r->attrs));
 +  *a = *r->attrs;
-   a->dest = dest;
    a->cached = 0;
  
 -  rte *new = sl_alloc(rte_slab);
 -  memcpy(new, r, sizeof(rte));
 -  new->attrs = rta_lookup(a);
+   ea_set_attr_u32(&a->eattrs, &ea_gen_flowspec_valid, 0, valid);
 +  rte new;
 +  memcpy(&new, r, sizeof(rte));
 +  new.attrs = a;
  
 -  return new;
 +  return rte_store(&new, n, tab);
  #else
    return NULL;
  #endif
  }
  
-   if (rt_get_source_attr(r) != RTS_BGP)
-     return;
 +static inline void
 +rt_flowspec_resolve_rte(rte *r, struct channel *c)
 +{
 +#ifdef CONFIG_BGP
-   if (!bc->base_table)
-     return;
++  enum flowspec_valid valid, old = rt_get_flowspec_valid(r);
 +  struct bgp_channel *bc = (struct bgp_channel *) c;
-   struct bgp_proto *p = (void *) r->src->proto;
-   int valid = rt_flowspec_check(bc->base_table, c->in_req.hook->table, r->net, r->attrs, p->is_interior);
-   int dest = valid ? RTD_NONE : RTD_UNREACHABLE;
 +
-   if (dest == r->attrs->dest)
++  if (        (rt_get_source_attr(r) == RTS_BGP)
++     && (c->channel == &channel_bgp)
++     && (bc->base_table))
++  {
++    struct bgp_proto *p = SKIP_BACK(struct bgp_proto, p, bc->c.proto);
++    valid = rt_flowspec_check(
++      bc->base_table,
++      c->in_req.hook->table,
++      r->net, r->attrs, p->is_interior);
++  }
++  else
++    valid = FLOWSPEC_UNKNOWN;
 +
-   r->attrs->dest = dest;
++  if (valid == old)
 +    return;
 +
 +  if (r->attrs->cached)
 +  {
 +    rta *a = tmp_alloc(RTA_MAX_SIZE);
 +    *a = *r->attrs;
 +    a->cached = 0;
 +    r->attrs = a;
 +  }
 +
++  if (valid == FLOWSPEC_UNKNOWN)
++    ea_unset_attr(&r->attrs->eattrs, 0, &ea_gen_flowspec_valid);
++  else
++    ea_set_attr_u32(&r->attrs->eattrs, &ea_gen_flowspec_valid, 0, valid);
 +#endif
 +}
  
  static inline int
  rt_next_hop_update_net(rtable *tab, net *n)
@@@ -3694,12 -3567,10 +3713,10 @@@ rt_update_hostentry(rtable *tab, struc
  
                direct++;
              }
-       }
  
        he->src = rta_clone(a);
-       he->dest = a->dest;
        he->nexthop_linkable = !direct;
 -      he->igp_metric = rt_get_igp_metric(e);
 +      he->igp_metric = rt_get_igp_metric(&e->rte);
      }
  
  done:
diff --cc nest/rt.h
index 0ee615b8a49ee9cfebe716e8d067066266c384e8,6f15fec69fd52581e759b08321c23feeaae2e0c2..eb868aa76e1ec21077bd4bc5384a1e8eee1b8518
+++ b/nest/rt.h
@@@ -460,9 -301,9 +459,6 @@@ rta_set_recursive_next_hop(rtable *dep
  }
  */
  
--int rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, int interior);
--
--
  /*
   *    Default protocol preferences
   */
index f3456369a10213a81c10bacab4590e3fe8a087af,b90dcd3f5da322f9eb1ec09bc82588ef80fa08e8..65d2567e614347daa096807d05c3a5efef8b1b26
@@@ -677,18 -677,13 +677,15 @@@ babel_announce_rte(struct babel_proto *
        }
      };
  
-     rta a0 = {
-       .dest = RTD_UNICAST,
-       .eattrs = &eattrs.l,
-     };
+     rta a0 = { .eattrs = &eattrs.l, };
  
 -    rta *a = rta_lookup(&a0);
 -    rte *rte = rte_get_temp(a, p->p.main_source);
 +    rte e0 = {
 +      .attrs = &a0,
 +      .src = p->p.main_source,
 +    };
  
      e->unreachable = 0;
 -    rte_update2(c, e->n.addr, rte, p->p.main_source);
 +    rte_update(c, e->n.addr, &e0, p->p.main_source);
    }
    else if (e->valid && (e->router_id != p->router_id))
    {
  
      ea_set_attr_u32(&a0.eattrs, &ea_gen_preference, 0, 1);
      ea_set_attr_u32(&a0.eattrs, &ea_gen_source, 0, RTS_BABEL);
+     ea_set_dest(&a0.eattrs, 0, RTD_UNREACHABLE);
  
 -    rta *a = rta_lookup(&a0);
 -    rte *rte = rte_get_temp(a, p->p.main_source);
 -    rte->pflags = 0;
 +    rte e0 = {
 +      .attrs = &a0,
 +      .src = p->p.main_source,
 +    };
  
      e->unreachable = 1;
 -    rte_update2(c, e->n.addr, rte, p->p.main_source);
 +    rte_update(c, e->n.addr, &e0, p->p.main_source);
    }
    else
    {
@@@ -2264,11 -2257,15 +2260,15 @@@ babel_kick_timer(struct babel_proto *p
  
  
  static int
 -babel_preexport(struct proto *P, struct rte *new)
 +babel_preexport(struct channel *c, struct rte *new)
  {
-   struct rta *a = new->attrs;
 -  if (new->src->proto != P)
++  if (new->src->proto != c->proto)
+     return 0;
    /* Reject our own unreachable routes */
-   if ((a->dest == RTD_UNREACHABLE) && (new->src->proto == c->proto))
+   eattr *ea = ea_find(new->attrs->eattrs, &ea_gen_nexthop);
+   struct nexthop_adata *nhad = (void *) ea->u.ptr;
+   if (!NEXTHOP_IS_REACHABLE(nhad))
      return -1;
  
    return 0;
index ccfda4df95ebf38e005c7ac37421c68c195e4d8a,6a9e4026c9834fc54898a2b4a94619e38346ad89..0b715eaa60fb080de7444c472f731e4cceb22ca9
@@@ -1714,9 -1711,20 +1714,21 @@@ bgp_preexport(struct channel *c, rte *e
    if (src == NULL)
      return 0;
  
 -  /* Reject flowspec that failed or are pending validation */
 -  if (net_is_flow(e->net->n.addr))
 +  /* Reject flowspec that failed validation */
-   if ((e->attrs->dest == RTD_UNREACHABLE) && net_is_flow(e->net))
-     return -1;
++  if (net_is_flow(e->net))
+     switch (rt_get_flowspec_valid(e))
+     {
+       case FLOWSPEC_VALID:
+       break;
+       case FLOWSPEC_INVALID:
+       return -1;
+       case FLOWSPEC_UNKNOWN:
 -      if ((rt_get_source_attr(e) == RTS_BGP) &&
 -          ((struct bgp_channel *) e->sender)->base_table)
 -        return -1;
++      ASSUME((rt_get_source_attr(e) != RTS_BGP) ||
++          !((struct bgp_channel *) SKIP_BACK(struct channel, in_req, e->sender->req))->base_table);
+       break;
++      case FLOWSPEC__MAX:
++      bug("This never happens.");
+     }
  
    /* IBGP route reflection, RFC 4456 */
    if (p->is_internal && src->is_internal && (p->local_as == src->local_as))
diff --cc proto/bgp/bgp.h
index 8e3ed70e8b2778b7ec3597e44a4855a9599a9f4d,2f98dc1b540655a4a6caa9521798667036a67dc7..662d9d48a3422acc4c1c7f5e0541794f053ec201
@@@ -517,9 -517,11 +517,11 @@@ struct rte_source *bgp_find_source(stru
  struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id);
  
  static inline int
 -rte_resolvable(rte *rt)
 +rta_resolvable(rta *a)
  {
-   return a->dest != RTD_UNREACHABLE;
 -  eattr *nhea = ea_find(rt->attrs->eattrs, &ea_gen_nexthop);
++  eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop);
+   struct nexthop_adata *nhad = (void *) nhea->u.ptr;
+   return NEXTHOP_IS_REACHABLE(nhad) || (nhad->dest != RTD_UNREACHABLE);
  }
  
  
Simple merge
diff --cc proto/ospf/rt.c
Simple merge
index c2b12cfc4c90c0d2bcff81104a17572cec1d6a85,09ec9f28261e29d7e73243020e489072f7e83ed9..6ff6a74577569798a020229859915923c11d02fb
@@@ -1366,20 -1366,13 +1366,11 @@@ ospf_rt_notify(struct proto *P, struct 
    uint tag = ea_get_int(a->eattrs, &ea_ospf_tag, 0);
  
    ip_addr fwd = IPA_NONE;
-   if (a->dest == RTD_UNICAST)
-   {
-     eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop);
-     if (!nhea)
-     {
-       log(L_ERR "%s: Unicast route without nexthop for %N",
-         p->p.name, n);
-       return;
-     }
-     struct nexthop_adata *nhad = (struct nexthop_adata *) nhea->u.ptr;
+   eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop);
 -  if (nhea)
 -  {
 -    struct nexthop_adata *nhad = (struct nexthop_adata *) nhea->u.ptr;
++  struct nexthop_adata *nhad = (struct nexthop_adata *) nhea->u.ptr;
++  if (NEXTHOP_IS_REACHABLE(nhad))
      if (use_gw_for_fwaddr(p, nhad->nh.gw, nhad->nh.iface))
        fwd = nhad->nh.gw;
--  }
  
    /* NSSA-LSA with P-bit set must have non-zero forwarding address */
    if (oa && ipa_zero(fwd))
Simple merge
diff --cc proto/rip/rip.c
Simple merge
Simple merge
Simple merge
index e63fe938246e562655e78fc5e829a7b09131df2c,40f6212ec61d82a738ff55565fba5444d8af42c7..a4172a6d4c7064cabc79df548ba93460d8575d34
@@@ -1900,11 -1908,9 +1907,9 @@@ nl_parse_route(struct nl_parse_state *s
    switch (i->rtm_type)
      {
      case RTN_UNICAST:
-       ra->dest = RTD_UNICAST;
        if (a[RTA_MULTIPATH])
          {
 -        struct nexthop_adata *nh = nl_parse_multipath(s, p, n, a[RTA_MULTIPATH], i->rtm_family, krt_src);
 +        struct nexthop_adata *nh = nl_parse_multipath(s, p, net, a[RTA_MULTIPATH], i->rtm_family, krt_src);
          if (!nh)
            SKIP("strange RTA_MULTIPATH\n");
  
Simple merge