2 #include "../port/lib.h"
6 #include "../port/error.h"
11 uchar vihl; /* Version and header length */
12 uchar tos; /* Type of service */
13 uchar length[2]; /* packet length */
14 uchar id[2]; /* Identification */
15 uchar frag[2]; /* Fragment information */
16 uchar ttl; /* Time to live */
17 uchar proto; /* Protocol */
18 uchar ipcksum[2]; /* Header checksum */
19 uchar src[4]; /* Ip source */
20 uchar dst[4]; /* Ip destination */
29 enum { /* Packet Types */
49 MinAdvise = 24, /* minimum needed for us to advise another protocol */
52 char *icmpnames[Maxtype+1] =
54 [EchoReply] "EchoReply",
55 [Unreachable] "Unreachable",
56 [SrcQuench] "SrcQuench",
57 [Redirect] "Redirect",
58 [EchoRequest] "EchoRequest",
59 [TimeExceed] "TimeExceed",
60 [InParmProblem] "InParmProblem",
61 [Timestamp] "Timestamp",
62 [TimestampReply] "TimestampReply",
63 [InfoRequest] "InfoRequest",
64 [InfoReply] "InfoReply",
65 [AddrMaskRequest] "AddrMaskRequest",
66 [AddrMaskReply ] "AddrMaskReply ",
87 static char *statnames[Nstats] =
90 [InErrors] "InErrors",
92 [CsumErrs] "CsumErrs",
94 [HlenErrs] "HlenErrs",
97 typedef struct Icmppriv Icmppriv;
104 ulong out[Maxtype+1];
107 static void icmpkick(void *x, Block*);
112 c->rq = qopen(64*1024, Qmsg, 0, c);
113 c->wq = qbypass(icmpkick, c);
117 icmpconnect(Conv *c, char **argv, int argc)
121 e = Fsstdconnect(c, argv, argc);
130 icmpstate(Conv *c, char *state, int n)
133 return snprint(state, n, "%s qin %d qout %d\n",
135 c->rq ? qlen(c->rq) : 0,
136 c->wq ? qlen(c->wq) : 0
141 icmpannounce(Conv *c, char **argv, int argc)
145 e = Fsstdannounce(c, argv, argc);
158 ipmove(c->laddr, IPnoaddr);
159 ipmove(c->raddr, IPnoaddr);
164 icmpkick(void *x, Block *bp)
173 if(blocklen(bp) < ICMP_IPSIZE + ICMP_HDRSIZE){
177 p = (Icmp *)(bp->rp);
180 if(p->type <= Maxtype)
181 ipriv->out[p->type]++;
183 v6tov4(p->dst, c->raddr);
184 v6tov4(p->src, c->laddr);
185 p->proto = IP_ICMPPROTO;
186 hnputs(p->icmpid, c->lport);
187 memset(p->cksum, 0, sizeof(p->cksum));
188 hnputs(p->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
189 ipriv->stats[OutMsgs]++;
190 ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
194 icmpttlexceeded(Fs *f, uchar *ia, Block *bp)
201 netlog(f, Logicmp, "sending icmpttlexceeded -> %V\n", p->src);
202 nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
203 nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
204 np = (Icmp *)nbp->rp;
206 memmove(np->dst, p->src, sizeof(np->dst));
208 memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
209 np->type = TimeExceed;
211 np->proto = IP_ICMPPROTO;
212 hnputs(np->icmpid, 0);
214 memset(np->cksum, 0, sizeof(np->cksum));
215 hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
216 ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
221 icmpunreachable(Fs *f, Block *bp, int code, int seq)
226 uchar addr[IPaddrlen];
230 /* only do this for unicast sources and destinations */
231 v4tov6(addr, p->dst);
232 i = ipforme(f, addr);
235 v4tov6(addr, p->src);
236 i = ipforme(f, addr);
237 if(i != 0 && (i&Runi) == 0)
240 netlog(f, Logicmp, "sending icmpnoconv -> %V\n", p->src);
241 nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
242 nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
243 np = (Icmp *)nbp->rp;
245 memmove(np->dst, p->src, sizeof(np->dst));
246 memmove(np->src, p->dst, sizeof(np->src));
247 memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
248 np->type = Unreachable;
250 np->proto = IP_ICMPPROTO;
251 hnputs(np->icmpid, 0);
252 hnputs(np->seq, seq);
253 memset(np->cksum, 0, sizeof(np->cksum));
254 hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
255 ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
259 icmpnoconv(Fs *f, Block *bp)
261 icmpunreachable(f, bp, 3, 0);
265 icmpcantfrag(Fs *f, Block *bp, int mtu)
267 icmpunreachable(f, bp, 4, mtu);
271 goticmpkt(Proto *icmp, Block *bp)
275 uchar dst[IPaddrlen];
280 recid = nhgets(p->icmpid);
282 for(c = icmp->conv; *c; c++) {
284 if(s->lport == recid)
285 if(ipcmp(s->raddr, dst) == 0){
286 bp = concatblock(bp);
296 mkechoreply(Block *bp)
303 memmove(ip, q->src, sizeof(q->dst));
304 memmove(q->src, q->dst, sizeof(q->src));
305 memmove(q->dst, ip, sizeof(q->dst));
307 memset(q->cksum, 0, sizeof(q->cksum));
308 hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
313 static char *unreachcode[] =
315 [0] "net unreachable",
316 [1] "host unreachable",
317 [2] "protocol unreachable",
318 [3] "port unreachable",
319 [4] "fragmentation needed and DF set",
320 [5] "source route failed",
321 [6] "destination network unknown",
322 [7] "destination host unknown",
323 [8] "source host isolated",
324 [9] "network administratively prohibited",
325 [10] "host administratively prohibited",
326 [11] "network unreachable for tos",
327 [12] "host unreachable for tos",
328 [13] "communication administratively prohibited",
329 [14] "host precedence violation",
330 [15] "precedence cutoff in effect",
334 icmpiput(Proto *icmp, Ipifc*, Block *bp)
346 ipriv->stats[InMsgs]++;
349 netlog(icmp->f, Logicmp, "icmpiput %s (%d) %d\n",
350 (p->type < nelem(icmpnames)? icmpnames[p->type]: ""),
353 if(n < ICMP_IPSIZE+ICMP_HDRSIZE){
354 ipriv->stats[InErrors]++;
355 ipriv->stats[HlenErrs]++;
356 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
359 iplen = nhgets(p->length);
361 ipriv->stats[LenErrs]++;
362 ipriv->stats[InErrors]++;
363 netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
366 if(ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)){
367 ipriv->stats[InErrors]++;
368 ipriv->stats[CsumErrs]++;
369 netlog(icmp->f, Logicmp, "icmp checksum error\n");
372 if(p->type <= Maxtype)
373 ipriv->in[p->type]++;
378 bp = trimblock(bp, 0, iplen);
379 r = mkechoreply(concatblock(bp));
380 ipriv->out[EchoReply]++;
381 ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
384 if(p->code >= nelem(unreachcode)) {
385 snprint(m2, sizeof m2, "unreachable %V->%V code %d",
386 p->src, p->dst, p->code);
389 msg = unreachcode[p->code];
391 bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
392 if(blocklen(bp) < MinAdvise){
393 ipriv->stats[LenErrs]++;
397 pr = Fsrcvpcolx(icmp->f, p->proto);
398 if(pr != nil && pr->advise != nil) {
399 (*pr->advise)(pr, bp, msg);
403 bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
408 snprint(m2, sizeof m2, "ttl exceeded at %V", p->src);
410 bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
411 if(blocklen(bp) < MinAdvise){
412 ipriv->stats[LenErrs]++;
416 pr = Fsrcvpcolx(icmp->f, p->proto);
417 if(pr != nil && pr->advise != nil) {
418 (*pr->advise)(pr, bp, m2);
421 bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
437 icmpadvise(Proto *icmp, Block *bp, char *msg)
441 uchar dst[IPaddrlen];
446 recid = nhgets(p->icmpid);
448 for(c = icmp->conv; *c; c++) {
450 if(s->lport == recid)
451 if(ipcmp(s->raddr, dst) == 0){
463 icmpstats(Proto *icmp, char *buf, int len)
472 for(i = 0; i < Nstats; i++)
473 p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]);
474 for(i = 0; i <= Maxtype; i++){
476 p = seprint(p, e, "%s: %lud %lud\n", icmpnames[i], priv->in[i], priv->out[i]);
478 p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]);
488 icmp = smalloc(sizeof(Proto));
489 icmp->priv = smalloc(sizeof(Icmppriv));
491 icmp->connect = icmpconnect;
492 icmp->announce = icmpannounce;
493 icmp->state = icmpstate;
494 icmp->create = icmpcreate;
495 icmp->close = icmpclose;
496 icmp->rcv = icmpiput;
497 icmp->stats = icmpstats;
499 icmp->advise = icmpadvise;
501 icmp->ipproto = IP_ICMPPROTO;