use crate::extractor::{Extracted, FlowExtractor};
use crate::view::PacketView;
use super::parse;
pub const GRE_IP_PROTO: u8 = 47;
const ETH_IPV4: u16 = 0x0800;
const ETH_IPV6: u16 = 0x86dd;
const ETH_TEB: u16 = 0x6558;
#[derive(Debug, Clone, Copy)]
pub struct InnerGre<E> {
pub extractor: E,
}
impl<E> InnerGre<E> {
pub fn new(extractor: E) -> Self {
Self { extractor }
}
}
impl<E: FlowExtractor> FlowExtractor for InnerGre<E> {
type Key = E::Key;
fn extract(&self, view: PacketView<'_>) -> Option<Extracted<E::Key>> {
let parsed = parse::parse_eth(view.frame)?;
let ip = parsed.ip?;
if ip.proto != GRE_IP_PROTO {
return None;
}
let (ethertype, after_gre) = peel_gre(ip.l4_payload)?;
let synthetic = synthesize_eth(ethertype, after_gre)?;
self.extractor.extract(PacketView {
frame: &synthetic,
timestamp: view.timestamp,
})
}
}
fn peel_gre(bytes: &[u8]) -> Option<(u16, &[u8])> {
if bytes.len() < 4 {
return None;
}
let flags_hi = bytes[0];
let flags_lo = bytes[1];
let c = flags_hi & 0x80 != 0; let k = flags_hi & 0x20 != 0; let s = flags_hi & 0x10 != 0; let version = flags_lo & 0x07;
if version != 0 {
return None;
}
let ethertype = u16::from_be_bytes([bytes[2], bytes[3]]);
let mut header_len = 4usize;
if c {
header_len += 4;
}
if k {
header_len += 4;
}
if s {
header_len += 4;
}
if bytes.len() < header_len {
return None;
}
Some((ethertype, &bytes[header_len..]))
}
fn synthesize_eth(ethertype: u16, inner: &[u8]) -> Option<Vec<u8>> {
if inner.is_empty() {
return None;
}
if ethertype == ETH_TEB {
return Some(inner.to_vec());
}
if ethertype != ETH_IPV4 && ethertype != ETH_IPV6 {
return None;
}
let mut out = Vec::with_capacity(14 + inner.len());
out.extend_from_slice(&[0u8; 12]); out.extend_from_slice(ðertype.to_be_bytes());
out.extend_from_slice(inner);
Some(out)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Timestamp;
use crate::extract::FiveTuple;
use etherparse::{Ethernet2Header, IpNumber, Ipv4Header, TcpHeader};
fn build_ipv4_in_gre(gre_flags: u8, gre_extra: usize) -> Vec<u8> {
let mut tcp = TcpHeader::new(12345, 80, 0, 8192);
tcp.syn = true;
let inner_ip = Ipv4Header::new(
tcp.header_len() as u16,
64,
IpNumber::TCP,
[1, 1, 1, 1],
[2, 2, 2, 2],
)
.unwrap();
let mut inner_l3 = Vec::new();
inner_ip.write(&mut inner_l3).unwrap();
tcp.write(&mut inner_l3).unwrap();
let mut gre = Vec::new();
gre.push(gre_flags);
gre.push(0); gre.extend_from_slice(Ð_IPV4.to_be_bytes());
gre.extend(std::iter::repeat_n(0u8, gre_extra));
let outer_ip_len = (gre.len() + inner_l3.len()) as u16;
let outer_ip = Ipv4Header::new(
outer_ip_len,
64,
IpNumber(GRE_IP_PROTO),
[10, 0, 0, 1],
[10, 0, 0, 2],
)
.unwrap();
let eth = Ethernet2Header {
source: [0; 6],
destination: [0; 6],
ether_type: etherparse::EtherType::IPV4,
};
let mut frame = Vec::new();
eth.write(&mut frame).unwrap();
outer_ip.write(&mut frame).unwrap();
frame.extend_from_slice(&gre);
frame.extend_from_slice(&inner_l3);
frame
}
#[test]
fn ipv4_in_gre_no_extras() {
let f = build_ipv4_in_gre(0, 0);
let v = PacketView::new(&f, Timestamp::default());
let e = InnerGre::new(FiveTuple::bidirectional());
let extracted = e.extract(v).expect("extract");
let dbg = format!("{:?}", extracted.key);
assert!(dbg.contains("1.1.1.1"), "{dbg}");
assert!(dbg.contains("2.2.2.2"), "{dbg}");
}
#[test]
fn ipv4_in_gre_with_checksum() {
let f = build_ipv4_in_gre(0x80, 4);
let v = PacketView::new(&f, Timestamp::default());
let e = InnerGre::new(FiveTuple::bidirectional());
assert!(e.extract(v).is_some());
}
#[test]
fn ipv4_in_gre_with_key() {
let f = build_ipv4_in_gre(0x20, 4);
let v = PacketView::new(&f, Timestamp::default());
let e = InnerGre::new(FiveTuple::bidirectional());
assert!(e.extract(v).is_some());
}
#[test]
fn ipv4_in_gre_with_checksum_and_key() {
let f = build_ipv4_in_gre(0x80 | 0x20, 8);
let v = PacketView::new(&f, Timestamp::default());
let e = InnerGre::new(FiveTuple::bidirectional());
assert!(e.extract(v).is_some());
}
#[test]
fn rejects_non_gre_ip() {
let bytes = crate::extract::parse::test_frames::ipv4_tcp(
[0; 6],
[0; 6],
[10, 0, 0, 1],
[10, 0, 0, 2],
1234,
80,
0,
0,
0x02,
b"",
);
let v = PacketView::new(&bytes, Timestamp::default());
let e = InnerGre::new(FiveTuple::bidirectional());
assert!(e.extract(v).is_none());
}
#[test]
fn rejects_pptp_version_1() {
let f = build_ipv4_in_gre(0, 0);
let mut bad = f.clone();
bad[34 + 1] = 1;
let v = PacketView::new(&bad, Timestamp::default());
let e = InnerGre::new(FiveTuple::bidirectional());
assert!(e.extract(v).is_none());
}
}