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
nm_is_identifier(const char * s,const char * e)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
nmreq_push_option(struct nmreq_header * h,struct nmreq_option * o)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
nmreq_header_init(struct nmreq_header * h,uint16_t reqtype,void * body)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
nmreq_header_decode(const char ** pifname,struct nmreq_header * h,struct nmctx * ctx)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
nmreq_get_mem_id(const char ** pifname,struct nmctx * ctx)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
nmreq_register_decode(const char ** pifname,struct nmreq_register * r,struct nmctx * ctx)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
nmreq_option_parsekeys(const char * prefix,char * body,struct nmreq_opt_parser * p,struct nmreq_parse_ctx * pctx)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
nmreq_option_decode1(char * opt,struct nmreq_opt_parser * parsers,void * token,struct nmctx * ctx)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
nmreq_options_decode(const char * opt,struct nmreq_opt_parser parsers[],void * token,struct nmctx * ctx)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 *
nmreq_find_option(struct nmreq_header * h,uint32_t t)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
nmreq_remove_option(struct nmreq_header * h,struct nmreq_option * o)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
nmreq_free_options(struct nmreq_header * h)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*
nmreq_option_name(uint32_t nro_reqtype)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 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