On Thu, Sep 2, 2021 at 2:21 PM Doug Evans <d...@google.com> wrote:

> This command dumps the ARP and NDP tables maintained within slirp.
> One use-case for it is showing the guest's IPv6 address(es).
>
> Signed-off-by: Doug Evans <d...@google.com>
>
Reviewed-by: Patrick Venture <vent...@google.com>

> ---
>  hmp-commands-info.hx               | 15 +++++++
>  include/net/slirp.h                |  1 +
>  net/slirp.c                        | 15 +++++++
>  tests/acceptance/info_neighbors.py | 69 ++++++++++++++++++++++++++++++
>  4 files changed, 100 insertions(+)
>  create mode 100644 tests/acceptance/info_neighbors.py
>
> diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
> index 27206ac049..386f09f163 100644
> --- a/hmp-commands-info.hx
> +++ b/hmp-commands-info.hx
> @@ -512,6 +512,21 @@ SRST
>      Show user network stack connection states.
>  ERST
>
> +#if defined(CONFIG_SLIRP)
> +    {
> +        .name       = "neighbors",
> +        .args_type  = "",
> +        .params     = "",
> +        .help       = "show the ARP and NDP tables",
> +        .cmd        = hmp_info_neighbors,
> +    },
> +#endif
> +
> +SRST
> +  ``info neighbors``
> +    Show the ARP and NDP tables.
> +ERST
> +
>      {
>          .name       = "migrate",
>          .args_type  = "",
> diff --git a/include/net/slirp.h b/include/net/slirp.h
> index bad3e1e241..b9ccfda1e7 100644
> --- a/include/net/slirp.h
> +++ b/include/net/slirp.h
> @@ -31,6 +31,7 @@ void hmp_hostfwd_add(Monitor *mon, const QDict *qdict);
>  void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict);
>
>  void hmp_info_usernet(Monitor *mon, const QDict *qdict);
> +void hmp_info_neighbors(Monitor *mon, const QDict *qdict);
>
>  #endif
>
> diff --git a/net/slirp.c b/net/slirp.c
> index ad3a838e0b..29a4cd3fe1 100644
> --- a/net/slirp.c
> +++ b/net/slirp.c
> @@ -1028,6 +1028,21 @@ void hmp_info_usernet(Monitor *mon, const QDict
> *qdict)
>      }
>  }
>
> +void hmp_info_neighbors(Monitor *mon, const QDict *qdict)
> +{
> +    SlirpState *s;
> +
> +    QTAILQ_FOREACH(s, &slirp_stacks, entry) {
> +        int id;
> +        bool got_hub_id = net_hub_id_for_client(&s->nc, &id) == 0;
> +        char *info = slirp_neighbor_info(s->slirp);
> +        monitor_printf(mon, "Hub %d (%s):\n%s",
> +                       got_hub_id ? id : -1,
> +                       s->nc.name, info);
> +        g_free(info);
> +    }
> +}
> +
>  static void
>  net_init_slirp_configs(const StringList *fwd, int flags)
>  {
> diff --git a/tests/acceptance/info_neighbors.py
> b/tests/acceptance/info_neighbors.py
> new file mode 100644
> index 0000000000..ff79ec3ff3
> --- /dev/null
> +++ b/tests/acceptance/info_neighbors.py
> @@ -0,0 +1,69 @@
> +# Test for the hmp command "info neighbors"
> +#
> +# Copyright 2021 Google LLC
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2 or
> +# later.  See the COPYING file in the top-level directory.
> +
> +import re
> +
> +from avocado_qemu import LinuxTest
> +from avocado_qemu import Test
> +
> +VNET_HUB_HEADER = 'Hub -1 (vnet):'
> +NEIGHBOR_HEADER_REGEX = '^ *Table *MacAddr *IP Address$'
> +
> +def trim(text):
> +    return " ".join(text.split())
> +
> +def hmc(test, cmd):
> +    return test.vm.command('human-monitor-command', command_line=cmd)
> +
> +def get_neighbors(test):
> +    output = hmc(test, 'info neighbors').splitlines()
> +    if len(output) < 2:
> +        test.fail("Insufficient output from 'info neighbors'")
> +    test.assertEquals(output[0], VNET_HUB_HEADER)
> +    test.assertTrue(re.fullmatch(NEIGHBOR_HEADER_REGEX, output[1]))
> +    return output[2:]
> +
> +class InfoNeighborsNone(Test):
> +
> +    def test_no_neighbors(self):
> +        self.vm.add_args('-nodefaults',
> +                         '-netdev', 'user,id=vnet',
> +                         '-device', 'virtio-net,netdev=vnet')
> +        self.vm.launch()
> +        neighbors = get_neighbors(self)
> +        self.assertEquals(len(neighbors), 0)
> +
> +class InfoNeighbors(LinuxTest):
> +
> +    def test_neighbors(self):
> +        """
> +        :avocado: tags=arch:x86_64
> +        :avocado: tags=machine:pc
> +        :avocado: tags=accel:kvm
> +        """
> +        self.require_accelerator('kvm')
> +        self.vm.add_args("-accel", "kvm")
> +        self.vm.add_args('-nographic',
> +                         '-m', '1024')
> +        self.launch_and_wait()
> +
> +        # Ensure there's some packets to the guest and back.
> +        self.ssh_command('pwd')
> +
> +        # We should now be aware of the guest as a neighbor.
> +        expected_ipv4_neighbor = 'ARP 52:54:00:12:34:56 10.0.2.15'
> +        # The default ipv6 net is fec0. Both fe80 and fec0 can appear.
> +        expected_ipv6_neighbors = [
> +            'NDP 52:54:00:12:34:56 fe80::5054:ff:fe12:3456',
> +            'NDP 52:54:00:12:34:56 fec0::5054:ff:fe12:3456'
> +        ]
> +        neighbors = get_neighbors(self)
> +        self.assertTrue(len(neighbors) >= 2 and len(neighbors) <= 3)
> +        # IPv4 is output first.
> +        self.assertEquals(trim(neighbors[0]), expected_ipv4_neighbor)
> +        for neighbor in neighbors[1:]:
> +            self.assertTrue(trim(neighbor) in expected_ipv6_neighbors)
> --
> 2.33.0.153.gba50c8fa24-goog
>
>
>

Reply via email to