]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/icmp.c
devip: fix block list handling for icmp/icmp6, use proper MinAdvise for icmp6
[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 char *icmpnames[Maxtype+1] =
48 {
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  ",
62 };
63
64 enum {
65         IP_ICMPPROTO    = 1,
66         ICMP_IPSIZE     = 20,
67         ICMP_HDRSIZE    = 8,
68
69         MinAdvise       = ICMP_IPSIZE+4,        /* minimum needed for us to advise another protocol */ 
70 };
71
72 enum
73 {
74         InMsgs,
75         InErrors,
76         OutMsgs,
77         CsumErrs,
78         LenErrs,
79         HlenErrs,
80
81         Nstats,
82 };
83
84 static char *statnames[Nstats] =
85 {
86 [InMsgs]        "InMsgs",
87 [InErrors]      "InErrors",
88 [OutMsgs]       "OutMsgs",
89 [CsumErrs]      "CsumErrs",
90 [LenErrs]       "LenErrs",
91 [HlenErrs]      "HlenErrs",
92 };
93
94 typedef struct Icmppriv Icmppriv;
95 struct Icmppriv
96 {
97         ulong   stats[Nstats];
98
99         /* message counts */
100         ulong   in[Maxtype+1];
101         ulong   out[Maxtype+1];
102 };
103
104 static void icmpkick(void *x, Block*);
105
106 static void
107 icmpcreate(Conv *c)
108 {
109         c->rq = qopen(64*1024, Qmsg, 0, c);
110         c->wq = qbypass(icmpkick, c);
111 }
112
113 char*
114 icmpconnect(Conv *c, char **argv, int argc)
115 {
116         char *e;
117
118         e = Fsstdconnect(c, argv, argc);
119         if(e != nil)
120                 return e;
121         Fsconnected(c, e);
122
123         return nil;
124 }
125
126 int
127 icmpstate(Conv *c, char *state, int n)
128 {
129         USED(c);
130         return snprint(state, n, "%s qin %d qout %d\n",
131                 "Datagram",
132                 c->rq ? qlen(c->rq) : 0,
133                 c->wq ? qlen(c->wq) : 0
134         );
135 }
136
137 char*
138 icmpannounce(Conv *c, char **argv, int argc)
139 {
140         char *e;
141
142         e = Fsstdannounce(c, argv, argc);
143         if(e != nil)
144                 return e;
145         Fsconnected(c, nil);
146
147         return nil;
148 }
149
150 void
151 icmpclose(Conv *c)
152 {
153         qclose(c->rq);
154         qclose(c->wq);
155         ipmove(c->laddr, IPnoaddr);
156         ipmove(c->raddr, IPnoaddr);
157         c->lport = 0;
158 }
159
160 static void
161 icmpkick(void *x, Block *bp)
162 {
163         Conv *c = x;
164         Icmp *p;
165         Icmppriv *ipriv;
166
167         if(bp == nil)
168                 return;
169         if(BLEN(bp) < ICMP_IPSIZE + ICMP_HDRSIZE){
170                 freeblist(bp);
171                 return;
172         }
173         p = (Icmp *)(bp->rp);
174         p->vihl = IP_VER4;
175         ipriv = c->p->priv;
176         if(p->type <= Maxtype)  
177                 ipriv->out[p->type]++;
178         
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);
187 }
188
189 static int
190 ip4reply(Fs *f, uchar ip4[4])
191 {
192         uchar addr[IPaddrlen];
193         int i;
194
195         v4tov6(addr, ip4);
196         if(ipismulticast(addr))
197                 return 0;
198         i = ipforme(f, addr);
199         return i == 0 || i == Runi;
200 }
201
202 static int
203 ip4me(Fs *f, uchar ip4[4])
204 {
205         uchar addr[IPaddrlen];
206
207         v4tov6(addr, ip4);
208         if(ipismulticast(addr))
209                 return 0;
210         return ipforme(f, addr) == Runi;
211 }
212
213 void
214 icmpttlexceeded(Fs *f, Ipifc *ifc, Block *bp)
215 {
216         Block   *nbp;
217         Icmp    *p, *np;
218         uchar   ia[IPv4addrlen];
219
220         p = (Icmp *)bp->rp;
221         if(!ip4reply(f, p->src) || !ipv4local(ifc, ia, p->src))
222                 return;
223
224         netlog(f, Logicmp, "sending icmpttlexceeded %V -> src %V dst %V\n",
225                 ia, p->src, p->dst);
226
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;
230         np->vihl = IP_VER4;
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;
235         np->code = 0;
236         np->proto = IP_ICMPPROTO;
237         hnputs(np->icmpid, 0);
238         hnputs(np->seq, 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);
242 }
243
244 static void
245 icmpunreachable(Fs *f, Block *bp, int code, int seq)
246 {
247         Block   *nbp;
248         Icmp    *p, *np;
249
250         p = (Icmp *)bp->rp;
251         if(!ip4me(f, p->dst) || !ip4reply(f, p->src))
252                 return;
253
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;
258         np->vihl = IP_VER4;
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;
263         np->code = code;
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);
270 }
271
272 void
273 icmpnoconv(Fs *f, Block *bp)
274 {
275         icmpunreachable(f, bp, 3, 0);
276 }
277
278 void
279 icmpcantfrag(Fs *f, Block *bp, int mtu)
280 {
281         icmpunreachable(f, bp, 4, mtu);
282 }
283
284 static void
285 goticmpkt(Proto *icmp, Block *bp)
286 {
287         ushort  recid;
288         uchar   dst[IPaddrlen], src[IPaddrlen];
289         Conv    **c, *s;
290         Icmp    *p;
291
292         p = (Icmp *) bp->rp;
293         v4tov6(dst, p->dst);
294         v4tov6(src, p->src);
295         recid = nhgets(p->icmpid);
296
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)));
301         }
302         freeblist(bp);
303 }
304
305 static Block *
306 mkechoreply(Block *bp, Fs *f)
307 {
308         Icmp    *q;
309         uchar   ip[4];
310
311         q = (Icmp *)bp->rp;
312         if(!ip4me(f, q->dst) || !ip4reply(f, q->src))
313                 return nil;
314
315         q->vihl = IP_VER4;
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));
319         q->type = EchoReply;
320         memset(q->cksum, 0, sizeof(q->cksum));
321         hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
322
323         return bp;
324 }
325
326 static char *unreachcode[] =
327 {
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",
344 };
345
346 static void
347 icmpiput(Proto *icmp, Ipifc*, Block *bp)
348 {
349         int     n;
350         Icmp    *p;
351         Block   *r;
352         Proto   *pr;
353         char    *msg;
354         char    m2[128];
355         Icmppriv *ipriv;
356
357         ipriv = icmp->priv;
358         ipriv->stats[InMsgs]++;
359
360         bp = concatblock(bp);
361         n = BLEN(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);
366                 goto raise;
367         }
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");
372                 goto raise;
373         }
374         p = (Icmp *)bp->rp;
375         netlog(icmp->f, Logicmp, "icmpiput %s (%d) %d\n",
376                 (p->type < nelem(icmpnames)? icmpnames[p->type]: ""),
377                 p->type, p->code);
378         if(p->type <= Maxtype)
379                 ipriv->in[p->type]++;
380
381         switch(p->type) {
382         case EchoRequest:
383                 r = mkechoreply(bp, icmp->f);
384                 if(r == nil)
385                         goto raise;
386                 ipriv->out[EchoReply]++;
387                 ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
388                 break;
389         case Unreachable:
390                 if(p->code >= nelem(unreachcode)) {
391                         snprint(m2, sizeof m2, "unreachable %V -> %V code %d",
392                                 p->src, p->dst, p->code);
393                         msg = m2;
394                 } else
395                         msg = unreachcode[p->code];
396
397                 bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
398                 if(BLEN(bp) < MinAdvise){
399                         ipriv->stats[LenErrs]++;
400                         goto raise;
401                 }
402                 p = (Icmp *)bp->rp;
403                 pr = Fsrcvpcolx(icmp->f, p->proto);
404                 if(pr != nil && pr->advise != nil) {
405                         (*pr->advise)(pr, bp, msg);
406                         return;
407                 }
408                 bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
409                 goticmpkt(icmp, bp);
410                 break;
411         case TimeExceed:
412                 if(p->code == 0){
413                         snprint(m2, sizeof m2, "ttl exceeded at %V", p->src);
414
415                         bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
416                         if(BLEN(bp) < MinAdvise){
417                                 ipriv->stats[LenErrs]++;
418                                 goto raise;
419                         }
420                         p = (Icmp *)bp->rp;
421                         pr = Fsrcvpcolx(icmp->f, p->proto);
422                         if(pr != nil && pr->advise != nil) {
423                                 (*pr->advise)(pr, bp, m2);
424                                 return;
425                         }
426                         bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
427                 }
428                 goticmpkt(icmp, bp);
429                 break;
430         default:
431                 goticmpkt(icmp, bp);
432                 break;
433         }
434         return;
435
436 raise:
437         freeblist(bp);
438 }
439
440 static void
441 icmpadvise(Proto *icmp, Block *bp, char *msg)
442 {
443         ushort  recid;
444         uchar   dst[IPaddrlen], src[IPaddrlen];
445         Conv    **c, *s;
446         Icmp    *p;
447
448         p = (Icmp *) bp->rp;
449         v4tov6(dst, p->dst);
450         v4tov6(src, p->src);
451         recid = nhgets(p->icmpid);
452
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){
457                         if(s->ignoreadvice)
458                                 break;
459                         qhangup(s->rq, msg);
460                         qhangup(s->wq, msg);
461                         break;
462                 }
463         }
464         freeblist(bp);
465 }
466
467 static int
468 icmpstats(Proto *icmp, char *buf, int len)
469 {
470         Icmppriv *priv;
471         char *p, *e;
472         int i;
473
474         priv = icmp->priv;
475         p = buf;
476         e = p+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]);
482                 else
483                         p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]);
484         }
485         return p - buf;
486 }
487         
488 void
489 icmpinit(Fs *fs)
490 {
491         Proto *icmp;
492
493         icmp = smalloc(sizeof(Proto));
494         icmp->priv = smalloc(sizeof(Icmppriv));
495         icmp->name = "icmp";
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;
503         icmp->ctl = nil;
504         icmp->advise = icmpadvise;
505         icmp->gc = nil;
506         icmp->ipproto = IP_ICMPPROTO;
507         icmp->nc = 128;
508         icmp->ptclsize = 0;
509
510         Fsproto(fs, icmp);
511 }