use std::error::Error;
use std::fmt::Display;
use crate::ip::{parse_ip_data, parse_ip_header, IpData, IpSpecific};
use crate::packet_filter::parse_packet_filter;
use crate::packet_filter::PacketFilter;
use crate::protocol::parse_proto_info;
use crate::protocol::{ProtoInfo, Protocol};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq)]
pub struct LogParseError {
pub raw_log: String,
pub reason: String,
}
impl Display for LogParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"failed to parse: log {}, reason {}",
self.raw_log, self.reason
)
}
}
impl Error for LogParseError {}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FwLog {
pub packet_filter: PacketFilter,
pub ip_specific: IpSpecific,
pub ip_data: IpData,
pub protocol: Protocol,
pub proto_info: ProtoInfo,
}
pub fn parse_log(input: &str) -> Result<FwLog, LogParseError> {
let (next, packet_filter) = parse_packet_filter(input).map_err(|_| LogParseError {
raw_log: input.into(),
reason: "Failed to parse packet filter".into(),
})?;
let (next, (protocol, ip_header)) = parse_ip_header(next).map_err(|_| LogParseError {
raw_log: input.into(),
reason: "Failed to parse IP header".into(),
})?;
let (next, ip_data) = parse_ip_data(next, &ip_header).map_err(|_| LogParseError {
raw_log: input.into(),
reason: "Failed to parse IP data".into(),
})?;
let (_, proto_info) = parse_proto_info(next, &protocol.name).map_err(|_| LogParseError {
raw_log: input.into(),
reason: "Failed to parse protocol-specific information".into(),
})?;
let firewall_log = FwLog {
packet_filter,
ip_specific: ip_header,
ip_data,
protocol,
proto_info,
};
Ok(firewall_log)
}
#[cfg(test)]
mod test {
use super::*;
use crate::ip::IpV4;
use crate::packet_filter::Action::*;
use crate::packet_filter::Dir::*;
use crate::packet_filter::Reason::*;
use crate::packet_filter::RuleInfo;
use crate::protocol::Ports;
use crate::protocol::ProtoName::*;
use crate::protocol::TcpInfo;
use crate::protocol::UdpInfo;
use std::net::IpAddr;
use std::net::Ipv4Addr;
use std::str::FromStr;
#[test]
fn it_works_tcp() {
let log = "96,,,fae559338f65e11c53669fc3642c93c2,vlan0.20,match,pass,out,\
4,0x0,,127,61633,0,DF,6,tcp,\
52,192.168.10.15,192.168.20.14,\
52461,9100,0,S,3442468761,,64240,,mss;nop;wscale;nop;nop;sackOK";
let flog = parse_log(log).unwrap();
assert_eq!(
FwLog {
packet_filter: PacketFilter {
rule_info: RuleInfo {
number: 96,
subrulenr: None,
anchorname: None,
label: "fae559338f65e11c53669fc3642c93c2".into(),
},
interface: "vlan0.20".into(),
reason: Match,
action: Pass,
dir: Out,
},
ip_specific: IpSpecific::IpV4(IpV4 {
version: 4,
tos: 0,
ecn: None,
ttl: 127,
id: 61633,
offset: 0,
flags: "DF".into(),
},),
ip_data: IpData {
length: 52,
src: IpAddr::V4(Ipv4Addr::from_str("192.168.10.15").unwrap()),
dst: IpAddr::V4(Ipv4Addr::from_str("192.168.20.14").unwrap()),
},
protocol: Protocol { num: 6, name: Tcp },
proto_info: ProtoInfo::TcpInfo(TcpInfo {
ports: Ports {
srcport: 52461,
dstport: 9100,
},
data_len: 0,
flags: "S".into(),
sequence_number: "3442468761".into(),
ack_number: None,
window: 64240,
urg: None,
options: "mss;nop;wscale;nop;nop;sackOK".into(),
},),
},
flog
);
}
#[test]
fn it_works_udp() {
let log = "96,,,fae559338f65e11c53669fc3642c93c2,vlan0.20,match,pass,out,\
4,0x0,,127,58940,0,none,17,udp,\
106,192.168.10.15,192.168.20.11,49678,161,86";
let flog = parse_log(log).unwrap();
assert_eq!(
(FwLog {
packet_filter: PacketFilter {
rule_info: RuleInfo {
number: 96,
subrulenr: None,
anchorname: None,
label: "fae559338f65e11c53669fc3642c93c2".into(),
},
interface: "vlan0.20".into(),
reason: Match,
action: Pass,
dir: Out,
},
ip_specific: IpSpecific::IpV4(IpV4 {
version: 4,
tos: 0,
ecn: None,
ttl: 127,
id: 58940,
offset: 0,
flags: "none".into(),
},),
ip_data: IpData {
length: 106,
src: IpAddr::V4(Ipv4Addr::from_str("192.168.10.15").unwrap()),
dst: IpAddr::V4(Ipv4Addr::from_str("192.168.20.11").unwrap()),
},
protocol: Protocol { num: 17, name: Udp },
proto_info: ProtoInfo::UdpInfo(UdpInfo {
ports: Ports {
srcport: 49678,
dstport: 161,
},
data_len: 86,
},),
}),
flog
);
}
#[test]
fn packet_filter_fail() {
let log = "ab,,,fae559338f65e11c53669fc3642c93c2,vlan0.20,match,pass,out,\
4,0x0,,127,58940,0,none,17,udp,\
106,192.168.10.15,192.168.20.11,49678,161,86";
assert_eq!(
Err(LogParseError {
raw_log: log.into(),
reason: "Failed to parse packet filter".into()
}),
parse_log(log)
);
}
#[test]
fn ip_header_fail() {
let log = "96,,,fae559338f65e11c53669fc3642c93c2,vlan0.20,match,pass,out,\
4,0x0,,127ab,58940,0,none,17,udp,\
106,192.168.10.15,192.168.20.11,49678,161,86";
assert_eq!(
Err(LogParseError {
raw_log: log.into(),
reason: "Failed to parse IP header".into()
}),
parse_log(log)
);
}
#[test]
fn ip_data_fail() {
let log = "96,,,fae559338f65e11c53669fc3642c93c2,vlan0.20,match,pass,out,\
4,0x0,,127,58940,0,none,17,udp,\
106,192.168.a10.15,192.168.20.11,49678,161,86";
assert_eq!(
Err(LogParseError {
raw_log: log.into(),
reason: "Failed to parse IP data".into()
}),
parse_log(log)
);
}
#[test]
fn protocol_specific_fail() {
let log = "96,,,fae559338f65e11c53669fc3642c93c2,vlan0.20,match,pass,out,\
4,0x0,,127,58940,0,none,17,udp,\
106,192.168.10.15,192.168.20.11,49678as,161,86";
assert_eq!(
Err(LogParseError {
raw_log: log.into(),
reason: "Failed to parse protocol-specific information".into()
}),
parse_log(log)
);
}
}