Files
ferrix/crates/server/proto/config.proto
T
Rayhaan JaufeerallyandClaude Opus 5.5 72444c2706 Control communities: session groups, prepends, and only ours to give
Control communities could say which peers a route goes to, by AS. Now
they can also name sessions by group, and ask for prepends:

  L:11:P .. L:13:P   our AS once to three times more towards AS P
                     (P 0: towards every peer in another AS)
  L:20:G / L:21:G    not to / only to the sessions of group G
  L:31:G .. L:33:G   our AS once to three times more towards group G

A group is a number given to sessions in the configuration (`groups` of
a peer): those at one exchange, or those over one link. The same network
at two exchanges is one AS but two sessions, which is what steering
inbound traffic between exchanges needs. Of several prepends that apply
the most counts, on top of the session's own as_path_prepend. Functions
2 to 9, as the documentation already promised, and 100 and up stay
informational; 10 to 99 are kept for actions to come.

They are acted on where a route leaves our AS, and taken off there; our
informational communities are not. To a peer in our own AS a route goes
with them, unchanged, so that a mark put on at one router is acted on at
another. Before, they were acted on and taken off on internal sessions
too, so a route marked "to nobody" did not even reach our own routers.

And they are ours to give: the communities of our own AS (L:n, L:n:m,
and 0:n where control communities are on) are taken off every route
from a peer in another AS as it comes in, before import marks are put
on. A peer at an exchange could otherwise decide where its routes go
from us, and set our informational marks. A customer that is to steer
its own routes is given import.accept_control_communities. An UPDATE
without any of ours is still taken as it came, without being written
out again.

This changes what a customer that set control communities gets: it now
needs accept_control_communities. The transit example and the interop
lab say so; the lab also has FRR, as a customer, ask for a prepend
towards BIRD's group, and BIRD, as a provider, have its "not to" taken
off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 16:12:05 +02:00

489 lines
21 KiB
Protocol Buffer

