Commit bf47fe4b authored by Ondřej Zajíček's avatar Ondřej Zajíček

Implements BGP route refresh.

parent 5e6f5681
......@@ -482,9 +482,6 @@ This argument can be omitted if there exists only a single instance.
number of networks, number of routes before and after filtering). If
you use <cf/count/ instead, only the statistics will be printed.
<tag>enable|disable|restart <m/name/|"<m/pattern/"|all</tag>
Enable, disable or restart a given protocol instance, instances matching the <cf><m/pattern/</cf> or <cf/all/ instances.
<tag>configure [soft] ["<m/config file/"]</tag>
Reload configuration from a given file. BIRD will smoothly
switch itself to the new configuration, protocols are
......@@ -496,6 +493,9 @@ This argument can be omitted if there exists only a single instance.
but new routes would be processed according to the new
filters.
<tag>enable|disable|restart <m/name/|"<m/pattern/"|all</tag>
Enable, disable or restart a given protocol instance, instances matching the <cf><m/pattern/</cf> or <cf/all/ instances.
<tag/down/
Shut BIRD down.
......@@ -944,6 +944,16 @@ for each neighbor using the following configuration parameters:
attributes to be transparent (for example does not prepend its AS number to
AS PATH attribute and keep MED attribute). Default: disabled.
<tag>enable route refresh <m/switch/</tag> When BGP speaker
changes its import filter, it has to re-examine all routes
received from its neighbor against the new filter. As these
routes might not be available, there is a BGP protocol
extension Route Refresh (specified in RFC 2918) that allows
BGP speaker to request re-advertisment of all routes from its
neighbor. This option specifies whether BIRD advertises this
capability and accepts such requests. Even when disabled, BIRD
can send route refresh requests. Default: on.
<tag>enable as4 <m/switch/</tag> BGP protocol was designed to use 2B AS numbers
and was extended later to allow 4B AS number. BIRD supports 4B AS extension,
but by disabling this option it can be persuaded not to advertise it and
......
......@@ -45,6 +45,7 @@ CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILT
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE)
CF_KEYWORDS(LISTEN, BGP, V6ONLY, ADDRESS, PORT, PASSWORDS, DESCRIPTION)
CF_KEYWORDS(RELOAD, REFEED)
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE)
......@@ -442,6 +443,12 @@ CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]]
{ proto_xxable($2, 1); } ;
CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]])
{ proto_xxable($2, 2); } ;
CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
{ proto_xxable($2, 3); } ;
CF_CLI(REFEED, proto_patt, <protocol> | \"<pattern>\" | all, [[Refeed protocol BROKEN]])
{ proto_xxable($2, 4); } ;
CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging]])
CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | <pattern> | all) (all | off | { states | routes | filters | events | packets }), [[Control protocol debugging]])
......
......@@ -73,6 +73,10 @@ proto_relink(struct proto *p)
rem_node(&p->n);
switch (p->core_state)
{
case FS_HUNGRY:
l = &inactive_proto_list;
break;
case FS_FEEDING:
case FS_HAPPY:
l = &active_proto_list;
break;
......@@ -80,7 +84,7 @@ proto_relink(struct proto *p)
l = &flush_proto_list;
break;
default:
l = &inactive_proto_list;
ASSERT(0);
}
proto_enqueue(l, p);
}
......@@ -549,7 +553,7 @@ proto_feed_more(void *P)
}
static void
proto_feed(void *P)
proto_feed_initial(void *P)
{
struct proto *p = P;
......@@ -577,15 +581,44 @@ proto_schedule_flush(struct proto *p)
}
static void
proto_schedule_feed(struct proto *p)
proto_schedule_feed(struct proto *p, int initial)
{
DBG("%s: Scheduling meal\n", p->name);
p->core_state = FS_FEEDING;
proto_relink(p);
p->attn->hook = proto_feed;
p->attn->hook = initial ? proto_feed_initial : proto_feed_more;
ev_schedule(p->attn);
}
/**
* proto_request_feeding - request feeding routes to the protocol
* @p: given protocol
*
* Sometimes it is needed to send again all routes to the
* protocol. This is called feeding and can be requested by this
* function. This would cause protocol core state transition
* to FS_FEEDING (during feeding) and when completed, it will
* switch back to FS_HAPPY. This function can be called even
* when feeding is already running, in that case it is restarted.
*/
void
proto_request_feeding(struct proto *p)
{
ASSERT(p->proto_state == PS_UP);
/* If we are already feeding, we want to restart it */
if (p->core_state == FS_FEEDING)
{
/* Unless feeding is in initial state */
if (p->attn->hook == proto_feed_initial)
return;
rt_feed_baby_abort(p);
}
proto_schedule_feed(p, 0);
}
/**
* proto_notify_state - notify core about protocol state change
* @p: protocol the state of which has changed
......@@ -635,7 +668,7 @@ proto_notify_state(struct proto *p, unsigned ps)
case PS_UP:
ASSERT(ops == PS_DOWN || ops == PS_START);
ASSERT(cs == FS_HUNGRY);
proto_schedule_feed(p);
proto_schedule_feed(p, 1);
break;
case PS_STOP:
if ((cs = FS_FEEDING) || (cs == FS_HAPPY))
......@@ -798,6 +831,7 @@ proto_xxable(char *pattern, int xx)
{
cli_msg(-9, "%s: disabled", p->name);
p->disabled = 1;
proto_rethink_goal(p);
}
break;
case 1:
......@@ -807,6 +841,7 @@ proto_xxable(char *pattern, int xx)
{
cli_msg(-11, "%s: enabled", p->name);
p->disabled = 0;
proto_rethink_goal(p);
}
break;
case 2:
......@@ -817,13 +852,33 @@ proto_xxable(char *pattern, int xx)
p->disabled = 1;
proto_rethink_goal(p);
p->disabled = 0;
proto_rethink_goal(p);
cli_msg(-12, "%s: restarted", p->name);
}
break;
case 3:
// FIXME change msg number
if (p->disabled)
cli_msg(-8, "%s: already disabled", p->name);
else if (p->reload_routes && p->reload_routes(p))
cli_msg(-12, "%s: reloading", p->name);
else
cli_msg(-12, "%s: reload failed", p->name);
break;
case 4:
// FIXME change msg number
if (p->disabled)
cli_msg(-8, "%s: already disabled", p->name);
else
{
proto_request_feeding(p);
cli_msg(-12, "%s: reexport failed", p->name);
}
break;
default:
ASSERT(0);
}
proto_rethink_goal(p);
}
WALK_PROTO_LIST_END;
if (!cnt)
......
......@@ -152,6 +152,9 @@ struct proto {
* It can construct a new rte, add private attributes and
* decide whether the route shall be imported: 1=yes, -1=no,
* 0=process it through the import filter set by the user.
* reload_routes Request protocol to reload all its routes to the core
* (using rte_update()). Returns: 0=reload cannot be done,
* 1= reload is scheduled and will happen (asynchronously).
*/
void (*if_notify)(struct proto *, unsigned flags, struct iface *i);
......@@ -161,6 +164,7 @@ struct proto {
struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
void (*store_tmp_attrs)(struct rte *rt, struct ea_list *attrs);
int (*import_control)(struct proto *, struct rte **rt, struct ea_list **attrs, struct linpool *pool);
int (*reload_routes)(struct proto *);
/*
* Routing entry hooks (called only for rte's belonging to this protocol):
......@@ -190,6 +194,7 @@ struct proto {
void *proto_new(struct proto_config *, unsigned size);
void *proto_config_new(struct protocol *, unsigned size);
void proto_request_feeding(struct proto *p);
void proto_show(struct symbol *, int);
struct proto *proto_get_named(struct symbol *, struct protocol *);
void proto_xxable(char *, int);
......@@ -271,6 +276,10 @@ void proto_notify_state(struct proto *p, unsigned state);
* HUNGRY/DOWN --> HUNGRY/START --> HUNGRY/UP -->
* FEEDING/UP --> HAPPY/UP --> FLUSHING/STOP|DOWN -->
* HUNGRY/STOP|DOWN --> HUNGRY/DOWN
*
* Sometimes, protocol might switch from HAPPY/UP to FEEDING/UP
* if it wants to refeed the routes (for example BGP does so
* as a result of received ROUTE-REFRESH request).
*/
#define FS_HUNGRY 0
......
......@@ -199,6 +199,14 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old,
else
p->stats.exp_withdraws_received++;
/* This is a tricky part - we don't know whether route 'old' was
exported to protocol 'p' or was filtered by the export filter.
We try tu run the export filter to know this to have a correct
value in 'old' argument of rt_update (and proper filter value)
FIXME - this is broken because 'configure soft' may change
filters but keep routes */
if (old)
{
if (p->out_filter == FILTER_REJECT)
......@@ -216,6 +224,7 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old,
}
}
/* FIXME - This is broken because of incorrect 'old' value (see above) */
if (!new && !old)
return;
......@@ -1122,6 +1131,11 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
ok = 0;
else if (!ic && d->export_mode > 1)
{
/* FIXME - this shows what should be exported according
to current filters, but not what was really exported.
'configure soft' command may change the export filter
and do not update routes */
if (p1->out_filter == FILTER_REJECT ||
p1->out_filter && f_run(p1->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)
ok = 0;
......
......@@ -676,6 +676,17 @@ bgp_neigh_notify(neighbor *n)
}
}
static int
bgp_reload_routes(struct proto *P)
{
struct bgp_proto *p = (struct bgp_proto *) P;
if (!p->conn || !p->conn->peer_refresh_support)
return 0;
bgp_schedule_packet(p->conn, PKT_ROUTE_REFRESH);
return 1;
}
static void
bgp_start_locked(struct object_lock *lock)
{
......@@ -792,6 +803,7 @@ bgp_init(struct proto_config *C)
P->rte_better = bgp_rte_better;
P->import_control = bgp_import_control;
P->neigh_notify = bgp_neigh_notify;
P->reload_routes = bgp_reload_routes;
p->cf = c;
p->local_as = c->local_as;
p->remote_as = c->remote_as;
......
......@@ -29,6 +29,7 @@ struct bgp_config {
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] */
u32 rr_cluster_id; /* Route reflector cluster ID, if different from local ID */
int rr_client; /* Whether neighbor is RR client of me */
......@@ -66,6 +67,7 @@ struct bgp_conn {
int start_state; /* protocol start_state snapshot when connection established */
int want_as4_support; /* Connection tries to establish AS4 session */
int peer_as4_support; /* Peer supports 4B AS numbers [RFC4893] */
int peer_refresh_support; /* Peer supports route refresh [RFC2918] */
unsigned hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */
};
......@@ -202,6 +204,7 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
#define PKT_UPDATE 0x02
#define PKT_NOTIFICATION 0x03
#define PKT_KEEPALIVE 0x04
#define PKT_ROUTE_REFRESH 0x05
#define PKT_SCHEDULE_CLOSE 0x1f /* Used internally to schedule socket close */
/* Attributes */
......
......@@ -23,7 +23,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY, SOURCE, ADDRESS,
PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4, ADVERTISE, IPV4,
CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER, MISSING, LLADDR,
DROP, IGNORE)
DROP, IGNORE, ROUTE, REFRESH)
CF_GRAMMAR
......@@ -40,6 +40,7 @@ bgp_proto_start: proto_start BGP {
BGP_CFG->error_amnesia_time = 300;
BGP_CFG->error_delay_time_min = 60;
BGP_CFG->error_delay_time_max = 300;
BGP_CFG->enable_refresh = 1;
BGP_CFG->enable_as4 = bgp_as4_support;
BGP_CFG->capabilities = 2;
BGP_CFG->advertise_ipv4 = 1;
......@@ -77,6 +78,7 @@ bgp_proto:
| bgp_proto ERROR FORGET TIME expr ';' { BGP_CFG->error_amnesia_time = $5; }
| bgp_proto ERROR WAIT TIME expr ',' expr ';' { BGP_CFG->error_delay_time_min = $5; BGP_CFG->error_delay_time_max = $7; }
| bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; }
| bgp_proto ENABLE ROUTE REFRESH bool ';' { BGP_CFG->enable_refresh = $5; }
| bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; }
| bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
| bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; }
......
......@@ -63,6 +63,14 @@ bgp_put_cap_ipv4(struct bgp_conn *conn UNUSED, byte *buf)
}
#endif
static byte *
bgp_put_cap_rr(struct bgp_conn *conn UNUSED, byte *buf)
{
*buf++ = 2; /* Capability 2: Support for route refresh */
*buf++ = 0; /* Capability data length */
return buf;
}
static byte *
bgp_put_cap_as4(struct bgp_conn *conn, byte *buf)
{
......@@ -105,6 +113,9 @@ bgp_create_open(struct bgp_conn *conn, byte *buf)
cap = bgp_put_cap_ipv6(conn, cap);
#endif
if (p->cf->enable_refresh)
cap = bgp_put_cap_rr(conn, cap);
if (conn->want_as4_support)
cap = bgp_put_cap_as4(conn, cap);
......@@ -386,6 +397,24 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
#endif
static byte *
bgp_create_route_refresh(struct bgp_conn *conn, byte *buf)
{
struct bgp_proto *p = conn->bgp;
BGP_TRACE(D_PACKETS, "Sending ROUTE-REFRESH");
#ifdef IPV6
*buf++ = 0; /* AFI IPv6 */
*buf++ = BGP_AF_IPV6;
#else
*buf++ = 0; /* AFI IPv4 */
*buf++ = BGP_AF_IPV4;
#endif
*buf++ = 0; /* RFU */
*buf++ = 1; /* and SAFI 1 */
return buf;
}
static void
bgp_create_header(byte *buf, unsigned int len, unsigned int type)
{
......@@ -447,6 +476,12 @@ bgp_fire_tx(struct bgp_conn *conn)
type = PKT_OPEN;
end = bgp_create_open(conn, pkt);
}
else if (s & (1 << PKT_ROUTE_REFRESH))
{
s &= ~(1 << PKT_ROUTE_REFRESH);
type = PKT_ROUTE_REFRESH;
end = bgp_create_route_refresh(conn, pkt);
}
else if (s & (1 << PKT_UPDATE))
{
end = bgp_create_update(conn, pkt);
......@@ -517,6 +552,11 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len)
switch (opt[0])
{
case 2:
if (cl != 0)
goto err;
conn->peer_refresh_support = 1;
break;
case 65:
if (cl != 4)
goto err;
......@@ -1084,6 +1124,30 @@ bgp_rx_keepalive(struct bgp_conn *conn)
}
}
static void
bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, int len)
{
struct bgp_proto *p = conn->bgp;
BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH");
if (conn->state != BS_ESTABLISHED)
{ bgp_error(conn, 5, 0, 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; }
/* FIXME - we ignore AFI/SAFI values, as we support
just one value and even an error code for an invalid
request is not defined */
proto_request_feeding(&p->p);
}
/**
* bgp_rx_packet - handle a received packet
* @conn: BGP connection
......@@ -1103,6 +1167,7 @@ bgp_rx_packet(struct bgp_conn *conn, byte *pkt, unsigned 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);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment