summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/control1
-rw-r--r--interface-definitions/interfaces-bridge.xml6
-rw-r--r--python/vyos/configsession.py3
-rw-r--r--python/vyos/ifconfig.py1061
-rwxr-xr-xsrc/conf_mode/interface-bridge.py130
-rwxr-xr-xsrc/conf_mode/interface-dummy.py35
-rwxr-xr-xsrc/conf_mode/interface-loopback.py19
-rwxr-xr-xsrc/conf_mode/ipsec-settings.py2
-rwxr-xr-xsrc/helpers/vyos-boot-config-loader.py101
-rw-r--r--src/systemd/vyos-hostsd.service9
10 files changed, 996 insertions, 371 deletions
diff --git a/debian/control b/debian/control
index 7b75ca111..12eb7c309 100644
--- a/debian/control
+++ b/debian/control
@@ -29,7 +29,6 @@ Depends: python3,
python3-vici (>= 5.7.2),
python3-bottle,
python3-zmq,
- python3-pyroute2,
ipaddrcheck,
tcpdump,
tshark,
diff --git a/interface-definitions/interfaces-bridge.xml b/interface-definitions/interfaces-bridge.xml
index e92a55d63..98633382c 100644
--- a/interface-definitions/interfaces-bridge.xml
+++ b/interface-definitions/interfaces-bridge.xml
@@ -47,14 +47,14 @@
</leafNode>
<leafNode name="aging">
<properties>
- <help>Interval addresses are retained</help>
+ <help>MAC address aging interval</help>
<valueHelp>
<format>0</format>
- <description>Disable retaining address in bridge (always flood)</description>
+ <description>Disable MAC address learning (always flood)</description>
</valueHelp>
<valueHelp>
<format>10-1000000</format>
- <description>Address aging time for bridge seconds (default 300)</description>
+ <description>MAC address aging time in seconds (default: 300)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-0 --range 10-1000000"/>
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py
index 8626839f2..acbdd3d5f 100644
--- a/python/vyos/configsession.py
+++ b/python/vyos/configsession.py
@@ -24,6 +24,7 @@ COMMENT = '/opt/vyatta/sbin/my_comment'
COMMIT = '/opt/vyatta/sbin/my_commit'
DISCARD = '/opt/vyatta/sbin/my_discard'
SHOW_CONFIG = ['/bin/cli-shell-api', 'showConfig']
+LOAD_CONFIG = ['/bin/cli-shell-api', 'loadFile']
# Default "commit via" string
APP = "vyos-http-api"
@@ -155,3 +156,5 @@ class ConfigSession(object):
if format == 'raw':
return config_data
+ def load_config(self, file_path):
+ self.__run_command(LOAD_CONFIG + [file_path])
diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py
index 5f28125af..506004fa0 100644
--- a/python/vyos/ifconfig.py
+++ b/python/vyos/ifconfig.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-
# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
@@ -15,113 +13,164 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-import sys
import os
-import re
-import json
-import socket
import subprocess
-import ipaddress
+import jinja2
from vyos.validate import *
from ipaddress import IPv4Network, IPv6Address
from netifaces import ifaddresses, AF_INET, AF_INET6
-
-dhclient_conf_dir = r'/var/lib/dhcp/dhclient_'
+from time import sleep
+
+dhcp_cfg = """
+# generated by ifconfig.py
+option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
+interface "{{ intf }}" {
+ send host-name "{{ hostname }}";
+ request subnet-mask, broadcast-address, routers, domain-name-servers, rfc3442-classless-static-routes, domain-name, interface-mtu;
+}
+"""
+
+dhcpv6_cfg = """
+# generated by ifconfig.py
+interface "{{ intf }}" {
+ request routers, domain-name-servers, domain-name;
+}
+"""
+
+dhclient_base = r'/var/lib/dhcp/dhclient_'
class Interface:
- def __init__(self, ifname=None, type=None):
+ def __init__(self, ifname, type=None):
"""
- Create instance of an IP interface
+ This is the base interface class which supports basic IP/MAC address
+ operations as well as DHCP(v6). Other interface which represent e.g.
+ and ethernet bridge are implemented as derived classes adding all
+ additional functionality.
- Example:
+ DEBUG:
+ This class has embedded debugging (print) which can be enabled by
+ creating the following file:
+ vyos@vyos# touch /tmp/vyos.ifconfig.debug
+ Example:
>>> from vyos.ifconfig import Interface
- >>> i = Interface('br111', type='bridge')
+ >>> i = Interface('eth0')
"""
- if not ifname:
- raise Exception("interface name required")
+ if not os.path.exists('/sys/class/net/{}'.format(ifname)) and not type:
+ raise Exception('interface "{}" not found'.format(str(ifname)))
- if not os.path.exists('/sys/class/net/{0}'.format(ifname)) and not type:
- raise Exception("interface {0} not found".format(str(ifname)))
+ self._ifname = str(ifname)
+ self._debug = False
- if not os.path.exists('/sys/class/net/{0}'.format(ifname)):
- try:
- cmd = 'ip link add dev "{}" type "{}"'.format(ifname, type)
- self._cmd(cmd)
- except subprocess.CalledProcessError as e:
- if self._debug():
- self._debug(e)
- if "Operation not supported" in str(e.output.decode()):
- print(str(e.output.decode()))
- sys.exit(0)
+ if os.path.isfile('/tmp/vyos.ifconfig.debug'):
+ self._debug = True
+
+ if not os.path.exists('/sys/class/net/{}'.format(ifname)):
+ cmd = 'ip link add dev "{}" type "{}"'.format(ifname, type)
+ self._cmd(cmd)
+
+ # per interface DHCP config files
+ self._dhcp_cfg_file = dhclient_base + self._ifname + '.conf'
+ self._dhcp_pid_file = dhclient_base + self._ifname + '.pid'
+ self._dhcp_lease_file = dhclient_base + self._ifname + '.leases'
+
+ # per interface DHCPv6 config files
+ self._dhcpv6_cfg_file = dhclient_base + self._ifname + '.v6conf'
+ self._dhcpv6_pid_file = dhclient_base + self._ifname + '.v6pid'
+ self._dhcpv6_lease_file = dhclient_base + self._ifname + '.v6leases'
+
+
+ def _debug_msg(self, msg):
+ if self._debug:
+ print('"DEBUG/{}: {}'.format(self._ifname, msg))
- self._ifname = str(ifname)
- @property
def remove(self):
"""
Remove system interface
Example:
-
>>> from vyos.ifconfig import Interface
- >>> i = Interface('br111', type='bridge')
- >>> i.remove
+ >>> i = Interface('eth0')
+ >>> i.remove()
"""
+ # stop DHCP(v6) if running
+ self.del_dhcp()
+ self.del_dhcpv6()
+
# NOTE (Improvement):
# after interface removal no other commands should be allowed
# to be called and instead should raise an Exception:
-
cmd = 'ip link del dev "{}"'.format(self._ifname)
self._cmd(cmd)
def _cmd(self, command):
+ self._debug_msg(command)
+
process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=True)
proc_stdout = process.communicate()[0].strip()
+
+ # add exception handling code
pass
+ def _read_sysfs(self, filename):
+ """
+ Provide a single primitive w/ error checking for reading from sysfs.
+ """
+ var = None
+ with open(filename, 'r') as f:
+ var = f.read().rstrip('\n')
+
+ self._debug_msg('read "{}" <- "{}"'.format(value, filename))
+ return var
+
+
+ def _write_sysfs(self, filename, value):
+ """
+ Provide a single primitive w/ error checking for writing to sysfs.
+ """
+ self._debug_msg('write "{}" -> "{}"'.format(value, filename))
+ with open(filename, 'w') as f:
+ f.write(str(value))
+
+ return None
+
+
@property
def mtu(self):
"""
Get/set interface mtu in bytes.
Example:
-
>>> from vyos.ifconfig import Interface
- >>> Interface('eth1').mtu
+ >>> Interface('eth0').mtu
'1500'
"""
-
- mtu = 0
- with open('/sys/class/net/{0}/mtu'.format(self._ifname), 'r') as f:
- mtu = f.read().rstrip('\n')
- return mtu
+ return self._read_sysfs('/sys/class/net/{0}/mtu'
+ .format(self._ifname))
@mtu.setter
- def mtu(self, mtu=None):
+ def mtu(self, mtu):
"""
Get/set interface mtu in bytes.
Example:
-
>>> from vyos.ifconfig import Interface
- >>> Interface('br100', type='bridge').mtu = 1400
- >>> Interface('br100').mtu
+ >>> Interface('eth0').mtu = 1400
+ >>> Interface('eth0').mtu
'1400'
"""
-
if mtu < 68 or mtu > 9000:
raise ValueError('Invalid MTU size: "{}"'.format(mru))
- with open('/sys/class/net/{0}/mtu'.format(self._ifname), 'w') as f:
- f.write(str(mtu))
-
+ return self._write_sysfs('/sys/class/net/{0}/mtu'
+ .format(self._ifname), mtu)
@property
def mac(self):
@@ -129,27 +178,23 @@ class Interface:
Get/set interface mac address
Example:
-
>>> from vyos.ifconfig import Interface
- >>> Interface('eth1').mac
+ >>> Interface('eth0').mac
'00:0c:29:11:aa:cc'
"""
- address = ''
- with open('/sys/class/net/{0}/address'.format(self._ifname), 'r') as f:
- address = f.read().rstrip('\n')
- return address
+ return self._read_sysfs('/sys/class/net/{0}/address'
+ .format(self._ifname))
@mac.setter
- def mac(self, mac=None):
+ def mac(self, mac):
"""
Get/set interface mac address
Example:
-
>>> from vyos.ifconfig import Interface
- >>> Interface('eth1').mac = '00:90:43:fe:fe:1b'
- >>> Interface('eth1').mac
+ >>> Interface('eth0').mac = '00:90:43:fe:fe:1b'
+ >>> Interface('eth0').mac
'00:90:43:fe:fe:1b'
"""
# a mac address consits out of 6 octets
@@ -176,6 +221,76 @@ class Interface:
@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):
+ """
+ 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'
+ """
+ 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):
+ """
+ Konfigure 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.
+
+ 1 - Ignore packets received if interface associated with the incoming
+ address is down.
+
+ 2 - Ignore packets received if interface associated with the incoming
+ address is down or has no carrier.
+
+ Default value is 0. Note that some distributions enable it in startup
+ scripts.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').link_detect = '1'
+ """
+ if link_filter >= 0 and link_filter <= 2:
+ return self._write_sysfs('/proc/sys/net/ipv4/conf/{0}/link_filter'
+ .format(self._ifname), link_filter)
+ else:
+ raise ValueError()
+
+
+ @property
def ifalias(self):
"""
Get/set interface alias name
@@ -183,14 +298,11 @@ class Interface:
Example:
>>> from vyos.ifconfig import Interface
- >>> Interface('eth1').ifalias
+ >>> Interface('eth0').ifalias
''
"""
-
- alias = ''
- with open('/sys/class/net/{0}/ifalias'.format(self._ifname), 'r') as f:
- alias = f.read().rstrip('\n')
- return alias
+ return self._read_sysfs('/sys/class/net/{0}/ifalias'
+ .format(self._ifname))
@ifalias.setter
@@ -199,25 +311,24 @@ class Interface:
Get/set interface alias name
Example:
-
>>> from vyos.ifconfig import Interface
- >>> Interface('eth1').ifalias = 'VyOS upstream interface'
- >>> Interface('eth1').ifalias
+ >>> Interface('eth0').ifalias = 'VyOS upstream interface'
+ >>> Interface('eth0').ifalias
'VyOS upstream interface'
to clear interface alias e.g. delete it use:
- >>> Interface('eth1').ifalias = ''
- >>> Interface('eth1').ifalias
+ >>> Interface('eth0').ifalias = ''
+ >>> Interface('eth0').ifalias
''
"""
-
- # clear interface alias
if not ifalias:
+ # clear interface alias
ifalias = '\0'
- with open('/sys/class/net/{0}/ifalias'.format(self._ifname), 'w') as f:
- f.write(str(ifalias))
+ self._write_sysfs('/sys/class/net/{0}/ifalias'
+ .format(self._ifname), ifalias)
+
@property
def state(self):
@@ -225,31 +336,25 @@ class Interface:
Enable (up) / Disable (down) an interface
Example:
-
>>> from vyos.ifconfig import Interface
- >>> Interface('eth1').state
+ >>> Interface('eth0').state
'up'
"""
-
- state = ''
- with open('/sys/class/net/{0}/operstate'.format(self._ifname), 'r') as f:
- state = f.read().rstrip('\n')
- return state
+ return self._read_sysfs('/sys/class/net/{0}/operstate'
+ .format(self._ifname))
@state.setter
- def state(self, state=None):
+ def state(self, state):
"""
Enable (up) / Disable (down) an interface
Example:
-
>>> from vyos.ifconfig import Interface
- >>> Interface('eth1').state = 'down'
- >>> Interface('eth1').state
+ >>> Interface('eth0').state = 'down'
+ >>> Interface('eth0').state
'down'
"""
-
if state not in ['up', 'down']:
raise ValueError('state must be "up" or "down"')
@@ -258,17 +363,98 @@ class Interface:
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):
+ """
+ Set per interface proxy ARP configuration
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').proxy_arp = 1
+ >>> Interface('eth0').proxy_arp
+ '1'
+ """
+ if int(enable) >= 0 and int(enable) <= 1:
+ return self._write_sysfs('/proc/sys/net/ipv4/conf/{}/proxy_arp'
+ .format(self._ifname), enable)
+ else:
+ raise ValueError("Value out of range")
+
+
+ @property
+ def proxy_arp_pvlan(self):
+ """
+ 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
+ '0'
+ """
+ return self._read_sysfs('/proc/sys/net/ipv4/conf/{}/proxy_arp_pvlan'
+ .format(self._ifname))
- def _debug(self, e=None):
+ @proxy_arp_pvlan.setter
+ def proxy_arp_pvlan(self, enable):
"""
- export DEBUG=1 to see debug messages
+ 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'
"""
- if os.getenv('DEBUG') == '1':
- if e:
- print ("Exception raised:\ncommand: {0}\nerror code: {1}\nsubprocess output: {2}".format(
- e.cmd, e.returncode, e.output.decode()))
- return True
- return False
+ if int(enable) >= 0 and int(enable) <= 1:
+ return self._write_sysfs('/proc/sys/net/ipv4/conf/{}/proxy_arp_pvlan'
+ .format(self._ifname), enable)
+ else:
+ raise ValueError("Value out of range")
def get_addr(self):
@@ -277,9 +463,8 @@ class Interface:
This is done using the netifaces and ipaddress python modules.
Example:
-
>>> from vyos.ifconfig import Interface
- >>> Interface('eth1').get_addrs()
+ >>> Interface('eth0').get_addrs()
['172.16.33.30/24', 'fe80::20c:29ff:fe11:a174/64']
"""
@@ -307,41 +492,31 @@ class Interface:
return ipv4 + ipv6
- def add_addr(self, addr=None):
+ def add_addr(self, addr):
"""
Add IP address to interface. Address is only added if it yet not added
to that interface.
Example:
-
- >>> from vyos.interfaceconfig import Interface
- >>> j = Interface('br100', type='bridge')
+ >>> from vyos.ifconfig import Interface
+ >>> j = Interface('eth0')
>>> j.add_addr('192.0.2.1/24')
>>> j.add_addr('2001:db8::ffff/64')
>>> j.get_addr()
['192.0.2.1/24', '2001:db8::ffff/64']
"""
-
- if not addr:
- raise ValueError('No IP address specified')
-
if not is_intf_addr_assigned(self._ifname, addr):
- cmd = ''
- if is_ipv4(addr):
- cmd = 'sudo ip -4 addr add "{}" broadcast + dev "{}"'.format(addr, self._ifname)
- elif is_ipv6(addr):
- cmd = 'sudo ip -6 addr add "{}" dev "{}"'.format(addr, self._ifname)
-
+ cmd = 'sudo ip addr add "{}" dev "{}"'.format(addr, self._ifname)
self._cmd(cmd)
- def del_addr(self, addr=None):
+ def del_addr(self, addr):
"""
Remove IP address from interface.
Example:
- >>> from vyos.interfaceconfig import Interface
- >>> j = Interface('br100', type='bridge')
+ >>> from vyos.ifconfig import Interface
+ >>> j = Interface('eth0')
>>> j.add_addr('2001:db8::ffff/64')
>>> j.add_addr('192.0.2.1/24')
>>> j.get_addr()
@@ -350,142 +525,560 @@ class Interface:
>>> j.get_addr()
['2001:db8::ffff/64']
"""
-
- if not addr:
- raise ValueError('No IP address specified')
-
if is_intf_addr_assigned(self._ifname, addr):
- cmd = ''
- if is_ipv4(addr):
- cmd = 'ip -4 addr del "{}" dev "{}"'.format(addr, self._ifname)
- elif is_ipv6(addr):
- cmd = 'ip -6 addr del "{}" dev "{}"'.format(addr, self._ifname)
-
+ cmd = 'ip addr del "{}" dev "{}"'.format(addr, self._ifname)
self._cmd(cmd)
# replace dhcpv4/v6 with systemd.networkd?
- def set_dhcpv4(self):
- conf_file = dhclient_conf_dir + self._ifname + '.conf'
- pidfile = dhclient_conf_dir + self._ifname + '.pid'
- leasefile = dhclient_conf_dir + self._ifname + '.leases'
-
- a = [
- '# generated by interface_config.py',
- 'option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;',
- 'interface \"' + self._ifname + '\" {',
- '\tsend host-name \"' + socket.gethostname() + '\";',
- '\trequest subnet-mask, broadcast-address, routers, domain-name-servers, rfc3442-classless-static-routes, domain-name, interface-mtu;',
- '}'
- ]
-
- cnf = ""
- for ln in a:
- cnf += str(ln + "\n")
- open(conf_file, 'w').write(cnf)
- if os.path.exists(dhclient_conf_dir + self._ifname + '.pid'):
- try:
- ret = subprocess.check_output(
- ['/sbin/dhclient -4 -r -pf ' + pidfile], shell=True).decode()
- except subprocess.CalledProcessError as e:
- if self._debug():
- self._debug(e)
- try:
- ret = subprocess.check_output(
- ['/sbin/dhclient -4 -q -nw -cf ' + conf_file + ' -pf ' + pidfile + ' -lf ' + leasefile + ' ' + self._ifname], shell=True).decode()
- return True
- except subprocess.CalledProcessError as e:
- if self._debug():
- self._debug(e)
- return None
+ def set_dhcp(self):
+ """
+ Configure interface as DHCP client. The dhclient binary is automatically
+ started in background!
- def del_dhcpv4(self):
- conf_file = dhclient_conf_dir + self._ifname + '.conf'
- pidfile = dhclient_conf_dir + self._ifname + '.pid'
- leasefile = dhclient_conf_dir + self._ifname + '.leases'
- if not os.path.exists(pidfile):
- return 1
- try:
- ret = subprocess.check_output(
- ['/sbin/dhclient -4 -r -pf ' + pidfile], shell=True).decode()
- return True
- except subprocess.CalledProcessError as e:
- if self._debug():
- self._debug(e)
- return None
+ Example:
- def get_dhcpv4(self):
- pidfile = dhclient_conf_dir + self._ifname + '.pid'
- if not os.path.exists(pidfile):
- print (
- "no dhcp client running on interface {0}".format(self._ifname))
- return False
+ >>> from vyos.ifconfig import Interface
+ >>> j = Interface('eth0')
+ >>> j.set_dhcp()
+ """
+ dhcp = {
+ 'hostname': 'vyos',
+ 'intf': self._ifname
+ }
+
+ # read configured system hostname.
+ # maybe change to vyos hostd client ???
+ with open('/etc/hostname', 'r') as f:
+ dhcp['hostname'] = f.read().rstrip('\n')
+
+ # render DHCP configuration
+ tmpl = jinja2.Template(dhcp_cfg)
+ dhcp_text = tmpl.render(dhcp)
+ with open(self._dhcp_cfg_file, 'w') as f:
+ f.write(dhcp_text)
+
+ cmd = 'start-stop-daemon --start --quiet --pidfile ' + self._dhcp_pid_file
+ cmd += ' --exec /sbin/dhclient --'
+ # now pass arguments to dhclient binary
+ cmd += ' -4 -nw -cf {} -pf {} -lf {} {}'.format(self._dhcp_cfg_file, self._dhcp_pid_file, self._dhcp_lease_file, self._ifname)
+ self._cmd(cmd)
+
+
+ def del_dhcp(self):
+ """
+ De-configure interface as DHCP clinet. All auto generated files like
+ pid, config and lease will be removed.
+
+ Example:
+
+ >>> from vyos.ifconfig import Interface
+ >>> j = Interface('eth0')
+ >>> j.del_dhcp()
+ """
+ pid = 0
+ if os.path.isfile(self._dhcp_pid_file):
+ with open(self._dhcp_pid_file, 'r') as f:
+ pid = int(f.read())
else:
- pid = open(pidfile, 'r').read()
- print(
- "dhclient running on {0} with pid {1}".format(self._ifname, pid))
- return True
+ self._debug_msg('No DHCP client PID found')
+ return None
+
+ # stop dhclient
+ cmd = 'start-stop-daemon --stop --quiet --pidfile {}'.format(self._dhcp_pid_file)
+ self._cmd(cmd)
+
+ # cleanup old config file
+ if os.path.isfile(self._dhcp_cfg_file):
+ os.remove(self._dhcp_cfg_file)
+
+ # cleanup old pid file
+ if os.path.isfile(self._dhcp_pid_file):
+ os.remove(self._dhcp_pid_file)
+
+ # cleanup old lease file
+ if os.path.isfile(self._dhcp_lease_file):
+ os.remove(self._dhcp_lease_file)
+
def set_dhcpv6(self):
- conf_file = dhclient_conf_dir + self._ifname + '.v6conf'
- pidfile = dhclient_conf_dir + self._ifname + '.v6pid'
- leasefile = dhclient_conf_dir + self._ifname + '.v6leases'
- a = [
- '# generated by interface_config.py',
- 'interface \"' + self._ifname + '\" {',
- '\trequest routers, domain-name-servers, domain-name;',
- '}'
- ]
- cnf = ""
- for ln in a:
- cnf += str(ln + "\n")
- open(conf_file, 'w').write(cnf)
- subprocess.call(
- ['sysctl', '-q', '-w', 'net.ipv6.conf.' + self._ifname + '.accept_ra=0'])
- if os.path.exists(pidfile):
- try:
- ret = subprocess.check_output(
- ['/sbin/dhclient -6 -q -x -pf ' + pidfile], shell=True).decode()
- except subprocess.CalledProcessError as e:
- if self._debug():
- self._debug(e)
- try:
- ret = subprocess.check_output(
- ['/sbin/dhclient -6 -q -nw -cf ' + conf_file + ' -pf ' + pidfile + ' -lf ' + leasefile + ' ' + self._ifname], shell=True).decode()
- return True
- except subprocess.CalledProcessError as e:
- if self._debug():
- self._debug(e)
- return None
+ """
+ Configure interface as DHCPv6 client. The dhclient binary is automatically
+ started in background!
+
+ Example:
+
+ >>> from vyos.ifconfig import Interface
+ >>> j = Interface('eth0')
+ >>> j.set_dhcpv6()
+ """
+ dhcpv6 = {
+ 'intf': self._ifname
+ }
+
+ # render DHCP configuration
+ tmpl = jinja2.Template(dhcpv6_cfg)
+ dhcpv6_text = tmpl.render(dhcpv6)
+ with open(self._dhcpv6_cfg_file, 'w') as f:
+ f.write(dhcpv6_text)
+
+ # https://bugs.launchpad.net/ubuntu/+source/ifupdown/+bug/1447715
+ #
+ # wee need to wait for IPv6 DAD to finish once and interface is added
+ # this suxx :-(
+ sleep(5)
+
+ # no longer accept router announcements on this interface
+ cmd = 'sysctl -q -w net.ipv6.conf.{}.accept_ra=0'.format(self._ifname)
+ self._cmd(cmd)
+
+ cmd = 'start-stop-daemon --start --quiet --pidfile ' + self._dhcpv6_pid_file
+ cmd += ' --exec /sbin/dhclient --'
+ # now pass arguments to dhclient binary
+ cmd += ' -6 -nw -cf {} -pf {} -lf {} {}'.format(self._dhcpv6_cfg_file, self._dhcpv6_pid_file, self._dhcpv6_lease_file, self._ifname)
+ self._cmd(cmd)
+
def del_dhcpv6(self):
- conf_file = dhclient_conf_dir + self._ifname + '.v6conf'
- pidfile = dhclient_conf_dir + self._ifname + '.v6pid'
- leasefile = dhclient_conf_dir + self._ifname + '.v6leases'
- if not os.path.exists(pidfile):
- return 1
- try:
- ret = subprocess.check_output(
- ['/sbin/dhclient -6 -q -x -pf ' + pidfile], shell=True).decode()
- subprocess.call(
- ['sysctl', '-q', '-w', 'net.ipv6.conf.' + self._ifname + '.accept_ra=1'])
- return True
- except subprocess.CalledProcessError as e:
- if self._debug():
- self._debug(e)
+ """
+ De-configure interface as DHCPv6 clinet. All auto generated files like
+ pid, config and lease will be removed.
+
+ Example:
+
+ >>> from vyos.ifconfig import Interface
+ >>> j = Interface('eth0')
+ >>> j.del_dhcpv6()
+ """
+ pid = 0
+ if os.path.isfile(self._dhcpv6_pid_file):
+ with open(self._dhcpv6_pid_file, 'r') as f:
+ pid = int(f.read())
+ else:
+ self._debug_msg('No DHCPv6 client PID found')
return None
- def get_dhcpv6(self):
- pidfile = dhclient_conf_dir + self._ifname + '.v6pid'
- if not os.path.exists(pidfile):
- print (
- "no dhcpv6 client running on interface {0}".format(self._ifname))
- return False
+ # stop dhclient
+ cmd = 'start-stop-daemon --stop --quiet --pidfile {}'.format(self._dhcpv6_pid_file)
+ self._cmd(cmd)
+
+ # accept router announcements on this interface
+ cmd = 'sysctl -q -w net.ipv6.conf.{}.accept_ra=1'.format(self._ifname)
+ self._cmd(cmd)
+
+ # cleanup old config file
+ if os.path.isfile(self._dhcpv6_cfg_file):
+ os.remove(self._dhcpv6_cfg_file)
+
+ # cleanup old pid file
+ if os.path.isfile(self._dhcpv6_pid_file):
+ os.remove(self._dhcpv6_pid_file)
+
+ # cleanup old lease file
+ if os.path.isfile(self._dhcpv6_lease_file):
+ os.remove(self._dhcpv6_lease_file)
+
+
+class LoopbackIf(Interface):
+ def __init__(self, ifname):
+ super().__init__(ifname, type='loopback')
+
+
+class DummyIf(Interface):
+ def __init__(self, ifname):
+ super().__init__(ifname, type='dummy')
+
+
+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/{0}/bridge/ageing_time'
+ .format(self._ifname)) / 100)
+
+ @ageing_time.setter
+ def 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
+ """
+ time = int(time) * 100
+ return self._write_sysfs('/sys/class/net/{0}/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/{0}/bridge/forward_delay'
+ .format(self._ifname)) / 100)
+
+ @forward_delay.setter
+ def 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
+ """
+ return self._write_sysfs('/sys/class/net/{0}/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/{0}/bridge/hello_time'
+ .format(self._ifname)) / 100)
+
+
+ @hello_time.setter
+ def 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
+ """
+ return self._write_sysfs('/sys/class/net/{0}/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/{0}/bridge/max_age'
+ .format(self._ifname)) / 100)
+
+ @max_age.setter
+ def 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
+ """
+ return self._write_sysfs('/sys/class/net/{0}/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/{0}/bridge/priority'
+ .format(self._ifname))
+
+ @priority.setter
+ def priority(self, priority):
+ """
+ Set bridge max aging time in seconds.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> BridgeIf('br0').priority = 8192
+ """
+ return self._write_sysfs('/sys/class/net/{0}/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/{0}/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):
+ """
+ Set bridge STP (Spannign Tree) state. 0 -> STP disabled, 1 -> STP enabled
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> BridgeIf('br0').stp_state = 1
+ """
+
+ if int(state) >= 0 and int(state) <= 1:
+ return self._write_sysfs('/sys/class/net/{0}/bridge/stp_state'
+ .format(self._ifname), state)
+ 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/{0}/bridge/multicast_querier'
+ .format(self._ifname))
+
+ @multicast_querier.setter
+ def 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
+ host, that host is tracked based on the time that the query was received
+ plus the multicast query interval time.
+
+ Use enable=1 to enable or enable=0 to disable
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> BridgeIf('br0').multicast_querier = 1
+ """
+ if int(enable) >= 0 and int(enable) <= 1:
+ return self._write_sysfs('/sys/class/net/{0}/bridge/multicast_querier'
+ .format(self._ifname), enable)
else:
- pid = open(pidfile, 'r').read()
- print(
- "dhclientv6 running on {0} with pid {1}".format(self._ifname, pid))
- return True
+ raise ValueError("Value out of range")
+
+
+ def add_port(self, interface):
+ """
+ Add physical interface to bridge (member port)
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> BridgeIf('br0').add_port('eth0')
+ >>> BridgeIf('br0').add_port('eth1')
+ """
+ if not interface:
+ raise ValueError('No interface address specified')
+
+ cmd = 'ip link set dev "{}" master "{}"'.format(interface, self._ifname)
+ self._cmd(cmd)
+
+
+ def del_port(self, interface):
+ """
+ Add bridge member port
+
+ Example:
+
+ >>> from vyos.ifconfig import Interface
+ >>> BridgeIf('br0').del_port('eth1')
+ """
+ if not interface:
+ raise ValueError('No interface address specified')
+
+ 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 BondIf(Interface):
+ def __init__(self, ifname):
+ super().__init__(ifname, type='bond')
+
+ @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.
+
+ 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):
+ """
+ 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.
+
+ The default value is layer2
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> BondIf('bond0').xmit_hash_policy = 'layer2+3'
+ >>> BondIf('bond0').proxy_arp
+ '1'
+ """
+ if not mode in ['layer2', 'layer2+3', 'layer3+4', 'encap2+3', 'encap3+4']:
+ raise ValueError()
+ return self._write_sysfs('/sys/class/net/{}/bonding/xmit_hash_policy'
+ .format(self._ifname), mode)
+
+ @property
+ def arp_interval(self):
+ """
+ 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))
+
+ @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.
+
+ The default value is no IP addresses.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> BondIf('bond0').arp_interval = '100'
+ >>> BondIf('bond0').arp_interval
+ '100'
+ """
+ return self._write_sysfs('/sys/class/net/{}/bonding/arp_interval'
+ .format(self._ifname), time)
+
+ @property
+ def 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
+ 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.
+
+ The default value is no IP addresses.
+
+ Example:
+ >>> from vyos.ifconfig import BondIf
+ >>> BondIf('bond0').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):
+ """
+ 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.
+
+ The default value is no IP addresses.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> BondIf('bond0').arp_ip_target = '192.0.2.1'
+ >>> BondIf('bond0').arp_ip_target
+ '192.0.2.1'
+ """
+ return self._write_sysfs('/sys/class/net/{}/bonding/arp_ip_target'
+ .format(self._ifname), mode)
-# TODO: dhcpv6-pd via dhclient
diff --git a/src/conf_mode/interface-bridge.py b/src/conf_mode/interface-bridge.py
index d5661be93..6e48a1382 100755
--- a/src/conf_mode/interface-bridge.py
+++ b/src/conf_mode/interface-bridge.py
@@ -16,21 +16,20 @@
#
#
-from os import environ
+import os
+
from copy import deepcopy
from sys import exit
-from pyroute2 import IPDB
from netifaces import interfaces
from vyos.config import Config
-from vyos.validate import is_ip
-from vyos.ifconfig import Interface as IF
+from vyos.ifconfig import BridgeIf, Interface
from vyos import ConfigError
default_config_data = {
'address': [],
'address_remove': [],
'aging': 300,
- 'arp_cache_timeout_ms': 30000,
+ 'arp_cache_tmo': 30,
'description': '',
'deleted': False,
'disable': False,
@@ -57,7 +56,7 @@ def get_config():
# determine tagNode instance
try:
- bridge['intf'] = environ['VYOS_TAGNODE_VALUE']
+ bridge['intf'] = os.environ['VYOS_TAGNODE_VALUE']
except KeyError as E:
print("Interface not specified")
@@ -82,8 +81,6 @@ def get_config():
# retrieve interface description
if conf.exists('description'):
bridge['description'] = conf.return_value('description')
- else:
- bridge['description'] = bridge['intf']
# Disable this bridge interface
if conf.exists('disable'):
@@ -107,7 +104,7 @@ def get_config():
# ARP cache entry timeout in seconds
if conf.exists('ip arp-cache-timeout'):
- bridge['arp_cache_timeout_ms'] = int(conf.return_value('ip arp-cache-timeout')) * 1000
+ bridge['arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout'))
# Media Access Control (MAC) address
if conf.exists('mac'):
@@ -181,56 +178,35 @@ def generate(bridge):
return None
def apply(bridge):
- ipdb = IPDB(mode='explicit')
- brif = bridge['intf']
+ br = BridgeIf(bridge['intf'])
if bridge['deleted']:
- try:
- # delete bridge interface
- with ipdb.interfaces[ brif ] as br:
- br.remove()
-
- # stop DHCP(v6) clients if configured
- for addr in bridge['address_remove']:
- if addr == 'dhcp':
- IF(brif).del_dhcpv4()
- elif addr == 'dhcpv6':
- IF(brif).del_dhcpv6()
- except:
- pass
+ # delete bridge interface
+ # DHCP is stopped inside remove()
+ br.remove()
else:
- try:
- # create bridge interface if it not already exists
- ipdb.create(kind='bridge', ifname=brif).commit()
- except:
- pass
-
- # get handle in bridge interface
- br = ipdb.interfaces[brif]
- # begin() a transaction prior to make any change
- br.begin()
# enable interface
- br.up()
- # set ageing time - - value is in centiseconds YES! centiseconds!
- br.br_ageing_time = bridge['aging'] * 100
- # set bridge forward delay - value is in centiseconds YES! centiseconds!
- br.br_forward_delay = bridge['forwarding_delay'] * 100
- # set hello time - value is in centiseconds YES! centiseconds!
- br.br_hello_time = bridge['hello_time'] * 100
- # set max message age - value is in centiseconds YES! centiseconds!
- br.br_max_age = bridge['max_age'] * 100
+ br.state = 'up'
+ # set ageing time
+ br.ageing_time = bridge['aging']
+ # set bridge forward delay
+ br.forward_delay = bridge['forwarding_delay']
+ # set hello time
+ br.hello_time = bridge['hello_time']
+ # set max message age
+ br.max_age = bridge['max_age']
# set bridge priority
- br.br_priority = bridge['priority']
+ br.priority = bridge['priority']
# turn stp on/off
- br.br_stp_state = bridge['stp']
+ br.stp_state = bridge['stp']
# enable or disable IGMP querier
- br.br_mcast_querier = bridge['igmp_querier']
+ br.multicast_querier = bridge['igmp_querier']
# update interface description used e.g. within SNMP
br.ifalias = bridge['description']
# Change interface MAC address
if bridge['mac']:
- br.set_address = bridge['mac']
+ br.mac = bridge['mac']
# remove interface from bridge
for intf in bridge['member_remove']:
@@ -240,52 +216,40 @@ def apply(bridge):
for member in bridge['member']:
br.add_port(member['name'])
+ # up/down interface
+ if bridge['disable']:
+ br.state = 'down'
+
# remove configured network interface addresses/DHCP(v6) configuration
for addr in bridge['address_remove']:
- try:
- is_ip(addr)
- br.del_ip(addr)
- except ValueError:
- if addr == 'dhcp':
- IF(brif).del_dhcpv4()
- elif addr == 'dhcpv6':
- IF(brif).del_dhcpv6()
+ if addr == 'dhcp':
+ br.del_dhcp()
+ elif addr == 'dhcpv6':
+ br.del_dhcpv6()
+ else:
+ br.del_addr(addr)
# add configured network interface addresses/DHCP(v6) configuration
for addr in bridge['address']:
- try:
- is_ip(addr)
- br.add_ip(addr)
- except:
- if addr == 'dhcp':
- IF(brif).set_dhcpv4()
- elif addr == 'dhcpv6':
- IF(brif).set_dhcpv6()
-
- # up/down interface
- if bridge['disable']:
- br.down()
-
- # commit changes on bridge interface
- br.commit()
+ if addr == 'dhcp':
+ br.set_dhcp()
+ elif addr == 'dhcpv6':
+ br.set_dhcpv6()
+ else:
+ br.add_addr(addr)
# configure additional bridge member options
for member in bridge['member']:
- # configure ARP cache timeout in milliseconds
- with open('/proc/sys/net/ipv4/neigh/' + member['name'] + '/base_reachable_time_ms', 'w') as f:
- f.write(str(bridge['arp_cache_timeout_ms']))
- # ignore link state changes
- with open('/proc/sys/net/ipv4/conf/' + member['name'] + '/link_filter', 'w') as f:
- f.write(str(bridge['disable_link_detect']))
-
- # adjust member port stp attributes
- member_if = ipdb.interfaces[ member['name'] ]
- member_if.begin()
# set bridge port cost
- member_if.brport_cost = member['cost']
+ br.set_cost(member['name'], member['cost'])
# set bridge port priority
- member_if.brport_priority = member['priority']
- member_if.commit()
+ br.set_priority(member['name'], member['priority'])
+
+ i = Interface(member['name'])
+ # configure ARP cache timeout
+ i.arp_cache_tmo = bridge['arp_cache_tmo']
+ # ignore link state changes
+ i.link_detect = bridge['disable_link_detect']
return None
diff --git a/src/conf_mode/interface-dummy.py b/src/conf_mode/interface-dummy.py
index d8a36a5b2..03afdc668 100755
--- a/src/conf_mode/interface-dummy.py
+++ b/src/conf_mode/interface-dummy.py
@@ -19,8 +19,8 @@
from os import environ
from copy import deepcopy
from sys import exit
-from pyroute2 import IPDB
from vyos.config import Config
+from vyos.ifconfig import DummyIf
from vyos import ConfigError
default_config_data = {
@@ -61,8 +61,6 @@ def get_config():
# retrieve interface description
if conf.exists('description'):
dummy['description'] = conf.return_value('description')
- else:
- dummy['description'] = dummy['intf']
# Disable this interface
if conf.exists('disable'):
@@ -83,45 +81,26 @@ def generate(dummy):
return None
def apply(dummy):
- ipdb = IPDB(mode='explicit')
- dummyif = dummy['intf']
+ du = DummyIf(dummy['intf'])
# Remove dummy interface
if dummy['deleted']:
- try:
- # delete dummy interface
- with ipdb.interface[ dummyif ] as du:
- du.remove()
- except:
- pass
+ du.remove()
else:
- try:
- # create dummy interface if it's non existing
- ipdb.create(kind='dummy', ifname=dummyif).commit()
- except:
- pass
-
- # retrieve handle to dummy interface
- du = ipdb.interfaces[dummyif]
- # begin a transaction prior to make any change
- du.begin()
# enable interface
- du.up()
+ du.state = 'up'
# update interface description used e.g. within SNMP
du.ifalias = dummy['description']
# Configure interface address(es)
for addr in dummy['address_remove']:
- du.del_ip(addr)
+ du.del_addr(addr)
for addr in dummy['address']:
- du.add_ip(addr)
+ du.add_addr(addr)
# disable interface on demand
if dummy['disable']:
- du.down()
-
- # commit changes on bridge interface
- du.commit()
+ du.state = 'down'
return None
diff --git a/src/conf_mode/interface-loopback.py b/src/conf_mode/interface-loopback.py
index 5c1419b11..be47324c1 100755
--- a/src/conf_mode/interface-loopback.py
+++ b/src/conf_mode/interface-loopback.py
@@ -18,7 +18,7 @@
from os import environ
from sys import exit
from copy import deepcopy
-from pyroute2 import IPDB
+from vyos.ifconfig import LoopbackIf
from vyos.config import Config
from vyos import ConfigError
@@ -57,8 +57,6 @@ def get_config():
# retrieve interface description
if conf.exists('description'):
loopback['description'] = conf.return_value('description')
- else:
- loopback['description'] = loopback['intf']
# Determine interface addresses (currently effective) - to determine which
# address is no longer valid and needs to be removed from the interface
@@ -75,28 +73,19 @@ def generate(loopback):
return None
def apply(loopback):
- ipdb = IPDB(mode='explicit')
- lo_if = loopback['intf']
-
- # the loopback device always exists
- lo = ipdb.interfaces[lo_if]
- # begin() a transaction prior to make any change
- lo.begin()
-
+ lo = LoopbackIf(loopback['intf'])
if not loopback['deleted']:
# update interface description used e.g. within SNMP
# update interface description used e.g. within SNMP
lo.ifalias = loopback['description']
# configure interface address(es)
for addr in loopback['address']:
- lo.add_ip(addr)
+ lo.add_addr(addr)
# remove interface address(es)
for addr in loopback['address_remove']:
- lo.del_ip(addr)
+ lo.del_addr(addr)
- # commit changes on loopback interface
- lo.commit()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/ipsec-settings.py b/src/conf_mode/ipsec-settings.py
index 8d25e7abd..156bb2edd 100755
--- a/src/conf_mode/ipsec-settings.py
+++ b/src/conf_mode/ipsec-settings.py
@@ -62,7 +62,7 @@ conn {{ra_conn_name}}
left={{outside_addr}}
leftsubnet=%dynamic[/1701]
rightsubnet=%dynamic
- mark=%unique
+ mark_in=%unique
auto=add
ike=aes256-sha1-modp1024,3des-sha1-modp1024,3des-sha1-modp1024!
dpddelay=15
diff --git a/src/helpers/vyos-boot-config-loader.py b/src/helpers/vyos-boot-config-loader.py
new file mode 100755
index 000000000..06c95765f
--- /dev/null
+++ b/src/helpers/vyos-boot-config-loader.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 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 sys
+import subprocess
+import traceback
+
+from vyos.configsession import ConfigSession, ConfigSessionError
+from vyos.configtree import ConfigTree
+
+STATUS_FILE = '/tmp/vyos-config-status'
+TRACE_FILE = '/tmp/boot-config-trace'
+
+session = ConfigSession(os.getpid(), 'vyos-boot-config-loader')
+env = session.get_session_env()
+
+default_file_name = env['vyatta_sysconfdir'] + '/config.boot.default'
+
+if len(sys.argv) < 1:
+ print("Must be called with argument.")
+ sys.exit(1)
+else:
+ file_name = sys.argv[1]
+
+def write_config_status(status):
+ with open(STATUS_FILE, 'w') as f:
+ f.write('{0}\n'.format(status))
+
+def trace_to_file(trace_file_name):
+ with open(trace_file_name, 'w') as trace_file:
+ traceback.print_exc(file=trace_file)
+
+def failsafe():
+ try:
+ with open(default_file_name, 'r') as f:
+ config_file = f.read()
+ except Exception as e:
+ print("Catastrophic: no default config file "
+ "'{0}'".format(default_file_name))
+ sys.exit(1)
+
+ config = ConfigTree(config_file)
+ if not config.exists(['system', 'login', 'user', 'vyos',
+ 'authentication', 'encrypted-password']):
+ print("No password entry in default config file;")
+ print("unable to recover password for user 'vyos'.")
+ sys.exit(1)
+ else:
+ passwd = config.return_value(['system', 'login', 'user', 'vyos',
+ 'authentication',
+ 'encrypted-password'])
+
+ cmd = ("useradd -s /bin/bash -G 'users,sudo' -m -N -p '{0}' "
+ "vyos".format(passwd))
+ try:
+ subprocess.check_call(cmd, shell=True)
+ except subprocess.CalledProcessError as e:
+ sys.exit("{0}".format(e))
+
+ with open('/etc/motd', 'a+') as f:
+ f.write('\n\n')
+ f.write('!!!!!\n')
+ f.write('There were errors loading the initial configuration;\n')
+ f.write('please examine the errors in {0} and correct.'
+ '\n'.format(TRACE_FILE))
+ f.write('!!!!!\n\n')
+
+try:
+ with open(file_name, 'r') as f:
+ config_file = f.read()
+except Exception as e:
+ write_config_status(1)
+ failsafe()
+ trace_to_file(TRACE_FILE)
+ sys.exit("{0}".format(e))
+
+try:
+ session.load_config(file_name)
+ session.commit()
+ write_config_status(0)
+except ConfigSessionError as e:
+ write_config_status(1)
+ failsafe()
+ trace_to_file(TRACE_FILE)
+ sys.exit(1)
diff --git a/src/systemd/vyos-hostsd.service b/src/systemd/vyos-hostsd.service
index 3b0fadb5c..2444f5352 100644
--- a/src/systemd/vyos-hostsd.service
+++ b/src/systemd/vyos-hostsd.service
@@ -1,8 +1,7 @@
[Unit]
Description=VyOS DNS configuration keeper
-After=auditd.service time-sync.target
-Before=network-pre.target vyos-router.service
-Wants=network-pre.target
+DefaultDependencies=no
+After=systemd-remount-fs.service
[Service]
ExecStart=/usr/bin/python3 -u /usr/libexec/vyos/services/vyos-hostsd
@@ -19,6 +18,4 @@ User=root
Group=vyattacfg
[Install]
-#
-WantedBy=network.target
-
+RequiredBy=cloud-init-local.service vyos-router.service