pub const GENEVE_FIXED_HEADER_LEN: usize = 8;
pub const GENEVE_PROTOCOL_DISCO: u16 = 0x7A11;
pub const GENEVE_PROTOCOL_WIREGUARD: u16 = 0x7A12;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GeneveHeader {
pub control: bool,
pub protocol: u16,
pub vni: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GeneveError {
TooShort,
BadVersion,
UnexpectedOptions,
}
impl GeneveHeader {
pub fn parse(buf: &[u8]) -> Result<(GeneveHeader, usize), GeneveError> {
if buf.len() < GENEVE_FIXED_HEADER_LEN {
return Err(GeneveError::TooShort);
}
let version = buf[0] >> 6;
if version != 0 {
return Err(GeneveError::BadVersion);
}
let opt_len_words = buf[0] & 0x3f;
if opt_len_words != 0 {
return Err(GeneveError::UnexpectedOptions);
}
let control = (buf[1] & 0x40) != 0;
let protocol = u16::from_be_bytes([buf[2], buf[3]]);
let vni = (u32::from(buf[4]) << 16) | (u32::from(buf[5]) << 8) | u32::from(buf[6]);
Ok((
GeneveHeader {
control,
protocol,
vni,
},
GENEVE_FIXED_HEADER_LEN,
))
}
pub fn encode(&self) -> [u8; GENEVE_FIXED_HEADER_LEN] {
let mut out = [0u8; GENEVE_FIXED_HEADER_LEN];
out[0] = 0;
out[1] = if self.control { 0x40 } else { 0x00 };
out[2..4].copy_from_slice(&self.protocol.to_be_bytes());
out[4] = (self.vni >> 16) as u8;
out[5] = (self.vni >> 8) as u8;
out[6] = self.vni as u8;
out
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_disco_data() {
let h = GeneveHeader {
control: true,
protocol: GENEVE_PROTOCOL_DISCO,
vni: 0x0A_BC_DE,
};
let bytes = h.encode();
let (parsed, off) = GeneveHeader::parse(&bytes).unwrap();
assert_eq!(off, GENEVE_FIXED_HEADER_LEN);
assert_eq!(parsed, h);
}
#[test]
fn roundtrip_wireguard_no_control() {
let h = GeneveHeader {
control: false,
protocol: GENEVE_PROTOCOL_WIREGUARD,
vni: 1,
};
let bytes = h.encode();
assert_eq!(bytes[1] & 0x40, 0, "control bit must be clear");
let (parsed, _) = GeneveHeader::parse(&bytes).unwrap();
assert_eq!(parsed, h);
}
#[test]
fn vni_is_24_bits() {
let h = GeneveHeader {
control: false,
protocol: GENEVE_PROTOCOL_DISCO,
vni: 0xFF_FF_FF,
};
let bytes = h.encode();
assert_eq!(bytes[7], 0);
let (parsed, _) = GeneveHeader::parse(&bytes).unwrap();
assert_eq!(parsed.vni, 0xFF_FF_FF);
}
#[test]
fn rejects_short_buffer() {
assert_eq!(GeneveHeader::parse(&[0u8; 7]), Err(GeneveError::TooShort));
}
#[test]
fn rejects_bad_version() {
let mut bytes = GeneveHeader {
control: false,
protocol: GENEVE_PROTOCOL_DISCO,
vni: 0,
}
.encode();
bytes[0] = 0x40; assert_eq!(GeneveHeader::parse(&bytes), Err(GeneveError::BadVersion));
}
#[test]
fn rejects_variable_options() {
let mut bytes = GeneveHeader {
control: false,
protocol: GENEVE_PROTOCOL_DISCO,
vni: 0,
}
.encode();
bytes[0] = 0x02; assert_eq!(
GeneveHeader::parse(&bytes),
Err(GeneveError::UnexpectedOptions)
);
}
#[test]
fn encode_matches_spec_byte_layout() {
let h = GeneveHeader {
control: true,
protocol: GENEVE_PROTOCOL_DISCO,
vni: 0x0A_BC_DE,
};
assert_eq!(h.encode(), [0x00, 0x40, 0x7A, 0x11, 0x0A, 0xBC, 0xDE, 0x00]);
}
#[test]
fn parse_known_wire_bytes() {
let wire = [0x00, 0x00, 0x7A, 0x12, 0x00, 0x00, 0x01, 0x00];
let (parsed, off) = GeneveHeader::parse(&wire).unwrap();
assert_eq!(
parsed,
GeneveHeader {
control: false,
protocol: GENEVE_PROTOCOL_WIREGUARD,
vni: 1,
}
);
assert_eq!(off, GENEVE_FIXED_HEADER_LEN);
}
#[test]
fn parse_returns_payload_offset() {
let mut buf = GeneveHeader {
control: false,
protocol: GENEVE_PROTOCOL_WIREGUARD,
vni: 7,
}
.encode()
.to_vec();
buf.extend_from_slice(b"payload");
let (_, off) = GeneveHeader::parse(&buf).unwrap();
assert_eq!(&buf[off..], b"payload");
}
}