Skip to content

Commit 9bd45d2

Browse files
solutions
1 parent 603978b commit 9bd45d2

File tree

4 files changed

+1175
-0
lines changed

4 files changed

+1175
-0
lines changed

solutions/fe.c

+386
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
1+
/*
2+
* This program reads UDP packets from the first netmap port and
3+
* selectively forwards them to the second or third port, depending
4+
* on the UDP destination port. The user can specify two UDP ports A and
5+
* B by command line: packets with destination port A will be forwarded
6+
* to the second netmap port; packets with destination port B will be
7+
* forwarded to the third netmap port; all the other packets are
8+
* dropped.
9+
*/
10+
#include <stdio.h>
11+
#include <stdlib.h>
12+
#include <unistd.h>
13+
#include <signal.h>
14+
#include <errno.h>
15+
#include <poll.h>
16+
#include <net/if.h>
17+
#include <stdint.h>
18+
#include <net/netmap.h>
19+
#define NETMAP_WITH_LIBS
20+
#include <net/netmap_user.h>
21+
#include <netinet/ether.h>
22+
#include <netinet/ip.h>
23+
#include <netinet/udp.h>
24+
#include <netinet/tcp.h>
25+
26+
static int stop = 0;
27+
static unsigned long long fwdback = 0;
28+
static unsigned long long fwda = 0;
29+
static unsigned long long fwdb = 0;
30+
static unsigned long long tot = 0;
31+
32+
static void
33+
sigint_handler(int signum)
34+
{
35+
stop = 1;
36+
}
37+
38+
static int
39+
rx_ready(struct nm_desc *nmd)
40+
{
41+
unsigned int ri;
42+
43+
for (ri = nmd->first_rx_ring; ri <= nmd->last_rx_ring; ri ++) {
44+
struct netmap_ring *ring;
45+
46+
ring = NETMAP_RXRING(nmd->nifp, ri);
47+
if (nm_ring_space(ring)) {
48+
return 1; /* there is something to read */
49+
}
50+
}
51+
52+
return 0;
53+
}
54+
55+
static inline int
56+
pkt_get_udp_port(const char *buf)
57+
{
58+
struct ether_header *ethh;
59+
struct iphdr *iph;
60+
struct udphdr *udph;
61+
62+
ethh = (struct ether_header *)buf;
63+
if (ethh->ether_type != htons(ETHERTYPE_IP)) {
64+
/* Filter out non-IP traffic. */
65+
return 0;
66+
}
67+
iph = (struct iphdr *)(ethh + 1);
68+
if (iph->protocol != IPPROTO_UDP) {
69+
/* Filter out non-UDP traffic. */
70+
return 0;
71+
}
72+
udph = (struct udphdr *)(iph + 1);
73+
74+
/* Return destination port. */
75+
return ntohs(udph->dest);
76+
}
77+
78+
#ifdef SOLUTION
79+
static int
80+
pkt_copy_or_drop(struct nm_desc *dst, const char *buf, unsigned len)
81+
{
82+
unsigned int di;
83+
84+
for (di = dst->first_tx_ring; di <= dst->last_tx_ring; di++) {
85+
struct netmap_ring *txring = NETMAP_TXRING(dst->nifp, di);
86+
87+
if (nm_ring_space(txring)) {
88+
struct netmap_slot *ts = &txring->slot[txring->head];
89+
char *txbuf = NETMAP_BUF(txring, ts->buf_idx);
90+
91+
ts->len = len;
92+
memcpy(txbuf, buf, len);
93+
txring->cur = txring->head = nm_ring_next(txring, txring->head);
94+
return 1;
95+
}
96+
}
97+
98+
return 0;
99+
}
100+
101+
static void
102+
route_forward(struct nm_desc *one, struct nm_desc *two, struct nm_desc *three,
103+
unsigned int udp_port_a, unsigned int udp_port_b)
104+
{
105+
unsigned int si = one->first_rx_ring;
106+
107+
while (si <= one->last_rx_ring) {
108+
struct netmap_ring *rxring;
109+
unsigned int rxhead;
110+
int nrx;
111+
112+
rxring = NETMAP_RXRING(one->nifp, si);
113+
nrx = nm_ring_space(rxring);
114+
if (nrx == 0) {
115+
si++;
116+
continue;
117+
}
118+
119+
rxhead = rxring->head;
120+
for (; nrx > 0; nrx--, rxhead = nm_ring_next(rxring, rxhead)) {
121+
struct netmap_slot *rs = &rxring->slot[rxhead];
122+
char *rxbuf = NETMAP_BUF(rxring, rs->buf_idx);
123+
int udp_port = pkt_get_udp_port(rxbuf);
124+
125+
if (udp_port == udp_port_a) {
126+
fwda += pkt_copy_or_drop(two, rxbuf, rs->len);
127+
} else if (udp_port == udp_port_b) {
128+
fwdb += pkt_copy_or_drop(three, rxbuf, rs->len);
129+
}
130+
tot ++;
131+
}
132+
rxring->head = rxring->cur = rxhead;
133+
}
134+
}
135+
#endif /* SOLUTION */
136+
137+
static void
138+
forward_pkts(struct nm_desc *src, struct nm_desc *dst)
139+
{
140+
unsigned int si = src->first_rx_ring;
141+
unsigned int di = dst->first_tx_ring;
142+
143+
while (si <= src->last_rx_ring && di <= dst->last_tx_ring) {
144+
struct netmap_ring *txring;
145+
struct netmap_ring *rxring;
146+
unsigned int rxhead, txhead;
147+
int nrx, ntx;
148+
149+
rxring = NETMAP_RXRING(src->nifp, si);
150+
txring = NETMAP_TXRING(dst->nifp, di);
151+
nrx = nm_ring_space(rxring);
152+
ntx = nm_ring_space(txring);
153+
if (nrx == 0) {
154+
si++;
155+
continue;
156+
}
157+
if (ntx == 0) {
158+
di++;
159+
continue;
160+
}
161+
162+
rxhead = rxring->head;
163+
txhead = txring->head;
164+
for (; nrx > 0 && ntx > 0;
165+
nrx --, rxhead = nm_ring_next(rxring, rxhead), tot ++) {
166+
struct netmap_slot *rs = &rxring->slot[rxhead];
167+
struct netmap_slot *ts = &txring->slot[txhead];
168+
char *rxbuf = NETMAP_BUF(rxring, rs->buf_idx);
169+
char *txbuf = NETMAP_BUF(txring, ts->buf_idx);
170+
171+
ts->len = rs->len;
172+
memcpy(txbuf, rxbuf, ts->len);
173+
txhead = nm_ring_next(txring, txhead);
174+
ntx --;
175+
fwdback ++;
176+
tot ++;
177+
}
178+
/* Update state of netmap ring. */
179+
rxring->head = rxring->cur = rxhead;
180+
txring->head = txring->cur = txhead;
181+
}
182+
}
183+
184+
static int
185+
main_loop(const char *netmap_port_one, const char *netmap_port_two,
186+
const char *netmap_port_three, int udp_port_a, int udp_port_b)
187+
{
188+
struct nm_desc *nmd_one;
189+
struct nm_desc *nmd_two;
190+
struct nm_desc *nmd_three;
191+
192+
nmd_one = nm_open(netmap_port_one, NULL, 0, NULL);
193+
if (nmd_one == NULL) {
194+
if (!errno) {
195+
printf("Failed to nm_open(%s): not a netmap port\n",
196+
netmap_port_one);
197+
} else {
198+
printf("Failed to nm_open(%s): %s\n", netmap_port_one,
199+
strerror(errno));
200+
}
201+
return -1;
202+
}
203+
204+
nmd_two = nm_open(netmap_port_two, NULL, 0, NULL);
205+
if (nmd_two == NULL) {
206+
if (!errno) {
207+
printf("Failed to nm_open(%s): not a netmap port\n",
208+
netmap_port_two);
209+
} else {
210+
printf("Failed to nm_open(%s): %s\n", netmap_port_two,
211+
strerror(errno));
212+
}
213+
return -1;
214+
}
215+
216+
nmd_three = nm_open(netmap_port_three, NULL, 0, NULL);
217+
if (nmd_three == NULL) {
218+
if (!errno) {
219+
printf("Failed to nm_open(%s): not a netmap port\n",
220+
netmap_port_three);
221+
} else {
222+
printf("Failed to nm_open(%s): %s\n", netmap_port_three,
223+
strerror(errno));
224+
}
225+
return -1;
226+
}
227+
228+
while (!stop) {
229+
#ifdef SOLUTION
230+
struct pollfd pfd[3];
231+
int ret;
232+
int two_ready, three_ready;
233+
234+
pfd[0].fd = nmd_one->fd;
235+
pfd[1].fd = nmd_two->fd;
236+
pfd[2].fd = nmd_three->fd;
237+
pfd[0].events = POLLIN;
238+
pfd[1].events = 0;
239+
pfd[2].events = 0;
240+
241+
/* We don't wait for TX space on ports two and three to avoid head of
242+
* line blocking (we don't know in advance which packets are going to
243+
* be forwarded where). As a result, unfortunately, we may end dropping
244+
* packets. */
245+
two_ready = rx_ready(nmd_two);
246+
three_ready = rx_ready(nmd_three);
247+
if (!two_ready) {
248+
pfd[1].events |= POLLIN;
249+
}
250+
if (!three_ready) {
251+
pfd[2].events |= POLLIN;
252+
}
253+
254+
/* We can wait for TX space on port one, because we know all traffic
255+
* coming from ports two and three unconditionally goes to port one. */
256+
if (two_ready || three_ready) {
257+
pfd[0].events |= POLLOUT;
258+
}
259+
260+
/* We poll with a timeout to have a chance to break the main loop if
261+
* no packets are coming. */
262+
ret = poll(pfd, 3, 1000);
263+
if (ret < 0) {
264+
perror("poll()");
265+
} else if (ret == 0) {
266+
/* Timeout */
267+
continue;
268+
}
269+
270+
/* Route and forward from port one to ports two and three. */
271+
route_forward(nmd_one, nmd_two, nmd_three, udp_port_a,
272+
udp_port_b);
273+
#endif /* SOLUTION */
274+
275+
/* Forward traffic from ports two and three back to port one. */
276+
forward_pkts(nmd_two, nmd_one);
277+
forward_pkts(nmd_three, nmd_one);
278+
}
279+
280+
nm_close(nmd_one);
281+
nm_close(nmd_two);
282+
nm_close(nmd_three);
283+
284+
printf("Total processed packets: %llu\n", tot);
285+
printf("Forwarded to port one : %llu\n", fwdback);
286+
printf("Forwarded to port two : %llu\n", fwda);
287+
printf("Forwarded to port three: %llu\n", fwdb);
288+
289+
return 0;
290+
}
291+
292+
static void
293+
usage(char **argv)
294+
{
295+
printf("usage: %s [-h] [-i NETMAP_PORT_ONE] "
296+
"[-i NETMAP_PORT_TWO] [-i NETMAP_PORT_THREE] "
297+
"[-p UDP_PORT_A] [-p UDP_PORT_B]\n", argv[0]);
298+
exit(EXIT_SUCCESS);
299+
}
300+
301+
int
302+
main(int argc, char **argv)
303+
{
304+
const char *netmap_port_one = NULL;
305+
const char *netmap_port_two = NULL;
306+
const char *netmap_port_three = NULL;
307+
int udp_port;
308+
int udp_port_a = 8000;
309+
int udp_port_b = 8001;
310+
int udp_port_args = 0;
311+
struct sigaction sa;
312+
int opt;
313+
int ret;
314+
315+
while ((opt = getopt(argc, argv, "hi:p:")) != -1) {
316+
switch (opt) {
317+
case 'h':
318+
usage(argv);
319+
return 0;
320+
321+
case 'i':
322+
if (netmap_port_one == NULL) {
323+
netmap_port_one = optarg;
324+
} else if (netmap_port_two == NULL) {
325+
netmap_port_two = optarg;
326+
} else if (netmap_port_three == NULL) {
327+
netmap_port_three = optarg;
328+
}
329+
break;
330+
331+
case 'p':
332+
udp_port = atoi(optarg);
333+
if (udp_port <= 0 || udp_port >= 65535) {
334+
printf(" invalid UDP port %s\n", optarg);
335+
usage(argv);
336+
}
337+
switch (udp_port_args) {
338+
case 0:
339+
udp_port_a = udp_port;
340+
break;
341+
case 1:
342+
udp_port_b = udp_port;
343+
break;
344+
}
345+
udp_port_args ++;
346+
break;
347+
348+
default:
349+
printf(" unrecognized option '-%c'\n", opt);
350+
usage(argv);
351+
return -1;
352+
}
353+
}
354+
355+
if (netmap_port_one == NULL) {
356+
printf(" missing netmap port #1\n");
357+
usage(argv);
358+
}
359+
360+
if (netmap_port_two == NULL) {
361+
printf(" missing netmap port #2\n");
362+
usage(argv);
363+
}
364+
365+
/* Register Ctrl-C handler. */
366+
sa.sa_handler = sigint_handler;
367+
sigemptyset(&sa.sa_mask);
368+
sa.sa_flags = SA_RESTART;
369+
ret = sigaction(SIGINT, &sa, NULL);
370+
if (ret) {
371+
perror("sigaction(SIGINT)");
372+
exit(EXIT_FAILURE);
373+
}
374+
(void)rx_ready;
375+
376+
printf("Port one : %s\n", netmap_port_one);
377+
printf("Port two : %s\n", netmap_port_two);
378+
printf("Port three: %s\n", netmap_port_three);
379+
printf("UDP port A: %d\n", udp_port_a);
380+
printf("UDP port B: %d\n", udp_port_b);
381+
382+
main_loop(netmap_port_one, netmap_port_two, netmap_port_three,
383+
udp_port_a, udp_port_b);
384+
385+
return 0;
386+
}

0 commit comments

Comments
 (0)