]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/ip.c
kernel: dont use atomic increment for Proc.nlocks, maintain Lock.m for lock(), use...
[plan9front.git] / sys / src / 9 / ip / ip.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "../port/error.h"
7
8 #include        "ip.h"
9
10 #define BLKIPVER(xp)    (((Ip4hdr*)((xp)->rp))->vihl&0xF0)
11
12 static char *statnames[] =
13 {
14 [Forwarding]    "Forwarding",
15 [DefaultTTL]    "DefaultTTL",
16 [InReceives]    "InReceives",
17 [InHdrErrors]   "InHdrErrors",
18 [InAddrErrors]  "InAddrErrors",
19 [ForwDatagrams] "ForwDatagrams",
20 [InUnknownProtos]       "InUnknownProtos",
21 [InDiscards]    "InDiscards",
22 [InDelivers]    "InDelivers",
23 [OutRequests]   "OutRequests",
24 [OutDiscards]   "OutDiscards",
25 [OutNoRoutes]   "OutNoRoutes",
26 [ReasmTimeout]  "ReasmTimeout",
27 [ReasmReqds]    "ReasmReqds",
28 [ReasmOKs]      "ReasmOKs",
29 [ReasmFails]    "ReasmFails",
30 [FragOKs]       "FragOKs",
31 [FragFails]     "FragFails",
32 [FragCreates]   "FragCreates",
33 };
34
35 #define BLKIP(xp)       ((Ip4hdr*)((xp)->rp))
36 /*
37  * This sleazy macro relies on the media header size being
38  * larger than sizeof(Ipfrag). ipreassemble checks this is true
39  */
40 #define BKFG(xp)        ((Ipfrag*)((xp)->base))
41
42 ushort          ipcsum(uchar*);
43 Block*          ip4reassemble(IP*, int, Block*, Ip4hdr*);
44 void            ipfragfree4(IP*, Fragment4*);
45 Fragment4*      ipfragallo4(IP*);
46
47 void
48 ip_init_6(Fs *f)
49 {
50         v6params *v6p;
51
52         v6p = smalloc(sizeof(v6params));
53
54         v6p->rp.mflag           = 0;            /* default not managed */
55         v6p->rp.oflag           = 0;
56         v6p->rp.maxraint        = 600000;       /* millisecs */
57         v6p->rp.minraint        = 200000;
58         v6p->rp.linkmtu         = 0;            /* no mtu sent */
59         v6p->rp.reachtime       = 0;
60         v6p->rp.rxmitra         = 0;
61         v6p->rp.ttl             = MAXTTL;
62         v6p->rp.routerlt        = 3 * v6p->rp.maxraint;
63
64         v6p->hp.rxmithost       = 1000;         /* v6 RETRANS_TIMER */
65
66         v6p->cdrouter           = -1;
67
68         f->v6p                  = v6p;
69 }
70
71 void
72 initfrag(IP *ip, int size)
73 {
74         Fragment4 *fq4, *eq4;
75         Fragment6 *fq6, *eq6;
76
77         ip->fragfree4 = (Fragment4*)malloc(sizeof(Fragment4) * size);
78         if(ip->fragfree4 == nil)
79                 panic("initfrag");
80
81         eq4 = &ip->fragfree4[size];
82         for(fq4 = ip->fragfree4; fq4 < eq4; fq4++)
83                 fq4->next = fq4+1;
84
85         ip->fragfree4[size-1].next = nil;
86
87         ip->fragfree6 = (Fragment6*)malloc(sizeof(Fragment6) * size);
88         if(ip->fragfree6 == nil)
89                 panic("initfrag");
90
91         eq6 = &ip->fragfree6[size];
92         for(fq6 = ip->fragfree6; fq6 < eq6; fq6++)
93                 fq6->next = fq6+1;
94
95         ip->fragfree6[size-1].next = nil;
96 }
97
98 void
99 ip_init(Fs *f)
100 {
101         IP *ip;
102
103         ip = smalloc(sizeof(IP));
104         initfrag(ip, 100);
105         f->ip = ip;
106
107         ip_init_6(f);
108 }
109
110 void
111 iprouting(Fs *f, int on)
112 {
113         f->ip->iprouting = on;
114         if(f->ip->iprouting==0)
115                 f->ip->stats[Forwarding] = 2;
116         else
117                 f->ip->stats[Forwarding] = 1;
118 }
119
120 int
121 ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
122 {
123         Ipifc *ifc;
124         uchar *gate;
125         ulong fragoff;
126         Block *xp, *nb;
127         Ip4hdr *eh, *feh;
128         int lid, len, seglen, chunk, dlen, blklen, offset, medialen;
129         Route *r, *sr;
130         IP *ip;
131         int rv = 0;
132
133         ip = f->ip;
134
135         /* Fill out the ip header */
136         eh = (Ip4hdr*)(bp->rp);
137
138         ip->stats[OutRequests]++;
139
140         /* Number of uchars in data and ip header to write */
141         len = blocklen(bp);
142
143         if(gating){
144                 chunk = nhgets(eh->length);
145                 if(chunk > len){
146                         ip->stats[OutDiscards]++;
147                         netlog(f, Logip, "short gated packet\n");
148                         goto free;
149                 }
150                 if(chunk < len)
151                         len = chunk;
152         }
153         if(len >= IP_MAX){
154                 ip->stats[OutDiscards]++;
155                 netlog(f, Logip, "exceeded ip max size %V\n", eh->dst);
156                 goto free;
157         }
158
159         r = v4lookup(f, eh->dst, c);
160         if(r == nil){
161                 ip->stats[OutNoRoutes]++;
162                 netlog(f, Logip, "no interface %V\n", eh->dst);
163                 rv = -1;
164                 goto free;
165         }
166
167         ifc = r->ifc;
168         if(r->type & (Rifc|Runi))
169                 gate = eh->dst;
170         else
171         if(r->type & (Rbcast|Rmulti)) {
172                 gate = eh->dst;
173                 sr = v4lookup(f, eh->src, nil);
174                 if(sr != nil && (sr->type & Runi))
175                         ifc = sr->ifc;
176         }
177         else
178                 gate = r->v4.gate;
179
180         if(!gating)
181                 eh->vihl = IP_VER4|IP_HLEN4;
182         eh->ttl = ttl;
183         if(!gating)
184                 eh->tos = tos;
185
186         if(!canrlock(ifc))
187                 goto free;
188         if(waserror()){
189                 runlock(ifc);
190                 nexterror();
191         }
192         if(ifc->m == nil)
193                 goto raise;
194
195         /* If we dont need to fragment just send it */
196         if(c && c->maxfragsize && c->maxfragsize < ifc->maxtu)
197                 medialen = c->maxfragsize - ifc->m->hsize;
198         else
199                 medialen = ifc->maxtu - ifc->m->hsize;
200         if(len <= medialen) {
201                 if(!gating)
202                         hnputs(eh->id, incref(&ip->id4));
203                 hnputs(eh->length, len);
204                 if(!gating){
205                         eh->frag[0] = 0;
206                         eh->frag[1] = 0;
207                 }
208                 eh->cksum[0] = 0;
209                 eh->cksum[1] = 0;
210                 hnputs(eh->cksum, ipcsum(&eh->vihl));
211                 ifc->m->bwrite(ifc, bp, V4, gate);
212                 runlock(ifc);
213                 poperror();
214                 return 0;
215         }
216
217 if((eh->frag[0] & (IP_DF>>8)) && !gating) print("%V: DF set\n", eh->dst);
218
219         if(eh->frag[0] & (IP_DF>>8)){
220                 ip->stats[FragFails]++;
221                 ip->stats[OutDiscards]++;
222                 icmpcantfrag(f, bp, medialen);
223                 netlog(f, Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst);
224                 goto raise;
225         }
226
227         seglen = (medialen - IP4HDR) & ~7;
228         if(seglen < 8){
229                 ip->stats[FragFails]++;
230                 ip->stats[OutDiscards]++;
231                 netlog(f, Logip, "%V seglen < 8\n", eh->dst);
232                 goto raise;
233         }
234
235         dlen = len - IP4HDR;
236         xp = bp;
237         if(gating)
238                 lid = nhgets(eh->id);
239         else
240                 lid = incref(&ip->id4);
241
242         offset = IP4HDR;
243         while(xp != nil && offset && offset >= BLEN(xp)) {
244                 offset -= BLEN(xp);
245                 xp = xp->next;
246         }
247         xp->rp += offset;
248
249         if(gating)
250                 fragoff = nhgets(eh->frag)<<3;
251         else
252                 fragoff = 0;
253         dlen += fragoff;
254         for(; fragoff < dlen; fragoff += seglen) {
255                 nb = allocb(IP4HDR+seglen);
256                 feh = (Ip4hdr*)(nb->rp);
257
258                 memmove(nb->wp, eh, IP4HDR);
259                 nb->wp += IP4HDR;
260
261                 if((fragoff + seglen) >= dlen) {
262                         seglen = dlen - fragoff;
263                         hnputs(feh->frag, fragoff>>3);
264                 }
265                 else
266                         hnputs(feh->frag, (fragoff>>3)|IP_MF);
267
268                 hnputs(feh->length, seglen + IP4HDR);
269                 hnputs(feh->id, lid);
270
271                 /* Copy up the data area */
272                 chunk = seglen;
273                 while(chunk) {
274                         if(!xp) {
275                                 ip->stats[OutDiscards]++;
276                                 ip->stats[FragFails]++;
277                                 freeblist(nb);
278                                 netlog(f, Logip, "!xp: chunk %d\n", chunk);
279                                 goto raise;
280                         }
281                         blklen = chunk;
282                         if(BLEN(xp) < chunk)
283                                 blklen = BLEN(xp);
284                         memmove(nb->wp, xp->rp, blklen);
285                         nb->wp += blklen;
286                         xp->rp += blklen;
287                         chunk -= blklen;
288                         if(xp->rp == xp->wp)
289                                 xp = xp->next;
290                 }
291
292                 feh->cksum[0] = 0;
293                 feh->cksum[1] = 0;
294                 hnputs(feh->cksum, ipcsum(&feh->vihl));
295                 ifc->m->bwrite(ifc, nb, V4, gate);
296                 ip->stats[FragCreates]++;
297         }
298         ip->stats[FragOKs]++;
299 raise:
300         runlock(ifc);
301         poperror();
302 free:
303         freeblist(bp);
304         return rv;
305 }
306
307 void
308 ipiput4(Fs *f, Ipifc *ifc, Block *bp)
309 {
310         int hl;
311         int hop, tos, proto, olen;
312         Ip4hdr *h;
313         Proto *p;
314         ushort frag;
315         int notforme;
316         uchar *dp, v6dst[IPaddrlen];
317         IP *ip;
318         Route *r;
319         Conv conv;
320
321         if(BLKIPVER(bp) != IP_VER4) {
322                 ipiput6(f, ifc, bp);
323                 return;
324         }
325
326         ip = f->ip;
327         ip->stats[InReceives]++;
328
329         /*
330          *  Ensure we have all the header info in the first
331          *  block.  Make life easier for other protocols by
332          *  collecting up to the first 64 bytes in the first block.
333          */
334         if(BLEN(bp) < 64) {
335                 hl = blocklen(bp);
336                 if(hl < IP4HDR)
337                         hl = IP4HDR;
338                 if(hl > 64)
339                         hl = 64;
340                 bp = pullupblock(bp, hl);
341                 if(bp == nil)
342                         return;
343         }
344
345         h = (Ip4hdr*)(bp->rp);
346
347         /* dump anything that whose header doesn't checksum */
348         if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {
349                 ip->stats[InHdrErrors]++;
350                 netlog(f, Logip, "ip: checksum error %V\n", h->src);
351                 freeblist(bp);
352                 return;
353         }
354         v4tov6(v6dst, h->dst);
355         notforme = ipforme(f, v6dst) == 0;
356
357         /* Check header length and version */
358         if((h->vihl&0x0F) != IP_HLEN4) {
359                 hl = (h->vihl&0xF)<<2;
360                 if(hl < (IP_HLEN4<<2)) {
361                         ip->stats[InHdrErrors]++;
362                         netlog(f, Logip, "ip: %V bad hivl %ux\n", h->src, h->vihl);
363                         freeblist(bp);
364                         return;
365                 }
366                 /* If this is not routed strip off the options */
367                 if(notforme == 0) {
368                         olen = nhgets(h->length);
369                         dp = bp->rp + (hl - (IP_HLEN4<<2));
370                         memmove(dp, h, IP_HLEN4<<2);
371                         bp->rp = dp;
372                         h = (Ip4hdr*)(bp->rp);
373                         h->vihl = (IP_VER4|IP_HLEN4);
374                         hnputs(h->length, olen-hl+(IP_HLEN4<<2));
375                 }
376         }
377
378         /* route */
379         if(notforme) {
380                 if(!ip->iprouting){
381                         freeblist(bp);
382                         return;
383                 }
384
385                 /* don't forward to source's network */
386                 memmove(&conv, ifc->conv, sizeof conv);
387                 conv.r = nil;
388                 r = v4lookup(f, h->dst, &conv);
389                 if(r == nil || r->ifc == ifc){
390                         ip->stats[OutDiscards]++;
391                         freeblist(bp);
392                         return;
393                 }
394
395                 /* don't forward if packet has timed out */
396                 hop = h->ttl;
397                 if(hop < 1) {
398                         ip->stats[InHdrErrors]++;
399                         icmpttlexceeded(f, ifc->lifc->local, bp);
400                         freeblist(bp);
401                         return;
402                 }
403
404                 /* reassemble if the interface expects it */
405 if(r->ifc == nil) panic("nil route rfc");
406                 if(r->ifc->reassemble){
407                         frag = nhgets(h->frag);
408                         if(frag) {
409                                 h->tos = 0;
410                                 if(frag & IP_MF)
411                                         h->tos = 1;
412                                 bp = ip4reassemble(ip, frag, bp, h);
413                                 if(bp == nil)
414                                         return;
415                                 h = (Ip4hdr*)(bp->rp);
416                         }
417                 }
418
419                 ip->stats[ForwDatagrams]++;
420                 tos = h->tos;
421                 hop = h->ttl;
422                 ipoput4(f, bp, 1, hop - 1, tos, &conv);
423                 return;
424         }
425
426         frag = nhgets(h->frag);
427         if(frag) {
428                 h->tos = 0;
429                 if(frag & IP_MF)
430                         h->tos = 1;
431                 bp = ip4reassemble(ip, frag, bp, h);
432                 if(bp == nil)
433                         return;
434                 h = (Ip4hdr*)(bp->rp);
435         }
436
437         /* don't let any frag info go up the stack */
438         h->frag[0] = 0;
439         h->frag[1] = 0;
440
441         proto = h->proto;
442         p = Fsrcvpcol(f, proto);
443         if(p != nil && p->rcv != nil) {
444                 ip->stats[InDelivers]++;
445                 (*p->rcv)(p, ifc, bp);
446                 return;
447         }
448         ip->stats[InDiscards]++;
449         ip->stats[InUnknownProtos]++;
450         freeblist(bp);
451 }
452
453 int
454 ipstats(Fs *f, char *buf, int len)
455 {
456         IP *ip;
457         char *p, *e;
458         int i;
459
460         ip = f->ip;
461         ip->stats[DefaultTTL] = MAXTTL;
462
463         p = buf;
464         e = p+len;
465         for(i = 0; i < Nipstats; i++)
466                 p = seprint(p, e, "%s: %llud\n", statnames[i], ip->stats[i]);
467         return p - buf;
468 }
469
470 Block*
471 ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
472 {
473         int fend;
474         ushort id;
475         Fragment4 *f, *fnext;
476         ulong src, dst;
477         Block *bl, **l, *last, *prev;
478         int ovlap, len, fragsize, pktposn;
479
480         src = nhgetl(ih->src);
481         dst = nhgetl(ih->dst);
482         id = nhgets(ih->id);
483
484         /*
485          *  block lists are too hard, pullupblock into a single block
486          */
487         if(bp->next){
488                 bp = pullupblock(bp, blocklen(bp));
489                 ih = (Ip4hdr*)(bp->rp);
490         }
491
492         qlock(&ip->fraglock4);
493
494         /*
495          *  find a reassembly queue for this fragment
496          */
497         for(f = ip->flisthead4; f; f = fnext){
498                 fnext = f->next;        /* because ipfragfree4 changes the list */
499                 if(f->src == src && f->dst == dst && f->id == id)
500                         break;
501                 if(f->age < NOW){
502                         ip->stats[ReasmTimeout]++;
503                         ipfragfree4(ip, f);
504                 }
505         }
506
507         /*
508          *  if this isn't a fragmented packet, accept it
509          *  and get rid of any fragments that might go
510          *  with it.
511          */
512         if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
513                 if(f != nil) {
514                         ipfragfree4(ip, f);
515                         ip->stats[ReasmFails]++;
516                 }
517                 qunlock(&ip->fraglock4);
518                 return bp;
519         }
520
521         if(bp->base+IPFRAGSZ >= bp->rp){
522                 bp = padblock(bp, IPFRAGSZ);
523                 bp->rp += IPFRAGSZ;
524         }
525
526         BKFG(bp)->foff = offset<<3;
527         BKFG(bp)->flen = nhgets(ih->length)-IP4HDR;
528
529         /* First fragment allocates a reassembly queue */
530         if(f == nil) {
531                 f = ipfragallo4(ip);
532                 f->id = id;
533                 f->src = src;
534                 f->dst = dst;
535
536                 f->blist = bp;
537
538                 qunlock(&ip->fraglock4);
539                 ip->stats[ReasmReqds]++;
540                 return nil;
541         }
542
543         /*
544          *  find the new fragment's position in the queue
545          */
546         prev = nil;
547         l = &f->blist;
548         bl = f->blist;
549         while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
550                 prev = bl;
551                 l = &bl->next;
552                 bl = bl->next;
553         }
554
555         /* Check overlap of a previous fragment - trim away as necessary */
556         if(prev) {
557                 ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
558                 if(ovlap > 0) {
559                         if(ovlap >= BKFG(bp)->flen) {
560                                 freeblist(bp);
561                                 qunlock(&ip->fraglock4);
562                                 return nil;
563                         }
564                         BKFG(prev)->flen -= ovlap;
565                 }
566         }
567
568         /* Link onto assembly queue */
569         bp->next = *l;
570         *l = bp;
571
572         /* Check to see if succeeding segments overlap */
573         if(bp->next) {
574                 l = &bp->next;
575                 fend = BKFG(bp)->foff + BKFG(bp)->flen;
576                 /* Take completely covered segments out */
577                 while(*l) {
578                         ovlap = fend - BKFG(*l)->foff;
579                         if(ovlap <= 0)
580                                 break;
581                         if(ovlap < BKFG(*l)->flen) {
582                                 BKFG(*l)->flen -= ovlap;
583                                 BKFG(*l)->foff += ovlap;
584                                 /* move up ih hdrs */
585                                 memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR);
586                                 (*l)->rp += ovlap;
587                                 break;
588                         }
589                         last = (*l)->next;
590                         (*l)->next = nil;
591                         freeblist(*l);
592                         *l = last;
593                 }
594         }
595
596         /*
597          *  look for a complete packet.  if we get to a fragment
598          *  without IP_MF set, we're done.
599          */
600         pktposn = 0;
601         for(bl = f->blist; bl; bl = bl->next) {
602                 if(BKFG(bl)->foff != pktposn)
603                         break;
604                 if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) {
605                         bl = f->blist;
606                         len = nhgets(BLKIP(bl)->length);
607                         bl->wp = bl->rp + len;
608
609                         /* Pullup all the fragment headers and
610                          * return a complete packet
611                          */
612                         for(bl = bl->next; bl; bl = bl->next) {
613                                 fragsize = BKFG(bl)->flen;
614                                 len += fragsize;
615                                 bl->rp += IP4HDR;
616                                 bl->wp = bl->rp + fragsize;
617                         }
618
619                         bl = f->blist;
620                         f->blist = nil;
621                         ipfragfree4(ip, f);
622                         ih = BLKIP(bl);
623                         hnputs(ih->length, len);
624                         qunlock(&ip->fraglock4);
625                         ip->stats[ReasmOKs]++;
626                         return bl;
627                 }
628                 pktposn += BKFG(bl)->flen;
629         }
630         qunlock(&ip->fraglock4);
631         return nil;
632 }
633
634 /*
635  * ipfragfree4 - Free a list of fragments - assume hold fraglock4
636  */
637 void
638 ipfragfree4(IP *ip, Fragment4 *frag)
639 {
640         Fragment4 *fl, **l;
641
642         if(frag->blist)
643                 freeblist(frag->blist);
644
645         frag->src = 0;
646         frag->id = 0;
647         frag->blist = nil;
648
649         l = &ip->flisthead4;
650         for(fl = *l; fl; fl = fl->next) {
651                 if(fl == frag) {
652                         *l = frag->next;
653                         break;
654                 }
655                 l = &fl->next;
656         }
657
658         frag->next = ip->fragfree4;
659         ip->fragfree4 = frag;
660
661 }
662
663 /*
664  * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
665  */
666 Fragment4 *
667 ipfragallo4(IP *ip)
668 {
669         Fragment4 *f;
670
671         while(ip->fragfree4 == nil) {
672                 /* free last entry on fraglist */
673                 for(f = ip->flisthead4; f->next; f = f->next)
674                         ;
675                 ipfragfree4(ip, f);
676         }
677         f = ip->fragfree4;
678         ip->fragfree4 = f->next;
679         f->next = ip->flisthead4;
680         ip->flisthead4 = f;
681         f->age = NOW + 30000;
682
683         return f;
684 }
685
686 ushort
687 ipcsum(uchar *addr)
688 {
689         int len;
690         ulong sum;
691
692         sum = 0;
693         len = (addr[0]&0xf)<<2;
694
695         while(len > 0) {
696                 sum += addr[0]<<8 | addr[1] ;
697                 len -= 2;
698                 addr += 2;
699         }
700
701         sum = (sum & 0xffff) + (sum >> 16);
702         sum = (sum & 0xffff) + (sum >> 16);
703
704         return (sum^0xffff);
705 }