From ff2857b03db854f99902766ad842aaa5fa29ec3c Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 26 Feb 2010 10:55:58 +0100 Subject: [PATCH] Many changes in (mainly) kernel syncers. - BSD kernel syncer is now self-conscious and can learn alien routes - important bugfix in BSD kernel syncer (crash after protocol restart) - many minor changes and bugfixes in kernel syncers and neighbor cache - direct protocol does not generate host and link local routes - min_scope check is removed, all routes have SCOPE_UNIVERSE by default - also fixes some remaining compiler warnings --- doc/bird.sgml | 15 +- lib/ip.h | 4 + nest/iface.h | 1 + nest/neighbor.c | 23 ++- nest/proto.c | 1 - nest/protocol.h | 1 - nest/rt-dev.c | 7 +- nest/rt-table.c | 52 ++----- proto/bgp/packets.c | 3 - proto/ospf/ospf.c | 12 +- proto/ospf/rt.c | 6 +- sysdep/bsd/krt-sock.c | 250 +++++++++++++++++---------------- sysdep/cf/bsd-v6.h | 2 +- sysdep/cf/bsd.h | 2 +- sysdep/linux/netlink/netlink.c | 151 ++++++++------------ sysdep/unix/io.c | 2 +- sysdep/unix/krt.c | 40 +++--- sysdep/unix/krt.h | 1 + sysdep/unix/unix.h | 4 + 19 files changed, 279 insertions(+), 298 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 3d82e45b..b70f0345 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -824,7 +824,14 @@ defined by using the defined( attribute ) operator. Network the route is talking about. Read-only. (See the chapter about routing tables.) - Address scope of the network ( Preference of the route. Valid values are 0-65535. (See the chapter about routing tables.) @@ -842,7 +849,11 @@ defined by using the defined( attribute ) operator. what protocol has told me about this route. Possible values: - Route type ( Type of destination the packets should be sent to (proto == p && ipa_equal(*a, n->addr)) + if (n->proto == p && ipa_equal(*a, n->addr) && (!ifa || (ifa == n->iface))) return n; class = ipa_classify(*a); @@ -129,7 +129,12 @@ neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags) return NULL; /* Bad scope or a somecast */ if (ifa) - scope = if_connected(a, ifa); + { + scope = if_connected(a, ifa); + + if ((scope < 0) && (flags & NEF_ONLINK)) + scope = class & IADDR_SCOPE_MASK; + } else WALK_LIST(i, iface_list) if ((scope = if_connected(a, i)) >= 0) @@ -138,22 +143,28 @@ neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags) break; } - if (!ifa && !(flags & NEF_STICKY)) + /* scope < 0 means i don't know neighbor */ + /* scope >= 0 implies ifa != NULL */ + + if ((scope < 0) && !(flags & NEF_STICKY)) return NULL; n = sl_alloc(neigh_slab); n->addr = *a; - n->iface = ifa; - if (ifa) + if (scope >= 0) { add_tail(&neigh_hash_table[h], &n->n); add_tail(&ifa->neighbors, &n->if_n); } else { + /* sticky flag does not work for link-local neighbors; + fortunately, we don't use this combination */ add_tail(&sticky_neigh_list, &n->n); + ifa = NULL; scope = 0; } + n->iface = ifa; n->proto = p; n->data = NULL; n->aux = 0; diff --git a/nest/proto.c b/nest/proto.c index 48837055..db6bf9bf 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -113,7 +113,6 @@ proto_new(struct proto_config *c, unsigned size) p->table = c->table->table; p->in_filter = c->in_filter; p->out_filter = c->out_filter; - p->min_scope = SCOPE_SITE; p->hash_key = random_u32(); c->proto = p; return p; diff --git a/nest/protocol.h b/nest/protocol.h index 99356a3d..d94873e4 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -130,7 +130,6 @@ struct proto { u32 debug; /* Debugging flags */ u32 mrtdump; /* MRTDump flags */ unsigned preference; /* Default route preference */ - int min_scope; /* Minimal route scope accepted */ unsigned accept_ra_types; /* Which types of route announcements are accepted (RA_OPTIMAL or RA_ANY) */ unsigned disabled; /* Manually disabled */ unsigned proto_state; /* Protocol state machine (see below) */ diff --git a/nest/rt-dev.c b/nest/rt-dev.c index b86015df..bb8eb8ee 100644 --- a/nest/rt-dev.c +++ b/nest/rt-dev.c @@ -33,6 +33,10 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) !iface_patt_find(&P->iface_list, ad->iface)) /* Empty list is automagically treated as "*" */ return; + + if (ad->scope <= SCOPE_LINK) + return; + if (c & IF_CHANGE_DOWN) { net *n; @@ -56,7 +60,7 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) bzero(&A, sizeof(A)); A.proto = p; A.source = RTS_DEVICE; - A.scope = ad->scope; + A.scope = SCOPE_UNIVERSE; A.cast = RTC_UNICAST; A.dest = RTD_DEVICE; A.iface = ad->iface; @@ -76,7 +80,6 @@ dev_init(struct proto_config *c) struct proto *p = proto_new(c, sizeof(struct proto)); p->ifa_notify = dev_ifa_notify; - p->min_scope = SCOPE_HOST; return p; } diff --git a/nest/rt-table.c b/nest/rt-table.c index 41ecf646..1860b1a1 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -158,7 +158,7 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg) } static inline void -do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int class, int refeed) +do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int refeed) { struct proto *p = a->proto; struct filter *filter = p->out_filter; @@ -183,13 +183,7 @@ do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rt stats->exp_updates_received++; char *drop_reason = NULL; - if ((class & IADDR_SCOPE_MASK) < p->min_scope) - { - stats->exp_updates_rejected++; - drop_reason = "out of scope"; - fast_exit_hack = 1; - } - else if ((ok = p->import_control ? p->import_control(p, &new, &tmpa, rte_update_pool) : 0) < 0) + if ((ok = p->import_control ? p->import_control(p, &new, &tmpa, rte_update_pool) : 0) < 0) { stats->exp_updates_rejected++; drop_reason = "rejected by protocol"; @@ -332,7 +326,6 @@ static void rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, ea_list *tmpa) { struct announce_hook *a; - int class = ipa_classify(net->n.prefix); if (type == RA_OPTIMAL) { @@ -346,7 +339,7 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, ea_list * { ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING); if (a->proto->accept_ra_types == type) - do_rte_announce(a, type, net, new, old, tmpa, class, 0); + do_rte_announce(a, type, net, new, old, tmpa, 0); } } @@ -362,33 +355,15 @@ rte_validate(rte *e) n->n.prefix, n->n.pxlen, e->sender->name); return 0; } - if (n->n.pxlen) + + c = ipa_classify_net(n->n.prefix); + if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) { - c = ipa_classify(n->n.prefix); - if (c < 0 || !(c & IADDR_HOST)) - { - if (!ipa_nonzero(n->n.prefix)) - { - /* Various default routes */ -#ifdef IPV6 - if (n->n.pxlen == 96) -#else - if (n->n.pxlen <= 1) -#endif - return 1; - } - log(L_WARN "Ignoring bogus route %I/%d received via %s", - n->n.prefix, n->n.pxlen, e->sender->name); - return 0; - } - if ((c & IADDR_SCOPE_MASK) < e->sender->min_scope) - { - log(L_WARN "Ignoring %s scope route %I/%d received from %I via %s", - ip_scope_text(c & IADDR_SCOPE_MASK), - n->n.prefix, n->n.pxlen, e->attrs->from, e->sender->name); - return 0; - } + log(L_WARN "Ignoring bogus route %I/%d received via %s", + n->n.prefix, n->n.pxlen, e->sender->name); + return 0; } + return 1; } @@ -1018,7 +993,7 @@ do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e) rte_update_lock(); tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL; - do_rte_announce(h, type, n, e, p->refeeding ? e : NULL, tmpa, ipa_classify(n->n.prefix), p->refeeding); + do_rte_announce(h, type, n, e, p->refeeding ? e : NULL, tmpa, p->refeeding); rte_update_unlock(); } @@ -1190,11 +1165,8 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) if (p2 && p2 != p0) ok = 0; if (ok && d->export_mode) { - int class = ipa_classify(n->n.prefix); int ic; - if ((class & IADDR_SCOPE_MASK) < p1->min_scope) - ok = 0; - else if ((ic = p1->import_control ? p1->import_control(p1, &e, &tmpa, rte_update_pool) : 0) < 0) + if ((ic = p1->import_control ? p1->import_control(p1, &e, &tmpa, rte_update_pool) : 0) < 0) ok = 0; else if (!ic && d->export_mode > 1) { diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 3609c568..2baa6e34 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -914,7 +914,6 @@ bgp_do_rx_update(struct bgp_conn *conn, rta *a = NULL; ip_addr prefix; net *n; - rte e; int err = 0, pxlen; p->mp_reach_len = 0; @@ -936,8 +935,6 @@ bgp_do_rx_update(struct bgp_conn *conn, DO_NLRI(mp_reach) { - int i; - /* Create fake NEXT_HOP attribute */ if (len < 1 || (*x != 16 && *x != 32) || len < *x + 2) goto bad; diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index edca5959..191aa456 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -1234,7 +1234,6 @@ show_lsa_sum_rt(struct top_hash_entry *he) static inline void show_lsa_external(struct top_hash_entry *he) { - struct ospf_lsa_header *lsa = &(he->lsa); struct ospf_lsa_ext *ext = he->lsa_body; char str_via[STD_ADDRESS_P_LENGTH + 8] = ""; char str_tag[16] = ""; @@ -1245,7 +1244,7 @@ show_lsa_external(struct top_hash_entry *he) rt_metric = ext->metric & METRIC_MASK; ebit = ext->metric & LSA_EXT_EBIT; #ifdef OSPFv2 - ip = ipa_and(ipa_from_u32(lsa->id), ext->netmask); + ip = ipa_and(ipa_from_u32(he->lsa.id), ext->netmask); pxlen = ipa_mklen(ext->netmask); rt_fwaddr = ext->fwaddr; rt_fwaddr_valid = !ipa_equal(rt_fwaddr, IPA_NONE); @@ -1282,10 +1281,7 @@ show_lsa_external(struct top_hash_entry *he) static inline void show_lsa_prefix(struct top_hash_entry *he, struct ospf_lsa_header *olsa) { - struct ospf_lsa_header *lsa = &(he->lsa); struct ospf_lsa_prefix *px = he->lsa_body; - struct ospf_lsa_ext *ext = he->lsa_body; - char *msg; ip_addr pxa; int pxlen; u8 pxopts; @@ -1504,8 +1500,10 @@ ospf_sh_lsadb(struct proto *p) break; #ifdef OSPFv3 case LSA_SCOPE_LINK: - struct iface *ifa = if_find_by_index(hea[i]->domain); - cli_msg(-1017, "Link %s", (ifa != NULL) ? ifa->name : "?"); + { + struct iface *ifa = if_find_by_index(hea[i]->domain); + cli_msg(-1017, "Link %s", (ifa != NULL) ? ifa->name : "?"); + } break; #endif } diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 84e47252..c856eea1 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -170,7 +170,7 @@ static void process_prefixes(struct ospf_area *oa) { struct proto_ospf *po = oa->po; - struct proto *p = &po->proto; + // struct proto *p = &po->proto; struct top_hash_entry *en, *src; struct ospf_lsa_prefix *px; ip_addr pxa; @@ -228,7 +228,6 @@ ospf_rt_spfa_rtlinks(struct ospf_area *oa, struct top_hash_entry *act, struct to { // struct proto *p = &oa->po->proto; struct proto_ospf *po = oa->po; - orta nf; u32 i; struct ospf_lsa_rt *rt = en->lsa_body; @@ -249,6 +248,7 @@ ospf_rt_spfa_rtlinks(struct ospf_area *oa, struct top_hash_entry *act, struct to */ DBG("\n"); + orta nf; nf.type = RTS_OSPF; nf.options = 0; nf.metric1 = act->dist + rtl->metric; @@ -571,7 +571,7 @@ ospf_rt_sum_tr(struct ospf_area *oa) type = ORT_NET; re = (ort *) fib_find(&po->rtf, &ip, pxlen); } - else if (en->lsa.type == LSA_T_SUM_RT) + else // en->lsa.type == LSA_T_SUM_RT { #ifdef OSPFv2 struct ospf_lsa_sum *ls = en->lsa_body; diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index a5279657..dac2c2e6 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -33,39 +33,8 @@ #include "lib/string.h" #include "lib/socket.h" -#ifdef IPV6 -#define HOST_MASK 128 -#else -#define HOST_MASK 32 -#endif - int rt_sock = 0; -#define CHECK_FAMILY(sa) \ - ((((struct sockaddr *)sa)->sa_family) == BIRD_AF) - -static struct iface * -krt_temp_iface_index(struct krt_proto *p, unsigned index) -{ - struct iface *i, *j; - - WALK_LIST(i, p->scan.temp_ifs) - if (i->index == index) - return i; - i = mb_allocz(p->p.pool, sizeof(struct iface)); - if (j = if_find_by_index(index)) - { - strcpy(i->name, j->name); - i->addr = j->addr; - } - else - strcpy(i->name, "?"); - i->index = index; - add_tail(&p->scan.temp_ifs, &i->n); - return i; -} - - int krt_capable(rte *e) { @@ -83,7 +52,7 @@ krt_capable(rte *e) || a->dest == RTD_UNREACHABLE #endif #ifdef RTF_BLACKHOLE - || a->dest == RTD_BLACKHOLE /* FIXME Prohibited? */ + || a->dest == RTD_BLACKHOLE #endif ); } @@ -96,6 +65,13 @@ krt_capable(rte *e) l = ROUNDUP(((struct sockaddr *)&(u))->sa_len);\ memmove(body, &(u), l); body += l;} +#define GETADDR(p, F) \ + bzero(p, sizeof(*p));\ + if ((addrs & (F)) && ((struct sockaddr *)body)->sa_len) {\ + unsigned int l = ROUNDUP(((struct sockaddr *)body)->sa_len);\ + memcpy(p, body, (l > sizeof(*p) ? sizeof(*p) : l));\ + body += l;} + static void krt_sock_send(int cmd, rte *e) { @@ -108,7 +84,7 @@ krt_sock_send(int cmd, rte *e) char *body = (char *)msg.buf; sockaddr gate, mask, dst; - DBG("krt-sock: send %I/%d via %I", net->n.prefix, net->n.pxlen, a->gw); + DBG("krt-sock: send %I/%d via %I\n", net->n.prefix, net->n.pxlen, a->gw); fill_in_sockaddr(&dst, net->n.prefix, 0); fill_in_sockaddr(&mask, ipa_mkmask(net->n.pxlen), 0); @@ -119,9 +95,9 @@ krt_sock_send(int cmd, rte *e) msg.rtm.rtm_type = cmd; msg.rtm.rtm_seq = msg_seq++; msg.rtm.rtm_addrs = RTA_DST; - msg.rtm.rtm_flags = RTF_UP; + msg.rtm.rtm_flags = RTF_UP | RTF_PROTO1; - if (net->n.pxlen == HOST_MASK) + if (net->n.pxlen == MAX_PREFIX_LENGTH) { msg.rtm.rtm_flags |= RTF_HOST; } @@ -200,12 +176,12 @@ krt_sock_send(int cmd, rte *e) msg.rtm.rtm_msglen = l; if ((l = write(rt_sock, (char *)&msg, l)) < 0) { - log(L_ERR "KIF: error writting route to socket (%I/%d)", net->n.prefix, net->n.pxlen); + log(L_ERR "KIF: Error sending route %I/%d to kernel", net->n.prefix, net->n.pxlen); } } void -krt_set_notify(struct krt_proto *p UNUSED, net *net UNUSED, rte *new, rte *old) +krt_set_notify(struct krt_proto *p UNUSED, net *net, rte *new, rte *old) { if (old) { @@ -258,68 +234,87 @@ krt_set_start(struct krt_proto *x, int first UNUSED) bug("krt-sock: sk_open failed"); } +#define SKIP(ARG...) do { DBG("KRT: Ignoring route - " ARG); return; } while(0) + static void krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan) { - sockaddr gate, mask, dst; rta a; rte *e; net *net; + sockaddr dst, gate, mask; ip_addr idst, igate, imask; void *body = (char *)msg->buf; int new = (msg->rtm.rtm_type == RTM_ADD); int src; + char *errmsg = "KRT: Invalid route received"; int flags = msg->rtm.rtm_flags; int addrs = msg->rtm.rtm_addrs; - int masklen = -1; - if (!(flags & RTF_UP)) - { - DBG("Down.\n"); - return; - } + if (!(flags & RTF_UP) && scan) + SKIP("not up in scan\n"); - if (flags & RTF_HOST) - masklen = HOST_MASK; + if (!(flags & RTF_DONE) && !scan) + SKIP("not done in async\n"); - if(!CHECK_FAMILY(body)) return; + if (flags & RTF_LLINFO) + SKIP("link-local\n"); - if(msg->rtm.rtm_flags & RTF_LLINFO) return; /* ARPs etc. */ + GETADDR(&dst, RTA_DST); + GETADDR(&gate, RTA_GATEWAY); + GETADDR(&mask, RTA_NETMASK); -#define GETADDR(p, F) \ - bzero(p, sizeof(*p));\ - if ((addrs & (F)) && ((struct sockaddr *)body)->sa_len) {\ - unsigned int l = ROUNDUP(((struct sockaddr *)body)->sa_len);\ - memcpy(p, body, (l > sizeof(*p) ? sizeof(*p) : l));\ - body += l;} + if (sa_family_check(&dst)) + get_sockaddr(&dst, &idst, NULL, 0); + else + SKIP("invalid DST"); - GETADDR (&dst, RTA_DST); - GETADDR (&gate, RTA_GATEWAY); - GETADDR (&mask, RTA_NETMASK); + /* We will check later whether we have valid gateway addr */ + if (sa_family_check(&gate)) + get_sockaddr(&gate, &igate, NULL, 0); + else + igate = IPA_NONE; - idst = IPA_NONE; - igate = IPA_NONE; - imask = IPA_NONE; - - get_sockaddr(&dst, &idst, NULL, 0); - if(CHECK_FAMILY(&gate)) get_sockaddr(&gate, &igate, NULL, 0); + /* We do not test family for RTA_NETMASK, because BSD sends us + some strange values, but interpreting them as IPv4/IPv6 works */ get_sockaddr(&mask, &imask, NULL, 0); - if (masklen < 0) masklen = ipa_mklen(imask); + int c = ipa_classify_net(idst); + if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) + SKIP("strange class/scope\n"); + + int pxlen = (flags & RTF_HOST) ? MAX_PREFIX_LENGTH : ipa_mklen(imask); + if (pxlen < 0) + { log(L_ERR "%s (%I) - netmask %I", errmsg, idst, imask); return; } + + if ((flags & RTF_GATEWAY) && ipa_zero(igate)) + { log(L_ERR "%s (%I/%d) - missing gateway", errmsg, idst, pxlen); return; } + + u32 self_mask = RTF_PROTO1; + u32 alien_mask = RTF_STATIC | RTF_PROTO1; + +#ifdef RTF_PROTO2 + alien_mask |= RTF_PROTO2; +#endif + +#ifdef RTF_PROTO3 + alien_mask |= RTF_PROTO3; +#endif if (flags & (RTF_DYNAMIC | RTF_MODIFIED)) - { - log(L_WARN "krt: Ignoring redirect to %I/%d via %I", idst, masklen, igate); - return; - } + src = KRT_SRC_REDIRECT; + else if (flags & self_mask) + { + if (!scan) + SKIP("echo\n"); + src = KRT_SRC_BIRD; + } + else if (flags & alien_mask) + src = KRT_SRC_ALIEN; + else + src = KRT_SRC_KERNEL; - if (masklen < 0) - { - log(L_WARN "krt: Got invalid route from kernel!"); - return; - } - - net = net_get(p->p.table, idst, masklen); + net = net_get(p->p.table, idst, pxlen); bzero(&a, sizeof(a)); @@ -333,56 +328,56 @@ krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan) a.iface = NULL; a.eattrs = NULL; - a.dest = RTD_NONE; - - if (flags & RTF_GATEWAY) - { - neighbor *ng = neigh_find(&p->p, &igate, 0); - if (ng && ng->scope) - a.iface = ng->iface; - else - { - log(L_WARN "Kernel told us to use non-neighbor %I for %I/%d", igate, net->n.prefix, net->n.pxlen); - return; - } - - a.dest = RTD_ROUTER; - a.gw = igate; - } - else - { - a.dest = RTD_DEVICE; - a.gw = IPA_NONE; - a.iface = krt_temp_iface_index(p, msg->rtm.rtm_index); - } + /* reject/blackhole routes have also set RTF_GATEWAY, + we wil check them first. */ #ifdef RTF_REJECT if(flags & RTF_REJECT) { a.dest = RTD_UNREACHABLE; - a.gw = IPA_NONE; + goto done; } #endif #ifdef RTF_BLACKHOLE if(flags & RTF_BLACKHOLE) { a.dest = RTD_BLACKHOLE; - a.gw = IPA_NONE; + goto done; } #endif - if (a.dest == RTD_NONE) + a.iface = if_find_by_index(msg->rtm.rtm_index); + if (!a.iface) + { + log(L_ERR "KRT: Received route %I/%d with unknown ifindex %u", + net->n.prefix, net->n.pxlen, msg->rtm.rtm_index); + return; + } + + if (flags & RTF_GATEWAY) { - log(L_WARN "Kernel reporting unknown route type to %I/%d", net->n.prefix, net->n.pxlen); - return; + neighbor *ng; + a.dest = RTD_ROUTER; + a.gw = igate; + + ng = neigh_find2(&p->p, &a.gw, a.iface, 0); + if (!ng || (ng->scope == SCOPE_HOST)) + { + log(L_ERR "KRT: Received route %I/%d with strange next-hop %I", + net->n.prefix, net->n.pxlen, a.gw); + return; + } } + else + a.dest = RTD_DEVICE; - src = KRT_SRC_UNKNOWN; /* FIXME */ - + done: e = rte_get_temp(&a); e->net = net; e->u.krt.src = src; - //e->u.krt.proto = i->rtm_protocol; - //e->u.krt.type = i->rtm_type; + + /* These are probably too Linux-specific */ + e->u.krt.proto = 0; + e->u.krt.type = 0; e->u.krt.metric = 0; if (scan) @@ -471,6 +466,10 @@ krt_read_addr(struct ks_msg *msg) int scope, masklen = -1; int new = (ifam->ifam_type == RTM_NEWADDR); + /* Strange messages with zero (invalid) ifindex appear on OpenBSD */ + if (ifam->ifam_index == 0) + return; + if(!(iface = if_find_by_index(ifam->ifam_index))) { log(L_ERR "KIF: Received address message for unknown interface %d", ifam->ifam_index); @@ -486,7 +485,9 @@ krt_read_addr(struct ks_msg *msg) GETADDR (&null, RTA_AUTHOR); GETADDR (&brd, RTA_BRD); - if(!CHECK_FAMILY(&addr)) return; /* Some other family address */ + /* Some other family address */ + if (!sa_family_check(&addr)) + return; get_sockaddr(&addr, &iaddr, NULL, 0); get_sockaddr(&mask, &imask, NULL, 0); @@ -593,27 +594,27 @@ krt_sysctl_scan(struct proto *p, pool *pool, byte **buf, size_t *bl, int cmd) mib[4] = cmd; mib[5] = 0; - if( sysctl(mib, 6 , NULL , &needed, NULL, 0) < 0) + if (sysctl(mib, 6 , NULL , &needed, NULL, 0) < 0) { die("RT scan..."); } obl = *bl; - while(needed > *bl) *bl *= 2; - while(needed < (*bl/2)) *bl /= 2; + while (needed > *bl) *bl *= 2; + while (needed < (*bl/2)) *bl /= 2; - if( (obl!=*bl) || !*buf) + if ((obl!=*bl) || !*buf) { - if(*buf) mb_free(*buf); - if( (*buf = mb_alloc(pool, *bl)) == NULL ) die("RT scan buf alloc"); + if (*buf) mb_free(*buf); + if ((*buf = mb_alloc(pool, *bl)) == NULL) die("RT scan buf alloc"); } on = needed; - if( sysctl(mib, 6 , *buf, &needed, NULL, 0) < 0) + if (sysctl(mib, 6 , *buf, &needed, NULL, 0) < 0) { - if(on != needed) return; /* The buffer size changed since last sysctl */ + if (on != needed) return; /* The buffer size changed since last sysctl */ die("RT scan 2"); } @@ -624,22 +625,23 @@ krt_sysctl_scan(struct proto *p, pool *pool, byte **buf, size_t *bl, int cmd) } } +static byte *krt_buffer = NULL; +static byte *kif_buffer = NULL; +static size_t krt_buflen = 32768; +static size_t kif_buflen = 4096; + void krt_scan_fire(struct krt_proto *p) { - static byte *buf = NULL; - static size_t bl = 32768; - krt_sysctl_scan((struct proto *)p , p->krt_pool, &buf, &bl, NET_RT_DUMP); + krt_sysctl_scan((struct proto *)p, p->krt_pool, &krt_buffer, &krt_buflen, NET_RT_DUMP); } void krt_if_scan(struct kif_proto *p) { - static byte *buf = NULL; - static size_t bl = 4096; struct proto *P = (struct proto *)p; if_start_update(); - krt_sysctl_scan(P, P->pool, &buf, &bl, NET_RT_IFLIST); + krt_sysctl_scan(P, P->pool, &kif_buffer, &kif_buflen, NET_RT_IFLIST); if_end_update(); } @@ -652,7 +654,9 @@ krt_set_construct(struct krt_config *c UNUSED) void krt_set_shutdown(struct krt_proto *x UNUSED, int last UNUSED) { -} + mb_free(krt_buffer); + krt_buffer = NULL; +} void krt_if_io_init(void) @@ -672,5 +676,7 @@ krt_if_start(struct kif_proto *p UNUSED) void krt_if_shutdown(struct kif_proto *p UNUSED) { + mb_free(kif_buffer); + kif_buffer = NULL; } diff --git a/sysdep/cf/bsd-v6.h b/sysdep/cf/bsd-v6.h index f3aefeb4..66985abf 100644 --- a/sysdep/cf/bsd-v6.h +++ b/sysdep/cf/bsd-v6.h @@ -9,7 +9,7 @@ #define IPV6 #define CONFIG_AUTO_ROUTES -#undef CONFIG_SELF_CONSCIOUS +#define CONFIG_SELF_CONSCIOUS #undef CONFIG_MULTIPLE_TABLES #undef CONFIG_UNIX_IFACE diff --git a/sysdep/cf/bsd.h b/sysdep/cf/bsd.h index 72b24720..acd1b58b 100644 --- a/sysdep/cf/bsd.h +++ b/sysdep/cf/bsd.h @@ -7,7 +7,7 @@ */ #define CONFIG_AUTO_ROUTES -#undef CONFIG_SELF_CONSCIOUS +#define CONFIG_SELF_CONSCIOUS #undef CONFIG_MULTIPLE_TABLES #undef CONFIG_UNIX_IFACE diff --git a/sysdep/linux/netlink/netlink.c b/sysdep/linux/netlink/netlink.c index f45fe159..b59b32f3 100644 --- a/sysdep/linux/netlink/netlink.c +++ b/sysdep/linux/netlink/netlink.c @@ -52,7 +52,6 @@ struct nl_sock static struct nl_sock nl_scan = {.fd = -1}; /* Netlink socket for synchronous scan */ static struct nl_sock nl_req = {.fd = -1}; /* Netlink socket for requests */ - static void nl_open_sock(struct nl_sock *nl) { @@ -555,23 +554,7 @@ krt_set_notify(struct krt_proto *p, net *n UNUSED, rte *new, rte *old) nl_send_route(p, new, 1); } -static struct iface * -krt_temp_iface(struct krt_proto *p, unsigned index) -{ - struct iface *i, *j; - - WALK_LIST(i, p->scan.temp_ifs) - if (i->index == index) - return i; - i = mb_allocz(p->p.pool, sizeof(struct iface)); - if (j = if_find_by_index(index)) - strcpy(i->name, j->name); - else - strcpy(i->name, "?"); - i->index = index; - add_tail(&p->scan.temp_ifs, &i->n); - return i; -} +#define SKIP(ARG...) do { DBG("KRT: Ignoring route - " ARG); return; } while(0) static void nl_parse_route(struct nlmsghdr *h, int scan) @@ -599,31 +582,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) #endif (a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr))) { - log(L_ERR "nl_parse_route: Malformed message received"); - return; - } - - p = nl_table_map[i->rtm_table]; /* Do we know this table? */ - if (!p) - return; - -#ifdef IPV6 - if (a[RTA_IIF]) - { - DBG("KRT: Ignoring route with IIF set\n"); - return; - } -#else - if (i->rtm_tos != 0) /* We don't support TOS */ - { - DBG("KRT: Ignoring route with TOS %02x\n", i->rtm_tos); - return; - } -#endif - - if (scan && !new) - { - DBG("KRT: Ignoring route deletion\n"); + log(L_ERR "KRT: Malformed message received"); return; } @@ -634,33 +593,57 @@ nl_parse_route(struct nlmsghdr *h, int scan) } else dst = IPA_NONE; + if (a[RTA_OIF]) memcpy(&oif, RTA_DATA(a[RTA_OIF]), sizeof(oif)); else oif = ~0; - DBG("Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, i->rtm_table, i->rtm_protocol, p->p.name); + DBG("KRT: Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, i->rtm_table, i->rtm_protocol, p->p.name); + + p = nl_table_map[i->rtm_table]; /* Do we know this table? */ + if (!p) + SKIP("unknown table %d", i->rtm_table); + +#ifdef IPV6 + if (a[RTA_IIF]) + SKIP("IIF set\n"); +#else + if (i->rtm_tos != 0) /* We don't support TOS */ + SKIP("TOS %02x\n", i->rtm_tos); +#endif + + if (scan && !new) + SKIP("RTM_DELROUTE in scan\n"); + + int c = ipa_classify_net(dst); + if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) + SKIP("strange class/scope\n"); + + // ignore rtm_scope, it is not a real scope + // if (i->rtm_scope != RT_SCOPE_UNIVERSE) + // SKIP("scope %u\n", i->rtm_scope); switch (i->rtm_protocol) { + case RTPROT_UNSPEC: + SKIP("proto unspec\n"); + case RTPROT_REDIRECT: src = KRT_SRC_REDIRECT; break; + case RTPROT_KERNEL: - DBG("Route originated in kernel, ignoring\n"); + src = KRT_SRC_KERNEL; return; + case RTPROT_BIRD: -#ifdef IPV6 - case RTPROT_BOOT: - /* Current Linux kernels don't remember rtm_protocol for IPv6 routes and supply RTPROT_BOOT instead */ -#endif if (!scan) - { - DBG("Echo of our own route, ignoring\n"); - return; - } + SKIP("echo\n"); src = KRT_SRC_BIRD; break; + + case RTPROT_BOOT: default: src = KRT_SRC_ALIEN; } @@ -679,52 +662,48 @@ nl_parse_route(struct nlmsghdr *h, int scan) switch (i->rtm_type) { case RTN_UNICAST: - if (oif == ~0U) + ra.iface = if_find_by_index(oif); + if (!ra.iface) { - log(L_ERR "KRT: Mysterious route with no OIF (%I/%d)", net->n.prefix, net->n.pxlen); + log(L_ERR "KRT: Received route %I/%d with unknown ifindex %u", + net->n.prefix, net->n.pxlen, oif); return; } + if (a[RTA_GATEWAY]) { - struct iface *ifa = if_find_by_index(oif); neighbor *ng; ra.dest = RTD_ROUTER; memcpy(&ra.gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ra.gw)); ipa_ntoh(ra.gw); - if (i->rtm_flags & RTNH_F_ONLINK) + ng = neigh_find2(&p->p, &ra.gw, ra.iface, + (i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0); + if (!ng || (ng->scope == SCOPE_HOST)) { - /* route with 'onlink' attribute */ - ra.iface = if_find_by_index(oif); - if (ra.iface == NULL) - { - log(L_WARN "Kernel told us to use unknown interface %u for %I/%d", - oif, net->n.prefix, net->n.pxlen); - return; - } - } - else - { - ng = neigh_find2(&p->p, &ra.gw, ifa, 0); - if (ng && ng->scope) - { - if (ng->iface != ifa) - log(L_WARN "KRT: Route with unexpected iface for %I/%d", net->n.prefix, net->n.pxlen); - ra.iface = ng->iface; - } - else - { - log(L_WARN "Kernel told us to use non-neighbor %I for %I/%d", ra.gw, net->n.prefix, net->n.pxlen); - return; - } - + log(L_ERR "KRT: Received route %I/%d with strange next-hop %I", + net->n.prefix, net->n.pxlen, ra.gw); + return; } } else { ra.dest = RTD_DEVICE; - ra.iface = krt_temp_iface(p, oif); + + /* + * In Linux IPv6, 'native' device routes have proto + * RTPROT_BOOT and not RTPROT_KERNEL (which they have in + * IPv4 and which is expected). We cannot distinguish + * 'native' and user defined device routes, so we ignore all + * such device routes and for consistency, we have the same + * behavior in IPv4. Anyway, users should use RTPROT_STATIC + * for their 'alien' routes. + */ + + if (i->rtm_protocol == RTPROT_BOOT) + src = KRT_SRC_KERNEL; } + break; case RTN_BLACKHOLE: ra.dest = RTD_BLACKHOLE; @@ -737,13 +716,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) break; /* FIXME: What about RTN_THROW? */ default: - DBG("KRT: Ignoring route with type=%d\n", i->rtm_type); - return; - } - - if (i->rtm_scope != RT_SCOPE_UNIVERSE) - { - DBG("KRT: Ignoring route with scope=%d\n", i->rtm_scope); + SKIP("type %d\n", i->rtm_type); return; } diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 915baf44..02d59abb 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -663,7 +663,6 @@ get_sockaddr(struct sockaddr_in *sa, ip_addr *a, unsigned *port, int check) static char * sk_set_ttl_int(sock *s) { - int one = 1; #ifdef IPV6 if (setsockopt(s->fd, SOL_IPV6, IPV6_UNICAST_HOPS, &s->ttl, sizeof(s->ttl)) < 0) return "IPV6_UNICAST_HOPS"; @@ -671,6 +670,7 @@ sk_set_ttl_int(sock *s) if (setsockopt(s->fd, SOL_IP, IP_TTL, &s->ttl, sizeof(s->ttl)) < 0) return "IP_TTL"; #ifdef CONFIG_UNIX_DONTROUTE + int one = 1; if (s->ttl == 1 && setsockopt(s->fd, SOL_SOCKET, SO_DONTROUTE, &one, sizeof(one)) < 0) return "SO_DONTROUTE"; #endif diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 6d94cada..47b96217 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -558,32 +558,30 @@ krt_got_route(struct krt_proto *p, rte *e) rte *old; net *net = e->net; int verdict; -#ifdef KRT_ALLOW_LEARN - int src = e->u.krt.src; -#endif -#ifdef CONFIG_AUTO_ROUTES - if (e->attrs->dest == RTD_DEVICE) +#ifdef KRT_ALLOW_LEARN + switch (e->u.krt.src) { - /* It's a device route. Probably a kernel-generated one. */ + case KRT_SRC_KERNEL: verdict = KRF_IGNORE; goto sentenced; - } -#endif -#ifdef KRT_ALLOW_LEARN - if (src == KRT_SRC_ALIEN) - { + case KRT_SRC_REDIRECT: + verdict = KRF_DELETE; + goto sentenced; + + case KRT_SRC_ALIEN: if (KRT_CF->learn) krt_learn_scan(p, e); else { - krt_trace_in_rl(&rl_alien_ignored, p, e, "alien route, ignored"); + krt_trace_in_rl(&rl_alien_ignored, p, e, "[alien] ignored"); rte_free(e); } return; } #endif + /* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */ if (net->n.flags & KRF_VERDICT_MASK) { @@ -605,7 +603,7 @@ krt_got_route(struct krt_proto *p, rte *e) else verdict = KRF_DELETE; -sentenced: + sentenced: krt_trace_in(p, e, ((char *[]) { "?", "seen", "will be updated", "will be removed", "ignored" }) [verdict]); net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict; if (verdict == KRF_UPDATE || verdict == KRF_DELETE) @@ -680,19 +678,24 @@ krt_prune(struct krt_proto *p) } void -krt_got_route_async(struct krt_proto *p, rte *e, int new UNUSED) +krt_got_route_async(struct krt_proto *p, rte *e, int new) { net *net = e->net; - int src = e->u.krt.src; - switch (src) + switch (e->u.krt.src) { case KRT_SRC_BIRD: ASSERT(0); /* Should be filtered by the back end */ + case KRT_SRC_REDIRECT: - DBG("It's a redirect, kill him! Kill! Kill!\n"); - krt_set_notify(p, net, NULL, e); + if (new) + { + krt_trace_in(p, e, "[redirect] deleting"); + krt_set_notify(p, net, NULL, e); + } + /* If !new, it is probably echo of our deletion */ break; + #ifdef KRT_ALLOW_LEARN case KRT_SRC_ALIEN: if (KRT_CF->learn) @@ -878,7 +881,6 @@ krt_init(struct proto_config *c) p->p.accept_ra_types = RA_OPTIMAL; p->p.rt_notify = krt_notify; - p->p.min_scope = SCOPE_HOST; return &p->p; } diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index 607e6993..1d9e1448 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -83,6 +83,7 @@ void krt_got_route_async(struct krt_proto *p, struct rte *e, int new); #define KRT_SRC_BIRD 0 /* Our route (not passed in async mode) */ #define KRT_SRC_REDIRECT 1 /* Redirect route, delete it */ #define KRT_SRC_ALIEN 2 /* Route installed by someone else */ +#define KRT_SRC_KERNEL 3 /* Kernel routes, are ignored by krt syncer */ extern struct protocol proto_unix_iface; diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index 1a461ee1..0b179e00 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -9,6 +9,8 @@ #ifndef _BIRD_UNIX_H_ #define _BIRD_UNIX_H_ +#include + struct pool; /* main.c */ @@ -29,10 +31,12 @@ volatile int async_shutdown_flag; #define BIRD_PF PF_INET6 #define BIRD_AF AF_INET6 typedef struct sockaddr_in6 sockaddr; +static inline int sa_family_check(sockaddr *sa) { return sa->sin6_family == AF_INET6; } #else #define BIRD_PF PF_INET #define BIRD_AF AF_INET typedef struct sockaddr_in sockaddr; +static inline int sa_family_check(sockaddr *sa) { return sa->sin_family == AF_INET; } #endif #ifndef SUN_LEN