summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jenkinsfile8
-rw-r--r--interface-definitions/interfaces-ethernet.xml12
-rw-r--r--op-mode-definitions/ipoe-server.xml10
-rw-r--r--op-mode-definitions/pppoe-server.xml10
-rw-r--r--python/vyos/ifconfig.py648
-rwxr-xr-xsrc/conf_mode/interface-bonding.py51
-rwxr-xr-xsrc/conf_mode/interface-bridge.py41
-rwxr-xr-xsrc/conf_mode/interface-dummy.py23
-rwxr-xr-xsrc/conf_mode/interface-ethernet.py32
-rwxr-xr-xsrc/conf_mode/interface-loopback.py23
-rwxr-xr-xsrc/conf_mode/interface-openvpn.py46
-rwxr-xr-xsrc/conf_mode/interface-vxlan.py18
-rwxr-xr-xsrc/conf_mode/interface-wireguard.py333
-rwxr-xr-xsrc/conf_mode/ipoe_server.py3
-rwxr-xr-xsrc/conf_mode/ntp.py3
-rwxr-xr-xsrc/services/vyos-hostsd4
-rw-r--r--src/tests/test_ntp.py259
17 files changed, 500 insertions, 1024 deletions
diff --git a/Jenkinsfile b/Jenkinsfile
index 8e70fdc1d..20eb2531d 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -72,7 +72,6 @@ setDescription()
pipeline {
agent {
docker {
- label 'Docker'
args '--sysctl net.ipv6.conf.lo.disable_ipv6=0 -e GOSU_UID=1006 -e GOSU_GID=1006'
image 'vyos/vyos-build:current'
}
@@ -97,12 +96,7 @@ pipeline {
steps {
script {
dir('build') {
- sh """
- #!/bin/bash
- sudo apt-get -o Acquire::Check-Valid-Until=false update
- sudo mk-build-deps -i -r -t \'apt-get --no-install-recommends -yq\' debian/control
- dpkg-buildpackage -b -us -uc -tc
- """
+ sh "dpkg-buildpackage -b -us -uc -tc"
}
}
}
diff --git a/interface-definitions/interfaces-ethernet.xml b/interface-definitions/interfaces-ethernet.xml
index 9244f3b5f..e4a56b216 100644
--- a/interface-definitions/interfaces-ethernet.xml
+++ b/interface-definitions/interfaces-ethernet.xml
@@ -394,6 +394,10 @@
<tagNode name="vif-s">
<properties>
<help>QinQ TAG-S Virtual Local Area Network (VLAN) ID</help>
+ <valueHelp>
+ <format>0-4094</format>
+ <description>QinQ TAG-S Virtual Local Area Network (VLAN) ID</description>
+ </valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-4094"/>
</constraint>
@@ -551,6 +555,10 @@
<tagNode name="vif-c">
<properties>
<help>QinQ TAG-C Virtual Local Area Network (VLAN) ID</help>
+ <valueHelp>
+ <format>0-4094</format>
+ <description>QinQ TAG-C Virtual Local Area Network (VLAN) ID</description>
+ </valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-4094"/>
</constraint>
@@ -692,6 +700,10 @@
<tagNode name="vif">
<properties>
<help>Virtual Local Area Network (VLAN) ID</help>
+ <valueHelp>
+ <format>0-4094</format>
+ <description>Virtual Local Area Network (VLAN) ID</description>
+ </valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-4094"/>
</constraint>
diff --git a/op-mode-definitions/ipoe-server.xml b/op-mode-definitions/ipoe-server.xml
index ea14e9a5c..369ceebea 100644
--- a/op-mode-definitions/ipoe-server.xml
+++ b/op-mode-definitions/ipoe-server.xml
@@ -23,4 +23,14 @@
</node>
</children>
</node>
+ <node name="restart">
+ <children>
+ <leafNode name="ipoe-server">
+ <properties>
+ <help>show ipoe-server status</help>
+ </properties>
+ <command>if [ -e /var/run/accel_ipoe.pid ]; then /usr/bin/accel-cmd restart -p 2002; else echo "ipoe-server not running"; fi</command>
+ </leafNode>
+ </children>
+ </node>
</interfaceDefinition>
diff --git a/op-mode-definitions/pppoe-server.xml b/op-mode-definitions/pppoe-server.xml
index a2366c710..1f6f7f844 100644
--- a/op-mode-definitions/pppoe-server.xml
+++ b/op-mode-definitions/pppoe-server.xml
@@ -29,4 +29,14 @@
</node>
</children>
</node>
+ <node name="restart">
+ <children>
+ <leafNode name="pppoe-server">
+ <properties>
+ <help>Restarts pppoe-server</help>
+ </properties>
+ <command>if [ -e /var/run/accel_pppoe.pid ]; then /usr/bin/accel-cmd restart -p 2001; else echo "pppoe-server not running"; fi</command>
+ </leafNode>
+ </children>
+ </node>
</interfaceDefinition>
diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py
index 8d4923957..a77cde5e7 100644
--- a/python/vyos/ifconfig.py
+++ b/python/vyos/ifconfig.py
@@ -60,7 +60,6 @@ class Interface:
>>> i = Interface('eth0')
"""
self._ifname = str(ifname)
- self._state = 'down'
if not os.path.exists('/sys/class/net/{}'.format(ifname)) and not type:
raise Exception('interface "{}" not found'.format(self._ifname))
@@ -145,28 +144,26 @@ class Interface:
cmd = 'ip link del dev {}'.format(self._ifname)
self._cmd(cmd)
- @property
- def mtu(self):
+ def get_mtu(self):
"""
Get/set interface mtu in bytes.
Example:
>>> from vyos.ifconfig import Interface
- >>> Interface('eth0').mtu
+ >>> Interface('eth0').get_mtu()
'1500'
"""
return self._read_sysfs('/sys/class/net/{}/mtu'
.format(self._ifname))
- @mtu.setter
- def mtu(self, mtu):
+ def set_mtu(self, mtu):
"""
Get/set interface mtu in bytes.
Example:
>>> from vyos.ifconfig import Interface
- >>> Interface('eth0').mtu = 1400
- >>> Interface('eth0').mtu
+ >>> Interface('eth0').set_mtu(1400)
+ >>> Interface('eth0').get_mtu()
'1400'
"""
if mtu < 68 or mtu > 9000:
@@ -175,29 +172,13 @@ class Interface:
return self._write_sysfs('/sys/class/net/{}/mtu'
.format(self._ifname), mtu)
- @property
- def mac(self):
+ def set_mac(self, mac):
"""
- Get/set interface mac address
+ Set interface MAC (Media Access Contrl) address to given value.
Example:
>>> from vyos.ifconfig import Interface
- >>> Interface('eth0').mac
- '00:0c:29:11:aa:cc'
- """
- return self._read_sysfs('/sys/class/net/{}/address'
- .format(self._ifname))
-
- @mac.setter
- def mac(self, mac):
- """
- Get/set interface mac address
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').mac = '00:90:43:fe:fe:1b'
- >>> Interface('eth0').mac
- '00:90:43:fe:fe:1b'
+ >>> Interface('eth0').set_mac('00:50:ab:cd:ef:01')
"""
# on interface removal (ethernet) an empty string is passed - ignore it
if not mac:
@@ -226,50 +207,22 @@ class Interface:
cmd = 'ip link set dev {} address {}'.format(self._ifname, mac)
self._cmd(cmd)
- @property
- def arp_cache_tmo(self):
- """
- Get configured ARP cache timeout value from interface in seconds.
- Internal Kernel representation is in milliseconds.
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').arp_cache_tmo
- '30'
- """
- return (self._read_sysfs('/proc/sys/net/ipv4/neigh/{0}/base_reachable_time_ms'
- .format(self._ifname)) / 1000)
- @arp_cache_tmo.setter
- def arp_cache_tmo(self, tmo):
+ def set_arp_cache_tmo(self, tmo):
"""
Set ARP cache timeout value in seconds. Internal Kernel representation
is in milliseconds.
Example:
>>> from vyos.ifconfig import Interface
- >>> Interface('eth0').arp_cache_tmo = '40'
+ >>> Interface('eth0').set_arp_cache_tmo(40)
"""
return self._write_sysfs('/proc/sys/net/ipv4/neigh/{0}/base_reachable_time_ms'
.format(self._ifname), (int(tmo) * 1000))
- @property
- def link_detect(self):
- """
- How does the kernel act when receiving packets on 'down' interfaces
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').link_detect
- '0'
- """
- return self._read_sysfs('/proc/sys/net/ipv4/conf/{0}/link_filter'
- .format(self._ifname))
-
- @link_detect.setter
- def link_detect(self, link_filter):
+ def set_link_detect(self, link_filter):
"""
- Konfigure kernel response in packets received on interfaces that are 'down'
+ Configure kernel response in packets received on interfaces that are 'down'
0 - Allow packets to be received for the address on this interface
even if interface is disabled or no carrier.
@@ -285,44 +238,25 @@ class Interface:
Example:
>>> from vyos.ifconfig import Interface
- >>> Interface('eth0').link_detect = '1'
+ >>> Interface('eth0').set_link_detect(1)
"""
- if link_filter >= 0 and link_filter <= 2:
+ if int(link_filter) >= 0 and int(link_filter) <= 2:
return self._write_sysfs('/proc/sys/net/ipv4/conf/{0}/link_filter'
.format(self._ifname), link_filter)
else:
raise ValueError("Value out of range")
- @property
- def ifalias(self):
- """
- Get/set interface alias name
-
- Example:
-
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').ifalias
- ''
- """
- return self._read_sysfs('/sys/class/net/{}/ifalias'
- .format(self._ifname))
-
- @ifalias.setter
- def ifalias(self, ifalias=None):
+ def set_alias(self, ifalias=None):
"""
- Get/set interface alias name
+ Set interface alias name used by e.g. SNMP
Example:
>>> from vyos.ifconfig import Interface
- >>> Interface('eth0').ifalias = 'VyOS upstream interface'
- >>> Interface('eth0').ifalias
- 'VyOS upstream interface'
+ >>> Interface('eth0').set_alias('VyOS upstream interface')
- to clear interface alias e.g. delete it use:
+ to clear alias e.g. delete it use:
- >>> Interface('eth0').ifalias = ''
- >>> Interface('eth0').ifalias
- ''
+ >>> Interface('eth0').set_ifalias('')
"""
if not ifalias:
# clear interface alias
@@ -331,63 +265,43 @@ class Interface:
self._write_sysfs('/sys/class/net/{}/ifalias'
.format(self._ifname), ifalias)
- @property
- def state(self):
+ def get_state(self):
"""
Enable (up) / Disable (down) an interface
Example:
>>> from vyos.ifconfig import Interface
- >>> Interface('eth0').state
+ >>> Interface('eth0').get_state()
'up'
"""
return self._read_sysfs('/sys/class/net/{}/operstate'
.format(self._ifname))
- @state.setter
- def state(self, state):
+ def set_state(self, state):
"""
Enable (up) / Disable (down) an interface
Example:
>>> from vyos.ifconfig import Interface
- >>> Interface('eth0').state = 'down'
- >>> Interface('eth0').state
+ >>> Interface('eth0').set_state('down')
+ >>> Interface('eth0').get_state()
'down'
"""
if state not in ['up', 'down']:
raise ValueError('state must be "up" or "down"')
- self._state = state
-
# Assemble command executed on system. Unfortunately there is no way
# to up/down an interface via sysfs
cmd = 'ip link set dev {} {}'.format(self._ifname, state)
self._cmd(cmd)
- @property
- def proxy_arp(self):
- """
- Get current proxy ARP configuration from sysfs. Default: 0
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').proxy_arp
- '0'
- """
- return self._read_sysfs('/proc/sys/net/ipv4/conf/{}/proxy_arp'
- .format(self._ifname))
-
- @proxy_arp.setter
- def proxy_arp(self, enable):
+ def set_proxy_arp(self, enable):
"""
Set per interface proxy ARP configuration
Example:
>>> from vyos.ifconfig import Interface
- >>> Interface('eth0').proxy_arp = 1
- >>> Interface('eth0').proxy_arp
- '1'
+ >>> Interface('eth0').set_proxy_arp(1)
"""
if int(enable) >= 0 and int(enable) <= 1:
return self._write_sysfs('/proc/sys/net/ipv4/conf/{}/proxy_arp'
@@ -395,8 +309,7 @@ class Interface:
else:
raise ValueError("Value out of range")
- @property
- def proxy_arp_pvlan(self):
+ def set_proxy_arp_pvlan(self, enable):
"""
Private VLAN proxy arp.
Basically allow proxy arp replies back to the same interface
@@ -418,38 +331,7 @@ class Interface:
Example:
>>> from vyos.ifconfig import Interface
- >>> Interface('eth0').proxy_arp_pvlan
- '0'
- """
- return self._read_sysfs('/proc/sys/net/ipv4/conf/{}/proxy_arp_pvlan'
- .format(self._ifname))
-
- @proxy_arp_pvlan.setter
- def proxy_arp_pvlan(self, enable):
- """
- Private VLAN proxy arp.
- Basically allow proxy arp replies back to the same interface
- (from which the ARP request/solicitation was received).
-
- This is done to support (ethernet) switch features, like RFC
- 3069, where the individual ports are NOT allowed to
- communicate with each other, but they are allowed to talk to
- the upstream router. As described in RFC 3069, it is possible
- to allow these hosts to communicate through the upstream
- router by proxy_arp'ing. Don't need to be used together with
- proxy_arp.
-
- This technology is known by different names:
- In RFC 3069 it is called VLAN Aggregation.
- Cisco and Allied Telesyn call it Private VLAN.
- Hewlett-Packard call it Source-Port filtering or port-isolation.
- Ericsson call it MAC-Forced Forwarding (RFC Draft).
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').proxy_arp_pvlan = 1
- >>> Interface('eth0').proxy_arp_pvlan
- '1'
+ >>> Interface('eth0').set_proxy_arp_pvlan(1)
"""
if int(enable) >= 0 and int(enable) <= 1:
return self._write_sysfs('/proc/sys/net/ipv4/conf/{}/proxy_arp_pvlan'
@@ -581,7 +463,7 @@ class Interface:
with open(self._dhcp_cfg_file, 'w') as f:
f.write(dhcp_text)
- if self._state == 'up':
+ if self.get_state() == 'up':
cmd = 'start-stop-daemon --start --quiet --pidfile ' + \
self._dhcp_pid_file
cmd += ' --exec /sbin/dhclient --'
@@ -659,7 +541,7 @@ class Interface:
with open(self._dhcpv6_cfg_file, 'w') as f:
f.write(dhcpv6_text)
- if self._state == 'up':
+ if self.get_state() == 'up':
# https://bugs.launchpad.net/ubuntu/+source/ifupdown/+bug/1447715
#
# wee need to wait for IPv6 DAD to finish once and interface is added
@@ -731,6 +613,21 @@ class LoopbackIf(Interface):
def __init__(self, ifname):
super().__init__(ifname, type='loopback')
+ def remove(self):
+ """
+ Loopback interface can not be deleted from operating system. We can
+ only remove all assigned IP addresses.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> i = LoopbackIf('lo').remove()
+ """
+ # remove all assigned IP addresses from interface
+ for addr in self.get_addr():
+ self.del_addr(addr)
+
+ # question: do we also delerte the loopback address? 127.0.0.1/8
+
class DummyIf(Interface):
@@ -744,6 +641,47 @@ class DummyIf(Interface):
super().__init__(ifname, type='dummy')
+class STPIf(Interface):
+ """
+ A spanning-tree capable interface. This applies only to bridge port member
+ interfaces!
+ """
+ def __init__(self, ifname):
+ super().__init__(ifname)
+
+ def set_path_cost(self, cost):
+ """
+ Set interface path cost, only relevant for STP enabled interfaces
+
+ Example:
+
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_path_cost(4)
+ """
+ if not os.path.isfile('/sys/class/net/{}/brport/path_cost'
+ .format(self._ifname)):
+ raise TypeError('{} is not a bridge port member'.format(self._ifname))
+
+ return self._write_sysfs('/sys/class/net/{}/brport/path_cost'
+ .format(self._ifname), cost)
+
+ def set_path_priority(self, priority):
+ """
+ Set interface path priority, only relevant for STP enabled interfaces
+
+ Example:
+
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_path_priority(4)
+ """
+ if not os.path.isfile('/sys/class/net/{}/brport/priority'
+ .format(self._ifname)):
+ raise TypeError('{} is not a bridge port member'.format(self._ifname))
+
+ return self._write_sysfs('/sys/class/net/{}/brport/priority'
+ .format(self._ifname), priority)
+
+
class BridgeIf(Interface):
"""
@@ -758,167 +696,73 @@ class BridgeIf(Interface):
def __init__(self, ifname):
super().__init__(ifname, type='bridge')
- @property
- def ageing_time(self):
- """
- Return configured bridge interface MAC address aging time in seconds.
- Internal kernel representation is in centiseconds, thus its converted
- in the end. Kernel default is 300 seconds.
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').aging_time
- '300'
- """
- return (self._read_sysfs('/sys/class/net/{}/bridge/ageing_time'
- .format(self._ifname)) / 100)
-
- @ageing_time.setter
- def ageing_time(self, time):
+ def set_ageing_time(self, time):
"""
Set bridge interface MAC address aging time in seconds. Internal kernel
representation is in centiseconds. Kernel default is 300 seconds.
Example:
- >>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').ageing_time = 2
+ >>> from vyos.ifconfig import BridgeIf
+ >>> BridgeIf('br0').ageing_time(2)
"""
time = int(time) * 100
return self._write_sysfs('/sys/class/net/{}/bridge/ageing_time'
.format(self._ifname), time)
- @property
- def forward_delay(self):
- """
- Get bridge forwarding delay in seconds. Internal Kernel representation
- is in centiseconds.
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').ageing_time
- '3'
- """
- return (self._read_sysfs('/sys/class/net/{}/bridge/forward_delay'
- .format(self._ifname)) / 100)
-
- @forward_delay.setter
- def forward_delay(self, time):
+ def set_forward_delay(self, time):
"""
Set bridge forwarding delay in seconds. Internal Kernel representation
is in centiseconds.
Example:
- >>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').forward_delay = 15
+ >>> from vyos.ifconfig import BridgeIf
+ >>> BridgeIf('br0').forward_delay(15)
"""
return self._write_sysfs('/sys/class/net/{}/bridge/forward_delay'
.format(self._ifname), (int(time) * 100))
- @property
- def hello_time(self):
- """
- Get bridge hello time in seconds. Internal Kernel representation
- is in centiseconds.
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').hello_time
- '2'
- """
- return (self._read_sysfs('/sys/class/net/{}/bridge/hello_time'
- .format(self._ifname)) / 100)
-
- @hello_time.setter
- def hello_time(self, time):
+ def set_hello_time(self, time):
"""
Set bridge hello time in seconds. Internal Kernel representation
is in centiseconds.
Example:
- >>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').hello_time = 2
+ >>> from vyos.ifconfig import BridgeIf
+ >>> BridgeIf('br0').set_hello_time(2)
"""
return self._write_sysfs('/sys/class/net/{}/bridge/hello_time'
.format(self._ifname), (int(time) * 100))
- @property
- def max_age(self):
- """
- Get bridge max max message age in seconds. Internal Kernel representation
- is in centiseconds.
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').max_age
- '20'
- """
-
- return (self._read_sysfs('/sys/class/net/{}/bridge/max_age'
- .format(self._ifname)) / 100)
-
- @max_age.setter
- def max_age(self, time):
+ def set_max_age(self, time):
"""
Set bridge max message age in seconds. Internal Kernel representation
is in centiseconds.
Example:
>>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').max_age = 30
+ >>> BridgeIf('br0').set_max_age(30)
"""
return self._write_sysfs('/sys/class/net/{}/bridge/max_age'
.format(self._ifname), (int(time) * 100))
- @property
- def priority(self):
- """
- Get bridge max aging time in seconds.
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').priority
- '32768'
- """
- return self._read_sysfs('/sys/class/net/{}/bridge/priority'
- .format(self._ifname))
-
- @priority.setter
- def priority(self, priority):
+ def set_priority(self, priority):
"""
Set bridge max aging time in seconds.
Example:
- >>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').priority = 8192
+ >>> from vyos.ifconfig import BridgeIf
+ >>> BridgeIf('br0').set_priority(8192)
"""
return self._write_sysfs('/sys/class/net/{}/bridge/priority'
.format(self._ifname), priority)
- @property
- def stp_state(self):
- """
- Get current bridge STP (Spanning Tree) state.
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').stp_state
- '0'
- """
-
- state = 0
- with open('/sys/class/net/{}/bridge/stp_state'.format(self._ifname), 'r') as f:
- state = int(f.read().rstrip('\n'))
-
- return state
-
- @stp_state.setter
- def stp_state(self, state):
+ def set_stp(self, state):
"""
- Set bridge STP (Spannign Tree) state. 0 -> STP disabled, 1 -> STP enabled
+ Set bridge STP (Spanning Tree) state. 0 -> STP disabled, 1 -> STP enabled
Example:
- >>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').stp_state = 1
+ >>> from vyos.ifconfig import BridgeIf
+ >>> BridgeIf('br0').set_stp(1)
"""
if int(state) >= 0 and int(state) <= 1:
@@ -927,21 +771,8 @@ class BridgeIf(Interface):
else:
raise ValueError("Value out of range")
- @property
- def multicast_querier(self):
- """
- Get bridge multicast querier membership state.
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').multicast_querier
- '0'
- """
- return self._read_sysfs('/sys/class/net/{}/bridge/multicast_querier'
- .format(self._ifname))
- @multicast_querier.setter
- def multicast_querier(self, enable):
+ def set_multicast_querier(self, enable):
"""
Sets whether the bridge actively runs a multicast querier or not. When a
bridge receives a 'multicast host membership' query from another network
@@ -952,7 +783,7 @@ class BridgeIf(Interface):
Example:
>>> from vyos.ifconfig import Interface
- >>> BridgeIf('br0').multicast_querier = 1
+ >>> BridgeIf('br0').set_multicast_querier(1)
"""
if int(enable) >= 0 and int(enable) <= 1:
return self._write_sysfs('/sys/class/net/{}/bridge/multicast_querier'
@@ -960,6 +791,7 @@ class BridgeIf(Interface):
else:
raise ValueError("Value out of range")
+
def add_port(self, interface):
"""
Add physical interface to bridge (member port)
@@ -983,31 +815,6 @@ class BridgeIf(Interface):
cmd = 'ip link set dev {} nomaster'.format(interface)
self._cmd(cmd)
- def set_cost(self, interface, cost):
- """
- Set interface path cost, only relevant for STP enabled interfaces
-
- Example:
-
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').path_cost(4)
- """
- return self._write_sysfs('/sys/class/net/{}/brif/{}/path_cost'
- .format(self._ifname, interface), cost)
-
- def set_priority(self, interface, priority):
- """
- Set interface path priority, only relevant for STP enabled interfaces
-
- Example:
-
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').priority(4)
- """
- return self._write_sysfs('/sys/class/net/{}/brif/{}/priority'
- .format(self._ifname, interface), priority)
-
-
class VLANIf(Interface):
"""
This class handels the creation and removal of a VLAN interface. It serves
@@ -1027,12 +834,22 @@ class VLANIf(Interface):
>>> i = Interface('eth0')
>>> i.remove()
"""
- # do we have sub interfaces (VLANs)?
- # we apply a regex matching subinterfaces (indicated by a .) of a
- # parent interface. 'bond0(?:\.\d+){1,2}' will match vif and vif-s/vif-c
- # subinterfaces
+ # Do we have sub interfaces (VLANs)? We apply a regex matching
+ # subinterfaces (indicated by a .) of a parent interface.
+ #
+ # As interfaces need to be deleted "in order" starting from Q-in-Q
+ # we delete them first.
+ vlan_ifs = [f for f in os.listdir(r'/sys/class/net') \
+ if re.match(self._ifname + r'(?:\.\d+)(?:\.\d+)', f)]
+
+ for vlan in vlan_ifs:
+ Interface(vlan).remove()
+
+ # After deleting all Q-in-Q interfaces delete other VLAN interfaces
+ # which probably acted as parent to Q-in-Q or have been regular 802.1q
+ # interface.
vlan_ifs = [f for f in os.listdir(r'/sys/class/net') \
- if re.match(self._ifname + r'(?:\.\d+){1,2}', f)]
+ if re.match(self._ifname + r'(?:\.\d+)', f)]
for vlan in vlan_ifs:
Interface(vlan).remove()
@@ -1106,8 +923,7 @@ class VLANIf(Interface):
>>> i.del_vlan()
"""
vlan_ifname = self._ifname + '.' + str(vlan_id)
- tmp = VLANIf(vlan_ifname)
- tmp.remove()
+ VLANIf(vlan_ifname).remove()
class EthernetIf(VLANIf):
@@ -1131,32 +947,6 @@ class EthernetIf(VLANIf):
link = os.readlink('/sys/class/net/{}/device/driver/module'.format(self._ifname))
return os.path.basename(link)
-
- def has_autoneg(self):
- """
- Not all drivers support autonegotiation.
-
- returns True -> Autonegotiation is supported by driver
- False -> Autonegotiation is not supported by driver
-
- Example:
- >>> from vyos.ifconfig import EthernetIf
- >>> i = EthernetIf('eth0')
- >>> i.has_autoneg()
- 'True'
- """
- regex = 'Supports auto-negotiation:[ ]\w+'
- tmp = self._cmd('/sbin/ethtool {}'.format(self._ifname))
- tmp = re.search(regex, tmp.decode())
-
- # Output is either 'Supports auto-negotiation: Yes' or
- # 'Supports auto-negotiation: No'
- if tmp.group().split(':')[1].lstrip() == "Yes":
- return True
- else:
- return False
-
-
def set_flow_control(self, enable):
"""
Changes the pause parameters of the specified Ethernet device.
@@ -1319,7 +1109,7 @@ class BondIf(VLANIf):
for s in self.get_slaves():
slave = {
'ifname' : s,
- 'state': Interface(s).state
+ 'state': Interface(s).get_state()
}
slave_list.append(slave)
@@ -1330,29 +1120,10 @@ class BondIf(VLANIf):
# physical interface
for slave in slave_list:
i = Interface(slave['ifname'])
- i.state = slave['state']
-
- @property
- def xmit_hash_policy(self):
- """
- Selects the transmit hash policy to use for slave selection in
- balance-xor, 802.3ad, and tlb modes. Possible values are: layer2,
- layer2+3, layer3+4, encap2+3, encap3+4.
+ i.set_state(slave['state'])
- The default value is layer2
-
- Example:
- >>> from vyos.ifconfig import BondIf
- >>> BondIf('bond0').xmit_hash_policy
- 'layer3+4'
- """
- # Linux Kernel appends has policy value to string, e.g. 'layer3+4 1',
- # so remove the later part and only return the mode as string.
- return self._read_sysfs('/sys/class/net/{}/bonding/xmit_hash_policy'
- .format(self._ifname)).split()[0]
- @xmit_hash_policy.setter
- def xmit_hash_policy(self, mode):
+ def set_hash_policy(self, mode):
"""
Selects the transmit hash policy to use for slave selection in
balance-xor, 802.3ad, and tlb modes. Possible values are: layer2,
@@ -1362,59 +1133,50 @@ class BondIf(VLANIf):
Example:
>>> from vyos.ifconfig import BondIf
- >>> BondIf('bond0').xmit_hash_policy = 'layer2+3'
- >>> BondIf('bond0').proxy_arp
- '1'
+ >>> BondIf('bond0').set_hash_policy('layer2+3')
"""
if not mode in ['layer2', 'layer2+3', 'layer3+4', 'encap2+3', 'encap3+4']:
raise ValueError("Value out of range")
return self._write_sysfs('/sys/class/net/{}/bonding/xmit_hash_policy'
.format(self._ifname), mode)
- @property
- def arp_interval(self):
+ def set_arp_interval(self, interval):
"""
Specifies the ARP link monitoring frequency in milliseconds.
- The ARP monitor works by periodically checking the slave devices to
- determine whether they have sent or received traffic recently (the
- precise criteria depends upon the bonding mode, and the state of the
- slave). Regular traffic is generated via ARP probes issued for the
- addresses specified by the arp_ip_target option.
-
- The default value is 0.
-
- Example:
- >>> from vyos.ifconfig import BondIf
- >>> BondIf('bond0').arp_interval
- '0'
- """
- return self._read_sysfs('/sys/class/net/{}/bonding/arp_interval'
- .format(self._ifname))
+ The ARP monitor works by periodically checking the slave devices
+ to determine whether they have sent or received traffic recently
+ (the precise criteria depends upon the bonding mode, and the
+ state of the slave). Regular traffic is generated via ARP probes
+ issued for the addresses specified by the arp_ip_target option.
- @arp_interval.setter
- def arp_interval(self, time):
- """
- Specifies the IP addresses to use as ARP monitoring peers when
- arp_interval is > 0. These are the targets of the ARP request sent to
- determine the health of the link to the targets. Specify these values
- in ddd.ddd.ddd.ddd format. Multiple IP addresses must be separated by
- a comma. At least one IP address must be given for ARP monitoring to
- function. The maximum number of targets that can be specified is 16.
+ If ARP monitoring is used in an etherchannel compatible mode
+ (modes 0 and 2), the switch should be configured in a mode that
+ evenly distributes packets across all links. If the switch is
+ configured to distribute the packets in an XOR fashion, all
+ replies from the ARP targets will be received on the same link
+ which could cause the other team members to fail.
- The default value is no IP addresses.
+ value of 0 disables ARP monitoring. The default value is 0.
Example:
>>> from vyos.ifconfig import BondIf
- >>> BondIf('bond0').arp_interval = '100'
- >>> BondIf('bond0').arp_interval
- '100'
- """
- return self._write_sysfs('/sys/class/net/{}/bonding/arp_interval'
- .format(self._ifname), time)
+ >>> BondIf('bond0').set_arp_interval('100')
+ """
+ if int(interval) == 0:
+ """
+ Specifies the MII link monitoring frequency in milliseconds.
+ This determines how often the link state of each slave is
+ inspected for link failures. A value of zero disables MII
+ link monitoring. A value of 100 is a good starting point.
+ """
+ return self._write_sysfs('/sys/class/net/{}/bonding/miimon'
+ .format(self._ifname), interval)
+ else:
+ return self._write_sysfs('/sys/class/net/{}/bonding/arp_interval'
+ .format(self._ifname), interval)
- @property
- def arp_ip_target(self):
+ def get_arp_ip_target(self):
"""
Specifies the IP addresses to use as ARP monitoring peers when
arp_interval is > 0. These are the targets of the ARP request sent to
@@ -1427,14 +1189,13 @@ class BondIf(VLANIf):
Example:
>>> from vyos.ifconfig import BondIf
- >>> BondIf('bond0').arp_ip_target
+ >>> BondIf('bond0').get_arp_ip_target()
'192.0.2.1'
"""
return self._read_sysfs('/sys/class/net/{}/bonding/arp_ip_target'
.format(self._ifname))
- @arp_ip_target.setter
- def arp_ip_target(self, target):
+ def set_arp_ip_target(self, target):
"""
Specifies the IP addresses to use as ARP monitoring peers when
arp_interval is > 0. These are the targets of the ARP request sent to
@@ -1447,52 +1208,13 @@ class BondIf(VLANIf):
Example:
>>> from vyos.ifconfig import BondIf
- >>> BondIf('bond0').arp_ip_target = '192.0.2.1'
- >>> BondIf('bond0').arp_ip_target
+ >>> BondIf('bond0').set_arp_ip_target('192.0.2.1')
+ >>> BondIf('bond0').get_arp_ip_target()
'192.0.2.1'
"""
return self._write_sysfs('/sys/class/net/{}/bonding/arp_ip_target'
.format(self._ifname), target)
- @property
- def miimon(self):
- """
- Specifies the MII link monitoring frequency in milliseconds.
- This determines how often the link state of each slave is
- inspected for link failures. A value of zero disables MII
- link monitoring. A value of 100 is a good starting point.
-
- The default value is 0.
-
- Example:
- >>> from vyos.ifconfig import BondIf
- >>> BondIf('bond0').miimon
- '250'
- """
- return self._read_sysfs('/sys/class/net/{}/bonding/miimon'
- .format(self._ifname))
-
-
- @miimon.setter
- def miimon(self, time):
- """
- Specifies the MII link monitoring frequency in milliseconds.
- This determines how often the link state of each slave is
- inspected for link failures. A value of zero disables MII
- link monitoring. A value of 100 is a good starting point.
-
- The default value is 0.
-
- Example:
- >>> from vyos.ifconfig import BondIf
- >>> BondIf('bond0').miimon = 250
- >>> BondIf('bond0').miimon
- '250'
- """
- return self._write_sysfs('/sys/class/net/{}/bonding/miimon'
- .format(self._ifname), time)
-
-
def add_port(self, interface):
"""
Enslave physical interface to bond.
@@ -1505,7 +1227,7 @@ class BondIf(VLANIf):
# 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).state = 'down'
+ Interface(interface).set_state('down')
return self._write_sysfs('/sys/class/net/{}/bonding/slaves'
.format(self._ifname), '+' + interface)
@@ -1534,28 +1256,8 @@ class BondIf(VLANIf):
.format(self._ifname))
return list(map(str, slaves.split()))
- @property
- def primary(self):
- """
- A string (eth0, eth2, etc) specifying which slave is the primary
- device. The specified device will always be the active slave while it
- is available. Only when the primary is off-line will alternate devices
- be used. This is useful when one slave is preferred over another, e.g.,
- when one slave has higher throughput than another.
- The primary option is only valid for active-backup, balance-tlb and
- balance-alb mode.
-
- Example:
- >>> from vyos.ifconfig import BondIf
- >>> BondIf('bond0').primary
- 'eth1'
- """
- return self._read_sysfs('/sys/class/net/{}/bonding/primary'
- .format(self._ifname))
-
- @primary.setter
- def primary(self, interface):
+ def set_primary(self, interface):
"""
A string (eth0, eth2, etc) specifying which slave is the primary
device. The specified device will always be the active slave while it
@@ -1568,9 +1270,7 @@ class BondIf(VLANIf):
Example:
>>> from vyos.ifconfig import BondIf
- >>> BondIf('bond0').primary = 'eth2'
- >>> BondIf('bond0').primary
- 'eth2'
+ >>> BondIf('bond0').set_primary('eth2')
"""
if not interface:
# reset primary interface
@@ -1579,25 +1279,7 @@ class BondIf(VLANIf):
return self._write_sysfs('/sys/class/net/{}/bonding/primary'
.format(self._ifname), interface)
- @property
- def mode(self):
- """
- Specifies one of the bonding policies. The default is balance-rr
- (round robin).
-
- Possible values are: balance-rr (0), active-backup (1), balance-xor (2),
- broadcast (3), 802.3ad (4), balance-tlb (5), balance-alb (6)
-
- Example:
- >>> from vyos.ifconfig import BondIf
- >>> BondIf('bond0').mode
- 'balance-rr'
- """
- return self._read_sysfs('/sys/class/net/{}/bonding/mode'
- .format(self._ifname)).split()[0]
-
- @mode.setter
- def mode(self, mode):
+ def set_mode(self, mode):
"""
Specifies one of the bonding policies. The default is balance-rr
(round robin).
@@ -1610,9 +1292,7 @@ class BondIf(VLANIf):
Example:
>>> from vyos.ifconfig import BondIf
- >>> BondIf('bond0').mode = '802.3ad'
- >>> BondIf('bond0').mode
- '802.3ad'
+ >>> BondIf('bond0').set_mode('802.3ad')
"""
if not mode in [
'balance-rr', 'active-backup', 'balance-xor', 'broadcast',
diff --git a/src/conf_mode/interface-bonding.py b/src/conf_mode/interface-bonding.py
index 9049913e6..4d5009c73 100755
--- a/src/conf_mode/interface-bonding.py
+++ b/src/conf_mode/interface-bonding.py
@@ -13,8 +13,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
import os
@@ -86,20 +84,20 @@ def apply_vlan_config(vlan, config):
raise TypeError()
# update interface description used e.g. within SNMP
- vlan.ifalias = config['description']
+ vlan.set_alias(config['description'])
# ignore link state changes
- vlan.link_detect = config['disable_link_detect']
+ vlan.set_link_detect(config['disable_link_detect'])
# Maximum Transmission Unit (MTU)
- vlan.mtu = config['mtu']
+ vlan.set_mtu(config['mtu'])
# Change VLAN interface MAC address
if config['mac']:
- vlan.mac = config['mac']
+ vlan.set_mac(config['mac'])
# enable/disable VLAN interface
if config['disable']:
- vlan.state = 'down'
+ vlan.set_state('down')
else:
- vlan.state = 'up'
+ vlan.set_state('up')
# Configure interface address(es)
# - not longer required addresses get removed first
@@ -339,7 +337,7 @@ def apply(bond):
else:
# Some parameters can not be changed when the bond is up.
# Always disable the bond prior changing anything
- b.state = 'down'
+ b.set_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!
@@ -347,11 +345,8 @@ def apply(bond):
b.del_port(intf)
# ARP link monitoring frequency, reset miimon when arp-montior is inactive
- if bond['arp_mon_intvl'] == 0:
- # reset miimon to default
- b.miimon = 250
- else:
- b.arp_interval = bond['arp_mon_intvl']
+ # 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
@@ -362,44 +357,44 @@ def apply(bond):
# 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.arp_ip_target.split()))
+ arp_tgt_addr = list(map(str, b.get_arp_ip_target().split()))
for addr in arp_tgt_addr:
- b.arp_ip_target = '-' + addr
+ b.set_arp_ip_target('-' + addr)
# Add configured ARP target addresses
for addr in bond['arp_mon_tgt']:
- b.arp_ip_target = '+' + addr
+ b.set_arp_ip_target('+' + addr)
# update interface description used e.g. within SNMP
- b.ifalias = bond['description']
+ b.set_alias(bond['description'])
#
# missing DHCP/DHCPv6 options go here
#
# ignore link state changes
- b.link_detect = bond['disable_link_detect']
+ b.set_link_detect(bond['disable_link_detect'])
# Bonding transmit hash policy
- b.xmit_hash_policy = bond['hash_policy']
+ b.set_hash_policy(bond['hash_policy'])
# configure ARP cache timeout in milliseconds
- b.arp_cache_tmp = bond['ip_arp_cache_tmo']
+ b.set_arp_cache_tmo(bond['ip_arp_cache_tmo'])
# Enable proxy-arp on this interface
- b.proxy_arp = bond['ip_proxy_arp']
+ b.set_proxy_arp(bond['ip_proxy_arp'])
# Enable private VLAN proxy ARP on this interface
- b.proxy_arp_pvlan = bond['ip_proxy_arp_pvlan']
+ b.set_proxy_arp_pvlan(bond['ip_proxy_arp_pvlan'])
# Change interface MAC address
if bond['mac']:
- b.mac = bond['mac']
+ b.set_mac(bond['mac'])
# Bonding policy
- b.mode = bond['mode']
+ b.set_mode(bond['mode'])
# Maximum Transmission Unit (MTU)
- b.mtu = bond['mtu']
+ b.set_mtu(bond['mtu'])
# Primary device interface
if bond['primary']:
- b.primary = bond['primary']
+ b.set_primary(bond['primary'])
# Add (enslave) interfaces to bond
for intf in bond['member']:
@@ -409,7 +404,7 @@ def apply(bond):
# parameters we will only re-enable the interface if it is not
# administratively disabled
if not bond['disable']:
- b.state = 'up'
+ b.set_state('up')
# Configure interface address(es)
# - not longer required addresses get removed first
diff --git a/src/conf_mode/interface-bridge.py b/src/conf_mode/interface-bridge.py
index 62589c798..37b5c4979 100755
--- a/src/conf_mode/interface-bridge.py
+++ b/src/conf_mode/interface-bridge.py
@@ -13,8 +13,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
import os
@@ -22,7 +20,7 @@ from copy import deepcopy
from sys import exit
from netifaces import interfaces
-from vyos.ifconfig import BridgeIf, Interface
+from vyos.ifconfig import BridgeIf, STPIf
from vyos.configdict import list_diff
from vyos.config import Config
from vyos import ConfigError
@@ -187,27 +185,27 @@ def apply(bridge):
br.remove()
else:
# enable interface
- br.state = 'up'
+ br.set_state('up')
# set ageing time
- br.ageing_time = bridge['aging']
+ br.set_ageing_time(bridge['aging'])
# set bridge forward delay
- br.forward_delay = bridge['forwarding_delay']
+ br.set_forward_delay(bridge['forwarding_delay'])
# set hello time
- br.hello_time = bridge['hello_time']
+ br.set_hello_time(bridge['hello_time'])
# set max message age
- br.max_age = bridge['max_age']
+ br.set_max_age(bridge['max_age'])
# set bridge priority
- br.priority = bridge['priority']
+ br.set_priority(bridge['priority'])
# turn stp on/off
- br.stp_state = bridge['stp']
+ br.set_stp(bridge['stp'])
# enable or disable IGMP querier
- br.multicast_querier = bridge['igmp_querier']
+ br.set_multicast_querier(bridge['igmp_querier'])
# update interface description used e.g. within SNMP
- br.ifalias = bridge['description']
+ br.set_alias(bridge['description'])
# Change interface MAC address
if bridge['mac']:
- br.mac = bridge['mac']
+ br.set_mac(bridge['mac'])
# remove interface from bridge
for intf in bridge['member_remove']:
@@ -219,7 +217,7 @@ def apply(bridge):
# up/down interface
if bridge['disable']:
- br.state = 'down'
+ br.set_state('down')
# Configure interface address(es)
# - not longer required addresses get removed first
@@ -231,16 +229,15 @@ def apply(bridge):
# configure additional bridge member options
for member in bridge['member']:
- # set bridge port cost
- br.set_cost(member['name'], member['cost'])
- # set bridge port priority
- br.set_priority(member['name'], member['priority'])
-
- i = Interface(member['name'])
+ i = STPIf(member['name'])
# configure ARP cache timeout
- i.arp_cache_tmo = bridge['arp_cache_tmo']
+ i.set_arp_cache_tmo(bridge['arp_cache_tmo'])
# ignore link state changes
- i.link_detect = bridge['disable_link_detect']
+ i.set_link_detect(bridge['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'])
return None
diff --git a/src/conf_mode/interface-dummy.py b/src/conf_mode/interface-dummy.py
index 614fe08db..eb0145f65 100755
--- a/src/conf_mode/interface-dummy.py
+++ b/src/conf_mode/interface-dummy.py
@@ -13,10 +13,9 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
-from os import environ
+import os
+
from copy import deepcopy
from sys import exit
@@ -40,7 +39,7 @@ def get_config():
# determine tagNode instance
try:
- dummy['intf'] = environ['VYOS_TAGNODE_VALUE']
+ dummy['intf'] = os.environ['VYOS_TAGNODE_VALUE']
except KeyError as E:
print("Interface not specified")
@@ -79,28 +78,28 @@ def generate(dummy):
return None
def apply(dummy):
- du = DummyIf(dummy['intf'])
+ d = DummyIf(dummy['intf'])
# Remove dummy interface
if dummy['deleted']:
- du.remove()
+ d.remove()
else:
- # enable interface
- du.state = 'up'
# update interface description used e.g. within SNMP
- du.ifalias = dummy['description']
+ d.set_alias(dummy['description'])
# Configure interface address(es)
# - not longer required addresses get removed first
# - newly addresses will be added second
for addr in dummy['address_remove']:
- du.del_addr(addr)
+ d.del_addr(addr)
for addr in dummy['address']:
- du.add_addr(addr)
+ d.add_addr(addr)
# disable interface on demand
if dummy['disable']:
- du.state = 'down'
+ d.set_state('down')
+ else:
+ d.set_state('up')
return None
diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py
index 5d597fd0a..99450b19e 100755
--- a/src/conf_mode/interface-ethernet.py
+++ b/src/conf_mode/interface-ethernet.py
@@ -67,20 +67,20 @@ def apply_vlan_config(vlan, config):
raise TypeError()
# update interface description used e.g. within SNMP
- vlan.ifalias = config['description']
+ vlan.set_alias(config['description'])
# ignore link state changes
- vlan.link_detect = config['disable_link_detect']
+ vlan.set_link_detect(config['disable_link_detect'])
# Maximum Transmission Unit (MTU)
- vlan.mtu = config['mtu']
+ vlan.set_mtu(config['mtu'])
# Change VLAN interface MAC address
if config['mac']:
- vlan.mac = config['mac']
+ vlan.set_mac(config['mac'])
# enable/disable VLAN interface
if config['disable']:
- vlan.state = 'down'
+ vlan.set_state('down')
else:
- vlan.state = 'up'
+ vlan.set_state('up')
# Configure interface address(es)
# - not longer required addresses get removed first
@@ -271,32 +271,32 @@ def apply(eth):
e.remove()
else:
# update interface description used e.g. within SNMP
- e.ifalias = eth['description']
+ e.set_alias(eth['description'])
#
# missing DHCP/DHCPv6 options go here
#
# ignore link state changes
- e.link_detect = eth['disable_link_detect']
+ 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.arp_cache_tmo = eth['ip_arp_cache_tmo']
+ e.set_arp_cache_tmo(eth['ip_arp_cache_tmo'])
# Enable proxy-arp on this interface
- e.proxy_arp = eth['ip_proxy_arp']
+ e.set_proxy_arp(eth['ip_proxy_arp'])
# Enable private VLAN proxy ARP on this interface
- e.proxy_arp_pvlan = eth['ip_proxy_arp_pvlan']
+ e.set_proxy_arp_pvlan(eth['ip_proxy_arp_pvlan'])
# Change interface MAC address - re-set to real hardware address (hw-id)
# if custom mac is removed
if eth['mac']:
- e.mac = eth['mac']
+ e.set_mac(eth['mac'])
else:
- e.mac = eth['hw_id']
+ e.set_mac(eth['hw_id'])
# Maximum Transmission Unit (MTU)
- e.mtu = eth['mtu']
+ e.set_mtu(eth['mtu'])
# GRO (generic receive offload)
e.set_gro(eth['offload_gro'])
@@ -318,9 +318,9 @@ def apply(eth):
# Enable/Disable interface
if eth['disable']:
- e.state = 'down'
+ e.set_state('down')
else:
- e.state = 'up'
+ e.set_state('up')
# Configure interface address(es)
# - not longer required addresses get removed first
diff --git a/src/conf_mode/interface-loopback.py b/src/conf_mode/interface-loopback.py
index a1a807868..10722d137 100755
--- a/src/conf_mode/interface-loopback.py
+++ b/src/conf_mode/interface-loopback.py
@@ -13,9 +13,9 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-from os import environ
+import os
+
from sys import exit
from copy import deepcopy
@@ -38,7 +38,7 @@ def get_config():
# determine tagNode instance
try:
- loopback['intf'] = environ['VYOS_TAGNODE_VALUE']
+ loopback['intf'] = os.environ['VYOS_TAGNODE_VALUE']
except KeyError as E:
print("Interface not specified")
@@ -72,21 +72,20 @@ def generate(loopback):
return None
def apply(loopback):
- lo = LoopbackIf(loopback['intf'])
- if not loopback['deleted']:
+ l = LoopbackIf(loopback['intf'])
+ if loopback['deleted']:
+ l.remove()
+ else:
# update interface description used e.g. within SNMP
- # update interface description used e.g. within SNMP
- lo.ifalias = loopback['description']
+ l.set_alias(loopback['description'])
# Configure interface address(es)
# - not longer required addresses get removed first
# - newly addresses will be added second
+ for addr in loopback['address_remove']:
+ l.del_addr(addr)
for addr in loopback['address']:
- lo.add_addr(addr)
-
- # remove interface address(es)
- for addr in loopback['address_remove']:
- lo.del_addr(addr)
+ l.add_addr(addr)
return None
diff --git a/src/conf_mode/interface-openvpn.py b/src/conf_mode/interface-openvpn.py
index 35e7928c2..a988e1ab1 100755
--- a/src/conf_mode/interface-openvpn.py
+++ b/src/conf_mode/interface-openvpn.py
@@ -13,8 +13,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
import os
import re
@@ -31,8 +29,9 @@ from pwd import getpwnam
from subprocess import Popen, PIPE
from time import sleep
-from vyos.config import Config
from vyos import ConfigError
+from vyos.config import Config
+from vyos.ifconfig import Interface
from vyos.validate import is_addr_assigned
user = 'openvpn'
@@ -226,6 +225,20 @@ auth-retry nointeract
client-config-dir /opt/vyatta/etc/openvpn/ccd/{{ intf }}
{% endif %}
+# DEPRECATED This option will be removed in OpenVPN 2.5
+# Until OpenVPN v2.3 the format of the X.509 Subject fields was formatted like this:
+# /C=US/L=Somewhere/CN=John Doe/emailAddress=john@example.com In addition the old
+# behaviour was to remap any character other than alphanumeric, underscore ('_'),
+# dash ('-'), dot ('.'), and slash ('/') to underscore ('_'). The X.509 Subject
+# string as returned by the tls_id environmental variable, could additionally
+# contain colon (':') or equal ('='). When using the --compat-names option, this
+# old formatting and remapping will be re-enabled again. This is purely implemented
+# for compatibility reasons when using older plug-ins or scripts which does not
+# handle the new formatting or UTF-8 characters.
+#
+# See https://phabricator.vyos.net/T1512
+compat-names
+
{% for option in options -%}
{{ option }}
{% endfor -%}
@@ -580,7 +593,7 @@ def get_config():
# Minimum required TLS version
if conf.exists('tls tls-version-min'):
openvpn['tls_version_min'] = conf.return_value('tls tls-version-min')
-
+
if conf.exists('shared-secret-key-file'):
openvpn['shared_secret_file'] = conf.return_value('shared-secret-key-file')
@@ -736,7 +749,7 @@ def verify(openvpn):
if openvpn['tls_auth']:
if not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', openvpn['tls_auth']):
raise ConfigError('Specified auth-file "{}" is invalid'.format(openvpn['tls_auth']))
-
+
if openvpn['tls_cert']:
if not checkCertHeader('-----BEGIN CERTIFICATE-----', openvpn['tls_cert']):
raise ConfigError('Specified cert-file "{}" is invalid'.format(openvpn['tls_cert']))
@@ -901,6 +914,29 @@ def apply(openvpn):
# execute assembled command
subprocess_cmd(cmd)
+
+ # better late then sorry ... but we can only set interface alias after
+ # OpenVPN has been launched and created the interface
+ cnt = 0
+ while openvpn['intf'] not in interfaces():
+ # If VPN tunnel can't be established because the peer/server isn't
+ # (temporarily) available, the vtun interface never becomes registered
+ # with the kernel, and the commit would hang if there is no bail out
+ # condition
+ cnt += 1
+ if cnt == 50:
+ break
+
+ # sleep 250ms
+ sleep(0.250)
+
+ try:
+ # we need to catch the exception if the interface is not up due to
+ # reason stated above
+ Interface(openvpn['intf']).set_alias(openvpn['description'])
+ except:
+ pass
+
return None
diff --git a/src/conf_mode/interface-vxlan.py b/src/conf_mode/interface-vxlan.py
index e97b4bf99..1097ae4d0 100755
--- a/src/conf_mode/interface-vxlan.py
+++ b/src/conf_mode/interface-vxlan.py
@@ -13,9 +13,9 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-from os import environ
+import os
+
from sys import exit
from copy import deepcopy
@@ -48,7 +48,7 @@ def get_config():
# determine tagNode instance
try:
- vxlan['intf'] = environ['VYOS_TAGNODE_VALUE']
+ vxlan['intf'] = os.environ['VYOS_TAGNODE_VALUE']
except KeyError as E:
print("Interface not specified")
@@ -127,7 +127,7 @@ def verify(vxlan):
if vxlan['link']:
# 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['link']).mtu)
+ underlay_mtu = int(Interface(vxlan['link']).get_mtu())
if underlay_mtu < (vxlan['mtu'] + 50):
raise ConfigError('VXLAN has a 50 byte overhead, underlaying device ' \
'MTU is to small ({})'.format(underlay_mtu))
@@ -163,14 +163,14 @@ def apply(vxlan):
# Finally create the new interface
v = VXLANIf(vxlan['intf'], config=conf)
# update interface description used e.g. by SNMP
- v.ifalias = vxlan['description']
+ v.set_alias(vxlan['description'])
# Maximum Transfer Unit (MTU)
- v.mtu = vxlan['mtu']
+ v.set_mtu(vxlan['mtu'])
# configure ARP cache timeout in milliseconds
- v.arp_cache_tmp = vxlan['ip_arp_cache_tmo']
+ v.set_arp_cache_tmo(vxlan['ip_arp_cache_tmo'])
# Enable proxy-arp on this interface
- v.proxy_arp = vxlan['ip_proxy_arp']
+ v.set_proxy_arp(vxlan['ip_proxy_arp'])
# Configure interface address(es) - no need to implicitly delete the
# old addresses as they have already been removed by deleting the
@@ -182,7 +182,7 @@ def apply(vxlan):
# parameters we will only re-enable the interface if it is not
# administratively disabled
if not vxlan['disable']:
- v.state='up'
+ v.set_state('up')
return None
diff --git a/src/conf_mode/interface-wireguard.py b/src/conf_mode/interface-wireguard.py
index 4ae3251fe..0be8b7b0f 100755
--- a/src/conf_mode/interface-wireguard.py
+++ b/src/conf_mode/interface-wireguard.py
@@ -19,40 +19,35 @@
import sys
import os
import re
-import syslog as sl
import subprocess
+from copy import deepcopy
+from netifaces import interfaces
-from vyos.config import Config
from vyos import ConfigError
+from vyos.config import Config
+from vyos.configdict import list_diff
from vyos.ifconfig import WireGuardIf
-try:
- ifname = str(os.environ['VYOS_TAGNODE_VALUE'])
- intfc = WireGuardIf(ifname)
-except KeyError:
- print("Interface not specified")
- sys.exit(1)
-
kdir = r'/config/auth/wireguard'
+
def _check_kmod():
if not os.path.exists('/sys/module/wireguard'):
- sl.syslog(sl.LOG_NOTICE, "loading wirguard kmod")
if os.system('sudo modprobe wireguard') != 0:
- sl.syslog(sl.LOG_NOTICE, "modprobe wireguard failed")
raise ConfigError("modprobe wireguard failed")
def _migrate_default_keys():
if os.path.exists('{}/private.key'.format(kdir)) and not os.path.exists('{}/default/private.key'.format(kdir)):
- sl.syslog(sl.LOG_NOTICE, "migrate keypair to default")
old_umask = os.umask(0o027)
location = '{}/default'.format(kdir)
subprocess.call(['sudo mkdir -p ' + location], shell=True)
subprocess.call(['sudo chgrp vyattacfg ' + location], shell=True)
subprocess.call(['sudo chmod 750 ' + location], shell=True)
- os.rename('{}/private.key'.format(kdir),'{}/private.key'.format(location))
- os.rename('{}/public.key'.format(kdir),'{}/public.key'.format(location))
+ os.rename('{}/private.key'.format(kdir),
+ '{}/private.key'.format(location))
+ os.rename('{}/public.key'.format(kdir),
+ '{}/public.key'.format(location))
os.umask(old_umask)
@@ -61,47 +56,82 @@ def get_config():
if not c.exists('interfaces wireguard'):
return None
- config_data = {
- ifname: {
- 'addr': '',
- 'descr': ifname,
- 'lport': None,
- 'status': 'exists',
- 'state': 'enabled',
- 'fwmark': 0x00,
- 'mtu': 1420,
- 'peer': {},
- 'pk' : '{}/default/private.key'.format(kdir)
- }
+ dflt_cnf = {
+ 'intfc': '',
+ 'addr': [],
+ 'addr_remove': [],
+ 'descr': '',
+ 'lport': None,
+ 'delete': False,
+ 'state': 'up',
+ 'fwmark': 0x00,
+ 'mtu': 1420,
+ 'peer': {},
+ 'peer_remove': [],
+ 'pk': '{}/default/private.key'.format(kdir)
}
+ if os.getenv('VYOS_TAGNODE_VALUE'):
+ ifname = str(os.environ['VYOS_TAGNODE_VALUE'])
+ wg = deepcopy(dflt_cnf)
+ wg['intfc'] = ifname
+ wg['descr'] = ifname
+ else:
+ print("ERROR: VYOS_TAGNODE_VALUE undefined")
+ sys.exit(1)
+
c.set_level('interfaces wireguard')
- if not c.exists_effective(ifname):
- config_data[ifname]['status'] = 'create'
+ # interface removal state
if not c.exists(ifname) and c.exists_effective(ifname):
- config_data[ifname]['status'] = 'delete'
-
- if config_data[ifname]['status'] != 'delete':
- if c.exists(ifname + ' address'):
- config_data[ifname]['addr'] = c.return_values(ifname + ' address')
- if c.exists(ifname + ' disable'):
- config_data[ifname]['state'] = 'disable'
- if c.exists(ifname + ' port'):
- config_data[ifname]['lport'] = c.return_value(ifname + ' port')
- if c.exists(ifname + ' fwmark'):
- config_data[ifname]['fwmark'] = c.return_value(ifname + ' fwmark')
- if c.exists(ifname + ' description'):
- config_data[ifname]['descr'] = c.return_value(
- ifname + ' description')
- if c.exists(ifname + ' mtu'):
- config_data[ifname]['mtu'] = c.return_value(ifname + ' mtu')
- if c.exists(ifname + ' private-key'):
- config_data[ifname]['pk'] = "{0}/{1}/private.key".format(kdir,c.return_value(ifname + ' private-key'))
- if c.exists(ifname + ' peer'):
- for p in c.list_nodes(ifname + ' peer'):
- if not c.exists(ifname + ' peer ' + p + ' disable'):
- config_data[ifname]['peer'].update(
+ wg['delete'] = True
+
+ if not wg['delete']:
+ c.set_level('interfaces wireguard {}'.format(ifname))
+ if c.exists('address'):
+ wg['addr'] = c.return_values('address')
+
+ # determine addresses which need to be removed
+ eff_addr = c.return_effective_values('address')
+ wg['addr_remove'] = list_diff(eff_addr, wg['addr'])
+
+ # ifalias description
+ if c.exists('description'):
+ wg['descr'] = c.return_value('description')
+
+ # link state
+ if c.exists('disable'):
+ wg['state'] = 'down'
+
+ # local port to listen on
+ if c.exists('port'):
+ wg['lport'] = c.return_value('port')
+
+ # fwmark value
+ if c.exists('fwmark'):
+ wg['fwmark'] = c.return_value('fwmark')
+
+ # mtu
+ if c.exists('mtu'):
+ wg['mtu'] = c.return_value('mtu')
+
+ # private key
+ if c.exists('private-key'):
+ wg['pk'] = "{0}/{1}/private.key".format(
+ kdir, c.return_value('private-key'))
+
+ # peer removal, wg identifies peers by its pubkey
+ peer_eff = c.list_effective_nodes('peer')
+ peer_rem = list_diff(peer_eff, c.list_nodes('peer'))
+ for p in peer_rem:
+ wg['peer_remove'].append(
+ c.return_effective_value('peer {} pubkey'.format(p)))
+
+ # peer settings
+ if c.exists('peer'):
+ for p in c.list_nodes('peer'):
+ if not c.exists('peer ' + p + ' disable'):
+ wg['peer'].update(
{
p: {
'allowed-ips': [],
@@ -110,46 +140,61 @@ def get_config():
}
}
)
- if c.exists(ifname + ' peer ' + p + ' pubkey'):
- config_data[ifname]['peer'][p]['pubkey'] = c.return_value(
- ifname + ' peer ' + p + ' pubkey')
- if c.exists(ifname + ' peer ' + p + ' allowed-ips'):
- config_data[ifname]['peer'][p]['allowed-ips'] = c.return_values(
- ifname + ' peer ' + p + ' allowed-ips')
- if c.exists(ifname + ' peer ' + p + ' endpoint'):
- config_data[ifname]['peer'][p]['endpoint'] = c.return_value(
- ifname + ' peer ' + p + ' endpoint')
- if c.exists(ifname + ' peer ' + p + ' persistent-keepalive'):
- config_data[ifname]['peer'][p]['persistent-keepalive'] = c.return_value(
- ifname + ' peer ' + p + ' persistent-keepalive')
- if c.exists(ifname + ' peer ' + p + ' preshared-key'):
- config_data[ifname]['peer'][p]['psk'] = c.return_value(
- ifname + ' peer ' + p + ' preshared-key')
-
- return config_data
+ # peer allowed-ips
+ if c.exists('peer ' + p + ' allowed-ips'):
+ wg['peer'][p]['allowed-ips'] = c.return_values(
+ 'peer ' + p + ' allowed-ips')
+ # peer endpoint
+ if c.exists('peer ' + p + ' endpoint'):
+ wg['peer'][p]['endpoint'] = c.return_value(
+ 'peer ' + p + ' endpoint')
+ # persistent-keepalive
+ if c.exists('peer ' + p + ' persistent-keepalive'):
+ wg['peer'][p]['persistent-keepalive'] = c.return_value(
+ 'peer ' + p + ' persistent-keepalive')
+ # preshared-key
+ if c.exists('peer ' + p + ' preshared-key'):
+ wg['peer'][p]['psk'] = c.return_value(
+ 'peer ' + p + ' preshared-key')
+ # peer pubkeys
+ key_eff = c.return_effective_value(
+ 'peer {peer} pubkey'.format(peer=p))
+ key_cfg = c.return_value(
+ 'peer {peer} pubkey'.format(peer=p))
+ wg['peer'][p]['pubkey'] = key_cfg
+
+ # 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)
+
+ return wg
+
def verify(c):
if not c:
return None
- if not os.path.exists(c[ifname]['pk']):
+ if not os.path.exists(c['pk']):
raise ConfigError(
"No keys found, generate them by executing: \'run generate wireguard [keypair|named-keypairs]\'")
- if c[ifname]['status'] != 'delete':
- if not c[ifname]['addr']:
+ if not c['delete']:
+ if not c['addr']:
raise ConfigError("ERROR: IP address required")
- if not c[ifname]['peer']:
+ if not c['peer']:
raise ConfigError("ERROR: peer required")
- for p in c[ifname]['peer']:
- if not c[ifname]['peer'][p]['allowed-ips']:
+ for p in c['peer']:
+ if not c['peer'][p]['allowed-ips']:
raise ConfigError("ERROR: allowed-ips required for peer " + p)
- if not c[ifname]['peer'][p]['pubkey']:
+ if not c['peer'][p]['pubkey']:
raise ConfigError("peer pubkey required for peer " + p)
def apply(c):
- # no wg config left, delete all wireguard devices, if any
+ # no wg configs left, remove all interface from system
+ # maybe move it into ifconfig.py
if not c:
net_devs = os.listdir('/sys/class/net/')
for dev in net_devs:
@@ -158,120 +203,74 @@ def apply(c):
if re.search("DEVTYPE=wireguard", buf, re.I | re.M):
wg_intf = re.sub("INTERFACE=", "", re.search(
"INTERFACE=.*", buf, re.I | re.M).group(0))
- sl.syslog(sl.LOG_NOTICE, "removing interface " + wg_intf)
subprocess.call(
['ip l d dev ' + wg_intf + ' >/dev/null'], shell=True)
return None
- # interface removal
- if c[ifname]['status'] == 'delete':
- sl.syslog(sl.LOG_NOTICE, "removing interface " + ifname)
+ # init wg class
+ intfc = WireGuardIf(c['intfc'])
+
+ # single interface removal
+ if c['delete']:
intfc.remove()
return None
- c_eff = Config()
- c_eff.set_level('interfaces wireguard')
+ # remove IP addresses
+ for ip in c['addr_remove']:
+ intfc.del_addr(ip)
- # interface state
- if c[ifname]['state'] == 'disable':
- sl.syslog(sl.LOG_NOTICE, "disable interface " + ifname)
- intfc.state = 'down'
- else:
- if not intfc.state == 'up':
- sl.syslog(sl.LOG_NOTICE, "enable interface " + ifname)
- intfc.state = 'up'
-
- # IP address
- if not c_eff.exists_effective(ifname + ' address'):
- for ip in c[ifname]['addr']:
- intfc.add_addr(ip)
- else:
- addr_eff = c_eff.return_effective_values(ifname + ' address')
- addr_rem = list(set(addr_eff) - set(c[ifname]['addr']))
- addr_add = list(set(c[ifname]['addr']) - set(addr_eff))
-
- if len(addr_rem) != 0:
- for ip in addr_rem:
- sl.syslog(
- sl.LOG_NOTICE, "remove IP address {0} from {1}".format(ip, ifname))
- intfc.del_addr(ip)
-
- if len(addr_add) != 0:
- for ip in addr_add:
- sl.syslog(
- sl.LOG_NOTICE, "add IP address {0} to {1}".format(ip, ifname))
- intfc.add_addr(ip)
-
- # interface MTU
- if c[ifname]['mtu'] != 1420:
- intfc.mtu = int(c[ifname]['mtu'])
- else:
- # default is set to 1420 in config_data
- intfc.mtu = int(c[ifname]['mtu'])
-
- # ifalias for snmp from description
- descr_eff = c_eff.return_effective_value(ifname + ' description')
- if descr_eff != c[ifname]['descr']:
- intfc.ifalias = str(c[ifname]['descr'])
+ # add IP addresses
+ for ip in c['addr']:
+ intfc.add_addr(ip)
- # peer deletion
- peer_eff = c_eff.list_effective_nodes(ifname + ' peer')
- peer_cnf = []
-
- try:
- for p in c[ifname]['peer']:
- peer_cnf.append(p)
- except KeyError:
- pass
-
- peer_rem = list(set(peer_eff) - set(peer_cnf))
- for p in peer_rem:
- pkey = c_eff.return_effective_value(ifname + ' peer ' + p + ' pubkey')
- intfc.remove_peer(pkey)
-
- # peer key update
- for p in peer_eff:
- if p in peer_cnf:
- ekey = c_eff.return_effective_value(
- ifname + ' peer ' + p + ' pubkey')
- nkey = c[ifname]['peer'][p]['pubkey']
- if nkey != ekey:
- sl.syslog(
- sl.LOG_NOTICE, "peer {0} pubkey changed from {1} to {2} on interface {3}".format(p, ekey, nkey, ifname))
- intfc.remove_peer(ekey)
-
- intfc.config['private-key'] = c[ifname]['pk']
- for p in c[ifname]['peer']:
- intfc.config['pubkey'] = str(c[ifname]['peer'][p]['pubkey'])
- intfc.config['allowed-ips'] = (c[ifname]['peer'][p]['allowed-ips'])
-
- # listen-port
- if c[ifname]['lport']:
- intfc.config['port'] = c[ifname]['lport']
+ # interface mtu
+ intfc.mtu = int(c['mtu'])
+ # ifalias for snmp from description
+ intfc.ifalias = str(c['descr'])
+
+ # remove peers
+ if c['peer_remove']:
+ for pkey in c['peer_remove']:
+ intfc.remove_peer(pkey)
+
+ # peer pubkey
+ # setting up the wg interface
+ intfc.config['private-key'] = c['pk']
+ for p in c['peer']:
+ # peer pubkey
+ intfc.config['pubkey'] = str(c['peer'][p]['pubkey'])
+ # peer allowed-ips
+ intfc.config['allowed-ips'] = c['peer'][p]['allowed-ips']
+ # local listen port
+ if c['lport']:
+ intfc.config['port'] = c['lport']
# fwmark
- if c[ifname]['fwmark']:
- intfc.config['fwmark'] = c[ifname]['fwmark']
-
+ if c['fwmark']:
+ intfc.config['fwmark'] = c['fwmark']
# endpoint
- if c[ifname]['peer'][p]['endpoint']:
- intfc.config['endpoint'] = c[ifname]['peer'][p]['endpoint']
+ if c['peer'][p]['endpoint']:
+ intfc.config['endpoint'] = c['peer'][p]['endpoint']
# persistent-keepalive
- if 'persistent-keepalive' in c[ifname]['peer'][p]:
- intfc.config['keepalive'] = c[ifname][
- 'peer'][p]['persistent-keepalive']
+ if 'persistent-keepalive' in c['peer'][p]:
+ intfc.config['keepalive'] = c['peer'][p]['persistent-keepalive']
+ # maybe move it into ifconfig.py
# preshared-key - needs to be read from a file
- if 'psk' in c[ifname]['peer'][p]:
+ if 'psk' in c['peer'][p]:
psk_file = '/config/auth/wireguard/psk'
old_umask = os.umask(0o077)
- open(psk_file, 'w').write(str(c[ifname]['peer'][p]['psk']))
+ open(psk_file, 'w').write(str(c['peer'][p]['psk']))
os.umask(old_umask)
intfc.config['psk'] = psk_file
-
intfc.update()
+ # interface state
+ intfc.state = c['state']
+
+ return None
+
if __name__ == '__main__':
try:
_check_kmod()
diff --git a/src/conf_mode/ipoe_server.py b/src/conf_mode/ipoe_server.py
index a60379760..1662e45e6 100755
--- a/src/conf_mode/ipoe_server.py
+++ b/src/conf_mode/ipoe_server.py
@@ -369,6 +369,9 @@ def verify(c):
if c == None or not c:
return None
+ if not c['interfaces']:
+ raise ConfigError("service ipoe-server interface requires a value")
+
for intfc in c['interfaces']:
if not c['interfaces'][intfc]['range']:
raise ConfigError("service ipoe-server interface " + intfc + " client-subnet needs a value")
diff --git a/src/conf_mode/ntp.py b/src/conf_mode/ntp.py
index f706d502f..8f32e6e81 100755
--- a/src/conf_mode/ntp.py
+++ b/src/conf_mode/ntp.py
@@ -42,6 +42,8 @@ restrict default noquery nopeer notrap nomodify
restrict 127.0.0.1
restrict -6 ::1
+# Do not listen on any interface address by default
+interface ignore wildcard
#
# Configurable section
#
@@ -63,7 +65,6 @@ restrict {{ n.address }} mask {{ n.netmask }} nomodify notrap nopeer
{% if listen_address -%}
# NTP should listen on configured addresses only
-interface ignore wildcard
{% for a in listen_address -%}
interface listen {{ a }}
{% endfor -%}
diff --git a/src/services/vyos-hostsd b/src/services/vyos-hostsd
index e7ecd8573..5c2ea71c8 100755
--- a/src/services/vyos-hostsd
+++ b/src/services/vyos-hostsd
@@ -166,9 +166,9 @@ def delete_name_servers(data, tag):
def set_host_name(state, data):
if data['host_name']:
state['host_name'] = data['host_name']
- if data['domain_name']:
+ if 'domain_name' in data:
state['domain_name'] = data['domain_name']
- if data['search_domains']:
+ if 'search_domains' in data:
state['search_domains'] = data['search_domains']
def get_name_servers(state, tag):
diff --git a/src/tests/test_ntp.py b/src/tests/test_ntp.py
deleted file mode 100644
index 1cde490b4..000000000
--- a/src/tests/test_ntp.py
+++ /dev/null
@@ -1,259 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2018 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
-
-import os
-import tempfile
-import unittest
-from unittest import TestCase, mock
-import ipaddress
-from contextlib import ExitStack
-import textwrap
-
-from vyos import ConfigError
-from vyos.config import Config
-try:
- from src.conf_mode import ntp
-except ModuleNotFoundError: # for unittest.main()
- import sys
- sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
- from src.conf_mode import ntp
-
-
-class TestNtp(TestCase):
-
- def test_get_config(self):
- tests = [
- {
- 'name': 'empty',
- 'config': {
- 'system ntp': None,
- },
- 'expected': None,
- },
- {
- 'name': 'full-options',
- 'config': {
- 'system ntp': 'yes',
- 'allow-clients address': ['192.0.2.0/24'],
- 'listen-address': ['198.51.100.0/24'],
- 'server': ['example.com'],
- 'server example.com noselect': 'yes',
- 'server example.com preempt': 'yes',
- 'server example.com prefer': 'yes',
- },
- 'expected': {
- 'allowed_networks': [{
- 'address': ipaddress.ip_address('192.0.2.0'),
- 'netmask': ipaddress.ip_address('255.255.255.0'),
- 'network': '192.0.2.0/24',
- }],
- 'listen_address': ['198.51.100.0/24'],
- 'servers': [
- {'name': 'example.com', 'options': ['noselect', 'preempt', 'prefer']}
- ]
- },
- },
- {
- 'name': 'non-options',
- 'config': {
- 'system ntp': 'yes',
- 'allow-clients address': ['192.0.2.0/24'],
- 'listen-address': ['198.51.100.0/24'],
- 'server': ['example.com'],
- },
- 'expected': {
- 'allowed_networks': [{
- 'address': ipaddress.ip_address('192.0.2.0'),
- 'netmask': ipaddress.ip_address('255.255.255.0'),
- 'network': '192.0.2.0/24',
- }],
- 'listen_address': ['198.51.100.0/24'],
- 'servers': [
- {'name': 'example.com', 'options': []}
- ]
- },
- },
- ]
- for case in tests:
- def mocked_fn(path):
- return case['config'].get(path)
-
- with self.subTest(msg = case['name']):
- m = {
- 'return_value': mock.Mock(side_effect = mocked_fn),
- 'return_values': mock.Mock(side_effect = mocked_fn),
- 'list_nodes': mock.Mock(side_effect = mocked_fn),
- 'exists': mock.Mock(side_effect = mocked_fn),
- }
- with mock.patch.multiple(Config, **m):
- actual = ntp.get_config()
- self.assertEqual(actual, case['expected'])
-
- def test_verify(self):
- tests = [
- {
- 'name': 'none',
- 'config': None,
- 'expected': None
- },
- {
- 'name': 'valid',
- 'config': {
- 'allowed_networks': [{
- 'address': ipaddress.ip_address('192.0.2.1'),
- 'netmask': ipaddress.ip_address('255.255.255.0'),
- 'network': '192.0.2.0/24',
- }],
- 'listen_address': ['198.51.100.0/24'],
- 'servers': [
- {'name': 'example.com', 'options': ['noselect', 'preempt', 'prefer']}
- ]
- },
- 'expected': None,
- },
- {
- 'name': 'not configure servers',
- 'config': {
- 'allowed_networks': [{
- 'address': ipaddress.ip_address('192.0.2.1'),
- 'netmask': ipaddress.ip_address('255.255.255.0'),
- 'network': '192.0.2.0/24',
- }],
- 'servers': []
- },
- 'expected': ConfigError,
- },
- {
- 'name': 'does not exist in the network',
- 'config': {
- 'allowed_networks': [{
- 'address': ipaddress.ip_address('192.0.2.1'),
- 'netmask': ipaddress.ip_address('255.255.255.0'),
- 'network': '192.0.2.0/50', # invalid netmask
- }],
- 'listen_address': ['198.51.100.0/24'],
- 'servers': [
- {'name': 'example.com', 'options': []}
- ]
- },
- 'expected': ConfigError,
- },
- ]
- for case in tests:
- with self.subTest(msg = case['name']):
- if case['expected'] is not None:
- with self.assertRaises(case['expected']):
- ntp.verify(case['config'])
- else:
- ntp.verify(case['config'])
-
- def test_generate(self):
- tests = [
- {
- 'name': 'empty',
- 'config': None,
- 'expected': '',
- },
- {
- 'name': 'valid',
- 'config': {
- 'allowed_networks': [
- {
- 'address': ipaddress.ip_address('192.0.2.1'),
- 'netmask': ipaddress.ip_address('255.255.255.0'),
- 'network': '192.0.2.0/24',
- },
- {
- 'address': ipaddress.ip_address('198.51.100.1'),
- 'netmask': ipaddress.ip_address('255.255.255.0'),
- 'network': '198.51.100.0/24',
- },
- ],
- 'listen_address': ['198.51.100.0/24'],
- 'servers': [
- {'name': '1.example.com', 'options': ['noselect', 'preempt', 'prefer']},
- {'name': '2.example.com', 'options': []},
- ]
- },
- 'expected': textwrap.dedent('''
- ### Autogenerated by ntp.py ###
-
- #
- # Non-configurable defaults
- #
- driftfile /var/lib/ntp/ntp.drift
- # By default, only allow ntpd to query time sources, ignore any incoming requests
- restrict default noquery nopeer notrap nomodify
- # Local users have unrestricted access, allowing reconfiguration via ntpdc
- restrict 127.0.0.1
- restrict -6 ::1
-
- #
- # Configurable section
- #
-
- # Server configuration for: 1.example.com
- server 1.example.com iburst noselect preempt prefer
- # Server configuration for: 2.example.com
- server 2.example.com iburst
-
-
- # Client configuration for network: 192.0.2.0/24
- restrict 192.0.2.1 mask 255.255.255.0 nomodify notrap nopeer
-
- # Client configuration for network: 198.51.100.0/24
- restrict 198.51.100.1 mask 255.255.255.0 nomodify notrap nopeer
-
-
-
- # NTP should listen on configured addresses only
- interface ignore wildcard
- interface listen 198.51.100.0/24
-
- '''),
- },
- ]
-
- for case in tests:
- with self.subTest(msg = case['name']):
- with tempfile.NamedTemporaryFile() as fp:
- ntp.config_file = fp.name
-
- ntp.generate(case['config'])
- actual = fp.file.read().decode('ascii')
- print(actual)
- self.assertEqual(case['expected'], actual)
-
- def test_apply(self):
- with tempfile.NamedTemporaryFile(delete = False) as fp:
- ntp.config_file = fp.name
- with mock.patch('os.system') as os_system:
- ntp.apply({}) # some configure
- os_system.assert_has_calls([
- mock.call('sudo systemctl restart ntp.service'),
- ])
- self.assertTrue(os.path.exists(fp.name))
-
- ntp.apply(None) # empty configure
- os_system.assert_has_calls([
- mock.call('sudo systemctl stop ntp.service'),
- ])
- self.assertFalse(os.path.exists(fp.name))
-
-if __name__ == "__main__":
- unittest.main()