#!/usr/bin/env python3 # # Copyright (C) 2017 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 sys import os import fnmatch import jinja2 from vyos.config import Config from vyos import ConfigError config_file = r'/etc/default/udp-broadcast-relay' config_tmpl = """ ### Autogenerated by bcast_relay.py ### # UDP broadcast relay configuration for instance {{ id }} {%- if description %} # Comment: {{ description }} {% endif %} DAEMON_ARGS="{% if address %}-s {{ address }} {% endif %}{{ id }} {{ port }} {{ interfaces | join(' ') }}" """ default_config_data = { 'disabled': False, 'instances': [] } def get_config(): relay = default_config_data conf = Config() if not conf.exists('service broadcast-relay'): return None else: conf.set_level('service broadcast-relay') # Service can be disabled by user if conf.exists('disable'): relay['disabled'] = True return relay # Parse configuration of each individual instance if conf.exists('id'): for id in conf.list_nodes('id'): conf.set_level('service broadcast-relay id {0}'.format(id)) config = { 'id': id, 'disabled': False, 'address': '', 'description': '', 'interfaces': [], 'port': '' } # Check if individual broadcast relay service is disabled if conf.exists('disable'): config['disabled'] = True # Source IP of forwarded packets, if empty original senders address is used if conf.exists('address'): config['address'] = conf.return_value('address') # A description for each individual broadcast relay service if conf.exists('description'): config['description'] = conf.return_value('description') # UDP port to listen on for broadcast frames if conf.exists('port'): config['port'] = conf.return_value('port') # Network interfaces to listen on for broadcast frames to be relayed if conf.exists('interface'): config['interfaces'] = conf.return_values('interface') relay['instances'].append(config) return relay def verify(relay): if relay is None: return None if relay['disabled']: return None for r in relay['instances']: # we don't have to check this instance when it's disabled if r['disabled']: continue # we certainly require a UDP port to listen to if not r['port']: raise ConfigError('UDP broadcast relay "{0}" requires a port number'.format(r['id'])) # Relaying data without two interface is kinda senseless ... if len(r['interfaces']) < 2: raise ConfigError('UDP broadcast relay "id {0}" requires at least 2 interfaces'.format(r['id'])) return None def generate(relay): if relay is None: return None config_dir = os.path.dirname(config_file) config_filename = os.path.basename(config_file) active_configs = [] for config in fnmatch.filter(os.listdir(config_dir), config_filename + '*'): # determine prefix length to identify service instance prefix_len = len(config_filename) active_configs.append(config[prefix_len:]) # sort our list active_configs.sort() # delete old configuration files for id in active_configs[:]: if os.path.exists(config_file + id): os.unlink(config_file + id) # If the service is disabled, we can bail out here if relay['disabled']: print('Warning: UDP broadcast relay service will be deactivated because it is disabled') return None for r in relay['instances']: # Skip writing instance config when it's disabled if r['disabled']: continue # configuration filename contains instance id file = config_file + str(r['id']) tmpl = jinja2.Template(config_tmpl) config_text = tmpl.render(r) with open(file, 'w') as f: f.write(config_text) return None def apply(relay): # first stop all running services os.system('sudo systemctl stop udp-broadcast-relay@{1..99}') if (relay is None) or relay['disabled']: return None # start only required service instances for r in relay['instances']: # Don't start individual instance when it's disabled if r['disabled']: continue os.system('sudo systemctl start udp-broadcast-relay@{0}'.format(r['id'])) return None if __name__ == '__main__': try: c = get_config() verify(c) generate(c) apply(c) except ConfigError as e: print(e) sys.exit(1)