mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-17 08:38:42 +00:00
Implements BGP route refresh.
This commit is contained in:
parent
5e6f568115
commit
bf47fe4b2e
@ -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]])
|
||||
|
67
nest/proto.c
67
nest/proto.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user