/* * trim to len bytes starting at offset */ struct block *trimblock(struct block *bp, int offset, int len) { uint32_t l, trim; int olen = len; QDEBUG checkb(bp, "trimblock 1"); if (blocklen(bp) < offset + len) { freeblist(bp); return NULL; } l =_pullblock(&bp, offset, 0); if (bp == NULL) return NULL; if (l != offset) { freeblist(bp); return NULL; } while ((l = BLEN(bp)) < len) { len -= l; bp = bp->next; } trim = BLEN(bp) - len; trim -= dropext(bp, trim); bp->wp -= trim; if (bp->next) { freeblist(bp->next); bp->next = NULL; } return bp; }
void udpadvise(Proto *udp, Block *bp, char *msg) { Udp4hdr *h4; Udp6hdr *h6; uchar source[IPaddrlen], dest[IPaddrlen]; ushort psource, pdest; Conv *s, **p; int version; h4 = (Udp4hdr*)(bp->rp); version = ((h4->vihl&0xF0)==IP_VER6) ? 6 : 4; switch(version) { case V4: v4tov6(dest, h4->udpdst); v4tov6(source, h4->udpsrc); psource = nhgets(h4->udpsport); pdest = nhgets(h4->udpdport); break; case V6: h6 = (Udp6hdr*)(bp->rp); ipmove(dest, h6->udpdst); ipmove(source, h6->udpsrc); psource = nhgets(h6->udpsport); pdest = nhgets(h6->udpdport); break; default: panic("udpadvise: version %d", version); return; /* to avoid a warning */ } /* Look for a connection */ qlock(udp); for(p = udp->conv; *p; p++) { s = *p; if(s->rport == pdest) if(s->lport == psource) if(ipcmp(s->raddr, dest) == 0) if(ipcmp(s->laddr, source) == 0){ if(s->ignoreadvice) break; qlock(s); qunlock(udp); qhangup(s->rq, msg); qhangup(s->wq, msg); qunlock(s); freeblist(bp); return; } } qunlock(udp); freeblist(bp); }
int qpass(struct queue *q, struct block *b) { int dlen, len, dowakeup; /* sync with qread */ dowakeup = 0; spin_lock_irqsave(&q->lock); if (q->len >= q->limit) { freeblist(b); spin_unlock_irqsave(&q->lock); return -1; } if (q->state & Qclosed) { len = blocklen(b); freeblist(b); spin_unlock_irqsave(&q->lock); return len; } /* add buffer to queue */ if (q->bfirst) q->blast->next = b; else q->bfirst = b; len = BALLOC(b); dlen = BLEN(b); QDEBUG checkb(b, "qpass"); while (b->next) { b = b->next; QDEBUG checkb(b, "qpass"); len += BALLOC(b); dlen += BLEN(b); } q->blast = b; q->len += len; q->dlen += dlen; if (q->len >= q->limit / 2) q->state |= Qflow; if (q->state & Qstarve) { q->state &= ~Qstarve; dowakeup = 1; } spin_unlock_irqsave(&q->lock); if (dowakeup) { rendez_wakeup(&q->rr); qwake_cb(q, FDTAP_FILT_READABLE); } return len; }
int qpass(Queue *q, Block *b) { int dlen, len, dowakeup; /* sync with qread */ dowakeup = 0; ilock(q); if(q->len >= q->limit){ freeblist(b); iunlock(q); return -1; } if(q->state & Qclosed){ len = BALLOC(b); freeblist(b); iunlock(q); return len; } /* add buffer to queue */ if(q->bfirst) q->blast->next = b; else q->bfirst = b; len = BALLOC(b); dlen = BLEN(b); QDEBUG checkb(b, "qpass"); while(b->next){ b = b->next; QDEBUG checkb(b, "qpass"); len += BALLOC(b); dlen += BLEN(b); } q->blast = b; q->len += len; q->dlen += dlen; if(q->len >= q->limit/2) q->state |= Qflow; if(q->state & Qstarve){ q->state &= ~Qstarve; dowakeup = 1; } iunlock(q); if(dowakeup) wakeup(&q->rr); return len; }
Block* adjustblock(Block* bp, int len) { int n; Block *nbp; if(len < 0){ freeb(bp); return nil; } if(bp->rp+len > bp->lim){ nbp = copyblock(bp, len); freeblist(bp); QDEBUG checkb(nbp, "adjustblock 1"); return nbp; } n = BLEN(bp); if(len > n) memset(bp->wp, 0, len-n); bp->wp = bp->rp+len; QDEBUG checkb(bp, "adjustblock 2"); return bp; }
void rudpadvise(Proto *rudp, Block *bp, char *msg) { Udphdr *h; uint8_t source[IPaddrlen], dest[IPaddrlen]; uint16_t psource, pdest; Conv *s, **p; h = (Udphdr*)(bp->rp); v4tov6(dest, h->udpdst); v4tov6(source, h->udpsrc); psource = nhgets(h->udpsport); pdest = nhgets(h->udpdport); /* Look for a connection */ for(p = rudp->conv; *p; p++) { s = *p; if(s->rport == pdest) if(s->lport == psource) if(ipcmp(s->raddr, dest) == 0) if(ipcmp(s->laddr, source) == 0){ qhangup(s->rq, msg); qhangup(s->wq, msg); break; } } freeblist(bp); }
/* * copy the contents of memory into a string of blocks. * return nil on error. */ Block* mem2bl(uchar *p, int len) { int n; Block *b, *first, **l; first = nil; l = &first; if(waserror()){ freeblist(first); nexterror(); } do { n = len; if(n > Maxatomic) n = Maxatomic; *l = b = allocb(n); setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]); memmove(b->wp, p, n); b->wp += n; p += n; len -= n; l = &b->next; } while(len > 0); poperror(); return first; }
/* * copy the contents of memory into a string of blocks. * return NULL on error. */ struct block *mem2bl(uint8_t * p, int len) { ERRSTACK(1); int n; struct block *b, *first, **l; first = NULL; l = &first; if (waserror()) { freeblist(first); nexterror(); } do { n = len; if (n > Maxatomic) n = Maxatomic; *l = b = block_alloc(n, MEM_WAIT); /* TODO consider extra_data */ memmove(b->wp, p, n); b->wp += n; p += n; len -= n; l = &b->next; } while (len > 0); poperror(); return first; }
/* * Mark a queue as closed. No further IO is permitted. * All blocks are released. */ void qclose(struct queue *q) { struct block *bfirst; if (q == NULL) return; /* mark it */ spin_lock_irqsave(&q->lock); q->state |= Qclosed; q->state &= ~(Qflow | Qstarve | Qdropoverflow); q->err[0] = 0; bfirst = q->bfirst; q->bfirst = 0; q->len = 0; q->dlen = 0; spin_unlock_irqsave(&q->lock); /* free queued blocks */ freeblist(bfirst); /* wake up readers/writers */ rendez_wakeup(&q->rr); rendez_wakeup(&q->wr); qwake_cb(q, FDTAP_FILT_HANGUP); }
/* * Mark a queue as closed. No further IO is permitted. * All blocks are released. */ void qclose(Queue *q) { Block *bfirst; if(q == nil) return; /* mark it */ ilock(q); q->state |= Qclosed; q->state &= ~(Qflow|Qstarve); strcpy(q->err, Ehungup); bfirst = q->bfirst; q->bfirst = 0; q->len = 0; q->dlen = 0; q->noblock = 0; iunlock(q); /* free queued blocks */ freeblist(bfirst); /* wake up readers/writers */ wakeup(&q->rr); wakeup(&q->wr); }
void releaseuio(Uio *uio) { if (uio->dest) { freeblist(uio->dest); } free(uio); }
/* * randomly don't send packets */ static void doipoput(Conv *c, Fs *f, Block *bp, int x, int ttl, int tos) { Rudpcb *ucb; ucb = (Rudpcb*)c->ptcl; if(ucb->randdrop && nrand(100) < ucb->randdrop) freeblist(bp); else ipoput4(f, bp, x, ttl, tos, nil); }
/* * trim to len bytes starting at offset */ Block * trimblock(Block *bp, int offset, int len) { ulong l; Block *nb, *startb; QDEBUG checkb(bp, "trimblock 1"); if(blocklen(bp) < offset+len) { freeblist(bp); return nil; } while((l = BLEN(bp)) < offset) { offset -= l; nb = bp->next; bp->next = nil; freeb(bp); bp = nb; } startb = bp; bp->rp += offset; while((l = BLEN(bp)) < len) { len -= l; bp = bp->next; } bp->wp -= (BLEN(bp) - len); if(bp->next) { freeblist(bp->next); bp->next = nil; } return startb; }
/* * make sure the first block has at least n bytes. If we started with * less than n bytes, make sure we have exactly n bytes. devssl.c depends * on this. */ Block* pullupblock(Block *bp, int n) { int i; Block *nbp; /* * this should almost always be true, the rest it * just to avoid every caller checking. */ if(BLEN(bp) >= n) return bp; /* * if not enough room in the first block, * add another to the front of the list. */ if(bp->lim - bp->rp < n){ nbp = allocb(n); nbp->next = bp; bp = nbp; } /* * copy bytes from the trailing blocks into the first */ n -= BLEN(bp); while(nbp = bp->next){ i = BLEN(nbp); if(i > n) { memmove(bp->wp, nbp->rp, n); bp->wp += n; nbp->rp += n; return bp; } else { memmove(bp->wp, nbp->rp, i); bp->wp += i; bp->next = nbp->next; nbp->next = 0; freeb(nbp); n -= i; if(n == 0) return bp; } } freeblist(bp); return 0; }
/* Throw away the next 'len' bytes in the queue returning the number actually * discarded. * * If the bytes are in the queue, then they must be discarded. The only time to * return less than len is if the q itself has less than len bytes. */ size_t qdiscard(struct queue *q, size_t len) { struct block *blist; size_t removed_amt; size_t sofar = 0; /* This is racy. There could be multiple qdiscarders or other consumers, * where the consumption could be interleaved. */ while (qlen(q) && len) { blist = __qbread(q, len, 0, MEM_WAIT); removed_amt = freeblist(blist); sofar += removed_amt; len -= removed_amt; } return sofar; }
/* * copy the string of blocks into * a single block and free the string */ Block* concatblock(Block *bp) { int len; Block *nb, *f; if(bp->next == 0) return bp; nb = allocb(blocklen(bp)); for(f = bp; f; f = f->next) { len = BLEN(f); memmove(nb->wp, f->rp, len); nb->wp += len; } freeblist(bp); return nb; }
/* called from icmp(v6) for unreachable hosts, time exceeded, etc. */ void espadvise(Proto *esp, Block *bp, char *msg) { Conv *c; Versdep vers; getverslens(pktipvers(esp->f, &bp), &vers); getpktspiaddrs(bp->rp, &vers); qlock(esp); c = convlookup(esp, vers.spi); if(c != nil) { qhangup(c->rq, msg); qhangup(c->wq, msg); } qunlock(esp); freeblist(bp); }
/* * flush the output queue */ void qflush(Queue *q) { Block *bfirst; /* mark it */ ilock(q); bfirst = q->bfirst; q->bfirst = 0; q->len = 0; q->dlen = 0; iunlock(q); /* free queued blocks */ freeblist(bfirst); /* wake up readers/writers */ wakeup(&q->wr); }
/* * flush the output queue */ void qflush(struct queue *q) { struct block *bfirst; /* mark it */ spin_lock_irqsave(&q->lock); bfirst = q->bfirst; q->bfirst = 0; q->len = 0; q->dlen = 0; spin_unlock_irqsave(&q->lock); /* free queued blocks */ freeblist(bfirst); /* wake up writers */ rendez_wakeup(&q->wr); qwake_cb(q, FDTAP_FILT_WRITABLE); }
void espadvise(Proto *esp, Block *bp, char *msg) { Esphdr *h; Conv *c; ulong spi; h = (Esphdr*)(bp->rp); spi = nhgets(h->espspi); qlock(esp); c = convlookup(esp, spi); if(c != nil) { qhangup(c->rq, msg); qhangup(c->wq, msg); } qunlock(esp); freeblist(bp); }
/* * copy the string of blocks into * a single block and free the string */ Block* concatblock(Block *bp) { int len; Block *nb, *f; if(bp->next == nil) return bp; nb = allocb(blocklen(bp)); for(f = bp; f != nil; f = f->next) { len = BLEN(f); memmove(nb->wp, f->rp, len); nb->wp += len; } concatblockcnt += BLEN(nb); freeblist(bp); QDEBUG checkb(nb, "concatblock 1"); return nb; }
/* * copy the string of blocks into * a single block and free the string */ struct block *concatblock(struct block *bp) { int len; struct block *nb, *f; if (bp->next == 0) return bp; /* probably use parts of qclone */ PANIC_EXTRA(bp); nb = block_alloc(blocklen(bp), MEM_WAIT); for (f = bp; f; f = f->next) { len = BLEN(f); memmove(nb->wp, f->rp, len); nb->wp += len; } concatblockcnt += BLEN(nb); freeblist(bp); QDEBUG checkb(nb, "concatblock 1"); return nb; }
/* * called with the Loop qlocked, * so only pushlink can mess with the queues */ static void closelink(Link *link, int dofree) { Queue *iq, *oq; Block *bp; ilock(link); iq = link->iq; oq = link->oq; bp = link->tq; link->tq = nil; link->tqtail = nil; link->tout = 0; link->tin = 0; timerdel(&link->ci); iunlock(link); if(iq != nil){ qclose(iq); if(dofree){ ilock(link); free(iq); link->iq = nil; iunlock(link); } } if(oq != nil){ qclose(oq); if(dofree){ ilock(link); free(oq); link->oq = nil; iunlock(link); } } freeblist(bp); }
void udpiput(struct Proto *udp, struct Ipifc *ifc, struct block *bp) { int len; Udp4hdr *uh4; Udp6hdr *uh6; struct conv *c; Udpcb *ucb; uint8_t raddr[IPaddrlen], laddr[IPaddrlen]; uint16_t rport, lport; Udppriv *upriv; struct Fs *f; int version; int ottl, oviclfl, olen; uint8_t *p; upriv = udp->priv; f = udp->f; upriv->ustats.udpInDatagrams++; uh4 = (Udp4hdr *) (bp->rp); version = ((uh4->vihl & 0xF0) == IP_VER6) ? V6 : V4; /* * Put back pseudo header for checksum * (remember old values for icmpnoconv()) */ switch (version) { case V4: ottl = uh4->Unused; uh4->Unused = 0; len = nhgets(uh4->udplen); olen = nhgets(uh4->udpplen); hnputs(uh4->udpplen, len); v4tov6(raddr, uh4->udpsrc); v4tov6(laddr, uh4->udpdst); lport = nhgets(uh4->udpdport); rport = nhgets(uh4->udpsport); if (!(bp->flag & Budpck) && (uh4->udpcksum[0] || uh4->udpcksum[1]) && ptclcsum(bp, UDP4_PHDR_OFF, len + UDP4_PHDR_SZ)) { upriv->ustats.udpInErrors++; netlog(f, Logudp, "udp: checksum error %I\n", raddr); printd("udp: checksum error %I\n", raddr); freeblist(bp); return; } uh4->Unused = ottl; hnputs(uh4->udpplen, olen); break; case V6: uh6 = (Udp6hdr *) (bp->rp); len = nhgets(uh6->udplen); oviclfl = nhgetl(uh6->viclfl); olen = nhgets(uh6->len); ottl = uh6->hoplimit; ipmove(raddr, uh6->udpsrc); ipmove(laddr, uh6->udpdst); lport = nhgets(uh6->udpdport); rport = nhgets(uh6->udpsport); memset(uh6, 0, 8); hnputl(uh6->viclfl, len); uh6->hoplimit = IP_UDPPROTO; if (ptclcsum(bp, UDP6_PHDR_OFF, len + UDP6_PHDR_SZ)) { upriv->ustats.udpInErrors++; netlog(f, Logudp, "udp: checksum error %I\n", raddr); printd("udp: checksum error %I\n", raddr); freeblist(bp); return; } hnputl(uh6->viclfl, oviclfl); hnputs(uh6->len, olen); uh6->nextheader = IP_UDPPROTO; uh6->hoplimit = ottl; break; default: panic("udpiput: version %d", version); return; /* to avoid a warning */ } qlock(&udp->qlock); c = iphtlook(&upriv->ht, raddr, rport, laddr, lport); if (c == NULL) { /* no converstation found */ upriv->ustats.udpNoPorts++; qunlock(&udp->qlock); netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport, laddr, lport); switch (version) { case V4: icmpnoconv(f, bp); break; case V6: icmphostunr(f, ifc, bp, icmp6_port_unreach, 0); break; default: panic("udpiput2: version %d", version); } freeblist(bp); return; } ucb = (Udpcb *) c->ptcl; if (c->state == Announced) { if (ucb->headers == 0) { /* create a new conversation */ if (ipforme(f, laddr) != Runi) { switch (version) { case V4: v4tov6(laddr, ifc->lifc->local); break; case V6: ipmove(laddr, ifc->lifc->local); break; default: panic("udpiput3: version %d", version); } } c = Fsnewcall(c, raddr, rport, laddr, lport, version); if (c == NULL) { qunlock(&udp->qlock); freeblist(bp); return; } iphtadd(&upriv->ht, c); ucb = (Udpcb *) c->ptcl; } } qlock(&c->qlock); qunlock(&udp->qlock); /* * Trim the packet down to data size */ len -= UDP_UDPHDR_SZ; switch (version) { case V4: bp = trimblock(bp, UDP4_IPHDR_SZ + UDP_UDPHDR_SZ, len); break; case V6: bp = trimblock(bp, UDP6_IPHDR_SZ + UDP_UDPHDR_SZ, len); break; default: bp = NULL; panic("udpiput4: version %d", version); } if (bp == NULL) { qunlock(&c->qlock); netlog(f, Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport, laddr, lport); upriv->lenerr++; return; } netlog(f, Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", raddr, rport, laddr, lport, len); switch (ucb->headers) { case 7: /* pass the src address */ bp = padblock(bp, UDP_USEAD7); p = bp->rp; ipmove(p, raddr); p += IPaddrlen; ipmove(p, laddr); p += IPaddrlen; ipmove(p, ifc->lifc->local); p += IPaddrlen; hnputs(p, rport); p += 2; hnputs(p, lport); break; case 6: /* pass the src address */ bp = padblock(bp, UDP_USEAD6); p = bp->rp; ipmove(p, raddr); p += IPaddrlen; ipmove(p, ipforme(f, laddr) == Runi ? laddr : ifc->lifc->local); p += IPaddrlen; hnputs(p, rport); p += 2; hnputs(p, lport); break; } if (bp->next) bp = concatblock(bp); if (qfull(c->rq)) { qunlock(&c->qlock); netlog(f, Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport, laddr, lport); freeblist(bp); return; } qpass(c->rq, bp); qunlock(&c->qlock); }
static void greiput(Proto *gre, Ipifc*, Block *bp) { int len; GREhdr *ghp; Conv *c, **p; ushort eproto; uchar raddr[IPaddrlen]; GREpriv *gpriv; gpriv = gre->priv; ghp = (GREhdr*)(bp->rp); v4tov6(raddr, ghp->src); eproto = nhgets(ghp->eproto); qlock(gre); /* Look for a conversation structure for this port and address */ c = nil; for(p = gre->conv; *p; p++) { c = *p; if(c->inuse == 0) continue; if(c->rport == eproto && (gpriv->raw || ipcmp(c->raddr, raddr) == 0)) break; } if(*p == nil) { qunlock(gre); freeblist(bp); return; } qunlock(gre); /* * Trim the packet down to data size */ len = nhgets(ghp->len) - GRE_IPONLY; if(len < GRE_IPPLUSGRE){ freeblist(bp); return; } bp = trimblock(bp, GRE_IPONLY, len); if(bp == nil){ gpriv->lenerr++; return; } /* * Can't delimit packet so pull it all into one block. */ if(qlen(c->rq) > 64*1024) freeblist(bp); else{ bp = concatblock(bp); if(bp == 0) panic("greiput"); qpass(c->rq, bp); } }
/* * Interrupt level copy out of a queue, return # bytes copied. */ int qconsume(Queue *q, void *vp, int len) { Block *b; int n, dowakeup; uchar *p = vp; Block *tofree = nil; /* sync with qwrite */ ilock(q); for(;;) { b = q->bfirst; if(b == 0){ q->state |= Qstarve; iunlock(q); return -1; } QDEBUG checkb(b, "qconsume 1"); n = BLEN(b); if(n > 0) break; q->bfirst = b->next; q->len -= BALLOC(b); /* remember to free this */ b->next = tofree; tofree = b; }; if(n < len) len = n; memmove(p, b->rp, len); consumecnt += n; b->rp += len; q->dlen -= len; /* discard the block if we're done with it */ if((q->state & Qmsg) || len == n){ q->bfirst = b->next; b->next = 0; q->len -= BALLOC(b); q->dlen -= BLEN(b); /* remember to free this */ b->next = tofree; tofree = b; } /* if writer flow controlled, restart */ if((q->state & Qflow) && q->len < q->limit/2){ q->state &= ~Qflow; dowakeup = 1; } else dowakeup = 0; iunlock(q); if(dowakeup) wakeup(&q->wr); if(tofree != nil) freeblist(tofree); return len; }
int arpwrite(struct Fs *fs, char *s, long len) { int n; struct route *r; struct arp *arp; struct block *bp; struct arpent *a, *fl, **l; struct medium *m; char *f[4], buf[256]; uint8_t ip[IPaddrlen], mac[MAClen]; arp = fs->arp; if (len <= 0) error(EINVAL, ERROR_FIXME); if (len > sizeof(buf)) len = sizeof(buf); strlcpy(buf, s, sizeof(buf)); if (len > 0 && buf[len - 2] == '\n') buf[len - 2] = 0; n = getfields(buf, f, 4, 1, " "); if (strcmp(f[0], "flush") == 0) { qlock(&arp->qlock); for (a = arp->cache; a < &arp->cache[NCACHE]; a++) { memset(a->ip, 0, sizeof(a->ip)); memset(a->mac, 0, sizeof(a->mac)); a->hash = NULL; a->state = 0; a->utime = 0; while (a->hold != NULL) { bp = a->hold->list; freeblist(a->hold); a->hold = bp; } } memset(arp->hash, 0, sizeof(arp->hash)); /* clear all pkts on these lists (rxmt, dropf/l) */ arp->rxmt = NULL; arp->dropf = NULL; arp->dropl = NULL; qunlock(&arp->qlock); } else if (strcmp(f[0], "add") == 0) { switch (n) { default: error(EINVAL, ERROR_FIXME); case 3: parseip(ip, f[1]); if (isv4(ip)) r = v4lookup(fs, ip + IPv4off, NULL); else r = v6lookup(fs, ip, NULL); if (r == NULL) error(EHOSTUNREACH, "Destination unreachable"); m = r->rt.ifc->m; n = parsemac(mac, f[2], m->maclen); break; case 4: m = ipfindmedium(f[1]); if (m == NULL) error(EINVAL, ERROR_FIXME); parseip(ip, f[2]); n = parsemac(mac, f[3], m->maclen); break; } if (m->ares == NULL) error(EINVAL, ERROR_FIXME); m->ares(fs, V6, ip, mac, n, 0); } else if (strcmp(f[0], "del") == 0) { if (n != 2) error(EINVAL, ERROR_FIXME); parseip(ip, f[1]); qlock(&arp->qlock); l = &arp->hash[haship(ip)]; for (a = *l; a; a = a->hash) { if (memcmp(ip, a->ip, sizeof(a->ip)) == 0) { *l = a->hash; break; } l = &a->hash; } if (a) { /* take out of re-transmit chain */ l = &arp->rxmt; for (fl = *l; fl; fl = fl->nextrxt) { if (fl == a) { *l = a->nextrxt; break; } l = &fl->nextrxt; } a->nextrxt = NULL; a->hash = NULL; a->hold = NULL; a->last = NULL; a->ifc = NULL; memset(a->ip, 0, sizeof(a->ip)); memset(a->mac, 0, sizeof(a->mac)); } qunlock(&arp->qlock); } else error(EINVAL, ERROR_FIXME); return len; }
/* * 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); }
/* * create a new arp entry for an ip address. */ static struct arpent *newarp6(struct arp *arp, uint8_t *ip, struct Ipifc *ifc, int addrxt) { unsigned int t; struct block *next, *xp; struct arpent *a, *e, *f, **l; struct medium *m = ifc->m; int empty; /* find oldest entry */ e = &arp->cache[NCACHE]; a = arp->cache; t = a->utime; for (f = a; f < e; f++) { if (f->utime < t) { t = f->utime; a = f; } } /* dump waiting packets */ xp = a->hold; a->hold = NULL; if (isv4(a->ip)) { while (xp) { next = xp->list; freeblist(xp); xp = next; } } else { /* queue icmp unreachable for rxmitproc later, w/o arp lock */ if (xp) { if (arp->dropl == NULL) arp->dropf = xp; else arp->dropl->list = xp; for (next = xp->list; next; next = next->list) xp = next; arp->dropl = xp; rendez_wakeup(&arp->rxmtq); } } /* take out of current chain */ l = &arp->hash[haship(a->ip)]; for (f = *l; f; f = f->hash) { if (f == a) { *l = a->hash; break; } l = &f->hash; } /* insert into new chain */ l = &arp->hash[haship(ip)]; a->hash = *l; *l = a; memmove(a->ip, ip, sizeof(a->ip)); a->utime = NOW; a->ctime = 0; /* somewhat of a "last sent time". 0, to trigger a send. */ a->type = m; a->rtime = NOW + ReTransTimer; a->rxtsrem = MAX_MULTICAST_SOLICIT; a->ifc = ifc; a->ifcid = ifc->ifcid; /* put to the end of re-transmit chain; addrxt is 0 when isv4(a->ip) */ if (!ipismulticast(a->ip) && addrxt) { l = &arp->rxmt; empty = (*l == NULL); for (f = *l; f; f = f->nextrxt) { if (f == a) { *l = a->nextrxt; break; } l = &f->nextrxt; } for (f = *l; f; f = f->nextrxt) { l = &f->nextrxt; } *l = a; if (empty) rendez_wakeup(&arp->rxmtq); } a->nextrxt = NULL; return a; }
/* Adds block (which can be a list of blocks) to the queue, subject to * qio_flags. Returns the length written on success or -1 on non-throwable * error. Adjust qio_flags to control the value-added features!. */ static ssize_t __qbwrite(struct queue *q, struct block *b, int qio_flags) { ssize_t ret; bool dowakeup = FALSE; bool was_empty; if (q->bypass) { ret = blocklen(b); (*q->bypass) (q->arg, b); return ret; } spin_lock_irqsave(&q->lock); was_empty = q->len == 0; if (q->state & Qclosed) { spin_unlock_irqsave(&q->lock); freeblist(b); if (!(qio_flags & QIO_CAN_ERR_SLEEP)) return -1; if (q->err[0]) error(EFAIL, q->err); else error(EFAIL, "connection closed"); } if ((qio_flags & QIO_LIMIT) && (q->len >= q->limit)) { /* drop overflow takes priority over regular non-blocking */ if ((qio_flags & QIO_DROP_OVERFLOW) || (q->state & Qdropoverflow)) { spin_unlock_irqsave(&q->lock); freeb(b); return -1; } /* People shouldn't set NON_BLOCK without CAN_ERR, but we can be nice * and catch it. */ if ((qio_flags & QIO_CAN_ERR_SLEEP) && (qio_flags & QIO_NON_BLOCK)) { spin_unlock_irqsave(&q->lock); freeb(b); error(EAGAIN, "queue full"); } } ret = enqueue_blist(q, b); QDEBUG checkb(b, "__qbwrite"); /* make sure other end gets awakened */ if (q->state & Qstarve) { q->state &= ~Qstarve; dowakeup = TRUE; } spin_unlock_irqsave(&q->lock); /* TODO: not sure if the usage of a kick is mutually exclusive with a * wakeup, meaning that actual users either want a kick or have qreaders. */ if (q->kick && (dowakeup || (q->state & Qkick))) q->kick(q->arg); if (dowakeup) rendez_wakeup(&q->rr); if (was_empty) qwake_cb(q, FDTAP_FILT_READABLE); /* * flow control, wait for queue to get below the limit * before allowing the process to continue and queue * more. We do this here so that postnote can only * interrupt us after the data has been queued. This * means that things like 9p flushes and ssl messages * will not be disrupted by software interrupts. * * Note - this is moderately dangerous since a process * that keeps getting interrupted and rewriting will * queue infinite crud. */ if ((qio_flags & QIO_CAN_ERR_SLEEP) && !(q->state & Qdropoverflow) && !(qio_flags & QIO_NON_BLOCK)) { /* This is a racy peek at the q status. If we accidentally block, we * set Qflow, so someone should wake us. If we accidentally don't * block, we just returned to the user and let them slip a block past * flow control. */ while (!qnotfull(q)) { spin_lock_irqsave(&q->lock); q->state |= Qflow; spin_unlock_irqsave(&q->lock); rendez_sleep(&q->wr, qnotfull, q); } } return ret; }