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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use serde::Serialize;
use num_traits::FromPrimitive;

/// BGP Role
///
/// Defined in [RFC9234](https://www.iana.org/go/rfc9234).
#[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone, Serialize)]
pub enum BgpRole {
    Provider = 0,
    RouteServer = 1,
    RouteServerClient = 2,
    Customer = 3,
    Peer = 4,
}

pub fn parse_bgp_role_value(value: &u8) -> Option<BgpRole> {
    BgpRole::from_u8(*value)
}

/// Validate the local-remote BGP Role pairs.
///
/// This function checks the role correctness by following the description in [Section 4.2 of RFC9234](https://www.rfc-editor.org/rfc/rfc9234.html#section-4.2).
///
/// The acceptable local-remote BGP pairs are:
///
/// Local AS Role | Remote AS Role
/// --- | ---
/// Provider | Customer
/// Customer | Provider
/// RouteServer | RouterServer-Client
/// RouterServer-Client | RouteServer
/// Peer | Peer
///
pub fn validate_role_pairs(local_role: &BgpRole, remote_role: &BgpRole) -> bool {
    match local_role {
        BgpRole::Provider => {
            if let BgpRole::Customer =  remote_role {
                return true
            }
            return false
        }
        BgpRole::RouteServer => {
            if let BgpRole::RouteServerClient =  remote_role {
                return true
            }
            return false
        }
        BgpRole::RouteServerClient => {
            if let BgpRole::RouteServer =  remote_role {
                return true
            }
            return false
        }
        BgpRole::Customer => {
            if let BgpRole::Provider =  remote_role {
                return true
            }
            return false
        }
        BgpRole::Peer => {
            if let BgpRole::Peer =  remote_role {
                return true
            }
            return false
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::bgp::BgpRole::*;
    use super::*;

    #[test]
    fn test_bgp_role_validation() {
        let mut local: BgpRole;
        let mut remote: BgpRole;

        local = Provider;
        remote = Customer;
        assert_eq!(validate_role_pairs(&local, &remote), true);
        for remote in [ Provider, Peer, RouteServer, RouteServerClient] {
            assert_eq!(validate_role_pairs(&local, &remote), false);
        }

        local = Customer;
        remote = Provider;
        assert_eq!(validate_role_pairs(&local, &remote), true);
        for remote in [ Customer, Peer, RouteServer, RouteServerClient] {
            assert_eq!(validate_role_pairs(&local, &remote), false);
        }

        local = RouteServer;
        remote = RouteServerClient;
        assert_eq!(validate_role_pairs(&local, &remote), true);
        for remote in [ Provider, Customer, Peer, RouteServer] {
            assert_eq!(validate_role_pairs(&local, &remote), false);
        }

        local = RouteServerClient;
        remote = RouteServer;
        assert_eq!(validate_role_pairs(&local, &remote), true);
        for remote in [ Provider, Customer, Peer, RouteServerClient] {
            assert_eq!(validate_role_pairs(&local, &remote), false);
        }

        local = Peer;
        remote = Peer;
        assert_eq!(validate_role_pairs(&local, &remote), true);
        for remote in [ Provider, Customer, RouteServer, RouteServerClient] {
            assert_eq!(validate_role_pairs(&local, &remote), false);
        }
    }
}