Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * qemu slirp test case - based on slirp/if.c from qemu commit a9158a5cba955b7
- * This shows how slirp->next_m can get set to an incorrect value, stalling a socket (session).
- *
- * Usage:
- * gcc slirp_test.c -o slirp_test -Wall && ./slirp_test
- */
- #include <stdbool.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- struct quehead {
- struct quehead *qh_link;
- struct quehead *qh_rlink;
- };
- struct Slirp {
- struct quehead if_fastq; /* fast queue (for interactive data) */
- struct quehead if_batchq; /* queue for non-interactive data */
- struct mbuf *next_m; /* pointer to next mbuf to output */
- bool if_start_busy; /* avoid if_start recursion */
- };
- typedef struct Slirp Slirp;
- struct socket {
- int so_queued;
- int so_nqueued;
- int so_iptos;
- };
- #define IPTOS_LOWDELAY 1
- struct mbuf {
- struct mbuf *m_next; /* Linked list of mbufs */
- struct mbuf *m_prev;
- struct mbuf *m_nextpkt; /* Next packet in queue/record */
- struct mbuf *m_prevpkt; /* Flags aren't used in the output queue */
- struct socket *m_so;
- Slirp *slirp;
- uint64_t expiration_date;
- int encap_result;
- };
- #define QEMU_CLOCK_REALTIME 1
- static uint64_t qemu_clock_get_ns(int x)
- {
- return 0;
- }
- static int if_encap(Slirp *slirp, struct mbuf *ifm)
- {
- printf("if_encap(%p) returning %d\n", ifm, ifm->encap_result);
- return ifm->encap_result;
- }
- static void m_free(void *foo)
- {
- }
- #define ifq_prev m_prev
- #define ifq_next m_next
- #define ifs_prev m_prevpkt
- #define ifs_next m_nextpkt
- #define ifq_so m_so
- inline void
- insque(void *a, void *b)
- {
- register struct quehead *element = (struct quehead *) a;
- register struct quehead *head = (struct quehead *) b;
- element->qh_link = head->qh_link;
- head->qh_link = (struct quehead *)element;
- element->qh_rlink = (struct quehead *)head;
- ((struct quehead *)(element->qh_link))->qh_rlink
- = (struct quehead *)element;
- }
- inline void
- remque(void *a)
- {
- register struct quehead *element = (struct quehead *) a;
- ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
- ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
- element->qh_rlink = NULL;
- }
- static void
- ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead)
- {
- ifm->ifs_next = ifmhead->ifs_next;
- ifmhead->ifs_next = ifm;
- ifm->ifs_prev = ifmhead;
- ifm->ifs_next->ifs_prev = ifm;
- }
- static inline void ifs_init(struct mbuf *ifm)
- {
- ifm->ifs_next = ifm->ifs_prev = ifm;
- }
- static void
- ifs_remque(struct mbuf *ifm)
- {
- ifm->ifs_prev->ifs_next = ifm->ifs_next;
- ifm->ifs_next->ifs_prev = ifm->ifs_prev;
- }
- void
- if_init(Slirp *slirp)
- {
- slirp->if_fastq.qh_link = slirp->if_fastq.qh_rlink = &slirp->if_fastq;
- slirp->if_batchq.qh_link = slirp->if_batchq.qh_rlink = &slirp->if_batchq;
- slirp->next_m = (struct mbuf *) &slirp->if_batchq;
- }
- void if_start(Slirp *slirp);
- /*
- * if_output: Queue packet into an output queue.
- * There are 2 output queue's, if_fastq and if_batchq.
- * Each output queue is a doubly linked list of double linked lists
- * of mbufs, each list belonging to one "session" (socket). This
- * way, we can output packets fairly by sending one packet from each
- * session, instead of all the packets from one session, then all packets
- * from the next session, etc. Packets on the if_fastq get absolute
- * priority, but if one session hogs the link, it gets "downgraded"
- * to the batchq until it runs out of packets, then it'll return
- * to the fastq (eg. if the user does an ls -alR in a telnet session,
- * it'll temporarily get downgraded to the batchq)
- */
- void
- if_output(struct socket *so, struct mbuf *ifm)
- {
- Slirp *slirp = ifm->slirp;
- struct mbuf *ifq;
- int on_fastq = 1;
- /*
- * See if there's already a batchq list for this session.
- * This can include an interactive session, which should go on fastq,
- * but gets too greedy... hence it'll be downgraded from fastq to batchq.
- * We mustn't put this packet back on the fastq (or we'll send it out of order)
- * XXX add cache here?
- */
- for (ifq = (struct mbuf *) slirp->if_batchq.qh_rlink;
- (struct quehead *) ifq != &slirp->if_batchq;
- ifq = ifq->ifq_prev) {
- if (so == ifq->ifq_so) {
- /* A match! */
- ifm->ifq_so = so;
- ifs_insque(ifm, ifq->ifs_prev);
- goto diddit;
- }
- }
- /* No match, check which queue to put it on */
- if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
- ifq = (struct mbuf *) slirp->if_fastq.qh_rlink;
- on_fastq = 1;
- /*
- * Check if this packet is a part of the last
- * packet's session
- */
- if (ifq->ifq_so == so) {
- ifm->ifq_so = so;
- ifs_insque(ifm, ifq->ifs_prev);
- goto diddit;
- }
- } else {
- ifq = (struct mbuf *) slirp->if_batchq.qh_rlink;
- /* Set next_m if the queue was empty so far */
- if ((struct quehead *) slirp->next_m == &slirp->if_batchq) {
- slirp->next_m = ifm;
- }
- }
- /* Create a new doubly linked list for this session */
- ifm->ifq_so = so;
- ifs_init(ifm);
- insque(ifm, ifq);
- diddit:
- if (so) {
- /* Update *_queued */
- so->so_queued++;
- so->so_nqueued++;
- /*
- * Check if the interactive session should be downgraded to
- * the batchq. A session is downgraded if it has queued 6
- * packets without pausing, and at least 3 of those packets
- * have been sent over the link
- * (XXX These are arbitrary numbers, probably not optimal..)
- */
- if (on_fastq && ((so->so_nqueued >= 6) &&
- (so->so_nqueued - so->so_queued) >= 3)) {
- /* Remove from current queue... */
- remque(ifm->ifs_next);
- /* ...And insert in the new. That'll teach ya! */
- insque(ifm->ifs_next, &slirp->if_batchq);
- }
- }
- #ifndef FULL_BOLT
- /*
- * This prevents us from malloc()ing too many mbufs
- */
- if_start(ifm->slirp);
- #endif
- }
- /*
- * Send a packet
- * We choose a packet based on its position in the output queues;
- * If there are packets on the fastq, they are sent FIFO, before
- * everything else. Otherwise we choose the first packet from the
- * batchq and send it. the next packet chosen will be from the session
- * after this one, then the session after that one, and so on.. So,
- * for example, if there are 3 ftp session's fighting for bandwidth,
- * one packet will be sent from the first session, then one packet
- * from the second session, then one packet from the third, then back
- * to the first, etc. etc.
- */
- void if_start(Slirp *slirp)
- {
- uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
- bool from_batchq, next_from_batchq;
- struct mbuf *ifm, *ifm_next, *ifqt;
- if (slirp->if_start_busy) {
- return;
- }
- slirp->if_start_busy = true;
- if (slirp->if_fastq.qh_link != &slirp->if_fastq) {
- ifm_next = (struct mbuf *) slirp->if_fastq.qh_link;
- next_from_batchq = false;
- } else if ((struct quehead *) slirp->next_m != &slirp->if_batchq) {
- /* Nothing on fastq, pick up from batchq via next_m */
- ifm_next = slirp->next_m;
- next_from_batchq = true;
- } else {
- ifm_next = NULL;
- }
- printf("\nif_start loop begins\n");
- while (ifm_next) {
- ifm = ifm_next;
- printf("ifm_next=%p (session=%p), slirp->next_m=%p\n",
- ifm, ifm->m_so, slirp->next_m);
- from_batchq = next_from_batchq;
- ifm_next = ifm->ifq_next;
- if ((struct quehead *) ifm_next == &slirp->if_fastq) {
- /* No more packets in fastq, switch to batchq */
- ifm_next = slirp->next_m;
- next_from_batchq = true;
- }
- if ((struct quehead *) ifm_next == &slirp->if_batchq) {
- /* end of batchq */
- ifm_next = NULL;
- }
- /* Try to send packet unless it already expired */
- if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) {
- /* Packet is delayed due to pending ARP or NDP resolution */
- continue;
- }
- if (ifm == slirp->next_m) {
- /* Set which packet to send on next iteration */
- slirp->next_m = ifm->ifq_next;
- }
- /* Remove it from the queue */
- ifqt = ifm->ifq_prev;
- remque(ifm);
- /* If there are more packets for this session, re-queue them */
- if (ifm->ifs_next != ifm) {
- struct mbuf *next = ifm->ifs_next;
- insque(next, ifqt);
- ifs_remque(ifm);
- if (!from_batchq) {
- /* Next packet in fastq is from the same session */
- ifm_next = next;
- next_from_batchq = false;
- } else if ((struct quehead *) slirp->next_m == &slirp->if_batchq) {
- /* Set next_m and ifm_next if the session packet is now the
- * only one on batchq */
- slirp->next_m = ifm_next = next;
- }
- }
- /* Update so_queued */
- if (ifm->ifq_so && --ifm->ifq_so->so_queued == 0) {
- /* If there's no more queued, reset nqueued */
- ifm->ifq_so->so_nqueued = 0;
- }
- m_free(ifm);
- }
- slirp->if_start_busy = false;
- }
- int main(int argc, char **argv)
- {
- Slirp s;
- memset(&s, 0, sizeof(s));
- if_init(&s);
- struct mbuf p0 = {0};
- struct socket s0 = {0};
- struct mbuf p1 = {0};
- struct socket s1 = {0};
- struct mbuf p2 = {0};
- struct mbuf p3 = {0};
- printf("s0 = %p\n", &s0);
- printf("s1 = %p\n", &s1);
- printf("\n");
- printf("p0 = %p\n", &p0);
- printf("p1 = %p\n", &p1);
- printf("p2 = %p\n", &p2);
- printf("p3 = %p\n", &p3);
- printf("&slirp->if_batchq = %p\n", &s.if_batchq);
- p0.slirp = &s;
- p0.encap_result = 0;
- if_output(&s0, &p0);
- p1.slirp = &s;
- p1.encap_result = 0;
- if_output(&s0, &p1);
- p0.encap_result = 1;
- p2.slirp = &s;
- p2.encap_result = 1;
- if_output(&s1, &p2);
- p3.slirp = &s;
- p3.encap_result = 1;
- if_output(&s0, &p3);
- printf("\nFinal results:\n");
- printf("slirp->if_batchq.qh_link = %p\n", s.if_batchq.qh_link);
- printf("slirp->if_batchq.qh_rlink = %p\n", s.if_batchq.qh_rlink);
- printf("slirp->next_m = %p\n", s.next_m);
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement