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