Commit 6518579c authored by mac's avatar mac

Old dummynet code and ipfw mods. Moved to attic.

parent 9f194304
OBJS=delay.o
POBJS=delay.po
CFLAGS += -Wall -O2 -DSPIN_OK #-DDEBUG=1 -g
CC=gcc
LIBS=-L. -ldivert
PLIBS=-L. -ldivert_p
all: delay
delay.po: delay.c
${CC} delay.c -pg -c -o $@
delay: ${OBJS}
$(CC) $(OBJS) ${LIBS} -o delay
delay_p: ${POBJS}
$(CC) -pg $(POBJS) ${PLIBS} -o $@
libdivert.a: divert.o divert_hash.o
${AR} cr libdivert.a divert.o divert_hash.o
${RANLIB} libdivert.a
install: delay
cp -f delay /tmp/delay
clean:
/bin/rm -rf $(OBJS) delay
/*
* Delay packets using divert sockets in FreeBSD or the OSKit with
* the FreeBSD 3.0 libs. Uses some functions from my divert sockets
* library to set up the ipfw rules and perform socket accounting.
*
* Inefficient, and the timers have potentially poor granularity.
* Nevertheless, it should be sufficient for large-scale delays
* (on the order of 10s of miliseconds), but will *not* work for
* delays < 1ms, for sure.
*
* Author: Dave Andersen <danderse@cs.utah.edu> <angio@pobox.com>
* 1999-08-27
*/
/*
* Account for the time it takes us to enqueue and such.
* Could be variable depending on load, of course, but this
* is better than nothing.
*/
#ifndef DELAY_COMPENSATION
#define DELAY_COMPENSATION 70
#endif
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/signal.h>
#include <assert.h>
#include <sys/time.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <limits.h>
#include "divert.h"
struct msockinfo *msock, *outsock;
#ifdef DEBUG
#define DPRINTF(fmt, args...) printf(fmt , ##args )
#else
#define DPRINTF(args...)
#endif
long timediff(struct timeval *time_now, struct timeval *time_prev);
/*
* I'm lazy, but want to see if an allocation fails...
*/
inline void *C_MALLOC(size_t size) {
void *tmp;
tmp = malloc(size);
assert(tmp != NULL);
return tmp;
}
struct packet *phead = NULL;
struct packet *ptail = NULL;
int sniffsock;
struct packet {
char *data;
int datalen;
struct timeval exptime;
struct sockaddr_in from;
int fromlen;
struct packet *next;
};
struct bufq {
struct bufq* next;
};
struct bufq *bufhead = NULL;
int bufq_size = 0;
void dump_stats() {
printf("At exit, bufq contains %d buffers\n", bufq_size);
}
/* Buffer management */
void *getbuf() {
struct bufq *nextbuf;
if (!bufhead) { return malloc(MAXPACKET); }
nextbuf = bufhead;
bufhead = bufhead->next;
bufq_size--;
return nextbuf;
}
void freebuf(void *freeme) {
((struct bufq *)freeme)->next = bufhead;
bufhead = (struct bufq *)freeme;
bufq_size++;
}
/*
* Put a packet at the end of the waiting queue.
*
* Right now, I simply run the full list at each clock tick, which is pretty
* inefficient, but this is a prototype. :-) It would be better to be more
* intelligent about this - keep a counter of the times elapsed, process
* packets until we hit one that isn't ready to go out, and then just
* increment that counter. Then start running the list with the counter.
* Would take more accounting, of course.
*
* Or just use the TCP timeout counters from the kernel. :)
*/
int packet_enq(char *data, int datalen, struct timeval exptime,
struct sockaddr_in from, int fromlen)
{
struct packet *tmp;
tmp = C_MALLOC(sizeof(*tmp));
if (ptail) {
ptail->next = tmp;
ptail = tmp;
} else {
phead = ptail = tmp;
}
ptail->data = data;
ptail->datalen = datalen;
ptail->exptime = exptime;
ptail->from = from;
ptail->fromlen = fromlen;
ptail->next = NULL;
return 0;
}
/*
* Put the packet back on the wire, unmodified.
*/
void packet_forw(struct packet *packet)
{
int wlen;
#ifdef DEBUG
printf("Sock: %d, len: %d, fromlen: %d\n",
msock->sock, packet->datalen, packet->fromlen);
IPDUMP((struct ip *)(packet->data));
#endif
wlen = sendto(msock->sock,
packet->data,
packet->datalen,
0,
(struct sockaddr *)&(packet->from),
packet->fromlen);
if (wlen <= 0) {
perror("sendto");
}
#if 0
printf("Forwarded, got %d\n", wlen);
#endif
}
/*
* Process the packet queue, forwarding any packets which are ready for
* forwarding, and removing them from the queue.
*/
int pq_run(struct timeval *curtime) {
struct packet *tmp;
struct packet *prev = NULL;
struct packet *next = NULL;
tmp = phead;
while (tmp != NULL) {
next = tmp->next;
if (timediff(&tmp->exptime, curtime) <= 0) {
packet_forw(tmp);
if (prev) {
prev->next = next;
} else {
if (tmp == phead) {
phead = next;
}
}
if (tmp == ptail) {
ptail = NULL;
}
freebuf(tmp->data);
free(tmp);
tmp = NULL;
} else {
/* No more packets to check... if the head
* isn't ready, nothing is */
return 0;
}
if (!prev && !next) {
phead = ptail = NULL;
return 0;
}
tmp = next;
}
return 0;
}
/*
* Close things down, delete the ipfw mappings, and exit
*/
void cleanup(int sig) {
DPRINTF("Sig %d caught\n", sig);
dump_stats();
free_socket(msock);
exit(0);
}
/*
* Return the # of microseconds difference between two
* timevals. Note that this could overflow if you start
* dealing with large delays, but 35 minutes is an awfully
* long time to delay a packet. :-)
*/
long timediff(struct timeval *time_now, struct timeval *time_prev)
{
long usecs_now;
if (time_prev->tv_sec == 0 &&
time_prev->tv_usec == 0) {
return 0;
}
usecs_now = (time_now->tv_sec - time_prev->tv_sec) * 1000000;
usecs_now += time_now->tv_usec;
return usecs_now - time_prev->tv_usec;
}
void usage() {
fprintf(stderr, "usage: delay [-h] [-ui] [-p port/type] [-d delay]\n");
}
void help() {
usage();
fprintf(stderr,
" -h help (this message)\n"
" -u delay UDP packets (default)\n"
" -i delay ICMP packets\n"
" -p <port/type> UDP: delay packets to this port\n"
" ICMP: Delay this ICMP type\n"
" -d <delay> Delay for this many microseconds\n"
"\n"
);
}
/*
* Set up our sockets, and start the receive / enqueue / run queue
* loop
*/
int main(int argc, char **argv) {
int port = 0;
char *packetbuf = NULL;
unsigned long granularity; /* Kind of */
unsigned long delay = 1000000;
struct timeval mytv, time_now, exptime;
int rc;
int proto = IPPROTO_UDP;
fd_set readfds;
/* getopt stuff */
extern char *optarg;
extern int optind;
int ch;
while ((ch = getopt(argc, argv, "huip:d:")) != -1)
switch (ch) {
case 'u':
proto = IPPROTO_UDP;
break;
case 'i':
proto = IPPROTO_ICMP;
break;
case 'p':
port = atoi(optarg);
break;
case 'd':
delay = atoi(optarg);
break;
case 'h':
help();
exit(-1);
default:
usage();
exit(-1);
}
argc -= optind;
argv += optind;
if (!port) {
fprintf(stderr, "must supply a port to divert\n");
usage();
exit(-1);
}
signal(SIGTERM, cleanup);
signal(SIGQUIT, cleanup);
signal(SIGINT, cleanup);
msock = get_socket();
if (proto == IPPROTO_UDP) {
add_mask_port(msock, port);
} else if (proto == IPPROTO_ICMP) {
add_mask_icmp(msock, port);
} else {
fprintf(stderr, "unknown proto type %d\n", proto);
cleanup(0);
exit(-1);
}
granularity = delay / 100;
/* Gotta love those 10ms timers... */
#define MIN_USEC 10000
if (granularity < MIN_USEC)
granularity = MIN_USEC;
mytv.tv_sec = granularity / 1000000;
mytv.tv_usec = granularity % 1000000;
if (mytv.tv_sec == 0 && mytv.tv_usec < MIN_USEC) {
mytv.tv_usec = MIN_USEC;
}
/* Do our delays by simply breaking out of the recvfrom every
* now and then. Note that this is potentially INACCURATE if
* we're getting a trickle of data - the timeout is actually
* an inactivity timer, so the "right pace" of data arriving could
* throw things off. Not to mention, we're only accurate to about
* 2% of the timeout anyway. We should be able to squeeze more
* accuracy out of this by decreasing the granularity, at the
* expense of effectively spinning waiting for data...
*
* I've tested it with a 1ms delay (and 10us granularity) and
* it doesn't seem to kill things.
*
* Yes, better ways exist and should be used.
*/
rc = setsockopt(msock->sock, SOL_SOCKET, SO_RCVTIMEO,
&mytv, sizeof(mytv));
if (rc) {
perror("Setsockopt for timeout failed");
exit(errno);
}
DPRINTF("Timeout set for receive divert socket\n");
/*
* The meat:
* 1 - Receive any packets. Enqueue them if necessary.
* 2 - Run the queue.
* 3 - Loop dat puppy.
*
* Note that order matters here - you have to enqueue the packets
* after running the queue, or they don't rest long enough.
*/
FD_ZERO(&readfds);
granularity = delay / 100;
while (1) {
struct sockaddr_in from;
int fromlen;
int len;
int rc;
long addusec;
if (!packetbuf) {
packetbuf = getbuf();
}
if (granularity < MIN_USEC) {
#ifdef SPIN_OK
granularity = 0;
#else
granularity = MIN_USEC;
#endif
}
mytv.tv_sec = granularity / 1000000;
mytv.tv_usec = granularity % 1000000;
FD_SET(msock->sock, &readfds);
rc = select(msock->sock+1, &readfds, NULL, NULL, &mytv);
len = 0;
if (FD_ISSET(msock->sock, &readfds)) {
bzero(&from, sizeof(from));
fromlen = sizeof(struct sockaddr_in);
len = recvfrom(msock->sock, packetbuf, MAXPACKET, 0,
(struct sockaddr *)&from, &fromlen);
if (len > 0) {
DPRINTF("Received packet of length %d\n", len);
}
}
gettimeofday(&time_now, NULL);
pq_run(&time_now);
if (len <= 0) {
continue;
}
exptime = time_now;
/* XXX: Cheap hack alert. Beware of this one */
addusec = delay - DELAY_COMPENSATION;
exptime.tv_sec += addusec / 1000000;
addusec %= 1000000;
if ((LONG_MAX - addusec) < exptime.tv_usec) {
exptime.tv_sec++;
exptime.tv_usec = exptime.tv_usec - LONG_MAX + addusec;
} else {
exptime.tv_usec += addusec;
}
packet_enq(packetbuf, len, exptime, from, fromlen);
packetbuf = NULL;
}
free_socket(msock);
DPRINTF("Exiting\n");
exit(0);
}
This diff is collapsed.
/*
* Copyright (c) 1998-2000 Luigi Rizzo, Universita` di Pisa
* Portions Copyright (c) 2000 Akamba Corp.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _IP_DUMMYNET_H
#define _IP_DUMMYNET_H
/*
* Definition of dummynet data structures.
* We first start with the heap which is used by the scheduler.
*
* Each list contains a set of parameters identifying the pipe, and
* a set of packets queued on the pipe itself.
*
* I could have used queue macros, but the management i have
* is pretty simple and this makes the code more portable.
*/
typedef u_int32_t dn_key ; /* sorting key */
#define DN_KEY_LT(a,b) ((int)((a)-(b)) < 0)
#define DN_KEY_LEQ(a,b) ((int)((a)-(b)) <= 0)
#define DN_KEY_GT(a,b) ((int)((a)-(b)) > 0)
#define DN_KEY_GEQ(a,b) ((int)((a)-(b)) >= 0)
struct dn_heap_entry {
dn_key key ; /* sorting key. Topmost element is smallest one */
void *object ; /* object pointer */
} ;
struct dn_heap {
int size ;
int elements ;
struct dn_heap_entry *p ; /* really an array of "size" entries */
} ;
/*
* MT_DUMMYNET is a new (fake) mbuf type that is prepended to the
* packet when it comes out of a pipe. The definition
* ought to go in /sys/sys/mbuf.h but here it is less intrusive.
*/
#define MT_DUMMYNET MT_CONTROL
/*
* struct dn_pkt identifies a packet in the dummynet queue. The
* first part is really an m_hdr for implementation purposes, and some
* fields are saved there. When passing the packet back to the ip_input/
* ip_output(), the struct is prepended to the mbuf chain with type
* MT_DUMMYNET, and contains the pointer to the matching rule.
*/
struct dn_pkt {
struct m_hdr hdr ;
#define dn_next hdr.mh_nextpkt /* next element in queue */
#define DN_NEXT(x) (struct dn_pkt *)(x)->dn_next
#define dn_m hdr.mh_next /* packet to be forwarded */
/* #define dn_dst hdr.mh_len -* dst, for ip_output */
#define dn_dir hdr.mh_flags /* action when pkt extracted from a queue */
#define DN_TO_IP_OUT 1
#define DN_TO_IP_IN 2
#define DN_TO_BDG_FWD 3
dn_key output_time; /* when the pkt is due for delivery */
struct ifnet *ifp; /* interface, for ip_output */
struct sockaddr_in *dn_dst ;
struct route ro; /* route, for ip_output. MUST COPY */
int flags ; /* flags, for ip_output (IPv6 ?) */
};
struct dn_queue {
struct dn_pkt *head, *tail;
} ;
/*
* We use per flow queues. Hashing is used to select the right slot,
* then we scan the list to match the flow-id.
* The pipe is shared as it is only a delay line and thus one is enough.
*/
struct dn_flow_queue {
struct dn_flow_queue *next ;
struct ipfw_flow_id id ;
struct dn_pipe *p ; /* parent pipe */
struct dn_queue r;
long numbytes ;
u_int len ;
u_int len_bytes ;
u_int64_t tot_pkts ; /* statistics counters */
u_int64_t tot_bytes ;
u_int32_t drops ;
int hash_slot ; /* debugging/diagnostic */
} ;
/* distribution types */
#define DN_DIST_CONST_TIME 0x01
#define DN_DIST_CONST_RATE 0x02
#define DN_DIST_UNIFORM 0x04
#define DN_DIST_POISSON 0x08
#define DN_DIST_TABLE_RANDOM 0x10
#define DN_DIST_TABLE_DETERM 0x20
#define DN_TABLE_DIST \
(DN_DIST_TABLE_RANDOM|DN_DIST_TABLE_DETERM|DN_DIST_POISSON)
#define DN_CONST_DIST (DN_DIST_CONST_RATE|DN_DIST_CONST_TIME)
/* delay, bandwidth, loss parameters for dn_pipe */
struct dn_delay {
int delay ; /* really, ticks */
int dist; /* distribution type */
int mean;
int stddev;
int *table; /* table of possible values */
int entries; /* entries in table */
int tablepos; /* current pos in table for deterministic */
};
struct dn_bw {
int bandwidth; /* bits/sec */
int dist; /* distribution type */
int mean;
int stddev;
int quantum; /* how frequently to recalculate bw */
int quantum_expire; /* its expiration date */
int *table; /* table of possible values */
int entries; /* entries in table */
int tablepos; /* current pos in table for deterministic */
};
struct dn_loss {
int plr ; /* pkt loss rate (2^31-1 means 100%) */
int dist; /* distribution type */
int nextdroptime; /* time to drop pkt at head of queue */
int mean;
int stddev;
int quantum; /* how frequently to recalc loss rate */
int quantum_expire; /* its expiration date */
int *table; /* table of possible values */
int entries; /* entries in table */
int tablepos; /* current pos in table for deterministic */
};
/*
* Pipe descriptor. Contains global parameters, delay-line queue,
* and the hash array of the per-flow queues.
*/
struct dn_pipe { /* a pipe */
struct dn_pipe *next ;
u_short pipe_nr ; /* number */
u_short flags ; /* to speed up things */
#define DN_HAVE_FLOW_MASK 8
struct dn_delay delay;
struct dn_bw bw;
struct dn_loss loss;
int queue_size ;
int queue_size_bytes ;
struct dn_queue p ;
struct ipfw_flow_id flow_mask ;
int rq_size ;
int rq_elements ;
struct dn_flow_queue **rq ; /* array of rq_size+1 entries */
u_int32_t last_expired ; /* do not expire too frequently */
};
#ifdef KERNEL
MALLOC_DECLARE(