summaryrefslogtreecommitdiff
path: root/sys/src/9/ip/icmp.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/ip/icmp.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/ip/icmp.c')
-rwxr-xr-xsys/src/9/ip/icmp.c492
1 files changed, 492 insertions, 0 deletions
diff --git a/sys/src/9/ip/icmp.c b/sys/src/9/ip/icmp.c
new file mode 100755
index 000000000..9dd5c9414
--- /dev/null
+++ b/sys/src/9/ip/icmp.c
@@ -0,0 +1,492 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+
+typedef struct Icmp {
+ uchar vihl; /* Version and header length */
+ uchar tos; /* Type of service */
+ uchar length[2]; /* packet length */
+ uchar id[2]; /* Identification */
+ uchar frag[2]; /* Fragment information */
+ uchar ttl; /* Time to live */
+ uchar proto; /* Protocol */
+ uchar ipcksum[2]; /* Header checksum */
+ uchar src[4]; /* Ip source */
+ uchar dst[4]; /* Ip destination */
+ uchar type;
+ uchar code;
+ uchar cksum[2];
+ uchar icmpid[2];
+ uchar seq[2];
+ uchar data[1];
+} Icmp;
+
+enum { /* Packet Types */
+ EchoReply = 0,
+ Unreachable = 3,
+ SrcQuench = 4,
+ Redirect = 5,
+ EchoRequest = 8,
+ TimeExceed = 11,
+ InParmProblem = 12,
+ Timestamp = 13,
+ TimestampReply = 14,
+ InfoRequest = 15,
+ InfoReply = 16,
+ AddrMaskRequest = 17,
+ AddrMaskReply = 18,
+
+ Maxtype = 18,
+};
+
+enum
+{
+ MinAdvise = 24, /* minimum needed for us to advise another protocol */
+};
+
+char *icmpnames[Maxtype+1] =
+{
+[EchoReply] "EchoReply",
+[Unreachable] "Unreachable",
+[SrcQuench] "SrcQuench",
+[Redirect] "Redirect",
+[EchoRequest] "EchoRequest",
+[TimeExceed] "TimeExceed",
+[InParmProblem] "InParmProblem",
+[Timestamp] "Timestamp",
+[TimestampReply] "TimestampReply",
+[InfoRequest] "InfoRequest",
+[InfoReply] "InfoReply",
+[AddrMaskRequest] "AddrMaskRequest",
+[AddrMaskReply ] "AddrMaskReply ",
+};
+
+enum {
+ IP_ICMPPROTO = 1,
+ ICMP_IPSIZE = 20,
+ ICMP_HDRSIZE = 8,
+};
+
+enum
+{
+ InMsgs,
+ InErrors,
+ OutMsgs,
+ CsumErrs,
+ LenErrs,
+ HlenErrs,
+
+ Nstats,
+};
+
+static char *statnames[Nstats] =
+{
+[InMsgs] "InMsgs",
+[InErrors] "InErrors",
+[OutMsgs] "OutMsgs",
+[CsumErrs] "CsumErrs",
+[LenErrs] "LenErrs",
+[HlenErrs] "HlenErrs",
+};
+
+typedef struct Icmppriv Icmppriv;
+struct Icmppriv
+{
+ ulong stats[Nstats];
+
+ /* message counts */
+ ulong in[Maxtype+1];
+ ulong out[Maxtype+1];
+};
+
+static void icmpkick(void *x, Block*);
+
+static void
+icmpcreate(Conv *c)
+{
+ c->rq = qopen(64*1024, Qmsg, 0, c);
+ c->wq = qbypass(icmpkick, c);
+}
+
+extern char*
+icmpconnect(Conv *c, char **argv, int argc)
+{
+ char *e;
+
+ e = Fsstdconnect(c, argv, argc);
+ if(e != nil)
+ return e;
+ Fsconnected(c, e);
+
+ return nil;
+}
+
+extern int
+icmpstate(Conv *c, char *state, int n)
+{
+ USED(c);
+ return snprint(state, n, "%s qin %d qout %d\n",
+ "Datagram",
+ c->rq ? qlen(c->rq) : 0,
+ c->wq ? qlen(c->wq) : 0
+ );
+}
+
+extern char*
+icmpannounce(Conv *c, char **argv, int argc)
+{
+ char *e;
+
+ e = Fsstdannounce(c, argv, argc);
+ if(e != nil)
+ return e;
+ Fsconnected(c, nil);
+
+ return nil;
+}
+
+extern void
+icmpclose(Conv *c)
+{
+ qclose(c->rq);
+ qclose(c->wq);
+ ipmove(c->laddr, IPnoaddr);
+ ipmove(c->raddr, IPnoaddr);
+ c->lport = 0;
+}
+
+static void
+icmpkick(void *x, Block *bp)
+{
+ Conv *c = x;
+ Icmp *p;
+ Icmppriv *ipriv;
+
+ if(bp == nil)
+ return;
+
+ if(blocklen(bp) < ICMP_IPSIZE + ICMP_HDRSIZE){
+ freeblist(bp);
+ return;
+ }
+ p = (Icmp *)(bp->rp);
+ p->vihl = IP_VER4;
+ ipriv = c->p->priv;
+ if(p->type <= Maxtype)
+ ipriv->out[p->type]++;
+
+ v6tov4(p->dst, c->raddr);
+ v6tov4(p->src, c->laddr);
+ p->proto = IP_ICMPPROTO;
+ hnputs(p->icmpid, c->lport);
+ memset(p->cksum, 0, sizeof(p->cksum));
+ hnputs(p->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
+ ipriv->stats[OutMsgs]++;
+ ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
+}
+
+extern void
+icmpttlexceeded(Fs *f, uchar *ia, Block *bp)
+{
+ Block *nbp;
+ Icmp *p, *np;
+
+ p = (Icmp *)bp->rp;
+
+ netlog(f, Logicmp, "sending icmpttlexceeded -> %V\n", p->src);
+ nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
+ nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
+ np = (Icmp *)nbp->rp;
+ np->vihl = IP_VER4;
+ memmove(np->dst, p->src, sizeof(np->dst));
+ v6tov4(np->src, ia);
+ memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
+ np->type = TimeExceed;
+ np->code = 0;
+ np->proto = IP_ICMPPROTO;
+ hnputs(np->icmpid, 0);
+ hnputs(np->seq, 0);
+ memset(np->cksum, 0, sizeof(np->cksum));
+ hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
+ ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
+
+}
+
+static void
+icmpunreachable(Fs *f, Block *bp, int code, int seq)
+{
+ Block *nbp;
+ Icmp *p, *np;
+ int i;
+ uchar addr[IPaddrlen];
+
+ p = (Icmp *)bp->rp;
+
+ /* only do this for unicast sources and destinations */
+ v4tov6(addr, p->dst);
+ i = ipforme(f, addr);
+ if((i&Runi) == 0)
+ return;
+ v4tov6(addr, p->src);
+ i = ipforme(f, addr);
+ if(i != 0 && (i&Runi) == 0)
+ return;
+
+ netlog(f, Logicmp, "sending icmpnoconv -> %V\n", p->src);
+ nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
+ nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
+ np = (Icmp *)nbp->rp;
+ np->vihl = IP_VER4;
+ memmove(np->dst, p->src, sizeof(np->dst));
+ memmove(np->src, p->dst, sizeof(np->src));
+ memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
+ np->type = Unreachable;
+ np->code = code;
+ np->proto = IP_ICMPPROTO;
+ hnputs(np->icmpid, 0);
+ hnputs(np->seq, seq);
+ memset(np->cksum, 0, sizeof(np->cksum));
+ hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
+ ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
+}
+
+extern void
+icmpnoconv(Fs *f, Block *bp)
+{
+ icmpunreachable(f, bp, 3, 0);
+}
+
+extern void
+icmpcantfrag(Fs *f, Block *bp, int mtu)
+{
+ icmpunreachable(f, bp, 4, mtu);
+}
+
+static void
+goticmpkt(Proto *icmp, Block *bp)
+{
+ Conv **c, *s;
+ Icmp *p;
+ uchar dst[IPaddrlen];
+ ushort recid;
+
+ p = (Icmp *) bp->rp;
+ v4tov6(dst, p->src);
+ recid = nhgets(p->icmpid);
+
+ for(c = icmp->conv; *c; c++) {
+ s = *c;
+ if(s->lport == recid)
+ if(ipcmp(s->raddr, dst) == 0){
+ bp = concatblock(bp);
+ if(bp != nil)
+ qpass(s->rq, bp);
+ return;
+ }
+ }
+ freeblist(bp);
+}
+
+static Block *
+mkechoreply(Block *bp)
+{
+ Icmp *q;
+ uchar ip[4];
+
+ q = (Icmp *)bp->rp;
+ q->vihl = IP_VER4;
+ memmove(ip, q->src, sizeof(q->dst));
+ memmove(q->src, q->dst, sizeof(q->src));
+ memmove(q->dst, ip, sizeof(q->dst));
+ q->type = EchoReply;
+ memset(q->cksum, 0, sizeof(q->cksum));
+ hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
+
+ return bp;
+}
+
+static char *unreachcode[] =
+{
+[0] "net unreachable",
+[1] "host unreachable",
+[2] "protocol unreachable",
+[3] "port unreachable",
+[4] "fragmentation needed and DF set",
+[5] "source route failed",
+};
+
+static void
+icmpiput(Proto *icmp, Ipifc*, Block *bp)
+{
+ int n, iplen;
+ Icmp *p;
+ Block *r;
+ Proto *pr;
+ char *msg;
+ char m2[128];
+ Icmppriv *ipriv;
+
+ ipriv = icmp->priv;
+
+ ipriv->stats[InMsgs]++;
+
+ p = (Icmp *)bp->rp;
+ netlog(icmp->f, Logicmp, "icmpiput %s (%d) %d\n",
+ (p->type < nelem(icmpnames)? icmpnames[p->type]: ""),
+ p->type, p->code);
+ n = blocklen(bp);
+ if(n < ICMP_IPSIZE+ICMP_HDRSIZE){
+ ipriv->stats[InErrors]++;
+ ipriv->stats[HlenErrs]++;
+ netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
+ goto raise;
+ }
+ iplen = nhgets(p->length);
+ if(iplen > n){
+ ipriv->stats[LenErrs]++;
+ ipriv->stats[InErrors]++;
+ netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
+ goto raise;
+ }
+ if(ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)){
+ ipriv->stats[InErrors]++;
+ ipriv->stats[CsumErrs]++;
+ netlog(icmp->f, Logicmp, "icmp checksum error\n");
+ goto raise;
+ }
+ if(p->type <= Maxtype)
+ ipriv->in[p->type]++;
+
+ switch(p->type) {
+ case EchoRequest:
+ if (iplen < n)
+ bp = trimblock(bp, 0, iplen);
+ r = mkechoreply(bp);
+ ipriv->out[EchoReply]++;
+ ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
+ break;
+ case Unreachable:
+ if(p->code > 5)
+ msg = unreachcode[1];
+ else
+ msg = unreachcode[p->code];
+
+ bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
+ if(blocklen(bp) < MinAdvise){
+ ipriv->stats[LenErrs]++;
+ goto raise;
+ }
+ p = (Icmp *)bp->rp;
+ pr = Fsrcvpcolx(icmp->f, p->proto);
+ if(pr != nil && pr->advise != nil) {
+ (*pr->advise)(pr, bp, msg);
+ return;
+ }
+
+ bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
+ goticmpkt(icmp, bp);
+ break;
+ case TimeExceed:
+ if(p->code == 0){
+ sprint(m2, "ttl exceeded at %V", p->src);
+
+ bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
+ if(blocklen(bp) < MinAdvise){
+ ipriv->stats[LenErrs]++;
+ goto raise;
+ }
+ p = (Icmp *)bp->rp;
+ pr = Fsrcvpcolx(icmp->f, p->proto);
+ if(pr != nil && pr->advise != nil) {
+ (*pr->advise)(pr, bp, m2);
+ return;
+ }
+ bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
+ }
+
+ goticmpkt(icmp, bp);
+ break;
+ default:
+ goticmpkt(icmp, bp);
+ break;
+ }
+ return;
+
+raise:
+ freeblist(bp);
+}
+
+void
+icmpadvise(Proto *icmp, Block *bp, char *msg)
+{
+ Conv **c, *s;
+ Icmp *p;
+ uchar dst[IPaddrlen];
+ ushort recid;
+
+ p = (Icmp *) bp->rp;
+ v4tov6(dst, p->dst);
+ recid = nhgets(p->icmpid);
+
+ for(c = icmp->conv; *c; c++) {
+ s = *c;
+ if(s->lport == recid)
+ if(ipcmp(s->raddr, dst) == 0){
+ qhangup(s->rq, msg);
+ qhangup(s->wq, msg);
+ break;
+ }
+ }
+ freeblist(bp);
+}
+
+int
+icmpstats(Proto *icmp, char *buf, int len)
+{
+ Icmppriv *priv;
+ char *p, *e;
+ int i;
+
+ priv = icmp->priv;
+ p = buf;
+ e = p+len;
+ for(i = 0; i < Nstats; i++)
+ p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]);
+ for(i = 0; i <= Maxtype; i++){
+ if(icmpnames[i])
+ p = seprint(p, e, "%s: %lud %lud\n", icmpnames[i], priv->in[i], priv->out[i]);
+ else
+ p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]);
+ }
+ return p - buf;
+}
+
+void
+icmpinit(Fs *fs)
+{
+ Proto *icmp;
+
+ icmp = smalloc(sizeof(Proto));
+ icmp->priv = smalloc(sizeof(Icmppriv));
+ icmp->name = "icmp";
+ icmp->connect = icmpconnect;
+ icmp->announce = icmpannounce;
+ icmp->state = icmpstate;
+ icmp->create = icmpcreate;
+ icmp->close = icmpclose;
+ icmp->rcv = icmpiput;
+ icmp->stats = icmpstats;
+ icmp->ctl = nil;
+ icmp->advise = icmpadvise;
+ icmp->gc = nil;
+ icmp->ipproto = IP_ICMPPROTO;
+ icmp->nc = 128;
+ icmp->ptclsize = 0;
+
+ Fsproto(fs, icmp);
+}