Fixes some 32 bit usages of NOW
[akaros.git] / kern / src / net / ipv6.c
1 // INFERNO
2 #include <vfs.h>
3 #include <kfs.h>
4 #include <slab.h>
5 #include <kmalloc.h>
6 #include <kref.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <error.h>
11 #include <cpio.h>
12 #include <pmap.h>
13 #include <smp.h>
14 #include <ip.h>
15
16 #include <vfs.h>
17 #include <kfs.h>
18 #include <slab.h>
19 #include <kmalloc.h>
20 #include <kref.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <assert.h>
24 #include <error.h>
25 #include <cpio.h>
26 #include <pmap.h>
27 #include <smp.h>
28 #include <ip.h>
29
30 enum {
31         IP4HDR = 20,                            /* sizeof(Ip4hdr) */
32         IP6HDR = 40,    /* sizeof(Ip6hdr) */
33         IP_HLEN4 = 0x05,        /* Header length in words */
34         IP_DF = 0x4000, /* Don't fragment */
35         IP_MF = 0x2000, /* More fragments */
36         IP6FHDR = 8,    /* sizeof(Fraghdr6) */
37         IP_MAX = (32 * 1024),   /* Maximum Internet packet size */
38 };
39
40 #define IPV6CLASS(hdr) ((hdr->vcf[0]&0x0F)<<2 | (hdr->vcf[1]&0xF0)>>2)
41 #define BLKIPVER(xp)    (((struct ip6hdr*)((xp)->rp))->vcf[0]&0xF0)
42 #define NEXT_ID(x) (__sync_add_and_fetch(&(x), 1))
43 /*
44  * This sleazy macro is stolen shamelessly from ip.c, see comment there.
45  */
46 #define BKFG(xp)        ((struct Ipfrag*)((xp)->base))
47 struct fragment6;
48
49 struct block *ip6reassemble(struct IP *, int unused_int, struct block *,
50                                                         struct ip6hdr *);
51 void ipfragfree6(struct IP *, struct fragment6 *);
52 struct fragment6 *ipfragallo6(struct IP *);
53 static struct block *procxtns(struct IP *ip, struct block *bp, int doreasm);
54 int unfraglen(struct block *bp, uint8_t * nexthdr, int setfh);
55 struct block *procopts(struct block *bp);
56
57 /* MIB II counters */
58 enum {
59         Forwarding,
60         DefaultTTL,
61         InReceives,
62         InHdrErrors,
63         InAddrErrors,
64         ForwDatagrams,
65         InUnknownProtos,
66         InDiscards,
67         InDelivers,
68         OutRequests,
69         OutDiscards,
70         OutNoRoutes,
71         ReasmTimeout,
72         ReasmReqds,
73         ReasmOKs,
74         ReasmFails,
75         FragOKs,
76         FragFails,
77         FragCreates,
78
79         Nstats,
80 };
81
82 static char *statnames[] = {
83         [Forwarding] "Forwarding",
84         [DefaultTTL] "DefaultTTL",
85         [InReceives] "InReceives",
86         [InHdrErrors] "InHdrErrors",
87         [InAddrErrors] "InAddrErrors",
88         [ForwDatagrams] "ForwDatagrams",
89         [InUnknownProtos] "InUnknownProtos",
90         [InDiscards] "InDiscards",
91         [InDelivers] "InDelivers",
92         [OutRequests] "OutRequests",
93         [OutDiscards] "OutDiscards",
94         [OutNoRoutes] "OutNoRoutes",
95         [ReasmTimeout] "ReasmTimeout",
96         [ReasmReqds] "ReasmReqds",
97         [ReasmOKs] "ReasmOKs",
98         [ReasmFails] "ReasmFails",
99         [FragOKs] "FragOKs",
100         [FragFails] "FragFails",
101         [FragCreates] "FragCreates",
102 };
103
104 struct Fragment4 {
105         struct block *blist;
106         struct fragment4 *next;
107         uint32_t src;
108         uint32_t dst;
109         uint16_t id;
110         uint64_t age;
111 };
112
113 struct fragment6 {
114         struct block *blist;
115         struct fragment6 *next;
116         uint8_t src[IPaddrlen];
117         uint8_t dst[IPaddrlen];
118         unsigned int id;
119         uint64_t age;
120 };
121
122 struct Ipfrag {
123         uint16_t foff;
124         uint16_t flen;
125 };
126
127 /* an instance of IP */
128 struct IP {
129         uint32_t stats[Nstats];
130
131         qlock_t fraglock4;
132         struct fragment4 *flisthead4;
133         struct fragment4 *fragfree4;
134         int id4;
135
136         qlock_t fraglock6;
137         struct fragment6 *flisthead6;
138         struct fragment6 *fragfree6;
139         int id6;
140
141         int iprouting;                          /* true if we route like a gateway */
142 };
143
144 int
145 ipoput6(struct Fs *f,
146                 struct block *bp, int gating, int ttl, int tos, struct conv *c)
147 {
148         ERRSTACK(1);
149         int tentative;
150         struct Ipifc *ifc;
151         uint8_t *gate, nexthdr;
152         struct ip6hdr *eh;
153         int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff,
154                 morefrags, blklen;
155         struct route *r, *sr;
156         struct fraghdr6 fraghdr;
157         struct block *xp, *nb;
158         struct IP *ip;
159         int rv = 0;
160
161         ip = f->ip;
162
163         /* Fill out the ip header */
164         eh = (struct ip6hdr *)(bp->rp);
165
166         ip->stats[OutRequests]++;
167
168         /* Number of uint8_ts in data and ip header to write */
169         len = blocklen(bp);
170
171         tentative = iptentative(f, eh->src);
172         if (tentative) {
173                 netlog(f, Logip, "reject tx of packet with tentative src address\n");
174                 goto free;
175         }
176
177         if (gating) {
178                 chunk = nhgets(eh->ploadlen);
179                 if (chunk > len) {
180                         ip->stats[OutDiscards]++;
181                         netlog(f, Logip, "short gated packet\n");
182                         goto free;
183                 }
184                 if (chunk + IPV6HDR_LEN < len)
185                         len = chunk + IPV6HDR_LEN;
186         }
187
188         if (len >= IP_MAX) {
189 //      print("len > IP_MAX, free\n");
190                 ip->stats[OutDiscards]++;
191                 netlog(f, Logip, "exceeded ip max size %I\n", eh->dst);
192                 goto free;
193         }
194
195         r = v6lookup(f, eh->dst, c);
196         if (r == NULL) {
197 //      print("no route for %I, src %I free\n", eh->dst, eh->src);
198                 ip->stats[OutNoRoutes]++;
199                 netlog(f, Logip, "no interface %I\n", eh->dst);
200                 rv = -1;
201                 goto free;
202         }
203
204         ifc = r->rt.ifc;
205         if (r->rt.type & (Rifc | Runi))
206                 gate = eh->dst;
207         else if (r->rt.type & (Rbcast | Rmulti)) {
208                 gate = eh->dst;
209                 sr = v6lookup(f, eh->src, NULL);
210                 if (sr != NULL && (sr->rt.type & Runi))
211                         ifc = sr->rt.ifc;
212         } else
213                 gate = r->v6.gate;
214
215         if (!gating)
216                 eh->vcf[0] = IP_VER6;
217         eh->ttl = ttl;
218         if (!gating) {
219                 eh->vcf[0] |= (tos >> 4);
220                 eh->vcf[1] = (tos << 4);
221         }
222
223         if (!canrlock(&ifc->rwlock)) {
224                 goto free;
225         }
226
227         if (waserror()) {
228                 runlock(&ifc->rwlock);
229                 nexterror();
230         }
231
232         if (ifc->m == NULL) {
233                 goto raise;
234         }
235
236         /* If we dont need to fragment just send it */
237         medialen = ifc->maxtu - ifc->m->hsize;
238         if (len <= medialen) {
239                 hnputs(eh->ploadlen, len - IPV6HDR_LEN);
240                 ifc->m->bwrite(ifc, bp, V6, gate);
241                 runlock(&ifc->rwlock);
242                 poperror();
243                 return 0;
244         }
245
246         if (gating)
247                 if (ifc->reassemble <= 0) {
248
249                         /* v6 intermediate nodes are not supposed to fragment pkts;
250                            we fragment if ifc->reassemble is turned on; an exception
251                            needed for nat.
252                          */
253
254                         ip->stats[OutDiscards]++;
255                         icmppkttoobig6(f, ifc, bp);
256                         netlog(f, Logip, "%I: gated pkts not fragmented\n", eh->dst);
257                         goto raise;
258                 }
259
260         /* start v6 fragmentation */
261         uflen = unfraglen(bp, &nexthdr, 1);
262         if (uflen > medialen) {
263                 ip->stats[FragFails]++;
264                 ip->stats[OutDiscards]++;
265                 netlog(f, Logip, "%I: unfragmentable part too big\n", eh->dst);
266                 goto raise;
267         }
268
269         flen = len - uflen;
270         seglen = (medialen - (uflen + IP6FHDR)) & ~7;
271         if (seglen < 8) {
272                 ip->stats[FragFails]++;
273                 ip->stats[OutDiscards]++;
274                 netlog(f, Logip, "%I: seglen < 8\n", eh->dst);
275                 goto raise;
276         }
277
278         lid = NEXT_ID(ip->id6);
279         fraghdr.nexthdr = nexthdr;
280         fraghdr.res = 0;
281         hnputl(fraghdr.id, lid);
282
283         xp = bp;
284         offset = uflen;
285         while (xp != NULL && offset && offset >= BLEN(xp)) {
286                 offset -= BLEN(xp);
287                 xp = xp->next;
288         }
289         xp->rp += offset;
290
291         fragoff = 0;
292         morefrags = 1;
293
294         for (; fragoff < flen; fragoff += seglen) {
295                 nb = allocb(uflen + IP6FHDR + seglen);
296
297                 if (fragoff + seglen >= flen) {
298                         seglen = flen - fragoff;
299                         morefrags = 0;
300                 }
301
302                 hnputs(eh->ploadlen, seglen + IP6FHDR);
303                 memmove(nb->wp, eh, uflen);
304                 nb->wp += uflen;
305
306                 hnputs(fraghdr.offsetRM, fragoff);      // last 3 bits must be 0
307                 fraghdr.offsetRM[1] |= morefrags;
308                 memmove(nb->wp, &fraghdr, IP6FHDR);
309                 nb->wp += IP6FHDR;
310
311                 /* Copy data */
312                 chunk = seglen;
313                 while (chunk) {
314                         if (!xp) {
315                                 ip->stats[OutDiscards]++;
316                                 ip->stats[FragFails]++;
317                                 freeblist(nb);
318                                 netlog(f, Logip, "!xp: chunk in v6%d\n", chunk);
319                                 goto raise;
320                         }
321                         blklen = chunk;
322                         if (BLEN(xp) < chunk)
323                                 blklen = BLEN(xp);
324                         memmove(nb->wp, xp->rp, blklen);
325
326                         nb->wp += blklen;
327                         xp->rp += blklen;
328                         chunk -= blklen;
329                         if (xp->rp == xp->wp)
330                                 xp = xp->next;
331                 }
332
333                 ifc->m->bwrite(ifc, nb, V6, gate);
334                 ip->stats[FragCreates]++;
335         }
336         ip->stats[FragOKs]++;
337
338 raise:
339         runlock(&ifc->rwlock);
340         poperror();
341 free:
342         freeblist(bp);
343         return rv;
344 }
345
346 void ipiput6(struct Fs *f, struct Ipifc *ifc, struct block *bp)
347 {
348         int hl;
349         int hop, tos;
350         uint8_t proto;
351         struct ip6hdr *h;
352         struct Proto *p;
353         int notforme;
354         int tentative;
355         uint8_t v6dst[IPaddrlen];
356         struct IP *ip;
357         struct route *r, *sr;
358
359         ip = f->ip;
360         ip->stats[InReceives]++;
361
362         /*
363          *  Ensure we have all the header info in the first
364          *  block.  Make life easier for other protocols by
365          *  collecting up to the first 64 bytes in the first block.
366          */
367         if (BLEN(bp) < 64) {
368                 hl = blocklen(bp);
369                 if (hl < IP6HDR)
370                         hl = IP6HDR;
371                 if (hl > 64)
372                         hl = 64;
373                 bp = pullupblock(bp, hl);
374                 if (bp == NULL)
375                         return;
376         }
377
378         h = (struct ip6hdr *)(bp->rp);
379
380         memmove(&v6dst[0], &(h->dst)[0], IPaddrlen);
381         notforme = ipforme(f, v6dst) == 0;
382         tentative = iptentative(f, v6dst);
383
384         if (tentative && (h->proto != ICMPv6)) {
385                 printd("tentative addr, drop\n");
386                 freeblist(bp);
387                 return;
388         }
389
390         /* Check header version */
391         if (BLKIPVER(bp) != IP_VER6) {
392                 ip->stats[InHdrErrors]++;
393                 netlog(f, Logip, "ip: bad version 0x%x\n", (h->vcf[0] & 0xF0) >> 2);
394                 freeblist(bp);
395                 return;
396         }
397
398         /* route */
399         if (notforme) {
400                 if (!ip->iprouting) {
401                         freeb(bp);
402                         return;
403                 }
404                 /* don't forward to source's network */
405                 sr = v6lookup(f, h->src, NULL);
406                 r = v6lookup(f, h->dst, NULL);
407
408                 if (r == NULL || sr == r) {
409                         ip->stats[OutDiscards]++;
410                         freeblist(bp);
411                         return;
412                 }
413
414                 /* don't forward if packet has timed out */
415                 hop = h->ttl;
416                 if (hop < 1) {
417                         ip->stats[InHdrErrors]++;
418                         icmpttlexceeded6(f, ifc, bp);
419                         freeblist(bp);
420                         return;
421                 }
422
423                 /* process headers & reassemble if the interface expects it */
424                 bp = procxtns(ip, bp, r->rt.ifc->reassemble);
425
426                 if (bp == NULL)
427                         return;
428
429                 ip->stats[ForwDatagrams]++;
430                 h = (struct ip6hdr *)(bp->rp);
431                 tos = IPV6CLASS(h);
432                 hop = h->ttl;
433                 ipoput6(f, bp, 1, hop - 1, tos, NULL);
434                 return;
435         }
436
437         /* reassemble & process headers if needed */
438         bp = procxtns(ip, bp, 1);
439
440         if (bp == NULL)
441                 return;
442
443         h = (struct ip6hdr *)(bp->rp);
444         proto = h->proto;
445         p = Fsrcvpcol(f, proto);
446         if (p != NULL && p->rcv != NULL) {
447                 ip->stats[InDelivers]++;
448                 (*p->rcv) (p, ifc, bp);
449                 return;
450         }
451
452         ip->stats[InDiscards]++;
453         ip->stats[InUnknownProtos]++;
454         freeblist(bp);
455 }
456
457 /*
458  * ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6
459  */
460 void ipfragfree6(struct IP *ip, struct fragment6 *frag)
461 {
462         struct fragment6 *fl, **l;
463
464         if (frag->blist)
465                 freeblist(frag->blist);
466
467         memset(frag->src, 0, IPaddrlen);
468         frag->id = 0;
469         frag->blist = NULL;
470
471         l = &ip->flisthead6;
472         for (fl = *l; fl; fl = fl->next) {
473                 if (fl == frag) {
474                         *l = frag->next;
475                         break;
476                 }
477                 l = &fl->next;
478         }
479
480         frag->next = ip->fragfree6;
481         ip->fragfree6 = frag;
482
483 }
484
485 /*
486  * ipfragallo6 - copied from ipfragalloc4
487  */
488 struct fragment6 *ipfragallo6(struct IP *ip)
489 {
490         struct fragment6 *f;
491
492         while (ip->fragfree6 == NULL) {
493                 /* free last entry on fraglist */
494                 for (f = ip->flisthead6; f->next; f = f->next) ;
495                 ipfragfree6(ip, f);
496         }
497         f = ip->fragfree6;
498         ip->fragfree6 = f->next;
499         f->next = ip->flisthead6;
500         ip->flisthead6 = f;
501         f->age = NOW + 30000;
502
503         return f;
504 }
505
506 static struct block *procxtns(struct IP *ip, struct block *bp, int doreasm)
507 {
508
509         int offset;
510         uint8_t proto;
511         struct ip6hdr *h;
512
513         h = (struct ip6hdr *)(bp->rp);
514         offset = unfraglen(bp, &proto, 0);
515
516         if ((proto == FH) && (doreasm != 0)) {
517                 bp = ip6reassemble(ip, offset, bp, h);
518                 if (bp == NULL)
519                         return NULL;
520                 offset = unfraglen(bp, &proto, 0);
521         }
522
523         if (proto == DOH || offset > IP6HDR)
524                 bp = procopts(bp);
525
526         return bp;
527 }
528
529 /*      returns length of "Unfragmentable part", i.e., sum of lengths of ipv6 hdr,
530  *      hop-by-hop & routing headers if present; *nexthdr is set to nexthdr value
531  *      of the last header in the "Unfragmentable part"; if setfh != 0, nexthdr
532  *      field of the last header in the "Unfragmentable part" is set to FH.
533  */
534 int unfraglen(struct block *bp, uint8_t * nexthdr, int setfh)
535 {
536         uint8_t *p, *q;
537         int ufl, hs;
538
539         p = bp->rp;
540         q = p + 6;      /* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */
541         *nexthdr = *q;
542         ufl = IP6HDR;
543         p += ufl;
544
545         for (;;) {
546                 if (*nexthdr == HBH || *nexthdr == RH) {
547                         *nexthdr = *p;
548                         hs = ((int)*(p + 1) + 1) * 8;
549                         ufl += hs;
550                         q = p;
551                         p += hs;
552                 } else
553                         break;
554         }
555
556         if (*nexthdr == FH)
557                 *q = *p;
558
559         if (setfh)
560                 *q = FH;
561
562         return ufl;
563 }
564
565 struct block *procopts(struct block *bp)
566 {
567         return bp;
568 }
569
570 struct block *ip6reassemble(struct IP *ip, int uflen, struct block *bp,
571                                                         struct ip6hdr *ih)
572 {
573
574         int fend, offset;
575         unsigned int id;
576         struct fragment6 *f, *fnext;
577         struct fraghdr6 *fraghdr;
578         uint8_t src[IPaddrlen], dst[IPaddrlen];
579         struct block *bl, **l, *last, *prev;
580         int ovlap, len, fragsize, pktposn;
581
582         fraghdr = (struct fraghdr6 *)(bp->rp + uflen);
583         memmove(src, ih->src, IPaddrlen);
584         memmove(dst, ih->dst, IPaddrlen);
585         id = nhgetl(fraghdr->id);
586         offset = nhgets(fraghdr->offsetRM) & ~7;
587
588         /*
589          *  block lists are too hard, pullupblock into a single block
590          */
591         if (bp->next) {
592                 bp = pullupblock(bp, blocklen(bp));
593                 ih = (struct ip6hdr *)(bp->rp);
594         }
595
596         qlock(&ip->fraglock6);
597
598         /*
599          *  find a reassembly queue for this fragment
600          */
601         for (f = ip->flisthead6; f; f = fnext) {
602                 fnext = f->next;
603                 if (ipcmp(f->src, src) == 0 && ipcmp(f->dst, dst) == 0 && f->id == id)
604                         break;
605                 if (f->age < NOW) {
606                         ip->stats[ReasmTimeout]++;
607                         ipfragfree6(ip, f);
608                 }
609         }
610
611         /*
612          *  if this isn't a fragmented packet, accept it
613          *  and get rid of any fragments that might go
614          *  with it.
615          */
616         if (nhgets(fraghdr->offsetRM) == 0) {   // first frag is also the last
617                 if (f != NULL) {
618                         ipfragfree6(ip, f);
619                         ip->stats[ReasmFails]++;
620                 }
621                 qunlock(&ip->fraglock6);
622                 return bp;
623         }
624
625         if (bp->base + sizeof(struct Ipfrag) >= bp->rp) {
626                 bp = padblock(bp, sizeof(struct Ipfrag));
627                 bp->rp += sizeof(struct Ipfrag);
628         }
629
630         BKFG(bp)->foff = offset;
631         BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR;
632
633         /* First fragment allocates a reassembly queue */
634         if (f == NULL) {
635                 f = ipfragallo6(ip);
636                 f->id = id;
637                 memmove(f->src, src, IPaddrlen);
638                 memmove(f->dst, dst, IPaddrlen);
639
640                 f->blist = bp;
641
642                 qunlock(&ip->fraglock6);
643                 ip->stats[ReasmReqds]++;
644                 return NULL;
645         }
646
647         /*
648          *  find the new fragment's position in the queue
649          */
650         prev = NULL;
651         l = &f->blist;
652         bl = f->blist;
653         while (bl != NULL && BKFG(bp)->foff > BKFG(bl)->foff) {
654                 prev = bl;
655                 l = &bl->next;
656                 bl = bl->next;
657         }
658
659         /* Check overlap of a previous fragment - trim away as necessary */
660         if (prev) {
661                 ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
662                 if (ovlap > 0) {
663                         if (ovlap >= BKFG(bp)->flen) {
664                                 freeblist(bp);
665                                 qunlock(&ip->fraglock6);
666                                 return NULL;
667                         }
668                         BKFG(prev)->flen -= ovlap;
669                 }
670         }
671
672         /* Link onto assembly queue */
673         bp->next = *l;
674         *l = bp;
675
676         /* Check to see if succeeding segments overlap */
677         if (bp->next) {
678                 l = &bp->next;
679                 fend = BKFG(bp)->foff + BKFG(bp)->flen;
680
681                 /* Take completely covered segments out */
682
683                 while (*l) {
684                         ovlap = fend - BKFG(*l)->foff;
685
686                         if (ovlap <= 0)
687                                 break;
688                         if (ovlap < BKFG(*l)->flen) {
689                                 BKFG(*l)->flen -= ovlap;
690                                 BKFG(*l)->foff += ovlap;
691                                 /* move up ih hdrs */
692                                 memmove((*l)->rp + ovlap, (*l)->rp, uflen);
693                                 (*l)->rp += ovlap;
694                                 break;
695                         }
696                         last = (*l)->next;
697                         (*l)->next = NULL;
698                         freeblist(*l);
699                         *l = last;
700                 }
701         }
702
703         /*
704          *  look for a complete packet.  if we get to a fragment
705          *  with the trailing bit of fraghdr->offsetRM[1] set, we're done.
706          */
707         pktposn = 0;
708         for (bl = f->blist; bl; bl = bl->next) {
709                 if (BKFG(bl)->foff != pktposn)
710                         break;
711
712                 fraghdr = (struct fraghdr6 *)(bl->rp + uflen);
713                 if ((fraghdr->offsetRM[1] & 1) == 0) {
714                         bl = f->blist;
715
716                         /* get rid of frag header in first fragment */
717
718                         memmove(bl->rp + IP6FHDR, bl->rp, uflen);
719                         bl->rp += IP6FHDR;
720                         len = nhgets(((struct ip6hdr *)(bl->rp))->ploadlen) - IP6FHDR;
721                         bl->wp = bl->rp + len + IP6HDR;
722
723                         /* Pullup all the fragment headers and
724                          * return a complete packet
725                          */
726                         for (bl = bl->next; bl; bl = bl->next) {
727                                 fragsize = BKFG(bl)->flen;
728                                 len += fragsize;
729                                 bl->rp += uflen + IP6FHDR;
730                                 bl->wp = bl->rp + fragsize;
731                         }
732
733                         bl = f->blist;
734                         f->blist = NULL;
735                         ipfragfree6(ip, f);
736                         ih = (struct ip6hdr *)(bl->rp);
737                         hnputs(ih->ploadlen, len);
738                         qunlock(&ip->fraglock6);
739                         ip->stats[ReasmOKs]++;
740                         return bl;
741                 }
742                 pktposn += BKFG(bl)->flen;
743         }
744         qunlock(&ip->fraglock6);
745         return NULL;
746 }