17dd7cddfSDavid du Colombier /*
2d5099b52SDavid du Colombier * Reliable User Datagram Protocol, currently only for IPv4.
37dd7cddfSDavid du Colombier * This protocol is compatible with UDP's packet format.
47dd7cddfSDavid du Colombier * It could be done over UDP if need be.
57dd7cddfSDavid du Colombier */
67dd7cddfSDavid du Colombier #include "u.h"
77dd7cddfSDavid du Colombier #include "../port/lib.h"
87dd7cddfSDavid du Colombier #include "mem.h"
97dd7cddfSDavid du Colombier #include "dat.h"
107dd7cddfSDavid du Colombier #include "fns.h"
117dd7cddfSDavid du Colombier #include "../port/error.h"
127dd7cddfSDavid du Colombier
137dd7cddfSDavid du Colombier #include "ip.h"
147dd7cddfSDavid du Colombier
157dd7cddfSDavid du Colombier #define DEBUG 0
167dd7cddfSDavid du Colombier #define DPRINT if(DEBUG)print
177dd7cddfSDavid du Colombier
187dd7cddfSDavid du Colombier #define SEQDIFF(a,b) ( (a)>=(b)?\
197dd7cddfSDavid du Colombier (a)-(b):\
207dd7cddfSDavid du Colombier 0xffffffffUL-((b)-(a)) )
217dd7cddfSDavid du Colombier #define INSEQ(a,start,end) ( (start)<=(end)?\
227dd7cddfSDavid du Colombier ((a)>(start)&&(a)<=(end)):\
237dd7cddfSDavid du Colombier ((a)>(start)||(a)<=(end)) )
247dd7cddfSDavid du Colombier #define UNACKED(r) SEQDIFF(r->sndseq, r->ackrcvd)
257dd7cddfSDavid du Colombier #define NEXTSEQ(a) ( (a)+1 == 0 ? 1 : (a)+1 )
267dd7cddfSDavid du Colombier
277dd7cddfSDavid du Colombier enum
287dd7cddfSDavid du Colombier {
297dd7cddfSDavid du Colombier UDP_PHDRSIZE = 12, /* pseudo header */
3084363d68SDavid du Colombier // UDP_HDRSIZE = 20, /* pseudo header + udp header */
317dd7cddfSDavid du Colombier UDP_RHDRSIZE = 36, /* pseudo header + udp header + rudp header */
327dd7cddfSDavid du Colombier UDP_IPHDR = 8, /* ip header */
337dd7cddfSDavid du Colombier IP_UDPPROTO = 254,
3484363d68SDavid du Colombier UDP_USEAD7 = 52, /* size of new ipv6 headers struct */
357dd7cddfSDavid du Colombier
367dd7cddfSDavid du Colombier Rudprxms = 200,
377dd7cddfSDavid du Colombier Rudptickms = 50,
387dd7cddfSDavid du Colombier Rudpmaxxmit = 10,
397dd7cddfSDavid du Colombier Maxunacked = 100,
407dd7cddfSDavid du Colombier };
417dd7cddfSDavid du Colombier
427dd7cddfSDavid du Colombier #define Hangupgen 0xffffffff /* used only in hangup messages */
437dd7cddfSDavid du Colombier
447dd7cddfSDavid du Colombier typedef struct Udphdr Udphdr;
457dd7cddfSDavid du Colombier struct Udphdr
467dd7cddfSDavid du Colombier {
477dd7cddfSDavid du Colombier /* ip header */
487dd7cddfSDavid du Colombier uchar vihl; /* Version and header length */
497dd7cddfSDavid du Colombier uchar tos; /* Type of service */
507dd7cddfSDavid du Colombier uchar length[2]; /* packet length */
517dd7cddfSDavid du Colombier uchar id[2]; /* Identification */
527dd7cddfSDavid du Colombier uchar frag[2]; /* Fragment information */
537dd7cddfSDavid du Colombier
547dd7cddfSDavid du Colombier /* pseudo header starts here */
557dd7cddfSDavid du Colombier uchar Unused;
567dd7cddfSDavid du Colombier uchar udpproto; /* Protocol */
577dd7cddfSDavid du Colombier uchar udpplen[2]; /* Header plus data length */
587dd7cddfSDavid du Colombier uchar udpsrc[4]; /* Ip source */
597dd7cddfSDavid du Colombier uchar udpdst[4]; /* Ip destination */
607dd7cddfSDavid du Colombier
617dd7cddfSDavid du Colombier /* udp header */
627dd7cddfSDavid du Colombier uchar udpsport[2]; /* Source port */
637dd7cddfSDavid du Colombier uchar udpdport[2]; /* Destination port */
647dd7cddfSDavid du Colombier uchar udplen[2]; /* data length */
657dd7cddfSDavid du Colombier uchar udpcksum[2]; /* Checksum */
667dd7cddfSDavid du Colombier };
677dd7cddfSDavid du Colombier
687dd7cddfSDavid du Colombier typedef struct Rudphdr Rudphdr;
697dd7cddfSDavid du Colombier struct Rudphdr
707dd7cddfSDavid du Colombier {
717dd7cddfSDavid du Colombier /* ip header */
727dd7cddfSDavid du Colombier uchar vihl; /* Version and header length */
737dd7cddfSDavid du Colombier uchar tos; /* Type of service */
747dd7cddfSDavid du Colombier uchar length[2]; /* packet length */
757dd7cddfSDavid du Colombier uchar id[2]; /* Identification */
767dd7cddfSDavid du Colombier uchar frag[2]; /* Fragment information */
777dd7cddfSDavid du Colombier
787dd7cddfSDavid du Colombier /* pseudo header starts here */
797dd7cddfSDavid du Colombier uchar Unused;
807dd7cddfSDavid du Colombier uchar udpproto; /* Protocol */
817dd7cddfSDavid du Colombier uchar udpplen[2]; /* Header plus data length */
827dd7cddfSDavid du Colombier uchar udpsrc[4]; /* Ip source */
837dd7cddfSDavid du Colombier uchar udpdst[4]; /* Ip destination */
847dd7cddfSDavid du Colombier
857dd7cddfSDavid du Colombier /* udp header */
867dd7cddfSDavid du Colombier uchar udpsport[2]; /* Source port */
877dd7cddfSDavid du Colombier uchar udpdport[2]; /* Destination port */
887dd7cddfSDavid du Colombier uchar udplen[2]; /* data length (includes rudp header) */
897dd7cddfSDavid du Colombier uchar udpcksum[2]; /* Checksum */
907dd7cddfSDavid du Colombier
917dd7cddfSDavid du Colombier /* rudp header */
927dd7cddfSDavid du Colombier uchar relseq[4]; /* id of this packet (or 0) */
937dd7cddfSDavid du Colombier uchar relsgen[4]; /* generation/time stamp */
947dd7cddfSDavid du Colombier uchar relack[4]; /* packet being acked (or 0) */
957dd7cddfSDavid du Colombier uchar relagen[4]; /* generation/time stamp */
967dd7cddfSDavid du Colombier };
977dd7cddfSDavid du Colombier
987dd7cddfSDavid du Colombier
997dd7cddfSDavid du Colombier /*
1007dd7cddfSDavid du Colombier * one state structure per destination
1017dd7cddfSDavid du Colombier */
1027dd7cddfSDavid du Colombier typedef struct Reliable Reliable;
1037dd7cddfSDavid du Colombier struct Reliable
1047dd7cddfSDavid du Colombier {
1057dd7cddfSDavid du Colombier Ref;
1067dd7cddfSDavid du Colombier
1077dd7cddfSDavid du Colombier Reliable *next;
1087dd7cddfSDavid du Colombier
1097dd7cddfSDavid du Colombier uchar addr[IPaddrlen]; /* always V6 when put here */
1107dd7cddfSDavid du Colombier ushort port;
1117dd7cddfSDavid du Colombier
1127dd7cddfSDavid du Colombier Block *unacked; /* unacked msg list */
1137dd7cddfSDavid du Colombier Block *unackedtail; /* and its tail */
1147dd7cddfSDavid du Colombier
1157dd7cddfSDavid du Colombier int timeout; /* time since first unacked msg sent */
1167dd7cddfSDavid du Colombier int xmits; /* number of times first unacked msg sent */
1177dd7cddfSDavid du Colombier
1187dd7cddfSDavid du Colombier ulong sndseq; /* next packet to be sent */
1197dd7cddfSDavid du Colombier ulong sndgen; /* and its generation */
1207dd7cddfSDavid du Colombier
1217dd7cddfSDavid du Colombier ulong rcvseq; /* last packet received */
1227dd7cddfSDavid du Colombier ulong rcvgen; /* and its generation */
1237dd7cddfSDavid du Colombier
1247dd7cddfSDavid du Colombier ulong acksent; /* last ack sent */
1257dd7cddfSDavid du Colombier ulong ackrcvd; /* last msg for which ack was rcvd */
1267dd7cddfSDavid du Colombier
1277dd7cddfSDavid du Colombier /* flow control */
1287dd7cddfSDavid du Colombier QLock lock;
1297dd7cddfSDavid du Colombier Rendez vous;
1307dd7cddfSDavid du Colombier int blocked;
1317dd7cddfSDavid du Colombier };
1327dd7cddfSDavid du Colombier
1337dd7cddfSDavid du Colombier
1347dd7cddfSDavid du Colombier
1357dd7cddfSDavid du Colombier /* MIB II counters */
1367dd7cddfSDavid du Colombier typedef struct Rudpstats Rudpstats;
1377dd7cddfSDavid du Colombier struct Rudpstats
1387dd7cddfSDavid du Colombier {
1397dd7cddfSDavid du Colombier ulong rudpInDatagrams;
1407dd7cddfSDavid du Colombier ulong rudpNoPorts;
1417dd7cddfSDavid du Colombier ulong rudpInErrors;
1427dd7cddfSDavid du Colombier ulong rudpOutDatagrams;
1437dd7cddfSDavid du Colombier };
1447dd7cddfSDavid du Colombier
1457dd7cddfSDavid du Colombier typedef struct Rudppriv Rudppriv;
1467dd7cddfSDavid du Colombier struct Rudppriv
1477dd7cddfSDavid du Colombier {
14880ee5cbfSDavid du Colombier Ipht ht;
1497dd7cddfSDavid du Colombier
1507dd7cddfSDavid du Colombier /* MIB counters */
1517dd7cddfSDavid du Colombier Rudpstats ustats;
1527dd7cddfSDavid du Colombier
1537dd7cddfSDavid du Colombier /* non-MIB stats */
1547dd7cddfSDavid du Colombier ulong csumerr; /* checksum errors */
1557dd7cddfSDavid du Colombier ulong lenerr; /* short packet */
1567dd7cddfSDavid du Colombier ulong rxmits; /* # of retransmissions */
1577dd7cddfSDavid du Colombier ulong orders; /* # of out of order pkts */
1587dd7cddfSDavid du Colombier
1597dd7cddfSDavid du Colombier /* keeping track of the ack kproc */
1607dd7cddfSDavid du Colombier int ackprocstarted;
1617dd7cddfSDavid du Colombier QLock apl;
1627dd7cddfSDavid du Colombier };
1637dd7cddfSDavid du Colombier
1647dd7cddfSDavid du Colombier
1657dd7cddfSDavid du Colombier static ulong generation = 0;
1667dd7cddfSDavid du Colombier static Rendez rend;
1677dd7cddfSDavid du Colombier
1687dd7cddfSDavid du Colombier /*
1697dd7cddfSDavid du Colombier * protocol specific part of Conv
1707dd7cddfSDavid du Colombier */
1717dd7cddfSDavid du Colombier typedef struct Rudpcb Rudpcb;
1727dd7cddfSDavid du Colombier struct Rudpcb
1737dd7cddfSDavid du Colombier {
1747dd7cddfSDavid du Colombier QLock;
1757dd7cddfSDavid du Colombier uchar headers;
1767dd7cddfSDavid du Colombier uchar randdrop;
1777dd7cddfSDavid du Colombier Reliable *r;
1787dd7cddfSDavid du Colombier };
1797dd7cddfSDavid du Colombier
1807dd7cddfSDavid du Colombier /*
1817dd7cddfSDavid du Colombier * local functions
1827dd7cddfSDavid du Colombier */
1837dd7cddfSDavid du Colombier void relsendack(Conv*, Reliable*, int);
1847dd7cddfSDavid du Colombier int reliput(Conv*, Block*, uchar*, ushort);
1857dd7cddfSDavid du Colombier Reliable *relstate(Rudpcb*, uchar*, ushort, char*);
1867dd7cddfSDavid du Colombier void relput(Reliable*);
1877dd7cddfSDavid du Colombier void relforget(Conv *, uchar*, int, int);
1887dd7cddfSDavid du Colombier void relackproc(void *);
1897dd7cddfSDavid du Colombier void relackq(Reliable *, Block*);
1907dd7cddfSDavid du Colombier void relhangup(Conv *, Reliable*);
1917dd7cddfSDavid du Colombier void relrexmit(Conv *, Reliable*);
1927dd7cddfSDavid du Colombier void relput(Reliable*);
1933ff48bf5SDavid du Colombier void rudpkick(void *x);
1947dd7cddfSDavid du Colombier
1957dd7cddfSDavid du Colombier static void
rudpstartackproc(Proto * rudp)1967dd7cddfSDavid du Colombier rudpstartackproc(Proto *rudp)
1977dd7cddfSDavid du Colombier {
1987dd7cddfSDavid du Colombier Rudppriv *rpriv;
1999a747e4fSDavid du Colombier char kpname[KNAMELEN];
2007dd7cddfSDavid du Colombier
2017dd7cddfSDavid du Colombier rpriv = rudp->priv;
2027dd7cddfSDavid du Colombier if(rpriv->ackprocstarted == 0){
2037dd7cddfSDavid du Colombier qlock(&rpriv->apl);
2047dd7cddfSDavid du Colombier if(rpriv->ackprocstarted == 0){
205*4e3613abSDavid du Colombier snprint(kpname, sizeof kpname, "#I%drudpack",
206*4e3613abSDavid du Colombier rudp->f->dev);
2077dd7cddfSDavid du Colombier kproc(kpname, relackproc, rudp);
2087dd7cddfSDavid du Colombier rpriv->ackprocstarted = 1;
2097dd7cddfSDavid du Colombier }
2107dd7cddfSDavid du Colombier qunlock(&rpriv->apl);
2117dd7cddfSDavid du Colombier }
2127dd7cddfSDavid du Colombier }
2137dd7cddfSDavid du Colombier
2147dd7cddfSDavid du Colombier static char*
rudpconnect(Conv * c,char ** argv,int argc)2157dd7cddfSDavid du Colombier rudpconnect(Conv *c, char **argv, int argc)
2167dd7cddfSDavid du Colombier {
2177dd7cddfSDavid du Colombier char *e;
21880ee5cbfSDavid du Colombier Rudppriv *upriv;
2197dd7cddfSDavid du Colombier
22080ee5cbfSDavid du Colombier upriv = c->p->priv;
2217dd7cddfSDavid du Colombier rudpstartackproc(c->p);
2227dd7cddfSDavid du Colombier e = Fsstdconnect(c, argv, argc);
2237dd7cddfSDavid du Colombier Fsconnected(c, e);
22480ee5cbfSDavid du Colombier iphtadd(&upriv->ht, c);
2257dd7cddfSDavid du Colombier
2267dd7cddfSDavid du Colombier return e;
2277dd7cddfSDavid du Colombier }
2287dd7cddfSDavid du Colombier
2297dd7cddfSDavid du Colombier
2307dd7cddfSDavid du Colombier static int
rudpstate(Conv * c,char * state,int n)2317dd7cddfSDavid du Colombier rudpstate(Conv *c, char *state, int n)
2327dd7cddfSDavid du Colombier {
2337dd7cddfSDavid du Colombier Rudpcb *ucb;
2347dd7cddfSDavid du Colombier Reliable *r;
2357dd7cddfSDavid du Colombier int m;
2367dd7cddfSDavid du Colombier
2377dd7cddfSDavid du Colombier m = snprint(state, n, "%s", c->inuse?"Open":"Closed");
2387dd7cddfSDavid du Colombier ucb = (Rudpcb*)c->ptcl;
2397dd7cddfSDavid du Colombier qlock(ucb);
2407dd7cddfSDavid du Colombier for(r = ucb->r; r; r = r->next)
2417dd7cddfSDavid du Colombier m += snprint(state+m, n-m, " %I/%ld", r->addr, UNACKED(r));
24203a1fc68SDavid du Colombier m += snprint(state+m, n-m, "\n");
2437dd7cddfSDavid du Colombier qunlock(ucb);
2447dd7cddfSDavid du Colombier return m;
2457dd7cddfSDavid du Colombier }
2467dd7cddfSDavid du Colombier
2477dd7cddfSDavid du Colombier static char*
rudpannounce(Conv * c,char ** argv,int argc)2487dd7cddfSDavid du Colombier rudpannounce(Conv *c, char** argv, int argc)
2497dd7cddfSDavid du Colombier {
2507dd7cddfSDavid du Colombier char *e;
25180ee5cbfSDavid du Colombier Rudppriv *upriv;
2527dd7cddfSDavid du Colombier
25380ee5cbfSDavid du Colombier upriv = c->p->priv;
2547dd7cddfSDavid du Colombier rudpstartackproc(c->p);
2557dd7cddfSDavid du Colombier e = Fsstdannounce(c, argv, argc);
2567dd7cddfSDavid du Colombier if(e != nil)
2577dd7cddfSDavid du Colombier return e;
2587dd7cddfSDavid du Colombier Fsconnected(c, nil);
25980ee5cbfSDavid du Colombier iphtadd(&upriv->ht, c);
2607dd7cddfSDavid du Colombier
2617dd7cddfSDavid du Colombier return nil;
2627dd7cddfSDavid du Colombier }
2637dd7cddfSDavid du Colombier
2647dd7cddfSDavid du Colombier static void
rudpcreate(Conv * c)2657dd7cddfSDavid du Colombier rudpcreate(Conv *c)
2667dd7cddfSDavid du Colombier {
2673ff48bf5SDavid du Colombier c->rq = qopen(64*1024, Qmsg, 0, 0);
2683ff48bf5SDavid du Colombier c->wq = qopen(64*1024, Qkick, rudpkick, c);
2697dd7cddfSDavid du Colombier }
2707dd7cddfSDavid du Colombier
2717dd7cddfSDavid du Colombier static void
rudpclose(Conv * c)2727dd7cddfSDavid du Colombier rudpclose(Conv *c)
2737dd7cddfSDavid du Colombier {
2747dd7cddfSDavid du Colombier Rudpcb *ucb;
2757dd7cddfSDavid du Colombier Reliable *r, *nr;
27680ee5cbfSDavid du Colombier Rudppriv *upriv;
27780ee5cbfSDavid du Colombier
27880ee5cbfSDavid du Colombier upriv = c->p->priv;
27980ee5cbfSDavid du Colombier iphtrem(&upriv->ht, c);
2807dd7cddfSDavid du Colombier
2817dd7cddfSDavid du Colombier /* force out any delayed acks */
2827dd7cddfSDavid du Colombier ucb = (Rudpcb*)c->ptcl;
2837dd7cddfSDavid du Colombier qlock(ucb);
2847dd7cddfSDavid du Colombier for(r = ucb->r; r; r = r->next){
2857dd7cddfSDavid du Colombier if(r->acksent != r->rcvseq)
2867dd7cddfSDavid du Colombier relsendack(c, r, 0);
2877dd7cddfSDavid du Colombier }
2887dd7cddfSDavid du Colombier qunlock(ucb);
2897dd7cddfSDavid du Colombier
2907dd7cddfSDavid du Colombier qclose(c->rq);
2917dd7cddfSDavid du Colombier qclose(c->wq);
2927dd7cddfSDavid du Colombier qclose(c->eq);
2937dd7cddfSDavid du Colombier ipmove(c->laddr, IPnoaddr);
2947dd7cddfSDavid du Colombier ipmove(c->raddr, IPnoaddr);
2957dd7cddfSDavid du Colombier c->lport = 0;
2967dd7cddfSDavid du Colombier c->rport = 0;
2977dd7cddfSDavid du Colombier
2987dd7cddfSDavid du Colombier ucb->headers = 0;
2997dd7cddfSDavid du Colombier ucb->randdrop = 0;
3007dd7cddfSDavid du Colombier qlock(ucb);
3017dd7cddfSDavid du Colombier for(r = ucb->r; r; r = nr){
3027dd7cddfSDavid du Colombier if(r->acksent != r->rcvseq)
3037dd7cddfSDavid du Colombier relsendack(c, r, 0);
3047dd7cddfSDavid du Colombier nr = r->next;
3057dd7cddfSDavid du Colombier relhangup(c, r);
3067dd7cddfSDavid du Colombier relput(r);
3077dd7cddfSDavid du Colombier }
3087dd7cddfSDavid du Colombier ucb->r = 0;
3097dd7cddfSDavid du Colombier
3107dd7cddfSDavid du Colombier qunlock(ucb);
3117dd7cddfSDavid du Colombier }
3127dd7cddfSDavid du Colombier
3137dd7cddfSDavid du Colombier /*
3147dd7cddfSDavid du Colombier * randomly don't send packets
3157dd7cddfSDavid du Colombier */
3167dd7cddfSDavid du Colombier static void
doipoput(Conv * c,Fs * f,Block * bp,int x,int ttl,int tos)3177dd7cddfSDavid du Colombier doipoput(Conv *c, Fs *f, Block *bp, int x, int ttl, int tos)
3187dd7cddfSDavid du Colombier {
3197dd7cddfSDavid du Colombier Rudpcb *ucb;
3207dd7cddfSDavid du Colombier
3217dd7cddfSDavid du Colombier ucb = (Rudpcb*)c->ptcl;
3227dd7cddfSDavid du Colombier if(ucb->randdrop && nrand(100) < ucb->randdrop)
3237dd7cddfSDavid du Colombier freeblist(bp);
3247dd7cddfSDavid du Colombier else
325a6a9e072SDavid du Colombier ipoput4(f, bp, x, ttl, tos, nil);
3267dd7cddfSDavid du Colombier }
3277dd7cddfSDavid du Colombier
3287dd7cddfSDavid du Colombier int
flow(void * v)3297dd7cddfSDavid du Colombier flow(void *v)
3307dd7cddfSDavid du Colombier {
3317dd7cddfSDavid du Colombier Reliable *r = v;
3327dd7cddfSDavid du Colombier
3337dd7cddfSDavid du Colombier return UNACKED(r) <= Maxunacked;
3347dd7cddfSDavid du Colombier }
3357dd7cddfSDavid du Colombier
3367dd7cddfSDavid du Colombier void
rudpkick(void * x)3373ff48bf5SDavid du Colombier rudpkick(void *x)
3387dd7cddfSDavid du Colombier {
3393ff48bf5SDavid du Colombier Conv *c = x;
3407dd7cddfSDavid du Colombier Udphdr *uh;
3417dd7cddfSDavid du Colombier ushort rport;
3427dd7cddfSDavid du Colombier uchar laddr[IPaddrlen], raddr[IPaddrlen];
3437dd7cddfSDavid du Colombier Block *bp;
3447dd7cddfSDavid du Colombier Rudpcb *ucb;
3457dd7cddfSDavid du Colombier Rudphdr *rh;
3467dd7cddfSDavid du Colombier Reliable *r;
3477dd7cddfSDavid du Colombier int dlen, ptcllen;
3487dd7cddfSDavid du Colombier Rudppriv *upriv;
3497dd7cddfSDavid du Colombier Fs *f;
3507dd7cddfSDavid du Colombier
3517dd7cddfSDavid du Colombier upriv = c->p->priv;
3527dd7cddfSDavid du Colombier f = c->p->f;
3537dd7cddfSDavid du Colombier
3547dd7cddfSDavid du Colombier netlog(c->p->f, Logrudp, "rudp: kick\n");
3557dd7cddfSDavid du Colombier bp = qget(c->wq);
3567dd7cddfSDavid du Colombier if(bp == nil)
3577dd7cddfSDavid du Colombier return;
3587dd7cddfSDavid du Colombier
3597dd7cddfSDavid du Colombier ucb = (Rudpcb*)c->ptcl;
3607dd7cddfSDavid du Colombier switch(ucb->headers) {
361dc5a79c1SDavid du Colombier case 7:
362dc5a79c1SDavid du Colombier /* get user specified addresses */
363dc5a79c1SDavid du Colombier bp = pullupblock(bp, UDP_USEAD7);
364dc5a79c1SDavid du Colombier if(bp == nil)
365dc5a79c1SDavid du Colombier return;
366dc5a79c1SDavid du Colombier ipmove(raddr, bp->rp);
367dc5a79c1SDavid du Colombier bp->rp += IPaddrlen;
368dc5a79c1SDavid du Colombier ipmove(laddr, bp->rp);
369dc5a79c1SDavid du Colombier bp->rp += IPaddrlen;
370dc5a79c1SDavid du Colombier /* pick interface closest to dest */
371dc5a79c1SDavid du Colombier if(ipforme(f, laddr) != Runi)
372dc5a79c1SDavid du Colombier findlocalip(f, laddr, raddr);
373dc5a79c1SDavid du Colombier bp->rp += IPaddrlen; /* Ignore ifc address */
374dc5a79c1SDavid du Colombier rport = nhgets(bp->rp);
375dc5a79c1SDavid du Colombier bp->rp += 2+2; /* Ignore local port */
376dc5a79c1SDavid du Colombier break;
3777dd7cddfSDavid du Colombier default:
3787dd7cddfSDavid du Colombier ipmove(raddr, c->raddr);
3797dd7cddfSDavid du Colombier ipmove(laddr, c->laddr);
3807dd7cddfSDavid du Colombier rport = c->rport;
3817dd7cddfSDavid du Colombier break;
3827dd7cddfSDavid du Colombier }
3837dd7cddfSDavid du Colombier
3847dd7cddfSDavid du Colombier dlen = blocklen(bp);
3857dd7cddfSDavid du Colombier
3867dd7cddfSDavid du Colombier /* Make space to fit rudp & ip header */
3877dd7cddfSDavid du Colombier bp = padblock(bp, UDP_IPHDR+UDP_RHDRSIZE);
3887dd7cddfSDavid du Colombier if(bp == nil)
3897dd7cddfSDavid du Colombier return;
3907dd7cddfSDavid du Colombier
3917dd7cddfSDavid du Colombier uh = (Udphdr *)(bp->rp);
3923ff48bf5SDavid du Colombier uh->vihl = IP_VER4;
3937dd7cddfSDavid du Colombier
3947dd7cddfSDavid du Colombier rh = (Rudphdr*)uh;
3957dd7cddfSDavid du Colombier
3967dd7cddfSDavid du Colombier ptcllen = dlen + (UDP_RHDRSIZE-UDP_PHDRSIZE);
3977dd7cddfSDavid du Colombier uh->Unused = 0;
3987dd7cddfSDavid du Colombier uh->udpproto = IP_UDPPROTO;
3997dd7cddfSDavid du Colombier uh->frag[0] = 0;
4007dd7cddfSDavid du Colombier uh->frag[1] = 0;
4017dd7cddfSDavid du Colombier hnputs(uh->udpplen, ptcllen);
4027dd7cddfSDavid du Colombier switch(ucb->headers){
403dc5a79c1SDavid du Colombier case 7:
4047dd7cddfSDavid du Colombier v6tov4(uh->udpdst, raddr);
4057dd7cddfSDavid du Colombier hnputs(uh->udpdport, rport);
4067dd7cddfSDavid du Colombier v6tov4(uh->udpsrc, laddr);
4077dd7cddfSDavid du Colombier break;
4087dd7cddfSDavid du Colombier default:
4097dd7cddfSDavid du Colombier v6tov4(uh->udpdst, c->raddr);
4107dd7cddfSDavid du Colombier hnputs(uh->udpdport, c->rport);
4117dd7cddfSDavid du Colombier if(ipcmp(c->laddr, IPnoaddr) == 0)
4127dd7cddfSDavid du Colombier findlocalip(f, c->laddr, c->raddr);
4137dd7cddfSDavid du Colombier v6tov4(uh->udpsrc, c->laddr);
4147dd7cddfSDavid du Colombier break;
4157dd7cddfSDavid du Colombier }
4167dd7cddfSDavid du Colombier hnputs(uh->udpsport, c->lport);
4177dd7cddfSDavid du Colombier hnputs(uh->udplen, ptcllen);
4187dd7cddfSDavid du Colombier uh->udpcksum[0] = 0;
4197dd7cddfSDavid du Colombier uh->udpcksum[1] = 0;
4207dd7cddfSDavid du Colombier
4217dd7cddfSDavid du Colombier qlock(ucb);
4227dd7cddfSDavid du Colombier r = relstate(ucb, raddr, rport, "kick");
4237dd7cddfSDavid du Colombier r->sndseq = NEXTSEQ(r->sndseq);
4247dd7cddfSDavid du Colombier hnputl(rh->relseq, r->sndseq);
4257dd7cddfSDavid du Colombier hnputl(rh->relsgen, r->sndgen);
4267dd7cddfSDavid du Colombier
4277dd7cddfSDavid du Colombier hnputl(rh->relack, r->rcvseq); /* ACK last rcvd packet */
4287dd7cddfSDavid du Colombier hnputl(rh->relagen, r->rcvgen);
4297dd7cddfSDavid du Colombier
4307dd7cddfSDavid du Colombier if(r->rcvseq != r->acksent)
4317dd7cddfSDavid du Colombier r->acksent = r->rcvseq;
4327dd7cddfSDavid du Colombier
4337dd7cddfSDavid du Colombier hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, dlen+UDP_RHDRSIZE));
4347dd7cddfSDavid du Colombier
4357dd7cddfSDavid du Colombier relackq(r, bp);
4367dd7cddfSDavid du Colombier qunlock(ucb);
4377dd7cddfSDavid du Colombier
4387dd7cddfSDavid du Colombier upriv->ustats.rudpOutDatagrams++;
4397dd7cddfSDavid du Colombier
4407dd7cddfSDavid du Colombier DPRINT("sent: %lud/%lud, %lud/%lud\n",
4417dd7cddfSDavid du Colombier r->sndseq, r->sndgen, r->rcvseq, r->rcvgen);
4427dd7cddfSDavid du Colombier
4437dd7cddfSDavid du Colombier doipoput(c, f, bp, 0, c->ttl, c->tos);
4447dd7cddfSDavid du Colombier
4457dd7cddfSDavid du Colombier if(waserror()) {
4467dd7cddfSDavid du Colombier relput(r);
4477dd7cddfSDavid du Colombier qunlock(&r->lock);
4487dd7cddfSDavid du Colombier nexterror();
4497dd7cddfSDavid du Colombier }
4507dd7cddfSDavid du Colombier
4517dd7cddfSDavid du Colombier /* flow control of sorts */
4527dd7cddfSDavid du Colombier qlock(&r->lock);
4537dd7cddfSDavid du Colombier if(UNACKED(r) > Maxunacked){
4547dd7cddfSDavid du Colombier r->blocked = 1;
4557dd7cddfSDavid du Colombier sleep(&r->vous, flow, r);
4567dd7cddfSDavid du Colombier r->blocked = 0;
4577dd7cddfSDavid du Colombier }
4587dd7cddfSDavid du Colombier
4597dd7cddfSDavid du Colombier qunlock(&r->lock);
4607dd7cddfSDavid du Colombier relput(r);
4617dd7cddfSDavid du Colombier poperror();
4627dd7cddfSDavid du Colombier }
4637dd7cddfSDavid du Colombier
4647dd7cddfSDavid du Colombier void
rudpiput(Proto * rudp,Ipifc * ifc,Block * bp)4659a747e4fSDavid du Colombier rudpiput(Proto *rudp, Ipifc *ifc, Block *bp)
4667dd7cddfSDavid du Colombier {
4677dd7cddfSDavid du Colombier int len, olen, ottl;
4687dd7cddfSDavid du Colombier Udphdr *uh;
46980ee5cbfSDavid du Colombier Conv *c;
4707dd7cddfSDavid du Colombier Rudpcb *ucb;
4717dd7cddfSDavid du Colombier uchar raddr[IPaddrlen], laddr[IPaddrlen];
4727dd7cddfSDavid du Colombier ushort rport, lport;
4737dd7cddfSDavid du Colombier Rudppriv *upriv;
4747dd7cddfSDavid du Colombier Fs *f;
475dc5a79c1SDavid du Colombier uchar *p;
4767dd7cddfSDavid du Colombier
4777dd7cddfSDavid du Colombier upriv = rudp->priv;
4787dd7cddfSDavid du Colombier f = rudp->f;
4797dd7cddfSDavid du Colombier
4807dd7cddfSDavid du Colombier upriv->ustats.rudpInDatagrams++;
4817dd7cddfSDavid du Colombier
4827dd7cddfSDavid du Colombier uh = (Udphdr*)(bp->rp);
4837dd7cddfSDavid du Colombier
4847dd7cddfSDavid du Colombier /* Put back pseudo header for checksum
4857dd7cddfSDavid du Colombier * (remember old values for icmpnoconv())
4867dd7cddfSDavid du Colombier */
4877dd7cddfSDavid du Colombier ottl = uh->Unused;
4887dd7cddfSDavid du Colombier uh->Unused = 0;
4897dd7cddfSDavid du Colombier len = nhgets(uh->udplen);
4907dd7cddfSDavid du Colombier olen = nhgets(uh->udpplen);
4917dd7cddfSDavid du Colombier hnputs(uh->udpplen, len);
4927dd7cddfSDavid du Colombier
4937dd7cddfSDavid du Colombier v4tov6(raddr, uh->udpsrc);
4947dd7cddfSDavid du Colombier v4tov6(laddr, uh->udpdst);
4957dd7cddfSDavid du Colombier lport = nhgets(uh->udpdport);
4967dd7cddfSDavid du Colombier rport = nhgets(uh->udpsport);
4977dd7cddfSDavid du Colombier
4987dd7cddfSDavid du Colombier if(nhgets(uh->udpcksum)) {
4997dd7cddfSDavid du Colombier if(ptclcsum(bp, UDP_IPHDR, len+UDP_PHDRSIZE)) {
5007dd7cddfSDavid du Colombier upriv->ustats.rudpInErrors++;
5017dd7cddfSDavid du Colombier upriv->csumerr++;
5027dd7cddfSDavid du Colombier netlog(f, Logrudp, "rudp: checksum error %I\n", raddr);
5037dd7cddfSDavid du Colombier DPRINT("rudp: checksum error %I\n", raddr);
5047dd7cddfSDavid du Colombier freeblist(bp);
5057dd7cddfSDavid du Colombier return;
5067dd7cddfSDavid du Colombier }
5077dd7cddfSDavid du Colombier }
5087dd7cddfSDavid du Colombier
5097dd7cddfSDavid du Colombier qlock(rudp);
5107dd7cddfSDavid du Colombier
51180ee5cbfSDavid du Colombier c = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
51280ee5cbfSDavid du Colombier if(c == nil){
513ac020a8fSDavid du Colombier /* no conversation found */
5147dd7cddfSDavid du Colombier upriv->ustats.rudpNoPorts++;
51580ee5cbfSDavid du Colombier qunlock(rudp);
51680ee5cbfSDavid du Colombier netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport,
5177dd7cddfSDavid du Colombier laddr, lport);
5187dd7cddfSDavid du Colombier uh->Unused = ottl;
5197dd7cddfSDavid du Colombier hnputs(uh->udpplen, olen);
5207dd7cddfSDavid du Colombier icmpnoconv(f, bp);
5217dd7cddfSDavid du Colombier freeblist(bp);
5227dd7cddfSDavid du Colombier return;
5237dd7cddfSDavid du Colombier }
5247dd7cddfSDavid du Colombier ucb = (Rudpcb*)c->ptcl;
5257dd7cddfSDavid du Colombier qlock(ucb);
5267dd7cddfSDavid du Colombier qunlock(rudp);
5277dd7cddfSDavid du Colombier
5287dd7cddfSDavid du Colombier if(reliput(c, bp, raddr, rport) < 0){
5297dd7cddfSDavid du Colombier qunlock(ucb);
5307dd7cddfSDavid du Colombier freeb(bp);
5317dd7cddfSDavid du Colombier return;
5327dd7cddfSDavid du Colombier }
5337dd7cddfSDavid du Colombier
5347dd7cddfSDavid du Colombier /*
5357dd7cddfSDavid du Colombier * Trim the packet down to data size
5367dd7cddfSDavid du Colombier */
5377dd7cddfSDavid du Colombier
5387dd7cddfSDavid du Colombier len -= (UDP_RHDRSIZE-UDP_PHDRSIZE);
5397dd7cddfSDavid du Colombier bp = trimblock(bp, UDP_IPHDR+UDP_RHDRSIZE, len);
5407dd7cddfSDavid du Colombier if(bp == nil) {
5417dd7cddfSDavid du Colombier netlog(f, Logrudp, "rudp: len err %I.%d -> %I.%d\n",
5427dd7cddfSDavid du Colombier raddr, rport, laddr, lport);
5437dd7cddfSDavid du Colombier DPRINT("rudp: len err %I.%d -> %I.%d\n",
5447dd7cddfSDavid du Colombier raddr, rport, laddr, lport);
5457dd7cddfSDavid du Colombier upriv->lenerr++;
5467dd7cddfSDavid du Colombier return;
5477dd7cddfSDavid du Colombier }
5487dd7cddfSDavid du Colombier
5497dd7cddfSDavid du Colombier netlog(f, Logrudpmsg, "rudp: %I.%d -> %I.%d l %d\n",
5507dd7cddfSDavid du Colombier raddr, rport, laddr, lport, len);
5517dd7cddfSDavid du Colombier
5527dd7cddfSDavid du Colombier switch(ucb->headers){
553dc5a79c1SDavid du Colombier case 7:
554dc5a79c1SDavid du Colombier /* pass the src address */
555dc5a79c1SDavid du Colombier bp = padblock(bp, UDP_USEAD7);
556dc5a79c1SDavid du Colombier p = bp->rp;
557dc5a79c1SDavid du Colombier ipmove(p, raddr); p += IPaddrlen;
558dc5a79c1SDavid du Colombier ipmove(p, laddr); p += IPaddrlen;
559dc5a79c1SDavid du Colombier ipmove(p, ifc->lifc->local); p += IPaddrlen;
560dc5a79c1SDavid du Colombier hnputs(p, rport); p += 2;
561dc5a79c1SDavid du Colombier hnputs(p, lport);
562dc5a79c1SDavid du Colombier break;
5637dd7cddfSDavid du Colombier default:
5647dd7cddfSDavid du Colombier /* connection oriented rudp */
5657dd7cddfSDavid du Colombier if(ipcmp(c->raddr, IPnoaddr) == 0){
5667dd7cddfSDavid du Colombier /* save the src address in the conversation */
5677dd7cddfSDavid du Colombier ipmove(c->raddr, raddr);
5687dd7cddfSDavid du Colombier c->rport = rport;
5697dd7cddfSDavid du Colombier
5707dd7cddfSDavid du Colombier /* reply with the same ip address (if not broadcast) */
5717dd7cddfSDavid du Colombier if(ipforme(f, laddr) == Runi)
5727dd7cddfSDavid du Colombier ipmove(c->laddr, laddr);
5737dd7cddfSDavid du Colombier else
5749a747e4fSDavid du Colombier v4tov6(c->laddr, ifc->lifc->local);
5757dd7cddfSDavid du Colombier }
5767dd7cddfSDavid du Colombier break;
5777dd7cddfSDavid du Colombier }
5787dd7cddfSDavid du Colombier if(bp->next)
5797dd7cddfSDavid du Colombier bp = concatblock(bp);
5807dd7cddfSDavid du Colombier
5817dd7cddfSDavid du Colombier if(qfull(c->rq)) {
5827dd7cddfSDavid du Colombier netlog(f, Logrudp, "rudp: qfull %I.%d -> %I.%d\n", raddr, rport,
5837dd7cddfSDavid du Colombier laddr, lport);
5847dd7cddfSDavid du Colombier freeblist(bp);
5857dd7cddfSDavid du Colombier }
5867dd7cddfSDavid du Colombier else
5877dd7cddfSDavid du Colombier qpass(c->rq, bp);
5887dd7cddfSDavid du Colombier
5897dd7cddfSDavid du Colombier qunlock(ucb);
5907dd7cddfSDavid du Colombier }
5917dd7cddfSDavid du Colombier
5927dd7cddfSDavid du Colombier static char *rudpunknown = "unknown rudp ctl request";
5937dd7cddfSDavid du Colombier
5947dd7cddfSDavid du Colombier char*
rudpctl(Conv * c,char ** f,int n)5957dd7cddfSDavid du Colombier rudpctl(Conv *c, char **f, int n)
5967dd7cddfSDavid du Colombier {
5977dd7cddfSDavid du Colombier Rudpcb *ucb;
5987dd7cddfSDavid du Colombier uchar ip[IPaddrlen];
5997dd7cddfSDavid du Colombier int x;
6007dd7cddfSDavid du Colombier
6017dd7cddfSDavid du Colombier ucb = (Rudpcb*)c->ptcl;
6027dd7cddfSDavid du Colombier if(n < 1)
6037dd7cddfSDavid du Colombier return rudpunknown;
6047dd7cddfSDavid du Colombier
60584363d68SDavid du Colombier if(strcmp(f[0], "headers") == 0){
606f2c197d9SDavid du Colombier ucb->headers = 7; /* new headers format */
6077dd7cddfSDavid du Colombier return nil;
6087dd7cddfSDavid du Colombier } else if(strcmp(f[0], "hangup") == 0){
6097dd7cddfSDavid du Colombier if(n < 3)
6107dd7cddfSDavid du Colombier return "bad syntax";
611ea58ad6fSDavid du Colombier if (parseip(ip, f[1]) == -1)
612ea58ad6fSDavid du Colombier return Ebadip;
6137dd7cddfSDavid du Colombier x = atoi(f[2]);
6147dd7cddfSDavid du Colombier qlock(ucb);
6157dd7cddfSDavid du Colombier relforget(c, ip, x, 1);
6167dd7cddfSDavid du Colombier qunlock(ucb);
6177dd7cddfSDavid du Colombier return nil;
6187dd7cddfSDavid du Colombier } else if(strcmp(f[0], "randdrop") == 0){
6197dd7cddfSDavid du Colombier x = 10; /* default is 10% */
6207dd7cddfSDavid du Colombier if(n > 1)
6217dd7cddfSDavid du Colombier x = atoi(f[1]);
6227dd7cddfSDavid du Colombier if(x > 100 || x < 0)
6237dd7cddfSDavid du Colombier return "illegal rudp drop rate";
6247dd7cddfSDavid du Colombier ucb->randdrop = x;
6257dd7cddfSDavid du Colombier return nil;
6267dd7cddfSDavid du Colombier }
6277dd7cddfSDavid du Colombier return rudpunknown;
6287dd7cddfSDavid du Colombier }
6297dd7cddfSDavid du Colombier
6307dd7cddfSDavid du Colombier void
rudpadvise(Proto * rudp,Block * bp,char * msg)6317dd7cddfSDavid du Colombier rudpadvise(Proto *rudp, Block *bp, char *msg)
6327dd7cddfSDavid du Colombier {
6337dd7cddfSDavid du Colombier Udphdr *h;
6347dd7cddfSDavid du Colombier uchar source[IPaddrlen], dest[IPaddrlen];
6357dd7cddfSDavid du Colombier ushort psource, pdest;
6367dd7cddfSDavid du Colombier Conv *s, **p;
6377dd7cddfSDavid du Colombier
6387dd7cddfSDavid du Colombier h = (Udphdr*)(bp->rp);
6397dd7cddfSDavid du Colombier
6407dd7cddfSDavid du Colombier v4tov6(dest, h->udpdst);
6417dd7cddfSDavid du Colombier v4tov6(source, h->udpsrc);
6427dd7cddfSDavid du Colombier psource = nhgets(h->udpsport);
6437dd7cddfSDavid du Colombier pdest = nhgets(h->udpdport);
6447dd7cddfSDavid du Colombier
6457dd7cddfSDavid du Colombier /* Look for a connection */
6467dd7cddfSDavid du Colombier for(p = rudp->conv; *p; p++) {
6477dd7cddfSDavid du Colombier s = *p;
6487dd7cddfSDavid du Colombier if(s->rport == pdest)
6497dd7cddfSDavid du Colombier if(s->lport == psource)
6507dd7cddfSDavid du Colombier if(ipcmp(s->raddr, dest) == 0)
6517dd7cddfSDavid du Colombier if(ipcmp(s->laddr, source) == 0){
6527dd7cddfSDavid du Colombier qhangup(s->rq, msg);
6537dd7cddfSDavid du Colombier qhangup(s->wq, msg);
6547dd7cddfSDavid du Colombier break;
6557dd7cddfSDavid du Colombier }
6567dd7cddfSDavid du Colombier }
6577dd7cddfSDavid du Colombier freeblist(bp);
6587dd7cddfSDavid du Colombier }
6597dd7cddfSDavid du Colombier
6607dd7cddfSDavid du Colombier int
rudpstats(Proto * rudp,char * buf,int len)6617dd7cddfSDavid du Colombier rudpstats(Proto *rudp, char *buf, int len)
6627dd7cddfSDavid du Colombier {
6637dd7cddfSDavid du Colombier Rudppriv *upriv;
6647dd7cddfSDavid du Colombier
6657dd7cddfSDavid du Colombier upriv = rudp->priv;
6667dd7cddfSDavid du Colombier return snprint(buf, len, "%lud %lud %lud %lud %lud %lud\n",
6677dd7cddfSDavid du Colombier upriv->ustats.rudpInDatagrams,
6687dd7cddfSDavid du Colombier upriv->ustats.rudpNoPorts,
6697dd7cddfSDavid du Colombier upriv->ustats.rudpInErrors,
6707dd7cddfSDavid du Colombier upriv->ustats.rudpOutDatagrams,
6717dd7cddfSDavid du Colombier upriv->rxmits,
6727dd7cddfSDavid du Colombier upriv->orders);
6737dd7cddfSDavid du Colombier }
6747dd7cddfSDavid du Colombier
6757dd7cddfSDavid du Colombier void
rudpinit(Fs * fs)6767dd7cddfSDavid du Colombier rudpinit(Fs *fs)
6777dd7cddfSDavid du Colombier {
6787dd7cddfSDavid du Colombier
6797dd7cddfSDavid du Colombier Proto *rudp;
6807dd7cddfSDavid du Colombier
6817dd7cddfSDavid du Colombier rudp = smalloc(sizeof(Proto));
6827dd7cddfSDavid du Colombier rudp->priv = smalloc(sizeof(Rudppriv));
6837dd7cddfSDavid du Colombier rudp->name = "rudp";
6847dd7cddfSDavid du Colombier rudp->connect = rudpconnect;
6857dd7cddfSDavid du Colombier rudp->announce = rudpannounce;
6867dd7cddfSDavid du Colombier rudp->ctl = rudpctl;
6877dd7cddfSDavid du Colombier rudp->state = rudpstate;
6887dd7cddfSDavid du Colombier rudp->create = rudpcreate;
6897dd7cddfSDavid du Colombier rudp->close = rudpclose;
6907dd7cddfSDavid du Colombier rudp->rcv = rudpiput;
6917dd7cddfSDavid du Colombier rudp->advise = rudpadvise;
6927dd7cddfSDavid du Colombier rudp->stats = rudpstats;
6937dd7cddfSDavid du Colombier rudp->ipproto = IP_UDPPROTO;
6947ec5746aSDavid du Colombier rudp->nc = 32;
6957dd7cddfSDavid du Colombier rudp->ptclsize = sizeof(Rudpcb);
6967dd7cddfSDavid du Colombier
6977dd7cddfSDavid du Colombier Fsproto(fs, rudp);
6987dd7cddfSDavid du Colombier }
6997dd7cddfSDavid du Colombier
7007dd7cddfSDavid du Colombier /*********************************************/
7017dd7cddfSDavid du Colombier /* Here starts the reliable helper functions */
7027dd7cddfSDavid du Colombier /*********************************************/
7037dd7cddfSDavid du Colombier /*
7047dd7cddfSDavid du Colombier * Enqueue a copy of an unacked block for possible retransmissions
7057dd7cddfSDavid du Colombier */
7067dd7cddfSDavid du Colombier void
relackq(Reliable * r,Block * bp)7077dd7cddfSDavid du Colombier relackq(Reliable *r, Block *bp)
7087dd7cddfSDavid du Colombier {
7097dd7cddfSDavid du Colombier Block *np;
7107dd7cddfSDavid du Colombier
7117dd7cddfSDavid du Colombier np = copyblock(bp, blocklen(bp));
7127dd7cddfSDavid du Colombier if(r->unacked)
7137dd7cddfSDavid du Colombier r->unackedtail->list = np;
7147dd7cddfSDavid du Colombier else {
7157dd7cddfSDavid du Colombier /* restart timer */
7167dd7cddfSDavid du Colombier r->timeout = 0;
7177dd7cddfSDavid du Colombier r->xmits = 1;
7187dd7cddfSDavid du Colombier r->unacked = np;
7197dd7cddfSDavid du Colombier }
7207dd7cddfSDavid du Colombier r->unackedtail = np;
7217dd7cddfSDavid du Colombier np->list = nil;
7227dd7cddfSDavid du Colombier }
7237dd7cddfSDavid du Colombier
7247dd7cddfSDavid du Colombier /*
7257dd7cddfSDavid du Colombier * retransmit unacked blocks
7267dd7cddfSDavid du Colombier */
7277dd7cddfSDavid du Colombier void
relackproc(void * a)7287dd7cddfSDavid du Colombier relackproc(void *a)
7297dd7cddfSDavid du Colombier {
7307dd7cddfSDavid du Colombier Rudpcb *ucb;
7317dd7cddfSDavid du Colombier Proto *rudp;
7327dd7cddfSDavid du Colombier Reliable *r;
7337dd7cddfSDavid du Colombier Conv **s, *c;
7347dd7cddfSDavid du Colombier
7357dd7cddfSDavid du Colombier rudp = (Proto *)a;
7367dd7cddfSDavid du Colombier
7377dd7cddfSDavid du Colombier loop:
738dc5a79c1SDavid du Colombier tsleep(&up->sleep, return0, 0, Rudptickms);
7397dd7cddfSDavid du Colombier
7407dd7cddfSDavid du Colombier for(s = rudp->conv; *s; s++) {
7417dd7cddfSDavid du Colombier c = *s;
7427dd7cddfSDavid du Colombier ucb = (Rudpcb*)c->ptcl;
7437dd7cddfSDavid du Colombier qlock(ucb);
7447dd7cddfSDavid du Colombier
7457dd7cddfSDavid du Colombier for(r = ucb->r; r; r = r->next) {
7467dd7cddfSDavid du Colombier if(r->unacked != nil){
7477dd7cddfSDavid du Colombier r->timeout += Rudptickms;
7487dd7cddfSDavid du Colombier if(r->timeout > Rudprxms*r->xmits)
7497dd7cddfSDavid du Colombier relrexmit(c, r);
7507dd7cddfSDavid du Colombier }
7517dd7cddfSDavid du Colombier if(r->acksent != r->rcvseq)
7527dd7cddfSDavid du Colombier relsendack(c, r, 0);
7537dd7cddfSDavid du Colombier }
7547dd7cddfSDavid du Colombier qunlock(ucb);
7557dd7cddfSDavid du Colombier }
7567dd7cddfSDavid du Colombier goto loop;
7577dd7cddfSDavid du Colombier }
7587dd7cddfSDavid du Colombier
7597dd7cddfSDavid du Colombier /*
7607dd7cddfSDavid du Colombier * get the state record for a conversation
7617dd7cddfSDavid du Colombier */
7627dd7cddfSDavid du Colombier Reliable*
relstate(Rudpcb * ucb,uchar * addr,ushort port,char * from)7637dd7cddfSDavid du Colombier relstate(Rudpcb *ucb, uchar *addr, ushort port, char *from)
7647dd7cddfSDavid du Colombier {
7657dd7cddfSDavid du Colombier Reliable *r, **l;
7667dd7cddfSDavid du Colombier
7677dd7cddfSDavid du Colombier l = &ucb->r;
7687dd7cddfSDavid du Colombier for(r = *l; r; r = *l){
7697dd7cddfSDavid du Colombier if(memcmp(addr, r->addr, IPaddrlen) == 0 &&
7707dd7cddfSDavid du Colombier port == r->port)
7717dd7cddfSDavid du Colombier break;
7727dd7cddfSDavid du Colombier l = &r->next;
7737dd7cddfSDavid du Colombier }
7747dd7cddfSDavid du Colombier
7757dd7cddfSDavid du Colombier /* no state for this addr/port, create some */
7767dd7cddfSDavid du Colombier if(r == nil){
7777dd7cddfSDavid du Colombier while(generation == 0)
7787dd7cddfSDavid du Colombier generation = rand();
7797dd7cddfSDavid du Colombier
7807dd7cddfSDavid du Colombier DPRINT("from %s new state %lud for %I!%ud\n",
7817dd7cddfSDavid du Colombier from, generation, addr, port);
7827dd7cddfSDavid du Colombier
7837dd7cddfSDavid du Colombier r = smalloc(sizeof(Reliable));
7847dd7cddfSDavid du Colombier memmove(r->addr, addr, IPaddrlen);
7857dd7cddfSDavid du Colombier r->port = port;
7867dd7cddfSDavid du Colombier r->unacked = 0;
7877dd7cddfSDavid du Colombier if(generation == Hangupgen)
7887dd7cddfSDavid du Colombier generation++;
7897dd7cddfSDavid du Colombier r->sndgen = generation++;
7907dd7cddfSDavid du Colombier r->sndseq = 0;
7917dd7cddfSDavid du Colombier r->ackrcvd = 0;
7927dd7cddfSDavid du Colombier r->rcvgen = 0;
7937dd7cddfSDavid du Colombier r->rcvseq = 0;
7947dd7cddfSDavid du Colombier r->acksent = 0;
7957dd7cddfSDavid du Colombier r->xmits = 0;
7967dd7cddfSDavid du Colombier r->timeout = 0;
7977dd7cddfSDavid du Colombier r->ref = 0;
7987dd7cddfSDavid du Colombier incref(r); /* one reference for being in the list */
7997dd7cddfSDavid du Colombier
8007dd7cddfSDavid du Colombier *l = r;
8017dd7cddfSDavid du Colombier }
8027dd7cddfSDavid du Colombier
8037dd7cddfSDavid du Colombier incref(r);
8047dd7cddfSDavid du Colombier return r;
8057dd7cddfSDavid du Colombier }
8067dd7cddfSDavid du Colombier
8077dd7cddfSDavid du Colombier void
relput(Reliable * r)8087dd7cddfSDavid du Colombier relput(Reliable *r)
8097dd7cddfSDavid du Colombier {
8107dd7cddfSDavid du Colombier if(decref(r) == 0)
8117dd7cddfSDavid du Colombier free(r);
8127dd7cddfSDavid du Colombier }
8137dd7cddfSDavid du Colombier
8147dd7cddfSDavid du Colombier /*
8157dd7cddfSDavid du Colombier * forget a Reliable state
8167dd7cddfSDavid du Colombier */
8177dd7cddfSDavid du Colombier void
relforget(Conv * c,uchar * ip,int port,int originator)8187dd7cddfSDavid du Colombier relforget(Conv *c, uchar *ip, int port, int originator)
8197dd7cddfSDavid du Colombier {
8207dd7cddfSDavid du Colombier Rudpcb *ucb;
8217dd7cddfSDavid du Colombier Reliable *r, **l;
8227dd7cddfSDavid du Colombier
8237dd7cddfSDavid du Colombier ucb = (Rudpcb*)c->ptcl;
8247dd7cddfSDavid du Colombier
8257dd7cddfSDavid du Colombier l = &ucb->r;
8267dd7cddfSDavid du Colombier for(r = *l; r; r = *l){
8277dd7cddfSDavid du Colombier if(ipcmp(ip, r->addr) == 0 && port == r->port){
8287dd7cddfSDavid du Colombier *l = r->next;
8297dd7cddfSDavid du Colombier if(originator)
8307dd7cddfSDavid du Colombier relsendack(c, r, 1);
8317dd7cddfSDavid du Colombier relhangup(c, r);
8327dd7cddfSDavid du Colombier relput(r); /* remove from the list */
8337dd7cddfSDavid du Colombier break;
8347dd7cddfSDavid du Colombier }
8357dd7cddfSDavid du Colombier l = &r->next;
8367dd7cddfSDavid du Colombier }
8377dd7cddfSDavid du Colombier }
8387dd7cddfSDavid du Colombier
8397dd7cddfSDavid du Colombier /*
8407dd7cddfSDavid du Colombier * process a rcvd reliable packet. return -1 if not to be passed to user process,
8417dd7cddfSDavid du Colombier * 0 therwise.
8427dd7cddfSDavid du Colombier *
8437dd7cddfSDavid du Colombier * called with ucb locked.
8447dd7cddfSDavid du Colombier */
8457dd7cddfSDavid du Colombier int
reliput(Conv * c,Block * bp,uchar * addr,ushort port)8467dd7cddfSDavid du Colombier reliput(Conv *c, Block *bp, uchar *addr, ushort port)
8477dd7cddfSDavid du Colombier {
8487dd7cddfSDavid du Colombier Block *nbp;
8497dd7cddfSDavid du Colombier Rudpcb *ucb;
8507dd7cddfSDavid du Colombier Rudppriv *upriv;
8517dd7cddfSDavid du Colombier Udphdr *uh;
8527dd7cddfSDavid du Colombier Reliable *r;
8537dd7cddfSDavid du Colombier Rudphdr *rh;
8547dd7cddfSDavid du Colombier ulong seq, ack, sgen, agen, ackreal;
8557dd7cddfSDavid du Colombier int rv = -1;
8567dd7cddfSDavid du Colombier
8577dd7cddfSDavid du Colombier /* get fields */
8587dd7cddfSDavid du Colombier uh = (Udphdr*)(bp->rp);
8597dd7cddfSDavid du Colombier rh = (Rudphdr*)uh;
8607dd7cddfSDavid du Colombier seq = nhgetl(rh->relseq);
8617dd7cddfSDavid du Colombier sgen = nhgetl(rh->relsgen);
8627dd7cddfSDavid du Colombier ack = nhgetl(rh->relack);
8637dd7cddfSDavid du Colombier agen = nhgetl(rh->relagen);
8647dd7cddfSDavid du Colombier
8657dd7cddfSDavid du Colombier upriv = c->p->priv;
8667dd7cddfSDavid du Colombier ucb = (Rudpcb*)c->ptcl;
8677dd7cddfSDavid du Colombier r = relstate(ucb, addr, port, "input");
8687dd7cddfSDavid du Colombier
8697dd7cddfSDavid du Colombier DPRINT("rcvd %lud/%lud, %lud/%lud, r->sndgen = %lud\n",
8707dd7cddfSDavid du Colombier seq, sgen, ack, agen, r->sndgen);
8717dd7cddfSDavid du Colombier
8727dd7cddfSDavid du Colombier /* if acking an incorrect generation, ignore */
8737dd7cddfSDavid du Colombier if(ack && agen != r->sndgen)
8747dd7cddfSDavid du Colombier goto out;
8757dd7cddfSDavid du Colombier
8767dd7cddfSDavid du Colombier /* Look for a hangup */
8777dd7cddfSDavid du Colombier if(sgen == Hangupgen) {
8787dd7cddfSDavid du Colombier if(agen == r->sndgen)
8797dd7cddfSDavid du Colombier relforget(c, addr, port, 0);
8807dd7cddfSDavid du Colombier goto out;
8817dd7cddfSDavid du Colombier }
8827dd7cddfSDavid du Colombier
8837dd7cddfSDavid du Colombier /* make sure we're not talking to a new remote side */
8847dd7cddfSDavid du Colombier if(r->rcvgen != sgen){
8857dd7cddfSDavid du Colombier if(seq != 0 && seq != 1)
8867dd7cddfSDavid du Colombier goto out;
8877dd7cddfSDavid du Colombier
8887dd7cddfSDavid du Colombier /* new connection */
8897dd7cddfSDavid du Colombier if(r->rcvgen != 0){
8907dd7cddfSDavid du Colombier DPRINT("new con r->rcvgen = %lud, sgen = %lud\n", r->rcvgen, sgen);
8917dd7cddfSDavid du Colombier relhangup(c, r);
8927dd7cddfSDavid du Colombier }
8937dd7cddfSDavid du Colombier r->rcvgen = sgen;
8947dd7cddfSDavid du Colombier }
8957dd7cddfSDavid du Colombier
8967dd7cddfSDavid du Colombier /* dequeue acked packets */
8977dd7cddfSDavid du Colombier if(ack && agen == r->sndgen){
8987dd7cddfSDavid du Colombier ackreal = 0;
8997dd7cddfSDavid du Colombier while(r->unacked != nil && INSEQ(ack, r->ackrcvd, r->sndseq)){
9007dd7cddfSDavid du Colombier nbp = r->unacked;
9017dd7cddfSDavid du Colombier r->unacked = nbp->list;
9027dd7cddfSDavid du Colombier DPRINT("%lud/%lud acked, r->sndgen = %lud\n",
9037dd7cddfSDavid du Colombier ack, agen, r->sndgen);
9047dd7cddfSDavid du Colombier freeb(nbp);
9057dd7cddfSDavid du Colombier r->ackrcvd = NEXTSEQ(r->ackrcvd);
9067dd7cddfSDavid du Colombier ackreal = 1;
9077dd7cddfSDavid du Colombier }
9087dd7cddfSDavid du Colombier
9097dd7cddfSDavid du Colombier /* flow control */
9107dd7cddfSDavid du Colombier if(UNACKED(r) < Maxunacked/8 && r->blocked)
9117dd7cddfSDavid du Colombier wakeup(&r->vous);
9127dd7cddfSDavid du Colombier
9137dd7cddfSDavid du Colombier /*
9147dd7cddfSDavid du Colombier * retransmit next packet if the acked packet
9157dd7cddfSDavid du Colombier * was transmitted more than once
9167dd7cddfSDavid du Colombier */
9177dd7cddfSDavid du Colombier if(ackreal && r->unacked != nil){
9187dd7cddfSDavid du Colombier r->timeout = 0;
9197dd7cddfSDavid du Colombier if(r->xmits > 1){
9207dd7cddfSDavid du Colombier r->xmits = 1;
9217dd7cddfSDavid du Colombier relrexmit(c, r);
9227dd7cddfSDavid du Colombier }
9237dd7cddfSDavid du Colombier }
9247dd7cddfSDavid du Colombier
9257dd7cddfSDavid du Colombier }
9267dd7cddfSDavid du Colombier
9277dd7cddfSDavid du Colombier /* no message or input queue full */
9287dd7cddfSDavid du Colombier if(seq == 0 || qfull(c->rq))
9297dd7cddfSDavid du Colombier goto out;
9307dd7cddfSDavid du Colombier
9317dd7cddfSDavid du Colombier /* refuse out of order delivery */
9327dd7cddfSDavid du Colombier if(seq != NEXTSEQ(r->rcvseq)){
9337dd7cddfSDavid du Colombier relsendack(c, r, 0); /* tell him we got it already */
9347dd7cddfSDavid du Colombier upriv->orders++;
9357dd7cddfSDavid du Colombier DPRINT("out of sequence %lud not %lud\n", seq, NEXTSEQ(r->rcvseq));
9367dd7cddfSDavid du Colombier goto out;
9377dd7cddfSDavid du Colombier }
9387dd7cddfSDavid du Colombier r->rcvseq = seq;
9397dd7cddfSDavid du Colombier
9407dd7cddfSDavid du Colombier rv = 0;
9417dd7cddfSDavid du Colombier out:
9427dd7cddfSDavid du Colombier relput(r);
9437dd7cddfSDavid du Colombier return rv;
9447dd7cddfSDavid du Colombier }
9457dd7cddfSDavid du Colombier
9467dd7cddfSDavid du Colombier void
relsendack(Conv * c,Reliable * r,int hangup)9477dd7cddfSDavid du Colombier relsendack(Conv *c, Reliable *r, int hangup)
9487dd7cddfSDavid du Colombier {
9497dd7cddfSDavid du Colombier Udphdr *uh;
9507dd7cddfSDavid du Colombier Block *bp;
9517dd7cddfSDavid du Colombier Rudphdr *rh;
9527dd7cddfSDavid du Colombier int ptcllen;
9537dd7cddfSDavid du Colombier Fs *f;
9547dd7cddfSDavid du Colombier
9557dd7cddfSDavid du Colombier bp = allocb(UDP_IPHDR + UDP_RHDRSIZE);
9567dd7cddfSDavid du Colombier if(bp == nil)
9577dd7cddfSDavid du Colombier return;
9587dd7cddfSDavid du Colombier bp->wp += UDP_IPHDR + UDP_RHDRSIZE;
9597dd7cddfSDavid du Colombier f = c->p->f;
9607dd7cddfSDavid du Colombier uh = (Udphdr *)(bp->rp);
9613ff48bf5SDavid du Colombier uh->vihl = IP_VER4;
9627dd7cddfSDavid du Colombier rh = (Rudphdr*)uh;
9637dd7cddfSDavid du Colombier
9647dd7cddfSDavid du Colombier ptcllen = (UDP_RHDRSIZE-UDP_PHDRSIZE);
9657dd7cddfSDavid du Colombier uh->Unused = 0;
9667dd7cddfSDavid du Colombier uh->udpproto = IP_UDPPROTO;
9677dd7cddfSDavid du Colombier uh->frag[0] = 0;
9687dd7cddfSDavid du Colombier uh->frag[1] = 0;
9697dd7cddfSDavid du Colombier hnputs(uh->udpplen, ptcllen);
9707dd7cddfSDavid du Colombier
9717dd7cddfSDavid du Colombier v6tov4(uh->udpdst, r->addr);
9727dd7cddfSDavid du Colombier hnputs(uh->udpdport, r->port);
9737dd7cddfSDavid du Colombier hnputs(uh->udpsport, c->lport);
9747dd7cddfSDavid du Colombier if(ipcmp(c->laddr, IPnoaddr) == 0)
9757dd7cddfSDavid du Colombier findlocalip(f, c->laddr, c->raddr);
9767dd7cddfSDavid du Colombier v6tov4(uh->udpsrc, c->laddr);
9777dd7cddfSDavid du Colombier hnputs(uh->udplen, ptcllen);
9787dd7cddfSDavid du Colombier
9797dd7cddfSDavid du Colombier if(hangup)
9807dd7cddfSDavid du Colombier hnputl(rh->relsgen, Hangupgen);
9817dd7cddfSDavid du Colombier else
9827dd7cddfSDavid du Colombier hnputl(rh->relsgen, r->sndgen);
9837dd7cddfSDavid du Colombier hnputl(rh->relseq, 0);
9847dd7cddfSDavid du Colombier hnputl(rh->relagen, r->rcvgen);
9857dd7cddfSDavid du Colombier hnputl(rh->relack, r->rcvseq);
9867dd7cddfSDavid du Colombier
9877dd7cddfSDavid du Colombier if(r->acksent < r->rcvseq)
9887dd7cddfSDavid du Colombier r->acksent = r->rcvseq;
9897dd7cddfSDavid du Colombier
9907dd7cddfSDavid du Colombier uh->udpcksum[0] = 0;
9917dd7cddfSDavid du Colombier uh->udpcksum[1] = 0;
9927dd7cddfSDavid du Colombier hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, UDP_RHDRSIZE));
9937dd7cddfSDavid du Colombier
9947dd7cddfSDavid du Colombier DPRINT("sendack: %lud/%lud, %lud/%lud\n", 0L, r->sndgen, r->rcvseq, r->rcvgen);
9957dd7cddfSDavid du Colombier doipoput(c, f, bp, 0, c->ttl, c->tos);
9967dd7cddfSDavid du Colombier }
9977dd7cddfSDavid du Colombier
9987dd7cddfSDavid du Colombier
9997dd7cddfSDavid du Colombier /*
10007dd7cddfSDavid du Colombier * called with ucb locked (and c locked if user initiated close)
10017dd7cddfSDavid du Colombier */
10027dd7cddfSDavid du Colombier void
relhangup(Conv * c,Reliable * r)10037dd7cddfSDavid du Colombier relhangup(Conv *c, Reliable *r)
10047dd7cddfSDavid du Colombier {
10057dd7cddfSDavid du Colombier int n;
10067dd7cddfSDavid du Colombier Block *bp;
10079a747e4fSDavid du Colombier char hup[ERRMAX];
10087dd7cddfSDavid du Colombier
10097dd7cddfSDavid du Colombier n = snprint(hup, sizeof(hup), "hangup %I!%d", r->addr, r->port);
10107dd7cddfSDavid du Colombier qproduce(c->eq, hup, n);
10117dd7cddfSDavid du Colombier
10127dd7cddfSDavid du Colombier /*
10137dd7cddfSDavid du Colombier * dump any unacked outgoing messages
10147dd7cddfSDavid du Colombier */
10157dd7cddfSDavid du Colombier for(bp = r->unacked; bp != nil; bp = r->unacked){
10167dd7cddfSDavid du Colombier r->unacked = bp->list;
10177dd7cddfSDavid du Colombier bp->list = nil;
10187dd7cddfSDavid du Colombier freeb(bp);
10197dd7cddfSDavid du Colombier }
10207dd7cddfSDavid du Colombier
10217dd7cddfSDavid du Colombier r->rcvgen = 0;
10227dd7cddfSDavid du Colombier r->rcvseq = 0;
10237dd7cddfSDavid du Colombier r->acksent = 0;
10247dd7cddfSDavid du Colombier if(generation == Hangupgen)
10257dd7cddfSDavid du Colombier generation++;
10267dd7cddfSDavid du Colombier r->sndgen = generation++;
10277dd7cddfSDavid du Colombier r->sndseq = 0;
10287dd7cddfSDavid du Colombier r->ackrcvd = 0;
10297dd7cddfSDavid du Colombier r->xmits = 0;
10307dd7cddfSDavid du Colombier r->timeout = 0;
10317dd7cddfSDavid du Colombier wakeup(&r->vous);
10327dd7cddfSDavid du Colombier }
10337dd7cddfSDavid du Colombier
10347dd7cddfSDavid du Colombier /*
10357dd7cddfSDavid du Colombier * called with ucb locked
10367dd7cddfSDavid du Colombier */
10377dd7cddfSDavid du Colombier void
relrexmit(Conv * c,Reliable * r)10387dd7cddfSDavid du Colombier relrexmit(Conv *c, Reliable *r)
10397dd7cddfSDavid du Colombier {
10407dd7cddfSDavid du Colombier Rudppriv *upriv;
10417dd7cddfSDavid du Colombier Block *np;
10427dd7cddfSDavid du Colombier Fs *f;
10437dd7cddfSDavid du Colombier
10447dd7cddfSDavid du Colombier upriv = c->p->priv;
10457dd7cddfSDavid du Colombier f = c->p->f;
10467dd7cddfSDavid du Colombier r->timeout = 0;
10477dd7cddfSDavid du Colombier if(r->xmits++ > Rudpmaxxmit){
10487dd7cddfSDavid du Colombier relhangup(c, r);
10497dd7cddfSDavid du Colombier return;
10507dd7cddfSDavid du Colombier }
10517dd7cddfSDavid du Colombier
10527dd7cddfSDavid du Colombier upriv->rxmits++;
10537dd7cddfSDavid du Colombier np = copyblock(r->unacked, blocklen(r->unacked));
10547dd7cddfSDavid du Colombier DPRINT("rxmit r->ackrvcd+1 = %lud\n", r->ackrcvd+1);
10557dd7cddfSDavid du Colombier doipoput(c, f, np, 0, c->ttl, c->tos);
10567dd7cddfSDavid du Colombier }
1057