Home | History | Annotate | Download | only in slirp
      1 /*
      2  * Copyright (c) 1995 Danny Gasparovski.
      3  *
      4  * Please read the file COPYRIGHT for the
      5  * terms and conditions of the copyright.
      6  */
      7 
      8 #include <slirp.h>
      9 
     10 int     if_queued = 0;                  /* Number of packets queued so far */
     11 
     12 struct  mbuf if_fastq;                  /* fast queue (for interactive data) */
     13 struct  mbuf if_batchq;                 /* queue for non-interactive data */
     14 struct	mbuf *next_m;			/* Pointer to next mbuf to output */
     15 
     16 #define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
     17 
     18 static void
     19 ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead)
     20 {
     21 	ifm->ifs_next = ifmhead->ifs_next;
     22 	ifmhead->ifs_next = ifm;
     23 	ifm->ifs_prev = ifmhead;
     24 	ifm->ifs_next->ifs_prev = ifm;
     25 }
     26 
     27 static void
     28 ifs_remque(struct mbuf *ifm)
     29 {
     30 	ifm->ifs_prev->ifs_next = ifm->ifs_next;
     31 	ifm->ifs_next->ifs_prev = ifm->ifs_prev;
     32 }
     33 
     34 void
     35 if_init(void)
     36 {
     37 	if_fastq.ifq_next = if_fastq.ifq_prev = &if_fastq;
     38 	if_batchq.ifq_next = if_batchq.ifq_prev = &if_batchq;
     39         //	sl_compress_init(&comp_s);
     40 	next_m = &if_batchq;
     41 }
     42 
     43 #if 0
     44 /*
     45  * This shouldn't be needed since the modem is blocking and
     46  * we don't expect any signals, but what the hell..
     47  */
     48 inline int
     49 writen(fd, bptr, n)
     50 	int fd;
     51 	char *bptr;
     52 	int n;
     53 {
     54 	int ret;
     55 	int total;
     56 
     57 	/* This should succeed most of the time */
     58 	ret = send(fd, bptr, n,0);
     59 	if (ret == n || ret <= 0)
     60 	   return ret;
     61 
     62 	/* Didn't write everything, go into the loop */
     63 	total = ret;
     64 	while (n > total) {
     65 		ret = send(fd, bptr+total, n-total,0);
     66 		if (ret <= 0)
     67 		   return ret;
     68 		total += ret;
     69 	}
     70 	return total;
     71 }
     72 
     73 /*
     74  * if_input - read() the tty, do "top level" processing (ie: check for any escapes),
     75  * and pass onto (*ttyp->if_input)
     76  *
     77  * XXXXX Any zeros arriving by themselves are NOT placed into the arriving packet.
     78  */
     79 #define INBUFF_SIZE 2048 /* XXX */
     80 void
     81 if_input(ttyp)
     82 	struct ttys *ttyp;
     83 {
     84 	u_char if_inbuff[INBUFF_SIZE];
     85 	int if_n;
     86 
     87 	DEBUG_CALL("if_input");
     88 	DEBUG_ARG("ttyp = %lx", (long)ttyp);
     89 
     90 	if_n = recv(ttyp->fd, (char *)if_inbuff, INBUFF_SIZE,0);
     91 
     92 	DEBUG_MISC((dfd, " read %d bytes\n", if_n));
     93 
     94 	if (if_n <= 0) {
     95 		if (if_n == 0 || (errno != EINTR && errno != EAGAIN)) {
     96 			if (ttyp->up)
     97 			   link_up--;
     98 			tty_detached(ttyp, 0);
     99 		}
    100 		return;
    101 	}
    102 	if (if_n == 1) {
    103 		if (*if_inbuff == '0') {
    104 			ttyp->ones = 0;
    105 			if (++ttyp->zeros >= 5)
    106 			   slirp_exit(0);
    107 			return;
    108 		}
    109 		if (*if_inbuff == '1') {
    110 			ttyp->zeros = 0;
    111 			if (++ttyp->ones >= 5)
    112 			   tty_detached(ttyp, 0);
    113 			return;
    114 		}
    115 	}
    116 	ttyp->ones = ttyp->zeros = 0;
    117 
    118 	(*ttyp->if_input)(ttyp, if_inbuff, if_n);
    119 }
    120 #endif
    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 	struct mbuf *ifq;
    139 	int on_fastq = 1;
    140 
    141 	DEBUG_CALL("if_output");
    142 	DEBUG_ARG("so = %lx", (long)so);
    143 	DEBUG_ARG("ifm = %lx", (long)ifm);
    144 
    145 	/*
    146 	 * First remove the mbuf from m_usedlist,
    147 	 * since we're gonna use m_next and m_prev ourselves
    148 	 * XXX Shouldn't need this, gotta change dtom() etc.
    149 	 */
    150 	if (ifm->m_flags & M_USEDLIST) {
    151 		remque(ifm);
    152 		ifm->m_flags &= ~M_USEDLIST;
    153 	}
    154 
    155 	/*
    156 	 * See if there's already a batchq list for this session.
    157 	 * This can include an interactive session, which should go on fastq,
    158 	 * but gets too greedy... hence it'll be downgraded from fastq to batchq.
    159 	 * We mustn't put this packet back on the fastq (or we'll send it out of order)
    160 	 * XXX add cache here?
    161 	 */
    162 	for (ifq = if_batchq.ifq_prev; ifq != &if_batchq; ifq = ifq->ifq_prev) {
    163 		if (so == ifq->ifq_so) {
    164 			/* A match! */
    165 			ifm->ifq_so = so;
    166 			ifs_insque(ifm, ifq->ifs_prev);
    167 			goto diddit;
    168 		}
    169 	}
    170 
    171 	/* No match, check which queue to put it on */
    172 	if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
    173 		ifq = if_fastq.ifq_prev;
    174 		on_fastq = 1;
    175 		/*
    176 		 * Check if this packet is a part of the last
    177 		 * packet's session
    178 		 */
    179 		if (ifq->ifq_so == so) {
    180 			ifm->ifq_so = so;
    181 			ifs_insque(ifm, ifq->ifs_prev);
    182 			goto diddit;
    183 		}
    184 	} else
    185 		ifq = if_batchq.ifq_prev;
    186 
    187 	/* Create a new doubly linked list for this session */
    188 	ifm->ifq_so = so;
    189 	ifs_init(ifm);
    190 	insque(ifm, ifq);
    191 
    192 diddit:
    193 	++if_queued;
    194 
    195 	if (so) {
    196 		/* Update *_queued */
    197 		so->so_queued++;
    198 		so->so_nqueued++;
    199 		/*
    200 		 * Check if the interactive session should be downgraded to
    201 		 * the batchq.  A session is downgraded if it has queued 6
    202 		 * packets without pausing, and at least 3 of those packets
    203 		 * have been sent over the link
    204 		 * (XXX These are arbitrary numbers, probably not optimal..)
    205 		 */
    206 		if (on_fastq && ((so->so_nqueued >= 6) &&
    207 				 (so->so_nqueued - so->so_queued) >= 3)) {
    208 
    209 			/* Remove from current queue... */
    210 			remque(ifm->ifs_next);
    211 
    212 			/* ...And insert in the new.  That'll teach ya! */
    213 			insque(ifm->ifs_next, &if_batchq);
    214 		}
    215 	}
    216 
    217 #ifndef FULL_BOLT
    218 	/*
    219 	 * This prevents us from malloc()ing too many mbufs
    220 	 */
    221 	if (link_up) {
    222 		/* if_start will check towrite */
    223 		if_start();
    224 	}
    225 #endif
    226 }
    227 
    228 /*
    229  * Send a packet
    230  * We choose a packet based on it's position in the output queues;
    231  * If there are packets on the fastq, they are sent FIFO, before
    232  * everything else.  Otherwise we choose the first packet from the
    233  * batchq and send it.  the next packet chosen will be from the session
    234  * after this one, then the session after that one, and so on..  So,
    235  * for example, if there are 3 ftp session's fighting for bandwidth,
    236  * one packet will be sent from the first session, then one packet
    237  * from the second session, then one packet from the third, then back
    238  * to the first, etc. etc.
    239  */
    240 void
    241 if_start(void)
    242 {
    243 	struct mbuf *ifm, *ifqt;
    244 
    245 	DEBUG_CALL("if_start");
    246 
    247 	if (if_queued == 0)
    248 	   return; /* Nothing to do */
    249 
    250  again:
    251         /* check if we can really output */
    252         if (!slirp_can_output())
    253             return;
    254 
    255 	/*
    256 	 * See which queue to get next packet from
    257 	 * If there's something in the fastq, select it immediately
    258 	 */
    259 	if (if_fastq.ifq_next != &if_fastq) {
    260 		ifm = if_fastq.ifq_next;
    261 	} else {
    262 		/* Nothing on fastq, see if next_m is valid */
    263 		if (next_m != &if_batchq)
    264 		   ifm = next_m;
    265 		else
    266 		   ifm = if_batchq.ifq_next;
    267 
    268 		/* Set which packet to send on next iteration */
    269 		next_m = ifm->ifq_next;
    270 	}
    271 	/* Remove it from the queue */
    272 	ifqt = ifm->ifq_prev;
    273 	remque(ifm);
    274 	--if_queued;
    275 
    276 	/* If there are more packets for this session, re-queue them */
    277 	if (ifm->ifs_next != /* ifm->ifs_prev != */ ifm) {
    278 		insque(ifm->ifs_next, ifqt);
    279 		ifs_remque(ifm);
    280 	}
    281 
    282 	/* Update so_queued */
    283 	if (ifm->ifq_so) {
    284 		if (--ifm->ifq_so->so_queued == 0)
    285 		   /* If there's no more queued, reset nqueued */
    286 		   ifm->ifq_so->so_nqueued = 0;
    287 	}
    288 
    289 	/* Encapsulate the packet for sending */
    290         if_encap((uint8_t *)ifm->m_data, ifm->m_len);
    291 
    292         m_free(ifm);
    293 
    294 	if (if_queued)
    295 	   goto again;
    296 }
    297