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