1 /* $FreeBSD$ */ 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <sys/ioctl.h> 5 #include <sys/mman.h> 6 #include <ctype.h> 7 #include <fcntl.h> 8 #include <inttypes.h> 9 #include <stdlib.h> 10 #include <stdio.h> 11 #include <stdarg.h> 12 #include <string.h> 13 #include <unistd.h> 14 #include <errno.h> 15 16 //#define NMREQ_DEBUG 17 #ifdef NMREQ_DEBUG 18 #define NETMAP_WITH_LIBS 19 #define ED(...) D(__VA_ARGS__) 20 #else 21 #define ED(...) 22 /* an identifier is a possibly empty sequence of alphanum characters and 23 * underscores 24 */ 25 static int 26 nm_is_identifier(const char *s, const char *e) 27 { 28 for (; s != e; s++) { 29 if (!isalnum(*s) && *s != '_') { 30 return 0; 31 } 32 } 33 34 return 1; 35 } 36 #endif /* NMREQ_DEBUG */ 37 38 #include <net/netmap_user.h> 39 #define LIBNETMAP_NOTHREADSAFE 40 #include "libnetmap.h" 41 42 void 43 nmreq_push_option(struct nmreq_header *h, struct nmreq_option *o) 44 { 45 o->nro_next = h->nr_options; 46 h->nr_options = (uintptr_t)o; 47 } 48 49 struct nmreq_prefix { 50 const char *prefix; /* the constant part of the prefix */ 51 size_t len; /* its strlen() */ 52 uint32_t flags; 53 #define NR_P_ID (1U << 0) /* whether an identifier is needed */ 54 #define NR_P_SKIP (1U << 1) /* whether the scope must be passed to netmap */ 55 #define NR_P_EMPTYID (1U << 2) /* whether an empty identifier is allowed */ 56 }; 57 58 #define declprefix(prefix, flags) { (prefix), (sizeof(prefix) - 1), (flags) } 59 60 static struct nmreq_prefix nmreq_prefixes[] = { 61 declprefix("netmap", NR_P_SKIP), 62 declprefix(NM_BDG_NAME, NR_P_ID|NR_P_EMPTYID), 63 { NULL } /* terminate the list */ 64 }; 65 66 void 67 nmreq_header_init(struct nmreq_header *h, uint16_t reqtype, void *body) 68 { 69 memset(h, 0, sizeof(*h)); 70 h->nr_version = NETMAP_API; 71 h->nr_reqtype = reqtype; 72 h->nr_body = (uintptr_t)body; 73 } 74 75 int 76 nmreq_header_decode(const char **pifname, struct nmreq_header *h, struct nmctx *ctx) 77 { 78 const char *scan = NULL; 79 const char *vpname = NULL; 80 const char *pipesep = NULL; 81 u_int namelen; 82 const char *ifname = *pifname; 83 struct nmreq_prefix *p; 84 85 scan = ifname; 86 for (p = nmreq_prefixes; p->prefix != NULL; p++) { 87 if (!strncmp(scan, p->prefix, p->len)) 88 break; 89 } 90 if (p->prefix == NULL) { 91 nmctx_ferror(ctx, "%s: invalid request, prefix unknown or missing", *pifname); 92 goto fail; 93 } 94 scan += p->len; 95 96 vpname = index(scan, ':'); 97 if (vpname == NULL) { 98 nmctx_ferror(ctx, "%s: missing ':'", ifname); 99 goto fail; 100 } 101 if (vpname != scan) { 102 /* there is an identifier, can we accept it? */ 103 if (!(p->flags & NR_P_ID)) { 104 nmctx_ferror(ctx, "%s: no identifier allowed between '%s' and ':'", *pifname, p->prefix); 105 goto fail; 106 } 107 108 if (!nm_is_identifier(scan, vpname)) { 109 nmctx_ferror(ctx, "%s: invalid identifier '%.*s'", *pifname, vpname - scan, scan); 110 goto fail; 111 } 112 } else { 113 if ((p->flags & NR_P_ID) && !(p->flags & NR_P_EMPTYID)) { 114 nmctx_ferror(ctx, "%s: identifier is missing between '%s' and ':'", *pifname, p->prefix); 115 goto fail; 116 } 117 } 118 ++vpname; /* skip the colon */ 119 if (p->flags & NR_P_SKIP) 120 ifname = vpname; 121 scan = vpname; 122 123 /* scan for a separator */ 124 for (; *scan && !index("-*^/@", *scan); scan++) 125 ; 126 127 /* search for possible pipe indicators */ 128 for (pipesep = vpname; pipesep != scan && !index("{}", *pipesep); pipesep++) 129 ; 130 131 if (!nm_is_identifier(vpname, pipesep)) { 132 nmctx_ferror(ctx, "%s: invalid port name '%.*s'", *pifname, 133 pipesep - vpname, vpname); 134 goto fail; 135 } 136 if (pipesep != scan) { 137 pipesep++; 138 if (*pipesep == '\0') { 139 nmctx_ferror(ctx, "%s: invalid empty pipe name", *pifname); 140 goto fail; 141 } 142 if (!nm_is_identifier(pipesep, scan)) { 143 nmctx_ferror(ctx, "%s: invalid pipe name '%.*s'", *pifname, scan - pipesep, pipesep); 144 goto fail; 145 } 146 } 147 148 namelen = scan - ifname; 149 if (namelen >= sizeof(h->nr_name)) { 150 nmctx_ferror(ctx, "name '%.*s' too long", namelen, ifname); 151 goto fail; 152 } 153 if (namelen == 0) { 154 nmctx_ferror(ctx, "%s: invalid empty port name", *pifname); 155 goto fail; 156 } 157 158 /* fill the header */ 159 memcpy(h->nr_name, ifname, namelen); 160 h->nr_name[namelen] = '\0'; 161 ED("name %s", h->nr_name); 162 163 *pifname = scan; 164 165 return 0; 166 fail: 167 errno = EINVAL; 168 return -1; 169 } 170 171 172 /* 173 * 0 not recognized 174 * -1 error 175 * >= 0 mem_id 176 */ 177 int32_t 178 nmreq_get_mem_id(const char **pifname, struct nmctx *ctx) 179 { 180 int fd = -1; 181 struct nmreq_header gh; 182 struct nmreq_port_info_get gb; 183 const char *ifname; 184 185 errno = 0; 186 ifname = *pifname; 187 188 if (ifname == NULL) 189 goto fail; 190 191 /* try to look for a netmap port with this name */ 192 fd = open("/dev/netmap", O_RDWR); 193 if (fd < 0) { 194 nmctx_ferror(ctx, "cannot open /dev/netmap: %s", strerror(errno)); 195 goto fail; 196 } 197 nmreq_header_init(&gh, NETMAP_REQ_PORT_INFO_GET, &gb); 198 if (nmreq_header_decode(&ifname, &gh, ctx) < 0) { 199 goto fail; 200 } 201 memset(&gb, 0, sizeof(gb)); 202 if (ioctl(fd, NIOCCTRL, &gh) < 0) { 203 nmctx_ferror(ctx, "cannot get info for '%s': %s", *pifname, strerror(errno)); 204 goto fail; 205 } 206 *pifname = ifname; 207 close(fd); 208 return gb.nr_mem_id; 209 210 fail: 211 if (fd >= 0) 212 close(fd); 213 if (!errno) 214 errno = EINVAL; 215 return -1; 216 } 217 218 219 int 220 nmreq_register_decode(const char **pifname, struct nmreq_register *r, struct nmctx *ctx) 221 { 222 enum { P_START, P_RNGSFXOK, P_GETNUM, P_FLAGS, P_FLAGSOK, P_MEMID, P_ONESW } p_state; 223 long num; 224 const char *scan = *pifname; 225 uint32_t nr_mode; 226 uint16_t nr_mem_id; 227 uint16_t nr_ringid; 228 uint64_t nr_flags; 229 230 /* fill the request */ 231 232 p_state = P_START; 233 /* defaults */ 234 nr_mode = NR_REG_ALL_NIC; /* default for no suffix */ 235 nr_mem_id = r->nr_mem_id; /* if non-zero, further updates are disabled */ 236 nr_ringid = 0; 237 nr_flags = 0; 238 while (*scan) { 239 switch (p_state) { 240 case P_START: 241 switch (*scan) { 242 case '^': /* only SW ring */ 243 nr_mode = NR_REG_SW; 244 p_state = P_ONESW; 245 break; 246 case '*': /* NIC and SW */ 247 nr_mode = NR_REG_NIC_SW; 248 p_state = P_RNGSFXOK; 249 break; 250 case '-': /* one NIC ring pair */ 251 nr_mode = NR_REG_ONE_NIC; 252 p_state = P_GETNUM; 253 break; 254 case '/': /* start of flags */ 255 p_state = P_FLAGS; 256 break; 257 case '@': /* start of memid */ 258 p_state = P_MEMID; 259 break; 260 default: 261 nmctx_ferror(ctx, "unknown modifier: '%c'", *scan); 262 goto fail; 263 } 264 scan++; 265 break; 266 case P_RNGSFXOK: 267 switch (*scan) { 268 case '/': 269 p_state = P_FLAGS; 270 break; 271 case '@': 272 p_state = P_MEMID; 273 break; 274 default: 275 nmctx_ferror(ctx, "unexpected character: '%c'", *scan); 276 goto fail; 277 } 278 scan++; 279 break; 280 case P_GETNUM: 281 if (!isdigit(*scan)) { 282 nmctx_ferror(ctx, "got '%s' while expecting a number", scan); 283 goto fail; 284 } 285 num = strtol(scan, (char **)&scan, 10); 286 if (num < 0 || num >= NETMAP_RING_MASK) { 287 nmctx_ferror(ctx, "'%ld' out of range [0, %d)", 288 num, NETMAP_RING_MASK); 289 goto fail; 290 } 291 nr_ringid = num & NETMAP_RING_MASK; 292 p_state = P_RNGSFXOK; 293 break; 294 case P_FLAGS: 295 case P_FLAGSOK: 296 switch (*scan) { 297 case '@': 298 p_state = P_MEMID; 299 scan++; 300 continue; 301 case 'x': 302 nr_flags |= NR_EXCLUSIVE; 303 break; 304 case 'z': 305 nr_flags |= NR_ZCOPY_MON; 306 break; 307 case 't': 308 nr_flags |= NR_MONITOR_TX; 309 break; 310 case 'r': 311 nr_flags |= NR_MONITOR_RX; 312 break; 313 case 'R': 314 nr_flags |= NR_RX_RINGS_ONLY; 315 break; 316 case 'T': 317 nr_flags |= NR_TX_RINGS_ONLY; 318 break; 319 default: 320 nmctx_ferror(ctx, "unrecognized flag: '%c'", *scan); 321 goto fail; 322 } 323 scan++; 324 p_state = P_FLAGSOK; 325 break; 326 case P_MEMID: 327 if (!isdigit(*scan)) { 328 scan--; /* escape to options */ 329 goto out; 330 } 331 num = strtol(scan, (char **)&scan, 10); 332 if (num <= 0) { 333 nmctx_ferror(ctx, "invalid mem_id: '%ld'", num); 334 goto fail; 335 } 336 if (nr_mem_id && nr_mem_id != num) { 337 nmctx_ferror(ctx, "invalid setting of mem_id to %ld (already set to %"PRIu16")", num, nr_mem_id); 338 goto fail; 339 } 340 nr_mem_id = num; 341 p_state = P_RNGSFXOK; 342 break; 343 case P_ONESW: 344 if (!isdigit(*scan)) { 345 p_state = P_RNGSFXOK; 346 } else { 347 nr_mode = NR_REG_ONE_SW; 348 p_state = P_GETNUM; 349 } 350 break; 351 } 352 } 353 if (p_state == P_MEMID && !*scan) { 354 nmctx_ferror(ctx, "invalid empty mem_id"); 355 goto fail; 356 } 357 if (p_state != P_START && p_state != P_RNGSFXOK && 358 p_state != P_FLAGSOK && p_state != P_MEMID && p_state != P_ONESW) { 359 nmctx_ferror(ctx, "unexpected end of request"); 360 goto fail; 361 } 362 out: 363 ED("flags: %s %s %s %s %s %s", 364 (nr_flags & NR_EXCLUSIVE) ? "EXCLUSIVE" : "", 365 (nr_flags & NR_ZCOPY_MON) ? "ZCOPY_MON" : "", 366 (nr_flags & NR_MONITOR_TX) ? "MONITOR_TX" : "", 367 (nr_flags & NR_MONITOR_RX) ? "MONITOR_RX" : "", 368 (nr_flags & NR_RX_RINGS_ONLY) ? "RX_RINGS_ONLY" : "", 369 (nr_flags & NR_TX_RINGS_ONLY) ? "TX_RINGS_ONLY" : ""); 370 r->nr_mode = nr_mode; 371 r->nr_ringid = nr_ringid; 372 r->nr_flags = nr_flags; 373 r->nr_mem_id = nr_mem_id; 374 *pifname = scan; 375 return 0; 376 377 fail: 378 if (!errno) 379 errno = EINVAL; 380 return -1; 381 } 382 383 384 static int 385 nmreq_option_parsekeys(const char *prefix, char *body, struct nmreq_opt_parser *p, 386 struct nmreq_parse_ctx *pctx) 387 { 388 char *scan; 389 char delim1; 390 struct nmreq_opt_key *k; 391 392 scan = body; 393 delim1 = *scan; 394 while (delim1 != '\0') { 395 char *key, *value; 396 char delim; 397 size_t vlen; 398 399 key = scan; 400 for ( scan++; *scan != '\0' && *scan != '=' && *scan != ','; scan++) { 401 if (*scan == '-') 402 *scan = '_'; 403 } 404 delim = *scan; 405 *scan = '\0'; 406 scan++; 407 for (k = p->keys; (k - p->keys) < NMREQ_OPT_MAXKEYS && k->key != NULL; 408 k++) { 409 if (!strcmp(k->key, key)) 410 goto found; 411 412 } 413 nmctx_ferror(pctx->ctx, "unknown key: '%s'", key); 414 errno = EINVAL; 415 return -1; 416 found: 417 if (pctx->keys[k->id] != NULL) { 418 nmctx_ferror(pctx->ctx, "option '%s': duplicate key '%s', already set to '%s'", 419 prefix, key, pctx->keys[k->id]); 420 errno = EINVAL; 421 return -1; 422 } 423 value = scan; 424 for ( ; *scan != '\0' && *scan != ','; scan++) 425 ; 426 delim1 = *scan; 427 *scan = '\0'; 428 vlen = scan - value; 429 scan++; 430 if (delim == '=') { 431 pctx->keys[k->id] = (vlen ? value : NULL); 432 } else { 433 if (!(k->flags & NMREQ_OPTK_ALLOWEMPTY)) { 434 nmctx_ferror(pctx->ctx, "option '%s': missing '=value' for key '%s'", 435 prefix, key); 436 errno = EINVAL; 437 return -1; 438 } 439 pctx->keys[k->id] = key; 440 } 441 } 442 /* now check that all no-default keys have been assigned */ 443 for (k = p->keys; (k - p->keys) < NMREQ_OPT_MAXKEYS && k->key != NULL; k++) { 444 if ((k->flags & NMREQ_OPTK_MUSTSET) && pctx->keys[k->id] == NULL) { 445 nmctx_ferror(pctx->ctx, "option '%s': mandatory key '%s' not assigned", 446 prefix, k->key); 447 errno = EINVAL; 448 return -1; 449 } 450 } 451 return 0; 452 } 453 454 455 static int 456 nmreq_option_decode1(char *opt, struct nmreq_opt_parser *parsers, 457 void *token, struct nmctx *ctx) 458 { 459 struct nmreq_opt_parser *p; 460 const char *prefix; 461 char *scan; 462 char delim; 463 struct nmreq_parse_ctx pctx; 464 int i; 465 466 prefix = opt; 467 /* find the delimiter */ 468 for (scan = opt; *scan != '\0' && *scan != ':' && *scan != '='; scan++) 469 ; 470 delim = *scan; 471 *scan = '\0'; 472 scan++; 473 /* find the prefix */ 474 for (p = parsers; p != NULL; p = p->next) { 475 if (!strcmp(prefix, p->prefix)) 476 break; 477 } 478 if (p == NULL) { 479 nmctx_ferror(ctx, "unknown option: '%s'", prefix); 480 errno = EINVAL; 481 return -1; 482 } 483 if (p->flags & NMREQ_OPTF_DISABLED) { 484 nmctx_ferror(ctx, "option '%s' is not supported", prefix); 485 errno = EOPNOTSUPP; 486 return -1; 487 } 488 /* prepare the parse context */ 489 pctx.ctx = ctx; 490 pctx.token = token; 491 for (i = 0; i < NMREQ_OPT_MAXKEYS; i++) 492 pctx.keys[i] = NULL; 493 switch (delim) { 494 case '\0': 495 /* no body */ 496 if (!(p->flags & NMREQ_OPTF_ALLOWEMPTY)) { 497 nmctx_ferror(ctx, "syntax error: missing body after '%s'", 498 prefix); 499 errno = EINVAL; 500 return -1; 501 } 502 break; 503 case '=': /* the body goes to the default option key, if any */ 504 if (p->default_key < 0 || p->default_key >= NMREQ_OPT_MAXKEYS) { 505 nmctx_ferror(ctx, "syntax error: '=' not valid after '%s'", 506 prefix); 507 errno = EINVAL; 508 return -1; 509 } 510 if (*scan == '\0') { 511 nmctx_ferror(ctx, "missing value for option '%s'", prefix); 512 errno = EINVAL; 513 return -1; 514 } 515 pctx.keys[p->default_key] = scan; 516 break; 517 case ':': /* parse 'key=value' strings */ 518 if (nmreq_option_parsekeys(prefix, scan, p, &pctx) < 0) 519 return -1; 520 break; 521 } 522 return p->parse(&pctx); 523 } 524 525 int 526 nmreq_options_decode(const char *opt, struct nmreq_opt_parser parsers[], 527 void *token, struct nmctx *ctx) 528 { 529 const char *scan, *opt1; 530 char *w; 531 size_t len; 532 int ret; 533 534 if (*opt == '\0') 535 return 0; /* empty list, OK */ 536 537 if (*opt != '@') { 538 nmctx_ferror(ctx, "option list does not start with '@'"); 539 errno = EINVAL; 540 return -1; 541 } 542 543 scan = opt; 544 do { 545 scan++; /* skip the plus */ 546 opt1 = scan; /* start of option */ 547 /* find the end of the option */ 548 for ( ; *scan != '\0' && *scan != '@'; scan++) 549 ; 550 len = scan - opt1; 551 if (len == 0) { 552 nmctx_ferror(ctx, "invalid empty option"); 553 errno = EINVAL; 554 return -1; 555 } 556 w = nmctx_malloc(ctx, len + 1); 557 if (w == NULL) { 558 nmctx_ferror(ctx, "out of memory"); 559 errno = ENOMEM; 560 return -1; 561 } 562 memcpy(w, opt1, len); 563 w[len] = '\0'; 564 ret = nmreq_option_decode1(w, parsers, token, ctx); 565 nmctx_free(ctx, w); 566 if (ret < 0) 567 return -1; 568 } while (*scan != '\0'); 569 570 return 0; 571 } 572 573 struct nmreq_option * 574 nmreq_find_option(struct nmreq_header *h, uint32_t t) 575 { 576 struct nmreq_option *o; 577 578 for (o = (struct nmreq_option *)h->nr_options; o != NULL; 579 o = (struct nmreq_option *)o->nro_next) { 580 if (o->nro_reqtype == t) 581 break; 582 } 583 return o; 584 } 585 586 void 587 nmreq_remove_option(struct nmreq_header *h, struct nmreq_option *o) 588 { 589 struct nmreq_option **nmo; 590 591 for (nmo = (struct nmreq_option **)&h->nr_options; *nmo != NULL; 592 nmo = (struct nmreq_option **)&(*nmo)->nro_next) { 593 if (*nmo == o) { 594 *((uint64_t *)(*nmo)) = o->nro_next; 595 o->nro_next = (uint64_t)(uintptr_t)NULL; 596 break; 597 } 598 } 599 } 600 601 void 602 nmreq_free_options(struct nmreq_header *h) 603 { 604 struct nmreq_option *o, *next; 605 606 for (o = (struct nmreq_option *)h->nr_options; o != NULL; o = next) { 607 next = (struct nmreq_option *)o->nro_next; 608 free(o); 609 } 610 } 611 612 const char* 613 nmreq_option_name(uint32_t nro_reqtype) 614 { 615 switch (nro_reqtype) { 616 case NETMAP_REQ_OPT_EXTMEM: 617 return "extmem"; 618 case NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS: 619 return "sync-kloop-eventfds"; 620 case NETMAP_REQ_OPT_CSB: 621 return "csb"; 622 case NETMAP_REQ_OPT_SYNC_KLOOP_MODE: 623 return "sync-kloop-mode"; 624 default: 625 return "unknown"; 626 } 627 } 628 629 #if 0 630 #include <inttypes.h> 631 static void 632 nmreq_dump(struct nmport_d *d) 633 { 634 printf("header:\n"); 635 printf(" nr_version: %"PRIu16"\n", d->hdr.nr_version); 636 printf(" nr_reqtype: %"PRIu16"\n", d->hdr.nr_reqtype); 637 printf(" nr_reserved: %"PRIu32"\n", d->hdr.nr_reserved); 638 printf(" nr_name: %s\n", d->hdr.nr_name); 639 printf(" nr_options: %lx\n", (unsigned long)d->hdr.nr_options); 640 printf(" nr_body: %lx\n", (unsigned long)d->hdr.nr_body); 641 printf("\n"); 642 printf("register (%p):\n", (void *)d->hdr.nr_body); 643 printf(" nr_mem_id: %"PRIu16"\n", d->reg.nr_mem_id); 644 printf(" nr_ringid: %"PRIu16"\n", d->reg.nr_ringid); 645 printf(" nr_mode: %lx\n", (unsigned long)d->reg.nr_mode); 646 printf(" nr_flags: %lx\n", (unsigned long)d->reg.nr_flags); 647 printf("\n"); 648 if (d->hdr.nr_options) { 649 struct nmreq_opt_extmem *e = (struct nmreq_opt_extmem *)d->hdr.nr_options; 650 printf("opt_extmem (%p):\n", e); 651 printf(" nro_opt.nro_next: %lx\n", (unsigned long)e->nro_opt.nro_next); 652 printf(" nro_opt.nro_reqtype: %"PRIu32"\n", e->nro_opt.nro_reqtype); 653 printf(" nro_usrptr: %lx\n", (unsigned long)e->nro_usrptr); 654 printf(" nro_info.nr_memsize %"PRIu64"\n", e->nro_info.nr_memsize); 655 } 656 printf("\n"); 657 printf("mem (%p):\n", d->mem); 658 printf(" refcount: %d\n", d->mem->refcount); 659 printf(" mem: %p\n", d->mem->mem); 660 printf(" size: %zu\n", d->mem->size); 661 printf("\n"); 662 printf("rings:\n"); 663 printf(" tx: [%d, %d]\n", d->first_tx_ring, d->last_tx_ring); 664 printf(" rx: [%d, %d]\n", d->first_rx_ring, d->last_rx_ring); 665 } 666 int 667 main(int argc, char *argv[]) 668 { 669 struct nmport_d *d; 670 671 if (argc < 2) { 672 fprintf(stderr, "usage: %s netmap-expr\n", argv[0]); 673 return 1; 674 } 675 676 d = nmport_open(argv[1]); 677 if (d != NULL) { 678 nmreq_dump(d); 679 nmport_close(d); 680 } 681 682 return 0; 683 } 684 #endif 685