View difference between Paste ID: CzGEiPUQ and 4gpA6baV
SHOW: | | - or go back to the newest paste.
1
/*
2-
 * Copyright (c) 1995 Danny Gasparovski.
2+
 * qemu slirp test case - based on slirp/if.c from qemu commit a9158a5cba955b7
3
 *
4-
 * Please read the file COPYRIGHT for the
4+
 * Usage:
5-
 * terms and conditions of the copyright.
5+
 *   gcc slirp_test.c -o slirp_test -Wall && ./slirp_test
6
 */
7
8
#include <stdbool.h>
9
#include <stdint.h>
10
#include <stdio.h>
11
#include <stdlib.h>
12
#include <string.h>
13
14
struct quehead {
15
    struct quehead *qh_link;
16
    struct quehead *qh_rlink;
17
};
18
19
struct Slirp {
20
    struct quehead if_fastq;   /* fast queue (for interactive data) */
21
    struct quehead if_batchq;  /* queue for non-interactive data */
22
    struct mbuf *next_m;    /* pointer to next mbuf to output */
23
    bool if_start_busy;     /* avoid if_start recursion */
24
};
25
26
typedef struct Slirp Slirp;
27
28
struct socket {
29
	int so_queued;
30
	int so_nqueued;
31
	int so_iptos;
32
};
33
34
#define IPTOS_LOWDELAY 1
35
36
struct mbuf {
37
	struct	mbuf *m_next;		/* Linked list of mbufs */
38
	struct	mbuf *m_prev;
39
	struct	mbuf *m_nextpkt;	/* Next packet in queue/record */
40
	struct	mbuf *m_prevpkt;	/* Flags aren't used in the output queue */
41
	struct	socket *m_so;
42
	Slirp *slirp;
43
	uint64_t expiration_date;
44
	int encap_result;
45
};
46
47
#define QEMU_CLOCK_REALTIME 1
48
static uint64_t qemu_clock_get_ns(int x)
49
{
50
	return 0;
51
}
52
53
static int if_encap(Slirp *slirp, struct mbuf *ifm)
54
{
55
	printf("if_encap(%p) returning %d\n", ifm, ifm->encap_result);
56
	return ifm->encap_result;
57
}
58
59
static void m_free(void *foo)
60
{
61
}
62
63
#define ifq_prev m_prev
64
#define ifq_next m_next
65
#define ifs_prev m_prevpkt
66
#define ifs_next m_nextpkt
67
#define ifq_so m_so
68
69
inline void
70
insque(void *a, void *b)
71
{
72
	register struct quehead *element = (struct quehead *) a;
73
	register struct quehead *head = (struct quehead *) b;
74
	element->qh_link = head->qh_link;
75
	head->qh_link = (struct quehead *)element;
76
	element->qh_rlink = (struct quehead *)head;
77
	((struct quehead *)(element->qh_link))->qh_rlink
78
	= (struct quehead *)element;
79
}
80
81
inline void
82
remque(void *a)
83
{
84
  register struct quehead *element = (struct quehead *) a;
85
  ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
86
  ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
87
  element->qh_rlink = NULL;
88
}
89
90
static void
91
ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead)
92
{
93
	ifm->ifs_next = ifmhead->ifs_next;
94
	ifmhead->ifs_next = ifm;
95
	ifm->ifs_prev = ifmhead;
96
	ifm->ifs_next->ifs_prev = ifm;
97
}
98
99
static inline void ifs_init(struct mbuf *ifm)
100
{
101
    ifm->ifs_next = ifm->ifs_prev = ifm;
102
}
103
104
static void
105
ifs_remque(struct mbuf *ifm)
106
{
107
	ifm->ifs_prev->ifs_next = ifm->ifs_next;
108
	ifm->ifs_next->ifs_prev = ifm->ifs_prev;
109
}
110
111
void
112
if_init(Slirp *slirp)
113
{
114
    slirp->if_fastq.qh_link = slirp->if_fastq.qh_rlink = &slirp->if_fastq;
115
    slirp->if_batchq.qh_link = slirp->if_batchq.qh_rlink = &slirp->if_batchq;
116
    slirp->next_m = (struct mbuf *) &slirp->if_batchq;
117
}
118
119
void if_start(Slirp *slirp);
120
121
/*
122
 * if_output: Queue packet into an output queue.
123
 * There are 2 output queue's, if_fastq and if_batchq.
124
 * Each output queue is a doubly linked list of double linked lists
125
 * of mbufs, each list belonging to one "session" (socket).  This
126
 * way, we can output packets fairly by sending one packet from each
127
 * session, instead of all the packets from one session, then all packets
128
 * from the next session, etc.  Packets on the if_fastq get absolute
129
 * priority, but if one session hogs the link, it gets "downgraded"
130
 * to the batchq until it runs out of packets, then it'll return
131
 * to the fastq (eg. if the user does an ls -alR in a telnet session,
132
 * it'll temporarily get downgraded to the batchq)
133
 */
