Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# Change Log
- 0.0.8
- Added 4-byte ASN support (RFC 6793)
- Added COMMUNITY path attribute parsing (RFC 1997)
- Added LARGE_COMMUNITY path attribute parsing (RFC 8092)
- Added timestamp to log messages
- Fixed header include guard issue causing build failures in CI

- 0.0.7
- Added automatic reconnection with exponential backoff (`-R` flag)
- Added output queue with dedicated writer thread to prevent keepalive stalls on slow stdout
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ BGPSee is a multi-threaded BGP client for the CLI. Its goal is to allow you to q

# Version

Current version is **0.0.7**
Current version is **0.0.8**

Major changes from **0.0.6** to **0.0.7**:
- Automatic reconnection with exponential backoff (`-R` flag)
- Output queue for reliable keepalive handling on slow connections
- Hold time negotiation per RFC 4271
- FSM race condition fixes
Major changes from **0.0.7** to **0.0.8**:
- 4-byte ASN support (RFC 6793)
- COMMUNITY path attribute parsing (RFC 1997)
- LARGE_COMMUNITY path attribute parsing (RFC 8092)
- Timestamp added to log messages

See the [CHANGELOG](CHANGELOG.md) for further information.

Expand Down
46 changes: 35 additions & 11 deletions src/bgp.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

