1use std::net::Ipv4Addr;
2
3use bytes::{Buf, BufMut, BytesMut};
4
5use crate::capability::{Capability, decode_optional_parameters, encode_optional_parameters};
6use crate::constants::{BGP_VERSION, HEADER_LEN, MAX_MESSAGE_LEN};
7use crate::error::{DecodeError, EncodeError};
8use crate::header::{BgpHeader, MessageType};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct OpenMessage {
13 pub version: u8,
15 pub my_as: u16,
19 pub hold_time: u16,
21 pub bgp_identifier: Ipv4Addr,
23 pub capabilities: Vec<Capability>,
25}
26
27impl OpenMessage {
28 pub fn decode(buf: &mut impl Buf, body_len: usize) -> Result<Self, DecodeError> {
37 if body_len < 10 {
39 return Err(DecodeError::MalformedField {
40 message_type: "OPEN",
41 detail: format!("body too short: {body_len} bytes (need at least 10)"),
42 });
43 }
44
45 if buf.remaining() < body_len {
46 return Err(DecodeError::Incomplete {
47 needed: body_len,
48 available: buf.remaining(),
49 });
50 }
51
52 let version = buf.get_u8();
53 if version != BGP_VERSION {
54 return Err(DecodeError::UnsupportedVersion { version });
55 }
56
57 let my_as = buf.get_u16();
58 let hold_time = buf.get_u16();
59 let bgp_identifier = Ipv4Addr::from(buf.get_u32());
60 let opt_params_len = buf.get_u8();
61
62 let expected_body = 10 + usize::from(opt_params_len);
64 if expected_body != body_len {
65 return Err(DecodeError::MalformedField {
66 message_type: "OPEN",
67 detail: format!(
68 "optional parameters length {opt_params_len} inconsistent \
69 with body length {body_len} (expected {expected_body})"
70 ),
71 });
72 }
73
74 let capabilities = decode_optional_parameters(buf, opt_params_len)?;
75
76 Ok(Self {
77 version,
78 my_as,
79 hold_time,
80 bgp_identifier,
81 capabilities,
82 })
83 }
84
85 pub fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> {
92 let mut opt_params = BytesMut::new();
94 encode_optional_parameters(&self.capabilities, &mut opt_params)?;
95 let opt_params_len = opt_params.len();
96
97 if opt_params_len > 255 {
98 return Err(EncodeError::ValueOutOfRange {
99 field: "optional_parameters_length",
100 value: opt_params_len.to_string(),
101 });
102 }
103
104 let total_len = HEADER_LEN + 10 + opt_params_len;
105 if total_len > usize::from(MAX_MESSAGE_LEN) {
106 return Err(EncodeError::MessageTooLong { size: total_len });
107 }
108
109 let header = BgpHeader {
111 #[expect(clippy::cast_possible_truncation)]
112 length: total_len as u16,
113 message_type: MessageType::Open,
114 };
115 header.encode(buf);
116
117 buf.put_u8(self.version);
119 buf.put_u16(self.my_as);
120 buf.put_u16(self.hold_time);
121 buf.put_u32(u32::from(self.bgp_identifier));
122 #[expect(clippy::cast_possible_truncation)]
123 buf.put_u8(opt_params_len as u8);
124 buf.put_slice(&opt_params);
125
126 Ok(())
127 }
128
129 #[must_use]
131 pub fn encoded_len(&self) -> usize {
132 let cap_size: usize = self.capabilities.iter().map(Capability::encoded_len).sum();
133 let opt_params_overhead = if self.capabilities.is_empty() { 0 } else { 2 };
135 HEADER_LEN + 10 + opt_params_overhead + cap_size
136 }
137
138 #[must_use]
141 pub fn four_byte_as(&self) -> u32 {
142 for cap in &self.capabilities {
143 if let Capability::FourOctetAs { asn } = cap {
144 return *asn;
145 }
146 }
147 u32::from(self.my_as)
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154 use crate::capability::Afi;
155 use crate::capability::Safi;
156 use crate::constants::MAX_MESSAGE_LEN;
157
158 fn minimal_open() -> OpenMessage {
159 OpenMessage {
160 version: BGP_VERSION,
161 my_as: 65001,
162 hold_time: 90,
163 bgp_identifier: Ipv4Addr::new(10, 0, 0, 1),
164 capabilities: vec![],
165 }
166 }
167
168 #[test]
169 fn encode_decode_minimal_open() {
170 let original = minimal_open();
171 let mut encoded = BytesMut::with_capacity(64);
172 original.encode(&mut encoded).unwrap();
173
174 let mut bytes = encoded.freeze();
175 let header = BgpHeader::decode(&mut bytes, MAX_MESSAGE_LEN).unwrap();
176 assert_eq!(header.message_type, MessageType::Open);
177 assert_eq!(header.length, 29); let body_len = usize::from(header.length) - HEADER_LEN;
180 let decoded = OpenMessage::decode(&mut bytes, body_len).unwrap();
181 assert_eq!(original, decoded);
182 }
183
184 #[test]
185 fn encode_decode_with_capabilities() {
186 let original = OpenMessage {
187 version: BGP_VERSION,
188 my_as: 23456, hold_time: 90,
190 bgp_identifier: Ipv4Addr::new(10, 0, 0, 1),
191 capabilities: vec![
192 Capability::MultiProtocol {
193 afi: Afi::Ipv4,
194 safi: Safi::Unicast,
195 },
196 Capability::FourOctetAs { asn: 4_200_000_001 },
197 ],
198 };
199
200 let mut encoded = BytesMut::with_capacity(128);
201 original.encode(&mut encoded).unwrap();
202
203 let mut bytes = encoded.freeze();
204 let header = BgpHeader::decode(&mut bytes, MAX_MESSAGE_LEN).unwrap();
205 let body_len = usize::from(header.length) - HEADER_LEN;
206 let decoded = OpenMessage::decode(&mut bytes, body_len).unwrap();
207 assert_eq!(original, decoded);
208 }
209
210 #[test]
211 fn four_byte_as_extraction() {
212 let open = OpenMessage {
213 version: BGP_VERSION,
214 my_as: 23456,
215 hold_time: 90,
216 bgp_identifier: Ipv4Addr::new(10, 0, 0, 1),
217 capabilities: vec![Capability::FourOctetAs { asn: 4_200_000_001 }],
218 };
219 assert_eq!(open.four_byte_as(), 4_200_000_001);
220 }
221
222 #[test]
223 fn four_byte_as_fallback_to_my_as() {
224 let open = minimal_open();
225 assert_eq!(open.four_byte_as(), 65001);
226 }
227
228 #[test]
229 fn reject_bad_version() {
230 let body: &[u8] = &[
231 3, 0xFD, 0xE9, 0, 90, 10, 0, 0, 1, 0, ];
237 let mut buf = bytes::Bytes::copy_from_slice(body);
238 assert!(matches!(
239 OpenMessage::decode(&mut buf, 10),
240 Err(DecodeError::UnsupportedVersion { version: 3 })
241 ));
242 }
243
244 #[test]
245 fn reject_body_too_short() {
246 let body: &[u8] = &[4, 0, 1]; let mut buf = bytes::Bytes::copy_from_slice(body);
248 assert!(matches!(
249 OpenMessage::decode(&mut buf, 3),
250 Err(DecodeError::MalformedField { .. })
251 ));
252 }
253
254 #[test]
255 fn reject_inconsistent_opt_params_length() {
256 let body: &[u8] = &[
257 4, 0xFD, 0xE9, 0, 90, 10, 0, 0, 1, 5, ];
263 let mut buf = bytes::Bytes::copy_from_slice(body);
264 assert!(matches!(
265 OpenMessage::decode(&mut buf, 10),
266 Err(DecodeError::MalformedField { .. })
267 ));
268 }
269
270 #[test]
271 fn unknown_capabilities_preserved() {
272 let original = OpenMessage {
273 version: BGP_VERSION,
274 my_as: 65001,
275 hold_time: 90,
276 bgp_identifier: Ipv4Addr::new(10, 0, 0, 1),
277 capabilities: vec![Capability::Unknown {
278 code: 128,
279 data: bytes::Bytes::from_static(&[0xDE, 0xAD]),
280 }],
281 };
282
283 let mut encoded = BytesMut::with_capacity(64);
284 original.encode(&mut encoded).unwrap();
285
286 let mut bytes = encoded.freeze();
287 let header = BgpHeader::decode(&mut bytes, MAX_MESSAGE_LEN).unwrap();
288 let body_len = usize::from(header.length) - HEADER_LEN;
289 let decoded = OpenMessage::decode(&mut bytes, body_len).unwrap();
290 assert_eq!(original, decoded);
291 }
292}