xref: /freebsd-13.1/tools/tools/netmap/lb.c (revision 73b2e3e5)
1689f146bSVincenzo Maffione /*
2689f146bSVincenzo Maffione  * Copyright (C) 2017 Corelight, Inc. and Universita` di Pisa. All rights reserved.
3689f146bSVincenzo Maffione  *
4689f146bSVincenzo Maffione  * Redistribution and use in source and binary forms, with or without
5689f146bSVincenzo Maffione  * modification, are permitted provided that the following conditions
6689f146bSVincenzo Maffione  * are met:
7689f146bSVincenzo Maffione  *   1. Redistributions of source code must retain the above copyright
8689f146bSVincenzo Maffione  *      notice, this list of conditions and the following disclaimer.
9689f146bSVincenzo Maffione  *   2. Redistributions in binary form must reproduce the above copyright
10689f146bSVincenzo Maffione  *      notice, this list of conditions and the following disclaimer in the
11689f146bSVincenzo Maffione  *    documentation and/or other materials provided with the distribution.
12689f146bSVincenzo Maffione  *
13689f146bSVincenzo Maffione  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14689f146bSVincenzo Maffione  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15689f146bSVincenzo Maffione  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16689f146bSVincenzo Maffione  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17689f146bSVincenzo Maffione  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18689f146bSVincenzo Maffione  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19689f146bSVincenzo Maffione  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20689f146bSVincenzo Maffione  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21689f146bSVincenzo Maffione  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22689f146bSVincenzo Maffione  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23689f146bSVincenzo Maffione  * SUCH DAMAGE.
24689f146bSVincenzo Maffione  */
25689f146bSVincenzo Maffione /* $FreeBSD$ */
26689f146bSVincenzo Maffione #include <ctype.h>
27*73b2e3e5SVincenzo Maffione #include <errno.h>
28689f146bSVincenzo Maffione #include <inttypes.h>
29*73b2e3e5SVincenzo Maffione #include <libnetmap.h>
30689f146bSVincenzo Maffione #include <netinet/in.h>		/* htonl */
31689f146bSVincenzo Maffione #include <pthread.h>
32*73b2e3e5SVincenzo Maffione #include <signal.h>
33*73b2e3e5SVincenzo Maffione #include <stdbool.h>
34*73b2e3e5SVincenzo Maffione #include <stdio.h>
35*73b2e3e5SVincenzo Maffione #include <stdlib.h>
36*73b2e3e5SVincenzo Maffione #include <string.h>
37*73b2e3e5SVincenzo Maffione #include <syslog.h>
38*73b2e3e5SVincenzo Maffione #include <sys/ioctl.h>
39*73b2e3e5SVincenzo Maffione #include <sys/poll.h>
40*73b2e3e5SVincenzo Maffione #include <unistd.h>
41689f146bSVincenzo Maffione 
42689f146bSVincenzo Maffione #include "pkt_hash.h"
43689f146bSVincenzo Maffione #include "ctrs.h"
44689f146bSVincenzo Maffione 
45689f146bSVincenzo Maffione 
46689f146bSVincenzo Maffione /*
47689f146bSVincenzo Maffione  * use our version of header structs, rather than bringing in a ton
48689f146bSVincenzo Maffione  * of platform specific ones
49689f146bSVincenzo Maffione  */
50689f146bSVincenzo Maffione #ifndef ETH_ALEN
51689f146bSVincenzo Maffione #define ETH_ALEN 6
52689f146bSVincenzo Maffione #endif
53689f146bSVincenzo Maffione 
54689f146bSVincenzo Maffione struct compact_eth_hdr {
55689f146bSVincenzo Maffione 	unsigned char h_dest[ETH_ALEN];
56689f146bSVincenzo Maffione 	unsigned char h_source[ETH_ALEN];
57689f146bSVincenzo Maffione 	u_int16_t h_proto;
58689f146bSVincenzo Maffione };
59689f146bSVincenzo Maffione 
60689f146bSVincenzo Maffione struct compact_ip_hdr {
61689f146bSVincenzo Maffione 	u_int8_t ihl:4, version:4;
62689f146bSVincenzo Maffione 	u_int8_t tos;
63689f146bSVincenzo Maffione 	u_int16_t tot_len;
64689f146bSVincenzo Maffione 	u_int16_t id;
65689f146bSVincenzo Maffione 	u_int16_t frag_off;
66689f146bSVincenzo Maffione 	u_int8_t ttl;
67689f146bSVincenzo Maffione 	u_int8_t protocol;
68689f146bSVincenzo Maffione 	u_int16_t check;
69689f146bSVincenzo Maffione 	u_int32_t saddr;
70689f146bSVincenzo Maffione 	u_int32_t daddr;
71689f146bSVincenzo Maffione };
72689f146bSVincenzo Maffione 
73689f146bSVincenzo Maffione struct compact_ipv6_hdr {
74689f146bSVincenzo Maffione 	u_int8_t priority:4, version:4;
75689f146bSVincenzo Maffione 	u_int8_t flow_lbl[3];
76689f146bSVincenzo Maffione 	u_int16_t payload_len;
77689f146bSVincenzo Maffione 	u_int8_t nexthdr;
78689f146bSVincenzo Maffione 	u_int8_t hop_limit;
79689f146bSVincenzo Maffione 	struct in6_addr saddr;
80689f146bSVincenzo Maffione 	struct in6_addr daddr;
81689f146bSVincenzo Maffione };
82689f146bSVincenzo Maffione 
83689f146bSVincenzo Maffione #define MAX_IFNAMELEN 	64
84689f146bSVincenzo Maffione #define MAX_PORTNAMELEN	(MAX_IFNAMELEN + 40)
85689f146bSVincenzo Maffione #define DEF_OUT_PIPES 	2
86689f146bSVincenzo Maffione #define DEF_EXTRA_BUFS 	0
87689f146bSVincenzo Maffione #define DEF_BATCH	2048
88689f146bSVincenzo Maffione #define DEF_WAIT_LINK	2
89689f146bSVincenzo Maffione #define DEF_STATS_INT	600
90*73b2e3e5SVincenzo Maffione #define BUF_REVOKE	150
91689f146bSVincenzo Maffione #define STAT_MSG_MAXSIZE 1024
92689f146bSVincenzo Maffione 
937eb32dc8SVincenzo Maffione static struct {
94*73b2e3e5SVincenzo Maffione 	char ifname[MAX_IFNAMELEN + 1];
95*73b2e3e5SVincenzo Maffione 	char base_name[MAX_IFNAMELEN + 1];
96689f146bSVincenzo Maffione 	int netmap_fd;
97689f146bSVincenzo Maffione 	uint16_t output_rings;
98689f146bSVincenzo Maffione 	uint16_t num_groups;
99689f146bSVincenzo Maffione 	uint32_t extra_bufs;
100689f146bSVincenzo Maffione 	uint16_t batch;
101689f146bSVincenzo Maffione 	int stdout_interval;
102689f146bSVincenzo Maffione 	int syslog_interval;
103689f146bSVincenzo Maffione 	int wait_link;
104689f146bSVincenzo Maffione 	bool busy_wait;
105689f146bSVincenzo Maffione } glob_arg;
106689f146bSVincenzo Maffione 
107689f146bSVincenzo Maffione /*
108689f146bSVincenzo Maffione  * the overflow queue is a circular queue of buffers
109689f146bSVincenzo Maffione  */
110689f146bSVincenzo Maffione struct overflow_queue {
111689f146bSVincenzo Maffione 	char name[MAX_IFNAMELEN + 16];
112689f146bSVincenzo Maffione 	struct netmap_slot *slots;
113689f146bSVincenzo Maffione 	uint32_t head;
114689f146bSVincenzo Maffione 	uint32_t tail;
115689f146bSVincenzo Maffione 	uint32_t n;
116689f146bSVincenzo Maffione 	uint32_t size;
117689f146bSVincenzo Maffione };
118689f146bSVincenzo Maffione 
1197eb32dc8SVincenzo Maffione static struct overflow_queue *freeq;
120689f146bSVincenzo Maffione 
121689f146bSVincenzo Maffione static inline int
oq_full(struct overflow_queue * q)122689f146bSVincenzo Maffione oq_full(struct overflow_queue *q)
123689f146bSVincenzo Maffione {
124689f146bSVincenzo Maffione 	return q->n >= q->size;
125689f146bSVincenzo Maffione }
126689f146bSVincenzo Maffione 
127689f146bSVincenzo Maffione static inline int
oq_empty(struct overflow_queue * q)128689f146bSVincenzo Maffione oq_empty(struct overflow_queue *q)
129689f146bSVincenzo Maffione {
130689f146bSVincenzo Maffione 	return q->n <= 0;
131689f146bSVincenzo Maffione }
132689f146bSVincenzo Maffione 
133689f146bSVincenzo Maffione static inline void
oq_enq(struct overflow_queue * q,const struct netmap_slot * s)134689f146bSVincenzo Maffione oq_enq(struct overflow_queue *q, const struct netmap_slot *s)
135689f146bSVincenzo Maffione {
136689f146bSVincenzo Maffione 	if (unlikely(oq_full(q))) {
137689f146bSVincenzo Maffione 		D("%s: queue full!", q->name);
138689f146bSVincenzo Maffione 		abort();
139689f146bSVincenzo Maffione 	}
140689f146bSVincenzo Maffione 	q->slots[q->tail] = *s;
141689f146bSVincenzo Maffione 	q->n++;
142689f146bSVincenzo Maffione 	q->tail++;
143689f146bSVincenzo Maffione 	if (q->tail >= q->size)
144689f146bSVincenzo Maffione 		q->tail = 0;
145689f146bSVincenzo Maffione }
146689f146bSVincenzo Maffione 
147689f146bSVincenzo Maffione static inline struct netmap_slot
oq_deq(struct overflow_queue * q)148689f146bSVincenzo Maffione oq_deq(struct overflow_queue *q)
149689f146bSVincenzo Maffione {
150689f146bSVincenzo Maffione 	struct netmap_slot s = q->slots[q->head];
151689f146bSVincenzo Maffione 	if (unlikely(oq_empty(q))) {
152689f146bSVincenzo Maffione 		D("%s: queue empty!", q->name);
153689f146bSVincenzo Maffione 		abort();
154689f146bSVincenzo Maffione 	}
155689f146bSVincenzo Maffione 	q->n--;
156689f146bSVincenzo Maffione 	q->head++;
157689f146bSVincenzo Maffione 	if (q->head >= q->size)
158689f146bSVincenzo Maffione 		q->head = 0;
159689f146bSVincenzo Maffione 	return s;
160689f146bSVincenzo Maffione }
161689f146bSVincenzo Maffione 
162689f146bSVincenzo Maffione static volatile int do_abort = 0;
163689f146bSVincenzo Maffione 
1647eb32dc8SVincenzo Maffione static uint64_t dropped = 0;
1657eb32dc8SVincenzo Maffione static uint64_t forwarded = 0;
1667eb32dc8SVincenzo Maffione static uint64_t received_bytes = 0;
1677eb32dc8SVincenzo Maffione static uint64_t received_pkts = 0;
1687eb32dc8SVincenzo Maffione static uint64_t non_ip = 0;
1697eb32dc8SVincenzo Maffione static uint32_t freeq_n = 0;
170689f146bSVincenzo Maffione 
171689f146bSVincenzo Maffione struct port_des {
172689f146bSVincenzo Maffione 	char interface[MAX_PORTNAMELEN];
173689f146bSVincenzo Maffione 	struct my_ctrs ctr;
174689f146bSVincenzo Maffione 	unsigned int last_sync;
175689f146bSVincenzo Maffione 	uint32_t last_tail;
176689f146bSVincenzo Maffione 	struct overflow_queue *oq;
177*73b2e3e5SVincenzo Maffione 	struct nmport_d *nmd;
178689f146bSVincenzo Maffione 	struct netmap_ring *ring;
179689f146bSVincenzo Maffione 	struct group_des *group;
180689f146bSVincenzo Maffione };
181689f146bSVincenzo Maffione 
1827eb32dc8SVincenzo Maffione static struct port_des *ports;
183689f146bSVincenzo Maffione 
184689f146bSVincenzo Maffione /* each group of pipes receives all the packets */
185689f146bSVincenzo Maffione struct group_des {
186689f146bSVincenzo Maffione 	char pipename[MAX_IFNAMELEN];
187689f146bSVincenzo Maffione 	struct port_des *ports;
188689f146bSVincenzo Maffione 	int first_id;
189689f146bSVincenzo Maffione 	int nports;
190689f146bSVincenzo Maffione 	int last;
191689f146bSVincenzo Maffione 	int custom_port;
192689f146bSVincenzo Maffione };
193689f146bSVincenzo Maffione 
1947eb32dc8SVincenzo Maffione static struct group_des *groups;
195689f146bSVincenzo Maffione 
196689f146bSVincenzo Maffione /* statistcs */
197689f146bSVincenzo Maffione struct counters {
198689f146bSVincenzo Maffione 	struct timeval ts;
199689f146bSVincenzo Maffione 	struct my_ctrs *ctrs;
200689f146bSVincenzo Maffione 	uint64_t received_pkts;
201689f146bSVincenzo Maffione 	uint64_t received_bytes;
202689f146bSVincenzo Maffione 	uint64_t non_ip;
203689f146bSVincenzo Maffione 	uint32_t freeq_n;
204689f146bSVincenzo Maffione 	int status __attribute__((aligned(64)));
205689f146bSVincenzo Maffione #define COUNTERS_EMPTY	0
206689f146bSVincenzo Maffione #define COUNTERS_FULL	1
207689f146bSVincenzo Maffione };
208689f146bSVincenzo Maffione 
2097eb32dc8SVincenzo Maffione static struct counters counters_buf;
210689f146bSVincenzo Maffione 
211689f146bSVincenzo Maffione static void *
print_stats(void * arg)212689f146bSVincenzo Maffione print_stats(void *arg)
213689f146bSVincenzo Maffione {
214689f146bSVincenzo Maffione 	int npipes = glob_arg.output_rings;
215689f146bSVincenzo Maffione 	int sys_int = 0;
216689f146bSVincenzo Maffione 	(void)arg;
217689f146bSVincenzo Maffione 	struct my_ctrs cur, prev;
218689f146bSVincenzo Maffione 	struct my_ctrs *pipe_prev;
219689f146bSVincenzo Maffione 
220689f146bSVincenzo Maffione 	pipe_prev = calloc(npipes, sizeof(struct my_ctrs));
221689f146bSVincenzo Maffione 	if (pipe_prev == NULL) {
222689f146bSVincenzo Maffione 		D("out of memory");
223689f146bSVincenzo Maffione 		exit(1);
224689f146bSVincenzo Maffione 	}
225689f146bSVincenzo Maffione 
226689f146bSVincenzo Maffione 	char stat_msg[STAT_MSG_MAXSIZE] = "";
227689f146bSVincenzo Maffione 
228689f146bSVincenzo Maffione 	memset(&prev, 0, sizeof(prev));
229689f146bSVincenzo Maffione 	while (!do_abort) {
230689f146bSVincenzo Maffione 		int j, dosyslog = 0, dostdout = 0, newdata;
231689f146bSVincenzo Maffione 		uint64_t pps = 0, dps = 0, bps = 0, dbps = 0, usec = 0;
232689f146bSVincenzo Maffione 		struct my_ctrs x;
233689f146bSVincenzo Maffione 
234689f146bSVincenzo Maffione 		counters_buf.status = COUNTERS_EMPTY;
235689f146bSVincenzo Maffione 		newdata = 0;
236689f146bSVincenzo Maffione 		memset(&cur, 0, sizeof(cur));
237689f146bSVincenzo Maffione 		sleep(1);
238689f146bSVincenzo Maffione 		if (counters_buf.status == COUNTERS_FULL) {
239689f146bSVincenzo Maffione 			__sync_synchronize();
240689f146bSVincenzo Maffione 			newdata = 1;
241689f146bSVincenzo Maffione 			cur.t = counters_buf.ts;
242689f146bSVincenzo Maffione 			if (prev.t.tv_sec || prev.t.tv_usec) {
243689f146bSVincenzo Maffione 				usec = (cur.t.tv_sec - prev.t.tv_sec) * 1000000 +
244689f146bSVincenzo Maffione 					cur.t.tv_usec - prev.t.tv_usec;
245689f146bSVincenzo Maffione 			}
246689f146bSVincenzo Maffione 		}
247689f146bSVincenzo Maffione 
248689f146bSVincenzo Maffione 		++sys_int;
249689f146bSVincenzo Maffione 		if (glob_arg.stdout_interval && sys_int % glob_arg.stdout_interval == 0)
250689f146bSVincenzo Maffione 				dostdout = 1;
251689f146bSVincenzo Maffione 		if (glob_arg.syslog_interval && sys_int % glob_arg.syslog_interval == 0)
252689f146bSVincenzo Maffione 				dosyslog = 1;
253689f146bSVincenzo Maffione 
254689f146bSVincenzo Maffione 		for (j = 0; j < npipes; ++j) {
255689f146bSVincenzo Maffione 			struct my_ctrs *c = &counters_buf.ctrs[j];
256689f146bSVincenzo Maffione 			cur.pkts += c->pkts;
257689f146bSVincenzo Maffione 			cur.drop += c->drop;
258689f146bSVincenzo Maffione 			cur.drop_bytes += c->drop_bytes;
259689f146bSVincenzo Maffione 			cur.bytes += c->bytes;
260689f146bSVincenzo Maffione 
261689f146bSVincenzo Maffione 			if (usec) {
262689f146bSVincenzo Maffione 				x.pkts = c->pkts - pipe_prev[j].pkts;
263689f146bSVincenzo Maffione 				x.drop = c->drop - pipe_prev[j].drop;
264689f146bSVincenzo Maffione 				x.bytes = c->bytes - pipe_prev[j].bytes;
265689f146bSVincenzo Maffione 				x.drop_bytes = c->drop_bytes - pipe_prev[j].drop_bytes;
266689f146bSVincenzo Maffione 				pps = (x.pkts*1000000 + usec/2) / usec;
267689f146bSVincenzo Maffione 				dps = (x.drop*1000000 + usec/2) / usec;
268689f146bSVincenzo Maffione 				bps = ((x.bytes*1000000 + usec/2) / usec) * 8;
269689f146bSVincenzo Maffione 				dbps = ((x.drop_bytes*1000000 + usec/2) / usec) * 8;
270689f146bSVincenzo Maffione 			}
271689f146bSVincenzo Maffione 			pipe_prev[j] = *c;
272689f146bSVincenzo Maffione 
273689f146bSVincenzo Maffione 			if ( (dosyslog || dostdout) && newdata )
274689f146bSVincenzo Maffione 				snprintf(stat_msg, STAT_MSG_MAXSIZE,
275689f146bSVincenzo Maffione 				       "{"
276689f146bSVincenzo Maffione 				       "\"ts\":%.6f,"
277689f146bSVincenzo Maffione 				       "\"interface\":\"%s\","
278689f146bSVincenzo Maffione 				       "\"output_ring\":%" PRIu16 ","
279689f146bSVincenzo Maffione 				       "\"packets_forwarded\":%" PRIu64 ","
280689f146bSVincenzo Maffione 				       "\"packets_dropped\":%" PRIu64 ","
281689f146bSVincenzo Maffione 				       "\"data_forward_rate_Mbps\":%.4f,"
282689f146bSVincenzo Maffione 				       "\"data_drop_rate_Mbps\":%.4f,"
283689f146bSVincenzo Maffione 				       "\"packet_forward_rate_kpps\":%.4f,"
284689f146bSVincenzo Maffione 				       "\"packet_drop_rate_kpps\":%.4f,"
285689f146bSVincenzo Maffione 				       "\"overflow_queue_size\":%" PRIu32
286689f146bSVincenzo Maffione 				       "}", cur.t.tv_sec + (cur.t.tv_usec / 1000000.0),
287689f146bSVincenzo Maffione 				            ports[j].interface,
288689f146bSVincenzo Maffione 				            j,
289689f146bSVincenzo Maffione 				            c->pkts,
290689f146bSVincenzo Maffione 				            c->drop,
291689f146bSVincenzo Maffione 				            (double)bps / 1024 / 1024,
292689f146bSVincenzo Maffione 				            (double)dbps / 1024 / 1024,
293689f146bSVincenzo Maffione 				            (double)pps / 1000,
294689f146bSVincenzo Maffione 				            (double)dps / 1000,
295689f146bSVincenzo Maffione 				            c->oq_n);
296689f146bSVincenzo Maffione 
297689f146bSVincenzo Maffione 			if (dosyslog && stat_msg[0])
298689f146bSVincenzo Maffione 				syslog(LOG_INFO, "%s", stat_msg);
299689f146bSVincenzo Maffione 			if (dostdout && stat_msg[0])
300689f146bSVincenzo Maffione 				printf("%s\n", stat_msg);
301689f146bSVincenzo Maffione 		}
302689f146bSVincenzo Maffione 		if (usec) {
303689f146bSVincenzo Maffione 			x.pkts = cur.pkts - prev.pkts;
304689f146bSVincenzo Maffione 			x.drop = cur.drop - prev.drop;
305689f146bSVincenzo Maffione 			x.bytes = cur.bytes - prev.bytes;
306689f146bSVincenzo Maffione 			x.drop_bytes = cur.drop_bytes - prev.drop_bytes;
307689f146bSVincenzo Maffione 			pps = (x.pkts*1000000 + usec/2) / usec;
308689f146bSVincenzo Maffione 			dps = (x.drop*1000000 + usec/2) / usec;
309689f146bSVincenzo Maffione 			bps = ((x.bytes*1000000 + usec/2) / usec) * 8;
310689f146bSVincenzo Maffione 			dbps = ((x.drop_bytes*1000000 + usec/2) / usec) * 8;
311689f146bSVincenzo Maffione 		}
312689f146bSVincenzo Maffione 
313689f146bSVincenzo Maffione 		if ( (dosyslog || dostdout) && newdata )
314689f146bSVincenzo Maffione 			snprintf(stat_msg, STAT_MSG_MAXSIZE,
315689f146bSVincenzo Maffione 			         "{"
316689f146bSVincenzo Maffione 			         "\"ts\":%.6f,"
317689f146bSVincenzo Maffione 			         "\"interface\":\"%s\","
318689f146bSVincenzo Maffione 			         "\"output_ring\":null,"
319689f146bSVincenzo Maffione 			         "\"packets_received\":%" PRIu64 ","
320689f146bSVincenzo Maffione 			         "\"packets_forwarded\":%" PRIu64 ","
321689f146bSVincenzo Maffione 			         "\"packets_dropped\":%" PRIu64 ","
322689f146bSVincenzo Maffione 			         "\"non_ip_packets\":%" PRIu64 ","
323689f146bSVincenzo Maffione 			         "\"data_forward_rate_Mbps\":%.4f,"
324689f146bSVincenzo Maffione 			         "\"data_drop_rate_Mbps\":%.4f,"
325689f146bSVincenzo Maffione 			         "\"packet_forward_rate_kpps\":%.4f,"
326689f146bSVincenzo Maffione 			         "\"packet_drop_rate_kpps\":%.4f,"
327689f146bSVincenzo Maffione 			         "\"free_buffer_slots\":%" PRIu32
328689f146bSVincenzo Maffione 			         "}", cur.t.tv_sec + (cur.t.tv_usec / 1000000.0),
329689f146bSVincenzo Maffione 			              glob_arg.ifname,
330689f146bSVincenzo Maffione 			              received_pkts,
331689f146bSVincenzo Maffione 			              cur.pkts,
332689f146bSVincenzo Maffione 			              cur.drop,
333689f146bSVincenzo Maffione 			              counters_buf.non_ip,
334689f146bSVincenzo Maffione 			              (double)bps / 1024 / 1024,
335689f146bSVincenzo Maffione 			              (double)dbps / 1024 / 1024,
336689f146bSVincenzo Maffione 			              (double)pps / 1000,
337689f146bSVincenzo Maffione 			              (double)dps / 1000,
338689f146bSVincenzo Maffione 			              counters_buf.freeq_n);
339689f146bSVincenzo Maffione 
340689f146bSVincenzo Maffione 		if (dosyslog && stat_msg[0])
341689f146bSVincenzo Maffione 			syslog(LOG_INFO, "%s", stat_msg);
342689f146bSVincenzo Maffione 		if (dostdout && stat_msg[0])
343689f146bSVincenzo Maffione 			printf("%s\n", stat_msg);
344689f146bSVincenzo Maffione 
345689f146bSVincenzo Maffione 		prev = cur;
346689f146bSVincenzo Maffione 	}
347689f146bSVincenzo Maffione 
348689f146bSVincenzo Maffione 	free(pipe_prev);
349689f146bSVincenzo Maffione 
350689f146bSVincenzo Maffione 	return NULL;
351689f146bSVincenzo Maffione }
352689f146bSVincenzo Maffione 
353689f146bSVincenzo Maffione static void
free_buffers(void)354689f146bSVincenzo Maffione free_buffers(void)
355689f146bSVincenzo Maffione {
356689f146bSVincenzo Maffione 	int i, tot = 0;
357689f146bSVincenzo Maffione 	struct port_des *rxport = &ports[glob_arg.output_rings];
358689f146bSVincenzo Maffione 
359689f146bSVincenzo Maffione 	/* build a netmap free list with the buffers in all the overflow queues */
360689f146bSVincenzo Maffione 	for (i = 0; i < glob_arg.output_rings + 1; i++) {
361689f146bSVincenzo Maffione 		struct port_des *cp = &ports[i];
362689f146bSVincenzo Maffione 		struct overflow_queue *q = cp->oq;
363689f146bSVincenzo Maffione 
364689f146bSVincenzo Maffione 		if (!q)
365689f146bSVincenzo Maffione 			continue;
366689f146bSVincenzo Maffione 
367689f146bSVincenzo Maffione 		while (q->n) {
368689f146bSVincenzo Maffione 			struct netmap_slot s = oq_deq(q);
369689f146bSVincenzo Maffione 			uint32_t *b = (uint32_t *)NETMAP_BUF(cp->ring, s.buf_idx);
370689f146bSVincenzo Maffione 
371689f146bSVincenzo Maffione 			*b = rxport->nmd->nifp->ni_bufs_head;
372689f146bSVincenzo Maffione 			rxport->nmd->nifp->ni_bufs_head = s.buf_idx;
373689f146bSVincenzo Maffione 			tot++;
374689f146bSVincenzo Maffione 		}
375689f146bSVincenzo Maffione 	}
376689f146bSVincenzo Maffione 	D("added %d buffers to netmap free list", tot);
377689f146bSVincenzo Maffione 
378689f146bSVincenzo Maffione 	for (i = 0; i < glob_arg.output_rings + 1; ++i) {
379*73b2e3e5SVincenzo Maffione 		nmport_close(ports[i].nmd);
380689f146bSVincenzo Maffione 	}
381689f146bSVincenzo Maffione }
382689f146bSVincenzo Maffione 
383689f146bSVincenzo Maffione 
sigint_h(int sig)384689f146bSVincenzo Maffione static void sigint_h(int sig)
385689f146bSVincenzo Maffione {
386689f146bSVincenzo Maffione 	(void)sig;		/* UNUSED */
387689f146bSVincenzo Maffione 	do_abort = 1;
388689f146bSVincenzo Maffione 	signal(SIGINT, SIG_DFL);
389689f146bSVincenzo Maffione }
390689f146bSVincenzo Maffione 
usage()3917eb32dc8SVincenzo Maffione static void usage()
392689f146bSVincenzo Maffione {
393689f146bSVincenzo Maffione 	printf("usage: lb [options]\n");
394689f146bSVincenzo Maffione 	printf("where options are:\n");
395689f146bSVincenzo Maffione 	printf("  -h              	view help text\n");
396689f146bSVincenzo Maffione 	printf("  -i iface        	interface name (required)\n");
397689f146bSVincenzo Maffione 	printf("  -p [prefix:]npipes	add a new group of output pipes\n");
398689f146bSVincenzo Maffione 	printf("  -B nbufs        	number of extra buffers (default: %d)\n", DEF_EXTRA_BUFS);
399689f146bSVincenzo Maffione 	printf("  -b batch        	batch size (default: %d)\n", DEF_BATCH);
400689f146bSVincenzo Maffione 	printf("  -w seconds        	wait for link up (default: %d)\n", DEF_WAIT_LINK);
401689f146bSVincenzo Maffione 	printf("  -W                    enable busy waiting. this will run your CPU at 100%%\n");
402689f146bSVincenzo Maffione 	printf("  -s seconds      	seconds between syslog stats messages (default: 0)\n");
403689f146bSVincenzo Maffione 	printf("  -o seconds      	seconds between stdout stats messages (default: 0)\n");
404689f146bSVincenzo Maffione 	exit(0);
405689f146bSVincenzo Maffione }
406689f146bSVincenzo Maffione 
407689f146bSVincenzo Maffione static int
parse_pipes(const char * spec)4087eb32dc8SVincenzo Maffione parse_pipes(const char *spec)
409689f146bSVincenzo Maffione {
4107eb32dc8SVincenzo Maffione 	const char *end = index(spec, ':');
411689f146bSVincenzo Maffione 	static int max_groups = 0;
412689f146bSVincenzo Maffione 	struct group_des *g;
413689f146bSVincenzo Maffione 
414689f146bSVincenzo Maffione 	ND("spec %s num_groups %d", spec, glob_arg.num_groups);
415689f146bSVincenzo Maffione 	if (max_groups < glob_arg.num_groups + 1) {
416689f146bSVincenzo Maffione 		size_t size = sizeof(*g) * (glob_arg.num_groups + 1);
417689f146bSVincenzo Maffione 		groups = realloc(groups, size);
418689f146bSVincenzo Maffione 		if (groups == NULL) {
419689f146bSVincenzo Maffione 			D("out of memory");
420689f146bSVincenzo Maffione 			return 1;
421689f146bSVincenzo Maffione 		}
422689f146bSVincenzo Maffione 	}
423689f146bSVincenzo Maffione 	g = &groups[glob_arg.num_groups];
424689f146bSVincenzo Maffione 	memset(g, 0, sizeof(*g));
425689f146bSVincenzo Maffione 
426689f146bSVincenzo Maffione 	if (end != NULL) {
427689f146bSVincenzo Maffione 		if (end - spec > MAX_IFNAMELEN - 8) {
428689f146bSVincenzo Maffione 			D("name '%s' too long", spec);
429689f146bSVincenzo Maffione 			return 1;
430689f146bSVincenzo Maffione 		}
431689f146bSVincenzo Maffione 		if (end == spec) {
432689f146bSVincenzo Maffione 			D("missing prefix before ':' in '%s'", spec);
433689f146bSVincenzo Maffione 			return 1;
434689f146bSVincenzo Maffione 		}
435689f146bSVincenzo Maffione 		strncpy(g->pipename, spec, end - spec);
436689f146bSVincenzo Maffione 		g->custom_port = 1;
437689f146bSVincenzo Maffione 		end++;
438689f146bSVincenzo Maffione 	} else {
439689f146bSVincenzo Maffione 		/* no prefix, this group will use the
440689f146bSVincenzo Maffione 		 * name of the input port.
441689f146bSVincenzo Maffione 		 * This will be set in init_groups(),
442689f146bSVincenzo Maffione 		 * since here the input port may still
443689f146bSVincenzo Maffione 		 * be uninitialized
444689f146bSVincenzo Maffione 		 */
445689f146bSVincenzo Maffione 		end = spec;
446689f146bSVincenzo Maffione 	}
447689f146bSVincenzo Maffione 	if (*end == '\0') {
448689f146bSVincenzo Maffione 		g->nports = DEF_OUT_PIPES;
449689f146bSVincenzo Maffione 	} else {
450689f146bSVincenzo Maffione 		g->nports = atoi(end);
451689f146bSVincenzo Maffione 		if (g->nports < 1) {
452689f146bSVincenzo Maffione 			D("invalid number of pipes '%s' (must be at least 1)", end);
453689f146bSVincenzo Maffione 			return 1;
454689f146bSVincenzo Maffione 		}
455689f146bSVincenzo Maffione 	}
456689f146bSVincenzo Maffione 	glob_arg.output_rings += g->nports;
457689f146bSVincenzo Maffione 	glob_arg.num_groups++;
458689f146bSVincenzo Maffione 	return 0;
459689f146bSVincenzo Maffione }
460689f146bSVincenzo Maffione 
461689f146bSVincenzo Maffione /* complete the initialization of the groups data structure */
4627eb32dc8SVincenzo Maffione static void
init_groups(void)4637eb32dc8SVincenzo Maffione init_groups(void)
464689f146bSVincenzo Maffione {
465689f146bSVincenzo Maffione 	int i, j, t = 0;
466689f146bSVincenzo Maffione 	struct group_des *g = NULL;
467689f146bSVincenzo Maffione 	for (i = 0; i < glob_arg.num_groups; i++) {
468689f146bSVincenzo Maffione 		g = &groups[i];
469689f146bSVincenzo Maffione 		g->ports = &ports[t];
470689f146bSVincenzo Maffione 		for (j = 0; j < g->nports; j++)
471689f146bSVincenzo Maffione 			g->ports[j].group = g;
472689f146bSVincenzo Maffione 		t += g->nports;
473689f146bSVincenzo Maffione 		if (!g->custom_port)
474689f146bSVincenzo Maffione 			strcpy(g->pipename, glob_arg.base_name);
475689f146bSVincenzo Maffione 		for (j = 0; j < i; j++) {
476689f146bSVincenzo Maffione 			struct group_des *h = &groups[j];
477689f146bSVincenzo Maffione 			if (!strcmp(h->pipename, g->pipename))
478689f146bSVincenzo Maffione 				g->first_id += h->nports;
479689f146bSVincenzo Maffione 		}
480689f146bSVincenzo Maffione 	}
481689f146bSVincenzo Maffione 	g->last = 1;
482689f146bSVincenzo Maffione }
483689f146bSVincenzo Maffione 
484*73b2e3e5SVincenzo Maffione 
485*73b2e3e5SVincenzo Maffione /* To support packets that span multiple slots (NS_MOREFRAG) we
486*73b2e3e5SVincenzo Maffione  * need to make sure of the following:
487*73b2e3e5SVincenzo Maffione  *
488*73b2e3e5SVincenzo Maffione  * - all fragments of the same packet must go to the same output pipe
489*73b2e3e5SVincenzo Maffione  * - when dropping, all fragments of the same packet must be dropped
490*73b2e3e5SVincenzo Maffione  *
491*73b2e3e5SVincenzo Maffione  * For the former point we remember and reuse the last hash computed
492*73b2e3e5SVincenzo Maffione  * in each input ring, and only update it when NS_MOREFRAG was not
493*73b2e3e5SVincenzo Maffione  * set in the last received slot (this marks the start of a new packet).
494*73b2e3e5SVincenzo Maffione  *
495*73b2e3e5SVincenzo Maffione  * For the latter point, we only update the output ring head pointer
496*73b2e3e5SVincenzo Maffione  * when an entire packet has been forwarded. We keep a shadow_head
497*73b2e3e5SVincenzo Maffione  * pointer to know where to put the next partial fragment and,
498*73b2e3e5SVincenzo Maffione  * when the need to drop arises, we roll it back to head.
499*73b2e3e5SVincenzo Maffione  */
500*73b2e3e5SVincenzo Maffione struct morefrag {
501*73b2e3e5SVincenzo Maffione 	uint16_t last_flag;	/* for intput rings */
502*73b2e3e5SVincenzo Maffione 	uint32_t last_hash;	/* for input rings */
503*73b2e3e5SVincenzo Maffione 	uint32_t shadow_head;	/* for output rings */
504*73b2e3e5SVincenzo Maffione };
505*73b2e3e5SVincenzo Maffione 
506689f146bSVincenzo Maffione /* push the packet described by slot rs to the group g.
507689f146bSVincenzo Maffione  * This may cause other buffers to be pushed down the
508689f146bSVincenzo Maffione  * chain headed by g.
509689f146bSVincenzo Maffione  * Return a free buffer.
510689f146bSVincenzo Maffione  */
5117eb32dc8SVincenzo Maffione static uint32_t
forward_packet(struct group_des * g,struct netmap_slot * rs)5127eb32dc8SVincenzo Maffione forward_packet(struct group_des *g, struct netmap_slot *rs)
513689f146bSVincenzo Maffione {
514689f146bSVincenzo Maffione 	uint32_t hash = rs->ptr;
515689f146bSVincenzo Maffione 	uint32_t output_port = hash % g->nports;
516689f146bSVincenzo Maffione 	struct port_des *port = &g->ports[output_port];
517689f146bSVincenzo Maffione 	struct netmap_ring *ring = port->ring;
518689f146bSVincenzo Maffione 	struct overflow_queue *q = port->oq;
519*73b2e3e5SVincenzo Maffione 	struct morefrag *mf = (struct morefrag *)ring->sem;
520*73b2e3e5SVincenzo Maffione 	uint16_t curmf = rs->flags & NS_MOREFRAG;
521689f146bSVincenzo Maffione 
522689f146bSVincenzo Maffione 	/* Move the packet to the output pipe, unless there is
523689f146bSVincenzo Maffione 	 * either no space left on the ring, or there is some
524689f146bSVincenzo Maffione 	 * packet still in the overflow queue (since those must
525689f146bSVincenzo Maffione 	 * take precedence over the new one)
526689f146bSVincenzo Maffione 	*/
527*73b2e3e5SVincenzo Maffione 	if (mf->shadow_head != ring->tail && (q == NULL || oq_empty(q))) {
528*73b2e3e5SVincenzo Maffione 		struct netmap_slot *ts = &ring->slot[mf->shadow_head];
529689f146bSVincenzo Maffione 		struct netmap_slot old_slot = *ts;
530689f146bSVincenzo Maffione 
531689f146bSVincenzo Maffione 		ts->buf_idx = rs->buf_idx;
532689f146bSVincenzo Maffione 		ts->len = rs->len;
533*73b2e3e5SVincenzo Maffione 		ts->flags = rs->flags | NS_BUF_CHANGED;
534689f146bSVincenzo Maffione 		ts->ptr = rs->ptr;
535*73b2e3e5SVincenzo Maffione 		mf->shadow_head = nm_ring_next(ring, mf->shadow_head);
536*73b2e3e5SVincenzo Maffione 		if (!curmf) {
537*73b2e3e5SVincenzo Maffione 			ring->head = mf->shadow_head;
538*73b2e3e5SVincenzo Maffione 		}
539*73b2e3e5SVincenzo Maffione 		ND("curmf %2x ts->flags %2x shadow_head %3u head %3u tail %3u",
540*73b2e3e5SVincenzo Maffione 				curmf, ts->flags, mf->shadow_head, ring->head, ring->tail);
541689f146bSVincenzo Maffione 		port->ctr.bytes += rs->len;
542689f146bSVincenzo Maffione 		port->ctr.pkts++;
543689f146bSVincenzo Maffione 		forwarded++;
544689f146bSVincenzo Maffione 		return old_slot.buf_idx;
545689f146bSVincenzo Maffione 	}
546689f146bSVincenzo Maffione 
547689f146bSVincenzo Maffione 	/* use the overflow queue, if available */
548689f146bSVincenzo Maffione 	if (q == NULL || oq_full(q)) {
549*73b2e3e5SVincenzo Maffione 		uint32_t scan;
550689f146bSVincenzo Maffione 		/* no space left on the ring and no overflow queue
551689f146bSVincenzo Maffione 		 * available: we are forced to drop the packet
552689f146bSVincenzo Maffione 		 */
553*73b2e3e5SVincenzo Maffione 
554*73b2e3e5SVincenzo Maffione 		/* drop previous fragments, if any */
555*73b2e3e5SVincenzo Maffione 		for (scan = ring->head; scan != mf->shadow_head;
556*73b2e3e5SVincenzo Maffione 				scan = nm_ring_next(ring, scan)) {
557*73b2e3e5SVincenzo Maffione 			struct netmap_slot *ts = &ring->slot[scan];
558*73b2e3e5SVincenzo Maffione 			dropped++;
559*73b2e3e5SVincenzo Maffione 			port->ctr.drop_bytes += ts->len;
560*73b2e3e5SVincenzo Maffione 		}
561*73b2e3e5SVincenzo Maffione 		mf->shadow_head = ring->head;
562*73b2e3e5SVincenzo Maffione 
563689f146bSVincenzo Maffione 		dropped++;
564689f146bSVincenzo Maffione 		port->ctr.drop++;
565689f146bSVincenzo Maffione 		port->ctr.drop_bytes += rs->len;
566689f146bSVincenzo Maffione 		return rs->buf_idx;
567689f146bSVincenzo Maffione 	}
568689f146bSVincenzo Maffione 
569689f146bSVincenzo Maffione 	oq_enq(q, rs);
570689f146bSVincenzo Maffione 
571689f146bSVincenzo Maffione 	/*
572689f146bSVincenzo Maffione 	 * we cannot continue down the chain and we need to
573689f146bSVincenzo Maffione 	 * return a free buffer now. We take it from the free queue.
574689f146bSVincenzo Maffione 	 */
575689f146bSVincenzo Maffione 	if (oq_empty(freeq)) {
576689f146bSVincenzo Maffione 		/* the free queue is empty. Revoke some buffers
577689f146bSVincenzo Maffione 		 * from the longest overflow queue
578689f146bSVincenzo Maffione 		 */
579689f146bSVincenzo Maffione 		uint32_t j;
580689f146bSVincenzo Maffione 		struct port_des *lp = &ports[0];
581689f146bSVincenzo Maffione 		uint32_t max = lp->oq->n;
582689f146bSVincenzo Maffione 
583689f146bSVincenzo Maffione 		/* let lp point to the port with the longest queue */
584689f146bSVincenzo Maffione 		for (j = 1; j < glob_arg.output_rings; j++) {
585689f146bSVincenzo Maffione 			struct port_des *cp = &ports[j];
586689f146bSVincenzo Maffione 			if (cp->oq->n > max) {
587689f146bSVincenzo Maffione 				lp = cp;
588689f146bSVincenzo Maffione 				max = cp->oq->n;
589689f146bSVincenzo Maffione 			}
590689f146bSVincenzo Maffione 		}
591689f146bSVincenzo Maffione 
592689f146bSVincenzo Maffione 		/* move the oldest BUF_REVOKE buffers from the
593689f146bSVincenzo Maffione 		 * lp queue to the free queue
594*73b2e3e5SVincenzo Maffione 		 *
595*73b2e3e5SVincenzo Maffione 		 * We cannot revoke a partially received packet.
596*73b2e3e5SVincenzo Maffione 		 * To make thinks simple we make sure to leave
597*73b2e3e5SVincenzo Maffione 		 * at least NETMAP_MAX_FRAGS slots in the queue.
598689f146bSVincenzo Maffione 		 */
599*73b2e3e5SVincenzo Maffione 		for (j = 0; lp->oq->n > NETMAP_MAX_FRAGS && j < BUF_REVOKE; j++) {
600689f146bSVincenzo Maffione 			struct netmap_slot tmp = oq_deq(lp->oq);
601689f146bSVincenzo Maffione 
602689f146bSVincenzo Maffione 			dropped++;
603689f146bSVincenzo Maffione 			lp->ctr.drop++;
604689f146bSVincenzo Maffione 			lp->ctr.drop_bytes += tmp.len;
605689f146bSVincenzo Maffione 
606689f146bSVincenzo Maffione 			oq_enq(freeq, &tmp);
607689f146bSVincenzo Maffione 		}
608689f146bSVincenzo Maffione 
609689f146bSVincenzo Maffione 		ND(1, "revoked %d buffers from %s", j, lq->name);
610689f146bSVincenzo Maffione 	}
611689f146bSVincenzo Maffione 
612689f146bSVincenzo Maffione 	return oq_deq(freeq).buf_idx;
613689f146bSVincenzo Maffione }
614689f146bSVincenzo Maffione 
main(int argc,char ** argv)615689f146bSVincenzo Maffione int main(int argc, char **argv)
616689f146bSVincenzo Maffione {
617689f146bSVincenzo Maffione 	int ch;
618689f146bSVincenzo Maffione 	uint32_t i;
619689f146bSVincenzo Maffione 	int rv;
620689f146bSVincenzo Maffione 	unsigned int iter = 0;
621689f146bSVincenzo Maffione 	int poll_timeout = 10; /* default */
622689f146bSVincenzo Maffione 
623689f146bSVincenzo Maffione 	glob_arg.ifname[0] = '\0';
624689f146bSVincenzo Maffione 	glob_arg.output_rings = 0;
625689f146bSVincenzo Maffione 	glob_arg.batch = DEF_BATCH;
626689f146bSVincenzo Maffione 	glob_arg.wait_link = DEF_WAIT_LINK;
627689f146bSVincenzo Maffione 	glob_arg.busy_wait = false;
628689f146bSVincenzo Maffione 	glob_arg.syslog_interval = 0;
629689f146bSVincenzo Maffione 	glob_arg.stdout_interval = 0;
630689f146bSVincenzo Maffione 
631689f146bSVincenzo Maffione 	while ( (ch = getopt(argc, argv, "hi:p:b:B:s:o:w:W")) != -1) {
632689f146bSVincenzo Maffione 		switch (ch) {
633689f146bSVincenzo Maffione 		case 'i':
634689f146bSVincenzo Maffione 			D("interface is %s", optarg);
635689f146bSVincenzo Maffione 			if (strlen(optarg) > MAX_IFNAMELEN - 8) {
636689f146bSVincenzo Maffione 				D("ifname too long %s", optarg);
637689f146bSVincenzo Maffione 				return 1;
638689f146bSVincenzo Maffione 			}
639689f146bSVincenzo Maffione 			if (strncmp(optarg, "netmap:", 7) && strncmp(optarg, "vale", 4)) {
640689f146bSVincenzo Maffione 				sprintf(glob_arg.ifname, "netmap:%s", optarg);
641689f146bSVincenzo Maffione 			} else {
642689f146bSVincenzo Maffione 				strcpy(glob_arg.ifname, optarg);
643689f146bSVincenzo Maffione 			}
644689f146bSVincenzo Maffione 			break;
645689f146bSVincenzo Maffione 
646689f146bSVincenzo Maffione 		case 'p':
647689f146bSVincenzo Maffione 			if (parse_pipes(optarg)) {
648689f146bSVincenzo Maffione 				usage();
649689f146bSVincenzo Maffione 				return 1;
650689f146bSVincenzo Maffione 			}
651689f146bSVincenzo Maffione 			break;
652689f146bSVincenzo Maffione 
653689f146bSVincenzo Maffione 		case 'B':
654689f146bSVincenzo Maffione 			glob_arg.extra_bufs = atoi(optarg);
655689f146bSVincenzo Maffione 			D("requested %d extra buffers", glob_arg.extra_bufs);
656689f146bSVincenzo Maffione 			break;
657689f146bSVincenzo Maffione 
658689f146bSVincenzo Maffione 		case 'b':
659689f146bSVincenzo Maffione 			glob_arg.batch = atoi(optarg);
660689f146bSVincenzo Maffione 			D("batch is %d", glob_arg.batch);
661689f146bSVincenzo Maffione 			break;
662689f146bSVincenzo Maffione 
663689f146bSVincenzo Maffione 		case 'w':
664689f146bSVincenzo Maffione 			glob_arg.wait_link = atoi(optarg);
665689f146bSVincenzo Maffione 			D("link wait for up time is %d", glob_arg.wait_link);
666689f146bSVincenzo Maffione 			break;
667689f146bSVincenzo Maffione 
668689f146bSVincenzo Maffione 		case 'W':
669689f146bSVincenzo Maffione 			glob_arg.busy_wait = true;
670689f146bSVincenzo Maffione 			break;
671689f146bSVincenzo Maffione 
672689f146bSVincenzo Maffione 		case 'o':
673689f146bSVincenzo Maffione 			glob_arg.stdout_interval = atoi(optarg);
674689f146bSVincenzo Maffione 			break;
675689f146bSVincenzo Maffione 
676689f146bSVincenzo Maffione 		case 's':
677689f146bSVincenzo Maffione 			glob_arg.syslog_interval = atoi(optarg);
678689f146bSVincenzo Maffione 			break;
679689f146bSVincenzo Maffione 
680689f146bSVincenzo Maffione 		case 'h':
681689f146bSVincenzo Maffione 			usage();
682689f146bSVincenzo Maffione 			return 0;
683689f146bSVincenzo Maffione 			break;
684689f146bSVincenzo Maffione 
685689f146bSVincenzo Maffione 		default:
686689f146bSVincenzo Maffione 			D("bad option %c %s", ch, optarg);
687689f146bSVincenzo Maffione 			usage();
688689f146bSVincenzo Maffione 			return 1;
689689f146bSVincenzo Maffione 		}
690689f146bSVincenzo Maffione 	}
691689f146bSVincenzo Maffione 
692689f146bSVincenzo Maffione 	if (glob_arg.ifname[0] == '\0') {
693689f146bSVincenzo Maffione 		D("missing interface name");
694689f146bSVincenzo Maffione 		usage();
695689f146bSVincenzo Maffione 		return 1;
696689f146bSVincenzo Maffione 	}
697689f146bSVincenzo Maffione 
698689f146bSVincenzo Maffione 	if (glob_arg.num_groups == 0)
699689f146bSVincenzo Maffione 		parse_pipes("");
700689f146bSVincenzo Maffione 
701689f146bSVincenzo Maffione 	if (glob_arg.syslog_interval) {
702689f146bSVincenzo Maffione 		setlogmask(LOG_UPTO(LOG_INFO));
703689f146bSVincenzo Maffione 		openlog("lb", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
704689f146bSVincenzo Maffione 	}
705689f146bSVincenzo Maffione 
706689f146bSVincenzo Maffione 	uint32_t npipes = glob_arg.output_rings;
707689f146bSVincenzo Maffione 
708689f146bSVincenzo Maffione 
709689f146bSVincenzo Maffione 	pthread_t stat_thread;
710689f146bSVincenzo Maffione 
711689f146bSVincenzo Maffione 	ports = calloc(npipes + 1, sizeof(struct port_des));
712689f146bSVincenzo Maffione 	if (!ports) {
713689f146bSVincenzo Maffione 		D("failed to allocate the stats array");
714689f146bSVincenzo Maffione 		return 1;
715689f146bSVincenzo Maffione 	}
716689f146bSVincenzo Maffione 	struct port_des *rxport = &ports[npipes];
717*73b2e3e5SVincenzo Maffione 
718*73b2e3e5SVincenzo Maffione 	rxport->nmd = nmport_prepare(glob_arg.ifname);
719*73b2e3e5SVincenzo Maffione 	if (rxport->nmd == NULL) {
720*73b2e3e5SVincenzo Maffione 		D("cannot parse %s", glob_arg.ifname);
721*73b2e3e5SVincenzo Maffione 		return (1);
722*73b2e3e5SVincenzo Maffione 	}
723*73b2e3e5SVincenzo Maffione 	/* extract the base name */
724*73b2e3e5SVincenzo Maffione 	strncpy(glob_arg.base_name, rxport->nmd->hdr.nr_name, MAX_IFNAMELEN);
725*73b2e3e5SVincenzo Maffione 
726689f146bSVincenzo Maffione 	init_groups();
727689f146bSVincenzo Maffione 
728689f146bSVincenzo Maffione 	memset(&counters_buf, 0, sizeof(counters_buf));
729689f146bSVincenzo Maffione 	counters_buf.ctrs = calloc(npipes, sizeof(struct my_ctrs));
730689f146bSVincenzo Maffione 	if (!counters_buf.ctrs) {
731689f146bSVincenzo Maffione 		D("failed to allocate the counters snapshot buffer");
732689f146bSVincenzo Maffione 		return 1;
733689f146bSVincenzo Maffione 	}
734689f146bSVincenzo Maffione 
735*73b2e3e5SVincenzo Maffione 	rxport->nmd->reg.nr_extra_bufs = glob_arg.extra_bufs;
736689f146bSVincenzo Maffione 
737*73b2e3e5SVincenzo Maffione 	if (nmport_open_desc(rxport->nmd) < 0) {
738689f146bSVincenzo Maffione 		D("cannot open %s", glob_arg.ifname);
739689f146bSVincenzo Maffione 		return (1);
740689f146bSVincenzo Maffione 	}
741*73b2e3e5SVincenzo Maffione 	D("successfully opened %s", glob_arg.ifname);
742689f146bSVincenzo Maffione 
743*73b2e3e5SVincenzo Maffione 	uint32_t extra_bufs = rxport->nmd->reg.nr_extra_bufs;
744689f146bSVincenzo Maffione 	struct overflow_queue *oq = NULL;
745689f146bSVincenzo Maffione 	/* reference ring to access the buffers */
746689f146bSVincenzo Maffione 	rxport->ring = NETMAP_RXRING(rxport->nmd->nifp, 0);
747689f146bSVincenzo Maffione 
748689f146bSVincenzo Maffione 	if (!glob_arg.extra_bufs)
749689f146bSVincenzo Maffione 		goto run;
750689f146bSVincenzo Maffione 
751689f146bSVincenzo Maffione 	D("obtained %d extra buffers", extra_bufs);
752689f146bSVincenzo Maffione 	if (!extra_bufs)
753689f146bSVincenzo Maffione 		goto run;
754689f146bSVincenzo Maffione 
755689f146bSVincenzo Maffione 	/* one overflow queue for each output pipe, plus one for the
756689f146bSVincenzo Maffione 	 * free extra buffers
757689f146bSVincenzo Maffione 	 */
758689f146bSVincenzo Maffione 	oq = calloc(npipes + 1, sizeof(struct overflow_queue));
759689f146bSVincenzo Maffione 	if (!oq) {
760689f146bSVincenzo Maffione 		D("failed to allocated overflow queues descriptors");
761689f146bSVincenzo Maffione 		goto run;
762689f146bSVincenzo Maffione 	}
763689f146bSVincenzo Maffione 
764689f146bSVincenzo Maffione 	freeq = &oq[npipes];
765689f146bSVincenzo Maffione 	rxport->oq = freeq;
766689f146bSVincenzo Maffione 
767689f146bSVincenzo Maffione 	freeq->slots = calloc(extra_bufs, sizeof(struct netmap_slot));
768689f146bSVincenzo Maffione 	if (!freeq->slots) {
769689f146bSVincenzo Maffione 		D("failed to allocate the free list");
770689f146bSVincenzo Maffione 	}
771689f146bSVincenzo Maffione 	freeq->size = extra_bufs;
772689f146bSVincenzo Maffione 	snprintf(freeq->name, MAX_IFNAMELEN, "free queue");
773689f146bSVincenzo Maffione 
774689f146bSVincenzo Maffione 	/*
775689f146bSVincenzo Maffione 	 * the list of buffers uses the first uint32_t in each buffer
776689f146bSVincenzo Maffione 	 * as the index of the next buffer.
777689f146bSVincenzo Maffione 	 */
778689f146bSVincenzo Maffione 	uint32_t scan;
779689f146bSVincenzo Maffione 	for (scan = rxport->nmd->nifp->ni_bufs_head;
780689f146bSVincenzo Maffione 	     scan;
781689f146bSVincenzo Maffione 	     scan = *(uint32_t *)NETMAP_BUF(rxport->ring, scan))
782689f146bSVincenzo Maffione 	{
783689f146bSVincenzo Maffione 		struct netmap_slot s;
784689f146bSVincenzo Maffione 		s.len = s.flags = 0;
785689f146bSVincenzo Maffione 		s.ptr = 0;
786689f146bSVincenzo Maffione 		s.buf_idx = scan;
787689f146bSVincenzo Maffione 		ND("freeq <- %d", s.buf_idx);
788689f146bSVincenzo Maffione 		oq_enq(freeq, &s);
789689f146bSVincenzo Maffione 	}
790689f146bSVincenzo Maffione 
791689f146bSVincenzo Maffione 
792689f146bSVincenzo Maffione 	if (freeq->n != extra_bufs) {
793689f146bSVincenzo Maffione 		D("something went wrong: netmap reported %d extra_bufs, but the free list contained %d",
794689f146bSVincenzo Maffione 				extra_bufs, freeq->n);
795689f146bSVincenzo Maffione 		return 1;
796689f146bSVincenzo Maffione 	}
797689f146bSVincenzo Maffione 	rxport->nmd->nifp->ni_bufs_head = 0;
798689f146bSVincenzo Maffione 
799689f146bSVincenzo Maffione run:
800689f146bSVincenzo Maffione 	atexit(free_buffers);
801689f146bSVincenzo Maffione 
802689f146bSVincenzo Maffione 	int j, t = 0;
803689f146bSVincenzo Maffione 	for (j = 0; j < glob_arg.num_groups; j++) {
804689f146bSVincenzo Maffione 		struct group_des *g = &groups[j];
805689f146bSVincenzo Maffione 		int k;
806689f146bSVincenzo Maffione 		for (k = 0; k < g->nports; ++k) {
807689f146bSVincenzo Maffione 			struct port_des *p = &g->ports[k];
808689f146bSVincenzo Maffione 			snprintf(p->interface, MAX_PORTNAMELEN, "%s%s{%d/xT@%d",
809689f146bSVincenzo Maffione 					(strncmp(g->pipename, "vale", 4) ? "netmap:" : ""),
810689f146bSVincenzo Maffione 					g->pipename, g->first_id + k,
811*73b2e3e5SVincenzo Maffione 					rxport->nmd->reg.nr_mem_id);
812689f146bSVincenzo Maffione 			D("opening pipe named %s", p->interface);
813689f146bSVincenzo Maffione 
814*73b2e3e5SVincenzo Maffione 			p->nmd = nmport_open(p->interface);
815689f146bSVincenzo Maffione 
816689f146bSVincenzo Maffione 			if (p->nmd == NULL) {
817689f146bSVincenzo Maffione 				D("cannot open %s", p->interface);
818689f146bSVincenzo Maffione 				return (1);
819*73b2e3e5SVincenzo Maffione 			} else if (p->nmd->mem != rxport->nmd->mem) {
820689f146bSVincenzo Maffione 				D("failed to open pipe #%d in zero-copy mode, "
821689f146bSVincenzo Maffione 					"please close any application that uses either pipe %s}%d, "
822689f146bSVincenzo Maffione 				        "or %s{%d, and retry",
823689f146bSVincenzo Maffione 					k + 1, g->pipename, g->first_id + k, g->pipename, g->first_id + k);
824689f146bSVincenzo Maffione 				return (1);
825689f146bSVincenzo Maffione 			} else {
826*73b2e3e5SVincenzo Maffione 				struct morefrag *mf;
827*73b2e3e5SVincenzo Maffione 
828689f146bSVincenzo Maffione 				D("successfully opened pipe #%d %s (tx slots: %d)",
829*73b2e3e5SVincenzo Maffione 				  k + 1, p->interface, p->nmd->reg.nr_tx_slots);
830689f146bSVincenzo Maffione 				p->ring = NETMAP_TXRING(p->nmd->nifp, 0);
831689f146bSVincenzo Maffione 				p->last_tail = nm_ring_next(p->ring, p->ring->tail);
832*73b2e3e5SVincenzo Maffione 				mf = (struct morefrag *)p->ring->sem;
833*73b2e3e5SVincenzo Maffione 				mf->last_flag = 0;	/* unused */
834*73b2e3e5SVincenzo Maffione 				mf->last_hash = 0;	/* unused */
835*73b2e3e5SVincenzo Maffione 				mf->shadow_head = p->ring->head;
836689f146bSVincenzo Maffione 			}
837689f146bSVincenzo Maffione 			D("zerocopy %s",
838689f146bSVincenzo Maffione 			  (rxport->nmd->mem == p->nmd->mem) ? "enabled" : "disabled");
839689f146bSVincenzo Maffione 
840689f146bSVincenzo Maffione 			if (extra_bufs) {
841689f146bSVincenzo Maffione 				struct overflow_queue *q = &oq[t + k];
842689f146bSVincenzo Maffione 				q->slots = calloc(extra_bufs, sizeof(struct netmap_slot));
843689f146bSVincenzo Maffione 				if (!q->slots) {
844689f146bSVincenzo Maffione 					D("failed to allocate overflow queue for pipe %d", k);
845689f146bSVincenzo Maffione 					/* make all overflow queue management fail */
846689f146bSVincenzo Maffione 					extra_bufs = 0;
847689f146bSVincenzo Maffione 				}
848689f146bSVincenzo Maffione 				q->size = extra_bufs;
849689f146bSVincenzo Maffione 				snprintf(q->name, sizeof(q->name), "oq %s{%4d", g->pipename, k);
850689f146bSVincenzo Maffione 				p->oq = q;
851689f146bSVincenzo Maffione 			}
852689f146bSVincenzo Maffione 		}
853689f146bSVincenzo Maffione 		t += g->nports;
854689f146bSVincenzo Maffione 	}
855689f146bSVincenzo Maffione 
856689f146bSVincenzo Maffione 	if (glob_arg.extra_bufs && !extra_bufs) {
857689f146bSVincenzo Maffione 		if (oq) {
858689f146bSVincenzo Maffione 			for (i = 0; i < npipes + 1; i++) {
859689f146bSVincenzo Maffione 				free(oq[i].slots);
860689f146bSVincenzo Maffione 				oq[i].slots = NULL;
861689f146bSVincenzo Maffione 			}
862689f146bSVincenzo Maffione 			free(oq);
863689f146bSVincenzo Maffione 			oq = NULL;
864689f146bSVincenzo Maffione 		}
865689f146bSVincenzo Maffione 		D("*** overflow queues disabled ***");
866689f146bSVincenzo Maffione 	}
867689f146bSVincenzo Maffione 
868689f146bSVincenzo Maffione 	sleep(glob_arg.wait_link);
869689f146bSVincenzo Maffione 
870689f146bSVincenzo Maffione 	/* start stats thread after wait_link */
871689f146bSVincenzo Maffione 	if (pthread_create(&stat_thread, NULL, print_stats, NULL) == -1) {
872689f146bSVincenzo Maffione 		D("unable to create the stats thread: %s", strerror(errno));
873689f146bSVincenzo Maffione 		return 1;
874689f146bSVincenzo Maffione 	}
875689f146bSVincenzo Maffione 
876689f146bSVincenzo Maffione 	struct pollfd pollfd[npipes + 1];
877689f146bSVincenzo Maffione 	memset(&pollfd, 0, sizeof(pollfd));
878689f146bSVincenzo Maffione 	signal(SIGINT, sigint_h);
879689f146bSVincenzo Maffione 
880689f146bSVincenzo Maffione 	/* make sure we wake up as often as needed, even when there are no
881689f146bSVincenzo Maffione 	 * packets coming in
882689f146bSVincenzo Maffione 	 */
883689f146bSVincenzo Maffione 	if (glob_arg.syslog_interval > 0 && glob_arg.syslog_interval < poll_timeout)
884689f146bSVincenzo Maffione 		poll_timeout = glob_arg.syslog_interval;
885689f146bSVincenzo Maffione 	if (glob_arg.stdout_interval > 0 && glob_arg.stdout_interval < poll_timeout)
886689f146bSVincenzo Maffione 		poll_timeout = glob_arg.stdout_interval;
887689f146bSVincenzo Maffione 
888*73b2e3e5SVincenzo Maffione 	/* initialize the morefrag structures for the input rings */
889*73b2e3e5SVincenzo Maffione 	for (i = rxport->nmd->first_rx_ring; i <= rxport->nmd->last_rx_ring; i++) {
890*73b2e3e5SVincenzo Maffione 		struct netmap_ring *rxring = NETMAP_RXRING(rxport->nmd->nifp, i);
891*73b2e3e5SVincenzo Maffione 		struct morefrag *mf = (struct morefrag *)rxring->sem;
892*73b2e3e5SVincenzo Maffione 
893*73b2e3e5SVincenzo Maffione 		mf->last_flag = 0;
894*73b2e3e5SVincenzo Maffione 		mf->last_hash = 0;
895*73b2e3e5SVincenzo Maffione 		mf->shadow_head = 0; /* unused */
896*73b2e3e5SVincenzo Maffione 	}
897*73b2e3e5SVincenzo Maffione 
898689f146bSVincenzo Maffione 	while (!do_abort) {
899689f146bSVincenzo Maffione 		u_int polli = 0;
900689f146bSVincenzo Maffione 		iter++;
901689f146bSVincenzo Maffione 
902689f146bSVincenzo Maffione 		for (i = 0; i < npipes; ++i) {
903689f146bSVincenzo Maffione 			struct netmap_ring *ring = ports[i].ring;
904689f146bSVincenzo Maffione 			int pending = nm_tx_pending(ring);
905689f146bSVincenzo Maffione 
906689f146bSVincenzo Maffione 			/* if there are packets pending, we want to be notified when
907689f146bSVincenzo Maffione 			 * tail moves, so we let cur=tail
908689f146bSVincenzo Maffione 			 */
909689f146bSVincenzo Maffione 			ring->cur = pending ? ring->tail : ring->head;
910689f146bSVincenzo Maffione 
911689f146bSVincenzo Maffione 			if (!glob_arg.busy_wait && !pending) {
912689f146bSVincenzo Maffione 				/* no need to poll, there are no packets pending */
913689f146bSVincenzo Maffione 				continue;
914689f146bSVincenzo Maffione 			}
915689f146bSVincenzo Maffione 			pollfd[polli].fd = ports[i].nmd->fd;
916689f146bSVincenzo Maffione 			pollfd[polli].events = POLLOUT;
917689f146bSVincenzo Maffione 			pollfd[polli].revents = 0;
918689f146bSVincenzo Maffione 			++polli;
919689f146bSVincenzo Maffione 		}
920689f146bSVincenzo Maffione 
921689f146bSVincenzo Maffione 		pollfd[polli].fd = rxport->nmd->fd;
922689f146bSVincenzo Maffione 		pollfd[polli].events = POLLIN;
923689f146bSVincenzo Maffione 		pollfd[polli].revents = 0;
924689f146bSVincenzo Maffione 		++polli;
925689f146bSVincenzo Maffione 
926*73b2e3e5SVincenzo Maffione 		ND(5, "polling %d file descriptors", polli);
927689f146bSVincenzo Maffione 		rv = poll(pollfd, polli, poll_timeout);
928689f146bSVincenzo Maffione 		if (rv <= 0) {
929689f146bSVincenzo Maffione 			if (rv < 0 && errno != EAGAIN && errno != EINTR)
930689f146bSVincenzo Maffione 				RD(1, "poll error %s", strerror(errno));
931689f146bSVincenzo Maffione 			goto send_stats;
932689f146bSVincenzo Maffione 		}
933689f146bSVincenzo Maffione 
934689f146bSVincenzo Maffione 		/* if there are several groups, try pushing released packets from
935689f146bSVincenzo Maffione 		 * upstream groups to the downstream ones.
936689f146bSVincenzo Maffione 		 *
937689f146bSVincenzo Maffione 		 * It is important to do this before returned slots are reused
938689f146bSVincenzo Maffione 		 * for new transmissions. For the same reason, this must be
939689f146bSVincenzo Maffione 		 * done starting from the last group going backwards.
940689f146bSVincenzo Maffione 		 */
941689f146bSVincenzo Maffione 		for (i = glob_arg.num_groups - 1U; i > 0; i--) {
942689f146bSVincenzo Maffione 			struct group_des *g = &groups[i - 1];
943689f146bSVincenzo Maffione 
944689f146bSVincenzo Maffione 			for (j = 0; j < g->nports; j++) {
945689f146bSVincenzo Maffione 				struct port_des *p = &g->ports[j];
946689f146bSVincenzo Maffione 				struct netmap_ring *ring = p->ring;
947689f146bSVincenzo Maffione 				uint32_t last = p->last_tail,
948689f146bSVincenzo Maffione 					 stop = nm_ring_next(ring, ring->tail);
949689f146bSVincenzo Maffione 
950689f146bSVincenzo Maffione 				/* slight abuse of the API here: we touch the slot
951689f146bSVincenzo Maffione 				 * pointed to by tail
952689f146bSVincenzo Maffione 				 */
953689f146bSVincenzo Maffione 				for ( ; last != stop; last = nm_ring_next(ring, last)) {
954689f146bSVincenzo Maffione 					struct netmap_slot *rs = &ring->slot[last];
955689f146bSVincenzo Maffione 					// XXX less aggressive?
956689f146bSVincenzo Maffione 					rs->buf_idx = forward_packet(g + 1, rs);
957*73b2e3e5SVincenzo Maffione 					rs->flags = NS_BUF_CHANGED;
958689f146bSVincenzo Maffione 					rs->ptr = 0;
959689f146bSVincenzo Maffione 				}
960689f146bSVincenzo Maffione 				p->last_tail = last;
961689f146bSVincenzo Maffione 			}
962689f146bSVincenzo Maffione 		}
963689f146bSVincenzo Maffione 
964689f146bSVincenzo Maffione 
965689f146bSVincenzo Maffione 
966689f146bSVincenzo Maffione 		if (oq) {
967689f146bSVincenzo Maffione 			/* try to push packets from the overflow queues
968689f146bSVincenzo Maffione 			 * to the corresponding pipes
969689f146bSVincenzo Maffione 			 */
970689f146bSVincenzo Maffione 			for (i = 0; i < npipes; i++) {
971689f146bSVincenzo Maffione 				struct port_des *p = &ports[i];
972689f146bSVincenzo Maffione 				struct overflow_queue *q = p->oq;
973*73b2e3e5SVincenzo Maffione 				uint32_t k;
974*73b2e3e5SVincenzo Maffione 				int64_t lim;
975689f146bSVincenzo Maffione 				struct netmap_ring *ring;
976689f146bSVincenzo Maffione 				struct netmap_slot *slot;
977*73b2e3e5SVincenzo Maffione 				struct morefrag *mf;
978689f146bSVincenzo Maffione 
979689f146bSVincenzo Maffione 				if (oq_empty(q))
980689f146bSVincenzo Maffione 					continue;
981689f146bSVincenzo Maffione 				ring = p->ring;
982*73b2e3e5SVincenzo Maffione 				mf = (struct morefrag *)ring->sem;
983*73b2e3e5SVincenzo Maffione 				lim = ring->tail - mf->shadow_head;
984689f146bSVincenzo Maffione 				if (!lim)
985689f146bSVincenzo Maffione 					continue;
986*73b2e3e5SVincenzo Maffione 				if (lim < 0)
987*73b2e3e5SVincenzo Maffione 					lim += ring->num_slots;
988689f146bSVincenzo Maffione 				if (q->n < lim)
989689f146bSVincenzo Maffione 					lim = q->n;
9907eb32dc8SVincenzo Maffione 				for (k = 0; k < lim; k++) {
991689f146bSVincenzo Maffione 					struct netmap_slot s = oq_deq(q), tmp;
992689f146bSVincenzo Maffione 					tmp.ptr = 0;
993*73b2e3e5SVincenzo Maffione 					slot = &ring->slot[mf->shadow_head];
994689f146bSVincenzo Maffione 					tmp.buf_idx = slot->buf_idx;
995689f146bSVincenzo Maffione 					oq_enq(freeq, &tmp);
996689f146bSVincenzo Maffione 					*slot = s;
997689f146bSVincenzo Maffione 					slot->flags |= NS_BUF_CHANGED;
998*73b2e3e5SVincenzo Maffione 					mf->shadow_head = nm_ring_next(ring, mf->shadow_head);
999*73b2e3e5SVincenzo Maffione 					if (!(slot->flags & NS_MOREFRAG))
1000*73b2e3e5SVincenzo Maffione 						ring->head = mf->shadow_head;
1001689f146bSVincenzo Maffione 				}
1002689f146bSVincenzo Maffione 			}
1003689f146bSVincenzo Maffione 		}
1004689f146bSVincenzo Maffione 
1005689f146bSVincenzo Maffione 		/* push any new packets from the input port to the first group */
1006689f146bSVincenzo Maffione 		int batch = 0;
1007689f146bSVincenzo Maffione 		for (i = rxport->nmd->first_rx_ring; i <= rxport->nmd->last_rx_ring; i++) {
1008689f146bSVincenzo Maffione 			struct netmap_ring *rxring = NETMAP_RXRING(rxport->nmd->nifp, i);
1009*73b2e3e5SVincenzo Maffione 			struct morefrag *mf = (struct morefrag *)rxring->sem;
1010689f146bSVincenzo Maffione 
1011689f146bSVincenzo Maffione 			//D("prepare to scan rings");
1012760fa2abSVincenzo Maffione 			int next_head = rxring->head;
1013760fa2abSVincenzo Maffione 			struct netmap_slot *next_slot = &rxring->slot[next_head];
1014689f146bSVincenzo Maffione 			const char *next_buf = NETMAP_BUF(rxring, next_slot->buf_idx);
1015689f146bSVincenzo Maffione 			while (!nm_ring_empty(rxring)) {
1016689f146bSVincenzo Maffione 				struct netmap_slot *rs = next_slot;
1017689f146bSVincenzo Maffione 				struct group_des *g = &groups[0];
1018689f146bSVincenzo Maffione 				++received_pkts;
1019689f146bSVincenzo Maffione 				received_bytes += rs->len;
1020689f146bSVincenzo Maffione 
1021689f146bSVincenzo Maffione 				// CHOOSE THE CORRECT OUTPUT PIPE
1022*73b2e3e5SVincenzo Maffione 				// If the previous slot had NS_MOREFRAG set, this is another
1023*73b2e3e5SVincenzo Maffione 				// fragment of the last packet and it should go to the same
1024*73b2e3e5SVincenzo Maffione 				// output pipe as before.
1025*73b2e3e5SVincenzo Maffione 				if (!mf->last_flag) {
1026*73b2e3e5SVincenzo Maffione 					// 'B' is just a hashing seed
1027*73b2e3e5SVincenzo Maffione 					mf->last_hash = pkt_hdr_hash((const unsigned char *)next_buf, 4, 'B');
1028*73b2e3e5SVincenzo Maffione 				}
1029*73b2e3e5SVincenzo Maffione 				mf->last_flag = rs->flags & NS_MOREFRAG;
1030*73b2e3e5SVincenzo Maffione 				rs->ptr = mf->last_hash;
1031689f146bSVincenzo Maffione 				if (rs->ptr == 0) {
1032689f146bSVincenzo Maffione 					non_ip++; // XXX ??
1033689f146bSVincenzo Maffione 				}
1034689f146bSVincenzo Maffione 				// prefetch the buffer for the next round
1035760fa2abSVincenzo Maffione 				next_head = nm_ring_next(rxring, next_head);
1036760fa2abSVincenzo Maffione 				next_slot = &rxring->slot[next_head];
1037689f146bSVincenzo Maffione 				next_buf = NETMAP_BUF(rxring, next_slot->buf_idx);
1038689f146bSVincenzo Maffione 				__builtin_prefetch(next_buf);
1039689f146bSVincenzo Maffione 				rs->buf_idx = forward_packet(g, rs);
1040*73b2e3e5SVincenzo Maffione 				rs->flags = NS_BUF_CHANGED;
1041760fa2abSVincenzo Maffione 				rxring->head = rxring->cur = next_head;
1042689f146bSVincenzo Maffione 
1043689f146bSVincenzo Maffione 				batch++;
1044689f146bSVincenzo Maffione 				if (unlikely(batch >= glob_arg.batch)) {
1045689f146bSVincenzo Maffione 					ioctl(rxport->nmd->fd, NIOCRXSYNC, NULL);
1046689f146bSVincenzo Maffione 					batch = 0;
1047689f146bSVincenzo Maffione 				}
1048689f146bSVincenzo Maffione 				ND(1,
1049689f146bSVincenzo Maffione 				   "Forwarded Packets: %"PRIu64" Dropped packets: %"PRIu64"   Percent: %.2f",
1050689f146bSVincenzo Maffione 				   forwarded, dropped,
1051689f146bSVincenzo Maffione 				   ((float)dropped / (float)forwarded * 100));
1052689f146bSVincenzo Maffione 			}
1053689f146bSVincenzo Maffione 
1054689f146bSVincenzo Maffione 		}
1055689f146bSVincenzo Maffione 
1056689f146bSVincenzo Maffione 	send_stats:
1057689f146bSVincenzo Maffione 		if (counters_buf.status == COUNTERS_FULL)
1058689f146bSVincenzo Maffione 			continue;
1059689f146bSVincenzo Maffione 		/* take a new snapshot of the counters */
1060689f146bSVincenzo Maffione 		gettimeofday(&counters_buf.ts, NULL);
1061689f146bSVincenzo Maffione 		for (i = 0; i < npipes; i++) {
1062689f146bSVincenzo Maffione 			struct my_ctrs *c = &counters_buf.ctrs[i];
1063689f146bSVincenzo Maffione 			*c = ports[i].ctr;
1064689f146bSVincenzo Maffione 			/*
1065689f146bSVincenzo Maffione 			 * If there are overflow queues, copy the number of them for each
1066689f146bSVincenzo Maffione 			 * port to the ctrs.oq_n variable for each port.
1067689f146bSVincenzo Maffione 			 */
1068689f146bSVincenzo Maffione 			if (ports[i].oq != NULL)
1069689f146bSVincenzo Maffione 				c->oq_n = ports[i].oq->n;
1070689f146bSVincenzo Maffione 		}
1071689f146bSVincenzo Maffione 		counters_buf.received_pkts = received_pkts;
1072689f146bSVincenzo Maffione 		counters_buf.received_bytes = received_bytes;
1073689f146bSVincenzo Maffione 		counters_buf.non_ip = non_ip;
1074689f146bSVincenzo Maffione 		if (freeq != NULL)
1075689f146bSVincenzo Maffione 			counters_buf.freeq_n = freeq->n;
1076689f146bSVincenzo Maffione 		__sync_synchronize();
1077689f146bSVincenzo Maffione 		counters_buf.status = COUNTERS_FULL;
1078689f146bSVincenzo Maffione 	}
1079689f146bSVincenzo Maffione 
1080689f146bSVincenzo Maffione 	/*
1081689f146bSVincenzo Maffione 	 * If freeq exists, copy the number to the freeq_n member of the
1082689f146bSVincenzo Maffione 	 * message struct, otherwise set it to 0.
1083689f146bSVincenzo Maffione 	 */
1084689f146bSVincenzo Maffione 	if (freeq != NULL) {
1085689f146bSVincenzo Maffione 		freeq_n = freeq->n;
1086689f146bSVincenzo Maffione 	} else {
1087689f146bSVincenzo Maffione 		freeq_n = 0;
1088689f146bSVincenzo Maffione 	}
1089689f146bSVincenzo Maffione 
1090689f146bSVincenzo Maffione 	pthread_join(stat_thread, NULL);
1091689f146bSVincenzo Maffione 
1092689f146bSVincenzo Maffione 	printf("%"PRIu64" packets forwarded.  %"PRIu64" packets dropped. Total %"PRIu64"\n", forwarded,
1093689f146bSVincenzo Maffione 	       dropped, forwarded + dropped);
1094689f146bSVincenzo Maffione 	return 0;
1095689f146bSVincenzo Maffione }
1096