On Thu, Sep 2, 2021 at 2:21 PM Doug Evans <[email protected]> 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 <[email protected]>
>
Reviewed-by: Patrick Venture <[email protected]>
> ---
> 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
>
>
>