diff options
author | kroy <kroy@kroy.io> | 2019-10-22 14:22:09 -0500 |
---|---|---|
committer | kroy <kroy@kroy.io> | 2019-10-22 14:22:09 -0500 |
commit | 091f68baec1b732bc28a203419be04b8e9b985e4 (patch) | |
tree | 65398648422bd3929552d1b63d51d835027769d9 /python/vyos | |
parent | 4ed6e7bf28eb409228008eb6ea4adab7837c71cd (diff) | |
download | vyos-1x-091f68baec1b732bc28a203419be04b8e9b985e4.tar.gz vyos-1x-091f68baec1b732bc28a203419be04b8e9b985e4.zip |
T1759: Migrating interfaces
Diffstat (limited to 'python/vyos')
-rw-r--r-- | python/vyos/interface.py | 131 | ||||
-rw-r--r-- | python/vyos/interfaces.py | 45 |
2 files changed, 175 insertions, 1 deletions
diff --git a/python/vyos/interface.py b/python/vyos/interface.py new file mode 100644 index 000000000..6d57d972b --- /dev/null +++ b/python/vyos/interface.py @@ -0,0 +1,131 @@ +# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# 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 vyos +from vyos.config import Config +import vyos.interfaces + +import re +import json + +import subprocess +import time +from datetime import timedelta +import glob +from os.path import isfile +from tabulate import tabulate +from hurry.filesize import size,alternative + +class Interface(): + + intf = None + intf_type = None + + def __init__(self,intf): + self.intf = intf + self.intf_type = vyos.interfaces.get_type_of_interface(self.intf) + + def print_interface(self): + if (self.intf_type == 'wireguard'): + self.print_wireguard_interface() + + self.print_interface_stats() + + def print_interface_stats(self): + stats = self.get_interface_stats() + rx = [['bytes','packets','errors','dropped','overrun','mcast'],[stats['rx_bytes'],stats['rx_packets'],stats['rx_errors'],stats['rx_dropped'],stats['rx_over_errors'],stats['multicast']]] + tx = [['bytes','packets','errors','dropped','carrier','collisions'],[stats['tx_bytes'],stats['tx_packets'],stats['tx_errors'],stats['tx_dropped'],stats['tx_carrier_errors'],stats['collisions']]] + output = "RX: \n" + output += tabulate(rx,headers="firstrow",numalign="right",tablefmt="plain") + output += "\n\nTX: \n" + output += tabulate(tx,headers="firstrow",numalign="right",tablefmt="plain") + print(' '.join(('\n'+output.lstrip()).splitlines(True))) + + def get_interface_stats(self): + interface_stats = dict() + devices = [f for f in glob.glob("/sys/class/net/**/statistics")] + for dev_path in devices: + metrics = [f for f in glob.glob(dev_path +"/**")] + dev = re.findall(r"/sys/class/net/(.*)/statistics",dev_path)[0] + dev_dict = dict() + for metric_path in metrics: + metric = metric_path.replace(dev_path+"/","") + if isfile(metric_path): + data = open(metric_path, 'r').read()[:-1] + dev_dict[metric] = int(data) + interface_stats[dev] = dev_dict + return interface_stats[self.intf] + + def print_wireguard_interface(self): + output = subprocess.check_output(["wg", "show", self.intf], universal_newlines=True) + wgdump = vyos.interfaces.wireguard_dump()[self.intf] + c = Config() + c.set_level("interfaces wireguard {}".format(self.intf)) + description = c.return_effective_value("description") + + print ("interface: {}".format(self.intf)) + """ if the interface has a description, modify the output to include it """ + if (description): + print (" description: {}".format(description)) + output = re.sub(r"interface: {}".format(re.escape(self.intf)),"interface: {}\n Description: {}".format(self.intf,description),output) + + print (" public key: {}".format(wgdump['public_key'])) + print (" private key: (hidden)") + print (" listening port: {}".format(wgdump['listen_port'])) + print () + + for peer in c.list_effective_nodes(' peer'): + if wgdump['peers']: + pubkey = c.return_effective_value("peer {} pubkey".format(peer)) + if pubkey in wgdump['peers']: + wgpeer = wgdump['peers'][pubkey] + + print (" peer: {}".format(peer)) + print (" public key: {}".format(pubkey)) + + """ figure out if the tunnel is recently active or not """ + status = "inactive" + if (wgpeer['latest_handshake'] is None): + """ no handshake ever """ + status = "inactive" + else: + if int(wgpeer['latest_handshake']) > 0: + delta = timedelta(seconds=int(time.time() - wgpeer['latest_handshake'])) + print (" latest handshake: {}".format(delta)) + if (time.time() - int(wgpeer['latest_handshake']) < (60*5)): + """ Five minutes and the tunnel is still active """ + status = "active" + else: + """ it's been longer than 5 minutes """ + status = "inactive" + elif int(wgpeer['latest_handshake']) == 0: + """ no handshake ever """ + status = "inactive" + print (" status: {}".format(status)) + + if wgpeer['endpoint'] is not None: + print (" endpoint: {}".format(wgpeer['endpoint'])) + + if wgpeer['allowed_ips'] is not None: + print (" allowed ips: {}".format(",".join(wgpeer['allowed_ips']).replace(",",", "))) + + if wgpeer['transfer_rx'] > 0 or wgpeer['transfer_tx'] > 0: + rx_size =size(wgpeer['transfer_rx'],system=alternative) + tx_size =size(wgpeer['transfer_tx'],system=alternative) + print (" transfer: {} received, {} sent".format(rx_size,tx_size)) + + if wgpeer['persistent_keepalive'] is not None: + print (" persistent keepalive: every {} seconds".format(wgpeer['persistent_keepalive'])) + print() diff --git a/python/vyos/interfaces.py b/python/vyos/interfaces.py index d69ce9d04..ecf061d17 100644 --- a/python/vyos/interfaces.py +++ b/python/vyos/interfaces.py @@ -16,9 +16,9 @@ import re import json +import subprocess import netifaces - intf_type_data_file = '/usr/share/vyos/interface-types.json' def list_interfaces(): @@ -54,3 +54,46 @@ def get_type_of_interface(intf): return key raise ValueError("No type found for interface name: {0}".format(intf)) + +def wireguard_dump(): + """Dump wireguard data in a python friendly way.""" + last_device=None + output = {} + + # Dump wireguard connection data + _f = subprocess.check_output(["wg", "show", "all", "dump"]).decode() + for line in _f.split('\n'): + if not line: + # Skip empty lines and last line + continue + items = line.split('\t') + + if last_device != items[0]: + # We are currently entering a new node + device, private_key, public_key, listen_port, fw_mark = items + last_device = device + + output[device] = { + 'private_key': None if private_key == '(none)' else private_key, + 'public_key': None if public_key == '(none)' else public_key, + 'listen_port': int(listen_port), + 'fw_mark': None if fw_mark == 'off' else int(fw_mark), + 'peers': {}, + } + else: + # We are entering a peer + device, public_key, preshared_key, endpoint, allowed_ips, latest_handshake, transfer_rx, transfer_tx, persistent_keepalive = items + if allowed_ips == '(none)': + allowed_ips = [] + else: + allowed_ips = allowed_ips.split('\t') + output[device]['peers'][public_key] = { + 'preshared_key': None if preshared_key == '(none)' else preshared_key, + 'endpoint': None if endpoint == '(none)' else endpoint, + 'allowed_ips': allowed_ips, + 'latest_handshake': None if latest_handshake == '0' else int(latest_handshake), + 'transfer_rx': int(transfer_rx), + 'transfer_tx': int(transfer_tx), + 'persistent_keepalive': None if persistent_keepalive == 'off' else int(persistent_keepalive), + } + return output |