netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
To: Magnus Karlsson <magnus.karlsson@gmail.com>
Cc: <magnus.karlsson@intel.com>, <bjorn@kernel.org>, <ast@kernel.org>,
	<daniel@iogearbox.net>, <netdev@vger.kernel.org>,
	<bpf@vger.kernel.org>, <yhs@fb.com>, <andrii@kernel.org>,
	<martin.lau@linux.dev>, <song@kernel.org>,
	<john.fastabend@gmail.com>, <kpsingh@kernel.org>,
	<sdf@google.com>, <haoluo@google.com>, <jolsa@kernel.org>,
	<jonathan.lemon@gmail.com>
Subject: Re: [PATCH bpf-next 15/15] selftests/xsk: automatically switch XDP programs
Date: Tue, 13 Dec 2022 14:33:50 +0100	[thread overview]
Message-ID: <Y5h/Pr14E4To9C2r@boxer> (raw)
In-Reply-To: <20221206090826.2957-16-magnus.karlsson@gmail.com>

On Tue, Dec 06, 2022 at 10:08:26AM +0100, Magnus Karlsson wrote:
> From: Magnus Karlsson <magnus.karlsson@intel.com>
> 
> Implement automatic switching of XDP programs and execution modes if
> needed by a test. This makes it much simpler to write a test as it
> only has to say what XDP program it needs if it is not the default
> one. This also makes it possible to remove the initial explicit
> attachment of the XDP program as well as the explicit mode switch in
> the code. These are now handled by the same code that just checks if a
> switch is necessary, so no special cases are needed.
> 
> The default XDP program for all tests is one that sends all packets to
> the AF_XDP socket. If you need another one, please use the new
> function test_spec_set_xdp_prog() to specify what XDP programs and
> maps to use for this test.
> 
> Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com>
> ---
>  tools/testing/selftests/bpf/xsk.c        |  14 +++
>  tools/testing/selftests/bpf/xsk.h        |   1 +
>  tools/testing/selftests/bpf/xskxceiver.c | 150 ++++++++++++-----------
>  tools/testing/selftests/bpf/xskxceiver.h |   7 +-
>  4 files changed, 102 insertions(+), 70 deletions(-)
> 
> diff --git a/tools/testing/selftests/bpf/xsk.c b/tools/testing/selftests/bpf/xsk.c
> index dc6b47280ec4..d9d44a29c7cc 100644
> --- a/tools/testing/selftests/bpf/xsk.c
> +++ b/tools/testing/selftests/bpf/xsk.c
> @@ -267,6 +267,20 @@ int xsk_umem__create(struct xsk_umem **umem_ptr, void *umem_area,
>  	return err;
>  }
>  
> +bool xsk_is_in_drv_mode(u32 ifindex)

any reason why this can't live in xskxceiver currently?

