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