This shows up when compiling valectl on a 32 bit platform like i386 and mips32. gcc-6.4 complains about this (-Wint-to-pointer-cast). Reviewed by: vmaffione Differential Revision: https://reviews.freebsd.org/D27814
446 lines
11 KiB
C
446 lines
11 KiB
C
/*
|
|
* Copyright (C) 2013-2014 Michio Honda. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/* $FreeBSD$ */
|
|
|
|
#define LIBNETMAP_NOTHREADSAFE
|
|
#include <libnetmap.h>
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <inttypes.h> /* PRI* macros */
|
|
#include <string.h> /* strcmp */
|
|
#include <fcntl.h> /* open */
|
|
#include <unistd.h> /* close */
|
|
#include <sys/ioctl.h> /* ioctl */
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h> /* apple needs sockaddr */
|
|
#include <net/if.h> /* ifreq */
|
|
#include <libgen.h> /* basename */
|
|
#include <stdlib.h> /* atoi, free */
|
|
|
|
int verbose;
|
|
|
|
struct args {
|
|
const char *name;
|
|
const char *config;
|
|
const char *mem_id;
|
|
|
|
uint16_t nr_reqtype;
|
|
uint32_t nr_mode;
|
|
};
|
|
|
|
static void
|
|
dump_port_info(struct nmreq_port_info_get *v)
|
|
{
|
|
printf("memsize: %"PRIu64"\n", v->nr_memsize);
|
|
printf("tx_slots: %"PRIu32"\n", v->nr_tx_slots);
|
|
printf("rx_slots: %"PRIu32"\n", v->nr_rx_slots);
|
|
printf("tx_rings: %"PRIu16"\n", v->nr_tx_rings);
|
|
printf("rx_rings %"PRIu16"\n", v->nr_rx_rings);
|
|
printf("mem_id: %"PRIu16"\n", v->nr_mem_id);
|
|
}
|
|
|
|
static void
|
|
dump_newif(struct nmreq_vale_newif *v)
|
|
{
|
|
printf("tx_slots: %"PRIu32"\n", v->nr_tx_slots);
|
|
printf("rx_slots: %"PRIu32"\n", v->nr_rx_slots);
|
|
printf("tx_rings: %"PRIu16"\n", v->nr_tx_rings);
|
|
printf("rx_ring: %"PRIu16"\n", v->nr_rx_rings);
|
|
printf("mem_id: %"PRIu16"\n", v->nr_mem_id);
|
|
}
|
|
|
|
static void
|
|
dump_vale_list(struct nmreq_vale_list *v)
|
|
{
|
|
printf("bridge_idx: %"PRIu16"\n", v->nr_bridge_idx);
|
|
printf("port_idx: %"PRIu16"\n", v->nr_port_idx);
|
|
}
|
|
|
|
|
|
static void
|
|
parse_ring_config(const char* conf,
|
|
uint32_t *nr_tx_slots,
|
|
uint32_t *nr_rx_slots,
|
|
uint16_t *nr_tx_rings,
|
|
uint16_t *nr_rx_rings)
|
|
{
|
|
char *w, *tok;
|
|
int i, v;
|
|
|
|
*nr_tx_rings = *nr_rx_rings = 0;
|
|
*nr_tx_slots = *nr_rx_slots = 0;
|
|
if (conf == NULL || ! *conf)
|
|
return;
|
|
w = strdup(conf);
|
|
for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
|
|
v = atoi(tok);
|
|
switch (i) {
|
|
case 0:
|
|
*nr_tx_slots = *nr_rx_slots = v;
|
|
break;
|
|
case 1:
|
|
*nr_rx_slots = v;
|
|
break;
|
|
case 2:
|
|
*nr_tx_rings = *nr_rx_rings = v;
|
|
break;
|
|
case 3:
|
|
*nr_rx_rings = v;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "ignored config: %s", tok);
|
|
break;
|
|
}
|
|
}
|
|
ND("txr %d txd %d rxr %d rxd %d",
|
|
*nr_tx_rings, *nr_tx_slots,
|
|
*nr_rx_rings, *nr_rx_slots);
|
|
free(w);
|
|
}
|
|
|
|
static int
|
|
parse_poll_config(const char *conf, struct nmreq_vale_polling *v)
|
|
{
|
|
char *w, *tok;
|
|
int i, p;
|
|
|
|
if (conf == NULL || ! *conf) {
|
|
fprintf(stderr, "invalid null/empty config\n");
|
|
return -1;
|
|
}
|
|
w = strdup(conf);
|
|
for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
|
|
p = atoi(tok);
|
|
switch (i) {
|
|
case 0:
|
|
v->nr_mode = p ? NETMAP_POLLING_MODE_MULTI_CPU :
|
|
NETMAP_POLLING_MODE_SINGLE_CPU;
|
|
break;
|
|
case 1:
|
|
v->nr_first_cpu_id = p;
|
|
break;
|
|
case 2:
|
|
if (v->nr_mode != NETMAP_POLLING_MODE_MULTI_CPU) {
|
|
fprintf(stderr, "too many numbers in '%s'\n", conf);
|
|
return -1;
|
|
}
|
|
v->nr_num_polling_cpus = p;
|
|
break;
|
|
case 3:
|
|
fprintf(stderr, "too many numbers in '%s'\n", conf);
|
|
return -1;
|
|
}
|
|
}
|
|
free(w);
|
|
return 0;
|
|
}
|
|
|
|
static int32_t
|
|
parse_mem_id(const char *mem_id)
|
|
{
|
|
int32_t id;
|
|
|
|
if (mem_id == NULL)
|
|
return 0;
|
|
if (isdigit(*mem_id))
|
|
return atoi(mem_id);
|
|
id = nmreq_get_mem_id(&mem_id, nmctx_get());
|
|
if (id == 0) {
|
|
fprintf(stderr, "invalid format in '-m %s' (missing 'netmap:'?)\n", mem_id);
|
|
return -1;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static int
|
|
list_all(int fd, struct nmreq_header *hdr)
|
|
{
|
|
int error;
|
|
struct nmreq_vale_list *vale_list =
|
|
(struct nmreq_vale_list *)(uintptr_t)hdr->nr_body;
|
|
|
|
for (;;) {
|
|
hdr->nr_name[0] = '\0';
|
|
error = ioctl(fd, NIOCCTRL, hdr);
|
|
if (error < 0) {
|
|
if (errno == ENOENT)
|
|
break;
|
|
|
|
fprintf(stderr, "failed to list all: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
printf("%s bridge_idx %"PRIu16" port_idx %"PRIu32"\n", hdr->nr_name,
|
|
vale_list->nr_bridge_idx, vale_list->nr_port_idx);
|
|
vale_list->nr_port_idx++;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
bdg_ctl(struct args *a)
|
|
{
|
|
struct nmreq_header hdr;
|
|
struct nmreq_vale_attach vale_attach;
|
|
struct nmreq_vale_detach vale_detach;
|
|
struct nmreq_vale_newif vale_newif;
|
|
struct nmreq_vale_list vale_list;
|
|
struct nmreq_vale_polling vale_polling;
|
|
struct nmreq_port_info_get port_info_get;
|
|
int error = 0;
|
|
int fd;
|
|
int32_t mem_id;
|
|
const char *action = NULL;
|
|
|
|
fd = open("/dev/netmap", O_RDWR);
|
|
if (fd == -1) {
|
|
perror("/dev/netmap");
|
|
return 1;
|
|
}
|
|
|
|
bzero(&hdr, sizeof(hdr));
|
|
hdr.nr_version = NETMAP_API;
|
|
if (a->name != NULL) { /* might be NULL */
|
|
strncpy(hdr.nr_name, a->name, NETMAP_REQ_IFNAMSIZ - 1);
|
|
hdr.nr_name[NETMAP_REQ_IFNAMSIZ - 1] = '\0';
|
|
}
|
|
hdr.nr_reqtype = a->nr_reqtype;
|
|
|
|
switch (a->nr_reqtype) {
|
|
case NETMAP_REQ_VALE_DELIF:
|
|
/* no body */
|
|
action = "remove";
|
|
break;
|
|
|
|
case NETMAP_REQ_VALE_NEWIF:
|
|
memset(&vale_newif, 0, sizeof(vale_newif));
|
|
hdr.nr_body = (uintptr_t)&vale_newif;
|
|
parse_ring_config(a->config,
|
|
&vale_newif.nr_tx_slots,
|
|
&vale_newif.nr_rx_slots,
|
|
&vale_newif.nr_tx_rings,
|
|
&vale_newif.nr_rx_rings);
|
|
mem_id = parse_mem_id(a->mem_id);
|
|
if (mem_id < 0)
|
|
return 1;
|
|
vale_newif.nr_mem_id = mem_id;
|
|
action = "create";
|
|
break;
|
|
|
|
case NETMAP_REQ_VALE_ATTACH:
|
|
memset(&vale_attach, 0, sizeof(vale_attach));
|
|
hdr.nr_body = (uintptr_t)&vale_attach;
|
|
vale_attach.reg.nr_mode = a->nr_mode;
|
|
parse_ring_config(a->config,
|
|
&vale_attach.reg.nr_tx_slots,
|
|
&vale_attach.reg.nr_rx_slots,
|
|
&vale_attach.reg.nr_tx_rings,
|
|
&vale_attach.reg.nr_rx_rings);
|
|
mem_id = parse_mem_id(a->mem_id);
|
|
if (mem_id < 0)
|
|
return 1;
|
|
vale_attach.reg.nr_mem_id = mem_id;
|
|
action = "attach";
|
|
break;
|
|
|
|
case NETMAP_REQ_VALE_DETACH:
|
|
memset(&vale_detach, 0, sizeof(vale_detach));
|
|
hdr.nr_body = (uintptr_t)&vale_detach;
|
|
action = "detach";
|
|
break;
|
|
|
|
case NETMAP_REQ_VALE_LIST:
|
|
memset(&vale_list, 0, sizeof(vale_list));
|
|
hdr.nr_body = (uintptr_t)&vale_list;
|
|
if (a->name == NULL) {
|
|
return list_all(fd, &hdr);
|
|
}
|
|
action = "list";
|
|
break;
|
|
|
|
case NETMAP_REQ_VALE_POLLING_ENABLE:
|
|
action = "enable polling on";
|
|
/* fall through */
|
|
case NETMAP_REQ_VALE_POLLING_DISABLE:
|
|
memset(&vale_polling, 0, sizeof(vale_polling));
|
|
hdr.nr_body = (uintptr_t)&vale_polling;
|
|
parse_poll_config(a->config, &vale_polling);
|
|
if (action == NULL)
|
|
action ="disable polling on";
|
|
break;
|
|
|
|
case NETMAP_REQ_PORT_INFO_GET:
|
|
memset(&port_info_get, 0, sizeof(port_info_get));
|
|
hdr.nr_body = (uintptr_t)&port_info_get;
|
|
action = "obtain info for";
|
|
break;
|
|
}
|
|
error = ioctl(fd, NIOCCTRL, &hdr);
|
|
if (error < 0) {
|
|
fprintf(stderr, "failed to %s %s: %s\n",
|
|
action, a->name, strerror(errno));
|
|
return 1;
|
|
}
|
|
switch (hdr.nr_reqtype) {
|
|
case NETMAP_REQ_VALE_NEWIF:
|
|
if (verbose) {
|
|
dump_newif(&vale_newif);
|
|
}
|
|
break;
|
|
|
|
case NETMAP_REQ_VALE_ATTACH:
|
|
if (verbose) {
|
|
printf("port_index: %"PRIu32"\n", vale_attach.port_index);
|
|
}
|
|
break;
|
|
|
|
case NETMAP_REQ_VALE_DETACH:
|
|
if (verbose) {
|
|
printf("port_index: %"PRIu32"\n", vale_detach.port_index);
|
|
}
|
|
break;
|
|
|
|
case NETMAP_REQ_VALE_LIST:
|
|
dump_vale_list(&vale_list);
|
|
break;
|
|
|
|
case NETMAP_REQ_PORT_INFO_GET:
|
|
dump_port_info(&port_info_get);
|
|
break;
|
|
}
|
|
close(fd);
|
|
return error;
|
|
}
|
|
|
|
static void
|
|
usage(int errcode)
|
|
{
|
|
fprintf(stderr,
|
|
"Usage:\n"
|
|
"vale-ctl [arguments]\n"
|
|
"\t-g interface interface name to get info\n"
|
|
"\t-d interface interface name to be detached\n"
|
|
"\t-a interface interface name to be attached\n"
|
|
"\t-h interface interface name to be attached with the host stack\n"
|
|
"\t-n interface interface name to be created\n"
|
|
"\t-r interface interface name to be deleted\n"
|
|
"\t-l vale-port show bridge and port indices\n"
|
|
"\t-C string ring/slot setting of an interface creating by -n\n"
|
|
"\t-p interface start polling. Additional -C x,y,z configures\n"
|
|
"\t\t x: 0 (REG_ALL_NIC) or 1 (REG_ONE_NIC),\n"
|
|
"\t\t y: CPU core id for ALL_NIC and core/ring for ONE_NIC\n"
|
|
"\t\t z: (ONE_NIC only) num of total cores/rings\n"
|
|
"\t-P interface stop polling\n"
|
|
"\t-m memid to use when creating a new interface\n"
|
|
"\t-v increase verbosity\n"
|
|
"with no arguments: list all existing vale ports\n");
|
|
exit(errcode);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int ch;
|
|
struct args a = {
|
|
.name = NULL,
|
|
.config = NULL,
|
|
.mem_id = NULL,
|
|
.nr_reqtype = 0,
|
|
.nr_mode = NR_REG_ALL_NIC,
|
|
};
|
|
|
|
while ((ch = getopt(argc, argv, "d:a:h:g:l:n:r:C:p:P:m:v")) != -1) {
|
|
switch (ch) {
|
|
default:
|
|
fprintf(stderr, "bad option %c %s", ch, optarg);
|
|
usage(1);
|
|
break;
|
|
case 'd':
|
|
a.nr_reqtype = NETMAP_REQ_VALE_DETACH;
|
|
a.name = optarg;
|
|
break;
|
|
case 'a':
|
|
a.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
|
|
a.nr_mode = NR_REG_ALL_NIC;
|
|
a.name = optarg;
|
|
break;
|
|
case 'h':
|
|
a.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
|
|
a.nr_mode = NR_REG_NIC_SW;
|
|
a.name = optarg;
|
|
break;
|
|
case 'n':
|
|
a.nr_reqtype = NETMAP_REQ_VALE_NEWIF;
|
|
a.name = optarg;
|
|
break;
|
|
case 'r':
|
|
a.nr_reqtype = NETMAP_REQ_VALE_DELIF;
|
|
a.name = optarg;
|
|
break;
|
|
case 'g':
|
|
a.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
|
|
a.name = optarg;
|
|
break;
|
|
case 'l':
|
|
a.nr_reqtype = NETMAP_REQ_VALE_LIST;
|
|
a.name = optarg;
|
|
if (strncmp(a.name, NM_BDG_NAME, strlen(NM_BDG_NAME))) {
|
|
fprintf(stderr, "invalid vale port name: '%s'\n", a.name);
|
|
usage(1);
|
|
}
|
|
break;
|
|
case 'C':
|
|
a.config = optarg;
|
|
break;
|
|
case 'p':
|
|
a.nr_reqtype = NETMAP_REQ_VALE_POLLING_ENABLE;
|
|
a.name = optarg;
|
|
break;
|
|
case 'P':
|
|
a.nr_reqtype = NETMAP_REQ_VALE_POLLING_DISABLE;
|
|
a.name = optarg;
|
|
break;
|
|
case 'm':
|
|
a.mem_id = optarg;
|
|
break;
|
|
case 'v':
|
|
verbose++;
|
|
break;
|
|
}
|
|
}
|
|
if (optind != argc) {
|
|
usage(1);
|
|
}
|
|
if (argc == 1) {
|
|
a.nr_reqtype = NETMAP_REQ_VALE_LIST;
|
|
a.name = NULL;
|
|
}
|
|
if (!a.nr_reqtype) {
|
|
usage(1);
|
|
}
|
|
return bdg_ctl(&a);
|
|
}
|