Files
bgp/tests/integration_tests/tests/basic_startup.rs
Rayhaan Jaufeerally e03df6176b
Some checks are pending
Rust / build (push) Waiting to run
Implemented basic input filtering.
2024-07-20 16:58:19 +00:00

641 lines
20 KiB
Rust

// Copyright 2021 Rayhaan Jaufeerally.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::io::{Read, Write};
use std::mem::size_of;
use std::net::Ipv4Addr;
use std::net::Ipv6Addr;
use std::net::TcpListener;
use std::net::TcpStream;
use std::net::{IpAddr, SocketAddrV6};
use std::os::unix::io::AsRawFd;
use std::time::Duration;
use tokio_util::codec::Decoder;
use tracing::info;
use bgp_packet::constants::{AddressFamilyIdentifier, SubsequentAddressFamilyIdentifier};
use bgp_packet::messages::BGPSubmessage;
use bgp_packet::traits::ParserContext;
use bgp_server::bgp_server::Server;
use bgp_server::config::{PeerConfig, PrefixAnnouncement, ServerConfig};
use bgp_server::route_server::route_server::bgp_server_admin_service_client::BgpServerAdminServiceClient;
use bgp_server::route_server::route_server::PeerStatusRequest;
#[macro_use]
extern crate serial_test;
fn init() {
match tracing_subscriber::fmt()
// .with_env_filter("server=trace,tokio=trace,basic_startup=trace")
.try_init()
{
Ok(()) => {}
Err(e) => {
eprintln!("Failed to setup tracing: {}", e);
}
}
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_bgp_listener_simple() {
init();
let sc = ServerConfig {
asn: 65535,
hold_time: 10,
identifier: Ipv4Addr::new(127, 0, 0, 1),
grpc_addr: None,
http_addr: None,
listen_addrs: vec!["[::]:9179".to_owned()],
peers: vec![],
};
let mut bgp_server = Server::new(sc);
bgp_server.start(true).await.unwrap();
// Try to connect to localhost:9179 and it should connect.
assert!(TcpStream::connect("[::1]:9179").is_ok());
bgp_server.shutdown().await;
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_bgp_listener_unknown_peer() {
init();
let sc = ServerConfig {
asn: 65535,
hold_time: 10,
identifier: Ipv4Addr::new(127, 0, 0, 1),
grpc_addr: None,
http_addr: None,
listen_addrs: vec!["[::]:9179".to_owned()],
peers: vec![],
};
let mut bgp_server = Server::new(sc);
bgp_server.start(true).await.unwrap();
// Try to connect to localhost:9179 and it should connect.
let conn = TcpStream::connect_timeout(&"[::1]:9179".parse().unwrap(), Duration::from_secs(3));
assert!(conn.is_ok());
let open_msg_bytes: &[u8] = &[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x39, 0x01, 0x04, 0x00, 0x2a, 0x00, 0xb4, 0xd4, 0x19, 0x16, 0x26, 0x1c, 0x02,
0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, 0x02, 0x02, 0x80, 0x00, 0x02, 0x02, 0x02, 0x00,
0x02, 0x02, 0x46, 0x00, 0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0x00, 0x2a,
];
assert!(conn.as_ref().unwrap().write(open_msg_bytes).is_ok());
let mut buf = Vec::with_capacity(256);
assert_eq!(conn.unwrap().read(&mut buf).unwrap(), 0);
bgp_server.shutdown().await;
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_bgp_listener_known_peer() {
init();
let v6_addr: Ipv6Addr = "::1".parse().unwrap();
let sc = ServerConfig {
asn: 65535,
hold_time: 10,
identifier: Ipv4Addr::new(127, 0, 0, 1),
grpc_addr: None,
http_addr: None,
listen_addrs: vec!["[::]:9179".to_owned()],
peers: vec![PeerConfig {
afi: AddressFamilyIdentifier::Ipv6,
safi: SubsequentAddressFamilyIdentifier::Unicast,
asn: 8758,
ip: IpAddr::V6(v6_addr),
announcements: vec![],
name: "local-test-peer".to_string(),
local_pref: 100,
port: None,
filter_in: Vec::default(),
filter_out: Vec::default(),
}],
};
let mut bgp_server = Server::new(sc);
bgp_server.start(true).await.unwrap();
// Try to connect to localhost:9179 and it should connect.
let mut conn =
TcpStream::connect_timeout(&"[::1]:9179".parse().unwrap(), Duration::from_secs(3)).unwrap();
// Make the stream blocking to be able to handle it easily in tests.
conn.set_nonblocking(false).unwrap();
conn.set_read_timeout(None).unwrap();
let open_msg_bytes: &[u8] = &[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x35, 0x01, 0x04, 0x22, 0x36, 0x00, 0xb4, 0xd4, 0x19, 0x1b, 0x2d, 0x18, 0x02,
0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x80, 0x00,
0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0x22, 0x36,
];
assert!(conn.write_all(open_msg_bytes).is_ok());
let mut open_buf = vec![0u8; 65536];
conn.read(&mut open_buf).unwrap();
let mut codec = bgp_packet::messages::Codec {
ctx: ParserContext {
four_octet_asn: None,
nlri_mode: None,
},
};
let response_open_msg = codec
.decode(&mut bytes::BytesMut::from(open_buf.as_slice()))
.unwrap();
info!("Response message is: {:?}", response_open_msg);
match response_open_msg.unwrap().payload {
BGPSubmessage::OpenMessage(_open) => {}
_ => {
assert!(false);
}
}
// Check that the server sends a keepalive after the open message.
let mut ka_buf = vec![0u8; 65536];
conn.read(&mut ka_buf).unwrap();
let response_ka_message = codec
.decode(&mut bytes::BytesMut::from(ka_buf.as_slice()))
.unwrap();
match response_ka_message.unwrap().payload {
BGPSubmessage::KeepaliveMessage(_ka) => {}
_ => {
assert!(false);
}
}
bgp_server.shutdown().await;
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_bgp_peer_statemachine_outbound_conn() {
init();
let v6_addr: Ipv6Addr = "::1".parse().unwrap();
// Listen on some arbitrary port and put that port into the config for the server to dial out to.
let listener = TcpListener::bind("[::1]:0".parse::<SocketAddrV6>().unwrap()).unwrap();
info!("Listener is listening on: {:?}", listener.local_addr());
let port: u16 = listener.local_addr().unwrap().port();
let sc = ServerConfig {
asn: 65535,
hold_time: 10,
identifier: Ipv4Addr::new(127, 0, 0, 1),
grpc_addr: None,
http_addr: None,
listen_addrs: vec!["[::]:9179".to_owned()],
peers: vec![PeerConfig {
afi: AddressFamilyIdentifier::Ipv6,
safi: SubsequentAddressFamilyIdentifier::Unicast,
asn: 8758,
ip: IpAddr::V6(v6_addr),
port: Some(port),
announcements: vec![],
name: "local-test-peer".to_string(),
local_pref: 100,
filter_in: Vec::default(),
filter_out: Vec::default(),
}],
};
let mut bgp_server = Server::new(sc);
bgp_server.start(true).await.unwrap();
// Wait for the connection from the bgp_server.
info!("Waiting for connection in test");
let (mut conn, _) = listener.accept().unwrap();
info!("Got a connection in test");
let open_msg_bytes: &[u8] = &[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x35, 0x01, 0x04, 0x22, 0x36, 0x00, 0xb4, 0xd4, 0x19, 0x1b, 0x2d, 0x18, 0x02,
0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x80, 0x00,
0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0x22, 0x36,
];
assert!(conn.write_all(open_msg_bytes).is_ok());
let mut open_buf = vec![0u8; 65536];
conn.read(&mut open_buf).unwrap();
let mut codec = bgp_packet::messages::Codec {
ctx: ParserContext {
four_octet_asn: None,
nlri_mode: None,
},
};
let response_open_msg = codec
.decode(&mut bytes::BytesMut::from(open_buf.as_slice()))
.unwrap();
info!("Response message is: {:?}", response_open_msg);
match response_open_msg.unwrap().payload {
BGPSubmessage::OpenMessage(_open) => {}
_ => {
assert!(false);
}
}
// Check that the server sends a keepalive after the open message.
let mut ka_buf = vec![0u8; 65536];
conn.read(&mut ka_buf).unwrap();
let response_ka_message = codec
.decode(&mut bytes::BytesMut::from(ka_buf.as_slice()))
.unwrap();
match response_ka_message.unwrap().payload {
BGPSubmessage::KeepaliveMessage(_ka) => {}
_ => {
assert!(false);
}
}
bgp_server.shutdown().await;
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
// Check that reconnecting to a connection that was previously established works.
async fn test_bgp_peer_statemachine_outbound_reconnection() {
init();
let v6_addr: Ipv6Addr = "::1".parse().unwrap();
// Listen on some arbitrary port and put that port into the config for the server to dial out to.
let listener = TcpListener::bind("[::1]:0".parse::<SocketAddrV6>().unwrap()).unwrap();
info!("Listener is listening on: {:?}", listener.local_addr());
let port: u16 = listener.local_addr().unwrap().port();
let sc = ServerConfig {
asn: 65535,
hold_time: 10,
identifier: Ipv4Addr::new(127, 0, 0, 1),
grpc_addr: None,
http_addr: None,
listen_addrs: vec!["[::]:9179".to_owned()],
peers: vec![PeerConfig {
afi: AddressFamilyIdentifier::Ipv6,
safi: SubsequentAddressFamilyIdentifier::Unicast,
asn: 8758,
ip: IpAddr::V6(v6_addr),
port: Some(port),
announcements: vec![],
name: "local-test-peer".to_string(),
local_pref: 100,
filter_in: Vec::default(),
filter_out: Vec::default(),
}],
};
let mut bgp_server = Server::new(sc);
bgp_server.start(true).await.unwrap();
// Wait for the connection from the bgp_server.
info!("Waiting for connection in test");
let (mut conn, _) = listener.accept().unwrap();
info!("Got a connection in test");
let open_msg_bytes: &[u8] = &[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x35, 0x01, 0x04, 0x22, 0x36, 0x00, 0xb4, 0xd4, 0x19, 0x1b, 0x2d, 0x18, 0x02,
0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x80, 0x00,
0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0x22, 0x36,
];
assert!(conn.write_all(open_msg_bytes).is_ok());
let mut open_buf = vec![0u8; 65536];
conn.read(&mut open_buf).unwrap();
let mut codec = bgp_packet::messages::Codec {
ctx: ParserContext {
four_octet_asn: None,
nlri_mode: None,
},
};
let response_open_msg = codec
.decode(&mut bytes::BytesMut::from(open_buf.as_slice()))
.unwrap();
info!("Response message is: {:?}", response_open_msg);
match response_open_msg.unwrap().payload {
BGPSubmessage::OpenMessage(_open) => {}
_ => {
assert!(false);
}
}
// Check that the server sends a keepalive after the open message.
let mut ka_buf = vec![0u8; 65536];
conn.read(&mut ka_buf).unwrap();
let response_ka_message = codec
.decode(&mut bytes::BytesMut::from(ka_buf.as_slice()))
.unwrap();
match response_ka_message.unwrap().payload {
BGPSubmessage::KeepaliveMessage(_ka) => {}
_ => {
assert!(false);
}
}
conn.shutdown(std::net::Shutdown::Both).unwrap();
// Expect that the other side reconnects to re-establish the connection.
info!("Waiting for re-connection in test");
let (mut conn, _) = listener.accept().unwrap();
info!("Got the re-connection in test");
let open_msg_bytes: &[u8] = &[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x35, 0x01, 0x04, 0x22, 0x36, 0x00, 0xb4, 0xd4, 0x19, 0x1b, 0x2d, 0x18, 0x02,
0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x80, 0x00,
0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0x22, 0x36,
];
assert!(conn.write_all(open_msg_bytes).is_ok());
let mut open_buf = vec![0u8; 65536];
conn.read(&mut open_buf).unwrap();
let mut codec = bgp_packet::messages::Codec {
ctx: ParserContext {
four_octet_asn: None,
nlri_mode: None,
},
};
let response_open_msg = codec
.decode(&mut bytes::BytesMut::from(open_buf.as_slice()))
.unwrap();
info!("Response message is: {:?}", response_open_msg);
match response_open_msg.unwrap().payload {
BGPSubmessage::OpenMessage(_open) => {}
_ => {
assert!(false);
}
}
// Check that the server sends a keepalive after the open message.
let mut ka_buf = vec![0u8; 65536];
conn.read(&mut ka_buf).unwrap();
let response_ka_message = codec
.decode(&mut bytes::BytesMut::from(ka_buf.as_slice()))
.unwrap();
match response_ka_message.unwrap().payload {
BGPSubmessage::KeepaliveMessage(_ka) => {}
_ => {
assert!(false);
}
}
bgp_server.shutdown().await;
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_bgp_listener_known_peer_inbound_reconnection() {
init();
let v6_addr: Ipv6Addr = "::1".parse().unwrap();
let sc = ServerConfig {
asn: 65535,
hold_time: 10,
identifier: Ipv4Addr::new(127, 0, 0, 1),
grpc_addr: None,
http_addr: None,
listen_addrs: vec!["[::]:9179".to_owned()],
peers: vec![PeerConfig {
afi: AddressFamilyIdentifier::Ipv6,
safi: SubsequentAddressFamilyIdentifier::Unicast,
asn: 8758,
ip: IpAddr::V6(v6_addr),
announcements: vec![],
name: "local-test-peer".to_string(),
local_pref: 100,
port: None,
filter_in: Vec::default(),
filter_out: Vec::default(),
}],
};
let mut bgp_server = Server::new(sc);
bgp_server.start(true).await.unwrap();
// Try to connect to localhost:9179 and it should connect.
let mut conn =
TcpStream::connect_timeout(&"[::1]:9179".parse().unwrap(), Duration::from_secs(3)).unwrap();
// Make the stream blocking to be able to handle it easily in tests.
conn.set_nonblocking(false).unwrap();
conn.set_read_timeout(None).unwrap();
// Unsafe set linger: simulate a broken TCP stream by setting linger with a deadline of 0.
// This causes a RST packet to be sent instead of a FIN, which means that the other side
// will exercise the error path.
unsafe {
let val: libc::linger = libc::linger {
l_onoff: 1,
l_linger: 0,
};
let ret_val = libc::setsockopt(
conn.as_raw_fd(),
libc::SOL_SOCKET,
libc::SO_LINGER,
&val as *const libc::linger as *const libc::c_void,
size_of::<libc::linger>() as libc::socklen_t,
);
assert!(ret_val == 0);
}
let open_msg_bytes: &[u8] = &[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x35, 0x01, 0x04, 0x22, 0x36, 0x00, 0xb4, 0xd4, 0x19, 0x1b, 0x2d, 0x18, 0x02,
0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x80, 0x00,
0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0x22, 0x36,
];
assert!(conn.write_all(open_msg_bytes).is_ok());
let mut open_buf = vec![0u8; 65536];
conn.read(&mut open_buf).unwrap();
let mut codec = bgp_packet::messages::Codec {
ctx: ParserContext {
four_octet_asn: None,
nlri_mode: None,
},
};
let response_open_msg = codec
.decode(&mut bytes::BytesMut::from(open_buf.as_slice()))
.unwrap();
info!("Response message is: {:?}", response_open_msg);
match response_open_msg.unwrap().payload {
BGPSubmessage::OpenMessage(_open) => {}
_ => {
assert!(false);
}
}
// Check that the server sends a keepalive after the open message.
let mut ka_buf = vec![0u8; 65536];
conn.read(&mut ka_buf).unwrap();
let response_ka_message = codec
.decode(&mut bytes::BytesMut::from(ka_buf.as_slice()))
.unwrap();
match response_ka_message.unwrap().payload {
BGPSubmessage::KeepaliveMessage(_ka) => {}
_ => {
assert!(false);
}
}
drop(conn);
// Try to connect to localhost:9179 and it should connect and send the OPEN message.
let mut conn =
TcpStream::connect_timeout(&"[::1]:9179".parse().unwrap(), Duration::from_secs(3)).unwrap();
let open_msg_bytes: &[u8] = &[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x35, 0x01, 0x04, 0x22, 0x36, 0x00, 0xb4, 0xd4, 0x19, 0x1b, 0x2d, 0x18, 0x02,
0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x80, 0x00,
0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0x22, 0x36,
];
assert!(conn.write_all(open_msg_bytes).is_ok());
let mut open_buf = vec![0u8; 65536];
conn.set_read_timeout(Some(Duration::from_secs(3))).unwrap();
conn.read(&mut open_buf).unwrap();
let mut codec = bgp_packet::messages::Codec {
ctx: ParserContext {
four_octet_asn: None,
nlri_mode: None,
},
};
let response_open_msg = codec
.decode(&mut bytes::BytesMut::from(open_buf.as_slice()))
.unwrap();
info!("Response message is: {:?}", response_open_msg);
match response_open_msg.unwrap().payload {
BGPSubmessage::OpenMessage(_open) => {}
_ => {
assert!(false);
}
}
info!("Reconnection successful");
bgp_server.shutdown().await;
}
// Spawn two instances of the BGP server and make them peer with each other and exchange a route.
// We then check that the route is accepted by the receiving peer.
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_multi_instance_announce() {
init();
let v6_addr: Ipv6Addr = "::1".parse().unwrap();
let config_a = ServerConfig {
asn: 65535,
hold_time: 3,
identifier: Ipv4Addr::new(127, 0, 0, 1),
grpc_addr: Some("[::]:9181".to_owned()),
http_addr: None,
listen_addrs: vec!["[::]:9179".to_owned()],
peers: vec![PeerConfig {
afi: AddressFamilyIdentifier::Ipv6,
safi: SubsequentAddressFamilyIdentifier::Unicast,
asn: 65536,
ip: IpAddr::V6(v6_addr),
port: Some(9180),
announcements: vec![PrefixAnnouncement {
prefix: "2001:db8:babe::/48".to_owned(),
nexthop: "2001:db8::1".parse().unwrap(),
local_pref: Some(100),
med: Some(100),
..Default::default()
}],
name: "config-b-peer".to_string(),
local_pref: 100,
filter_in: Vec::default(),
filter_out: Vec::default(),
}],
};
let mut config_b = config_a.clone();
config_b.asn = 65536;
config_b.listen_addrs = vec!["[::]:9180".to_owned()];
config_b.grpc_addr = Some("[::]:9182".to_owned());
config_b.peers[0].asn = 65535;
config_b.peers[0].port = Some(9179);
config_b.peers[0].name = "config-a-peer".to_owned();
let mut server_a = Server::new(config_a);
server_a.start(true).await.unwrap();
let mut server_b = Server::new(config_b);
server_b.start(true).await.unwrap();
// Connect to the grpc endpoint of server_b and check until the peer is healthy.
let mut stub = BgpServerAdminServiceClient::connect("http://[::1]:9182")
.await
.unwrap();
let mut established = false;
for _ in 1..10 {
let response = stub
.peer_status(PeerStatusRequest::default())
.await
.unwrap()
.into_inner();
info!(?response);
if response.peer_status[0].state == "Established" {
established = true;
}
tokio::time::sleep(Duration::from_secs(1)).await;
}
assert!(established);
}