summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--op-mode-definitions/ipv6-route.xml.in30
-rw-r--r--op-mode-definitions/show-arp.xml.in4
-rw-r--r--op-mode-definitions/show-ip.xml.in24
-rw-r--r--op-mode-definitions/show-ipv6.xml.in2
-rwxr-xr-xsrc/op_mode/show_neigh.py162
5 files changed, 133 insertions, 89 deletions
diff --git a/op-mode-definitions/ipv6-route.xml.in b/op-mode-definitions/ipv6-route.xml.in
index 5f20444d4..4f8792f9f 100644
--- a/op-mode-definitions/ipv6-route.xml.in
+++ b/op-mode-definitions/ipv6-route.xml.in
@@ -7,7 +7,7 @@
<children>
<node name="ipv6">
<properties>
- <help>Show IPv6 routing information</help>
+ <help>Show IPv6 networking information</help>
</properties>
<children>
<leafNode name="groups">
@@ -16,14 +16,32 @@
</properties>
<command>netstat -gn6</command>
</leafNode>
-
- <leafNode name="neighbors">
+ <node name="neighbors">
<properties>
- <help>Show IPv6 Neighbor Discovery (ND) information</help>
+ <help>Show IPv6 neighbor (NDP) table</help>
</properties>
<command>${vyos_op_scripts_dir}/show_neigh.py --family inet6</command>
- </leafNode>
-
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Show IPv6 neighbor table for specified interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py -b</script>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_neigh.py --family inet6 --interface "$5"</command>
+ </tagNode>
+ <tagNode name="state">
+ <properties>
+ <help>Show IPv6 neighbors with specified state</help>
+ <completionHelp>
+ <list>reachable stale failed permanent</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_neigh.py --family inet6 --state "$5"</command>
+ </tagNode>
+ </children>
+ </node>
</children>
</node>
</children>
diff --git a/op-mode-definitions/show-arp.xml.in b/op-mode-definitions/show-arp.xml.in
index 12e7d3aa2..58cc6e45e 100644
--- a/op-mode-definitions/show-arp.xml.in
+++ b/op-mode-definitions/show-arp.xml.in
@@ -6,7 +6,7 @@
<properties>
<help>Show Address Resolution Protocol (ARP) information</help>
</properties>
- <command>/usr/sbin/arp -e -n</command>
+ <command>${vyos_op_scripts_dir}/show_neigh.py --family inet</command>
<children>
<tagNode name="interface">
<properties>
@@ -15,7 +15,7 @@
<script>${vyos_completion_dir}/list_interfaces.py -b</script>
</completionHelp>
</properties>
- <command>/usr/sbin/arp -e -n -i "$4"</command>
+ <command>${vyos_op_scripts_dir}/show_neigh.py --family inet --interface "$4"</command>
</tagNode>
</children>
</node>
diff --git a/op-mode-definitions/show-ip.xml.in b/op-mode-definitions/show-ip.xml.in
index 91564440d..d342ac192 100644
--- a/op-mode-definitions/show-ip.xml.in
+++ b/op-mode-definitions/show-ip.xml.in
@@ -4,14 +4,34 @@
<children>
<node name="ip">
<properties>
- <help>Show IPv4 routing information</help>
+ <help>Show IPv4 networking information</help>
</properties>
<children>
<node name="neighbors">
<properties>
- <help>Show IPv4 Neighbor Discovery (ND) information</help>
+ <help>Show IPv4 neighbor (ARP) table</help>
</properties>
<command>${vyos_op_scripts_dir}/show_neigh.py --family inet</command>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Show IPv4 neighbor table for specified interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py -b</script>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_neigh.py --family inet --interface "$5"</command>
+ </tagNode>
+ <tagNode name="state">
+ <properties>
+ <help>Show IPv4 neighbors with specified state</help>
+ <completionHelp>
+ <list>reachable stale failed permanent</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_neigh.py --family inet --state "$5"</command>
+ </tagNode>
+ </children>
</node>
</children>
</node>
diff --git a/op-mode-definitions/show-ipv6.xml.in b/op-mode-definitions/show-ipv6.xml.in
index a59c8df0c..66bc2485a 100644
--- a/op-mode-definitions/show-ipv6.xml.in
+++ b/op-mode-definitions/show-ipv6.xml.in
@@ -4,7 +4,7 @@
<children>
<node name="ipv6">
<properties>
- <help>Show IPv6 routing information</help>
+ <help>Show IPv6 networking information</help>
</properties>
<children>
<node name="access-list">
diff --git a/src/op_mode/show_neigh.py b/src/op_mode/show_neigh.py
index 94e745493..d874bd544 100755
--- a/src/op_mode/show_neigh.py
+++ b/src/op_mode/show_neigh.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2022 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
@@ -14,83 +14,89 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#ip -j -f inet neigh list | jq
-#[
- #{
- #"dst": "192.168.101.8",
- #"dev": "enp0s25",
- #"lladdr": "78:d2:94:72:77:7e",
- #"state": [
- #"STALE"
- #]
- #},
- #{
- #"dst": "192.168.101.185",
- #"dev": "enp0s25",
- #"lladdr": "34:46:ec:76:f8:9b",
- #"state": [
- #"STALE"
- #]
- #},
- #{
- #"dst": "192.168.101.225",
- #"dev": "enp0s25",
- #"lladdr": "c2:cb:fa:bf:a0:35",
- #"state": [
- #"STALE"
- #]
- #},
- #{
- #"dst": "192.168.101.1",
- #"dev": "enp0s25",
- #"lladdr": "00:98:2b:f8:3f:11",
- #"state": [
- #"REACHABLE"
- #]
- #},
- #{
- #"dst": "192.168.101.181",
- #"dev": "enp0s25",
- #"lladdr": "d8:9b:3b:d5:88:22",
- #"state": [
- #"STALE"
- #]
- #}
-#]
+# Sample output of `ip --json neigh list`:
+#
+# [
+# {
+# "dst": "192.168.1.1",
+# "dev": "eth0", # Missing if `dev ...` option is used
+# "lladdr": "00:aa:bb:cc:dd:ee", # May be missing for failed entries
+# "state": [
+# "REACHABLE"
+# ]
+# },
+# ]
import sys
-import argparse
-import json
-from vyos.util import cmd
-
-def main():
- #parese args
- parser = argparse.ArgumentParser()
- parser.add_argument('--family', help='Protocol family', required=True)
- args = parser.parse_args()
-
- neigh_raw_json = cmd(f'ip -j -f {args.family} neigh list')
- neigh_raw_json = neigh_raw_json.lower()
- neigh_json = json.loads(neigh_raw_json)
-
- format_neigh = '%-50s %-10s %-20s %s'
- print(format_neigh % ("IP Address", "Device", "State", "LLADDR"))
- print(format_neigh % ("----------", "------", "-----", "------"))
-
- if neigh_json is not None:
- for neigh_item in neigh_json:
- dev = neigh_item['dev']
- dst = neigh_item['dst']
- lladdr = neigh_item['lladdr'] if 'lladdr' in neigh_item else ''
- state = neigh_item['state']
-
- i = 0
- for state_item in state:
- if i == 0:
- print(format_neigh % (dst, dev, state_item, lladdr))
- else:
- print(format_neigh % ('', '', state_item, ''))
- i+=1
-
+
+
+def get_raw_data(family, device=None, state=None):
+ from json import loads
+ from vyos.util import cmd
+
+ if device:
+ device = f"dev {device}"
+ else:
+ device = ""
+
+ if state:
+ state = f"nud {state}"
+ else:
+ state = ""
+
+ neigh_cmd = f"ip --family {family} --json neighbor list {device} {state}"
+
+ data = loads(cmd(neigh_cmd))
+
+ return data
+
+def get_formatted_output(family, device=None, state=None):
+ from tabulate import tabulate
+
+ def entry_to_list(e, intf=None):
+ dst = e["dst"]
+
+ # State is always a list in the iproute2 output
+ state = ", ".join(e["state"])
+
+ # Link layer address is absent from e.g. FAILED entries
+ if "lladdr" in e:
+ lladdr = e["lladdr"]
+ else:
+ lladdr = None
+
+ # Device field is absent from outputs of `ip neigh list dev ...`
+ if "dev" in e:
+ dev = e["dev"]
+ elif device:
+ dev = device
+ else:
+ raise ValueError("interface is not defined")
+
+ return [dst, dev, lladdr, state]
+
+ neighs = get_raw_data(family, device=device, state=state)
+ neighs = map(entry_to_list, neighs)
+
+ headers = ["Address", "Interface", "Link layer address", "State"]
+ return tabulate(neighs, headers)
+
if __name__ == '__main__':
- main()
+ from argparse import ArgumentParser
+
+ parser = ArgumentParser()
+ parser.add_argument("-f", "--family", type=str, default="inet", help="Address family")
+ parser.add_argument("-i", "--interface", type=str, help="Network interface")
+ parser.add_argument("-s", "--state", type=str, help="Neighbor table entry state")
+
+ args = parser.parse_args()
+
+ if args.state:
+ if args.state not in ["reachable", "failed", "stale", "permanent"]:
+ raise ValueError(f"""Incorrect state "{args.state}"! Must be one of: reachable, stale, failed, permanent""")
+
+ try:
+ print(get_formatted_output(args.family, device=args.interface, state=args.state))
+ except ValueError as e:
+ print(e)
+ sys.exit(1)