1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// Copyright 2021 Mullvad VPN AB.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/// ICMP type (and code). Used to match a rule against an ICMP packets `type` and `code` fields.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum IcmpType {
    /// Echo reply.
    EchoRep,
    /// Destination unreachable
    Unreach(IcmpUnreachCode),
    /// Echo request.
    EchoReq,
    /// Traceroute.
    Trace,
    /// ICMPv6
    Icmp6(Icmp6Type),
}

/// ICMP code fields for destination unreachable ICMP packet's ([`IcmpType::Unreach`]).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[repr(u8)]
pub enum IcmpUnreachCode {
    /// Network unreachable.
    NetUnreach = 0,
    /// Host unreachable.
    HostUnreach = 1,
    /// Protocol unreachable.
    ProtoUnreach = 2,
    /// Port unreachable.
    PortUnreach = 3,
    /// Fragmentation needed but DF bit set.
    NeedFrag = 4,
}

/// Values for the `type` field in ICMPv6 packets.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[repr(u8)]
pub enum Icmp6Type {
    /// Router solicitation.
    RouterSol = 133,
    /// Router advertisement.
    RouterAdv = 134,
    /// Neighbor solicitation.
    NeighbrSol = 135,
    /// Neighbor advertisement.
    NeighbrAdv = 136,
    /// Shorter route exists
    Redir = 137,
}

impl IcmpType {
    /// Returns the FFI representation for this ICMP type
    fn raw_type(&self) -> u8 {
        use IcmpType::*;
        match self {
            EchoRep => 0,
            Unreach(_) => 3,
            EchoReq => 8,
            Trace => 30,
            Icmp6(icmp6_type) => *icmp6_type as u8,
        }
    }

    /// Returns the FFI representation of the code for this ICMP type.
    /// Returns `None` if this ICMP type does not use the code field.
    fn raw_code(&self) -> Option<u8> {
        use IcmpType::*;
        match self {
            Unreach(unreach_code) => Some(*unreach_code as u8),
            _ => None,
        }
    }
}

impl crate::conversion::CopyTo<crate::ffi::pfvar::pf_rule> for IcmpType {
    fn copy_to(&self, pf_rule: &mut crate::ffi::pfvar::pf_rule) {
        // The field should be set to one higher than the constants.
        // See OpenBSD implementation of the `pfctl` CLI tool for reference.
        pf_rule.type_ = self.raw_type() + 1;
        pf_rule.code = match self.raw_code() {
            Some(raw_code) => raw_code + 1,
            None => 0,
        };
    }
}