summaryrefslogtreecommitdiff
path: root/src/conf_mode/system_ipv6.py
blob: a2442d0099e09aed7e65044b036bb6b7176a13e2 (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
#!/usr/bin/env python3
#
# Copyright (C) 2019-2023 VyOS maintainers and contributors
#
# 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/>.

import os

from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configverify import verify_route_map
from vyos.template import render_to_string
from vyos.utils.dict import dict_search
from vyos.utils.file import write_file
from vyos.utils.process import is_systemd_service_active
from vyos.utils.system import sysctl_write
from vyos.configdep import set_dependents
from vyos.configdep import call_dependents
from vyos import ConfigError
from vyos import frr
from vyos import airbag
airbag.enable()

def get_config(config=None):
    if config:
        conf = config
    else:
        conf = Config()
    base = ['system', 'ipv6']

    opt = conf.get_config_dict(base, key_mangling=('-', '_'),
                               get_first_key=True,
                               with_recursive_defaults=True)

    # When working with FRR we need to know the corresponding address-family
    opt['afi'] = 'ipv6'

    # We also need the route-map information from the config
    #
    # XXX: one MUST always call this without the key_mangling() option! See
    # vyos.configverify.verify_common_route_maps() for more information.
    tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'],
                                                          get_first_key=True)}}
    # Merge policy dict into "regular" config dict
    opt = dict_merge(tmp, opt)

    # If IPv6 neighbor table size is set here and also manually in sysctl, the more
    # fine grained value from sysctl must win
    set_dependents('sysctl', conf)

    return opt

def verify(opt):
    if 'protocol' in opt:
        for protocol, protocol_options in opt['protocol'].items():
            if 'route_map' in protocol_options:
                verify_route_map(protocol_options['route_map'], opt)
    return

def generate(opt):
    opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt)
    return

def apply(opt):
    # configure multipath
    tmp = dict_search('multipath.layer4_hashing', opt)
    value = '1' if (tmp != None) else '0'
    sysctl_write('net.ipv6.fib_multipath_hash_policy', value)

    # Apply ND threshold values
    # table_size has a default value - thus the key always exists
    size = int(dict_search('neighbor.table_size', opt))
    # Amount upon reaching which the records begin to be cleared immediately
    sysctl_write('net.ipv6.neigh.default.gc_thresh3', size)
    # Amount after which the records begin to be cleaned after 5 seconds
    sysctl_write('net.ipv6.neigh.default.gc_thresh2', size // 2)
    # Minimum number of stored records is indicated which is not cleared
    sysctl_write('net.ipv6.neigh.default.gc_thresh1', size // 8)

    # enable/disable IPv6 forwarding
    tmp = dict_search('disable_forwarding', opt)
    value = '0' if (tmp != None) else '1'
    write_file('/proc/sys/net/ipv6/conf/all/forwarding', value)

    # configure IPv6 strict-dad
    tmp = dict_search('strict_dad', opt)
    value = '2' if (tmp != None) else '1'
    for root, dirs, files in os.walk('/proc/sys/net/ipv6/conf'):
        for name in files:
            if name == 'accept_dad':
                write_file(os.path.join(root, name), value)

    # During startup of vyos-router that brings up FRR, the service is not yet
    # running when this script is called first. Skip this part and wait for initial
    # commit of the configuration to trigger this statement
    if is_systemd_service_active('frr.service'):
        zebra_daemon = 'zebra'
        # Save original configuration prior to starting any commit actions
        frr_cfg = frr.FRRConfig()

        # The route-map used for the FIB (zebra) is part of the zebra daemon
        frr_cfg.load_configuration(zebra_daemon)
        frr_cfg.modify_section(r'no ipv6 nht resolve-via-default')
        frr_cfg.modify_section(r'ipv6 protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
        if 'frr_zebra_config' in opt:
            frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
        frr_cfg.commit_configuration(zebra_daemon)

    call_dependents()

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