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