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