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 */
47 char *icmpnames[Maxtype+1] =
49 [EchoReply] "EchoReply",
50 [Unreachable] "Unreachable",
51 [SrcQuench] "SrcQuench",
52 [Redirect] "Redirect",
53 [EchoRequest] "EchoRequest",
54 [TimeExceed] "TimeExceed",
55 [InParmProblem] "InParmProblem",
56 [Timestamp] "Timestamp",
57 [TimestampReply] "TimestampReply",
58 [InfoRequest] "InfoRequest",
59 [InfoReply] "InfoReply",
60 [AddrMaskRequest] "AddrMaskRequest",
61 [AddrMaskReply ] "AddrMaskReply ",
69 MinAdvise = ICMP_IPSIZE+4, /* minimum needed for us to advise another protocol */
84 static char *statnames[Nstats] =
87 [InErrors] "InErrors",
89 [CsumErrs] "CsumErrs",
91 [HlenErrs] "HlenErrs",
94 typedef struct Icmppriv Icmppriv;
101 ulong out[Maxtype+1];
104 static void icmpkick(void *x, Block*);
109 c->rq = qopen(64*1024, Qmsg, 0, c);
110 c->wq = qbypass(icmpkick, c);
114 icmpconnect(Conv *c, char **argv, int argc)
118 e = Fsstdconnect(c, argv, argc);
127 icmpstate(Conv *c, char *state, int n)
130 return snprint(state, n, "%s qin %d qout %d\n",
132 c->rq ? qlen(c->rq) : 0,
133 c->wq ? qlen(c->wq) : 0
138 icmpannounce(Conv *c, char **argv, int argc)
142 e = Fsstdannounce(c, argv, argc);
155 ipmove(c->laddr, IPnoaddr);
156 ipmove(c->raddr, IPnoaddr);
161 icmpkick(void *x, Block *bp)
169 if(BLEN(bp) < ICMP_IPSIZE + ICMP_HDRSIZE){
173 p = (Icmp *)(bp->rp);
176 if(p->type <= Maxtype)
177 ipriv->out[p->type]++;
179 v6tov4(p->dst, c->raddr);
180 v6tov4(p->src, c->laddr);
181 p->proto = IP_ICMPPROTO;
182 hnputs(p->icmpid, c->lport);
183 memset(p->cksum, 0, sizeof(p->cksum));
184 hnputs(p->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
185 ipriv->stats[OutMsgs]++;
186 ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
190 ip4reply(Fs *f, uchar ip4[4])
192 uchar addr[IPaddrlen];
196 if(ipismulticast(addr))
198 i = ipforme(f, addr);
199 return i == 0 || i == Runi;
203 ip4me(Fs *f, uchar ip4[4])
205 uchar addr[IPaddrlen];
208 if(ipismulticast(addr))
210 return ipforme(f, addr) == Runi;
214 icmpttlexceeded(Fs *f, Ipifc *ifc, Block *bp)
218 uchar ia[IPv4addrlen];
221 if(!ip4reply(f, p->src) || !ipv4local(ifc, ia, p->src))
224 netlog(f, Logicmp, "sending icmpttlexceeded %V -> src %V dst %V\n",
227 nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
228 nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
229 np = (Icmp *)nbp->rp;
231 memmove(np->src, ia, sizeof(np->src));
232 memmove(np->dst, p->src, sizeof(np->dst));
233 memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
234 np->type = TimeExceed;
236 np->proto = IP_ICMPPROTO;
237 hnputs(np->icmpid, 0);
239 memset(np->cksum, 0, sizeof(np->cksum));
240 hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
241 ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
245 icmpunreachable(Fs *f, Block *bp, int code, int seq)
251 if(!ip4me(f, p->dst) || !ip4reply(f, p->src))
254 netlog(f, Logicmp, "sending icmpnoconv -> %V\n", p->src);
255 nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
256 nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
257 np = (Icmp *)nbp->rp;
259 memmove(np->dst, p->src, sizeof(np->dst));
260 memmove(np->src, p->dst, sizeof(np->src));
261 memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
262 np->type = Unreachable;
264 np->proto = IP_ICMPPROTO;
265 hnputs(np->icmpid, 0);
266 hnputs(np->seq, seq);
267 memset(np->cksum, 0, sizeof(np->cksum));
268 hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
269 ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
273 icmpnoconv(Fs *f, Block *bp)
275 icmpunreachable(f, bp, 3, 0);
279 icmpcantfrag(Fs *f, Block *bp, int mtu)
281 icmpunreachable(f, bp, 4, mtu);
285 goticmpkt(Proto *icmp, Block *bp)
288 uchar dst[IPaddrlen], src[IPaddrlen];
295 recid = nhgets(p->icmpid);
297 for(c = icmp->conv; (s = *c) != nil; c++){
298 if(s->lport == recid)
299 if(ipcmp(s->laddr, dst) == 0 || ipcmp(s->raddr, src) == 0)
300 qpass(s->rq, copyblock(bp, blocklen(bp)));
306 mkechoreply(Block *bp, Fs *f)
312 if(!ip4me(f, q->dst) || !ip4reply(f, q->src))
316 memmove(ip, q->src, sizeof(q->dst));
317 memmove(q->src, q->dst, sizeof(q->src));
318 memmove(q->dst, ip, sizeof(q->dst));
320 memset(q->cksum, 0, sizeof(q->cksum));
321 hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
326 static char *unreachcode[] =
328 [0] "net unreachable",
329 [1] "host unreachable",
330 [2] "protocol unreachable",
331 [3] "port unreachable",
332 [4] "fragmentation needed and DF set",
333 [5] "source route failed",
334 [6] "destination network unknown",
335 [7] "destination host unknown",
336 [8] "source host isolated",
337 [9] "network administratively prohibited",
338 [10] "host administratively prohibited",
339 [11] "network unreachable for tos",
340 [12] "host unreachable for tos",
341 [13] "communication administratively prohibited",
342 [14] "host precedence violation",
343 [15] "precedence cutoff in effect",
347 icmpiput(Proto *icmp, Ipifc*, Block *bp)
358 ipriv->stats[InMsgs]++;
360 bp = concatblock(bp);
362 if(n < ICMP_IPSIZE+ICMP_HDRSIZE){
363 ipriv->stats[InErrors]++;
364 ipriv->stats[HlenErrs]++;
365 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
368 if(ptclcsum(bp, ICMP_IPSIZE, n - ICMP_IPSIZE)){
369 ipriv->stats[InErrors]++;
370 ipriv->stats[CsumErrs]++;
371 netlog(icmp->f, Logicmp, "icmp checksum error\n");
375 netlog(icmp->f, Logicmp, "icmpiput %s (%d) %d\n",
376 (p->type < nelem(icmpnames)? icmpnames[p->type]: ""),
378 if(p->type <= Maxtype)
379 ipriv->in[p->type]++;
383 r = mkechoreply(bp, icmp->f);
386 ipriv->out[EchoReply]++;
387 ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
390 if(p->code >= nelem(unreachcode)) {
391 snprint(m2, sizeof m2, "unreachable %V -> %V code %d",
392 p->src, p->dst, p->code);
395 msg = unreachcode[p->code];
397 bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
398 if(BLEN(bp) < MinAdvise){
399 ipriv->stats[LenErrs]++;
403 pr = Fsrcvpcolx(icmp->f, p->proto);
404 if(pr != nil && pr->advise != nil) {
405 (*pr->advise)(pr, bp, msg);
408 bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
413 snprint(m2, sizeof m2, "ttl exceeded at %V", p->src);
415 bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
416 if(BLEN(bp) < MinAdvise){
417 ipriv->stats[LenErrs]++;
421 pr = Fsrcvpcolx(icmp->f, p->proto);
422 if(pr != nil && pr->advise != nil) {
423 (*pr->advise)(pr, bp, m2);
426 bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
441 icmpadvise(Proto *icmp, Block *bp, char *msg)
444 uchar dst[IPaddrlen], src[IPaddrlen];
451 recid = nhgets(p->icmpid);
453 for(c = icmp->conv; (s = *c) != nil; c++){
454 if(s->lport == recid)
455 if(ipcmp(s->laddr, src) == 0)
456 if(ipcmp(s->raddr, dst) == 0){
468 icmpstats(Proto *icmp, char *buf, int len)
477 for(i = 0; i < Nstats; i++)
478 p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]);
479 for(i = 0; i <= Maxtype; i++){
480 if(icmpnames[i] != nil)
481 p = seprint(p, e, "%s: %lud %lud\n", icmpnames[i], priv->in[i], priv->out[i]);
483 p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]);
493 icmp = smalloc(sizeof(Proto));
494 icmp->priv = smalloc(sizeof(Icmppriv));
496 icmp->connect = icmpconnect;
497 icmp->announce = icmpannounce;
498 icmp->state = icmpstate;
499 icmp->create = icmpcreate;
500 icmp->close = icmpclose;
501 icmp->rcv = icmpiput;
502 icmp->stats = icmpstats;
504 icmp->advise = icmpadvise;
506 icmp->ipproto = IP_ICMPPROTO;