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