summaryrefslogtreecommitdiff
path: root/proto
diff options
context:
space:
mode:
Diffstat (limited to 'proto')
-rw-r--r--proto/Doc3
-rw-r--r--proto/babel/Makefile9
-rw-r--r--proto/babel/babel.c202
-rw-r--r--proto/babel/babel.h25
-rw-r--r--proto/babel/config.Y2
-rw-r--r--proto/babel/packets.c56
-rw-r--r--proto/bfd/Makefile9
-rw-r--r--proto/bfd/bfd.c11
-rw-r--r--proto/bfd/bfd.h8
-rw-r--r--proto/bfd/io.h2
-rw-r--r--proto/bfd/packets.c13
-rw-r--r--proto/bgp/Makefile9
-rw-r--r--proto/bgp/attrs.c2756
-rw-r--r--proto/bgp/bgp.c1634
-rw-r--r--proto/bgp/bgp.h486
-rw-r--r--proto/bgp/config.Y159
-rw-r--r--proto/bgp/packets.c3405
-rw-r--r--proto/ospf/Makefile9
-rw-r--r--proto/ospf/config.Y63
-rw-r--r--proto/ospf/dbdes.c2
-rw-r--r--proto/ospf/hello.c6
-rw-r--r--proto/ospf/iface.c48
-rw-r--r--proto/ospf/lsalib.c30
-rw-r--r--proto/ospf/lsalib.h4
-rw-r--r--proto/ospf/neighbor.c7
-rw-r--r--proto/ospf/ospf.c116
-rw-r--r--proto/ospf/ospf.h108
-rw-r--r--proto/ospf/packet.c8
-rw-r--r--proto/ospf/rt.c389
-rw-r--r--proto/ospf/rt.h6
-rw-r--r--proto/ospf/topology.c120
-rw-r--r--proto/ospf/topology.h6
-rw-r--r--proto/pipe/Makefile10
-rw-r--r--proto/pipe/config.Y25
-rw-r--r--proto/pipe/pipe.c266
-rw-r--r--proto/pipe/pipe.h16
-rw-r--r--proto/radv/Makefile9
-rw-r--r--proto/radv/config.Y33
-rw-r--r--proto/radv/packets.c26
-rw-r--r--proto/radv/radv.c55
-rw-r--r--proto/radv/radv.h11
-rw-r--r--proto/rip/Makefile9
-rw-r--r--proto/rip/config.Y22
-rw-r--r--proto/rip/packets.c81
-rw-r--r--proto/rip/rip.c144
-rw-r--r--proto/rip/rip.h21
-rw-r--r--proto/rpki/Doc5
-rw-r--r--proto/rpki/Makefile6
-rw-r--r--proto/rpki/config.Y144
-rw-r--r--proto/rpki/packets.c1073
-rw-r--r--proto/rpki/packets.h45
-rw-r--r--proto/rpki/rpki.c926
-rw-r--r--proto/rpki/rpki.h165
-rw-r--r--proto/rpki/ssh_transport.c75
-rw-r--r--proto/rpki/tcp_transport.c78
-rw-r--r--proto/rpki/transport.c135
-rw-r--r--proto/rpki/transport.h79
-rw-r--r--proto/static/Makefile10
-rw-r--r--proto/static/config.Y133
-rw-r--r--proto/static/static.c887
-rw-r--r--proto/static/static.h53
61 files changed, 9439 insertions, 4814 deletions
diff --git a/proto/Doc b/proto/Doc
index 04c25bc0..ef573d2a 100644
--- a/proto/Doc
+++ b/proto/Doc
@@ -4,7 +4,8 @@ C bfd
C bgp
C ospf
C pipe
-C rip
C radv
+C rip
+C rpki
C static
S ../nest/rt-dev.c
diff --git a/proto/babel/Makefile b/proto/babel/Makefile
index 400ffbac..a5b4a13b 100644
--- a/proto/babel/Makefile
+++ b/proto/babel/Makefile
@@ -1,5 +1,6 @@
-source=babel.c packets.c
-root-rel=../../
-dir-name=proto/babel
+src := babel.c packets.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
-include ../../Rules
+tests_objs := $(tests_objs) $(src-o-files) \ No newline at end of file
diff --git a/proto/babel/babel.c b/proto/babel/babel.c
index 38be6909..1b1d9f62 100644
--- a/proto/babel/babel.c
+++ b/proto/babel/babel.c
@@ -53,8 +53,7 @@ static void babel_dump_route(struct babel_route *r);
static void babel_select_route(struct babel_entry *e);
static void babel_send_route_request(struct babel_entry *e, struct babel_neighbor *n);
static void babel_send_wildcard_request(struct babel_iface *ifa);
-static int babel_cache_seqno_request(struct babel_proto *p, ip_addr prefix, u8 plen,
- u64 router_id, u16 seqno);
+static int babel_cache_seqno_request(struct babel_proto *p, net_addr *n, u64 router_id, u16 seqno);
static void babel_trigger_iface_update(struct babel_iface *ifa);
static void babel_trigger_update(struct babel_proto *p);
static void babel_send_seqno_request(struct babel_entry *e);
@@ -67,27 +66,25 @@ static inline void babel_iface_kick_timer(struct babel_iface *ifa);
*/
static void
-babel_init_entry(struct fib_node *n)
+babel_init_entry(void *E)
{
- struct babel_entry *e = (void *) n;
- e->proto = NULL;
- e->selected_in = NULL;
- e->selected_out = NULL;
+ struct babel_entry *e = E;
+
e->updated = now;
init_list(&e->sources);
init_list(&e->routes);
}
static inline struct babel_entry *
-babel_find_entry(struct babel_proto *p, ip_addr prefix, u8 plen)
+babel_find_entry(struct babel_proto *p, const net_addr *n)
{
- return fib_find(&p->rtable, &prefix, plen);
+ return fib_find(&p->rtable, n);
}
static struct babel_entry *
-babel_get_entry(struct babel_proto *p, ip_addr prefix, u8 plen)
+babel_get_entry(struct babel_proto *p, const net_addr *n)
{
- struct babel_entry *e = fib_get(&p->rtable, &prefix, plen);
+ struct babel_entry *e = fib_get(&p->rtable, n);
e->proto = p;
return e;
}
@@ -180,8 +177,8 @@ babel_flush_route(struct babel_route *r)
{
struct babel_proto *p = r->e->proto;
- DBG("Babel: Flush route %I/%d router_id %lR neigh %I\n",
- r->e->n.prefix, r->e->n.pxlen, r->router_id, r->neigh ? r->neigh->addr : IPA_NONE);
+ DBG("Babel: Flush route %N router_id %lR neigh %I\n",
+ r->e->n.addr, r->router_id, r->neigh ? r->neigh->addr : IPA_NONE);
rem_node(NODE r);
@@ -203,8 +200,8 @@ babel_expire_route(struct babel_route *r)
struct babel_proto *p = r->e->proto;
struct babel_entry *e = r->e;
- TRACE(D_EVENTS, "Route expiry timer for %I/%d router-id %lR fired",
- e->n.prefix, e->n.pxlen, r->router_id);
+ TRACE(D_EVENTS, "Route expiry timer for %N router-id %lR fired",
+ e->n.addr, r->router_id);
if (r->metric < BABEL_INFINITY)
{
@@ -229,16 +226,14 @@ babel_refresh_route(struct babel_route *r)
static void
babel_expire_routes(struct babel_proto *p)
{
- struct babel_entry *e;
struct babel_route *r, *rx;
struct fib_iterator fit;
FIB_ITERATE_INIT(&fit, &p->rtable);
loop:
- FIB_ITERATE_START(&p->rtable, &fit, n)
+ FIB_ITERATE_START(&p->rtable, &fit, struct babel_entry, e)
{
- e = (struct babel_entry *) n;
int changed = 0;
WALK_LIST_DELSAFE(r, rx, e->routes)
@@ -261,7 +256,7 @@ loop:
* babel_rt_notify() -> p->rtable change, invalidating hidden variables.
*/
- FIB_ITERATE_PUT(&fit, n);
+ FIB_ITERATE_PUT(&fit);
babel_select_route(e);
goto loop;
}
@@ -271,12 +266,12 @@ loop:
/* Remove empty entries */
if (EMPTY_LIST(e->sources) && EMPTY_LIST(e->routes))
{
- FIB_ITERATE_PUT(&fit, n);
+ FIB_ITERATE_PUT(&fit);
fib_delete(&p->rtable, e);
goto loop;
}
}
- FIB_ITERATE_END(n);
+ FIB_ITERATE_END;
}
static struct babel_neighbor *
@@ -476,35 +471,31 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
if (r)
{
- net *n = net_get(p->p.table, e->n.prefix, e->n.pxlen);
- rta A = {
+ rta *ap0 = allocz(RTA_MAX_SIZE);
+ *ap0 = (rta) {
.src = p->p.main_source,
.source = RTS_BABEL,
.scope = SCOPE_UNIVERSE,
- .cast = RTC_UNICAST,
- .dest = r->metric == BABEL_INFINITY ? RTD_UNREACHABLE : RTD_ROUTER,
- .flags = 0,
+ .dest = r->metric == BABEL_INFINITY ? RTD_UNREACHABLE : RTD_UNICAST,
.from = r->neigh->addr,
- .iface = r->neigh->ifa->iface,
+ .nh.iface = r->neigh->ifa->iface,
};
if (r->metric < BABEL_INFINITY)
- A.gw = r->next_hop;
+ ap0->nh.gw = r->next_hop;
- rta *a = rta_lookup(&A);
+ rta *a = rta_lookup(ap0);
rte *rte = rte_get_temp(a);
rte->u.babel.metric = r->metric;
rte->u.babel.router_id = r->router_id;
- rte->net = n;
rte->pflags = 0;
- rte_update(&p->p, n, rte);
+ rte_update(&p->p, e->n.addr, rte);
}
else
{
/* Retraction */
- net *n = net_find(p->p.table, e->n.prefix, e->n.pxlen);
- rte_update(&p->p, n, NULL);
+ rte_update(&p->p, e->n.addr, NULL);
}
}
@@ -541,8 +532,8 @@ babel_select_route(struct babel_entry *e)
((!e->selected_in && cur->metric < BABEL_INFINITY) ||
(e->selected_in && cur->metric < e->selected_in->metric)))
{
- TRACE(D_EVENTS, "Picked new route for prefix %I/%d: router id %lR metric %d",
- e->n.prefix, e->n.pxlen, cur->router_id, cur->metric);
+ TRACE(D_EVENTS, "Picked new route for prefix %N: router id %lR metric %d",
+ e->n.addr, cur->router_id, cur->metric);
e->selected_in = cur;
e->updated = now;
@@ -557,8 +548,8 @@ babel_select_route(struct babel_entry *e)
babel_build_rte() will set the unreachable flag if the metric is BABEL_INFINITY.*/
if (e->selected_in)
{
- TRACE(D_EVENTS, "Lost feasible route for prefix %I/%d",
- e->n.prefix, e->n.pxlen);
+ TRACE(D_EVENTS, "Lost feasible route for prefix %N",
+ e->n.addr);
e->selected_in->metric = BABEL_INFINITY;
e->updated = now;
@@ -576,7 +567,7 @@ babel_select_route(struct babel_entry *e)
/* No route currently selected, and no new one selected; this means we
don't have a route to this destination anymore (and were probably
called from an expiry timer). Remove the route from the nest. */
- TRACE(D_EVENTS, "Flushing route for prefix %I/%d", e->n.prefix, e->n.pxlen);
+ TRACE(D_EVENTS, "Flushing route for prefix %N", e->n.addr);
e->selected_in = NULL;
e->updated = now;
@@ -663,12 +654,11 @@ babel_send_route_request(struct babel_entry *e, struct babel_neighbor *n)
struct babel_iface *ifa = n->ifa;
union babel_msg msg = {};
- TRACE(D_PACKETS, "Sending route request for %I/%d to %I",
- e->n.prefix, e->n.pxlen, n->addr);
+ TRACE(D_PACKETS, "Sending route request for %N to %I",
+ e->n.addr, n->addr);
msg.type = BABEL_TLV_ROUTE_REQUEST;
- msg.route_request.prefix = e->n.prefix;
- msg.route_request.plen = e->n.pxlen;
+ net_copy(&msg.route_request.net, e->n.addr);
babel_send_unicast(&msg, ifa, n->addr);
}
@@ -698,18 +688,17 @@ babel_send_seqno_request(struct babel_entry *e)
union babel_msg msg = {};
s = babel_find_source(e, r->router_id);
- if (!s || !babel_cache_seqno_request(p, e->n.prefix, e->n.pxlen, r->router_id, s->seqno + 1))
+ if (!s || !babel_cache_seqno_request(p, e->n.addr, r->router_id, s->seqno + 1))
return;
- TRACE(D_PACKETS, "Sending seqno request for %I/%d router-id %lR seqno %d",
- e->n.prefix, e->n.pxlen, r->router_id, s->seqno + 1);
+ TRACE(D_PACKETS, "Sending seqno request for %N router-id %lR seqno %d",
+ e->n.addr, r->router_id, s->seqno + 1);
msg.type = BABEL_TLV_SEQNO_REQUEST;
- msg.seqno_request.plen = e->n.pxlen;
- msg.seqno_request.seqno = s->seqno + 1;
msg.seqno_request.hop_count = BABEL_INITIAL_HOP_COUNT;
+ msg.seqno_request.seqno = s->seqno + 1;
msg.seqno_request.router_id = r->router_id;
- msg.seqno_request.prefix = e->n.prefix;
+ net_copy(&msg.seqno_request.net, e->n.addr);
WALK_LIST(ifa, p->interfaces)
babel_enqueue(&msg, ifa);
@@ -725,18 +714,17 @@ babel_unicast_seqno_request(struct babel_route *r)
union babel_msg msg = {};
s = babel_find_source(e, r->router_id);
- if (!s || !babel_cache_seqno_request(p, e->n.prefix, e->n.pxlen, r->router_id, s->seqno + 1))
+ if (!s || !babel_cache_seqno_request(p, e->n.addr, r->router_id, s->seqno + 1))
return;
- TRACE(D_PACKETS, "Sending seqno request for %I/%d router-id %lR seqno %d",
- e->n.prefix, e->n.pxlen, r->router_id, s->seqno + 1);
+ TRACE(D_PACKETS, "Sending seqno request for %N router-id %lR seqno %d",
+ e->n.addr, r->router_id, s->seqno + 1);
msg.type = BABEL_TLV_SEQNO_REQUEST;
- msg.seqno_request.plen = e->n.pxlen;
- msg.seqno_request.seqno = s->seqno + 1;
msg.seqno_request.hop_count = BABEL_INITIAL_HOP_COUNT;
+ msg.seqno_request.seqno = s->seqno + 1;
msg.seqno_request.router_id = r->router_id;
- msg.seqno_request.prefix = e->n.prefix;
+ net_copy(&msg.seqno_request.net, e->n.addr);
babel_send_unicast(&msg, ifa, r->neigh->addr);
}
@@ -756,9 +744,8 @@ babel_send_update(struct babel_iface *ifa, bird_clock_t changed)
{
struct babel_proto *p = ifa->proto;
- FIB_WALK(&p->rtable, n)
+ FIB_WALK(&p->rtable, struct babel_entry, e)
{
- struct babel_entry *e = (void *) n;
struct babel_route *r = e->selected_out;
if (!r)
@@ -776,17 +763,16 @@ babel_send_update(struct babel_iface *ifa, bird_clock_t changed)
if (e->updated < changed)
continue;
- TRACE(D_PACKETS, "Sending update for %I/%d router-id %lR seqno %d metric %d",
- e->n.prefix, e->n.pxlen, r->router_id, r->seqno, r->metric);
+ TRACE(D_PACKETS, "Sending update for %N router-id %lR seqno %d metric %d",
+ e->n.addr, r->router_id, r->seqno, r->metric);
union babel_msg msg = {};
msg.type = BABEL_TLV_UPDATE;
- msg.update.plen = e->n.pxlen;
msg.update.interval = ifa->cf->update_interval;
msg.update.seqno = r->seqno;
msg.update.metric = r->metric;
- msg.update.prefix = e->n.prefix;
msg.update.router_id = r->router_id;
+ net_copy(&msg.update.net, e->n.addr);
babel_enqueue(&msg, ifa);
@@ -839,20 +825,18 @@ babel_trigger_update(struct babel_proto *p)
/* A retraction is an update with an infinite metric */
static void
-babel_send_retraction(struct babel_iface *ifa, ip_addr prefix, int plen)
+babel_send_retraction(struct babel_iface *ifa, net_addr *n)
{
struct babel_proto *p = ifa->proto;
union babel_msg msg = {};
- TRACE(D_PACKETS, "Sending retraction for %I/%d seqno %d",
- prefix, plen, p->update_seqno);
+ TRACE(D_PACKETS, "Sending retraction for %N seqno %d", n, p->update_seqno);
msg.type = BABEL_TLV_UPDATE;
- msg.update.plen = plen;
msg.update.interval = ifa->cf->update_interval;
msg.update.seqno = p->update_seqno;
msg.update.metric = BABEL_INFINITY;
- msg.update.prefix = prefix;
+ msg.update.net = *n;
babel_enqueue(&msg, ifa);
}
@@ -941,22 +925,20 @@ babel_expire_seqno_requests(struct babel_proto *p)
* found. Otherwise, a new entry is stored in the cache.
*/
static int
-babel_cache_seqno_request(struct babel_proto *p, ip_addr prefix, u8 plen,
+babel_cache_seqno_request(struct babel_proto *p, net_addr *n,
u64 router_id, u16 seqno)
{
struct babel_seqno_request *r;
WALK_LIST(r, p->seqno_cache)
{
- if (ipa_equal(r->prefix, prefix) && (r->plen == plen) &&
- (r->router_id == router_id) && (r->seqno == seqno))
+ if (net_equal(&r->net, n) && (r->router_id == router_id) && (r->seqno == seqno))
return 0;
}
/* no entries found */
r = sl_alloc(p->seqno_slab);
- r->prefix = prefix;
- r->plen = plen;
+ net_copy(&r->net, n);
r->router_id = router_id;
r->seqno = seqno;
r->updated = now;
@@ -973,8 +955,8 @@ babel_forward_seqno_request(struct babel_entry *e,
struct babel_proto *p = e->proto;
struct babel_route *r;
- TRACE(D_PACKETS, "Forwarding seqno request for %I/%d router-id %lR seqno %d",
- e->n.prefix, e->n.pxlen, in->router_id, in->seqno);
+ TRACE(D_PACKETS, "Forwarding seqno request for %N router-id %lR seqno %d",
+ e->n.addr, in->router_id, in->seqno);
WALK_LIST(r, e->routes)
{
@@ -982,16 +964,15 @@ babel_forward_seqno_request(struct babel_entry *e,
!OUR_ROUTE(r) &&
!ipa_equal(r->neigh->addr, sender))
{
- if (!babel_cache_seqno_request(p, e->n.prefix, e->n.pxlen, in->router_id, in->seqno))
+ if (!babel_cache_seqno_request(p, e->n.addr, in->router_id, in->seqno))
return;
union babel_msg msg = {};
msg.type = BABEL_TLV_SEQNO_REQUEST;
- msg.seqno_request.plen = in->plen;
- msg.seqno_request.seqno = in->seqno;
msg.seqno_request.hop_count = in->hop_count-1;
+ msg.seqno_request.seqno = in->seqno;
msg.seqno_request.router_id = in->router_id;
- msg.seqno_request.prefix = e->n.prefix;
+ net_copy(&msg.seqno_request.net, e->n.addr);
babel_send_unicast(&msg, r->neigh->ifa, r->neigh->addr);
return;
@@ -1073,8 +1054,11 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
node *n;
int feasible;
- TRACE(D_PACKETS, "Handling update for %I/%d with seqno %d metric %d",
- msg->prefix, msg->plen, msg->seqno, msg->metric);
+ if (msg->wildcard)
+ TRACE(D_PACKETS, "Handling wildcard retraction", msg->seqno);
+ else
+ TRACE(D_PACKETS, "Handling update for %N with seqno %d metric %d",
+ &msg->net, msg->seqno, msg->metric);
nbr = babel_find_neighbor(ifa, msg->sender);
if (!nbr)
@@ -1140,7 +1124,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
}
else
{
- e = babel_find_entry(p, msg->prefix, msg->plen);
+ e = babel_find_entry(p, &msg->net);
if (!e)
return;
@@ -1159,7 +1143,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
return;
}
- e = babel_get_entry(p, msg->prefix, msg->plen);
+ e = babel_get_entry(p, &msg->net);
r = babel_find_route(e, nbr); /* the route entry indexed by neighbour */
s = babel_find_source(e, msg->router_id); /* for feasibility */
feasible = babel_is_feasible(s, msg->seqno, msg->metric);
@@ -1231,14 +1215,14 @@ babel_handle_route_request(union babel_msg *m, struct babel_iface *ifa)
return;
}
- TRACE(D_PACKETS, "Handling route request for %I/%d", msg->prefix, msg->plen);
+ TRACE(D_PACKETS, "Handling route request for %N", &msg->net);
/* Non-wildcard request - see if we have an entry for the route.
If not, send a retraction, otherwise send an update. */
- struct babel_entry *e = babel_find_entry(p, msg->prefix, msg->plen);
+ struct babel_entry *e = babel_find_entry(p, &msg->net);
if (!e)
{
- babel_send_retraction(ifa, msg->prefix, msg->plen);
+ babel_send_retraction(ifa, &msg->net);
}
else
{
@@ -1256,11 +1240,11 @@ babel_handle_seqno_request(union babel_msg *m, struct babel_iface *ifa)
/* RFC 6126 3.8.1.2 */
- TRACE(D_PACKETS, "Handling seqno request for %I/%d router-id %lR seqno %d hop count %d",
- msg->prefix, msg->plen, msg->router_id, msg->seqno, msg->hop_count);
+ TRACE(D_PACKETS, "Handling seqno request for %N router-id %lR seqno %d hop count %d",
+ &msg->net, msg->router_id, msg->seqno, msg->hop_count);
/* Ignore if we have no such entry or entry has infinite metric */
- struct babel_entry *e = babel_find_entry(p, msg->prefix, msg->plen);
+ struct babel_entry *e = babel_find_entry(p, &msg->net);
if (!e || !e->selected_out || (e->selected_out->metric == BABEL_INFINITY))
return;
@@ -1668,7 +1652,7 @@ babel_dump_entry(struct babel_entry *e)
struct babel_source *s;
struct babel_route *r;
- debug("Babel: Entry %I/%d:\n", e->n.prefix, e->n.pxlen);
+ debug("Babel: Entry %N:\n", e->n.addr);
WALK_LIST(s,e->sources)
{ debug(" "); babel_dump_source(s); }
@@ -1715,9 +1699,9 @@ babel_dump(struct proto *P)
WALK_LIST(ifa, p->interfaces)
babel_dump_iface(ifa);
- FIB_WALK(&p->rtable, n)
+ FIB_WALK(&p->rtable, struct babel_entry, e)
{
- babel_dump_entry((struct babel_entry *) n);
+ babel_dump_entry(e);
}
FIB_WALK_END;
}
@@ -1828,11 +1812,9 @@ void
babel_show_entries(struct proto *P)
{
struct babel_proto *p = (void *) P;
- struct babel_entry *e = NULL;
struct babel_source *s = NULL;
struct babel_route *r = NULL;
- char ipbuf[STD_ADDRESS_P_LENGTH+5];
char ridbuf[ROUTER_ID_64_LENGTH+1];
if (p->p.proto_state != PS_UP)
@@ -1846,17 +1828,14 @@ babel_show_entries(struct proto *P)
cli_msg(-1025, "%-29s %-23s %6s %5s %7s %7s",
"Prefix", "Router ID", "Metric", "Seqno", "Expires", "Sources");
- FIB_WALK(&p->rtable, n)
+ FIB_WALK(&p->rtable, struct babel_entry, e)
{
- e = (struct babel_entry *) n;
r = e->selected_in ? e->selected_in : e->selected_out;
int srcs = 0;
WALK_LIST(s, e->sources)
srcs++;
- bsprintf(ipbuf, "%I/%u", e->n.prefix, e->n.pxlen);
-
if (r)
{
if (r->router_id == p->router_id)
@@ -1865,12 +1844,12 @@ babel_show_entries(struct proto *P)
bsprintf(ridbuf, "%lR", r->router_id);
int time = r->expires ? r->expires - now : 0;
- cli_msg(-1025, "%-29s %-23s %6u %5u %7u %7u",
- ipbuf, ridbuf, r->metric, r->seqno, MAX(time, 0), srcs);
+ cli_msg(-1025, "%-29N %-23s %6u %5u %7u %7u",
+ e->n.addr, ridbuf, r->metric, r->seqno, MAX(time, 0), srcs);
}
else
{
- cli_msg(-1025, "%-29s %-44s %7u", ipbuf, "<pending>", srcs);
+ cli_msg(-1025, "%-29N %-44s %7u", e->n.addr, "<pending>", srcs);
}
}
FIB_WALK_END;
@@ -1964,7 +1943,7 @@ babel_store_tmp_attrs(struct rte *rt, struct ea_list *attrs)
* so store it into our data structures.
*/
static void
-babel_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net,
+babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
struct rte *new, struct rte *old UNUSED, struct ea_list *attrs UNUSED)
{
struct babel_proto *p = (void *) P;
@@ -1974,7 +1953,7 @@ babel_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *ne
if (new)
{
/* Update */
- e = babel_get_entry(p, net->n.prefix, net->n.pxlen);
+ e = babel_get_entry(p, net->n.addr);
if (new->attrs->src->proto != P)
{
@@ -1996,7 +1975,7 @@ babel_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *ne
else
{
/* Withdraw */
- e = babel_find_entry(p, net->n.prefix, net->n.pxlen);
+ e = babel_find_entry(p, net->n.addr);
if (!e || !e->selected_out)
return;
@@ -2046,11 +2025,12 @@ babel_rte_same(struct rte *new, struct rte *old)
static struct proto *
-babel_init(struct proto_config *cfg)
+babel_init(struct proto_config *CF)
{
- struct proto *P = proto_new(cfg, sizeof(struct babel_proto));
+ struct proto *P = proto_new(CF);
+
+ P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
- P->accept_ra_types = RA_OPTIMAL;
P->if_notify = babel_if_notify;
P->rt_notify = babel_rt_notify;
P->import_control = babel_import_control;
@@ -2068,7 +2048,8 @@ babel_start(struct proto *P)
struct babel_proto *p = (void *) P;
struct babel_config *cf = (void *) P->cf;
- fib_init(&p->rtable, P->pool, sizeof(struct babel_entry), 0, babel_init_entry);
+ fib_init(&p->rtable, P->pool, NET_IP6, sizeof(struct babel_entry),
+ OFFSETOF(struct babel_entry, n), 0, babel_init_entry);
init_list(&p->interfaces);
p->timer = tm_new_set(P->pool, babel_timer, p, 0, 1);
tm_start(p->timer, 2);
@@ -2111,14 +2092,17 @@ babel_shutdown(struct proto *P)
}
static int
-babel_reconfigure(struct proto *P, struct proto_config *c)
+babel_reconfigure(struct proto *P, struct proto_config *CF)
{
struct babel_proto *p = (void *) P;
- struct babel_config *new = (void *) c;
+ struct babel_config *new = (void *) CF;
TRACE(D_EVENTS, "Reconfiguring");
- p->p.cf = c;
+ if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
+ return 0;
+
+ p->p.cf = CF;
babel_reconfigure_ifaces(p, new);
babel_trigger_update(p);
@@ -2133,6 +2117,8 @@ struct protocol proto_babel = {
.template = "babel%d",
.attr_class = EAP_BABEL,
.preference = DEF_PREF_BABEL,
+ .channel_mask = NB_IP6,
+ .proto_size = sizeof(struct babel_proto),
.config_size = sizeof(struct babel_config),
.init = babel_init,
.dump = babel_dump,
diff --git a/proto/babel/babel.h b/proto/babel/babel.h
index 6a95d82f..792c9d60 100644
--- a/proto/babel/babel.h
+++ b/proto/babel/babel.h
@@ -21,11 +21,7 @@
#include "lib/lists.h"
#include "lib/socket.h"
#include "lib/string.h"
-#include "lib/timer.h"
-
-#ifndef IPV6
-#error "The Babel protocol only speaks IPv6"
-#endif
+#include "sysdep/unix/timer.h"
#define EA_BABEL_METRIC EA_CODE(EAP_BABEL, 0)
#define EA_BABEL_ROUTER_ID EA_CODE(EAP_BABEL, 1)
@@ -56,7 +52,7 @@
/* Max interval that will not overflow when carried as 16-bit centiseconds */
#define BABEL_MAX_INTERVAL (0xFFFF/BABEL_TIME_UNITS)
-#define BABEL_OVERHEAD (SIZE_OF_IP_HEADER+UDP_HEADER_LENGTH)
+#define BABEL_OVERHEAD (IP6_HEADER_LENGTH+UDP_HEADER_LENGTH)
#define BABEL_MIN_MTU (512 + BABEL_OVERHEAD)
@@ -212,7 +208,6 @@ struct babel_route {
};
struct babel_entry {
- struct fib_node n;
struct babel_proto *proto;
struct babel_route *selected_in;
struct babel_route *selected_out;
@@ -221,13 +216,14 @@ struct babel_entry {
list sources; /* Source entries for this prefix (struct babel_source). */
list routes; /* Routes for this prefix (struct babel_route) */
+
+ struct fib_node n;
};
/* Stores forwarded seqno requests for duplicate suppression. */
struct babel_seqno_request {
node n;
- ip_addr prefix;
- u8 plen;
+ net_addr net;
u64 router_id;
u16 seqno;
bird_clock_t updated;
@@ -269,12 +265,11 @@ struct babel_msg_ihu {
struct babel_msg_update {
u8 type;
u8 wildcard;
- u8 plen;
u16 interval;
u16 seqno;
u16 metric;
- ip_addr prefix;
u64 router_id;
+ net_addr net;
ip_addr next_hop;
ip_addr sender;
};
@@ -282,17 +277,15 @@ struct babel_msg_update {
struct babel_msg_route_request {
u8 type;
u8 full;
- u8 plen;
- ip_addr prefix;
+ net_addr net;
};
struct babel_msg_seqno_request {
u8 type;
- u8 plen;
- u16 seqno;
u8 hop_count;
+ u16 seqno;
u64 router_id;
- ip_addr prefix;
+ net_addr net;
ip_addr sender;
};
diff --git a/proto/babel/config.Y b/proto/babel/config.Y
index b6170852..cf8983fa 100644
--- a/proto/babel/config.Y
+++ b/proto/babel/config.Y
@@ -30,11 +30,13 @@ CF_ADDTO(proto, babel_proto)
babel_proto_start: proto_start BABEL
{
this_proto = proto_config_new(&proto_babel, $1);
+ this_proto->net_type = NET_IP6;
init_list(&BABEL_CFG->iface_list);
};
babel_proto_item:
proto_item
+ | proto_channel
| INTERFACE babel_iface
;
diff --git a/proto/babel/packets.c b/proto/babel/packets.c
index 08054832..70cfc196 100644
--- a/proto/babel/packets.c
+++ b/proto/babel/packets.c
@@ -146,7 +146,8 @@ struct babel_write_state {
#define TLV_HDR(tlv,t,l) ({ tlv->type = t; tlv->length = l - sizeof(struct babel_tlv); })
#define TLV_HDR0(tlv,t) TLV_HDR(tlv, t, tlv_data[t].min_length)
-#define BYTES(n) ((((uint) n) + 7) / 8)
+#define NET_SIZE(n) BYTES(net_pxlen(n))
+
static inline u16
get_time16(const void *p)
@@ -161,19 +162,19 @@ put_time16(void *p, u16 v)
put_u16(p, v * BABEL_TIME_UNITS);
}
-static inline ip6_addr
-get_ip6_px(const void *p, uint plen)
+static inline void
+read_ip6_px(net_addr *n, const void *p, uint plen)
{
ip6_addr addr = IPA_NONE;
memcpy(&addr, p, BYTES(plen));
- return ip6_ntoh(addr);
+ net_fill_ip6(n, ip6_ntoh(addr), plen);
}
static inline void
-put_ip6_px(void *p, ip6_addr addr, uint plen)
+put_ip6_px(void *p, net_addr *n)
{
- addr = ip6_hton(addr);
- memcpy(p, &addr, BYTES(plen));
+ ip6_addr addr = ip6_hton(net6_prefix(n));
+ memcpy(p, &addr, NET_SIZE(n));
}
static inline ip6_addr
@@ -480,6 +481,9 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
if (tlv->plen > 0)
return PARSE_ERROR;
+ if (msg->metric != 65535)
+ return PARSE_ERROR;
+
msg->wildcard = 1;
break;
@@ -488,7 +492,7 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
return PARSE_IGNORE;
case BABEL_AE_IP6:
- if (tlv->plen > MAX_PREFIX_LENGTH)
+ if (tlv->plen > IP6_MAX_PREFIX_LENGTH)
return PARSE_ERROR;
/* Cannot omit data if there is no saved prefix */
@@ -499,18 +503,18 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
memcpy(buf, state->def_ip6_prefix, tlv->omitted);
memcpy(buf + tlv->omitted, tlv->addr, len);
- msg->plen = tlv->plen;
- msg->prefix = ipa_from_ip6(get_ip6(buf));
+ ip6_addr prefix = get_ip6(buf);
+ net_fill_ip6(&msg->net, prefix, tlv->plen);
if (tlv->flags & BABEL_FLAG_DEF_PREFIX)
{
- put_ip6(state->def_ip6_prefix, msg->prefix);
+ put_ip6(state->def_ip6_prefix, prefix);
state->def_ip6_prefix_seen = 1;
}
if (tlv->flags & BABEL_FLAG_ROUTER_ID)
{
- state->router_id = ((u64) _I2(msg->prefix)) << 32 | _I3(msg->prefix);
+ state->router_id = ((u64) _I2(prefix)) << 32 | _I3(prefix);
state->router_id_seen = 1;
}
break;
@@ -559,7 +563,7 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
tlv = (struct babel_tlv_update *) NEXT_TLV(tlv);
}
- uint len = sizeof(struct babel_tlv_update) + BYTES(msg->plen);
+ uint len = sizeof(struct babel_tlv_update) + NET_SIZE(&msg->net);
if (len0 + len > max_len)
return 0;
@@ -575,8 +579,8 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
else
{
tlv->ae = BABEL_AE_IP6;
- tlv->plen = msg->plen;
- put_ip6_px(tlv->addr, msg->prefix, msg->plen);
+ tlv->plen = net6_pxlen(&msg->net);
+ put_ip6_px(tlv->addr, &msg->net);
}
put_time16(&tlv->interval, msg->interval);
@@ -610,14 +614,13 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m,
return PARSE_IGNORE;
case BABEL_AE_IP6:
- if (tlv->plen > MAX_PREFIX_LENGTH)
+ if (tlv->plen > IP6_MAX_PREFIX_LENGTH)
return PARSE_ERROR;
if (TLV_OPT_LENGTH(tlv) < BYTES(tlv->plen))
return PARSE_ERROR;
- msg->plen = tlv->plen;
- msg->prefix = get_ip6_px(tlv->addr, tlv->plen);
+ read_ip6_px(&msg->net, tlv->addr, tlv->plen);
return PARSE_SUCCESS;
case BABEL_AE_IP6_LL:
@@ -637,7 +640,7 @@ babel_write_route_request(struct babel_tlv *hdr, union babel_msg *m,
struct babel_tlv_route_request *tlv = (void *) hdr;
struct babel_msg_route_request *msg = &m->route_request;
- uint len = sizeof(struct babel_tlv_route_request) + BYTES(msg->plen);
+ uint len = sizeof(struct babel_tlv_route_request) + NET_SIZE(&msg->net);
if (len > max_len)
return 0;
@@ -652,8 +655,8 @@ babel_write_route_request(struct babel_tlv *hdr, union babel_msg *m,
else
{
tlv->ae = BABEL_AE_IP6;
- tlv->plen = msg->plen;
- put_ip6_px(tlv->addr, msg->prefix, msg->plen);
+ tlv->plen = net6_pxlen(&msg->net);
+ put_ip6_px(tlv->addr, &msg->net);
}
return len;
@@ -685,14 +688,13 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
return PARSE_IGNORE;
case BABEL_AE_IP6:
- if (tlv->plen > MAX_PREFIX_LENGTH)
+ if (tlv->plen > IP6_MAX_PREFIX_LENGTH)
return PARSE_ERROR;
if (TLV_OPT_LENGTH(tlv) < BYTES(tlv->plen))
return PARSE_ERROR;
- msg->plen = tlv->plen;
- msg->prefix = get_ip6_px(tlv->addr, tlv->plen);
+ read_ip6_px(&msg->net, tlv->addr, tlv->plen);
return PARSE_SUCCESS;
case BABEL_AE_IP6_LL:
@@ -712,18 +714,18 @@ babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
struct babel_tlv_seqno_request *tlv = (void *) hdr;
struct babel_msg_seqno_request *msg = &m->seqno_request;
- uint len = sizeof(struct babel_tlv_seqno_request) + BYTES(msg->plen);
+ uint len = sizeof(struct babel_tlv_seqno_request) + NET_SIZE(&msg->net);
if (len > max_len)
return 0;
TLV_HDR(tlv, BABEL_TLV_SEQNO_REQUEST, len);
tlv->ae = BABEL_AE_IP6;
- tlv->plen = msg->plen;
+ tlv->plen = net6_pxlen(&msg->net);
put_u16(&tlv->seqno, msg->seqno);
tlv->hop_count = msg->hop_count;
put_u64(&tlv->router_id, msg->router_id);
- put_ip6_px(tlv->addr, msg->prefix, msg->plen);
+ put_ip6_px(tlv->addr, &msg->net);
return len;
}
diff --git a/proto/bfd/Makefile b/proto/bfd/Makefile
index c28cedec..402122fc 100644
--- a/proto/bfd/Makefile
+++ b/proto/bfd/Makefile
@@ -1,5 +1,6 @@
-source=bfd.c packets.c io.c
-root-rel=../../
-dir-name=proto/bfd
+src := bfd.c io.c packets.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
-include ../../Rules
+tests_objs := $(tests_objs) $(src-o-files) \ No newline at end of file
diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c
index 79135fae..e2c6050b 100644
--- a/proto/bfd/bfd.c
+++ b/proto/bfd/bfd.c
@@ -112,7 +112,7 @@
#define HASH_IP_KEY(n) n->addr
#define HASH_IP_NEXT(n) n->next_ip
#define HASH_IP_EQ(a,b) ipa_equal(a,b)
-#define HASH_IP_FN(k) ipa_hash32(k)
+#define HASH_IP_FN(k) ipa_hash(k)
static list bfd_proto_list;
static list bfd_wait_list;
@@ -954,7 +954,7 @@ bfd_init_all(void)
static struct proto *
bfd_init(struct proto_config *c)
{
- struct proto *p = proto_new(c, sizeof(struct bfd_proto));
+ struct proto *p = proto_new(c);
p->neigh_notify = bfd_neigh_notify;
@@ -983,8 +983,10 @@ bfd_start(struct proto *P)
add_tail(&bfd_proto_list, &p->bfd_node);
birdloop_enter(p->loop);
- p->rx_1 = bfd_open_rx_sk(p, 0);
- p->rx_m = bfd_open_rx_sk(p, 1);
+ p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4);
+ p->rx4_m = bfd_open_rx_sk(p, 1, SK_IPV4);
+ p->rx6_1 = bfd_open_rx_sk(p, 0, SK_IPV6);
+ p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6);
birdloop_leave(p->loop);
bfd_take_requests(p);
@@ -1118,6 +1120,7 @@ bfd_show_sessions(struct proto *P)
struct protocol proto_bfd = {
.name = "BFD",
.template = "bfd%d",
+ .proto_size = sizeof(struct bfd_proto),
.config_size = sizeof(struct bfd_config),
.init = bfd_init,
.start = bfd_start,
diff --git a/proto/bfd/bfd.h b/proto/bfd/bfd.h
index 46e09879..e2e75753 100644
--- a/proto/bfd/bfd.h
+++ b/proto/bfd/bfd.h
@@ -87,8 +87,10 @@ struct bfd_proto
sock *notify_ws;
list notify_list;
- sock *rx_1;
- sock *rx_m;
+ sock *rx4_1;
+ sock *rx6_1;
+ sock *rx4_m;
+ sock *rx6_m;
list iface_list;
};
@@ -201,7 +203,7 @@ void bfd_show_sessions(struct proto *P);
/* packets.c */
void bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final);
-sock * bfd_open_rx_sk(struct bfd_proto *p, int multihop);
+sock * bfd_open_rx_sk(struct bfd_proto *p, int multihop, int inet_version);
sock * bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa);
diff --git a/proto/bfd/io.h b/proto/bfd/io.h
index 641ee054..45836f84 100644
--- a/proto/bfd/io.h
+++ b/proto/bfd/io.h
@@ -12,7 +12,7 @@
#include "lib/resource.h"
#include "lib/event.h"
#include "lib/socket.h"
-// #include "lib/timer.h"
+// #include "sysdep/unix/timer.h"
typedef struct timer2
diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c
index 129db72f..b76efda6 100644
--- a/proto/bfd/packets.c
+++ b/proto/bfd/packets.c
@@ -248,7 +248,7 @@ bfd_check_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_
/* BFD CSNs are in 32-bit circular number space */
u32 csn = ntohl(auth->csn);
if (s->rx_csn_known &&
- (((csn - s->rx_csn) > (3 * s->detect_mult)) ||
+ (((csn - s->rx_csn) > (3 * (uint) s->detect_mult)) ||
(meticulous && (csn == s->rx_csn))))
{
/* We want to report both new and old CSN */
@@ -405,10 +405,11 @@ bfd_err_hook(sock *sk, int err)
}
sock *
-bfd_open_rx_sk(struct bfd_proto *p, int multihop)
+bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
{
sock *sk = sk_new(p->tpool);
sk->type = SK_UDP;
+ sk->subtype = af;
sk->sport = !multihop ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
sk->data = p;
@@ -421,10 +422,6 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop)
sk->priority = sk_priority_control;
sk->flags = SKF_THREAD | SKF_LADDR_RX | (!multihop ? SKF_TTL_RX : 0);
-#ifdef IPV6
- sk->flags |= SKF_V6ONLY;
-#endif
-
if (sk_open(sk) < 0)
goto err;
@@ -456,10 +453,6 @@ bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
sk->ttl = ifa ? 255 : -1;
sk->flags = SKF_THREAD | SKF_BIND | SKF_HIGH_PORT;
-#ifdef IPV6
- sk->flags |= SKF_V6ONLY;
-#endif
-
if (sk_open(sk) < 0)
goto err;
diff --git a/proto/bgp/Makefile b/proto/bgp/Makefile
index a634cf0d..00aaef5e 100644
--- a/proto/bgp/Makefile
+++ b/proto/bgp/Makefile
@@ -1,5 +1,6 @@
-source=bgp.c attrs.c packets.c
-root-rel=../../
-dir-name=proto/bgp
+src := attrs.c bgp.c packets.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
-include ../../Rules
+tests_objs := $(tests_objs) $(src-o-files) \ No newline at end of file
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index b9e2490d..882ba44e 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -2,6 +2,8 @@
* BIRD -- BGP Attributes
*
* (c) 2000 Martin Mares <mj@ucw.cz>
+ * (c) 2008--2016 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2008--2016 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@@ -39,890 +41,1249 @@
* specifies that such updates should be ignored, but that is generally
* a bad idea.
*
- * Error checking of optional transitive attributes is done according to
- * draft-ietf-idr-optional-transitive-03, but errors are handled always
- * as withdraws.
+ * BGP attribute table has several hooks:
*
- * Unexpected AS_CONFED_* segments in AS_PATH are logged and removed,
- * but unknown segments cause a session drop with Malformed AS_PATH
- * error (see validate_path()). The behavior in such case is not
- * explicitly specified by RFC 4271. RFC 5065 specifies that
- * inconsistent AS_CONFED_* segments should cause a session drop, but
- * implementations that pass invalid AS_CONFED_* segments are
- * widespread.
+ * export - Hook that validates and normalizes attribute during export phase.
+ * Receives eattr, may modify it (e.g., sort community lists for canonical
+ * representation), UNSET() it (e.g., skip empty lists), or WITHDRAW() it if
+ * necessary. May assume that eattr has value valid w.r.t. its type, but may be
+ * invalid w.r.t. BGP constraints. Optional.
*
- * Error handling of AS4_* attributes is done as specified by RFC 6793. There
- * are several possible inconsistencies between AGGREGATOR and AS4_AGGREGATOR
- * that are not handled by that RFC, these are logged and ignored (see
- * bgp_reconstruct_4b_attrs()).
+ * encode - Hook that converts internal representation to external one during
+ * packet writing. Receives eattr and puts it in the buffer (including attribute
+ * header). Returns number of bytes, or -1 if not enough space. May assume that
+ * eattr has value valid w.r.t. its type and validated by export hook. Mandatory
+ * for all known attributes that exist internally after export phase (i.e., all
+ * except pseudoattributes MP_(UN)REACH_NLRI).
+ *
+ * decode - Hook that converts external representation to internal one during
+ * packet parsing. Receives attribute data in buffer, validates it and adds
+ * attribute to ea_list. If data are invalid, steps DISCARD(), WITHDRAW() or
+ * bgp_parse_error() may be used to escape. Mandatory for all known attributes.
+ *
+ * format - Optional hook that converts eattr to textual representation.
*/
+// XXXX review pool usage : c->c.proto->pool
-static byte bgp_mandatory_attrs[] = { BA_ORIGIN, BA_AS_PATH
-#ifndef IPV6
-,BA_NEXT_HOP
-#endif
-};
-struct attr_desc {
- char *name;
- int expected_length;
- int expected_flags;
- int type;
- int allow_in_ebgp;
- int (*validate)(struct bgp_proto *p, byte *attr, int len);
- void (*format)(eattr *ea, byte *buf, int buflen);
+struct bgp_attr_desc {
+ const char *name;
+ uint type;
+ uint flags;
+ void (*export)(struct bgp_export_state *s, eattr *a);
+ int (*encode)(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
+ void (*decode)(struct bgp_parse_state *s, uint code, uint flags, byte *data, uint len, ea_list **to);
+ void (*format)(eattr *ea, byte *buf, uint size);
};
-#define IGNORE -1
-#define WITHDRAW -2
+static const struct bgp_attr_desc bgp_attr_table[];
+
+static inline int bgp_attr_known(uint code);
+
+eattr *
+bgp_set_attr(ea_list **attrs, struct linpool *pool, uint code, uint flags, uintptr_t val)
+{
+ ASSERT(bgp_attr_known(code));
+
+ ea_list *a = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
+ eattr *e = &a->attrs[0];
+
+ a->flags = EALF_SORTED;
+ a->count = 1;
+ a->next = *attrs;
+ *attrs = a;
+
+ e->id = EA_CODE(EAP_BGP, code);
+ e->type = bgp_attr_table[code].type;
+ e->flags = flags;
+
+ if (e->type & EAF_EMBEDDED)
+ e->u.data = (u32) val;
+ else
+ e->u.ptr = (struct adata *) val;
+
+ return e;
+}
+
+
+
+#define REPORT(msg, args...) \
+ ({ log(L_REMOTE "%s: " msg, s->proto->p.name, ## args); })
+
+#define DISCARD(msg, args...) \
+ ({ REPORT(msg, ## args); return; })
+
+#define WITHDRAW(msg, args...) \
+ ({ REPORT(msg, ## args); s->err_withdraw = 1; return; })
+
+#define UNSET(a) \
+ ({ a->type = EAF_TYPE_UNDEF; return; })
+
+#define NEW_BGP "Discarding %s attribute received from AS4-aware neighbor"
+#define BAD_EBGP "Discarding %s attribute received from EBGP neighbor"
+#define BAD_LENGTH "Malformed %s attribute - invalid length (%u)"
+#define BAD_VALUE "Malformed %s attribute - invalid value (%u)"
+#define NO_MANDATORY "Missing mandatory %s attribute"
+
+
+static inline int
+bgp_put_attr_hdr3(byte *buf, uint code, uint flags, uint len)
+{
+ *buf++ = flags;
+ *buf++ = code;
+ *buf++ = len;
+ return 3;
+}
+
+static inline int
+bgp_put_attr_hdr4(byte *buf, uint code, uint flags, uint len)
+{
+ *buf++ = flags | BAF_EXT_LEN;
+ *buf++ = code;
+ put_u16(buf, len);
+ return 4;
+}
+
+static inline int
+bgp_put_attr_hdr(byte *buf, uint code, uint flags, uint len)
+{
+ if (len < 256)
+ return bgp_put_attr_hdr3(buf, code, flags, len);
+ else
+ return bgp_put_attr_hdr4(buf, code, flags, len);
+}
static int
-bgp_check_origin(struct bgp_proto *p UNUSED, byte *a, int len UNUSED)
+bgp_encode_u8(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
{
- if (*a > 2)
- return 6;
- return 0;
+ if (size < (3+1))
+ return -1;
+
+ bgp_put_attr_hdr3(buf, EA_ID(a->id), a->flags, 1);
+ buf[3] = a->u.data;
+
+ return 3+1;
}
-static void
-bgp_format_origin(eattr *a, byte *buf, int buflen UNUSED)
+static int
+bgp_encode_u32(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
{
- static char *bgp_origin_names[] = { "IGP", "EGP", "Incomplete" };
+ if (size < (3+4))
+ return -1;
+
+ bgp_put_attr_hdr3(buf, EA_ID(a->id), a->flags, 4);
+ put_u32(buf+3, a->u.data);
- bsprintf(buf, bgp_origin_names[a->u.data]);
+ return 3+4;
}
static int
-path_segment_contains(byte *p, int bs, u32 asn)
+bgp_encode_u32s(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
{
- int i;
- int len = p[1];
- p += 2;
+ uint len = a->u.ptr->length;
- for(i=0; i<len; i++)
- {
- u32 asn2 = (bs == 4) ? get_u32(p) : get_u16(p);
- if (asn2 == asn)
- return 1;
- p += bs;
- }
+ if (size < (4+len))
+ return -1;
- return 0;
+ uint hdr = bgp_put_attr_hdr(buf, EA_ID(a->id), a->flags, len);
+ put_u32s(buf + hdr, (u32 *) a->u.ptr->data, len / 4);
+
+ return hdr + len;
}
-/* Validates path attribute, removes AS_CONFED_* segments, and also returns path length */
static int
-validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, uint *ilength)
+bgp_put_attr(byte *buf, uint size, uint code, uint flags, byte *data, uint len)
{
- int res = 0;
- u8 *a, *dst;
- int len, plen;
+ if (size < (4+len))
+ return -1;
- dst = a = idata;
- len = *ilength;
+ uint hdr = bgp_put_attr_hdr(buf, code, flags, len);
+ memcpy(buf + hdr, data, len);
- while (len)
- {
- if (len < 2)
- return -1;
-
- plen = 2 + bs * a[1];
- if (len < plen)
- return -1;
-
- if (a[1] == 0)
- {
- log(L_WARN "%s: %s_PATH attribute contains empty segment, skipping it",
- p->p.name, as_path ? "AS" : "AS4");
- goto skip;
- }
-
- switch (a[0])
- {
- case AS_PATH_SET:
- res++;
- break;
-
- case AS_PATH_SEQUENCE:
- res += a[1];
- break;
-
- case AS_PATH_CONFED_SEQUENCE:
- case AS_PATH_CONFED_SET:
- if (as_path && path_segment_contains(a, bs, p->remote_as))
- {
- log(L_WARN "%s: AS_CONFED_* segment with peer ASN found, misconfigured confederation?", p->p.name);
- return -1;
- }
-
- log(L_WARN "%s: %s_PATH attribute contains AS_CONFED_* segment, skipping segment",
- p->p.name, as_path ? "AS" : "AS4");
- goto skip;
-
- default:
- return -1;
- }
-
- if (dst != a)
- memmove(dst, a, plen);
- dst += plen;
-
- skip:
- len -= plen;
- a += plen;
- }
+ return hdr + len;
+}
- *ilength = dst - idata;
- return res;
+static int
+bgp_encode_raw(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
+{
+ return bgp_put_attr(buf, size, EA_ID(a->id), a->flags, a->u.ptr->data, a->u.ptr->length);
}
-static inline int
-validate_as_path(struct bgp_proto *p, byte *a, int *len)
+
+/*
+ * Attribute hooks
+ */
+
+static void
+bgp_export_origin(struct bgp_export_state *s, eattr *a)
{
- return validate_path(p, 1, p->as4_session ? 4 : 2, a, len);
+ if (a->u.data > 2)
+ WITHDRAW(BAD_VALUE, "ORIGIN", a->u.data);
}
-static inline int
-validate_as4_path(struct bgp_proto *p, struct adata *path)
+static void
+bgp_decode_origin(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
- return validate_path(p, 0, 4, path->data, &path->length);
+ if (len != 1)
+ WITHDRAW(BAD_LENGTH, "ORIGIN", len);
+
+ if (data[0] > 2)
+ WITHDRAW(BAD_VALUE, "ORIGIN", data[0]);
+
+ bgp_set_attr_u32(to, s->pool, BA_ORIGIN, flags, data[0]);
}
+static void
+bgp_format_origin(eattr *a, byte *buf, uint size UNUSED)
+{
+ static const char *bgp_origin_names[] = { "IGP", "EGP", "Incomplete" };
+
+ bsprintf(buf, (a->u.data <= 2) ? bgp_origin_names[a->u.data] : "?");
+}
+
+
static int
-bgp_check_next_hop(struct bgp_proto *p UNUSED, byte *a UNUSED6, int len UNUSED6)
+bgp_encode_as_path(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
{
-#ifdef IPV6
- return IGNORE;
-#else
- ip_addr addr;
+ byte *data = a->u.ptr->data;
+ uint len = a->u.ptr->length;
+
+ if (!s->as4_session)
+ {
+ /* Prepare 16-bit AS_PATH (from 32-bit one) in a temporary buffer */
+ byte *src = data;
+ data = alloca(len);
+ len = as_path_32to16(data, src, len);
+ }
+
+ return bgp_put_attr(buf, size, BA_AS_PATH, a->flags, data, len);
+}
- memcpy(&addr, a, len);
- ipa_ntoh(addr);
- if (ipa_classify(addr) & IADDR_HOST)
+static void
+bgp_decode_as_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
+{
+ struct bgp_proto *p = s->proto;
+ int as_length = s->as4_session ? 4 : 2;
+ int as_confed = p->cf->confederation && p->is_interior;
+ char err[128];
+
+ if (!as_path_valid(data, len, as_length, as_confed, err, sizeof(err)))
+ WITHDRAW("Malformed AS_PATH attribute - %s", err);
+
+ /* In some circumstances check for initial AS_CONFED_SEQUENCE; RFC 5065 5.0 */
+ if (p->is_interior && !p->is_internal &&
+ ((len < 2) || (data[0] != AS_PATH_CONFED_SEQUENCE)))
+ WITHDRAW("Malformed AS_PATH attribute - %s", "missing initial AS_CONFED_SEQUENCE");
+
+ if (!s->as4_session)
+ {
+ /* Prepare 32-bit AS_PATH (from 16-bit one) in a temporary buffer */
+ byte *src = data;
+ data = alloca(2*len);
+ len = as_path_16to32(data, src, len);
+ }
+
+ bgp_set_attr_data(to, s->pool, BA_AS_PATH, flags, data, len);
+}
+
+
+static int
+bgp_encode_next_hop(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
+{
+ /*
+ * The NEXT_HOP attribute is used only in traditional (IPv4) BGP. In MP-BGP,
+ * the next hop is encoded as a part of the MP_REACH_NLRI attribute, so we
+ * store it and encode it later by AFI-specific hooks.
+ */
+
+ if (s->channel->afi == BGP_AF_IPV4)
+ {
+ ASSERT(a->u.ptr->length == sizeof(ip_addr));
+
+ if (size < (3+4))
+ return -1;
+
+ bgp_put_attr_hdr3(buf, BA_NEXT_HOP, a->flags, 4);
+ put_ip4(buf+3, ipa_to_ip4( *(ip_addr *) a->u.ptr->data ));
+
+ return 3+4;
+ }
+ else
+ {
+ s->mp_next_hop = a;
return 0;
+ }
+}
+
+static void
+bgp_decode_next_hop(struct bgp_parse_state *s, uint code UNUSED, uint flags UNUSED, byte *data, uint len, ea_list **to UNUSED)
+{
+ if (len != 4)
+ WITHDRAW(BAD_LENGTH, "NEXT_HOP", len);
+
+ /* Semantic checks are done later */
+ s->ip_next_hop_len = len;
+ s->ip_next_hop_data = data;
+}
+
+/* TODO: This function should use AF-specific hook */
+static void
+bgp_format_next_hop(eattr *a, byte *buf, uint size UNUSED)
+{
+ ip_addr *nh = (void *) a->u.ptr->data;
+ uint len = a->u.ptr->length;
+
+ ASSERT((len == 16) || (len == 32));
+
+ /* in IPv6, we may have two addresses in NEXT HOP */
+ if ((len == 16) || ipa_zero(nh[1]))
+ bsprintf(buf, "%I", nh[0]);
else
- return 8;
-#endif
+ bsprintf(buf, "%I %I", nh[0], nh[1]);
}
+
static void
-bgp_format_next_hop(eattr *a, byte *buf, int buflen UNUSED)
+bgp_decode_med(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
- ip_addr *ipp = (ip_addr *) a->u.ptr->data;
-#ifdef IPV6
- /* in IPv6, we might have two addresses in NEXT HOP */
- if ((a->u.ptr->length == NEXT_HOP_LENGTH) && ipa_nonzero(ipp[1]))
- {
- bsprintf(buf, "%I %I", ipp[0], ipp[1]);
- return;
- }
-#endif
+ if (len != 4)
+ WITHDRAW(BAD_LENGTH, "MULTI_EXIT_DISC", len);
- bsprintf(buf, "%I", ipp[0]);
+ u32 val = get_u32(data);
+ bgp_set_attr_u32(to, s->pool, BA_MULTI_EXIT_DISC, flags, val);
}
-static int
-bgp_check_aggregator(struct bgp_proto *p, byte *a UNUSED, int len)
+
+static void
+bgp_export_local_pref(struct bgp_export_state *s, eattr *a)
{
- int exp_len = p->as4_session ? 8 : 6;
-
- return (len == exp_len) ? 0 : WITHDRAW;
+ if (!s->proto->is_interior && !s->proto->cf->allow_local_pref)
+ UNSET(a);
}
static void
-bgp_format_aggregator(eattr *a, byte *buf, int buflen UNUSED)
+bgp_decode_local_pref(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
- struct adata *ad = a->u.ptr;
- byte *data = ad->data;
- u32 as;
+ if (!s->proto->is_interior && !s->proto->cf->allow_local_pref)
+ DISCARD(BAD_EBGP, "LOCAL_PREF");
- as = get_u32(data);
- data += 4;
+ if (len != 4)
+ WITHDRAW(BAD_LENGTH, "LOCAL_PREF", len);
- bsprintf(buf, "%d.%d.%d.%d AS%u", data[0], data[1], data[2], data[3], as);
+ u32 val = get_u32(data);
+ bgp_set_attr_u32(to, s->pool, BA_LOCAL_PREF, flags, val);
}
-static int
-bgp_check_community(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
+
+static void
+bgp_decode_atomic_aggr(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data UNUSED, uint len, ea_list **to)
{
- return ((len % 4) == 0) ? 0 : WITHDRAW;
+ if (len != 0)
+ DISCARD(BAD_LENGTH, "ATOMIC_AGGR", len);
+
+ bgp_set_attr_data(to, s->pool, BA_ATOMIC_AGGR, flags, NULL, 0);
}
static int
-bgp_check_cluster_list(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
+bgp_encode_aggregator(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
{
- return ((len % 4) == 0) ? 0 : 5;
+ byte *data = a->u.ptr->data;
+ uint len = a->u.ptr->length;
+
+ if (!s->as4_session)
+ {
+ /* Prepare 16-bit AGGREGATOR (from 32-bit one) in a temporary buffer */
+ byte *src = data;
+ data = alloca(6);
+ len = aggregator_32to16(data, src);
+ }
+
+ return bgp_put_attr(buf, size, BA_AGGREGATOR, a->flags, data, len);
}
static void
-bgp_format_cluster_list(eattr *a, byte *buf, int buflen)
+bgp_decode_aggregator(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
- /* Truncates cluster lists larger than buflen, probably not a problem */
- int_set_format(a->u.ptr, 0, -1, buf, buflen);
+ if (len != (s->as4_session ? 8 : 6))
+ DISCARD(BAD_LENGTH, "AGGREGATOR", len);
+
+ if (!s->as4_session)
+ {
+ /* Prepare 32-bit AGGREGATOR (from 16-bit one) in a temporary buffer */
+ byte *src = data;
+ data = alloca(8);
+ len = aggregator_16to32(data, src);
+ }
+
+ bgp_set_attr_data(to, s->pool, BA_AGGREGATOR, flags, data, len);
}
-static int
-bgp_check_reach_nlri(struct bgp_proto *p UNUSED, byte *a UNUSED, int len UNUSED)
+static void
+bgp_format_aggregator(eattr *a, byte *buf, uint size UNUSED)
{
-#ifdef IPV6
- p->mp_reach_start = a;
- p->mp_reach_len = len;
-#endif
- return IGNORE;
+ byte *data = a->u.ptr->data;
+
+ bsprintf(buf, "%I4 AS%u", get_ip4(data+4), get_u32(data+0));
}
-static int
-bgp_check_unreach_nlri(struct bgp_proto *p UNUSED, byte *a UNUSED, int len UNUSED)
+
+static void
+bgp_export_community(struct bgp_export_state *s, eattr *a)
{
-#ifdef IPV6
- p->mp_unreach_start = a;
- p->mp_unreach_len = len;
-#endif
- return IGNORE;
+ if (a->u.ptr->length == 0)
+ UNSET(a);
+
+ a->u.ptr = int_set_sort(s->pool, a->u.ptr);
}
-static int
-bgp_check_ext_community(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
+static void
+bgp_decode_community(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
- return ((len % 8) == 0) ? 0 : WITHDRAW;
+ if (!len || (len % 4))
+ WITHDRAW(BAD_LENGTH, "COMMUNITY", len);
+
+ struct adata *ad = lp_alloc_adata(s->pool, len);
+ get_u32s(data, (u32 *) ad->data, len / 4);
+ bgp_set_attr_ptr(to, s->pool, BA_COMMUNITY, flags, ad);
}
-static int
-bgp_check_large_community(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
-{
- return ((len % 12) == 0) ? 0 : WITHDRAW;
-}
-
-
-static struct attr_desc bgp_attr_table[] = {
- { NULL, -1, 0, 0, 0, /* Undefined */
- NULL, NULL },
- { "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, 1, /* BA_ORIGIN */
- bgp_check_origin, bgp_format_origin },
- { "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, 1, /* BA_AS_PATH */
- NULL, NULL }, /* is checked by validate_as_path() as a special case */
- { "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, 1, /* BA_NEXT_HOP */
- bgp_check_next_hop, bgp_format_next_hop },
- { "med", 4, BAF_OPTIONAL, EAF_TYPE_INT, 1, /* BA_MULTI_EXIT_DISC */
- NULL, NULL },
- { "local_pref", 4, BAF_TRANSITIVE, EAF_TYPE_INT, 1, /* BA_LOCAL_PREF */
- NULL, NULL },
- { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_ATOMIC_AGGR */
- NULL, NULL },
- { "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AGGREGATOR */
- bgp_check_aggregator, bgp_format_aggregator },
- { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */
- bgp_check_community, NULL },
- { "originator_id", 4, BAF_OPTIONAL, EAF_TYPE_ROUTER_ID, 0, /* BA_ORIGINATOR_ID */
- NULL, NULL },
- { "cluster_list", -1, BAF_OPTIONAL, EAF_TYPE_INT_SET, 0, /* BA_CLUSTER_LIST */
- bgp_check_cluster_list, bgp_format_cluster_list },
- { .name = NULL }, /* BA_DPA */
- { .name = NULL }, /* BA_ADVERTISER */
- { .name = NULL }, /* BA_RCID_PATH */
- { "mp_reach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_REACH_NLRI */
- bgp_check_reach_nlri, NULL },
- { "mp_unreach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_UNREACH_NLRI */
- bgp_check_unreach_nlri, NULL },
- { "ext_community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_EC_SET, 1, /* BA_EXT_COMMUNITY */
- bgp_check_ext_community, NULL },
- { "as4_path", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */
- NULL, NULL },
- { "as4_aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */
- NULL, NULL },
- [BA_LARGE_COMMUNITY] =
- { "large_community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_LC_SET, 1,
- bgp_check_large_community, NULL }
-};
-/* BA_AS4_PATH is type EAF_TYPE_OPAQUE and not type EAF_TYPE_AS_PATH.
- * It does not matter as this attribute does not appear on routes in the routing table.
- */
+static void
+bgp_export_originator_id(struct bgp_export_state *s, eattr *a)
+{
+ if (!s->proto->is_internal)
+ UNSET(a);
+}
+
+static void
+bgp_decode_originator_id(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
+{
+ if (!s->proto->is_internal)
+ DISCARD(BAD_EBGP, "ORIGINATOR_ID");
-#define ATTR_KNOWN(code) ((code) < ARRAY_SIZE(bgp_attr_table) && bgp_attr_table[code].name)
+ if (len != 4)
+ WITHDRAW(BAD_LENGTH, "ORIGINATOR_ID", len);
-static inline struct adata *
-bgp_alloc_adata(struct linpool *pool, unsigned len)
+ u32 val = get_u32(data);
+ bgp_set_attr_u32(to, s->pool, BA_ORIGINATOR_ID, flags, val);
+}
+
+
+static void
+bgp_export_cluster_list(struct bgp_export_state *s UNUSED, eattr *a)
{
- struct adata *ad = lp_alloc(pool, sizeof(struct adata) + len);
- ad->length = len;
- return ad;
+ if (!s->proto->is_internal)
+ UNSET(a);
+
+ if (a->u.ptr->length == 0)
+ UNSET(a);
}
static void
-bgp_set_attr(eattr *e, unsigned attr, uintptr_t val)
+bgp_decode_cluster_list(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
- ASSERT(ATTR_KNOWN(attr));
- e->id = EA_CODE(EAP_BGP, attr);
- e->type = bgp_attr_table[attr].type;
- e->flags = bgp_attr_table[attr].expected_flags;
- if (e->type & EAF_EMBEDDED)
- e->u.data = val;
- else
- e->u.ptr = (struct adata *) val;
+ if (!s->proto->is_internal)
+ DISCARD(BAD_EBGP, "CLUSTER_LIST");
+
+ if (!len || (len % 4))
+ WITHDRAW(BAD_LENGTH, "CLUSTER_LIST", len);
+
+ struct adata *ad = lp_alloc_adata(s->pool, len);
+ get_u32s(data, (u32 *) ad->data, len / 4);
+ bgp_set_attr_ptr(to, s->pool, BA_CLUSTER_LIST, flags, ad);
}
-static byte *
-bgp_set_attr_wa(eattr *e, struct linpool *pool, unsigned attr, unsigned len)
+static void
+bgp_format_cluster_list(eattr *a, byte *buf, uint size)
{
- struct adata *ad = bgp_alloc_adata(pool, len);
- bgp_set_attr(e, attr, (uintptr_t) ad);
- return ad->data;
+ /* Truncates cluster lists larger than buflen, probably not a problem */
+ int_set_format(a->u.ptr, 0, -1, buf, size);
}
-void
-bgp_attach_attr(ea_list **to, struct linpool *pool, unsigned attr, uintptr_t val)
+
+static inline u32
+get_af3(byte *buf)
{
- ea_list *a = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
- a->next = *to;
- *to = a;
- a->flags = EALF_SORTED;
- a->count = 1;
- bgp_set_attr(a->attrs, attr, val);
+ return (get_u16(buf) << 16) | buf[2];
}
-byte *
-bgp_attach_attr_wa(ea_list **to, struct linpool *pool, unsigned attr, unsigned len)
+static void
+bgp_decode_mp_reach_nlri(struct bgp_parse_state *s, uint code UNUSED, uint flags UNUSED, byte *data, uint len, ea_list **to UNUSED)
{
- struct adata *ad = bgp_alloc_adata(pool, len);
- bgp_attach_attr(to, pool, attr, (uintptr_t) ad);
- return ad->data;
+ /*
+ * 2 B MP_REACH_NLRI data - Address Family Identifier
+ * 1 B MP_REACH_NLRI data - Subsequent Address Family Identifier
+ * 1 B MP_REACH_NLRI data - Length of Next Hop Network Address
+ * var MP_REACH_NLRI data - Network Address of Next Hop
+ * 1 B MP_REACH_NLRI data - Reserved (zero)
+ * var MP_REACH_NLRI data - Network Layer Reachability Information
+ */
+
+ if ((len < 5) || (len < (5 + (uint) data[3])))
+ bgp_parse_error(s, 9);
+
+ s->mp_reach_af = get_af3(data);
+ s->mp_next_hop_len = data[3];
+ s->mp_next_hop_data = data + 4;
+ s->mp_reach_len = len - 5 - s->mp_next_hop_len;
+ s->mp_reach_nlri = data + 5 + s->mp_next_hop_len;
}
-static int
-bgp_encode_attr_hdr(byte *dst, uint flags, unsigned code, int len)
+
+static void
+bgp_decode_mp_unreach_nlri(struct bgp_parse_state *s, uint code UNUSED, uint flags UNUSED, byte *data, uint len, ea_list **to UNUSED)
{
- int wlen;
+ /*
+ * 2 B MP_UNREACH_NLRI data - Address Family Identifier
+ * 1 B MP_UNREACH_NLRI data - Subsequent Address Family Identifier
+ * var MP_UNREACH_NLRI data - Network Layer Reachability Information
+ */
- DBG("\tAttribute %02x (%d bytes, flags %02x)\n", code, len, flags);
+ if (len < 3)
+ bgp_parse_error(s, 9);
+
+ s->mp_unreach_af = get_af3(data);
+ s->mp_unreach_len = len - 3;
+ s->mp_unreach_nlri = data + 3;
+}
- if (len < 256)
- {
- *dst++ = flags;
- *dst++ = code;
- *dst++ = len;
- wlen = 3;
- }
- else
- {
- *dst++ = flags | BAF_EXT_LEN;
- *dst++ = code;
- put_u16(dst, len);
- wlen = 4;
- }
- return wlen;
+static void
+bgp_export_ext_community(struct bgp_export_state *s, eattr *a)
+{
+ if (a->u.ptr->length == 0)
+ UNSET(a);
+
+ a->u.ptr = ec_set_sort(s->pool, a->u.ptr);
}
static void
-aggregator_convert_to_old(struct adata *aggr, byte *dst, int *new_used)
+bgp_decode_ext_community(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
- byte *src = aggr->data;
- *new_used = 0;
+ if (!len || (len % 8))
+ WITHDRAW(BAD_LENGTH, "EXT_COMMUNITY", len);
- u32 as = get_u32(src);
- if (as > 0xFFFF)
- {
- as = AS_TRANS;
- *new_used = 1;
- }
- put_u16(dst, as);
+ struct adata *ad = lp_alloc_adata(s->pool, len);
+ get_u32s(data, (u32 *) ad->data, len / 4);
+ bgp_set_attr_ptr(to, s->pool, BA_EXT_COMMUNITY, flags, ad);
+}
- /* Copy IPv4 address */
- memcpy(dst + 2, src + 4, 4);
+
+static void
+bgp_decode_as4_aggregator(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
+{
+ if (s->as4_session)
+ DISCARD(NEW_BGP, "AS4_AGGREGATOR");
+
+ if (len != 8)
+ DISCARD(BAD_LENGTH, "AS4_AGGREGATOR", len);
+
+ bgp_set_attr_data(to, s->pool, BA_AS4_AGGREGATOR, flags, data, len);
}
static void
-aggregator_convert_to_new(struct adata *aggr, byte *dst)
+bgp_decode_as4_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
- byte *src = aggr->data;
+ char err[128];
- u32 as = get_u16(src);
- put_u32(dst, as);
+ if (s->as4_session)
+ DISCARD(NEW_BGP, "AS4_PATH");
- /* Copy IPv4 address */
- memcpy(dst + 4, src + 2, 4);
+ if (len < 6)
+ DISCARD(BAD_LENGTH, "AS4_PATH", len);
+
+ if (!as_path_valid(data, len, 4, 1, err, sizeof(err)))
+ DISCARD("Malformed AS4_PATH attribute - %s", err);
+
+ struct adata *a = lp_alloc_adata(s->pool, len);
+ memcpy(a->data, data, len);
+
+ /* AS_CONFED* segments are invalid in AS4_PATH; RFC 6793 6 */
+ if (as_path_contains_confed(a))
+ {
+ REPORT("Discarding AS_CONFED* segment from AS4_PATH attribute");
+ a = as_path_strip_confed(s->pool, a);
+ }
+
+ bgp_set_attr_ptr(to, s->pool, BA_AS4_PATH, flags, a);
+}
+
+static void
+bgp_export_large_community(struct bgp_export_state *s, eattr *a)
+{
+ if (a->u.ptr->length == 0)
+ UNSET(a);
+
+ a->u.ptr = lc_set_sort(s->pool, a->u.ptr);
+}
+
+static void
+bgp_decode_large_community(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
+{
+ if (!len || (len % 12))
+ WITHDRAW(BAD_LENGTH, "LARGE_COMMUNITY", len);
+
+ struct adata *ad = lp_alloc_adata(s->pool, len);
+ get_u32s(data, (u32 *) ad->data, len / 4);
+ bgp_set_attr_ptr(to, s->pool, BA_LARGE_COMMUNITY, flags, ad);
+}
+
+static void
+bgp_export_mpls_label_stack(struct bgp_export_state *s, eattr *a)
+{
+ net_addr *n = s->route->net->n.addr;
+ u32 *labels = (u32 *) a->u.ptr->data;
+ uint lnum = a->u.ptr->length / 4;
+
+ /* Perhaps we should just ignore it? */
+ if (!s->mpls)
+ WITHDRAW("Unexpected MPLS stack");
+
+ /* Empty MPLS stack is not allowed */
+ if (!lnum)
+ WITHDRAW("Malformed MPLS stack - empty");
+
+ /* This is ugly, but we must ensure that labels fit into NLRI field */
+ if ((24*lnum + (net_is_vpn(n) ? 64 : 0) + net_pxlen(n)) > 255)
+ WITHDRAW("Malformed MPLS stack - too many labels (%u)", lnum);
+
+ for (uint i = 0; i < lnum; i++)
+ {
+ if (labels[i] > 0xfffff)
+ WITHDRAW("Malformed MPLS stack - invalid label (%u)", labels[i]);
+
+ /* TODO: Check for special-purpose label values? */
+ }
}
static int
-bgp_get_attr_len(eattr *a)
+bgp_encode_mpls_label_stack(struct bgp_write_state *s, eattr *a, byte *buf UNUSED, uint size UNUSED)
{
- int len;
- if (ATTR_KNOWN(EA_ID(a->id)))
+ /*
+ * MPLS labels are encoded as a part of the NLRI in MP_REACH_NLRI attribute,
+ * so we store MPLS_LABEL_STACK and encode it later by AFI-specific hooks.
+ */
+
+ s->mpls_labels = a->u.ptr;
+ return 0;
+}
+
+static void
+bgp_decode_mpls_label_stack(struct bgp_parse_state *s, uint code UNUSED, uint flags UNUSED, byte *data UNUSED, uint len UNUSED, ea_list **to UNUSED)
+{
+ DISCARD("Discarding received attribute #0");
+}
+
+static void
+bgp_format_mpls_label_stack(eattr *a, byte *buf, uint size)
+{
+ u32 *labels = (u32 *) a->u.ptr->data;
+ uint lnum = a->u.ptr->length / 4;
+ char *pos = buf;
+
+ for (uint i = 0; i < lnum; i++)
+ {
+ if (size < 20)
{
- int code = EA_ID(a->id);
- struct attr_desc *desc = &bgp_attr_table[code];
- len = desc->expected_length;
- if (len < 0)
- {
- ASSERT(!(a->type & EAF_EMBEDDED));
- len = a->u.ptr->length;
- }
+ bsprintf(pos, "...");
+ return;
}
+
+ uint l = bsprintf(pos, "%d/", labels[i]);
+ ADVANCE(pos, size, l);
+ }
+
+ /* Clear last slash or terminate empty string */
+ pos[lnum ? -1 : 0] = 0;
+}
+
+static inline void
+bgp_decode_unknown(struct bgp_parse_state *s, uint code, uint flags, byte *data, uint len, ea_list **to)
+{
+ bgp_set_attr_data(to, s->pool, code, flags, data, len);
+}
+
+
+/*
+ * Attribute table
+ */
+
+static const struct bgp_attr_desc bgp_attr_table[] = {
+ [BA_ORIGIN] = {
+ .name = "origin",
+ .type = EAF_TYPE_INT,
+ .flags = BAF_TRANSITIVE,
+ .export = bgp_export_origin,
+ .encode = bgp_encode_u8,
+ .decode = bgp_decode_origin,
+ .format = bgp_format_origin,
+ },
+ [BA_AS_PATH] = {
+ .name = "as_path",
+ .type = EAF_TYPE_AS_PATH,
+ .flags = BAF_TRANSITIVE,
+ .encode = bgp_encode_as_path,
+ .decode = bgp_decode_as_path,
+ },
+ [BA_NEXT_HOP] = {
+ .name = "next_hop",
+ .type = EAF_TYPE_IP_ADDRESS,
+ .flags = BAF_TRANSITIVE,
+ .encode = bgp_encode_next_hop,
+ .decode = bgp_decode_next_hop,
+ .format = bgp_format_next_hop,
+ },
+ [BA_MULTI_EXIT_DISC] = {
+ .name = "med",
+ .type = EAF_TYPE_INT,
+ .flags = BAF_OPTIONAL,
+ .encode = bgp_encode_u32,
+ .decode = bgp_decode_med,
+ },
+ [BA_LOCAL_PREF] = {
+ .name = "local_pref",
+ .type = EAF_TYPE_INT,
+ .flags = BAF_TRANSITIVE,
+ .export = bgp_export_local_pref,
+ .encode = bgp_encode_u32,
+ .decode = bgp_decode_local_pref,
+ },
+ [BA_ATOMIC_AGGR] = {
+ .name = "atomic_aggr",
+ .type = EAF_TYPE_OPAQUE,
+ .flags = BAF_TRANSITIVE,
+ .encode = bgp_encode_raw,
+ .decode = bgp_decode_atomic_aggr,
+ },
+ [BA_AGGREGATOR] = {
+ .name = "aggregator",
+ .type = EAF_TYPE_OPAQUE,
+ .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
+ .encode = bgp_encode_aggregator,
+ .decode = bgp_decode_aggregator,
+ .format = bgp_format_aggregator,
+ },
+ [BA_COMMUNITY] = {
+ .name = "community",
+ .type = EAF_TYPE_INT_SET,
+ .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
+ .export = bgp_export_community,
+ .encode = bgp_encode_u32s,
+ .decode = bgp_decode_community,
+ },
+ [BA_ORIGINATOR_ID] = {
+ .name = "originator_id",
+ .type = EAF_TYPE_ROUTER_ID,
+ .flags = BAF_OPTIONAL,
+ .export = bgp_export_originator_id,
+ .encode = bgp_encode_u32,
+ .decode = bgp_decode_originator_id,
+ },
+ [BA_CLUSTER_LIST] = {
+ .name = "cluster_list",
+ .type = EAF_TYPE_INT_SET,
+ .flags = BAF_OPTIONAL,
+ .export = bgp_export_cluster_list,
+ .encode = bgp_encode_u32s,
+ .decode = bgp_decode_cluster_list,
+ .format = bgp_format_cluster_list,
+ },
+ [BA_MP_REACH_NLRI] = {
+ .name = "mp_reach_nlri",
+ .type = EAF_TYPE_OPAQUE,
+ .flags = BAF_OPTIONAL,
+ .decode = bgp_decode_mp_reach_nlri,
+ },
+ [BA_MP_UNREACH_NLRI] = {
+ .name = "mp_unreach_nlri",
+ .type = EAF_TYPE_OPAQUE,
+ .flags = BAF_OPTIONAL,
+ .decode = bgp_decode_mp_unreach_nlri,
+ },
+ [BA_EXT_COMMUNITY] = {
+ .name = "ext_community",
+ .type = EAF_TYPE_EC_SET,
+ .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
+ .export = bgp_export_ext_community,
+ .encode = bgp_encode_u32s,
+ .decode = bgp_decode_ext_community,
+ },
+ [BA_AS4_PATH] = {
+ .name = "as4_path",
+ .type = EAF_TYPE_AS_PATH,
+ .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
+ .encode = bgp_encode_raw,
+ .decode = bgp_decode_as4_path,
+ },
+ [BA_AS4_AGGREGATOR] = {
+ .name = "as4_aggregator",
+ .type = EAF_TYPE_OPAQUE,
+ .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
+ .encode = bgp_encode_raw,
+ .decode = bgp_decode_as4_aggregator,
+ .format = bgp_format_aggregator,
+ },
+ [BA_LARGE_COMMUNITY] = {
+ .name = "large_community",
+ .type = EAF_TYPE_LC_SET,
+ .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
+ .export = bgp_export_large_community,
+ .encode = bgp_encode_u32s,
+ .decode = bgp_decode_large_community,
+ },
+ [BA_MPLS_LABEL_STACK] = {
+ .name = "mpls_label_stack",
+ .type = EAF_TYPE_INT_SET,
+ .export = bgp_export_mpls_label_stack,
+ .encode = bgp_encode_mpls_label_stack,
+ .decode = bgp_decode_mpls_label_stack,
+ .format = bgp_format_mpls_label_stack,
+ },
+};
+
+static inline int
+bgp_attr_known(uint code)
+{
+ return (code < ARRAY_SIZE(bgp_attr_table)) && bgp_attr_table[code].name;
+}
+
+
+/*
+ * Attribute export
+ */
+
+static inline void
+bgp_export_attr(struct bgp_export_state *s, eattr *a, ea_list *to)
+{
+ if (EA_PROTO(a->id) != EAP_BGP)
+ return;
+
+ uint code = EA_ID(a->id);
+
+ if (bgp_attr_known(code))
+ {
+ const struct bgp_attr_desc *desc = &bgp_attr_table[code];
+
+ /* The flags might have been zero if the attr was added by filters */
+ a->flags = (a->flags & BAF_PARTIAL) | desc->flags;
+
+ /* Set partial bit if new opt-trans attribute is attached to non-local route */
+ if ((s->src != NULL) && (a->type & EAF_ORIGINATED) &&
+ (a->flags & BAF_OPTIONAL) && (a->flags & BAF_TRANSITIVE))
+ a->flags |= BAF_PARTIAL;
+
+ /* Call specific hook */
+ CALL(desc->export, s, a);
+
+ /* Attribute might become undefined in hook */
+ if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
+ return;
+ }
else
- {
- ASSERT((a->type & EAF_TYPE_MASK) == EAF_TYPE_OPAQUE);
- len = a->u.ptr->length;
- }
-
- return len;
+ {
+ /* Don't re-export unknown non-transitive attributes */
+ if (!(a->flags & BAF_TRANSITIVE))
+ return;
+
+ a->flags |= BAF_PARTIAL;
+ }
+
+ /* Append updated attribute */
+ to->attrs[to->count++] = *a;
}
-#define ADVANCE(w, r, l) do { r -= l; w += l; } while (0)
+/**
+ * bgp_export_attrs - export BGP attributes
+ * @s: BGP export state
+ * @attrs: a list of extended attributes
+ *
+ * The bgp_export_attrs() function takes a list of attributes and merges it to
+ * one newly allocated and sorted segment. Attributes are validated and
+ * normalized by type-specific export hooks and attribute flags are updated.
+ * Some attributes may be eliminated (e.g. unknown non-tranitive attributes, or
+ * empty community sets).
+ *
+ * Result: one sorted attribute list segment, or NULL if attributes are unsuitable.
+ */
+static inline ea_list *
+bgp_export_attrs(struct bgp_export_state *s, ea_list *attrs)
+{
+ /* Merge the attribute list */
+ ea_list *new = lp_alloc(s->pool, ea_scan(attrs));
+ ea_merge(attrs, new);
+ ea_sort(new);
+
+ uint i, count;
+ count = new->count;
+ new->count = 0;
+
+ /* Export each attribute */
+ for (i = 0; i < count; i++)
+ bgp_export_attr(s, &new->attrs[i], new);
+
+ if (s->err_withdraw)
+ return NULL;
+
+ return new;
+}
+
+
+/*
+ * Attribute encoding
+ */
+
+static inline int
+bgp_encode_attr(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
+{
+ ASSERT(EA_PROTO(a->id) == EAP_BGP);
+
+ uint code = EA_ID(a->id);
+
+ if (bgp_attr_known(code))
+ return bgp_attr_table[code].encode(s, a, buf, size);
+ else
+ return bgp_encode_raw(s, a, buf, size);
+}
/**
* bgp_encode_attrs - encode BGP attributes
- * @p: BGP instance
- * @w: buffer
+ * @s: BGP write state
* @attrs: a list of extended attributes
- * @remains: remaining space in the buffer
+ * @buf: buffer
+ * @end: buffer end
*
* The bgp_encode_attrs() function takes a list of extended attributes
* and converts it to its BGP representation (a part of an Update message).
*
* Result: Length of the attribute block generated or -1 if not enough space.
*/
-uint
-bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
+int
+bgp_encode_attrs(struct bgp_write_state *s, ea_list *attrs, byte *buf, byte *end)
{
- uint i, code, type, flags;
- byte *start = w;
- int len, rv;
+ byte *pos = buf;
+ int i, len;
- for(i=0; i<attrs->count; i++)
- {
- eattr *a = &attrs->attrs[i];
- ASSERT(EA_PROTO(a->id) == EAP_BGP);
- code = EA_ID(a->id);
-
-#ifdef IPV6
- /* When talking multiprotocol BGP, the NEXT_HOP attributes are used only temporarily. */
- if (code == BA_NEXT_HOP)
- continue;
-#endif
-
- /* When AS4-aware BGP speaker is talking to non-AS4-aware BGP speaker,
- * we have to convert our 4B AS_PATH to 2B AS_PATH and send our AS_PATH
- * as optional AS4_PATH attribute.
- */
- if ((code == BA_AS_PATH) && (! p->as4_session))
- {
- len = a->u.ptr->length;
-
- if (remains < (len + 4))
- goto err_no_buffer;
-
- /* Using temporary buffer because don't know a length of created attr
- * and therefore a length of a header. Perhaps i should better always
- * use BAF_EXT_LEN. */
-
- byte buf[len];
- int new_used;
- int nl = as_path_convert_to_old(a->u.ptr, buf, &new_used);
-
- DBG("BGP: Encoding old AS_PATH\n");
- rv = bgp_encode_attr_hdr(w, BAF_TRANSITIVE, BA_AS_PATH, nl);
- ADVANCE(w, remains, rv);
- memcpy(w, buf, nl);
- ADVANCE(w, remains, nl);
-
- if (! new_used)
- continue;
-
- if (remains < (len + 4))
- goto err_no_buffer;
-
- /* We should discard AS_CONFED_SEQUENCE or AS_CONFED_SET path segments
- * here but we don't support confederations and such paths we already
- * discarded in bgp_check_as_path().
- */
-
- DBG("BGP: Encoding AS4_PATH\n");
- rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AS4_PATH, len);
- ADVANCE(w, remains, rv);
- memcpy(w, a->u.ptr->data, len);
- ADVANCE(w, remains, len);
-
- continue;
- }
-
- /* The same issue with AGGREGATOR attribute */
- if ((code == BA_AGGREGATOR) && (! p->as4_session))
- {
- int new_used;
-
- len = 6;
- if (remains < (len + 3))
- goto err_no_buffer;
-
- rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AGGREGATOR, len);
- ADVANCE(w, remains, rv);
- aggregator_convert_to_old(a->u.ptr, w, &new_used);
- ADVANCE(w, remains, len);
-
- if (! new_used)
- continue;
-
- len = 8;
- if (remains < (len + 3))
- goto err_no_buffer;
-
- rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AS4_AGGREGATOR, len);
- ADVANCE(w, remains, rv);
- memcpy(w, a->u.ptr->data, len);
- ADVANCE(w, remains, len);
-
- continue;
- }
-
- /* Standard path continues here ... */
-
- type = a->type & EAF_TYPE_MASK;
- flags = a->flags & (BAF_OPTIONAL | BAF_TRANSITIVE | BAF_PARTIAL);
- len = bgp_get_attr_len(a);
-
- /* Skip empty sets */
- if (((type == EAF_TYPE_INT_SET) || (type == EAF_TYPE_EC_SET) || (type == EAF_TYPE_LC_SET)) && (len == 0))
- continue;
-
- if (remains < len + 4)
- goto err_no_buffer;
-
- rv = bgp_encode_attr_hdr(w, flags, code, len);
- ADVANCE(w, remains, rv);
-
- switch (type)
- {
- case EAF_TYPE_INT:
- case EAF_TYPE_ROUTER_ID:
- if (len == 4)
- put_u32(w, a->u.data);
- else
- *w = a->u.data;
- break;
- case EAF_TYPE_IP_ADDRESS:
- {
- ip_addr ip = *(ip_addr *)a->u.ptr->data;
- ipa_hton(ip);
- memcpy(w, &ip, len);
- break;
- }
- case EAF_TYPE_INT_SET:
- case EAF_TYPE_LC_SET:
- case EAF_TYPE_EC_SET:
- {
- u32 *z = int_set_get_data(a->u.ptr);
- int i;
- for(i=0; i<len; i+=4)
- put_u32(w+i, *z++);
- break;
- }
- case EAF_TYPE_OPAQUE:
- case EAF_TYPE_AS_PATH:
- memcpy(w, a->u.ptr->data, len);
- break;
- default:
- bug("bgp_encode_attrs: unknown attribute type %02x", a->type);
- }
- ADVANCE(w, remains, len);
- }
- return w - start;
+ for (i = 0; i < attrs->count; i++)
+ {
+ len = bgp_encode_attr(s, &attrs->attrs[i], pos, end - pos);
- err_no_buffer:
- return -1;
+ if (len < 0)
+ return -1;
+
+ pos += len;
+ }
+
+ return pos - buf;
}
+
/*
-static void
-bgp_init_prefix(struct fib_node *N)
+ * Attribute decoding
+ */
+
+static void bgp_process_as4_attrs(ea_list **attrs, struct linpool *pool);
+
+static inline int
+bgp_as_path_loopy(struct bgp_proto *p, ea_list *attrs, u32 asn)
{
- struct bgp_prefix *p = (struct bgp_prefix *) N;
- p->bucket_node.next = NULL;
+ eattr *e = bgp_find_attr(attrs, BA_AS_PATH);
+ int num = p->cf->allow_local_as + 1;
+ return (e && (num > 0) && as_path_contains(e->u.ptr, asn, num));
}
-*/
-static int
-bgp_compare_u32(const u32 *x, const u32 *y)
+static inline int
+bgp_originator_id_loopy(struct bgp_proto *p, ea_list *attrs)
{
- return (*x < *y) ? -1 : (*x > *y) ? 1 : 0;
+ eattr *e = bgp_find_attr(attrs, BA_ORIGINATOR_ID);
+ return (e && (e->u.data == p->local_id));
}
-static inline void
-bgp_normalize_int_set(u32 *dest, u32 *src, unsigned cnt)
+static inline int
+bgp_cluster_list_loopy(struct bgp_proto *p, ea_list *attrs)
{
- memcpy(dest, src, sizeof(u32) * cnt);
- qsort(dest, cnt, sizeof(u32), (int(*)(const void *, const void *)) bgp_compare_u32);
+ eattr *e = bgp_find_attr(attrs, BA_CLUSTER_LIST);
+ return (e && int_set_contains(e->u.ptr, p->rr_cluster_id));
}
-static int
-bgp_compare_ec(const u32 *xp, const u32 *yp)
+static inline void
+bgp_decode_attr(struct bgp_parse_state *s, uint code, uint flags, byte *data, uint len, ea_list **to)
{
- u64 x = ec_get(xp, 0);
- u64 y = ec_get(yp, 0);
- return (x < y) ? -1 : (x > y) ? 1 : 0;
+ /* Handle duplicate attributes; RFC 7606 3 (g) */
+ if (BIT32_TEST(s->attrs_seen, code))
+ {
+ if ((code == BA_MP_REACH_NLRI) || (code == BA_MP_UNREACH_NLRI))
+ bgp_parse_error(s, 1);
+ else
+ DISCARD("Discarding duplicate attribute (code %u)", code);
+ }
+ BIT32_SET(s->attrs_seen, code);
+
+ if (bgp_attr_known(code))
+ {
+ const struct bgp_attr_desc *desc = &bgp_attr_table[code];
+
+ /* Handle conflicting flags; RFC 7606 3 (c) */
+ if ((flags ^ desc->flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))
+ WITHDRAW("Malformed %s attribute - conflicting flags (%02x)", desc->name, flags);
+
+ desc->decode(s, code, flags, data, len, to);
+ }
+ else /* Unknown attribute */
+ {
+ if (!(flags & BAF_OPTIONAL))
+ WITHDRAW("Unknown attribute (code %u) - conflicting flags (%02x)", code, flags);
+
+ bgp_decode_unknown(s, code, flags, data, len, to);
+ }
}
-static inline void
-bgp_normalize_ec_set(struct adata *ad, u32 *src, int internal)
+/**
+ * bgp_decode_attrs - check and decode BGP attributes
+ * @s: BGP parse state
+ * @data: start of attribute block
+ * @len: length of attribute block
+ *
+ * This function takes a BGP attribute block (a part of an Update message), checks
+ * its consistency and converts it to a list of BIRD route attributes represented
+ * by an (uncached) &rta.
+ */
+ea_list *
+bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len)
{
- u32 *dst = int_set_get_data(ad);
+ struct bgp_proto *p = s->proto;
+ ea_list *attrs = NULL;
+ uint code, flags, alen;
+ byte *pos = data;
+
+ /* Parse the attributes */
+ while (len)
+ {
+ alen = 0;
- /* Remove non-transitive communities (EC_TBIT active) on external sessions */
- if (! internal)
+ /* Read attribute type */
+ if (len < 2)
+ goto framing_error;
+ flags = pos[0];
+ code = pos[1];
+ ADVANCE(pos, len, 2);
+
+ /* Read attribute length */
+ if (flags & BAF_EXT_LEN)
{
- int len = int_set_get_size(ad);
- u32 *t = dst;
- int i;
-
- for (i=0; i < len; i += 2)
- {
- if (src[i] & EC_TBIT)
- continue;
-
- *t++ = src[i];
- *t++ = src[i+1];
- }
-
- ad->length = (t - dst) * 4;
+ if (len < 2)
+ goto framing_error;
+ alen = get_u16(pos);
+ ADVANCE(pos, len, 2);
+ }
+ else
+ {
+ if (len < 1)
+ goto framing_error;
+ alen = *pos;
+ ADVANCE(pos, len, 1);
}
- else
- memcpy(dst, src, ad->length);
- qsort(dst, ad->length / 8, 8, (int(*)(const void *, const void *)) bgp_compare_ec);
-}
+ if (alen > len)
+ goto framing_error;
-static int
-bgp_compare_lc(const u32 *x, const u32 *y)
-{
- if (x[0] != y[0])
- return (x[0] > y[0]) ? 1 : -1;
- if (x[1] != y[1])
- return (x[1] > y[1]) ? 1 : -1;
- if (x[2] != y[2])
- return (x[2] > y[2]) ? 1 : -1;
- return 0;
+ DBG("Attr %02x %02x %u\n", code, flags, alen);
+
+ bgp_decode_attr(s, code, flags, pos, alen, &attrs);
+ ADVANCE(pos, len, alen);
+ }
+
+ if (s->err_withdraw)
+ goto withdraw;
+
+ /* If there is no reachability NLRI, we are finished */
+ if (!s->ip_reach_len && !s->mp_reach_len)
+ return NULL;
+
+
+ /* Handle missing mandatory attributes; RFC 7606 3 (d) */
+ if (!BIT32_TEST(s->attrs_seen, BA_ORIGIN))
+ { REPORT(NO_MANDATORY, "ORIGIN"); goto withdraw; }
+
+ if (!BIT32_TEST(s->attrs_seen, BA_AS_PATH))
+ { REPORT(NO_MANDATORY, "AS_PATH"); goto withdraw; }
+
+ /* When receiving attributes from non-AS4-aware BGP speaker, we have to
+ reconstruct AS_PATH and AGGREGATOR attributes; RFC 6793 4.2.3 */
+ if (!p->as4_session)
+ bgp_process_as4_attrs(&attrs, s->pool);
+
+ /* Reject routes with our ASN in AS_PATH attribute */
+ if (bgp_as_path_loopy(p, attrs, p->local_as))
+ goto withdraw;
+
+ /* Reject routes with our Confederation ID in AS_PATH attribute; RFC 5065 4.0 */
+ if ((p->public_as != p->local_as) && bgp_as_path_loopy(p, attrs, p->public_as))
+ goto withdraw;
+
+ /* Reject routes with our Router ID in ORIGINATOR_ID attribute; RFC 4456 8 */
+ if (p->is_internal && bgp_originator_id_loopy(p, attrs))
+ goto withdraw;
+
+ /* Reject routes with our Cluster ID in CLUSTER_LIST attribute; RFC 4456 8 */
+ if (p->rr_client && bgp_cluster_list_loopy(p, attrs))
+ goto withdraw;
+
+ /* If there is no local preference, define one */
+ if (!BIT32_TEST(s->attrs_seen, BA_LOCAL_PREF))
+ bgp_set_attr_u32(&attrs, s->pool, BA_LOCAL_PREF, 0, p->cf->default_local_pref);
+
+ return attrs;
+
+
+framing_error:
+ /* RFC 7606 4 - handle attribute framing errors */
+ REPORT("Malformed attribute list - framing error (%u/%u) at %d",
+ alen, len, (int) (pos - s->attrs));
+
+withdraw:
+ /* RFC 7606 5.2 - handle missing NLRI during errors */
+ if (!s->ip_reach_len && !s->mp_reach_len)
+ bgp_parse_error(s, 1);
+
+ s->err_withdraw = 1;
+ return NULL;
}
-static inline void
-bgp_normalize_lc_set(u32 *dest, u32 *src, unsigned cnt)
+
+/*
+ * Route bucket hash table
+ */
+
+#define RBH_KEY(b) b->eattrs, b->hash
+#define RBH_NEXT(b) b->next
+#define RBH_EQ(a1,h1,a2,h2) h1 == h2 && ea_same(a1, a2)
+#define RBH_FN(a,h) h
+
+#define RBH_REHASH bgp_rbh_rehash
+#define RBH_PARAMS /8, *2, 2, 2, 8, 20
+
+
+HASH_DEFINE_REHASH_FN(RBH, struct bgp_bucket)
+
+void
+bgp_init_bucket_table(struct bgp_channel *c)
{
- memcpy(dest, src, LCOMM_LENGTH * cnt);
- qsort(dest, cnt, LCOMM_LENGTH, (int(*)(const void *, const void *)) bgp_compare_lc);
-}
+ HASH_INIT(c->bucket_hash, c->pool, 8);
-static void
-bgp_rehash_buckets(struct bgp_proto *p)
-{
- struct bgp_bucket **old = p->bucket_hash;
- struct bgp_bucket **new;
- unsigned oldn = p->hash_size;
- unsigned i, e, mask;
- struct bgp_bucket *b;
-
- p->hash_size = p->hash_limit;
- DBG("BGP: Rehashing bucket table from %d to %d\n", oldn, p->hash_size);
- p->hash_limit *= 4;
- if (p->hash_limit >= 65536)
- p->hash_limit = ~0;
- new = p->bucket_hash = mb_allocz(p->p.pool, p->hash_size * sizeof(struct bgp_bucket *));
- mask = p->hash_size - 1;
- for (i=0; i<oldn; i++)
- while (b = old[i])
- {
- old[i] = b->hash_next;
- e = b->hash & mask;
- b->hash_next = new[e];
- if (b->hash_next)
- b->hash_next->hash_prev = b;
- b->hash_prev = NULL;
- new[e] = b;
- }
- mb_free(old);
+ init_list(&c->bucket_queue);
+ c->withdraw_bucket = NULL;
}
static struct bgp_bucket *
-bgp_new_bucket(struct bgp_proto *p, ea_list *new, unsigned hash)
+bgp_get_bucket(struct bgp_channel *c, ea_list *new)
{
- struct bgp_bucket *b;
- unsigned ea_size = sizeof(ea_list) + new->count * sizeof(eattr);
- unsigned ea_size_aligned = BIRD_ALIGN(ea_size, CPU_STRUCT_ALIGN);
- unsigned size = sizeof(struct bgp_bucket) + ea_size_aligned;
- unsigned i;
+ /* Hash and lookup */
+ u32 hash = ea_hash(new);
+ struct bgp_bucket *b = HASH_FIND(c->bucket_hash, RBH, new, hash);
+
+ if (b)
+ return b;
+
+ uint ea_size = sizeof(ea_list) + new->count * sizeof(eattr);
+ uint ea_size_aligned = BIRD_ALIGN(ea_size, CPU_STRUCT_ALIGN);
+ uint size = sizeof(struct bgp_bucket) + ea_size_aligned;
+ uint i;
byte *dest;
- unsigned index = hash & (p->hash_size - 1);
/* Gather total size of non-inline attributes */
- for (i=0; i<new->count; i++)
- {
- eattr *a = &new->attrs[i];
- if (!(a->type & EAF_EMBEDDED))
- size += BIRD_ALIGN(sizeof(struct adata) + a->u.ptr->length, CPU_STRUCT_ALIGN);
- }
+ for (i = 0; i < new->count; i++)
+ {
+ eattr *a = &new->attrs[i];
- /* Create the bucket and hash it */
- b = mb_alloc(p->p.pool, size);
- b->hash_next = p->bucket_hash[index];
- if (b->hash_next)
- b->hash_next->hash_prev = b;
- p->bucket_hash[index] = b;
- b->hash_prev = NULL;
- b->hash = hash;
- add_tail(&p->bucket_queue, &b->send_node);
+ if (!(a->type & EAF_EMBEDDED))
+ size += BIRD_ALIGN(sizeof(struct adata) + a->u.ptr->length, CPU_STRUCT_ALIGN);
+ }
+
+ /* Create the bucket */
+ b = mb_alloc(c->pool, size);
init_list(&b->prefixes);
+ b->hash = hash;
+
+ /* Copy list of extended attributes */
memcpy(b->eattrs, new, ea_size);
- dest = ((byte *)b->eattrs) + ea_size_aligned;
+ dest = ((byte *) b->eattrs) + ea_size_aligned;
/* Copy values of non-inline attributes */
- for (i=0; i<new->count; i++)
+ for (i = 0; i < new->count; i++)
+ {
+ eattr *a = &b->eattrs->attrs[i];
+
+ if (!(a->type & EAF_EMBEDDED))
{
- eattr *a = &b->eattrs->attrs[i];
- if (!(a->type & EAF_EMBEDDED))
- {
- struct adata *oa = a->u.ptr;
- struct adata *na = (struct adata *) dest;
- memcpy(na, oa, sizeof(struct adata) + oa->length);
- a->u.ptr = na;
- dest += BIRD_ALIGN(sizeof(struct adata) + na->length, CPU_STRUCT_ALIGN);
- }
+ struct adata *oa = a->u.ptr;
+ struct adata *na = (struct adata *) dest;
+ memcpy(na, oa, sizeof(struct adata) + oa->length);
+ a->u.ptr = na;
+ dest += BIRD_ALIGN(sizeof(struct adata) + na->length, CPU_STRUCT_ALIGN);
}
+ }
- /* If needed, rehash */
- p->hash_count++;
- if (p->hash_count > p->hash_limit)
- bgp_rehash_buckets(p);
+ /* Insert the bucket to send queue and bucket hash */
+ add_tail(&c->bucket_queue, &b->send_node);
+ HASH_INSERT2(c->bucket_hash, RBH, c->pool, b);
return b;
}
static struct bgp_bucket *
-bgp_get_bucket(struct bgp_proto *p, net *n, ea_list *attrs, int originate)
+bgp_get_withdraw_bucket(struct bgp_channel *c)
{
- ea_list *new;
- unsigned i, cnt, hash, code;
- eattr *a, *d;
- u32 seen = 0;
- struct bgp_bucket *b;
-
- /* Merge the attribute list */
- new = alloca(ea_scan(attrs));
- ea_merge(attrs, new);
- ea_sort(new);
+ if (!c->withdraw_bucket)
+ {
+ c->withdraw_bucket = mb_allocz(c->pool, sizeof(struct bgp_bucket));
+ init_list(&c->withdraw_bucket->prefixes);
+ }
- /* Normalize attributes */
- d = new->attrs;
- cnt = new->count;
- new->count = 0;
- for(i=0; i<cnt; i++)
- {
- a = &new->attrs[i];
- if (EA_PROTO(a->id) != EAP_BGP)
- continue;
- code = EA_ID(a->id);
- if (ATTR_KNOWN(code))
- {
- if (!p->is_internal)
- {
- if (!bgp_attr_table[code].allow_in_ebgp)
- continue;
- if ((code == BA_LOCAL_PREF) && !p->cf->allow_local_pref)
- continue;
- }
- /* The flags might have been zero if the attr was added by filters */
- a->flags = (a->flags & BAF_PARTIAL) | bgp_attr_table[code].expected_flags;
- if (code < 32)
- seen |= 1 << code;
- }
- else
- {
- /* Don't re-export unknown non-transitive attributes */
- if (!(a->flags & BAF_TRANSITIVE))
- continue;
- }
- *d = *a;
- if ((d->type & EAF_ORIGINATED) && !originate && (d->flags & BAF_TRANSITIVE) && (d->flags & BAF_OPTIONAL))
- d->flags |= BAF_PARTIAL;
- switch (d->type & EAF_TYPE_MASK)
- {
- case EAF_TYPE_INT_SET:
- {
- struct adata *z = alloca(sizeof(struct adata) + d->u.ptr->length);
- z->length = d->u.ptr->length;
- bgp_normalize_int_set((u32 *) z->data, (u32 *) d->u.ptr->data, z->length / 4);
- d->u.ptr = z;
- break;
- }
- case EAF_TYPE_EC_SET:
- {
- struct adata *z = alloca(sizeof(struct adata) + d->u.ptr->length);
- z->length = d->u.ptr->length;
- bgp_normalize_ec_set(z, (u32 *) d->u.ptr->data, p->is_internal);
- d->u.ptr = z;
- break;
- }
- case EAF_TYPE_LC_SET:
- {
- struct adata *z = alloca(sizeof(struct adata) + d->u.ptr->length);
- z->length = d->u.ptr->length;
- bgp_normalize_lc_set((u32 *) z->data, (u32 *) d->u.ptr->data, z->length / LCOMM_LENGTH);
- d->u.ptr = z;
- break;
- }
- default: ;
- }
- d++;
- new->count++;
- }
+ return c->withdraw_bucket;
+}
- /* Hash */
- hash = ea_hash(new);
- for(b=p->bucket_hash[hash & (p->hash_size - 1)]; b; b=b->hash_next)
- if (b->hash == hash && ea_same(b->eattrs, new))
- {
- DBG("Found bucket.\n");
- return b;
- }
-
- /* Ensure that there are all mandatory attributes */
- for(i=0; i<ARRAY_SIZE(bgp_mandatory_attrs); i++)
- if (!(seen & (1 << bgp_mandatory_attrs[i])))
- {
- log(L_ERR "%s: Mandatory attribute %s missing in route %I/%d", p->p.name, bgp_attr_table[bgp_mandatory_attrs[i]].name, n->n.prefix, n->n.pxlen);
- return NULL;
- }
-
- /* Check if next hop is valid */
- a = ea_find(new, EA_CODE(EAP_BGP, BA_NEXT_HOP));
- if (!a || ipa_equal(p->cf->remote_ip, *(ip_addr *)a->u.ptr->data))
- {
- log(L_ERR "%s: Invalid NEXT_HOP attribute in route %I/%d", p->p.name, n->n.prefix, n->n.pxlen);
- return NULL;
- }
+void
+bgp_free_bucket(struct bgp_channel *c, struct bgp_bucket *b)
+{
+ rem_node(&b->send_node);
+ HASH_REMOVE2(c->bucket_hash, RBH, c->pool, b);
+ mb_free(b);
+}
- /* Create new bucket */
- DBG("Creating bucket.\n");
- return bgp_new_bucket(p, new, hash);
+void
+bgp_defer_bucket(struct bgp_channel *c, struct bgp_bucket *b)
+{
+ rem_node(&b->send_node);
+ add_tail(&c->bucket_queue, &b->send_node);
}
void
-bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck)
+bgp_withdraw_bucket(struct bgp_channel *c, struct bgp_bucket *b)
{
- if (buck->hash_next)
- buck->hash_next->hash_prev = buck->hash_prev;
- if (buck->hash_prev)
- buck->hash_prev->hash_next = buck->hash_next;
- else
- p->bucket_hash[buck->hash & (p->hash_size-1)] = buck->hash_next;
- mb_free(buck);
+ struct bgp_proto *p = (void *) c->c.proto;
+ struct bgp_bucket *wb = bgp_get_withdraw_bucket(c);
+
+ log(L_ERR "%s: Attribute list too long", p->p.name);
+ while (!EMPTY_LIST(b->prefixes))
+ {
+ struct bgp_prefix *px = HEAD(b->prefixes);
+
+ log(L_ERR "%s: - withdrawing %N", p->p.name, &px->net);
+ rem_node(&px->buck_node);
+ add_tail(&wb->prefixes, &px->buck_node);
+ }
}
-/* Prefix hash table */
+/*
+ * Prefix hash table
+ */
-#define PXH_KEY(n1) n1->n.prefix, n1->n.pxlen, n1->path_id
-#define PXH_NEXT(n) n->next
-#define PXH_EQ(p1,l1,i1,p2,l2,i2) ipa_equal(p1, p2) && l1 == l2 && i1 == i2
-#define PXH_FN(p,l,i) ipa_hash32(p) ^ u32_hash((l << 16) ^ i)
+#define PXH_KEY(px) px->net, px->path_id, px->hash
+#define PXH_NEXT(px) px->next
+#define PXH_EQ(n1,i1,h1,n2,i2,h2) h1 == h2 && i1 == i2 && net_equal(n1, n2)
+#define PXH_FN(n,i,h) h
#define PXH_REHASH bgp_pxh_rehash
#define PXH_PARAMS /8, *2, 2, 2, 8, 20
@@ -931,308 +1292,282 @@ bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck)
HASH_DEFINE_REHASH_FN(PXH, struct bgp_prefix)
void
-bgp_init_prefix_table(struct bgp_proto *p, u32 order)
+bgp_init_prefix_table(struct bgp_channel *c)
{
- HASH_INIT(p->prefix_hash, p->p.pool, order);
+ HASH_INIT(c->prefix_hash, c->pool, 8);
- p->prefix_slab = sl_new(p->p.pool, sizeof(struct bgp_prefix));
+ uint alen = net_addr_length[c->c.net_type];
+ c->prefix_slab = alen ? sl_new(c->pool, sizeof(struct bgp_prefix) + alen) : NULL;
}
void
-bgp_free_prefix_table(struct bgp_proto *p)
+bgp_free_prefix_table(struct bgp_channel *c)
{
- HASH_FREE(p->prefix_hash);
+ HASH_FREE(c->prefix_hash);
- rfree(p->prefix_slab);
- p->prefix_slab = NULL;
+ rfree(c->prefix_slab);
+ c->prefix_slab = NULL;
}
static struct bgp_prefix *
-bgp_get_prefix(struct bgp_proto *p, ip_addr prefix, int pxlen, u32 path_id)
+bgp_get_prefix(struct bgp_channel *c, net_addr *net, u32 path_id)
{
- struct bgp_prefix *bp = HASH_FIND(p->prefix_hash, PXH, prefix, pxlen, path_id);
+ u32 hash = net_hash(net) ^ u32_hash(path_id);
+ struct bgp_prefix *px = HASH_FIND(c->prefix_hash, PXH, net, path_id, hash);
+
+ if (px)
+ {
+ rem_node(&px->buck_node);
+ return px;
+ }
- if (bp)
- return bp;
+ if (c->prefix_slab)
+ px = sl_alloc(c->prefix_slab);
+ else
+ px = mb_alloc(c->pool, sizeof(struct bgp_prefix) + net->length);
- bp = sl_alloc(p->prefix_slab);
- bp->n.prefix = prefix;
- bp->n.pxlen = pxlen;
- bp->path_id = path_id;
- bp->bucket_node.next = NULL;
+ px->buck_node.next = NULL;
+ px->buck_node.prev = NULL;
+ px->hash = hash;
+ px->path_id = path_id;
+ net_copy(px->net, net);
- HASH_INSERT2(p->prefix_hash, PXH, p->p.pool, bp);
+ HASH_INSERT2(c->prefix_hash, PXH, c->pool, px);
- return bp;
+ return px;
}
void
-bgp_free_prefix(struct bgp_proto *p, struct bgp_prefix *bp)
+bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
{
- HASH_REMOVE2(p->prefix_hash, PXH, p->p.pool, bp);
- sl_free(p->prefix_slab, bp);
+ rem_node(&px->buck_node);
+ HASH_REMOVE2(c->prefix_hash, PXH, c->pool, px);
+
+ if (c->prefix_slab)
+ sl_free(c->prefix_slab, px);
+ else
+ mb_free(px);
}
-void
-bgp_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs)
+/*
+ * BGP protocol glue
+ */
+
+int
+bgp_import_control(struct proto *P, rte **new, ea_list **attrs UNUSED, struct linpool *pool UNUSED)
{
+ rte *e = *new;
+ struct proto *SRC = e->attrs->src->proto;
struct bgp_proto *p = (struct bgp_proto *) P;
- struct bgp_bucket *buck;
- struct bgp_prefix *px;
- rte *key;
- u32 path_id;
+ struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (struct bgp_proto *) SRC : NULL;
- DBG("BGP: Got route %I/%d %s\n", n->n.prefix, n->n.pxlen, new ? "up" : "down");
+ /* Reject our routes */
+ if (src == p)
+ return -1;
- if (new)
- {
- key = new;
- buck = bgp_get_bucket(p, n, attrs, new->attrs->source != RTS_BGP);
- if (!buck) /* Inconsistent attribute list */
- return;
- }
- else
- {
- key = old;
- if (!(buck = p->withdraw_bucket))
- {
- buck = p->withdraw_bucket = mb_alloc(P->pool, sizeof(struct bgp_bucket));
- init_list(&buck->prefixes);
- }
- }
- path_id = p->add_path_tx ? key->attrs->src->global_id : 0;
- px = bgp_get_prefix(p, n->n.prefix, n->n.pxlen, path_id);
- if (px->bucket_node.next)
- {
- DBG("\tRemoving old entry.\n");
- rem_node(&px->bucket_node);
- }
- add_tail(&buck->prefixes, &px->bucket_node);
- bgp_schedule_packet(p->conn, PKT_UPDATE);
-}
+ /* Accept non-BGP routes */
+ if (src == NULL)
+ return 0;
-static int
-bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *pool)
-{
- ea_list *ea = lp_alloc(pool, sizeof(ea_list) + 4*sizeof(eattr));
- rta *rta = e->attrs;
- byte *z;
+ // XXXX: Check next hop AF
- ea->next = *attrs;
- *attrs = ea;
- ea->flags = EALF_SORTED;
- ea->count = 4;
+ /* IBGP route reflection, RFC 4456 */
+ if (p->is_internal && src->is_internal && (p->local_as == src->local_as))
+ {
+ /* Rejected unless configured as route reflector */
+ if (!p->rr_client && !src->rr_client)
+ return -1;
+
+ /* Generally, this should be handled when path is received, but we check it
+ also here as rr_cluster_id may be undefined or different in src. */
+ if (p->rr_cluster_id && bgp_cluster_list_loopy(p, e->attrs->eattrs))
+ return -1;
+ }
- bgp_set_attr(ea->attrs, BA_ORIGIN,
- ((rta->source == RTS_OSPF_EXT1) || (rta->source == RTS_OSPF_EXT2)) ? ORIGIN_INCOMPLETE : ORIGIN_IGP);
+ /* Handle well-known communities, RFC 1997 */
+ struct eattr *c;
+ if (p->cf->interpret_communities &&
+ (c = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_COMMUNITY))))
+ {
+ struct adata *d = c->u.ptr;
- if (p->is_internal)
- bgp_set_attr_wa(ea->attrs+1, pool, BA_AS_PATH, 0);
- else
- {
- z = bgp_set_attr_wa(ea->attrs+1, pool, BA_AS_PATH, 6);
- z[0] = AS_PATH_SEQUENCE;
- z[1] = 1; /* 1 AS */
- put_u32(z+2, p->local_as);
- }
+ /* Do not export anywhere */
+ if (int_set_contains(d, BGP_COMM_NO_ADVERTISE))
+ return -1;
- /* iBGP -> use gw, eBGP multi-hop -> use source_addr,
- eBGP single-hop -> use gw if on the same iface */
- z = bgp_set_attr_wa(ea->attrs+2, pool, BA_NEXT_HOP, NEXT_HOP_LENGTH);
- if (p->cf->next_hop_self ||
- rta->dest != RTD_ROUTER ||
- ipa_equal(rta->gw, IPA_NONE) ||
- ipa_is_link_local(rta->gw) ||
- (!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);
+ /* Do not export outside of AS (or member-AS) */
+ if (!p->is_internal && int_set_contains(d, BGP_COMM_NO_EXPORT_SUBCONFED))
+ return -1;
- bgp_set_attr(ea->attrs+3, BA_LOCAL_PREF, p->cf->default_local_pref);
+ /* Do not export outside of AS (or confederation) */
+ if (!p->is_interior && int_set_contains(d, BGP_COMM_NO_EXPORT))
+ return -1;
+ }
- return 0; /* Leave decision to the filters */
+ return 0;
}
-static inline int
-bgp_as_path_loopy(struct bgp_proto *p, rta *a)
-{
- int num = p->cf->allow_local_as + 1;
- eattr *e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
- return (e && (num > 0) && as_path_contains(e->u.ptr, p->local_as, num));
-}
+static adata null_adata; /* adata of length 0 */
-static inline int
-bgp_originator_id_loopy(struct bgp_proto *p, rta *a)
-{
- eattr *e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID));
- return (e && (e->u.data == p->local_id));
-}
-
-static inline int
-bgp_cluster_list_loopy(struct bgp_proto *p, rta *a)
+static ea_list *
+bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *attrs0, struct linpool *pool)
{
- eattr *e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_CLUSTER_LIST));
- return (e && p->rr_client && int_set_contains(e->u.ptr, p->rr_cluster_id));
-}
+ struct proto *SRC = e->attrs->src->proto;
+ struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (void *) SRC : NULL;
+ struct bgp_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls };
+ ea_list *attrs = attrs0;
+ eattr *a;
+ adata *ad;
+ /* ORIGIN attribute - mandatory, attach if missing */
+ if (! bgp_find_attr(attrs0, BA_ORIGIN))
+ bgp_set_attr_u32(&attrs, pool, BA_ORIGIN, 0, src ? ORIGIN_INCOMPLETE : ORIGIN_IGP);
-static inline void
-bgp_path_prepend(rte *e, ea_list **attrs, struct linpool *pool, u32 as)
-{
- eattr *a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
- bgp_attach_attr(attrs, pool, BA_AS_PATH, (uintptr_t) as_path_prepend(pool, a->u.ptr, as));
-}
+ /* AS_PATH attribute - mandatory */
+ a = bgp_find_attr(attrs0, BA_AS_PATH);
+ ad = a ? a->u.ptr : &null_adata;
-static inline void
-bgp_cluster_list_prepend(rte *e, ea_list **attrs, struct linpool *pool, u32 cid)
-{
- eattr *a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_CLUSTER_LIST));
- bgp_attach_attr(attrs, pool, BA_CLUSTER_LIST, (uintptr_t) int_set_prepend(pool, a ? a->u.ptr : NULL, cid));
-}
+ /* AS_PATH attribute - strip AS_CONFED* segments outside confederation */
+ if ((!p->cf->confederation || !p->is_interior) && as_path_contains_confed(ad))
+ ad = as_path_strip_confed(pool, ad);
-static int
-bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *pool, int rr)
-{
- eattr *a;
+ /* AS_PATH attribute - keep or prepend ASN */
+ if (p->is_internal ||
+ (p->rs_client && src && src->rs_client))
+ {
+ /* IBGP or route server -> just ensure there is one */
+ if (!a)
+ bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, &null_adata);
+ }
+ else if (p->is_interior)
+ {
+ /* Confederation -> prepend ASN as AS_CONFED_SEQUENCE */
+ ad = as_path_prepend2(pool, ad, AS_PATH_CONFED_SEQUENCE, p->public_as);
+ bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, ad);
+ }
+ else /* Regular EBGP (no RS, no confederation) */
+ {
+ /* Regular EBGP -> prepend ASN as regular sequence */
+ ad = as_path_prepend2(pool, ad, AS_PATH_SEQUENCE, p->public_as);
+ bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, ad);
+
+ /* MULTI_EXIT_DESC attribute - accept only if set in export filter */
+ a = bgp_find_attr(attrs0, BA_MULTI_EXIT_DISC);
+ if (a && !(a->type & EAF_FRESH))
+ bgp_unset_attr(&attrs, pool, BA_MULTI_EXIT_DISC);
+ }
- if (!p->is_internal && !p->rs_client)
- {
- bgp_path_prepend(e, attrs, pool, p->local_as);
-
- /* The MULTI_EXIT_DISC attribute received from a neighboring AS MUST NOT be
- * propagated to other neighboring ASes.
- * Perhaps it would be better to undefine it.
- */
- a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
- if (a)
- bgp_attach_attr(attrs, pool, BA_MULTI_EXIT_DISC, 0);
- }
+ /* NEXT_HOP attribute - delegated to AF-specific hook */
+ a = bgp_find_attr(attrs0, BA_NEXT_HOP);
+ bgp_update_next_hop(&s, a, &attrs);
- /* 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->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 */
- }
- else
- {
- /* Need to create new one */
- byte *b = bgp_attach_attr_wa(attrs, pool, BA_NEXT_HOP, NEXT_HOP_LENGTH);
- set_next_hop(b, p->source_addr);
- }
+ /* LOCAL_PREF attribute - required for IBGP, attach if missing */
+ if (p->is_interior && ! bgp_find_attr(attrs0, BA_LOCAL_PREF))
+ bgp_set_attr_u32(&attrs, pool, BA_LOCAL_PREF, 0, p->cf->default_local_pref);
- if (rr)
- {
- /* Handling route reflection, RFC 4456 */
- struct bgp_proto *src = (struct bgp_proto *) e->attrs->src->proto;
+ /* IBGP route reflection, RFC 4456 */
+ if (src && src->is_internal && p->is_internal && (src->local_as == p->local_as))
+ {
+ /* ORIGINATOR_ID attribute - attach if not already set */
+ if (! bgp_find_attr(attrs0, BA_ORIGINATOR_ID))
+ bgp_set_attr_u32(&attrs, pool, BA_ORIGINATOR_ID, 0, src->remote_id);
- a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID));
- if (!a)
- bgp_attach_attr(attrs, pool, BA_ORIGINATOR_ID, src->remote_id);
+ /* CLUSTER_LIST attribute - prepend cluster ID */
+ a = bgp_find_attr(attrs0, BA_CLUSTER_LIST);
+ ad = a ? a->u.ptr : NULL;
- /* We attach proper cluster ID according to whether the route is entering or leaving the cluster */
- bgp_cluster_list_prepend(e, attrs, pool, src->rr_client ? src->rr_cluster_id : p->rr_cluster_id);
+ /* Prepend src cluster ID */
+ if (src->rr_cluster_id)
+ ad = int_set_prepend(pool, ad, src->rr_cluster_id);
- /* Two RR clients with different cluster ID, hmmm */
- if (src->rr_client && p->rr_client && (src->rr_cluster_id != p->rr_cluster_id))
- bgp_cluster_list_prepend(e, attrs, pool, p->rr_cluster_id);
- }
+ /* Prepend dst cluster ID if src and dst clusters are different */
+ if (p->rr_cluster_id && (src->rr_cluster_id != p->rr_cluster_id))
+ ad = int_set_prepend(pool, ad, p->rr_cluster_id);
- return 0; /* Leave decision to the filters */
-}
+ /* Should be at least one prepended cluster ID */
+ bgp_set_attr_ptr(&attrs, pool, BA_CLUSTER_LIST, 0, ad);
+ }
-static int
-bgp_community_filter(struct bgp_proto *p, rte *e)
-{
- eattr *a;
- struct adata *d;
+ /* AS4_* transition attributes, RFC 6793 4.2.2 */
+ if (! p->as4_session)
+ {
+ a = bgp_find_attr(attrs, BA_AS_PATH);
+ if (a && as_path_contains_as4(a->u.ptr))
+ {
+ bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, as_path_to_old(pool, a->u.ptr));
+ bgp_set_attr_ptr(&attrs, pool, BA_AS4_PATH, 0, as_path_strip_confed(pool, a->u.ptr));
+ }
- /* Check if we aren't forbidden to export the route by communities */
- a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_COMMUNITY));
- if (a)
+ a = bgp_find_attr(attrs, BA_AGGREGATOR);
+ if (a && aggregator_contains_as4(a->u.ptr))
{
- d = a->u.ptr;
- if (int_set_contains(d, BGP_COMM_NO_ADVERTISE))
- {
- DBG("\tNO_ADVERTISE\n");
- return 1;
- }
- if (!p->is_internal &&
- (int_set_contains(d, BGP_COMM_NO_EXPORT) ||
- int_set_contains(d, BGP_COMM_NO_EXPORT_SUBCONFED)))
- {
- DBG("\tNO_EXPORT\n");
- return 1;
- }
+ bgp_set_attr_ptr(&attrs, pool, BA_AGGREGATOR, 0, aggregator_to_old(pool, a->u.ptr));
+ bgp_set_attr_ptr(&attrs, pool, BA_AS4_AGGREGATOR, 0, a->u.ptr);
}
+ }
- return 0;
+ /*
+ * Presence of mandatory attributes ORIGIN and AS_PATH is ensured by above
+ * conditions. Presence and validity of quasi-mandatory NEXT_HOP attribute
+ * should be checked in AF-specific hooks.
+ */
+
+ /* Apply per-attribute export hooks for validatation and normalization */
+ return bgp_export_attrs(&s, attrs);
}
-int
-bgp_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool)
+void
+bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old, ea_list *attrs)
{
- rte *e = *new;
- struct bgp_proto *p = (struct bgp_proto *) P;
- struct bgp_proto *new_bgp = (e->attrs->src->proto->proto == &proto_bgp) ?
- (struct bgp_proto *) e->attrs->src->proto : NULL;
+ struct bgp_proto *p = (void *) P;
+ struct bgp_channel *c = (void *) C;
+ struct bgp_bucket *buck;
+ struct bgp_prefix *px;
+ u32 path;
- if (p == new_bgp) /* Poison reverse updates */
- return -1;
- if (new_bgp)
- {
- /* We should check here for cluster list loop, because the receiving BGP instance
- might have different cluster ID */
- if (bgp_cluster_list_loopy(p, e->attrs))
- return -1;
-
- if (p->cf->interpret_communities && bgp_community_filter(p, e))
- return -1;
-
- if (p->local_as == new_bgp->local_as && p->is_internal && new_bgp->is_internal)
- {
- /* Redistribution of internal routes with IBGP */
- if (p->rr_client || new_bgp->rr_client)
- /* Route reflection, RFC 4456 */
- return bgp_update_attrs(p, e, attrs, pool, 1);
- else
- return -1;
- }
- else
- return bgp_update_attrs(p, e, attrs, pool, 0);
- }
+ if (new)
+ {
+ attrs = bgp_update_attrs(p, c, new, attrs, bgp_linpool2);
+
+ /* If attributes are invalid, we fail back to withdraw */
+ buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c);
+ path = new->attrs->src->global_id;
+
+ lp_flush(bgp_linpool2);
+ }
else
- return bgp_create_attrs(p, e, attrs, pool);
+ {
+ buck = bgp_get_withdraw_bucket(c);
+ path = old->attrs->src->global_id;
+ }
+
+ px = bgp_get_prefix(c, n->n.addr, c->add_path_tx ? path : 0);
+ add_tail(&buck->prefixes, &px->buck_node);
+
+ bgp_schedule_packet(p->conn, c, PKT_UPDATE);
}
+
static inline u32
bgp_get_neighbor(rte *r)
{
eattr *e = ea_find(r->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
u32 as;
- if (e && as_path_get_first(e->u.ptr, &as))
+ if (e && as_path_get_first_regular(e->u.ptr, &as))
return as;
- else
- return ((struct bgp_proto *) r->attrs->src->proto)->remote_as;
+
+ /* If AS_PATH is not defined, we treat rte as locally originated */
+ struct bgp_proto *p = (void *) r->attrs->src->proto;
+ return p->cf->confederation ?: p->local_as;
}
static inline int
rte_resolvable(rte *rt)
{
- int rd = rt->attrs->dest;
- return (rd == RTD_ROUTER) || (rd == RTD_DEVICE) || (rd == RTD_MULTIPATH);
+ return rt->attrs->dest == RTD_UNICAST;
}
int
@@ -1271,16 +1606,16 @@ bgp_rte_better(rte *new, rte *old)
/* RFC 4271 9.1.2.2. a) Use AS path lengths */
if (new_bgp->cf->compare_path_lengths || old_bgp->cf->compare_path_lengths)
- {
- x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
- y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
- n = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN;
- o = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN;
- if (n < o)
- return 1;
- if (n > o)
- return 0;
- }
+ {
+ x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
+ y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
+ n = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN;
+ o = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN;
+ if (n < o)
+ return 1;
+ if (n > o)
+ return 0;
+ }
/* RFC 4271 9.1.2.2. b) Use origins */
x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN));
@@ -1305,21 +1640,21 @@ bgp_rte_better(rte *new, rte *old)
*/
if (new_bgp->cf->med_metric || old_bgp->cf->med_metric ||
(bgp_get_neighbor(new) == bgp_get_neighbor(old)))
- {
- x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
- y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
- n = x ? x->u.data : new_bgp->cf->default_med;
- o = y ? y->u.data : old_bgp->cf->default_med;
- if (n < o)
- return 1;
- if (n > o)
- return 0;
- }
+ {
+ x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
+ y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
+ n = x ? x->u.data : new_bgp->cf->default_med;
+ o = y ? y->u.data : old_bgp->cf->default_med;
+ if (n < o)
+ return 1;
+ if (n > o)
+ return 0;
+ }
/* RFC 4271 9.1.2.2. d) Prefer external peers */
- if (new_bgp->is_internal > old_bgp->is_internal)
+ if (new_bgp->is_interior > old_bgp->is_interior)
return 0;
- if (new_bgp->is_internal < old_bgp->is_internal)
+ if (new_bgp->is_interior < old_bgp->is_interior)
return 1;
/* RFC 4271 9.1.2.2. e) Compare IGP metrics */
@@ -1331,7 +1666,7 @@ bgp_rte_better(rte *new, rte *old)
return 0;
/* RFC 4271 9.1.2.2. f) Compare BGP identifiers */
- /* RFC 4456 9. a) Use ORIGINATOR_ID instead of local neighor ID */
+ /* RFC 4456 9. a) Use ORIGINATOR_ID instead of local neighbor ID */
x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID));
y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID));
n = x ? x->u.data : new_bgp->remote_id;
@@ -1390,18 +1725,18 @@ bgp_rte_mergable(rte *pri, rte *sec)
/* RFC 4271 9.1.2.2. a) Use AS path lengths */
if (pri_bgp->cf->compare_path_lengths || sec_bgp->cf->compare_path_lengths)
- {
- x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
- y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
- p = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN;
- s = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN;
+ {
+ x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
+ y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
+ p = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN;
+ s = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN;
- if (p != s)
- return 0;
+ if (p != s)
+ return 0;
-// if (DELTA(p, s) > pri_bgp->cf->relax_multipath)
-// return 0;
- }
+// if (DELTA(p, s) > pri_bgp->cf->relax_multipath)
+// return 0;
+ }
/* RFC 4271 9.1.2.2. b) Use origins */
x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN));
@@ -1414,17 +1749,17 @@ bgp_rte_mergable(rte *pri, rte *sec)
/* RFC 4271 9.1.2.2. c) Compare MED's */
if (pri_bgp->cf->med_metric || sec_bgp->cf->med_metric ||
(bgp_get_neighbor(pri) == bgp_get_neighbor(sec)))
- {
- x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
- y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
- p = x ? x->u.data : pri_bgp->cf->default_med;
- s = y ? y->u.data : sec_bgp->cf->default_med;
- if (p != s)
- return 0;
- }
+ {
+ x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
+ y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
+ p = x ? x->u.data : pri_bgp->cf->default_med;
+ s = y ? y->u.data : sec_bgp->cf->default_med;
+ if (p != s)
+ return 0;
+ }
/* RFC 4271 9.1.2.2. d) Prefer external peers */
- if (pri_bgp->is_internal != sec_bgp->is_internal)
+ if (pri_bgp->is_interior != sec_bgp->is_interior)
return 0;
/* RFC 4271 9.1.2.2. e) Compare IGP metrics */
@@ -1439,7 +1774,6 @@ bgp_rte_mergable(rte *pri, rte *sec)
}
-
static inline int
same_group(rte *r, u32 lpref, u32 lasn)
{
@@ -1484,7 +1818,7 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
* that this fn is not called for them.
*
* The idea is simple, the implementation is more problematic,
- * mostly because of optimizations in rte_recalculate() that
+ * mostly because of optimizations in rte_recalculate() that
* avoids full recalculation in most cases.
*
* We can assume that at least one of new, old is non-NULL and both
@@ -1496,14 +1830,14 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
/* If new and old are from different groups, we just process that
as two independent events */
if (new && old && !same_group(old, lpref, lasn))
- {
- int i1, i2;
- i1 = bgp_rte_recalculate(table, net, NULL, old, old_best);
- i2 = bgp_rte_recalculate(table, net, new, NULL, old_best);
- return i1 || i2;
- }
+ {
+ int i1, i2;
+ i1 = bgp_rte_recalculate(table, net, NULL, old, old_best);
+ i2 = bgp_rte_recalculate(table, net, new, NULL, old_best);
+ return i1 || i2;
+ }
- /*
+ /*
* We could find the best-in-group and then make some shortcuts like
* in rte_recalculate, but as we would have to walk through all
* net->routes just to find it, it is probably not worth. So we
@@ -1515,35 +1849,35 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
new->u.bgp.suppressed = 1;
if (old)
+ {
+ old_is_group_best = !old->u.bgp.suppressed;
+ old->u.bgp.suppressed = 1;
+ int new_is_better = new && bgp_rte_better(new, old);
+
+ /* The first case - replace not best with worse (or remove not best) */
+ if (!old_is_group_best && !new_is_better)
+ return 0;
+
+ /* The second case - replace the best with better */
+ if (old_is_group_best && new_is_better)
{
- old_is_group_best = !old->u.bgp.suppressed;
- old->u.bgp.suppressed = 1;
- int new_is_better = new && bgp_rte_better(new, old);
-
- /* The first case - replace not best with worse (or remove not best) */
- if (!old_is_group_best && !new_is_better)
- return 0;
-
- /* The second case - replace the best with better */
- if (old_is_group_best && new_is_better)
- {
- /* new is best-in-group, the see discussion below - this is
- a special variant of NBG && OBG. From OBG we can deduce
- that same_group(old_best) iff (old == old_best) */
- new->u.bgp.suppressed = 0;
- return (old == old_best);
- }
+ /* new is best-in-group, the see discussion below - this is
+ a special variant of NBG && OBG. From OBG we can deduce
+ that same_group(old_best) iff (old == old_best) */
+ new->u.bgp.suppressed = 0;
+ return (old == 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; rte_is_valid(s); s=s->next)
if (use_deterministic_med(s) && same_group(s, lpref, lasn))
- {
- s->u.bgp.suppressed = 1;
- if (!r || bgp_rte_better(s, r))
- r = s;
- }
+ {
+ s->u.bgp.suppressed = 1;
+ if (!r || bgp_rte_better(s, r))
+ r = s;
+ }
/* Simple case - the last route in group disappears */
if (!r)
@@ -1582,397 +1916,77 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
return old_is_group_best;
}
-static struct adata *
-bgp_aggregator_convert_to_new(struct adata *old, struct linpool *pool)
-{
- struct adata *newa = lp_alloc(pool, sizeof(struct adata) + 8);
- newa->length = 8;
- aggregator_convert_to_new(old, newa->data);
- return newa;
-}
-
-/* Take last req_as ASNs from path old2 (in 2B format), convert to 4B format
- * and append path old4 (in 4B format).
+/*
+ * Reconstruct AS_PATH and AGGREGATOR according to RFC 6793 4.2.3
*/
-static struct adata *
-bgp_merge_as_paths(struct adata *old2, struct adata *old4, int req_as, struct linpool *pool)
-{
- byte buf[old2->length * 2];
-
- int ol = as_path_convert_to_new(old2, buf, req_as);
- int nl = ol + (old4 ? old4->length : 0);
-
- struct adata *newa = lp_alloc(pool, sizeof(struct adata) + nl);
- newa->length = nl;
- memcpy(newa->data, buf, ol);
- if (old4) memcpy(newa->data + ol, old4->data, old4->length);
-
- return newa;
-}
-
-static int
-as4_aggregator_valid(struct adata *aggr)
-{
- return aggr->length == 8;
-}
-
-
-/* Reconstruct 4B AS_PATH and AGGREGATOR according to RFC 4893 4.2.3 */
-static void
-bgp_reconstruct_4b_atts(struct bgp_proto *p, rta *a, struct linpool *pool)
-{
- eattr *p2 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
- eattr *p4 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS4_PATH));
- eattr *a2 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AGGREGATOR));
- eattr *a4 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS4_AGGREGATOR));
- int a4_removed = 0;
-
- if (a4 && !as4_aggregator_valid(a4->u.ptr))
- {
- log(L_WARN "%s: AS4_AGGREGATOR attribute is invalid, skipping attribute", p->p.name);
- a4 = NULL;
- a4_removed = 1;
- }
-
- if (a2)
- {
- u32 a2_as = get_u16(a2->u.ptr->data);
-
- if (a4)
- {
- if (a2_as != AS_TRANS)
- {
- /* Routes were aggregated by old router and therefore AS4_PATH
- * and AS4_AGGREGATOR is invalid
- *
- * Convert AS_PATH and AGGREGATOR to 4B format and finish.
- */
-
- a2->u.ptr = bgp_aggregator_convert_to_new(a2->u.ptr, pool);
- p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, NULL, AS_PATH_MAXLEN, pool);
-
- return;
- }
- else
- {
- /* Common case, use AS4_AGGREGATOR attribute */
- a2->u.ptr = a4->u.ptr;
- }
- }
- else
- {
- /* Common case, use old AGGREGATOR attribute */
- a2->u.ptr = bgp_aggregator_convert_to_new(a2->u.ptr, pool);
-
- if ((a2_as == AS_TRANS) && !a4_removed)
- log(L_WARN "%s: AGGREGATOR attribute contain AS_TRANS, but AS4_AGGREGATOR is missing", p->p.name);
- }
- }
- else
- if (a4)
- log(L_WARN "%s: AS4_AGGREGATOR attribute received, but AGGREGATOR attribute is missing", p->p.name);
-
- int p2_len = as_path_getlen_int(p2->u.ptr, 2);
- int p4_len = p4 ? validate_as4_path(p, p4->u.ptr) : -1;
-
- if (p4 && (p4_len < 0))
- log(L_WARN "%s: AS4_PATH attribute is malformed, skipping attribute", p->p.name);
-
- if ((p4_len <= 0) || (p2_len < p4_len))
- p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, NULL, AS_PATH_MAXLEN, pool);
- else
- p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, p4->u.ptr, p2_len - p4_len, pool);
-}
-
static void
-bgp_remove_as4_attrs(struct bgp_proto *p, rta *a)
+bgp_process_as4_attrs(ea_list **attrs, struct linpool *pool)
{
- unsigned id1 = EA_CODE(EAP_BGP, BA_AS4_PATH);
- unsigned id2 = EA_CODE(EAP_BGP, BA_AS4_AGGREGATOR);
- ea_list **el = &(a->eattrs);
+ eattr *p2 = bgp_find_attr(*attrs, BA_AS_PATH);
+ eattr *p4 = bgp_find_attr(*attrs, BA_AS4_PATH);
+ eattr *a2 = bgp_find_attr(*attrs, BA_AGGREGATOR);
+ eattr *a4 = bgp_find_attr(*attrs, BA_AS4_AGGREGATOR);
- /* We know that ea_lists constructed in bgp_decode attrs have one attribute per ea_list struct */
- while (*el != NULL)
- {
- unsigned fid = (*el)->attrs[0].id;
-
- if ((fid == id1) || (fid == id2))
- {
- *el = (*el)->next;
- if (p->as4_session)
- log(L_WARN "%s: Unexpected AS4_* attributes received", p->p.name);
- }
- else
- el = &((*el)->next);
- }
-}
+ /* First, unset AS4_* attributes */
+ if (p4) bgp_unset_attr(attrs, pool, BA_AS4_PATH);
+ if (a4) bgp_unset_attr(attrs, pool, BA_AS4_AGGREGATOR);
-/**
- * bgp_decode_attrs - check and decode BGP attributes
- * @conn: connection
- * @attr: start of attribute block
- * @len: length of attribute block
- * @pool: linear pool to make all the allocations in
- * @mandatory: 1 iff presence of mandatory attributes has to be checked
- *
- * This function takes a BGP attribute block (a part of an Update message), checks
- * its consistency and converts it to a list of BIRD route attributes represented
- * by a &rta.
- */
-struct rta *
-bgp_decode_attrs(struct bgp_conn *conn, byte *attr, uint len, struct linpool *pool, int mandatory)
-{
- struct bgp_proto *bgp = conn->bgp;
- rta *a = lp_alloc(pool, sizeof(struct rta));
- uint flags, code, l, i, type;
- int errcode;
- byte *z, *attr_start;
- byte seen[256/8];
- ea_list *ea;
- struct adata *ad;
- int withdraw = 0;
-
- bzero(a, sizeof(rta));
- a->source = RTS_BGP;
- a->scope = SCOPE_UNIVERSE;
- a->cast = RTC_UNICAST;
- /* a->dest = RTD_ROUTER; -- set in bgp_set_next_hop() */
- a->from = bgp->cf->remote_ip;
-
- /* Parse the attributes */
- bzero(seen, sizeof(seen));
- DBG("BGP: Parsing attributes\n");
- while (len)
- {
- if (len < 2)
- goto malformed;
- attr_start = attr;
- flags = *attr++;
- code = *attr++;
- len -= 2;
- if (flags & BAF_EXT_LEN)
- {
- if (len < 2)
- goto malformed;
- l = get_u16(attr);
- attr += 2;
- len -= 2;
- }
- else
- {
- if (len < 1)
- goto malformed;
- l = *attr++;
- len--;
- }
- if (l > len)
- goto malformed;
- len -= l;
- z = attr;
- attr += l;
- DBG("Attr %02x %02x %d\n", code, flags, l);
- if (seen[code/8] & (1 << (code%8)))
- goto malformed;
- if (ATTR_KNOWN(code))
- {
- struct attr_desc *desc = &bgp_attr_table[code];
- if (desc->expected_length >= 0 && desc->expected_length != (int) l)
- { errcode = 5; goto err; }
- if ((desc->expected_flags ^ flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))
- { errcode = 4; goto err; }
- if (!bgp->is_internal)
- {
- if (!desc->allow_in_ebgp)
- continue;
- if ((code == BA_LOCAL_PREF) && !bgp->cf->allow_local_pref)
- continue;
- }
- if (desc->validate)
- {
- errcode = desc->validate(bgp, z, l);
- if (errcode > 0)
- goto err;
- if (errcode == IGNORE)
- continue;
- if (errcode <= WITHDRAW)
- {
- log(L_WARN "%s: Attribute %s is malformed, withdrawing update",
- bgp->p.name, desc->name);
- withdraw = 1;
- }
- }
- else if (code == BA_AS_PATH)
- {
- /* Special case as it might also trim the attribute */
- if (validate_as_path(bgp, z, &l) < 0)
- { errcode = 11; goto err; }
- }
- type = desc->type;
- }
- else /* Unknown attribute */
- {
- if (!(flags & BAF_OPTIONAL))
- { errcode = 2; goto err; }
- type = EAF_TYPE_OPAQUE;
- }
-
- // Only OPTIONAL and TRANSITIVE attributes may have non-zero PARTIAL flag
- // if (!((flags & BAF_OPTIONAL) && (flags & BAF_TRANSITIVE)) && (flags & BAF_PARTIAL))
- // { errcode = 4; goto err; }
-
- seen[code/8] |= (1 << (code%8));
- ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
- ea->next = a->eattrs;
- a->eattrs = ea;
- ea->flags = 0;
- ea->count = 1;
- ea->attrs[0].id = EA_CODE(EAP_BGP, code);
- ea->attrs[0].flags = flags;
- ea->attrs[0].type = type;
- if (type & EAF_EMBEDDED)
- ad = NULL;
- else
- {
- ad = lp_alloc(pool, sizeof(struct adata) + l);
- ea->attrs[0].u.ptr = ad;
- ad->length = l;
- memcpy(ad->data, z, l);
- }
- switch (type)
- {
- case EAF_TYPE_ROUTER_ID:
- case EAF_TYPE_INT:
- if (l == 1)
- ea->attrs[0].u.data = *z;
- else
- ea->attrs[0].u.data = get_u32(z);
- break;
- case EAF_TYPE_IP_ADDRESS:
- ipa_ntoh(*(ip_addr *)ad->data);
- break;
- case EAF_TYPE_INT_SET:
- case EAF_TYPE_LC_SET:
- case EAF_TYPE_EC_SET:
- {
- u32 *z = (u32 *) ad->data;
- for(i=0; i<ad->length/4; i++)
- z[i] = ntohl(z[i]);
- break;
- }
- }
- }
-
- if (withdraw)
- goto withdraw;
-
-#ifdef IPV6
- /* If we received MP_REACH_NLRI we should check mandatory attributes */
- if (bgp->mp_reach_len != 0)
- mandatory = 1;
-#endif
-
- /* If there is no (reachability) NLRI, we should exit now */
- if (! mandatory)
- return a;
-
- /* Check if all mandatory attributes are present */
- for(i=0; i < ARRAY_SIZE(bgp_mandatory_attrs); i++)
- {
- code = bgp_mandatory_attrs[i];
- if (!(seen[code/8] & (1 << (code%8))))
- {
- bgp_error(conn, 3, 3, &bgp_mandatory_attrs[i], 1);
- return NULL;
- }
- }
-
- /* When receiving attributes from non-AS4-aware BGP speaker,
- * we have to reconstruct 4B AS_PATH and AGGREGATOR attributes
- */
- if (! bgp->as4_session)
- bgp_reconstruct_4b_atts(bgp, a, pool);
-
- bgp_remove_as4_attrs(bgp, a);
-
- /* If the AS path attribute contains our AS, reject the routes */
- if (bgp_as_path_loopy(bgp, a))
- goto withdraw;
-
- /* Two checks for IBGP loops caused by route reflection, RFC 4456 */
- if (bgp_originator_id_loopy(bgp, a) ||
- bgp_cluster_list_loopy(bgp, a))
- goto withdraw;
+ /* Handle AGGREGATOR attribute */
+ if (a2 && a4)
+ {
+ u32 a2_asn = get_u32(a2->u.ptr->data);
- /* If there's no local preference, define one */
- if (!(seen[0] & (1 << BA_LOCAL_PREF)))
- bgp_attach_attr(&a->eattrs, pool, BA_LOCAL_PREF, bgp->cf->default_local_pref);
+ /* If routes were aggregated by an old router, then AS4_PATH and
+ AS4_AGGREGATOR are invalid. In that case we give up. */
+ if (a2_asn != AS_TRANS)
+ return;
- return a;
+ /* Use AS4_AGGREGATOR instead of AGGREGATOR */
+ a2->u.ptr = a4->u.ptr;
+ }
-withdraw:
- return NULL;
+ /* Handle AS_PATH attribute */
+ if (p2 && p4)
+ {
+ /* Both as_path_getlen() and as_path_cut() take AS_CONFED* as zero length */
+ int p2_len = as_path_getlen(p2->u.ptr);
+ int p4_len = as_path_getlen(p4->u.ptr);
-malformed:
- bgp_error(conn, 3, 1, NULL, 0);
- return NULL;
+ /* AS_PATH is too short, give up */
+ if (p2_len < p4_len)
+ return;
-err:
- bgp_error(conn, 3, errcode, attr_start, z+l-attr_start);
- return NULL;
+ /* Merge AS_PATH and AS4_PATH */
+ as_path_cut(p2->u.ptr, p2_len - p4_len);
+ p2->u.ptr = as_path_merge(pool, p2->u.ptr, p4->u.ptr);
+ }
}
int
bgp_get_attr(eattr *a, byte *buf, int buflen)
{
uint i = EA_ID(a->id);
- struct attr_desc *d;
+ const struct bgp_attr_desc *d;
int len;
- if (ATTR_KNOWN(i))
+ if (bgp_attr_known(i))
+ {
+ d = &bgp_attr_table[i];
+ len = bsprintf(buf, "%s", d->name);
+ buf += len;
+ if (d->format)
{
- d = &bgp_attr_table[i];
- len = bsprintf(buf, "%s", d->name);
- buf += len;
- if (d->format)
- {
- *buf++ = ':';
- *buf++ = ' ';
- d->format(a, buf, buflen - len - 2);
- return GA_FULL;
- }
- return GA_NAME;
+ *buf++ = ':';
+ *buf++ = ' ';
+ d->format(a, buf, buflen - len - 2);
+ return GA_FULL;
}
- bsprintf(buf, "%02x%s", i, (a->flags & BAF_TRANSITIVE) ? " [t]" : "");
- return GA_NAME;
-}
-
-void
-bgp_init_bucket_table(struct bgp_proto *p)
-{
- p->hash_size = 256;
- p->hash_limit = p->hash_size * 4;
- p->bucket_hash = mb_allocz(p->p.pool, p->hash_size * sizeof(struct bgp_bucket *));
- init_list(&p->bucket_queue);
- p->withdraw_bucket = NULL;
- // fib_init(&p->prefix_fib, p->p.pool, sizeof(struct bgp_prefix), 0, bgp_init_prefix);
-}
-
-void
-bgp_free_bucket_table(struct bgp_proto *p)
-{
- mb_free(p->bucket_hash);
- p->bucket_hash = NULL;
-
- struct bgp_bucket *b;
- WALK_LIST_FIRST(b, p->bucket_queue)
- {
- rem_node(&b->send_node);
- mb_free(b);
+ return GA_NAME;
}
- mb_free(p->withdraw_bucket);
- p->withdraw_bucket = NULL;
+ bsprintf(buf, "%02x%s", i, (a->flags & BAF_TRANSITIVE) ? " [t]" : "");
+ return GA_NAME;
}
void
@@ -1988,14 +2002,14 @@ bgp_get_route_info(rte *e, byte *buf, ea_list *attrs)
buf += bsprintf(buf, "-");
if (e->attrs->hostentry)
- {
- if (!rte_resolvable(e))
- buf += bsprintf(buf, "/-");
- else if (e->attrs->igp_metric >= IGP_METRIC_UNKNOWN)
- buf += bsprintf(buf, "/?");
- else
- buf += bsprintf(buf, "/%d", e->attrs->igp_metric);
- }
+ {
+ if (!rte_resolvable(e))
+ buf += bsprintf(buf, "/-");
+ else if (e->attrs->igp_metric >= IGP_METRIC_UNKNOWN)
+ buf += bsprintf(buf, "/?");
+ else
+ buf += bsprintf(buf, "/%d", e->attrs->igp_metric);
+ }
buf += bsprintf(buf, ") [");
if (p && as_path_get_last(p->u.ptr, &origas))
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index f706e76e..cccced57 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -2,6 +2,8 @@
* BIRD -- The Border Gateway Protocol
*
* (c) 2000 Martin Mares <mj@ucw.cz>
+ * (c) 2008--2016 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2008--2016 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@@ -9,48 +11,52 @@
/**
* DOC: Border Gateway Protocol
*
- * The BGP protocol is implemented in three parts: |bgp.c| which takes care of the
- * connection and most of the interface with BIRD core, |packets.c| handling
+ * The BGP protocol is implemented in three parts: |bgp.c| which takes care of
+ * the connection and most of the interface with BIRD core, |packets.c| handling
* both incoming and outgoing BGP packets and |attrs.c| containing functions for
* manipulation with BGP attribute lists.
*
- * As opposed to the other existing routing daemons, BIRD has a sophisticated core
- * architecture which is able to keep all the information needed by BGP in the
- * primary routing table, therefore no complex data structures like a central
- * BGP table are needed. This increases memory footprint of a BGP router with
- * many connections, but not too much and, which is more important, it makes
- * BGP much easier to implement.
+ * As opposed to the other existing routing daemons, BIRD has a sophisticated
+ * core architecture which is able to keep all the information needed by BGP in
+ * the primary routing table, therefore no complex data structures like a
+ * central BGP table are needed. This increases memory footprint of a BGP router
+ * with many connections, but not too much and, which is more important, it
+ * makes BGP much easier to implement.
*
- * Each instance of BGP (corresponding to a single BGP peer) is described by a &bgp_proto
- * structure to which are attached individual connections represented by &bgp_connection
- * (usually, there exists only one connection, but during BGP session setup, there
- * can be more of them). The connections are handled according to the BGP state machine
- * defined in the RFC with all the timers and all the parameters configurable.
+ * Each instance of BGP (corresponding to a single BGP peer) is described by a
+ * &bgp_proto structure to which are attached individual connections represented
+ * by &bgp_connection (usually, there exists only one connection, but during BGP
+ * session setup, there can be more of them). The connections are handled
+ * according to the BGP state machine defined in the RFC with all the timers and
+ * all the parameters configurable.
*
- * In incoming direction, we listen on the connection's socket and each time we receive
- * some input, we pass it to bgp_rx(). It decodes packet headers and the markers and
- * passes complete packets to bgp_rx_packet() which distributes the packet according
- * to its type.
+ * In incoming direction, we listen on the connection's socket and each time we
+ * receive some input, we pass it to bgp_rx(). It decodes packet headers and the
+ * markers and passes complete packets to bgp_rx_packet() which distributes the
+ * packet according to its type.
*
- * In outgoing direction, we gather all the routing updates and sort them to buckets
- * (&bgp_bucket) according to their attributes (we keep a hash table for fast comparison
- * of &rta's and a &fib which helps us to find if we already have another route for
- * the same destination queued for sending, so that we can replace it with the new one
- * immediately instead of sending both updates). There also exists a special bucket holding
- * all the route withdrawals which cannot be queued anywhere else as they don't have any
- * attributes. If we have any packet to send (due to either new routes or the connection
- * tracking code wanting to send a Open, Keepalive or Notification message), we call
- * bgp_schedule_packet() which sets the corresponding bit in a @packet_to_send
- * bit field in &bgp_conn and as soon as the transmit socket buffer becomes empty,
- * we call bgp_fire_tx(). It inspects state of all the packet type bits and calls
- * the corresponding bgp_create_xx() functions, eventually rescheduling the same packet
- * type if we have more data of the same type to send.
+ * In outgoing direction, we gather all the routing updates and sort them to
+ * buckets (&bgp_bucket) according to their attributes (we keep a hash table for
+ * fast comparison of &rta's and a &fib which helps us to find if we already
+ * have another route for the same destination queued for sending, so that we
+ * can replace it with the new one immediately instead of sending both
+ * updates). There also exists a special bucket holding all the route
+ * withdrawals which cannot be queued anywhere else as they don't have any
+ * attributes. If we have any packet to send (due to either new routes or the
+ * connection tracking code wanting to send a Open, Keepalive or Notification
+ * message), we call bgp_schedule_packet() which sets the corresponding bit in a
+ * @packet_to_send bit field in &bgp_conn and as soon as the transmit socket
+ * buffer becomes empty, we call bgp_fire_tx(). It inspects state of all the
+ * packet type bits and calls the corresponding bgp_create_xx() functions,
+ * eventually rescheduling the same packet type if we have more data of the same
+ * type to send.
*
- * The processing of attributes consists of two functions: bgp_decode_attrs() for checking
- * of the attribute blocks and translating them to the language of BIRD's extended attributes
- * and bgp_encode_attrs() which does the converse. Both functions are built around a
- * @bgp_attr_table array describing all important characteristics of all known attributes.
- * Unknown transitive attributes are attached to the route as %EAF_TYPE_OPAQUE byte streams.
+ * The processing of attributes consists of two functions: bgp_decode_attrs()
+ * for checking of the attribute blocks and translating them to the language of
+ * BIRD's extended attributes and bgp_encode_attrs() which does the
+ * converse. Both functions are built around a @bgp_attr_table array describing
+ * all important characteristics of all known attributes. Unknown transitive
+ * attributes are attached to the route as %EAF_TYPE_OPAQUE byte streams.
*
* BGP protocol implements graceful restart in both restarting (local restart)
* and receiving (neighbor restart) roles. The first is handled mostly by the
@@ -61,7 +67,39 @@
* point of view and therefore maintaining received routes. Routing table
* refresh cycle (rt_refresh_begin(), rt_refresh_end()) is used for removing
* stale routes after reestablishment of BGP session during graceful restart.
- */
+ *
+ * Supported standards:
+ * <itemize>
+ * <item> <rfc id="4271"> - Border Gateway Protocol 4 (BGP)
+ * <item> <rfc id="1997"> - BGP Communities Attribute
+ * <item> <rfc id="2385"> - Protection of BGP Sessions via TCP MD5 Signature
+ * <item> <rfc id="2545"> - Use of BGP Multiprotocol Extensions for IPv6
+ * <item> <rfc id="2918"> - Route Refresh Capability
+ * <item> <rfc id="3107"> - Carrying Label Information in BGP
+ * <item> <rfc id="4360"> - BGP Extended Communities Attribute
+ * <item> <rfc id="4364"> - BGP/MPLS IPv4 Virtual Private Networks
+ * <item> <rfc id="4456"> - BGP Route Reflection
+ * <item> <rfc id="4486"> - Subcodes for BGP Cease Notification Message
+ * <item> <rfc id="4659"> - BGP/MPLS IPv6 Virtual Private Networks
+ * <item> <rfc id="4724"> - Graceful Restart Mechanism for BGP
+ * <item> <rfc id="4760"> - Multiprotocol extensions for BGP
+ * <item> <rfc id="4798"> - Connecting IPv6 Islands over IPv4 MPLS
+ * <item> <rfc id="5065"> - AS confederations for BGP
+ * <item> <rfc id="5082"> - Generalized TTL Security Mechanism
+ * <item> <rfc id="5492"> - Capabilities Advertisement with BGP
+ * <item> <rfc id="5549"> - Advertising IPv4 NLRI with an IPv6 Next Hop
+ * <item> <rfc id="5575"> - Dissemination of Flow Specification Rules
+ * <item> <rfc id="5668"> - 4-Octet AS Specific BGP Extended Community
+ * <item> <rfc id="6286"> - AS-Wide Unique BGP Identifier
+ * <item> <rfc id="6608"> - Subcodes for BGP Finite State Machine Error
+ * <item> <rfc id="6793"> - BGP Support for 4-Octet AS Numbers
+ * <item> <rfc id="7313"> - Enhanced Route Refresh Capability for BGP
+ * <item> <rfc id="7606"> - Revised Error Handling for BGP UPDATE Messages
+ * <item> <rfc id="7911"> - Advertisement of Multiple Paths in BGP
+ * <item> <rfc id="7947"> - Internet Exchange BGP Route Server
+ * <item> <rfc id="8092"> - BGP Large Communities Attribute
+ * </itemize>
+*/
#undef LOCAL_DEBUG
@@ -80,70 +118,150 @@
struct linpool *bgp_linpool; /* Global temporary pool */
-static sock *bgp_listen_sk; /* Global listening socket */
-static int bgp_counter; /* Number of protocol instances using the listening socket */
+struct linpool *bgp_linpool2; /* Global temporary pool for bgp_rt_notify() */
+static list bgp_sockets; /* Global list of listening sockets */
+
-static void bgp_close(struct bgp_proto *p, int apply_md5);
static void bgp_connect(struct bgp_proto *p);
static void bgp_active(struct bgp_proto *p);
-static sock *bgp_setup_listen_sk(ip_addr addr, unsigned port, u32 flags);
static void bgp_update_bfd(struct bgp_proto *p, int use_bfd);
+static int bgp_incoming_connection(sock *sk, uint dummy UNUSED);
+static void bgp_listen_sock_err(sock *sk UNUSED, int err);
/**
* bgp_open - open a BGP instance
* @p: BGP instance
*
- * This function allocates and configures shared BGP resources.
- * Should be called as the last step during initialization
- * (when lock is acquired and neighbor is ready).
- * When error, state changed to PS_DOWN, -1 is returned and caller
- * should return immediately.
+ * This function allocates and configures shared BGP resources, mainly listening
+ * sockets. Should be called as the last step during initialization (when lock
+ * is acquired and neighbor is ready). When error, caller should change state to
+ * PS_DOWN and return immediately.
*/
static int
bgp_open(struct bgp_proto *p)
{
- struct config *cfg = p->cf->c.global;
- int errcode;
+ struct bgp_socket *bs = NULL;
+ struct iface *ifa = p->cf->strict_bind ? p->cf->iface : NULL;
+ ip_addr addr = p->cf->strict_bind ? p->cf->local_ip :
+ (ipa_is_ip4(p->cf->remote_ip) ? IPA_NONE4 : IPA_NONE6);
+ uint port = p->cf->local_port;
- if (!bgp_listen_sk)
- bgp_listen_sk = bgp_setup_listen_sk(cfg->listen_bgp_addr, cfg->listen_bgp_port, cfg->listen_bgp_flags);
+ /* FIXME: Add some global init? */
+ if (!bgp_linpool)
+ init_list(&bgp_sockets);
- if (!bgp_listen_sk)
+ /* We assume that cf->iface is defined iff cf->local_ip is link-local */
+
+ WALK_LIST(bs, bgp_sockets)
+ if (ipa_equal(bs->sk->saddr, addr) && (bs->sk->iface == ifa) && (bs->sk->sport == port))
{
- errcode = BEM_NO_SOCKET;
- goto err;
+ bs->uc++;
+ p->sock = bs;
+ return 0;
}
- if (!bgp_linpool)
- bgp_linpool = lp_new(&root_pool, 4080);
+ sock *sk = sk_new(proto_pool);
+ sk->type = SK_TCP_PASSIVE;
+ sk->ttl = 255;
+ sk->saddr = addr;
+ sk->sport = port;
+ sk->flags = 0;
+ sk->tos = IP_PREC_INTERNET_CONTROL;
+ sk->rbsize = BGP_RX_BUFFER_SIZE;
+ sk->tbsize = BGP_TX_BUFFER_SIZE;
+ sk->rx_hook = bgp_incoming_connection;
+ sk->err_hook = bgp_listen_sock_err;
+
+ if (sk_open(sk) < 0)
+ goto err;
- bgp_counter++;
+ bs = mb_allocz(proto_pool, sizeof(struct bgp_socket));
+ bs->sk = sk;
+ bs->uc = 1;
+ p->sock = bs;
- if (p->cf->password)
- if (sk_set_md5_auth(bgp_listen_sk, p->cf->source_addr, p->cf->remote_ip,
- p->cf->iface, p->cf->password, p->cf->setkey) < 0)
- {
- sk_log_error(bgp_listen_sk, p->p.name);
- bgp_close(p, 0);
- errcode = BEM_INVALID_MD5;
- goto err;
- }
+ add_tail(&bgp_sockets, &bs->n);
+
+ if (!bgp_linpool)
+ {
+ bgp_linpool = lp_new_default(proto_pool);
+ bgp_linpool2 = lp_new_default(proto_pool);
+ }
return 0;
err:
- p->p.disabled = 1;
- bgp_store_error(p, NULL, BE_MISC, errcode);
- proto_notify_state(&p->p, PS_DOWN);
+ sk_log_error(sk, p->p.name);
+ log(L_ERR "%s: Cannot open listening socket", p->p.name);
+ rfree(sk);
return -1;
}
+/**
+ * bgp_close - close a BGP instance
+ * @p: BGP instance
+ *
+ * This function frees and deconfigures shared BGP resources.
+ */
+static void
+bgp_close(struct bgp_proto *p)
+{
+ struct bgp_socket *bs = p->sock;
+
+ ASSERT(bs && bs->uc);
+
+ if (--bs->uc)
+ return;
+
+ rfree(bs->sk);
+ rem_node(&bs->n);
+ mb_free(bs);
+
+ if (!EMPTY_LIST(bgp_sockets))
+ return;
+
+ rfree(bgp_linpool);
+ bgp_linpool = NULL;
+
+ rfree(bgp_linpool2);
+ bgp_linpool2 = NULL;
+}
+
+static inline int
+bgp_setup_auth(struct bgp_proto *p, int enable)
+{
+ if (p->cf->password)
+ {
+ int rv = sk_set_md5_auth(p->sock->sk,
+ p->cf->local_ip, p->cf->remote_ip, p->cf->iface,
+ enable ? p->cf->password : NULL, p->cf->setkey);
+
+ if (rv < 0)
+ sk_log_error(p->sock->sk, p->p.name);
+
+ return rv;
+ }
+ else
+ return 0;
+}
+
+static inline struct bgp_channel *
+bgp_find_channel(struct bgp_proto *p, u32 afi)
+{
+ struct bgp_channel *c;
+ WALK_LIST(c, p->p.channels)
+ if (c->afi == afi)
+ return c;
+
+ return NULL;
+}
+
static void
bgp_startup(struct bgp_proto *p)
{
BGP_TRACE(D_EVENTS, "Started");
- p->start_state = p->cf->capabilities ? BSS_CONNECT : BSS_CONNECT_NOCAP;
+ p->start_state = BSS_CONNECT;
if (!p->cf->passive)
bgp_active(p);
@@ -159,50 +277,36 @@ bgp_startup_timeout(timer *t)
static void
bgp_initiate(struct bgp_proto *p)
{
- int rv = bgp_open(p);
- if (rv < 0)
- return;
+ int err_val;
+
+ if (bgp_open(p) < 0)
+ { err_val = BEM_NO_SOCKET; goto err1; }
+
+ if (bgp_setup_auth(p, 1) < 0)
+ { err_val = BEM_INVALID_MD5; goto err2; }
if (p->cf->bfd)
bgp_update_bfd(p, p->cf->bfd);
if (p->startup_delay)
- {
- p->start_state = BSS_DELAY;
- BGP_TRACE(D_EVENTS, "Startup delayed by %d seconds due to errors", p->startup_delay);
- bgp_start_timer(p->startup_timer, p->startup_delay);
- }
+ {
+ p->start_state = BSS_DELAY;
+ BGP_TRACE(D_EVENTS, "Startup delayed by %d seconds due to errors", p->startup_delay);
+ bgp_start_timer(p->startup_timer, p->startup_delay);
+ }
else
bgp_startup(p);
-}
-/**
- * bgp_close - close a BGP instance
- * @p: BGP instance
- * @apply_md5: 0 to disable unsetting MD5 auth
- *
- * This function frees and deconfigures shared BGP resources.
- * @apply_md5 is set to 0 when bgp_close is called as a cleanup
- * from failed bgp_open().
- */
-static void
-bgp_close(struct bgp_proto *p, int apply_md5)
-{
- ASSERT(bgp_counter);
- bgp_counter--;
+ return;
- if (p->cf->password && apply_md5)
- if (sk_set_md5_auth(bgp_listen_sk, p->cf->source_addr, p->cf->remote_ip,
- p->cf->iface, NULL, p->cf->setkey) < 0)
- sk_log_error(bgp_listen_sk, p->p.name);
+err2:
+ bgp_close(p);
+err1:
+ p->p.disabled = 1;
+ bgp_store_error(p, NULL, BE_MISC, err_val);
+ proto_notify_state(&p->p, PS_DOWN);
- if (!bgp_counter)
- {
- rfree(bgp_listen_sk);
- bgp_listen_sk = NULL;
- rfree(bgp_linpool);
- bgp_linpool = NULL;
- }
+ return;
}
/**
@@ -210,19 +314,19 @@ bgp_close(struct bgp_proto *p, int apply_md5)
* @t: timer
* @value: time to fire (0 to disable the timer)
*
- * This functions calls tm_start() on @t with time @value and the
- * amount of randomization suggested by the BGP standard. Please use
- * it for all BGP timers.
+ * This functions calls tm_start() on @t with time @value and the amount of
+ * randomization suggested by the BGP standard. Please use it for all BGP
+ * timers.
*/
void
bgp_start_timer(timer *t, int value)
{
if (value)
- {
- /* The randomization procedure is specified in RFC 1771: 9.2.3.3 */
- t->randomize = value / 4;
- tm_start(t, value - t->randomize);
- }
+ {
+ /* The randomization procedure is specified in RFC 1771: 9.2.3.3 */
+ t->randomize = value / 4;
+ tm_start(t, value - t->randomize);
+ }
else
tm_stop(t);
}
@@ -231,8 +335,8 @@ bgp_start_timer(timer *t, int value)
* bgp_close_conn - close a BGP connection
* @conn: connection to close
*
- * This function takes a connection described by the &bgp_conn structure,
- * closes its socket and frees all resources associated with it.
+ * This function takes a connection described by the &bgp_conn structure, closes
+ * its socket and frees all resources associated with it.
*/
void
bgp_close_conn(struct bgp_conn *conn)
@@ -241,16 +345,22 @@ bgp_close_conn(struct bgp_conn *conn)
DBG("BGP: Closing connection\n");
conn->packets_to_send = 0;
- rfree(conn->connect_retry_timer);
- conn->connect_retry_timer = NULL;
+ conn->channels_to_send = 0;
+ rfree(conn->connect_timer);
+ conn->connect_timer = NULL;
rfree(conn->keepalive_timer);
conn->keepalive_timer = NULL;
rfree(conn->hold_timer);
conn->hold_timer = NULL;
- rfree(conn->sk);
- conn->sk = NULL;
rfree(conn->tx_ev);
conn->tx_ev = NULL;
+ rfree(conn->sk);
+ conn->sk = NULL;
+
+ mb_free(conn->local_caps);
+ conn->local_caps = NULL;
+ mb_free(conn->remote_caps);
+ conn->remote_caps = NULL;
}
@@ -258,9 +368,9 @@ bgp_close_conn(struct bgp_conn *conn)
* bgp_update_startup_delay - update a startup delay
* @p: BGP instance
*
- * This function updates a startup delay that is used to postpone next BGP connect.
- * It also handles disable_after_error and might stop BGP instance when error
- * happened and disable_after_error is on.
+ * This function updates a startup delay that is used to postpone next BGP
+ * connect. It also handles disable_after_error and might stop BGP instance
+ * when error happened and disable_after_error is on.
*
* It should be called when BGP protocol error happened.
*/
@@ -277,11 +387,11 @@ bgp_update_startup_delay(struct bgp_proto *p)
p->last_proto_error = now;
if (cf->disable_after_error)
- {
- p->startup_delay = 0;
- p->p.disabled = 1;
- return;
- }
+ {
+ p->startup_delay = 0;
+ p->p.disabled = 1;
+ return;
+ }
if (!p->startup_delay)
p->startup_delay = cf->error_delay_time_min;
@@ -290,32 +400,38 @@ bgp_update_startup_delay(struct bgp_proto *p)
}
static void
-bgp_graceful_close_conn(struct bgp_conn *conn, unsigned subcode)
+bgp_graceful_close_conn(struct bgp_conn *conn, uint subcode)
{
switch (conn->state)
- {
- case BS_IDLE:
- case BS_CLOSE:
- return;
- case BS_CONNECT:
- case BS_ACTIVE:
- bgp_conn_enter_idle_state(conn);
- return;
- case BS_OPENSENT:
- case BS_OPENCONFIRM:
- case BS_ESTABLISHED:
- bgp_error(conn, 6, subcode, NULL, 0);
- return;
- default:
- bug("bgp_graceful_close_conn: Unknown state %d", conn->state);
- }
+ {
+ case BS_IDLE:
+ case BS_CLOSE:
+ return;
+
+ case BS_CONNECT:
+ case BS_ACTIVE:
+ bgp_conn_enter_idle_state(conn);
+ return;
+
+ case BS_OPENSENT:
+ case BS_OPENCONFIRM:
+ case BS_ESTABLISHED:
+ bgp_error(conn, 6, subcode, NULL, 0);
+ return;
+
+ default:
+ bug("bgp_graceful_close_conn: Unknown state %d", conn->state);
+ }
}
static void
bgp_down(struct bgp_proto *p)
{
if (p->start_state > BSS_PREPARE)
- bgp_close(p, 1);
+ {
+ bgp_setup_auth(p, 0);
+ bgp_close(p);
+ }
BGP_TRACE(D_EVENTS, "Down");
proto_notify_state(&p->p, PS_DOWN);
@@ -327,20 +443,20 @@ bgp_decision(void *vp)
struct bgp_proto *p = vp;
DBG("BGP: Decision start\n");
- if ((p->p.proto_state == PS_START)
- && (p->outgoing_conn.state == BS_IDLE)
- && (p->incoming_conn.state != BS_OPENCONFIRM)
- && (!p->cf->passive))
+ if ((p->p.proto_state == PS_START) &&
+ (p->outgoing_conn.state == BS_IDLE) &&
+ (p->incoming_conn.state != BS_OPENCONFIRM) &&
+ !p->cf->passive)
bgp_active(p);
- if ((p->p.proto_state == PS_STOP)
- && (p->outgoing_conn.state == BS_IDLE)
- && (p->incoming_conn.state == BS_IDLE))
+ if ((p->p.proto_state == PS_STOP) &&
+ (p->outgoing_conn.state == BS_IDLE) &&
+ (p->incoming_conn.state == BS_IDLE))
bgp_down(p);
}
void
-bgp_stop(struct bgp_proto *p, unsigned subcode)
+bgp_stop(struct bgp_proto *p, uint subcode)
{
proto_notify_state(&p->p, PS_STOP);
bgp_graceful_close_conn(&p->outgoing_conn, subcode);
@@ -349,7 +465,7 @@ bgp_stop(struct bgp_proto *p, unsigned subcode)
}
static inline void
-bgp_conn_set_state(struct bgp_conn *conn, unsigned new_state)
+bgp_conn_set_state(struct bgp_conn *conn, uint new_state)
{
if (conn->bgp->p.mrtdump & MD_STATES)
mrt_dump_bgp_state_change(conn, conn->state, new_state);
@@ -364,13 +480,17 @@ bgp_conn_enter_openconfirm_state(struct bgp_conn *conn)
bgp_conn_set_state(conn, BS_OPENCONFIRM);
}
+static const struct bgp_af_caps dummy_af_caps = { };
+
void
bgp_conn_enter_established_state(struct bgp_conn *conn)
{
struct bgp_proto *p = conn->bgp;
+ struct bgp_caps *local = conn->local_caps;
+ struct bgp_caps *peer = conn->remote_caps;
+ struct bgp_channel *c;
BGP_TRACE(D_EVENTS, "BGP session established");
- DBG("BGP: UP!!!\n");
/* For multi-hop BGP sessions */
if (ipa_zero(p->source_addr))
@@ -381,30 +501,92 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
p->conn = conn;
p->last_error_class = 0;
p->last_error_code = 0;
- p->feed_state = BFS_NONE;
- p->load_state = BFS_NONE;
- bgp_init_bucket_table(p);
- bgp_init_prefix_table(p, 8);
- int peer_gr_ready = conn->peer_gr_aware && !(conn->peer_gr_flags & BGP_GRF_RESTART);
+ p->as4_session = conn->as4_session;
- if (p->p.gr_recovery && !peer_gr_ready)
- proto_graceful_restart_unlock(&p->p);
+ p->route_refresh = peer->route_refresh;
+ p->enhanced_refresh = local->enhanced_refresh && peer->enhanced_refresh;
- if (p->p.gr_recovery && (p->cf->gr_mode == BGP_GR_ABLE) && peer_gr_ready)
- p->p.gr_wait = 1;
+ /* Whether we may handle possible GR of peer (it has some AF GR-able) */
+ p->gr_ready = 0; /* Updated later */
- if (p->gr_active)
+ /* Whether peer is ready to handle our GR recovery */
+ int peer_gr_ready = peer->gr_aware && !(peer->gr_flags & BGP_GRF_RESTART);
+
+ if (p->gr_active_num)
tm_stop(p->gr_timer);
- if (p->gr_active && (!conn->peer_gr_able || !(conn->peer_gr_aflags & BGP_GRF_FORWARDING)))
- bgp_graceful_restart_done(p);
+ /* Number of active channels */
+ int num = 0;
+
+ WALK_LIST(c, p->p.channels)
+ {
+ const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi);
+ const struct bgp_af_caps *rem = bgp_find_af_caps(peer, c->afi);
+
+ /* Ignore AFIs that were not announced in multiprotocol capability */
+ if (!loc || !loc->ready)
+ loc = &dummy_af_caps;
+
+ if (!rem || !rem->ready)
+ rem = &dummy_af_caps;
+
+ int active = loc->ready && rem->ready;
+ c->c.disabled = !active;
+ c->c.reloadable = p->route_refresh;
+
+ c->index = active ? num++ : 0;
+
+ c->feed_state = BFS_NONE;
+ c->load_state = BFS_NONE;
+
+ /* Channels where peer may do GR */
+ c->gr_ready = active && local->gr_aware && rem->gr_able;
+ p->gr_ready = p->gr_ready || c->gr_ready;
+
+ /* Channels not able to recover gracefully */
+ if (p->p.gr_recovery && (!active || !peer_gr_ready))
+ channel_graceful_restart_unlock(&c->c);
+
+ /* Channels waiting for local convergence */
+ if (p->p.gr_recovery && loc->gr_able && peer_gr_ready)
+ c->c.gr_wait = 1;
+
+ /* Channels where peer is not able to recover gracefully */
+ if (c->gr_active && ! (c->gr_ready && (rem->gr_af_flags & BGP_GRF_FORWARDING)))
+ bgp_graceful_restart_done(c);
+
+ /* GR capability implies that neighbor will send End-of-RIB */
+ if (peer->gr_aware)
+ c->load_state = BFS_LOADING;
+
+ c->ext_next_hop = c->cf->ext_next_hop && (bgp_channel_is_ipv6(c) || rem->ext_next_hop);
+ c->add_path_rx = (loc->add_path & BGP_ADD_PATH_RX) && (rem->add_path & BGP_ADD_PATH_TX);
+ c->add_path_tx = (loc->add_path & BGP_ADD_PATH_TX) && (rem->add_path & BGP_ADD_PATH_RX);
- /* GR capability implies that neighbor will send End-of-RIB */
- if (conn->peer_gr_aware)
- p->load_state = BFS_LOADING;
+ /* Update RA mode */
+ if (c->add_path_tx)
+ c->c.ra_mode = RA_ANY;
+ else if (c->cf->secondary)
+ c->c.ra_mode = RA_ACCEPTED;
+ else
+ c->c.ra_mode = RA_OPTIMAL;
+ }
+
+ p->afi_map = mb_alloc(p->p.pool, num * sizeof(u32));
+ p->channel_map = mb_alloc(p->p.pool, num * sizeof(void *));
+ p->channel_count = num;
- /* proto_notify_state() will likely call bgp_feed_begin(), setting p->feed_state */
+ WALK_LIST(c, p->p.channels)
+ {
+ if (c->c.disabled)
+ continue;
+
+ p->afi_map[c->index] = c->afi;
+ p->channel_map[c->index] = c;
+ }
+
+ /* proto_notify_state() will likely call bgp_feed_begin(), setting c->feed_state */
bgp_conn_set_state(conn, BS_ESTABLISHED);
proto_notify_state(&p->p, PS_UP);
@@ -416,8 +598,9 @@ bgp_conn_leave_established_state(struct bgp_proto *p)
BGP_TRACE(D_EVENTS, "BGP session closed");
p->conn = NULL;
- bgp_free_prefix_table(p);
- bgp_free_bucket_table(p);
+ // XXXX free these tables to avoid memory leak during graceful restart
+ // bgp_free_prefix_table(p);
+ // bgp_free_bucket_table(p);
if (p->p.proto_state == PS_UP)
bgp_stop(p, 0);
@@ -471,34 +654,57 @@ bgp_handle_graceful_restart(struct bgp_proto *p)
ASSERT(p->conn && (p->conn->state == BS_ESTABLISHED) && p->gr_ready);
BGP_TRACE(D_EVENTS, "Neighbor graceful restart detected%s",
- p->gr_active ? " - already pending" : "");
- proto_notify_state(&p->p, PS_START);
+ p->gr_active_num ? " - already pending" : "");
- if (p->gr_active)
- rt_refresh_end(p->p.main_ahook->table, p->p.main_ahook);
+ p->gr_active_num = 0;
- p->gr_active = 1;
- bgp_start_timer(p->gr_timer, p->conn->peer_gr_time);
- rt_refresh_begin(p->p.main_ahook->table, p->p.main_ahook);
+ struct bgp_channel *c;
+ WALK_LIST(c, p->p.channels)
+ {
+ if (c->gr_ready)
+ {
+ if (c->gr_active)
+ rt_refresh_end(c->c.table, &c->c);
+
+ c->gr_active = 1;
+ p->gr_active_num++;
+ rt_refresh_begin(c->c.table, &c->c);
+ }
+ else
+ {
+ /* Just flush the routes */
+ rt_refresh_begin(c->c.table, &c->c);
+ rt_refresh_end(c->c.table, &c->c);
+ }
+ }
+
+ proto_notify_state(&p->p, PS_START);
+ bgp_start_timer(p->gr_timer, p->conn->local_caps->gr_time);
}
/**
* bgp_graceful_restart_done - finish active BGP graceful restart
- * @p: BGP instance
+ * @c: BGP channel
*
* This function is called when the active BGP graceful restart of the neighbor
- * should be finished - either successfully (the neighbor sends all paths and
- * reports end-of-RIB on the new session) or unsuccessfully (the neighbor does
- * not support BGP graceful restart on the new session). The function ends
- * routing table refresh cycle and stops BGP restart timer.
+ * should be finished for channel @c - either successfully (the neighbor sends
+ * all paths and reports end-of-RIB for given AFI/SAFI on the new session) or
+ * unsuccessfully (the neighbor does not support BGP graceful restart on the new
+ * session). The function ends the routing table refresh cycle.
*/
void
-bgp_graceful_restart_done(struct bgp_proto *p)
+bgp_graceful_restart_done(struct bgp_channel *c)
{
- BGP_TRACE(D_EVENTS, "Neighbor graceful restart done");
- p->gr_active = 0;
- tm_stop(p->gr_timer);
- rt_refresh_end(p->p.main_ahook->table, p->p.main_ahook);
+ struct bgp_proto *p = (void *) c->c.proto;
+
+ ASSERT(c->gr_active);
+ c->gr_active = 0;
+ p->gr_active_num--;
+
+ if (!p->gr_active_num)
+ BGP_TRACE(D_EVENTS, "Neighbor graceful restart done");
+
+ rt_refresh_end(c->c.table, &c->c);
}
/**
@@ -522,7 +728,7 @@ bgp_graceful_restart_timeout(timer *t)
/**
* bgp_refresh_begin - start incoming enhanced route refresh sequence
- * @p: BGP instance
+ * @c: BGP channel
*
* This function is called when an incoming enhanced route refresh sequence is
* started by the neighbor, demarcated by the BoRR packet. The function updates
@@ -531,18 +737,20 @@ bgp_graceful_restart_timeout(timer *t)
* ensure that these two sequences do not overlap.
*/
void
-bgp_refresh_begin(struct bgp_proto *p)
+bgp_refresh_begin(struct bgp_channel *c)
{
- if (p->load_state == BFS_LOADING)
- { log(L_WARN "%s: BEGIN-OF-RR received before END-OF-RIB, ignoring", p->p.name); return; }
+ struct bgp_proto *p = (void *) c->c.proto;
- p->load_state = BFS_REFRESHING;
- rt_refresh_begin(p->p.main_ahook->table, p->p.main_ahook);
+ if (c->load_state == BFS_LOADING)
+ { log(L_WARN "%s: BEGIN-OF-RR received before END-OF-RIB, ignoring", p->p.name); return; }
+
+ c->load_state = BFS_REFRESHING;
+ rt_refresh_begin(c->c.table, &c->c);
}
/**
* bgp_refresh_end - finish incoming enhanced route refresh sequence
- * @p: BGP instance
+ * @c: BGP channel
*
* This function is called when an incoming enhanced route refresh sequence is
* finished by the neighbor, demarcated by the EoRR packet. The function updates
@@ -550,39 +758,26 @@ bgp_refresh_begin(struct bgp_proto *p)
* during the sequence are removed by the nest.
*/
void
-bgp_refresh_end(struct bgp_proto *p)
+bgp_refresh_end(struct bgp_channel *c)
{
- if (p->load_state != BFS_REFRESHING)
- { log(L_WARN "%s: END-OF-RR received without prior BEGIN-OF-RR, ignoring", p->p.name); return; }
+ struct bgp_proto *p = (void *) c->c.proto;
- p->load_state = BFS_NONE;
- rt_refresh_end(p->p.main_ahook->table, p->p.main_ahook);
+ if (c->load_state != BFS_REFRESHING)
+ { log(L_WARN "%s: END-OF-RR received without prior BEGIN-OF-RR, ignoring", p->p.name); return; }
+
+ c->load_state = BFS_NONE;
+ rt_refresh_end(c->c.table, &c->c);
}
static void
bgp_send_open(struct bgp_conn *conn)
{
- conn->start_state = conn->bgp->start_state;
-
- // Default values, possibly changed by receiving capabilities.
- conn->advertised_as = 0;
- conn->peer_refresh_support = 0;
- conn->peer_as4_support = 0;
- conn->peer_add_path = 0;
- conn->peer_enhanced_refresh_support = 0;
- conn->peer_gr_aware = 0;
- conn->peer_gr_able = 0;
- conn->peer_gr_time = 0;
- conn->peer_gr_flags = 0;
- conn->peer_gr_aflags = 0;
- conn->peer_ext_messages_support = 0;
-
DBG("BGP: Sending open\n");
conn->sk->rx_hook = bgp_rx;
conn->sk->tx_hook = bgp_tx;
- tm_stop(conn->connect_retry_timer);
- bgp_schedule_packet(conn, PKT_OPEN);
+ tm_stop(conn->connect_timer);
+ bgp_schedule_packet(conn, NULL, PKT_OPEN);
bgp_conn_set_state(conn, BS_OPENSENT);
bgp_start_timer(conn->hold_timer, conn->bgp->cf->initial_hold_time);
}
@@ -605,10 +800,10 @@ bgp_connect_timeout(timer *t)
DBG("BGP: connect_timeout\n");
if (p->p.proto_state == PS_START)
- {
- bgp_close_conn(conn);
- bgp_connect(p);
- }
+ {
+ bgp_close_conn(conn);
+ bgp_connect(p);
+ }
else
bgp_conn_enter_idle_state(conn);
}
@@ -672,7 +867,7 @@ bgp_keepalive_timeout(timer *t)
struct bgp_conn *conn = t->data;
DBG("BGP: Keepalive timer\n");
- bgp_schedule_packet(conn, PKT_KEEPALIVE);
+ bgp_schedule_packet(conn, NULL, PKT_KEEPALIVE);
/* Kick TX a bit faster */
if (ev_active(conn->tx_ev))
@@ -682,21 +877,18 @@ bgp_keepalive_timeout(timer *t)
static void
bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn)
{
- timer *t;
-
conn->sk = NULL;
conn->bgp = p;
+
conn->packets_to_send = 0;
+ conn->channels_to_send = 0;
+ conn->last_channel = 0;
+ conn->last_channel_count = 0;
+
+ conn->connect_timer = tm_new_set(p->p.pool, bgp_connect_timeout, conn, 0, 0);
+ conn->hold_timer = tm_new_set(p->p.pool, bgp_hold_timeout, conn, 0, 0);
+ conn->keepalive_timer = tm_new_set(p->p.pool, bgp_keepalive_timeout, conn, 0, 0);
- t = conn->connect_retry_timer = tm_new(p->p.pool);
- t->hook = bgp_connect_timeout;
- t->data = conn;
- t = conn->hold_timer = tm_new(p->p.pool);
- t->hook = bgp_hold_timeout;
- t->data = conn;
- t = conn->keepalive_timer = tm_new(p->p.pool);
- t->hook = bgp_keepalive_timeout;
- t->data = conn;
conn->tx_ev = ev_new(p->p.pool);
conn->tx_ev->hook = bgp_kick_tx;
conn->tx_ev->data = conn;
@@ -720,7 +912,7 @@ bgp_active(struct bgp_proto *p)
BGP_TRACE(D_EVENTS, "Connect delayed by %d seconds", delay);
bgp_setup_conn(p, conn);
bgp_conn_set_state(conn, BS_ACTIVE);
- bgp_start_timer(conn->connect_retry_timer, delay);
+ bgp_start_timer(conn->connect_timer, delay);
}
/**
@@ -734,12 +926,11 @@ bgp_active(struct bgp_proto *p)
static void
bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing connection */
{
- sock *s;
struct bgp_conn *conn = &p->outgoing_conn;
int hops = p->cf->multihop ? : 1;
DBG("BGP: Connecting\n");
- s = sk_new(p->p.pool);
+ sock *s = sk_new(p->p.pool);
s->type = SK_TCP_ACTIVE;
s->saddr = p->source_addr;
s->daddr = p->cf->remote_ip;
@@ -766,10 +957,10 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c
goto err;
DBG("BGP: Waiting for connect success\n");
- bgp_start_timer(conn->connect_retry_timer, p->cf->connect_retry_time);
+ bgp_start_timer(conn->connect_timer, p->cf->connect_retry_time);
return;
- err:
+err:
sk_log_error(s, p->p.name);
bgp_sock_err(s, 0);
return;
@@ -783,16 +974,15 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c
static struct bgp_proto *
bgp_find_proto(sock *sk)
{
- struct proto_config *pc;
+ struct bgp_proto *p;
- WALK_LIST(pc, config->protos)
- if ((pc->protocol == &proto_bgp) && pc->proto)
- {
- struct bgp_proto *p = (struct bgp_proto *) pc->proto;
- if (ipa_equal(p->cf->remote_ip, sk->daddr) &&
- (!p->cf->iface || (p->cf->iface == sk->iface)))
- return p;
- }
+ WALK_LIST(p, proto_list)
+ if ((p->p.proto == &proto_bgp) &&
+ ipa_equal(p->cf->remote_ip, sk->daddr) &&
+ (!p->cf->iface || (p->cf->iface == sk->iface)) &&
+ (ipa_zero(p->cf->local_ip) || ipa_equal(p->cf->local_ip, sk->saddr)) &&
+ (p->cf->local_port == sk->sport))
+ return p;
return NULL;
}
@@ -818,12 +1008,12 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
DBG("BGP: Incoming connection from %I port %d\n", sk->daddr, sk->dport);
p = bgp_find_proto(sk);
if (!p)
- {
- log(L_WARN "BGP: Unexpected connect from unknown address %I%J (port %d)",
- sk->daddr, ipa_is_link_local(sk->daddr) ? sk->iface : NULL, sk->dport);
- rfree(sk);
- return 0;
- }
+ {
+ log(L_WARN "BGP: Unexpected connect from unknown address %I%J (port %d)",
+ sk->daddr, ipa_is_link_local(sk->daddr) ? sk->iface : NULL, sk->dport);
+ rfree(sk);
+ return 0;
+ }
/*
* BIRD should keep multiple incoming connections in OpenSent state (for
@@ -836,26 +1026,26 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
(p->start_state >= BSS_CONNECT) && (!p->incoming_conn.sk);
if (p->conn && (p->conn->state == BS_ESTABLISHED) && p->gr_ready)
- {
- bgp_store_error(p, NULL, BE_MISC, BEM_GRACEFUL_RESTART);
- bgp_handle_graceful_restart(p);
- bgp_conn_enter_idle_state(p->conn);
- acc = 1;
-
- /* There might be separate incoming connection in OpenSent state */
- if (p->incoming_conn.state > BS_ACTIVE)
- bgp_close_conn(&p->incoming_conn);
- }
+ {
+ bgp_store_error(p, NULL, BE_MISC, BEM_GRACEFUL_RESTART);
+ bgp_handle_graceful_restart(p);
+ bgp_conn_enter_idle_state(p->conn);
+ acc = 1;
+
+ /* There might be separate incoming connection in OpenSent state */
+ if (p->incoming_conn.state > BS_ACTIVE)
+ bgp_close_conn(&p->incoming_conn);
+ }
BGP_TRACE(D_EVENTS, "Incoming connection from %I%J (port %d) %s",
sk->daddr, ipa_is_link_local(sk->daddr) ? sk->iface : NULL,
sk->dport, acc ? "accepted" : "rejected");
if (!acc)
- {
- rfree(sk);
- return 0;
- }
+ {
+ rfree(sk);
+ return 0;
+ }
hops = p->cf->multihop ? : 1;
@@ -867,11 +1057,11 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
goto err;
if (p->cf->enable_extended_messages)
- {
- sk->rbsize = BGP_RX_BUFFER_EXT_SIZE;
- sk->tbsize = BGP_TX_BUFFER_EXT_SIZE;
- sk_reallocate(sk);
- }
+ {
+ sk->rbsize = BGP_RX_BUFFER_EXT_SIZE;
+ sk->tbsize = BGP_TX_BUFFER_EXT_SIZE;
+ sk_reallocate(sk);
+ }
bgp_setup_conn(p, &p->incoming_conn);
bgp_setup_sk(&p->incoming_conn, sk);
@@ -894,34 +1084,6 @@ bgp_listen_sock_err(sock *sk UNUSED, int err)
log(L_ERR "BGP: Error on listening socket: %M", err);
}
-static sock *
-bgp_setup_listen_sk(ip_addr addr, unsigned port, u32 flags)
-{
- sock *s = sk_new(&root_pool);
- DBG("BGP: Creating listening socket\n");
- s->type = SK_TCP_PASSIVE;
- s->ttl = 255;
- s->saddr = addr;
- s->sport = port ? port : BGP_PORT;
- s->flags = flags ? 0 : SKF_V6ONLY;
- s->tos = IP_PREC_INTERNET_CONTROL;
- s->rbsize = BGP_RX_BUFFER_SIZE;
- s->tbsize = BGP_TX_BUFFER_SIZE;
- s->rx_hook = bgp_incoming_connection;
- s->err_hook = bgp_listen_sock_err;
-
- if (sk_open(s) < 0)
- goto err;
-
- return s;
-
- err:
- sk_log_error(s, "BGP");
- log(L_ERR "BGP: Cannot open listening socket");
- rfree(s);
- return NULL;
-}
-
static void
bgp_start_neighbor(struct bgp_proto *p)
{
@@ -930,23 +1092,21 @@ bgp_start_neighbor(struct bgp_proto *p)
if (ipa_zero(p->source_addr))
p->source_addr = p->neigh->ifa->ip;
-#ifdef IPV6
+ if (ipa_is_link_local(p->source_addr))
+ p->link_addr = p->source_addr;
+ else
{
+ /* Find some link-local address for given iface */
struct ifa *a;
- p->local_link = IPA_NONE;
WALK_LIST(a, p->neigh->iface->addrs)
if (a->scope == SCOPE_LINK)
- {
- p->local_link = a->ip;
- break;
- }
-
- if (! ipa_nonzero(p->local_link))
- log(L_WARN "%s: Missing link local address on interface %s", p->p.name, p->neigh->iface->name);
+ {
+ p->link_addr = a->ip;
+ break;
+ }
- DBG("BGP: Selected link-level address %I\n", p->local_link);
+ DBG("%s: Selected link-local address %I\n", p->p.name, p->link_addr);
}
-#endif
bgp_initiate(p);
}
@@ -966,34 +1126,34 @@ bgp_neigh_notify(neighbor *n)
int prepare = (ps == PS_START) && (p->start_state == BSS_PREPARE);
if (n->scope <= 0)
+ {
+ if (!prepare)
{
- if (!prepare)
- {
- BGP_TRACE(D_EVENTS, "Neighbor lost");
- bgp_store_error(p, NULL, BE_MISC, BEM_NEIGHBOR_LOST);
- /* Perhaps also run bgp_update_startup_delay(p)? */
- bgp_stop(p, 0);
- }
+ BGP_TRACE(D_EVENTS, "Neighbor lost");
+ bgp_store_error(p, NULL, BE_MISC, BEM_NEIGHBOR_LOST);
+ /* Perhaps also run bgp_update_startup_delay(p)? */
+ bgp_stop(p, 0);
}
+ }
else if (p->cf->check_link && !(n->iface->flags & IF_LINK_UP))
+ {
+ if (!prepare)
{
- if (!prepare)
- {
- BGP_TRACE(D_EVENTS, "Link down");
- bgp_store_error(p, NULL, BE_MISC, BEM_LINK_DOWN);
- if (ps == PS_UP)
- bgp_update_startup_delay(p);
- bgp_stop(p, 0);
- }
+ BGP_TRACE(D_EVENTS, "Link down");
+ bgp_store_error(p, NULL, BE_MISC, BEM_LINK_DOWN);
+ if (ps == PS_UP)
+ bgp_update_startup_delay(p);
+ bgp_stop(p, 0);
}
+ }
else
+ {
+ if (prepare)
{
- if (prepare)
- {
- BGP_TRACE(D_EVENTS, "Neighbor ready");
- bgp_start_neighbor(p);
- }
+ BGP_TRACE(D_EVENTS, "Neighbor ready");
+ bgp_start_neighbor(p);
}
+ }
}
static void
@@ -1003,13 +1163,13 @@ bgp_bfd_notify(struct bfd_request *req)
int ps = p->p.proto_state;
if (req->down && ((ps == PS_START) || (ps == PS_UP)))
- {
- BGP_TRACE(D_EVENTS, "BFD session down");
- bgp_store_error(p, NULL, BE_MISC, BEM_BFD_DOWN);
- if (ps == PS_UP)
- bgp_update_startup_delay(p);
- bgp_stop(p, 0);
- }
+ {
+ BGP_TRACE(D_EVENTS, "BFD session down");
+ bgp_store_error(p, NULL, BE_MISC, BEM_BFD_DOWN);
+ if (ps == PS_UP)
+ bgp_update_startup_delay(p);
+ bgp_stop(p, 0);
+ }
}
static void
@@ -1021,71 +1181,72 @@ bgp_update_bfd(struct bgp_proto *p, int use_bfd)
bgp_bfd_notify, p);
if (!use_bfd && p->bfd_req)
- {
- rfree(p->bfd_req);
- p->bfd_req = NULL;
- }
+ {
+ rfree(p->bfd_req);
+ p->bfd_req = NULL;
+ }
}
-static int
-bgp_reload_routes(struct proto *P)
+static void
+bgp_reload_routes(struct channel *C)
{
- struct bgp_proto *p = (struct bgp_proto *) P;
- if (!p->conn || !p->conn->peer_refresh_support)
- return 0;
+ struct bgp_proto *p = (void *) C->proto;
+ struct bgp_channel *c = (void *) C;
- bgp_schedule_packet(p->conn, PKT_ROUTE_REFRESH);
- return 1;
+ ASSERT(p->conn && p->route_refresh);
+
+ bgp_schedule_packet(p->conn, c, PKT_ROUTE_REFRESH);
}
static void
-bgp_feed_begin(struct proto *P, int initial)
+bgp_feed_begin(struct channel *C, int initial)
{
- struct bgp_proto *p = (struct bgp_proto *) P;
+ struct bgp_proto *p = (void *) C->proto;
+ struct bgp_channel *c = (void *) C;
/* This should not happen */
if (!p->conn)
return;
if (initial && p->cf->gr_mode)
- p->feed_state = BFS_LOADING;
+ c->feed_state = BFS_LOADING;
/* It is refeed and both sides support enhanced route refresh */
- if (!initial && p->cf->enable_refresh &&
- p->conn->peer_enhanced_refresh_support)
- {
- /* BoRR must not be sent before End-of-RIB */
- if (p->feed_state == BFS_LOADING || p->feed_state == BFS_LOADED)
- return;
+ if (!initial && p->enhanced_refresh)
+ {
+ /* BoRR must not be sent before End-of-RIB */
+ if (c->feed_state == BFS_LOADING || c->feed_state == BFS_LOADED)
+ return;
- p->feed_state = BFS_REFRESHING;
- bgp_schedule_packet(p->conn, PKT_BEGIN_REFRESH);
- }
+ c->feed_state = BFS_REFRESHING;
+ bgp_schedule_packet(p->conn, c, PKT_BEGIN_REFRESH);
+ }
}
static void
-bgp_feed_end(struct proto *P)
+bgp_feed_end(struct channel *C)
{
- struct bgp_proto *p = (struct bgp_proto *) P;
+ struct bgp_proto *p = (void *) C->proto;
+ struct bgp_channel *c = (void *) C;
/* This should not happen */
if (!p->conn)
return;
/* Non-demarcated feed ended, nothing to do */
- if (p->feed_state == BFS_NONE)
+ if (c->feed_state == BFS_NONE)
return;
/* Schedule End-of-RIB packet */
- if (p->feed_state == BFS_LOADING)
- p->feed_state = BFS_LOADED;
+ if (c->feed_state == BFS_LOADING)
+ c->feed_state = BFS_LOADED;
/* Schedule EoRR packet */
- if (p->feed_state == BFS_REFRESHING)
- p->feed_state = BFS_REFRESHED;
+ if (c->feed_state == BFS_REFRESHING)
+ c->feed_state = BFS_REFRESHED;
/* Kick TX hook */
- bgp_schedule_packet(p->conn, PKT_UPDATE);
+ bgp_schedule_packet(p->conn, c, PKT_UPDATE);
}
@@ -1096,30 +1257,30 @@ bgp_start_locked(struct object_lock *lock)
struct bgp_config *cf = p->cf;
if (p->p.proto_state != PS_START)
- {
- DBG("BGP: Got lock in different state %d\n", p->p.proto_state);
- return;
- }
+ {
+ DBG("BGP: Got lock in different state %d\n", p->p.proto_state);
+ return;
+ }
DBG("BGP: Got lock\n");
if (cf->multihop)
- {
- /* Multi-hop sessions do not use neighbor entries */
- bgp_initiate(p);
- return;
- }
+ {
+ /* Multi-hop sessions do not use neighbor entries */
+ bgp_initiate(p);
+ return;
+ }
neighbor *n = neigh_find2(&p->p, &cf->remote_ip, cf->iface, NEF_STICKY);
if (!n)
- {
- log(L_ERR "%s: Invalid remote address %I%J", p->p.name, cf->remote_ip, cf->iface);
- /* As we do not start yet, we can just disable protocol */
- p->p.disabled = 1;
- bgp_store_error(p, NULL, BE_MISC, BEM_INVALID_NEXT_HOP);
- proto_notify_state(&p->p, PS_DOWN);
- return;
- }
+ {
+ log(L_ERR "%s: Invalid remote address %I%J", p->p.name, cf->remote_ip, cf->iface);
+ /* As we do not start yet, we can just disable protocol */
+ p->p.disabled = 1;
+ bgp_store_error(p, NULL, BE_MISC, BEM_INVALID_NEXT_HOP);
+ proto_notify_state(&p->p, PS_DOWN);
+ return;
+ }
p->neigh = n;
@@ -1144,9 +1305,7 @@ bgp_start(struct proto *P)
p->neigh = NULL;
p->bfd_req = NULL;
p->gr_ready = 0;
- p->gr_active = 0;
-
- rt_lock_table(p->igp_table);
+ p->gr_active_num = 0;
p->event = ev_new(p->p.pool);
p->event->hook = bgp_decision;
@@ -1165,15 +1324,20 @@ bgp_start(struct proto *P)
p->rr_cluster_id = p->cf->rr_cluster_id ? p->cf->rr_cluster_id : p->local_id;
p->remote_id = 0;
- p->source_addr = p->cf->source_addr;
+ p->source_addr = p->cf->local_ip;
+ p->link_addr = IPA_NONE;
+ /* XXXX */
if (p->p.gr_recovery && p->cf->gr_mode)
- proto_graceful_restart_lock(P);
+ {
+ struct bgp_channel *c;
+ WALK_LIST(c, p->p.channels)
+ channel_graceful_restart_lock(&c->c);
+ }
/*
- * Before attempting to create the connection, we need to lock the
- * port, so that are sure we're the only instance attempting to talk
- * with that neighbor.
+ * Before attempting to create the connection, we need to lock the port,
+ * so that we are the only instance attempting to talk with that neighbor.
*/
lock = p->lock = olock_new(P->pool);
@@ -1194,78 +1358,64 @@ static int
bgp_shutdown(struct proto *P)
{
struct bgp_proto *p = (struct bgp_proto *) P;
- unsigned subcode = 0;
+ uint subcode = 0;
BGP_TRACE(D_EVENTS, "Shutdown requested");
switch (P->down_code)
- {
- case PDC_CF_REMOVE:
- case PDC_CF_DISABLE:
- subcode = 3; // Errcode 6, 3 - peer de-configured
- break;
-
- case PDC_CF_RESTART:
- subcode = 6; // Errcode 6, 6 - other configuration change
- break;
-
- case PDC_CMD_DISABLE:
- case PDC_CMD_SHUTDOWN:
- subcode = 2; // Errcode 6, 2 - administrative shutdown
- break;
-
- case PDC_CMD_RESTART:
- 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 */
- log(L_WARN "%s: Route limit exceeded, shutting down", p->p.name);
- goto limit;
-
- case PDC_OUT_LIMIT_HIT:
- subcode = proto_restart ? 4 : 2; // Administrative reset or shutdown
-
- limit:
- bgp_store_error(p, NULL, BE_AUTO_DOWN, BEA_ROUTE_LIMIT_EXCEEDED);
- if (proto_restart)
- bgp_update_startup_delay(p);
- else
- p->startup_delay = 0;
- goto done;
- }
+ {
+ case PDC_CF_REMOVE:
+ case PDC_CF_DISABLE:
+ subcode = 3; // Errcode 6, 3 - peer de-configured
+ break;
+
+ case PDC_CF_RESTART:
+ subcode = 6; // Errcode 6, 6 - other configuration change
+ break;
+
+ case PDC_CMD_DISABLE:
+ case PDC_CMD_SHUTDOWN:
+ subcode = 2; // Errcode 6, 2 - administrative shutdown
+ break;
+
+ case PDC_CMD_RESTART:
+ 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 */
+ log(L_WARN "%s: Route limit exceeded, shutting down", p->p.name);
+ goto limit;
+
+ case PDC_OUT_LIMIT_HIT:
+ subcode = proto_restart ? 4 : 2; // Administrative reset or shutdown
+
+ limit:
+ bgp_store_error(p, NULL, BE_AUTO_DOWN, BEA_ROUTE_LIMIT_EXCEEDED);
+ if (proto_restart)
+ bgp_update_startup_delay(p);
+ else
+ p->startup_delay = 0;
+ goto done;
+ }
bgp_store_error(p, NULL, BE_MAN_DOWN, 0);
p->startup_delay = 0;
- done:
+done:
bgp_stop(p, subcode);
return p->p.proto_state;
}
-static void
-bgp_cleanup(struct proto *P)
-{
- struct bgp_proto *p = (struct bgp_proto *) P;
- rt_unlock_table(p->igp_table);
-}
-
-static rtable *
-get_igp_table(struct bgp_config *cf)
-{
- return cf->igp_table ? cf->igp_table->table : cf->c.table->table;
-}
-
static struct proto *
-bgp_init(struct proto_config *C)
+bgp_init(struct proto_config *CF)
{
- struct proto *P = proto_new(C, sizeof(struct bgp_proto));
- struct bgp_config *c = (struct bgp_config *) C;
+ struct proto *P = proto_new(CF);
struct bgp_proto *p = (struct bgp_proto *) P;
+ struct bgp_config *cf = (struct bgp_config *) CF;
- P->accept_ra_types = c->secondary ? RA_ACCEPTED : RA_OPTIMAL;
P->rt_notify = bgp_rt_notify;
P->import_control = bgp_import_control;
P->neigh_notify = bgp_neigh_notify;
@@ -1274,102 +1424,276 @@ bgp_init(struct proto_config *C)
P->feed_end = bgp_feed_end;
P->rte_better = bgp_rte_better;
P->rte_mergable = bgp_rte_mergable;
- P->rte_recalculate = c->deterministic_med ? bgp_rte_recalculate : NULL;
-
- p->cf = c;
- p->local_as = c->local_as;
- p->remote_as = c->remote_as;
- p->is_internal = (c->local_as == c->remote_as);
- p->rs_client = c->rs_client;
- p->rr_client = c->rr_client;
- p->igp_table = get_igp_table(c);
+ P->rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL;
+
+ p->cf = cf;
+ p->local_as = cf->local_as;
+ p->remote_as = cf->remote_as;
+ p->public_as = cf->local_as;
+ p->is_internal = (cf->local_as == cf->remote_as);
+ p->is_interior = p->is_internal || cf->confederation_member;
+ p->rs_client = cf->rs_client;
+ p->rr_client = cf->rr_client;
+
+ /* Confederation ID is used for truly external peers */
+ if (cf->confederation && !p->is_interior)
+ p->public_as = cf->confederation;
+
+ /* Add all channels */
+ struct bgp_channel_config *cc;
+ WALK_LIST(cc, CF->channels)
+ proto_add_channel(P, &cc->c);
return P;
}
+static void
+bgp_channel_init(struct channel *C, struct channel_config *CF)
+{
+ struct bgp_channel *c = (void *) C;
+ struct bgp_channel_config *cf = (void *) CF;
+
+ c->cf = cf;
+ c->afi = cf->afi;
+ c->desc = cf->desc;
+
+ if (cf->igp_table_ip4)
+ c->igp_table_ip4 = cf->igp_table_ip4->table;
+
+ if (cf->igp_table_ip6)
+ c->igp_table_ip6 = cf->igp_table_ip6->table;
+}
+
+static int
+bgp_channel_start(struct channel *C)
+{
+ struct bgp_proto *p = (void *) C->proto;
+ struct bgp_channel *c = (void *) C;
+ ip_addr src = p->source_addr;
+
+ if (c->igp_table_ip4)
+ rt_lock_table(c->igp_table_ip4);
+
+ if (c->igp_table_ip6)
+ rt_lock_table(c->igp_table_ip6);
+
+ c->pool = p->p.pool; // XXXX
+ bgp_init_bucket_table(c);
+ bgp_init_prefix_table(c);
+
+ c->next_hop_addr = c->cf->next_hop_addr;
+ c->link_addr = IPA_NONE;
+ c->packets_to_send = 0;
+
+ /* Try to use source address as next hop address */
+ if (ipa_zero(c->next_hop_addr))
+ {
+ if (bgp_channel_is_ipv4(c) && (ipa_is_ip4(src) || c->ext_next_hop))
+ c->next_hop_addr = src;
+
+ if (bgp_channel_is_ipv6(c) && (ipa_is_ip6(src) || c->ext_next_hop))
+ c->next_hop_addr = src;
+ }
+
+ /* Exit if no feasible next hop address is found */
+ if (ipa_zero(c->next_hop_addr))
+ {
+ log(L_WARN "%s: Missing next hop address", p->p.name);
+ return 0;
+ }
+
+ /* Set link-local address for IPv6 single-hop BGP */
+ if (ipa_is_ip6(c->next_hop_addr) && p->neigh)
+ {
+ c->link_addr = p->link_addr;
+
+ if (ipa_zero(c->link_addr))
+ log(L_WARN "%s: Missing link-local address", p->p.name);
+ }
+
+ /* Link local address is already in c->link_addr */
+ if (ipa_is_link_local(c->next_hop_addr))
+ c->next_hop_addr = IPA_NONE;
+
+ return 0; /* XXXX: Currently undefined */
+}
+
+static void
+bgp_channel_shutdown(struct channel *C)
+{
+ struct bgp_channel *c = (void *) C;
+
+ /* XXXX: cleanup bucket and prefix tables */
+
+ c->next_hop_addr = IPA_NONE;
+ c->link_addr = IPA_NONE;
+}
+
+static void
+bgp_channel_cleanup(struct channel *C)
+{
+ struct bgp_channel *c = (void *) C;
+
+ if (c->igp_table_ip4)
+ rt_unlock_table(c->igp_table_ip4);
+
+ if (c->igp_table_ip6)
+ rt_unlock_table(c->igp_table_ip6);
+}
+
+static inline struct bgp_channel_config *
+bgp_find_channel_config(struct bgp_config *cf, u32 afi)
+{
+ struct bgp_channel_config *cc;
+
+ WALK_LIST(cc, cf->c.channels)
+ if (cc->afi == afi)
+ return cc;
+
+ return NULL;
+}
+
+struct rtable_config *
+bgp_default_igp_table(struct bgp_config *cf, struct bgp_channel_config *cc, u32 type)
+{
+ struct bgp_channel_config *cc2;
+ struct rtable_config *tab;
+
+ /* First, try table connected by the channel */
+ if (cc->c.table->addr_type == type)
+ return cc->c.table;
+
+ /* Find paired channel with the same SAFI but the other AFI */
+ u32 afi2 = cc->afi ^ 0x30000;
+ cc2 = bgp_find_channel_config(cf, afi2);
+
+ /* Second, try IGP table configured in the paired channel */
+ if (cc2 && (tab = (type == NET_IP4) ? cc2->igp_table_ip4 : cc2->igp_table_ip6))
+ return tab;
+
+ /* Third, try table connected by the paired channel */
+ if (cc2 && (cc2->c.table->addr_type == type))
+ return cc2->c.table;
+
+ /* Last, try default table of given type */
+ if (tab = cf->c.global->def_tables[type])
+ return tab;
+
+ cf_error("Undefined IGP table");
+}
+
void
-bgp_check_config(struct bgp_config *c)
+bgp_postconfig(struct proto_config *CF)
{
- int internal = (c->local_as == c->remote_as);
+ struct bgp_config *cf = (void *) CF;
+ int internal = (cf->local_as == cf->remote_as);
/* Do not check templates at all */
- if (c->c.class == SYM_TEMPLATE)
+ if (cf->c.class == SYM_TEMPLATE)
return;
/* EBGP direct by default, IBGP multihop by default */
- if (c->multihop < 0)
- c->multihop = internal ? 64 : 0;
-
- /* Different default for gw_mode */
- if (!c->gw_mode)
- c->gw_mode = c->multihop ? GW_RECURSIVE : GW_DIRECT;
-
- /* Different default based on rs_client */
- if (!c->missing_lladdr)
- c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF;
-
- /* Disable after error incompatible with restart limit action */
- if (c->c.in_limit && (c->c.in_limit->action == PLA_RESTART) && c->disable_after_error)
- c->c.in_limit->action = PLA_DISABLE;
+ if (cf->multihop < 0)
+ cf->multihop = internal ? 64 : 0;
- if (!c->local_as)
+ if (!cf->local_as)
cf_error("Local AS number must be set");
- if (ipa_zero(c->remote_ip))
+ if (ipa_zero(cf->remote_ip))
cf_error("Neighbor must be configured");
- if (!c->remote_as)
+ if (!cf->remote_as)
cf_error("Remote AS number must be set");
- if (ipa_is_link_local(c->remote_ip) && !c->iface)
+ if (ipa_is_link_local(cf->remote_ip) && !cf->iface)
cf_error("Link-local neighbor address requires specified interface");
- if (!(c->capabilities && c->enable_as4) && (c->remote_as > 0xFFFF))
+ if (!(cf->capabilities && cf->enable_as4) && (cf->remote_as > 0xFFFF))
cf_error("Neighbor AS number out of range (AS4 not available)");
- if (!internal && c->rr_client)
+ if (!internal && cf->rr_client)
cf_error("Only internal neighbor can be RR client");
- if (internal && c->rs_client)
+ if (internal && cf->rs_client)
cf_error("Only external neighbor can be RS client");
- if (c->multihop && (c->gw_mode == GW_DIRECT))
- cf_error("Multihop BGP cannot use direct gateway mode");
+ if (!cf->confederation && cf->confederation_member)
+ cf_error("Confederation ID must be set for member sessions");
- if (c->multihop && (ipa_is_link_local(c->remote_ip) ||
- ipa_is_link_local(c->source_addr)))
+ if (cf->multihop && (ipa_is_link_local(cf->local_ip) ||
+ ipa_is_link_local(cf->remote_ip)))
cf_error("Multihop BGP cannot be used with link-local addresses");
- if (c->multihop && c->iface)
+ if (cf->multihop && cf->iface)
cf_error("Multihop BGP cannot be bound to interface");
- if (c->multihop && c->check_link)
+ if (cf->multihop && cf->check_link)
cf_error("Multihop BGP cannot depend on link state");
- if (c->multihop && c->bfd && ipa_zero(c->source_addr))
- cf_error("Multihop BGP with BFD requires specified source address");
+ if (cf->multihop && cf->bfd && ipa_zero(cf->local_ip))
+ cf_error("Multihop BGP with BFD requires specified local address");
- if ((c->gw_mode == GW_RECURSIVE) && c->c.table->sorted)
- cf_error("BGP in recursive mode prohibits sorted table");
- if (c->deterministic_med && c->c.table->sorted)
- cf_error("BGP with deterministic MED prohibits sorted table");
+ struct bgp_channel_config *cc;
+ WALK_LIST(cc, CF->channels)
+ {
+ /* Disable after error incompatible with restart limit action */
+ if ((cc->c.in_limit.action == PLA_RESTART) && cf->disable_after_error)
+ cc->c.in_limit.action = PLA_DISABLE;
+
+ /* Different default based on rs_client */
+ if (!cc->missing_lladdr)
+ cc->missing_lladdr = cf->rs_client ? MLL_IGNORE : MLL_SELF;
+
+ /* Different default for gw_mode */
+ if (!cc->gw_mode)
+ cc->gw_mode = cf->multihop ? GW_RECURSIVE : GW_DIRECT;
+
+ /* Default based on proto config */
+ if (cc->gr_able == 0xff)
+ cc->gr_able = (cf->gr_mode == BGP_GR_ABLE);
+
+ /* Default values of IGP tables */
+ if ((cc->gw_mode == GW_RECURSIVE) && !cc->desc->no_igp)
+ {
+ if (!cc->igp_table_ip4 && (bgp_cc_is_ipv4(cc) || cc->ext_next_hop))
+ cc->igp_table_ip4 = bgp_default_igp_table(cf, cc, NET_IP4);
+
+ if (!cc->igp_table_ip6 && (bgp_cc_is_ipv6(cc) || cc->ext_next_hop))
+ cc->igp_table_ip6 = bgp_default_igp_table(cf, cc, NET_IP6);
- if (c->secondary && !c->c.table->sorted)
- cf_error("BGP with secondary option requires sorted table");
+ if (cc->igp_table_ip4 && bgp_cc_is_ipv6(cc) && !cc->ext_next_hop)
+ cf_error("Mismatched IGP table type");
+
+ if (cc->igp_table_ip6 && bgp_cc_is_ipv4(cc) && !cc->ext_next_hop)
+ cf_error("Mismatched IGP table type");
+ }
+
+ if (cf->multihop && (cc->gw_mode == GW_DIRECT))
+ cf_error("Multihop BGP cannot use direct gateway mode");
+
+ if ((cc->gw_mode == GW_RECURSIVE) && cc->c.table->sorted)
+ cf_error("BGP in recursive mode prohibits sorted table");
+
+ if (cf->deterministic_med && cc->c.table->sorted)
+ cf_error("BGP with deterministic MED prohibits sorted table");
+
+ if (cc->secondary && !cc->c.table->sorted)
+ cf_error("BGP with secondary option requires sorted table");
+ }
}
static int
-bgp_reconfigure(struct proto *P, struct proto_config *C)
+bgp_reconfigure(struct proto *P, struct proto_config *CF)
{
- struct bgp_config *new = (struct bgp_config *) C;
- struct bgp_proto *p = (struct bgp_proto *) P;
+ struct bgp_proto *p = (void *) P;
+ struct bgp_config *new = (void *) CF;
struct bgp_config *old = p->cf;
- if (proto_get_router_id(C) != p->local_id)
+ if (proto_get_router_id(CF) != p->local_id)
return 0;
int same = !memcmp(((byte *) old) + sizeof(struct proto_config),
@@ -1377,8 +1701,26 @@ bgp_reconfigure(struct proto *P, struct proto_config *C)
// password item is last and must be checked separately
OFFSETOF(struct bgp_config, password) - sizeof(struct proto_config))
&& ((!old->password && !new->password)
- || (old->password && new->password && !strcmp(old->password, new->password)))
- && (get_igp_table(old) == get_igp_table(new));
+ || (old->password && new->password && !strcmp(old->password, new->password)));
+
+ /* FIXME: Move channel reconfiguration to generic protocol code ? */
+ struct channel *C, *C2;
+ struct bgp_channel_config *cc;
+
+ WALK_LIST(C, p->p.channels)
+ C->stale = 1;
+
+ WALK_LIST(cc, new->c.channels)
+ {
+ C = (struct channel *) bgp_find_channel(p, cc->afi);
+ same = proto_configure_channel(P, &C, &cc->c) && same;
+ C->stale = 0;
+ }
+
+ WALK_LIST_DELSAFE(C, C2, p->p.channels)
+ if (C->stale)
+ same = proto_configure_channel(P, &C, NULL) && same;
+
if (same && (p->start_state > BSS_PREPARE))
bgp_update_bfd(p, new->bfd);
@@ -1390,11 +1732,34 @@ bgp_reconfigure(struct proto *P, struct proto_config *C)
return same;
}
+#define IGP_TABLE(cf, sym) ((cf)->igp_table_##sym ? (cf)->igp_table_##sym ->table : NULL )
+
+static int
+bgp_channel_reconfigure(struct channel *C, struct channel_config *CC)
+{
+ struct bgp_channel *c = (void *) C;
+ struct bgp_channel_config *new = (void *) CC;
+ struct bgp_channel_config *old = c->cf;
+
+ if (memcmp(((byte *) old) + sizeof(struct channel_config),
+ ((byte *) new) + sizeof(struct channel_config),
+ /* Remaining items must be checked separately */
+ OFFSETOF(struct bgp_channel_config, rest) - sizeof(struct channel_config)))
+ return 0;
+
+ /* Check change in IGP tables */
+ if ((IGP_TABLE(old, ip4) != IGP_TABLE(new, ip4)) ||
+ (IGP_TABLE(old, ip6) != IGP_TABLE(new, ip6)))
+ return 0;
+
+ c->cf = new;
+ return 1;
+}
+
static void
-bgp_copy_config(struct proto_config *dest, struct proto_config *src)
+bgp_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUSED)
{
/* Just a shallow copy */
- proto_copy_rest(dest, src, sizeof(struct bgp_config));
}
@@ -1411,14 +1776,14 @@ bgp_copy_config(struct proto_config *dest, struct proto_config *src)
* closes the connection.
*/
void
-bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int len)
+bgp_error(struct bgp_conn *c, uint code, uint subcode, byte *data, int len)
{
struct bgp_proto *p = c->bgp;
if (c->state == BS_CLOSE)
return;
- bgp_log_error(p, BE_BGP_TX, "Error", code, subcode, data, (len > 0) ? len : -len);
+ bgp_log_error(p, BE_BGP_TX, "Error", code, subcode, data, ABS(len));
bgp_store_error(p, c, BE_BGP_TX, (code << 16) | subcode);
bgp_conn_enter_close_state(c);
@@ -1426,13 +1791,13 @@ bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int l
c->notify_subcode = subcode;
c->notify_data = data;
c->notify_size = (len > 0) ? len : 0;
- bgp_schedule_packet(c, PKT_NOTIFICATION);
+ bgp_schedule_packet(c, NULL, PKT_NOTIFICATION);
if (code != 6)
- {
- bgp_update_startup_delay(p);
- bgp_stop(p, 0);
- }
+ {
+ bgp_update_startup_delay(p);
+ bgp_stop(p, 0);
+ }
}
/**
@@ -1471,19 +1836,19 @@ static const char *
bgp_last_errmsg(struct bgp_proto *p)
{
switch (p->last_error_class)
- {
- case BE_MISC:
- return bgp_misc_errors[p->last_error_code];
- case BE_SOCKET:
- return (p->last_error_code == 0) ? "Connection closed" : strerror(p->last_error_code);
- case BE_BGP_RX:
- case BE_BGP_TX:
- return bgp_error_dsc(p->last_error_code >> 16, p->last_error_code & 0xFF);
- case BE_AUTO_DOWN:
- return bgp_auto_errors[p->last_error_code];
- default:
- return "";
- }
+ {
+ case BE_MISC:
+ return bgp_misc_errors[p->last_error_code];
+ case BE_SOCKET:
+ return (p->last_error_code == 0) ? "Connection closed" : strerror(p->last_error_code);
+ case BE_BGP_RX:
+ case BE_BGP_TX:
+ return bgp_error_dsc(p->last_error_code >> 16, p->last_error_code & 0xFF);
+ case BE_AUTO_DOWN:
+ return bgp_auto_errors[p->last_error_code];
+ default:
+ return "";
+ }
}
static const char *
@@ -1514,48 +1879,164 @@ bgp_get_status(struct proto *P, byte *buf)
}
static void
+bgp_show_afis(int code, char *s, u32 *afis, uint count)
+{
+ buffer b;
+ LOG_BUFFER_INIT(b);
+
+ buffer_puts(&b, s);
+
+ for (u32 *af = afis; af < (afis + count); af++)
+ {
+ const struct bgp_af_desc *desc = bgp_get_af_desc(*af);
+ if (desc)
+ buffer_print(&b, " %s", desc->name);
+ else
+ buffer_print(&b, " <%u/%u>", BGP_AFI(*af), BGP_SAFI(*af));
+ }
+
+ if (b.pos == b.end)
+ strcpy(b.end - 32, " ... <too long>");
+
+ cli_msg(code, b.start);
+}
+
+static void
+bgp_show_capabilities(struct bgp_proto *p UNUSED, struct bgp_caps *caps)
+{
+ struct bgp_af_caps *ac;
+ uint any_mp_bgp = 0;
+ uint any_gr_able = 0;
+ uint any_add_path = 0;
+ uint any_ext_next_hop = 0;
+ u32 *afl1 = alloca(caps->af_count * sizeof(u32));
+ u32 *afl2 = alloca(caps->af_count * sizeof(u32));
+ uint afn1, afn2;
+
+ WALK_AF_CAPS(caps, ac)
+ {
+ any_mp_bgp |= ac->ready;
+ any_gr_able |= ac->gr_able;
+ any_add_path |= ac->add_path;
+ any_ext_next_hop |= ac->ext_next_hop;
+ }
+
+ if (any_mp_bgp)
+ {
+ cli_msg(-1006, " Multiprotocol");
+
+ afn1 = 0;
+ WALK_AF_CAPS(caps, ac)
+ if (ac->ready)
+ afl1[afn1++] = ac->afi;
+
+ bgp_show_afis(-1006, " AF announced:", afl1, afn1);
+ }
+
+ if (caps->route_refresh)
+ cli_msg(-1006, " Route refresh");
+
+ if (any_ext_next_hop)
+ {
+ cli_msg(-1006, " Extended next hop");
+
+ afn1 = 0;
+ WALK_AF_CAPS(caps, ac)
+ if (ac->ext_next_hop)
+ afl1[afn1++] = ac->afi;
+
+ bgp_show_afis(-1006, " IPv6 nexthop:", afl1, afn1);
+ }
+
+ if (caps->ext_messages)
+ cli_msg(-1006, " Extended message");
+
+ if (caps->gr_aware)
+ cli_msg(-1006, " Graceful restart");
+
+ if (any_gr_able)
+ {
+ /* Continues from gr_aware */
+ cli_msg(-1006, " Restart time: %u", caps->gr_time);
+ if (caps->gr_flags & BGP_GRF_RESTART)
+ cli_msg(-1006, " Restart recovery");
+
+ afn1 = afn2 = 0;
+ WALK_AF_CAPS(caps, ac)
+ {
+ if (ac->gr_able)
+ afl1[afn1++] = ac->afi;
+
+ if (ac->gr_af_flags & BGP_GRF_FORWARDING)
+ afl2[afn2++] = ac->afi;
+ }
+
+ bgp_show_afis(-1006, " AF supported:", afl1, afn1);
+ bgp_show_afis(-1006, " AF preserved:", afl2, afn2);
+ }
+
+ if (caps->as4_support)
+ cli_msg(-1006, " 4-octet AS numbers");
+
+ if (any_add_path)
+ {
+ cli_msg(-1006, " ADD-PATH");
+
+ afn1 = afn2 = 0;
+ WALK_AF_CAPS(caps, ac)
+ {
+ if (ac->add_path & BGP_ADD_PATH_RX)
+ afl1[afn1++] = ac->afi;
+
+ if (ac->add_path & BGP_ADD_PATH_TX)
+ afl2[afn2++] = ac->afi;
+ }
+
+ bgp_show_afis(-1006, " RX:", afl1, afn1);
+ bgp_show_afis(-1006, " TX:", afl2, afn2);
+ }
+
+ if (caps->enhanced_refresh)
+ cli_msg(-1006, " Enhanced refresh");
+}
+
+static void
bgp_show_proto_info(struct proto *P)
{
struct bgp_proto *p = (struct bgp_proto *) P;
- struct bgp_conn *c = p->conn;
-
- proto_show_basic_info(P);
cli_msg(-1006, " BGP state: %s", bgp_state_dsc(p));
cli_msg(-1006, " Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface);
cli_msg(-1006, " Neighbor AS: %u", p->remote_as);
- if (p->gr_active)
+ if (p->gr_active_num)
cli_msg(-1006, " Neighbor graceful restart active");
if (P->proto_state == PS_START)
- {
- struct bgp_conn *oc = &p->outgoing_conn;
+ {
+ struct bgp_conn *oc = &p->outgoing_conn;
- if ((p->start_state < BSS_CONNECT) &&
- (p->startup_timer->expires))
- cli_msg(-1006, " Error wait: %d/%d",
- p->startup_timer->expires - now, p->startup_delay);
+ if ((p->start_state < BSS_CONNECT) &&
+ (p->startup_timer->expires))
+ cli_msg(-1006, " Error wait: %d/%d",
+ p->startup_timer->expires - now, p->startup_delay);
- if ((oc->state == BS_ACTIVE) &&
- (oc->connect_retry_timer->expires))
- cli_msg(-1006, " Connect delay: %d/%d",
- oc->connect_retry_timer->expires - now, p->cf->connect_delay_time);
+ if ((oc->state == BS_ACTIVE) &&
+ (oc->connect_timer->expires))
+ cli_msg(-1006, " Connect delay: %d/%d",
+ oc->connect_timer->expires - now, p->cf->connect_delay_time);
- if (p->gr_active && p->gr_timer->expires)
- cli_msg(-1006, " Restart timer: %d/-", p->gr_timer->expires - now);
- }
+ if (p->gr_active_num && p->gr_timer->expires)
+ cli_msg(-1006, " Restart timer: %d/-", p->gr_timer->expires - now);
+ }
else if (P->proto_state == PS_UP)
- {
- cli_msg(-1006, " Neighbor ID: %R", p->remote_id);
- cli_msg(-1006, " Neighbor caps: %s%s%s%s%s%s%s",
- c->peer_refresh_support ? " refresh" : "",
- c->peer_enhanced_refresh_support ? " enhanced-refresh" : "",
- c->peer_gr_able ? " restart-able" : (c->peer_gr_aware ? " restart-aware" : ""),
- c->peer_as4_support ? " AS4" : "",
- (c->peer_add_path & ADD_PATH_RX) ? " add-path-rx" : "",
- (c->peer_add_path & ADD_PATH_TX) ? " add-path-tx" : "",
- c->peer_ext_messages_support ? " ext-messages" : "");
+ {
+ cli_msg(-1006, " Neighbor ID: %R", p->remote_id);
+ cli_msg(-1006, " Local capabilities");
+ bgp_show_capabilities(p, p->conn->local_caps);
+ cli_msg(-1006, " Neighbor capabilities");
+ bgp_show_capabilities(p, p->conn->remote_caps);
+/* XXXX
cli_msg(-1006, " Session: %s%s%s%s%s%s%s%s",
p->is_internal ? "internal" : "external",
p->cf->multihop ? " multihop" : "",
@@ -1565,35 +2046,60 @@ bgp_show_proto_info(struct proto *P)
p->add_path_rx ? " add-path-rx" : "",
p->add_path_tx ? " add-path-tx" : "",
p->ext_messages ? " ext-messages" : "");
- 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.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",
- tm_remains(c->keepalive_timer), c->keepalive_time);
- }
+*/
+ cli_msg(-1006, " Source address: %I", p->source_addr);
+ cli_msg(-1006, " Hold timer: %d/%d",
+ tm_remains(p->conn->hold_timer), p->conn->hold_time);
+ cli_msg(-1006, " Keepalive timer: %d/%d",
+ tm_remains(p->conn->keepalive_timer), p->conn->keepalive_time);
+ }
if ((p->last_error_class != BE_NONE) &&
(p->last_error_class != BE_MAN_DOWN))
+ {
+ const char *err1 = bgp_err_classes[p->last_error_class];
+ const char *err2 = bgp_last_errmsg(p);
+ cli_msg(-1006, " Last error: %s%s", err1, err2);
+ }
+
+ {
+ /* XXXX ?? */
+ struct bgp_channel *c;
+ WALK_LIST(c, p->p.channels)
{
- const char *err1 = bgp_err_classes[p->last_error_class];
- const char *err2 = bgp_last_errmsg(p);
- cli_msg(-1006, " Last error: %s%s", err1, err2);
+ channel_show_info(&c->c);
+
+ if (c->igp_table_ip4)
+ cli_msg(-1006, " IGP IPv4 table: %s", c->igp_table_ip4->name);
+
+ if (c->igp_table_ip6)
+ cli_msg(-1006, " IGP IPv6 table: %s", c->igp_table_ip6->name);
}
+ }
}
+struct channel_class channel_bgp = {
+ .channel_size = sizeof(struct bgp_channel),
+ .config_size = sizeof(struct bgp_channel_config),
+ .init = bgp_channel_init,
+ .start = bgp_channel_start,
+ .shutdown = bgp_channel_shutdown,
+ .cleanup = bgp_channel_cleanup,
+ .reconfigure = bgp_channel_reconfigure,
+};
+
struct protocol proto_bgp = {
.name = "BGP",
.template = "bgp%d",
.attr_class = EAP_BGP,
.preference = DEF_PREF_BGP,
+ .channel_mask = NB_IP | NB_VPN | NB_FLOW,
+ .proto_size = sizeof(struct bgp_proto),
.config_size = sizeof(struct bgp_config),
+ .postconfig = bgp_postconfig,
.init = bgp_init,
.start = bgp_start,
.shutdown = bgp_shutdown,
- .cleanup = bgp_cleanup,
.reconfigure = bgp_reconfigure,
.copy_config = bgp_copy_config,
.get_status = bgp_get_status,
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index e47a0eb1..7ffcb68a 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -2,6 +2,8 @@
* BIRD -- The Border Gateway Protocol
*
* (c) 2000 Martin Mares <mj@ucw.cz>
+ * (c) 2008--2016 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2008--2016 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@@ -10,26 +12,80 @@
#define _BIRD_BGP_H_
#include <stdint.h>
+#include <setjmp.h>
+#include "nest/bird.h"
#include "nest/route.h"
#include "nest/bfd.h"
+//#include "lib/lists.h"
#include "lib/hash.h"
+#include "lib/socket.h"
struct linpool;
struct eattr;
+
+/* Address families */
+
+#define BGP_AFI_IPV4 1
+#define BGP_AFI_IPV6 2
+
+#define BGP_SAFI_UNICAST 1
+#define BGP_SAFI_MULTICAST 2
+#define BGP_SAFI_MPLS 4
+#define BGP_SAFI_MPLS_VPN 128
+#define BGP_SAFI_VPN_MULTICAST 129
+#define BGP_SAFI_FLOW 133
+
+/* Internal AF codes */
+
+#define BGP_AF(A, B) (((u32)(A) << 16) | (u32)(B))
+#define BGP_AFI(A) ((u32)(A) >> 16)
+#define BGP_SAFI(A) ((u32)(A) & 0xFFFF)
+
+#define BGP_AF_IPV4 BGP_AF( BGP_AFI_IPV4, BGP_SAFI_UNICAST )
+#define BGP_AF_IPV6 BGP_AF( BGP_AFI_IPV6, BGP_SAFI_UNICAST )
+#define BGP_AF_IPV4_MC BGP_AF( BGP_AFI_IPV4, BGP_SAFI_MULTICAST )
+#define BGP_AF_IPV6_MC BGP_AF( BGP_AFI_IPV6, BGP_SAFI_MULTICAST )
+#define BGP_AF_IPV4_MPLS BGP_AF( BGP_AFI_IPV4, BGP_SAFI_MPLS )
+#define BGP_AF_IPV6_MPLS BGP_AF( BGP_AFI_IPV6, BGP_SAFI_MPLS )
+#define BGP_AF_VPN4_MPLS BGP_AF( BGP_AFI_IPV4, BGP_SAFI_MPLS_VPN )
+#define BGP_AF_VPN6_MPLS BGP_AF( BGP_AFI_IPV6, BGP_SAFI_MPLS_VPN )
+#define BGP_AF_VPN4_MC BGP_AF( BGP_AFI_IPV4, BGP_SAFI_VPN_MULTICAST )
+#define BGP_AF_VPN6_MC BGP_AF( BGP_AFI_IPV6, BGP_SAFI_VPN_MULTICAST )
+#define BGP_AF_FLOW4 BGP_AF( BGP_AFI_IPV4, BGP_SAFI_FLOW )
+#define BGP_AF_FLOW6 BGP_AF( BGP_AFI_IPV6, BGP_SAFI_FLOW )
+
+
+struct bgp_write_state;
+struct bgp_parse_state;
+struct bgp_export_state;
+struct bgp_bucket;
+
+struct bgp_af_desc {
+ u32 afi;
+ u32 net;
+ u8 mpls;
+ u8 no_igp;
+ const char *name;
+ uint (*encode_nlri)(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size);
+ void (*decode_nlri)(struct bgp_parse_state *s, byte *pos, uint len, rta *a);
+ void (*update_next_hop)(struct bgp_export_state *s, eattr *nh, ea_list **to);
+ uint (*encode_next_hop)(struct bgp_write_state *s, eattr *nh, byte *buf, uint size);
+ void (*decode_next_hop)(struct bgp_parse_state *s, byte *pos, uint len, rta *a);
+};
+
+
struct bgp_config {
struct proto_config c;
u32 local_as, remote_as;
+ ip_addr local_ip; /* Source address to use */
ip_addr remote_ip;
- ip_addr source_addr; /* Source address to use */
struct iface *iface; /* Interface for link-local addresses */
+ u16 local_port; /* Local listening port */
u16 remote_port; /* Neighbor destination port */
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 strict_bind; /* Bind listening socket to local address */
+ int ttl_security; /* Enable TTL security [RFC 5082] */
int compare_path_lengths; /* Use path lengths when selecting best route */
int med_metric; /* Compare MULTI_EXIT_DISC even between routes from differen ASes */
int igp_metric; /* Use IGP metrics when selecting best route */
@@ -37,18 +93,17 @@ struct bgp_config {
int deterministic_med; /* Use more complicated algo to have strict RFC 4271 MED comparison */
u32 default_local_pref; /* Default value for LOCAL_PREF attribute */
u32 default_med; /* Default value for MULTI_EXIT_DISC attribute */
- int capabilities; /* Enable capability handshake [RFC3392] */
- int enable_refresh; /* Enable local support for route refresh [RFC2918] */
- int enable_as4; /* Enable local support for 4B AS numbers [RFC4893] */
+ int capabilities; /* Enable capability handshake [RFC 5492] */
+ int enable_refresh; /* Enable local support for route refresh [RFC 2918] */
+ int enable_as4; /* Enable local support for 4B AS numbers [RFC 6793] */
int enable_extended_messages; /* Enable local support for extended messages [draft] */
u32 rr_cluster_id; /* Route reflector cluster ID, if different from local ID */
int rr_client; /* Whether neighbor is RR client of me */
int rs_client; /* Whether neighbor is RS client of me */
- int advertise_ipv4; /* Whether we should add IPv4 capability advertisement to OPEN message */
+ u32 confederation; /* Confederation ID, or zero if confeds not active */
+ int confederation_member; /* Whether neighbor AS is member of our confederation */
int passive; /* Do not initiate outgoing connection */
int interpret_communities; /* Hardwired handling of well-known communities */
- int secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
- int add_path; /* Use ADD-PATH extension [RFC7911] */
int allow_local_as; /* Allow that number of local ASNs in incoming AS_PATHs */
int allow_local_pref; /* Allow LOCAL_PREF in EBGP sessions */
int gr_mode; /* Graceful restart mode (BGP_GR_*) */
@@ -64,11 +119,31 @@ struct bgp_config {
unsigned disable_after_error; /* Disable the protocol when error is detected */
char *password; /* Password used for MD5 authentication */
- struct rtable_config *igp_table; /* Table used for recursive next hop lookups */
int check_link; /* Use iface link state for liveness detection */
int bfd; /* Use BFD for liveness detection */
};
+struct bgp_channel_config {
+ struct channel_config c;
+
+ u32 afi;
+ const struct bgp_af_desc *desc;
+
+ ip_addr next_hop_addr; /* Local address for NEXT_HOP attribute */
+ u8 next_hop_self; /* Always set next hop to local IP address */
+ u8 next_hop_keep; /* Do not touch next hop attribute */
+ u8 missing_lladdr; /* What we will do when we don' know link-local addr, see MLL_* */
+ u8 gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */
+ u8 secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
+ u8 gr_able; /* Allow full graceful restart for the channel */
+ u8 ext_next_hop; /* Allow both IPv4 and IPv6 next hops */
+ u8 add_path; /* Use ADD-PATH extension [RFC 7911] */
+
+ uint rest[0]; /* Remaining items are reconfigured separately */
+ struct rtable_config *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */
+ struct rtable_config *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */
+};
+
#define MLL_SELF 1
#define MLL_DROP 2
#define MLL_IGNORE 3
@@ -76,112 +151,241 @@ struct bgp_config {
#define GW_DIRECT 1
#define GW_RECURSIVE 2
-#define ADD_PATH_RX 1
-#define ADD_PATH_TX 2
-#define ADD_PATH_FULL 3
+#define BGP_ADD_PATH_RX 1
+#define BGP_ADD_PATH_TX 2
+#define BGP_ADD_PATH_FULL 3
-#define BGP_GR_ABLE 1
-#define BGP_GR_AWARE 2
+#define BGP_GR_ABLE 1
+#define BGP_GR_AWARE 2
-/* For peer_gr_flags */
+/* For GR capability common flags */
#define BGP_GRF_RESTART 0x80
-/* For peer_gr_aflags */
+/* For GR capability per-AF flags */
#define BGP_GRF_FORWARDING 0x80
+struct bgp_af_caps {
+ u32 afi;
+ u8 ready; /* Multiprotocol capability, RFC 4760 */
+ u8 gr_able; /* Graceful restart support, RFC 4724 */
+ u8 gr_af_flags; /* Graceful restart per-AF flags */
+ u8 ext_next_hop; /* Extended IPv6 next hop, RFC 5549 */
+ u8 add_path; /* Multiple paths support, RFC 7911 */
+};
+
+struct bgp_caps {
+ u32 as4_number; /* Announced ASN */
+
+ u8 as4_support; /* Four-octet AS capability, RFC 6793 */
+ u8 ext_messages; /* Extended message length, RFC draft */
+ u8 route_refresh; /* Route refresh capability, RFC 2918 */
+ u8 enhanced_refresh; /* Enhanced route refresh, RFC 7313 */
+
+ u8 gr_aware; /* Graceful restart capability, RFC 4724 */
+ u8 gr_flags; /* Graceful restart flags */
+ u16 gr_time; /* Graceful restart time in seconds */
+
+ u16 af_count; /* Number of af_data items */
+
+ struct bgp_af_caps af_data[0]; /* Per-AF capability data */
+};
+
+#define WALK_AF_CAPS(caps,ac) \
+ for (ac = caps->af_data; ac < &caps->af_data[caps->af_count]; ac++)
+
+
+struct bgp_socket {
+ node n; /* Node in global bgp_sockets */
+ sock *sk; /* Real listening socket */
+ u32 uc; /* Use count */
+};
+
struct bgp_conn {
struct bgp_proto *bgp;
struct birdsock *sk;
- uint state; /* State of connection state machine */
- struct timer *connect_retry_timer;
+ u8 state; /* State of connection state machine */
+ u8 as4_session; /* Session uses 4B AS numbers in AS_PATH (both sides support it) */
+ u8 ext_messages; /* Session uses extended message length */
+
+ struct bgp_caps *local_caps;
+ struct bgp_caps *remote_caps;
+ struct timer *connect_timer;
struct timer *hold_timer;
struct timer *keepalive_timer;
struct event *tx_ev;
- int packets_to_send; /* Bitmap of packet types to be sent */
+ u32 packets_to_send; /* Bitmap of packet types to be sent */
+ u32 channels_to_send; /* Bitmap of channels with packets to be sent */
+ u8 last_channel; /* Channel used last time for TX */
+ u8 last_channel_count; /* Number of times the last channel was used in succession */
int notify_code, notify_subcode, notify_size;
byte *notify_data;
- u32 advertised_as; /* Temporary value for AS number received */
- int start_state; /* protocol start_state snapshot when connection established */
- u8 peer_refresh_support; /* Peer supports route refresh [RFC2918] */
- u8 peer_as4_support; /* Peer supports 4B AS numbers [RFC4893] */
- u8 peer_add_path; /* Peer supports ADD-PATH [RFC7911] */
- u8 peer_enhanced_refresh_support; /* Peer supports enhanced refresh [RFC7313] */
- u8 peer_gr_aware;
- u8 peer_gr_able;
- u16 peer_gr_time;
- u8 peer_gr_flags;
- u8 peer_gr_aflags;
- u8 peer_ext_messages_support; /* Peer supports extended message length [draft] */
- unsigned hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */
+
+ uint hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */
};
struct bgp_proto {
struct proto p;
struct bgp_config *cf; /* Shortcut to BGP configuration */
u32 local_as, remote_as;
- int start_state; /* Substates that partitions BS_START */
- u8 is_internal; /* Internal BGP connection (local_as == remote_as) */
- u8 as4_session; /* Session uses 4B AS numbers in AS_PATH (both sides support it) */
- u8 add_path_rx; /* Session expects receive of ADD-PATH extended NLRI */
- u8 add_path_tx; /* Session expects transmit of ADD-PATH extended NLRI */
- u8 ext_messages; /* Session allows to use extended messages (both sides support it) */
+ u32 public_as; /* Externally visible ASN (local_as or confederation id) */
u32 local_id; /* BGP identifier of this router */
u32 remote_id; /* BGP identifier of the neighbor */
u32 rr_cluster_id; /* Route reflector cluster ID */
- int rr_client; /* Whether neighbor is RR client of me */
- int rs_client; /* Whether neighbor is RS client of me */
+ int start_state; /* Substates that partitions BS_START */
+ u8 is_internal; /* Internal BGP session (local_as == remote_as) */
+ u8 is_interior; /* Internal or intra-confederation BGP session */
+ u8 as4_session; /* Session uses 4B AS numbers in AS_PATH (both sides support it) */
+ u8 rr_client; /* Whether neighbor is RR client of me */
+ u8 rs_client; /* Whether neighbor is RS client of me */
+ u8 route_refresh; /* Route refresh allowed to send [RFC 2918] */
+ u8 enhanced_refresh; /* Enhanced refresh is negotiated [RFC 7313] */
u8 gr_ready; /* Neighbor could do graceful restart */
- u8 gr_active; /* Neighbor is doing graceful restart */
- u8 feed_state; /* Feed state (TX) for EoR, RR packets, see BFS_* */
- u8 load_state; /* Load state (RX) for EoR, RR packets, see BFS_* */
+ u8 gr_active_num; /* Neighbor is doing GR, number of active channels */
+ u8 channel_count; /* Number of active channels */
+ u32 *afi_map; /* Map channel index -> AFI */
+ struct bgp_channel **channel_map; /* Map channel index -> channel */
struct bgp_conn *conn; /* Connection we have established */
struct bgp_conn outgoing_conn; /* Outgoing connection we're working with */
struct bgp_conn incoming_conn; /* Incoming connection we have neither accepted nor rejected yet */
struct object_lock *lock; /* Lock for neighbor connection */
struct neighbor *neigh; /* Neighbor entry corresponding to remote ip, NULL if multihop */
+ struct bgp_socket *sock; /* Shared listening socket */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
ip_addr source_addr; /* Local address used as an advertised next hop */
- rtable *igp_table; /* Table used for recursive next hop lookups */
+ ip_addr link_addr; /* Link-local version of source_addr */
struct event *event; /* Event for respawning and shutting process */
struct timer *startup_timer; /* Timer used to delay protocol startup due to previous errors (startup_delay) */
struct timer *gr_timer; /* Timer waiting for reestablishment after graceful restart */
- struct bgp_bucket **bucket_hash; /* Hash table of attribute buckets */
- uint hash_size, hash_count, hash_limit;
- HASH(struct bgp_prefix) prefix_hash; /* Prefixes to be sent */
- slab *prefix_slab; /* Slab holding prefix nodes */
- list bucket_queue; /* Queue of buckets to send */
- struct bgp_bucket *withdraw_bucket; /* Withdrawn routes */
unsigned startup_delay; /* Time to delay protocol startup by due to errors */
bird_clock_t last_proto_error; /* Time of last error that leads to protocol stop */
u8 last_error_class; /* Error class of last error */
u32 last_error_code; /* Error code of last error. BGP protocol errors
are encoded as (bgp_err_code << 16 | bgp_err_subcode) */
-#ifdef IPV6
- byte *mp_reach_start, *mp_unreach_start; /* Multiprotocol BGP attribute notes */
- unsigned mp_reach_len, mp_unreach_len;
- ip_addr local_link; /* Link-level version of source_addr */
-#endif
+};
+
+struct bgp_channel {
+ struct channel c;
+
+ /* Rest are BGP specific data */
+ struct bgp_channel_config *cf;
+ pool *pool; /* XXXX */
+
+ u32 afi;
+ u32 index;
+ const struct bgp_af_desc *desc;
+
+ HASH(struct bgp_bucket) bucket_hash; /* Hash table of route buckets */
+ struct bgp_bucket *withdraw_bucket; /* Withdrawn routes */
+ list bucket_queue; /* Queue of buckets to send (struct bgp_bucket) */
+
+ HASH(struct bgp_prefix) prefix_hash; /* Prefixes to be sent */
+ slab *prefix_slab; /* Slab holding prefix nodes */
+
+ rtable *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */
+ rtable *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */
+ ip_addr next_hop_addr; /* Local address for NEXT_HOP attribute */
+ ip_addr link_addr; /* Link-local version of next_hop_addr */
+
+ u32 packets_to_send; /* Bitmap of packet types to be sent */
+
+ u8 gr_ready; /* Neighbor could do GR on this AF */
+ u8 gr_active; /* Neighbor is doing GR and keeping fwd state */
+
+ u8 ext_next_hop; /* Session allows both IPv4 and IPv6 next hops */
+
+ u8 add_path_rx; /* Session expects receive of ADD-PATH extended NLRI */
+ u8 add_path_tx; /* Session expects transmit of ADD-PATH extended NLRI */
+
+ u8 feed_state; /* Feed state (TX) for EoR, RR packets, see BFS_* */
+ u8 load_state; /* Load state (RX) for EoR, RR packets, see BFS_* */
};
struct bgp_prefix {
- struct {
- ip_addr prefix;
- int pxlen;
- } n;
+ node buck_node; /* Node in per-bucket list */
+ struct bgp_prefix *next; /* Node in prefix hash table */
+ u32 hash;
u32 path_id;
- struct bgp_prefix *next;
- node bucket_node; /* Node in per-bucket list */
+ net_addr net[0];
};
struct bgp_bucket {
node send_node; /* Node in send queue */
- struct bgp_bucket *hash_next, *hash_prev; /* Node in bucket hash table */
- unsigned hash; /* Hash over extended attributes */
- list prefixes; /* Prefixes in this buckets */
+ struct bgp_bucket *next; /* Node in bucket hash table */
+ list prefixes; /* Prefixes in this bucket (struct bgp_prefix) */
+ u32 hash; /* Hash over extended attributes */
ea_list eattrs[0]; /* Per-bucket extended attributes */
};
+struct bgp_export_state {
+ struct bgp_proto *proto;
+ struct bgp_channel *channel;
+ struct linpool *pool;
+
+ struct bgp_proto *src;
+ rte *route;
+ int mpls;
+
+ u32 attrs_seen[1];
+ uint err_withdraw;
+};
+
+struct bgp_write_state {
+ struct bgp_proto *proto;
+ struct bgp_channel *channel;
+ struct linpool *pool;
+
+ int as4_session;
+ int add_path;
+ int mpls;
+
+ eattr *mp_next_hop;
+ adata *mpls_labels;
+};
+
+struct bgp_parse_state {
+ struct bgp_proto *proto;
+ struct bgp_channel *channel;
+ struct linpool *pool;
+
+ int as4_session;
+ int add_path;
+ int mpls;
+
+ u32 attrs_seen[256/32];
+
+ u32 mp_reach_af;
+ u32 mp_unreach_af;
+
+ uint attr_len;
+ uint ip_reach_len;
+ uint ip_unreach_len;
+ uint ip_next_hop_len;
+ uint mp_reach_len;
+ uint mp_unreach_len;
+ uint mp_next_hop_len;
+
+ byte *attrs;
+ byte *ip_reach_nlri;
+ byte *ip_unreach_nlri;
+ byte *ip_next_hop_data;
+ byte *mp_reach_nlri;
+ byte *mp_unreach_nlri;
+ byte *mp_next_hop_data;
+
+ uint err_withdraw;
+ uint err_subcode;
+ jmp_buf err_jmpbuf;
+
+ struct hostentry *hostentry;
+ adata *mpls_labels;
+
+ /* Cached state for bgp_rte_update() */
+ u32 last_id;
+ struct rte_src *last_src;
+ rta *cached_rta;
+};
+
#define BGP_PORT 179
#define BGP_VERSION 4
#define BGP_HEADER_LENGTH 19
@@ -192,10 +396,30 @@ struct bgp_bucket {
#define BGP_RX_BUFFER_EXT_SIZE 65535
#define BGP_TX_BUFFER_EXT_SIZE 65535
-static inline uint bgp_max_packet_length(struct bgp_proto *p)
-{ return p->ext_messages ? BGP_MAX_EXT_MSG_LENGTH : BGP_MAX_MESSAGE_LENGTH; }
+static inline int bgp_channel_is_ipv4(struct bgp_channel *c)
+{ return BGP_AFI(c->afi) == BGP_AFI_IPV4; }
+
+static inline int bgp_channel_is_ipv6(struct bgp_channel *c)
+{ return BGP_AFI(c->afi) == BGP_AFI_IPV6; }
+
+static inline int bgp_cc_is_ipv4(struct bgp_channel_config *c)
+{ return BGP_AFI(c->afi) == BGP_AFI_IPV4; }
+
+static inline int bgp_cc_is_ipv6(struct bgp_channel_config *c)
+{ return BGP_AFI(c->afi) == BGP_AFI_IPV6; }
+
+static inline uint bgp_max_packet_length(struct bgp_conn *conn)
+{ return conn->ext_messages ? BGP_MAX_EXT_MSG_LENGTH : BGP_MAX_MESSAGE_LENGTH; }
+
+static inline void
+bgp_parse_error(struct bgp_parse_state *s, uint subcode)
+{
+ s->err_subcode = subcode;
+ longjmp(s->err_jmpbuf, 1);
+}
extern struct linpool *bgp_linpool;
+extern struct linpool *bgp_linpool2;
void bgp_start_timer(struct timer *t, int value);
@@ -208,9 +432,9 @@ void bgp_conn_enter_established_state(struct bgp_conn *conn);
void bgp_conn_enter_close_state(struct bgp_conn *conn);
void bgp_conn_enter_idle_state(struct bgp_conn *conn);
void bgp_handle_graceful_restart(struct bgp_proto *p);
-void bgp_graceful_restart_done(struct bgp_proto *p);
-void bgp_refresh_begin(struct bgp_proto *p);
-void bgp_refresh_end(struct bgp_proto *p);
+void bgp_graceful_restart_done(struct bgp_channel *c);
+void bgp_refresh_begin(struct bgp_channel *c);
+void bgp_refresh_end(struct bgp_channel *c);
void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code);
void bgp_stop(struct bgp_proto *p, unsigned subcode);
@@ -233,48 +457,71 @@ struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id);
/* attrs.c */
-/* Hack: although BA_NEXT_HOP attribute has type EAF_TYPE_IP_ADDRESS, in IPv6
- * we store two addesses in it - a global address and a link local address.
- */
-#ifdef IPV6
-#define NEXT_HOP_LENGTH (2*sizeof(ip_addr))
-static inline void set_next_hop(byte *b, ip_addr addr) { ((ip_addr *) b)[0] = addr; ((ip_addr *) b)[1] = IPA_NONE; }
-#else
-#define NEXT_HOP_LENGTH sizeof(ip_addr)
-static inline void set_next_hop(byte *b, ip_addr addr) { ((ip_addr *) b)[0] = addr; }
-#endif
+static inline eattr *
+bgp_find_attr(ea_list *attrs, uint code)
+{
+ return ea_find(attrs, EA_CODE(EAP_BGP, code));
+}
+
+eattr *
+bgp_set_attr(ea_list **attrs, struct linpool *pool, uint code, uint flags, uintptr_t val);
+
+static inline void
+bgp_set_attr_u32(ea_list **to, struct linpool *pool, uint code, uint flags, u32 val)
+{ bgp_set_attr(to, pool, code, flags, (uintptr_t) val); }
+
+static inline void
+bgp_set_attr_ptr(ea_list **to, struct linpool *pool, uint code, uint flags, struct adata *val)
+{ bgp_set_attr(to, pool, code, flags, (uintptr_t) val); }
+
+static inline void
+bgp_set_attr_data(ea_list **to, struct linpool *pool, uint code, uint flags, void *data, uint len)
+{
+ struct adata *a = lp_alloc_adata(pool, len);
+ memcpy(a->data, data, len);
+ bgp_set_attr(to, pool, code, flags, (uintptr_t) a);
+}
+
+static inline void
+bgp_unset_attr(ea_list **to, struct linpool *pool, uint code)
+{ eattr *e = bgp_set_attr(to, pool, code, 0, 0); e->type = EAF_TYPE_UNDEF; }
+
+
+int bgp_encode_attrs(struct bgp_write_state *s, ea_list *attrs, byte *buf, byte *end);
+ea_list * bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len);
+
+void bgp_init_bucket_table(struct bgp_channel *c);
+void bgp_free_bucket(struct bgp_channel *c, struct bgp_bucket *b);
+void bgp_defer_bucket(struct bgp_channel *c, struct bgp_bucket *b);
+void bgp_withdraw_bucket(struct bgp_channel *c, struct bgp_bucket *b);
+
+void bgp_init_prefix_table(struct bgp_channel *c);
+void bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *bp);
-void bgp_attach_attr(struct ea_list **to, struct linpool *pool, unsigned attr, uintptr_t val);
-byte *bgp_attach_attr_wa(struct ea_list **to, struct linpool *pool, unsigned attr, unsigned len);
-struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, uint len, struct linpool *pool, int mandatory);
-int bgp_get_attr(struct eattr *e, byte *buf, int buflen);
int bgp_rte_better(struct rte *, struct rte *);
int bgp_rte_mergable(rte *pri, rte *sec);
int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best);
-void bgp_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs);
+void bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old, ea_list *attrs);
int bgp_import_control(struct proto *, struct rte **, struct ea_list **, struct linpool *);
-void bgp_init_bucket_table(struct bgp_proto *);
-void bgp_free_bucket_table(struct bgp_proto *p);
-void bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck);
-void bgp_init_prefix_table(struct bgp_proto *p, u32 order);
-void bgp_free_prefix_table(struct bgp_proto *p);
-void bgp_free_prefix(struct bgp_proto *p, struct bgp_prefix *bp);
-uint bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains);
+int bgp_get_attr(struct eattr *e, byte *buf, int buflen);
void bgp_get_route_info(struct rte *, byte *buf, struct ea_list *attrs);
-inline static void bgp_attach_attr_ip(struct ea_list **to, struct linpool *pool, unsigned attr, ip_addr a)
-{ *(ip_addr *) bgp_attach_attr_wa(to, pool, attr, sizeof(ip_addr)) = a; }
/* packets.c */
void mrt_dump_bgp_state_change(struct bgp_conn *conn, unsigned old, unsigned new);
-void bgp_schedule_packet(struct bgp_conn *conn, int type);
+const struct bgp_af_desc *bgp_get_af_desc(u32 afi);
+const struct bgp_af_caps *bgp_find_af_caps(struct bgp_caps *caps, u32 afi);
+void bgp_schedule_packet(struct bgp_conn *conn, struct bgp_channel *c, int type);
void bgp_kick_tx(void *vconn);
void bgp_tx(struct birdsock *sk);
int bgp_rx(struct birdsock *sk, uint size);
const char * bgp_error_dsc(unsigned code, unsigned subcode);
void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len);
+void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
+
+
/* Packet types */
#define PKT_OPEN 0x01
@@ -292,26 +539,25 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
#define BAF_PARTIAL 0x20
#define BAF_EXT_LEN 0x10
-#define BA_ORIGIN 0x01 /* [RFC1771] */ /* WM */
+#define BA_ORIGIN 0x01 /* RFC 4271 */ /* WM */
#define BA_AS_PATH 0x02 /* WM */
#define BA_NEXT_HOP 0x03 /* WM */
#define BA_MULTI_EXIT_DISC 0x04 /* ON */
#define BA_LOCAL_PREF 0x05 /* WD */
#define BA_ATOMIC_AGGR 0x06 /* WD */
#define BA_AGGREGATOR 0x07 /* OT */
-#define BA_COMMUNITY 0x08 /* [RFC1997] */ /* OT */
-#define BA_ORIGINATOR_ID 0x09 /* [RFC1966] */ /* ON */
-#define BA_CLUSTER_LIST 0x0a /* ON */
-/* We don't support these: */
-#define BA_DPA 0x0b /* ??? */
-#define BA_ADVERTISER 0x0c /* [RFC1863] */
-#define BA_RCID_PATH 0x0d
-#define BA_MP_REACH_NLRI 0x0e /* [RFC2283] */
-#define BA_MP_UNREACH_NLRI 0x0f
-#define BA_EXT_COMMUNITY 0x10 /* [RFC4360] */
-#define BA_AS4_PATH 0x11 /* [RFC4893] */
-#define BA_AS4_AGGREGATOR 0x12
-#define BA_LARGE_COMMUNITY 0x20 /* [RFC8092] */
+#define BA_COMMUNITY 0x08 /* RFC 1997 */ /* OT */
+#define BA_ORIGINATOR_ID 0x09 /* RFC 4456 */ /* ON */
+#define BA_CLUSTER_LIST 0x0a /* RFC 4456 */ /* ON */
+#define BA_MP_REACH_NLRI 0x0e /* RFC 4760 */
+#define BA_MP_UNREACH_NLRI 0x0f /* RFC 4760 */
+#define BA_EXT_COMMUNITY 0x10 /* RFC 4360 */
+#define BA_AS4_PATH 0x11 /* RFC 6793 */
+#define BA_AS4_AGGREGATOR 0x12 /* RFC 6793 */
+#define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */
+
+/* Bird's private internal BGP attributes */
+#define BA_MPLS_LABEL_STACK 0xfe /* MPLS label stack transfer attribute */
/* BGP connection states */
@@ -331,14 +577,12 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
*
* When BGP protocol is started by core, it goes to BSS_PREPARE. When BGP
* protocol done what is neccessary to start itself (like acquiring the lock),
- * it goes to BSS_CONNECT. When some connection attempt failed because of
- * option or capability error, it goes to BSS_CONNECT_NOCAP.
+ * it goes to BSS_CONNECT.
*/
#define BSS_PREPARE 0 /* Used before ordinary BGP started, i. e. waiting for lock */
#define BSS_DELAY 1 /* Startup delay due to previous errors */
#define BSS_CONNECT 2 /* Ordinary BGP connecting */
-#define BSS_CONNECT_NOCAP 3 /* Legacy BGP connecting (without capabilities) */
/* BGP feed states (TX)
@@ -347,7 +591,7 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
*
* RFC 7313 specifies that a route refresh should be demarcated by BoRR and EoRR packets.
*
- * These states (stored in p->feed_state) are used to keep track of these
+ * These states (stored in c->feed_state) are used to keep track of these
* requirements. When such feed is started, BFS_LOADING / BFS_REFRESHING is
* set. When it ended, BFS_LOADED / BFS_REFRESHED is set to schedule End-of-RIB
* or EoRR packet. When the packet is sent, the state returned to BFS_NONE.
@@ -403,15 +647,5 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
#define ORIGIN_EGP 1
#define ORIGIN_INCOMPLETE 2
-/* Address families */
-
-#define BGP_AF_IPV4 1
-#define BGP_AF_IPV6 2
-
-#ifdef IPV6
-#define BGP_AF BGP_AF_IPV6
-#else
-#define BGP_AF BGP_AF_IPV4
-#endif
#endif
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index 55c602f1..941ae5b6 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -13,28 +13,32 @@ CF_HDR
CF_DEFINES
#define BGP_CFG ((struct bgp_config *) this_proto)
+#define BGP_CC ((struct bgp_channel_config *) this_channel)
CF_DECLS
-CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
- KEEPALIVE, MULTIHOP, STARTUP, VIA, NEXT, HOP, SELF, DEFAULT,
- PATH, METRIC, ERROR, START, DELAY, FORGET, WAIT, ENABLE,
- DISABLE, AFTER, BGP_PATH, BGP_LOCAL_PREF, BGP_MED, BGP_ORIGIN,
- BGP_NEXT_HOP, BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY,
- BGP_EXT_COMMUNITY, SOURCE, ADDRESS, PASSWORD, RR, RS, CLIENT,
- CLUSTER, ID, AS4, ADVERTISE, IPV4, CAPABILITIES, LIMIT, PASSIVE,
- PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
- INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
- TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
- SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX, GRACEFUL, RESTART, AWARE,
- CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY, BGP_LARGE_COMMUNITY)
+CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
+ MULTIHOP, STARTUP, VIA, NEXT, HOP, SELF, DEFAULT, PATH, METRIC, ERROR,
+ START, DELAY, FORGET, WAIT, ENABLE, DISABLE, AFTER, BGP_PATH,
+ BGP_LOCAL_PREF, BGP_MED, BGP_ORIGIN, BGP_NEXT_HOP, BGP_ATOMIC_AGGR,
+ BGP_AGGREGATOR, BGP_COMMUNITY, BGP_EXT_COMMUNITY, BGP_LARGE_COMMUNITY,
+ SOURCE, ADDRESS, PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4, ADVERTISE,
+ IPV4, CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER, MISSING, LLADDR,
+ DROP, IGNORE, ROUTE, REFRESH, INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID,
+ BGP_CLUSTER_LIST, IGP, TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL,
+ SECURITY, DETERMINISTIC, SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX,
+ GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY,
+ STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6)
+
+%type <i32> bgp_afi
CF_GRAMMAR
-CF_ADDTO(proto, bgp_proto '}' { bgp_check_config(BGP_CFG); } )
+CF_ADDTO(proto, bgp_proto '}' )
bgp_proto_start: proto_start BGP {
this_proto = proto_config_new(&proto_bgp, $1);
+ BGP_CFG->local_port = BGP_PORT;
BGP_CFG->remote_port = BGP_PORT;
BGP_CFG->multihop = -1; /* undefined */
BGP_CFG->hold_time = 240;
@@ -49,26 +53,35 @@ bgp_proto_start: proto_start BGP {
BGP_CFG->enable_refresh = 1;
BGP_CFG->enable_as4 = 1;
BGP_CFG->capabilities = 2;
- BGP_CFG->advertise_ipv4 = 1;
BGP_CFG->interpret_communities = 1;
BGP_CFG->default_local_pref = 100;
BGP_CFG->gr_mode = BGP_GR_AWARE;
BGP_CFG->gr_time = 120;
BGP_CFG->setkey = 1;
- }
+ }
+ ;
+
+bgp_loc_opts:
+ /* empty */
+ | bgp_loc_opts PORT expr { BGP_CFG->local_port = $3; if (($3<1) || ($3>65535)) cf_error("Invalid port number"); }
+ | bgp_loc_opts AS expr { BGP_CFG->local_as = $3; }
;
bgp_nbr_opts:
/* empty */
- | bgp_nbr_opts PORT expr { BGP_CFG->remote_port = $3; if (($3<1) || ($3>65535)) cf_error("Invalid port number"); }
+ | bgp_nbr_opts PORT expr { BGP_CFG->remote_port = $3; if (($3<1) || ($3>65535)) cf_error("Invalid port number"); }
| bgp_nbr_opts AS expr { BGP_CFG->remote_as = $3; }
;
bgp_proto:
bgp_proto_start proto_name '{'
| bgp_proto proto_item ';'
- | bgp_proto LOCAL AS expr ';' { BGP_CFG->local_as = $4; }
- | bgp_proto LOCAL ipa AS expr ';' { BGP_CFG->source_addr = $3; BGP_CFG->local_as = $5; }
+ | bgp_proto bgp_proto_channel ';'
+ | bgp_proto LOCAL bgp_loc_opts ';'
+ | bgp_proto LOCAL ipa ipa_scope bgp_loc_opts ';' {
+ BGP_CFG->local_ip = $3;
+ if ($4) BGP_CFG->iface = $4;
+ }
| bgp_proto NEIGHBOR bgp_nbr_opts ';'
| bgp_proto NEIGHBOR ipa ipa_scope bgp_nbr_opts ';' {
if (ipa_nonzero(BGP_CFG->remote_ip))
@@ -78,20 +91,16 @@ bgp_proto:
}
| bgp_proto INTERFACE TEXT ';' { BGP_CFG->iface = if_get_by_name($3); }
| bgp_proto RR CLUSTER ID idval ';' { BGP_CFG->rr_cluster_id = $5; }
- | bgp_proto RR CLIENT ';' { BGP_CFG->rr_client = 1; }
- | bgp_proto RS CLIENT ';' { BGP_CFG->rs_client = 1; }
+ | bgp_proto RR CLIENT bool ';' { BGP_CFG->rr_client = $4; }
+ | bgp_proto RS CLIENT bool ';' { BGP_CFG->rs_client = $4; }
+ | bgp_proto CONFEDERATION expr ';' { BGP_CFG->confederation = $3; }
+ | bgp_proto CONFEDERATION MEMBER bool ';' { BGP_CFG->confederation_member = $4; }
| bgp_proto HOLD TIME expr ';' { BGP_CFG->hold_time = $4; }
| bgp_proto STARTUP HOLD TIME expr ';' { BGP_CFG->initial_hold_time = $5; }
| bgp_proto DIRECT ';' { BGP_CFG->multihop = 0; }
| 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_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; }
- | bgp_proto GATEWAY DIRECT ';' { BGP_CFG->gw_mode = GW_DIRECT; }
- | bgp_proto GATEWAY RECURSIVE ';' { BGP_CFG->gw_mode = GW_RECURSIVE; }
+ | bgp_proto STRICT BIND bool ';' { BGP_CFG->strict_bind = $4; }
| bgp_proto PATH METRIC bool ';' { BGP_CFG->compare_path_lengths = $4; }
| bgp_proto MED METRIC bool ';' { BGP_CFG->med_metric = $4; }
| bgp_proto IGP METRIC bool ';' { BGP_CFG->igp_metric = $4; }
@@ -99,7 +108,7 @@ bgp_proto:
| bgp_proto DETERMINISTIC MED bool ';' { BGP_CFG->deterministic_med = $4; }
| bgp_proto DEFAULT BGP_MED expr ';' { BGP_CFG->default_med = $4; }
| bgp_proto DEFAULT BGP_LOCAL_PREF expr ';' { BGP_CFG->default_local_pref = $4; }
- | bgp_proto SOURCE ADDRESS ipa ';' { BGP_CFG->source_addr = $4; }
+ | bgp_proto SOURCE ADDRESS ipa ';' { BGP_CFG->local_ip = $4; }
| bgp_proto START DELAY TIME expr ';' { BGP_CFG->connect_delay_time = $5; log(L_WARN "%s: Start delay time option is deprecated, use connect delay time", this_proto->name); }
| bgp_proto CONNECT DELAY TIME expr ';' { BGP_CFG->connect_delay_time = $5; }
| bgp_proto CONNECT RETRY TIME expr ';' { BGP_CFG->connect_retry_time = $5; }
@@ -111,33 +120,101 @@ bgp_proto:
| bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; }
| bgp_proto ENABLE EXTENDED MESSAGES bool ';' { BGP_CFG->enable_extended_messages = $5; }
| bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
- | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; }
| bgp_proto PASSWORD text ';' { BGP_CFG->password = $3; }
| bgp_proto SETKEY bool ';' { BGP_CFG->setkey = $3; }
- | bgp_proto ROUTE LIMIT expr ';' {
- this_proto->in_limit = cfg_allocz(sizeof(struct proto_limit));
- this_proto->in_limit->limit = $4;
- this_proto->in_limit->action = PLA_RESTART;
- log(L_WARN "%s: Route limit option is deprecated, use import limit", this_proto->name);
- }
| bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
| bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
- | bgp_proto SECONDARY bool ';' { BGP_CFG->secondary = $3; }
- | bgp_proto ADD PATHS RX ';' { BGP_CFG->add_path = ADD_PATH_RX; }
- | bgp_proto ADD PATHS TX ';' { BGP_CFG->add_path = ADD_PATH_TX; }
- | bgp_proto ADD PATHS bool ';' { BGP_CFG->add_path = $4 ? ADD_PATH_FULL : 0; }
- | bgp_proto ALLOW BGP_LOCAL_PREF bool ';' { BGP_CFG->allow_local_pref = $4; }
| bgp_proto ALLOW LOCAL AS ';' { BGP_CFG->allow_local_as = -1; }
| bgp_proto ALLOW LOCAL AS expr ';' { BGP_CFG->allow_local_as = $5; }
+ | bgp_proto ALLOW BGP_LOCAL_PREF bool ';' { BGP_CFG->allow_local_pref = $4; }
| bgp_proto GRACEFUL RESTART bool ';' { BGP_CFG->gr_mode = $4; }
| bgp_proto GRACEFUL RESTART AWARE ';' { BGP_CFG->gr_mode = BGP_GR_AWARE; }
| bgp_proto GRACEFUL RESTART TIME expr ';' { BGP_CFG->gr_time = $5; }
- | bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
| bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
| bgp_proto CHECK LINK bool ';' { BGP_CFG->check_link = $4; }
| bgp_proto BFD bool ';' { BGP_CFG->bfd = $3; cf_check_bfd($3); }
;
+bgp_afi:
+ IPV4 { $$ = BGP_AF_IPV4; }
+ | IPV6 { $$ = BGP_AF_IPV6; }
+ | IPV4 MULTICAST { $$ = BGP_AF_IPV4_MC; }
+ | IPV6 MULTICAST { $$ = BGP_AF_IPV6_MC; }
+ | IPV4 MPLS { $$ = BGP_AF_IPV4_MPLS; }
+ | IPV6 MPLS { $$ = BGP_AF_IPV6_MPLS; }
+ | VPN4 MPLS { $$ = BGP_AF_VPN4_MPLS; }
+ | VPN6 MPLS { $$ = BGP_AF_VPN6_MPLS; }
+ | VPN4 MULTICAST { $$ = BGP_AF_VPN4_MC; }
+ | VPN6 MULTICAST { $$ = BGP_AF_VPN6_MC; }
+ | FLOW4 { $$ = BGP_AF_FLOW4; }
+ | FLOW6 { $$ = BGP_AF_FLOW6; }
+ ;
+
+bgp_channel_start: bgp_afi
+{
+ const struct bgp_af_desc *desc = bgp_get_af_desc($1);
+
+ if (!desc)
+ cf_error("Unknown AFI/SAFI");
+
+ this_channel = channel_config_new(&channel_bgp, desc->net, this_proto);
+ BGP_CC->c.name = desc->name;
+ BGP_CC->c.ra_mode = RA_UNDEF;
+ BGP_CC->afi = $1;
+ BGP_CC->desc = desc;
+ BGP_CC->gr_able = 0xff; /* undefined */
+};
+
+bgp_channel_item:
+ channel_item
+ | NEXT HOP ADDRESS ipa { BGP_CC->next_hop_addr = $4; }
+ | NEXT HOP SELF { BGP_CC->next_hop_self = 1; BGP_CC->next_hop_keep = 0; }
+ | NEXT HOP KEEP { BGP_CC->next_hop_keep = 1; BGP_CC->next_hop_self = 0; }
+ | MISSING LLADDR SELF { BGP_CC->missing_lladdr = MLL_SELF; }
+ | MISSING LLADDR DROP { BGP_CC->missing_lladdr = MLL_DROP; }
+ | MISSING LLADDR IGNORE { BGP_CC->missing_lladdr = MLL_IGNORE; }
+ | GATEWAY DIRECT { BGP_CC->gw_mode = GW_DIRECT; }
+ | GATEWAY RECURSIVE { BGP_CC->gw_mode = GW_RECURSIVE; }
+ | SECONDARY bool { BGP_CC->secondary = $2; }
+ | GRACEFUL RESTART bool { BGP_CC->gr_able = $3; }
+ | EXTENDED NEXT HOP bool { BGP_CC->ext_next_hop = $4; }
+ | ADD PATHS RX { BGP_CC->add_path = BGP_ADD_PATH_RX; }
+ | ADD PATHS TX { BGP_CC->add_path = BGP_ADD_PATH_TX; }
+ | ADD PATHS bool { BGP_CC->add_path = $3 ? BGP_ADD_PATH_FULL : 0; }
+ | IGP TABLE rtable {
+ if (BGP_CC->desc->no_igp)
+ cf_error("IGP table not allowed here");
+
+ if ($3->addr_type == NET_IP4)
+ BGP_CC->igp_table_ip4 = $3;
+ else if ($3->addr_type == NET_IP6)
+ BGP_CC->igp_table_ip6 = $3;
+ else
+ cf_error("Mismatched IGP table type");
+ }
+ ;
+
+bgp_channel_opts:
+ /* empty */
+ | bgp_channel_opts bgp_channel_item ';'
+ ;
+
+bgp_channel_opt_list:
+ /* empty */
+ | '{' bgp_channel_opts '}'
+ ;
+
+bgp_channel_end:
+{
+ if (!this_channel->table)
+ cf_error("Routing table not specified");
+
+ this_channel = NULL;
+};
+
+bgp_proto_channel: bgp_channel_start bgp_channel_opt_list bgp_channel_end;
+
+
CF_ADDTO(dynamic_attr, BGP_ORIGIN
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_ENUM_BGP_ORIGIN, EA_CODE(EAP_BGP, BA_ORIGIN)); })
CF_ADDTO(dynamic_attr, BGP_PATH
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index ab87bdcc..0e974746 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -2,12 +2,16 @@
* BIRD -- BGP Packet Processing
*
* (c) 2000 Martin Mares <mj@ucw.cz>
+ * (c) 2008--2016 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2008--2016 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#undef LOCAL_DEBUG
+#include <stdlib.h>
+
#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
@@ -16,6 +20,7 @@
#include "nest/mrtdump.h"
#include "conf/conf.h"
#include "lib/unaligned.h"
+#include "lib/flowspec.h"
#include "lib/socket.h"
#include "nest/cli.h"
@@ -27,6 +32,13 @@
#define BGP_RR_BEGIN 1
#define BGP_RR_END 2
+#define BGP_NLRI_MAX (4 + 1 + 32)
+
+#define BGP_MPLS_BOS 1 /* Bottom-of-stack bit */
+#define BGP_MPLS_MAX 10 /* Max number of labels that 24*n <= 255 */
+#define BGP_MPLS_NULL 3 /* Implicit NULL label */
+#define BGP_MPLS_MAGIC 0x800000 /* Magic withdraw label value, RFC 3107 3 */
+
static struct tbf rl_rcv_update = TBF_DEFAULT_LOG_LIMITS;
static struct tbf rl_snd_update = TBF_DEFAULT_LOG_LIMITS;
@@ -38,6 +50,46 @@ static byte fsm_err_subcode[BS_MAX] = {
[BS_ESTABLISHED] = 3
};
+
+static struct bgp_channel *
+bgp_get_channel(struct bgp_proto *p, u32 afi)
+{
+ uint i;
+
+ for (i = 0; i < p->channel_count; i++)
+ if (p->afi_map[i] == afi)
+ return p->channel_map[i];
+
+ return NULL;
+}
+
+static inline void
+put_af3(byte *buf, u32 id)
+{
+ put_u16(buf, id >> 16);
+ buf[2] = id & 0xff;
+}
+
+static inline void
+put_af4(byte *buf, u32 id)
+{
+ put_u16(buf, id >> 16);
+ buf[2] = 0;
+ buf[3] = id & 0xff;
+}
+
+static inline u32
+get_af3(byte *buf)
+{
+ return (get_u16(buf) << 16) | buf[2];
+}
+
+static inline u32
+get_af4(byte *buf)
+{
+ return (get_u16(buf) << 16) | buf[3];
+}
+
/*
* MRT Dump format is not semantically specified.
* We will use these values in appropriate fields:
@@ -58,31 +110,41 @@ static byte *
mrt_put_bgp4_hdr(byte *buf, struct bgp_conn *conn, int as4)
{
struct bgp_proto *p = conn->bgp;
+ uint v4 = ipa_is_ip4(p->cf->remote_ip);
if (as4)
- {
- put_u32(buf+0, p->remote_as);
- put_u32(buf+4, p->local_as);
- buf+=8;
- }
+ {
+ put_u32(buf+0, p->remote_as);
+ put_u32(buf+4, p->public_as);
+ buf+=8;
+ }
else
- {
- put_u16(buf+0, (p->remote_as <= 0xFFFF) ? p->remote_as : AS_TRANS);
- put_u16(buf+2, (p->local_as <= 0xFFFF) ? p->local_as : AS_TRANS);
- buf+=4;
- }
+ {
+ put_u16(buf+0, (p->remote_as <= 0xFFFF) ? p->remote_as : AS_TRANS);
+ put_u16(buf+2, (p->public_as <= 0xFFFF) ? p->public_as : AS_TRANS);
+ buf+=4;
+ }
put_u16(buf+0, (p->neigh && p->neigh->iface) ? p->neigh->iface->index : 0);
- put_u16(buf+2, BGP_AF);
+ put_u16(buf+2, v4 ? BGP_AFI_IPV4 : BGP_AFI_IPV6);
buf+=4;
- buf = put_ipa(buf, conn->sk ? conn->sk->daddr : IPA_NONE);
- buf = put_ipa(buf, conn->sk ? conn->sk->saddr : IPA_NONE);
+
+ if (v4)
+ {
+ buf = put_ip4(buf, conn->sk ? ipa_to_ip4(conn->sk->daddr) : IP4_NONE);
+ buf = put_ip4(buf, conn->sk ? ipa_to_ip4(conn->sk->saddr) : IP4_NONE);
+ }
+ else
+ {
+ buf = put_ip6(buf, conn->sk ? ipa_to_ip6(conn->sk->daddr) : IP6_NONE);
+ buf = put_ip6(buf, conn->sk ? ipa_to_ip6(conn->sk->saddr) : IP6_NONE);
+ }
return buf;
}
static void
-mrt_dump_bgp_packet(struct bgp_conn *conn, byte *pkt, unsigned len)
+mrt_dump_bgp_packet(struct bgp_conn *conn, byte *pkt, uint len)
{
byte *buf = alloca(128+len); /* 128 is enough for MRT headers */
byte *bp = buf + MRTDUMP_HDR_LENGTH;
@@ -96,14 +158,14 @@ mrt_dump_bgp_packet(struct bgp_conn *conn, byte *pkt, unsigned len)
}
static inline u16
-convert_state(unsigned state)
+convert_state(uint state)
{
/* Convert state from our BS_* values to values used in MRTDump */
return (state == BS_CLOSE) ? 1 : state + 1;
}
void
-mrt_dump_bgp_state_change(struct bgp_conn *conn, unsigned old, unsigned new)
+mrt_dump_bgp_state_change(struct bgp_conn *conn, uint old, uint new)
{
byte buf[128];
byte *bp = buf + MRTDUMP_HDR_LENGTH;
@@ -127,1303 +189,2426 @@ bgp_create_notification(struct bgp_conn *conn, byte *buf)
return buf + 2 + conn->notify_size;
}
-#ifdef IPV6
-static byte *
-bgp_put_cap_ipv6(struct bgp_proto *p UNUSED, byte *buf)
-{
- *buf++ = 1; /* Capability 1: Multiprotocol extensions */
- *buf++ = 4; /* Capability data length */
- *buf++ = 0; /* We support AF IPv6 */
- *buf++ = BGP_AF_IPV6;
- *buf++ = 0; /* RFU */
- *buf++ = 1; /* and SAFI 1 */
- return buf;
-}
-#else
+/* Capability negotiation as per RFC 5492 */
-static byte *
-bgp_put_cap_ipv4(struct bgp_proto *p UNUSED, byte *buf)
-{
- *buf++ = 1; /* Capability 1: Multiprotocol extensions */
- *buf++ = 4; /* Capability data length */
- *buf++ = 0; /* We support AF IPv4 */
- *buf++ = BGP_AF_IPV4;
- *buf++ = 0; /* RFU */
- *buf++ = 1; /* and SAFI 1 */
- return buf;
+const struct bgp_af_caps *
+bgp_find_af_caps(struct bgp_caps *caps, u32 afi)
+{
+ struct bgp_af_caps *ac;
+
+ WALK_AF_CAPS(caps, ac)
+ if (ac->afi == afi)
+ return ac;
+
+ return NULL;
}
-#endif
-static byte *
-bgp_put_cap_rr(struct bgp_proto *p UNUSED, byte *buf)
+static struct bgp_af_caps *
+bgp_get_af_caps(struct bgp_caps *caps, u32 afi)
{
- *buf++ = 2; /* Capability 2: Support for route refresh */
- *buf++ = 0; /* Capability data length */
- return buf;
+ struct bgp_af_caps *ac;
+
+ WALK_AF_CAPS(caps, ac)
+ if (ac->afi == afi)
+ return ac;
+
+ ac = &caps->af_data[caps->af_count++];
+ memset(ac, 0, sizeof(struct bgp_af_caps));
+ ac->afi = afi;
+
+ return ac;
}
-static byte *
-bgp_put_cap_ext_msg(struct bgp_proto *p UNUSED, byte *buf)
+static int
+bgp_af_caps_cmp(const void *X, const void *Y)
{
- *buf++ = 6; /* Capability 6: Support for extended messages */
- *buf++ = 0; /* Capability data length */
- return buf;
+ const struct bgp_af_caps *x = X, *y = Y;
+ return (x->afi < y->afi) ? -1 : (x->afi > y->afi) ? 1 : 0;
}
+
static byte *
-bgp_put_cap_gr1(struct bgp_proto *p, byte *buf)
+bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
{
- *buf++ = 64; /* Capability 64: Support for graceful restart */
- *buf++ = 6; /* Capability data length */
+ struct bgp_proto *p = conn->bgp;
+ struct bgp_channel *c;
+ struct bgp_caps *caps;
+ struct bgp_af_caps *ac;
+ uint any_ext_next_hop = 0;
+ uint any_add_path = 0;
+ byte *data;
- put_u16(buf, p->cf->gr_time);
- if (p->p.gr_recovery)
- buf[0] |= BGP_GRF_RESTART;
- buf += 2;
+ /* Prepare bgp_caps structure */
+
+ int n = list_length(&p->p.channels);
+ caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + n * sizeof(struct bgp_af_caps));
+ conn->local_caps = caps;
+
+ caps->as4_support = p->cf->enable_as4;
+ caps->ext_messages = p->cf->enable_extended_messages;
+ caps->route_refresh = p->cf->enable_refresh;
+ caps->enhanced_refresh = p->cf->enable_refresh;
+
+ if (caps->as4_support)
+ caps->as4_number = p->public_as;
+
+ if (p->cf->gr_mode)
+ {
+ caps->gr_aware = 1;
+ caps->gr_time = p->cf->gr_time;
+ caps->gr_flags = p->p.gr_recovery ? BGP_GRF_RESTART : 0;
+ }
+
+ /* Allocate and fill per-AF fields */
+ WALK_LIST(c, p->p.channels)
+ {
+ ac = &caps->af_data[caps->af_count++];
+ ac->afi = c->afi;
+ ac->ready = 1;
+
+ ac->ext_next_hop = bgp_channel_is_ipv4(c) && c->cf->ext_next_hop;
+ any_ext_next_hop |= ac->ext_next_hop;
+
+ ac->add_path = c->cf->add_path;
+ any_add_path |= ac->add_path;
+
+ if (c->cf->gr_able)
+ {
+ ac->gr_able = 1;
+
+ if (p->p.gr_recovery)
+ ac->gr_af_flags |= BGP_GRF_FORWARDING;
+ }
+ }
+
+ /* Sort capability fields by AFI/SAFI */
+ qsort(caps->af_data, caps->af_count, sizeof(struct bgp_af_caps), bgp_af_caps_cmp);
- *buf++ = 0; /* Appropriate AF */
- *buf++ = BGP_AF;
- *buf++ = 1; /* and SAFI 1 */
- *buf++ = p->p.gr_recovery ? BGP_GRF_FORWARDING : 0;
+
+ /* Create capability list in buffer */
+
+ /*
+ * Note that max length is ~ 20+14*af_count. With max 12 channels that is
+ * 188. Option limit is 253 and buffer size is 4096, so we cannot overflow
+ * unless we add new capabilities or more AFs.
+ */
+
+ WALK_AF_CAPS(caps, ac)
+ if (ac->ready)
+ {
+ *buf++ = 1; /* Capability 1: Multiprotocol extensions */
+ *buf++ = 4; /* Capability data length */
+ put_af4(buf, ac->afi);
+ buf += 4;
+ }
+
+ if (caps->route_refresh)
+ {
+ *buf++ = 2; /* Capability 2: Support for route refresh */
+ *buf++ = 0; /* Capability data length */
+ }
+
+ if (any_ext_next_hop)
+ {
+ *buf++ = 5; /* Capability 5: Support for extended next hop */
+ *buf++ = 0; /* Capability data length, will be fixed later */
+ data = buf;
+
+ WALK_AF_CAPS(caps, ac)
+ if (ac->ext_next_hop)
+ {
+ put_af4(buf, ac->afi);
+ put_u16(buf+4, BGP_AFI_IPV6);
+ buf += 6;
+ }
+
+ data[-1] = buf - data;
+ }
+
+ if (caps->ext_messages)
+ {
+ *buf++ = 6; /* Capability 6: Support for extended messages */
+ *buf++ = 0; /* Capability data length */
+ }
+
+ if (caps->gr_aware)
+ {
+ *buf++ = 64; /* Capability 64: Support for graceful restart */
+ *buf++ = 0; /* Capability data length, will be fixed later */
+ data = buf;
+
+ put_u16(buf, caps->gr_time);
+ buf[0] |= caps->gr_flags;
+ buf += 2;
+
+ WALK_AF_CAPS(caps, ac)
+ if (ac->gr_able)
+ {
+ put_af3(buf, ac->afi);
+ buf[3] = ac->gr_af_flags;
+ buf += 4;
+ }
+
+ data[-1] = buf - data;
+ }
+
+ if (caps->as4_support)
+ {
+ *buf++ = 65; /* Capability 65: Support for 4-octet AS number */
+ *buf++ = 4; /* Capability data length */
+ put_u32(buf, p->public_as);
+ buf += 4;
+ }
+
+ if (any_add_path)
+ {
+ *buf++ = 69; /* Capability 69: Support for ADD-PATH */
+ *buf++ = 0; /* Capability data length, will be fixed later */
+ data = buf;
+
+ WALK_AF_CAPS(caps, ac)
+ if (ac->add_path)
+ {
+ put_af3(buf, ac->afi);
+ buf[3] = ac->add_path;
+ buf += 4;
+ }
+
+ data[-1] = buf - data;
+ }
+
+ if (caps->enhanced_refresh)
+ {
+ *buf++ = 70; /* Capability 70: Support for enhanced route refresh */
+ *buf++ = 0; /* Capability data length */
+ }
return buf;
}
-static byte *
-bgp_put_cap_gr2(struct bgp_proto *p UNUSED, byte *buf)
+static void
+bgp_read_capabilities(struct bgp_conn *conn, struct bgp_caps *caps, byte *pos, int len)
{
- *buf++ = 64; /* Capability 64: Support for graceful restart */
- *buf++ = 2; /* Capability data length */
- put_u16(buf, 0);
- return buf + 2;
-}
+ struct bgp_proto *p = conn->bgp;
+ struct bgp_af_caps *ac;
+ int i, cl;
+ u32 af;
-static byte *
-bgp_put_cap_as4(struct bgp_proto *p, byte *buf)
-{
- *buf++ = 65; /* Capability 65: Support for 4-octet AS number */
- *buf++ = 4; /* Capability data length */
- put_u32(buf, p->local_as);
- return buf + 4;
-}
+ while (len > 0)
+ {
+ if (len < 2 || len < (2 + pos[1]))
+ goto err;
-static byte *
-bgp_put_cap_add_path(struct bgp_proto *p, byte *buf)
-{
- *buf++ = 69; /* Capability 69: Support for ADD-PATH */
- *buf++ = 4; /* Capability data length */
+ /* Capability length */
+ cl = pos[1];
- *buf++ = 0; /* Appropriate AF */
- *buf++ = BGP_AF;
- *buf++ = 1; /* SAFI 1 */
+ /* Capability type */
+ switch (pos[0])
+ {
+ case 1: /* Multiprotocol capability, RFC 4760 */
+ if (cl != 4)
+ goto err;
- *buf++ = p->cf->add_path;
+ af = get_af4(pos+2);
+ ac = bgp_get_af_caps(caps, af);
+ ac->ready = 1;
+ break;
- return buf;
+ case 2: /* Route refresh capability, RFC 2918 */
+ if (cl != 0)
+ goto err;
+
+ caps->route_refresh = 1;
+ break;
+
+ case 5: /* Extended next hop encoding capability, RFC 5549 */
+ if (cl % 6)
+ goto err;
+
+ for (i = 0; i < cl; i += 6)
+ {
+ /* Specified only for IPv4 prefixes with IPv6 next hops */
+ if ((get_u16(pos+2+i+0) != BGP_AFI_IPV4) ||
+ (get_u16(pos+2+i+4) != BGP_AFI_IPV6))
+ continue;
+
+ af = get_af4(pos+2+i);
+ ac = bgp_get_af_caps(caps, af);
+ ac->ext_next_hop = 1;
+ }
+ break;
+
+ case 6: /* Extended message length capability, RFC draft */
+ if (cl != 0)
+ goto err;
+
+ caps->ext_messages = 1;
+ break;
+
+ case 64: /* Graceful restart capability, RFC 4724 */
+ if (cl % 4 != 2)
+ goto err;
+
+ /* Only the last instance is valid */
+ WALK_AF_CAPS(caps, ac)
+ {
+ ac->gr_able = 0;
+ ac->gr_af_flags = 0;
+ }
+
+ caps->gr_aware = 1;
+ caps->gr_flags = pos[2] & 0xf0;
+ caps->gr_time = get_u16(pos + 2) & 0x0fff;
+
+ for (i = 2; i < cl; i += 4)
+ {
+ af = get_af3(pos+2+i);
+ ac = bgp_get_af_caps(caps, af);
+ ac->gr_able = 1;
+ ac->gr_af_flags = pos[2+i+3];
+ }
+ break;
+
+ case 65: /* AS4 capability, RFC 6793 */
+ if (cl != 4)
+ goto err;
+
+ caps->as4_support = 1;
+ caps->as4_number = get_u32(pos + 2);
+ break;
+
+ case 69: /* ADD-PATH capability, RFC 7911 */
+ if (cl % 4)
+ goto err;
+
+ for (i = 0; i < cl; i += 4)
+ {
+ byte val = pos[2+i+3];
+ if (!val || (val > BGP_ADD_PATH_FULL))
+ {
+ log(L_WARN "%s: Got ADD-PATH capability with unknown value %u, ignoring",
+ p->p.name, val);
+ break;
+ }
+ }
+
+ for (i = 0; i < cl; i += 4)
+ {
+ af = get_af3(pos+2+i);
+ ac = bgp_get_af_caps(caps, af);
+ ac->add_path = pos[2+i+3];
+ }
+ break;
+
+ case 70: /* Enhanced route refresh capability, RFC 7313 */
+ if (cl != 0)
+ goto err;
+
+ caps->enhanced_refresh = 1;
+ break;
+
+ /* We can safely ignore all other capabilities */
+ }
+
+ ADVANCE(pos, len, 2 + cl);
+ }
+ return;
+
+err:
+ bgp_error(conn, 2, 0, NULL, 0);
+ return;
}
-static byte *
-bgp_put_cap_err(struct bgp_proto *p UNUSED, byte *buf)
+static int
+bgp_read_options(struct bgp_conn *conn, byte *pos, int len)
{
- *buf++ = 70; /* Capability 70: Support for enhanced route refresh */
- *buf++ = 0; /* Capability data length */
- return buf;
-}
+ struct bgp_proto *p = conn->bgp;
+ struct bgp_caps *caps;
+ int ol;
+
+ /* Max number of announced AFIs is limited by max option length (255) */
+ caps = alloca(sizeof(struct bgp_caps) + 64 * sizeof(struct bgp_af_caps));
+ memset(caps, 0, sizeof(struct bgp_caps));
+
+ while (len > 0)
+ {
+ if ((len < 2) || (len < (2 + pos[1])))
+ { bgp_error(conn, 2, 0, NULL, 0); return -1; }
+ ol = pos[1];
+ if (pos[0] == 2)
+ {
+ /* BGP capabilities, RFC 5492 */
+ if (p->cf->capabilities)
+ bgp_read_capabilities(conn, caps, pos + 2, ol);
+ }
+ else
+ {
+ /* Unknown option */
+ bgp_error(conn, 2, 4, pos, ol); /* FIXME: ol or ol+2 ? */
+ return -1;
+ }
+
+ ADVANCE(pos, len, 2 + ol);
+ }
+
+ uint n = sizeof(struct bgp_caps) + caps->af_count * sizeof(struct bgp_af_caps);
+ conn->remote_caps = mb_allocz(p->p.pool, n);
+ memcpy(conn->remote_caps, caps, n);
+
+ return 0;
+}
static byte *
bgp_create_open(struct bgp_conn *conn, byte *buf)
{
struct bgp_proto *p = conn->bgp;
- byte *cap;
- int cap_len;
BGP_TRACE(D_PACKETS, "Sending OPEN(ver=%d,as=%d,hold=%d,id=%08x)",
- BGP_VERSION, p->local_as, p->cf->hold_time, p->local_id);
+ BGP_VERSION, p->public_as, p->cf->hold_time, p->local_id);
+
buf[0] = BGP_VERSION;
- put_u16(buf+1, (p->local_as < 0xFFFF) ? p->local_as : AS_TRANS);
+ put_u16(buf+1, (p->public_as < 0xFFFF) ? p->public_as : AS_TRANS);
put_u16(buf+3, p->cf->hold_time);
put_u32(buf+5, p->local_id);
- if (conn->start_state == BSS_CONNECT_NOCAP)
- {
- BGP_TRACE(D_PACKETS, "Skipping capabilities");
- buf[9] = 0;
- return buf + 10;
- }
+ if (p->cf->capabilities)
+ {
+ /* Prepare local_caps and write capabilities to buffer */
+ byte *end = bgp_write_capabilities(conn, buf+12);
+ uint len = end - (buf+12);
+
+ buf[9] = len + 2; /* Optional parameters length */
+ buf[10] = 2; /* Option 2: Capability list */
+ buf[11] = len; /* Option data length */
+
+ return end;
+ }
+ else
+ {
+ /* Prepare empty local_caps */
+ conn->local_caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps));
+
+ buf[9] = 0; /* No optional parameters */
+ return buf + 10;
+ }
+
+ return buf;
+}
+
+static void
+bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
+{
+ struct bgp_proto *p = conn->bgp;
+ struct bgp_conn *other;
+ u32 asn, hold, id;
- /* Skipped 3 B for length field and Capabilities parameter header */
- cap = buf + 12;
+ /* Check state */
+ if (conn->state != BS_OPENSENT)
+ { bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0); return; }
+
+ /* Check message contents */
+ if (len < 29 || len != 29 + (uint) pkt[28])
+ { bgp_error(conn, 1, 2, pkt+16, 2); return; }
-#ifndef IPV6
- if (p->cf->advertise_ipv4)
- cap = bgp_put_cap_ipv4(p, cap);
-#endif
+ if (pkt[19] != BGP_VERSION)
+ { u16 val = BGP_VERSION; bgp_error(conn, 2, 1, (byte *) &val, 2); return; }
-#ifdef IPV6
- cap = bgp_put_cap_ipv6(p, cap);
-#endif
+ asn = get_u16(pkt+20);
+ hold = get_u16(pkt+22);
+ id = get_u32(pkt+24);
+ BGP_TRACE(D_PACKETS, "Got OPEN(as=%d,hold=%d,id=%R)", asn, hold, id);
- if (p->cf->enable_refresh)
- cap = bgp_put_cap_rr(p, cap);
+ if (bgp_read_options(conn, pkt+29, pkt[28]) < 0)
+ return;
- if (p->cf->gr_mode == BGP_GR_ABLE)
- cap = bgp_put_cap_gr1(p, cap);
- else if (p->cf->gr_mode == BGP_GR_AWARE)
- cap = bgp_put_cap_gr2(p, cap);
+ if (hold > 0 && hold < 3)
+ { bgp_error(conn, 2, 6, pkt+22, 2); return; }
- if (p->cf->enable_as4)
- cap = bgp_put_cap_as4(p, cap);
+ /* RFC 6286 2.2 - router ID is nonzero and AS-wide unique */
+ if (!id || (p->is_internal && id == p->local_id))
+ { bgp_error(conn, 2, 3, pkt+24, -4); return; }
- if (p->cf->add_path)
- cap = bgp_put_cap_add_path(p, cap);
+ struct bgp_caps *caps = conn->remote_caps;
- if (p->cf->enable_refresh)
- cap = bgp_put_cap_err(p, cap);
+ if (caps->as4_support)
+ {
+ u32 as4 = caps->as4_number;
- if (p->cf->enable_extended_messages)
- cap = bgp_put_cap_ext_msg(p, cap);
+ if ((as4 != asn) && (asn != AS_TRANS))
+ log(L_WARN "%s: Peer advertised inconsistent AS numbers", p->p.name);
- cap_len = cap - buf - 12;
- if (cap_len > 0)
- {
- buf[9] = cap_len + 2; /* Optional params len */
- buf[10] = 2; /* Option: Capability list */
- buf[11] = cap_len; /* Option length */
- return cap;
- }
+ if (as4 != p->remote_as)
+ { as4 = htonl(as4); bgp_error(conn, 2, 2, (byte *) &as4, 4); return; }
+ }
else
+ {
+ if (asn != p->remote_as)
+ { bgp_error(conn, 2, 2, pkt+20, 2); return; }
+ }
+
+ /* Check the other connection */
+ other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn;
+ switch (other->state)
+ {
+ case BS_CONNECT:
+ case BS_ACTIVE:
+ /* Stop outgoing connection attempts */
+ bgp_conn_enter_idle_state(other);
+ break;
+
+ case BS_IDLE:
+ case BS_OPENSENT:
+ case BS_CLOSE:
+ break;
+
+ case BS_OPENCONFIRM:
+ /*
+ * Description of collision detection rules in RFC 4271 is confusing and
+ * contradictory, but it is essentially:
+ *
+ * 1. Router with higher ID is dominant
+ * 2. If both have the same ID, router with higher ASN is dominant [RFC6286]
+ * 3. When both connections are in OpenConfirm state, one initiated by
+ * the dominant router is kept.
+ *
+ * The first line in the expression below evaluates whether the neighbor
+ * is dominant, the second line whether the new connection was initiated
+ * by the neighbor. If both are true (or both are false), we keep the new
+ * connection, otherwise we keep the old one.
+ */
+ if (((p->local_id < id) || ((p->local_id == id) && (p->public_as < p->remote_as)))
+ == (conn == &p->incoming_conn))
{
- buf[9] = 0; /* No optional parameters */
- return buf + 10;
+ /* Should close the other connection */
+ BGP_TRACE(D_EVENTS, "Connection collision, giving up the other connection");
+ bgp_error(other, 6, 7, NULL, 0);
+ break;
}
+ /* Fall thru */
+ case BS_ESTABLISHED:
+ /* Should close this connection */
+ BGP_TRACE(D_EVENTS, "Connection collision, giving up this connection");
+ bgp_error(conn, 6, 7, NULL, 0);
+ return;
+
+ default:
+ bug("bgp_rx_open: Unknown state");
+ }
+
+ /* Update our local variables */
+ conn->hold_time = MIN(hold, p->cf->hold_time);
+ conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3;
+ conn->as4_session = conn->local_caps->as4_support && caps->as4_support;
+ conn->ext_messages = conn->local_caps->ext_messages && caps->ext_messages;
+ p->remote_id = id;
+
+ DBG("BGP: Hold timer set to %d, keepalive to %d, AS to %d, ID to %x, AS4 session to %d\n",
+ conn->hold_time, conn->keepalive_time, p->remote_as, p->remote_id, conn->as4_session);
+
+ bgp_schedule_packet(conn, NULL, PKT_KEEPALIVE);
+ bgp_start_timer(conn->hold_timer, conn->hold_time);
+ bgp_conn_enter_openconfirm_state(conn);
}
-static uint
-bgp_encode_prefixes(struct bgp_proto *p, byte *w, struct bgp_bucket *buck, uint remains)
+
+/*
+ * Next hop handling
+ */
+
+#define REPORT(msg, args...) \
+ ({ log(L_REMOTE "%s: " msg, s->proto->p.name, ## args); })
+
+#define DISCARD(msg, args...) \
+ ({ REPORT(msg, ## args); return; })
+
+#define WITHDRAW(msg, args...) \
+ ({ REPORT(msg, ## args); s->err_withdraw = 1; return; })
+
+#define BAD_AFI "Unexpected AF <%u/%u> in UPDATE"
+#define BAD_NEXT_HOP "Invalid NEXT_HOP attribute"
+#define NO_NEXT_HOP "Missing NEXT_HOP attribute"
+#define NO_LABEL_STACK "Missing MPLS stack"
+
+
+static void
+bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
{
- byte *start = w;
- ip_addr a;
- int bytes;
+ struct bgp_proto *p = s->proto;
+ struct bgp_channel *c = s->channel;
- while (!EMPTY_LIST(buck->prefixes) && (remains >= (5+sizeof(ip_addr))))
- {
- struct bgp_prefix *px = SKIP_BACK(struct bgp_prefix, bucket_node, HEAD(buck->prefixes));
- DBG("\tDequeued route %I/%d\n", px->n.prefix, px->n.pxlen);
+ if (c->cf->gw_mode == GW_DIRECT)
+ {
+ neighbor *nbr = NULL;
- if (p->add_path_tx)
- {
- put_u32(w, px->path_id);
- w += 4;
- remains -= 4;
- }
+ /* GW_DIRECT -> single_hop -> p->neigh != NULL */
+ if (ipa_nonzero(gw))
+ nbr = neigh_find2(&p->p, &gw, NULL, 0);
+ else if (ipa_nonzero(ll))
+ nbr = neigh_find2(&p->p, &ll, p->neigh->iface, 0);
- *w++ = px->n.pxlen;
- bytes = (px->n.pxlen + 7) / 8;
- a = px->n.prefix;
- ipa_hton(a);
- memcpy(w, &a, bytes);
- w += bytes;
- remains -= bytes + 1;
- rem_node(&px->bucket_node);
- bgp_free_prefix(p, px);
- // fib_delete(&p->prefix_fib, px);
- }
- return w - start;
+ if (!nbr || (nbr->scope == SCOPE_HOST))
+ WITHDRAW(BAD_NEXT_HOP);
+
+ a->dest = RTD_UNICAST;
+ a->nh.gw = nbr->addr;
+ a->nh.iface = nbr->iface;
+ }
+ else /* GW_RECURSIVE */
+ {
+ if (ipa_zero(gw))
+ WITHDRAW(BAD_NEXT_HOP);
+
+ rtable *tab = ipa_is_ip4(gw) ? c->igp_table_ip4 : c->igp_table_ip6;
+ s->hostentry = rt_get_hostentry(tab, gw, ll, c->c.table);
+
+ if (!s->mpls)
+ rta_apply_hostentry(a, s->hostentry, NULL);
+
+ /* With MPLS, hostentry is applied later in bgp_apply_mpls_labels() */
+ }
}
static void
-bgp_flush_prefixes(struct bgp_proto *p, struct bgp_bucket *buck)
+bgp_apply_mpls_labels(struct bgp_parse_state *s, rta *a, u32 *labels, uint lnum)
{
- while (!EMPTY_LIST(buck->prefixes))
- {
- struct bgp_prefix *px = SKIP_BACK(struct bgp_prefix, bucket_node, HEAD(buck->prefixes));
- log(L_ERR "%s: - route %I/%d skipped", p->p.name, px->n.prefix, px->n.pxlen);
- rem_node(&px->bucket_node);
- bgp_free_prefix(p, px);
- // fib_delete(&p->prefix_fib, px);
- }
+ if (lnum > MPLS_MAX_LABEL_STACK)
+ {
+ REPORT("Too many MPLS labels ($u)", lnum);
+
+ a->dest = RTD_UNREACHABLE;
+ a->hostentry = NULL;
+ a->nh = (struct nexthop) { };
+ return;
+ }
+
+ /* Handle implicit NULL as empty MPLS stack */
+ if ((lnum == 1) && (labels[0] == BGP_MPLS_NULL))
+ lnum = 0;
+
+ if (s->channel->cf->gw_mode == GW_DIRECT)
+ {
+ a->nh.labels = lnum;
+ memcpy(a->nh.label, labels, 4*lnum);
+ }
+ else /* GW_RECURSIVE */
+ {
+ mpls_label_stack ms;
+
+ ms.len = lnum;
+ memcpy(ms.stack, labels, 4*lnum);
+ rta_apply_hostentry(a, s->hostentry, &ms);
+ }
}
-#ifndef IPV6 /* IPv4 version */
-static byte *
-bgp_create_update(struct bgp_conn *conn, byte *buf)
+static inline int
+bgp_use_next_hop(struct bgp_export_state *s, eattr *a)
{
- struct bgp_proto *p = conn->bgp;
- struct bgp_bucket *buck;
- int remains = bgp_max_packet_length(p) - BGP_HEADER_LENGTH - 4;
- byte *w;
- int wd_size = 0;
- int r_size = 0;
- int a_size = 0;
-
- w = buf+2;
- if ((buck = p->withdraw_bucket) && !EMPTY_LIST(buck->prefixes))
- {
- DBG("Withdrawn routes:\n");
- wd_size = bgp_encode_prefixes(p, w, buck, remains);
- w += wd_size;
- remains -= wd_size;
- }
- put_u16(buf, wd_size);
+ struct bgp_proto *p = s->proto;
+ ip_addr *nh = (void *) a->u.ptr->data;
- if (!wd_size)
- {
- while ((buck = (struct bgp_bucket *) HEAD(p->bucket_queue))->send_node.next)
- {
- if (EMPTY_LIST(buck->prefixes))
- {
- DBG("Deleting empty bucket %p\n", buck);
- rem_node(&buck->send_node);
- bgp_free_bucket(p, buck);
- continue;
- }
-
- DBG("Processing bucket %p\n", buck);
- a_size = bgp_encode_attrs(p, w+2, buck->eattrs, remains - 1024);
-
- if (a_size < 0)
- {
- log(L_ERR "%s: Attribute list too long, skipping corresponding routes", p->p.name);
- bgp_flush_prefixes(p, buck);
- rem_node(&buck->send_node);
- bgp_free_bucket(p, buck);
- continue;
- }
-
- put_u16(w, a_size);
- w += a_size + 2;
- r_size = bgp_encode_prefixes(p, w, buck, remains - a_size);
- w += r_size;
- break;
- }
- }
- if (!a_size) /* Attributes not already encoded */
+ if (s->channel->cf->next_hop_self)
+ return 0;
+
+ if (s->channel->cf->next_hop_keep)
+ return 1;
+
+ /* Keep it when explicitly set in export filter */
+ if (a->type & EAF_FRESH)
+ return 1;
+
+ /* Keep it when exported to internal peers */
+ if (p->is_interior && ipa_nonzero(*nh))
+ return 1;
+
+ /* Keep it when forwarded between single-hop BGPs on the same iface */
+ struct iface *ifa = (s->src && s->src->neigh) ? s->src->neigh->iface : NULL;
+ return p->neigh && (p->neigh->iface == ifa);
+}
+
+static inline int
+bgp_use_gateway(struct bgp_export_state *s)
+{
+ struct bgp_proto *p = s->proto;
+ rta *ra = s->route->attrs;
+
+ if (s->channel->cf->next_hop_self)
+ return 0;
+
+ /* We need one valid global gateway */
+ if ((ra->dest != RTD_UNICAST) || ra->nh.next || ipa_zero(ra->nh.gw) || ipa_is_link_local(ra->nh.gw))
+ return 0;
+
+ /* Use it when exported to internal peers */
+ if (p->is_interior)
+ return 1;
+
+ /* Use it when forwarded to single-hop BGP peer on on the same iface */
+ return p->neigh && (p->neigh->iface == ra->nh.iface);
+}
+
+static void
+bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
+{
+ if (!a || !bgp_use_next_hop(s, a))
+ {
+ if (bgp_use_gateway(s))
{
- put_u16(w, 0);
- w += 2;
+ rta *ra = s->route->attrs;
+ ip_addr nh[1] = { ra->nh.gw };
+ bgp_set_attr_data(to, s->pool, BA_NEXT_HOP, 0, nh, 16);
+
+ if (s->mpls)
+ {
+ u32 implicit_null = BGP_MPLS_NULL;
+ u32 *labels = ra->nh.labels ? ra->nh.label : &implicit_null;
+ uint lnum = ra->nh.labels ? ra->nh.labels : 1;
+ bgp_set_attr_data(to, s->pool, BA_MPLS_LABEL_STACK, 0, labels, lnum * 4);
+ }
}
- if (wd_size || r_size)
+ else
{
- BGP_TRACE_RL(&rl_snd_update, D_PACKETS, "Sending UPDATE");
- return w;
+ ip_addr nh[2] = { s->channel->next_hop_addr, s->channel->link_addr };
+ bgp_set_attr_data(to, s->pool, BA_NEXT_HOP, 0, nh, ipa_nonzero(nh[1]) ? 32 : 16);
+
+ /* TODO: Use local MPLS assigned label */
+ if (s->mpls)
+ bgp_unset_attr(to, s->pool, BA_MPLS_LABEL_STACK);
}
+ }
+
+ /* Check if next hop is valid */
+ a = bgp_find_attr(*to, BA_NEXT_HOP);
+ if (!a)
+ WITHDRAW(NO_NEXT_HOP);
+
+ ip_addr *nh = (void *) a->u.ptr->data;
+ ip_addr peer = s->proto->cf->remote_ip;
+ uint len = a->u.ptr->length;
+
+ /* Forbid zero next hop */
+ if (ipa_zero(nh[0]) && ((len != 32) || ipa_zero(nh[1])))
+ WITHDRAW(BAD_NEXT_HOP);
+
+ /* Forbid next hop equal to neighbor IP */
+ if (ipa_equal(peer, nh[0]) || ((len == 32) && ipa_equal(peer, nh[1])))
+ WITHDRAW(BAD_NEXT_HOP);
+
+ /* Forbid next hop with non-matching AF */
+ if ((ipa_is_ip4(nh[0]) != bgp_channel_is_ipv4(s->channel)) &&
+ !s->channel->ext_next_hop)
+ WITHDRAW(BAD_NEXT_HOP);
+
+ /* Just check if MPLS stack */
+ if (s->mpls && !bgp_find_attr(*to, BA_MPLS_LABEL_STACK))
+ WITHDRAW(NO_LABEL_STACK);
+}
+
+static uint
+bgp_encode_next_hop_ip(struct bgp_write_state *s, eattr *a, byte *buf, uint size UNUSED)
+{
+ /* This function is used only for MP-BGP, see bgp_encode_next_hop() for IPv4 BGP */
+ ip_addr *nh = (void *) a->u.ptr->data;
+ uint len = a->u.ptr->length;
+
+ ASSERT((len == 16) || (len == 32));
+
+ /*
+ * Both IPv4 and IPv6 next hops can be used (with ext_next_hop enabled). This
+ * is specified in RFC 5549 for IPv4 and in RFC 4798 for IPv6. The difference
+ * is that IPv4 address is directly encoded with IPv4 NLRI, but as IPv4-mapped
+ * IPv6 address with IPv6 NLRI.
+ */
+
+ if (bgp_channel_is_ipv4(s->channel) && ipa_is_ip4(nh[0]))
+ {
+ put_ip4(buf, ipa_to_ip4(nh[0]));
+ return 4;
+ }
+
+ put_ip6(buf, ipa_to_ip6(nh[0]));
+
+ if (len == 32)
+ put_ip6(buf+16, ipa_to_ip6(nh[1]));
+
+ return len;
+}
+
+static void
+bgp_decode_next_hop_ip(struct bgp_parse_state *s, byte *data, uint len, rta *a)
+{
+ struct bgp_channel *c = s->channel;
+ struct adata *ad = lp_alloc_adata(s->pool, 32);
+ ip_addr *nh = (void *) ad->data;
+
+ if (len == 4)
+ {
+ nh[0] = ipa_from_ip4(get_ip4(data));
+ nh[1] = IPA_NONE;
+ }
+ else if (len == 16)
+ {
+ nh[0] = ipa_from_ip6(get_ip6(data));
+ nh[1] = IPA_NONE;
+
+ if (ipa_is_link_local(nh[0]))
+ { nh[1] = nh[0]; nh[0] = IPA_NONE; }
+ }
+ else if (len == 32)
+ {
+ nh[0] = ipa_from_ip6(get_ip6(data));
+ nh[1] = ipa_from_ip6(get_ip6(data+16));
+
+ if (ipa_is_ip4(nh[0]) || !ip6_is_link_local(nh[1]))
+ nh[1] = IPA_NONE;
+ }
else
- return NULL;
+ bgp_parse_error(s, 9);
+
+ if (ipa_zero(nh[1]))
+ ad->length = 16;
+
+ if ((bgp_channel_is_ipv4(c) != ipa_is_ip4(nh[0])) && !c->ext_next_hop)
+ WITHDRAW(BAD_NEXT_HOP);
+
+ // XXXX validate next hop
+
+ bgp_set_attr_ptr(&(a->eattrs), s->pool, BA_NEXT_HOP, 0, ad);
+ bgp_apply_next_hop(s, a, nh[0], nh[1]);
}
-static byte *
-bgp_create_end_mark(struct bgp_conn *conn, byte *buf)
+static uint
+bgp_encode_next_hop_vpn(struct bgp_write_state *s, eattr *a, byte *buf, uint size UNUSED)
{
- struct bgp_proto *p = conn->bgp;
- BGP_TRACE(D_PACKETS, "Sending END-OF-RIB");
+ ip_addr *nh = (void *) a->u.ptr->data;
+ uint len = a->u.ptr->length;
- put_u32(buf, 0);
- return buf+4;
+ ASSERT((len == 16) || (len == 32));
+
+ /*
+ * Both IPv4 and IPv6 next hops can be used (with ext_next_hop enabled). This
+ * is specified in RFC 5549 for VPNv4 and in RFC 4659 for VPNv6. The difference
+ * is that IPv4 address is directly encoded with VPNv4 NLRI, but as IPv4-mapped
+ * IPv6 address with VPNv6 NLRI.
+ */
+
+ if (bgp_channel_is_ipv4(s->channel) && ipa_is_ip4(nh[0]))
+ {
+ put_u64(buf, 0); /* VPN RD is 0 */
+ put_ip4(buf+8, ipa_to_ip4(nh[0]));
+ return 12;
+ }
+
+ put_u64(buf, 0); /* VPN RD is 0 */
+ put_ip6(buf+8, ipa_to_ip6(nh[0]));
+
+ if (len == 16)
+ return 24;
+
+ put_u64(buf+24, 0); /* VPN RD is 0 */
+ put_ip6(buf+32, ipa_to_ip6(nh[1]));
+
+ return 48;
}
-#else /* IPv6 version */
+static void
+bgp_decode_next_hop_vpn(struct bgp_parse_state *s, byte *data, uint len, rta *a)
+{
+ struct bgp_channel *c = s->channel;
+ struct adata *ad = lp_alloc_adata(s->pool, 32);
+ ip_addr *nh = (void *) ad->data;
-static inline int
-same_iface(struct bgp_proto *p, ip_addr *ip)
+ if (len == 12)
+ {
+ nh[0] = ipa_from_ip4(get_ip4(data+8));
+ nh[1] = IPA_NONE;
+ }
+ else if (len == 24)
+ {
+ nh[0] = ipa_from_ip6(get_ip6(data+8));
+ nh[1] = IPA_NONE;
+
+ if (ipa_is_link_local(nh[0]))
+ { nh[1] = nh[0]; nh[0] = IPA_NONE; }
+ }
+ else if (len == 48)
+ {
+ nh[0] = ipa_from_ip6(get_ip6(data+8));
+ nh[1] = ipa_from_ip6(get_ip6(data+32));
+
+ if (ipa_is_ip4(nh[0]) || !ip6_is_link_local(nh[1]))
+ nh[1] = IPA_NONE;
+ }
+ else
+ bgp_parse_error(s, 9);
+
+ if (ipa_zero(nh[1]))
+ ad->length = 16;
+
+ /* XXXX which error */
+ if ((get_u64(data) != 0) || ((len == 48) && (get_u64(data+24) != 0)))
+ bgp_parse_error(s, 9);
+
+ if ((bgp_channel_is_ipv4(c) != ipa_is_ip4(nh[0])) && !c->ext_next_hop)
+ WITHDRAW(BAD_NEXT_HOP);
+
+ // XXXX validate next hop
+
+ bgp_set_attr_ptr(&(a->eattrs), s->pool, BA_NEXT_HOP, 0, ad);
+ bgp_apply_next_hop(s, a, nh[0], nh[1]);
+}
+
+
+
+static uint
+bgp_encode_next_hop_none(struct bgp_write_state *s UNUSED, eattr *a UNUSED, byte *buf UNUSED, uint size UNUSED)
{
- neighbor *n = neigh_find(&p->p, ip, 0);
- return n && p->neigh && n->iface == p->neigh->iface;
+ return 0;
}
-static byte *
-bgp_create_update(struct bgp_conn *conn, byte *buf)
+static void
+bgp_decode_next_hop_none(struct bgp_parse_state *s UNUSED, byte *data UNUSED, uint len UNUSED, rta *a UNUSED)
{
- struct bgp_proto *p = conn->bgp;
- struct bgp_bucket *buck;
- int size, second, rem_stored;
- int remains = bgp_max_packet_length(p) - BGP_HEADER_LENGTH - 4;
- byte *w, *w_stored, *tmp, *tstart;
- ip_addr *ipp, ip, ip_ll;
- ea_list *ea;
- eattr *nh;
+ /*
+ * Although we expect no next hop and RFC 7606 7.11 states that attribute
+ * MP_REACH_NLRI with unexpected next hop length is considered malformed,
+ * FlowSpec RFC 5575 4 states that next hop shall be ignored on receipt.
+ */
+
+ return;
+}
- put_u16(buf, 0);
- w = buf+4;
+static void
+bgp_update_next_hop_none(struct bgp_export_state *s, eattr *a, ea_list **to)
+{
+ /* NEXT_HOP shall not pass */
+ if (a)
+ bgp_unset_attr(to, s->pool, BA_NEXT_HOP);
+}
+
+
+/*
+ * UPDATE
+ */
+
+static void
+bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
+{
+ if (path_id != s->last_id)
+ {
+ s->last_src = rt_get_source(&s->proto->p, path_id);
+ s->last_id = path_id;
- if ((buck = p->withdraw_bucket) && !EMPTY_LIST(buck->prefixes))
+ rta_free(s->cached_rta);
+ s->cached_rta = NULL;
+ }
+
+ if (!a0)
+ {
+ /* Route withdraw */
+ rte_update2(&s->channel->c, n, NULL, s->last_src);
+ return;
+ }
+
+ /* Prepare cached route attributes */
+ if (s->cached_rta == NULL)
+ {
+ a0->src = s->last_src;
+
+ /* Workaround for rta_lookup() breaking eattrs */
+ ea_list *ea = a0->eattrs;
+ s->cached_rta = rta_lookup(a0);
+ a0->eattrs = ea;
+ }
+
+ rta *a = rta_clone(s->cached_rta);
+ rte *e = rte_get_temp(a);
+
+ e->pflags = 0;
+ e->u.bgp.suppressed = 0;
+ rte_update2(&s->channel->c, n, e, s->last_src);
+}
+
+static void
+bgp_encode_mpls_labels(struct bgp_write_state *s UNUSED, adata *mpls, byte **pos, uint *size, byte *pxlen)
+{
+ u32 dummy = 0;
+ u32 *labels = mpls ? (u32 *) mpls->data : &dummy;
+ uint lnum = mpls ? (mpls->length / 4) : 1;
+
+ for (uint i = 0; i < lnum; i++)
+ {
+ put_u24(*pos, labels[i] << 4);
+ ADVANCE(*pos, *size, 3);
+ }
+
+ /* Add bottom-of-stack flag */
+ (*pos)[-1] |= BGP_MPLS_BOS;
+
+ *pxlen += 24 * lnum;
+}
+
+static void
+bgp_decode_mpls_labels(struct bgp_parse_state *s, byte **pos, uint *len, uint *pxlen, rta *a)
+{
+ u32 labels[BGP_MPLS_MAX], label;
+ uint lnum = 0;
+
+ do {
+ if (*pxlen < 24)
+ bgp_parse_error(s, 1);
+
+ label = get_u24(*pos);
+ labels[lnum++] = label >> 4;
+ ADVANCE(*pos, *len, 3);
+ *pxlen -= 24;
+
+ /* Withdraw: Magic label stack value 0x800000 according to RFC 3107, section 3, last paragraph */
+ if (!a && !s->err_withdraw && (lnum == 1) && (label == BGP_MPLS_MAGIC))
+ break;
+ }
+ while (!(label & BGP_MPLS_BOS));
+
+ if (!a)
+ return;
+
+ /* Attach MPLS attribute unless we already have one */
+ if (!s->mpls_labels)
+ {
+ s->mpls_labels = lp_alloc_adata(s->pool, 4*BGP_MPLS_MAX);
+ bgp_set_attr_ptr(&(a->eattrs), s->pool, BA_MPLS_LABEL_STACK, 0, s->mpls_labels);
+ }
+
+ /* Overwrite data in the attribute */
+ s->mpls_labels->length = 4*lnum;
+ memcpy(s->mpls_labels->data, labels, 4*lnum);
+
+ /* Update next hop entry in rta */
+ bgp_apply_mpls_labels(s, a, labels, lnum);
+
+ /* Attributes were changed, invalidate cached entry */
+ rta_free(s->cached_rta);
+ s->cached_rta = NULL;
+
+ return;
+}
+
+static uint
+bgp_encode_nlri_ip4(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
+{
+ byte *pos = buf;
+
+ while (!EMPTY_LIST(buck->prefixes) && (size >= BGP_NLRI_MAX))
+ {
+ struct bgp_prefix *px = HEAD(buck->prefixes);
+ struct net_addr_ip4 *net = (void *) px->net;
+
+ /* Encode path ID */
+ if (s->add_path)
{
- DBG("Withdrawn routes:\n");
- tmp = bgp_attach_attr_wa(&ea, bgp_linpool, BA_MP_UNREACH_NLRI, remains-8);
- *tmp++ = 0;
- *tmp++ = BGP_AF_IPV6;
- *tmp++ = 1;
- ea->attrs[0].u.ptr->length = 3 + bgp_encode_prefixes(p, tmp, buck, remains-11);
- size = bgp_encode_attrs(p, w, ea, remains);
- ASSERT(size >= 0);
- w += size;
- remains -= size;
+ put_u32(pos, px->path_id);
+ ADVANCE(pos, size, 4);
}
- else
+
+ /* Encode prefix length */
+ *pos = net->pxlen;
+ ADVANCE(pos, size, 1);
+
+ /* Encode MPLS labels */
+ if (s->mpls)
+ bgp_encode_mpls_labels(s, s->mpls_labels, &pos, &size, pos - 1);
+
+ /* Encode prefix body */
+ ip4_addr a = ip4_hton(net->prefix);
+ uint b = (net->pxlen + 7) / 8;
+ memcpy(pos, &a, b);
+ ADVANCE(pos, size, b);
+
+ bgp_free_prefix(s->channel, px);
+ }
+
+ return pos - buf;
+}
+
+static void
+bgp_decode_nlri_ip4(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
+{
+ while (len)
+ {
+ net_addr_ip4 net;
+ u32 path_id = 0;
+
+ /* Decode path ID */
+ if (s->add_path)
{
- while ((buck = (struct bgp_bucket *) HEAD(p->bucket_queue))->send_node.next)
- {
- if (EMPTY_LIST(buck->prefixes))
- {
- DBG("Deleting empty bucket %p\n", buck);
- rem_node(&buck->send_node);
- bgp_free_bucket(p, buck);
- continue;
- }
-
- DBG("Processing bucket %p\n", buck);
- rem_stored = remains;
- w_stored = w;
-
- size = bgp_encode_attrs(p, w, buck->eattrs, remains - 1024);
- if (size < 0)
- {
- log(L_ERR "%s: Attribute list too long, skipping corresponding routes", p->p.name);
- bgp_flush_prefixes(p, buck);
- rem_node(&buck->send_node);
- bgp_free_bucket(p, buck);
- continue;
- }
- w += size;
- remains -= size;
-
- /* We have two addresses here in NEXT_HOP eattr. Really.
- Unless NEXT_HOP was modified by filter */
- nh = ea_find(buck->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
- ASSERT(nh);
- second = (nh->u.ptr->length == NEXT_HOP_LENGTH);
- ipp = (ip_addr *) nh->u.ptr->data;
- ip = ipp[0];
- ip_ll = IPA_NONE;
-
- if (ipa_equal(ip, p->source_addr))
- ip_ll = p->local_link;
- else
- {
- /* If we send a route with 'third party' next hop destinated
- * in the same interface, we should also send a link local
- * next hop address. We use the received one (stored in the
- * other part of BA_NEXT_HOP eattr). If we didn't received
- * it (for example it is a static route), we can't use
- * 'third party' next hop and we have to use local IP address
- * as next hop. Sending original next hop address without
- * link local address seems to be a natural way to solve that
- * problem, but it is contrary to RFC 2545 and Quagga does not
- * accept such routes.
- *
- * There are two cases, either we have global IP, or
- * IPA_NONE if the neighbor is link-local. For IPA_NONE,
- * we suppose it is on the same iface, see bgp_update_attrs().
- */
-
- if (ipa_zero(ip) || same_iface(p, &ip))
- {
- if (second && ipa_nonzero(ipp[1]))
- ip_ll = ipp[1];
- else
- {
- switch (p->cf->missing_lladdr)
- {
- case MLL_SELF:
- ip = p->source_addr;
- ip_ll = p->local_link;
- break;
- case MLL_DROP:
- log(L_ERR "%s: Missing link-local next hop address, skipping corresponding routes", p->p.name);
- w = w_stored;
- remains = rem_stored;
- bgp_flush_prefixes(p, buck);
- rem_node(&buck->send_node);
- bgp_free_bucket(p, buck);
- continue;
- case MLL_IGNORE:
- break;
- }
- }
- }
- }
-
- tstart = tmp = bgp_attach_attr_wa(&ea, bgp_linpool, BA_MP_REACH_NLRI, remains-8);
- *tmp++ = 0;
- *tmp++ = BGP_AF_IPV6;
- *tmp++ = 1;
-
- if (ipa_is_link_local(ip))
- ip = IPA_NONE;
-
- if (ipa_nonzero(ip_ll))
- {
- *tmp++ = 32;
- ipa_hton(ip);
- memcpy(tmp, &ip, 16);
- ipa_hton(ip_ll);
- memcpy(tmp+16, &ip_ll, 16);
- tmp += 32;
- }
- else
- {
- *tmp++ = 16;
- ipa_hton(ip);
- memcpy(tmp, &ip, 16);
- tmp += 16;
- }
-
- *tmp++ = 0; /* No SNPA information */
- tmp += bgp_encode_prefixes(p, tmp, buck, remains - (8+3+32+1));
- ea->attrs[0].u.ptr->length = tmp - tstart;
- size = bgp_encode_attrs(p, w, ea, remains);
- ASSERT(size >= 0);
- w += size;
- break;
- }
+ if (len < 5)
+ bgp_parse_error(s, 1);
+
+ path_id = get_u32(pos);
+ ADVANCE(pos, len, 4);
}
- size = w - (buf+4);
- put_u16(buf+2, size);
- lp_flush(bgp_linpool);
- if (size)
+ /* Decode prefix length */
+ uint l = *pos;
+ ADVANCE(pos, len, 1);
+
+ if (len < ((l + 7) / 8))
+ bgp_parse_error(s, 1);
+
+ /* Decode MPLS labels */
+ if (s->mpls)
+ bgp_decode_mpls_labels(s, &pos, &len, &l, a);
+
+ if (l > IP4_MAX_PREFIX_LENGTH)
+ bgp_parse_error(s, 10);
+
+ /* Decode prefix body */
+ ip4_addr addr = IP4_NONE;
+ uint b = (l + 7) / 8;
+ memcpy(&addr, pos, b);
+ ADVANCE(pos, len, b);
+
+ net = NET_ADDR_IP4(ip4_ntoh(addr), l);
+ net_normalize_ip4(&net);
+
+ // XXXX validate prefix
+
+ bgp_rte_update(s, (net_addr *) &net, path_id, a);
+ }
+}
+
+
+static uint
+bgp_encode_nlri_ip6(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
+{
+ byte *pos = buf;
+
+ while (!EMPTY_LIST(buck->prefixes) && (size >= BGP_NLRI_MAX))
+ {
+ struct bgp_prefix *px = HEAD(buck->prefixes);
+ struct net_addr_ip6 *net = (void *) px->net;
+
+ /* Encode path ID */
+ if (s->add_path)
{
- BGP_TRACE_RL(&rl_snd_update, D_PACKETS, "Sending UPDATE");
- return w;
+ put_u32(pos, px->path_id);
+ ADVANCE(pos, size, 4);
}
- else
- return NULL;
+
+ /* Encode prefix length */
+ *pos = net->pxlen;
+ ADVANCE(pos, size, 1);
+
+ /* Encode MPLS labels */
+ if (s->mpls)
+ bgp_encode_mpls_labels(s, s->mpls_labels, &pos, &size, pos - 1);
+
+ /* Encode prefix body */
+ ip6_addr a = ip6_hton(net->prefix);
+ uint b = (net->pxlen + 7) / 8;
+ memcpy(pos, &a, b);
+ ADVANCE(pos, size, b);
+
+ bgp_free_prefix(s->channel, px);
+ }
+
+ return pos - buf;
}
-static byte *
-bgp_create_end_mark(struct bgp_conn *conn, byte *buf)
+static void
+bgp_decode_nlri_ip6(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
{
- struct bgp_proto *p = conn->bgp;
- BGP_TRACE(D_PACKETS, "Sending END-OF-RIB");
+ while (len)
+ {
+ net_addr_ip6 net;
+ u32 path_id = 0;
- put_u16(buf+0, 0);
- put_u16(buf+2, 6); /* length 4-9 */
- buf += 4;
+ /* Decode path ID */
+ if (s->add_path)
+ {
+ if (len < 5)
+ bgp_parse_error(s, 1);
- /* Empty MP_UNREACH_NLRI atribute */
- *buf++ = BAF_OPTIONAL;
- *buf++ = BA_MP_UNREACH_NLRI;
- *buf++ = 3; /* Length 7-9 */
- *buf++ = 0; /* AFI */
- *buf++ = BGP_AF_IPV6;
- *buf++ = 1; /* SAFI */
- return buf;
-}
+ path_id = get_u32(pos);
+ ADVANCE(pos, len, 4);
+ }
-#endif
+ /* Decode prefix length */
+ uint l = *pos;
+ ADVANCE(pos, len, 1);
-static inline byte *
-bgp_create_route_refresh(struct bgp_conn *conn, byte *buf)
-{
- struct bgp_proto *p = conn->bgp;
- BGP_TRACE(D_PACKETS, "Sending ROUTE-REFRESH");
+ if (len < ((l + 7) / 8))
+ bgp_parse_error(s, 1);
- /* Original original route refresh request, RFC 2918 */
- *buf++ = 0;
- *buf++ = BGP_AF;
- *buf++ = BGP_RR_REQUEST;
- *buf++ = 1; /* SAFI */
- return buf;
+ /* Decode MPLS labels */
+ if (s->mpls)
+ bgp_decode_mpls_labels(s, &pos, &len, &l, a);
+
+ if (l > IP6_MAX_PREFIX_LENGTH)
+ bgp_parse_error(s, 10);
+
+ /* Decode prefix body */
+ ip6_addr addr = IP6_NONE;
+ uint b = (l + 7) / 8;
+ memcpy(&addr, pos, b);
+ ADVANCE(pos, len, b);
+
+ net = NET_ADDR_IP6(ip6_ntoh(addr), l);
+ net_normalize_ip6(&net);
+
+ // XXXX validate prefix
+
+ bgp_rte_update(s, (net_addr *) &net, path_id, a);
+ }
}
-static inline byte *
-bgp_create_begin_refresh(struct bgp_conn *conn, byte *buf)
+static uint
+bgp_encode_nlri_vpn4(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
{
- struct bgp_proto *p = conn->bgp;
- BGP_TRACE(D_PACKETS, "Sending BEGIN-OF-RR");
+ byte *pos = buf;
- /* Demarcation of beginning of route refresh (BoRR), RFC 7313 */
- *buf++ = 0;
- *buf++ = BGP_AF;
- *buf++ = BGP_RR_BEGIN;
- *buf++ = 1; /* SAFI */
- return buf;
+ while (!EMPTY_LIST(buck->prefixes) && (size >= BGP_NLRI_MAX))
+ {
+ struct bgp_prefix *px = HEAD(buck->prefixes);
+ struct net_addr_vpn4 *net = (void *) px->net;
+
+ /* Encode path ID */
+ if (s->add_path)
+ {
+ put_u32(pos, px->path_id);
+ ADVANCE(pos, size, 4);
+ }
+
+ /* Encode prefix length */
+ *pos = 64 + net->pxlen;
+ ADVANCE(pos, size, 1);
+
+ /* Encode MPLS labels */
+ if (s->mpls)
+ bgp_encode_mpls_labels(s, s->mpls_labels, &pos, &size, pos - 1);
+
+ /* Encode route distinguisher */
+ put_u64(pos, net->rd);
+ ADVANCE(pos, size, 8);
+
+ /* Encode prefix body */
+ ip4_addr a = ip4_hton(net->prefix);
+ uint b = (net->pxlen + 7) / 8;
+ memcpy(pos, &a, b);
+ ADVANCE(pos, size, b);
+
+ bgp_free_prefix(s->channel, px);
+ }
+
+ return pos - buf;
}
-static inline byte *
-bgp_create_end_refresh(struct bgp_conn *conn, byte *buf)
+static void
+bgp_decode_nlri_vpn4(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
{
- struct bgp_proto *p = conn->bgp;
- BGP_TRACE(D_PACKETS, "Sending END-OF-RR");
+ while (len)
+ {
+ net_addr_vpn4 net;
+ u32 path_id = 0;
- /* Demarcation of ending of route refresh (EoRR), RFC 7313 */
- *buf++ = 0;
- *buf++ = BGP_AF;
- *buf++ = BGP_RR_END;
- *buf++ = 1; /* SAFI */
- return buf;
+ /* Decode path ID */
+ if (s->add_path)
+ {
+ if (len < 5)
+ bgp_parse_error(s, 1);
+
+ path_id = get_u32(pos);
+ ADVANCE(pos, len, 4);
+ }
+
+ /* Decode prefix length */
+ uint l = *pos;
+ ADVANCE(pos, len, 1);
+
+ if (len < ((l + 7) / 8))
+ bgp_parse_error(s, 1);
+
+ /* Decode MPLS labels */
+ if (s->mpls)
+ bgp_decode_mpls_labels(s, &pos, &len, &l, a);
+
+ /* Decode route distinguisher */
+ if (l < 64)
+ bgp_parse_error(s, 1);
+
+ u64 rd = get_u64(pos);
+ ADVANCE(pos, len, 8);
+ l -= 64;
+
+ if (l > IP4_MAX_PREFIX_LENGTH)
+ bgp_parse_error(s, 10);
+
+ /* Decode prefix body */
+ ip4_addr addr = IP4_NONE;
+ uint b = (l + 7) / 8;
+ memcpy(&addr, pos, b);
+ ADVANCE(pos, len, b);
+
+ net = NET_ADDR_VPN4(ip4_ntoh(addr), l, rd);
+ net_normalize_vpn4(&net);
+
+ // XXXX validate prefix
+
+ bgp_rte_update(s, (net_addr *) &net, path_id, a);
+ }
}
+static uint
+bgp_encode_nlri_vpn6(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
+{
+ byte *pos = buf;
+
+ while (!EMPTY_LIST(buck->prefixes) && (size >= BGP_NLRI_MAX))
+ {
+ struct bgp_prefix *px = HEAD(buck->prefixes);
+ struct net_addr_vpn6 *net = (void *) px->net;
+
+ /* Encode path ID */
+ if (s->add_path)
+ {
+ put_u32(pos, px->path_id);
+ ADVANCE(pos, size, 4);
+ }
+
+ /* Encode prefix length */
+ *pos = 64 + net->pxlen;
+ ADVANCE(pos, size, 1);
+
+ /* Encode MPLS labels */
+ bgp_encode_mpls_labels(s, s->mpls_labels, &pos, &size, pos - 1);
+
+ /* Encode route distinguisher */
+ put_u64(pos, net->rd);
+ ADVANCE(pos, size, 8);
+
+ /* Encode prefix body */
+ ip6_addr a = ip6_hton(net->prefix);
+ uint b = (net->pxlen + 7) / 8;
+ memcpy(pos, &a, b);
+ ADVANCE(pos, size, b);
+
+ bgp_free_prefix(s->channel, px);
+ }
+
+ return pos - buf;
+}
+
static void
-bgp_create_header(byte *buf, uint len, uint type)
+bgp_decode_nlri_vpn6(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
{
- memset(buf, 0xff, 16); /* Marker */
- put_u16(buf+16, len);
- buf[18] = type;
+ while (len)
+ {
+ net_addr_vpn6 net;
+ u32 path_id = 0;
+
+ /* Decode path ID */
+ if (s->add_path)
+ {
+ if (len < 5)
+ bgp_parse_error(s, 1);
+
+ path_id = get_u32(pos);
+ ADVANCE(pos, len, 4);
+ }
+
+ /* Decode prefix length */
+ uint l = *pos;
+ ADVANCE(pos, len, 1);
+
+ if (len < ((l + 7) / 8))
+ bgp_parse_error(s, 1);
+
+ /* Decode MPLS labels */
+ if (s->mpls)
+ bgp_decode_mpls_labels(s, &pos, &len, &l, a);
+
+ /* Decode route distinguisher */
+ if (l < 64)
+ bgp_parse_error(s, 1);
+
+ u64 rd = get_u64(pos);
+ ADVANCE(pos, len, 8);
+ l -= 64;
+
+ if (l > IP6_MAX_PREFIX_LENGTH)
+ bgp_parse_error(s, 10);
+
+ /* Decode prefix body */
+ ip6_addr addr = IP6_NONE;
+ uint b = (l + 7) / 8;
+ memcpy(&addr, pos, b);
+ ADVANCE(pos, len, b);
+
+ net = NET_ADDR_VPN6(ip6_ntoh(addr), l, rd);
+ net_normalize_vpn6(&net);
+
+ // XXXX validate prefix
+
+ bgp_rte_update(s, (net_addr *) &net, path_id, a);
+ }
}
-/**
- * bgp_fire_tx - transmit packets
- * @conn: connection
- *
- * Whenever the transmit buffers of the underlying TCP connection
- * are free and we have any packets queued for sending, the socket functions
- * call bgp_fire_tx() which takes care of selecting the highest priority packet
- * queued (Notification > Keepalive > Open > Update), assembling its header
- * and body and sending it to the connection.
- */
-static int
-bgp_fire_tx(struct bgp_conn *conn)
+
+static uint
+bgp_encode_nlri_flow4(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
{
- struct bgp_proto *p = conn->bgp;
- uint s = conn->packets_to_send;
- sock *sk = conn->sk;
- byte *buf, *pkt, *end;
- int type;
+ byte *pos = buf;
+
+ while (!EMPTY_LIST(buck->prefixes) && (size >= 4))
+ {
+ struct bgp_prefix *px = HEAD(buck->prefixes);
+ struct net_addr_flow4 *net = (void *) px->net;
+ uint flen = net->length - sizeof(net_addr_flow4);
- if (!sk)
+ /* Encode path ID */
+ if (s->add_path)
{
- conn->packets_to_send = 0;
- return 0;
+ put_u32(pos, px->path_id);
+ ADVANCE(pos, size, 4);
}
- buf = sk->tbuf;
- pkt = buf + BGP_HEADER_LENGTH;
- if (s & (1 << PKT_SCHEDULE_CLOSE))
+ if (flen > size)
+ break;
+
+ /* Copy whole flow data including length */
+ memcpy(pos, net->data, flen);
+ ADVANCE(pos, size, flen);
+
+ bgp_free_prefix(s->channel, px);
+ }
+
+ return pos - buf;
+}
+
+static void
+bgp_decode_nlri_flow4(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
+{
+ while (len)
+ {
+ u32 path_id = 0;
+
+ /* Decode path ID */
+ if (s->add_path)
{
- /* We can finally close connection and enter idle state */
- bgp_conn_enter_idle_state(conn);
- return 0;
+ if (len < 4)
+ bgp_parse_error(s, 1);
+
+ path_id = get_u32(pos);
+ ADVANCE(pos, len, 4);
}
- if (s & (1 << PKT_NOTIFICATION))
+
+ if (len < 2)
+ bgp_parse_error(s, 1);
+
+ /* Decode flow length */
+ uint hlen = flow_hdr_length(pos);
+ uint dlen = flow_read_length(pos);
+ uint flen = hlen + dlen;
+ byte *data = pos + hlen;
+
+ if (len < flen)
+ bgp_parse_error(s, 1);
+
+ /* Validate flow data */
+ enum flow_validated_state r = flow4_validate(data, dlen);
+ if (r != FLOW_ST_VALID)
{
- s = 1 << PKT_SCHEDULE_CLOSE;
- type = PKT_NOTIFICATION;
- end = bgp_create_notification(conn, pkt);
+ log(L_REMOTE "%s: Invalid flow route: %s", s->proto->p.name, flow_validated_state_str(r));
+ bgp_parse_error(s, 1);
}
- else if (s & (1 << PKT_KEEPALIVE))
+
+ if (data[0] != FLOW_TYPE_DST_PREFIX)
{
- s &= ~(1 << PKT_KEEPALIVE);
- type = PKT_KEEPALIVE;
- end = pkt; /* Keepalives carry no data */
- BGP_TRACE(D_PACKETS, "Sending KEEPALIVE");
- bgp_start_timer(conn->keepalive_timer, conn->keepalive_time);
+ log(L_REMOTE "%s: No dst prefix at first pos", s->proto->p.name);
+ bgp_parse_error(s, 1);
}
- else if (s & (1 << PKT_OPEN))
+
+ /* Decode dst prefix */
+ ip4_addr px = IP4_NONE;
+ uint pxlen = data[1];
+
+ // FIXME: Use some generic function
+ memcpy(&px, data, BYTES(pxlen));
+ px = ip4_and(px, ip4_mkmask(pxlen));
+
+ /* Prepare the flow */
+ net_addr *n = alloca(sizeof(struct net_addr_flow4) + flen);
+ net_fill_flow4(n, px, pxlen, pos, flen);
+ ADVANCE(pos, len, flen);
+
+ bgp_rte_update(s, n, path_id, a);
+ }
+}
+
+
+static uint
+bgp_encode_nlri_flow6(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
+{
+ byte *pos = buf;
+
+ while (!EMPTY_LIST(buck->prefixes) && (size >= 4))
+ {
+ struct bgp_prefix *px = HEAD(buck->prefixes);
+ struct net_addr_flow6 *net = (void *) px->net;
+ uint flen = net->length - sizeof(net_addr_flow6);
+
+ /* Encode path ID */
+ if (s->add_path)
{
- s &= ~(1 << PKT_OPEN);
- type = PKT_OPEN;
- end = bgp_create_open(conn, pkt);
+ put_u32(pos, px->path_id);
+ ADVANCE(pos, size, 4);
}
- else if (s & (1 << PKT_ROUTE_REFRESH))
+
+ if (flen > size)
+ break;
+
+ /* Copy whole flow data including length */
+ memcpy(pos, net->data, flen);
+ ADVANCE(pos, size, flen);
+
+ bgp_free_prefix(s->channel, px);
+ }
+
+ return pos - buf;
+}
+
+static void
+bgp_decode_nlri_flow6(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
+{
+ while (len)
+ {
+ u32 path_id = 0;
+
+ /* Decode path ID */
+ if (s->add_path)
{
- s &= ~(1 << PKT_ROUTE_REFRESH);
- type = PKT_ROUTE_REFRESH;
- end = bgp_create_route_refresh(conn, pkt);
+ if (len < 4)
+ bgp_parse_error(s, 1);
+
+ path_id = get_u32(pos);
+ ADVANCE(pos, len, 4);
}
- else if (s & (1 << PKT_BEGIN_REFRESH))
+
+ if (len < 2)
+ bgp_parse_error(s, 1);
+
+ /* Decode flow length */
+ uint hlen = flow_hdr_length(pos);
+ uint dlen = flow_read_length(pos);
+ uint flen = hlen + dlen;
+ byte *data = pos + hlen;
+
+ if (len < flen)
+ bgp_parse_error(s, 1);
+
+ /* Validate flow data */
+ enum flow_validated_state r = flow6_validate(data, dlen);
+ if (r != FLOW_ST_VALID)
{
- s &= ~(1 << PKT_BEGIN_REFRESH);
- type = PKT_ROUTE_REFRESH; /* BoRR is a subtype of RR */
- end = bgp_create_begin_refresh(conn, pkt);
+ log(L_REMOTE "%s: Invalid flow route: %s", s->proto->p.name, flow_validated_state_str(r));
+ bgp_parse_error(s, 1);
}
- else if (s & (1 << PKT_UPDATE))
+
+ if (data[0] != FLOW_TYPE_DST_PREFIX)
{
- type = PKT_UPDATE;
- end = bgp_create_update(conn, pkt);
+ log(L_REMOTE "%s: No dst prefix at first pos", s->proto->p.name);
+ bgp_parse_error(s, 1);
+ }
- if (!end)
- {
- /* No update to send, perhaps we need to send End-of-RIB or EoRR */
+ /* Decode dst prefix */
+ ip6_addr px = IP6_NONE;
+ uint pxlen = data[1];
- conn->packets_to_send = 0;
+ // FIXME: Use some generic function
+ memcpy(&px, data, BYTES(pxlen));
+ px = ip6_and(px, ip6_mkmask(pxlen));
- if (p->feed_state == BFS_LOADED)
- {
- type = PKT_UPDATE;
- end = bgp_create_end_mark(conn, pkt);
- }
+ /* Prepare the flow */
+ net_addr *n = alloca(sizeof(struct net_addr_flow6) + flen);
+ net_fill_flow6(n, px, pxlen, pos, flen);
+ ADVANCE(pos, len, flen);
- else if (p->feed_state == BFS_REFRESHED)
- {
- type = PKT_ROUTE_REFRESH;
- end = bgp_create_end_refresh(conn, pkt);
- }
+ bgp_rte_update(s, n, path_id, a);
+ }
+}
- else /* Really nothing to send */
- return 0;
- p->feed_state = BFS_NONE;
- }
- }
- else
- return 0;
+static const struct bgp_af_desc bgp_af_table[] = {
+ {
+ .afi = BGP_AF_IPV4,
+ .net = NET_IP4,
+ .name = "ipv4",
+ .encode_nlri = bgp_encode_nlri_ip4,
+ .decode_nlri = bgp_decode_nlri_ip4,
+ .encode_next_hop = bgp_encode_next_hop_ip,
+ .decode_next_hop = bgp_decode_next_hop_ip,
+ .update_next_hop = bgp_update_next_hop_ip,
+ },
+ {
+ .afi = BGP_AF_IPV4_MC,
+ .net = NET_IP4,
+ .name = "ipv4-mc",
+ .encode_nlri = bgp_encode_nlri_ip4,
+ .decode_nlri = bgp_decode_nlri_ip4,
+ .encode_next_hop = bgp_encode_next_hop_ip,
+ .decode_next_hop = bgp_decode_next_hop_ip,
+ .update_next_hop = bgp_update_next_hop_ip,
+ },
+ {
+ .afi = BGP_AF_IPV4_MPLS,
+ .net = NET_IP4,
+ .mpls = 1,
+ .name = "ipv4-mpls",
+ .encode_nlri = bgp_encode_nlri_ip4,
+ .decode_nlri = bgp_decode_nlri_ip4,
+ .encode_next_hop = bgp_encode_next_hop_ip,
+ .decode_next_hop = bgp_decode_next_hop_ip,
+ .update_next_hop = bgp_update_next_hop_ip,
+ },
+ {
+ .afi = BGP_AF_IPV6,
+ .net = NET_IP6,
+ .name = "ipv6",
+ .encode_nlri = bgp_encode_nlri_ip6,
+ .decode_nlri = bgp_decode_nlri_ip6,
+ .encode_next_hop = bgp_encode_next_hop_ip,
+ .decode_next_hop = bgp_decode_next_hop_ip,
+ .update_next_hop = bgp_update_next_hop_ip,
+ },
+ {
+ .afi = BGP_AF_IPV6_MC,
+ .net = NET_IP6,
+ .name = "ipv6-mc",
+ .encode_nlri = bgp_encode_nlri_ip6,
+ .decode_nlri = bgp_decode_nlri_ip6,
+ .encode_next_hop = bgp_encode_next_hop_ip,
+ .decode_next_hop = bgp_decode_next_hop_ip,
+ .update_next_hop = bgp_update_next_hop_ip,
+ },
+ {
+ .afi = BGP_AF_IPV6_MPLS,
+ .net = NET_IP6,
+ .mpls = 1,
+ .name = "ipv6-mpls",
+ .encode_nlri = bgp_encode_nlri_ip6,
+ .decode_nlri = bgp_decode_nlri_ip6,
+ .encode_next_hop = bgp_encode_next_hop_ip,
+ .decode_next_hop = bgp_decode_next_hop_ip,
+ .update_next_hop = bgp_update_next_hop_ip,
+ },
+ {
+ .afi = BGP_AF_VPN4_MPLS,
+ .net = NET_VPN4,
+ .mpls = 1,
+ .name = "vpn4-mpls",
+ .encode_nlri = bgp_encode_nlri_vpn4,
+ .decode_nlri = bgp_decode_nlri_vpn4,
+ .encode_next_hop = bgp_encode_next_hop_vpn,
+ .decode_next_hop = bgp_decode_next_hop_vpn,
+ .update_next_hop = bgp_update_next_hop_ip,
+ },
+ {
+ .afi = BGP_AF_VPN6_MPLS,
+ .net = NET_VPN6,
+ .mpls = 1,
+ .name = "vpn6-mpls",
+ .encode_nlri = bgp_encode_nlri_vpn6,
+ .decode_nlri = bgp_decode_nlri_vpn6,
+ .encode_next_hop = bgp_encode_next_hop_vpn,
+ .decode_next_hop = bgp_decode_next_hop_vpn,
+ .update_next_hop = bgp_update_next_hop_ip,
+ },
+ {
+ .afi = BGP_AF_VPN4_MC,
+ .net = NET_VPN4,
+ .name = "vpn4-mc",
+ .encode_nlri = bgp_encode_nlri_vpn4,
+ .decode_nlri = bgp_decode_nlri_vpn4,
+ .encode_next_hop = bgp_encode_next_hop_vpn,
+ .decode_next_hop = bgp_decode_next_hop_vpn,
+ .update_next_hop = bgp_update_next_hop_ip,
+ },
+ {
+ .afi = BGP_AF_VPN6_MC,
+ .net = NET_VPN6,
+ .name = "vpn6-mc",
+ .encode_nlri = bgp_encode_nlri_vpn6,
+ .decode_nlri = bgp_decode_nlri_vpn6,
+ .encode_next_hop = bgp_encode_next_hop_vpn,
+ .decode_next_hop = bgp_decode_next_hop_vpn,
+ .update_next_hop = bgp_update_next_hop_ip,
+ },
+ {
+ .afi = BGP_AF_FLOW4,
+ .net = NET_FLOW4,
+ .no_igp = 1,
+ .name = "flow4",
+ .encode_nlri = bgp_encode_nlri_flow4,
+ .decode_nlri = bgp_decode_nlri_flow4,
+ .encode_next_hop = bgp_encode_next_hop_none,
+ .decode_next_hop = bgp_decode_next_hop_none,
+ .update_next_hop = bgp_update_next_hop_none,
+ },
+ {
+ .afi = BGP_AF_FLOW6,
+ .net = NET_FLOW6,
+ .no_igp = 1,
+ .name = "flow6",
+ .encode_nlri = bgp_encode_nlri_flow6,
+ .decode_nlri = bgp_decode_nlri_flow6,
+ .encode_next_hop = bgp_encode_next_hop_none,
+ .decode_next_hop = bgp_decode_next_hop_none,
+ .update_next_hop = bgp_update_next_hop_none,
+ },
+};
- conn->packets_to_send = s;
- bgp_create_header(buf, end - buf, type);
- return sk_send(sk, end - buf);
+const struct bgp_af_desc *
+bgp_get_af_desc(u32 afi)
+{
+ uint i;
+ for (i = 0; i < ARRAY_SIZE(bgp_af_table); i++)
+ if (bgp_af_table[i].afi == afi)
+ return &bgp_af_table[i];
+
+ return NULL;
}
-/**
- * bgp_schedule_packet - schedule a packet for transmission
- * @conn: connection
- * @type: packet type
- *
- * Schedule a packet of type @type to be sent as soon as possible.
- */
-void
-bgp_schedule_packet(struct bgp_conn *conn, int type)
+static inline uint
+bgp_encode_nlri(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, byte *end)
{
- DBG("BGP: Scheduling packet type %d\n", type);
- conn->packets_to_send |= 1 << type;
- if (conn->sk && conn->sk->tpos == conn->sk->tbuf && !ev_active(conn->tx_ev))
- ev_schedule(conn->tx_ev);
+ return s->channel->desc->encode_nlri(s, buck, buf, end - buf);
}
-void
-bgp_kick_tx(void *vconn)
+static inline uint
+bgp_encode_next_hop(struct bgp_write_state *s, eattr *nh, byte *buf)
{
- struct bgp_conn *conn = vconn;
-
- DBG("BGP: kicking TX\n");
- while (bgp_fire_tx(conn) > 0)
- ;
+ return s->channel->desc->encode_next_hop(s, nh, buf, 255);
}
void
-bgp_tx(sock *sk)
+bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to)
{
- struct bgp_conn *conn = sk->data;
-
- DBG("BGP: TX hook\n");
- while (bgp_fire_tx(conn) > 0)
- ;
+ s->channel->desc->update_next_hop(s, a, to);
}
-/* Capatibility negotiation as per RFC 2842 */
+#define MAX_ATTRS_LENGTH (end-buf+BGP_HEADER_LENGTH - 1024)
-void
-bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len)
+static byte *
+bgp_create_ip_reach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, byte *end)
{
- // struct bgp_proto *p = conn->bgp;
- int i, cl;
+ /*
+ * 2 B Withdrawn Routes Length (zero)
+ * --- IPv4 Withdrawn Routes NLRI (unused)
+ * 2 B Total Path Attribute Length
+ * var Path Attributes
+ * var IPv4 Network Layer Reachability Information
+ */
+
+ int lr, la;
+
+ la = bgp_encode_attrs(s, buck->eattrs, buf+4, buf + MAX_ATTRS_LENGTH);
+ if (la < 0)
+ {
+ /* Attribute list too long */
+ bgp_withdraw_bucket(s->channel, buck);
+ return NULL;
+ }
- while (len > 0)
- {
- if (len < 2 || len < 2 + opt[1])
- goto err;
+ put_u16(buf+0, 0);
+ put_u16(buf+2, la);
- cl = opt[1];
+ lr = bgp_encode_nlri(s, buck, buf+4+la, end);
- switch (opt[0])
- {
- case 2: /* Route refresh capability, RFC 2918 */
- if (cl != 0)
- goto err;
- conn->peer_refresh_support = 1;
- break;
+ return buf+4+la+lr;
+}
- case 6: /* Extended message length capability, draft */
- if (cl != 0)
- goto err;
- conn->peer_ext_messages_support = 1;
- break;
+static byte *
+bgp_create_mp_reach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, byte *end)
+{
+ /*
+ * 2 B IPv4 Withdrawn Routes Length (zero)
+ * --- IPv4 Withdrawn Routes NLRI (unused)
+ * 2 B Total Path Attribute Length
+ * 1 B MP_REACH_NLRI hdr - Attribute Flags
+ * 1 B MP_REACH_NLRI hdr - Attribute Type Code
+ * 2 B MP_REACH_NLRI hdr - Length of Attribute Data
+ * 2 B MP_REACH_NLRI data - Address Family Identifier
+ * 1 B MP_REACH_NLRI data - Subsequent Address Family Identifier
+ * 1 B MP_REACH_NLRI data - Length of Next Hop Network Address
+ * var MP_REACH_NLRI data - Network Address of Next Hop
+ * 1 B MP_REACH_NLRI data - Reserved (zero)
+ * var MP_REACH_NLRI data - Network Layer Reachability Information
+ * var Rest of Path Attributes
+ * --- IPv4 Network Layer Reachability Information (unused)
+ */
+
+ int lh, lr, la; /* Lengths of next hop, NLRI and attributes */
+
+ /* Begin of MP_REACH_NLRI atribute */
+ buf[4] = BAF_OPTIONAL | BAF_EXT_LEN;
+ buf[5] = BA_MP_REACH_NLRI;
+ put_u16(buf+6, 0); /* Will be fixed later */
+ put_af3(buf+8, s->channel->afi);
+ byte *pos = buf+11;
+
+ /* Encode attributes to temporary buffer */
+ byte *abuf = alloca(MAX_ATTRS_LENGTH);
+ la = bgp_encode_attrs(s, buck->eattrs, abuf, abuf + MAX_ATTRS_LENGTH);
+ if (la < 0)
+ {
+ /* Attribute list too long */
+ bgp_withdraw_bucket(s->channel, buck);
+ return NULL;
+ }
- case 64: /* Graceful restart capability, RFC 4724 */
- if (cl % 4 != 2)
- goto err;
- conn->peer_gr_aware = 1;
- conn->peer_gr_able = 0;
- conn->peer_gr_time = get_u16(opt + 2) & 0x0fff;
- conn->peer_gr_flags = opt[2] & 0xf0;
- conn->peer_gr_aflags = 0;
- for (i = 2; i < cl; i += 4)
- if (opt[2+i+0] == 0 && opt[2+i+1] == BGP_AF && opt[2+i+2] == 1) /* Match AFI/SAFI */
- {
- conn->peer_gr_able = 1;
- conn->peer_gr_aflags = opt[2+i+3];
- }
- break;
+ /* Encode the next hop */
+ lh = bgp_encode_next_hop(s, s->mp_next_hop, pos+1);
+ *pos = lh;
+ pos += 1+lh;
- case 65: /* AS4 capability, RFC 4893 */
- if (cl != 4)
- goto err;
- conn->peer_as4_support = 1;
- if (conn->bgp->cf->enable_as4)
- conn->advertised_as = get_u32(opt + 2);
- break;
+ /* Reserved field */
+ *pos++ = 0;
- case 69: /* ADD-PATH capability, RFC 7911 */
- if (cl % 4)
- goto err;
- for (i = 0; i < cl; i += 4)
- if (opt[2+i+0] == 0 && opt[2+i+1] == BGP_AF && opt[2+i+2] == 1) /* Match AFI/SAFI */
- conn->peer_add_path = opt[2+i+3];
- if (conn->peer_add_path > ADD_PATH_FULL)
- goto err;
- break;
+ /* Encode the NLRI */
+ lr = bgp_encode_nlri(s, buck, pos, end - la);
+ pos += lr;
- case 70: /* Enhanced route refresh capability, RFC 7313 */
- if (cl != 0)
- goto err;
- conn->peer_enhanced_refresh_support = 1;
- break;
+ /* End of MP_REACH_NLRI atribute, update data length */
+ put_u16(buf+6, pos-buf-8);
- /* We can safely ignore all other capabilities */
- }
- len -= 2 + cl;
- opt += 2 + cl;
- }
- return;
+ /* Copy remaining attributes */
+ memcpy(pos, abuf, la);
+ pos += la;
- err:
- bgp_error(conn, 2, 0, NULL, 0);
- return;
+ /* Initial UPDATE fields */
+ put_u16(buf+0, 0);
+ put_u16(buf+2, pos-buf-4);
+
+ return pos;
}
-static int
-bgp_parse_options(struct bgp_conn *conn, byte *opt, int len)
+#undef MAX_ATTRS_LENGTH
+
+static byte *
+bgp_create_ip_unreach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, byte *end)
{
- struct bgp_proto *p = conn->bgp;
- int ol;
+ /*
+ * 2 B Withdrawn Routes Length
+ * var IPv4 Withdrawn Routes NLRI
+ * 2 B Total Path Attribute Length (zero)
+ * --- Path Attributes (unused)
+ * --- IPv4 Network Layer Reachability Information (unused)
+ */
- while (len > 0)
- {
- if (len < 2 || len < 2 + opt[1])
- { bgp_error(conn, 2, 0, NULL, 0); return 0; }
-#ifdef LOCAL_DEBUG
- {
- int i;
- DBG("\tOption %02x:", opt[0]);
- for(i=0; i<opt[1]; i++)
- DBG(" %02x", opt[2+i]);
- DBG("\n");
- }
-#endif
+ uint len = bgp_encode_nlri(s, buck, buf+2, end);
- ol = opt[1];
- switch (opt[0])
- {
- case 2:
- if (conn->start_state == BSS_CONNECT_NOCAP)
- BGP_TRACE(D_PACKETS, "Ignoring received capabilities");
- else
- bgp_parse_capabilities(conn, opt + 2, ol);
- break;
+ put_u16(buf+0, len);
+ put_u16(buf+2+len, 0);
- default:
- /*
- * BGP specs don't tell us to send which option
- * we didn't recognize, but it's common practice
- * to do so. Also, capability negotiation with
- * Cisco routers doesn't work without that.
- */
- bgp_error(conn, 2, 4, opt, ol);
- return 0;
- }
- len -= 2 + ol;
- opt += 2 + ol;
- }
- return 0;
+ return buf+4+len;
}
-static void
-bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
+static byte *
+bgp_create_mp_unreach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, byte *end)
{
- struct bgp_conn *other;
- struct bgp_proto *p = conn->bgp;
- unsigned hold;
- u16 base_as;
- u32 id;
+ /*
+ * 2 B Withdrawn Routes Length (zero)
+ * --- IPv4 Withdrawn Routes NLRI (unused)
+ * 2 B Total Path Attribute Length
+ * 1 B MP_UNREACH_NLRI hdr - Attribute Flags
+ * 1 B MP_UNREACH_NLRI hdr - Attribute Type Code
+ * 2 B MP_UNREACH_NLRI hdr - Length of Attribute Data
+ * 2 B MP_UNREACH_NLRI data - Address Family Identifier
+ * 1 B MP_UNREACH_NLRI data - Subsequent Address Family Identifier
+ * var MP_UNREACH_NLRI data - Network Layer Reachability Information
+ * --- IPv4 Network Layer Reachability Information (unused)
+ */
+
+ uint len = bgp_encode_nlri(s, buck, buf+11, end);
- /* Check state */
- if (conn->state != BS_OPENSENT)
- { bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0); return; }
+ put_u16(buf+0, 0);
+ put_u16(buf+2, 7+len);
- /* Check message contents */
- if (len < 29 || len != 29U + pkt[28])
- { bgp_error(conn, 1, 2, pkt+16, 2); return; }
- if (pkt[19] != BGP_VERSION)
- { bgp_error(conn, 2, 1, pkt+19, 1); return; } /* RFC 1771 says 16 bits, draft-09 tells to use 8 */
- conn->advertised_as = base_as = get_u16(pkt+20);
- hold = get_u16(pkt+22);
- id = get_u32(pkt+24);
- BGP_TRACE(D_PACKETS, "Got OPEN(as=%d,hold=%d,id=%08x)", conn->advertised_as, hold, id);
+ /* Begin of MP_UNREACH_NLRI atribute */
+ buf[4] = BAF_OPTIONAL | BAF_EXT_LEN;
+ buf[5] = BA_MP_UNREACH_NLRI;
+ put_u16(buf+6, 3+len);
+ put_af3(buf+8, s->channel->afi);
- if (bgp_parse_options(conn, pkt+29, pkt[28]))
- return;
+ return buf+11+len;
+}
- if (hold > 0 && hold < 3)
- { bgp_error(conn, 2, 6, pkt+22, 2); return; }
+static byte *
+bgp_create_update(struct bgp_channel *c, byte *buf)
+{
+ struct bgp_proto *p = (void *) c->c.proto;
+ struct bgp_bucket *buck;
+ byte *end = buf + (bgp_max_packet_length(p->conn) - BGP_HEADER_LENGTH);
+ byte *res = NULL;
+
+again: ;
+
+ /* Initialize write state */
+ struct bgp_write_state s = {
+ .proto = p,
+ .channel = c,
+ .pool = bgp_linpool,
+ .as4_session = p->as4_session,
+ .add_path = c->add_path_tx,
+ .mpls = c->desc->mpls,
+ };
+
+ /* Try unreachable bucket */
+ if ((buck = c->withdraw_bucket) && !EMPTY_LIST(buck->prefixes))
+ {
+ res = (c->afi == BGP_AF_IPV4) && !c->ext_next_hop ?
+ bgp_create_ip_unreach(&s, buck, buf, end):
+ bgp_create_mp_unreach(&s, buck, buf, end);
- /* RFC 6286 2.2 - router ID is nonzero and AS-wide unique */
- if (!id || (p->is_internal && id == p->local_id))
- { bgp_error(conn, 2, 3, pkt+24, -4); return; }
+ goto done;
+ }
- if ((conn->advertised_as != base_as) && (base_as != AS_TRANS))
- log(L_WARN "%s: Peer advertised inconsistent AS numbers", p->p.name);
+ /* Try reachable buckets */
+ if (!EMPTY_LIST(c->bucket_queue))
+ {
+ buck = HEAD(c->bucket_queue);
- if (conn->advertised_as != p->remote_as)
+ /* Cleanup empty buckets */
+ if (EMPTY_LIST(buck->prefixes))
{
- if (conn->peer_as4_support)
- {
- u32 val = htonl(conn->advertised_as);
- bgp_error(conn, 2, 2, (byte *) &val, 4);
- }
- else
- bgp_error(conn, 2, 2, pkt+20, 2);
-
- return;
+ bgp_free_bucket(c, buck);
+ goto again;
}
- /* Check the other connection */
- other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn;
- switch (other->state)
- {
- case BS_CONNECT:
- case BS_ACTIVE:
- /* Stop outgoing connection attempts */
- bgp_conn_enter_idle_state(other);
- break;
+ res = (c->afi == BGP_AF_IPV4) && !c->ext_next_hop ?
+ bgp_create_ip_reach(&s, buck, buf, end):
+ bgp_create_mp_reach(&s, buck, buf, end);
- case BS_IDLE:
- case BS_OPENSENT:
- case BS_CLOSE:
- break;
+ if (EMPTY_LIST(buck->prefixes))
+ bgp_free_bucket(c, buck);
+ else
+ bgp_defer_bucket(c, buck);
- case BS_OPENCONFIRM:
- /*
- * Description of collision detection rules in RFC 4271 is confusing and
- * contradictory, but it is essentially:
- *
- * 1. Router with higher ID is dominant
- * 2. If both have the same ID, router with higher ASN is dominant [RFC6286]
- * 3. When both connections are in OpenConfirm state, one initiated by
- * the dominant router is kept.
- *
- * The first line in the expression below evaluates whether the neighbor
- * is dominant, the second line whether the new connection was initiated
- * by the neighbor. If both are true (or both are false), we keep the new
- * connection, otherwise we keep the old one.
- */
- if (((p->local_id < id) || ((p->local_id == id) && (p->local_as < p->remote_as)))
- == (conn == &p->incoming_conn))
- {
- /* Should close the other connection */
- BGP_TRACE(D_EVENTS, "Connection collision, giving up the other connection");
- bgp_error(other, 6, 7, NULL, 0);
- break;
- }
- /* Fall thru */
- case BS_ESTABLISHED:
- /* Should close this connection */
- BGP_TRACE(D_EVENTS, "Connection collision, giving up this connection");
- bgp_error(conn, 6, 7, NULL, 0);
- return;
- default:
- bug("bgp_rx_open: Unknown state");
- }
+ if (!res)
+ goto again;
- /* Update our local variables */
- conn->hold_time = MIN(hold, p->cf->hold_time);
- conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3;
- p->remote_id = id;
- p->as4_session = p->cf->enable_as4 && conn->peer_as4_support;
- p->add_path_rx = (p->cf->add_path & ADD_PATH_RX) && (conn->peer_add_path & ADD_PATH_TX);
- p->add_path_tx = (p->cf->add_path & ADD_PATH_TX) && (conn->peer_add_path & ADD_PATH_RX);
- p->gr_ready = p->cf->gr_mode && conn->peer_gr_able;
- p->ext_messages = p->cf->enable_extended_messages && conn->peer_ext_messages_support;
-
- /* Update RA mode */
- if (p->add_path_tx)
- p->p.accept_ra_types = RA_ANY;
- else if (p->cf->secondary)
- p->p.accept_ra_types = RA_ACCEPTED;
- else
- p->p.accept_ra_types = RA_OPTIMAL;
+ goto done;
+ }
- DBG("BGP: Hold timer set to %d, keepalive to %d, AS to %d, ID to %x, AS4 session to %d\n", conn->hold_time, conn->keepalive_time, p->remote_as, p->remote_id, p->as4_session);
+ /* No more prefixes to send */
+ return NULL;
- bgp_schedule_packet(conn, PKT_KEEPALIVE);
- bgp_start_timer(conn->hold_timer, conn->hold_time);
- bgp_conn_enter_openconfirm_state(conn);
+done:
+ BGP_TRACE_RL(&rl_snd_update, D_PACKETS, "Sending UPDATE");
+ lp_flush(s.pool);
+
+ return res;
}
+static byte *
+bgp_create_ip_end_mark(struct bgp_channel *c UNUSED, byte *buf)
+{
+ /* Empty update packet */
+ put_u32(buf, 0);
-static inline void
-bgp_rx_end_mark(struct bgp_proto *p)
+ return buf+4;
+}
+
+static byte *
+bgp_create_mp_end_mark(struct bgp_channel *c, byte *buf)
{
- BGP_TRACE(D_PACKETS, "Got END-OF-RIB");
+ put_u16(buf+0, 0);
+ put_u16(buf+2, 6); /* length 4--9 */
- if (p->load_state == BFS_LOADING)
- p->load_state = BFS_NONE;
+ /* Empty MP_UNREACH_NLRI atribute */
+ buf[4] = BAF_OPTIONAL;
+ buf[5] = BA_MP_UNREACH_NLRI;
+ buf[6] = 3; /* Length 7--9 */
+ put_af3(buf+7, c->afi);
- if (p->p.gr_recovery)
- proto_graceful_restart_unlock(&p->p);
-
- if (p->gr_active)
- bgp_graceful_restart_done(p);
-}
-
-
-#define DECODE_PREFIX(pp, ll) do { \
- if (p->add_path_rx) \
- { \
- if (ll < 5) { err=1; goto done; } \
- path_id = get_u32(pp); \
- pp += 4; \
- ll -= 4; \
- } \
- int b = *pp++; \
- int q; \
- ll--; \
- if (b > BITS_PER_IP_ADDRESS) { err=10; goto done; } \
- q = (b+7) / 8; \
- if (ll < q) { err=1; goto done; } \
- memcpy(&prefix, pp, q); \
- pp += q; \
- ll -= q; \
- ipa_ntoh(prefix); \
- prefix = ipa_and(prefix, ipa_mkmask(b)); \
- pxlen = b; \
-} while (0)
+ return buf+10;
+}
+static byte *
+bgp_create_end_mark(struct bgp_channel *c, byte *buf)
+{
+ struct bgp_proto *p = (void *) c->c.proto;
+
+ BGP_TRACE(D_PACKETS, "Sending END-OF-RIB");
+
+ return (c->afi == BGP_AF_IPV4) ?
+ bgp_create_ip_end_mark(c, buf):
+ bgp_create_mp_end_mark(c, buf);
+}
static inline void
-bgp_rte_update(struct bgp_proto *p, ip_addr prefix, int pxlen,
- u32 path_id, u32 *last_id, struct rte_src **src,
- rta *a0, rta **a)
+bgp_rx_end_mark(struct bgp_parse_state *s, u32 afi)
{
- if (path_id != *last_id)
- {
- *src = rt_get_source(&p->p, path_id);
- *last_id = path_id;
+ struct bgp_proto *p = s->proto;
+ struct bgp_channel *c = bgp_get_channel(p, afi);
- if (*a)
- {
- rta_free(*a);
- *a = NULL;
- }
- }
+ BGP_TRACE(D_PACKETS, "Got END-OF-RIB");
- /* Prepare cached route attributes */
- if (!*a)
- {
- a0->src = *src;
+ if (!c)
+ DISCARD(BAD_AFI, BGP_AFI(afi), BGP_SAFI(afi));
- /* Workaround for rta_lookup() breaking eattrs */
- ea_list *ea = a0->eattrs;
- *a = rta_lookup(a0);
- a0->eattrs = ea;
- }
+ if (c->load_state == BFS_LOADING)
+ c->load_state = BFS_NONE;
- net *n = net_get(p->p.table, prefix, pxlen);
- rte *e = rte_get_temp(rta_clone(*a));
- e->net = n;
- e->pflags = 0;
- e->u.bgp.suppressed = 0;
- rte_update2(p->p.main_ahook, n, e, *src);
+ if (p->p.gr_recovery)
+ channel_graceful_restart_unlock(&c->c);
+
+ if (c->gr_active)
+ bgp_graceful_restart_done(c);
}
static inline void
-bgp_rte_withdraw(struct bgp_proto *p, ip_addr prefix, int pxlen,
- u32 path_id, u32 *last_id, struct rte_src **src)
+bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_list *ea, byte *nh, uint nh_len)
{
- if (path_id != *last_id)
- {
- *src = rt_find_source(&p->p, path_id);
- *last_id = path_id;
- }
+ struct bgp_channel *c = bgp_get_channel(s->proto, afi);
+ rta *a = NULL;
- net *n = net_find(p->p.table, prefix, pxlen);
- rte_update2( p->p.main_ahook, n, NULL, *src);
-}
+ if (!c)
+ DISCARD(BAD_AFI, BGP_AFI(afi), BGP_SAFI(afi));
-static inline int
-bgp_set_next_hop(struct bgp_proto *p, rta *a)
-{
- struct eattr *nh = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
- ip_addr *nexthop = (ip_addr *) nh->u.ptr->data;
+ s->channel = c;
+ s->add_path = c->add_path_rx;
+ s->mpls = c->desc->mpls;
-#ifdef IPV6
- int second = (nh->u.ptr->length == NEXT_HOP_LENGTH) && ipa_nonzero(nexthop[1]);
+ s->last_id = 0;
+ s->last_src = s->proto->p.main_source;
- /* First address should not be link-local, but may be zero in direct mode */
- if (ipa_is_link_local(*nexthop))
- *nexthop = IPA_NONE;
-#else
- int second = 0;
-#endif
-
- if (p->cf->gw_mode == GW_DIRECT)
- {
- neighbor *ng = NULL;
+ /*
+ * IPv4 BGP and MP-BGP may be used together in one update, therefore we do not
+ * add BA_NEXT_HOP in bgp_decode_attrs(), but we add it here independently for
+ * IPv4 BGP and MP-BGP. We undo the attribute (and possibly others attached by
+ * decode_next_hop hooks) by restoring a->eattrs afterwards.
+ */
- if (ipa_nonzero(*nexthop))
- ng = neigh_find(&p->p, nexthop, 0);
- else if (second) /* GW_DIRECT -> single_hop -> p->neigh != NULL */
- ng = neigh_find2(&p->p, nexthop + 1, p->neigh->iface, 0);
+ if (ea)
+ {
+ a = allocz(RTA_MAX_SIZE);
- /* Fallback */
- if (!ng)
- ng = p->neigh;
+ a->source = RTS_BGP;
+ a->scope = SCOPE_UNIVERSE;
+ a->from = s->proto->cf->remote_ip;
+ a->eattrs = ea;
- if (ng->scope == SCOPE_HOST)
- return 0;
+ c->desc->decode_next_hop(s, nh, nh_len, a);
- a->dest = RTD_ROUTER;
- a->gw = ng->addr;
- a->iface = ng->iface;
- a->hostentry = NULL;
- a->igp_metric = 0;
- }
- else /* GW_RECURSIVE */
- {
- if (ipa_zero(*nexthop))
- return 0;
+ /* Handle withdraw during next hop decoding */
+ if (s->err_withdraw)
+ a = NULL;
+ }
- rta_set_recursive_next_hop(p->p.table, a, p->igp_table, nexthop, nexthop + second);
- }
+ c->desc->decode_nlri(s, nlri, len, a);
- return 1;
+ rta_free(s->cached_rta);
+ s->cached_rta = NULL;
}
-#ifndef IPV6 /* IPv4 version */
-
static void
-bgp_do_rx_update(struct bgp_conn *conn,
- byte *withdrawn, int withdrawn_len,
- byte *nlri, int nlri_len,
- byte *attrs, int attr_len)
+bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
{
struct bgp_proto *p = conn->bgp;
- struct rte_src *src = p->p.main_source;
- rta *a0, *a = NULL;
- ip_addr prefix;
- int pxlen, err = 0;
- u32 path_id = 0;
- u32 last_id = 0;
+ ea_list *ea = NULL;
- /* Check for End-of-RIB marker */
- if (!withdrawn_len && !attr_len && !nlri_len)
- {
- bgp_rx_end_mark(p);
- return;
- }
+ BGP_TRACE_RL(&rl_rcv_update, D_PACKETS, "Got UPDATE");
- /* Withdraw routes */
- while (withdrawn_len)
- {
- DECODE_PREFIX(withdrawn, withdrawn_len);
- DBG("Withdraw %I/%d\n", prefix, pxlen);
+ /* Workaround for some BGP implementations that skip initial KEEPALIVE */
+ if (conn->state == BS_OPENCONFIRM)
+ bgp_conn_enter_established_state(conn);
- bgp_rte_withdraw(p, prefix, pxlen, path_id, &last_id, &src);
- }
+ if (conn->state != BS_ESTABLISHED)
+ { bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0); return; }
- if (!attr_len && !nlri_len) /* shortcut */
- return;
+ bgp_start_timer(conn->hold_timer, conn->hold_time);
- a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, nlri_len);
+ /* Initialize parse state */
+ struct bgp_parse_state s = {
+ .proto = p,
+ .pool = bgp_linpool,
+ .as4_session = p->as4_session,
+ };
- if (conn->state != BS_ESTABLISHED) /* fatal error during decoding */
- return;
+ /* Parse error handler */
+ if (setjmp(s.err_jmpbuf))
+ {
+ bgp_error(conn, 3, s.err_subcode, NULL, 0);
+ goto done;
+ }
- if (a0 && nlri_len && !bgp_set_next_hop(p, a0))
- a0 = NULL;
+ /* Check minimal length */
+ if (len < 23)
+ { bgp_error(conn, 1, 2, pkt+16, 2); return; }
- last_id = 0;
- src = p->p.main_source;
+ /* Skip fixed header */
+ uint pos = 19;
- while (nlri_len)
- {
- DECODE_PREFIX(nlri, nlri_len);
- DBG("Add %I/%d\n", prefix, pxlen);
+ /*
+ * UPDATE message format
+ *
+ * 2 B IPv4 Withdrawn Routes Length
+ * var IPv4 Withdrawn Routes NLRI
+ * 2 B Total Path Attribute Length
+ * var Path Attributes
+ * var IPv4 Reachable Routes NLRI
+ */
- if (a0)
- bgp_rte_update(p, prefix, pxlen, path_id, &last_id, &src, a0, &a);
- else /* Forced withdraw as a result of soft error */
- bgp_rte_withdraw(p, prefix, pxlen, path_id, &last_id, &src);
- }
+ s.ip_unreach_len = get_u16(pkt + pos);
+ s.ip_unreach_nlri = pkt + pos + 2;
+ pos += 2 + s.ip_unreach_len;
- done:
- if (a)
- rta_free(a);
+ if (pos + 2 > len)
+ bgp_parse_error(&s, 1);
- if (err)
- bgp_error(conn, 3, err, NULL, 0);
+ s.attr_len = get_u16(pkt + pos);
+ s.attrs = pkt + pos + 2;
+ pos += 2 + s.attr_len;
+
+ if (pos > len)
+ bgp_parse_error(&s, 1);
+ s.ip_reach_len = len - pos;
+ s.ip_reach_nlri = pkt + pos;
+
+
+ if (s.attr_len)
+ ea = bgp_decode_attrs(&s, s.attrs, s.attr_len);
+
+ /* Check for End-of-RIB marker */
+ if (!s.attr_len && !s.ip_unreach_len && !s.ip_reach_len)
+ { bgp_rx_end_mark(&s, BGP_AF_IPV4); goto done; }
+
+ /* Check for MP End-of-RIB marker */
+ if ((s.attr_len < 8) && !s.ip_unreach_len && !s.ip_reach_len &&
+ !s.mp_reach_len && !s.mp_unreach_len && s.mp_unreach_af)
+ { bgp_rx_end_mark(&s, s.mp_unreach_af); goto done; }
+
+ if (s.ip_unreach_len)
+ bgp_decode_nlri(&s, BGP_AF_IPV4, s.ip_unreach_nlri, s.ip_unreach_len, NULL, NULL, 0);
+
+ if (s.mp_unreach_len)
+ bgp_decode_nlri(&s, s.mp_unreach_af, s.mp_unreach_nlri, s.mp_unreach_len, NULL, NULL, 0);
+
+ if (s.ip_reach_len)
+ bgp_decode_nlri(&s, BGP_AF_IPV4, s.ip_reach_nlri, s.ip_reach_len,
+ ea, s.ip_next_hop_data, s.ip_next_hop_len);
+
+ if (s.mp_reach_len)
+ bgp_decode_nlri(&s, s.mp_reach_af, s.mp_reach_nlri, s.mp_reach_len,
+ ea, s.mp_next_hop_data, s.mp_next_hop_len);
+
+done:
+ rta_free(s.cached_rta);
+ lp_flush(s.pool);
return;
}
-#else /* IPv6 version */
-#define DO_NLRI(name) \
- x = p->name##_start; \
- len = len0 = p->name##_len; \
- if (len) \
- { \
- if (len < 3) { err=9; goto done; } \
- af = get_u16(x); \
- x += 3; \
- len -= 3; \
- DBG("\tNLRI AF=%d sub=%d len=%d\n", af, x[-1], len);\
- } \
- else \
- af = 0; \
- if (af == BGP_AF_IPV6)
+/*
+ * ROUTE-REFRESH
+ */
-static void
-bgp_attach_next_hop(rta *a0, byte *x)
+static inline byte *
+bgp_create_route_refresh(struct bgp_channel *c, byte *buf)
{
- ip_addr *nh = (ip_addr *) bgp_attach_attr_wa(&a0->eattrs, bgp_linpool, BA_NEXT_HOP, NEXT_HOP_LENGTH);
- memcpy(nh, x+1, 16);
- ipa_ntoh(nh[0]);
+ struct bgp_proto *p = (void *) c->c.proto;
- /* We store received link local address in the other part of BA_NEXT_HOP eattr. */
- if (*x == 32)
- {
- memcpy(nh+1, x+17, 16);
- ipa_ntoh(nh[1]);
- }
- else
- nh[1] = IPA_NONE;
+ BGP_TRACE(D_PACKETS, "Sending ROUTE-REFRESH");
+
+ /* Original route refresh request, RFC 2918 */
+ put_af4(buf, c->afi);
+ buf[2] = BGP_RR_REQUEST;
+
+ return buf+4;
+}
+
+static inline byte *
+bgp_create_begin_refresh(struct bgp_channel *c, byte *buf)
+{
+ struct bgp_proto *p = (void *) c->c.proto;
+
+ BGP_TRACE(D_PACKETS, "Sending BEGIN-OF-RR");
+
+ /* Demarcation of beginning of route refresh (BoRR), RFC 7313 */
+ put_af4(buf, c->afi);
+ buf[2] = BGP_RR_BEGIN;
+
+ return buf+4;
}
+static inline byte *
+bgp_create_end_refresh(struct bgp_channel *c, byte *buf)
+{
+ struct bgp_proto *p = (void *) c->c.proto;
+
+ BGP_TRACE(D_PACKETS, "Sending END-OF-RR");
+
+ /* Demarcation of ending of route refresh (EoRR), RFC 7313 */
+ put_af4(buf, c->afi);
+ buf[2] = BGP_RR_END;
+
+ return buf+4;
+}
static void
-bgp_do_rx_update(struct bgp_conn *conn,
- byte *withdrawn UNUSED, int withdrawn_len,
- byte *nlri UNUSED, int nlri_len,
- byte *attrs, int attr_len)
+bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, uint len)
{
struct bgp_proto *p = conn->bgp;
- struct rte_src *src = p->p.main_source;
- byte *x;
- int len, len0;
- unsigned af;
- rta *a0, *a = NULL;
- ip_addr prefix;
- int pxlen, err = 0;
- u32 path_id = 0;
- u32 last_id = 0;
-
- p->mp_reach_len = 0;
- p->mp_unreach_len = 0;
- a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, 0);
-
- if (conn->state != BS_ESTABLISHED) /* fatal error during decoding */
- return;
- /* Check for End-of-RIB marker */
- if ((attr_len < 8) && !withdrawn_len && !nlri_len && !p->mp_reach_len &&
- (p->mp_unreach_len == 3) && (get_u16(p->mp_unreach_start) == BGP_AF_IPV6))
- {
- bgp_rx_end_mark(p);
- return;
- }
+ if (conn->state != BS_ESTABLISHED)
+ { bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0); return; }
- DO_NLRI(mp_unreach)
- {
- while (len)
- {
- DECODE_PREFIX(x, len);
- DBG("Withdraw %I/%d\n", prefix, pxlen);
- bgp_rte_withdraw(p, prefix, pxlen, path_id, &last_id, &src);
- }
- }
+ if (!conn->local_caps->route_refresh)
+ { bgp_error(conn, 1, 3, pkt+18, 1); return; }
- DO_NLRI(mp_reach)
- {
- /* Create fake NEXT_HOP attribute */
- if (len < 1 || (*x != 16 && *x != 32) || len < *x + 2)
- { err = 9; goto done; }
+ if (len < (BGP_HEADER_LENGTH + 4))
+ { bgp_error(conn, 1, 2, pkt+16, 2); return; }
- if (a0)
- bgp_attach_next_hop(a0, x);
+ if (len > (BGP_HEADER_LENGTH + 4))
+ { bgp_error(conn, 7, 1, pkt, MIN(len, 2048)); return; }
- /* Also ignore one reserved byte */
- len -= *x + 2;
- x += *x + 2;
+ struct bgp_channel *c = bgp_get_channel(p, get_af4(pkt+19));
+ if (!c)
+ {
+ log(L_WARN "%s: Got ROUTE-REFRESH subtype %u for AF %u.%u, ignoring",
+ p->p.name, pkt[21], get_u16(pkt+19), pkt[22]);
+ return;
+ }
- if (a0 && ! bgp_set_next_hop(p, a0))
- a0 = NULL;
+ /* RFC 7313 redefined reserved field as RR message subtype */
+ uint subtype = p->enhanced_refresh ? pkt[21] : BGP_RR_REQUEST;
- last_id = 0;
- src = p->p.main_source;
+ switch (subtype)
+ {
+ case BGP_RR_REQUEST:
+ BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH");
+ channel_request_feeding(&c->c);
+ break;
- while (len)
- {
- DECODE_PREFIX(x, len);
- DBG("Add %I/%d\n", prefix, pxlen);
+ case BGP_RR_BEGIN:
+ BGP_TRACE(D_PACKETS, "Got BEGIN-OF-RR");
+ bgp_refresh_begin(c);
+ break;
- if (a0)
- bgp_rte_update(p, prefix, pxlen, path_id, &last_id, &src, a0, &a);
- else /* Forced withdraw as a result of soft error */
- bgp_rte_withdraw(p, prefix, pxlen, path_id, &last_id, &src);
- }
- }
+ case BGP_RR_END:
+ BGP_TRACE(D_PACKETS, "Got END-OF-RR");
+ bgp_refresh_end(c);
+ break;
- done:
- if (a)
- rta_free(a);
+ default:
+ log(L_WARN "%s: Got ROUTE-REFRESH message with unknown subtype %u, ignoring",
+ p->p.name, subtype);
+ break;
+ }
+}
+
+static inline struct bgp_channel *
+bgp_get_channel_to_send(struct bgp_proto *p, struct bgp_conn *conn)
+{
+ uint i = conn->last_channel;
- if (err) /* Use subcode 9, not err */
- bgp_error(conn, 3, 9, NULL, 0);
+ /* Try the last channel, but at most several times */
+ if ((conn->channels_to_send & (1 << i)) &&
+ (conn->last_channel_count < 16))
+ goto found;
- return;
+ /* Find channel with non-zero channels_to_send */
+ do
+ {
+ i++;
+ if (i >= p->channel_count)
+ i = 0;
+ }
+ while (! (conn->channels_to_send & (1 << i)));
+
+ /* Use that channel */
+ conn->last_channel = i;
+ conn->last_channel_count = 0;
+
+found:
+ conn->last_channel_count++;
+ return p->channel_map[i];
}
-#endif
+static inline int
+bgp_send(struct bgp_conn *conn, uint type, uint len)
+{
+ sock *sk = conn->sk;
+ byte *buf = sk->tbuf;
-static void
-bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
+ memset(buf, 0xff, 16); /* Marker */
+ put_u16(buf+16, len);
+ buf[18] = type;
+
+ return sk_send(sk, len);
+}
+
+/**
+ * bgp_fire_tx - transmit packets
+ * @conn: connection
+ *
+ * Whenever the transmit buffers of the underlying TCP connection
+ * are free and we have any packets queued for sending, the socket functions
+ * call bgp_fire_tx() which takes care of selecting the highest priority packet
+ * queued (Notification > Keepalive > Open > Update), assembling its header
+ * and body and sending it to the connection.
+ */
+static int
+bgp_fire_tx(struct bgp_conn *conn)
{
struct bgp_proto *p = conn->bgp;
- byte *withdrawn, *attrs, *nlri;
- uint withdrawn_len, attr_len, nlri_len;
+ struct bgp_channel *c;
+ byte *buf, *pkt, *end;
+ uint s;
- BGP_TRACE_RL(&rl_rcv_update, D_PACKETS, "Got UPDATE");
+ if (!conn->sk)
+ return 0;
- /* Workaround for some BGP implementations that skip initial KEEPALIVE */
- if (conn->state == BS_OPENCONFIRM)
- bgp_conn_enter_established_state(conn);
+ buf = conn->sk->tbuf;
+ pkt = buf + BGP_HEADER_LENGTH;
+ s = conn->packets_to_send;
- if (conn->state != BS_ESTABLISHED)
- { bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0); return; }
- bgp_start_timer(conn->hold_timer, conn->hold_time);
+ if (s & (1 << PKT_SCHEDULE_CLOSE))
+ {
+ /* We can finally close connection and enter idle state */
+ bgp_conn_enter_idle_state(conn);
+ return 0;
+ }
+ if (s & (1 << PKT_NOTIFICATION))
+ {
+ conn->packets_to_send = 1 << PKT_SCHEDULE_CLOSE;
+ end = bgp_create_notification(conn, pkt);
+ return bgp_send(conn, PKT_NOTIFICATION, end - buf);
+ }
+ else if (s & (1 << PKT_KEEPALIVE))
+ {
+ conn->packets_to_send &= ~(1 << PKT_KEEPALIVE);
+ BGP_TRACE(D_PACKETS, "Sending KEEPALIVE");
+ bgp_start_timer(conn->keepalive_timer, conn->keepalive_time);
+ return bgp_send(conn, PKT_KEEPALIVE, BGP_HEADER_LENGTH);
+ }
+ else if (s & (1 << PKT_OPEN))
+ {
+ conn->packets_to_send &= ~(1 << PKT_OPEN);
+ end = bgp_create_open(conn, pkt);
+ return bgp_send(conn, PKT_OPEN, end - buf);
+ }
+ else while (conn->channels_to_send)
+ {
+ c = bgp_get_channel_to_send(p, conn);
+ s = c->packets_to_send;
- /* Find parts of the packet and check sizes */
- if (len < 23)
+ if (s & (1 << PKT_ROUTE_REFRESH))
{
- bgp_error(conn, 1, 2, pkt+16, 2);
- return;
+ c->packets_to_send &= ~(1 << PKT_ROUTE_REFRESH);
+ end = bgp_create_route_refresh(c, pkt);
+ return bgp_send(conn, PKT_ROUTE_REFRESH, end - buf);
}
- withdrawn = pkt + 21;
- withdrawn_len = get_u16(pkt + 19);
- if (withdrawn_len + 23 > len)
- goto malformed;
- attrs = withdrawn + withdrawn_len + 2;
- attr_len = get_u16(attrs - 2);
- if (withdrawn_len + attr_len + 23 > len)
- goto malformed;
- nlri = attrs + attr_len;
- nlri_len = len - withdrawn_len - attr_len - 23;
- if (!attr_len && nlri_len)
- goto malformed;
- DBG("Sizes: withdrawn=%d, attrs=%d, NLRI=%d\n", withdrawn_len, attr_len, nlri_len);
-
- lp_flush(bgp_linpool);
-
- bgp_do_rx_update(conn, withdrawn, withdrawn_len, nlri, nlri_len, attrs, attr_len);
- return;
+ else if (s & (1 << PKT_BEGIN_REFRESH))
+ {
+ /* BoRR is a subtype of RR, but uses separate bit in packets_to_send */
+ c->packets_to_send &= ~(1 << PKT_BEGIN_REFRESH);
+ end = bgp_create_begin_refresh(c, pkt);
+ return bgp_send(conn, PKT_ROUTE_REFRESH, end - buf);
+ }
+ else if (s & (1 << PKT_UPDATE))
+ {
+ end = bgp_create_update(c, pkt);
+ if (end)
+ return bgp_send(conn, PKT_UPDATE, end - buf);
+
+ /* No update to send, perhaps we need to send End-of-RIB or EoRR */
+ c->packets_to_send = 0;
+ conn->channels_to_send &= ~(1 << c->index);
+
+ if (c->feed_state == BFS_LOADED)
+ {
+ c->feed_state = BFS_NONE;
+ end = bgp_create_end_mark(c, pkt);
+ return bgp_send(conn, PKT_UPDATE, end - buf);
+ }
+
+ else if (c->feed_state == BFS_REFRESHED)
+ {
+ c->feed_state = BFS_NONE;
+ end = bgp_create_end_refresh(c, pkt);
+ return bgp_send(conn, PKT_ROUTE_REFRESH, end - buf);
+ }
+ }
+ else if (s)
+ bug("Channel packets_to_send: %x", s);
+
+ c->packets_to_send = 0;
+ conn->channels_to_send &= ~(1 << c->index);
+ }
+
+ return 0;
+}
+
+/**
+ * bgp_schedule_packet - schedule a packet for transmission
+ * @conn: connection
+ * @c: channel
+ * @type: packet type
+ *
+ * Schedule a packet of type @type to be sent as soon as possible.
+ */
+void
+bgp_schedule_packet(struct bgp_conn *conn, struct bgp_channel *c, int type)
+{
+ ASSERT(conn->sk);
+
+ DBG("BGP: Scheduling packet type %d\n", type);
+
+ if (c)
+ {
+ if (! conn->channels_to_send)
+ {
+ conn->last_channel = c->index;
+ conn->last_channel_count = 0;
+ }
+
+ c->packets_to_send |= 1 << type;
+ conn->channels_to_send |= 1 << c->index;
+ }
+ else
+ conn->packets_to_send |= 1 << type;
+
+ if ((conn->sk->tpos == conn->sk->tbuf) && !ev_active(conn->tx_ev))
+ ev_schedule(conn->tx_ev);
+}
+
+void
+bgp_kick_tx(void *vconn)
+{
+ struct bgp_conn *conn = vconn;
-malformed:
- bgp_error(conn, 3, 1, NULL, 0);
+ DBG("BGP: kicking TX\n");
+ while (bgp_fire_tx(conn) > 0)
+ ;
+}
+
+void
+bgp_tx(sock *sk)
+{
+ struct bgp_conn *conn = sk->data;
+
+ DBG("BGP: TX hook\n");
+ while (bgp_fire_tx(conn) > 0)
+ ;
}
+
static struct {
byte major, minor;
byte *msg;
@@ -1480,26 +2665,25 @@ static struct {
* which might be static string or given temporary buffer.
*/
const char *
-bgp_error_dsc(unsigned code, unsigned subcode)
+bgp_error_dsc(uint code, uint subcode)
{
static char buff[32];
- unsigned i;
+ uint i;
+
for (i=0; i < ARRAY_SIZE(bgp_msg_table); i++)
if (bgp_msg_table[i].major == code && bgp_msg_table[i].minor == subcode)
- {
- return bgp_msg_table[i].msg;
- }
+ return bgp_msg_table[i].msg;
- bsprintf(buff, "Unknown error %d.%d", code, subcode);
+ bsprintf(buff, "Unknown error %u.%u", code, subcode);
return buff;
}
void
-bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len)
+bgp_log_error(struct bgp_proto *p, u8 class, char *msg, uint code, uint subcode, byte *data, uint len)
{
const byte *name;
byte *t, argbuf[36];
- unsigned i;
+ uint i;
/* Don't report Cease messages generated by myself */
if (code == 6 && class == BE_BGP_TX)
@@ -1515,7 +2699,7 @@ bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsigned
if ((code == 2) && (subcode == 2) && ((len == 2) || (len == 4)))
{
/* Bad peer AS - we would like to print the AS */
- t += bsprintf(t, "%d", (len == 2) ? get_u16(data) : get_u32(data));
+ t += bsprintf(t, "%u", (len == 2) ? get_u16(data) : get_u32(data));
goto done;
}
if (len > 16)
@@ -1532,47 +2716,25 @@ static void
bgp_rx_notification(struct bgp_conn *conn, byte *pkt, uint len)
{
struct bgp_proto *p = conn->bgp;
+
if (len < 21)
- {
- bgp_error(conn, 1, 2, pkt+16, 2);
- return;
- }
+ { bgp_error(conn, 1, 2, pkt+16, 2); return; }
- unsigned code = pkt[19];
- unsigned subcode = pkt[20];
+ uint code = pkt[19];
+ uint subcode = pkt[20];
int err = (code != 6);
bgp_log_error(p, BE_BGP_RX, "Received", code, subcode, pkt+21, len-21);
bgp_store_error(p, conn, BE_BGP_RX, (code << 16) | subcode);
-#ifndef IPV6
- if ((code == 2) && ((subcode == 4) || (subcode == 7))
- /* Error related to capability:
- * 4 - Peer does not support capabilities at all.
- * 7 - Peer request some capability. Strange unless it is IPv6 only peer.
- */
- && (p->cf->capabilities == 2)
- /* Capabilities are not explicitly enabled or disabled, therefore heuristic is used */
- && (conn->start_state == BSS_CONNECT)
- /* Failed connection attempt have used capabilities */
- && (p->cf->remote_as <= 0xFFFF))
- /* Not possible with disabled capabilities */
- {
- /* We try connect without capabilities */
- log(L_WARN "%s: Capability related error received, retry with capabilities disabled", p->p.name);
- p->start_state = BSS_CONNECT_NOCAP;
- err = 0;
- }
-#endif
-
bgp_conn_enter_close_state(conn);
- bgp_schedule_packet(conn, PKT_SCHEDULE_CLOSE);
+ bgp_schedule_packet(conn, NULL, PKT_SCHEDULE_CLOSE);
- if (err)
- {
- bgp_update_startup_delay(p);
- bgp_stop(p, 0);
- }
+ if (err)
+ {
+ bgp_update_startup_delay(p);
+ bgp_stop(p, 0);
+ }
}
static void
@@ -1582,64 +2744,12 @@ bgp_rx_keepalive(struct bgp_conn *conn)
BGP_TRACE(D_PACKETS, "Got KEEPALIVE");
bgp_start_timer(conn->hold_timer, conn->hold_time);
- switch (conn->state)
- {
- case BS_OPENCONFIRM:
- bgp_conn_enter_established_state(conn);
- break;
- case BS_ESTABLISHED:
- break;
- default:
- bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0);
- }
-}
-static void
-bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, uint len)
-{
- struct bgp_proto *p = conn->bgp;
+ if (conn->state == BS_OPENCONFIRM)
+ { bgp_conn_enter_established_state(conn); return; }
if (conn->state != BS_ESTABLISHED)
- { bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0); return; }
-
- if (!p->cf->enable_refresh)
- { bgp_error(conn, 1, 3, pkt+18, 1); return; }
-
- if (len < (BGP_HEADER_LENGTH + 4))
- { bgp_error(conn, 1, 2, pkt+16, 2); return; }
-
- if (len > (BGP_HEADER_LENGTH + 4))
- { bgp_error(conn, 7, 1, pkt, MIN(len, 2048)); return; }
-
- /* FIXME - we ignore AFI/SAFI values, as we support
- just one value and even an error code for an invalid
- request is not defined */
-
- /* RFC 7313 redefined reserved field as RR message subtype */
- uint subtype = conn->peer_enhanced_refresh_support ? pkt[21] : BGP_RR_REQUEST;
-
- switch (subtype)
- {
- case BGP_RR_REQUEST:
- BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH");
- proto_request_feeding(&p->p);
- break;
-
- case BGP_RR_BEGIN:
- BGP_TRACE(D_PACKETS, "Got BEGIN-OF-RR");
- bgp_refresh_begin(p);
- break;
-
- case BGP_RR_END:
- BGP_TRACE(D_PACKETS, "Got END-OF-RR");
- bgp_refresh_end(p);
- break;
-
- default:
- log(L_WARN "%s: Got ROUTE-REFRESH message with unknown subtype %u, ignoring",
- p->p.name, subtype);
- break;
- }
+ bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0);
}
@@ -1653,7 +2763,7 @@ bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, uint len)
* packet handler according to the packet type.
*/
static void
-bgp_rx_packet(struct bgp_conn *conn, byte *pkt, unsigned len)
+bgp_rx_packet(struct bgp_conn *conn, byte *pkt, uint len)
{
byte type = pkt[18];
@@ -1663,14 +2773,14 @@ bgp_rx_packet(struct bgp_conn *conn, byte *pkt, unsigned len)
mrt_dump_bgp_packet(conn, pkt, len);
switch (type)
- {
- case PKT_OPEN: return bgp_rx_open(conn, pkt, len);
- case PKT_UPDATE: return bgp_rx_update(conn, pkt, len);
- case PKT_NOTIFICATION: return bgp_rx_notification(conn, pkt, len);
- case PKT_KEEPALIVE: return bgp_rx_keepalive(conn);
- case PKT_ROUTE_REFRESH: return bgp_rx_route_refresh(conn, pkt, len);
- default: bgp_error(conn, 1, 3, pkt+18, 1);
- }
+ {
+ case PKT_OPEN: return bgp_rx_open(conn, pkt, len);
+ case PKT_UPDATE: return bgp_rx_update(conn, pkt, len);
+ case PKT_NOTIFICATION: return bgp_rx_notification(conn, pkt, len);
+ case PKT_KEEPALIVE: return bgp_rx_keepalive(conn);
+ case PKT_ROUTE_REFRESH: return bgp_rx_route_refresh(conn, pkt, len);
+ default: bgp_error(conn, 1, 3, pkt+18, 1);
+ }
}
/**
@@ -1687,10 +2797,9 @@ int
bgp_rx(sock *sk, uint size)
{
struct bgp_conn *conn = sk->data;
- struct bgp_proto *p = conn->bgp;
byte *pkt_start = sk->rbuf;
byte *end = pkt_start + size;
- unsigned i, len;
+ uint i, len;
DBG("BGP: RX hook: Got %d bytes\n", size);
while (end >= pkt_start + BGP_HEADER_LENGTH)
@@ -1704,7 +2813,7 @@ bgp_rx(sock *sk, uint size)
break;
}
len = get_u16(pkt_start+16);
- if (len < BGP_HEADER_LENGTH || len > bgp_max_packet_length(p))
+ if ((len < BGP_HEADER_LENGTH) || (len > bgp_max_packet_length(conn)))
{
bgp_error(conn, 1, 2, pkt_start+16, 2);
break;
diff --git a/proto/ospf/Makefile b/proto/ospf/Makefile
index f90222cf..39e74f71 100644
--- a/proto/ospf/Makefile
+++ b/proto/ospf/Makefile
@@ -1,5 +1,6 @@
-source=ospf.c topology.c packet.c hello.c neighbor.c iface.c dbdes.c lsreq.c lsupd.c lsack.c lsalib.c rt.c
-root-rel=../../
-dir-name=proto/ospf
+src := dbdes.c hello.c iface.c lsack.c lsalib.c lsreq.c lsupd.c neighbor.c ospf.c packet.c rt.c topology.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
-include ../../Rules
+tests_objs := $(tests_objs) $(src-o-files) \ No newline at end of file
diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y
index 7b35b191..1f379bf4 100644
--- a/proto/ospf/config.Y
+++ b/proto/ospf/config.Y
@@ -82,6 +82,10 @@ ospf_proto_finish(void)
if (EMPTY_LIST(cf->area_list))
cf_error( "No configured areas in OSPF");
+ /* Define default channel */
+ if (EMPTY_LIST(this_proto->channels))
+ channel_config_new(NULL, this_proto->net_type, this_proto);
+
int areano = 0;
int backbone = 0;
int nssa = 0;
@@ -98,7 +102,7 @@ ospf_proto_finish(void)
cf->abr = areano > 1;
/* Route export or NSSA translation (RFC 3101 3.1) */
- cf->asbr = (this_proto->out_filter != FILTER_REJECT) || (nssa && cf->abr);
+ cf->asbr = (proto_cf_main_channel(this_proto)->out_filter != FILTER_REJECT) || (nssa && cf->abr);
if (cf->abr && !backbone)
{
@@ -122,7 +126,7 @@ static inline void
ospf_check_defcost(int cost)
{
if ((cost <= 0) || (cost >= LSINFINITY))
- cf_error("Default cost must be in range 1-%d", LSINFINITY-1);
+ cf_error("Default cost must be in range 1-%u", LSINFINITY-1);
}
static inline void
@@ -135,8 +139,8 @@ ospf_check_auth(void)
CF_DECLS
-CF_KEYWORDS(OSPF, AREA, OSPF_METRIC1, OSPF_METRIC2, OSPF_TAG, OSPF_ROUTER_ID)
-CF_KEYWORDS(NEIGHBORS, RFC1583COMPAT, STUB, TICK, COST, COST2, RETRANSMIT)
+CF_KEYWORDS(OSPF, V2, V3, OSPF_METRIC1, OSPF_METRIC2, OSPF_TAG, OSPF_ROUTER_ID)
+CF_KEYWORDS(AREA, NEIGHBORS, RFC1583COMPAT, STUB, TICK, COST, COST2, RETRANSMIT)
CF_KEYWORDS(HELLO, TRANSMIT, PRIORITY, DEAD, TYPE, BROADCAST, BCAST, DEFAULT)
CF_KEYWORDS(NONBROADCAST, NBMA, POINTOPOINT, PTP, POINTOMULTIPOINT, PTMP)
CF_KEYWORDS(NONE, SIMPLE, AUTHENTICATION, STRICT, CRYPTOGRAPHIC, TTL, SECURITY)
@@ -146,23 +150,30 @@ CF_KEYWORDS(WAIT, DELAY, LSADB, ECMP, LIMIT, WEIGHT, NSSA, TRANSLATOR, STABILITY
CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL, NETMASK, TX, PRIORITY, LENGTH)
CF_KEYWORDS(SECONDARY, MERGE, LSA, SUPPRESSION)
-%type <t> opttext
%type <ld> lsadb_args
-%type <i> nbma_eligible
+%type <i> ospf_variant nbma_eligible
CF_GRAMMAR
CF_ADDTO(proto, ospf_proto '}' { ospf_proto_finish(); } )
-ospf_proto_start: proto_start OSPF {
- this_proto = proto_config_new(&proto_ospf, $1);
- init_list(&OSPF_CFG->area_list);
- init_list(&OSPF_CFG->vlink_list);
- OSPF_CFG->tick = OSPF_DEFAULT_TICK;
- OSPF_CFG->ospf2 = OSPF_IS_V2;
- }
+ospf_variant:
+ OSPF { $$ = 1; }
+ | OSPF V2 { $$ = 1; }
+ | OSPF V3 { $$ = 0; }
;
+ospf_proto_start: proto_start ospf_variant
+{
+ this_proto = proto_config_new(&proto_ospf, $1);
+ this_proto->net_type = $2 ? NET_IP4 : NET_IP6;
+
+ init_list(&OSPF_CFG->area_list);
+ init_list(&OSPF_CFG->vlink_list);
+ OSPF_CFG->tick = OSPF_DEFAULT_TICK;
+ OSPF_CFG->ospf2 = $2;
+};
+
ospf_proto:
ospf_proto_start proto_name '{'
| ospf_proto ospf_proto_item ';'
@@ -170,13 +181,14 @@ ospf_proto:
ospf_proto_item:
proto_item
+ | proto_channel
| RFC1583COMPAT bool { OSPF_CFG->rfc1583 = $2; }
| STUB ROUTER bool { OSPF_CFG->stub_router = $3; }
| ECMP bool { OSPF_CFG->ecmp = $2 ? OSPF_DEFAULT_ECMP_LIMIT : 0; }
- | ECMP bool LIMIT expr { OSPF_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); }
+ | ECMP bool LIMIT expr { OSPF_CFG->ecmp = $2 ? $4 : 0; }
| MERGE EXTERNAL bool { OSPF_CFG->merge_external = $3; }
- | TICK expr { OSPF_CFG->tick = $2; if($2<=0) cf_error("Tick must be greater than zero"); }
- | INSTANCE ID expr { OSPF_CFG->instance_id = $3; if (($3<0) || ($3>255)) cf_error("Instance ID must be in range 0-255"); }
+ | TICK expr { OSPF_CFG->tick = $2; if($2 <= 0) cf_error("Tick must be greater than zero"); }
+ | INSTANCE ID expr { OSPF_CFG->instance_id = $3; if ($3 > 255) cf_error("Instance ID must be in range 0-255"); }
| ospf_area
;
@@ -226,10 +238,10 @@ ospf_stubnet:
;
ospf_stubnet_start:
- prefix {
+ net_ip {
this_stubnet = cfg_allocz(sizeof(struct ospf_stubnet_config));
add_tail(&this_area->stubnet_list, NODE this_stubnet);
- this_stubnet->px = $1;
+ this_stubnet->prefix = $1;
this_stubnet->cost = COST_D;
}
;
@@ -306,7 +318,7 @@ ospf_iface_item:
| REAL BROADCAST bool { OSPF_PATT->real_bcast = $3; if (!ospf_cfg_is_v2()) cf_error("Real broadcast option requires OSPFv2"); }
| PTP NETMASK bool { OSPF_PATT->ptp_netmask = $3; if (!ospf_cfg_is_v2()) cf_error("PtP 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"); }
+ | PRIORITY expr { OSPF_PATT->priority = $2 ; if ($2>255) cf_error("Priority must be in range 0-255"); }
| STRICT NONBROADCAST bool { OSPF_PATT->strictnbma = $3 ; }
| STUB bool { OSPF_PATT->stub = $2 ; }
| CHECK LINK bool { OSPF_PATT->check_link = $3; }
@@ -325,7 +337,6 @@ ospf_iface_item:
| TTL SECURITY bool { OSPF_PATT->ttl_security = $3; }
| TTL SECURITY TX ONLY { OSPF_PATT->ttl_security = 2; }
| BFD bool { OSPF_PATT->bfd = $2; cf_check_bfd($2); }
- | SECONDARY bool { OSPF_PATT->bsd_secondary = $2; }
| password_list { ospf_check_auth(); }
;
@@ -336,12 +347,11 @@ pref_list:
pref_item: pref_base pref_opt ';' ;
-pref_base: prefix
+pref_base: net_ip
{
this_pref = cfg_allocz(sizeof(struct area_net_config));
add_tail(this_nets, NODE this_pref);
- this_pref->px.addr = $1.addr;
- this_pref->px.len = $1.len;
+ this_pref->prefix = $1;
}
;
@@ -394,7 +404,7 @@ ospf_iface_start:
ospf_instance_id:
/* empty */
- | INSTANCE expr { OSPF_PATT->instance_id = $2; if (($2<0) || ($2>255)) cf_error("Instance ID must be in range 0-255"); }
+ | INSTANCE expr { OSPF_PATT->instance_id = $2; if ($2 > 255) cf_error("Instance ID must be in range 0-255"); }
;
ospf_iface_patt_list:
@@ -415,11 +425,6 @@ ospf_iface:
ospf_iface_start ospf_iface_patt_list ospf_iface_opt_list { ospf_iface_finish(); }
;
-opttext:
- TEXT
- | /* empty */ { $$ = NULL; }
- ;
-
CF_ADDTO(dynamic_attr, OSPF_METRIC1 { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_OSPF_METRIC1); })
CF_ADDTO(dynamic_attr, OSPF_METRIC2 { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_OSPF_METRIC2); })
CF_ADDTO(dynamic_attr, OSPF_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_OSPF_TAG); })
diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c
index d6904343..195b03ad 100644
--- a/proto/ospf/dbdes.c
+++ b/proto/ospf/dbdes.c
@@ -39,7 +39,7 @@ struct ospf_dbdes3_packet
static inline uint
-ospf_dbdes_hdrlen(struct ospf_proto *p UNUSED4 UNUSED6)
+ospf_dbdes_hdrlen(struct ospf_proto *p)
{
return ospf_is_v2(p) ?
sizeof(struct ospf_dbdes2_packet) : sizeof(struct ospf_dbdes3_packet);
diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c
index e00487dc..2c55155d 100644
--- a/proto/ospf/hello.c
+++ b/proto/ospf/hello.c
@@ -74,7 +74,7 @@ ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn)
((ifa->type == OSPF_IT_PTP) && !ifa->ptp_netmask))
ps->netmask = 0;
else
- ps->netmask = htonl(u32_mkmask(ifa->addr->pxlen));
+ ps->netmask = htonl(u32_mkmask(ifa->addr->prefix.pxlen));
ps->helloint = ntohs(ifa->helloint);
ps->options = ifa->oa->options;
@@ -198,7 +198,7 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa,
/* RFC 2328 10.5 */
/*
- * We may not yet havethe associate neighbor, so we use Router ID from the
+ * We may not yet have the associate neighbor, so we use Router ID from the
* packet instead of one from the neighbor structure for log messages.
*/
u32 rcv_rid = ntohl(pkt->routerid);
@@ -227,7 +227,7 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa,
if ((ifa->type != OSPF_IT_VLINK) &&
(ifa->type != OSPF_IT_PTP) &&
- ((uint) pxlen != ifa->addr->pxlen))
+ ((uint) pxlen != ifa->addr->prefix.pxlen))
DROP("prefix length mismatch", pxlen);
neighbors = ps->neighbors;
diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c
index 280fa4c1..675cf76d 100644
--- a/proto/ospf/iface.c
+++ b/proto/ospf/iface.c
@@ -55,7 +55,9 @@ ifa_tx_length(struct ospf_iface *ifa)
static inline uint
ifa_tx_hdrlen(struct ospf_iface *ifa)
{
- uint hlen = SIZE_OF_IP_HEADER;
+ struct ospf_proto *p = ifa->oa->po;
+
+ uint hlen = ospf_is_v2(p) ? IP4_HEADER_LENGTH : IP6_HEADER_LENGTH;
/* Relevant just for OSPFv2 */
if (ifa->autype == OSPF_AUTH_CRYPT)
@@ -115,6 +117,7 @@ ospf_sk_open(struct ospf_iface *ifa)
sock *sk = sk_new(ifa->pool);
sk->type = SK_IP;
+ sk->subtype = ospf_is_v2(p) ? SK_IPV4 : SK_IPV6;
sk->dport = OSPF_PROTO;
sk->saddr = ifa->addr->ip;
sk->iface = ifa->iface;
@@ -199,6 +202,7 @@ ospf_open_vlink_sk(struct ospf_proto *p)
{
sock *sk = sk_new(p->p.pool);
sk->type = SK_IP;
+ sk->subtype = ospf_is_v2(p) ? SK_IPV4 : SK_IPV6;
sk->dport = OSPF_PROTO;
/* FIXME: configurable tos/priority ? */
@@ -244,8 +248,8 @@ ospf_iface_down(struct ospf_iface *ifa)
OSPF_TRACE(D_EVENTS, "Removing interface %s (peer %I) from area %R",
ifa->ifname, ifa->addr->opposite, ifa->oa->areaid);
else
- OSPF_TRACE(D_EVENTS, "Removing interface %s (%I/%d) from area %R",
- ifa->ifname, ifa->addr->prefix, ifa->addr->pxlen, ifa->oa->areaid);
+ OSPF_TRACE(D_EVENTS, "Removing interface %s (%N) from area %R",
+ ifa->ifname, &ifa->addr->prefix, ifa->oa->areaid);
/* First of all kill all the related vlinks */
WALK_LIST(iff, p->iface_list)
@@ -530,15 +534,6 @@ ospf_iface_stubby(struct ospf_iface_patt *ip, struct ifa *addr)
if (addr->iface->flags & IF_LOOPBACK)
return 1;
- /*
- * For compatibility reasons on BSD systems, we force OSPF
- * interfaces with non-primary IP prefixes to be stub.
- */
-#if defined(OSPFv2) && !defined(CONFIG_MC_PROPER_SRC)
- if (!ip->bsd_secondary && !(addr->flags & IA_PRIMARY))
- return 1;
-#endif
-
return ip->stub;
}
@@ -557,8 +552,8 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
OSPF_TRACE(D_EVENTS, "Adding interface %s (peer %I) to area %R",
iface->name, addr->opposite, oa->areaid);
else
- OSPF_TRACE(D_EVENTS, "Adding interface %s (%I/%d) to area %R",
- iface->name, addr->prefix, addr->pxlen, oa->areaid);
+ OSPF_TRACE(D_EVENTS, "Adding interface %s (%N) to area %R",
+ iface->name, &addr->prefix, oa->areaid);
pool = rp_new(p->p.pool, "OSPF Interface");
ifa = mb_allocz(pool, sizeof(struct ospf_iface));
@@ -596,6 +591,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
if (ip->ptp_netmask < 2)
ifa->ptp_netmask = ip->ptp_netmask;
+ ifa->drip = ifa->bdrip = ospf_is_v2(p) ? IPA_NONE4 : IPA_NONE6;
ifa->type = ospf_iface_classify(ip->type, addr);
@@ -635,7 +631,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
should be used). Because OSPFv3 iface is not subnet-specific,
there is no need for ipa_in_net() check */
- if (ospf_is_v2(p) && !ipa_in_net(nb->ip, addr->prefix, addr->pxlen))
+ if (ospf_is_v2(p) && !ipa_in_netX(nb->ip, &addr->prefix))
continue;
if (ospf_is_v3(p) && !ipa_is_link_local(nb->ip))
@@ -648,7 +644,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
add_tail(&oa->po->iface_list, NODE ifa);
struct object_lock *lock = olock_new(pool);
- lock->addr = ospf_is_v2(p) ? ifa->addr->prefix : IPA_NONE;
+ lock->addr = ospf_is_v2(p) ? ipa_from_ip4(net4_prefix(&ifa->addr->prefix)) : IPA_NONE;
lock->type = OBJLOCK_IP;
lock->port = OSPF_PROTO;
lock->inst = ifa->instance_id;
@@ -898,7 +894,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
WALK_LIST(nb, new->nbma_list)
{
/* See related note in ospf_iface_new() */
- if (ospf_is_v2(p) && !ipa_in_net(nb->ip, ifa->addr->prefix, ifa->addr->pxlen))
+ if (ospf_is_v2(p) && !ipa_in_netX(nb->ip, &ifa->addr->prefix))
continue;
if (ospf_is_v3(p) && !ipa_is_link_local(nb->ip))
@@ -1085,6 +1081,9 @@ ospf_ifa_notify2(struct proto *P, uint flags, struct ifa *a)
{
struct ospf_proto *p = (struct ospf_proto *) P;
+ if (a->prefix.type != NET_IP4)
+ return;
+
if (a->flags & IA_SECONDARY)
return;
@@ -1114,6 +1113,9 @@ ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a)
{
struct ospf_proto *p = (struct ospf_proto *) P;
+ if (a->prefix.type != NET_IP6)
+ return;
+
if (a->flags & IA_SECONDARY)
return;
@@ -1166,6 +1168,9 @@ ospf_reconfigure_ifaces2(struct ospf_proto *p)
WALK_LIST(a, iface->addrs)
{
+ if (a->prefix.type != NET_IP4)
+ continue;
+
if (a->flags & IA_SECONDARY)
continue;
@@ -1184,8 +1189,8 @@ ospf_reconfigure_ifaces2(struct ospf_proto *p)
continue;
/* Hard restart */
- log(L_INFO "%s: Restarting interface %s (%I/%d) in area %R",
- p->p.name, ifa->ifname, a->prefix, a->pxlen, s.oa->areaid);
+ log(L_INFO "%s: Restarting interface %s (%N) in area %R",
+ p->p.name, ifa->ifname, &a->prefix, s.oa->areaid);
ospf_iface_shutdown(ifa);
ospf_iface_remove(ifa);
}
@@ -1209,6 +1214,9 @@ ospf_reconfigure_ifaces3(struct ospf_proto *p)
WALK_LIST(a, iface->addrs)
{
+ if (a->prefix.type != NET_IP6)
+ continue;
+
if (a->flags & IA_SECONDARY)
continue;
@@ -1340,7 +1348,7 @@ ospf_iface_info(struct ospf_iface *ifa)
else if (ifa->addr->flags & IA_PEER)
cli_msg(-1015, "Interface %s (peer %I)", ifa->ifname, ifa->addr->opposite);
else
- cli_msg(-1015, "Interface %s (%I/%d)", ifa->ifname, ifa->addr->prefix, ifa->addr->pxlen);
+ cli_msg(-1015, "Interface %s (%N)", ifa->ifname, &ifa->addr->prefix);
cli_msg(-1015, "\tType: %s%s", ospf_it[ifa->type], more);
cli_msg(-1015, "\tArea: %R (%u)", ifa->oa->areaid, ifa->oa->areaid);
diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c
index cb7b186a..b88a114d 100644
--- a/proto/ospf/lsalib.c
+++ b/proto/ospf/lsalib.c
@@ -280,21 +280,19 @@ lsa_walk_rt(struct ospf_lsa_rt_walk *rt)
void
-lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, ip_addr *ip, int *pxlen, u8 *pxopts, u32 *metric)
+lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, net_addr *net, u8 *pxopts, u32 *metric)
{
if (ospf2)
{
struct ospf_lsa_sum2 *ls = en->lsa_body;
- *ip = ipa_from_u32(en->lsa.id & ls->netmask);
- *pxlen = u32_masklen(ls->netmask);
+ net_fill_ip4(net, ip4_from_u32(en->lsa.id & ls->netmask), u32_masklen(ls->netmask));
*pxopts = 0;
*metric = ls->metric & LSA_METRIC_MASK;
}
else
{
struct ospf_lsa_sum3_net *ls = en->lsa_body;
- u16 rest;
- lsa_get_ipv6_prefix(ls->prefix, ip, pxlen, pxopts, &rest);
+ ospf_get_ipv6_prefix(ls->prefix, net, pxopts, NULL);
*metric = ls->metric & LSA_METRIC_MASK;
}
}
@@ -324,8 +322,9 @@ lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *r
if (ospf2)
{
struct ospf_lsa_ext2 *ext = en->lsa_body;
- rt->ip = ipa_from_u32(en->lsa.id & ext->netmask);
- rt->pxlen = u32_masklen(ext->netmask);
+ net_fill_ip4(&rt->net,
+ ip4_from_u32(en->lsa.id & ext->netmask),
+ u32_masklen(ext->netmask));
rt->pxopts = 0;
rt->metric = ext->metric & LSA_METRIC_MASK;
rt->ebit = ext->metric & LSA_EXT2_EBIT;
@@ -339,14 +338,13 @@ lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *r
else
{
struct ospf_lsa_ext3 *ext = en->lsa_body;
- u16 rest;
- u32 *buf = lsa_get_ipv6_prefix(ext->rest, &rt->ip, &rt->pxlen, &rt->pxopts, &rest);
+ u32 *buf = ospf_get_ipv6_prefix(ext->rest, &rt->net, &rt->pxopts, NULL);
rt->metric = ext->metric & LSA_METRIC_MASK;
rt->ebit = ext->metric & LSA_EXT3_EBIT;
rt->fbit = ext->metric & LSA_EXT3_FBIT;
if (rt->fbit)
- buf = lsa_get_ipv6_addr(buf, &rt->fwaddr);
+ buf = ospf_get_ipv6_addr(buf, &rt->fwaddr);
else
rt->fwaddr = IPA_NONE;
@@ -452,7 +450,7 @@ lsa_validate_sum3_net(struct ospf_lsa_header *lsa, struct ospf_lsa_sum3_net *bod
return 0;
u8 pxl = pxlen(body->prefix);
- if (pxl > MAX_PREFIX_LENGTH)
+ if (pxl > IP6_MAX_PREFIX_LENGTH)
return 0;
if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum3_net) +
@@ -491,11 +489,11 @@ lsa_validate_ext3(struct ospf_lsa_header *lsa, struct ospf_lsa_ext3 *body)
return 0;
u8 pxl = pxlen(body->rest);
- if (pxl > MAX_PREFIX_LENGTH)
+ if (pxl > IP6_MAX_PREFIX_LENGTH)
return 0;
int len = IPV6_PREFIX_SPACE(pxl);
- if (body->metric & LSA_EXT3_FBIT) // forwardinf address
+ if (body->metric & LSA_EXT3_FBIT) // forwarding address
len += 16;
if (body->metric & LSA_EXT3_TBIT) // route tag
len += 4;
@@ -520,7 +518,7 @@ lsa_validate_pxlist(struct ospf_lsa_header *lsa, u32 pxcount, uint offset, u8 *p
return 0;
u8 pxl = pxlen((u32 *) (pbuf + offset));
- if (pxl > MAX_PREFIX_LENGTH)
+ if (pxl > IP6_MAX_PREFIX_LENGTH)
return 0;
offset += IPV6_PREFIX_SPACE(pxl);
@@ -554,8 +552,8 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body)
/**
* lsa_validate - check whether given LSA is valid
* @lsa: LSA header
- * @lsa_type: one of %LSA_T_xxx
- * @ospf2: %true means OSPF version 2, %false means OSPF version 3
+ * @lsa_type: internal LSA type (%LSA_T_xxx)
+ * @ospf2: %true for OSPFv2, %false for OSPFv3
* @body: pointer to LSA body
*
* Checks internal structure of given LSA body (minimal length,
diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h
index 638b3525..c93f0295 100644
--- a/proto/ospf/lsalib.h
+++ b/proto/ospf/lsalib.h
@@ -41,7 +41,7 @@ void lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *do
static inline void lsa_get_type_domain(struct ospf_lsa_header *lsa, struct ospf_iface *ifa, u32 *otype, u32 *domain)
{ lsa_get_type_domain_(lsa->type_raw, ifa, otype, domain); }
-static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p UNUSED4 UNUSED6)
+static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p)
{ return ospf_is_v2(p) ? (h->type_raw & LSA_T_V2_MASK) : h->type_raw; }
@@ -55,7 +55,7 @@ u16 lsa_verify_checksum(const void *lsa_n, int lsa_len);
int lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2);
void lsa_walk_rt_init(struct ospf_proto *po, struct top_hash_entry *act, struct ospf_lsa_rt_walk *rt);
int lsa_walk_rt(struct ospf_lsa_rt_walk *rt);
-void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, ip_addr *ip, int *pxlen, u8 *pxopts, u32 *metric);
+void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, net_addr *net, u8 *pxopts, u32 *metric);
void lsa_parse_sum_rt(struct top_hash_entry *en, int ospf2, u32 *drid, u32 *metric, u32 *options);
void lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *rt);
int lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body);
diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c
index 9fe3c028..b68ba6f4 100644
--- a/proto/ospf/neighbor.c
+++ b/proto/ospf/neighbor.c
@@ -359,7 +359,7 @@ can_do_adj(struct ospf_neighbor *n)
}
-static inline u32 neigh_get_id(struct ospf_proto *p UNUSED4 UNUSED6, struct ospf_neighbor *n)
+static inline u32 neigh_get_id(struct ospf_proto *p, struct ospf_neighbor *n)
{ return ospf_is_v2(p) ? ipa_to_u32(n->ip) : n->rid; }
static struct ospf_neighbor *
@@ -507,13 +507,14 @@ ospf_dr_election(struct ospf_iface *ifa)
u32 old_drid = ifa->drid;
u32 old_bdrid = ifa->bdrid;
+ ip_addr none = ospf_is_v2(p) ? IPA_NONE4 : IPA_NONE6;
ifa->drid = ndr ? ndr->rid : 0;
- ifa->drip = ndr ? ndr->ip : IPA_NONE;
+ ifa->drip = ndr ? ndr->ip : none;
ifa->dr_iface_id = ndr ? ndr->iface_id : 0;
ifa->bdrid = nbdr ? nbdr->rid : 0;
- ifa->bdrip = nbdr ? nbdr->ip : IPA_NONE;
+ ifa->bdrip = nbdr ? nbdr->ip : none;
DBG("DR=%R, BDR=%R\n", ifa->drid, ifa->bdrid);
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index d5d5d354..daf76ff2 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -102,18 +102,11 @@
static int ospf_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool);
static struct ea_list *ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool);
static void ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs);
-static int ospf_reload_routes(struct proto *P);
+static void ospf_reload_routes(struct channel *C);
static int ospf_rte_better(struct rte *new, struct rte *old);
static int ospf_rte_same(struct rte *new, struct rte *old);
static void ospf_disp(timer *timer);
-static void
-ospf_area_initfib(struct fib_node *fn)
-{
- struct area_net *an = (struct area_net *) fn;
- an->hidden = 0;
- an->active = 0;
-}
static void
add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac)
@@ -122,18 +115,20 @@ add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac)
struct area_net_config *anc;
struct area_net *an;
- fib_init(&oa->net_fib, p->p.pool, sizeof(struct area_net), 0, ospf_area_initfib);
- fib_init(&oa->enet_fib, p->p.pool, sizeof(struct area_net), 0, ospf_area_initfib);
+ fib_init(&oa->net_fib, p->p.pool, ospf_is_v2(p) ? NET_IP4 : NET_IP6,
+ sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL);
+ fib_init(&oa->enet_fib, p->p.pool, ospf_is_v2(p) ? NET_IP4 : NET_IP6,
+ sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL);
WALK_LIST(anc, ac->net_list)
{
- an = (struct area_net *) fib_get(&oa->net_fib, &anc->px.addr, anc->px.len);
+ an = fib_get(&oa->net_fib, &anc->prefix);
an->hidden = anc->hidden;
}
WALK_LIST(anc, ac->enet_list)
{
- an = (struct area_net *) fib_get(&oa->enet_fib, &anc->px.addr, anc->px.len);
+ an = fib_get(&oa->enet_fib, &anc->prefix);
an->hidden = anc->hidden;
an->tag = anc->tag;
}
@@ -154,7 +149,7 @@ ospf_area_add(struct ospf_proto *p, struct ospf_area_config *ac)
oa->areaid = ac->areaid;
oa->rt = NULL;
oa->po = p;
- fib_init(&oa->rtr, p->p.pool, sizeof(ort), 0, ospf_rt_initort);
+ fib_init(&oa->rtr, p->p.pool, NET_IP4, sizeof(ort), OFFSETOF(ort, fn), 0, NULL);
add_area_nets(oa, ac);
if (oa->areaid == 0)
@@ -240,10 +235,13 @@ ospf_start(struct proto *P)
p->lsab_size = 256;
p->lsab_used = 0;
p->lsab = mb_alloc(P->pool, p->lsab_size);
- p->nhpool = lp_new(P->pool, 12*sizeof(struct mpnh));
+ p->nhpool = lp_new(P->pool, 12*sizeof(struct nexthop));
init_list(&(p->iface_list));
init_list(&(p->area_list));
- fib_init(&p->rtf, P->pool, sizeof(ort), 0, ospf_rt_initort);
+ fib_init(&p->rtf, P->pool, p->ospf2 ? NET_IP4 : NET_IP6,
+ sizeof(ort), OFFSETOF(ort, fn), 0, NULL);
+ if (ospf_is_v3(p))
+ idm_init(&p->idm, P->pool, 16);
p->areano = 0;
p->gr = ospf_top_new(p, P->pool);
s_init_list(&(p->lsal));
@@ -299,15 +297,16 @@ ospf_dump(struct proto *P)
}
static struct proto *
-ospf_init(struct proto_config *c)
+ospf_init(struct proto_config *CF)
{
- struct ospf_config *oc = (struct ospf_config *) c;
- struct proto *P = proto_new(c, sizeof(struct ospf_proto));
+ struct ospf_config *cf = (struct ospf_config *) CF;
+ struct proto *P = proto_new(CF);
+
+ P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
- P->accept_ra_types = RA_OPTIMAL;
P->rt_notify = ospf_rt_notify;
P->if_notify = ospf_if_notify;
- P->ifa_notify = oc->ospf2 ? ospf_ifa_notify2 : ospf_ifa_notify3;
+ P->ifa_notify = cf->ospf2 ? ospf_ifa_notify2 : ospf_ifa_notify3;
P->import_control = ospf_import_control;
P->reload_routes = ospf_reload_routes;
P->make_tmp_attrs = ospf_make_tmp_attrs;
@@ -391,17 +390,16 @@ ospf_schedule_rtcalc(struct ospf_proto *p)
p->calcrt = 1;
}
-static int
-ospf_reload_routes(struct proto *P)
+static void
+ospf_reload_routes(struct channel *C)
{
- struct ospf_proto *p = (struct ospf_proto *) P;
+ struct ospf_proto *p = (struct ospf_proto *) C->proto;
- if (p->calcrt != 2)
- OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation with route reload");
+ if (p->calcrt == 2)
+ return;
+ OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation with route reload");
p->calcrt = 2;
-
- return 1;
}
@@ -506,9 +504,9 @@ ospf_shutdown(struct proto *P)
ospf_iface_shutdown(ifa);
/* Cleanup locked rta entries */
- FIB_WALK(&p->rtf, nftmp)
+ FIB_WALK(&p->rtf, ort, nf)
{
- rta_free(((ort *) nftmp)->old_rta);
+ rta_free(nf->old_rta);
}
FIB_WALK_END;
@@ -639,17 +637,20 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)
* nonbroadcast network, cost of interface, etc.
*/
static int
-ospf_reconfigure(struct proto *P, struct proto_config *c)
+ospf_reconfigure(struct proto *P, struct proto_config *CF)
{
struct ospf_proto *p = (struct ospf_proto *) P;
struct ospf_config *old = (struct ospf_config *) (P->cf);
- struct ospf_config *new = (struct ospf_config *) c;
+ struct ospf_config *new = (struct ospf_config *) CF;
struct ospf_area_config *nac;
struct ospf_area *oa, *oax;
struct ospf_iface *ifa, *ifx;
struct ospf_iface_patt *ip;
- if (proto_get_router_id(c) != p->router_id)
+ if (proto_get_router_id(CF) != p->router_id)
+ return 0;
+
+ if (p->ospf2 != new->ospf2)
return 0;
if (p->rfc1583 != new->rfc1583)
@@ -658,6 +659,9 @@ ospf_reconfigure(struct proto *P, struct proto_config *c)
if (old->abr != new->abr)
return 0;
+ if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
+ return 0;
+
p->stub_router = new->stub_router;
p->merge_external = new->merge_external;
p->asbr = new->asbr;
@@ -746,7 +750,6 @@ ospf_sh(struct proto *P)
struct ospf_iface *ifa;
struct ospf_neighbor *n;
int ifano, nno, adjno, firstfib;
- struct area_net *anet;
if (p->p.proto_state != PS_UP)
{
@@ -795,29 +798,27 @@ ospf_sh(struct proto *P)
cli_msg(-1014, "\t\tNumber of adjacent neighbors:\t%u", adjno);
firstfib = 1;
- FIB_WALK(&oa->net_fib, nftmp)
+ FIB_WALK(&oa->net_fib, struct area_net, anet)
{
- anet = (struct area_net *) nftmp;
if(firstfib)
{
cli_msg(-1014, "\t\tArea networks:");
firstfib = 0;
}
- cli_msg(-1014, "\t\t\t%1I/%u\t%s\t%s", anet->fn.prefix, anet->fn.pxlen,
+ cli_msg(-1014, "\t\t\t%1N\t%s\t%s", anet->fn.addr,
anet->hidden ? "Hidden" : "Advertise", anet->active ? "Active" : "");
}
FIB_WALK_END;
firstfib = 1;
- FIB_WALK(&oa->enet_fib, nftmp)
+ FIB_WALK(&oa->enet_fib, struct area_net, anet)
{
- anet = (struct area_net *) nftmp;
if(firstfib)
{
cli_msg(-1014, "\t\tArea external networks:");
firstfib = 0;
}
- cli_msg(-1014, "\t\t\t%1I/%u\t%s\t%s", anet->fn.prefix, anet->fn.pxlen,
+ cli_msg(-1014, "\t\t\t%1N\t%s\t%s", anet->fn.addr,
anet->hidden ? "Hidden" : "Advertise", anet->active ? "Active" : "");
}
FIB_WALK_END;
@@ -1074,13 +1075,12 @@ show_lsa_network(struct top_hash_entry *he, int ospf2)
static inline void
show_lsa_sum_net(struct top_hash_entry *he, int ospf2)
{
- ip_addr ip;
- int pxlen;
+ net_addr net;
u8 pxopts;
u32 metric;
- lsa_parse_sum_net(he, ospf2, &ip, &pxlen, &pxopts, &metric);
- cli_msg(-1016, "\t\txnetwork %I/%d metric %u", ip, pxlen, metric);
+ lsa_parse_sum_net(he, ospf2, &net, &pxopts, &metric);
+ cli_msg(-1016, "\t\txnetwork %N metric %u", &net, metric);
}
static inline void
@@ -1099,7 +1099,7 @@ static inline void
show_lsa_external(struct top_hash_entry *he, int ospf2)
{
struct ospf_lsa_ext_local rt;
- char str_via[STD_ADDRESS_P_LENGTH + 8] = "";
+ char str_via[IPA_MAX_TEXT_LENGTH + 8] = "";
char str_tag[16] = "";
if (he->lsa_type == LSA_T_EXT)
@@ -1113,19 +1113,15 @@ show_lsa_external(struct top_hash_entry *he, int ospf2)
if (rt.tag)
bsprintf(str_tag, " tag %08x", rt.tag);
- cli_msg(-1016, "\t\t%s %I/%d metric%s %u%s%s",
+ cli_msg(-1016, "\t\t%s %N metric%s %u%s%s",
(he->lsa_type == LSA_T_NSSA) ? "nssa-ext" : "external",
- rt.ip, rt.pxlen, rt.ebit ? "2" : "", rt.metric, str_via, str_tag);
+ &rt.net, rt.ebit ? "2" : "", rt.metric, str_via, str_tag);
}
static inline void
show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode)
{
struct ospf_lsa_prefix *px = he->lsa_body;
- ip_addr pxa;
- int pxlen;
- u8 pxopts;
- u16 metric;
u32 *buf;
int i;
@@ -1141,14 +1137,18 @@ show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode)
buf = px->rest;
for (i = 0; i < px->pxcount; i++)
- {
- buf = lsa_get_ipv6_prefix(buf, &pxa, &pxlen, &pxopts, &metric);
+ {
+ net_addr net;
+ u8 pxopts;
+ u16 metric;
- if (px->ref_type == LSA_T_RT)
- cli_msg(-1016, "\t\tstubnet %I/%d metric %u", pxa, pxlen, metric);
- else
- cli_msg(-1016, "\t\taddress %I/%d", pxa, pxlen);
- }
+ buf = ospf_get_ipv6_prefix(buf, &net, &pxopts, &metric);
+
+ if (px->ref_type == LSA_T_RT)
+ cli_msg(-1016, "\t\tstubnet %N metric %u", &net, metric);
+ else
+ cli_msg(-1016, "\t\taddress %N", &net);
+ }
}
void
@@ -1468,6 +1468,8 @@ struct protocol proto_ospf = {
.template = "ospf%d",
.attr_class = EAP_OSPF,
.preference = DEF_PREF_OSPF,
+ .channel_mask = NB_IP,
+ .proto_size = sizeof(struct ospf_proto),
.config_size = sizeof(struct ospf_config),
.init = ospf_init,
.dump = ospf_dump,
diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h
index 81c610d5..e3eae2b5 100644
--- a/proto/ospf/ospf.h
+++ b/proto/ospf/ospf.h
@@ -14,11 +14,11 @@
#include "nest/bird.h"
#include "lib/checksum.h"
-#include "lib/ip.h"
+#include "lib/idm.h"
#include "lib/lists.h"
#include "lib/slists.h"
#include "lib/socket.h"
-#include "lib/timer.h"
+#include "sysdep/unix/timer.h"
#include "lib/resource.h"
#include "nest/protocol.h"
#include "nest/iface.h"
@@ -37,14 +37,6 @@
#endif
-#ifdef IPV6
-#define OSPF_IS_V2 0
-#else
-#define OSPF_IS_V2 1
-#endif
-
-// FIXME: MAX_PREFIX_LENGTH
-
#define OSPF_TRACE(flags, msg, args...) \
do { if ((p->p.debug & flags) || OSPF_FORCE_DEBUG) \
log(L_TRACE "%s: " msg, p->p.name , ## args ); } while(0)
@@ -87,7 +79,6 @@
#define OSPF_VLINK_ID_OFFSET 0x80000000
-
struct ospf_config
{
struct proto_config c;
@@ -125,24 +116,24 @@ struct ospf_area_config
struct area_net_config
{
node n;
- struct prefix px;
+ net_addr prefix;
u32 tag;
u8 hidden;
};
struct area_net
{
- struct fib_node fn;
u32 metric; /* With possible LSA_EXT3_EBIT for NSSA area nets */
u32 tag;
u8 hidden;
u8 active;
+ struct fib_node fn;
};
struct ospf_stubnet_config
{
node n;
- struct prefix px;
+ net_addr prefix;
u32 cost;
u8 hidden;
u8 summary;
@@ -189,7 +180,6 @@ struct ospf_iface_patt
u8 ptp_netmask; /* bool + 2 for unspecified */
u8 ttl_security; /* bool + 2 for TX only */
u8 bfd;
- u8 bsd_secondary;
list *passwords;
};
@@ -220,6 +210,7 @@ struct ospf_proto
int areano; /* Number of area I belong to */
int padj; /* Number of neighbors in Exchange or Loading state */
struct fib rtf; /* Routing table */
+ struct idm idm; /* OSPFv3 LSA ID map */
byte ospf2; /* OSPF v2 or v3 */
byte rfc1583; /* RFC1583 compatibility */
byte stub_router; /* Do not forward transit traffic */
@@ -682,8 +673,8 @@ struct ospf_lsa_ext3
struct ospf_lsa_ext_local
{
- ip_addr ip, fwaddr;
- int pxlen;
+ net_addr net;
+ ip_addr fwaddr;
u32 metric, ebit, fbit, tag, propagate;
u8 pxopts;
};
@@ -721,73 +712,80 @@ lsa_net_count(struct ospf_lsa_header *lsa)
/* In ospf_area->rtr we store paths to routers, but we use RID (and not IP address)
as index, so we need to encapsulate RID to IP address */
-#define ipa_from_rid(x) ipa_from_u32(x)
-#define ipa_to_rid(x) ipa_to_u32(x)
+#define net_from_rid(x) NET_ADDR_IP4(ip4_from_u32(x), IP4_MAX_PREFIX_LENGTH)
+#define rid_from_net(x) ip4_to_u32(((net_addr_ip4 *) x)->prefix)
#define IPV6_PREFIX_SPACE(x) ((((x) + 63) / 32) * 4)
#define IPV6_PREFIX_WORDS(x) (((x) + 63) / 32)
-/* FIXME: these four functions should be significantly redesigned w.r.t. integration,
+/* FIXME: these functions should be significantly redesigned w.r.t. integration,
also should be named as ospf3_* instead of *_ipv6_* */
+static inline int
+ospf_valid_prefix(net_addr *n)
+{
+ /* In OSPFv2, prefix is stored as netmask; ip4_masklen() returns 255 for invalid one */
+ return n->pxlen <= IP6_MAX_PREFIX_LENGTH;
+}
+
static inline u32 *
-lsa_get_ipv6_prefix(u32 *buf, ip_addr *addr, int *pxlen, u8 *pxopts, u16 *rest)
+ospf_get_ipv6_prefix(u32 *buf, net_addr *N, u8 *pxopts, u16 *rest)
{
- u8 pxl = (*buf >> 24);
- *pxopts = (*buf >> 16);
- *rest = *buf;
- *pxlen = pxl;
+ net_addr_ip6 *net = (void *) N;
+ u8 pxlen = (*buf >> 24);
+ *pxopts = (*buf >> 16) & 0xff;
+ if (rest) *rest = *buf & 0xffff;
buf++;
- *addr = IPA_NONE;
+ *net = NET_ADDR_IP6(IP6_NONE, pxlen);
-#ifdef IPV6
- if (pxl > 0)
- _I0(*addr) = *buf++;
- if (pxl > 32)
- _I1(*addr) = *buf++;
- if (pxl > 64)
- _I2(*addr) = *buf++;
- if (pxl > 96)
- _I3(*addr) = *buf++;
+ if (pxlen > 0)
+ _I0(net->prefix) = *buf++;
+ if (pxlen > 32)
+ _I1(net->prefix) = *buf++;
+ if (pxlen > 64)
+ _I2(net->prefix) = *buf++;
+ if (pxlen > 96)
+ _I3(net->prefix) = *buf++;
/* Clean up remaining bits */
- if (pxl < 128)
- addr->addr[pxl / 32] &= u32_mkmask(pxl % 32);
-#endif
+ if (pxlen < 128)
+ net->prefix.addr[pxlen / 32] &= u32_mkmask(pxlen % 32);
return buf;
}
static inline u32 *
-lsa_get_ipv6_addr(u32 *buf, ip_addr *addr)
+ospf_get_ipv6_addr(u32 *buf, ip_addr *addr)
{
- *addr = *(ip_addr *) buf;
+ *addr = ipa_from_ip6(*(ip6_addr *) buf);
return buf + 4;
}
static inline u32 *
-put_ipv6_prefix(u32 *buf, ip_addr addr UNUSED4, u8 pxlen UNUSED4, u8 pxopts UNUSED4, u16 lh UNUSED4)
+ospf_put_ipv6_prefix(u32 *buf, net_addr *N, u8 pxopts, u16 rest)
{
-#ifdef IPV6
- *buf++ = ((pxlen << 24) | (pxopts << 16) | lh);
+ net_addr_ip6 *net = (void *) N;
+ u32 pxlen = net->pxlen;
+
+ *buf++ = ((pxlen << 24) | (pxopts << 16) | rest);
if (pxlen > 0)
- *buf++ = _I0(addr);
+ *buf++ = _I0(net->prefix);
if (pxlen > 32)
- *buf++ = _I1(addr);
+ *buf++ = _I1(net->prefix);
if (pxlen > 64)
- *buf++ = _I2(addr);
+ *buf++ = _I2(net->prefix);
if (pxlen > 96)
- *buf++ = _I3(addr);
-#endif
+ *buf++ = _I3(net->prefix);
+
return buf;
}
static inline u32 *
-put_ipv6_addr(u32 *buf, ip_addr addr)
+ospf_put_ipv6_addr(u32 *buf, ip_addr addr)
{
- *(ip_addr *) buf = addr;
+ *(ip6_addr *) buf = ipa_to_ip6(addr);
return buf + 4;
}
@@ -831,17 +829,13 @@ static inline void ospf_notify_net_lsa(struct ospf_iface *ifa)
static inline void ospf_notify_link_lsa(struct ospf_iface *ifa)
{ ifa->update_link_lsa = 1; }
-
-#define ospf_is_v2(X) OSPF_IS_V2
-#define ospf_is_v3(X) (!OSPF_IS_V2)
-/*
static inline int ospf_is_v2(struct ospf_proto *p)
{ return p->ospf2; }
static inline int ospf_is_v3(struct ospf_proto *p)
{ return ! p->ospf2; }
-*/
-static inline int ospf_get_version(struct ospf_proto *p UNUSED4 UNUSED6)
+
+static inline int ospf_get_version(struct ospf_proto *p)
{ return ospf_is_v2(p) ? 2 : 3; }
struct ospf_area *ospf_find_area(struct ospf_proto *p, u32 aid);
@@ -925,7 +919,7 @@ static inline void ospf_send_to_des(struct ospf_iface *ifa)
#define SKIP(DSC) do { err_dsc = DSC; goto skip; } while(0)
#endif
-static inline uint ospf_pkt_hdrlen(struct ospf_proto *p UNUSED4 UNUSED6)
+static inline uint ospf_pkt_hdrlen(struct ospf_proto *p)
{ return ospf_is_v2(p) ? (sizeof(struct ospf_packet) + sizeof(union ospf_auth)) : sizeof(struct ospf_packet); }
static inline void * ospf_tx_buffer(struct ospf_iface *ifa)
diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c
index 6b6a97a4..b84780d3 100644
--- a/proto/ospf/packet.c
+++ b/proto/ospf/packet.c
@@ -203,7 +203,7 @@ drop:
/**
* ospf_rx_hook
* @sk: socket we received the packet.
- * @len: size of the packet
+ * @len: length of the packet
*
* This is the entry point for messages from neighbors. Many checks (like
* authentication, checksums, size) are done before the packet is passed to
@@ -231,7 +231,7 @@ ospf_rx_hook(sock *sk, uint len)
return 1;
int src_local, dst_local, dst_mcast;
- src_local = ipa_in_net(sk->faddr, ifa->addr->prefix, ifa->addr->pxlen);
+ src_local = ipa_in_netX(sk->faddr, &ifa->addr->prefix);
dst_local = ipa_equal(sk->laddr, ifa->addr->ip);
dst_mcast = ipa_equal(sk->laddr, ifa->all_routers) || ipa_equal(sk->laddr, ifa->des_routers);
@@ -491,8 +491,8 @@ ospf_send_to_agt(struct ospf_iface *ifa, u8 state)
void
ospf_send_to_bdr(struct ospf_iface *ifa)
{
- if (ipa_nonzero(ifa->drip))
+ if (ipa_nonzero2(ifa->drip))
ospf_send_to(ifa, ifa->drip);
- if (ipa_nonzero(ifa->bdrip))
+ if (ipa_nonzero2(ifa->bdrip))
ospf_send_to(ifa, ifa->bdrip);
}
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index 368e3d05..df9eb75b 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -21,17 +21,8 @@ static inline void reset_ri(ort *ort)
bzero(&ort->n, sizeof(orta));
}
-void
-ospf_rt_initort(struct fib_node *fn)
-{
- ort *ri = (ort *) fn;
- reset_ri(ri);
- ri->old_rta = NULL;
- ri->fn.flags = 0;
-}
-
static inline int
-nh_is_vlink(struct mpnh *nhs)
+nh_is_vlink(struct nexthop *nhs)
{
return !nhs->iface;
}
@@ -42,20 +33,19 @@ unresolved_vlink(ort *ort)
return ort->n.nhs && nh_is_vlink(ort->n.nhs);
}
-static inline struct mpnh *
+static inline struct nexthop *
new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, byte weight)
{
- struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh));
+ struct nexthop *nh = lp_allocz(p->nhpool, sizeof(struct nexthop));
nh->gw = gw;
nh->iface = iface;
- nh->next = NULL;
nh->weight = weight;
return nh;
}
/* Returns true if there are device nexthops in n */
static inline int
-has_device_nexthops(const struct mpnh *n)
+has_device_nexthops(const struct nexthop *n)
{
for (; n; n = n->next)
if (ipa_zero(n->gw))
@@ -65,13 +55,13 @@ has_device_nexthops(const struct mpnh *n)
}
/* Replace device nexthops with nexthops to gw */
-static struct mpnh *
-fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
+static struct nexthop *
+fix_device_nexthops(struct ospf_proto *p, const struct nexthop *n, ip_addr gw)
{
- struct mpnh *root1 = NULL;
- struct mpnh *root2 = NULL;
- struct mpnh **nn1 = &root1;
- struct mpnh **nn2 = &root2;
+ struct nexthop *root1 = NULL;
+ struct nexthop *root2 = NULL;
+ struct nexthop **nn1 = &root1;
+ struct nexthop **nn2 = &root2;
if (!p->ecmp)
return new_nexthop(p, gw, n->iface, n->weight);
@@ -82,7 +72,7 @@ fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
for (; n; n = n->next)
{
- struct mpnh *nn = new_nexthop(p, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight);
+ struct nexthop *nn = new_nexthop(p, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight);
if (ipa_zero(n->gw))
{
@@ -96,7 +86,7 @@ fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
}
}
- return mpnh_merge(root1, root2, 1, 1, p->ecmp, p->nhpool);
+ return nexthop_merge(root1, root2, 1, 1, p->ecmp, p->nhpool);
}
@@ -292,7 +282,7 @@ ort_merge(struct ospf_proto *p, ort *o, const orta *new)
if (old->nhs != new->nhs)
{
- old->nhs = mpnh_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
+ old->nhs = nexthop_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
p->ecmp, p->nhpool);
old->nhs_reuse = 1;
}
@@ -308,7 +298,7 @@ ort_merge_ext(struct ospf_proto *p, ort *o, const orta *new)
if (old->nhs != new->nhs)
{
- old->nhs = mpnh_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
+ old->nhs = nexthop_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
p->ecmp, p->nhpool);
old->nhs_reuse = 1;
}
@@ -334,9 +324,9 @@ ort_merge_ext(struct ospf_proto *p, ort *o, const orta *new)
static inline void
-ri_install_net(struct ospf_proto *p, ip_addr prefix, int pxlen, const orta *new)
+ri_install_net(struct ospf_proto *p, net_addr *net, const orta *new)
{
- ort *old = (ort *) fib_get(&p->rtf, &prefix, pxlen);
+ ort *old = fib_get(&p->rtf, net);
int cmp = orta_compare(p, new, &old->n);
if (cmp > 0)
@@ -348,8 +338,8 @@ ri_install_net(struct ospf_proto *p, ip_addr prefix, int pxlen, const orta *new)
static inline void
ri_install_rt(struct ospf_area *oa, u32 rid, const orta *new)
{
- ip_addr addr = ipa_from_rid(rid);
- ort *old = (ort *) fib_get(&oa->rtr, &addr, MAX_PREFIX_LENGTH);
+ net_addr_ip4 nrid = net_from_rid(rid);
+ ort *old = fib_get(&oa->rtr, (net_addr *) &nrid);
int cmp = orta_compare(oa->po, new, &old->n);
if (cmp > 0)
@@ -359,17 +349,19 @@ ri_install_rt(struct ospf_area *oa, u32 rid, const orta *new)
}
static inline void
-ri_install_asbr(struct ospf_proto *p, ip_addr *addr, const orta *new)
+ri_install_asbr(struct ospf_proto *p, u32 rid, const orta *new)
{
- ort *old = (ort *) fib_get(&p->backbone->rtr, addr, MAX_PREFIX_LENGTH);
+ net_addr_ip4 nrid = net_from_rid(rid);
+ ort *old = fib_get(&p->backbone->rtr, (net_addr *) &nrid);
+
if (orta_compare_asbr(p, new, &old->n) > 0)
ort_replace(old, new);
}
static inline void
-ri_install_ext(struct ospf_proto *p, ip_addr prefix, int pxlen, const orta *new)
+ri_install_ext(struct ospf_proto *p, net_addr *net, const orta *new)
{
- ort *old = (ort *) fib_get(&p->rtf, &prefix, pxlen);
+ ort *old = fib_get(&p->rtf, net);
int cmp = orta_compare_ext(p, new, &old->n);
if (cmp > 0)
@@ -404,7 +396,7 @@ px_pos_to_ifa(struct ospf_area *oa, int pos)
static void
-add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_hash_entry *en, int pos)
+add_network(struct ospf_area *oa, net_addr *net, int metric, struct top_hash_entry *en, int pos)
{
struct ospf_proto *p = oa->po;
@@ -419,7 +411,7 @@ add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_
.nhs = en->nhs
};
- if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH)
+ if (!ospf_valid_prefix(net))
{
log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)",
p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt);
@@ -440,7 +432,7 @@ add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_
nf.nhs = ifa ? new_nexthop(p, IPA_NONE, ifa->iface, ifa->ecmp_weight) : NULL;
}
- ri_install_net(p, px, pxlen, &nf);
+ ri_install_net(p, net, &nf);
}
@@ -451,8 +443,7 @@ spfa_process_rt(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entr
struct ospf_lsa_rt *rt = act->lsa_body;
struct ospf_lsa_rt_walk rtl;
struct top_hash_entry *tmp;
- ip_addr prefix;
- int pxlen, i;
+ int i;
if (rt->options & OPT_RT_V)
oa->trcap = 1;
@@ -502,9 +493,10 @@ spfa_process_rt(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entr
* the same result by handing them here because add_network()
* will keep the best (not the first) found route.
*/
- prefix = ipa_from_u32(rtl.id & rtl.data);
- pxlen = u32_masklen(rtl.data);
- add_network(oa, prefix, pxlen, act->dist + rtl.metric, act, i);
+ net_addr_ip4 net =
+ NET_ADDR_IP4(ip4_from_u32(rtl.id & rtl.data), u32_masklen(rtl.data));
+
+ add_network(oa, (net_addr *) &net, act->dist + rtl.metric, act, i);
break;
case LSART_NET:
@@ -526,14 +518,14 @@ spfa_process_net(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_ent
{
struct ospf_lsa_net *ln = act->lsa_body;
struct top_hash_entry *tmp;
- ip_addr prefix;
- int pxlen, i, cnt;
+ int i, cnt;
if (ospf_is_v2(p))
{
- prefix = ipa_from_u32(act->lsa.id & ln->optx);
- pxlen = u32_masklen(ln->optx);
- add_network(oa, prefix, pxlen, act->dist, act, -1);
+ net_addr_ip4 net =
+ NET_ADDR_IP4(ip4_from_u32(act->lsa.id & ln->optx), u32_masklen(ln->optx));
+
+ add_network(oa, (net_addr *) &net, act->dist, act, -1);
}
cnt = lsa_net_count(&act->lsa);
@@ -549,10 +541,6 @@ spfa_process_prefixes(struct ospf_proto *p, struct ospf_area *oa)
{
struct top_hash_entry *en, *src;
struct ospf_lsa_prefix *px;
- ip_addr pxa;
- int pxlen;
- u8 pxopts;
- u16 metric;
u32 *buf;
int i;
@@ -587,18 +575,22 @@ spfa_process_prefixes(struct ospf_proto *p, struct ospf_area *oa)
buf = px->rest;
for (i = 0; i < px->pxcount; i++)
- {
- buf = lsa_get_ipv6_prefix(buf, &pxa, &pxlen, &pxopts, &metric);
+ {
+ net_addr_ip6 net;
+ u8 pxopts;
+ u16 metric;
- if (pxopts & OPT_PX_NU)
- continue;
+ buf = ospf_get_ipv6_prefix(buf, (net_addr *) &net, &pxopts, &metric);
+
+ if (pxopts & OPT_PX_NU)
+ continue;
- /* Store the first global address to use it later as a vlink endpoint */
- if ((pxopts & OPT_PX_LA) && ipa_zero(src->lb))
- src->lb = pxa;
+ /* Store the first global address to use it later as a vlink endpoint */
+ if ((pxopts & OPT_PX_LA) && ipa_zero(src->lb))
+ src->lb = ipa_from_ip6(net.prefix);
- add_network(oa, pxa, pxlen, src->dist + metric, src, i);
- }
+ add_network(oa, (net_addr *) &net, src->dist + metric, src, i);
+ }
}
}
@@ -741,13 +733,12 @@ ospf_rt_sum(struct ospf_area *oa)
{
struct ospf_proto *p = oa->po;
struct top_hash_entry *en;
- ip_addr ip, abrip;
+ net_addr net;
u32 dst_rid, metric, options;
ort *abr;
- int pxlen = -1, type = -1;
+ int type;
u8 pxopts;
-
OSPF_TRACE(D_EVENTS, "Starting routing table calculation for inter-area (area %R)", oa->areaid);
WALK_SLIST(en, p->lsal)
@@ -770,18 +761,18 @@ ospf_rt_sum(struct ospf_area *oa)
if (en->lsa_type == LSA_T_SUM_NET)
{
- lsa_parse_sum_net(en, ospf_is_v2(p), &ip, &pxlen, &pxopts, &metric);
+ lsa_parse_sum_net(en, ospf_is_v2(p), &net, &pxopts, &metric);
- if (pxopts & OPT_PX_NU)
- continue;
-
- if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH)
+ if (!ospf_valid_prefix(&net))
{
log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)",
p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt);
continue;
}
+ if (pxopts & OPT_PX_NU)
+ continue;
+
options = 0;
type = ORT_NET;
}
@@ -802,8 +793,8 @@ ospf_rt_sum(struct ospf_area *oa)
continue;
/* 16.2. (4) */
- abrip = ipa_from_rid(en->lsa.rt);
- abr = (ort *) fib_find(&oa->rtr, &abrip, MAX_PREFIX_LENGTH);
+ net_addr_ip4 nrid = net_from_rid(en->lsa.rt);
+ abr = fib_find(&oa->rtr, (net_addr *) &nrid);
if (!abr || !abr->n.type)
continue;
@@ -827,7 +818,7 @@ ospf_rt_sum(struct ospf_area *oa)
};
if (type == ORT_NET)
- ri_install_net(p, ip, pxlen, &nf);
+ ri_install_net(p, &net, &nf);
else
ri_install_rt(oa, dst_rid, &nf);
}
@@ -841,11 +832,7 @@ ospf_rt_sum_tr(struct ospf_area *oa)
struct ospf_area *bb = p->backbone;
struct top_hash_entry *en;
ort *re, *abr;
- ip_addr ip, abrip;
- u32 dst_rid, metric, options;
- int pxlen;
- u8 pxopts;
-
+ u32 metric;
if (!bb)
return;
@@ -868,26 +855,31 @@ ospf_rt_sum_tr(struct ospf_area *oa)
if (en->lsa_type == LSA_T_SUM_NET)
{
- lsa_parse_sum_net(en, ospf_is_v2(p), &ip, &pxlen, &pxopts, &metric);
+ net_addr net;
+ u8 pxopts;
- if (pxopts & OPT_PX_NU)
- continue;
+ lsa_parse_sum_net(en, ospf_is_v2(p), &net, &pxopts, &metric);
- if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH)
+ if (!ospf_valid_prefix(&net))
{
log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)",
p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt);
continue;
}
- re = fib_find(&p->rtf, &ip, pxlen);
+ if (pxopts & OPT_PX_NU)
+ continue;
+
+ re = fib_find(&p->rtf, &net);
}
else // en->lsa_type == LSA_T_SUM_RT
{
+ u32 dst_rid, options;
+
lsa_parse_sum_rt(en, ospf_is_v2(p), &dst_rid, &metric, &options);
- ip = ipa_from_rid(dst_rid);
- re = fib_find(&bb->rtr, &ip, MAX_PREFIX_LENGTH);
+ net_addr_ip4 nrid = net_from_rid(dst_rid);
+ re = fib_find(&bb->rtr, (net_addr *) &nrid);
}
/* 16.3 (1b) */
@@ -905,8 +897,8 @@ ospf_rt_sum_tr(struct ospf_area *oa)
continue;
/* 16.3. (4) */
- abrip = ipa_from_rid(en->lsa.rt);
- abr = fib_find(&oa->rtr, &abrip, MAX_PREFIX_LENGTH);
+ net_addr_ip4 nrid = net_from_rid(en->lsa.rt);
+ abr = fib_find(&oa->rtr, (net_addr *) &nrid);
if (!abr || !abr->n.type)
continue;
@@ -997,7 +989,7 @@ decide_sum_lsa(struct ospf_area *oa, ort *nf, int dest)
return 1;
struct area_net *anet = (struct area_net *)
- fib_route(&nf->n.oa->net_fib, nf->fn.prefix, nf->fn.pxlen);
+ fib_route(&nf->n.oa->net_fib, nf->fn.addr);
/* Condensed area network found */
if (anet)
@@ -1016,13 +1008,13 @@ check_sum_net_lsa(struct ospf_proto *p, ort *nf)
if (nf->area_net)
{
/* It is a default route for stub areas, handled entirely in ospf_rt_abr() */
- if (nf->fn.pxlen == 0)
+ if (nf->fn.addr->pxlen == 0)
return;
/* Find that area network */
WALK_LIST(anet_oa, p->area_list)
{
- anet = (struct area_net *) fib_find(&anet_oa->net_fib, &nf->fn.prefix, nf->fn.pxlen);
+ anet = fib_find(&anet_oa->net_fib, nf->fn.addr);
if (anet)
break;
}
@@ -1041,14 +1033,16 @@ check_sum_net_lsa(struct ospf_proto *p, ort *nf)
static inline void
check_sum_rt_lsa(struct ospf_proto *p, ort *nf)
{
+ u32 rid = rid_from_net(nf->fn.addr);
+
struct ospf_area *oa;
WALK_LIST(oa, p->area_list)
if (decide_sum_lsa(oa, nf, ORT_ROUTER))
- ospf_originate_sum_rt_lsa(p, oa, nf, nf->n.metric1, nf->n.options);
+ ospf_originate_sum_rt_lsa(p, oa, rid, nf->n.metric1, nf->n.options);
}
static inline int
-decide_nssa_lsa(struct ospf_proto *p UNUSED4 UNUSED6, ort *nf, struct ospf_lsa_ext_local *rt)
+decide_nssa_lsa(struct ospf_proto *p, ort *nf, struct ospf_lsa_ext_local *rt)
{
struct ospf_area *oa = nf->n.oa;
struct top_hash_entry *en = nf->n.en;
@@ -1057,7 +1051,7 @@ decide_nssa_lsa(struct ospf_proto *p UNUSED4 UNUSED6, ort *nf, struct ospf_lsa_e
return 0;
/* Condensed area network found */
- if (fib_route(&oa->enet_fib, nf->fn.prefix, nf->fn.pxlen))
+ if (fib_route(&oa->enet_fib, nf->fn.addr))
return 0;
if (!en || (en->lsa_type != LSA_T_NSSA))
@@ -1092,7 +1086,7 @@ check_nssa_lsa(struct ospf_proto *p, ort *nf)
/* Find that area network */
WALK_LIST(oa, p->area_list)
{
- anet = (struct area_net *) fib_find(&oa->enet_fib, &nf->fn.prefix, nf->fn.pxlen);
+ anet = fib_find(&oa->enet_fib, nf->fn.addr);
if (anet)
break;
}
@@ -1162,24 +1156,20 @@ static void
ospf_rt_abr1(struct ospf_proto *p)
{
struct area_net *anet;
- ort *nf, *default_nf;
+ ort *default_nf;
+ net_addr default_net;
/* RFC 2328 G.3 - incomplete resolution of virtual next hops - routers */
- FIB_WALK(&p->backbone->rtr, nftmp)
+ FIB_WALK(&p->backbone->rtr, ort, nf)
{
- nf = (ort *) nftmp;
-
if (nf->n.type && unresolved_vlink(nf))
reset_ri(nf);
}
FIB_WALK_END;
- FIB_WALK(&p->rtf, nftmp)
+ FIB_WALK(&p->rtf, ort, nf)
{
- nf = (ort *) nftmp;
-
-
/* RFC 2328 G.3 - incomplete resolution of virtual next hops - networks */
if (nf->n.type && unresolved_vlink(nf))
reset_ri(nf);
@@ -1188,7 +1178,7 @@ ospf_rt_abr1(struct ospf_proto *p)
/* Compute condensed area networks */
if (nf->n.type == RTS_OSPF)
{
- anet = (struct area_net *) fib_route(&nf->n.oa->net_fib, nf->fn.prefix, nf->fn.pxlen);
+ anet = (struct area_net *) fib_route(&nf->n.oa->net_fib, nf->fn.addr);
if (anet)
{
if (!anet->active)
@@ -1196,7 +1186,7 @@ ospf_rt_abr1(struct ospf_proto *p)
anet->active = 1;
/* Get a RT entry and mark it to know that it is an area network */
- ort *nfi = (ort *) fib_get(&p->rtf, &anet->fn.prefix, anet->fn.pxlen);
+ ort *nfi = fib_get(&p->rtf, anet->fn.addr);
nfi->area_net = 1;
/* 16.2. (3) */
@@ -1211,8 +1201,13 @@ ospf_rt_abr1(struct ospf_proto *p)
}
FIB_WALK_END;
- ip_addr addr = IPA_NONE;
- default_nf = (ort *) fib_get(&p->rtf, &addr, 0);
+
+ if (ospf_is_v2(p))
+ net_fill_ip4(&default_net, IP4_NONE, 0);
+ else
+ net_fill_ip6(&default_net, IP6_NONE, 0);
+
+ default_nf = fib_get(&p->rtf, &default_net);
default_nf->area_net = 1;
struct ospf_area *oa;
@@ -1239,11 +1234,10 @@ ospf_rt_abr1(struct ospf_proto *p)
/* RFC 2328 16.4. (3) - precompute preferred ASBR entries */
if (oa_is_ext(oa))
{
- FIB_WALK(&oa->rtr, nftmp)
+ FIB_WALK(&oa->rtr, ort, nf)
{
- nf = (ort *) nftmp;
if (nf->n.options & ORTA_ASBR)
- ri_install_asbr(p, &nf->fn.prefix, &nf->n);
+ ri_install_asbr(p, rid_from_net(nf->fn.addr), &nf->n);
}
FIB_WALK_END;
}
@@ -1251,9 +1245,9 @@ ospf_rt_abr1(struct ospf_proto *p)
/* Originate or flush ASBR summary LSAs */
- FIB_WALK(&p->backbone->rtr, nftmp)
+ FIB_WALK(&p->backbone->rtr, ort, nf)
{
- check_sum_rt_lsa(p, (ort *) nftmp);
+ check_sum_rt_lsa(p, nf);
}
FIB_WALK_END;
@@ -1280,8 +1274,6 @@ ospf_rt_abr2(struct ospf_proto *p)
{
struct ospf_area *oa;
struct top_hash_entry *en;
- ort *nf, *nf2;
-
/* RFC 3103 3.1 - type-7 translator election */
struct ospf_area *bb = p->backbone;
@@ -1293,13 +1285,12 @@ ospf_rt_abr2(struct ospf_proto *p)
if (oa->ac->translator)
goto decided;
- FIB_WALK(&oa->rtr, nftmp)
+ FIB_WALK(&oa->rtr, ort, nf)
{
- nf = (ort *) nftmp;
if (!nf->n.type || !(nf->n.options & ORTA_ABR))
continue;
- nf2 = fib_find(&bb->rtr, &nf->fn.prefix, MAX_PREFIX_LENGTH);
+ ort *nf2 = fib_find(&bb->rtr, nf->fn.addr);
if (!nf2 || !nf2->n.type || !(nf2->n.options & ORTA_ABR))
continue;
@@ -1339,13 +1330,11 @@ ospf_rt_abr2(struct ospf_proto *p)
/* Compute condensed external networks */
- FIB_WALK(&p->rtf, nftmp)
+ FIB_WALK(&p->rtf, ort, nf)
{
- nf = (ort *) nftmp;
if (rt_is_nssa(nf) && (nf->n.options & ORTA_PROP))
{
- struct area_net *anet = (struct area_net *)
- fib_route(&nf->n.oa->enet_fib, nf->fn.prefix, nf->fn.pxlen);
+ struct area_net *anet = fib_route(&nf->n.oa->enet_fib, nf->fn.addr);
if (anet)
{
@@ -1354,7 +1343,7 @@ ospf_rt_abr2(struct ospf_proto *p)
anet->active = 1;
/* Get a RT entry and mark it to know that it is an area network */
- nf2 = (ort *) fib_get(&p->rtf, &anet->fn.prefix, anet->fn.pxlen);
+ ort *nf2 = fib_get(&p->rtf, anet->fn.addr);
nf2->area_net = 1;
}
@@ -1369,10 +1358,8 @@ ospf_rt_abr2(struct ospf_proto *p)
FIB_WALK_END;
- FIB_WALK(&p->rtf, nftmp)
+ FIB_WALK(&p->rtf, ort, nf)
{
- nf = (ort *) nftmp;
-
check_sum_net_lsa(p, nf);
check_nssa_lsa(p, nf);
}
@@ -1382,22 +1369,57 @@ ospf_rt_abr2(struct ospf_proto *p)
/* Like fib_route(), but ignores dummy rt entries */
static void *
-ospf_fib_route(struct fib *f, ip_addr a, int len)
+ospf_fib_route_ip4(struct fib *f, ip4_addr a, int len)
{
- ip_addr a0;
+ net_addr_ip4 net = NET_ADDR_IP4(a, len);
ort *nf;
- while (len >= 0)
+loop:
+ nf = fib_find(f, (net_addr *) &net);
+ if (nf && nf->n.type)
+ return nf;
+
+ if (net.pxlen > 0)
+ {
+ net.pxlen--;
+ ip4_clrbit(&net.prefix, net.pxlen);
+ goto loop;
+ }
+
+ return NULL;
+}
+
+static void *
+ospf_fib_route_ip6(struct fib *f, ip6_addr a, int len)
+{
+ net_addr_ip6 net = NET_ADDR_IP6(a, len);
+ ort *nf;
+
+loop:
+ nf = fib_find(f, (net_addr *) &net);
+ if (nf && nf->n.type)
+ return nf;
+
+ if (net.pxlen > 0)
{
- a0 = ipa_and(a, ipa_mkmask(len));
- nf = fib_find(f, &a0, len);
- if (nf && nf->n.type)
- return nf;
- len--;
+ net.pxlen--;
+ ip6_clrbit(&net.prefix, net.pxlen);
+ goto loop;
}
+
return NULL;
}
+static void *
+ospf_fib_route(struct fib *f, ip_addr a)
+{
+ if (f->addr_type == NET_IP4)
+ return ospf_fib_route_ip4(f, ipa_to_ip4(a), IP4_MAX_PREFIX_LENGTH);
+ else
+ return ospf_fib_route_ip6(f, ipa_to_ip6(a), IP6_MAX_PREFIX_LENGTH);
+}
+
+
/* RFC 2328 16.4. calculating external routes */
static void
ospf_ext_spf(struct ospf_proto *p)
@@ -1405,7 +1427,6 @@ ospf_ext_spf(struct ospf_proto *p)
struct top_hash_entry *en;
struct ospf_lsa_ext_local rt;
ort *nf1, *nf2;
- ip_addr rtid;
u32 br_metric;
struct ospf_area *atmp;
@@ -1431,19 +1452,18 @@ ospf_ext_spf(struct ospf_proto *p)
lsa_parse_ext(en, ospf_is_v2(p), &rt);
- if (rt.metric == LSINFINITY)
- continue;
-
- if (rt.pxopts & OPT_PX_NU)
- continue;
-
- if (rt.pxlen < 0 || rt.pxlen > MAX_PREFIX_LENGTH)
+ if (!ospf_valid_prefix(&rt.net))
{
log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)",
p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt);
continue;
}
+ if (rt.metric == LSINFINITY)
+ continue;
+
+ if (rt.pxopts & OPT_PX_NU)
+ continue;
/* 16.4. (3) */
/* If there are more areas, we already precomputed preferred ASBR
@@ -1457,8 +1477,8 @@ ospf_ext_spf(struct ospf_proto *p)
if (!atmp)
continue; /* Should not happen */
- rtid = ipa_from_rid(en->lsa.rt);
- nf1 = fib_find(&atmp->rtr, &rtid, MAX_PREFIX_LENGTH);
+ net_addr_ip4 nrid = net_from_rid(en->lsa.rt);
+ nf1 = fib_find(&atmp->rtr, (net_addr *) &nrid);
if (!nf1 || !nf1->n.type)
continue; /* No AS boundary router found */
@@ -1468,7 +1488,7 @@ ospf_ext_spf(struct ospf_proto *p)
/* 16.4. (3) NSSA - special rule for default routes */
/* ABR should use default only if P-bit is set and summaries are active */
- if ((en->lsa_type == LSA_T_NSSA) && ipa_zero(rt.ip) && (rt.pxlen == 0) &&
+ if ((en->lsa_type == LSA_T_NSSA) && (rt.net.pxlen == 0) &&
(p->areano > 1) && !(rt.propagate && atmp->ac->summary))
continue;
@@ -1480,7 +1500,7 @@ ospf_ext_spf(struct ospf_proto *p)
}
else
{
- nf2 = ospf_fib_route(&p->rtf, rt.fwaddr, MAX_PREFIX_LENGTH);
+ nf2 = ospf_fib_route(&p->rtf, rt.fwaddr);
if (!nf2)
continue;
@@ -1542,7 +1562,7 @@ ospf_ext_spf(struct ospf_proto *p)
nfa.oa = atmp; /* undefined in RFC 2328 */
nfa.en = en; /* store LSA for later (NSSA processing) */
- ri_install_ext(p, rt.ip, rt.pxlen, &nfa);
+ ri_install_ext(p, &rt.net, &nfa);
}
}
@@ -1552,13 +1572,10 @@ ospf_rt_reset(struct ospf_proto *p)
{
struct ospf_area *oa;
struct top_hash_entry *en;
- struct area_net *anet;
- ort *ri;
/* Reset old routing table */
- FIB_WALK(&p->rtf, nftmp)
+ FIB_WALK(&p->rtf, ort, ri)
{
- ri = (ort *) nftmp;
ri->area_net = 0;
ri->keep = 0;
reset_ri(ri);
@@ -1580,9 +1597,8 @@ ospf_rt_reset(struct ospf_proto *p)
WALK_LIST(oa, p->area_list)
{
/* Reset ASBR routing tables */
- FIB_WALK(&oa->rtr, nftmp)
+ FIB_WALK(&oa->rtr, ort, ri)
{
- ri = (ort *) nftmp;
reset_ri(ri);
}
FIB_WALK_END;
@@ -1590,17 +1606,15 @@ ospf_rt_reset(struct ospf_proto *p)
/* Reset condensed area networks */
if (p->areano > 1)
{
- FIB_WALK(&oa->net_fib, nftmp)
+ FIB_WALK(&oa->net_fib, struct area_net, anet)
{
- anet = (struct area_net *) nftmp;
anet->active = 0;
anet->metric = 0;
}
FIB_WALK_END;
- FIB_WALK(&oa->enet_fib, nftmp)
+ FIB_WALK(&oa->enet_fib, struct area_net, anet)
{
- anet = (struct area_net *) nftmp;
anet->active = 0;
anet->metric = 0;
}
@@ -1659,18 +1673,18 @@ ospf_rt_spf(struct ospf_proto *p)
static inline int
-inherit_nexthops(struct mpnh *pn)
+inherit_nexthops(struct nexthop *pn)
{
/* Proper nexthops (with defined GW) or dummy vlink nexthops (without iface) */
return pn && (ipa_nonzero(pn->gw) || !pn->iface);
}
-static struct mpnh *
+static struct nexthop *
calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
struct top_hash_entry *par, int pos)
{
struct ospf_proto *p = oa->po;
- struct mpnh *pn = par->nhs;
+ struct nexthop *pn = par->nhs;
struct ospf_iface *ifa;
u32 rid = en->lsa.rt;
@@ -1798,7 +1812,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
if (!link_back(oa, en, par))
return;
- struct mpnh *nhs = calc_next_hop(oa, en, par, pos);
+ struct nexthop *nhs = calc_next_hop(oa, en, par, pos);
if (!nhs)
{
log(L_WARN "%s: Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)",
@@ -1836,7 +1850,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
/* Merge old and new */
int new_reuse = (par->nhs != nhs);
- en->nhs = mpnh_merge(en->nhs, nhs, en->nhs_reuse, new_reuse, p->ecmp, p->nhpool);
+ en->nhs = nexthop_merge(en->nhs, nhs, en->nhs_reuse, new_reuse, p->ecmp, p->nhpool);
en->nhs_reuse = 1;
return;
}
@@ -1892,8 +1906,7 @@ ort_changed(ort *nf, rta *nr)
(nf->n.metric1 != nf->old_metric1) || (nf->n.metric2 != nf->old_metric2) ||
(nf->n.tag != nf->old_tag) || (nf->n.rid != nf->old_rid) ||
(nr->source != or->source) || (nr->dest != or->dest) ||
- (nr->iface != or->iface) || !ipa_equal(nr->gw, or->gw) ||
- !mpnh_same(nr->nexthops, or->nexthops);
+ !nexthop_same(&(nr->nh), &(or->nh));
}
static void
@@ -1902,25 +1915,22 @@ rt_sync(struct ospf_proto *p)
struct top_hash_entry *en;
struct fib_iterator fit;
struct fib *fib = &p->rtf;
- ort *nf;
struct ospf_area *oa;
/* This is used for forced reload of routes */
int reload = (p->calcrt == 2);
- OSPF_TRACE(D_EVENTS, "Starting routing table synchronisation");
+ OSPF_TRACE(D_EVENTS, "Starting routing table synchronization");
DBG("Now syncing my rt table with nest's\n");
FIB_ITERATE_INIT(&fit, fib);
again1:
- FIB_ITERATE_START(fib, &fit, nftmp)
+ FIB_ITERATE_START(fib, &fit, ort, nf)
{
- nf = (ort *) nftmp;
-
/* Sanity check of next-hop addresses, failure should not happen */
if (nf->n.type)
{
- struct mpnh *nh;
+ struct nexthop *nh;
for (nh = nf->n.nhs; nh; nh = nh->next)
if (ipa_nonzero(nh->gw))
{
@@ -1943,29 +1953,12 @@ again1:
.src = p->p.main_source,
.source = nf->n.type,
.scope = SCOPE_UNIVERSE,
- .cast = RTC_UNICAST
+ .dest = RTD_UNICAST,
+ .nh = *(nf->n.nhs),
};
- if (nf->n.nhs->next)
- {
- a0.dest = RTD_MULTIPATH;
- a0.nexthops = nf->n.nhs;
- }
- else if (ipa_nonzero(nf->n.nhs->gw))
- {
- a0.dest = RTD_ROUTER;
- a0.iface = nf->n.nhs->iface;
- a0.gw = nf->n.nhs->gw;
- }
- else
- {
- a0.dest = RTD_DEVICE;
- a0.iface = nf->n.nhs->iface;
- }
-
if (reload || ort_changed(nf, &a0))
{
- net *ne = net_get(p->p.table, nf->fn.prefix, nf->fn.pxlen);
rta *a = rta_lookup(&a0);
rte *e = rte_get_temp(a);
@@ -1976,12 +1969,10 @@ again1:
e->u.ospf.tag = nf->old_tag = nf->n.tag;
e->u.ospf.router_id = nf->old_rid = nf->n.rid;
e->pflags = 0;
- e->net = ne;
- e->pref = p->p.preference;
- DBG("Mod rte type %d - %I/%d via %I on iface %s, met %d\n",
- a0.source, nf->fn.prefix, nf->fn.pxlen, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);
- rte_update(&p->p, ne, e);
+ DBG("Mod rte type %d - %N via %I on iface %s, met %d\n",
+ a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);
+ rte_update(&p->p, nf->fn.addr, e);
}
}
else if (nf->old_rta)
@@ -1990,19 +1981,21 @@ again1:
rta_free(nf->old_rta);
nf->old_rta = NULL;
- net *ne = net_get(p->p.table, nf->fn.prefix, nf->fn.pxlen);
- rte_update(&p->p, ne, NULL);
+ rte_update(&p->p, nf->fn.addr, NULL);
}
/* Remove unused rt entry, some special entries are persistent */
if (!nf->n.type && !nf->external_rte && !nf->area_net && !nf->keep)
{
- FIB_ITERATE_PUT(&fit, nftmp);
- fib_delete(fib, nftmp);
+ if (nf->lsa_id)
+ idm_free(&p->idm, nf->lsa_id);
+
+ FIB_ITERATE_PUT(&fit);
+ fib_delete(fib, nf);
goto again1;
}
}
- FIB_ITERATE_END(nftmp);
+ FIB_ITERATE_END;
WALK_LIST(oa, p->area_list)
@@ -2010,18 +2003,16 @@ again1:
/* Cleanup ASBR hash tables */
FIB_ITERATE_INIT(&fit, &oa->rtr);
again2:
- FIB_ITERATE_START(&oa->rtr, &fit, nftmp)
+ FIB_ITERATE_START(&oa->rtr, &fit, ort, nf)
{
- nf = (ort *) nftmp;
-
if (!nf->n.type)
{
- FIB_ITERATE_PUT(&fit, nftmp);
- fib_delete(&oa->rtr, nftmp);
+ FIB_ITERATE_PUT(&fit);
+ fib_delete(&oa->rtr, nf);
goto again2;
}
}
- FIB_ITERATE_END(nftmp);
+ FIB_ITERATE_END;
}
/* Cleanup stale LSAs */
diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h
index 73b28375..589d2bc5 100644
--- a/proto/ospf/rt.h
+++ b/proto/ospf/rt.h
@@ -53,7 +53,7 @@ typedef struct orta
struct ospf_area *oa;
struct ospf_area *voa; /* Used when route is replaced in ospf_rt_sum_tr(),
NULL otherwise */
- struct mpnh *nhs; /* Next hops computed during SPF */
+ struct nexthop *nhs; /* Next hops computed during SPF */
struct top_hash_entry *en; /* LSA responsible for this orta */
}
orta;
@@ -78,13 +78,15 @@ typedef struct ort
* route was not in the last update, in that case other old_* values are not
* valid.
*/
- struct fib_node fn;
orta n;
u32 old_metric1, old_metric2, old_tag, old_rid;
rta *old_rta;
+ u32 lsa_id;
u8 external_rte;
u8 area_net;
u8 keep;
+
+ struct fib_node fn;
}
ort;
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index 341eff87..ce77f57a 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -283,8 +283,8 @@ ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa)
if (en->nf != lsa->nf)
{
- log(L_ERR "%s: LSA ID collision for %I/%d",
- p->p.name, lsa->nf->fn.prefix, lsa->nf->fn.pxlen);
+ log(L_ERR "%s: LSA ID collision for %N",
+ p->p.name, lsa->nf->fn.addr);
en = NULL;
goto drop;
@@ -514,14 +514,14 @@ ospf_update_lsadb(struct ospf_proto *p)
}
-static inline u32
-ort_to_lsaid(struct ospf_proto *p UNUSED4 UNUSED6, ort *nf)
+static u32
+ort_to_lsaid(struct ospf_proto *p, ort *nf)
{
/*
* In OSPFv2, We have to map IP prefixes to u32 in such manner that resulting
* u32 interpreted as IP address is a member of given prefix. Therefore, /32
- * prefix have to be mapped on itself. All received prefixes have to be
- * mapped on different u32s.
+ * prefix has to be mapped on itself. All received prefixes have to be mapped
+ * on different u32s.
*
* We have an assumption that if there is nontrivial (non-/32) network prefix,
* then there is not /32 prefix for the first and the last IP address of the
@@ -542,17 +542,21 @@ ort_to_lsaid(struct ospf_proto *p UNUSED4 UNUSED6, ort *nf)
* network appeared, we choose a different way.
*
* In OSPFv3, it is simpler. There is not a requirement for membership of the
- * result in the input network, so we just use a hash-based unique ID of a
- * routing table entry for a route that originated given LSA. For ext-LSA, it
- * is an imported route in the nest's routing table (p->table). For summary-LSA,
- * it is a 'source' route in the protocol internal routing table (p->rtf).
+ * result in the input network, so we just allocate a unique ID from ID map
+ * and store it in nf->lsa_id for further reference.
*/
if (ospf_is_v3(p))
- return nf->fn.uid;
+ {
+ if (!nf->lsa_id)
+ nf->lsa_id = idm_alloc(&p->idm);
- u32 id = ipa_to_u32(nf->fn.prefix);
- int pxlen = nf->fn.pxlen;
+ return nf->lsa_id;
+ }
+
+ net_addr_ip4 *net = (void *) nf->fn.addr;
+ u32 id = ip4_to_u32(net->prefix);
+ int pxlen = net->pxlen;
if ((pxlen == 0) || (pxlen == 32))
return id;
@@ -628,12 +632,12 @@ configured_stubnet(struct ospf_area *oa, struct ifa *a)
{
if (sn->summary)
{
- if (ipa_in_net(a->prefix, sn->px.addr, sn->px.len) && (a->pxlen >= sn->px.len))
+ if (net_in_netX(&a->prefix, &sn->prefix))
return 1;
}
else
{
- if (ipa_equal(a->prefix, sn->px.addr) && (a->pxlen == sn->px.len))
+ if (net_equal(&a->prefix, &sn->prefix))
return 1;
}
}
@@ -781,7 +785,8 @@ prepare_rt2_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
(ifa->type == OSPF_IT_PTMP))
add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(ifa->addr->ip), 0xffffffff, 0);
else
- add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(ifa->addr->prefix), u32_mkmask(ifa->addr->pxlen), ifa->cost);
+ add_rt2_lsa_link(p, LSART_STUB, ip4_to_u32(net4_prefix(&ifa->addr->prefix)),
+ u32_mkmask(net4_pxlen(&ifa->addr->prefix)), ifa->cost);
i++;
ifa->rt_pos_end = i;
@@ -790,7 +795,8 @@ prepare_rt2_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
struct ospf_stubnet_config *sn;
WALK_LIST(sn, oa->ac->stubnet_list)
if (!sn->hidden)
- add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(sn->px.addr), u32_mkmask(sn->px.len), sn->cost), i++;
+ add_rt2_lsa_link(p, LSART_STUB, ip4_to_u32(net4_prefix(&sn->prefix)),
+ u32_mkmask(net4_pxlen(&sn->prefix)), sn->cost), i++;
struct ospf_lsa_rt *rt = p->lsab;
/* Store number of links in lower half of options */
@@ -907,7 +913,7 @@ prepare_net2_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
ASSERT(p->lsab_used == 0);
net = lsab_alloc(p, sizeof(struct ospf_lsa_net) + 4 * nodes);
- net->optx = u32_mkmask(ifa->addr->pxlen);
+ net->optx = u32_mkmask(ifa->addr->prefix.pxlen);
net->routers[0] = p->router_id;
WALK_LIST(n, ifa->neigh_list)
@@ -999,9 +1005,10 @@ prepare_sum3_net_lsa_body(struct ospf_proto *p, ort *nf, u32 metric)
{
struct ospf_lsa_sum3_net *sum;
- sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_net) + IPV6_PREFIX_SPACE(nf->fn.pxlen));
+ sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_net) +
+ IPV6_PREFIX_SPACE(nf->fn.addr->pxlen));
sum->metric = metric;
- put_ipv6_prefix(sum->prefix, nf->fn.prefix, nf->fn.pxlen, 0, 0);
+ ospf_put_ipv6_prefix(sum->prefix, nf->fn.addr, 0, 0);
}
static inline void
@@ -1028,7 +1035,7 @@ ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf,
};
if (ospf_is_v2(p))
- prepare_sum2_lsa_body(p, nf->fn.pxlen, metric);
+ prepare_sum2_lsa_body(p, nf->fn.addr->pxlen, metric);
else
prepare_sum3_net_lsa_body(p, nf, metric);
@@ -1036,20 +1043,20 @@ ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf,
}
void
-ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric, u32 options)
+ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, u32 drid, int metric, u32 options)
{
struct ospf_new_lsa lsa = {
.type = LSA_T_SUM_RT,
.mode = LSA_M_RTCALC,
.dom = oa->areaid,
- .id = ipa_to_rid(nf->fn.prefix), /* Router ID of ASBR, irrelevant for OSPFv3 */
+ .id = drid, /* Router ID of ASBR, irrelevant for OSPFv3 */
.opts = oa->options
};
if (ospf_is_v2(p))
prepare_sum2_lsa_body(p, 0, metric);
else
- prepare_sum3_rt_lsa_body(p, lsa.id, metric, options & LSA_OPTIONS_MASK);
+ prepare_sum3_rt_lsa_body(p, drid, metric, options & LSA_OPTIONS_MASK);
ospf_originate_lsa(p, &lsa);
}
@@ -1082,7 +1089,7 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf,
{
struct ospf_lsa_ext3 *ext;
int bsize = sizeof(struct ospf_lsa_ext3)
- + IPV6_PREFIX_SPACE(nf->fn.pxlen)
+ + IPV6_PREFIX_SPACE(nf->fn.addr->pxlen)
+ (ipa_nonzero(fwaddr) ? 16 : 0)
+ (tag ? 4 : 0);
@@ -1090,7 +1097,7 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf,
ext->metric = metric & LSA_METRIC_MASK;
u32 *buf = ext->rest;
- buf = put_ipv6_prefix(buf, nf->fn.prefix, nf->fn.pxlen, pbit ? OPT_PX_P : 0, 0);
+ buf = ospf_put_ipv6_prefix(buf, nf->fn.addr, pbit ? OPT_PX_P : 0, 0);
if (ebit)
ext->metric |= LSA_EXT3_EBIT;
@@ -1098,7 +1105,7 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf,
if (ipa_nonzero(fwaddr))
{
ext->metric |= LSA_EXT3_FBIT;
- buf = put_ipv6_addr(buf, fwaddr);
+ buf = ospf_put_ipv6_addr(buf, fwaddr);
}
if (tag)
@@ -1140,7 +1147,7 @@ ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 m
};
if (ospf_is_v2(p))
- prepare_ext2_lsa_body(p, nf->fn.pxlen, metric, ebit, fwaddr, tag);
+ prepare_ext2_lsa_body(p, nf->fn.addr->pxlen, metric, ebit, fwaddr, tag);
else
prepare_ext3_lsa_body(p, nf, metric, ebit, fwaddr, tag, oa && pbit);
@@ -1177,7 +1184,7 @@ use_gw_for_fwaddr(struct ospf_proto *p, ip_addr gw, struct iface *iface)
WALK_LIST(ifa, p->iface_list)
if ((ifa->iface == iface) &&
- (!ospf_is_v2(p) || ipa_in_net(gw, ifa->addr->prefix, ifa->addr->pxlen)))
+ (!ospf_is_v2(p) || ipa_in_netX(gw, &ifa->addr->prefix)))
return 1;
return 0;
@@ -1215,7 +1222,8 @@ find_surrogate_fwaddr(struct ospf_proto *p, struct ospf_area *oa)
{
WALK_LIST(a, ifa->iface->addrs)
{
- if ((a->flags & IA_SECONDARY) ||
+ if ((a->prefix.type != NET_IP6) ||
+ (a->flags & IA_SECONDARY) ||
(a->flags & IA_PEER) ||
(a->scope <= SCOPE_LINK))
continue;
@@ -1234,7 +1242,7 @@ find_surrogate_fwaddr(struct ospf_proto *p, struct ospf_area *oa)
}
void
-ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *ea)
+ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *ea)
{
struct ospf_proto *p = (struct ospf_proto *) P;
struct ospf_area *oa = NULL; /* non-NULL for NSSA-LSA */
@@ -1253,7 +1261,7 @@ ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U
if (!new)
{
- nf = (ort *) fib_find(&p->rtf, &n->n.prefix, n->n.pxlen);
+ nf = fib_find(&p->rtf, n->n.addr);
if (!nf || !nf->external_rte)
return;
@@ -1280,8 +1288,8 @@ ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U
ip_addr fwd = IPA_NONE;
- if ((a->dest == RTD_ROUTER) && use_gw_for_fwaddr(p, a->gw, a->iface))
- fwd = a->gw;
+ if ((a->dest == RTD_UNICAST) && use_gw_for_fwaddr(p, a->nh.gw, a->nh.iface))
+ fwd = a->nh.gw;
/* NSSA-LSA with P-bit set must have non-zero forwarding address */
if (oa && ipa_zero(fwd))
@@ -1290,13 +1298,13 @@ ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U
if (ipa_zero(fwd))
{
- log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %I/%d",
- p->p.name, n->n.prefix, n->n.pxlen);
+ log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %N",
+ p->p.name, n->n.addr);
return;
}
}
- nf = (ort *) fib_get(&p->rtf, &n->n.prefix, n->n.pxlen);
+ nf = fib_get(&p->rtf, n->n.addr);
ospf_originate_ext_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1);
nf->external_rte = 1;
}
@@ -1308,11 +1316,11 @@ ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U
*/
static inline void
-lsab_put_prefix(struct ospf_proto *p, ip_addr prefix, u32 pxlen, u32 cost)
+lsab_put_prefix(struct ospf_proto *p, net_addr *net, u32 cost)
{
- void *buf = lsab_alloc(p, IPV6_PREFIX_SPACE(pxlen));
- u8 flags = (pxlen < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA;
- put_ipv6_prefix(buf, prefix, pxlen, flags, cost);
+ void *buf = lsab_alloc(p, IPV6_PREFIX_SPACE(net6_pxlen(net)));
+ u8 flags = (net6_pxlen(net) < IP6_MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA;
+ ospf_put_ipv6_prefix(buf, net, flags, cost);
}
static void
@@ -1330,11 +1338,12 @@ prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
struct ifa *a;
WALK_LIST(a, ifa->iface->addrs)
{
- if ((a->flags & IA_SECONDARY) ||
- (a->scope < SCOPE_SITE))
+ if ((a->prefix.type != NET_IP6) ||
+ (a->flags & IA_SECONDARY) ||
+ (a->scope <= SCOPE_LINK))
continue;
- lsab_put_prefix(p, a->prefix, a->pxlen, 0);
+ lsab_put_prefix(p, &a->prefix, 0);
i++;
}
@@ -1401,12 +1410,13 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
struct ifa *a;
WALK_LIST(a, ifa->iface->addrs)
{
- if ((a->flags & IA_SECONDARY) ||
+ if ((a->prefix.type != NET_IP6) ||
+ (a->flags & IA_SECONDARY) ||
(a->flags & IA_PEER) ||
(a->scope <= SCOPE_LINK))
continue;
- if (((a->pxlen < MAX_PREFIX_LENGTH) && net_lsa) ||
+ if (((a->prefix.pxlen < IP6_MAX_PREFIX_LENGTH) && net_lsa) ||
configured_stubnet(oa, a))
continue;
@@ -1414,11 +1424,12 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
(ifa->state == OSPF_IS_LOOP) ||
(ifa->type == OSPF_IT_PTMP))
{
- lsab_put_prefix(p, a->ip, MAX_PREFIX_LENGTH, 0);
+ net_addr_ip6 net = NET_ADDR_IP6(a->ip, IP6_MAX_PREFIX_LENGTH);
+ lsab_put_prefix(p, (net_addr *) &net, 0);
host_addr = 1;
}
else
- lsab_put_prefix(p, a->prefix, a->pxlen, ifa->cost);
+ lsab_put_prefix(p, &a->prefix, ifa->cost);
i++;
}
@@ -1429,8 +1440,8 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
WALK_LIST(sn, oa->ac->stubnet_list)
if (!sn->hidden)
{
- lsab_put_prefix(p, sn->px.addr, sn->px.len, sn->cost);
- if (sn->px.len == MAX_PREFIX_LENGTH)
+ lsab_put_prefix(p, &sn->prefix, sn->cost);
+ if (sn->prefix.pxlen == IP6_MAX_PREFIX_LENGTH)
host_addr = 1;
i++;
}
@@ -1447,11 +1458,14 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
struct ifa *a;
WALK_LIST(a, ifa->iface->addrs)
{
- if ((a->flags & IA_SECONDARY) || (a->scope <= SCOPE_LINK))
+ if ((a->prefix.type != NET_IP6) ||
+ (a->flags & IA_SECONDARY) ||
+ (a->scope <= SCOPE_LINK))
continue;
/* Found some IP */
- lsab_put_prefix(p, a->ip, MAX_PREFIX_LENGTH, 0);
+ net_addr_ip6 net = NET_ADDR_IP6(a->ip, IP6_MAX_PREFIX_LENGTH);
+ lsab_put_prefix(p, (net_addr *) &net, 0);
i++;
goto done;
}
@@ -1748,7 +1762,7 @@ ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type)
* and request lists of OSPF neighbors.
*/
struct top_graph *
-ospf_top_new(struct ospf_proto *p UNUSED4 UNUSED6, pool *pool)
+ospf_top_new(struct ospf_proto *p, pool *pool)
{
struct top_graph *f;
diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h
index 5652ced0..d1682c54 100644
--- a/proto/ospf/topology.h
+++ b/proto/ospf/topology.h
@@ -28,7 +28,7 @@ struct top_hash_entry
u16 next_lsa_opts; /* For postponed LSA origination */
bird_clock_t inst_time; /* Time of installation into DB */
struct ort *nf; /* Reference fibnode for sum and ext LSAs, NULL for otherwise */
- struct mpnh *nhs; /* Computed nexthops - valid only in ospf_rt_spf() */
+ struct nexthop *nhs; /* Computed nexthops - valid only in ospf_rt_spf() */
ip_addr lb; /* In OSPFv2, link back address. In OSPFv3, any global address in the area useful for vlinks */
u32 lb_id; /* Interface ID of link back iface (for bcast or NBMA networks) */
u32 dist; /* Distance from the root */
@@ -185,10 +185,10 @@ static inline void ospf_flush2_lsa(struct ospf_proto *p, struct top_hash_entry *
{ if (*en) { ospf_flush_lsa(p, *en); *en = NULL; } }
void ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric);
-void ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric, u32 options);
+void ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, u32 drid, int metric, u32 options);
void ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 mode, u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit);
-void ospf_rt_notify(struct proto *P, rtable *tbl, net *n, rte *new, rte *old, ea_list *attrs);
+void ospf_rt_notify(struct proto *P, struct channel *ch, net *n, rte *new, rte *old, ea_list *attrs);
void ospf_update_topology(struct ospf_proto *p);
struct top_hash_entry *ospf_hash_find(struct top_graph *, u32 domain, u32 lsa, u32 rtr, u32 type);
diff --git a/proto/pipe/Makefile b/proto/pipe/Makefile
index 77de5b88..5093da98 100644
--- a/proto/pipe/Makefile
+++ b/proto/pipe/Makefile
@@ -1,6 +1,6 @@
-source=pipe.c
-root-rel=../../
-dir-name=proto/pipe
-
-include ../../Rules
+src := pipe.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
+tests_objs := $(tests_objs) $(src-o-files) \ No newline at end of file
diff --git a/proto/pipe/config.Y b/proto/pipe/config.Y
index 8daf2e7c..f51ee575 100644
--- a/proto/pipe/config.Y
+++ b/proto/pipe/config.Y
@@ -16,28 +16,25 @@ CF_DEFINES
CF_DECLS
-CF_KEYWORDS(PIPE, PEER, TABLE, MODE, OPAQUE, TRANSPARENT)
+CF_KEYWORDS(PIPE, PEER, TABLE)
CF_GRAMMAR
-CF_ADDTO(proto, pipe_proto '}')
+CF_ADDTO(proto, pipe_proto '}' { this_channel = NULL; } )
-pipe_proto_start: proto_start PIPE {
- this_proto = proto_config_new(&proto_pipe, $1);
- PIPE_CFG->mode = PIPE_TRANSPARENT;
- }
- ;
+pipe_proto_start: proto_start PIPE
+{
+ this_proto = proto_config_new(&proto_pipe, $1);
+ this_channel = channel_config_new(NULL, 0, this_proto);
+ this_channel->in_filter = FILTER_ACCEPT;
+ this_channel->out_filter = FILTER_ACCEPT;
+};
pipe_proto:
pipe_proto_start proto_name '{'
| pipe_proto proto_item ';'
- | pipe_proto PEER TABLE SYM ';' {
- if ($4->class != SYM_TABLE)
- cf_error("Routing table name expected");
- PIPE_CFG->peer = $4->def;
- }
- | pipe_proto MODE OPAQUE ';' { PIPE_CFG->mode = PIPE_OPAQUE; }
- | pipe_proto MODE TRANSPARENT ';' { PIPE_CFG->mode = PIPE_TRANSPARENT; }
+ | pipe_proto channel_item ';'
+ | pipe_proto PEER TABLE rtable ';' { PIPE_CFG->peer = $4; }
;
CF_CODE
diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
index 6ef80322..310f3c01 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -44,54 +44,42 @@
#include "pipe.h"
static void
-pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs)
+pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *old, ea_list *attrs)
{
- struct pipe_proto *p = (struct pipe_proto *) P;
- struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook;
- rtable *dst_table = ah->table;
+ struct pipe_proto *p = (void *) P;
+ struct channel *dst = (src_ch == p->pri) ? p->sec : p->pri;
struct rte_src *src;
- net *nn;
rte *e;
- rta a;
+ rta *a;
if (!new && !old)
return;
- if (dst_table->pipe_busy)
+ if (dst->table->pipe_busy)
{
- log(L_ERR "Pipe loop detected when sending %I/%d to table %s",
- n->n.prefix, n->n.pxlen, dst_table->name);
+ log(L_ERR "Pipe loop detected when sending %N to table %s",
+ n->n.addr, dst->table->name);
return;
}
- nn = net_get(dst_table, n->n.prefix, n->n.pxlen);
if (new)
{
- memcpy(&a, new->attrs, sizeof(rta));
-
- if (p->mode == PIPE_OPAQUE)
- {
- a.src = P->main_source;
- a.source = RTS_PIPE;
- }
-
- a.aflags = 0;
- a.eattrs = attrs;
- a.hostentry = NULL;
- e = rte_get_temp(&a);
- e->net = nn;
+ a = alloca(rta_size(new->attrs));
+ memcpy(a, new->attrs, rta_size(new->attrs));
+
+ a->aflags = 0;
+ a->eattrs = attrs;
+ a->hostentry = NULL;
+ e = rte_get_temp(a);
e->pflags = 0;
- if (p->mode == PIPE_TRANSPARENT)
- {
- /* Copy protocol specific embedded attributes. */
- memcpy(&(e->u), &(new->u), sizeof(e->u));
- e->pref = new->pref;
- e->pflags = new->pflags;
- }
+ /* Copy protocol specific embedded attributes. */
+ memcpy(&(e->u), &(new->u), sizeof(e->u));
+ e->pref = new->pref;
+ e->pflags = new->pflags;
- src = a.src;
+ src = a->src;
}
else
{
@@ -99,9 +87,9 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
src = old->attrs->src;
}
- src_table->pipe_busy = 1;
- rte_update2(ah, nn, e, src);
- src_table->pipe_busy = 0;
+ src_ch->table->pipe_busy = 1;
+ rte_update2(dst, n->n.addr, e, src);
+ src_ch->table->pipe_busy = 0;
}
static int
@@ -111,171 +99,117 @@ pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpo
if (pp == P)
return -1; /* Avoid local loops automatically */
+
return 0;
}
-static int
-pipe_reload_routes(struct proto *P)
+static void
+pipe_reload_routes(struct channel *C)
{
- struct pipe_proto *p = (struct pipe_proto *) P;
-
- /*
- * Because the pipe protocol feeds routes from both routing tables
- * together, both directions are reloaded during refeed and 'reload
- * out' command works like 'reload' command. For symmetry, we also
- * request refeed when 'reload in' command is used.
- */
- proto_request_feeding(P);
+ struct pipe_proto *p = (void *) C->proto;
- proto_reset_limit(P->main_ahook->in_limit);
- proto_reset_limit(p->peer_ahook->in_limit);
-
- return 1;
+ /* Route reload on one channel is just refeed on the other */
+ channel_request_feeding((C == p->pri) ? p->sec : p->pri);
}
-static struct proto *
-pipe_init(struct proto_config *C)
-{
- struct pipe_config *c = (struct pipe_config *) C;
- struct proto *P = proto_new(C, sizeof(struct pipe_proto));
- struct pipe_proto *p = (struct pipe_proto *) P;
- p->mode = c->mode;
- p->peer_table = c->peer->table;
- P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY;
- P->rt_notify = pipe_rt_notify;
- P->import_control = pipe_import_control;
- P->reload_routes = pipe_reload_routes;
-
- return P;
-}
-
-static int
-pipe_start(struct proto *P)
+static void
+pipe_postconfig(struct proto_config *CF)
{
- struct pipe_config *cf = (struct pipe_config *) P->cf;
- struct pipe_proto *p = (struct pipe_proto *) P;
+ struct pipe_config *cf = (void *) CF;
+ struct channel_config *cc = proto_cf_main_channel(CF);
- /* Lock both tables, unlock is handled in pipe_cleanup() */
- rt_lock_table(P->table);
- rt_lock_table(p->peer_table);
+ if (!cc->table)
+ cf_error("Primary routing table not specified");
- /* Going directly to PS_UP - prepare for feeding,
- connect the protocol to both routing tables */
+ if (!cf->peer)
+ cf_error("Secondary routing table not specified");
- P->main_ahook = proto_add_announce_hook(P, P->table, &P->stats);
- P->main_ahook->out_filter = cf->c.out_filter;
- P->main_ahook->in_limit = cf->c.in_limit;
- proto_reset_limit(P->main_ahook->in_limit);
+ if (cc->table == cf->peer)
+ cf_error("Primary table and peer table must be different");
- p->peer_ahook = proto_add_announce_hook(P, p->peer_table, &p->peer_stats);
- p->peer_ahook->out_filter = cf->c.in_filter;
- p->peer_ahook->in_limit = cf->c.out_limit;
- proto_reset_limit(p->peer_ahook->in_limit);
+ if (cc->table->addr_type != cf->peer->addr_type)
+ cf_error("Primary table and peer table must have the same type");
- if (p->mode == PIPE_OPAQUE)
- {
- P->main_source = rt_get_source(P, 0);
- rt_lock_source(P->main_source);
- }
+ if (cc->rx_limit.action)
+ cf_error("Pipe protocol does not support receive limits");
- return PS_UP;
+ if (cc->in_keep_filtered)
+ cf_error("Pipe protocol prohibits keeping filtered routes");
}
-static void
-pipe_cleanup(struct proto *P)
+static int
+pipe_configure_channels(struct pipe_proto *p, struct pipe_config *cf)
{
- struct pipe_proto *p = (struct pipe_proto *) P;
-
- bzero(&P->stats, sizeof(struct proto_stats));
- bzero(&p->peer_stats, sizeof(struct proto_stats));
-
- P->main_ahook = NULL;
- p->peer_ahook = NULL;
-
- if (p->mode == PIPE_OPAQUE)
- rt_unlock_source(P->main_source);
- P->main_source = NULL;
-
- rt_unlock_table(P->table);
- rt_unlock_table(p->peer_table);
+ struct channel_config *cc = proto_cf_main_channel(&cf->c);
+
+ struct channel_config pri_cf = {
+ .name = "pri",
+ .channel = cc->channel,
+ .table = cc->table,
+ .out_filter = cc->out_filter,
+ .in_limit = cc->in_limit,
+ .ra_mode = RA_ANY
+ };
+
+ struct channel_config sec_cf = {
+ .name = "sec",
+ .channel = cc->channel,
+ .table = cf->peer,
+ .out_filter = cc->in_filter,
+ .in_limit = cc->out_limit,
+ .ra_mode = RA_ANY
+ };
+
+ return
+ proto_configure_channel(&p->p, &p->pri, &pri_cf) &&
+ proto_configure_channel(&p->p, &p->sec, &sec_cf);
}
-static void
-pipe_postconfig(struct proto_config *C)
+static struct proto *
+pipe_init(struct proto_config *CF)
{
- struct pipe_config *c = (struct pipe_config *) C;
+ struct proto *P = proto_new(CF);
+ struct pipe_proto *p = (void *) P;
+ struct pipe_config *cf = (void *) CF;
- if (!c->peer)
- cf_error("Name of peer routing table not specified");
- if (c->peer == C->table)
- cf_error("Primary table and peer table must be different");
+ P->rt_notify = pipe_rt_notify;
+ P->import_control = pipe_import_control;
+ P->reload_routes = pipe_reload_routes;
- 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");
-}
+ pipe_configure_channels(p, cf);
-extern int proto_reconfig_type;
+ return P;
+}
static int
-pipe_reconfigure(struct proto *P, struct proto_config *new)
+pipe_reconfigure(struct proto *P, struct proto_config *CF)
{
- struct pipe_proto *p = (struct pipe_proto *)P;
- struct proto_config *old = P->cf;
- struct pipe_config *oc = (struct pipe_config *) old;
- struct pipe_config *nc = (struct pipe_config *) new;
-
- if ((oc->peer->table != nc->peer->table) || (oc->mode != nc->mode))
- return 0;
-
- /* Update output filters in ahooks */
- if (P->main_ahook)
- {
- P->main_ahook->out_filter = new->out_filter;
- P->main_ahook->in_limit = new->in_limit;
- proto_verify_limits(P->main_ahook);
- }
-
- if (p->peer_ahook)
- {
- p->peer_ahook->out_filter = new->in_filter;
- p->peer_ahook->in_limit = new->out_limit;
- proto_verify_limits(p->peer_ahook);
- }
-
- if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
- return 1;
-
- if ((new->preference != old->preference)
- || ! filter_same(new->in_filter, old->in_filter)
- || ! filter_same(new->out_filter, old->out_filter))
- proto_request_feeding(P);
+ struct pipe_proto *p = (void *) P;
+ struct pipe_config *cf = (void *) CF;
- return 1;
+ return pipe_configure_channels(p, cf);
}
static void
-pipe_copy_config(struct proto_config *dest, struct proto_config *src)
+pipe_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUSED)
{
/* Just a shallow copy, not many items here */
- proto_copy_rest(dest, src, sizeof(struct pipe_config));
}
static void
pipe_get_status(struct proto *P, byte *buf)
{
- struct pipe_proto *p = (struct pipe_proto *) P;
+ struct pipe_proto *p = (void *) P;
- bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer_table->name);
+ bsprintf(buf, "%s <=> %s", p->pri->table->name, p->sec->table->name);
}
static void
pipe_show_stats(struct pipe_proto *p)
{
- struct proto_stats *s1 = &p->p.stats;
- struct proto_stats *s2 = &p->peer_stats;
+ struct proto_stats *s1 = &p->pri->stats;
+ struct proto_stats *s2 = &p->sec->stats;
/*
* Pipe stats (as anything related to pipes) are a bit tricky. There
@@ -318,17 +252,16 @@ pipe_show_stats(struct pipe_proto *p)
static void
pipe_show_proto_info(struct proto *P)
{
- struct pipe_proto *p = (struct pipe_proto *) P;
- struct pipe_config *cf = (struct pipe_config *) P->cf;
+ struct pipe_proto *p = (void *) P;
- // cli_msg(-1006, " Table: %s", P->table->name);
- // cli_msg(-1006, " Peer table: %s", p->peer_table->name);
- cli_msg(-1006, " Preference: %d", P->preference);
- cli_msg(-1006, " Input filter: %s", filter_name(cf->c.in_filter));
- cli_msg(-1006, " Output filter: %s", filter_name(cf->c.out_filter));
+ cli_msg(-1006, " Channel %s", "main");
+ cli_msg(-1006, " Table: %s", p->pri->table->name);
+ cli_msg(-1006, " Peer table: %s", p->sec->table->name);
+ cli_msg(-1006, " Import filter: %s", filter_name(p->sec->out_filter));
+ cli_msg(-1006, " Export filter: %s", filter_name(p->pri->out_filter));
- proto_show_limit(cf->c.in_limit, "Import limit:");
- proto_show_limit(cf->c.out_limit, "Export limit:");
+ channel_show_limit(&p->pri->in_limit, "Import limit:");
+ channel_show_limit(&p->sec->in_limit, "Export limit:");
if (P->proto_state != PS_DOWN)
pipe_show_stats(p);
@@ -338,13 +271,10 @@ pipe_show_proto_info(struct proto *P)
struct protocol proto_pipe = {
.name = "Pipe",
.template = "pipe%d",
- .multitable = 1,
- .preference = DEF_PREF_PIPE,
+ .proto_size = sizeof(struct pipe_proto),
.config_size = sizeof(struct pipe_config),
.postconfig = pipe_postconfig,
.init = pipe_init,
- .start = pipe_start,
- .cleanup = pipe_cleanup,
.reconfigure = pipe_reconfigure,
.copy_config = pipe_copy_config,
.get_status = pipe_get_status,
diff --git a/proto/pipe/pipe.h b/proto/pipe/pipe.h
index 50b31698..038c6666 100644
--- a/proto/pipe/pipe.h
+++ b/proto/pipe/pipe.h
@@ -9,27 +9,15 @@
#ifndef _BIRD_PIPE_H_
#define _BIRD_PIPE_H_
-#define PIPE_OPAQUE 0
-#define PIPE_TRANSPARENT 1
-
struct pipe_config {
struct proto_config c;
struct rtable_config *peer; /* Table we're connected to */
- int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */
};
struct pipe_proto {
struct proto p;
- struct rtable *peer_table;
- struct announce_hook *peer_ahook; /* Announce hook for direction peer->primary */
- struct proto_stats peer_stats; /* Statistics for the direction peer->primary */
- int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */
+ struct channel *pri;
+ struct channel *sec;
};
-
-extern struct protocol proto_pipe;
-
-static inline int proto_is_pipe(struct proto *p)
-{ return p->proto == &proto_pipe; }
-
#endif
diff --git a/proto/radv/Makefile b/proto/radv/Makefile
index efc4d4af..05317eff 100644
--- a/proto/radv/Makefile
+++ b/proto/radv/Makefile
@@ -1,5 +1,6 @@
-source=radv.c packets.c
-root-rel=../../
-dir-name=proto/radv
+src := packets.c radv.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
-include ../../Rules
+tests_objs := $(tests_objs) $(src-o-files) \ No newline at end of file
diff --git a/proto/radv/config.Y b/proto/radv/config.Y
index da300667..b5f4b5f2 100644
--- a/proto/radv/config.Y
+++ b/proto/radv/config.Y
@@ -41,6 +41,7 @@ CF_ADDTO(proto, radv_proto)
radv_proto_start: proto_start RADV
{
this_proto = proto_config_new(&proto_radv, $1);
+
init_list(&RADV_CFG->patt_list);
init_list(&RADV_CFG->pref_list);
init_list(&RADV_CFG->rdnss_list);
@@ -49,15 +50,12 @@ radv_proto_start: proto_start RADV
radv_proto_item:
proto_item
+ | proto_channel
| INTERFACE radv_iface
| 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;
- }
+ | TRIGGER net_ip6 { RADV_CFG->trigger = $2; }
;
radv_proto_opts:
@@ -93,14 +91,14 @@ radv_iface_item:
| MIN DELAY expr { RADV_IFACE->min_delay = $3; if ($3 <= 0) cf_error("Min delay must be positive"); }
| MANAGED bool { RADV_IFACE->managed = $2; }
| OTHER CONFIG bool { RADV_IFACE->other_config = $3; }
- | LINK MTU expr { RADV_IFACE->link_mtu = $3; if ($3 < 0) cf_error("Link MTU must be 0 or positive"); }
- | 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"); }
+ | LINK MTU expr { RADV_IFACE->link_mtu = $3; }
+ | REACHABLE TIME expr { RADV_IFACE->reachable_time = $3; if ($3 > 3600000) cf_error("Reachable time must be in range 0-3600000"); }
+ | RETRANS TIMER expr { RADV_IFACE->retrans_timer = $3; }
+ | CURRENT HOP LIMIT expr { RADV_IFACE->current_hop_limit = $4; if ($4 > 255) cf_error("Current hop limit must be in range 0-255"); }
| 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;
+ if ($3 > 9000) cf_error("Default lifetime must be in range 0-9000");
+ if ($4 != (uint) -1) RADV_IFACE->default_lifetime_sensitive = $4;
}
| DEFAULT PREFERENCE radv_preference { RADV_IFACE->default_preference = $3; }
| PREFIX radv_prefix { add_tail(&RADV_IFACE->pref_list, NODE this_radv_prefix); }
@@ -127,7 +125,7 @@ radv_iface_finish:
if ((ic->min_ra_int > 3) &&
(ic->min_ra_int > (ic->max_ra_int * 3 / 4)))
- cf_error("Min RA interval must be at most 3/4 * Max RA interval %d %d", ic->min_ra_int, ic->max_ra_int);
+ cf_error("Min RA interval must be at most 3/4 * Max RA interval");
if ((ic->default_lifetime > 0) && (ic->default_lifetime < ic->max_ra_int))
cf_error("Default lifetime must be either 0 or at least Max RA interval");
@@ -148,11 +146,10 @@ radv_iface:
radv_iface_start iface_patt_list_nopx radv_iface_opt_list radv_iface_finish;
-radv_prefix_start: prefix
+radv_prefix_start: net_ip6
{
this_radv_prefix = cfg_allocz(sizeof(struct radv_prefix_config));
- RADV_PREFIX->prefix = $1.addr;
- RADV_PREFIX->pxlen = $1.len;
+ RADV_PREFIX->prefix = *(net_addr_ip6 *) &($1);
RADV_PREFIX->onlink = 1;
RADV_PREFIX->autonomous = 1;
@@ -166,13 +163,11 @@ radv_prefix_item:
| AUTONOMOUS bool { RADV_PREFIX->autonomous = $2; }
| 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;
+ if ($4 != (uint) -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;
+ if ($4 != (uint) -1) RADV_PREFIX->preferred_lifetime_sensitive = $4;
}
;
diff --git a/proto/radv/packets.c b/proto/radv/packets.c
index 4bf960c3..f4352155 100644
--- a/proto/radv/packets.c
+++ b/proto/radv/packets.c
@@ -38,7 +38,7 @@ struct radv_opt_prefix
u32 valid_lifetime;
u32 preferred_lifetime;
u32 reserved;
- ip_addr prefix;
+ ip6_addr prefix;
};
#define OPT_PX_ONLINK 0x80
@@ -58,7 +58,7 @@ struct radv_opt_rdnss
u8 length;
u16 reserved;
u32 lifetime;
- ip_addr servers[];
+ ip6_addr servers[];
};
struct radv_opt_dnssl
@@ -90,11 +90,11 @@ radv_prefix_match(struct radv_iface *ifa, struct ifa *a)
return NULL;
WALK_LIST(pc, ifa->cf->pref_list)
- if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen))
+ if (net_in_netX(&a->prefix, (net_addr *) &pc->prefix))
return pc;
WALK_LIST(pc, cf->pref_list)
- if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen))
+ if (net_in_netX(&a->prefix, (net_addr *) &pc->prefix))
return pc;
return &default_prefix;
@@ -109,7 +109,7 @@ radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *b
{
struct radv_rdnss_config *rcf_base = rcf;
struct radv_opt_rdnss *op = (void *) *buf;
- int max_i = (bufend - *buf - sizeof(struct radv_opt_rdnss)) / sizeof(ip_addr);
+ int max_i = (bufend - *buf - sizeof(struct radv_opt_rdnss)) / sizeof(ip6_addr);
int i = 0;
if (max_i < 1)
@@ -123,20 +123,19 @@ radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *b
else
op->lifetime = htonl(rcf->lifetime);
- while(NODE_VALID(rcf) &&
+ while(NODE_VALID(rcf) &&
(rcf->lifetime == rcf_base->lifetime) &&
(rcf->lifetime_mult == rcf_base->lifetime_mult))
{
if (i >= max_i)
goto too_much;
- op->servers[i] = rcf->server;
- ipa_hton(op->servers[i]);
+ op->servers[i] = ip6_hton(rcf->server);
i++;
rcf = NODE_NEXT(rcf);
}
-
+
op->length = 1+2*i;
*buf += 8 * op->length;
}
@@ -273,6 +272,9 @@ radv_prepare_ra(struct radv_iface *ifa)
struct ifa *addr;
WALK_LIST(addr, ifa->iface->addrs)
{
+ if (addr->prefix.type != NET_IP6)
+ continue;
+
struct radv_prefix_config *pc;
pc = radv_prefix_match(ifa, addr);
@@ -288,7 +290,7 @@ radv_prepare_ra(struct radv_iface *ifa)
struct radv_opt_prefix *op = (void *) buf;
op->type = OPT_PREFIX;
op->length = 4;
- op->pxlen = addr->pxlen;
+ op->pxlen = net6_pxlen(&addr->prefix);
op->flags = (pc->onlink ? OPT_PX_ONLINK : 0) |
(pc->autonomous ? OPT_PX_AUTONOMOUS : 0);
op->valid_lifetime = (ra->active || !pc->valid_lifetime_sensitive) ?
@@ -296,8 +298,7 @@ radv_prepare_ra(struct radv_iface *ifa)
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);
+ op->prefix = ip6_hton(net6_prefix(&addr->prefix));
buf += sizeof(*op);
}
@@ -409,6 +410,7 @@ radv_sk_open(struct radv_iface *ifa)
{
sock *sk = sk_new(ifa->ra->p.pool);
sk->type = SK_IP;
+ sk->subtype = SK_IPV6;
sk->dport = ICMPV6_PROTO;
sk->saddr = ifa->addr->ip;
diff --git a/proto/radv/radv.c b/proto/radv/radv.c
index 5c52217d..d7d975ab 100644
--- a/proto/radv/radv.c
+++ b/proto/radv/radv.c
@@ -143,7 +143,7 @@ find_lladdr(struct iface *iface)
{
struct ifa *a;
WALK_LIST(a, iface->addrs)
- if (a->scope == SCOPE_LINK)
+ if ((a->prefix.type == NET_IP6) && (a->scope == SCOPE_LINK))
return a;
return NULL;
@@ -256,11 +256,16 @@ radv_ifa_notify(struct proto *p, unsigned flags UNUSED, struct ifa *a)
radv_iface_notify(ifa, RA_EV_CHANGE);
}
-static inline int radv_net_match_trigger(struct radv_config *cf, net *n)
+static inline int
+radv_trigger_valid(struct radv_config *cf)
{
- return cf->trigger_valid &&
- (n->n.pxlen == cf->trigger_pxlen) &&
- ipa_equal(n->n.prefix, cf->trigger_prefix);
+ return cf->trigger.type != 0;
+}
+
+static inline int
+radv_net_match_trigger(struct radv_config *cf, net *n)
+{
+ return radv_trigger_valid(cf) && net_equal(n->n.addr, &cf->trigger);
}
int
@@ -276,7 +281,7 @@ radv_import_control(struct proto *p, rte **new, ea_list **attrs UNUSED, struct l
}
static void
-radv_rt_notify(struct proto *p, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs UNUSED)
+radv_rt_notify(struct proto *p, struct channel *ch 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);
@@ -303,23 +308,35 @@ radv_check_active(struct proto_radv *ra)
{
struct radv_config *cf = (struct radv_config *) (ra->p.cf);
- if (! cf->trigger_valid)
+ if (!radv_trigger_valid(cf))
return 1;
- return rt_examine(ra->p.table, cf->trigger_prefix, cf->trigger_pxlen,
- &(ra->p), ra->p.cf->out_filter);
+ struct channel *c = ra->p.main_channel;
+ return rt_examine(c->table, &cf->trigger, &ra->p, c->out_filter);
+}
+
+static void
+radv_postconfig(struct proto_config *CF)
+{
+ // struct radv_config *cf = (void *) CF;
+
+ /* Define default channel */
+ if (EMPTY_LIST(CF->channels))
+ channel_config_new(NULL, NET_IP6, CF);
}
static struct proto *
-radv_init(struct proto_config *c)
+radv_init(struct proto_config *CF)
{
- struct proto *p = proto_new(c, sizeof(struct proto_radv));
+ struct proto *p = proto_new(CF);
+
+ p->main_channel = proto_add_channel(p, proto_cf_main_channel(CF));
- 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;
}
@@ -330,7 +347,7 @@ radv_start(struct proto *p)
struct radv_config *cf = (struct radv_config *) (p->cf);
init_list(&(ra->iface_list));
- ra->active = !cf->trigger_valid;
+ ra->active = !radv_trigger_valid(cf);
return PS_UP;
}
@@ -355,11 +372,11 @@ radv_shutdown(struct proto *p)
}
static int
-radv_reconfigure(struct proto *p, struct proto_config *c)
+radv_reconfigure(struct proto *p, struct proto_config *CF)
{
struct proto_radv *ra = (struct proto_radv *) p;
// struct radv_config *old = (struct radv_config *) (p->cf);
- struct radv_config *new = (struct radv_config *) c;
+ struct radv_config *new = (struct radv_config *) CF;
/*
* The question is why there is a reconfigure function for RAdv if
@@ -369,7 +386,10 @@ 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 */
+ if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
+ return 0;
+
+ p->cf = CF; /* radv_check_active() requires proper p->cf */
ra->active = radv_check_active(ra);
struct iface *iface;
@@ -426,7 +446,10 @@ radv_get_status(struct proto *p, byte *buf)
struct protocol proto_radv = {
.name = "RAdv",
.template = "radv%d",
+ .channel_mask = NB_IP6,
+ .proto_size = sizeof(struct proto_radv),
.config_size = sizeof(struct radv_config),
+ .postconfig = radv_postconfig,
.init = radv_init,
.start = radv_start,
.shutdown = radv_shutdown,
diff --git a/proto/radv/radv.h b/proto/radv/radv.h
index e2bf07ba..735aa218 100644
--- a/proto/radv/radv.h
+++ b/proto/radv/radv.h
@@ -13,7 +13,7 @@
#include "lib/ip.h"
#include "lib/lists.h"
#include "lib/socket.h"
-#include "lib/timer.h"
+#include "sysdep/unix/timer.h"
#include "lib/resource.h"
#include "nest/protocol.h"
#include "nest/iface.h"
@@ -50,9 +50,7 @@ struct radv_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 */
+ net_addr trigger; /* Prefix of a trigger route, if defined */
};
struct radv_iface_config
@@ -83,8 +81,7 @@ struct radv_iface_config
struct radv_prefix_config
{
node n;
- ip_addr prefix;
- uint pxlen;
+ net_addr_ip6 prefix;
u8 skip; /* Do not include this prefix to RA */
u8 onlink; /* Standard options from RFC 4261 */
@@ -100,7 +97,7 @@ struct radv_rdnss_config
node n;
u32 lifetime; /* Valid if lifetime_mult is 0 */
u16 lifetime_mult; /* Lifetime specified as multiple of max_ra_int */
- ip_addr server; /* IP address of recursive DNS server */
+ ip6_addr server; /* IP address of recursive DNS server */
};
struct radv_dnssl_config
diff --git a/proto/rip/Makefile b/proto/rip/Makefile
index d2d3c987..7feabcd8 100644
--- a/proto/rip/Makefile
+++ b/proto/rip/Makefile
@@ -1,5 +1,6 @@
-source=rip.c packets.c
-root-rel=../../
-dir-name=proto/rip
+src := packets.c rip.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
-include ../../Rules
+tests_objs := $(tests_objs) $(src-o-files) \ No newline at end of file
diff --git a/proto/rip/config.Y b/proto/rip/config.Y
index 4ec45c7a..46f6c177 100644
--- a/proto/rip/config.Y
+++ b/proto/rip/config.Y
@@ -32,34 +32,40 @@ rip_check_auth(void)
CF_DECLS
-CF_KEYWORDS(RIP, ECMP, LIMIT, WEIGHT, INFINITY, METRIC, UPDATE, TIMEOUT,
+CF_KEYWORDS(RIP, NG, ECMP, LIMIT, WEIGHT, INFINITY, METRIC, UPDATE, TIMEOUT,
GARBAGE, PORT, ADDRESS, MODE, BROADCAST, MULTICAST, PASSIVE,
VERSION, SPLIT, HORIZON, POISON, REVERSE, CHECK, ZERO, TIME, BFD,
AUTHENTICATION, NONE, PLAINTEXT, CRYPTOGRAPHIC, MD5, TTL, SECURITY,
RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK, RIP_METRIC, RIP_TAG)
-%type <i> rip_auth
+%type <i> rip_variant rip_auth
CF_GRAMMAR
CF_ADDTO(proto, rip_proto)
-rip_proto_start: proto_start RIP
+rip_variant:
+ RIP { $$ = 1; }
+ | RIP NG { $$ = 0; }
+ ;
+
+rip_proto_start: proto_start rip_variant
{
this_proto = proto_config_new(&proto_rip, $1);
- init_list(&RIP_CFG->patt_list);
+ this_proto->net_type = $2 ? NET_IP4 : NET_IP6;
- RIP_CFG->rip2 = RIP_IS_V2;
+ init_list(&RIP_CFG->patt_list);
+ RIP_CFG->rip2 = $2;
RIP_CFG->infinity = RIP_DEFAULT_INFINITY;
-
RIP_CFG->min_timeout_time = 60;
RIP_CFG->max_garbage_time = 60;
};
rip_proto_item:
proto_item
+ | proto_channel
| ECMP bool { RIP_CFG->ecmp = $2 ? RIP_DEFAULT_ECMP_LIMIT : 0; }
- | ECMP bool LIMIT expr { RIP_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); }
+ | ECMP bool LIMIT expr { RIP_CFG->ecmp = $2 ? $4 : 0; }
| INFINITY expr { RIP_CFG->infinity = $2; }
| INTERFACE rip_iface
;
@@ -131,7 +137,7 @@ rip_iface_item:
| MODE MULTICAST { RIP_IFACE->mode = RIP_IM_MULTICAST; }
| MODE BROADCAST { RIP_IFACE->mode = RIP_IM_BROADCAST; if (rip_cfg_is_ng()) cf_error("Broadcast not supported in RIPng"); }
| PASSIVE bool { RIP_IFACE->passive = $2; }
- | ADDRESS ipa { RIP_IFACE->address = $2; }
+ | ADDRESS ipa { RIP_IFACE->address = $2; if (ipa_is_ip4($2) != rip_cfg_is_v2()) cf_error("IP address version mismatch"); }
| PORT expr { RIP_IFACE->port = $2; if (($2<1) || ($2>65535)) cf_error("Invalid port number"); }
| VERSION expr { RIP_IFACE->version = $2;
if (rip_cfg_is_ng()) cf_error("Version not supported in RIPng");
diff --git a/proto/rip/packets.c b/proto/rip/packets.c
index 468927e6..e97809c8 100644
--- a/proto/rip/packets.c
+++ b/proto/rip/packets.c
@@ -9,6 +9,8 @@
* Can be freely distributed and used under the terms of the GNU GPL.
*/
+#undef LOCAL_DEBUG
+
#include "rip.h"
#include "lib/mac.h"
@@ -76,8 +78,7 @@ struct rip_auth_tail
/* Internal representation of RTE block data */
struct rip_block
{
- ip_addr prefix;
- int pxlen;
+ net_addr net;
u32 metric;
u16 tag;
u16 no_af;
@@ -106,30 +107,30 @@ static inline uint rip_pkt_hdrlen(struct rip_iface *ifa)
{ return sizeof(struct rip_packet) + (ifa->cf->auth_type ? RIP_BLOCK_LENGTH : 0); }
static inline void
-rip_put_block(struct rip_proto *p UNUSED4 UNUSED6, byte *pos, struct rip_block *rte)
+rip_put_block(struct rip_proto *p, byte *pos, struct rip_block *rte)
{
if (rip_is_v2(p))
{
struct rip_block_v2 *block = (void *) pos;
block->family = rte->no_af ? 0 : htons(RIP_AF_IPV4);
block->tag = htons(rte->tag);
- block->network = ip4_hton(ipa_to_ip4(rte->prefix));
- block->netmask = ip4_hton(ip4_mkmask(rte->pxlen));
+ block->network = ip4_hton(net4_prefix(&rte->net));
+ block->netmask = ip4_hton(ip4_mkmask(net4_pxlen(&rte->net)));
block->next_hop = ip4_hton(ipa_to_ip4(rte->next_hop));
block->metric = htonl(rte->metric);
}
else /* RIPng */
{
struct rip_block_ng *block = (void *) pos;
- block->prefix = ip6_hton(ipa_to_ip6(rte->prefix));
+ block->prefix = ip6_hton(net6_prefix(&rte->net));
block->tag = htons(rte->tag);
- block->pxlen = rte->pxlen;
+ block->pxlen = net6_pxlen(&rte->net);
block->metric = rte->metric;
}
}
static inline void
-rip_put_next_hop(struct rip_proto *p UNUSED, byte *pos, struct rip_block *rte UNUSED4)
+rip_put_next_hop(struct rip_proto *p UNUSED, byte *pos, struct rip_block *rte)
{
struct rip_block_ng *block = (void *) pos;
block->prefix = ip6_hton(ipa_to_ip6(rte->next_hop));
@@ -139,7 +140,7 @@ rip_put_next_hop(struct rip_proto *p UNUSED, byte *pos, struct rip_block *rte UN
}
static inline int
-rip_get_block(struct rip_proto *p UNUSED4 UNUSED6, byte *pos, struct rip_block *rte)
+rip_get_block(struct rip_proto *p, byte *pos, struct rip_block *rte)
{
if (rip_is_v2(p))
{
@@ -149,8 +150,8 @@ rip_get_block(struct rip_proto *p UNUSED4 UNUSED6, byte *pos, struct rip_block *
if (block->family != (rte->no_af ? 0 : htons(RIP_AF_IPV4)))
return 0;
- rte->prefix = ipa_from_ip4(ip4_ntoh(block->network));
- rte->pxlen = ip4_masklen(ip4_ntoh(block->netmask));
+ uint pxlen = ip4_masklen(ip4_ntoh(block->netmask));
+ net_fill_ip4(&rte->net, ip4_ntoh(block->network), pxlen);
rte->metric = ntohl(block->metric);
rte->tag = ntohs(block->tag);
rte->next_hop = ipa_from_ip4(ip4_ntoh(block->next_hop));
@@ -169,8 +170,8 @@ rip_get_block(struct rip_proto *p UNUSED4 UNUSED6, byte *pos, struct rip_block *
return 0;
}
- rte->prefix = ipa_from_ip6(ip6_ntoh(block->prefix));
- rte->pxlen = block->pxlen;
+ uint pxlen = (block->pxlen <= IP6_MAX_PREFIX_LENGTH) ? block->pxlen : 255;
+ net_fill_ip6(&rte->net, ip6_ntoh(block->prefix), pxlen);
rte->metric = block->metric;
rte->tag = ntohs(block->tag);
/* rte->next_hop is deliberately kept unmodified */;
@@ -406,8 +407,9 @@ rip_receive_request(struct rip_proto *p, struct rip_iface *ifa, struct rip_packe
if (!rip_get_block(p, pos, &b))
return;
- /* Special case - zero prefix, infinity metric */
- if (ipa_nonzero(b.prefix) || b.pxlen || (b.metric != p->infinity))
+ /* Special case - infinity metric, for RIPng also zero prefix */
+ if ((b.metric != p->infinity) ||
+ (rip_is_ng(p) && !net_zero_ip6((net_addr_ip6 *) &b.net)))
return;
/* We do nothing if TX is already active */
@@ -440,17 +442,15 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa)
pkt->unused = 0;
pos += rip_pkt_hdrlen(ifa);
- FIB_ITERATE_START(&p->rtable, &ifa->tx_fit, z)
+ FIB_ITERATE_START(&p->rtable, &ifa->tx_fit, struct rip_entry, en)
{
- struct rip_entry *en = (struct rip_entry *) z;
-
/* Dummy entries */
if (!en->valid)
goto next_entry;
/* Stale entries that should be removed */
if ((en->valid == RIP_ENTRY_STALE) &&
- ((en->changed + ifa->cf->garbage_time) <= now))
+ ((en->changed + (bird_clock_t) ifa->cf->garbage_time) <= now))
goto next_entry;
/* Triggered updates */
@@ -460,28 +460,28 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa)
/* Not enough space for current entry */
if (pos > max)
{
- FIB_ITERATE_PUT(&ifa->tx_fit, z);
+ FIB_ITERATE_PUT(&ifa->tx_fit);
goto break_loop;
}
struct rip_block rte = {
- .prefix = en->n.prefix,
- .pxlen = en->n.pxlen,
.metric = en->metric,
.tag = en->tag
};
+ net_copy(&rte.net, en->n.addr);
+
if (en->iface == ifa->iface)
rte.next_hop = en->next_hop;
if (rip_is_v2(p) && (ifa->cf->version == RIP_V1))
{
/* Skipping subnets (i.e. not hosts, classful networks or default route) */
- if (ip4_masklen(ip4_class_mask(ipa_to_ip4(en->n.prefix))) != en->n.pxlen)
+ if (ip4_masklen(ip4_class_mask(net4_prefix(&rte.net))) != rte.net.pxlen)
goto next_entry;
rte.tag = 0;
- rte.pxlen = 0;
+ rte.net.pxlen = 0;
rte.next_hop = IPA_NONE;
}
@@ -497,7 +497,7 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa)
goto next_entry;
}
- // TRACE(D_PACKETS, " %I/%d -> %I metric %d", rte.prefix, rte.pxlen, rte.next_hop, rte.metric);
+ // TRACE(D_PACKETS, " %N -> %I metric %d", &rte.net, rte.next_hop, rte.metric);
/* RIPng next hop entry */
if (rip_is_ng(p) && !ipa_equal(rte.next_hop, last_next_hop))
@@ -513,7 +513,7 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa)
next_entry: ;
}
- FIB_ITERATE_END(z);
+ FIB_ITERATE_END;
ifa->tx_active = 0;
/* Do not send empty packet */
@@ -542,7 +542,7 @@ break_loop:
void
rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, bird_clock_t changed)
{
- DBG("RIP: Opening TX session to %I on %s\n", dst, ifa->iface->name);
+ DBG("RIP: Opening TX session to %I on %s\n", addr, ifa->iface->name);
rip_reset_tx_session(p, ifa);
@@ -598,23 +598,25 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack
if (!rip_get_block(p, pos, &rte))
continue;
- int c = ipa_classify_net(rte.prefix);
- if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
- SKIP("invalid prefix");
-
if (rip_is_v2(p) && (pkt->version == RIP_V1))
{
- if (ifa->cf->check_zero && (rte.tag || rte.pxlen || ipa_nonzero(rte.next_hop)))
+ if (ifa->cf->check_zero && (rte.tag || rte.net.pxlen || ipa_nonzero(rte.next_hop)))
SKIP("RIPv1 reserved field is nonzero");
rte.tag = 0;
- rte.pxlen = ip4_masklen(ip4_class_mask(ipa_to_ip4(rte.prefix)));
+ rte.net.pxlen = ip4_masklen(ip4_class_mask(net4_prefix(&rte.net)));
rte.next_hop = IPA_NONE;
}
- if ((rte.pxlen < 0) || (rte.pxlen > MAX_PREFIX_LENGTH))
+ if (rte.net.pxlen == 255)
SKIP("invalid prefix length");
+ net_normalize(&rte.net);
+
+ int c = net_classify(&rte.net);
+ if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
+ SKIP("invalid prefix");
+
if (rte.metric > p->infinity)
SKIP("invalid metric");
@@ -625,7 +627,7 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack
rte.next_hop = IPA_NONE;
}
- // TRACE(D_PACKETS, " %I/%d -> %I metric %d", rte.prefix, rte.pxlen, rte.next_hop, rte.metric);
+ // TRACE(D_PACKETS, " %N -> %I metric %d", &rte.net.n, rte.next_hop, rte.metric);
rte.metric += ifa->cf->metric;
@@ -639,16 +641,16 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack
.expires = now + ifa->cf->timeout_time
};
- rip_update_rte(p, &rte.prefix, rte.pxlen, &new);
+ rip_update_rte(p, &rte.net, &new);
}
else
- rip_withdraw_rte(p, &rte.prefix, rte.pxlen, from);
+ rip_withdraw_rte(p, &rte.net, from);
continue;
skip:
- LOG_RTE("Ignoring route %I/%d received from %I - %s",
- rte.prefix, rte.pxlen, from->nbr->addr, err_dsc);
+ LOG_RTE("Ignoring route %N received from %I - %s",
+ &rte.net, from->nbr->addr, err_dsc);
}
}
@@ -736,6 +738,7 @@ rip_open_socket(struct rip_iface *ifa)
sock *sk = sk_new(p->p.pool);
sk->type = SK_UDP;
+ sk->subtype = rip_is_v2(p) ? SK_IPV4 : SK_IPV6;
sk->sport = ifa->cf->port;
sk->dport = ifa->cf->port;
sk->iface = ifa->iface;
diff --git a/proto/rip/rip.c b/proto/rip/rip.c
index 7b380097..55fb47c5 100644
--- a/proto/rip/rip.c
+++ b/proto/rip/rip.c
@@ -92,15 +92,6 @@ static void rip_trigger_update(struct rip_proto *p);
* RIP routes
*/
-static void
-rip_init_entry(struct fib_node *fn)
-{
- // struct rip_entry *en = (void) *fn;
-
- const uint offset = OFFSETOF(struct rip_entry, routes);
- memset((byte *)fn + offset, 0, sizeof(struct rip_entry) - offset);
-}
-
static struct rip_rte *
rip_add_rte(struct rip_proto *p, struct rip_rte **rp, struct rip_rte *src)
{
@@ -152,27 +143,20 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
if (rt)
{
/* Update */
- net *n = net_get(p->p.table, en->n.prefix, en->n.pxlen);
-
rta a0 = {
.src = p->p.main_source,
.source = RTS_RIP,
.scope = SCOPE_UNIVERSE,
- .cast = RTC_UNICAST
+ .dest = RTD_UNICAST,
};
u8 rt_metric = rt->metric;
u16 rt_tag = rt->tag;
- struct rip_rte *rt2 = rt->next;
-
- /* Find second valid rte */
- while (rt2 && !rip_valid_rte(rt2))
- rt2 = rt2->next;
- if (p->ecmp && rt2)
+ if (p->ecmp)
{
/* ECMP route */
- struct mpnh *nhs = NULL;
+ struct nexthop *nhs = NULL;
int num = 0;
for (rt = en->routes; rt && (num < p->ecmp); rt = rt->next)
@@ -180,54 +164,51 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
if (!rip_valid_rte(rt))
continue;
- struct mpnh *nh = alloca(sizeof(struct mpnh));
+ struct nexthop *nh = allocz(sizeof(struct nexthop));
+
nh->gw = rt->next_hop;
nh->iface = rt->from->nbr->iface;
nh->weight = rt->from->ifa->cf->ecmp_weight;
- mpnh_insert(&nhs, nh);
+
+ nexthop_insert(&nhs, nh);
num++;
if (rt->tag != rt_tag)
rt_tag = 0;
}
- a0.dest = RTD_MULTIPATH;
- a0.nexthops = nhs;
+ a0.nh = *nhs;
}
else
{
/* Unipath route */
- a0.dest = RTD_ROUTER;
- a0.gw = rt->next_hop;
- a0.iface = rt->from->nbr->iface;
a0.from = rt->from->nbr->addr;
+ a0.nh.gw = rt->next_hop;
+ a0.nh.iface = rt->from->nbr->iface;
}
rta *a = rta_lookup(&a0);
rte *e = rte_get_temp(a);
- e->u.rip.from = a0.iface;
+ e->u.rip.from = a0.nh.iface;
e->u.rip.metric = rt_metric;
e->u.rip.tag = rt_tag;
- e->net = n;
e->pflags = 0;
- rte_update(&p->p, n, e);
+ rte_update(&p->p, en->n.addr, e);
}
else
{
/* Withdraw */
- net *n = net_find(p->p.table, en->n.prefix, en->n.pxlen);
- rte_update(&p->p, n, NULL);
+ rte_update(&p->p, en->n.addr, NULL);
}
}
/**
* rip_update_rte - enter a route update to RIP routing table
* @p: RIP instance
- * @prefix: network prefix
- * @pxlen: network prefix length
+ * @addr: network address
* @new: a &rip_rte representing the new route
*
* The function is called by the RIP packet processing code whenever it receives
@@ -237,9 +218,9 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
* rip_withdraw_rte() should be called instead of rip_update_rte().
*/
void
-rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte *new)
+rip_update_rte(struct rip_proto *p, net_addr *n, struct rip_rte *new)
{
- struct rip_entry *en = fib_get(&p->rtable, prefix, pxlen);
+ struct rip_entry *en = fib_get(&p->rtable, n);
struct rip_rte *rt, **rp;
int changed = 0;
@@ -279,8 +260,7 @@ rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte *
/**
* rip_withdraw_rte - enter a route withdraw to RIP routing table
* @p: RIP instance
- * @prefix: network prefix
- * @pxlen: network prefix length
+ * @addr: network address
* @from: a &rip_neighbor propagating the withdraw
*
* The function is called by the RIP packet processing code whenever it receives
@@ -288,9 +268,9 @@ rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte *
* removed. Eventually, the change is also propagated by rip_announce_rte().
*/
void
-rip_withdraw_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_neighbor *from)
+rip_withdraw_rte(struct rip_proto *p, net_addr *n, struct rip_neighbor *from)
{
- struct rip_entry *en = fib_find(&p->rtable, prefix, pxlen);
+ struct rip_entry *en = fib_find(&p->rtable, n);
struct rip_rte *rt, **rp;
if (!en)
@@ -317,7 +297,7 @@ rip_withdraw_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_nei
* it into our data structures.
*/
static void
-rip_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net, struct rte *new,
+rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, struct rte *new,
struct rte *old UNUSED, struct ea_list *attrs)
{
struct rip_proto *p = (struct rip_proto *) P;
@@ -332,15 +312,15 @@ rip_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net,
if (rt_metric > p->infinity)
{
- log(L_WARN "%s: Invalid rip_metric value %u for route %I/%d",
- p->p.name, rt_metric, net->n.prefix, net->n.pxlen);
+ log(L_WARN "%s: Invalid rip_metric value %u for route %N",
+ p->p.name, rt_metric, net->n.addr);
rt_metric = p->infinity;
}
if (rt_tag > 0xffff)
{
- log(L_WARN "%s: Invalid rip_tag value %u for route %I/%d",
- p->p.name, rt_tag, net->n.prefix, net->n.pxlen);
+ log(L_WARN "%s: Invalid rip_tag value %u for route %N",
+ p->p.name, rt_tag, net->n.addr);
rt_metric = p->infinity;
rt_tag = 0;
}
@@ -352,7 +332,7 @@ rip_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net,
* collection.
*/
- en = fib_get(&p->rtable, &net->n.prefix, net->n.pxlen);
+ en = fib_get(&p->rtable, net->n.addr);
old_metric = en->valid ? en->metric : -1;
@@ -360,13 +340,13 @@ rip_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net,
en->metric = rt_metric;
en->tag = rt_tag;
en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL;
- en->iface = new->attrs->iface;
- en->next_hop = new->attrs->gw;
+ en->iface = new->attrs->nh.iface;
+ en->next_hop = new->attrs->nh.gw;
}
else
{
/* Withdraw */
- en = fib_find(&p->rtable, &net->n.prefix, net->n.pxlen);
+ en = fib_find(&p->rtable, net->n.addr);
if (!en || en->valid != RIP_ENTRY_VALID)
return;
@@ -704,7 +684,7 @@ rip_reconfigure_iface(struct rip_proto *p, struct rip_iface *ifa, struct rip_ifa
rip_iface_update_buffers(ifa);
- if (ifa->next_regular > (now + new->update_time))
+ if (ifa->next_regular > (now + (bird_clock_t) new->update_time))
ifa->next_regular = now + (random() % new->update_time) + 1;
if (new->check_link != old->check_link)
@@ -830,9 +810,8 @@ rip_timer(timer *t)
FIB_ITERATE_INIT(&fit, &p->rtable);
loop:
- FIB_ITERATE_START(&p->rtable, &fit, node)
+ FIB_ITERATE_START(&p->rtable, &fit, struct rip_entry, en)
{
- struct rip_entry *en = (struct rip_entry *) node;
struct rip_rte *rt, **rp;
int changed = 0;
@@ -859,7 +838,7 @@ rip_timer(timer *t)
* rip_rt_notify() -> p->rtable change, invalidating hidden variables.
*/
- FIB_ITERATE_PUT_NEXT(&fit, &p->rtable, node);
+ FIB_ITERATE_PUT_NEXT(&fit, &p->rtable);
rip_announce_rte(p, en);
goto loop;
}
@@ -871,7 +850,7 @@ rip_timer(timer *t)
if (expires <= now)
{
- // TRACE(D_EVENTS, "entry is too old: %I/%d", en->n.prefix, en->n.pxlen);
+ // TRACE(D_EVENTS, "entry is too old: %N", en->n.addr);
en->valid = 0;
}
else
@@ -881,12 +860,12 @@ rip_timer(timer *t)
/* Remove empty nodes */
if (!en->valid && !en->routes)
{
- FIB_ITERATE_PUT(&fit, node);
- fib_delete(&p->rtable, node);
+ FIB_ITERATE_PUT(&fit);
+ fib_delete(&p->rtable, en);
goto loop;
}
}
- FIB_ITERATE_END(node);
+ FIB_ITERATE_END;
p->rt_reload = 0;
@@ -1035,19 +1014,17 @@ rip_import_control(struct proto *P UNUSED, struct rte **rt, struct ea_list **att
return 0;
}
-static int
-rip_reload_routes(struct proto *P)
+static void
+rip_reload_routes(struct channel *C)
{
- struct rip_proto *p = (struct rip_proto *) P;
+ struct rip_proto *p = (struct rip_proto *) C->proto;
if (p->rt_reload)
- return 1;
+ return;
TRACE(D_EVENTS, "Scheduling route reload");
p->rt_reload = 1;
rip_kick_timer(p);
-
- return 1;
}
static struct ea_list *
@@ -1078,12 +1055,23 @@ rip_rte_same(struct rte *new, struct rte *old)
}
+static void
+rip_postconfig(struct proto_config *CF)
+{
+ // struct rip_config *cf = (void *) CF;
+
+ /* Define default channel */
+ if (EMPTY_LIST(CF->channels))
+ channel_config_new(NULL, CF->net_type, CF);
+}
+
static struct proto *
-rip_init(struct proto_config *cfg)
+rip_init(struct proto_config *CF)
{
- struct proto *P = proto_new(cfg, sizeof(struct rip_proto));
+ struct proto *P = proto_new(CF);
+
+ P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
- P->accept_ra_types = RA_OPTIMAL;
P->if_notify = rip_if_notify;
P->rt_notify = rip_rt_notify;
P->neigh_notify = rip_neigh_notify;
@@ -1104,10 +1092,12 @@ rip_start(struct proto *P)
struct rip_config *cf = (void *) (P->cf);
init_list(&p->iface_list);
- fib_init(&p->rtable, P->pool, sizeof(struct rip_entry), 0, rip_init_entry);
+ fib_init(&p->rtable, P->pool, cf->rip2 ? NET_IP4 : NET_IP6,
+ sizeof(struct rip_entry), OFFSETOF(struct rip_entry, n), 0, NULL);
p->rte_slab = sl_new(P->pool, sizeof(struct rip_rte));
p->timer = tm_new_set(P->pool, rip_timer, p, 0, 0);
+ p->rip2 = cf->rip2;
p->ecmp = cf->ecmp;
p->infinity = cf->infinity;
p->triggered = 0;
@@ -1121,18 +1111,24 @@ rip_start(struct proto *P)
}
static int
-rip_reconfigure(struct proto *P, struct proto_config *c)
+rip_reconfigure(struct proto *P, struct proto_config *CF)
{
struct rip_proto *p = (void *) P;
- struct rip_config *new = (void *) c;
+ struct rip_config *new = (void *) CF;
// struct rip_config *old = (void *) (P->cf);
+ if (new->rip2 != p->rip2)
+ return 0;
+
if (new->infinity != p->infinity)
return 0;
+ if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
+ return 0;
+
TRACE(D_EVENTS, "Reconfiguring");
- p->p.cf = c;
+ p->p.cf = CF;
p->ecmp = new->ecmp;
rip_reconfigure_ifaces(p, new);
@@ -1250,11 +1246,10 @@ rip_dump(struct proto *P)
int i;
i = 0;
- FIB_WALK(&p->rtable, e)
+ FIB_WALK(&p->rtable, struct rip_entry, en)
{
- struct rip_entry *en = (struct rip_entry *) e;
- debug("RIP: entry #%d: %I/%d via %I dev %s valid %d metric %d age %d s\n",
- i++, en->n.prefix, en->n.pxlen, en->next_hop, en->iface->name,
+ debug("RIP: entry #%d: %N via %I dev %s valid %d metric %d age %d s\n",
+ i++, en->n.addr, en->next_hop, en->iface->name,
en->valid, en->metric, now - en->changed);
}
FIB_WALK_END;
@@ -1274,7 +1269,10 @@ struct protocol proto_rip = {
.template = "rip%d",
.attr_class = EAP_RIP,
.preference = DEF_PREF_RIP,
+ .channel_mask = NB_IP,
+ .proto_size = sizeof(struct rip_proto),
.config_size = sizeof(struct rip_config),
+ .postconfig = rip_postconfig,
.init = rip_init,
.dump = rip_dump,
.start = rip_start,
diff --git a/proto/rip/rip.h b/proto/rip/rip.h
index b24d9536..03dc9138 100644
--- a/proto/rip/rip.h
+++ b/proto/rip/rip.h
@@ -24,15 +24,9 @@
#include "lib/resource.h"
#include "lib/socket.h"
#include "lib/string.h"
-#include "lib/timer.h"
+#include "sysdep/unix/timer.h"
-#ifdef IPV6
-#define RIP_IS_V2 0
-#else
-#define RIP_IS_V2 1
-#endif
-
#define RIP_V1 1
#define RIP_V2 2
@@ -98,6 +92,7 @@ struct rip_proto
slab *rte_slab; /* Slab for internal routes (struct rip_rte) */
timer *timer; /* Main protocol timer */
+ u8 rip2; /* RIPv2 (IPv4) or RIPng (IPv6) */
u8 ecmp; /* Maximum number of nexthops in ECMP route, or 0 */
u8 infinity; /* Maximum metric value, representing infinity */
u8 triggered; /* Logical AND of interface want_triggered values */
@@ -149,7 +144,6 @@ struct rip_neighbor
struct rip_entry
{
- struct fib_node n;
struct rip_rte *routes; /* List of incoming routes */
u8 valid; /* Entry validity state (RIP_ENTRY_*) */
@@ -160,6 +154,8 @@ struct rip_entry
ip_addr next_hop; /* Outgoing route next hop */
bird_clock_t changed; /* Last time when the outgoing route metric changed */
+
+ struct fib_node n;
};
struct rip_rte
@@ -189,16 +185,11 @@ struct rip_rte
#define EA_RIP_METRIC EA_CODE(EAP_RIP, 0)
#define EA_RIP_TAG EA_CODE(EAP_RIP, 1)
-#define rip_is_v2(X) RIP_IS_V2
-#define rip_is_ng(X) (!RIP_IS_V2)
-
-/*
static inline int rip_is_v2(struct rip_proto *p)
{ return p->rip2; }
static inline int rip_is_ng(struct rip_proto *p)
{ return ! p->rip2; }
-*/
static inline void
rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa)
@@ -211,8 +202,8 @@ rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa)
}
/* rip.c */
-void rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte *new);
-void rip_withdraw_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_neighbor *from);
+void rip_update_rte(struct rip_proto *p, net_addr *n, struct rip_rte *new);
+void rip_withdraw_rte(struct rip_proto *p, net_addr *n, struct rip_neighbor *from);
struct rip_neighbor * rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa);
void rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n);
void rip_show_interfaces(struct proto *P, char *iff);
diff --git a/proto/rpki/Doc b/proto/rpki/Doc
new file mode 100644
index 00000000..d1d1bf55
--- /dev/null
+++ b/proto/rpki/Doc
@@ -0,0 +1,5 @@
+S rpki.c
+S packets.c
+S transport.c
+S tcp_transport.c
+S ssh_transport.c
diff --git a/proto/rpki/Makefile b/proto/rpki/Makefile
new file mode 100644
index 00000000..eb09b7df
--- /dev/null
+++ b/proto/rpki/Makefile
@@ -0,0 +1,6 @@
+src := rpki.c packets.c tcp_transport.c ssh_transport.c transport.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
+
+tests_objs := $(tests_objs) $(src-o-files) \ No newline at end of file
diff --git a/proto/rpki/config.Y b/proto/rpki/config.Y
new file mode 100644
index 00000000..39fdfd01
--- /dev/null
+++ b/proto/rpki/config.Y
@@ -0,0 +1,144 @@
+/*
+ * BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
+ *
+ * (c) 2015 CZ.NIC
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+CF_HDR
+
+#include "proto/rpki/rpki.h"
+
+CF_DEFINES
+
+#define RPKI_CFG ((struct rpki_config *) this_proto)
+#define RPKI_TR_SSH_CFG ((struct rpki_tr_ssh_config *) RPKI_CFG->tr_config.spec)
+
+static void
+rpki_check_unused_hostname(void)
+{
+ if (RPKI_CFG->hostname != NULL)
+ cf_error("Only one cache server per protocol allowed");
+}
+
+static void
+rpki_check_unused_transport(void)
+{
+ if (RPKI_CFG->tr_config.spec != NULL)
+ cf_error("At the most one transport per protocol allowed");
+}
+
+CF_DECLS
+
+CF_KEYWORDS(RPKI, REMOTE, BIRD, PRIVATE, PUBLIC, KEY, TCP, SSH, TRANSPORT, USER,
+ RETRY, REFRESH, EXPIRE, KEEP)
+
+%type <i> rpki_keep_interval
+
+CF_GRAMMAR
+
+CF_ADDTO(proto, rpki_proto)
+
+rpki_proto_start: proto_start RPKI {
+ this_proto = proto_config_new(&proto_rpki, $1);
+ RPKI_CFG->retry_interval = RPKI_RETRY_INTERVAL;
+ RPKI_CFG->refresh_interval = RPKI_REFRESH_INTERVAL;
+ RPKI_CFG->expire_interval = RPKI_EXPIRE_INTERVAL;
+};
+
+rpki_proto: rpki_proto_start proto_name '{' rpki_proto_opts '}' { rpki_check_config(RPKI_CFG); };
+
+rpki_proto_opts:
+ /* empty */
+ | rpki_proto_opts rpki_proto_item ';'
+ ;
+
+rpki_proto_item:
+ proto_item
+ | proto_channel
+ | REMOTE rpki_cache_addr
+ | REMOTE rpki_cache_addr rpki_proto_item_port
+ | rpki_proto_item_port
+ | TRANSPORT rpki_transport
+ | REFRESH rpki_keep_interval expr {
+ if (rpki_check_refresh_interval($3))
+ cf_error(rpki_check_refresh_interval($3));
+ RPKI_CFG->refresh_interval = $3;
+ RPKI_CFG->keep_refresh_interval = $2;
+ }
+ | RETRY rpki_keep_interval expr {
+ if (rpki_check_retry_interval($3))
+ cf_error(rpki_check_retry_interval($3));
+ RPKI_CFG->retry_interval = $3;
+ RPKI_CFG->keep_retry_interval = $2;
+ }
+ | EXPIRE rpki_keep_interval expr {
+ if (rpki_check_expire_interval($3))
+ cf_error(rpki_check_expire_interval($3));
+ RPKI_CFG->expire_interval = $3;
+ RPKI_CFG->keep_expire_interval = $2;
+ }
+ ;
+
+rpki_keep_interval:
+ /* empty */ { $$ = 0; }
+ | KEEP { $$ = 1; }
+ ;
+
+rpki_proto_item_port: PORT expr { check_u16($2); RPKI_CFG->port = $2; };
+
+rpki_cache_addr:
+ text {
+ rpki_check_unused_hostname();
+ RPKI_CFG->hostname = $1;
+ }
+ | ipa {
+ rpki_check_unused_hostname();
+ RPKI_CFG->ip = $1;
+ /* Ensure hostname is filled */
+ char *hostname = cfg_allocz(sizeof(INET6_ADDRSTRLEN + 1));
+ bsnprintf(hostname, INET6_ADDRSTRLEN+1, "%I", RPKI_CFG->ip);
+ RPKI_CFG->hostname = hostname;
+ }
+ ;
+
+rpki_transport:
+ TCP rpki_transport_tcp_init
+ | SSH rpki_transport_ssh_init '{' rpki_transport_ssh_opts '}' rpki_transport_ssh_check
+ ;
+
+rpki_transport_tcp_init:
+{
+ rpki_check_unused_transport();
+ RPKI_CFG->tr_config.spec = cfg_allocz(sizeof(struct rpki_tr_tcp_config));
+ RPKI_CFG->tr_config.type = RPKI_TR_TCP;
+};
+
+rpki_transport_ssh_init:
+{
+ rpki_check_unused_transport();
+ RPKI_CFG->tr_config.spec = cfg_allocz(sizeof(struct rpki_tr_ssh_config));
+ RPKI_CFG->tr_config.type = RPKI_TR_SSH;
+};
+
+rpki_transport_ssh_opts:
+ /* empty */
+ | rpki_transport_ssh_opts rpki_transport_ssh_item ';'
+ ;
+
+rpki_transport_ssh_item:
+ BIRD PRIVATE KEY text { RPKI_TR_SSH_CFG->bird_private_key = $4; }
+ | REMOTE PUBLIC KEY text { RPKI_TR_SSH_CFG->cache_public_key = $4; }
+ | USER text { RPKI_TR_SSH_CFG->user = $2; }
+ ;
+
+rpki_transport_ssh_check:
+{
+ if (RPKI_TR_SSH_CFG->user == NULL)
+ cf_error("User must be set");
+};
+
+CF_CODE
+
+CF_END
diff --git a/proto/rpki/packets.c b/proto/rpki/packets.c
new file mode 100644
index 00000000..60ca3936
--- /dev/null
+++ b/proto/rpki/packets.c
@@ -0,0 +1,1073 @@
+/*
+ * BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
+ *
+ * (c) 2015 CZ.NIC
+ * (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
+ *
+ * This file was a part of RTRlib: http://rpki.realmv6.org/
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#undef LOCAL_DEBUG
+
+#include "rpki.h"
+#include "transport.h"
+#include "packets.h"
+
+#define RPKI_ADD_FLAG 0b00000001
+
+enum rpki_transmit_type {
+ RPKI_RECV = 0,
+ RPKI_SEND = 1,
+};
+
+enum pdu_error_type {
+ CORRUPT_DATA = 0,
+ INTERNAL_ERROR = 1,
+ NO_DATA_AVAIL = 2,
+ INVALID_REQUEST = 3,
+ UNSUPPORTED_PROTOCOL_VER = 4,
+ UNSUPPORTED_PDU_TYPE = 5,
+ WITHDRAWAL_OF_UNKNOWN_RECORD = 6,
+ DUPLICATE_ANNOUNCEMENT = 7,
+ PDU_TOO_BIG = 32
+};
+
+static const char *str_pdu_error_type[] = {
+ [CORRUPT_DATA] = "Corrupt-Data",
+ [INTERNAL_ERROR] = "Internal-Error",
+ [NO_DATA_AVAIL] = "No-Data-Available",
+ [INVALID_REQUEST] = "Invalid-Request",
+ [UNSUPPORTED_PROTOCOL_VER] = "Unsupported-Protocol-Version",
+ [UNSUPPORTED_PDU_TYPE] = "Unsupported-PDU-Type",
+ [WITHDRAWAL_OF_UNKNOWN_RECORD]= "Withdrawal-Of-Unknown-Record",
+ [DUPLICATE_ANNOUNCEMENT] = "Duplicate-Announcement",
+ [PDU_TOO_BIG] = "PDU-Too-Big",
+};
+
+enum pdu_type {
+ SERIAL_NOTIFY = 0,
+ SERIAL_QUERY = 1,
+ RESET_QUERY = 2,
+ CACHE_RESPONSE = 3,
+ IPV4_PREFIX = 4,
+ RESERVED = 5,
+ IPV6_PREFIX = 6,
+ END_OF_DATA = 7,
+ CACHE_RESET = 8,
+ ROUTER_KEY = 9,
+ ERROR = 10,
+ PDU_TYPE_MAX
+};
+
+static const char *str_pdu_type_[] = {
+ [SERIAL_NOTIFY] = "Serial Notify",
+ [SERIAL_QUERY] = "Serial Query",
+ [RESET_QUERY] = "Reset Query",
+ [CACHE_RESPONSE] = "Cache Response",
+ [IPV4_PREFIX] = "IPv4 Prefix",
+ [RESERVED] = "Reserved",
+ [IPV6_PREFIX] = "IPv6 Prefix",
+ [END_OF_DATA] = "End of Data",
+ [CACHE_RESET] = "Cache Reset",
+ [ROUTER_KEY] = "Router Key",
+ [ERROR] = "Error"
+};
+
+static const char *str_pdu_type(uint type) {
+ if (type < PDU_TYPE_MAX)
+ return str_pdu_type_[type];
+ else
+ return "Undefined packet type";
+}
+
+/*
+ * 0 8 16 24 31
+ * .-------------------------------------------.
+ * | Protocol | PDU | |
+ * | Version | Type | reserved = zero |
+ * | 0 or 1 | 0 - 10 | |
+ * +-------------------------------------------+
+ * | |
+ * | Length >= 8 |
+ * | |
+ * `-------------------------------------------' */
+struct pdu_header {
+ u8 ver;
+ u8 type;
+ u16 reserved;
+ u32 len;
+} PACKED;
+
+struct pdu_cache_response {
+ u8 ver;
+ u8 type;
+ u16 session_id;
+ u32 len;
+} PACKED;
+
+struct pdu_serial_notify {
+ u8 ver;
+ u8 type;
+ u16 session_id;
+ u32 len;
+ u32 serial_num;
+} PACKED;
+
+struct pdu_serial_query {
+ u8 ver;
+ u8 type;
+ u16 session_id;
+ u32 len;
+ u32 serial_num;
+} PACKED;
+
+struct pdu_ipv4 {
+ u8 ver;
+ u8 type;
+ u16 reserved;
+ u32 len;
+ u8 flags;
+ u8 prefix_len;
+ u8 max_prefix_len;
+ u8 zero;
+ ip4_addr prefix;
+ u32 asn;
+} PACKED;
+
+struct pdu_ipv6 {
+ u8 ver;
+ u8 type;
+ u16 reserved;
+ u32 len;
+ u8 flags;
+ u8 prefix_len;
+ u8 max_prefix_len;
+ u8 zero;
+ ip6_addr prefix;
+ u32 asn;
+} PACKED;
+
+/*
+ * 0 8 16 24 31
+ * .-------------------------------------------.
+ * | Protocol | PDU | |
+ * | Version | Type | Error Code |
+ * | 1 | 10 | |
+ * +-------------------------------------------+
+ * | |
+ * | Length |
+ * | |
+ * +-------------------------------------------+
+ * | |
+ * | Length of Encapsulated PDU |
+ * | |
+ * +-------------------------------------------+
+ * | |
+ * ~ Copy of Erroneous PDU ~
+ * | |
+ * +-------------------------------------------+
+ * | |
+ * | Length of Error Text |
+ * | |
+ * +-------------------------------------------+
+ * | |
+ * | Arbitrary Text |
+ * | of |
+ * ~ Error Diagnostic Message ~
+ * | |
+ * `-------------------------------------------' */
+struct pdu_error {
+ u8 ver;
+ u8 type;
+ u16 error_code;
+ u32 len;
+ u32 len_enc_pdu; /* Length of Encapsulated PDU */
+ byte rest[]; /* Copy of Erroneous PDU
+ * Length of Error Text
+ * Error Diagnostic Message */
+} PACKED;
+
+struct pdu_reset_query {
+ u8 ver;
+ u8 type;
+ u16 flags;
+ u32 len;
+} PACKED;
+
+struct pdu_end_of_data_v0 {
+ u8 ver;
+ u8 type;
+ u16 session_id;
+ u32 len;
+ u32 serial_num;
+} PACKED;
+
+struct pdu_end_of_data_v1 {
+ u8 ver;
+ u8 type;
+ u16 session_id;
+ u32 len;
+ u32 serial_num;
+ u32 refresh_interval;
+ u32 retry_interval;
+ u32 expire_interval;
+} PACKED;
+
+static const size_t min_pdu_size[] = {
+ [SERIAL_NOTIFY] = sizeof(struct pdu_serial_notify),
+ [SERIAL_QUERY] = sizeof(struct pdu_serial_query),
+ [RESET_QUERY] = sizeof(struct pdu_reset_query),
+ [CACHE_RESPONSE] = sizeof(struct pdu_cache_response),
+ [IPV4_PREFIX] = sizeof(struct pdu_ipv4),
+ [RESERVED] = sizeof(struct pdu_header),
+ [IPV6_PREFIX] = sizeof(struct pdu_ipv6),
+ [END_OF_DATA] = sizeof(struct pdu_end_of_data_v0),
+ [CACHE_RESET] = sizeof(struct pdu_cache_response),
+ [ROUTER_KEY] = sizeof(struct pdu_header), /* FIXME */
+ [ERROR] = 16,
+};
+
+static int rpki_send_error_pdu(struct rpki_cache *cache, const enum pdu_error_type error_code, const u32 err_pdu_len, const struct pdu_header *erroneous_pdu, const char *fmt, ...);
+
+static void
+rpki_pdu_to_network_byte_order(struct pdu_header *pdu)
+{
+ pdu->reserved = htons(pdu->reserved);
+ pdu->len = htonl(pdu->len);
+
+ switch (pdu->type)
+ {
+ case SERIAL_QUERY:
+ {
+ /* Note that a session_id is converted using converting header->reserved */
+ struct pdu_serial_query *sq_pdu = (void *) pdu;
+ sq_pdu->serial_num = htonl(sq_pdu->serial_num);
+ break;
+ }
+
+ case ERROR:
+ {
+ struct pdu_error *err = (void *) pdu;
+ u32 *err_text_len = (u32 *)(err->rest + err->len_enc_pdu);
+ *err_text_len = htonl(*err_text_len);
+ err->len_enc_pdu = htonl(err->len_enc_pdu);
+ break;
+ }
+
+ case RESET_QUERY:
+ break;
+
+ default:
+ bug("PDU type %s should not be sent by us", str_pdu_type(pdu->type));
+ }
+}
+
+static void
+rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
+{
+ /* The Router Key PDU has two one-byte fields instead of one two-bytes field. */
+ if (pdu->type != ROUTER_KEY)
+ pdu->reserved = ntohs(pdu->reserved);
+
+ pdu->len = ntohl(pdu->len);
+
+ switch (pdu->type)
+ {
+ case SERIAL_NOTIFY:
+ {
+ /* Note that a session_id is converted using converting header->reserved */
+ struct pdu_serial_notify *sn_pdu = (void *) pdu;
+ sn_pdu->serial_num = ntohl(sn_pdu->serial_num);
+ break;
+ }
+
+ case END_OF_DATA:
+ {
+ /* Note that a session_id is converted using converting header->reserved */
+ struct pdu_end_of_data_v0 *eod0 = (void *) pdu;
+ eod0->serial_num = ntohl(eod0->serial_num); /* Same either for version 1 */
+
+ if (pdu->ver == RPKI_VERSION_1)
+ {
+ struct pdu_end_of_data_v1 *eod1 = (void *) pdu;
+ eod1->expire_interval = ntohl(eod1->expire_interval);
+ eod1->refresh_interval = ntohl(eod1->refresh_interval);
+ eod1->retry_interval = ntohl(eod1->retry_interval);
+ }
+ break;
+ }
+
+ case IPV4_PREFIX:
+ {
+ struct pdu_ipv4 *ipv4 = (void *) pdu;
+ ipv4->prefix = ip4_ntoh(ipv4->prefix);
+ ipv4->asn = ntohl(ipv4->asn);
+ break;
+ }
+
+ case IPV6_PREFIX:
+ {
+ struct pdu_ipv6 *ipv6 = (void *) pdu;
+ ipv6->prefix = ip6_ntoh(ipv6->prefix);
+ ipv6->asn = ntohl(ipv6->asn);
+ break;
+ }
+
+ case ERROR:
+ {
+ /* Note that a error_code is converted using converting header->reserved */
+ struct pdu_error *err = (void *) pdu;
+ err->len_enc_pdu = ntohl(err->len_enc_pdu);
+ u32 *err_text_len = (u32 *)(err->rest + err->len_enc_pdu);
+ *err_text_len = htonl(*err_text_len);
+ break;
+ }
+
+ case ROUTER_KEY:
+ /* Router Key PDU is not supported yet */
+
+ case SERIAL_QUERY:
+ case RESET_QUERY:
+ /* Serial/Reset Query are sent only in direction router to cache.
+ * We don't care here. */
+
+ case CACHE_RESPONSE:
+ case CACHE_RESET:
+ /* Converted with pdu->reserved */
+ break;
+ }
+}
+
+/**
+ * rpki_convert_pdu_back_to_network_byte_order - convert host-byte order PDU back to network-byte order
+ * @out: allocated memory for writing a converted PDU of size @in->len
+ * @in: host-byte order PDU
+ *
+ * Assumed: |A == ntoh(ntoh(A))|
+ */
+static struct pdu_header *
+rpki_pdu_back_to_network_byte_order(struct pdu_header *out, const struct pdu_header *in)
+{
+ memcpy(out, in, in->len);
+ rpki_pdu_to_host_byte_order(out);
+ return out;
+}
+
+static void
+rpki_log_packet(struct rpki_cache *cache, const struct pdu_header *pdu, const enum rpki_transmit_type action)
+{
+ if (!(cache->p->p.debug & D_PACKETS))
+ return;
+
+ const char *str_type = str_pdu_type(pdu->type);
+ char detail[256];
+
+#define SAVE(fn) \
+ do { \
+ if (fn < 0) \
+ { \
+ bsnprintf(detail + sizeof(detail) - 16, 16, "... <too long>)"); \
+ goto detail_finished; \
+ } \
+ } while(0) \
+
+ switch (pdu->type)
+ {
+ case SERIAL_NOTIFY:
+ case SERIAL_QUERY:
+ SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u, serial number: %u)", pdu->reserved, ((struct pdu_serial_notify *) pdu)->serial_num));
+ break;
+
+ case END_OF_DATA:
+ {
+ const struct pdu_end_of_data_v1 *eod = (void *) pdu;
+ if (eod->ver == RPKI_VERSION_1)
+ SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u, serial number: %u, refresh: %us, retry: %us, expire: %us)", eod->session_id, eod->serial_num, eod->refresh_interval, eod->retry_interval, eod->expire_interval));
+ else
+ SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u, serial number: %u)", eod->session_id, eod->serial_num));
+ break;
+ }
+
+ case CACHE_RESPONSE:
+ SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u)", pdu->reserved));
+ break;
+
+ case IPV4_PREFIX:
+ {
+ const struct pdu_ipv4 *ipv4 = (void *) pdu;
+ SAVE(bsnprintf(detail, sizeof(detail), "(%I4/%u-%u AS%u)", ipv4->prefix, ipv4->prefix_len, ipv4->max_prefix_len, ipv4->asn));
+ break;
+ }
+
+ case IPV6_PREFIX:
+ {
+ const struct pdu_ipv6 *ipv6 = (void *) pdu;
+ SAVE(bsnprintf(detail, sizeof(detail), "(%I6/%u-%u AS%u)", ipv6->prefix, ipv6->prefix_len, ipv6->max_prefix_len, ipv6->asn));
+ break;
+ }
+
+ case ROUTER_KEY:
+ /* We don't support saving Router Key PDUs yet */
+ SAVE(bsnprintf(detail, sizeof(detail), "(ignored)"));
+ break;
+
+ case ERROR:
+ {
+ const struct pdu_error *err = (void *) pdu;
+ SAVE(bsnprintf(detail, sizeof(detail), "(%s", str_pdu_error_type[err->error_code]));
+
+ /* Optional description of error */
+ const u32 len_err_txt = *((u32 *) (err->rest + err->len_enc_pdu));
+ if (len_err_txt > 0)
+ {
+ size_t expected_len = err->len_enc_pdu + len_err_txt + 16;
+ if (expected_len == err->len)
+ {
+ char txt[len_err_txt + 1];
+ char *pdu_txt = (char *) err->rest + err->len_enc_pdu + 4;
+ bsnprintf(txt, sizeof(txt), "%s", pdu_txt); /* it's ensured that txt is ended with a null byte */
+ SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail), ": '%s'", txt));
+ }
+ else
+ {
+ SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail), ", malformed size"));
+ }
+ }
+
+ /* Optional encapsulated erroneous packet */
+ if (err->len_enc_pdu)
+ {
+ SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail), ", %s packet:", str_pdu_type(((struct pdu_header *) err->rest)->type)));
+ if (err->rest + err->len_enc_pdu <= (byte *)err + err->len)
+ {
+ for (const byte *c = err->rest; c != err->rest + err->len_enc_pdu; c++)
+ SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail), " %02X", *c));
+ }
+ }
+
+ SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail), ")"));
+ break;
+ }
+
+ default:
+ *detail = '\0';
+ }
+#undef SAVE
+
+ detail_finished:
+
+ if (action == RPKI_RECV)
+ {
+ CACHE_TRACE(D_PACKETS, cache, "Received %s packet %s", str_type, detail);
+ }
+ else
+ {
+ CACHE_TRACE(D_PACKETS, cache, "Sending %s packet %s", str_type, detail);
+ }
+
+#if defined(LOCAL_DEBUG) || defined(GLOBAL_DEBUG)
+ int seq = 0;
+ for(const byte *c = pdu; c != pdu + pdu->len; c++)
+ {
+ if ((seq % 4) == 0)
+ DBG("%2d: ", seq);
+
+ DBG(" 0x%02X %-3u", *c, *c);
+
+ if ((++seq % 4) == 0)
+ DBG("\n");
+ }
+ if ((seq % 4) != 0)
+ DBG("\n");
+#endif
+}
+
+static int
+rpki_send_pdu(struct rpki_cache *cache, const void *pdu, const uint len)
+{
+ struct rpki_proto *p = cache->p;
+ sock *sk = cache->tr_sock->sk;
+
+ rpki_log_packet(cache, pdu, RPKI_SEND);
+
+ if (sk->tbuf != sk->tpos)
+ {
+ RPKI_WARN(p, "Old packet overwritten in TX buffer");
+ }
+
+ if (len > sk->tbsize)
+ {
+ RPKI_WARN(p, "%u bytes is too much for send", len);
+ ASSERT(0);
+ return RPKI_ERROR;
+ }
+
+ memcpy(sk->tbuf, pdu, len);
+ rpki_pdu_to_network_byte_order((void *) sk->tbuf);
+
+ if (!sk_send(sk, len))
+ {
+ DBG("Cannot send just the whole data. It will be sent using a call of tx_hook()");
+ }
+
+ return RPKI_SUCCESS;
+}
+
+/**
+ * rpki_check_receive_packet - make a basic validation of received RPKI PDU header
+ * @cache: cache connection instance
+ * @pdu: RPKI PDU in network byte order
+ *
+ * This function checks protocol version, PDU type, version and size. If all is all right then
+ * function returns |RPKI_SUCCESS| otherwise sends Error PDU and returns
+ * |RPKI_ERROR|.
+ */
+static int
+rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu)
+{
+ u32 pdu_len = ntohl(pdu->len);
+
+ /*
+ * Minimal and maximal allowed PDU size is treated in rpki_rx_hook() function.
+ * @header.len corresponds to number of bytes of @pdu and
+ * it is in range from RPKI_PDU_HEADER_LEN to RPKI_PDU_MAX_LEN bytes.
+ */
+
+ /* Do not handle error PDUs here, leave this task to rpki_handle_error_pdu() */
+ if (pdu->ver != cache->version && pdu->type != ERROR)
+ {
+ /* If this is the first PDU we have received */
+ if (cache->request_session_id)
+ {
+ if (pdu->type == SERIAL_NOTIFY)
+ {
+ /*
+ * The router MUST ignore any Serial Notify PDUs it might receive from
+ * the cache during this initial start-up period, regardless of the
+ * Protocol Version field in the Serial Notify PDU.
+ * (https://tools.ietf.org/html/draft-ietf-sidr-rpki-rtr-rfc6810-bis-07#section-7)
+ */
+ }
+ else if (cache->last_update == 0
+ && pdu->ver <= RPKI_MAX_VERSION
+ && pdu->ver < cache->version)
+ {
+ CACHE_TRACE(D_EVENTS, cache, "Downgrade session to %s from %u to %u version", rpki_get_cache_ident(cache), cache->version, pdu->ver);
+ cache->version = pdu->ver;
+ }
+ else
+ {
+ /* If this is not the first PDU we have received, something is wrong with
+ * the server implementation -> Error */
+ rpki_send_error_pdu(cache, UNSUPPORTED_PROTOCOL_VER, pdu_len, pdu, "PDU with unsupported Protocol version received");
+ return RPKI_ERROR;
+ }
+ }
+ }
+
+ if ((pdu->type >= PDU_TYPE_MAX) || (pdu->ver == RPKI_VERSION_0 && pdu->type == ROUTER_KEY))
+ {
+ rpki_send_error_pdu(cache, UNSUPPORTED_PDU_TYPE, pdu_len, pdu, "Unsupported PDU type %u received", pdu->type);
+ return RPKI_ERROR;
+ }
+
+ if (pdu_len < min_pdu_size[pdu->type])
+ {
+ rpki_send_error_pdu(cache, CORRUPT_DATA, pdu_len, pdu, "Received %s packet with %d bytes, but expected at least %d bytes", str_pdu_type(pdu->type), pdu_len, min_pdu_size[pdu->type]);
+ return RPKI_ERROR;
+ }
+
+ return RPKI_SUCCESS;
+}
+
+static int
+rpki_handle_error_pdu(struct rpki_cache *cache, const struct pdu_error *pdu)
+{
+ switch (pdu->error_code)
+ {
+ case CORRUPT_DATA:
+ case INTERNAL_ERROR:
+ case INVALID_REQUEST:
+ case UNSUPPORTED_PDU_TYPE:
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_FATAL);
+ break;
+
+ case NO_DATA_AVAIL:
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_NO_DATA_AVAIL);
+ break;
+
+ case UNSUPPORTED_PROTOCOL_VER:
+ CACHE_TRACE(D_PACKETS, cache, "Client uses unsupported protocol version");
+ if (pdu->ver <= RPKI_MAX_VERSION &&
+ pdu->ver < cache->version)
+ {
+ CACHE_TRACE(D_EVENTS, cache, "Downgrading from protocol version %d to version %d", cache->version, pdu->ver);
+ cache->version = pdu->ver;
+ rpki_cache_change_state(cache, RPKI_CS_FAST_RECONNECT);
+ }
+ else
+ {
+ CACHE_TRACE(D_PACKETS, cache, "Got UNSUPPORTED_PROTOCOL_VER error PDU with invalid values, " \
+ "current version: %d, PDU version: %d", cache->version, pdu->ver);
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_FATAL);
+ }
+ break;
+
+ default:
+ CACHE_TRACE(D_PACKETS, cache, "Error unknown, server sent unsupported error code %u", pdu->error_code);
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_FATAL);
+ break;
+ }
+
+ return RPKI_SUCCESS;
+}
+
+static void
+rpki_handle_serial_notify_pdu(struct rpki_cache *cache, const struct pdu_serial_notify *pdu)
+{
+ /* The router MUST ignore any Serial Notify PDUs it might receive from
+ * the cache during this initial start-up period, regardless of the
+ * Protocol Version field in the Serial Notify PDU.
+ * (https://tools.ietf.org/html/draft-ietf-sidr-rpki-rtr-rfc6810-bis-07#section-7)
+ */
+ if (cache->request_session_id)
+ {
+ CACHE_TRACE(D_PACKETS, cache, "Ignore a Serial Notify packet during initial start-up period");
+ return;
+ }
+
+ /* XXX Serial number should be compared using method RFC 1982 (3.2) */
+ if (cache->serial_num != pdu->serial_num)
+ rpki_cache_change_state(cache, RPKI_CS_SYNC_START);
+}
+
+static int
+rpki_handle_cache_response_pdu(struct rpki_cache *cache, const struct pdu_cache_response *pdu)
+{
+ if (cache->request_session_id)
+ {
+ if (cache->last_update != 0)
+ {
+ /*
+ * This isn't the first sync and we already received records. This point
+ * is after Reset Query and before importing new records from cache
+ * server. We need to load new ones and kick out missing ones. So start
+ * a refresh cycle.
+ */
+ if (cache->p->roa4_channel)
+ rt_refresh_begin(cache->p->roa4_channel->table, cache->p->roa4_channel);
+ if (cache->p->roa6_channel)
+ rt_refresh_begin(cache->p->roa6_channel->table, cache->p->roa6_channel);
+
+ cache->p->refresh_channels = 1;
+ }
+ cache->session_id = pdu->session_id;
+ cache->request_session_id = 0;
+ }
+ else
+ {
+ if (cache->session_id != pdu->session_id)
+ {
+ byte tmp[pdu->len];
+ const struct pdu_header *hton_pdu = rpki_pdu_back_to_network_byte_order((void *) tmp, (const void *) pdu);
+ rpki_send_error_pdu(cache, CORRUPT_DATA, pdu->len, hton_pdu, "Wrong session_id %u in Cache Response PDU", pdu->session_id);
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_FATAL);
+ return RPKI_ERROR;
+ }
+ }
+
+ rpki_cache_change_state(cache, RPKI_CS_SYNC_RUNNING);
+ return RPKI_SUCCESS;
+}
+
+/**
+ * rpki_prefix_pdu_2_net_addr - convert IPv4/IPv6 Prefix PDU into net_addr_union
+ * @pdu: host byte order IPv4/IPv6 Prefix PDU
+ * @n: allocated net_addr_union for save ROA
+ *
+ * This function reads ROA data from IPv4/IPv6 Prefix PDU and
+ * write them into net_addr_roa4 or net_addr_roa6 data structure.
+ */
+static net_addr_union *
+rpki_prefix_pdu_2_net_addr(const struct pdu_header *pdu, net_addr_union *n)
+{
+ /*
+ * Note that sizeof(net_addr_roa6) > sizeof(net_addr)
+ * and thence we must use net_addr_union and not only net_addr
+ */
+
+ if (pdu->type == IPV4_PREFIX)
+ {
+ const struct pdu_ipv4 *ipv4 = (void *) pdu;
+ n->roa4.type = NET_ROA4;
+ n->roa4.length = sizeof(net_addr_roa4);
+ n->roa4.prefix = ipv4->prefix;
+ n->roa4.asn = ipv4->asn;
+ n->roa4.pxlen = ipv4->prefix_len;
+ n->roa4.max_pxlen = ipv4->max_prefix_len;
+ }
+ else
+ {
+ const struct pdu_ipv6 *ipv6 = (void *) pdu;
+ n->roa6.type = NET_ROA6;
+ n->roa6.length = sizeof(net_addr_roa6);
+ n->roa6.prefix = ipv6->prefix;
+ n->roa6.asn = ipv6->asn;
+ n->roa6.pxlen = ipv6->prefix_len;
+ n->roa6.max_pxlen = ipv6->max_prefix_len;
+ }
+
+ return n;
+}
+
+static int
+rpki_handle_prefix_pdu(struct rpki_cache *cache, const struct pdu_header *pdu)
+{
+ const enum pdu_type type = pdu->type;
+ ASSERT(type == IPV4_PREFIX || type == IPV6_PREFIX);
+
+ net_addr_union addr = {};
+ rpki_prefix_pdu_2_net_addr(pdu, &addr);
+
+ struct channel *channel = NULL;
+
+ if (type == IPV4_PREFIX)
+ channel = cache->p->roa4_channel;
+ if (type == IPV6_PREFIX)
+ channel = cache->p->roa6_channel;
+
+ if (!channel)
+ {
+ CACHE_TRACE(D_ROUTES, cache, "Skip %N, missing %s channel", &addr, (type == IPV4_PREFIX ? "roa4" : "roa6"), addr);
+ return RPKI_ERROR;
+ }
+
+ cache->last_rx_prefix = now;
+
+ /* A place for 'flags' is same for both data structures pdu_ipv4 or pdu_ipv6 */
+ struct pdu_ipv4 *pfx = (void *) pdu;
+ if (pfx->flags & RPKI_ADD_FLAG)
+ rpki_table_add_roa(cache, channel, &addr);
+ else
+ rpki_table_remove_roa(cache, channel, &addr);
+
+ return RPKI_SUCCESS;
+}
+
+static uint
+rpki_check_interval(struct rpki_cache *cache, const char *(check_fn)(uint), uint interval)
+{
+ if (check_fn(interval))
+ {
+ RPKI_WARN(cache->p, "%s, received %u seconds", check_fn(interval), interval);
+ return 0;
+ }
+ return 1;
+}
+
+static void
+rpki_handle_end_of_data_pdu(struct rpki_cache *cache, const struct pdu_end_of_data_v1 *pdu)
+{
+ const struct rpki_config *cf = (void *) cache->p->p.cf;
+
+ if (pdu->session_id != cache->session_id)
+ {
+ byte tmp[pdu->len];
+ const struct pdu_header *hton_pdu = rpki_pdu_back_to_network_byte_order((void *) tmp, (const void *) pdu);
+ rpki_send_error_pdu(cache, CORRUPT_DATA, pdu->len, hton_pdu, "Received Session ID %u, but expected %u", pdu->session_id, cache->session_id);
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_FATAL);
+ return;
+ }
+
+ if (pdu->ver == RPKI_VERSION_1)
+ {
+ if (!cf->keep_refresh_interval && rpki_check_interval(cache, rpki_check_refresh_interval, pdu->refresh_interval))
+ cache->refresh_interval = pdu->refresh_interval;
+
+ if (!cf->keep_retry_interval && rpki_check_interval(cache, rpki_check_retry_interval, pdu->retry_interval))
+ cache->retry_interval = pdu->retry_interval;
+
+ if (!cf->keep_expire_interval && rpki_check_interval(cache, rpki_check_expire_interval, pdu->expire_interval))
+ cache->expire_interval = pdu->expire_interval;
+
+ CACHE_TRACE(D_EVENTS, cache, "New interval values: "
+ "refresh: %s%us, "
+ "retry: %s%us, "
+ "expire: %s%us",
+ (cf->keep_refresh_interval ? "keeps " : ""), cache->refresh_interval,
+ (cf->keep_retry_interval ? "keeps " : ""), cache->retry_interval,
+ (cf->keep_expire_interval ? "keeps " : ""), cache->expire_interval);
+ }
+
+ if (cache->p->refresh_channels)
+ {
+ cache->p->refresh_channels = 0;
+ if (cache->p->roa4_channel)
+ rt_refresh_end(cache->p->roa4_channel->table, cache->p->roa4_channel);
+ if (cache->p->roa6_channel)
+ rt_refresh_end(cache->p->roa6_channel->table, cache->p->roa6_channel);
+ }
+
+ cache->last_update = now;
+ cache->serial_num = pdu->serial_num;
+ rpki_cache_change_state(cache, RPKI_CS_ESTABLISHED);
+}
+
+/**
+ * rpki_rx_packet - process a received RPKI PDU
+ * @cache: RPKI connection instance
+ * @pdu: a RPKI PDU in network byte order
+ */
+static void
+rpki_rx_packet(struct rpki_cache *cache, struct pdu_header *pdu)
+{
+ struct rpki_proto *p = cache->p;
+
+ if (rpki_check_receive_packet(cache, pdu) == RPKI_ERROR)
+ {
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_FATAL);
+ return;
+ }
+
+ rpki_pdu_to_host_byte_order(pdu);
+ rpki_log_packet(cache, pdu, RPKI_RECV);
+
+ switch (pdu->type)
+ {
+ case RESET_QUERY:
+ case SERIAL_QUERY:
+ RPKI_WARN(p, "Received a %s packet that is destined for cache server", str_pdu_type(pdu->type));
+ break;
+
+ case SERIAL_NOTIFY:
+ /* This is a signal to synchronize with the cache server just now */
+ rpki_handle_serial_notify_pdu(cache, (void *) pdu);
+ break;
+
+ case CACHE_RESPONSE:
+ rpki_handle_cache_response_pdu(cache, (void *) pdu);
+ break;
+
+ case IPV4_PREFIX:
+ case IPV6_PREFIX:
+ rpki_handle_prefix_pdu(cache, pdu);
+ break;
+
+ case END_OF_DATA:
+ rpki_handle_end_of_data_pdu(cache, (void *) pdu);
+ break;
+
+ case CACHE_RESET:
+ /* Cache cannot provide an incremental update. */
+ rpki_cache_change_state(cache, RPKI_CS_NO_INCR_UPDATE_AVAIL);
+ break;
+
+ case ERROR:
+ rpki_handle_error_pdu(cache, (void *) pdu);
+ break;
+
+ case ROUTER_KEY:
+ /* TODO: Implement Router Key PDU handling */
+ break;
+
+ default:
+ CACHE_TRACE(D_PACKETS, cache, "Received unsupported type (%u)", pdu->type);
+ };
+}
+
+int
+rpki_rx_hook(struct birdsock *sk, uint size)
+{
+ struct rpki_cache *cache = sk->data;
+ struct rpki_proto *p = cache->p;
+
+ byte *pkt_start = sk->rbuf;
+ byte *end = pkt_start + size;
+
+ DBG("rx hook got %u bytes \n", size);
+
+ while (end >= pkt_start + RPKI_PDU_HEADER_LEN)
+ {
+ struct pdu_header *pdu = (void *) pkt_start;
+ u32 pdu_size = ntohl(pdu->len);
+
+ if (pdu_size < RPKI_PDU_HEADER_LEN || pdu_size > RPKI_PDU_MAX_LEN)
+ {
+ RPKI_WARN(p, "Received invalid packet length %u, purge the whole receiving buffer", pdu_size);
+ return 1; /* Purge recv buffer */
+ }
+
+ if (end < pkt_start + pdu_size)
+ break;
+
+ rpki_rx_packet(cache, pdu);
+
+ /* It is possible that bird socket was freed/closed */
+ if (p->p.proto_state == PS_DOWN || sk != cache->tr_sock->sk)
+ return 0;
+
+ pkt_start += pdu_size;
+ }
+
+ if (pkt_start != sk->rbuf)
+ {
+ CACHE_DBG(cache, "Move %u bytes of a memory at the start of buffer", end - pkt_start);
+ memmove(sk->rbuf, pkt_start, end - pkt_start);
+ sk->rpos = sk->rbuf + (end - pkt_start);
+ }
+
+ return 0; /* Not purge sk->rbuf */
+}
+
+void
+rpki_err_hook(struct birdsock *sk, int error_num)
+{
+ struct rpki_cache *cache = sk->data;
+
+ if (error_num)
+ {
+ /* sk->err may contains a SSH error description */
+ if (sk->err)
+ CACHE_TRACE(D_EVENTS, cache, "Lost connection: %s", sk->err);
+ else
+ CACHE_TRACE(D_EVENTS, cache, "Lost connection: %M", error_num);
+ }
+ else
+ {
+ CACHE_TRACE(D_EVENTS, cache, "The other side closed a connection");
+ }
+
+
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
+}
+
+static int
+rpki_fire_tx(struct rpki_cache *cache)
+{
+ sock *sk = cache->tr_sock->sk;
+
+ uint bytes_to_send = sk->tpos - sk->tbuf;
+ DBG("Sending %u bytes", bytes_to_send);
+ return sk_send(sk, bytes_to_send);
+}
+
+void
+rpki_tx_hook(sock *sk)
+{
+ struct rpki_cache *cache = sk->data;
+
+ while (rpki_fire_tx(cache) > 0)
+ ;
+}
+
+void
+rpki_connected_hook(sock *sk)
+{
+ struct rpki_cache *cache = sk->data;
+
+ CACHE_TRACE(D_EVENTS, cache, "Connected");
+ proto_notify_state(&cache->p->p, PS_UP);
+
+ sk->rx_hook = rpki_rx_hook;
+ sk->tx_hook = rpki_tx_hook;
+
+ rpki_cache_change_state(cache, RPKI_CS_SYNC_START);
+}
+
+/**
+ * rpki_send_error_pdu - send RPKI Error PDU
+ * @cache: RPKI connection instance
+ * @error_code: PDU Error type
+ * @err_pdu_len: length of @erroneous_pdu
+ * @erroneous_pdu: optional network byte-order PDU that invokes Error by us or NULL
+ * @fmt: optional description text of error or NULL
+ * @args: optional arguments for @fmt
+ *
+ * This function prepares Error PDU and sends it to a cache server.
+ */
+static int
+rpki_send_error_pdu(struct rpki_cache *cache, const enum pdu_error_type error_code, const u32 err_pdu_len, const struct pdu_header *erroneous_pdu, const char *fmt, ...)
+{
+ va_list args;
+ char msg[128];
+
+ /* Size including the terminating null byte ('\0') */
+ int msg_len = 0;
+
+ /* Don't send errors for erroneous error PDUs */
+ if (err_pdu_len >= 2)
+ {
+ if (erroneous_pdu->type == ERROR)
+ return RPKI_SUCCESS;
+ }
+
+ if (fmt)
+ {
+ va_start(args, fmt);
+ msg_len = bvsnprintf(msg, sizeof(msg), fmt, args) + 1;
+ }
+
+ u32 pdu_size = 16 + err_pdu_len + msg_len;
+ byte pdu[pdu_size];
+ memset(pdu, 0, sizeof(pdu));
+
+ struct pdu_error *e = (void *) pdu;
+ e->ver = cache->version;
+ e->type = ERROR;
+ e->error_code = error_code;
+ e->len = pdu_size;
+
+ e->len_enc_pdu = err_pdu_len;
+ if (err_pdu_len > 0)
+ memcpy(e->rest, erroneous_pdu, err_pdu_len);
+
+ *((u32 *)(e->rest + err_pdu_len)) = msg_len;
+ if (msg_len > 0)
+ memcpy(e->rest + err_pdu_len + 4, msg, msg_len);
+
+ return rpki_send_pdu(cache, pdu, pdu_size);
+}
+
+int
+rpki_send_serial_query(struct rpki_cache *cache)
+{
+ struct pdu_serial_query pdu = {
+ .ver = cache->version,
+ .type = SERIAL_QUERY,
+ .session_id = cache->session_id,
+ .len = sizeof(pdu),
+ .serial_num = cache->serial_num
+ };
+
+ if (rpki_send_pdu(cache, &pdu, sizeof(pdu)) != RPKI_SUCCESS)
+ {
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
+ return RPKI_ERROR;
+ }
+
+ return RPKI_SUCCESS;
+}
+
+int
+rpki_send_reset_query(struct rpki_cache *cache)
+{
+ struct pdu_reset_query pdu = {
+ .ver = cache->version,
+ .type = RESET_QUERY,
+ .len = sizeof(pdu),
+ };
+
+ if (rpki_send_pdu(cache, &pdu, sizeof(pdu)) != RPKI_SUCCESS)
+ {
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
+ return RPKI_ERROR;
+ }
+
+ return RPKI_SUCCESS;
+}
diff --git a/proto/rpki/packets.h b/proto/rpki/packets.h
new file mode 100644
index 00000000..d6f8a249
--- /dev/null
+++ b/proto/rpki/packets.h
@@ -0,0 +1,45 @@
+/*
+ * BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
+ *
+ * (c) 2015 CZ.NIC
+ * (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
+ *
+ * This file was a part of RTRlib: http://rpki.realmv6.org/
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_RPKI_PACKETS_H_
+#define _BIRD_RPKI_PACKETS_H_
+
+#include <arpa/inet.h>
+
+#define RPKI_PDU_HEADER_LEN 8
+
+/* A Error PDU size is the biggest (has encapsulate PDU inside):
+ * +8 bytes (Header size)
+ * +4 bytes (Length of Encapsulated PDU)
+ * +32 bytes (Encapsulated PDU IPv6 32)
+ * +4 bytes (Length of inserted text)
+ * +800 bytes (UTF-8 text 400*2 bytes)
+ * ------------
+ * = 848 bytes (Maximal expected PDU size) */
+#define RPKI_PDU_MAX_LEN 848
+
+/* RX buffer size has a great impact to scheduler granularity */
+#define RPKI_RX_BUFFER_SIZE 4096
+#define RPKI_TX_BUFFER_SIZE RPKI_PDU_MAX_LEN
+
+/* Return values */
+enum rpki_rtvals {
+ RPKI_SUCCESS = 0,
+ RPKI_ERROR = -1
+};
+
+int rpki_send_serial_query(struct rpki_cache *cache);
+int rpki_send_reset_query(struct rpki_cache *cache);
+int rpki_rx_hook(sock *sk, uint size);
+void rpki_connected_hook(sock *sk);
+void rpki_err_hook(sock *sk, int size);
+
+#endif
diff --git a/proto/rpki/rpki.c b/proto/rpki/rpki.c
new file mode 100644
index 00000000..349d2f70
--- /dev/null
+++ b/proto/rpki/rpki.c
@@ -0,0 +1,926 @@
+/*
+ * BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
+ *
+ * (c) 2015 CZ.NIC
+ * (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
+ *
+ * Using RTRlib: http://rpki.realmv6.org/
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/**
+ * DOC: RPKI To Router (RPKI-RTR)
+ *
+ * The RPKI-RTR protocol is implemented in several files: |rpki.c| containing
+ * the routes handling, protocol logic, timer events, cache connection,
+ * reconfiguration, configuration and protocol glue with BIRD core, |packets.c|
+ * containing the RPKI packets handling and finally all transports files:
+ * |transport.c|, |tcp_transport.c| and |ssh_transport.c|.
+ *
+ * The |transport.c| is a middle layer and interface for each specific
+ * transport. Transport is a way how to wrap a communication with a cache
+ * server. There is supported an unprotected TCP transport and an encrypted
+ * SSHv2 transport. The SSH transport requires LibSSH library. LibSSH is
+ * loading dynamically using |dlopen()| function. SSH support is integrated in
+ * |sysdep/unix/io.c|. Each transport must implement an initialization
+ * function, an open function and a socket identification function. That's all.
+ *
+ * This implementation is based on the RTRlib (http://rpki.realmv6.org/). The
+ * BIRD takes over files |packets.c|, |rtr.c| (inside |rpki.c|), |transport.c|,
+ * |tcp_transport.c| and |ssh_transport.c| from RTRlib.
+ *
+ * A RPKI-RTR connection is described by a structure &rpki_cache. The main
+ * logic is located in |rpki_cache_change_state()| function. There is a state
+ * machine. The standard starting state flow looks like |Down| ~> |Connecting|
+ * ~> |Sync-Start| ~> |Sync-Running| ~> |Established| and then the last three
+ * states are periodically repeated.
+ *
+ * |Connecting| state establishes the transport connection. The state from a
+ * call |rpki_cache_change_state(CONNECTING)| to a call |rpki_connected_hook()|
+ *
+ * |Sync-Start| state starts with sending |Reset Query| or |Serial Query| and
+ * then waits for |Cache Response|. The state from |rpki_connected_hook()| to
+ * |rpki_handle_cache_response_pdu()|
+ *
+ * During |Sync-Running| BIRD receives data with IPv4/IPv6 Prefixes from cache
+ * server. The state starts from |rpki_handle_cache_response_pdu()| and ends
+ * in |rpki_handle_end_of_data_pdu()|.
+ *
+ * |Established| state means that BIRD has synced all data with cache server.
+ * Schedules a refresh timer event that invokes |Sync-Start|. Schedules Expire
+ * timer event and stops a Retry timer event.
+ *
+ * |Transport Error| state means that we have some troubles with a network
+ * connection. We cannot connect to a cache server or we wait too long for some
+ * expected PDU for received - |Cache Response| or |End of Data|. It closes
+ * current connection and schedules a Retry timer event.
+ *
+ * |Fatal Protocol Error| is occurred e.g. by received a bad Session ID. We
+ * restart a protocol, so all ROAs are flushed immediately.
+ *
+ * The RPKI-RTR protocol (RFC 6810 bis) defines configurable refresh, retry and
+ * expire intervals. For maintaining a connection are used timer events that
+ * are scheduled by |rpki_schedule_next_refresh()|,
+ * |rpki_schedule_next_retry()| and |rpki_schedule_next_expire()| functions.
+ *
+ * A Refresh timer event performs a sync of |Established| connection. So it
+ * shifts state to |Sync-Start|. If at the beginning of second call of a
+ * refresh event is connection in |Sync-Start| state then we didn't receive a
+ * |Cache Response| from a cache server and we invoke |Transport Error| state.
+ *
+ * A Retry timer event attempts to connect cache server. It is activated after
+ * |Transport Error| state and terminated by reaching |Established| state.
+ * If cache connection is still connecting to the cache server at the beginning
+ * of an event call then the Retry timer event invokes |Transport Error| state.
+ *
+ * An Expire timer event checks expiration of ROAs. If a last successful sync
+ * was more ago than the expire interval then the Expire timer event invokes a
+ * protocol restart thereby removes all ROAs learned from that cache server and
+ * continue trying to connect to cache server. The Expire event is activated
+ * by initial successful loading of ROAs, receiving End of Data PDU.
+ *
+ * A reconfiguration of cache connection works well without restarting when we
+ * change only intervals values.
+ *
+ * Supported standards:
+ * - RFC 6810 - main RPKI-RTR standard
+ * - RFC 6810 bis - an explicit timing parameters and protocol version number negotiation
+ */
+
+#include <stdlib.h>
+#include <netdb.h>
+
+#undef LOCAL_DEBUG
+
+#include "rpki.h"
+#include "lib/string.h"
+#include "nest/cli.h"
+
+/* Return values for reconfiguration functions */
+#define NEED_RESTART 0
+#define SUCCESSFUL_RECONF 1
+
+static int rpki_open_connection(struct rpki_cache *cache);
+static void rpki_close_connection(struct rpki_cache *cache);
+static void rpki_schedule_next_refresh(struct rpki_cache *cache);
+static void rpki_schedule_next_retry(struct rpki_cache *cache);
+static void rpki_schedule_next_expire_check(struct rpki_cache *cache);
+static void rpki_stop_refresh_timer_event(struct rpki_cache *cache);
+static void rpki_stop_retry_timer_event(struct rpki_cache *cache);
+static void rpki_stop_expire_timer_event(struct rpki_cache *cache);
+
+
+/*
+ * Routes handling
+ */
+
+void
+rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr)
+{
+ struct rpki_proto *p = cache->p;
+
+ rta a0 = {
+ .src = p->p.main_source,
+ .source = RTS_RPKI,
+ .scope = SCOPE_UNIVERSE,
+ .dest = RTD_NONE,
+ };
+
+ rta *a = rta_lookup(&a0);
+ rte *e = rte_get_temp(a);
+
+ e->pflags = 0;
+
+ rte_update2(channel, &pfxr->n, e, a0.src);
+}
+
+void
+rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr)
+{
+ struct rpki_proto *p = cache->p;
+ rte_update2(channel, &pfxr->n, NULL, p->p.main_source);
+}
+
+
+/*
+ * RPKI Protocol Logic
+ */
+
+static const char *str_cache_states[] = {
+ [RPKI_CS_CONNECTING] = "Connecting",
+ [RPKI_CS_ESTABLISHED] = "Established",
+ [RPKI_CS_RESET] = "Reseting",
+ [RPKI_CS_SYNC_START] = "Sync-Start",
+ [RPKI_CS_SYNC_RUNNING] = "Sync-Running",
+ [RPKI_CS_FAST_RECONNECT] = "Fast-Reconnect",
+ [RPKI_CS_NO_INCR_UPDATE_AVAIL]= "No-Increment-Update-Available",
+ [RPKI_CS_ERROR_NO_DATA_AVAIL] = "Cache-Error-No-Data-Available",
+ [RPKI_CS_ERROR_FATAL] = "Fatal-Protocol-Error",
+ [RPKI_CS_ERROR_TRANSPORT] = "Transport-Error",
+ [RPKI_CS_SHUTDOWN] = "Down"
+};
+
+/**
+ * rpki_cache_state_to_str - give a text representation of cache state
+ * @state: A cache state
+ *
+ * The function converts logic cache state into string.
+ */
+const char *
+rpki_cache_state_to_str(enum rpki_cache_state state)
+{
+ return str_cache_states[state];
+}
+
+/**
+ * rpki_start_cache - connect to a cache server
+ * @cache: RPKI connection instance
+ *
+ * This function is a high level method to kick up a connection to a cache server.
+ */
+static void
+rpki_start_cache(struct rpki_cache *cache)
+{
+ rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
+}
+
+/**
+ * rpki_force_restart_proto - force shutdown and start protocol again
+ * @p: RPKI protocol instance
+ *
+ * This function calls shutdown and frees all protocol resources as well.
+ * After calling this function should be no operations with protocol data,
+ * they could be freed already.
+ */
+static void
+rpki_force_restart_proto(struct rpki_proto *p)
+{
+ if (p->cache)
+ {
+ CACHE_DBG(p->cache, "Connection object destroying");
+ }
+
+ /* Sign as freed */
+ p->cache = NULL;
+
+ proto_notify_state(&p->p, PS_DOWN);
+}
+
+/**
+ * rpki_cache_change_state - check and change cache state
+ * @cache: RPKI cache instance
+ * @new_state: suggested new state
+ *
+ * This function makes transitions between internal states.
+ * It represents the core of logic management of RPKI protocol.
+ * Cannot transit into the same state as cache is in already.
+ */
+void
+rpki_cache_change_state(struct rpki_cache *cache, const enum rpki_cache_state new_state)
+{
+ const enum rpki_cache_state old_state = cache->state;
+
+ if (old_state == new_state)
+ return;
+
+ cache->state = new_state;
+ CACHE_TRACE(D_EVENTS, cache, "Changing from %s to %s state", rpki_cache_state_to_str(old_state), rpki_cache_state_to_str(new_state));
+
+ switch (new_state)
+ {
+ case RPKI_CS_CONNECTING:
+ {
+ sock *sk = cache->tr_sock->sk;
+
+ if (sk == NULL || sk->fd < 0)
+ rpki_open_connection(cache);
+ else
+ rpki_cache_change_state(cache, RPKI_CS_SYNC_START);
+
+ rpki_schedule_next_retry(cache);
+ break;
+ }
+
+ case RPKI_CS_ESTABLISHED:
+ rpki_schedule_next_refresh(cache);
+ rpki_schedule_next_expire_check(cache);
+ rpki_stop_retry_timer_event(cache);
+ break;
+
+ case RPKI_CS_RESET:
+ /* Resetting cache connection. */
+ cache->request_session_id = 1;
+ cache->serial_num = 0;
+ rpki_cache_change_state(cache, RPKI_CS_SYNC_START);
+ break;
+
+ case RPKI_CS_SYNC_START:
+ /* Requesting for receive ROAs from a cache server. */
+ if (cache->request_session_id)
+ {
+ /* Send request for Session ID */
+ if (rpki_send_reset_query(cache) != RPKI_SUCCESS)
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
+ }
+ else
+ {
+ /* We have already a session_id. So send a Serial Query and start an incremental sync */
+ if (rpki_send_serial_query(cache) != RPKI_SUCCESS)
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
+ }
+ break;
+
+ case RPKI_CS_SYNC_RUNNING:
+ /* The state between Cache Response and End of Data. Only waiting for
+ * receiving all IP Prefix PDUs and finally a End of Data PDU. */
+ break;
+
+ case RPKI_CS_NO_INCR_UPDATE_AVAIL:
+ /* Server was unable to answer the last Serial Query and sent Cache Reset. */
+ rpki_cache_change_state(cache, RPKI_CS_RESET);
+ break;
+
+ case RPKI_CS_ERROR_NO_DATA_AVAIL:
+ /* No validation records are available on the cache server. */
+ rpki_cache_change_state(cache, RPKI_CS_RESET);
+ break;
+
+ case RPKI_CS_ERROR_FATAL:
+ /* Fatal protocol error occurred. */
+ rpki_force_restart_proto(cache->p);
+ break;
+
+ case RPKI_CS_ERROR_TRANSPORT:
+ /* Error on the transport socket occurred. */
+ rpki_close_connection(cache);
+ rpki_schedule_next_retry(cache);
+ rpki_stop_refresh_timer_event(cache);
+ break;
+
+ case RPKI_CS_FAST_RECONNECT:
+ /* Reconnect without any waiting period */
+ rpki_close_connection(cache);
+ rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
+ break;
+
+ case RPKI_CS_SHUTDOWN:
+ bug("This isn't never really called.");
+ break;
+ };
+}
+
+
+/*
+ * RPKI Timer Events
+ */
+
+static void
+rpki_schedule_next_refresh(struct rpki_cache *cache)
+{
+ uint time_to_wait = cache->refresh_interval;
+
+ CACHE_DBG(cache, "after %u seconds", time_to_wait);
+ tm_start(cache->refresh_timer, time_to_wait);
+}
+
+static void
+rpki_schedule_next_retry(struct rpki_cache *cache)
+{
+ uint time_to_wait = cache->retry_interval;
+
+ CACHE_DBG(cache, "after %u seconds", time_to_wait);
+ tm_start(cache->retry_timer, time_to_wait);
+}
+
+static void
+rpki_schedule_next_expire_check(struct rpki_cache *cache)
+{
+ /* A minimum time to wait is 1 second */
+ uint time_to_wait = MAX(((int)cache->expire_interval - (int)(now - cache->last_update)), 1);
+
+ CACHE_DBG(cache, "after %u seconds", time_to_wait);
+ tm_start(cache->expire_timer, time_to_wait);
+}
+
+static void
+rpki_stop_refresh_timer_event(struct rpki_cache *cache)
+{
+ CACHE_DBG(cache, "Stop");
+ tm_stop(cache->refresh_timer);
+}
+
+static void
+rpki_stop_retry_timer_event(struct rpki_cache *cache)
+{
+ CACHE_DBG(cache, "Stop");
+ tm_stop(cache->retry_timer);
+}
+
+static void UNUSED
+rpki_stop_expire_timer_event(struct rpki_cache *cache)
+{
+ CACHE_DBG(cache, "Stop");
+ tm_stop(cache->expire_timer);
+}
+
+static int
+rpki_do_we_recv_prefix_pdu_in_last_seconds(struct rpki_cache *cache)
+{
+ if (cache->last_rx_prefix == 0)
+ return 0;
+
+ return ((now - cache->last_rx_prefix) <= 2);
+}
+
+/**
+ * rpki_refresh_hook - control a scheduling of downloading data from cache server
+ * @tm: refresh timer with cache connection instance in data
+ *
+ * This function is periodically called during &ESTABLISHED or &SYNC* state
+ * cache connection. The first refresh schedule is invoked after receiving a
+ * |End of Data| PDU and has run by some &ERROR is occurred.
+ */
+static void
+rpki_refresh_hook(struct timer *tm)
+{
+ struct rpki_cache *cache = tm->data;
+
+ CACHE_DBG(cache, "%s", rpki_cache_state_to_str(cache->state));
+
+ switch (cache->state)
+ {
+ case RPKI_CS_ESTABLISHED:
+ rpki_cache_change_state(cache, RPKI_CS_SYNC_START);
+ break;
+
+ case RPKI_CS_SYNC_START:
+ /* We sent Serial/Reset Query in last refresh hook call
+ * and didn't receive Cache Response yet. It is probably
+ * troubles with network. */
+ case RPKI_CS_SYNC_RUNNING:
+ /* We sent Serial/Reset Query in last refresh hook call
+ * and we got Cache Response but didn't get End-Of-Data yet.
+ * It could be a trouble with network or only too long synchronization. */
+ if (!rpki_do_we_recv_prefix_pdu_in_last_seconds(cache))
+ {
+ CACHE_TRACE(D_EVENTS, cache, "Sync takes more time than refresh interval %us, resetting connection", cache->refresh_interval);
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (cache->state != RPKI_CS_SHUTDOWN && cache->state != RPKI_CS_ERROR_TRANSPORT)
+ rpki_schedule_next_refresh(cache);
+ else
+ rpki_stop_refresh_timer_event(cache);
+}
+
+/**
+ * rpki_retry_hook - control a scheduling of retrying connection to cache server
+ * @tm: retry timer with cache connection instance in data
+ *
+ * This function is periodically called during &ERROR* state cache connection.
+ * The first retry schedule is invoked after any &ERROR* state occurred and
+ * ends by reaching of &ESTABLISHED state again.
+ */
+static void
+rpki_retry_hook(struct timer *tm)
+{
+ struct rpki_cache *cache = tm->data;
+
+ CACHE_DBG(cache, "%s", rpki_cache_state_to_str(cache->state));
+
+ switch (cache->state)
+ {
+ case RPKI_CS_ESTABLISHED:
+ case RPKI_CS_SHUTDOWN:
+ break;
+
+ case RPKI_CS_CONNECTING:
+ case RPKI_CS_SYNC_START:
+ case RPKI_CS_SYNC_RUNNING:
+ if (!rpki_do_we_recv_prefix_pdu_in_last_seconds(cache))
+ {
+ /* We tried to establish a connection in last retry hook call and haven't done
+ * yet. It looks like troubles with network. We are aggressive here. */
+ CACHE_TRACE(D_EVENTS, cache, "Sync takes more time than retry interval %us, resetting connection.", cache->retry_interval);
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
+ }
+ break;
+
+ default:
+ rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
+ break;
+ }
+
+ if (cache->state != RPKI_CS_ESTABLISHED)
+ rpki_schedule_next_retry(cache);
+ else
+ rpki_stop_retry_timer_event(cache);
+}
+
+/**
+ * rpki_expire_hook - control a expiration of ROA entries
+ * @tm: expire timer with cache connection instance in data
+ *
+ * This function is scheduled after received a |End of Data| PDU.
+ * A waiting interval is calculated dynamically by last update.
+ * If we reach an expiration time then we invoke a restarting
+ * of the protocol.
+ */
+static void
+rpki_expire_hook(struct timer *tm)
+{
+ struct rpki_cache *cache = tm->data;
+
+ if (cache->last_update == 0)
+ return;
+
+ CACHE_DBG(cache, "%s", rpki_cache_state_to_str(cache->state));
+
+ if ((cache->last_update + cache->expire_interval) < now)
+ {
+ CACHE_TRACE(D_EVENTS, cache, "All ROAs expired");
+ rpki_force_restart_proto(cache->p);
+ }
+ else
+ {
+ CACHE_DBG(cache, "Remains %d seconds to become ROAs obsolete", (int)cache->expire_interval - (int)(now - cache->last_update));
+ rpki_schedule_next_expire_check(cache);
+ }
+}
+
+/**
+ * rpki_check_refresh_interval - check validity of refresh interval value
+ * @seconds: suggested value
+ *
+ * This function validates value and should return |NULL|.
+ * If the check doesn't pass then returns error message.
+ */
+const char *
+rpki_check_refresh_interval(uint seconds)
+{
+ if (seconds < 1)
+ return "Minimum allowed refresh interval is 1 second";
+ if (seconds > 86400)
+ return "Maximum allowed refresh interval is 86400 seconds";
+ return NULL;
+}
+
+/**
+ * rpki_check_retry_interval - check validity of retry interval value
+ * @seconds: suggested value
+ *
+ * This function validates value and should return |NULL|.
+ * If the check doesn't pass then returns error message.
+ */
+const char *
+rpki_check_retry_interval(uint seconds)
+{
+ if (seconds < 1)
+ return "Minimum allowed retry interval is 1 second";
+ if (seconds > 7200)
+ return "Maximum allowed retry interval is 7200 seconds";
+ return NULL;
+}
+
+/**
+ * rpki_check_expire_interval - check validity of expire interval value
+ * @seconds: suggested value
+ *
+ * This function validates value and should return |NULL|.
+ * If the check doesn't pass then returns error message.
+ */
+const char *
+rpki_check_expire_interval(uint seconds)
+{
+ if (seconds < 600)
+ return "Minimum allowed expire interval is 600 seconds";
+ if (seconds > 172800)
+ return "Maximum allowed expire interval is 172800 seconds";
+ return NULL;
+}
+
+
+/*
+ * RPKI Cache
+ */
+
+static struct rpki_cache *
+rpki_init_cache(struct rpki_proto *p, struct rpki_config *cf)
+{
+ pool *pool = rp_new(p->p.pool, cf->hostname);
+
+ struct rpki_cache *cache = mb_allocz(pool, sizeof(struct rpki_cache));
+
+ cache->pool = pool;
+ cache->p = p;
+
+ cache->state = RPKI_CS_SHUTDOWN;
+ cache->request_session_id = 1;
+ cache->version = RPKI_MAX_VERSION;
+
+ cache->refresh_interval = cf->refresh_interval;
+ cache->retry_interval = cf->retry_interval;
+ cache->expire_interval = cf->expire_interval;
+ cache->refresh_timer = tm_new_set(pool, &rpki_refresh_hook, cache, 0, 0);
+ cache->retry_timer = tm_new_set(pool, &rpki_retry_hook, cache, 0, 0);
+ cache->expire_timer = tm_new_set(pool, &rpki_expire_hook, cache, 0, 0);
+
+ cache->tr_sock = mb_allocz(pool, sizeof(struct rpki_tr_sock));
+ cache->tr_sock->cache = cache;
+
+ switch (cf->tr_config.type)
+ {
+ case RPKI_TR_TCP: rpki_tr_tcp_init(cache->tr_sock); break;
+ case RPKI_TR_SSH: rpki_tr_ssh_init(cache->tr_sock); break;
+ };
+
+ CACHE_DBG(cache, "Connection object created");
+
+ return cache;
+}
+
+/**
+ * rpki_get_cache_ident - give a text representation of cache server name
+ * @cache: RPKI connection instance
+ *
+ * The function converts cache connection into string.
+ */
+const char *
+rpki_get_cache_ident(struct rpki_cache *cache)
+{
+ return rpki_tr_ident(cache->tr_sock);
+}
+
+static int
+rpki_open_connection(struct rpki_cache *cache)
+{
+ CACHE_TRACE(D_EVENTS, cache, "Opening a connection");
+
+ if (rpki_tr_open(cache->tr_sock) == RPKI_TR_ERROR)
+ {
+ rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
+ return RPKI_TR_ERROR;
+ }
+
+ return RPKI_TR_SUCCESS;
+}
+
+static void
+rpki_close_connection(struct rpki_cache *cache)
+{
+ CACHE_TRACE(D_EVENTS, cache, "Closing a connection");
+ rpki_tr_close(cache->tr_sock);
+ proto_notify_state(&cache->p->p, PS_START);
+}
+
+static int
+rpki_shutdown(struct proto *P)
+{
+ struct rpki_proto *p = (void *) P;
+
+ rpki_force_restart_proto(p);
+
+ /* Protocol memory pool will be automatically freed */
+ return PS_DOWN;
+}
+
+
+/*
+ * RPKI Reconfiguration
+ */
+
+static int
+rpki_try_fast_reconnect(struct rpki_cache *cache)
+{
+ if (cache->state == RPKI_CS_ESTABLISHED)
+ {
+ rpki_cache_change_state(cache, RPKI_CS_FAST_RECONNECT);
+ return SUCCESSFUL_RECONF;
+ }
+
+ return NEED_RESTART;
+}
+
+/**
+ * rpki_reconfigure_cache - a cache reconfiguration
+ * @p: RPKI protocol instance
+ * @cache: a cache connection
+ * @new: new RPKI configuration
+ * @old: old RPKI configuration
+ *
+ * This function reconfigures existing single cache server connection with new
+ * existing configuration. Generally, a change of time intervals could be
+ * reconfigured without restarting and all others changes requires a restart of
+ * protocol. Returns |NEED_TO_RESTART| or |SUCCESSFUL_RECONF|.
+ */
+static int
+rpki_reconfigure_cache(struct rpki_proto *p UNUSED, struct rpki_cache *cache, struct rpki_config *new, struct rpki_config *old)
+{
+ u8 try_fast_reconnect = 0;
+
+ if (strcmp(old->hostname, new->hostname) != 0)
+ {
+ CACHE_TRACE(D_EVENTS, cache, "Cache server address changed to %s", new->hostname);
+ return NEED_RESTART;
+ }
+
+ if (old->port != new->port)
+ {
+ CACHE_TRACE(D_EVENTS, cache, "Cache server port changed to %u", new->port);
+ return NEED_RESTART;
+ }
+
+ if (old->tr_config.type != new->tr_config.type)
+ {
+ CACHE_TRACE(D_EVENTS, cache, "Transport type changed");
+ return NEED_RESTART;
+ }
+ else if (new->tr_config.type == RPKI_TR_SSH)
+ {
+ struct rpki_tr_ssh_config *ssh_old = (void *) old->tr_config.spec;
+ struct rpki_tr_ssh_config *ssh_new = (void *) new->tr_config.spec;
+ if ((strcmp(ssh_old->bird_private_key, ssh_new->bird_private_key) != 0) ||
+ (strcmp(ssh_old->cache_public_key, ssh_new->cache_public_key) != 0) ||
+ (strcmp(ssh_old->user, ssh_new->user) != 0))
+ {
+ CACHE_TRACE(D_EVENTS, cache, "Settings of SSH transport configuration changed");
+ try_fast_reconnect = 1;
+ }
+ }
+
+#define TEST_INTERVAL(name, Name) \
+ if (cache->name##_interval != new->name##_interval || \
+ old->keep_##name##_interval != new->keep_##name##_interval) \
+ { \
+ cache->name##_interval = new->name##_interval; \
+ CACHE_TRACE(D_EVENTS, cache, #Name " interval changed to %u seconds %s", cache->name##_interval, (new->keep_##name##_interval ? "and keep it" : "")); \
+ try_fast_reconnect = 1; \
+ }
+ TEST_INTERVAL(refresh, Refresh);
+ TEST_INTERVAL(retry, Retry);
+ TEST_INTERVAL(expire, Expire);
+#undef TEST_INTERVAL
+
+ if (try_fast_reconnect)
+ return rpki_try_fast_reconnect(cache);
+
+ return SUCCESSFUL_RECONF;
+}
+
+/**
+ * rpki_reconfigure - a protocol reconfiguration hook
+ * @P: a protocol instance
+ * @CF: a new protocol configuration
+ *
+ * This function reconfigures whole protocol.
+ * It sets new protocol configuration into a protocol structure.
+ * Returns |NEED_TO_RESTART| or |SUCCESSFUL_RECONF|.
+ */
+static int
+rpki_reconfigure(struct proto *P, struct proto_config *CF)
+{
+ struct rpki_proto *p = (void *) P;
+ struct rpki_config *new = (void *) CF;
+ struct rpki_config *old = (void *) p->p.cf;
+ struct rpki_cache *cache = p->cache;
+
+ if (!proto_configure_channel(&p->p, &p->roa4_channel, proto_cf_find_channel(CF, NET_ROA4)) ||
+ !proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6)))
+ return NEED_RESTART;
+
+ if (rpki_reconfigure_cache(p, cache, new, old) != SUCCESSFUL_RECONF)
+ return NEED_RESTART;
+
+ return SUCCESSFUL_RECONF;
+}
+
+
+/*
+ * RPKI Protocol Glue
+ */
+
+static struct proto *
+rpki_init(struct proto_config *CF)
+{
+ struct proto *P = proto_new(CF);
+ struct rpki_proto *p = (void *) P;
+
+ proto_configure_channel(&p->p, &p->roa4_channel, proto_cf_find_channel(CF, NET_ROA4));
+ proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6));
+
+ return P;
+}
+
+static int
+rpki_start(struct proto *P)
+{
+ struct rpki_proto *p = (void *) P;
+ struct rpki_config *cf = (void *) P->cf;
+
+ p->cache = rpki_init_cache(p, cf);
+ rpki_start_cache(p->cache);
+
+ return PS_START;
+}
+
+static void
+rpki_get_status(struct proto *P, byte *buf)
+{
+ struct rpki_proto *p = (struct rpki_proto *) P;
+
+ if (P->proto_state == PS_DOWN)
+ {
+ *buf = 0;
+ return;
+ }
+
+ if (p->cache)
+ bsprintf(buf, "%s", rpki_cache_state_to_str(p->cache->state));
+ else
+ bsprintf(buf, "No cache server configured");
+}
+
+static void
+rpki_show_proto_info_timer(const char *name, uint num, timer *t)
+{
+ if (t->expires)
+ cli_msg(-1006, " %-17s %us (remains %us)", name, num, tm_remains(t));
+ else
+ cli_msg(-1006, " %-17s ---", name);
+}
+
+static void
+rpki_show_proto_info(struct proto *P)
+{
+ struct rpki_proto *p = (struct rpki_proto *) P;
+ struct rpki_config *cf = (void *) p->p.cf;
+ struct rpki_cache *cache = p->cache;
+
+ if (P->proto_state == PS_DOWN)
+ return;
+
+ if (cache)
+ {
+ const char *transport_name = "---";
+
+ switch (cf->tr_config.type)
+ {
+ case RPKI_TR_SSH: transport_name = "SSHv2"; break;
+ case RPKI_TR_TCP: transport_name = "Unprotected over TCP"; break;
+ };
+
+ cli_msg(-1006, " Cache server: %s", rpki_get_cache_ident(cache));
+ cli_msg(-1006, " Status: %s", rpki_cache_state_to_str(cache->state));
+ cli_msg(-1006, " Transport: %s", transport_name);
+ cli_msg(-1006, " Protocol version: %u", cache->version);
+
+ if (cache->request_session_id)
+ cli_msg(-1006, " Session ID: ---");
+ else
+ cli_msg(-1006, " Session ID: %u", cache->session_id);
+
+ if (cache->last_update)
+ {
+ cli_msg(-1006, " Serial number: %u", cache->serial_num);
+ cli_msg(-1006, " Last update: before %us", now - cache->last_update);
+ }
+ else
+ {
+ cli_msg(-1006, " Serial number: ---");
+ cli_msg(-1006, " Last update: ---");
+ }
+
+ rpki_show_proto_info_timer("Refresh interval:", cache->refresh_interval, cache->refresh_timer);
+ rpki_show_proto_info_timer("Retry interval:", cache->retry_interval, cache->retry_timer);
+ rpki_show_proto_info_timer("Expire interval:", cache->expire_interval, cache->expire_timer);
+
+ if (p->roa4_channel)
+ channel_show_info(p->roa4_channel);
+ else
+ cli_msg(-1006, " No roa4 channel");
+
+ if (p->roa6_channel)
+ channel_show_info(p->roa6_channel);
+ else
+ cli_msg(-1006, " No roa6 channel");
+ }
+}
+
+
+/*
+ * RPKI Protocol Configuration
+ */
+
+/**
+ * rpki_check_config - check and complete configuration of RPKI protocol
+ * @cf: RPKI configuration
+ *
+ * This function is called at the end of parsing RPKI protocol configuration.
+ */
+void
+rpki_check_config(struct rpki_config *cf)
+{
+ /* Do not check templates at all */
+ if (cf->c.class == SYM_TEMPLATE)
+ return;
+
+ if (ipa_zero(cf->ip) && cf->hostname == NULL)
+ cf_error("IP address or hostname of cache server must be set");
+
+ /* Set default transport type */
+ if (cf->tr_config.spec == NULL)
+ {
+ cf->tr_config.spec = cfg_allocz(sizeof(struct rpki_tr_tcp_config));
+ cf->tr_config.type = RPKI_TR_TCP;
+ }
+
+ if (cf->port == 0)
+ {
+ /* Set default port numbers */
+ switch (cf->tr_config.type)
+ {
+ case RPKI_TR_SSH:
+ cf->port = RPKI_SSH_PORT;
+ break;
+ default:
+ cf->port = RPKI_TCP_PORT;
+ }
+ }
+}
+
+static void
+rpki_postconfig(struct proto_config *CF)
+{
+ /* Define default channel */
+ if (EMPTY_LIST(CF->channels))
+ channel_config_new(NULL, CF->net_type, CF);
+}
+
+static void
+rpki_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUSED)
+{
+ /* FIXME: Should copy transport */
+}
+
+struct protocol proto_rpki = {
+ .name = "RPKI",
+ .template = "rpki%d",
+ .preference = DEF_PREF_RPKI,
+ .proto_size = sizeof(struct rpki_proto),
+ .config_size = sizeof(struct rpki_config),
+ .init = rpki_init,
+ .start = rpki_start,
+ .postconfig = rpki_postconfig,
+ .channel_mask = (NB_ROA4 | NB_ROA6),
+ .show_proto_info = rpki_show_proto_info,
+ .shutdown = rpki_shutdown,
+ .copy_config = rpki_copy_config,
+ .reconfigure = rpki_reconfigure,
+ .get_status = rpki_get_status,
+};
diff --git a/proto/rpki/rpki.h b/proto/rpki/rpki.h
new file mode 100644
index 00000000..80ef83df
--- /dev/null
+++ b/proto/rpki/rpki.h
@@ -0,0 +1,165 @@
+/*
+ * BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
+ *
+ * (c) 2015 CZ.NIC
+ * (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
+ *
+ * Using RTRlib: http://rpki.realmv6.org/
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_RPKI_H_
+#define _BIRD_RPKI_H_
+
+#include "nest/bird.h"
+#include "nest/route.h"
+#include "nest/protocol.h"
+#include "lib/socket.h"
+#include "lib/ip.h"
+
+#include "transport.h"
+#include "packets.h"
+
+#define RPKI_TCP_PORT 323
+#define RPKI_SSH_PORT 22
+#define RPKI_RETRY_INTERVAL 600
+#define RPKI_REFRESH_INTERVAL 3600
+#define RPKI_EXPIRE_INTERVAL 7200
+
+#define RPKI_VERSION_0 0
+#define RPKI_VERSION_1 1
+#define RPKI_MAX_VERSION RPKI_VERSION_1
+
+
+/*
+ * RPKI Cache
+ */
+
+enum rpki_cache_state {
+ RPKI_CS_CONNECTING, /* Socket is establishing the transport connection. */
+ RPKI_CS_ESTABLISHED, /* Connection is established, socket is waiting for a Serial Notify or expiration of the refresh_interval timer */
+ RPKI_CS_RESET, /* Resetting RTR connection. */
+ RPKI_CS_SYNC_START, /* Sending a Serial/Reset Query PDU and expecting a Cache Response PDU */
+ RPKI_CS_SYNC_RUNNING, /* Receiving validation records from the RTR server. A state between Cache Response PDU and End of Data PDU */
+ RPKI_CS_FAST_RECONNECT, /* Reconnect without any waiting period */
+ RPKI_CS_NO_INCR_UPDATE_AVAIL, /* Server is unable to answer the last Serial Query and sent Cache Reset. */
+ RPKI_CS_ERROR_NO_DATA_AVAIL, /* Server is unable to answer either a Serial Query or a Reset Query because it has no useful data available at this time. */
+ RPKI_CS_ERROR_FATAL, /* Fatal protocol error occurred. */
+ RPKI_CS_ERROR_TRANSPORT, /* Error on the transport socket occurred. */
+ RPKI_CS_SHUTDOWN, /* RTR Socket is stopped. */
+};
+
+struct rpki_cache {
+ pool *pool; /* Pool containing cache objects */
+ struct rpki_proto *p;
+
+ struct rpki_tr_sock *tr_sock; /* Transport specific socket */
+ enum rpki_cache_state state; /* RPKI_CS_* */
+ u32 session_id;
+ u8 request_session_id; /* 1: have to request new session id; 0: we have already received session id */
+ u32 serial_num; /* Serial number denotes the logical version of data from cache server */
+ u8 version; /* Protocol version */
+ bird_clock_t last_update; /* Last successful synchronization with cache server */
+ bird_clock_t last_rx_prefix; /* Last received prefix PDU */
+
+ /* Intervals can be changed by cache server on the fly */
+ u32 refresh_interval; /* Actual refresh interval */
+ u32 retry_interval;
+ u32 expire_interval;
+ timer *retry_timer; /* Retry timer event */
+ timer *refresh_timer; /* Refresh timer event */
+ timer *expire_timer; /* Expire timer event */
+};
+
+const char *rpki_get_cache_ident(struct rpki_cache *cache);
+const char *rpki_cache_state_to_str(enum rpki_cache_state state);
+
+
+/*
+ * Routes handling
+ */
+
+void rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr);
+void rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr);
+
+
+/*
+ * RPKI Protocol Logic
+ */
+
+void rpki_cache_change_state(struct rpki_cache *cache, const enum rpki_cache_state new_state);
+
+
+/*
+ * RPKI Timer Events
+ */
+
+const char *rpki_check_refresh_interval(uint seconds);
+const char *rpki_check_retry_interval(uint seconds);
+const char *rpki_check_expire_interval(uint seconds);
+
+
+/*
+ * RPKI Protocol Configuration
+ */
+
+struct rpki_proto {
+ struct proto p;
+ struct rpki_cache *cache;
+
+ struct channel *roa4_channel;
+ struct channel *roa6_channel;
+ u8 refresh_channels; /* For non-incremental updates using rt_refresh_begin(), rt_refresh_end() */
+};
+
+struct rpki_config {
+ struct proto_config c;
+ const char *hostname; /* Full domain name or stringified IP address of cache server */
+ ip_addr ip; /* IP address of cache server or IPA_NONE */
+ u16 port; /* Port number of cache server */
+ struct rpki_tr_config tr_config; /* Specific transport configuration structure */
+ u32 refresh_interval; /* Time interval (in seconds) for periodical downloading data from cache server */
+ u32 retry_interval; /* Time interval (in seconds) for an unreachable server */
+ u32 expire_interval; /* Maximal lifetime (in seconds) of ROAs without any successful refreshment */
+ u8 keep_refresh_interval:1; /* Do not overwrite refresh interval by cache server update */
+ u8 keep_retry_interval:1; /* Do not overwrite retry interval by cache server update */
+ u8 keep_expire_interval:1; /* Do not overwrite expire interval by cache server update */
+};
+
+void rpki_check_config(struct rpki_config *cf);
+
+
+/*
+ * Logger
+ */
+
+#define RPKI_LOG(log_level, rpki, msg, args...) \
+ do { \
+ log(log_level "%s: " msg, (rpki)->p.name , ## args); \
+ } while(0)
+
+#if defined(LOCAL_DEBUG) || defined(GLOBAL_DEBUG)
+#define CACHE_DBG(cache,msg,args...) \
+ do { \
+ RPKI_LOG(L_DEBUG, (cache)->p, "%s [%s] %s " msg, rpki_get_cache_ident(cache), rpki_cache_state_to_str((cache)->state), __func__, ## args); \
+ } while(0)
+#else
+#define CACHE_DBG(cache,msg,args...) do { } while(0)
+#endif
+
+#define RPKI_TRACE(level,rpki,msg,args...) \
+ do { \
+ if ((rpki)->p.debug & level) \
+ RPKI_LOG(L_TRACE, rpki, msg, ## args); \
+ } while(0)
+
+#define CACHE_TRACE(level,cache,msg,args...) \
+ do { \
+ if ((cache)->p->p.debug & level) \
+ RPKI_LOG(L_TRACE, (cache)->p, msg, ## args); \
+ } while(0)
+
+#define RPKI_WARN(p, msg, args...) RPKI_LOG(L_WARN, p, msg, ## args);
+
+#endif /* _BIRD_RPKI_H_ */
diff --git a/proto/rpki/ssh_transport.c b/proto/rpki/ssh_transport.c
new file mode 100644
index 00000000..cd49ab90
--- /dev/null
+++ b/proto/rpki/ssh_transport.c
@@ -0,0 +1,75 @@
+/*
+ * BIRD -- An implementation of the SSH protocol for the RPKI transport
+ *
+ * (c) 2015 CZ.NIC
+ * (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
+ *
+ * This file was a part of RTRlib: http://rpki.realmv6.org/
+ * This transport implementation uses libssh (http://www.libssh.org/)
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "rpki.h"
+
+static int
+rpki_tr_ssh_open(struct rpki_tr_sock *tr)
+{
+ struct rpki_cache *cache = tr->cache;
+ struct rpki_config *cf = (void *) cache->p->p.cf;
+ struct rpki_tr_ssh_config *ssh_cf = (void *) cf->tr_config.spec;
+ sock *sk = tr->sk;
+
+ sk->type = SK_SSH_ACTIVE;
+ sk->ssh = mb_allocz(sk->pool, sizeof(struct ssh_sock));
+ sk->ssh->username = ssh_cf->user;
+ sk->ssh->client_privkey_path = ssh_cf->bird_private_key;
+ sk->ssh->server_hostkey_path = ssh_cf->cache_public_key;
+ sk->ssh->subsystem = "rpki-rtr";
+ sk->ssh->state = SK_SSH_CONNECT;
+
+ if (sk_open(sk) != 0)
+ return RPKI_TR_ERROR;
+
+ return RPKI_TR_SUCCESS;
+}
+
+static const char *
+rpki_tr_ssh_ident(struct rpki_tr_sock *tr)
+{
+ ASSERT(tr != NULL);
+
+ struct rpki_cache *cache = tr->cache;
+ struct rpki_config *cf = (void *) cache->p->p.cf;
+ struct rpki_tr_ssh_config *ssh_cf = (void *) cf->tr_config.spec;
+
+ if (tr->ident != NULL)
+ return tr->ident;
+
+ const char *username = ssh_cf->user;
+ const char *host = cf->hostname;
+ u16 port = cf->port;
+
+ size_t len = strlen(username) + 1 + strlen(host) + 1 + 5 + 1; /* <user> + '@' + <host> + ':' + <port> + '\0' */
+ char *ident = mb_alloc(cache->pool, len);
+ bsnprintf(ident, len, "%s@%s:%u", username, host, port);
+ tr->ident = ident;
+
+ return tr->ident;
+}
+
+/**
+ * rpki_tr_ssh_init - initializes the RPKI transport structure for a SSH connection
+ * @tr: allocated RPKI transport structure
+ */
+void
+rpki_tr_ssh_init(struct rpki_tr_sock *tr)
+{
+ tr->open_fp = &rpki_tr_ssh_open;
+ tr->ident_fp = &rpki_tr_ssh_ident;
+}
diff --git a/proto/rpki/tcp_transport.c b/proto/rpki/tcp_transport.c
new file mode 100644
index 00000000..6c05964a
--- /dev/null
+++ b/proto/rpki/tcp_transport.c
@@ -0,0 +1,78 @@
+/*
+ * BIRD -- An implementation of the TCP protocol for the RPKI protocol transport
+ *
+ * (c) 2015 CZ.NIC
+ * (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
+ *
+ * This file was a part of RTRlib: http://rpki.realmv6.org/
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "rpki.h"
+#include "sysdep/unix/unix.h"
+
+static int
+rpki_tr_tcp_open(struct rpki_tr_sock *tr)
+{
+ sock *sk = tr->sk;
+
+ sk->type = SK_TCP_ACTIVE;
+
+ if (sk_open(sk) != 0)
+ return RPKI_TR_ERROR;
+
+ return RPKI_TR_SUCCESS;
+}
+
+static const char *
+rpki_tr_tcp_ident(struct rpki_tr_sock *tr)
+{
+ ASSERT(tr != NULL);
+
+ struct rpki_cache *cache = tr->cache;
+ struct rpki_config *cf = (void *) cache->p->p.cf;
+
+ if (tr->ident != NULL)
+ return tr->ident;
+
+ const char *host = cf->hostname;
+ ip_addr ip = cf->ip;
+ u16 port = cf->port;
+
+ size_t colon_and_port_len = 6; /* max ":65535" */
+ size_t ident_len;
+ if (host)
+ ident_len = strlen(host) + colon_and_port_len + 1;
+ else
+ ident_len = IPA_MAX_TEXT_LENGTH + colon_and_port_len + 1;
+
+ char *ident = mb_alloc(cache->pool, ident_len);
+ if (host)
+ bsnprintf(ident, ident_len, "%s:%u", host, port);
+ else
+ bsnprintf(ident, ident_len, "%I:%u", ip, port);
+
+ tr->ident = ident;
+ return tr->ident;
+}
+
+/**
+ * rpki_tr_tcp_init - initializes the RPKI transport structure for a TCP connection
+ * @tr: allocated RPKI transport structure
+ */
+void
+rpki_tr_tcp_init(struct rpki_tr_sock *tr)
+{
+ tr->open_fp = &rpki_tr_tcp_open;
+ tr->ident_fp = &rpki_tr_tcp_ident;
+}
diff --git a/proto/rpki/transport.c b/proto/rpki/transport.c
new file mode 100644
index 00000000..182667be
--- /dev/null
+++ b/proto/rpki/transport.c
@@ -0,0 +1,135 @@
+/*
+ * BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
+ *
+ * (c) 2015 CZ.NIC
+ * (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
+ *
+ * This file was a part of RTRlib: http://rpki.realmv6.org/
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "rpki.h"
+#include "transport.h"
+#include "sysdep/unix/unix.h"
+
+/**
+ * rpki_hostname_autoresolv - auto-resolve an IP address from a hostname
+ * @host: domain name of host, e.g. "rpki-validator.realmv6.org"
+ *
+ * This function resolves an IP address from a hostname.
+ * Returns &ip_addr structure with IP address or |IPA_NONE|.
+ */
+static ip_addr
+rpki_hostname_autoresolv(const char *host)
+{
+ ip_addr addr = {};
+ struct addrinfo *res;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_STREAM,
+ .ai_flags = AI_ADDRCONFIG,
+ };
+
+ if (!host)
+ return IPA_NONE;
+
+ int err_code = getaddrinfo(host, NULL, &hints, &res);
+ if (err_code != 0)
+ {
+ log(L_DEBUG "getaddrinfo failed: %s", gai_strerror(err_code));
+ return IPA_NONE;
+ }
+
+ sockaddr sa = {
+ .sa = *res->ai_addr,
+ };
+
+ uint unused;
+ sockaddr_read(&sa, res->ai_family, &addr, NULL, &unused);
+
+ freeaddrinfo(res);
+ return addr;
+}
+
+/**
+ * rpki_tr_open - prepare and open a socket connection
+ * @tr: initialized transport socket
+ *
+ * Prepare and open a socket connection specified by @tr that must be initialized before.
+ * This function ends with a calling the sk_open() function.
+ * Returns RPKI_TR_SUCCESS or RPKI_TR_ERROR.
+ */
+int
+rpki_tr_open(struct rpki_tr_sock *tr)
+{
+ struct rpki_cache *cache = tr->cache;
+ struct rpki_config *cf = (void *) cache->p->p.cf;
+
+ ASSERT(tr->sk == NULL);
+ tr->sk = sk_new(cache->pool);
+ sock *sk = tr->sk;
+
+ /* sk->type -1 is invalid value, a correct value MUST be set in the specific transport layer in open_fp() hook */
+ sk->type = -1;
+
+ sk->tx_hook = rpki_connected_hook;
+ sk->err_hook = rpki_err_hook;
+ sk->data = cache;
+ sk->daddr = cf->ip;
+ sk->dport = cf->port;
+ sk->host = cf->hostname;
+ sk->rbsize = RPKI_RX_BUFFER_SIZE;
+ sk->tbsize = RPKI_TX_BUFFER_SIZE;
+ sk->tos = IP_PREC_INTERNET_CONTROL;
+
+ if (ipa_zero2(sk->daddr) && sk->host)
+ {
+ sk->daddr = rpki_hostname_autoresolv(sk->host);
+ if (ipa_zero(sk->daddr))
+ {
+ CACHE_TRACE(D_EVENTS, cache, "Cannot resolve the hostname '%s'", sk->host);
+ return RPKI_TR_ERROR;
+ }
+ }
+
+ return tr->open_fp(tr);
+}
+
+/**
+ * rpki_tr_close - close socket and prepare it for possible next open
+ * @tr: successfully opened transport socket
+ *
+ * Close socket and free resources.
+ */
+void
+rpki_tr_close(struct rpki_tr_sock *tr)
+{
+ if (tr->ident)
+ {
+ mb_free((char *) tr->ident);
+ tr->ident = NULL;
+ }
+
+ if (tr->sk)
+ {
+ rfree(tr->sk);
+ tr->sk = NULL;
+ }
+}
+
+/**
+ * rpki_tr_ident - Returns a string identifier for the rpki transport socket
+ * @tr: successfully opened transport socket
+ *
+ * Returns a \0 terminated string identifier for the socket endpoint, e.g. "<host>:<port>".
+ * Memory is allocated inside @tr structure.
+ */
+inline const char *
+rpki_tr_ident(struct rpki_tr_sock *tr)
+{
+ return tr->ident_fp(tr);
+}
diff --git a/proto/rpki/transport.h b/proto/rpki/transport.h
new file mode 100644
index 00000000..f90b7e42
--- /dev/null
+++ b/proto/rpki/transport.h
@@ -0,0 +1,79 @@
+/*
+ * BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
+ *
+ * (c) 2015 CZ.NIC
+ * (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
+ *
+ * This file was a part of RTRlib: http://rpki.realmv6.org/
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/*
+ * The RPKI transport sockets implement the communication channel
+ * (e.g., SSH, TCP, TCP-AO) between an RPKI server and client.
+ *
+ * Before using the transport socket, a tr_socket must be
+ * initialized based on a protocol-dependent init function (e.g.,
+ * rpki_tr_tcp_init()).
+ *
+ * The rpki_tr_* functions call the corresponding function pointers, which are
+ * passed in the rpki_tr_sock structure, and forward the remaining arguments.
+ */
+
+#ifndef _BIRD_RPKI_TRANSPORT_H_
+#define _BIRD_RPKI_TRANSPORT_H_
+
+#include <time.h>
+
+/* The return values for rpki_tr_ functions */
+enum rpki_tr_rtvals {
+ RPKI_TR_SUCCESS = 0, /* Operation was successful */
+ RPKI_TR_ERROR = -1, /* Error occurred */
+ RPKI_TR_WOULDBLOCK = -2, /* No data is available on the socket */
+ RPKI_TR_INTR = -3, /* Call was interrupted from a signal */
+ RPKI_TR_CLOSED = -4 /* Connection closed */
+};
+
+/* A transport socket structure */
+struct rpki_tr_sock {
+ sock *sk; /* Standard BIRD socket */
+ struct rpki_cache *cache; /* Cache server */
+ int (*open_fp)(struct rpki_tr_sock *); /* Function that establishes the socket connection */
+ const char *(*ident_fp)(struct rpki_tr_sock *); /* Function that returns an identifier for the socket endpoint */
+ const char *ident; /* Internal. Use ident_fp() hook instead of this pointer */
+};
+
+int rpki_tr_open(struct rpki_tr_sock *tr);
+void rpki_tr_close(struct rpki_tr_sock *tr);
+const char *rpki_tr_ident(struct rpki_tr_sock *tr);
+
+/* Types of supported transports */
+enum rpki_tr_type {
+ RPKI_TR_TCP, /* Unprotected transport over TCP */
+ RPKI_TR_SSH, /* Protected transport by SSHv2 connection */
+};
+
+/* Common configure structure for transports */
+struct rpki_tr_config {
+ enum rpki_tr_type type; /* RPKI_TR_TCP or RPKI_TR_SSH */
+ const void *spec; /* Specific transport configuration, i.e. rpki_tr_tcp_config or rpki_tr_ssh_config */
+};
+
+struct rpki_tr_tcp_config {
+ /* No internal configuration data */
+};
+
+struct rpki_tr_ssh_config {
+ const char *bird_private_key; /* Filepath to the BIRD server private key */
+ const char *cache_public_key; /* Filepath to the public key of cache server, can be file known_hosts */
+ const char *user; /* Username for SSH connection */
+};
+
+/* ssh_transport.c */
+void rpki_tr_ssh_init(struct rpki_tr_sock *tr);
+
+/* tcp_transport.c */
+void rpki_tr_tcp_init(struct rpki_tr_sock *tr);
+
+#endif /* _BIRD_RPKI_TRANSPORT_H_ */
diff --git a/proto/static/Makefile b/proto/static/Makefile
index 61fadbea..e38f9b74 100644
--- a/proto/static/Makefile
+++ b/proto/static/Makefile
@@ -1,6 +1,6 @@
-source=static.c
-root-rel=../../
-dir-name=proto/static
-
-include ../../Rules
+src := static.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
+tests_objs := $(tests_objs) $(src-o-files) \ No newline at end of file
diff --git a/proto/static/config.Y b/proto/static/config.Y
index 182721b3..66ae3c98 100644
--- a/proto/static/config.Y
+++ b/proto/static/config.Y
@@ -13,98 +13,116 @@ CF_HDR
CF_DEFINES
#define STATIC_CFG ((struct static_config *) this_proto)
-static struct static_route *this_srt, *this_srt_nh, *last_srt_nh;
+static struct static_route *this_srt, *this_snh;
static struct f_inst **this_srt_last_cmd;
+static struct static_route *
+static_nexthop_new(void)
+{
+ struct static_route *nh = this_srt;
+
+ if (this_snh)
+ {
+ /* Additional next hop */
+ nh = cfg_allocz(sizeof(struct static_route));
+ nh->net = this_srt->net;
+ this_snh->mp_next = nh;
+ }
+
+ nh->dest = RTD_UNICAST;
+ nh->mp_head = this_srt;
+ return nh;
+};
+
static void
static_route_finish(void)
{
- struct static_route *r;
-
- /* Update undefined use_bfd entries in multipath nexthops */
- if (this_srt->dest == RTD_MULTIPATH)
- for (r = this_srt->mp_next; r; r = r->mp_next)
- if (r->use_bfd < 0)
- r->use_bfd = this_srt->use_bfd;
+ if (net_type_match(this_srt->net, NB_DEST) == !this_srt->dest)
+ cf_error("Unexpected or missing nexthop/type");
}
CF_DECLS
CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK)
-CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD)
+CF_KEYWORDS(WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS)
CF_GRAMMAR
CF_ADDTO(proto, static_proto '}')
-static_proto_start: proto_start STATIC {
- this_proto = proto_config_new(&proto_static, $1);
- static_init_config((struct static_config *) this_proto);
- }
- ;
+static_proto_start: proto_start STATIC
+{
+ this_proto = proto_config_new(&proto_static, $1);
+ init_list(&STATIC_CFG->routes);
+};
static_proto:
static_proto_start proto_name '{'
| static_proto proto_item ';'
+ | static_proto proto_channel ';' { this_proto->net_type = $2->net_type; }
| static_proto CHECK LINK bool ';' { STATIC_CFG->check_link = $4; }
- | static_proto IGP TABLE rtable ';' { STATIC_CFG->igp_table = $4; }
+ | static_proto IGP TABLE rtable ';' {
+ if ($4->addr_type == NET_IP4)
+ STATIC_CFG->igp_table_ip4 = $4;
+ else if ($4->addr_type == NET_IP6)
+ STATIC_CFG->igp_table_ip6 = $4;
+ else
+ cf_error("Incompatible IGP table type");
+ }
| static_proto stat_route stat_route_opt_list ';' { static_route_finish(); }
;
-stat_route0: ROUTE prefix {
- this_srt = cfg_allocz(sizeof(struct static_route));
- add_tail(&STATIC_CFG->other_routes, &this_srt->n);
- this_srt->net = $2.addr;
- this_srt->masklen = $2.len;
- this_srt_last_cmd = &(this_srt->cmds);
+stat_nexthop:
+ VIA ipa ipa_scope {
+ this_snh = static_nexthop_new();
+ this_snh->via = $2;
+ this_snh->iface = $3;
+ }
+ | VIA TEXT {
+ this_snh = static_nexthop_new();
+ this_snh->via = IPA_NONE;
+ this_snh->iface = if_get_by_name($2);
+ }
+ | stat_nexthop MPLS label_stack {
+ this_snh->mls = $3;
}
- ;
+ | stat_nexthop WEIGHT expr {
+ this_snh->weight = $3 - 1;
+ if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256");
+ }
+ | stat_nexthop BFD bool {
+ this_snh->use_bfd = $3; cf_check_bfd($3);
+ }
+;
-stat_multipath1:
- VIA ipa ipa_scope {
- last_srt_nh = this_srt_nh;
- this_srt_nh = cfg_allocz(sizeof(struct static_route));
- this_srt_nh->dest = RTD_NONE;
- this_srt_nh->via = $2;
- this_srt_nh->via_if = $3;
- this_srt_nh->if_name = (void *) this_srt; /* really */
- this_srt_nh->use_bfd = -1; /* undefined */
- }
- | stat_multipath1 WEIGHT expr {
- this_srt_nh->masklen = $3 - 1; /* really */
- if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256");
- }
- | stat_multipath1 BFD bool {
- this_srt_nh->use_bfd = $3; cf_check_bfd($3);
- }
- ;
+stat_nexthops:
+ stat_nexthop
+ | stat_nexthops stat_nexthop
+;
-stat_multipath:
- stat_multipath1 { this_srt->mp_next = this_srt_nh; }
- | stat_multipath stat_multipath1 { last_srt_nh->mp_next = this_srt_nh; }
+stat_route0: ROUTE net_any {
+ this_srt = cfg_allocz(sizeof(struct static_route));
+ add_tail(&STATIC_CFG->routes, &this_srt->n);
+ this_srt->net = $2;
+ this_srt_last_cmd = &(this_srt->cmds);
+ this_srt->mp_next = NULL;
+ this_snh = NULL;
+ }
;
stat_route:
- stat_route0 VIA ipa ipa_scope {
- this_srt->dest = RTD_ROUTER;
+ stat_route0 stat_nexthops
+ | stat_route0 RECURSIVE ipa {
+ this_srt->dest = RTDX_RECURSIVE;
this_srt->via = $3;
- this_srt->via_if = $4;
- }
- | stat_route0 VIA TEXT {
- this_srt->dest = RTD_DEVICE;
- this_srt->if_name = $3;
- rem_node(&this_srt->n);
- add_tail(&STATIC_CFG->iface_routes, &this_srt->n);
}
- | stat_route0 MULTIPATH stat_multipath {
- this_srt->dest = RTD_MULTIPATH;
- }
- | stat_route0 RECURSIVE ipa {
+ | stat_route0 RECURSIVE ipa MPLS label_stack {
this_srt->dest = RTDX_RECURSIVE;
this_srt->via = $3;
+ this_srt->mls = $5;
}
-
+ | stat_route0 { this_srt->dest = RTD_NONE; }
| stat_route0 DROP { this_srt->dest = RTD_BLACKHOLE; }
| stat_route0 REJECT { this_srt->dest = RTD_UNREACHABLE; }
| stat_route0 BLACKHOLE { this_srt->dest = RTD_BLACKHOLE; }
@@ -114,7 +132,6 @@ stat_route:
stat_route_item:
cmd { *this_srt_last_cmd = $1; this_srt_last_cmd = &($1->next); }
- | BFD bool ';' { this_srt->use_bfd = $2; cf_check_bfd($2); }
;
stat_route_opts:
diff --git a/proto/static/static.c b/proto/static/static.c
index 849067b9..bb1501a5 100644
--- a/proto/static/static.c
+++ b/proto/static/static.c
@@ -9,33 +9,32 @@
/**
* DOC: Static
*
- * The Static protocol is implemented in a straightforward way. It keeps
- * two lists of static routes: one containing interface routes and one
- * holding the remaining ones. Interface routes are inserted and removed according
- * to interface events received from the core via the if_notify() hook. Routes
- * pointing to a neighboring router use a sticky node in the neighbor cache
- * to be notified about gaining or losing the neighbor. Special
- * routes like black holes or rejects are inserted all the time.
+ * The Static protocol is implemented in a straightforward way. It keeps a list
+ * of static routes. Routes of dest RTD_UNICAST have associated sticky node in
+ * the neighbor cache to be notified about gaining or losing the neighbor and
+ * about interface-related events (e.g. link down). They may also have a BFD
+ * request if associated with a BFD session. When a route is notified,
+ * static_decide() is used to see whether the route activeness is changed. In
+ * such case, the route is marked as dirty and scheduled to be announced or
+ * withdrawn, which is done asynchronously from event hook. Routes of other
+ * types (e.g. black holes) are announced all the time.
*
- * Multipath routes are tricky. Because these routes depends on
- * several neighbors we need to integrate that to the neighbor
- * notification handling, we use dummy static_route nodes, one for
- * each nexthop. Therefore, a multipath route consists of a master
- * static_route node (of dest RTD_MULTIPATH), which specifies prefix
- * and is used in most circumstances, and a list of dummy static_route
- * nodes (of dest RTD_NONE), which stores info about nexthops and are
- * connected to neighbor entries and neighbor notifications. Dummy
- * nodes are chained using mp_next, they aren't in other_routes list,
- * and abuse some fields (masklen, if_name) for other purposes.
+ * Multipath routes are a bit tricky. To represent additional next hops, dummy
+ * static_route nodes are used, which are chained using @mp_next field and link
+ * to the master node by @mp_head field. Each next hop has a separate neighbor
+ * entry and an activeness state, but the master node is used for most purposes.
+ * Note that most functions DO NOT accept dummy nodes as arguments.
*
* The only other thing worth mentioning is that when asked for reconfiguration,
* Static not only compares the two configurations, but it also calculates
- * difference between the lists of static routes and it just inserts the
- * newly added routes and removes the obsolete ones.
+ * difference between the lists of static routes and it just inserts the newly
+ * added routes, removes the obsolete ones and reannounces changed ones.
*/
#undef LOCAL_DEBUG
+#include <stdlib.h>
+
#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
@@ -50,107 +49,116 @@
static linpool *static_lp;
-static inline rtable *
-p_igp_table(struct proto *p)
-{
- struct static_config *cf = (void *) p->cf;
- return cf->igp_table ? cf->igp_table->table : p->table;
-}
-
static void
-static_install(struct proto *p, struct static_route *r, struct iface *ifa)
+static_announce_rte(struct static_proto *p, struct static_route *r)
{
- net *n;
- rta a;
- rte *e;
+ rta *a = allocz(RTA_MAX_SIZE);
+ a->src = p->p.main_source;
+ a->source = RTS_STATIC;
+ a->scope = SCOPE_UNIVERSE;
+ a->dest = r->dest;
- if (r->installed > 0)
- return;
+ if (r->dest == RTD_UNICAST)
+ {
+ struct static_route *r2;
+ struct nexthop *nhs = NULL;
- DBG("Installing static route %I/%d, rtd=%d\n", r->net, r->masklen, r->dest);
- bzero(&a, sizeof(a));
- a.src = p->main_source;
- a.source = (r->dest == RTD_DEVICE) ? RTS_STATIC_DEVICE : RTS_STATIC;
- a.scope = SCOPE_UNIVERSE;
- a.cast = RTC_UNICAST;
- a.dest = r->dest;
- a.gw = r->via;
- a.iface = ifa;
-
- if (r->dest == RTD_MULTIPATH)
+ for (r2 = r; r2; r2 = r2->mp_next)
{
- struct static_route *r2;
- struct mpnh *nhs = NULL;
-
- for (r2 = r->mp_next; r2; r2 = r2->mp_next)
- if (r2->installed)
- {
- struct mpnh *nh = alloca(sizeof(struct mpnh));
- nh->gw = r2->via;
- nh->iface = r2->neigh->iface;
- nh->weight = r2->masklen; /* really */
- mpnh_insert(&nhs, nh);
- }
-
- /* There is at least one nexthop */
- if (!nhs->next)
- {
- /* Fallback to unipath route for exactly one nexthop */
- a.dest = RTD_ROUTER;
- a.gw = nhs->gw;
- a.iface = nhs->iface;
- }
- else
- a.nexthops = nhs;
+ if (!r2->active)
+ continue;
+
+ struct nexthop *nh = allocz(NEXTHOP_MAX_SIZE);
+ nh->gw = r2->via;
+ nh->iface = r2->neigh->iface;
+ nh->weight = r2->weight;
+ if (r2->mls)
+ {
+ nh->labels = r2->mls->len;
+ memcpy(nh->label, r2->mls->stack, r2->mls->len * sizeof(u32));
+ }
+
+ nexthop_insert(&nhs, nh);
}
+ if (!nhs)
+ goto withdraw;
+
+ nexthop_link(a, nhs);
+ }
+
if (r->dest == RTDX_RECURSIVE)
- rta_set_recursive_next_hop(p->table, &a, p_igp_table(p), &r->via, &r->via);
+ {
+ rtable *tab = ipa_is_ip4(r->via) ? p->igp_table_ip4 : p->igp_table_ip6;
+ rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls);
+ }
- /* We skip rta_lookup() here */
+ /* Already announced */
+ if (r->state == SRS_CLEAN)
+ return;
- n = net_get(p->table, r->net, r->masklen);
- e = rte_get_temp(&a);
- e->net = n;
+ /* We skip rta_lookup() here */
+ rte *e = rte_get_temp(a);
e->pflags = 0;
if (r->cmds)
f_eval_rte(r->cmds, &e, static_lp);
- rte_update(p, n, e);
- r->installed = 1;
+ rte_update(&p->p, r->net, e);
+ r->state = SRS_CLEAN;
if (r->cmds)
lp_flush(static_lp);
+
+ return;
+
+withdraw:
+ if (r->state == SRS_DOWN)
+ return;
+
+ rte_update(&p->p, r->net, NULL);
+ r->state = SRS_DOWN;
}
static void
-static_remove(struct proto *p, struct static_route *r)
+static_mark_rte(struct static_proto *p, struct static_route *r)
{
- net *n;
-
- if (!r->installed)
+ if (r->state == SRS_DIRTY)
return;
- DBG("Removing static route %I/%d via %I\n", r->net, r->masklen, r->via);
- n = net_find(p->table, r->net, r->masklen);
- rte_update(p, n, NULL);
- r->installed = 0;
+ r->state = SRS_DIRTY;
+ BUFFER_PUSH(p->marked) = r;
+
+ if (!ev_active(p->event))
+ ev_schedule(p->event);
+}
+
+static void
+static_announce_marked(void *P)
+{
+ struct static_proto *p = P;
+
+ BUFFER_WALK(p->marked, r)
+ static_announce_rte(P, r);
+
+ BUFFER_FLUSH(p->marked);
}
static void
static_bfd_notify(struct bfd_request *req);
static void
-static_update_bfd(struct proto *p, struct static_route *r)
+static_update_bfd(struct static_proto *p, struct static_route *r)
{
+ /* The @r is a RTD_UNICAST next hop, may be a dummy node */
+
struct neighbor *nb = r->neigh;
int bfd_up = (nb->scope > 0) && r->use_bfd;
if (bfd_up && !r->bfd_req)
{
// ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
- r->bfd_req = bfd_request_session(p->pool, r->via, nb->ifa->ip, nb->iface,
+ r->bfd_req = bfd_request_session(p->p.pool, r->via, nb->ifa->ip, nb->iface,
static_bfd_notify, r);
}
@@ -162,212 +170,171 @@ static_update_bfd(struct proto *p, struct static_route *r)
}
static int
-static_decide(struct static_config *cf, struct static_route *r)
+static_decide(struct static_proto *p, struct static_route *r)
{
- /* r->dest != RTD_MULTIPATH, but may be RTD_NONE (part of multipath route)
- the route also have to be valid (r->neigh != NULL) */
+ /* The @r is a RTD_UNICAST next hop, may be a dummy node */
+
+ struct static_config *cf = (void *) p->p.cf;
+ uint old_active = r->active;
if (r->neigh->scope < 0)
- return 0;
+ goto fail;
if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP))
- return 0;
+ goto fail;
- if (r->bfd_req && r->bfd_req->state != BFD_STATE_UP)
- return 0;
+ if (r->bfd_req && (r->bfd_req->state != BFD_STATE_UP))
+ goto fail;
- return 1;
-}
+ r->active = 1;
+ return !old_active;
+fail:
+ r->active = 0;
+ return old_active;
+}
static void
-static_add(struct proto *p, struct static_config *cf, struct static_route *r)
+static_add_rte(struct static_proto *p, struct static_route *r)
{
- DBG("static_add(%I/%d,%d)\n", r->net, r->masklen, r->dest);
- switch (r->dest)
- {
- case RTD_ROUTER:
- {
- struct neighbor *n = neigh_find2(p, &r->via, r->via_if, NEF_STICKY);
- if (n)
- {
- r->chain = n->data;
- n->data = r;
- r->neigh = n;
-
- static_update_bfd(p, r);
- if (static_decide(cf, r))
- static_install(p, r, n->iface);
- else
- static_remove(p, r);
- }
- else
- {
- log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
- static_remove(p, r);
- }
- break;
- }
+ if (r->dest == RTD_UNICAST)
+ {
+ struct static_route *r2;
+ struct neighbor *n;
- case RTD_DEVICE:
- break;
+ for (r2 = r; r2; r2 = r2->mp_next)
+ {
+ n = ipa_nonzero(r2->via) ?
+ neigh_find2(&p->p, &r2->via, r2->iface, NEF_STICKY) :
+ neigh_find_iface(&p->p, r2->iface);
- case RTD_MULTIPATH:
+ if (!n)
{
- int count = 0;
- struct static_route *r2;
-
- for (r2 = r->mp_next; r2; r2 = r2->mp_next)
- {
- struct neighbor *n = neigh_find2(p, &r2->via, r2->via_if, NEF_STICKY);
- if (n)
- {
- r2->chain = n->data;
- n->data = r2;
- r2->neigh = n;
-
- static_update_bfd(p, r2);
- r2->installed = static_decide(cf, r2);
- count += r2->installed;
- }
- else
- {
- log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via);
- r2->installed = 0;
- }
- }
-
- if (count)
- static_install(p, r, NULL);
- else
- static_remove(p, r);
- break;
+ log(L_WARN "Invalid next hop %I of static route %N", r2->via, r2->net);
+ continue;
}
- default:
- static_install(p, r, NULL);
+ r2->neigh = n;
+ r2->chain = n->data;
+ n->data = r2;
+
+ static_update_bfd(p, r2);
+ static_decide(p, r2);
}
+ }
+
+ static_announce_rte(p, r);
}
static void
-static_rte_cleanup(struct proto *p UNUSED, struct static_route *r)
+static_reset_rte(struct static_proto *p UNUSED, struct static_route *r)
{
struct static_route *r2;
- if (r->bfd_req)
+ for (r2 = r; r2; r2 = r2->mp_next)
{
- rfree(r->bfd_req);
- r->bfd_req = NULL;
- }
+ r2->neigh = NULL;
+ r2->chain = NULL;
- if (r->dest == RTD_MULTIPATH)
- for (r2 = r->mp_next; r2; r2 = r2->mp_next)
- if (r2->bfd_req)
- {
- rfree(r2->bfd_req);
- r2->bfd_req = NULL;
- }
+ r2->state = 0;
+ r2->active = 0;
+
+ rfree(r2->bfd_req);
+ r2->bfd_req = NULL;
+ }
}
-static int
-static_start(struct proto *p)
+static void
+static_remove_rte(struct static_proto *p, struct static_route *r)
{
- struct static_config *cf = (void *) p->cf;
- struct static_route *r;
+ if (r->state)
+ rte_update(&p->p, r->net, NULL);
- DBG("Static: take off!\n");
+ static_reset_rte(p, r);
+}
- if (!static_lp)
- static_lp = lp_new(&root_pool, 1008);
- if (cf->igp_table)
- rt_lock_table(cf->igp_table->table);
+static inline int
+static_same_dest(struct static_route *x, struct static_route *y)
+{
+ if (x->dest != y->dest)
+ return 0;
+
+ switch (x->dest)
+ {
+ case RTD_UNICAST:
+ for (; x && y; x = x->mp_next, y = y->mp_next)
+ {
+ if (!ipa_equal(x->via, y->via) ||
+ (x->iface != y->iface) ||
+ (x->use_bfd != y->use_bfd) ||
+ (x->weight != y->weight) ||
+ (!x->mls != !y->mls) ||
+ ((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
+ return 0;
+
+ if (!x->mls)
+ continue;
+
+ for (uint i = 0; i < x->mls->len; i++)
+ if (x->mls->stack[i] != y->mls->stack[i])
+ return 0;
+ }
+ return !x && !y;
- /* We have to go UP before routes could be installed */
- proto_notify_state(p, PS_UP);
+ case RTDX_RECURSIVE:
+ if (!ipa_equal(x->via, y->via) ||
+ (!x->mls != !y->mls) ||
+ ((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
+ return 0;
- WALK_LIST(r, cf->other_routes)
- static_add(p, cf, r);
- return PS_UP;
-}
+ if (!x->mls)
+ return 1;
-static int
-static_shutdown(struct proto *p)
-{
- struct static_config *cf = (void *) p->cf;
- struct static_route *r;
+ for (uint i = 0; i < x->mls->len; i++)
+ if (x->mls->stack[i] != y->mls->stack[i])
+ return 0;
- /* Just reset the flag, the routes will be flushed by the nest */
- WALK_LIST(r, cf->iface_routes)
- r->installed = 0;
- WALK_LIST(r, cf->other_routes)
- {
- static_rte_cleanup(p, r);
- r->installed = 0;
- }
+ return 1;
- return PS_DOWN;
+ default:
+ return 1;
+ }
}
-static void
-static_cleanup(struct proto *p)
+static inline int
+static_same_rte(struct static_route *or, struct static_route *nr)
{
- struct static_config *cf = (void *) p->cf;
-
- if (cf->igp_table)
- rt_unlock_table(cf->igp_table->table);
+ /* Note that i_same() requires arguments in (new, old) order */
+ return static_same_dest(or, nr) && i_same(nr->cmds, or->cmds);
}
static void
-static_update_rte(struct proto *p, struct static_route *r)
+static_reconfigure_rte(struct static_proto *p, struct static_route *or, struct static_route *nr)
{
- switch (r->dest)
- {
- case RTD_ROUTER:
- if (static_decide((struct static_config *) p->cf, r))
- static_install(p, r, r->neigh->iface);
- else
- static_remove(p, r);
- break;
-
- case RTD_NONE: /* a part of multipath route */
- {
- int decision = static_decide((struct static_config *) p->cf, r);
- if (decision == r->installed)
- break; /* no change */
- r->installed = decision;
-
- struct static_route *r1, *r2;
- int count = 0;
- r1 = (void *) r->if_name; /* really */
- for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
- count += r2->installed;
-
- if (count)
- {
- /* Set of nexthops changed - force reinstall */
- r1->installed = 0;
- static_install(p, r1, NULL);
- }
- else
- static_remove(p, r1);
+ if ((or->state == SRS_CLEAN) && !static_same_rte(or, nr))
+ nr->state = SRS_DIRTY;
+ else
+ nr->state = or->state;
- break;
- }
- }
+ static_add_rte(p, nr);
+ static_reset_rte(p, or);
}
+
static void
static_neigh_notify(struct neighbor *n)
{
- struct proto *p = n->proto;
+ struct static_proto *p = (void *) n->proto;
struct static_route *r;
DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
- for(r=n->data; r; r=r->chain)
+ for (r = n->data; r; r = r->chain)
{
static_update_bfd(p, r);
- static_update_rte(p, r);
+
+ if (static_decide(p, r))
+ static_mark_rte(p, r->mp_head);
}
}
@@ -375,241 +342,232 @@ static void
static_bfd_notify(struct bfd_request *req)
{
struct static_route *r = req->data;
- struct proto *p = r->neigh->proto;
+ struct static_proto *p = (void *) r->neigh->proto;
// if (req->down) TRACE(D_EVENTS, "BFD session down for nbr %I on %s", XXXX);
- static_update_rte(p, r);
+ if (static_decide(p, r))
+ static_mark_rte(p, r->mp_head);
}
-static void
-static_dump_rt(struct static_route *r)
+static int
+static_rte_mergable(rte *pri UNUSED, rte *sec UNUSED)
{
- debug("%-1I/%2d: ", r->net, r->masklen);
- switch (r->dest)
- {
- case RTD_ROUTER:
- debug("via %I\n", r->via);
- break;
- case RTD_DEVICE:
- debug("dev %s\n", r->if_name);
- break;
- default:
- debug("rtd %d\n", r->dest);
- break;
- }
+ return 1;
}
+
static void
-static_dump(struct proto *p)
+static_postconfig(struct proto_config *CF)
{
- struct static_config *c = (void *) p->cf;
+ struct static_config *cf = (void *) CF;
struct static_route *r;
- debug("Independent static routes:\n");
- WALK_LIST(r, c->other_routes)
- static_dump_rt(r);
- debug("Device static routes:\n");
- WALK_LIST(r, c->iface_routes)
- static_dump_rt(r);
-}
+ if (EMPTY_LIST(CF->channels))
+ cf_error("Channel not specified");
-static void
-static_if_notify(struct proto *p, unsigned flags, struct iface *i)
-{
- struct static_route *r;
- struct static_config *c = (void *) p->cf;
+ struct channel_config *cc = proto_cf_main_channel(CF);
- if (flags & IF_CHANGE_UP)
- {
- WALK_LIST(r, c->iface_routes)
- if (!strcmp(r->if_name, i->name))
- static_install(p, r, i);
- }
- else if (flags & IF_CHANGE_DOWN)
- {
- WALK_LIST(r, c->iface_routes)
- if (!strcmp(r->if_name, i->name))
- static_remove(p, r);
- }
-}
+ if (!cf->igp_table_ip4)
+ cf->igp_table_ip4 = (cc->table->addr_type == NET_IP4) ?
+ cc->table : cf->c.global->def_tables[NET_IP4];
-int
-static_rte_mergable(rte *pri UNUSED, rte *sec UNUSED)
-{
- return 1;
-}
+ if (!cf->igp_table_ip6)
+ cf->igp_table_ip6 = (cc->table->addr_type == NET_IP6) ?
+ cc->table : cf->c.global->def_tables[NET_IP6];
-void
-static_init_config(struct static_config *c)
-{
- init_list(&c->iface_routes);
- init_list(&c->other_routes);
+ WALK_LIST(r, cf->routes)
+ if (r->net && (r->net->type != CF->net_type))
+ cf_error("Route %N incompatible with channel type", r->net);
}
static struct proto *
-static_init(struct proto_config *c)
+static_init(struct proto_config *CF)
{
- struct proto *p = proto_new(c, sizeof(struct proto));
+ struct proto *P = proto_new(CF);
+ struct static_proto *p = (void *) P;
+ struct static_config *cf = (void *) CF;
- p->neigh_notify = static_neigh_notify;
- p->if_notify = static_if_notify;
- p->rte_mergable = static_rte_mergable;
+ P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
- return p;
-}
+ P->neigh_notify = static_neigh_notify;
+ P->rte_mergable = static_rte_mergable;
-static inline int
-static_same_net(struct static_route *x, struct static_route *y)
-{
- return ipa_equal(x->net, y->net) && (x->masklen == y->masklen);
+ if (cf->igp_table_ip4)
+ p->igp_table_ip4 = cf->igp_table_ip4->table;
+
+ if (cf->igp_table_ip6)
+ p->igp_table_ip6 = cf->igp_table_ip6->table;
+
+ return P;
}
-static inline int
-static_same_dest(struct static_route *x, struct static_route *y)
+static int
+static_start(struct proto *P)
{
- if (x->dest != y->dest)
- return 0;
+ struct static_proto *p = (void *) P;
+ struct static_config *cf = (void *) P->cf;
+ struct static_route *r;
- switch (x->dest)
- {
- case RTD_ROUTER:
- return ipa_equal(x->via, y->via) && (x->via_if == y->via_if);
+ if (!static_lp)
+ static_lp = lp_new(&root_pool, LP_GOOD_SIZE(1024));
- case RTD_DEVICE:
- return !strcmp(x->if_name, y->if_name);
+ if (p->igp_table_ip4)
+ rt_lock_table(p->igp_table_ip4);
- case RTD_MULTIPATH:
- for (x = x->mp_next, y = y->mp_next;
- x && y;
- x = x->mp_next, y = y->mp_next)
- if (!ipa_equal(x->via, y->via) || (x->via_if != y->via_if) || (x->use_bfd != y->use_bfd))
- return 0;
- return !x && !y;
+ if (p->igp_table_ip6)
+ rt_lock_table(p->igp_table_ip6);
- case RTDX_RECURSIVE:
- return ipa_equal(x->via, y->via);
+ p->event = ev_new(p->p.pool);
+ p->event->hook = static_announce_marked;
+ p->event->data = p;
- default:
- return 1;
- }
+ BUFFER_INIT(p->marked, p->p.pool, 4);
+
+ /* We have to go UP before routes could be installed */
+ proto_notify_state(P, PS_UP);
+
+ WALK_LIST(r, cf->routes)
+ static_add_rte(p, r);
+
+ return PS_UP;
}
-static inline int
-static_same_rte(struct static_route *x, struct static_route *y)
+static int
+static_shutdown(struct proto *P)
{
- /* Note that i_same() requires arguments in (new, old) order */
- return static_same_dest(x, y) && i_same(y->cmds, x->cmds);
-}
+ struct static_proto *p = (void *) P;
+ struct static_config *cf = (void *) P->cf;
+ struct static_route *r;
+ /* Just reset the flag, the routes will be flushed by the nest */
+ WALK_LIST(r, cf->routes)
+ static_reset_rte(p, r);
+
+ return PS_DOWN;
+}
static void
-static_match(struct proto *p, struct static_route *r, struct static_config *n)
+static_cleanup(struct proto *P)
{
- struct static_route *t;
-
- /*
- * For given old route *r we find whether a route to the same
- * network is also in the new route list. In that case, we keep the
- * route and possibly update the route later if destination changed.
- * Otherwise, we remove the route.
- */
+ struct static_proto *p = (void *) P;
- if (r->neigh)
- r->neigh->data = NULL;
+ if (p->igp_table_ip4)
+ rt_unlock_table(p->igp_table_ip4);
- WALK_LIST(t, n->iface_routes)
- if (static_same_net(r, t))
- goto found;
+ if (p->igp_table_ip6)
+ rt_unlock_table(p->igp_table_ip6);
+}
- WALK_LIST(t, n->other_routes)
- if (static_same_net(r, t))
- goto found;
+static void
+static_dump_rte(struct static_route *r)
+{
+ debug("%-1N: ", r->net);
+ if (r->dest == RTD_UNICAST)
+ if (r->iface && ipa_zero(r->via))
+ debug("dev %s\n", r->iface->name);
+ else
+ debug("via %I%J\n", r->via, r->iface);
+ else
+ debug("rtd %d\n", r->dest);
+}
- static_remove(p, r);
- return;
+static void
+static_dump(struct proto *P)
+{
+ struct static_config *c = (void *) P->cf;
+ struct static_route *r;
- found:
- /* If destination is different, force reinstall */
- if ((r->installed > 0) && !static_same_rte(r, t))
- t->installed = -1;
- else
- t->installed = r->installed;
+ debug("Static routes:\n");
+ WALK_LIST(r, c->routes)
+ static_dump_rte(r);
}
-static inline rtable *
-cf_igp_table(struct static_config *cf)
+#define IGP_TABLE(cf, sym) ((cf)->igp_table_##sym ? (cf)->igp_table_##sym ->table : NULL )
+
+static inline int
+static_cmp_rte(const void *X, const void *Y)
{
- return cf->igp_table ? cf->igp_table->table : NULL;
+ struct static_route *x = *(void **)X, *y = *(void **)Y;
+ return net_compare(x->net, y->net);
}
static int
-static_reconfigure(struct proto *p, struct proto_config *new)
+static_reconfigure(struct proto *P, struct proto_config *CF)
{
- struct static_config *o = (void *) p->cf;
- struct static_config *n = (void *) new;
- struct static_route *r;
+ struct static_proto *p = (void *) P;
+ struct static_config *o = (void *) P->cf;
+ struct static_config *n = (void *) CF;
+ struct static_route *r, *r2, *or, *nr;
+
+ /* Check change in IGP tables */
+ if ((IGP_TABLE(o, ip4) != IGP_TABLE(n, ip4)) ||
+ (IGP_TABLE(o, ip6) != IGP_TABLE(n, ip6)))
+ return 0;
- if (cf_igp_table(o) != cf_igp_table(n))
+ if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
return 0;
- /* Delete all obsolete routes and reset neighbor entries */
- WALK_LIST(r, o->iface_routes)
- static_match(p, r, n);
- WALK_LIST(r, o->other_routes)
- static_match(p, r, n);
+ p->p.cf = CF;
- /* Now add all new routes, those not changed will be ignored by static_install() */
- WALK_LIST(r, n->iface_routes)
- {
- struct iface *ifa;
- if ((ifa = if_find_by_name(r->if_name)) && (ifa->flags & IF_UP))
- static_install(p, r, ifa);
- }
- WALK_LIST(r, n->other_routes)
- static_add(p, n, r);
+ /* Reset route lists in neighbor entries */
+ WALK_LIST(r, o->routes)
+ for (r2 = r; r2; r2 = r2->mp_next)
+ if (r2->neigh)
+ r2->neigh->data = NULL;
- WALK_LIST(r, o->other_routes)
- static_rte_cleanup(p, r);
+ /* Reconfigure initial matching sequence */
+ for (or = HEAD(o->routes), nr = HEAD(n->routes);
+ NODE_VALID(or) && NODE_VALID(nr) && net_equal(or->net, nr->net);
+ or = NODE_NEXT(or), nr = NODE_NEXT(nr))
+ static_reconfigure_rte(p, or, nr);
- return 1;
-}
+ if (!NODE_VALID(or) && !NODE_VALID(nr))
+ return 1;
-static void
-static_copy_routes(list *dlst, list *slst)
-{
- struct static_route *dr, *sr;
+ /* Reconfigure remaining routes, sort them to find matching pairs */
+ struct static_route *or2, *nr2, **orbuf, **nrbuf;
+ uint ornum = 0, nrnum = 0, orpos = 0, nrpos = 0, i;
- init_list(dlst);
- WALK_LIST(sr, *slst)
- {
- /* copy one route */
- dr = cfg_alloc(sizeof(struct static_route));
- memcpy(dr, sr, sizeof(struct static_route));
-
- /* This fn is supposed to be called on fresh src routes, which have 'live'
- fields (like .chain, .neigh or .installed) zero, so no need to zero them */
-
- /* We need to copy multipath chain, because there are backptrs in 'if_name' */
- if (dr->dest == RTD_MULTIPATH)
- {
- struct static_route *md, *ms, **mp_last;
-
- mp_last = &(dr->mp_next);
- for (ms = sr->mp_next; ms; ms = ms->mp_next)
- {
- md = cfg_alloc(sizeof(struct static_route));
- memcpy(md, ms, sizeof(struct static_route));
- md->if_name = (void *) dr; /* really */
-
- *mp_last = md;
- mp_last = &(md->mp_next);
- }
- *mp_last = NULL;
- }
-
- add_tail(dlst, (node *) dr);
- }
+ for (or2 = or; NODE_VALID(or2); or2 = NODE_NEXT(or2))
+ ornum++;
+
+ for (nr2 = nr; NODE_VALID(nr2); nr2 = NODE_NEXT(nr2))
+ nrnum++;
+
+ orbuf = xmalloc(ornum * sizeof(void *));
+ nrbuf = xmalloc(nrnum * sizeof(void *));
+
+ for (i = 0, or2 = or; i < ornum; i++, or2 = NODE_NEXT(or2))
+ orbuf[i] = or2;
+
+ for (i = 0, nr2 = nr; i < nrnum; i++, nr2 = NODE_NEXT(nr2))
+ nrbuf[i] = nr2;
+
+ qsort(orbuf, ornum, sizeof(struct static_route *), static_cmp_rte);
+ qsort(nrbuf, nrnum, sizeof(struct static_route *), static_cmp_rte);
+
+ while ((orpos < ornum) && (nrpos < nrnum))
+ {
+ int x = net_compare(orbuf[orpos]->net, nrbuf[nrpos]->net);
+ if (x < 0)
+ static_remove_rte(p, orbuf[orpos++]);
+ else if (x > 0)
+ static_add_rte(p, nrbuf[nrpos++]);
+ else
+ static_reconfigure_rte(p, orbuf[orpos++], nrbuf[nrpos++]);
+ }
+
+ while (orpos < ornum)
+ static_remove_rte(p, orbuf[orpos++]);
+
+ while (nrpos < nrnum)
+ static_add_rte(p, nrbuf[nrpos++]);
+
+ xfree(orbuf);
+ xfree(nrbuf);
+
+ return 1;
}
static void
@@ -618,53 +576,64 @@ static_copy_config(struct proto_config *dest, struct proto_config *src)
struct static_config *d = (struct static_config *) dest;
struct static_config *s = (struct static_config *) src;
- /* Shallow copy of everything */
- proto_copy_rest(dest, src, sizeof(struct static_config));
+ struct static_route *srt, *snh;
- /* Copy route lists */
- static_copy_routes(&d->iface_routes, &s->iface_routes);
- static_copy_routes(&d->other_routes, &s->other_routes);
-}
+ /* Copy route list */
+ init_list(&d->routes);
+ WALK_LIST(srt, s->routes)
+ {
+ struct static_route *drt = NULL, *dnh = NULL, **dnp = &drt;
+ for (snh = srt; snh; snh = snh->mp_next)
+ {
+ dnh = cfg_alloc(sizeof(struct static_route));
+ memcpy(dnh, snh, sizeof(struct static_route));
-struct protocol proto_static = {
- .name = "Static",
- .template = "static%d",
- .preference = DEF_PREF_STATIC,
- .config_size = sizeof(struct static_config),
- .init = static_init,
- .dump = static_dump,
- .start = static_start,
- .shutdown = static_shutdown,
- .cleanup = static_cleanup,
- .reconfigure = static_reconfigure,
- .copy_config = static_copy_config
-};
+ if (!drt)
+ add_tail(&d->routes, &(dnh->n));
+
+ *dnp = dnh;
+ dnp = &(dnh->mp_next);
+
+ if (snh->mp_head)
+ dnh->mp_head = drt;
+ }
+ }
+}
static void
static_show_rt(struct static_route *r)
{
- byte via[STD_ADDRESS_P_LENGTH + 16];
-
switch (r->dest)
+ {
+ case RTD_UNICAST:
+ {
+ struct static_route *r2;
+
+ cli_msg(-1009, "%N", r->net);
+ for (r2 = r; r2; r2 = r2->mp_next)
{
- case RTD_ROUTER: bsprintf(via, "via %I%J", r->via, r->via_if); break;
- case RTD_DEVICE: bsprintf(via, "dev %s", r->if_name); break;
- case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break;
- case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
- case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
- case RTD_MULTIPATH: bsprintf(via, "multipath"); break;
- case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
- default: bsprintf(via, "???");
+ if (r2->iface && ipa_zero(r2->via))
+ cli_msg(-1009, "\tdev %s%s%s", r2->iface->name,
+ r2->bfd_req ? " (bfd)" : "", r2->active ? "" : " (dormant)");
+ else
+ cli_msg(-1009, "\tvia %I%J%s%s", r2->via, r2->iface,
+ r2->bfd_req ? " (bfd)" : "", r2->active ? "" : " (dormant)");
}
- cli_msg(-1009, "%I/%d %s%s%s", r->net, r->masklen, via,
- r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)");
+ break;
+ }
- struct static_route *r2;
- if (r->dest == RTD_MULTIPATH)
- for (r2 = r->mp_next; r2; r2 = r2->mp_next)
- cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->masklen + 1, /* really */
- r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)");
+ case RTD_NONE:
+ case RTD_BLACKHOLE:
+ case RTD_UNREACHABLE:
+ case RTD_PROHIBIT:
+ cli_msg(-1009, "%N\t%s", r->net, rta_dest_names[r->dest]);
+ break;
+
+ case RTDX_RECURSIVE:
+ cli_msg(-1009, "%N\trecursive %I", r->net, r->via);
+ break;
+ }
}
void
@@ -673,9 +642,25 @@ static_show(struct proto *P)
struct static_config *c = (void *) P->cf;
struct static_route *r;
- WALK_LIST(r, c->other_routes)
- static_show_rt(r);
- WALK_LIST(r, c->iface_routes)
+ WALK_LIST(r, c->routes)
static_show_rt(r);
cli_msg(0, "");
}
+
+
+struct protocol proto_static = {
+ .name = "Static",
+ .template = "static%d",
+ .preference = DEF_PREF_STATIC,
+ .channel_mask = NB_ANY,
+ .proto_size = sizeof(struct static_proto),
+ .config_size = sizeof(struct static_config),
+ .postconfig = static_postconfig,
+ .init = static_init,
+ .dump = static_dump,
+ .start = static_start,
+ .shutdown = static_shutdown,
+ .cleanup = static_cleanup,
+ .reconfigure = static_reconfigure,
+ .copy_config = static_copy_config
+};
diff --git a/proto/static/static.h b/proto/static/static.h
index 6b047234..c84dfa98 100644
--- a/proto/static/static.h
+++ b/proto/static/static.h
@@ -11,41 +11,60 @@
#include "nest/route.h"
#include "nest/bfd.h"
+#include "lib/buffer.h"
struct static_config {
struct proto_config c;
- list iface_routes; /* Routes to search on interface events */
- list other_routes; /* Routes hooked to neighbor cache and reject routes */
+ list routes; /* List of static routes (struct static_route) */
int check_link; /* Whether iface link state is used */
- struct rtable_config *igp_table; /* Table used for recursive next hop lookups */
+ struct rtable_config *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */
+ struct rtable_config *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */
};
+struct static_proto {
+ struct proto p;
-void static_init_config(struct static_config *);
+ struct event *event; /* Event for announcing updated routes */
+ BUFFER(struct static_route *) marked; /* Routes marked for reannouncement */
+ rtable *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */
+ rtable *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */
+};
struct static_route {
node n;
- struct static_route *chain; /* Next for the same neighbor */
- ip_addr net; /* Network we route */
- int masklen; /* Mask length */
- int dest; /* Destination type (RTD_*) */
+ net_addr *net; /* Network we route */
ip_addr via; /* Destination router */
- struct iface *via_if; /* Destination iface, for link-local vias */
- struct neighbor *neigh;
- byte *if_name; /* Name for RTD_DEVICE routes */
- struct static_route *mp_next; /* Nexthops for RTD_MULTIPATH routes */
+ struct iface *iface; /* Destination iface, for link-local vias or device routes */
+ struct neighbor *neigh; /* Associated neighbor entry */
+ struct static_route *chain; /* Next for the same neighbor */
+ struct static_route *mp_head; /* First nexthop of this route */
+ struct static_route *mp_next; /* Nexthops for multipath routes */
struct f_inst *cmds; /* List of commands for setting attributes */
- int installed; /* Installed in rt table, -1 for reinstall */
- int use_bfd; /* Configured to use BFD */
+ byte dest; /* Destination type (RTD_*) */
+ byte state; /* State of route announcement (SRS_*) */
+ byte active; /* Next hop is active (nbr/iface/BFD available) */
+ byte weight; /* Multipath next hop weight */
+ byte use_bfd; /* Configured to use BFD */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
+ mpls_label_stack *mls; /* MPLS label stack; may be NULL */
};
-/* Dummy nodes (parts of multipath route) abuses masklen field for weight
- and if_name field for a ptr to the master (RTD_MULTIPATH) node. */
-
+/*
+ * Note that data fields neigh, chain, state, active and bfd_req are runtime
+ * data, not real configuration data. Must be handled carefully.
+ *
+ * Regular (i.e. dest == RTD_UNICAST) routes use static_route structure for
+ * additional next hops (fields mp_head, mp_next). Note that 'state' is for
+ * whole route, while 'active' is for each next hop. Also note that fields
+ * mp_head, mp_next, active are zero for other kinds of routes.
+ */
#define RTDX_RECURSIVE 0x7f /* Phony dest value for recursive routes */
+#define SRS_DOWN 0 /* Route is not announced */
+#define SRS_CLEAN 1 /* Route is active and announced */
+#define SRS_DIRTY 2 /* Route changed since announcement */
+
void static_show(struct proto *);
#endif