198937Sdes/*
298937Sdes * Copyright (C) 2013-2014 Michio Honda. All rights reserved.
398937Sdes *
498937Sdes * Redistribution and use in source and binary forms, with or without
598937Sdes * modification, are permitted provided that the following conditions
698937Sdes * are met:
798937Sdes *   1. Redistributions of source code must retain the above copyright
898937Sdes *      notice, this list of conditions and the following disclaimer.
998937Sdes *   2. Redistributions in binary form must reproduce the above copyright
1098937Sdes *      notice, this list of conditions and the following disclaimer in the
1198937Sdes *    documentation and/or other materials provided with the distribution.
1298937Sdes *
1398937Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1498937Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1598937Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1698937Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1798937Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1898937Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1998937Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2098937Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2198937Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2298937Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2398937Sdes * SUCH DAMAGE.
2498937Sdes */
2598937Sdes
2698937Sdes/* $FreeBSD$ */
2798937Sdes
2898937Sdes#include <errno.h>
2998937Sdes#include <stdio.h>
3098937Sdes#include <inttypes.h>	/* PRI* macros */
3198937Sdes#include <string.h>	/* strcmp */
3298937Sdes#include <fcntl.h>	/* open */
3398937Sdes#include <unistd.h>	/* close */
34162852Sdes#include <sys/ioctl.h>	/* ioctl */
35162852Sdes#include <sys/param.h>
36162852Sdes#include <sys/socket.h>	/* apple needs sockaddr */
37162852Sdes#include <net/if.h>	/* ifreq */
3898937Sdes#include <net/netmap.h>
3998937Sdes#include <net/netmap_user.h>
4098937Sdes#include <libgen.h>	/* basename */
4198937Sdes#include <stdlib.h>	/* atoi, free */
4298937Sdes
4398937Sdes/* debug support */
4498937Sdes#define ND(format, ...)	do {} while(0)
4598937Sdes#define D(format, ...)					\
4698937Sdes	fprintf(stderr, "%s [%d] " format "\n",		\
4798937Sdes	__FUNCTION__, __LINE__, ##__VA_ARGS__)
4898937Sdes
4998937Sdes/* XXX cut and paste from pkt-gen.c because I'm not sure whether this
5098937Sdes * program may include nm_util.h
5198937Sdes */
5298937Sdesvoid parse_nmr_config(const char* conf, struct nmreq *nmr)
5398937Sdes{
5498937Sdes	char *w, *tok;
5598937Sdes	int i, v;
5698937Sdes
5798937Sdes	nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
5898937Sdes	nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
5998937Sdes	if (conf == NULL || ! *conf)
6098937Sdes		return;
6198937Sdes	w = strdup(conf);
6298937Sdes	for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
6398937Sdes		v = atoi(tok);
6498937Sdes		switch (i) {
6598937Sdes		case 0:
6698937Sdes			nmr->nr_tx_slots = nmr->nr_rx_slots = v;
6798937Sdes			break;
6898937Sdes		case 1:
6998937Sdes			nmr->nr_rx_slots = v;
7098937Sdes			break;
7198937Sdes		case 2:
7298937Sdes			nmr->nr_tx_rings = nmr->nr_rx_rings = v;
7398937Sdes			break;
7498937Sdes		case 3:
7598937Sdes			nmr->nr_rx_rings = v;
7698937Sdes			break;
7798937Sdes		default:
7898937Sdes			D("ignored config: %s", tok);
7998937Sdes			break;
8098937Sdes		}
8198937Sdes	}
8298937Sdes	D("txr %d txd %d rxr %d rxd %d",
8398937Sdes			nmr->nr_tx_rings, nmr->nr_tx_slots,
8498937Sdes			nmr->nr_rx_rings, nmr->nr_rx_slots);
8598937Sdes	free(w);
8698937Sdes}
8798937Sdes
8898937Sdesstatic int
8998937Sdesbdg_ctl(const char *name, int nr_cmd, int nr_arg, char *nmr_config)
9098937Sdes{
9198937Sdes	struct nmreq nmr;
9298937Sdes	int error = 0;
9398937Sdes	int fd = open("/dev/netmap", O_RDWR);
9498937Sdes
9598937Sdes	if (fd == -1) {
9698937Sdes		D("Unable to open /dev/netmap");
9798937Sdes		return -1;
9898937Sdes	}
9998937Sdes
10098937Sdes	bzero(&nmr, sizeof(nmr));
10198937Sdes	nmr.nr_version = NETMAP_API;
10298937Sdes	if (name != NULL) /* might be NULL */
10398937Sdes		strncpy(nmr.nr_name, name, sizeof(nmr.nr_name));
10498937Sdes	nmr.nr_cmd = nr_cmd;
10598937Sdes	parse_nmr_config(nmr_config, &nmr);
10698937Sdes
10798937Sdes	switch (nr_cmd) {
10898937Sdes	case NETMAP_BDG_DELIF:
10998937Sdes	case NETMAP_BDG_NEWIF:
11098937Sdes		error = ioctl(fd, NIOCREGIF, &nmr);
11198937Sdes		if (error == -1) {
11298937Sdes			ND("Unable to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
11398937Sdes			perror(name);
11498937Sdes		} else {
11598937Sdes			ND("Success to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
11698937Sdes		}
11798937Sdes		break;
11898937Sdes	case NETMAP_BDG_ATTACH:
11998937Sdes	case NETMAP_BDG_DETACH:
12098937Sdes		if (nr_arg && nr_arg != NETMAP_BDG_HOST)
12198937Sdes			nr_arg = 0;
12298937Sdes		nmr.nr_arg1 = nr_arg;
12398937Sdes		error = ioctl(fd, NIOCREGIF, &nmr);
12498937Sdes		if (error == -1) {
12598937Sdes			ND("Unable to %s %s to the bridge", nr_cmd ==
12698937Sdes			    NETMAP_BDG_DETACH?"detach":"attach", name);
12798937Sdes			perror(name);
12898937Sdes		} else
12998937Sdes			ND("Success to %s %s to the bridge", nr_cmd ==
13098937Sdes			    NETMAP_BDG_DETACH?"detach":"attach", name);
13198937Sdes		break;
13298937Sdes
13398937Sdes	case NETMAP_BDG_LIST:
13498937Sdes		if (strlen(nmr.nr_name)) { /* name to bridge/port info */
13598937Sdes			error = ioctl(fd, NIOCGINFO, &nmr);
13698937Sdes			if (error) {
13798937Sdes				ND("Unable to obtain info for %s", name);
13898937Sdes				perror(name);
13998937Sdes			} else
14098937Sdes				D("%s at bridge:%d port:%d", name, nmr.nr_arg1,
14198937Sdes				    nmr.nr_arg2);
14298937Sdes			break;
14398937Sdes		}
14498937Sdes
14598937Sdes		/* scan all the bridges and ports */
14698937Sdes		nmr.nr_arg1 = nmr.nr_arg2 = 0;
14798937Sdes		for (; !ioctl(fd, NIOCGINFO, &nmr); nmr.nr_arg2++) {
14898937Sdes			D("bridge:%d port:%d %s", nmr.nr_arg1, nmr.nr_arg2,
14998937Sdes			    nmr.nr_name);
15098937Sdes			nmr.nr_name[0] = '\0';
15198937Sdes		}
15298937Sdes
15398937Sdes		break;
15498937Sdes
15598937Sdes	default: /* GINFO */
15698937Sdes		nmr.nr_cmd = nmr.nr_arg1 = nmr.nr_arg2 = 0;
15798937Sdes		error = ioctl(fd, NIOCGINFO, &nmr);
15898937Sdes		if (error) {
15998937Sdes			ND("Unable to get if info for %s", name);
16098937Sdes			perror(name);
16198937Sdes		} else
16298937Sdes			D("%s: %d queues.", name, nmr.nr_rx_rings);
16398937Sdes		break;
16498937Sdes	}
16598937Sdes	close(fd);
16698937Sdes	return error;
16798937Sdes}
16898937Sdes
16998937Sdesint
17098937Sdesmain(int argc, char *argv[])
17198937Sdes{
17298937Sdes	int ch, nr_cmd = 0, nr_arg = 0;
17398937Sdes	const char *command = basename(argv[0]);
17498937Sdes	char *name = NULL, *nmr_config = NULL;
17598937Sdes
17698937Sdes	if (argc > 3) {
17798937Sdesusage:
17898937Sdes		fprintf(stderr,
17998937Sdes			"Usage:\n"
18098937Sdes			"%s arguments\n"
18198937Sdes			"\t-g interface	interface name to get info\n"
18298937Sdes			"\t-d interface	interface name to be detached\n"
18398937Sdes			"\t-a interface	interface name to be attached\n"
18498937Sdes			"\t-h interface	interface name to be attached with the host stack\n"
18598937Sdes			"\t-n interface	interface name to be created\n"
18698937Sdes			"\t-r interface	interface name to be deleted\n"
18798937Sdes			"\t-l list all or specified bridge's interfaces (default)\n"
18898937Sdes			"\t-C string ring/slot setting of an interface creating by -n\n"
18998937Sdes			"", command);
19098937Sdes		return 0;
19198937Sdes	}
19298937Sdes
19398937Sdes	while ((ch = getopt(argc, argv, "d:a:h:g:l:n:r:C:")) != -1) {
19498937Sdes		name = optarg; /* default */
19598937Sdes		switch (ch) {
19698937Sdes		default:
19798937Sdes			fprintf(stderr, "bad option %c %s", ch, optarg);
19898937Sdes			goto usage;
19998937Sdes		case 'd':
20098937Sdes			nr_cmd = NETMAP_BDG_DETACH;
20198937Sdes			break;
20298937Sdes		case 'a':
20398937Sdes			nr_cmd = NETMAP_BDG_ATTACH;
20498937Sdes			break;
20598937Sdes		case 'h':
20698937Sdes			nr_cmd = NETMAP_BDG_ATTACH;
20798937Sdes			nr_arg = NETMAP_BDG_HOST;
20898937Sdes			break;
20998937Sdes		case 'n':
21098937Sdes			nr_cmd = NETMAP_BDG_NEWIF;
21198937Sdes			break;
21298937Sdes		case 'r':
21398937Sdes			nr_cmd = NETMAP_BDG_DELIF;
21498937Sdes			break;
21598937Sdes		case 'g':
21698937Sdes			nr_cmd = 0;
21798937Sdes			break;
21898937Sdes		case 'l':
21998937Sdes			nr_cmd = NETMAP_BDG_LIST;
22098937Sdes			if (optind < argc && argv[optind][0] == '-')
22198937Sdes				name = NULL;
22298937Sdes			break;
22398937Sdes		case 'C':
22498937Sdes			nmr_config = strdup(optarg);
22598937Sdes			break;
22698937Sdes		}
22798937Sdes		if (optind != argc) {
22898937Sdes			// fprintf(stderr, "optind %d argc %d\n", optind, argc);
22998937Sdes			goto usage;
23098937Sdes		}
23198937Sdes	}
23298937Sdes	if (argc == 1)
23398937Sdes		nr_cmd = NETMAP_BDG_LIST;
23498937Sdes	return bdg_ctl(name, nr_cmd, nr_arg, nmr_config) ? 1 : 0;
23598937Sdes}
23698937Sdes