From cc95d6f89282c9d66120f2a9b40a9aaff2f981bb Mon Sep 17 00:00:00 2001 From: fmertz Date: Wed, 24 Jun 2020 16:25:21 +0000 Subject: system display: T2564 Extend VyOS to support appliance LCDs Added support for system LCDs under CLI system display +data/templates/system-display/LCDd.conf.tmpl template for LCDd server configuration file +data/templates/system-display/lcdproc.conf.tmpl template for lcdproc client configuration file +interface-definitions/system-display.xml.in CLI for system display +src/conf_mode/system-display.py processing code for system display +src/systemd/lcdproc.service systemd service definition file for lcdproc client CLI: system display model (SDEC|EZIO) system display config (enabled|disabled) system display show host (cpu|cpu-all|cpu-hist|disk|load-hist|memory|proc|uptime) network interface alias units (bps|Bps|pps) clock (big|mini|date-time) title system display duration system display hello system display bye --- interface-definitions/system-display.xml.in | 245 ++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 interface-definitions/system-display.xml.in (limited to 'interface-definitions') diff --git a/interface-definitions/system-display.xml.in b/interface-definitions/system-display.xml.in new file mode 100644 index 000000000..7a5cf9283 --- /dev/null +++ b/interface-definitions/system-display.xml.in @@ -0,0 +1,245 @@ + + + + + + + + System display LCD/VFD/LED + 400 + + + + + + Model of the display attached to this system [REQUIRED] + + SDEC EZIO + + + (SDEC|EZIO) + + Invalid system display model + + SDEC + Display model for Lanner, Watchguard, Nexcom NSA, Sophos UTM appliances with built-in SDEC LCD + + + EZIO + Display model for Portwell, Caswell appliances with built-in EZIO-100 or EZIO-300 LCD + + + + + + + Configuration status + + enabled disabled + + + (enabled|disabled) + + Invalid system display config status + + enabled + Turn system display configuration ON + + + disabled + Turn system display configuration OFF + + + + + + Select the screens for the system display [REQUIRED] + + + + + + + Select host screens for the system display + + cpu cpu-all cpu-hist disk load-hist memory proc uptime + + + (cpu|cpu-all|cpu-hist|disk|load-hist|memory|proc|uptime) + + Invalid host screen + + cpu + Detailed CPU usage + + + cpu-all + CPU usage overview (one line per CPU) + + + cpu-hist + CPU usage histogram + + + disk + File systems fill level + + + load-hist + Load histogram + + + memory + Memory and swap usage + + + proc + Top processes by size + + + uptime + System uptime + + + + + + + Network settings for system display + + + + + + Show network traffic on the system display [Max 3 interfaces] + + + + + + + + Interface alias + + [A-Za-z0-9]{1,10} + + Invalid alias, must be 1 to 10 char or digit + + + + + + + + Unit for network details + + bps Bps pps + + + (bps|Bps|pps) + + Invalid network detail unit + + bps + Bit(s) per second + + + Bps + Byte(s) per second + + + pps + Packet(s) per second + + + + + + + + + + Show a clock on the system display + + big mini date-time + + + (big|mini|date-time) + + Invalid clock format + + big + Multi-line clock + + + mini + Minimal clock + + + date-time + Clock with Date and Time + + + + + + + Screen title to show on the system display + + [A-Za-z0-9]{1,16} + + Invalid title, must be 1 to 16 char or digit + + + + + + + + Time in sec to show each screen on the system display + + 1-30 + Numer of seconds + + + + + + + + + + Message to show when system display first starts + + .{1,16} + + Hello message must be 1 to 16 char + + + + + + Message to show when system display stops + + .{1,16} + + Bye message must be 1 to 16 char + + + + + + + -- cgit v1.2.3 From 3e8b6841db6ab803b898376754b469954d212cf7 Mon Sep 17 00:00:00 2001 From: fmertz Date: Sun, 28 Jun 2020 18:27:11 +0000 Subject: system display: T2564: Lowercase model names * data/templates/system-display/LCDd.conf.tmpl Lowercase sdec ezio * interface-definitions/system-display.xml.in Lowercase sdec ezio --- data/templates/system-display/LCDd.conf.tmpl | 6 +++--- interface-definitions/system-display.xml.in | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/system-display/LCDd.conf.tmpl b/data/templates/system-display/LCDd.conf.tmpl index 0b605e09d..8079dcb76 100644 --- a/data/templates/system-display/LCDd.conf.tmpl +++ b/data/templates/system-display/LCDd.conf.tmpl @@ -52,11 +52,11 @@ DriverPath=/usr/lib/x86_64-linux-gnu/lcdproc/ # Olimex_MOD_LCD1x9, picolcd, pyramid, rawserial, sdeclcd, sed1330, # sed1520, serialPOS, serialVFD, shuttleVFD, sli, stv5730, svga, t6963, # text, tyan, ula200, vlsys_m428, xosd, yard2LCD -{%- if model == 'SDEC' %} +{%- if model == 'sdec' %} Driver=sdeclcd {%- endif %} -{%- if model == 'EZIO' %} +{%- if model == 'ezio' %} Driver=hd44780 {%- endif %} @@ -544,7 +544,7 @@ Speed=19200 ## Hitachi HD44780 driver ## [hd44780] -{%- if model == 'EZIO' %} +{%- if model == 'ezio' %} ConnectionType=ezio Device=/dev/ttyS1 Keypad=yes diff --git a/interface-definitions/system-display.xml.in b/interface-definitions/system-display.xml.in index 7a5cf9283..ac9f17b6c 100644 --- a/interface-definitions/system-display.xml.in +++ b/interface-definitions/system-display.xml.in @@ -1,7 +1,7 @@ + Model of the display attached to this system [REQUIRED] - SDEC EZIO + sdec ezio - (SDEC|EZIO) + (sdec|ezio) Invalid system display model - SDEC + sdec Display model for Lanner, Watchguard, Nexcom NSA, Sophos UTM appliances with built-in SDEC LCD - EZIO + ezio Display model for Portwell, Caswell appliances with built-in EZIO-100 or EZIO-300 LCD -- cgit v1.2.3 From f3e0a409ee01e1b3ffa0c9a267434a7725f61231 Mon Sep 17 00:00:00 2001 From: fmertz Date: Sun, 28 Jun 2020 20:36:19 +0000 Subject: system display: T2564: Replace "config (enabled|disabled)" with "display disabled" * interface-definitions/system-display.xml.in system disply disabled * src/conf_mode/system-display.py system display disabled --- interface-definitions/system-display.xml.in | 26 ++++++-------------------- src/conf_mode/system-display.py | 2 +- 2 files changed, 7 insertions(+), 21 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/system-display.xml.in b/interface-definitions/system-display.xml.in index ac9f17b6c..afa6dba10 100644 --- a/interface-definitions/system-display.xml.in +++ b/interface-definitions/system-display.xml.in @@ -2,7 +2,6 @@ @@ -42,27 +42,13 @@ - - + + - Configuration status - - enabled disabled - - - (enabled|disabled) - - Invalid system display config status - - enabled - Turn system display configuration ON - - - disabled - Turn system display configuration OFF - + Disable sytem display + - + Select the screens for the system display [REQUIRED] diff --git a/src/conf_mode/system-display.py b/src/conf_mode/system-display.py index 4db375d8e..e30d591cc 100755 --- a/src/conf_mode/system-display.py +++ b/src/conf_mode/system-display.py @@ -66,7 +66,7 @@ def verify(c): return None def apply(c): - if not c or c['config'] == 'disabled': + if not c or 'disabled' in c.keys(): # Stop client first run('systemctl stop lcdproc.service') # Stop server next -- cgit v1.2.3 From d4199a17adeac422882013a8951d18b7c3b74f2a Mon Sep 17 00:00:00 2001 From: fmertz Date: Mon, 29 Jun 2020 14:45:57 +0000 Subject: system display: T2564: Changed "duration" to "time" * data/templates/system-display/LCDd.conf.tmpl duration to time * interface-definitions/system-display.xml.in duration to time --- data/templates/system-display/LCDd.conf.tmpl | 2 +- interface-definitions/system-display.xml.in | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/system-display/LCDd.conf.tmpl b/data/templates/system-display/LCDd.conf.tmpl index 8079dcb76..0ef796c76 100644 --- a/data/templates/system-display/LCDd.conf.tmpl +++ b/data/templates/system-display/LCDd.conf.tmpl @@ -92,7 +92,7 @@ GoodBye="{%- if bye %}{{ bye }}{%- else %}Bye from VyOS{%- endif %}" #FrameInterval=125000 # Sets the default time in seconds to displays a screen. [default: 4] -WaitTime={%- if duration %}{{ duration }}{%- else%}4{%- endif %} +WaitTime={%- if time %}{{ time }}{%- else%}4{%- endif %} # If set to no, LCDd will start with screen rotation disabled. This has the # same effect as if the ToggleRotateKey had been pressed. Rotation will start diff --git a/interface-definitions/system-display.xml.in b/interface-definitions/system-display.xml.in index afa6dba10..0439288a0 100644 --- a/interface-definitions/system-display.xml.in +++ b/interface-definitions/system-display.xml.in @@ -8,7 +8,7 @@ clock (big|mini|date-time) title - system display duration + system display time system display hello system display bye system display disabled @@ -191,8 +191,8 @@ - - + + Time in sec to show each screen on the system display @@ -203,7 +203,7 @@ - + -- cgit v1.2.3 From b5e0a1796cc7887245ae79c759f709e1705f5e93 Mon Sep 17 00:00:00 2001 From: fmertz Date: Sun, 5 Jul 2020 15:58:33 +0000 Subject: system display: T2564: Added test model * data/templates/system-display/LCDd.conf.tmpl * interface-definitions/system-display.xml.in May need to ba backed out, test code only for USB CrystalFontz CF533 --- data/templates/system-display/LCDd.conf.tmpl | 45 +++++++--------------------- interface-definitions/system-display.xml.in | 10 +++++-- 2 files changed, 17 insertions(+), 38 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/system-display/LCDd.conf.tmpl b/data/templates/system-display/LCDd.conf.tmpl index 0ef796c76..1dd646202 100644 --- a/data/templates/system-display/LCDd.conf.tmpl +++ b/data/templates/system-display/LCDd.conf.tmpl @@ -60,6 +60,10 @@ Driver=sdeclcd Driver=hd44780 {%- endif %} +{%- if model == 'test' %} +Driver=CFontzPacket +{%- endif %} + # Tells the driver to bind to the given interface. [default: 127.0.0.1] #Bind=127.0.0.1 @@ -191,44 +195,15 @@ Reboot=no ## CrystalFontz packet driver (for CFA533, CFA631, CFA633 & CFA635) ## [CFontzPacket] - -# Select the LCD model [default: 633; legal: 533, 631, 633, 635] -Model=633 - -# Select the output device to use [default: /dev/lcd] -Device=/dev/ttyS1 - -# Set the initial contrast [default: 560; legal: 0 - 1000] +{%- if model == 'test' %} +Model=533 +Device=/dev/serial/by-bus/usb0b1.1p1.0 Contrast=350 - -# Set the initial brightness [default: 1000; legal: 0 - 1000] Brightness=1000 - -# Set the initial off-brightness [default: 0; legal: 0 - 1000] -# This value is used when the display is normally -# switched off in case LCDd is inactive OffBrightness=50 - -# Reinitialize the LCD's BIOS on driver start. [default: no; legal: yes, no] Reboot=yes - -# Enable the USB flag if the device is connected to an USB port. For -# serial ports leave it disabled. [default: no; legal: yes, no] -#USB=yes - -# Very old 633 firmware versions do not support partial screen updates using -# 'Send Data to LCD' command (31). For those devices it may be necessary to -# enable this flag. [default: no; legal: yes, no] -#OldFirmware=yes - -# Override the LCD size known for the selected model. Usually setting this -# value should not be necessary. -#Size=20x4 - -# Override the default communication speed known for the selected model. -# Default value depends on model [legal: 19200, 115200] -#Speed=115200 - +USB=yes +{%- endif %} ## Curses driver ## @@ -546,7 +521,7 @@ Speed=19200 [hd44780] {%- if model == 'ezio' %} ConnectionType=ezio -Device=/dev/ttyS1 +Device=/dev/ttyUSB0 Keypad=yes Size=16x2 KeyMatrix_4_1=Enter diff --git a/interface-definitions/system-display.xml.in b/interface-definitions/system-display.xml.in index 0439288a0..fbd897996 100644 --- a/interface-definitions/system-display.xml.in +++ b/interface-definitions/system-display.xml.in @@ -1,7 +1,7 @@ -- cgit v1.2.3 From 6d2ffb9badcd15d431b8bbb6b28d2171d06e6dc4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 30 Jun 2020 20:38:17 +0200 Subject: ifconfig: T2653: set arp-cache-timeout default value of 30ms --- interface-definitions/include/interface-arp-cache-timeout.xml.i | 1 + 1 file changed, 1 insertion(+) (limited to 'interface-definitions') diff --git a/interface-definitions/include/interface-arp-cache-timeout.xml.i b/interface-definitions/include/interface-arp-cache-timeout.xml.i index 81d35f593..e65321158 100644 --- a/interface-definitions/include/interface-arp-cache-timeout.xml.i +++ b/interface-definitions/include/interface-arp-cache-timeout.xml.i @@ -10,4 +10,5 @@ ARP cache entry timeout must be between 1 and 86400 seconds + 30 -- cgit v1.2.3 From ebefa38b9fa946fde82a4c9b55122c037598143b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 1 Jul 2020 19:06:52 +0200 Subject: ethernet: ifconfig: T2653: move to get_config_dict() The current VyOS CLI parser code written in Python contains a ton of duplicates which I can also hold myself accountable for - or maybe mainly me - depends on the angle of judge. While providing a new update() method in vyos.ifconfig.interfaces() this is extended for ethernet based interfaces which also supports 802.1q, 802.1ad VLANs. This commit migrates the existing codebase for an ethernet based interfaces and implements the missing parts for VLANs. Adding or migrating other interfaces (e.g. bridge or bond) will become much easier as they must reuse the entire functionality - we now walk towards a single codepath. Thanks for all who made this combined effort possible! Signed-off-by: Christian Poessinger --- interface-definitions/interfaces-ethernet.xml.in | 2 + python/vyos/configdict.py | 29 +- python/vyos/configverify.py | 53 +++- python/vyos/ifconfig/ethernet.py | 101 ++++++- python/vyos/ifconfig/interface.py | 106 +++++++- python/vyos/ifconfig_vlan.py | 24 ++ src/conf_mode/interfaces-ethernet.py | 329 +++++------------------ 7 files changed, 371 insertions(+), 273 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in index 1e32a15f8..e8f3f09f1 100644 --- a/interface-definitions/interfaces-ethernet.xml.in +++ b/interface-definitions/interfaces-ethernet.xml.in @@ -56,6 +56,7 @@ duplex must be auto, half or full + auto #include @@ -265,6 +266,7 @@ Speed must be auto, 10, 100, 1000, 2500, 5000, 10000, 25000, 40000, 50000 or 100000 + auto #include #include diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 0dc7578d8..682caed8f 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -102,12 +102,35 @@ def dict_merge(source, destination): return tmp def list_diff(first, second): - """ - Diff two dictionaries and return only unique items - """ + """ Diff two dictionaries and return only unique items """ second = set(second) return [item for item in first if item not in second] +def T2665_default_dict_cleanup(dict): + """ Cleanup default keys for tag nodes https://phabricator.vyos.net/T2665. """ + # Cleanup + for vif in ['vif', 'vif_s']: + if vif in dict.keys(): + for key in ['ip', 'mtu']: + if key in dict[vif].keys(): + del dict[vif][key] + + # cleanup VIF-S defaults + if 'vif_c' in dict[vif].keys(): + for key in ['ip', 'mtu']: + if key in dict[vif]['vif_c'].keys(): + del dict[vif]['vif_c'][key] + # If there is no vif-c defined and we just cleaned the default + # keys - we can clean the entire vif-c dict as it's useless + if not dict[vif]['vif_c']: + del dict[vif]['vif_c'] + + # If there is no real vif/vif-s defined and we just cleaned the default + # keys - we can clean the entire vif dict as it's useless + if not dict[vif]: + del dict[vif] + + return dict def get_ethertype(ethertype_val): if ethertype_val == '0x88A8': diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 32129a048..36b10c956 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -41,14 +41,14 @@ def verify_vrf(config): def verify_address(config): """ - Common helper function used by interface implementations to - perform recurring validation of IP address assignmenr - when interface also is part of a bridge. + Common helper function used by interface implementations to perform + recurring validation of IP address assignment when interface is part + of a bridge or bond. """ if {'is_bridge_member', 'address'} <= set(config): raise ConfigError( - f'Cannot assign address to interface "{ifname}" as it is a ' - f'member of bridge "{is_bridge_member}"!'.format(**config)) + 'Cannot assign address to interface "{ifname}" as it is a ' + 'member of bridge "{is_bridge_member}"!'.format(**config)) def verify_bridge_delete(config): @@ -62,6 +62,15 @@ def verify_bridge_delete(config): 'Interface "{ifname}" cannot be deleted as it is a ' 'member of bridge "{is_bridge_member}"!'.format(**config)) +def verify_interface_exists(config): + """ + Common helper function used by interface implementations to perform + recurring validation if an interface actually exists. + """ + from netifaces import interfaces + if not config['ifname'] in interfaces(): + raise ConfigError(f'Interface "{ifname}" does not exist!' + .format(**config)) def verify_source_interface(config): """ @@ -76,3 +85,37 @@ def verify_source_interface(config): if not config['source_interface'] in interfaces(): raise ConfigError(f'Source interface {source_interface} does not ' f'exist'.format(**config)) + +def verify_dhcpv6(config): + """ + Common helper function used by interface implementations to perform + recurring validation of DHCPv6 options which are mutually exclusive. + """ + if {'parameters_only', 'temporary'} <= set(config.get('dhcpv6_options', {})): + raise ConfigError('DHCPv6 temporary and parameters-only options ' + 'are mutually exclusive!') + +def verify_vlan_config(config): + """ + Common helper function used by interface implementations to perform + recurring validation of interface VLANs + """ + # 802.1q VLANs + for vlan in config.get('vif', {}).keys(): + vlan = config['vif'][vlan] + verify_dhcpv6(vlan) + verify_address(vlan) + verify_vrf(vlan) + + # 802.1ad (Q-in-Q) VLANs + for vlan in config.get('vif_s', {}).keys(): + vlan = config['vif_s'][vlan] + verify_dhcpv6(vlan) + verify_address(vlan) + verify_vrf(vlan) + + for vlan in config.get('vif_s', {}).get('vif_c', {}).keys(): + vlan = config['vif_c'][vlan] + verify_dhcpv6(vlan) + verify_address(vlan) + verify_vrf(vlan) diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 5b18926c9..8a50a8699 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -15,13 +15,14 @@ import os import re +import jmespath +from vyos.configdict import get_ethertype from vyos.ifconfig.interface import Interface from vyos.ifconfig.vlan import VLAN from vyos.validate import assert_list from vyos.util import run - @Interface.register @VLAN.enable class EthernetIf(Interface): @@ -252,3 +253,101 @@ class EthernetIf(Interface): >>> i.set_udp_offload('on') """ return self.set_interface('ufo', state) + + + def update(self, config): + """ General helper function which works on a dictionary retrived by + get_config_dict(). It's main intention is to consolidate the scattered + interface setup code and provide a single point of entry when workin + on any interface. """ + + # now call the regular function from within our base class + super().update(config) + + # disable ethernet flow control (pause frames) + value = 'off' if 'disable_flow_control' in config.keys() else 'on' + self.set_flow_control(value) + + # GRO (generic receive offload) + tmp = jmespath.search('offload_options.generic_receive', config) + value = tmp if (tmp != None) else 'off' + self.set_gro(value) + + # GSO (generic segmentation offload) + tmp = jmespath.search('offload_options.generic_segmentation', config) + value = tmp if (tmp != None) else 'off' + self.set_gso(value) + + # scatter-gather option + tmp = jmespath.search('offload_options.scatter_gather', config) + value = tmp if (tmp != None) else 'off' + self.set_sg(value) + + # TSO (TCP segmentation offloading) + tmp = jmespath.search('offload_options.udp_fragmentation', config) + value = tmp if (tmp != None) else 'off' + self.set_tso(value) + + # UDP fragmentation offloading + tmp = jmespath.search('offload_options.udp_fragmentation', config) + value = tmp if (tmp != None) else 'off' + self.set_ufo(value) + + # Set physical interface speed and duplex + if {'speed', 'duplex'} <= set(config): + speed = config.get('speed') + duplex = config.get('duplex') + self.set_speed_duplex(speed, duplex) + + # Delete old IPv6 EUI64 addresses before changing MAC + + # Change interface MAC address - re-set to real hardware address (hw-id) + # if custom mac is removed. Skip if bond member. + if 'is_bond_member' not in config: + mac = config.get('hw_id') + if 'mac' in config: + mac = config.get('mac') + if mac: + self.set_mac(mac) + + # Add IPv6 EUI-based addresses + tmp = jmespath.search('ipv6.address.eui64', config) + if tmp: + # XXX: T2636 workaround: convert string to a list with one element + if isinstance(tmp, str): + tmp = [tmp] + for addr in tmp: + self.add_ipv6_eui64_address(addr) + + # re-add ourselves to any bridge we might have fallen out of + if 'is_bridge_member' in config: + bridge = config.get('is_bridge_member') + self.add_to_bridge(bridge) + + # remove no longer required 802.1ad (Q-in-Q VLANs) + for vif_s_id in config.get('vif_s_remove', {}): + self.del_vlan(vif_s_id) + + # create/update 802.1ad (Q-in-Q VLANs) + for vif_s_id, vif_s in config.get('vif_s', {}).items(): + tmp=get_ethertype(vif_s.get('ethertype', '0x88A8')) + s_vlan = self.add_vlan(vif_s_id, ethertype=tmp) + s_vlan.update(vif_s) + + # remove no longer required client VLAN (vif-c) + for vif_c_id in vif_s.get('vif_c_remove', {}): + s_vlan.del_vlan(vif_c_id) + + # create/update client VLAN (vif-c) interface + for vif_c_id, vif_c in vif_s.get('vif_c', {}).items(): + c_vlan = s_vlan.add_vlan(vif_c_id) + c_vlan.update(vif_c) + + # remove no longer required 802.1q VLAN interfaces + for vif_id in config.get('vif_remove', {}): + self.del_vlan(vif_id) + + # create/update 802.1q VLAN interfaces + for vif_id, vif in config.get('vif', {}).items(): + vlan = self.add_vlan(vif_id) + vlan.update(vif) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 8d7b247fc..689faa22b 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -16,6 +16,7 @@ import os import re import json +import jmespath from copy import deepcopy from ipaddress import IPv4Network @@ -322,11 +323,11 @@ class Interface(Control): self.set_admin_state('down') self.set_interface('mac', mac) - + # Turn an interface to the 'up' state if it was changed to 'down' by this fucntion if prev_state == 'up': self.set_admin_state('up') - + def set_vrf(self, vrf=''): """ Add/Remove interface from given VRF instance. @@ -773,14 +774,17 @@ class Interface(Control): on any interface. """ # Update interface description - self.set_alias(config.get('description', None)) + self.set_alias(config.get('description', '')) + + # Ignore link state changes + value = '2' if 'disable_link_detect' in config else '1' + self.set_link_detect(value) # Configure assigned interface IP addresses. No longer # configured addresses will be removed first new_addr = config.get('address', []) - # XXX workaround for T2636, convert IP address string to a list - # with one element + # XXX: T2636 workaround: convert string to a list with one element if isinstance(new_addr, str): new_addr = [new_addr] @@ -800,6 +804,96 @@ class Interface(Control): # Bind interface instance into VRF self.set_vrf(config.get('vrf', '')) + # DHCP options + if 'dhcp_options' in config: + dhcp_options = config.get('dhcp_options') + if 'client_id' in dhcp_options: + self.dhcp.v4.options['client_id'] = dhcp_options.get('client_id') + + if 'host_name' in dhcp_options: + self.dhcp.v4.options['hostname'] = dhcp_options.get('host_name') + + if 'vendor_class_id' in dhcp_options: + self.dhcp.v4.options['vendor_class_id'] = dhcp_options.get('vendor_class_id') + + # DHCPv6 options + if 'dhcpv6_options' in config: + dhcpv6_options = config.get('dhcpv6_options') + if 'parameters_only' in dhcpv6_options: + self.dhcp.v6.options['dhcpv6_prm_only'] = True + + if 'temporary' in dhcpv6_options: + self.dhcp.v6.options['dhcpv6_temporary'] = True + + if 'prefix_delegation' in dhcpv6_options: + prefix_delegation = dhcpv6_options.get('prefix_delegation') + if 'length' in prefix_delegation: + self.dhcp.v6.options['dhcpv6_pd_length'] = prefix_delegation.get('length') + + if 'interface' in prefix_delegation: + self.dhcp.v6.options['dhcpv6_pd_interfaces'] = prefix_delegation.get('interface') + + # Configure ARP cache timeout in milliseconds - has default value + tmp = jmespath.search('ip.arp_cache_timeout', config) + value = tmp if (tmp != None) else '30' + self.set_arp_cache_tmo(value) + + # Configure ARP filter configuration + tmp = jmespath.search('ip.disable_arp_filter', config) + value = '0' if (tmp != None) else '1' + self.set_arp_filter(value) + + # Configure ARP accept + tmp = jmespath.search('ip.enable_arp_accept', config) + value = '1' if (tmp != None) else '0' + self.set_arp_accept(value) + + # Configure ARP announce + tmp = jmespath.search('ip.enable_arp_announce', config) + value = '1' if (tmp != None) else '0' + self.set_arp_announce(value) + + # Configure ARP ignore + tmp = jmespath.search('ip.enable_arp_ignore', config) + value = '1' if (tmp != None) else '0' + self.set_arp_ignore(value) + + # Enable proxy-arp on this interface + tmp = jmespath.search('ip.enable_proxy_arp', config) + value = '1' if (tmp != None) else '0' + self.set_proxy_arp(value) + + # Enable private VLAN proxy ARP on this interface + tmp = jmespath.search('ip.proxy_arp_pvlan', config) + value = '1' if (tmp != None) else '0' + self.set_proxy_arp_pvlan(value) + + # IPv6 forwarding + tmp = jmespath.search('ipv6.disable_forwarding', config) + value = '0' if (tmp != None) else '1' + self.set_ipv6_forwarding(value) + + # IPv6 router advertisements + tmp = jmespath.search('ipv6.address.autoconf', config) + value = '2' if (tmp != None) else '1' + if 'dhcpv6' in new_addr: + value = '2' + self.set_ipv6_accept_ra(value) + + # IPv6 address autoconfiguration + tmp = jmespath.search('ipv6.address.autoconf', config) + value = '1' if (tmp != None) else '0' + self.set_ipv6_autoconf(value) + + # IPv6 Duplicate Address Detection (DAD) tries + tmp = jmespath.search('ipv6.dup_addr_detect_transmits', config) + value = tmp if (tmp != None) else '1' + self.set_ipv6_dad_messages(value) + + # MTU - Maximum Transfer Unit + if 'mtu' in config: + self.set_mtu(config.get('mtu')) + # Interface administrative state - state = 'down' if 'disable' in config.keys() else 'up' + state = 'down' if 'disable' in config else 'up' self.set_admin_state(state) diff --git a/python/vyos/ifconfig_vlan.py b/python/vyos/ifconfig_vlan.py index 442cb0db8..ecb6796fa 100644 --- a/python/vyos/ifconfig_vlan.py +++ b/python/vyos/ifconfig_vlan.py @@ -16,6 +16,30 @@ from netifaces import interfaces from vyos import ConfigError +def get_removed_vlans(conf, dict): + """ + Common function to parse a dictionary retrieved via get_config_dict() and + determine any added/removed VLAN interfaces - be it 802.1q or Q-in-Q. + """ + from vyos.configdiff import get_config_diff, Diff + + # Check vif, vif-s/vif-c VLAN interfaces for removal + D = get_config_diff(conf, key_mangling=('-', '_')) + D.set_level(conf.get_level()) + # get_child_nodes() will return dict_keys(), mangle this into a list with PEP448 + keys = D.get_child_nodes_diff(['vif'], expand_nodes=Diff.DELETE)['delete'].keys() + dict['vif_remove'] = [*keys] + + # get_child_nodes() will return dict_keys(), mangle this into a list with PEP448 + keys = D.get_child_nodes_diff(['vif-s'], expand_nodes=Diff.DELETE)['delete'].keys() + dict['vif_s_remove'] = [*keys] + + for vif in dict.get('vif_s', {}).keys(): + keys = D.get_child_nodes_diff(['vif-s', vif, 'vif-c'], expand_nodes=Diff.DELETE)['delete'].keys() + dict['vif_s'][vif]['vif_c_remove'] = [*keys] + + return dict + def apply_all_vlans(intf, intfconfig): """ Function applies all VLANs to the passed interface. diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 8b895c4d2..60aafae32 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -17,295 +17,108 @@ import os from sys import exit -from copy import deepcopy -from netifaces import interfaces +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configdict import T2665_default_dict_cleanup +from vyos.configverify import verify_interface_exists +from vyos.configverify import verify_dhcpv6 +from vyos.configverify import verify_address +from vyos.configverify import verify_vrf +from vyos.configverify import verify_vlan_config from vyos.ifconfig import EthernetIf -from vyos.ifconfig_vlan import apply_all_vlans, verify_vlan_config -from vyos.configdict import list_diff, intf_to_dict, add_to_dict, interface_default_data +from vyos.ifconfig_vlan import get_removed_vlans from vyos.validate import is_member -from vyos.config import Config +from vyos.xml import defaults from vyos import ConfigError - from vyos import airbag airbag.enable() -default_config_data = { - **interface_default_data, - 'deleted': False, - 'duplex': 'auto', - 'flow_control': 'on', - 'hw_id': '', - 'ip_arp_cache_tmo': 30, - 'ip_proxy_arp_pvlan': 0, - 'is_bond_member': False, - 'intf': '', - 'offload_gro': 'off', - 'offload_gso': 'off', - 'offload_sg': 'off', - 'offload_tso': 'off', - 'offload_ufo': 'off', - 'speed': 'auto', - 'vif_s': {}, - 'vif_s_remove': [], - 'vif': {}, - 'vif_remove': [], - 'vrf': '' -} - def get_config(): + """ Retrive CLI config as dictionary. Dictionary can never be empty, + as at least the interface name will be added or a deleted flag """ + conf = Config() + # determine tagNode instance if 'VYOS_TAGNODE_VALUE' not in os.environ: raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') - ifname = os.environ['VYOS_TAGNODE_VALUE'] - conf = Config() - - # check if ethernet interface has been removed - cfg_base = ['interfaces', 'ethernet', ifname] - if not conf.exists(cfg_base): - eth = deepcopy(default_config_data) - eth['intf'] = ifname - eth['deleted'] = True - # we can not bail out early as ethernet interface can not be removed - # Kernel will complain with: RTNETLINK answers: Operation not supported. - # Thus we need to remove individual settings - return eth - - # set new configuration level - conf.set_level(cfg_base) - - eth, disabled = intf_to_dict(conf, default_config_data) - - # disable ethernet flow control (pause frames) - if conf.exists('disable-flow-control'): - eth['flow_control'] = 'off' - - # retrieve real hardware address - if conf.exists('hw-id'): - eth['hw_id'] = conf.return_value('hw-id') - - # interface duplex - if conf.exists('duplex'): - eth['duplex'] = conf.return_value('duplex') + # retrieve interface default values + base = ['interfaces', 'ethernet'] + default_values = defaults(base) - # ARP cache entry timeout in seconds - if conf.exists('ip arp-cache-timeout'): - eth['ip_arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout')) - - # Enable private VLAN proxy ARP on this interface - if conf.exists('ip proxy-arp-pvlan'): - eth['ip_proxy_arp_pvlan'] = 1 - - # check if we are a member of any bond - eth['is_bond_member'] = is_member(conf, eth['intf'], 'bonding') - - # GRO (generic receive offload) - if conf.exists('offload-options generic-receive'): - eth['offload_gro'] = conf.return_value('offload-options generic-receive') - - # GSO (generic segmentation offload) - if conf.exists('offload-options generic-segmentation'): - eth['offload_gso'] = conf.return_value('offload-options generic-segmentation') - - # scatter-gather option - if conf.exists('offload-options scatter-gather'): - eth['offload_sg'] = conf.return_value('offload-options scatter-gather') - - # TSO (TCP segmentation offloading) - if conf.exists('offload-options tcp-segmentation'): - eth['offload_tso'] = conf.return_value('offload-options tcp-segmentation') - - # UDP fragmentation offloading - if conf.exists('offload-options udp-fragmentation'): - eth['offload_ufo'] = conf.return_value('offload-options udp-fragmentation') - - # interface speed - if conf.exists('speed'): - eth['speed'] = conf.return_value('speed') - - # remove default IPv6 link-local address if member of a bond - if eth['is_bond_member'] and 'fe80::/64' in eth['ipv6_eui64_prefix']: - eth['ipv6_eui64_prefix'].remove('fe80::/64') - eth['ipv6_eui64_prefix_remove'].append('fe80::/64') - - add_to_dict(conf, disabled, eth, 'vif', 'vif') - add_to_dict(conf, disabled, eth, 'vif-s', 'vif_s') - - return eth - - -def verify(eth): - if eth['deleted']: + ifname = os.environ['VYOS_TAGNODE_VALUE'] + base = base + [ifname] + # setup config level which is extracted in get_removed_vlans() + conf.set_level(base) + ethernet = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) + + # Check if interface has been removed + if ethernet == {}: + ethernet.update({'deleted' : ''}) + + # We have gathered the dict representation of the CLI, but there are + # default options which we need to update into the dictionary + # retrived. + ethernet = dict_merge(default_values, ethernet) + + # Add interface instance name into dictionary + ethernet.update({'ifname': ifname}) + + # Check if we are a member of a bridge device + bridge = is_member(conf, ifname, 'bridge') + if bridge: + tmp = {'is_bridge_member' : bridge} + ethernet.update(tmp) + + # Check if we are a member of a bond device + bond = is_member(conf, ifname, 'bonding') + if bond: + tmp = {'is_bond_member' : bond} + ethernet.update(tmp) + + ethernet = T2665_default_dict_cleanup( ethernet ) + # Check vif, vif-s/vif-c VLAN interfaces for removal + ethernet = get_removed_vlans( conf, ethernet ) + return ethernet + +def verify(ethernet): + if 'deleted' in ethernet.keys(): return None - if eth['intf'] not in interfaces(): - raise ConfigError(f"Interface ethernet {eth['intf']} does not exist") + verify_interface_exists(ethernet) - if eth['speed'] == 'auto': - if eth['duplex'] != 'auto': + if ethernet.get('speed', None) == 'auto': + if ethernet.get('duplex', None) != 'auto': raise ConfigError('If speed is hardcoded, duplex must be hardcoded, too') - if eth['duplex'] == 'auto': - if eth['speed'] != 'auto': + if ethernet.get('duplex', None) == 'auto': + if ethernet.get('speed', None) != 'auto': raise ConfigError('If duplex is hardcoded, speed must be hardcoded, too') - if eth['dhcpv6_prm_only'] and eth['dhcpv6_temporary']: - raise ConfigError('DHCPv6 temporary and parameters-only options are mutually exclusive!') + verify_dhcpv6(ethernet) + verify_address(ethernet) + verify_vrf(ethernet) - memberof = eth['is_bridge_member'] if eth['is_bridge_member'] else eth['is_bond_member'] - - if ( memberof - and ( eth['address'] - or eth['ipv6_eui64_prefix'] - or eth['ipv6_autoconf'] ) ): - raise ConfigError(( - f'Cannot assign address to interface "{eth["intf"]}" ' - f'as it is a member of "{memberof}"!')) - - if eth['vrf']: - if eth['vrf'] not in interfaces(): - raise ConfigError(f'VRF "{eth["vrf"]}" does not exist') - - if memberof: - raise ConfigError(( - f'Interface "{eth["intf"]}" cannot be member of VRF "{eth["vrf"]}" ' - f'and "{memberof}" at the same time!')) - - if eth['mac'] and eth['is_bond_member']: - print('WARNING: "mac {0}" command will be ignored because {1} is a part of {2}'\ - .format(eth['mac'], eth['intf'], eth['is_bond_member'])) + if {'is_bond_member', 'mac'} <= set(ethernet): + print(f'WARNING: changing mac address "{mac}" will be ignored as "{ifname}" ' + f'is a member of bond "{is_bond_member}"'.format(**ethernet)) # use common function to verify VLAN configuration - verify_vlan_config(eth) + verify_vlan_config(ethernet) return None -def generate(eth): +def generate(ethernet): return None -def apply(eth): - e = EthernetIf(eth['intf']) - if eth['deleted']: - # apply all vlans to interface (they need removing too) - apply_all_vlans(e, eth) - +def apply(ethernet): + e = EthernetIf(ethernet['ifname']) + if 'deleted' in ethernet.keys(): # delete interface e.remove() else: - # update interface description used e.g. within SNMP - e.set_alias(eth['description']) - - if eth['dhcp_client_id']: - e.dhcp.v4.options['client_id'] = eth['dhcp_client_id'] - - if eth['dhcp_hostname']: - e.dhcp.v4.options['hostname'] = eth['dhcp_hostname'] - - if eth['dhcp_vendor_class_id']: - e.dhcp.v4.options['vendor_class_id'] = eth['dhcp_vendor_class_id'] - - if eth['dhcpv6_prm_only']: - e.dhcp.v6.options['dhcpv6_prm_only'] = True - - if eth['dhcpv6_temporary']: - e.dhcp.v6.options['dhcpv6_temporary'] = True - - if eth['dhcpv6_pd_length']: - e.dhcp.v6.options['dhcpv6_pd_length'] = eth['dhcpv6_pd_length'] - - if eth['dhcpv6_pd_interfaces']: - e.dhcp.v6.options['dhcpv6_pd_interfaces'] = eth['dhcpv6_pd_interfaces'] - - # ignore link state changes - e.set_link_detect(eth['disable_link_detect']) - # disable ethernet flow control (pause frames) - e.set_flow_control(eth['flow_control']) - # configure ARP cache timeout in milliseconds - e.set_arp_cache_tmo(eth['ip_arp_cache_tmo']) - # configure ARP filter configuration - e.set_arp_filter(eth['ip_disable_arp_filter']) - # configure ARP accept - e.set_arp_accept(eth['ip_enable_arp_accept']) - # configure ARP announce - e.set_arp_announce(eth['ip_enable_arp_announce']) - # configure ARP ignore - e.set_arp_ignore(eth['ip_enable_arp_ignore']) - # Enable proxy-arp on this interface - e.set_proxy_arp(eth['ip_proxy_arp']) - # Enable private VLAN proxy ARP on this interface - e.set_proxy_arp_pvlan(eth['ip_proxy_arp_pvlan']) - # IPv6 accept RA - e.set_ipv6_accept_ra(eth['ipv6_accept_ra']) - # IPv6 address autoconfiguration - e.set_ipv6_autoconf(eth['ipv6_autoconf']) - # IPv6 forwarding - e.set_ipv6_forwarding(eth['ipv6_forwarding']) - # IPv6 Duplicate Address Detection (DAD) tries - e.set_ipv6_dad_messages(eth['ipv6_dup_addr_detect']) - - # Delete old IPv6 EUI64 addresses before changing MAC - for addr in eth['ipv6_eui64_prefix_remove']: - e.del_ipv6_eui64_address(addr) - - # Change interface MAC address - re-set to real hardware address (hw-id) - # if custom mac is removed. Skip if bond member. - if not eth['is_bond_member']: - if eth['mac']: - e.set_mac(eth['mac']) - elif eth['hw_id']: - e.set_mac(eth['hw_id']) - - # Add IPv6 EUI-based addresses - for addr in eth['ipv6_eui64_prefix']: - e.add_ipv6_eui64_address(addr) - - # Maximum Transmission Unit (MTU) - e.set_mtu(eth['mtu']) - - # GRO (generic receive offload) - e.set_gro(eth['offload_gro']) - - # GSO (generic segmentation offload) - e.set_gso(eth['offload_gso']) - - # scatter-gather option - e.set_sg(eth['offload_sg']) - - # TSO (TCP segmentation offloading) - e.set_tso(eth['offload_tso']) - - # UDP fragmentation offloading - e.set_ufo(eth['offload_ufo']) - - # Set physical interface speed and duplex - e.set_speed_duplex(eth['speed'], eth['duplex']) - - # Enable/Disable interface - if eth['disable']: - e.set_admin_state('down') - else: - e.set_admin_state('up') - - # Configure interface address(es) - # - not longer required addresses get removed first - # - newly addresses will be added second - for addr in eth['address_remove']: - e.del_addr(addr) - for addr in eth['address']: - e.add_addr(addr) - - # assign/remove VRF (ONLY when not a member of a bridge or bond, - # otherwise 'nomaster' removes it from it) - if not ( eth['is_bridge_member'] or eth['is_bond_member'] ): - e.set_vrf(eth['vrf']) - - # re-add ourselves to any bridge we might have fallen out of - if eth['is_bridge_member']: - e.add_to_bridge(eth['is_bridge_member']) - - # apply all vlans to interface - apply_all_vlans(e, eth) + e.update(ethernet) if __name__ == '__main__': -- cgit v1.2.3 From a25d7095e009469d8ef60b63deddd94d30921723 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Jul 2020 20:45:29 +0200 Subject: bridge: ifconfig: T2653: move to get_config_dict() The current VyOS CLI parser code written in Python contains a ton of duplicates which I can also hold myself accountable for - or maybe mainly me - depends on the angle of judge. While providing a new update() method in vyos.ifconfig.interfaces() this is extended for bridge interfaces in the derived bridge class. Signed-off-by: Christian Poessinger --- interface-definitions/interfaces-bridge.xml.in | 7 + python/vyos/configdict.py | 97 ++++++ python/vyos/ifconfig/bridge.py | 68 +++- python/vyos/ifconfig/ethernet.py | 20 -- python/vyos/ifconfig/interface.py | 25 ++ python/vyos/ifconfig_vlan.py | 9 +- python/vyos/util.py | 2 +- src/conf_mode/interfaces-bridge.py | 413 +++++-------------------- src/conf_mode/interfaces-ethernet.py | 54 +--- 9 files changed, 296 insertions(+), 399 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in index 6b610e623..92356d696 100644 --- a/interface-definitions/interfaces-bridge.xml.in +++ b/interface-definitions/interfaces-bridge.xml.in @@ -32,6 +32,7 @@ + 300 #include #include @@ -51,6 +52,7 @@ Forwarding delay must be between 0 and 200 seconds + 14 @@ -64,6 +66,7 @@ Bridge Hello interval must be between 1 and 10 seconds + 2 @@ -107,6 +110,7 @@ Bridge max aging value must be between 1 and 40 seconds + 20 @@ -133,6 +137,7 @@ Path cost value must be between 1 and 65535 + 100 @@ -146,6 +151,7 @@ Port priority value must be between 0 and 63 + 32 @@ -163,6 +169,7 @@ Bridge priority must be between 0 and 65535 (multiples of 4096) + 32768 diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 682caed8f..4fca426cd 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -17,6 +17,7 @@ A library for retrieving value dicts from VyOS configs in a declarative fashion. """ +import jmespath from enum import Enum from copy import deepcopy @@ -132,6 +133,102 @@ def T2665_default_dict_cleanup(dict): return dict +def leaf_node_changed(conf, key): + """ + Check if a leaf node was altered. If it has been altered - values has been + changed, or it was added/removed, we will return the old value. If nothing + has been changed, None is returned + """ + from vyos.configdiff import get_config_diff + + D = get_config_diff(conf, key_mangling=('-', '_')) + D.set_level(conf.get_level()) + (new, old) = D.get_value_diff(key) + if new != old: + if isinstance(old, str): + return old + elif isinstance(old, list): + if isinstance(new, str): + new = [new] + elif isinstance(new, type(None)): + new = [] + return list_diff(old, new) + + return None + +def get_interface_dict(config, base, ifname): + """ + Common utility function to retrieve and mandgle the interfaces available + in CLI configuration. All interfaces have a common base ground where the + value retrival is identical - so it can and should be reused + + Will return a dictionary with the necessary interface configuration + """ + from vyos.xml import defaults + from vyos.ifconfig_vlan import get_removed_vlans + + # retrieve interface default values + default_values = defaults(base) + + # setup config level which is extracted in get_removed_vlans() + config.set_level(base + [ifname]) + dict = config.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) + + # Check if interface has been removed + if dict == {}: + dict.update({'deleted' : ''}) + + # Add interface instance name into dictionary + dict.update({'ifname': ifname}) + + # We have gathered the dict representation of the CLI, but there are + # default options which we need to update into the dictionary + # retrived. + dict = dict_merge(default_values, dict) + + # Check if we are a member of a bridge device + bridge = is_member(config, ifname, 'bridge') + if bridge: + dict.update({'is_bridge_member' : bridge}) + + # Check if we are a member of a bond device + bond = is_member(config, ifname, 'bonding') + if bond: + dict.update({'is_bond_member' : bond}) + + mac = leaf_node_changed(config, ['mac']) + if mac: + dict.update({'mac_old' : mac}) + + eui64 = leaf_node_changed(config, ['ipv6', 'address', 'eui64']) + if eui64: + # XXX: T2636 workaround: convert string to a list with one element + if isinstance(eui64, str): + eui64 = [eui64] + tmp = jmespath.search('ipv6.address', dict) + if not tmp: + dict.update({'ipv6': {'address': {'eui64_old': eui64}}}) + else: + dict['ipv6']['address'].update({'eui64_old': eui64}) + + # remove wrongly inserted values + dict = T2665_default_dict_cleanup(dict) + + # The values are identical for vif, vif-s and vif-c as the all include the same + # XML definitions which hold the defaults + default_vif_values = defaults(base + ['vif']) + for vif, vif_config in dict.get('vif', {}).items(): + vif_config.update(default_vif_values) + for vif_s, vif_s_config in dict.get('vif_s', {}).items(): + vif_s_config.update(default_vif_values) + for vif_c, vif_c_config in vif_s_config.get('vif_c', {}).items(): + vif_c_config.update(default_vif_values) + + # Check vif, vif-s/vif-c VLAN interfaces for removal + dict = get_removed_vlans(config, dict) + + return dict + def get_ethertype(ethertype_val): if ethertype_val == '0x88A8': return '802.1ad' diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py index 44b92c1db..af950b35d 100644 --- a/python/vyos/ifconfig/bridge.py +++ b/python/vyos/ifconfig/bridge.py @@ -13,12 +13,13 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +import jmespath from vyos.ifconfig.interface import Interface - +from vyos.ifconfig.stp import STP from vyos.validate import assert_boolean from vyos.validate import assert_positive - +from vyos.util import cmd @Interface.register class BridgeIf(Interface): @@ -187,3 +188,66 @@ class BridgeIf(Interface): >>> BridgeIf('br0').del_port('eth1') """ return self.set_interface('del_port', interface) + + def update(self, config): + """ General helper function which works on a dictionary retrived by + get_config_dict(). It's main intention is to consolidate the scattered + interface setup code and provide a single point of entry when workin + on any interface. """ + + # now call the regular function from within our base class + super().update(config) + + # Set ageing time + value = config.get('aging') + self.set_ageing_time(value) + + # set bridge forward delay + value = config.get('forwarding_delay') + self.set_forward_delay(value) + + # set hello time + value = config.get('hello_time') + self.set_hello_time(value) + + # set max message age + value = config.get('max_age') + self.set_max_age(value) + + # set bridge priority + value = config.get('priority') + self.set_priority(value) + + # enable/disable spanning tree + value = '1' if 'stp' in config else '0' + self.set_stp(value) + + # enable or disable IGMP querier + tmp = jmespath.search('igmp.querier', config) + value = '1' if (tmp != None) else '0' + self.set_multicast_querier(value) + + # remove interface from bridge + tmp = jmespath.search('member.interface_remove', config) + if tmp: + for member in tmp: + self.del_port(member) + + STPBridgeIf = STP.enable(BridgeIf) + tmp = jmespath.search('member.interface', config) + if tmp: + for interface, interface_config in tmp.items(): + # if we've come here we already verified the interface doesn't + # have addresses configured so just flush any remaining ones + cmd(f'ip addr flush dev "{interface}"') + # enslave interface port to bridge + self.add_port(interface) + + tmp = STPBridgeIf(interface) + # set bridge port path cost + value = interface_config.get('cost') + tmp.set_path_cost(value) + + # set bridge port path priority + value = interface_config.get('priority') + tmp.set_path_priority(value) diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 8a50a8699..1725116e2 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -299,26 +299,6 @@ class EthernetIf(Interface): duplex = config.get('duplex') self.set_speed_duplex(speed, duplex) - # Delete old IPv6 EUI64 addresses before changing MAC - - # Change interface MAC address - re-set to real hardware address (hw-id) - # if custom mac is removed. Skip if bond member. - if 'is_bond_member' not in config: - mac = config.get('hw_id') - if 'mac' in config: - mac = config.get('mac') - if mac: - self.set_mac(mac) - - # Add IPv6 EUI-based addresses - tmp = jmespath.search('ipv6.address.eui64', config) - if tmp: - # XXX: T2636 workaround: convert string to a list with one element - if isinstance(tmp, str): - tmp = [tmp] - for addr in tmp: - self.add_ipv6_eui64_address(addr) - # re-add ourselves to any bridge we might have fallen out of if 'is_bridge_member' in config: bridge = config.get('is_bridge_member') diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index be3617f7d..ea770af23 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -922,6 +922,31 @@ class Interface(Control): if 'mtu' in config: self.set_mtu(config.get('mtu')) + # Delete old IPv6 EUI64 addresses before changing MAC + tmp = jmespath.search('ipv6.address.eui64_old', config) + if tmp: + for addr in tmp: + self.del_ipv6_eui64_address(addr) + + # Change interface MAC address - re-set to real hardware address (hw-id) + # if custom mac is removed. Skip if bond member. + if 'is_bond_member' not in config: + mac = config.get('hw_id') + if 'mac' in config: + mac = config.get('mac') + if mac: + self.set_mac(mac) + + # Add IPv6 EUI-based addresses + tmp = jmespath.search('ipv6.address.eui64', config) + if tmp: + # XXX: T2636 workaround: convert string to a list with one element + if isinstance(tmp, str): + tmp = [tmp] + for addr in tmp: + self.add_ipv6_eui64_address(addr) + + # Interface administrative state state = 'down' if 'disable' in config else 'up' self.set_admin_state(state) diff --git a/python/vyos/ifconfig_vlan.py b/python/vyos/ifconfig_vlan.py index ecb6796fa..0e4ecda53 100644 --- a/python/vyos/ifconfig_vlan.py +++ b/python/vyos/ifconfig_vlan.py @@ -28,15 +28,18 @@ def get_removed_vlans(conf, dict): D.set_level(conf.get_level()) # get_child_nodes() will return dict_keys(), mangle this into a list with PEP448 keys = D.get_child_nodes_diff(['vif'], expand_nodes=Diff.DELETE)['delete'].keys() - dict['vif_remove'] = [*keys] + if keys: + dict.update({'vif_remove': [*keys]}) # get_child_nodes() will return dict_keys(), mangle this into a list with PEP448 keys = D.get_child_nodes_diff(['vif-s'], expand_nodes=Diff.DELETE)['delete'].keys() - dict['vif_s_remove'] = [*keys] + if keys: + dict.update({'vif_s_remove': [*keys]}) for vif in dict.get('vif_s', {}).keys(): keys = D.get_child_nodes_diff(['vif-s', vif, 'vif-c'], expand_nodes=Diff.DELETE)['delete'].keys() - dict['vif_s'][vif]['vif_c_remove'] = [*keys] + if keys: + dict.update({'vif_s': { vif : {'vif_c_remove': [*keys]}}}) return dict diff --git a/python/vyos/util.py b/python/vyos/util.py index 7234be6cb..7078762df 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -242,7 +242,7 @@ def chown(path, user, group): if not os.path.exists(path): return False - + uid = getpwnam(user).pw_uid gid = getgrnam(group).gr_gid os.chown(path, uid, gid) diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index 1e4fa5816..7998a251a 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -16,251 +16,116 @@ import os -from copy import deepcopy from sys import exit from netifaces import interfaces -from vyos.ifconfig import BridgeIf, Section -from vyos.ifconfig.stp import STP -from vyos.configdict import list_diff, interface_default_data -from vyos.validate import is_member, has_address_configured from vyos.config import Config -from vyos.util import cmd, get_bridge_member_config +from vyos.configdict import get_interface_dict +from vyos.configdiff import get_config_diff, Diff +from vyos.configverify import verify_dhcpv6 +from vyos.configverify import verify_vrf +from vyos.ifconfig import BridgeIf +from vyos.validate import is_member, has_address_configured +from vyos.xml import defaults + +from vyos.util import cmd from vyos import ConfigError from vyos import airbag airbag.enable() -default_config_data = { - **interface_default_data, - 'aging': 300, - 'arp_cache_tmo': 30, - 'deleted': False, - 'forwarding_delay': 14, - 'hello_time': 2, - 'igmp_querier': 0, - 'intf': '', - 'max_age': 20, - 'member': [], - 'member_remove': [], - 'priority': 32768, - 'stp': 0 -} +def get_removed_members(conf): + D = get_config_diff(conf, key_mangling=('-', '_')) + D.set_level(conf.get_level()) + # get_child_nodes() will return dict_keys(), mangle this into a list with PEP448 + keys = D.get_child_nodes_diff(['member', 'interface'], expand_nodes=Diff.DELETE)['delete'].keys() + return list(keys) def get_config(): - bridge = deepcopy(default_config_data) + """ + Retrive CLI config as dictionary. Dictionary can never be empty, as at least the + interface name will be added or a deleted flag + """ conf = Config() + base = ['interfaces', 'bridge'] # determine tagNode instance if 'VYOS_TAGNODE_VALUE' not in os.environ: raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') - bridge['intf'] = os.environ['VYOS_TAGNODE_VALUE'] - - # Check if bridge has been removed - if not conf.exists('interfaces bridge ' + bridge['intf']): - bridge['deleted'] = True - return bridge - - # set new configuration level - conf.set_level('interfaces bridge ' + bridge['intf']) - - # retrieve configured interface addresses - if conf.exists('address'): - bridge['address'] = conf.return_values('address') - - # Determine interface addresses (currently effective) - to determine which - # address is no longer valid and needs to be removed - eff_addr = conf.return_effective_values('address') - bridge['address_remove'] = list_diff(eff_addr, bridge['address']) - - # retrieve aging - how long addresses are retained - if conf.exists('aging'): - bridge['aging'] = int(conf.return_value('aging')) - - # retrieve interface description - if conf.exists('description'): - bridge['description'] = conf.return_value('description') - - # get DHCP client identifier - if conf.exists('dhcp-options client-id'): - bridge['dhcp_client_id'] = conf.return_value('dhcp-options client-id') - - # DHCP client host name (overrides the system host name) - if conf.exists('dhcp-options host-name'): - bridge['dhcp_hostname'] = conf.return_value('dhcp-options host-name') - - # DHCP client vendor identifier - if conf.exists('dhcp-options vendor-class-id'): - bridge['dhcp_vendor_class_id'] = conf.return_value('dhcp-options vendor-class-id') - - # DHCPv6 only acquire config parameters, no address - if conf.exists('dhcpv6-options parameters-only'): - bridge['dhcpv6_prm_only'] = True - - # DHCPv6 temporary IPv6 address - if conf.exists('dhcpv6-options temporary'): - bridge['dhcpv6_temporary'] = True - - # Disable this bridge interface - if conf.exists('disable'): - bridge['disable'] = True - - # Ignore link state changes - if conf.exists('disable-link-detect'): - bridge['disable_link_detect'] = 2 - - # Forwarding delay - if conf.exists('forwarding-delay'): - bridge['forwarding_delay'] = int(conf.return_value('forwarding-delay')) - - # Hello packet advertisment interval - if conf.exists('hello-time'): - bridge['hello_time'] = int(conf.return_value('hello-time')) - - # Enable Internet Group Management Protocol (IGMP) querier - if conf.exists('igmp querier'): - bridge['igmp_querier'] = 1 - - # ARP cache entry timeout in seconds - if conf.exists('ip arp-cache-timeout'): - bridge['arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout')) - - # ARP filter configuration - if conf.exists('ip disable-arp-filter'): - bridge['ip_disable_arp_filter'] = 0 - - # ARP enable accept - if conf.exists('ip enable-arp-accept'): - bridge['ip_enable_arp_accept'] = 1 - - # ARP enable announce - if conf.exists('ip enable-arp-announce'): - bridge['ip_enable_arp_announce'] = 1 - - # ARP enable ignore - if conf.exists('ip enable-arp-ignore'): - bridge['ip_enable_arp_ignore'] = 1 - - # Enable acquisition of IPv6 address using stateless autoconfig (SLAAC) - if conf.exists('ipv6 address autoconf'): - bridge['ipv6_autoconf'] = 1 - - # Get prefixes for IPv6 addressing based on MAC address (EUI-64) - if conf.exists('ipv6 address eui64'): - bridge['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64') - - # Determine currently effective EUI64 addresses - to determine which - # address is no longer valid and needs to be removed - eff_addr = conf.return_effective_values('ipv6 address eui64') - bridge['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, bridge['ipv6_eui64_prefix']) - - # Remove the default link-local address if set. - if conf.exists('ipv6 address no-default-link-local'): - bridge['ipv6_eui64_prefix_remove'].append('fe80::/64') - else: - # add the link-local by default to make IPv6 work - bridge['ipv6_eui64_prefix'].append('fe80::/64') - - # Disable IPv6 forwarding on this interface - if conf.exists('ipv6 disable-forwarding'): - bridge['ipv6_forwarding'] = 0 - - # IPv6 Duplicate Address Detection (DAD) tries - if conf.exists('ipv6 dup-addr-detect-transmits'): - bridge['ipv6_dup_addr_detect'] = int(conf.return_value('ipv6 dup-addr-detect-transmits')) - - # Media Access Control (MAC) address - if conf.exists('mac'): - bridge['mac'] = conf.return_value('mac') - - # Find out if MAC has changed - if so, we need to delete all IPv6 EUI64 addresses - # before re-adding them - if ( bridge['mac'] and bridge['intf'] in Section.interfaces(section='bridge') - and bridge['mac'] != BridgeIf(bridge['intf'], create=False).get_mac() ): - bridge['ipv6_eui64_prefix_remove'] += bridge['ipv6_eui64_prefix'] - - # to make IPv6 SLAAC and DHCPv6 work with forwarding=1, - # accept_ra must be 2 - if bridge['ipv6_autoconf'] or 'dhcpv6' in bridge['address']: - bridge['ipv6_accept_ra'] = 2 - - # Interval at which neighbor bridges are removed - if conf.exists('max-age'): - bridge['max_age'] = int(conf.return_value('max-age')) - - # Determine bridge member interface (currently configured) - for intf in conf.list_nodes('member interface'): - # defaults are stored in util.py (they can't be here as all interface - # scripts use the function) - memberconf = get_bridge_member_config(conf, bridge['intf'], intf) - if memberconf: - memberconf['name'] = intf - bridge['member'].append(memberconf) - - # Determine bridge member interface (currently effective) - to determine which - # interfaces is no longer assigend to the bridge and thus can be removed - eff_intf = conf.list_effective_nodes('member interface') - act_intf = conf.list_nodes('member interface') - bridge['member_remove'] = list_diff(eff_intf, act_intf) - - # Priority for this bridge - if conf.exists('priority'): - bridge['priority'] = int(conf.return_value('priority')) - - # Enable spanning tree protocol - if conf.exists('stp'): - bridge['stp'] = 1 - - # retrieve VRF instance - if conf.exists('vrf'): - bridge['vrf'] = conf.return_value('vrf') + ifname = os.environ['VYOS_TAGNODE_VALUE'] + bridge = get_interface_dict(conf, base, ifname) + + # determine which members have been removed + tmp = get_removed_members(conf) + if tmp: + if 'member' in bridge: + bridge['member'].update({'interface_remove': tmp }) + else: + bridge.update({'member': {'interface_remove': tmp }}) + + if 'member' in bridge and 'interface' in bridge['member']: + # XXX TT2665 we need a copy of the dict keys for iteration, else we will get: + # RuntimeError: dictionary changed size during iteration + for interface in list(bridge['member']['interface']): + for key in ['cost', 'priority']: + if interface == key: + del bridge['member']['interface'][key] + continue + + # the default dictionary is not properly paged into the dict (see T2665) + # thus we will ammend it ourself + default_member_values = defaults(base + ['member', 'interface']) + + for interface, interface_config in bridge['member']['interface'].items(): + interface_config.update(default_member_values) + + # Check if we are a member of another bridge device + tmp = is_member(conf, interface, 'bridge') + if tmp and tmp != ifname: + interface_config.update({'is_bridge_member' : tmp}) + + # Check if we are a member of a bond device + tmp = is_member(conf, interface, 'bonding') + if tmp: + interface_config.update({'is_bond_member' : tmp}) + + # Bridge members must not have an assigned address + tmp = has_address_configured(conf, interface) + if tmp: + interface_config.update({'has_address' : ''}) return bridge def verify(bridge): - if bridge['dhcpv6_prm_only'] and bridge['dhcpv6_temporary']: - raise ConfigError('DHCPv6 temporary and parameters-only options are mutually exclusive!') + if 'deleted' in bridge: + return None - vrf_name = bridge['vrf'] - if vrf_name and vrf_name not in interfaces(): - raise ConfigError(f'VRF "{vrf_name}" does not exist') + verify_dhcpv6(bridge) + verify_vrf(bridge) - conf = Config() - for intf in bridge['member']: - # the interface must exist prior adding it to a bridge - if intf['name'] not in interfaces(): - raise ConfigError(( - f'Cannot add nonexistent interface "{intf["name"]}" ' - f'to bridge "{bridge["intf"]}"')) + if 'member' in bridge: + member = bridge.get('member') + bridge_name = bridge['ifname'] + for interface, interface_config in member.get('interface', {}).items(): + error_msg = f'Can not add interface "{interface}" to bridge "{bridge_name}", ' - if intf['name'] == 'lo': - raise ConfigError('Loopback interface "lo" can not be added to a bridge') + if interface == 'lo': + raise ConfigError('Loopback interface "lo" can not be added to a bridge') - # bridge members aren't allowed to be members of another bridge - for br in conf.list_nodes('interfaces bridge'): - # it makes no sense to verify ourself in this case - if br == bridge['intf']: - continue + if interface not in interfaces(): + raise ConfigError(error_msg + 'it does not exist!') - tmp = conf.list_nodes(f'interfaces bridge {br} member interface') - if intf['name'] in tmp: - raise ConfigError(( - f'Cannot add interface "{intf["name"]}" to bridge ' - f'"{bridge["intf"]}", it is already a member of bridge "{br}"!')) + if 'is_bridge_member' in interface_config: + tmp = interface_config['is_bridge_member'] + raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!') - # bridge members are not allowed to be bond members - tmp = is_member(conf, intf['name'], 'bonding') - if tmp: - raise ConfigError(( - f'Cannot add interface "{intf["name"]}" to bridge ' - f'"{bridge["intf"]}", it is already a member of bond "{tmp}"!')) + if 'is_bond_member' in interface_config: + tmp = interface_config['is_bond_member'] + raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!') - # bridge members must not have an assigned address - if has_address_configured(conf, intf['name']): - raise ConfigError(( - f'Cannot add interface "{intf["name"]}" to bridge ' - f'"{bridge["intf"]}", it has an address assigned!')) + if 'has_address' in interface_config: + raise ConfigError(error_msg + 'it has an address assigned!') return None @@ -268,120 +133,12 @@ def generate(bridge): return None def apply(bridge): - br = BridgeIf(bridge['intf']) - - if bridge['deleted']: + br = BridgeIf(bridge['ifname']) + if 'deleted' in bridge: # delete interface br.remove() else: - # enable interface - br.set_admin_state('up') - # set ageing time - br.set_ageing_time(bridge['aging']) - # set bridge forward delay - br.set_forward_delay(bridge['forwarding_delay']) - # set hello time - br.set_hello_time(bridge['hello_time']) - # configure ARP filter configuration - br.set_arp_filter(bridge['ip_disable_arp_filter']) - # configure ARP accept - br.set_arp_accept(bridge['ip_enable_arp_accept']) - # configure ARP announce - br.set_arp_announce(bridge['ip_enable_arp_announce']) - # configure ARP ignore - br.set_arp_ignore(bridge['ip_enable_arp_ignore']) - # IPv6 accept RA - br.set_ipv6_accept_ra(bridge['ipv6_accept_ra']) - # IPv6 address autoconfiguration - br.set_ipv6_autoconf(bridge['ipv6_autoconf']) - # IPv6 forwarding - br.set_ipv6_forwarding(bridge['ipv6_forwarding']) - # IPv6 Duplicate Address Detection (DAD) tries - br.set_ipv6_dad_messages(bridge['ipv6_dup_addr_detect']) - # set max message age - br.set_max_age(bridge['max_age']) - # set bridge priority - br.set_priority(bridge['priority']) - # turn stp on/off - br.set_stp(bridge['stp']) - # enable or disable IGMP querier - br.set_multicast_querier(bridge['igmp_querier']) - # update interface description used e.g. within SNMP - br.set_alias(bridge['description']) - - if bridge['dhcp_client_id']: - br.dhcp.v4.options['client_id'] = bridge['dhcp_client_id'] - - if bridge['dhcp_hostname']: - br.dhcp.v4.options['hostname'] = bridge['dhcp_hostname'] - - if bridge['dhcp_vendor_class_id']: - br.dhcp.v4.options['vendor_class_id'] = bridge['dhcp_vendor_class_id'] - - if bridge['dhcpv6_prm_only']: - br.dhcp.v6.options['dhcpv6_prm_only'] = True - - if bridge['dhcpv6_temporary']: - br.dhcp.v6.options['dhcpv6_temporary'] = True - - if bridge['dhcpv6_pd_length']: - br.dhcp.v6.options['dhcpv6_pd_length'] = br['dhcpv6_pd_length'] - - if bridge['dhcpv6_pd_interfaces']: - br.dhcp.v6.options['dhcpv6_pd_interfaces'] = br['dhcpv6_pd_interfaces'] - - # assign/remove VRF - br.set_vrf(bridge['vrf']) - - # Delete old IPv6 EUI64 addresses before changing MAC - # (adding members to a fresh bridge changes its MAC too) - for addr in bridge['ipv6_eui64_prefix_remove']: - br.del_ipv6_eui64_address(addr) - - # remove interface from bridge - for intf in bridge['member_remove']: - br.del_port(intf) - - # add interfaces to bridge - for member in bridge['member']: - # if we've come here we already verified the interface doesn't - # have addresses configured so just flush any remaining ones - cmd(f'ip addr flush dev "{member["name"]}"') - br.add_port(member['name']) - - # Change interface MAC address - if bridge['mac']: - br.set_mac(bridge['mac']) - - # Add IPv6 EUI-based addresses (must be done after adding the - # 1st bridge member or setting its MAC) - for addr in bridge['ipv6_eui64_prefix']: - br.add_ipv6_eui64_address(addr) - - # up/down interface - if bridge['disable']: - br.set_admin_state('down') - - # Configure interface address(es) - # - not longer required addresses get removed first - # - newly addresses will be added second - for addr in bridge['address_remove']: - br.del_addr(addr) - for addr in bridge['address']: - br.add_addr(addr) - - STPBridgeIf = STP.enable(BridgeIf) - # configure additional bridge member options - for member in bridge['member']: - i = STPBridgeIf(member['name']) - # configure ARP cache timeout - i.set_arp_cache_tmo(member['arp_cache_tmo']) - # ignore link state changes - i.set_link_detect(member['disable_link_detect']) - # set bridge port path cost - i.set_path_cost(member['cost']) - # set bridge port path priority - i.set_path_priority(member['priority']) + br.update(bridge) return None diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 60aafae32..d43552e50 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -19,72 +19,36 @@ import os from sys import exit from vyos.config import Config -from vyos.configdict import dict_merge -from vyos.configdict import T2665_default_dict_cleanup +from vyos.configdict import get_interface_dict from vyos.configverify import verify_interface_exists from vyos.configverify import verify_dhcpv6 from vyos.configverify import verify_address from vyos.configverify import verify_vrf from vyos.configverify import verify_vlan_config from vyos.ifconfig import EthernetIf -from vyos.ifconfig_vlan import get_removed_vlans -from vyos.validate import is_member -from vyos.xml import defaults from vyos import ConfigError from vyos import airbag airbag.enable() def get_config(): - """ Retrive CLI config as dictionary. Dictionary can never be empty, - as at least the interface name will be added or a deleted flag """ + """ + Retrive CLI config as dictionary. Dictionary can never be empty, as at least the + interface name will be added or a deleted flag + """ conf = Config() + base = ['interfaces', 'ethernet'] # determine tagNode instance if 'VYOS_TAGNODE_VALUE' not in os.environ: raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') - # retrieve interface default values - base = ['interfaces', 'ethernet'] - default_values = defaults(base) - ifname = os.environ['VYOS_TAGNODE_VALUE'] - base = base + [ifname] - # setup config level which is extracted in get_removed_vlans() - conf.set_level(base) - ethernet = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - - # Check if interface has been removed - if ethernet == {}: - ethernet.update({'deleted' : ''}) - - # We have gathered the dict representation of the CLI, but there are - # default options which we need to update into the dictionary - # retrived. - ethernet = dict_merge(default_values, ethernet) - - # Add interface instance name into dictionary - ethernet.update({'ifname': ifname}) - - # Check if we are a member of a bridge device - bridge = is_member(conf, ifname, 'bridge') - if bridge: - tmp = {'is_bridge_member' : bridge} - ethernet.update(tmp) - - # Check if we are a member of a bond device - bond = is_member(conf, ifname, 'bonding') - if bond: - tmp = {'is_bond_member' : bond} - ethernet.update(tmp) - - ethernet = T2665_default_dict_cleanup( ethernet ) - # Check vif, vif-s/vif-c VLAN interfaces for removal - ethernet = get_removed_vlans( conf, ethernet ) + ethernet = get_interface_dict(conf, base, ifname) return ethernet def verify(ethernet): - if 'deleted' in ethernet.keys(): + if 'deleted' in ethernet: return None verify_interface_exists(ethernet) @@ -114,7 +78,7 @@ def generate(ethernet): def apply(ethernet): e = EthernetIf(ethernet['ifname']) - if 'deleted' in ethernet.keys(): + if 'deleted' in ethernet: # delete interface e.remove() else: -- cgit v1.2.3 From c8cd7951e38ae2819d4c9f87089fcf59b7e6b70d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Jul 2020 22:25:12 +0200 Subject: pseudo-ethernet: ifconfig: T2653: move to get_config_dict() The current VyOS CLI parser code written in Python contains a ton of duplicates which I can also hold myself accountable for - or maybe mainly me - depends on the angle of judge. While providing a new update() method in vyos.ifconfig.interfaces() this is extended for pdeudo-ethernet interfaces in the derived class. --- .../interfaces-pseudo-ethernet.xml.in | 1 + src/conf_mode/interfaces-pseudo-ethernet.py | 216 +++------------------ 2 files changed, 32 insertions(+), 185 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-pseudo-ethernet.xml.in b/interface-definitions/interfaces-pseudo-ethernet.xml.in index d5f9ca661..0ef45e2c2 100644 --- a/interface-definitions/interfaces-pseudo-ethernet.xml.in +++ b/interface-definitions/interfaces-pseudo-ethernet.xml.in @@ -70,6 +70,7 @@ mode must be private, vepa, bridge or passthru + private #include #include diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py index fb8237bee..cce9b020b 100755 --- a/src/conf_mode/interfaces-pseudo-ethernet.py +++ b/src/conf_mode/interfaces-pseudo-ethernet.py @@ -18,115 +18,52 @@ import os from copy import deepcopy from sys import exit -from netifaces import interfaces from vyos.config import Config -from vyos.configdict import list_diff, intf_to_dict, add_to_dict, interface_default_data -from vyos.ifconfig import MACVLANIf, Section -from vyos.ifconfig_vlan import apply_all_vlans, verify_vlan_config +from vyos.configdict import get_interface_dict +from vyos.configdict import leaf_node_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.ifconfig import MACVLANIf from vyos import ConfigError from vyos import airbag airbag.enable() -default_config_data = { - **interface_default_data, - 'deleted': False, - 'intf': '', - 'ip_arp_cache_tmo': 30, - 'ip_proxy_arp_pvlan': 0, - 'source_interface': '', - 'recreating_required': False, - 'mode': 'private', - 'vif_s': {}, - 'vif_s_remove': [], - 'vif': {}, - 'vif_remove': [], - 'vrf': '' -} - def get_config(): - peth = deepcopy(default_config_data) + """ + Retrive CLI config as dictionary. Dictionary can never be empty, as at least the + interface name will be added or a deleted flag + """ conf = Config() + base = ['interfaces', 'pseudo-ethernet'] # determine tagNode instance if 'VYOS_TAGNODE_VALUE' not in os.environ: raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') - peth['intf'] = os.environ['VYOS_TAGNODE_VALUE'] - - # Check if interface has been removed - cfg_base = ['interfaces', 'pseudo-ethernet', peth['intf']] - if not conf.exists(cfg_base): - peth['deleted'] = True - return peth - - # set new configuration level - conf.set_level(cfg_base) - - peth, disabled = intf_to_dict(conf, default_config_data) - - # ARP cache entry timeout in seconds - if conf.exists(['ip', 'arp-cache-timeout']): - peth['ip_arp_cache_tmo'] = int(conf.return_value(['ip', 'arp-cache-timeout'])) - - # Enable private VLAN proxy ARP on this interface - if conf.exists(['ip', 'proxy-arp-pvlan']): - peth['ip_proxy_arp_pvlan'] = 1 - - # Physical interface - if conf.exists(['source-interface']): - peth['source_interface'] = conf.return_value(['source-interface']) - tmp = conf.return_effective_value(['source-interface']) - if tmp != peth['source_interface']: - peth['recreating_required'] = True - - # MACvlan mode - if conf.exists(['mode']): - peth['mode'] = conf.return_value(['mode']) - tmp = conf.return_effective_value(['mode']) - if tmp != peth['mode']: - peth['recreating_required'] = True + ifname = os.environ['VYOS_TAGNODE_VALUE'] + peth = get_interface_dict(conf, base, ifname) - add_to_dict(conf, disabled, peth, 'vif', 'vif') - add_to_dict(conf, disabled, peth, 'vif-s', 'vif_s') + mode = leaf_node_changed(conf, ['mode']) + if mode: + peth.update({'mode_old' : mode}) + import pprint + pprint.pprint(peth) return peth def verify(peth): - if peth['deleted']: - if peth['is_bridge_member']: - raise ConfigError(( - f'Cannot delete interface "{peth["intf"]}" as it is a ' - f'member of bridge "{peth["is_bridge_member"]}"!')) - + if 'deleted' in peth: + verify_bridge_delete(peth) return None - if not peth['source_interface']: - raise ConfigError(( - f'Link device must be set for pseudo-ethernet "{peth["intf"]}"')) - - if not peth['source_interface'] in interfaces(): - raise ConfigError(( - f'Pseudo-ethernet "{peth["intf"]}" link device does not exist')) - - if ( peth['is_bridge_member'] - and ( peth['address'] - or peth['ipv6_eui64_prefix'] - or peth['ipv6_autoconf'] ) ): - raise ConfigError(( - f'Cannot assign address to interface "{peth["intf"]}" ' - f'as it is a member of bridge "{peth["is_bridge_member"]}"!')) - - if peth['vrf']: - if peth['vrf'] not in interfaces(): - raise ConfigError(f'VRF "{peth["vrf"]}" does not exist') - - if peth['is_bridge_member']: - raise ConfigError(( - f'Interface "{peth["intf"]}" cannot be member of VRF ' - f'"{peth["vrf"]}" and bridge {peth["is_bridge_member"]} ' - f'at the same time!')) + verify_source_interface(peth) + verify_vrf(peth) + verify_address(peth) # use common function to verify VLAN configuration verify_vlan_config(peth) @@ -136,17 +73,16 @@ def generate(peth): return None def apply(peth): - if peth['deleted']: + if 'deleted' in peth: # delete interface - MACVLANIf(peth['intf']).remove() + MACVLANIf(peth['ifname']).remove() return None # Check if MACVLAN interface already exists. Parameters like the underlaying # source-interface device or mode can not be changed on the fly and the interface # needs to be recreated from the bottom. - if peth['intf'] in interfaces(): - if peth['recreating_required']: - MACVLANIf(peth['intf']).remove() + if 'mode_old' in peth: + MACVLANIf(peth['ifname']).remove() # MACVLAN interface needs to be created on-block instead of passing a ton # of arguments, I just use a dict that is managed by vyos.ifconfig @@ -158,98 +94,8 @@ def apply(peth): # It is safe to "re-create" the interface always, there is a sanity check # that the interface will only be create if its non existent - p = MACVLANIf(peth['intf'], **conf) - - # update interface description used e.g. within SNMP - p.set_alias(peth['description']) - - if peth['dhcp_client_id']: - p.dhcp.v4.options['client_id'] = peth['dhcp_client_id'] - - if peth['dhcp_hostname']: - p.dhcp.v4.options['hostname'] = peth['dhcp_hostname'] - - if peth['dhcp_vendor_class_id']: - p.dhcp.v4.options['vendor_class_id'] = peth['dhcp_vendor_class_id'] - - if peth['dhcpv6_prm_only']: - p.dhcp.v6.options['dhcpv6_prm_only'] = True - - if peth['dhcpv6_temporary']: - p.dhcp.v6.options['dhcpv6_temporary'] = True - - if peth['dhcpv6_pd_length']: - p.dhcp.v6.options['dhcpv6_pd_length'] = peth['dhcpv6_pd_length'] - - if peth['dhcpv6_pd_interfaces']: - p.dhcp.v6.options['dhcpv6_pd_interfaces'] = peth['dhcpv6_pd_interfaces'] - - # ignore link state changes - p.set_link_detect(peth['disable_link_detect']) - # configure ARP cache timeout in milliseconds - p.set_arp_cache_tmo(peth['ip_arp_cache_tmo']) - # configure ARP filter configuration - p.set_arp_filter(peth['ip_disable_arp_filter']) - # configure ARP accept - p.set_arp_accept(peth['ip_enable_arp_accept']) - # configure ARP announce - p.set_arp_announce(peth['ip_enable_arp_announce']) - # configure ARP ignore - p.set_arp_ignore(peth['ip_enable_arp_ignore']) - # Enable proxy-arp on this interface - p.set_proxy_arp(peth['ip_proxy_arp']) - # Enable private VLAN proxy ARP on this interface - p.set_proxy_arp_pvlan(peth['ip_proxy_arp_pvlan']) - # IPv6 accept RA - p.set_ipv6_accept_ra(peth['ipv6_accept_ra']) - # IPv6 address autoconfiguration - p.set_ipv6_autoconf(peth['ipv6_autoconf']) - # IPv6 forwarding - p.set_ipv6_forwarding(peth['ipv6_forwarding']) - # IPv6 Duplicate Address Detection (DAD) tries - p.set_ipv6_dad_messages(peth['ipv6_dup_addr_detect']) - - # assign/remove VRF (ONLY when not a member of a bridge, - # otherwise 'nomaster' removes it from it) - if not peth['is_bridge_member']: - p.set_vrf(peth['vrf']) - - # Delete old IPv6 EUI64 addresses before changing MAC - for addr in peth['ipv6_eui64_prefix_remove']: - p.del_ipv6_eui64_address(addr) - - # Change interface MAC address - if peth['mac']: - p.set_mac(peth['mac']) - - # Add IPv6 EUI-based addresses - for addr in peth['ipv6_eui64_prefix']: - p.add_ipv6_eui64_address(addr) - - # Change interface mode - p.set_mode(peth['mode']) - - # Enable/Disable interface - if peth['disable']: - p.set_admin_state('down') - else: - p.set_admin_state('up') - - # Configure interface address(es) - # - not longer required addresses get removed first - # - newly addresses will be added second - for addr in peth['address_remove']: - p.del_addr(addr) - for addr in peth['address']: - p.add_addr(addr) - - # re-add ourselves to any bridge we might have fallen out of - if peth['is_bridge_member']: - p.add_to_bridge(peth['is_bridge_member']) - - # apply all vlans to interface - apply_all_vlans(p, peth) - + p = MACVLANIf(peth['ifname'], **conf) + p.update(peth) return None if __name__ == '__main__': -- cgit v1.2.3 From f81b0443cf09c34cb1f2060094e3eb294b8fa192 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 24 Jul 2020 17:20:50 +0200 Subject: bonding: ifconfig: T2653: move to get_config_dict() The current VyOS CLI parser code written in Python contains a ton of duplicates which I can also hold myself accountable for - or maybe mainly me - depends on the angle of judge. --- interface-definitions/interfaces-bonding.xml.in | 2 + python/vyos/ifconfig/bond.py | 118 ++++++- python/vyos/ifconfig/interface.py | 16 +- python/vyos/validate.py | 5 +- src/conf_mode/interfaces-bonding.py | 437 ++++++------------------ 5 files changed, 241 insertions(+), 337 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in index ddd52979b..7d658f6a0 100644 --- a/interface-definitions/interfaces-bonding.xml.in +++ b/interface-definitions/interfaces-bonding.xml.in @@ -78,6 +78,7 @@ hash-policy must be layer2 layer2+3 or layer3+4 + layer2 @@ -137,6 +138,7 @@ mode must be 802.3ad, active-backup, broadcast, round-robin, transmit-load-balance, adaptive-load-balance, or xor + 802.3ad diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py index 47dd4ff34..5a48ac632 100644 --- a/python/vyos/ifconfig/bond.py +++ b/python/vyos/ifconfig/bond.py @@ -14,14 +14,15 @@ # License along with this library. If not, see . import os +import jmespath from vyos.ifconfig.interface import Interface from vyos.ifconfig.vlan import VLAN +from vyos.util import cmd from vyos.validate import assert_list from vyos.validate import assert_positive - @Interface.register @VLAN.enable class BondIf(Interface): @@ -179,7 +180,13 @@ class BondIf(Interface): >>> BondIf('bond0').get_arp_ip_target() '192.0.2.1' """ - return self.get_interface('bond_arp_ip_target') + # As this function might also be called from update() of a VLAN interface + # we must check if the bond_arp_ip_target retrieval worked or not - as this + # can not be set for a bond vif interface + try: + return self.get_interface('bond_arp_ip_target') + except FileNotFoundError: + return '' def set_arp_ip_target(self, target): """ @@ -209,11 +216,31 @@ class BondIf(Interface): >>> BondIf('bond0').add_port('eth0') >>> BondIf('bond0').add_port('eth1') """ - # An interface can only be added to a bond if it is in 'down' state. If - # interface is in 'up' state, the following Kernel error will be thrown: - # bond0: eth1 is up - this may be due to an out of date ifenslave. - Interface(interface).set_admin_state('down') - return self.set_interface('bond_add_port', f'+{interface}') + + # From drivers/net/bonding/bond_main.c: + # ... + # bond_set_slave_link_state(new_slave, + # BOND_LINK_UP, + # BOND_SLAVE_NOTIFY_NOW); + # ... + # + # The kernel will ALWAYS place new bond members in "up" state regardless + # what the CLI will tell us! + + # Physical interface must be in admin down state before they can be + # enslaved. If this is not the case an error will be shown: + # bond0: eth0 is up - this may be due to an out of date ifenslave + slave = Interface(interface) + slave_state = slave.get_admin_state() + if slave_state == 'up': + slave.set_admin_state('down') + + ret = self.set_interface('bond_add_port', f'+{interface}') + # The kernel will ALWAYS place new bond members in "up" state regardless + # what the LI is configured for - thus we place the interface in its + # desired state + slave.set_admin_state(slave_state) + return ret def del_port(self, interface): """ @@ -277,3 +304,80 @@ class BondIf(Interface): >>> BondIf('bond0').set_mode('802.3ad') """ return self.set_interface('bond_mode', mode) + + def update(self, config): + """ General helper function which works on a dictionary retrived by + get_config_dict(). It's main intention is to consolidate the scattered + interface setup code and provide a single point of entry when workin + on any interface. """ + + # use ref-counting function to place an interface into admin down state. + # set_admin_state_up() must be called the same amount of times else the + # interface won't come up. This can/should be used to prevent link flapping + # when changing interface parameters require the interface to be down. + # We will disable it once before reconfiguration and enable it afterwards. + if 'shutdown_required' in config: + self.set_admin_state('down') + + # call base class first + super().update(config) + + # ARP monitor targets need to be synchronized between sysfs and CLI. + # Unfortunately an address can't be send twice to sysfs as this will + # result in the following exception: OSError: [Errno 22] Invalid argument. + # + # We remove ALL addresses prior to adding new ones, this will remove + # addresses manually added by the user too - but as we are limited to 16 adresses + # from the kernel side this looks valid to me. We won't run into an error + # when a user added manual adresses which would result in having more + # then 16 adresses in total. + arp_tgt_addr = list(map(str, self.get_arp_ip_target().split())) + for addr in arp_tgt_addr: + self.set_arp_ip_target('-' + addr) + + # Add configured ARP target addresses + value = jmespath.search('arp_monitor.target', config) + if isinstance(value, str): + value = [value] + if value: + for addr in value: + self.set_arp_ip_target('+' + addr) + + # Bonding transmit hash policy + value = config.get('hash_policy') + if value: self.set_hash_policy(value) + + # Some interface options can only be changed if the interface is + # administratively down + if self.get_admin_state() == 'down': + # Delete bond member port(s) + for interface in self.get_slaves(): + self.del_port(interface) + + # Bonding policy/mode + value = config.get('mode') + if value: self.set_mode(value) + + # Add (enslave) interfaces to bond + value = jmespath.search('member.interface', config) + if value: + for interface in value: + # if we've come here we already verified the interface does + # not have an addresses configured so just flush any + # remaining ones + cmd(f'ip addr flush dev "{interface}"') + self.add_port(interface) + + # Primary device interface - must be set after 'mode' + value = config.get('primary') + if value: self.set_primary(value) + + # Enable/Disable of an interface must always be done at the end of the + # derived class to make use of the ref-counting set_admin_state() + # function. We will only enable the interface if 'up' was called as + # often as 'down'. This is required by some interface implementations + # as certain parameters can only be changed when the interface is + # in admin-down state. This ensures the link does not flap during + # reconfiguration. + state = 'down' if 'disable' in config else 'up' + self.set_admin_state(state) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 1fe4f74f2..7e887db1b 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -205,6 +205,7 @@ class Interface(Control): # make sure the ifname is the first argument and not from the dict self.config['ifname'] = ifname + self._admin_state_down_cnt = 0 # we must have updated config before initialising the Interface super().__init__(**kargs) @@ -594,7 +595,13 @@ class Interface(Control): if not int(flags, 16) & 1: return None - return self.set_interface('admin_state', state) + if state == 'up': + self._admin_state_down_cnt -= 1 + if self._admin_state_down_cnt < 1: + return self.set_interface('admin_state', state) + else: + self._admin_state_down_cnt += 1 + return self.set_interface('admin_state', state) def set_proxy_arp(self, enable): """ @@ -829,8 +836,11 @@ class Interface(Control): # There are some items in the configuration which can only be applied # if this instance is not bound to a bridge. This should be checked # by the caller but better save then sorry! - if not config.get('is_bridge_member', False): - # Bind interface instance into VRF + if not any(k in ['is_bond_member', 'is_bridge_member'] for k in config): + # Bind interface to given VRF or unbind it if vrf node is not set. + # unbinding will call 'ip link set dev eth0 nomaster' which will + # also drop the interface out of a bridge or bond - thus this is + # checked before self.set_vrf(config.get('vrf', '')) # DHCP options diff --git a/python/vyos/validate.py b/python/vyos/validate.py index a0620e4dd..ceeb6888a 100644 --- a/python/vyos/validate.py +++ b/python/vyos/validate.py @@ -279,7 +279,6 @@ def is_member(conf, interface, intftype=None): False -> interface type cannot have members """ ret_val = None - if intftype not in ['bonding', 'bridge', None]: raise ValueError(( f'unknown interface type "{intftype}" or it cannot ' @@ -292,9 +291,9 @@ def is_member(conf, interface, intftype=None): conf.set_level([]) for it in intftype: - base = 'interfaces ' + it + base = ['interfaces', it] for intf in conf.list_nodes(base): - memberintf = [base, intf, 'member', 'interface'] + memberintf = base + [intf, 'member', 'interface'] if xml.is_tag(memberintf): if interface in conf.list_nodes(memberintf): ret_val = intf diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index a16c4e105..8e87a0059 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -16,41 +16,25 @@ import os -from copy import deepcopy from sys import exit from netifaces import interfaces -from vyos.ifconfig import BondIf -from vyos.ifconfig_vlan import apply_all_vlans, verify_vlan_config -from vyos.configdict import list_diff, intf_to_dict, add_to_dict, interface_default_data from vyos.config import Config -from vyos.util import call, cmd -from vyos.validate import is_member, has_address_configured +from vyos.configdict import get_interface_dict +from vyos.configdict import leaf_node_changed +from vyos.configverify import verify_address +from vyos.configverify import verify_bridge_delete +from vyos.configverify import verify_dhcpv6 +from vyos.configverify import verify_source_interface +from vyos.configverify import verify_vlan_config +from vyos.configverify import verify_vrf +from vyos.ifconfig import BondIf +from vyos.validate import is_member +from vyos.validate import has_address_configured from vyos import ConfigError - from vyos import airbag airbag.enable() -default_config_data = { - **interface_default_data, - 'arp_mon_intvl': 0, - 'arp_mon_tgt': [], - 'deleted': False, - 'hash_policy': 'layer2', - 'intf': '', - 'ip_arp_cache_tmo': 30, - 'ip_proxy_arp_pvlan': 0, - 'mode': '802.3ad', - 'member': [], - 'shutdown_required': False, - 'primary': '', - 'vif_s': {}, - 'vif_s_remove': [], - 'vif': {}, - 'vif_remove': [], -} - - def get_bond_mode(mode): if mode == 'round-robin': return 'balance-rr' @@ -67,339 +51,144 @@ def get_bond_mode(mode): elif mode == 'adaptive-load-balance': return 'balance-alb' else: - raise ConfigError('invalid bond mode "{}"'.format(mode)) + raise ConfigError(f'invalid bond mode "{mode}"') def get_config(): + """ + Retrive CLI config as dictionary. Dictionary can never be empty, as at least the + interface name will be added or a deleted flag + """ + conf = Config() + base = ['interfaces', 'bonding'] + # determine tagNode instance if 'VYOS_TAGNODE_VALUE' not in os.environ: raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') ifname = os.environ['VYOS_TAGNODE_VALUE'] - conf = Config() + bond = get_interface_dict(conf, base, ifname) + + # To make our own life easier transfor the list of member interfaces + # into a dictionary - we will use this to add additional information + # later on for wach member + if 'member' in bond and 'interface' in bond['member']: + # first convert it to a list if only one member is given + if isinstance(bond['member']['interface'], str): + bond['member']['interface'] = [bond['member']['interface']] + + tmp={} + for interface in bond['member']['interface']: + tmp.update({interface: {}}) + + bond['member']['interface'] = tmp + + if 'mode' in bond: + bond['mode'] = get_bond_mode(bond['mode']) + + tmp = leaf_node_changed(conf, ['mode']) + if tmp: + bond.update({'shutdown_required': ''}) + + # determine which members have been removed + tmp = leaf_node_changed(conf, ['member', 'interface']) + if tmp: + bond.update({'shutdown_required': ''}) + if 'member' in bond: + bond['member'].update({'interface_remove': tmp }) + else: + bond.update({'member': {'interface_remove': tmp }}) + + if 'member' in bond and 'interface' in bond['member']: + for interface, interface_config in bond['member']['interface'].items(): + # Check if we are a member of another bond device + tmp = is_member(conf, interface, 'bridge') + if tmp: + interface_config.update({'is_bridge_member' : tmp}) - # initialize kernel module if not loaded - if not os.path.isfile('/sys/class/net/bonding_masters'): - import syslog - syslog.syslog(syslog.LOG_NOTICE, "loading bonding kernel module") - if call('modprobe bonding max_bonds=0 miimon=250') != 0: - syslog.syslog(syslog.LOG_NOTICE, "failed loading bonding kernel module") - raise ConfigError("failed loading bonding kernel module") - - # check if bond has been removed - cfg_base = 'interfaces bonding ' + ifname - if not conf.exists(cfg_base): - bond = deepcopy(default_config_data) - bond['intf'] = ifname - bond['deleted'] = True - return bond - - # set new configuration level - conf.set_level(cfg_base) - - bond, disabled = intf_to_dict(conf, default_config_data) - - # ARP link monitoring frequency in milliseconds - if conf.exists('arp-monitor interval'): - bond['arp_mon_intvl'] = int(conf.return_value('arp-monitor interval')) - - # IP address to use for ARP monitoring - if conf.exists('arp-monitor target'): - bond['arp_mon_tgt'] = conf.return_values('arp-monitor target') - - # Bonding transmit hash policy - if conf.exists('hash-policy'): - bond['hash_policy'] = conf.return_value('hash-policy') - - # ARP cache entry timeout in seconds - if conf.exists('ip arp-cache-timeout'): - bond['ip_arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout')) - - # Enable private VLAN proxy ARP on this interface - if conf.exists('ip proxy-arp-pvlan'): - bond['ip_proxy_arp_pvlan'] = 1 - - # Bonding mode - if conf.exists('mode'): - act_mode = conf.return_value('mode') - eff_mode = conf.return_effective_value('mode') - if not (act_mode == eff_mode): - bond['shutdown_required'] = True - - bond['mode'] = get_bond_mode(act_mode) - - # determine bond member interfaces (currently configured) - bond['member'] = conf.return_values('member interface') - - # We can not call conf.return_effective_values() as it would not work - # on reboots. Reboots/First boot will return that running config and - # saved config is the same, thus on a reboot the bond members will - # not be added all (https://phabricator.vyos.net/T2030) - live_members = BondIf(bond['intf']).get_slaves() - if not (bond['member'] == live_members): - bond['shutdown_required'] = True - - # Primary device interface - if conf.exists('primary'): - bond['primary'] = conf.return_value('primary') - - add_to_dict(conf, disabled, bond, 'vif', 'vif') - add_to_dict(conf, disabled, bond, 'vif-s', 'vif_s') + # Check if we are a member of a bond device + tmp = is_member(conf, interface, 'bonding') + if tmp and tmp != ifname: + interface_config.update({'is_bond_member' : tmp}) + + # bond members must not have an assigned address + tmp = has_address_configured(conf, interface) + if tmp: + interface_config.update({'has_address' : ''}) return bond def verify(bond): - if bond['deleted']: - if bond['is_bridge_member']: - raise ConfigError(( - f'Cannot delete interface "{bond["intf"]}" as it is a ' - f'member of bridge "{bond["is_bridge_member"]}"!')) - + if 'deleted' in bond: + verify_bridge_delete(bond) return None - if len(bond['arp_mon_tgt']) > 16: - raise ConfigError('The maximum number of arp-monitor targets is 16') + if 'arp_monitor' in bond: + if 'target' in bond['arp_monitor'] and len(int(bond['arp_monitor']['target'])) > 16: + raise ConfigError('The maximum number of arp-monitor targets is 16') + + if 'interval' in bond['arp_monitor'] and len(int(bond['arp_monitor']['interval'])) > 0: + if bond['mode'] in ['802.3ad', 'balance-tlb', 'balance-alb']: + raise ConfigError('ARP link monitoring does not work for mode 802.3ad, ' \ + 'transmit-load-balance or adaptive-load-balance') - if bond['primary']: + if 'primary' in bond: if bond['mode'] not in ['active-backup', 'balance-tlb', 'balance-alb']: - raise ConfigError(( - 'Mode dependency failed, primary not supported in mode ' - f'"{bond["mode"]}"!')) - - if ( bond['is_bridge_member'] - and ( bond['address'] - or bond['ipv6_eui64_prefix'] - or bond['ipv6_autoconf'] ) ): - raise ConfigError(( - f'Cannot assign address to interface "{bond["intf"]}" ' - f'as it is a member of bridge "{bond["is_bridge_member"]}"!')) - - if bond['vrf']: - if bond['vrf'] not in interfaces(): - raise ConfigError(f'VRF "{bond["vrf"]}" does not exist') - - if bond['is_bridge_member']: - raise ConfigError(( - f'Interface "{bond["intf"]}" cannot be member of VRF ' - f'"{bond["vrf"]}" and bridge {bond["is_bridge_member"]} ' - f'at the same time!')) + raise ConfigError('Option primary - mode dependency failed, not' + 'supported in mode {mode}!'.format(**bond)) + + verify_address(bond) + verify_dhcpv6(bond) + verify_vrf(bond) # use common function to verify VLAN configuration verify_vlan_config(bond) - conf = Config() - for intf in bond['member']: - # check if member interface is "real" - if intf not in interfaces(): - raise ConfigError(f'Interface {intf} does not exist!') - - # a bonding member interface is only allowed to be assigned to one bond! - all_bonds = conf.list_nodes('interfaces bonding') - # We do not need to check our own bond - all_bonds.remove(bond['intf']) - for tmp in all_bonds: - if conf.exists('interfaces bonding {tmp} member interface {intf}'): - raise ConfigError(( - f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' - f'it is already a member of bond "{tmp}"!')) - - # can not add interfaces with an assigned address to a bond - if has_address_configured(conf, intf): - raise ConfigError(( - f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' - f'it has an address assigned!')) - - # bond members are not allowed to be bridge members - tmp = is_member(conf, intf, 'bridge') - if tmp: - raise ConfigError(( - f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' - f'it is already a member of bridge "{tmp}"!')) - - # bond members are not allowed to be vrrp members - for tmp in conf.list_nodes('high-availability vrrp group'): - if conf.exists('high-availability vrrp group {tmp} interface {intf}'): - raise ConfigError(( - f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' - f'it is already a member of VRRP group "{tmp}"!')) - - # bond members are not allowed to be underlaying psuedo-ethernet devices - for tmp in conf.list_nodes('interfaces pseudo-ethernet'): - if conf.exists('interfaces pseudo-ethernet {tmp} link {intf}'): - raise ConfigError(( - f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' - f'it is already the link of pseudo-ethernet "{tmp}"!')) - - # bond members are not allowed to be underlaying vxlan devices - for tmp in conf.list_nodes('interfaces vxlan'): - if conf.exists('interfaces vxlan {tmp} link {intf}'): - raise ConfigError(( - f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' - f'it is already the link of VXLAN "{tmp}"!')) - - if bond['primary']: - if bond['primary'] not in bond['member']: - raise ConfigError(f'Bond "{bond["intf"]}" primary interface must be a member') + bond_name = bond['ifname'] + if 'member' in bond: + member = bond.get('member') + for interface, interface_config in member.get('interface', {}).items(): + error_msg = f'Can not add interface "{interface}" to bond "{bond_name}", ' + + if interface == 'lo': + raise ConfigError('Loopback interface "lo" can not be added to a bond') + + if interface not in interfaces(): + raise ConfigError(error_msg + 'it does not exist!') + + if 'is_bridge_member' in interface_config: + tmp = interface_config['is_bridge_member'] + raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!') + + if 'is_bond_member' in interface_config: + tmp = interface_config['is_bond_member'] + raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!') + + if 'has_address' in interface_config: + raise ConfigError(error_msg + 'it has an address assigned!') + + + if 'primary' in bond: + if bond['primary'] not in bond['member']['interface']: + raise ConfigError(f'Primary interface of bond "{bond_name}" must be a member interface') if bond['mode'] not in ['active-backup', 'balance-tlb', 'balance-alb']: raise ConfigError('primary interface only works for mode active-backup, ' \ 'transmit-load-balance or adaptive-load-balance') - if bond['arp_mon_intvl'] > 0: - if bond['mode'] in ['802.3ad', 'balance-tlb', 'balance-alb']: - raise ConfigError('ARP link monitoring does not work for mode 802.3ad, ' \ - 'transmit-load-balance or adaptive-load-balance') - return None def generate(bond): return None def apply(bond): - b = BondIf(bond['intf']) + b = BondIf(bond['ifname']) - if bond['deleted']: + if 'deleted' in bond: # delete interface b.remove() else: - # ARP link monitoring frequency, reset miimon when arp-montior is inactive - # this is done inside BondIf automatically - b.set_arp_interval(bond['arp_mon_intvl']) - - # ARP monitor targets need to be synchronized between sysfs and CLI. - # Unfortunately an address can't be send twice to sysfs as this will - # result in the following exception: OSError: [Errno 22] Invalid argument. - # - # We remove ALL adresses prior adding new ones, this will remove addresses - # added manually by the user too - but as we are limited to 16 adresses - # from the kernel side this looks valid to me. We won't run into an error - # when a user added manual adresses which would result in having more - # then 16 adresses in total. - arp_tgt_addr = list(map(str, b.get_arp_ip_target().split())) - for addr in arp_tgt_addr: - b.set_arp_ip_target('-' + addr) - - # Add configured ARP target addresses - for addr in bond['arp_mon_tgt']: - b.set_arp_ip_target('+' + addr) - - # update interface description used e.g. within SNMP - b.set_alias(bond['description']) - - if bond['dhcp_client_id']: - b.dhcp.v4.options['client_id'] = bond['dhcp_client_id'] - - if bond['dhcp_hostname']: - b.dhcp.v4.options['hostname'] = bond['dhcp_hostname'] - - if bond['dhcp_vendor_class_id']: - b.dhcp.v4.options['vendor_class_id'] = bond['dhcp_vendor_class_id'] - - if bond['dhcpv6_prm_only']: - b.dhcp.v6.options['dhcpv6_prm_only'] = True - - if bond['dhcpv6_temporary']: - b.dhcp.v6.options['dhcpv6_temporary'] = True - - if bond['dhcpv6_pd_length']: - b.dhcp.v6.options['dhcpv6_pd_length'] = bond['dhcpv6_pd_length'] - - if bond['dhcpv6_pd_interfaces']: - b.dhcp.v6.options['dhcpv6_pd_interfaces'] = bond['dhcpv6_pd_interfaces'] - - # ignore link state changes - b.set_link_detect(bond['disable_link_detect']) - # Bonding transmit hash policy - b.set_hash_policy(bond['hash_policy']) - # configure ARP cache timeout in milliseconds - b.set_arp_cache_tmo(bond['ip_arp_cache_tmo']) - # configure ARP filter configuration - b.set_arp_filter(bond['ip_disable_arp_filter']) - # configure ARP accept - b.set_arp_accept(bond['ip_enable_arp_accept']) - # configure ARP announce - b.set_arp_announce(bond['ip_enable_arp_announce']) - # configure ARP ignore - b.set_arp_ignore(bond['ip_enable_arp_ignore']) - # Enable proxy-arp on this interface - b.set_proxy_arp(bond['ip_proxy_arp']) - # Enable private VLAN proxy ARP on this interface - b.set_proxy_arp_pvlan(bond['ip_proxy_arp_pvlan']) - # IPv6 accept RA - b.set_ipv6_accept_ra(bond['ipv6_accept_ra']) - # IPv6 address autoconfiguration - b.set_ipv6_autoconf(bond['ipv6_autoconf']) - # IPv6 forwarding - b.set_ipv6_forwarding(bond['ipv6_forwarding']) - # IPv6 Duplicate Address Detection (DAD) tries - b.set_ipv6_dad_messages(bond['ipv6_dup_addr_detect']) - - # Delete old IPv6 EUI64 addresses before changing MAC - for addr in bond['ipv6_eui64_prefix_remove']: - b.del_ipv6_eui64_address(addr) - - # Change interface MAC address - if bond['mac']: - b.set_mac(bond['mac']) - - # Add IPv6 EUI-based addresses - for addr in bond['ipv6_eui64_prefix']: - b.add_ipv6_eui64_address(addr) - - # Maximum Transmission Unit (MTU) - b.set_mtu(bond['mtu']) - - # Primary device interface - if bond['primary']: - b.set_primary(bond['primary']) - - # Some parameters can not be changed when the bond is up. - if bond['shutdown_required']: - # Disable bond prior changing of certain properties - b.set_admin_state('down') - - # The bonding mode can not be changed when there are interfaces enslaved - # to this bond, thus we will free all interfaces from the bond first! - for intf in b.get_slaves(): - b.del_port(intf) - - # Bonding policy/mode - b.set_mode(bond['mode']) - - # Add (enslave) interfaces to bond - for intf in bond['member']: - # if we've come here we already verified the interface doesn't - # have addresses configured so just flush any remaining ones - cmd(f'ip addr flush dev "{intf}"') - b.add_port(intf) - - # As the bond interface is always disabled first when changing - # parameters we will only re-enable the interface if it is not - # administratively disabled - if not bond['disable']: - b.set_admin_state('up') - else: - b.set_admin_state('down') - - # Configure interface address(es) - # - not longer required addresses get removed first - # - newly addresses will be added second - for addr in bond['address_remove']: - b.del_addr(addr) - for addr in bond['address']: - b.add_addr(addr) - - # assign/remove VRF (ONLY when not a member of a bridge, - # otherwise 'nomaster' removes it from it) - if not bond['is_bridge_member']: - b.set_vrf(bond['vrf']) - - # re-add ourselves to any bridge we might have fallen out of - if bond['is_bridge_member']: - b.add_to_bridge(bond['is_bridge_member']) - - # apply all vlans to interface - apply_all_vlans(b, bond) + b.update(bond) return None -- cgit v1.2.3 From 79af6c7b35164d3313c39dff2bc1bffbb4b326cd Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 24 Jul 2020 22:00:36 +0200 Subject: wireless: ifconfig: T2653: move to get_config_dict() The current VyOS CLI parser code written in Python contains a ton of duplicates which I can also hold myself accountable for - or maybe mainly me - depends on the angle of judge. --- data/templates/wifi/cfg80211.conf.tmpl | 4 +- data/templates/wifi/crda.tmpl | 4 +- data/templates/wifi/hostapd.conf.tmpl | 424 ++++++-------- data/templates/wifi/wpa_supplicant.conf.tmpl | 4 +- interface-definitions/interfaces-wireless.xml.in | 15 +- src/conf_mode/interfaces-wireless.py | 686 +++++------------------ 6 files changed, 337 insertions(+), 800 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/wifi/cfg80211.conf.tmpl b/data/templates/wifi/cfg80211.conf.tmpl index b21bacc1e..91df57aab 100644 --- a/data/templates/wifi/cfg80211.conf.tmpl +++ b/data/templates/wifi/cfg80211.conf.tmpl @@ -1,3 +1 @@ -{%- if regdom -%} -options cfg80211 ieee80211_regdom={{ regdom }} -{% endif %} +{{ 'options cfg80211 ieee80211_regdom=' + regdom if regdom is defined }} diff --git a/data/templates/wifi/crda.tmpl b/data/templates/wifi/crda.tmpl index 750ad86ee..6cd125e37 100644 --- a/data/templates/wifi/crda.tmpl +++ b/data/templates/wifi/crda.tmpl @@ -1,3 +1 @@ -{%- if regdom -%} -REGDOMAIN={{ regdom }} -{% endif %} +{{ 'REGDOMAIN=' + regdom if regdom is defined }} diff --git a/data/templates/wifi/hostapd.conf.tmpl b/data/templates/wifi/hostapd.conf.tmpl index d6068e4db..765668c57 100644 --- a/data/templates/wifi/hostapd.conf.tmpl +++ b/data/templates/wifi/hostapd.conf.tmpl @@ -9,7 +9,7 @@ device_name={{ description | truncate(32, True) }} # management frames with the Host AP driver); wlan0 with many nl80211 drivers # Note: This attribute can be overridden by the values supplied with the '-i' # command line parameter. -interface={{ intf }} +interface={{ ifname }} # Driver interface type (hostap/wired/none/nl80211/bsd); # default: hostap). nl80211 is used with all Linux mac80211 drivers. @@ -28,8 +28,7 @@ logger_syslog_level=0 logger_stdout=-1 logger_stdout_level=0 -{%- if country_code %} - +{% if country_code %} # Country code (ISO/IEC 3166-1). Used to set regulatory domain. # Set as needed to indicate country in which device is operating. # This can limit available channels and transmit power. @@ -42,14 +41,12 @@ country_code={{ country_code }} ieee80211d=1 {% endif %} -{%- if ssid %} - +{% if ssid %} # SSID to be used in IEEE 802.11 management frames ssid={{ ssid }} {% endif %} -{%- if channel %} - +{% if channel %} # Channel number (IEEE 802.11) # (default: 0, i.e., not set) # Please note that some drivers do not use this value from hostapd and the @@ -61,8 +58,7 @@ ssid={{ ssid }} channel={{ channel }} {% endif %} -{%- if mode %} - +{% if mode %} # Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz), # g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used # with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this @@ -71,29 +67,30 @@ channel={{ channel }} # special value "any" can be used to indicate that any support band can be used. # This special case is currently supported only with drivers with which # offloaded ACS is used. -{% if 'n' in mode -%} +{% if 'n' in mode %} hw_mode=g -{% elif 'ac' in mode -%} +{% elif 'ac' in mode %} hw_mode=a ieee80211h=1 ieee80211ac=1 -{% else -%} +{% else %} hw_mode={{ mode }} -{% endif %} +{% endif %} {% endif %} # ieee80211w: Whether management frame protection (MFP) is enabled # 0 = disabled (default) # 1 = optional # 2 = required -{% if 'disabled' in mgmt_frame_protection -%} +{% if 'disabled' in mgmt_frame_protection %} ieee80211w=0 -{% elif 'optional' in mgmt_frame_protection -%} +{% elif 'optional' in mgmt_frame_protection %} ieee80211w=1 -{% elif 'required' in mgmt_frame_protection -%} +{% elif 'required' in mgmt_frame_protection %} ieee80211w=2 {% endif %} +{% if capabilities is defined and capabilities.ht is defined %} # ht_capab: HT capabilities (list of flags) # LDPC coding capability: [LDPC] = supported # Supported channel width set: [HT40-] = both 20 MHz and 40 MHz with secondary @@ -127,79 +124,50 @@ ieee80211w=2 # DSSS/CCK Mode in 40 MHz: [DSSS_CCK-40] = allowed (not allowed if not set) # 40 MHz intolerant [40-INTOLERANT] (not advertised if not set) # L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set) -{% if cap_ht %} -ht_capab= -{%- endif -%} - -{%- if cap_ht_40mhz_incapable -%} -[40-INTOLERANT] -{%- endif -%} - -{%- if cap_ht_delayed_block_ack -%} -[DELAYED-BA] -{%- endif -%} - -{%- if cap_ht_dsss_cck_40 -%} -[DSSS_CCK-40] -{%- endif -%} - -{%- if cap_ht_greenfield -%} -[GF] -{%- endif -%} - -{%- if cap_ht_ldpc -%} -[LDPC] -{%- endif -%} - -{%- if cap_ht_lsig_protection -%} -[LSIG-TXOP-PROT] -{%- endif -%} - -{%- if cap_ht_max_amsdu -%} -[MAX-AMSDU-{{ cap_ht_max_amsdu }}] -{%- endif -%} - -{%- if cap_ht_smps -%} -[SMPS-{{ cap_ht_smps | upper }}] -{%- endif -%} - -{%- if cap_ht_chan_set_width -%} -{%- for csw in cap_ht_chan_set_width -%} -[{{ csw | upper }}] -{%- endfor -%} -{%- endif -%} - -{%- if cap_ht_short_gi -%} -{%- for gi in cap_ht_short_gi -%} -[SHORT-GI-{{ gi }}] -{%- endfor -%} -{%- endif -%} - -{%- if cap_ht_stbc_tx -%} -[TX-STBC] -{%- endif -%} -{%- if cap_ht_stbc_rx -%} -[RX-STBC{{ cap_ht_stbc_rx }}] -{%- endif %} +{% set output = '' %} +{% set output = output + '[40-INTOLERANT]' if capabilities.ht.fourtymhz_incapable is defined else '' %} +{% set output = output + '[DELAYED-BA]' if capabilities.ht.delayed_block_ack is defined else '' %} +{% set output = output + '[DSSS_CCK-40]' if capabilities.ht.dsss_cck_40 is defined else '' %} +{% set output = output + '[GF]' if capabilities.ht.greenfield is defined else '' %} +{% set output = output + '[LDPC]' if capabilities.ht.ldpc is defined else '' %} +{% set output = output + '[LSIG-TXOP-PROT]' if capabilities.ht.lsig_protection is defined else '' %} +{% set output = output + '[TX-STBC]' if capabilities.ht.stbc.tx is defined else '' %} +{% set output = output + '[RX-STBC-' + capabilities.ht.stbc.rx | upper + ']' if capabilities.ht.stbc.tx is defined else '' %} +{% set output = output + '[MAX-AMSDU-' + capabilities.ht.max_amsdu + ']' if capabilities.ht.max_amsdu is defined else '' %} +{% set output = output + '[SMPS-' + capabilities.ht.smps | upper + ']' if capabilities.ht.smps is defined else '' %} + +{% if capabilities.ht.channel_set_width is defined %} +{% for csw in capabilities.ht.channel_set_width %} +{% set output = output + '[' + csw | upper + ']' %} +{% endfor %} +{% endif %} -# Required for full HT and VHT functionality -wme_enabled=1 +{% if capabilities.ht.short_gi is defined %} +{% for short_gi in capabilities.ht.short_gi %} +{% set output = output + '[SHORT-GI-' + short_gi | upper + ']' %} +{% endfor %} +{% endif %} -{% if cap_ht_powersave -%} +ht_capab={{ output }} + +{% if capabilities.ht.auto_powersave is defined %} # WMM-PS Unscheduled Automatic Power Save Delivery [U-APSD] # Enable this flag if U-APSD supported outside hostapd (eg., Firmware/driver) uapsd_advertisement_enabled=1 -{%- endif %} +{% endif %} + +{% endif %} + +# Required for full HT and VHT functionality +wme_enabled=1 -{% if cap_req_ht -%} + +{% if capabilities is defined and capabilities.require_ht is defined %} # Require stations to support HT PHY (reject association if they do not) require_ht=1 {% endif %} -{%- if cap_vht_chan_set_width -%} -vht_oper_chwidth={{ cap_vht_chan_set_width }} -{%- endif %} - +{% if capabilities is defined and capabilities.vht is defined %} # vht_capab: VHT capabilities (list of flags) # # vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454] @@ -316,133 +284,95 @@ vht_oper_chwidth={{ cap_vht_chan_set_width }} # Tx Antenna Pattern Consistency: [TX-ANTENNA-PATTERN] # Indicates the possibility of Tx antenna pattern change # 0 = Tx antenna pattern might change during the lifetime of an association -# 1 = Tx antenna pattern does not change during the lifetime of an association -{% if cap_vht %} -vht_capab= -{%- endif -%} - -{%- if cap_vht_max_mpdu -%} -[MAX-MPDU-{{ cap_vht_max_mpdu }}] -{%- endif -%} - -{%- if cap_vht_max_mpdu_exp -%} -[MAX-A-MPDU-LEN-EXP{{ cap_vht_max_mpdu_exp }}] -{%- endif -%} - -{%- if cap_vht_chan_set_width -%} -{%- if '2' in cap_vht_chan_set_width -%} -[VHT160] -{%- elif '3' in cap_vht_chan_set_width -%} -[VHT160-80PLUS80] -{%- endif -%} -{%- endif -%} - -{%- if cap_vht_stbc_tx -%} -[TX-STBC-2BY1] -{%- endif -%} - -{%- if cap_vht_stbc_rx -%} -[RX-STBC-{{ cap_vht_stbc_rx }}] -{%- endif -%} - -{%- if cap_vht_link_adaptation -%} -{%- if 'unsolicited' in cap_vht_link_adaptation -%} -[VHT-LINK-ADAPT2] -{%- elif 'both' in cap_vht_link_adaptation -%} -[VHT-LINK-ADAPT3] -{%- endif -%} -{%- endif -%} - -{%- if cap_vht_short_gi -%} -{%- for gi in cap_vht_short_gi -%} -[SHORT-GI-{{ gi }}] -{%- endfor -%} -{%- endif -%} - -{%- if cap_vht_ldpc -%} -[RXLDPC] -{%- endif -%} - -{%- if cap_vht_tx_powersave -%} -[VHT-TXOP-PS] -{%- endif -%} - -{%- if cap_vht_vht_cf -%} -[HTC-VHT] -{%- endif -%} - -{%- if cap_vht_beamform -%} -{%- for beamform in cap_vht_beamform -%} -{%- if 'single-user-beamformer' in beamform -%} -[SU-BEAMFORMER] -{%- elif 'single-user-beamformee' in beamform -%} -[SU-BEAMFORMEE] -{%- elif 'multi-user-beamformer' in beamform -%} -[MU-BEAMFORMER] -{%- elif 'multi-user-beamformee' in beamform -%} -[MU-BEAMFORMEE] -{%- endif -%} -{%- endfor -%} -{%- endif -%} - -{%- if cap_vht_antenna_fixed -%} -[RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN] -{%- endif -%} - -{%- if cap_vht_antenna_cnt -%} -{%- if cap_vht_antenna_cnt|int > 1 -%} -{%- if cap_vht_beamform -%} -{%- for beamform in cap_vht_beamform -%} -{%- if 'single-user-beamformer' in beamform -%} -{%- if cap_vht_antenna_cnt|int < 6 -%} -[BF-ANTENNA-{{ cap_vht_antenna_cnt|int -1 }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt|int -1}}] -{%- endif -%} -{%- else -%} -{%- if cap_vht_antenna_cnt|int < 5 -%} -[BF-ANTENNA-{{ cap_vht_antenna_cnt }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt }}] -{%- endif -%} -{%- endif -%} -{%- endfor -%} -{%- else -%} -{%- if cap_vht_antenna_cnt|int < 5 -%} -[BF-ANTENNA-{{ cap_vht_antenna_cnt }}][SOUNDING-DIMENSION-{{ cap_vht_antenna_cnt }}] -{%- endif -%} -{%- endif -%} -{%- endif -%} -{%- endif %} +# 1 = Tx antenna pattern does not change during the lifetime of an + +{% if capabilities.vht.center_channel_freq.freq_1 is defined %} +# center freq = 5 GHz + (5 * index) +# So index 42 gives center freq 5.210 GHz +# which is channel 42 in 5G band +vht_oper_centr_freq_seg0_idx={{ capabilities.vht.center_channel_freq.freq_1 }} +{% endif %} + +{% if capabilities.vht.center_channel_freq.freq_2 is defined %} +# center freq = 5 GHz + (5 * index) +# So index 159 gives center freq 5.795 GHz +# which is channel 159 in 5G band +vht_oper_centr_freq_seg1_idx={{ capabilities.vht.center_channel_freq.freq_2 }} +{% endif %} + +{% if capabilities.vht.channel_set_width is defined %} +vht_oper_chwidth={{ capabilities.vht.channel_set_width }} +{% endif %} + +{% set output = '' %} +{% set output = output + '[TX-STBC-2BY1]' if capabilities.vht.stbc.tx is defined else '' %} +{% set output = output + '[RXLDPC]' if capabilities.vht.ldpc is defined else '' %} +{% set output = output + '[VHT-TXOP-PS]' if capabilities.vht.tx_powersave is defined else '' %} +{% set output = output + '[HTC-VHT]' if capabilities.vht.vht_cf is defined else '' %} +{% set output = output + '[RX-ANTENNA-PATTERN]' if capabilities.vht.antenna_pattern_fixed is defined else '' %} +{% set output = output + '[TX-ANTENNA-PATTERN]' if capabilities.vht.antenna_pattern_fixed is defined else '' %} + +{% set output = output + '[RX-STBC-' + capabilities.vht.stbc.rx + ']' if capabilities.vht.stbc.rx is defined else '' %} +{% set output = output + '[MAX-MPDU-' + capabilities.vht.max_mpdu + ']' if capabilities.vht.max_mpdu is defined else '' %} +{% set output = output + '[MAX-A-MPDU-LEN-EXP-' + capabilities.vht.max_mpdu_exp + ']' if capabilities.vht.max_mpdu_exp is defined else '' %} +{% set output = output + '[MAX-A-MPDU-LEN-EXP-' + capabilities.vht.max_mpdu_exp + ']' if capabilities.vht.max_mpdu_exp is defined else '' %} + +{% set output = output + '[VHT160]' if capabilities.vht.max_mpdu_exp is defined and capabilities.vht.max_mpdu_exp == '2' else '' %} +{% set output = output + '[VHT160-80PLUS80]' if capabilities.vht.max_mpdu_exp is defined and capabilities.vht.max_mpdu_exp == '3' else '' %} +{% set output = output + '[VHT-LINK-ADAPT2]' if capabilities.vht.link_adaptation is defined and capabilities.vht.link_adaptation == 'unsolicited' else '' %} +{% set output = output + '[VHT-LINK-ADAPT3]' if capabilities.vht.link_adaptation is defined and capabilities.vht.link_adaptation == 'both' else '' %} + +{% if capabilities.vht.short_gi is defined %} +{% for short_gi in capabilities.vht.short_gi %} +{% set output = output + '[SHORT-GI-' + short_gi | upper + ']' %} +{% endfor %} +{% endif %} + +{% if capabilities.vht.beamform %} +{% for beamform in capabilities.vht.beamform %} +{% set output = output + '[SU-BEAMFORMER]' if beamform == 'single-user-beamformer' else '' %} +{% set output = output + '[SU-BEAMFORMEE]' if beamform == 'single-user-beamformee' else '' %} +{% set output = output + '[MU-BEAMFORMER]' if beamform == 'multi-user-beamformer' else '' %} +{% set output = output + '[MU-BEAMFORMEE]' if beamform == 'multi-user-beamformee' else '' %} +{% endfor %} +{% endif %} + +{% if capabilities.vht.antenna_count is defined and capabilities.vht.antenna_count|int > 1 %} +{% if capabilities.vht.beamform %} +{% if beamform == 'single-user-beamformer' %} +{% if capabilities.vht.antenna_count is defined and capabilities.vht.antenna_count|int > 1 and capabilities.vht.antenna_count|int < 6 %} +{% set output = output + '[BF-ANTENNA-' + capabilities.vht.antenna_count|int -1 + ']' %} +{% set output = output + '[SOUNDING-DIMENSION-' + capabilities.vht.antenna_count|int -1 + ']' %} +{% endif %} +{% endif %} +{% if capabilities.vht.antenna_count is defined and capabilities.vht.antenna_count|int > 1 and capabilities.vht.antenna_count|int < 5 %} +{% set output = output + '[BF-ANTENNA-' + capabilities.vht.antenna_count + ']' %} +{% set output = output + '[SOUNDING-DIMENSION-' + capabilities.vht.antenna_count+ ']' %} +{% endif %} +{% endif %} +{% endif %} + +vht_capab={{ output }} +{% endif %} # ieee80211n: Whether IEEE 802.11n (HT) is enabled # 0 = disabled (default) # 1 = enabled # Note: You will also need to enable WMM for full HT functionality. # Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band. -{% if cap_req_vht -%} +{% if capabilities is defined and capabilities.require_vht is defined %} ieee80211n=0 # Require stations to support VHT PHY (reject association if they do not) require_vht=1 -{% else -%} -{% if 'n' in mode or 'ac' in mode -%} +{% else %} +{% if 'n' in mode or 'ac' in mode %} ieee80211n=1 -{% else -%} +{% else %} ieee80211n=0 -{%- endif %} +{% endif %} {% endif %} -{% if cap_vht_center_freq_1 -%} -# center freq = 5 GHz + (5 * index) -# So index 42 gives center freq 5.210 GHz -# which is channel 42 in 5G band -vht_oper_centr_freq_seg0_idx={{ cap_vht_center_freq_1 }} -{% endif %} - -{% if cap_vht_center_freq_2 -%} -# center freq = 5 GHz + (5 * index) -# So index 159 gives center freq 5.795 GHz -# which is channel 159 in 5G band -vht_oper_centr_freq_seg1_idx={{ cap_vht_center_freq_2 }} -{% endif %} - -{% if disable_broadcast_ssid -%} +{% if disable_broadcast_ssid is defined %} # Send empty SSID in beacons and ignore probe request frames that do not # specify full SSID, i.e., require stations to know SSID. # default: disabled (0) @@ -463,7 +393,7 @@ ignore_broadcast_ssid=1 # 2 = use external RADIUS server (accept/deny lists are searched first) macaddr_acl=0 -{% if max_stations -%} +{% if max_stations is defined %} # Maximum number of stations allowed in station table. New stations will be # rejected after the station table is full. IEEE 802.11 has a limit of 2007 # different association IDs, so this number should not be larger than that. @@ -471,13 +401,13 @@ macaddr_acl=0 max_num_sta={{ max_stations }} {% endif %} -{% if isolate_stations -%} +{% if isolate_stations is defined %} # Client isolation can be used to prevent low-level bridging of frames between # associated stations in the BSS. By default, this bridging is allowed. ap_isolate=1 {% endif %} -{% if reduce_transmit_power -%} +{% if reduce_transmit_power is defined %} # Add Power Constraint element to Beacon and Probe Response frames # This config option adds Power Constraint element when applicable and Country # element is added. Power Constraint element is required by Transmit Power @@ -486,14 +416,15 @@ ap_isolate=1 local_pwr_constraint={{ reduce_transmit_power }} {% endif %} -{% if expunge_failing_stations -%} +{% if expunge_failing_stations is defined %} # Disassociate stations based on excessive transmission failures or other # indications of connection loss. This depends on the driver capabilities and # may not be available with all drivers. disassoc_low_ack=1 {% endif %} -{% if sec_wep -%} + +{% if security is defined and security.wep is defined %} # IEEE 802.11 specifies two authentication algorithms. hostapd can be # configured to allow both of these or only one. Open system authentication # should be used with IEEE 802.1X. @@ -522,13 +453,14 @@ wep_default_key=0 # digits, depending on whether 40-bit (64-bit), 104-bit (128-bit), or # 128-bit (152-bit) WEP is used. # Only the default key must be supplied; the others are optional. -{% if sec_wep_key -%} -{% for key in sec_wep_key -%} -wep_key{{ loop.index -1 }}={{ key}} -{% endfor %} -{%- endif %} +{% if security.wep.key is defined %} +{% for key in sec_wep_key %} +wep_key{{ loop.index -1 }}={{ security.wep.key }} +{% endfor %} +{% endif %} -{% elif sec_wpa -%} + +{% elif security is defined and security.wpa is defined %} ##### WPA/IEEE 802.11i configuration ########################################## # Enable WPA. Setting this variable configures the AP to require WPA (either @@ -542,15 +474,17 @@ wep_key{{ loop.index -1 }}={{ key}} # and/or WPA2 (full IEEE 802.11i/RSN): # bit0 = WPA # bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) -{% if 'both' in sec_wpa_mode -%} +{% if security.wpa.mode is defined %} +{% if security.wpa.mode == 'both' %} wpa=3 -{%- elif 'wpa2' in sec_wpa_mode -%} +{% elif security.wpa.mode == 'wpa2' %} wpa=2 -{%- elif 'wpa' in sec_wpa_mode -%} +{% elif security.wpa.mode == 'wpa' %} wpa=1 -{%- endif %} +{% endif %} +{% endif %} -{% if sec_wpa_cipher -%} +{% if security.wpa.cipher is defined %} # Set of accepted cipher suites (encryption algorithms) for pairwise keys # (unicast packets). This is a space separated list of algorithms: # CCMP = AES in Counter mode with CBC-MAC (CCMP-128) @@ -563,26 +497,39 @@ wpa=1 # allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, # TKIP will be used as the group cipher. The optional group_cipher parameter can # be used to override this automatic selection. -{% if 'wpa2' in sec_wpa_mode -%} + +{% if security.wpa.mode is defined and security.wpa.mode == 'wpa2' %} # Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value) -rsn_pairwise={{ sec_wpa_cipher | join(" ") }} -{% else -%} +{% if security.wpa.cipher is string %} +rsn_pairwise={{ security.wpa.cipher }} +{% else %} +rsn_pairwise={{ security.wpa.cipher | join(" ") }} +{% endif %} +{% else %} # Pairwise cipher for WPA (v1) (default: TKIP) -wpa_pairwise={{ sec_wpa_cipher | join(" ") }} -{%- endif -%} -{% endif %} - -{% if sec_wpa_group_cipher -%} +{% if security.wpa.cipher is string %} +wpa_pairwise={{ security.wpa.cipher }} +{% else %} +wpa_pairwise={{ security.wpa.cipher | join(" ") }} +{% endif %} +{% endif %} +{% endif %} + +{% if security.wpa.group_cipher is defined %} # Optional override for automatic group cipher selection # This can be used to select a specific group cipher regardless of which # pairwise ciphers were enabled for WPA and RSN. It should be noted that # overriding the group cipher with an unexpected value can result in # interoperability issues and in general, this parameter is mainly used for # testing purposes. -group_cipher={{ sec_wpa_group_cipher | join(" ") }} -{% endif %} - -{% if sec_wpa_passphrase -%} +{% if security.wpa.group_cipher is string %} +group_cipher={{ security.wpa.group_cipher }} +{% else %} +group_cipher={{ security.wpa.group_cipher | join(" ") }} +{% endif %} +{% endif %} + +{% if security.wpa.passphrase is defined %} # IEEE 802.11 specifies two authentication algorithms. hostapd can be # configured to allow both of these or only one. Open system authentication # should be used with IEEE 802.1X. @@ -595,7 +542,7 @@ auth_algs=1 # secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase # (8..63 characters) that will be converted to PSK. This conversion uses SSID # so the PSK changes when ASCII passphrase is used and the SSID is changed. -wpa_passphrase={{ sec_wpa_passphrase }} +wpa_passphrase={{ security.wpa.passphrase }} # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The # entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be @@ -604,7 +551,7 @@ wpa_passphrase={{ sec_wpa_passphrase }} # WPA-PSK-SHA256 = WPA2-Personal using SHA256 wpa_key_mgmt=WPA-PSK -{% elif sec_wpa_radius -%} +{% elif security.wpa.radius is defined %} ##### IEEE 802.1X-2004 related configuration ################################## # Require IEEE 802.1X authorization ieee8021x=1 @@ -616,40 +563,37 @@ ieee8021x=1 # WPA-EAP-SHA256 = WPA2-Enterprise using SHA256 wpa_key_mgmt=WPA-EAP -{% if sec_wpa_radius_source -%} +{% if security.wpa.radius.server is defined %} # RADIUS client forced local IP address for the access point # Normally the local IP address is determined automatically based on configured # IP addresses, but this field can be used to force a specific address to be # used, e.g., when the device has multiple IP addresses. -radius_client_addr={{ sec_wpa_radius_source }} - -# The own IP address of the access point (used as NAS-IP-Address) -own_ip_addr={{ sec_wpa_radius_source }} -{% else %} # The own IP address of the access point (used as NAS-IP-Address) +{% if security.wpa.radius.source_address is defined %} +radius_client_addr={{ security.wpa.radius.source_address }} +own_ip_addr={{ security.wpa.radius.source_address }} +{% else %} own_ip_addr=127.0.0.1 -{% endif %} +{% endif %} -{% for radius in sec_wpa_radius -%} -{%- if not radius.disabled -%} +{% for radius in security.wpa.radius.server if not radius.disabled %} # RADIUS authentication server auth_server_addr={{ radius.server }} auth_server_port={{ radius.port }} auth_server_shared_secret={{ radius.key }} -{% if radius.acc_port -%} + +{% if radius.acc_port %} # RADIUS accounting server acct_server_addr={{ radius.server }} acct_server_port={{ radius.acc_port }} acct_server_shared_secret={{ radius.key }} -{% endif %} -{% endif %} -{% endfor %} - -{% endif %} - -{% else %} +{% endif %} +{% endfor %} +{% else %} # Open system auth_algs=1 +{% endif %} +{% endif %} {% endif %} # TX queue parameters (EDCF / bursting) diff --git a/data/templates/wifi/wpa_supplicant.conf.tmpl b/data/templates/wifi/wpa_supplicant.conf.tmpl index 2784883f1..9ddad35fd 100644 --- a/data/templates/wifi/wpa_supplicant.conf.tmpl +++ b/data/templates/wifi/wpa_supplicant.conf.tmpl @@ -1,8 +1,8 @@ # WPA supplicant config network={ ssid="{{ ssid }}" -{%- if sec_wpa_passphrase %} - psk="{{ sec_wpa_passphrase }}" +{% if security is defined and security.wpa is defined and security.wpa.passphrase is defined %} + psk="{{ security.wpa.passphrase }}" {% else %} key_mgmt=NONE {% endif %} diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in index 06c7734f5..6f0ec9e71 100644 --- a/interface-definitions/interfaces-wireless.xml.in +++ b/interface-definitions/interfaces-wireless.xml.in @@ -320,7 +320,7 @@ VHT link adaptation capabilities - unsolicited both + unsolicited both unsolicited @@ -451,6 +451,7 @@ Disable broadcast of SSID from access-point + #include @@ -551,9 +552,10 @@ 802.11ac - 1300 Mbits/sec - (a|b|g|n|ac) + ^(a|b|g|n|ac)$ + g @@ -637,7 +639,7 @@ Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] - (GCMP-256|GCMP|CCMP-256|CCMP|TKIP) + ^(GCMP-256|GCMP|CCMP-256|CCMP|TKIP)$ Invalid cipher selection @@ -670,7 +672,7 @@ Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] - (GCMP-256|GCMP|CCMP-256|CCMP|TKIP) + ^(GCMP-256|GCMP|CCMP-256|CCMP|TKIP)$ Invalid group cipher selection @@ -695,7 +697,7 @@ Allow both WPA and WPA2 - (wpa|wpa2|both) + ^(wpa|wpa2|both)$ Unknown WPA mode @@ -762,10 +764,11 @@ Passively monitor all packets on the frequency/channel - (access-point|station|monitor) + ^(access-point|station|monitor)$ Type must be access-point, station or monitor + monitor #include #include diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py index 0162b642c..42b55ee6a 100755 --- a/src/conf_mode/interfaces-wireless.py +++ b/src/conf_mode/interfaces-wireless.py @@ -15,497 +15,169 @@ # along with this program. If not, see . import os + from sys import exit from re import findall - from copy import deepcopy - -from netifaces import interfaces from netaddr import EUI, mac_unix_expanded from vyos.config import Config -from vyos.configdict import list_diff, intf_to_dict, add_to_dict, interface_default_data -from vyos.ifconfig import WiFiIf, Section -from vyos.ifconfig_vlan import apply_all_vlans, verify_vlan_config +from vyos.configdict import get_interface_dict +from vyos.configdict import dict_merge +from vyos.configverify import verify_address +from vyos.configverify import verify_bridge_delete +from vyos.configverify import verify_dhcpv6 +from vyos.configverify import verify_source_interface +from vyos.configverify import verify_vlan_config +from vyos.configverify import verify_vrf +from vyos.ifconfig import WiFiIf from vyos.template import render -from vyos.util import chown, call -from vyos.validate import is_member +from vyos.util import call from vyos import ConfigError - from vyos import airbag airbag.enable() -default_config_data = { - **interface_default_data, - 'cap_ht' : False, - 'cap_ht_40mhz_incapable' : False, - 'cap_ht_powersave' : False, - 'cap_ht_chan_set_width' : '', - 'cap_ht_delayed_block_ack' : False, - 'cap_ht_dsss_cck_40' : False, - 'cap_ht_greenfield' : False, - 'cap_ht_ldpc' : False, - 'cap_ht_lsig_protection' : False, - 'cap_ht_max_amsdu' : '', - 'cap_ht_short_gi' : [], - 'cap_ht_smps' : '', - 'cap_ht_stbc_rx' : '', - 'cap_ht_stbc_tx' : False, - 'cap_req_ht' : False, - 'cap_req_vht' : False, - 'cap_vht' : False, - 'cap_vht_antenna_cnt' : '', - 'cap_vht_antenna_fixed' : False, - 'cap_vht_beamform' : '', - 'cap_vht_center_freq_1' : '', - 'cap_vht_center_freq_2' : '', - 'cap_vht_chan_set_width' : '', - 'cap_vht_ldpc' : False, - 'cap_vht_link_adaptation' : '', - 'cap_vht_max_mpdu_exp' : '', - 'cap_vht_max_mpdu' : '', - 'cap_vht_short_gi' : [], - 'cap_vht_stbc_rx' : '', - 'cap_vht_stbc_tx' : False, - 'cap_vht_tx_powersave' : False, - 'cap_vht_vht_cf' : False, - 'channel': '', - 'country_code': '', - 'deleted': False, - 'disable_broadcast_ssid' : False, - 'disable_link_detect' : 1, - 'expunge_failing_stations' : False, - 'hw_id' : '', - 'intf': '', - 'isolate_stations' : False, - 'max_stations' : '', - 'mgmt_frame_protection' : 'disabled', - 'mode' : 'g', - 'phy' : '', - 'reduce_transmit_power' : '', - 'sec_wep' : False, - 'sec_wep_key' : [], - 'sec_wpa' : False, - 'sec_wpa_cipher' : [], - 'sec_wpa_mode' : 'both', - 'sec_wpa_passphrase' : '', - 'sec_wpa_radius' : [], - 'ssid' : '', - 'op_mode' : 'monitor', - 'vif': {}, - 'vif_remove': [], - 'vif_s': {}, - 'vif_s_remove': [] -} - # XXX: wpa_supplicant works on the source interface -wpa_suppl_conf = '/run/wpa_supplicant/{intf}.conf' -hostapd_conf = '/run/hostapd/{intf}.conf' +wpa_suppl_conf = '/run/wpa_supplicant/{ifname}.conf' +hostapd_conf = '/run/hostapd/{ifname}.conf' + +def find_other_stations(conf, base, ifname): + """ + Only one wireless interface per phy can be in station mode - + find all interfaces attached to a phy which run in station mode + """ + old_level = conf.get_level() + conf.set_level(base) + dict = {} + for phy in os.listdir('/sys/class/ieee80211'): + list = [] + for interface in conf.list_nodes([]): + if interface == ifname: + continue + # the following node is mandatory + if conf.exists([interface, 'physical-device', phy]): + tmp = conf.return_value([interface, 'type']) + if tmp == 'station': + list.append(interface) + if list: + dict.update({phy: list}) + conf.set_level(old_level) + return dict def get_config(): + """ + Retrive CLI config as dictionary. Dictionary can never be empty, as at least the + interface name will be added or a deleted flag + """ + conf = Config() + base = ['interfaces', 'wireless'] + # determine tagNode instance if 'VYOS_TAGNODE_VALUE' not in os.environ: raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') ifname = os.environ['VYOS_TAGNODE_VALUE'] - conf = Config() - - # check if wireless interface has been removed - cfg_base = ['interfaces', 'wireless ', ifname] - if not conf.exists(cfg_base): - wifi = deepcopy(default_config_data) - wifi['intf'] = ifname - wifi['deleted'] = True - # we need to know if we're a bridge member so we can refuse deletion - wifi['is_bridge_member'] = is_member(conf, wifi['intf'], 'bridge') - # we can not bail out early as wireless interface can not be removed - # Kernel will complain with: RTNETLINK answers: Operation not supported. - # Thus we need to remove individual settings - return wifi - - # set new configuration level - conf.set_level(cfg_base) - - # get common interface settings - wifi, disabled = intf_to_dict(conf, default_config_data) - - # 40MHz intolerance, use 20MHz only - if conf.exists('capabilities ht 40mhz-incapable'): - wifi['cap_ht'] = True - wifi['cap_ht_40mhz_incapable'] = True - - # WMM-PS Unscheduled Automatic Power Save Delivery [U-APSD] - if conf.exists('capabilities ht auto-powersave'): - wifi['cap_ht'] = True - wifi['cap_ht_powersave'] = True - - # Supported channel set width - if conf.exists('capabilities ht channel-set-width'): - wifi['cap_ht'] = True - wifi['cap_ht_chan_set_width'] = conf.return_values('capabilities ht channel-set-width') - - # HT-delayed Block Ack - if conf.exists('capabilities ht delayed-block-ack'): - wifi['cap_ht'] = True - wifi['cap_ht_delayed_block_ack'] = True - - # DSSS/CCK Mode in 40 MHz - if conf.exists('capabilities ht dsss-cck-40'): - wifi['cap_ht'] = True - wifi['cap_ht_dsss_cck_40'] = True - - # HT-greenfield capability - if conf.exists('capabilities ht greenfield'): - wifi['cap_ht'] = True - wifi['cap_ht_greenfield'] = True - - # LDPC coding capability - if conf.exists('capabilities ht ldpc'): - wifi['cap_ht'] = True - wifi['cap_ht_ldpc'] = True - - # L-SIG TXOP protection capability - if conf.exists('capabilities ht lsig-protection'): - wifi['cap_ht'] = True - wifi['cap_ht_lsig_protection'] = True - - # Set Maximum A-MSDU length - if conf.exists('capabilities ht max-amsdu'): - wifi['cap_ht'] = True - wifi['cap_ht_max_amsdu'] = conf.return_value('capabilities ht max-amsdu') - - # Short GI capabilities - if conf.exists('capabilities ht short-gi'): - wifi['cap_ht'] = True - wifi['cap_ht_short_gi'] = conf.return_values('capabilities ht short-gi') - - # Spatial Multiplexing Power Save (SMPS) settings - if conf.exists('capabilities ht smps'): - wifi['cap_ht'] = True - wifi['cap_ht_smps'] = conf.return_value('capabilities ht smps') - - # Support for receiving PPDU using STBC (Space Time Block Coding) - if conf.exists('capabilities ht stbc rx'): - wifi['cap_ht'] = True - wifi['cap_ht_stbc_rx'] = conf.return_value('capabilities ht stbc rx') - - # Support for sending PPDU using STBC (Space Time Block Coding) - if conf.exists('capabilities ht stbc tx'): - wifi['cap_ht'] = True - wifi['cap_ht_stbc_tx'] = True - - # Require stations to support HT PHY (reject association if they do not) - if conf.exists('capabilities require-ht'): - wifi['cap_req_ht'] = True - - # Require stations to support VHT PHY (reject association if they do not) - if conf.exists('capabilities require-vht'): - wifi['cap_req_vht'] = True - - # Number of antennas on this card - if conf.exists('capabilities vht antenna-count'): - wifi['cap_vht'] = True - wifi['cap_vht_antenna_cnt'] = conf.return_value('capabilities vht antenna-count') - - # set if antenna pattern does not change during the lifetime of an association - if conf.exists('capabilities vht antenna-pattern-fixed'): - wifi['cap_vht'] = True - wifi['cap_vht_antenna_fixed'] = True - - # Beamforming capabilities - if conf.exists('capabilities vht beamform'): - wifi['cap_vht'] = True - wifi['cap_vht_beamform'] = conf.return_values('capabilities vht beamform') - - # VHT operating channel center frequency - center freq 1 (for use with 80, 80+80 and 160 modes) - if conf.exists('capabilities vht center-channel-freq freq-1'): - wifi['cap_vht'] = True - wifi['cap_vht_center_freq_1'] = conf.return_value('capabilities vht center-channel-freq freq-1') - - # VHT operating channel center frequency - center freq 2 (for use with the 80+80 mode) - if conf.exists('capabilities vht center-channel-freq freq-2'): - wifi['cap_vht'] = True - wifi['cap_vht_center_freq_2'] = conf.return_value('capabilities vht center-channel-freq freq-2') - - # VHT operating Channel width - if conf.exists('capabilities vht channel-set-width'): - wifi['cap_vht'] = True - wifi['cap_vht_chan_set_width'] = conf.return_value('capabilities vht channel-set-width') - - # LDPC coding capability - if conf.exists('capabilities vht ldpc'): - wifi['cap_vht'] = True - wifi['cap_vht_ldpc'] = True - - # VHT link adaptation capabilities - if conf.exists('capabilities vht link-adaptation'): - wifi['cap_vht'] = True - wifi['cap_vht_link_adaptation'] = conf.return_value('capabilities vht link-adaptation') - - # Set the maximum length of A-MPDU pre-EOF padding that the station can receive - if conf.exists('capabilities vht max-mpdu-exp'): - wifi['cap_vht'] = True - wifi['cap_vht_max_mpdu_exp'] = conf.return_value('capabilities vht max-mpdu-exp') - - # Increase Maximum MPDU length - if conf.exists('capabilities vht max-mpdu'): - wifi['cap_vht'] = True - wifi['cap_vht_max_mpdu'] = conf.return_value('capabilities vht max-mpdu') - - # Increase Maximum MPDU length - if conf.exists('capabilities vht short-gi'): - wifi['cap_vht'] = True - wifi['cap_vht_short_gi'] = conf.return_values('capabilities vht short-gi') - - # Support for receiving PPDU using STBC (Space Time Block Coding) - if conf.exists('capabilities vht stbc rx'): - wifi['cap_vht'] = True - wifi['cap_vht_stbc_rx'] = conf.return_value('capabilities vht stbc rx') - - # Support for the transmission of at least 2x1 STBC (Space Time Block Coding) - if conf.exists('capabilities vht stbc tx'): - wifi['cap_vht'] = True - wifi['cap_vht_stbc_tx'] = True - - # Support for VHT TXOP Power Save Mode - if conf.exists('capabilities vht tx-powersave'): - wifi['cap_vht'] = True - wifi['cap_vht_tx_powersave'] = True - - # STA supports receiving a VHT variant HT Control field - if conf.exists('capabilities vht vht-cf'): - wifi['cap_vht'] = True - wifi['cap_vht_vht_cf'] = True - - # Wireless radio channel - if conf.exists('channel'): - wifi['channel'] = conf.return_value('channel') - - # Disable broadcast of SSID from access-point - if conf.exists('disable-broadcast-ssid'): - wifi['disable_broadcast_ssid'] = True - - # Disassociate stations based on excessive transmission failures - if conf.exists('expunge-failing-stations'): - wifi['expunge_failing_stations'] = True - - # retrieve real hardware address - if conf.exists('hw-id'): - wifi['hw_id'] = conf.return_value('hw-id') - - # Isolate stations on the AP so they cannot see each other - if conf.exists('isolate-stations'): - wifi['isolate_stations'] = True - - # Wireless physical device - if conf.exists('physical-device'): - wifi['phy'] = conf.return_value('physical-device') - - # Maximum number of wireless radio stations - if conf.exists('max-stations'): - wifi['max_stations'] = conf.return_value('max-stations') - - # Management Frame Protection (MFP) according to IEEE 802.11w - if conf.exists('mgmt-frame-protection'): - wifi['mgmt_frame_protection'] = conf.return_value('mgmt-frame-protection') - - # Wireless radio mode - if conf.exists('mode'): - wifi['mode'] = conf.return_value('mode') - - # Transmission power reduction in dBm - if conf.exists('reduce-transmit-power'): - wifi['reduce_transmit_power'] = conf.return_value('reduce-transmit-power') - - # WEP enabled? - if conf.exists('security wep'): - wifi['sec_wep'] = True - - # WEP encryption key(s) - if conf.exists('security wep key'): - wifi['sec_wep_key'] = conf.return_values('security wep key') - - # WPA enabled? - if conf.exists('security wpa'): - wifi['sec_wpa'] = True - - # WPA Cipher suite - if conf.exists('security wpa cipher'): - wifi['sec_wpa_cipher'] = conf.return_values('security wpa cipher') - - # WPA mode - if conf.exists('security wpa mode'): - wifi['sec_wpa_mode'] = conf.return_value('security wpa mode') - - # WPA default ciphers depend on WPA mode - if not wifi['sec_wpa_cipher']: - if wifi['sec_wpa_mode'] == 'wpa': - wifi['sec_wpa_cipher'].append('TKIP') - wifi['sec_wpa_cipher'].append('CCMP') - - elif wifi['sec_wpa_mode'] == 'wpa2': - wifi['sec_wpa_cipher'].append('CCMP') - - elif wifi['sec_wpa_mode'] == 'both': - wifi['sec_wpa_cipher'].append('CCMP') - wifi['sec_wpa_cipher'].append('TKIP') - - # WPA Group Cipher suite - if conf.exists('security wpa group-cipher'): - wifi['sec_wpa_group_cipher'] = conf.return_values('security wpa group-cipher') - - # WPA personal shared pass phrase - if conf.exists('security wpa passphrase'): - wifi['sec_wpa_passphrase'] = conf.return_value('security wpa passphrase') - - # WPA RADIUS source address - if conf.exists('security wpa radius source-address'): - wifi['sec_wpa_radius_source'] = conf.return_value('security wpa radius source-address') - - # WPA RADIUS server - for server in conf.list_nodes('security wpa radius server'): - # set new configuration level - conf.set_level(cfg_base + ' security wpa radius server ' + server) - radius = { - 'server' : server, - 'acc_port' : '', - 'disabled': False, - 'port' : 1812, - 'key' : '' - } - - # RADIUS server port - if conf.exists('port'): - radius['port'] = int(conf.return_value('port')) - - # receive RADIUS accounting info - if conf.exists('accounting'): - radius['acc_port'] = radius['port'] + 1 - - # Check if RADIUS server was temporary disabled - if conf.exists(['disable']): - radius['disabled'] = True - - # RADIUS server shared-secret - if conf.exists('key'): - radius['key'] = conf.return_value('key') - - # append RADIUS server to list of servers - wifi['sec_wpa_radius'].append(radius) - - # re-set configuration level to parse new nodes - conf.set_level(cfg_base) - - # Wireless access-point service set identifier (SSID) - if conf.exists('ssid'): - wifi['ssid'] = conf.return_value('ssid') - - # Wireless device type for this interface - if conf.exists('type'): - tmp = conf.return_value('type') - if tmp == 'access-point': - tmp = 'ap' - - wifi['op_mode'] = tmp + wifi = get_interface_dict(conf, base, ifname) + + if 'security' in wifi and 'wpa' in wifi['security']: + wpa_cipher = wifi['security']['wpa'].get('cipher') + wpa_mode = wifi['security']['wpa'].get('mode') + if not wpa_cipher: + tmp = None + if wpa_mode == 'wpa': + tmp = {'security': {'wpa': {'cipher' : ['TKIP', 'CCMP']}}} + elif wpa_mode == 'wpa2': + tmp = {'security': {'wpa': {'cipher' : ['CCMP']}}} + elif wpa_mode == 'both': + tmp = {'security': {'wpa': {'cipher' : ['CCMP', 'TKIP']}}} + + if tmp: wifi = dict_merge(tmp, wifi) # retrieve configured regulatory domain - conf.set_level('system') - if conf.exists('wifi-regulatory-domain'): - wifi['country_code'] = conf.return_value('wifi-regulatory-domain') + conf.set_level(['system']) + if conf.exists(['wifi-regulatory-domain']): + wifi['country_code'] = conf.return_value(['wifi-regulatory-domain']) - return wifi + # Only one wireless interface per phy can be in station mode + tmp = find_other_stations(conf, base, wifi['ifname']) + if tmp: wifi['station_interfaces'] = tmp + return wifi def verify(wifi): - if wifi['deleted']: - if wifi['is_bridge_member']: - raise ConfigError(( - f'Cannot delete interface "{wifi["intf"]}" as it is a ' - f'member of bridge "{wifi["is_bridge_member"]}"!')) - + if 'deleted' in wifi: + verify_bridge_delete(wifi) return None - if wifi['op_mode'] != 'monitor' and not wifi['ssid']: - raise ConfigError('SSID must be set for {}'.format(wifi['intf'])) - - if not wifi['phy']: - raise ConfigError('You must specify physical-device') + if 'physical_device' not in wifi: + raise ConfigError('You must specify a physical-device "phy"') - if not wifi['mode']: + if 'type' not in wifi: raise ConfigError('You must specify a WiFi mode') - if wifi['op_mode'] == 'ap': - c = Config() - if not c.exists('system wifi-regulatory-domain'): - raise ConfigError('Wireless regulatory domain is mandatory,\n' \ - 'use "set system wifi-regulatory-domain".') - - if not wifi['channel']: - raise ConfigError('Channel must be set for {}'.format(wifi['intf'])) + if 'ssid' not in wifi and wifi['type'] != 'monitor': + raise ConfigError('SSID must be configured') - if len(wifi['sec_wep_key']) > 4: - raise ConfigError('No more then 4 WEP keys configurable') - - if wifi['cap_vht'] and not wifi['cap_ht']: - raise ConfigError('Specify HT flags if you want to use VHT!') - - if wifi['cap_vht_beamform'] and wifi['cap_vht_antenna_cnt'] == 1: - raise ConfigError('Cannot use beam forming with just one antenna!') - - if wifi['cap_vht_beamform'] == 'single-user-beamformer' and wifi['cap_vht_antenna_cnt'] < 3: - # Nasty Gotcha: see https://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf lines 692-705 - raise ConfigError('Single-user beam former requires at least 3 antennas!') - - if wifi['sec_wep'] and (len(wifi['sec_wep_key']) == 0): - raise ConfigError('Missing WEP keys') - - if wifi['sec_wpa'] and not (wifi['sec_wpa_passphrase'] or wifi['sec_wpa_radius']): - raise ConfigError('Misssing WPA key or RADIUS server') - - for radius in wifi['sec_wpa_radius']: - if not radius['key']: - raise ConfigError('Misssing RADIUS shared secret key for server: {}'.format(radius['server'])) - - if ( wifi['is_bridge_member'] - and ( wifi['address'] - or wifi['ipv6_eui64_prefix'] - or wifi['ipv6_autoconf'] ) ): - raise ConfigError(( - f'Cannot assign address to interface "{wifi["intf"]}" ' - f'as it is a member of bridge "{wifi["is_bridge_member"]}"!')) - - if wifi['vrf']: - if wifi['vrf'] not in interfaces(): - raise ConfigError(f'VRF "{wifi["vrf"]}" does not exist') - - if wifi['is_bridge_member']: - raise ConfigError(( - f'Interface "{wifi["intf"]}" cannot be member of VRF ' - f'"{wifi["vrf"]}" and bridge {wifi["is_bridge_member"]} ' - f'at the same time!')) + if wifi['type'] == 'access-point': + if 'country_code' not in wifi: + raise ConfigError('Wireless regulatory domain is mandatory,\n' \ + 'use "set system wifi-regulatory-domain" for configuration.') + + if 'channel' not in wifi: + raise ConfigError('Wireless channel must be configured!') + + if 'security' in wifi: + if {'wep', 'wpa'} <= set(wifi.get('security', {})): + raise ConfigError('Must either use WEP or WPA security!') + + if 'wep' in wifi['security']: + if 'key' in wifi['security']['wep'] and len(wifi['security']['wep']) > 4: + raise ConfigError('No more then 4 WEP keys configurable') + elif 'key' not in wifi['security']['wep']: + raise ConfigError('Security WEP configured - missing WEP keys!') + + elif 'wpa' in wifi['security']: + wpa = wifi['security']['wpa'] + if not any(i in ['passphrase', 'radius'] for i in wpa): + raise ConfigError('Misssing WPA key or RADIUS server') + + if 'radius' in wpa: + if 'server' in wpa['radius']: + for server in wpa['radius']['server']: + if 'key' not in wpa['radius']['server'][server]: + raise ConfigError(f'Misssing RADIUS shared secret key for server: {server}') + + if 'capabilities' in wifi: + capabilities = wifi['capabilities'] + if 'vht' in capabilities: + if 'ht' not in capabilities: + raise ConfigError('Specify HT flags if you want to use VHT!') + + if {'beamform', 'antenna_count'} <= set(capabilities.get('vht', {})): + if capabilities['vht']['antenna_count'] == '1': + raise ConfigError('Cannot use beam forming with just one antenna!') + + if capabilities['vht']['beamform'] == 'single-user-beamformer': + if int(capabilities['vht']['antenna_count']) < 3: + # Nasty Gotcha: see https://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf lines 692-705 + raise ConfigError('Single-user beam former requires at least 3 antennas!') + + if 'station_interfaces' in wifi and wifi['type'] == 'station': + phy = wifi['physical_device'] + if phy in wifi['station_interfaces']: + if len(wifi['station_interfaces'][phy]) > 0: + raise ConfigError('Only one station per wireless physical interface possible!') + + verify_address(wifi) + verify_vrf(wifi) # use common function to verify VLAN configuration verify_vlan_config(wifi) - conf = Config() - # Only one wireless interface per phy can be in station mode - base = ['interfaces', 'wireless'] - for phy in os.listdir('/sys/class/ieee80211'): - stations = [] - for wlan in conf.list_nodes(base): - # the following node is mandatory - if conf.exists(base + [wlan, 'physical-device', phy]): - tmp = conf.return_value(base + [wlan, 'type']) - if tmp == 'station': - stations.append(wlan) - - if len(stations) > 1: - raise ConfigError('Only one station per wireless physical interface possible!') - return None def generate(wifi): - interface = wifi['intf'] + interface = wifi['ifname'] # always stop hostapd service first before reconfiguring it call(f'systemctl stop hostapd@{interface}.service') @@ -513,7 +185,7 @@ def generate(wifi): call(f'systemctl stop wpa_supplicant@{interface}.service') # Delete config files if interface is removed - if wifi['deleted']: + if 'deleted' in wifi: if os.path.isfile(hostapd_conf.format(**wifi)): os.unlink(hostapd_conf.format(**wifi)) @@ -522,10 +194,10 @@ def generate(wifi): return None - if not wifi['mac']: + if 'mac' not in wifi: # http://wiki.stocksy.co.uk/wiki/Multiple_SSIDs_with_hostapd # generate locally administered MAC address from used phy interface - with open('/sys/class/ieee80211/{}/addresses'.format(wifi['phy']), 'r') as f: + with open('/sys/class/ieee80211/{physical_device}/addresses'.format(**wifi), 'r') as f: # some PHYs tend to have multiple interfaces and thus supply multiple MAC # addresses - we only need the first one for our calculation tmp = f.readline().rstrip() @@ -545,20 +217,18 @@ def generate(wifi): wifi['mac'] = str(mac) # render appropriate new config files depending on access-point or station mode - if wifi['op_mode'] == 'ap': - render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.tmpl', wifi) + if wifi['type'] == 'access-point': + render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.tmpl', wifi, trim_blocks=True) - elif wifi['op_mode'] == 'station': - render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.tmpl', wifi) + elif wifi['type'] == 'station': + render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.tmpl', wifi, trim_blocks=True) return None def apply(wifi): - interface = wifi['intf'] - if wifi['deleted']: - w = WiFiIf(interface) - # delete interface - w.remove() + interface = wifi['ifname'] + if 'deleted' in wifi: + WiFiIf(interface).remove() else: # WiFi interface needs to be created on-block (e.g. mode or physical # interface) instead of passing a ton of arguments, I just use a dict @@ -566,97 +236,21 @@ def apply(wifi): conf = deepcopy(WiFiIf.get_config()) # Assign WiFi instance configuration parameters to config dict - conf['phy'] = wifi['phy'] + conf['phy'] = wifi['physical_device'] # Finally create the new interface w = WiFiIf(interface, **conf) - - # assign/remove VRF (ONLY when not a member of a bridge, - # otherwise 'nomaster' removes it from it) - if not wifi['is_bridge_member']: - w.set_vrf(wifi['vrf']) - - # update interface description used e.g. within SNMP - w.set_alias(wifi['description']) - - if wifi['dhcp_client_id']: - w.dhcp.v4.options['client_id'] = wifi['dhcp_client_id'] - - if wifi['dhcp_hostname']: - w.dhcp.v4.options['hostname'] = wifi['dhcp_hostname'] - - if wifi['dhcp_vendor_class_id']: - w.dhcp.v4.options['vendor_class_id'] = wifi['dhcp_vendor_class_id'] - - if wifi['dhcpv6_prm_only']: - w.dhcp.v6.options['dhcpv6_prm_only'] = True - - if wifi['dhcpv6_temporary']: - w.dhcp.v6.options['dhcpv6_temporary'] = True - - if wifi['dhcpv6_pd_length']: - w.dhcp.v6.options['dhcpv6_pd_length'] = wifi['dhcpv6_pd_length'] - - if wifi['dhcpv6_pd_interfaces']: - w.dhcp.v6.options['dhcpv6_pd_interfaces'] = wifi['dhcpv6_pd_interfaces'] - - # ignore link state changes - w.set_link_detect(wifi['disable_link_detect']) - - # Delete old IPv6 EUI64 addresses before changing MAC - for addr in wifi['ipv6_eui64_prefix_remove']: - w.del_ipv6_eui64_address(addr) - - # Change interface MAC address - re-set to real hardware address (hw-id) - # if custom mac is removed - if wifi['mac']: - w.set_mac(wifi['mac']) - elif wifi['hw_id']: - w.set_mac(wifi['hw_id']) - - # Add IPv6 EUI-based addresses - for addr in wifi['ipv6_eui64_prefix']: - w.add_ipv6_eui64_address(addr) - - # configure ARP filter configuration - w.set_arp_filter(wifi['ip_disable_arp_filter']) - # configure ARP accept - w.set_arp_accept(wifi['ip_enable_arp_accept']) - # configure ARP announce - w.set_arp_announce(wifi['ip_enable_arp_announce']) - # configure ARP ignore - w.set_arp_ignore(wifi['ip_enable_arp_ignore']) - # IPv6 accept RA - w.set_ipv6_accept_ra(wifi['ipv6_accept_ra']) - # IPv6 address autoconfiguration - w.set_ipv6_autoconf(wifi['ipv6_autoconf']) - # IPv6 forwarding - w.set_ipv6_forwarding(wifi['ipv6_forwarding']) - # IPv6 Duplicate Address Detection (DAD) tries - w.set_ipv6_dad_messages(wifi['ipv6_dup_addr_detect']) - - # Configure interface address(es) - # - not longer required addresses get removed first - # - newly addresses will be added second - for addr in wifi['address_remove']: - w.del_addr(addr) - for addr in wifi['address']: - w.add_addr(addr) - - # apply all vlans to interface - apply_all_vlans(w, wifi) + w.update(wifi) # Enable/Disable interface - interface is always placed in # administrative down state in WiFiIf class - if not wifi['disable']: - w.set_admin_state('up') - + if 'disable' not in wifi: # Physical interface is now configured. Proceed by starting hostapd or # wpa_supplicant daemon. When type is monitor we can just skip this. - if wifi['op_mode'] == 'ap': + if wifi['type'] == 'access-point': call(f'systemctl start hostapd@{interface}.service') - elif wifi['op_mode'] == 'station': + elif wifi['type'] == 'station': call(f'systemctl start wpa_supplicant@{interface}.service') return None -- cgit v1.2.3 From 675942ce3e2329a0122da189cd5944df08d7fcab Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 25 Jul 2020 23:52:12 +0200 Subject: l2tpv3: ifconfig: T2653: move implementation to get_interface_dict() The current VyOS CLI parser code written in Python contains a ton of duplicates which I can also hold myself accountable for - or maybe mainly me - depends on the angle of judge. --- interface-definitions/interfaces-l2tpv3.xml.in | 3 + src/conf_mode/interfaces-l2tpv3.py | 252 +++++-------------------- 2 files changed, 48 insertions(+), 207 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in index 30dd9b604..3a878ad76 100644 --- a/interface-definitions/interfaces-l2tpv3.xml.in +++ b/interface-definitions/interfaces-l2tpv3.xml.in @@ -29,6 +29,7 @@ + 5000 #include @@ -50,6 +51,7 @@ Encapsulation must be UDP or IP + udp @@ -138,6 +140,7 @@ + 5000 diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py index 866419f2c..0978df5b6 100755 --- a/src/conf_mode/interfaces-l2tpv3.py +++ b/src/conf_mode/interfaces-l2tpv3.py @@ -21,196 +21,65 @@ from copy import deepcopy from netifaces import interfaces from vyos.config import Config -from vyos.ifconfig import L2TPv3If, Interface -from vyos import ConfigError -from vyos.util import call +from vyos.configdict import get_interface_dict +from vyos.configdict import leaf_node_changed +from vyos.configverify import verify_address +from vyos.configverify import verify_bridge_delete +from vyos.ifconfig import L2TPv3If from vyos.util import check_kmod -from vyos.validate import is_member, is_addr_assigned - +from vyos.validate import is_addr_assigned +from vyos import ConfigError from vyos import airbag airbag.enable() k_mod = ['l2tp_eth', 'l2tp_netlink', 'l2tp_ip', 'l2tp_ip6'] -default_config_data = { - 'address': [], - 'deleted': False, - 'description': '', - 'disable': False, - 'encapsulation': 'udp', - 'local_address': '', - 'local_port': 5000, - 'intf': '', - 'ipv6_accept_ra': 1, - 'ipv6_autoconf': 0, - 'ipv6_eui64_prefix': [], - 'ipv6_forwarding': 1, - 'ipv6_dup_addr_detect': 1, - 'is_bridge_member': False, - 'mtu': 1488, - 'peer_session_id': '', - 'peer_tunnel_id': '', - 'remote_address': '', - 'remote_port': 5000, - 'session_id': '', - 'tunnel_id': '' -} def get_config(): - l2tpv3 = deepcopy(default_config_data) + """ + Retrive CLI config as dictionary. Dictionary can never be empty, as at least the + interface name will be added or a deleted flag + """ conf = Config() + base = ['interfaces', 'l2tpv3'] + l2tpv3 = get_interface_dict(conf, base) - # determine tagNode instance - if 'VYOS_TAGNODE_VALUE' not in os.environ: - raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') - - l2tpv3['intf'] = os.environ['VYOS_TAGNODE_VALUE'] - - # check if interface is member of a bridge - l2tpv3['is_bridge_member'] = is_member(conf, l2tpv3['intf'], 'bridge') - - # Check if interface has been removed - if not conf.exists('interfaces l2tpv3 ' + l2tpv3['intf']): - l2tpv3['deleted'] = True - interface = l2tpv3['intf'] - - # to delete the l2tpv3 interface we need the current tunnel_id and session_id - if conf.exists_effective(f'interfaces l2tpv3 {interface} tunnel-id'): - l2tpv3['tunnel_id'] = conf.return_effective_value(f'interfaces l2tpv3 {interface} tunnel-id') - - if conf.exists_effective(f'interfaces l2tpv3 {interface} session-id'): - l2tpv3['session_id'] = conf.return_effective_value(f'interfaces l2tpv3 {interface} session-id') - - return l2tpv3 - - # set new configuration level - conf.set_level('interfaces l2tpv3 ' + l2tpv3['intf']) - - # retrieve configured interface addresses - if conf.exists('address'): - l2tpv3['address'] = conf.return_values('address') - - # retrieve interface description - if conf.exists('description'): - l2tpv3['description'] = conf.return_value('description') - - # get tunnel destination port - if conf.exists('destination-port'): - l2tpv3['remote_port'] = int(conf.return_value('destination-port')) - - # Disable this interface - if conf.exists('disable'): - l2tpv3['disable'] = True - - # get tunnel encapsulation type - if conf.exists('encapsulation'): - l2tpv3['encapsulation'] = conf.return_value('encapsulation') - - # get tunnel local ip address - if conf.exists('local-ip'): - l2tpv3['local_address'] = conf.return_value('local-ip') - - # Enable acquisition of IPv6 address using stateless autoconfig (SLAAC) - if conf.exists('ipv6 address autoconf'): - l2tpv3['ipv6_autoconf'] = 1 - - # Get prefixes for IPv6 addressing based on MAC address (EUI-64) - if conf.exists('ipv6 address eui64'): - l2tpv3['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64') - - # Remove the default link-local address if set. - if not ( conf.exists('ipv6 address no-default-link-local') or - l2tpv3['is_bridge_member'] ): - # add the link-local by default to make IPv6 work - l2tpv3['ipv6_eui64_prefix'].append('fe80::/64') - - # Disable IPv6 forwarding on this interface - if conf.exists('ipv6 disable-forwarding'): - l2tpv3['ipv6_forwarding'] = 0 + # L2TPv3 is "special" the default MTU is 1488 - update accordingly + # as the config_level is already st in get_interface_dict() - we can use [] + tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) + if 'mtu' not in tmp: + l2tpv3['mtu'] = '1488' - # IPv6 Duplicate Address Detection (DAD) tries - if conf.exists('ipv6 dup-addr-detect-transmits'): - l2tpv3['ipv6_dup_addr_detect'] = int(conf.return_value('ipv6 dup-addr-detect-transmits')) + # To delete an l2tpv3 interface we need the current tunnel and session-id + if 'deleted' in l2tpv3: + tmp = leaf_node_changed(conf, ['tunnel-id']) + l2tpv3.update({'tunnel_id': tmp}) - # to make IPv6 SLAAC and DHCPv6 work with forwarding=1, - # accept_ra must be 2 - if l2tpv3['ipv6_autoconf'] or 'dhcpv6' in l2tpv3['address']: - l2tpv3['ipv6_accept_ra'] = 2 - - # Maximum Transmission Unit (MTU) - if conf.exists('mtu'): - l2tpv3['mtu'] = int(conf.return_value('mtu')) - - # Remote session id - if conf.exists('peer-session-id'): - l2tpv3['peer_session_id'] = conf.return_value('peer-session-id') - - # Remote tunnel id - if conf.exists('peer-tunnel-id'): - l2tpv3['peer_tunnel_id'] = conf.return_value('peer-tunnel-id') - - # Remote address of L2TPv3 tunnel - if conf.exists('remote-ip'): - l2tpv3['remote_address'] = conf.return_value('remote-ip') - - # Local session id - if conf.exists('session-id'): - l2tpv3['session_id'] = conf.return_value('session-id') - - # get local tunnel port - if conf.exists('source-port'): - l2tpv3['local_port'] = conf.return_value('source-port') - - # get local tunnel id - if conf.exists('tunnel-id'): - l2tpv3['tunnel_id'] = conf.return_value('tunnel-id') + tmp = leaf_node_changed(conf, ['session-id']) + l2tpv3.update({'session_id': tmp}) return l2tpv3 - def verify(l2tpv3): - interface = l2tpv3['intf'] - - if l2tpv3['deleted']: - if l2tpv3['is_bridge_member']: - raise ConfigError(( - f'Interface "{l2tpv3["intf"]}" cannot be deleted as it is a ' - f'member of bridge "{l2tpv3["is_bridge_member"]}"!')) - + if 'deleted' in l2tpv3: + verify_bridge_delete(l2tpv3) return None - if not l2tpv3['local_address']: - raise ConfigError(f'Must configure the l2tpv3 local-ip for {interface}') + interface = l2tpv3['ifname'] - if not is_addr_assigned(l2tpv3['local_address']): - raise ConfigError(f'Must use a configured IP on l2tpv3 local-ip for {interface}') + for key in ['local_ip', 'remote_ip', 'tunnel_id', 'peer_tunnel_id', + 'session_id', 'peer_session_id']: + if key not in l2tpv3: + tmp = key.replace('_', '-') + raise ConfigError(f'L2TPv3 {tmp} must be configured!') - if not l2tpv3['remote_address']: - raise ConfigError(f'Must configure the l2tpv3 remote-ip for {interface}') - - if not l2tpv3['tunnel_id']: - raise ConfigError(f'Must configure the l2tpv3 tunnel-id for {interface}') - - if not l2tpv3['peer_tunnel_id']: - raise ConfigError(f'Must configure the l2tpv3 peer-tunnel-id for {interface}') - - if not l2tpv3['session_id']: - raise ConfigError(f'Must configure the l2tpv3 session-id for {interface}') - - if not l2tpv3['peer_session_id']: - raise ConfigError(f'Must configure the l2tpv3 peer-session-id for {interface}') - - if ( l2tpv3['is_bridge_member'] - and ( l2tpv3['address'] - or l2tpv3['ipv6_eui64_prefix'] - or l2tpv3['ipv6_autoconf'] ) ): - raise ConfigError(( - f'Cannot assign address to interface "{l2tpv3["intf"]}" ' - f'as it is a member of bridge "{l2tpv3["is_bridge_member"]}"!')) + if not is_addr_assigned(l2tpv3['local_ip']): + raise ConfigError('L2TPv3 local-ip address ' + '"{local_ip}" is not configured!'.format(**l2tpv3)) + verify_address(l2tpv3) return None - def generate(l2tpv3): return None @@ -221,59 +90,28 @@ def apply(l2tpv3): conf = deepcopy(L2TPv3If.get_config()) # Check if L2TPv3 interface already exists - if l2tpv3['intf'] in interfaces(): + if l2tpv3['ifname'] in interfaces(): # L2TPv3 is picky when changing tunnels/sessions, thus we can simply # always delete it first. conf['session_id'] = l2tpv3['session_id'] conf['tunnel_id'] = l2tpv3['tunnel_id'] - l = L2TPv3If(l2tpv3['intf'], **conf) + l = L2TPv3If(l2tpv3['ifname'], **conf) l.remove() - if not l2tpv3['deleted']: + if 'deleted' not in l2tpv3: conf['peer_tunnel_id'] = l2tpv3['peer_tunnel_id'] - conf['local_port'] = l2tpv3['local_port'] - conf['remote_port'] = l2tpv3['remote_port'] + conf['local_port'] = l2tpv3['source_port'] + conf['remote_port'] = l2tpv3['destination_port'] conf['encapsulation'] = l2tpv3['encapsulation'] - conf['local_address'] = l2tpv3['local_address'] - conf['remote_address'] = l2tpv3['remote_address'] + conf['local_address'] = l2tpv3['local_ip'] + conf['remote_address'] = l2tpv3['remote_ip'] conf['session_id'] = l2tpv3['session_id'] conf['tunnel_id'] = l2tpv3['tunnel_id'] conf['peer_session_id'] = l2tpv3['peer_session_id'] # Finally create the new interface - l = L2TPv3If(l2tpv3['intf'], **conf) - # update interface description used e.g. by SNMP - l.set_alias(l2tpv3['description']) - # Maximum Transfer Unit (MTU) - l.set_mtu(l2tpv3['mtu']) - # IPv6 accept RA - l.set_ipv6_accept_ra(l2tpv3['ipv6_accept_ra']) - # IPv6 address autoconfiguration - l.set_ipv6_autoconf(l2tpv3['ipv6_autoconf']) - # IPv6 forwarding - l.set_ipv6_forwarding(l2tpv3['ipv6_forwarding']) - # IPv6 Duplicate Address Detection (DAD) tries - l.set_ipv6_dad_messages(l2tpv3['ipv6_dup_addr_detect']) - - # Configure interface address(es) - no need to implicitly delete the - # old addresses as they have already been removed by deleting the - # interface above - for addr in l2tpv3['address']: - l.add_addr(addr) - - # IPv6 EUI-based addresses - for addr in l2tpv3['ipv6_eui64_prefix']: - l.add_ipv6_eui64_address(addr) - - # As the interface is always disabled first when changing parameters - # we will only re-enable the interface if it is not administratively - # disabled - if not l2tpv3['disable']: - l.set_admin_state('up') - - # re-add ourselves to any bridge we might have fallen out of - if l2tpv3['is_bridge_member']: - l.add_to_bridge(l2tpv3['is_bridge_member']) + l = L2TPv3If(l2tpv3['ifname'], **conf) + l.update(l2tpv3) return None -- cgit v1.2.3 From 9578890e1e4dc31da7387a6ad810d2efb22391e7 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Sun, 26 Jul 2020 14:06:01 +0300 Subject: Fix a typo in BFD command help. --- interface-definitions/protocols-bfd.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'interface-definitions') diff --git a/interface-definitions/protocols-bfd.xml.in b/interface-definitions/protocols-bfd.xml.in index 62e2c87b9..8900e7955 100644 --- a/interface-definitions/protocols-bfd.xml.in +++ b/interface-definitions/protocols-bfd.xml.in @@ -28,7 +28,7 @@ - Bind listener to specifid interface/address, mandatory for IPv6 + Bind listener to specified interface/address, mandatory for IPv6 -- cgit v1.2.3 From 6b531e04474d7d976d7cee11e556c3fe3dc2b69f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 26 Jul 2020 09:55:18 +0200 Subject: pseudo-ethernet: ifconfig: T2733: support MTU configuration --- interface-definitions/interfaces-pseudo-ethernet.xml.in | 1 + 1 file changed, 1 insertion(+) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-pseudo-ethernet.xml.in b/interface-definitions/interfaces-pseudo-ethernet.xml.in index 0ef45e2c2..4382db598 100644 --- a/interface-definitions/interfaces-pseudo-ethernet.xml.in +++ b/interface-definitions/interfaces-pseudo-ethernet.xml.in @@ -72,6 +72,7 @@ private + #include #include #include -- cgit v1.2.3 From a156d2f1479affe4e7cfa56785e4d2d61a776cea Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 26 Jul 2020 11:16:13 +0200 Subject: vxlan: ifconfig: T2653: move to get_interface_dict() The current VyOS CLI parser code written in Python contains a ton of duplicates which I can also hold myself accountable for - or maybe mainly me - depends on the angle of judge. --- interface-definitions/interfaces-vxlan.xml.in | 1 + python/vyos/ifconfig/vxlan.py | 20 +- src/conf_mode/interfaces-vxlan.py | 253 ++++---------------------- 3 files changed, 46 insertions(+), 228 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in index bd3ab4022..8529f6885 100644 --- a/interface-definitions/interfaces-vxlan.xml.in +++ b/interface-definitions/interfaces-vxlan.xml.in @@ -93,6 +93,7 @@ + 8472 diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py index 973b4ef05..0dddab7b7 100644 --- a/python/vyos/ifconfig/vxlan.py +++ b/python/vyos/ifconfig/vxlan.py @@ -47,8 +47,8 @@ class VXLANIf(Interface): 'port': 8472, # The Linux implementation of VXLAN pre-dates # the IANA's selection of a standard destination port 'remote': '', - 'src_address': '', - 'src_interface': '', + 'source_address': '', + 'source_interface': '', 'vni': 0 } definition = { @@ -60,29 +60,29 @@ class VXLANIf(Interface): } } options = Interface.options + \ - ['group', 'remote', 'src_interface', 'port', 'vni', 'src_address'] + ['group', 'remote', 'source_interface', 'port', 'vni', 'source_address'] mapping = { 'ifname': 'add', 'vni': 'id', 'port': 'dstport', - 'src_address': 'local', - 'src_interface': 'dev', + 'source_address': 'local', + 'source_interface': 'dev', } def _create(self): cmdline = ['ifname', 'type', 'vni', 'port'] - if self.config['src_address']: - cmdline.append('src_address') + if self.config['source_address']: + cmdline.append('source_address') if self.config['remote']: cmdline.append('remote') - if self.config['group'] or self.config['src_interface']: - if self.config['group'] and self.config['src_interface']: + if self.config['group'] or self.config['source_interface']: + if self.config['group'] and self.config['source_interface']: cmdline.append('group') - cmdline.append('src_interface') + cmdline.append('source_interface') else: ifname = self.config['ifname'] raise ConfigError( diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index 39db814b4..47c0bdcb8 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -21,197 +21,61 @@ from copy import deepcopy from netifaces import interfaces from vyos.config import Config +from vyos.configdict import get_interface_dict +from vyos.configverify import verify_address +from vyos.configverify import verify_bridge_delete +from vyos.configverify import verify_source_interface from vyos.ifconfig import VXLANIf, Interface -from vyos.validate import is_member from vyos import ConfigError - from vyos import airbag airbag.enable() -default_config_data = { - 'address': [], - 'deleted': False, - 'description': '', - 'disable': False, - 'group': '', - 'intf': '', - 'ip_arp_cache_tmo': 30, - 'ip_disable_arp_filter': 1, - 'ip_enable_arp_accept': 0, - 'ip_enable_arp_announce': 0, - 'ip_enable_arp_ignore': 0, - 'ip_proxy_arp': 0, - 'ipv6_accept_ra': 1, - 'ipv6_autoconf': 0, - 'ipv6_eui64_prefix': [], - 'ipv6_forwarding': 1, - 'ipv6_dup_addr_detect': 1, - 'is_bridge_member': False, - 'source_address': '', - 'source_interface': '', - 'mtu': 1450, - 'remote': '', - 'remote_port': 8472, # The Linux implementation of VXLAN pre-dates - # the IANA's selection of a standard destination port - 'vni': '' -} - def get_config(): - vxlan = deepcopy(default_config_data) + """ + Retrive CLI config as dictionary. Dictionary can never be empty, as at least the + interface name will be added or a deleted flag + """ conf = Config() + base = ['interfaces', 'vxlan'] + vxlan = get_interface_dict(conf, base) - # determine tagNode instance - if 'VYOS_TAGNODE_VALUE' not in os.environ: - raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') - - vxlan['intf'] = os.environ['VYOS_TAGNODE_VALUE'] - - # check if interface is member if a bridge - vxlan['is_bridge_member'] = is_member(conf, vxlan['intf'], 'bridge') - - # Check if interface has been removed - if not conf.exists('interfaces vxlan ' + vxlan['intf']): - vxlan['deleted'] = True - return vxlan - - # set new configuration level - conf.set_level('interfaces vxlan ' + vxlan['intf']) - - # retrieve configured interface addresses - if conf.exists('address'): - vxlan['address'] = conf.return_values('address') - - # retrieve interface description - if conf.exists('description'): - vxlan['description'] = conf.return_value('description') - - # Disable this interface - if conf.exists('disable'): - vxlan['disable'] = True - - # VXLAN multicast grou - if conf.exists('group'): - vxlan['group'] = conf.return_value('group') - - # ARP cache entry timeout in seconds - if conf.exists('ip arp-cache-timeout'): - vxlan['ip_arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout')) - - # ARP filter configuration - if conf.exists('ip disable-arp-filter'): - vxlan['ip_disable_arp_filter'] = 0 - - # ARP enable accept - if conf.exists('ip enable-arp-accept'): - vxlan['ip_enable_arp_accept'] = 1 - - # ARP enable announce - if conf.exists('ip enable-arp-announce'): - vxlan['ip_enable_arp_announce'] = 1 - - # ARP enable ignore - if conf.exists('ip enable-arp-ignore'): - vxlan['ip_enable_arp_ignore'] = 1 - - # Enable proxy-arp on this interface - if conf.exists('ip enable-proxy-arp'): - vxlan['ip_proxy_arp'] = 1 - - # Enable acquisition of IPv6 address using stateless autoconfig (SLAAC) - if conf.exists('ipv6 address autoconf'): - vxlan['ipv6_autoconf'] = 1 - - # Get prefixes for IPv6 addressing based on MAC address (EUI-64) - if conf.exists('ipv6 address eui64'): - vxlan['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64') - - # Remove the default link-local address if set. - if not ( conf.exists('ipv6 address no-default-link-local') - or vxlan['is_bridge_member'] ): - # add the link-local by default to make IPv6 work - vxlan['ipv6_eui64_prefix'].append('fe80::/64') - - # Disable IPv6 forwarding on this interface - if conf.exists('ipv6 disable-forwarding'): - vxlan['ipv6_forwarding'] = 0 - - # IPv6 Duplicate Address Detection (DAD) tries - if conf.exists('ipv6 dup-addr-detect-transmits'): - vxlan['ipv6_dup_addr_detect'] = int(conf.return_value('ipv6 dup-addr-detect-transmits')) - - # to make IPv6 SLAAC and DHCPv6 work with forwarding=1, - # accept_ra must be 2 - if vxlan['ipv6_autoconf'] or 'dhcpv6' in vxlan['address']: - vxlan['ipv6_accept_ra'] = 2 - - # VXLAN source address - if conf.exists('source-address'): - vxlan['source_address'] = conf.return_value('source-address') - - # VXLAN underlay interface - if conf.exists('source-interface'): - vxlan['source_interface'] = conf.return_value('source-interface') - - # Maximum Transmission Unit (MTU) - if conf.exists('mtu'): - vxlan['mtu'] = int(conf.return_value('mtu')) - - # Remote address of VXLAN tunnel - if conf.exists('remote'): - vxlan['remote'] = conf.return_value('remote') - - # Remote port of VXLAN tunnel - if conf.exists('port'): - vxlan['remote_port'] = int(conf.return_value('port')) - - # Virtual Network Identifier - if conf.exists('vni'): - vxlan['vni'] = conf.return_value('vni') + # VXLAN is "special" the default MTU is 1492 - update accordingly + # as the config_level is already st in get_interface_dict() - we can use [] + tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) + if 'mtu' not in tmp: + vxlan['mtu'] = '1450' return vxlan - def verify(vxlan): - if vxlan['deleted']: - if vxlan['is_bridge_member']: - raise ConfigError(( - f'Cannot delete interface "{vxlan["intf"]}" as it is a ' - f'member of bridge "{vxlan["is_bridge_member"]}"!')) - + if 'deleted' in vxlan: + verify_bridge_delete(vxlan) return None - if vxlan['mtu'] < 1500: + if int(vxlan['mtu']) < 1500: print('WARNING: RFC7348 recommends VXLAN tunnels preserve a 1500 byte MTU') - if vxlan['group']: - if not vxlan['source_interface']: + if 'group' in vxlan: + if 'source_interface' not in vxlan: raise ConfigError('Multicast VXLAN requires an underlaying interface ') - if not vxlan['source_interface'] in interfaces(): - raise ConfigError('VXLAN source interface does not exist') + verify_source_interface(vxlan) - if not (vxlan['group'] or vxlan['remote'] or vxlan['source_address']): + if not any(tmp in ['group', 'remote', 'source_address'] for tmp in vxlan): raise ConfigError('Group, remote or source-address must be configured') - if not vxlan['vni']: + if 'vni' not in vxlan: raise ConfigError('Must configure VNI for VXLAN') - if vxlan['source_interface']: + if 'source_interface' in vxlan: # VXLAN adds a 50 byte overhead - we need to check the underlaying MTU # if our configured MTU is at least 50 bytes less underlay_mtu = int(Interface(vxlan['source_interface']).get_mtu()) - if underlay_mtu < (vxlan['mtu'] + 50): + if underlay_mtu < (int(vxlan['mtu']) + 50): raise ConfigError('VXLAN has a 50 byte overhead, underlaying device ' \ - 'MTU is to small ({})'.format(underlay_mtu)) - - if ( vxlan['is_bridge_member'] - and ( vxlan['address'] - or vxlan['ipv6_eui64_prefix'] - or vxlan['ipv6_autoconf'] ) ): - raise ConfigError(( - f'Cannot assign address to interface "{vxlan["intf"]}" ' - f'as it is a member of bridge "{vxlan["is_bridge_member"]}"!')) + f'MTU is to small ({underlay_mtu} bytes)') + verify_address(vxlan) return None @@ -221,73 +85,26 @@ def generate(vxlan): def apply(vxlan): # Check if the VXLAN interface already exists - if vxlan['intf'] in interfaces(): - v = VXLANIf(vxlan['intf']) + if vxlan['ifname'] in interfaces(): + v = VXLANIf(vxlan['ifname']) # VXLAN is super picky and the tunnel always needs to be recreated, # thus we can simply always delete it first. v.remove() - if not vxlan['deleted']: + if 'deleted' not in vxlan: # VXLAN interface needs to be created on-block # instead of passing a ton of arguments, I just use a dict # that is managed by vyos.ifconfig conf = deepcopy(VXLANIf.get_config()) # Assign VXLAN instance configuration parameters to config dict - conf['vni'] = vxlan['vni'] - conf['group'] = vxlan['group'] - conf['src_address'] = vxlan['source_address'] - conf['src_interface'] = vxlan['source_interface'] - conf['remote'] = vxlan['remote'] - conf['port'] = vxlan['remote_port'] + for tmp in ['vni', 'group', 'source_address', 'source_interface', 'remote', 'port']: + if tmp in vxlan: + conf[tmp] = vxlan[tmp] # Finally create the new interface - v = VXLANIf(vxlan['intf'], **conf) - # update interface description used e.g. by SNMP - v.set_alias(vxlan['description']) - # Maximum Transfer Unit (MTU) - v.set_mtu(vxlan['mtu']) - - # configure ARP cache timeout in milliseconds - v.set_arp_cache_tmo(vxlan['ip_arp_cache_tmo']) - # configure ARP filter configuration - v.set_arp_filter(vxlan['ip_disable_arp_filter']) - # configure ARP accept - v.set_arp_accept(vxlan['ip_enable_arp_accept']) - # configure ARP announce - v.set_arp_announce(vxlan['ip_enable_arp_announce']) - # configure ARP ignore - v.set_arp_ignore(vxlan['ip_enable_arp_ignore']) - # Enable proxy-arp on this interface - v.set_proxy_arp(vxlan['ip_proxy_arp']) - # IPv6 accept RA - v.set_ipv6_accept_ra(vxlan['ipv6_accept_ra']) - # IPv6 address autoconfiguration - v.set_ipv6_autoconf(vxlan['ipv6_autoconf']) - # IPv6 forwarding - v.set_ipv6_forwarding(vxlan['ipv6_forwarding']) - # IPv6 Duplicate Address Detection (DAD) tries - v.set_ipv6_dad_messages(vxlan['ipv6_dup_addr_detect']) - - # Configure interface address(es) - no need to implicitly delete the - # old addresses as they have already been removed by deleting the - # interface above - for addr in vxlan['address']: - v.add_addr(addr) - - # IPv6 EUI-based addresses - for addr in vxlan['ipv6_eui64_prefix']: - v.add_ipv6_eui64_address(addr) - - # As the VXLAN interface is always disabled first when changing - # parameters we will only re-enable the interface if it is not - # administratively disabled - if not vxlan['disable']: - v.set_admin_state('up') - - # re-add ourselves to any bridge we might have fallen out of - if vxlan['is_bridge_member']: - v.add_to_bridge(vxlan['is_bridge_member']) + v = VXLANIf(vxlan['ifname'], **conf) + v.update(vxlan) return None -- cgit v1.2.3 From c87616aaef53e69ddf135725bb33d105afaa6c5b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 26 Jul 2020 13:16:19 +0200 Subject: wireguard: T2734: fwmark is a 32bit value - fix validator --- interface-definitions/interfaces-wireguard.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in index 5894f159d..edf9bf696 100644 --- a/interface-definitions/interfaces-wireguard.xml.in +++ b/interface-definitions/interfaces-wireguard.xml.in @@ -30,7 +30,7 @@ value which marks the packet for QoS/shaper - + -- cgit v1.2.3 From 789775af9f5e3f9239ef4583eb4ef7538e40b37c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 26 Jul 2020 16:36:48 +0200 Subject: wireguard: ifconfig: T2653: move to get_config_dict() The current VyOS CLI parser code written in Python contains a ton of duplicates which I can also hold myself accountable for - or maybe mainly me - depends on the angle of judge. --- interface-definitions/interfaces-wireguard.xml.in | 8 +- python/vyos/ifconfig/wireguard.py | 136 +++++----- src/conf_mode/interfaces-wireguard.py | 300 ++++------------------ 3 files changed, 139 insertions(+), 305 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in index edf9bf696..981bce826 100644 --- a/interface-definitions/interfaces-wireguard.xml.in +++ b/interface-definitions/interfaces-wireguard.xml.in @@ -33,6 +33,7 @@ + 0 @@ -41,6 +42,7 @@ + default @@ -103,7 +105,11 @@ #include - how often send keep alives in seconds + Interval to send keepalive messages + + 1-65535 + Interval in seconds + diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py index 62ca57ca2..fad4ef282 100644 --- a/python/vyos/ifconfig/wireguard.py +++ b/python/vyos/ifconfig/wireguard.py @@ -24,7 +24,7 @@ from hurry.filesize import alternative from vyos.config import Config from vyos.ifconfig import Interface from vyos.ifconfig import Operational - +from vyos.validate import is_ipv6 class WireGuardOperational(Operational): def _dump(self): @@ -169,65 +169,79 @@ class WireGuardIf(Interface): ['port', 'private_key', 'pubkey', 'psk', 'allowed_ips', 'fwmark', 'endpoint', 'keepalive'] - """ - Wireguard interface class, contains a comnfig dictionary since - wireguard VPN is being comnfigured via the wg command rather than - writing the config into a file. Otherwise if a pre-shared key is used - (symetric enryption key), it would we exposed within multiple files. - Currently it's only within the config.boot if the config was saved. - - Example: - >>> from vyos.ifconfig import WireGuardIf as wg_if - >>> wg_intfc = wg_if("wg01") - >>> print (wg_intfc.wg_config) - {'private_key': None, 'keepalive': 0, 'endpoint': None, 'port': 0, - 'allowed_ips': [], 'pubkey': None, 'fwmark': 0, 'psk': '/dev/null'} - >>> wg_intfc.wg_config['keepalive'] = 100 - >>> print (wg_intfc.wg_config) - {'private_key': None, 'keepalive': 100, 'endpoint': None, 'port': 0, - 'allowed_ips': [], 'pubkey': None, 'fwmark': 0, 'psk': '/dev/null'} - """ - - def update(self): - if not self.config['private_key']: - raise ValueError("private key required") - else: - # fmask permission check? - pass - - cmd = 'wg set {ifname}'.format(**self.config) - cmd += ' listen-port {port}'.format(**self.config) - cmd += ' fwmark "{fwmark}" '.format(**self.config) - cmd += ' private-key {private_key}'.format(**self.config) - cmd += ' peer {pubkey}'.format(**self.config) - cmd += ' persistent-keepalive {keepalive}'.format(**self.config) - # allowed-ips must be properly quoted else the interface can't be properly - # created as the wg utility will tread multiple IP addresses as command - # parameters - cmd += ' allowed-ips "{}"'.format(','.join(self.config['allowed-ips'])) - - if self.config['endpoint']: - cmd += ' endpoint "{endpoint}"'.format(**self.config) - - psk_file = '' - if self.config['psk']: - psk_file = '/tmp/{ifname}.psk'.format(**self.config) - with open(psk_file, 'w') as f: - f.write(self.config['psk']) + def update(self, config): + """ General helper function which works on a dictionary retrived by + get_config_dict(). It's main intention is to consolidate the scattered + interface setup code and provide a single point of entry when workin + on any interface. """ + + # remove no longer associated peers first + if 'peer_remove' in config: + for tmp in config['peer_remove']: + peer = config['peer_remove'][tmp] + peer['ifname'] = config['ifname'] + + cmd = 'wg set {ifname} peer {pubkey} remove' + self._cmd(cmd.format(**peer)) + + # Wireguard base command is identical for every peer + base_cmd = 'wg set {ifname} private-key {private_key}' + if 'port' in config: + base_cmd += ' listen-port {port}' + if 'fwmark' in config: + base_cmd += ' fwmark {fwmark}' + + base_cmd = base_cmd.format(**config) + + for tmp in config['peer']: + peer = config['peer'][tmp] + + # start of with a fresh 'wg' command + cmd = base_cmd + ' peer {pubkey}' + + # If no PSK is given remove it by using /dev/null - passing keys via + # the shell (usually bash) is considered insecure, thus we use a file + no_psk_file = '/dev/null' + psk_file = no_psk_file + if 'preshared_key' in peer: + psk_file = '/tmp/tmp.wireguard.psk' + with open(psk_file, 'w') as f: + f.write(peer['preshared_key']) cmd += f' preshared-key {psk_file}' - self._cmd(cmd) - - # PSK key file is not required to be stored persistently as its backed by CLI - if os.path.exists(psk_file): - os.remove(psk_file) - - def remove_peer(self, peerkey): - """ - Remove a peer of an interface, peers are identified by their public key. - Giving it a readable name is a vyos feature, to remove a peer the pubkey - and the interface is needed, to remove the entry. - """ - cmd = "wg set {0} peer {1} remove".format( - self.config['ifname'], str(peerkey)) - return self._cmd(cmd) + # Persistent keepalive is optional + if 'persistent_keepalive'in peer: + cmd += ' persistent-keepalive {persistent_keepalive}' + + # Multiple allowed-ip ranges can be defined - ensure we are always + # dealing with a list + if isinstance(peer['allowed_ips'], str): + peer['allowed_ips'] = [peer['allowed_ips']] + cmd += ' allowed-ips ' + ','.join(peer['allowed_ips']) + + # Endpoint configuration is optional + if {'address', 'port'} <= set(peer): + if is_ipv6(config['address']): + cmd += ' endpoint [{address}]:{port}' + else: + cmd += ' endpoint {address}:{port}' + + self._cmd(cmd.format(**peer)) + + # PSK key file is not required to be stored persistently as its backed by CLI + if psk_file != no_psk_file and os.path.exists(psk_file): + os.remove(psk_file) + + # call base class + super().update(config) + + # Enable/Disable of an interface must always be done at the end of the + # derived class to make use of the ref-counting set_admin_state() + # function. We will only enable the interface if 'up' was called as + # often as 'down'. This is required by some interface implementations + # as certain parameters can only be changed when the interface is + # in admin-down state. This ensures the link does not flap during + # reconfiguration. + state = 'down' if 'disable' in config else 'up' + self.set_admin_state(state) + diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index 982aefa5f..6325a8b05 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -15,44 +15,29 @@ # along with this program. If not, see . import os -import re from sys import exit from copy import deepcopy -from netifaces import interfaces from vyos.config import Config -from vyos.configdict import list_diff +from vyos.configdict import dict_merge +from vyos.configdict import get_interface_dict +from vyos.configdict import node_changed +from vyos.configdict import leaf_node_changed +from vyos.configverify import verify_vrf +from vyos.configverify import verify_address +from vyos.configverify import verify_bridge_delete from vyos.ifconfig import WireGuardIf -from vyos.util import chown, chmod_750, call +from vyos.util import chown, chmod_750 from vyos.util import check_kmod -from vyos.validate import is_member, is_ipv6 from vyos import ConfigError - from vyos import airbag airbag.enable() -kdir = r'/config/auth/wireguard' k_mod = 'wireguard' -default_config_data = { - 'intfc': '', - 'address': [], - 'address_remove': [], - 'description': '', - 'listen_port': '', - 'deleted': False, - 'disable': False, - 'fwmark': 0, - 'is_bridge_member': False, - 'mtu': 1420, - 'peer': [], - 'peer_remove': [], # stores public keys of peers to remove - 'pk': f'{kdir}/default/private.key', - 'vrf': '' -} - def _migrate_default_keys(): + kdir = r'/config/auth/wireguard' if os.path.exists(f'{kdir}/private.key') and not os.path.exists(f'{kdir}/default/private.key'): location = f'{kdir}/default' if not os.path.exists(location): @@ -65,246 +50,75 @@ def _migrate_default_keys(): def get_config(): + """ + Retrive CLI config as dictionary. Dictionary can never be empty, as at least the + interface name will be added or a deleted flag + """ conf = Config() base = ['interfaces', 'wireguard'] + wireguard = get_interface_dict(conf, base) - # determine tagNode instance - if 'VYOS_TAGNODE_VALUE' not in os.environ: - raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') - - wg = deepcopy(default_config_data) - wg['intf'] = os.environ['VYOS_TAGNODE_VALUE'] - - # check if interface is member if a bridge - wg['is_bridge_member'] = is_member(conf, wg['intf'], 'bridge') - - # Check if interface has been removed - if not conf.exists(base + [wg['intf']]): - wg['deleted'] = True - return wg - - conf.set_level(base + [wg['intf']]) - - # retrieve configured interface addresses - if conf.exists(['address']): - wg['address'] = conf.return_values(['address']) - - # get interface addresses (currently effective) - to determine which - # address is no longer valid and needs to be removed - eff_addr = conf.return_effective_values(['address']) - wg['address_remove'] = list_diff(eff_addr, wg['address']) - - # retrieve interface description - if conf.exists(['description']): - wg['description'] = conf.return_value(['description']) - - # disable interface - if conf.exists(['disable']): - wg['disable'] = True - - # local port to listen on - if conf.exists(['port']): - wg['listen_port'] = conf.return_value(['port']) - - # fwmark value - if conf.exists(['fwmark']): - wg['fwmark'] = int(conf.return_value(['fwmark'])) - - # Maximum Transmission Unit (MTU) - if conf.exists('mtu'): - wg['mtu'] = int(conf.return_value(['mtu'])) - - # retrieve VRF instance - if conf.exists('vrf'): - wg['vrf'] = conf.return_value('vrf') - - # private key - if conf.exists(['private-key']): - wg['pk'] = "{0}/{1}/private.key".format( - kdir, conf.return_value(['private-key'])) - - # peer removal, wg identifies peers by its pubkey - peer_eff = conf.list_effective_nodes(['peer']) - peer_rem = list_diff(peer_eff, conf.list_nodes(['peer'])) - for peer in peer_rem: - wg['peer_remove'].append( - conf.return_effective_value(['peer', peer, 'pubkey'])) - - # peer settings - if conf.exists(['peer']): - for p in conf.list_nodes(['peer']): - # set new config level for this peer - conf.set_level(base + [wg['intf'], 'peer', p]) - peer = { - 'allowed-ips': [], - 'address': '', - 'name': p, - 'persistent_keepalive': '', - 'port': '', - 'psk': '', - 'pubkey': '' - } - - # peer allowed-ips - if conf.exists(['allowed-ips']): - peer['allowed-ips'] = conf.return_values(['allowed-ips']) - - # peer address - if conf.exists(['address']): - peer['address'] = conf.return_value(['address']) - - # peer port - if conf.exists(['port']): - peer['port'] = conf.return_value(['port']) + # Wireguard is "special" the default MTU is 1420 - update accordingly + # as the config_level is already st in get_interface_dict() - we can use [] + tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) + if 'mtu' not in tmp: + wireguard['mtu'] = '1420' - # persistent-keepalive - if conf.exists(['persistent-keepalive']): - peer['persistent_keepalive'] = conf.return_value(['persistent-keepalive']) + # Mangle private key - it has a default so its always valid + wireguard['private_key'] = '/config/auth/wireguard/{private_key}/private.key'.format(**wireguard) - # preshared-key - if conf.exists(['preshared-key']): - peer['psk'] = conf.return_value(['preshared-key']) + # Determine which Wireguard peer has been removed. + # Peers can only be removed with their public key! + tmp = node_changed(conf, ['peer']) + if tmp: + dict = {} + for peer in tmp: + peer_config = leaf_node_changed(conf, ['peer', peer, 'pubkey']) + dict = dict_merge({'peer_remove' : {peer : {'pubkey' : peer_config}}}, dict) + wireguard.update(dict) - # peer pubkeys - if conf.exists(['pubkey']): - key_eff = conf.return_effective_value(['pubkey']) - key_cfg = conf.return_value(['pubkey']) - peer['pubkey'] = key_cfg + return wireguard - # on a pubkey change we need to remove the pubkey first - # peers are identified by pubkey, so key update means - # peer removal and re-add - if key_eff != key_cfg and key_eff != None: - wg['peer_remove'].append(key_cfg) - - # if a peer is disabled, we have to exec a remove for it's pubkey - if conf.exists(['disable']): - wg['peer_remove'].append(peer['pubkey']) - else: - wg['peer'].append(peer) - - return wg - - -def verify(wg): - if wg['deleted']: - if wg['is_bridge_member']: - raise ConfigError(( - f'Cannot delete interface "{wg["intf"]}" as it is a member ' - f'of bridge "{wg["is_bridge_member"]}"!')) +def verify(wireguard): + if 'deleted' in wireguard: + verify_bridge_delete(wireguard) return None - if wg['is_bridge_member'] and wg['address']: - raise ConfigError(( - f'Cannot assign address to interface "{wg["intf"]}" ' - f'as it is a member of bridge "{wg["is_bridge_member"]}"!')) - - if wg['vrf']: - if wg['vrf'] not in interfaces(): - raise ConfigError(f'VRF "{wg["vrf"]}" does not exist') + verify_address(wireguard) + verify_vrf(wireguard) - if wg['is_bridge_member']: - raise ConfigError(( - f'Interface "{wg["intf"]}" cannot be member of VRF ' - f'"{wg["vrf"]}" and bridge {wg["is_bridge_member"]} ' - f'at the same time!')) + if not os.path.exists(wireguard['private_key']): + raise ConfigError('Wireguard private-key not found! Execute: ' \ + '"run generate wireguard [default-keypair|named-keypairs]"') - if not os.path.exists(wg['pk']): - raise ConfigError('No keys found, generate them by executing:\n' \ - '"run generate wireguard [keypair|named-keypairs]"') + if 'address' not in wireguard: + raise ConfigError('IP address required!') - if not wg['address']: - raise ConfigError(f'IP address required for interface "{wg["intf"]}"!') - - if not wg['peer']: - raise ConfigError(f'Peer required for interface "{wg["intf"]}"!') + if 'peer' not in wireguard: + raise ConfigError('At least one Wireguard peer is required!') # run checks on individual configured WireGuard peer - for peer in wg['peer']: - if not peer['allowed-ips']: - raise ConfigError(f'Peer allowed-ips required for peer "{peer["name"]}"!') - - if not peer['pubkey']: - raise ConfigError(f'Peer public-key required for peer "{peer["name"]}"!') - - if peer['address'] and not peer['port']: - raise ConfigError(f'Peer "{peer["name"]}" port must be defined if address is defined!') + for tmp in wireguard['peer']: + peer = wireguard['peer'][tmp] - if not peer['address'] and peer['port']: - raise ConfigError(f'Peer "{peer["name"]}" address must be defined if port is defined!') + if 'allowed_ips' not in peer: + raise ConfigError(f'Wireguard allowed-ips required for peer "{tmp}"!') + if 'pubkey' not in peer: + raise ConfigError(f'Wireguard public-key required for peer "{tmp}"!') -def apply(wg): - # init wg class - w = WireGuardIf(wg['intf']) + if ('address' in peer and 'port' not in peer) or ('port' in peer and 'address' not in peer): + raise ConfigError('Both Wireguard port and address must be defined ' + f'for peer "{tmp}" if either one of them is set!') - # single interface removal - if wg['deleted']: - w.remove() +def apply(wireguard): + if 'deleted' in wireguard: + WireGuardIf(wireguard['ifname']).remove() return None - # Configure interface address(es) - # - not longer required addresses get removed first - # - newly addresses will be added second - for addr in wg['address_remove']: - w.del_addr(addr) - for addr in wg['address']: - w.add_addr(addr) - - # Maximum Transmission Unit (MTU) - w.set_mtu(wg['mtu']) - - # update interface description used e.g. within SNMP - w.set_alias(wg['description']) - - # assign/remove VRF (ONLY when not a member of a bridge, - # otherwise 'nomaster' removes it from it) - if not wg['is_bridge_member']: - w.set_vrf(wg['vrf']) - - # remove peers - for pub_key in wg['peer_remove']: - w.remove_peer(pub_key) - - # peer pubkey - # setting up the wg interface - w.config['private_key'] = c['pk'] - - for peer in wg['peer']: - # peer pubkey - w.config['pubkey'] = peer['pubkey'] - # peer allowed-ips - w.config['allowed-ips'] = peer['allowed-ips'] - # local listen port - if wg['listen_port']: - w.config['port'] = wg['listen_port'] - # fwmark - if c['fwmark']: - w.config['fwmark'] = wg['fwmark'] - - # endpoint - if peer['address'] and peer['port']: - if is_ipv6(peer['address']): - w.config['endpoint'] = '[{}]:{}'.format(peer['address'], peer['port']) - else: - w.config['endpoint'] = '{}:{}'.format(peer['address'], peer['port']) - - # persistent-keepalive - if peer['persistent_keepalive']: - w.config['keepalive'] = peer['persistent_keepalive'] - - if peer['psk']: - w.config['psk'] = peer['psk'] - - w.update() - - # Enable/Disable interface - if wg['disable']: - w.set_admin_state('down') - else: - w.set_admin_state('up') - + w = WireGuardIf(wireguard['ifname']) + w.update(wireguard) return None if __name__ == '__main__': -- cgit v1.2.3 From 9f7ef46636c8b995a3b758484324db4bc9a4df37 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 29 Jul 2020 17:31:35 +0200 Subject: mdns-repeater: T2742: migrate to get_config_dict() --- data/templates/mdns-repeater/mdns-repeater.tmpl | 2 +- interface-definitions/mdns-repeater.xml.in | 38 -------- interface-definitions/service_mdns-repeater.xml.in | 37 +++++++ src/conf_mode/mdns_repeater.py | 107 --------------------- src/conf_mode/service_mdns-repeater.py | 89 +++++++++++++++++ 5 files changed, 127 insertions(+), 146 deletions(-) delete mode 100644 interface-definitions/mdns-repeater.xml.in create mode 100644 interface-definitions/service_mdns-repeater.xml.in delete mode 100755 src/conf_mode/mdns_repeater.py create mode 100755 src/conf_mode/service_mdns-repeater.py (limited to 'interface-definitions') diff --git a/data/templates/mdns-repeater/mdns-repeater.tmpl b/data/templates/mdns-repeater/mdns-repeater.tmpl index 3fc4db67e..80f4ab047 100644 --- a/data/templates/mdns-repeater/mdns-repeater.tmpl +++ b/data/templates/mdns-repeater/mdns-repeater.tmpl @@ -1,2 +1,2 @@ ### Autogenerated by mdns_repeater.py ### -DAEMON_ARGS="{{ interfaces | join(' ') }}" +DAEMON_ARGS="{{ interface | join(' ') }}" diff --git a/interface-definitions/mdns-repeater.xml.in b/interface-definitions/mdns-repeater.xml.in deleted file mode 100644 index a59321294..000000000 --- a/interface-definitions/mdns-repeater.xml.in +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - Multicast DNS (mDNS) parameters - - - - - mDNS repeater configuration - 990 - - - - - Disable mDNS repeater service - - - - - - Interface to repeat mDNS advertisements [REQUIRED] - - - - - - - - - - - - - diff --git a/interface-definitions/service_mdns-repeater.xml.in b/interface-definitions/service_mdns-repeater.xml.in new file mode 100644 index 000000000..e21b1b27c --- /dev/null +++ b/interface-definitions/service_mdns-repeater.xml.in @@ -0,0 +1,37 @@ + + + + + + + Multicast DNS (mDNS) parameters + + + + + mDNS repeater configuration + 990 + + + + + Disable mDNS repeater service + + + + + + Interface to repeat mDNS advertisements [REQUIRED] + + + + + + + + + + + + + diff --git a/src/conf_mode/mdns_repeater.py b/src/conf_mode/mdns_repeater.py deleted file mode 100755 index b43f9bdd8..000000000 --- a/src/conf_mode/mdns_repeater.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2017-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 netifaces import ifaddresses, AF_INET - -from vyos.config import Config -from vyos import ConfigError -from vyos.util import call -from vyos.template import render - -from vyos import airbag -airbag.enable() - -config_file = r'/etc/default/mdns-repeater' - -default_config_data = { - 'disabled': False, - 'interfaces': [] -} - -def get_config(): - mdns = deepcopy(default_config_data) - conf = Config() - base = ['service', 'mdns', 'repeater'] - if not conf.exists(base): - return None - else: - conf.set_level(base) - - # Service can be disabled by user - if conf.exists(['disable']): - mdns['disabled'] = True - return mdns - - # Interface to repeat mDNS advertisements - if conf.exists(['interface']): - mdns['interfaces'] = conf.return_values(['interface']) - - return mdns - -def verify(mdns): - if mdns is None: - return None - - if mdns['disabled']: - return None - - # We need at least two interfaces to repeat mDNS advertisments - if len(mdns['interfaces']) < 2: - raise ConfigError('mDNS repeater requires at least 2 configured interfaces!') - - # For mdns-repeater to work it is essential that the interfaces has - # an IPv4 address assigned - for interface in mdns['interfaces']: - if AF_INET in ifaddresses(interface).keys(): - if len(ifaddresses(interface)[AF_INET]) < 1: - raise ConfigError('mDNS repeater requires an IPv6 address configured on interface %s!'.format(interface)) - - return None - -def generate(mdns): - if mdns is None: - return None - - if mdns['disabled']: - print('Warning: mDNS repeater will be deactivated because it is disabled') - return None - - render(config_file, 'mdns-repeater/mdns-repeater.tmpl', mdns) - return None - -def apply(mdns): - if (mdns is None) or mdns['disabled']: - call('systemctl stop mdns-repeater.service') - if os.path.exists(config_file): - os.unlink(config_file) - else: - call('systemctl restart mdns-repeater.service') - - return None - -if __name__ == '__main__': - try: - c = get_config() - verify(c) - generate(c) - apply(c) - except ConfigError as e: - print(e) - exit(1) diff --git a/src/conf_mode/service_mdns-repeater.py b/src/conf_mode/service_mdns-repeater.py new file mode 100755 index 000000000..1a6b2c328 --- /dev/null +++ b/src/conf_mode/service_mdns-repeater.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2017-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 netifaces import ifaddresses, interfaces, AF_INET + +from vyos.config import Config +from vyos.template import render +from vyos.util import call +from vyos import ConfigError +from vyos import airbag +airbag.enable() + +config_file = r'/etc/default/mdns-repeater' + +def get_config(): + conf = Config() + base = ['service', 'mdns', 'repeater'] + mdns = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + return mdns + +def verify(mdns): + if not mdns: + return None + + if 'disable' in mdns: + return None + + # We need at least two interfaces to repeat mDNS advertisments + if 'interface' not in mdns or len(mdns['interface']) < 2: + raise ConfigError('mDNS repeater requires at least 2 configured interfaces!') + + # For mdns-repeater to work it is essential that the interfaces has + # an IPv4 address assigned + for interface in mdns['interface']: + if interface not in interfaces(): + raise ConfigError(f'Interface "{interface}" does not exist!') + + if AF_INET not in ifaddresses(interface): + raise ConfigError('mDNS repeater requires an IPv4 address to be ' + f'configured on interface "{interface}"') + + return None + +def generate(mdns): + if not mdns: + return None + + if 'disable' in mdns: + print('Warning: mDNS repeater will be deactivated because it is disabled') + return None + + render(config_file, 'mdns-repeater/mdns-repeater.tmpl', mdns) + return None + +def apply(mdns): + if not mdns or 'disable' in mdns: + call('systemctl stop mdns-repeater.service') + if os.path.exists(config_file): + os.unlink(config_file) + else: + call('systemctl restart mdns-repeater.service') + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) -- cgit v1.2.3 From f75f9cf6aae10c061e7b757b4e8a658b4457def7 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 30 Jul 2020 22:54:48 +0200 Subject: router-advert: T2745: migrate to get_config_dict() --- data/templates/router-advert/radvd.conf.tmpl | 72 +++++---- interface-definitions/service_router-advert.xml.in | 11 +- src/conf_mode/service_router-advert.py | 163 ++++++--------------- 3 files changed, 98 insertions(+), 148 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/router-advert/radvd.conf.tmpl b/data/templates/router-advert/radvd.conf.tmpl index 073623eac..cebfc54b5 100644 --- a/data/templates/router-advert/radvd.conf.tmpl +++ b/data/templates/router-advert/radvd.conf.tmpl @@ -1,37 +1,47 @@ ### Autogenerated by service_router-advert.py ### -{% for i in interfaces -%} -interface {{ i.name }} { +{% if interface is defined and interface is not none %} +{% for iface in interface %} +interface {{ iface }} { IgnoreIfMissing on; - AdvDefaultPreference {{ i.default_preference }}; - AdvManagedFlag {{ i.managed_flag }}; - MaxRtrAdvInterval {{ i.interval_max }}; -{% if i.interval_min %} - MinRtrAdvInterval {{ i.interval_min }}; -{% endif %} - AdvReachableTime {{ i.reachable_time }}; - AdvIntervalOpt {{ i.send_advert }}; - AdvSendAdvert {{ i.send_advert }}; -{% if i.default_lifetime %} - AdvDefaultLifetime {{ i.default_lifetime }}; -{% endif %} -{% if i.link_mtu %} - AdvLinkMTU {{ i.link_mtu }}; -{% endif %} - AdvOtherConfigFlag {{ i.other_config_flag }}; - AdvRetransTimer {{ i.retrans_timer }}; - AdvCurHopLimit {{ i.hop_limit }}; -{% for p in i.prefixes %} - prefix {{ p.prefix }} { - AdvAutonomous {{ p.autonomous_flag }}; - AdvValidLifetime {{ p.valid_lifetime }}; - AdvOnLink {{ p.on_link }}; - AdvPreferredLifetime {{ p.preferred_lifetime }}; +{% if interface[iface].default_preference is defined and interface[iface].default_preference is not none %} + AdvDefaultPreference {{ interface[iface].default_preference }}; +{% endif %} +{% if interface[iface].managed_flag is defined and interface[iface].managed_flag is not none %} + AdvManagedFlag {{ 'on' if interface[iface].managed_flag is defined else 'off' }}; +{% endif %} +{% if interface[iface].interval.max is defined and interface[iface].interval.max is not none %} + MaxRtrAdvInterval {{ interface[iface].interval.max }}; +{% endif %} +{% if interface[iface].interval.min is defined and interface[iface].interval.min is not none %} + MinRtrAdvInterval {{ interface[iface].interval.min }}; +{% endif %} +{% if interface[iface].reachable_time is defined and interface[iface].reachable_time is not none %} + AdvReachableTime {{ interface[iface].reachable_time }}; +{% endif %} + AdvIntervalOpt {{ 'off' if interface[iface].no_send_advert is defined else 'on' }}; + AdvSendAdvert {{ 'off' if interface[iface].no_send_advert is defined else 'on' }}; +{% if interface[iface].default_lifetime is defined %} + AdvDefaultLifetime {{ interface[iface].default_lifetime }}; +{% endif %} +{% if interface[iface].link_mtu is defined %} + AdvLinkMTU {{ interface[iface].link_mtu }}; +{% endif %} + AdvOtherConfigFlag {{ 'on' if interface[iface].other_config_flag is defined else 'off' }}; + AdvRetransTimer {{ interface[iface].retrans_timer }}; + AdvCurHopLimit {{ interface[iface].hop_limit }}; +{% for prefix in interface[iface].prefix %} + prefix {{ prefix }} { + AdvAutonomous {{ 'off' if interface[iface].prefix[prefix].no_autonomous_flag is defined else 'on' }}; + AdvValidLifetime {{ interface[iface].prefix[prefix].valid_lifetime }}; + AdvOnLink {{ 'off' if interface[iface].prefix[prefix].no_on_link_flag is defined else 'on' }}; + AdvPreferredLifetime {{ interface[iface].prefix[prefix].preferred_lifetime }}; }; -{% endfor %} -{% if i.name_server %} - RDNSS {{ i.name_server | join(" ") }} { +{% endfor %} +{% if interface[iface].name_server is defined %} + RDNSS {{ interface[iface].name_server | join(" ") }} { }; -{% endif %} +{% endif %} }; -{% endfor -%} +{% endfor -%} +{% endif %} diff --git a/interface-definitions/service_router-advert.xml.in b/interface-definitions/service_router-advert.xml.in index 6a4706ab7..5a472fc9a 100644 --- a/interface-definitions/service_router-advert.xml.in +++ b/interface-definitions/service_router-advert.xml.in @@ -32,6 +32,7 @@ Hop count must be between 0 and 255 + 64 @@ -69,10 +70,11 @@ Default router has high preference - (low|medium|high) + ^(low|medium|high)$ Default preference must be low, medium or high + medium @@ -116,6 +118,7 @@ Maximum interval must be between 4 and 1800 seconds + 600 @@ -191,9 +194,10 @@ - (infinity) + ^(infinity)$ + 14400 @@ -214,6 +218,7 @@ (infinity) + 2592000 @@ -233,6 +238,7 @@ Reachable time must be 0 or between 1 and 3600000 milliseconds + 0 @@ -250,6 +256,7 @@ Retransmit interval must be 0 or between 1 and 4294967295 milliseconds + 0 diff --git a/src/conf_mode/service_router-advert.py b/src/conf_mode/service_router-advert.py index ef6148ebd..1b333e1a5 100755 --- a/src/conf_mode/service_router-advert.py +++ b/src/conf_mode/service_router-advert.py @@ -16,145 +16,77 @@ import os -from stat import S_IRUSR, S_IWUSR, S_IRGRP from sys import exit from vyos.config import Config -from vyos import ConfigError -from vyos.util import call +from vyos.configdict import dict_merge from vyos.template import render - +from vyos.util import call +from vyos.xml import defaults +from vyos import ConfigError from vyos import airbag airbag.enable() config_file = r'/run/radvd/radvd.conf' -default_config_data = { - 'interfaces': [] -} - def get_config(): - rtradv = default_config_data conf = Config() - base_level = ['service', 'router-advert'] - - if not conf.exists(base_level): - return rtradv - - for interface in conf.list_nodes(base_level + ['interface']): - intf = { - 'name': interface, - 'hop_limit' : '64', - 'default_lifetime': '', - 'default_preference': 'medium', - 'dnssl': [], - 'link_mtu': '', - 'managed_flag': 'off', - 'interval_max': '600', - 'interval_min': '', - 'name_server': [], - 'other_config_flag': 'off', - 'prefixes' : [], - 'reachable_time': '0', - 'retrans_timer': '0', - 'send_advert': 'on' - } - - # set config level first to reduce boilerplate code - conf.set_level(base_level + ['interface', interface]) - - if conf.exists(['hop-limit']): - intf['hop_limit'] = conf.return_value(['hop-limit']) - - if conf.exists(['default-lifetime']): - intf['default_lifetime'] = conf.return_value(['default-lifetime']) - - if conf.exists(['default-preference']): - intf['default_preference'] = conf.return_value(['default-preference']) - - if conf.exists(['dnssl']): - intf['dnssl'] = conf.return_values(['dnssl']) - - if conf.exists(['link-mtu']): - intf['link_mtu'] = conf.return_value(['link-mtu']) - - if conf.exists(['managed-flag']): - intf['managed_flag'] = 'on' - - if conf.exists(['interval', 'max']): - intf['interval_max'] = conf.return_value(['interval', 'max']) - - if conf.exists(['interval', 'min']): - intf['interval_min'] = conf.return_value(['interval', 'min']) - - if conf.exists(['name-server']): - intf['name_server'] = conf.return_values(['name-server']) - - if conf.exists(['other-config-flag']): - intf['other_config_flag'] = 'on' - - if conf.exists(['reachable-time']): - intf['reachable_time'] = conf.return_value(['reachable-time']) - - if conf.exists(['retrans-timer']): - intf['retrans_timer'] = conf.return_value(['retrans-timer']) - - if conf.exists(['no-send-advert']): - intf['send_advert'] = 'off' - - for prefix in conf.list_nodes(['prefix']): - tmp = { - 'prefix' : prefix, - 'autonomous_flag' : 'on', - 'on_link' : 'on', - 'preferred_lifetime': 14400, - 'valid_lifetime' : 2592000 - - } - - # set config level first to reduce boilerplate code - conf.set_level(base_level + ['interface', interface, 'prefix', prefix]) - - if conf.exists(['no-autonomous-flag']): - tmp['autonomous_flag'] = 'off' - - if conf.exists(['no-on-link-flag']): - tmp['on_link'] = 'off' - - if conf.exists(['preferred-lifetime']): - tmp['preferred_lifetime'] = int(conf.return_value(['preferred-lifetime'])) - - if conf.exists(['valid-lifetime']): - tmp['valid_lifetime'] = int(conf.return_value(['valid-lifetime'])) - - intf['prefixes'].append(tmp) - - rtradv['interfaces'].append(intf) + base = ['service', 'router-advert'] + rtradv = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # We have gathered the dict representation of the CLI, but there are default + # options which we need to update into the dictionary retrived. + default_interface_values = defaults(base + ['interface']) + # we deal with prefix defaults later on + if 'prefix' in default_interface_values: + del default_interface_values['prefix'] + + default_prefix_values = defaults(base + ['interface', 'prefix']) + + if 'interface' in rtradv: + for interface in rtradv['interface']: + rtradv['interface'][interface] = dict_merge( + default_interface_values, rtradv['interface'][interface]) + + if 'prefix' in rtradv['interface'][interface]: + for prefix in rtradv['interface'][interface]['prefix']: + rtradv['interface'][interface]['prefix'][prefix] = dict_merge( + default_prefix_values, rtradv['interface'][interface]['prefix'][prefix]) + + if 'name_server' in rtradv['interface'][interface]: + # always use a list when dealing with nameservers - eases the template generation + if isinstance(rtradv['interface'][interface]['name_server'], str): + rtradv['interface'][interface]['name_server'] = [ + rtradv['interface'][interface]['name_server']] return rtradv def verify(rtradv): - for interface in rtradv['interfaces']: - for prefix in interface['prefixes']: - if not (prefix['valid_lifetime'] > prefix['preferred_lifetime']): - raise ConfigError('Prefix valid-lifetime must be greater then preferred-lifetime') + if not rtradv: + return None + + if 'interface' not in rtradv: + return None + + for interface in rtradv['interface']: + interface = rtradv['interface'][interface] + if 'prefix' in interface: + for prefix in interface['prefix']: + prefix = interface['prefix'][prefix] + if not (int(prefix['valid_lifetime']) > int(prefix['preferred_lifetime'])): + raise ConfigError('Prefix valid-lifetime must be greater then preferred-lifetime') return None def generate(rtradv): - if not rtradv['interfaces']: + if not rtradv: return None - render(config_file, 'router-advert/radvd.conf.tmpl', rtradv, trim_blocks=True) - - # adjust file permissions of new configuration file - if os.path.exists(config_file): - os.chmod(config_file, S_IRUSR | S_IWUSR | S_IRGRP) - + render(config_file, 'router-advert/radvd.conf.tmpl', rtradv, trim_blocks=True, permission=0o644) return None def apply(rtradv): - if not rtradv['interfaces']: + if not rtradv: # bail out early - looks like removal from running config call('systemctl stop radvd.service') if os.path.exists(config_file): @@ -163,6 +95,7 @@ def apply(rtradv): return None call('systemctl restart radvd.service') + return None if __name__ == '__main__': -- cgit v1.2.3 From ea10917897bb4478e7f6f576f2e1c42b134cc5c0 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 2 Aug 2020 14:58:42 +0200 Subject: accel-ppp: T2756: make RADIUS accounting port configurable Make the port used for RADIUS accounting user configurable. This is now valid for the following services which are based on Accel-PPP: * ipoe-server * pppoe-server * l2tp * pptp * sstp --- data/templates/accel-ppp/ipoe.config.tmpl | 2 +- data/templates/accel-ppp/l2tp.config.tmpl | 2 +- data/templates/accel-ppp/pppoe.config.tmpl | 2 +- data/templates/accel-ppp/pptp.config.tmpl | 2 +- data/templates/accel-ppp/sstp.config.tmpl | 2 +- interface-definitions/include/accel-radius-additions.xml.in | 12 ++++++++++++ src/conf_mode/service_ipoe-server.py | 6 +++++- src/conf_mode/service_pppoe-server.py | 6 +++++- src/conf_mode/vpn_l2tp.py | 6 +++++- src/conf_mode/vpn_pptp.py | 6 +++++- src/conf_mode/vpn_sstp.py | 6 +++++- 11 files changed, 42 insertions(+), 10 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/accel-ppp/ipoe.config.tmpl b/data/templates/accel-ppp/ipoe.config.tmpl index 84de5bf51..fca520efa 100644 --- a/data/templates/accel-ppp/ipoe.config.tmpl +++ b/data/templates/accel-ppp/ipoe.config.tmpl @@ -77,7 +77,7 @@ chap-secrets={{ chap_secrets_file }} [radius] verbose=1 {% for r in radius_server %} -server={{ r.server }},{{ r.key }},auth-port={{ r.port }},req-limit=0,fail-time={{ r.fail_time }} +server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }} {% endfor -%} acct-timeout={{ radius_acct_tmo }} diff --git a/data/templates/accel-ppp/l2tp.config.tmpl b/data/templates/accel-ppp/l2tp.config.tmpl index b0ef17525..b9131684d 100644 --- a/data/templates/accel-ppp/l2tp.config.tmpl +++ b/data/templates/accel-ppp/l2tp.config.tmpl @@ -83,7 +83,7 @@ chap-secrets={{ chap_secrets_file }} [radius] verbose=1 {% for r in radius_server %} -server={{ r.server }},{{ r.key }},auth-port={{ r.port }},req-limit=0,fail-time={{ r.fail_time }} +server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }} {% endfor -%} acct-timeout={{ radius_acct_tmo }} diff --git a/data/templates/accel-ppp/pppoe.config.tmpl b/data/templates/accel-ppp/pppoe.config.tmpl index 370ca7946..5ad628fde 100644 --- a/data/templates/accel-ppp/pppoe.config.tmpl +++ b/data/templates/accel-ppp/pppoe.config.tmpl @@ -93,7 +93,7 @@ chap-secrets={{ chap_secrets_file }} [radius] verbose=1 {% for r in radius_server %} -server={{ r.server }},{{ r.key }},auth-port={{ r.port }},req-limit=0,fail-time={{ r.fail_time }} +server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }} {% endfor -%} acct-timeout={{ radius_acct_tmo }} diff --git a/data/templates/accel-ppp/pptp.config.tmpl b/data/templates/accel-ppp/pptp.config.tmpl index 0bbfc13c5..e0f2c6da9 100644 --- a/data/templates/accel-ppp/pptp.config.tmpl +++ b/data/templates/accel-ppp/pptp.config.tmpl @@ -66,7 +66,7 @@ chap-secrets={{ chap_secrets_file }} [radius] verbose=1 {% for r in radius_server %} -server={{ r.server }},{{ r.key }},auth-port={{ r.port }},req-limit=0,fail-time={{ r.fail_time }} +server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }} {% endfor -%} acct-timeout={{ radius_acct_tmo }} diff --git a/data/templates/accel-ppp/sstp.config.tmpl b/data/templates/accel-ppp/sstp.config.tmpl index 2c8c00023..c9e4a1d7d 100644 --- a/data/templates/accel-ppp/sstp.config.tmpl +++ b/data/templates/accel-ppp/sstp.config.tmpl @@ -69,7 +69,7 @@ chap-secrets={{ chap_secrets_file }} [radius] verbose=1 {% for r in radius_server %} -server={{ r.server }},{{ r.key }},auth-port={{ r.port }},req-limit=0,fail-time={{ r.fail_time }} +server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }} {% endfor -%} acct-timeout={{ radius_acct_tmo }} diff --git a/interface-definitions/include/accel-radius-additions.xml.in b/interface-definitions/include/accel-radius-additions.xml.in index 227a043cd..e37b68514 100644 --- a/interface-definitions/include/accel-radius-additions.xml.in +++ b/interface-definitions/include/accel-radius-additions.xml.in @@ -2,6 +2,18 @@ + + + Accounting port + + 1-65535 + Numeric IP port (default: 1813) + + + + + + Mark server unavailable for <n> seconds on failure diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py index fbc1000dd..553cc2e97 100755 --- a/src/conf_mode/service_ipoe-server.py +++ b/src/conf_mode/service_ipoe-server.py @@ -147,7 +147,8 @@ def get_config(): 'server' : server, 'key' : '', 'fail_time' : 0, - 'port' : '1812' + 'port' : '1812', + 'acct_port' : '1813' } conf.set_level(base_path + ['authentication', 'radius', 'server', server]) @@ -158,6 +159,9 @@ def get_config(): if conf.exists(['port']): radius['port'] = conf.return_value(['port']) + if conf.exists(['acct-port']): + radius['acct_port'] = conf.return_value(['acct-port']) + if conf.exists(['key']): radius['key'] = conf.return_value(['key']) diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py index 3149bbb2f..a8357f653 100755 --- a/src/conf_mode/service_pppoe-server.py +++ b/src/conf_mode/service_pppoe-server.py @@ -242,7 +242,8 @@ def get_config(): 'server' : server, 'key' : '', 'fail_time' : 0, - 'port' : '1812' + 'port' : '1812', + 'acct_port' : '1813' } conf.set_level(base_path + ['authentication', 'radius', 'server', server]) @@ -253,6 +254,9 @@ def get_config(): if conf.exists(['port']): radius['port'] = conf.return_value(['port']) + if conf.exists(['acct-port']): + radius['acct_port'] = conf.return_value(['acct-port']) + if conf.exists(['key']): radius['key'] = conf.return_value(['key']) diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py index 88df2902e..26ad1af84 100755 --- a/src/conf_mode/vpn_l2tp.py +++ b/src/conf_mode/vpn_l2tp.py @@ -151,7 +151,8 @@ def get_config(): 'server' : server, 'key' : '', 'fail_time' : 0, - 'port' : '1812' + 'port' : '1812', + 'acct_port' : '1813' } conf.set_level(base_path + ['authentication', 'radius', 'server', server]) @@ -162,6 +163,9 @@ def get_config(): if conf.exists(['port']): radius['port'] = conf.return_value(['port']) + if conf.exists(['acct-port']): + radius['acct_port'] = conf.return_value(['acct-port']) + if conf.exists(['key']): radius['key'] = conf.return_value(['key']) diff --git a/src/conf_mode/vpn_pptp.py b/src/conf_mode/vpn_pptp.py index 4536692d2..32cbadd74 100755 --- a/src/conf_mode/vpn_pptp.py +++ b/src/conf_mode/vpn_pptp.py @@ -111,7 +111,8 @@ def get_config(): 'server' : server, 'key' : '', 'fail_time' : 0, - 'port' : '1812' + 'port' : '1812', + 'acct_port' : '1813' } conf.set_level(base_path + ['authentication', 'radius', 'server', server]) @@ -122,6 +123,9 @@ def get_config(): if conf.exists(['port']): radius['port'] = conf.return_value(['port']) + if conf.exists(['acct-port']): + radius['acct_port'] = conf.return_value(['acct-port']) + if conf.exists(['key']): radius['key'] = conf.return_value(['key']) diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py index 4c4d8e403..ddb499bf4 100755 --- a/src/conf_mode/vpn_sstp.py +++ b/src/conf_mode/vpn_sstp.py @@ -118,7 +118,8 @@ def get_config(): 'server' : server, 'key' : '', 'fail_time' : 0, - 'port' : '1812' + 'port' : '1812', + 'acct_port' : '1813' } conf.set_level(base_path + ['authentication', 'radius', 'server', server]) @@ -129,6 +130,9 @@ def get_config(): if conf.exists(['port']): radius['port'] = conf.return_value(['port']) + if conf.exists(['acct-port']): + radius['acct_port'] = conf.return_value(['acct-port']) + if conf.exists(['key']): radius['key'] = conf.return_value(['key']) -- cgit v1.2.3 From 43a7f4e8032e5285f3dd99785f1b41e3c2dd1ad0 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 3 Aug 2020 17:10:41 +0200 Subject: lldp: T393: improve all completion helper --- interface-definitions/lldp.xml.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'interface-definitions') diff --git a/interface-definitions/lldp.xml.in b/interface-definitions/lldp.xml.in index 3a2899b57..8f6629d81 100644 --- a/interface-definitions/lldp.xml.in +++ b/interface-definitions/lldp.xml.in @@ -21,6 +21,7 @@ + all @@ -73,7 +74,7 @@ Datum should be WGS84, NAD83, or MLLW - (WGS84|NAD83|MLLW)$ + ^(WGS84|NAD83|MLLW)$ -- cgit v1.2.3 From 4526ceadf4a9a81b51ec394970188daccd69539b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 4 Aug 2020 08:34:52 +0200 Subject: cleanup: convert multiple files from DOS to UNIX line ending --- data/templates/frr/bgp.frr.tmpl | 2 +- data/templates/system/curlrc.tmpl | 16 +++++----- .../include/bgp-afi-aggregate-address.xml.i | 24 +++++++-------- .../systemd/system/radvd.service.d/override.conf | 34 +++++++++++----------- src/systemd/wpa_supplicant-macsec@.service | 34 +++++++++++----------- 5 files changed, 55 insertions(+), 55 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/frr/bgp.frr.tmpl b/data/templates/frr/bgp.frr.tmpl index cd6f31c93..cdf4cb4fe 100644 --- a/data/templates/frr/bgp.frr.tmpl +++ b/data/templates/frr/bgp.frr.tmpl @@ -1 +1 @@ -! +! diff --git a/data/templates/system/curlrc.tmpl b/data/templates/system/curlrc.tmpl index 675e35a0c..3e5ce801c 100644 --- a/data/templates/system/curlrc.tmpl +++ b/data/templates/system/curlrc.tmpl @@ -1,8 +1,8 @@ -{% if http_client is defined %} -{% if http_client.source_interface is defined %} ---interface "{{ http_client.source_interface }}" -{% endif %} -{% if http_client.source_address is defined %} ---interface "{{ http_client.source_address }}" -{% endif %} -{% endif %} +{% if http_client is defined %} +{% if http_client.source_interface is defined %} +--interface "{{ http_client.source_interface }}" +{% endif %} +{% if http_client.source_address is defined %} +--interface "{{ http_client.source_address }}" +{% endif %} +{% endif %} diff --git a/interface-definitions/include/bgp-afi-aggregate-address.xml.i b/interface-definitions/include/bgp-afi-aggregate-address.xml.i index 40c030fc1..050ee0074 100644 --- a/interface-definitions/include/bgp-afi-aggregate-address.xml.i +++ b/interface-definitions/include/bgp-afi-aggregate-address.xml.i @@ -1,12 +1,12 @@ - - - Generate AS-set path information for this aggregate address - - - - - - Announce the aggregate summary network only - - - + + + Generate AS-set path information for this aggregate address + + + + + + Announce the aggregate summary network only + + + diff --git a/src/etc/systemd/system/radvd.service.d/override.conf b/src/etc/systemd/system/radvd.service.d/override.conf index 44c4345e1..c2f640cf5 100644 --- a/src/etc/systemd/system/radvd.service.d/override.conf +++ b/src/etc/systemd/system/radvd.service.d/override.conf @@ -1,17 +1,17 @@ -[Unit] -ConditionPathExists=/run/radvd/radvd.conf -After= -After=vyos-router.service - -[Service] -WorkingDirectory= -WorkingDirectory=/run/radvd -ExecStartPre= -ExecStartPre=/usr/sbin/radvd --logmethod stderr_clean --configtest --config /run/radvd/radvd.conf -ExecStart= -ExecStart=/usr/sbin/radvd --logmethod stderr_clean --config /run/radvd/radvd.conf --pidfile /run/radvd/radvd.pid -ExecReload= -ExecReload=/usr/sbin/radvd --logmethod stderr_clean --configtest --config /run/radvd/radvd.conf -ExecReload=/bin/kill -HUP $MAINPID -PIDFile= -PIDFile=/run/radvd/radvd.pid +[Unit] +ConditionPathExists=/run/radvd/radvd.conf +After= +After=vyos-router.service + +[Service] +WorkingDirectory= +WorkingDirectory=/run/radvd +ExecStartPre= +ExecStartPre=/usr/sbin/radvd --logmethod stderr_clean --configtest --config /run/radvd/radvd.conf +ExecStart= +ExecStart=/usr/sbin/radvd --logmethod stderr_clean --config /run/radvd/radvd.conf --pidfile /run/radvd/radvd.pid +ExecReload= +ExecReload=/usr/sbin/radvd --logmethod stderr_clean --configtest --config /run/radvd/radvd.conf +ExecReload=/bin/kill -HUP $MAINPID +PIDFile= +PIDFile=/run/radvd/radvd.pid diff --git a/src/systemd/wpa_supplicant-macsec@.service b/src/systemd/wpa_supplicant-macsec@.service index 21e189e4b..7e0bee8e1 100644 --- a/src/systemd/wpa_supplicant-macsec@.service +++ b/src/systemd/wpa_supplicant-macsec@.service @@ -1,17 +1,17 @@ -[Unit] -Description=WPA supplicant daemon (macsec-specific version) -Requires=sys-subsystem-net-devices-%i.device -ConditionPathExists=/run/wpa_supplicant/%I.conf -After=vyos-router.service -RequiresMountsFor=/run - -# NetworkManager users will probably want the dbus version instead. - -[Service] -Type=simple -WorkingDirectory=/run/wpa_supplicant -PIDFile=/run/wpa_supplicant/%I.pid -ExecStart=/sbin/wpa_supplicant -c/run/wpa_supplicant/%I.conf -Dmacsec_linux -i%I - -[Install] -WantedBy=multi-user.target +[Unit] +Description=WPA supplicant daemon (macsec-specific version) +Requires=sys-subsystem-net-devices-%i.device +ConditionPathExists=/run/wpa_supplicant/%I.conf +After=vyos-router.service +RequiresMountsFor=/run + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +WorkingDirectory=/run/wpa_supplicant +PIDFile=/run/wpa_supplicant/%I.pid +ExecStart=/sbin/wpa_supplicant -c/run/wpa_supplicant/%I.conf -Dmacsec_linux -i%I + +[Install] +WantedBy=multi-user.target -- cgit v1.2.3 From 846e306700af191d22dc874992bbf5f04d2799c4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 4 Aug 2020 22:33:18 +0200 Subject: ssh: T2651: add cli options for source address When running SSH from the VyOS system the source IP address can be set by: set system options ssh-client source-address x.x.x.x --- data/templates/system/ssh_config.tmpl | 3 +++ interface-definitions/system-options.xml.in | 10 +++++++++- src/conf_mode/system-options.py | 31 ++++++++++++++++++++++++----- 3 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 data/templates/system/ssh_config.tmpl (limited to 'interface-definitions') diff --git a/data/templates/system/ssh_config.tmpl b/data/templates/system/ssh_config.tmpl new file mode 100644 index 000000000..509bd5479 --- /dev/null +++ b/data/templates/system/ssh_config.tmpl @@ -0,0 +1,3 @@ +{% if ssh_client is defined and ssh_client.source_address is defined and ssh_client.source_address is not none %} +BindAddress {{ ssh_client.source_address }} +{% endif %} diff --git a/interface-definitions/system-options.xml.in b/interface-definitions/system-options.xml.in index 194773329..a5fec10db 100644 --- a/interface-definitions/system-options.xml.in +++ b/interface-definitions/system-options.xml.in @@ -46,13 +46,21 @@ - Global options used for HTTP based commands + Global options used for HTTP client #include #include + + + Global options used for SSH client + + + #include + + diff --git a/src/conf_mode/system-options.py b/src/conf_mode/system-options.py index d7c5c0443..0aacd19d8 100755 --- a/src/conf_mode/system-options.py +++ b/src/conf_mode/system-options.py @@ -22,11 +22,13 @@ from sys import exit from vyos.config import Config from vyos.template import render from vyos.util import call +from vyos.validate import is_addr_assigned from vyos import ConfigError from vyos import airbag airbag.enable() -config_file = r'/etc/curlrc' +curlrc_config = r'/etc/curlrc' +ssh_config = r'/etc/ssh/ssh_config' systemd_action_file = '/lib/systemd/system/ctrl-alt-del.target' def get_config(): @@ -36,9 +38,9 @@ def get_config(): return options def verify(options): - if 'http_client' in options.keys(): + if 'http_client' in options: config = options['http_client'] - if 'source_interface' in config.keys(): + if 'source_interface' in config: if not config['source_interface'] in interfaces(): raise ConfigError(f'Source interface {source_interface} does not ' f'exist'.format(**config)) @@ -46,10 +48,21 @@ def verify(options): if {'source_address', 'source_interface'} <= set(config): raise ConfigError('Can not define both HTTP source-interface and source-address') + if 'source_address' in config: + if not is_addr_assigned(config['source_address']): + raise ConfigError('No interface with give address specified!') + + if 'ssh_client' in options: + config = options['ssh_client'] + if 'source_address' in config: + if not is_addr_assigned(config['source_address']): + raise ConfigError('No interface with give address specified!') + return None def generate(options): - render(config_file, 'system/curlrc.tmpl', options, trim_blocks=True) + render(curlrc_config, 'system/curlrc.tmpl', options, trim_blocks=True) + render(ssh_config, 'system/ssh_config.tmpl', options, trim_blocks=True) return None def apply(options): @@ -63,12 +76,20 @@ def apply(options): if os.path.exists(systemd_action_file): os.unlink(systemd_action_file) - if 'ctrl_alt_del_action' in options.keys(): + if 'ctrl_alt_del_action' in options: if options['ctrl_alt_del_action'] == 'reboot': os.symlink('/lib/systemd/system/reboot.target', systemd_action_file) elif options['ctrl_alt_del_action'] == 'poweroff': os.symlink('/lib/systemd/system/poweroff.target', systemd_action_file) + if 'http_client' not in options: + if os.path.exists(curlrc_config): + os.unlink(curlrc_config) + + if 'ssh_client' not in options: + if os.path.exists(ssh_config): + os.unlink(ssh_config) + # Reboot system on kernel panic with open('/proc/sys/kernel/panic', 'w') as f: if 'reboot_on_panic' in options.keys(): -- cgit v1.2.3 From dbc682e40a0b3c93e3005d6b3d4e4c34965bba7d Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Thu, 6 Aug 2020 17:55:10 +0200 Subject: nat: T2764: increase maximum rule number to 999999 --- interface-definitions/include/nat-rule.xml.i | 6 +++--- interface-definitions/nat.xml.in | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/include/nat-rule.xml.i b/interface-definitions/include/nat-rule.xml.i index f62a08987..a2d058479 100644 --- a/interface-definitions/include/nat-rule.xml.i +++ b/interface-definitions/include/nat-rule.xml.i @@ -2,13 +2,13 @@ Rule number for NAT - 1-9999 + 1-999999 Number for this NAT rule - + - NAT rule number must be between 1 and 9999 + NAT rule number must be between 1 and 999999 diff --git a/interface-definitions/nat.xml.in b/interface-definitions/nat.xml.in index f8415b7c0..8a14f4d25 100644 --- a/interface-definitions/nat.xml.in +++ b/interface-definitions/nat.xml.in @@ -61,13 +61,13 @@ NPTv6 rule number - 1-9999 + 1-999999 Number for this rule - + - NAT rule number must be between 1 and 9999 + NAT rule number must be between 1 and 999999 -- cgit v1.2.3 From b082a6fb211ef19d75c4c81414be9aa1b9248b45 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 13 Aug 2020 18:31:08 +0200 Subject: lcd: T2564: flatten CLI interface * set system lcd device * set system lcd model Both device and model have completion helpers for supported interfaces and LCD displays. --- data/templates/lcd/LCDd.conf.tmpl | 126 ++ data/templates/lcd/lcdproc.conf.tmpl | 60 + data/templates/system-display/LCDd.conf.tmpl | 1500 -------------------- data/templates/system-display/lcdproc.conf.tmpl | 173 --- interface-definitions/system-display.xml.in | 235 --- interface-definitions/system-lcd.xml.in | 62 + python/vyos/util.py | 12 + src/conf_mode/interfaces-wirelessmodem.py | 12 +- src/conf_mode/system-display.py | 98 -- src/conf_mode/system_lcd.py | 84 ++ .../systemd/system/LCDd.service.d/override.conf | 8 + .../system/hostapd@.service.d/override.conf | 7 +- src/systemd/LCDd@.service | 10 - src/systemd/lcdproc.service | 13 + src/systemd/lcdproc@.service | 10 - 15 files changed, 369 insertions(+), 2041 deletions(-) create mode 100644 data/templates/lcd/LCDd.conf.tmpl create mode 100644 data/templates/lcd/lcdproc.conf.tmpl delete mode 100644 data/templates/system-display/LCDd.conf.tmpl delete mode 100644 data/templates/system-display/lcdproc.conf.tmpl delete mode 100644 interface-definitions/system-display.xml.in create mode 100644 interface-definitions/system-lcd.xml.in delete mode 100755 src/conf_mode/system-display.py create mode 100755 src/conf_mode/system_lcd.py create mode 100644 src/etc/systemd/system/LCDd.service.d/override.conf delete mode 100644 src/systemd/LCDd@.service create mode 100644 src/systemd/lcdproc.service delete mode 100644 src/systemd/lcdproc@.service (limited to 'interface-definitions') diff --git a/data/templates/lcd/LCDd.conf.tmpl b/data/templates/lcd/LCDd.conf.tmpl new file mode 100644 index 000000000..da749d04a --- /dev/null +++ b/data/templates/lcd/LCDd.conf.tmpl @@ -0,0 +1,126 @@ +### Autogenerted by system-display.py ## + +# LCDd.conf -- configuration file for the LCDproc server daemon LCDd +# +# This file contains the configuration for the LCDd server. +# +# The format is ini-file-like. It is divided into sections that start at +# markers that look like [section]. Comments are all line-based comments, +# and are lines that start with '#' or ';'. +# +# The server has a 'central' section named [server]. For the menu there is +# a section called [menu]. Further each driver has a section which +# defines how the driver acts. +# +# The drivers are activated by specifying them in a driver= line in the +# server section, like: +# +# Driver=curses +# +# This tells LCDd to use the curses driver. +# The first driver that is loaded and is capable of output defines the +# size of the display. The default driver to use is curses. +# If the driver is specified using the -d command line option, +# the Driver= options in the config file are ignored. +# +# The drivers read their own options from the respective sections. + +## Server section with all kinds of settings for the LCDd server ## +[server] + +# Where can we find the driver modules ? +# NOTE: Always place a slash as last character ! +DriverPath=/usr/lib/x86_64-linux-gnu/lcdproc/ + +# Tells the server to load the given drivers. Multiple lines can be given. +# The name of the driver is case sensitive and determines the section +# where to look for further configuration options of the specific driver +# as well as the name of the dynamic driver module to load at runtime. +# The latter one can be changed by giving a File= directive in the +# driver specific section. +# +# The following drivers are supported: +# bayrad, CFontz, CFontzPacket, curses, CwLnx, ea65, EyeboxOne, futaba, +# g15, glcd, glcdlib, glk, hd44780, icp_a106, imon, imonlcd,, IOWarrior, +# irman, joy, lb216, lcdm001, lcterm, linux_input, lirc, lis, MD8800, +# mdm166a, ms6931, mtc_s16209x, MtxOrb, mx5000, NoritakeVFD, +# Olimex_MOD_LCD1x9, picolcd, pyramid, rawserial, sdeclcd, sed1330, +# sed1520, serialPOS, serialVFD, shuttleVFD, sli, stv5730, svga, t6963, +# text, tyan, ula200, vlsys_m428, xosd, yard2LCD + +{% if model is defined and model.startswith('CFA-') %} +Driver=CFontzPacket +{% endif %} + +# Tells the driver to bind to the given interface. [default: 127.0.0.1] +Bind=127.0.0.1 + +# Listen on this specified port. [default: 13666] +Port=13666 + +# Sets the reporting level; defaults to warnings and errors only. +# [default: 2; legal: 0-5] +ReportLevel=3 + +# Should we report to syslog instead of stderr? [default: no; legal: yes, no] +ReportToSyslog=yes + +# User to run as. LCDd will drop its root privileges and run as this user +# instead. [default: nobody] +User=nobody + +# The server will stay in the foreground if set to yes. +# [default: no, legal: yes, no] +Foreground=yes + +# Hello message: each entry represents a display line; default: builtin +Hello="Starting VyOS" +Hello=" ... " + +# GoodBye message: each entry represents a display line; default: builtin +GoodBye=" VyOS shutting" +GoodBye=" down... " + +# Sets the interval in microseconds for updating the display. +# [default: 125000 meaning 8Hz] +FrameInterval=250000 # 4 updates per second + +# Sets the default time in seconds to displays a screen. [default: 4] +WaitTime=1 + +# If set to no, LCDd will start with screen rotation disabled. This has the +# same effect as if the ToggleRotateKey had been pressed. Rotation will start +# if the ToggleRotateKey is pressed. Note that this setting does not turn off +# priority sorting of screens. [default: on; legal: on, off] +AutoRotate=on + +# If yes, the the serverscreen will be rotated as a usual info screen. If no, +# it will be a background screen, only visible when no other screens are +# active. The special value 'blank' is similar to no, but only a blank screen +# is displayed. [default: on; legal: on, off, blank] +ServerScreen=blank + +# Set master backlight setting. If set to 'open' a client may control the +# backlight for its own screens (only). [default: open; legal: off, open, on] +Backlight=on + +# Set master heartbeat setting. If set to 'open' a client may control the +# heartbeat for its own screens (only). [default: open; legal: off, open, on] +Heartbeat=off + +# set title scrolling speed [default: 10; legal: 0-10] +TitleSpeed=10 + +{% if model is defined and model is not none %} +{% if model.startswith('CFA-') %} +## CrystalFontz packet driver (for CFA533, CFA631, CFA633 & CFA635) ## +[CFontzPacket] +Model={{ model.split('-')[1] }} +Device={{ device }} +Contrast=350 +Brightness=500 +OffBrightness=50 +Reboot=yes +USB=yes +{% endif %} +{% endif %} diff --git a/data/templates/lcd/lcdproc.conf.tmpl b/data/templates/lcd/lcdproc.conf.tmpl new file mode 100644 index 000000000..c79f3cd0d --- /dev/null +++ b/data/templates/lcd/lcdproc.conf.tmpl @@ -0,0 +1,60 @@ +### autogenerated by system-lcd.py ### + +# LCDproc client configuration file + +[lcdproc] +Server=127.0.0.1 +Port=13666 + +# set reporting level +ReportLevel=3 + +# report to to syslog ? +ReportToSyslog=true + +Foreground=yes + +[CPU] +Active=true +OnTime=1 +OffTime=2 +ShowInvisible=false + +[SMP-CPU] +Active=false + +[Memory] +Active=false + +[Load] +Active=false + +[Uptime] +Active=true + +[ProcSize] +Active=false + +[Disk] +Active=false + +[About] +Active=false + +[TimeDate] +Active=true +TimeFormat="%H:%M:%S" + +[OldTime] +Active=false + +[BigClock] +Active=false + +[MiniClock] +Active=false + +# Display the title bar in two-line mode. Note that with four lines or more +# the title is always shown. [default: true; legal: true, false] +ShowTitle=false + diff --git a/data/templates/system-display/LCDd.conf.tmpl b/data/templates/system-display/LCDd.conf.tmpl deleted file mode 100644 index 1dd646202..000000000 --- a/data/templates/system-display/LCDd.conf.tmpl +++ /dev/null @@ -1,1500 +0,0 @@ -### Autogenerted by system-display.py ## -# LCDd.conf -- configuration file for the LCDproc server daemon LCDd -# -# This file contains the configuration for the LCDd server. -# -# The format is ini-file-like. It is divided into sections that start at -# markers that look like [section]. Comments are all line-based comments, -# and are lines that start with '#' or ';'. -# -# The server has a 'central' section named [server]. For the menu there is -# a section called [menu]. Further each driver has a section which -# defines how the driver acts. -# -# The drivers are activated by specifying them in a driver= line in the -# server section, like: -# -# Driver=curses -# -# This tells LCDd to use the curses driver. -# The first driver that is loaded and is capable of output defines the -# size of the display. The default driver to use is curses. -# If the driver is specified using the -d command line option, -# the Driver= options in the config file are ignored. -# -# The drivers read their own options from the respective sections. - - - -## Server section with all kinds of settings for the LCDd server ## -[server] - -# Where can we find the driver modules ? -# IMPORTANT: Make sure to change this setting to reflect your -# specific setup! Otherwise LCDd won't be able to find -# the driver modules and will thus not be able to -# function properly. -# NOTE: Always place a slash as last character ! -DriverPath=/usr/lib/x86_64-linux-gnu/lcdproc/ - -# Tells the server to load the given drivers. Multiple lines can be given. -# The name of the driver is case sensitive and determines the section -# where to look for further configuration options of the specific driver -# as well as the name of the dynamic driver module to load at runtime. -# The latter one can be changed by giving a File= directive in the -# driver specific section. -# -# The following drivers are supported: -# bayrad, CFontz, CFontzPacket, curses, CwLnx, ea65, EyeboxOne, futaba, -# g15, glcd, glcdlib, glk, hd44780, icp_a106, imon, imonlcd,, IOWarrior, -# irman, joy, lb216, lcdm001, lcterm, linux_input, lirc, lis, MD8800, -# mdm166a, ms6931, mtc_s16209x, MtxOrb, mx5000, NoritakeVFD, -# Olimex_MOD_LCD1x9, picolcd, pyramid, rawserial, sdeclcd, sed1330, -# sed1520, serialPOS, serialVFD, shuttleVFD, sli, stv5730, svga, t6963, -# text, tyan, ula200, vlsys_m428, xosd, yard2LCD -{%- if model == 'sdec' %} -Driver=sdeclcd -{%- endif %} - -{%- if model == 'ezio' %} -Driver=hd44780 -{%- endif %} - -{%- if model == 'test' %} -Driver=CFontzPacket -{%- endif %} - -# Tells the driver to bind to the given interface. [default: 127.0.0.1] -#Bind=127.0.0.1 - -# Listen on this specified port. [default: 13666] -#Port=13666 - -# Sets the reporting level; defaults to warnings and errors only. -# [default: 2; legal: 0-5] -#ReportLevel=3 - -# Should we report to syslog instead of stderr? [default: no; legal: yes, no] -#ReportToSyslog=yes - -# User to run as. LCDd will drop its root privileges and run as this user -# instead. [default: nobody] -User=nobody - -# The server will stay in the foreground if set to yes. -# [default: no, legal: yes, no] -#Foreground=yes - -# Hello message: each entry represents a display line; default: builtin -Hello="{%- if hello %}{{ hello }}{%- else %}Welcome to VyOS{%- endif %}" - -# GoodBye message: each entry represents a display line; default: builtin -GoodBye="{%- if bye %}{{ bye }}{%- else %}Bye from VyOS{%- endif %}" - -# Sets the interval in microseconds for updating the display. -# [default: 125000 meaning 8Hz] -#FrameInterval=125000 - -# Sets the default time in seconds to displays a screen. [default: 4] -WaitTime={%- if time %}{{ time }}{%- else%}4{%- endif %} - -# If set to no, LCDd will start with screen rotation disabled. This has the -# same effect as if the ToggleRotateKey had been pressed. Rotation will start -# if the ToggleRotateKey is pressed. Note that this setting does not turn off -# priority sorting of screens. [default: on; legal: on, off] -#AutoRotate=off - -# If yes, the the serverscreen will be rotated as a usual info screen. If no, -# it will be a background screen, only visible when no other screens are -# active. The special value 'blank' is similar to no, but only a blank screen -# is displayed. [default: on; legal: on, off, blank] -ServerScreen=no - -# Set master backlight setting. If set to 'open' a client may control the -# backlight for its own screens (only). [default: open; legal: off, open, on] -#Backlight=open - -# Set master heartbeat setting. If set to 'open' a client may control the -# heartbeat for its own screens (only). [default: open; legal: off, open, on] -#Heartbeat=open - -# set title scrolling speed [default: 10; legal: 0-10] -#TitleSpeed=10 - -# The "...Key=" lines define what the server does with keypresses that -# don't go to any client. The ToggleRotateKey stops rotation of screens, while -# the PrevScreenKey and NextScreenKey go back / forward one screen (even if -# rotation is disabled. -# Assign the key string returned by the driver to the ...Key setting. These -# are the defaults: -ToggleRotateKey=Enter -PrevScreenKey=Left -NextScreenKey=Right -#ScrollUpKey=Up -#ScrollDownKey=Down - -## The menu section. The menu is an internal LCDproc client. ## -[menu] -# If true the server allows transitions between different client's menus -# [default: false; legal: true, false] -#PermissiveGoto=false - -# You can configure what keys the menu should use. Note that the MenuKey -# will be reserved exclusively, the others work in shared mode. - -# Up to six keys are supported. The MenuKey (to enter and exit the menu), the -# EnterKey (to select values) and at least one movement keys are required. -# These are the default key assignments: -MenuKey=Escape -EnterKey=Enter -UpKey=Up -DownKey=Down -#LeftKey=Left -#RightKey=Right - - -### Driver sections are below this line, in alphabetical order ### - - -## EMAC BayRAD driver ## -[bayrad] - -# Select the output device to use [default: /dev/lcd] -Device=/dev/lcd - -# Set the communication speed [default: 9600; legal: 1200, 2400, 9600, 19200] -Speed=9600 - - - -## CrystalFontz driver (for CF632 & CF634) ## -[CFontz] - -# Select the output device to use [default: /dev/lcd] -Device=/dev/ttyS0 -# Select the LCD size [default: 20x4] -Size=20x4 -# Set the initial contrast [default: 560; legal: 0 - 1000] -Contrast=350 -# Set the initial brightness [default: 1000; legal: 0 - 1000] -Brightness=1000 -# Set the initial off-brightness [default: 0; legal: 0 - 1000] -# This value is used when the display is normally -# switched off in case LCDd is inactive -OffBrightness=0 -# Set the communication speed [default: 9600; legal: 1200, 2400, 9600, 19200, -# 115200] -Speed=9600 -# Set the firmware version (New means >= 2.0) [default: no; legal: yes, no] -NewFirmware=no -# Reinitialize the LCD's BIOS [default: no; legal: yes, no] -# normally you shouldn't need this -Reboot=no - - - -## CrystalFontz packet driver (for CFA533, CFA631, CFA633 & CFA635) ## -[CFontzPacket] -{%- if model == 'test' %} -Model=533 -Device=/dev/serial/by-bus/usb0b1.1p1.0 -Contrast=350 -Brightness=1000 -OffBrightness=50 -Reboot=yes -USB=yes -{%- endif %} - - -## Curses driver ## -[curses] - -# color settings -# foreground color [default: blue] -Foreground=blue -# background color when "backlight" is off [default: cyan] -Background=cyan -# background color when "backlight" is on [default: red] -Backlight=red - -# display size [default: 20x4] -Size=20x2 - -# What position (X,Y) to start the left top corner at... -# Default: (7,7) -TopLeftX=7 -TopLeftY=7 - -# use ASC symbols for icons & bars [default: no; legal: yes, no] -UseACS=no - -# draw Border [default: yes; legal: yes, no] -DrawBorder=yes - - - -## Cwlinux driver ## -[CwLnx] - -# Select the LCD model [default: 12232; legal: 12232, 12832, 1602] -Model=12232 - -# Select the output device to use [default: /dev/lcd] -Device=/dev/ttyUSB0 - -# Select the LCD size. Default depends on model: -# 12232: 20x4 -# 12832: 21x4 -# 1602: 16x2 -Size=20x4 - -# Set the communication speed [default: 19200; legal: 9600, 19200] -Speed=19200 - -# Reinitialize the LCD's BIOS [default: no; legal: yes, no] -# normally you shouldn't need this -Reboot=no - -# If you have a keypad connected. Keypad layout is currently not -# configureable from the config file. -Keypad=yes - -# If you have a non-standard keypad you can associate any keystrings to keys. -# There are 6 input keys in the CwLnx hardware that generate characters -# from 'A' to 'F'. -# -# The following is the built-in default mapping hardcoded in the driver. -# You can leave those unchanged if you have a standard keypad. -# You can change it if you want to report other keystrings or have a non -# standard keypad. -# KeyMap_A=Up -# KeyMap_B=Down -# KeyMap_C=Left -# KeyMap_D=Right -# KeyMap_E=Enter -# KeyMap_F=Escape - -# keypad_test_mode permits one to test keypad assignment -# Default value is no -#keypad_test_mode=yes - - - -## ea65 driver for the display in AOpen XC Cube AV EA65 media barebones ## -[ea65] - -# Device is fixed /dev/ttyS1 -# Width and Height are fixed 9x1 - -# As the VFD is self luminescent we don't have a backlight -# But we can use the backlight functions to control the front LEDs -# Brightness 0 to 299 -> LEDs off -# Brightness 300 to 699 -> LEDs half bright -# Brightness 700 to 1000 -> LEDs full bright -Brightness=500 -# OffBrightness is the the value used for the 'backlight off' state -OffBrightness=0 - - - -## EyeboxOne driver ## -[EyeboxOne] - -# Select the output device to use [default: /dev/ttyS1] -# Device=/dev/cua01 -Device=/dev/ttyS1 - -# Set the display size [default: 20x4] -Size=20x4 - -# Switch on the backlight? [default: yes] -Backlight=yes - -# Switch on the cursor? [default: no] -Cursor=no - -# Set the communication speed [default: 19200; legal: 1200, 2400, 9600, 19200] -Speed=19200 - -# Enter Key is a \r character, so it's hardcoded in the driver -LeftKey=D -RightKey=C -UpKey=A -DownKey=B -EscapeKey=P - -# You can find out which key of your display sends which -# character by setting keypad_test_mode to yes and running -# LCDd. LCDd will output all characters it receives. -# Afterwards you can modify the settings above and set -# keypad_set_mode to no again. -keypad_test_mode=no - -## Futaba TOSD-5711BB VFD Driver ## -[futaba] - -## g15 driver for Logitech G15 Keyboard LCDs ## -[g15] - -# Display size (currently unused) -size=20x5 - - - -## glcd generic graphical display driver -[glcd] -# Select what type of connection. See documentation for types. -ConnectionType=t6963 - -# Width and height of the display in pixel. The supported sizes may depend on -# the ConnectionType. [default: 128x64; legal: 1x1 - 640x480] -#Size=128x64 - -# Width and height of a character cell in pixels. This value is only used if -# the driver has been compiled with FreeType and it is enabled. Otherwise the -# default 6x8 cell is used. -#CellSize=12x16 - -# If LCDproc has been compiled with FreeType 2 support this option can be used -# to turn if off intentionally. [default: yes; legal: yes, no] -#useFT2=no - -# Path to font file to use for FreeType rendering. This font must be monospace -# and should contain some special Unicode characters like arrows (Andale Mono -# is recommended and can be fetched at http://corefonts.sf.net). -#normal_font=/usr/local/lib/X11/fonts/TTF/andalemo.ttf - -# Some fonts miss the Unicode characters used to represent icons. In this case -# the built-in 5x8 font can used if this option is turned off. [default: yes; -# legal: yes, no] -#fontHasIcons=no - -# Set the initial contrast if supported by connection type. -# [default: 600; legal: 0 - 1000] -#Contrast=600 - -# Set brightness of the backlight if the backlight is switched 'on'. -# [default: 800; legal: 0 - 1000] -#Brightness=1000 - -# Set brightness of the backlight if the backlight is switched 'off'. Set this -# to zero to completely turn off the backlight. [default: 100; legal: 0 - 1000] -#OffBrightness=0 - -# Time (ms) from first key report to first repeat. Set to 0 to disable repeated -# key reports. [default: 500; legal: 0 - 3000] -#KeyRepeatDelay=500 - -# Time (ms) between repeated key reports. Ignored if KeyRepeatDelay is disabled -# (set to zero). [default: 300; legal: 0 - 3000] -#KeyRepeatInterval=300 - -# Assign key strings to keys. There may be up to 16 keys numbered 'A' to 'Z'. -# By default keys 'A' to 'F' are assigned Up, Down, Left, Right, Enter, Escape. -KeyMap_A=Up -KeyMap_B=Down -KeyMap_C=Enter -KeyMap_D=Escape - -# --- t6963 options --- - -# Parallel port to use [default: 0x378; legal: 0x200 - 0x400] -#Port=0x378 - -# Use LPT port in bi-directional mode. This should work on most LPT port -# and is required for proper timing! [default: yes; legal: yes, no] -#bidirectional=yes - -# Insert additional delays into reads / writes. [default: no; legal: yes, no] -#delayBus=no - -# --- serdisplib options --- - -# Name of the underlying serdisplib driver, e.g. ctinclud. See -# serdisplib documentation for details. -serdisp_name=t6963 - -# The display device to use, e.g. serraw:/dev/ttyS0, -# parport:/dev/parport0 or USB:07c0/1501. -serdisp_device=/dev/ppi0 - -# Options string to pass to serdisplib during initialization. Use -# this to set any display related options (e.g. wiring). The display size is -# always set based on the Size configured above! By default, no options are -# set. -# Important: The value must be quoted as it contains equal signs! -#serdisp_options="INVERT=1" - -# --- x11 options --- - -# PixelSize is size of each dot in pixels + a pixel gap. [default: 3+1] -#x11_PixelSize=3+1 - -# Colors are in RRGGBB format prefixed with "0x". -# PixelColor: The color of each dot at full contrast. [default: 0x000000] -#x11_PixelColor=0x000000 - -# BacklightColor: The color of the backlight as full brightness. -# [default: 0x80FF80] -#x11_BacklightColor=0x80FF80 - -# Border: Adds a border (empty space) around the LCD portion of X11 window. -# [default: 20] -#x11_Border=20 - -# Inverted: inverts the pixels [default: no; legal: yes, no] -#x11_Inverted=no - -# --- picolcdgfx options --- - -# Time in ms for usb_read to wait on a key press. [default: 125; legal: >0] -#picolcdgfx_KeyTimeout=125 - -# Inverted: Inverts the pixels. [default: no; legal: yes or no] -#picolcdgfx_Inverted=no - - - -## glcdlib meta driver for graphical LCDs ## -[glcdlib] - -## mandatory: - -# which graphical display supported by graphlcd-base to use [default: image] -# (see /etc/graphlcd.conf for possible drivers) -Driver=noritake800 - -# no=use graphlcd bitmap fonts (they have only one size / font file) -# yes=use fonts supported by FreeType2 (needs Freetype2 support in -# libglcdprocdriver and its dependants) -UseFT2=yes - -# text resolution in fixed width characters [default: 16x4] -# (if it won't fit according to available physical pixel resolution -# and the minimum available font face size in pixels, then -# 'DebugBorder' will automatically be turned on) -TextResolution=20x4 - -# path to font file to use -FontFile=/usr/share/fonts/corefonts/courbd.ttf - -## these only apply if UseFT2=yes: - -# character encoding to use -CharEncoding=iso8859-2 - -# minimum size in pixels in which fonts should be rendered -MinFontFaceSize=7x12 - -## optional: -Brightness=50 # Brightness (in %) if applicable -Contrast=50 # Contrast (in %) if applicable -Backlight=no # Backlight if applicable -UpsideDown=no # flip image upside down -Invert=no # invert light/dark pixels -ShowDebugFrame=no # turns on/off 1 pixel thick debugging - # border within the usable text area, - # for setting up TextResolution and - # MinFontFaceSize (if using FT2); -ShowBigBorder=no # border around the unused area -ShowThinBorder=yes # border around the unused area -PixelShiftX=0 -PixelShiftY=2 - - - -## Matrix Orbital GLK driver ## -[glk] - -# select the serial device to use [default: /dev/lcd] -Device=/dev/lcd - -# set the initial contrast value [default: 500; legal: 0 - 1000] -Contrast=500 - -# set the serial port speed [default: 19200; legal: 9600, 19200, 38400, 57600, 115200] -Speed=19200 - - - -## Hitachi HD44780 driver ## -[hd44780] -{%- if model == 'ezio' %} -ConnectionType=ezio -Device=/dev/ttyUSB0 -Keypad=yes -Size=16x2 -KeyMatrix_4_1=Enter -KeyMatrix_4_2=Up -KeyMatrix_4_3=Down -KeyMatrix_4_4=Escape -{%- endif %} - -# Select what type of connection. See documentation for available types. -#ConnectionType=4bit - -# Select model if have non-standard one which require extra initialization or handling or -# just want extra features it offers. -# Available: standard (default), extended, winstar_oled, pt6314_vfd -# - standard is default, use for LCDs not mentioned below. -# - extended, hd66712, ks0073: allows use 4-line "extended" mode, -# same as deprecated now option ExtendedMode=yes -# - winstar_oled, weh00xxyya: changes initialization for WINSTAR's WEH00xxyyA displays -# and allows handling brightness -# - pt6314_vfd: allows handling brightness on PTC's PT6314 VFDs -# -# This option should be independent of connection type. -#Model = standard - -# I/O address of the LPT port. Usual values are: 0x278, 0x378 and 0x3BC. -# For I2C connections this sets the slave address (usually 0x20). -#Port=0x378 - -# Device of the serial, I2C, or SPI interface [default: /dev/lcd] -#Device=/dev/ttyS0 - -# Bitrate of the serial port (0 for interface default) -#Speed=0 - -# If you have a keypad connected. -# You may also need to configure the keypad layout further on in this file. -#Keypad=no - -# Set the initial contrast (bwctusb, lcd2usb, and usb4all) -# [default: 800; legal: 0 - 1000] -#Contrast=0 - -# Set brightness of the backlight (lcd2usb and usb4all): -# Brightness is the brightness while the backlight is set to 'on'. -# [default: 800; legal: 0 - 1000] -#Brightness=1000 - -# OffBrightness is the brightness while the backlight is set to 'off'. -# [default: 300; legal: 0 - 1000] -#OffBrightness=0 - -# Specify if you have a switchable backlight and if yes, can select method for turning it on/off: -# -# - none - no switchable backlight is available. For compability also boolean -# 0, n, no, off and false are aliases. -# - external - use external pin or any other method defined with ConnectionType backlight -# handling. For backward compability also this value is chosen for boolean -# TRUE values: 1, y, yes, on and true. -# - internal - means that backlight is handled using internal commands according -# to selected display model (with Model option). Depending on model, -# Brightness and OffBrightness options can be taken into account. -# - internalCmds - means that commands for turning on and off backlight are given -# with extra options BacklightOnCmd and BacklightOffCmd, which would be treated -# as catch up (last resort) for other types of displays which have similar features. -# -# You can provide multiple occurences of this option to use more than one method. -# Default is model specific: Winstar OLED and PT6314 VFD enables internal backlight mode, -# for others it is set to none. -#Backlight = none - -# Commands for enabling internal backlight for use with Backlight=internalCmds. -# Up to 4 bytes can be encoded, as integer number in big-endian order. -# -# NOTE: this is advanced option, if command contains bits other than only brighness handling, -# they must be set accordingly to not disrupt display state. If for example 'FUNCTION SET' command -# is used for this purpose, bits of interface length (4-bit / 8-bit) must be set according to -# selected ConnectionType. -#BacklightCmdOn=0x1223 - -# Commands for disabling internal backlight for use with Backlight=internalCmds. -# Up to 4 bytes can be encoded, as integer number in big-endian order. -#BacklightCmdOff=0x1234 - - -# If you have the additional output port ("bargraph") and you want to -# be able to control it with the lcdproc OUTPUT command -#OutputPort=no - -# Specifies if the last line is pixel addressable (yes) or it controls an -# underline effect (no). [default: yes; legal: yes, no] -#Lastline=yes - -# Specifies the size of the LCD. -# In case of multiple combined displays, this should be the total size. -#Size=20x4 - -# For multiple combined displays: how many lines does each display have. -# Vspan=2,2 means both displays have 2 lines. -#vspan=2,2 - -# If you have an HD66712, a KS0073 or another controller with 'extended mode', -# set this flag to get into 4-line mode. On displays with just two lines, do -# not set this flag. -# As an additional restriction, controllers with and without extended mode -# AND 4 lines cannot be mixed for those connection types that support more -# than one display! -# NOTE: This option is deprecated in favour of choosing Model=extended option. -#ExtendedMode=yes - -# In extended mode, on some controllers like the ST7036 (in 3 line mode) -# the next line in DDRAM won't start 0x20 higher. [default: 0x20] -#LineAddress=0x10 - -# Character map to to map ISO-8859-1 to the LCD's character set -# [default: hd44780_default; legal: hd44780_default, hd44780_euro, ea_ks0073, -# sed1278f_0b, hd44780_koi8_r, hd44780_cp1251, hd44780_8859_5, upd16314, -# weh001602a_1] -# (hd44780_koi8_r, hd44780_cp1251, hd44780_8859_5, upd16314 and weh001602a_1 -# are possible if compiled with additional charmaps) -CharMap=hd44780_default - -# Font bank to be used for some displays such as the WINSTAR WEH001602A -# 0: English/Japanese (default) -# 1: Western Europe I -# 2: English/Rusian -# 3: Western Europe II -#FontBank=0 - -# If your display is slow and cannot keep up with the flow of data from -# LCDd, garbage can appear on the LCDd. Set this delay factor to 2 or 4 -# to increase the delays. Default: 1. -#DelayMult=2 - -# Some displays (e.g. vdr-wakeup) need a message from the driver to that it -# is still alive. When set to a value bigger then null the character in the -# upper left corner is updated every seconds. Default: 0. -#KeepAliveDisplay=0 - -# If you experience occasional garbage on your display you can use this -# option as workaround. If set to a value bigger than null it forces a -# full screen refresh seconds. Default: 0. -#RefreshDisplay=5 - -# You can reduce the inserted delays by setting this to false. -# On fast PCs it is possible your LCD does not respond correctly. -# Default: true. -#DelayBus=true - -# If you have a keypad you can assign keystrings to the keys. -# See documentation for used terms and how to wire it. -# For example to give directly connected key 4 the string "Enter", use: -# KeyDirect_4=Enter -# For matrix keys use the X and Y coordinates of the key: -# KeyMatrix_1_3=Enter -#KeyMatrix_4_1=Enter -#KeyMatrix_4_2=Up -#KeyMatrix_4_3=Down -#KeyMatrix_4_4=Escape - -## ICP Peripheral Comminication Protocol driver ## -# Supports A125 and A106 -# -# Short Press Select: Down -# Long Press Select: Up -# Short Press Enter: Enter -# Long Press Enter: Escape -# -[icp_a106] -Device=/dev/ttyS1 - -# Display dimensions -Size=20x2 - - -## Code Mercenaries IO-Warrior driver ## -[IOWarrior] - -# display dimensions -Size=20x4 - -# serial number. Must be exactly as listed by usbview -# (if not given, the 1st IOWarrior found gets used) -#SerialNumber=00000674 - -# If you have an HD66712, a KS0073 or another 'almost HD44780-compatible', -# set this flag to get into extended mode (4-line linear). -#ExtendedMode=yes - -# Specifies if the last line is pixel addressable (yes) or it controls an -# underline effect (no). [default: yes; legal: yes, no] -#Lastline=yes - - - -## Soundgraph/Ahanix/Silverstone/Uneed/Accent iMON driver ## -[imon] - -# select the device to use -Device=/dev/lcd0 - -# display dimensions -Size=16x2 - -# Character map to to map ISO-8859-1 to the displays character set. -# [default: none; legal: none, hd44780_euro, upd16314, hd44780_koi8_r, -# hd44780_cp1251, hd44780_8859_5 ] (upd16314, hd44780_koi8_r, -# hd44780_cp1251, hd44780_8859_5 are possible if compiled with additional -# charmaps) -CharMap=hd44780_euro - -## Soundgraph iMON LCD ## -[imonlcd] -# Specify which iMon protocol should be used -# [legal: 0, 1; default: 0] -# Choose 0 for 15c2:ffdc device, -# Choose 1 for 15c2:0038 device -Protocol=0 - -# Set the exit behavior [legal: 0-2; default: 1] -# 0 means leave shutdown message, -# 1 means show the big clock, -# 2 means blank device -#OnExit=2 - -# Select the output device to use [default: /dev/lcd0] -Device=/dev/lcd0 - -# Select the displays contrast [default: 200; legal: 0-1000] -Contrast=200 - -# Specify the size of the display in pixels [default: 96x16] -#Size=96x16 - -# Set the backlight state [default: on; legal: on, off] -#Backlight=on - -# Set the disc mode [legal: 0,1; default: 0] -# 0 => spin the "slim" disc - two disc segments, -# 1 => their complement spinning; -#DiscMode=0 - - - -## IrMan driver ## -[IrMan] -# in case of trouble with IrMan, try the Lirc emulator for IrMan - -# Select the input device to use -#Device=/dev/irman - -# Select the configuration file to use -#Config=/etc/irman.cfg - - - -## IRtrans driver ## -[irtrans] - -# Does the device have a backlight? [default: no; legal: yes, no] -#Backlight=no - -# IRTrans device to connect to [default: localhost] -#Hostname=localhost - -# display dimensions -Size=16x2 - - - -## Joystick driver ## -[joy] - -# Select the input device to use [default: /dev/js0] -Device=/dev/js0 - -# set the axis map -Map_Axis1neg=Left -Map_Axis1pos=Right -Map_Axis2neg=Up -Map_Axis2pos=Down - -# set the button map -Map_Button1=Enter -Map_Button2=Escape - - -## JW-002 driver ## -[jw002] - -# Select the output device to use [default: /dev/lcd] -#Device=/dev/ttyS0 - -# Set the display size [default: 24x8] -Size=24x8 - -# Optional X and Y offsets (in characters) to center a smaller display -# size on the full 24x8 panel -X_offset=0 -Y_offset=0 - -# Set the communication speed [default: 19200; legal: 1200, 2400, 9600, 19200] -Speed=19200 - -# Pick which font page to use [default: 0] -# Note that different fonts probably have their bargraph chars in different -# spots. For ROM-based fonts 0-3, those characters are already known. -Font=0 - -# The following table translates from jw002 key letters to logical key names. -# By default no keys are mapped, meaning the keypad is not used at all. -#KeyMap_I=Left -#KeyMap_J=Right -#KeyMap_H=Up -#KeyMap_K=Down -#KeyMap_L=Enter -#KeyMap_A=Escape -# See the [menu] section for an explanation of the key mappings - -# You can find out which key of your display sends which -# character by setting keypad_test_mode to yes and running -# LCDd. LCDd will output all characters it receives. -# Afterwards you can modify the settings above and set -# keypad_set_mode to no again. -keypad_test_mode=no - - -## LB216 driver ## -[lb216] - -# Select the output device to use [default: /dev/lcd] -Device=/dev/lcd - -# Set the initial brightness [default: 255; legal: 0 - 255] -Brightness=255 - -# Set the communication speed [default: 9600; legal: 2400, 9600] -Speed=9600 - -# Reinitialize the LCD's BIOS [default: no; legal: yes, no] -Reboot=no - - - -## LCDM001 driver ## -[lcdm001] - -Device=/dev/ttyS1 - -# keypad settings -# Keyname Function -# Normal context Menu context -# ------- -------------- ------------ -# PauseKey Pause/Continue Enter/select -# BackKey Back(Go to previous screen) Up/Left -# ForwardKey Forward(Go to next screen) Down/Right -# MainMenuKey Open main menu Exit/Cancel -PauseKey=LeftKey -BackKey=UpKey -ForwardKey=DownKey -MainMenuKey=RightKey - -# You can rearrange the settings here. -# If your device is broken, have a look at server/drivers/lcdm001.h - - - -## HNE LCTerm driver ## -[lcterm] -Device=/dev/ttyS1 -Size=16x2 - - -## Linux event device input driver ## -[linux_input] - -# Select the input device to use [default: /dev/input/event0]. This may be -# either an absolute path to the input node, starting with '/', or -# an input device name, e.g. "Logitech Gaming Keyboard Gaming Keys". -# Device=/dev/input/event0 - -# specify a non-default key map -#key=1,Escape -#key=28,Enter -#key=96,Enter -#key=105,Left -#key=106,Right -#key=103,Up -#key=108,Down - - -## LIRC input driver ## -[lirc] - -# Specify an alternative location of the lircrc file [default: ~/.lircrc] -#lircrc=/etc/lircrc.lcdproc - -# Must be the same as in your lircrc -#prog=lcdd - - - -## LIS MCE 2005 driver ## -[lis] - -# Set the initial brightness [default: 1000; legal: 0 - 1000] -# 0-250 = 25%, 251-500 = 50%, 501-750 = 75%, 751-1000 = 100% -#Brightness=1000 - -# Columns by lines [default: 20x2] -#Size=20x2 - -# USB Vendor ID [default: 0x0403] -# Change only if testing a compatible device. -#VendorID=0x0403 - -# USB Product ID [default: 0x6001] -# Change only if testing a compatible device. -#ProductID=0x6001 - -# Specifies if the last line is pixel addressable (yes) or it only controls an -# underline effect (no). [default: yes; legal: yes, no] -#Lastline=yes - - - -##The driver for the VFD of the Medion MD8800 PC ## -[MD8800] -# device to use [default: /dev/ttyS1] -#Device=/dev/ttyS1 - -# display size [default: 16x2] -#Size=16x2 - -# Set the initial brightness [default: 1000; legal: 0 - 1000] -Brightness=1000 -# Set the initial off-brightness [default: 0; legal: 0 - 1000] -# This value is used when the display is normally -# switched off in case LCDd is inactive -OffBrightness=50 - - - -## Futuba MDM166A Display -[mdm166a] -# Show self-running clock after LCDd shutdown -# Possible values: [default: no; legal: no, small, big] -Clock=big -# Dim display, no dimming gives full brightness [default: no, legal: yes, no] -Dimming=no -# Dim display in case LCDd is inactive [default: no, legal: yes, no] -OffDimming=yes - - - -## MSI MS-6931 driver for displays in 1HU servers ## -[ms6931] - -# device to use [default: /dev/ttyS1] -Device=/dev/ttyS1 - -# display size [default: 16x2] -#Size=16x2 - - - -## MTC-S16209x driver ## -[mtc_s16209x] - -# Select the output device to use [default: /dev/lcd] -Device=/dev/lcd - -# Set the initial brightness [default: 255; legal: 0 - 255] -Brightness=255 - -# Reinitialize the LCD's BIOS [default: no; legal: yes, no] -Reboot=no - - - -## Matrix Orbital driver ## -[MtxOrb] - -# Select the output device to use [default: /dev/lcd] -Device=/dev/ttyS0 - -# Set the display size [default: 20x4] -Size=20x4 - -# Set the display type [default: lcd; legal: lcd, lkd, vfd, vkd] -Type=lkd - -# Set the initial contrast [default: 480] -# NOTE: The driver will ignore this if the display -# is a vfd or vkd as they don't have this feature -Contrast=480 - -# Some old displays do not have an adjustable backlight but only can -# switch the backlight on/off. If you experience randomly appearing block -# characters, try setting this to false. [default: yes; legal: yes, no] -hasAdjustableBacklight=no - -# Set the initial brightness [default: 1000; legal: 0 - 1000] -Brightness=1000 -# Set the initial off-brightness [default: 0; legal: 0 - 1000] -# This value is used when the display is normally -# switched off in case LCDd is inactive -OffBrightness=0 - -# Set the communication speed [default: 19200; legal: 1200, 2400, 9600, 19200] -Speed=19200 - -# The following table translates from MtxOrb key letters to logical key names. -# By default no keys are mapped, meaning the keypad is not used at all. -#KeyMap_A=Left -#KeyMap_B=Right -#KeyMap_C=Up -#KeyMap_D=Down -#KeyMap_E=Enter -#KeyMap_F=Escape -# See the [menu] section for an explanation of the key mappings - -# You can find out which key of your display sends which -# character by setting keypad_test_mode to yes and running -# LCDd. LCDd will output all characters it receives. -# Afterwards you can modify the settings above and set -# keypad_set_mode to no again. -keypad_test_mode=no - - - -## mx5000 driver for LCD display on the Logitech MX5000 keyboard ## -[mx5000] - -# Select the output device to use [default: /dev/hiddev0] -Device = /dev/hiddev0 -# Time to wait in ms after the refresh screen has been sent [default: 1000] -WaitAfterRefresh = 1000 - - - -## Noritake VFD driver ## -[NoritakeVFD] -# device where the VFD is. Usual values are /dev/ttyS0 and /dev/ttyS1 -# [default: /dev/lcd] -Device=/dev/ttyS0 -# Specifies the size of the LCD. -Size=20x4 -# Set the initial brightness [default: 1000; legal: 0 - 1000] -Brightness=1000 -# Set the initial off-brightness [default: 0; legal: 0 - 1000] -# This value is used when the display is normally -# switched off in case LCDd is inactive -OffBrightness=50 -# set the serial port speed [default: 9600, legal: 1200, 2400, 9600, 19200, 115200] -Speed=9600 -# Set serial data parity [default: 0; legal: 0-2 ] -# Meaning: 0(=none), 1(=odd), 2(=even) -Parity=0 -# re-initialize the VFD [default: no; legal: yes, no] -Reboot=no - - - -## Olimex MOD-LCD1x9 driver ## -[Olimex_MOD_LCD1x9] - -# device file of the i2c controler -Device=/dev/i2c-0 - - -## Mini-box.com picoLCD (usblcd) driver ## -[picolcd] - -# KeyTimeout is only used if the picoLCD driver is built with libusb-0.1. When -# built with libusb-1.0 key and IR data is input asynchronously so there is no -# need to wait for the USB data. -# KeyTimeout is the time in ms that LCDd spends waiting for a key press before -# cycling through other duties. Higher values make LCDd use less CPU time and -# make key presses more detectable. Lower values make LCDd more responsive -# but a little prone to missing key presses. 500 (.5 second) is the default -# and a balanced value. [default: 500; legal: 0 - 1000] -KeyTimeout=500 - -# Key auto repeat is only available if the picoLCD driver is built with -# libusb-1.0. Use KeyRepeatDelay and KeyRepeatInterval to configure key auto -# repeat. -# -# Key auto repeat delay (time in ms from first key report to first repeat). Use -# zero to disable auto repeat. [default: 300; legal: 0 - 3000] -KeyRepeatDelay=300 - -# Key auto repeat interval (time in ms between repeat reports). Only used if -# KeyRepeatDelay is not zero. [default: 200; legal: 0 - 3000] -KeyRepeatInterval=200 - -# Sets the initial state of the backlight upon start-up. -# [default: on; legal: on, off] -#Backlight=on - -# Set the initial brightness [default: 1000; legal: 0 - 1000]. Works only -# with the 20x4 device -Brightness=1000 - -# Set the brightness while the backlight is 'off' [default: 0; legal: 0 - 1000]. -# Works only with the 20x4 device. -#OffBrightness=0 - -# Set the initial contrast [default: 1000; legal: 0 - 1000] -Contrast=1000 - -# Link the key lights to the backlight? [default: on; legal: on, off] -#LinkLights=off - -# Light the keys? [default: on; legal: on, off] -Keylights=on - -# If Keylights is on, the you can unlight specific keys below: -# Key0 is the directional pad. Key1 - Key5 correspond to the F1 - F5 keys. -# There is no LED for the +/- keys. This is a handy way to indicate to users -# which keys are disabled. [default: on; legal: on, off] -Key0Light=on -Key1Light=on -Key2Light=on -Key3Light=on -Key4Light=on -Key5Light=on - -# Host name or IP address of the LIRC instance that is to receive IR codes -# If not set, or set to an empty value, IR support is disabled. -#LircHost=127.0.0.1 - -# UDP port on which LIRC is listening [default: 8765; legal: 1 - 65535] -LircPort=8765 - -# UDP data time unit for LIRC [default: off; legal: on, off] -# On: times sent in microseconds (requires LIRC UDP driver that accepts this). -# Off: times sent in 'jiffies' (1/16384s) (supported by standard LIRC UDP driver). -LircTime_us=on - -# Threshold in microseconds of the gap that triggers flushing the IR data -# to lirc [default: 8000; legal: 1000 - ] -# If LircTime_us is on values greater than 32.767ms will disable the flush -# If LircTime_us is off values greater than 1.999938s will disable the flush -LircFlushThreshold=10000 - - - -## Pyramid LCD driver ## -[pyramid] - -# device to connect to [default: /dev/lcd] -Device=/dev/ttyUSB0 - - - -## rawserial driver ## -[rawserial] - -# Select the output device to use [default: /dev/cuaU0] -Device=/dev/ttyS0 - -# Serial port baudrate [default: 9600] -Speed=9600 - -# Specifies the size of the LCD. If this driver is loaded as a secondary driver -# it always adopts to the size of the primary driver. If loaded as the only -# (or primary) driver, the size can be set. [default: 40x4] -#Size=16x2 - -# How often to dump the LCD contents out the port, in Hertz (times per second) -# 1 = once per second, 4 is 4 times per second, 0.1 is once every 10 seconds. -# [default: 1; legal: 0.0005 - 10] -UpdateRate=1 - - - -## SDEC driver for Watchguard Firebox ## -[sdeclcd] -# No options - - -## Seiko Epson 1330 driver ## -[sed1330] - -# Port where the LPT is. Common values are 0x278, 0x378 and 0x3BC -Port=0x378 - -# Type of LCD module (legal: G321D, G121C, G242C, G191D, G2446, SP14Q002) -# Note: Currently only tested with G321D & SP14Q002. -Type=G321D - -# Width x Height of a character cell in pixels [legal: 6x7 - 8x16; default: 6x10] -CellSize=6x10 - -# Select what type of connection [legal: classic, bitshaker; default: classic] -ConnectionType=classic - - - -## Seiko Epson 1520 driver ## -[sed1520] - -# Port where the LPT is. Usual values are 0x278, 0x378 and 0x3BC -Port=0x378 - -# Select the interface type (wiring) for the display. Supported values are -# 68 for 68-style connection (RESET level high) and 80 for 80-style connection -# (RESET level low). [legal: 68, 80; default: 80] -InterfaceType=80 - -# On fast machines it may be necessary to slow down transfer to the display. -# If this value is set to zero, delay is disabled. Any value greater than -# zero slows down each write by one microsecond. [legal: 0-1000; default: 1] -DelayMult=0 - -# The original wiring used an inverter to drive the control lines. If you do -# not use an inverter set haveInverter to no. [default: yes; legal: yes, no] -HaveInverter=no - -# On some displays column data in memory is mapped to segment lines from right -# to left. This is called inverted mapping (not to be confused with -# 'haveInverter' from above). [default: no; legal: yes, no] -#InvertedMapping=yes - -# At least one display is reported (Everbouquet MG1203D) that requires sending -# three times 0xFF before a reset during initialization. -# [default: no; legal: yes, no] -#UseHardReset=yes - - -## serial POS display driver ## -[serialPOS] - -# Device to use in serial mode [default: /dev/ttyS0] -Device=/dev/ttyS0 - -# Specifies the size of the display in characters. [default: 16x2] -Size=16x2 - -# Specifies the cell size of each character cell on the display in characters. -# [default: 5x8] -Cellsize=5x8 - -# Specifies the number of custom characters supported by the display. -# [default: 0] -Custom_chars=0 - -# Set the communication protocol to use with the POS display. -# [default: AEDEX; legal: AEDEX, CD5220, Epson, Emax, LogicControls, Ultimate] -Type=AEDEX - -# communication baud rate with the display [default: 9600; legal: 1200, 2400, -# 4800, 9600, 19200, 115200] -Speed=9600 - - - -## Serial VFD driver ## -## Drives various (see below) serial 5x7dot VFD's. ## -[serialVFD] - -# Specifies the displaytype.[default: 0] -# 0 NEC (FIPC8367 based) VFDs. -# 1 KD Rev 2.1. -# 2 Noritake VFDs (*). -# 3 Futaba VFDs -# 4 IEE S03601-95B -# 5 IEE S03601-96-080 (*) -# 6 Futaba NA202SD08FA (allmost IEE compatible) -# 7 Samsung 20S207DA4 and 20S207DA6 -# 8 Nixdorf BA6x / VT100 -# (* most should work, not tested yet.) -Type=0 - -# "no" if display connected serial, "yes" if connected parallel. [default: no] -# I.e. serial by default -use_parallel=no - -# Number of Custom-Characters. default is display type dependent -#Custom-Characters=0 - -# Portaddress where the LPT is. Used in parallel mode only. Usual values are -# 0x278, 0x378 and 0x3BC. -Port=0x378 - -# Set parallel port timing delay (us). Used in parallel mode only. -# [default: 2; legal: 0 - 255] -#PortWait=2 - -# Device to use in serial mode. Usual values are /dev/ttyS0 and /dev/ttyS1 -Device=/dev/ttyS1 - -# Specifies the size of the VFD. -Size=20x2 - -# Set the initial brightness [default: 1000; legal: 0 - 1000] -# (4 steps 0-250, 251-500, 501-750, 751-1000) -Brightness=1000 -# Set the initial off-brightness [default: 0; legal: 0 - 1000] -# This value is used when the display is normally -# switched off in case LCDd is inactive -# (4 steps 0-250, 251-500, 501-750, 751-1000) -OffBrightness=0 - -# set the serial port speed [default: 9600; legal: 1200, 2400, 9600, 19200, 115200] -Speed=9600 - -# enable ISO 8859 1 compatibility [default: yes; legal: yes, no] -#ISO_8859_1=yes - - - -## shuttleVFD driver ## -[shuttleVFD] -# No options - - - -## stv5730 driver ## -[stv5730] - -# Port the device is connected to [default: 0x378] -Port=0x378 - - -[SureElec] - -# Port the device is connected to (by default first USB serial port) -Device=/dev/ttyUSB0 - -# Edition level of the device (can be 1, 2 or 3) [default: 2] -#Edition=1 - -# set display size -# Note: The size can be obtained directly from device for edition 2 & 3. -#Size=16x2 - -# Set the initial contrast [default: 480; legal: 0 - 1000] -#Contrast=200 - -# Set the initial brightness [default: 480; legal: 1 - 1000] -#Brightness=480 - -# Set the initial off-brightness [default: 100; legal: 1 - 1000] -# This value is used when the display is normally -# switched off in case LCDd is inactive -#OffBrightness=100 - - -## SVGAlib driver ## -[svga] - -# svgalib mode to use [default: G320x240x256 ] -# legal values are supported svgalib modes -#Mode=G640x480x256 - -# set display size [default: 20x4] -Size=20x4 - -# Set the initial contrast [default: 500; legal: 0 - 1000] -# Can be set but does not change anything internally -Contrast=500 - -# Set the initial brightness [default: 1000; legal: 1 - 1000] -Brightness=1000 - -# Set the initial off-brightness [default: 500; legal: 1 - 1000] -# This value is used when the display is normally -# switched off in case LCDd is inactive -OffBrightness=500 - - - -## Text driver ## -[text] -# Set the display size [default: 20x4] -Size=20x4 - - - -## Toshiba T6963 driver ## -[t6963] - -# set display size in pixels [default: 128x64] -Size=128x64 - -# port to use [default: 0x378; legal: 0x200 - 0x400] -Port=0x378 - -# Use LPT port in bi-directional mode. This should work on most LPT port and -# is required for proper timing! [default: yes; legal: yes, no] -#bidirectional=yes - -# Insert additional delays into reads / writes. [default: no; legal: yes, no] -#delayBus=no - -# Clear graphic memory on start-up. [default: no; legal: yes, no] -#ClearGraphic=no - - - -## Tyan Barebones LCD driver (GS10 & GS12 series) ## -[tyan] - -# Select the output device to use [default: /dev/lcd] -Device=/dev/lcd - -# Set the communication speed [default: 9600; legal: 4800, 9600] -Speed=9600 - -# set display size [default: 16x2] -Size=16x2 - - - -## ELV ula200 driver ## -[ula200] - -# Select the LCD size [default: 20x4] -Size=20x4 - -# If you have a non standard keypad you can associate any keystrings to keys. -# There are 6 input key in the CwLnx hardware that generate characters -# from 'A' to 'F'. -# -# The following it the built-in default mapping hardcoded in the driver. -# You can leave those unchanged if you have a standard keypad. -# You can change it if you want to report other keystrings or have a non -# standard keypad. -# KeyMap_A=Up -# KeyMap_B=Down -# KeyMap_C=Left -# KeyMap_D=Right -# KeyMap_E=Enter -# KeyMap_F=Escape - - - -## Wirz SLI LCD driver ## -[sli] - -# Select the output device to use [default: /dev/lcd] -Device=/dev/lcd - -# Set the communication speed [default: 19200; legal: 1200, 2400, 9600, 19200, -# 38400, 57600, 115200] -Speed=19200 - - - -## vlsys_m428 for VFD/IR combination in Moneual MonCaso 320 ## -[vlsys_m428] - -# Select the output device to use [default: /dev/ttyUSB0] -#Device=/dev/ttyUSB0 - - - -## OnScreen Display using libxosd ## -[xosd] - -# set display size [default: 20x4] -Size=20x4 - -# Offset in pixels from the top-left corner of the monitor [default: 0x0] -Offset=200x200 - -# X font to use, in XLFD format, as given by "xfontsel" -Font=-*-terminus-*-r-*-*-*-320-*-*-*-*-* - -## Y.A.R.D.2 LCD section -[yard2LCD] -Size=20x4 -# If rendering rate is too high, change in server\main.h #define RENDER_FREQ 8 to "1" - -# EOF diff --git a/data/templates/system-display/lcdproc.conf.tmpl b/data/templates/system-display/lcdproc.conf.tmpl deleted file mode 100644 index 92aee8efe..000000000 --- a/data/templates/system-display/lcdproc.conf.tmpl +++ /dev/null @@ -1,173 +0,0 @@ -### autogenerated by system-display.py ### - -# system display show host (CPU|SMP-CPU|CPU-Graph|Load|Memory|Proc-Size|Disk|Uptime) -# network interface alias -# units (bps|Bps|pps) -# clock (big|mini|date-time) - -# LCDproc client configuration file - -## general options ## -[lcdproc] -# address of the LCDd server to connect to -Server=127.0.0.1 - -# Port of the server to connect to -Port=13666 - -# set reporting level -#ReportLevel=2 - -# report to to syslog ? -ReportToSyslog=true - -# run in foreground [default: false; legal: true, false] -#Foreground=true - -# PidFile location when running as daemon [default: /var/run/lcdproc.pid] -#PidFile=/var/run/lcdproc.pid - -# slow down initial announcement of modes (in 1/100s) -#delay=2 - - -## screen specific configuration options ## -{%- if show %} -# display name for the main menu [default: LCDproc HOST] -DisplayName="{%- if show['title'] %}{{ show['title'] }}{%- else %}VyOS{%- endif %}" - -{%- if show['host'] %} - -[CPU] -# Show screen -Active={%- if 'cpu' in show['host'] %}true{%- else %}false{%- endif %} -OnTime=1 -OffTime=2 -ShowInvisible=false - -[SMP-CPU] -# Show screen -Active={%- if 'cpu-all' in show['host'] %}true{%- else %}false{%- endif %} - -[Memory] -# Show screen -Active={%- if 'memory' in show['host'] %}true{%- else %}false{%- endif %} - -[Load] -# Show screen -Active={%- if 'load-hist' in show['host'] %}true{%- else %}false{%- endif %} -# Min Load Avg at which the backlight will be turned off [default: 0.05] -LowLoad=0.05 -# Max Load Avg at which the backlight will start blinking [default: 1.3] -HighLoad=1.3 - -[Uptime] -# Show screen -Active={%- if 'uptime' in show['host'] %}true{%- else %}false{%- endif %} - -[CPUGraph] -# Show screen -Active={%- if 'cpu-hist' in show['host'] %}true{%- else %}false{%- endif %} - -[ProcSize] -# Show screen -Active={%- if 'proc' in show['host'] %}true{%- else %}false{%- endif %} - -[Disk] -# Show screen -Active={%- if 'disk' in show['host'] %}true{%- else %}false{%- endif %} -{%- else %} {# if show['host'] #} -{# Turn off sections that default active #} - -[CPU] -Active=false - -[Memory] -Active=false - -[Load] -Active=false - -{%- endif %} {# if show['host'] #} - -[TimeDate] -# Show screen -Active={%- if show['clock'] == 'date-time' %}true{%- else %}false{%- endif %} -# time format [default: %H:%M:%S; legal: see strftime(3)] -TimeFormat="%H:%M:%S" -# date format [default: %x; legal: see strftime(3)] -DateFormat="%x" - -[BigClock] -# Show screen -Active={%- if show['clock'] == 'big' %}true{%- else %}false{%- endif %} - -[MiniClock] -# Show screen -Active={%- if show['clock'] == 'mini' %}true{%- else %}false{%- endif %} -# time format [default: %H:%M; legal: see strftime(3)] -TimeFormat="%H:%M" - -{%- if show['network'] %} -[Iface] -# Show screen -Active={%- if show['network']['interface'] %}true{%- else %}false{%- endif %} -{%- for i in show['network']['interface'] %} -# Show stats for Interface {{ i }} -Interface{{ loop.index0 }}={{ i }} -{%- if show['network']['interface'][i]['alias'] %} -# Interface alias name to display [default: ] -Alias{{ loop.index0 }}={{ show['network']['interface'][i]['alias'] }} -{%- endif %} -{%- endfor %} - -# Units to display [default: byte; legal: byte, bit, packet] -{%- if show['network']['units'] == 'bps' %} -unit=bit -{%- elif show['network']['units'] == 'Bps'%} -unit=byte -{%- elif show['network']['units'] == 'pps' %} -unit=packet -{%- else %} -unit=bit -{%- endif %} -# add screen with transferred traffic -#transfer=TRUE -{%- endif %} {# if show['network'] #} - -{%- else %}{# if show #} -{# Turn off sections that default active #} - -[CPU] -Active=false - -[Memory] -Active=false - -[Load] -Active=false - -[TimeDate] -Active=false -{%- endif %}{# if show #} - -[Battery] -# Show screen -Active=false - -[About] -# Show screen -Active=false - -[OldTime] -# Show screen -Active=false -# time format [default: %H:%M:%S; legal: see strftime(3)] -TimeFormat="%H:%M:%S" -# date format [default: %x; legal: see strftime(3)] -DateFormat="%x" -# Display the title bar in two-line mode. Note that with four lines or more -# the title is always shown. [default: true; legal: true, false] -#ShowTitle=false - -# EOF diff --git a/interface-definitions/system-display.xml.in b/interface-definitions/system-display.xml.in deleted file mode 100644 index fbd897996..000000000 --- a/interface-definitions/system-display.xml.in +++ /dev/null @@ -1,235 +0,0 @@ - - - - - - - - System display LCD/VFD/LED - 400 - - - - - - Model of the display attached to this system [REQUIRED] - - sdec ezio test - - - (sdec|ezio|test) - - Invalid system display model - - sdec - Display model for Lanner, Watchguard, Nexcom NSA, Sophos UTM appliances with built-in SDEC LCD - - - ezio - Display model for Portwell, Caswell appliances with built-in EZIO-100 or EZIO-300 LCD - - - test - Test model for USB CrystalFonz CF533 - - - - - - - Disable sytem display - - - - - - Select the screens for the system display [REQUIRED] - - - - - - - Select host screens for the system display - - cpu cpu-all cpu-hist disk load-hist memory proc uptime - - - (cpu|cpu-all|cpu-hist|disk|load-hist|memory|proc|uptime) - - Invalid host screen - - cpu - Detailed CPU usage - - - cpu-all - CPU usage overview (one line per CPU) - - - cpu-hist - CPU usage histogram - - - disk - File systems fill level - - - load-hist - Load histogram - - - memory - Memory and swap usage - - - proc - Top processes by size - - - uptime - System uptime - - - - - - - Network settings for system display - - - - - - Show network traffic on the system display [Max 3 interfaces] - - - - - - - - Interface alias - - [A-Za-z0-9]{1,10} - - Invalid alias, must be 1 to 10 char or digit - - - - - - - - Unit for network details - - bps Bps pps - - - (bps|Bps|pps) - - Invalid network detail unit - - bps - Bit(s) per second - - - Bps - Byte(s) per second - - - pps - Packet(s) per second - - - - - - - - - - Show a clock on the system display - - big mini date-time - - - (big|mini|date-time) - - Invalid clock format - - big - Multi-line clock - - - mini - Minimal clock - - - date-time - Clock with Date and Time - - - - - - - Screen title to show on the system display - - [A-Za-z0-9]{1,16} - - Invalid title, must be 1 to 16 char or digit - - - - - - - - Time in sec to show each screen on the system display - - 1-30 - Numer of seconds - - - - - - - - - - Message to show when system display first starts - - .{1,16} - - Hello message must be 1 to 16 char - - - - - - Message to show when system display stops - - .{1,16} - - Bye message must be 1 to 16 char - - - - - - - diff --git a/interface-definitions/system-lcd.xml.in b/interface-definitions/system-lcd.xml.in new file mode 100644 index 000000000..ad59acb6b --- /dev/null +++ b/interface-definitions/system-lcd.xml.in @@ -0,0 +1,62 @@ + + + + + + + System LCD display + 100 + + + + + Model of the display attached to this system [REQUIRED] + + CFA-533 CFA-631 CFA-633 CFA-635 + + + CFA-533 + Crystalfontz CFA-533 + + + CFA-631 + Crystalfontz CFA-631 + + + CFA-633 + Crystalfontz CFA-633 + + + CFA-635 + Crystalfontz CFA-635 + + + ^(CFA-533|CFA-631|CFA-633|CFA-635)$ + + + + + + Physical device used by LCD display + + + + + + ttySXX + TTY device name, regular serial port + + + usbNbXpY + TTY device name, USB based + + + ^(ttyS[0-9]+|usb[0-9]+b.*)$ + + + + + + + + diff --git a/python/vyos/util.py b/python/vyos/util.py index 7078762df..c07fef599 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -661,3 +661,15 @@ def check_kmod(k_mod): if not os.path.exists(f'/sys/module/{module}'): if call(f'modprobe {module}') != 0: raise ConfigError(f'Loading Kernel module {module} failed') + +def find_device_file(device): + """ Recurively search /dev for the given device file and return its full path. + If no device file was found 'None' is returned """ + from fnmatch import fnmatch + + for root, dirs, files in os.walk('/dev'): + for basename in files: + if fnmatch(basename, device): + return os.path.join(root, basename) + + return None diff --git a/src/conf_mode/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py index 4081be3c9..6d168d918 100755 --- a/src/conf_mode/interfaces-wirelessmodem.py +++ b/src/conf_mode/interfaces-wirelessmodem.py @@ -16,7 +16,6 @@ import os -from fnmatch import fnmatch from sys import exit from vyos.config import Config @@ -25,22 +24,13 @@ from vyos.configverify import verify_vrf from vyos.template import render from vyos.util import call from vyos.util import check_kmod +from vyos.util import find_device_file from vyos import ConfigError from vyos import airbag airbag.enable() k_mod = ['option', 'usb_wwan', 'usbserial'] -def find_device_file(device): - """ Recurively search /dev for the given device file and return its full path. - If no device file was found 'None' is returned """ - for root, dirs, files in os.walk('/dev'): - for basename in files: - if fnmatch(basename, device): - return os.path.join(root, basename) - - return None - def get_config(): """ Retrive CLI config as dictionary. Dictionary can never be empty, as at least the diff --git a/src/conf_mode/system-display.py b/src/conf_mode/system-display.py deleted file mode 100755 index 3eafc30c0..000000000 --- a/src/conf_mode/system-display.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2020 Francois Mertz fireboxled at gmail.com -# -# 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 vyos.config import Config -from vyos import ConfigError -from vyos.util import run -from vyos.template import render - -from vyos import airbag -airbag.enable() - -def get_config(): - conf = Config() - base = ['system', 'display'] - display = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) - # Return a (possibly empty) configuration dictionary - return display - -def verify(config_dict): - if not config_dict: - return None - - if 'model' not in config_dict: - raise ConfigError('Display model is [REQUIRED]') - - if ( 'show' not in config_dict - or ( 'clock' not in config_dict['show'] - and 'network' not in config_dict['show'] - and 'host' not in config_dict['show'] - ) - ): - raise ConfigError('Display show must have a clock, host or network') - - if ( 'network' in config_dict['show'] - and 'interface' not in config_dict['show']['network'] - ): - raise ConfigError('Display show network must have an interface') - - if ( 'network' in config_dict['show'] - and 'interface' in config_dict['show']['network'] - and len(config_dict['show']['network']['interface']) > 3 - ): - raise ConfigError('Display show network cannot have > 3 interfaces') - - return None - -def generate(config_dict): - if not config_dict: - return None - # Render config file for daemon LCDd - render('/run/LCDd/LCDd.lo.conf', 'system-display/LCDd.conf.tmpl', config_dict) - # Render config file for client lcdproc - render('/run/lcdproc/lcdproc.lo.conf', 'system-display/lcdproc.conf.tmpl', config_dict) - - return None - -def apply(config_dict): - # Stop client - run('systemctl stop lcdproc@lo.service') - - if not config_dict or 'disabled' in config_dict: - # Stop server - run('systemctl stop LCDd@lo.service') - return None - - # Restart server - run('systemctl restart LCDd@lo.service') - # Start client - run('systemctl start lcdproc@lo.service') - - return None - -if __name__ == '__main__': - try: - config_dict = get_config() - verify(config_dict) - generate(config_dict) - apply(config_dict) - except ConfigError as e: - print(e) - exit(1) diff --git a/src/conf_mode/system_lcd.py b/src/conf_mode/system_lcd.py new file mode 100755 index 000000000..0ad1318f0 --- /dev/null +++ b/src/conf_mode/system_lcd.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +# +# Copyright 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 vyos.config import Config +from vyos.util import call +from vyos.util import find_device_file +from vyos.template import render +from vyos import ConfigError +from vyos import airbag +airbag.enable() + +lcdd_conf = '/run/LCDd/LCDd.conf' +lcdproc_conf = '/run/lcdproc/lcdproc.conf' + +def get_config(): + conf = Config() + base = ['system', 'lcd'] + lcd = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True) + # Return (possibly empty) dictionary + return lcd + +def verify(lcd): + if not lcd: + return None + + if not {'device', 'model'} <= set(lcd): + raise ConfigError('Both device and driver must be set!') + + return None + +def generate(lcd): + if not lcd: + return None + + if 'device' in lcd: + lcd['device'] = find_device_file(lcd['device']) + + # Render config file for daemon LCDd + render(lcdd_conf, 'lcd/LCDd.conf.tmpl', lcd, trim_blocks=True) + # Render config file for client lcdproc + render(lcdproc_conf, 'lcd/lcdproc.conf.tmpl', lcd, trim_blocks=True) + + return None + +def apply(lcd): + if not lcd: + call('systemctl stop lcdproc.service LCDd.service') + + for file in [lcdd_conf, lcdproc_conf]: + if os.path.exists(file): + os.remove(file) + else: + # Restart server + call('systemctl restart LCDd.service lcdproc.service') + + return None + +if __name__ == '__main__': + try: + config_dict = get_config() + verify(config_dict) + generate(config_dict) + apply(config_dict) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/etc/systemd/system/LCDd.service.d/override.conf b/src/etc/systemd/system/LCDd.service.d/override.conf new file mode 100644 index 000000000..5f3f0dc95 --- /dev/null +++ b/src/etc/systemd/system/LCDd.service.d/override.conf @@ -0,0 +1,8 @@ +[Unit] +After= +After=vyos-router.service + +[Service] +ExecStart= +ExecStart=/usr/sbin/LCDd -c /run/LCDd/LCDd.conf + diff --git a/src/etc/systemd/system/hostapd@.service.d/override.conf b/src/etc/systemd/system/hostapd@.service.d/override.conf index bb8e81d7a..b03dbc299 100644 --- a/src/etc/systemd/system/hostapd@.service.d/override.conf +++ b/src/etc/systemd/system/hostapd@.service.d/override.conf @@ -3,8 +3,7 @@ After= After=vyos-router.service [Service] -WorkingDirectory=/run/hostapd -EnvironmentFile= +WorkingDirectory=/run/LCDd ExecStart= -ExecStart=/usr/sbin/hostapd -B -P /run/hostapd/%i.pid /run/hostapd/%i.conf -PIDFile=/run/hostapd/%i.pid +ExecStart=/usr/sbin/LCDd -s 1 -f -c /run/LCDd/LCDd.conf + diff --git a/src/systemd/LCDd@.service b/src/systemd/LCDd@.service deleted file mode 100644 index a4604cf21..000000000 --- a/src/systemd/LCDd@.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=LCD display daemon on %I -Documentation=man:LCDd(8) http://www.lcdproc.org/ - -[Service] -User=root -ExecStart=/usr/sbin/LCDd -s 1 -f -c /run/LCDd/LCDd.%I.conf - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/src/systemd/lcdproc.service b/src/systemd/lcdproc.service new file mode 100644 index 000000000..5aa99ec78 --- /dev/null +++ b/src/systemd/lcdproc.service @@ -0,0 +1,13 @@ +[Unit] +Description=LCDproc system status information viewer on %I +Documentation=man:lcdproc(8) http://www.lcdproc.org/ +After=vyos-router.service +After=LCDd.service + +[Service] +User=root +ExecStart=/usr/bin/lcdproc -f -c /run/lcdproc/lcdproc.conf +PIDFile=/run/lcdproc/lcdproc.pid + +[Install] +WantedBy=multi-user.target diff --git a/src/systemd/lcdproc@.service b/src/systemd/lcdproc@.service deleted file mode 100644 index 9a1723dba..000000000 --- a/src/systemd/lcdproc@.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=LCDproc system status information viewer on %I -Documentation=man:lcdproc(8) http://www.lcdproc.org/ - -[Service] -User=root -ExecStart=/usr/bin/lcdproc -f -c /run/lcdproc/lcdproc.%I.conf - -[Install] -WantedBy=multi-user.target \ No newline at end of file -- cgit v1.2.3 From b1675e420479cd6b582c66c0902d6e326340658f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 18 Aug 2020 16:40:51 +0200 Subject: lcd: T2564: add support for Lanner, Watchguard, Symantec boxes --- data/templates/lcd/LCDd.conf.tmpl | 12 ++++++++++-- interface-definitions/system-lcd.xml.in | 16 ++++++++++------ src/conf_mode/system_lcd.py | 4 ++++ src/systemd/lcdproc.service | 4 ++-- 4 files changed, 26 insertions(+), 10 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/lcd/LCDd.conf.tmpl b/data/templates/lcd/LCDd.conf.tmpl index da749d04a..bde177b6a 100644 --- a/data/templates/lcd/LCDd.conf.tmpl +++ b/data/templates/lcd/LCDd.conf.tmpl @@ -48,8 +48,12 @@ DriverPath=/usr/lib/x86_64-linux-gnu/lcdproc/ # sed1520, serialPOS, serialVFD, shuttleVFD, sli, stv5730, svga, t6963, # text, tyan, ula200, vlsys_m428, xosd, yard2LCD -{% if model is defined and model.startswith('CFA-') %} +{% if model is defined %} +{% if model.startswith('cfa-') %} Driver=CFontzPacket +{% elif model == 'sdec' %} +Driver=sdeclcd +{% endif %} {% endif %} # Tells the driver to bind to the given interface. [default: 127.0.0.1] @@ -112,7 +116,7 @@ Heartbeat=off TitleSpeed=10 {% if model is defined and model is not none %} -{% if model.startswith('CFA-') %} +{% if model.startswith('cfa-') %} ## CrystalFontz packet driver (for CFA533, CFA631, CFA633 & CFA635) ## [CFontzPacket] Model={{ model.split('-')[1] }} @@ -122,5 +126,9 @@ Brightness=500 OffBrightness=50 Reboot=yes USB=yes +{% elif model == 'sdec' %} +## SDEC driver for Lanner, Watchguard, Sophos sppliances ## +[sdeclcd] +# No options {% endif %} {% endif %} diff --git a/interface-definitions/system-lcd.xml.in b/interface-definitions/system-lcd.xml.in index ad59acb6b..36116ae1b 100644 --- a/interface-definitions/system-lcd.xml.in +++ b/interface-definitions/system-lcd.xml.in @@ -12,26 +12,30 @@ Model of the display attached to this system [REQUIRED] - CFA-533 CFA-631 CFA-633 CFA-635 + cfa-533 cfa-631 cfa-633 cfa-635 sdec - CFA-533 + cfa-533 Crystalfontz CFA-533 - CFA-631 + cfa-631 Crystalfontz CFA-631 - CFA-633 + cfa-633 Crystalfontz CFA-633 - CFA-635 + cfa-635 Crystalfontz CFA-635 + + sdec + Lanner, Watchguard, Nexcom NSA, Sophos UTM appliances + - ^(CFA-533|CFA-631|CFA-633|CFA-635)$ + ^(cfa-533|cfa-631|cfa-633|cfa-635|sdec)$ diff --git a/src/conf_mode/system_lcd.py b/src/conf_mode/system_lcd.py index 0ad1318f0..31a09252d 100755 --- a/src/conf_mode/system_lcd.py +++ b/src/conf_mode/system_lcd.py @@ -41,6 +41,10 @@ def verify(lcd): if not lcd: return None + if 'model' in lcd and lcd['model'] in ['sdec']: + # This is a fixed LCD display, no device needed - bail out early + return None + if not {'device', 'model'} <= set(lcd): raise ConfigError('Both device and driver must be set!') diff --git a/src/systemd/lcdproc.service b/src/systemd/lcdproc.service index 5aa99ec78..ef717667a 100644 --- a/src/systemd/lcdproc.service +++ b/src/systemd/lcdproc.service @@ -1,8 +1,8 @@ [Unit] Description=LCDproc system status information viewer on %I Documentation=man:lcdproc(8) http://www.lcdproc.org/ -After=vyos-router.service -After=LCDd.service +After=vyos-router.service LCDd.service +Requires=LCDd.service [Service] User=root -- cgit v1.2.3 From 865cba395223afaa263ff7581fb53ee509f36266 Mon Sep 17 00:00:00 2001 From: Eshenko Dmitriy Date: Wed, 19 Aug 2020 00:37:00 +0300 Subject: anyconnect: T2036: add anyconnect VPN support --- data/templates/ocserv/ocserv_config.tmpl | 82 +++++++ data/templates/ocserv/ocserv_passwd.tmpl | 6 + data/templates/ocserv/radius_conf.tmpl | 22 ++ data/templates/ocserv/radius_servers.tmpl | 7 + debian/control | 1 + interface-definitions/vpn_anyconnect.xml.in | 258 +++++++++++++++++++++ op-mode-definitions/anyconnect.xml | 20 ++ src/conf_mode/vpn_anyconnect.py | 132 +++++++++++ .../systemd/system/ocserv.service.d/override.conf | 14 ++ src/op_mode/anyconnect-control.py | 67 ++++++ 10 files changed, 609 insertions(+) create mode 100644 data/templates/ocserv/ocserv_config.tmpl create mode 100644 data/templates/ocserv/ocserv_passwd.tmpl create mode 100644 data/templates/ocserv/radius_conf.tmpl create mode 100644 data/templates/ocserv/radius_servers.tmpl create mode 100644 interface-definitions/vpn_anyconnect.xml.in create mode 100644 op-mode-definitions/anyconnect.xml create mode 100755 src/conf_mode/vpn_anyconnect.py create mode 100644 src/etc/systemd/system/ocserv.service.d/override.conf create mode 100755 src/op_mode/anyconnect-control.py (limited to 'interface-definitions') diff --git a/data/templates/ocserv/ocserv_config.tmpl b/data/templates/ocserv/ocserv_config.tmpl new file mode 100644 index 000000000..6aaeff693 --- /dev/null +++ b/data/templates/ocserv/ocserv_config.tmpl @@ -0,0 +1,82 @@ +### generated by vpn_anyconnect.py ### + +tcp-port = {{ listen_ports.tcp }} +udp-port = {{ listen_ports.udp }} + +run-as-user = nobody +run-as-group = daemon + +{% if "radius" in authentication.mode %} +auth = "radius [config=/run/ocserv/radiusclient.conf]" +{% else %} +auth = "plain[/run/ocserv/ocpasswd]" +{% endif %} + +{% if ssl.cert_file %} +server-cert = {{ ssl.cert_file }} +{% endif %} + +{% if ssl.key_file %} +server-key = {{ ssl.key_file }} +{% endif %} + +{% if ssl.ca_cert_file %} +ca-cert = {{ ssl.ca_cert_file }} +{% endif %} + +socket-file = /run/ocserv/ocserv.socket +occtl-socket-file = /run/ocserv/occtl.socket +use-occtl = true +isolate-workers = true +keepalive = 300 +dpd = 60 +mobile-dpd = 300 +switch-to-tcp-timeout = 30 +tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128" +auth-timeout = 240 +idle-timeout = 1200 +mobile-idle-timeout = 1800 +min-reauth-time = 3 +cookie-timeout = 300 +rekey-method = ssl +try-mtu-discovery = true +cisco-client-compat = true +dtls-legacy = true + + +# The name to use for the tun device +device = sslvpn + +# An alternative way of specifying the network: +{% if network_settings %} +# DNS settings +{% if network_settings.name_server is string %} +dns = {{ network_settings.name_server }} +{% else %} +{% for dns in network_settings.name_server %} +dns = {{ dns }} +{% endfor %} +{% endif %} +# IPv4 network pool +{% if network_settings.client_ip_settings %} +{% if network_settings.client_ip_settings.subnet %} +ipv4-network = {{ network_settings.client_ip_settings.subnet }} +{% endif %} +{% endif %} +# IPv6 network pool +{% if network_settings.client_ipv6_pool %} +{% if network_settings.client_ipv6_pool.prefix %} +ipv6-network = {{ network_settings.client_ipv6_pool.prefix }} +ipv6-subnet-prefix = {{ network_settings.client_ipv6_pool.mask }} +{% endif %} +{% endif %} +{% endif %} + +{% if network_settings.push_route is string %} +route = {{ network_settings.push_route }} +{% else %} +{% for route in network_settings.push_route %} +route = {{ route }} +{% endfor %} +{% endif %} + diff --git a/data/templates/ocserv/ocserv_passwd.tmpl b/data/templates/ocserv/ocserv_passwd.tmpl new file mode 100644 index 000000000..ffadb4860 --- /dev/null +++ b/data/templates/ocserv/ocserv_passwd.tmpl @@ -0,0 +1,6 @@ +#:: +{% for user in username if username is defined %} +{% if not "disable" in username[user] %} +{{ user }}:*:{{ username[user].hash }} +{% endif %} +{% endfor %} \ No newline at end of file diff --git a/data/templates/ocserv/radius_conf.tmpl b/data/templates/ocserv/radius_conf.tmpl new file mode 100644 index 000000000..2d19306a0 --- /dev/null +++ b/data/templates/ocserv/radius_conf.tmpl @@ -0,0 +1,22 @@ +### generated by cpn_anyconnect.py ### +nas-identifier VyOS +{% for srv in server %} +{% if not "disable" in server[srv] %} +{% if "port" in server[srv] %} +authserver {{ srv }}:{{server[srv]["port"]}} +{% else %} +authserver {{ srv }} +{% endif %} +{% endif %} +{% endfor %} +radius_timeout {{ timeout }} +{% if source_address %} +bindaddr {{ source_address }} +{% else %} +bindaddr * +{% endif %} +servers /run/ocserv/radius_servers +dictionary /etc/radcli/dictionary +default_realm +radius_retries 3 +# \ No newline at end of file diff --git a/data/templates/ocserv/radius_servers.tmpl b/data/templates/ocserv/radius_servers.tmpl new file mode 100644 index 000000000..ba21fa074 --- /dev/null +++ b/data/templates/ocserv/radius_servers.tmpl @@ -0,0 +1,7 @@ +### generated by cpn_anyconnect.py ### +# server key +{% for srv in server %} +{% if not "disable" in server[srv] %} +{{ srv }} {{ server[srv].key }} +{% endif %} +{% endfor %} diff --git a/debian/control b/debian/control index 34ba4be8e..520401d57 100644 --- a/debian/control +++ b/debian/control @@ -107,6 +107,7 @@ Depends: python3, libatomic1, fastnetmon, libndp-tools, + ocserv, tcptraceroute Description: VyOS configuration scripts and data VyOS configuration scripts, interface definitions, and everything diff --git a/interface-definitions/vpn_anyconnect.xml.in b/interface-definitions/vpn_anyconnect.xml.in new file mode 100644 index 000000000..e74326986 --- /dev/null +++ b/interface-definitions/vpn_anyconnect.xml.in @@ -0,0 +1,258 @@ + + + + + + + SSL VPN AnyConnect + 901 + + + + + Authentication for remote access SSL VPN Server + + + + + Authentication mode used by this server + + local + Use local username/password configuration + + + radius + Use RADIUS server for user autentication + + + (local|radius) + + + local radius + + + + + + Local user authentication for SSL VPN server + + + + + User name for authentication + + + + + Option to disable a SSL VPN Server user + + + + + + Password for authentication + + + + + + + #include + + + + + Session timeout + + 1-30 + Session timeout in seconds (default: 2) + + + + + Timeout must be between 1 and 30 seconds + + 2 + + + + + + + + SSL Certificate, SSL Key and CA (/config/auth) + + + + + tcp port number to accept connections (default: 443) + + 1-65535 + Numeric IP port (default: 443) + + + + + + 443 + + + + udp port number to accept connections (default: 443) + + 1-65535 + Numeric IP port (default: 443) + + + + + + 443 + + + + + + SSL Certificate, SSL Key and CA (/config/auth) + + + + + Certificate Authority certificate + + + + + file + File in /config/auth directory + + + + + + + + + Server Certificate + + file + File in /config/auth directory + + + + + + + + + Privat Key of the Server Certificate + + file + File in /config/auth directory + + + + + + + + + + + Network settings + + + + + Route to be pushed to the client + + ipv4net + IPv4 network and prefix length + + + ipv6net + IPv6 network and prefix length + + + + + + + + + + Client IP pools settings + + + + + Client IP subnet (CIDR notation) + + ipv4net + IPv4 address and prefix length + + + + + Not a valid CIDR formatted prefix + + + + + + + Pool of client IPv6 addresses + + + + + Pool of addresses used to assign to clients + + ipv6net + IPv6 address and prefix length + + + + + + + + + Prefix length used for individual client + + <48-128> + Client prefix length (default: 64) + + + + + + 64 + + + + + + Domain Name Server (DNS) propagated to client + + ipv4 + Domain Name Server (DNS) IPv4 address + + + ipv6 + Domain Name Server (DNS) IPv6 address + + + + + + + + + + + + + + + diff --git a/op-mode-definitions/anyconnect.xml b/op-mode-definitions/anyconnect.xml new file mode 100644 index 000000000..7e8cdd35b --- /dev/null +++ b/op-mode-definitions/anyconnect.xml @@ -0,0 +1,20 @@ + + + + + + + show anyconnect-server information + + + + + Show active anyconnect server sessions + + ${vyos_op_scripts_dir}/anyconnect-control.py --action="show_sessions" + + + + + + diff --git a/src/conf_mode/vpn_anyconnect.py b/src/conf_mode/vpn_anyconnect.py new file mode 100755 index 000000000..45c06bffa --- /dev/null +++ b/src/conf_mode/vpn_anyconnect.py @@ -0,0 +1,132 @@ +#!/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 vyos.config import Config +from vyos.configdict import dict_merge +from vyos.xml import defaults +from vyos.template import render +from vyos.util import call +from vyos import ConfigError +from crypt import crypt, mksalt, METHOD_SHA512 + +from vyos import airbag +airbag.enable() + +cfg_dir = '/run/ocserv' +ocserv_conf = cfg_dir + '/ocserv.conf' +ocserv_passwd = cfg_dir + '/ocpasswd' +radius_cfg = cfg_dir + '/radiusclient.conf' +radius_servers = cfg_dir + '/radius_servers' + + +# Generate hash from user cleartext password +def get_hash(password): + return crypt(password, mksalt(METHOD_SHA512)) + + +def get_config(): + conf = Config() + base = ['vpn', 'anyconnect'] + ocserv = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + default_values = defaults(base) + ocserv = dict_merge(default_values, ocserv) + return ocserv + + +def verify(ocserv): + if ocserv is None: + return None + + # Check authentication + if "authentication" in ocserv: + if "mode" in ocserv["authentication"]: + if "local" in ocserv["authentication"]["mode"]: + if not ocserv["authentication"]["local_users"] or not ocserv["authentication"]["local_users"]["username"]: + raise ConfigError('Anyconect mode local required at leat one user') + else: + for user in ocserv["authentication"]["local_users"]["username"]: + if not "password" in ocserv["authentication"]["local_users"]["username"][user]: + raise ConfigError(f'password required for user {user}') + else: + raise ConfigError('anyconnect authentication mode required') + else: + raise ConfigError('anyconnect authentication credentials required') + + # Check ssl + if "ssl" in ocserv: + req_cert = ['ca_cert_file', 'cert_file', 'key_file'] + for cert in req_cert: + if not cert in ocserv["ssl"]: + raise ConfigError('anyconnect ssl {0} required'.format(cert.replace('_', '-'))) + else: + raise ConfigError('anyconnect ssl required') + + # Check network settings + if "network_settings" in ocserv: + if "push_route" in ocserv["network_settings"]: + # Replace default route + if "0.0.0.0/0" in ocserv["network_settings"]["push_route"]: + ocserv["network_settings"]["push_route"].remove("0.0.0.0/0") + ocserv["network_settings"]["push_route"].append("default") + else: + ocserv["network_settings"]["push_route"] = "default" + else: + raise ConfigError('anyconnect network settings required') + + +def generate(ocserv): + if not ocserv: + return None + + if "radius" in ocserv["authentication"]["mode"]: + # Render radius client configuration + render(radius_cfg, 'ocserv/radius_conf.tmpl', ocserv["authentication"]["radius"], trim_blocks=True) + # Render radius servers + render(radius_servers, 'ocserv/radius_servers.tmpl', ocserv["authentication"]["radius"], trim_blocks=True) + else: + if "local_users" in ocserv["authentication"]: + for user in ocserv["authentication"]["local_users"]["username"]: + ocserv["authentication"]["local_users"]["username"][user]["hash"] = get_hash(ocserv["authentication"]["local_users"]["username"][user]["password"]) + # Render local users + render(ocserv_passwd, 'ocserv/ocserv_passwd.tmpl', ocserv["authentication"]["local_users"], trim_blocks=True) + + # Render config + render(ocserv_conf, 'ocserv/ocserv_config.tmpl', ocserv, trim_blocks=True) + + + +def apply(ocserv): + if not ocserv: + call('systemctl stop ocserv.service') + for file in [ocserv_conf, ocserv_passwd]: + if os.path.exists(file): + os.unlink(file) + else: + call('systemctl restart ocserv.service') + + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/etc/systemd/system/ocserv.service.d/override.conf b/src/etc/systemd/system/ocserv.service.d/override.conf new file mode 100644 index 000000000..89dbb153f --- /dev/null +++ b/src/etc/systemd/system/ocserv.service.d/override.conf @@ -0,0 +1,14 @@ +[Unit] +RequiresMountsFor=/run +ConditionPathExists=/run/ocserv/ocserv.conf +After= +After=vyos-router.service +After=dbus.service + +[Service] +WorkingDirectory=/run/ocserv +PIDFile= +PIDFile=/run/ocserv/ocserv.pid +ExecStart= +ExecStart=/usr/sbin/ocserv --foreground --pid-file /run/ocserv/ocserv.pid --config /run/ocserv/ocserv.conf + diff --git a/src/op_mode/anyconnect-control.py b/src/op_mode/anyconnect-control.py new file mode 100755 index 000000000..6382016b7 --- /dev/null +++ b/src/op_mode/anyconnect-control.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 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 sys +import argparse +import json + +from vyos.config import Config +from vyos.util import popen, run, DEVNULL +from tabulate import tabulate + +occtl = '/usr/bin/occtl' +occtl_socket = '/run/ocserv/occtl.socket' + +def show_sessions(): + out, code = popen("sudo {0} -j -s {1} show users".format(occtl, occtl_socket),stderr=DEVNULL) + if code: + sys.exit('Cannot get anyconnect users information') + else: + headers = ["interface", "username", "ip", "remote IP", "RX", "TX", "state", "uptime"] + sessions = json.loads(out) + ses_list = [] + for ses in sessions: + ses_list.append([ses["Device"], ses["Username"], ses["IPv4"], ses["Remote IP"], ses["_RX"], ses["_TX"], ses["State"], ses["_Connected at"]]) + if len(ses_list) > 0: + print(tabulate(ses_list, headers)) + else: + print("No active anyconnect sessions") + +def is_ocserv_configured(): + if not Config().exists_effective('vpn anyconnect'): + print("vpn anyconnect server is not configured") + sys.exit(1) + +def main(): + #parese args + parser = argparse.ArgumentParser() + parser.add_argument('--action', help='Control action', required=True) + parser.add_argument('--selector', help='Selector username|ifname|sid', required=False) + parser.add_argument('--target', help='Target must contain username|ifname|sid', required=False) + args = parser.parse_args() + + + # Check is IPoE configured + is_ocserv_configured() + + if args.action == "restart": + run("systemctl restart ocserv") + sys.exit(0) + elif args.action == "show_sessions": + show_sessions() + +if __name__ == '__main__': + main() -- cgit v1.2.3 From 93c3a4a4f05014914dd2e9353ddd676e20bced1a Mon Sep 17 00:00:00 2001 From: sever-sever Date: Wed, 19 Aug 2020 17:07:14 +0000 Subject: mpls-ldp: T915: Add discovery hello timers --- data/templates/frr/ldpd.frr.tmpl | 12 ++++++++++++ interface-definitions/protocols-mpls.xml.in | 26 +++++++++++++++++++++++++- src/conf_mode/protocols_mpls.py | 24 +++++++++++++++++++++--- 3 files changed, 58 insertions(+), 4 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/frr/ldpd.frr.tmpl b/data/templates/frr/ldpd.frr.tmpl index bbff88ae5..dbaa917e8 100644 --- a/data/templates/frr/ldpd.frr.tmpl +++ b/data/templates/frr/ldpd.frr.tmpl @@ -21,6 +21,18 @@ no discovery transport-address {{ old_ldp.d_transp_ipv4 }} {% if ldp.d_transp_ipv4 -%} discovery transport-address {{ ldp.d_transp_ipv4 }} {% endif -%} +{% if old_ldp.hello_holdtime -%} +no discovery hello holdtime {{ old_ldp.hello_holdtime }} +{% endif -%} +{% if ldp.hello_holdtime -%} +discovery hello holdtime {{ ldp.hello_holdtime }} +{% endif -%} +{% if old_ldp.hello_interval -%} +no discovery hello interval {{ old_ldp.hello_interval }} +{% endif -%} +{% if ldp.hello_interval -%} +discovery hello interval {{ ldp.hello_interval }} +{% endif -%} {% for interface in old_ldp.interfaces -%} no interface {{interface}} {% endfor -%} diff --git a/interface-definitions/protocols-mpls.xml.in b/interface-definitions/protocols-mpls.xml.in index 376323855..3e9edbf72 100644 --- a/interface-definitions/protocols-mpls.xml.in +++ b/interface-definitions/protocols-mpls.xml.in @@ -54,6 +54,30 @@ + + + Hello holdtime + + 1-65535 + Time in seconds + + + + + + + + + Hello interval + + 1-65535 + Time in seconds + + + + + + Transport ipv4 address @@ -95,4 +119,4 @@ - \ No newline at end of file + diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py index 72208ffa1..bcb16fa04 100755 --- a/src/conf_mode/protocols_mpls.py +++ b/src/conf_mode/protocols_mpls.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -38,13 +38,17 @@ def get_config(): 'interfaces' : [], 'neighbors' : {}, 'd_transp_ipv4' : None, - 'd_transp_ipv6' : None + 'd_transp_ipv6' : None, + 'hello_holdtime' : None, + 'hello_interval' : None }, 'ldp' : { 'interfaces' : [], 'neighbors' : {}, 'd_transp_ipv4' : None, - 'd_transp_ipv6' : None + 'd_transp_ipv6' : None, + 'hello_holdtime' : None, + 'hello_interval' : None } } if not (conf.exists('protocols mpls') or conf.exists_effective('protocols mpls')): @@ -61,6 +65,20 @@ def get_config(): if conf.exists('router-id'): mpls_conf['router_id'] = conf.return_value('router-id') + # Get hello holdtime + if conf.exists_effective('discovery hello-holdtime'): + mpls_conf['old_ldp']['hello_holdtime'] = conf.return_effective_value('discovery hello-holdtime') + + if conf.exists('discovery hello-holdtime'): + mpls_conf['ldp']['hello_holdtime'] = conf.return_value('discovery hello-holdtime') + + # Get hello interval + if conf.exists_effective('discovery hello-interval'): + mpls_conf['old_ldp']['hello_interval'] = conf.return_effective_value('discovery hello-interval') + + if conf.exists('discovery hello-interval'): + mpls_conf['ldp']['hello_interval'] = conf.return_value('discovery hello-interval') + # Get discovery transport-ipv4-address if conf.exists_effective('discovery transport-ipv4-address'): mpls_conf['old_ldp']['d_transp_ipv4'] = conf.return_effective_value('discovery transport-ipv4-address') -- cgit v1.2.3 From 98c2c5e0585e0806099a353de207f392223eff9a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 22 Aug 2020 23:15:29 +0200 Subject: dhcpv6-pd: T2677: optimize CLI interface for PD configuration The current CLI did not support multiple prefix-delegations per interface. Some ISPs only send one /64 to a client per prefix-delegation request, but they allow the customer to request multiple prefixes. The 'dhcpv6-options prefix-delegation' node has been renamed and converted to a tag node named 'dhcpv6-options pd'. The tag node specifies a PD request (>=0). In the past the user needed to know what prefix will be assigned and required to calculate the sla-len by himself. The 'sla-len' node was dropped and is now calculated in the background from the 'dhcpv6-options pd 0 length' node. It is no longer mandatory to supply the 'sla-id' node, if sla-id is not specified it is 'guessed' by counting upwards. Example configuration: ---------------------- ethernet eth1 { address dhcpv6 dhcpv6-options { pd 0 { length 56 interface eth2 { address 1 } } } } This will request a /56 assignment from the ISP and will delegate a /64 network to interface eth2. VyOS will use the interface address ::1 on the delegate interface (eth2) as its local address. --- data/templates/dhcp-client/ipv6.tmpl | 48 +++++++++------- interface-definitions/include/dhcp-options.xml.i | 2 +- interface-definitions/include/dhcpv6-options.xml.i | 40 ++++++-------- python/vyos/configdict.py | 64 ++++++++++++++++++---- src/conf_mode/interfaces-pppoe.py | 4 +- src/migration-scripts/interfaces/11-to-12 | 58 ++++++++++++++++++++ 6 files changed, 157 insertions(+), 59 deletions(-) create mode 100755 src/migration-scripts/interfaces/11-to-12 (limited to 'interface-definitions') diff --git a/data/templates/dhcp-client/ipv6.tmpl b/data/templates/dhcp-client/ipv6.tmpl index 9673f302b..e9285d86b 100644 --- a/data/templates/dhcp-client/ipv6.tmpl +++ b/data/templates/dhcp-client/ipv6.tmpl @@ -8,37 +8,43 @@ interface {{ ifname }} { information-only; {% endif %} {% if dhcpv6_options is not defined or dhcpv6_options.temporary is not defined %} - send ia-na 1; # non-temporary address + send ia-na 0; # non-temporary address {% endif %} -{% if dhcpv6_options is defined and dhcpv6_options.prefix_delegation is defined %} - send ia-pd 2; # prefix delegation +{% if dhcpv6_options is defined and dhcpv6_options.pd is defined %} +{% for pd in dhcpv6_options.pd %} + send ia-pd {{ pd }}; # prefix delegation #{{ pd }} +{% endfor %} {% endif %} }; {% if dhcpv6_options is not defined or dhcpv6_options.temporary is not defined %} -id-assoc na 1 { - # Identity association NA +id-assoc na 0 { + # Identity association for non temporary address }; {% endif %} -{% if dhcpv6_options is defined and dhcpv6_options.prefix_delegation is defined %} -id-assoc pd 2 { -{% if dhcpv6_options.prefix_delegation.length is defined %} - prefix ::/{{ dhcpv6_options.prefix_delegation.length }} infinity; -{% endif %} -{% for interface in dhcpv6_options.prefix_delegation.interface %} +{% if dhcpv6_options is defined and dhcpv6_options.pd is defined %} +{% for pd in dhcpv6_options.pd %} +id-assoc pd {{ pd }} { +{# length got a default value #} + prefix ::/{{ dhcpv6_options.pd[pd].length }} infinity; +{% set sla_len = 64 - dhcpv6_options.pd[pd].length|int %} +{% set count = namespace(value=0) %} +{% for interface in dhcpv6_options.pd[pd].interface if dhcpv6_options.pd[pd].interface is defined %} prefix-interface {{ interface }} { -{% if dhcpv6_options.prefix_delegation.interface[interface].sla_id is defined %} - sla-id {{ dhcpv6_options.prefix_delegation.interface[interface].sla_id }}; -{% endif %} -{% if dhcpv6_options.prefix_delegation.interface[interface].sla_len is defined %} - sla-len {{ dhcpv6_options.prefix_delegation.interface[interface].sla_len }}; -{% endif %} -{% if dhcpv6_options.prefix_delegation.interface[interface].address is defined %} - ifid {{ dhcpv6_options.prefix_delegation.interface[interface].address }}; -{% endif %} + sla-len {{ sla_len }}; +{% if dhcpv6_options.pd[pd].interface[interface].sla_id is defined and dhcpv6_options.pd[pd].interface[interface].sla_id is not none %} + sla-id {{ dhcpv6_options.pd[pd].interface[interface].sla_id }}; +{% else %} + sla-id {{ count.value }}; +{% endif %} +{% if dhcpv6_options.pd[pd].interface[interface].address is defined and dhcpv6_options.pd[pd].interface[interface].address is not none %} + ifid {{ dhcpv6_options.pd[pd].interface[interface].address }}; +{% endif %} }; -{% endfor %} +{% set count.value = count.value + 1 %} +{% endfor %} }; +{% endfor %} {% endif %} diff --git a/interface-definitions/include/dhcp-options.xml.i b/interface-definitions/include/dhcp-options.xml.i index 0f71d9321..9989291fc 100644 --- a/interface-definitions/include/dhcp-options.xml.i +++ b/interface-definitions/include/dhcp-options.xml.i @@ -1,6 +1,6 @@ - DHCP options + DHCP client settings/options diff --git a/interface-definitions/include/dhcpv6-options.xml.i b/interface-definitions/include/dhcpv6-options.xml.i index 98a87dba2..5d088b83d 100644 --- a/interface-definitions/include/dhcpv6-options.xml.i +++ b/interface-definitions/include/dhcpv6-options.xml.i @@ -1,11 +1,24 @@ - DHCPv6 options + DHCPv6 client settings/options - + + + Acquire only config parameters, no address + + + + - DHCPv6 Prefix Delegation Options + DHCPv6 prefix delegation interface statement + + instance number + Prefix delegation instance (>= 0) + + + + @@ -19,6 +32,7 @@ + 64 @@ -52,28 +66,10 @@ - - - Site-Level aggregator (SLA) length - - 0-128 - Length of delegated prefix - - - - - - - - - - Acquire only config parameters, no address - - - + IPv6 "temporary" address diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 010eda45c..c1e93955e 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -93,7 +93,7 @@ def dict_merge(source, destination): tmp = deepcopy(destination) for key, value in source.items(): - if key not in tmp.keys(): + if key not in tmp: tmp[key] = value elif isinstance(source[key], dict): tmp[key] = dict_merge(source[key], tmp[key]) @@ -109,15 +109,15 @@ def T2665_default_dict_cleanup(dict): """ Cleanup default keys for tag nodes https://phabricator.vyos.net/T2665. """ # Cleanup for vif in ['vif', 'vif_s']: - if vif in dict.keys(): - for key in ['ip', 'mtu']: - if key in dict[vif].keys(): + if vif in dict: + for key in ['ip', 'mtu', 'dhcpv6_options']: + if key in dict[vif]: del dict[vif][key] # cleanup VIF-S defaults - if 'vif_c' in dict[vif].keys(): - for key in ['ip', 'mtu']: - if key in dict[vif]['vif_c'].keys(): + if 'vif_c' in dict[vif]: + for key in ['ip', 'mtu', 'dhcpv6_options']: + if key in dict[vif]['vif_c']: del dict[vif]['vif_c'][key] # If there is no vif-c defined and we just cleaned the default # keys - we can clean the entire vif-c dict as it's useless @@ -129,6 +129,21 @@ def T2665_default_dict_cleanup(dict): if not dict[vif]: del dict[vif] + if 'dhcpv6_options' in dict and 'pd' in dict['dhcpv6_options']: + if 'length' in dict['dhcpv6_options']['pd']: + del dict['dhcpv6_options']['pd']['length'] + + # delete empty dicts + if 'dhcpv6_options' in dict: + if 'pd' in dict['dhcpv6_options']: + # test if 'pd' is an empty node so we can remove it + if not dict['dhcpv6_options']['pd']: + del dict['dhcpv6_options']['pd'] + + # test if 'dhcpv6_options' is an empty node so we can remove it + if not dict['dhcpv6_options']: + del dict['dhcpv6_options'] + return dict def leaf_node_changed(conf, path): @@ -193,6 +208,16 @@ def get_removed_vlans(conf, dict): return dict + +def dict_add_dhcpv6pd_defaults(defaults, config_dict): + # Implant default dictionary for DHCPv6-PD instances + if 'dhcpv6_options' in config_dict and 'pd' in config_dict['dhcpv6_options']: + for pd, pd_config in config_dict['dhcpv6_options']['pd'].items(): + config_dict['dhcpv6_options']['pd'][pd] = dict_merge( + defaults, pd_config) + + return config_dict + def get_interface_dict(config, base, ifname=''): """ Common utility function to retrieve and mandgle the interfaces available @@ -257,15 +282,30 @@ def get_interface_dict(config, base, ifname=''): # remove wrongly inserted values dict = T2665_default_dict_cleanup(dict) - # The values are identical for vif, vif-s and vif-c as the all include the same - # XML definitions which hold the defaults + # Implant default dictionary for DHCPv6-PD instances + default_pd_values = defaults(base + ['dhcpv6-options', 'pd']) + dict = dict_add_dhcpv6pd_defaults(default_pd_values, dict) + + # Implant default dictionary in vif/vif-s VLAN interfaces. Values are + # identical for all types of VLAN interfaces as they all include the same + # XML definitions which hold the defaults. default_vif_values = defaults(base + ['vif']) for vif, vif_config in dict.get('vif', {}).items(): - vif_config = dict_merge(default_vif_values, vif_config) + dict['vif'][vif] = dict_add_dhcpv6pd_defaults( + default_pd_values, vif_config) + dict['vif'][vif] = T2665_default_dict_cleanup( + dict_merge(default_vif_values, vif_config)) + for vif_s, vif_s_config in dict.get('vif_s', {}).items(): - vif_s_config = dict_merge(default_vif_values, vif_s_config) + dict['vif_s'][vif_s] = dict_add_dhcpv6pd_defaults( + default_pd_values, vif_s_config) + dict['vif_s'][vif_s] = T2665_default_dict_cleanup( + dict_merge(default_vif_values, vif_s_config)) for vif_c, vif_c_config in vif_s_config.get('vif_c', {}).items(): - vif_c_config = dict_merge(default_vif_values, vif_c_config) + dict['vif_s'][vif_s]['vif_c'][vif_c] = dict_add_dhcpv6pd_defaults( + default_pd_values, vif_c_config) + dict['vif_s'][vif_s]['vif_c'][vif_c] = T2665_default_dict_cleanup( + dict_merge(default_vif_values, vif_c_config)) # Check vif, vif-s/vif-c VLAN interfaces for removal dict = get_removed_vlans(config, dict) diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py index 928113b49..901ea769c 100755 --- a/src/conf_mode/interfaces-pppoe.py +++ b/src/conf_mode/interfaces-pppoe.py @@ -15,7 +15,6 @@ # along with this program. If not, see . import os -import jmespath from sys import exit from copy import deepcopy @@ -104,8 +103,7 @@ def generate(pppoe): render(script_pppoe_ipv6_up, 'pppoe/ipv6-up.script.tmpl', pppoe, trim_blocks=True, permission=0o755) - tmp = jmespath.search('dhcpv6_options.prefix_delegation.interface', pppoe) - if tmp and len(tmp) > 0: + if 'dhcpv6_options' in pppoe and 'pd' in pppoe['dhcpv6_options']: # ipv6.tmpl relies on ifname - this should be made consitent in the # future better then double key-ing the same value render(config_wide_dhcp6c, 'dhcp-client/ipv6.tmpl', pppoe, trim_blocks=True) diff --git a/src/migration-scripts/interfaces/11-to-12 b/src/migration-scripts/interfaces/11-to-12 new file mode 100755 index 000000000..0dad24642 --- /dev/null +++ b/src/migration-scripts/interfaces/11-to-12 @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 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 . + +# - rename 'dhcpv6-options prefix-delegation' from single node to a new tag node +# 'dhcpv6-options pd 0' +# - delete 'sla-len' from CLI - value is calculated on demand + +from sys import exit, argv +from vyos.configtree import ConfigTree + +if __name__ == '__main__': + if (len(argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = argv[1] + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + + for type in config.list_nodes(['interfaces']): + for interface in config.list_nodes(['interfaces', type]): + # cache current config tree + base_path = ['interfaces', type, interface, 'dhcpv6-options'] + old_base = base_path + ['prefix-delegation'] + new_base = base_path + ['pd'] + if config.exists(old_base): + config.set(new_base) + config.set_tag(new_base) + config.copy(old_base, new_base + ['0']) + config.delete(old_base) + + for pd in config.list_nodes(new_base): + for tmp in config.list_nodes(new_base + [pd, 'interface']): + sla_config = new_base + [pd, 'interface', tmp, 'sla-len'] + if config.exists(sla_config): + config.delete(sla_config) + + try: + with open(file_name, 'w') as f: + f.write(config.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) -- cgit v1.2.3 From a574a01ab5c02aeb90c6d099dce482c1bf8be096 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 23 Aug 2020 00:16:45 +0200 Subject: dhcpv6-pd: T2677: add support for rapid-commit option When rapid-commit is specified, dhcp6c will include a rapid-commit option in solicit messages and wait for an immediate reply instead of advertisements. --- data/templates/dhcp-client/ipv6.tmpl | 3 +++ interface-definitions/include/dhcpv6-options.xml.i | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'interface-definitions') diff --git a/data/templates/dhcp-client/ipv6.tmpl b/data/templates/dhcp-client/ipv6.tmpl index 85841fe94..68f668117 100644 --- a/data/templates/dhcp-client/ipv6.tmpl +++ b/data/templates/dhcp-client/ipv6.tmpl @@ -11,6 +11,9 @@ interface {{ ifname }} { {% if dhcpv6_options is not defined or dhcpv6_options.temporary is not defined %} send ia-na 0; # non-temporary address {% endif %} +{% if dhcpv6_options is defined and dhcpv6_options.rapid_commit is defined %} + send rapid-commit; # wait for immediate reply instead of advertisements +{% endif %} {% endif %} {% if dhcpv6_options is defined and dhcpv6_options.pd is defined %} {% for pd in dhcpv6_options.pd %} diff --git a/interface-definitions/include/dhcpv6-options.xml.i b/interface-definitions/include/dhcpv6-options.xml.i index 5d088b83d..b0a806806 100644 --- a/interface-definitions/include/dhcpv6-options.xml.i +++ b/interface-definitions/include/dhcpv6-options.xml.i @@ -70,9 +70,15 @@ + + + Wait for immediate reply instead of advertisements + + + - IPv6 "temporary" address + IPv6 temporary address -- cgit v1.2.3 From 12f566f4566c8ef115b15f0c11280cb3ea9a7673 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 26 Aug 2020 20:00:14 +0200 Subject: pppoe-server: bugfix MPPE node definition PPPoE server supports setting the MPPE preferences, it is implemented as node with leafNodes for require, prefer and deny. This will render a wrong AccelPPP configuration as it will be "None" if specified. pppoe-server { ppp-options { mppe { deny require } } } instead it must be: pppoe-server { ppp-options { mppe require } } --- interface-definitions/service_pppoe-server.xml.in | 42 +++++++++++------------ 1 file changed, 20 insertions(+), 22 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in index c7ba2617a..605f47b37 100644 --- a/interface-definitions/service_pppoe-server.xml.in +++ b/interface-definitions/service_pppoe-server.xml.in @@ -311,31 +311,29 @@ - + Specifies MPPE negotiation preference. (default prefer mppe) + + deny prefer require + + + deny + Deny MPPE + + + prefer + Ask client for MPPE - do not fail on reject + + + require + Ask client for MPPE - drop connection on reject + + + ^(deny|prefer|require)$ + - - - - Ask client for MPPE, if it rejects then drop the connection - - - - - - Ask client for MPPE, if it rejects do not fail - - - - - - Deny MPPE - - - - - + LCP echo-requests/sec -- cgit v1.2.3