/* * make sure the first block has at least n bytes in its main body */ struct block *pullupqueue(struct queue *q, int n) { struct block *b; /* TODO: lock to protect the queue links? */ if ((BHLEN(q->bfirst) >= n)) return q->bfirst; q->bfirst = pullupblock(q->bfirst, n); for (b = q->bfirst; b != NULL && b->next != NULL; b = b->next) ; q->blast = b; return q->bfirst; }
static int pktipvers(Fs *f, Block **bpp) { if (*bpp == nil || BLEN(*bpp) == 0) { /* get enough to identify the IP version */ *bpp = pullupblock(*bpp, IP4HDR); if(*bpp == nil) { netlog(f, Logesp, "esp: short packet\n"); return 0; } } return (((Esp4hdr*)(*bpp)->rp)->vihl & 0xf0) == IP_VER4? V4: V6; }
/* * make sure the first block has at least n bytes */ Block* pullupqueue(Queue *q, int n) { Block *b; if(BLEN(q->bfirst) >= n) return q->bfirst; q->bfirst = pullupblock(q->bfirst, n); for(b = q->bfirst; b != nil && b->next != nil; b = b->next) ; q->blast = b; return q->bfirst; }
static void grekick(void *x, Block *bp) { Conv *c = x; GREhdr *ghp; uchar laddr[IPaddrlen], raddr[IPaddrlen]; if(bp == nil) return; /* Make space to fit ip header (gre header already there) */ bp = padblock(bp, GRE_IPONLY); if(bp == nil) return; /* make sure the message has a GRE header */ bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE); if(bp == nil) return; ghp = (GREhdr *)(bp->rp); ghp->vihl = IP_VER4; if(!((GREpriv*)c->p->priv)->raw){ v4tov6(raddr, ghp->dst); if(ipcmp(raddr, v4prefix) == 0) memmove(ghp->dst, c->raddr + IPv4off, IPv4addrlen); v4tov6(laddr, ghp->src); if(ipcmp(laddr, v4prefix) == 0){ if(ipcmp(c->laddr, IPnoaddr) == 0) findlocalip(c->p->f, c->laddr, raddr); /* pick interface closest to dest */ memmove(ghp->src, c->laddr + IPv4off, IPv4addrlen); } hnputs(ghp->eproto, c->rport); } ghp->proto = IP_GREPROTO; ghp->frag[0] = 0; ghp->frag[1] = 0; ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil); }
void udpkick(void *x, struct block *bp) { struct conv *c = x; Udp4hdr *uh4; Udp6hdr *uh6; uint16_t rport; uint8_t laddr[IPaddrlen], raddr[IPaddrlen]; Udpcb *ucb; int dlen, ptcllen; Udppriv *upriv; struct Fs *f; int version; struct conv *rc; upriv = c->p->priv; assert(upriv); f = c->p->f; netlog(c->p->f, Logudp, "udp: kick\n"); if (bp == NULL) return; ucb = (Udpcb *) c->ptcl; switch (ucb->headers) { case 7: /* get user specified addresses */ bp = pullupblock(bp, UDP_USEAD7); if (bp == NULL) return; ipmove(raddr, bp->rp); bp->rp += IPaddrlen; ipmove(laddr, bp->rp); bp->rp += IPaddrlen; /* pick interface closest to dest */ if (ipforme(f, laddr) != Runi) findlocalip(f, laddr, raddr); bp->rp += IPaddrlen; /* Ignore ifc address */ rport = nhgets(bp->rp); bp->rp += 2 + 2; /* Ignore local port */ break; case 6: /* get user specified addresses */ bp = pullupblock(bp, UDP_USEAD6); if (bp == NULL) return; ipmove(raddr, bp->rp); bp->rp += IPaddrlen; ipmove(laddr, bp->rp); bp->rp += IPaddrlen; /* pick interface closest to dest */ if (ipforme(f, laddr) != Runi) findlocalip(f, laddr, raddr); rport = nhgets(bp->rp); bp->rp += 2 + 2; /* Ignore local port */ break; default: rport = 0; break; } if (ucb->headers) { if (memcmp(laddr, v4prefix, IPv4off) == 0 || ipcmp(laddr, IPnoaddr) == 0) version = V4; else version = V6; } else { if ((memcmp(c->raddr, v4prefix, IPv4off) == 0 && memcmp(c->laddr, v4prefix, IPv4off) == 0) || ipcmp(c->raddr, IPnoaddr) == 0) version = V4; else version = V6; } dlen = blocklen(bp); /* fill in pseudo header and compute checksum */ switch (version) { case V4: bp = padblock(bp, UDP4_IPHDR_SZ + UDP_UDPHDR_SZ); if (bp == NULL) return; uh4 = (Udp4hdr *) (bp->rp); ptcllen = dlen + UDP_UDPHDR_SZ; uh4->Unused = 0; uh4->udpproto = IP_UDPPROTO; uh4->frag[0] = 0; uh4->frag[1] = 0; hnputs(uh4->udpplen, ptcllen); if (ucb->headers) { v6tov4(uh4->udpdst, raddr); hnputs(uh4->udpdport, rport); v6tov4(uh4->udpsrc, laddr); rc = NULL; } else { v6tov4(uh4->udpdst, c->raddr); hnputs(uh4->udpdport, c->rport); if (ipcmp(c->laddr, IPnoaddr) == 0) findlocalip(f, c->laddr, c->raddr); v6tov4(uh4->udpsrc, c->laddr); rc = c; } hnputs(uh4->udpsport, c->lport); hnputs(uh4->udplen, ptcllen); uh4->udpcksum[0] = 0; uh4->udpcksum[1] = 0; hnputs(uh4->udpcksum, ~ptclcsum(bp, UDP4_PHDR_OFF, UDP4_PHDR_SZ)); bp->checksum_start = UDP4_IPHDR_SZ; bp->checksum_offset = uh4->udpcksum - uh4->udpsport; bp->flag |= Budpck; uh4->vihl = IP_VER4; ipoput4(f, bp, 0, c->ttl, c->tos, rc); break; case V6: bp = padblock(bp, UDP6_IPHDR_SZ + UDP_UDPHDR_SZ); if (bp == NULL) return; // using the v6 ip header to create pseudo header // first then reset it to the normal ip header uh6 = (Udp6hdr *) (bp->rp); memset(uh6, 0, 8); ptcllen = dlen + UDP_UDPHDR_SZ; hnputl(uh6->viclfl, ptcllen); uh6->hoplimit = IP_UDPPROTO; if (ucb->headers) { ipmove(uh6->udpdst, raddr); hnputs(uh6->udpdport, rport); ipmove(uh6->udpsrc, laddr); rc = NULL; } else { ipmove(uh6->udpdst, c->raddr); hnputs(uh6->udpdport, c->rport); if (ipcmp(c->laddr, IPnoaddr) == 0) findlocalip(f, c->laddr, c->raddr); ipmove(uh6->udpsrc, c->laddr); rc = c; } hnputs(uh6->udpsport, c->lport); hnputs(uh6->udplen, ptcllen); uh6->udpcksum[0] = 0; uh6->udpcksum[1] = 0; hnputs(uh6->udpcksum, ptclcsum(bp, UDP6_PHDR_OFF, dlen + UDP_UDPHDR_SZ + UDP6_PHDR_SZ)); memset(uh6, 0, 8); uh6->viclfl[0] = IP_VER6; hnputs(uh6->len, ptcllen); uh6->nextheader = IP_UDPPROTO; ipoput6(f, bp, 0, c->ttl, c->tos, rc); break; default: panic("udpkick: version %d", version); } upriv->ustats.udpOutDatagrams++; }
/* * decapsulate IP packet from IP/ESP packet in bp and * pass the result up the spi's Conv's read queue. */ void espiput(Proto *esp, Ipifc *ipifc, Block *bp) { Mach *m = machp(); int payload, nexthdr; uint8_t *auth, *espspi; Conv *c; Espcb *ecb; Esptail *et; Fs *f; Userhdr *uh; Versdep vers; f = esp->f; getverslens(pktipvers(f, &bp), &vers); bp = pullupblock(bp, vers.hdrlen + Esptaillen); if(bp == nil) { netlog(f, Logesp, "esp: short packet\n"); return; } getpktspiaddrs(bp->rp, &vers); qlock(esp); /* Look for a conversation structure for this port */ c = convlookup(esp, vers.spi); if(c == nil) { qunlock(esp); netlog(f, Logesp, "esp: no conv %I -> %I!%lud\n", vers.raddr, vers.laddr, vers.spi); icmpnoconv(f, bp); freeblist(bp); return; } qlock(c); qunlock(esp); ecb = c->ptcl; /* too hard to do decryption/authentication on block lists */ if(bp->next) bp = concatblock(bp); if(BLEN(bp) < vers.hdrlen + ecb->espivlen + Esptaillen + ecb->ahlen) { qunlock(c); netlog(f, Logesp, "esp: short block %I -> %I!%lud\n", vers.raddr, vers.laddr, vers.spi); freeb(bp); return; } auth = bp->wp - ecb->ahlen; espspi = vers.version == V4? ((Esp4hdr*)bp->rp)->espspi: ((Esp6hdr*)bp->rp)->espspi; /* compute secure hash and authenticate */ if(!ecb->auth(ecb, espspi, auth - espspi, auth)) { qunlock(c); print("esp: bad auth %I -> %I!%ld\n", vers.raddr, vers.laddr, vers.spi); netlog(f, Logesp, "esp: bad auth %I -> %I!%lud\n", vers.raddr, vers.laddr, vers.spi); freeb(bp); return; } payload = BLEN(bp) - vers.hdrlen - ecb->ahlen; if(payload <= 0 || payload % 4 != 0 || payload % ecb->espblklen != 0) { qunlock(c); netlog(f, Logesp, "esp: bad length %I -> %I!%lud payload=%d BLEN=%lud\n", vers.raddr, vers.laddr, vers.spi, payload, BLEN(bp)); freeb(bp); return; } /* decrypt payload */ if(!ecb->cipher(ecb, bp->rp + vers.hdrlen, payload)) { qunlock(c); print("esp: cipher failed %I -> %I!%ld: %s\n", vers.raddr, vers.laddr, vers.spi, m->externup->errstr); netlog(f, Logesp, "esp: cipher failed %I -> %I!%lud: %s\n", vers.raddr, vers.laddr, vers.spi, m->externup->errstr); freeb(bp); return; } payload -= Esptaillen; et = (Esptail*)(bp->rp + vers.hdrlen + payload); payload -= et->pad + ecb->espivlen; nexthdr = et->nexthdr; if(payload <= 0) { qunlock(c); netlog(f, Logesp, "esp: short packet after decrypt %I -> %I!%lud\n", vers.raddr, vers.laddr, vers.spi); freeb(bp); return; } /* trim packet */ bp->rp += vers.hdrlen + ecb->espivlen; /* toss original IP & ESP hdrs */ bp->wp = bp->rp + payload; if(ecb->header) { /* assume Userhdrlen < Esp4hdrlen < Esp6hdrlen */ bp->rp -= Userhdrlen; uh = (Userhdr*)bp->rp; memset(uh, 0, Userhdrlen); uh->nexthdr = nexthdr; } /* ingress filtering here? */ if(qfull(c->rq)){ netlog(f, Logesp, "esp: qfull %I -> %I.%uld\n", vers.raddr, vers.laddr, vers.spi); freeblist(bp); }else { // print("esp: pass up: %uld\n", BLEN(bp)); qpass(c->rq, bp); /* pass packet up the read queue */ } qunlock(c); }
/* * encapsulate next IP packet on x's write queue in IP/ESP packet * and initiate output of the result. */ static void espkick(void *x) { int nexthdr, payload, pad, align; uint8_t *auth; Block *bp; Conv *c = x; Esp4hdr *eh4; Esp6hdr *eh6; Espcb *ecb; Esptail *et; Userhdr *uh; Versdep vers; getverslens(convipvers(c), &vers); bp = qget(c->wq); if(bp == nil) return; qlock(c); ecb = c->ptcl; if(ecb->header) { /* make sure the message has a User header */ bp = pullupblock(bp, Userhdrlen); if(bp == nil) { qunlock(c); return; } uh = (Userhdr*)bp->rp; nexthdr = uh->nexthdr; bp->rp += Userhdrlen; } else { nexthdr = 0; /* what should this be? */ } payload = BLEN(bp) + ecb->espivlen; /* Make space to fit ip header */ bp = padblock(bp, vers.hdrlen + ecb->espivlen); getpktspiaddrs(bp->rp, &vers); align = 4; if(ecb->espblklen > align) align = ecb->espblklen; if(align % ecb->ahblklen != 0) panic("espkick: ahblklen is important after all"); pad = (align-1) - (payload + Esptaillen-1)%align; /* * Make space for tail * this is done by calling padblock with a negative size * Padblock does not change bp->wp! */ bp = padblock(bp, -(pad+Esptaillen+ecb->ahlen)); bp->wp += pad+Esptaillen+ecb->ahlen; et = (Esptail*)(bp->rp + vers.hdrlen + payload + pad); /* fill in tail */ et->pad = pad; et->nexthdr = nexthdr; /* encrypt the payload */ ecb->cipher(ecb, bp->rp + vers.hdrlen, payload + pad + Esptaillen); auth = bp->rp + vers.hdrlen + payload + pad + Esptaillen; /* fill in head; construct a new IP header and an ESP header */ if (vers.version == V4) { eh4 = (Esp4hdr *)bp->rp; eh4->vihl = IP_VER4; v6tov4(eh4->espsrc, c->laddr); v6tov4(eh4->espdst, c->raddr); eh4->espproto = IP_ESPPROTO; eh4->frag[0] = 0; eh4->frag[1] = 0; hnputl(eh4->espspi, ecb->spi); hnputl(eh4->espseq, ++ecb->seq); } else { eh6 = (Esp6hdr *)bp->rp; eh6->vcf[0] = IP_VER6; ipmove(eh6->src, c->laddr); ipmove(eh6->dst, c->raddr); eh6->proto = IP_ESPPROTO; hnputl(eh6->espspi, ecb->spi); hnputl(eh6->espseq, ++ecb->seq); } /* compute secure hash */ ecb->auth(ecb, bp->rp + vers.iphdrlen, (vers.hdrlen - vers.iphdrlen) + payload + pad + Esptaillen, auth); qunlock(c); /* print("esp: pass down: %uld\n", BLEN(bp)); */ if (vers.version == V4) ipoput4(c->p->f, bp, 0, c->ttl, c->tos, c); else ipoput6(c->p->f, bp, 0, c->ttl, c->tos, c); }
void rudpkick(void *x) { Proc *up = externup(); Conv *c = x; Udphdr *uh; uint16_t rport; uint8_t laddr[IPaddrlen], raddr[IPaddrlen]; Block *bp; Rudpcb *ucb; Rudphdr *rh; Reliable *r; int dlen, ptcllen; Rudppriv *upriv; Fs *f; upriv = c->p->priv; f = c->p->f; netlog(c->p->f, Logrudp, "rudp: kick\n"); bp = qget(c->wq); if(bp == nil) return; ucb = (Rudpcb*)c->ptcl; switch(ucb->headers) { case 7: /* get user specified addresses */ bp = pullupblock(bp, UDP_USEAD7); if(bp == nil) return; ipmove(raddr, bp->rp); bp->rp += IPaddrlen; ipmove(laddr, bp->rp); bp->rp += IPaddrlen; /* pick interface closest to dest */ if(ipforme(f, laddr) != Runi) findlocalip(f, laddr, raddr); bp->rp += IPaddrlen; /* Ignore ifc address */ rport = nhgets(bp->rp); bp->rp += 2+2; /* Ignore local port */ break; default: ipmove(raddr, c->raddr); ipmove(laddr, c->laddr); rport = c->rport; break; } dlen = blocklen(bp); /* Make space to fit rudp & ip header */ bp = padblock(bp, UDP_IPHDR+UDP_RHDRSIZE); if(bp == nil) return; uh = (Udphdr *)(bp->rp); uh->vihl = IP_VER4; rh = (Rudphdr*)uh; ptcllen = dlen + (UDP_RHDRSIZE-UDP_PHDRSIZE); uh->Unused = 0; uh->udpproto = IP_UDPPROTO; uh->frag[0] = 0; uh->frag[1] = 0; hnputs(uh->udpplen, ptcllen); switch(ucb->headers){ case 7: v6tov4(uh->udpdst, raddr); hnputs(uh->udpdport, rport); v6tov4(uh->udpsrc, laddr); break; default: v6tov4(uh->udpdst, c->raddr); hnputs(uh->udpdport, c->rport); if(ipcmp(c->laddr, IPnoaddr) == 0) findlocalip(f, c->laddr, c->raddr); v6tov4(uh->udpsrc, c->laddr); break; } hnputs(uh->udpsport, c->lport); hnputs(uh->udplen, ptcllen); uh->udpcksum[0] = 0; uh->udpcksum[1] = 0; qlock(&ucb->ql); r = relstate(ucb, raddr, rport, "kick"); r->sndseq = NEXTSEQ(r->sndseq); hnputl(rh->relseq, r->sndseq); hnputl(rh->relsgen, r->sndgen); hnputl(rh->relack, r->rcvseq); /* ACK last rcvd packet */ hnputl(rh->relagen, r->rcvgen); if(r->rcvseq != r->acksent) r->acksent = r->rcvseq; hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, dlen+UDP_RHDRSIZE)); relackq(r, bp); qunlock(&ucb->ql); upriv->ustats.rudpOutDatagrams++; DPRINT("sent: %lud/%lud, %lud/%lud\n", r->sndseq, r->sndgen, r->rcvseq, r->rcvgen); doipoput(c, f, bp, 0, c->ttl, c->tos); if(waserror()) { relput(r); qunlock(&r->lock); nexterror(); } /* flow control of sorts */ qlock(&r->lock); if(UNACKED(r) > Maxunacked){ r->blocked = 1; sleep(&r->vous, flow, r); r->blocked = 0; } qunlock(&r->lock); relput(r); poperror(); }
void espiput(Proto *esp, Ipifc*, Block *bp) { Esphdr *eh; Esptail *et; Userhdr *uh; Conv *c; Espcb *ecb; uchar raddr[IPaddrlen], laddr[IPaddrlen]; Fs *f; uchar *auth; ulong spi; int payload, nexthdr; f = esp->f; bp = pullupblock(bp, EsphdrSize+EsptailSize); if(bp == nil) { netlog(f, Logesp, "esp: short packet\n"); return; } eh = (Esphdr*)(bp->rp); spi = nhgetl(eh->espspi); v4tov6(raddr, eh->espsrc); v4tov6(laddr, eh->espdst); qlock(esp); /* Look for a conversation structure for this port */ c = convlookup(esp, spi); if(c == nil) { qunlock(esp); netlog(f, Logesp, "esp: no conv %I -> %I!%d\n", raddr, laddr, spi); icmpnoconv(f, bp); freeblist(bp); return; } qlock(c); qunlock(esp); ecb = c->ptcl; // too hard to do decryption/authentication on block lists if(bp->next) bp = concatblock(bp); if(BLEN(bp) < EsphdrSize + ecb->espivlen + EsptailSize + ecb->ahlen) { qunlock(c); netlog(f, Logesp, "esp: short block %I -> %I!%d\n", raddr, laddr, spi); freeb(bp); return; } eh = (Esphdr*)(bp->rp); auth = bp->wp - ecb->ahlen; if(!ecb->auth(ecb, eh->espspi, auth-eh->espspi, auth)) { qunlock(c); print("esp: bad auth %I -> %I!%ld\n", raddr, laddr, spi); netlog(f, Logesp, "esp: bad auth %I -> %I!%d\n", raddr, laddr, spi); freeb(bp); return; } payload = BLEN(bp)-EsphdrSize-ecb->ahlen; if(payload<=0 || payload%4 != 0 || payload%ecb->espblklen!=0) { qunlock(c); netlog(f, Logesp, "esp: bad length %I -> %I!%d payload=%d BLEN=%d\n", raddr, laddr, spi, payload, BLEN(bp)); freeb(bp); return; } if(!ecb->cipher(ecb, bp->rp+EsphdrSize, payload)) { qunlock(c); print("esp: cipher failed %I -> %I!%ld: %r\n", raddr, laddr, spi); netlog(f, Logesp, "esp: cipher failed %I -> %I!%d: %r\n", raddr, laddr, spi); freeb(bp); return; } payload -= EsptailSize; et = (Esptail*)(bp->rp + EsphdrSize + payload); payload -= et->pad + ecb->espivlen; nexthdr = et->nexthdr; if(payload <= 0) { qunlock(c); netlog(f, Logesp, "esp: short packet after decrypt %I -> %I!%d\n", raddr, laddr, spi); freeb(bp); return; } // trim packet bp->rp += EsphdrSize + ecb->espivlen; bp->wp = bp->rp + payload; if(ecb->header) { // assume UserhdrSize < EsphdrSize bp->rp -= UserhdrSize; uh = (Userhdr*)bp->rp; memset(uh, 0, UserhdrSize); uh->nexthdr = nexthdr; } if(qfull(c->rq)){ netlog(f, Logesp, "esp: qfull %I -> %I.%uld\n", raddr, laddr, spi); freeblist(bp); }else { //print("esp: pass up: %uld\n", BLEN(bp)); qpass(c->rq, bp); } qunlock(c); }
static void espkick(void *x) { Conv *c = x; Esphdr *eh; Esptail *et; Userhdr *uh; Espcb *ecb; Block *bp; int nexthdr; int payload; int pad; int align; uchar *auth; bp = qget(c->wq); if(bp == nil) return; qlock(c); ecb = c->ptcl; if(ecb->header) { /* make sure the message has a User header */ bp = pullupblock(bp, UserhdrSize); if(bp == nil) { qunlock(c); return; } uh = (Userhdr*)bp->rp; nexthdr = uh->nexthdr; bp->rp += UserhdrSize; } else { nexthdr = 0; // what should this be? } payload = BLEN(bp) + ecb->espivlen; /* Make space to fit ip header */ bp = padblock(bp, EsphdrSize + ecb->espivlen); align = 4; if(ecb->espblklen > align) align = ecb->espblklen; if(align % ecb->ahblklen != 0) panic("espkick: ahblklen is important after all"); pad = (align-1) - (payload + EsptailSize-1)%align; /* * Make space for tail * this is done by calling padblock with a negative size * Padblock does not change bp->wp! */ bp = padblock(bp, -(pad+EsptailSize+ecb->ahlen)); bp->wp += pad+EsptailSize+ecb->ahlen; eh = (Esphdr *)(bp->rp); et = (Esptail*)(bp->rp + EsphdrSize + payload + pad); // fill in tail et->pad = pad; et->nexthdr = nexthdr; ecb->cipher(ecb, bp->rp+EsphdrSize, payload+pad+EsptailSize); auth = bp->rp + EsphdrSize + payload + pad + EsptailSize; // fill in head eh->vihl = IP_VER4; hnputl(eh->espspi, ecb->spi); hnputl(eh->espseq, ++ecb->seq); v6tov4(eh->espsrc, c->laddr); v6tov4(eh->espdst, c->raddr); eh->espproto = IP_ESPPROTO; eh->frag[0] = 0; eh->frag[1] = 0; ecb->auth(ecb, bp->rp+IphdrSize, (EsphdrSize-IphdrSize)+payload+pad+EsptailSize, auth); qunlock(c); //print("esp: pass down: %uld\n", BLEN(bp)); ipoput4(c->p->f, bp, 0, c->ttl, c->tos, c); }