| /* Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. |
| * Portions Copyright © 1997-1999 Vita Nuova Limited |
| * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited |
| * (www.vitanuova.com) |
| * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others |
| * |
| * Modified for the Akaros operating system: |
| * Copyright (c) 2013-2014 The Regents of the University of California |
| * Copyright (c) 2013-2015 Google Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. */ |
| |
| #include <kmalloc.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <syscall.h> |
| #include <error.h> |
| #include <net/ip.h> |
| |
| struct ICMPpkt { |
| uint8_t type; |
| uint8_t code; |
| uint8_t cksum[2]; |
| uint8_t icmpid[2]; |
| uint8_t seq[2]; |
| }; |
| |
| /* plan 9 uses anon struct members. |
| * We have been naming the struct |
| * members and just using the extra level of deref |
| * e.g. i->x becomes i->i6->x. |
| */ |
| struct IPICMP { |
| /* |
| Ip6hdr; |
| ICMPpkt; |
| */ |
| uint8_t vcf[4]; // version:4, traffic class:8, flow label:20 |
| uint8_t ploadlen[2]; // payload length: packet length - 40 |
| uint8_t proto; // next header type |
| uint8_t ttl; // hop limit |
| uint8_t src[IPaddrlen]; |
| uint8_t dst[IPaddrlen]; |
| uint8_t type; |
| uint8_t code; |
| uint8_t cksum[2]; |
| uint8_t icmpid[2]; |
| uint8_t seq[2]; |
| |
| }; |
| |
| struct NdiscC { |
| //IPICMP; |
| uint8_t vcf[4]; // version:4, traffic class:8, flow label:20 |
| uint8_t ploadlen[2]; // payload length: packet length - 40 |
| uint8_t proto; // next header type |
| uint8_t ttl; // hop limit |
| uint8_t src[IPaddrlen]; |
| uint8_t dst[IPaddrlen]; |
| uint8_t type; |
| uint8_t code; |
| uint8_t cksum[2]; |
| uint8_t icmpid[2]; |
| uint8_t seq[2]; |
| |
| uint8_t target[IPaddrlen]; |
| }; |
| |
| struct Ndpkt { |
| //NdiscC; |
| uint8_t vcf[4]; // version:4, traffic class:8, flow label:20 |
| uint8_t ploadlen[2]; // payload length: packet length - 40 |
| uint8_t proto; // next header type |
| uint8_t ttl; // hop limit |
| uint8_t src[IPaddrlen]; |
| uint8_t dst[IPaddrlen]; |
| uint8_t type; |
| uint8_t code; |
| uint8_t cksum[2]; |
| uint8_t icmpid[2]; |
| uint8_t seq[2]; |
| |
| uint8_t target[IPaddrlen]; |
| |
| uint8_t otype; |
| uint8_t olen; // length in units of 8 octets(incl type, code), |
| // 1 for IEEE 802 addresses |
| uint8_t lnaddr[6]; // link-layer address |
| }; |
| |
| enum { |
| // ICMPv6 types |
| EchoReply = 0, |
| UnreachableV6 = 1, |
| PacketTooBigV6 = 2, |
| TimeExceedV6 = 3, |
| SrcQuench = 4, |
| ParamProblemV6 = 4, |
| Redirect = 5, |
| EchoRequest = 8, |
| TimeExceed = 11, |
| InParmProblem = 12, |
| Timestamp = 13, |
| TimestampReply = 14, |
| InfoRequest = 15, |
| InfoReply = 16, |
| AddrMaskRequest = 17, |
| AddrMaskReply = 18, |
| EchoRequestV6 = 128, |
| EchoReplyV6 = 129, |
| RouterSolicit = 133, |
| RouterAdvert = 134, |
| NbrSolicit = 135, |
| NbrAdvert = 136, |
| RedirectV6 = 137, |
| |
| Maxtype6 = 137, |
| }; |
| |
| char *icmpnames6[Maxtype6 + 1] = { |
| [EchoReply] "EchoReply", |
| [UnreachableV6] "UnreachableV6", |
| [PacketTooBigV6] "PacketTooBigV6", |
| [TimeExceedV6] "TimeExceedV6", |
| [SrcQuench] "SrcQuench", |
| [Redirect] "Redirect", |
| [EchoRequest] "EchoRequest", |
| [TimeExceed] "TimeExceed", |
| [InParmProblem] "InParmProblem", |
| [Timestamp] "Timestamp", |
| [TimestampReply] "TimestampReply", |
| [InfoRequest] "InfoRequest", |
| [InfoReply] "InfoReply", |
| [AddrMaskRequest] "AddrMaskRequest", |
| [AddrMaskReply] "AddrMaskReply", |
| [EchoRequestV6] "EchoRequestV6", |
| [EchoReplyV6] "EchoReplyV6", |
| [RouterSolicit] "RouterSolicit", |
| [RouterAdvert] "RouterAdvert", |
| [NbrSolicit] "NbrSolicit", |
| [NbrAdvert] "NbrAdvert", |
| [RedirectV6] "RedirectV6", |
| }; |
| |
| enum { |
| InMsgs6, |
| InErrors6, |
| OutMsgs6, |
| CsumErrs6, |
| LenErrs6, |
| HlenErrs6, |
| HoplimErrs6, |
| IcmpCodeErrs6, |
| TargetErrs6, |
| OptlenErrs6, |
| AddrmxpErrs6, |
| RouterAddrErrs6, |
| |
| Nstats6, |
| }; |
| |
| static char *statnames6[Nstats6] = { |
| [InMsgs6] "InMsgs", |
| [InErrors6] "InErrors", |
| [OutMsgs6] "OutMsgs", |
| [CsumErrs6] "CsumErrs", |
| [LenErrs6] "LenErrs", |
| [HlenErrs6] "HlenErrs", |
| [HoplimErrs6] "HoplimErrs", |
| [IcmpCodeErrs6] "IcmpCodeErrs", |
| [TargetErrs6] "TargetErrs", |
| [OptlenErrs6] "OptlenErrs", |
| [AddrmxpErrs6] "AddrmxpErrs", |
| [RouterAddrErrs6] "RouterAddrErrs", |
| }; |
| |
| typedef struct Icmppriv6 { |
| uint32_t stats[Nstats6]; |
| |
| /* message counts */ |
| uint32_t in[Maxtype6 + 1]; |
| uint32_t out[Maxtype6 + 1]; |
| } Icmppriv6; |
| |
| typedef struct Icmpcb6 { |
| uint8_t headers; |
| } Icmpcb6; |
| |
| static char *unreachcode[] = { |
| [icmp6_no_route] "no route to destination", |
| [icmp6_ad_prohib] "comm with destination administratively prohibited", |
| [icmp6_unassigned] "icmp unreachable: unassigned error code (2)", |
| [icmp6_adr_unreach] "address unreachable", |
| [icmp6_port_unreach] "port unreachable", |
| [icmp6_unkn_code] "icmp unreachable: unknown code", |
| }; |
| |
| enum { |
| ICMP_USEAD6 = 40, |
| }; |
| |
| enum { |
| Oflag = 1 << 5, |
| Sflag = 1 << 6, |
| Rflag = 1 << 7, |
| }; |
| |
| enum { |
| slladd = 1, |
| tlladd = 2, |
| prfinfo = 3, |
| redhdr = 4, |
| mtuopt = 5, |
| }; |
| |
| static void icmpkick6(void *x, struct block *bp); |
| |
| static void icmpcreate6(struct conv *c) |
| { |
| c->rq = qopen(64 * 1024, Qmsg, 0, c); |
| c->wq = qbypass(icmpkick6, c); |
| } |
| |
| static void set_cksum(struct block *bp) |
| { |
| struct IPICMP *p = (struct IPICMP *)(bp->rp); |
| |
| hnputl(p->vcf, 0); // borrow IP header as pseudoheader |
| hnputs(p->ploadlen, blocklen(bp) - IPV6HDR_LEN); |
| p->proto = 0; |
| p->ttl = ICMPv6; // ttl gets set later |
| hnputs(p->cksum, 0); |
| hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp))); |
| p->proto = ICMPv6; |
| } |
| |
| static struct block *newIPICMP(int packetlen) |
| { |
| struct block *nbp; |
| |
| nbp = block_alloc(packetlen, MEM_WAIT); |
| nbp->wp += packetlen; |
| memset(nbp->rp, 0, packetlen); |
| return nbp; |
| } |
| |
| void icmpadvise6(struct Proto *icmp, struct block *bp, char *msg) |
| { |
| struct conv **c, *s; |
| struct IPICMP *p; |
| uint16_t recid; |
| |
| p = (struct IPICMP *)bp->rp; |
| recid = nhgets(p->icmpid); |
| |
| for (c = icmp->conv; *c; c++) { |
| s = *c; |
| if (s->lport == recid) |
| if (ipcmp(s->raddr, p->dst) == 0) { |
| qhangup(s->rq, msg); |
| qhangup(s->wq, msg); |
| break; |
| } |
| } |
| freeblist(bp); |
| } |
| |
| static void icmpkick6(void *x, struct block *bp) |
| { |
| struct conv *c = x; |
| struct IPICMP *p; |
| uint8_t laddr[IPaddrlen], raddr[IPaddrlen]; |
| Icmppriv6 *ipriv = c->p->priv; |
| Icmpcb6 *icb = (struct Icmpcb6 *)c->ptcl; |
| |
| if (bp == NULL) |
| return; |
| |
| if (icb->headers == 6) { |
| /* get user specified addresses */ |
| bp = pullupblock(bp, ICMP_USEAD6); |
| if (bp == NULL) |
| return; |
| bp->rp += 8; |
| ipmove(laddr, bp->rp); |
| bp->rp += IPaddrlen; |
| ipmove(raddr, bp->rp); |
| bp->rp += IPaddrlen; |
| bp = padblock(bp, sizeof(struct ip6hdr)); |
| |
| if (blocklen(bp) < sizeof(struct IPICMP)) { |
| freeblist(bp); |
| return; |
| } |
| p = (struct IPICMP *)(bp->rp); |
| |
| ipmove(p->dst, raddr); |
| ipmove(p->src, laddr); |
| |
| } else { |
| if (blocklen(bp) < sizeof(struct IPICMP)) { |
| freeblist(bp); |
| return; |
| } |
| p = (struct IPICMP *)(bp->rp); |
| |
| ipmove(p->dst, c->raddr); |
| ipmove(p->src, c->laddr); |
| hnputs(p->icmpid, c->lport); |
| } |
| |
| set_cksum(bp); |
| p->vcf[0] = 0x06 << 4; |
| if (p->type <= Maxtype6) |
| ipriv->out[p->type]++; |
| ipoput6(c->p->f, bp, 0, c->ttl, c->tos, NULL); |
| } |
| |
| static void icmpctl6(struct conv *c, char **argv, int argc) |
| { |
| Icmpcb6 *icb = (Icmpcb6*)c->ptcl; |
| |
| if ((argc == 1) && strcmp(argv[0], "headers") == 0) |
| icb->headers = 6; |
| else |
| error(EINVAL, "unknown command to icmpctl6"); |
| } |
| |
| static void goticmpkt6(struct Proto *icmp, struct block *bp, int muxkey) |
| { |
| struct conv **c, *s; |
| struct IPICMP *p = (struct IPICMP *)bp->rp; |
| uint16_t recid; |
| uint8_t *addr; |
| |
| if (muxkey == 0) { |
| recid = nhgets(p->icmpid); |
| addr = p->src; |
| } else { |
| recid = muxkey; |
| addr = p->dst; |
| } |
| |
| for (c = icmp->conv; *c; c++) { |
| s = *c; |
| if (s->lport == recid && ipcmp(s->raddr, addr) == 0) { |
| bp = concatblock(bp); |
| if (bp != NULL) |
| qpass(s->rq, bp); |
| return; |
| } |
| } |
| |
| freeblist(bp); |
| } |
| |
| static struct block *mkechoreply6(struct block *bp) |
| { |
| struct IPICMP *p = (struct IPICMP *)(bp->rp); |
| uint8_t addr[IPaddrlen]; |
| |
| ipmove(addr, p->src); |
| ipmove(p->src, p->dst); |
| ipmove(p->dst, addr); |
| p->type = EchoReplyV6; |
| set_cksum(bp); |
| return bp; |
| } |
| |
| /* |
| * sends out an ICMPv6 neighbor solicitation |
| * suni == SRC_UNSPEC or SRC_UNI, |
| * tuni == TARG_MULTI => multicast for address resolution, |
| * and tuni == TARG_UNI => neighbor reachability. |
| */ |
| |
| void icmpns(struct Fs *f, uint8_t *src, int suni, uint8_t *targ, int tuni, |
| uint8_t *mac) |
| { |
| struct block *nbp; |
| struct Ndpkt *np; |
| struct Proto *icmp = f->t2p[ICMPv6]; |
| struct Icmppriv6 *ipriv = icmp->priv; |
| |
| nbp = newIPICMP(sizeof(struct Ndpkt)); |
| np = (struct Ndpkt *)nbp->rp; |
| |
| if (suni == SRC_UNSPEC) |
| memmove(np->src, v6Unspecified, IPaddrlen); |
| else |
| memmove(np->src, src, IPaddrlen); |
| |
| if (tuni == TARG_UNI) |
| memmove(np->dst, targ, IPaddrlen); |
| else |
| ipv62smcast(np->dst, targ); |
| |
| np->type = NbrSolicit; |
| np->code = 0; |
| memmove(np->target, targ, IPaddrlen); |
| if (suni != SRC_UNSPEC) { |
| np->otype = SRC_LLADDRESS; |
| np->olen = 1; /* 1+1+6 = 8 = 1 8-octet */ |
| memmove(np->lnaddr, mac, sizeof(np->lnaddr)); |
| } else { |
| int r = sizeof(struct Ndpkt) - sizeof(struct NdiscC); |
| nbp->wp -= r; |
| } |
| |
| set_cksum(nbp); |
| np = (struct Ndpkt *)nbp->rp; |
| np->ttl = HOP_LIMIT; |
| np->vcf[0] = 0x06 << 4; |
| ipriv->out[NbrSolicit]++; |
| netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ); |
| ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL); |
| } |
| |
| /* |
| * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags. |
| */ |
| void icmpna(struct Fs *f, uint8_t * src, uint8_t * dst, uint8_t * targ, |
| uint8_t * mac, uint8_t flags) |
| { |
| struct block *nbp; |
| struct Ndpkt *np; |
| struct Proto *icmp = f->t2p[ICMPv6]; |
| Icmppriv6 *ipriv = icmp->priv; |
| |
| nbp = newIPICMP(sizeof(struct Ndpkt)); |
| np = (struct Ndpkt *)nbp->rp; |
| |
| memmove(np->src, src, IPaddrlen); |
| memmove(np->dst, dst, IPaddrlen); |
| |
| np->type = NbrAdvert; |
| np->code = 0; |
| np->icmpid[0] = flags; |
| memmove(np->target, targ, IPaddrlen); |
| |
| np->otype = TARGET_LLADDRESS; |
| np->olen = 1; |
| memmove(np->lnaddr, mac, sizeof(np->lnaddr)); |
| |
| set_cksum(nbp); |
| np = (struct Ndpkt *)nbp->rp; |
| np->ttl = HOP_LIMIT; |
| np->vcf[0] = 0x06 << 4; |
| ipriv->out[NbrAdvert]++; |
| netlog(f, Logicmp, "sending neighbor advertisement %I\n", src); |
| ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL); |
| } |
| |
| void icmphostunr(struct Fs *f, struct Ipifc *ifc, |
| struct block *bp, int code, int free) |
| { |
| struct block *nbp; |
| struct IPICMP *np; |
| struct ip6hdr *p; |
| int osz = BLEN(bp); |
| int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU); |
| struct Proto *icmp = f->t2p[ICMPv6]; |
| Icmppriv6 *ipriv = icmp->priv; |
| |
| p = (struct ip6hdr *)bp->rp; |
| |
| if (isv6mcast(p->src)) |
| goto freebl; |
| |
| nbp = newIPICMP(sz); |
| np = (struct IPICMP *)nbp->rp; |
| |
| rlock(&ifc->rwlock); |
| if (ipv6anylocal(ifc, np->src)) { |
| netlog(f, Logicmp, "send icmphostunr -> s%I d%I\n", |
| p->src, p->dst); |
| } else { |
| netlog(f, Logicmp, "icmphostunr fail -> s%I d%I\n", |
| p->src, p->dst); |
| runlock(&ifc->rwlock); |
| freeblist(nbp); |
| goto freebl; |
| } |
| |
| memmove(np->dst, p->src, IPaddrlen); |
| np->type = UnreachableV6; |
| np->code = code; |
| memmove(nbp->rp + sizeof(struct IPICMP), bp->rp, |
| sz - sizeof(struct IPICMP)); |
| set_cksum(nbp); |
| np->ttl = HOP_LIMIT; |
| np->vcf[0] = 0x06 << 4; |
| ipriv->out[UnreachableV6]++; |
| |
| if (free) |
| ipiput6(f, ifc, nbp); |
| else |
| ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL); |
| runlock(&ifc->rwlock); |
| freebl: |
| if (free) |
| freeblist(bp); |
| } |
| |
| void icmpttlexceeded6(struct Fs *f, struct Ipifc *ifc, struct block *bp) |
| { |
| struct block *nbp; |
| struct IPICMP *np; |
| struct ip6hdr *p; |
| int osz = BLEN(bp); |
| int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU); |
| struct Proto *icmp = f->t2p[ICMPv6]; |
| Icmppriv6 *ipriv = icmp->priv; |
| |
| p = (struct ip6hdr *)bp->rp; |
| |
| if (isv6mcast(p->src)) |
| return; |
| |
| nbp = newIPICMP(sz); |
| np = (struct IPICMP *)nbp->rp; |
| |
| if (ipv6anylocal(ifc, np->src)) { |
| netlog(f, Logicmp, "send icmpttlexceeded6 -> s%I d%I\n", |
| p->src, p->dst); |
| } else { |
| netlog(f, Logicmp, "icmpttlexceeded6 fail -> s%I d%I\n", |
| p->src, p->dst); |
| return; |
| } |
| |
| memmove(np->dst, p->src, IPaddrlen); |
| np->type = TimeExceedV6; |
| np->code = 0; |
| memmove(nbp->rp + sizeof(struct IPICMP), bp->rp, |
| sz - sizeof(struct IPICMP)); |
| set_cksum(nbp); |
| np->ttl = HOP_LIMIT; |
| np->vcf[0] = 0x06 << 4; |
| ipriv->out[TimeExceedV6]++; |
| ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL); |
| } |
| |
| void icmppkttoobig6(struct Fs *f, struct Ipifc *ifc, struct block *bp) |
| { |
| struct block *nbp; |
| struct IPICMP *np; |
| struct ip6hdr *p; |
| int osz = BLEN(bp); |
| int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU); |
| struct Proto *icmp = f->t2p[ICMPv6]; |
| Icmppriv6 *ipriv = icmp->priv; |
| |
| p = (struct ip6hdr *)bp->rp; |
| |
| if (isv6mcast(p->src)) |
| return; |
| |
| nbp = newIPICMP(sz); |
| np = (struct IPICMP *)nbp->rp; |
| |
| if (!ipv6anylocal(ifc, np->src)) { |
| netlog(f, Logicmp, "icmppkttoobig6 fail -> s%I d%I\n", |
| p->src, p->dst); |
| return; |
| } |
| netlog(f, Logicmp, "send icmppkttoobig6 -> s%I d%I\n", |
| p->src, p->dst); |
| |
| memmove(np->dst, p->src, IPaddrlen); |
| np->type = PacketTooBigV6; |
| np->code = 0; |
| hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize); |
| memmove(nbp->rp + sizeof(struct IPICMP), bp->rp, |
| sz - sizeof(struct IPICMP)); |
| set_cksum(nbp); |
| np->ttl = HOP_LIMIT; |
| np->vcf[0] = 0x06 << 4; |
| ipriv->out[PacketTooBigV6]++; |
| ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL); |
| } |
| |
| /* |
| * RFC 2461, pages 39-40, pages 57-58. |
| */ |
| static int valid(struct Proto *icmp, struct Ipifc *ifc, |
| struct block *bp, Icmppriv6 * ipriv) |
| { |
| int sz, osz, unsp, n, ttl, iplen; |
| int pktsz = BLEN(bp); |
| uint8_t *packet = bp->rp; |
| struct IPICMP *p = (struct IPICMP *)packet; |
| struct Ndpkt *np; |
| |
| n = blocklen(bp); |
| if (n < sizeof(struct IPICMP)) { |
| ipriv->stats[HlenErrs6]++; |
| netlog(icmp->f, Logicmp, "icmp hlen %d\n", n); |
| goto err; |
| } |
| |
| iplen = nhgets(p->ploadlen); |
| if (iplen > n - IPV6HDR_LEN || (iplen % 1)) { |
| ipriv->stats[LenErrs6]++; |
| netlog(icmp->f, Logicmp, "icmp length %d\n", iplen); |
| goto err; |
| } |
| // Rather than construct explicit pseudoheader, overwrite IPv6 header |
| if (p->proto != ICMPv6) { |
| // This code assumes no extension headers!!! |
| netlog(icmp->f, Logicmp, "icmp error: extension header\n"); |
| goto err; |
| } |
| memset(packet, 0, 4); |
| ttl = p->ttl; |
| p->ttl = p->proto; |
| p->proto = 0; |
| if (ptclcsum(bp, 0, iplen + IPV6HDR_LEN)) { |
| ipriv->stats[CsumErrs6]++; |
| netlog(icmp->f, Logicmp, "icmp checksum error\n"); |
| goto err; |
| } |
| p->proto = p->ttl; |
| p->ttl = ttl; |
| |
| /* additional tests for some pkt types */ |
| if ((p->type == NbrSolicit) || |
| (p->type == NbrAdvert) || |
| (p->type == RouterAdvert) || |
| (p->type == RouterSolicit) || (p->type == RedirectV6)) { |
| |
| if (p->ttl != HOP_LIMIT) { |
| ipriv->stats[HoplimErrs6]++; |
| goto err; |
| } |
| if (p->code != 0) { |
| ipriv->stats[IcmpCodeErrs6]++; |
| goto err; |
| } |
| |
| switch (p->type) { |
| case NbrSolicit: |
| case NbrAdvert: |
| np = (struct Ndpkt *)p; |
| if (isv6mcast(np->target)) { |
| ipriv->stats[TargetErrs6]++; |
| goto err; |
| } |
| if (optexsts(np) && (np->olen == 0)) { |
| ipriv->stats[OptlenErrs6]++; |
| goto err; |
| } |
| |
| if (p->type == NbrSolicit) { |
| if (ipcmp(np->src, v6Unspecified) == 0) { |
| if (!issmcast(np->dst) || optexsts(np)) |
| { |
| ipriv->stats[AddrmxpErrs6]++; |
| goto err; |
| } |
| } |
| } |
| |
| if (p->type == NbrAdvert) { |
| if ((isv6mcast(np->dst)) && |
| (nhgets(np->icmpid) & Sflag)) { |
| ipriv->stats[AddrmxpErrs6]++; |
| goto err; |
| } |
| } |
| break; |
| |
| case RouterAdvert: |
| if (pktsz - sizeof(struct ip6hdr) < 16) { |
| ipriv->stats[HlenErrs6]++; |
| goto err; |
| } |
| if (!islinklocal(p->src)) { |
| ipriv->stats[RouterAddrErrs6]++; |
| goto err; |
| } |
| sz = sizeof(struct IPICMP) + 8; |
| while ((sz + 1) < pktsz) { |
| osz = *(packet + sz + 1); |
| if (osz <= 0) { |
| ipriv->stats[OptlenErrs6]++; |
| goto err; |
| } |
| sz += 8 * osz; |
| } |
| break; |
| |
| case RouterSolicit: |
| if (pktsz - sizeof(struct ip6hdr) < 8) { |
| ipriv->stats[HlenErrs6]++; |
| goto err; |
| } |
| unsp = (ipcmp(p->src, v6Unspecified) == 0); |
| sz = sizeof(struct IPICMP) + 8; |
| while ((sz + 1) < pktsz) { |
| osz = *(packet + sz + 1); |
| if ((osz <= 0) || |
| (unsp && (*(packet + sz) == slladd))) { |
| ipriv->stats[OptlenErrs6]++; |
| goto err; |
| } |
| sz += 8 * osz; |
| } |
| break; |
| |
| case RedirectV6: |
| //to be filled in |
| break; |
| |
| default: |
| goto err; |
| } |
| } |
| |
| return 1; |
| |
| err: |
| ipriv->stats[InErrors6]++; |
| return 0; |
| } |
| |
| static int targettype(struct Fs *f, struct Ipifc *ifc, uint8_t * target) |
| { |
| struct Iplifc *lifc; |
| int t; |
| |
| rlock(&ifc->rwlock); |
| if (ipproxyifc(f, ifc, target)) { |
| runlock(&ifc->rwlock); |
| return t_uniproxy; |
| } |
| |
| for (lifc = ifc->lifc; lifc; lifc = lifc->next) { |
| if (ipcmp(lifc->local, target) == 0) { |
| t = (lifc->tentative) ? t_unitent : t_unirany; |
| runlock(&ifc->rwlock); |
| return t; |
| } |
| } |
| |
| runlock(&ifc->rwlock); |
| return 0; |
| } |
| |
| static void icmpiput6(struct Proto *icmp, struct Ipifc *ipifc, struct block *bp) |
| { |
| uint8_t *packet = bp->rp; |
| struct IPICMP *p = (struct IPICMP *)packet; |
| Icmppriv6 *ipriv = icmp->priv; |
| struct block *r; |
| struct Proto *pr; |
| char *msg, m2[128]; |
| struct Ndpkt *np; |
| uint8_t pktflags; |
| uint8_t lsrc[IPaddrlen]; |
| int refresh = 1; |
| struct Iplifc *lifc; |
| |
| if (!valid(icmp, ipifc, bp, ipriv)) |
| goto raise; |
| |
| if (p->type <= Maxtype6) |
| ipriv->in[p->type]++; |
| else |
| goto raise; |
| |
| switch (p->type) { |
| case EchoRequestV6: |
| r = mkechoreply6(bp); |
| ipriv->out[EchoReply]++; |
| ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, NULL); |
| break; |
| |
| case UnreachableV6: |
| if (p->code > 4) |
| msg = unreachcode[icmp6_unkn_code]; |
| else |
| msg = unreachcode[p->code]; |
| |
| bp->rp += sizeof(struct IPICMP); |
| if (blocklen(bp) < 8) { |
| ipriv->stats[LenErrs6]++; |
| goto raise; |
| } |
| p = (struct IPICMP *)bp->rp; |
| pr = Fsrcvpcolx(icmp->f, p->proto); |
| if (pr != NULL && pr->advise != NULL) { |
| (*pr->advise) (pr, bp, msg); |
| return; |
| } |
| |
| bp->rp -= sizeof(struct IPICMP); |
| goticmpkt6(icmp, bp, 0); |
| break; |
| |
| case TimeExceedV6: |
| if (p->code == 0) { |
| snprintf(m2, sizeof(m2), "ttl exceeded at %I", p->src); |
| |
| bp->rp += sizeof(struct IPICMP); |
| if (blocklen(bp) < 8) { |
| ipriv->stats[LenErrs6]++; |
| goto raise; |
| } |
| p = (struct IPICMP *)bp->rp; |
| pr = Fsrcvpcolx(icmp->f, p->proto); |
| if (pr != NULL && pr->advise != NULL) { |
| (*pr->advise) (pr, bp, m2); |
| return; |
| } |
| bp->rp -= sizeof(struct IPICMP); |
| } |
| |
| goticmpkt6(icmp, bp, 0); |
| break; |
| |
| case RouterAdvert: |
| case RouterSolicit: |
| /* using lsrc as a temp, munge hdr for goticmp6 |
| memmove(lsrc, p->src, IPaddrlen); |
| memmove(p->src, p->dst, IPaddrlen); |
| memmove(p->dst, lsrc, IPaddrlen); */ |
| |
| goticmpkt6(icmp, bp, p->type); |
| break; |
| |
| case NbrSolicit: |
| np = (struct Ndpkt *)p; |
| pktflags = 0; |
| switch (targettype(icmp->f, ipifc, np->target)) { |
| case t_unirany: |
| pktflags |= Oflag; |
| /* fall through */ |
| |
| case t_uniproxy: |
| if (ipcmp(np->src, v6Unspecified) != 0) { |
| arpenter(icmp->f, V6, np->src, |
| np->lnaddr, 8 * np->olen - 2, |
| 0); |
| pktflags |= Sflag; |
| } |
| if (ipv6local(ipifc, lsrc)) { |
| icmpna(icmp->f, lsrc, |
| (ipcmp(np->src, v6Unspecified) |
| == 0) ? v6allnodesL : np->src, |
| np->target, ipifc->mac, |
| pktflags); |
| } else |
| freeblist(bp); |
| break; |
| |
| case t_unitent: |
| /* not clear what needs to be done. send up an |
| * icmp mesg saying don't use this address? */ |
| |
| default: |
| freeblist(bp); |
| } |
| |
| break; |
| |
| case NbrAdvert: |
| np = (struct Ndpkt *)p; |
| |
| /* if the target address matches one of the local interface |
| * address and the local interface address has tentative bit |
| * set, then insert into ARP table. this is so the duplication |
| * address detection part of ipconfig can discover duplication |
| * through the arp table |
| */ |
| lifc = iplocalonifc(ipifc, np->target); |
| if (lifc && lifc->tentative) |
| refresh = 0; |
| arpenter(icmp->f, V6, np->target, np->lnaddr, 8 * np->olen - 2, |
| refresh); |
| freeblist(bp); |
| break; |
| |
| case PacketTooBigV6: |
| |
| default: |
| goticmpkt6(icmp, bp, 0); |
| break; |
| } |
| return; |
| |
| raise: |
| freeblist(bp); |
| |
| } |
| |
| int icmpstats6(struct Proto *icmp6, char *buf, int len) |
| { |
| Icmppriv6 *priv; |
| char *p, *e; |
| int i; |
| |
| priv = icmp6->priv; |
| p = buf; |
| e = p + len; |
| for (i = 0; i < Nstats6; i++) |
| p = seprintf(p, e, "%s: %u\n", statnames6[i], priv->stats[i]); |
| for (i = 0; i <= Maxtype6; i++) { |
| if (icmpnames6[i]) |
| p = seprintf(p, e, "%s: %u %u\n", icmpnames6[i], |
| priv->in[i], priv->out[i]); |
| else |
| p = seprintf(p, e, "%d: %u %u\n", i, priv->in[i], |
| priv->out[i]); |
| } |
| return p - buf; |
| } |
| |
| // need to import from icmp.c |
| extern int icmpstate(struct conv *c, char *state, int n); |
| extern void icmpannounce(struct conv *c, char **argv, int argc); |
| extern void icmpconnect(struct conv *c, char **argv, int argc); |
| extern void icmpclose(struct conv *c); |
| |
| void icmp6init(struct Fs *fs) |
| { |
| struct Proto *icmp6 = kzmalloc(sizeof(struct Proto), 0); |
| |
| icmp6->priv = kzmalloc(sizeof(Icmppriv6), 0); |
| icmp6->name = "icmpv6"; |
| icmp6->connect = icmpconnect; |
| icmp6->announce = icmpannounce; |
| icmp6->state = icmpstate; |
| icmp6->create = icmpcreate6; |
| icmp6->close = icmpclose; |
| icmp6->rcv = icmpiput6; |
| icmp6->stats = icmpstats6; |
| icmp6->ctl = icmpctl6; |
| icmp6->advise = icmpadvise6; |
| icmp6->gc = NULL; |
| icmp6->ipproto = ICMPv6; |
| icmp6->nc = 16; |
| icmp6->ptclsize = sizeof(Icmpcb6); |
| |
| Fsproto(fs, icmp6); |
| } |