rustflow_export 0.20.0

IPFIX exporter for captured network traffic; the `rustflow export` command
Documentation
use rustflow_core::common::InformationElement;
use rustflow_core::ipfix::parser::{
    FieldSpecifier, IPFIX_VARIABLE_LENGTH, OptionsTemplateRecord, TemplateRecord,
};

use crate::capture::Link;
use crate::meter::Mode;
use crate::sampler::Sampling;

pub const FLOW_IPV4_TEMPLATE_ID: u16 = 256;
pub const FLOW_IPV6_TEMPLATE_ID: u16 = 257;
pub const OPTIONS_TEMPLATE_ID: u16 = 258;
pub const PACKET_FRAME_TEMPLATE_ID: u16 = 259;
pub const PACKET_IP_TEMPLATE_ID: u16 = 260;

pub fn create_templates(mode: Mode) -> Vec<TemplateRecord> {
    match mode {
        Mode::Flow => create_flow_templates().to_vec(),
        Mode::Packet => [Link::Ethernet, Link::Ip]
            .map(create_packet_template)
            .to_vec(),
    }
}

pub fn packet_template_id(link: Link) -> u16 {
    match link {
        Link::Ethernet => PACKET_FRAME_TEMPLATE_ID,
        Link::Ip => PACKET_IP_TEMPLATE_ID,
    }
}

pub fn packet_fields(link: Link) -> [FieldSpecifier; 2] {
    use InformationElement::*;

    let section = match link {
        Link::Ethernet => DataLinkFrameSection,
        Link::Ip => IpHeaderPacketSection,
    };
    [
        FieldSpecifier::from_ie(DataLinkFrameSize, 2),
        FieldSpecifier::from_ie(section, IPFIX_VARIABLE_LENGTH),
    ]
}

fn create_packet_template(link: Link) -> TemplateRecord {
    TemplateRecord::new(packet_template_id(link), packet_fields(link).to_vec())
}

fn create_flow_templates() -> [TemplateRecord; 2] {
    use InformationElement::*;

    [
        flow_template(
            FLOW_IPV4_TEMPLATE_ID,
            [
                FieldSpecifier::from_ie(SourceIpv4Address, 4),
                FieldSpecifier::from_ie(DestinationIpv4Address, 4),
            ],
        ),
        flow_template(
            FLOW_IPV6_TEMPLATE_ID,
            [
                FieldSpecifier::from_ie(SourceIpv6Address, 16),
                FieldSpecifier::from_ie(DestinationIpv6Address, 16),
            ],
        ),
    ]
}

fn flow_template(template_id: u16, addresses: [FieldSpecifier; 2]) -> TemplateRecord {
    use InformationElement::*;

    let mut fields = addresses.to_vec();
    fields.extend([
        FieldSpecifier::from_ie(IpVersion, 1),
        FieldSpecifier::from_ie(ProtocolIdentifier, 1),
        FieldSpecifier::from_ie(SourceTransportPort, 2),
        FieldSpecifier::from_ie(DestinationTransportPort, 2),
        FieldSpecifier::from_ie(OctetDeltaCount, 8),
        FieldSpecifier::from_ie(PacketDeltaCount, 8),
        FieldSpecifier::from_ie(TcpControlBits, 2),
        FieldSpecifier::from_ie(FlowStartMilliseconds, 8),
        FieldSpecifier::from_ie(FlowEndMilliseconds, 8),
        FieldSpecifier::from_ie(FlowEndReason, 1),
    ]);

    TemplateRecord::new(template_id, fields)
}

pub fn create_options_template(sampling: Sampling) -> OptionsTemplateRecord {
    use InformationElement::*;

    let mut fields = vec![FieldSpecifier::from_ie(ObservationDomainId, 4)];
    match sampling {
        Sampling::Count { .. } => fields.extend([
            FieldSpecifier::from_ie(SamplingInterval, 4),
            FieldSpecifier::from_ie(SelectorAlgorithm, 2),
            FieldSpecifier::from_ie(SamplingPacketInterval, 4),
            FieldSpecifier::from_ie(SamplingPacketSpace, 4),
        ]),
    }

    OptionsTemplateRecord::new(OPTIONS_TEMPLATE_ID, 1, fields)
}