///|
pub struct StateChange {
source : Source?
timestamp : UInt
microseconds : Int?
peer_asn : UInt
peer_ip : String
old_state : Int
new_state : Int
} derive(ToJson)
///|
pub extend StateChange with ToJson::{to_json}
///|
pub struct BgpRecord {
message_type : Int
events : Array[RouteEvent]
states : Array[StateChange]
ignored_families : Int
ignored : Array[(Int, Int)]
}
///|
fn read_nlri_paths(
c : Cursor,
afi : Int,
addpath : Bool,
) -> Array[(String, UInt?)] raise ParseError {
let prefixes = []
while c.remaining() > 0 {
let id = if addpath { Some(c.u32()) } else { None }
prefixes.push((read_prefix(c, afi).text(), id))
}
prefixes
}
///|
fn make_event(
frame : Frame,
peer_asn : UInt,
peer_ip : String,
outbound : Bool,
action : String,
prefix : String,
attributes : Attributes,
path_id? : UInt,
) -> RouteEvent {
{
schema_version: "2.0",
source: None,
timestamp: frame.header.timestamp,
microseconds: frame.microseconds,
action,
prefix,
path_id,
peer_asn,
peer_ip,
outbound,
originated: None,
attributes,
}
}
///|
pub fn parse_bgp4mp(frame : Frame) -> BgpRecord raise ParseError {
if frame.header.kind != 16 && frame.header.kind != 17 {
raise Invalid(0, "不是 BGP4MP")
}
let subtype = frame.header.subtype
let width = match subtype {
0 | 1 | 6 | 8 | 10 => 2
4 | 5 | 7 | 9 | 11 => 4
_ => raise Invalid(0, "不支持的 BGP4MP 子类型")
}
let c = Cursor::new(frame.payload)
let peer_asn = if width == 4 {
c.u32()
} else {
c.u16().reinterpret_as_uint()
}
if width == 4 {
ignore(c.u32())
} else {
ignore(c.u16())
}
ignore(c.u16())
let afi = c.u16()
let size = address_size(afi)
let peer_ip = ip_text(c.bytes(size))
ignore(c.bytes(size))
if subtype == 0 || subtype == 5 {
let old_state = c.u16()
let new_state = c.u16()
if old_state < 1 || old_state > 6 || new_state < 1 || new_state > 6 {
raise Invalid(c.pos - 4, "无效 BGP 会话状态")
}
c.finish()
return {
message_type: 0,
ignored_families: 0,
ignored: [],
events: [],
states: [
{
timestamp: frame.header.timestamp,
source: None,
microseconds: frame.microseconds,
peer_asn,
peer_ip,
old_state,
new_state,
},
],
}
}
let outbound = subtype == 6 || subtype == 7 || subtype == 10 || subtype == 11
let addpath = subtype >= 8 && subtype <= 11
let message = c.take(c.remaining())
for i = 0; i < 16; i = i + 1 {
if message.u8() != 255 {
raise Invalid(message.pos - 1, "无效 BGP marker")
}
}
let length = message.u16()
if length < 19 || length != message.remaining() + 18 {
raise Invalid(message.pos - 2, "BGP 消息长度不匹配")
}
let message_type = message.u8()
if message_type < 1 || message_type > 5 {
raise Invalid(message.pos - 1, "无效 BGP 消息类型")
}
let events = []
let mut ignored_families = 0
let ignored = []
if message_type == 2 {
let withdrawn = message.take(message.u16())
let withdrawals = read_nlri_paths(withdrawn, 1, addpath)
let attributes = parse_attributes(message.bytes(message.u16()), width)
let announcements = read_nlri_paths(message, 1, addpath)
if !announcements.is_empty() {
require_announcement_attributes(attributes, true)
}
for attr in attributes.other {
if attr.code == 14 || attr.code == 15 {
let reach = attr.code == 14
let mp = parse_mp(attr.value, reach, addpath~)
if reach && mp.supported {
require_announcement_attributes(attributes, false)
}
if !mp.supported {
ignored_families += 1
ignored.push((mp.afi, mp.safi))
}
for i, prefix in mp.prefixes {
let attrs = if reach {
{ ..attributes, next_hop: mp.next_hop, }
} else {
empty_attributes()
}
events.push(
make_event(
frame,
peer_asn,
peer_ip,
outbound,
if reach {
"announce"
} else {
"withdraw"
},
prefix,
attrs,
path_id?=mp.path_ids[i],
),
)
}
}
}
for (prefix, id) in withdrawals {
events.push(
make_event(
frame,
peer_asn,
peer_ip,
outbound,
"withdraw",
prefix,
empty_attributes(),
path_id?=id,
),
)
}
for (prefix, id) in announcements {
events.push(
make_event(
frame,
peer_asn,
peer_ip,
outbound,
"announce",
prefix,
attributes,
path_id?=id,
),
)
}
} else {
if message_type == 4 && message.remaining() != 0 {
raise Invalid(message.pos, "KEEPALIVE 消息包含额外数据")
}
if message_type == 1 && message.remaining() < 10 {
raise Invalid(message.pos, "OPEN 消息截断")
}
if message_type == 3 && message.remaining() < 2 {
raise Invalid(message.pos, "NOTIFICATION 消息截断")
}
if message_type == 5 && message.remaining() != 4 {
raise Invalid(message.pos, "ROUTE-REFRESH 长度错误")
}
ignore(message.bytes(message.remaining()))
}
message.finish()
{ message_type, events, states: [], ignored_families, ignored, }
}