1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7
8 #include "ip.h"
9
10 typedef struct Icmp {
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 */
21 uchar type;
22 uchar code;
23 uchar cksum[2];
24 uchar icmpid[2];
25 uchar seq[2];
26 uchar data[1];
27 } Icmp;
28
29 enum { /* Packet Types */
30 EchoReply = 0,
31 Unreachable = 3,
32 SrcQuench = 4,
33 Redirect = 5,
34 EchoRequest = 8,
35 TimeExceed = 11,
36 InParmProblem = 12,
37 Timestamp = 13,
38 TimestampReply = 14,
39 InfoRequest = 15,
40 InfoReply = 16,
41 AddrMaskRequest = 17,
42 AddrMaskReply = 18,
43
44 Maxtype = 18,
45 };
46
47 enum
48 {
49 MinAdvise = 24, /* minimum needed for us to advise another protocol */
50 };
51
52 char *icmpnames[Maxtype+1] =
53 {
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",
67 };
68
69 enum {
70 IP_ICMPPROTO = 1,
71 ICMP_IPSIZE = 20,
72 ICMP_HDRSIZE = 8,
73 };
74
75 enum
76 {
77 InMsgs,
78 InErrors,
79 OutMsgs,
80 CsumErrs,
81 LenErrs,
82 HlenErrs,
83
84 Nstats,
85 };
86
87 static char *statnames[Nstats] =
88 {
89 [InMsgs] "InMsgs",
90 [InErrors] "InErrors",
91 [OutMsgs] "OutMsgs",
92 [CsumErrs] "CsumErrs",
93 [LenErrs] "LenErrs",
94 [HlenErrs] "HlenErrs",
95 };
96
97 typedef struct Icmppriv Icmppriv;
98 struct Icmppriv
99 {
100 ulong stats[Nstats];
101
102 /* message counts */
103 ulong in[Maxtype+1];
104 ulong out[Maxtype+1];
105 };
106
107 static void icmpkick(void *x, Block*);
108
109 static void
icmpcreate(Conv * c)110 icmpcreate(Conv *c)
111 {
112 c->rq = qopen(64*1024, Qmsg, 0, c);
113 c->wq = qbypass(icmpkick, c);
114 }
115
116 extern char*
icmpconnect(Conv * c,char ** argv,int argc)117 icmpconnect(Conv *c, char **argv, int argc)
118 {
119 char *e;
120
121 e = Fsstdconnect(c, argv, argc);
122 if(e != nil)
123 return e;
124 Fsconnected(c, e);
125
126 return nil;
127 }
128
129 extern int
icmpstate(Conv * c,char * state,int n)130 icmpstate(Conv *c, char *state, int n)
131 {
132 USED(c);
133 return snprint(state, n, "%s qin %d qout %d\n",
134 "Datagram",
135 c->rq ? qlen(c->rq) : 0,
136 c->wq ? qlen(c->wq) : 0
137 );
138 }
139
140 extern char*
icmpannounce(Conv * c,char ** argv,int argc)141 icmpannounce(Conv *c, char **argv, int argc)
142 {
143 char *e;
144
145 e = Fsstdannounce(c, argv, argc);
146 if(e != nil)
147 return e;
148 Fsconnected(c, nil);
149
150 return nil;
151 }
152
153 extern void
icmpclose(Conv * c)154 icmpclose(Conv *c)
155 {
156 qclose(c->rq);
157 qclose(c->wq);
158 ipmove(c->laddr, IPnoaddr);
159 ipmove(c->raddr, IPnoaddr);
160 c->lport = 0;
161 }
162
163 static void
icmpkick(void * x,Block * bp)164 icmpkick(void *x, Block *bp)
165 {
166 Conv *c = x;
167 Icmp *p;
168 Icmppriv *ipriv;
169
170 if(bp == nil)
171 return;
172
173 if(blocklen(bp) < ICMP_IPSIZE + ICMP_HDRSIZE){
174 freeblist(bp);
175 return;
176 }
177 p = (Icmp *)(bp->rp);
178 p->vihl = IP_VER4;
179 ipriv = c->p->priv;
180 if(p->type <= Maxtype)
181 ipriv->out[p->type]++;
182
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);
191 }
192
193 extern void
icmpttlexceeded(Fs * f,uchar * ia,Block * bp)194 icmpttlexceeded(Fs *f, uchar *ia, Block *bp)
195 {
196 Block *nbp;
197 Icmp *p, *np;
198
199 p = (Icmp *)bp->rp;
200
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;
205 np->vihl = IP_VER4;
206 memmove(np->dst, p->src, sizeof(np->dst));
207 v6tov4(np->src, ia);
208 memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
209 np->type = TimeExceed;
210 np->code = 0;
211 np->proto = IP_ICMPPROTO;
212 hnputs(np->icmpid, 0);
213 hnputs(np->seq, 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);
217
218 }
219
220 static void
icmpunreachable(Fs * f,Block * bp,int code,int seq)221 icmpunreachable(Fs *f, Block *bp, int code, int seq)
222 {
223 Block *nbp;
224 Icmp *p, *np;
225 int i;
226 uchar addr[IPaddrlen];
227
228 p = (Icmp *)bp->rp;
229
230 /* only do this for unicast sources and destinations */
231 v4tov6(addr, p->dst);
232 i = ipforme(f, addr);
233 if((i&Runi) == 0)
234 return;
235 v4tov6(addr, p->src);
236 i = ipforme(f, addr);
237 if(i != 0 && (i&Runi) == 0)
238 return;
239
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;
244 np->vihl = IP_VER4;
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;
249 np->code = code;
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);
256 }
257
258 extern void
icmpnoconv(Fs * f,Block * bp)259 icmpnoconv(Fs *f, Block *bp)
260 {
261 icmpunreachable(f, bp, 3, 0);
262 }
263
264 extern void
icmpcantfrag(Fs * f,Block * bp,int mtu)265 icmpcantfrag(Fs *f, Block *bp, int mtu)
266 {
267 icmpunreachable(f, bp, 4, mtu);
268 }
269
270 static void
goticmpkt(Proto * icmp,Block * bp)271 goticmpkt(Proto *icmp, Block *bp)
272 {
273 Conv **c, *s;
274 Icmp *p;
275 uchar dst[IPaddrlen];
276 ushort recid;
277
278 p = (Icmp *) bp->rp;
279 v4tov6(dst, p->src);
280 recid = nhgets(p->icmpid);
281
282 for(c = icmp->conv; *c; c++) {
283 s = *c;
284 if(s->lport == recid)
285 if(ipcmp(s->raddr, dst) == 0){
286 bp = concatblock(bp);
287 if(bp != nil)
288 qpass(s->rq, bp);
289 return;
290 }
291 }
292 freeblist(bp);
293 }
294
295 static Block *
mkechoreply(Block * bp)296 mkechoreply(Block *bp)
297 {
298 Icmp *q;
299 uchar ip[4];
300
301 q = (Icmp *)bp->rp;
302 q->vihl = IP_VER4;
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));
306 q->type = EchoReply;
307 memset(q->cksum, 0, sizeof(q->cksum));
308 hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
309
310 return bp;
311 }
312
313 static char *unreachcode[] =
314 {
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 };
322
323 static void
icmpiput(Proto * icmp,Ipifc *,Block * bp)324 icmpiput(Proto *icmp, Ipifc*, Block *bp)
325 {
326 int n, iplen;
327 Icmp *p;
328 Block *r;
329 Proto *pr;
330 char *msg;
331 char m2[128];
332 Icmppriv *ipriv;
333
334 ipriv = icmp->priv;
335
336 ipriv->stats[InMsgs]++;
337
338 p = (Icmp *)bp->rp;
339 netlog(icmp->f, Logicmp, "icmpiput %s (%d) %d\n",
340 (p->type < nelem(icmpnames)? icmpnames[p->type]: ""),
341 p->type, p->code);
342 n = blocklen(bp);
343 if(n < ICMP_IPSIZE+ICMP_HDRSIZE){
344 ipriv->stats[InErrors]++;
345 ipriv->stats[HlenErrs]++;
346 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
347 goto raise;
348 }
349 iplen = nhgets(p->length);
350 if(iplen > n){
351 ipriv->stats[LenErrs]++;
352 ipriv->stats[InErrors]++;
353 netlog(icmp->f, Logicmp, "icmp length error n %d iplen %d\n",
354 n, iplen);
355 goto raise;
356 }
357 if(ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)){
358 ipriv->stats[InErrors]++;
359 ipriv->stats[CsumErrs]++;
360 netlog(icmp->f, Logicmp, "icmp checksum error n %d iplen %d\n",
361 n, iplen);
362 goto raise;
363 }
364 if(p->type <= Maxtype)
365 ipriv->in[p->type]++;
366
367 switch(p->type) {
368 case EchoRequest:
369 if (iplen < n)
370 bp = trimblock(bp, 0, iplen);
371 r = mkechoreply(concatblock(bp));
372 ipriv->out[EchoReply]++;
373 ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
374 break;
375 case Unreachable:
376 if(p->code > 5)
377 msg = unreachcode[1];
378 else
379 msg = unreachcode[p->code];
380
381 bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
382 if(blocklen(bp) < MinAdvise){
383 ipriv->stats[LenErrs]++;
384 goto raise;
385 }
386 p = (Icmp *)bp->rp;
387 pr = Fsrcvpcolx(icmp->f, p->proto);
388 if(pr != nil && pr->advise != nil) {
389 (*pr->advise)(pr, bp, msg);
390 return;
391 }
392
393 bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
394 goticmpkt(icmp, bp);
395 break;
396 case TimeExceed:
397 if(p->code == 0){
398 snprint(m2, sizeof m2, "ttl exceeded at %V", p->src);
399
400 bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
401 if(blocklen(bp) < MinAdvise){
402 ipriv->stats[LenErrs]++;
403 goto raise;
404 }
405 p = (Icmp *)bp->rp;
406 pr = Fsrcvpcolx(icmp->f, p->proto);
407 if(pr != nil && pr->advise != nil) {
408 (*pr->advise)(pr, bp, m2);
409 return;
410 }
411 bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
412 }
413
414 goticmpkt(icmp, bp);
415 break;
416 default:
417 goticmpkt(icmp, bp);
418 break;
419 }
420 return;
421
422 raise:
423 freeblist(bp);
424 }
425
426 void
icmpadvise(Proto * icmp,Block * bp,char * msg)427 icmpadvise(Proto *icmp, Block *bp, char *msg)
428 {
429 Conv **c, *s;
430 Icmp *p;
431 uchar dst[IPaddrlen];
432 ushort recid;
433
434 p = (Icmp *) bp->rp;
435 v4tov6(dst, p->dst);
436 recid = nhgets(p->icmpid);
437
438 for(c = icmp->conv; *c; c++) {
439 s = *c;
440 if(s->lport == recid)
441 if(ipcmp(s->raddr, dst) == 0){
442 qhangup(s->rq, msg);
443 qhangup(s->wq, msg);
444 break;
445 }
446 }
447 freeblist(bp);
448 }
449
450 int
icmpstats(Proto * icmp,char * buf,int len)451 icmpstats(Proto *icmp, char *buf, int len)
452 {
453 Icmppriv *priv;
454 char *p, *e;
455 int i;
456
457 priv = icmp->priv;
458 p = buf;
459 e = p+len;
460 for(i = 0; i < Nstats; i++)
461 p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]);
462 for(i = 0; i <= Maxtype; i++){
463 if(icmpnames[i])
464 p = seprint(p, e, "%s: %lud %lud\n", icmpnames[i], priv->in[i], priv->out[i]);
465 else
466 p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]);
467 }
468 return p - buf;
469 }
470
471 void
icmpinit(Fs * fs)472 icmpinit(Fs *fs)
473 {
474 Proto *icmp;
475
476 icmp = smalloc(sizeof(Proto));
477 icmp->priv = smalloc(sizeof(Icmppriv));
478 icmp->name = "icmp";
479 icmp->connect = icmpconnect;
480 icmp->announce = icmpannounce;
481 icmp->state = icmpstate;
482 icmp->create = icmpcreate;
483 icmp->close = icmpclose;
484 icmp->rcv = icmpiput;
485 icmp->stats = icmpstats;
486 icmp->ctl = nil;
487 icmp->advise = icmpadvise;
488 icmp->gc = nil;
489 icmp->ipproto = IP_ICMPPROTO;
490 icmp->nc = 128;
491 icmp->ptclsize = 0;
492
493 Fsproto(fs, icmp);
494 }
495