> +{
> +	LIBBPF_OPTS(bpf_xdp_query_opts, opts);
> +	int ret;
> +
> +	ret = bpf_xdp_query(ifindex, XDP_FLAGS_DRV_MODE, &opts);
> +	if (ret) {
> +		printf("DRV mode query returned error %s\n", strerror(errno));
> +		return false;
> +	}
> +
> +	return opts.attach_mode == XDP_ATTACHED_DRV;
> +}
> +
>  int xsk_attach_xdp_program(struct bpf_program *prog, int ifindex, u32 xdp_flags)
>  {
>  	int prog_fd;
> diff --git a/tools/testing/selftests/bpf/xsk.h b/tools/testing/selftests/bpf/xsk.h
> index 5624d31b8db7..3cb9d69589b8 100644
> --- a/tools/testing/selftests/bpf/xsk.h
> +++ b/tools/testing/selftests/bpf/xsk.h
> @@ -201,6 +201,7 @@ int xsk_attach_xdp_program(struct bpf_program *prog, int ifindex, u32 xdp_flags)
>  void xsk_detach_xdp_program(int ifindex, u32 xdp_flags);
>  int xsk_update_xskmap(struct bpf_map *map, struct xsk_socket *xsk);
>  void xsk_clear_xskmap(struct bpf_map *map);
> +bool xsk_is_in_drv_mode(u32 ifindex);
>  
>  struct xsk_socket_config {
>  	__u32 rx_size;
> diff --git a/tools/testing/selftests/bpf/xskxceiver.c b/tools/testing/selftests/bpf/xskxceiver.c
> index 26cd64d4209f..ae9370f7145e 100644
> --- a/tools/testing/selftests/bpf/xskxceiver.c
> +++ b/tools/testing/selftests/bpf/xskxceiver.c
> @@ -96,6 +96,9 @@
>  #include <time.h>
>  #include <unistd.h>
>  #include <stdatomic.h>
> +
> +#include "xsk_def_prog.skel.h"
> +#include "xsk_xdp_drop.skel.h"
>  #include "xsk.h"
>  #include "xskxceiver.h"
>  #include <bpf/bpf.h>
> @@ -362,7 +365,6 @@ static bool ifobj_zc_avail(struct ifobject *ifobject)
>  	xsk = calloc(1, sizeof(struct xsk_socket_info));
>  	if (!xsk)
>  		goto out;
> -	ifobject->xdp_flags = XDP_FLAGS_DRV_MODE;
>  	ifobject->bind_flags = XDP_USE_NEED_WAKEUP | XDP_ZEROCOPY;
>  	ifobject->rx_on = true;
>  	xsk->rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
> @@ -501,6 +503,10 @@ static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
>  	test->total_steps = 1;
>  	test->nb_sockets = 1;
>  	test->fail = false;
> +	test->xdp_prog_rx = ifobj_rx->def_prog->progs.xsk_def_prog;
> +	test->xskmap_rx = ifobj_rx->def_prog->maps.xsk;
> +	test->xdp_prog_tx = ifobj_tx->def_prog->progs.xsk_def_prog;
> +	test->xskmap_tx = ifobj_tx->def_prog->maps.xsk;
>  }
>  
>  static void test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
> @@ -540,6 +546,16 @@ static void test_spec_set_name(struct test_spec *test, const char *name)
>  	strncpy(test->name, name, MAX_TEST_NAME_SIZE);
>  }
>  
> +static void test_spec_set_xdp_prog(struct test_spec *test, struct bpf_program *xdp_prog_rx,
> +				   struct bpf_program *xdp_prog_tx, struct bpf_map *xskmap_rx,
> +				   struct bpf_map *xskmap_tx)
> +{
> +	test->xdp_prog_rx = xdp_prog_rx;
> +	test->xdp_prog_tx = xdp_prog_tx;
> +	test->xskmap_rx = xskmap_rx;
> +	test->xskmap_tx = xskmap_tx;
> +}
> +
>  static void pkt_stream_reset(struct pkt_stream *pkt_stream)
>  {
>  	if (pkt_stream)
> @@ -1364,6 +1380,57 @@ static void handler(int signum)
>  	pthread_exit(NULL);
>  }
>  
> +static bool xdp_prog_changed(struct test_spec *test, struct ifobject *ifobj)
> +{
> +	return ifobj->xdp_prog != test->xdp_prog_rx || ifobj->mode != test->mode;
> +}
> +
> +static void xsk_attach_xdp_progs(struct test_spec *test, struct ifobject *ifobj_rx,
> +				 struct ifobject *ifobj_tx)
> +{
> +	int err;
> +
> +	if (xdp_prog_changed(test, ifobj_rx)) {
> +		xsk_detach_xdp_program(ifobj_rx->ifindex, mode_to_xdp_flags(ifobj_rx->mode));
> +		err = xsk_attach_xdp_program(test->xdp_prog_rx, ifobj_rx->ifindex,
> +					     mode_to_xdp_flags(test->mode));
> +		if (err) {
> +			printf("Error attaching XDP program\n");
> +			exit_with_error(-err);
> +		}
> +
> +		if (ifobj_rx->mode != test->mode && test->mode == TEST_MODE_DRV)

This strictly implies that TEST_MODE_SKB is followed by TEST_MODE_DRV,
correct? What if I would swap the order of the tests?

IOW why is it specific to TEST_MODE_DRV? I know that current code also
implies that, but maybe making it generic would make it easier to for
example run a standalone test later on?

> +			if (!xsk_is_in_drv_mode(ifobj_rx->ifindex)) {
> +				ksft_print_msg("ERROR: XDP prog not in DRV mode\n");
> +				exit_with_error(EINVAL);
> +			}
> +
> +		ifobj_rx->xdp_prog = test->xdp_prog_rx;
> +		ifobj_rx->xskmap = test->xskmap_rx;
> +		ifobj_rx->mode = test->mode;
> +	}
> +
> +	if (ifobj_tx && !ifobj_tx->shared_umem && xdp_prog_changed(test, ifobj_tx)) {
> +		xsk_detach_xdp_program(ifobj_tx->ifindex, mode_to_xdp_flags(ifobj_tx->mode));
> +		err = xsk_attach_xdp_program(test->xdp_prog_tx, ifobj_tx->ifindex,
> +					     mode_to_xdp_flags(test->mode));
> +		if (err) {
> +			printf("Error attaching XDP program\n");
> +			exit_with_error(-err);
> +		}
> +
> +		if (ifobj_rx->mode != test->mode && test->mode == TEST_MODE_DRV)
> +			if (!xsk_is_in_drv_mode(ifobj_tx->ifindex)) {
> +				ksft_print_msg("ERROR: XDP prog not in DRV mode\n");
> +				exit_with_error(EINVAL);
> +			}
> +
> +		ifobj_tx->xdp_prog = test->xdp_prog_tx;
> +		ifobj_tx->xskmap = test->xskmap_tx;
> +		ifobj_tx->mode = test->mode;
> +	}

All of the code above is repeated for ifobj_rx. So what if we do this:

/* FIXME: proper args and naming */
static void xsk_reattach_prog(int xyz, int abc)
{
	xsk_detach_xdp_program(ifobj_tx->ifindex, mode_to_xdp_flags(ifobj_tx->mode));
	err = xsk_attach_xdp_program(test->xdp_prog_tx, ifobj_tx->ifindex,
				     mode_to_xdp_flags(test->mode));
	if (err) {
		printf("Error attaching XDP program\n");
		exit_with_error(-err);
	}

	if (ifobj_rx->mode != test->mode && test->mode == TEST_MODE_DRV)
		if (!xsk_is_in_drv_mode(ifobj_tx->ifindex)) {
			ksft_print_msg("ERROR: XDP prog not in DRV mode\n");
			exit_with_error(EINVAL);
		}

	ifobj_tx->xdp_prog = test->xdp_prog_tx;
	ifobj_tx->xskmap = test->xskmap_tx;
	ifobj_tx->mode = test->mode;
}

Then in xsk_attach_xdp_progs:

	if (xdp_prog_changed(test, ifobj_rx)) {
		xsk_reattach_prog(xyz, abc);

	if (!ifobj_tx || ifobj_tx->shared_umem)
		return;

	if (xdp_prog_changed(test, ifobj_tx))
		xsk_reattach_prog(xyz, abc);

?

> +}
> +
>  static int __testapp_validate_traffic(struct test_spec *test, struct ifobject *ifobj_rx,
>  				      struct ifobject *ifobj_tx)
>  {
> @@ -1411,7 +1478,11 @@ static int __testapp_validate_traffic(struct test_spec *test, struct ifobject *i
>  
>  static int testapp_validate_traffic(struct test_spec *test)
>  {
> -	return __testapp_validate_traffic(test, test->ifobj_rx, test->ifobj_tx);
> +	struct ifobject *ifobj_rx = test->ifobj_rx;
> +	struct ifobject *ifobj_tx = test->ifobj_tx;
> +
> +	xsk_attach_xdp_progs(test, ifobj_rx, ifobj_tx);
> +	return __testapp_validate_traffic(test, ifobj_rx, ifobj_tx);
>  }
>  
>  static int testapp_validate_traffic_single_thread(struct test_spec *test, struct ifobject *ifobj)
> @@ -1454,7 +1525,7 @@ static void testapp_bidi(struct test_spec *test)
>  
>  	print_verbose("Switching Tx/Rx vectors\n");
>  	swap_directions(&test->ifobj_rx, &test->ifobj_tx);
> -	testapp_validate_traffic(test);
> +	__testapp_validate_traffic(test, test->ifobj_rx, test->ifobj_tx);
>  
>  	swap_directions(&test->ifobj_rx, &test->ifobj_tx);
>  }
> @@ -1623,31 +1694,15 @@ static void testapp_invalid_desc(struct test_spec *test)
>  
>  static void testapp_xdp_drop(struct test_spec *test)
>  {
> -	struct ifobject *ifobj = test->ifobj_rx;
> -	int err;
> +	struct xsk_xdp_drop *skel_rx = test->ifobj_rx->xdp_drop;
> +	struct xsk_xdp_drop *skel_tx = test->ifobj_tx->xdp_drop;
>  
>  	test_spec_set_name(test, "XDP_CONSUMES_SOME_PACKETS");
> -	xsk_detach_xdp_program(ifobj->ifindex, ifobj->xdp_flags);
> -	err = xsk_attach_xdp_program(ifobj->xdp_drop->progs.xsk_xdp_drop, ifobj->ifindex,
> -				     ifobj->xdp_flags);
> -	if (err) {
> -		printf("Error attaching XDP_DROP program\n");
> -		test->fail = true;
> -		return;
> -	}
> -	ifobj->xskmap = ifobj->xdp_drop->maps.xsk;
> +	test_spec_set_xdp_prog(test, skel_rx->progs.xsk_xdp_drop, skel_tx->progs.xsk_xdp_drop,
> +			       skel_rx->maps.xsk, skel_tx->maps.xsk);
>  
>  	pkt_stream_receive_half(test);
>  	testapp_validate_traffic(test);
> -
> -	xsk_detach_xdp_program(ifobj->ifindex, ifobj->xdp_flags);
> -	err = xsk_attach_xdp_program(ifobj->def_prog->progs.xsk_def_prog, ifobj->ifindex,
> -				     ifobj->xdp_flags);
> -	if (err) {
> -		printf("Error restoring default XDP program\n");
> -		exit_with_error(-err);
> -	}
> -	ifobj->xskmap = ifobj->def_prog->maps.xsk;
>  }
>  
>  static void testapp_poll_txq_tmout(struct test_spec *test)
> @@ -1689,7 +1744,7 @@ static void xsk_unload_xdp_programs(struct ifobject *ifobj)
>  
>  static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char *src_mac,
>  		       const char *dst_ip, const char *src_ip, const u16 dst_port,
> -		       const u16 src_port, thread_func_t func_ptr, bool load_xdp)
> +		       const u16 src_port, thread_func_t func_ptr)
>  {
>  	struct in_addr ip;
>  	int err;
> @@ -1708,23 +1763,11 @@ static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char *
>  
>  	ifobj->func_ptr = func_ptr;
>  
> -	if (!load_xdp)
> -		return;
> -
>  	err = xsk_load_xdp_programs(ifobj);
>  	if (err) {
>  		printf("Error loading XDP program\n");
>  		exit_with_error(err);
>  	}
> -
> -	ifobj->xdp_flags = mode_to_xdp_flags(TEST_MODE_SKB);
> -	err = xsk_attach_xdp_program(ifobj->def_prog->progs.xsk_def_prog, ifobj->ifindex,
> -				     ifobj->xdp_flags);
> -	if (err) {
> -		printf("Error attaching XDP program\n");
> -		exit_with_error(-err);
> -	}
> -	ifobj->xskmap = ifobj->def_prog->maps.xsk;
>  }
>  
>  static void run_pkt_test(struct test_spec *test, enum test_mode mode, enum test_type type)
> @@ -1886,31 +1929,6 @@ static bool is_xdp_supported(int ifindex)
>  	return true;
>  }
>  
> -static void change_to_drv_mode(struct ifobject *ifobj)
> -{
> -	LIBBPF_OPTS(bpf_xdp_query_opts, opts);
> -	int ret;
> -
> -	xsk_detach_xdp_program(ifobj->ifindex, ifobj->xdp_flags);
> -	ifobj->xdp_flags = XDP_FLAGS_DRV_MODE;
> -	ret = xsk_attach_xdp_program(ifobj->def_prog->progs.xsk_def_prog, ifobj->ifindex,
> -				     ifobj->xdp_flags);
> -	if (ret) {
> -		printf("Error attaching XDP program\n");
> -		exit_with_error(-ret);
> -	}
> -	ifobj->xskmap = ifobj->def_prog->maps.xsk;
> -
> -	ret = bpf_xdp_query(ifobj->ifindex, XDP_FLAGS_DRV_MODE, &opts);
> -	if (ret)
> -		exit_with_error(errno);
> -
> -	if (opts.attach_mode != XDP_ATTACHED_DRV) {
> -		ksft_print_msg("ERROR: [%s] XDP prog not in DRV mode\n");
> -		exit_with_error(EINVAL);
> -	}
> -}
> -
>  int main(int argc, char **argv)
>  {
>  	struct pkt_stream *rx_pkt_stream_default;
> @@ -1951,9 +1969,9 @@ int main(int argc, char **argv)
>  	}
>  
>  	init_iface(ifobj_rx, MAC1, MAC2, IP1, IP2, UDP_PORT1, UDP_PORT2,
> -		   worker_testapp_validate_rx, true);
> +		   worker_testapp_validate_rx);
>  	init_iface(ifobj_tx, MAC2, MAC1, IP2, IP1, UDP_PORT2, UDP_PORT1,
> -		   worker_testapp_validate_tx, !shared_netdev);
> +		   worker_testapp_validate_tx);
>  
>  	test_spec_init(&test, ifobj_tx, ifobj_rx, 0);
>  	tx_pkt_stream_default = pkt_stream_generate(ifobj_tx->umem, DEFAULT_PKT_CNT, PKT_SIZE);
> @@ -1966,12 +1984,6 @@ int main(int argc, char **argv)
>  	ksft_set_plan(modes * TEST_TYPE_MAX);
>  
>  	for (i = 0; i < modes; i++) {
> -		if (i == TEST_MODE_DRV) {
> -			change_to_drv_mode(ifobj_rx);
> -			if (!shared_netdev)
> -				change_to_drv_mode(ifobj_tx);
> -		}
> -
>  		for (j = 0; j < TEST_TYPE_MAX; j++) {
>  			test_spec_init(&test, ifobj_tx, ifobj_rx, i);
>  			run_pkt_test(&test, i, j);
> diff --git a/tools/testing/selftests/bpf/xskxceiver.h b/tools/testing/selftests/bpf/xskxceiver.h
> index 3483ac240b2e..5c66908577ef 100644
> --- a/tools/testing/selftests/bpf/xskxceiver.h
> +++ b/tools/testing/selftests/bpf/xskxceiver.h
> @@ -145,10 +145,11 @@ struct ifobject {
>  	struct xsk_def_prog *def_prog;
>  	struct xsk_xdp_drop *xdp_drop;
>  	struct bpf_map *xskmap;
> +	struct bpf_program *xdp_prog;
> +	enum test_mode mode;
>  	int ifindex;
>  	u32 dst_ip;
>  	u32 src_ip;
> -	u32 xdp_flags;
>  	u32 bind_flags;
>  	u16 src_port;
>  	u16 dst_port;
> @@ -168,6 +169,10 @@ struct test_spec {
>  	struct ifobject *ifobj_rx;
>  	struct pkt_stream *tx_pkt_stream_default;
>  	struct pkt_stream *rx_pkt_stream_default;
> +	struct bpf_program *xdp_prog_rx;
> +	struct bpf_program *xdp_prog_tx;
> +	struct bpf_map *xskmap_rx;
> +	struct bpf_map *xskmap_tx;
>  	u16 total_steps;
>  	u16 current_step;
>  	u16 nb_sockets;
> -- 
> 2.34.1
> 

      reply	other threads:[~2022-12-13 13:34 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-06  9:08 [PATCH bpf-next 00/15] selftests/xsk: speed-ups, fixes, and new XDP programs Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 01/15] selftests/xsk: print correct payload for packet dump Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 02/15] selftests/xsk: do not close unused file descriptors Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 03/15] selftests/xsk: submit correct number of frames in populate_fill_ring Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 04/15] selftests/xsk: print correct error codes when exiting Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 05/15] selftests/xsk: remove unused variable outstanding_tx Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 06/15] selftests/xsk: add debug option for creating netdevs Magnus Karlsson
