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;
132*8bbc9479SDavid du Colombier uchar v4dst[IPv4addrlen];
1337dd7cddfSDavid du Colombier
1347dd7cddfSDavid du Colombier ip = f->ip;
1357dd7cddfSDavid du Colombier
1367dd7cddfSDavid du Colombier /* Fill out the ip header */
1373ff48bf5SDavid du Colombier eh = (Ip4hdr*)(bp->rp);
1387dd7cddfSDavid du Colombier
13959cc4ca5SDavid du Colombier ip->stats[OutRequests]++;
1407dd7cddfSDavid du Colombier
1417dd7cddfSDavid du Colombier /* Number of uchars in data and ip header to write */
1427dd7cddfSDavid du Colombier len = blocklen(bp);
1437dd7cddfSDavid du Colombier
1447dd7cddfSDavid du Colombier if(gating){
1457dd7cddfSDavid du Colombier chunk = nhgets(eh->length);
1467dd7cddfSDavid du Colombier if(chunk > len){
14759cc4ca5SDavid du Colombier ip->stats[OutDiscards]++;
1487dd7cddfSDavid du Colombier netlog(f, Logip, "short gated packet\n");
1497dd7cddfSDavid du Colombier goto free;
1507dd7cddfSDavid du Colombier }
1517dd7cddfSDavid du Colombier if(chunk < len)
1527dd7cddfSDavid du Colombier len = chunk;
1537dd7cddfSDavid du Colombier }
1547dd7cddfSDavid du Colombier if(len >= IP_MAX){
15559cc4ca5SDavid du Colombier ip->stats[OutDiscards]++;
1567dd7cddfSDavid du Colombier netlog(f, Logip, "exceeded ip max size %V\n", eh->dst);
1577dd7cddfSDavid du Colombier goto free;
1587dd7cddfSDavid du Colombier }
1597dd7cddfSDavid du Colombier
160a6a9e072SDavid du Colombier r = v4lookup(f, eh->dst, c);
1617dd7cddfSDavid du Colombier if(r == nil){
16259cc4ca5SDavid du Colombier ip->stats[OutNoRoutes]++;
1637dd7cddfSDavid du Colombier netlog(f, Logip, "no interface %V\n", eh->dst);
164e6c6b7f8SDavid du Colombier rv = -1;
1657dd7cddfSDavid du Colombier goto free;
1667dd7cddfSDavid du Colombier }
1677dd7cddfSDavid du Colombier
1687dd7cddfSDavid du Colombier ifc = r->ifc;
1697dd7cddfSDavid du Colombier if(r->type & (Rifc|Runi))
1707dd7cddfSDavid du Colombier gate = eh->dst;
1717dd7cddfSDavid du Colombier else
1727dd7cddfSDavid du Colombier if(r->type & (Rbcast|Rmulti)) {
173*8bbc9479SDavid du Colombier if(nhgetl(r->v4.gate) == 0){
174*8bbc9479SDavid du Colombier hnputl(v4dst, r->v4.address);
175*8bbc9479SDavid du Colombier gate = v4dst;
176*8bbc9479SDavid du Colombier }else
1777dd7cddfSDavid du Colombier gate = eh->dst;
178a6a9e072SDavid du Colombier sr = v4lookup(f, eh->src, nil);
1797dd7cddfSDavid du Colombier if(sr != nil && (sr->type & Runi))
1807dd7cddfSDavid du Colombier ifc = sr->ifc;
1817dd7cddfSDavid du Colombier }
1827dd7cddfSDavid du Colombier else
1837dd7cddfSDavid du Colombier gate = r->v4.gate;
1847dd7cddfSDavid du Colombier
1857dd7cddfSDavid du Colombier if(!gating)
1863ff48bf5SDavid du Colombier eh->vihl = IP_VER4|IP_HLEN4;
1877dd7cddfSDavid du Colombier eh->ttl = ttl;
1889a747e4fSDavid du Colombier if(!gating)
1897dd7cddfSDavid du Colombier eh->tos = tos;
1907dd7cddfSDavid du Colombier
1917dd7cddfSDavid du Colombier if(!canrlock(ifc))
1927dd7cddfSDavid du Colombier goto free;
1937dd7cddfSDavid du Colombier if(waserror()){
1947dd7cddfSDavid du Colombier runlock(ifc);
1957dd7cddfSDavid du Colombier nexterror();
1967dd7cddfSDavid du Colombier }
197cc057a51SDavid du Colombier if(ifc->medium == nil)
1987dd7cddfSDavid du Colombier goto raise;
1997dd7cddfSDavid du Colombier
2007dd7cddfSDavid du Colombier /* If we dont need to fragment just send it */
2017ec5746aSDavid du Colombier if(c && c->maxfragsize && c->maxfragsize < ifc->maxtu)
202cc057a51SDavid du Colombier medialen = c->maxfragsize - ifc->medium->hsize;
2037ec5746aSDavid du Colombier else
204cc057a51SDavid du Colombier medialen = ifc->maxtu - ifc->medium->hsize;
2057dd7cddfSDavid du Colombier if(len <= medialen) {
2067dd7cddfSDavid du Colombier if(!gating)
2073ff48bf5SDavid du Colombier hnputs(eh->id, incref(&ip->id4));
2087dd7cddfSDavid du Colombier hnputs(eh->length, len);
2099a747e4fSDavid du Colombier if(!gating){
2107dd7cddfSDavid du Colombier eh->frag[0] = 0;
2117dd7cddfSDavid du Colombier eh->frag[1] = 0;
2129a747e4fSDavid du Colombier }
2137dd7cddfSDavid du Colombier eh->cksum[0] = 0;
2147dd7cddfSDavid du Colombier eh->cksum[1] = 0;
2157dd7cddfSDavid du Colombier hnputs(eh->cksum, ipcsum(&eh->vihl));
2167ec5746aSDavid du Colombier assert(bp->next == nil);
217cc057a51SDavid du Colombier ifc->medium->bwrite(ifc, bp, V4, gate);
2187dd7cddfSDavid du Colombier runlock(ifc);
2197dd7cddfSDavid du Colombier poperror();
220e6c6b7f8SDavid du Colombier return 0;
2217dd7cddfSDavid du Colombier }
2227dd7cddfSDavid du Colombier
2236b7c5dceSDavid du Colombier if((eh->frag[0] & (IP_DF>>8)) && !gating)
2246b7c5dceSDavid du Colombier print("%V: DF set\n", eh->dst);
225d9306527SDavid du Colombier
2267dd7cddfSDavid du Colombier if(eh->frag[0] & (IP_DF>>8)){
22759cc4ca5SDavid du Colombier ip->stats[FragFails]++;
22859cc4ca5SDavid du Colombier ip->stats[OutDiscards]++;
229d9306527SDavid du Colombier icmpcantfrag(f, bp, medialen);
2307dd7cddfSDavid du Colombier netlog(f, Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst);
2317dd7cddfSDavid du Colombier goto raise;
2327dd7cddfSDavid du Colombier }
2337dd7cddfSDavid du Colombier
2343ff48bf5SDavid du Colombier seglen = (medialen - IP4HDR) & ~7;
2357dd7cddfSDavid du Colombier if(seglen < 8){
23659cc4ca5SDavid du Colombier ip->stats[FragFails]++;
23759cc4ca5SDavid du Colombier ip->stats[OutDiscards]++;
2387dd7cddfSDavid du Colombier netlog(f, Logip, "%V seglen < 8\n", eh->dst);
2397dd7cddfSDavid du Colombier goto raise;
2407dd7cddfSDavid du Colombier }
2417dd7cddfSDavid du Colombier
2423ff48bf5SDavid du Colombier dlen = len - IP4HDR;
2437dd7cddfSDavid du Colombier xp = bp;
2447dd7cddfSDavid du Colombier if(gating)
2457dd7cddfSDavid du Colombier lid = nhgets(eh->id);
2467dd7cddfSDavid du Colombier else
2473ff48bf5SDavid du Colombier lid = incref(&ip->id4);
2487dd7cddfSDavid du Colombier
2493ff48bf5SDavid du Colombier offset = IP4HDR;
2507dd7cddfSDavid du Colombier while(xp != nil && offset && offset >= BLEN(xp)) {
2517dd7cddfSDavid du Colombier offset -= BLEN(xp);
2527dd7cddfSDavid du Colombier xp = xp->next;
2537dd7cddfSDavid du Colombier }
2547dd7cddfSDavid du Colombier xp->rp += offset;
2557dd7cddfSDavid du Colombier
2569a747e4fSDavid du Colombier if(gating)
2579a747e4fSDavid du Colombier fragoff = nhgets(eh->frag)<<3;
2589a747e4fSDavid du Colombier else
2599a747e4fSDavid du Colombier fragoff = 0;
2609a747e4fSDavid du Colombier dlen += fragoff;
2619a747e4fSDavid du Colombier for(; fragoff < dlen; fragoff += seglen) {
2623ff48bf5SDavid du Colombier nb = allocb(IP4HDR+seglen);
2633ff48bf5SDavid du Colombier feh = (Ip4hdr*)(nb->rp);
2647dd7cddfSDavid du Colombier
2653ff48bf5SDavid du Colombier memmove(nb->wp, eh, IP4HDR);
2663ff48bf5SDavid du Colombier nb->wp += IP4HDR;
2677dd7cddfSDavid du Colombier
2687dd7cddfSDavid du Colombier if((fragoff + seglen) >= dlen) {
2697dd7cddfSDavid du Colombier seglen = dlen - fragoff;
2707dd7cddfSDavid du Colombier hnputs(feh->frag, fragoff>>3);
2717dd7cddfSDavid du Colombier }
2727dd7cddfSDavid du Colombier else
2737dd7cddfSDavid du Colombier hnputs(feh->frag, (fragoff>>3)|IP_MF);
2747dd7cddfSDavid du Colombier
2753ff48bf5SDavid du Colombier hnputs(feh->length, seglen + IP4HDR);
2767dd7cddfSDavid du Colombier hnputs(feh->id, lid);
2777dd7cddfSDavid du Colombier
2787dd7cddfSDavid du Colombier /* Copy up the data area */
2797dd7cddfSDavid du Colombier chunk = seglen;
2807dd7cddfSDavid du Colombier while(chunk) {
2817dd7cddfSDavid du Colombier if(!xp) {
28259cc4ca5SDavid du Colombier ip->stats[OutDiscards]++;
28359cc4ca5SDavid du Colombier ip->stats[FragFails]++;
2847dd7cddfSDavid du Colombier freeblist(nb);
2857dd7cddfSDavid du Colombier netlog(f, Logip, "!xp: chunk %d\n", chunk);
2867dd7cddfSDavid du Colombier goto raise;
2877dd7cddfSDavid du Colombier }
2887dd7cddfSDavid du Colombier blklen = chunk;
2897dd7cddfSDavid du Colombier if(BLEN(xp) < chunk)
2907dd7cddfSDavid du Colombier blklen = BLEN(xp);
2917dd7cddfSDavid du Colombier memmove(nb->wp, xp->rp, blklen);
2927dd7cddfSDavid du Colombier nb->wp += blklen;
2937dd7cddfSDavid du Colombier xp->rp += blklen;
2947dd7cddfSDavid du Colombier chunk -= blklen;
2957dd7cddfSDavid du Colombier if(xp->rp == xp->wp)
2967dd7cddfSDavid du Colombier xp = xp->next;
2977dd7cddfSDavid du Colombier }
2987dd7cddfSDavid du Colombier
2997dd7cddfSDavid du Colombier feh->cksum[0] = 0;
3007dd7cddfSDavid du Colombier feh->cksum[1] = 0;
3017dd7cddfSDavid du Colombier hnputs(feh->cksum, ipcsum(&feh->vihl));
302cc057a51SDavid du Colombier ifc->medium->bwrite(ifc, nb, V4, gate);
30359cc4ca5SDavid du Colombier ip->stats[FragCreates]++;
3047dd7cddfSDavid du Colombier }
30559cc4ca5SDavid du Colombier ip->stats[FragOKs]++;
3067dd7cddfSDavid du Colombier raise:
3077dd7cddfSDavid du Colombier runlock(ifc);
3087dd7cddfSDavid du Colombier poperror();
3097dd7cddfSDavid du Colombier free:
3107dd7cddfSDavid du Colombier freeblist(bp);
311e6c6b7f8SDavid du Colombier return rv;
3127dd7cddfSDavid du Colombier }
3137dd7cddfSDavid du Colombier
3147dd7cddfSDavid du Colombier void
ipiput4(Fs * f,Ipifc * ifc,Block * bp)3153ff48bf5SDavid du Colombier ipiput4(Fs *f, Ipifc *ifc, Block *bp)
3167dd7cddfSDavid du Colombier {
3177dd7cddfSDavid du Colombier int hl;
3183ff48bf5SDavid du Colombier int hop, tos, proto, olen;
3193ff48bf5SDavid du Colombier Ip4hdr *h;
3207dd7cddfSDavid du Colombier Proto *p;
3217dd7cddfSDavid du Colombier ushort frag;
3227dd7cddfSDavid du Colombier int notforme;
3237dd7cddfSDavid du Colombier uchar *dp, v6dst[IPaddrlen];
3247dd7cddfSDavid du Colombier IP *ip;
325a6a9e072SDavid du Colombier Route *r;
3267ec5746aSDavid du Colombier Conv conv;
3277ec5746aSDavid du Colombier
3283ff48bf5SDavid du Colombier if(BLKIPVER(bp) != IP_VER4) {
3293ff48bf5SDavid du Colombier ipiput6(f, ifc, bp);
3303ff48bf5SDavid du Colombier return;
3313ff48bf5SDavid du Colombier }
3327dd7cddfSDavid du Colombier
3337dd7cddfSDavid du Colombier ip = f->ip;
33459cc4ca5SDavid du Colombier ip->stats[InReceives]++;
3357dd7cddfSDavid du Colombier
3367dd7cddfSDavid du Colombier /*
3377dd7cddfSDavid du Colombier * Ensure we have all the header info in the first
3387dd7cddfSDavid du Colombier * block. Make life easier for other protocols by
3397dd7cddfSDavid du Colombier * collecting up to the first 64 bytes in the first block.
3407dd7cddfSDavid du Colombier */
3417dd7cddfSDavid du Colombier if(BLEN(bp) < 64) {
3427dd7cddfSDavid du Colombier hl = blocklen(bp);
3433ff48bf5SDavid du Colombier if(hl < IP4HDR)
3443ff48bf5SDavid du Colombier hl = IP4HDR;
3457dd7cddfSDavid du Colombier if(hl > 64)
3467dd7cddfSDavid du Colombier hl = 64;
3477dd7cddfSDavid du Colombier bp = pullupblock(bp, hl);
3487dd7cddfSDavid du Colombier if(bp == nil)
3497dd7cddfSDavid du Colombier return;
3507dd7cddfSDavid du Colombier }
3513ff48bf5SDavid du Colombier
3523ff48bf5SDavid du Colombier h = (Ip4hdr*)(bp->rp);
3537dd7cddfSDavid du Colombier
3547dd7cddfSDavid du Colombier /* dump anything that whose header doesn't checksum */
3555fab9909SDavid du Colombier if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {
35659cc4ca5SDavid du Colombier ip->stats[InHdrErrors]++;
3577dd7cddfSDavid du Colombier netlog(f, Logip, "ip: checksum error %V\n", h->src);
3587dd7cddfSDavid du Colombier freeblist(bp);
3597dd7cddfSDavid du Colombier return;
3607dd7cddfSDavid du Colombier }
3617dd7cddfSDavid du Colombier v4tov6(v6dst, h->dst);
3627dd7cddfSDavid du Colombier notforme = ipforme(f, v6dst) == 0;
3637dd7cddfSDavid du Colombier
3647dd7cddfSDavid du Colombier /* Check header length and version */
3653ff48bf5SDavid du Colombier if((h->vihl&0x0F) != IP_HLEN4) {
3667dd7cddfSDavid du Colombier hl = (h->vihl&0xF)<<2;
3673ff48bf5SDavid du Colombier if(hl < (IP_HLEN4<<2)) {
36859cc4ca5SDavid du Colombier ip->stats[InHdrErrors]++;
3697dd7cddfSDavid du Colombier netlog(f, Logip, "ip: %V bad hivl %ux\n", h->src, h->vihl);
3707dd7cddfSDavid du Colombier freeblist(bp);
3717dd7cddfSDavid du Colombier return;
3727dd7cddfSDavid du Colombier }
3737dd7cddfSDavid du Colombier /* If this is not routed strip off the options */
3747dd7cddfSDavid du Colombier if(notforme == 0) {
375b7b24591SDavid du Colombier olen = nhgets(h->length);
3763ff48bf5SDavid du Colombier dp = bp->rp + (hl - (IP_HLEN4<<2));
3773ff48bf5SDavid du Colombier memmove(dp, h, IP_HLEN4<<2);
3787dd7cddfSDavid du Colombier bp->rp = dp;
3793ff48bf5SDavid du Colombier h = (Ip4hdr*)(bp->rp);
3803ff48bf5SDavid du Colombier h->vihl = (IP_VER4|IP_HLEN4);
3813ff48bf5SDavid du Colombier hnputs(h->length, olen-hl+(IP_HLEN4<<2));
3827dd7cddfSDavid du Colombier }
3837dd7cddfSDavid du Colombier }
3847dd7cddfSDavid du Colombier
3857dd7cddfSDavid du Colombier /* route */
3867dd7cddfSDavid du Colombier if(notforme) {
3877dd7cddfSDavid du Colombier if(!ip->iprouting){
3887ec5746aSDavid du Colombier freeblist(bp);
3897dd7cddfSDavid du Colombier return;
3907dd7cddfSDavid du Colombier }
3913ff48bf5SDavid du Colombier
392a6a9e072SDavid du Colombier /* don't forward to source's network */
39384ba54caSDavid du Colombier memset(&conv, 0, sizeof conv);
394a6a9e072SDavid du Colombier conv.r = nil;
395a6a9e072SDavid du Colombier r = v4lookup(f, h->dst, &conv);
39604b73bddSDavid du Colombier if(r == nil || r->ifc == ifc){
39759cc4ca5SDavid du Colombier ip->stats[OutDiscards]++;
3987dd7cddfSDavid du Colombier freeblist(bp);
3997dd7cddfSDavid du Colombier return;
4007dd7cddfSDavid du Colombier }
4017dd7cddfSDavid du Colombier
4027dd7cddfSDavid du Colombier /* don't forward if packet has timed out */
4033ff48bf5SDavid du Colombier hop = h->ttl;
4043ff48bf5SDavid du Colombier if(hop < 1) {
40559cc4ca5SDavid du Colombier ip->stats[InHdrErrors]++;
4063ff48bf5SDavid du Colombier icmpttlexceeded(f, ifc->lifc->local, bp);
4077dd7cddfSDavid du Colombier freeblist(bp);
4087dd7cddfSDavid du Colombier return;
4097dd7cddfSDavid du Colombier }
4107dd7cddfSDavid du Colombier
4119a747e4fSDavid du Colombier /* reassemble if the interface expects it */
41204b73bddSDavid du Colombier if(r->ifc == nil) panic("nil route rfc");
4139a747e4fSDavid du Colombier if(r->ifc->reassemble){
4149a747e4fSDavid du Colombier frag = nhgets(h->frag);
4159a747e4fSDavid du Colombier if(frag) {
4169a747e4fSDavid du Colombier h->tos = 0;
4179a747e4fSDavid du Colombier if(frag & IP_MF)
4189a747e4fSDavid du Colombier h->tos = 1;
4193ff48bf5SDavid du Colombier bp = ip4reassemble(ip, frag, bp, h);
4209a747e4fSDavid du Colombier if(bp == nil)
4219a747e4fSDavid du Colombier return;
4223ff48bf5SDavid du Colombier h = (Ip4hdr*)(bp->rp);
4239a747e4fSDavid du Colombier }
4249a747e4fSDavid du Colombier }
4259a747e4fSDavid du Colombier
42659cc4ca5SDavid du Colombier ip->stats[ForwDatagrams]++;
4273ff48bf5SDavid du Colombier tos = h->tos;
4283ff48bf5SDavid du Colombier hop = h->ttl;
429a6a9e072SDavid du Colombier ipoput4(f, bp, 1, hop - 1, tos, &conv);
4307dd7cddfSDavid du Colombier return;
4317dd7cddfSDavid du Colombier }
4327dd7cddfSDavid du Colombier
4337dd7cddfSDavid du Colombier frag = nhgets(h->frag);
4347dd7cddfSDavid du Colombier if(frag) {
4357dd7cddfSDavid du Colombier h->tos = 0;
4367dd7cddfSDavid du Colombier if(frag & IP_MF)
4377dd7cddfSDavid du Colombier h->tos = 1;
4383ff48bf5SDavid du Colombier bp = ip4reassemble(ip, frag, bp, h);
4397dd7cddfSDavid du Colombier if(bp == nil)
4407dd7cddfSDavid du Colombier return;
4413ff48bf5SDavid du Colombier h = (Ip4hdr*)(bp->rp);
4427dd7cddfSDavid du Colombier }
4437dd7cddfSDavid du Colombier
444d9306527SDavid du Colombier /* don't let any frag info go up the stack */
445d9306527SDavid du Colombier h->frag[0] = 0;
446d9306527SDavid du Colombier h->frag[1] = 0;
447d9306527SDavid du Colombier
4483ff48bf5SDavid du Colombier proto = h->proto;
4493ff48bf5SDavid du Colombier p = Fsrcvpcol(f, proto);
4507dd7cddfSDavid du Colombier if(p != nil && p->rcv != nil) {
45159cc4ca5SDavid du Colombier ip->stats[InDelivers]++;
4529a747e4fSDavid du Colombier (*p->rcv)(p, ifc, bp);
4537dd7cddfSDavid du Colombier return;
4547dd7cddfSDavid du Colombier }
45559cc4ca5SDavid du Colombier ip->stats[InDiscards]++;
45659cc4ca5SDavid du Colombier ip->stats[InUnknownProtos]++;
4577dd7cddfSDavid du Colombier freeblist(bp);
4587dd7cddfSDavid du Colombier }
4597dd7cddfSDavid du Colombier
4607dd7cddfSDavid du Colombier int
ipstats(Fs * f,char * buf,int len)4617dd7cddfSDavid du Colombier ipstats(Fs *f, char *buf, int len)
4627dd7cddfSDavid du Colombier {
4637dd7cddfSDavid du Colombier IP *ip;
46459cc4ca5SDavid du Colombier char *p, *e;
46559cc4ca5SDavid du Colombier int i;
4667dd7cddfSDavid du Colombier
4677dd7cddfSDavid du Colombier ip = f->ip;
46859cc4ca5SDavid du Colombier ip->stats[DefaultTTL] = MAXTTL;
46959cc4ca5SDavid du Colombier
47059cc4ca5SDavid du Colombier p = buf;
47159cc4ca5SDavid du Colombier e = p+len;
4725e27dea9SDavid du Colombier for(i = 0; i < Nipstats; i++)
4735e27dea9SDavid du Colombier p = seprint(p, e, "%s: %llud\n", statnames[i], ip->stats[i]);
47459cc4ca5SDavid du Colombier return p - buf;
4757dd7cddfSDavid du Colombier }
4767dd7cddfSDavid du Colombier
4777dd7cddfSDavid du Colombier Block*
ip4reassemble(IP * ip,int offset,Block * bp,Ip4hdr * ih)4783ff48bf5SDavid du Colombier ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
4797dd7cddfSDavid du Colombier {
4807dd7cddfSDavid du Colombier int fend;
4817dd7cddfSDavid du Colombier ushort id;
4823ff48bf5SDavid du Colombier Fragment4 *f, *fnext;
4837dd7cddfSDavid du Colombier ulong src, dst;
4847dd7cddfSDavid du Colombier Block *bl, **l, *last, *prev;
4857dd7cddfSDavid du Colombier int ovlap, len, fragsize, pktposn;
4867dd7cddfSDavid du Colombier
4877dd7cddfSDavid du Colombier src = nhgetl(ih->src);
4887dd7cddfSDavid du Colombier dst = nhgetl(ih->dst);
4897dd7cddfSDavid du Colombier id = nhgets(ih->id);
4907dd7cddfSDavid du Colombier
4917dd7cddfSDavid du Colombier /*
4927dd7cddfSDavid du Colombier * block lists are too hard, pullupblock into a single block
4937dd7cddfSDavid du Colombier */
4947dd7cddfSDavid du Colombier if(bp->next){
4957dd7cddfSDavid du Colombier bp = pullupblock(bp, blocklen(bp));
4963ff48bf5SDavid du Colombier ih = (Ip4hdr*)(bp->rp);
4977dd7cddfSDavid du Colombier }
4987dd7cddfSDavid du Colombier
4993ff48bf5SDavid du Colombier qlock(&ip->fraglock4);
5007dd7cddfSDavid du Colombier
5017dd7cddfSDavid du Colombier /*
5027dd7cddfSDavid du Colombier * find a reassembly queue for this fragment
5037dd7cddfSDavid du Colombier */
5043ff48bf5SDavid du Colombier for(f = ip->flisthead4; f; f = fnext){
5053ff48bf5SDavid du Colombier fnext = f->next; /* because ipfragfree4 changes the list */
5067dd7cddfSDavid du Colombier if(f->src == src && f->dst == dst && f->id == id)
5077dd7cddfSDavid du Colombier break;
5083ff48bf5SDavid du Colombier if(f->age < NOW){
50959cc4ca5SDavid du Colombier ip->stats[ReasmTimeout]++;
5103ff48bf5SDavid du Colombier ipfragfree4(ip, f);
5117dd7cddfSDavid du Colombier }
5127dd7cddfSDavid du Colombier }
5137dd7cddfSDavid du Colombier
5147dd7cddfSDavid du Colombier /*
5157dd7cddfSDavid du Colombier * if this isn't a fragmented packet, accept it
5167dd7cddfSDavid du Colombier * and get rid of any fragments that might go
5177dd7cddfSDavid du Colombier * with it.
5187dd7cddfSDavid du Colombier */
5197dd7cddfSDavid du Colombier if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
5207dd7cddfSDavid du Colombier if(f != nil) {
5213ff48bf5SDavid du Colombier ipfragfree4(ip, f);
52259cc4ca5SDavid du Colombier ip->stats[ReasmFails]++;
5237dd7cddfSDavid du Colombier }
5243ff48bf5SDavid du Colombier qunlock(&ip->fraglock4);
5257dd7cddfSDavid du Colombier return bp;
5267dd7cddfSDavid du Colombier }
5277dd7cddfSDavid du Colombier
5287ec5746aSDavid du Colombier if(bp->base+IPFRAGSZ >= bp->rp){
5297ec5746aSDavid du Colombier bp = padblock(bp, IPFRAGSZ);
5307ec5746aSDavid du Colombier bp->rp += IPFRAGSZ;
5317dd7cddfSDavid du Colombier }
5327dd7cddfSDavid du Colombier
5337dd7cddfSDavid du Colombier BKFG(bp)->foff = offset<<3;
5343ff48bf5SDavid du Colombier BKFG(bp)->flen = nhgets(ih->length)-IP4HDR;
5357dd7cddfSDavid du Colombier
5367dd7cddfSDavid du Colombier /* First fragment allocates a reassembly queue */
5377dd7cddfSDavid du Colombier if(f == nil) {
5383ff48bf5SDavid du Colombier f = ipfragallo4(ip);
5397dd7cddfSDavid du Colombier f->id = id;
5407dd7cddfSDavid du Colombier f->src = src;
5417dd7cddfSDavid du Colombier f->dst = dst;
5427dd7cddfSDavid du Colombier
5437dd7cddfSDavid du Colombier f->blist = bp;
5447dd7cddfSDavid du Colombier
5453ff48bf5SDavid du Colombier qunlock(&ip->fraglock4);
54659cc4ca5SDavid du Colombier ip->stats[ReasmReqds]++;
5477dd7cddfSDavid du Colombier return nil;
5487dd7cddfSDavid du Colombier }
5497dd7cddfSDavid du Colombier
5507dd7cddfSDavid du Colombier /*
5517dd7cddfSDavid du Colombier * find the new fragment's position in the queue
5527dd7cddfSDavid du Colombier */
5537dd7cddfSDavid du Colombier prev = nil;
5547dd7cddfSDavid du Colombier l = &f->blist;
5557dd7cddfSDavid du Colombier bl = f->blist;
5567dd7cddfSDavid du Colombier while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
5577dd7cddfSDavid du Colombier prev = bl;
5587dd7cddfSDavid du Colombier l = &bl->next;
5597dd7cddfSDavid du Colombier bl = bl->next;
5607dd7cddfSDavid du Colombier }
5617dd7cddfSDavid du Colombier
5627dd7cddfSDavid du Colombier /* Check overlap of a previous fragment - trim away as necessary */
5637dd7cddfSDavid du Colombier if(prev) {
5647dd7cddfSDavid du Colombier ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
5657dd7cddfSDavid du Colombier if(ovlap > 0) {
5667dd7cddfSDavid du Colombier if(ovlap >= BKFG(bp)->flen) {
5677dd7cddfSDavid du Colombier freeblist(bp);
5683ff48bf5SDavid du Colombier qunlock(&ip->fraglock4);
5697dd7cddfSDavid du Colombier return nil;
5707dd7cddfSDavid du Colombier }
5717dd7cddfSDavid du Colombier BKFG(prev)->flen -= ovlap;
5727dd7cddfSDavid du Colombier }
5737dd7cddfSDavid du Colombier }
5747dd7cddfSDavid du Colombier
5757dd7cddfSDavid du Colombier /* Link onto assembly queue */
5767dd7cddfSDavid du Colombier bp->next = *l;
5777dd7cddfSDavid du Colombier *l = bp;
5787dd7cddfSDavid du Colombier
5797dd7cddfSDavid du Colombier /* Check to see if succeeding segments overlap */
5807dd7cddfSDavid du Colombier if(bp->next) {
5817dd7cddfSDavid du Colombier l = &bp->next;
5827dd7cddfSDavid du Colombier fend = BKFG(bp)->foff + BKFG(bp)->flen;
5837dd7cddfSDavid du Colombier /* Take completely covered segments out */
5847dd7cddfSDavid du Colombier while(*l) {
5857dd7cddfSDavid du Colombier ovlap = fend - BKFG(*l)->foff;
5867dd7cddfSDavid du Colombier if(ovlap <= 0)
5877dd7cddfSDavid du Colombier break;
5887dd7cddfSDavid du Colombier if(ovlap < BKFG(*l)->flen) {
5897dd7cddfSDavid du Colombier BKFG(*l)->flen -= ovlap;
5907dd7cddfSDavid du Colombier BKFG(*l)->foff += ovlap;
5917dd7cddfSDavid du Colombier /* move up ih hdrs */
5923ff48bf5SDavid du Colombier memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR);
5937dd7cddfSDavid du Colombier (*l)->rp += ovlap;
5947dd7cddfSDavid du Colombier break;
5957dd7cddfSDavid du Colombier }
5967dd7cddfSDavid du Colombier last = (*l)->next;
5977dd7cddfSDavid du Colombier (*l)->next = nil;
5987dd7cddfSDavid du Colombier freeblist(*l);
5997dd7cddfSDavid du Colombier *l = last;
6007dd7cddfSDavid du Colombier }
6017dd7cddfSDavid du Colombier }
6027dd7cddfSDavid du Colombier
6037dd7cddfSDavid du Colombier /*
6047dd7cddfSDavid du Colombier * look for a complete packet. if we get to a fragment
6057dd7cddfSDavid du Colombier * without IP_MF set, we're done.
6067dd7cddfSDavid du Colombier */
6077dd7cddfSDavid du Colombier pktposn = 0;
6087dd7cddfSDavid du Colombier for(bl = f->blist; bl; bl = bl->next) {
6097dd7cddfSDavid du Colombier if(BKFG(bl)->foff != pktposn)
6107dd7cddfSDavid du Colombier break;
6117dd7cddfSDavid du Colombier if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) {
6127dd7cddfSDavid du Colombier bl = f->blist;
6137dd7cddfSDavid du Colombier len = nhgets(BLKIP(bl)->length);
6147dd7cddfSDavid du Colombier bl->wp = bl->rp + len;
6157dd7cddfSDavid du Colombier
6167dd7cddfSDavid du Colombier /* Pullup all the fragment headers and
6177dd7cddfSDavid du Colombier * return a complete packet
6187dd7cddfSDavid du Colombier */
6197dd7cddfSDavid du Colombier for(bl = bl->next; bl; bl = bl->next) {
6207dd7cddfSDavid du Colombier fragsize = BKFG(bl)->flen;
6217dd7cddfSDavid du Colombier len += fragsize;
6223ff48bf5SDavid du Colombier bl->rp += IP4HDR;
6237dd7cddfSDavid du Colombier bl->wp = bl->rp + fragsize;
6247dd7cddfSDavid du Colombier }
6257dd7cddfSDavid du Colombier
6267dd7cddfSDavid du Colombier bl = f->blist;
6277dd7cddfSDavid du Colombier f->blist = nil;
6283ff48bf5SDavid du Colombier ipfragfree4(ip, f);
6297dd7cddfSDavid du Colombier ih = BLKIP(bl);
6307dd7cddfSDavid du Colombier hnputs(ih->length, len);
6313ff48bf5SDavid du Colombier qunlock(&ip->fraglock4);
63259cc4ca5SDavid du Colombier ip->stats[ReasmOKs]++;
6337dd7cddfSDavid du Colombier return bl;
6347dd7cddfSDavid du Colombier }
6357dd7cddfSDavid du Colombier pktposn += BKFG(bl)->flen;
6367dd7cddfSDavid du Colombier }
6373ff48bf5SDavid du Colombier qunlock(&ip->fraglock4);
6387dd7cddfSDavid du Colombier return nil;
6397dd7cddfSDavid du Colombier }
6407dd7cddfSDavid du Colombier
6417dd7cddfSDavid du Colombier /*
6423ff48bf5SDavid du Colombier * ipfragfree4 - Free a list of fragments - assume hold fraglock4
6437dd7cddfSDavid du Colombier */
6447dd7cddfSDavid du Colombier void
ipfragfree4(IP * ip,Fragment4 * frag)6453ff48bf5SDavid du Colombier ipfragfree4(IP *ip, Fragment4 *frag)
6467dd7cddfSDavid du Colombier {
6473ff48bf5SDavid du Colombier Fragment4 *fl, **l;
6487dd7cddfSDavid du Colombier
6497dd7cddfSDavid du Colombier if(frag->blist)
6507dd7cddfSDavid du Colombier freeblist(frag->blist);
6517dd7cddfSDavid du Colombier
6527dd7cddfSDavid du Colombier frag->src = 0;
6537dd7cddfSDavid du Colombier frag->id = 0;
6547dd7cddfSDavid du Colombier frag->blist = nil;
6557dd7cddfSDavid du Colombier
6563ff48bf5SDavid du Colombier l = &ip->flisthead4;
6577dd7cddfSDavid du Colombier for(fl = *l; fl; fl = fl->next) {
6587dd7cddfSDavid du Colombier if(fl == frag) {
6597dd7cddfSDavid du Colombier *l = frag->next;
6607dd7cddfSDavid du Colombier break;
6617dd7cddfSDavid du Colombier }
6627dd7cddfSDavid du Colombier l = &fl->next;
6637dd7cddfSDavid du Colombier }
6647dd7cddfSDavid du Colombier
6653ff48bf5SDavid du Colombier frag->next = ip->fragfree4;
6663ff48bf5SDavid du Colombier ip->fragfree4 = frag;
6677dd7cddfSDavid du Colombier
6687dd7cddfSDavid du Colombier }
6697dd7cddfSDavid du Colombier
6707dd7cddfSDavid du Colombier /*
6713ff48bf5SDavid du Colombier * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
6727dd7cddfSDavid du Colombier */
6733ff48bf5SDavid du Colombier Fragment4 *
ipfragallo4(IP * ip)6743ff48bf5SDavid du Colombier ipfragallo4(IP *ip)
6757dd7cddfSDavid du Colombier {
6763ff48bf5SDavid du Colombier Fragment4 *f;
6777dd7cddfSDavid du Colombier
6783ff48bf5SDavid du Colombier while(ip->fragfree4 == nil) {
6797dd7cddfSDavid du Colombier /* free last entry on fraglist */
6803ff48bf5SDavid du Colombier for(f = ip->flisthead4; f->next; f = f->next)
6817dd7cddfSDavid du Colombier ;
6823ff48bf5SDavid du Colombier ipfragfree4(ip, f);
6837dd7cddfSDavid du Colombier }
6843ff48bf5SDavid du Colombier f = ip->fragfree4;
6853ff48bf5SDavid du Colombier ip->fragfree4 = f->next;
6863ff48bf5SDavid du Colombier f->next = ip->flisthead4;
6873ff48bf5SDavid du Colombier ip->flisthead4 = f;
6883ff48bf5SDavid du Colombier f->age = NOW + 30000;
6897dd7cddfSDavid du Colombier
6907dd7cddfSDavid du Colombier return f;
6917dd7cddfSDavid du Colombier }
6927dd7cddfSDavid du Colombier
6937dd7cddfSDavid du Colombier ushort
ipcsum(uchar * addr)6947dd7cddfSDavid du Colombier ipcsum(uchar *addr)
6957dd7cddfSDavid du Colombier {
6967dd7cddfSDavid du Colombier int len;
6977dd7cddfSDavid du Colombier ulong sum;
6987dd7cddfSDavid du Colombier
6997dd7cddfSDavid du Colombier sum = 0;
7007dd7cddfSDavid du Colombier len = (addr[0]&0xf)<<2;
7017dd7cddfSDavid du Colombier
7027dd7cddfSDavid du Colombier while(len > 0) {
7037dd7cddfSDavid du Colombier sum += addr[0]<<8 | addr[1] ;
7047dd7cddfSDavid du Colombier len -= 2;
7057dd7cddfSDavid du Colombier addr += 2;
7067dd7cddfSDavid du Colombier }
7077dd7cddfSDavid du Colombier
7087dd7cddfSDavid du Colombier sum = (sum & 0xffff) + (sum >> 16);
7097dd7cddfSDavid du Colombier sum = (sum & 0xffff) + (sum >> 16);
7107dd7cddfSDavid du Colombier
7117dd7cddfSDavid du Colombier return (sum^0xffff);
7127dd7cddfSDavid du Colombier }
713