Skip to content

Commit fde8b09

Browse files
authored
Merge pull request #77 from nrybowski/rfc5613
ospf: Add RFC 5613 support
2 parents 0e6fc0e + cb38d26 commit fde8b09

File tree

136 files changed

+8600
-6883
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

136 files changed

+8600
-6883
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ Holo supports the following Internet Standards:
194194
* RFC 5243 - OSPF Database Exchange Summary List Optimization
195195
* RFC 5250 - The OSPF Opaque LSA Option
196196
* RFC 5340 - OSPF for IPv6
197+
* RFC 5613 - OSPF Link-Local Signaling
197198
* RFC 5642 - Dynamic Hostname Exchange Mechanism for OSPF
198199
* RFC 5709 - OSPFv2 HMAC-SHA Cryptographic Authentication
199200
* RFC 5838 - Support of Address Families in OSPFv3

holo-ospf/src/area.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,11 @@ pub struct Range {
8585
// Represents the possible locations of the OSPF Options field.
8686
#[derive(Clone, Copy, Debug, Eq, new, PartialEq)]
8787
pub enum OptionsLocation {
88-
Packet { pkt_type: PacketType, auth: bool },
88+
Packet {
89+
pkt_type: PacketType,
90+
auth: bool,
91+
lls: bool,
92+
},
8993
Lsa,
9094
}
9195

holo-ospf/src/events.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,14 @@ where
378378
nbr.bdr = hello.bdr();
379379
}
380380

381+
// Examine LLS data block if enabled and present in the packet.
382+
if iface.config.lls_enabled
383+
&& hello.options().l_bit()
384+
&& let Some(_lls) = hello.lls()
385+
{
386+
// TODO: Handle LLS data
387+
}
388+
381389
Ok(())
382390
}
383391

@@ -642,6 +650,14 @@ where
642650
}
643651
}
644652

653+
// Examine LLS data block if enabled and present the packet.
654+
if iface.config.lls_enabled
655+
&& dbdesc.options().l_bit()
656+
&& let Some(_lls) = dbdesc.lls()
657+
{
658+
// TODO: Handle LLS data
659+
}
660+
645661
// Save last received Database Description packet.
646662
nbr.last_rcvd_dbdesc = Some(LastDbDesc {
647663
options: dbdesc.options(),

holo-ospf/src/northbound/configuration.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ pub struct InterfaceCfg<V: Version> {
197197
pub bfd_enabled: bool,
198198
pub bfd_params: bfd::ClientCfg,
199199
pub trace_opts: InterfaceTraceOptions,
200+
pub lls_enabled: bool,
200201
}
201202

202203
#[derive(Debug)]
@@ -854,6 +855,14 @@ where
854855
let transmit_delay = args.dnode.get_u16();
855856
iface.config.transmit_delay = transmit_delay;
856857
})
858+
.path(ospf::areas::area::interfaces::interface::lls::PATH)
859+
.modify_apply(|instance, args| {
860+
let (_area_idx, iface_idx) = args.list_entry.into_interface().unwrap();
861+
let iface = &mut instance.arenas.interfaces[iface_idx];
862+
863+
let enabled = args.dnode.get_bool();
864+
iface.config.lls_enabled = enabled;
865+
})
857866
.path(ospf::areas::area::interfaces::interface::enabled::PATH)
858867
.modify_apply(|instance, args| {
859868
let (area_idx, iface_idx) =
@@ -2116,6 +2125,7 @@ where
21162125
ospf::areas::area::interfaces::interface::mtu_ignore::DFLT;
21172126
let bfd_enabled =
21182127
ospf::areas::area::interfaces::interface::bfd::enabled::DFLT;
2128+
let lls_enabled = ospf::areas::area::interfaces::interface::lls::DFLT;
21192129

21202130
InterfaceCfg {
21212131
instance_id: InheritableConfig::new(instance_id),
@@ -2137,6 +2147,7 @@ where
21372147
bfd_enabled,
21382148
bfd_params: Default::default(),
21392149
trace_opts: Default::default(),
2150+
lls_enabled,
21402151
}
21412152
}
21422153
}

