#!/usr/bin/env python3 # # Copyright (C) 2018-2020 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 . import os from sys import exit from copy import deepcopy from jinja2 import FileSystemLoader, Environment from netifaces import interfaces from vyos.config import Config from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError from vyos.util import call config_file = r'/etc/igmpproxy.conf' default_config_data = { 'disable': False, 'disable_quickleave': False, 'interfaces': [], } def get_config(): igmp_proxy = deepcopy(default_config_data) conf = Config() base = ['protocols', 'igmp-proxy'] if not conf.exists(base): return None else: conf.set_level(base) # Network interfaces to listen on if conf.exists(['disable']): igmp_proxy['disable'] = True # Option to disable "quickleave" if conf.exists(['disable-quickleave']): igmp_proxy['disable_quickleave'] = True for intf in conf.list_nodes(['interface']): conf.set_level(base + ['interface', intf]) interface = { 'name': intf, 'alt_subnet': [], 'role': 'downstream', 'threshold': '1', 'whitelist': [] } if conf.exists(['alt-subnet']): interface['alt_subnet'] = conf.return_values(['alt-subnet']) if conf.exists(['role']): interface['role'] = conf.return_value(['role']) if conf.exists(['threshold']): interface['threshold'] = conf.return_value(['threshold']) if conf.exists(['whitelist']): interface['whitelist'] = conf.return_values(['whitelist']) # Append interface configuration to global configuration list igmp_proxy['interfaces'].append(interface) return igmp_proxy def verify(igmp_proxy): # bail out early - looks like removal from running config if igmp_proxy is None: return None # bail out early - service is disabled if igmp_proxy['disable']: return None # at least two interfaces are required, one upstream and one downstream if len(igmp_proxy['interfaces']) < 2: raise ConfigError('Must define an upstream and at least 1 downstream interface!') upstream = 0 for interface in igmp_proxy['interfaces']: if interface['name'] not in interfaces(): raise ConfigError('Interface "{}" does not exist'.format(interface['name'])) if "upstream" == interface['role']: upstream += 1 if upstream == 0: raise ConfigError('At least 1 upstream interface is required!') elif upstream > 1: raise ConfigError('Only 1 upstream interface allowed!') return None def generate(igmp_proxy): # bail out early - looks like removal from running config if igmp_proxy is None: return None # bail out early - service is disabled, but inform user if igmp_proxy['disable']: print('Warning: IGMP Proxy will be deactivated because it is disabled') return None # Prepare Jinja2 template loader from files tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'igmp-proxy') fs_loader = FileSystemLoader(tmpl_path) env = Environment(loader=fs_loader) tmpl = env.get_template('igmpproxy.conf.tmpl') config_text = tmpl.render(igmp_proxy) with open(config_file, 'w') as f: f.write(config_text) return None def apply(igmp_proxy): if igmp_proxy is None or igmp_proxy['disable']: # IGMP Proxy support is removed in the commit call('sudo systemctl stop igmpproxy.service') if os.path.exists(config_file): os.unlink(config_file) else: call('systemctl restart igmpproxy.service') return None if __name__ == '__main__': try: c = get_config() verify(c) generate(c) apply(c) except ConfigError as e: print(e) exit(1)