qio: Track the amount of bytes read
[akaros.git] / kern / src / net / icmp.c
1 /* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
2  * Portions Copyright © 1997-1999 Vita Nuova Limited
3  * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
4  *                                (www.vitanuova.com)
5  * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
6  *
7  * Modified for the Akaros operating system:
8  * Copyright (c) 2013-2014 The Regents of the University of California
9  * Copyright (c) 2013-2015 Google Inc.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a copy
12  * of this software and associated documentation files (the "Software"), to deal
13  * in the Software without restriction, including without limitation the rights
14  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15  * copies of the Software, and to permit persons to whom the Software is
16  * furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE. */
28
29 #include <vfs.h>
30 #include <kfs.h>
31 #include <slab.h>
32 #include <kmalloc.h>
33 #include <kref.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <assert.h>
37 #include <error.h>
38 #include <cpio.h>
39 #include <pmap.h>
40 #include <smp.h>
41 #include <ip.h>
42
43 #include <vfs.h>
44 #include <kfs.h>
45 #include <slab.h>
46 #include <kmalloc.h>
47 #include <kref.h>
48 #include <string.h>
49 #include <stdio.h>
50 #include <assert.h>
51 #include <error.h>
52 #include <cpio.h>
53 #include <pmap.h>
54 #include <smp.h>
55 #include <ip.h>
56
57 typedef struct Icmp {
58         uint8_t vihl;                           /* Version and header length */
59         uint8_t tos;                            /* Type of service */
60         uint8_t length[2];                      /* packet length */
61         uint8_t id[2];                          /* Identification */
62         uint8_t frag[2];                        /* Fragment information */
63         uint8_t ttl;                            /* Time to live */
64         uint8_t proto;                          /* Protocol */
65         uint8_t ipcksum[2];                     /* Header checksum */
66         uint8_t src[4];                         /* Ip source */
67         uint8_t dst[4];                         /* Ip destination */
68         uint8_t type;
69         uint8_t code;
70         uint8_t cksum[2];
71         uint8_t icmpid[2];
72         uint8_t seq[2];
73         uint8_t data[1];
74 } Icmp;
75
76 enum {                                                  /* Packet Types */
77         EchoReply = 0,
78         Unreachable = 3,
79         SrcQuench = 4,
80         Redirect = 5,
81         EchoRequest = 8,
82         TimeExceed = 11,
83         InParmProblem = 12,
84         Timestamp = 13,
85         TimestampReply = 14,
86         InfoRequest = 15,
87         InfoReply = 16,
88         AddrMaskRequest = 17,
89         AddrMaskReply = 18,
90
91         Maxtype = 18,
92 };
93
94 enum {
95         MinAdvise = 24,                         /* minimum needed for us to advise another protocol */
96 };
97
98 char *icmpnames[Maxtype + 1] = {
99         [EchoReply] "EchoReply",
100         [Unreachable] "Unreachable",
101         [SrcQuench] "SrcQuench",
102         [Redirect] "Redirect",
103         [EchoRequest] "EchoRequest",
104         [TimeExceed] "TimeExceed",
105         [InParmProblem] "InParmProblem",
106         [Timestamp] "Timestamp",
107         [TimestampReply] "TimestampReply",
108         [InfoRequest] "InfoRequest",
109         [InfoReply] "InfoReply",
110         [AddrMaskRequest] "AddrMaskRequest",
111         [AddrMaskReply] "AddrMaskReply  ",
112 };
113
114 enum {
115         IP_ICMPPROTO = 1,
116         ICMP_IPSIZE = 20,
117         ICMP_HDRSIZE = 8,
118 };
119
120 enum {
121         InMsgs,
122         InErrors,
123         OutMsgs,
124         CsumErrs,
125         LenErrs,
126         HlenErrs,
127
128         Nstats,
129 };
130
131 static char *statnames[Nstats] = {
132         [InMsgs] "InMsgs",
133         [InErrors] "InErrors",
134         [OutMsgs] "OutMsgs",
135         [CsumErrs] "CsumErrs",
136         [LenErrs] "LenErrs",
137         [HlenErrs] "HlenErrs",
138 };
139
140 typedef struct Icmppriv Icmppriv;
141 struct Icmppriv {
142         uint32_t stats[Nstats];
143
144         /* message counts */
145         uint32_t in[Maxtype + 1];
146         uint32_t out[Maxtype + 1];
147 };
148
149 static void icmpkick(void *x, struct block *);
150
151 static void icmpcreate(struct conv *c)
152 {
153         c->rq = qopen(64 * 1024, Qmsg, 0, c);
154         c->wq = qbypass(icmpkick, c);
155 }
156
157 void icmpconnect(struct conv *c, char **argv, int argc)
158 {
159         Fsstdconnect(c, argv, argc);
160         Fsconnected(c, 0);
161 }
162
163 extern int icmpstate(struct conv *c, char *state, int n)
164 {
165         return snprintf(state, n, "%s qin %d qout %d\n",
166                                         "Datagram",
167                                         c->rq ? qlen(c->rq) : 0, c->wq ? qlen(c->wq) : 0);
168 }
169
170 void icmpannounce(struct conv *c, char **argv, int argc)
171 {
172         Fsstdannounce(c, argv, argc);
173         Fsconnected(c, NULL);
174 }
175
176 extern void icmpclose(struct conv *c)
177 {
178         qclose(c->rq);
179         qclose(c->wq);
180         ipmove(c->laddr, IPnoaddr);
181         ipmove(c->raddr, IPnoaddr);
182         c->lport = 0;
183 }
184
185 static void icmpkick(void *x, struct block *bp)
186 {
187         struct conv *c = x;
188         Icmp *p;
189         Icmppriv *ipriv;
190
191         if (bp == NULL)
192                 return;
193
194         bp = pullupblock(bp, ICMP_IPSIZE + ICMP_HDRSIZE);
195         if (bp == 0)
196                 return;
197         p = (Icmp *) (bp->rp);
198         p->vihl = IP_VER4;
199         ipriv = c->p->priv;
200         if (p->type <= Maxtype)
201                 ipriv->out[p->type]++;
202
203         v6tov4(p->dst, c->raddr);
204         v6tov4(p->src, c->laddr);
205         p->proto = IP_ICMPPROTO;
206         hnputs(p->icmpid, c->lport);
207         memset(p->cksum, 0, sizeof(p->cksum));
208         hnputs(p->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
209         ipriv->stats[OutMsgs]++;
210         netlog(c->p->f, Logicmp,
211                "icmp output: Type %s (%d,%d), To %V, TTL %d, ID %d, SEQ %d\n",
212                icmpnames[MIN(p->type, Maxtype)],
213                p->type, p->code, p->dst, p->ttl, nhgets(p->icmpid), nhgets(p->seq));
214         ipoput4(c->p->f, bp, 0, c->ttl, c->tos, NULL);
215 }
216
217 extern void icmpttlexceeded(struct Fs *f, uint8_t * ia, struct block *bp)
218 {
219         struct block *nbp;
220         Icmp *p, *np;
221
222         p = (Icmp *) bp->rp;
223
224         netlog(f, Logicmp, "sending icmpttlexceeded -> %V\n", p->src);
225         nbp = block_alloc(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8, MEM_WAIT);
226         nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
227         np = (Icmp *) nbp->rp;
228         np->vihl = IP_VER4;
229         memmove(np->dst, p->src, sizeof(np->dst));
230         v6tov4(np->src, ia);
231         memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
232         np->type = TimeExceed;
233         np->code = 0;
234         np->proto = IP_ICMPPROTO;
235         hnputs(np->icmpid, 0);
236         hnputs(np->seq, 0);
237         memset(np->cksum, 0, sizeof(np->cksum));
238         hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
239         ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
240
241 }
242
243 static void icmpunreachable(struct Fs *f, struct block *bp, int code, int seq)
244 {
245         struct block *nbp;
246         Icmp *p, *np;
247         int i;
248         uint8_t addr[IPaddrlen];
249
250         p = (Icmp *) bp->rp;
251
252         /* only do this for unicast sources and destinations */
253         v4tov6(addr, p->dst);
254         i = ipforme(f, addr);
255         if ((i & Runi) == 0)
256                 return;
257         v4tov6(addr, p->src);
258         i = ipforme(f, addr);
259         if (i != 0 && (i & Runi) == 0)
260                 return;
261
262         /* TODO: Clean this up or remove it.  This is for things like UDP port
263          * unreachable.  But we might not be UDP, due to how the code is built.
264          * Check the UDP netlog if you see this. */
265         netlog(f, Logicmp, "sending icmpnoconv -> %V\n", p->src);
266         nbp = block_alloc(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8, MEM_WAIT);
267         nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
268         np = (Icmp *) nbp->rp;
269         np->vihl = IP_VER4;
270         memmove(np->dst, p->src, sizeof(np->dst));
271         memmove(np->src, p->dst, sizeof(np->src));
272         memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
273         np->type = Unreachable;
274         np->code = code;
275         np->proto = IP_ICMPPROTO;
276         hnputs(np->icmpid, 0);
277         hnputs(np->seq, seq);
278         memset(np->cksum, 0, sizeof(np->cksum));
279         hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
280         ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
281 }
282
283 extern void icmpnoconv(struct Fs *f, struct block *bp)
284 {
285         icmpunreachable(f, bp, 3, 0);
286 }
287
288 extern void icmpcantfrag(struct Fs *f, struct block *bp, int mtu)
289 {
290         icmpunreachable(f, bp, 4, mtu);
291 }
292
293 static void goticmpkt(struct Proto *icmp, struct block *bp)
294 {
295         struct conv **c, *s;
296         Icmp *p;
297         uint8_t dst[IPaddrlen];
298         uint16_t recid;
299
300         p = (Icmp *) bp->rp;
301         v4tov6(dst, p->src);
302         recid = nhgets(p->icmpid);
303
304         for (c = icmp->conv; *c; c++) {
305                 s = *c;
306                 if (s->lport == recid)
307                         if (ipcmp(s->raddr, dst) == 0) {
308                                 bp = concatblock(bp);
309                                 if (bp != NULL)
310                                         qpass(s->rq, bp);
311                                 return;
312                         }
313         }
314         freeblist(bp);
315 }
316
317 static struct block *mkechoreply(struct Proto *icmp, struct block *bp)
318 {
319         Icmp *q;
320         uint8_t ip[4];
321
322         /* we're repurposing bp to send it back out.  we need to remove any inbound
323          * checksum flags (which were saying the HW did the checksum) */
324         bp->flag &= ~BCKSUM_FLAGS;
325         q = (Icmp *) bp->rp;
326         q->vihl = IP_VER4;
327         memmove(ip, q->src, sizeof(q->dst));
328         memmove(q->src, q->dst, sizeof(q->src));
329         memmove(q->dst, ip, sizeof(q->dst));
330         q->type = EchoReply;
331         memset(q->cksum, 0, sizeof(q->cksum));
332         hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
333         netlog(icmp->f, Logicmp,
334                "icmp echo reply: To %V, TTL %d, ID %d, SEQ %d\n",
335                q->dst, q->ttl, nhgets(q->icmpid), nhgets(q->seq));
336         return bp;
337 }
338
339 static char *unreachcode[] = {
340         [0] "net unreachable",
341         [1] "host unreachable",
342         [2] "protocol unreachable",
343         [3] "port unreachable",
344         [4] "fragmentation needed and DF set",
345         [5] "source route failed",
346 };
347
348 static void icmpiput(struct Proto *icmp, struct Ipifc *unused, struct block *bp)
349 {
350         int n, iplen;
351         Icmp *p;
352         struct block *r;
353         struct Proto *pr;
354         char *msg;
355         char m2[128];
356         Icmppriv *ipriv;
357
358         bp = pullupblock(bp, ICMP_IPSIZE + ICMP_HDRSIZE);
359         if (bp == NULL)
360                 return;
361
362         ipriv = icmp->priv;
363
364         ipriv->stats[InMsgs]++;
365
366         p = (Icmp *) bp->rp;
367         /* The ID and SEQ are only for Echo Request and Reply, but close enough. */
368         netlog(icmp->f, Logicmp,
369                "icmp input: Type %s (%d,%d), From %V, TTL %d, ID %d, SEQ %d\n",
370                icmpnames[MIN(p->type, Maxtype)],
371                p->type, p->code, p->src, p->ttl, nhgets(p->icmpid), nhgets(p->seq));
372         n = blocklen(bp);
373         if (n < ICMP_IPSIZE + ICMP_HDRSIZE) {
374                 /* pullupblock should fail if dlen < size.  b->len >= b->dlen. */
375                 panic("We did a pullupblock and thought we had enough!");
376                 ipriv->stats[InErrors]++;
377                 ipriv->stats[HlenErrs]++;
378                 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
379                 goto raise;
380         }
381         iplen = nhgets(p->length);
382         if (iplen > n || (iplen % 1)) {
383                 ipriv->stats[LenErrs]++;
384                 ipriv->stats[InErrors]++;
385                 netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
386                 goto raise;
387         }
388         if (ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)) {
389                 ipriv->stats[InErrors]++;
390                 ipriv->stats[CsumErrs]++;
391                 netlog(icmp->f, Logicmp, "icmp checksum error\n");
392                 goto raise;
393         }
394         if (p->type <= Maxtype)
395                 ipriv->in[p->type]++;
396
397         switch (p->type) {
398                 case EchoRequest:
399                         if (iplen < n)
400                                 bp = trimblock(bp, 0, iplen);
401                         r = mkechoreply(icmp, bp);
402                         ipriv->out[EchoReply]++;
403                         ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, NULL);
404                         break;
405                 case Unreachable:
406                         if (p->code > 5)
407                                 msg = unreachcode[1];
408                         else
409                                 msg = unreachcode[p->code];
410
411                         bp->rp += ICMP_IPSIZE + ICMP_HDRSIZE;
412                         if (blocklen(bp) < MinAdvise) {
413                                 ipriv->stats[LenErrs]++;
414                                 goto raise;
415                         }
416                         p = (Icmp *) bp->rp;
417                         pr = Fsrcvpcolx(icmp->f, p->proto);
418                         if (pr != NULL && pr->advise != NULL) {
419                                 (*pr->advise) (pr, bp, msg);
420                                 return;
421                         }
422
423                         bp->rp -= ICMP_IPSIZE + ICMP_HDRSIZE;
424                         goticmpkt(icmp, bp);
425                         break;
426                 case TimeExceed:
427                         if (p->code == 0) {
428                                 snprintf(m2, sizeof(m2), "ttl exceeded at %V", p->src);
429
430                                 bp->rp += ICMP_IPSIZE + ICMP_HDRSIZE;
431                                 if (blocklen(bp) < MinAdvise) {
432                                         ipriv->stats[LenErrs]++;
433                                         goto raise;
434                                 }
435                                 p = (Icmp *) bp->rp;
436                                 pr = Fsrcvpcolx(icmp->f, p->proto);
437                                 if (pr != NULL && pr->advise != NULL) {
438                                         (*pr->advise) (pr, bp, m2);
439                                         return;
440                                 }
441                                 bp->rp -= ICMP_IPSIZE + ICMP_HDRSIZE;
442                         }
443
444                         goticmpkt(icmp, bp);
445                         break;
446                 default:
447                         goticmpkt(icmp, bp);
448                         break;
449         }
450         return;
451
452 raise:
453         freeblist(bp);
454 }
455
456 void icmpadvise(struct Proto *icmp, struct block *bp, char *msg)
457 {
458         struct conv **c, *s;
459         Icmp *p;
460         uint8_t dst[IPaddrlen];
461         uint16_t recid;
462
463         p = (Icmp *) bp->rp;
464         v4tov6(dst, p->dst);
465         recid = nhgets(p->icmpid);
466
467         for (c = icmp->conv; *c; c++) {
468                 s = *c;
469                 if (s->lport == recid)
470                         if (ipcmp(s->raddr, dst) == 0) {
471                                 qhangup(s->rq, msg);
472                                 qhangup(s->wq, msg);
473                                 break;
474                         }
475         }
476         freeblist(bp);
477 }
478
479 int icmpstats(struct Proto *icmp, char *buf, int len)
480 {
481         Icmppriv *priv;
482         char *p, *e;
483         int i;
484
485         priv = icmp->priv;
486         p = buf;
487         e = p + len;
488         for (i = 0; i < Nstats; i++)
489                 p = seprintf(p, e, "%s: %u\n", statnames[i], priv->stats[i]);
490         for (i = 0; i <= Maxtype; i++) {
491                 if (icmpnames[i])
492                         p = seprintf(p, e, "%s: %u %u\n", icmpnames[i], priv->in[i],
493                                                  priv->out[i]);
494                 else
495                         p = seprintf(p, e, "%d: %u %u\n", i, priv->in[i], priv->out[i]);
496         }
497         return p - buf;
498 }
499
500 void icmpinit(struct Fs *fs)
501 {
502         struct Proto *icmp;
503
504         icmp = kzmalloc(sizeof(struct Proto), 0);
505         icmp->priv = kzmalloc(sizeof(Icmppriv), 0);
506         icmp->name = "icmp";
507         icmp->connect = icmpconnect;
508         icmp->announce = icmpannounce;
509         icmp->state = icmpstate;
510         icmp->create = icmpcreate;
511         icmp->close = icmpclose;
512         icmp->rcv = icmpiput;
513         icmp->stats = icmpstats;
514         icmp->ctl = NULL;
515         icmp->advise = icmpadvise;
516         icmp->gc = NULL;
517         icmp->ipproto = IP_ICMPPROTO;
518         icmp->nc = 128;
519         icmp->ptclsize = 0;
520
521         Fsproto(fs, icmp);
522 }