1ccf6439bSDavid du Colombier /*
2ccf6439bSDavid du Colombier * Internet Control Message Protocol for IPv6
3ccf6439bSDavid du Colombier */
43ff48bf5SDavid du Colombier #include "u.h"
53ff48bf5SDavid du Colombier #include "../port/lib.h"
63ff48bf5SDavid du Colombier #include "mem.h"
73ff48bf5SDavid du Colombier #include "dat.h"
83ff48bf5SDavid du Colombier #include "fns.h"
93ff48bf5SDavid du Colombier #include "../port/error.h"
103ff48bf5SDavid du Colombier #include "ip.h"
113ff48bf5SDavid du Colombier #include "ipv6.h"
123ff48bf5SDavid du Colombier
13ccf6439bSDavid du Colombier enum
14ccf6439bSDavid du Colombier {
15ccf6439bSDavid du Colombier InMsgs6,
16ccf6439bSDavid du Colombier InErrors6,
17ccf6439bSDavid du Colombier OutMsgs6,
18ccf6439bSDavid du Colombier CsumErrs6,
19ccf6439bSDavid du Colombier LenErrs6,
20ccf6439bSDavid du Colombier HlenErrs6,
21ccf6439bSDavid du Colombier HoplimErrs6,
22ccf6439bSDavid du Colombier IcmpCodeErrs6,
23ccf6439bSDavid du Colombier TargetErrs6,
24ccf6439bSDavid du Colombier OptlenErrs6,
25ccf6439bSDavid du Colombier AddrmxpErrs6,
26ccf6439bSDavid du Colombier RouterAddrErrs6,
27ccf6439bSDavid du Colombier
28ccf6439bSDavid du Colombier Nstats6,
29ccf6439bSDavid du Colombier };
30ccf6439bSDavid du Colombier
31ccf6439bSDavid du Colombier enum {
32ccf6439bSDavid du Colombier ICMP_USEAD6 = 40,
33ccf6439bSDavid du Colombier };
34ccf6439bSDavid du Colombier
35ccf6439bSDavid du Colombier enum {
36ccf6439bSDavid du Colombier Oflag = 1<<5,
37ccf6439bSDavid du Colombier Sflag = 1<<6,
38ccf6439bSDavid du Colombier Rflag = 1<<7,
39ccf6439bSDavid du Colombier };
40ccf6439bSDavid du Colombier
41ccf6439bSDavid du Colombier enum {
42ccf6439bSDavid du Colombier /* ICMPv6 types */
43ccf6439bSDavid du Colombier EchoReply = 0,
44ccf6439bSDavid du Colombier UnreachableV6 = 1,
45ccf6439bSDavid du Colombier PacketTooBigV6 = 2,
46ccf6439bSDavid du Colombier TimeExceedV6 = 3,
47ccf6439bSDavid du Colombier SrcQuench = 4,
48ccf6439bSDavid du Colombier ParamProblemV6 = 4,
49ccf6439bSDavid du Colombier Redirect = 5,
50ccf6439bSDavid du Colombier EchoRequest = 8,
51ccf6439bSDavid du Colombier TimeExceed = 11,
52ccf6439bSDavid du Colombier InParmProblem = 12,
53ccf6439bSDavid du Colombier Timestamp = 13,
54ccf6439bSDavid du Colombier TimestampReply = 14,
55ccf6439bSDavid du Colombier InfoRequest = 15,
56ccf6439bSDavid du Colombier InfoReply = 16,
57ccf6439bSDavid du Colombier AddrMaskRequest = 17,
58ccf6439bSDavid du Colombier AddrMaskReply = 18,
59ccf6439bSDavid du Colombier EchoRequestV6 = 128,
60ccf6439bSDavid du Colombier EchoReplyV6 = 129,
61ccf6439bSDavid du Colombier RouterSolicit = 133,
62ccf6439bSDavid du Colombier RouterAdvert = 134,
63ccf6439bSDavid du Colombier NbrSolicit = 135,
64ccf6439bSDavid du Colombier NbrAdvert = 136,
65ccf6439bSDavid du Colombier RedirectV6 = 137,
66ccf6439bSDavid du Colombier
67ccf6439bSDavid du Colombier Maxtype6 = 137,
68ccf6439bSDavid du Colombier };
69ccf6439bSDavid du Colombier
70410ea80bSDavid du Colombier /* on-the-wire packet formats */
713ff48bf5SDavid du Colombier typedef struct IPICMP IPICMP;
723ff48bf5SDavid du Colombier typedef struct Ndpkt Ndpkt;
733ff48bf5SDavid du Colombier typedef struct NdiscC NdiscC;
743ff48bf5SDavid du Colombier
75410ea80bSDavid du Colombier /* we do this to avoid possible struct padding */
76410ea80bSDavid du Colombier #define ICMPHDR \
77410ea80bSDavid du Colombier IPV6HDR; \
78410ea80bSDavid du Colombier uchar type; \
79410ea80bSDavid du Colombier uchar code; \
80410ea80bSDavid du Colombier uchar cksum[2]; \
81410ea80bSDavid du Colombier uchar icmpid[2]; \
82410ea80bSDavid du Colombier uchar seq[2]
833ff48bf5SDavid du Colombier
843ff48bf5SDavid du Colombier struct IPICMP {
85410ea80bSDavid du Colombier ICMPHDR;
86410ea80bSDavid du Colombier uchar payload[];
873ff48bf5SDavid du Colombier };
883ff48bf5SDavid du Colombier
89410ea80bSDavid du Colombier #define IPICMPSZ offsetof(IPICMP, payload[0])
90410ea80bSDavid du Colombier
91410ea80bSDavid du Colombier struct NdiscC {
92410ea80bSDavid du Colombier ICMPHDR;
933ff48bf5SDavid du Colombier uchar target[IPaddrlen];
94410ea80bSDavid du Colombier uchar payload[];
953ff48bf5SDavid du Colombier };
963ff48bf5SDavid du Colombier
97410ea80bSDavid du Colombier #define NDISCSZ offsetof(NdiscC, payload[0])
98410ea80bSDavid du Colombier
99410ea80bSDavid du Colombier struct Ndpkt {
100410ea80bSDavid du Colombier ICMPHDR;
101410ea80bSDavid du Colombier uchar target[IPaddrlen];
1023ff48bf5SDavid du Colombier uchar otype;
103ccf6439bSDavid du Colombier uchar olen; /* length in units of 8 octets(incl type, code),
104ccf6439bSDavid du Colombier * 1 for IEEE 802 addresses */
105ccf6439bSDavid du Colombier uchar lnaddr[6]; /* link-layer address */
106410ea80bSDavid du Colombier uchar payload[];
1073ff48bf5SDavid du Colombier };
1083ff48bf5SDavid du Colombier
109410ea80bSDavid du Colombier #define NDPKTSZ offsetof(Ndpkt, payload[0])
110410ea80bSDavid du Colombier
111ccf6439bSDavid du Colombier typedef struct Icmppriv6
112ccf6439bSDavid du Colombier {
113ccf6439bSDavid du Colombier ulong stats[Nstats6];
1143ff48bf5SDavid du Colombier
115ccf6439bSDavid du Colombier /* message counts */
116ccf6439bSDavid du Colombier ulong in[Maxtype6+1];
117ccf6439bSDavid du Colombier ulong out[Maxtype6+1];
118ccf6439bSDavid du Colombier } Icmppriv6;
119ccf6439bSDavid du Colombier
120ccf6439bSDavid du Colombier typedef struct Icmpcb6
121ccf6439bSDavid du Colombier {
122ccf6439bSDavid du Colombier QLock;
123ccf6439bSDavid du Colombier uchar headers;
124ccf6439bSDavid du Colombier } Icmpcb6;
1253ff48bf5SDavid du Colombier
1263ff48bf5SDavid du Colombier char *icmpnames6[Maxtype6+1] =
1273ff48bf5SDavid du Colombier {
1283ff48bf5SDavid du Colombier [EchoReply] "EchoReply",
1293ff48bf5SDavid du Colombier [UnreachableV6] "UnreachableV6",
1303ff48bf5SDavid du Colombier [PacketTooBigV6] "PacketTooBigV6",
1313ff48bf5SDavid du Colombier [TimeExceedV6] "TimeExceedV6",
1323ff48bf5SDavid du Colombier [SrcQuench] "SrcQuench",
1333ff48bf5SDavid du Colombier [Redirect] "Redirect",
1343ff48bf5SDavid du Colombier [EchoRequest] "EchoRequest",
1353ff48bf5SDavid du Colombier [TimeExceed] "TimeExceed",
1363ff48bf5SDavid du Colombier [InParmProblem] "InParmProblem",
1373ff48bf5SDavid du Colombier [Timestamp] "Timestamp",
1383ff48bf5SDavid du Colombier [TimestampReply] "TimestampReply",
1393ff48bf5SDavid du Colombier [InfoRequest] "InfoRequest",
1403ff48bf5SDavid du Colombier [InfoReply] "InfoReply",
1413ff48bf5SDavid du Colombier [AddrMaskRequest] "AddrMaskRequest",
1423ff48bf5SDavid du Colombier [AddrMaskReply] "AddrMaskReply",
1433ff48bf5SDavid du Colombier [EchoRequestV6] "EchoRequestV6",
1443ff48bf5SDavid du Colombier [EchoReplyV6] "EchoReplyV6",
1453ff48bf5SDavid du Colombier [RouterSolicit] "RouterSolicit",
1463ff48bf5SDavid du Colombier [RouterAdvert] "RouterAdvert",
1473ff48bf5SDavid du Colombier [NbrSolicit] "NbrSolicit",
1483ff48bf5SDavid du Colombier [NbrAdvert] "NbrAdvert",
1493ff48bf5SDavid du Colombier [RedirectV6] "RedirectV6",
1503ff48bf5SDavid du Colombier };
1513ff48bf5SDavid du Colombier
1523ff48bf5SDavid du Colombier static char *statnames6[Nstats6] =
1533ff48bf5SDavid du Colombier {
1543ff48bf5SDavid du Colombier [InMsgs6] "InMsgs",
1553ff48bf5SDavid du Colombier [InErrors6] "InErrors",
1563ff48bf5SDavid du Colombier [OutMsgs6] "OutMsgs",
1573ff48bf5SDavid du Colombier [CsumErrs6] "CsumErrs",
1583ff48bf5SDavid du Colombier [LenErrs6] "LenErrs",
1593ff48bf5SDavid du Colombier [HlenErrs6] "HlenErrs",
1603ff48bf5SDavid du Colombier [HoplimErrs6] "HoplimErrs",
1613ff48bf5SDavid du Colombier [IcmpCodeErrs6] "IcmpCodeErrs",
1623ff48bf5SDavid du Colombier [TargetErrs6] "TargetErrs",
1633ff48bf5SDavid du Colombier [OptlenErrs6] "OptlenErrs",
1643ff48bf5SDavid du Colombier [AddrmxpErrs6] "AddrmxpErrs",
1653ff48bf5SDavid du Colombier [RouterAddrErrs6] "RouterAddrErrs",
1663ff48bf5SDavid du Colombier };
1673ff48bf5SDavid du Colombier
1683ff48bf5SDavid du Colombier static char *unreachcode[] =
1693ff48bf5SDavid du Colombier {
170f2c197d9SDavid du Colombier [Icmp6_no_route] "no route to destination",
171f2c197d9SDavid du Colombier [Icmp6_ad_prohib] "comm with destination administratively prohibited",
172f2c197d9SDavid du Colombier [Icmp6_out_src_scope] "beyond scope of source address",
173f2c197d9SDavid du Colombier [Icmp6_adr_unreach] "address unreachable",
174f2c197d9SDavid du Colombier [Icmp6_port_unreach] "port unreachable",
175f2c197d9SDavid du Colombier [Icmp6_gress_src_fail] "source address failed ingress/egress policy",
176f2c197d9SDavid du Colombier [Icmp6_rej_route] "reject route to destination",
177f2c197d9SDavid du Colombier [Icmp6_unknown] "icmp unreachable: unknown code",
1783ff48bf5SDavid du Colombier };
1793ff48bf5SDavid du Colombier
180e6c6b7f8SDavid du Colombier static void icmpkick6(void *x, Block *bp);
1813ff48bf5SDavid du Colombier
1823ff48bf5SDavid du Colombier static void
icmpcreate6(Conv * c)1833ff48bf5SDavid du Colombier icmpcreate6(Conv *c)
1843ff48bf5SDavid du Colombier {
1853ff48bf5SDavid du Colombier c->rq = qopen(64*1024, Qmsg, 0, c);
186e6c6b7f8SDavid du Colombier c->wq = qbypass(icmpkick6, c);
1873ff48bf5SDavid du Colombier }
1883ff48bf5SDavid du Colombier
1893ff48bf5SDavid du Colombier static void
set_cksum(Block * bp)1903ff48bf5SDavid du Colombier set_cksum(Block *bp)
1913ff48bf5SDavid du Colombier {
1923ff48bf5SDavid du Colombier IPICMP *p = (IPICMP *)(bp->rp);
1933ff48bf5SDavid du Colombier
194ccf6439bSDavid du Colombier hnputl(p->vcf, 0); /* borrow IP header as pseudoheader */
1950774058cSDavid du Colombier hnputs(p->ploadlen, blocklen(bp) - IP6HDR);
1963ff48bf5SDavid du Colombier p->proto = 0;
197ccf6439bSDavid du Colombier p->ttl = ICMPv6; /* ttl gets set later */
1983ff48bf5SDavid du Colombier hnputs(p->cksum, 0);
1993ff48bf5SDavid du Colombier hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp)));
2003ff48bf5SDavid du Colombier p->proto = ICMPv6;
2013ff48bf5SDavid du Colombier }
2023ff48bf5SDavid du Colombier
2033ff48bf5SDavid du Colombier static Block *
newIPICMP(int packetlen)2043ff48bf5SDavid du Colombier newIPICMP(int packetlen)
2053ff48bf5SDavid du Colombier {
2063ff48bf5SDavid du Colombier Block *nbp;
207ccf6439bSDavid du Colombier
2083ff48bf5SDavid du Colombier nbp = allocb(packetlen);
2093ff48bf5SDavid du Colombier nbp->wp += packetlen;
2103ff48bf5SDavid du Colombier memset(nbp->rp, 0, packetlen);
2113ff48bf5SDavid du Colombier return nbp;
2123ff48bf5SDavid du Colombier }
2133ff48bf5SDavid du Colombier
2143ff48bf5SDavid du Colombier void
icmpadvise6(Proto * icmp,Block * bp,char * msg)2153ff48bf5SDavid du Colombier icmpadvise6(Proto *icmp, Block *bp, char *msg)
2163ff48bf5SDavid du Colombier {
217ccf6439bSDavid du Colombier ushort recid;
2183ff48bf5SDavid du Colombier Conv **c, *s;
2193ff48bf5SDavid du Colombier IPICMP *p;
2203ff48bf5SDavid du Colombier
2213ff48bf5SDavid du Colombier p = (IPICMP *)bp->rp;
2223ff48bf5SDavid du Colombier recid = nhgets(p->icmpid);
2233ff48bf5SDavid du Colombier
2243ff48bf5SDavid du Colombier for(c = icmp->conv; *c; c++) {
2253ff48bf5SDavid du Colombier s = *c;
226ccf6439bSDavid du Colombier if(s->lport == recid && ipcmp(s->raddr, p->dst) == 0){
2273ff48bf5SDavid du Colombier qhangup(s->rq, msg);
2283ff48bf5SDavid du Colombier qhangup(s->wq, msg);
2293ff48bf5SDavid du Colombier break;
2303ff48bf5SDavid du Colombier }
2313ff48bf5SDavid du Colombier }
2323ff48bf5SDavid du Colombier freeblist(bp);
2333ff48bf5SDavid du Colombier }
2343ff48bf5SDavid du Colombier
2353ff48bf5SDavid du Colombier static void
icmpkick6(void * x,Block * bp)236e6c6b7f8SDavid du Colombier icmpkick6(void *x, Block *bp)
2373ff48bf5SDavid du Colombier {
238ccf6439bSDavid du Colombier uchar laddr[IPaddrlen], raddr[IPaddrlen];
2393ff48bf5SDavid du Colombier Conv *c = x;
2403ff48bf5SDavid du Colombier IPICMP *p;
2413ff48bf5SDavid du Colombier Icmppriv6 *ipriv = c->p->priv;
2423ff48bf5SDavid du Colombier Icmpcb6 *icb = (Icmpcb6*)c->ptcl;
2433ff48bf5SDavid du Colombier
2443ff48bf5SDavid du Colombier if(bp == nil)
2453ff48bf5SDavid du Colombier return;
2463ff48bf5SDavid du Colombier
2473ff48bf5SDavid du Colombier if(icb->headers==6) {
2483ff48bf5SDavid du Colombier /* get user specified addresses */
2493ff48bf5SDavid du Colombier bp = pullupblock(bp, ICMP_USEAD6);
2503ff48bf5SDavid du Colombier if(bp == nil)
2513ff48bf5SDavid du Colombier return;
2523ff48bf5SDavid du Colombier bp->rp += 8;
2533ff48bf5SDavid du Colombier ipmove(laddr, bp->rp);
2543ff48bf5SDavid du Colombier bp->rp += IPaddrlen;
2553ff48bf5SDavid du Colombier ipmove(raddr, bp->rp);
2563ff48bf5SDavid du Colombier bp->rp += IPaddrlen;
257410ea80bSDavid du Colombier bp = padblock(bp, IP6HDR);
2583ff48bf5SDavid du Colombier }
2593ff48bf5SDavid du Colombier
260410ea80bSDavid du Colombier if(blocklen(bp) < IPICMPSZ){
2613ff48bf5SDavid du Colombier freeblist(bp);
2623ff48bf5SDavid du Colombier return;
2633ff48bf5SDavid du Colombier }
2643ff48bf5SDavid du Colombier p = (IPICMP *)(bp->rp);
2653ff48bf5SDavid du Colombier if(icb->headers == 6) {
2663ff48bf5SDavid du Colombier ipmove(p->dst, raddr);
2673ff48bf5SDavid du Colombier ipmove(p->src, laddr);
2683ff48bf5SDavid du Colombier } else {
2693ff48bf5SDavid du Colombier ipmove(p->dst, c->raddr);
2703ff48bf5SDavid du Colombier ipmove(p->src, c->laddr);
2713ff48bf5SDavid du Colombier hnputs(p->icmpid, c->lport);
2723ff48bf5SDavid du Colombier }
2733ff48bf5SDavid du Colombier
2743ff48bf5SDavid du Colombier set_cksum(bp);
2753ff48bf5SDavid du Colombier p->vcf[0] = 0x06 << 4;
2763ff48bf5SDavid du Colombier if(p->type <= Maxtype6)
2773ff48bf5SDavid du Colombier ipriv->out[p->type]++;
278a6a9e072SDavid du Colombier ipoput6(c->p->f, bp, 0, c->ttl, c->tos, nil);
2793ff48bf5SDavid du Colombier }
2803ff48bf5SDavid du Colombier
2813ff48bf5SDavid du Colombier char*
icmpctl6(Conv * c,char ** argv,int argc)2823ff48bf5SDavid du Colombier icmpctl6(Conv *c, char **argv, int argc)
2833ff48bf5SDavid du Colombier {
2843ff48bf5SDavid du Colombier Icmpcb6 *icb;
2853ff48bf5SDavid du Colombier
2863ff48bf5SDavid du Colombier icb = (Icmpcb6*) c->ptcl;
287ccf6439bSDavid du Colombier if(argc==1 && strcmp(argv[0], "headers")==0) {
2883ff48bf5SDavid du Colombier icb->headers = 6;
2893ff48bf5SDavid du Colombier return nil;
2903ff48bf5SDavid du Colombier }
2913ff48bf5SDavid du Colombier return "unknown control request";
2923ff48bf5SDavid du Colombier }
2933ff48bf5SDavid du Colombier
2943ff48bf5SDavid du Colombier static void
goticmpkt6(Proto * icmp,Block * bp,int muxkey)2953ff48bf5SDavid du Colombier goticmpkt6(Proto *icmp, Block *bp, int muxkey)
2963ff48bf5SDavid du Colombier {
2973ff48bf5SDavid du Colombier ushort recid;
2983ff48bf5SDavid du Colombier uchar *addr;
299ccf6439bSDavid du Colombier Conv **c, *s;
300ccf6439bSDavid du Colombier IPICMP *p = (IPICMP *)bp->rp;
3013ff48bf5SDavid du Colombier
3023ff48bf5SDavid du Colombier if(muxkey == 0) {
3033ff48bf5SDavid du Colombier recid = nhgets(p->icmpid);
3043ff48bf5SDavid du Colombier addr = p->src;
305ccf6439bSDavid du Colombier } else {
3063ff48bf5SDavid du Colombier recid = muxkey;
3073ff48bf5SDavid du Colombier addr = p->dst;
3083ff48bf5SDavid du Colombier }
3093ff48bf5SDavid du Colombier
3103ff48bf5SDavid du Colombier for(c = icmp->conv; *c; c++){
3113ff48bf5SDavid du Colombier s = *c;
3123ff48bf5SDavid du Colombier if(s->lport == recid && ipcmp(s->raddr, addr) == 0){
3133ff48bf5SDavid du Colombier bp = concatblock(bp);
3143ff48bf5SDavid du Colombier if(bp != nil)
3153ff48bf5SDavid du Colombier qpass(s->rq, bp);
3163ff48bf5SDavid du Colombier return;
3173ff48bf5SDavid du Colombier }
3183ff48bf5SDavid du Colombier }
3193ff48bf5SDavid du Colombier
3203ff48bf5SDavid du Colombier freeblist(bp);
3213ff48bf5SDavid du Colombier }
3223ff48bf5SDavid du Colombier
3233ff48bf5SDavid du Colombier static Block *
mkechoreply6(Block * bp,Ipifc * ifc)3247366567fSDavid du Colombier mkechoreply6(Block *bp, Ipifc *ifc)
3253ff48bf5SDavid du Colombier {
3263ff48bf5SDavid du Colombier uchar addr[IPaddrlen];
327ccf6439bSDavid du Colombier IPICMP *p = (IPICMP *)(bp->rp);
3283ff48bf5SDavid du Colombier
3293ff48bf5SDavid du Colombier ipmove(addr, p->src);
3307366567fSDavid du Colombier if(!isv6mcast(p->dst))
3313ff48bf5SDavid du Colombier ipmove(p->src, p->dst);
3327366567fSDavid du Colombier else if (!ipv6anylocal(ifc, p->src))
3337366567fSDavid du Colombier return nil;
3343ff48bf5SDavid du Colombier ipmove(p->dst, addr);
3353ff48bf5SDavid du Colombier p->type = EchoReplyV6;
3363ff48bf5SDavid du Colombier set_cksum(bp);
3373ff48bf5SDavid du Colombier return bp;
3383ff48bf5SDavid du Colombier }
3393ff48bf5SDavid du Colombier
3403ff48bf5SDavid du Colombier /*
3413ff48bf5SDavid du Colombier * sends out an ICMPv6 neighbor solicitation
3423ff48bf5SDavid du Colombier * suni == SRC_UNSPEC or SRC_UNI,
3433ff48bf5SDavid du Colombier * tuni == TARG_MULTI => multicast for address resolution,
344f0ed0fb6SDavid du Colombier * and tuni == TARG_UNI => neighbor reachability.
3453ff48bf5SDavid du Colombier */
3463ff48bf5SDavid du Colombier extern void
icmpns(Fs * f,uchar * src,int suni,uchar * targ,int tuni,uchar * mac)3473ff48bf5SDavid du Colombier icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac)
3483ff48bf5SDavid du Colombier {
3493ff48bf5SDavid du Colombier Block *nbp;
3503ff48bf5SDavid du Colombier Ndpkt *np;
3513ff48bf5SDavid du Colombier Proto *icmp = f->t2p[ICMPv6];
3523ff48bf5SDavid du Colombier Icmppriv6 *ipriv = icmp->priv;
3533ff48bf5SDavid du Colombier
354410ea80bSDavid du Colombier nbp = newIPICMP(NDPKTSZ);
3553ff48bf5SDavid du Colombier np = (Ndpkt*) nbp->rp;
3563ff48bf5SDavid du Colombier
3573ff48bf5SDavid du Colombier if(suni == SRC_UNSPEC)
3583ff48bf5SDavid du Colombier memmove(np->src, v6Unspecified, IPaddrlen);
3593ff48bf5SDavid du Colombier else
3603ff48bf5SDavid du Colombier memmove(np->src, src, IPaddrlen);
3613ff48bf5SDavid du Colombier
3623ff48bf5SDavid du Colombier if(tuni == TARG_UNI)
3633ff48bf5SDavid du Colombier memmove(np->dst, targ, IPaddrlen);
3643ff48bf5SDavid du Colombier else
3653ff48bf5SDavid du Colombier ipv62smcast(np->dst, targ);
3663ff48bf5SDavid du Colombier
3673ff48bf5SDavid du Colombier np->type = NbrSolicit;
3683ff48bf5SDavid du Colombier np->code = 0;
3693ff48bf5SDavid du Colombier memmove(np->target, targ, IPaddrlen);
3703ff48bf5SDavid du Colombier if(suni != SRC_UNSPEC) {
371ccf6439bSDavid du Colombier np->otype = SRC_LLADDR;
3723ff48bf5SDavid du Colombier np->olen = 1; /* 1+1+6 = 8 = 1 8-octet */
3733ff48bf5SDavid du Colombier memmove(np->lnaddr, mac, sizeof(np->lnaddr));
374ccf6439bSDavid du Colombier } else
375410ea80bSDavid du Colombier nbp->wp -= NDPKTSZ - NDISCSZ;
3763ff48bf5SDavid du Colombier
3773ff48bf5SDavid du Colombier set_cksum(nbp);
3783ff48bf5SDavid du Colombier np = (Ndpkt*)nbp->rp;
3793ff48bf5SDavid du Colombier np->ttl = HOP_LIMIT;
3803ff48bf5SDavid du Colombier np->vcf[0] = 0x06 << 4;
3813ff48bf5SDavid du Colombier ipriv->out[NbrSolicit]++;
3823ff48bf5SDavid du Colombier netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ);
383a6a9e072SDavid du Colombier ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
3843ff48bf5SDavid du Colombier }
3853ff48bf5SDavid du Colombier
3863ff48bf5SDavid du Colombier /*
3873ff48bf5SDavid du Colombier * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
3883ff48bf5SDavid du Colombier */
3893ff48bf5SDavid du Colombier extern void
icmpna(Fs * f,uchar * src,uchar * dst,uchar * targ,uchar * mac,uchar flags)3903ff48bf5SDavid du Colombier icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags)
3913ff48bf5SDavid du Colombier {
3923ff48bf5SDavid du Colombier Block *nbp;
3933ff48bf5SDavid du Colombier Ndpkt *np;
3943ff48bf5SDavid du Colombier Proto *icmp = f->t2p[ICMPv6];
3953ff48bf5SDavid du Colombier Icmppriv6 *ipriv = icmp->priv;
3963ff48bf5SDavid du Colombier
397410ea80bSDavid du Colombier nbp = newIPICMP(NDPKTSZ);
3983ff48bf5SDavid du Colombier np = (Ndpkt*)nbp->rp;
3993ff48bf5SDavid du Colombier
4003ff48bf5SDavid du Colombier memmove(np->src, src, IPaddrlen);
4013ff48bf5SDavid du Colombier memmove(np->dst, dst, IPaddrlen);
4023ff48bf5SDavid du Colombier
4033ff48bf5SDavid du Colombier np->type = NbrAdvert;
4043ff48bf5SDavid du Colombier np->code = 0;
4053ff48bf5SDavid du Colombier np->icmpid[0] = flags;
4063ff48bf5SDavid du Colombier memmove(np->target, targ, IPaddrlen);
4073ff48bf5SDavid du Colombier
408ccf6439bSDavid du Colombier np->otype = TARGET_LLADDR;
4093ff48bf5SDavid du Colombier np->olen = 1;
4103ff48bf5SDavid du Colombier memmove(np->lnaddr, mac, sizeof(np->lnaddr));
4113ff48bf5SDavid du Colombier
4123ff48bf5SDavid du Colombier set_cksum(nbp);
4133ff48bf5SDavid du Colombier np = (Ndpkt*) nbp->rp;
4143ff48bf5SDavid du Colombier np->ttl = HOP_LIMIT;
4153ff48bf5SDavid du Colombier np->vcf[0] = 0x06 << 4;
4163ff48bf5SDavid du Colombier ipriv->out[NbrAdvert]++;
4173ff48bf5SDavid du Colombier netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
418a6a9e072SDavid du Colombier ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
4193ff48bf5SDavid du Colombier }
4203ff48bf5SDavid du Colombier
4216083aa43SDavid du Colombier /* if free is true, freeblist(bp) before return. */
4223ff48bf5SDavid du Colombier extern void
icmphostunr(Fs * f,Ipifc * ifc,Block * bp,int code,int free)4233ff48bf5SDavid du Colombier icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free)
4243ff48bf5SDavid du Colombier {
4256083aa43SDavid du Colombier int osz, sz;
4263ff48bf5SDavid du Colombier Block *nbp;
4273ff48bf5SDavid du Colombier IPICMP *np;
4286083aa43SDavid du Colombier Icmppriv6 *ipriv;
4293ff48bf5SDavid du Colombier Ip6hdr *p;
4306083aa43SDavid du Colombier Proto *icmp;
4313ff48bf5SDavid du Colombier
4326083aa43SDavid du Colombier osz = BLEN(bp);
4336083aa43SDavid du Colombier sz = MIN(IPICMPSZ + osz, v6MINTU);
4346083aa43SDavid du Colombier icmp = f->t2p[ICMPv6];
4356083aa43SDavid du Colombier ipriv = icmp->priv;
4363ff48bf5SDavid du Colombier p = (Ip6hdr *)bp->rp;
4373ff48bf5SDavid du Colombier if(isv6mcast(p->src))
4386083aa43SDavid du Colombier goto freebl;
4393ff48bf5SDavid du Colombier nbp = newIPICMP(sz);
4403ff48bf5SDavid du Colombier np = (IPICMP *)nbp->rp;
4413ff48bf5SDavid du Colombier
4423ff48bf5SDavid du Colombier rlock(ifc);
4436083aa43SDavid du Colombier if(!ipv6anylocal(ifc, np->src)){
4447133e0eeSDavid du Colombier netlog(f, Logicmp, "icmphostunr fail -> src %I dst %I\n",
445ccf6439bSDavid du Colombier p->src, p->dst);
4466083aa43SDavid du Colombier runlock(ifc);
4473ff48bf5SDavid du Colombier freeblist(nbp);
4486083aa43SDavid du Colombier goto freebl;
4493ff48bf5SDavid du Colombier }
4503ff48bf5SDavid du Colombier
4516083aa43SDavid du Colombier netlog(f, Logicmp, "send icmphostunr -> src %I dst %I\n", p->src, p->dst);
4523ff48bf5SDavid du Colombier memmove(np->dst, p->src, IPaddrlen);
4533ff48bf5SDavid du Colombier np->type = UnreachableV6;
4543ff48bf5SDavid du Colombier np->code = code;
455410ea80bSDavid du Colombier memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
4563ff48bf5SDavid du Colombier set_cksum(nbp);
4573ff48bf5SDavid du Colombier np->ttl = HOP_LIMIT;
4583ff48bf5SDavid du Colombier np->vcf[0] = 0x06 << 4;
4593ff48bf5SDavid du Colombier ipriv->out[UnreachableV6]++;
4603ff48bf5SDavid du Colombier
4613ff48bf5SDavid du Colombier if(free)
4623ff48bf5SDavid du Colombier ipiput6(f, ifc, nbp);
4636083aa43SDavid du Colombier else
464a6a9e072SDavid du Colombier ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
4653ff48bf5SDavid du Colombier runlock(ifc);
4666083aa43SDavid du Colombier freebl:
4676083aa43SDavid du Colombier if(free)
4683ff48bf5SDavid du Colombier freeblist(bp);
4693ff48bf5SDavid du Colombier }
4703ff48bf5SDavid du Colombier
4713ff48bf5SDavid du Colombier extern void
icmpttlexceeded6(Fs * f,Ipifc * ifc,Block * bp)4723ff48bf5SDavid du Colombier icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp)
4733ff48bf5SDavid du Colombier {
474ccf6439bSDavid du Colombier int osz = BLEN(bp);
475410ea80bSDavid du Colombier int sz = MIN(IPICMPSZ + osz, v6MINTU);
4763ff48bf5SDavid du Colombier Block *nbp;
4773ff48bf5SDavid du Colombier IPICMP *np;
4783ff48bf5SDavid du Colombier Ip6hdr *p;
4793ff48bf5SDavid du Colombier Proto *icmp = f->t2p[ICMPv6];
4803ff48bf5SDavid du Colombier Icmppriv6 *ipriv = icmp->priv;
4813ff48bf5SDavid du Colombier
4823ff48bf5SDavid du Colombier p = (Ip6hdr *)bp->rp;
4833ff48bf5SDavid du Colombier if(isv6mcast(p->src))
4843ff48bf5SDavid du Colombier return;
4853ff48bf5SDavid du Colombier
4863ff48bf5SDavid du Colombier nbp = newIPICMP(sz);
4873ff48bf5SDavid du Colombier np = (IPICMP *) nbp->rp;
488ccf6439bSDavid du Colombier if(ipv6anylocal(ifc, np->src))
4897133e0eeSDavid du Colombier netlog(f, Logicmp, "send icmpttlexceeded6 -> src %I dst %I\n",
490ccf6439bSDavid du Colombier p->src, p->dst);
4913ff48bf5SDavid du Colombier else {
4927133e0eeSDavid du Colombier netlog(f, Logicmp, "icmpttlexceeded6 fail -> src %I dst %I\n",
493ccf6439bSDavid du Colombier p->src, p->dst);
4943ff48bf5SDavid du Colombier return;
4953ff48bf5SDavid du Colombier }
4963ff48bf5SDavid du Colombier
4973ff48bf5SDavid du Colombier memmove(np->dst, p->src, IPaddrlen);
4983ff48bf5SDavid du Colombier np->type = TimeExceedV6;
4993ff48bf5SDavid du Colombier np->code = 0;
500410ea80bSDavid du Colombier memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
5013ff48bf5SDavid du Colombier set_cksum(nbp);
5023ff48bf5SDavid du Colombier np->ttl = HOP_LIMIT;
5033ff48bf5SDavid du Colombier np->vcf[0] = 0x06 << 4;
5043ff48bf5SDavid du Colombier ipriv->out[TimeExceedV6]++;
505a6a9e072SDavid du Colombier ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
5063ff48bf5SDavid du Colombier }
5073ff48bf5SDavid du Colombier
5083ff48bf5SDavid du Colombier extern void
icmppkttoobig6(Fs * f,Ipifc * ifc,Block * bp)5093ff48bf5SDavid du Colombier icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp)
5103ff48bf5SDavid du Colombier {
511ccf6439bSDavid du Colombier int osz = BLEN(bp);
512410ea80bSDavid du Colombier int sz = MIN(IPICMPSZ + osz, v6MINTU);
5133ff48bf5SDavid du Colombier Block *nbp;
5143ff48bf5SDavid du Colombier IPICMP *np;
5153ff48bf5SDavid du Colombier Ip6hdr *p;
5163ff48bf5SDavid du Colombier Proto *icmp = f->t2p[ICMPv6];
5173ff48bf5SDavid du Colombier Icmppriv6 *ipriv = icmp->priv;
5183ff48bf5SDavid du Colombier
5193ff48bf5SDavid du Colombier p = (Ip6hdr *)bp->rp;
5203ff48bf5SDavid du Colombier if(isv6mcast(p->src))
5213ff48bf5SDavid du Colombier return;
5223ff48bf5SDavid du Colombier
5233ff48bf5SDavid du Colombier nbp = newIPICMP(sz);
5243ff48bf5SDavid du Colombier np = (IPICMP *)nbp->rp;
525ccf6439bSDavid du Colombier if(ipv6anylocal(ifc, np->src))
5267133e0eeSDavid du Colombier netlog(f, Logicmp, "send icmppkttoobig6 -> src %I dst %I\n",
527ccf6439bSDavid du Colombier p->src, p->dst);
5283ff48bf5SDavid du Colombier else {
5297133e0eeSDavid du Colombier netlog(f, Logicmp, "icmppkttoobig6 fail -> src %I dst %I\n",
530ccf6439bSDavid du Colombier p->src, p->dst);
5313ff48bf5SDavid du Colombier return;
5323ff48bf5SDavid du Colombier }
5333ff48bf5SDavid du Colombier
5343ff48bf5SDavid du Colombier memmove(np->dst, p->src, IPaddrlen);
5353ff48bf5SDavid du Colombier np->type = PacketTooBigV6;
5363ff48bf5SDavid du Colombier np->code = 0;
5373f695129SDavid du Colombier hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize);
538410ea80bSDavid du Colombier memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
5393ff48bf5SDavid du Colombier set_cksum(nbp);
5403ff48bf5SDavid du Colombier np->ttl = HOP_LIMIT;
5413ff48bf5SDavid du Colombier np->vcf[0] = 0x06 << 4;
5423ff48bf5SDavid du Colombier ipriv->out[PacketTooBigV6]++;
543a6a9e072SDavid du Colombier ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
5443ff48bf5SDavid du Colombier }
5453ff48bf5SDavid du Colombier
5463ff48bf5SDavid du Colombier /*
5473ff48bf5SDavid du Colombier * RFC 2461, pages 39-40, pages 57-58.
5483ff48bf5SDavid du Colombier */
5493ff48bf5SDavid du Colombier static int
valid(Proto * icmp,Ipifc *,Block * bp,Icmppriv6 * ipriv)5506083aa43SDavid du Colombier valid(Proto *icmp, Ipifc *, Block *bp, Icmppriv6 *ipriv)
551ccf6439bSDavid du Colombier {
5526083aa43SDavid du Colombier int sz, osz, unsp, n, ttl, iplen, pktsz;
5536083aa43SDavid du Colombier uchar *packet;
5546083aa43SDavid du Colombier IPICMP *p;
5553ff48bf5SDavid du Colombier Ndpkt *np;
5563ff48bf5SDavid du Colombier
5573ff48bf5SDavid du Colombier n = blocklen(bp);
558410ea80bSDavid du Colombier if(n < IPICMPSZ) {
5593ff48bf5SDavid du Colombier ipriv->stats[HlenErrs6]++;
5603ff48bf5SDavid du Colombier netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
5613ff48bf5SDavid du Colombier goto err;
5623ff48bf5SDavid du Colombier }
5633ff48bf5SDavid du Colombier
5646083aa43SDavid du Colombier packet = bp->rp;
5656083aa43SDavid du Colombier p = (IPICMP *)packet;
5666083aa43SDavid du Colombier pktsz = BLEN(bp);
5673ff48bf5SDavid du Colombier iplen = nhgets(p->ploadlen);
5685acbe002SDavid du Colombier if(iplen > n - IP6HDR) {
5693ff48bf5SDavid du Colombier ipriv->stats[LenErrs6]++;
5703ff48bf5SDavid du Colombier netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
5713ff48bf5SDavid du Colombier goto err;
5723ff48bf5SDavid du Colombier }
5733ff48bf5SDavid du Colombier
574ccf6439bSDavid du Colombier /* Rather than construct explicit pseudoheader, overwrite IPv6 header */
5753ff48bf5SDavid du Colombier if(p->proto != ICMPv6) {
576ccf6439bSDavid du Colombier /* This code assumes no extension headers!!! */
5773ff48bf5SDavid du Colombier netlog(icmp->f, Logicmp, "icmp error: extension header\n");
5783ff48bf5SDavid du Colombier goto err;
5793ff48bf5SDavid du Colombier }
5803ff48bf5SDavid du Colombier memset(packet, 0, 4);
5813ff48bf5SDavid du Colombier ttl = p->ttl;
5823ff48bf5SDavid du Colombier p->ttl = p->proto;
5833ff48bf5SDavid du Colombier p->proto = 0;
5840774058cSDavid du Colombier if(ptclcsum(bp, 0, iplen + IP6HDR)) {
5853ff48bf5SDavid du Colombier ipriv->stats[CsumErrs6]++;
5863ff48bf5SDavid du Colombier netlog(icmp->f, Logicmp, "icmp checksum error\n");
5873ff48bf5SDavid du Colombier goto err;
5883ff48bf5SDavid du Colombier }
5893ff48bf5SDavid du Colombier p->proto = p->ttl;
5903ff48bf5SDavid du Colombier p->ttl = ttl;
5913ff48bf5SDavid du Colombier
5923ff48bf5SDavid du Colombier /* additional tests for some pkt types */
5936083aa43SDavid du Colombier if (p->type != NbrSolicit && p->type != NbrAdvert &&
5946083aa43SDavid du Colombier p->type != RouterAdvert && p->type != RouterSolicit &&
5956083aa43SDavid du Colombier p->type != RedirectV6)
5966083aa43SDavid du Colombier return 1; /* TODO: unknown, presumed valid; why? */
5973ff48bf5SDavid du Colombier if(p->ttl != HOP_LIMIT) {
5983ff48bf5SDavid du Colombier ipriv->stats[HoplimErrs6]++;
5993ff48bf5SDavid du Colombier goto err;
6003ff48bf5SDavid du Colombier }
6013ff48bf5SDavid du Colombier if(p->code != 0) {
6023ff48bf5SDavid du Colombier ipriv->stats[IcmpCodeErrs6]++;
6033ff48bf5SDavid du Colombier goto err;
6043ff48bf5SDavid du Colombier }
6053ff48bf5SDavid du Colombier
6063ff48bf5SDavid du Colombier switch (p->type) {
6073ff48bf5SDavid du Colombier case NbrSolicit:
6083ff48bf5SDavid du Colombier case NbrAdvert:
6093ff48bf5SDavid du Colombier np = (Ndpkt*) p;
6103ff48bf5SDavid du Colombier if(isv6mcast(np->target)) {
6113ff48bf5SDavid du Colombier ipriv->stats[TargetErrs6]++;
6123ff48bf5SDavid du Colombier goto err;
6133ff48bf5SDavid du Colombier }
614ccf6439bSDavid du Colombier if(optexsts(np) && np->olen == 0) {
6153ff48bf5SDavid du Colombier ipriv->stats[OptlenErrs6]++;
6163ff48bf5SDavid du Colombier goto err;
6173ff48bf5SDavid du Colombier }
6186083aa43SDavid du Colombier if (p->type == NbrSolicit && ipcmp(np->src, v6Unspecified) == 0)
6193ff48bf5SDavid du Colombier if(!issmcast(np->dst) || optexsts(np)) {
6203ff48bf5SDavid du Colombier ipriv->stats[AddrmxpErrs6]++;
6213ff48bf5SDavid du Colombier goto err;
6223ff48bf5SDavid du Colombier }
6236083aa43SDavid du Colombier if(p->type == NbrAdvert && isv6mcast(np->dst) &&
6246083aa43SDavid du Colombier nhgets(np->icmpid) & Sflag){
6253ff48bf5SDavid du Colombier ipriv->stats[AddrmxpErrs6]++;
6263ff48bf5SDavid du Colombier goto err;
6273ff48bf5SDavid du Colombier }
6283ff48bf5SDavid du Colombier break;
6293ff48bf5SDavid du Colombier case RouterAdvert:
630410ea80bSDavid du Colombier if(pktsz - IP6HDR < 16) {
6313ff48bf5SDavid du Colombier ipriv->stats[HlenErrs6]++;
6323ff48bf5SDavid du Colombier goto err;
6333ff48bf5SDavid du Colombier }
6343ff48bf5SDavid du Colombier if(!islinklocal(p->src)) {
6353ff48bf5SDavid du Colombier ipriv->stats[RouterAddrErrs6]++;
6363ff48bf5SDavid du Colombier goto err;
6373ff48bf5SDavid du Colombier }
6386083aa43SDavid du Colombier for (sz = IPICMPSZ + 8; sz+1 < pktsz; sz += 8*osz) {
639ccf6439bSDavid du Colombier osz = packet[sz+1];
6403ff48bf5SDavid du Colombier if(osz <= 0) {
6413ff48bf5SDavid du Colombier ipriv->stats[OptlenErrs6]++;
6423ff48bf5SDavid du Colombier goto err;
6433ff48bf5SDavid du Colombier }
6443ff48bf5SDavid du Colombier }
6453ff48bf5SDavid du Colombier break;
6463ff48bf5SDavid du Colombier case RouterSolicit:
647410ea80bSDavid du Colombier if(pktsz - IP6HDR < 8) {
6483ff48bf5SDavid du Colombier ipriv->stats[HlenErrs6]++;
6493ff48bf5SDavid du Colombier goto err;
6503ff48bf5SDavid du Colombier }
6513ff48bf5SDavid du Colombier unsp = (ipcmp(p->src, v6Unspecified) == 0);
6526083aa43SDavid du Colombier for (sz = IPICMPSZ + 8; sz+1 < pktsz; sz += 8*osz) {
653ccf6439bSDavid du Colombier osz = packet[sz+1];
6546083aa43SDavid du Colombier if(osz <= 0 || (unsp && packet[sz] == SRC_LLADDR)) {
6553ff48bf5SDavid du Colombier ipriv->stats[OptlenErrs6]++;
6563ff48bf5SDavid du Colombier goto err;
6573ff48bf5SDavid du Colombier }
6583ff48bf5SDavid du Colombier }
6593ff48bf5SDavid du Colombier break;
6603ff48bf5SDavid du Colombier case RedirectV6:
6616083aa43SDavid du Colombier /* TODO: fill in */
6623ff48bf5SDavid du Colombier break;
6633ff48bf5SDavid du Colombier default:
6643ff48bf5SDavid du Colombier goto err;
6653ff48bf5SDavid du Colombier }
6663ff48bf5SDavid du Colombier return 1;
6673ff48bf5SDavid du Colombier err:
6683ff48bf5SDavid du Colombier ipriv->stats[InErrors6]++;
6693ff48bf5SDavid du Colombier return 0;
6703ff48bf5SDavid du Colombier }
6713ff48bf5SDavid du Colombier
6723ff48bf5SDavid du Colombier static int
targettype(Fs * f,Ipifc * ifc,uchar * target)6733ff48bf5SDavid du Colombier targettype(Fs *f, Ipifc *ifc, uchar *target)
6743ff48bf5SDavid du Colombier {
6753ff48bf5SDavid du Colombier Iplifc *lifc;
6763ff48bf5SDavid du Colombier int t;
6773ff48bf5SDavid du Colombier
6783ff48bf5SDavid du Colombier rlock(ifc);
6793ff48bf5SDavid du Colombier if(ipproxyifc(f, ifc, target)) {
6803ff48bf5SDavid du Colombier runlock(ifc);
681f2c197d9SDavid du Colombier return Tuniproxy;
6823ff48bf5SDavid du Colombier }
6833ff48bf5SDavid du Colombier
684ccf6439bSDavid du Colombier for(lifc = ifc->lifc; lifc; lifc = lifc->next)
6853ff48bf5SDavid du Colombier if(ipcmp(lifc->local, target) == 0) {
686f2c197d9SDavid du Colombier t = (lifc->tentative)? Tunitent: Tunirany;
6873ff48bf5SDavid du Colombier runlock(ifc);
6883ff48bf5SDavid du Colombier return t;
6893ff48bf5SDavid du Colombier }
6903ff48bf5SDavid du Colombier
6913ff48bf5SDavid du Colombier runlock(ifc);
6923ff48bf5SDavid du Colombier return 0;
6933ff48bf5SDavid du Colombier }
6943ff48bf5SDavid du Colombier
6956083aa43SDavid du Colombier /* bp needs to be freed with freeblist or passed on. */
6963ff48bf5SDavid du Colombier static void
icmpiput6(Proto * icmp,Ipifc * ipifc,Block * bp)6973ff48bf5SDavid du Colombier icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp)
6983ff48bf5SDavid du Colombier {
6996083aa43SDavid du Colombier int type;
700ccf6439bSDavid du Colombier char *msg, m2[128];
701ccf6439bSDavid du Colombier uchar pktflags;
7026083aa43SDavid du Colombier uchar *packet, *src;
703ccf6439bSDavid du Colombier uchar lsrc[IPaddrlen];
704ccf6439bSDavid du Colombier Block *r;
7056083aa43SDavid du Colombier IPICMP *p;
7066083aa43SDavid du Colombier Icmppriv6 *ipriv;
7073ff48bf5SDavid du Colombier Iplifc *lifc;
708ccf6439bSDavid du Colombier Ndpkt* np;
709ccf6439bSDavid du Colombier Proto *pr;
7103ff48bf5SDavid du Colombier
7116083aa43SDavid du Colombier packet = bp->rp;
7126083aa43SDavid du Colombier p = (IPICMP *)packet;
7136083aa43SDavid du Colombier type = p->type;
7146083aa43SDavid du Colombier ipriv = icmp->priv;
7156083aa43SDavid du Colombier if(!valid(icmp, ipifc, bp, ipriv) || type > Maxtype6)
7163ff48bf5SDavid du Colombier goto raise;
7173ff48bf5SDavid du Colombier
7186083aa43SDavid du Colombier ipriv->in[type]++;
7196083aa43SDavid du Colombier switch(type) {
7203ff48bf5SDavid du Colombier case EchoRequestV6:
721*58da3067SDavid du Colombier bp = concatblock(bp);
7227366567fSDavid du Colombier r = mkechoreply6(bp, ipifc);
7237366567fSDavid du Colombier if(r == nil)
7247366567fSDavid du Colombier goto raise;
7253ff48bf5SDavid du Colombier ipriv->out[EchoReply]++;
726a6a9e072SDavid du Colombier ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
7273ff48bf5SDavid du Colombier break;
7283ff48bf5SDavid du Colombier case UnreachableV6:
729f2c197d9SDavid du Colombier if(p->code >= nelem(unreachcode))
730f2c197d9SDavid du Colombier msg = unreachcode[Icmp6_unknown];
7313ff48bf5SDavid du Colombier else
7323ff48bf5SDavid du Colombier msg = unreachcode[p->code];
7333ff48bf5SDavid du Colombier
734410ea80bSDavid du Colombier bp->rp += IPICMPSZ;
7353ff48bf5SDavid du Colombier if(blocklen(bp) < 8){
7363ff48bf5SDavid du Colombier ipriv->stats[LenErrs6]++;
7373ff48bf5SDavid du Colombier goto raise;
7383ff48bf5SDavid du Colombier }
7393ff48bf5SDavid du Colombier p = (IPICMP *)bp->rp;
7403ff48bf5SDavid du Colombier pr = Fsrcvpcolx(icmp->f, p->proto);
7413ff48bf5SDavid du Colombier if(pr != nil && pr->advise != nil) {
7423ff48bf5SDavid du Colombier (*pr->advise)(pr, bp, msg);
7433ff48bf5SDavid du Colombier return;
7443ff48bf5SDavid du Colombier }
7453ff48bf5SDavid du Colombier
746410ea80bSDavid du Colombier bp->rp -= IPICMPSZ;
7473ff48bf5SDavid du Colombier goticmpkt6(icmp, bp, 0);
7483ff48bf5SDavid du Colombier break;
7493ff48bf5SDavid du Colombier case TimeExceedV6:
7503ff48bf5SDavid du Colombier if(p->code == 0){
7514e3613abSDavid du Colombier snprint(m2, sizeof m2, "ttl exceeded at %I", p->src);
752410ea80bSDavid du Colombier bp->rp += IPICMPSZ;
7533ff48bf5SDavid du Colombier if(blocklen(bp) < 8){
7543ff48bf5SDavid du Colombier ipriv->stats[LenErrs6]++;
7553ff48bf5SDavid du Colombier goto raise;
7563ff48bf5SDavid du Colombier }
7573ff48bf5SDavid du Colombier p = (IPICMP *)bp->rp;
7583ff48bf5SDavid du Colombier pr = Fsrcvpcolx(icmp->f, p->proto);
759107aedb4SDavid du Colombier if(pr && pr->advise) {
7603ff48bf5SDavid du Colombier (*pr->advise)(pr, bp, m2);
7613ff48bf5SDavid du Colombier return;
7623ff48bf5SDavid du Colombier }
763410ea80bSDavid du Colombier bp->rp -= IPICMPSZ;
7643ff48bf5SDavid du Colombier }
7653ff48bf5SDavid du Colombier goticmpkt6(icmp, bp, 0);
7663ff48bf5SDavid du Colombier break;
7673ff48bf5SDavid du Colombier case RouterAdvert:
7683ff48bf5SDavid du Colombier case RouterSolicit:
769ccf6439bSDavid du Colombier /* using lsrc as a temp, munge hdr for goticmp6 */
770ccf6439bSDavid du Colombier if (0) {
7713ff48bf5SDavid du Colombier memmove(lsrc, p->src, IPaddrlen);
7723ff48bf5SDavid du Colombier memmove(p->src, p->dst, IPaddrlen);
773ccf6439bSDavid du Colombier memmove(p->dst, lsrc, IPaddrlen);
774ccf6439bSDavid du Colombier }
7756083aa43SDavid du Colombier goticmpkt6(icmp, bp, type);
7763ff48bf5SDavid du Colombier break;
7773ff48bf5SDavid du Colombier case NbrSolicit:
7786083aa43SDavid du Colombier np = (Ndpkt*)p; /* within bp */
7793ff48bf5SDavid du Colombier pktflags = 0;
7803ff48bf5SDavid du Colombier switch (targettype(icmp->f, ipifc, np->target)) {
781f2c197d9SDavid du Colombier case Tunirany:
7823ff48bf5SDavid du Colombier pktflags |= Oflag;
7833ff48bf5SDavid du Colombier /* fall through */
784f2c197d9SDavid du Colombier case Tuniproxy:
7853ff48bf5SDavid du Colombier if(ipcmp(np->src, v6Unspecified) != 0) {
786ccf6439bSDavid du Colombier arpenter(icmp->f, V6, np->src, np->lnaddr,
787ccf6439bSDavid du Colombier 8*np->olen-2, 0);
7883ff48bf5SDavid du Colombier pktflags |= Sflag;
7893ff48bf5SDavid du Colombier }
7906083aa43SDavid du Colombier if(ipv6local(ipifc, lsrc)) {
7916083aa43SDavid du Colombier src = np->src;
7926083aa43SDavid du Colombier if(ipcmp(src, v6Unspecified) == 0)
7936083aa43SDavid du Colombier src = v6allnodesL;
7946083aa43SDavid du Colombier icmpna(icmp->f, lsrc, src, np->target,
7956083aa43SDavid du Colombier ipifc->mac, pktflags);
7963ff48bf5SDavid du Colombier }
7973ff48bf5SDavid du Colombier break;
7986083aa43SDavid du Colombier case Tunitent:
7996083aa43SDavid du Colombier /*
8006083aa43SDavid du Colombier * not clear what needs to be done. send up
8016083aa43SDavid du Colombier * an icmp mesg saying `don't use this address'?
8026083aa43SDavid du Colombier */
8036083aa43SDavid du Colombier break;
8046083aa43SDavid du Colombier }
8056083aa43SDavid du Colombier freeblist(bp);
8066083aa43SDavid du Colombier break;
8073ff48bf5SDavid du Colombier case NbrAdvert:
808ccf6439bSDavid du Colombier /*
809ccf6439bSDavid du Colombier * if the target address matches one of the local interface
810ccf6439bSDavid du Colombier * addresses and the local interface address has tentative bit
811ccf6439bSDavid du Colombier * set, insert into ARP table. this is so the duplicate address
8123ff48bf5SDavid du Colombier * detection part of ipconfig can discover duplication through
813ccf6439bSDavid du Colombier * the arp table.
8143ff48bf5SDavid du Colombier */
8156083aa43SDavid du Colombier np = (Ndpkt*)p; /* within bp */
8163ff48bf5SDavid du Colombier lifc = iplocalonifc(ipifc, np->target);
817ccf6439bSDavid du Colombier arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2,
8186083aa43SDavid du Colombier lifc && lifc->tentative);
8193ff48bf5SDavid du Colombier freeblist(bp);
8203ff48bf5SDavid du Colombier break;
8213ff48bf5SDavid du Colombier case PacketTooBigV6:
8223ff48bf5SDavid du Colombier default:
8233ff48bf5SDavid du Colombier goticmpkt6(icmp, bp, 0);
8243ff48bf5SDavid du Colombier break;
8253ff48bf5SDavid du Colombier }
8263ff48bf5SDavid du Colombier return;
8276083aa43SDavid du Colombier
8283ff48bf5SDavid du Colombier raise:
8293ff48bf5SDavid du Colombier freeblist(bp);
8303ff48bf5SDavid du Colombier }
8313ff48bf5SDavid du Colombier
8323ff48bf5SDavid du Colombier int
icmpstats6(Proto * icmp6,char * buf,int len)8333ff48bf5SDavid du Colombier icmpstats6(Proto *icmp6, char *buf, int len)
8343ff48bf5SDavid du Colombier {
8353ff48bf5SDavid du Colombier Icmppriv6 *priv;
8363ff48bf5SDavid du Colombier char *p, *e;
8373ff48bf5SDavid du Colombier int i;
8383ff48bf5SDavid du Colombier
8393ff48bf5SDavid du Colombier priv = icmp6->priv;
8403ff48bf5SDavid du Colombier p = buf;
8413ff48bf5SDavid du Colombier e = p+len;
8423ff48bf5SDavid du Colombier for(i = 0; i < Nstats6; i++)
8433ff48bf5SDavid du Colombier p = seprint(p, e, "%s: %lud\n", statnames6[i], priv->stats[i]);
844ccf6439bSDavid du Colombier for(i = 0; i <= Maxtype6; i++)
8453ff48bf5SDavid du Colombier if(icmpnames6[i])
846ccf6439bSDavid du Colombier p = seprint(p, e, "%s: %lud %lud\n", icmpnames6[i],
847ccf6439bSDavid du Colombier priv->in[i], priv->out[i]);
8486083aa43SDavid du Colombier else if (0)
849ccf6439bSDavid du Colombier p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i],
850ccf6439bSDavid du Colombier priv->out[i]);
8513ff48bf5SDavid du Colombier return p - buf;
8523ff48bf5SDavid du Colombier }
8533ff48bf5SDavid du Colombier
854ccf6439bSDavid du Colombier /* import from icmp.c */
8553ff48bf5SDavid du Colombier extern int icmpstate(Conv *c, char *state, int n);
8563ff48bf5SDavid du Colombier extern char* icmpannounce(Conv *c, char **argv, int argc);
8573ff48bf5SDavid du Colombier extern char* icmpconnect(Conv *c, char **argv, int argc);
8583ff48bf5SDavid du Colombier extern void icmpclose(Conv *c);
8593ff48bf5SDavid du Colombier
8603ff48bf5SDavid du Colombier void
icmp6init(Fs * fs)8613ff48bf5SDavid du Colombier icmp6init(Fs *fs)
8623ff48bf5SDavid du Colombier {
8633ff48bf5SDavid du Colombier Proto *icmp6 = smalloc(sizeof(Proto));
8643ff48bf5SDavid du Colombier
8653ff48bf5SDavid du Colombier icmp6->priv = smalloc(sizeof(Icmppriv6));
8663ff48bf5SDavid du Colombier icmp6->name = "icmpv6";
8673ff48bf5SDavid du Colombier icmp6->connect = icmpconnect;
8683ff48bf5SDavid du Colombier icmp6->announce = icmpannounce;
8693ff48bf5SDavid du Colombier icmp6->state = icmpstate;
8703ff48bf5SDavid du Colombier icmp6->create = icmpcreate6;
8713ff48bf5SDavid du Colombier icmp6->close = icmpclose;
8723ff48bf5SDavid du Colombier icmp6->rcv = icmpiput6;
8733ff48bf5SDavid du Colombier icmp6->stats = icmpstats6;
8743ff48bf5SDavid du Colombier icmp6->ctl = icmpctl6;
8753ff48bf5SDavid du Colombier icmp6->advise = icmpadvise6;
8763ff48bf5SDavid du Colombier icmp6->gc = nil;
8773ff48bf5SDavid du Colombier icmp6->ipproto = ICMPv6;
8783ff48bf5SDavid du Colombier icmp6->nc = 16;
8793ff48bf5SDavid du Colombier icmp6->ptclsize = sizeof(Icmpcb6);
8803ff48bf5SDavid du Colombier
8813ff48bf5SDavid du Colombier Fsproto(fs, icmp6);
8823ff48bf5SDavid du Colombier }
883