From 8ecbaf9c70b802a1200ad37f2bfd4bc64173c5fe Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 16 Aug 2012 13:09:26 +0200 Subject: Fixes a bug with neighbor cache and overlapping IP prefixes. When there are overlapping IP prefixes and one disappears, neighbors associated with it was removed even if there is another covering IP prefix. --- nest/neighbor.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/nest/neighbor.c b/nest/neighbor.c index 506d9bde..9dce8119 100644 --- a/nest/neighbor.c +++ b/nest/neighbor.c @@ -114,7 +114,7 @@ neighbor * neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags) { neighbor *n; - int class, scope = -1; ; + int class, scope = -1; unsigned int h = neigh_hash(p, a); struct iface *i; @@ -240,7 +240,21 @@ neigh_down(neighbor *n) n->proto->neigh_notify(n); rem_node(&n->n); if (n->flags & NEF_STICKY) - add_tail(&sticky_neigh_list, &n->n); + { + add_tail(&sticky_neigh_list, &n->n); + + /* Respawn neighbor if there is another matching prefix */ + struct iface *i; + int scope; + + if (!n->iface) + WALK_LIST(i, iface_list) + if ((scope = if_connected(&n->addr, i)) >= 0) + { + neigh_up(n, i, scope); + return; + } + } else sl_free(neigh_slab, n); } -- cgit v1.2.3 From 0343d066dab077d1391640c53198199b16bef993 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 29 Aug 2012 12:42:49 +0200 Subject: Fixes a bug in primary IP selection. --- sysdep/unix/krt.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 2bd1bc44..2128e136 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -113,13 +113,19 @@ kif_request_scan(void) tm_start(kif_scan_timer, 1); } -static inline int -prefer_scope(struct ifa *a, struct ifa *b) -{ return (a->scope > SCOPE_LINK) && (b->scope <= SCOPE_LINK); } - static inline int prefer_addr(struct ifa *a, struct ifa *b) -{ return ipa_compare(a->ip, b->ip) < 0; } +{ + int sa = a->scope > SCOPE_LINK; + int sb = b->scope > SCOPE_LINK; + + if (sa < sb) + return 0; + else if (sa > sb) + return 1; + else + return ipa_compare(a->ip, b->ip) < 0; +} static inline struct ifa * find_preferred_ifa(struct iface *i, ip_addr prefix, ip_addr mask) @@ -130,7 +136,7 @@ find_preferred_ifa(struct iface *i, ip_addr prefix, ip_addr mask) { if (!(a->flags & IA_SECONDARY) && ipa_equal(ipa_and(a->ip, mask), prefix) && - (!b || prefer_scope(a, b) || prefer_addr(a, b))) + (!b || prefer_addr(a, b))) b = a; } -- cgit v1.2.3 From e4404cef0be10e639566986a2f8c1906c9f37de1 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 29 Oct 2012 20:29:31 +0100 Subject: Fixes several bugs related to OSPFv3 vlinks. --- proto/ospf/config.Y | 1 + proto/ospf/lsupd.c | 4 +++ proto/ospf/topology.c | 74 +++++++++++++++++++++++++++++++++------------------ 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 67b0785f..a617292e 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -92,6 +92,7 @@ ospf_proto_finish(void) if (cf->abr && !backbone) { struct ospf_area_config *ac = cfg_allocz(sizeof(struct ospf_area_config)); + ac->type = OPT_E; /* Backbone is non-stub */ add_head(&cf->area_list, NODE ac); init_list(&ac->patt_list); init_list(&ac->net_list); diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 16967a7f..633ed533 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -112,6 +112,10 @@ ospf_lsa_flooding_allowed(struct ospf_lsa_header *lsa, u32 domain, struct ospf_i { u32 scope = LSA_SCOPE(lsa); + /* Handle inactive vlinks */ + if (ifa->state == OSPF_IS_DOWN) + return 0; + /* 4.5.2 (Case 2) */ if (unknown_lsa_type(lsa) && !(lsa->type & LSA_UBIT)) scope = LSA_SCOPE_LINK; diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index ec012b22..a9be12ee 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -1249,7 +1249,6 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length) struct ospf_config *cf = (struct ospf_config *) (po->proto.cf); struct ospf_iface *ifa; struct ospf_lsa_prefix *lp; - struct ifa *vlink_addr = NULL; int host_addr = 0; int net_lsa; int i = 0; @@ -1263,7 +1262,7 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length) WALK_LIST(ifa, po->iface_list) { - if ((ifa->oa != oa) || (ifa->state == OSPF_IS_DOWN)) + if ((ifa->oa != oa) || (ifa->type == OSPF_IT_VLINK) || (ifa->state == OSPF_IS_DOWN)) continue; ifa->px_pos_beg = i; @@ -1282,9 +1281,6 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length) (a->scope <= SCOPE_LINK)) continue; - if (!vlink_addr) - vlink_addr = a; - if (((a->pxlen < MAX_PREFIX_LENGTH) && net_lsa) || configured_stubnet(oa, a)) continue; @@ -1304,23 +1300,41 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length) ifa->px_pos_end = i; } - /* If there are some configured vlinks, add some global address, - which will be used as a vlink endpoint. */ - if (!EMPTY_LIST(cf->vlink_list) && !host_addr && vlink_addr) - { - lsa_put_prefix(po, vlink_addr->ip, MAX_PREFIX_LENGTH, 0); - i++; - } - struct ospf_stubnet_config *sn; if (oa->ac) WALK_LIST(sn, oa->ac->stubnet_list) if (!sn->hidden) { lsa_put_prefix(po, sn->px.addr, sn->px.len, sn->cost); + if (sn->px.len == MAX_PREFIX_LENGTH) + host_addr = 1; + i++; + } + + /* 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) + { + WALK_LIST(ifa, po->iface_list) + { + if ((ifa->type == OSPF_IT_VLINK) || (ifa->state == OSPF_IS_DOWN)) + continue; + + struct ifa *a; + WALK_LIST(a, ifa->iface->addrs) + { + if ((a->flags & IA_SECONDARY) || (a->scope <= SCOPE_LINK)) + continue; + + /* Found some IP */ + lsa_put_prefix(po, a->ip, MAX_PREFIX_LENGTH, 0); i++; + goto done; } + } + } + done: lp = po->lsab; lp->pxcount = i; *length = po->lsab_used + sizeof(struct ospf_lsa_header); @@ -1389,15 +1403,12 @@ add_prefix(struct proto_ospf *po, u32 *px, int offset, int *pxc) { u32 *pxl = lsab_offset(po, offset); int i; - for (i = 0; i < *pxc; i++) + for (i = 0; i < *pxc; pxl = prefix_advance(pxl), i++) + if (prefix_same(px, pxl)) { - if (prefix_same(px, pxl)) - { - /* Options should be logically OR'ed together */ - *pxl |= *px; - return; - } - pxl = prefix_advance(pxl); + /* Options should be logically OR'ed together */ + *pxl |= (*px & 0x00FF0000); + return; } ASSERT(pxl == lsab_end(po)); @@ -1405,6 +1416,7 @@ add_prefix(struct proto_ospf *po, u32 *px, int offset, int *pxc) int pxspace = prefix_space(px); pxl = lsab_alloc(po, pxspace); memcpy(pxl, px, pxspace); + *pxl &= 0xFFFF0000; /* Set metric to zero */ (*pxc)++; } @@ -1415,11 +1427,21 @@ add_link_lsa(struct proto_ospf *po, struct top_hash_entry *en, int offset, int * u32 *pxb = ll->rest; int j; - for (j = 0; j < ll->pxcount; j++) - { - add_prefix(po, pxb, offset, pxc); - pxb = prefix_advance(pxb); - } + for (j = 0; j < ll->pxcount; pxb = prefix_advance(pxb), j++) + { + u8 pxlen = (pxb[0] >> 24); + u8 pxopts = (pxb[0] >> 16); + + /* Skip NU or LA prefixes */ + if (pxopts & (OPT_PX_NU | OPT_PX_LA)) + continue; + + /* Skip link-local prefixes */ + if ((pxlen >= 10) && ((pxb[1] & 0xffc00000) == 0xfe800000)) + continue; + + add_prefix(po, pxb, offset, pxc); + } } -- cgit v1.2.3 From 8249ad9b304ea88b29e3aea76ebe49bb50348aaa Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 29 Oct 2012 20:39:03 +0100 Subject: Fixes sorting in OSPF show state. --- proto/ospf/ospf.c | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index aa62da14..5792453d 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -953,8 +953,10 @@ lsa_compare_for_state(const void *p1, const void *p2) struct ospf_lsa_header *lsa1 = &(he1->lsa); struct ospf_lsa_header *lsa2 = &(he2->lsa); - if (he1->domain != he2->domain) - return he1->domain - he2->domain; + if (he1->domain < he2->domain) + return -1; + if (he1->domain > he2->domain) + return 1; #ifdef OSPFv3 struct ospf_lsa_header lsatmp1, lsatmp2; @@ -979,14 +981,18 @@ lsa_compare_for_state(const void *p1, const void *p2) { #ifdef OSPFv3 /* In OSPFv3, neworks are named base on ID of DR */ - if (lsa1->rt != lsa2->rt) - return lsa1->rt - lsa2->rt; + if (lsa1->rt < lsa2->rt) + return -1; + if (lsa1->rt > lsa2->rt) + return 1; #endif /* For OSPFv2, this is IP of the network, for OSPFv3, this is interface ID */ - if (lsa1->id != lsa2->id) - return lsa1->id - lsa2->id; + if (lsa1->id < lsa2->id) + return -1; + if (lsa1->id > lsa2->id) + return 1; #ifdef OSPFv3 if (px1 != px2) @@ -997,14 +1003,20 @@ lsa_compare_for_state(const void *p1, const void *p2) } else { - if (lsa1->rt != lsa2->rt) - return lsa1->rt - lsa2->rt; + if (lsa1->rt < lsa2->rt) + return -1; + if (lsa1->rt > lsa2->rt) + return 1; - if (lsa1->type != lsa2->type) - return lsa1->type - lsa2->type; - - if (lsa1->id != lsa2->id) - return lsa1->id - lsa2->id; + if (lsa1->type < lsa2->type) + return -1; + if (lsa1->type > lsa2->type) + return 1; + + if (lsa1->id < lsa2->id) + return -1; + if (lsa1->id > lsa2->id) + return 1; #ifdef OSPFv3 if (px1 != px2) @@ -1023,12 +1035,16 @@ ext_compare_for_state(const void *p1, const void *p2) struct ospf_lsa_header *lsa1 = &(he1->lsa); struct ospf_lsa_header *lsa2 = &(he2->lsa); - if (lsa1->rt != lsa2->rt) - return lsa1->rt - lsa2->rt; + if (lsa1->rt < lsa2->rt) + return -1; + if (lsa1->rt > lsa2->rt) + return 1; + + if (lsa1->id < lsa2->id) + return -1; + if (lsa1->id > lsa2->id) + return 1; - if (lsa1->id != lsa2->id) - return lsa1->id - lsa2->id; - return lsa1->sn - lsa2->sn; } -- cgit v1.2.3 From dd4da6f640fb581cbd7d1ca537bf382558492b8e Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 31 Oct 2012 17:14:35 +0100 Subject: Fixes another bug in OSPFv3 vlinks. --- proto/ospf/hello.c | 2 +- proto/ospf/iface.c | 2 ++ proto/ospf/lsupd.c | 2 +- proto/ospf/neighbor.c | 2 +- proto/ospf/ospf.c | 1 + proto/ospf/ospf.h | 4 +++- proto/ospf/topology.c | 18 +++++++++--------- 7 files changed, 18 insertions(+), 13 deletions(-) diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index f9ba28f6..6ec5c511 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -261,7 +261,7 @@ ospf_hello_send(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) pkt->priority = ifa->priority; #ifdef OSPFv3 - pkt->iface_id = htonl(ifa->iface->index); + pkt->iface_id = htonl(ifa->iface_id); pkt->options3 = ifa->oa->options >> 16; pkt->options2 = ifa->oa->options >> 8; diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index a6a0c6c1..aa7f7892 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -567,6 +567,8 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i log(L_WARN "%s: Cannot use interface %s as %s, forcing %s", p->name, iface->name, ospf_it[old_type], ospf_it[ifa->type]); + /* Assign iface ID, for vlinks, this is ugly hack */ + ifa->iface_id = (ifa->type != OSPF_IT_VLINK) ? iface->index : oa->po->last_vlink_id++; init_list(&ifa->neigh_list); init_list(&ifa->nbma_list); diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 633ed533..a5da4251 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -123,7 +123,7 @@ ospf_lsa_flooding_allowed(struct ospf_lsa_header *lsa, u32 domain, struct ospf_i switch (scope) { case LSA_SCOPE_LINK: - return ifa->iface->index == domain; + return ifa->iface_id == domain; case LSA_SCOPE_AREA: return ifa->oa->areaid == domain; diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index 642365b3..26d81dce 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -459,7 +459,7 @@ bdr_election(struct ospf_iface *ifa) #else /* OSPFv3 */ me.dr = ifa->drid; me.bdr = ifa->bdrid; - me.iface_id = ifa->iface->index; + me.iface_id = ifa->iface_id; #endif add_tail(&ifa->neigh_list, NODE & me); diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index 5792453d..6654e107 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -232,6 +232,7 @@ ospf_start(struct proto *p) struct ospf_area_config *ac; po->router_id = proto_get_router_id(p->cf); + po->last_vlink_id = 0x80000000; po->rfc1583 = c->rfc1583; po->ebit = 0; po->ecmp = c->ecmp; diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 3bffaf91..7111a13d 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -189,7 +189,8 @@ struct ospf_iface u32 rxmtint; /* number of seconds between LSA retransmissions */ u32 pollint; /* Poll interval */ u32 deadint; /* after "deadint" missing hellos is router dead */ - u32 vid; /* Id of peer of virtual link */ + u32 iface_id; /* Interface ID (iface->index or new value for vlinks) */ + u32 vid; /* ID of peer of virtual link */ ip_addr vip; /* IP of peer of virtual link */ struct ospf_iface *vifa; /* OSPF iface which the vlink goes through */ struct ospf_area *voa; /* OSPF area which the vlink goes through */ @@ -776,6 +777,7 @@ struct proto_ospf int lsab_size, lsab_used; linpool *nhpool; /* Linpool used for next hops computed in SPF */ u32 router_id; + u32 last_vlink_id; /* Interface IDs for vlinks (starts at 0x80000000) */ }; struct ospf_iface_patt diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index a9be12ee..177cd53a 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -259,7 +259,7 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length) ln->type = LSART_PTP; ln->id = neigh->rid; ln->data = (ifa->addr->flags & IA_PEER) ? - ifa->iface->index : ipa_to_u32(ifa->addr->ip); + ifa->iface_id : ipa_to_u32(ifa->addr->ip); ln->metric = ifa->cost; ln->padding = 0; i++; @@ -368,7 +368,7 @@ add_lsa_rt_link(struct proto_ospf *po, struct ospf_iface *ifa, u8 type, u32 nif, ln->type = type; ln->padding = 0; ln->metric = ifa->cost; - ln->lif = ifa->iface->index; + ln->lif = ifa->iface_id; ln->nif = nif; ln->id = id; } @@ -546,7 +546,7 @@ originate_net_lsa_body(struct ospf_iface *ifa, u16 *length, if (n->state == NEIGHBOR_FULL) { #ifdef OSPFv3 - en = ospf_hash_find(po->gr, ifa->iface->index, n->iface_id, n->rid, LSA_T_LINK); + en = ospf_hash_find(po->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK); if (en) options |= ((struct ospf_lsa_link *) en->lsa_body)->options; #endif @@ -596,7 +596,7 @@ originate_net_lsa(struct ospf_iface *ifa) lsa.options = ifa->oa->options; lsa.id = ipa_to_u32(ifa->addr->ip); #else /* OSPFv3 */ - lsa.id = ifa->iface->index; + lsa.id = ifa->iface_id; #endif lsa.rt = po->router_id; @@ -1207,10 +1207,10 @@ originate_link_lsa(struct ospf_iface *ifa) lsa.age = 0; lsa.type = LSA_T_LINK; - lsa.id = ifa->iface->index; + lsa.id = ifa->iface_id; lsa.rt = po->router_id; lsa.sn = get_seqnum(ifa->link_lsa); - u32 dom = ifa->iface->index; + u32 dom = ifa->iface_id; body = originate_link_lsa_body(ifa, &lsa.length); lsasum_calculate(&lsa, body); @@ -1471,7 +1471,7 @@ originate_prefix_net_lsa_body(struct ospf_iface *ifa, u16 *length) WALK_LIST(n, ifa->neigh_list) if ((n->state == NEIGHBOR_FULL) && - (en = ospf_hash_find(po->gr, ifa->iface->index, n->iface_id, n->rid, LSA_T_LINK))) + (en = ospf_hash_find(po->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK))) add_link_lsa(po, en, offset, &pxc); lp = po->lsab; @@ -1493,7 +1493,7 @@ originate_prefix_net_lsa(struct ospf_iface *ifa) lsa.age = 0; lsa.type = LSA_T_PREFIX; - lsa.id = ifa->iface->index; + lsa.id = ifa->iface_id; lsa.rt = po->router_id; lsa.sn = get_seqnum(ifa->pxn_lsa); u32 dom = ifa->oa->areaid; @@ -1664,7 +1664,7 @@ ospf_lsa_domain(u32 type, struct ospf_iface *ifa) switch (type & LSA_SCOPE_MASK) { case LSA_SCOPE_LINK: - return ifa->iface->index; + return ifa->iface_id; case LSA_SCOPE_AREA: return ifa->oa->areaid; -- cgit v1.2.3 From cf98be7b6743e45dde9e0458664cc0762bf08867 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 10 Nov 2012 14:26:13 +0100 Subject: Allows rejected routes to be kept and examined. When 'import keep rejected' protocol option is activated, routes rejected by the import filter are kept in the routing table, but they are hidden and not propagated to other protocols. It is possible to examine them using 'show route rejected'. --- doc/bird.sgml | 22 +++++++++-- nest/config.Y | 9 ++++- nest/proto.c | 20 +++++++--- nest/protocol.h | 5 ++- nest/route.h | 10 ++++- nest/rt-table.c | 113 ++++++++++++++++++++++++++++++++++++++---------------- proto/bgp/attrs.c | 2 +- proto/bgp/bgp.c | 2 +- sysdep/unix/krt.c | 4 +- 9 files changed, 135 insertions(+), 52 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 24bc3026..e5550590 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -459,6 +459,14 @@ to zero to disable it. An empty is equivalent to import keep rejected + Usually, if an import filter rejects a route, the route is + forgotten. When this option is active, rejected routes are + kept in the routing table, but they are hidden and not + propagated to other protocols. But it is possible to show them + using import limit Specify an import route limit (a maximum number of routes imported from the protocol) and optionally the action to be @@ -467,8 +475,11 @@ to zero to disable it. An empty is equivalent to export limit Specify an export route limit, works similarly to @@ -661,6 +672,9 @@ This argument can be omitted if there exists only a single instance.

You can also select just routes added by a specific protocol. protocol . +

If BIRD is configured to keep rejected routes (see The rdnss local + rdnss local Use only local (interface-specific) RDNSS definitions for this interface. Otherwise, both global and local definitions are used. Could also be used to disable RDNSS for given interface if no local definitons are specified. Default: no. - dnssl local + dnssl local Use only local DNSSL definitions for this interface. See diff --git a/nest/config.Y b/nest/config.Y index a75dd0c3..93402d90 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -44,7 +44,7 @@ CF_DECLS CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT) CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS) -CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE) +CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, REJECTED) CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, MAX, FLUSH) CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED) @@ -187,6 +187,7 @@ proto_item: | EXPORT imexport { this_proto->out_filter = $2; } | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; } | EXPORT LIMIT limit_spec { this_proto->out_limit = $3; } + | IMPORT KEEP REJECTED bool { this_proto->in_keep_rejected = $4; } | TABLE rtable { this_proto->table = $2; } | ROUTER ID idval { this_proto->router_id = $3; } | DESCRIPTION TEXT { this_proto->dsc = $2; } @@ -405,7 +406,7 @@ CF_CLI(SHOW INTERFACES SUMMARY,,, [[Show summary of network interfaces]]) { if_show_summary(); } ; CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]]) -CF_CLI(SHOW ROUTE, r_args, [[[|for |for ] [table ] [filter |where ] [all] [primary] [(export|preexport)

] [protocol

] [stats|count]]], [[Show routing table]]) +CF_CLI(SHOW ROUTE, r_args, [[[|for |for ] [table ] [filter |where ] [all] [primary] [rejected] [(export|preexport)

] [protocol

] [stats|count]]], [[Show routing table]]) { rt_show($3); } ; r_args: @@ -451,6 +452,10 @@ r_args: $$ = $1; $$->primary_only = 1; } + | r_args REJECTED { + $$ = $1; + $$->rejected = 1; + } | r_args export_or_preexport SYM { struct proto_config *c = (struct proto_config *) $3->def; $$ = $1; diff --git a/nest/proto.c b/nest/proto.c index 53d3f1a2..2fb0e796 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -414,6 +414,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config p->main_ahook->out_filter = nc->out_filter; p->main_ahook->in_limit = nc->in_limit; p->main_ahook->out_limit = nc->out_limit; + p->main_ahook->in_keep_rejected = nc->in_keep_rejected; } /* Update routes when filters changed. If the protocol in not UP, @@ -719,8 +720,9 @@ proto_fell_down(struct proto *p) { DBG("Protocol %s down\n", p->name); - if (p->stats.imp_routes != 0) - log(L_ERR "Protocol %s is down but still has %d routes", p->name, p->stats.imp_routes); + u32 all_routes = p->stats.imp_routes + p->stats.rej_routes; + if (all_routes != 0) + log(L_ERR "Protocol %s is down but still has %d routes", p->name, all_routes); bzero(&p->stats, sizeof(struct proto_stats)); proto_free_ahooks(p); @@ -796,6 +798,7 @@ proto_schedule_feed(struct proto *p, int initial) p->main_ahook->out_filter = p->cf->out_filter; p->main_ahook->in_limit = p->cf->in_limit; p->main_ahook->out_limit = p->cf->out_limit; + p->main_ahook->in_keep_rejected = p->cf->in_keep_rejected; proto_reset_limit(p->main_ahook->in_limit); proto_reset_limit(p->main_ahook->out_limit); } @@ -1093,10 +1096,15 @@ proto_state_name(struct proto *p) } static void -proto_show_stats(struct proto_stats *s) +proto_show_stats(struct proto_stats *s, int in_keep_rejected) { - cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred", - s->imp_routes, s->exp_routes, s->pref_routes); + if (in_keep_rejected) + cli_msg(-1006, " Routes: %u imported, %u rejected, %u exported, %u preferred", + s->imp_routes, s->rej_routes, s->exp_routes, s->pref_routes); + else + cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred", + s->imp_routes, s->exp_routes, s->pref_routes); + cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted"); cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u", s->imp_updates_received, s->imp_updates_invalid, @@ -1134,7 +1142,7 @@ proto_show_basic_info(struct proto *p) proto_show_limit(p->cf->out_limit, "Export limit:"); if (p->proto_state != PS_DOWN) - proto_show_stats(&p->stats); + proto_show_stats(&p->stats, p->cf->in_keep_rejected); } void diff --git a/nest/protocol.h b/nest/protocol.h index 11fcb164..b10016d7 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -91,6 +91,7 @@ struct proto_config { int class; /* SYM_PROTO or SYM_TEMPLATE */ u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */ unsigned preference, disabled; /* Generic parameters */ + int in_keep_rejected; /* Routes rejected in import filter are kept */ u32 router_id; /* Protocol specific router ID */ struct rtable_config *table; /* Table we're attached to */ struct filter *in_filter, *out_filter; /* Attached filters */ @@ -106,7 +107,8 @@ struct proto_config { struct proto_stats { /* Import - from protocol to core */ u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */ - u32 pref_routes; /* Number of routes that are preferred, sum over all routing table */ + u32 rej_routes; /* Number of routes rejected in import filter but kept in the routing table */ + u32 pref_routes; /* Number of routes that are preferred, sum over all routing tables */ u32 imp_updates_received; /* Number of route updates received */ u32 imp_updates_invalid; /* Number of route updates rejected as invalid */ u32 imp_updates_filtered; /* Number of route updates rejected by filters */ @@ -410,6 +412,7 @@ struct announce_hook { struct proto_limit *out_limit; /* Output limit */ struct proto_stats *stats; /* Per-table protocol statistics */ struct announce_hook *next; /* Next hook for the same protocol */ + int in_keep_rejected; /* Routes rejected in import filter are kept */ }; struct announce_hook *proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats); diff --git a/nest/route.h b/nest/route.h index 524e69b3..3c10fc55 100644 --- a/nest/route.h +++ b/nest/route.h @@ -221,6 +221,14 @@ typedef struct rte { } rte; #define REF_COW 1 /* Copy this rte on write */ +#define REF_REJECTED 2 /* Route is rejected by import filter */ + +/* Route is valid for propagation (may depend on other flags in the future), accepts NULL */ +static inline int rte_is_valid(rte *r) { return r && !(r->flags & REF_REJECTED); } + +/* Route just has REF_REJECTED flag */ +static inline int rte_is_rejected(rte *r) { return !!(r->flags & REF_REJECTED); } + /* Types of route announcement, also used as flags */ #define RA_OPTIMAL 1 /* Announcement of optimal route change */ @@ -263,7 +271,7 @@ struct rt_show_data { struct fib_iterator fit; struct proto *show_protocol; struct proto *export_protocol; - int export_mode, primary_only; + int export_mode, primary_only, rejected; struct config *running_on_config; int net_counter, rt_counter, show_counter; int stats, show_for; diff --git a/nest/rt-table.c b/nest/rt-table.c index 118f4c25..421a05ea 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -69,7 +69,7 @@ net_route(rtable *tab, ip_addr a, int len) { a0 = ipa_and(a, ipa_mkmask(len)); n = fib_find(&tab->fib, &a0, len); - if (n && n->routes) + if (n && rte_is_valid(n->routes)) return n; len--; } @@ -139,8 +139,11 @@ rte_better(rte *new, rte *old) { int (*better)(rte *, rte *); - if (!old) + if (!rte_is_valid(old)) return 1; + if (!rte_is_valid(new)) + return 0; + if (new->pref > old->pref) return 1; if (new->pref < old->pref) @@ -399,9 +402,13 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol rte *old_free = NULL; rte *r; - /* Used to track whether we met old_changed position. If it is NULL - it was the first and met it implicitly before current best route. */ - int old_meet = (old_changed && !before_old) ? 1 : 0; + /* Used to track whether we met old_changed position. If before_old is NULL + old_changed was the first and we met it implicitly before current best route. */ + int old_meet = old_changed && !before_old; + + /* Note that before_old is either NULL or valid (not rejected) route. + If old_changed is valid, before_old have to be too. If old changed route + was not valid, caller must use NULL for both old_changed and before_old. */ if (new_changed) stats->exp_updates_received++; @@ -409,7 +416,7 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol stats->exp_withdraws_received++; /* First, find the new_best route - first accepted by filters */ - for (r=net->routes; r; r=r->next) + for (r=net->routes; rte_is_valid(r); r=r->next) { if (new_best = export_filter(ah, r, &new_free, &tmpa, 0)) break; @@ -428,7 +435,8 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol if (feed) { if (feed == 2) /* refeed */ - old_best = new_best ? new_best : net->routes; + old_best = new_best ? new_best : + (rte_is_valid(net->routes) ? net->routes : NULL); else old_best = NULL; @@ -477,7 +485,7 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol } /* Fourth case */ - for (r=r->next; r; r=r->next) + for (r=r->next; rte_is_valid(r); r=r->next) { if (old_best = export_filter(ah, r, &old_free, NULL, 1)) goto found; @@ -531,7 +539,14 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol static void rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *before_old, ea_list *tmpa) { - struct announce_hook *a; + if (!rte_is_valid(old)) + old = before_old = NULL; + + if (!rte_is_valid(new)) + new = NULL; + + if (!old && !new) + return; if (type == RA_OPTIMAL) { @@ -544,6 +559,7 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *befo rt_notify_hostcache(tab, net); } + struct announce_hook *a; WALK_LIST(a, tab->hooks) { ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING); @@ -650,8 +666,13 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str if (new && rte_same(old, new)) { /* No changes, ignore the new route */ - stats->imp_updates_ignored++; - rte_trace_in(D_ROUTES, p, new, "ignored"); + + if (!rte_is_rejected(new)) + { + stats->imp_updates_ignored++; + rte_trace_in(D_ROUTES, p, new, "ignored"); + } + rte_free_quick(new); #ifdef CONFIG_RIP /* lastmod is used internally by RIP as the last time @@ -680,8 +701,10 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str struct proto_limit *l = ah->in_limit; if (l && !old && new) { - if (stats->imp_routes >= l->limit) - proto_notify_limit(ah, l, stats->imp_routes); + u32 all_routes = stats->imp_routes + stats->rej_routes; + + if (all_routes >= l->limit) + proto_notify_limit(ah, l, all_routes); if (l->state == PLS_BLOCKED) { @@ -692,15 +715,15 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str } } - if (new) + if (new && !rte_is_rejected(new)) stats->imp_updates_accepted++; else stats->imp_withdraws_accepted++; if (new) - stats->imp_routes++; + rte_is_rejected(new) ? stats->rej_routes++ : stats->imp_routes++; if (old) - stats->imp_routes--; + rte_is_rejected(old) ? stats->rej_routes-- : stats->imp_routes--; if (table->config->sorted) { @@ -900,27 +923,41 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src) stats->imp_updates_invalid++; goto drop; } + if (filter == FILTER_REJECT) { stats->imp_updates_filtered++; rte_trace_in(D_FILTERS, p, new, "filtered out"); - goto drop; + + if (! ah->in_keep_rejected) + goto drop; + + /* new is a private copy, i could modify it */ + new->flags |= REF_REJECTED; } - if (src->make_tmp_attrs) - tmpa = src->make_tmp_attrs(new, rte_update_pool); - if (filter) + else { - ea_list *old_tmpa = tmpa; - int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0); - if (fr > F_ACCEPT) + if (src->make_tmp_attrs) + tmpa = src->make_tmp_attrs(new, rte_update_pool); + if (filter && (filter != FILTER_REJECT)) { - stats->imp_updates_filtered++; - rte_trace_in(D_FILTERS, p, new, "filtered out"); - goto drop; + ea_list *old_tmpa = tmpa; + int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0); + if (fr > F_ACCEPT) + { + stats->imp_updates_filtered++; + rte_trace_in(D_FILTERS, p, new, "filtered out"); + + if (! ah->in_keep_rejected) + goto drop; + + new->flags |= REF_REJECTED; + } + if (tmpa != old_tmpa && src->store_tmp_attrs) + src->store_tmp_attrs(new, tmpa); } - if (tmpa != old_tmpa && src->store_tmp_attrs) - src->store_tmp_attrs(new, tmpa); } + if (!(new->attrs->aflags & RTAF_CACHED)) /* Need to copy attributes */ new->attrs = rta_lookup(new->attrs); new->flags |= REF_COW; @@ -1553,9 +1590,11 @@ again: return 0; } + /* XXXX perhaps we should change feed for RA_ACCEPTED to not use 'new' */ + if ((p->accept_ra_types == RA_OPTIMAL) || (p->accept_ra_types == RA_ACCEPTED)) - if (e) + if (rte_is_valid(e)) { if (p->core_state != FS_FEEDING) return 1; /* In the meantime, the protocol fell down. */ @@ -1564,7 +1603,7 @@ again: } if (p->accept_ra_types == RA_ANY) - for(e = n->routes; e != NULL; e = e->next) + for(e = n->routes; rte_is_valid(e); e = e->next) { if (p->core_state != FS_FEEDING) return 1; /* In the meantime, the protocol fell down. */ @@ -1817,7 +1856,8 @@ rt_update_hostentry(rtable *tab, struct hostentry *he) net *n = net_route(tab, he->addr, MAX_PREFIX_LENGTH); if (n) { - rta *a = n->routes->attrs; + rte *e = n->routes; + rta *a = e->attrs; pxlen = n->n.pxlen; if (a->hostentry) @@ -1850,7 +1890,7 @@ rt_update_hostentry(rtable *tab, struct hostentry *he) } he->src = rta_clone(a); - he->igp_metric = rt_get_igp_metric(n->routes); + he->igp_metric = rt_get_igp_metric(e); } done: @@ -1980,14 +2020,19 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) int ok; bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen); - if (n->routes) - d->net_counter++; + for(e=n->routes; e; e=e->next) { + if (rte_is_rejected(e) != d->rejected) + continue; + struct ea_list *tmpa; struct proto *p0 = e->attrs->proto; struct proto *p1 = d->export_protocol; struct proto *p2 = d->show_protocol; + + if (ia[0]) + d->net_counter++; d->rt_counter++; ee = e; rte_update_lock(); /* We use the update buffer for filtering */ diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index e5bc84dd..9f71544e 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -1346,7 +1346,7 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best) /* The default case - find a new best-in-group route */ r = new; /* new may not be in the list */ - for (s=net->routes; s; s=s->next) + for (s=net->routes; rte_is_valid(s); s=s->next) if (use_deterministic_med(s) && same_group(s, lpref, lasn)) { s->u.bgp.suppressed = 1; diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index dbc59eea..2eb8ccb4 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1188,7 +1188,7 @@ bgp_show_proto_info(struct proto *P) cli_msg(-1006, " Source address: %I", p->source_addr); if (P->cf->in_limit) cli_msg(-1006, " Route limit: %d/%d", - p->p.stats.imp_routes, P->cf->in_limit->limit); + p->p.stats.imp_routes + p->p.stats.rej_routes, P->cf->in_limit->limit); cli_msg(-1006, " Hold timer: %d/%d", tm_remains(c->hold_timer), c->hold_time); cli_msg(-1006, " Keepalive timer: %d/%d", diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 2128e136..6c0e5e91 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -581,7 +581,7 @@ krt_flush_routes(struct krt_proto *p) { net *n = (net *) f; rte *e = n->routes; - if (e && (n->n.flags & KRF_INSTALLED)) + if (rte_is_valid(e) && (n->n.flags & KRF_INSTALLED)) { /* FIXME: this does not work if gw is changed in export filter */ krt_replace_rte(p, e->net, NULL, e, NULL); @@ -656,7 +656,7 @@ krt_got_route(struct krt_proto *p, rte *e) } old = net->routes; - if ((net->n.flags & KRF_INSTALLED) && old) + if ((net->n.flags & KRF_INSTALLED) && rte_is_valid(old)) { /* There may be changes in route attributes, we ignore that. Also, this does not work well if gw is changed in export filter */ -- cgit v1.2.3 From a55a90faec5cce09cee65f484e3731207af00335 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 10 Nov 2012 14:54:35 +0100 Subject: Peer address of stub iface should be announced in OSPF Router LSA. --- proto/ospf/topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 177cd53a..bfa071d8 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -305,7 +305,7 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length) /* Now we will originate stub area if there is no primary */ if (net_lsa || (ifa->type == OSPF_IT_VLINK) || - (ifa->addr->flags & IA_PEER) || + ((ifa->addr->flags & IA_PEER) && ! ifa->cf->stub) || configured_stubnet(oa, ifa->addr)) continue; -- cgit v1.2.3 From 227af52fb5be09c841fbd9f86e7bb3992b981a4a Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 10 Nov 2012 16:18:12 +0100 Subject: Fixes OSPF reconfigure w.r.t. downed ifaces. --- proto/ospf/iface.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index aa7f7892..290a8634 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -886,6 +886,10 @@ ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) struct ifa *a; WALK_LIST(iface, iface_list) + { + if (! (iface->flags & IF_UP)) + continue; + WALK_LIST(a, iface->addrs) { if (a->flags & IA_SECONDARY) @@ -911,6 +915,7 @@ ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) ospf_iface_new(oa, a, ip); } } + } } @@ -1014,6 +1019,10 @@ ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) struct ifa *a; WALK_LIST(iface, iface_list) + { + if (! (iface->flags & IF_UP)) + continue; + WALK_LIST(a, iface->addrs) { if (a->flags & IA_SECONDARY) @@ -1042,6 +1051,7 @@ ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) ospf_iface_new(oa, a, ip); } } + } } #endif -- cgit v1.2.3 From e16469bc4d182428687a5ef5f2fb4707afa15abd Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Mon, 12 Nov 2012 13:48:29 +0100 Subject: AS# in bgp.agreggator was a signed integer - fixed. --- proto/bgp/attrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 9f71544e..98b2f2c2 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -238,7 +238,7 @@ bgp_format_aggregator(eattr *a, byte *buf, int buflen UNUSED) as = get_u32(data); data += 4; - bsprintf(buf, "%d.%d.%d.%d AS%d", data[0], data[1], data[2], data[3], as); + bsprintf(buf, "%d.%d.%d.%d AS%u", data[0], data[1], data[2], data[3], as); } static int -- cgit v1.2.3 From 15550957957f3c790f3bec3f6b8721559ea25969 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 15 Nov 2012 01:29:01 +0100 Subject: Changes 'rejected' to 'filtered' in one of the last patches. --- doc/bird.sgml | 12 ++++++------ nest/config.Y | 10 +++++----- nest/proto.c | 16 ++++++++-------- nest/protocol.h | 6 +++--- nest/route.h | 10 +++++----- nest/rt-table.c | 20 ++++++++++---------- proto/bgp/bgp.c | 2 +- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index e5550590..7cea3921 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -459,12 +459,12 @@ to zero to disable it. An empty is equivalent to import keep rejected + import keep filtered Usually, if an import filter rejects a route, the route is - forgotten. When this option is active, rejected routes are + forgotten. When this option is active, these routes are kept in the routing table, but they are hidden and not propagated to other protocols. But it is possible to show them - using import limit @@ -476,7 +476,7 @@ to zero to disable it. An empty is equivalent to You can also select just routes added by a specific protocol. protocol . -

If BIRD is configured to keep rejected routes (see If BIRD is configured to keep filtered routes (see The out_filter = $2; } | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; } | EXPORT LIMIT limit_spec { this_proto->out_limit = $3; } - | IMPORT KEEP REJECTED bool { this_proto->in_keep_rejected = $4; } + | IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; } | TABLE rtable { this_proto->table = $2; } | ROUTER ID idval { this_proto->router_id = $3; } | DESCRIPTION TEXT { this_proto->dsc = $2; } @@ -406,7 +406,7 @@ CF_CLI(SHOW INTERFACES SUMMARY,,, [[Show summary of network interfaces]]) { if_show_summary(); } ; CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]]) -CF_CLI(SHOW ROUTE, r_args, [[[|for |for ] [table ] [filter |where ] [all] [primary] [rejected] [(export|preexport)

] [protocol

] [stats|count]]], [[Show routing table]]) +CF_CLI(SHOW ROUTE, r_args, [[[|for |for ] [table ] [filter |where ] [all] [primary] [filtered] [(export|preexport)

] [protocol

] [stats|count]]], [[Show routing table]]) { rt_show($3); } ; r_args: @@ -452,9 +452,9 @@ r_args: $$ = $1; $$->primary_only = 1; } - | r_args REJECTED { + | r_args FILTERED { $$ = $1; - $$->rejected = 1; + $$->filtered = 1; } | r_args export_or_preexport SYM { struct proto_config *c = (struct proto_config *) $3->def; diff --git a/nest/proto.c b/nest/proto.c index 2fb0e796..e9afa2fe 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -414,7 +414,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config p->main_ahook->out_filter = nc->out_filter; p->main_ahook->in_limit = nc->in_limit; p->main_ahook->out_limit = nc->out_limit; - p->main_ahook->in_keep_rejected = nc->in_keep_rejected; + p->main_ahook->in_keep_filtered = nc->in_keep_filtered; } /* Update routes when filters changed. If the protocol in not UP, @@ -720,7 +720,7 @@ proto_fell_down(struct proto *p) { DBG("Protocol %s down\n", p->name); - u32 all_routes = p->stats.imp_routes + p->stats.rej_routes; + u32 all_routes = p->stats.imp_routes + p->stats.filt_routes; if (all_routes != 0) log(L_ERR "Protocol %s is down but still has %d routes", p->name, all_routes); @@ -798,7 +798,7 @@ proto_schedule_feed(struct proto *p, int initial) p->main_ahook->out_filter = p->cf->out_filter; p->main_ahook->in_limit = p->cf->in_limit; p->main_ahook->out_limit = p->cf->out_limit; - p->main_ahook->in_keep_rejected = p->cf->in_keep_rejected; + p->main_ahook->in_keep_filtered = p->cf->in_keep_filtered; proto_reset_limit(p->main_ahook->in_limit); proto_reset_limit(p->main_ahook->out_limit); } @@ -1096,11 +1096,11 @@ proto_state_name(struct proto *p) } static void -proto_show_stats(struct proto_stats *s, int in_keep_rejected) +proto_show_stats(struct proto_stats *s, int in_keep_filtered) { - if (in_keep_rejected) - cli_msg(-1006, " Routes: %u imported, %u rejected, %u exported, %u preferred", - s->imp_routes, s->rej_routes, s->exp_routes, s->pref_routes); + if (in_keep_filtered) + cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred", + s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes); else cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred", s->imp_routes, s->exp_routes, s->pref_routes); @@ -1142,7 +1142,7 @@ proto_show_basic_info(struct proto *p) proto_show_limit(p->cf->out_limit, "Export limit:"); if (p->proto_state != PS_DOWN) - proto_show_stats(&p->stats, p->cf->in_keep_rejected); + proto_show_stats(&p->stats, p->cf->in_keep_filtered); } void diff --git a/nest/protocol.h b/nest/protocol.h index b10016d7..cf2ca0a4 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -91,7 +91,7 @@ struct proto_config { int class; /* SYM_PROTO or SYM_TEMPLATE */ u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */ unsigned preference, disabled; /* Generic parameters */ - int in_keep_rejected; /* Routes rejected in import filter are kept */ + int in_keep_filtered; /* Routes rejected in import filter are kept */ u32 router_id; /* Protocol specific router ID */ struct rtable_config *table; /* Table we're attached to */ struct filter *in_filter, *out_filter; /* Attached filters */ @@ -107,7 +107,7 @@ struct proto_config { struct proto_stats { /* Import - from protocol to core */ u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */ - u32 rej_routes; /* Number of routes rejected in import filter but kept in the routing table */ + u32 filt_routes; /* Number of routes rejected in import filter but kept in the routing table */ u32 pref_routes; /* Number of routes that are preferred, sum over all routing tables */ u32 imp_updates_received; /* Number of route updates received */ u32 imp_updates_invalid; /* Number of route updates rejected as invalid */ @@ -412,7 +412,7 @@ struct announce_hook { struct proto_limit *out_limit; /* Output limit */ struct proto_stats *stats; /* Per-table protocol statistics */ struct announce_hook *next; /* Next hook for the same protocol */ - int in_keep_rejected; /* Routes rejected in import filter are kept */ + int in_keep_filtered; /* Routes rejected in import filter are kept */ }; struct announce_hook *proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats); diff --git a/nest/route.h b/nest/route.h index 3c10fc55..177baa38 100644 --- a/nest/route.h +++ b/nest/route.h @@ -221,13 +221,13 @@ typedef struct rte { } rte; #define REF_COW 1 /* Copy this rte on write */ -#define REF_REJECTED 2 /* Route is rejected by import filter */ +#define REF_FILTERED 2 /* Route is rejected by import filter */ /* Route is valid for propagation (may depend on other flags in the future), accepts NULL */ -static inline int rte_is_valid(rte *r) { return r && !(r->flags & REF_REJECTED); } +static inline int rte_is_valid(rte *r) { return r && !(r->flags & REF_FILTERED); } -/* Route just has REF_REJECTED flag */ -static inline int rte_is_rejected(rte *r) { return !!(r->flags & REF_REJECTED); } +/* Route just has REF_FILTERED flag */ +static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED); } /* Types of route announcement, also used as flags */ @@ -271,7 +271,7 @@ struct rt_show_data { struct fib_iterator fit; struct proto *show_protocol; struct proto *export_protocol; - int export_mode, primary_only, rejected; + int export_mode, primary_only, filtered; struct config *running_on_config; int net_counter, rt_counter, show_counter; int stats, show_for; diff --git a/nest/rt-table.c b/nest/rt-table.c index 421a05ea..102218b2 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -667,7 +667,7 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str { /* No changes, ignore the new route */ - if (!rte_is_rejected(new)) + if (!rte_is_filtered(new)) { stats->imp_updates_ignored++; rte_trace_in(D_ROUTES, p, new, "ignored"); @@ -701,7 +701,7 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str struct proto_limit *l = ah->in_limit; if (l && !old && new) { - u32 all_routes = stats->imp_routes + stats->rej_routes; + u32 all_routes = stats->imp_routes + stats->filt_routes; if (all_routes >= l->limit) proto_notify_limit(ah, l, all_routes); @@ -715,15 +715,15 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str } } - if (new && !rte_is_rejected(new)) + if (new && !rte_is_filtered(new)) stats->imp_updates_accepted++; else stats->imp_withdraws_accepted++; if (new) - rte_is_rejected(new) ? stats->rej_routes++ : stats->imp_routes++; + rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++; if (old) - rte_is_rejected(old) ? stats->rej_routes-- : stats->imp_routes--; + rte_is_filtered(old) ? stats->filt_routes-- : stats->imp_routes--; if (table->config->sorted) { @@ -929,11 +929,11 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src) stats->imp_updates_filtered++; rte_trace_in(D_FILTERS, p, new, "filtered out"); - if (! ah->in_keep_rejected) + if (! ah->in_keep_filtered) goto drop; /* new is a private copy, i could modify it */ - new->flags |= REF_REJECTED; + new->flags |= REF_FILTERED; } else { @@ -948,10 +948,10 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src) stats->imp_updates_filtered++; rte_trace_in(D_FILTERS, p, new, "filtered out"); - if (! ah->in_keep_rejected) + if (! ah->in_keep_filtered) goto drop; - new->flags |= REF_REJECTED; + new->flags |= REF_FILTERED; } if (tmpa != old_tmpa && src->store_tmp_attrs) src->store_tmp_attrs(new, tmpa); @@ -2023,7 +2023,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) for(e=n->routes; e; e=e->next) { - if (rte_is_rejected(e) != d->rejected) + if (rte_is_filtered(e) != d->filtered) continue; struct ea_list *tmpa; diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 2eb8ccb4..346c641b 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1188,7 +1188,7 @@ bgp_show_proto_info(struct proto *P) cli_msg(-1006, " Source address: %I", p->source_addr); if (P->cf->in_limit) cli_msg(-1006, " Route limit: %d/%d", - p->p.stats.imp_routes + p->p.stats.rej_routes, P->cf->in_limit->limit); + p->p.stats.imp_routes + p->p.stats.filt_routes, P->cf->in_limit->limit); cli_msg(-1006, " Hold timer: %d/%d", tm_remains(c->hold_timer), c->hold_time); cli_msg(-1006, " Keepalive timer: %d/%d", -- cgit v1.2.3 From 6cadbf325bfcf25a04d869778abb443f9e1b6119 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 15 Nov 2012 14:08:20 +0100 Subject: Change unnamed ptp link description on OSPFv2. Although it is a slight deviation from the standard, it has no ill consequences for OSPFv2 and the change fixes a compatibility issue with some broken implementations. --- proto/ospf/topology.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index bfa071d8..5f4d1d54 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -258,8 +258,17 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length) ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); ln->type = LSART_PTP; ln->id = neigh->rid; - ln->data = (ifa->addr->flags & IA_PEER) ? - ifa->iface_id : ipa_to_u32(ifa->addr->ip); + + /* + * ln->data should be ifa->iface_id in case of no/ptp + * address (ifa->addr->flags & IA_PEER) on PTP link (see + * RFC 2328 12.4.1.1.), but the iface ID value has no use, + * while using IP address even in this case is here for + * compatibility with some broken implementations that use + * this address as a next-hop. + */ + ln->data = ipa_to_u32(ifa->addr->ip); + ln->metric = ifa->cost; ln->padding = 0; i++; -- cgit v1.2.3 From cf3a704b6a2263aba6bb6adb4c2c9dd93b72f470 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 16 Nov 2012 02:34:12 +0100 Subject: Updates the documentation. --- doc/bird.sgml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/bird.sgml b/doc/bird.sgml index 7cea3921..d833f82f 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1654,6 +1654,15 @@ use cases that use the direct protocol (like abusing eBGP as an IGP routing protocol), in most cases it is not needed to have these device routes in BIRD routing table and to use the direct protocol. +

There is one notable case when you definitely want to use the +direct protocol -- running BIRD on BSD systems. Having high priority +device routes for directly connected networks from the direct protocol +protects kernel device routes from being overwritten or removed by IGP +routes during some transient network conditions, because a lower +priority IGP route for the same network is not exported to the kernel +routing table. This is an issue on BSD systems only, as on Linux +systems BIRD cannot change non-BIRD route in the kernel routing table. +

The only configurable thing about direct is what interfaces it watches:

-- cgit v1.2.3 From 70577529244d6d920b75d95e797156e05141db30 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 16 Nov 2012 13:29:16 +0100 Subject: Fixes route tracing w.r.t. kept filtered routes. --- nest/rt-table.c | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/nest/rt-table.c b/nest/rt-table.c index 102218b2..2f0840f0 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -627,6 +627,8 @@ rte_same(rte *x, rte *y) (!x->attrs->proto->rte_same || x->attrs->proto->rte_same(x, y)); } +static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); } + static void rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, struct proto *src) { @@ -715,10 +717,15 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str } } - if (new && !rte_is_filtered(new)) + int new_ok = rte_is_ok(new); + int old_ok = rte_is_ok(old); + + if (new_ok) stats->imp_updates_accepted++; - else + else if (old_ok) stats->imp_withdraws_accepted++; + else + stats->imp_withdraws_ignored++; if (new) rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++; @@ -808,17 +815,19 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str new->lastmod = now; /* Log the route change */ - if (new) - rte_trace_in(D_ROUTES, p, new, net->routes == new ? "added [best]" : "added"); - - if (!new && (p->debug & D_ROUTES)) + if (p->debug & D_ROUTES) { - if (old != old_best) - rte_trace_in(D_ROUTES, p, old, "removed"); - else if (net->routes) - rte_trace_in(D_ROUTES, p, old, "removed [replaced]"); - else - rte_trace_in(D_ROUTES, p, old, "removed [sole]"); + if (new_ok) + rte_trace(p, new, '>', new == net->routes ? "added [best]" : "added"); + else if (old_ok) + { + if (old != old_best) + rte_trace(p, old, '>', "removed"); + else if (rte_is_ok(net->routes)) + rte_trace(p, old, '>', "removed [replaced]"); + else + rte_trace(p, old, '>', "removed [sole]"); + } } /* Propagate the route change */ @@ -833,17 +842,13 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str (table->gc_time + table->config->gc_min_time <= now)) rt_schedule_gc(table); + if (old_ok && p->rte_remove) + p->rte_remove(net, old); + if (new_ok && p->rte_insert) + p->rte_insert(net, new); + if (old) - { - if (p->rte_remove) - p->rte_remove(net, old); - rte_free_quick(old); - } - if (new) - { - if (p->rte_insert) - p->rte_insert(net, new); - } + rte_free_quick(old); } static int rte_update_nest_cnt; /* Nesting counter to allow recursive updates */ -- cgit v1.2.3 From c93c02088a026b83f452fbd260135ba4c8da7ecf Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 16 Nov 2012 13:30:54 +0100 Subject: NEWS and version update. --- NEWS | 8 +++++++- misc/bird.spec | 2 +- sysdep/config.h | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 3aaa4dd6..f8e76601 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,9 @@ +Version 1.3.9 (2012-11-16) + o BIRD can be configured to keep and show filtered routes. + o Dragonfly BSD support. + o Fixed OSPFv3 vlinks. + o Several minor bugfixes. + Version 1.3.8 (2012-08-07) o Generalized import and export route limits. o RDNSS and DNSSL support for RAdv. @@ -11,7 +17,7 @@ Version 1.3.8 (2012-08-07) Version 1.3.7 (2012-03-22) o Route Origin Authorization basics. o RIPng working again. - o Extended clist operations in filters. + o Extended clist operations in filters. o Fixes several bugs in BSD iface handling. o Several minor bugfixes and enhancements. diff --git a/misc/bird.spec b/misc/bird.spec index de63a6a0..daf4088b 100644 --- a/misc/bird.spec +++ b/misc/bird.spec @@ -1,6 +1,6 @@ Summary: BIRD Internet Routing Daemon Name: bird -Version: 1.3.8 +Version: 1.3.9 Release: 1 Copyright: GPL Group: Networking/Daemons diff --git a/sysdep/config.h b/sysdep/config.h index 7106e4ba..e97d9e20 100644 --- a/sysdep/config.h +++ b/sysdep/config.h @@ -7,7 +7,7 @@ #define _BIRD_CONFIG_H_ /* BIRD version */ -#define BIRD_VERSION "1.3.8" +#define BIRD_VERSION "1.3.9" /* Include parameters determined by configure script */ #include "sysdep/autoconf.h" -- cgit v1.2.3 From 3e40f3e795e39f0b92445fd5295382220077c77f Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 27 Nov 2012 01:25:47 +0100 Subject: Fixes setting of route attributes of type router id. --- filter/filter.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/filter/filter.c b/filter/filter.c index 49b67391..fb2034ee 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -956,11 +956,25 @@ interpret(struct f_inst *what) l->attrs[0].type = what->aux | EAF_ORIGINATED; switch (what->aux & EAF_TYPE_MASK) { case EAF_TYPE_INT: - case EAF_TYPE_ROUTER_ID: if (v1.type != T_INT) runtime( "Setting int attribute to non-int value" ); l->attrs[0].u.data = v1.val.i; break; + + case EAF_TYPE_ROUTER_ID: +#ifndef IPV6 + /* IP->Quad implicit conversion */ + if (v1.type == T_IP) { + l->attrs[0].u.data = ipa_to_u32(v1.val.px.ip); + break; + } +#endif + /* T_INT for backward compatibility */ + if ((v1.type != T_QUAD) && (v1.type != T_INT)) + runtime( "Setting quad attribute to non-quad value" ); + l->attrs[0].u.data = v1.val.i; + break; + case EAF_TYPE_OPAQUE: runtime( "Setting opaque attribute is not allowed" ); break; -- cgit v1.2.3 From b31774eeb01a2f63e4ce4dc83f36ffd17879593e Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 27 Nov 2012 01:30:09 +0100 Subject: Removes some nonsense. --- filter/filter.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/filter/filter.c b/filter/filter.c index fb2034ee..44fcf293 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -622,9 +622,6 @@ interpret(struct f_inst *what) case T_VOID: runtime( "Can't operate with values of type void" ); case T_INT: if (v2.val.i == 0) runtime( "Mother told me not to divide by 0" ); res.val.i = v1.val.i / v2.val.i; break; - case T_IP: if (v2.type != T_INT) - runtime( "Incompatible types in / operator" ); - break; default: runtime( "Usage of unknown type" ); } break; -- cgit v1.2.3 From 80a9cadc76101157707aecc0b482ad88ad702fc3 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 27 Nov 2012 02:08:04 +0100 Subject: Changes static route targets drop/reject to blackhole/unreachable. To be consistent with rest of BIRD and Linux. Old names are also allowed for compatibility. --- bird.conf | 10 +++++----- doc/bird.conf.example | 4 ++-- doc/bird.sgml | 9 +++++---- proto/static/config.Y | 11 +++++++---- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/bird.conf b/bird.conf index 2d10ef4b..bafd6ea1 100644 --- a/bird.conf +++ b/bird.conf @@ -25,14 +25,14 @@ protocol kernel { protocol static { # disabled; - route fec0:2::/64 reject; - route fec0:3::/64 reject; - route fec0:4::/64 reject; + route fec0:2::/64 blackhole; + route fec0:3::/64 unreachable; + route fec0:4::/64 prohibit; # route 0.0.0.0/0 via 195.113.31.113; -# route 62.168.0.0/25 reject; +# route 62.168.0.0/25 unreachable; # route 1.2.3.4/32 via 195.113.31.124; -# route 10.0.0.0/8 reject; +# route 10.0.0.0/8 unreachable; # route 10.1.1.0:255.255.255.0 via 62.168.0.3; # route 10.1.2.0:255.255.255.0 via 62.168.0.3; # route 10.1.3.0:255.255.255.0 via 62.168.0.4; diff --git a/doc/bird.conf.example b/doc/bird.conf.example index 5e07ab5a..dcc62e29 100644 --- a/doc/bird.conf.example +++ b/doc/bird.conf.example @@ -67,8 +67,8 @@ protocol static { # debug { states, routes, filters, interfaces, events, packets }; # debug all; # route 0.0.0.0/0 via 198.51.100.13; -# route 198.51.100.0/25 reject; -# route 10.0.0.0/8 reject; +# route 198.51.100.0/25 unreachable; +# route 10.0.0.0/8 unreachable; # route 10.1.1.0:255.255.255.0 via 198.51.100.3; # route 10.1.2.0:255.255.255.0 via 198.51.100.3; # route 10.1.3.0:255.255.255.0 via 198.51.100.4; diff --git a/doc/bird.sgml b/doc/bird.sgml index d833f82f..d351cedc 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2733,9 +2733,10 @@ definition of the protocol contains mainly a list of static routes: route through an interface to hosts on a directly connected network. route Static recursive route, its nexthop depends on a route table lookup for given IP address. - route Special routes - specifying to drop the packet, return it as unreachable or return - it as administratively prohibited. + route Special routes + specifying to silently drop the packet, return it as unreachable or return + it as administratively prohibited. First two targets are also known + as check link If set, hardware link states of network interfaces are taken @@ -2761,7 +2762,7 @@ protocol static { via 198.51.100.10 weight 2 via 198.51.100.20 via 192.0.2.1; - route 203.0.113.0/24 reject; # Sink route + route 203.0.113.0/24 unreachable; # Sink route route 10.2.0.0/24 via "arc0"; # Secondary network } diff --git a/proto/static/config.Y b/proto/static/config.Y index f8e84f92..2d9d4b42 100644 --- a/proto/static/config.Y +++ b/proto/static/config.Y @@ -18,7 +18,7 @@ static struct static_route *this_srt, *this_srt_nh, *last_srt_nh; CF_DECLS CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK) -CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE) +CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE) CF_GRAMMAR @@ -86,9 +86,12 @@ stat_route: this_srt->dest = RTDX_RECURSIVE; this_srt->via = $3; } - | stat_route0 DROP { this_srt->dest = RTD_BLACKHOLE; } - | stat_route0 REJECT { this_srt->dest = RTD_UNREACHABLE; } - | stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; } + + | stat_route0 DROP { this_srt->dest = RTD_BLACKHOLE; } + | stat_route0 REJECT { this_srt->dest = RTD_UNREACHABLE; } + | stat_route0 BLACKHOLE { this_srt->dest = RTD_BLACKHOLE; } + | stat_route0 UNREACHABLE { this_srt->dest = RTD_UNREACHABLE; } + | stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; } ; CF_CLI(SHOW STATIC, optsym, [], [[Show details of static protocol]]) -- cgit v1.2.3 From a92cf57dd6ba021a495fe7268c86dc8e6aeecbb2 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 26 Dec 2012 12:40:48 +0100 Subject: Implements undo command and optional timeout for configuration Several new configure command variants: configure undo - undo last reconfiguration configure timeout - configure with scheduled undo if not confirmed in timeout configure confirm - confirm last configuration configure check - just parse and validate config file --- conf/conf.c | 253 ++++++++++++++++++++++++++++++++++++++------------- conf/conf.h | 29 +++--- conf/gen_commands.m4 | 3 + conf/gen_parser.m4 | 1 + doc/bird.sgml | 37 +++++++- doc/reply_codes | 6 ++ nest/cli.c | 20 +++- nest/cli.h | 2 + nest/cmds.c | 6 +- nest/proto.c | 4 +- sysdep/unix/config.Y | 33 +++++-- sysdep/unix/io.c | 5 +- sysdep/unix/krt.c | 2 +- sysdep/unix/main.c | 113 ++++++++++++++++++----- sysdep/unix/timer.h | 1 + sysdep/unix/unix.h | 7 +- 16 files changed, 407 insertions(+), 115 deletions(-) diff --git a/conf/conf.c b/conf/conf.c index 9375861f..6dfa3691 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -21,9 +21,12 @@ * There can exist up to four different configurations at one time: an active * one (pointed to by @config), configuration we are just switching from * (@old_config), one queued for the next reconfiguration (@future_config; - * if it's non-%NULL and the user wants to reconfigure once again, we just + * if there is one and the user wants to reconfigure once again, we just * free the previous queued config and replace it with the new one) and - * finally a config being parsed (@new_config). + * finally a config being parsed (@new_config). The stored @old_config + * is also used for undo reconfiguration, which works in a similar way. + * Reconfiguration could also have timeout (using @config_timer) and undo + * is automatically called if the new configuration is not confirmed later. * * Loading of new configuration is very simple: just call config_alloc() * to get a new &config structure, then use config_parse() to parse a @@ -55,10 +58,23 @@ static jmp_buf conf_jmpbuf; -struct config *config, *new_config, *old_config, *future_config; -static event *config_event; -int shutting_down, future_type; -bird_clock_t boot_time; +struct config *config, *new_config; + +static struct config *old_config; /* Old configuration */ +static struct config *future_config; /* New config held here if recon requested during recon */ +static int old_cftype; /* Type of transition old_config -> config (RECONFIG_SOFT/HARD) */ +static int future_cftype; /* Type of scheduled transition, may also be RECONFIG_UNDO */ +/* Note that when future_cftype is RECONFIG_UNDO, then future_config is NULL, + therefore proper check for future scheduled config checks future_cftype */ + +static event *config_event; /* Event for finalizing reconfiguration */ +static timer *config_timer; /* Timer for scheduled configuration rollback */ + +/* These are public just for cmd_show_status(), should not be accessed elsewhere */ +int shutting_down; /* Shutdown requested, do not accept new config changes */ +int configuring; /* Reconfiguration is running */ +int undo_available; /* Undo was not requested from last reconfiguration */ +/* Note that both shutting_down and undo_available are related to requests, not processing */ /** * config_alloc - allocate a new configuration @@ -82,8 +98,6 @@ config_alloc(byte *name) c->load_time = now; c->tf_base.fmt1 = c->tf_log.fmt1 = "%d-%m-%Y %T"; - if (!boot_time) - boot_time = now; return c; } @@ -154,7 +168,8 @@ cli_parse(struct config *c) void config_free(struct config *c) { - rfree(c->pool); + if (c) + rfree(c->pool); } void @@ -170,10 +185,7 @@ config_del_obstacle(struct config *c) DBG("+++ deleting obstacle %d\n", c->obstacle_count); c->obstacle_count--; if (!c->obstacle_count) - { - ASSERT(config_event); - ev_schedule(config_event); - } + ev_schedule(config_event); } static int @@ -197,16 +209,31 @@ global_commit(struct config *new, struct config *old) static int config_do_commit(struct config *c, int type) { - int force_restart, nobs; + if (type == RECONFIG_UNDO) + { + c = old_config; + type = old_cftype; + } + else + config_free(old_config); - DBG("do_commit\n"); old_config = config; - config = new_config = c; + old_cftype = type; + config = c; + + configuring = 1; + if (old_config && !config->shutdown) + log(L_INFO "Reconfiguring"); + + /* This should not be necessary, but it seems there are some + functions that access new_config instead of config */ + new_config = config; + if (old_config) old_config->obstacle_count++; DBG("sysdep_commit\n"); - force_restart = sysdep_commit(c, old_config); + int force_restart = sysdep_commit(c, old_config); DBG("global_commit\n"); force_restart |= global_commit(c, old_config); DBG("rt_commit\n"); @@ -214,38 +241,38 @@ config_do_commit(struct config *c, int type) roa_commit(c, old_config); DBG("protos_commit\n"); protos_commit(c, old_config, force_restart, type); - new_config = NULL; /* Just to be sure nobody uses that now */ + + /* Just to be sure nobody uses that now */ + new_config = NULL; + + int obs = 0; if (old_config) - nobs = --old_config->obstacle_count; - else - nobs = 0; - DBG("do_commit finished with %d obstacles remaining\n", nobs); - return !nobs; + obs = --old_config->obstacle_count; + + DBG("do_commit finished with %d obstacles remaining\n", obs); + return !obs; } static void config_done(void *unused UNUSED) { - struct config *c; + if (config->shutdown) + sysdep_shutdown_done(); + + configuring = 0; + if (old_config) + log(L_INFO "Reconfigured"); - DBG("config_done\n"); - for(;;) + if (future_cftype) { - if (config->shutdown) - sysdep_shutdown_done(); - log(L_INFO "Reconfigured"); - if (old_config) - { - config_free(old_config); - old_config = NULL; - } - if (!future_config) - break; - c = future_config; + int type = future_cftype; + struct config *conf = future_config; + future_cftype = RECONFIG_NONE; future_config = NULL; + log(L_INFO "Reconfiguring to queued configuration"); - if (!config_do_commit(c, future_type)) - break; + if (config_do_commit(conf, type)) + config_done(NULL); } } @@ -253,6 +280,7 @@ config_done(void *unused UNUSED) * config_commit - commit a configuration * @c: new configuration * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD) + * @timeout: timeout for undo (or 0 for no timeout) * * When a configuration is parsed and prepared for use, the * config_commit() function starts the process of reconfiguration. @@ -265,6 +293,10 @@ config_done(void *unused UNUSED) * using config_del_obstacle(), the old configuration is freed and * everything runs according to the new one. * + * When @timeout is nonzero, the undo timer is activated with given + * timeout. The timer is deactivated when config_commit(), + * config_confirm() or config_undo() is called. + * * Result: %CONF_DONE if the configuration has been accepted immediately, * %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED * if it's been queued due to another reconfiguration being in progress now @@ -272,49 +304,147 @@ config_done(void *unused UNUSED) * are accepted. */ int -config_commit(struct config *c, int type) +config_commit(struct config *c, int type, int timeout) { - if (!config) /* First-time configuration */ + if (shutting_down) { - config_do_commit(c, RECONFIG_HARD); - return CONF_DONE; + config_free(c); + return CONF_SHUTDOWN; } - if (old_config) /* Reconfiguration already in progress */ + + undo_available = 1; + if (timeout > 0) + tm_start(config_timer, timeout); + else + tm_stop(config_timer); + + if (configuring) { - if (shutting_down == 2) - { - log(L_INFO "New configuration discarded due to shutdown"); - config_free(c); - return CONF_SHUTDOWN; - } - if (future_config) + if (future_cftype) { log(L_INFO "Queueing new configuration, ignoring the one already queued"); config_free(future_config); } else - log(L_INFO "Queued new configuration"); + log(L_INFO "Queueing new configuration"); + + future_cftype = type; future_config = c; - future_type = type; return CONF_QUEUED; } - if (!shutting_down) - log(L_INFO "Reconfiguring"); - if (config_do_commit(c, type)) { config_done(NULL); return CONF_DONE; } - if (!config_event) + return CONF_PROGRESS; +} + +/** + * config_confirm - confirm a commited configuration + * + * When the undo timer is activated by config_commit() with nonzero timeout, + * this function can be used to deactivate it and therefore confirm + * the current configuration. + * + * Result: %CONF_CONFIRM when the current configuration is confirmed, + * %CONF_NONE when there is nothing to confirm (i.e. undo timer is not active). + */ +int +config_confirm(void) +{ + if (config_timer->expires == 0) + return CONF_NOTHING; + + tm_stop(config_timer); + + return CONF_CONFIRM; +} + +/** + * config_undo - undo a configuration + * + * Function config_undo() can be used to change the current + * configuration back to stored %old_config. If no reconfiguration is + * running, this stored configuration is commited in the same way as a + * new configuration in config_commit(). If there is already a + * reconfiguration in progress and no next reconfiguration is + * scheduled, then the undo is scheduled for later processing as + * usual, but if another reconfiguration is already scheduled, then + * such reconfiguration is removed instead (i.e. undo is applied on + * the last commit that scheduled it). + * + * Result: %CONF_DONE if the configuration has been accepted immediately, + * %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED + * if it's been queued due to another reconfiguration being in progress now, + * %CONF_UNQUEUED if a scheduled reconfiguration is removed, %CONF_NOTHING + * if there is no relevant configuration to undo (the previous config request + * was config_undo() too) or %CONF_SHUTDOWN if BIRD is in shutdown mode and + * no new configuration changes are accepted. + */ +int +config_undo(void) +{ + if (shutting_down) + return CONF_SHUTDOWN; + + if (!undo_available || !old_config) + return CONF_NOTHING; + + undo_available = 0; + tm_stop(config_timer); + + if (configuring) { - config_event = ev_new(&root_pool); - config_event->hook = config_done; + if (future_cftype) + { + config_free(future_config); + future_config = NULL; + + log(L_INFO "Removing queued configuration"); + future_cftype = RECONFIG_NONE; + return CONF_UNQUEUED; + } + else + { + log(L_INFO "Queueing undo configuration"); + future_cftype = RECONFIG_UNDO; + return CONF_QUEUED; + } + } + + if (config_do_commit(NULL, RECONFIG_UNDO)) + { + config_done(NULL); + return CONF_DONE; } return CONF_PROGRESS; } +extern void cmd_reconfig_undo_notify(void); + +static void +config_timeout(struct timer *t) +{ + log(L_INFO "Config timeout expired, starting undo"); + cmd_reconfig_undo_notify(); + + int r = config_undo(); + if (r < 0) + log(L_ERR "Undo request failed"); +} + +void +config_init(void) +{ + config_event = ev_new(&root_pool); + config_event->hook = config_done; + + config_timer = tm_new(&root_pool); + config_timer->hook = config_timeout; +} + /** * order_shutdown - order BIRD shutdown * @@ -328,15 +458,16 @@ order_shutdown(void) if (shutting_down) return; + log(L_INFO "Shutting down"); c = lp_alloc(config->mem, sizeof(struct config)); memcpy(c, config, sizeof(struct config)); init_list(&c->protos); init_list(&c->tables); c->shutdown = 1; + + config_commit(c, RECONFIG_HARD, 0); shutting_down = 1; - config_commit(c, RECONFIG_HARD); - shutting_down = 2; } /** diff --git a/conf/conf.h b/conf/conf.h index c76832b6..19300f54 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -54,28 +54,33 @@ struct config { /* Please don't use these variables in protocols. Use proto_config->global instead. */ extern struct config *config; /* Currently active configuration */ extern struct config *new_config; /* Configuration being parsed */ -extern struct config *old_config; /* Old configuration when reconfiguration is in progress */ -extern struct config *future_config; /* New config held here if recon requested during recon */ - -extern int shutting_down; -extern bird_clock_t boot_time; struct config *config_alloc(byte *name); int config_parse(struct config *); int cli_parse(struct config *); void config_free(struct config *); -int config_commit(struct config *, int type); -#define RECONFIG_HARD 0 -#define RECONFIG_SOFT 1 +int config_commit(struct config *, int type, int timeout); +int config_confirm(void); +int config_undo(void); +void config_init(void); void cf_error(char *msg, ...) NORET; void config_add_obstacle(struct config *); void config_del_obstacle(struct config *); void order_shutdown(void); -#define CONF_DONE 0 -#define CONF_PROGRESS 1 -#define CONF_QUEUED 2 -#define CONF_SHUTDOWN 3 +#define RECONFIG_NONE 0 +#define RECONFIG_HARD 1 +#define RECONFIG_SOFT 2 +#define RECONFIG_UNDO 3 + +#define CONF_DONE 0 +#define CONF_PROGRESS 1 +#define CONF_QUEUED 2 +#define CONF_UNQUEUED 3 +#define CONF_CONFIRM 4 +#define CONF_SHUTDOWN -1 +#define CONF_NOTHING -2 + /* Pools */ diff --git a/conf/gen_commands.m4 b/conf/gen_commands.m4 index a88ba014..3ed21f13 100644 --- a/conf/gen_commands.m4 +++ b/conf/gen_commands.m4 @@ -10,6 +10,9 @@ m4_divert(-1)m4_dnl m4_define(CF_CLI, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$3", "$4", 1 }, m4_divert(-1)') +m4_define(CF_CLI_CMD, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1 }, +m4_divert(-1)') + m4_define(CF_CLI_HELP, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 0 }, m4_divert(-1)') diff --git a/conf/gen_parser.m4 b/conf/gen_parser.m4 index 74385f32..00b55023 100644 --- a/conf/gen_parser.m4 +++ b/conf/gen_parser.m4 @@ -44,6 +44,7 @@ m4_define(CF_CLI, `m4_define([[CF_cmd]], cmd_[[]]m4_translit($1, [[ ]], _))DNL m4_divert(2)CF_KEYWORDS(m4_translit($1, [[ ]], [[,]])) m4_divert(3)CF_ADDTO(cli_cmd, CF_cmd) CF_cmd: $1 $2 END') +m4_define(CF_CLI_CMD, `') m4_define(CF_CLI_HELP, `') # ENUM declarations are ignored diff --git a/doc/bird.sgml b/doc/bird.sgml index d351cedc..615ced98 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -702,19 +702,48 @@ This argument can be omitted if there exists only a single instance. flush roa [table ] Remove all dynamic ROA entries from a ROA table. - configure [soft] [" + configure [soft] [" Reload configuration from a given file. BIRD will smoothly switch itself to the new configuration, protocols are reconfigured if possible, restarted otherwise. Changes in - filters usually lead to restart of affected protocols. If - configure confirm + Deactivate the config undo timer and therefore confirm the current + configuration. + + configure undo + Undo the last configuration change and smoothly switch back to + the previous (stored) configuration. If the last configuration + change was soft, the undo change is also soft. There is only + one level of undo, but in some specific cases when several + reconfiguration requests are given immediately in a row and + the intermediate ones are skipped then the undo also skips them back. + + configure check [" + Read and parse given config file, but do not use it. useful + for checking syntactic and some semantic validity of an config + file. + enable|disable|restart - Enable, disable or restart a given protocol instance, instances matching the or or + reload [in|out] diff --git a/doc/reply_codes b/doc/reply_codes index 7ec2e27d..58807241 100644 --- a/doc/reply_codes +++ b/doc/reply_codes @@ -25,6 +25,12 @@ Reply codes of BIRD command-line interface 0014 Route count 0015 Reloading 0016 Access restricted +0017 Reconfiguration already in progress, removing queued config +0018 Reconfiguration confirmed +0019 Nothing to do (configure undo/confirm) +0020 Configuration OK +0021 Undo requested +0022 Undo scheduled 1000 BIRD version 1001 Interface list diff --git a/nest/cli.c b/nest/cli.c index d245790b..11f98794 100644 --- a/nest/cli.c +++ b/nest/cli.c @@ -122,6 +122,7 @@ cli_printf(cli *c, int code, char *msg, ...) va_list args; byte buf[CLI_LINE_SIZE]; int cd = code; + int errcode; int size, cnt; if (cd < 0) @@ -131,16 +132,26 @@ cli_printf(cli *c, int code, char *msg, ...) size = bsprintf(buf, " "); else size = bsprintf(buf, "%04d-", cd); + errcode = -8000; + } + else if (cd == CLI_ASYNC_CODE) + { + size = 1; buf[0] = '+'; + errcode = cd; } else - size = bsprintf(buf, "%04d ", cd); + { + size = bsprintf(buf, "%04d ", cd); + errcode = 8000; + } + c->last_reply = cd; va_start(args, msg); cnt = bvsnprintf(buf+size, sizeof(buf)-size-1, msg, args); va_end(args); if (cnt < 0) { - cli_printf(c, code < 0 ? -8000 : 8000, ""); + cli_printf(c, errcode, ""); return; } size += cnt; @@ -385,12 +396,17 @@ cli_echo(unsigned int class, byte *msg) } } +/* Hack for scheduled undo notification */ +extern cli *cmd_reconfig_stored_cli; + void cli_free(cli *c) { cli_set_log_echo(c, 0, 0); if (c->cleanup) c->cleanup(c); + if (c == cmd_reconfig_stored_cli) + cmd_reconfig_stored_cli = NULL; rfree(c->pool); } diff --git a/nest/cli.h b/nest/cli.h index ea64680a..396656e8 100644 --- a/nest/cli.h +++ b/nest/cli.h @@ -49,6 +49,8 @@ typedef struct cli { extern pool *cli_pool; extern struct cli *this_cli; /* Used during parsing */ +#define CLI_ASYNC_CODE 10000 + /* Functions to be called by command handlers */ void cli_printf(cli *, int, char *, ...); diff --git a/nest/cmds.c b/nest/cmds.c index 2a803930..54ace169 100644 --- a/nest/cmds.c +++ b/nest/cmds.c @@ -14,6 +14,9 @@ #include "lib/string.h" #include "lib/resource.h" +extern int shutting_down; +extern int configuring; + void cmd_show_status(void) { @@ -27,9 +30,10 @@ cmd_show_status(void) cli_msg(-1011, "Last reboot on %s", tim); tm_format_datetime(tim, &config->tf_base, config->load_time); cli_msg(-1011, "Last reconfiguration on %s", tim); + if (shutting_down) cli_msg(13, "Shutdown in progress"); - else if (old_config) + else if (configuring) cli_msg(13, "Reconfiguration in progress"); else cli_msg(13, "Daemon is up and running"); diff --git a/nest/proto.c b/nest/proto.c index e9afa2fe..1334884e 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -516,7 +516,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART; p->cf_new = nc; } - else if (!shutting_down) + else if (!new->shutdown) { log(L_INFO "Removing protocol %s", p->name); p->down_code = PDC_CF_REMOVE; @@ -537,7 +537,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty WALK_LIST(nc, new->protos) if (!nc->proto) { - if (old_config) /* Not a first-time configuration */ + if (old) /* Not a first-time configuration */ log(L_INFO "Adding protocol %s", nc->name); proto_init(nc); } diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y index 844f53df..7bade918 100644 --- a/sysdep/unix/config.Y +++ b/sysdep/unix/config.Y @@ -14,9 +14,9 @@ CF_HDR CF_DECLS CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT) -CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, BASE, NAME) +CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, BASE, NAME, CONFIRM, UNDO, CHECK, TIMEOUT) -%type log_mask log_mask_list log_cat +%type log_mask log_mask_list log_cat cfg_timeout %type log_file %type cfg_name %type timeformat_which @@ -104,13 +104,26 @@ timeformat_base: /* Unix specific commands */ -CF_CLI_HELP(CONFIGURE, [soft] [\"\"], [[Reload configuration]]) +CF_CLI_HELP(CONFIGURE, ..., [[Reload configuration]]) -CF_CLI(CONFIGURE, cfg_name, [\"\"], [[Reload configuration]]) -{ cmd_reconfig($2, RECONFIG_HARD); } ; +CF_CLI(CONFIGURE, cfg_name cfg_timeout, [\"\"] [timeout []], [[Reload configuration]]) +{ cmd_reconfig($2, RECONFIG_HARD, $3); } ; -CF_CLI(CONFIGURE SOFT, cfg_name, [\"\"], [[Reload configuration and ignore changes in filters]]) -{ cmd_reconfig($3, RECONFIG_SOFT); } ; +CF_CLI(CONFIGURE SOFT, cfg_name cfg_timeout, [\"\"] [timeout []], [[Reload configuration and ignore changes in filters]]) +{ cmd_reconfig($3, RECONFIG_SOFT, $4); } ; + +/* Hack to get input completion for 'timeout' */ +CF_CLI_CMD(CONFIGURE TIMEOUT, [], [[Reload configuration with undo timeout]]) +CF_CLI_CMD(CONFIGURE SOFT TIMEOUT, [], [[Reload configuration with undo timeout]]) + +CF_CLI(CONFIGURE CONFIRM,,, [[Confirm last configuration change - deactivate undo timeout]]) +{ cmd_reconfig_confirm(); } ; + +CF_CLI(CONFIGURE UNDO,,, [[Undo last configuration change]]) +{ cmd_reconfig_undo(); } ; + +CF_CLI(CONFIGURE CHECK, cfg_name, [\"\"], [[Parse configuration and check its validity]]) +{ cmd_check_config($3); } ; CF_CLI(DOWN,,, [[Shut the daemon down]]) { cmd_shutdown(); } ; @@ -120,6 +133,12 @@ cfg_name: | TEXT ; +cfg_timeout: + /* empty */ { $$ = 0; } + | TIMEOUT { $$ = UNIX_DEFAULT_CONFIGURE_TIMEOUT; } + | TIMEOUT expr { $$ = $2; } + ; + CF_CODE CF_END diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index f91b5278..80914afe 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -121,7 +121,7 @@ static list near_timers, far_timers; static bird_clock_t first_far_timer = TIME_INFINITY; /* now must be different from 0, because 0 is a special value in timer->expires */ -bird_clock_t now = 1, now_real; +bird_clock_t now = 1, now_real, boot_time; static void update_times_plain(void) @@ -1530,6 +1530,7 @@ io_init(void) krt_io_init(); init_times(); update_times(); + boot_time = now; srandom((int) now_real); } @@ -1557,7 +1558,7 @@ io_loop(void) tm_shot(); continue; } - timo.tv_sec = events ? 0 : tout - now; + timo.tv_sec = events ? 0 : MIN(tout - now, 3); timo.tv_usec = 0; if (sock_recalc_fdsets_p) diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 6c0e5e91..3761ace6 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -900,7 +900,7 @@ krt_notify(struct proto *P, struct rtable *table UNUSED, net *net, { struct krt_proto *p = (struct krt_proto *) P; - if (shutting_down) + if (config->shutdown) return; if (!(net->n.flags & KRF_INSTALLED)) old = NULL; diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index f0344a8f..23040e54 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -210,7 +210,7 @@ read_config(void) else die("Unable to open configuration file %s: %m", config_name); } - config_commit(conf, RECONFIG_HARD); + config_commit(conf, RECONFIG_HARD, 0); } void @@ -228,19 +228,17 @@ async_config(void) config_free(conf); } else - config_commit(conf, RECONFIG_HARD); + config_commit(conf, RECONFIG_HARD, 0); } -void -cmd_reconfig(char *name, int type) +static struct config * +cmd_read_config(char *name) { struct config *conf; - if (cli_access_restricted()) - return; - if (!name) name = config_name; + cli_msg(-2, "Reading configuration from %s", name); if (!unix_read_config(&conf, name)) { @@ -249,24 +247,94 @@ cmd_reconfig(char *name, int type) else cli_msg(8002, "%s: %m", name); config_free(conf); + conf = NULL; } - else + + return conf; +} + +void +cmd_check_config(char *name) +{ + struct config *conf = cmd_read_config(name); + if (!conf) + return; + + cli_msg(20, "Configuration OK"); + config_free(conf); +} + +static void +cmd_reconfig_msg(int r) +{ + switch (r) { - switch (config_commit(conf, type)) - { - case CONF_DONE: - cli_msg(3, "Reconfigured."); - break; - case CONF_PROGRESS: - cli_msg(4, "Reconfiguration in progress."); - break; - case CONF_SHUTDOWN: - cli_msg(6, "Reconfiguration ignored, shutting down."); - break; - default: - cli_msg(5, "Reconfiguration already in progress, queueing new config"); - } + case CONF_DONE: cli_msg( 3, "Reconfigured"); break; + case CONF_PROGRESS: cli_msg( 4, "Reconfiguration in progress"); break; + case CONF_QUEUED: cli_msg( 5, "Reconfiguration already in progress, queueing new config"); break; + case CONF_UNQUEUED: cli_msg(17, "Reconfiguration already in progress, removing queued config"); break; + case CONF_CONFIRM: cli_msg(18, "Reconfiguration confirmed"); break; + case CONF_SHUTDOWN: cli_msg( 6, "Reconfiguration ignored, shutting down"); break; + case CONF_NOTHING: cli_msg(19, "Nothing to do"); break; + default: break; + } +} + +/* Hack for scheduled undo notification */ +cli *cmd_reconfig_stored_cli; + +void +cmd_reconfig_undo_notify(void) +{ + if (cmd_reconfig_stored_cli) + { + cli *c = cmd_reconfig_stored_cli; + cli_printf(c, CLI_ASYNC_CODE, "Config timeout expired, starting undo"); + cli_write_trigger(c); + } +} + +void +cmd_reconfig(char *name, int type, int timeout) +{ + if (cli_access_restricted()) + return; + + struct config *conf = cmd_read_config(name); + if (!conf) + return; + + int r = config_commit(conf, type, timeout); + + if ((r >= 0) && (timeout > 0)) + { + cmd_reconfig_stored_cli = this_cli; + cli_msg(-22, "Undo scheduled in %d s", timeout); } + + cmd_reconfig_msg(r); +} + +void +cmd_reconfig_confirm(void) +{ + if (cli_access_restricted()) + return; + + int r = config_confirm(); + cmd_reconfig_msg(r); +} + +void +cmd_reconfig_undo(void) +{ + if (cli_access_restricted()) + return; + + cli_msg(-21, "Undo requested"); + + int r = config_undo(); + cmd_reconfig_msg(r); } /* @@ -623,6 +691,7 @@ main(int argc, char **argv) rt_init(); if_init(); roa_init(); + config_init(); uid_t use_uid = get_uid(use_user); gid_t use_gid = get_gid(use_group); diff --git a/sysdep/unix/timer.h b/sysdep/unix/timer.h index a788ae27..17450322 100644 --- a/sysdep/unix/timer.h +++ b/sysdep/unix/timer.h @@ -32,6 +32,7 @@ void tm_dump_all(void); extern bird_clock_t now; /* Relative, monotonic time in seconds */ extern bird_clock_t now_real; /* Time in seconds since fixed known epoch */ +extern bird_clock_t boot_time; static inline bird_clock_t tm_remains(timer *t) diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index 3e85c85c..1fc26db2 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -19,9 +19,14 @@ extern char *bird_name; void async_config(void); void async_dump(void); void async_shutdown(void); -void cmd_reconfig(char *name, int type); +void cmd_check_config(char *name); +void cmd_reconfig(char *name, int type, int timeout); +void cmd_reconfig_confirm(void); +void cmd_reconfig_undo(void); void cmd_shutdown(void); +#define UNIX_DEFAULT_CONFIGURE_TIMEOUT 300 + /* io.c */ volatile int async_config_flag; -- cgit v1.2.3 From 79b4e12e6032faf6bb1f3feac385bd36ee53019e Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 27 Dec 2012 12:56:23 +0100 Subject: Implements interface masks for choosing router id. Router ID could be automatically determined based of subset of ifaces/addresses specified by 'router id from' option. The patch also does some minor changes related to router ID reconfiguration. Thanks to Alexander V. Chernikov for most of the work. --- conf/conf.c | 16 +++++++++++--- conf/conf.h | 1 + doc/bird.sgml | 13 ++++++++++- nest/config.Y | 17 +++++++++++--- nest/iface.c | 66 +++++++++++++++++++++++++++++++++++++++---------------- nest/iface.h | 4 ++++ nest/proto.c | 14 +++++++++--- proto/bgp/bgp.c | 3 +++ proto/ospf/ospf.c | 3 +++ 9 files changed, 108 insertions(+), 29 deletions(-) diff --git a/conf/conf.c b/conf/conf.c index 6dfa3691..14225d3b 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -200,9 +200,19 @@ global_commit(struct config *new, struct config *old) log(L_WARN "Reconfiguration of BGP listening socket not implemented, please restart BIRD."); if (!new->router_id) - new->router_id = old->router_id; - if (new->router_id != old->router_id) - return 1; + { + new->router_id = old->router_id; + + if (new->router_id_from) + { + u32 id = if_choose_router_id(new->router_id_from, old->router_id); + if (!id) + log(L_WARN "Cannot determine router ID, using old one"); + else + new->router_id = id; + } + } + return 0; } diff --git a/conf/conf.h b/conf/conf.h index 19300f54..683374e0 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -26,6 +26,7 @@ struct config { int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */ char *syslog_name; /* Name used for syslog (NULL -> no syslog) */ struct rtable_config *master_rtc; /* Configuration of master routing table */ + struct iface_patt *router_id_from; /* Configured list of router ID iface patterns */ u32 router_id; /* Our Router ID */ ip_addr listen_bgp_addr; /* Listening BGP socket should use this address */ diff --git a/doc/bird.sgml b/doc/bird.sgml index 615ced98..4e04a138 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -337,7 +337,18 @@ protocol rip { Besides, there are some predefined numeric constants based on /etc/iproute2/rt_* files. A list of defined constants can be seen (together with other symbols) using 'show symbols' command. - router id Set BIRD's router ID. It's a world-wide unique identification of your router, usually one of router's IPv4 addresses. Default: in IPv4 version, the lowest IP address of a non-loopback interface. In IPv6 version, this option is mandatory. + router id + Set BIRD's router ID. It's a world-wide unique identification + of your router, usually one of router's IPv4 addresses. + Default: in IPv4 version, the lowest IP address of a + non-loopback interface. In IPv6 version, this option is + mandatory. + + router id from [-] [ " + Set BIRD's router ID based on an IP address of an interface + specified by an interface pattern. The option is applicable + for IPv4 version only. See + section for detailed description of interface patterns. listen bgp [address This option allows to specify address and port where BGP diff --git a/nest/config.Y b/nest/config.Y index cb6a85c2..dbd72055 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -75,9 +75,9 @@ CF_GRAMMAR CF_ADDTO(conf, rtrid) -rtrid: ROUTER ID idval ';' { - new_config->router_id = $3; - } +rtrid: + ROUTER ID idval ';' { new_config->router_id = $3; } + | ROUTER ID FROM iface_patt ';' { new_config->router_id_from = this_ipatt; } ; idval: @@ -264,6 +264,17 @@ iface_patt_list: | iface_patt_list ',' iface_patt_node ; +iface_patt_init: { + /* Generic this_ipatt init */ + this_ipatt = cfg_allocz(sizeof(struct iface_patt)); + init_list(&this_ipatt->ipn_list); + } + ; + +iface_patt: + iface_patt_init iface_patt_list + ; + /* Direct device route protocol */ diff --git a/nest/iface.c b/nest/iface.c index eea3d3b1..da79b21f 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -35,8 +35,6 @@ static pool *if_pool; -static void auto_router_id(void); - list iface_list; /** @@ -354,9 +352,6 @@ if_end_update(void) struct iface *i; struct ifa *a, *b; - if (!config->router_id) - auto_router_id(); - WALK_LIST(i, iface_list) { if (!(i->flags & IF_UPDATED)) @@ -583,24 +578,57 @@ ifa_delete(struct ifa *a) } } -static void -auto_router_id(void) +u32 +if_choose_router_id(struct iface_patt *mask, u32 old_id) { #ifndef IPV6 - struct iface *i, *j; + struct iface *i; + struct ifa *a, *b; - j = NULL; + b = NULL; WALK_LIST(i, iface_list) - if ((i->flags & IF_ADMIN_UP) && - !(i->flags & (IF_IGNORE | IF_SHUTDOWN)) && - i->addr && - !(i->addr->flags & IA_PEER) && - (!j || ipa_to_u32(i->addr->ip) < ipa_to_u32(j->addr->ip))) - j = i; - if (!j) - die("Cannot determine router ID (no suitable network interface found), please configure it manually"); - log(L_INFO "Guessed router ID %I according to interface %s", j->addr->ip, j->name); - config->router_id = ipa_to_u32(j->addr->ip); + { + if (!(i->flags & IF_ADMIN_UP) || + (i->flags & (IF_IGNORE | IF_SHUTDOWN))) + continue; + + WALK_LIST(a, i->addrs) + { + if (a->flags & IA_SECONDARY) + continue; + + if (a->scope <= SCOPE_LINK) + continue; + + /* FIXME: This should go away */ + if (a->flags & IA_PEER) + continue; + + /* FIXME: This should go away too */ + if (!mask && (a != i->addr)) + continue; + + /* Check pattern if specified */ + if (mask && !iface_patt_match(mask, i, a)) + continue; + + /* No pattern or pattern matched */ + if (!b || ipa_to_u32(a->ip) < ipa_to_u32(b->ip)) + b = a; + } + } + + if (!b) + return 0; + + u32 id = ipa_to_u32(b->ip); + if (id != old_id) + log(L_INFO "Chosen router ID %R according to interface %s", id, b->iface->name); + + return id; + +#else + return 0; #endif } diff --git a/nest/iface.h b/nest/iface.h index 2416f82f..697ea543 100644 --- a/nest/iface.h +++ b/nest/iface.h @@ -101,6 +101,7 @@ struct iface *if_find_by_name(char *); struct iface *if_get_by_name(char *); void ifa_recalc_all_primary_addresses(void); + /* The Neighbor Cache */ typedef struct neighbor { @@ -161,4 +162,7 @@ int iface_patt_match(struct iface_patt *ifp, struct iface *i, struct ifa *a); struct iface_patt *iface_patt_find(list *l, struct iface *i, struct ifa *a); int iface_patts_equal(list *, list *, int (*)(struct iface_patt *, struct iface_patt *)); + +u32 if_choose_router_id(struct iface_patt *mask, u32 old_id); + #endif diff --git a/nest/proto.c b/nest/proto.c index 1334884e..b976a6cb 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -382,11 +382,9 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config /* If there is a too big change in core attributes, ... */ if ((nc->protocol != oc->protocol) || (nc->disabled != p->disabled) || - (nc->table->table != oc->table->table) || - (proto_get_router_id(nc) != proto_get_router_id(oc))) + (nc->table->table != oc->table->table)) return 0; - p->debug = nc->debug; p->mrtdump = nc->mrtdump; proto_reconfig_type = type; @@ -552,6 +550,16 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty initial_device_proto = NULL; } + /* Determine router ID for the first time - it has to be here and not in + global_commit() because it is postponed after start of device protocol */ + if (!config->router_id) + { + config->router_id = if_choose_router_id(config->router_id_from, 0); + if (!config->router_id) + die("Cannot determine router ID, please configure it manually"); + } + + /* Start all other protocols */ WALK_LIST_DELSAFE(p, n, initial_proto_list) proto_rethink_goal(p); } diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 346c641b..249d2e07 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1009,6 +1009,9 @@ bgp_reconfigure(struct proto *P, struct proto_config *C) struct bgp_proto *p = (struct bgp_proto *) P; struct bgp_config *old = p->cf; + if (proto_get_router_id(C) != p->local_id) + return 0; + int same = !memcmp(((byte *) old) + sizeof(struct proto_config), ((byte *) new) + sizeof(struct proto_config), // password item is last and must be checked separately diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index 6654e107..a3b6b2e7 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -729,6 +729,9 @@ ospf_reconfigure(struct proto *p, struct proto_config *c) struct ospf_iface *ifa, *ifx; struct ospf_iface_patt *ip; + if (proto_get_router_id(c) != po->router_id) + return 0; + if (po->rfc1583 != new->rfc1583) return 0; -- cgit v1.2.3 From b662290f40ea0fa0b1a1ba283e50e833724f2050 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 10 Jan 2013 13:07:33 +0100 Subject: Separate import and receive limits. They have different behavior w.r.t. filtered routes that are kept. --- README | 2 +- doc/bird.sgml | 20 ++++++++++++++------ nest/config.Y | 3 ++- nest/proto.c | 21 +++++++++++++++------ nest/protocol.h | 15 ++++++++++++--- nest/rt-table.c | 49 ++++++++++++++++++++++++++++++++++++++++++++----- proto/bgp/bgp.c | 1 + proto/pipe/pipe.c | 5 +++++ 8 files changed, 94 insertions(+), 22 deletions(-) diff --git a/README b/README index 5c2ef076..daeb18bd 100644 --- a/README +++ b/README @@ -3,7 +3,7 @@ (c) 1998--2008 Martin Mares (c) 1998--2000 Pavel Machek (c) 1998--2008 Ondrej Filip - (c) 2009--2011 CZ.NIC z.s.p.o. + (c) 2009--2013 CZ.NIC z.s.p.o. ================================================================================ diff --git a/doc/bird.sgml b/doc/bird.sgml index 4e04a138..b0d4e6a1 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -482,15 +482,23 @@ to zero to disable it. An empty is equivalent to receive limit + Specify an receive route limit (a maximum number of routes + received from the protocol and remembered). It works almost + identically to import limit option, the only + difference is that if export limit Specify an export route limit, works similarly to diff --git a/nest/config.Y b/nest/config.Y index dbd72055..e46b5fb5 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -44,7 +44,7 @@ CF_DECLS CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT) CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS) -CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED) +CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED) CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, MAX, FLUSH) CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED) @@ -185,6 +185,7 @@ proto_item: | MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; } | IMPORT imexport { this_proto->in_filter = $2; } | EXPORT imexport { this_proto->out_filter = $2; } + | RECEIVE LIMIT limit_spec { this_proto->rx_limit = $3; } | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; } | EXPORT LIMIT limit_spec { this_proto->out_limit = $3; } | IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; } diff --git a/nest/proto.c b/nest/proto.c index b976a6cb..7e7fb7fa 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -344,6 +344,7 @@ protos_postconfig(struct config *c) WALK_LIST(x, c->protos) { DBG(" %s", x->name); + p = x->protocol; if (p->postconfig) p->postconfig(x); @@ -410,6 +411,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config { p->main_ahook->in_filter = nc->in_filter; p->main_ahook->out_filter = nc->out_filter; + p->main_ahook->rx_limit = nc->rx_limit; p->main_ahook->in_limit = nc->in_limit; p->main_ahook->out_limit = nc->out_limit; p->main_ahook->in_keep_filtered = nc->in_keep_filtered; @@ -804,9 +806,11 @@ proto_schedule_feed(struct proto *p, int initial) p->main_ahook = proto_add_announce_hook(p, p->table, &p->stats); p->main_ahook->in_filter = p->cf->in_filter; p->main_ahook->out_filter = p->cf->out_filter; + p->main_ahook->rx_limit = p->cf->rx_limit; p->main_ahook->in_limit = p->cf->in_limit; p->main_ahook->out_limit = p->cf->out_limit; p->main_ahook->in_keep_filtered = p->cf->in_keep_filtered; + proto_reset_limit(p->main_ahook->rx_limit); proto_reset_limit(p->main_ahook->in_limit); proto_reset_limit(p->main_ahook->out_limit); } @@ -978,6 +982,7 @@ proto_limit_name(struct proto_limit *l) * proto_notify_limit: notify about limit hit and take appropriate action * @ah: announce hook * @l: limit being hit + * @dir: limit direction (PLD_*) * @rt_count: the number of routes * * The function is called by the route processing core when limit @l @@ -985,10 +990,11 @@ proto_limit_name(struct proto_limit *l) * according to @l->action. */ void -proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count) +proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count) { + const char *dir_name[PLD_MAX] = { "receive", "import" , "export" }; + const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT }; struct proto *p = ah->proto; - int dir = (ah->in_limit == l); if (l->state == PLS_BLOCKED) return; @@ -996,7 +1002,7 @@ proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count /* For warning action, we want the log message every time we hit the limit */ if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit))) log(L_WARN "Protocol %s hits route %s limit (%d), action: %s", - p->name, dir ? "import" : "export", l->limit, proto_limit_name(l)); + p->name, dir_name[dir], l->limit, proto_limit_name(l)); switch (l->action) { @@ -1011,8 +1017,7 @@ proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count case PLA_RESTART: case PLA_DISABLE: l->state = PLS_BLOCKED; - proto_schedule_down(p, l->action == PLA_RESTART, - dir ? PDC_IN_LIMIT_HIT : PDC_OUT_LIMIT_HIT); + proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]); break; } } @@ -1146,6 +1151,7 @@ proto_show_basic_info(struct proto *p) cli_msg(-1006, " Input filter: %s", filter_name(p->cf->in_filter)); cli_msg(-1006, " Output filter: %s", filter_name(p->cf->out_filter)); + proto_show_limit(p->cf->rx_limit, "Receive limit:"); proto_show_limit(p->cf->in_limit, "Import limit:"); proto_show_limit(p->cf->out_limit, "Export limit:"); @@ -1267,7 +1273,10 @@ proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED) * Perhaps, but these hooks work asynchronously. */ if (!p->proto->multitable) - proto_reset_limit(p->main_ahook->in_limit); + { + proto_reset_limit(p->main_ahook->rx_limit); + proto_reset_limit(p->main_ahook->in_limit); + } } /* re-exporting routes */ diff --git a/nest/protocol.h b/nest/protocol.h index cf2ca0a4..033a0ede 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -95,6 +95,8 @@ struct proto_config { u32 router_id; /* Protocol specific router ID */ struct rtable_config *table; /* Table we're attached to */ struct filter *in_filter, *out_filter; /* Attached filters */ + struct proto_limit *rx_limit; /* Limit for receiving routes from protocol + (relevant when in_keep_filtered is active) */ struct proto_limit *in_limit; /* Limit for importing routes from protocol */ struct proto_limit *out_limit; /* Limit for exporting routes to protocol */ @@ -225,8 +227,9 @@ struct proto_spec { #define PDC_CMD_DISABLE 0x11 /* Result of disable command */ #define PDC_CMD_RESTART 0x12 /* Result of restart command */ #define PDC_CMD_SHUTDOWN 0x13 /* Result of global shutdown */ -#define PDC_IN_LIMIT_HIT 0x21 /* Route import limit reached */ -#define PDC_OUT_LIMIT_HIT 0x22 /* Route export limit reached */ +#define PDC_RX_LIMIT_HIT 0x21 /* Route receive limit reached */ +#define PDC_IN_LIMIT_HIT 0x22 /* Route import limit reached */ +#define PDC_OUT_LIMIT_HIT 0x23 /* Route export limit reached */ void *proto_new(struct proto_config *, unsigned size); @@ -373,6 +376,11 @@ extern struct proto_config *cf_dev_proto; * Protocol limits */ +#define PLD_RX 0 /* Receive limit */ +#define PLD_IN 1 /* Import limit */ +#define PLD_OUT 2 /* Export limit */ +#define PLD_MAX 3 + #define PLA_WARN 1 /* Issue log warning */ #define PLA_BLOCK 2 /* Block new routes */ #define PLA_RESTART 4 /* Force protocol restart */ @@ -388,7 +396,7 @@ struct proto_limit { byte state; /* State of limit (PLS_*) */ }; -void proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count); +void proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count); static inline void proto_reset_limit(struct proto_limit *l) @@ -408,6 +416,7 @@ struct announce_hook { struct proto *proto; struct filter *in_filter; /* Input filter */ struct filter *out_filter; /* Output filter */ + struct proto_limit *rx_limit; /* Receive limit (for in_keep_filtered) */ struct proto_limit *in_limit; /* Input limit */ struct proto_limit *out_limit; /* Output limit */ struct proto_stats *stats; /* Per-table protocol statistics */ diff --git a/nest/rt-table.c b/nest/rt-table.c index 2f0840f0..99175448 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -285,7 +285,7 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm if (l && new) { if ((!old || refeed) && (stats->exp_routes >= l->limit)) - proto_notify_limit(ah, l, stats->exp_routes); + proto_notify_limit(ah, l, PLD_OUT, stats->exp_routes); if (l->state == PLS_BLOCKED) { @@ -700,16 +700,22 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str return; } - struct proto_limit *l = ah->in_limit; + int new_ok = rte_is_ok(new); + int old_ok = rte_is_ok(old); + + struct proto_limit *l = ah->rx_limit; if (l && !old && new) { u32 all_routes = stats->imp_routes + stats->filt_routes; if (all_routes >= l->limit) - proto_notify_limit(ah, l, all_routes); + proto_notify_limit(ah, l, PLD_RX, all_routes); if (l->state == PLS_BLOCKED) { + /* In receive limit the situation is simple, old is NULL so + we just free new and exit like nothing happened */ + stats->imp_updates_ignored++; rte_trace_in(D_FILTERS, p, new, "ignored [limit]"); rte_free_quick(new); @@ -717,8 +723,39 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str } } - int new_ok = rte_is_ok(new); - int old_ok = rte_is_ok(old); + l = ah->in_limit; + if (l && !old_ok && new_ok) + { + if (stats->imp_routes >= l->limit) + proto_notify_limit(ah, l, PLD_IN, stats->imp_routes); + + if (l->state == PLS_BLOCKED) + { + /* In import limit the situation is more complicated. We + shouldn't just drop the route, we should handle it like + it was filtered. We also have to continue the route + processing if old or new is non-NULL, but we should exit + if both are NULL as this case is probably assumed to be + already handled. */ + + stats->imp_updates_ignored++; + rte_trace_in(D_FILTERS, p, new, "ignored [limit]"); + + if (ah->in_keep_filtered) + new->flags |= REF_FILTERED; + else + { rte_free_quick(new); new = NULL; } + + /* Note that old && !new could be possible when + ah->in_keep_filtered changed in the recent past. */ + + if (!old && !new) + return; + + new_ok = 0; + goto skip_stats1; + } + } if (new_ok) stats->imp_updates_accepted++; @@ -727,6 +764,8 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str else stats->imp_withdraws_ignored++; + skip_stats1: + if (new) rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++; if (old) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 249d2e07..0f351b44 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -878,6 +878,7 @@ bgp_shutdown(struct proto *P) subcode = 4; // Errcode 6, 4 - administrative reset break; + case PDC_RX_LIMIT_HIT: case PDC_IN_LIMIT_HIT: subcode = 1; // Errcode 6, 1 - max number of prefixes reached /* log message for compatibility */ diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index 6099d284..51be3c7d 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -200,6 +200,11 @@ pipe_postconfig(struct proto_config *C) cf_error("Name of peer routing table not specified"); if (c->peer == C->table) cf_error("Primary table and peer table must be different"); + + if (C->in_keep_filtered) + cf_error("Pipe protocol prohibits keeping filtered routes"); + if (C->rx_limit) + cf_error("Pipe protocol does not support receive limits"); } extern int proto_reconfig_type; -- cgit v1.2.3 From 13d4dd138d5dc6c884ded280f9244fac707c4f32 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 11 Jan 2013 14:53:20 +0100 Subject: NEWS update. --- NEWS | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index f8e76601..16e1f6d1 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ -Version 1.3.9 (2012-11-16) +Version 1.3.9 (2013-01-11) o BIRD can be configured to keep and show filtered routes. + o Separate receive and import limits. + o Several new reconfiguration cmd options (undo, timeout, check). + o Configurable automatic router ID selection. o Dragonfly BSD support. o Fixed OSPFv3 vlinks. o Several minor bugfixes. -- cgit v1.2.3 From d214ae4fdc1e323f89efb8a80c068fef4a45758f Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 12 Jan 2013 21:26:42 +0100 Subject: Fix missing documentation for one option. --- doc/bird.sgml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/bird.sgml b/doc/bird.sgml index b0d4e6a1..f6f9aad7 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2546,6 +2546,13 @@ interface definitions, prefix definitions and DNS definitions:

Prefix specific options: + skip + This option allows to specify that given prefix should not be + advertised. This is useful for making exceptions from a + default policy of advertising all prefixes. Note that for + withdrawing an already advertised prefix it is more useful to + advertise it with zero valid lifetime. Default: no + onlink This option specifies whether hosts may use the advertised prefix for onlink determination. Default: yes -- cgit v1.2.3 From 36da2857bc911924a250a234f38cf58c3b21f1bc Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 8 Feb 2013 23:58:27 +0100 Subject: Implements router advertisements activated by received routes. The RAdv protocol could be configured to change its behavior based on availability of routes, e.g., do not announce router lifetime when a default route is not available. --- doc/bird.sgml | 43 +++++++++++++++++++------ filter/filter.c | 6 ++++ nest/route.h | 7 ++++ nest/rt-table.c | 37 ++++++++++++++++++--- proto/radv/config.Y | 36 ++++++++++++++++++--- proto/radv/packets.c | 32 ++++++++++-------- proto/radv/radv.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++-- proto/radv/radv.h | 8 +++++ 8 files changed, 225 insertions(+), 35 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index f6f9aad7..762834e3 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -691,8 +691,8 @@ This argument can be omitted if there exists only a single instance.

You can also select just routes added by a specific protocol. protocol . -

If BIRD is configured to keep filtered routes (see If BIRD is configured to keep filtered routes (see The dnssl that just specifies one DNS search domain. + +

Interface specific options: @@ -2525,11 +2545,12 @@ interface definitions, prefix definitions and DNS definitions: This option specifies which value of Hop Limit should be used by hosts. Valid values are 0-255, 0 means unspecified. Default: 64 - default lifetime + default lifetime This option specifies the time (in seconds) how long (after the receipt of RA) hosts may use the router as a default - router. 0 means do not use as a default router. Default: 3 * - . + Default: 3 * rdnss local Use only local (interface-specific) RDNSS definitions for this @@ -2561,18 +2582,20 @@ interface definitions, prefix definitions and DNS definitions: This option specifies whether hosts may use the advertised prefix for stateless autoconfiguration. Default: yes - valid lifetime + valid lifetime This option specifies the time (in seconds) how long (after the receipt of RA) the prefix information is valid, i.e., autoconfigured IP addresses can be assigned and hosts with that IP addresses are considered directly reachable. 0 means - the prefix is no longer valid. Default: 86400 (1 day) + the prefix is no longer valid. For . Default: 86400 (1 day), preferred lifetime + preferred lifetime This option specifies the time (in seconds) how long (after the receipt of RA) IP addresses generated from the prefix - using stateless autoconfiguration remain preferred. Default: - 14400 (4 hours) + using stateless autoconfiguration remain preferred. For + . + Default: 14400 (4 hours), diff --git a/filter/filter.c b/filter/filter.c index 44fcf293..c35d0425 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1429,6 +1429,12 @@ i_same(struct f_inst *f1, struct f_inst *f2) int f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struct linpool *tmp_pool, int flags) { + if (filter == FILTER_ACCEPT) + return F_ACCEPT; + + if (filter == FILTER_REJECT) + return F_REJECT; + int rte_cow = ((*rte)->flags & REF_COW); DBG( "Running filter `%s'...", filter->name ); diff --git a/nest/route.h b/nest/route.h index 177baa38..8fd01a66 100644 --- a/nest/route.h +++ b/nest/route.h @@ -235,6 +235,12 @@ static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED); #define RA_ACCEPTED 2 /* Announcement of first accepted route */ #define RA_ANY 3 /* Announcement of any route change */ +/* Return value of import_control() callback */ +#define RIC_ACCEPT 1 /* Accepted by protocol */ +#define RIC_PROCESS 0 /* Process it through import filter */ +#define RIC_REJECT -1 /* Rejected by protocol */ +#define RIC_DROP -2 /* Silently dropped by protocol */ + struct config; void rt_init(void); @@ -250,6 +256,7 @@ rte *rte_get_temp(struct rta *); void rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src); static inline void rte_update(rtable *tab, net *net, struct proto *p, struct proto *src, rte *new) { rte_update2(p->main_ahook, net, new, src); } void rte_discard(rtable *tab, rte *old); +int rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter); void rte_dump(rte *); void rte_free(rte *); rte *rte_do_cow(rte *); diff --git a/nest/rt-table.c b/nest/rt-table.c index 99175448..75bfa6ba 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -213,7 +213,8 @@ export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa, goto reject; stats->exp_updates_rejected++; - rte_trace_out(D_FILTERS, p, rt, "rejected by protocol"); + if (v == RIC_REJECT) + rte_trace_out(D_FILTERS, p, rt, "rejected by protocol"); goto reject; } if (v > 0) @@ -1042,6 +1043,34 @@ rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during gar rte_update_unlock(); } +/* Check rtable for best route to given net whether it would be exported do p */ +int +rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter) +{ + net *n = net_find(t, prefix, pxlen); + rte *rt = n ? n->routes : NULL; + + if (!rte_is_valid(rt)) + return 0; + + rte_update_lock(); + + /* Rest is stripped down export_filter() */ + struct proto *src = rt->attrs->proto; + ea_list *tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(rt, rte_update_pool) : NULL; + int v = p->import_control ? p->import_control(p, &rt, &tmpa, rte_update_pool) : 0; + if (v == RIC_PROCESS) + v = (f_run(filter, &rt, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT); + + /* Discard temporary rte */ + if (rt != n->routes) + rte_free(rt); + + rte_update_unlock(); + + return v > 0; +} + /** * rte_dump - dump a route * @e: &rte to be dumped @@ -2081,7 +2110,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) ee = e; rte_update_lock(); /* We use the update buffer for filtering */ tmpa = p0->make_tmp_attrs ? p0->make_tmp_attrs(e, rte_update_pool) : NULL; - ok = (d->filter == FILTER_ACCEPT || f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT); + ok = f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT; if (p2 && p2 != p0) ok = 0; if (ok && d->export_mode) { @@ -2095,8 +2124,8 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) 'configure soft' command may change the export filter and do not update routes */ - if ((a = proto_find_announce_hook(p1, d->table)) && ((a->out_filter == FILTER_REJECT) || - (a->out_filter && f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))) + if ((a = proto_find_announce_hook(p1, d->table)) && + (f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)) ok = 0; } } diff --git a/proto/radv/config.Y b/proto/radv/config.Y index abccd2c7..fbec5a0a 100644 --- a/proto/radv/config.Y +++ b/proto/radv/config.Y @@ -30,9 +30,9 @@ CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL, MANAGED, OTHER, CONFIG, LINK, MTU, REACHABLE, TIME, RETRANS, TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT, LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN, - LOCAL) + LOCAL, TRIGGER, SENSITIVE) -%type radv_mult +%type radv_mult radv_sensitive CF_GRAMMAR @@ -53,6 +53,11 @@ radv_proto_item: | PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); } | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_CFG->rdnss_list, &radv_dns_list); } | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_CFG->dnssl_list, &radv_dns_list); } + | TRIGGER prefix { + RADV_CFG->trigger_prefix = $2.addr; + RADV_CFG->trigger_pxlen = $2.len; + RADV_CFG->trigger_valid = 1; + } ; radv_proto_opts: @@ -78,6 +83,7 @@ radv_iface_start: RADV_IFACE->min_delay = DEFAULT_MIN_DELAY; RADV_IFACE->current_hop_limit = DEFAULT_CURRENT_HOP_LIMIT; RADV_IFACE->default_lifetime = -1; + RADV_IFACE->default_lifetime_sensitive = 1; }; radv_iface_item: @@ -90,7 +96,11 @@ radv_iface_item: | REACHABLE TIME expr { RADV_IFACE->reachable_time = $3; if (($3 < 0) || ($3 > 3600000)) cf_error("Reachable time must be in range 0-3600000"); } | RETRANS TIMER expr { RADV_IFACE->retrans_timer = $3; if ($3 < 0) cf_error("Retrans timer must be 0 or positive"); } | CURRENT HOP LIMIT expr { RADV_IFACE->current_hop_limit = $4; if (($4 < 0) || ($4 > 255)) cf_error("Current hop limit must be in range 0-255"); } - | DEFAULT LIFETIME expr { RADV_IFACE->default_lifetime = $3; if (($3 < 0) || ($3 > 9000)) cf_error("Default lifetime must be in range 0-9000"); } + | DEFAULT LIFETIME expr radv_sensitive { + RADV_IFACE->default_lifetime = $3; + if (($3 < 0) || ($3 > 9000)) cf_error("Default lifetime must be in range 0-9000"); + if ($4 != -1) RADV_IFACE->default_lifetime_sensitive = $4; + } | PREFIX radv_prefix { add_tail(&RADV_IFACE->pref_list, NODE this_radv_prefix); } | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_IFACE->rdnss_list, &radv_dns_list); } | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_IFACE->dnssl_list, &radv_dns_list); } @@ -147,14 +157,25 @@ radv_prefix_item: SKIP bool { RADV_PREFIX->skip = $2; } | ONLINK bool { RADV_PREFIX->onlink = $2; } | AUTONOMOUS bool { RADV_PREFIX->autonomous = $2; } - | VALID LIFETIME expr { RADV_PREFIX->valid_lifetime = $3; if ($3 < 0) cf_error("Valid lifetime must be 0 or positive"); } - | PREFERRED LIFETIME expr { RADV_PREFIX->preferred_lifetime = $3; if ($3 < 0) cf_error("Preferred lifetime must be 0 or positive"); } + | VALID LIFETIME expr radv_sensitive { + RADV_PREFIX->valid_lifetime = $3; + if ($3 < 0) cf_error("Valid lifetime must be 0 or positive"); + if ($4 != -1) RADV_PREFIX->valid_lifetime_sensitive = $4; + } + | PREFERRED LIFETIME expr radv_sensitive { + RADV_PREFIX->preferred_lifetime = $3; + if ($3 < 0) cf_error("Preferred lifetime must be 0 or positive"); + if ($4 != -1) RADV_PREFIX->preferred_lifetime_sensitive = $4; + } ; radv_prefix_finish: { if (RADV_PREFIX->preferred_lifetime > RADV_PREFIX->valid_lifetime) cf_error("Preferred lifetime must be at most Valid lifetime"); + + if (RADV_PREFIX->valid_lifetime_sensitive > RADV_PREFIX->preferred_lifetime_sensitive) + cf_error("Valid lifetime sensitive requires that Preferred lifetime is sensitive too"); }; radv_prefix_opts: @@ -268,6 +289,11 @@ radv_mult: | MULT expr { $$ = 0; radv_mult_val = $2; if (($2 < 1) || ($2 > 254)) cf_error("Multiplier must be in range 1-254"); } ; +radv_sensitive: + /* empty */ { $$ = -1 } + | SENSITIVE bool { $$ = $2 } + ; + CF_CODE CF_END diff --git a/proto/radv/packets.c b/proto/radv/packets.c index 6fdfcaa3..dd839536 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -240,6 +240,7 @@ radv_prepare_ra(struct radv_iface *ifa) { struct proto_radv *ra = ifa->ra; struct radv_config *cf = (struct radv_config *) (ra->p.cf); + struct radv_iface_config *ic = ifa->cf; char *buf = ifa->sk->tbuf; char *bufstart = buf; @@ -249,21 +250,22 @@ radv_prepare_ra(struct radv_iface *ifa) pkt->type = ICMPV6_RA; pkt->code = 0; pkt->checksum = 0; - pkt->current_hop_limit = ifa->cf->current_hop_limit; - pkt->flags = (ifa->cf->managed ? OPT_RA_MANAGED : 0) | - (ifa->cf->other_config ? OPT_RA_OTHER_CFG : 0); - pkt->router_lifetime = htons(ifa->cf->default_lifetime); - pkt->reachable_time = htonl(ifa->cf->reachable_time); - pkt->retrans_timer = htonl(ifa->cf->retrans_timer); + pkt->current_hop_limit = ic->current_hop_limit; + pkt->flags = (ic->managed ? OPT_RA_MANAGED : 0) | + (ic->other_config ? OPT_RA_OTHER_CFG : 0); + pkt->router_lifetime = (ra->active || !ic->default_lifetime_sensitive) ? + htons(ic->default_lifetime) : 0; + pkt->reachable_time = htonl(ic->reachable_time); + pkt->retrans_timer = htonl(ic->retrans_timer); buf += sizeof(*pkt); - if (ifa->cf->link_mtu) + if (ic->link_mtu) { struct radv_opt_mtu *om = (void *) buf; om->type = OPT_MTU; om->length = 1; om->reserved = 0; - om->mtu = htonl(ifa->cf->link_mtu); + om->mtu = htonl(ic->link_mtu); buf += sizeof (*om); } @@ -288,26 +290,28 @@ radv_prepare_ra(struct radv_iface *ifa) op->pxlen = addr->pxlen; op->flags = (pc->onlink ? OPT_PX_ONLINK : 0) | (pc->autonomous ? OPT_PX_AUTONOMOUS : 0); - op->valid_lifetime = htonl(pc->valid_lifetime); - op->preferred_lifetime = htonl(pc->preferred_lifetime); + op->valid_lifetime = (ra->active || !pc->valid_lifetime_sensitive) ? + htonl(pc->valid_lifetime) : 0; + op->preferred_lifetime = (ra->active || !pc->preferred_lifetime_sensitive) ? + htonl(pc->preferred_lifetime) : 0; op->reserved = 0; op->prefix = addr->prefix; ipa_hton(op->prefix); buf += sizeof(*op); } - if (! ifa->cf->rdnss_local) + if (! ic->rdnss_local) if (radv_prepare_rdnss(ifa, &cf->rdnss_list, &buf, bufend) < 0) goto done; - if (radv_prepare_rdnss(ifa, &ifa->cf->rdnss_list, &buf, bufend) < 0) + if (radv_prepare_rdnss(ifa, &ic->rdnss_list, &buf, bufend) < 0) goto done; - if (! ifa->cf->dnssl_local) + if (! ic->dnssl_local) if (radv_prepare_dnssl(ifa, &cf->dnssl_list, &buf, bufend) < 0) goto done; - if (radv_prepare_dnssl(ifa, &ifa->cf->dnssl_list, &buf, bufend) < 0) + if (radv_prepare_dnssl(ifa, &ic->dnssl_list, &buf, bufend) < 0) goto done; done: diff --git a/proto/radv/radv.c b/proto/radv/radv.c index 5e7296a3..a6b9b16c 100644 --- a/proto/radv/radv.c +++ b/proto/radv/radv.c @@ -30,6 +30,13 @@ * by RA_EV_* codes), and radv_timer(), which triggers sending RAs and * computes the next timeout. * + * The RAdv protocol could receive routes (through + * radv_import_control() and radv_rt_notify()), but only the + * configured trigger route is tracked (in &active var). When a radv + * protocol is reconfigured, the connected routing table is examined + * (in radv_check_active()) to have proper &active value in case of + * the specified trigger prefix was changed. + * * Supported standards: * - RFC 4861 - main RA standard * - RFC 6106 - DNS extensions (RDDNS, DNSSL) @@ -93,6 +100,16 @@ radv_iface_notify(struct radv_iface *ifa, int event) tm_start(ifa->timer, after); } +static void +radv_iface_notify_all(struct proto_radv *ra, int event) +{ + struct radv_iface *ifa; + + WALK_LIST(ifa, ra->iface_list) + radv_iface_notify(ifa, event); +} + + static struct radv_iface * radv_iface_find(struct proto_radv *ra, struct iface *what) { @@ -238,11 +255,68 @@ radv_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) radv_iface_notify(ifa, RA_EV_CHANGE); } +static inline int radv_net_match_trigger(struct radv_config *cf, net *n) +{ + return cf->trigger_valid && + (n->n.pxlen == cf->trigger_pxlen) && + ipa_equal(n->n.prefix, cf->trigger_prefix); +} + +int +radv_import_control(struct proto *p, rte **new, ea_list **attrs UNUSED, struct linpool *pool UNUSED) +{ + // struct proto_radv *ra = (struct proto_radv *) p; + struct radv_config *cf = (struct radv_config *) (p->cf); + + if (radv_net_match_trigger(cf, (*new)->net)) + return RIC_PROCESS; + + return RIC_DROP; +} + +static void +radv_rt_notify(struct proto *p, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs UNUSED) +{ + struct proto_radv *ra = (struct proto_radv *) p; + struct radv_config *cf = (struct radv_config *) (p->cf); + + if (radv_net_match_trigger(cf, n)) + { + u8 old_active = ra->active; + ra->active = !!new; + + if (ra->active == old_active) + return; + + if (ra->active) + RADV_TRACE(D_EVENTS, "Triggered"); + else + RADV_TRACE(D_EVENTS, "Suppressed"); + + radv_iface_notify_all(ra, RA_EV_CHANGE); + } +} + +static int +radv_check_active(struct proto_radv *ra) +{ + struct radv_config *cf = (struct radv_config *) (ra->p.cf); + + if (! cf->trigger_valid) + return 1; + + return rt_examine(ra->p.table, cf->trigger_prefix, cf->trigger_pxlen, + &(ra->p), ra->p.cf->out_filter); +} + static struct proto * radv_init(struct proto_config *c) { struct proto *p = proto_new(c, sizeof(struct proto_radv)); + p->accept_ra_types = RA_OPTIMAL; + p->import_control = radv_import_control; + p->rt_notify = radv_rt_notify; p->if_notify = radv_if_notify; p->ifa_notify = radv_ifa_notify; return p; @@ -252,9 +326,10 @@ static int radv_start(struct proto *p) { struct proto_radv *ra = (struct proto_radv *) p; - // struct radv_config *cf = (struct radv_config *) (p->cf); + struct radv_config *cf = (struct radv_config *) (p->cf); init_list(&(ra->iface_list)); + ra->active = !cf->trigger_valid; return PS_UP; } @@ -293,6 +368,9 @@ radv_reconfigure(struct proto *p, struct proto_config *c) * causing nodes to temporary remove their default routes. */ + p->cf = c; /* radv_check_active() requires proper p->cf */ + ra->active = radv_check_active(ra); + struct iface *iface; WALK_LIST(iface, iface_list) { @@ -335,6 +413,14 @@ radv_copy_config(struct proto_config *dest, struct proto_config *src) cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct radv_prefix_config)); } +static void +radv_get_status(struct proto *p, byte *buf) +{ + struct proto_radv *ra = (struct proto_radv *) p; + + if (!ra->active) + strcpy(buf, "Suppressed"); +} struct protocol proto_radv = { .name = "RAdv", @@ -343,5 +429,6 @@ struct protocol proto_radv = { .start = radv_start, .shutdown = radv_shutdown, .reconfigure = radv_reconfigure, - .copy_config = radv_copy_config + .copy_config = radv_copy_config, + .get_status = radv_get_status }; diff --git a/proto/radv/radv.h b/proto/radv/radv.h index 48af8c00..f80e4530 100644 --- a/proto/radv/radv.h +++ b/proto/radv/radv.h @@ -52,6 +52,10 @@ struct radv_config list pref_list; /* Global list of prefix configs (struct radv_prefix_config) */ list rdnss_list; /* Global list of RDNSS configs (struct radv_rdnss_config) */ list dnssl_list; /* Global list of DNSSL configs (struct radv_dnssl_config) */ + + ip_addr trigger_prefix; /* Prefix of a trigger route, if defined */ + u8 trigger_pxlen; /* Pxlen of a trigger route, if defined */ + u8 trigger_valid; /* Whether a trigger route is defined */ }; struct radv_iface_config @@ -75,6 +79,7 @@ struct radv_iface_config u32 retrans_timer; u32 current_hop_limit; u32 default_lifetime; + u8 default_lifetime_sensitive; /* Whether default_lifetime depends on trigger */ }; struct radv_prefix_config @@ -88,6 +93,8 @@ struct radv_prefix_config u8 autonomous; u32 valid_lifetime; u32 preferred_lifetime; + u8 valid_lifetime_sensitive; /* Whether valid_lifetime depends on trigger */ + u8 preferred_lifetime_sensitive; /* Whether preferred_lifetime depends on trigger */ }; struct radv_rdnss_config @@ -113,6 +120,7 @@ struct proto_radv { struct proto p; list iface_list; /* List of active ifaces */ + u8 active; /* Whether radv is active w.r.t. triggers */ }; struct radv_iface -- cgit v1.2.3 From c6a2fe64bed8dc67af0e868052b055aa0f45cdf2 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 9 Feb 2013 00:53:04 +0100 Subject: Fixes handling of iface routes in static proto during reconfiguration. During reconfiguration, iface routes were installed even when iface was down. --- proto/static/static.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/static/static.c b/proto/static/static.c index 6a027f50..9eee820d 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -461,7 +461,7 @@ static_reconfigure(struct proto *p, struct proto_config *new) WALK_LIST(r, n->iface_routes) { struct iface *ifa; - if (ifa = if_find_by_name(r->if_name)) + if ((ifa = if_find_by_name(r->if_name)) && (ifa->flags & IF_UP)) static_install(p, r, ifa); } WALK_LIST(r, n->other_routes) -- cgit v1.2.3 From 155134f3960bc06a18c8c7d9a97181b786d77a3a Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Sun, 10 Feb 2013 19:04:08 +0100 Subject: A few semicolons added to decrease a number of warnings. --- proto/ospf/config.Y | 2 +- proto/radv/config.Y | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index a617292e..3f09afba 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -440,7 +440,7 @@ lsadb_args: $$ = cfg_allocz(sizeof(struct lsadb_show_data)); } | lsadb_args GLOBAL { $$ = $1; $$->scope = LSA_SCOPE_AS; } - | lsadb_args AREA idval { $$ = $1; $$->scope = LSA_SCOPE_AREA; $$->area = $3 } + | lsadb_args AREA idval { $$ = $1; $$->scope = LSA_SCOPE_AREA; $$->area = $3; } | lsadb_args LINK { $$ = $1; $$->scope = 1; /* hack, 0 is no filter */ } | lsadb_args TYPE NUM { $$ = $1; $$->type = $3; } | lsadb_args LSID idval { $$ = $1; $$->lsid = $3; } diff --git a/proto/radv/config.Y b/proto/radv/config.Y index fbec5a0a..c5b7aaee 100644 --- a/proto/radv/config.Y +++ b/proto/radv/config.Y @@ -290,8 +290,8 @@ radv_mult: ; radv_sensitive: - /* empty */ { $$ = -1 } - | SENSITIVE bool { $$ = $2 } + /* empty */ { $$ = -1; } + | SENSITIVE bool { $$ = $2; } ; CF_CODE -- cgit v1.2.3 From 0bc3542ab6e0a96342e35ead8ff1c52f980facc2 Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Sun, 10 Feb 2013 19:06:56 +0100 Subject: Route limits can be disabled - this makes sense for protocol templates --- doc/bird.sgml | 12 ++++++------ nest/config.Y | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 762834e3..1baa1528 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -478,7 +478,7 @@ to zero to disable it. An empty is equivalent to import limit + import limit [ Specify an import route limit (a maximum number of routes imported from the protocol) and optionally the action to be taken when the limit is hit. Warn action just prints warning @@ -486,9 +486,9 @@ to zero to disable it. An empty is equivalent to receive limit + receive limit [ Specify an receive route limit (a maximum number of routes received from the protocol and remembered). It works almost identically to import limit option, the only @@ -498,9 +498,9 @@ to zero to disable it. An empty is equivalent to export limit + export limit [ Specify an export route limit, works similarly to the import limit option, but for the routes exported to the protocol. This option is experimental, there are some @@ -509,7 +509,7 @@ to zero to disable it. An empty is equivalent to description " This is an optional description of the protocol. It is displayed as a part of the diff --git a/nest/config.Y b/nest/config.Y index e46b5fb5..75728e0d 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -216,6 +216,7 @@ limit_spec: l->action = $2; $$ = l; } + | OFF { $$ = 0; } ; rtable: -- cgit v1.2.3 From 8c4da7e01ded3f06cbf873e67c5ae1cf70cf280b Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Sun, 10 Feb 2013 19:17:38 +0100 Subject: Symbol names enclosed by apostrophes can contain DOTs. --- conf/cf-lex.l | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index c8eae0e8..e0430485 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -172,7 +172,7 @@ else: { return ELSECOL; } -({ALPHA}{ALNUM}*|[']({ALNUM}|[-])*[']) { +({ALPHA}{ALNUM}*|[']({ALNUM}|[-]|[\.])*[']) { if(*yytext == '\'') { yytext[yyleng-1] = 0; yytext++; -- cgit v1.2.3 From 4c2abee74e64f64fba61aad6e2b66e3895820003 Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Tue, 12 Feb 2013 13:15:01 +0100 Subject: Allow submitting BIRD commands from UNIX shell even in restricted mode. --- client/client.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/client/client.c b/client/client.c index 8711cf0a..d8f0060c 100644 --- a/client/client.c +++ b/client/client.c @@ -29,6 +29,7 @@ static char *opt_list = "s:vr"; static int verbose; static char *init_cmd; static int once; +static int restricted; static char *server_path = PATH_CONTROL_SOCKET; static int server_fd; @@ -70,7 +71,7 @@ parse_args(int argc, char **argv) verbose++; break; case 'r': - init_cmd = "restrict"; + restricted = 1; break; default: usage(); @@ -83,9 +84,6 @@ parse_args(int argc, char **argv) int i; int len = 0; - if (init_cmd) - usage(); - for (i = optind; i < argc; i++) len += strlen(argv[i]) + 1; @@ -303,6 +301,13 @@ update_state(void) if (nstate == cstate) return; + if (restricted) + { + submit_server_command("restrict"); + restricted = 0; + return; + } + if (init_cmd) { /* First transition - client received hello from BIRD -- cgit v1.2.3 From 9d969be5f2d867704e82bd7d6c8049623d50708f Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Thu, 14 Feb 2013 23:35:51 +0100 Subject: I still believe that 0 == NULL, however this patch will make Santiago happy. :-) --- nest/config.Y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nest/config.Y b/nest/config.Y index 75728e0d..07e1af31 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -216,7 +216,7 @@ limit_spec: l->action = $2; $$ = l; } - | OFF { $$ = 0; } + | OFF { $$ = NULL; } ; rtable: -- cgit v1.2.3 From 2bf59bf4d3e4fcaff489d3445134e5e2e2af9cf6 Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Thu, 21 Feb 2013 00:44:59 +0100 Subject: Hotfix to solve an issue with delaying timers reported by Aleksey Chudov. --- doc/bird.sgml | 4 ++-- proto/rip/rip.c | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 1baa1528..893d3bfa 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2734,7 +2734,7 @@ other than equally configured BIRD. I have warned you. period number specifies the number of seconds between periodic updates. Default is 30 seconds. A lower number will mean faster convergence but bigger network - load. Do not use values lower than 10. + load. Do not use values lower than 12. timeout time number specifies how old route has to be to be considered unreachable. Default is 4*interfaces ); P->timer = tm_new( p->pool ); P->timer->data = p; - P->timer->randomize = 5; - P->timer->recurrent = (P_CF->period / 6)+1; + P->timer->randomize = 2; + P->timer->recurrent = (P_CF->period / 6) - 1; + if (P_CF->period < 12) { + log(L_WARN "Period %d is too low. So I am using 12 which is the lowest possible value.", P_CF->period); + P->timer->recurrent = 1; + } P->timer->hook = rip_timer; tm_start( P->timer, 5 ); rif = new_iface(p, NULL, 0, NULL); /* Initialize dummy interface */ @@ -956,9 +960,11 @@ rip_rte_insert(net *net UNUSED, rte *rte) static void rip_rte_remove(net *net UNUSED, rte *rte) { - // struct proto *p = rte->attrs->proto; +#ifdef LOCAL_DEBUG + struct proto *p = rte->attrs->proto; CHK_MAGIC; DBG( "rip_rte_remove: %p\n", rte ); +#endif rem_node( &rte->u.rip.garbage ); } -- cgit v1.2.3 From 04ddefb357b2b8759be16633f7bb1df49b0405ea Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Fri, 22 Feb 2013 07:15:27 +0100 Subject: Use BIRD's ASSERT instead of assert.h --- proto/rip/rip.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 4b303305..35a151ad 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -59,7 +59,6 @@ #include "lib/string.h" #include "rip.h" -#include #define P ((struct rip_proto *) p) #define P_CF ((struct rip_proto_config *)p->cf) @@ -583,9 +582,9 @@ rip_start(struct proto *p) struct rip_interface *rif; DBG( "RIP: starting instance...\n" ); - assert( sizeof(struct rip_packet_heading) == 4); - assert( sizeof(struct rip_block) == 20); - assert( sizeof(struct rip_block_auth) == 20); + ASSERT(sizeof(struct rip_packet_heading) == 4); + ASSERT(sizeof(struct rip_block) == 20); + ASSERT(sizeof(struct rip_block_auth) == 20); #ifdef LOCAL_DEBUG P->magic = RIP_MAGIC; -- cgit v1.2.3 From a9c38203bdcad92f7ac0a8a912241d2acb483f2c Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Sun, 24 Feb 2013 00:43:08 +0100 Subject: Allow 1 sec RIP update. --- proto/rip/rip.c | 26 +++++++++++++------------- proto/rip/rip.h | 1 + 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 35a151ad..5007715d 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -7,14 +7,13 @@ * Can be freely distributed and used under the terms of the GNU GPL. * FIXME: IpV6 support: packet size - FIXME: (nonurgent) IpV6 support: receive "route using" blocks - FIXME: (nonurgent) IpV6 support: generate "nexthop" blocks + FIXME: (nonurgent) IPv6 support: receive "route using" blocks + FIXME: (nonurgent) IPv6 support: generate "nexthop" blocks next hops are only advisory, and they are pretty ugly in IpV6. I suggest just forgetting about them. FIXME: (nonurgent): fold rip_connection into rip_interface? - FIXME: (nonurgent) allow bigger frequencies than 1 regular update in 6 seconds (?) FIXME: propagation of metric=infinity into main routing table may or may not be good idea. */ @@ -162,7 +161,7 @@ rip_tx( sock *s ) FIB_ITERATE_START(&P->rtable, &c->iter, z) { struct rip_entry *e = (struct rip_entry *) z; - if (!rif->triggered || (!(e->updated < now-5))) { + if (!rif->triggered || (!(e->updated < now-2))) { /* FIXME: Should be probably 1 or some different algorithm */ nullupdate = 0; i = rip_tx_prepare( p, packet->block + i, e, rif, i ); if (i >= maxi) { @@ -557,17 +556,23 @@ rip_timer(timer *t) DBG( "RIP: Broadcasting routing tables\n" ); { struct rip_interface *rif; + + if ( P_CF->period > 2 ) { /* Bring some randomness into sending times */ + if (! (P->tx_count % P_CF->period)) P->rnd_count = random_u32() % 2; + } else P->rnd_count = P->tx_count % P_CF->period; + WALK_LIST( rif, P->interfaces ) { struct iface *iface = rif->iface; if (!iface) continue; if (rif->mode & IM_QUIET) continue; if (!(iface->flags & IF_UP)) continue; + rif->triggered = P->rnd_count; - rif->triggered = (P->tx_count % 6); rip_sendto( p, IPA_NONE, 0, rif ); } - P->tx_count ++; + P->tx_count++; + P->rnd_count--; } DBG( "RIP: tick tock done\n" ); @@ -595,14 +600,9 @@ rip_start(struct proto *p) init_list( &P->interfaces ); P->timer = tm_new( p->pool ); P->timer->data = p; - P->timer->randomize = 2; - P->timer->recurrent = (P_CF->period / 6) - 1; - if (P_CF->period < 12) { - log(L_WARN "Period %d is too low. So I am using 12 which is the lowest possible value.", P_CF->period); - P->timer->recurrent = 1; - } + P->timer->recurrent = 1; P->timer->hook = rip_timer; - tm_start( P->timer, 5 ); + tm_start( P->timer, 2 ); rif = new_iface(p, NULL, 0, NULL); /* Initialize dummy interface */ add_head( &P->interfaces, NODE rif ); CHK_MAGIC; diff --git a/proto/rip/rip.h b/proto/rip/rip.h index 896fab64..e0816d0e 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -162,6 +162,7 @@ struct rip_proto { int magic; #endif int tx_count; /* Do one regular update once in a while */ + int rnd_count; /* Randomize sending time */ }; #ifdef LOCAL_DEBUG -- cgit v1.2.3 From e667622a35722ec007137e678f4f70841562e57f Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Mon, 25 Feb 2013 10:39:46 +0100 Subject: Default rounting table for 'show route export/preexport/protocol' is the one related to a respective protocol. --- doc/bird.sgml | 3 ++- nest/config.Y | 1 - nest/rt-table.c | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 893d3bfa..e83cf0e1 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -670,7 +670,8 @@ This argument can be omitted if there exists only a single instance. Show the list of symbols defined in the configuration (names of protocols, routing tables etc.). show route [[for] - Show contents of a routing table (by default of the main one), + Show contents of a routing table (by default of the main one or + the table attached to a respective protocol), that is routes, their metrics and (in case the pxlen = 256; $$->filter = FILTER_ACCEPT; - $$->table = config->master_rtc->table; } | r_args prefix { $$ = $1; diff --git a/nest/rt-table.c b/nest/rt-table.c index 75bfa6ba..e3fd985c 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -2204,6 +2204,11 @@ rt_show(struct rt_show_data *d) { net *n; + /* Default is either a master table or a table related to a respective protocol */ + if ((!d->table) && d->export_protocol) d->table = d->export_protocol->table; + if ((!d->table) && d->show_protocol) d->table = d->show_protocol->table; + if (!d->table) d->table = config->master_rtc->table; + if (d->pxlen == 256) { FIB_ITERATE_INIT(&d->fit, &d->table->fib); -- cgit v1.2.3 From de41dcd13d6f9d4785c80e6234ac38f2a15f5429 Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Tue, 26 Feb 2013 14:13:11 +0100 Subject: Redundant lines removed. --- proto/rip/rip.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 5007715d..8d9271d4 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -531,13 +531,10 @@ rip_timer(timer *t) WALK_LIST_DELSAFE( e, et, P->garbage ) { rte *rte; rte = SKIP_BACK( struct rte, u.rip.garbage, e ); -#ifdef LOCAL_DEBUG - { - struct proto *p = rte->attrs->proto; - CHK_MAGIC; - } + + CHK_MAGIC; + DBG( "Garbage: (%p)", rte ); rte_dump( rte ); -#endif if (now - rte->lastmod > P_CF->timeout_time) { TRACE(D_EVENTS, "entry is too old: %I", rte->net->n.prefix ); -- cgit v1.2.3 From a9fc659b840e13323aa43e92eb8f39ceb19b5ed6 Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Tue, 26 Feb 2013 14:29:53 +0100 Subject: Small typos fixed. --- proto/rip/rip.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 8d9271d4..341df7eb 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -6,10 +6,10 @@ * * Can be freely distributed and used under the terms of the GNU GPL. * - FIXME: IpV6 support: packet size + FIXME: IPv6 support: packet size FIXME: (nonurgent) IPv6 support: receive "route using" blocks FIXME: (nonurgent) IPv6 support: generate "nexthop" blocks - next hops are only advisory, and they are pretty ugly in IpV6. + next hops are only advisory, and they are pretty ugly in IPv6. I suggest just forgetting about them. FIXME: (nonurgent): fold rip_connection into rip_interface? @@ -46,6 +46,7 @@ */ #undef LOCAL_DEBUG +#define LOCAL_DEBUG 1 #include "nest/bird.h" #include "nest/iface.h" @@ -357,26 +358,26 @@ advertise_entry( struct proto *p, struct rip_block *b, ip_addr whotoldme, struct static void process_block( struct proto *p, struct rip_block *block, ip_addr whotoldme, struct iface *iface ) { + int metric, pxlen; + #ifndef IPV6 - int metric = ntohl( block->metric ); + metric = ntohl( block->metric ); + pxlen = ipa_mklen(block->netmask); #else - int metric = block->metric; + metric = block->metric; + pxlen = block->pxlen; #endif ip_addr network = block->network; CHK_MAGIC; -#ifdef IPV6 - TRACE(D_ROUTES, "block: %I tells me: %I/%d available, metric %d... ", - whotoldme, network, block->pxlen, metric ); -#else + TRACE(D_ROUTES, "block: %I tells me: %I/%d available, metric %d... ", - whotoldme, network, ipa_mklen(block->netmask), metric ); -#endif + whotoldme, network, pxlen, metric ); if ((!metric) || (metric > P_CF->infinity)) { -#ifdef IPV6 /* Someone is sedning us nexthop and we are ignoring it */ +#ifdef IPV6 /* Someone is sending us nexthop and we are ignoring it */ if (metric == 0xff) - { DBG( "IpV6 nexthop ignored" ); return; } + { DBG( "IPv6 nexthop ignored" ); return; } #endif log( L_WARN "%s: Got metric %d from %I", p->name, metric, whotoldme ); return; -- cgit v1.2.3 From 5c2c4ea8b1e924fce433094e744c0467da55aaab Mon Sep 17 00:00:00 2001 From: Tomas Hlavacek Date: Wed, 23 Jan 2013 15:51:04 +0100 Subject: Rename client/client.c to client_full.c Rename client/client.c to client-full.c and change the Makefile accordingly. This is a preparation step for introducing a new lightweight client which should reuse as much code as possible from the old one but it should not depend on external libraries. Signed-off-by: Tomas Hlavacek --- client/Makefile | 2 +- client/client.c | 578 --------------------------------------------------- client/client_full.c | 578 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 579 insertions(+), 579 deletions(-) delete mode 100644 client/client.c create mode 100644 client/client_full.c diff --git a/client/Makefile b/client/Makefile index 867476cc..74b30b77 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,4 +1,4 @@ -source=client.c commands.c util.c +source=client_full.c commands.c util.c root-rel=../ dir-name=client diff --git a/client/client.c b/client/client.c deleted file mode 100644 index d8f0060c..00000000 --- a/client/client.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * BIRD Client - * - * (c) 1999--2004 Martin Mares - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nest/bird.h" -#include "lib/resource.h" -#include "lib/string.h" -#include "client/client.h" -#include "sysdep/unix/unix.h" - -static char *opt_list = "s:vr"; -static int verbose; -static char *init_cmd; -static int once; -static int restricted; - -static char *server_path = PATH_CONTROL_SOCKET; -static int server_fd; -static byte server_read_buf[4096]; -static byte *server_read_pos = server_read_buf; - -#define STATE_PROMPT 0 -#define STATE_CMD_SERVER 1 -#define STATE_CMD_USER 2 - -static int input_initialized; -static int input_hidden_end; -static int cstate = STATE_CMD_SERVER; -static int nstate = STATE_CMD_SERVER; - -static int num_lines, skip_input, interactive; - -/*** Parsing of arguments ***/ - -static void -usage(void) -{ - fprintf(stderr, "Usage: birdc [-s ] [-v] [-r]\n"); - exit(1); -} - -static void -parse_args(int argc, char **argv) -{ - int c; - - while ((c = getopt(argc, argv, opt_list)) >= 0) - switch (c) - { - case 's': - server_path = optarg; - break; - case 'v': - verbose++; - break; - case 'r': - restricted = 1; - break; - default: - usage(); - } - - /* If some arguments are not options, we take it as commands */ - if (optind < argc) - { - char *tmp; - int i; - int len = 0; - - for (i = optind; i < argc; i++) - len += strlen(argv[i]) + 1; - - tmp = init_cmd = malloc(len); - for (i = optind; i < argc; i++) - { - strcpy(tmp, argv[i]); - tmp += strlen(tmp); - *tmp++ = ' '; - } - tmp[-1] = 0; - - once = 1; - } -} - -/*** Input ***/ - -static void server_send(char *); - -/* HACK: libreadline internals we need to access */ -extern int _rl_vis_botlin; -extern void _rl_move_vert(int); -extern Function *rl_last_func; - -static int -handle_internal_command(char *cmd) -{ - if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4)) - { - cleanup(); - exit(0); - } - if (!strncmp(cmd, "help", 4)) - { - puts("Press `?' for context sensitive help."); - return 1; - } - return 0; -} - -void -submit_server_command(char *cmd) -{ - server_send(cmd); - nstate = STATE_CMD_SERVER; - num_lines = 2; -} - -static void -add_history_dedup(char *cmd) -{ - /* Add history line if it differs from the last one */ - HIST_ENTRY *he = history_get(history_length); - if (!he || strcmp(he->line, cmd)) - add_history(cmd); -} - -static void -got_line(char *cmd_buffer) -{ - char *cmd; - - if (!cmd_buffer) - { - cleanup(); - exit(0); - } - if (cmd_buffer[0]) - { - cmd = cmd_expand(cmd_buffer); - if (cmd) - { - add_history_dedup(cmd); - - if (!handle_internal_command(cmd)) - submit_server_command(cmd); - - free(cmd); - } - else - add_history_dedup(cmd_buffer); - } - free(cmd_buffer); -} - -void -input_start_list(void) /* Leave the currently edited line and make space for listing */ -{ - _rl_move_vert(_rl_vis_botlin); -#ifdef HAVE_RL_CRLF - rl_crlf(); -#endif -} - -void -input_stop_list(void) /* Reprint the currently edited line after listing */ -{ - rl_on_new_line(); - rl_redisplay(); -} - -static int -input_complete(int arg UNUSED, int key UNUSED) -{ - static int complete_flag; - char buf[256]; - - if (rl_last_func != input_complete) - complete_flag = 0; - switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) - { - case 0: - complete_flag = 1; - break; - case 1: - rl_insert_text(buf); - break; - default: - complete_flag = 1; -#ifdef HAVE_RL_DING - rl_ding(); -#endif - } - return 0; -} - -static int -input_help(int arg, int key UNUSED) -{ - int i, in_string, in_bracket; - - if (arg != 1) - return rl_insert(arg, '?'); - - in_string = in_bracket = 0; - for (i = 0; i < rl_point; i++) - { - - if (rl_line_buffer[i] == '"') - in_string = ! in_string; - else if (! in_string) - { - if (rl_line_buffer[i] == '[') - in_bracket++; - else if (rl_line_buffer[i] == ']') - in_bracket--; - } - } - - /* `?' inside string or path -> insert */ - if (in_string || in_bracket) - return rl_insert(1, '?'); - - rl_begin_undo_group(); /* HACK: We want to display `?' at point position */ - rl_insert_text("?"); - rl_redisplay(); - rl_end_undo_group(); - input_start_list(); - cmd_help(rl_line_buffer, rl_point); - rl_undo_command(1, 0); - input_stop_list(); - return 0; -} - -static void -input_init(void) -{ - rl_readline_name = "birdc"; - rl_add_defun("bird-complete", input_complete, '\t'); - rl_add_defun("bird-help", input_help, '?'); - rl_callback_handler_install("bird> ", got_line); - input_initialized = 1; -// readline library does strange things when stdin is nonblocking. -// if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) -// die("fcntl: %m"); -} - -static void -input_hide(void) -{ - input_hidden_end = rl_end; - rl_end = 0; - rl_expand_prompt(""); - rl_redisplay(); -} - -static void -input_reveal(void) -{ - /* need this, otherwise some lib seems to eat pending output when - the prompt is displayed */ - fflush(stdout); - tcdrain(fileno(stdout)); - - rl_end = input_hidden_end; - rl_expand_prompt("bird> "); - rl_forced_update_display(); -} - -void -cleanup(void) -{ - if (input_initialized) - { - input_initialized = 0; - input_hide(); - rl_callback_handler_remove(); - } -} - -void -update_state(void) -{ - if (nstate == cstate) - return; - - if (restricted) - { - submit_server_command("restrict"); - restricted = 0; - return; - } - - if (init_cmd) - { - /* First transition - client received hello from BIRD - and there is waiting initial command */ - submit_server_command(init_cmd); - init_cmd = NULL; - return; - } - - if (!init_cmd && once) - { - /* Initial command is finished and we want to exit */ - cleanup(); - exit(0); - } - - if (nstate == STATE_PROMPT) - { - if (input_initialized) - input_reveal(); - else - input_init(); - } - - if (nstate != STATE_PROMPT) - input_hide(); - - cstate = nstate; -} - -void -more(void) -{ - printf("--More--\015"); - fflush(stdout); - - redo: - switch (getchar()) - { - case 32: - num_lines = 2; - break; - case 13: - num_lines--; - break; - case 'q': - skip_input = 1; - break; - default: - goto redo; - } - - printf(" \015"); - fflush(stdout); -} - - -/*** Communication with server ***/ - -static void -server_connect(void) -{ - struct sockaddr_un sa; - - server_fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (server_fd < 0) - die("Cannot create socket: %m"); - - if (strlen(server_path) >= sizeof(sa.sun_path)) - die("server_connect: path too long"); - - bzero(&sa, sizeof(sa)); - sa.sun_family = AF_UNIX; - strcpy(sa.sun_path, server_path); - if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) - die("Unable to connect to server control socket (%s): %m", server_path); - if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) - die("fcntl: %m"); -} - -#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) - -static void -server_got_reply(char *x) -{ - int code; - int len = 0; - - if (*x == '+') /* Async reply */ - PRINTF(len, ">>> %s\n", x+1); - else if (x[0] == ' ') /* Continuation */ - PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); - else if (strlen(x) > 4 && - sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && - (x[4] == ' ' || x[4] == '-')) - { - if (code) - PRINTF(len, "%s\n", verbose ? x : x+5); - if (x[4] == ' ') - { - nstate = STATE_PROMPT; - skip_input = 0; - return; - } - } - else - PRINTF(len, "??? <%s>\n", x); - - if (skip_input) - return; - - if (interactive && input_initialized && (len > 0)) - { - int lns = LINES ? LINES : 25; - int cls = COLS ? COLS : 80; - num_lines += (len + cls - 1) / cls; /* Divide and round up */ - if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) - more(); - } -} - -static void -server_read(void) -{ - int c; - byte *start, *p; - - redo: - c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); - if (!c) - die("Connection closed by server."); - if (c < 0) - { - if (errno == EINTR) - goto redo; - else - die("Server read error: %m"); - } - - start = server_read_buf; - p = server_read_pos; - server_read_pos += c; - while (p < server_read_pos) - if (*p++ == '\n') - { - p[-1] = 0; - server_got_reply(start); - start = p; - } - if (start != server_read_buf) - { - int l = server_read_pos - start; - memmove(server_read_buf, start, l); - server_read_pos = server_read_buf + l; - } - else if (server_read_pos == server_read_buf + sizeof(server_read_buf)) - { - strcpy(server_read_buf, "?"); - server_read_pos = server_read_buf + 11; - } -} - -static fd_set select_fds; - -static void -select_loop(void) -{ - int rv; - while (1) - { - FD_ZERO(&select_fds); - - if (cstate != STATE_CMD_USER) - FD_SET(server_fd, &select_fds); - if (cstate != STATE_CMD_SERVER) - FD_SET(0, &select_fds); - - rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); - if (rv < 0) - { - if (errno == EINTR) - continue; - else - die("select: %m"); - } - - if (FD_ISSET(server_fd, &select_fds)) - { - server_read(); - update_state(); - } - - if (FD_ISSET(0, &select_fds)) - { - rl_callback_read_char(); - update_state(); - } - } -} - -static void -wait_for_write(int fd) -{ - while (1) - { - int rv; - fd_set set; - FD_ZERO(&set); - FD_SET(fd, &set); - - rv = select(fd+1, NULL, &set, NULL, NULL); - if (rv < 0) - { - if (errno == EINTR) - continue; - else - die("select: %m"); - } - - if (FD_ISSET(server_fd, &set)) - return; - } -} - -static void -server_send(char *cmd) -{ - int l = strlen(cmd); - byte *z = alloca(l + 1); - - memcpy(z, cmd, l); - z[l++] = '\n'; - while (l) - { - int cnt = write(server_fd, z, l); - - if (cnt < 0) - { - if (errno == EAGAIN) - wait_for_write(server_fd); - else if (errno == EINTR) - continue; - else - die("Server write error: %m"); - } - else - { - l -= cnt; - z += cnt; - } - } -} - -int -main(int argc, char **argv) -{ -#ifdef HAVE_LIBDMALLOC - if (!getenv("DMALLOC_OPTIONS")) - dmalloc_debug(0x2f03d00); -#endif - - interactive = isatty(0); - parse_args(argc, argv); - cmd_build_tree(); - server_connect(); - select_loop(); - return 0; -} diff --git a/client/client_full.c b/client/client_full.c new file mode 100644 index 00000000..d8f0060c --- /dev/null +++ b/client/client_full.c @@ -0,0 +1,578 @@ +/* + * BIRD Client + * + * (c) 1999--2004 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +static char *opt_list = "s:vr"; +static int verbose; +static char *init_cmd; +static int once; +static int restricted; + +static char *server_path = PATH_CONTROL_SOCKET; +static int server_fd; +static byte server_read_buf[4096]; +static byte *server_read_pos = server_read_buf; + +#define STATE_PROMPT 0 +#define STATE_CMD_SERVER 1 +#define STATE_CMD_USER 2 + +static int input_initialized; +static int input_hidden_end; +static int cstate = STATE_CMD_SERVER; +static int nstate = STATE_CMD_SERVER; + +static int num_lines, skip_input, interactive; + +/*** Parsing of arguments ***/ + +static void +usage(void) +{ + fprintf(stderr, "Usage: birdc [-s ] [-v] [-r]\n"); + exit(1); +} + +static void +parse_args(int argc, char **argv) +{ + int c; + + while ((c = getopt(argc, argv, opt_list)) >= 0) + switch (c) + { + case 's': + server_path = optarg; + break; + case 'v': + verbose++; + break; + case 'r': + restricted = 1; + break; + default: + usage(); + } + + /* If some arguments are not options, we take it as commands */ + if (optind < argc) + { + char *tmp; + int i; + int len = 0; + + for (i = optind; i < argc; i++) + len += strlen(argv[i]) + 1; + + tmp = init_cmd = malloc(len); + for (i = optind; i < argc; i++) + { + strcpy(tmp, argv[i]); + tmp += strlen(tmp); + *tmp++ = ' '; + } + tmp[-1] = 0; + + once = 1; + } +} + +/*** Input ***/ + +static void server_send(char *); + +/* HACK: libreadline internals we need to access */ +extern int _rl_vis_botlin; +extern void _rl_move_vert(int); +extern Function *rl_last_func; + +static int +handle_internal_command(char *cmd) +{ + if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4)) + { + cleanup(); + exit(0); + } + if (!strncmp(cmd, "help", 4)) + { + puts("Press `?' for context sensitive help."); + return 1; + } + return 0; +} + +void +submit_server_command(char *cmd) +{ + server_send(cmd); + nstate = STATE_CMD_SERVER; + num_lines = 2; +} + +static void +add_history_dedup(char *cmd) +{ + /* Add history line if it differs from the last one */ + HIST_ENTRY *he = history_get(history_length); + if (!he || strcmp(he->line, cmd)) + add_history(cmd); +} + +static void +got_line(char *cmd_buffer) +{ + char *cmd; + + if (!cmd_buffer) + { + cleanup(); + exit(0); + } + if (cmd_buffer[0]) + { + cmd = cmd_expand(cmd_buffer); + if (cmd) + { + add_history_dedup(cmd); + + if (!handle_internal_command(cmd)) + submit_server_command(cmd); + + free(cmd); + } + else + add_history_dedup(cmd_buffer); + } + free(cmd_buffer); +} + +void +input_start_list(void) /* Leave the currently edited line and make space for listing */ +{ + _rl_move_vert(_rl_vis_botlin); +#ifdef HAVE_RL_CRLF + rl_crlf(); +#endif +} + +void +input_stop_list(void) /* Reprint the currently edited line after listing */ +{ + rl_on_new_line(); + rl_redisplay(); +} + +static int +input_complete(int arg UNUSED, int key UNUSED) +{ + static int complete_flag; + char buf[256]; + + if (rl_last_func != input_complete) + complete_flag = 0; + switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) + { + case 0: + complete_flag = 1; + break; + case 1: + rl_insert_text(buf); + break; + default: + complete_flag = 1; +#ifdef HAVE_RL_DING + rl_ding(); +#endif + } + return 0; +} + +static int +input_help(int arg, int key UNUSED) +{ + int i, in_string, in_bracket; + + if (arg != 1) + return rl_insert(arg, '?'); + + in_string = in_bracket = 0; + for (i = 0; i < rl_point; i++) + { + + if (rl_line_buffer[i] == '"') + in_string = ! in_string; + else if (! in_string) + { + if (rl_line_buffer[i] == '[') + in_bracket++; + else if (rl_line_buffer[i] == ']') + in_bracket--; + } + } + + /* `?' inside string or path -> insert */ + if (in_string || in_bracket) + return rl_insert(1, '?'); + + rl_begin_undo_group(); /* HACK: We want to display `?' at point position */ + rl_insert_text("?"); + rl_redisplay(); + rl_end_undo_group(); + input_start_list(); + cmd_help(rl_line_buffer, rl_point); + rl_undo_command(1, 0); + input_stop_list(); + return 0; +} + +static void +input_init(void) +{ + rl_readline_name = "birdc"; + rl_add_defun("bird-complete", input_complete, '\t'); + rl_add_defun("bird-help", input_help, '?'); + rl_callback_handler_install("bird> ", got_line); + input_initialized = 1; +// readline library does strange things when stdin is nonblocking. +// if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) +// die("fcntl: %m"); +} + +static void +input_hide(void) +{ + input_hidden_end = rl_end; + rl_end = 0; + rl_expand_prompt(""); + rl_redisplay(); +} + +static void +input_reveal(void) +{ + /* need this, otherwise some lib seems to eat pending output when + the prompt is displayed */ + fflush(stdout); + tcdrain(fileno(stdout)); + + rl_end = input_hidden_end; + rl_expand_prompt("bird> "); + rl_forced_update_display(); +} + +void +cleanup(void) +{ + if (input_initialized) + { + input_initialized = 0; + input_hide(); + rl_callback_handler_remove(); + } +} + +void +update_state(void) +{ + if (nstate == cstate) + return; + + if (restricted) + { + submit_server_command("restrict"); + restricted = 0; + return; + } + + if (init_cmd) + { + /* First transition - client received hello from BIRD + and there is waiting initial command */ + submit_server_command(init_cmd); + init_cmd = NULL; + return; + } + + if (!init_cmd && once) + { + /* Initial command is finished and we want to exit */ + cleanup(); + exit(0); + } + + if (nstate == STATE_PROMPT) + { + if (input_initialized) + input_reveal(); + else + input_init(); + } + + if (nstate != STATE_PROMPT) + input_hide(); + + cstate = nstate; +} + +void +more(void) +{ + printf("--More--\015"); + fflush(stdout); + + redo: + switch (getchar()) + { + case 32: + num_lines = 2; + break; + case 13: + num_lines--; + break; + case 'q': + skip_input = 1; + break; + default: + goto redo; + } + + printf(" \015"); + fflush(stdout); +} + + +/*** Communication with server ***/ + +static void +server_connect(void) +{ + struct sockaddr_un sa; + + server_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (server_fd < 0) + die("Cannot create socket: %m"); + + if (strlen(server_path) >= sizeof(sa.sun_path)) + die("server_connect: path too long"); + + bzero(&sa, sizeof(sa)); + sa.sun_family = AF_UNIX; + strcpy(sa.sun_path, server_path); + if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) + die("Unable to connect to server control socket (%s): %m", server_path); + if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) + die("fcntl: %m"); +} + +#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) + +static void +server_got_reply(char *x) +{ + int code; + int len = 0; + + if (*x == '+') /* Async reply */ + PRINTF(len, ">>> %s\n", x+1); + else if (x[0] == ' ') /* Continuation */ + PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); + else if (strlen(x) > 4 && + sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && + (x[4] == ' ' || x[4] == '-')) + { + if (code) + PRINTF(len, "%s\n", verbose ? x : x+5); + if (x[4] == ' ') + { + nstate = STATE_PROMPT; + skip_input = 0; + return; + } + } + else + PRINTF(len, "??? <%s>\n", x); + + if (skip_input) + return; + + if (interactive && input_initialized && (len > 0)) + { + int lns = LINES ? LINES : 25; + int cls = COLS ? COLS : 80; + num_lines += (len + cls - 1) / cls; /* Divide and round up */ + if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) + more(); + } +} + +static void +server_read(void) +{ + int c; + byte *start, *p; + + redo: + c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); + if (!c) + die("Connection closed by server."); + if (c < 0) + { + if (errno == EINTR) + goto redo; + else + die("Server read error: %m"); + } + + start = server_read_buf; + p = server_read_pos; + server_read_pos += c; + while (p < server_read_pos) + if (*p++ == '\n') + { + p[-1] = 0; + server_got_reply(start); + start = p; + } + if (start != server_read_buf) + { + int l = server_read_pos - start; + memmove(server_read_buf, start, l); + server_read_pos = server_read_buf + l; + } + else if (server_read_pos == server_read_buf + sizeof(server_read_buf)) + { + strcpy(server_read_buf, "?"); + server_read_pos = server_read_buf + 11; + } +} + +static fd_set select_fds; + +static void +select_loop(void) +{ + int rv; + while (1) + { + FD_ZERO(&select_fds); + + if (cstate != STATE_CMD_USER) + FD_SET(server_fd, &select_fds); + if (cstate != STATE_CMD_SERVER) + FD_SET(0, &select_fds); + + rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); + if (rv < 0) + { + if (errno == EINTR) + continue; + else + die("select: %m"); + } + + if (FD_ISSET(server_fd, &select_fds)) + { + server_read(); + update_state(); + } + + if (FD_ISSET(0, &select_fds)) + { + rl_callback_read_char(); + update_state(); + } + } +} + +static void +wait_for_write(int fd) +{ + while (1) + { + int rv; + fd_set set; + FD_ZERO(&set); + FD_SET(fd, &set); + + rv = select(fd+1, NULL, &set, NULL, NULL); + if (rv < 0) + { + if (errno == EINTR) + continue; + else + die("select: %m"); + } + + if (FD_ISSET(server_fd, &set)) + return; + } +} + +static void +server_send(char *cmd) +{ + int l = strlen(cmd); + byte *z = alloca(l + 1); + + memcpy(z, cmd, l); + z[l++] = '\n'; + while (l) + { + int cnt = write(server_fd, z, l); + + if (cnt < 0) + { + if (errno == EAGAIN) + wait_for_write(server_fd); + else if (errno == EINTR) + continue; + else + die("Server write error: %m"); + } + else + { + l -= cnt; + z += cnt; + } + } +} + +int +main(int argc, char **argv) +{ +#ifdef HAVE_LIBDMALLOC + if (!getenv("DMALLOC_OPTIONS")) + dmalloc_debug(0x2f03d00); +#endif + + interactive = isatty(0); + parse_args(argc, argv); + cmd_build_tree(); + server_connect(); + select_loop(); + return 0; +} -- cgit v1.2.3 From e454916149d4efe66732fdd0388181813cab6ed0 Mon Sep 17 00:00:00 2001 From: Tomas Hlavacek Date: Wed, 23 Jan 2013 17:14:53 +0100 Subject: Pull out independent routines from client_full.c Pull out routines for interacting with the server and interpreting internal commands which are not dependent on libreadline and ncurses libraries. This is a preparation step for a new lightweight birdc client. --- client/Makefile | 2 +- client/client.h | 14 +++ client/client_common.c | 179 ++++++++++++++++++++++++++++++++++ client/client_full.c | 256 +++++++++++-------------------------------------- 4 files changed, 248 insertions(+), 203 deletions(-) create mode 100644 client/client_common.c diff --git a/client/Makefile b/client/Makefile index 74b30b77..fdefe6ab 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,4 +1,4 @@ -source=client_full.c commands.c util.c +source=client_full.c commands.c util.c client_common.c root-rel=../ dir-name=client diff --git a/client/client.h b/client/client.h index 64de97ec..373b1c6f 100644 --- a/client/client.h +++ b/client/client.h @@ -18,3 +18,17 @@ void cmd_build_tree(void); void cmd_help(char *cmd, int len); int cmd_complete(char *cmd, int len, char *buf, int again); char *cmd_expand(char *cmd); + +/* client_common.c */ + +#define STATE_PROMPT 0 +#define STATE_CMD_SERVER 1 +#define STATE_CMD_USER 2 + +#define SERVER_READ_BUF_LEN 4096 + +int handle_internal_command(char *cmd); +void submit_server_command(char *cmd); +void server_connect(void); +void server_read(void); +void server_send(char *cmd); diff --git a/client/client_common.c b/client/client_common.c new file mode 100644 index 00000000..5f0a36dd --- /dev/null +++ b/client/client_common.c @@ -0,0 +1,179 @@ +/* + * BIRD Client + * + * (c) 1999--2004 Martin Mares + * (c) 2013 Tomas Hlavacek + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +char *server_path = PATH_CONTROL_SOCKET; +int server_fd; +byte server_read_buf[SERVER_READ_BUF_LEN]; +byte *server_read_pos = server_read_buf; + +int input_initialized; +int input_hidden_end; +int cstate = STATE_CMD_SERVER; +int nstate = STATE_CMD_SERVER; + +int num_lines, skip_input, interactive; + + +/*** Input ***/ + +int +handle_internal_command(char *cmd) +{ + if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4)) + { + cleanup(); + exit(0); + } + if (!strncmp(cmd, "help", 4)) + { + puts("Press `?' for context sensitive help."); + return 1; + } + return 0; +} + +void +submit_server_command(char *cmd) +{ + server_send(cmd); + nstate = STATE_CMD_SERVER; + num_lines = 2; +} + +/*** Communication with server ***/ + +void +server_connect(void) +{ + struct sockaddr_un sa; + + server_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (server_fd < 0) + die("Cannot create socket: %m"); + + if (strlen(server_path) >= sizeof(sa.sun_path)) + die("server_connect: path too long"); + + bzero(&sa, sizeof(sa)); + sa.sun_family = AF_UNIX; + strcpy(sa.sun_path, server_path); + if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) + die("Unable to connect to server control socket (%s): %m", server_path); + if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) + die("fcntl: %m"); +} + +void +server_read(void) +{ + int c; + byte *start, *p; + + redo: + c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); + if (!c) + die("Connection closed by server."); + if (c < 0) + { + if (errno == EINTR) + goto redo; + else + die("Server read error: %m"); + } + + start = server_read_buf; + p = server_read_pos; + server_read_pos += c; + while (p < server_read_pos) + if (*p++ == '\n') + { + p[-1] = 0; + server_got_reply(start); + start = p; + } + if (start != server_read_buf) + { + int l = server_read_pos - start; + memmove(server_read_buf, start, l); + server_read_pos = server_read_buf + l; + } + else if (server_read_pos == server_read_buf + sizeof(server_read_buf)) + { + strcpy(server_read_buf, "?"); + server_read_pos = server_read_buf + 11; + } +} + +void +wait_for_write(int fd) +{ + while (1) + { + int rv; + fd_set set; + FD_ZERO(&set); + FD_SET(fd, &set); + + rv = select(fd+1, NULL, &set, NULL, NULL); + if (rv < 0) + { + if (errno == EINTR) + continue; + else + die("select: %m"); + } + + if (FD_ISSET(server_fd, &set)) + return; + } +} + +void +server_send(char *cmd) +{ + int l = strlen(cmd); + byte *z = alloca(l + 1); + + memcpy(z, cmd, l); + z[l++] = '\n'; + while (l) + { + int cnt = write(server_fd, z, l); + + if (cnt < 0) + { + if (errno == EAGAIN) + wait_for_write(server_fd); + else if (errno == EINTR) + continue; + else + die("Server write error: %m"); + } + else + { + l -= cnt; + z += cnt; + } + } +} diff --git a/client/client_full.c b/client/client_full.c index d8f0060c..9b4dd2fb 100644 --- a/client/client_full.c +++ b/client/client_full.c @@ -31,21 +31,17 @@ static char *init_cmd; static int once; static int restricted; -static char *server_path = PATH_CONTROL_SOCKET; -static int server_fd; -static byte server_read_buf[4096]; -static byte *server_read_pos = server_read_buf; +extern char *server_path; +extern int server_fd; +extern byte server_read_buf[SERVER_READ_BUF_LEN]; +extern byte *server_read_pos; -#define STATE_PROMPT 0 -#define STATE_CMD_SERVER 1 -#define STATE_CMD_USER 2 +extern int input_initialized; +extern int input_hidden_end; +extern int cstate; +extern int nstate; -static int input_initialized; -static int input_hidden_end; -static int cstate = STATE_CMD_SERVER; -static int nstate = STATE_CMD_SERVER; - -static int num_lines, skip_input, interactive; +extern int num_lines, skip_input, interactive; /*** Parsing of arguments ***/ @@ -102,37 +98,11 @@ parse_args(int argc, char **argv) /*** Input ***/ -static void server_send(char *); - /* HACK: libreadline internals we need to access */ extern int _rl_vis_botlin; extern void _rl_move_vert(int); extern Function *rl_last_func; -static int -handle_internal_command(char *cmd) -{ - if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4)) - { - cleanup(); - exit(0); - } - if (!strncmp(cmd, "help", 4)) - { - puts("Press `?' for context sensitive help."); - return 1; - } - return 0; -} - -void -submit_server_command(char *cmd) -{ - server_send(cmd); - nstate = STATE_CMD_SERVER; - num_lines = 2; -} - static void add_history_dedup(char *cmd) { @@ -142,8 +112,7 @@ add_history_dedup(char *cmd) add_history(cmd); } -static void -got_line(char *cmd_buffer) +void got_line(char *cmd_buffer) { char *cmd; @@ -156,16 +125,16 @@ got_line(char *cmd_buffer) { cmd = cmd_expand(cmd_buffer); if (cmd) - { - add_history_dedup(cmd); + { + add_history_dedup(cmd); - if (!handle_internal_command(cmd)) - submit_server_command(cmd); + if (!handle_internal_command(cmd)) + submit_server_command(cmd); - free(cmd); - } + free(cmd); + } else - add_history_dedup(cmd_buffer); + add_history_dedup(cmd_buffer); } free(cmd_buffer); } @@ -284,17 +253,6 @@ input_reveal(void) rl_forced_update_display(); } -void -cleanup(void) -{ - if (input_initialized) - { - input_initialized = 0; - input_hide(); - rl_callback_handler_remove(); - } -} - void update_state(void) { @@ -364,111 +322,18 @@ more(void) fflush(stdout); } - -/*** Communication with server ***/ - -static void -server_connect(void) -{ - struct sockaddr_un sa; - - server_fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (server_fd < 0) - die("Cannot create socket: %m"); - - if (strlen(server_path) >= sizeof(sa.sun_path)) - die("server_connect: path too long"); - - bzero(&sa, sizeof(sa)); - sa.sun_family = AF_UNIX; - strcpy(sa.sun_path, server_path); - if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) - die("Unable to connect to server control socket (%s): %m", server_path); - if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) - die("fcntl: %m"); -} - -#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) - -static void -server_got_reply(char *x) +void cleanup(void) { - int code; - int len = 0; - - if (*x == '+') /* Async reply */ - PRINTF(len, ">>> %s\n", x+1); - else if (x[0] == ' ') /* Continuation */ - PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); - else if (strlen(x) > 4 && - sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && - (x[4] == ' ' || x[4] == '-')) - { - if (code) - PRINTF(len, "%s\n", verbose ? x : x+5); - if (x[4] == ' ') - { - nstate = STATE_PROMPT; - skip_input = 0; - return; - } - } - else - PRINTF(len, "??? <%s>\n", x); - - if (skip_input) - return; - - if (interactive && input_initialized && (len > 0)) + if (input_initialized) { - int lns = LINES ? LINES : 25; - int cls = COLS ? COLS : 80; - num_lines += (len + cls - 1) / cls; /* Divide and round up */ - if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) - more(); + input_initialized = 0; + input_hide(); + rl_callback_handler_remove(); } } -static void -server_read(void) -{ - int c; - byte *start, *p; - - redo: - c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); - if (!c) - die("Connection closed by server."); - if (c < 0) - { - if (errno == EINTR) - goto redo; - else - die("Server read error: %m"); - } - start = server_read_buf; - p = server_read_pos; - server_read_pos += c; - while (p < server_read_pos) - if (*p++ == '\n') - { - p[-1] = 0; - server_got_reply(start); - start = p; - } - if (start != server_read_buf) - { - int l = server_read_pos - start; - memmove(server_read_buf, start, l); - server_read_pos = server_read_buf + l; - } - else if (server_read_pos == server_read_buf + sizeof(server_read_buf)) - { - strcpy(server_read_buf, "?"); - server_read_pos = server_read_buf + 11; - } -} +/*** Communication with server ***/ static fd_set select_fds; @@ -508,56 +373,43 @@ select_loop(void) } } -static void -wait_for_write(int fd) -{ - while (1) - { - int rv; - fd_set set; - FD_ZERO(&set); - FD_SET(fd, &set); +#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) - rv = select(fd+1, NULL, &set, NULL, NULL); - if (rv < 0) - { - if (errno == EINTR) - continue; - else - die("select: %m"); - } +void server_got_reply(char *x) +{ + int code; + int len = 0; - if (FD_ISSET(server_fd, &set)) - return; + if (*x == '+') /* Async reply */ + PRINTF(len, ">>> %s\n", x+1); + else if (x[0] == ' ') /* Continuation */ + PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); + else if (strlen(x) > 4 && + sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && + (x[4] == ' ' || x[4] == '-')) + { + if (code) + PRINTF(len, "%s\n", verbose ? x : x+5); + if (x[4] == ' ') + { + nstate = STATE_PROMPT; + skip_input = 0; + return; + } } -} + else + PRINTF(len, "??? <%s>\n", x); -static void -server_send(char *cmd) -{ - int l = strlen(cmd); - byte *z = alloca(l + 1); + if (skip_input) + return; - memcpy(z, cmd, l); - z[l++] = '\n'; - while (l) + if (interactive && input_initialized && (len > 0)) { - int cnt = write(server_fd, z, l); - - if (cnt < 0) - { - if (errno == EAGAIN) - wait_for_write(server_fd); - else if (errno == EINTR) - continue; - else - die("Server write error: %m"); - } - else - { - l -= cnt; - z += cnt; - } + int lns = LINES ? LINES : 25; + int cls = COLS ? COLS : 80; + num_lines += (len + cls - 1) / cls; /* Divide and round up */ + if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) + more(); } } -- cgit v1.2.3 From 8322ecde124188a9408b54afead4666bb954e5a5 Mon Sep 17 00:00:00 2001 From: Tomas Hlavacek Date: Sun, 24 Feb 2013 23:47:22 +0100 Subject: Add lightweight client - birdcl Restructure client/ subdir. Add two different flavors of client. The full featured birdc client code is in client/birdc/. The new light client birtcl is in client/birdcl/. Common sources of both clients are directly in client/. Rework on-line auto-completion in client/command.c to conditionally turn off ncurses-specific code. Add lightweight client without libreadline and ncurses dependencies - birdcl. The birdcl lacks support of history, on-line auto-completion and there are different implementations of "more" functionality and help on '?' press. New client operates in canonical terminal mode (apart from "more" display) and therefore all commands have to be executed by a return key including help commands (called by '?' character in the end of the line). Apart from these limitations the interaction style should be the same as for the full client - birdc. Build of birdcl is always on (independent on --enable-client parameter). --- client/Makefile | 2 +- client/birdc/Makefile | 5 + client/birdc/client.c | 430 +++++++++++++++++++++++++++++++++++++++++++++++++ client/birdcl/Makefile | 5 + client/birdcl/client.c | 416 +++++++++++++++++++++++++++++++++++++++++++++++ client/client.h | 3 +- client/client_full.c | 430 ------------------------------------------------- configure.in | 2 +- tools/Makefile.in | 27 +++- tools/Rules.in | 8 +- 10 files changed, 884 insertions(+), 444 deletions(-) create mode 100644 client/birdc/Makefile create mode 100644 client/birdc/client.c create mode 100644 client/birdcl/Makefile create mode 100644 client/birdcl/client.c delete mode 100644 client/client_full.c diff --git a/client/Makefile b/client/Makefile index fdefe6ab..3568833e 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,4 +1,4 @@ -source=client_full.c commands.c util.c client_common.c +source=commands.c util.c client_common.c root-rel=../ dir-name=client diff --git a/client/birdc/Makefile b/client/birdc/Makefile new file mode 100644 index 00000000..154a8d20 --- /dev/null +++ b/client/birdc/Makefile @@ -0,0 +1,5 @@ +source=client.c +root-rel=../../ +dir-name=client/birdc + +include ../../Rules diff --git a/client/birdc/client.c b/client/birdc/client.c new file mode 100644 index 00000000..9b4dd2fb --- /dev/null +++ b/client/birdc/client.c @@ -0,0 +1,430 @@ +/* + * BIRD Client + * + * (c) 1999--2004 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +static char *opt_list = "s:vr"; +static int verbose; +static char *init_cmd; +static int once; +static int restricted; + +extern char *server_path; +extern int server_fd; +extern byte server_read_buf[SERVER_READ_BUF_LEN]; +extern byte *server_read_pos; + +extern int input_initialized; +extern int input_hidden_end; +extern int cstate; +extern int nstate; + +extern int num_lines, skip_input, interactive; + +/*** Parsing of arguments ***/ + +static void +usage(void) +{ + fprintf(stderr, "Usage: birdc [-s ] [-v] [-r]\n"); + exit(1); +} + +static void +parse_args(int argc, char **argv) +{ + int c; + + while ((c = getopt(argc, argv, opt_list)) >= 0) + switch (c) + { + case 's': + server_path = optarg; + break; + case 'v': + verbose++; + break; + case 'r': + restricted = 1; + break; + default: + usage(); + } + + /* If some arguments are not options, we take it as commands */ + if (optind < argc) + { + char *tmp; + int i; + int len = 0; + + for (i = optind; i < argc; i++) + len += strlen(argv[i]) + 1; + + tmp = init_cmd = malloc(len); + for (i = optind; i < argc; i++) + { + strcpy(tmp, argv[i]); + tmp += strlen(tmp); + *tmp++ = ' '; + } + tmp[-1] = 0; + + once = 1; + } +} + +/*** Input ***/ + +/* HACK: libreadline internals we need to access */ +extern int _rl_vis_botlin; +extern void _rl_move_vert(int); +extern Function *rl_last_func; + +static void +add_history_dedup(char *cmd) +{ + /* Add history line if it differs from the last one */ + HIST_ENTRY *he = history_get(history_length); + if (!he || strcmp(he->line, cmd)) + add_history(cmd); +} + +void got_line(char *cmd_buffer) +{ + char *cmd; + + if (!cmd_buffer) + { + cleanup(); + exit(0); + } + if (cmd_buffer[0]) + { + cmd = cmd_expand(cmd_buffer); + if (cmd) + { + add_history_dedup(cmd); + + if (!handle_internal_command(cmd)) + submit_server_command(cmd); + + free(cmd); + } + else + add_history_dedup(cmd_buffer); + } + free(cmd_buffer); +} + +void +input_start_list(void) /* Leave the currently edited line and make space for listing */ +{ + _rl_move_vert(_rl_vis_botlin); +#ifdef HAVE_RL_CRLF + rl_crlf(); +#endif +} + +void +input_stop_list(void) /* Reprint the currently edited line after listing */ +{ + rl_on_new_line(); + rl_redisplay(); +} + +static int +input_complete(int arg UNUSED, int key UNUSED) +{ + static int complete_flag; + char buf[256]; + + if (rl_last_func != input_complete) + complete_flag = 0; + switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) + { + case 0: + complete_flag = 1; + break; + case 1: + rl_insert_text(buf); + break; + default: + complete_flag = 1; +#ifdef HAVE_RL_DING + rl_ding(); +#endif + } + return 0; +} + +static int +input_help(int arg, int key UNUSED) +{ + int i, in_string, in_bracket; + + if (arg != 1) + return rl_insert(arg, '?'); + + in_string = in_bracket = 0; + for (i = 0; i < rl_point; i++) + { + + if (rl_line_buffer[i] == '"') + in_string = ! in_string; + else if (! in_string) + { + if (rl_line_buffer[i] == '[') + in_bracket++; + else if (rl_line_buffer[i] == ']') + in_bracket--; + } + } + + /* `?' inside string or path -> insert */ + if (in_string || in_bracket) + return rl_insert(1, '?'); + + rl_begin_undo_group(); /* HACK: We want to display `?' at point position */ + rl_insert_text("?"); + rl_redisplay(); + rl_end_undo_group(); + input_start_list(); + cmd_help(rl_line_buffer, rl_point); + rl_undo_command(1, 0); + input_stop_list(); + return 0; +} + +static void +input_init(void) +{ + rl_readline_name = "birdc"; + rl_add_defun("bird-complete", input_complete, '\t'); + rl_add_defun("bird-help", input_help, '?'); + rl_callback_handler_install("bird> ", got_line); + input_initialized = 1; +// readline library does strange things when stdin is nonblocking. +// if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) +// die("fcntl: %m"); +} + +static void +input_hide(void) +{ + input_hidden_end = rl_end; + rl_end = 0; + rl_expand_prompt(""); + rl_redisplay(); +} + +static void +input_reveal(void) +{ + /* need this, otherwise some lib seems to eat pending output when + the prompt is displayed */ + fflush(stdout); + tcdrain(fileno(stdout)); + + rl_end = input_hidden_end; + rl_expand_prompt("bird> "); + rl_forced_update_display(); +} + +void +update_state(void) +{ + if (nstate == cstate) + return; + + if (restricted) + { + submit_server_command("restrict"); + restricted = 0; + return; + } + + if (init_cmd) + { + /* First transition - client received hello from BIRD + and there is waiting initial command */ + submit_server_command(init_cmd); + init_cmd = NULL; + return; + } + + if (!init_cmd && once) + { + /* Initial command is finished and we want to exit */ + cleanup(); + exit(0); + } + + if (nstate == STATE_PROMPT) + { + if (input_initialized) + input_reveal(); + else + input_init(); + } + + if (nstate != STATE_PROMPT) + input_hide(); + + cstate = nstate; +} + +void +more(void) +{ + printf("--More--\015"); + fflush(stdout); + + redo: + switch (getchar()) + { + case 32: + num_lines = 2; + break; + case 13: + num_lines--; + break; + case 'q': + skip_input = 1; + break; + default: + goto redo; + } + + printf(" \015"); + fflush(stdout); +} + +void cleanup(void) +{ + if (input_initialized) + { + input_initialized = 0; + input_hide(); + rl_callback_handler_remove(); + } +} + + +/*** Communication with server ***/ + +static fd_set select_fds; + +static void +select_loop(void) +{ + int rv; + while (1) + { + FD_ZERO(&select_fds); + + if (cstate != STATE_CMD_USER) + FD_SET(server_fd, &select_fds); + if (cstate != STATE_CMD_SERVER) + FD_SET(0, &select_fds); + + rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); + if (rv < 0) + { + if (errno == EINTR) + continue; + else + die("select: %m"); + } + + if (FD_ISSET(server_fd, &select_fds)) + { + server_read(); + update_state(); + } + + if (FD_ISSET(0, &select_fds)) + { + rl_callback_read_char(); + update_state(); + } + } +} + +#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) + +void server_got_reply(char *x) +{ + int code; + int len = 0; + + if (*x == '+') /* Async reply */ + PRINTF(len, ">>> %s\n", x+1); + else if (x[0] == ' ') /* Continuation */ + PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); + else if (strlen(x) > 4 && + sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && + (x[4] == ' ' || x[4] == '-')) + { + if (code) + PRINTF(len, "%s\n", verbose ? x : x+5); + if (x[4] == ' ') + { + nstate = STATE_PROMPT; + skip_input = 0; + return; + } + } + else + PRINTF(len, "??? <%s>\n", x); + + if (skip_input) + return; + + if (interactive && input_initialized && (len > 0)) + { + int lns = LINES ? LINES : 25; + int cls = COLS ? COLS : 80; + num_lines += (len + cls - 1) / cls; /* Divide and round up */ + if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) + more(); + } +} + +int +main(int argc, char **argv) +{ +#ifdef HAVE_LIBDMALLOC + if (!getenv("DMALLOC_OPTIONS")) + dmalloc_debug(0x2f03d00); +#endif + + interactive = isatty(0); + parse_args(argc, argv); + cmd_build_tree(); + server_connect(); + select_loop(); + return 0; +} diff --git a/client/birdcl/Makefile b/client/birdcl/Makefile new file mode 100644 index 00000000..fcdc5eb2 --- /dev/null +++ b/client/birdcl/Makefile @@ -0,0 +1,5 @@ +source=client.c +root-rel=../../ +dir-name=client/birdcl + +include ../../Rules diff --git a/client/birdcl/client.c b/client/birdcl/client.c new file mode 100644 index 00000000..0a7e3808 --- /dev/null +++ b/client/birdcl/client.c @@ -0,0 +1,416 @@ +/* + * BIRD Client + * + * (c) 1999--2004 Martin Mares + * (c) 2013 Tomas Hlavacek + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +#define INPUT_BUF_LEN 2048 + +static char *opt_list = "s:vr"; +static int verbose; +static char *init_cmd; +static int once; + +extern char *server_path; +extern int server_fd; + +extern int nstate; +extern int num_lines, skip_input, interactive; + +static int term_lns=25; +static int term_cls=80; +struct termios tty_save; + +void +input_start_list(void) +{ + /* Empty in non-ncurses version. */ +} + +void +input_stop_list(void) +{ + /* Empty in non-ncurses version. */ +} + +/*** Parsing of arguments ***/ + +static void +usage(void) +{ + fprintf(stderr, "Usage: birdc [-s ] [-v] [-r]\n"); + exit(1); +} + +static void +parse_args(int argc, char **argv) +{ + int c; + + while ((c = getopt(argc, argv, opt_list)) >= 0) + switch (c) + { + case 's': + server_path = optarg; + break; + case 'v': + verbose++; + break; + case 'r': + init_cmd = "restrict"; + break; + default: + usage(); + } + + /* If some arguments are not options, we take it as commands */ + if (optind < argc) + { + char *tmp; + int i; + int len = 0; + + if (init_cmd) + usage(); + + for (i = optind; i < argc; i++) + len += strlen(argv[i]) + 1; + + tmp = init_cmd = malloc(len); + for (i = optind; i < argc; i++) + { + strcpy(tmp, argv[i]); + tmp += strlen(tmp); + *tmp++ = ' '; + } + tmp[-1] = 0; + + once = 1; + } +} + +static void +run_init_cmd(void) +{ + if (init_cmd) + { + /* First transition - client received hello from BIRD + and there is waiting initial command */ + submit_server_command(init_cmd); + init_cmd = NULL; + return; + } + + if (!init_cmd && once && (nstate == STATE_CMD_USER)) + { + /* Initial command is finished and we want to exit */ + cleanup(); + exit(0); + } +} + +/*** Input ***/ + +static void +got_line(char *cmd_buffer) +{ + char *cmd; + + if (!cmd_buffer) + { + cleanup(); + exit(0); + } + if (cmd_buffer[0]) + { + cmd = cmd_expand(cmd_buffer); + if (cmd) + { + if (!handle_internal_command(cmd)) + submit_server_command(cmd); + + free(cmd); + } + } + free(cmd_buffer); +} + +void +cleanup(void) +{ + /* No ncurses -> restore terminal state. */ + if (interactive) + if (tcsetattr (0, TCSANOW, &tty_save) != 0) + { + perror("tcsetattr error"); + exit(EXIT_FAILURE); + } +} + +static void +print_prompt(void) +{ + /* No ncurses -> no status to reveal/hide, print prompt manually. */ + printf("bird> "); + fflush(stdout); +} + + +static void +term_read(void) +{ + char *buf = malloc(INPUT_BUF_LEN); + + if (fgets(buf, INPUT_BUF_LEN, stdin) == NULL) { + free(buf); + exit(0); + } + + if (buf[strlen(buf)-1] != '\n') + { + printf("Input too long.\n"); + free(buf); + return; + } + + if (strlen(buf) <= 0) + { + free(buf); + return; + } + + buf[strlen(buf)-1] = '\0'; + + if (!interactive) + { + print_prompt(); + printf("%s\n",buf); + } + + if (strchr(buf, '?')) + { + printf("\n"); + cmd_help(buf, strlen(buf)); + free(buf); + return; + } + + if (strlen(buf) > 0) + { + got_line(buf); /* buf is free()-ed inside */ + } + else + { + free(buf); /* no command, only newline -> no-op */ + return; + } + +} + +/*** Communication with server ***/ + +void +more(void) +{ + struct termios tty; + + printf("--More--\015"); + fflush(stdout); + + if (tcgetattr(0, &tty) != 0) + { + perror("tcgetattr error"); + exit(EXIT_FAILURE); + } + tty.c_lflag &= (~ECHO); + tty.c_lflag &= (~ICANON); + if (tcsetattr (0, TCSANOW, &tty) != 0) + { + perror("tcsetattr error"); + exit(EXIT_FAILURE); + } + + redo: + switch (getchar()) + { + case 32: + num_lines = 2; + break; + case 13: + num_lines--; + break; + case '\n': + num_lines--; + break; + case 'q': + skip_input = 1; + break; + default: + goto redo; + } + + tty.c_lflag |= ECHO; + tty.c_lflag |= ICANON; + if (tcsetattr (0, TCSANOW, &tty) != 0) + { + perror("tcsetattr error"); + exit(EXIT_FAILURE); + } + + printf(" \015"); + fflush(stdout); +} + +static void +get_term_size(void) +{ + struct winsize tws; + if (ioctl(0, TIOCGWINSZ, &tws) == 0) + { + term_lns = tws.ws_row; + term_cls = tws.ws_col; + } + else + { + term_lns = 25; + term_cls = 80; + } +} + +#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) + +void +server_got_reply(char *x) +{ + int code; + int len = 0; + + if (*x == '+') /* Async reply */ + PRINTF(len, ">>> %s\n", x+1); + else if (x[0] == ' ') /* Continuation */ + PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); + else if (strlen(x) > 4 && + sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && + (x[4] == ' ' || x[4] == '-')) + { + if (code) + PRINTF(len, "%s\n", verbose ? x : x+5); + + if (x[4] == ' ') + { + nstate = STATE_CMD_USER; + skip_input = 0; + return; + } + } + else + PRINTF(len, "??? <%s>\n", x); + + if (skip_input) + return; + + if (interactive && (len > 0)) + { + num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */ + if (num_lines >= term_lns) + more(); + } +} + +static fd_set select_fds; + +static void +select_loop(void) +{ + int rv; + + while (1) + { + FD_ZERO(&select_fds); + + if (nstate != STATE_CMD_USER) + FD_SET(server_fd, &select_fds); + + if (nstate != STATE_CMD_SERVER) + { + FD_SET(0, &select_fds); + if (interactive) + print_prompt(); + } + + rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); + if (rv < 0) + { + if (errno == EINTR) + continue; + else + die("select: %m"); + } + + if (FD_ISSET(server_fd, &select_fds)) + { + server_read(); + run_init_cmd(); + } + + if (FD_ISSET(0, &select_fds)) + term_read(); + } +} + +static void +sig_handler(int signal) +{ + cleanup(); + exit(0); +} + +int +main(int argc, char **argv) +{ + interactive = isatty(fileno(stdin)); + if (interactive) + { + if (signal(SIGINT, sig_handler) == SIG_IGN) + signal(SIGINT, SIG_IGN); + if (signal(SIGHUP, sig_handler) == SIG_IGN) + signal(SIGHUP, SIG_IGN); + if (signal(SIGTERM, sig_handler) == SIG_IGN) + signal(SIGTERM, SIG_IGN); + + get_term_size(); + + if (tcgetattr(0, &tty_save) != 0) + { + perror("tcgetattr error"); + return(EXIT_FAILURE); + } + } + + parse_args(argc, argv); + cmd_build_tree(); + server_connect(); + select_loop(); + return 0; +} diff --git a/client/client.h b/client/client.h index 373b1c6f..2d215059 100644 --- a/client/client.h +++ b/client/client.h @@ -6,11 +6,12 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -/* client.c */ +/* client.c callbacks */ void cleanup(void); void input_start_list(void); void input_stop_list(void); +void server_got_reply(char *x); /* commands.c */ diff --git a/client/client_full.c b/client/client_full.c deleted file mode 100644 index 9b4dd2fb..00000000 --- a/client/client_full.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * BIRD Client - * - * (c) 1999--2004 Martin Mares - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nest/bird.h" -#include "lib/resource.h" -#include "lib/string.h" -#include "client/client.h" -#include "sysdep/unix/unix.h" - -static char *opt_list = "s:vr"; -static int verbose; -static char *init_cmd; -static int once; -static int restricted; - -extern char *server_path; -extern int server_fd; -extern byte server_read_buf[SERVER_READ_BUF_LEN]; -extern byte *server_read_pos; - -extern int input_initialized; -extern int input_hidden_end; -extern int cstate; -extern int nstate; - -extern int num_lines, skip_input, interactive; - -/*** Parsing of arguments ***/ - -static void -usage(void) -{ - fprintf(stderr, "Usage: birdc [-s ] [-v] [-r]\n"); - exit(1); -} - -static void -parse_args(int argc, char **argv) -{ - int c; - - while ((c = getopt(argc, argv, opt_list)) >= 0) - switch (c) - { - case 's': - server_path = optarg; - break; - case 'v': - verbose++; - break; - case 'r': - restricted = 1; - break; - default: - usage(); - } - - /* If some arguments are not options, we take it as commands */ - if (optind < argc) - { - char *tmp; - int i; - int len = 0; - - for (i = optind; i < argc; i++) - len += strlen(argv[i]) + 1; - - tmp = init_cmd = malloc(len); - for (i = optind; i < argc; i++) - { - strcpy(tmp, argv[i]); - tmp += strlen(tmp); - *tmp++ = ' '; - } - tmp[-1] = 0; - - once = 1; - } -} - -/*** Input ***/ - -/* HACK: libreadline internals we need to access */ -extern int _rl_vis_botlin; -extern void _rl_move_vert(int); -extern Function *rl_last_func; - -static void -add_history_dedup(char *cmd) -{ - /* Add history line if it differs from the last one */ - HIST_ENTRY *he = history_get(history_length); - if (!he || strcmp(he->line, cmd)) - add_history(cmd); -} - -void got_line(char *cmd_buffer) -{ - char *cmd; - - if (!cmd_buffer) - { - cleanup(); - exit(0); - } - if (cmd_buffer[0]) - { - cmd = cmd_expand(cmd_buffer); - if (cmd) - { - add_history_dedup(cmd); - - if (!handle_internal_command(cmd)) - submit_server_command(cmd); - - free(cmd); - } - else - add_history_dedup(cmd_buffer); - } - free(cmd_buffer); -} - -void -input_start_list(void) /* Leave the currently edited line and make space for listing */ -{ - _rl_move_vert(_rl_vis_botlin); -#ifdef HAVE_RL_CRLF - rl_crlf(); -#endif -} - -void -input_stop_list(void) /* Reprint the currently edited line after listing */ -{ - rl_on_new_line(); - rl_redisplay(); -} - -static int -input_complete(int arg UNUSED, int key UNUSED) -{ - static int complete_flag; - char buf[256]; - - if (rl_last_func != input_complete) - complete_flag = 0; - switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) - { - case 0: - complete_flag = 1; - break; - case 1: - rl_insert_text(buf); - break; - default: - complete_flag = 1; -#ifdef HAVE_RL_DING - rl_ding(); -#endif - } - return 0; -} - -static int -input_help(int arg, int key UNUSED) -{ - int i, in_string, in_bracket; - - if (arg != 1) - return rl_insert(arg, '?'); - - in_string = in_bracket = 0; - for (i = 0; i < rl_point; i++) - { - - if (rl_line_buffer[i] == '"') - in_string = ! in_string; - else if (! in_string) - { - if (rl_line_buffer[i] == '[') - in_bracket++; - else if (rl_line_buffer[i] == ']') - in_bracket--; - } - } - - /* `?' inside string or path -> insert */ - if (in_string || in_bracket) - return rl_insert(1, '?'); - - rl_begin_undo_group(); /* HACK: We want to display `?' at point position */ - rl_insert_text("?"); - rl_redisplay(); - rl_end_undo_group(); - input_start_list(); - cmd_help(rl_line_buffer, rl_point); - rl_undo_command(1, 0); - input_stop_list(); - return 0; -} - -static void -input_init(void) -{ - rl_readline_name = "birdc"; - rl_add_defun("bird-complete", input_complete, '\t'); - rl_add_defun("bird-help", input_help, '?'); - rl_callback_handler_install("bird> ", got_line); - input_initialized = 1; -// readline library does strange things when stdin is nonblocking. -// if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) -// die("fcntl: %m"); -} - -static void -input_hide(void) -{ - input_hidden_end = rl_end; - rl_end = 0; - rl_expand_prompt(""); - rl_redisplay(); -} - -static void -input_reveal(void) -{ - /* need this, otherwise some lib seems to eat pending output when - the prompt is displayed */ - fflush(stdout); - tcdrain(fileno(stdout)); - - rl_end = input_hidden_end; - rl_expand_prompt("bird> "); - rl_forced_update_display(); -} - -void -update_state(void) -{ - if (nstate == cstate) - return; - - if (restricted) - { - submit_server_command("restrict"); - restricted = 0; - return; - } - - if (init_cmd) - { - /* First transition - client received hello from BIRD - and there is waiting initial command */ - submit_server_command(init_cmd); - init_cmd = NULL; - return; - } - - if (!init_cmd && once) - { - /* Initial command is finished and we want to exit */ - cleanup(); - exit(0); - } - - if (nstate == STATE_PROMPT) - { - if (input_initialized) - input_reveal(); - else - input_init(); - } - - if (nstate != STATE_PROMPT) - input_hide(); - - cstate = nstate; -} - -void -more(void) -{ - printf("--More--\015"); - fflush(stdout); - - redo: - switch (getchar()) - { - case 32: - num_lines = 2; - break; - case 13: - num_lines--; - break; - case 'q': - skip_input = 1; - break; - default: - goto redo; - } - - printf(" \015"); - fflush(stdout); -} - -void cleanup(void) -{ - if (input_initialized) - { - input_initialized = 0; - input_hide(); - rl_callback_handler_remove(); - } -} - - -/*** Communication with server ***/ - -static fd_set select_fds; - -static void -select_loop(void) -{ - int rv; - while (1) - { - FD_ZERO(&select_fds); - - if (cstate != STATE_CMD_USER) - FD_SET(server_fd, &select_fds); - if (cstate != STATE_CMD_SERVER) - FD_SET(0, &select_fds); - - rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); - if (rv < 0) - { - if (errno == EINTR) - continue; - else - die("select: %m"); - } - - if (FD_ISSET(server_fd, &select_fds)) - { - server_read(); - update_state(); - } - - if (FD_ISSET(0, &select_fds)) - { - rl_callback_read_char(); - update_state(); - } - } -} - -#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) - -void server_got_reply(char *x) -{ - int code; - int len = 0; - - if (*x == '+') /* Async reply */ - PRINTF(len, ">>> %s\n", x+1); - else if (x[0] == ' ') /* Continuation */ - PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); - else if (strlen(x) > 4 && - sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && - (x[4] == ' ' || x[4] == '-')) - { - if (code) - PRINTF(len, "%s\n", verbose ? x : x+5); - if (x[4] == ' ') - { - nstate = STATE_PROMPT; - skip_input = 0; - return; - } - } - else - PRINTF(len, "??? <%s>\n", x); - - if (skip_input) - return; - - if (interactive && input_initialized && (len > 0)) - { - int lns = LINES ? LINES : 25; - int cls = COLS ? COLS : 80; - num_lines += (len + cls - 1) / cls; /* Divide and round up */ - if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) - more(); - } -} - -int -main(int argc, char **argv) -{ -#ifdef HAVE_LIBDMALLOC - if (!getenv("DMALLOC_OPTIONS")) - dmalloc_debug(0x2f03d00); -#endif - - interactive = isatty(0); - parse_args(argc, argv); - cmd_build_tree(); - server_connect(); - select_loop(); - return 0; -} diff --git a/configure.in b/configure.in index 54993dfc..534baf21 100644 --- a/configure.in +++ b/configure.in @@ -234,7 +234,7 @@ fi CLIENT= CLIENT_LIBS= if test "$enable_client" = yes ; then - CLIENT=client + CLIENT=birdc AC_CHECK_LIB(history, add_history, CLIENT_LIBS="-lhistory") AC_CHECK_LIB(ncurses, tgetent, USE_TERMCAP_LIB=-lncurses, AC_CHECK_LIB(curses, tgetent, USE_TERMCAP_LIB=-lcurses, diff --git a/tools/Makefile.in b/tools/Makefile.in index 728e5797..adf35b64 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -3,29 +3,36 @@ include Rules -.PHONY: all daemon client subdir depend clean distclean tags docs userdocs progdocs +.PHONY: all daemon birdci birdcl subdir depend clean distclean tags docs userdocs progdocs -all: sysdep/paths.h .dep-stamp subdir daemon @CLIENT@ +all: sysdep/paths.h .dep-stamp subdir daemon birdcl @CLIENT@ daemon: $(exedir)/bird -client: $(exedir)/birdc +birdc: $(exedir)/birdc + +birdcl: $(exedir)/birdcl bird-dep := $(addsuffix /all.o, $(static-dirs)) conf/all.o lib/birdlib.a $(bird-dep): sysdep/paths.h .dep-stamp subdir -birdc-dep := client/all.o lib/birdlib.a +birdc-dep := client/birdc/all.o client/all.o lib/birdlib.a $(birdc-dep): sysdep/paths.h .dep-stamp subdir +birdcl-dep := client/birdcl/all.o client/all.o lib/birdlib.a + +$(birdcl-dep): sysdep/paths.h .dep-stamp subdir + + depend: sysdep/paths.h .dir-stamp set -e ; for a in $(dynamic-dirs) ; do $(MAKE) -C $$a $@ ; done - set -e ; for a in $(static-dirs) $(client-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done + set -e ; for a in $(static-dirs) $(birdcl-dirs) $(birdc-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done subdir: sysdep/paths.h .dir-stamp .dep-stamp set -e ; for a in $(dynamic-dirs) ; do $(MAKE) -C $$a $@ ; done - set -e ; for a in $(static-dirs) $(client-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done + set -e ; for a in $(static-dirs) $(birdcl-dirs) $(birdc-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done $(exedir)/bird: $(bird-dep) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) @@ -33,8 +40,11 @@ $(exedir)/bird: $(bird-dep) $(exedir)/birdc: $(birdc-dep) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(CLIENT_LIBS) +$(exedir)/birdcl: $(birdcl-dep) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + .dir-stamp: sysdep/paths.h - mkdir -p $(static-dirs) $(client-dirs) $(doc-dirs) + mkdir -p $(static-dirs) $(birdcl-dirs) $(birdc-dirs) $(doc-dirs) touch .dir-stamp .dep-stamp: @@ -58,6 +68,7 @@ tags: install: all $(INSTALL) -d $(DESTDIR)/$(sbindir) $(DESTDIR)/$(sysconfdir) $(DESTDIR)/@runtimedir@ $(INSTALL_PROGRAM) -s $(exedir)/bird $(DESTDIR)/$(sbindir)/bird@SUFFIX@ + $(INSTALL_PROGRAM) -s $(exedir)/birdcl $(DESTDIR)/$(sbindir)/birdcl@SUFFIX@ if test -n "@CLIENT@" ; then \ $(INSTALL_PROGRAM) -s $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc@SUFFIX@ ; \ fi @@ -74,7 +85,7 @@ install-docs: clean: find . -name "*.[oa]" -o -name core -o -name depend -o -name "*.html" | xargs rm -f rm -f conf/cf-lex.c conf/cf-parse.* conf/commands.h conf/keywords.h - rm -f $(exedir)/bird $(exedir)/birdc $(exedir)/bird.ctl $(exedir)/bird6.ctl .dep-stamp + rm -f $(exedir)/bird $(exedir)/birdcl $(exedir)/birdc $(exedir)/bird.ctl $(exedir)/bird6.ctl .dep-stamp distclean: clean rm -f config.* configure sysdep/autoconf.h sysdep/paths.h Makefile Rules diff --git a/tools/Rules.in b/tools/Rules.in index fc06aeb1..fd10f5de 100644 --- a/tools/Rules.in +++ b/tools/Rules.in @@ -11,12 +11,14 @@ static-dirs := nest filter $(addprefix proto/,$(protocols)) static-dir-paths := $(addprefix $(srcdir)/,$(static-dirs)) dynamic-dirs := lib conf dynamic-dir-paths := $(dynamic-dirs) -client-dirs := @CLIENT@ -client-dir-paths := $(client-dirs) +birdc-dirs := client client/@CLIENT@ +birdc-dir-paths := $(birdc-dirs) +birdcl-dirs := client client/birdcl +birdcl-dir-paths := $(birdcl-dirs) doc-dirs := doc doc-dir-paths := $(doc-dirs) -all-dirs:=$(static-dirs) $(dynamic-dirs) $(client-dirs) $(doc-dirs) +all-dirs:=$(static-dirs) $(dynamic-dirs) $(birdc-dirs) $(doc-dirs) clean-dirs:=$(all-dirs) proto sysdep CPPFLAGS=-I$(root-rel) -I$(srcdir) @CPPFLAGS@ -- cgit v1.2.3 From ce1348537455e5482a283f7a4cae734d13dcf34e Mon Sep 17 00:00:00 2001 From: Tomas Hlavacek Date: Tue, 19 Mar 2013 18:02:40 +0100 Subject: Fix birdcl questionmark handling Fix handling of questionmark handling in the bird light client. The questionmark should display help when it is the last non-blank character on command line. Otherwise the questionmark does not have any special meaning and it could be a part of a pattern. --- client/birdcl/client.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/client/birdcl/client.c b/client/birdcl/client.c index 0a7e3808..21b63270 100644 --- a/client/birdcl/client.c +++ b/client/birdcl/client.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "nest/bird.h" #include "lib/resource.h" @@ -177,6 +178,18 @@ print_prompt(void) } +static int lastnb(char *str) +{ + int i; + for (i=strlen(str)-1; i>0; i--) + { + if(!isblank(str[i])) + return i; + } + + return 0; +} + static void term_read(void) { @@ -208,7 +221,7 @@ term_read(void) printf("%s\n",buf); } - if (strchr(buf, '?')) + if (buf[lastnb(buf)] == '?') { printf("\n"); cmd_help(buf, strlen(buf)); -- cgit v1.2.3 From 568d9c9faeab70951d8e9bfea521e1b38a9a3d1c Mon Sep 17 00:00:00 2001 From: Tomas Hlavacek Date: Sat, 6 Apr 2013 22:07:32 +0200 Subject: Fix birdcl async message handling Fix handling of async messafe in the bird light client. The async message may occure at the any moment so we need the client to liste for the message from server when it waits for user input. --- client/birdcl/client.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/birdcl/client.c b/client/birdcl/client.c index 21b63270..1e920ce8 100644 --- a/client/birdcl/client.c +++ b/client/birdcl/client.c @@ -36,7 +36,7 @@ static int once; extern char *server_path; extern int server_fd; -extern int nstate; +extern int cstate; extern int num_lines, skip_input, interactive; static int term_lns=25; @@ -123,7 +123,7 @@ run_init_cmd(void) return; } - if (!init_cmd && once && (nstate == STATE_CMD_USER)) + if (!init_cmd && once && (cstate == STATE_PROMPT)) { /* Initial command is finished and we want to exit */ cleanup(); @@ -332,7 +332,7 @@ server_got_reply(char *x) if (x[4] == ' ') { - nstate = STATE_CMD_USER; + cstate = STATE_PROMPT; skip_input = 0; return; } @@ -362,10 +362,10 @@ select_loop(void) { FD_ZERO(&select_fds); - if (nstate != STATE_CMD_USER) + if (cstate != STATE_CMD_USER) FD_SET(server_fd, &select_fds); - if (nstate != STATE_CMD_SERVER) + if (cstate != STATE_CMD_SERVER) { FD_SET(0, &select_fds); if (interactive) -- cgit v1.2.3 From 9ff5257357d9975654279db17bbc8525583ba1cc Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 16 Apr 2013 16:22:31 +0200 Subject: Better handling of global addresses as configured NBMA neighbors in OSPFv3. Configured NBMA neighbors in OSPFv3 should be link-local addresses, old behavior was to silently ignore global ones. The patch allows BIRD to accept global ones, but adds a warning and a documentation notice. Thanks to Wilco Baan Hofman for the bugreport. --- doc/bird.sgml | 5 ++++- proto/ospf/config.Y | 28 ++++++++++------------------ proto/ospf/iface.c | 24 ++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index e83cf0e1..8e5641e0 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2212,7 +2212,10 @@ protocol ospf <name> { neighbors { A set of neighbors to which Hello messages on NBMA or PtMP networks are to be sent. For NBMA networks, some of them - could be marked as eligible. + could be marked as eligible. In OSPFv3, link-local addresses + should be used, using global ones is possible, but it is + nonstandard and might be problematic. And definitely, + link-local and global addresses should not be mixed. diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 3f09afba..b16d46a9 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -135,6 +135,7 @@ CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL) %type opttext %type lsadb_args +%type nbma_eligible CF_GRAMMAR @@ -295,7 +296,7 @@ ospf_iface_item: | STUB bool { OSPF_PATT->stub = $2 ; } | CHECK LINK bool { OSPF_PATT->check_link = $3; } | ECMP WEIGHT expr { OSPF_PATT->ecmp_weight = $3 - 1; if (($3<1) || ($3>256)) cf_error("ECMP weight must be in range 1-256"); } - | NEIGHBORS '{' ipa_list '}' + | NEIGHBORS '{' nbma_list '}' | AUTHENTICATION NONE { OSPF_PATT->autype = OSPF_AUTH_NONE ; } | AUTHENTICATION SIMPLE { OSPF_PATT->autype = OSPF_AUTH_SIMPLE ; } | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT ; } @@ -327,33 +328,24 @@ pref_opt: | TAG expr { this_pref->tag = $2; } ; -ipa_list: +nbma_list: /* empty */ - | ipa_list ipa_item + | nbma_list nbma_item ; -ipa_item: - ipa_el - | ipa_ne; +nbma_eligible: + /* empty */ { $$ = 0; } + | ELIGIBLE { $$ = 1; } + ; -ipa_el: IPA ';' +nbma_item: IPA nbma_eligible ';' { this_nbma = cfg_allocz(sizeof(struct nbma_node)); add_tail(&OSPF_PATT->nbma_list, NODE this_nbma); this_nbma->ip=$1; - this_nbma->eligible=0; + this_nbma->eligible=$2; } ; - -ipa_ne: IPA ELIGIBLE ';' - { - this_nbma = cfg_allocz(sizeof(struct nbma_node)); - add_tail(&OSPF_PATT->nbma_list, NODE this_nbma); - this_nbma->ip=$1; - this_nbma->eligible=1; - } -; - ospf_iface_start: { diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 290a8634..39084cef 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -574,8 +574,22 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i init_list(&ifa->nbma_list); WALK_LIST(nb, ip->nbma_list) - if (ipa_in_net(nb->ip, addr->prefix, addr->pxlen)) - add_nbma_node(ifa, nb, 0); + { + /* In OSPFv3, addr is link-local while configured neighbors could + have global IP (although RFC 5340 C.5 says link-local addresses + should be used). Because OSPFv3 iface is not subnet-specific, + there is no need for ipa_in_net() check */ + +#ifdef OSPFv2 + if (!ipa_in_net(nb->ip, addr->prefix, addr->pxlen)) + continue; +#else + if (!ipa_has_link_scope(nb->ip)) + log(L_WARN "In OSPFv3, configured neighbor address (%I) should be link-local", nb->ip); +#endif + + add_nbma_node(ifa, nb, 0); + } ifa->state = OSPF_IS_DOWN; add_tail(&oa->po->iface_list, NODE ifa); @@ -771,8 +785,14 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* NBMA LIST - add new */ WALK_LIST(nb, new->nbma_list) { + /* See related note in ospf_iface_new() */ +#ifdef OSPFv2 if (!ipa_in_net(nb->ip, ifa->addr->prefix, ifa->addr->pxlen)) continue; +#else + if (!ipa_has_link_scope(nb->ip)) + log(L_WARN "In OSPFv3, configured neighbor address (%I) should be link-local", nb->ip); +#endif if (! find_nbma_node(ifa, nb->ip)) { -- cgit v1.2.3 From 48bc232f08141d26691237c3d79db587ce16932b Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 16 Apr 2013 17:27:34 +0200 Subject: Implements 'next hop keep' option for BGP. This option allows to keep the received next hop even in cases when the route is sent to an interface with a different subnet. --- doc/bird.sgml | 5 +++++ proto/bgp/attrs.c | 8 ++++++-- proto/bgp/bgp.h | 1 + proto/bgp/config.Y | 3 ++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 8e5641e0..300a71f3 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1329,6 +1329,11 @@ for each neighbor using the following configuration parameters: circumvent misconfigurations of other routers. Default: disabled. + next hop keep Forward the received Next Hop + attribute even in situations where the local address should be + used instead, like when the route is sent to an interface with + a different subnet. Default: disabled. + missing lladdr self|drop|ignoreNext Hop attribute in BGP-IPv6 sometimes contains just the global IPv6 address, but sometimes it has to contain both global and link-local diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 98b2f2c2..c27a4988 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -935,7 +935,8 @@ bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p rta->dest != RTD_ROUTER || ipa_equal(rta->gw, IPA_NONE) || ipa_has_link_scope(rta->gw) || - (!p->is_internal && (!p->neigh || (rta->iface != p->neigh->iface)))) + (!p->is_internal && !p->cf->next_hop_keep && + (!p->neigh || (rta->iface != p->neigh->iface)))) set_next_hop(z, p->source_addr); else set_next_hop(z, rta->gw); @@ -1003,10 +1004,13 @@ bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p /* iBGP -> keep next_hop, eBGP multi-hop -> use source_addr, * eBGP single-hop -> keep next_hop if on the same iface. * If the next_hop is zero (i.e. link-local), keep only if on the same iface. + * + * Note that same-iface-check uses iface from route, which is based on gw. */ a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); if (a && !p->cf->next_hop_self && - ((p->is_internal && ipa_nonzero(*((ip_addr *) a->u.ptr->data))) || + (p->cf->next_hop_keep || + (p->is_internal && ipa_nonzero(*((ip_addr *) a->u.ptr->data))) || (p->neigh && (e->attrs->iface == p->neigh->iface)))) { /* Leave the original next hop attribute, will check later where does it point */ diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index c3adf254..13c7fd80 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -24,6 +24,7 @@ struct bgp_config { int multihop; /* Number of hops if multihop */ int ttl_security; /* Enable TTL security [RFC5082] */ int next_hop_self; /* Always set next hop to local IP address */ + int next_hop_keep; /* Do not touch next hop attribute */ int missing_lladdr; /* What we will do when we don' know link-local addr, see MLL_* */ int gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */ int compare_path_lengths; /* Use path lengths when selecting best route */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 8b80d7fd..d5e5aaca 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -76,7 +76,8 @@ bgp_proto: | bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; } | bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; } | bgp_proto MULTIHOP expr ';' { BGP_CFG->multihop = $3; if (($3<1) || ($3>255)) cf_error("Multihop must be in range 1-255"); } - | bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; } + | bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; BGP_CFG->next_hop_keep = 0; } + | bgp_proto NEXT HOP KEEP ';' { BGP_CFG->next_hop_keep = 1; BGP_CFG->next_hop_self = 0; } | bgp_proto MISSING LLADDR SELF ';' { BGP_CFG->missing_lladdr = MLL_SELF; } | bgp_proto MISSING LLADDR DROP ';' { BGP_CFG->missing_lladdr = MLL_DROP; } | bgp_proto MISSING LLADDR IGNORE ';' { BGP_CFG->missing_lladdr = MLL_IGNORE; } -- cgit v1.2.3 From 8bd9b930c320f09d3b3792b5f991cf702e9d55be Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 16 Apr 2013 17:40:44 +0200 Subject: Fixes a bug in IPv6 BGP next hop processing. BGP next hop attributes with empty link-local IPv6 addresses were not handled properly. Thanks to Sergey Popovich for the bugfix. --- proto/bgp/packets.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index cfa37fb5..f2e03f87 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -820,7 +820,7 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a) ip_addr *nexthop = (ip_addr *) nh->u.ptr->data; #ifdef IPV6 - int second = (nh->u.ptr->length == NEXT_HOP_LENGTH); + int second = (nh->u.ptr->length == NEXT_HOP_LENGTH) && ipa_nonzero(nexthop[1]); /* First address should not be link-local, but may be zero in direct mode */ if (ipa_has_link_scope(*nexthop)) -- cgit v1.2.3 From cd3b02d198093abbbe671f647e4deb2470eb9cf1 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 16 Apr 2013 17:53:22 +0200 Subject: Allows IP of loopback to be used in automatic router ID selection. Thanks to Alexander V. Chernikov for the patch. --- nest/iface.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nest/iface.c b/nest/iface.c index da79b21f..b4ab70c3 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -589,7 +589,7 @@ if_choose_router_id(struct iface_patt *mask, u32 old_id) WALK_LIST(i, iface_list) { if (!(i->flags & IF_ADMIN_UP) || - (i->flags & (IF_IGNORE | IF_SHUTDOWN))) + (i->flags & IF_SHUTDOWN)) continue; WALK_LIST(a, i->addrs) @@ -612,6 +612,10 @@ if_choose_router_id(struct iface_patt *mask, u32 old_id) if (mask && !iface_patt_match(mask, i, a)) continue; + /* FIXME: This should go away too */ + if ((i->flags & IF_IGNORE) && !mask) + continue; + /* No pattern or pattern matched */ if (!b || ipa_to_u32(a->ip) < ipa_to_u32(b->ip)) b = a; -- cgit v1.2.3 From 8df02847e8af29863c325b7297e3a2b2ed5f961c Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 17 Apr 2013 13:06:40 +0200 Subject: Fixes a compatibility issue in OSPFv2 PtP links. BIRD used zero netmask in hello packets on all PtP links, not just on unnumbered ones. This patch fixes it and adds option 'ptp netmask' for overriding the default behavior. Thanks to Alexander V. Chernikov for the original patch. --- doc/bird.sgml | 13 +++++++++++++ proto/ospf/config.Y | 4 +++- proto/ospf/hello.c | 3 ++- proto/ospf/iface.c | 3 +++ proto/ospf/ospf.h | 2 ++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 300a71f3..fab49105 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1940,6 +1940,7 @@ protocol ospf <name> { nonbroadcast|nbma|pointomultipoint|ptmp]; strict nonbroadcast <switch>; real broadcast <switch>; + ptp netmask <switch>; check link <switch>; ecmp weight <num>; authentication [none|simple|cryptographic]; @@ -2183,6 +2184,18 @@ protocol ospf <name> { probably is not interoperable with other OSPF implementations. Default value is no. + ptp netmask + In check link switch If set, a hardware link state (reported by OS) is taken into consideration. When a link disappears (e.g. an ethernet cable is diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index b16d46a9..2cc0b963 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -131,7 +131,7 @@ CF_KEYWORDS(NONE, SIMPLE, AUTHENTICATION, STRICT, CRYPTOGRAPHIC) CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, CHECK, LINK) 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) +CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL, NETMASK) %type opttext %type lsadb_args @@ -290,6 +290,7 @@ ospf_iface_item: | TYPE POINTOMULTIPOINT { OSPF_PATT->type = OSPF_IT_PTMP ; } | TYPE PTMP { OSPF_PATT->type = OSPF_IT_PTMP ; } | REAL BROADCAST bool { OSPF_PATT->real_bcast = $3; if (OSPF_VERSION != 2) cf_error("Real broadcast option requires OSPFv2"); } + | PTP NETMASK bool { OSPF_PATT->ptp_netmask = $3; if (OSPF_VERSION != 2) cf_error("Real netmask option requires OSPFv2"); } | TRANSMIT DELAY expr { OSPF_PATT->inftransdelay = $3 ; if (($3<=0) || ($3>65535)) cf_error("Transmit delay must be in range 1-65535"); } | PRIORITY expr { OSPF_PATT->priority = $2 ; if (($2<0) || ($2>255)) cf_error("Priority must be in range 0-255"); } | STRICT NONBROADCAST bool { OSPF_PATT->strictnbma = $3 ; } @@ -364,6 +365,7 @@ ospf_iface_start: OSPF_PATT->type = OSPF_IT_UNDEF; init_list(&OSPF_PATT->nbma_list); OSPF_PATT->autype = OSPF_AUTH_NONE; + OSPF_PATT->ptp_netmask = 2; /* not specified */ reset_passwords(); } ; diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index 6ec5c511..d5aa1b95 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -253,7 +253,8 @@ ospf_hello_send(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) #ifdef OSPFv2 pkt->netmask = ipa_mkmask(ifa->addr->pxlen); ipa_hton(pkt->netmask); - if ((ifa->type == OSPF_IT_VLINK) || (ifa->type == OSPF_IT_PTP)) + if ((ifa->type == OSPF_IT_VLINK) || + ((ifa->type == OSPF_IT_PTP) && !ifa->ptp_netmask)) pkt->netmask = IPA_NONE; #endif diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 39084cef..9050f7b1 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -537,6 +537,9 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i #ifdef OSPFv2 ifa->autype = ip->autype; ifa->passwords = ip->passwords; + ifa->ptp_netmask = !(addr->flags & IA_PEER); + if (ip->ptp_netmask < 2) + ifa->ptp_netmask = ip->ptp_netmask; #endif #ifdef OSPFv3 diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 7111a13d..d924e657 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -273,6 +273,7 @@ struct ospf_iface u16 rxbuf; /* Buffer size */ u8 check_link; /* Whether iface link change is used */ u8 ecmp_weight; /* Weight used for ECMP */ + u8 ptp_netmask; /* Send real netmask for P2P */ }; struct ospf_md5 @@ -810,6 +811,7 @@ struct ospf_iface_patt u8 check_link; u8 ecmp_weight; u8 real_bcast; /* Not really used in OSPFv3 */ + u8 ptp_netmask; /* bool but 2 for unspecified */ #ifdef OSPFv2 list *passwords; -- cgit v1.2.3 From efd6d12b975441c7e1875a59dd9e0f3db7e958cb Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 17 Apr 2013 15:09:50 +0200 Subject: Adds two new default GCC options. Adds two new default GCC options related to optimizations (-fno-strict-aliasing and -fno-strict-overflow). This should fix some hyperaggressive GCC optimizations. Also updates autoconf option detection. --- aclocal.m4 | 30 +++++++++++++++++------------- configure.in | 12 +++++++----- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/aclocal.m4 b/aclocal.m4 index 75b3f92a..3ceb6eb6 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -133,19 +133,23 @@ if test "$bird_cv_struct_ip_mreqn" = yes ; then fi ]) -AC_DEFUN(BIRD_CHECK_GCC_OPTIONS, -[AC_CACHE_VAL(bird_cv_c_option_no_pointer_sign, [ -cat >conftest.c <&AS_MESSAGE_LOG_FD 2>&1 ; then - bird_cv_c_option_no_pointer_sign=yes -else - bird_cv_c_option_no_pointer_sign=no -fi -rm -rf conftest* a.out -])]) +AC_DEFUN(BIRD_CHECK_GCC_OPTION, +[ + bird_tmp_cflags="$CFLAGS" + + CFLAGS="$3 $2" + AC_CACHE_CHECK([whether CC supports $2], $1, + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], [$1=yes], [$1=no])]) + + CFLAGS="$bird_tmp_cflags" +]) + +AC_DEFUN(BIRD_ADD_GCC_OPTION, +[ + if test "$$1" = yes ; then + CFLAGS="$CFLAGS $2" + fi +]) # BIRD_CHECK_PROG_FLAVOR_GNU(PROGRAM-PATH, IF-SUCCESS, [IF-FAILURE]) # copied autoconf internal _AC_PATH_PROG_FLAVOR_GNU diff --git a/configure.in b/configure.in index 54993dfc..272a2431 100644 --- a/configure.in +++ b/configure.in @@ -87,15 +87,17 @@ if test -z "$GCC" ; then AC_MSG_ERROR([This program requires the GNU C Compiler.]) fi -AC_MSG_CHECKING([what CFLAGS should we use]) if test "$bird_cflags_default" = yes ; then - BIRD_CHECK_GCC_OPTIONS + BIRD_CHECK_GCC_OPTION(bird_cv_c_option_wno_pointer_sign, -Wno-pointer-sign, -Wall) + BIRD_CHECK_GCC_OPTION(bird_cv_c_option_fno_strict_aliasing, -fno-strict-aliasing) + BIRD_CHECK_GCC_OPTION(bird_cv_c_option_fno_strict_overflow, -fno-strict-overflow) CFLAGS="$CFLAGS -Wall -Wstrict-prototypes -Wno-parentheses" - if test "$bird_cv_c_option_no_pointer_sign" = yes ; then - CFLAGS="$CFLAGS -Wno-pointer-sign" - fi + BIRD_ADD_GCC_OPTION(bird_cv_c_option_wno_pointer_sign, -Wno-pointer-sign) + BIRD_ADD_GCC_OPTION(bird_cv_c_option_fno_strict_aliasing, -fno-strict-aliasing) + BIRD_ADD_GCC_OPTION(bird_cv_c_option_fno_strict_overflow, -fno-strict-overflow) fi +AC_MSG_CHECKING([CFLAGS]) AC_MSG_RESULT($CFLAGS) -- cgit v1.2.3 From d2c392d44839baaefa48f4a38060be648d3415fb Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 19 Apr 2013 13:59:08 +0200 Subject: Removes unnecessary client subdirectories and updates buildsystem. Renames some files: birdc/client.c -> birdc.c birdcl/client.c -> birdcl.c client_common.c -> common.c --- client/Makefile | 8 +- client/birdc/Makefile | 5 - client/birdc/client.c | 430 ------------------------------------------------- client/birdcl/Makefile | 5 - client/birdcl/client.c | 429 ------------------------------------------------ client/client.h | 2 +- client/client_common.c | 179 -------------------- tools/Makefile.in | 14 +- tools/Rules.in | 14 +- 9 files changed, 24 insertions(+), 1062 deletions(-) delete mode 100644 client/birdc/Makefile delete mode 100644 client/birdc/client.c delete mode 100644 client/birdcl/Makefile delete mode 100644 client/birdcl/client.c delete mode 100644 client/client_common.c diff --git a/client/Makefile b/client/Makefile index 3568833e..8c2f91e0 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,5 +1,11 @@ -source=commands.c util.c client_common.c +source=commands.c util.c common.c root-rel=../ dir-name=client +clients := $(client) birdcl + +source-dep := $(source) $(addsuffix .c,$(clients)) + +subdir: $(addsuffix .o,$(clients)) + include ../Rules diff --git a/client/birdc/Makefile b/client/birdc/Makefile deleted file mode 100644 index 154a8d20..00000000 --- a/client/birdc/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -source=client.c -root-rel=../../ -dir-name=client/birdc - -include ../../Rules diff --git a/client/birdc/client.c b/client/birdc/client.c deleted file mode 100644 index 9b4dd2fb..00000000 --- a/client/birdc/client.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * BIRD Client - * - * (c) 1999--2004 Martin Mares - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nest/bird.h" -#include "lib/resource.h" -#include "lib/string.h" -#include "client/client.h" -#include "sysdep/unix/unix.h" - -static char *opt_list = "s:vr"; -static int verbose; -static char *init_cmd; -static int once; -static int restricted; - -extern char *server_path; -extern int server_fd; -extern byte server_read_buf[SERVER_READ_BUF_LEN]; -extern byte *server_read_pos; - -extern int input_initialized; -extern int input_hidden_end; -extern int cstate; -extern int nstate; - -extern int num_lines, skip_input, interactive; - -/*** Parsing of arguments ***/ - -static void -usage(void) -{ - fprintf(stderr, "Usage: birdc [-s ] [-v] [-r]\n"); - exit(1); -} - -static void -parse_args(int argc, char **argv) -{ - int c; - - while ((c = getopt(argc, argv, opt_list)) >= 0) - switch (c) - { - case 's': - server_path = optarg; - break; - case 'v': - verbose++; - break; - case 'r': - restricted = 1; - break; - default: - usage(); - } - - /* If some arguments are not options, we take it as commands */ - if (optind < argc) - { - char *tmp; - int i; - int len = 0; - - for (i = optind; i < argc; i++) - len += strlen(argv[i]) + 1; - - tmp = init_cmd = malloc(len); - for (i = optind; i < argc; i++) - { - strcpy(tmp, argv[i]); - tmp += strlen(tmp); - *tmp++ = ' '; - } - tmp[-1] = 0; - - once = 1; - } -} - -/*** Input ***/ - -/* HACK: libreadline internals we need to access */ -extern int _rl_vis_botlin; -extern void _rl_move_vert(int); -extern Function *rl_last_func; - -static void -add_history_dedup(char *cmd) -{ - /* Add history line if it differs from the last one */ - HIST_ENTRY *he = history_get(history_length); - if (!he || strcmp(he->line, cmd)) - add_history(cmd); -} - -void got_line(char *cmd_buffer) -{ - char *cmd; - - if (!cmd_buffer) - { - cleanup(); - exit(0); - } - if (cmd_buffer[0]) - { - cmd = cmd_expand(cmd_buffer); - if (cmd) - { - add_history_dedup(cmd); - - if (!handle_internal_command(cmd)) - submit_server_command(cmd); - - free(cmd); - } - else - add_history_dedup(cmd_buffer); - } - free(cmd_buffer); -} - -void -input_start_list(void) /* Leave the currently edited line and make space for listing */ -{ - _rl_move_vert(_rl_vis_botlin); -#ifdef HAVE_RL_CRLF - rl_crlf(); -#endif -} - -void -input_stop_list(void) /* Reprint the currently edited line after listing */ -{ - rl_on_new_line(); - rl_redisplay(); -} - -static int -input_complete(int arg UNUSED, int key UNUSED) -{ - static int complete_flag; - char buf[256]; - - if (rl_last_func != input_complete) - complete_flag = 0; - switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) - { - case 0: - complete_flag = 1; - break; - case 1: - rl_insert_text(buf); - break; - default: - complete_flag = 1; -#ifdef HAVE_RL_DING - rl_ding(); -#endif - } - return 0; -} - -static int -input_help(int arg, int key UNUSED) -{ - int i, in_string, in_bracket; - - if (arg != 1) - return rl_insert(arg, '?'); - - in_string = in_bracket = 0; - for (i = 0; i < rl_point; i++) - { - - if (rl_line_buffer[i] == '"') - in_string = ! in_string; - else if (! in_string) - { - if (rl_line_buffer[i] == '[') - in_bracket++; - else if (rl_line_buffer[i] == ']') - in_bracket--; - } - } - - /* `?' inside string or path -> insert */ - if (in_string || in_bracket) - return rl_insert(1, '?'); - - rl_begin_undo_group(); /* HACK: We want to display `?' at point position */ - rl_insert_text("?"); - rl_redisplay(); - rl_end_undo_group(); - input_start_list(); - cmd_help(rl_line_buffer, rl_point); - rl_undo_command(1, 0); - input_stop_list(); - return 0; -} - -static void -input_init(void) -{ - rl_readline_name = "birdc"; - rl_add_defun("bird-complete", input_complete, '\t'); - rl_add_defun("bird-help", input_help, '?'); - rl_callback_handler_install("bird> ", got_line); - input_initialized = 1; -// readline library does strange things when stdin is nonblocking. -// if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) -// die("fcntl: %m"); -} - -static void -input_hide(void) -{ - input_hidden_end = rl_end; - rl_end = 0; - rl_expand_prompt(""); - rl_redisplay(); -} - -static void -input_reveal(void) -{ - /* need this, otherwise some lib seems to eat pending output when - the prompt is displayed */ - fflush(stdout); - tcdrain(fileno(stdout)); - - rl_end = input_hidden_end; - rl_expand_prompt("bird> "); - rl_forced_update_display(); -} - -void -update_state(void) -{ - if (nstate == cstate) - return; - - if (restricted) - { - submit_server_command("restrict"); - restricted = 0; - return; - } - - if (init_cmd) - { - /* First transition - client received hello from BIRD - and there is waiting initial command */ - submit_server_command(init_cmd); - init_cmd = NULL; - return; - } - - if (!init_cmd && once) - { - /* Initial command is finished and we want to exit */ - cleanup(); - exit(0); - } - - if (nstate == STATE_PROMPT) - { - if (input_initialized) - input_reveal(); - else - input_init(); - } - - if (nstate != STATE_PROMPT) - input_hide(); - - cstate = nstate; -} - -void -more(void) -{ - printf("--More--\015"); - fflush(stdout); - - redo: - switch (getchar()) - { - case 32: - num_lines = 2; - break; - case 13: - num_lines--; - break; - case 'q': - skip_input = 1; - break; - default: - goto redo; - } - - printf(" \015"); - fflush(stdout); -} - -void cleanup(void) -{ - if (input_initialized) - { - input_initialized = 0; - input_hide(); - rl_callback_handler_remove(); - } -} - - -/*** Communication with server ***/ - -static fd_set select_fds; - -static void -select_loop(void) -{ - int rv; - while (1) - { - FD_ZERO(&select_fds); - - if (cstate != STATE_CMD_USER) - FD_SET(server_fd, &select_fds); - if (cstate != STATE_CMD_SERVER) - FD_SET(0, &select_fds); - - rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); - if (rv < 0) - { - if (errno == EINTR) - continue; - else - die("select: %m"); - } - - if (FD_ISSET(server_fd, &select_fds)) - { - server_read(); - update_state(); - } - - if (FD_ISSET(0, &select_fds)) - { - rl_callback_read_char(); - update_state(); - } - } -} - -#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) - -void server_got_reply(char *x) -{ - int code; - int len = 0; - - if (*x == '+') /* Async reply */ - PRINTF(len, ">>> %s\n", x+1); - else if (x[0] == ' ') /* Continuation */ - PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); - else if (strlen(x) > 4 && - sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && - (x[4] == ' ' || x[4] == '-')) - { - if (code) - PRINTF(len, "%s\n", verbose ? x : x+5); - if (x[4] == ' ') - { - nstate = STATE_PROMPT; - skip_input = 0; - return; - } - } - else - PRINTF(len, "??? <%s>\n", x); - - if (skip_input) - return; - - if (interactive && input_initialized && (len > 0)) - { - int lns = LINES ? LINES : 25; - int cls = COLS ? COLS : 80; - num_lines += (len + cls - 1) / cls; /* Divide and round up */ - if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) - more(); - } -} - -int -main(int argc, char **argv) -{ -#ifdef HAVE_LIBDMALLOC - if (!getenv("DMALLOC_OPTIONS")) - dmalloc_debug(0x2f03d00); -#endif - - interactive = isatty(0); - parse_args(argc, argv); - cmd_build_tree(); - server_connect(); - select_loop(); - return 0; -} diff --git a/client/birdcl/Makefile b/client/birdcl/Makefile deleted file mode 100644 index fcdc5eb2..00000000 --- a/client/birdcl/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -source=client.c -root-rel=../../ -dir-name=client/birdcl - -include ../../Rules diff --git a/client/birdcl/client.c b/client/birdcl/client.c deleted file mode 100644 index 1e920ce8..00000000 --- a/client/birdcl/client.c +++ /dev/null @@ -1,429 +0,0 @@ -/* - * BIRD Client - * - * (c) 1999--2004 Martin Mares - * (c) 2013 Tomas Hlavacek - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nest/bird.h" -#include "lib/resource.h" -#include "lib/string.h" -#include "client/client.h" -#include "sysdep/unix/unix.h" - -#define INPUT_BUF_LEN 2048 - -static char *opt_list = "s:vr"; -static int verbose; -static char *init_cmd; -static int once; - -extern char *server_path; -extern int server_fd; - -extern int cstate; -extern int num_lines, skip_input, interactive; - -static int term_lns=25; -static int term_cls=80; -struct termios tty_save; - -void -input_start_list(void) -{ - /* Empty in non-ncurses version. */ -} - -void -input_stop_list(void) -{ - /* Empty in non-ncurses version. */ -} - -/*** Parsing of arguments ***/ - -static void -usage(void) -{ - fprintf(stderr, "Usage: birdc [-s ] [-v] [-r]\n"); - exit(1); -} - -static void -parse_args(int argc, char **argv) -{ - int c; - - while ((c = getopt(argc, argv, opt_list)) >= 0) - switch (c) - { - case 's': - server_path = optarg; - break; - case 'v': - verbose++; - break; - case 'r': - init_cmd = "restrict"; - break; - default: - usage(); - } - - /* If some arguments are not options, we take it as commands */ - if (optind < argc) - { - char *tmp; - int i; - int len = 0; - - if (init_cmd) - usage(); - - for (i = optind; i < argc; i++) - len += strlen(argv[i]) + 1; - - tmp = init_cmd = malloc(len); - for (i = optind; i < argc; i++) - { - strcpy(tmp, argv[i]); - tmp += strlen(tmp); - *tmp++ = ' '; - } - tmp[-1] = 0; - - once = 1; - } -} - -static void -run_init_cmd(void) -{ - if (init_cmd) - { - /* First transition - client received hello from BIRD - and there is waiting initial command */ - submit_server_command(init_cmd); - init_cmd = NULL; - return; - } - - if (!init_cmd && once && (cstate == STATE_PROMPT)) - { - /* Initial command is finished and we want to exit */ - cleanup(); - exit(0); - } -} - -/*** Input ***/ - -static void -got_line(char *cmd_buffer) -{ - char *cmd; - - if (!cmd_buffer) - { - cleanup(); - exit(0); - } - if (cmd_buffer[0]) - { - cmd = cmd_expand(cmd_buffer); - if (cmd) - { - if (!handle_internal_command(cmd)) - submit_server_command(cmd); - - free(cmd); - } - } - free(cmd_buffer); -} - -void -cleanup(void) -{ - /* No ncurses -> restore terminal state. */ - if (interactive) - if (tcsetattr (0, TCSANOW, &tty_save) != 0) - { - perror("tcsetattr error"); - exit(EXIT_FAILURE); - } -} - -static void -print_prompt(void) -{ - /* No ncurses -> no status to reveal/hide, print prompt manually. */ - printf("bird> "); - fflush(stdout); -} - - -static int lastnb(char *str) -{ - int i; - for (i=strlen(str)-1; i>0; i--) - { - if(!isblank(str[i])) - return i; - } - - return 0; -} - -static void -term_read(void) -{ - char *buf = malloc(INPUT_BUF_LEN); - - if (fgets(buf, INPUT_BUF_LEN, stdin) == NULL) { - free(buf); - exit(0); - } - - if (buf[strlen(buf)-1] != '\n') - { - printf("Input too long.\n"); - free(buf); - return; - } - - if (strlen(buf) <= 0) - { - free(buf); - return; - } - - buf[strlen(buf)-1] = '\0'; - - if (!interactive) - { - print_prompt(); - printf("%s\n",buf); - } - - if (buf[lastnb(buf)] == '?') - { - printf("\n"); - cmd_help(buf, strlen(buf)); - free(buf); - return; - } - - if (strlen(buf) > 0) - { - got_line(buf); /* buf is free()-ed inside */ - } - else - { - free(buf); /* no command, only newline -> no-op */ - return; - } - -} - -/*** Communication with server ***/ - -void -more(void) -{ - struct termios tty; - - printf("--More--\015"); - fflush(stdout); - - if (tcgetattr(0, &tty) != 0) - { - perror("tcgetattr error"); - exit(EXIT_FAILURE); - } - tty.c_lflag &= (~ECHO); - tty.c_lflag &= (~ICANON); - if (tcsetattr (0, TCSANOW, &tty) != 0) - { - perror("tcsetattr error"); - exit(EXIT_FAILURE); - } - - redo: - switch (getchar()) - { - case 32: - num_lines = 2; - break; - case 13: - num_lines--; - break; - case '\n': - num_lines--; - break; - case 'q': - skip_input = 1; - break; - default: - goto redo; - } - - tty.c_lflag |= ECHO; - tty.c_lflag |= ICANON; - if (tcsetattr (0, TCSANOW, &tty) != 0) - { - perror("tcsetattr error"); - exit(EXIT_FAILURE); - } - - printf(" \015"); - fflush(stdout); -} - -static void -get_term_size(void) -{ - struct winsize tws; - if (ioctl(0, TIOCGWINSZ, &tws) == 0) - { - term_lns = tws.ws_row; - term_cls = tws.ws_col; - } - else - { - term_lns = 25; - term_cls = 80; - } -} - -#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) - -void -server_got_reply(char *x) -{ - int code; - int len = 0; - - if (*x == '+') /* Async reply */ - PRINTF(len, ">>> %s\n", x+1); - else if (x[0] == ' ') /* Continuation */ - PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); - else if (strlen(x) > 4 && - sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && - (x[4] == ' ' || x[4] == '-')) - { - if (code) - PRINTF(len, "%s\n", verbose ? x : x+5); - - if (x[4] == ' ') - { - cstate = STATE_PROMPT; - skip_input = 0; - return; - } - } - else - PRINTF(len, "??? <%s>\n", x); - - if (skip_input) - return; - - if (interactive && (len > 0)) - { - num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */ - if (num_lines >= term_lns) - more(); - } -} - -static fd_set select_fds; - -static void -select_loop(void) -{ - int rv; - - while (1) - { - FD_ZERO(&select_fds); - - if (cstate != STATE_CMD_USER) - FD_SET(server_fd, &select_fds); - - if (cstate != STATE_CMD_SERVER) - { - FD_SET(0, &select_fds); - if (interactive) - print_prompt(); - } - - rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); - if (rv < 0) - { - if (errno == EINTR) - continue; - else - die("select: %m"); - } - - if (FD_ISSET(server_fd, &select_fds)) - { - server_read(); - run_init_cmd(); - } - - if (FD_ISSET(0, &select_fds)) - term_read(); - } -} - -static void -sig_handler(int signal) -{ - cleanup(); - exit(0); -} - -int -main(int argc, char **argv) -{ - interactive = isatty(fileno(stdin)); - if (interactive) - { - if (signal(SIGINT, sig_handler) == SIG_IGN) - signal(SIGINT, SIG_IGN); - if (signal(SIGHUP, sig_handler) == SIG_IGN) - signal(SIGHUP, SIG_IGN); - if (signal(SIGTERM, sig_handler) == SIG_IGN) - signal(SIGTERM, SIG_IGN); - - get_term_size(); - - if (tcgetattr(0, &tty_save) != 0) - { - perror("tcgetattr error"); - return(EXIT_FAILURE); - } - } - - parse_args(argc, argv); - cmd_build_tree(); - server_connect(); - select_loop(); - return 0; -} diff --git a/client/client.h b/client/client.h index 2d215059..2e4e2ea3 100644 --- a/client/client.h +++ b/client/client.h @@ -20,7 +20,7 @@ void cmd_help(char *cmd, int len); int cmd_complete(char *cmd, int len, char *buf, int again); char *cmd_expand(char *cmd); -/* client_common.c */ +/* common.c */ #define STATE_PROMPT 0 #define STATE_CMD_SERVER 1 diff --git a/client/client_common.c b/client/client_common.c deleted file mode 100644 index 5f0a36dd..00000000 --- a/client/client_common.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * BIRD Client - * - * (c) 1999--2004 Martin Mares - * (c) 2013 Tomas Hlavacek - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nest/bird.h" -#include "lib/resource.h" -#include "lib/string.h" -#include "client/client.h" -#include "sysdep/unix/unix.h" - -char *server_path = PATH_CONTROL_SOCKET; -int server_fd; -byte server_read_buf[SERVER_READ_BUF_LEN]; -byte *server_read_pos = server_read_buf; - -int input_initialized; -int input_hidden_end; -int cstate = STATE_CMD_SERVER; -int nstate = STATE_CMD_SERVER; - -int num_lines, skip_input, interactive; - - -/*** Input ***/ - -int -handle_internal_command(char *cmd) -{ - if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4)) - { - cleanup(); - exit(0); - } - if (!strncmp(cmd, "help", 4)) - { - puts("Press `?' for context sensitive help."); - return 1; - } - return 0; -} - -void -submit_server_command(char *cmd) -{ - server_send(cmd); - nstate = STATE_CMD_SERVER; - num_lines = 2; -} - -/*** Communication with server ***/ - -void -server_connect(void) -{ - struct sockaddr_un sa; - - server_fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (server_fd < 0) - die("Cannot create socket: %m"); - - if (strlen(server_path) >= sizeof(sa.sun_path)) - die("server_connect: path too long"); - - bzero(&sa, sizeof(sa)); - sa.sun_family = AF_UNIX; - strcpy(sa.sun_path, server_path); - if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) - die("Unable to connect to server control socket (%s): %m", server_path); - if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) - die("fcntl: %m"); -} - -void -server_read(void) -{ - int c; - byte *start, *p; - - redo: - c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); - if (!c) - die("Connection closed by server."); - if (c < 0) - { - if (errno == EINTR) - goto redo; - else - die("Server read error: %m"); - } - - start = server_read_buf; - p = server_read_pos; - server_read_pos += c; - while (p < server_read_pos) - if (*p++ == '\n') - { - p[-1] = 0; - server_got_reply(start); - start = p; - } - if (start != server_read_buf) - { - int l = server_read_pos - start; - memmove(server_read_buf, start, l); - server_read_pos = server_read_buf + l; - } - else if (server_read_pos == server_read_buf + sizeof(server_read_buf)) - { - strcpy(server_read_buf, "?"); - server_read_pos = server_read_buf + 11; - } -} - -void -wait_for_write(int fd) -{ - while (1) - { - int rv; - fd_set set; - FD_ZERO(&set); - FD_SET(fd, &set); - - rv = select(fd+1, NULL, &set, NULL, NULL); - if (rv < 0) - { - if (errno == EINTR) - continue; - else - die("select: %m"); - } - - if (FD_ISSET(server_fd, &set)) - return; - } -} - -void -server_send(char *cmd) -{ - int l = strlen(cmd); - byte *z = alloca(l + 1); - - memcpy(z, cmd, l); - z[l++] = '\n'; - while (l) - { - int cnt = write(server_fd, z, l); - - if (cnt < 0) - { - if (errno == EAGAIN) - wait_for_write(server_fd); - else if (errno == EINTR) - continue; - else - die("Server write error: %m"); - } - else - { - l -= cnt; - z += cnt; - } - } -} diff --git a/tools/Makefile.in b/tools/Makefile.in index adf35b64..feb83b9f 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -3,7 +3,7 @@ include Rules -.PHONY: all daemon birdci birdcl subdir depend clean distclean tags docs userdocs progdocs +.PHONY: all daemon birdc birdcl subdir depend clean distclean tags docs userdocs progdocs all: sysdep/paths.h .dep-stamp subdir daemon birdcl @CLIENT@ @@ -17,22 +17,24 @@ bird-dep := $(addsuffix /all.o, $(static-dirs)) conf/all.o lib/birdlib.a $(bird-dep): sysdep/paths.h .dep-stamp subdir -birdc-dep := client/birdc/all.o client/all.o lib/birdlib.a +birdc-dep := client/birdc.o client/all.o lib/birdlib.a $(birdc-dep): sysdep/paths.h .dep-stamp subdir -birdcl-dep := client/birdcl/all.o client/all.o lib/birdlib.a +birdcl-dep := client/birdcl.o client/all.o lib/birdlib.a $(birdcl-dep): sysdep/paths.h .dep-stamp subdir +export client := @CLIENT@ + depend: sysdep/paths.h .dir-stamp set -e ; for a in $(dynamic-dirs) ; do $(MAKE) -C $$a $@ ; done - set -e ; for a in $(static-dirs) $(birdcl-dirs) $(birdc-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done + set -e ; for a in $(static-dirs) $(client-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done subdir: sysdep/paths.h .dir-stamp .dep-stamp set -e ; for a in $(dynamic-dirs) ; do $(MAKE) -C $$a $@ ; done - set -e ; for a in $(static-dirs) $(birdcl-dirs) $(birdc-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done + set -e ; for a in $(static-dirs) $(client-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done $(exedir)/bird: $(bird-dep) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) @@ -44,7 +46,7 @@ $(exedir)/birdcl: $(birdcl-dep) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) .dir-stamp: sysdep/paths.h - mkdir -p $(static-dirs) $(birdcl-dirs) $(birdc-dirs) $(doc-dirs) + mkdir -p $(static-dirs) $(client-dirs) $(doc-dirs) touch .dir-stamp .dep-stamp: diff --git a/tools/Rules.in b/tools/Rules.in index fd10f5de..ca930ec8 100644 --- a/tools/Rules.in +++ b/tools/Rules.in @@ -11,14 +11,12 @@ static-dirs := nest filter $(addprefix proto/,$(protocols)) static-dir-paths := $(addprefix $(srcdir)/,$(static-dirs)) dynamic-dirs := lib conf dynamic-dir-paths := $(dynamic-dirs) -birdc-dirs := client client/@CLIENT@ -birdc-dir-paths := $(birdc-dirs) -birdcl-dirs := client client/birdcl -birdcl-dir-paths := $(birdcl-dirs) +client-dirs := client +client-dir-paths := $(client-dirs) doc-dirs := doc doc-dir-paths := $(doc-dirs) -all-dirs:=$(static-dirs) $(dynamic-dirs) $(birdc-dirs) $(doc-dirs) +all-dirs:=$(static-dirs) $(dynamic-dirs) $(client-dirs) $(doc-dirs) clean-dirs:=$(all-dirs) proto sysdep CPPFLAGS=-I$(root-rel) -I$(srcdir) @CPPFLAGS@ @@ -77,8 +75,12 @@ endif %.o: $(src-path)%.c $(CC) $(CFLAGS) -o $@ -c $< +ifndef source-dep +source-dep := $(source) +endif + depend: - $(CC) $(CPPFLAGS) -MM $(addprefix $(src-path),$(source)) >depend + $(CC) $(CPPFLAGS) -MM $(addprefix $(src-path),$(source-dep)) >depend ifneq ($(wildcard depend),) include depend -- cgit v1.2.3 From a5e9f3d26f887deb451a3ea086e52266c117aa0a Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 23 Apr 2013 02:42:35 +0200 Subject: Restructures birdc and birdcl to merge duplicated code. The BIRD client code is restructured that most of the code (including main function) is shared in client.c, while birdc.c and birdcl.c contain just I/O-specific callbacks. This removes all duplicated code from variant-specific files. --- client/Makefile | 2 +- client/birdc.c | 223 +++++++++++++++++++++++++++++ client/birdcl.c | 165 +++++++++++++++++++++ client/client.c | 436 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ client/client.h | 31 ++-- doc/bird.sgml | 8 +- 6 files changed, 848 insertions(+), 17 deletions(-) create mode 100644 client/birdc.c create mode 100644 client/birdcl.c create mode 100644 client/client.c diff --git a/client/Makefile b/client/Makefile index 8c2f91e0..a1578766 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,4 +1,4 @@ -source=commands.c util.c common.c +source=commands.c util.c client.c root-rel=../ dir-name=client diff --git a/client/birdc.c b/client/birdc.c new file mode 100644 index 00000000..9dd6d9b9 --- /dev/null +++ b/client/birdc.c @@ -0,0 +1,223 @@ +/* + * BIRD Client - Readline variant I/O + * + * (c) 1999--2004 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +static int input_hidden_end; +static int prompt_active; + +/*** Input ***/ + +/* HACK: libreadline internals we need to access */ +extern int _rl_vis_botlin; +extern void _rl_move_vert(int); +extern Function *rl_last_func; + +static void +add_history_dedup(char *cmd) +{ + /* Add history line if it differs from the last one */ + HIST_ENTRY *he = history_get(history_length); + if (!he || strcmp(he->line, cmd)) + add_history(cmd); +} + +static void +input_got_line(char *cmd_buffer) +{ + if (!cmd_buffer) + { + cleanup(); + exit(0); + } + + if (cmd_buffer[0]) + { + add_history_dedup(cmd_buffer); + submit_command(cmd_buffer); + } + + free(cmd_buffer); +} + +void +input_start_list(void) +{ + /* Leave the currently edited line and make space for listing */ + _rl_move_vert(_rl_vis_botlin); +#ifdef HAVE_RL_CRLF + rl_crlf(); +#endif +} + +void +input_stop_list(void) +{ + /* Reprint the currently edited line after listing */ + rl_on_new_line(); + rl_redisplay(); +} + +static int +input_complete(int arg UNUSED, int key UNUSED) +{ + static int complete_flag; + char buf[256]; + + if (rl_last_func != input_complete) + complete_flag = 0; + switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) + { + case 0: + complete_flag = 1; + break; + case 1: + rl_insert_text(buf); + break; + default: + complete_flag = 1; +#ifdef HAVE_RL_DING + rl_ding(); +#endif + } + return 0; +} + +static int +input_help(int arg, int key UNUSED) +{ + int i, in_string, in_bracket; + + if (arg != 1) + return rl_insert(arg, '?'); + + in_string = in_bracket = 0; + for (i = 0; i < rl_point; i++) + { + + if (rl_line_buffer[i] == '"') + in_string = ! in_string; + else if (! in_string) + { + if (rl_line_buffer[i] == '[') + in_bracket++; + else if (rl_line_buffer[i] == ']') + in_bracket--; + } + } + + /* `?' inside string or path -> insert */ + if (in_string || in_bracket) + return rl_insert(1, '?'); + + rl_begin_undo_group(); /* HACK: We want to display `?' at point position */ + rl_insert_text("?"); + rl_redisplay(); + rl_end_undo_group(); + input_start_list(); + cmd_help(rl_line_buffer, rl_point); + rl_undo_command(1, 0); + input_stop_list(); + return 0; +} + +void +input_init(void) +{ + rl_readline_name = "birdc"; + rl_add_defun("bird-complete", input_complete, '\t'); + rl_add_defun("bird-help", input_help, '?'); + rl_callback_handler_install("bird> ", input_got_line); + + // rl_get_screen_size(); + term_lns = LINES ? LINES : 25; + term_cls = COLS ? COLS : 80; + + prompt_active = 1; + + // readline library does strange things when stdin is nonblocking. + // if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) + // die("fcntl: %m"); +} + +static void +input_reveal(void) +{ + /* need this, otherwise some lib seems to eat pending output when + the prompt is displayed */ + fflush(stdout); + tcdrain(STDOUT_FILENO); + + rl_end = input_hidden_end; + rl_expand_prompt("bird> "); + rl_forced_update_display(); + + prompt_active = 1; +} + +static void +input_hide(void) +{ + input_hidden_end = rl_end; + rl_end = 0; + rl_expand_prompt(""); + rl_redisplay(); + + prompt_active = 0; +} + +void +input_notify(int prompt) +{ + if (prompt == prompt_active) + return; + + if (prompt) + input_reveal(); + else + input_hide(); +} + +void +input_read(void) +{ + rl_callback_read_char(); +} + +void +more_begin(void) +{ +} + +void +more_end(void) +{ +} + +void +cleanup(void) +{ + if (init) + return; + + input_hide(); + rl_callback_handler_remove(); +} diff --git a/client/birdcl.c b/client/birdcl.c new file mode 100644 index 00000000..c41b046c --- /dev/null +++ b/client/birdcl.c @@ -0,0 +1,165 @@ +/* + * BIRD Client - Light variant I/O + * + * (c) 1999--2004 Martin Mares + * (c) 2013 Tomas Hlavacek + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include + +#include +#include + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +#define INPUT_BUF_LEN 2048 + +struct termios tty_save; + +void +input_start_list(void) +{ + /* Empty in non-ncurses version. */ +} + +void +input_stop_list(void) +{ + /* Empty in non-ncurses version. */ +} + +void +input_notify(int prompt) +{ + /* No ncurses -> no status to reveal/hide, print prompt manually. */ + if (!prompt) + return; + + printf("bird> "); + fflush(stdout); +} + + +static int +lastnb(char *str, int i) +{ + while (i--) + if ((str[i] != ' ') && (str[i] != '\t')) + return str[i]; + + return 0; +} + +void +input_read(void) +{ + char buf[INPUT_BUF_LEN]; + + if ((fgets(buf, INPUT_BUF_LEN, stdin) == NULL) || (buf[0] == 0)) + { + putchar('\n'); + cleanup(); + exit(0); + } + + int l = strlen(buf); + if ((l+1) == INPUT_BUF_LEN) + { + printf("Input too long.\n"); + return; + } + + if (buf[l-1] == '\n') + buf[--l] = '\0'; + + if (!interactive) + printf("%s\n", buf); + + if (l == 0) + return; + + if (lastnb(buf, l) == '?') + { + cmd_help(buf, strlen(buf)); + return; + } + + submit_command(buf); +} + +static struct termios stored_tty; +static int more_active = 0; + +void +more_begin(void) +{ + static struct termios tty; + + tty = stored_tty; + tty.c_lflag &= (~ECHO); + tty.c_lflag &= (~ICANON); + + if (tcsetattr (0, TCSANOW, &tty) < 0) + die("tcsetattr: %m"); + + more_active = 1; +} + +void +more_end(void) +{ + more_active = 0; + + if (tcsetattr (0, TCSANOW, &stored_tty) < 0) + die("tcsetattr: %m"); +} + +static void +sig_handler(int signal) +{ + cleanup(); + exit(0); +} + +void +input_init(void) +{ + if (!interactive) + return; + + if (tcgetattr(0, &stored_tty) < 0) + die("tcgetattr: %m"); + + if (signal(SIGINT, sig_handler) == SIG_IGN) + signal(SIGINT, SIG_IGN); + if (signal(SIGTERM, sig_handler) == SIG_IGN) + signal(SIGTERM, SIG_IGN); + + struct winsize tws; + if (ioctl(0, TIOCGWINSZ, &tws) == 0) + { + term_lns = tws.ws_row; + term_cls = tws.ws_col; + } + else + { + term_lns = 25; + term_cls = 80; + } +} + +void +cleanup(void) +{ + if (more_active) + more_end(); +} diff --git a/client/client.c b/client/client.c new file mode 100644 index 00000000..61caf38b --- /dev/null +++ b/client/client.c @@ -0,0 +1,436 @@ +/* + * BIRD Client + * + * (c) 1999--2004 Martin Mares + * (c) 2013 Tomas Hlavacek + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +/** + * DOC: BIRD client + * + * There are two variants of BIRD client: regular and light. regular + * variant depends on readline and ncurses libraries, while light + * variant uses just libc. Most of the code and the main() is common + * for both variants (in client.c file) and just a few functions are + * different (in birdc.c for regular and birdcl.c for light). Two + * binaries are generated by linking common object files like client.o + * (which is compiled from client.c just once) with either birdc.o or + * birdcl.o for each variant. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +#define SERVER_READ_BUF_LEN 4096 + +static char *opt_list = "s:vr"; +static int verbose, restricted, once; +static char *init_cmd; + +static char *server_path = PATH_CONTROL_SOCKET; +static int server_fd; +static byte server_read_buf[SERVER_READ_BUF_LEN]; +static byte *server_read_pos = server_read_buf; + +int init = 1; /* During intial sequence */ +int busy = 1; /* Executing BIRD command */ +int interactive; /* Whether stdin is terminal */ + +static int num_lines, skip_input; +int term_lns, term_cls; + + +/*** Parsing of arguments ***/ + +static void +usage(char *name) +{ + fprintf(stderr, "Usage: %s [-s ] [-v] [-r]\n", name); + exit(1); +} + +static void +parse_args(int argc, char **argv) +{ + int c; + + while ((c = getopt(argc, argv, opt_list)) >= 0) + switch (c) + { + case 's': + server_path = optarg; + break; + case 'v': + verbose++; + break; + case 'r': + restricted = 1; + break; + default: + usage(argv[0]); + } + + /* If some arguments are not options, we take it as commands */ + if (optind < argc) + { + char *tmp; + int i; + int len = 0; + + for (i = optind; i < argc; i++) + len += strlen(argv[i]) + 1; + + tmp = init_cmd = malloc(len); + for (i = optind; i < argc; i++) + { + strcpy(tmp, argv[i]); + tmp += strlen(tmp); + *tmp++ = ' '; + } + tmp[-1] = 0; + + once = 1; + interactive = 0; + } +} + + +/*** Input ***/ + +static void server_send(char *cmd); + +static int +handle_internal_command(char *cmd) +{ + if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4)) + { + cleanup(); + exit(0); + } + if (!strncmp(cmd, "help", 4)) + { + puts("Press `?' for context sensitive help."); + return 1; + } + return 0; +} + +static void +submit_server_command(char *cmd) +{ + busy = 1; + num_lines = 2; + server_send(cmd); +} + +void +submit_command(char *cmd_raw) +{ + char *cmd = cmd_expand(cmd_raw); + + if (!cmd) + return; + + if (!handle_internal_command(cmd)) + submit_server_command(cmd); + + free(cmd); +} + +static void +init_commands(void) +{ + if (restricted) + { + submit_server_command("restrict"); + restricted = 0; + return; + } + + if (init_cmd) + { + /* First transition - client received hello from BIRD + and there is waiting initial command */ + submit_server_command(init_cmd); + init_cmd = NULL; + return; + } + + if (once) + { + /* Initial command is finished and we want to exit */ + cleanup(); + exit(0); + } + + input_init(); + init = 0; +} + + +/*** Output ***/ + +void +more(void) +{ + more_begin(); + printf("--More--\015"); + fflush(stdout); + + redo: + switch (getchar()) + { + case ' ': + num_lines = 2; + break; + case '\n': + case '\r': + num_lines--; + break; + case 'q': + skip_input = 1; + break; + default: + goto redo; + } + + printf(" \015"); + fflush(stdout); + more_end(); +} + + +/*** Communication with server ***/ + +static void +server_connect(void) +{ + struct sockaddr_un sa; + + server_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (server_fd < 0) + die("Cannot create socket: %m"); + + if (strlen(server_path) >= sizeof(sa.sun_path)) + die("server_connect: path too long"); + + bzero(&sa, sizeof(sa)); + sa.sun_family = AF_UNIX; + strcpy(sa.sun_path, server_path); + if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) + die("Unable to connect to server control socket (%s): %m", server_path); + if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) + die("fcntl: %m"); +} + + +#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) + +static void +server_got_reply(char *x) +{ + int code; + int len = 0; + + if (*x == '+') /* Async reply */ + PRINTF(len, ">>> %s\n", x+1); + else if (x[0] == ' ') /* Continuation */ + PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); + else if (strlen(x) > 4 && + sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && + (x[4] == ' ' || x[4] == '-')) + { + if (code) + PRINTF(len, "%s\n", verbose ? x : x+5); + + if (x[4] == ' ') + { + busy = 0; + skip_input = 0; + return; + } + } + else + PRINTF(len, "??? <%s>\n", x); + + if (interactive && busy && !skip_input && !init && (len > 0)) + { + num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */ + if (num_lines >= term_lns) + more(); + } +} + +static void +server_read(void) +{ + int c; + byte *start, *p; + + redo: + c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); + if (!c) + die("Connection closed by server."); + if (c < 0) + { + if (errno == EINTR) + goto redo; + else + die("Server read error: %m"); + } + + start = server_read_buf; + p = server_read_pos; + server_read_pos += c; + while (p < server_read_pos) + if (*p++ == '\n') + { + p[-1] = 0; + server_got_reply(start); + start = p; + } + if (start != server_read_buf) + { + int l = server_read_pos - start; + memmove(server_read_buf, start, l); + server_read_pos = server_read_buf + l; + } + else if (server_read_pos == server_read_buf + sizeof(server_read_buf)) + { + strcpy(server_read_buf, "?"); + server_read_pos = server_read_buf + 11; + } +} + +static void +select_loop(void) +{ + int rv; + while (1) + { + if (init && !busy) + init_commands(); + + if (!init) + input_notify(!busy); + + fd_set select_fds; + FD_ZERO(&select_fds); + + FD_SET(server_fd, &select_fds); + if (!busy) + FD_SET(0, &select_fds); + + rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); + if (rv < 0) + { + if (errno == EINTR) + continue; + else + die("select: %m"); + } + + if (FD_ISSET(0, &select_fds)) + { + input_read(); + continue; + } + + if (FD_ISSET(server_fd, &select_fds)) + { + server_read(); + continue; + } + } +} + +static void +wait_for_write(int fd) +{ + while (1) + { + int rv; + fd_set set; + FD_ZERO(&set); + FD_SET(fd, &set); + + rv = select(fd+1, NULL, &set, NULL, NULL); + if (rv < 0) + { + if (errno == EINTR) + continue; + else + die("select: %m"); + } + + if (FD_ISSET(server_fd, &set)) + return; + } +} + +static void +server_send(char *cmd) +{ + int l = strlen(cmd); + byte *z = alloca(l + 1); + + memcpy(z, cmd, l); + z[l++] = '\n'; + while (l) + { + int cnt = write(server_fd, z, l); + + if (cnt < 0) + { + if (errno == EAGAIN) + wait_for_write(server_fd); + else if (errno == EINTR) + continue; + else + die("Server write error: %m"); + } + else + { + l -= cnt; + z += cnt; + } + } +} + + +/* XXXX + + get_term_size(); + + if (tcgetattr(0, &tty_save) != 0) + { + perror("tcgetattr error"); + return(EXIT_FAILURE); + } + } + + */ +int +main(int argc, char **argv) +{ + interactive = isatty(0); + parse_args(argc, argv); + cmd_build_tree(); + server_connect(); + select_loop(); + return 0; +} diff --git a/client/client.h b/client/client.h index 2e4e2ea3..b194a772 100644 --- a/client/client.h +++ b/client/client.h @@ -6,12 +6,23 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -/* client.c callbacks */ -void cleanup(void); +extern int init, busy, interactive; +extern int term_lns, term_cls; + +/* birdc.c / birdcl.c */ + void input_start_list(void); void input_stop_list(void); -void server_got_reply(char *x); + +void input_init(void); +void input_notify(int prompt); +void input_read(void); + +void more_begin(void); +void more_end(void); + +void cleanup(void); /* commands.c */ @@ -20,16 +31,6 @@ void cmd_help(char *cmd, int len); int cmd_complete(char *cmd, int len, char *buf, int again); char *cmd_expand(char *cmd); -/* common.c */ - -#define STATE_PROMPT 0 -#define STATE_CMD_SERVER 1 -#define STATE_CMD_USER 2 - -#define SERVER_READ_BUF_LEN 4096 +/* client.c */ -int handle_internal_command(char *cmd); -void submit_server_command(char *cmd); -void server_connect(void); -void server_read(void); -void server_send(char *cmd); +void submit_command(char *cmd_raw); diff --git a/doc/bird.sgml b/doc/bird.sgml index e83cf0e1..88d35e49 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -623,7 +623,13 @@ codes along with the messages. You do not necessarily need to use -- the format of communication between BIRD and There is also lightweight variant of BIRD client called +Many commands have the Here is a brief list of supported functions: -- cgit v1.2.3 From 572c6440432e3138ea622cfb5a4ef7580d77ef4a Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 29 Apr 2013 22:08:05 +0200 Subject: Fixes a crash when mrtdump is enabled and interface goes away. Thanks to Peter Christensen for the bugfix. --- proto/bgp/packets.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index f2e03f87..bf52c8cd 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -58,7 +58,7 @@ mrt_put_bgp4_hdr(byte *buf, struct bgp_conn *conn, int as4) buf+=4; } - put_u16(buf+0, p->neigh ? p->neigh->iface->index : 0); + put_u16(buf+0, (p->neigh && p->neigh->iface) ? p->neigh->iface->index : 0); put_u16(buf+2, BGP_AF); buf+=4; buf = ipa_put_addr(buf, conn->sk ? conn->sk->daddr : IPA_NONE); -- cgit v1.2.3 From a2017200c71293d0a28a39d1f250ba38d57f6289 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 29 Apr 2013 22:33:50 +0200 Subject: NEWS and version update. --- NEWS | 12 ++++++++++++ misc/bird.spec | 2 +- sysdep/config.h | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 16e1f6d1..a8ed8425 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,15 @@ +Version 1.3.10 (2013-04-30) + o Lightweight BIRD client for embedded environments. + o Dynamic IPv6 router advertisements. + o New 'next hop keep' option for BGP. + o Smart default routing table for 'show route export/preexport/protocol'. + o Automatic router ID selection could be configured to use address of loopback. + o Allows configured global addresses of NBMA neighbors in OSPFv3. + o Allows BIRD commands from UNIX shell even in restricted mode. + o Route limits inherited from templates can be disabled. + o Symbol names enclosed by apostrophes can contain dots. + o Several bugfixes. + Version 1.3.9 (2013-01-11) o BIRD can be configured to keep and show filtered routes. o Separate receive and import limits. diff --git a/misc/bird.spec b/misc/bird.spec index daf4088b..47b0c928 100644 --- a/misc/bird.spec +++ b/misc/bird.spec @@ -1,6 +1,6 @@ Summary: BIRD Internet Routing Daemon Name: bird -Version: 1.3.9 +Version: 1.3.10 Release: 1 Copyright: GPL Group: Networking/Daemons diff --git a/sysdep/config.h b/sysdep/config.h index e97d9e20..3b83d558 100644 --- a/sysdep/config.h +++ b/sysdep/config.h @@ -7,7 +7,7 @@ #define _BIRD_CONFIG_H_ /* BIRD version */ -#define BIRD_VERSION "1.3.9" +#define BIRD_VERSION "1.3.10" /* Include parameters determined by configure script */ #include "sysdep/autoconf.h" -- cgit v1.2.3 From 9c99d753fd672bd9839715ee325ef01cca993dbf Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 9 May 2013 11:11:06 +0200 Subject: Fixes a problem with BGP neighbors, link-local addresses and locking. Thanks to Fritz Grimpen for the bugfix. --- proto/bgp/bgp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 0f351b44..b1594b92 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -840,7 +840,6 @@ bgp_start(struct proto *P) lock->iface = p->cf->iface; lock->type = OBJLOCK_TCP; lock->port = BGP_PORT; - lock->iface = NULL; lock->hook = bgp_start_locked; lock->data = p; olock_acquire(lock); -- cgit v1.2.3 From 9810d055628877232f811d684567e203381e10dc Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 28 May 2013 10:44:44 +0200 Subject: Fixes problems with routing table scans on some platforms. Negative bit shifts are definitely undefined oprations. --- sysdep/linux/netlink.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index d1b203ef..1d24ae0f 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -843,9 +843,11 @@ nl_parse_route(struct nlmsghdr *h, int scan) memcpy(&ra.gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ra.gw)); ipa_ntoh(ra.gw); +#ifdef IPV6 /* Silently skip strange 6to4 routes */ if (ipa_in_net(ra.gw, IPA_NONE, 96)) return; +#endif ng = neigh_find2(&p->p, &ra.gw, ra.iface, (i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0); -- cgit v1.2.3 From 924868543c2010f3ef2cfcb7ba6bac5988ab3264 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 28 May 2013 10:48:14 +0200 Subject: Fixes crash with vlinks. --- proto/ospf/iface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 9050f7b1..3da8f56c 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -537,7 +537,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i #ifdef OSPFv2 ifa->autype = ip->autype; ifa->passwords = ip->passwords; - ifa->ptp_netmask = !(addr->flags & IA_PEER); + ifa->ptp_netmask = addr ? !(addr->flags & IA_PEER) : 0; if (ip->ptp_netmask < 2) ifa->ptp_netmask = ip->ptp_netmask; #endif -- cgit v1.2.3 From f623ab9875cad2d129f708e95021d3a252930000 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 11 Jun 2013 12:12:11 +0200 Subject: Implements OSPF stub router option (RFC 3137). Also fixes OSPFv3 routing table calculcation w.r.t. errata 2078 to RFC 5340. --- doc/bird.sgml | 10 ++++++++++ proto/ospf/config.Y | 1 + proto/ospf/ospf.c | 7 +++++-- proto/ospf/ospf.h | 2 ++ proto/ospf/rt.c | 6 +++++- proto/ospf/topology.c | 8 ++++---- 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 18f3601b..0681bd53 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1904,6 +1904,7 @@ on nonbroadcast networks. protocol ospf <name> { rfc1583compat <switch>; + stub router <switch>; tick <num>; ecmp <switch> [limit <num>]; area <id> { @@ -1983,6 +1984,15 @@ protocol ospf <name> { url="ftp://ftp.rfc-editor.org/in-notes/rfc1583.txt">. Default value is no. + stub router switch + This option configures the router to be a stub router, i.e., + a router that participates in the OSPF topology but does not + allow transit traffic. In OSPFv2, this is implemented by + advertising maximum metric for outgoing links, as suggested + by RFC 3137. + In OSPFv3, the stub router behavior is announced by clearing + the R-bit in the router LSA. Default value is no. + tick num The routing table calculation and clean-up of areas' databases is not performed when a single link state diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 2cc0b963..ba050d85 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -158,6 +158,7 @@ ospf_proto: ospf_proto_item: proto_item | RFC1583COMPAT bool { OSPF_CFG->rfc1583 = $2; } + | STUB ROUTER bool { OSPF_CFG->stub_router = $3; } | ECMP bool { OSPF_CFG->ecmp = $2 ? DEFAULT_ECMP_LIMIT : 0; } | ECMP bool LIMIT expr { OSPF_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); } | TICK expr { OSPF_CFG->tick = $2; if($2<=0) cf_error("Tick must be greater than zero"); } diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index a3b6b2e7..2fa87201 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -167,7 +167,7 @@ ospf_area_add(struct proto_ospf *po, struct ospf_area_config *ac, int reconf) #ifdef OSPFv2 oa->options = ac->type; #else /* OSPFv3 */ - oa->options = OPT_R | ac->type | OPT_V6; + oa->options = ac->type | OPT_V6 | (po->stub_router ? 0 : OPT_R); #endif /* @@ -234,6 +234,7 @@ ospf_start(struct proto *p) po->router_id = proto_get_router_id(p->cf); po->last_vlink_id = 0x80000000; po->rfc1583 = c->rfc1583; + po->stub_router = c->stub_router; po->ebit = 0; po->ecmp = c->ecmp; po->tick = c->tick; @@ -690,7 +691,7 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) #ifdef OSPFv2 oa->options = nac->type; #else /* OSPFv3 */ - oa->options = OPT_R | nac->type | OPT_V6; + oa->options = nac->type | OPT_V6 | (oa->po->stub_router ? 0 : OPT_R); #endif if (oa_is_nssa(oa) && (oa->po->areano > 1)) oa->po->ebit = 1; @@ -738,6 +739,7 @@ ospf_reconfigure(struct proto *p, struct proto_config *c) if (old->abr != new->abr) return 0; + po->stub_router = new->stub_router; po->ecmp = new->ecmp; po->tick = new->tick; po->disp_timer->recurrent = po->tick; @@ -831,6 +833,7 @@ ospf_sh(struct proto *p) cli_msg(-1014, "%s:", p->name); cli_msg(-1014, "RFC1583 compatibility: %s", (po->rfc1583 ? "enable" : "disabled")); + cli_msg(-1014, "Stub router: %s", (po->stub_router ? "Yes" : "No")); cli_msg(-1014, "RT scheduler tick: %d", po->tick); cli_msg(-1014, "Number of areas: %u", po->areano); cli_msg(-1014, "Number of LSAs in DB:\t%u", po->gr->hash_entries); diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index d924e657..7608225f 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -83,6 +83,7 @@ struct ospf_config struct proto_config c; unsigned tick; byte rfc1583; + byte stub_router; byte abr; int ecmp; list area_list; /* list of struct ospf_area_config */ @@ -771,6 +772,7 @@ struct proto_ospf int areano; /* Number of area I belong to */ struct fib rtf; /* Routing table */ byte rfc1583; /* RFC1583 compatibility */ + byte stub_router; /* Do not forward transit traffic */ byte ebit; /* Did I originate any ext lsa? */ byte ecmp; /* Maximal number of nexthops in ECMP route, or 0 */ struct ospf_area *backbone; /* If exists */ diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 4b8de4b8..f509b896 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -501,6 +501,10 @@ ospf_rt_spfa(struct ospf_area *oa) #ifdef OSPFv2 ospf_rt_spfa_rtlinks(oa, act, act); #else /* OSPFv3 */ + /* Errata 2078 to RFC 5340 4.8.1 - skip links from non-routing nodes */ + if ((act != oa->rt) && !(rt->options & OPT_R)) + break; + for (tmp = ospf_hash_find_rt_first(po->gr, act->domain, act->lsa.rt); tmp; tmp = ospf_hash_find_rt_next(tmp)) ospf_rt_spfa_rtlinks(oa, act, tmp); @@ -1839,7 +1843,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, if (en->lsa.type == LSA_T_RT) { struct ospf_lsa_rt *rt = en->lsa_body; - if (!(rt->options & OPT_V6) || !(rt->options & OPT_R)) + if (!(rt->options & OPT_V6)) return; } #endif diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 5f4d1d54..5d93c0e9 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -233,6 +233,7 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length) WALK_LIST(ifa, po->iface_list) { int net_lsa = 0; + u32 link_cost = po->stub_router ? 0xffff : ifa->cost; if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) && (!EMPTY_LIST(ifa->neigh_list))) @@ -268,8 +269,7 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length) * this address as a next-hop. */ ln->data = ipa_to_u32(ifa->addr->ip); - - ln->metric = ifa->cost; + ln->metric = link_cost; ln->padding = 0; i++; } @@ -283,7 +283,7 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length) ln->type = LSART_NET; ln->id = ipa_to_u32(ifa->drip); ln->data = ipa_to_u32(ifa->addr->ip); - ln->metric = ifa->cost; + ln->metric = link_cost; ln->padding = 0; i++; net_lsa = 1; @@ -298,7 +298,7 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length) ln->type = LSART_VLNK; ln->id = neigh->rid; ln->data = ipa_to_u32(ifa->addr->ip); - ln->metric = ifa->cost; + ln->metric = link_cost; ln->padding = 0; i++; } -- cgit v1.2.3 From fad04c750ca6906fb095f1b45958dec0ac8e210c Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 13 Jun 2013 11:27:14 +0200 Subject: Fixes problems with kernel routes multiple routing tables. Temporary dummy routes created by a kernel protocol during routing table scan get mixed with real routes propagated from another kernel protocol through a pipe. --- nest/rt-table.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/nest/rt-table.c b/nest/rt-table.c index e3fd985c..ebdac833 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -906,6 +906,26 @@ rte_update_unlock(void) lp_flush(rte_update_pool); } +static inline void +rte_hide_dummy_routes(net *net, rte **dummy) +{ + if (net->routes && net->routes->attrs->source == RTS_DUMMY) + { + *dummy = net->routes; + net->routes = (*dummy)->next; + } +} + +static inline void +rte_unhide_dummy_routes(net *net, rte **dummy) +{ + if (*dummy) + { + (*dummy)->next = net->routes; + net->routes = *dummy; + } +} + /** * rte_update - enter a new update to a routing table * @table: table to be updated @@ -955,6 +975,7 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src) struct proto_stats *stats = ah->stats; struct filter *filter = ah->in_filter; ea_list *tmpa = NULL; + rte *dummy = NULL; rte_update_lock(); if (new) @@ -1010,14 +1031,18 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src) else stats->imp_withdraws_received++; + recalc: + rte_hide_dummy_routes(net, &dummy); rte_recalculate(ah, net, new, tmpa, src); + rte_unhide_dummy_routes(net, &dummy); rte_update_unlock(); return; -drop: + drop: rte_free(new); - rte_recalculate(ah, net, NULL, NULL, src); - rte_update_unlock(); + new = NULL; + tmpa = NULL; + goto recalc; } /* Independent call to rte_announce(), used from next hop -- cgit v1.2.3 From ef4a50be10c6dd0abffd957132cd146029c3d79d Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 24 Jun 2013 16:37:30 +0200 Subject: Better packet priority and traffic class handling. Implements support for IPv6 traffic class, sets higher priority for OSPF and RIP outgoing packets by default and allows to configure ToS/DS/TClass IP header field and the local priority of outgoing packets. --- doc/bird.sgml | 48 +++++++++++++++++++++++++++++++++++++++++------- lib/ipv6.h | 7 +------ lib/socket.h | 4 +++- nest/config.Y | 8 ++++++-- proto/ospf/config.Y | 6 +++++- proto/ospf/iface.c | 8 ++++++-- proto/ospf/ospf.h | 2 ++ proto/rip/config.Y | 6 +++++- proto/rip/rip.c | 7 +++++-- proto/rip/rip.h | 2 ++ sysdep/bsd/sysio.h | 9 +++++++++ sysdep/linux/sysio.h | 19 +++++++++++++++++++ sysdep/unix/io.c | 11 +++++++++-- 13 files changed, 113 insertions(+), 24 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 0681bd53..7277b2b9 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -567,6 +567,22 @@ to zero to disable it. An empty is equivalent to interface "eth*" 192.168.1.0/24; - start the protocol on all ethernet interfaces that have address from 192.168.1.0/24. +

There are two options that can be specified per-interface. First is metric, with -default one. Second is mode multicast|broadcast|quiet|nolisten|version1, it selects mode for -rip to work in. If nothing is specified, rip runs in multicast mode. version1 is -currently equivalent to broadcast, and it makes RIP talk to a broadcast address even -through multicast mode is possible. quiet option means that RIP will not transmit -any periodic messages to this interface and nolisten means that RIP will send to this -interface but not listen to it. +

There are some options that can be specified per-interface: + + + metric + This option specifies the metric of the interface. Valid + + mode multicast|broadcast|quiet|nolisten|version1 + This option selects the mode for RIP to work in. If nothing is + specified, RIP runs in multicast mode. tx class|dscp|priority + These options specify the ToS/DiffServ/Traffic class/Priority + of the outgoing RIP packets. See common option for detailed description. +

The following options generally override behavior specified in RFC. If you use any of these options, BIRD will no longer be RFC-compliant, which means it will not be able to talk to anything diff --git a/lib/ipv6.h b/lib/ipv6.h index 6f8e7b3c..2247d3fd 100644 --- a/lib/ipv6.h +++ b/lib/ipv6.h @@ -128,11 +128,6 @@ static inline byte * ipv6_put_addr(byte *buf, ip_addr a) return buf+16; } -/* - * RFC 1883 defines packet precendece, but RFC 2460 replaces it - * by generic Traffic Class ID with no defined semantics. Better - * not use it yet. - */ -#define IP_PREC_INTERNET_CONTROL -1 +#define IP_PREC_INTERNET_CONTROL 0xc0 #endif diff --git a/lib/socket.h b/lib/socket.h index 0ee43b52..fbddfb4c 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -20,7 +20,8 @@ typedef struct birdsock { void *data; /* User data */ ip_addr saddr, daddr; /* IPA_NONE = unspecified */ unsigned sport, dport; /* 0 = unspecified (for IP: protocol type) */ - int tos; /* TOS and priority, -1 = default */ + int tos; /* TOS / traffic class, -1 = default */ + int priority; /* Local socket priority, -1 = default */ int ttl; /* Time To Live, -1 = default */ u32 flags; struct iface *iface; /* Interface; specify this for broad/multicast sockets */ @@ -81,6 +82,7 @@ sk_send_buffer_empty(sock *sk) return sk->tbuf == sk->tpos; } +extern int sk_priority_control; /* Suggested priority for control traffic, should be sysdep define */ /* Socket flags */ diff --git a/nest/config.Y b/nest/config.Y index 183059e8..b85a5733 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -48,7 +48,7 @@ CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERE CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, MAX, FLUSH) CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED) -CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC) +CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP) CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE) @@ -65,7 +65,7 @@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID) %type roa_args %type roa_table_arg %type sym_args -%type proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport roa_mode limit_action tab_sorted +%type proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport roa_mode limit_action tab_sorted tos %type proto_patt proto_patt2 %type limit_spec @@ -277,6 +277,10 @@ iface_patt: iface_patt_init iface_patt_list ; +tos: + CLASS expr { $$ = $2 & 0xfc; if (($2 < 0) || ($2 > 255)) cf_error("TX class must be in range 0-255"); } + | DSCP expr { $$ = ($2 & 0x3f) << 2; if (($2 < 0) || ($2 > 63)) cf_error("TX DSCP must be in range 0-63"); } + ; /* Direct device route protocol */ diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index ba050d85..d9379a7c 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -131,7 +131,7 @@ CF_KEYWORDS(NONE, SIMPLE, AUTHENTICATION, STRICT, CRYPTOGRAPHIC) CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, CHECK, LINK) 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) +CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL, NETMASK, TX, PRIORITY) %type opttext %type lsadb_args @@ -305,6 +305,8 @@ ospf_iface_item: | RX BUFFER LARGE { OSPF_PATT->rxbuf = OSPF_RXBUF_LARGE ; } | RX BUFFER NORMAL { OSPF_PATT->rxbuf = OSPF_RXBUF_NORMAL ; } | RX BUFFER expr { OSPF_PATT->rxbuf = $3 ; if (($3 < OSPF_RXBUF_MINSIZE) || ($3 > OSPF_MAX_PKT_SIZE)) cf_error("Buffer size must be in range 256-65535"); } + | TX tos { OSPF_PATT->tx_tos = $2; } + | TX PRIORITY expr { OSPF_PATT->tx_priority = $3; } | password_list ; @@ -367,6 +369,8 @@ ospf_iface_start: init_list(&OSPF_PATT->nbma_list); OSPF_PATT->autype = OSPF_AUTH_NONE; OSPF_PATT->ptp_netmask = 2; /* not specified */ + OSPF_PATT->tx_tos = IP_PREC_INTERNET_CONTROL; + OSPF_PATT->tx_priority = sk_priority_control; reset_passwords(); } ; diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 3da8f56c..bc3b1ef6 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -77,7 +77,8 @@ ospf_sk_open(struct ospf_iface *ifa) sk->dport = OSPF_PROTO; sk->saddr = IPA_NONE; - sk->tos = IP_PREC_INTERNET_CONTROL; + sk->tos = ifa->cf->tx_tos; + sk->priority = ifa->cf->tx_priority; sk->rx_hook = ospf_rx_hook; sk->tx_hook = ospf_tx_hook; sk->err_hook = ospf_err_hook; @@ -659,7 +660,10 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) if (ifa->stub != new_stub) return 0; - if (new->real_bcast != ifa->cf->real_bcast) + /* Change of these options would require to reset the iface socket */ + if ((new->real_bcast != ifa->cf->real_bcast) || + (new->tx_tos != ifa->cf->tx_tos) || + (new->tx_priority != ifa->cf->tx_priority)) return 0; ifa->cf = new; diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 7608225f..56ebcd31 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -800,6 +800,8 @@ struct ospf_iface_patt u32 priority; u32 voa; u32 vid; + int tx_tos; + int tx_priority; u16 rxbuf; #define OSPF_RXBUF_NORMAL 0 #define OSPF_RXBUF_LARGE 1 diff --git a/proto/rip/config.Y b/proto/rip/config.Y index cd4f30e7..ec82aa3d 100644 --- a/proto/rip/config.Y +++ b/proto/rip/config.Y @@ -27,7 +27,7 @@ CF_DECLS CF_KEYWORDS(RIP, INFINITY, METRIC, PORT, PERIOD, GARBAGE, TIMEOUT, MODE, BROADCAST, MULTICAST, QUIET, NOLISTEN, VERSION1, AUTHENTICATION, NONE, PLAINTEXT, MD5, - HONOR, NEVER, NEIGHBOR, ALWAYS, + HONOR, NEVER, NEIGHBOR, ALWAYS, TX, PRIORITY, RIP_METRIC, RIP_TAG) %type rip_mode rip_auth @@ -76,6 +76,8 @@ rip_mode: rip_iface_item: | METRIC expr { RIP_IPATT->metric = $2; } | MODE rip_mode { RIP_IPATT->mode |= $2; } + | TX tos { RIP_IPATT->tx_tos = $2; } + | TX PRIORITY expr { RIP_IPATT->tx_priority = $3; } ; rip_iface_opts: @@ -94,6 +96,8 @@ rip_iface_init: add_tail(&RIP_CFG->iface_list, NODE this_ipatt); init_list(&this_ipatt->ipn_list); RIP_IPATT->metric = 1; + RIP_IPATT->tx_tos = IP_PREC_INTERNET_CONTROL; + RIP_IPATT->tx_priority = sk_priority_control; } ; diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 341df7eb..c09eae79 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -707,7 +707,8 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ if (new) { rif->sock->ttl = 1; - rif->sock->tos = IP_PREC_INTERNET_CONTROL; + rif->sock->tos = PATT->tx_tos; + rif->sock->priority = PATT->tx_priority; rif->sock->flags = SKF_LADDR_RX; } @@ -1007,7 +1008,9 @@ static int rip_pat_compare(struct rip_patt *a, struct rip_patt *b) { return ((a->metric == b->metric) && - (a->mode == b->mode)); + (a->mode == b->mode) && + (a->tx_tos == b->tx_tos) && + (a->tx_priority == b->tx_priority)); } static int diff --git a/proto/rip/rip.h b/proto/rip/rip.h index e0816d0e..2cce8c81 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -128,6 +128,8 @@ struct rip_patt { #define IM_QUIET 4 #define IM_NOLISTEN 8 #define IM_VERSION1 16 + int tx_tos; + int tx_priority; }; struct rip_proto_config { diff --git a/sysdep/bsd/sysio.h b/sysdep/bsd/sysio.h index 4f91def5..085f16fa 100644 --- a/sysdep/bsd/sysio.h +++ b/sysdep/bsd/sysio.h @@ -284,3 +284,12 @@ sk_set_min_ttl6(sock *s, int ttl) #endif + +int sk_priority_control = -1; + +static int +sk_set_priority(sock *s, int prio UNUSED) +{ + log(L_WARN "Socket priority not supported"); + return -1; +} diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index 90b3ebd9..41287e71 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -310,3 +310,22 @@ sk_set_min_ttl6(sock *s, int ttl) } #endif + + +#ifndef IPV6_TCLASS +#define IPV6_TCLASS 67 +#endif + +int sk_priority_control = 7; + +static int +sk_set_priority(sock *s, int prio) +{ + if (setsockopt(s->fd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0) + { + log(L_WARN "sk_set_priority: setsockopt: %m"); + return -1; + } + + return 0; +} diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 80914afe..434a05be 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -598,7 +598,7 @@ sock_new(pool *p) sock *s = ralloc(p, &sk_class); s->pool = p; // s->saddr = s->daddr = IPA_NONE; - s->tos = s->ttl = -1; + s->tos = s->priority = s->ttl = -1; s->fd = -1; return s; } @@ -783,11 +783,18 @@ sk_setup(sock *s) ERR("fcntl(O_NONBLOCK)"); if (s->type == SK_UNIX) return NULL; -#ifndef IPV6 + +#ifdef IPV6 + if ((s->tos >= 0) && setsockopt(fd, SOL_IPV6, IPV6_TCLASS, &s->tos, sizeof(s->tos)) < 0) + WARN("IPV6_TCLASS"); +#else if ((s->tos >= 0) && setsockopt(fd, SOL_IP, IP_TOS, &s->tos, sizeof(s->tos)) < 0) WARN("IP_TOS"); #endif + if (s->priority >= 0) + sk_set_priority(s, s->priority); + #ifdef IPV6 int v = 1; if ((s->flags & SKF_V6ONLY) && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v, sizeof(v)) < 0) -- cgit v1.2.3 From 70e212f913b6ce9d343d6c401b4f1712986a5f8c Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 25 Jun 2013 15:33:00 +0200 Subject: Implements TTL security for OSPF and RIP. Interfaces for OSPF and RIP could be configured to use (and request) TTL 255 for traffic to direct neighbors. Thanks to Simon Dickhoven for the original patch for RIPng. --- lib/socket.h | 1 + proto/ospf/config.Y | 6 +++-- proto/ospf/iface.c | 5 +++-- proto/ospf/ospf.h | 4 +++- proto/ospf/packet.c | 6 +++++ proto/rip/config.Y | 13 +++++++++-- proto/rip/rip.c | 13 +++++++++-- proto/rip/rip.h | 2 ++ sysdep/bsd/sysio.h | 63 ++++++++++++++++++++++++++++++++-------------------- sysdep/linux/sysio.h | 38 +++++++++++++++++++++---------- sysdep/unix/io.c | 44 ++++++++++++++++++++++++++---------- 11 files changed, 138 insertions(+), 57 deletions(-) diff --git a/lib/socket.h b/lib/socket.h index fbddfb4c..6e0a769b 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -89,6 +89,7 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shoul #define SKF_V6ONLY 1 /* Use IPV6_V6ONLY socket option */ #define SKF_LADDR_RX 2 /* Report local address for RX packets */ #define SKF_LADDR_TX 4 /* Allow to specify local address for TX packets */ +#define SKF_TTL_RX 8 /* Report TTL / Hop Limit for RX packets */ /* diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index d9379a7c..f042e1aa 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -127,8 +127,8 @@ CF_KEYWORDS(OSPF, AREA, OSPF_METRIC1, OSPF_METRIC2, OSPF_TAG, OSPF_ROUTER_ID) CF_KEYWORDS(NEIGHBORS, RFC1583COMPAT, STUB, TICK, COST, COST2, RETRANSMIT) CF_KEYWORDS(HELLO, TRANSMIT, PRIORITY, DEAD, TYPE, BROADCAST, BCAST) CF_KEYWORDS(NONBROADCAST, NBMA, POINTOPOINT, PTP, POINTOMULTIPOINT, PTMP) -CF_KEYWORDS(NONE, SIMPLE, AUTHENTICATION, STRICT, CRYPTOGRAPHIC) -CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, CHECK, LINK) +CF_KEYWORDS(NONE, SIMPLE, AUTHENTICATION, STRICT, CRYPTOGRAPHIC, TTL, SECURITY) +CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, CHECK, LINK, ONLY) 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) @@ -307,6 +307,8 @@ ospf_iface_item: | RX BUFFER expr { OSPF_PATT->rxbuf = $3 ; if (($3 < OSPF_RXBUF_MINSIZE) || ($3 > OSPF_MAX_PKT_SIZE)) cf_error("Buffer size must be in range 256-65535"); } | TX tos { OSPF_PATT->tx_tos = $2; } | TX PRIORITY expr { OSPF_PATT->tx_priority = $3; } + | TTL SECURITY bool { OSPF_PATT->ttl_security = $3; } + | TTL SECURITY TX ONLY { OSPF_PATT->ttl_security = 2; } | password_list ; diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index bc3b1ef6..698ef620 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -86,7 +86,7 @@ ospf_sk_open(struct ospf_iface *ifa) sk->rbsize = rxbufsize(ifa); sk->tbsize = rxbufsize(ifa); sk->data = (void *) ifa; - sk->flags = SKF_LADDR_RX; + sk->flags = SKF_LADDR_RX | (ifa->check_ttl ? SKF_TTL_RX : 0); if (sk_open(sk) != 0) goto err; @@ -131,7 +131,7 @@ ospf_sk_open(struct ospf_iface *ifa) else { ifa->all_routers = AllSPFRouters; - sk->ttl = 1; /* Hack, this will affect just multicast packets */ + sk->ttl = ifa->cf->ttl_security ? 255 : 1; if (sk_setup_multicast(sk) < 0) goto err; @@ -534,6 +534,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i ifa->rxbuf = ip->rxbuf; ifa->check_link = ip->check_link; ifa->ecmp_weight = ip->ecmp_weight; + ifa->check_ttl = (ip->ttl_security == 1); #ifdef OSPFv2 ifa->autype = ip->autype; diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 56ebcd31..f1409af3 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -275,6 +275,7 @@ struct ospf_iface u8 check_link; /* Whether iface link change is used */ u8 ecmp_weight; /* Weight used for ECMP */ u8 ptp_netmask; /* Send real netmask for P2P */ + u8 check_ttl; /* Check incoming packets for TTL 255 */ }; struct ospf_md5 @@ -815,7 +816,8 @@ struct ospf_iface_patt u8 check_link; u8 ecmp_weight; u8 real_bcast; /* Not really used in OSPFv3 */ - u8 ptp_netmask; /* bool but 2 for unspecified */ + u8 ptp_netmask; /* bool + 2 for unspecified */ + u8 ttl_security; /* bool + 2 for TX only */ #ifdef OSPFv2 list *passwords; diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index 241a58f7..4338bc1a 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -309,6 +309,12 @@ ospf_rx_hook(sock *sk, int size) return 1; } + if (ifa->check_ttl && (sk->ttl < 255)) + { + log(L_ERR "%s%I - TTL %d (< 255)", mesg, sk->faddr, sk->ttl); + return 1; + } + if ((unsigned) size < sizeof(struct ospf_packet)) { log(L_ERR "%s%I - too short (%u bytes)", mesg, sk->faddr, size); diff --git a/proto/rip/config.Y b/proto/rip/config.Y index ec82aa3d..791c43a2 100644 --- a/proto/rip/config.Y +++ b/proto/rip/config.Y @@ -22,12 +22,18 @@ CF_DEFINES #define RIP_CFG ((struct rip_proto_config *) this_proto) #define RIP_IPATT ((struct rip_patt *) this_ipatt) +#ifdef IPV6 +#define RIP_DEFAULT_TTL_SECURITY 2 +#else +#define RIP_DEFAULT_TTL_SECURITY 0 +#endif + CF_DECLS CF_KEYWORDS(RIP, INFINITY, METRIC, PORT, PERIOD, GARBAGE, TIMEOUT, MODE, BROADCAST, MULTICAST, QUIET, NOLISTEN, VERSION1, - AUTHENTICATION, NONE, PLAINTEXT, MD5, - HONOR, NEVER, NEIGHBOR, ALWAYS, TX, PRIORITY, + AUTHENTICATION, NONE, PLAINTEXT, MD5, TTL, SECURITY, + HONOR, NEVER, NEIGHBOR, ALWAYS, TX, PRIORITY, ONLY, RIP_METRIC, RIP_TAG) %type rip_mode rip_auth @@ -78,6 +84,8 @@ rip_iface_item: | MODE rip_mode { RIP_IPATT->mode |= $2; } | TX tos { RIP_IPATT->tx_tos = $2; } | TX PRIORITY expr { RIP_IPATT->tx_priority = $3; } + | TTL SECURITY bool { RIP_IPATT->ttl_security = $3; } + | TTL SECURITY TX ONLY { RIP_IPATT->ttl_security = 2; } ; rip_iface_opts: @@ -98,6 +106,7 @@ rip_iface_init: RIP_IPATT->metric = 1; RIP_IPATT->tx_tos = IP_PREC_INTERNET_CONTROL; RIP_IPATT->tx_priority = sk_priority_control; + RIP_IPATT->ttl_security = RIP_DEFAULT_TTL_SECURITY; } ; diff --git a/proto/rip/rip.c b/proto/rip/rip.c index c09eae79..3ec070b3 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -480,6 +480,14 @@ rip_rx(sock *s, int size) iface = i->iface; #endif + if (i->check_ttl && (s->ttl < 255)) + { + log( L_REMOTE "%s: Discarding packet with TTL %d (< 255) from %I on %s", + p->name, s->ttl, s->faddr, i->iface->name); + return 1; + } + + CHK_MAGIC; DBG( "RIP: message came: %d bytes from %I via %s\n", size, s->faddr, i->iface ? i->iface->name : "(dummy)" ); size -= sizeof( struct rip_packet_heading ); @@ -686,6 +694,7 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ rif->mode = PATT->mode; rif->metric = PATT->metric; rif->multicast = (!(PATT->mode & IM_BROADCAST)) && (flags & IF_MULTICAST); + rif->check_ttl = (PATT->ttl_security == 1); } /* lookup multicasts over unnumbered links - no: rip is not defined over unnumbered links */ @@ -706,10 +715,10 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ rif->sock->dport = P_CF->port; if (new) { - rif->sock->ttl = 1; rif->sock->tos = PATT->tx_tos; rif->sock->priority = PATT->tx_priority; - rif->sock->flags = SKF_LADDR_RX; + rif->sock->ttl = PATT->ttl_security ? 255 : 1; + rif->sock->flags = SKF_LADDR_RX | (rif->check_ttl ? SKF_TTL_RX : 0); } if (new) { diff --git a/proto/rip/rip.h b/proto/rip/rip.h index 2cce8c81..6e0d5aad 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -114,6 +114,7 @@ struct rip_interface { struct rip_connection *busy; int metric; /* You don't want to put struct rip_patt *patt here -- think about reconfigure */ int mode; + int check_ttl; /* Check incoming packets for TTL 255 */ int triggered; struct object_lock *lock; int multicast; @@ -130,6 +131,7 @@ struct rip_patt { #define IM_VERSION1 16 int tx_tos; int tx_priority; + int ttl_security; /* bool + 2 for TX only (send, but do not check on RX) */ }; struct rip_proto_config { diff --git a/sysdep/bsd/sysio.h b/sysdep/bsd/sysio.h index 085f16fa..031eac9a 100644 --- a/sysdep/bsd/sysio.h +++ b/sysdep/bsd/sysio.h @@ -113,7 +113,9 @@ sysio_leave_group(sock *s, ip_addr maddr) /* BSD RX/TX packet info handling for IPv4 */ /* it uses IP_RECVDSTADDR / IP_RECVIF socket options instead of IP_PKTINFO */ -#define CMSG_RX_SPACE (CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(struct sockaddr_dl))) +#define CMSG_RX_SPACE (CMSG_SPACE(sizeof(struct in_addr)) + \ + CMSG_SPACE(sizeof(struct sockaddr_dl)) + \ + CMSG_SPACE(sizeof(char))) #define CMSG_TX_SPACE CMSG_SPACE(sizeof(struct in_addr)) static char * @@ -121,13 +123,18 @@ sysio_register_cmsgs(sock *s) { int ok = 1; if (s->flags & SKF_LADDR_RX) - { - if (setsockopt(s->fd, IPPROTO_IP, IP_RECVDSTADDR, &ok, sizeof(ok)) < 0) - return "IP_RECVDSTADDR"; + { + if (setsockopt(s->fd, IPPROTO_IP, IP_RECVDSTADDR, &ok, sizeof(ok)) < 0) + return "IP_RECVDSTADDR"; + + if (setsockopt(s->fd, IPPROTO_IP, IP_RECVIF, &ok, sizeof(ok)) < 0) + return "IP_RECVIF"; + } + + if ((s->flags & SKF_TTL_RX) && + (setsockopt(s->fd, IPPROTO_IP, IP_RECVTTL, &ok, sizeof(ok)) < 0)) + return "IP_RECVTTL"; - if (setsockopt(s->fd, IPPROTO_IP, IP_RECVIF, &ok, sizeof(ok)) < 0) - return "IP_RECVIF"; - } return NULL; } @@ -136,27 +143,35 @@ static void sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) { struct cmsghdr *cm; + struct in_addr *ra = NULL; + struct sockaddr_dl *ri = NULL; + unsigned char *ttl = NULL; - if (!(s->flags & SKF_LADDR_RX)) - return; + for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) + { + if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVDSTADDR) + ra = (struct in_addr *) CMSG_DATA(cm); - s->laddr = IPA_NONE; - s->lifindex = 0; + if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVIF) + ri = (struct sockaddr_dl *) CMSG_DATA(cm); - for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) - { - if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVDSTADDR) - { - struct in_addr *ra = (struct in_addr *) CMSG_DATA(cm); - get_inaddr(&s->laddr, ra); - } + if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVTTL) + ttl = (unsigned char *) CMSG_DATA(cm); + } - if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVIF) - { - struct sockaddr_dl *ri = (struct sockaddr_dl *) CMSG_DATA(cm); - s->lifindex = ri->sdl_index; - } - } + if (s->flags & SKF_LADDR_RX) + { + s->laddr = IPA_NONE; + s->lifindex = 0; + + if (ra) + get_inaddr(&s->laddr, ra); + if (ri) + s->lifindex = ri->sdl_index; + } + + if (s->flags & SKF_TTL_RX) + s->ttl = ttl ? *ttl : -1; // log(L_WARN "RX %I %d", s->laddr, s->lifindex); } diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index 41287e71..250ed586 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -194,17 +194,22 @@ sk_set_md5_auth_int(sock *s, sockaddr *sa, char *passwd) /* RX/TX packet info handling for IPv4 */ /* Mostly similar to standardized IPv6 code */ -#define CMSG_RX_SPACE CMSG_SPACE(sizeof(struct in_pktinfo)) +#define CMSG_RX_SPACE (CMSG_SPACE(sizeof(struct in_pktinfo)) + CMSG_SPACE(sizeof(int))) #define CMSG_TX_SPACE CMSG_SPACE(sizeof(struct in_pktinfo)) static char * sysio_register_cmsgs(sock *s) { int ok = 1; + if ((s->flags & SKF_LADDR_RX) && - setsockopt(s->fd, IPPROTO_IP, IP_PKTINFO, &ok, sizeof(ok)) < 0) + (setsockopt(s->fd, IPPROTO_IP, IP_PKTINFO, &ok, sizeof(ok)) < 0)) return "IP_PKTINFO"; + if ((s->flags & SKF_TTL_RX) && + (setsockopt(s->fd, IPPROTO_IP, IP_RECVTTL, &ok, sizeof(ok)) < 0)) + return "IP_RECVTTL"; + return NULL; } @@ -213,25 +218,34 @@ sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) { struct cmsghdr *cm; struct in_pktinfo *pi = NULL; - - if (!(s->flags & SKF_LADDR_RX)) - return; + int *ttl = NULL; for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) + { + if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_PKTINFO) + pi = (struct in_pktinfo *) CMSG_DATA(cm); + + if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_TTL) + ttl = (int *) CMSG_DATA(cm); + } + + if (s->flags & SKF_LADDR_RX) + { + if (pi) { - if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_PKTINFO) - pi = (struct in_pktinfo *) CMSG_DATA(cm); + get_inaddr(&s->laddr, &pi->ipi_addr); + s->lifindex = pi->ipi_ifindex; } - - if (!pi) + else { s->laddr = IPA_NONE; s->lifindex = 0; - return; } + } + + if (s->flags & SKF_TTL_RX) + s->ttl = ttl ? *ttl : -1; - get_inaddr(&s->laddr, &pi->ipi_addr); - s->lifindex = pi->ipi_ifindex; return; } diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 434a05be..93863885 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -673,7 +673,7 @@ get_sockaddr(struct sockaddr_in *sa, ip_addr *a, struct iface **ifa, unsigned *p #ifdef IPV6 /* PKTINFO handling is also standardized in IPv6 */ -#define CMSG_RX_SPACE CMSG_SPACE(sizeof(struct in6_pktinfo)) +#define CMSG_RX_SPACE (CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int))) #define CMSG_TX_SPACE CMSG_SPACE(sizeof(struct in6_pktinfo)) /* @@ -685,15 +685,26 @@ get_sockaddr(struct sockaddr_in *sa, ip_addr *a, struct iface **ifa, unsigned *p #ifndef IPV6_RECVPKTINFO #define IPV6_RECVPKTINFO IPV6_PKTINFO #endif +/* + * Same goes for IPV6_HOPLIMIT -> IPV6_RECVHOPLIMIT. + */ +#ifndef IPV6_RECVHOPLIMIT +#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT +#endif static char * sysio_register_cmsgs(sock *s) { int ok = 1; + if ((s->flags & SKF_LADDR_RX) && - setsockopt(s->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &ok, sizeof(ok)) < 0) + (setsockopt(s->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &ok, sizeof(ok)) < 0)) return "IPV6_RECVPKTINFO"; + if ((s->flags & SKF_TTL_RX) && + (setsockopt(s->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &ok, sizeof(ok)) < 0)) + return "IPV6_RECVHOPLIMIT"; + return NULL; } @@ -702,25 +713,34 @@ sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) { struct cmsghdr *cm; struct in6_pktinfo *pi = NULL; - - if (!(s->flags & SKF_LADDR_RX)) - return; + int *hlim = NULL; for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) + { + if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO) + pi = (struct in6_pktinfo *) CMSG_DATA(cm); + + if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_HOPLIMIT) + hlim = (int *) CMSG_DATA(cm); + } + + if (s->flags & SKF_LADDR_RX) + { + if (pi) { - if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO) - pi = (struct in6_pktinfo *) CMSG_DATA(cm); + get_inaddr(&s->laddr, &pi->ipi6_addr); + s->lifindex = pi->ipi6_ifindex; } - - if (!pi) + else { s->laddr = IPA_NONE; s->lifindex = 0; - return; } + } + + if (s->flags & SKF_TTL_RX) + s->ttl = hlim ? *hlim : -1; - get_inaddr(&s->laddr, &pi->ipi6_addr); - s->lifindex = pi->ipi6_ifindex; return; } -- cgit v1.2.3 From 6ac4f87a2d661c739e55a63577e7bccf696c7abd Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 26 Jun 2013 14:35:39 +0200 Subject: Documentation for TTL security. --- doc/bird.sgml | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 7277b2b9..aa8a53ec 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -470,7 +470,7 @@ to zero to disable it. An empty is equivalent to import keep filtered + import keep filtered Usually, if an import filter rejects a route, the route is forgotten. When this option is active, these routes are kept in the routing table, but they are hidden and not @@ -1966,6 +1966,9 @@ protocol ospf <name> { ptp netmask <switch>; check link <switch>; ecmp weight <num>; + ttl security [<switch>; | tx only] + tx class|dscp <num>; + tx priority <num>; authentication [none|simple|cryptographic]; password "<text>"; password "<text>" { @@ -2236,6 +2239,20 @@ protocol ospf <name> { prefix) is propagated. It is possible that some hardware drivers or platforms do not implement this feature. Default value is no. + ttl security [ + TTL security is a feature that protects routing protocols + from remote spoofed packets by using TTL 255 instead of TTL 1 + for protocol packets destined to neighbors. Because TTL is + decremented when packets are forwarded, it is non-trivial to + spoof packets with TTL 255 from remote locations. Note that + this option would interfere with OSPF virtual links. + + If this option is enabled, the router will send OSPF packets + with TTL 255 and drop received packets with TTL less than + 255. If this option si set to tx class|dscp|priority These options specify the ToS/DiffServ/Traffic class/Priority of the outgoing OSPF packets. See sys.table_id] = p; - if (first) - { - nl_open(); - nl_open_async(); - } + + nl_open(); + nl_open_async(); } void -krt_sys_shutdown(struct krt_proto *p UNUSED, int last UNUSED) +krt_sys_shutdown(struct krt_proto *p UNUSED) { } diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 3761ace6..54297921 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -69,12 +69,14 @@ pool *krt_pool; static linpool *krt_filter_lp; +static list krt_proto_list; void krt_io_init(void) { krt_pool = rp_new(&root_pool, "Kernel Syncer"); krt_filter_lp = lp_new(krt_pool, 4080); + init_list(&krt_proto_list); } /* @@ -565,12 +567,6 @@ krt_dump_attrs(rte *e) * Routes */ -#ifdef CONFIG_ALL_TABLES_AT_ONCE -static timer *krt_scan_timer; -static int krt_instance_count; -static list krt_instance_list; -#endif - static void krt_flush_routes(struct krt_proto *p) { @@ -812,34 +808,88 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new) * Periodic scanning */ + +#ifdef CONFIG_ALL_TABLES_AT_ONCE + +static timer *krt_scan_timer; +static int krt_scan_count; + static void krt_scan(timer *t UNUSED) { struct krt_proto *p; kif_force_scan(); -#ifdef CONFIG_ALL_TABLES_AT_ONCE + + /* We need some node to decide whether to print the debug messages or not */ + p = SKIP_BACK(struct krt_proto, krt_node, HEAD(krt_proto_list)); + KRT_TRACE(p, D_EVENTS, "Scanning routing table"); + + krt_do_scan(NULL); + + void *q; + WALK_LIST(q, krt_proto_list) { - void *q; - /* We need some node to decide whether to print the debug messages or not */ - p = SKIP_BACK(struct krt_proto, instance_node, HEAD(krt_instance_list)); - if (p->instance_node.next) - KRT_TRACE(p, D_EVENTS, "Scanning routing table"); - krt_do_scan(NULL); - WALK_LIST(q, krt_instance_list) - { - p = SKIP_BACK(struct krt_proto, instance_node, q); - krt_prune(p); - } + p = SKIP_BACK(struct krt_proto, krt_node, q); + krt_prune(p); + } +} + +static void +krt_scan_timer_start(struct krt_proto *p) +{ + if (!krt_scan_count) + { + krt_scan_timer = tm_new_set(krt_pool, krt_scan, NULL, 0, KRT_CF->scan_time); + tm_start(krt_scan_timer, 0); + } + + krt_scan_count++; +} + +static void +krt_scan_timer_stop(struct krt_proto *p) +{ + krt_scan_count--; + + if (!krt_scan_count) + { + rfree(krt_scan_timer); + krt_scan_timer = NULL; } +} + #else - p = t->data; + +static void +krt_scan(timer *t) +{ + struct krt_proto *p = t->data; + + kif_force_scan(); + KRT_TRACE(p, D_EVENTS, "Scanning routing table"); krt_do_scan(p); krt_prune(p); -#endif } +static void +krt_scan_timer_start(struct krt_proto *p) +{ + p->scan_timer = tm_new_set(p->p.pool, krt_scan, p, 0, KRT_CF->scan_time); + tm_start(p->scan_timer, 0); +} + +static void +krt_scan_timer_stop(struct krt_proto *p) +{ + tm_stop(p->scan_timer); +} + +#endif + + + /* * Updates @@ -942,52 +992,20 @@ krt_init(struct proto_config *c) return &p->p; } -static timer * -krt_start_timer(struct krt_proto *p) -{ - timer *t; - - t = tm_new(p->krt_pool); - t->hook = krt_scan; - t->data = p; - t->recurrent = KRT_CF->scan_time; - tm_start(t, 0); - return t; -} - static int krt_start(struct proto *P) { struct krt_proto *p = (struct krt_proto *) P; - int first = 1; -#ifdef CONFIG_ALL_TABLES_AT_ONCE - if (!krt_instance_count++) - init_list(&krt_instance_list); - else - first = 0; - p->krt_pool = krt_pool; - add_tail(&krt_instance_list, &p->instance_node); -#else - p->krt_pool = P->pool; -#endif + add_tail(&krt_proto_list, &p->krt_node); #ifdef KRT_ALLOW_LEARN krt_learn_init(p); #endif - krt_sys_start(p, first); + krt_sys_start(p); - /* Start periodic routing table scanning */ -#ifdef CONFIG_ALL_TABLES_AT_ONCE - if (first) - krt_scan_timer = krt_start_timer(p); - else - tm_start(krt_scan_timer, 0); - p->scan_timer = krt_scan_timer; -#else - p->scan_timer = krt_start_timer(p); -#endif + krt_scan_timer_start(p); return PS_UP; } @@ -996,26 +1014,16 @@ static int krt_shutdown(struct proto *P) { struct krt_proto *p = (struct krt_proto *) P; - int last = 1; -#ifdef CONFIG_ALL_TABLES_AT_ONCE - rem_node(&p->instance_node); - if (--krt_instance_count) - last = 0; - else -#endif - tm_stop(p->scan_timer); + krt_scan_timer_stop(p); /* FIXME we should flush routes even when persist during reconfiguration */ if (p->initialized && !KRT_CF->persist) krt_flush_routes(p); - krt_sys_shutdown(p, last); + krt_sys_shutdown(p); -#ifdef CONFIG_ALL_TABLES_AT_ONCE - if (last) - rfree(krt_scan_timer); -#endif + rem_node(&p->krt_node); return PS_DOWN; } diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index d6fbf721..446914d2 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -52,15 +52,17 @@ struct krt_config { struct krt_proto { struct proto p; - struct krt_status sys; /* Sysdep state */ + struct krt_state sys; /* Sysdep state */ + #ifdef KRT_ALLOW_LEARN struct rtable krt_table; /* Internal table of inherited routes */ #endif - pool *krt_pool; /* Pool used for common krt data */ + +#ifndef CONFIG_ALL_TABLES_AT_ONCE timer *scan_timer; -#ifdef CONFIG_ALL_TABLES_AT_ONCE - node instance_node; /* Node in krt instance list */ #endif + + node krt_node; /* Node in krt_proto_list */ int initialized; /* First scan has already been finished */ }; @@ -103,7 +105,7 @@ struct kif_config { struct kif_proto { struct proto p; - struct kif_status sys; /* Sysdep state */ + struct kif_state sys; /* Sysdep state */ }; #define KIF_CF ((struct kif_config *)p->p.cf) @@ -114,8 +116,8 @@ struct proto_config * krt_init_config(int class); /* krt sysdep */ void krt_sys_init(struct krt_proto *); -void krt_sys_start(struct krt_proto *, int); -void krt_sys_shutdown(struct krt_proto *, int); +void krt_sys_start(struct krt_proto *); +void krt_sys_shutdown(struct krt_proto *); int krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o); void krt_sys_preconfig(struct config *); -- cgit v1.2.3 From c01a94663cc18f53fd741c5d44387eead9ca88af Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sun, 7 Jul 2013 12:11:42 +0200 Subject: Implements multiple routing table support for FreeBSD and OpenBSD. Inspired by the patch from Alexander V. Chernikov. --- sysdep/bsd/Modules | 1 + sysdep/bsd/krt-sock.Y | 32 ++++ sysdep/bsd/krt-sock.c | 460 +++++++++++++++++++++++++++++++++++++++++--------- sysdep/bsd/krt-sys.h | 11 +- sysdep/cf/bsd-v6.h | 1 + sysdep/cf/bsd.h | 1 + 6 files changed, 418 insertions(+), 88 deletions(-) create mode 100644 sysdep/bsd/krt-sock.Y diff --git a/sysdep/bsd/Modules b/sysdep/bsd/Modules index 3729587d..96455db7 100644 --- a/sysdep/bsd/Modules +++ b/sysdep/bsd/Modules @@ -1,3 +1,4 @@ krt-sock.c +krt-sock.Y krt-sys.h sysio.h diff --git a/sysdep/bsd/krt-sock.Y b/sysdep/bsd/krt-sock.Y new file mode 100644 index 00000000..0218f188 --- /dev/null +++ b/sysdep/bsd/krt-sock.Y @@ -0,0 +1,32 @@ +/* + * BIRD -- BSD Kernel Syncer Configuration + * + * (c) 1999--2000 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +CF_HDR + +CF_DECLS + +CF_KEYWORDS(KERNEL, TABLE) + +CF_GRAMMAR + +CF_ADDTO(kern_proto, kern_proto kern_sys_item ';') + +kern_sys_item: + KERNEL TABLE expr { + if ($3 && (krt_max_tables == 1)) + cf_error("Multiple kernel routing tables not supported"); + if ($3 < 0 || $3 >= krt_max_tables) + cf_error("Kernel table id must be in range 0-%d", krt_max_tables - 1); + + THIS_KRT->sys.table_id = $3; + } + ; + +CF_CODE + +CF_END diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index cad0cfc9..08dfccc8 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -1,5 +1,5 @@ /* - * BIRD -- Unix Routing Table Syncing + * BIRD -- BSD Routing Table Syncing * * (c) 2004 Ondrej Filip * @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -34,18 +35,112 @@ #include "lib/socket.h" -#ifndef RTAX_MAX -#define RTAX_MAX 8 +/* + * There are significant differences in multiple tables support between BSD variants. + * + * OpenBSD has table_id field for routes in route socket protocol, therefore all + * tables could be managed by one kernel socket. FreeBSD lacks such field, + * therefore multiple sockets (locked to specific table using SO_SETFIB socket + * option) must be used. + * + * Both FreeBSD and OpenBSD uses separate scans for each table. In OpenBSD, + * table_id is specified explicitly as sysctl scan argument, while in FreeBSD it + * is handled implicitly by changing default table using setfib() syscall. + * + * KRT_SHARED_SOCKET - use shared kernel socked instead of one for each krt_proto + * KRT_USE_SETFIB_SCAN - use setfib() for sysctl() route scan + * KRT_USE_SETFIB_SOCK - use SO_SETFIB socket option for kernel sockets + * KRT_USE_SYSCTL_7 - use 7-th arg of sysctl() as table id for route scans + * KRT_USE_SYSCTL_NET_FIBS - use net.fibs sysctl() for dynamic max number of fibs + */ + +#ifdef __FreeBSD__ +#define KRT_MAX_TABLES 256 +#define KRT_USE_SETFIB_SCAN +#define KRT_USE_SETFIB_SOCK +#define KRT_USE_SYSCTL_NET_FIBS #endif -struct ks_msg +#ifdef __OpenBSD__ +#define KRT_MAX_TABLES (RT_TABLEID_MAX+1) +#define KRT_SHARED_SOCKET +#define KRT_USE_SYSCTL_7 +#endif + +#ifndef KRT_MAX_TABLES +#define KRT_MAX_TABLES 1 +#endif + + + +/* Dynamic max number of tables */ + +int krt_max_tables; + +#ifdef KRT_USE_SYSCTL_NET_FIBS + +static int +krt_get_max_tables(void) { - struct rt_msghdr rtm; - struct sockaddr_storage buf[RTAX_MAX]; -}; + int fibs; + size_t fibs_len = sizeof(fibs); + + if (sysctlbyname("net.fibs", &fibs, &fibs_len, NULL, 0) < 0) + { + log(L_WARN "KRT: unable to get max number of fib tables: %m"); + return 1; + } + + return MIN(fibs, KRT_MAX_TABLES); +} + +#else + +static int +krt_get_max_tables(void) +{ + return KRT_MAX_TABLES; +} + +#endif /* KRT_USE_SYSCTL_NET_FIBS */ + + +/* setfib() syscall for FreeBSD scans */ + +#ifdef KRT_USE_SETFIB_SCAN + +/* +static int krt_default_fib; + +static int +krt_get_active_fib(void) +{ + int fib; + size_t fib_len = sizeof(fib); + + if (sysctlbyname("net.my_fibnum", &fib, &fib_len, NULL, 0) < 0) + { + log(L_WARN "KRT: unable to get active fib number: %m"); + return 0; + } + + return fib; +} +*/ + +extern int setfib(int fib); +#endif /* KRT_USE_SETFIB_SCAN */ -static int rt_sock = 0; + +/* table_id -> krt_proto map */ + +#ifdef KRT_SHARED_SOCKET +static struct krt_proto *krt_table_map[KRT_MAX_TABLES]; +#endif + + +/* Route socket message processing */ int krt_capable(rte *e) @@ -65,6 +160,16 @@ krt_capable(rte *e) ); } +#ifndef RTAX_MAX +#define RTAX_MAX 8 +#endif + +struct ks_msg +{ + struct rt_msghdr rtm; + struct sockaddr_storage buf[RTAX_MAX]; +}; + #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) @@ -81,7 +186,7 @@ krt_capable(rte *e) body += l;} static int -krt_sock_send(int cmd, rte *e) +krt_send_route(struct krt_proto *p, int cmd, rte *e) { net *net = e->net; rta *a = e->attrs; @@ -103,13 +208,13 @@ krt_sock_send(int cmd, rte *e) msg.rtm.rtm_flags = RTF_UP | RTF_PROTO1; if (net->n.pxlen == MAX_PREFIX_LENGTH) - { msg.rtm.rtm_flags |= RTF_HOST; - } else - { msg.rtm.rtm_addrs |= RTA_NETMASK; - } + +#ifdef KRT_SHARED_SOCKET + msg.rtm.rtm_tableid = KRT_CF->sys.table_id; +#endif #ifdef RTF_REJECT if(a->dest == RTD_UNREACHABLE) @@ -192,7 +297,7 @@ krt_sock_send(int cmd, rte *e) l = body - (char *)&msg; msg.rtm.rtm_msglen = l; - if ((l = write(rt_sock, (char *)&msg, l)) < 0) { + if ((l = write(p->sys.sk->fd, (char *)&msg, l)) < 0) { log(L_ERR "KRT: Error sending route %I/%d to kernel: %m", net->n.prefix, net->n.pxlen); return -1; } @@ -201,16 +306,16 @@ krt_sock_send(int cmd, rte *e) } void -krt_replace_rte(struct krt_proto *p UNUSED, net *n, rte *new, rte *old, +krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs UNUSED) { int err = 0; if (old) - krt_sock_send(RTM_DELETE, old); + krt_send_route(p, RTM_DELETE, old); if (new) - err = krt_sock_send(RTM_ADD, new); + err = krt_send_route(p, RTM_ADD, new); if (err < 0) n->n.flags |= KRF_SYNC_ERROR; @@ -221,8 +326,10 @@ krt_replace_rte(struct krt_proto *p UNUSED, net *n, rte *new, rte *old, #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) +krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan) { + /* p is NULL iff KRT_SHARED_SOCKET and !scan */ + rte *e; net *net; sockaddr dst, gate, mask; @@ -244,6 +351,17 @@ krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan) if (flags & RTF_LLINFO) SKIP("link-local\n"); +#ifdef KRT_SHARED_SOCKET + if (!scan) + { + int table_id = msg->rtm.rtm_tableid; + p = (table_id < KRT_MAX_TABLES) ? krt_table_map[table_id] : NULL; + + if (!p) + SKIP("unknown table id %d\n", table_id); + } +#endif + GETADDR(&dst, RTA_DST); GETADDR(&gate, RTA_GATEWAY); GETADDR(&mask, RTA_NETMASK); @@ -594,17 +712,18 @@ krt_read_addr(struct ks_msg *msg) ifa_delete(&ifa); } - -void +static void krt_read_msg(struct proto *p, struct ks_msg *msg, int scan) { + /* p is NULL iff KRT_SHARED_SOCKET and !scan */ + switch (msg->rtm.rtm_type) { case RTM_GET: if(!scan) return; case RTM_ADD: case RTM_DELETE: - krt_read_rt(msg, (struct krt_proto *)p, scan); + krt_read_route(msg, (struct krt_proto *)p, scan); break; case RTM_IFANNOUNCE: krt_read_ifannounce(msg); @@ -621,14 +740,57 @@ krt_read_msg(struct proto *p, struct ks_msg *msg, int scan) } } + +/* Sysctl based scans */ + +static byte *krt_buffer; +static size_t krt_buflen, krt_bufmin; +static struct proto *krt_buffer_owner; + +static byte * +krt_buffer_update(struct proto *p, size_t *needed) +{ + size_t req = *needed; + + if ((req > krt_buflen) || + ((p == krt_buffer_owner) && (req < krt_bufmin))) + { + /* min buflen is 32 kB, step is 8 kB, or 128 kB if > 1 MB */ + size_t step = (req < 0x100000) ? 0x2000 : 0x20000; + krt_buflen = (req < 0x6000) ? 0x8000 : (req + step); + krt_bufmin = (req < 0x8000) ? 0 : (req - 2*step); + + if (krt_buffer) + mb_free(krt_buffer); + krt_buffer = mb_alloc(krt_pool, krt_buflen); + krt_buffer_owner = p; + } + + *needed = krt_buflen; + return krt_buffer; +} + +static void +krt_buffer_release(struct proto *p) +{ + if (p == krt_buffer_owner) + { + mb_free(krt_buffer); + krt_buffer = NULL; + krt_buflen = 0; + krt_buffer_owner = 0; + } +} + static void -krt_sysctl_scan(struct proto *p, pool *pool, byte **buf, size_t *bl, int cmd) +krt_sysctl_scan(struct proto *p, int cmd, int table_id) { - byte *next; - int mib[6]; - size_t obl, needed; + byte *buf, *next; + int mib[7], mcnt; + size_t needed; struct ks_msg *m; int retries = 3; + int rv; mib[0] = CTL_NET; mib[1] = PF_ROUTE; @@ -636,124 +798,258 @@ krt_sysctl_scan(struct proto *p, pool *pool, byte **buf, size_t *bl, int cmd) mib[3] = BIRD_PF; mib[4] = cmd; mib[5] = 0; + mcnt = 6; - try: - if (sysctl(mib, 6 , NULL , &needed, NULL, 0) < 0) - die("krt_sysctl_scan 1: %m"); - - obl = *bl; +#ifdef KRT_USE_SYSCTL_7 + if (table_id >= 0) + { + mib[6] = table_id; + mcnt = 7; + } +#endif - while (needed > *bl) *bl *= 2; - while (needed < (*bl/2)) *bl /= 2; +#ifdef KRT_USE_SETFIB_SCAN + if (table_id > 0) + if (setfib(table_id) < 0) + { + log(L_ERR "KRT: setfib(%d) failed: %m", table_id); + return; + } +#endif - if ((obl!=*bl) || !*buf) + try: + rv = sysctl(mib, mcnt, NULL, &needed, NULL, 0); + if (rv < 0) { - if (*buf) mb_free(*buf); - if ((*buf = mb_alloc(pool, *bl)) == NULL) die("RT scan buf alloc"); + /* OpenBSD returns EINVAL for not yet used tables */ + if ((errno == EINVAL) && (table_id > 0)) + goto exit; + + log(L_ERR "KRT: Route scan estimate failed: %m"); + goto exit; } - if (sysctl(mib, 6 , *buf, &needed, NULL, 0) < 0) + /* The table is empty */ + if (needed == 0) + goto exit; + + buf = krt_buffer_update(p, &needed); + + rv = sysctl(mib, mcnt, buf, &needed, NULL, 0); + if (rv < 0) { - if (errno == ENOMEM) - { - /* The buffer size changed since last sysctl ('needed' is not changed) */ - if (retries--) - goto try; + /* The buffer size changed since last sysctl ('needed' is not changed) */ + if ((errno == ENOMEM) && retries--) + goto try; - log(L_ERR "KRT: Route scan failed"); - return; - } - die("krt_sysctl_scan 2: %m"); + log(L_ERR "KRT: Route scan failed: %m"); + goto exit; } - for (next = *buf; next < (*buf + needed); next += m->rtm.rtm_msglen) +#ifdef KRT_USE_SETFIB_SCAN + if (table_id > 0) + if (setfib(0) < 0) + die("KRT: setfib(%d) failed: %m", 0); +#endif + + /* Process received messages */ + for (next = buf; next < (buf + needed); next += m->rtm.rtm_msglen) { m = (struct ks_msg *)next; krt_read_msg(p, m, 1); } -} -static byte *krt_buffer = NULL; -static byte *kif_buffer = NULL; -static size_t krt_buflen = 32768; -static size_t kif_buflen = 4096; + return; + + exit: + krt_buffer_release(p); + +#ifdef KRT_USE_SETFIB_SCAN + if (table_id > 0) + if (setfib(0) < 0) + die("KRT: setfib(%d) failed: %m", 0); +#endif +} void krt_do_scan(struct krt_proto *p) { - krt_sysctl_scan(&p->p, p->p.pool, &krt_buffer, &krt_buflen, NET_RT_DUMP); + krt_sysctl_scan(&p->p, NET_RT_DUMP, KRT_CF->sys.table_id); } void kif_do_scan(struct kif_proto *p) { if_start_update(); - krt_sysctl_scan(&p->p, p->p.pool, &kif_buffer, &kif_buflen, NET_RT_IFLIST); + krt_sysctl_scan(&p->p, NET_RT_IFLIST, -1); if_end_update(); } + +/* Kernel sockets */ + static int krt_sock_hook(sock *sk, int size UNUSED) { struct ks_msg msg; int l = read(sk->fd, (char *)&msg, sizeof(msg)); - if(l <= 0) + if (l <= 0) log(L_ERR "krt-sock: read failed"); else - krt_read_msg((struct proto *)sk->data, &msg, 0); + krt_read_msg((struct proto *) sk->data, &msg, 0); return 0; } +static sock * +krt_sock_open(pool *pool, void *data, int table_id) +{ + sock *sk; + int fd; + + fd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); + if (fd < 0) + die("Cannot open kernel socket for routes"); + +#ifdef KRT_USE_SETFIB_SOCK + if (table_id > 0) + { + if (setsockopt(fd, SOL_SOCKET, SO_SETFIB, &table_id, sizeof(table_id)) < 0) + die("Cannot set FIB %d for kernel socket: %m", table_id); + } +#endif + + sk = sk_new(pool); + sk->type = SK_MAGIC; + sk->rx_hook = krt_sock_hook; + sk->fd = fd; + sk->data = data; + + if (sk_open(sk) < 0) + bug("krt-sock: sk_open failed"); + + return sk; +} + + +#ifdef KRT_SHARED_SOCKET + +static sock *krt_sock; +static int krt_sock_count; + + +static void +krt_sock_open_shared(void) +{ + if (!krt_sock_count) + krt_sock = krt_sock_open(krt_pool, NULL, -1); + + krt_sock_count++; +} + +static void +krt_sock_close_shared(void) +{ + krt_sock_count--; + + if (!krt_sock_count) + { + rfree(krt_sock); + krt_sock = NULL; + } +} + void -krt_sys_start(struct krt_proto *x) +krt_sys_start(struct krt_proto *p) { - sock *sk_rt; - static int ks_open_tried = 0; + krt_table_map[KRT_CF->sys.table_id] = p; - if (ks_open_tried) - return; + krt_sock_open_shared(); + p->sys.sk = krt_sock; +} - ks_open_tried = 1; +void +krt_sys_shutdown(struct krt_proto *p) +{ + krt_sock_close_shared(); + p->sys.sk = NULL; - DBG("KRT: Opening kernel socket\n"); + krt_table_map[KRT_CF->sys.table_id] = NULL; - if( (rt_sock = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) - die("Cannot open kernel socket for routes"); + krt_buffer_release(&p->p); +} - sk_rt = sk_new(krt_pool); - sk_rt->type = SK_MAGIC; - sk_rt->rx_hook = krt_sock_hook; - sk_rt->fd = rt_sock; - sk_rt->data = x; - if (sk_open(sk_rt)) - bug("krt-sock: sk_open failed"); +#else + +void +krt_sys_start(struct krt_proto *p) +{ + p->sys.sk = krt_sock_open(p->p.pool, p, KRT_CF->sys.table_id); } void -krt_sys_shutdown(struct krt_proto *x UNUSED) +krt_sys_shutdown(struct krt_proto *p) { - if (!krt_buffer) - return; + rfree(p->sys.sk); + p->sys.sk = NULL; + + krt_buffer_release(&p->p); +} + +#endif /* KRT_SHARED_SOCKET */ + - mb_free(krt_buffer); - krt_buffer = NULL; +/* KRT configuration callbacks */ + +static u32 krt_table_cf[(KRT_MAX_TABLES+31) / 32]; + +int +krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o) +{ + return n->sys.table_id == o->sys.table_id; } +void +krt_sys_preconfig(struct config *c UNUSED) +{ + krt_max_tables = krt_get_max_tables(); + bzero(&krt_table_cf, sizeof(krt_table_cf)); +} void -kif_sys_start(struct kif_proto *p UNUSED) +krt_sys_postconfig(struct krt_config *x) { + u32 *tbl = krt_table_cf; + int id = x->sys.table_id; + + if (tbl[id/32] & (1 << (id%32))) + cf_error("Multiple kernel syncers defined for table #%d", id); + + tbl[id/32] |= (1 << (id%32)); } +void krt_sys_init_config(struct krt_config *c) +{ + c->sys.table_id = 0; /* Default table */ +} + +void krt_sys_copy_config(struct krt_config *d, struct krt_config *s) +{ + d->sys.table_id = s->sys.table_id; +} + + +/* KIF misc code */ + void -kif_sys_shutdown(struct kif_proto *p UNUSED) +kif_sys_start(struct kif_proto *p UNUSED) { - if (!kif_buffer) - return; +} - mb_free(kif_buffer); - kif_buffer = NULL; +void +kif_sys_shutdown(struct kif_proto *p) +{ + krt_buffer_release(&p->p); } diff --git a/sysdep/bsd/krt-sys.h b/sysdep/bsd/krt-sys.h index 5e4529c5..9c0d4972 100644 --- a/sysdep/bsd/krt-sys.h +++ b/sysdep/bsd/krt-sys.h @@ -9,6 +9,7 @@ #ifndef _BIRD_KRT_SYS_H_ #define _BIRD_KRT_SYS_H_ +struct birdsock; /* Kernel interfaces */ @@ -30,20 +31,18 @@ static inline void kif_sys_copy_config(struct kif_config *d UNUSED, struct kif_c /* Kernel routes */ +extern int krt_max_tables; + struct krt_params { + int table_id; /* Kernel table ID we sync with */ }; struct krt_state { + struct birdsock *sk; }; static inline void krt_sys_init(struct krt_proto *p UNUSED) { } -static inline int krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n UNUSED, struct krt_config *o UNUSED) { return 1; } - -static inline void krt_sys_preconfig(struct config *c UNUSED) { } -static inline void krt_sys_postconfig(struct krt_config *c UNUSED) { } -static inline void krt_sys_init_config(struct krt_config *c UNUSED) { } -static inline void krt_sys_copy_config(struct krt_config *d UNUSED, struct krt_config *s UNUSED) { } #endif diff --git a/sysdep/cf/bsd-v6.h b/sysdep/cf/bsd-v6.h index b7f25f64..3403299f 100644 --- a/sysdep/cf/bsd-v6.h +++ b/sysdep/cf/bsd-v6.h @@ -10,6 +10,7 @@ #define CONFIG_AUTO_ROUTES #define CONFIG_SELF_CONSCIOUS +#define CONFIG_MULTIPLE_TABLES #define CONFIG_SKIP_MC_BIND diff --git a/sysdep/cf/bsd.h b/sysdep/cf/bsd.h index e7cc135f..1101b228 100644 --- a/sysdep/cf/bsd.h +++ b/sysdep/cf/bsd.h @@ -8,6 +8,7 @@ #define CONFIG_AUTO_ROUTES #define CONFIG_SELF_CONSCIOUS +#define CONFIG_MULTIPLE_TABLES #define CONFIG_SKIP_MC_BIND -- cgit v1.2.3 From cc31b75a8fd7949533c12db2c3e9d67eeaf46d10 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 9 Jul 2013 23:27:10 +0200 Subject: Implements 'bgppath ~ int set' filter op. --- doc/bird.sgml | 2 +- filter/filter.c | 3 +++ filter/test.conf | 4 ++-- nest/a-path.c | 23 +++++++++++++++++++++++ nest/attrs.h | 3 +++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index aa8a53ec..27f8b869 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1090,7 +1090,7 @@ incompatible with each other (that is to prevent you from shooting in the foot). Special operators include There is one operator related to ROA infrastructure - data; + u8 *q = p+path->length; + int i, n; + + while (p Date: Thu, 11 Jul 2013 13:50:44 +0200 Subject: Some fixes for TTL security. --- proto/ospf/iface.c | 4 +++- sysdep/bsd/sysio.h | 26 +++++++++++++------------- sysdep/unix/io.c | 6 +++--- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 698ef620..63c26466 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -87,6 +87,7 @@ ospf_sk_open(struct ospf_iface *ifa) sk->tbsize = rxbufsize(ifa); sk->data = (void *) ifa; sk->flags = SKF_LADDR_RX | (ifa->check_ttl ? SKF_TTL_RX : 0); + sk->ttl = ifa->cf->ttl_security ? 255 : -1; if (sk_open(sk) != 0) goto err; @@ -664,7 +665,8 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* Change of these options would require to reset the iface socket */ if ((new->real_bcast != ifa->cf->real_bcast) || (new->tx_tos != ifa->cf->tx_tos) || - (new->tx_priority != ifa->cf->tx_priority)) + (new->tx_priority != ifa->cf->tx_priority) || + (new->ttl_security != ifa->cf->ttl_security)) return 0; ifa->cf = new; diff --git a/sysdep/bsd/sysio.h b/sysdep/bsd/sysio.h index 031eac9a..cf049a0b 100644 --- a/sysdep/bsd/sysio.h +++ b/sysdep/bsd/sysio.h @@ -6,9 +6,22 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ +#ifdef __NetBSD__ + +#ifndef IP_RECVTTL +#define IP_RECVTTL 23 +#endif + +#ifndef IP_MINTTL +#define IP_MINTTL 24 +#endif + +#endif + #ifdef __DragonFly__ #define TCP_MD5SIG TCP_SIGNATURE_ENABLE #endif + #ifdef IPV6 static inline void @@ -259,8 +272,6 @@ sk_set_md5_auth_int(sock *s, sockaddr *sa, char *passwd) #ifndef IPV6 -#ifdef IP_MINTTL - static int sk_set_min_ttl4(sock *s, int ttl) { @@ -277,17 +288,6 @@ sk_set_min_ttl4(sock *s, int ttl) return 0; } -#else /* no IP_MINTTL */ - -static int -sk_set_min_ttl4(sock *s, int ttl) -{ - log(L_ERR "IPv4 TTL security not supported"); - return -1; -} - -#endif - #else /* IPv6 */ static int diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 93863885..4fee10e7 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -821,10 +821,10 @@ sk_setup(sock *s) WARN("IPV6_V6ONLY"); #endif - if (s->ttl >= 0) - err = sk_set_ttl_int(s); + if ((s->ttl >= 0) && (err = sk_set_ttl_int(s))) + goto bad; - sysio_register_cmsgs(s); + err = sysio_register_cmsgs(s); bad: return err; } -- cgit v1.2.3 From 48b15ef10fede35113af71bd0dbb0b27a5fcb8f5 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 13 Jul 2013 01:39:41 +0200 Subject: Fixes stuck connection during BGP session shutdown. If TX buffers were full during BGP session shutdown then a protocol waited indefinitely to be able to send notification packet to close the session. --- proto/bgp/bgp.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index b1594b92..32153452 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -384,10 +384,12 @@ bgp_conn_enter_close_state(struct bgp_conn *conn) int os = conn->state; bgp_conn_set_state(conn, BS_CLOSE); - tm_stop(conn->hold_timer); tm_stop(conn->keepalive_timer); conn->sk->rx_hook = NULL; + /* Timeout for CLOSE state, if we cannot send notification soon then we just hangup */ + bgp_start_timer(conn->hold_timer, 10); + if (os == BS_ESTABLISHED) bgp_conn_leave_established_state(p); } @@ -478,9 +480,18 @@ static void bgp_hold_timeout(timer *t) { struct bgp_conn *conn = t->data; + struct bgp_proto *p = conn->bgp; DBG("BGP: Hold timeout\n"); + /* We are already closing the connection - just do hangup */ + if (conn->state == BS_CLOSE) + { + BGP_TRACE(D_EVENTS, "Connection stalled"); + bgp_conn_enter_idle_state(conn); + return; + } + /* If there is something in input queue, we are probably congested and perhaps just not processed BGP packets in time. */ -- cgit v1.2.3 From 9135c1f0ca6322bff9648895b5394b97761b4bcb Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 24 Jul 2013 14:11:12 +0200 Subject: Fixes bug in protocol flushing and rtable pruning. When route was propagated to another rtable through a pipe and then the pipe was reconfigured softly in such a way that any subsequent route updates are filtered, then the source protocol shutdown didn't clean up the route in the second rtable which caused stale routes and potential crashes. --- nest/proto.c | 6 +++++- nest/route.h | 3 +-- nest/rt-table.c | 48 ++++++++++++++++++++++++++++++------------------ 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/nest/proto.c b/nest/proto.c index 7e7fb7fa..60495aa0 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -835,14 +835,18 @@ static void proto_schedule_flush_loop(void) { struct proto *p; + struct announce_hook *h; if (flush_loop_state) return; flush_loop_state = 1; - rt_schedule_prune_all(); WALK_LIST(p, flush_proto_list) + { p->flushing = 1; + for (h=p->ahooks; h; h=h->next) + h->table->prune_state = 1; + } ev_schedule(proto_flush_event); } diff --git a/nest/route.h b/nest/route.h index 8fd01a66..35b5fa19 100644 --- a/nest/route.h +++ b/nest/route.h @@ -141,7 +141,7 @@ typedef struct rtable { int gc_counter; /* Number of operations since last GC */ bird_clock_t gc_time; /* Time of last GC */ byte gc_scheduled; /* GC is scheduled */ - byte prune_state; /* Table prune state, 1 -> prune is running */ + byte prune_state; /* Table prune state, 1 -> scheduled, 2-> running */ byte hcu_scheduled; /* Hostcache update is scheduled */ byte nhu_state; /* Next Hop Update state */ struct fib_iterator prune_fit; /* Rtable prune FIB iterator */ @@ -265,7 +265,6 @@ void rt_dump(rtable *); void rt_dump_all(void); int rt_feed_baby(struct proto *p); void rt_feed_baby_abort(struct proto *p); -void rt_schedule_prune_all(void); int rt_prune_loop(void); struct rtable_config *rt_new_table(struct symbol *s); diff --git a/nest/rt-table.c b/nest/rt-table.c index ebdac833..16dd9bcd 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -1268,19 +1268,8 @@ rt_init(void) } -/* Called from proto_schedule_flush_loop() only, - ensuring that all prune states are zero */ -void -rt_schedule_prune_all(void) -{ - rtable *t; - - WALK_LIST(t, routing_tables) - t->prune_state = 1; -} - static inline int -rt_prune_step(rtable *tab, int *max_feed) +rt_prune_step(rtable *tab, int step, int *max_feed) { struct fib_iterator *fit = &tab->prune_fit; @@ -1306,8 +1295,8 @@ again: rescan: for (e=n->routes; e; e=e->next) - if (e->sender->proto->core_state != FS_HAPPY && - e->sender->proto->core_state != FS_FEEDING) + if (e->sender->proto->flushing || + (step && e->attrs->proto->flushing)) { if (*max_feed <= 0) { @@ -1315,6 +1304,10 @@ again: return 0; } + if (step) + log(L_WARN "Route %I/%d from %s still in %s after flush", + n->n.prefix, n->n.pxlen, e->attrs->proto->name, tab->name); + rte_discard(tab, e); (*max_feed)--; @@ -1339,23 +1332,42 @@ again: /** * rt_prune_loop - prune routing tables - * @tab: routing table to be pruned * * The prune loop scans routing tables and removes routes belonging to - * inactive protocols and also stale network entries. Returns 1 when + * flushing protocols and also stale network entries. Returns 1 when * all such routes are pruned. It is a part of the protocol flushing * loop. + * + * The prune loop runs in two steps. In the first step it prunes just + * the routes with flushing senders (in explicitly marked tables) so + * the route removal is propagated as usual. In the second step, all + * remaining relevant routes are removed. Ideally, there shouldn't be + * any, but it happens when pipe filters are changed. */ int rt_prune_loop(void) { - rtable *t; + static int step = 0; int max_feed = 512; + rtable *t; + again: WALK_LIST(t, routing_tables) - if (! rt_prune_step(t, &max_feed)) + if (! rt_prune_step(t, step, &max_feed)) return 0; + if (step == 0) + { + /* Prepare for the second step */ + WALK_LIST(t, routing_tables) + t->prune_state = 1; + + step = 1; + goto again; + } + + /* Done */ + step = 0; return 1; } -- cgit v1.2.3 From e1afee279993363ffb4a7005554d0774eb09b764 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 24 Jul 2013 14:19:37 +0200 Subject: Fixes socket error hook for radv protocol. --- proto/radv/packets.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/radv/packets.c b/proto/radv/packets.c index dd839536..38abaa4c 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -395,7 +395,7 @@ static void radv_err_hook(sock *sk, int err) { struct radv_iface *ifa = sk->data; - log(L_ERR "%s: Socket error: %m", ifa->ra->p.name, err); + log(L_ERR "%s: Socket error on %s: %M", ifa->ra->p.name, ifa->iface->name, err); } int -- cgit v1.2.3 From a0b176e3b2b50d3a30574afa927e0ee8ef65be68 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 24 Jul 2013 14:20:46 +0200 Subject: Fixes header file name. Thanks to Fritz Grimpen for the patch. --- sysdep/unix/io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 4fee10e7..fcf5dd1d 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -17,10 +17,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include -- cgit v1.2.3 From 508d936078aecc8fbbb9ca1218104599c4a3cb4a Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 25 Jul 2013 13:15:32 +0200 Subject: Implements eval command and minor CLI cleanups. Implemented eval command can be used to evaluate expressions. The patch also documents echo command and allows to use log classes instead of integer as a mask for echo. --- doc/bird.sgml | 16 ++++++++++++---- doc/reply_codes | 2 ++ filter/filter.c | 32 ++++++++++++++++++++++---------- filter/filter.h | 3 +++ nest/cmds.c | 20 ++++++++++++++++++++ nest/cmds.h | 3 +++ nest/config.Y | 8 ++++++-- sysdep/unix/log.c | 2 ++ 8 files changed, 70 insertions(+), 16 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 27f8b869..a1d3dc2c 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -282,7 +282,7 @@ protocol rip { include " This statement causes inclusion of a new file. The maximal depth is set to 5. - log " +

There are several options that give sense only with certain protocols: -- cgit v1.2.3 From f8e8fcfabeb206287065f48e800743b0aa797cc2 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 29 Jul 2013 13:07:15 +0200 Subject: Test commit. --- doc/bird.sgml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 162ab43d..6bf443e1 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -408,8 +408,8 @@ protocol rip { entries. The option may be used multiple times. Other entries can be added dynamically by eval Evaluates given filter expression. It - is used by us for testing of filters. + eval + Evaluates given filter expression. It is used by us for testing of filters. Protocol options -- cgit v1.2.3 From 00192d5ab88ff9eeccbc1bc10cb534976a56963d Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 13 Aug 2013 20:25:05 +0200 Subject: Implements proper setting of 'gw' route attribute. Thanks to Sergey Popovich for the bugreport. --- doc/bird.sgml | 2 +- filter/config.Y | 1 - filter/filter.c | 23 ++++++++++++++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 6bf443e1..7db9fad2 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1169,7 +1169,7 @@ undefined value is regarded as empty clist for most purposes. Preference of the route. Valid values are 0-65535. (See the chapter about routing tables.) - The router which the route has originated from. Read-only. + The router which the route has originated from. Next hop packets routed using this route should be forwarded to. diff --git a/filter/config.Y b/filter/config.Y index 7f73b895..66234050 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -681,7 +681,6 @@ symbol: static_attr: FROM { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = OFFSETOF(struct rta, from); $$->a1.i = 1; } - | GW { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = OFFSETOF(struct rta, gw); $$->a1.i = 1; } | NET { $$ = f_new_inst(); $$->aux = T_PREFIX; $$->a2.i = 0x12345678; /* This is actually ok - T_PREFIX is special-cased. */ } | PROTO { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = 0x12345678; /* T_STRING is also special-cased. */ } diff --git a/filter/filter.c b/filter/filter.c index d784c253..98bae331 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -853,10 +853,29 @@ interpret(struct f_inst *what) f_rta_cow(); { struct rta *rta = (*f_rte)->attrs; + ip_addr ip; + switch (what->aux) { case T_IP: - * (ip_addr *) ((char *) rta + what->a2.i) = v1.val.px.ip; + ip = v1.val.px.ip; + + /* "gw" attribute? */ + if (what->a2.i == OFFSETOF(struct rta, gw)) + { + neighbor *n = neigh_find(rta->proto, &ip, 0); + if (!n || (n->scope == SCOPE_HOST)) + runtime( "Invalid gw address" ); + + rta->dest = RTD_ROUTER; + rta->gw = ip; + rta->iface = n->iface; + rta->nexthops = NULL; + rta->hostentry = NULL; + } + else /* or "from" attribute? */ + rta->from = ip; + break; case T_ENUM_SCOPE: @@ -867,10 +886,12 @@ interpret(struct f_inst *what) i = v1.val.i; if ((i != RTD_BLACKHOLE) && (i != RTD_UNREACHABLE) && (i != RTD_PROHIBIT)) runtime( "Destination can be changed only to blackhole, unreachable or prohibit" ); + rta->dest = i; rta->gw = IPA_NONE; rta->iface = NULL; rta->nexthops = NULL; + rta->hostentry = NULL; break; default: -- cgit v1.2.3 From b21955e05800c3ceedfe39eef605da84285296c7 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 13 Aug 2013 20:42:43 +0200 Subject: Fixes a bug related to mixed up neighbor events in BGP. Neighbor events related to received route next hops got mixed up with sticky neighbor node for an IP of the BGP peer. If a neighbor for a next hop disappears, BGP session is shut down. --- proto/bgp/bgp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 32153452..7cad75df 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -743,6 +743,9 @@ bgp_neigh_notify(neighbor *n) { struct bgp_proto *p = (struct bgp_proto *) n->proto; + if (! (n->flags & NEF_STICKY)) + return; + if (n->scope > 0) { if ((p->p.proto_state == PS_START) && (p->start_state == BSS_PREPARE)) -- cgit v1.2.3 From 8a112d8ba2e77d79468146ec8f54b3c90b6e68e4 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 13 Aug 2013 23:04:06 +0200 Subject: Removes strip from make install Thanks to Alexander V. Chernikov for the patch. --- tools/Makefile.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/Makefile.in b/tools/Makefile.in index feb83b9f..062ba916 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -69,10 +69,10 @@ tags: install: all $(INSTALL) -d $(DESTDIR)/$(sbindir) $(DESTDIR)/$(sysconfdir) $(DESTDIR)/@runtimedir@ - $(INSTALL_PROGRAM) -s $(exedir)/bird $(DESTDIR)/$(sbindir)/bird@SUFFIX@ - $(INSTALL_PROGRAM) -s $(exedir)/birdcl $(DESTDIR)/$(sbindir)/birdcl@SUFFIX@ + $(INSTALL_PROGRAM) $(exedir)/bird $(DESTDIR)/$(sbindir)/bird@SUFFIX@ + $(INSTALL_PROGRAM) $(exedir)/birdcl $(DESTDIR)/$(sbindir)/birdcl@SUFFIX@ if test -n "@CLIENT@" ; then \ - $(INSTALL_PROGRAM) -s $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc@SUFFIX@ ; \ + $(INSTALL_PROGRAM) $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc@SUFFIX@ ; \ fi if ! test -f $(DESTDIR)/@CONFIG_FILE@ ; then \ $(INSTALL_DATA) $(srcdir)/doc/bird.conf.example $(DESTDIR)/@CONFIG_FILE@ ; \ -- cgit v1.2.3 From bff9ce5130d16af2fd802d42bdb2bff00980c9ae Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 15 Aug 2013 01:06:47 +0200 Subject: Extends delete/filter operators to work no bgp_paths. --- doc/bird.sgml | 17 +++++++++++++-- filter/filter.c | 29 +++++++++++++++++++++++++- filter/test.conf | 4 ++++ nest/a-path.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ nest/attrs.h | 2 ++ 5 files changed, 112 insertions(+), 3 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 7db9fad2..3cd80c32 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1033,10 +1033,23 @@ incompatible with each other (that is to prevent you from shooting in the foot). returns the length of path prepend( prepends ASN prepend( prepends ASN delete( deletes all instances of ASN + filter( deletes all ASNs from path + can be shortened to if from.type == T_INT)) + set = v2.val.t; + else + runtime("Can't delete non-integer (set)"); + + switch (what->aux) + { + case 'a': runtime("Can't add to path"); + case 'd': pos = 0; break; + case 'f': pos = 1; break; + default: bug("unknown Ca operation"); + } + + if (pos && !set) + runtime("Can't filter integer"); + + res.type = T_PATH; + res.val.ad = as_path_filter(f_pool, v1.val.ad, set, key, pos); + } + else if (v1.type == T_CLIST) { /* Community (or cluster) list */ struct f_val dummy; diff --git a/filter/test.conf b/filter/test.conf index 4f40abff..048983b5 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -104,6 +104,8 @@ eclist el2; print "Should be true: ", p2 ~ [= (3+2) (2*2) 3 2 1 =], " ", p2 ~ mkpath(5, 4); print "Should be true: ", p2.len = 5, " ", p2.first = 5, " ", p2.last = 1; print "5 = ", p2.len; + print "Delete 3: ", delete(p2, 3); + print "Filter 1-3: ", filter(p2, [1..3]); pm1 = [= 1 2 * 3 4 5 =]; p2 = prepend( + empty +, 5 ); @@ -113,6 +115,8 @@ eclist el2; p2 = prepend( p2, 2 ); p2 = prepend( p2, 1 ); print "Should be true: ", p2 ~ pm1, " ", p2, " ", pm1; + print "Delete 3: ", delete(p2, 3); + print "Delete 4-5: ", delete(p2, [4..5]); l = - empty -; print "Should be false in this special case: ", l ~ [(*,*)]; diff --git a/nest/a-path.c b/nest/a-path.c index 712e77a3..b1812981 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -287,6 +287,69 @@ as_path_match_set(struct adata *path, struct f_tree *set) return 0; } +struct adata * +as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos) +{ + if (!path) + return NULL; + + int len = path->length; + u8 *p = path->data; + u8 *q = path->data + len; + u8 *d, *d2; + int i, bt, sn, dn; + u8 buf[len]; + + d = buf; + while (p 0) + { + /* Nonempty block, set block header and advance */ + d[0] = bt; + d[1] = dn; + d = d2; + } + } + + int nl = d - buf; + if (nl == path->length) + return path; + + struct adata *res = lp_alloc(pool, sizeof(struct adata) + nl); + res->length = nl; + memcpy(res->data, buf, nl); + + return res; +} + struct pm_pos { diff --git a/nest/attrs.h b/nest/attrs.h index 12f2fcf4..44a23e18 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -37,6 +37,8 @@ int as_path_get_first(struct adata *path, u32 *orig_as); int as_path_get_last(struct adata *path, u32 *last_as); int as_path_is_member(struct adata *path, u32 as); int as_path_match_set(struct adata *path, struct f_tree *set); +struct adata *as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos); + #define PM_ASN 0 #define PM_QUESTION 1 -- cgit v1.2.3 From 1f64a487a065cc27c52ab0d3d38b7c82926fea70 Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Thu, 15 Aug 2013 13:29:33 +0200 Subject: Symbol names enclosed by apostrophes can contain colons. --- conf/cf-lex.l | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 50f390e0..b1bbeae2 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -172,7 +172,7 @@ else: { return ELSECOL; } -({ALPHA}{ALNUM}*|[']({ALNUM}|[-]|[\.])*[']) { +({ALPHA}{ALNUM}*|[']({ALNUM}|[-]|[\.]|[:])*[']) { if(*yytext == '\'') { yytext[yyleng-1] = 0; yytext++; -- cgit v1.2.3 From 6d90e57332e102e261d69a1a05dfaa19fb31d933 Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Thu, 15 Aug 2013 19:54:18 +0200 Subject: Typo in documentation fixed. --- doc/bird.sgml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 6bf443e1..a2266424 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1496,8 +1496,8 @@ for each neighbor using the following configuration parameters: route limit The maximal number of routes that may be imported from the protocol. If the route limit is - exceeded, the connection is closed with error. Limit is currently implemented as - disable after error When an error is encountered (either locally or by the other side), disable the instance automatically -- cgit v1.2.3 From e628cad0ca9eb7d9bf4141e57201169c46faa661 Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Thu, 15 Aug 2013 20:20:05 +0200 Subject: BGP option 'route limit' is marked as obsolete. 'import limit' should be used instead. --- doc/bird-6.html | 1731 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1731 insertions(+) create mode 100644 doc/bird-6.html diff --git a/doc/bird-6.html b/doc/bird-6.html new file mode 100644 index 00000000..d21209ee --- /dev/null +++ b/doc/bird-6.html @@ -0,0 +1,1731 @@ + + + + + BIRD User's Guide: Protocols + + + + + +Next +Previous +Contents +


+

6. Protocols

+ +

6.1 BGP +

+ +

The Border Gateway Protocol is the routing protocol used for backbone +level routing in the today's Internet. Contrary to the other protocols, its convergence +doesn't rely on all routers following the same rules for route selection, +making it possible to implement any routing policy at any router in the +network, the only restriction being that if a router advertises a route, +it must accept and forward packets according to it. +

+

BGP works in terms of autonomous systems (often abbreviated as +AS). Each AS is a part of the network with common management and +common routing policy. It is identified by a unique 16-bit number +(ASN). Routers within each AS usually exchange AS-internal routing +information with each other using an interior gateway protocol (IGP, +such as OSPF or RIP). Boundary routers at the border of +the AS communicate global (inter-AS) network reachability information with +their neighbors in the neighboring AS'es via exterior BGP (eBGP) and +redistribute received information to other routers in the AS via +interior BGP (iBGP). +

+

Each BGP router sends to its neighbors updates of the parts of its +routing table it wishes to export along with complete path information +(a list of AS'es the packet will travel through if it uses the particular +route) in order to avoid routing loops. +

+

BIRD supports all requirements of the BGP4 standard as defined in +RFC 4271 +ftp://ftp.rfc-editor.org/in-notes/rfc4271.txt +It also supports the community attributes +(RFC 1997 +ftp://ftp.rfc-editor.org/in-notes/rfc1997.txt), +capability negotiation +(RFC 3392 +ftp://ftp.rfc-editor.org/in-notes/rfc3392.txt), +MD5 password authentication +(RFC 2385 +ftp://ftp.rfc-editor.org/in-notes/rfc2385.txt), +extended communities +(RFC 4360 +ftp://ftp.rfc-editor.org/in-notes/rfc4360.txt), +route reflectors +(RFC 4456 +ftp://ftp.rfc-editor.org/in-notes/rfc4456.txt), +multiprotocol extensions +(RFC 4760 +ftp://ftp.rfc-editor.org/in-notes/rfc4760.txt), +4B AS numbers +(RFC 4893 +ftp://ftp.rfc-editor.org/in-notes/rfc4893.txt), +and 4B AS numbers in extended communities +(RFC 5668 +ftp://ftp.rfc-editor.org/in-notes/rfc5668.txt). +

+

For IPv6, it uses the standard multiprotocol extensions defined in +RFC 2283 +ftp://ftp.rfc-editor.org/in-notes/rfc2283.txt +including changes described in the +latest draft +ftp://ftp.rfc-editor.org/internet-drafts/draft-ietf-idr-bgp4-multiprotocol-v2-05.txt +and applied to IPv6 according to +RFC 2545 +ftp://ftp.rfc-editor.org/in-notes/rfc2545.txt. +

+

Route selection rules

+ +

BGP doesn't have any simple metric, so the rules for selection of an optimal +route among multiple BGP routes with the same preference are a bit more complex +and they are implemented according to the following algorithm. It starts the first +rule, if there are more "best" routes, then it uses the second rule to choose +among them and so on. +

+

    +
  • Prefer route with the highest Local Preference attribute.
  • +
  • Prefer route with the shortest AS path.
  • +
  • Prefer IGP origin over EGP and EGP origin over incomplete.
  • +
  • Prefer the lowest value of the Multiple Exit Discriminator.
  • +
  • Prefer routes received via eBGP over ones received via iBGP.
  • +
  • Prefer routes with lower internal distance to a boundary router.
  • +
  • Prefer the route with the lowest value of router ID of the +advertising router.
  • +
+

+

IGP routing table

+ +

BGP is mainly concerned with global network reachability and with +routes to other autonomous systems. When such routes are redistributed +to routers in the AS via BGP, they contain IP addresses of a boundary +routers (in route attribute NEXT_HOP). BGP depends on existing IGP +routing table with AS-internal routes to determine immediate next hops +for routes and to know their internal distances to boundary routers +for the purpose of BGP route selection. In BIRD, there is usually +one routing table used for both IGP routes and BGP routes. +

+

Configuration

+ +

Each instance of the BGP corresponds to one neighboring router. +This allows to set routing policy and all the other parameters differently +for each neighbor using the following configuration parameters: +

+

+
local [ip] as number

Define which AS we +are part of. (Note that contrary to other IP routers, BIRD is +able to act as a router located in multiple AS'es +simultaneously, but in such cases you need to tweak the BGP +paths manually in the filters to get consistent behavior.) +Optional ip argument specifies a source address, +equivalent to the source address option (see below). +This parameter is mandatory. +

+

neighbor ip as number

Define neighboring router +this instance will be talking to and what AS it's located in. Unless +you use the multihop clause, it must be directly connected to one +of your router's interfaces. In case the neighbor is in the same AS +as we are, we automatically switch to iBGP. This parameter is mandatory. +

+

multihop [number]

Configure multihop BGP +session to a neighbor that isn't directly connected. +Accurately, this option should be used if the configured +neighbor IP address does not match with any local network +subnets. Such IP address have to be reachable through system +routing table. For multihop BGP it is recommended to +explicitly configure source address to have it +stable. Optional number argument can be used to specify +the number of hops (used for TTL). Note that the number of +networks (edges) in a path is counted, i.e. if two BGP +speakers are separated by one router, the number of hops is +2. Default: switched off. +

+

source address ip

Define local address we +should use for next hop calculation and as a source address +for the BGP session. Default: the address of the local +end of the interface our neighbor is connected to. +

+

next hop self

Avoid calculation of the Next Hop +attribute and always advertise our own source address as a +next hop. This needs to be used only occasionally to +circumvent misconfigurations of other routers. Default: +disabled. +

+

next hop keep

Forward the received Next Hop +attribute even in situations where the local address should be +used instead, like when the route is sent to an interface with +a different subnet. Default: disabled. +

+

missing lladdr self|drop|ignore

Next Hop attribute +in BGP-IPv6 sometimes contains just the global IPv6 address, +but sometimes it has to contain both global and link-local +IPv6 addresses. This option specifies what to do if BIRD have +to send both addresses but does not know link-local address. +This situation might happen when routes from other protocols +are exported to BGP, or when improper updates are received +from BGP peers. self means that BIRD advertises its own +local address instead. drop means that BIRD skips that +prefixes and logs error. ignore means that BIRD ignores +the problem and sends just the global address (and therefore +forms improper BGP update). Default: self, unless BIRD +is configured as a route server (option rs client), in +that case default is ignore, because route servers usually +do not forward packets themselves. +

+

gateway direct|recursive

For received routes, their +gw (immediate next hop) attribute is computed from +received bgp_next_hop attribute. This option specifies +how it is computed. Direct mode means that the IP address from +bgp_next_hop is used if it is directly reachable, +otherwise the neighbor IP address is used. Recursive mode +means that the gateway is computed by an IGP routing table +lookup for the IP address from bgp_next_hop. Recursive +mode is the behavior specified by the BGP standard. Direct +mode is simpler, does not require any routes in a routing +table, and was used in older versions of BIRD, but does not +handle well nontrivial iBGP setups and multihop. Recursive +mode is incompatible with +sorted tables. Default: direct for singlehop eBGP, +recursive otherwise. +

+

igp table name

Specifies a table that is used +as an IGP routing table. Default: the same as the table BGP is +connected to. +

+

ttl security switch

Use GTSM (RFC 5082 - the +generalized TTL security mechanism). GTSM protects against +spoofed packets by ignoring received packets with a smaller +than expected TTL. To work properly, GTSM have to be enabled +on both sides of a BGP session. If both ttl security and +multihop options are enabled, multihop option should +specify proper hop value to compute expected TTL. Kernel +support required: Linux: 2.6.34+ (IPv4), 2.6.35+ (IPv6), BSD: +since long ago, IPv4 only. Note that full (ICMP protection, +for example) RFC 5082 support is provided by Linux +only. Default: disabled. +

+

password string

Use this password for MD5 authentication +of BGP sessions. Default: no authentication. Password has to be set by +external utility (e.g. setkey(8)) on BSD systems. +

+

passive switch

Standard BGP behavior is both +initiating outgoing connections and accepting incoming +connections. In passive mode, outgoing connections are not +initiated. Default: off. +

+

rr client

Be a route reflector and treat the neighbor as +a route reflection client. Default: disabled. +

+

rr cluster id IPv4 address

Route reflectors use cluster id +to avoid route reflection loops. When there is one route reflector in a cluster +it usually uses its router id as a cluster id, but when there are more route +reflectors in a cluster, these need to be configured (using this option) to +use a common cluster id. Clients in a cluster need not know their cluster +id and this option is not allowed for them. Default: the same as router id. +

+

rs client

Be a route server and treat the neighbor +as a route server client. A route server is used as a +replacement for full mesh EBGP routing in Internet exchange +points in a similar way to route reflectors used in IBGP routing. +BIRD does not implement obsoleted RFC 1863, but uses ad-hoc implementation, +which behaves like plain EBGP but reduces modifications to advertised route +attributes to be transparent (for example does not prepend its AS number to +AS PATH attribute and keeps MED attribute). Default: disabled. +

+

secondary switch

Usually, if an import filter +rejects a selected route, no other route is propagated for +that network. This option allows to try the next route in +order until one that is accepted is found or all routes for +that network are rejected. This can be used for route servers +that need to propagate different tables to each client but do +not want to have these tables explicitly (to conserve memory). +This option requires that the connected routing table is +sorted. Default: off. +

+

enable route refresh switch

When BGP speaker +changes its import filter, it has to re-examine all routes +received from its neighbor against the new filter. As these +routes might not be available, there is a BGP protocol +extension Route Refresh (specified in RFC 2918) that allows +BGP speaker to request re-advertisement of all routes from its +neighbor. This option specifies whether BIRD advertises this +capability and accepts such requests. Even when disabled, BIRD +can send route refresh requests. Default: on. +

+

interpret communities switch

RFC 1997 demands +that BGP speaker should process well-known communities like +no-export (65535, 65281) or no-advertise (65535, 65282). For +example, received route carrying a no-adverise community +should not be advertised to any of its neighbors. If this +option is enabled (which is by default), BIRD has such +behavior automatically (it is evaluated when a route is +exported to the BGP protocol just before the export filter). +Otherwise, this integrated processing of well-known +communities is disabled. In that case, similar behavior can be +implemented in the export filter. Default: on. +

+

enable as4 switch

BGP protocol was designed to use 2B AS numbers +and was extended later to allow 4B AS number. BIRD supports 4B AS extension, +but by disabling this option it can be persuaded not to advertise it and +to maintain old-style sessions with its neighbors. This might be useful for +circumventing bugs in neighbor's implementation of 4B AS extension. +Even when disabled (off), BIRD behaves internally as AS4-aware BGP router. +Default: on. +

+

capabilities switch

Use capability advertisement +to advertise optional capabilities. This is standard behavior +for newer BGP implementations, but there might be some older +BGP implementations that reject such connection attempts. +When disabled (off), features that request it (4B AS support) +are also disabled. Default: on, with automatic fallback to +off when received capability-related error. +

+

advertise ipv4 switch

Advertise IPv4 multiprotocol capability. +This is not a correct behavior according to the strict interpretation +of RFC 4760, but it is widespread and required by some BGP +implementations (Cisco and Quagga). This option is relevant +to IPv4 mode with enabled capability advertisement only. Default: on. +

+

route limit number

The maximal number of routes +that may be imported from the protocol. If the route limit is +exceeded, the connection is closed with an error. Limit is currently implemented as +import limit number action restart. This option is obsolete and it is +replaced by +import limit option. Default: no limit. +

+

disable after error switch

When an error is encountered (either +locally or by the other side), disable the instance automatically +and wait for an administrator to fix the problem manually. Default: off. +

+

hold time number

Time in seconds to wait for a Keepalive +message from the other side before considering the connection stale. +Default: depends on agreement with the neighboring router, we prefer +240 seconds if the other side is willing to accept it. +

+

startup hold time number

Value of the hold timer used +before the routers have a chance to exchange open messages and agree +on the real value. Default: 240 seconds. +

+

keepalive time number

Delay in seconds between sending +of two consecutive Keepalive messages. Default: One third of the hold time. +

+

connect retry time number

Time in seconds to wait before +retrying a failed attempt to connect. Default: 120 seconds. +

+

start delay time number

Delay in seconds between protocol +startup and the first attempt to connect. Default: 5 seconds. +

+

error wait time number,number

Minimum and maximum delay in seconds between a protocol +failure (either local or reported by the peer) and automatic restart. +Doesn't apply when disable after error is configured. If consecutive +errors happen, the delay is increased exponentially until it reaches the maximum. Default: 60, 300. +

+

error forget time number

Maximum time in seconds between two protocol +failures to treat them as a error sequence which makes the error wait time +increase exponentially. Default: 300 seconds. +

+

path metric switch

Enable comparison of path lengths +when deciding which BGP route is the best one. Default: on. +

+

med metric switch

Enable comparison of MED +attributes (during best route selection) even between routes +received from different ASes. This may be useful if all MED +attributes contain some consistent metric, perhaps enforced in +import filters of AS boundary routers. If this option is +disabled, MED attributes are compared only if routes are +received from the same AS (which is the standard behavior). +Default: off. +

+

deterministic med switch

BGP route selection +algorithm is often viewed as a comparison between individual +routes (e.g. if a new route appears and is better than the +current best one, it is chosen as the new best one). But the +proper route selection, as specified by RFC 4271, cannot be +fully implemented in that way. The problem is mainly in +handling the MED attribute. BIRD, by default, uses an +simplification based on individual route comparison, which in +some cases may lead to temporally dependent behavior (i.e. the +selection is dependent on the order in which routes appeared). +This option enables a different (and slower) algorithm +implementing proper RFC 4271 route selection, which is +deterministic. Alternative way how to get deterministic +behavior is to use med metric option. This option is +incompatible with +sorted tables. +Default: off. +

+

igp metric switch

Enable comparison of internal +distances to boundary routers during best route selection. Default: on. +

+

prefer older switch

Standard route selection algorithm +breaks ties by comparing router IDs. This changes the behavior +to prefer older routes (when both are external and from different +peer). For details, see RFC 5004. Default: off. +

+

default bgp_med number

Value of the Multiple Exit +Discriminator to be used during route selection when the MED attribute +is missing. Default: 0. +

+

default bgp_local_pref number

A default value +for the Local Preference attribute. It is used when a new +Local Preference attribute is attached to a route by the BGP +protocol itself (for example, if a route is received through +eBGP and therefore does not have such attribute). Default: 100 +(0 in pre-1.2.0 versions of BIRD). +

+

+

Attributes

+ +

BGP defines several route attributes. Some of them (those marked with `I' in the +table below) are available on internal BGP connections only, some of them (marked +with `O') are optional. +

+

+
bgppath bgp_path

Sequence of AS numbers describing the AS path +the packet will travel through when forwarded according to the particular route. +In case of internal BGP it doesn't contain the number of the local AS. +

+

int bgp_local_pref [I]

Local preference value used for +selection among multiple BGP routes (see the selection rules above). It's +used as an additional metric which is propagated through the whole local AS. +

+

int bgp_med [O]

The Multiple Exit Discriminator of the route +is an optional attribute which is used on external (inter-AS) links to +convey to an adjacent AS the optimal entry point into the local AS. +The received attribute is also propagated over internal BGP links. +The attribute value is zeroed when a route is exported to an external BGP +instance to ensure that the attribute received from a neighboring AS is +not propagated to other neighboring ASes. A new value might be set in +the export filter of an external BGP instance. +See RFC 4451 +ftp://ftp.rfc-editor.org/in-notes/rfc4451.txt +for further discussion of BGP MED attribute. +

+

enum bgp_origin

Origin of the route: either ORIGIN_IGP +if the route has originated in an interior routing protocol or +ORIGIN_EGP if it's been imported from the EGP protocol +(nowadays it seems to be obsolete) or ORIGIN_INCOMPLETE if the origin +is unknown. +

+

ip bgp_next_hop

Next hop to be used for forwarding of packets +to this destination. On internal BGP connections, it's an address of the +originating router if it's inside the local AS or a boundary router the +packet will leave the AS through if it's an exterior route, so each BGP +speaker within the AS has a chance to use the shortest interior path +possible to this point. +

+

void bgp_atomic_aggr [O]

This is an optional attribute +which carries no value, but the sole presence of which indicates that the route +has been aggregated from multiple routes by some router on the path from +the originator. +

+

clist bgp_community [O]

List of community values associated +with the route. Each such value is a pair (represented as a pair data +type inside the filters) of 16-bit integers, the first of them containing the number of the AS which defines +the community and the second one being a per-AS identifier. There are lots +of uses of the community mechanism, but generally they are used to carry +policy information like "don't export to USA peers". As each AS can define +its own routing policy, it also has a complete freedom about which community +attributes it defines and what will their semantics be. +

+

eclist bgp_ext_community [O]

List of extended community +values associated with the route. Extended communities have similar usage +as plain communities, but they have an extended range (to allow 4B ASNs) +and a nontrivial structure with a type field. Individual community values are +represented using an ec data type inside the filters. +

+

quad bgp_originator_id [I, O]

This attribute is created by the +route reflector when reflecting the route and contains the router ID of the +originator of the route in the local AS. +

+

clist bgp_cluster_list [I, O]

This attribute contains a list +of cluster IDs of route reflectors. Each route reflector prepends its +cluster ID when reflecting the route. +

+

+

Example

+ +

+


+
+protocol bgp {
+        local as 65000;                      # Use a private AS number
+        neighbor 198.51.100.130 as 64496;    # Our neighbor ...
+        multihop;                            # ... which is connected indirectly
+        export filter {                      # We use non-trivial export rules
+                if source = RTS_STATIC then { # Export only static routes
+                        # Assign our community
+                        bgp_community.add((65000,64501));
+                        # Artificially increase path length
+                        # by advertising local AS number twice
+                        if bgp_path ~ [= 65000 =] then
+                                bgp_path.prepend(65000);
+                        accept;
+                }
+                reject;
+        };
+        import all;
+        source address 198.51.100.14;   # Use a non-standard source address
+}
+
+
+

+

6.2 Device +

+ +

The Device protocol is not a real routing protocol. It doesn't generate +any routes and it only serves as a module for getting information about network +interfaces from the kernel. +

+

Except for very unusual circumstances, you probably should include +this protocol in the configuration since almost all other protocols +require network interfaces to be defined for them to work with. +

+

Configuration

+ +

+

+
scan time number

Time in seconds between two scans +of the network interface list. On systems where we are notified about +interface status changes asynchronously (such as newer versions of +Linux), we need to scan the list only in order to avoid confusion by lost +notification messages, so the default time is set to a large value. +

+

primary [ "mask" ] prefix

If a network interface has more than one network address, BIRD +has to choose one of them as a primary one. By default, BIRD +chooses the lexicographically smallest address as the primary +one. +

This option allows to specify which network address should be +chosen as a primary one. Network addresses that match +prefix are preferred to non-matching addresses. If more +primary options are used, the first one has the highest +preference. If "mask" is specified, then such +primary option is relevant only to matching network +interfaces. +

In all cases, an address marked by operating system as +secondary cannot be chosen as the primary one. +

+

+

As the Device protocol doesn't generate any routes, it cannot have +any attributes. Example configuration looks like this: +

+

+


+
+protocol device {
+        scan time 10;           # Scan the interfaces often
+        primary "eth0" 192.168.1.1;
+        primary 192.168.0.0/16;
+}
+
+
+

+

6.3 Direct +

+ +

The Direct protocol is a simple generator of device routes for all the +directly connected networks according to the list of interfaces provided +by the kernel via the Device protocol. +

+

The question is whether it is a good idea to have such device +routes in BIRD routing table. OS kernel usually handles device routes +for directly connected networks by itself so we don't need (and don't +want) to export these routes to the kernel protocol. OSPF protocol +creates device routes for its interfaces itself and BGP protocol is +usually used for exporting aggregate routes. Although there are some +use cases that use the direct protocol (like abusing eBGP as an IGP +routing protocol), in most cases it is not needed to have these device +routes in BIRD routing table and to use the direct protocol. +

+

There is one notable case when you definitely want to use the +direct protocol -- running BIRD on BSD systems. Having high priority +device routes for directly connected networks from the direct protocol +protects kernel device routes from being overwritten or removed by IGP +routes during some transient network conditions, because a lower +priority IGP route for the same network is not exported to the kernel +routing table. This is an issue on BSD systems only, as on Linux +systems BIRD cannot change non-BIRD route in the kernel routing table. +

+

The only configurable thing about direct is what interfaces it watches: +

+

+

+
interface pattern [, ...]

By default, the Direct +protocol will generate device routes for all the interfaces +available. If you want to restrict it to some subset of interfaces +(for example if you're using multiple routing tables for policy +routing and some of the policy domains don't contain all interfaces), +just use this clause. +

+

+

Direct device routes don't contain any specific attributes. +

+

Example config might look like this: +

+

+


+
+protocol direct {
+        interface "-arc*", "*";         # Exclude the ARCnets
+}
+
+
+

+

6.4 Kernel +

+ +

The Kernel protocol is not a real routing protocol. Instead of communicating +with other routers in the network, it performs synchronization of BIRD's routing +tables with the OS kernel. Basically, it sends all routing table updates to the kernel +and from time to time it scans the kernel tables to see whether some routes have +disappeared (for example due to unnoticed up/down transition of an interface) +or whether an `alien' route has been added by someone else (depending on the +learn switch, such routes are either ignored or accepted to our +table). +

+

Unfortunately, there is one thing that makes the routing table +synchronization a bit more complicated. In the kernel routing table +there are also device routes for directly connected networks. These +routes are usually managed by OS itself (as a part of IP address +configuration) and we don't want to touch that. They are completely +ignored during the scan of the kernel tables and also the export of +device routes from BIRD tables to kernel routing tables is restricted +to prevent accidental interference. This restriction can be disabled using +device routes switch. +

+

If your OS supports only a single routing table, you can configure +only one instance of the Kernel protocol. If it supports multiple +tables (in order to allow policy routing; such an OS is for example +Linux), you can run as many instances as you want, but each of them +must be connected to a different BIRD routing table and to a different +kernel table. +

+

Because the kernel protocol is partially integrated with the +connected routing table, there are two limitations - it is not +possible to connect more kernel protocols to the same routing table +and changing route destination/gateway in an export +filter of a kernel protocol does not work. Both limitations can be +overcome using another routing table and the pipe protocol. +

+

Configuration

+ +

+

+
persist switch

Tell BIRD to leave all its routes in the +routing tables when it exits (instead of cleaning them up). +

scan time number

Time in seconds between two consecutive scans of the +kernel routing table. +

learn switch

Enable learning of routes added to the kernel +routing tables by other routing daemons or by the system administrator. +This is possible only on systems which support identification of route +authorship. +

+

device routes switch

Enable export of device +routes to the kernel routing table. By default, such routes +are rejected (with the exception of explicitly configured +device routes from the static protocol) regardless of the +export filter to protect device routes in kernel routing table +(managed by OS itself) from accidental overwriting or erasing. +

+

kernel table number

Select which kernel table should +this particular instance of the Kernel protocol work with. Available +only on systems supporting multiple routing tables. +

+

+

Attributes

+ +

The Kernel protocol defines several attributes. These attributes +are translated to appropriate system (and OS-specific) route attributes. +We support these attributes: +

+

+
int krt_source

The original source of the imported +kernel route. The value is system-dependent. On Linux, it is +a value of the protocol field of the route. See +/etc/iproute2/rt_protos for common values. On BSD, it is +based on STATIC and PROTOx flags. The attribute is read-only. +

+

int krt_metric

The kernel metric of +the route. When multiple same routes are in a kernel routing +table, the Linux kernel chooses one with lower metric. +

+

ip krt_prefsrc

(Linux) The preferred source address. +Used in source address selection for outgoing packets. Have to +be one of IP addresses of the router. +

+

int krt_realm

(Linux) The realm of the route. Can be +used for traffic classification. +

+

+

Example

+ +

A simple configuration can look this way: +

+

+


+
+protocol kernel {
+        export all;
+}
+
+
+

+

Or for a system with two routing tables: +

+

+


+
+protocol kernel {               # Primary routing table
+        learn;                  # Learn alien routes from the kernel
+        persist;                # Don't remove routes on bird shutdown
+        scan time 10;           # Scan kernel routing table every 10 seconds
+        import all;
+        export all;
+}
+
+protocol kernel {               # Secondary routing table
+        table auxtable;
+        kernel table 100;
+        export all;
+}
+
+
+

+

6.5 OSPF +

+ +

Introduction

+ +

Open Shortest Path First (OSPF) is a quite complex interior gateway +protocol. The current IPv4 version (OSPFv2) is defined in RFC +2328 +ftp://ftp.rfc-editor.org/in-notes/rfc2328.txt and +the current IPv6 version (OSPFv3) is defined in RFC 5340 +ftp://ftp.rfc-editor.org/in-notes/rfc5340.txt It's a link state +(a.k.a. shortest path first) protocol -- each router maintains a +database describing the autonomous system's topology. Each participating +router has an identical copy of the database and all routers run the +same algorithm calculating a shortest path tree with themselves as a +root. OSPF chooses the least cost path as the best path. +

+

In OSPF, the autonomous system can be split to several areas in order +to reduce the amount of resources consumed for exchanging the routing +information and to protect the other areas from incorrect routing data. +Topology of the area is hidden to the rest of the autonomous system. +

+

Another very important feature of OSPF is that +it can keep routing information from other protocols (like Static or BGP) +in its link state database as external routes. Each external route can +be tagged by the advertising router, making it possible to pass additional +information between routers on the boundary of the autonomous system. +

+

OSPF quickly detects topological changes in the autonomous system (such +as router interface failures) and calculates new loop-free routes after a short +period of convergence. Only a minimal amount of +routing traffic is involved. +

+

Each router participating in OSPF routing periodically sends Hello messages +to all its interfaces. This allows neighbors to be discovered dynamically. +Then the neighbors exchange theirs parts of the link state database and keep it +identical by flooding updates. The flooding process is reliable and ensures +that each router detects all changes. +

+

Configuration

+ +

In the main part of configuration, there can be multiple definitions of +OSPF areas, each with a different id. These definitions includes many other +switches and multiple definitions of interfaces. Definition of interface +may contain many switches and constant definitions and list of neighbors +on nonbroadcast networks. +

+


+
+protocol ospf <name> {
+        rfc1583compat <switch>;
+        stub router <switch>;
+        tick <num>;
+        ecmp <switch> [limit <num>];
+        area <id> {
+                stub;
+                nssa;
+                summary <switch>;
+                default nssa <switch>;
+                default cost <num>;
+                default cost2 <num>;
+                translator <switch>;
+                translator stability <num>;
+
+                networks {
+                        <prefix>;
+                        <prefix> hidden;
+                }
+                external {
+                        <prefix>;
+                        <prefix> hidden;
+                        <prefix> tag <num>;
+                }
+                stubnet <prefix>;
+                stubnet <prefix> {
+                        hidden <switch>;
+                        summary <switch>;
+                        cost <num>;
+                }
+                interface <interface pattern> [instance <num>] {
+                        cost <num>;
+                        stub <switch>;
+                        hello <num>;
+                        poll <num>;
+                        retransmit <num>;
+                        priority <num>;
+                        wait <num>;
+                        dead count <num>;
+                        dead <num>;
+                        rx buffer [normal|large|<num>];
+                        type [broadcast|bcast|pointopoint|ptp|
+                                nonbroadcast|nbma|pointomultipoint|ptmp];
+                        strict nonbroadcast <switch>;
+                        real broadcast <switch>;
+                        ptp netmask <switch>;
+                        check link <switch>;
+                        ecmp weight <num>;
+                        ttl security [<switch>; | tx only]
+                        tx class|dscp <num>;
+                        tx priority <num>;
+                        authentication [none|simple|cryptographic];
+                        password "<text>";
+                        password "<text>" {
+                                id <num>;
+                                generate from "<date>";
+                                generate to "<date>";
+                                accept from "<date>";
+                                accept to "<date>";
+                        };
+                        neighbors {
+                                <ip>;
+                                <ip> eligible;
+                        };
+                };
+                virtual link <id> [instance <num>] {
+                        hello <num>;
+                        retransmit <num>;
+                        wait <num>;
+                        dead count <num>;
+                        dead <num>;
+                        authentication [none|simple|cryptographic];
+                        password "<text>";
+                };
+        };
+}
+
+
+

+

+
rfc1583compat switch

This option controls compatibility of routing table +calculation with RFC 1583 +ftp://ftp.rfc-editor.org/in-notes/rfc1583.txt. Default +value is no. +

+

stub router switch

This option configures the router to be a stub router, i.e., +a router that participates in the OSPF topology but does not +allow transit traffic. In OSPFv2, this is implemented by +advertising maximum metric for outgoing links, as suggested +by RFC 3137 +ftp://ftp.rfc-editor.org/in-notes/rfc3137.txt. +In OSPFv3, the stub router behavior is announced by clearing +the R-bit in the router LSA. Default value is no. +

+

tick num

The routing table calculation and clean-up of areas' databases +is not performed when a single link state +change arrives. To lower the CPU utilization, it's processed later +at periodical intervals of num seconds. The default value is 1. +

+

ecmp switch [limit number]

This option specifies whether OSPF is allowed to generate +ECMP (equal-cost multipath) routes. Such routes are used when +there are several directions to the destination, each with +the same (computed) cost. This option also allows to specify +a limit on maximal number of nexthops in one route. By +default, ECMP is disabled. If enabled, default value of the +limit is 16. +

+

area id

This defines an OSPF area with given area ID (an integer or an IPv4 +address, similarly to a router ID). The most important area is +the backbone (ID 0) to which every other area must be connected. +

+

stub

This option configures the area to be a stub area. External +routes are not flooded into stub areas. Also summary LSAs can be +limited in stub areas (see option summary). +By default, the area is not a stub area. +

+

nssa

This option configures the area to be a NSSA (Not-So-Stubby +Area). NSSA is a variant of a stub area which allows a +limited way of external route propagation. Global external +routes are not propagated into a NSSA, but an external route +can be imported into NSSA as a (area-wide) NSSA-LSA (and +possibly translated and/or aggregated on area boundary). +By default, the area is not NSSA. +

+

summary switch

This option controls propagation of summary LSAs into stub or +NSSA areas. If enabled, summary LSAs are propagated as usual, +otherwise just the default summary route (0.0.0.0/0) is +propagated (this is sometimes called totally stubby area). If +a stub area has more area boundary routers, propagating +summary LSAs could lead to more efficient routing at the cost +of larger link state database. Default value is no. +

+

default nssa switch

When summary option is enabled, default summary route is +no longer propagated to the NSSA. In that case, this option +allows to originate default route as NSSA-LSA to the NSSA. +Default value is no. +

+

default cost num

This option controls the cost of a default route propagated to +stub and NSSA areas. Default value is 1000. +

+

default cost2 num

When a default route is originated as NSSA-LSA, its cost +can use either type 1 or type 2 metric. This option allows +to specify the cost of a default route in type 2 metric. +By default, type 1 metric (option default cost) is used. +

+

translator switch

This option controls translation of NSSA-LSAs into external +LSAs. By default, one translator per NSSA is automatically +elected from area boundary routers. If enabled, this area +boundary router would unconditionally translate all NSSA-LSAs +regardless of translator election. Default value is no. +

+

translator stability num

This option controls the translator stability interval (in +seconds). When the new translator is elected, the old one +keeps translating until the interval is over. Default value +is 40. +

+

networks { set }

Definition of area IP ranges. This is used in summary LSA origination. +Hidden networks are not propagated into other areas. +

+

external { set }

Definition of external area IP ranges for NSSAs. This is used +for NSSA-LSA translation. Hidden networks are not translated +into external LSAs. Networks can have configured route tag. +

+

stubnet prefix { options }

Stub networks are networks that are not transit networks +between OSPF routers. They are also propagated through an +OSPF area as a part of a link state database. By default, +BIRD generates a stub network record for each primary network +address on each OSPF interface that does not have any OSPF +neighbors, and also for each non-primary network address on +each OSPF interface. This option allows to alter a set of +stub networks propagated by this router. +

Each instance of this option adds a stub network with given +network prefix to the set of propagated stub network, unless +option hidden is used. It also suppresses default stub +networks for given network prefix. When option +summary is used, also default stub networks that are +subnetworks of given stub network are suppressed. This might +be used, for example, to aggregate generated stub networks. +

+

interface pattern [instance num]

Defines that the specified interfaces belong to the area being defined. +See +interface common option for detailed description. +In OSPFv3, you can specify instance ID for that interface +description, so it is possible to have several instances of +that interface with different options or even in different areas. +

+

virtual link id [instance num]

Virtual link to router with the router id. Virtual link acts +as a point-to-point interface belonging to backbone. The +actual area is used as transport area. This item cannot be in +the backbone. In OSPFv3, you could also use several virtual +links to one destination with different instance IDs. +

+

cost num

Specifies output cost (metric) of an interface. Default value is 10. +

+

stub switch

If set to interface it does not listen to any packet and does not send +any hello. Default value is no. +

+

hello num

Specifies interval in seconds between sending of Hello messages. Beware, all +routers on the same network need to have the same hello interval. +Default value is 10. +

+

poll num

Specifies interval in seconds between sending of Hello messages for +some neighbors on NBMA network. Default value is 20. +

+

retransmit num

Specifies interval in seconds between retransmissions of unacknowledged updates. +Default value is 5. +

+

priority num

On every multiple access network (e.g., the Ethernet) Designed Router +and Backup Designed router are elected. These routers have some +special functions in the flooding process. Higher priority increases +preferences in this election. Routers with priority 0 are not +eligible. Default value is 1. +

+

wait num

After start, router waits for the specified number of seconds between starting +election and building adjacency. Default value is 40. +

+

dead count num

When the router does not receive any messages from a neighbor in +dead count*hello seconds, it will consider the neighbor down. +

+

dead num

When the router does not receive any messages from a neighbor in +dead seconds, it will consider the neighbor down. If both directives +dead count and dead are used, dead has precendence. +

+

rx buffer num

This sets the size of buffer used for receiving packets. The buffer should +be bigger than maximal size of any packets. Value NORMAL (default) +means 2*MTU, value LARGE means maximal allowed packet - 65535. +

+

type broadcast|bcast

BIRD detects a type of a connected network automatically, but +sometimes it's convenient to force use of a different type +manually. On broadcast networks (like ethernet), flooding +and Hello messages are sent using multicasts (a single packet +for all the neighbors). A designated router is elected and it +is responsible for synchronizing the link-state databases and +originating network LSAs. This network type cannot be used on +physically NBMA networks and on unnumbered networks (networks +without proper IP prefix). +

+

type pointopoint|ptp

Point-to-point networks connect just 2 routers together. No +election is performed and no network LSA is originated, which +makes it simpler and faster to establish. This network type +is useful not only for physically PtP ifaces (like PPP or +tunnels), but also for broadcast networks used as PtP links. +This network type cannot be used on physically NBMA networks. +

+

type nonbroadcast|nbma

On NBMA networks, the packets are sent to each neighbor +separately because of lack of multicast capabilities. +Like on broadcast networks, a designated router is elected, +which plays a central role in propagation of LSAs. +This network type cannot be used on unnumbered networks. +

+

type pointomultipoint|ptmp

This is another network type designed to handle NBMA +networks. In this case the NBMA network is treated as a +collection of PtP links. This is useful if not every pair of +routers on the NBMA network has direct communication, or if +the NBMA network is used as an (possibly unnumbered) PtP +link. +

+

strict nonbroadcast switch

If set, don't send hello to any undefined neighbor. This switch +is ignored on other than NBMA or PtMP networks. Default value is no. +

+

real broadcast switch

In type broadcast or type ptp network +configuration, OSPF packets are sent as IP multicast +packets. This option changes the behavior to using +old-fashioned IP broadcast packets. This may be useful as a +workaround if IP multicast for some reason does not work or +does not work reliably. This is a non-standard option and +probably is not interoperable with other OSPF +implementations. Default value is no. +

+

ptp netmask switch

In type ptp network configurations, OSPFv2 +implementations should ignore received netmask field in hello +packets and should send hello packets with zero netmask field +on unnumbered PtP links. But some OSPFv2 implementations +perform netmask checking even for PtP links. This option +specifies whether real netmask will be used in hello packets +on type ptp interfaces. You should ignore this option +unless you meet some compatibility problems related to this +issue. Default value is no for unnumbered PtP links, yes +otherwise. +

+

check link switch

If set, a hardware link state (reported by OS) is taken into +consideration. When a link disappears (e.g. an ethernet cable is +unplugged), neighbors are immediately considered unreachable +and only the address of the iface (instead of whole network +prefix) is propagated. It is possible that some hardware +drivers or platforms do not implement this feature. Default value is no. +

+

ttl security [switch | tx only]

TTL security is a feature that protects routing protocols +from remote spoofed packets by using TTL 255 instead of TTL 1 +for protocol packets destined to neighbors. Because TTL is +decremented when packets are forwarded, it is non-trivial to +spoof packets with TTL 255 from remote locations. Note that +this option would interfere with OSPF virtual links. +

If this option is enabled, the router will send OSPF packets +with TTL 255 and drop received packets with TTL less than +255. If this option si set to tx only, TTL 255 is used +for sent packets, but is not checked for received +packets. Default value is no. +

+

tx class|dscp|priority num

These options specify the ToS/DiffServ/Traffic class/Priority +of the outgoing OSPF packets. See +tx class common option for detailed description. +

+

ecmp weight num

When ECMP (multipath) routes are allowed, this value specifies +a relative weight used for nexthops going through the iface. +Allowed values are 1-256. Default value is 1. +

+

authentication none

No passwords are sent in OSPF packets. This is the default value. +

+

authentication simple

Every packet carries 8 bytes of password. Received packets +lacking this password are ignored. This authentication mechanism is +very weak. +

+

authentication cryptographic

16-byte long MD5 digest is appended to every packet. For the digest +generation 16-byte long passwords are used. Those passwords are +not sent via network, so this mechanism is quite secure. +Packets can still be read by an attacker. +

+

password "text"

An 8-byte or 16-byte password used for authentication. +See +password common option for detailed description. +

+

neighbors { set }

A set of neighbors to which Hello messages on NBMA or PtMP +networks are to be sent. For NBMA networks, some of them +could be marked as eligible. In OSPFv3, link-local addresses +should be used, using global ones is possible, but it is +nonstandard and might be problematic. And definitely, +link-local and global addresses should not be mixed. +

+

+

+

Attributes

+ +

OSPF defines four route attributes. Each internal route has a metric. +Metric is ranging from 1 to infinity (65535). +External routes use metric type 1 or metric type 2. +A metric of type 1 is comparable with internal metric, a +metric of type 2 is always longer +than any metric of type 1 or any internal metric. +Internal metric or metric of type 1 is stored in attribute +ospf_metric1, metric type 2 is stored in attribute ospf_metric2. +If you specify both metrics only metric1 is used. +

Each external route can also carry attribute ospf_tag which is a +32-bit integer which is used when exporting routes to other protocols; +otherwise, it doesn't affect routing inside the OSPF domain at all. +The fourth attribute ospf_router_id is a router ID of the router +advertising that route/network. This attribute is read-only. Default +is ospf_metric2 = 10000 and ospf_tag = 0. +

+

Example

+ +

+

+


+
+protocol ospf MyOSPF {
+        rfc1583compat yes;
+        tick 2;
+        export filter {
+                if source = RTS_BGP then {
+                        ospf_metric1 = 100;
+                        accept;
+                }
+                reject;
+        };
+        area 0.0.0.0 {
+                interface "eth*" {
+                        cost 11;
+                        hello 15;
+                        priority 100;
+                        retransmit 7;
+                        authentication simple;
+                        password "aaa";
+                };
+                interface "ppp*" {
+                        cost 100;
+                        authentication cryptographic;
+                        password "abc" {
+                                id 1;
+                                generate to "22-04-2003 11:00:06";
+                                accept from "17-01-2001 12:01:05";
+                        };
+                        password "def" {
+                                id 2;
+                                generate to "22-07-2005 17:03:21";
+                                accept from "22-02-2001 11:34:06";
+                        };
+                };
+                interface "arc0" {
+                        cost 10;
+                        stub yes;
+                };
+                interface "arc1";
+        };
+        area 120 {
+                stub yes;
+                networks {
+                        172.16.1.0/24;
+                        172.16.2.0/24 hidden;
+                }
+                interface "-arc0" , "arc*" {
+                        type nonbroadcast;
+                        authentication none;
+                        strict nonbroadcast yes;
+                        wait 120;
+                        poll 40;
+                        dead count 8;
+                        neighbors {
+                                192.168.120.1 eligible;
+                                192.168.120.2;
+                                192.168.120.10;
+                        };
+                };
+        };
+}
+
+
+

+

6.6 Pipe +

+ +

Introduction

+ +

The Pipe protocol serves as a link between two routing tables, allowing routes to be +passed from a table declared as primary (i.e., the one the pipe is connected to using the +table configuration keyword) to the secondary one (declared using peer table) +and vice versa, depending on what's allowed by the filters. Export filters control export +of routes from the primary table to the secondary one, import filters control the opposite +direction. +

+

The Pipe protocol may work in the transparent mode mode or in the opaque mode. +In the transparent mode, the Pipe protocol retransmits all routes from +one table to the other table, retaining their original source and +attributes. If import and export filters are set to accept, then both +tables would have the same content. The transparent mode is the default mode. +

+

In the opaque mode, the Pipe protocol retransmits optimal route +from one table to the other table in a similar way like other +protocols send and receive routes. Retransmitted route will have the +source set to the Pipe protocol, which may limit access to protocol +specific route attributes. This mode is mainly for compatibility, it +is not suggested for new configs. The mode can be changed by +mode option. +

+

The primary use of multiple routing tables and the Pipe protocol is for policy routing, +where handling of a single packet doesn't depend only on its destination address, but also +on its source address, source interface, protocol type and other similar parameters. +In many systems (Linux being a good example), the kernel allows to enforce routing policies +by defining routing rules which choose one of several routing tables to be used for a packet +according to its parameters. Setting of these rules is outside the scope of BIRD's work +(on Linux, you can use the ip command), but you can create several routing tables in BIRD, +connect them to the kernel ones, use filters to control which routes appear in which tables +and also you can employ the Pipe protocol for exporting a selected subset of one table to +another one. +

+

Configuration

+ +

+

+
peer table table

Defines secondary routing table to connect to. The +primary one is selected by the table keyword. +

+

mode opaque|transparent

Specifies the mode for the pipe to work in. Default is transparent. +

+

+

Attributes

+ +

The Pipe protocol doesn't define any route attributes. +

+

Example

+ +

Let's consider a router which serves as a boundary router of two different autonomous +systems, each of them connected to a subset of interfaces of the router, having its own +exterior connectivity and wishing to use the other AS as a backup connectivity in case +of outage of its own exterior line. +

+

Probably the simplest solution to this situation is to use two routing tables (we'll +call them as1 and as2) and set up kernel routing rules, so that packets having +arrived from interfaces belonging to the first AS will be routed according to as1 +and similarly for the second AS. Thus we have split our router to two logical routers, +each one acting on its own routing table, having its own routing protocols on its own +interfaces. In order to use the other AS's routes for backup purposes, we can pass +the routes between the tables through a Pipe protocol while decreasing their preferences +and correcting their BGP paths to reflect the AS boundary crossing. +

+


+
+table as1;                              # Define the tables
+table as2;
+
+protocol kernel kern1 {                 # Synchronize them with the kernel
+        table as1;
+        kernel table 1;
+}
+
+protocol kernel kern2 {
+        table as2;
+        kernel table 2;
+}
+
+protocol bgp bgp1 {                     # The outside connections
+        table as1;
+        local as 1;
+        neighbor 192.168.0.1 as 1001;
+        export all;
+        import all;
+}
+
+protocol bgp bgp2 {
+        table as2;
+        local as 2;
+        neighbor 10.0.0.1 as 1002;
+        export all;
+        import all;
+}
+
+protocol pipe {                         # The Pipe
+        table as1;
+        peer table as2;
+        export filter {
+                if net ~ [ 1.0.0.0/8+] then {   # Only AS1 networks
+                        if preference>10 then preference = preference-10;
+                        if source=RTS_BGP then bgp_path.prepend(1);
+                        accept;
+                }
+                reject;
+        };
+        import filter {
+                if net ~ [ 2.0.0.0/8+] then {   # Only AS2 networks
+                        if preference>10 then preference = preference-10;
+                        if source=RTS_BGP then bgp_path.prepend(2);
+                        accept;
+                }
+                reject;
+        };
+}
+
+
+

+

6.7 RAdv +

+ +

Introduction

+ +

The RAdv protocol is an implementation of Router Advertisements, +which are used in the IPv6 stateless autoconfiguration. IPv6 routers +send (in irregular time intervals or as an answer to a request) +advertisement packets to connected networks. These packets contain +basic information about a local network (e.g. a list of network +prefixes), which allows network hosts to autoconfigure network +addresses and choose a default route. BIRD implements router behavior +as defined in +RFC 4861 +ftp://ftp.rfc-editor.org/in-notes/rfc4861.txt +and also the DNS extensions from +RFC 6106 +ftp://ftp.rfc-editor.org/in-notes/rfc6106.txt. +

+

Configuration

+ +

There are several classes of definitions in RAdv configuration -- +interface definitions, prefix definitions and DNS definitions: +

+

+
interface pattern [, ...] { options }

Interface definitions specify a set of interfaces on which the +protocol is activated and contain interface specific options. +See +interface common options for +detailed description. +

+

prefix prefix { options }

Prefix definitions allow to modify a list of advertised +prefixes. By default, the advertised prefixes are the same as +the network prefixes assigned to the interface. For each +network prefix, the matching prefix definition is found and +its options are used. If no matching prefix definition is +found, the prefix is used with default options. +

Prefix definitions can be either global or interface-specific. +The second ones are part of interface options. The prefix +definition matching is done in the first-match style, when +interface-specific definitions are processed before global +definitions. As expected, the prefix definition is matching if +the network prefix is a subnet of the prefix in prefix +definition. +

+

rdnss { options }

RDNSS definitions allow to specify a list of advertised +recursive DNS servers together with their options. As options +are seldom necessary, there is also a short variant rdnss +address that just specifies one DNS server. Multiple +definitions are cumulative. RDNSS definitions may also be +interface-specific when used inside interface options. By +default, interface uses both global and interface-specific +options, but that can be changed by rdnss local option. +

+

dnssl { options }

DNSSL definitions allow to specify a list of advertised DNS +search domains together with their options. Like rdnss +above, multiple definitions are cumulative, they can be used +also as interface-specific options and there is a short +variant dnssl domain that just specifies one DNS +search domain. +

+ +

trigger prefix

RAdv protocol could be configured to change its behavior based +on availability of routes. When this option is used, the +protocol waits in suppressed state until a trigger route +(for the specified network) is exported to the protocol, the +protocol also returnsd to suppressed state if the +trigger route disappears. Note that route export depends +on specified export filter, as usual. This option could be +used, e.g., for handling failover in multihoming scenarios. +

During suppressed state, router advertisements are generated, +but with some fields zeroed. Exact behavior depends on which +fields are zeroed, this can be configured by +sensitive option for appropriate fields. By default, just +default lifetime (also called router lifetime) is +zeroed, which means hosts cannot use the router as a default +router. preferred lifetime and valid lifetime could +also be configured as sensitive for a prefix, which would +cause autoconfigured IPs to be deprecated or even removed. +

+

+

Interface specific options: +

+

+
max ra interval expr

Unsolicited router advertisements are sent in irregular time +intervals. This option specifies the maximum length of these +intervals, in seconds. Valid values are 4-1800. Default: 600 +

+

min ra interval expr

This option specifies the minimum length of that intervals, in +seconds. Must be at least 3 and at most 3/4 * max ra interval. +Default: about 1/3 * max ra interval. +

+

min delay expr

The minimum delay between two consecutive router advertisements, +in seconds. Default: 3 +

+

managed switch

This option specifies whether hosts should use DHCPv6 for +IP address configuration. Default: no +

+

other config switch

This option specifies whether hosts should use DHCPv6 to +receive other configuration information. Default: no +

+

link mtu expr

This option specifies which value of MTU should be used by +hosts. 0 means unspecified. Default: 0 +

+

reachable time expr

This option specifies the time (in milliseconds) how long +hosts should assume a neighbor is reachable (from the last +confirmation). Maximum is 3600000, 0 means unspecified. +Default 0. +

+

retrans timer expr

This option specifies the time (in milliseconds) how long +hosts should wait before retransmitting Neighbor Solicitation +messages. 0 means unspecified. Default 0. +

+

current hop limit expr

This option specifies which value of Hop Limit should be used +by hosts. Valid values are 0-255, 0 means unspecified. Default: 64 +

+

default lifetime expr [sensitive switch]

This option specifies the time (in seconds) how long (after +the receipt of RA) hosts may use the router as a default +router. 0 means do not use as a default router. For +sensitive option, see +trigger. +Default: 3 * max ra interval, sensitive yes. +

+

rdnss local switch

Use only local (interface-specific) RDNSS definitions for this +interface. Otherwise, both global and local definitions are +used. Could also be used to disable RDNSS for given interface +if no local definitons are specified. Default: no. +

+

dnssl local switch

Use only local DNSSL definitions for this interface. See +rdnss local option above. Default: no. +

+

+

+

Prefix specific options: +

+

+
skip switch

This option allows to specify that given prefix should not be +advertised. This is useful for making exceptions from a +default policy of advertising all prefixes. Note that for +withdrawing an already advertised prefix it is more useful to +advertise it with zero valid lifetime. Default: no +

+

onlink switch

This option specifies whether hosts may use the advertised +prefix for onlink determination. Default: yes +

+

autonomous switch

This option specifies whether hosts may use the advertised +prefix for stateless autoconfiguration. Default: yes +

+

valid lifetime expr [sensitive switch]

This option specifies the time (in seconds) how long (after +the receipt of RA) the prefix information is valid, i.e., +autoconfigured IP addresses can be assigned and hosts with +that IP addresses are considered directly reachable. 0 means +the prefix is no longer valid. For sensitive option, see +trigger. Default: 86400 (1 day), sensitive no. +

+

preferred lifetime expr [sensitive switch]

This option specifies the time (in seconds) how long (after +the receipt of RA) IP addresses generated from the prefix +using stateless autoconfiguration remain preferred. For +sensitive option, see +trigger. +Default: 14400 (4 hours), sensitive no. +

+

+

+

RDNSS specific options: +

+

+
ns address

This option specifies one recursive DNS server. Can be used +multiple times for multiple servers. It is mandatory to have +at least one ns option in rdnss definition. +

+

lifetime [mult] expr

This option specifies the time how long the RDNSS information +may be used by clients after the receipt of RA. It is +expressed either in seconds or (when mult is used) in +multiples of max ra interval. Note that RDNSS information +is also invalidated when default lifetime expires. 0 +means these addresses are no longer valid DNS servers. +Default: 3 * max ra interval. +

+

+

+

DNSSL specific options: +

+

+
domain address

This option specifies one DNS search domain. Can be used +multiple times for multiple domains. It is mandatory to have +at least one domain option in dnssl definition. +

+

lifetime [mult] expr

This option specifies the time how long the DNSSL information +may be used by clients after the receipt of RA. Details are +the same as for RDNSS lifetime option above. +Default: 3 * max ra interval. +

+

+

+

Example

+ +

+


+
+protocol radv {
+        interface "eth2" {
+                max ra interval 5;      # Fast failover with more routers
+                managed yes;            # Using DHCPv6 on eth2
+                prefix ::/0 {
+                        autonomous off; # So do not autoconfigure any IP
+                };
+        };
+
+        interface "eth*";               # No need for any other options
+
+        prefix 2001:0DB8:1234::/48 {
+                preferred lifetime 0;   # Deprecated address range
+        };
+
+        prefix 2001:0DB8:2000::/48 {
+                autonomous off;         # Do not autoconfigure
+        };
+
+        rdnss 2001:0DB8:1234::10;       # Short form of RDNSS
+
+        rdnss {
+                lifetime mult 10;
+                ns 2001:0DB8:1234::11;
+                ns 2001:0DB8:1234::12;
+        };
+
+        dnssl {
+                lifetime 3600;
+                domain "abc.com";
+                domain "xyz.com";
+        };
+}
+
+
+

+

6.8 RIP +

+ +

Introduction

+ +

The RIP protocol (also sometimes called Rest In Pieces) is a simple protocol, where each router broadcasts (to all its neighbors) +distances to all networks it can reach. When a router hears distance to another network, it increments +it and broadcasts it back. Broadcasts are done in regular intervals. Therefore, if some network goes +unreachable, routers keep telling each other that its distance is the original distance plus 1 (actually, plus +interface metric, which is usually one). After some time, the distance reaches infinity (that's 15 in +RIP) and all routers know that network is unreachable. RIP tries to minimize situations where +counting to infinity is necessary, because it is slow. Due to infinity being 16, you can't use +RIP on networks where maximal distance is higher than 15 hosts. You can read more about RIP at +http://www.ietf.org/html.charters/rip-charter.html. Both IPv4 +(RFC 1723 +ftp://ftp.rfc-editor.org/in-notes/rfc1723.txt) +and IPv6 (RFC 2080 +ftp://ftp.rfc-editor.org/in-notes/rfc2080.txt) versions of RIP are supported by BIRD, historical RIPv1 (RFC 1058 +ftp://ftp.rfc-editor.org/in-notes/rfc1058.txt)is +not currently supported. RIPv4 MD5 authentication (RFC 2082 +ftp://ftp.rfc-editor.org/in-notes/rfc2082.txt) is supported. +

+

RIP is a very simple protocol, and it has a lot of shortcomings. Slow +convergence, big network load and inability to handle larger networks +makes it pretty much obsolete. (It is still usable on very small networks.) +

+

Configuration

+ +

In addition to options common for all to other protocols, RIP supports the following ones: +

+

+
authentication none|plaintext|md5

selects authentication method to be used. none means that +packets are not authenticated at all, plaintext means that a plaintext password is embedded +into each packet, and md5 means that packets are authenticated using a MD5 cryptographic +hash. If you set authentication to not-none, it is a good idea to add password +section. Default: none. +

+

honor always|neighbor|never

specifies when should requests for dumping routing table +be honored. (Always, when sent from a host on a directly connected +network or never.) Routing table updates are honored only from +neighbors, that is not configurable. Default: never. +

+

+

There are some options that can be specified per-interface: +

+

+
metric num

This option specifies the metric of the interface. Valid +

+

mode multicast|broadcast|quiet|nolisten|version1

This option selects the mode for RIP to work in. If nothing is +specified, RIP runs in multicast mode. version1 is +currently equivalent to broadcast, and it makes RIP talk +to a broadcast address even through multicast mode is +possible. quiet option means that RIP will not transmit +any periodic messages to this interface and nolisten +means that RIP will send to this interface butnot listen to it. +

+

ttl security [switch | tx only]

TTL security is a feature that protects routing protocols +from remote spoofed packets by using TTL 255 instead of TTL 1 +for protocol packets destined to neighbors. Because TTL is +decremented when packets are forwarded, it is non-trivial to +spoof packets with TTL 255 from remote locations. +

If this option is enabled, the router will send RIP packets +with TTL 255 and drop received packets with TTL less than +255. If this option si set to tx only, TTL 255 is used +for sent packets, but is not checked for received +packets. Such setting does not offer protection, but offers +compatibility with neighbors regardless of whether they use +ttl security. +

Note that for RIPng, TTL security is a standard behavior +(required by RFC 2080), but BIRD uses tx only by +default, for compatibility with older versions. For IPv4 RIP, +default value is no. +

+

tx class|dscp|priority num

These options specify the ToS/DiffServ/Traffic class/Priority +of the outgoing RIP packets. See +tx class common option for detailed description. +

+

+

The following options generally override behavior specified in RFC. If you use any of these +options, BIRD will no longer be RFC-compliant, which means it will not be able to talk to anything +other than equally configured BIRD. I have warned you. +

+

+
port number

selects IP port to operate on, default 520. (This is useful when testing BIRD, if you +set this to an address >1024, you will not need to run bird with UID==0). +

+

infinity number

selects the value of infinity, default is 16. Bigger values will make protocol convergence +even slower. +

+

period number

specifies the number of seconds between periodic updates. Default is 30 seconds. A lower +number will mean faster convergence but bigger network +load. Do not use values lower than 12. +

+

timeout time number

specifies how old route has to be to be considered unreachable. Default is 4*period. +

+

garbage time number

specifies how old route has to be to be discarded. Default is 10*period. +

+

+

Attributes

+ +

RIP defines two route attributes: +

+

+
int rip_metric

RIP metric of the route (ranging from 0 to infinity). +When routes from different RIP instances are available and all of them have the same +preference, BIRD prefers the route with lowest rip_metric. +When importing a non-RIP route, the metric defaults to 5. +

+

int rip_tag

RIP route tag: a 16-bit number which can be used +to carry additional information with the route (for example, an originating AS number +in case of external routes). When importing a non-RIP route, the tag defaults to 0. +

+

+

Example

+ +

+


+
+protocol rip MyRIP_test {
+        debug all;
+        port 1520;
+        period 12;
+        garbage time 60;
+        interface "eth0" { metric 3; mode multicast; };
+        interface "eth*" { metric 2; mode broadcast; };
+        honor neighbor;
+        authentication none;
+        import filter { print "importing"; accept; };
+        export filter { print "exporting"; accept; };
+}
+
+
+

+

6.9 Static +

+ +

The Static protocol doesn't communicate with other routers in the network, +but instead it allows you to define routes manually. This is often used for +specifying how to forward packets to parts of the network which don't use +dynamic routing at all and also for defining sink routes (i.e., those +telling to return packets as undeliverable if they are in your IP block, +you don't have any specific destination for them and you don't want to send +them out through the default route to prevent routing loops). +

+

There are five types of static routes: `classical' routes telling +to forward packets to a neighboring router, multipath routes +specifying several (possibly weighted) neighboring routers, device +routes specifying forwarding to hosts on a directly connected network, +recursive routes computing their nexthops by doing route table lookups +for a given IP and special routes (sink, blackhole etc.) which specify +a special action to be done instead of forwarding the packet. +

+

When the particular destination is not available (the interface is down or +the next hop of the route is not a neighbor at the moment), Static just +uninstalls the route from the table it is connected to and adds it again as soon +as the destination becomes adjacent again. +

+

The Static protocol does not have many configuration options. The +definition of the protocol contains mainly a list of static routes: +

+

+
route prefix via ip

Static route through +a neighboring router. +

route prefix multipath via ip [weight num] [via ...]

Static multipath route. Contains several nexthops (gateways), possibly +with their weights. +

route prefix via "interface"

Static device +route through an interface to hosts on a directly connected network. +

route prefix recursive ip

Static recursive route, +its nexthop depends on a route table lookup for given IP address. +

route prefix blackhole|unreachable|prohibit

Special routes +specifying to silently drop the packet, return it as unreachable or return +it as administratively prohibited. First two targets are also known +as drop and reject. +

+

check link switch

If set, hardware link states of network interfaces are taken +into consideration. When link disappears (e.g. ethernet cable +is unplugged), static routes directing to that interface are +removed. It is possible that some hardware drivers or +platforms do not implement this feature. Default: off. +

+

igp table name

Specifies a table that is used +for route table lookups of recursive routes. Default: the +same table as the protocol is connected to. +

+

+

Static routes have no specific attributes. +

+

Example static config might look like this: +

+

+


+
+protocol static {
+        table testable;                  # Connect to a non-default routing table
+        route 0.0.0.0/0 via 198.51.100.130; # Default route
+        route 10.0.0.0/8 multipath       # Multipath route
+                via 198.51.100.10 weight 2
+                via 198.51.100.20
+                via 192.0.2.1;
+        route 203.0.113.0/24 unreachable; # Sink route
+        route 10.2.0.0/24 via "arc0";    # Secondary network
+}
+
+
+

+


+Next +Previous +Contents + + -- cgit v1.2.3 From b0a8c7fc8547eef21ede33887580b5e867ee742c Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Thu, 15 Aug 2013 20:26:50 +0200 Subject: Wrong change commited - 'route limit' marked as obsolete. --- doc/bird-6.html | 1731 ------------------------------------------------------- doc/bird.sgml | 5 +- 2 files changed, 3 insertions(+), 1733 deletions(-) delete mode 100644 doc/bird-6.html diff --git a/doc/bird-6.html b/doc/bird-6.html deleted file mode 100644 index d21209ee..00000000 --- a/doc/bird-6.html +++ /dev/null @@ -1,1731 +0,0 @@ - - - - - BIRD User's Guide: Protocols - - - - - -Next -Previous -Contents -
-

6. Protocols

- -

6.1 BGP -

- -

The Border Gateway Protocol is the routing protocol used for backbone -level routing in the today's Internet. Contrary to the other protocols, its convergence -doesn't rely on all routers following the same rules for route selection, -making it possible to implement any routing policy at any router in the -network, the only restriction being that if a router advertises a route, -it must accept and forward packets according to it. -

-

BGP works in terms of autonomous systems (often abbreviated as -AS). Each AS is a part of the network with common management and -common routing policy. It is identified by a unique 16-bit number -(ASN). Routers within each AS usually exchange AS-internal routing -information with each other using an interior gateway protocol (IGP, -such as OSPF or RIP). Boundary routers at the border of -the AS communicate global (inter-AS) network reachability information with -their neighbors in the neighboring AS'es via exterior BGP (eBGP) and -redistribute received information to other routers in the AS via -interior BGP (iBGP). -

-

Each BGP router sends to its neighbors updates of the parts of its -routing table it wishes to export along with complete path information -(a list of AS'es the packet will travel through if it uses the particular -route) in order to avoid routing loops. -

-

BIRD supports all requirements of the BGP4 standard as defined in -RFC 4271 -ftp://ftp.rfc-editor.org/in-notes/rfc4271.txt -It also supports the community attributes -(RFC 1997 -ftp://ftp.rfc-editor.org/in-notes/rfc1997.txt), -capability negotiation -(RFC 3392 -ftp://ftp.rfc-editor.org/in-notes/rfc3392.txt), -MD5 password authentication -(RFC 2385 -ftp://ftp.rfc-editor.org/in-notes/rfc2385.txt), -extended communities -(RFC 4360 -ftp://ftp.rfc-editor.org/in-notes/rfc4360.txt), -route reflectors -(RFC 4456 -ftp://ftp.rfc-editor.org/in-notes/rfc4456.txt), -multiprotocol extensions -(RFC 4760 -ftp://ftp.rfc-editor.org/in-notes/rfc4760.txt), -4B AS numbers -(RFC 4893 -ftp://ftp.rfc-editor.org/in-notes/rfc4893.txt), -and 4B AS numbers in extended communities -(RFC 5668 -ftp://ftp.rfc-editor.org/in-notes/rfc5668.txt). -

-

For IPv6, it uses the standard multiprotocol extensions defined in -RFC 2283 -ftp://ftp.rfc-editor.org/in-notes/rfc2283.txt -including changes described in the -latest draft -ftp://ftp.rfc-editor.org/internet-drafts/draft-ietf-idr-bgp4-multiprotocol-v2-05.txt -and applied to IPv6 according to -RFC 2545 -ftp://ftp.rfc-editor.org/in-notes/rfc2545.txt. -

-

Route selection rules

- -

BGP doesn't have any simple metric, so the rules for selection of an optimal -route among multiple BGP routes with the same preference are a bit more complex -and they are implemented according to the following algorithm. It starts the first -rule, if there are more "best" routes, then it uses the second rule to choose -among them and so on. -

-

    -
  • Prefer route with the highest Local Preference attribute.
  • -
  • Prefer route with the shortest AS path.
  • -
  • Prefer IGP origin over EGP and EGP origin over incomplete.
  • -
  • Prefer the lowest value of the Multiple Exit Discriminator.
  • -
  • Prefer routes received via eBGP over ones received via iBGP.
  • -
  • Prefer routes with lower internal distance to a boundary router.
  • -
  • Prefer the route with the lowest value of router ID of the -advertising router.
  • -
-

-

IGP routing table

- -

BGP is mainly concerned with global network reachability and with -routes to other autonomous systems. When such routes are redistributed -to routers in the AS via BGP, they contain IP addresses of a boundary -routers (in route attribute NEXT_HOP). BGP depends on existing IGP -routing table with AS-internal routes to determine immediate next hops -for routes and to know their internal distances to boundary routers -for the purpose of BGP route selection. In BIRD, there is usually -one routing table used for both IGP routes and BGP routes. -

-

Configuration

- -

Each instance of the BGP corresponds to one neighboring router. -This allows to set routing policy and all the other parameters differently -for each neighbor using the following configuration parameters: -

-

-
local [ip] as number

Define which AS we -are part of. (Note that contrary to other IP routers, BIRD is -able to act as a router located in multiple AS'es -simultaneously, but in such cases you need to tweak the BGP -paths manually in the filters to get consistent behavior.) -Optional ip argument specifies a source address, -equivalent to the source address option (see below). -This parameter is mandatory. -

-

neighbor ip as number

Define neighboring router -this instance will be talking to and what AS it's located in. Unless -you use the multihop clause, it must be directly connected to one -of your router's interfaces. In case the neighbor is in the same AS -as we are, we automatically switch to iBGP. This parameter is mandatory. -

-

multihop [number]

Configure multihop BGP -session to a neighbor that isn't directly connected. -Accurately, this option should be used if the configured -neighbor IP address does not match with any local network -subnets. Such IP address have to be reachable through system -routing table. For multihop BGP it is recommended to -explicitly configure source address to have it -stable. Optional number argument can be used to specify -the number of hops (used for TTL). Note that the number of -networks (edges) in a path is counted, i.e. if two BGP -speakers are separated by one router, the number of hops is -2. Default: switched off. -

-

source address ip

Define local address we -should use for next hop calculation and as a source address -for the BGP session. Default: the address of the local -end of the interface our neighbor is connected to. -

-

next hop self

Avoid calculation of the Next Hop -attribute and always advertise our own source address as a -next hop. This needs to be used only occasionally to -circumvent misconfigurations of other routers. Default: -disabled. -

-

next hop keep

Forward the received Next Hop -attribute even in situations where the local address should be -used instead, like when the route is sent to an interface with -a different subnet. Default: disabled. -

-

missing lladdr self|drop|ignore

Next Hop attribute -in BGP-IPv6 sometimes contains just the global IPv6 address, -but sometimes it has to contain both global and link-local -IPv6 addresses. This option specifies what to do if BIRD have -to send both addresses but does not know link-local address. -This situation might happen when routes from other protocols -are exported to BGP, or when improper updates are received -from BGP peers. self means that BIRD advertises its own -local address instead. drop means that BIRD skips that -prefixes and logs error. ignore means that BIRD ignores -the problem and sends just the global address (and therefore -forms improper BGP update). Default: self, unless BIRD -is configured as a route server (option rs client), in -that case default is ignore, because route servers usually -do not forward packets themselves. -

-

gateway direct|recursive

For received routes, their -gw (immediate next hop) attribute is computed from -received bgp_next_hop attribute. This option specifies -how it is computed. Direct mode means that the IP address from -bgp_next_hop is used if it is directly reachable, -otherwise the neighbor IP address is used. Recursive mode -means that the gateway is computed by an IGP routing table -lookup for the IP address from bgp_next_hop. Recursive -mode is the behavior specified by the BGP standard. Direct -mode is simpler, does not require any routes in a routing -table, and was used in older versions of BIRD, but does not -handle well nontrivial iBGP setups and multihop. Recursive -mode is incompatible with -sorted tables. Default: direct for singlehop eBGP, -recursive otherwise. -

-

igp table name

Specifies a table that is used -as an IGP routing table. Default: the same as the table BGP is -connected to. -

-

ttl security switch

Use GTSM (RFC 5082 - the -generalized TTL security mechanism). GTSM protects against -spoofed packets by ignoring received packets with a smaller -than expected TTL. To work properly, GTSM have to be enabled -on both sides of a BGP session. If both ttl security and -multihop options are enabled, multihop option should -specify proper hop value to compute expected TTL. Kernel -support required: Linux: 2.6.34+ (IPv4), 2.6.35+ (IPv6), BSD: -since long ago, IPv4 only. Note that full (ICMP protection, -for example) RFC 5082 support is provided by Linux -only. Default: disabled. -

-

password string

Use this password for MD5 authentication -of BGP sessions. Default: no authentication. Password has to be set by -external utility (e.g. setkey(8)) on BSD systems. -

-

passive switch

Standard BGP behavior is both -initiating outgoing connections and accepting incoming -connections. In passive mode, outgoing connections are not -initiated. Default: off. -

-

rr client

Be a route reflector and treat the neighbor as -a route reflection client. Default: disabled. -

-

rr cluster id IPv4 address

Route reflectors use cluster id -to avoid route reflection loops. When there is one route reflector in a cluster -it usually uses its router id as a cluster id, but when there are more route -reflectors in a cluster, these need to be configured (using this option) to -use a common cluster id. Clients in a cluster need not know their cluster -id and this option is not allowed for them. Default: the same as router id. -

-

rs client

Be a route server and treat the neighbor -as a route server client. A route server is used as a -replacement for full mesh EBGP routing in Internet exchange -points in a similar way to route reflectors used in IBGP routing. -BIRD does not implement obsoleted RFC 1863, but uses ad-hoc implementation, -which behaves like plain EBGP but reduces modifications to advertised route -attributes to be transparent (for example does not prepend its AS number to -AS PATH attribute and keeps MED attribute). Default: disabled. -

-

secondary switch

Usually, if an import filter -rejects a selected route, no other route is propagated for -that network. This option allows to try the next route in -order until one that is accepted is found or all routes for -that network are rejected. This can be used for route servers -that need to propagate different tables to each client but do -not want to have these tables explicitly (to conserve memory). -This option requires that the connected routing table is -sorted. Default: off. -

-

enable route refresh switch

When BGP speaker -changes its import filter, it has to re-examine all routes -received from its neighbor against the new filter. As these -routes might not be available, there is a BGP protocol -extension Route Refresh (specified in RFC 2918) that allows -BGP speaker to request re-advertisement of all routes from its -neighbor. This option specifies whether BIRD advertises this -capability and accepts such requests. Even when disabled, BIRD -can send route refresh requests. Default: on. -

-

interpret communities switch

RFC 1997 demands -that BGP speaker should process well-known communities like -no-export (65535, 65281) or no-advertise (65535, 65282). For -example, received route carrying a no-adverise community -should not be advertised to any of its neighbors. If this -option is enabled (which is by default), BIRD has such -behavior automatically (it is evaluated when a route is -exported to the BGP protocol just before the export filter). -Otherwise, this integrated processing of well-known -communities is disabled. In that case, similar behavior can be -implemented in the export filter. Default: on. -

-

enable as4 switch

BGP protocol was designed to use 2B AS numbers -and was extended later to allow 4B AS number. BIRD supports 4B AS extension, -but by disabling this option it can be persuaded not to advertise it and -to maintain old-style sessions with its neighbors. This might be useful for -circumventing bugs in neighbor's implementation of 4B AS extension. -Even when disabled (off), BIRD behaves internally as AS4-aware BGP router. -Default: on. -

-

capabilities switch

Use capability advertisement -to advertise optional capabilities. This is standard behavior -for newer BGP implementations, but there might be some older -BGP implementations that reject such connection attempts. -When disabled (off), features that request it (4B AS support) -are also disabled. Default: on, with automatic fallback to -off when received capability-related error. -

-

advertise ipv4 switch

Advertise IPv4 multiprotocol capability. -This is not a correct behavior according to the strict interpretation -of RFC 4760, but it is widespread and required by some BGP -implementations (Cisco and Quagga). This option is relevant -to IPv4 mode with enabled capability advertisement only. Default: on. -

-

route limit number

The maximal number of routes -that may be imported from the protocol. If the route limit is -exceeded, the connection is closed with an error. Limit is currently implemented as -import limit number action restart. This option is obsolete and it is -replaced by -import limit option. Default: no limit. -

-

disable after error switch

When an error is encountered (either -locally or by the other side), disable the instance automatically -and wait for an administrator to fix the problem manually. Default: off. -

-

hold time number

Time in seconds to wait for a Keepalive -message from the other side before considering the connection stale. -Default: depends on agreement with the neighboring router, we prefer -240 seconds if the other side is willing to accept it. -

-

startup hold time number

Value of the hold timer used -before the routers have a chance to exchange open messages and agree -on the real value. Default: 240 seconds. -

-

keepalive time number

Delay in seconds between sending -of two consecutive Keepalive messages. Default: One third of the hold time. -

-

connect retry time number

Time in seconds to wait before -retrying a failed attempt to connect. Default: 120 seconds. -

-

start delay time number

Delay in seconds between protocol -startup and the first attempt to connect. Default: 5 seconds. -

-

error wait time number,number

Minimum and maximum delay in seconds between a protocol -failure (either local or reported by the peer) and automatic restart. -Doesn't apply when disable after error is configured. If consecutive -errors happen, the delay is increased exponentially until it reaches the maximum. Default: 60, 300. -

-

error forget time number

Maximum time in seconds between two protocol -failures to treat them as a error sequence which makes the error wait time -increase exponentially. Default: 300 seconds. -

-

path metric switch

Enable comparison of path lengths -when deciding which BGP route is the best one. Default: on. -

-

med metric switch

Enable comparison of MED -attributes (during best route selection) even between routes -received from different ASes. This may be useful if all MED -attributes contain some consistent metric, perhaps enforced in -import filters of AS boundary routers. If this option is -disabled, MED attributes are compared only if routes are -received from the same AS (which is the standard behavior). -Default: off. -

-

deterministic med switch

BGP route selection -algorithm is often viewed as a comparison between individual -routes (e.g. if a new route appears and is better than the -current best one, it is chosen as the new best one). But the -proper route selection, as specified by RFC 4271, cannot be -fully implemented in that way. The problem is mainly in -handling the MED attribute. BIRD, by default, uses an -simplification based on individual route comparison, which in -some cases may lead to temporally dependent behavior (i.e. the -selection is dependent on the order in which routes appeared). -This option enables a different (and slower) algorithm -implementing proper RFC 4271 route selection, which is -deterministic. Alternative way how to get deterministic -behavior is to use med metric option. This option is -incompatible with -sorted tables. -Default: off. -

-

igp metric switch

Enable comparison of internal -distances to boundary routers during best route selection. Default: on. -

-

prefer older switch

Standard route selection algorithm -breaks ties by comparing router IDs. This changes the behavior -to prefer older routes (when both are external and from different -peer). For details, see RFC 5004. Default: off. -

-

default bgp_med number

Value of the Multiple Exit -Discriminator to be used during route selection when the MED attribute -is missing. Default: 0. -

-

default bgp_local_pref number

A default value -for the Local Preference attribute. It is used when a new -Local Preference attribute is attached to a route by the BGP -protocol itself (for example, if a route is received through -eBGP and therefore does not have such attribute). Default: 100 -(0 in pre-1.2.0 versions of BIRD). -

-

-

Attributes

- -

BGP defines several route attributes. Some of them (those marked with `I' in the -table below) are available on internal BGP connections only, some of them (marked -with `O') are optional. -

-

-
bgppath bgp_path

Sequence of AS numbers describing the AS path -the packet will travel through when forwarded according to the particular route. -In case of internal BGP it doesn't contain the number of the local AS. -

-

int bgp_local_pref [I]

Local preference value used for -selection among multiple BGP routes (see the selection rules above). It's -used as an additional metric which is propagated through the whole local AS. -

-

int bgp_med [O]

The Multiple Exit Discriminator of the route -is an optional attribute which is used on external (inter-AS) links to -convey to an adjacent AS the optimal entry point into the local AS. -The received attribute is also propagated over internal BGP links. -The attribute value is zeroed when a route is exported to an external BGP -instance to ensure that the attribute received from a neighboring AS is -not propagated to other neighboring ASes. A new value might be set in -the export filter of an external BGP instance. -See RFC 4451 -ftp://ftp.rfc-editor.org/in-notes/rfc4451.txt -for further discussion of BGP MED attribute. -

-

enum bgp_origin

Origin of the route: either ORIGIN_IGP -if the route has originated in an interior routing protocol or -ORIGIN_EGP if it's been imported from the EGP protocol -(nowadays it seems to be obsolete) or ORIGIN_INCOMPLETE if the origin -is unknown. -

-

ip bgp_next_hop

Next hop to be used for forwarding of packets -to this destination. On internal BGP connections, it's an address of the -originating router if it's inside the local AS or a boundary router the -packet will leave the AS through if it's an exterior route, so each BGP -speaker within the AS has a chance to use the shortest interior path -possible to this point. -

-

void bgp_atomic_aggr [O]

This is an optional attribute -which carries no value, but the sole presence of which indicates that the route -has been aggregated from multiple routes by some router on the path from -the originator. -

-

clist bgp_community [O]

List of community values associated -with the route. Each such value is a pair (represented as a pair data -type inside the filters) of 16-bit integers, the first of them containing the number of the AS which defines -the community and the second one being a per-AS identifier. There are lots -of uses of the community mechanism, but generally they are used to carry -policy information like "don't export to USA peers". As each AS can define -its own routing policy, it also has a complete freedom about which community -attributes it defines and what will their semantics be. -

-

eclist bgp_ext_community [O]

List of extended community -values associated with the route. Extended communities have similar usage -as plain communities, but they have an extended range (to allow 4B ASNs) -and a nontrivial structure with a type field. Individual community values are -represented using an ec data type inside the filters. -

-

quad bgp_originator_id [I, O]

This attribute is created by the -route reflector when reflecting the route and contains the router ID of the -originator of the route in the local AS. -

-

clist bgp_cluster_list [I, O]

This attribute contains a list -of cluster IDs of route reflectors. Each route reflector prepends its -cluster ID when reflecting the route. -

-

-

Example

- -

-


-
-protocol bgp {
-        local as 65000;                      # Use a private AS number
-        neighbor 198.51.100.130 as 64496;    # Our neighbor ...
-        multihop;                            # ... which is connected indirectly
-        export filter {                      # We use non-trivial export rules
-                if source = RTS_STATIC then { # Export only static routes
-                        # Assign our community
-                        bgp_community.add((65000,64501));
-                        # Artificially increase path length
-                        # by advertising local AS number twice
-                        if bgp_path ~ [= 65000 =] then
-                                bgp_path.prepend(65000);
-                        accept;
-                }
-                reject;
-        };
-        import all;
-        source address 198.51.100.14;   # Use a non-standard source address
-}
-
-
-

-

6.2 Device -

- -

The Device protocol is not a real routing protocol. It doesn't generate -any routes and it only serves as a module for getting information about network -interfaces from the kernel. -

-

Except for very unusual circumstances, you probably should include -this protocol in the configuration since almost all other protocols -require network interfaces to be defined for them to work with. -

-

Configuration

- -

-

-
scan time number

Time in seconds between two scans -of the network interface list. On systems where we are notified about -interface status changes asynchronously (such as newer versions of -Linux), we need to scan the list only in order to avoid confusion by lost -notification messages, so the default time is set to a large value. -

-

primary [ "mask" ] prefix

If a network interface has more than one network address, BIRD -has to choose one of them as a primary one. By default, BIRD -chooses the lexicographically smallest address as the primary -one. -

This option allows to specify which network address should be -chosen as a primary one. Network addresses that match -prefix are preferred to non-matching addresses. If more -primary options are used, the first one has the highest -preference. If "mask" is specified, then such -primary option is relevant only to matching network -interfaces. -

In all cases, an address marked by operating system as -secondary cannot be chosen as the primary one. -

-

-

As the Device protocol doesn't generate any routes, it cannot have -any attributes. Example configuration looks like this: -

-

-


-
-protocol device {
-        scan time 10;           # Scan the interfaces often
-        primary "eth0" 192.168.1.1;
-        primary 192.168.0.0/16;
-}
-
-
-

-

6.3 Direct -

- -

The Direct protocol is a simple generator of device routes for all the -directly connected networks according to the list of interfaces provided -by the kernel via the Device protocol. -

-

The question is whether it is a good idea to have such device -routes in BIRD routing table. OS kernel usually handles device routes -for directly connected networks by itself so we don't need (and don't -want) to export these routes to the kernel protocol. OSPF protocol -creates device routes for its interfaces itself and BGP protocol is -usually used for exporting aggregate routes. Although there are some -use cases that use the direct protocol (like abusing eBGP as an IGP -routing protocol), in most cases it is not needed to have these device -routes in BIRD routing table and to use the direct protocol. -

-

There is one notable case when you definitely want to use the -direct protocol -- running BIRD on BSD systems. Having high priority -device routes for directly connected networks from the direct protocol -protects kernel device routes from being overwritten or removed by IGP -routes during some transient network conditions, because a lower -priority IGP route for the same network is not exported to the kernel -routing table. This is an issue on BSD systems only, as on Linux -systems BIRD cannot change non-BIRD route in the kernel routing table. -

-

The only configurable thing about direct is what interfaces it watches: -

-

-

-
interface pattern [, ...]

By default, the Direct -protocol will generate device routes for all the interfaces -available. If you want to restrict it to some subset of interfaces -(for example if you're using multiple routing tables for policy -routing and some of the policy domains don't contain all interfaces), -just use this clause. -

-

-

Direct device routes don't contain any specific attributes. -

-

Example config might look like this: -

-

-


-
-protocol direct {
-        interface "-arc*", "*";         # Exclude the ARCnets
-}
-
-
-

-

6.4 Kernel -

- -

The Kernel protocol is not a real routing protocol. Instead of communicating -with other routers in the network, it performs synchronization of BIRD's routing -tables with the OS kernel. Basically, it sends all routing table updates to the kernel -and from time to time it scans the kernel tables to see whether some routes have -disappeared (for example due to unnoticed up/down transition of an interface) -or whether an `alien' route has been added by someone else (depending on the -learn switch, such routes are either ignored or accepted to our -table). -

-

Unfortunately, there is one thing that makes the routing table -synchronization a bit more complicated. In the kernel routing table -there are also device routes for directly connected networks. These -routes are usually managed by OS itself (as a part of IP address -configuration) and we don't want to touch that. They are completely -ignored during the scan of the kernel tables and also the export of -device routes from BIRD tables to kernel routing tables is restricted -to prevent accidental interference. This restriction can be disabled using -device routes switch. -

-

If your OS supports only a single routing table, you can configure -only one instance of the Kernel protocol. If it supports multiple -tables (in order to allow policy routing; such an OS is for example -Linux), you can run as many instances as you want, but each of them -must be connected to a different BIRD routing table and to a different -kernel table. -

-

Because the kernel protocol is partially integrated with the -connected routing table, there are two limitations - it is not -possible to connect more kernel protocols to the same routing table -and changing route destination/gateway in an export -filter of a kernel protocol does not work. Both limitations can be -overcome using another routing table and the pipe protocol. -

-

Configuration

- -

-

-
persist switch

Tell BIRD to leave all its routes in the -routing tables when it exits (instead of cleaning them up). -

scan time number

Time in seconds between two consecutive scans of the -kernel routing table. -

learn switch

Enable learning of routes added to the kernel -routing tables by other routing daemons or by the system administrator. -This is possible only on systems which support identification of route -authorship. -

-

device routes switch

Enable export of device -routes to the kernel routing table. By default, such routes -are rejected (with the exception of explicitly configured -device routes from the static protocol) regardless of the -export filter to protect device routes in kernel routing table -(managed by OS itself) from accidental overwriting or erasing. -

-

kernel table number

Select which kernel table should -this particular instance of the Kernel protocol work with. Available -only on systems supporting multiple routing tables. -

-

-

Attributes

- -

The Kernel protocol defines several attributes. These attributes -are translated to appropriate system (and OS-specific) route attributes. -We support these attributes: -

-

-
int krt_source

The original source of the imported -kernel route. The value is system-dependent. On Linux, it is -a value of the protocol field of the route. See -/etc/iproute2/rt_protos for common values. On BSD, it is -based on STATIC and PROTOx flags. The attribute is read-only. -

-

int krt_metric

The kernel metric of -the route. When multiple same routes are in a kernel routing -table, the Linux kernel chooses one with lower metric. -

-

ip krt_prefsrc

(Linux) The preferred source address. -Used in source address selection for outgoing packets. Have to -be one of IP addresses of the router. -

-

int krt_realm

(Linux) The realm of the route. Can be -used for traffic classification. -

-

-

Example

- -

A simple configuration can look this way: -

-

-


-
-protocol kernel {
-        export all;
-}
-
-
-

-

Or for a system with two routing tables: -

-

-


-
-protocol kernel {               # Primary routing table
-        learn;                  # Learn alien routes from the kernel
-        persist;                # Don't remove routes on bird shutdown
-        scan time 10;           # Scan kernel routing table every 10 seconds
-        import all;
-        export all;
-}
-
-protocol kernel {               # Secondary routing table
-        table auxtable;
-        kernel table 100;
-        export all;
-}
-
-
-

-

6.5 OSPF -

- -

Introduction

- -

Open Shortest Path First (OSPF) is a quite complex interior gateway -protocol. The current IPv4 version (OSPFv2) is defined in RFC -2328 -ftp://ftp.rfc-editor.org/in-notes/rfc2328.txt and -the current IPv6 version (OSPFv3) is defined in RFC 5340 -ftp://ftp.rfc-editor.org/in-notes/rfc5340.txt It's a link state -(a.k.a. shortest path first) protocol -- each router maintains a -database describing the autonomous system's topology. Each participating -router has an identical copy of the database and all routers run the -same algorithm calculating a shortest path tree with themselves as a -root. OSPF chooses the least cost path as the best path. -

-

In OSPF, the autonomous system can be split to several areas in order -to reduce the amount of resources consumed for exchanging the routing -information and to protect the other areas from incorrect routing data. -Topology of the area is hidden to the rest of the autonomous system. -

-

Another very important feature of OSPF is that -it can keep routing information from other protocols (like Static or BGP) -in its link state database as external routes. Each external route can -be tagged by the advertising router, making it possible to pass additional -information between routers on the boundary of the autonomous system. -

-

OSPF quickly detects topological changes in the autonomous system (such -as router interface failures) and calculates new loop-free routes after a short -period of convergence. Only a minimal amount of -routing traffic is involved. -

-

Each router participating in OSPF routing periodically sends Hello messages -to all its interfaces. This allows neighbors to be discovered dynamically. -Then the neighbors exchange theirs parts of the link state database and keep it -identical by flooding updates. The flooding process is reliable and ensures -that each router detects all changes. -

-

Configuration

- -

In the main part of configuration, there can be multiple definitions of -OSPF areas, each with a different id. These definitions includes many other -switches and multiple definitions of interfaces. Definition of interface -may contain many switches and constant definitions and list of neighbors -on nonbroadcast networks. -

-


-
-protocol ospf <name> {
-        rfc1583compat <switch>;
-        stub router <switch>;
-        tick <num>;
-        ecmp <switch> [limit <num>];
-        area <id> {
-                stub;
-                nssa;
-                summary <switch>;
-                default nssa <switch>;
-                default cost <num>;
-                default cost2 <num>;
-                translator <switch>;
-                translator stability <num>;
-
-                networks {
-                        <prefix>;
-                        <prefix> hidden;
-                }
-                external {
-                        <prefix>;
-                        <prefix> hidden;
-                        <prefix> tag <num>;
-                }
-                stubnet <prefix>;
-                stubnet <prefix> {
-                        hidden <switch>;
-                        summary <switch>;
-                        cost <num>;
-                }
-                interface <interface pattern> [instance <num>] {
-                        cost <num>;
-                        stub <switch>;
-                        hello <num>;
-                        poll <num>;
-                        retransmit <num>;
-                        priority <num>;
-                        wait <num>;
-                        dead count <num>;
-                        dead <num>;
-                        rx buffer [normal|large|<num>];
-                        type [broadcast|bcast|pointopoint|ptp|
-                                nonbroadcast|nbma|pointomultipoint|ptmp];
-                        strict nonbroadcast <switch>;
-                        real broadcast <switch>;
-                        ptp netmask <switch>;
-                        check link <switch>;
-                        ecmp weight <num>;
-                        ttl security [<switch>; | tx only]
-                        tx class|dscp <num>;
-                        tx priority <num>;
-                        authentication [none|simple|cryptographic];
-                        password "<text>";
-                        password "<text>" {
-                                id <num>;
-                                generate from "<date>";
-                                generate to "<date>";
-                                accept from "<date>";
-                                accept to "<date>";
-                        };
-                        neighbors {
-                                <ip>;
-                                <ip> eligible;
-                        };
-                };
-                virtual link <id> [instance <num>] {
-                        hello <num>;
-                        retransmit <num>;
-                        wait <num>;
-                        dead count <num>;
-                        dead <num>;
-                        authentication [none|simple|cryptographic];
-                        password "<text>";
-                };
-        };
-}
-
-
-

-

-
rfc1583compat switch

This option controls compatibility of routing table -calculation with RFC 1583 -ftp://ftp.rfc-editor.org/in-notes/rfc1583.txt. Default -value is no. -

-

stub router switch

This option configures the router to be a stub router, i.e., -a router that participates in the OSPF topology but does not -allow transit traffic. In OSPFv2, this is implemented by -advertising maximum metric for outgoing links, as suggested -by RFC 3137 -ftp://ftp.rfc-editor.org/in-notes/rfc3137.txt. -In OSPFv3, the stub router behavior is announced by clearing -the R-bit in the router LSA. Default value is no. -

-

tick num

The routing table calculation and clean-up of areas' databases -is not performed when a single link state -change arrives. To lower the CPU utilization, it's processed later -at periodical intervals of num seconds. The default value is 1. -

-

ecmp switch [limit number]

This option specifies whether OSPF is allowed to generate -ECMP (equal-cost multipath) routes. Such routes are used when -there are several directions to the destination, each with -the same (computed) cost. This option also allows to specify -a limit on maximal number of nexthops in one route. By -default, ECMP is disabled. If enabled, default value of the -limit is 16. -

-

area id

This defines an OSPF area with given area ID (an integer or an IPv4 -address, similarly to a router ID). The most important area is -the backbone (ID 0) to which every other area must be connected. -

-

stub

This option configures the area to be a stub area. External -routes are not flooded into stub areas. Also summary LSAs can be -limited in stub areas (see option summary). -By default, the area is not a stub area. -

-

nssa

This option configures the area to be a NSSA (Not-So-Stubby -Area). NSSA is a variant of a stub area which allows a -limited way of external route propagation. Global external -routes are not propagated into a NSSA, but an external route -can be imported into NSSA as a (area-wide) NSSA-LSA (and -possibly translated and/or aggregated on area boundary). -By default, the area is not NSSA. -

-

summary switch

This option controls propagation of summary LSAs into stub or -NSSA areas. If enabled, summary LSAs are propagated as usual, -otherwise just the default summary route (0.0.0.0/0) is -propagated (this is sometimes called totally stubby area). If -a stub area has more area boundary routers, propagating -summary LSAs could lead to more efficient routing at the cost -of larger link state database. Default value is no. -

-

default nssa switch

When summary option is enabled, default summary route is -no longer propagated to the NSSA. In that case, this option -allows to originate default route as NSSA-LSA to the NSSA. -Default value is no. -

-

default cost num

This option controls the cost of a default route propagated to -stub and NSSA areas. Default value is 1000. -

-

default cost2 num

When a default route is originated as NSSA-LSA, its cost -can use either type 1 or type 2 metric. This option allows -to specify the cost of a default route in type 2 metric. -By default, type 1 metric (option default cost) is used. -

-

translator switch

This option controls translation of NSSA-LSAs into external -LSAs. By default, one translator per NSSA is automatically -elected from area boundary routers. If enabled, this area -boundary router would unconditionally translate all NSSA-LSAs -regardless of translator election. Default value is no. -

-

translator stability num

This option controls the translator stability interval (in -seconds). When the new translator is elected, the old one -keeps translating until the interval is over. Default value -is 40. -

-

networks { set }

Definition of area IP ranges. This is used in summary LSA origination. -Hidden networks are not propagated into other areas. -

-

external { set }

Definition of external area IP ranges for NSSAs. This is used -for NSSA-LSA translation. Hidden networks are not translated -into external LSAs. Networks can have configured route tag. -

-

stubnet prefix { options }

Stub networks are networks that are not transit networks -between OSPF routers. They are also propagated through an -OSPF area as a part of a link state database. By default, -BIRD generates a stub network record for each primary network -address on each OSPF interface that does not have any OSPF -neighbors, and also for each non-primary network address on -each OSPF interface. This option allows to alter a set of -stub networks propagated by this router. -

Each instance of this option adds a stub network with given -network prefix to the set of propagated stub network, unless -option hidden is used. It also suppresses default stub -networks for given network prefix. When option -summary is used, also default stub networks that are -subnetworks of given stub network are suppressed. This might -be used, for example, to aggregate generated stub networks. -

-

interface pattern [instance num]

Defines that the specified interfaces belong to the area being defined. -See -interface common option for detailed description. -In OSPFv3, you can specify instance ID for that interface -description, so it is possible to have several instances of -that interface with different options or even in different areas. -

-

virtual link id [instance num]

Virtual link to router with the router id. Virtual link acts -as a point-to-point interface belonging to backbone. The -actual area is used as transport area. This item cannot be in -the backbone. In OSPFv3, you could also use several virtual -links to one destination with different instance IDs. -

-

cost num

Specifies output cost (metric) of an interface. Default value is 10. -

-

stub switch

If set to interface it does not listen to any packet and does not send -any hello. Default value is no. -

-

hello num

Specifies interval in seconds between sending of Hello messages. Beware, all -routers on the same network need to have the same hello interval. -Default value is 10. -

-

poll num

Specifies interval in seconds between sending of Hello messages for -some neighbors on NBMA network. Default value is 20. -

-

retransmit num

Specifies interval in seconds between retransmissions of unacknowledged updates. -Default value is 5. -

-

priority num

On every multiple access network (e.g., the Ethernet) Designed Router -and Backup Designed router are elected. These routers have some -special functions in the flooding process. Higher priority increases -preferences in this election. Routers with priority 0 are not -eligible. Default value is 1. -

-

wait num

After start, router waits for the specified number of seconds between starting -election and building adjacency. Default value is 40. -

-

dead count num

When the router does not receive any messages from a neighbor in -dead count*hello seconds, it will consider the neighbor down. -

-

dead num

When the router does not receive any messages from a neighbor in -dead seconds, it will consider the neighbor down. If both directives -dead count and dead are used, dead has precendence. -

-

rx buffer num

This sets the size of buffer used for receiving packets. The buffer should -be bigger than maximal size of any packets. Value NORMAL (default) -means 2*MTU, value LARGE means maximal allowed packet - 65535. -

-

type broadcast|bcast

BIRD detects a type of a connected network automatically, but -sometimes it's convenient to force use of a different type -manually. On broadcast networks (like ethernet), flooding -and Hello messages are sent using multicasts (a single packet -for all the neighbors). A designated router is elected and it -is responsible for synchronizing the link-state databases and -originating network LSAs. This network type cannot be used on -physically NBMA networks and on unnumbered networks (networks -without proper IP prefix). -

-

type pointopoint|ptp

Point-to-point networks connect just 2 routers together. No -election is performed and no network LSA is originated, which -makes it simpler and faster to establish. This network type -is useful not only for physically PtP ifaces (like PPP or -tunnels), but also for broadcast networks used as PtP links. -This network type cannot be used on physically NBMA networks. -

-

type nonbroadcast|nbma

On NBMA networks, the packets are sent to each neighbor -separately because of lack of multicast capabilities. -Like on broadcast networks, a designated router is elected, -which plays a central role in propagation of LSAs. -This network type cannot be used on unnumbered networks. -

-

type pointomultipoint|ptmp

This is another network type designed to handle NBMA -networks. In this case the NBMA network is treated as a -collection of PtP links. This is useful if not every pair of -routers on the NBMA network has direct communication, or if -the NBMA network is used as an (possibly unnumbered) PtP -link. -

-

strict nonbroadcast switch

If set, don't send hello to any undefined neighbor. This switch -is ignored on other than NBMA or PtMP networks. Default value is no. -

-

real broadcast switch

In type broadcast or type ptp network -configuration, OSPF packets are sent as IP multicast -packets. This option changes the behavior to using -old-fashioned IP broadcast packets. This may be useful as a -workaround if IP multicast for some reason does not work or -does not work reliably. This is a non-standard option and -probably is not interoperable with other OSPF -implementations. Default value is no. -

-

ptp netmask switch

In type ptp network configurations, OSPFv2 -implementations should ignore received netmask field in hello -packets and should send hello packets with zero netmask field -on unnumbered PtP links. But some OSPFv2 implementations -perform netmask checking even for PtP links. This option -specifies whether real netmask will be used in hello packets -on type ptp interfaces. You should ignore this option -unless you meet some compatibility problems related to this -issue. Default value is no for unnumbered PtP links, yes -otherwise. -

-

check link switch

If set, a hardware link state (reported by OS) is taken into -consideration. When a link disappears (e.g. an ethernet cable is -unplugged), neighbors are immediately considered unreachable -and only the address of the iface (instead of whole network -prefix) is propagated. It is possible that some hardware -drivers or platforms do not implement this feature. Default value is no. -

-

ttl security [switch | tx only]

TTL security is a feature that protects routing protocols -from remote spoofed packets by using TTL 255 instead of TTL 1 -for protocol packets destined to neighbors. Because TTL is -decremented when packets are forwarded, it is non-trivial to -spoof packets with TTL 255 from remote locations. Note that -this option would interfere with OSPF virtual links. -

If this option is enabled, the router will send OSPF packets -with TTL 255 and drop received packets with TTL less than -255. If this option si set to tx only, TTL 255 is used -for sent packets, but is not checked for received -packets. Default value is no. -

-

tx class|dscp|priority num

These options specify the ToS/DiffServ/Traffic class/Priority -of the outgoing OSPF packets. See -tx class common option for detailed description. -

-

ecmp weight num

When ECMP (multipath) routes are allowed, this value specifies -a relative weight used for nexthops going through the iface. -Allowed values are 1-256. Default value is 1. -

-

authentication none

No passwords are sent in OSPF packets. This is the default value. -

-

authentication simple

Every packet carries 8 bytes of password. Received packets -lacking this password are ignored. This authentication mechanism is -very weak. -

-

authentication cryptographic

16-byte long MD5 digest is appended to every packet. For the digest -generation 16-byte long passwords are used. Those passwords are -not sent via network, so this mechanism is quite secure. -Packets can still be read by an attacker. -

-

password "text"

An 8-byte or 16-byte password used for authentication. -See -password common option for detailed description. -

-

neighbors { set }

A set of neighbors to which Hello messages on NBMA or PtMP -networks are to be sent. For NBMA networks, some of them -could be marked as eligible. In OSPFv3, link-local addresses -should be used, using global ones is possible, but it is -nonstandard and might be problematic. And definitely, -link-local and global addresses should not be mixed. -

-

-

-

Attributes

- -

OSPF defines four route attributes. Each internal route has a metric. -Metric is ranging from 1 to infinity (65535). -External routes use metric type 1 or metric type 2. -A metric of type 1 is comparable with internal metric, a -metric of type 2 is always longer -than any metric of type 1 or any internal metric. -Internal metric or metric of type 1 is stored in attribute -ospf_metric1, metric type 2 is stored in attribute ospf_metric2. -If you specify both metrics only metric1 is used. -

Each external route can also carry attribute ospf_tag which is a -32-bit integer which is used when exporting routes to other protocols; -otherwise, it doesn't affect routing inside the OSPF domain at all. -The fourth attribute ospf_router_id is a router ID of the router -advertising that route/network. This attribute is read-only. Default -is ospf_metric2 = 10000 and ospf_tag = 0. -

-

Example

- -

-

-


-
-protocol ospf MyOSPF {
-        rfc1583compat yes;
-        tick 2;
-        export filter {
-                if source = RTS_BGP then {
-                        ospf_metric1 = 100;
-                        accept;
-                }
-                reject;
-        };
-        area 0.0.0.0 {
-                interface "eth*" {
-                        cost 11;
-                        hello 15;
-                        priority 100;
-                        retransmit 7;
-                        authentication simple;
-                        password "aaa";
-                };
-                interface "ppp*" {
-                        cost 100;
-                        authentication cryptographic;
-                        password "abc" {
-                                id 1;
-                                generate to "22-04-2003 11:00:06";
-                                accept from "17-01-2001 12:01:05";
-                        };
-                        password "def" {
-                                id 2;
-                                generate to "22-07-2005 17:03:21";
-                                accept from "22-02-2001 11:34:06";
-                        };
-                };
-                interface "arc0" {
-                        cost 10;
-                        stub yes;
-                };
-                interface "arc1";
-        };
-        area 120 {
-                stub yes;
-                networks {
-                        172.16.1.0/24;
-                        172.16.2.0/24 hidden;
-                }
-                interface "-arc0" , "arc*" {
-                        type nonbroadcast;
-                        authentication none;
-                        strict nonbroadcast yes;
-                        wait 120;
-                        poll 40;
-                        dead count 8;
-                        neighbors {
-                                192.168.120.1 eligible;
-                                192.168.120.2;
-                                192.168.120.10;
-                        };
-                };
-        };
-}
-
-
-

-

6.6 Pipe -

- -

Introduction

- -

The Pipe protocol serves as a link between two routing tables, allowing routes to be -passed from a table declared as primary (i.e., the one the pipe is connected to using the -table configuration keyword) to the secondary one (declared using peer table) -and vice versa, depending on what's allowed by the filters. Export filters control export -of routes from the primary table to the secondary one, import filters control the opposite -direction. -

-

The Pipe protocol may work in the transparent mode mode or in the opaque mode. -In the transparent mode, the Pipe protocol retransmits all routes from -one table to the other table, retaining their original source and -attributes. If import and export filters are set to accept, then both -tables would have the same content. The transparent mode is the default mode. -

-

In the opaque mode, the Pipe protocol retransmits optimal route -from one table to the other table in a similar way like other -protocols send and receive routes. Retransmitted route will have the -source set to the Pipe protocol, which may limit access to protocol -specific route attributes. This mode is mainly for compatibility, it -is not suggested for new configs. The mode can be changed by -mode option. -

-

The primary use of multiple routing tables and the Pipe protocol is for policy routing, -where handling of a single packet doesn't depend only on its destination address, but also -on its source address, source interface, protocol type and other similar parameters. -In many systems (Linux being a good example), the kernel allows to enforce routing policies -by defining routing rules which choose one of several routing tables to be used for a packet -according to its parameters. Setting of these rules is outside the scope of BIRD's work -(on Linux, you can use the ip command), but you can create several routing tables in BIRD, -connect them to the kernel ones, use filters to control which routes appear in which tables -and also you can employ the Pipe protocol for exporting a selected subset of one table to -another one. -

-

Configuration

- -

-

-
peer table table

Defines secondary routing table to connect to. The -primary one is selected by the table keyword. -

-

mode opaque|transparent

Specifies the mode for the pipe to work in. Default is transparent. -

-

-

Attributes

- -

The Pipe protocol doesn't define any route attributes. -

-

Example

- -

Let's consider a router which serves as a boundary router of two different autonomous -systems, each of them connected to a subset of interfaces of the router, having its own -exterior connectivity and wishing to use the other AS as a backup connectivity in case -of outage of its own exterior line. -

-

Probably the simplest solution to this situation is to use two routing tables (we'll -call them as1 and as2) and set up kernel routing rules, so that packets having -arrived from interfaces belonging to the first AS will be routed according to as1 -and similarly for the second AS. Thus we have split our router to two logical routers, -each one acting on its own routing table, having its own routing protocols on its own -interfaces. In order to use the other AS's routes for backup purposes, we can pass -the routes between the tables through a Pipe protocol while decreasing their preferences -and correcting their BGP paths to reflect the AS boundary crossing. -

-


-
-table as1;                              # Define the tables
-table as2;
-
-protocol kernel kern1 {                 # Synchronize them with the kernel
-        table as1;
-        kernel table 1;
-}
-
-protocol kernel kern2 {
-        table as2;
-        kernel table 2;
-}
-
-protocol bgp bgp1 {                     # The outside connections
-        table as1;
-        local as 1;
-        neighbor 192.168.0.1 as 1001;
-        export all;
-        import all;
-}
-
-protocol bgp bgp2 {
-        table as2;
-        local as 2;
-        neighbor 10.0.0.1 as 1002;
-        export all;
-        import all;
-}
-
-protocol pipe {                         # The Pipe
-        table as1;
-        peer table as2;
-        export filter {
-                if net ~ [ 1.0.0.0/8+] then {   # Only AS1 networks
-                        if preference>10 then preference = preference-10;
-                        if source=RTS_BGP then bgp_path.prepend(1);
-                        accept;
-                }
-                reject;
-        };
-        import filter {
-                if net ~ [ 2.0.0.0/8+] then {   # Only AS2 networks
-                        if preference>10 then preference = preference-10;
-                        if source=RTS_BGP then bgp_path.prepend(2);
-                        accept;
-                }
-                reject;
-        };
-}
-
-
-

-

6.7 RAdv -

- -

Introduction

- -

The RAdv protocol is an implementation of Router Advertisements, -which are used in the IPv6 stateless autoconfiguration. IPv6 routers -send (in irregular time intervals or as an answer to a request) -advertisement packets to connected networks. These packets contain -basic information about a local network (e.g. a list of network -prefixes), which allows network hosts to autoconfigure network -addresses and choose a default route. BIRD implements router behavior -as defined in -RFC 4861 -ftp://ftp.rfc-editor.org/in-notes/rfc4861.txt -and also the DNS extensions from -RFC 6106 -ftp://ftp.rfc-editor.org/in-notes/rfc6106.txt. -

-

Configuration

- -

There are several classes of definitions in RAdv configuration -- -interface definitions, prefix definitions and DNS definitions: -

-

-
interface pattern [, ...] { options }

Interface definitions specify a set of interfaces on which the -protocol is activated and contain interface specific options. -See -interface common options for -detailed description. -

-

prefix prefix { options }

Prefix definitions allow to modify a list of advertised -prefixes. By default, the advertised prefixes are the same as -the network prefixes assigned to the interface. For each -network prefix, the matching prefix definition is found and -its options are used. If no matching prefix definition is -found, the prefix is used with default options. -

Prefix definitions can be either global or interface-specific. -The second ones are part of interface options. The prefix -definition matching is done in the first-match style, when -interface-specific definitions are processed before global -definitions. As expected, the prefix definition is matching if -the network prefix is a subnet of the prefix in prefix -definition. -

-

rdnss { options }

RDNSS definitions allow to specify a list of advertised -recursive DNS servers together with their options. As options -are seldom necessary, there is also a short variant rdnss -address that just specifies one DNS server. Multiple -definitions are cumulative. RDNSS definitions may also be -interface-specific when used inside interface options. By -default, interface uses both global and interface-specific -options, but that can be changed by rdnss local option. -

-

dnssl { options }

DNSSL definitions allow to specify a list of advertised DNS -search domains together with their options. Like rdnss -above, multiple definitions are cumulative, they can be used -also as interface-specific options and there is a short -variant dnssl domain that just specifies one DNS -search domain. -

- -

trigger prefix

RAdv protocol could be configured to change its behavior based -on availability of routes. When this option is used, the -protocol waits in suppressed state until a trigger route -(for the specified network) is exported to the protocol, the -protocol also returnsd to suppressed state if the -trigger route disappears. Note that route export depends -on specified export filter, as usual. This option could be -used, e.g., for handling failover in multihoming scenarios. -

During suppressed state, router advertisements are generated, -but with some fields zeroed. Exact behavior depends on which -fields are zeroed, this can be configured by -sensitive option for appropriate fields. By default, just -default lifetime (also called router lifetime) is -zeroed, which means hosts cannot use the router as a default -router. preferred lifetime and valid lifetime could -also be configured as sensitive for a prefix, which would -cause autoconfigured IPs to be deprecated or even removed. -

-

-

Interface specific options: -

-

-
max ra interval expr

Unsolicited router advertisements are sent in irregular time -intervals. This option specifies the maximum length of these -intervals, in seconds. Valid values are 4-1800. Default: 600 -

-

min ra interval expr

This option specifies the minimum length of that intervals, in -seconds. Must be at least 3 and at most 3/4 * max ra interval. -Default: about 1/3 * max ra interval. -

-

min delay expr

The minimum delay between two consecutive router advertisements, -in seconds. Default: 3 -

-

managed switch

This option specifies whether hosts should use DHCPv6 for -IP address configuration. Default: no -

-

other config switch

This option specifies whether hosts should use DHCPv6 to -receive other configuration information. Default: no -

-

link mtu expr

This option specifies which value of MTU should be used by -hosts. 0 means unspecified. Default: 0 -

-

reachable time expr

This option specifies the time (in milliseconds) how long -hosts should assume a neighbor is reachable (from the last -confirmation). Maximum is 3600000, 0 means unspecified. -Default 0. -

-

retrans timer expr

This option specifies the time (in milliseconds) how long -hosts should wait before retransmitting Neighbor Solicitation -messages. 0 means unspecified. Default 0. -

-

current hop limit expr

This option specifies which value of Hop Limit should be used -by hosts. Valid values are 0-255, 0 means unspecified. Default: 64 -

-

default lifetime expr [sensitive switch]

This option specifies the time (in seconds) how long (after -the receipt of RA) hosts may use the router as a default -router. 0 means do not use as a default router. For -sensitive option, see -trigger. -Default: 3 * max ra interval, sensitive yes. -

-

rdnss local switch

Use only local (interface-specific) RDNSS definitions for this -interface. Otherwise, both global and local definitions are -used. Could also be used to disable RDNSS for given interface -if no local definitons are specified. Default: no. -

-

dnssl local switch

Use only local DNSSL definitions for this interface. See -rdnss local option above. Default: no. -

-

-

-

Prefix specific options: -

-

-
skip switch

This option allows to specify that given prefix should not be -advertised. This is useful for making exceptions from a -default policy of advertising all prefixes. Note that for -withdrawing an already advertised prefix it is more useful to -advertise it with zero valid lifetime. Default: no -

-

onlink switch

This option specifies whether hosts may use the advertised -prefix for onlink determination. Default: yes -

-

autonomous switch

This option specifies whether hosts may use the advertised -prefix for stateless autoconfiguration. Default: yes -

-

valid lifetime expr [sensitive switch]

This option specifies the time (in seconds) how long (after -the receipt of RA) the prefix information is valid, i.e., -autoconfigured IP addresses can be assigned and hosts with -that IP addresses are considered directly reachable. 0 means -the prefix is no longer valid. For sensitive option, see -trigger. Default: 86400 (1 day), sensitive no. -

-

preferred lifetime expr [sensitive switch]

This option specifies the time (in seconds) how long (after -the receipt of RA) IP addresses generated from the prefix -using stateless autoconfiguration remain preferred. For -sensitive option, see -trigger. -Default: 14400 (4 hours), sensitive no. -

-

-

-

RDNSS specific options: -

-

-
ns address

This option specifies one recursive DNS server. Can be used -multiple times for multiple servers. It is mandatory to have -at least one ns option in rdnss definition. -

-

lifetime [mult] expr

This option specifies the time how long the RDNSS information -may be used by clients after the receipt of RA. It is -expressed either in seconds or (when mult is used) in -multiples of max ra interval. Note that RDNSS information -is also invalidated when default lifetime expires. 0 -means these addresses are no longer valid DNS servers. -Default: 3 * max ra interval. -

-

-

-

DNSSL specific options: -

-

-
domain address

This option specifies one DNS search domain. Can be used -multiple times for multiple domains. It is mandatory to have -at least one domain option in dnssl definition. -

-

lifetime [mult] expr

This option specifies the time how long the DNSSL information -may be used by clients after the receipt of RA. Details are -the same as for RDNSS lifetime option above. -Default: 3 * max ra interval. -

-

-

-

Example

- -

-


-
-protocol radv {
-        interface "eth2" {
-                max ra interval 5;      # Fast failover with more routers
-                managed yes;            # Using DHCPv6 on eth2
-                prefix ::/0 {
-                        autonomous off; # So do not autoconfigure any IP
-                };
-        };
-
-        interface "eth*";               # No need for any other options
-
-        prefix 2001:0DB8:1234::/48 {
-                preferred lifetime 0;   # Deprecated address range
-        };
-
-        prefix 2001:0DB8:2000::/48 {
-                autonomous off;         # Do not autoconfigure
-        };
-
-        rdnss 2001:0DB8:1234::10;       # Short form of RDNSS
-
-        rdnss {
-                lifetime mult 10;
-                ns 2001:0DB8:1234::11;
-                ns 2001:0DB8:1234::12;
-        };
-
-        dnssl {
-                lifetime 3600;
-                domain "abc.com";
-                domain "xyz.com";
-        };
-}
-
-
-

-

6.8 RIP -

- -

Introduction

- -

The RIP protocol (also sometimes called Rest In Pieces) is a simple protocol, where each router broadcasts (to all its neighbors) -distances to all networks it can reach. When a router hears distance to another network, it increments -it and broadcasts it back. Broadcasts are done in regular intervals. Therefore, if some network goes -unreachable, routers keep telling each other that its distance is the original distance plus 1 (actually, plus -interface metric, which is usually one). After some time, the distance reaches infinity (that's 15 in -RIP) and all routers know that network is unreachable. RIP tries to minimize situations where -counting to infinity is necessary, because it is slow. Due to infinity being 16, you can't use -RIP on networks where maximal distance is higher than 15 hosts. You can read more about RIP at -http://www.ietf.org/html.charters/rip-charter.html. Both IPv4 -(RFC 1723 -ftp://ftp.rfc-editor.org/in-notes/rfc1723.txt) -and IPv6 (RFC 2080 -ftp://ftp.rfc-editor.org/in-notes/rfc2080.txt) versions of RIP are supported by BIRD, historical RIPv1 (RFC 1058 -ftp://ftp.rfc-editor.org/in-notes/rfc1058.txt)is -not currently supported. RIPv4 MD5 authentication (RFC 2082 -ftp://ftp.rfc-editor.org/in-notes/rfc2082.txt) is supported. -

-

RIP is a very simple protocol, and it has a lot of shortcomings. Slow -convergence, big network load and inability to handle larger networks -makes it pretty much obsolete. (It is still usable on very small networks.) -

-

Configuration

- -

In addition to options common for all to other protocols, RIP supports the following ones: -

-

-
authentication none|plaintext|md5

selects authentication method to be used. none means that -packets are not authenticated at all, plaintext means that a plaintext password is embedded -into each packet, and md5 means that packets are authenticated using a MD5 cryptographic -hash. If you set authentication to not-none, it is a good idea to add password -section. Default: none. -

-

honor always|neighbor|never

specifies when should requests for dumping routing table -be honored. (Always, when sent from a host on a directly connected -network or never.) Routing table updates are honored only from -neighbors, that is not configurable. Default: never. -

-

-

There are some options that can be specified per-interface: -

-

-
metric num

This option specifies the metric of the interface. Valid -

-

mode multicast|broadcast|quiet|nolisten|version1

This option selects the mode for RIP to work in. If nothing is -specified, RIP runs in multicast mode. version1 is -currently equivalent to broadcast, and it makes RIP talk -to a broadcast address even through multicast mode is -possible. quiet option means that RIP will not transmit -any periodic messages to this interface and nolisten -means that RIP will send to this interface butnot listen to it. -

-

ttl security [switch | tx only]

TTL security is a feature that protects routing protocols -from remote spoofed packets by using TTL 255 instead of TTL 1 -for protocol packets destined to neighbors. Because TTL is -decremented when packets are forwarded, it is non-trivial to -spoof packets with TTL 255 from remote locations. -

If this option is enabled, the router will send RIP packets -with TTL 255 and drop received packets with TTL less than -255. If this option si set to tx only, TTL 255 is used -for sent packets, but is not checked for received -packets. Such setting does not offer protection, but offers -compatibility with neighbors regardless of whether they use -ttl security. -

Note that for RIPng, TTL security is a standard behavior -(required by RFC 2080), but BIRD uses tx only by -default, for compatibility with older versions. For IPv4 RIP, -default value is no. -

-

tx class|dscp|priority num

These options specify the ToS/DiffServ/Traffic class/Priority -of the outgoing RIP packets. See -tx class common option for detailed description. -

-

-

The following options generally override behavior specified in RFC. If you use any of these -options, BIRD will no longer be RFC-compliant, which means it will not be able to talk to anything -other than equally configured BIRD. I have warned you. -

-

-
port number

selects IP port to operate on, default 520. (This is useful when testing BIRD, if you -set this to an address >1024, you will not need to run bird with UID==0). -

-

infinity number

selects the value of infinity, default is 16. Bigger values will make protocol convergence -even slower. -

-

period number

specifies the number of seconds between periodic updates. Default is 30 seconds. A lower -number will mean faster convergence but bigger network -load. Do not use values lower than 12. -

-

timeout time number

specifies how old route has to be to be considered unreachable. Default is 4*period. -

-

garbage time number

specifies how old route has to be to be discarded. Default is 10*period. -

-

-

Attributes

- -

RIP defines two route attributes: -

-

-
int rip_metric

RIP metric of the route (ranging from 0 to infinity). -When routes from different RIP instances are available and all of them have the same -preference, BIRD prefers the route with lowest rip_metric. -When importing a non-RIP route, the metric defaults to 5. -

-

int rip_tag

RIP route tag: a 16-bit number which can be used -to carry additional information with the route (for example, an originating AS number -in case of external routes). When importing a non-RIP route, the tag defaults to 0. -

-

-

Example

- -

-


-
-protocol rip MyRIP_test {
-        debug all;
-        port 1520;
-        period 12;
-        garbage time 60;
-        interface "eth0" { metric 3; mode multicast; };
-        interface "eth*" { metric 2; mode broadcast; };
-        honor neighbor;
-        authentication none;
-        import filter { print "importing"; accept; };
-        export filter { print "exporting"; accept; };
-}
-
-
-

-

6.9 Static -

- -

The Static protocol doesn't communicate with other routers in the network, -but instead it allows you to define routes manually. This is often used for -specifying how to forward packets to parts of the network which don't use -dynamic routing at all and also for defining sink routes (i.e., those -telling to return packets as undeliverable if they are in your IP block, -you don't have any specific destination for them and you don't want to send -them out through the default route to prevent routing loops). -

-

There are five types of static routes: `classical' routes telling -to forward packets to a neighboring router, multipath routes -specifying several (possibly weighted) neighboring routers, device -routes specifying forwarding to hosts on a directly connected network, -recursive routes computing their nexthops by doing route table lookups -for a given IP and special routes (sink, blackhole etc.) which specify -a special action to be done instead of forwarding the packet. -

-

When the particular destination is not available (the interface is down or -the next hop of the route is not a neighbor at the moment), Static just -uninstalls the route from the table it is connected to and adds it again as soon -as the destination becomes adjacent again. -

-

The Static protocol does not have many configuration options. The -definition of the protocol contains mainly a list of static routes: -

-

-
route prefix via ip

Static route through -a neighboring router. -

route prefix multipath via ip [weight num] [via ...]

Static multipath route. Contains several nexthops (gateways), possibly -with their weights. -

route prefix via "interface"

Static device -route through an interface to hosts on a directly connected network. -

route prefix recursive ip

Static recursive route, -its nexthop depends on a route table lookup for given IP address. -

route prefix blackhole|unreachable|prohibit

Special routes -specifying to silently drop the packet, return it as unreachable or return -it as administratively prohibited. First two targets are also known -as drop and reject. -

-

check link switch

If set, hardware link states of network interfaces are taken -into consideration. When link disappears (e.g. ethernet cable -is unplugged), static routes directing to that interface are -removed. It is possible that some hardware drivers or -platforms do not implement this feature. Default: off. -

-

igp table name

Specifies a table that is used -for route table lookups of recursive routes. Default: the -same table as the protocol is connected to. -

-

-

Static routes have no specific attributes. -

-

Example static config might look like this: -

-

-


-
-protocol static {
-        table testable;                  # Connect to a non-default routing table
-        route 0.0.0.0/0 via 198.51.100.130; # Default route
-        route 10.0.0.0/8 multipath       # Multipath route
-                via 198.51.100.10 weight 2
-                via 198.51.100.20
-                via 192.0.2.1;
-        route 203.0.113.0/24 unreachable; # Sink route
-        route 10.2.0.0/24 via "arc0";    # Secondary network
-}
-
-
-

-


-Next -Previous -Contents - - diff --git a/doc/bird.sgml b/doc/bird.sgml index a2266424..5ceb8ab9 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -477,7 +477,7 @@ to zero to disable it. An empty is equivalent to import limit [ +

BFD configuration consists mainly of multiple definitions of interfaces. +Most BFD config options are session specific. When a new session is requested +and dynamically created, it is configured from one of these definitions. For +sessions to directly connected neighbors, Note that to use BFD for other protocols like OSPF or BGP, these protocols +also have to be configured to request BFD sessions, usually by Some of BFD session options require +protocol bfd [<name>] { + interface <interface pattern> { + interval <time>; + min rx interval <time>; + min tx interval <time>; + idle tx interval <time>; + multiplier <num>; + passive <switch>; + }; + multihop { + interval <time>; + min rx interval <time>; + min tx interval <time>; + idle tx interval <time>; + multiplier <num>; + passive <switch>; + }; + neighbor <ip> [dev "<interface>"] [local <ip>] [multihop <switch>]; +} + + + + interface + Interface definitions allow to specify options for sessions associated + with such interfaces and also may contain interface specific options. + See common option for a detailed + description of interface patterns. Note that contrary to the behavior of + multihop { + Multihop definitions allow to specify options for multihop BFD sessions, + in the same manner as neighbor + BFD sessions are usually created on demand as requested by other + protocols (like OSPF or BGP). This option allows to explicitly add + a BFD session to the specified neighbor regardless of such requests. + + The session is identified by the IP address of the neighbor, with + optional specification of used interface and local IP. By default + the neighbor must be directly connected, unless the the session is + configured as multihop. Note that local IP must be specified for + multihop sessions. + + +