2 * Internet Control Message Protocol for IPv6
5 #include "../port/lib.h"
9 #include "../port/error.h"
70 /* on-the-wire packet formats */
71 typedef struct IPICMP IPICMP;
72 typedef struct Ndpkt Ndpkt;
73 typedef struct NdiscC NdiscC;
75 /* we do this to avoid possible struct padding */
89 #define IPICMPSZ offsetof(IPICMP, payload[0])
93 uchar target[IPaddrlen];
97 #define NDISCSZ offsetof(NdiscC, payload[0])
101 uchar target[IPaddrlen];
103 uchar olen; /* length in units of 8 octets(incl type, code),
104 * 1 for IEEE 802 addresses */
105 uchar lnaddr[6]; /* link-layer address */
109 #define NDPKTSZ offsetof(Ndpkt, payload[0])
111 typedef struct Icmppriv6
113 ulong stats[Nstats6];
116 ulong in[Maxtype6+1];
117 ulong out[Maxtype6+1];
120 typedef struct Icmpcb6
126 char *icmpnames6[Maxtype6+1] =
128 [EchoReply] "EchoReply",
129 [UnreachableV6] "UnreachableV6",
130 [PacketTooBigV6] "PacketTooBigV6",
131 [TimeExceedV6] "TimeExceedV6",
132 [SrcQuench] "SrcQuench",
133 [Redirect] "Redirect",
134 [EchoRequest] "EchoRequest",
135 [TimeExceed] "TimeExceed",
136 [InParmProblem] "InParmProblem",
137 [Timestamp] "Timestamp",
138 [TimestampReply] "TimestampReply",
139 [InfoRequest] "InfoRequest",
140 [InfoReply] "InfoReply",
141 [AddrMaskRequest] "AddrMaskRequest",
142 [AddrMaskReply] "AddrMaskReply",
143 [EchoRequestV6] "EchoRequestV6",
144 [EchoReplyV6] "EchoReplyV6",
145 [RouterSolicit] "RouterSolicit",
146 [RouterAdvert] "RouterAdvert",
147 [NbrSolicit] "NbrSolicit",
148 [NbrAdvert] "NbrAdvert",
149 [RedirectV6] "RedirectV6",
152 static char *statnames6[Nstats6] =
155 [InErrors6] "InErrors",
156 [OutMsgs6] "OutMsgs",
157 [CsumErrs6] "CsumErrs",
158 [LenErrs6] "LenErrs",
159 [HlenErrs6] "HlenErrs",
160 [HoplimErrs6] "HoplimErrs",
161 [IcmpCodeErrs6] "IcmpCodeErrs",
162 [TargetErrs6] "TargetErrs",
163 [OptlenErrs6] "OptlenErrs",
164 [AddrmxpErrs6] "AddrmxpErrs",
165 [RouterAddrErrs6] "RouterAddrErrs",
168 static char *unreachcode[] =
170 [Icmp6_no_route] "no route to destination",
171 [Icmp6_ad_prohib] "comm with destination administratively prohibited",
172 [Icmp6_out_src_scope] "beyond scope of source address",
173 [Icmp6_adr_unreach] "address unreachable",
174 [Icmp6_port_unreach] "port unreachable",
175 [Icmp6_gress_src_fail] "source address failed ingress/egress policy",
176 [Icmp6_rej_route] "reject route to destination",
177 [Icmp6_unknown] "icmp unreachable: unknown code",
180 static void icmpkick6(void *x, Block *bp);
185 c->rq = qopen(64*1024, Qmsg, 0, c);
186 c->wq = qbypass(icmpkick6, c);
192 IPICMP *p = (IPICMP *)(bp->rp);
194 hnputl(p->vcf, 0); /* borrow IP header as pseudoheader */
195 hnputs(p->ploadlen, blocklen(bp) - IP6HDR);
197 p->ttl = ICMPv6; /* ttl gets set later */
199 hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp)));
204 newIPICMP(int packetlen)
208 nbp = allocb(packetlen);
209 nbp->wp += packetlen;
210 memset(nbp->rp, 0, packetlen);
215 icmpadvise6(Proto *icmp, Block *bp, char *msg)
221 p = (IPICMP *)bp->rp;
222 recid = nhgets(p->icmpid);
224 for(c = icmp->conv; *c; c++) {
226 if(s->lport == recid && ipcmp(s->raddr, p->dst) == 0){
236 icmpkick6(void *x, Block *bp)
238 uchar laddr[IPaddrlen], raddr[IPaddrlen];
241 Icmppriv6 *ipriv = c->p->priv;
242 Icmpcb6 *icb = (Icmpcb6*)c->ptcl;
247 if(icb->headers==6) {
248 /* get user specified addresses */
249 bp = pullupblock(bp, ICMP_USEAD6);
253 ipmove(laddr, bp->rp);
255 ipmove(raddr, bp->rp);
257 bp = padblock(bp, IP6HDR);
260 if(blocklen(bp) < IPICMPSZ){
264 p = (IPICMP *)(bp->rp);
265 if(icb->headers == 6) {
266 ipmove(p->dst, raddr);
267 ipmove(p->src, laddr);
269 ipmove(p->dst, c->raddr);
270 ipmove(p->src, c->laddr);
271 hnputs(p->icmpid, c->lport);
275 p->vcf[0] = 0x06 << 4;
276 if(p->type <= Maxtype6)
277 ipriv->out[p->type]++;
278 ipoput6(c->p->f, bp, 0, c->ttl, c->tos, nil);
282 icmpctl6(Conv *c, char **argv, int argc)
286 icb = (Icmpcb6*) c->ptcl;
287 if(argc==1 && strcmp(argv[0], "headers")==0) {
291 return "unknown control request";
295 goticmpkt6(Proto *icmp, Block *bp, int muxkey)
300 IPICMP *p = (IPICMP *)bp->rp;
303 recid = nhgets(p->icmpid);
310 for(c = icmp->conv; *c; c++){
312 if(s->lport == recid && ipcmp(s->raddr, addr) == 0){
313 bp = concatblock(bp);
324 mkechoreply6(Block *bp, Ipifc *ifc)
326 uchar addr[IPaddrlen];
327 IPICMP *p = (IPICMP *)(bp->rp);
329 ipmove(addr, p->src);
330 if(!isv6mcast(p->dst))
331 ipmove(p->src, p->dst);
332 else if (!ipv6anylocal(ifc, p->src))
334 ipmove(p->dst, addr);
335 p->type = EchoReplyV6;
341 * sends out an ICMPv6 neighbor solicitation
342 * suni == SRC_UNSPEC or SRC_UNI,
343 * tuni == TARG_MULTI => multicast for address resolution,
344 * and tuni == TARG_UNI => neighbor reachability.
347 icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac)
351 Proto *icmp = f->t2p[ICMPv6];
352 Icmppriv6 *ipriv = icmp->priv;
354 nbp = newIPICMP(NDPKTSZ);
355 np = (Ndpkt*) nbp->rp;
357 if(suni == SRC_UNSPEC)
358 memmove(np->src, v6Unspecified, IPaddrlen);
360 memmove(np->src, src, IPaddrlen);
363 memmove(np->dst, targ, IPaddrlen);
365 ipv62smcast(np->dst, targ);
367 np->type = NbrSolicit;
369 memmove(np->target, targ, IPaddrlen);
370 if(suni != SRC_UNSPEC) {
371 np->otype = SRC_LLADDR;
372 np->olen = 1; /* 1+1+6 = 8 = 1 8-octet */
373 memmove(np->lnaddr, mac, sizeof(np->lnaddr));
375 nbp->wp -= NDPKTSZ - NDISCSZ;
378 np = (Ndpkt*)nbp->rp;
380 np->vcf[0] = 0x06 << 4;
381 ipriv->out[NbrSolicit]++;
382 netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ);
383 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
387 * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
390 icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags)
394 Proto *icmp = f->t2p[ICMPv6];
395 Icmppriv6 *ipriv = icmp->priv;
397 nbp = newIPICMP(NDPKTSZ);
398 np = (Ndpkt*)nbp->rp;
400 memmove(np->src, src, IPaddrlen);
401 memmove(np->dst, dst, IPaddrlen);
403 np->type = NbrAdvert;
405 np->icmpid[0] = flags;
406 memmove(np->target, targ, IPaddrlen);
408 np->otype = TARGET_LLADDR;
410 memmove(np->lnaddr, mac, sizeof(np->lnaddr));
413 np = (Ndpkt*) nbp->rp;
415 np->vcf[0] = 0x06 << 4;
416 ipriv->out[NbrAdvert]++;
417 netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
418 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
422 icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free)
425 int sz = MIN(IPICMPSZ + osz, v6MINTU);
429 Proto *icmp = f->t2p[ICMPv6];
430 Icmppriv6 *ipriv = icmp->priv;
432 p = (Ip6hdr *)bp->rp;
434 if(isv6mcast(p->src))
438 np = (IPICMP *)nbp->rp;
441 if(ipv6anylocal(ifc, np->src))
442 netlog(f, Logicmp, "send icmphostunr -> src %I dst %I\n",
445 netlog(f, Logicmp, "icmphostunr fail -> src %I dst %I\n",
454 memmove(np->dst, p->src, IPaddrlen);
455 np->type = UnreachableV6;
457 memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
460 np->vcf[0] = 0x06 << 4;
461 ipriv->out[UnreachableV6]++;
464 ipiput6(f, ifc, nbp);
466 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
476 icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp)
479 int sz = MIN(IPICMPSZ + osz, v6MINTU);
483 Proto *icmp = f->t2p[ICMPv6];
484 Icmppriv6 *ipriv = icmp->priv;
486 p = (Ip6hdr *)bp->rp;
488 if(isv6mcast(p->src))
492 np = (IPICMP *) nbp->rp;
494 if(ipv6anylocal(ifc, np->src))
495 netlog(f, Logicmp, "send icmpttlexceeded6 -> src %I dst %I\n",
498 netlog(f, Logicmp, "icmpttlexceeded6 fail -> src %I dst %I\n",
503 memmove(np->dst, p->src, IPaddrlen);
504 np->type = TimeExceedV6;
506 memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
509 np->vcf[0] = 0x06 << 4;
510 ipriv->out[TimeExceedV6]++;
511 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
515 icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp)
518 int sz = MIN(IPICMPSZ + osz, v6MINTU);
522 Proto *icmp = f->t2p[ICMPv6];
523 Icmppriv6 *ipriv = icmp->priv;
525 p = (Ip6hdr *)bp->rp;
527 if(isv6mcast(p->src))
531 np = (IPICMP *)nbp->rp;
533 if(ipv6anylocal(ifc, np->src))
534 netlog(f, Logicmp, "send icmppkttoobig6 -> src %I dst %I\n",
537 netlog(f, Logicmp, "icmppkttoobig6 fail -> src %I dst %I\n",
542 memmove(np->dst, p->src, IPaddrlen);
543 np->type = PacketTooBigV6;
545 hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize);
546 memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
549 np->vcf[0] = 0x06 << 4;
550 ipriv->out[PacketTooBigV6]++;
551 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
555 * RFC 2461, pages 39-40, pages 57-58.
558 valid(Proto *icmp, Ipifc *ifc, Block *bp, Icmppriv6 *ipriv)
560 int sz, osz, unsp, n, ttl, iplen;
561 int pktsz = BLEN(bp);
562 uchar *packet = bp->rp;
563 IPICMP *p = (IPICMP *) packet;
569 ipriv->stats[HlenErrs6]++;
570 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
574 iplen = nhgets(p->ploadlen);
575 if(iplen > n - IP6HDR) {
576 ipriv->stats[LenErrs6]++;
577 netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
581 /* Rather than construct explicit pseudoheader, overwrite IPv6 header */
582 if(p->proto != ICMPv6) {
583 /* This code assumes no extension headers!!! */
584 netlog(icmp->f, Logicmp, "icmp error: extension header\n");
587 memset(packet, 0, 4);
591 if(ptclcsum(bp, 0, iplen + IP6HDR)) {
592 ipriv->stats[CsumErrs6]++;
593 netlog(icmp->f, Logicmp, "icmp checksum error\n");
599 /* additional tests for some pkt types */
600 if (p->type == NbrSolicit || p->type == NbrAdvert ||
601 p->type == RouterAdvert || p->type == RouterSolicit ||
602 p->type == RedirectV6) {
603 if(p->ttl != HOP_LIMIT) {
604 ipriv->stats[HoplimErrs6]++;
608 ipriv->stats[IcmpCodeErrs6]++;
616 if(isv6mcast(np->target)) {
617 ipriv->stats[TargetErrs6]++;
620 if(optexsts(np) && np->olen == 0) {
621 ipriv->stats[OptlenErrs6]++;
625 if (p->type == NbrSolicit &&
626 ipcmp(np->src, v6Unspecified) == 0)
627 if(!issmcast(np->dst) || optexsts(np)) {
628 ipriv->stats[AddrmxpErrs6]++;
632 if(p->type == NbrAdvert)
633 if(isv6mcast(np->dst) &&
634 (nhgets(np->icmpid) & Sflag)){
635 ipriv->stats[AddrmxpErrs6]++;
641 if(pktsz - IP6HDR < 16) {
642 ipriv->stats[HlenErrs6]++;
645 if(!islinklocal(p->src)) {
646 ipriv->stats[RouterAddrErrs6]++;
650 while (sz+1 < pktsz) {
653 ipriv->stats[OptlenErrs6]++;
661 if(pktsz - IP6HDR < 8) {
662 ipriv->stats[HlenErrs6]++;
665 unsp = (ipcmp(p->src, v6Unspecified) == 0);
667 while (sz+1 < pktsz) {
670 (unsp && packet[sz] == SRC_LLADDR)) {
671 ipriv->stats[OptlenErrs6]++;
679 /* to be filled in */
688 ipriv->stats[InErrors6]++;
693 targettype(Fs *f, Ipifc *ifc, uchar *target)
699 if(ipproxyifc(f, ifc, target)) {
704 for(lifc = ifc->lifc; lifc; lifc = lifc->next)
705 if(ipcmp(lifc->local, target) == 0) {
706 t = (lifc->tentative)? Tunitent: Tunirany;
716 icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp)
721 uchar *packet = bp->rp;
722 uchar lsrc[IPaddrlen];
724 IPICMP *p = (IPICMP *)packet;
725 Icmppriv6 *ipriv = icmp->priv;
730 if(!valid(icmp, ipifc, bp, ipriv) || p->type > Maxtype6)
733 ipriv->in[p->type]++;
737 r = mkechoreply6(concatblock(bp), ipifc);
740 ipriv->out[EchoReply]++;
741 ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
745 if(p->code >= nelem(unreachcode))
746 msg = unreachcode[Icmp6_unknown];
748 msg = unreachcode[p->code];
751 if(blocklen(bp) < 8){
752 ipriv->stats[LenErrs6]++;
755 p = (IPICMP *)bp->rp;
756 pr = Fsrcvpcolx(icmp->f, p->proto);
757 if(pr != nil && pr->advise != nil) {
758 (*pr->advise)(pr, bp, msg);
763 goticmpkt6(icmp, bp, 0);
768 sprint(m2, "ttl exceeded at %I", p->src);
771 if(blocklen(bp) < 8){
772 ipriv->stats[LenErrs6]++;
775 p = (IPICMP *)bp->rp;
776 pr = Fsrcvpcolx(icmp->f, p->proto);
777 if(pr && pr->advise) {
778 (*pr->advise)(pr, bp, m2);
784 goticmpkt6(icmp, bp, 0);
789 /* using lsrc as a temp, munge hdr for goticmp6 */
791 memmove(lsrc, p->src, IPaddrlen);
792 memmove(p->src, p->dst, IPaddrlen);
793 memmove(p->dst, lsrc, IPaddrlen);
795 goticmpkt6(icmp, bp, p->type);
801 switch (targettype(icmp->f, ipifc, np->target)) {
807 if(ipcmp(np->src, v6Unspecified) != 0) {
808 arpenter(icmp->f, V6, np->src, np->lnaddr,
812 if(ipv6local(ipifc, lsrc))
813 icmpna(icmp->f, lsrc,
814 (ipcmp(np->src, v6Unspecified) == 0?
815 v6allnodesL: np->src),
816 np->target, ipifc->mac, pktflags);
822 /* not clear what needs to be done. send up
823 * an icmp mesg saying don't use this address? */
833 * if the target address matches one of the local interface
834 * addresses and the local interface address has tentative bit
835 * set, insert into ARP table. this is so the duplicate address
836 * detection part of ipconfig can discover duplication through
839 lifc = iplocalonifc(ipifc, np->target);
840 if(lifc && lifc->tentative)
842 arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2,
849 goticmpkt6(icmp, bp, 0);
858 icmpstats6(Proto *icmp6, char *buf, int len)
867 for(i = 0; i < Nstats6; i++)
868 p = seprint(p, e, "%s: %lud\n", statnames6[i], priv->stats[i]);
869 for(i = 0; i <= Maxtype6; i++)
871 p = seprint(p, e, "%s: %lud %lud\n", icmpnames6[i],
872 priv->in[i], priv->out[i]);
874 p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i],
881 /* import from icmp.c */
882 extern int icmpstate(Conv *c, char *state, int n);
883 extern char* icmpannounce(Conv *c, char **argv, int argc);
884 extern char* icmpconnect(Conv *c, char **argv, int argc);
885 extern void icmpclose(Conv *c);
890 Proto *icmp6 = smalloc(sizeof(Proto));
892 icmp6->priv = smalloc(sizeof(Icmppriv6));
893 icmp6->name = "icmpv6";
894 icmp6->connect = icmpconnect;
895 icmp6->announce = icmpannounce;
896 icmp6->state = icmpstate;
897 icmp6->create = icmpcreate6;
898 icmp6->close = icmpclose;
899 icmp6->rcv = icmpiput6;
900 icmp6->stats = icmpstats6;
901 icmp6->ctl = icmpctl6;
902 icmp6->advise = icmpadvise6;
904 icmp6->ipproto = ICMPv6;
906 icmp6->ptclsize = sizeof(Icmpcb6);