summaryrefslogtreecommitdiff
path: root/src/conf_mode/interfaces_pseudo-ethernet.py
blob: cb0f7e7d0d9b3087cae230b034c01438727508f6 (plain)
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from sys import exit

from vyos.config import Config
from vyos.configdep import set_dependents
from vyos.configdep import call_dependents
from vyos.configdict import get_interface_dict
from vyos.configdict import is_source_interface
from vyos.configdict import is_node_changed
from vyos.configdict import is_vrf_changed
from vyos.configverify import verify_vrf
from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_source_interface
from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_mtu_parent
from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_mirror_redirect
from vyos.ifconfig import MACVLANIf
from vyos.utils.network import interface_exists
from vyos import ConfigError

from vyos import airbag
airbag.enable()

def get_config(config=None):
    """
    Retrieve CLI config as dictionary. Dictionary can never be empty, as at
    least the interface name will be added or a deleted flag
    """
    if config:
        conf = config
    else:
        conf = Config()
    base = ['interfaces', 'pseudo-ethernet']
    ifname, peth = get_interface_dict(conf, base)

    mode = is_node_changed(conf, ['mode'])
    if mode: peth.update({'shutdown_required' : {}})

    if is_node_changed(conf, base + [ifname, 'mode']):
        peth.update({'rebuild_required': {}})

    if 'source_interface' in peth:
        _, peth['parent'] = get_interface_dict(conf, ['interfaces', 'ethernet'],
                                               peth['source_interface'])
        # test if source-interface is maybe already used by another interface
        tmp = is_source_interface(conf, peth['source_interface'], ['macsec'])
        if tmp and tmp != ifname: peth.update({'is_source_interface' : tmp})

    # Protocols static arp dependency
    if 'static_arp' in peth:
        set_dependents('static_arp', conf)

    # Check vrf membership, to ensure firewall is updated
    if is_vrf_changed(conf, ifname):
        set_dependents('firewall', conf)

    return peth

def _verify_anycast_gateway(peth: dict):
    """Validate anycast-gateway requirements."""

    if 'anycast_gateway' not in peth:
        return

    ifname = peth['ifname']

    # Requirement 1: MAC address must be explicitly configured
    if 'mac' not in peth:
        raise ConfigError(
            f'Anycast-gateway requires an explicit MAC address to be set on interface {ifname}. '
            f'Use: set interfaces pseudo-ethernet {ifname} mac <mac>'
        )

    # Requirement 2: source-interface must be a bridge or bridge sub-interface
    source_iface = peth.get('source_interface')
    if not source_iface:
        raise ConfigError(
            f'Anycast-gateway requires source-interface to be set on interface {ifname}'
        )

    if not source_iface.startswith('br'):
        raise ConfigError(
            'Anycast-gateway requires source-interface to be a bridge '
            'or a bridge vlan interface (e.g. br0 or br0.100), but '
            f'"{source_iface}" is neither of these two.'
        )

def verify(peth):
    if 'deleted' in peth:
        verify_bridge_delete(peth)
        return None

    verify_source_interface(peth)
    verify_vrf(peth)
    verify_address(peth)
    verify_mtu_parent(peth, peth['parent'])
    verify_mtu_ipv6(peth)
    verify_mirror_redirect(peth)
    # use common function to verify VLAN configuration
    verify_vlan_config(peth)

    _verify_anycast_gateway(peth)

    return None

def generate(peth):
    return None

def apply(peth):
    # Check if the MACVLAN interface already exists
    if 'rebuild_required' in peth or 'deleted' in peth:
        if interface_exists(peth['ifname']):
            p = MACVLANIf(**peth)
            # MACVLAN is always needs to be recreated,
            # thus we can simply always delete it first.
            p.remove()

    if 'deleted' not in peth:
        p = MACVLANIf(**peth)
        p.update(peth)

    # run the dependents
    call_dependents()

    return None

if __name__ == '__main__':
    try:
        c = get_config()
        verify(c)
        generate(c)
        apply(c)
    except ConfigError as e:
        print(e)
        exit(1)