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