134
void
135
if_output(struct socket *so, struct mbuf *ifm)
136
{
137
	Slirp *slirp = ifm->slirp;
138
	struct mbuf *ifq;
139
	int on_fastq = 1;
140
141
	/*
142
	 * See if there's already a batchq list for this session.
143
	 * This can include an interactive session, which should go on fastq,
144
	 * but gets too greedy... hence it'll be downgraded from fastq to batchq.
145
	 * We mustn't put this packet back on the fastq (or we'll send it out of order)
146
	 * XXX add cache here?
147
	 */
148
	for (ifq = (struct mbuf *) slirp->if_batchq.qh_rlink;
149
	     (struct quehead *) ifq != &slirp->if_batchq;
150
	     ifq = ifq->ifq_prev) {
151
		if (so == ifq->ifq_so) {
152
			/* A match! */
153
			ifm->ifq_so = so;
154
			ifs_insque(ifm, ifq->ifs_prev);
155
			goto diddit;
156
		}
157
	}
158
159
	/* No match, check which queue to put it on */
160
	if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
161
		ifq = (struct mbuf *) slirp->if_fastq.qh_rlink;
162
		on_fastq = 1;
163
		/*
164
		 * Check if this packet is a part of the last
165
		 * packet's session
166
		 */
167
		if (ifq->ifq_so == so) {
168
			ifm->ifq_so = so;
169
			ifs_insque(ifm, ifq->ifs_prev);
170
			goto diddit;
171
		}
172
        } else {
173
		ifq = (struct mbuf *) slirp->if_batchq.qh_rlink;
174
                /* Set next_m if the queue was empty so far */
175
                if ((struct quehead *) slirp->next_m == &slirp->if_batchq) {
176
                    slirp->next_m = ifm;
177
                }
178
        }
179
180
	/* Create a new doubly linked list for this session */
181
	ifm->ifq_so = so;
182
	ifs_init(ifm);
183
	insque(ifm, ifq);
184
185
diddit:
186
	if (so) {
187
		/* Update *_queued */
188
		so->so_queued++;
189
		so->so_nqueued++;
190
		/*
191
		 * Check if the interactive session should be downgraded to
192
		 * the batchq.  A session is downgraded if it has queued 6
193
		 * packets without pausing, and at least 3 of those packets
194
		 * have been sent over the link
195
		 * (XXX These are arbitrary numbers, probably not optimal..)
196
		 */
197
		if (on_fastq && ((so->so_nqueued >= 6) &&
198
				 (so->so_nqueued - so->so_queued) >= 3)) {
199
200
			/* Remove from current queue... */
201
			remque(ifm->ifs_next);
202
203
			/* ...And insert in the new.  That'll teach ya! */
204
			insque(ifm->ifs_next, &slirp->if_batchq);
205
		}
206
	}
207
208
#ifndef FULL_BOLT
209
	/*
210
	 * This prevents us from malloc()ing too many mbufs
211
	 */
212
	if_start(ifm->slirp);
213
#endif
214
}
215
216
/*
217
 * Send a packet
218
 * We choose a packet based on its position in the output queues;
219
 * If there are packets on the fastq, they are sent FIFO, before
220
 * everything else.  Otherwise we choose the first packet from the
221
 * batchq and send it.  the next packet chosen will be from the session
222
 * after this one, then the session after that one, and so on..  So,
223
 * for example, if there are 3 ftp session's fighting for bandwidth,
224
 * one packet will be sent from the first session, then one packet
225
 * from the second session, then one packet from the third, then back
226
 * to the first, etc. etc.
227
 */