holo-ospf/src/ospfv2/area.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ impl AreaVersion<Self> for Ospfv2 {
4242
options.insert(Options::O);
4343
}
4444

45+
if let OptionsLocation::Packet { lls: true, .. } = location {
46+
options.insert(Options::L);
47+
}
48+
4549
options
4650
}
4751
}

holo-ospf/src/ospfv2/interface.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ impl InterfaceVersion<Self> for Ospfv2 {
5555
auth_seqno: None,
5656
};
5757

58+
let lls = if iface.config.lls_enabled {
59+
// TODO: Get LLS configuration
60+
None
61+
} else {
62+
None
63+
};
64+
5865
Packet::Hello(Hello {
5966
hdr,
6067
network_mask: iface.system.primary_addr.unwrap().mask(),
@@ -64,13 +71,15 @@ impl InterfaceVersion<Self> for Ospfv2 {
6471
OptionsLocation::new_packet(
6572
PacketType::Hello,
6673
iface.state.auth.is_some(),
74+
lls.is_some(),
6775
),
6876
),
6977
priority: iface.config.priority,
7078
dead_interval: iface.config.dead_interval as u32,
7179
dr: iface.state.dr,
7280
bdr: iface.state.bdr,
7381
neighbors: iface.state.neighbors.router_ids().collect(),
82+
lls,
7483
})
7584
}
7685

