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