228
void if_start(Slirp *slirp)
229
{
230
    uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
231
    bool from_batchq, next_from_batchq;
232
    struct mbuf *ifm, *ifm_next, *ifqt;
233
234
    if (slirp->if_start_busy) {
235
        return;
236
    }
237
    slirp->if_start_busy = true;
238
239
    if (slirp->if_fastq.qh_link != &slirp->if_fastq) {
240
        ifm_next = (struct mbuf *) slirp->if_fastq.qh_link;
241
        next_from_batchq = false;
242
    } else if ((struct quehead *) slirp->next_m != &slirp->if_batchq) {
243
        /* Nothing on fastq, pick up from batchq via next_m */
244
        ifm_next = slirp->next_m;
245
        next_from_batchq = true;
246
    } else {
247
        ifm_next = NULL;
248
    }
249
250
    printf("\nif_start loop begins\n");
251
    while (ifm_next) {
252
        ifm = ifm_next;
253
        printf("ifm_next=%p (session=%p), slirp->next_m=%p\n",
254
		ifm, ifm->m_so, slirp->next_m);
255
        from_batchq = next_from_batchq;
256
257
        ifm_next = ifm->ifq_next;
258
        if ((struct quehead *) ifm_next == &slirp->if_fastq) {
259
            /* No more packets in fastq, switch to batchq */
260
            ifm_next = slirp->next_m;
261
            next_from_batchq = true;
262
        }
263
        if ((struct quehead *) ifm_next == &slirp->if_batchq) {
264
            /* end of batchq */
265
            ifm_next = NULL;
266
        }
267
268
        /* Try to send packet unless it already expired */
269
        if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) {
270
            /* Packet is delayed due to pending ARP or NDP resolution */
271
            continue;
272
        }
273
274
        if (ifm == slirp->next_m) {
275
            /* Set which packet to send on next iteration */
276
            slirp->next_m = ifm->ifq_next;
277
        }
278
279
        /* Remove it from the queue */
280
        ifqt = ifm->ifq_prev;
281
        remque(ifm);
282
283
        /* If there are more packets for this session, re-queue them */
284
        if (ifm->ifs_next != ifm) {
285
            struct mbuf *next = ifm->ifs_next;
286
287
            insque(next, ifqt);
288
            ifs_remque(ifm);
289
290
            if (!from_batchq) {
291
                /* Next packet in fastq is from the same session */
292
                ifm_next = next;
293
                next_from_batchq = false;
294
            } else if ((struct quehead *) slirp->next_m == &slirp->if_batchq) {
295
                /* Set next_m and ifm_next if the session packet is now the
296
                 * only one on batchq */
297
                slirp->next_m = ifm_next = next;
298
            }
299
        }
300
301
        /* Update so_queued */
302
        if (ifm->ifq_so && --ifm->ifq_so->so_queued == 0) {
303
            /* If there's no more queued, reset nqueued */
304
            ifm->ifq_so->so_nqueued = 0;
305
        }
306
307
        m_free(ifm);
308
    }
309
310
    slirp->if_start_busy = false;
311
}
312
313
int main(int argc, char **argv)
314-
	Slirp s = {0};
314+
315
	Slirp s;
316
    memset(&s, 0, sizeof(s));
317
	if_init(&s);
318
319
	struct mbuf p0 = {0};
320
	struct socket s0 = {0};
321
	struct mbuf p1 = {0};
322
	struct socket s1 = {0};
323
	struct mbuf p2 = {0};
324
	struct mbuf p3 = {0};
325
326
	printf("s0                        = %p\n", &s0);
327
	printf("s1                        = %p\n", &s1);
328
	printf("\n");
329
	printf("p0                        = %p\n", &p0);
330
	printf("p1                        = %p\n", &p1);
331
	printf("p2                        = %p\n", &p2);
332
	printf("p3                        = %p\n", &p3);
333
	printf("&slirp->if_batchq         = %p\n", &s.if_batchq);
334
335
	p0.slirp = &s;
336
	p0.encap_result = 0;
337
	if_output(&s0, &p0);
338
339
	p1.slirp = &s;
340
	p1.encap_result = 0;
341
	if_output(&s0, &p1);
342
343
	p0.encap_result = 1;
344
	p2.slirp = &s;
345
	p2.encap_result = 1;
346
	if_output(&s1, &p2);
347
348
	p3.slirp = &s;
349
	p3.encap_result = 1;
350
	if_output(&s0, &p3);
351
352
	printf("\nFinal results:\n");
353
	printf("slirp->if_batchq.qh_link  = %p\n", s.if_batchq.qh_link);
354
	printf("slirp->if_batchq.qh_rlink = %p\n", s.if_batchq.qh_rlink);
355
	printf("slirp->next_m             = %p\n", s.next_m);
356
357
	return 0;
358
}