syntax = "proto3";
package bgp.server;
// Top level BGP Server configuration.
message BgpConfig {
// The router ID, written as a dotted-quad IPv4 address (e.g. "192.0.2.1").
// Required even on IPv6-only routers per RFC 4271.
string router_id = 1;
// The local 4-byte AS number (RFC 6793).
uint32 local_asn = 2;
// Endpoints to accept incoming BGP connections on.
// Defaults to 0.0.0.0:179 and [::]:179 when empty.
repeated TcpEndpoint listen_endpoints = 3;
// Peers configured on this server.
repeated Peer peers = 4;
// Routes this router originates and announces to every peer.
repeated OriginatedRoute originate = 5;
// Where to serve the HTTP API, status page and metrics. The port is
// required. Unset disables the HTTP server.
TcpEndpoint http_endpoint = 6;
// Initial delay between attempts to connect to a configured peer, in
// seconds. Doubles after each failure up to two minutes. Defaults to 5.
uint32 connect_retry_seconds = 7;
// Connection collisions (RFC 4271 §6.8) against a session that is already
// Established are, by default, resolved by closing the new connection.
// When true they are resolved like any other collision: the connection
// initiated by the higher BGP Identifier survives, and a second connection
// opened by the same side replaces the first. This lets a peer that
// restarted take over from a session we have not yet noticed is dead,
// instead of being refused until the hold timer expires.
bool collision_detect_established_state = 8;
// Connections are accepted only from the addresses of configured peers;
// from anywhere else they are closed before a byte is read. When true,
// any host that can reach a listening endpoint may open a session, as
// any AS it cares to name, and have its routes taken. That is for
// testing on a closed network and nothing else.
bool accept_unconfigured_peers = 9;
// Route origin validation (RFC 6811) and AS path verification with the
// RPKI. Left out, routes are not validated.
Rpki rpki = 10;
// Put the best route of every prefix in the kernel's routing table, so
// that the machine forwards packets by them. Left out, the daemon only
// speaks BGP and the machine's forwarding is not touched.
Kernel kernel = 11;
// What is announced, and how, to every peer that has no `export` of its
// own.
ExportPolicy export = 12;
// Let routes say, with communities, which peers they are passed on to
// and how far away we look to them, as the route servers of Internet
// exchanges and transit networks do. With L our own AS number, P a
// peer's, and G a group of sessions (`groups` of a peer):
//
// 0:P or L:0:P not to the peer with AS P
// L:P or L:1:P to the peer with AS P, and then only to peers
// named so
// 0:L or L:0:0 to nobody
// L:11:P with our AS once more on the path to the peer
// L:12:P with AS P; twice, three times. P 0 is every
// L:13:P peer in another AS.
// L:20:G not to the sessions of group G
// L:21:G to the sessions of group G, and then only to
// sessions named so, by group or AS
// L:31:G with our AS once more on the path to the
// L:32:G sessions of group G; twice, three times
// L:33:G
//
// A route with none of these goes to everyone it otherwise would. "Not"
// wins over "to". Of several prepends that apply, the most counts, on
// top of the session's export.as_path_prepend. The two-part form is a
// standard community, with room for AS numbers below 65536; the
// three-part forms are large communities and work for any.
//
// They are acted on where a route leaves our AS, and taken off there:
// to a peer in our own AS a route goes with them, for that router to act
// on. They are honoured on routes we originate too (`communities` and
// `large_communities` of `originate`), on what import.add_communities
// puts on, and on what a peer in our own AS, or one with
// import.accept_control_communities, sends. From any other peer the
// communities of our own AS are taken off as its routes come in, so that
// nobody outside can say what is done with our routes.
//
// With this on, standard communities L:n are taken for "to the peer
// with AS n": mark routes with something else (large communities
// L:100:n and up, say; functions 2 to 9 are left for marks too).
bool control_communities = 13;
// Graceful restart, for every peer that has no `graceful_restart` of its
// own. Left out, it is on, as the defaults below have it.
GracefulRestart graceful_restart = 14;
}
// Graceful restart (RFC 4724): BGP restarting without the routes going.
//
// When the daemon is restarted in good order for an upgrade (on SIGUSR1),
// it leaves its routes in the kernel, and its peers keep the routes it
// announced to them, for as long as they were told a restart takes. When a
// peer restarts, its routes are kept and used as before, marked stale,
// until it has sent them again; those it does not send again are removed.
// Either way, nothing is withdrawn across the network for a restart.
//
// It is in use with a peer that offers it too. A session that ends with a
// Hard Reset (RFC 8538), or because of an error, has its routes removed at
// once, as without graceful restart.
message GracefulRestart {
// True does not offer graceful restart: a peer's routes are removed as
// soon as its session ends, and it removes ours as soon as it sees ours
// end.
optional bool disabled = 1;
// How long, in seconds, peers are told a restart of this daemon takes:
// how long they keep our routes after our session with them ends. At
// most 4095. Defaults to 120.
optional uint32 restart_time_seconds = 2;
// The longest time, in seconds, a restarting peer's routes are kept
// stale: while it is gone, whatever restart time it gave, and again once
// it is back, until it has sent all its routes. Zero keeps none.
// Defaults to 120.
optional uint32 stale_routes_seconds = 3;
}
// Installing routes in the kernel. Linux only; the daemon needs the
// CAP_NET_ADMIN capability.
//
// The best route of a prefix is installed if its next hop is a neighbor:
// an address in the network of one of the machine's interfaces. Routes
// are marked as BGP's ("proto bgp"), and every route with that mark, the
// table and the metric below is taken to be this daemon's: what it finds
// at startup and no longer wants, it removes. When the daemon stops in
// good order it removes its routes.
message Kernel {
// The routing table. Defaults to 254, the main table, which is the one
// packets are forwarded by unless rules say otherwise.
uint32 table = 1;
// The metric of the routes. Defaults to 20. Of several routes for a
// prefix the kernel uses the one with the lowest metric.
optional uint32 metric = 2;
// By default, where the kernel has nexthop objects (Linux 5.3 and
// later), a gateway is put there once as such an object and routes name
// it, which makes installing a full table about a third faster. `ip
// route` then shows routes with "nhid". True has every route carry its
// own gateway, as on older kernels.
bool without_nexthop_objects = 3;
// How long, after the daemon starts, routes of its own that an earlier
// run left in the kernel are kept though no peer has announced them
// (again). Defaults to 120. The daemon's table is empty until its peers
// have sent theirs; removing the old routes at once would stop the
// machine forwarding for as long as that takes. After this time, what
// was not announced again is removed.
// Zero removes them at once.
optional uint32 stale_seconds = 4;
}
// Where to get the validated ROA payloads that say which AS may originate
// which prefixes. With this set, a route whose origin is not authorised
// for its prefix ("invalid") is refused, from every peer that is not in
// our own AS and does not have accept_rpki_invalid set. Routes that
// nothing is known about are taken as before.
//
// A validator that speaks version 2 of the protocol also gives the ASPAs,
// in which networks say who their providers are. With those, a route
// whose AS path cannot have come about by networks passing routes on as
// they should (a route leak, or a made-up path) is refused, from every
// peer that has a role set and does not have accept_aspa_invalid set.
message Rpki {
// The RPKI-to-Router servers (RFC 8210, draft-ietf-sidrops-8210bis) of
// one or more validators, such as Routinator, rpki-client with StayRTR,
// or FORT, in order of preference. The port is required. One is used at
// a time, over plain TCP: keep it on a network you trust.
repeated TcpEndpoint caches = 1;
// How often to ask the cache for changes unprompted, how long to wait
// before trying again after a failure, and how long the data may be used
// without a cache confirming it, after which nothing is invalid any
// more. Each defaults to what the cache says, or to 3600, 600 and 7200.
uint32 refresh_seconds = 2;
uint32 retry_seconds = 3;
uint32 expire_seconds = 4;
}
// A route originated by this router. It is announced with an AS_PATH of the
// local AS and, on each session, that session's local address as next hop.
message OriginatedRoute {
// The prefix in CIDR notation, e.g. "192.0.2.0/24" or "2001:db8::/32".
string prefix = 1;
// ORIGIN attribute: "IGP" (the default), "EGP" or "INCOMPLETE".
string origin = 2;
// Extra copies of the local AS to prepend to the AS_PATH. At most 16.
uint32 as_path_prepend = 3;
// MULTI_EXIT_DISC. Left out of the announcement when unset.
optional uint32 med = 4;
// Standard communities (RFC 1997): "ASN:VALUE", or a well-known name such
// as "NO_EXPORT".
repeated string communities = 5;
// Large communities (RFC 8092): "GLOBAL:LOCAL1:LOCAL2".
repeated string large_communities = 6;
}
// An IP address and port pair.
message TcpEndpoint {
// IP address (V4 or V6). A peer at a link-local IPv6 address has the
// interface it is on after it: "fe80::1%eth0".
string address = 1;
// TCP port. Defaults to 179 for BGP when unset.
uint32 port = 2;
}
// Configuration of a BGP peer.
message Peer {
// Human-readable name for logging and diagnostics.
string name = 1;
// Address and port to connect to for this peer.
TcpEndpoint endpoint = 2;
// The peer's 4-byte AS number (RFC 6793).
uint32 remote_asn = 3;
// Hold time to advertise in the OPEN message, in seconds (RFC 4271 §4.2).
// At least 3, or 0 for a session without keepalives that is never timed
// out. Defaults to 90 when unset.
optional uint32 hold_time_seconds = 4;
// Source IP address for connections we open to this peer. It is also the
// next hop of the routes we send it. Required when the peer identifies us
// by address and the host has several, e.g. when both speakers run on the
// same machine. Must be the same address family as the endpoint. Unset
// lets the operating system choose.
string local_address = 5;
// The address families to exchange with this peer. Each is offered to the
// peer in our OPEN, and is in use on the session only if the peer offers
// it too (RFC 4760). Defaults to all of them when empty. A session with
// no family in common is refused.
repeated AddressFamily address_families = 6;
// ADD-PATH (RFC 7911): whether to take several routes for one prefix
// from this peer, to send it several, or both. It is in use, for each
// direction and address family, only if the peer agrees. Defaults to
// off: one route for each prefix, as in plain BGP.
AddPath add_path = 7;
// Which of the peer's routes are taken. Left out, the general checks
// apply and there is no limit and no list.
ImportPolicy import = 8;
// What the peer is to us. Not for a peer in our own AS. With it set:
//
// - Our OPEN says what we are to the peer, and a peer that says it is
// something else than configured here is refused (RFC 9234). One side
// having the other down as a customer and the other as a peer is how
// leaks come about.
// - Routes are marked "only to customer" as they come in from a provider,
// a peer or a route server, and as they go out to a customer, a peer
// or a route server client. A route with that mark that comes back up
// from a customer, or across from a second peer, is a leak and is
// refused. This cannot be turned off.
// - Where rpki is configured, the AS paths of the peer's routes are
// verified against the ASPAs: a route from a customer, a peer or a
// route server must have come up through providers all the way; one
// from a provider may have come up and then down.
//
// Left out, none of this is done for the peer.
Role role = 9;
// Refuse the peer unless its OPEN says what it is. By default a peer
// that says nothing (one that does not implement RFC 9234) is taken at
// this configuration's word for it. Needs role.
bool require_role = 10;
// What is announced to the peer, and how. Left out, the top-level
// `export` holds, and without that the routes we originate are
// announced as they are.
ExportPolicy export = 11;
// Graceful restart with this peer. What is set here takes the place of
// what the top-level `graceful_restart` says.
GracefulRestart graceful_restart = 12;
// The groups the session is in, by number, for control communities to
// name sessions by (see control_communities): those at one Internet
// exchange, say, or those over one link, whatever AS the peers are. Use
// the same numbers on all routers of the AS: a route marked on one is
// acted on by another. From 1.
repeated uint32 groups = 13;
}
// What is announced to a peer.
//
// The routes we originate are always announced. Routes learned from other
// peers are passed on only if learned_routes says so, and then only as
// far as these let them:
//
// - Never back to the peer a route came from, nor to a peer whose AS is
// on its path already.
// - A route from a peer in our own AS is not passed to another such peer
// (there is no route reflection).
// - Where peers have roles (RFC 9234): a route learned from a provider, a
// peer or a route server goes on to customers only. So with roles on
// all peers, turning this on makes us transit for our customers and for
// nobody else. Without roles there is no such rule, and every route
// goes to every peer: do not turn this on for a peer without a role
// unless that is what is wanted.
// - The communities NO_EXPORT and NO_ADVERTISE, and control_communities.
//
// A route is passed on with our AS added to its path and ourselves as
// next hop, without the MED it came with. To a peer in our own AS the
// path is left as it is and LOCAL_PREF goes along. To a peer whose role
// is ROUTE_SERVER_CLIENT the route goes as a route server sends it
// (RFC 7947): path, MED and, where the client can use it, next hop
// unchanged.
message ExportPolicy {
// Pass on the best routes learned from other peers.
bool learned_routes = 1;
// Communities put on every route announced to the peer: which router
// it went through, say. "64601:100", or a well-known name.
repeated string add_communities = 2;
// Large communities put on, as "64601:1:2".
repeated string add_large_communities = 3;
// Communities taken off first: one ("64601:100"), all of an AS
// ("64601:*"), or large ones with three parts ("64601:1:*").
repeated string remove_communities = 4;
// How many more times our AS is put on the path of every route
// announced to the peer, to make them look further away. At most 16.
uint32 as_path_prepend = 5;
}
// What a peer is to us.
enum Role {
ROLE_UNSPECIFIED = 0;
// We buy transit from the peer.
PROVIDER = 1;
// The peer buys transit from us.
CUSTOMER = 2;
// The peer and we exchange our own and our customers' routes.
PEER = 3;
// The peer is an Internet exchange's route server.
ROUTE_SERVER = 4;
// We are a route server and the peer is one of our clients.
ROUTE_SERVER_CLIENT = 5;
}
// What is taken from a peer. Every route is judged before it goes into
// the routing table. One that is refused is dropped, and so is the route
// it would have replaced.
//
// A route whose AS path holds our own AS number is always refused.
message ImportPolicy {
// The most routes the peer may have at a time. One more ends the session
// with Cease / Maximum Number of Prefixes Reached, and the peer is kept
// down for hold_down_seconds. Set it somewhat above what the peer is
// expected to send. Zero is no limit.
uint32 max_prefixes = 1;
// How long the peer is kept down after going over max_prefixes. Neither
// side's connections are taken meanwhile. Defaults to 600.
uint32 hold_down_seconds = 2;
// By default, prefixes that are not routed on the Internet are refused
// (private, loopback, link-local, documentation, multicast and reserved
// space), and so are routes with a reserved, private or documentation AS
// number anywhere in their path. True takes them.
bool accept_bogons = 3;
// By default a default route (0.0.0.0/0 or ::/0) is refused. True takes
// one, as from an upstream that is asked to send it.
bool accept_default_route = 4;
// The longest prefixes taken. Default to 24 and 48, the longest that are
// routed between networks.
uint32 max_ipv4_prefix_length = 5;
uint32 max_ipv6_prefix_length = 6;
// By default the AS path of a route has to begin with the peer's own AS
// number. True takes any, as is needed for an Internet exchange's route
// server, which does not put its AS on the path.
bool accept_any_first_as = 7;
// The longest AS path taken, counted in AS numbers. Defaults to 64.
uint32 max_as_path_length = 8;
// The prefixes the peer may announce. Empty allows any. This is the
// list generated for a peer from the routing registries (with bgpq4,
// say).
repeated PrefixRange prefixes = 9;
// The AS numbers the peer's routes may originate from: the last AS of
// the path. Empty allows any.
repeated uint32 origin_asns = 10;
// By default, where route origin validation is configured (rpki), a
// route that is invalid is refused. True takes it all the same.
bool accept_rpki_invalid = 11;
// By default, where the validator gives ASPAs and the peer has a role,
// a route whose AS path they find invalid is refused. True takes it all
// the same.
bool accept_aspa_invalid = 12;
// By default a route whose AS path has an AS_SET in it is refused: sets
// are deprecated (RFC 9774), and with one nobody can say where a route
// comes from. True takes such routes.
bool accept_as_sets = 13;
// By default the next hop of a route from another network has to be the
// peer itself, so that a peer can only draw traffic to itself and not
// send it to a third party. True takes any next hop packets can be sent
// to. Not needed for a peer whose role is ROUTE_SERVER, whose routes
// have its clients as next hops.
bool accept_third_party_next_hop = 14;
// Communities put on every route taken from the peer: where it was
// learned, say, for other routers of ours and for `export` to go by.
// "64601:100", or a well-known name.
repeated string add_communities = 15;
// Large communities put on, as "64601:1:2".
repeated string add_large_communities = 16;
// Communities taken off first: one ("64601:100"), all of an AS
// ("64601:*"), or large ones with three parts ("64601:1:*"). Marks that
// mean something to us are not to be taken on a peer's word.
repeated string remove_communities = 17;
// The LOCAL_PREF of the peer's routes: of two routes for a prefix the
// one with the higher is chosen, whatever else they have. Left out, 100.
optional uint32 local_pref = 18;
// By default the communities of our own AS (L:n and L:n:m; and 0:n,
// with control_communities on) are taken off the routes of a peer in
// another AS as they come in, before add_communities puts ours on: what
// they ask of us is not for others to ask. True keeps them, for a
// customer that is to steer its own routes with control communities, or
// a route server's client. Not for a peer in our own AS, whose routes
// keep them in any case.
bool accept_control_communities = 19;
}
// A prefix, and optionally the more specific prefixes inside it.
message PrefixRange {
// In CIDR notation, e.g. "193.0.0.0/21".
string prefix = 1;
// The longest prefix inside it that is also allowed. Left out, only the
// prefix itself is.
uint32 max_length = 2;
}
// The directions ADD-PATH can be offered for.
enum AddPath {
ADD_PATH_OFF = 0;
// Accept several routes for a prefix from the peer.
ADD_PATH_RECEIVE = 1;
// Send the peer several routes for a prefix.
ADD_PATH_SEND = 2;
ADD_PATH_BOTH = 3;
}
// An address family routes can be exchanged for.
enum AddressFamily {
ADDRESS_FAMILY_UNSPECIFIED = 0;
IPV4_UNICAST = 1;
IPV6_UNICAST = 2;
}