blob: 741ea1815c432d3a57b632b25f0a8036583ba0fc [file] [log] [blame]
/* 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);
}