struct bgp_instance {
uint8_t version;
uint16_t local_asn;
uint32_t local_asn;
uint32_t local_rid;
int n_peers;
struct bgp_peer *peers[MAX_BGP_PEERS];
Expand Down Expand Up @@ -162,7 +162,7 @@ int setup_reconnect_timer(struct bgp_peer *peer) {
*/


struct bgp_instance *create_bgp_instance(uint16_t local_asn, uint32_t local_rid, uint8_t version) {
struct bgp_instance *create_bgp_instance(uint32_t local_asn, uint32_t local_rid, uint8_t version) {
struct bgp_instance *i;

log_print(LOG_DEBUG, "Creating new peer (ASN %d, RID: %d, Version: %d)\n", local_asn, local_rid, version);
Expand Down Expand Up @@ -239,7 +239,7 @@ struct bgp_peer *get_peer_from_instance(struct bgp_instance *i, unsigned int id)


//TODO: Need to fix unsigned here, as we need to be able to signal failure of the function
unsigned int create_bgp_peer(struct bgp_instance *i, const char *peer_ip, const uint16_t peer_asn, const char *peer_name) {
unsigned int create_bgp_peer(struct bgp_instance *i, const char *peer_ip, const uint32_t peer_asn, const char *peer_name) {
struct bgp_peer *peer;
unsigned int new_id;

Expand Down Expand Up @@ -309,6 +309,7 @@ unsigned int create_bgp_peer(struct bgp_instance *i, const char *peer_ip, const
peer->socket.fd = -1;

peer->peer_asn = peer_asn;
peer->four_octet_asn = 0;

//Init the stdout lock and the output function
pthread_mutex_init(&peer->stdout_lock, NULL);
Expand Down Expand Up @@ -625,7 +626,7 @@ void *bgp_peer_thread(void *param) {
//Did we receive messages?
if (peer->socket.fd >= 0 && FD_ISSET(peer->socket.fd, set)) {
log_print(LOG_DEBUG, "Calling recv_msg() on socket\n");
message = recv_msg(peer->socket.fd);
message = recv_msg(peer);

if (!message) {
log_print(LOG_ERROR, "recv_msg() errored\n");
Expand Down Expand Up @@ -789,7 +790,7 @@ int fsm_state_idle(struct bgp_peer *peer, fd_set *set) {
//Start the ConnectRetryTimer
start_timer(peer->local_timers, ConnectRetryTimer);

log_print(LOG_INFO, "Opening connection to %s,%d (%s)\n", peer->peer_ip, peer->peer_asn, peer->name);
log_print(LOG_INFO, "Opening connection to %s,%u (%s)\n", peer->peer_ip, peer->peer_asn, peer->name);
peer->socket.fd = tcp_connect(peer->peer_ip, "179", peer->source_ip);

if (peer->socket.fd < 0) {
Expand All @@ -814,6 +815,7 @@ int fsm_state_idle(struct bgp_peer *peer, fd_set *set) {

int fsm_state_connect(struct bgp_peer *peer) {
struct bgp_capabilities *caps;
uint16_t open_asn;

log_print(LOG_DEBUG, "Peer %s FSM state: CONNECT\n", peer->name);

Expand All @@ -823,12 +825,14 @@ int fsm_state_connect(struct bgp_peer *peer) {
bgp_capabilities_add_route_refresh(caps);
bgp_capabilities_add_mp_ext(caps, BGP_AFI_IPV4, BGP_SAFI_UNICAST);
bgp_capabilities_add_mp_ext(caps, BGP_AFI_IPV6, BGP_SAFI_UNICAST);
bgp_capabilities_add_four_octet_asn(caps, *peer->local_asn);
}

//TODO: fix hold timer
/* RFC 6793: OPEN ASN field is 2 bytes. Use AS_TRANS (23456) if local ASN > 65535 */
open_asn = (*peer->local_asn > 65535) ? 23456 : (uint16_t)*peer->local_asn;

log_print(LOG_DEBUG, "Sending OPEN to peer %s\n", peer->name);
/* Note: caps ownership transfers to the queued message, freed by free_msg() */
queue_and_send_open(peer, *peer->version, *peer->local_asn, 30, *peer->local_rid, caps);
queue_and_send_open(peer, *peer->version, open_asn, 30, *peer->local_rid, caps);

start_timer(peer->local_timers, HoldTimer);
peer->fsm_state = OPENSENT;
Expand All @@ -855,10 +859,30 @@ int fsm_state_opensent(struct bgp_peer *peer, struct bgp_msg *msg, fd_set *set)
if (message->type == OPEN) {
log_print(LOG_DEBUG, "Checking OPEN for correctness\n");

/* Check 4-byte ASN capability (RFC 6793) */
uint32_t peer_real_asn = message->open.asn;
uint32_t peer_cap_asn = 0;
int peer_has_4byte = 0;

if (message->open.capabilities) {
peer_has_4byte = bgp_capabilities_has_four_octet_asn(
message->open.capabilities, &peer_cap_asn);
}

if (peer_has_4byte) {
/* Peer supports 4-byte ASN - use capability value as real ASN */
peer_real_asn = peer_cap_asn;
peer->four_octet_asn = 1;
log_print(LOG_INFO, "Peer %s supports 4-byte ASN (ASN %u)\n",
peer->name, peer_real_asn);
} else {
peer->four_octet_asn = 0;
}

//Check peer ASN matches configured ASN
if (message->open.asn != peer->peer_asn) {
log_print(LOG_WARN, "Peer %s ASN mismatch: expected %d, got %d\n",
peer->name, peer->peer_asn, message->open.asn);
if (peer_real_asn != peer->peer_asn) {
log_print(LOG_WARN, "Peer %s ASN mismatch: expected %u, got %u\n",
peer->name, peer->peer_asn, peer_real_asn);
queue_and_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_OPEN_PEER_AS);
bgp_close_socket(peer);
peer->fsm_state = IDLE;
Expand Down
4 changes: 2 additions & 2 deletions src/bgp.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ struct bgp_peer;
struct bgp_instance;
struct bgp_local;

struct bgp_instance *create_bgp_instance(uint16_t, uint32_t, uint8_t);
struct bgp_instance *create_bgp_instance(uint32_t, uint32_t, uint8_t);
void free_bgp_instance(struct bgp_instance *);

unsigned int create_bgp_peer(struct bgp_instance *, const char *, const uint16_t, const char *);
unsigned int create_bgp_peer(struct bgp_instance *, const char *, const uint32_t, const char *);
unsigned int bgp_peer_source(struct bgp_instance *, unsigned int, const char *);

int set_bgp_output(struct bgp_instance *, unsigned int, enum bgp_output);
Expand Down
21 changes: 21 additions & 0 deletions src/bgp_capability.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,24 @@ const char *bgp_capability_name(uint8_t code) {
default: return "Unknown";
}
}

int bgp_capabilities_has_four_octet_asn(const struct bgp_capabilities *caps, uint32_t *asn) {
struct list_head *pos;
struct bgp_capability *cap;

if (!caps) {
return 0;
}

list_for_each(pos, &caps->caps) {
cap = list_entry(pos, struct bgp_capability, list);
if (cap->code == BGP_CAP_FOUR_OCTET_ASN && cap->length == 4 && cap->value) {
if (asn) {
*asn = uchar_be_to_uint32(cap->value);
}
return 1;
}
}

return 0;
}
6 changes: 6 additions & 0 deletions src/bgp_capability.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,9 @@ struct bgp_capabilities *bgp_capabilities_parse(const unsigned char *opt_params,
* Returns a static string describing the capability
*/
const char *bgp_capability_name(uint8_t code);

/*
* Check if 4-octet ASN capability is present and extract the ASN
* Returns 1 if present (and sets *asn to the 4-byte ASN), 0 if not present
*/
int bgp_capabilities_has_four_octet_asn(const struct bgp_capabilities *caps, uint32_t *asn);
Loading