summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniil Baturin <daniil@baturin.org>2019-02-21 22:46:51 +0100
committerDaniil Baturin <daniil@baturin.org>2019-02-21 22:59:45 +0100
commit852253ce2c5cae371663d0f15b9f416b0848e9b9 (patch)
treeb14f1ef3be9503a3f292dc88752164682adb37c3
parent944a665cfc19cca1af9d46a70fb31ba1f4893d68 (diff)
downloadvyos-1x-852253ce2c5cae371663d0f15b9f416b0848e9b9.tar.gz
vyos-1x-852253ce2c5cae371663d0f15b9f416b0848e9b9.zip
[IPsec] T1260: VICI-based implementation of "show vpn ipsec sa"
-rw-r--r--debian/control1
-rwxr-xr-xsrc/op_mode/show_ipsec_sa.py168
2 files changed, 90 insertions, 79 deletions
diff --git a/debian/control b/debian/control
index 859c36f14..45e037c07 100644
--- a/debian/control
+++ b/debian/control
@@ -26,6 +26,7 @@ Depends: python3,
python3-six,
python3-isc-dhcp-leases,
python3-hurry.filesize,
+ python3-vici (>= 5.7.2),
ipaddrcheck,
tcpdump,
tshark,
diff --git a/src/op_mode/show_ipsec_sa.py b/src/op_mode/show_ipsec_sa.py
index d1385f959..0828743e8 100755
--- a/src/op_mode/show_ipsec_sa.py
+++ b/src/op_mode/show_ipsec_sa.py
@@ -1,90 +1,100 @@
#!/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 re
import sys
-import subprocess
+import vici
import tabulate
import hurry.filesize
-def parse_conn_spec(s):
- try:
- # Example: ESTABLISHED 14 seconds ago, 10.0.0.2[foo]...10.0.0.1[10.0.0.1]
- return re.search(r'.*ESTABLISHED\s+(.*)ago,\s(.*)\[(.*)\]\.\.\.(.*)\[(.*)\].*', s).groups()
- except AttributeError:
- # No active SAs found, so we have nothing to display
- print("No established security associations found.")
- print("Use \"show vpn ipsec sa verbose\" to view inactive and connecting tunnels.")
- sys.exit(0)
-
-def parse_sa_counters(s):
- bytes_in, bytes_out = None, None
- try:
- # Example with traffic: AES_CBC_256/HMAC_SHA2_256_128/ECP_521, 2382660 bytes_i (1789 pkts, 2s ago), 2382660 bytes_o ...
- bytes_in, bytes_out = re.search(r'\s+(\d+)\s+bytes_i\s\(.*pkts,.*\),\s+(\d+)\s+bytes_o', s).groups()
- except AttributeError:
- try:
- # Example without traffic: 3DES_CBC/HMAC_MD5_96/MODP_1024, 0 bytes_i, 0 bytes_o, rekeying in 45 minutes
- bytes_in, bytes_out = re.search(r'\s+(\d+)\s+bytes_i,\s+(\d+)\s+bytes_o,\s+rekeying', s).groups()
- except AttributeError:
- pass
-
- if (bytes_in is not None) and (bytes_out is not None):
- # Convert bytes to human-readable units
- bytes_in = hurry.filesize.size(int(bytes_in))
- bytes_out = hurry.filesize.size(int(bytes_out))
-
- result = "{0}/{1}".format(bytes_in, bytes_out)
- else:
- result = "N/A"
-
- return result
-
-def parse_ike_proposal(s):
- result = re.search(r'IKE proposal:\s+(.*)\s', s)
- if result:
- return result.groups(0)[0]
- else:
- return "N/A"
-
-
-# Get a list of all configured connections
-with open('/etc/ipsec.conf', 'r') as f:
- config = f.read()
- connections = set(re.findall(r'conn\s([^\s]+)\s*\n', config))
- connections = list(filter(lambda s: s != '%default', connections))
+import vyos.util
+
try:
- # DMVPN connections have to be handled separately
- with open('/etc/swanctl/swanctl.conf', 'r') as f:
- dmvpn_config = f.read()
- dmvpn_connections = re.findall(r'\s+(dmvpn-.*)\s+{\n', dmvpn_config)
- connections += dmvpn_connections
-except:
- pass
-
-status_data = []
-
-for conn in connections:
- status = subprocess.check_output("ipsec statusall {0}".format(conn), shell=True).decode()
- if not re.search(r'ESTABLISHED', status):
- status_line = [conn, "down", None, None, None, None, None]
- else:
- try:
- time, _, _, ip, id = parse_conn_spec(status)
- if ip == id:
- id = None
- counters = parse_sa_counters(status)
- enc = parse_ike_proposal(status)
- status_line = [conn, "up", time, counters, ip, id, enc]
- except Exception as e:
- print(status)
- raise e
- status_line = [conn, None, None, None, None, None]
-
- status_line = list(map(lambda x: "N/A" if x is None else x, status_line))
- status_data.append(status_line)
-
-headers = ["Connection", "State", "Up", "Bytes In/Out", "Remote address", "Remote ID", "Proposal"]
-output = tabulate.tabulate(status_data, headers)
+ session = vici.Session()
+ sas = session.list_sas()
+except PermissionError:
+ print("You do not have a permission to connect to the IPsec daemon")
+ sys.exit(1)
+except ConnectionRefusedError:
+ print("IPsec is not runing")
+ sys.exit(1)
+except Exception as e:
+ print("An error occured: {0}".format(e))
+ sys.exit(1)
+
+sa_data = []
+
+for sa in sas:
+ # list_sas() returns a list of single-item dicts
+ for peer in sa:
+ parent_sa = sa[peer]
+
+ if parent_sa["state"] == b"ESTABLISHED":
+ state = "up"
+ else:
+ state = "down"
+
+ if state == "up":
+ uptime = vyos.util.seconds_to_human(parent_sa["established"].decode())
+ else:
+ uptime = "N/A"
+
+ remote_host = parent_sa["remote-host"].decode()
+ remote_id = parent_sa["remote-id"].decode()
+
+ if remote_host == remote_id:
+ remote_id = "N/A"
+
+ # The counters can only be obtained from the child SAs
+ child_sas = parent_sa["child-sas"]
+ installed_sas = {k: v for k, v in child_sas.items() if v["state"] == b"INSTALLED"}
+
+ if not installed_sas:
+ data = [peer, state, "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"]
+ sa_data.append(data)
+ else:
+ for csa in installed_sas:
+ isa = installed_sas[csa]
+
+ bytes_in = hurry.filesize.size(int(isa["bytes-in"].decode()))
+ bytes_out = hurry.filesize.size(int(isa["bytes-out"].decode()))
+ bytes_str = "{0}/{1}".format(bytes_in, bytes_out)
+
+ pkts_in = hurry.filesize.size(int(isa["packets-in"].decode()), system=hurry.filesize.si)
+ pkts_out = hurry.filesize.size(int(isa["packets-out"].decode()), system=hurry.filesize.si)
+ pkts_str = "{0}/{1}".format(pkts_in, pkts_out)
+ # Remove B from <1K values
+ pkts_str = re.sub(r'B', r'', pkts_str)
+
+ enc = isa["encr-alg"].decode()
+ key_size = isa["encr-keysize"].decode()
+ hash = isa["integ-alg"].decode()
+ if "dh-group" in isa:
+ dh_group = isa["dh-group"].decode()
+ else:
+ dh_group = ""
+ proposal = "{0}_{1}/{2}".format(enc, key_size, hash)
+ if dh_group:
+ proposal = "{0}/{1}".format(proposal, dh_group)
+
+ data = [peer, state, uptime, bytes_str, pkts_str, remote_host, remote_id, proposal]
+ sa_data.append(data)
+
+headers = ["Connection", "State", "Uptime", "Bytes In/Out", "Packets In/Out", "Remote address", "Remote ID", "Proposal"]
+output = tabulate.tabulate(sa_data, headers)
print(output)