17dd7cddfSDavid du Colombier #include "u.h"
27dd7cddfSDavid du Colombier #include "../port/lib.h"
37dd7cddfSDavid du Colombier #include "mem.h"
47dd7cddfSDavid du Colombier #include "dat.h"
57dd7cddfSDavid du Colombier #include "fns.h"
67dd7cddfSDavid du Colombier #include "../port/error.h"
77dd7cddfSDavid du Colombier
83ff48bf5SDavid du Colombier #include "ip.h"
97dd7cddfSDavid du Colombier
103ff48bf5SDavid du Colombier #define BLKIPVER(xp) (((Ip4hdr*)((xp)->rp))->vihl&0xF0)
113ff48bf5SDavid du Colombier
1259cc4ca5SDavid du Colombier static char *statnames[] =
1359cc4ca5SDavid du Colombier {
1459cc4ca5SDavid du Colombier [Forwarding] "Forwarding",
1559cc4ca5SDavid du Colombier [DefaultTTL] "DefaultTTL",
1659cc4ca5SDavid du Colombier [InReceives] "InReceives",
1759cc4ca5SDavid du Colombier [InHdrErrors] "InHdrErrors",
1859cc4ca5SDavid du Colombier [InAddrErrors] "InAddrErrors",
1959cc4ca5SDavid du Colombier [ForwDatagrams] "ForwDatagrams",
2059cc4ca5SDavid du Colombier [InUnknownProtos] "InUnknownProtos",
2159cc4ca5SDavid du Colombier [InDiscards] "InDiscards",
2259cc4ca5SDavid du Colombier [InDelivers] "InDelivers",
2359cc4ca5SDavid du Colombier [OutRequests] "OutRequests",
2459cc4ca5SDavid du Colombier [OutDiscards] "OutDiscards",
2559cc4ca5SDavid du Colombier [OutNoRoutes] "OutNoRoutes",
2659cc4ca5SDavid du Colombier [ReasmTimeout] "ReasmTimeout",
2759cc4ca5SDavid du Colombier [ReasmReqds] "ReasmReqds",
2859cc4ca5SDavid du Colombier [ReasmOKs] "ReasmOKs",
2959cc4ca5SDavid du Colombier [ReasmFails] "ReasmFails",
3059cc4ca5SDavid du Colombier [FragOKs] "FragOKs",
3159cc4ca5SDavid du Colombier [FragFails] "FragFails",
3259cc4ca5SDavid du Colombier [FragCreates] "FragCreates",
337dd7cddfSDavid du Colombier };
347dd7cddfSDavid du Colombier
353ff48bf5SDavid du Colombier #define BLKIP(xp) ((Ip4hdr*)((xp)->rp))
367dd7cddfSDavid du Colombier /*
377dd7cddfSDavid du Colombier * This sleazy macro relies on the media header size being
387dd7cddfSDavid du Colombier * larger than sizeof(Ipfrag). ipreassemble checks this is true
397dd7cddfSDavid du Colombier */
407dd7cddfSDavid du Colombier #define BKFG(xp) ((Ipfrag*)((xp)->base))
417dd7cddfSDavid du Colombier
427dd7cddfSDavid du Colombier ushort ipcsum(uchar*);
433ff48bf5SDavid du Colombier Block* ip4reassemble(IP*, int, Block*, Ip4hdr*);
443ff48bf5SDavid du Colombier void ipfragfree4(IP*, Fragment4*);
453ff48bf5SDavid du Colombier Fragment4* ipfragallo4(IP*);
463ff48bf5SDavid du Colombier
473ff48bf5SDavid du Colombier void
ip_init_6(Fs * f)483ff48bf5SDavid du Colombier ip_init_6(Fs *f)
493ff48bf5SDavid du Colombier {
503ff48bf5SDavid du Colombier v6params *v6p;
513ff48bf5SDavid du Colombier
523ff48bf5SDavid du Colombier v6p = smalloc(sizeof(v6params));
533ff48bf5SDavid du Colombier
543179bee6SDavid du Colombier v6p->rp.mflag = 0; /* default not managed */
553ff48bf5SDavid du Colombier v6p->rp.oflag = 0;
563179bee6SDavid du Colombier v6p->rp.maxraint = 600000; /* millisecs */
573ff48bf5SDavid du Colombier v6p->rp.minraint = 200000;
583179bee6SDavid du Colombier v6p->rp.linkmtu = 0; /* no mtu sent */
593ff48bf5SDavid du Colombier v6p->rp.reachtime = 0;
603ff48bf5SDavid du Colombier v6p->rp.rxmitra = 0;
613ff48bf5SDavid du Colombier v6p->rp.ttl = MAXTTL;
623179bee6SDavid du Colombier v6p->rp.routerlt = 3 * v6p->rp.maxraint;
633ff48bf5SDavid du Colombier
643179bee6SDavid du Colombier v6p->hp.rxmithost = 1000; /* v6 RETRANS_TIMER */
653ff48bf5SDavid du Colombier
663ff48bf5SDavid du Colombier v6p->cdrouter = -1;
673ff48bf5SDavid du Colombier
683ff48bf5SDavid du Colombier f->v6p = v6p;
693ff48bf5SDavid du Colombier }
703ff48bf5SDavid du Colombier
713ff48bf5SDavid du Colombier void
initfrag(IP * ip,int size)723ff48bf5SDavid du Colombier initfrag(IP *ip, int size)
733ff48bf5SDavid du Colombier {
743ff48bf5SDavid du Colombier Fragment4 *fq4, *eq4;
753ff48bf5SDavid du Colombier Fragment6 *fq6, *eq6;
763ff48bf5SDavid du Colombier
773ff48bf5SDavid du Colombier ip->fragfree4 = (Fragment4*)malloc(sizeof(Fragment4) * size);
783ff48bf5SDavid du Colombier if(ip->fragfree4 == nil)
793ff48bf5SDavid du Colombier panic("initfrag");
803ff48bf5SDavid du Colombier
813ff48bf5SDavid du Colombier eq4 = &ip->fragfree4[size];
823ff48bf5SDavid du Colombier for(fq4 = ip->fragfree4; fq4 < eq4; fq4++)
833ff48bf5SDavid du Colombier fq4->next = fq4+1;
843ff48bf5SDavid du Colombier
853ff48bf5SDavid du Colombier ip->fragfree4[size-1].next = nil;
863ff48bf5SDavid du Colombier
873ff48bf5SDavid du Colombier ip->fragfree6 = (Fragment6*)malloc(sizeof(Fragment6) * size);
883ff48bf5SDavid du Colombier if(ip->fragfree6 == nil)
893ff48bf5SDavid du Colombier panic("initfrag");
903ff48bf5SDavid du Colombier
913ff48bf5SDavid du Colombier eq6 = &ip->fragfree6[size];
923ff48bf5SDavid du Colombier for(fq6 = ip->fragfree6; fq6 < eq6; fq6++)
933ff48bf5SDavid du Colombier fq6->next = fq6+1;
943ff48bf5SDavid du Colombier
953ff48bf5SDavid du Colombier ip->fragfree6[size-1].next = nil;
963ff48bf5SDavid du Colombier }
977dd7cddfSDavid du Colombier
987dd7cddfSDavid du Colombier void
ip_init(Fs * f)997dd7cddfSDavid du Colombier ip_init(Fs *f)
1007dd7cddfSDavid du Colombier {
1017dd7cddfSDavid du Colombier IP *ip;
1027dd7cddfSDavid du Colombier
1037dd7cddfSDavid du Colombier ip = smalloc(sizeof(IP));
1047dd7cddfSDavid du Colombier initfrag(ip, 100);
1057dd7cddfSDavid du Colombier f->ip = ip;
1063ff48bf5SDavid du Colombier
1073ff48bf5SDavid du Colombier ip_init_6(f);
1087dd7cddfSDavid du Colombier }
1097dd7cddfSDavid du Colombier
1107dd7cddfSDavid du Colombier void
iprouting(Fs * f,int on)1117dd7cddfSDavid du Colombier iprouting(Fs *f, int on)
1127dd7cddfSDavid du Colombier {
1137dd7cddfSDavid du Colombier f->ip->iprouting = on;
1147dd7cddfSDavid du Colombier if(f->ip->iprouting==0)
11559cc4ca5SDavid du Colombier f->ip->stats[Forwarding] = 2;
1167dd7cddfSDavid du Colombier else
11759cc4ca5SDavid du Colombier f->ip->stats[Forwarding] = 1;
1187dd7cddfSDavid du Colombier }
1197dd7cddfSDavid du Colombier
120e6c6b7f8SDavid du Colombier int
ipoput4(Fs * f,Block * bp,int gating,int ttl,int tos,Conv * c)121a6a9e072SDavid du Colombier ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
1227dd7cddfSDavid du Colombier {
1237dd7cddfSDavid du Colombier Ipifc *ifc;
1247dd7cddfSDavid du Colombier uchar *gate;
1259a747e4fSDavid du Colombier ulong fragoff;
1267dd7cddfSDavid du Colombier Block *xp, *nb;
1273ff48bf5SDavid du Colombier Ip4hdr *eh, *feh;
1287dd7cddfSDavid du Colombier int lid, len, seglen, chunk, dlen, blklen, offset, medialen;
1297dd7cddfSDavid du Colombier Route *r, *sr;
1307dd7cddfSDavid du Colombier IP *ip;
131e6c6b7f8SDavid du Colombier int rv = 0;
1327dd7cddfSDavid du Colombier
1337dd7cddfSDavid du Colombier ip = f->ip;
1347dd7cddfSDavid du Colombier
1357dd7cddfSDavid du Colombier /* Fill out the ip header */
1363ff48bf5SDavid du Colombier eh = (Ip4hdr*)(bp->rp);
1377dd7cddfSDavid du Colombier
13859cc4ca5SDavid du Colombier ip->stats[OutRequests]++;
1397dd7cddfSDavid du Colombier
1407dd7cddfSDavid du Colombier /* Number of uchars in data and ip header to write */
1417dd7cddfSDavid du Colombier len = blocklen(bp);
1427dd7cddfSDavid du Colombier
1437dd7cddfSDavid du Colombier if(gating){
1447dd7cddfSDavid du Colombier chunk = nhgets(eh->length);
1457dd7cddfSDavid du Colombier if(chunk > len){
14659cc4ca5SDavid du Colombier ip->stats[OutDiscards]++;
1477dd7cddfSDavid du Colombier netlog(f, Logip, "short gated packet\n");
1487dd7cddfSDavid du Colombier goto free;
1497dd7cddfSDavid du Colombier }
1507dd7cddfSDavid du Colombier if(chunk < len)
1517dd7cddfSDavid du Colombier len = chunk;
1527dd7cddfSDavid du Colombier }
1537dd7cddfSDavid du Colombier if(len >= IP_MAX){
15459cc4ca5SDavid du Colombier ip->stats[OutDiscards]++;
1557dd7cddfSDavid du Colombier netlog(f, Logip, "exceeded ip max size %V\n", eh->dst);
1567dd7cddfSDavid du Colombier goto free;
1577dd7cddfSDavid du Colombier }
1587dd7cddfSDavid du Colombier
159a6a9e072SDavid du Colombier r = v4lookup(f, eh->dst, c);
1607dd7cddfSDavid du Colombier if(r == nil){
16159cc4ca5SDavid du Colombier ip->stats[OutNoRoutes]++;
1627dd7cddfSDavid du Colombier netlog(f, Logip, "no interface %V\n", eh->dst);
163e6c6b7f8SDavid du Colombier rv = -1;
1647dd7cddfSDavid du Colombier goto free;
1657dd7cddfSDavid du Colombier }
1667dd7cddfSDavid du Colombier
1677dd7cddfSDavid du Colombier ifc = r->ifc;
1687dd7cddfSDavid du Colombier if(r->type & (Rifc|Runi))
1697dd7cddfSDavid du Colombier gate = eh->dst;
1707dd7cddfSDavid du Colombier else
1717dd7cddfSDavid du Colombier if(r->type & (Rbcast|Rmulti)) {
1727dd7cddfSDavid du Colombier gate = eh->dst;
173a6a9e072SDavid du Colombier sr = v4lookup(f, eh->src, nil);
1747dd7cddfSDavid du Colombier if(sr != nil && (sr->type & Runi))
1757dd7cddfSDavid du Colombier ifc = sr->ifc;
1767dd7cddfSDavid du Colombier }
1777dd7cddfSDavid du Colombier else
1787dd7cddfSDavid du Colombier gate = r->v4.gate;
1797dd7cddfSDavid du Colombier
1807dd7cddfSDavid du Colombier if(!gating)
1813ff48bf5SDavid du Colombier eh->vihl = IP_VER4|IP_HLEN4;
1827dd7cddfSDavid du Colombier eh->ttl = ttl;
1839a747e4fSDavid du Colombier if(!gating)
1847dd7cddfSDavid du Colombier eh->tos = tos;
1857dd7cddfSDavid du Colombier
1867dd7cddfSDavid du Colombier if(!canrlock(ifc))
1877dd7cddfSDavid du Colombier goto free;
1887dd7cddfSDavid du Colombier if(waserror()){
1897dd7cddfSDavid du Colombier runlock(ifc);
1907dd7cddfSDavid du Colombier nexterror();
1917dd7cddfSDavid du Colombier }
1927dd7cddfSDavid du Colombier if(ifc->m == nil)
1937dd7cddfSDavid du Colombier goto raise;
1947dd7cddfSDavid du Colombier
1957dd7cddfSDavid du Colombier /* If we dont need to fragment just send it */
1967ec5746aSDavid du Colombier if(c && c->maxfragsize && c->maxfragsize < ifc->maxtu)
1977ec5746aSDavid du Colombier medialen = c->maxfragsize - ifc->m->hsize;
1987ec5746aSDavid du Colombier else
1993f695129SDavid du Colombier medialen = ifc->maxtu - ifc->m->hsize;
2007dd7cddfSDavid du Colombier if(len <= medialen) {
2017dd7cddfSDavid du Colombier if(!gating)
2023ff48bf5SDavid du Colombier hnputs(eh->id, incref(&ip->id4));
2037dd7cddfSDavid du Colombier hnputs(eh->length, len);
2049a747e4fSDavid du Colombier if(!gating){
2057dd7cddfSDavid du Colombier eh->frag[0] = 0;
2067dd7cddfSDavid du Colombier eh->frag[1] = 0;
2079a747e4fSDavid du Colombier }
2087dd7cddfSDavid du Colombier eh->cksum[0] = 0;
2097dd7cddfSDavid du Colombier eh->cksum[1] = 0;
2107dd7cddfSDavid du Colombier hnputs(eh->cksum, ipcsum(&eh->vihl));
2117ec5746aSDavid du Colombier assert(bp->next == nil);
2127dd7cddfSDavid du Colombier ifc->m->bwrite(ifc, bp, V4, gate);
2137dd7cddfSDavid du Colombier runlock(ifc);
2147dd7cddfSDavid du Colombier poperror();
215e6c6b7f8SDavid du Colombier return 0;
2167dd7cddfSDavid du Colombier }
2177dd7cddfSDavid du Colombier
218*6b7c5dceSDavid du Colombier if((eh->frag[0] & (IP_DF>>8)) && !gating)
219*6b7c5dceSDavid du Colombier print("%V: DF set\n", eh->dst);
220d9306527SDavid du Colombier
2217dd7cddfSDavid du Colombier if(eh->frag[0] & (IP_DF>>8)){
22259cc4ca5SDavid du Colombier ip->stats[FragFails]++;
22359cc4ca5SDavid du Colombier ip->stats[OutDiscards]++;
224d9306527SDavid du Colombier icmpcantfrag(f, bp, medialen);
2257dd7cddfSDavid du Colombier netlog(f, Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst);
2267dd7cddfSDavid du Colombier goto raise;
2277dd7cddfSDavid du Colombier }
2287dd7cddfSDavid du Colombier
2293ff48bf5SDavid du Colombier seglen = (medialen - IP4HDR) & ~7;
2307dd7cddfSDavid du Colombier if(seglen < 8){
23159cc4ca5SDavid du Colombier ip->stats[FragFails]++;
23259cc4ca5SDavid du Colombier ip->stats[OutDiscards]++;
2337dd7cddfSDavid du Colombier netlog(f, Logip, "%V seglen < 8\n", eh->dst);
2347dd7cddfSDavid du Colombier goto raise;
2357dd7cddfSDavid du Colombier }
2367dd7cddfSDavid du Colombier
2373ff48bf5SDavid du Colombier dlen = len - IP4HDR;
2387dd7cddfSDavid du Colombier xp = bp;
2397dd7cddfSDavid du Colombier if(gating)
2407dd7cddfSDavid du Colombier lid = nhgets(eh->id);
2417dd7cddfSDavid du Colombier else
2423ff48bf5SDavid du Colombier lid = incref(&ip->id4);
2437dd7cddfSDavid du Colombier
2443ff48bf5SDavid du Colombier offset = IP4HDR;
2457dd7cddfSDavid du Colombier while(xp != nil && offset && offset >= BLEN(xp)) {
2467dd7cddfSDavid du Colombier offset -= BLEN(xp);
2477dd7cddfSDavid du Colombier xp = xp->next;
2487dd7cddfSDavid du Colombier }
2497dd7cddfSDavid du Colombier xp->rp += offset;
2507dd7cddfSDavid du Colombier
2519a747e4fSDavid du Colombier if(gating)
2529a747e4fSDavid du Colombier fragoff = nhgets(eh->frag)<<3;
2539a747e4fSDavid du Colombier else
2549a747e4fSDavid du Colombier fragoff = 0;
2559a747e4fSDavid du Colombier dlen += fragoff;
2569a747e4fSDavid du Colombier for(; fragoff < dlen; fragoff += seglen) {
2573ff48bf5SDavid du Colombier nb = allocb(IP4HDR+seglen);
2583ff48bf5SDavid du Colombier feh = (Ip4hdr*)(nb->rp);
2597dd7cddfSDavid du Colombier
2603ff48bf5SDavid du Colombier memmove(nb->wp, eh, IP4HDR);
2613ff48bf5SDavid du Colombier nb->wp += IP4HDR;
2627dd7cddfSDavid du Colombier
2637dd7cddfSDavid du Colombier if((fragoff + seglen) >= dlen) {
2647dd7cddfSDavid du Colombier seglen = dlen - fragoff;
2657dd7cddfSDavid du Colombier hnputs(feh->frag, fragoff>>3);
2667dd7cddfSDavid du Colombier }
2677dd7cddfSDavid du Colombier else
2687dd7cddfSDavid du Colombier hnputs(feh->frag, (fragoff>>3)|IP_MF);
2697dd7cddfSDavid du Colombier
2703ff48bf5SDavid du Colombier hnputs(feh->length, seglen + IP4HDR);
2717dd7cddfSDavid du Colombier hnputs(feh->id, lid);
2727dd7cddfSDavid du Colombier
2737dd7cddfSDavid du Colombier /* Copy up the data area */
2747dd7cddfSDavid du Colombier chunk = seglen;
2757dd7cddfSDavid du Colombier while(chunk) {
2767dd7cddfSDavid du Colombier if(!xp) {
27759cc4ca5SDavid du Colombier ip->stats[OutDiscards]++;
27859cc4ca5SDavid du Colombier ip->stats[FragFails]++;
2797dd7cddfSDavid du Colombier freeblist(nb);
2807dd7cddfSDavid du Colombier netlog(f, Logip, "!xp: chunk %d\n", chunk);
2817dd7cddfSDavid du Colombier goto raise;
2827dd7cddfSDavid du Colombier }
2837dd7cddfSDavid du Colombier blklen = chunk;
2847dd7cddfSDavid du Colombier if(BLEN(xp) < chunk)
2857dd7cddfSDavid du Colombier blklen = BLEN(xp);
2867dd7cddfSDavid du Colombier memmove(nb->wp, xp->rp, blklen);
2877dd7cddfSDavid du Colombier nb->wp += blklen;
2887dd7cddfSDavid du Colombier xp->rp += blklen;
2897dd7cddfSDavid du Colombier chunk -= blklen;
2907dd7cddfSDavid du Colombier if(xp->rp == xp->wp)
2917dd7cddfSDavid du Colombier xp = xp->next;
2927dd7cddfSDavid du Colombier }
2937dd7cddfSDavid du Colombier
2947dd7cddfSDavid du Colombier feh->cksum[0] = 0;
2957dd7cddfSDavid du Colombier feh->cksum[1] = 0;
2967dd7cddfSDavid du Colombier hnputs(feh->cksum, ipcsum(&feh->vihl));
2977dd7cddfSDavid du Colombier ifc->m->bwrite(ifc, nb, V4, gate);
29859cc4ca5SDavid du Colombier ip->stats[FragCreates]++;
2997dd7cddfSDavid du Colombier }
30059cc4ca5SDavid du Colombier ip->stats[FragOKs]++;
3017dd7cddfSDavid du Colombier raise:
3027dd7cddfSDavid du Colombier runlock(ifc);
3037dd7cddfSDavid du Colombier poperror();
3047dd7cddfSDavid du Colombier free:
3057dd7cddfSDavid du Colombier freeblist(bp);
306e6c6b7f8SDavid du Colombier return rv;
3077dd7cddfSDavid du Colombier }
3087dd7cddfSDavid du Colombier
3097dd7cddfSDavid du Colombier void
ipiput4(Fs * f,Ipifc * ifc,Block * bp)3103ff48bf5SDavid du Colombier ipiput4(Fs *f, Ipifc *ifc, Block *bp)
3117dd7cddfSDavid du Colombier {
3127dd7cddfSDavid du Colombier int hl;
3133ff48bf5SDavid du Colombier int hop, tos, proto, olen;
3143ff48bf5SDavid du Colombier Ip4hdr *h;
3157dd7cddfSDavid du Colombier Proto *p;
3167dd7cddfSDavid du Colombier ushort frag;
3177dd7cddfSDavid du Colombier int notforme;
3187dd7cddfSDavid du Colombier uchar *dp, v6dst[IPaddrlen];
3197dd7cddfSDavid du Colombier IP *ip;
320a6a9e072SDavid du Colombier Route *r;
3217ec5746aSDavid du Colombier Conv conv;
3227ec5746aSDavid du Colombier
3233ff48bf5SDavid du Colombier if(BLKIPVER(bp) != IP_VER4) {
3243ff48bf5SDavid du Colombier ipiput6(f, ifc, bp);
3253ff48bf5SDavid du Colombier return;
3263ff48bf5SDavid du Colombier }
3277dd7cddfSDavid du Colombier
3287dd7cddfSDavid du Colombier ip = f->ip;
32959cc4ca5SDavid du Colombier ip->stats[InReceives]++;
3307dd7cddfSDavid du Colombier
3317dd7cddfSDavid du Colombier /*
3327dd7cddfSDavid du Colombier * Ensure we have all the header info in the first
3337dd7cddfSDavid du Colombier * block. Make life easier for other protocols by
3347dd7cddfSDavid du Colombier * collecting up to the first 64 bytes in the first block.
3357dd7cddfSDavid du Colombier */
3367dd7cddfSDavid du Colombier if(BLEN(bp) < 64) {
3377dd7cddfSDavid du Colombier hl = blocklen(bp);
3383ff48bf5SDavid du Colombier if(hl < IP4HDR)
3393ff48bf5SDavid du Colombier hl = IP4HDR;
3407dd7cddfSDavid du Colombier if(hl > 64)
3417dd7cddfSDavid du Colombier hl = 64;
3427dd7cddfSDavid du Colombier bp = pullupblock(bp, hl);
3437dd7cddfSDavid du Colombier if(bp == nil)
3447dd7cddfSDavid du Colombier return;
3457dd7cddfSDavid du Colombier }
3463ff48bf5SDavid du Colombier
3473ff48bf5SDavid du Colombier h = (Ip4hdr*)(bp->rp);
3487dd7cddfSDavid du Colombier
3497dd7cddfSDavid du Colombier /* dump anything that whose header doesn't checksum */
3505fab9909SDavid du Colombier if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {
35159cc4ca5SDavid du Colombier ip->stats[InHdrErrors]++;
3527dd7cddfSDavid du Colombier netlog(f, Logip, "ip: checksum error %V\n", h->src);
3537dd7cddfSDavid du Colombier freeblist(bp);
3547dd7cddfSDavid du Colombier return;
3557dd7cddfSDavid du Colombier }
3567dd7cddfSDavid du Colombier v4tov6(v6dst, h->dst);
3577dd7cddfSDavid du Colombier notforme = ipforme(f, v6dst) == 0;
3587dd7cddfSDavid du Colombier
3597dd7cddfSDavid du Colombier /* Check header length and version */
3603ff48bf5SDavid du Colombier if((h->vihl&0x0F) != IP_HLEN4) {
3617dd7cddfSDavid du Colombier hl = (h->vihl&0xF)<<2;
3623ff48bf5SDavid du Colombier if(hl < (IP_HLEN4<<2)) {
36359cc4ca5SDavid du Colombier ip->stats[InHdrErrors]++;
3647dd7cddfSDavid du Colombier netlog(f, Logip, "ip: %V bad hivl %ux\n", h->src, h->vihl);
3657dd7cddfSDavid du Colombier freeblist(bp);
3667dd7cddfSDavid du Colombier return;
3677dd7cddfSDavid du Colombier }
3687dd7cddfSDavid du Colombier /* If this is not routed strip off the options */
3697dd7cddfSDavid du Colombier if(notforme == 0) {
370b7b24591SDavid du Colombier olen = nhgets(h->length);
3713ff48bf5SDavid du Colombier dp = bp->rp + (hl - (IP_HLEN4<<2));
3723ff48bf5SDavid du Colombier memmove(dp, h, IP_HLEN4<<2);
3737dd7cddfSDavid du Colombier bp->rp = dp;
3743ff48bf5SDavid du Colombier h = (Ip4hdr*)(bp->rp);
3753ff48bf5SDavid du Colombier h->vihl = (IP_VER4|IP_HLEN4);
3763ff48bf5SDavid du Colombier hnputs(h->length, olen-hl+(IP_HLEN4<<2));
3777dd7cddfSDavid du Colombier }
3787dd7cddfSDavid du Colombier }
3797dd7cddfSDavid du Colombier
3807dd7cddfSDavid du Colombier /* route */
3817dd7cddfSDavid du Colombier if(notforme) {
3827dd7cddfSDavid du Colombier if(!ip->iprouting){
3837ec5746aSDavid du Colombier freeblist(bp);
3847dd7cddfSDavid du Colombier return;
3857dd7cddfSDavid du Colombier }
3863ff48bf5SDavid du Colombier
387a6a9e072SDavid du Colombier /* don't forward to source's network */
38884ba54caSDavid du Colombier memset(&conv, 0, sizeof conv);
389a6a9e072SDavid du Colombier conv.r = nil;
390a6a9e072SDavid du Colombier r = v4lookup(f, h->dst, &conv);
39104b73bddSDavid du Colombier if(r == nil || r->ifc == ifc){
39259cc4ca5SDavid du Colombier ip->stats[OutDiscards]++;
3937dd7cddfSDavid du Colombier freeblist(bp);
3947dd7cddfSDavid du Colombier return;
3957dd7cddfSDavid du Colombier }
3967dd7cddfSDavid du Colombier
3977dd7cddfSDavid du Colombier /* don't forward if packet has timed out */
3983ff48bf5SDavid du Colombier hop = h->ttl;
3993ff48bf5SDavid du Colombier if(hop < 1) {
40059cc4ca5SDavid du Colombier ip->stats[InHdrErrors]++;
4013ff48bf5SDavid du Colombier icmpttlexceeded(f, ifc->lifc->local, bp);
4027dd7cddfSDavid du Colombier freeblist(bp);
4037dd7cddfSDavid du Colombier return;
4047dd7cddfSDavid du Colombier }
4057dd7cddfSDavid du Colombier
4069a747e4fSDavid du Colombier /* reassemble if the interface expects it */
40704b73bddSDavid du Colombier if(r->ifc == nil) panic("nil route rfc");
4089a747e4fSDavid du Colombier if(r->ifc->reassemble){
4099a747e4fSDavid du Colombier frag = nhgets(h->frag);
4109a747e4fSDavid du Colombier if(frag) {
4119a747e4fSDavid du Colombier h->tos = 0;
4129a747e4fSDavid du Colombier if(frag & IP_MF)
4139a747e4fSDavid du Colombier h->tos = 1;
4143ff48bf5SDavid du Colombier bp = ip4reassemble(ip, frag, bp, h);
4159a747e4fSDavid du Colombier if(bp == nil)
4169a747e4fSDavid du Colombier return;
4173ff48bf5SDavid du Colombier h = (Ip4hdr*)(bp->rp);
4189a747e4fSDavid du Colombier }
4199a747e4fSDavid du Colombier }
4209a747e4fSDavid du Colombier
42159cc4ca5SDavid du Colombier ip->stats[ForwDatagrams]++;
4223ff48bf5SDavid du Colombier tos = h->tos;
4233ff48bf5SDavid du Colombier hop = h->ttl;
424a6a9e072SDavid du Colombier ipoput4(f, bp, 1, hop - 1, tos, &conv);
4257dd7cddfSDavid du Colombier return;
4267dd7cddfSDavid du Colombier }
4277dd7cddfSDavid du Colombier
4287dd7cddfSDavid du Colombier frag = nhgets(h->frag);
4297dd7cddfSDavid du Colombier if(frag) {
4307dd7cddfSDavid du Colombier h->tos = 0;
4317dd7cddfSDavid du Colombier if(frag & IP_MF)
4327dd7cddfSDavid du Colombier h->tos = 1;
4333ff48bf5SDavid du Colombier bp = ip4reassemble(ip, frag, bp, h);
4347dd7cddfSDavid du Colombier if(bp == nil)
4357dd7cddfSDavid du Colombier return;
4363ff48bf5SDavid du Colombier h = (Ip4hdr*)(bp->rp);
4377dd7cddfSDavid du Colombier }
4387dd7cddfSDavid du Colombier
439d9306527SDavid du Colombier /* don't let any frag info go up the stack */
440d9306527SDavid du Colombier h->frag[0] = 0;
441d9306527SDavid du Colombier h->frag[1] = 0;
442d9306527SDavid du Colombier
4433ff48bf5SDavid du Colombier proto = h->proto;
4443ff48bf5SDavid du Colombier p = Fsrcvpcol(f, proto);
4457dd7cddfSDavid du Colombier if(p != nil && p->rcv != nil) {
44659cc4ca5SDavid du Colombier ip->stats[InDelivers]++;
4479a747e4fSDavid du Colombier (*p->rcv)(p, ifc, bp);
4487dd7cddfSDavid du Colombier return;
4497dd7cddfSDavid du Colombier }
45059cc4ca5SDavid du Colombier ip->stats[InDiscards]++;
45159cc4ca5SDavid du Colombier ip->stats[InUnknownProtos]++;
4527dd7cddfSDavid du Colombier freeblist(bp);
4537dd7cddfSDavid du Colombier }
4547dd7cddfSDavid du Colombier
4557dd7cddfSDavid du Colombier int
ipstats(Fs * f,char * buf,int len)4567dd7cddfSDavid du Colombier ipstats(Fs *f, char *buf, int len)
4577dd7cddfSDavid du Colombier {
4587dd7cddfSDavid du Colombier IP *ip;
45959cc4ca5SDavid du Colombier char *p, *e;
46059cc4ca5SDavid du Colombier int i;
4617dd7cddfSDavid du Colombier
4627dd7cddfSDavid du Colombier ip = f->ip;
46359cc4ca5SDavid du Colombier ip->stats[DefaultTTL] = MAXTTL;
46459cc4ca5SDavid du Colombier
46559cc4ca5SDavid du Colombier p = buf;
46659cc4ca5SDavid du Colombier e = p+len;
4675e27dea9SDavid du Colombier for(i = 0; i < Nipstats; i++)
4685e27dea9SDavid du Colombier p = seprint(p, e, "%s: %llud\n", statnames[i], ip->stats[i]);
46959cc4ca5SDavid du Colombier return p - buf;
4707dd7cddfSDavid du Colombier }
4717dd7cddfSDavid du Colombier
4727dd7cddfSDavid du Colombier Block*
ip4reassemble(IP * ip,int offset,Block * bp,Ip4hdr * ih)4733ff48bf5SDavid du Colombier ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
4747dd7cddfSDavid du Colombier {
4757dd7cddfSDavid du Colombier int fend;
4767dd7cddfSDavid du Colombier ushort id;
4773ff48bf5SDavid du Colombier Fragment4 *f, *fnext;
4787dd7cddfSDavid du Colombier ulong src, dst;
4797dd7cddfSDavid du Colombier Block *bl, **l, *last, *prev;
4807dd7cddfSDavid du Colombier int ovlap, len, fragsize, pktposn;
4817dd7cddfSDavid du Colombier
4827dd7cddfSDavid du Colombier src = nhgetl(ih->src);
4837dd7cddfSDavid du Colombier dst = nhgetl(ih->dst);
4847dd7cddfSDavid du Colombier id = nhgets(ih->id);
4857dd7cddfSDavid du Colombier
4867dd7cddfSDavid du Colombier /*
4877dd7cddfSDavid du Colombier * block lists are too hard, pullupblock into a single block
4887dd7cddfSDavid du Colombier */
4897dd7cddfSDavid du Colombier if(bp->next){
4907dd7cddfSDavid du Colombier bp = pullupblock(bp, blocklen(bp));
4913ff48bf5SDavid du Colombier ih = (Ip4hdr*)(bp->rp);
4927dd7cddfSDavid du Colombier }
4937dd7cddfSDavid du Colombier
4943ff48bf5SDavid du Colombier qlock(&ip->fraglock4);
4957dd7cddfSDavid du Colombier
4967dd7cddfSDavid du Colombier /*
4977dd7cddfSDavid du Colombier * find a reassembly queue for this fragment
4987dd7cddfSDavid du Colombier */
4993ff48bf5SDavid du Colombier for(f = ip->flisthead4; f; f = fnext){
5003ff48bf5SDavid du Colombier fnext = f->next; /* because ipfragfree4 changes the list */
5017dd7cddfSDavid du Colombier if(f->src == src && f->dst == dst && f->id == id)
5027dd7cddfSDavid du Colombier break;
5033ff48bf5SDavid du Colombier if(f->age < NOW){
50459cc4ca5SDavid du Colombier ip->stats[ReasmTimeout]++;
5053ff48bf5SDavid du Colombier ipfragfree4(ip, f);
5067dd7cddfSDavid du Colombier }
5077dd7cddfSDavid du Colombier }
5087dd7cddfSDavid du Colombier
5097dd7cddfSDavid du Colombier /*
5107dd7cddfSDavid du Colombier * if this isn't a fragmented packet, accept it
5117dd7cddfSDavid du Colombier * and get rid of any fragments that might go
5127dd7cddfSDavid du Colombier * with it.
5137dd7cddfSDavid du Colombier */
5147dd7cddfSDavid du Colombier if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
5157dd7cddfSDavid du Colombier if(f != nil) {
5163ff48bf5SDavid du Colombier ipfragfree4(ip, f);
51759cc4ca5SDavid du Colombier ip->stats[ReasmFails]++;
5187dd7cddfSDavid du Colombier }
5193ff48bf5SDavid du Colombier qunlock(&ip->fraglock4);
5207dd7cddfSDavid du Colombier return bp;
5217dd7cddfSDavid du Colombier }
5227dd7cddfSDavid du Colombier
5237ec5746aSDavid du Colombier if(bp->base+IPFRAGSZ >= bp->rp){
5247ec5746aSDavid du Colombier bp = padblock(bp, IPFRAGSZ);
5257ec5746aSDavid du Colombier bp->rp += IPFRAGSZ;
5267dd7cddfSDavid du Colombier }
5277dd7cddfSDavid du Colombier
5287dd7cddfSDavid du Colombier BKFG(bp)->foff = offset<<3;
5293ff48bf5SDavid du Colombier BKFG(bp)->flen = nhgets(ih->length)-IP4HDR;
5307dd7cddfSDavid du Colombier
5317dd7cddfSDavid du Colombier /* First fragment allocates a reassembly queue */
5327dd7cddfSDavid du Colombier if(f == nil) {
5333ff48bf5SDavid du Colombier f = ipfragallo4(ip);
5347dd7cddfSDavid du Colombier f->id = id;
5357dd7cddfSDavid du Colombier f->src = src;
5367dd7cddfSDavid du Colombier f->dst = dst;
5377dd7cddfSDavid du Colombier
5387dd7cddfSDavid du Colombier f->blist = bp;
5397dd7cddfSDavid du Colombier
5403ff48bf5SDavid du Colombier qunlock(&ip->fraglock4);
54159cc4ca5SDavid du Colombier ip->stats[ReasmReqds]++;
5427dd7cddfSDavid du Colombier return nil;
5437dd7cddfSDavid du Colombier }
5447dd7cddfSDavid du Colombier
5457dd7cddfSDavid du Colombier /*
5467dd7cddfSDavid du Colombier * find the new fragment's position in the queue
5477dd7cddfSDavid du Colombier */
5487dd7cddfSDavid du Colombier prev = nil;
5497dd7cddfSDavid du Colombier l = &f->blist;
5507dd7cddfSDavid du Colombier bl = f->blist;
5517dd7cddfSDavid du Colombier while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
5527dd7cddfSDavid du Colombier prev = bl;
5537dd7cddfSDavid du Colombier l = &bl->next;
5547dd7cddfSDavid du Colombier bl = bl->next;
5557dd7cddfSDavid du Colombier }
5567dd7cddfSDavid du Colombier
5577dd7cddfSDavid du Colombier /* Check overlap of a previous fragment - trim away as necessary */
5587dd7cddfSDavid du Colombier if(prev) {
5597dd7cddfSDavid du Colombier ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
5607dd7cddfSDavid du Colombier if(ovlap > 0) {
5617dd7cddfSDavid du Colombier if(ovlap >= BKFG(bp)->flen) {
5627dd7cddfSDavid du Colombier freeblist(bp);
5633ff48bf5SDavid du Colombier qunlock(&ip->fraglock4);
5647dd7cddfSDavid du Colombier return nil;
5657dd7cddfSDavid du Colombier }
5667dd7cddfSDavid du Colombier BKFG(prev)->flen -= ovlap;
5677dd7cddfSDavid du Colombier }
5687dd7cddfSDavid du Colombier }
5697dd7cddfSDavid du Colombier
5707dd7cddfSDavid du Colombier /* Link onto assembly queue */
5717dd7cddfSDavid du Colombier bp->next = *l;
5727dd7cddfSDavid du Colombier *l = bp;
5737dd7cddfSDavid du Colombier
5747dd7cddfSDavid du Colombier /* Check to see if succeeding segments overlap */
5757dd7cddfSDavid du Colombier if(bp->next) {
5767dd7cddfSDavid du Colombier l = &bp->next;
5777dd7cddfSDavid du Colombier fend = BKFG(bp)->foff + BKFG(bp)->flen;
5787dd7cddfSDavid du Colombier /* Take completely covered segments out */
5797dd7cddfSDavid du Colombier while(*l) {
5807dd7cddfSDavid du Colombier ovlap = fend - BKFG(*l)->foff;
5817dd7cddfSDavid du Colombier if(ovlap <= 0)
5827dd7cddfSDavid du Colombier break;
5837dd7cddfSDavid du Colombier if(ovlap < BKFG(*l)->flen) {
5847dd7cddfSDavid du Colombier BKFG(*l)->flen -= ovlap;
5857dd7cddfSDavid du Colombier BKFG(*l)->foff += ovlap;
5867dd7cddfSDavid du Colombier /* move up ih hdrs */
5873ff48bf5SDavid du Colombier memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR);
5887dd7cddfSDavid du Colombier (*l)->rp += ovlap;
5897dd7cddfSDavid du Colombier break;
5907dd7cddfSDavid du Colombier }
5917dd7cddfSDavid du Colombier last = (*l)->next;
5927dd7cddfSDavid du Colombier (*l)->next = nil;
5937dd7cddfSDavid du Colombier freeblist(*l);
5947dd7cddfSDavid du Colombier *l = last;
5957dd7cddfSDavid du Colombier }
5967dd7cddfSDavid du Colombier }
5977dd7cddfSDavid du Colombier
5987dd7cddfSDavid du Colombier /*
5997dd7cddfSDavid du Colombier * look for a complete packet. if we get to a fragment
6007dd7cddfSDavid du Colombier * without IP_MF set, we're done.
6017dd7cddfSDavid du Colombier */
6027dd7cddfSDavid du Colombier pktposn = 0;
6037dd7cddfSDavid du Colombier for(bl = f->blist; bl; bl = bl->next) {
6047dd7cddfSDavid du Colombier if(BKFG(bl)->foff != pktposn)
6057dd7cddfSDavid du Colombier break;
6067dd7cddfSDavid du Colombier if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) {
6077dd7cddfSDavid du Colombier bl = f->blist;
6087dd7cddfSDavid du Colombier len = nhgets(BLKIP(bl)->length);
6097dd7cddfSDavid du Colombier bl->wp = bl->rp + len;
6107dd7cddfSDavid du Colombier
6117dd7cddfSDavid du Colombier /* Pullup all the fragment headers and
6127dd7cddfSDavid du Colombier * return a complete packet
6137dd7cddfSDavid du Colombier */
6147dd7cddfSDavid du Colombier for(bl = bl->next; bl; bl = bl->next) {
6157dd7cddfSDavid du Colombier fragsize = BKFG(bl)->flen;
6167dd7cddfSDavid du Colombier len += fragsize;
6173ff48bf5SDavid du Colombier bl->rp += IP4HDR;
6187dd7cddfSDavid du Colombier bl->wp = bl->rp + fragsize;
6197dd7cddfSDavid du Colombier }
6207dd7cddfSDavid du Colombier
6217dd7cddfSDavid du Colombier bl = f->blist;
6227dd7cddfSDavid du Colombier f->blist = nil;
6233ff48bf5SDavid du Colombier ipfragfree4(ip, f);
6247dd7cddfSDavid du Colombier ih = BLKIP(bl);
6257dd7cddfSDavid du Colombier hnputs(ih->length, len);
6263ff48bf5SDavid du Colombier qunlock(&ip->fraglock4);
62759cc4ca5SDavid du Colombier ip->stats[ReasmOKs]++;
6287dd7cddfSDavid du Colombier return bl;
6297dd7cddfSDavid du Colombier }
6307dd7cddfSDavid du Colombier pktposn += BKFG(bl)->flen;
6317dd7cddfSDavid du Colombier }
6323ff48bf5SDavid du Colombier qunlock(&ip->fraglock4);
6337dd7cddfSDavid du Colombier return nil;
6347dd7cddfSDavid du Colombier }
6357dd7cddfSDavid du Colombier
6367dd7cddfSDavid du Colombier /*
6373ff48bf5SDavid du Colombier * ipfragfree4 - Free a list of fragments - assume hold fraglock4
6387dd7cddfSDavid du Colombier */
6397dd7cddfSDavid du Colombier void
ipfragfree4(IP * ip,Fragment4 * frag)6403ff48bf5SDavid du Colombier ipfragfree4(IP *ip, Fragment4 *frag)
6417dd7cddfSDavid du Colombier {
6423ff48bf5SDavid du Colombier Fragment4 *fl, **l;
6437dd7cddfSDavid du Colombier
6447dd7cddfSDavid du Colombier if(frag->blist)
6457dd7cddfSDavid du Colombier freeblist(frag->blist);
6467dd7cddfSDavid du Colombier
6477dd7cddfSDavid du Colombier frag->src = 0;
6487dd7cddfSDavid du Colombier frag->id = 0;
6497dd7cddfSDavid du Colombier frag->blist = nil;
6507dd7cddfSDavid du Colombier
6513ff48bf5SDavid du Colombier l = &ip->flisthead4;
6527dd7cddfSDavid du Colombier for(fl = *l; fl; fl = fl->next) {
6537dd7cddfSDavid du Colombier if(fl == frag) {
6547dd7cddfSDavid du Colombier *l = frag->next;
6557dd7cddfSDavid du Colombier break;
6567dd7cddfSDavid du Colombier }
6577dd7cddfSDavid du Colombier l = &fl->next;
6587dd7cddfSDavid du Colombier }
6597dd7cddfSDavid du Colombier
6603ff48bf5SDavid du Colombier frag->next = ip->fragfree4;
6613ff48bf5SDavid du Colombier ip->fragfree4 = frag;
6627dd7cddfSDavid du Colombier
6637dd7cddfSDavid du Colombier }
6647dd7cddfSDavid du Colombier
6657dd7cddfSDavid du Colombier /*
6663ff48bf5SDavid du Colombier * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
6677dd7cddfSDavid du Colombier */
6683ff48bf5SDavid du Colombier Fragment4 *
ipfragallo4(IP * ip)6693ff48bf5SDavid du Colombier ipfragallo4(IP *ip)
6707dd7cddfSDavid du Colombier {
6713ff48bf5SDavid du Colombier Fragment4 *f;
6727dd7cddfSDavid du Colombier
6733ff48bf5SDavid du Colombier while(ip->fragfree4 == nil) {
6747dd7cddfSDavid du Colombier /* free last entry on fraglist */
6753ff48bf5SDavid du Colombier for(f = ip->flisthead4; f->next; f = f->next)
6767dd7cddfSDavid du Colombier ;
6773ff48bf5SDavid du Colombier ipfragfree4(ip, f);
6787dd7cddfSDavid du Colombier }
6793ff48bf5SDavid du Colombier f = ip->fragfree4;
6803ff48bf5SDavid du Colombier ip->fragfree4 = f->next;
6813ff48bf5SDavid du Colombier f->next = ip->flisthead4;
6823ff48bf5SDavid du Colombier ip->flisthead4 = f;
6833ff48bf5SDavid du Colombier f->age = NOW + 30000;
6847dd7cddfSDavid du Colombier
6857dd7cddfSDavid du Colombier return f;
6867dd7cddfSDavid du Colombier }
6877dd7cddfSDavid du Colombier
6887dd7cddfSDavid du Colombier ushort
ipcsum(uchar * addr)6897dd7cddfSDavid du Colombier ipcsum(uchar *addr)
6907dd7cddfSDavid du Colombier {
6917dd7cddfSDavid du Colombier int len;
6927dd7cddfSDavid du Colombier ulong sum;
6937dd7cddfSDavid du Colombier
6947dd7cddfSDavid du Colombier sum = 0;
6957dd7cddfSDavid du Colombier len = (addr[0]&0xf)<<2;
6967dd7cddfSDavid du Colombier
6977dd7cddfSDavid du Colombier while(len > 0) {
6987dd7cddfSDavid du Colombier sum += addr[0]<<8 | addr[1] ;
6997dd7cddfSDavid du Colombier len -= 2;
7007dd7cddfSDavid du Colombier addr += 2;
7017dd7cddfSDavid du Colombier }
7027dd7cddfSDavid du Colombier
7037dd7cddfSDavid du Colombier sum = (sum & 0xffff) + (sum >> 16);
7047dd7cddfSDavid du Colombier sum = (sum & 0xffff) + (sum >> 16);
7057dd7cddfSDavid du Colombier
7067dd7cddfSDavid du Colombier return (sum^0xffff);
7077dd7cddfSDavid du Colombier }
708