Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/thepacketgeek/bgpd-rs

BGP Peering Tool in Rust
https://github.com/thepacketgeek/bgpd-rs

bgp rust-lang

Last synced: about 10 hours ago
JSON representation

BGP Peering Tool in Rust

Awesome Lists containing this project

README

        

# BGPd-rs

BGP service daemon built in Rust
[![Actions Status](https://github.com/thepacketgeek/bgpd-rs/workflows/cargo/badge.svg)](https://github.com/thepacketgeek/bgpd-rs/actions)

![PCAP](examples/pcap.png)

## Features
- [x] Listen for Incoming BGP sessions
- Specified peers can be an IP address or Network+Mask
- [x] Initiate outbound TCP connection to idle peers
- Will attempt connection based on configured poll interval
- [x] Negotiate OPEN Capabilities
- [x] Receive and respond to Keepalives (on hold time based interval)
- [x] Process UPDATE messages, store in RIB
- [x] Config reloading for Peer status (enable, passive, etc.)
- [ ] Update static route advertisements mid-session
- [x] CLI interface for viewing peer status, routes, etc.
- [x] Advertise routes to peers (specified from API and/or Config)
- [x] API/CLI interface for interacting with BGPd
- [x] Flowspec Support
- [ ] Route Refresh
- [ ] Neighbor MD5 Authentication
- [ ] Route Policy for filtering of learned & advertised routes

# Peer config
Peers and their config are defined in `TOML` format; see an example [here](examples/config.toml).

Details of config values:
```toml
router_id = "1.1.1.1" # Default Router ID for the service
default_as = 65000 # Used as the local-as if `local_as` is not defined for a peer
bgp_socket = "127.0.0.1:1179" # BGP address & port
api_socket = "0.0.0.0:8080" # API address & port [Listen on all interfaces (IPv4 & IPv6)]

[[peers]]
remote_ip = "127.0.0.2" # This can also be an IPv6 address, see next peer
# remote_ip = "10.0.0.0/24" # Network+Mask will accept inbound connections from any source in the subnet
remote_as = 65000
passive = true # If passive, bgpd won't attempt outbound connections
router_id = "127.0.0.1" # Can override local Router ID for this peer
hold_timer = 90 # Set the hold timer for the peer, defaults to 180 seconds
families = [ # Define the families this session should support
"ipv4 unicast",
"ipv6 unicast",
]
[[peers.static_routes]] # Add static routes (advertised at session start)
prefix = "9.9.9.0/24"
next_hop = "127.0.0.1"
[[peers.static_routes]]
prefix = "3001:100::/64"
next_hop = "3001:1::1"
[[peers.static_flows]] # Add static Flowspec rules too!
afi = 2
action = "traffic-rate 24000"
matches= [
"source 3001:100::/56",
"destination-port >8000 <=8080",
"packet-length >100",
]
as_path = ["65000", "500"]
communities = ["101", "202", "65000:99"]

[[peers]]
remote_ip = "::2"
enabled = false # Peer is essentially de-configured
remote_as = 100
local_as = 200
families = [
"ipv6 unicast",
]
```

You can send the BGPd process a `SIGHUP` [E.g. `pkill -HUP bgpd$`] to reload and update peer configs. The following items can be updated:

## Peers
- Added & removed
- Enabled/disabled
- Active/passive polling for idle peers
- *Hold Timer
- *Supported Families

> * When not in an active session only, since these are negotiated in the OPEN

# View BGPd Information
BGPd offers an JSON RCP API that can be queried to view operational info like neighbors and routes:

Neighbor uptime & prefixes received
```sh
$ curl localhost:8080 -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"show_peers","params":null,"id":0}' | jq '.result[] | {peer: .peer, uptime: .uptime, prefixes_received: .prefixes_received}'
{
"peer": "127.0.0.2",
"uptime": "00:31:13",
"prefixes_received": 4
}
{
"peer": "127.0.0.3",
"uptime": null,
"prefixes_received": null
}
{
"peer": "172.16.20.2",
"uptime": "00:31:20",
"prefixes_received": 2
}
```

Learned routes (with attributes)
```sh
$ curl localhost:8080 -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"show_routes_learned","params": {"from_peer": "172.16.20.2"},"id":0}' | jq '.result[]'
{
"afi": "IPv6",
"age": "00:00:38",
"as_path": "",
"communities": [],
"local_pref": 100,
"multi_exit_disc": null,
"next_hop": "::ffff:172.16.20.2",
"origin": "IGP",
"prefix": "3001:172:16:20::/64",
"received_at": 1572898659,
"safi": "Unicast",
"source": "172.16.20.2"
}
{
"afi": "IPv4",
"age": "00:00:38",
"as_path": "",
"communities": [],
"local_pref": 100,
"multi_exit_disc": null,
"next_hop": "172.16.20.2",
"origin": "IGP",
"prefix": "172.16.20.0/24",
"received_at": 1572898659,
"safi": "Unicast",
"source": "172.16.20.2"
}
```

The `bgpd` [CLI](src/cli/mod.rs) can also be used to view peer & route information via the BGPd API (and announce routes too!)

# Development
I'm currently using [ExaBGP](https://github.com/Exa-Networks/exabgp) (Python) and [GoBGP](https://github.com/osrg/gobgp) (Go) to act as my BGP peers for testing.
- Here's an [intro article](https://thepacketgeek.com/influence-routing-decisions-with-python-and-exabgp/) about installing & getting started with ExaBGP.

## ExaBGP setup
With ExaBGP installed, you can use a config from the `examples/exabgp` dir:

**conf_127.0.0.2.ini**
```ini
neighbor 127.0.0.1 {
router-id 2.2.2.2;
local-address 127.0.0.2; # Our local update-source
local-as 65000; # Our local AS
peer-as 65000; # Peer's AS

announce {
ipv4 {
unicast 2.100.0.0/24 next-hop self med 500 extended-community [ target:65000:1.1.1.1 ];
unicast 2.200.0.0/24 next-hop self as-path [ 100 200 ];
unicast 2.10.0.0/24 next-hop self med 10 community [404 65000:10];
}
}
}
```

Running the exabgp service with the command:

```sh
$ env exabgp.tcp.port=1179 exabgp.tcp.bind="127.0.0.2" exabgp ./conf_127.0.0.2.ini --once
```
> *--once only attempts a single connection, auto-quits when session ends*

## Gobgp
With GoBGP installed, you can use a config from the `examples/gobgp` dir:

**gobgpd.toml**
```toml
[global.config]
as = 65000
router-id = "4.4.4.4"
port = 1179
local-address-list = ["127.0.0.4"]

[[neighbors]]
[neighbors.config]
neighbor-address = "127.0.0.1"
peer-as = 65000
[neighbors.transport.config]
passive-mode = false
local-address = "127.0.0.4"
remote-port = 1179
[neighbors.timers.config]
connect-retry = 5
hold-time = 30
keepalive-interval = 10
```

Running the gobgpd service with the command:

```sh
$ gobgpd -f ./examples/gobgp/gobgpd.toml
```

## BGPd Setup
Start the bgpd-rs daemon running `bgpd` with a provided config as follows:

```sh
$ cargo run -- run ./examples/config.toml -vv
```

You may notice that I'm using TCP port 1179 in the example config for testing, if you want/need to use TCP 179 for testing with a peer that can't change the port (*cough*Cisco*cough*), you need to run bgpd with sudo permissions:

```sh
$ cargo build --release
$ sudo ./targets/release/bgpd run ./examples/config.toml -vv
```

# Thanks to
- [bgp-rs](https://github.com/DevQps/bgp-rs) for the BGP Message Parsing
- [tokio](https://tokio.rs/) for the Runtime
- [ParityTech](https://github.com/paritytech/jsonrpsee) for the JSON RPC API