net.c100644 423 423 16155 6300167373 7470 0ustar luigi/* * net.c -- socket I/O -- This is a stripped down version of the * net.cc module from vic, with the following (original) copyright: * * Copyright (c) 1993-1994 The Regents of the University of California. * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and the Network Research Group at * Lawrence Berkeley Laboratory. * 4. Neither the name of the University nor of the Laboratory may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ #include "rmdp.h" /*** *** We need the following routines: - open a multicast socket for send. (receiver: send request; sender: send data) - open a multicast socket for receive (receiver: get request; sender: get data) - open a unicast socket for send (sender: send reply) - open a unicast socket for receive (receiver: get reply) *** ***/ void nonblock(int fd) { #ifdef WIN32 u_long flag = 1; if (ioctlsocket(fd, FIONBIO, &flag) == -1) { fprintf(stderr, "ioctlsocket: FIONBIO: %lu\n", GetLastError()); exit(1); } #else int flags = fcntl(fd, F_GETFL, 0); #if defined(hpux) || defined(__hpux) flags |= O_NONBLOCK; #else flags |= O_NONBLOCK|O_NDELAY; #endif if (fcntl(fd, F_SETFL, flags) == -1) { perror("fcntl: F_SETFL"); } #endif } int openssock(n32 addr, n16 port, int ttl) { int fd; struct sockaddr_in sin; fprintf(stderr,"--- openssock addr %s port %d\n", inet_ntoa(*(struct in_addr *)&addr), ntohs(port) ); fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("socket"); exit(1); } nonblock(fd); #ifdef WIN32 memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = 0; sin.sin_addr.s_addr = INADDR_ANY; if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("bind"); exit(1); } #endif memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = port; sin.sin_addr.s_addr = addr; if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("connect"); exit(1); } if (IN_CLASSD(ntohl(addr))) { #ifdef IP_ADD_MEMBERSHIP #ifdef WIN32 u_int t; #else u_char t; #endif /*** set the multicast TTL ***/ t = (ttl > 255) ? 255 : (ttl < 0) ? 0 : ttl; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&t, sizeof(t)) < 0) { perror("IP_MULTICAST_TTL"); exit(1); } #else fprintf(stderr, "not compiled with support for IP multicast\n" "you must specify a unicast destination\n"); exit(1); #endif } return fd ; } int openrsock(n32 addr, n16 *port) { int fd; struct sockaddr_in sin; int on = 1; fprintf(stderr,"--- openrsock addr %s port %d\n", inet_ntoa(*(struct in_addr *)&addr), ntohs(*port) ); fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("socket"); exit(1); } nonblock(fd); if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,(char *)&on, sizeof(on)) < 0) { perror("SO_REUSEADDR"); } #ifdef SO_REUSEPORT on = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&on, sizeof(on)) < 0) { perror("SO_REUSEPORT"); exit(1); } #endif memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = *port; #ifdef IP_ADD_MEMBERSHIP if (IN_CLASSD(ntohl(addr))) { struct ip_mreq mr; /* * Try to bind the multicast address as the socket * dest address. On many systems this won't work * so fall back to a destination of INADDR_ANY if * the first bind fails. */ #if 0 sin.sin_addr.s_addr = addr; if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { fprintf(stderr, "first bind failed, try INADDR_ANY\n"); sin.sin_addr.s_addr = INADDR_ANY; if (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) { perror("bind"); exit(1); } } #else sin.sin_addr.s_addr = INADDR_ANY; if (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) { perror("bind"); exit(1); } #endif /* * XXX This is bogus multicast setup that really * shouldn't have to be done (group membership should be * implicit in the IP class D address, route should contain * ttl & no loopback flag, etc.). Steve Deering has promised * to fix this for the 4.4bsd release. We're all waiting * with bated breath. */ mr.imr_multiaddr.s_addr = addr; mr.imr_interface.s_addr = INADDR_ANY; if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mr, sizeof(mr)) < 0) { perror("IP_ADD_MEMBERSHIP"); return (fd); } } else #endif { /* * bind the local port to this socket. If that * fails, another app probably has the addresses bound so * just exit. */ sin.sin_addr.s_addr = addr ; if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("bind"); exit(1); } } if (*port == 0) { memset((char *)&sin, 0, sizeof(sin)); on = sizeof(sin); getsockname(fd, (struct sockaddr *)&sin, &on) ; *port = sin.sin_port ; } fprintf(stderr, "--- port bound to %s : %d\n", inet_ntoa(sin.sin_addr), ntohs(*port) ); return fd ; } void localname(struct sockaddr_in * p, int ssock_) { int len = sizeof(*p); memset((char *)p, 0, sizeof(*p)); p->sin_family = AF_INET; if (getsockname(ssock_, (struct sockaddr *)p, &len) < 0) { perror("getsockname"); p->sin_addr.s_addr = 0; p->sin_port = 0; } if (p->sin_addr.s_addr == 0) { char hostname[80]; struct hostent *hp; if (gethostname(hostname, sizeof(hostname)) >= 0) { if ((hp = gethostbyname(hostname)) >= 0) { p->sin_addr.s_addr = ((struct in_addr *)hp->h_addr)->s_addr; } } } fprintf(stderr, "localname addr is %s\n", inet_ntoa(p->sin_addr) ); } /*** end of file ***/ compat.c100644 423 423 5246 6542206775 10154 0ustar luigi/* * compat.c -- machine dependant functions for rmdp. * At this time, only time-handling functions. * This file is part of * * rmdp -- Reliable Multicast data Distribution Protocol * * (C) 1996-1998 Luigi Rizzo and Lorenzo Vicisano * (luigi@iet.unipi.it, vicisano@cs.ucl.ac.uk) * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Luigi Rizzo, * Lorenzo Vicisano and other contributors. * 4. Neither the name of the Authors nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. * */ #include "rmdp.h" /* * compute the difference between two timestamps */ long diff_t(struct timeval b, struct timeval a) { return (ui32)( 1000000*(b.tv_sec - a.tv_sec) + b.tv_usec - a.tv_usec) ; } /* * return a timestamp in microseconds as a long int. * XXX should check for overflows */ long timestamp_u() { struct timeval t; (void)gettimeofday(&t, NULL); return((ui32)(t.tv_sec*1000000+t.tv_usec)); } /* * return a timestamp in milliseconds as a long int. * XXX should check for overflows */ long timestamp_m() { struct timeval t; (void)gettimeofday(&t, NULL); return((ui32)(t.tv_sec*1000+t.tv_usec/1000)); } void timestamp_t(struct timeval *t) { (void)gettimeofday(t, NULL); } event.c100644 423 423 21431 6543317474 10024 0ustar luigi/* * event.c -- scheduler for rmdp * * This file is part of * * rmdp -- Reliable Multicast data Distribution Protocol * * (C) 1996-1998 Luigi Rizzo and Lorenzo Vicisano * (luigi@iet.unipi.it, vicisano@cs.ucl.ac.uk) * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Luigi Rizzo, * Lorenzo Vicisano and other contributors. * 4. Neither the name of the Authors nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. * */ /*** *** These routines implement an event loop. First, the event list must be created using createevlist(). Timeouts can be registered with settimer(). They always expire, and there is no way to remove them. i/o descriptors can be inserted/deleted using insertchan() and deletechan(). One function per timeout, and one function per channel, can be called. *** ***/ #include "event.h" #include "time.h" #undef TEST #undef ONE_GO /* define to make getevent return after each event */ static TIMIT *timit_free_list = NULL; static int timit_free_count = 0 ; void free_timit(TIMIT *p) { p->next = timit_free_list ; timit_free_list = p; bzero(p, sizeof (TIMIT) ); timit_free_count++ ; } TIMIT * new_timit() { TIMIT *p; if (timit_free_list) { p = timit_free_list ; timit_free_list = p->next ; timit_free_count-- ; } else { p = (TIMIT *)calloc(1, sizeof(TIMIT)) ; if (p == NULL) { perror("new_timit: calloc"); exit(1); } } return p ; } EVLIST * createevlist() { int i; EVLIST *e; if (NULL==(e=(EVLIST *)calloc(1, sizeof(EVLIST)))) { perror("createevlist: calloc"); exit(1); } for (i=0; iio_chan[i]= -1; } e->io_ch_num = 0; e->timevent = NULL; FD_ZERO(&(e->in_s)); FD_ZERO(&(e->out_s)); FD_ZERO(&(e->err_s)); timestamp_t(&(e->lasttime)); e->late = 0; return(e); } int getevent(EVLIST *e, int blocking) { struct timeval thistime, interval, *timp; ui32 past; TIMIT *tim; int id, max_chan, i, j, ret; again: id = EV_NO_PENDING ; /* see how much time has past since last update */ timestamp_t(&thistime); past = diff_t(thistime, e->lasttime) ; DEB(fprintf(stderr, "# %u usec past\n", past)); tim = e->timevent; DEB( { TIMIT *ti = e->timevent; fprintf(stderr, "X"); for ( ; ti != NULL ; ti = ti->next ) fprintf(stderr, "--->[%u] (%u)|", ti->delta, ti->id); fprintf(stderr, "\n"); } ); if (tim != NULL) { if (tim->delta <= past) { e->late = past - tim->delta; /* how late was this event ? */ if (e->late > 1000) { /* more than 1ms late... */ DEB(fprintf(stderr, "--- we are %u usec late with ev. %d\n", e->late, tim->id)); } e->timevent = tim->next; /* first adj the event list (which could be acceded) */ if (tim->t_f) tim->t_f(tim->t_p); /* execute the action */ past -= tim->delta; /* update virtual time */ #ifdef NODEF /* we don't free the arguments */ free(tim->t_p); #endif id = tim->id; free_timit(tim); tim = e->timevent; e->lasttime = thistime ; #ifdef ONE_GO return(id); #endif goto again; } else e->late = past ; } /* set timeout to next event */ timp = &interval; if (e->timevent != NULL) { ui32 next = e->timevent->delta - e->late; interval.tv_sec = next/1000000; interval.tv_usec = next%1000000; DEB(fprintf(stderr, "# timeout in %u usec\n", next)); } else { interval = e->default_timeout ; if (blocking && e->default_timeout.tv_sec==0 && e->default_timeout.tv_usec==0) timp = NULL; } FD_ZERO(&(e->in_s)); max_chan = -1 ; for (j=0; j< e->io_ch_num; j++) if ( (i = e->io_chan[j]) >= 0 ) { if (i > max_chan) max_chan = i ; FD_SET( i, &(e->in_s)); DEB(fprintf(stderr, "selecting with %d\n", i )); } if (e->io_ch_num) { ret=select(1+max_chan, &(e->in_s), &(e->out_s), &(e->err_s), timp); DEB(fprintf(stderr, "select returning...\n")) if (ret < 0) { /* signal... */ fprintf(stderr, "getevent SIGNAL during poll, shouldn't happen...\n"); return EV_GOTSIGNAL ; } else if (ret == 0) { /* timeout */ DEB(fprintf(stderr,"timeout, ev %x blocking %d\n", e->timevent, blocking)); if (e->timevent != NULL) goto again; else return EV_DEFTIMEOUT ; } else { /* descriptor ready */ for (i=0; !(e->io_chan[i]>=0 && FD_ISSET(e->io_chan[i], &(e->in_s))) && iio_chan[i]>=0 && FD_ISSET(e->io_chan[i], &(e->in_s)))); e->selected = e->io_chan[i]; if (e->io_f[i]) e->io_f[i](e->io_p[i]); id = e->io_id[i]; } } else if (id == EV_NO_PENDING) id = EV_TERMINATE; return(id); } void set_default_timeout(EVLIST *e, struct timeval *tv) { if (tv) e->default_timeout = *tv; else e->default_timeout.tv_sec = e->default_timeout.tv_usec = 0; } void insertchan(EVLIST *e, int chan, int id, int (*f)(void *), void *p) { int i; DEB(fprintf(stderr, "insertchan: inserting chan %d\n", chan)) /*** *** check this... ***/ for (i=0; e->io_chan[i]!=chan && e->io_chan[i]>=0 && iio_chan[i]<0 || e->io_chan[i]==chan); if (e->io_chan[i] != chan) (e->io_ch_num)++; e->io_chan[i]=chan; e->io_id[i]=id; e->io_f[i] = f; e->io_p[i] = p; } void deletechan(EVLIST *e, int chan) { int i; DEB(fprintf(stderr, "deletechan: deleting chan %d\n", chan)) for (i=0; e->io_chan[i]!=chan && iio_chan[i]==chan); e->io_chan[i]=-1; (e->io_ch_num)--; } int showselected (EVLIST *e) { return(e->selected); } /* * arrange for a timer event to happen after delta usec * * `id' will be returned by getevent after the event execution, * avoid using id = EV_NO_PENDING . * * If `real' is set, the event will occurr delta usec * after the actual calling time. Otherwise, it is assumed that the * function has been called by the scheduler, and the event will * occur exactly delta usec. after the previous schedule * for the event. That allows not to accumulate errors. */ void settimer(EVLIST *e, int id, ui32 delta, CALLBACK f, void *p, int real) { TIMIT *a, *pr = NULL, *t = e->timevent; struct timeval thistime; a = new_timit() ; a->t_f = f; a->t_p = p; a->next = NULL; a->id = id; if (delta < e->late) { DEB(fprintf(stderr, "ARGH we are scheduling event" " %d %u usec in the past!!!\n", id, e->late-delta)); #ifdef NO delta = 0; } else { delta -= e->late; #endif } if (real) { timestamp_t(&thistime); delta += diff_t( thistime, e->lasttime) ; } if (e->timevent == NULL) { a->delta = delta; e->timevent = a; return; } while(t!= NULL && delta > t->delta) { delta -= t->delta; pr = t; t = t->next; } a->delta = delta; a->next = t; if (t != NULL) (t->delta) -= delta; if (t == e->timevent) { e->timevent = a; } else { pr->next = a; } } #ifdef TEST int my_f(void *p) { struct timeval time; timestamp_t(&time); printf("%d at %u\n", *(int *)p, time.tv_usec+(time.tv_sec%1000)*1000000); *(int *)p = *(int *)p +1; if (*(int *)p < 1000) settimer(e, (*(int *)p)+1, 10000, my_f, p, 0); } /* * arrange for a timer event to happen every delta msec */ int main() { int cont =0; EVLIST *e = createevlist(); settimer(e, 1, 10000, my_f, (void *)&cont, 1); while (getevent(e, 0)!=EV_NO_PENDING) ; } #endif /* TEST */ fec.c100644 423 423 57304 6547135404 7443 0ustar luigi/* * fec.c -- forward error correction based on Vandermonde matrices * 980624 * (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it) * * Portions derived from code by Phil Karn (karn@ka9q.ampr.org), * Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari * Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995 * * 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 AUTHORS ``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 AUTHORS * 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. */ /* * The following parameter defines how many bits are used for * field elements. The code supports any value from 2 to 16 * but fastest operation is achieved with 8 bit elements * This is the only parameter you may want to change. */ #ifndef GF_BITS #define GF_BITS 8 /* code over GF(2**GF_BITS) - change to suit */ #endif #include #include #include /* * compatibility stuff */ #ifdef MSDOS /* but also for others, e.g. sun... */ #define NEED_BCOPY #define bcmp(a,b,n) memcmp(a,b,n) #endif #ifdef NEED_BCOPY #define bcopy(s, d, siz) memcpy((d), (s), (siz)) #define bzero(d, siz) memset((d), '\0', (siz)) #endif /* * stuff used for testing purposes only */ #ifdef TEST #define DEB(x) #define DDB(x) x #define DEBUG 0 /* minimal debugging */ #ifdef MSDOS #include struct timeval { unsigned long ticks; }; #define gettimeofday(x, dummy) { (x)->ticks = clock() ; } #define DIFF_T(a,b) (1+ 1000000*(a.ticks - b.ticks) / CLOCKS_PER_SEC ) typedef unsigned long u_long ; typedef unsigned short u_short ; #else /* typically, unix systems */ #include #define DIFF_T(a,b) \ (1+ 1000000*(a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec) ) #endif #define TICK(t) \ {struct timeval x ; \ gettimeofday(&x, NULL) ; \ t = x.tv_usec + 1000000* (x.tv_sec & 0xff ) ; \ } #define TOCK(t) \ { u_long t1 ; TICK(t1) ; \ if (t1 < t) t = 256000000 + t1 - t ; \ else t = t1 - t ; \ if (t == 0) t = 1 ;} u_long ticks[10]; /* vars for timekeeping */ #else #define DEB(x) #define DDB(x) #define TICK(x) #define TOCK(x) #endif /* TEST */ /* * You should not need to change anything beyond this point. * The first part of the file implements linear algebra in GF. * * gf is the type used to store an element of the Galois Field. * Must constain at least GF_BITS bits. * * Note: unsigned char will work up to GF(256) but int seems to run * faster on the Pentium. We use int whenever have to deal with an * index, since they are generally faster. */ #if (GF_BITS < 2 && GF_BITS >16) #error "GF_BITS must be 2 .. 16" #endif #if (GF_BITS <= 8) typedef unsigned char gf; #else typedef unsigned short gf; #endif #define GF_SIZE ((1 << GF_BITS) - 1) /* powers of \alpha */ /* * Primitive polynomials - see Lin & Costello, Appendix A, * and Lee & Messerschmitt, p. 453. */ static char *allPp[] = { /* GF_BITS polynomial */ NULL, /* 0 no code */ NULL, /* 1 no code */ "111", /* 2 1+x+x^2 */ "1101", /* 3 1+x+x^3 */ "11001", /* 4 1+x+x^4 */ "101001", /* 5 1+x^2+x^5 */ "1100001", /* 6 1+x+x^6 */ "10010001", /* 7 1 + x^3 + x^7 */ "101110001", /* 8 1+x^2+x^3+x^4+x^8 */ "1000100001", /* 9 1+x^4+x^9 */ "10010000001", /* 10 1+x^3+x^10 */ "101000000001", /* 11 1+x^2+x^11 */ "1100101000001", /* 12 1+x+x^4+x^6+x^12 */ "11011000000001", /* 13 1+x+x^3+x^4+x^13 */ "110000100010001", /* 14 1+x+x^6+x^10+x^14 */ "1100000000000001", /* 15 1+x+x^15 */ "11010000000010001" /* 16 1+x+x^3+x^12+x^16 */ }; /* * To speed up computations, we have tables for logarithm, exponent * and inverse of a number. If GF_BITS <= 8, we use a table for * multiplication as well (it takes 64K, no big deal even on a PDA, * especially because it can be pre-initialized an put into a ROM!), * otherwhise we use a table of logarithms. * In any case the macro gf_mul(x,y) takes care of multiplications. */ static gf gf_exp[2*GF_SIZE]; /* index->poly form conversion table */ static int gf_log[GF_SIZE + 1]; /* Poly->index form conversion table */ static gf inverse[GF_SIZE+1]; /* inverse of field elem. */ /* inv[\alpha**i]=\alpha**(GF_SIZE-i-1) */ /* * modnn(x) computes x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, * without a slow divide. */ static inline gf modnn(int x) { while (x >= GF_SIZE) { x -= GF_SIZE; x = (x >> GF_BITS) + (x & GF_SIZE); } return x; } #define SWAP(a,b,t) {t tmp; tmp=a; a=b; b=tmp;} /* * gf_mul(x,y) multiplies two numbers. If GF_BITS<=8, it is much * faster to use a multiplication table. * * USE_GF_MULC, GF_MULC0(c) and GF_ADDMULC(x) can be used when multiplying * many numbers by the same constant. In this case the first * call sets the constant, and others perform the multiplications. * A value related to the multiplication is held in a local variable * declared with USE_GF_MULC . See usage in addmul1(). */ #if (GF_BITS <= 8) static gf gf_mul_table[GF_SIZE + 1][GF_SIZE + 1]; #define gf_mul(x,y) gf_mul_table[x][y] #define USE_GF_MULC register gf * __gf_mulc_ #define GF_MULC0(c) __gf_mulc_ = gf_mul_table[c] #define GF_ADDMULC(dst, x) dst ^= __gf_mulc_[x] static void init_mul_table() { int i, j; for (i=0; i< GF_SIZE+1; i++) for (j=0; j< GF_SIZE+1; j++) gf_mul_table[i][j] = gf_exp[modnn(gf_log[i] + gf_log[j]) ] ; for (j=0; j< GF_SIZE+1; j++) gf_mul_table[0][j] = gf_mul_table[j][0] = 0; } #else /* GF_BITS > 8 */ static inline gf gf_mul(x,y) { if ( (x) == 0 || (y)==0 ) return 0; return gf_exp[gf_log[x] + gf_log[y] ] ; } #define init_mul_table() #define USE_GF_MULC register gf * __gf_mulc_ #define GF_MULC0(c) __gf_mulc_ = &gf_exp[ gf_log[c] ] #define GF_ADDMULC(dst, x) { if (x) dst ^= __gf_mulc_[ gf_log[x] ] ; } #endif /* * Generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m] * Lookup tables: * index->polynomial form gf_exp[] contains j= \alpha^i; * polynomial form -> index form gf_log[ j = \alpha^i ] = i * \alpha=x is the primitive element of GF(2^m) * * For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple * multiplication of two numbers can be resolved without calling modnn */ /* * i use malloc so many times, it is easier to put checks all in * one place. */ static void * my_malloc(int sz, char *err_string) { void *p = malloc( sz ); if (p == NULL) { fprintf(stderr, "-- malloc failure allocating %s\n", err_string); exit(1) ; } return p ; } #define NEW_GF_MATRIX(rows, cols) \ (gf *)my_malloc(rows * cols * sizeof(gf), " ## __LINE__ ## " ) /* * initialize the data structures used for computations in GF. */ static void generate_gf(void) { int i; gf mask; char *Pp = allPp[GF_BITS] ; mask = 1; /* x ** 0 = 1 */ gf_exp[GF_BITS] = 0; /* will be updated at the end of the 1st loop */ /* * first, generate the (polynomial representation of) powers of \alpha, * which are stored in gf_exp[i] = \alpha ** i . * At the same time build gf_log[gf_exp[i]] = i . * The first GF_BITS powers are simply bits shifted to the left. */ for (i = 0; i < GF_BITS; i++, mask <<= 1 ) { gf_exp[i] = mask; gf_log[gf_exp[i]] = i; /* * If Pp[i] == 1 then \alpha ** i occurs in poly-repr * gf_exp[GF_BITS] = \alpha ** GF_BITS */ if ( Pp[i] == '1' ) gf_exp[GF_BITS] ^= mask; } /* * now gf_exp[GF_BITS] = \alpha ** GF_BITS is complete, so can als * compute its inverse. */ gf_log[gf_exp[GF_BITS]] = GF_BITS; /* * Poly-repr of \alpha ** (i+1) is given by poly-repr of * \alpha ** i shifted left one-bit and accounting for any * \alpha ** GF_BITS term that may occur when poly-repr of * \alpha ** i is shifted. */ mask = 1 << (GF_BITS - 1 ) ; for (i = GF_BITS + 1; i < GF_SIZE; i++) { if (gf_exp[i - 1] >= mask) gf_exp[i] = gf_exp[GF_BITS] ^ ((gf_exp[i - 1] ^ mask) << 1); else gf_exp[i] = gf_exp[i - 1] << 1; gf_log[gf_exp[i]] = i; } /* * log(0) is not defined, so use a special value */ gf_log[0] = GF_SIZE ; /* set the extended gf_exp values for fast multiply */ for (i = 0 ; i < GF_SIZE ; i++) gf_exp[i + GF_SIZE] = gf_exp[i] ; /* * again special cases. 0 has no inverse. This used to * be initialized to GF_SIZE, but it should make no difference * since noone is supposed to read from here. */ inverse[0] = 0 ; inverse[1] = 1; for (i=2; i<=GF_SIZE; i++) inverse[i] = gf_exp[GF_SIZE-gf_log[i]]; } /* * Various linear algebra operations that i use often. */ /* * addmul() computes dst[] = dst[] + c * src[] * This is used often, so better optimize it! Currently the loop is * unrolled 16 times, a good value for 486 and pentium-class machines. * The case c=0 is also optimized, whereas c=1 is not. These * calls are unfrequent in my typical apps so I did not bother. * * Note that gcc on */ #define addmul(dst, src, c, sz) \ if (c != 0) addmul1(dst, src, c, sz) #define UNROLL 16 /* 1, 4, 8, 16 */ static void addmul1(gf *dst1, gf *src1, gf c, int sz) { USE_GF_MULC ; register gf *dst = dst1, *src = src1 ; gf *lim = &dst[sz - UNROLL + 1] ; GF_MULC0(c) ; #if (UNROLL > 1) /* unrolling by 8/16 is quite effective on the pentium */ for (; dst < lim ; dst += UNROLL, src += UNROLL ) { GF_ADDMULC( dst[0] , src[0] ); GF_ADDMULC( dst[1] , src[1] ); GF_ADDMULC( dst[2] , src[2] ); GF_ADDMULC( dst[3] , src[3] ); #if (UNROLL > 4) GF_ADDMULC( dst[4] , src[4] ); GF_ADDMULC( dst[5] , src[5] ); GF_ADDMULC( dst[6] , src[6] ); GF_ADDMULC( dst[7] , src[7] ); #endif #if (UNROLL > 8) GF_ADDMULC( dst[8] , src[8] ); GF_ADDMULC( dst[9] , src[9] ); GF_ADDMULC( dst[10] , src[10] ); GF_ADDMULC( dst[11] , src[11] ); GF_ADDMULC( dst[12] , src[12] ); GF_ADDMULC( dst[13] , src[13] ); GF_ADDMULC( dst[14] , src[14] ); GF_ADDMULC( dst[15] , src[15] ); #endif } #endif lim += UNROLL - 1 ; for (; dst < lim; dst++, src++ ) /* final components */ GF_ADDMULC( *dst , *src ); } /* * computes C = AB where A is n*k, B is k*m, C is n*m */ static void matmul(gf *a, gf *b, gf *c, int n, int k, int m) { int row, col, i ; for (row = 0; row < n ; row++) { for (col = 0; col < m ; col++) { gf *pa = &a[ row * k ]; gf *pb = &b[ col ]; gf acc = 0 ; for (i = 0; i < k ; i++, pa++, pb += m ) acc ^= gf_mul( *pa, *pb ) ; c[ row * m + col ] = acc ; } } } #ifdef DEBUG /* * returns 1 if the square matrix is identiy * (only for test) */ static int is_identity(gf *m, int k) { int row, col ; for (row=0; row 1) { fprintf(stderr, "singular matrix\n"); goto fail ; } } } } if (icol == -1) { fprintf(stderr, "XXX pivot not found!\n"); goto fail ; } found_piv: ++(ipiv[icol]) ; /* * swap rows irow and icol, so afterwards the diagonal * element will be correct. Rarely done, not worth * optimizing. */ if (irow != icol) { for (ix = 0 ; ix < k ; ix++ ) { SWAP( src[irow*k + ix], src[icol*k + ix], gf) ; } } indxr[col] = irow ; indxc[col] = icol ; pivot_row = &src[icol*k] ; c = pivot_row[icol] ; if (c == 0) { fprintf(stderr, "singular matrix 2\n"); goto fail ; } if (c != 1 ) { /* otherwhise this is a NOP */ /* * this is done often , but optimizing is not so * fruitful, at least in the obvious ways (unrolling) */ DEB( pivswaps++ ; ) c = inverse[ c ] ; pivot_row[icol] = 1 ; for (ix = 0 ; ix < k ; ix++ ) pivot_row[ix] = gf_mul(c, pivot_row[ix] ); } /* * from all rows, remove multiples of the selected row * to zero the relevant entry (in fact, the entry is not zero * because we know it must be zero). * (Here, if we know that the pivot_row is the identity, * we can optimize the addmul). */ id_row[icol] = 1; if (bcmp(pivot_row, id_row, k*sizeof(gf)) != 0) { for (p = src, ix = 0 ; ix < k ; ix++, p += k ) { if (ix != icol) { c = p[icol] ; p[icol] = 0 ; addmul(p, pivot_row, c, k ); } } } id_row[icol] = 0; } /* done all columns */ for (col = k-1 ; col >= 0 ; col-- ) { if (indxr[col] <0 || indxr[col] >= k) fprintf(stderr, "AARGH, indxr[col] %d\n", indxr[col]); else if (indxc[col] <0 || indxc[col] >= k) fprintf(stderr, "AARGH, indxc[col] %d\n", indxc[col]); else if (indxr[col] != indxc[col] ) { for (row = 0 ; row < k ; row++ ) { SWAP( src[row*k + indxr[col]], src[row*k + indxc[col]], gf) ; } } } error = 0 ; fail: free(indxc); free(indxr); free(ipiv); free(id_row); free(temp_row); return error ; } /* * fast code for inverting a vandermonde matrix. * XXX NOTE: It assumes that the matrix * is not singular and _IS_ a vandermonde matrix. Only uses * the second column of the matrix, containing the p_i's. * * Algorithm borrowed from "Numerical recipes in C" -- sec.2.8, but * largely revised for my purposes. * p = coefficients of the matrix (p_i) * q = values of the polynomial (known) */ int invert_vdm(gf *src, int k) { int i, j, row, col ; gf *b, *c, *p; gf t, xx ; if (k == 1) /* degenerate case, matrix must be p^0 = 1 */ return 0 ; /* * c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1 * b holds the coefficient for the matrix inversion */ c = NEW_GF_MATRIX(1, k); b = NEW_GF_MATRIX(1, k); p = NEW_GF_MATRIX(1, k); for ( j=1, i = 0 ; i < k ; i++, j+=k ) { c[i] = 0 ; p[i] = src[j] ; /* p[i] */ } /* * construct coeffs. recursively. We know c[k] = 1 (implicit) * and start P_0 = x - p_0, then at each stage multiply by * x - p_i generating P_i = x P_{i-1} - p_i P_{i-1} * After k steps we are done. */ c[k-1] = p[0] ; /* really -p(0), but x = -x in GF(2^m) */ for (i = 1 ; i < k ; i++ ) { gf p_i = p[i] ; /* see above comment */ for (j = k-1 - ( i - 1 ) ; j < k-1 ; j++ ) c[j] ^= gf_mul( p_i, c[j+1] ) ; c[k-1] ^= p_i ; } for (row = 0 ; row < k ; row++ ) { /* * synthetic division etc. */ xx = p[row] ; t = 1 ; b[k-1] = 1 ; /* this is in fact c[k] */ for (i = k-2 ; i >= 0 ; i-- ) { b[i] = c[i+1] ^ gf_mul(xx, b[i+1]) ; t = gf_mul(xx, t) ^ b[i] ; } for (col = 0 ; col < k ; col++ ) src[col*k + row] = gf_mul(inverse[t], b[col] ); } free(c) ; free(b) ; free(p) ; return 0 ; } static int fec_initialized = 0 ; static void init_fec() { TICK(ticks[0]); generate_gf(); TOCK(ticks[0]); DDB(fprintf(stderr, "generate_gf took %ldus\n", ticks[0]);) TICK(ticks[0]); init_mul_table(); TOCK(ticks[0]); DDB(fprintf(stderr, "init_mul_table took %ldus\n", ticks[0]);) fec_initialized = 1 ; } /* * This section contains the proper FEC encoding/decoding routines. * The encoding matrix is computed starting with a Vandermonde matrix, * and then transforming it into a systematic matrix. */ #define FEC_MAGIC 0xFECC0DEC struct fec_parms { u_long magic ; int k, n ; /* parameters of the code */ gf *enc_matrix ; } ; void fec_free(struct fec_parms *p) { if (p==NULL || p->magic != ( ( (FEC_MAGIC ^ p->k) ^ p->n) ^ (int)(p->enc_matrix)) ) { fprintf(stderr, "bad parameters to fec_free\n"); return ; } free(p->enc_matrix); free(p); } /* * create a new encoder, returning a descriptor. This contains k,n and * the encoding matrix. */ struct fec_parms * fec_new(int k, int n) { int row, col ; gf *p, *tmp_m ; struct fec_parms *retval ; if (fec_initialized == 0) init_fec(); if (k > GF_SIZE + 1 || n > GF_SIZE + 1 || k > n ) { fprintf(stderr, "Invalid parameters k %d n %d GF_SIZE %d\n", k, n, GF_SIZE ); return NULL ; } retval = my_malloc(sizeof(struct fec_parms), "new_code"); retval->k = k ; retval->n = n ; retval->enc_matrix = NEW_GF_MATRIX(n, k); retval->magic = ( ( FEC_MAGIC ^ k) ^ n) ^ (int)(retval->enc_matrix) ; tmp_m = NEW_GF_MATRIX(n, k); /* * fill the matrix with powers of field elements, starting from 0. * The first row is special, cannot be computed with exp. table. */ tmp_m[0] = 1 ; for (col = 1; col < k ; col++) tmp_m[col] = 0 ; for (p = tmp_m + k, row = 0; row < n-1 ; row++, p += k) { for ( col = 0 ; col < k ; col ++ ) p[col] = gf_exp[modnn(row*col)]; } /* * quick code to build systematic matrix: invert the top * k*k vandermonde matrix, multiply right the bottom n-k rows * by the inverse, and construct the identity matrix at the top. */ TICK(ticks[3]); invert_vdm(tmp_m, k); /* much faster than invert_mat */ matmul(tmp_m + k*k, tmp_m, retval->enc_matrix + k*k, n - k, k, k); /* * the upper matrix is I so do not bother with a slow multiply */ bzero(retval->enc_matrix, k*k*sizeof(gf) ); for (p = retval->enc_matrix, col = 0 ; col < k ; col++, p += k+1 ) *p = 1 ; free(tmp_m); TOCK(ticks[3]); DDB(fprintf(stderr, "--- %ld us to build encoding matrix\n", ticks[3]);) DEB(pr_matrix(retval->enc_matrix, n, k, "encoding_matrix");) return retval ; } /* * fec_encode accepts as input pointers to n data packets of size sz, * and produces as output a packet pointed to by fec, computed * with index "index". */ void fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz) { int i, k = code->k ; gf *p ; if (GF_BITS > 8) sz /= 2 ; if (index < k) bcopy(src[index], fec, sz*sizeof(gf) ) ; else if (index < code->n) { p = &(code->enc_matrix[index*k] ); bzero(fec, sz*sizeof(gf)); for (i = 0; i < k ; i++) addmul(fec, src[i], p[i], sz ) ; } else fprintf(stderr, "Invalid index %d (max %d)\n", index, code->n - 1 ); } /* * shuffle move src packets in their position */ static int shuffle(gf *pkt[], int index[], int k) { int i; for ( i = 0 ; i < k ; ) { if (index[i] >= k || index[i] == i) i++ ; else { /* * put pkt in the right position (first check for conflicts). */ int c = index[i] ; if (index[c] == c) { DEB(fprintf(stderr, "\nshuffle, error at %d\n", i);) return 1 ; } SWAP(index[i], index[c], int) ; SWAP(pkt[i], pkt[c], gf *) ; } } DEB( /* just test that it works... */ for ( i = 0 ; i < k ; i++ ) { if (index[i] < k && index[i] != i) { fprintf(stderr, "shuffle: after\n"); for (i=0; ik ; gf *p, *matrix = NEW_GF_MATRIX(k, k); TICK(ticks[9]); for (i = 0, p = matrix ; i < k ; i++, p += k ) { #if 1 /* this is simply an optimization, not very useful indeed */ if (index[i] < k) { bzero(p, k*sizeof(gf) ); p[i] = 1 ; } else #endif if (index[i] < code->n ) bcopy( &(code->enc_matrix[index[i]*k]), p, k*sizeof(gf) ); else { fprintf(stderr, "decode: invalid index %d (max %d)\n", index[i], code->n - 1 ); free(matrix) ; return NULL ; } } TICK(ticks[9]); if (invert_mat(matrix, k)) { free(matrix); matrix = NULL ; } TOCK(ticks[9]); return matrix ; } /* * fec_decode receives as input a vector of packets, the indexes of * packets, and produces the correct vector as output. * * Input: * code: pointer to code descriptor * pkt: pointers to received packets. They are modified * to store the output packets (in place) * index: pointer to packet indexes (modified) * sz: size of each packet */ int fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz) { gf *m_dec ; gf **new_pkt ; int row, col , k = code->k ; if (GF_BITS > 8) sz /= 2 ; if (shuffle(pkt, index, k)) /* error if true */ return 1 ; m_dec = build_decode_matrix(code, pkt, index); if (m_dec == NULL) return 1 ; /* error */ /* * do the actual decoding */ new_pkt = my_malloc (k * sizeof (gf * ), "new pkt pointers" ); for (row = 0 ; row < k ; row++ ) { if (index[row] >= k) { new_pkt[row] = my_malloc (sz * sizeof (gf), "new pkt buffer" ); bzero(new_pkt[row], sz * sizeof(gf) ) ; for (col = 0 ; col < k ; col++ ) addmul(new_pkt[row], pkt[col], m_dec[row*k + col], sz) ; } } /* * move pkts to their final destination */ for (row = 0 ; row < k ; row++ ) { if (index[row] >= k) { bcopy(new_pkt[row], pkt[row], sz*sizeof(gf)); free(new_pkt[row]); } } free(new_pkt); free(m_dec); return 0; } /*********** end of FEC code -- beginning of test code ************/ #if (TEST || DEBUG) void test_gf() { int i ; /* * test gf tables. Sufficiently tested... */ for (i=0; i<= GF_SIZE; i++) { if (gf_exp[gf_log[i]] != i) fprintf(stderr, "bad exp/log i %d log %d exp(log) %d\n", i, gf_log[i], gf_exp[gf_log[i]]); if (i != 0 && gf_mul(i, inverse[i]) != 1) fprintf(stderr, "bad mul/inv i %d inv %d i*inv(i) %d\n", i, inverse[i], gf_mul(i, inverse[i]) ); if (gf_mul(0,i) != 0) fprintf(stderr, "bad mul table 0,%d\n",i); if (gf_mul(i,0) != 0) fprintf(stderr, "bad mul table %d,0\n",i); } } #endif /* TEST */ rmdp.c100644 423 423 13126 6543317625 7645 0ustar luigi/* * rmdp.c -- packet assembly/disassembly functions * and parsing utilities * * This file is part of * * rmdp -- Reliable Multicast data Distribution Protocol * * (C) 1996-1998 Luigi Rizzo and Lorenzo Vicisano * (luigi@iet.unipi.it, vicisano@cs.ucl.ac.uk) * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Luigi Rizzo, * Lorenzo Vicisano and other contributors. * 4. Neither the name of the Authors nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. * */ #include "rmdp.h" /* #include */ #ifdef SunOS #include #endif /* * compute hopefully unique id */ ui32 compute_id() { short seed[3]; struct timeval getime; gettimeofday(&getime, NULL); seed[0] = (short)getime.tv_usec; seed[1] = (short)(getime.tv_usec*0xffff); seed[2] = (short)getime.tv_sec; return(nrand48(seed)); } n32 url_addr(char *s) { char *p1, *p2; struct hostent *hp; p1=strstr(s,"//"); if (p1 == NULL) return 0; p2 = strstr(p1+2,"/"); if (p2 == NULL) return 0; *p2 = '\0'; hp = gethostbyname(p1+2); if (hp == NULL) { fprintf(stderr,"gethostbyname failed\n"); return 0; } *p2 = '/' ; return ((struct in_addr *)hp->h_addr)->s_addr ; } char * validpath(char *path) { n32 pa; static char myhn[256]; static n32 myha[10]; static int n_myha =0; int i; char *p; if (strncmp(path,"rmdp://",7) ) { fprintf(stderr,"can only serve rmdp!\n"); return NULL ; } pa = url_addr(path); if (n_myha == 0) { struct hostent *hp; if (gethostname(myhn, sizeof(myhn)) >= 0) { if ((hp = gethostbyname(myhn)) >= 0) { #ifdef __FreeBSD__ while( ((struct in_addr *)hp->h_addr_list[n_myha]) != NULL) { myha[n_myha] = ((struct in_addr *)hp->h_addr_list[n_myha])->s_addr; fprintf(stderr,"local addr %s\n", inet_ntoa(*(struct in_addr *)&(myha[n_myha]) ) ); if (++n_myha == 10) break; } #else myha[0] = ((struct in_addr *)hp->h_addr)->s_addr; n_myha = 1 ; #endif } } } for (i=0; i< n_myha; i++) { if (pa == myha[i]) { p = strstr(path+7,"/"); while (p && *p =='/') p++; return p; } } DDB(fprintf(stderr, "sorry req does not match any local addr\n")); return NULL; } /*** parameter parsing ***/ int set_link_addr(char *s, char *a, int addrlen, n16 *p, char *msg) { char *s1, *q = a ; strncpy(q, s, addrlen); s1=strchr(q,'/'); if (s1) { *p=htons(atoi(s1+1)); *s1='\0'; printf("%s session [%s] [%d]\n", msg, q, ntohs(*p)); } else return -1; return 0; } /**** packet building utilities ***/ u_char * add_magic(u_char *p) { *(n32 *)p = htonl(RMDP_V0_MAGIC) ; return p+4; } u_char * add_opt1(u_char *p, RMDP_TAG tag) { *p++ = tag; return p; } u_char * add_opt2(u_char *p, RMDP_TAG tag, n16 value) { *p++ = tag; *p++ = 4; bcopy(&value, p, 2); return p+2; } u_char * add_opt4(u_char *p, RMDP_TAG tag, n32 value) { *p++ = tag; *p++ = 6; bcopy(&value, p, 4); return p+4; } u_char * add_opts(u_char *p, RMDP_TAG tag, char *value) { n16 n = htons(strlen(value) + 1 + 3 /* tag + len */); *p++ = tag; bcopy(&n, p, 2); n = ntohs(n) - 3 ; bcopy(value, p+2, n); return p+2+n; } /*** packet decoding utilities ***/ int check_magic(u_char *p, int len) { n32 l; if (len < 4 ) /* at least the magic... */ return 0; bcopy(p, &l, 4); if (l != ntohl(RMDP_V0_MAGIC) ) { DEB(fprintf(stderr, "bad magic number 0x%08x...\n", (int)ntohl(l) )); return 0; } return 1; } int getoptlen(u_char *p, int len) { i16 l; u_char opt = *p; if (opt >= 0x80) { /* variable len, 16 bit field */ if (len < 3) { fprintf(stderr, "opt 0x%02x missing length\n", opt); return 0; } bcopy(p+1, &l, 2); l = ntohs(l); } else if (opt >= 0x10) { if (len < 2) { fprintf(stderr, "opt 0x%02x missing length\n", opt); return 0; } l = p[1]; } else l = 1; if (l > len) { fprintf(stderr, "opt 0x%02x len %d (%d) missing data\n", opt, l, len); return 0; } return l; } sender.c100644 423 423 22416 6547135424 10164 0ustar luigi/* * sender.c -- sender main routines * * This file is part of * * rmdp -- Reliable Multicast data Distribution Protocol * * (C) 1996, 1997 Luigi Rizzo and Lorenzo Vicisano * (luigi@iet.unipi.it, vicisano@cs.ucl.ac.uk) * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Luigi Rizzo, * Lorenzo Vicisano and other contributors. * 4. Neither the name of the Authors nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. * */ #include "rmdp.h" #include "sessions.h" ui32 dumm_addr; ui16 dumm_port; int k_glob = 32, n_glob = 256; int ttl_glob = TTL; int data_pkt_len = DATA_SIZE; EVLIST *events; /* callback attach. and timers */ ui32 source_id; void *no_ar; /* * various auxiliary functions ** */ void badcmdline(char *arg) { fprintf(stderr, "Bad argument %s\nCommand line:\n\t rmdpd [options]\n" "options:\n" "\t-d \n" "\t-c \n" "\t-k \n" "\t-n \n" "\t-l \n" "\t-t \n", arg); exit(1); } /*** I must find a better place for these two... ***/ n32 reply_addr; n16 reply_port; int handle_v1req(u_char *rq, int len, u_char *resp) { u_char *p = rq, *tx = resp; char *thepath = NULL ; n32 s_id, c_id, r_id, r_addr, m_addr ; n16 m_port, r_port ; ui32 req_rate = 0; int unicast_reply = 0 ; RMDP_TAG type = 0; c_id = 0; r_id = 0; s_id = 0; r_addr = 0; r_port = 0; m_addr = 0; m_port = 0; if (!check_magic(rq, len)) return 0; p = rq + 4; len -= 4; fprintf(stderr,"REQ: "); while (len > 0) { u_char opt = *p; i16 l = getoptlen(p, len) ; if (l == 0) return 0; len -= l; switch(opt) { case RMDP_T_PAD : fprintf(stderr, " pad,"); break; case RMDP_T_END : fprintf(stderr, " "); len = 0; break ; case RMDP_T_NEW_REQ : /* a new request */ type = opt; fprintf(stderr, " new,"); break; case RMDP_T_R_CONT : type = opt; fprintf(stderr, " continue,"); break; case RMDP_T_R_RESUME : type = opt; fprintf(stderr, " resume,"); break; case RMDP_T_R_UNICAST : unicast_reply = 1; fprintf(stderr, " unicast,"); break; case RMDP_T_C_ID : bcopy(p+2, &c_id , 4); fprintf(stderr, " c_id 0x%08x,", c_id); break; case RMDP_T_R_ID : bcopy(p+2, &r_id , 4); fprintf(stderr, " r_id 0x%08x,", r_id); break; case RMDP_T_S_ID : bcopy(p+2, &s_id , 4); fprintf(stderr, " s_id 0x%08x", s_id); break; case RMDP_T_R_RATE : bcopy(p+2, &req_rate , 4); req_rate = ntohl(req_rate); fprintf(stderr, " rate %d b/s,", req_rate); break; case RMDP_T_R_ADDR : bcopy(p+2, &r_addr , 4); fprintf(stderr, "\n\treply addr %s,", inet_ntoa(*(struct in_addr *)&r_addr) ); break; case RMDP_T_R_PORT : bcopy(p+2, &r_port , 2); fprintf(stderr, " reply port %d,", ntohs(r_port) ); break; case RMDP_T_R_FILE : thepath = p+3; fprintf(stderr, "\n\tfile \"%s\",", thepath); break; default : fprintf(stderr, "\noption 0x%02x len %d unknown\n", opt, l); break ; } p += l; } fprintf(stderr, "\n"); switch (type) { case RMDP_T_NEW_REQ : /* need file, r_id, c_id, r_addr, r_port */ if (!(thepath && r_id && c_id && r_addr && r_port)) { fprintf(stderr, "missing parameters... \n"); if (thepath == NULL) fprintf(stderr,"no path\n"); if (r_id == 0) fprintf(stderr,"no r_id\n"); if (c_id == 0) fprintf(stderr,"no c_id\n"); if (r_addr == 0) fprintf(stderr,"no r_addr\n"); if (r_port == 0) fprintf(stderr,"no r_port\n"); return 0; } if (unicast_reply) { m_addr = r_addr; m_port = r_port; } /*** *** check if the path is acceptable, otherwise do not even respond ***/ thepath = validpath(thepath); if (thepath == NULL) { fprintf(stderr,"+++ validpath %s returns null\n", thepath); return 0 ; } schedule(thepath, c_id, r_id, &req_rate, &s_id, &m_addr, &m_port); tx = add_magic(resp); tx = add_opt1(tx, RMDP_T_RESP); tx = add_opt4(tx, RMDP_T_C_ID, c_id); tx = add_opt4(tx, RMDP_T_R_ID, r_id); if (s_id == 0) { /* file not found ... */ char buf[256]; sprintf(buf, "xx %s not found xx", thepath) ; tx = add_opts( tx, RMDP_T_ERRMSG, buf ); } else { tx = add_opt4(tx, RMDP_T_S_ID, s_id); tx = add_opt4(tx, RMDP_T_D_ADDR, m_addr); tx = add_opt2(tx, RMDP_T_D_PORT, m_port); tx = add_opt4(tx, RMDP_T_R_RATE, htonl(req_rate)); } tx = add_opt1(tx, RMDP_T_END); reply_addr = r_addr ; reply_port = r_port ; break; case RMDP_T_R_CONT : /* continue transmission */ if (!s_id) { fprintf(stderr, "missing parameters... \n"); return 0; } prolong(s_id); /* XXX just accept... */ } return tx - resp ; /* XXX */ } /* * handle an incoming request */ int handle_req(void *p) { struct sockaddr_in srcaddr; int ch = showselected(events); int srcaddrlen = sizeof(srcaddr); int len; char request[1024]; u_char response[1024]; if ((len = recvfrom(ch, (char *)&request, sizeof(request), 0, (struct sockaddr *)&srcaddr, &srcaddrlen))<=0) { perror("sender: recvfrom"); return(-1); } fprintf(stderr, "\n=== got packet of size %d on sock %d\n", len, ch); len = handle_v1req(request, len, response); if (len) { int risu = -1; int rsock = openssock(reply_addr, reply_port, 0 /* XXX TTL */ ); if (rsock) { risu = send(rsock, response, len, 0) ; close(rsock); } if (risu < 0 ) fprintf(stderr, "rmdpd: error sending response\n"); } return 0; } int ctrls, datas; int main(int argc, char *argv[]) { int i; char dataaddr[ADDRLEN], ctrladdr[ADDRLEN]; /* data & control m-cast addresses */ n16 dport = 0 , cport = 0; ui8 ttl=TTL; extern char *optarg; extern int optind; /* parse command line... */ while ((i = getopt(argc,argv,"t:d:c:k:n:l:")) != EOF) { switch(i) { case 'd': /* data addr/port */ if(set_link_addr(optarg, dataaddr, ADDRLEN, &dport, "data")<0) badcmdline(optarg); break; case 'c': /* command addr/port */ if(set_link_addr(optarg, ctrladdr, ADDRLEN, &cport, "control")<0) badcmdline(optarg); break; case 't': /* TTL */ ttl = atoi(optarg); if (ttl <= 0 || ttl > 255) badcmdline(optarg); break; case 'k': /* k */ k_glob = atoi(optarg); fprintf(stderr,"rmdpd: k = %d\n", k_glob); if (k_glob <= 0 || k_glob > 64) badcmdline(optarg); break; case 'n': /* n */ n_glob = atoi(optarg); if (n_glob < 0) badcmdline(optarg); break; case 'l': /* data packet size */ data_pkt_len = atoi(optarg); if (data_pkt_len < 16 /* XXX */ || data_pkt_len > MAX_DATA_PKT_LEN) badcmdline(optarg); break; } } if (cport == 0) set_link_addr(DEFAULT_CPORT, ctrladdr, ADDRLEN, &cport, "control"); if (dport == 0) set_link_addr(DEFAULT_DPORT, dataaddr, ADDRLEN, &dport, "data"); fprintf(stderr, "using ttl %d, k %d, n %d\n", ttl, k_glob, n_glob); argc -= optind; argv += optind; /* init encoding functions */ if (n_glob<=k_glob) { fprintf(stderr,"Sorry, you have chosen no redundancy\n"); exit(1); } if (n_glob>256) { fprintf(stderr,"Sorry, cannot handle %d FEC packets\n" "please reduce n\n", n_glob); exit(1); } fec_code = fec_new(k_glob, n_glob); events = createevlist(); dumm_addr = inet_addr(dataaddr) ; dumm_port = dport; datas = openssock(dumm_addr, dport, ttl /* TTL */); /*** open the multicast channel ***/ ctrls = openrsock(inet_addr(ctrladdr), &cport); insertchan(events, ctrls, 1 /* XXX */, handle_req, no_ar); #if 0 /*** open the unicast channel ***/ ctrls = openrsock(0, &cport); insertchan(events, ctrls, 1 /* XXX */, handle_req, no_ar); #endif while (getevent(events, 1)!=EV_NO_PENDING); fec_free(fec_code); exit(0); } sessions.c100644 423 423 25200 6543317715 10545 0ustar luigi/* * sessions.c -- session handling for rmdpd * * This file is part of * * rmdp -- Reliable Multicast data Distribution Protocol * * (C) 1996-1998 Luigi Rizzo and Lorenzo Vicisano * (luigi@iet.unipi.it, vicisano@cs.ucl.ac.uk) * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Luigi Rizzo, * Lorenzo Vicisano and other contributors. * 4. Neither the name of the Authors nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. * */ #include "rmdp.h" #include "sessions.h" #include "event.h" #include /* for stat */ extern EVLIST *events; extern ui32 source_id; extern int data_pkt_len; extern int k_glob; extern int n_glob; extern int ttl_glob; extern ui32 dumm_addr; extern ui16 dumm_port; extern int datas; void *fec_code; SESSION *all_sessions=NULL; void prolong(n32 s_id) { SESSION *se ; for (se = all_sessions ; se ; se = se -> next ) { if (se->id == s_id) { DEB(fprintf(stderr,"session found\n");) if (se->pkts_left == 0) { se_start(se, 1 /* set pkts_left */); } else se->pkts_left += 3*se->B ; return; } } fprintf(stderr, "--- Warning, session 0x%08x not found\n", s_id); } /* * check if a request for this object is already scheduled; * in case schedule it, or modify the schedule parameters. * return the session id and the actual bandwidth; */ void schedule(char *path, ui32 c_id, ui32 r_id, ui32 *req_bw, ui32 *s_id, n32 *addr, n16 *port) { SESSION *se; /* see if an already scheduled session for this pathname * can be extended and in case change its parameters... * Unicast sessions explicitly set m_addr, so there is no * already existing session. */ for (se=all_sessions; se; se= se->next) { if (!strcmp(path, se->fname) && se->ch == datas) { if ( (*addr == 0 ) /* multicast session */ || ( (se->r_id == r_id ) && (se->c_id == c_id) ) ) { DEB(fprintf(stderr, "already have an active session for %s\n", path) ); *s_id = se->id ; *addr = se->ip ; *port = se->port ; if (se->pkts_left == 0) { if (*req_bw < 1000 || *req_bw > 1024*1024 ) *req_bw = 64*1024 ; /* bit/s */ se->rate = (int)(8192 * 1.0e6/ *req_bw) ; se_start(se, 1 /* set pkts_left */); } else { *req_bw = (int)(8192*1.0e6/se->rate); se->pkts_left = se->B * (3 + se->k); /* XXX fixme */ } DDB(fprintf(stderr, "+++ running at %d Kbit/s\n", (int)(8192*1.0e3/se->rate));) return ; } } } { struct stat filest; int i=stat(path, &filest); if (i < 0 ) { /* not found ? */ *s_id = 0 ; return ; /* XXX what should we do? */ } } if (NULL == (se = calloc(1, sizeof(SESSION)))) { perror("sender: calloc"); exit(1); /* XXX */ } se->next = all_sessions; all_sessions = se ; *s_id = compute_id(); /* XXX must see if unique and != 0 */ if (*addr == 0) { se->ip = *addr = dumm_addr; /* XXX */ se->port = *port = dumm_port; /* XXX */ } else { se->ip = *addr ; se->port = *port ; se->ch = openssock(*addr, *port, 0 /* TTL XXX */ ); } se->k = k_glob; se->n = n_glob; se->flags = 0; se->c_id = c_id; se->r_id = r_id; se->id = *s_id; se->ttl = ttl_glob; /* XXX */ if (*req_bw < 1000 || *req_bw > 1024*1024 ) *req_bw = 64*1024 ; /* bit/s */ se->rate = (int)(8192 * 1.0e6/ *req_bw) ; fprintf(stderr, "+++ running at %8.3f Kbit/s\n", (8000.0e3/se->rate)); /* 1K = 1024 bits! */ se->idx = 0; if (NULL == (se->fname = calloc(1, strlen(path)+1))) { perror("sender: calloc"); exit(1); /* XXX */ } strcpy(se->fname, path); se_start(se, 1 /* set pkts_left */); } int se_start(SESSION *s, int set_pkts_left) { ui32 fpkts; struct stat filest; int done, i, j; FILE *inf; if (s->alldata == NULL ) { fprintf(stderr, "--- reading file for %s\n", s->fname); stat(s->fname, &filest); (s->flength) = filest.st_size; if ((s->flength) == 0) { DEB(fprintf(stderr, "XXX se_start: flength == 0\n")); /* XXX */ } fpkts = ((s->flength)+data_pkt_len-1)/data_pkt_len; s->B = (fpkts+(s->k)-1)/(s->k); if (NULL == (inf=fopen(s->fname, "r"))) { DDB(fprintf(stderr, "XXX se_start: can't open %s\n", s->fname)); /* XXX */ } if (NULL == (s->alldata = (void *)calloc(s->B, sizeof(void *)))) { DDB(fprintf(stderr, "XXX se_start: can't allocate alldata\n")); /* XXX */ } for (done=0, i=0; i < s->B ; i++ ) { s->alldata[i] = (void **)calloc((s->k), sizeof(void *)) ; if ( s->alldata[i] == NULL ) { DDB(fprintf(stderr, "XXX se_start: can't allocate alldata\n")); /* XXX */ } for (j=0; j<(s->k); j++) { if (NULL ==(s->alldata[i][j] = (void *)calloc(1, data_pkt_len))) { DDB(fprintf(stderr, "XXX se_start: can't allocate alldata\n")); /* XXX */ } if (!done && (fread((void *)(s->alldata[i][j]), 1, data_pkt_len, inf)) != data_pkt_len) { done = 1; } } } } if (set_pkts_left) { s->pkts_left = s->B * (3 + s->k); /* XXX fixme */ if (s->ch == 0) /* XXX */ s->ch = datas ; settimer(events, s->id, s->rate, (CALLBACK)se_send, (void *)s, 0); } return(0); } /* * se_flush flushes the data buffers for a file, but does not * completely clean the session. This is done so that memory can * be recovered but state is retained for a sufficient time. */ int se_flush(SESSION *s) { int i, j; if (s->alldata) { for (i=0; i< s->B ; i++){ for (j=0; j < s->k ; j++) free(s->alldata[i][j]); free(s->alldata[i]); } free(s->alldata); s->alldata = NULL ; DEB(fprintf(stderr, "--- freed data for \"%s\"\n", s->fname)); } return 0 ; } /* * se_delete does delete a session, it aborts in case the session * has ben resumed (pkts_left > 0). * * note -- it might happen that a se_delete is received for a stale * session. */ int se_delete(SESSION *s) { int i; SESSION *se, *pr; struct timeval now; for (se=all_sessions; se ; pr = se, se = se->next) if (s == se) break; if (se == NULL) { fprintf(stderr,"-- received a delete for a stale session, ignoring\n"); return 0; } if (s->pkts_left > 0 ) { DDB(fprintf(stderr,"xxx session \"%s\" resumed!\n", s->fname);) s->busy = 0; return 0; } if (s->busy == 0) { s->busy = 1; settimer(events, s->id, 60*1000000, (CALLBACK)se_delete, s, 1); } timestamp_t (&now); fprintf(stderr,"--- now %ld.%06ld, flush %ld.%06ld, delete %ld.%06ld\n", now.tv_sec % 86400, now.tv_usec, s->flush_at.tv_sec % 86400 , s->flush_at.tv_usec, s->delete_at.tv_sec %86400 , s->delete_at.tv_usec); fprintf(stderr, "--- handling session 0x%08x\n", s); i= diff_t (now, s->flush_at); if (i < 0 ) { fprintf(stderr,"--- schedule a flush for 0x%08x in %d ms\n", s->id, -i/1000 ); settimer(events, s->id, -i, (CALLBACK)se_delete, s, 0); return 0 ; } se_flush(s); i= diff_t (now, s->delete_at); if (i < 0 ) { fprintf(stderr,"--- schedule a delete for 0x%08x in %d ms\n", s->id, -i/1000 ); settimer(events, s->id, -i, (CALLBACK)se_delete, s, 0); return 0 ; } /* * XXX should check unicast... */ if (s->ch != datas) { /* XXX hack... */ DDB(fprintf(stderr, "--- closing socket for \"%s\"\n", s->fname)); close(s->ch); } DDB(fprintf(stderr, "--- killing name for \"%s\"\n", s->fname)); free(s->fname); s->fname = NULL ; if (se == all_sessions) all_sessions = all_sessions->next; else pr->next = se->next; free(se); return 0 ; } int se_send(SESSION *s) { int pkt_ix, blk_ix; DATA_P packet; ui32 thislen; static int do_err = 1; send_again: if (s->alldata == NULL) se_start(s, 0 ); /* allocate and load... */ blk_ix = (s->idx)%(s->B); pkt_ix = ((s->idx)/(s->B))%(s->n); fec_encode(fec_code, s->alldata[blk_ix], &packet.data, pkt_ix, data_pkt_len); DEB(fprintf(stderr, "sending data (%3d %3d) on session %x\n", pkt_ix, blk_ix, s->id)) if (s->pkts_left < max(10, s->B) ) s->flags |= RMDP_F_LAST_ROUND ; else s->flags &= ~RMDP_F_LAST_ROUND ; FILL_D_H(packet, htons((ui16)(s->idx)), htonl(source_id), s->id, htons(pkt_ix), htons(blk_ix), htonl(s->flength), htons(s->k), s->flags, htons(data_pkt_len)); #ifdef DO_TIMING packet.timestamp = htonl(timestamp_u()); #endif (s->idx)++; thislen = data_pkt_len; if (send(s->ch, (char *)&packet, PKT_LEN-MAX_DATA_PKT_LEN+thislen, 0) == -1) { if (do_err) perror("se_send: send"); do_err = 0; /* XXX */ } else do_err = 1; if ( /* (s->flags & RMDP_F_LAST_ROUND) || */ (s->idx % s->B == 0)) { DDB(fprintf(stderr, "--- %4d packets left for session 0x%08x \"%s\"\r", s->pkts_left, s->id, s->fname) ); } s->pkts_left -- ; if (s->pkts_left) { if ( ( (s->idx - 1) & 0xc) == 0xc ) goto send_again;/* every 16 generates a burst of 5 */ settimer(events, s->id, s->rate, (CALLBACK)se_send, s, 0); } else { timestamp_t( &(s->flush_at) ); timestamp_t( &(s->delete_at) ); s->flush_at.tv_sec += 10; /* XXX */ s->delete_at.tv_sec += 120; /* XXX */ DDB(fprintf(stderr, "--- Session %x terminates in 10/120 sec\n", s->id)); if (s->busy) { DDB(fprintf(stderr, "--- pending delete for Session %x\n", s->id)); } else { s->busy = 1; settimer(events, s->id, 10*1000000, (CALLBACK)se_delete, s, 1); } } return 0; } receiver.c100644 423 423 44455 6547135436 10522 0ustar luigi/* * receiver.c -- receiver routines for rmdp. * * This file is part of * * rmdp -- Reliable Multicast data Distribution Protocol * * (C) 1996-1998 Luigi Rizzo and Lorenzo Vicisano * (luigi@iet.unipi.it, vicisano@cs.ucl.ac.uk) * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Luigi Rizzo, * Lorenzo Vicisano and other contributors. * 4. Neither the name of the Authors nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. */ /* * receiver main routines * * The receiver first sends requests with exponential backoff. * When data are missing, send continuation requests, again with * exponential backoff. */ /*** *** TODO - implement randomization of CONT requests - implement exponential backoff on requests. - make CONT request contain also the URL and original parameters; - implement checkpoint/recovery of a session - make the session id a fingerprint of the source. - add a "-n" flag so that the output file name is got from the URL - support a change of address/(session ID ?) upon a continuation request. *** ***/ #include "rmdp.h" #include "net.h" #define DEFAULT_RATE (64*1024) #define LOGGING #ifdef LOGGING #define LOG(x) x; FILE *logf; #else #define LOG(x) #endif EVLIST *eventlist; /* callback attach. and timers */ #define ID_GETRESPONSE 23 #define ID_ASK_CONT 34 #define ID_SENDREQ 100 #define ID_INITRX 101 #define ID_GETDATA 104 #define MAX_RETRY 20 /*** *** all these are `per session', and should be insulated, in case... ***/ char req_buf[1024]; /* requests to the server */ int ctrls; /* control (out) socket */ int response_received; /* set when the response is received */ int seen_last_round = 0; int cont_pending = 0; /* have a cont request pending */ int datas; /* data (in) socket */ char response[2048]; void *fec_code ; struct rx_session { void ***alldata; /* received packets... */ int **whichdata, *countdata; /* info on received packets */ n32 d_addr; /* multicast address for data */ n16 d_port; n32 myid; /* appl. level id of this receiver */ n32 myrequest; /* id of the request issued */ n32 session_id; /* id of the assigned session */ int B, N; /* encoding parameters */ int data_pkt_len; int missingblocks, duppackets; char outfn[256]; ui32 fpkts, flength; ui32 starttime; int data_rate; ui16 last_r; /* last received */ ui16 first_sn ; /* first sequence number */ ui32 last_time_s, last_time_r; ui32 tot_lost; int retries; } rxs; void decode(); int decode_block(int i); int get_data(void *p); int allocation(); int init_reception(void *p); int get_response(void *p); /* * various auxiliary functions ** */ void badcmdline(char *arg) { fprintf(stderr, "Bad argument %s\nCommand line:\n\t rmdp [options]\n" "options:\n" "\t-c \n" "\t-t \n" "\t-u -- use unicast UDP\n" "\t-r rate -- desired rx rate (bit/s)\n" "\t\n", arg); exit(1); } #define REQ_TIMEOUT 5000000 /* 5 sec. */ #define S_REQ_ARG struct s_req_arg S_REQ_ARG { int s; int req_len; u_char *request; }; int ask_cont(void *dummy) { char *r; cont_pending = 0 ; if (seen_last_round) { DDB(fprintf(stderr, "\nsending continue request %d\n", rxs.retries)); r = add_magic(req_buf); r = add_opt1(r, RMDP_T_R_CONT); r = add_opt4(r, RMDP_T_S_ID, rxs.session_id); if (send(ctrls, req_buf, r - req_buf, 0 ) == -1 ) { perror("cont_request: send"); } } else { DEB(fprintf(stderr, "ask_cont does not send, seen_last_round=0\n")); rxs.retries = 0; } return 0; } int send_request(S_REQ_ARG * s_arg) { if (response_received) { return 0; } DDB(fprintf(stderr, "send_request: sending req of size %d on desc %d, retry %d\n", s_arg->req_len, s_arg->s, rxs.retries) ); if (send(s_arg->s, (char *)(s_arg->request), s_arg->req_len, 0) == -1) { perror("send_request: send"); exit(1); } if (rxs.retries++ < MAX_RETRY) settimer(eventlist, ID_SENDREQ, REQ_TIMEOUT, (CALLBACK)send_request, (void *)s_arg, 0); else { fprintf(stderr,"Sorry, server not responding\n"); exit(1); } return(0); } int get_response(void *dummy) { struct sockaddr_in srcaddr; int ch = showselected(eventlist); int len, srcaddrlen=sizeof(srcaddr); ui32 rate; n32 c_id = 0, r_id = 0, s_id = 0; int type = 0; u_char *p ; char *errp = NULL ; DEB(fprintf(stderr, "get_response handling response from fd = %d\n", ch)); if ((len = recvfrom(ch, (char *)&response, sizeof(response), 0, (struct sockaddr *)&srcaddr, &srcaddrlen))<=0) perror("get_response: recvfrom"); DEB(fprintf(stderr, "--- pkt len %d\n", len) ); rxs.d_addr = 0 ; rxs.d_port = 0; if (!check_magic(response, len)) return 0; p = response + 4; len -= 4; fprintf(stderr,"\nREPLY: "); while (len > 0) { u_char opt = *p; i16 l = getoptlen(p, len) ; if (l == 0) return 0; len -= l; switch(opt) { case RMDP_T_RESP: type = RMDP_T_RESP ; break; case RMDP_T_PAD : fprintf(stderr, " pad,"); break; case RMDP_T_END : fprintf(stderr, " \n"); len = 0; break ; case RMDP_T_C_ID : bcopy(p+2, &c_id , 4); fprintf(stderr, " c_id 0x%08x,", c_id); break; case RMDP_T_R_RATE : { char c=' '; struct timeval tv; bcopy(p+2, &rate , 4); rxs.data_rate = rate = ntohl(rate); if (rate > 8192) { rate /= 1024; c='K'; } fprintf(stderr, " rate %d %cbit/s,", rate, c); tv.tv_sec = 5*8192/(1+rxs.data_rate); tv.tv_usec = 0; if (tv.tv_sec < 3) tv.tv_sec = 3; else if (tv.tv_sec > 20) tv.tv_sec = 20; set_default_timeout(eventlist, &tv); } break; case RMDP_T_R_ID : bcopy(p+2, &r_id , 4); fprintf(stderr, " r_id 0x%08x,", r_id); break; case RMDP_T_S_ID : bcopy(p+2, &s_id , 4); fprintf(stderr, " s_id 0x%08x,", s_id); break; case RMDP_T_D_ADDR : bcopy(p+2, &rxs.d_addr , 4); fprintf(stderr, "\n\tdata addr %s,", inet_ntoa(*(struct in_addr *)&rxs.d_addr) ); break; case RMDP_T_D_PORT : bcopy(p+2, &rxs.d_port , 2); fprintf(stderr, " data port %d,", ntohs(rxs.d_port) ); break; case RMDP_T_ERRMSG : errp = p + 3; break ; default : fprintf(stderr, "option 0x%02x len %d unknown\n", opt, l); break ; } p += l; } if ( ! ( (type == RMDP_T_RESP) && (c_id == rxs.myid) && (r_id == rxs.myrequest) ) ) return 0 ; /* not good or not mine! */ if ( ! ( rxs.d_port && rxs.d_addr && s_id ) ) { if (!errp) errp = "Server denied request"; fprintf(stderr, "Data unavailable, reason: %s\n", errp) ; exit ( 0 ); } response_received = 1; deletechan(eventlist, ch); /* use in net. order... XXX must handle s_id == 0 */ rxs.session_id = s_id; rxs.B = rxs.duppackets = 0; datas = openrsock(rxs.d_addr, &rxs.d_port); insertchan(eventlist, datas, ID_GETDATA, get_data, NULL); return(0); } /* * now allocation does not purge fixed fields of rxs, so * the session can be easily resumed. */ int allocation() { int done, i, j; rxs.retries = 0; rxs.last_r = -1; rxs.tot_lost = 0; rxs.alldata = calloc(rxs.B+1, sizeof(void *)); rxs.countdata = (int *)calloc(rxs.B, sizeof(int)); rxs.whichdata = (int **)calloc(rxs.B+1, sizeof(void *)); for (done=0, i=0; i <= rxs.B ; i++) { /* one more for decoding... */ rxs.whichdata[i] = (int *)calloc(rxs.N, sizeof(int)); rxs.alldata[i] = (void **)calloc(rxs.N, sizeof(void *)); for (j=0; j 1000) || (rxs.flength > 10000000) ) { fprintf(stderr, "something wrong with data sizes," " len 0x%08x B %d\n", rxs.flength , rxs.B); exit(1); } allocation(); rxs.starttime = timestamp_m(); rxs.first_sn = this_sn ; } rxs.retries = 0; if (rxs.countdata[blk_ix] >= rxs.N) { rxs.duppackets++; DDB(fprintf(stderr, "\nDUP! blk %d have already %d, %d totdup\r", blk_ix, rxs.countdata[blk_ix] , rxs.duppackets)) } else { int valid = 1; for (i=0; i < rxs.countdata[blk_ix]; i++) if (pkt_ix == rxs.whichdata[blk_ix][i]) { valid = 0; DEB(fprintf(stderr, "not valid.. blk %d pkt %d\n", blk_ix, pkt_ix)) break; } if (valid) { rxs.whichdata[blk_ix][rxs.countdata[blk_ix]] = pkt_ix; bcopy(packet.data, rxs.alldata[blk_ix][rxs.countdata[blk_ix]], rxs.data_pkt_len); rxs.countdata[blk_ix]++; DDB(fprintf(stderr, "got blk %4d pkt %4d (%4d pkts); " "miss %4d pkts %4d blks \r", blk_ix, pkt_ix, rxs.countdata[blk_ix], rxs.N-rxs.countdata[blk_ix],rxs.missingblocks)) if (rxs.countdata[blk_ix] == rxs.N) { rxs.missingblocks--; decode_block(blk_ix); } } } /* end reception ? */ if (rxs.missingblocks == 0) { ui32 endtime; int j ; endtime = timestamp_m(); deletechan(eventlist, chan); DDB(printf("Stats: %d useful, %d dup\n" "in %d sec. (%f pkts/sec)\n", rxs.B*rxs.N, rxs.duppackets, (endtime-rxs.starttime)/1000, (double)rxs.B*rxs.N/(1+(endtime-rxs.starttime)/1000))); decode(); /* XXX must free data structures */ if (rxs.last_r <= rxs.first_sn) i = (rxs.last_r + 65536 - rxs.first_sn) ; else i = (rxs.last_r - rxs.first_sn) ; i++ ; /* must be incremented to count sent pkts! */ j = rxs.B*rxs.N + rxs.duppackets ; DDB(fprintf(stderr, "rx %d out of %d pkts (loss rate %8.2f %%)\n", j, i, (double)(100*(i-j))/(double)i );) } return(0); } void decode() { if (rxs.outfn[0]) { int i, j, len = rxs.data_pkt_len; FILE *of; if (NULL==(of=fopen(rxs.outfn, "w"))) { perror("fopen"); exit(1); } for (i=0; i 0; j++) { len=rxs.data_pkt_len; if (rxs.flength < len) len=rxs.flength; rxs.flength -= len; fwrite(rxs.alldata[i][j], len, 1, of); } } fclose(of); } } int decode_block(int i) { if (fec_code == NULL) fec_code = fec_new(rxs.N, 256 /* XXX */ ); fec_decode(fec_code, rxs.alldata[i], rxs.whichdata[i], rxs.data_pkt_len); fprintf(stderr, "\n++ block %d done\n", i); return 0 ; } int main(int argc, char *argv[]) { int i, id; n32 myaddr; struct sockaddr_in s; char ctrladdr[ADDRLEN]; /* data & control m-cast addresses */ n16 cport = 0; n32 caddr = 0; /* data & control ports # (net order) */ int reqin; /* request (in) udp sock */ n16 reqin_p; /* request (in) udp port (net order) */ ui8 ttl = TTL; char req_str[512]; int unicast = 0 ; S_REQ_ARG send_r_a; char *r; rxs.retries = 0 ; bzero(&rxs, sizeof(rxs) ); strcpy(rxs.outfn,"fileout"); rxs.data_rate = DEFAULT_RATE ; /* parse command line... */ while ((i = getopt(argc,argv,"ur:t:d:c:o:")) != EOF) { switch(i) { case 'u': /* unicast reply */ unicast = 1; break; case 'r': /* data rate */ { int l = strlen(optarg); char c=' '; if (l>0) c=optarg[l-1]; rxs.data_rate = atoi(optarg) ; if (c=='K' || c=='k') rxs.data_rate *= 1024; else if (c=='M') rxs.data_rate *= 1024*1024; } break; case 'c': /* command addr/port */ if (set_link_addr(optarg, ctrladdr, ADDRLEN, &cport, "control")<0) badcmdline(optarg); break; case 't': /* TTL */ ttl = atoi(optarg); if (ttl <= 0 || ttl > 255) badcmdline(optarg); break; case 'o': /* output file */ strncpy(rxs.outfn, optarg, sizeof(rxs.outfn)); break; } } if (cport == 0) set_link_addr(DEFAULT_CPORT, ctrladdr, ADDRLEN, &cport, "control"); argc -= optind; argv += optind; if (argv[0]) { strncpy(req_str, argv[0], sizeof(req_str)); DEB( fprintf(stderr,"--- request is \"%s\"\n", req_str) ); } else { badcmdline(""); } caddr = inet_addr(ctrladdr); if (unicast) { /*** *** extract hostip from the request, use that as *** the address for the control session ***/ caddr = url_addr(req_str) ; if (caddr == 0) { fprintf(stderr, "sorry, no server address available for %s\n", req_str); exit(1); } fprintf(stderr, " addr is %s\n", inet_ntoa(*(struct in_addr *)&caddr) ); } fec_code = NULL ; eventlist = createevlist(); #ifdef LOGGING if (NULL==(logf=fopen("logfile", "w"))) { perror("fopen"); exit(1); } #endif rxs.myid = compute_id(); rxs.myrequest = compute_id(); /* request (out) mcast socket */ ctrls = openssock(caddr, cport, ttl); /* response (in) udp socket */ localname(&s, ctrls); myaddr = s.sin_addr.s_addr ; reqin_p = 0; reqin = openrsock(INADDR_ANY, &reqin_p); insertchan(eventlist, reqin, ID_GETRESPONSE, get_response, NULL); r = add_magic(req_buf); r = add_opt1(r, RMDP_T_NEW_REQ); if (unicast) r = add_opt1(r, RMDP_T_R_UNICAST); r = add_opt4(r, RMDP_T_R_ID, rxs.myrequest); r = add_opt4(r, RMDP_T_C_ID, rxs.myid); r = add_opt4(r, RMDP_T_R_RATE, htonl(rxs.data_rate)); r = add_opt4(r, RMDP_T_R_ADDR, myaddr); r = add_opt2(r, RMDP_T_R_PORT, reqin_p); r = add_opts(r, RMDP_T_R_FILE, req_str); r = add_opt1(r, RMDP_T_END); send_r_a.request = req_buf; send_r_a.req_len = r - req_buf ; response_received = 0; send_r_a.s = ctrls; fprintf(stderr, "--- schedule request...\n"); { struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; set_default_timeout(eventlist, &tv); } settimer(eventlist, ID_SENDREQ, 0, (CALLBACK)send_request, &send_r_a, 1); while (EV_TERMINATE != (id = getevent(eventlist, 1))) { switch(id) { case EV_DEFTIMEOUT : if (response_received && rxs.session_id) { rxs.retries++; if (rxs.retries >= MAX_RETRY) { /* should we give up ? */ fprintf(stderr, "sorry couldn't complete the reception!\n"); exit(0); } /*** change default timeout... XXX ***/ seen_last_round = 1; ask_cont(NULL); } else { DEB(fprintf(stderr,"got timeout -- shouldn't happen!\n")); exit(1); } break; case ID_GETDATA : break; default: fprintf(stderr,"got event %d\n", id); break; } } exit(0); } rmdp.h100644 423 423 20105 6542210757 7642 0ustar luigi/* * header file for rmdp * * This file is part of * * rmdp -- Reliable Multicast data Distribution Protocol * * (C) 1996-1998 Luigi Rizzo and Lorenzo Vicisano * (luigi@iet.unipi.it, vicisano@cs.ucl.ac.uk) * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Luigi Rizzo, * Lorenzo Vicisano and other contributors. * 4. Neither the name of the Authors nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. * */ #ifndef RMDP_H #define RMDP_H #include #include #include #include #include #include /* for send/receive... */ /* * these ought to go in fec.h ... * Remember, this only work for GF_BITS=8 */ void *fec_new(int k, int n); void fec_free(void *code); void fec_encode(void *code, void *data[], void *dst, int i, int sz); void fec_decode(void *code, void *data[], int i[], int sz); #define TTL 16 #define DATA_SIZE 1024 /* default ... */ typedef int (*CALLBACK)(void *); typedef char i8; /* 8 bit signed int */ typedef short i16; /* 16 bit signed int */ typedef int i32; /* 32 bit signed int */ #if defined(__alpha) typedef long i64; /* 64 bit signed int */ #endif typedef unsigned char ui8; /* 8 bit unsigned int */ typedef unsigned short ui16; /* 16 bit unsigned int */ typedef unsigned short n16; /* 16 bit net format */ typedef unsigned int ui32; /* 32 bit unsigned int */ typedef unsigned int n32; /* 32 bit net format */ #if defined(__alpha) typedef unsigned long ui64; /* 64 bit unsigned int */ #endif typedef unsigned long ui64; /* 64 bit unsigned int */ #include "compat.h" #include "event.h" #include "net.h" /*** *** make port number for CPORT & DPORT different, so that *** the sender does not listen for its own data ***/ #define DEFAULT_CPORT "224.5.5.5/5656" #define DEFAULT_DPORT "224.5.5.6/5657" #define DDB(x) {x; fflush(stderr);} #define DEB(x) #ifdef BYTE_ORDER #if (BYTE_ORDER == LITTLE_ENDIAN) #define _LITTLE_ENDIAN #elif BYTE_ORDER == BIG_ENDIAN #define _BIG_ENDIAN #endif #else /* BYTE_ORDER */ #if ( !defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) ) #error endianness not defined! check compat.h #endif #endif /* BYTE_ORDER */ ui32 compute_id(void); int set_link_addr(char *s, char *a, int addrlen, n16 *p, char *msg); n32 url_addr(char *); char *validpath(char *); /*** *** Control packets carry a version number, then a sequence of tags *** similar to TCP/BOOTP options. Tags are followed by a length *** (byte/2bytes) and the data item. The length includes both headers *** ***/ typedef u_char RMDP_TAG; #define RMDP_V0_MAGIC 0xa1e55ad0 /*** 1 byte, fixed length ***/ #define RMDP_T_PAD 0x0 /* len 1 */ #define RMDP_T_END 0x1 /* len 1 */ #define RMDP_T_NEW_REQ 0x2 /* len 1. New request */ #define RMDP_T_R_CONT 0x3 /* len 1. Continue sending */ #define RMDP_T_R_RESUME 0x4 /* len 1. Resume sending */ #define RMDP_T_R_UNICAST 0x5 /* len 1. unicast data */ #define RMDP_T_RESP 0x8 /* len 1. Response */ /*** n bytes, n=0..255 ***/ #define RMDP_T_S_ID 0x41 /* len 1+1+4. Session ID */ #define RMDP_T_C_ID 0x42 /* len 1+1+4. Client ID */ #define RMDP_T_R_ID 0x43 /* len 1+1+4. Request ID */ #define RMDP_T_R_ADDR 0x44 /* len 1+1+4. Reply addr */ #define RMDP_T_D_ADDR 0x45 /* len 1+1+4. Data (multicast) addr */ #define RMDP_T_R_RATE 0x46 /* len 1+1+4. request rate, bytes/s */ #define RMDP_T_R_PORT 0x50 /* len 1+1+2. Reply port */ #define RMDP_T_D_PORT 0x51 /* len 1+1+2. Data (multicast) port */ #define RMDP_T_R_LASTK 0x58 /* len 1+1+2. Last k seen (cont/resume) */ #define RMDP_T_R_LASTB 0x59 /* len 1+1+2. Last B seen (cont/resume) */ /*** n bytes, n = 0..64k-1 ***/ #define RMDP_T_R_FILE 0x81 /* len 1+2+s. File name */ #define RMDP_T_ERRMSG 0x82 /* len 1+2+s. Error message */ int check_magic(u_char *p, int len); u_char *add_magic(u_char *p); u_char *add_opt1(u_char *p, RMDP_TAG tag); u_char *add_opt2(u_char *p, RMDP_TAG tag, n16 value); u_char *add_opt4(u_char *p, RMDP_TAG tag, n32 value); u_char *add_opts(u_char *p, RMDP_TAG tag, char *value); int getoptlen(u_char *p, int len); #define RMDP_VERSION 0 /* packet types */ #define RMDP_DATA_T 0 /* data packet (down-link) */ #define RMDP_TREQ_T 1 /* tx request packet (up-link) */ #define RMDP_TRES_T 2 /* tx response paket (down-link) */ /** ** data packet ** 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | V=0 | T | flags | sequence # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | source id | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | session id | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | packet size (data only) | N | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | pkt idx | blk idx | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data | | .... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ** ** **/ extern void *fec_code ; #define MAX_DATA_PKT_LEN 4096 #define DATA_P struct data_p DATA_P { #ifdef _LITTLE_ENDIAN ui8 pad:1, type:3, /* packet type */ vers:4; /* version */ #endif #ifdef _BIG_ENDIAN ui8 vers:4, /* version */ type:3, /* packet type */ pad:1; #endif /* #define RMDP_F_LAST_ROUND htons(1) */ #define RMDP_F_LAST_ROUND '\1' ui8 flags; /* various flags, net order */ ui16 sn; /* sequence number */ ui32 suid; /* source ID */ ui32 seid; /* session ID */ ui16 packetsize; /* packet size (data only) */ ui16 the_N; /* value of N used at the source */ ui16 pkt_ix; /* packet # */ ui16 blk_ix; /* block # */ #ifdef DO_TIMING ui32 timestamp; #endif ui32 datasize; /* total data size */ char data[MAX_DATA_PKT_LEN]; }; #define FILL_D_H(p,t_sn,t_suid,se_id,pktn,blkn,dsz,n,f,pkts) { \ (p).vers=RMDP_VERSION; \ (p).type=RMDP_DATA_T; \ (p).sn=(t_sn); \ (p).suid=(t_suid); \ (p).seid=(se_id); \ (p).pkt_ix=(pktn); \ (p).blk_ix=(blkn); \ (p).datasize=(dsz); \ (p).the_N=(n); \ (p).flags=(f); \ (p).packetsize=(pkts); \ } #define PKT_LEN sizeof(DATA_P) #endif /* RMDP_H */ net.h100644 423 423 4521 6542206515 7447 0ustar luigi/* * net.h -- header for net routines. * * rmdp -- Reliable Multicast data Distribution Protocol * * (C) 1996-1998 Luigi Rizzo and Lorenzo Vicisano * (luigi@iet.unipi.it, vicisano@cs.ucl.ac.uk) * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Luigi Rizzo, * Lorenzo Vicisano and other contributors. * 4. Neither the name of the Authors nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. * */ #ifndef RMDP__NET_H #define RMDP__NET_H #include #include #include #include #include #include #include #include #include #include extern char *inet_ntoa(); #define NAME_LEN 128 int openssock(n32 addr, n16 port, int ttl); int openrsock(n32 addr, n16 *port); void localname(struct sockaddr_in * p, int ssock_); #endif /* RMDP__NET_H */ compat.h100644 423 423 5217 6542206447 10153 0ustar luigi/* * compat.h -- compatibility headers for rmdp. * * This file is part of * * rmdp -- Reliable Multicast data Distribution Protocol * * (C) 1996-1998 Luigi Rizzo and Lorenzo Vicisano * (luigi@iet.unipi.it, vicisano@cs.ucl.ac.uk) * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Luigi Rizzo, * Lorenzo Vicisano and other contributors. * 4. Neither the name of the Authors nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. * */ #ifndef RMDP__MISC_H #define RMDP__MISC_H #include #ifdef IRIX #include #endif #ifdef HPUX #include #endif #ifdef FreeBSD #include #endif #ifdef SunOS #include #endif /* return time in msec */ long timestamp_m(); /* return time in usec */ long timestamp_u(); long diff_t(struct timeval b, struct timeval a); /* return time in struct timeval */ void timestamp_t(struct timeval *t); #define ADDRLEN 32 #define MAX_PATH 256 #define min(x,y) (((x)>(y))?(y):(x)) #define max(x,y) (((x)<(y))?(y):(x)) #ifdef __FreeBSD__ #include #else #define bcopy(s, d, siz) memcpy((d), (s), (siz)) #define bzero(d, siz) memset((d), '\0', (siz)) #endif #endif /* RMDP__MISC_H */ event.h100644 423 423 7431 6542206477 10014 0ustar luigi/* * event.h -- header file for event handling routines * * rmdp -- Reliable Multicast data Distribution Protocol * * (C) 1996-1998 Luigi Rizzo and Lorenzo Vicisano * (luigi@iet.unipi.it, vicisano@cs.ucl.ac.uk) * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Luigi Rizzo, * Lorenzo Vicisano and other contributors. * 4. Neither the name of the Authors nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. * */ #ifndef RMDP__EVENT_H #define RMDP__EVENT_H #include #include #include #include #include #include #include #include #include "rmdp.h" #define MAX_CHAN 32 #define TIMIT struct timit TIMIT { TIMIT *next; int id; /* event id. */ int flags; /* flags */ int (*t_f)(void *); /* function to call */ void *t_p; /* function parameter list */ ui32 delta; /* delta t (usec) */ }; #define EVLIST struct evlist EVLIST { int io_chan[MAX_CHAN]; /* fd, -1 if empty */ int io_id[MAX_CHAN]; /* channel id. */ ui16 io_ch_num; /* number of active channels */ int (*io_f[MAX_CHAN])(void *); /* function to call */ void *io_p[MAX_CHAN]; /* function parameter list */ fd_set in_s; fd_set out_s; fd_set err_s; TIMIT *timevent; /* timevent list */ struct timeval lasttime; struct timeval default_timeout; ui32 late; /* we are scheduling the current event late usec late! */ int selected; /* the last selected fd */ }; /** ** event tags **/ #define EV_DEFTIMEOUT -4 /* got default timeout */ #define EV_GOTSIGNAL -3 /* got a signal in select() */ #define EV_TERMINATE -2 /* exit event loop with no timer pending and no channel to listen to */ #define EV_NO_PENDING -1 /* exit event loop with no timer pending but 1 or more channels to listen to (only if blocking == 0) */ #define EV_NULL 0 /* exit event loop with no event occurred but some timer pending */ EVLIST *createevlist(); int getevent(EVLIST *e, int blocking); void insertchan(EVLIST *e, int chan, int id, CALLBACK f, void *p); void deletechan(EVLIST *e, int chan); void settimer(EVLIST *e, int id, ui32 delta, CALLBACK f, void *p, int real); void set_default_timeout(EVLIST *e, struct timeval *tv); int showselected (EVLIST *e); #endif /* RMDP__EVENT_H */ sessions.h100644 423 423 6210 6542206610 10520 0ustar luigi/* * sessions.h -- header file for session handling routines (rmdpd) * * This file is part of * * rmdp -- Reliable Multicast data Distribution Protocol * * (C) 1996-1998 Luigi Rizzo and Lorenzo Vicisano * (luigi@iet.unipi.it, vicisano@cs.ucl.ac.uk) * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Luigi Rizzo, * Lorenzo Vicisano and other contributors. * 4. Neither the name of the Authors nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. * */ #ifndef RMDP__SESSIONS_H #define RMDP__SESSIONS_H /* #include */ #include "rmdp.h" #include #define SESSION struct session SESSION { SESSION *next; /* link field */ ui32 busy; /* there is a pending delete */ ui32 id; /* session id. no need for order */ ui32 c_id; /* client id */ ui32 r_id; /* request id */ ui32 ip; /* transmitting ip addr. (net order) */ ui16 port; /* transmitting port # (net order) */ int ch; /* transmitting channel */ int ttl; ui32 rate; /* tx rate, actually period in usec. */ ui32 curr_rate; /* current tx rate */ ui32 idx; /* next pkt. to transmit */ ui32 pkts_left; /* packets left to transmit */ void ***alldata; /* all plain data packets */ char *fname; ui32 flength; ui16 flags; /* various flags, net order */ int B; int k; int n; struct timeval flush_at, delete_at; }; extern SESSION *all_sessions; void schedule(char *path, ui32 c_id, ui32 r_id, ui32 *this_sched, ui32 *this_sess_id, ui32 *addr, ui16 *port); int se_start(SESSION *s, int set_pkts_left); int se_delete(SESSION *s); int se_send(SESSION *s); void prolong(n32 s_id); #endif /* RMDP__SESSIONS_H */ Makefile100644 423 423 2134 6542211413 10137 0ustar luigi# Makefile for rmdp -- 18june98 # Version without congestio control. # CFLAGS=-g -Wall -D$(OSTYPE) -DDO_TIMING #CFLAGS=-O2 -Wall -D$(OSTYPE) CC=gcc LIBS= $(OSLIBS) COMMON_C= net.c compat.c event.c \ fec.c rmdp.c SND_C= sender.c sessions.c # parse.c RCVR_C= receiver.c SENDER_C= $(COMMON_C) $(SND_C) RECEIVER_C= $(COMMON_C) $(RCVR_C) COMMON_H= rmdp.h net.h compat.h event.h SND_H= sessions.h SENDER_H= $(COMMON_H) $(SND_H) #OTHERS=Makefile re se parse.c parse.h nv_getmulti.c misc.c types.c OTHERS=Makefile makescript README rmdp.1 ALLSRCS=$(COMMON_C) $(SND_C) $(RCVR_C) $(COMMON_H) $(SND_H) $(OTHERS) SENDER_O=$(SENDER_C:.c=.o) RECEIVER_O=$(RECEIVER_C:.c=.o) #.SILENT: alone: sh makescript all: rmdpd rmdp clean: rm -f *.o rmdpd rmdp rmdpd: $(SENDER_O) $(CC) $(CFLAGS) -o rmdpd $(SENDER_O) $(LIBS) rmdp: $(RECEIVER_O) $(CC) $(CFLAGS) -o rmdp $(RECEIVER_O) $(LIBS) tgz: tar cvf - $(ALLSRCS) | gzip > rmdp.tgz sender.o: $(SENDER_H) sessions.o: $(SENDER_H) net.o: $(COMMON_H) rmdp.o: $(COMMON_H) compat.o: $(COMMON_H) event.o: $(COMMON_H) fec.o: $(COMMON_H) receiver.o: $(COMMON_H) # DO NOT DELETE makescript100711 423 423 6372 6541453563 10604 0ustar luigi#!/bin/sh # # Build script for rat. This script determines the type of machine it's # running on, and calls "make" with the appropriate macro definitions # to set up the machine specific includes etc. # # NOTE: If the environment variable MAKE is set, it's value will be # used instead of the default "make". Use this to give a path # to gnu make, for example. # # $Revision$ # $Date$ # # Copyright (c) 1996 University College London # 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. # 3. All advertising materials mentioning features or use of this software # must display the following acknowledgement: # This product includes software developed by the Computer Science # Department at University College London # 4. Neither the name of the University nor of the Department may be used # to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESSED 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 AUTHORS 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. # # Find out the type of system we're running on. The nasty stuff is needed for $OSMVER # since some systems (HP-UX) give the OS version in a weird format. At the end of this # we should have the following variables set: # # OSTYPE : Base operating system type # OSMVER : The major version number of the system # OSVERS : The full version number of the system # echo "Determining system type..." OSTYPE=`uname -s` OSVERS=`uname -r` case $OSTYPE in Linux ) OSMVER=`echo $OSVERS | awk -F. '{printf("%d_%d", $1, $2)}'` ;; SunOS | IRIX ) OSMVER=`echo $OSVERS | awk -F. '{print $1}'` OSLIBS="-lsocket -lnsl" ;; HP-UX ) OSTYPE=HPUX OSMVER=`echo $OSVERS | awk -F. '{print $2}'` ;; FreeBSD ) OSMVER=`echo $OSVERS | awk -F. '{print $1}'` OSLIBS="" ;; * ) echo "$OSTYPE $OSVERS is not supported!" exit ;; esac echo "OSTYPE=$OSTYPE" echo "OSVERS=$OSVERS" echo "OSMVER=$OSMVER" export OSLIBS cmd="${MAKE:=make} all OSTYPE=$OSTYPE OSMVER=$OSMVER OSVERS=$OSVERS" exec $cmd README100644 423 423 3057 6542211600 7362 0ustar luigi RMDP, a Reliable Multicast data Distribution Protocol. Luigi Rizzo (l.rizzo@iet.unipi.it) Lorenzo Vicisano (l.vicisano@cs.ucl.ac.uk) Version 0.50 -- 18-jun-1997 WARNING WARNING WARNING WARNING WARNING WARNING This is a very experimental version for evaluation only. It is extremely buggy and insecure -- use it at your own risk. RMDP is a multicast ftp-like application, especially suited to those cases where the same file is requested by many users at the same time. RMDP sends data using multicast (or unicast) UDP, and uses a redundant encoding of data so that transfers can be carried on reliably even in presence of huge losses (30..50% and more), and little or no feedback is required from the receivers. An rmdp server is simply started with rmdpd It listens to a multicast port (default 224.5.5.5/5656) and a unicast port (default 5656). A client can request files as rmdp [-u] [-o outfilename] rmdp://host/file the request is sent to a multicast port, any available server will respond using a unicast port, then data are transmitted over a multicast channel. If the -u flag is used, then all transaction occur over a unicast channel. Some of the advantages of rmdp -- namely, sharing bandwidth -- are lost, but others remain -- most noticeably, the graceful degradation of performance in presence of large losses. More options are present, and even more will be added in the future. See the manpage for a full list. RELEASE INFORMATION: 18-feb-97 release of v.0.00 18-jun98 upgraded the FEC code, improved performance, fixed bugs. rmdp.1100644 423 423 4617 6300171225 7531 0ustar luigi.\" Man page for rmdp .\" .\" Copyright (c) 1997 Luigi Rizzo .\" .Dd February 12, 1997 .Dt RMDP 1 .Os FreeBSD 2.2 .Sh NAME .Nm rmdp .Nd Reliable Multicast data Distribution Protocol .Sh SYNOPSIS .Nm rmdp .Op Fl u .Op Fl c Ar control_address/port .Op Fl t Ar ttl .Op Fl r Ar rate .Op Fl o Ar destination source_url .Nm rmdpd .Op Fl c Ar control_address/port .Op Fl d Ar control_address/port .Op Fl t Ar ttl .Op Fl k Ar data_blocks .Op Fl n Ar total_blocks .Ar source_directory .Sh DESCRIPTION .Nm rmdp and .Nm rmdpd are the client and server, respectively, for the rmdp protocol. The client requests and receives the desired file using a reliable multicast protocol that allows multiple receivers to access the same data at once. .Pp The following options are available for .Nm rmdp: .Bl -tag -width flag .It Fl u Use unicast UDP for sending requests and receiving data. .It Fl c Specify the address (usually a multicast one) and port to be used for sending requests to the server. The default address/port are .Ar 224.5.5.5/5656 In case .Fl u is used, the only the port is significant. .It Fl t Ar ttl use the specified TTL for multicast transmissions. Default is 16 .It Fl r Ar rate Specifies the desired rate in bit/s (or Kbit/s, Mbit/s if the number is followed by K or M respectively). Default is 64K .It Fl o Ar destination uses .Ar destination as output file name. Default name is .Ar "fileout" .It Ar source_url specifies the URL for the requested document. Currently the only supported protocol is .Ar rmdp hence the URL looks like .Ar rmdp://hostname/file .El .Pp The following options are available for .Nm rmdpd: .Bl -tag -width flag .It Fl t Ar ttl use the specified TTL for multicast transmissions. Default is 16 .It Fl r Ar rate Specifies the maximum data rate in bit/s (or Kbit/s, Mbit/s if the number is followed by K or M respectively). Default is 1M .It Fl c Specify the address (usually a multicast one) and port to be used for receiving requests at the server. The default address/port are .Ar 224.5.5.5/5656 .It Fl d Specify the address (usually a multicast one) and port to be used for sending data to the clients. The default address/port are .Ar 224.5.5.6/5656 .Pp .Sh AUTHORS .Nm rmdp has been designed and implemented by Luigi Rizzo (luigi@iet.unipi.it) and Lorenzo Vicisano (vicisano@cs.ucl.ac.uk) .Sh BUGS Far too many at this time. This is an experimental version and performance is not taken into much consideration.