diff options
| author | Daniil Baturin <daniil@vyos.io> | 2022-06-02 09:20:37 -0400 | 
|---|---|---|
| committer | Daniil Baturin <daniil@vyos.io> | 2022-06-02 09:20:37 -0400 | 
| commit | 67b342a2c59035a5282f92e3b3d77f698dc8887c (patch) | |
| tree | 10883d56d49d9cbab035db896d0eb8b0399f3b3d | |
| parent | b9a26085b49397eb73e7110872b5c07015c911b4 (diff) | |
| download | vyos-1x-67b342a2c59035a5282f92e3b3d77f698dc8887c.tar.gz vyos-1x-67b342a2c59035a5282f92e3b3d77f698dc8887c.zip | |
T4446: use a unified neighbor display script
| -rw-r--r-- | op-mode-definitions/ipv6-route.xml.in | 30 | ||||
| -rw-r--r-- | op-mode-definitions/show-arp.xml.in | 4 | ||||
| -rw-r--r-- | op-mode-definitions/show-ip.xml.in | 24 | ||||
| -rw-r--r-- | op-mode-definitions/show-ipv6.xml.in | 2 | ||||
| -rwxr-xr-x | src/op_mode/show_neigh.py | 162 | 
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..4d96ee869 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 = "dev {0}".format(device) +    else: +        device = "" + +    if state: +        state = "nud {0}".format(state) +    else: +        state = "" + +    neigh_cmd = "ip --family {0} --json neighbor list {1} {2}".format(family, 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("Incorrect state {0}! Must be one of: reachable, stale, failed, permanent".format(args.state)) + +    try: +        print(get_formatted_output(args.family, device=args.interface, state=args.state)) +    except ValueError as e: +        print(e) +        sys.exit(1) | 
