Skip to content

Commit f261ca8

Browse files
committed
ospf: Add RFC 5613 support (Link Local Signaling)
This commit introduces LLS data block appended to OSPF Hello and DbDesc packets. The current code is shared between OSPFv2 and OSPFv3. - About authentication: For OSPFv{2,3}, if the authentication is disabled, the LLS data block checksum is verified. For OSPFv2, when the authentication is enabled, the CA-TLV digest is verified. For OSPFv3, when the authentication is enabled, there is nothing to do as the LLS data block is included in the authentication digest. This behavior is documented in RFC 7166 Section 2. - About enabling LLS data blocks through YANG: LLS data blocks are enabled per interface. That enables generating LLS data block for Hello and DbDesc messages orginated for a that specific interface. If some LLS data is specified through OSPF configuration, the L-bit is enabled in the packet options. This feature also enables examining LLS data block for received messages if the option is enabled and the L-bit flag is present in the message options. Currently, the operation detailed above are mainly placeholders for future behavior since RFC 5613 does not mention how to configure the TLVs it specifies. - About the presence of EO TLV in LlsDbDescData: RFC 4811 Section 2.1 specifies that EO TLV may be included in DbDesc packets. Signed-off-by: Nicolas Rybowski <[email protected]> --- v0 -> v1: Fixed nits mentioned in Renato's review at holo-routing#77
1 parent 9238c1a commit f261ca8

File tree

18 files changed

+878
-32
lines changed

18 files changed