holo-ospf/src/ospfv2/packet/lls.rs

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
//
2+
// Copyright (c) The Holo Core Contributors
3+
//
4+
// SPDX-License-Identifier: MIT
5+
//
6+
7+
use std::sync::atomic::Ordering;
8+
9+
use bytes::{Buf, BufMut, Bytes, BytesMut};
10+
use derive_new::new;
11+
use num_traits::FromPrimitive;
12+
use serde::{Deserialize, Serialize};
13+
14+
use super::{PacketHdrAuth, validate_digest};
15+
use crate::packet::auth::{self, AuthDecodeCtx, AuthEncodeCtx};
16+
use crate::packet::error::{DecodeError, DecodeResult};
17+
use crate::packet::lls::{
18+
ExtendedOptionsFlagsTlv, LLS_HDR_SIZE, LlsDbDescData, LlsHelloData,
19+
LlsTlvType, LlsVersion, lls_encode_end, lls_encode_start,
20+
};
21+
use crate::packet::tlv::{
22+
UnknownTlv, tlv_encode_end, tlv_encode_start, tlv_wire_len,
23+
};
24+
use crate::packet::{OptionsVersion, PacketVersion};
25+
use crate::version::Ospfv2;
26+
27+
#[derive(Clone, Debug, Eq, PartialEq, Default)]
28+
pub struct LlsDataBlock {
29+
pub eof: Option<ExtendedOptionsFlagsTlv>,
30+
pub unknown_tlvs: Vec<UnknownTlv>,
31+
}
32+
33+
impl From<LlsHelloData> for LlsDataBlock {
34+
fn from(value: LlsHelloData) -> Self {
35+
let mut lls = LlsDataBlock::default();
36+
lls.eof = value.eof.map(ExtendedOptionsFlagsTlv);
37+
lls
38+
}
39+
}
40+
41+
impl From<LlsDataBlock> for LlsHelloData {
42+
fn from(value: LlsDataBlock) -> Self {
43+
LlsHelloData {
44+
eof: value.eof.map(|tlv| tlv.0),
45+
}
46+
}
47+
}
48+
49+
impl From<LlsDbDescData> for LlsDataBlock {
50+
fn from(value: LlsDbDescData) -> Self {
51+
let mut lls = LlsDataBlock::default();
52+
lls.eof = value.eof.map(ExtendedOptionsFlagsTlv);
53+
lls
54+
}
55+
}
56+
57+
impl From<LlsDataBlock> for LlsDbDescData {
58+
fn from(value: LlsDataBlock) -> Self {
59+
LlsDbDescData {
60+
eof: value.eof.map(|tlv| tlv.0),
61+
}
62+
}
63+
}
64+
65+
// RFC 5613 : LLS Cryptographic Authentication TLV
66+
//
67+
// 0 1 2 3
68+
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
69+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70+
// | 2 | AuthLen |
71+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72+
// | Sequence Number |
73+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
74+
// | |
75+
// . .
76+
// . AuthData .
77+
// . .
78+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
79+
//
80+
#[derive(Clone, Debug, Eq, PartialEq, new)]
81+
#[derive(Serialize, Deserialize)]
82+
pub struct CryptoAuthTlv {
83+
pub seqno: u32,
84+
#[new(default)]
85+
pub auth_data: Bytes,
86+
}
87+
88+
impl CryptoAuthTlv {
89+
pub(crate) fn encode(
90+
&self,
91+
buf: &mut BytesMut,
92+
lls_start_pos: usize,
93+
auth: &AuthEncodeCtx<'_>,
94+
) {
95+
let start_pos = tlv_encode_start(buf, LlsTlvType::CryptoAuth);
96+
buf.put_u32(self.seqno);
97+
98+
// Add place holder for auth digest.
99+
let auth_digest_start = buf.len();
100+
buf.put_slice(&vec![0; auth.key.algo.digest_size() as usize]);
101+
102+
// Encode TLV length. It will be rewritten later with the same value.
103+
tlv_encode_end(buf, start_pos);
104+
105+
// Write LLS data block length as CA TLV MUST be the last chunck of the
106+
// block per RFC 5613. It will be rewritten later with the same value.
107+
lls_encode_end::<Ospfv2>(buf, lls_start_pos, true);
108+
109+
// Compute auth digest on full LLS block until auth digest.
110+
let digest = auth::message_digest(
111+
&buf[lls_start_pos..auth_digest_start],
112+
auth.key.algo,
113+
&auth.key.string,
114+
None,
115+
None,
116+
);
117+
118+
// Replace auth digest place holder with actual digest.
119+
buf[auth_digest_start..].copy_from_slice(&digest);
120+
}
121+
122+
pub(crate) fn decode(tlv_len: u16, buf: &mut Bytes) -> DecodeResult<Self> {
123+
if tlv_len < 4 || buf.remaining() < tlv_len as usize {
124+
return Err(DecodeError::InvalidTlvLength(tlv_len));
125+
}
126+
let seqno = buf.try_get_u32()?;
127+
let auth_data_len = tlv_len - 4;
128+
let auth_data = buf.slice(..auth_data_len as usize);
129+
130+
Ok(CryptoAuthTlv { seqno, auth_data })
131+
}
132+
}
133+
134+
impl LlsVersion<Self> for Ospfv2 {
135+
type LlsDataBlock = LlsDataBlock;
136+
137+
fn encode_lls_block(
138+
buf: &mut BytesMut,
139+
lls: LlsDataBlock,
140+
auth: Option<&AuthEncodeCtx<'_>>,
141+
) {
142+
let start_pos = lls_encode_start(buf);
143+
144+
if let Some(eof) = lls.eof {
145+
eof.encode(buf);
146+
}
147+
148+
// RFC 5613 : "The CA-TLV MUST NOT appear more than once in the LLS
149+
// block. Also, when present, this TLV MUST be the last TLV in the LLS
150+
// block."
151+
if let Some(auth) = auth {
152+
let ca = CryptoAuthTlv::new(
153+
(auth.seqno.load(Ordering::Relaxed) as u32) - 1,
154+
);
155+
ca.encode(buf, start_pos, auth);
156+
}
157+
lls_encode_end::<Ospfv2>(buf, start_pos, auth.is_some());
158+
}
159+
160+
fn decode_lls_block(
161+
data: &[u8],
162+
pkt_len: u16,
163+
hdr_auth: PacketHdrAuth,
164+
auth: Option<&AuthDecodeCtx<'_>>,
165+
) -> DecodeResult<Option<LlsDataBlock>> {
166+
// Test the presence of the L-bit indicating a LLS data block.
167+
if Self::packet_options(data).is_none_or(|options| !options.l_bit()) {
168+
return Ok(None);
169+
}
170+
171+
let mut buf = Bytes::copy_from_slice(&data[pkt_len as usize..]);
172+
173+
// Sanity check on buffer length.
174+
if buf.remaining() < LLS_HDR_SIZE as usize {
175+
return Err(DecodeError::InvalidLength(buf.len() as u16));
176+
}
177+
178+
// If authentication trailer is embedded, skip it.
179+
// The authentication digest has already been verified earlier, so no
180+
// need for a double check here.
181+
if let PacketHdrAuth::Cryptographic { auth_len, .. } = hdr_auth {
182+
buf.advance(auth_len as usize);
183+
} else {
184+
// Validate LLS block checksum when authentication is disabled.
185+
Self::verify_cksum(&buf)?;
186+
};
187+
188+
let mut data = buf.clone();
189+
190+
let mut lls_block = LlsDataBlock::default();
191+
192+
let _cksum = buf.try_get_u16()?;
193+
let lls_len = buf.try_get_u16()?;
194+
195+
// RFC 5613 Section 2.2: " The 16-bit LLS Data Length field contains
196+
// the length (in 32-bit words) of the LLS block including the header
197+
// and payload."
198+
let block_len = ((lls_len * 4) - LLS_HDR_SIZE) as usize;
199+
200+
// Validate LLS block length
201+
if block_len > buf.remaining() {
202+
return Err(DecodeError::InvalidLength(block_len as u16));
203+
}
204+
buf = buf.slice(0..block_len);
205+
data = data.slice(0..block_len + LLS_HDR_SIZE as usize);
206+
207+
while buf.remaining() >= LLS_HDR_SIZE as usize {
208+
// Parse TLV type.
209+
let tlv_type = buf.try_get_u16()?;
210+
let tlv_etype = LlsTlvType::from_u16(tlv_type);
211+
212+
// Parse and validate TLV length.
213+
let tlv_len = buf.try_get_u16()?;
214+
let tlv_wlen = tlv_wire_len(tlv_len);
215+
if tlv_wlen as usize > buf.remaining() {
216+
return Err(DecodeError::InvalidTlvLength(tlv_len));
217+
}
218+
219+
// Parse TLV value.
220+
let mut buf_tlv = buf.copy_to_bytes(tlv_wlen as usize);
221+
match tlv_etype {
222+
Some(LlsTlvType::ExtendedOptionsFlags) => {
223+
let opts =
224+
ExtendedOptionsFlagsTlv::decode(tlv_len, &mut buf_tlv)?;
225+
lls_block.eof = Some(opts);
226+
}
227+
Some(LlsTlvType::CryptoAuth) => {
228+
let ca = CryptoAuthTlv::decode(tlv_len, &mut buf_tlv)?;
229+
230+
if let PacketHdrAuth::Cryptographic {
231+
key_id,
232+
auth_len,
233+
seqno,
234+
} = hdr_auth
235+
{
236+
// RFC 5613 Section 2.5 : "The Sequence Number field
237+
// contains the cryptographic sequence number
238+
// that is used to prevent simple replay attacks. For the
239+
// LLS block to be considered authentic, the Sequence Number
240+
// in the CA-TLV MUST match the Sequence Number in the
241+
// OSPFv2 packet header Authentication field (which MUST be
242+
// present)."
243+
if seqno != ca.seqno {
244+
return Err(DecodeError::AuthError);
245+
}
246+
247+
validate_digest(
248+
key_id,
249+
auth_len,
250+
auth,
251+
&ca.auth_data,
252+
// Skip the CA TLV digest
253+
&data[..data.len() - ca.auth_data.len()],
254+
)?;
255+
}
256+
}
257+
_ => {
258+
// Save unknown TLV.
259+
lls_block
260+
.unknown_tlvs
261+
.push(UnknownTlv::new(tlv_type, tlv_len, buf_tlv));
262+
}
263+
}
264+
}
265+
Ok(Some(lls_block))
266+
}
267+
}

0 commit comments

Comments
 (0)