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