Refactored icmpkick6
[akaros.git] / kern / src / net / rudp.c
1 /*
2  *  This protocol is compatible with UDP's packet format.
3  *  It could be done over UDP if need be.
4  */
5 #include        "u.h"
6 #include        "../port/lib.h"
7 #include        "mem.h"
8 #include        "dat.h"
9 #include        "fns.h"
10 #include        "../port/error.h"
11
12 #include        "ip.h"
13
14 #define DEBUG   0
15 #define DPRINT if(DEBUG)print
16
17 #define SEQDIFF(a,b) ( (a)>=(b)?\
18                         (a)-(b):\
19                         0xffffffffUL-((b)-(a)) )
20 #define INSEQ(a,start,end) ( (start)<=(end)?\
21                                 ((a)>(start)&&(a)<=(end)):\
22                                 ((a)>(start)||(a)<=(end)) )
23 #define UNACKED(r) SEQDIFF(r->sndseq, r->ackrcvd)
24 #define NEXTSEQ(a) ( (a)+1 == 0 ? 1 : (a)+1 )
25
26 enum {
27         UDP_HDRSIZE = 20,                       /* pseudo header + udp header */
28         UDP_PHDRSIZE = 12,      /* pseudo header */
29         UDP_RHDRSIZE = 36,      /* pseudo header + udp header + rudp header */
30         UDP_IPHDR = 8,  /* ip header */
31         IP_UDPPROTO = 254,
32         UDP_USEAD7 = 52,
33         UDP_USEAD6 = 36,
34         UDP_USEAD4 = 12,
35
36         Rudprxms = 200,
37         Rudptickms = 50,
38         Rudpmaxxmit = 10,
39         Maxunacked = 100,
40
41 };
42
43 #define Hangupgen       0xffffffff      /* used only in hangup messages */
44
45 typedef struct Udphdr Udphdr;
46 struct Udphdr {
47         /* ip header */
48         uchar vihl;                                     /* Version and header length */
49         uchar tos;                                      /* Type of service */
50         uchar length[2];                        /* packet length */
51         uchar id[2];                            /* Identification */
52         uchar frag[2];                          /* Fragment information */
53
54         /* pseudo header starts here */
55         uchar Unused;
56         uchar udpproto;                         /* Protocol */
57         uchar udpplen[2];                       /* Header plus data length */
58         uchar udpsrc[4];                        /* Ip source */
59         uchar udpdst[4];                        /* Ip destination */
60
61         /* udp header */
62         uchar udpsport[2];                      /* Source port */
63         uchar udpdport[2];                      /* Destination port */
64         uchar udplen[2];                        /* data length */
65         uchar udpcksum[2];                      /* Checksum */
66 };
67
68 typedef struct Rudphdr Rudphdr;
69 struct Rudphdr {
70         /* ip header */
71         uchar vihl;                                     /* Version and header length */
72         uchar tos;                                      /* Type of service */
73         uchar length[2];                        /* packet length */
74         uchar id[2];                            /* Identification */
75         uchar frag[2];                          /* Fragment information */
76
77         /* pseudo header starts here */
78         uchar Unused;
79         uchar udpproto;                         /* Protocol */
80         uchar udpplen[2];                       /* Header plus data length */
81         uchar udpsrc[4];                        /* Ip source */
82         uchar udpdst[4];                        /* Ip destination */
83
84         /* udp header */
85         uchar udpsport[2];                      /* Source port */
86         uchar udpdport[2];                      /* Destination port */
87         uchar udplen[2];                        /* data length (includes rudp header) */
88         uchar udpcksum[2];                      /* Checksum */
89
90         /* rudp header */
91         uchar relseq[4];                        /* id of this packet (or 0) */
92         uchar relsgen[4];                       /* generation/time stamp */
93         uchar relack[4];                        /* packet being acked (or 0) */
94         uchar relagen[4];                       /* generation/time stamp */
95 };
96
97 /*
98  *  one state structure per destination
99  */
100 typedef struct Reliable Reliable;
101 struct Reliable {
102         Ref;
103
104         Reliable *next;
105
106         uchar addr[IPaddrlen];          /* always V6 when put here */
107         ushort port;
108
109         Block *unacked;                         /* unacked msg list */
110         Block *unackedtail;                     /*  and its tail */
111
112         int timeout;                            /* time since first unacked msg sent */
113         int xmits;                                      /* number of times first unacked msg sent */
114
115         ulong sndseq;                           /* next packet to be sent */
116         ulong sndgen;                           /*  and its generation */
117
118         ulong rcvseq;                           /* last packet received */
119         ulong rcvgen;                           /*  and its generation */
120
121         ulong acksent;                          /* last ack sent */
122         ulong ackrcvd;                          /* last msg for which ack was rcvd */
123
124         /* flow control */
125         QLock lock;
126         Rendez vous;
127         int blocked;
128 };
129
130 /* MIB II counters */
131 typedef struct Rudpstats Rudpstats;
132 struct Rudpstats {
133         ulong rudpInDatagrams;
134         ulong rudpNoPorts;
135         ulong rudpInErrors;
136         ulong rudpOutDatagrams;
137 };
138
139 typedef struct Rudppriv Rudppriv;
140 struct Rudppriv {
141         Ipht ht;
142
143         /* MIB counters */
144         Rudpstats ustats;
145
146         /* non-MIB stats */
147         ulong csumerr;                          /* checksum errors */
148         ulong lenerr;                           /* short packet */
149         ulong rxmits;                           /* # of retransmissions */
150         ulong orders;                           /* # of out of order pkts */
151
152         /* keeping track of the ack kproc */
153         int ackprocstarted;
154         QLock apl;
155 };
156
157 static ulong generation = 0;
158 static Rendez rend;
159
160 /*
161  *  protocol specific part of Conv
162  */
163 typedef struct Rudpcb Rudpcb;
164 struct Rudpcb {
165         QLock;
166         uchar headers;
167         uchar randdrop;
168         Reliable *r;
169 };
170
171 /*
172  * local functions 
173  */
174 void relsendack(Conv *, Reliable *, int);
175 int reliput(Conv *, Block *, uchar *, ushort);
176 Reliable *relstate(Rudpcb *, uchar *, ushort, char *);
177 void relput(Reliable *);
178 void relforget(Conv *, uchar *, int, int);
179 void relackproc(void *);
180 void relackq(Reliable *, Block *);
181 void relhangup(Conv *, Reliable *);
182 void relrexmit(Conv *, Reliable *);
183 void relput(Reliable *);
184 void rudpkick(void *x);
185
186 static void rudpstartackproc(Proto * rudp)
187 {
188         Rudppriv *rpriv;
189         char kpname[KNAMELEN];
190
191         rpriv = rudp->priv;
192         if (rpriv->ackprocstarted == 0) {
193                 qlock(&rpriv->apl);
194                 if (rpriv->ackprocstarted == 0) {
195                         sprint(kpname, "#I%drudpack", rudp->f->dev);
196                         ktask(kpname, relackproc, rudp);
197                         rpriv->ackprocstarted = 1;
198                 }
199                 qunlock(&rpriv->apl);
200         }
201 }
202
203 static char *rudpconnect(Conv * c, char **argv, int argc)
204 {
205         char *e;
206         Rudppriv *upriv;
207
208         upriv = c->p->priv;
209         rudpstartackproc(c->p);
210         e = Fsstdconnect(c, argv, argc);
211         Fsconnected(c, e);
212         iphtadd(&upriv->ht, c);
213
214         return e;
215 }
216
217 static int rudpstate(Conv * c, char *state, int n)
218 {
219         Rudpcb *ucb;
220         Reliable *r;
221         int m;
222
223         m = snprint(state, n, "%s", c->inuse ? "Open" : "Closed");
224         ucb = (Rudpcb *) c->ptcl;
225         qlock(ucb);
226         for (r = ucb->r; r; r = r->next)
227                 m += snprint(state + m, n - m, " %I/%ld", r->addr, UNACKED(r));
228         qunlock(ucb);
229         return m;
230 }
231
232 static char *rudpannounce(Conv * c, char **argv, int argc)
233 {
234         char *e;
235         Rudppriv *upriv;
236
237         upriv = c->p->priv;
238         rudpstartackproc(c->p);
239         e = Fsstdannounce(c, argv, argc);
240         if (e != nil)
241                 return e;
242         Fsconnected(c, nil);
243         iphtadd(&upriv->ht, c);
244
245         return nil;
246 }
247
248 static void rudpcreate(Conv * c)
249 {
250         c->rq = qopen(64 * 1024, Qmsg, 0, 0);
251         c->wq = qopen(64 * 1024, Qkick, rudpkick, c);
252 }
253
254 static void rudpclose(Conv * c)
255 {
256         Rudpcb *ucb;
257         Reliable *r, *nr;
258         Rudppriv *upriv;
259
260         upriv = c->p->priv;
261         iphtrem(&upriv->ht, c);
262
263         /* force out any delayed acks */
264         ucb = (Rudpcb *) c->ptcl;
265         qlock(ucb);
266         for (r = ucb->r; r; r = r->next) {
267                 if (r->acksent != r->rcvseq)
268                         relsendack(c, r, 0);
269         }
270         qunlock(ucb);
271
272         qclose(c->rq);
273         qclose(c->wq);
274         qclose(c->eq);
275         ipmove(c->laddr, IPnoaddr);
276         ipmove(c->raddr, IPnoaddr);
277         c->lport = 0;
278         c->rport = 0;
279
280         ucb->headers = 0;
281         ucb->randdrop = 0;
282         qlock(ucb);
283         for (r = ucb->r; r; r = nr) {
284                 if (r->acksent != r->rcvseq)
285                         relsendack(c, r, 0);
286                 nr = r->next;
287                 relhangup(c, r);
288                 relput(r);
289         }
290         ucb->r = 0;
291
292         qunlock(ucb);
293 }
294
295 /*
296  *  randomly don't send packets
297  */
298 static void doipoput(Conv * c, Fs * f, Block * bp, int x, int ttl, int tos)
299 {
300         Rudpcb *ucb;
301
302         ucb = (Rudpcb *) c->ptcl;
303         if (ucb->randdrop && nrand(100) < ucb->randdrop)
304                 freeblist(bp);
305         else
306                 ipoput4(f, bp, x, ttl, tos, nil);
307 }
308
309 int flow(void *v)
310 {
311         Reliable *r = v;
312
313         return UNACKED(r) <= Maxunacked;
314 }
315
316 void rudpkick(void *x)
317 {
318         Conv *c = x;
319         Udphdr *uh;
320         ushort rport;
321         uchar laddr[IPaddrlen], raddr[IPaddrlen];
322         Block *bp;
323         Rudpcb *ucb;
324         Rudphdr *rh;
325         Reliable *r;
326         int dlen, ptcllen;
327         Rudppriv *upriv;
328         Fs *f;
329
330         upriv = c->p->priv;
331         f = c->p->f;
332
333         netlog(c->p->f, Logrudp, "rudp: kick\n");
334         bp = qget(c->wq);
335         if (bp == nil)
336                 return;
337
338         ucb = (Rudpcb *) c->ptcl;
339         switch (ucb->headers) {
340                 case 7:
341                         /* get user specified addresses */
342                         bp = pullupblock(bp, UDP_USEAD7);
343                         if (bp == nil)
344                                 return;
345                         ipmove(raddr, bp->rp);
346                         bp->rp += IPaddrlen;
347                         ipmove(laddr, bp->rp);
348                         bp->rp += IPaddrlen;
349                         /* pick interface closest to dest */
350                         if (ipforme(f, laddr) != Runi)
351                                 findlocalip(f, laddr, raddr);
352                         bp->rp += IPaddrlen;    /* Ignore ifc address */
353                         rport = nhgets(bp->rp);
354                         bp->rp += 2 + 2;        /* Ignore local port */
355                         break;
356                 case 6:
357                         /* get user specified addresses */
358                         bp = pullupblock(bp, UDP_USEAD6);
359                         if (bp == nil)
360                                 return;
361                         ipmove(raddr, bp->rp);
362                         bp->rp += IPaddrlen;
363                         ipmove(laddr, bp->rp);
364                         bp->rp += IPaddrlen;
365                         /* pick interface closest to dest */
366                         if (ipforme(f, laddr) != Runi)
367                                 findlocalip(f, laddr, raddr);
368                         rport = nhgets(bp->rp);
369
370                         bp->rp += 4;    /* Igonore local port */
371                         break;
372                 default:
373                         ipmove(raddr, c->raddr);
374                         ipmove(laddr, c->laddr);
375                         rport = c->rport;
376
377                         break;
378         }
379
380         dlen = blocklen(bp);
381
382         /* Make space to fit rudp & ip header */
383         bp = padblock(bp, UDP_IPHDR + UDP_RHDRSIZE);
384         if (bp == nil)
385                 return;
386
387         uh = (Udphdr *) (bp->rp);
388         uh->vihl = IP_VER4;
389
390         rh = (Rudphdr *) uh;
391
392         ptcllen = dlen + (UDP_RHDRSIZE - UDP_PHDRSIZE);
393         uh->Unused = 0;
394         uh->udpproto = IP_UDPPROTO;
395         uh->frag[0] = 0;
396         uh->frag[1] = 0;
397         hnputs(uh->udpplen, ptcllen);
398         switch (ucb->headers) {
399                 case 6:
400                 case 7:
401                         v6tov4(uh->udpdst, raddr);
402                         hnputs(uh->udpdport, rport);
403                         v6tov4(uh->udpsrc, laddr);
404                         break;
405                 default:
406                         v6tov4(uh->udpdst, c->raddr);
407                         hnputs(uh->udpdport, c->rport);
408                         if (ipcmp(c->laddr, IPnoaddr) == 0)
409                                 findlocalip(f, c->laddr, c->raddr);
410                         v6tov4(uh->udpsrc, c->laddr);
411                         break;
412         }
413         hnputs(uh->udpsport, c->lport);
414         hnputs(uh->udplen, ptcllen);
415         uh->udpcksum[0] = 0;
416         uh->udpcksum[1] = 0;
417
418         qlock(ucb);
419         r = relstate(ucb, raddr, rport, "kick");
420         r->sndseq = NEXTSEQ(r->sndseq);
421         hnputl(rh->relseq, r->sndseq);
422         hnputl(rh->relsgen, r->sndgen);
423
424         hnputl(rh->relack, r->rcvseq);  /* ACK last rcvd packet */
425         hnputl(rh->relagen, r->rcvgen);
426
427         if (r->rcvseq != r->acksent)
428                 r->acksent = r->rcvseq;
429
430         hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, dlen + UDP_RHDRSIZE));
431
432         relackq(r, bp);
433         qunlock(ucb);
434
435         upriv->ustats.rudpOutDatagrams++;
436
437         DPRINT("sent: %lu/%lu, %lu/%lu\n",
438                    r->sndseq, r->sndgen, r->rcvseq, r->rcvgen);
439
440         doipoput(c, f, bp, 0, c->ttl, c->tos);
441
442         if (waserror()) {
443                 relput(r);
444                 qunlock(&r->lock);
445                 nexterror();
446         }
447
448         /* flow control of sorts */
449         qlock(&r->lock);
450         if (UNACKED(r) > Maxunacked) {
451                 r->blocked = 1;
452                 rendez_sleep(&r->vous, flow, r);
453                 r->blocked = 0;
454         }
455
456         qunlock(&r->lock);
457         relput(r);
458         poperror();
459 }
460
461 void rudpiput(Proto * rudp, Ipifc * ifc, Block * bp)
462 {
463         int len, olen, ottl;
464         Udphdr *uh;
465         Conv *c;
466         Rudpcb *ucb;
467         uchar raddr[IPaddrlen], laddr[IPaddrlen];
468         ushort rport, lport;
469         Rudppriv *upriv;
470         Fs *f;
471         uchar *p;
472
473         upriv = rudp->priv;
474         f = rudp->f;
475
476         upriv->ustats.rudpInDatagrams++;
477
478         uh = (Udphdr *) (bp->rp);
479
480         /* Put back pseudo header for checksum 
481          * (remember old values for icmpnoconv()) 
482          */
483         ottl = uh->Unused;
484         uh->Unused = 0;
485         len = nhgets(uh->udplen);
486         olen = nhgets(uh->udpplen);
487         hnputs(uh->udpplen, len);
488
489         v4tov6(raddr, uh->udpsrc);
490         v4tov6(laddr, uh->udpdst);
491         lport = nhgets(uh->udpdport);
492         rport = nhgets(uh->udpsport);
493
494         if (nhgets(uh->udpcksum)) {
495                 if (ptclcsum(bp, UDP_IPHDR, len + UDP_PHDRSIZE)) {
496                         upriv->ustats.rudpInErrors++;
497                         upriv->csumerr++;
498                         netlog(f, Logrudp, "rudp: checksum error %I\n", raddr);
499                         DPRINT("rudp: checksum error %I\n", raddr);
500                         freeblist(bp);
501                         return;
502                 }
503         }
504
505         qlock(rudp);
506
507         c = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
508         if (c == nil) {
509                 /* no converstation found */
510                 upriv->ustats.rudpNoPorts++;
511                 qunlock(rudp);
512                 netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport,
513                            laddr, lport);
514                 uh->Unused = ottl;
515                 hnputs(uh->udpplen, olen);
516                 icmpnoconv(f, bp);
517                 freeblist(bp);
518                 return;
519         }
520         ucb = (Rudpcb *) c->ptcl;
521         qlock(ucb);
522         qunlock(rudp);
523
524         if (reliput(c, bp, raddr, rport) < 0) {
525                 qunlock(ucb);
526                 freeb(bp);
527                 return;
528         }
529
530         /*
531          * Trim the packet down to data size
532          */
533
534         len -= (UDP_RHDRSIZE - UDP_PHDRSIZE);
535         bp = trimblock(bp, UDP_IPHDR + UDP_RHDRSIZE, len);
536         if (bp == nil) {
537                 netlog(f, Logrudp, "rudp: len err %I.%d -> %I.%d\n",
538                            raddr, rport, laddr, lport);
539                 DPRINT("rudp: len err %I.%d -> %I.%d\n", raddr, rport, laddr, lport);
540                 upriv->lenerr++;
541                 return;
542         }
543
544         netlog(f, Logrudpmsg, "rudp: %I.%d -> %I.%d l %d\n",
545                    raddr, rport, laddr, lport, len);
546
547         switch (ucb->headers) {
548                 case 7:
549                         /* pass the src address */
550                         bp = padblock(bp, UDP_USEAD7);
551                         p = bp->rp;
552                         ipmove(p, raddr);
553                         p += IPaddrlen;
554                         ipmove(p, laddr);
555                         p += IPaddrlen;
556                         ipmove(p, ifc->lifc->local);
557                         p += IPaddrlen;
558                         hnputs(p, rport);
559                         p += 2;
560                         hnputs(p, lport);
561                         break;
562                 case 6:
563                         /* pass the src address */
564                         bp = padblock(bp, UDP_USEAD6);
565                         p = bp->rp;
566                         ipmove(p, raddr);
567                         p += IPaddrlen;
568                         ipmove(p, ipforme(f, laddr) == Runi ? laddr : ifc->lifc->local);
569                         p += IPaddrlen;
570                         hnputs(p, rport);
571                         p += 2;
572                         hnputs(p, lport);
573                         break;
574                 default:
575                         /* connection oriented rudp */
576                         if (ipcmp(c->raddr, IPnoaddr) == 0) {
577                                 /* save the src address in the conversation */
578                                 ipmove(c->raddr, raddr);
579                                 c->rport = rport;
580
581                                 /* reply with the same ip address (if not broadcast) */
582                                 if (ipforme(f, laddr) == Runi)
583                                         ipmove(c->laddr, laddr);
584                                 else
585                                         v4tov6(c->laddr, ifc->lifc->local);
586                         }
587                         break;
588         }
589         if (bp->next)
590                 bp = concatblock(bp);
591
592         if (qfull(c->rq)) {
593                 netlog(f, Logrudp, "rudp: qfull %I.%d -> %I.%d\n", raddr, rport,
594                            laddr, lport);
595                 freeblist(bp);
596         } else
597                 qpass(c->rq, bp);
598
599         qunlock(ucb);
600 }
601
602 static char *rudpunknown = "unknown rudp ctl request";
603
604 char *rudpctl(Conv * c, char **f, int n)
605 {
606         Rudpcb *ucb;
607         uchar ip[IPaddrlen];
608         int x;
609
610         ucb = (Rudpcb *) c->ptcl;
611         if (n < 1)
612                 return rudpunknown;
613
614         if (strcmp(f[0], "headers++4") == 0) {
615                 ucb->headers = 7;
616                 return nil;
617         } else if (strcmp(f[0], "headers") == 0) {
618                 ucb->headers = 6;
619                 return nil;
620         } else if (strcmp(f[0], "hangup") == 0) {
621                 if (n < 3)
622                         return "bad syntax";
623                 parseip(ip, f[1]);
624                 x = atoi(f[2]);
625                 qlock(ucb);
626                 relforget(c, ip, x, 1);
627                 qunlock(ucb);
628                 return nil;
629         } else if (strcmp(f[0], "randdrop") == 0) {
630                 x = 10; /* default is 10% */
631                 if (n > 1)
632                         x = atoi(f[1]);
633                 if (x > 100 || x < 0)
634                         return "illegal rudp drop rate";
635                 ucb->randdrop = x;
636                 return nil;
637         }
638         return rudpunknown;
639 }
640
641 void rudpadvise(Proto * rudp, Block * bp, char *msg)
642 {
643         Udphdr *h;
644         uchar source[IPaddrlen], dest[IPaddrlen];
645         ushort psource, pdest;
646         Conv *s, **p;
647
648         h = (Udphdr *) (bp->rp);
649
650         v4tov6(dest, h->udpdst);
651         v4tov6(source, h->udpsrc);
652         psource = nhgets(h->udpsport);
653         pdest = nhgets(h->udpdport);
654
655         /* Look for a connection */
656         for (p = rudp->conv; *p; p++) {
657                 s = *p;
658                 if (s->rport == pdest)
659                         if (s->lport == psource)
660                                 if (ipcmp(s->raddr, dest) == 0)
661                                         if (ipcmp(s->laddr, source) == 0) {
662                                                 qhangup(s->rq, msg);
663                                                 qhangup(s->wq, msg);
664                                                 break;
665                                         }
666         }
667         freeblist(bp);
668 }
669
670 int rudpstats(Proto * rudp, char *buf, int len)
671 {
672         Rudppriv *upriv;
673
674         upriv = rudp->priv;
675         return snprint(buf, len, "%lu %lu %lu %lu %lu %lu\n",
676                                    upriv->ustats.rudpInDatagrams,
677                                    upriv->ustats.rudpNoPorts,
678                                    upriv->ustats.rudpInErrors,
679                                    upriv->ustats.rudpOutDatagrams,
680                                    upriv->rxmits, upriv->orders);
681 }
682
683 void rudpinit(Fs * fs)
684 {
685
686         Proto *rudp;
687
688         rudp = smalloc(sizeof(Proto));
689         rudp->priv = smalloc(sizeof(Rudppriv));
690         rudp->name = "rudp";
691         rudp->connect = rudpconnect;
692         rudp->announce = rudpannounce;
693         rudp->ctl = rudpctl;
694         rudp->state = rudpstate;
695         rudp->create = rudpcreate;
696         rudp->close = rudpclose;
697         rudp->rcv = rudpiput;
698         rudp->advise = rudpadvise;
699         rudp->stats = rudpstats;
700         rudp->ipproto = IP_UDPPROTO;
701         rudp->nc = 16;
702         rudp->ptclsize = sizeof(Rudpcb);
703
704         Fsproto(fs, rudp);
705 }
706
707 /*********************************************/
708 /* Here starts the reliable helper functions */
709 /*********************************************/
710 /*
711  *  Enqueue a copy of an unacked block for possible retransmissions
712  */
713 void relackq(Reliable * r, Block * bp)
714 {
715         Block *np;
716
717         np = copyblock(bp, blocklen(bp));
718         if (r->unacked)
719                 r->unackedtail->list = np;
720         else {
721                 /* restart timer */
722                 r->timeout = 0;
723                 r->xmits = 1;
724                 r->unacked = np;
725         }
726         r->unackedtail = np;
727         np->list = nil;
728 }
729
730 /*
731  *  retransmit unacked blocks
732  */
733 void relackproc(void *a)
734 {
735         Rudpcb *ucb;
736         Proto *rudp;
737         Reliable *r;
738         Conv **s, *c;
739
740         rudp = (Proto *) a;
741
742 loop:
743         udelay_sched(Rudptickms * 1000);
744
745         for (s = rudp->conv; *s; s++) {
746                 c = *s;
747                 ucb = (Rudpcb *) c->ptcl;
748                 qlock(ucb);
749
750                 for (r = ucb->r; r; r = r->next) {
751                         if (r->unacked != nil) {
752                                 r->timeout += Rudptickms;
753                                 if (r->timeout > Rudprxms * r->xmits)
754                                         relrexmit(c, r);
755                         }
756                         if (r->acksent != r->rcvseq)
757                                 relsendack(c, r, 0);
758                 }
759                 qunlock(ucb);
760         }
761         goto loop;
762 }
763
764 /*
765  *  get the state record for a conversation
766  */
767 Reliable *relstate(Rudpcb * ucb, uchar * addr, ushort port, char *from)
768 {
769         Reliable *r, **l;
770
771         l = &ucb->r;
772         for (r = *l; r; r = *l) {
773                 if (memcmp(addr, r->addr, IPaddrlen) == 0 && port == r->port)
774                         break;
775                 l = &r->next;
776         }
777
778         /* no state for this addr/port, create some */
779         if (r == nil) {
780                 while (generation == 0)
781                         generation = rand();
782
783                 DPRINT("from %s new state %lu for %I!%u\n",
784                            from, generation, addr, port);
785
786                 r = smalloc(sizeof(Reliable));
787                 memmove(r->addr, addr, IPaddrlen);
788                 r->port = port;
789                 r->unacked = 0;
790                 if (generation == Hangupgen)
791                         generation++;
792                 r->sndgen = generation++;
793                 r->sndseq = 0;
794                 r->ackrcvd = 0;
795                 r->rcvgen = 0;
796                 r->rcvseq = 0;
797                 r->acksent = 0;
798                 r->xmits = 0;
799                 r->timeout = 0;
800                 r->ref = 0;
801                 incref(r);      /* one reference for being in the list */
802
803                 *l = r;
804         }
805
806         incref(r);
807         return r;
808 }
809
810 void relput(Reliable * r)
811 {
812         if (decref(r) == 0)
813                 free(r);
814 }
815
816 /*
817  *  forget a Reliable state
818  */
819 void relforget(Conv * c, uchar * ip, int port, int originator)
820 {
821         Rudpcb *ucb;
822         Reliable *r, **l;
823
824         ucb = (Rudpcb *) c->ptcl;
825
826         l = &ucb->r;
827         for (r = *l; r; r = *l) {
828                 if (ipcmp(ip, r->addr) == 0 && port == r->port) {
829                         *l = r->next;
830                         if (originator)
831                                 relsendack(c, r, 1);
832                         relhangup(c, r);
833                         relput(r);      /* remove from the list */
834                         break;
835                 }
836                 l = &r->next;
837         }
838 }
839
840 /* 
841  *  process a rcvd reliable packet. return -1 if not to be passed to user process,
842  *  0 therwise.
843  *
844  *  called with ucb locked.
845  */
846 int reliput(Conv * c, Block * bp, uchar * addr, ushort port)
847 {
848         Block *nbp;
849         Rudpcb *ucb;
850         Rudppriv *upriv;
851         Udphdr *uh;
852         Reliable *r;
853         Rudphdr *rh;
854         ulong seq, ack, sgen, agen, ackreal;
855         int rv = -1;
856
857         /* get fields */
858         uh = (Udphdr *) (bp->rp);
859         rh = (Rudphdr *) uh;
860         seq = nhgetl(rh->relseq);
861         sgen = nhgetl(rh->relsgen);
862         ack = nhgetl(rh->relack);
863         agen = nhgetl(rh->relagen);
864
865         upriv = c->p->priv;
866         ucb = (Rudpcb *) c->ptcl;
867         r = relstate(ucb, addr, port, "input");
868
869         DPRINT("rcvd %lu/%lu, %lu/%lu, r->sndgen = %lu\n",
870                    seq, sgen, ack, agen, r->sndgen);
871
872         /* if acking an incorrect generation, ignore */
873         if (ack && agen != r->sndgen)
874                 goto out;
875
876         /* Look for a hangup */
877         if (sgen == Hangupgen) {
878                 if (agen == r->sndgen)
879                         relforget(c, addr, port, 0);
880                 goto out;
881         }
882
883         /* make sure we're not talking to a new remote side */
884         if (r->rcvgen != sgen) {
885                 if (seq != 0 && seq != 1)
886                         goto out;
887
888                 /* new connection */
889                 if (r->rcvgen != 0) {
890                         DPRINT("new con r->rcvgen = %lu, sgen = %lu\n", r->rcvgen, sgen);
891                         relhangup(c, r);
892                 }
893                 r->rcvgen = sgen;
894         }
895
896         /* dequeue acked packets */
897         if (ack && agen == r->sndgen) {
898                 ackreal = 0;
899                 while (r->unacked != nil && INSEQ(ack, r->ackrcvd, r->sndseq)) {
900                         nbp = r->unacked;
901                         r->unacked = nbp->list;
902                         DPRINT("%lu/%lu acked, r->sndgen = %lu\n", ack, agen, r->sndgen);
903                         freeb(nbp);
904                         r->ackrcvd = NEXTSEQ(r->ackrcvd);
905                         ackreal = 1;
906                 }
907
908                 /* flow control */
909                 if (UNACKED(r) < Maxunacked / 8 && r->blocked)
910                         rendez_wakeup(&r->vous);
911
912                 /*
913                  *  retransmit next packet if the acked packet
914                  *  was transmitted more than once
915                  */
916                 if (ackreal && r->unacked != nil) {
917                         r->timeout = 0;
918                         if (r->xmits > 1) {
919                                 r->xmits = 1;
920                                 relrexmit(c, r);
921                         }
922                 }
923
924         }
925
926         /* no message or input queue full */
927         if (seq == 0 || qfull(c->rq))
928                 goto out;
929
930         /* refuse out of order delivery */
931         if (seq != NEXTSEQ(r->rcvseq)) {
932                 relsendack(c, r, 0);    /* tell him we got it already */
933                 upriv->orders++;
934                 DPRINT("out of sequence %lu not %lu\n", seq, NEXTSEQ(r->rcvseq));
935                 goto out;
936         }
937         r->rcvseq = seq;
938
939         rv = 0;
940 out:
941         relput(r);
942         return rv;
943 }
944
945 void relsendack(Conv * c, Reliable * r, int hangup)
946 {
947         Udphdr *uh;
948         Block *bp;
949         Rudphdr *rh;
950         int ptcllen;
951         Fs *f;
952
953         bp = allocb(UDP_IPHDR + UDP_RHDRSIZE);
954         if (bp == nil)
955                 return;
956         bp->wp += UDP_IPHDR + UDP_RHDRSIZE;
957         f = c->p->f;
958         uh = (Udphdr *) (bp->rp);
959         uh->vihl = IP_VER4;
960         rh = (Rudphdr *) uh;
961
962         ptcllen = (UDP_RHDRSIZE - UDP_PHDRSIZE);
963         uh->Unused = 0;
964         uh->udpproto = IP_UDPPROTO;
965         uh->frag[0] = 0;
966         uh->frag[1] = 0;
967         hnputs(uh->udpplen, ptcllen);
968
969         v6tov4(uh->udpdst, r->addr);
970         hnputs(uh->udpdport, r->port);
971         hnputs(uh->udpsport, c->lport);
972         if (ipcmp(c->laddr, IPnoaddr) == 0)
973                 findlocalip(f, c->laddr, c->raddr);
974         v6tov4(uh->udpsrc, c->laddr);
975         hnputs(uh->udplen, ptcllen);
976
977         if (hangup)
978                 hnputl(rh->relsgen, Hangupgen);
979         else
980                 hnputl(rh->relsgen, r->sndgen);
981         hnputl(rh->relseq, 0);
982         hnputl(rh->relagen, r->rcvgen);
983         hnputl(rh->relack, r->rcvseq);
984
985         if (r->acksent < r->rcvseq)
986                 r->acksent = r->rcvseq;
987
988         uh->udpcksum[0] = 0;
989         uh->udpcksum[1] = 0;
990         hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, UDP_RHDRSIZE));
991
992         DPRINT("sendack: %lu/%lu, %lu/%lu\n", 0L, r->sndgen, r->rcvseq, r->rcvgen);
993         doipoput(c, f, bp, 0, c->ttl, c->tos);
994 }
995
996 /*
997  *  called with ucb locked (and c locked if user initiated close)
998  */
999 void relhangup(Conv * c, Reliable * r)
1000 {
1001         int n;
1002         Block *bp;
1003         char hup[ERRMAX];
1004
1005         n = snprint(hup, sizeof(hup), "hangup %I!%d", r->addr, r->port);
1006         qproduce(c->eq, hup, n);
1007
1008         /*
1009          *  dump any unacked outgoing messages
1010          */
1011         for (bp = r->unacked; bp != nil; bp = r->unacked) {
1012                 r->unacked = bp->list;
1013                 bp->list = nil;
1014                 freeb(bp);
1015         }
1016
1017         r->rcvgen = 0;
1018         r->rcvseq = 0;
1019         r->acksent = 0;
1020         if (generation == Hangupgen)
1021                 generation++;
1022         r->sndgen = generation++;
1023         r->sndseq = 0;
1024         r->ackrcvd = 0;
1025         r->xmits = 0;
1026         r->timeout = 0;
1027         rendez_wakeup(&r->vous);
1028 }
1029
1030 /*
1031  *  called with ucb locked
1032  */
1033 void relrexmit(Conv * c, Reliable * r)
1034 {
1035         Rudppriv *upriv;
1036         Block *np;
1037         Fs *f;
1038
1039         upriv = c->p->priv;
1040         f = c->p->f;
1041         r->timeout = 0;
1042         if (r->xmits++ > Rudpmaxxmit) {
1043                 relhangup(c, r);
1044                 return;
1045         }
1046
1047         upriv->rxmits++;
1048         np = copyblock(r->unacked, blocklen(r->unacked));
1049         DPRINT("rxmit r->ackrvcd+1 = %lu\n", r->ackrcvd + 1);
1050         doipoput(c, f, np, 0, c->ttl, c->tos);
1051 }