]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/icmp.c
devip: various icmp stuff
[plan9front.git] / sys / src / 9 / ip / icmp.c
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
110 icmpcreate(Conv *c)
111 {
112         c->rq = qopen(64*1024, Qmsg, 0, c);
113         c->wq = qbypass(icmpkick, c);
114 }
115
116 char*
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 int
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 char*
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 void
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
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 static int
194 ip4reply(Fs *f, uchar ip4[4])
195 {
196         uchar addr[IPaddrlen];
197         int i;
198
199         v4tov6(addr, ip4);
200         if(ipismulticast(addr))
201                 return 0;
202         i = ipforme(f, addr);
203         return i == 0 || i == Runi;
204 }
205
206 static int
207 ip4me(Fs *f, uchar ip4[4])
208 {
209         uchar addr[IPaddrlen];
210
211         v4tov6(addr, ip4);
212         if(ipismulticast(addr))
213                 return 0;
214         return ipforme(f, addr) == Runi;
215 }
216
217 void
218 icmpttlexceeded(Fs *f, Ipifc *ifc, Block *bp)
219 {
220         Block   *nbp;
221         Icmp    *p, *np;
222         uchar   ia[IPv4addrlen];
223
224         p = (Icmp *)bp->rp;
225         if(!ip4reply(f, p->src) || !ipv4local(ifc, ia, p->src))
226                 return;
227
228         netlog(f, Logicmp, "sending icmpttlexceeded %V -> src %V dst %V\n",
229                 ia, p->src, p->dst);
230
231         nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
232         nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
233         np = (Icmp *)nbp->rp;
234         np->vihl = IP_VER4;
235         memmove(np->src, ia, sizeof(np->src));
236         memmove(np->dst, p->src, sizeof(np->dst));
237         memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
238         np->type = TimeExceed;
239         np->code = 0;
240         np->proto = IP_ICMPPROTO;
241         hnputs(np->icmpid, 0);
242         hnputs(np->seq, 0);
243         memset(np->cksum, 0, sizeof(np->cksum));
244         hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
245         ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
246 }
247
248 static void
249 icmpunreachable(Fs *f, Block *bp, int code, int seq)
250 {
251         Block   *nbp;
252         Icmp    *p, *np;
253
254         p = (Icmp *)bp->rp;
255         if(!ip4me(f, p->dst) || !ip4reply(f, p->src))
256                 return;
257
258         netlog(f, Logicmp, "sending icmpnoconv -> %V\n", p->src);
259         nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
260         nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
261         np = (Icmp *)nbp->rp;
262         np->vihl = IP_VER4;
263         memmove(np->dst, p->src, sizeof(np->dst));
264         memmove(np->src, p->dst, sizeof(np->src));
265         memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
266         np->type = Unreachable;
267         np->code = code;
268         np->proto = IP_ICMPPROTO;
269         hnputs(np->icmpid, 0);
270         hnputs(np->seq, seq);
271         memset(np->cksum, 0, sizeof(np->cksum));
272         hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
273         ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
274 }
275
276 void
277 icmpnoconv(Fs *f, Block *bp)
278 {
279         icmpunreachable(f, bp, 3, 0);
280 }
281
282 void
283 icmpcantfrag(Fs *f, Block *bp, int mtu)
284 {
285         icmpunreachable(f, bp, 4, mtu);
286 }
287
288 static void
289 goticmpkt(Proto *icmp, Block *bp)
290 {
291         ushort  recid;
292         uchar   dst[IPaddrlen], src[IPaddrlen];
293         Conv    **c, *s;
294         Icmp    *p;
295
296         p = (Icmp *) bp->rp;
297         v4tov6(dst, p->dst);
298         v4tov6(src, p->src);
299         recid = nhgets(p->icmpid);
300
301         for(c = icmp->conv; (s = *c) != nil; c++){
302                 if(s->lport == recid)
303                 if(ipcmp(s->laddr, dst) == 0 || ipcmp(s->raddr, src) == 0)
304                         qpass(s->rq, copyblock(bp, blocklen(bp)));
305         }
306         freeblist(bp);
307 }
308
309 static Block *
310 mkechoreply(Block *bp, Fs *f)
311 {
312         Icmp    *q;
313         uchar   ip[4];
314
315         q = (Icmp *)bp->rp;
316         if(!ip4me(f, q->dst) || !ip4reply(f, q->src))
317                 return nil;
318
319         q->vihl = IP_VER4;
320         memmove(ip, q->src, sizeof(q->dst));
321         memmove(q->src, q->dst, sizeof(q->src));
322         memmove(q->dst, ip,  sizeof(q->dst));
323         q->type = EchoReply;
324         memset(q->cksum, 0, sizeof(q->cksum));
325         hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
326
327         return bp;
328 }
329
330 static char *unreachcode[] =
331 {
332 [0]     "net unreachable",
333 [1]     "host unreachable",
334 [2]     "protocol unreachable",
335 [3]     "port unreachable",
336 [4]     "fragmentation needed and DF set",
337 [5]     "source route failed",
338 [6]     "destination network unknown",
339 [7]     "destination host unknown",
340 [8]     "source host isolated",
341 [9]     "network administratively prohibited",
342 [10]    "host administratively prohibited",
343 [11]    "network unreachable for tos",
344 [12]    "host unreachable for tos",
345 [13]    "communication administratively prohibited",
346 [14]    "host precedence violation",
347 [15]    "precedence cutoff in effect",
348 };
349
350 static void
351 icmpiput(Proto *icmp, Ipifc*, Block *bp)
352 {
353         int     n, iplen;
354         Icmp    *p;
355         Block   *r;
356         Proto   *pr;
357         char    *msg;
358         char    m2[128];
359         Icmppriv *ipriv;
360
361         ipriv = icmp->priv;
362         
363         ipriv->stats[InMsgs]++;
364
365         p = (Icmp *)bp->rp;
366         netlog(icmp->f, Logicmp, "icmpiput %s (%d) %d\n",
367                 (p->type < nelem(icmpnames)? icmpnames[p->type]: ""),
368                 p->type, p->code);
369         n = blocklen(bp);
370         if(n < ICMP_IPSIZE+ICMP_HDRSIZE){
371                 ipriv->stats[InErrors]++;
372                 ipriv->stats[HlenErrs]++;
373                 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
374                 goto raise;
375         }
376         iplen = nhgets(p->length);
377         if(iplen > n){
378                 ipriv->stats[LenErrs]++;
379                 ipriv->stats[InErrors]++;
380                 netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
381                 goto raise;
382         }
383         if(ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)){
384                 ipriv->stats[InErrors]++;
385                 ipriv->stats[CsumErrs]++;
386                 netlog(icmp->f, Logicmp, "icmp checksum error\n");
387                 goto raise;
388         }
389         if(p->type <= Maxtype)
390                 ipriv->in[p->type]++;
391
392         switch(p->type) {
393         case EchoRequest:
394                 if(iplen < n)
395                         bp = trimblock(bp, 0, iplen);
396                 if(bp->next != nil)
397                         bp = concatblock(bp);
398                 r = mkechoreply(bp, icmp->f);
399                 if(r == nil)
400                         goto raise;
401                 ipriv->out[EchoReply]++;
402                 ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
403                 break;
404         case Unreachable:
405                 if(p->code >= nelem(unreachcode)) {
406                         snprint(m2, sizeof m2, "unreachable %V -> %V code %d",
407                                 p->src, p->dst, p->code);
408                         msg = m2;
409                 } else
410                         msg = unreachcode[p->code];
411
412                 bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
413                 if(blocklen(bp) < MinAdvise){
414                         ipriv->stats[LenErrs]++;
415                         goto raise;
416                 }
417                 p = (Icmp *)bp->rp;
418                 pr = Fsrcvpcolx(icmp->f, p->proto);
419                 if(pr != nil && pr->advise != nil) {
420                         (*pr->advise)(pr, bp, msg);
421                         return;
422                 }
423
424                 bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
425                 goticmpkt(icmp, bp);
426                 break;
427         case TimeExceed:
428                 if(p->code == 0){
429                         snprint(m2, sizeof m2, "ttl exceeded at %V", p->src);
430
431                         bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
432                         if(blocklen(bp) < MinAdvise){
433                                 ipriv->stats[LenErrs]++;
434                                 goto raise;
435                         }
436                         p = (Icmp *)bp->rp;
437                         pr = Fsrcvpcolx(icmp->f, p->proto);
438                         if(pr != nil && pr->advise != nil) {
439                                 (*pr->advise)(pr, bp, m2);
440                                 return;
441                         }
442                         bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
443                 }
444
445                 goticmpkt(icmp, bp);
446                 break;
447         default:
448                 goticmpkt(icmp, bp);
449                 break;
450         }
451         return;
452
453 raise:
454         freeblist(bp);
455 }
456
457 static void
458 icmpadvise(Proto *icmp, Block *bp, char *msg)
459 {
460         ushort  recid;
461         uchar   dst[IPaddrlen], src[IPaddrlen];
462         Conv    **c, *s;
463         Icmp    *p;
464
465         p = (Icmp *) bp->rp;
466         v4tov6(dst, p->dst);
467         v4tov6(src, p->src);
468         recid = nhgets(p->icmpid);
469
470         for(c = icmp->conv; (s = *c) != nil; c++){
471                 if(s->lport == recid)
472                 if(ipcmp(s->laddr, src) == 0)
473                 if(ipcmp(s->raddr, dst) == 0){
474                         if(s->ignoreadvice)
475                                 break;
476                         qhangup(s->rq, msg);
477                         qhangup(s->wq, msg);
478                         break;
479                 }
480         }
481         freeblist(bp);
482 }
483
484 static int
485 icmpstats(Proto *icmp, char *buf, int len)
486 {
487         Icmppriv *priv;
488         char *p, *e;
489         int i;
490
491         priv = icmp->priv;
492         p = buf;
493         e = p+len;
494         for(i = 0; i < Nstats; i++)
495                 p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]);
496         for(i = 0; i <= Maxtype; i++){
497                 if(icmpnames[i] != nil)
498                         p = seprint(p, e, "%s: %lud %lud\n", icmpnames[i], priv->in[i], priv->out[i]);
499                 else
500                         p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]);
501         }
502         return p - buf;
503 }
504         
505 void
506 icmpinit(Fs *fs)
507 {
508         Proto *icmp;
509
510         icmp = smalloc(sizeof(Proto));
511         icmp->priv = smalloc(sizeof(Icmppriv));
512         icmp->name = "icmp";
513         icmp->connect = icmpconnect;
514         icmp->announce = icmpannounce;
515         icmp->state = icmpstate;
516         icmp->create = icmpcreate;
517         icmp->close = icmpclose;
518         icmp->rcv = icmpiput;
519         icmp->stats = icmpstats;
520         icmp->ctl = nil;
521         icmp->advise = icmpadvise;
522         icmp->gc = nil;
523         icmp->ipproto = IP_ICMPPROTO;
524         icmp->nc = 128;
525         icmp->ptclsize = 0;
526
527         Fsproto(fs, icmp);
528 }