Fix 8 space tab formatting for non-C files
[akaros.git] / kern / src / net / icmp6.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 <kmalloc.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <syscall.h>
33 #include <error.h>
34 #include <net/ip.h>
35
36 struct ICMPpkt {
37         uint8_t type;
38         uint8_t code;
39         uint8_t cksum[2];
40         uint8_t icmpid[2];
41         uint8_t seq[2];
42 };
43
44 /* plan 9 uses anon struct members.
45  * We have been naming the struct
46  * members and just using the extra level of deref
47  * e.g. i->x becomes i->i6->x.
48  */
49 struct IPICMP {
50 /*
51         Ip6hdr;
52         ICMPpkt;
53 */
54         uint8_t vcf[4];         // version:4, traffic class:8, flow label:20
55         uint8_t ploadlen[2];    // payload length: packet length - 40
56         uint8_t proto;          // next header type
57         uint8_t ttl;            // hop limit
58         uint8_t src[IPaddrlen];
59         uint8_t dst[IPaddrlen];
60         uint8_t type;
61         uint8_t code;
62         uint8_t cksum[2];
63         uint8_t icmpid[2];
64         uint8_t seq[2];
65
66 };
67
68 struct NdiscC {
69         //IPICMP;
70         uint8_t vcf[4];         // version:4, traffic class:8, flow label:20
71         uint8_t ploadlen[2];    // payload length: packet length - 40
72         uint8_t proto;          // next header type
73         uint8_t ttl;            // hop limit
74         uint8_t src[IPaddrlen];
75         uint8_t dst[IPaddrlen];
76         uint8_t type;
77         uint8_t code;
78         uint8_t cksum[2];
79         uint8_t icmpid[2];
80         uint8_t seq[2];
81
82         uint8_t target[IPaddrlen];
83 };
84
85 struct Ndpkt {
86         //NdiscC;
87         uint8_t vcf[4];         // version:4, traffic class:8, flow label:20
88         uint8_t ploadlen[2];    // payload length: packet length - 40
89         uint8_t proto;          // next header type
90         uint8_t ttl;            // hop limit
91         uint8_t src[IPaddrlen];
92         uint8_t dst[IPaddrlen];
93         uint8_t type;
94         uint8_t code;
95         uint8_t cksum[2];
96         uint8_t icmpid[2];
97         uint8_t seq[2];
98
99         uint8_t target[IPaddrlen];
100
101         uint8_t otype;
102         uint8_t olen;           // length in units of 8 octets(incl type, code),
103         // 1 for IEEE 802 addresses
104         uint8_t lnaddr[6];      // link-layer address
105 };
106
107 enum {
108         // ICMPv6 types
109         EchoReply = 0,
110         UnreachableV6 = 1,
111         PacketTooBigV6 = 2,
112         TimeExceedV6 = 3,
113         SrcQuench = 4,
114         ParamProblemV6 = 4,
115         Redirect = 5,
116         EchoRequest = 8,
117         TimeExceed = 11,
118         InParmProblem = 12,
119         Timestamp = 13,
120         TimestampReply = 14,
121         InfoRequest = 15,
122         InfoReply = 16,
123         AddrMaskRequest = 17,
124         AddrMaskReply = 18,
125         EchoRequestV6 = 128,
126         EchoReplyV6 = 129,
127         RouterSolicit = 133,
128         RouterAdvert = 134,
129         NbrSolicit = 135,
130         NbrAdvert = 136,
131         RedirectV6 = 137,
132
133         Maxtype6 = 137,
134 };
135
136 char *icmpnames6[Maxtype6 + 1] = {
137         [EchoReply] "EchoReply",
138         [UnreachableV6] "UnreachableV6",
139         [PacketTooBigV6] "PacketTooBigV6",
140         [TimeExceedV6] "TimeExceedV6",
141         [SrcQuench] "SrcQuench",
142         [Redirect] "Redirect",
143         [EchoRequest] "EchoRequest",
144         [TimeExceed] "TimeExceed",
145         [InParmProblem] "InParmProblem",
146         [Timestamp] "Timestamp",
147         [TimestampReply] "TimestampReply",
148         [InfoRequest] "InfoRequest",
149         [InfoReply] "InfoReply",
150         [AddrMaskRequest] "AddrMaskRequest",
151         [AddrMaskReply] "AddrMaskReply",
152         [EchoRequestV6] "EchoRequestV6",
153         [EchoReplyV6] "EchoReplyV6",
154         [RouterSolicit] "RouterSolicit",
155         [RouterAdvert] "RouterAdvert",
156         [NbrSolicit] "NbrSolicit",
157         [NbrAdvert] "NbrAdvert",
158         [RedirectV6] "RedirectV6",
159 };
160
161 enum {
162         InMsgs6,
163         InErrors6,
164         OutMsgs6,
165         CsumErrs6,
166         LenErrs6,
167         HlenErrs6,
168         HoplimErrs6,
169         IcmpCodeErrs6,
170         TargetErrs6,
171         OptlenErrs6,
172         AddrmxpErrs6,
173         RouterAddrErrs6,
174
175         Nstats6,
176 };
177
178 static char *statnames6[Nstats6] = {
179         [InMsgs6] "InMsgs",
180         [InErrors6] "InErrors",
181         [OutMsgs6] "OutMsgs",
182         [CsumErrs6] "CsumErrs",
183         [LenErrs6] "LenErrs",
184         [HlenErrs6] "HlenErrs",
185         [HoplimErrs6] "HoplimErrs",
186         [IcmpCodeErrs6] "IcmpCodeErrs",
187         [TargetErrs6] "TargetErrs",
188         [OptlenErrs6] "OptlenErrs",
189         [AddrmxpErrs6] "AddrmxpErrs",
190         [RouterAddrErrs6] "RouterAddrErrs",
191 };
192
193 typedef struct Icmppriv6 {
194         uint32_t stats[Nstats6];
195
196         /* message counts */
197         uint32_t in[Maxtype6 + 1];
198         uint32_t out[Maxtype6 + 1];
199 } Icmppriv6;
200
201 typedef struct Icmpcb6 {
202         uint8_t headers;
203 } Icmpcb6;
204
205 static char *unreachcode[] = {
206         [icmp6_no_route] "no route to destination",
207         [icmp6_ad_prohib] "comm with destination administratively prohibited",
208         [icmp6_unassigned] "icmp unreachable: unassigned error code (2)",
209         [icmp6_adr_unreach] "address unreachable",
210         [icmp6_port_unreach] "port unreachable",
211         [icmp6_unkn_code] "icmp unreachable: unknown code",
212 };
213
214 enum {
215         ICMP_USEAD6 = 40,
216 };
217
218 enum {
219         Oflag = 1 << 5,
220         Sflag = 1 << 6,
221         Rflag = 1 << 7,
222 };
223
224 enum {
225         slladd = 1,
226         tlladd = 2,
227         prfinfo = 3,
228         redhdr = 4,
229         mtuopt = 5,
230 };
231
232 static void icmpkick6(void *x, struct block *bp);
233
234 static void icmpcreate6(struct conv *c)
235 {
236         c->rq = qopen(64 * 1024, Qmsg, 0, c);
237         c->wq = qbypass(icmpkick6, c);
238 }
239
240 static void set_cksum(struct block *bp)
241 {
242         struct IPICMP *p = (struct IPICMP *)(bp->rp);
243
244         hnputl(p->vcf, 0);      // borrow IP header as pseudoheader
245         hnputs(p->ploadlen, blocklen(bp) - IPV6HDR_LEN);
246         p->proto = 0;
247         p->ttl = ICMPv6;        // ttl gets set later
248         hnputs(p->cksum, 0);
249         hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp)));
250         p->proto = ICMPv6;
251 }
252
253 static struct block *newIPICMP(int packetlen)
254 {
255         struct block *nbp;
256
257         nbp = block_alloc(packetlen, MEM_WAIT);
258         nbp->wp += packetlen;
259         memset(nbp->rp, 0, packetlen);
260         return nbp;
261 }
262
263 void icmpadvise6(struct Proto *icmp, struct block *bp, char *msg)
264 {
265         struct conv **c, *s;
266         struct IPICMP *p;
267         uint16_t recid;
268
269         p = (struct IPICMP *)bp->rp;
270         recid = nhgets(p->icmpid);
271
272         for (c = icmp->conv; *c; c++) {
273                 s = *c;
274                 if (s->lport == recid)
275                         if (ipcmp(s->raddr, p->dst) == 0) {
276                                 qhangup(s->rq, msg);
277                                 qhangup(s->wq, msg);
278                                 break;
279                         }
280         }
281         freeblist(bp);
282 }
283
284 static void icmpkick6(void *x, struct block *bp)
285 {
286         struct conv *c = x;
287         struct IPICMP *p;
288         uint8_t laddr[IPaddrlen], raddr[IPaddrlen];
289         Icmppriv6 *ipriv = c->p->priv;
290         Icmpcb6 *icb = (struct Icmpcb6 *)c->ptcl;
291
292         if (bp == NULL)
293                 return;
294
295         if (icb->headers == 6) {
296                 /* get user specified addresses */
297                 bp = pullupblock(bp, ICMP_USEAD6);
298                 if (bp == NULL)
299                         return;
300                 bp->rp += 8;
301                 ipmove(laddr, bp->rp);
302                 bp->rp += IPaddrlen;
303                 ipmove(raddr, bp->rp);
304                 bp->rp += IPaddrlen;
305                 bp = padblock(bp, sizeof(struct ip6hdr));
306
307                 if (blocklen(bp) < sizeof(struct IPICMP)) {
308                         freeblist(bp);
309                         return;
310                 }
311                 p = (struct IPICMP *)(bp->rp);
312
313                 ipmove(p->dst, raddr);
314                 ipmove(p->src, laddr);
315
316         } else {
317                 if (blocklen(bp) < sizeof(struct IPICMP)) {
318                         freeblist(bp);
319                         return;
320                 }
321                 p = (struct IPICMP *)(bp->rp);
322
323                 ipmove(p->dst, c->raddr);
324                 ipmove(p->src, c->laddr);
325                 hnputs(p->icmpid, c->lport);
326         }
327
328         set_cksum(bp);
329         p->vcf[0] = 0x06 << 4;
330         if (p->type <= Maxtype6)
331                 ipriv->out[p->type]++;
332         ipoput6(c->p->f, bp, 0, c->ttl, c->tos, NULL);
333 }
334
335 static void icmpctl6(struct conv *c, char **argv, int argc)
336 {
337         Icmpcb6 *icb = (Icmpcb6*)c->ptcl;
338
339         if ((argc == 1) && strcmp(argv[0], "headers") == 0)
340                 icb->headers = 6;
341         else
342                 error(EINVAL, "unknown command to icmpctl6");
343 }
344
345 static void goticmpkt6(struct Proto *icmp, struct block *bp, int muxkey)
346 {
347         struct conv **c, *s;
348         struct IPICMP *p = (struct IPICMP *)bp->rp;
349         uint16_t recid;
350         uint8_t *addr;
351
352         if (muxkey == 0) {
353                 recid = nhgets(p->icmpid);
354                 addr = p->src;
355         } else {
356                 recid = muxkey;
357                 addr = p->dst;
358         }
359
360         for (c = icmp->conv; *c; c++) {
361                 s = *c;
362                 if (s->lport == recid && ipcmp(s->raddr, addr) == 0) {
363                         bp = concatblock(bp);
364                         if (bp != NULL)
365                                 qpass(s->rq, bp);
366                         return;
367                 }
368         }
369
370         freeblist(bp);
371 }
372
373 static struct block *mkechoreply6(struct block *bp)
374 {
375         struct IPICMP *p = (struct IPICMP *)(bp->rp);
376         uint8_t addr[IPaddrlen];
377
378         ipmove(addr, p->src);
379         ipmove(p->src, p->dst);
380         ipmove(p->dst, addr);
381         p->type = EchoReplyV6;
382         set_cksum(bp);
383         return bp;
384 }
385
386 /*
387  * sends out an ICMPv6 neighbor solicitation
388  *      suni == SRC_UNSPEC or SRC_UNI,
389  *      tuni == TARG_MULTI => multicast for address resolution,
390  *      and tuni == TARG_UNI => neighbor reachability.
391  */
392
393 void icmpns(struct Fs *f, uint8_t *src, int suni, uint8_t *targ, int tuni,
394             uint8_t *mac)
395 {
396         struct block *nbp;
397         struct Ndpkt *np;
398         struct Proto *icmp = f->t2p[ICMPv6];
399         struct Icmppriv6 *ipriv = icmp->priv;
400
401         nbp = newIPICMP(sizeof(struct Ndpkt));
402         np = (struct Ndpkt *)nbp->rp;
403
404         if (suni == SRC_UNSPEC)
405                 memmove(np->src, v6Unspecified, IPaddrlen);
406         else
407                 memmove(np->src, src, IPaddrlen);
408
409         if (tuni == TARG_UNI)
410                 memmove(np->dst, targ, IPaddrlen);
411         else
412                 ipv62smcast(np->dst, targ);
413
414         np->type = NbrSolicit;
415         np->code = 0;
416         memmove(np->target, targ, IPaddrlen);
417         if (suni != SRC_UNSPEC) {
418                 np->otype = SRC_LLADDRESS;
419                 np->olen = 1;   /* 1+1+6 = 8 = 1 8-octet */
420                 memmove(np->lnaddr, mac, sizeof(np->lnaddr));
421         } else {
422                 int r = sizeof(struct Ndpkt) - sizeof(struct NdiscC);
423                 nbp->wp -= r;
424         }
425
426         set_cksum(nbp);
427         np = (struct Ndpkt *)nbp->rp;
428         np->ttl = HOP_LIMIT;
429         np->vcf[0] = 0x06 << 4;
430         ipriv->out[NbrSolicit]++;
431         netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ);
432         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
433 }
434
435 /*
436  * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
437  */
438 void icmpna(struct Fs *f, uint8_t * src, uint8_t * dst, uint8_t * targ,
439             uint8_t * mac, uint8_t flags)
440 {
441         struct block *nbp;
442         struct Ndpkt *np;
443         struct Proto *icmp = f->t2p[ICMPv6];
444         Icmppriv6 *ipriv = icmp->priv;
445
446         nbp = newIPICMP(sizeof(struct Ndpkt));
447         np = (struct Ndpkt *)nbp->rp;
448
449         memmove(np->src, src, IPaddrlen);
450         memmove(np->dst, dst, IPaddrlen);
451
452         np->type = NbrAdvert;
453         np->code = 0;
454         np->icmpid[0] = flags;
455         memmove(np->target, targ, IPaddrlen);
456
457         np->otype = TARGET_LLADDRESS;
458         np->olen = 1;
459         memmove(np->lnaddr, mac, sizeof(np->lnaddr));
460
461         set_cksum(nbp);
462         np = (struct Ndpkt *)nbp->rp;
463         np->ttl = HOP_LIMIT;
464         np->vcf[0] = 0x06 << 4;
465         ipriv->out[NbrAdvert]++;
466         netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
467         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
468 }
469
470 void icmphostunr(struct Fs *f, struct Ipifc *ifc,
471                  struct block *bp, int code, int free)
472 {
473         struct block *nbp;
474         struct IPICMP *np;
475         struct ip6hdr *p;
476         int osz = BLEN(bp);
477         int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU);
478         struct Proto *icmp = f->t2p[ICMPv6];
479         Icmppriv6 *ipriv = icmp->priv;
480
481         p = (struct ip6hdr *)bp->rp;
482
483         if (isv6mcast(p->src))
484                 goto freebl;
485
486         nbp = newIPICMP(sz);
487         np = (struct IPICMP *)nbp->rp;
488
489         rlock(&ifc->rwlock);
490         if (ipv6anylocal(ifc, np->src)) {
491                 netlog(f, Logicmp, "send icmphostunr -> s%I d%I\n",
492                        p->src, p->dst);
493         } else {
494                 netlog(f, Logicmp, "icmphostunr fail -> s%I d%I\n",
495                        p->src, p->dst);
496                 runlock(&ifc->rwlock);
497                 freeblist(nbp);
498                 goto freebl;
499         }
500
501         memmove(np->dst, p->src, IPaddrlen);
502         np->type = UnreachableV6;
503         np->code = code;
504         memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
505                         sz - sizeof(struct IPICMP));
506         set_cksum(nbp);
507         np->ttl = HOP_LIMIT;
508         np->vcf[0] = 0x06 << 4;
509         ipriv->out[UnreachableV6]++;
510
511         if (free)
512                 ipiput6(f, ifc, nbp);
513         else
514                 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
515         runlock(&ifc->rwlock);
516 freebl:
517         if (free)
518                 freeblist(bp);
519 }
520
521 void icmpttlexceeded6(struct Fs *f, struct Ipifc *ifc, struct block *bp)
522 {
523         struct block *nbp;
524         struct IPICMP *np;
525         struct ip6hdr *p;
526         int osz = BLEN(bp);
527         int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU);
528         struct Proto *icmp = f->t2p[ICMPv6];
529         Icmppriv6 *ipriv = icmp->priv;
530
531         p = (struct ip6hdr *)bp->rp;
532
533         if (isv6mcast(p->src))
534                 return;
535
536         nbp = newIPICMP(sz);
537         np = (struct IPICMP *)nbp->rp;
538
539         if (ipv6anylocal(ifc, np->src)) {
540                 netlog(f, Logicmp, "send icmpttlexceeded6 -> s%I d%I\n",
541                        p->src, p->dst);
542         } else {
543                 netlog(f, Logicmp, "icmpttlexceeded6 fail -> s%I d%I\n",
544                        p->src, p->dst);
545                 return;
546         }
547
548         memmove(np->dst, p->src, IPaddrlen);
549         np->type = TimeExceedV6;
550         np->code = 0;
551         memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
552                 sz - sizeof(struct IPICMP));
553         set_cksum(nbp);
554         np->ttl = HOP_LIMIT;
555         np->vcf[0] = 0x06 << 4;
556         ipriv->out[TimeExceedV6]++;
557         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
558 }
559
560 void icmppkttoobig6(struct Fs *f, struct Ipifc *ifc, struct block *bp)
561 {
562         struct block *nbp;
563         struct IPICMP *np;
564         struct ip6hdr *p;
565         int osz = BLEN(bp);
566         int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU);
567         struct Proto *icmp = f->t2p[ICMPv6];
568         Icmppriv6 *ipriv = icmp->priv;
569
570         p = (struct ip6hdr *)bp->rp;
571
572         if (isv6mcast(p->src))
573                 return;
574
575         nbp = newIPICMP(sz);
576         np = (struct IPICMP *)nbp->rp;
577
578         if (!ipv6anylocal(ifc, np->src)) {
579                 netlog(f, Logicmp, "icmppkttoobig6 fail -> s%I d%I\n",
580                        p->src, p->dst);
581                 return;
582         }
583         netlog(f, Logicmp, "send icmppkttoobig6 -> s%I d%I\n",
584                p->src, p->dst);
585
586         memmove(np->dst, p->src, IPaddrlen);
587         np->type = PacketTooBigV6;
588         np->code = 0;
589         hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize);
590         memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
591                         sz - sizeof(struct IPICMP));
592         set_cksum(nbp);
593         np->ttl = HOP_LIMIT;
594         np->vcf[0] = 0x06 << 4;
595         ipriv->out[PacketTooBigV6]++;
596         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
597 }
598
599 /*
600  * RFC 2461, pages 39-40, pages 57-58.
601  */
602 static int valid(struct Proto *icmp, struct Ipifc *ifc,
603                  struct block *bp, Icmppriv6 * ipriv)
604 {
605         int sz, osz, unsp, n, ttl, iplen;
606         int pktsz = BLEN(bp);
607         uint8_t *packet = bp->rp;
608         struct IPICMP *p = (struct IPICMP *)packet;
609         struct Ndpkt *np;
610
611         n = blocklen(bp);
612         if (n < sizeof(struct IPICMP)) {
613                 ipriv->stats[HlenErrs6]++;
614                 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
615                 goto err;
616         }
617
618         iplen = nhgets(p->ploadlen);
619         if (iplen > n - IPV6HDR_LEN || (iplen % 1)) {
620                 ipriv->stats[LenErrs6]++;
621                 netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
622                 goto err;
623         }
624         // Rather than construct explicit pseudoheader, overwrite IPv6 header
625         if (p->proto != ICMPv6) {
626                 // This code assumes no extension headers!!!
627                 netlog(icmp->f, Logicmp, "icmp error: extension header\n");
628                 goto err;
629         }
630         memset(packet, 0, 4);
631         ttl = p->ttl;
632         p->ttl = p->proto;
633         p->proto = 0;
634         if (ptclcsum(bp, 0, iplen + IPV6HDR_LEN)) {
635                 ipriv->stats[CsumErrs6]++;
636                 netlog(icmp->f, Logicmp, "icmp checksum error\n");
637                 goto err;
638         }
639         p->proto = p->ttl;
640         p->ttl = ttl;
641
642         /* additional tests for some pkt types */
643         if ((p->type == NbrSolicit) ||
644                 (p->type == NbrAdvert) ||
645                 (p->type == RouterAdvert) ||
646                 (p->type == RouterSolicit) || (p->type == RedirectV6)) {
647
648                 if (p->ttl != HOP_LIMIT) {
649                         ipriv->stats[HoplimErrs6]++;
650                         goto err;
651                 }
652                 if (p->code != 0) {
653                         ipriv->stats[IcmpCodeErrs6]++;
654                         goto err;
655                 }
656
657                 switch (p->type) {
658                 case NbrSolicit:
659                 case NbrAdvert:
660                         np = (struct Ndpkt *)p;
661                         if (isv6mcast(np->target)) {
662                                 ipriv->stats[TargetErrs6]++;
663                                 goto err;
664                         }
665                         if (optexsts(np) && (np->olen == 0)) {
666                                 ipriv->stats[OptlenErrs6]++;
667                                 goto err;
668                         }
669
670                         if (p->type == NbrSolicit) {
671                                 if (ipcmp(np->src, v6Unspecified) == 0) {
672                                         if (!issmcast(np->dst) || optexsts(np))
673                                         {
674                                                 ipriv->stats[AddrmxpErrs6]++;
675                                                 goto err;
676                                         }
677                                 }
678                         }
679
680                         if (p->type == NbrAdvert) {
681                                 if ((isv6mcast(np->dst)) &&
682                                     (nhgets(np->icmpid) & Sflag)) {
683                                         ipriv->stats[AddrmxpErrs6]++;
684                                         goto err;
685                                 }
686                         }
687                         break;
688
689                 case RouterAdvert:
690                         if (pktsz - sizeof(struct ip6hdr) < 16) {
691                                 ipriv->stats[HlenErrs6]++;
692                                 goto err;
693                         }
694                         if (!islinklocal(p->src)) {
695                                 ipriv->stats[RouterAddrErrs6]++;
696                                 goto err;
697                         }
698                         sz = sizeof(struct IPICMP) + 8;
699                         while ((sz + 1) < pktsz) {
700                                 osz = *(packet + sz + 1);
701                                 if (osz <= 0) {
702                                         ipriv->stats[OptlenErrs6]++;
703                                         goto err;
704                                 }
705                                 sz += 8 * osz;
706                         }
707                         break;
708
709                 case RouterSolicit:
710                         if (pktsz - sizeof(struct ip6hdr) < 8) {
711                                 ipriv->stats[HlenErrs6]++;
712                                 goto err;
713                         }
714                         unsp = (ipcmp(p->src, v6Unspecified) == 0);
715                         sz = sizeof(struct IPICMP) + 8;
716                         while ((sz + 1) < pktsz) {
717                                 osz = *(packet + sz + 1);
718                                 if ((osz <= 0) ||
719                                     (unsp && (*(packet + sz) == slladd))) {
720                                         ipriv->stats[OptlenErrs6]++;
721                                         goto err;
722                                 }
723                                 sz += 8 * osz;
724                         }
725                         break;
726
727                 case RedirectV6:
728                         //to be filled in
729                         break;
730
731                 default:
732                         goto err;
733                 }
734         }
735
736         return 1;
737
738 err:
739         ipriv->stats[InErrors6]++;
740         return 0;
741 }
742
743 static int targettype(struct Fs *f, struct Ipifc *ifc, uint8_t * target)
744 {
745         struct Iplifc *lifc;
746         int t;
747
748         rlock(&ifc->rwlock);
749         if (ipproxyifc(f, ifc, target)) {
750                 runlock(&ifc->rwlock);
751                 return t_uniproxy;
752         }
753
754         for (lifc = ifc->lifc; lifc; lifc = lifc->next) {
755                 if (ipcmp(lifc->local, target) == 0) {
756                         t = (lifc->tentative) ? t_unitent : t_unirany;
757                         runlock(&ifc->rwlock);
758                         return t;
759                 }
760         }
761
762         runlock(&ifc->rwlock);
763         return 0;
764 }
765
766 static void icmpiput6(struct Proto *icmp, struct Ipifc *ipifc, struct block *bp)
767 {
768         uint8_t *packet = bp->rp;
769         struct IPICMP *p = (struct IPICMP *)packet;
770         Icmppriv6 *ipriv = icmp->priv;
771         struct block *r;
772         struct Proto *pr;
773         char *msg, m2[128];
774         struct Ndpkt *np;
775         uint8_t pktflags;
776         uint8_t lsrc[IPaddrlen];
777         int refresh = 1;
778         struct Iplifc *lifc;
779
780         if (!valid(icmp, ipifc, bp, ipriv))
781                 goto raise;
782
783         if (p->type <= Maxtype6)
784                 ipriv->in[p->type]++;
785         else
786                 goto raise;
787
788         switch (p->type) {
789         case EchoRequestV6:
790                 r = mkechoreply6(bp);
791                 ipriv->out[EchoReply]++;
792                 ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, NULL);
793                 break;
794
795         case UnreachableV6:
796                 if (p->code > 4)
797                         msg = unreachcode[icmp6_unkn_code];
798                 else
799                         msg = unreachcode[p->code];
800
801                 bp->rp += sizeof(struct IPICMP);
802                 if (blocklen(bp) < 8) {
803                         ipriv->stats[LenErrs6]++;
804                         goto raise;
805                 }
806                 p = (struct IPICMP *)bp->rp;
807                 pr = Fsrcvpcolx(icmp->f, p->proto);
808                 if (pr != NULL && pr->advise != NULL) {
809                         (*pr->advise) (pr, bp, msg);
810                         return;
811                 }
812
813                 bp->rp -= sizeof(struct IPICMP);
814                 goticmpkt6(icmp, bp, 0);
815                 break;
816
817         case TimeExceedV6:
818                 if (p->code == 0) {
819                         snprintf(m2, sizeof(m2), "ttl exceeded at %I", p->src);
820
821                         bp->rp += sizeof(struct IPICMP);
822                         if (blocklen(bp) < 8) {
823                                 ipriv->stats[LenErrs6]++;
824                                 goto raise;
825                         }
826                         p = (struct IPICMP *)bp->rp;
827                         pr = Fsrcvpcolx(icmp->f, p->proto);
828                         if (pr != NULL && pr->advise != NULL) {
829                                 (*pr->advise) (pr, bp, m2);
830                                 return;
831                         }
832                         bp->rp -= sizeof(struct IPICMP);
833                 }
834
835                 goticmpkt6(icmp, bp, 0);
836                 break;
837
838         case RouterAdvert:
839         case RouterSolicit:
840                 /* using lsrc as a temp, munge hdr for goticmp6
841                    memmove(lsrc, p->src, IPaddrlen);
842                    memmove(p->src, p->dst, IPaddrlen);
843                    memmove(p->dst, lsrc, IPaddrlen); */
844
845                 goticmpkt6(icmp, bp, p->type);
846                 break;
847
848         case NbrSolicit:
849                 np = (struct Ndpkt *)p;
850                 pktflags = 0;
851                 switch (targettype(icmp->f, ipifc, np->target)) {
852                         case t_unirany:
853                                 pktflags |= Oflag;
854                                 /* fall through */
855
856                         case t_uniproxy:
857                                 if (ipcmp(np->src, v6Unspecified) != 0) {
858                                         arpenter(icmp->f, V6, np->src,
859                                                  np->lnaddr, 8 * np->olen - 2,
860                                                  0);
861                                         pktflags |= Sflag;
862                                 }
863                                 if (ipv6local(ipifc, lsrc)) {
864                                         icmpna(icmp->f, lsrc,
865                                                (ipcmp(np->src, v6Unspecified)
866                                                 == 0) ? v6allnodesL : np->src,
867                                                np->target, ipifc->mac,
868                                                pktflags);
869                                 } else
870                                         freeblist(bp);
871                                 break;
872
873                         case t_unitent:
874                                 /* not clear what needs to be done. send up an
875                                  * icmp mesg saying don't use this address? */
876
877                         default:
878                                 freeblist(bp);
879                 }
880
881                 break;
882
883         case NbrAdvert:
884                 np = (struct Ndpkt *)p;
885
886                 /* if the target address matches one of the local interface
887                  * address and the local interface address has tentative bit
888                  * set, then insert into ARP table. this is so the duplication
889                  * address detection part of ipconfig can discover duplication
890                  * through the arp table
891                  */
892                 lifc = iplocalonifc(ipifc, np->target);
893                 if (lifc && lifc->tentative)
894                         refresh = 0;
895                 arpenter(icmp->f, V6, np->target, np->lnaddr, 8 * np->olen - 2,
896                                  refresh);
897                 freeblist(bp);
898                 break;
899
900         case PacketTooBigV6:
901
902         default:
903                 goticmpkt6(icmp, bp, 0);
904                 break;
905         }
906         return;
907
908 raise:
909         freeblist(bp);
910
911 }
912
913 int icmpstats6(struct Proto *icmp6, char *buf, int len)
914 {
915         Icmppriv6 *priv;
916         char *p, *e;
917         int i;
918
919         priv = icmp6->priv;
920         p = buf;
921         e = p + len;
922         for (i = 0; i < Nstats6; i++)
923                 p = seprintf(p, e, "%s: %u\n", statnames6[i], priv->stats[i]);
924         for (i = 0; i <= Maxtype6; i++) {
925                 if (icmpnames6[i])
926                         p = seprintf(p, e, "%s: %u %u\n", icmpnames6[i],
927                                      priv->in[i], priv->out[i]);
928                 else
929                         p = seprintf(p, e, "%d: %u %u\n", i, priv->in[i],
930                                      priv->out[i]);
931         }
932         return p - buf;
933 }
934
935 // need to import from icmp.c
936 extern int icmpstate(struct conv *c, char *state, int n);
937 extern void icmpannounce(struct conv *c, char **argv, int argc);
938 extern void icmpconnect(struct conv *c, char **argv, int argc);
939 extern void icmpclose(struct conv *c);
940
941 void icmp6init(struct Fs *fs)
942 {
943         struct Proto *icmp6 = kzmalloc(sizeof(struct Proto), 0);
944
945         icmp6->priv = kzmalloc(sizeof(Icmppriv6), 0);
946         icmp6->name = "icmpv6";
947         icmp6->connect = icmpconnect;
948         icmp6->announce = icmpannounce;
949         icmp6->state = icmpstate;
950         icmp6->create = icmpcreate6;
951         icmp6->close = icmpclose;
952         icmp6->rcv = icmpiput6;
953         icmp6->stats = icmpstats6;
954         icmp6->ctl = icmpctl6;
955         icmp6->advise = icmpadvise6;
956         icmp6->gc = NULL;
957         icmp6->ipproto = ICMPv6;
958         icmp6->nc = 16;
959         icmp6->ptclsize = sizeof(Icmpcb6);
960
961         Fsproto(fs, icmp6);
962 }