xref: /freebsd-13.1/lib/libnetmap/nmport.c (revision 8e613cf5)
1 /* $FreeBSD$ */
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <sys/ioctl.h>
5 #include <sys/mman.h>
6 #include <fcntl.h>
7 #include <inttypes.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <stdarg.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <net/netmap_user.h>
15 #define LIBNETMAP_NOTHREADSAFE
16 #include "libnetmap.h"
17 
18 struct nmport_cleanup_d {
19 	struct nmport_cleanup_d *next;
20 	void (*cleanup)(struct nmport_cleanup_d *, struct nmport_d *);
21 };
22 
23 static void
24 nmport_push_cleanup(struct nmport_d *d, struct nmport_cleanup_d *c)
25 {
26 	c->next = d->clist;
27 	d->clist = c;
28 }
29 
30 static void
31 nmport_pop_cleanup(struct nmport_d *d)
32 {
33 	struct nmport_cleanup_d *top;
34 
35 	top = d->clist;
36 	d->clist = d->clist->next;
37 	(*top->cleanup)(top, d);
38 	nmctx_free(d->ctx, top);
39 }
40 
41 void nmport_do_cleanup(struct nmport_d *d)
42 {
43 	while (d->clist != NULL) {
44 		nmport_pop_cleanup(d);
45 	}
46 }
47 
48 static struct nmport_d *
49 nmport_new_with_ctx(struct nmctx *ctx)
50 {
51 	struct nmport_d *d;
52 
53 	/* allocate a descriptor */
54 	d = nmctx_malloc(ctx, sizeof(*d));
55 	if (d == NULL) {
56 		nmctx_ferror(ctx, "cannot allocate nmport descriptor");
57 		goto out;
58 	}
59 	memset(d, 0, sizeof(*d));
60 
61 	nmreq_header_init(&d->hdr, NETMAP_REQ_REGISTER, &d->reg);
62 
63 	d->ctx = ctx;
64 	d->fd = -1;
65 
66 out:
67 	return d;
68 }
69 
70 struct nmport_d *
71 nmport_new(void)
72 {
73 	struct nmctx *ctx = nmctx_get();
74 	return nmport_new_with_ctx(ctx);
75 }
76 
77 
78 void
79 nmport_delete(struct nmport_d *d)
80 {
81 	nmctx_free(d->ctx, d);
82 }
83 
84 void
85 nmport_extmem_cleanup(struct nmport_cleanup_d *c, struct nmport_d *d)
86 {
87 	(void)c;
88 
89 	if (d->extmem == NULL)
90 		return;
91 
92 	nmreq_remove_option(&d->hdr, &d->extmem->nro_opt);
93 	nmctx_free(d->ctx, d->extmem);
94 	d->extmem = NULL;
95 }
96 
97 
98 int
99 nmport_extmem(struct nmport_d *d, void *base, size_t size)
100 {
101 	struct nmctx *ctx = d->ctx;
102 	struct nmport_cleanup_d *clnup = NULL;
103 
104 	if (d->register_done) {
105 		nmctx_ferror(ctx, "%s: cannot set extmem of an already registered port", d->hdr.nr_name);
106 		errno = EINVAL;
107 		return -1;
108 	}
109 
110 	if (d->extmem != NULL) {
111 		nmctx_ferror(ctx, "%s: extmem already in use", d->hdr.nr_name);
112 		errno = EINVAL;
113 		return -1;
114 	}
115 
116 	clnup = (struct nmport_cleanup_d *)nmctx_malloc(ctx, sizeof(*clnup));
117 	if (clnup == NULL) {
118 		nmctx_ferror(ctx, "failed to allocate cleanup descriptor");
119 		errno = ENOMEM;
120 		return -1;
121 	}
122 
123 	d->extmem = nmctx_malloc(ctx, sizeof(*d->extmem));
124 	if (d->extmem == NULL) {
125 		nmctx_ferror(ctx, "%s: cannot allocate extmem option", d->hdr.nr_name);
126 		nmctx_free(ctx, clnup);
127 		errno = ENOMEM;
128 		return -1;
129 	}
130 	memset(d->extmem, 0, sizeof(*d->extmem));
131 	d->extmem->nro_usrptr = (uintptr_t)base;
132 	d->extmem->nro_opt.nro_reqtype = NETMAP_REQ_OPT_EXTMEM;
133 	d->extmem->nro_info.nr_memsize = size;
134 	nmreq_push_option(&d->hdr, &d->extmem->nro_opt);
135 
136 	clnup->cleanup = nmport_extmem_cleanup;
137 	nmport_push_cleanup(d, clnup);
138 
139 	return 0;
140 }
141 
142 struct nmport_extmem_from_file_cleanup_d {
143 	struct nmport_cleanup_d up;
144 	void *p;
145 	size_t size;
146 };
147 
148 void nmport_extmem_from_file_cleanup(struct nmport_cleanup_d *c,
149 		struct nmport_d *d)
150 {
151 	struct nmport_extmem_from_file_cleanup_d *cc =
152 		(struct nmport_extmem_from_file_cleanup_d *)c;
153 
154 	munmap(cc->p, cc->size);
155 }
156 
157 int
158 nmport_extmem_from_file(struct nmport_d *d, const char *fname)
159 {
160 	struct nmctx *ctx = d->ctx;
161 	int fd = -1;
162 	off_t mapsize;
163 	void *p;
164 	struct nmport_extmem_from_file_cleanup_d *clnup = NULL;
165 
166 	clnup = nmctx_malloc(ctx, sizeof(*clnup));
167 	if (clnup == NULL) {
168 		nmctx_ferror(ctx, "cannot allocate cleanup descriptor");
169 		errno = ENOMEM;
170 		goto fail;
171 	}
172 
173 	fd = open(fname, O_RDWR);
174 	if (fd < 0) {
175 		nmctx_ferror(ctx, "cannot open '%s': %s", fname, strerror(errno));
176 		goto fail;
177 	}
178 	mapsize = lseek(fd, 0, SEEK_END);
179 	if (mapsize < 0) {
180 		nmctx_ferror(ctx, "failed to obtain filesize of '%s': %s", fname, strerror(errno));
181 		goto fail;
182 	}
183 	p = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
184 	if (p == MAP_FAILED) {
185 		nmctx_ferror(ctx, "cannot mmap '%s': %s", fname, strerror(errno));
186 		goto fail;
187 	}
188 	close(fd);
189 
190 	clnup->p = p;
191 	clnup->size = mapsize;
192 	clnup->up.cleanup = nmport_extmem_from_file_cleanup;
193 	nmport_push_cleanup(d, &clnup->up);
194 
195 	if (nmport_extmem(d, p, mapsize) < 0)
196 		goto fail;
197 
198 	return 0;
199 
200 fail:
201 	if (fd >= 0)
202 		close(fd);
203 	if (clnup != NULL) {
204 		if (clnup->p != MAP_FAILED)
205 			nmport_pop_cleanup(d);
206 		else
207 			nmctx_free(ctx, clnup);
208 	}
209 	return -1;
210 }
211 
212 struct nmreq_pools_info*
213 nmport_extmem_getinfo(struct nmport_d *d)
214 {
215 	if (d->extmem == NULL)
216 		return NULL;
217 	return &d->extmem->nro_info;
218 }
219 
220 /* head of the list of options */
221 static struct nmreq_opt_parser *nmport_opt_parsers;
222 
223 #define NPOPT_PARSER(o)		nmport_opt_##o##_parser
224 #define NPOPT_DESC(o)		nmport_opt_##o##_desc
225 #define NPOPT_NRKEYS(o)		(NPOPT_DESC(o).nr_keys)
226 #define NPOPT_DECL(o, f)						\
227 static int NPOPT_PARSER(o)(struct nmreq_parse_ctx *);			\
228 static struct nmreq_opt_parser NPOPT_DESC(o) = {			\
229 	.prefix = #o,							\
230 	.parse = NPOPT_PARSER(o),					\
231 	.flags = (f),							\
232 	.default_key = -1,						\
233 	.nr_keys = 0,							\
234 	.next = NULL,							\
235 };									\
236 static void __attribute__((constructor))				\
237 nmport_opt_##o##_ctor(void)						\
238 {									\
239 	NPOPT_DESC(o).next = nmport_opt_parsers;			\
240 	nmport_opt_parsers = &NPOPT_DESC(o);				\
241 }
242 struct nmport_key_desc {
243 	struct nmreq_opt_parser *option;
244 	const char *key;
245 	unsigned int flags;
246 	int id;
247 };
248 static void
249 nmport_opt_key_ctor(struct nmport_key_desc *k)
250 {
251 	struct nmreq_opt_parser *o = k->option;
252 	struct nmreq_opt_key *ok;
253 
254 	k->id = o->nr_keys;
255 	ok = &o->keys[k->id];
256 	ok->key = k->key;
257 	ok->id = k->id;
258 	ok->flags = k->flags;
259 	o->nr_keys++;
260 	if (ok->flags & NMREQ_OPTK_DEFAULT)
261 		o->default_key = ok->id;
262 }
263 #define NPKEY_DESC(o, k)	nmport_opt_##o##_key_##k##_desc
264 #define NPKEY_ID(o, k)		(NPKEY_DESC(o, k).id)
265 #define NPKEY_DECL(o, k, f)						\
266 static struct nmport_key_desc NPKEY_DESC(o, k) = {			\
267 	.option = &NPOPT_DESC(o),					\
268 	.key = #k,							\
269 	.flags = (f),							\
270 	.id = -1,							\
271 };									\
272 static void __attribute__((constructor))				\
273 nmport_opt_##o##_key_##k##_ctor(void)					\
274 {									\
275 	nmport_opt_key_ctor(&NPKEY_DESC(o, k));				\
276 }
277 #define nmport_key(p, o, k)	((p)->keys[NPKEY_ID(o, k)])
278 #define nmport_defkey(p, o)	((p)->keys[NPOPT_DESC(o).default_key])
279 
280 NPOPT_DECL(share, 0)
281 	NPKEY_DECL(share, port, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
282 NPOPT_DECL(extmem, 0)
283 	NPKEY_DECL(extmem, file, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
284 	NPKEY_DECL(extmem, if_num, 0)
285 	NPKEY_DECL(extmem, if_size, 0)
286 	NPKEY_DECL(extmem, ring_num, 0)
287 	NPKEY_DECL(extmem, ring_size, 0)
288 	NPKEY_DECL(extmem, buf_num, 0)
289 	NPKEY_DECL(extmem, buf_size, 0)
290 NPOPT_DECL(conf, 0)
291 	NPKEY_DECL(conf, rings, 0)
292 	NPKEY_DECL(conf, host_rings, 0)
293 	NPKEY_DECL(conf, slots, 0)
294 	NPKEY_DECL(conf, tx_rings, 0)
295 	NPKEY_DECL(conf, rx_rings, 0)
296 	NPKEY_DECL(conf, host_tx_rings, 0)
297 	NPKEY_DECL(conf, host_rx_rings, 0)
298 	NPKEY_DECL(conf, tx_slots, 0)
299 	NPKEY_DECL(conf, rx_slots, 0)
300 
301 
302 static int
303 NPOPT_PARSER(share)(struct nmreq_parse_ctx *p)
304 {
305 	struct nmctx *ctx = p->ctx;
306 	struct nmport_d *d = p->token;
307 	int32_t mem_id;
308 	const char *v = nmport_defkey(p, share);
309 
310 	mem_id = nmreq_get_mem_id(&v, ctx);
311 	if (mem_id < 0)
312 		return -1;
313 	if (d->reg.nr_mem_id && d->reg.nr_mem_id != mem_id) {
314 		nmctx_ferror(ctx, "cannot set mem_id to %"PRId32", already set to %"PRIu16"",
315 				mem_id, d->reg.nr_mem_id);
316 		errno = EINVAL;
317 		return -1;
318 	}
319 	d->reg.nr_mem_id = mem_id;
320 	return 0;
321 }
322 
323 static int
324 NPOPT_PARSER(extmem)(struct nmreq_parse_ctx *p)
325 {
326 	struct nmport_d *d;
327 	struct nmreq_pools_info *pi;
328 	int i;
329 
330 	d = p->token;
331 
332 	if (nmport_extmem_from_file(d, nmport_key(p, extmem, file)) < 0)
333 		return -1;
334 
335 	pi = &d->extmem->nro_info;
336 
337 	for  (i = 0; i < NPOPT_NRKEYS(extmem); i++) {
338 		const char *k = p->keys[i];
339 		uint32_t v;
340 
341 		if (k == NULL)
342 			continue;
343 
344 		v = atoi(k);
345 		if (i == NPKEY_ID(extmem, if_num)) {
346 			pi->nr_if_pool_objtotal = v;
347 		} else if (i == NPKEY_ID(extmem, if_size)) {
348 			pi->nr_if_pool_objsize = v;
349 		} else if (i == NPKEY_ID(extmem, ring_num)) {
350 			pi->nr_ring_pool_objtotal = v;
351 		} else if (i == NPKEY_ID(extmem, ring_size)) {
352 			pi->nr_ring_pool_objsize = v;
353 		} else if (i == NPKEY_ID(extmem, buf_num)) {
354 			pi->nr_buf_pool_objtotal = v;
355 		} else if (i == NPKEY_ID(extmem, buf_size)) {
356 			pi->nr_buf_pool_objsize = v;
357 		}
358 	}
359 	return 0;
360 }
361 
362 static int
363 NPOPT_PARSER(conf)(struct nmreq_parse_ctx *p)
364 {
365 	struct nmport_d *d;
366 
367 	d = p->token;
368 
369 	if (nmport_key(p, conf, rings) != NULL) {
370 		uint16_t nr_rings = atoi(nmport_key(p, conf, rings));
371 		d->reg.nr_tx_rings = nr_rings;
372 		d->reg.nr_rx_rings = nr_rings;
373 	}
374 	if (nmport_key(p, conf, host_rings) != NULL) {
375 		uint16_t nr_rings = atoi(nmport_key(p, conf, host_rings));
376 		d->reg.nr_host_tx_rings = nr_rings;
377 		d->reg.nr_host_rx_rings = nr_rings;
378 	}
379 	if (nmport_key(p, conf, slots) != NULL) {
380 		uint32_t nr_slots = atoi(nmport_key(p, conf, slots));
381 		d->reg.nr_tx_slots = nr_slots;
382 		d->reg.nr_rx_slots = nr_slots;
383 	}
384 	if (nmport_key(p, conf, tx_rings) != NULL) {
385 		d->reg.nr_tx_rings = atoi(nmport_key(p, conf, tx_rings));
386 	}
387 	if (nmport_key(p, conf, rx_rings) != NULL) {
388 		d->reg.nr_rx_rings = atoi(nmport_key(p, conf, rx_rings));
389 	}
390 	if (nmport_key(p, conf, host_tx_rings) != NULL) {
391 		d->reg.nr_host_tx_rings = atoi(nmport_key(p, conf, host_tx_rings));
392 	}
393 	if (nmport_key(p, conf, host_rx_rings) != NULL) {
394 		d->reg.nr_host_rx_rings = atoi(nmport_key(p, conf, host_rx_rings));
395 	}
396 	if (nmport_key(p, conf, tx_slots) != NULL) {
397 		d->reg.nr_tx_slots = atoi(nmport_key(p, conf, tx_slots));
398 	}
399 	if (nmport_key(p, conf, rx_slots) != NULL) {
400 		d->reg.nr_rx_slots = atoi(nmport_key(p, conf, rx_slots));
401 	}
402 	return 0;
403 }
404 
405 void
406 nmport_disable_option(const char *opt)
407 {
408 	struct nmreq_opt_parser *p;
409 
410 	for (p = nmport_opt_parsers; p != NULL; p = p->next) {
411 		if (!strcmp(p->prefix, opt)) {
412 			p->flags |= NMREQ_OPTF_DISABLED;
413 		}
414 	}
415 }
416 
417 int
418 nmport_enable_option(const char *opt)
419 {
420 	struct nmreq_opt_parser *p;
421 
422 	for (p = nmport_opt_parsers; p != NULL; p = p->next) {
423 		if (!strcmp(p->prefix, opt)) {
424 			p->flags &= ~NMREQ_OPTF_DISABLED;
425 			return 0;
426 		}
427 	}
428 	errno = EOPNOTSUPP;
429 	return -1;
430 }
431 
432 
433 int
434 nmport_parse(struct nmport_d *d, const char *ifname)
435 {
436 	const char *scan = ifname;
437 
438 	if (nmreq_header_decode(&scan, &d->hdr, d->ctx) < 0) {
439 		goto err;
440 	}
441 
442 	/* parse the register request */
443 	if (nmreq_register_decode(&scan, &d->reg, d->ctx) < 0) {
444 		goto err;
445 	}
446 
447 	/* parse the options, if any */
448 	if (nmreq_options_decode(scan, nmport_opt_parsers, d, d->ctx) < 0) {
449 		goto err;
450 	}
451 	return 0;
452 
453 err:
454 	nmport_undo_parse(d);
455 	return -1;
456 }
457 
458 void
459 nmport_undo_parse(struct nmport_d *d)
460 {
461 	nmport_do_cleanup(d);
462 	memset(&d->reg, 0, sizeof(d->reg));
463 	memset(&d->hdr, 0, sizeof(d->hdr));
464 }
465 
466 struct nmport_d *
467 nmport_prepare(const char *ifname)
468 {
469 	struct nmport_d *d;
470 
471 	/* allocate a descriptor */
472 	d = nmport_new();
473 	if (d == NULL)
474 		goto err;
475 
476 	/* parse the header */
477 	if (nmport_parse(d, ifname) < 0)
478 		goto err;
479 
480 	return d;
481 
482 err:
483 	nmport_undo_prepare(d);
484 	return NULL;
485 }
486 
487 void
488 nmport_undo_prepare(struct nmport_d *d)
489 {
490 	if (d == NULL)
491 		return;
492 	nmport_undo_parse(d);
493 	nmport_delete(d);
494 }
495 
496 int
497 nmport_register(struct nmport_d *d)
498 {
499 	struct nmctx *ctx = d->ctx;
500 
501 	if (d->register_done) {
502 		errno = EINVAL;
503 		nmctx_ferror(ctx, "%s: already registered", d->hdr.nr_name);
504 		return -1;
505 	}
506 
507 	d->fd = open("/dev/netmap", O_RDWR);
508 	if (d->fd < 0) {
509 		nmctx_ferror(ctx, "/dev/netmap: %s", strerror(errno));
510 		goto err;
511 	}
512 
513 	if (ioctl(d->fd, NIOCCTRL, &d->hdr) < 0) {
514 		struct nmreq_option *o;
515 		int option_errors = 0;
516 
517 		nmreq_foreach_option(&d->hdr, o) {
518 			if (o->nro_status) {
519 				nmctx_ferror(ctx, "%s: option %s: %s",
520 						d->hdr.nr_name,
521 						nmreq_option_name(o->nro_reqtype),
522 						strerror(o->nro_status));
523 				option_errors++;
524 			}
525 
526 		}
527 		if (!option_errors)
528 			nmctx_ferror(ctx, "%s: %s", d->hdr.nr_name, strerror(errno));
529 		goto err;
530 	}
531 
532 	d->register_done = 1;
533 
534 	return 0;
535 
536 err:
537 	nmport_undo_register(d);
538 	return -1;
539 }
540 
541 void
542 nmport_undo_register(struct nmport_d *d)
543 {
544 	if (d->fd >= 0)
545 		close(d->fd);
546 	d->fd = -1;
547 	d->register_done = 0;
548 }
549 
550 /* lookup the mem_id in the mem-list: do a new mmap() if
551  * not found, reuse existing otherwise
552  */
553 int
554 nmport_mmap(struct nmport_d *d)
555 {
556 	struct nmctx *ctx = d->ctx;
557 	struct nmem_d *m = NULL;
558 	u_int num_tx, num_rx;
559 	int i;
560 
561 	if (d->mmap_done) {
562 		errno = EINVAL;
563 		nmctx_ferror(ctx, "%s: already mapped", d->hdr.nr_name);
564 		return -1;
565 	}
566 
567 	if (!d->register_done) {
568 		errno = EINVAL;
569 		nmctx_ferror(ctx, "cannot map unregistered port");
570 		return -1;
571 	}
572 
573 	nmctx_lock(ctx);
574 
575 	for (m = ctx->mem_descs; m != NULL; m = m->next)
576 		if (m->mem_id == d->reg.nr_mem_id)
577 			break;
578 
579 	if (m == NULL) {
580 		m = nmctx_malloc(ctx, sizeof(*m));
581 		if (m == NULL) {
582 			nmctx_ferror(ctx, "cannot allocate memory descriptor");
583 			goto err;
584 		}
585 		memset(m, 0, sizeof(*m));
586 		if (d->extmem != NULL) {
587 			m->mem = (void *)d->extmem->nro_usrptr;
588 			m->size = d->extmem->nro_info.nr_memsize;
589 			m->is_extmem = 1;
590 		} else {
591 			m->mem = mmap(NULL, d->reg.nr_memsize, PROT_READ|PROT_WRITE,
592 					MAP_SHARED, d->fd, 0);
593 			if (m->mem == MAP_FAILED) {
594 				nmctx_ferror(ctx, "mmap: %s", strerror(errno));
595 				goto err;
596 			}
597 			m->size = d->reg.nr_memsize;
598 		}
599 		m->mem_id = d->reg.nr_mem_id;
600 		m->next = ctx->mem_descs;
601 		if (ctx->mem_descs != NULL)
602 			ctx->mem_descs->prev = m;
603 		ctx->mem_descs = m;
604 	}
605 	m->refcount++;
606 
607 	nmctx_unlock(ctx);
608 
609 	d->mem = m;
610 
611 	d->nifp = NETMAP_IF(m->mem, d->reg.nr_offset);
612 
613 	num_tx = d->reg.nr_tx_rings + d->nifp->ni_host_tx_rings;
614 	for (i = 0; i < num_tx && !d->nifp->ring_ofs[i]; i++)
615 		;
616 	d->first_tx_ring = i;
617 	for ( ; i < num_tx && d->nifp->ring_ofs[i]; i++)
618 		;
619 	d->last_tx_ring = i - 1;
620 
621 	num_rx = d->reg.nr_rx_rings + d->nifp->ni_host_rx_rings;
622 	for (i = 0; i < num_rx && !d->nifp->ring_ofs[i + num_tx]; i++)
623 		;
624 	d->first_rx_ring = i;
625 	for ( ; i < num_rx && d->nifp->ring_ofs[i + num_tx]; i++)
626 		;
627 	d->last_rx_ring = i - 1;
628 
629 	d->mmap_done = 1;
630 
631 	return 0;
632 
633 err:
634 	nmctx_unlock(ctx);
635 	nmport_undo_mmap(d);
636 	return -1;
637 }
638 
639 void
640 nmport_undo_mmap(struct nmport_d *d)
641 {
642 	struct nmem_d *m;
643 	struct nmctx *ctx = d->ctx;
644 
645 	m = d->mem;
646 	if (m == NULL)
647 		return;
648 	nmctx_lock(ctx);
649 	m->refcount--;
650 	if (m->refcount <= 0) {
651 		if (!m->is_extmem && m->mem != MAP_FAILED)
652 			munmap(m->mem, m->size);
653 		/* extract from the list and free */
654 		if (m->next != NULL)
655 			m->next->prev = m->prev;
656 		if (m->prev != NULL)
657 			m->prev->next = m->next;
658 		else
659 			ctx->mem_descs = m->next;
660 		nmctx_free(ctx, m);
661 		d->mem = NULL;
662 	}
663 	nmctx_unlock(ctx);
664 	d->mmap_done = 0;
665 	d->mem = NULL;
666 	d->nifp = NULL;
667 	d->first_tx_ring = 0;
668 	d->last_tx_ring = 0;
669 	d->first_rx_ring = 0;
670 	d->last_rx_ring = 0;
671 	d->cur_tx_ring = 0;
672 	d->cur_rx_ring = 0;
673 }
674 
675 int
676 nmport_open_desc(struct nmport_d *d)
677 {
678 	if (nmport_register(d) < 0)
679 		goto err;
680 
681 	if (nmport_mmap(d) < 0)
682 		goto err;
683 
684 	return 0;
685 err:
686 	nmport_undo_open_desc(d);
687 	return -1;
688 }
689 
690 void
691 nmport_undo_open_desc(struct nmport_d *d)
692 {
693 	nmport_undo_mmap(d);
694 	nmport_undo_register(d);
695 }
696 
697 
698 struct nmport_d *
699 nmport_open(const char *ifname)
700 {
701 	struct nmport_d *d;
702 
703 	/* prepare the descriptor */
704 	d = nmport_prepare(ifname);
705 	if (d == NULL)
706 		goto err;
707 
708 	/* open netmap and register */
709 	if (nmport_open_desc(d) < 0)
710 		goto err;
711 
712 	return d;
713 
714 err:
715 	nmport_close(d);
716 	return NULL;
717 }
718 
719 void
720 nmport_close(struct nmport_d *d)
721 {
722 	if (d == NULL)
723 		return;
724 	nmport_undo_open_desc(d);
725 	nmport_undo_prepare(d);
726 }
727 
728 struct nmport_d *
729 nmport_clone(struct nmport_d *d)
730 {
731 	struct nmport_d *c;
732 	struct nmctx *ctx;
733 
734 	ctx = d->ctx;
735 
736 	if (d->extmem != NULL && !d->register_done) {
737 		errno = EINVAL;
738 		nmctx_ferror(ctx, "cannot clone unregistered port that is using extmem");
739 		return NULL;
740 	}
741 
742 	c = nmport_new_with_ctx(ctx);
743 	if (c == NULL)
744 		return NULL;
745 	/* copy the output of parse */
746 	c->hdr = d->hdr;
747 	/* redirect the pointer to the body */
748 	c->hdr.nr_body = (uintptr_t)&c->reg;
749 	/* options are not cloned */
750 	c->hdr.nr_options = 0;
751 	c->reg = d->reg; /* this also copies the mem_id */
752 	/* put the new port in an un-registered, unmapped state */
753 	c->fd = -1;
754 	c->nifp = NULL;
755 	c->register_done = 0;
756 	c->mem = NULL;
757 	c->extmem = NULL;
758 	c->mmap_done = 0;
759 	c->first_tx_ring = 0;
760 	c->last_tx_ring = 0;
761 	c->first_rx_ring = 0;
762 	c->last_rx_ring = 0;
763 	c->cur_tx_ring = 0;
764 	c->cur_rx_ring = 0;
765 
766 	return c;
767 }
768 
769 int
770 nmport_inject(struct nmport_d *d, const void *buf, size_t size)
771 {
772 	u_int c, n = d->last_tx_ring - d->first_tx_ring + 1,
773 		ri = d->cur_tx_ring;
774 
775 	for (c = 0; c < n ; c++, ri++) {
776 		/* compute current ring to use */
777 		struct netmap_ring *ring;
778 		uint32_t i, j, idx;
779 		size_t rem;
780 
781 		if (ri > d->last_tx_ring)
782 			ri = d->first_tx_ring;
783 		ring = NETMAP_TXRING(d->nifp, ri);
784 		rem = size;
785 		j = ring->cur;
786 		while (rem > ring->nr_buf_size && j != ring->tail) {
787 			rem -= ring->nr_buf_size;
788 			j = nm_ring_next(ring, j);
789 		}
790 		if (j == ring->tail && rem > 0)
791 			continue;
792 		i = ring->cur;
793 		while (i != j) {
794 			idx = ring->slot[i].buf_idx;
795 			ring->slot[i].len = ring->nr_buf_size;
796 			ring->slot[i].flags = NS_MOREFRAG;
797 			nm_pkt_copy(buf, NETMAP_BUF(ring, idx), ring->nr_buf_size);
798 			i = nm_ring_next(ring, i);
799 			buf = (char *)buf + ring->nr_buf_size;
800 		}
801 		idx = ring->slot[i].buf_idx;
802 		ring->slot[i].len = rem;
803 		ring->slot[i].flags = 0;
804 		nm_pkt_copy(buf, NETMAP_BUF(ring, idx), rem);
805 		ring->head = ring->cur = nm_ring_next(ring, i);
806 		d->cur_tx_ring = ri;
807 		return size;
808 	}
809 	return 0; /* fail */
810 }
811