+878
-32
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+
let digest = auth::message_digest(
98+
&buf[lls_start_pos..],
99+
auth.key.algo,
100+
&auth.key.string,
101+
None,
102+
None,
103+
);
104+
buf.put_slice(&digest);
105+
tlv_encode_end(buf, start_pos);
106+
}
107+
108+
pub(crate) fn decode(tlv_len: u16, buf: &mut Bytes) -> DecodeResult<Self> {
109+
if tlv_len < 4 || buf.remaining() < tlv_len as usize {
110+
return Err(DecodeError::InvalidTlvLength(tlv_len));
111+
}
112+
let seqno = buf.try_get_u32()?;
113+
let auth_data_len = tlv_len - 4;
114+
let auth_data = buf.slice(..auth_data_len as usize);
115+
116+
Ok(CryptoAuthTlv { seqno, auth_data })
117+
}
118+
}
119+
120+
impl LlsVersion<Self> for Ospfv2 {
121+
type LlsDataBlock = LlsDataBlock;
122+
123+
fn encode_lls_block(
124+
buf: &mut BytesMut,
125+
lls: LlsDataBlock,
126+
auth: Option<&AuthEncodeCtx<'_>>,
127+
) {
128+
let start_pos = lls_encode_start(buf);
129+
130+
if let Some(eof) = lls.eof {
131+
eof.encode(buf);
132+
}
133+
134+
// RFC 5613 : "The CA-TLV MUST NOT appear more than once in the LLS
135+
// block. Also, when present, this TLV MUST be the last TLV in the LLS
136+
// block."
137+
if let Some(auth) = auth {
138+
let ca = CryptoAuthTlv::new(
139+
(auth.seqno.load(Ordering::Relaxed) as u32) - 1,
140+
);
141+
ca.encode(buf, start_pos, auth);
142+
}
143+
lls_encode_end::<Ospfv2>(buf, start_pos, auth.is_some());
144+
}
145+
146+
fn decode_lls_block(
147+
data: &[u8],
148+
pkt_len: u16,
149+
hdr_auth: PacketHdrAuth,
150+
auth: Option<&AuthDecodeCtx<'_>>,
151+
) -> DecodeResult<Option<LlsDataBlock>> {
152+
// Test the presence of the L-bit indicating a LLS data block.
153+
if Self::packet_options(data).is_none_or(|options| !options.l_bit()) {
154+
return Ok(None);
155+
}
156+
157+
let mut buf = Bytes::copy_from_slice(&data[pkt_len as usize..]);
158+
159+
// Sanity check on buffer length.
160+
if buf.remaining() < LLS_HDR_SIZE as usize {
161+
return Err(DecodeError::InvalidLength(buf.len() as u16));
162+
}
163+
164+
// If authentication trailer is embedded, skip it.
165+
// The authentication digest has already been verified earlier, so no
166+
// need for a double check here.
167+
if let PacketHdrAuth::Cryptographic { auth_len, .. } = hdr_auth {
168+
buf.advance(auth_len as usize);
169+
} else {
170+
// Validate LLS block checksum when authentication is disabled.
171+
Self::verify_cksum(&buf)?;
172+
};
173+
174+
let mut data = buf.clone();
175+
176+
let mut lls_block = LlsDataBlock::default();
177+
178+
let _cksum = buf.try_get_u16()?;
179+
let lls_len = buf.try_get_u16()?;
180+
181+
// RFC 5613 Section 2.2: " The 16-bit LLS Data Length field contains
182+
// the length (in 32-bit words) of the LLS block including the header
183+
// and payload."
184+
let block_len = ((lls_len * 4) - LLS_HDR_SIZE) as usize;
185+
186+
// Validate LLS block length
187+
if block_len > buf.remaining() {
188+
return Err(DecodeError::InvalidLength(block_len as u16));
189+
}
190+
buf = buf.slice(0..block_len);
191+
data = data.slice(0..block_len + LLS_HDR_SIZE as usize);
192+
193+
while buf.remaining() >= LLS_HDR_SIZE as usize {
194+
// Parse TLV type.
195+
let tlv_type = buf.try_get_u16()?;
196+
let tlv_etype = LlsTlvType::from_u16(tlv_type);
197+
198+
// Parse and validate TLV length.
199+
let tlv_len = buf.try_get_u16()?;
200+
let tlv_wlen = tlv_wire_len(tlv_len);
201+
if tlv_wlen as usize > buf.remaining() {
202+
return Err(DecodeError::InvalidTlvLength(tlv_len));
203+
}
204+
205+
// Parse TLV value.
206+
let mut buf_tlv = buf.copy_to_bytes(tlv_wlen as usize);
207+
match tlv_etype {
208+
Some(LlsTlvType::ExtendedOptionsFlags) => {
209+
let opts =
210+
ExtendedOptionsFlagsTlv::decode(tlv_len, &mut buf_tlv)?;
211+
lls_block.eof = Some(opts);
212+
}
213+
Some(LlsTlvType::CryptoAuth) => {
214+
let ca = CryptoAuthTlv::decode(tlv_len, &mut buf_tlv)?;
215+
216+
if let PacketHdrAuth::Cryptographic {
217+
key_id,
218+
auth_len,
219+
seqno,
220+
} = hdr_auth
221+
{
222+
// RFC 5613 Section 2.5 : "The Sequence Number field
223+
// contains the cryptographic sequence number
224+
// that is used to prevent simple replay attacks. For the
225+
// LLS block to be considered authentic, the Sequence Number
226+
// in the CA-TLV MUST match the Sequence Number in the
227+
// OSPFv2 packet header Authentication field (which MUST be
228+
// present)."
229+
if seqno != ca.seqno {
230+
return Err(DecodeError::AuthError);
231+
}
232+
233+
// Skip the CA TLV digest
234+
let mut digest_data = BytesMut::from(
235+
&data[..data.len() - ca.auth_data.len()],
236+
);
237+
238+
// Remove LLS block length at its unknown upon digest
239+
// encoding.
240+
digest_data[3] = 0;
241+
242+
// Remove LLS CA TLV length at its unknown upon digest
243+
// encoding. Per RFC 5613, CA TLV MUST be the last in
244+
// the LLS data block.
245+
let offset = digest_data.len() - 5;
246+
digest_data[offset] = 0;
247+
248+
validate_digest(
249+
key_id,
250+
auth_len,
251+
auth,
252+
&ca.auth_data,
253+
&digest_data,
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)