void relsendack(Conv *c, Reliable *r, int hangup) { Udphdr *uh; Block *bp; Rudphdr *rh; int ptcllen; Fs *f; bp = allocb(UDP_IPHDR + UDP_RHDRSIZE); if(bp == nil) return; bp->wp += UDP_IPHDR + UDP_RHDRSIZE; f = c->p->f; uh = (Udphdr *)(bp->rp); uh->vihl = IP_VER4; rh = (Rudphdr*)uh; ptcllen = (UDP_RHDRSIZE-UDP_PHDRSIZE); uh->Unused = 0; uh->udpproto = IP_UDPPROTO; uh->frag[0] = 0; uh->frag[1] = 0; hnputs(uh->udpplen, ptcllen); v6tov4(uh->udpdst, r->addr); hnputs(uh->udpdport, r->port); hnputs(uh->udpsport, c->lport); if(ipcmp(c->laddr, IPnoaddr) == 0) findlocalip(f, c->laddr, c->raddr); v6tov4(uh->udpsrc, c->laddr); hnputs(uh->udplen, ptcllen); if(hangup) hnputl(rh->relsgen, Hangupgen); else hnputl(rh->relsgen, r->sndgen); hnputl(rh->relseq, 0); hnputl(rh->relagen, r->rcvgen); hnputl(rh->relack, r->rcvseq); if(r->acksent < r->rcvseq) r->acksent = r->rcvseq; uh->udpcksum[0] = 0; uh->udpcksum[1] = 0; hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, UDP_RHDRSIZE)); DPRINT("sendack: %lud/%lud, %lud/%lud\n", 0L, r->sndgen, r->rcvseq, r->rcvgen); doipoput(c, f, bp, 0, c->ttl, c->tos); }
long ipread(Chan *ch, void *a, long n, vlong offset) { int r; Conv *c; Proto *x; uchar ip[4]; char buf[128], *p; /*print("ipread %s %lux\n", c2name(ch), (long)ch->qid.path);*/ p = a; switch(TYPE(ch->qid)) { default: error(Eperm); case Qcs: return csread(ch, a, n, offset); case Qprotodir: case Qtopdir: case Qconvdir: return devdirread(ch, a, n, 0, 0, ipgen); case Qctl: sprint(buf, "%d", CONV(ch->qid)); return readstr(offset, p, n, buf); case Qremote: c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; hnputl(ip, c->raddr); sprint(buf, "%I!%d\n", ip, c->rport); return readstr(offset, p, n, buf); case Qlocal: c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; hnputl(ip, c->laddr); sprint(buf, "%I!%d\n", ip, c->lport); return readstr(offset, p, n, buf); case Qstatus: x = &proto[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; sprint(buf, "%s/%d %d %s \n", c->p->name, c->x, c->r.ref, c->state); return readstr(offset, p, n, buf); case Qdata: c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; r = so_recv(c->sfd, a, n, 0); if(r < 0){ oserrstr(); nexterror(); } return r; } }
static int hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd) { int i; Devlink *l; l = pickdevlink(d); i = pickea(l); if(i == -1){ downdev(d, "resend fails; no netlink/ea"); return -1; } if(f->srb && sys->ticks - f->srb->ticksent > Srbtimeout){ eventlog("%æ: srb timeout\n", d); frameerror(d, f, Etimedout); return -1; } memmove(h->dst, l->eatab[i], Eaddrlen); memmove(h->src, l->nl->ea, sizeof h->src); hnputs(h->type, Aoetype); h->verflag = Aoever << 4; h->error = 0; hnputs(h->major, d->major); h->minor = d->minor; h->cmd = cmd; hnputl(h->tag, f->tag = newtag(d)); f->dl = l; f->nl = l->nl; f->eaidx = i; f->ticksent = sys->ticks; return f->tag; }
SmbProcessResult smbresponsesend(SmbSession *s) { uchar cmd; SmbProcessResult pr; assert(smbbufferoffsetgetb(s->response, 4, &cmd)); smbloglock(); smblogprint(cmd, "sending:\n"); smblogdata(cmd, smblogprint, smbbufferreadpointer(s->response), smbbufferreadspace(s->response), 256); smblogunlock(); if (s->nbss) { NbScatterGather a[2]; a[0].p = smbbufferreadpointer(s->response); a[0].l = smbbufferreadspace(s->response); a[1].p = nil; nbssgatherwrite(s->nbss, a); pr = SmbProcessResultOk; } else if (s->cifss) { ulong l = smbbufferreadspace(s->response); uchar nl[4]; hnputl(nl, l); write(s->cifss->fd, nl, 4); write(s->cifss->fd, smbbufferreadpointer(s->response), l); pr = SmbProcessResultOk; } else pr = SmbProcessResultDie; smbbufferreset(s->response); return pr; }
void hnputv(void *p, int64_t v) { uint8_t *a; a = p; hnputl(a, v >> 32); hnputl(a + 4, v); }
uint8_t* optaddulong(uint8_t *p, int op, uint32_t x) { p[0] = op; p[1] = 4; hnputl(p+2, x); return p+6; }
void igmpproc(void *a) { IGMPrep *rp, **lrp; Multicast *mp, **lmp; uchar ip[IPaddrlen]; USED(a); for(;;){ sleep(&igmpalloc.r, isreport, 0); for(;;){ lock(&igmpalloc); if(igmpalloc.reports == nil) break; /* look for a single report */ lrp = &igmpalloc.reports; mp = nil; for(rp = *lrp; rp; rp = *lrp){ rp->ticks++; lmp = &rp->multi; for(mp = *lmp; mp; mp = *lmp){ if(rp->ticks >= mp->timeout){ *lmp = mp->next; break; } lmp = &mp->next; } if(mp != nil) break; if(rp->multi != nil){ lrp = &rp->next; continue; } else { *lrp = rp->next; free(rp); } } unlock(&igmpalloc); if(mp){ /* do a single report and try again */ hnputl(ip, mp->addr); igmpsendreport(rp->m, ip); free(mp); continue; } tsleep(&up->sleep, return0, 0, MSPTICK); } unlock(&igmpalloc); } }
void hnputv(void *p, uvlong v) { uchar *a; a = p; hnputl(a, v>>32); hnputl(a+4, v); }
void dhcpsend(int type) { int n; uint8_t *p; Bootp bp; Udphdr *up; memset(&bp, 0, sizeof bp); up = (Udphdr*)bp.udphdr; hnputs(up->rport, 67); bp.op = Bootrequest; hnputl(bp.xid, dhcp.xid); hnputs(bp.secs, time(0) - dhcp.starttime); hnputs(bp.flags, Fbroadcast); /* reply must be broadcast */ memmove(bp.optmagic, optmagic, 4); p = bp.optdata; p = optaddbyte(p, ODtype, type); p = optadd(p, ODclientid, dhcp.cid, strlen(dhcp.cid)); switch(type) { default: myfatal("dhcpsend: unknown message type: %d", type); case Discover: ipmove(up->raddr, IPv4bcast); /* broadcast */ break; case Request: if(dhcp.state == Sbound || dhcp.state == Srenewing) ipmove(up->raddr, dhcp.server); else ipmove(up->raddr, IPv4bcast); /* broadcast */ p = optaddulong(p, ODlease, dhcp.lease); if(dhcp.state == Sselecting || dhcp.state == Srequesting) { p = optaddaddr(p, ODipaddr, dhcp.client); /* mistake?? */ p = optaddaddr(p, ODserverid, dhcp.server); } else v6tov4(bp.ciaddr, dhcp.client); break; case Release: ipmove(up->raddr, dhcp.server); v6tov4(bp.ciaddr, dhcp.client); p = optaddaddr(p, ODipaddr, dhcp.client); p = optaddaddr(p, ODserverid, dhcp.server); break; } *p++ = OBend; n = p - (uint8_t*)&bp; if(write(dhcp.fd, &bp, n) != n) myfatal("dhcpsend: write failed: %r"); }
void igmpsendreport(Medium *m, uchar *addr) { IGMPpkt *p; Block *bp; bp = allocb(sizeof(IGMPpkt)); if(bp == nil) return; p = (IGMPpkt*)bp->wp; p->vihl = IP_VER4; bp->wp += IGMPPKTSZ; memset(bp->rp, 0, IGMPPKTSZ); hnputl(p->src, Mediumgetaddr(m)); hnputl(p->dst, Ipallsys); p->vertype = (1<<4) | IGMPreport; p->proto = IP_IGMPPROTO; memmove(p->group, addr, IPaddrlen); hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)); netlog(Logigmp, "igmpreport %I\n", p->group); stats.outreports++; ipoput4(bp, 0, 1, DFLTTOS, nil); /* TTL of 1 */ }
/* Write block to file descriptor */ void B2fd(Block* blk, int fd) { int sz; char size[4]; sz = (int)(blk->wrp - blk->beg); hnputl((void*) size, sz); if (write(fd, size, 4) != 4) exits("could not write response size"); if (write(fd, blk->beg, sz) != sz) exits("could not write response"); }
void so_bind(int fd, int su, unsigned long addr, unsigned short port) { int i, one; struct sockaddr sa; struct sockaddr_in *sin; sin = (struct sockaddr_in*)&sa; one = 1; // if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) { // oserrstr(up->genbuf, sizeof(up->genbuf)); // print("setsockopt: %s", err); // } if(su) { for(i = 600; i < 1024; i++) { memset(&sa, 0, sizeof(sa)); sin->sin_family = AF_INET; hnputl(&sin->sin_addr.s_addr, addr); hnputs(&sin->sin_port, i); if(bind(fd, &sa, sizeof(sa)) >= 0) return; } oserror(); } memset(&sa, 0, sizeof(sa)); sin->sin_family = AF_INET; hnputl(&sin->sin_addr.s_addr, addr); hnputs(&sin->sin_port, port); if(bind(fd, &sa, sizeof(sa)) < 0) oserror(); }
static int rbootpread(char *bp, ulong offset, int len) { int n, i; char *buf; uchar a[4]; if(debug) print("dhcp: bootpread() \n"); buf = smalloc(READSTR); if(waserror()){ free(buf); nexterror(); } hnputl(a, fsip); n = snprint(buf, READSTR, "fsip %15V\n", a); hnputl(a, auip); n += snprint(buf + n, READSTR-n, "auip %15V\n", a); hnputl(a, gwip); n += snprint(buf + n, READSTR-n, "gwip %15V\n", a); hnputl(a, ipmask); n += snprint(buf + n, READSTR-n, "ipmask %15V\n", a); hnputl(a, ipaddr); n += snprint(buf + n, READSTR-n, "ipaddr %15V\n", a); n += snprint(buf+n, READSTR-n, "expired %lud\n", iplease); n += snprint(buf + n, READSTR-n, "dns"); if(dns2ip){ hnputl(a, dns2ip); n+=snprint(buf + n, READSTR-n, " %15V", a); } if(dns1ip){ hnputl(a, dns1ip); n += snprint(buf + n, READSTR-n, " %15V", a); } for(i=0; i<2; i++) if(ipcmp(pppdns[i], IPnoaddr) != 0 && ipcmp(pppdns[i], v4prefix) != 0) n += snprint(buf + n, READSTR-n, " %15I", pppdns[i]); snprint(buf + n, READSTR-n, "\n"); len = readstr(offset, bp, len, buf); poperror(); free(buf); return len; }
void so_connect(int fd, unsigned long raddr, unsigned short rport) { int r; struct sockaddr sa; struct sockaddr_in *sin; memset(&sa, 0, sizeof(sa)); sin = (struct sockaddr_in*)&sa; sin->sin_family = AF_INET; hnputs(&sin->sin_port, rport); hnputl(&sin->sin_addr.s_addr, raddr); osenter(); r = connect(fd, &sa, sizeof(sa)); osleave(); if(r < 0) oserror(); }
/* * write out a packet trace */ void tracepkt(uint8_t *ps, int len) { struct pcap_pkthdr *goo; if(Mflag && len > Mflag) len = Mflag; if(pcap){ goo = (struct pcap_pkthdr*)(ps-16); goo->ts = pkttime; goo->caplen = len; goo->len = len; write(1, goo, len+16); } else { hnputs(ps-10, len); hnputl(ps-8, pkttime>>32); hnputl(ps-4, pkttime); write(1, ps-10, len+10); } }
int nbssgatherwrite(NbSession *s, NbScatterGather *a) { uchar hdr[4]; NbScatterGather *ap; long l = 0; for (ap = a; ap->p; ap++) l += ap->l; //print("nbssgatherwrite %ld bytes\n", l); hnputl(hdr, l); //nbdumpdata(hdr, sizeof(hdr)); if (write(s->fd, hdr, sizeof(hdr)) != sizeof(hdr)) return -1; for (ap = a; ap->p; ap++) { //nbdumpdata(ap->p, ap->l); if (write(s->fd, ap->p, ap->l) != ap->l) return -1; } return 0; }
static int hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd) { int i; Devlink *l; if(f->srb && MACHP(0)->ticks - f->srb->ticksent > Maxreqticks){ eventlog("%æ: srb timeout\n", d); if(cmd == ACata && f->srb && Nofail(d, s)) f->srb->ticksent = MACHP(0)->ticks; else frameerror(d, f, Etimedout); return -1; } l = pickdevlink(d); i = pickea(l); if(i == -1){ if(cmd != ACata || f->srb == nil || !Nofail(d, s)) downdev(d, "resend fails; no netlink/ea"); return -1; } memmove(h->dst, l->eatab[i], Eaddrlen); memmove(h->src, l->nl->ea, sizeof h->src); hnputs(h->type, Aoetype); h->verflag = Aoever << 4; h->error = 0; hnputs(h->major, d->major); h->minor = d->minor; h->cmd = cmd; hnputl(h->tag, f->tag = newtag(d)); f->dl = l; f->nl = l->nl; f->eaidx = i; f->ticksent = MACHP(0)->ticks; return f->tag; }
int eipfmt(Fmt *f) { char buf[5*8]; static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux"; static char *ifmt = "%d.%d.%d.%d"; uchar *p, ip[16]; ulong ul; switch(f->r) { case 'E': /* Ethernet address */ p = va_arg(f->args, uchar*); snprint(buf, sizeof buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); return fmtstrcpy(f, buf); case 'I': ul = va_arg(f->args, ulong); hnputl(ip, ul); snprint(buf, sizeof buf, ifmt, ip[0], ip[1], ip[2], ip[3]); return fmtstrcpy(f, buf); } return fmtstrcpy(f, "(eipfmt)"); }
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); }
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++; }
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); }
static int rc4cipher(Espcb *ecb, uchar *p, int n) { Esprc4 *esprc4; RC4state tmpstate; ulong seq; long d, dd; if(n < 4) return 0; esprc4 = ecb->espstate; if(ecb->incoming) { seq = nhgetl(p); p += 4; n -= 4; d = seq-esprc4->cseq; if(d == 0) { rc4(&esprc4->current, p, n); esprc4->cseq += n; if(esprc4->ovalid) { dd = esprc4->cseq - esprc4->lgseq; if(dd > RC4back) esprc4->ovalid = 0; } } else if(d > 0) { print("missing packet: %uld %ld\n", seq, d); // this link is hosed if(d > RC4forward) { strcpy(up->errstr, "rc4cipher: skipped too much"); return 0; } esprc4->lgseq = seq; if(!esprc4->ovalid) { esprc4->ovalid = 1; esprc4->oseq = esprc4->cseq; memmove(&esprc4->old, &esprc4->current, sizeof(RC4state)); } rc4skip(&esprc4->current, d); rc4(&esprc4->current, p, n); esprc4->cseq = seq+n; } else { print("reordered packet: %uld %ld\n", seq, d); dd = seq - esprc4->oseq; if(!esprc4->ovalid || -d > RC4back || dd < 0) { strcpy(up->errstr, "rc4cipher: too far back"); return 0; } memmove(&tmpstate, &esprc4->old, sizeof(RC4state)); rc4skip(&tmpstate, dd); rc4(&tmpstate, p, n); return 1; } // move old state up if(esprc4->ovalid) { dd = esprc4->cseq - RC4back - esprc4->oseq; if(dd > 0) { rc4skip(&esprc4->old, dd); esprc4->oseq += dd; } } } else { hnputl(p, esprc4->cseq); p += 4; n -= 4; rc4(&esprc4->current, p, n); esprc4->cseq += n; } return 1; }
int eipconv(va_list *arg, Fconv *f) { char buf[8*5]; static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux"; static char *ifmt = "%d.%d.%d.%d"; uint8_t *p, ip[16]; uint32_t *lp; uint16_t s; int i, j, n, eln, eli; switch(f->chr) { case 'E': /* Ethernet address */ p = va_arg(*arg, uint8_t*); sprint(buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); break; case 'I': /* Ip address */ p = va_arg(*arg, uint8_t*); common: if(memcmp(p, v4prefix, 12) == 0) sprint(buf, ifmt, p[12], p[13], p[14], p[15]); else { /* find longest elision */ eln = eli = -1; for(i = 0; i < 16; i += 2){ for(j = i; j < 16; j += 2) if(p[j] != 0 || p[j+1] != 0) break; if(j > i && j - i > eln){ eli = i; eln = j - i; } } /* print with possible elision */ n = 0; for(i = 0; i < 16; i += 2){ if(i == eli){ n += sprint(buf+n, "::"); i += eln; if(i >= 16) break; } else if(i != 0) n += sprint(buf+n, ":"); s = (p[i]<<8) + p[i+1]; n += sprint(buf+n, "%x", s); } } break; case 'i': /* v6 address as 4 longs */ lp = va_arg(*arg, uint32_t*); for(i = 0; i < 4; i++) hnputl(ip+4*i, *lp++); p = ip; goto common; case 'V': /* v4 ip address */ p = va_arg(*arg, uint8_t*); sprint(buf, ifmt, p[0], p[1], p[2], p[3]); break; case 'M': /* ip mask */ p = va_arg(*arg, uint8_t*); /* look for a prefix mask */ for(i = 0; i < 16; i++) if(p[i] != 0xff) break; if(i < 16){ if((prefixvals[p[i]] & Isprefix) == 0) goto common; for(j = i+1; j < 16; j++) if(p[j] != 0) goto common; n = 8*i + (prefixvals[p[i]] & ~Isprefix); } else n = 8*16; /* got one, use /xx format */ sprint(buf, "/%d", n); break; default: strcpy(buf, "(eipconv)"); } strconv(buf, f); return sizeof(uint8_t*); }
int main(int argc, char **argv) { char *addr, *p, *q, to[4]; char buf[2048]; int port, fd, nfd, one, len, n, tot; ulong ip; struct sockaddr_in sin; WSADATA wasdat; if(argc != 1 && argc != 2) { usage: fprintf(stderr, "usage: winplumb [tcp!ipaddr!port]\n"); ExitThread(1); } if(argc == 1) addr = "tcp!*!17890"; else addr = argv[1]; strcpy(buf, addr); p = strchr(buf, '!'); if(p == 0) goto usage; q = strchr(p+1, '!'); if(q == 0) goto usage; *p++ = 0; *q++ = 0; if(strcmp(buf, "tcp") != 0) goto usage; port = atoi(q); if(strcmp(p, "*") == 0) ip = 0; else ip = parseip(to, p); WSAStartup(MAKEWORD(1, 1), &wasdat); fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0) { oserror(); fprintf(stderr, "socket: %s\n", errbuf); ExitThread(1); } one = 1; if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one) != 0) { oserror(); fprintf(stderr, "setsockopt nodelay: %s\n", errbuf); } if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof one) != 0) { oserror(); fprintf(stderr, "setsockopt reuse: %s\n", errbuf); } memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; hnputs(&sin.sin_port, port); hnputl(&sin.sin_addr, ip); if(bind(fd, (struct sockaddr*)&sin, sizeof sin) < 0) { oserror(); fprintf(stderr, "bind: %s\n", errbuf); ExitThread(1); } if(listen(fd, 5) < 0) { oserror(); fprintf(stderr, "listen: %s\n", errbuf); ExitThread(1); } for(;;) { len = sizeof sin; nfd = accept(fd, (struct sockaddr*)&sin, &len); if(nfd < 0) { oserror(); fprintf(stderr, "accept: %s\n", errbuf); continue; } tot = 0; while(tot == 0 || buf[tot-1] != '\n') { n = recv(nfd, buf+tot, sizeof buf-tot, 0); if(n < 0) break; tot += n; } if(buf[tot-1] == '\n') { buf[tot-1] = 0; p = strchr(buf, ' '); if(p) *p++ = 0; ShellExecute(0, 0, buf, p, 0, SW_SHOWNORMAL); } closesocket(nfd); } }
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(); }
/* * 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); }
/* * broadcast routes onto all networks */ void sendto(Ifc *ip) { int h, n; uint8_t raddr[Pasize], mbuf[Udphdrsize+512]; Ripmsg *m; Route *r; Udphdr *u; u = (Udphdr*)mbuf; for(n = 0; n < Pasize; n++) raddr[n] = ip->net[n] | ~(ip->mask[n]); v4tov6(u->raddr, raddr); hnputs(u->rport, 520); m = (Ripmsg*)(mbuf+Udphdrsize); m->type = Response; m->vers = Version; if(debug) fprint(2, "to %V\n", u->raddr); n = 0; for(h = 0; h < Nhash; h++){ for(r = ralloc.hash[h]; r; r = r->next){ /* * don't send any route back to the net * it came from */ if(onnet(r->gate, ip->net, ip->mask)) continue; /* * don't tell a network about itself */ if(equivip(r->dest, ip->net)) continue; /* * don't tell nets about other net's subnets */ if(!equivip(r->mask, v4defmask(r->dest)) && !equivip(ip->cmask, v4defmask(r->dest))) continue; memset(&m->rip[n], 0, sizeof(m->rip[n])); memmove(m->rip[n].addr, r->dest, Pasize); if(r->metric < 1) hnputl(m->rip[n].metric, 1); else hnputl(m->rip[n].metric, r->metric); hnputs(m->rip[n].family, AF_INET); if(debug) fprint(2, " %16V & %16V -> %16V %2d\n", r->dest, r->mask, r->gate, r->metric); if(++n == Maxroutes && !readonly){ write(ripfd, mbuf, Udphdrsize + 4 + n*20); n = 0; } } } if(n && !readonly) write(ripfd, mbuf, Udphdrsize+4+n*20); }
int eipfmt(Fmt *f) { char buf[5*8]; static char *efmt = "%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux"; static char *ifmt = "%d.%d.%d.%d"; uchar *p, ip[16]; ulong *lp; ushort s; int i, j, n, eln, eli; switch(f->r) { case 'E': /* Ethernet address */ p = va_arg(f->args, uchar*); snprint(buf, sizeof buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); return fmtstrcpy(f, buf); case 'I': /* Ip address */ p = va_arg(f->args, uchar*); common: if(memcmp(p, v4prefix, 12) == 0){ snprint(buf, sizeof buf, ifmt, p[12], p[13], p[14], p[15]); return fmtstrcpy(f, buf); } /* find longest elision */ eln = eli = -1; for(i = 0; i < 16; i += 2){ for(j = i; j < 16; j += 2) if(p[j] != 0 || p[j+1] != 0) break; if(j > i && j - i > eln){ eli = i; eln = j - i; } } /* print with possible elision */ n = 0; for(i = 0; i < 16; i += 2){ if(i == eli){ n += sprint(buf+n, "::"); i += eln; if(i >= 16) break; } else if(i != 0) n += sprint(buf+n, ":"); s = (p[i]<<8) + p[i+1]; n += sprint(buf+n, "%ux", s); } return fmtstrcpy(f, buf); case 'i': /* v6 address as 4 longs */ lp = va_arg(f->args, ulong*); for(i = 0; i < 4; i++) hnputl(ip+4*i, *lp++); p = ip; goto common; case 'V': /* v4 ip address */ p = va_arg(f->args, uchar*); snprint(buf, sizeof buf, ifmt, p[0], p[1], p[2], p[3]); return fmtstrcpy(f, buf); case 'M': /* ip mask */ p = va_arg(f->args, uchar*); /* look for a prefix mask */ for(i = 0; i < 16; i++) if(p[i] != 0xff) break; if(i < 16){ if((prefixvals[p[i]] & Isprefix) == 0) goto common; for(j = i+1; j < 16; j++) if(p[j] != 0) goto common; n = 8*i + (prefixvals[p[i]] & ~Isprefix); } else n = 8*16; /* got one, use /xx format */ snprint(buf, sizeof buf, "/%d", n); return fmtstrcpy(f, buf); } return fmtstrcpy(f, "(eipfmt)"); }
static char* rbootp(Ipifc *ifc) { int cfd, dfd, tries, n; char ia[5+3*16], im[16], *av[3]; uchar nipaddr[4], ngwip[4], nipmask[4]; char dir[Maxpath]; static uchar vend_rfc1048[] = { 99, 130, 83, 99 }; uchar *vend; /* * broadcast bootp's till we get a reply, * or fixed number of tries */ if(debug) print("dhcp: bootp() called\n"); tries = 0; av[1] = "0.0.0.0"; av[2] = "0.0.0.0"; ipifcadd(ifc, av, 3, 0, nil); cfd = kannounce("udp!*!68", dir); if(cfd < 0) return "dhcp announce failed"; strcat(dir, "/data"); if(kwrite(cfd, "headers", 7) < 0){ kclose(cfd); return "dhcp ctl headers failed"; } kwrite(cfd, "oldheaders", 10); dfd = kopen(dir, ORDWR); if(dfd < 0){ kclose(cfd); return "dhcp open data failed"; } kclose(cfd); while(tries<1){ tries++; memset(sid, 0, 4); iplease=0; dhcpmsgtype=-2; /* DHCPDISCOVER*/ done = 0; recv = 0; kproc("rcvbootp", rcvbootp, (void*)dfd, KPDUPFDG); /* Prepare DHCPDISCOVER */ memset(&req, 0, sizeof(req)); ipmove(req.raddr, IPv4bcast); hnputs(req.rport, 67); req.op = Bootrequest; req.htype = 1; /* ethernet (all we know) */ req.hlen = 6; /* ethernet (all we know) */ memmove(req.chaddr, ifc->mac, 6); /* Hardware MAC address */ //ipv4local(ifc, req.ciaddr); /* Fill in the local IP address if we know it */ memset(req.file, 0, sizeof(req.file)); vend=req.vend; memmove(vend, vend_rfc1048, 4); vend+=4; *vend++=53; *vend++=1;*vend++=1; /* dhcp msg type==3, dhcprequest */ *vend++=61;*vend++=7;*vend++=1; memmove(vend, ifc->mac, 6);vend+=6; *vend=0xff; if(debug) dispvend(req.vend); for(n=0;n<4;n++){ if(kwrite(dfd, &req, sizeof(req))<0) /* SEND DHCPDISCOVER */ print("DHCPDISCOVER: %r"); tsleep(&bootpr, return0, 0, 1000); /* wait DHCPOFFER */ if(debug) print("[DHCP] DISCOVER: msgtype = %d\n", dhcpmsgtype); if(dhcpmsgtype==2) /* DHCPOFFER */ break; else if(dhcpmsgtype==0) /* bootp */ return nil; else if(dhcpmsgtype== -2) /* time out */ continue; else break; } if(dhcpmsgtype!=2) continue; /* DHCPREQUEST */ memset(req.vend, 0, sizeof(req.vend)); vend=req.vend; memmove(vend, vend_rfc1048, 4);vend+=4; *vend++=53; *vend++=1;*vend++=3; /* dhcp msg type==3, dhcprequest */ *vend++=50; *vend++=4; /* requested ip address */ *vend++=(ipaddr >> 24)&0xff; *vend++=(ipaddr >> 16)&0xff; *vend++=(ipaddr >> 8) & 0xff; *vend++=ipaddr & 0xff; *vend++=51;*vend++=4; /* lease time */ *vend++=(iplease>>24)&0xff; *vend++=(iplease>>16)&0xff; *vend++=(iplease>>8)&0xff; *vend++=iplease&0xff; *vend++=54; *vend++=4; /* server identifier */ memmove(vend, sid, 4); vend+=4; *vend++=61;*vend++=07;*vend++=01; /* client identifier */ memmove(vend, ifc->mac, 6);vend+=6; *vend=0xff; if(debug) dispvend(req.vend); if(kwrite(dfd, &req, sizeof(req))<0){ print("DHCPREQUEST: %r"); continue; } tsleep(&bootpr, return0, 0, 2000); if(dhcpmsgtype==5) /* wait for DHCPACK */ break; else continue; /* CHECK ARP */ /* DHCPDECLINE */ } kclose(dfd); done = 1; if(rcvprocp != nil){ postnote(rcvprocp, 1, "timeout", 0); rcvprocp = nil; } av[1] = "0.0.0.0"; av[2] = "0.0.0.0"; ipifcrem(ifc, av, 3); hnputl(nipaddr, ipaddr); sprint(ia, "%V", nipaddr); hnputl(nipmask, ipmask); sprint(im, "%V", nipmask); av[1] = ia; av[2] = im; ipifcadd(ifc, av, 3, 0, nil); if(gwip != 0) { hnputl(ngwip, gwip); n = sprint(ia, "add 0.0.0.0 0.0.0.0 %V", ngwip); routewrite(ifc->conv->p->f, nil, ia, n); } return nil; }