1 /* 2 * Copyright (C) 2013-2014 Michio Honda. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 /* $FreeBSD$ */ 27 28 #define LIBNETMAP_NOTHREADSAFE 29 #include <libnetmap.h> 30 31 #include <errno.h> 32 #include <stdio.h> 33 #include <inttypes.h> /* PRI* macros */ 34 #include <string.h> /* strcmp */ 35 #include <fcntl.h> /* open */ 36 #include <unistd.h> /* close */ 37 #include <sys/ioctl.h> /* ioctl */ 38 #include <sys/param.h> 39 #include <sys/socket.h> /* apple needs sockaddr */ 40 #include <net/if.h> /* ifreq */ 41 #include <libgen.h> /* basename */ 42 #include <stdlib.h> /* atoi, free */ 43 44 int verbose; 45 46 struct args { 47 const char *name; 48 const char *config; 49 const char *mem_id; 50 51 uint16_t nr_reqtype; 52 uint32_t nr_mode; 53 }; 54 55 static void 56 dump_port_info(struct nmreq_port_info_get *v) 57 { 58 printf("memsize: %"PRIu64"\n", v->nr_memsize); 59 printf("tx_slots: %"PRIu32"\n", v->nr_tx_slots); 60 printf("rx_slots: %"PRIu32"\n", v->nr_rx_slots); 61 printf("tx_rings: %"PRIu16"\n", v->nr_tx_rings); 62 printf("rx_rings %"PRIu16"\n", v->nr_rx_rings); 63 printf("mem_id: %"PRIu16"\n", v->nr_mem_id); 64 } 65 66 static void 67 dump_newif(struct nmreq_vale_newif *v) 68 { 69 printf("tx_slots: %"PRIu32"\n", v->nr_tx_slots); 70 printf("rx_slots: %"PRIu32"\n", v->nr_rx_slots); 71 printf("tx_rings: %"PRIu16"\n", v->nr_tx_rings); 72 printf("rx_ring: %"PRIu16"\n", v->nr_rx_rings); 73 printf("mem_id: %"PRIu16"\n", v->nr_mem_id); 74 } 75 76 static void 77 dump_vale_list(struct nmreq_vale_list *v) 78 { 79 printf("bridge_idx: %"PRIu16"\n", v->nr_bridge_idx); 80 printf("port_idx: %"PRIu16"\n", v->nr_port_idx); 81 } 82 83 84 static void 85 parse_ring_config(const char* conf, 86 uint32_t *nr_tx_slots, 87 uint32_t *nr_rx_slots, 88 uint16_t *nr_tx_rings, 89 uint16_t *nr_rx_rings) 90 { 91 char *w, *tok; 92 int i, v; 93 94 *nr_tx_rings = *nr_rx_rings = 0; 95 *nr_tx_slots = *nr_rx_slots = 0; 96 if (conf == NULL || ! *conf) 97 return; 98 w = strdup(conf); 99 for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) { 100 v = atoi(tok); 101 switch (i) { 102 case 0: 103 *nr_tx_slots = *nr_rx_slots = v; 104 break; 105 case 1: 106 *nr_rx_slots = v; 107 break; 108 case 2: 109 *nr_tx_rings = *nr_rx_rings = v; 110 break; 111 case 3: 112 *nr_rx_rings = v; 113 break; 114 default: 115 fprintf(stderr, "ignored config: %s", tok); 116 break; 117 } 118 } 119 ND("txr %d txd %d rxr %d rxd %d", 120 *nr_tx_rings, *nr_tx_slots, 121 *nr_rx_rings, *nr_rx_slots); 122 free(w); 123 } 124 125 static int 126 parse_poll_config(const char *conf, struct nmreq_vale_polling *v) 127 { 128 char *w, *tok; 129 int i, p; 130 131 if (conf == NULL || ! *conf) { 132 fprintf(stderr, "invalid null/empty config\n"); 133 return -1; 134 } 135 w = strdup(conf); 136 for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) { 137 p = atoi(tok); 138 switch (i) { 139 case 0: 140 v->nr_mode = p ? NETMAP_POLLING_MODE_MULTI_CPU : 141 NETMAP_POLLING_MODE_SINGLE_CPU; 142 break; 143 case 1: 144 v->nr_first_cpu_id = p; 145 break; 146 case 2: 147 if (v->nr_mode != NETMAP_POLLING_MODE_MULTI_CPU) { 148 fprintf(stderr, "too many numbers in '%s'\n", conf); 149 return -1; 150 } 151 v->nr_num_polling_cpus = p; 152 break; 153 case 3: 154 fprintf(stderr, "too many numbers in '%s'\n", conf); 155 return -1; 156 } 157 } 158 free(w); 159 return 0; 160 } 161 162 static int32_t 163 parse_mem_id(const char *mem_id) 164 { 165 int32_t id; 166 167 if (mem_id == NULL) 168 return 0; 169 if (isdigit(*mem_id)) 170 return atoi(mem_id); 171 id = nmreq_get_mem_id(&mem_id, nmctx_get()); 172 if (id == 0) { 173 fprintf(stderr, "invalid format in '-m %s' (missing 'netmap:'?)\n", mem_id); 174 return -1; 175 } 176 return id; 177 } 178 179 static int 180 list_all(int fd, struct nmreq_header *hdr) 181 { 182 int error; 183 struct nmreq_vale_list *vale_list = 184 (struct nmreq_vale_list *)(uintptr_t)hdr->nr_body; 185 186 for (;;) { 187 hdr->nr_name[0] = '\0'; 188 error = ioctl(fd, NIOCCTRL, hdr); 189 if (error < 0) { 190 if (errno == ENOENT) 191 break; 192 193 fprintf(stderr, "failed to list all: %s\n", strerror(errno)); 194 return 1; 195 } 196 printf("%s bridge_idx %"PRIu16" port_idx %"PRIu32"\n", hdr->nr_name, 197 vale_list->nr_bridge_idx, vale_list->nr_port_idx); 198 vale_list->nr_port_idx++; 199 } 200 return 1; 201 } 202 203 static int 204 bdg_ctl(struct args *a) 205 { 206 struct nmreq_header hdr; 207 struct nmreq_vale_attach vale_attach; 208 struct nmreq_vale_detach vale_detach; 209 struct nmreq_vale_newif vale_newif; 210 struct nmreq_vale_list vale_list; 211 struct nmreq_vale_polling vale_polling; 212 struct nmreq_port_info_get port_info_get; 213 int error = 0; 214 int fd; 215 int32_t mem_id; 216 const char *action = NULL; 217 218 fd = open("/dev/netmap", O_RDWR); 219 if (fd == -1) { 220 perror("/dev/netmap"); 221 return 1; 222 } 223 224 bzero(&hdr, sizeof(hdr)); 225 hdr.nr_version = NETMAP_API; 226 if (a->name != NULL) { /* might be NULL */ 227 strncpy(hdr.nr_name, a->name, NETMAP_REQ_IFNAMSIZ - 1); 228 hdr.nr_name[NETMAP_REQ_IFNAMSIZ - 1] = '\0'; 229 } 230 hdr.nr_reqtype = a->nr_reqtype; 231 232 switch (a->nr_reqtype) { 233 case NETMAP_REQ_VALE_DELIF: 234 /* no body */ 235 action = "remove"; 236 break; 237 238 case NETMAP_REQ_VALE_NEWIF: 239 memset(&vale_newif, 0, sizeof(vale_newif)); 240 hdr.nr_body = (uintptr_t)&vale_newif; 241 parse_ring_config(a->config, 242 &vale_newif.nr_tx_slots, 243 &vale_newif.nr_rx_slots, 244 &vale_newif.nr_tx_rings, 245 &vale_newif.nr_rx_rings); 246 mem_id = parse_mem_id(a->mem_id); 247 if (mem_id < 0) 248 return 1; 249 vale_newif.nr_mem_id = mem_id; 250 action = "create"; 251 break; 252 253 case NETMAP_REQ_VALE_ATTACH: 254 memset(&vale_attach, 0, sizeof(vale_attach)); 255 hdr.nr_body = (uintptr_t)&vale_attach; 256 vale_attach.reg.nr_mode = a->nr_mode; 257 parse_ring_config(a->config, 258 &vale_attach.reg.nr_tx_slots, 259 &vale_attach.reg.nr_rx_slots, 260 &vale_attach.reg.nr_tx_rings, 261 &vale_attach.reg.nr_rx_rings); 262 mem_id = parse_mem_id(a->mem_id); 263 if (mem_id < 0) 264 return 1; 265 vale_attach.reg.nr_mem_id = mem_id; 266 action = "attach"; 267 break; 268 269 case NETMAP_REQ_VALE_DETACH: 270 memset(&vale_detach, 0, sizeof(vale_detach)); 271 hdr.nr_body = (uintptr_t)&vale_detach; 272 action = "detach"; 273 break; 274 275 case NETMAP_REQ_VALE_LIST: 276 memset(&vale_list, 0, sizeof(vale_list)); 277 hdr.nr_body = (uintptr_t)&vale_list; 278 if (a->name == NULL) { 279 return list_all(fd, &hdr); 280 } 281 action = "list"; 282 break; 283 284 case NETMAP_REQ_VALE_POLLING_ENABLE: 285 action = "enable polling on"; 286 /* fall through */ 287 case NETMAP_REQ_VALE_POLLING_DISABLE: 288 memset(&vale_polling, 0, sizeof(vale_polling)); 289 hdr.nr_body = (uintptr_t)&vale_polling; 290 parse_poll_config(a->config, &vale_polling); 291 if (action == NULL) 292 action ="disable polling on"; 293 break; 294 295 case NETMAP_REQ_PORT_INFO_GET: 296 memset(&port_info_get, 0, sizeof(port_info_get)); 297 hdr.nr_body = (uintptr_t)&port_info_get; 298 action = "obtain info for"; 299 break; 300 } 301 error = ioctl(fd, NIOCCTRL, &hdr); 302 if (error < 0) { 303 fprintf(stderr, "failed to %s %s: %s\n", 304 action, a->name, strerror(errno)); 305 return 1; 306 } 307 switch (hdr.nr_reqtype) { 308 case NETMAP_REQ_VALE_NEWIF: 309 if (verbose) { 310 dump_newif(&vale_newif); 311 } 312 break; 313 314 case NETMAP_REQ_VALE_ATTACH: 315 if (verbose) { 316 printf("port_index: %"PRIu32"\n", vale_attach.port_index); 317 } 318 break; 319 320 case NETMAP_REQ_VALE_DETACH: 321 if (verbose) { 322 printf("port_index: %"PRIu32"\n", vale_detach.port_index); 323 } 324 break; 325 326 case NETMAP_REQ_VALE_LIST: 327 dump_vale_list(&vale_list); 328 break; 329 330 case NETMAP_REQ_PORT_INFO_GET: 331 dump_port_info(&port_info_get); 332 break; 333 } 334 close(fd); 335 return error; 336 } 337 338 static void 339 usage(int errcode) 340 { 341 fprintf(stderr, 342 "Usage:\n" 343 "vale-ctl [arguments]\n" 344 "\t-g interface interface name to get info\n" 345 "\t-d interface interface name to be detached\n" 346 "\t-a interface interface name to be attached\n" 347 "\t-h interface interface name to be attached with the host stack\n" 348 "\t-n interface interface name to be created\n" 349 "\t-r interface interface name to be deleted\n" 350 "\t-l vale-port show bridge and port indices\n" 351 "\t-C string ring/slot setting of an interface creating by -n\n" 352 "\t-p interface start polling. Additional -C x,y,z configures\n" 353 "\t\t x: 0 (REG_ALL_NIC) or 1 (REG_ONE_NIC),\n" 354 "\t\t y: CPU core id for ALL_NIC and core/ring for ONE_NIC\n" 355 "\t\t z: (ONE_NIC only) num of total cores/rings\n" 356 "\t-P interface stop polling\n" 357 "\t-m memid to use when creating a new interface\n" 358 "\t-v increase verbosity\n" 359 "with no arguments: list all existing vale ports\n"); 360 exit(errcode); 361 } 362 363 int 364 main(int argc, char *argv[]) 365 { 366 int ch; 367 struct args a = { 368 .name = NULL, 369 .config = NULL, 370 .mem_id = NULL, 371 .nr_reqtype = 0, 372 .nr_mode = NR_REG_ALL_NIC, 373 }; 374 375 while ((ch = getopt(argc, argv, "d:a:h:g:l:n:r:C:p:P:m:v")) != -1) { 376 switch (ch) { 377 default: 378 fprintf(stderr, "bad option %c %s", ch, optarg); 379 usage(1); 380 break; 381 case 'd': 382 a.nr_reqtype = NETMAP_REQ_VALE_DETACH; 383 a.name = optarg; 384 break; 385 case 'a': 386 a.nr_reqtype = NETMAP_REQ_VALE_ATTACH; 387 a.nr_mode = NR_REG_ALL_NIC; 388 a.name = optarg; 389 break; 390 case 'h': 391 a.nr_reqtype = NETMAP_REQ_VALE_ATTACH; 392 a.nr_mode = NR_REG_NIC_SW; 393 a.name = optarg; 394 break; 395 case 'n': 396 a.nr_reqtype = NETMAP_REQ_VALE_NEWIF; 397 a.name = optarg; 398 break; 399 case 'r': 400 a.nr_reqtype = NETMAP_REQ_VALE_DELIF; 401 a.name = optarg; 402 break; 403 case 'g': 404 a.nr_reqtype = NETMAP_REQ_PORT_INFO_GET; 405 a.name = optarg; 406 break; 407 case 'l': 408 a.nr_reqtype = NETMAP_REQ_VALE_LIST; 409 a.name = optarg; 410 if (strncmp(a.name, NM_BDG_NAME, strlen(NM_BDG_NAME))) { 411 fprintf(stderr, "invalid vale port name: '%s'\n", a.name); 412 usage(1); 413 } 414 break; 415 case 'C': 416 a.config = optarg; 417 break; 418 case 'p': 419 a.nr_reqtype = NETMAP_REQ_VALE_POLLING_ENABLE; 420 a.name = optarg; 421 break; 422 case 'P': 423 a.nr_reqtype = NETMAP_REQ_VALE_POLLING_DISABLE; 424 a.name = optarg; 425 break; 426 case 'm': 427 a.mem_id = optarg; 428 break; 429 case 'v': 430 verbose++; 431 break; 432 } 433 } 434 if (optind != argc) { 435 usage(1); 436 } 437 if (argc == 1) { 438 a.nr_reqtype = NETMAP_REQ_VALE_LIST; 439 a.name = NULL; 440 } 441 if (!a.nr_reqtype) { 442 usage(1); 443 } 444 return bdg_ctl(&a); 445 } 446