2022-12-07 13:39   ` Björn Töpel
2022-12-06  9:08 ` [PATCH bpf-next 07/15] selftests/xsk: get rid of asm store/release implementations Magnus Karlsson
2022-12-06 23:48   ` Daniel Borkmann
2022-12-07  8:23     ` Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 08/15] selftests/xsk: remove namespaces Magnus Karlsson
2022-12-12 14:19   ` Maciej Fijalkowski
2022-12-14 10:44     ` Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 09/15] selftests/xsk: load and attach XDP program only once per mode Magnus Karlsson
2022-12-12 15:01   ` Maciej Fijalkowski
2022-12-14 10:50     ` Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 10/15] selftests/xsk: remove unnecessary code in control path Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 11/15] selftests/xsk: get rid of built-in XDP program Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 12/15] selftests/xsk: add test when some packets are XDP_DROPed Magnus Karlsson
2022-12-12 15:13   ` Maciej Fijalkowski
2022-12-14 10:53     ` Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 13/15] selftests/xsk: merge dual and single thread dispatchers Magnus Karlsson
2022-12-12 16:10   ` Maciej Fijalkowski
2022-12-14 10:53     ` Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 14/15] selftests/xsk: automatically restore packet stream Magnus Karlsson
2022-12-06  9:08 ` [PATCH bpf-next 15/15] selftests/xsk: automatically switch XDP programs Magnus Karlsson
2022-12-13 13:33   ` Maciej Fijalkowski [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Y5h/Pr14E4To9C2r@boxer \
    --to=maciej.fijalkowski@intel.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bjorn@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=haoluo@google.com \
    --cc=john.fastabend@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=jonathan.lemon@gmail.com \
    --cc=kpsingh@kernel.org \
    --cc=magnus.karlsson@gmail.com \
    --cc=magnus.karlsson@intel.com \
    --cc=martin.lau@linux.dev \
    --cc=netdev@vger.kernel.org \
    --cc=sdf@google.com \
    --cc=song@kernel.org \
    --cc=yhs@fb.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).