Make all block allocations use the same func [2/2]
[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 <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         qlock_t qlock;
203         uint8_t headers;
204 } Icmpcb6;
205
206 static char *unreachcode[] = {
207         [icmp6_no_route] "no route to destination",
208         [icmp6_ad_prohib] "comm with destination administratively prohibited",
209         [icmp6_unassigned] "icmp unreachable: unassigned error code (2)",
210         [icmp6_adr_unreach] "address unreachable",
211         [icmp6_port_unreach] "port unreachable",
212         [icmp6_unkn_code] "icmp unreachable: unknown code",
213 };
214
215 enum {
216         ICMP_USEAD6 = 40,
217 };
218
219 enum {
220         Oflag = 1 << 5,
221         Sflag = 1 << 6,
222         Rflag = 1 << 7,
223 };
224
225 enum {
226         slladd = 1,
227         tlladd = 2,
228         prfinfo = 3,
229         redhdr = 4,
230         mtuopt = 5,
231 };
232
233 static void icmpkick6(void *x, struct block *bp);
234
235 static void icmpcreate6(struct conv *c)
236 {
237         c->rq = qopen(64 * 1024, Qmsg, 0, c);
238         c->wq = qbypass(icmpkick6, c);
239 }
240
241 static void set_cksum(struct block *bp)
242 {
243         struct IPICMP *p = (struct IPICMP *)(bp->rp);
244
245         hnputl(p->vcf, 0);      // borrow IP header as pseudoheader
246         hnputs(p->ploadlen, blocklen(bp) - IPV6HDR_LEN);
247         p->proto = 0;
248         p->ttl = ICMPv6;        // ttl gets set later
249         hnputs(p->cksum, 0);
250         hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp)));
251         p->proto = ICMPv6;
252 }
253
254 static struct block *newIPICMP(int packetlen)
255 {
256         struct block *nbp;
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,
394             uint8_t *src, int suni,
395             uint8_t *targ, int tuni,
396             uint8_t *mac)
397 {
398         struct block *nbp;
399         struct Ndpkt *np;
400         struct Proto *icmp = f->t2p[ICMPv6];
401         struct Icmppriv6 *ipriv = icmp->priv;
402
403         nbp = newIPICMP(sizeof(struct Ndpkt));
404         np = (struct Ndpkt *)nbp->rp;
405
406         if (suni == SRC_UNSPEC)
407                 memmove(np->src, v6Unspecified, IPaddrlen);
408         else
409                 memmove(np->src, src, IPaddrlen);
410
411         if (tuni == TARG_UNI)
412                 memmove(np->dst, targ, IPaddrlen);
413         else
414                 ipv62smcast(np->dst, targ);
415
416         np->type = NbrSolicit;
417         np->code = 0;
418         memmove(np->target, targ, IPaddrlen);
419         if (suni != SRC_UNSPEC) {
420                 np->otype = SRC_LLADDRESS;
421                 np->olen = 1;   /* 1+1+6 = 8 = 1 8-octet */
422                 memmove(np->lnaddr, mac, sizeof(np->lnaddr));
423         } else {
424                 int r = sizeof(struct Ndpkt) - sizeof(struct NdiscC);
425                 nbp->wp -= r;
426         }
427
428         set_cksum(nbp);
429         np = (struct Ndpkt *)nbp->rp;
430         np->ttl = HOP_LIMIT;
431         np->vcf[0] = 0x06 << 4;
432         ipriv->out[NbrSolicit]++;
433         netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ);
434         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
435 }
436
437 /*
438  * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
439  */
440 void icmpna(struct Fs *f, uint8_t * src, uint8_t * dst, uint8_t * targ,
441             uint8_t * mac, uint8_t flags)
442 {
443         struct block *nbp;
444         struct Ndpkt *np;
445         struct Proto *icmp = f->t2p[ICMPv6];
446         Icmppriv6 *ipriv = icmp->priv;
447
448         nbp = newIPICMP(sizeof(struct Ndpkt));
449         np = (struct Ndpkt *)nbp->rp;
450
451         memmove(np->src, src, IPaddrlen);
452         memmove(np->dst, dst, IPaddrlen);
453
454         np->type = NbrAdvert;
455         np->code = 0;
456         np->icmpid[0] = flags;
457         memmove(np->target, targ, IPaddrlen);
458
459         np->otype = TARGET_LLADDRESS;
460         np->olen = 1;
461         memmove(np->lnaddr, mac, sizeof(np->lnaddr));
462
463         set_cksum(nbp);
464         np = (struct Ndpkt *)nbp->rp;
465         np->ttl = HOP_LIMIT;
466         np->vcf[0] = 0x06 << 4;
467         ipriv->out[NbrAdvert]++;
468         netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
469         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
470 }
471
472 void icmphostunr(struct Fs *f, struct Ipifc *ifc,
473                  struct block *bp, int code, int free)
474 {
475         struct block *nbp;
476         struct IPICMP *np;
477         struct ip6hdr *p;
478         int osz = BLEN(bp);
479         int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU);
480         struct Proto *icmp = f->t2p[ICMPv6];
481         Icmppriv6 *ipriv = icmp->priv;
482
483         p = (struct ip6hdr *)bp->rp;
484
485         if (isv6mcast(p->src))
486                 goto freebl;
487
488         nbp = newIPICMP(sz);
489         np = (struct IPICMP *)nbp->rp;
490
491         rlock(&ifc->rwlock);
492         if (ipv6anylocal(ifc, np->src)) {
493                 netlog(f, Logicmp, "send icmphostunr -> s%I d%I\n", p->src, p->dst);
494         } else {
495                 netlog(f, Logicmp, "icmphostunr fail -> s%I d%I\n", 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", p->src,
541                        p->dst);
542         } else {
543                 netlog(f, Logicmp, "icmpttlexceeded6 fail -> s%I d%I\n", p->src,
544                        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", p->src, p->dst);
580                 return;
581         }
582         netlog(f, Logicmp, "send icmppkttoobig6 -> s%I d%I\n", p->src, p->dst);
583
584         memmove(np->dst, p->src, IPaddrlen);
585         np->type = PacketTooBigV6;
586         np->code = 0;
587         hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize);
588         memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
589                         sz - sizeof(struct IPICMP));
590         set_cksum(nbp);
591         np->ttl = HOP_LIMIT;
592         np->vcf[0] = 0x06 << 4;
593         ipriv->out[PacketTooBigV6]++;
594         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
595 }
596
597 /*
598  * RFC 2461, pages 39-40, pages 57-58.
599  */
600 static int valid(struct Proto *icmp, struct Ipifc *ifc,
601                  struct block *bp, Icmppriv6 * ipriv)
602 {
603         int sz, osz, unsp, n, ttl, iplen;
604         int pktsz = BLEN(bp);
605         uint8_t *packet = bp->rp;
606         struct IPICMP *p = (struct IPICMP *)packet;
607         struct Ndpkt *np;
608
609         n = blocklen(bp);
610         if (n < sizeof(struct IPICMP)) {
611                 ipriv->stats[HlenErrs6]++;
612                 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
613                 goto err;
614         }
615
616         iplen = nhgets(p->ploadlen);
617         if (iplen > n - IPV6HDR_LEN || (iplen % 1)) {
618                 ipriv->stats[LenErrs6]++;
619                 netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
620                 goto err;
621         }
622         // Rather than construct explicit pseudoheader, overwrite IPv6 header
623         if (p->proto != ICMPv6) {
624                 // This code assumes no extension headers!!!
625                 netlog(icmp->f, Logicmp, "icmp error: extension header\n");
626                 goto err;
627         }
628         memset(packet, 0, 4);
629         ttl = p->ttl;
630         p->ttl = p->proto;
631         p->proto = 0;
632         if (ptclcsum(bp, 0, iplen + IPV6HDR_LEN)) {
633                 ipriv->stats[CsumErrs6]++;
634                 netlog(icmp->f, Logicmp, "icmp checksum error\n");
635                 goto err;
636         }
637         p->proto = p->ttl;
638         p->ttl = ttl;
639
640         /* additional tests for some pkt types */
641         if ((p->type == NbrSolicit) ||
642                 (p->type == NbrAdvert) ||
643                 (p->type == RouterAdvert) ||
644                 (p->type == RouterSolicit) || (p->type == RedirectV6)) {
645
646                 if (p->ttl != HOP_LIMIT) {
647                         ipriv->stats[HoplimErrs6]++;
648                         goto err;
649                 }
650                 if (p->code != 0) {
651                         ipriv->stats[IcmpCodeErrs6]++;
652                         goto err;
653                 }
654
655                 switch (p->type) {
656                         case NbrSolicit:
657                         case NbrAdvert:
658                                 np = (struct Ndpkt *)p;
659                                 if (isv6mcast(np->target)) {
660                                         ipriv->stats[TargetErrs6]++;
661                                         goto err;
662                                 }
663                                 if (optexsts(np) && (np->olen == 0)) {
664                                         ipriv->stats[OptlenErrs6]++;
665                                         goto err;
666                                 }
667
668                                 if (p->type == NbrSolicit) {
669                                         if (ipcmp(np->src, v6Unspecified) == 0) {
670                                                 if (!issmcast(np->dst) || optexsts(np)) {
671                                                         ipriv->stats[AddrmxpErrs6]++;
672                                                         goto err;
673                                                 }
674                                         }
675                                 }
676
677                                 if (p->type == NbrAdvert) {
678                                         if ((isv6mcast(np->dst)) && (nhgets(np->icmpid) & Sflag)) {
679                                                 ipriv->stats[AddrmxpErrs6]++;
680                                                 goto err;
681                                         }
682                                 }
683                                 break;
684
685                         case RouterAdvert:
686                                 if (pktsz - sizeof(struct ip6hdr) < 16) {
687                                         ipriv->stats[HlenErrs6]++;
688                                         goto err;
689                                 }
690                                 if (!islinklocal(p->src)) {
691                                         ipriv->stats[RouterAddrErrs6]++;
692                                         goto err;
693                                 }
694                                 sz = sizeof(struct IPICMP) + 8;
695                                 while ((sz + 1) < pktsz) {
696                                         osz = *(packet + sz + 1);
697                                         if (osz <= 0) {
698                                                 ipriv->stats[OptlenErrs6]++;
699                                                 goto err;
700                                         }
701                                         sz += 8 * osz;
702                                 }
703                                 break;
704
705                         case RouterSolicit:
706                                 if (pktsz - sizeof(struct ip6hdr) < 8) {
707                                         ipriv->stats[HlenErrs6]++;
708                                         goto err;
709                                 }
710                                 unsp = (ipcmp(p->src, v6Unspecified) == 0);
711                                 sz = sizeof(struct IPICMP) + 8;
712                                 while ((sz + 1) < pktsz) {
713                                         osz = *(packet + sz + 1);
714                                         if ((osz <= 0) || (unsp && (*(packet + sz) == slladd))) {
715                                                 ipriv->stats[OptlenErrs6]++;
716                                                 goto err;
717                                         }
718                                         sz += 8 * osz;
719                                 }
720                                 break;
721
722                         case RedirectV6:
723                                 //to be filled in
724                                 break;
725
726                         default:
727                                 goto err;
728                 }
729         }
730
731         return 1;
732
733 err:
734         ipriv->stats[InErrors6]++;
735         return 0;
736 }
737
738 static int targettype(struct Fs *f, struct Ipifc *ifc, uint8_t * target)
739 {
740         struct Iplifc *lifc;
741         int t;
742
743         rlock(&ifc->rwlock);
744         if (ipproxyifc(f, ifc, target)) {
745                 runlock(&ifc->rwlock);
746                 return t_uniproxy;
747         }
748
749         for (lifc = ifc->lifc; lifc; lifc = lifc->next) {
750                 if (ipcmp(lifc->local, target) == 0) {
751                         t = (lifc->tentative) ? t_unitent : t_unirany;
752                         runlock(&ifc->rwlock);
753                         return t;
754                 }
755         }
756
757         runlock(&ifc->rwlock);
758         return 0;
759 }
760
761 static void icmpiput6(struct Proto *icmp, struct Ipifc *ipifc, struct block *bp)
762 {
763         uint8_t *packet = bp->rp;
764         struct IPICMP *p = (struct IPICMP *)packet;
765         Icmppriv6 *ipriv = icmp->priv;
766         struct block *r;
767         struct Proto *pr;
768         char *msg, m2[128];
769         struct Ndpkt *np;
770         uint8_t pktflags;
771         uint8_t lsrc[IPaddrlen];
772         int refresh = 1;
773         struct Iplifc *lifc;
774
775         if (!valid(icmp, ipifc, bp, ipriv))
776                 goto raise;
777
778         if (p->type <= Maxtype6)
779                 ipriv->in[p->type]++;
780         else
781                 goto raise;
782
783         switch (p->type) {
784                 case EchoRequestV6:
785                         r = mkechoreply6(bp);
786                         ipriv->out[EchoReply]++;
787                         ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, NULL);
788                         break;
789
790                 case UnreachableV6:
791                         if (p->code > 4)
792                                 msg = unreachcode[icmp6_unkn_code];
793                         else
794                                 msg = unreachcode[p->code];
795
796                         bp->rp += sizeof(struct IPICMP);
797                         if (blocklen(bp) < 8) {
798                                 ipriv->stats[LenErrs6]++;
799                                 goto raise;
800                         }
801                         p = (struct IPICMP *)bp->rp;
802                         pr = Fsrcvpcolx(icmp->f, p->proto);
803                         if (pr != NULL && pr->advise != NULL) {
804                                 (*pr->advise) (pr, bp, msg);
805                                 return;
806                         }
807
808                         bp->rp -= sizeof(struct IPICMP);
809                         goticmpkt6(icmp, bp, 0);
810                         break;
811
812                 case TimeExceedV6:
813                         if (p->code == 0) {
814                                 snprintf(m2, sizeof(m2), "ttl exceeded at %I", p->src);
815
816                                 bp->rp += sizeof(struct IPICMP);
817                                 if (blocklen(bp) < 8) {
818                                         ipriv->stats[LenErrs6]++;
819                                         goto raise;
820                                 }
821                                 p = (struct IPICMP *)bp->rp;
822                                 pr = Fsrcvpcolx(icmp->f, p->proto);
823                                 if (pr != NULL && pr->advise != NULL) {
824                                         (*pr->advise) (pr, bp, m2);
825                                         return;
826                                 }
827                                 bp->rp -= sizeof(struct IPICMP);
828                         }
829
830                         goticmpkt6(icmp, bp, 0);
831                         break;
832
833                 case RouterAdvert:
834                 case RouterSolicit:
835                         /* using lsrc as a temp, munge hdr for goticmp6
836                            memmove(lsrc, p->src, IPaddrlen);
837                            memmove(p->src, p->dst, IPaddrlen);
838                            memmove(p->dst, lsrc, IPaddrlen); */
839
840                         goticmpkt6(icmp, bp, p->type);
841                         break;
842
843                 case NbrSolicit:
844                         np = (struct Ndpkt *)p;
845                         pktflags = 0;
846                         switch (targettype(icmp->f, ipifc, np->target)) {
847                                 case t_unirany:
848                                         pktflags |= Oflag;
849                                         /* fall through */
850
851                                 case t_uniproxy:
852                                         if (ipcmp(np->src, v6Unspecified) != 0) {
853                                                 arpenter(icmp->f, V6, np->src, np->lnaddr,
854                                                                  8 * np->olen - 2, 0);
855                                                 pktflags |= Sflag;
856                                         }
857                                         if (ipv6local(ipifc, lsrc)) {
858                                                 icmpna(icmp->f, lsrc,
859                                                            (ipcmp(np->src, v6Unspecified) ==
860                                                                 0) ? v6allnodesL : np->src, np->target,
861                                                            ipifc->mac, pktflags);
862                                         } else
863                                                 freeblist(bp);
864                                         break;
865
866                                 case t_unitent:
867                                         /* not clear what needs to be done. send up
868                                          * an icmp mesg saying don't use this address? */
869
870                                 default:
871                                         freeblist(bp);
872                         }
873
874                         break;
875
876                 case NbrAdvert:
877                         np = (struct Ndpkt *)p;
878
879                         /* if the target address matches one of the local interface
880                          * address and the local interface address has tentative bit set,
881                          * then insert into ARP table. this is so the duplication address
882                          * detection part of ipconfig can discover duplication through
883                          * the arp table
884                          */
885                         lifc = iplocalonifc(ipifc, np->target);
886                         if (lifc && lifc->tentative)
887                                 refresh = 0;
888                         arpenter(icmp->f, V6, np->target, np->lnaddr, 8 * np->olen - 2,
889                                          refresh);
890                         freeblist(bp);
891                         break;
892
893                 case PacketTooBigV6:
894
895                 default:
896                         goticmpkt6(icmp, bp, 0);
897                         break;
898         }
899         return;
900
901 raise:
902         freeblist(bp);
903
904 }
905
906 int icmpstats6(struct Proto *icmp6, char *buf, int len)
907 {
908         Icmppriv6 *priv;
909         char *p, *e;
910         int i;
911
912         priv = icmp6->priv;
913         p = buf;
914         e = p + len;
915         for (i = 0; i < Nstats6; i++)
916                 p = seprintf(p, e, "%s: %u\n", statnames6[i], priv->stats[i]);
917         for (i = 0; i <= Maxtype6; i++) {
918                 if (icmpnames6[i])
919                         p = seprintf(p, e, "%s: %u %u\n", icmpnames6[i], priv->in[i],
920                                                  priv->out[i]);
921                 else
922                         p = seprintf(p, e, "%d: %u %u\n", i, priv->in[i], priv->out[i]);
923         }
924         return p - buf;
925 }
926
927 // need to import from icmp.c
928 extern int icmpstate(struct conv *c, char *state, int n);
929 extern void icmpannounce(struct conv *c, char **argv, int argc);
930 extern void icmpconnect(struct conv *c, char **argv, int argc);
931 extern void icmpclose(struct conv *c);
932
933 void icmp6newconv(struct Proto *icmp6, struct conv *conv)
934 {
935         /* Fsprotoclone alloc'd our priv struct and attached it to conv already.
936          * Now we need to init it */
937         struct Icmpcb6 *icb = (struct Icmpcb6 *)conv->ptcl;
938         qlock_init(&icb->qlock);
939 }
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->newconv = icmp6newconv;
957         icmp6->gc = NULL;
958         icmp6->ipproto = ICMPv6;
959         icmp6->nc = 16;
960         icmp6->ptclsize = sizeof(Icmpcb6);
961
962         Fsproto(fs, icmp6);
963 }