19ef1f84bSDavid du Colombier /*
29ef1f84bSDavid du Colombier * Internet Control Message Protocol for IPv6
39ef1f84bSDavid du Colombier */
49ef1f84bSDavid du Colombier #include "u.h"
59ef1f84bSDavid du Colombier #include "../port/lib.h"
69ef1f84bSDavid du Colombier #include "mem.h"
79ef1f84bSDavid du Colombier #include "dat.h"
89ef1f84bSDavid du Colombier #include "fns.h"
99ef1f84bSDavid du Colombier #include "../port/error.h"
109ef1f84bSDavid du Colombier #include "ip.h"
119ef1f84bSDavid du Colombier #include "ipv6.h"
129ef1f84bSDavid du Colombier
139ef1f84bSDavid du Colombier enum
149ef1f84bSDavid du Colombier {
159ef1f84bSDavid du Colombier InMsgs6,
169ef1f84bSDavid du Colombier InErrors6,
179ef1f84bSDavid du Colombier OutMsgs6,
189ef1f84bSDavid du Colombier CsumErrs6,
199ef1f84bSDavid du Colombier LenErrs6,
209ef1f84bSDavid du Colombier HlenErrs6,
219ef1f84bSDavid du Colombier HoplimErrs6,
229ef1f84bSDavid du Colombier IcmpCodeErrs6,
239ef1f84bSDavid du Colombier TargetErrs6,
249ef1f84bSDavid du Colombier OptlenErrs6,
259ef1f84bSDavid du Colombier AddrmxpErrs6,
269ef1f84bSDavid du Colombier RouterAddrErrs6,
279ef1f84bSDavid du Colombier
289ef1f84bSDavid du Colombier Nstats6,
299ef1f84bSDavid du Colombier };
309ef1f84bSDavid du Colombier
319ef1f84bSDavid du Colombier enum {
329ef1f84bSDavid du Colombier ICMP_USEAD6 = 40,
339ef1f84bSDavid du Colombier };
349ef1f84bSDavid du Colombier
359ef1f84bSDavid du Colombier enum {
369ef1f84bSDavid du Colombier Oflag = 1<<5,
379ef1f84bSDavid du Colombier Sflag = 1<<6,
389ef1f84bSDavid du Colombier Rflag = 1<<7,
399ef1f84bSDavid du Colombier };
409ef1f84bSDavid du Colombier
419ef1f84bSDavid du Colombier enum {
429ef1f84bSDavid du Colombier /* ICMPv6 types */
439ef1f84bSDavid du Colombier EchoReply = 0,
449ef1f84bSDavid du Colombier UnreachableV6 = 1,
459ef1f84bSDavid du Colombier PacketTooBigV6 = 2,
469ef1f84bSDavid du Colombier TimeExceedV6 = 3,
479ef1f84bSDavid du Colombier SrcQuench = 4,
489ef1f84bSDavid du Colombier ParamProblemV6 = 4,
499ef1f84bSDavid du Colombier Redirect = 5,
509ef1f84bSDavid du Colombier EchoRequest = 8,
519ef1f84bSDavid du Colombier TimeExceed = 11,
529ef1f84bSDavid du Colombier InParmProblem = 12,
539ef1f84bSDavid du Colombier Timestamp = 13,
549ef1f84bSDavid du Colombier TimestampReply = 14,
559ef1f84bSDavid du Colombier InfoRequest = 15,
569ef1f84bSDavid du Colombier InfoReply = 16,
579ef1f84bSDavid du Colombier AddrMaskRequest = 17,
589ef1f84bSDavid du Colombier AddrMaskReply = 18,
599ef1f84bSDavid du Colombier EchoRequestV6 = 128,
609ef1f84bSDavid du Colombier EchoReplyV6 = 129,
619ef1f84bSDavid du Colombier RouterSolicit = 133,
629ef1f84bSDavid du Colombier RouterAdvert = 134,
639ef1f84bSDavid du Colombier NbrSolicit = 135,
649ef1f84bSDavid du Colombier NbrAdvert = 136,
659ef1f84bSDavid du Colombier RedirectV6 = 137,
669ef1f84bSDavid du Colombier
679ef1f84bSDavid du Colombier Maxtype6 = 137,
689ef1f84bSDavid du Colombier };
699ef1f84bSDavid du Colombier
709ef1f84bSDavid du Colombier /* on-the-wire packet formats */
719ef1f84bSDavid du Colombier typedef struct IPICMP IPICMP;
729ef1f84bSDavid du Colombier typedef struct Ndpkt Ndpkt;
739ef1f84bSDavid du Colombier typedef struct NdiscC NdiscC;
749ef1f84bSDavid du Colombier
759ef1f84bSDavid du Colombier /* we do this to avoid possible struct padding */
769ef1f84bSDavid du Colombier #define ICMPHDR \
779ef1f84bSDavid du Colombier IPV6HDR; \
789ef1f84bSDavid du Colombier uchar type; \
799ef1f84bSDavid du Colombier uchar code; \
809ef1f84bSDavid du Colombier uchar cksum[2]; \
819ef1f84bSDavid du Colombier uchar icmpid[2]; \
829ef1f84bSDavid du Colombier uchar seq[2]
839ef1f84bSDavid du Colombier
849ef1f84bSDavid du Colombier struct IPICMP {
859ef1f84bSDavid du Colombier ICMPHDR;
869ef1f84bSDavid du Colombier uchar payload[];
879ef1f84bSDavid du Colombier };
889ef1f84bSDavid du Colombier
899ef1f84bSDavid du Colombier #define IPICMPSZ offsetof(IPICMP, payload[0])
909ef1f84bSDavid du Colombier
919ef1f84bSDavid du Colombier struct NdiscC {
929ef1f84bSDavid du Colombier ICMPHDR;
939ef1f84bSDavid du Colombier uchar target[IPaddrlen];
949ef1f84bSDavid du Colombier uchar payload[];
959ef1f84bSDavid du Colombier };
969ef1f84bSDavid du Colombier
979ef1f84bSDavid du Colombier #define NDISCSZ offsetof(NdiscC, payload[0])
989ef1f84bSDavid du Colombier
999ef1f84bSDavid du Colombier struct Ndpkt {
1009ef1f84bSDavid du Colombier ICMPHDR;
1019ef1f84bSDavid du Colombier uchar target[IPaddrlen];
1029ef1f84bSDavid du Colombier uchar otype;
1039ef1f84bSDavid du Colombier uchar olen; /* length in units of 8 octets(incl type, code),
1049ef1f84bSDavid du Colombier * 1 for IEEE 802 addresses */
1059ef1f84bSDavid du Colombier uchar lnaddr[6]; /* link-layer address */
1069ef1f84bSDavid du Colombier uchar payload[];
1079ef1f84bSDavid du Colombier };
1089ef1f84bSDavid du Colombier
1099ef1f84bSDavid du Colombier #define NDPKTSZ offsetof(Ndpkt, payload[0])
1109ef1f84bSDavid du Colombier
1119ef1f84bSDavid du Colombier typedef struct Icmppriv6
1129ef1f84bSDavid du Colombier {
1139ef1f84bSDavid du Colombier ulong stats[Nstats6];
1149ef1f84bSDavid du Colombier
1159ef1f84bSDavid du Colombier /* message counts */
1169ef1f84bSDavid du Colombier ulong in[Maxtype6+1];
1179ef1f84bSDavid du Colombier ulong out[Maxtype6+1];
1189ef1f84bSDavid du Colombier } Icmppriv6;
1199ef1f84bSDavid du Colombier
1209ef1f84bSDavid du Colombier typedef struct Icmpcb6
1219ef1f84bSDavid du Colombier {
1229ef1f84bSDavid du Colombier QLock;
1239ef1f84bSDavid du Colombier uchar headers;
1249ef1f84bSDavid du Colombier } Icmpcb6;
1259ef1f84bSDavid du Colombier
1269ef1f84bSDavid du Colombier char *icmpnames6[Maxtype6+1] =
1279ef1f84bSDavid du Colombier {
1289ef1f84bSDavid du Colombier [EchoReply] "EchoReply",
1299ef1f84bSDavid du Colombier [UnreachableV6] "UnreachableV6",
1309ef1f84bSDavid du Colombier [PacketTooBigV6] "PacketTooBigV6",
1319ef1f84bSDavid du Colombier [TimeExceedV6] "TimeExceedV6",
1329ef1f84bSDavid du Colombier [SrcQuench] "SrcQuench",
1339ef1f84bSDavid du Colombier [Redirect] "Redirect",
1349ef1f84bSDavid du Colombier [EchoRequest] "EchoRequest",
1359ef1f84bSDavid du Colombier [TimeExceed] "TimeExceed",
1369ef1f84bSDavid du Colombier [InParmProblem] "InParmProblem",
1379ef1f84bSDavid du Colombier [Timestamp] "Timestamp",
1389ef1f84bSDavid du Colombier [TimestampReply] "TimestampReply",
1399ef1f84bSDavid du Colombier [InfoRequest] "InfoRequest",
1409ef1f84bSDavid du Colombier [InfoReply] "InfoReply",
1419ef1f84bSDavid du Colombier [AddrMaskRequest] "AddrMaskRequest",
1429ef1f84bSDavid du Colombier [AddrMaskReply] "AddrMaskReply",
1439ef1f84bSDavid du Colombier [EchoRequestV6] "EchoRequestV6",
1449ef1f84bSDavid du Colombier [EchoReplyV6] "EchoReplyV6",
1459ef1f84bSDavid du Colombier [RouterSolicit] "RouterSolicit",
1469ef1f84bSDavid du Colombier [RouterAdvert] "RouterAdvert",
1479ef1f84bSDavid du Colombier [NbrSolicit] "NbrSolicit",
1489ef1f84bSDavid du Colombier [NbrAdvert] "NbrAdvert",
1499ef1f84bSDavid du Colombier [RedirectV6] "RedirectV6",
1509ef1f84bSDavid du Colombier };
1519ef1f84bSDavid du Colombier
1529ef1f84bSDavid du Colombier static char *statnames6[Nstats6] =
1539ef1f84bSDavid du Colombier {
1549ef1f84bSDavid du Colombier [InMsgs6] "InMsgs",
1559ef1f84bSDavid du Colombier [InErrors6] "InErrors",
1569ef1f84bSDavid du Colombier [OutMsgs6] "OutMsgs",
1579ef1f84bSDavid du Colombier [CsumErrs6] "CsumErrs",
1589ef1f84bSDavid du Colombier [LenErrs6] "LenErrs",
1599ef1f84bSDavid du Colombier [HlenErrs6] "HlenErrs",
1609ef1f84bSDavid du Colombier [HoplimErrs6] "HoplimErrs",
1619ef1f84bSDavid du Colombier [IcmpCodeErrs6] "IcmpCodeErrs",
1629ef1f84bSDavid du Colombier [TargetErrs6] "TargetErrs",
1639ef1f84bSDavid du Colombier [OptlenErrs6] "OptlenErrs",
1649ef1f84bSDavid du Colombier [AddrmxpErrs6] "AddrmxpErrs",
1659ef1f84bSDavid du Colombier [RouterAddrErrs6] "RouterAddrErrs",
1669ef1f84bSDavid du Colombier };
1679ef1f84bSDavid du Colombier
1689ef1f84bSDavid du Colombier static char *unreachcode[] =
1699ef1f84bSDavid du Colombier {
1709ef1f84bSDavid du Colombier [Icmp6_no_route] "no route to destination",
1719ef1f84bSDavid du Colombier [Icmp6_ad_prohib] "comm with destination administratively prohibited",
1729ef1f84bSDavid du Colombier [Icmp6_out_src_scope] "beyond scope of source address",
1739ef1f84bSDavid du Colombier [Icmp6_adr_unreach] "address unreachable",
1749ef1f84bSDavid du Colombier [Icmp6_port_unreach] "port unreachable",
1759ef1f84bSDavid du Colombier [Icmp6_gress_src_fail] "source address failed ingress/egress policy",
1769ef1f84bSDavid du Colombier [Icmp6_rej_route] "reject route to destination",
1779ef1f84bSDavid du Colombier [Icmp6_unknown] "icmp unreachable: unknown code",
1789ef1f84bSDavid du Colombier };
1799ef1f84bSDavid du Colombier
1809ef1f84bSDavid du Colombier static void icmpkick6(void *x, Block *bp);
1819ef1f84bSDavid du Colombier
1829ef1f84bSDavid du Colombier static void
icmpcreate6(Conv * c)1839ef1f84bSDavid du Colombier icmpcreate6(Conv *c)
1849ef1f84bSDavid du Colombier {
1859ef1f84bSDavid du Colombier c->rq = qopen(64*1024, Qmsg, 0, c);
1869ef1f84bSDavid du Colombier c->wq = qbypass(icmpkick6, c);
1879ef1f84bSDavid du Colombier }
1889ef1f84bSDavid du Colombier
1899ef1f84bSDavid du Colombier static void
set_cksum(Block * bp)1909ef1f84bSDavid du Colombier set_cksum(Block *bp)
1919ef1f84bSDavid du Colombier {
1929ef1f84bSDavid du Colombier IPICMP *p = (IPICMP *)(bp->rp);
1939ef1f84bSDavid du Colombier
1949ef1f84bSDavid du Colombier hnputl(p->vcf, 0); /* borrow IP header as pseudoheader */
1959ef1f84bSDavid du Colombier hnputs(p->ploadlen, blocklen(bp) - IP6HDR);
1969ef1f84bSDavid du Colombier p->proto = 0;
1979ef1f84bSDavid du Colombier p->ttl = ICMPv6; /* ttl gets set later */
1989ef1f84bSDavid du Colombier hnputs(p->cksum, 0);
1999ef1f84bSDavid du Colombier hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp)));
2009ef1f84bSDavid du Colombier p->proto = ICMPv6;
2019ef1f84bSDavid du Colombier }
2029ef1f84bSDavid du Colombier
2039ef1f84bSDavid du Colombier static Block *
newIPICMP(int packetlen)2049ef1f84bSDavid du Colombier newIPICMP(int packetlen)
2059ef1f84bSDavid du Colombier {
2069ef1f84bSDavid du Colombier Block *nbp;
2079ef1f84bSDavid du Colombier
2089ef1f84bSDavid du Colombier nbp = allocb(packetlen);
2099ef1f84bSDavid du Colombier nbp->wp += packetlen;
2109ef1f84bSDavid du Colombier memset(nbp->rp, 0, packetlen);
2119ef1f84bSDavid du Colombier return nbp;
2129ef1f84bSDavid du Colombier }
2139ef1f84bSDavid du Colombier
2149ef1f84bSDavid du Colombier void
icmpadvise6(Proto * icmp,Block * bp,char * msg)2159ef1f84bSDavid du Colombier icmpadvise6(Proto *icmp, Block *bp, char *msg)
2169ef1f84bSDavid du Colombier {
2179ef1f84bSDavid du Colombier ushort recid;
2189ef1f84bSDavid du Colombier Conv **c, *s;
2199ef1f84bSDavid du Colombier IPICMP *p;
2209ef1f84bSDavid du Colombier
2219ef1f84bSDavid du Colombier p = (IPICMP *)bp->rp;
2229ef1f84bSDavid du Colombier recid = nhgets(p->icmpid);
2239ef1f84bSDavid du Colombier
2249ef1f84bSDavid du Colombier for(c = icmp->conv; *c; c++) {
2259ef1f84bSDavid du Colombier s = *c;
2269ef1f84bSDavid du Colombier if(s->lport == recid && ipcmp(s->raddr, p->dst) == 0){
2279ef1f84bSDavid du Colombier qhangup(s->rq, msg);
2289ef1f84bSDavid du Colombier qhangup(s->wq, msg);
2299ef1f84bSDavid du Colombier break;
2309ef1f84bSDavid du Colombier }
2319ef1f84bSDavid du Colombier }
2329ef1f84bSDavid du Colombier freeblist(bp);
2339ef1f84bSDavid du Colombier }
2349ef1f84bSDavid du Colombier
2359ef1f84bSDavid du Colombier static void
icmpkick6(void * x,Block * bp)2369ef1f84bSDavid du Colombier icmpkick6(void *x, Block *bp)
2379ef1f84bSDavid du Colombier {
2389ef1f84bSDavid du Colombier uchar laddr[IPaddrlen], raddr[IPaddrlen];
2399ef1f84bSDavid du Colombier Conv *c = x;
2409ef1f84bSDavid du Colombier IPICMP *p;
2419ef1f84bSDavid du Colombier Icmppriv6 *ipriv = c->p->priv;
2429ef1f84bSDavid du Colombier Icmpcb6 *icb = (Icmpcb6*)c->ptcl;
2439ef1f84bSDavid du Colombier
2449ef1f84bSDavid du Colombier if(bp == nil)
2459ef1f84bSDavid du Colombier return;
2469ef1f84bSDavid du Colombier
2479ef1f84bSDavid du Colombier if(icb->headers==6) {
2489ef1f84bSDavid du Colombier /* get user specified addresses */
2499ef1f84bSDavid du Colombier bp = pullupblock(bp, ICMP_USEAD6);
2509ef1f84bSDavid du Colombier if(bp == nil)
2519ef1f84bSDavid du Colombier return;
2529ef1f84bSDavid du Colombier bp->rp += 8;
2539ef1f84bSDavid du Colombier ipmove(laddr, bp->rp);
2549ef1f84bSDavid du Colombier bp->rp += IPaddrlen;
2559ef1f84bSDavid du Colombier ipmove(raddr, bp->rp);
2569ef1f84bSDavid du Colombier bp->rp += IPaddrlen;
2579ef1f84bSDavid du Colombier bp = padblock(bp, IP6HDR);
2589ef1f84bSDavid du Colombier }
2599ef1f84bSDavid du Colombier
2609ef1f84bSDavid du Colombier if(blocklen(bp) < IPICMPSZ){
2619ef1f84bSDavid du Colombier freeblist(bp);
2629ef1f84bSDavid du Colombier return;
2639ef1f84bSDavid du Colombier }
2649ef1f84bSDavid du Colombier p = (IPICMP *)(bp->rp);
2659ef1f84bSDavid du Colombier if(icb->headers == 6) {
2669ef1f84bSDavid du Colombier ipmove(p->dst, raddr);
2679ef1f84bSDavid du Colombier ipmove(p->src, laddr);
2689ef1f84bSDavid du Colombier } else {
2699ef1f84bSDavid du Colombier ipmove(p->dst, c->raddr);
2709ef1f84bSDavid du Colombier ipmove(p->src, c->laddr);
2719ef1f84bSDavid du Colombier hnputs(p->icmpid, c->lport);
2729ef1f84bSDavid du Colombier }
2739ef1f84bSDavid du Colombier
2749ef1f84bSDavid du Colombier set_cksum(bp);
2759ef1f84bSDavid du Colombier p->vcf[0] = 0x06 << 4;
2769ef1f84bSDavid du Colombier if(p->type <= Maxtype6)
2779ef1f84bSDavid du Colombier ipriv->out[p->type]++;
2789ef1f84bSDavid du Colombier ipoput6(c->p->f, bp, 0, c->ttl, c->tos, nil);
2799ef1f84bSDavid du Colombier }
2809ef1f84bSDavid du Colombier
2819ef1f84bSDavid du Colombier char*
icmpctl6(Conv * c,char ** argv,int argc)2829ef1f84bSDavid du Colombier icmpctl6(Conv *c, char **argv, int argc)
2839ef1f84bSDavid du Colombier {
2849ef1f84bSDavid du Colombier Icmpcb6 *icb;
2859ef1f84bSDavid du Colombier
2869ef1f84bSDavid du Colombier icb = (Icmpcb6*) c->ptcl;
2879ef1f84bSDavid du Colombier if(argc==1 && strcmp(argv[0], "headers")==0) {
2889ef1f84bSDavid du Colombier icb->headers = 6;
2899ef1f84bSDavid du Colombier return nil;
2909ef1f84bSDavid du Colombier }
2919ef1f84bSDavid du Colombier return "unknown control request";
2929ef1f84bSDavid du Colombier }
2939ef1f84bSDavid du Colombier
2949ef1f84bSDavid du Colombier static void
goticmpkt6(Proto * icmp,Block * bp,int muxkey)2959ef1f84bSDavid du Colombier goticmpkt6(Proto *icmp, Block *bp, int muxkey)
2969ef1f84bSDavid du Colombier {
2979ef1f84bSDavid du Colombier ushort recid;
2989ef1f84bSDavid du Colombier uchar *addr;
2999ef1f84bSDavid du Colombier Conv **c, *s;
3009ef1f84bSDavid du Colombier IPICMP *p = (IPICMP *)bp->rp;
3019ef1f84bSDavid du Colombier
3029ef1f84bSDavid du Colombier if(muxkey == 0) {
3039ef1f84bSDavid du Colombier recid = nhgets(p->icmpid);
3049ef1f84bSDavid du Colombier addr = p->src;
3059ef1f84bSDavid du Colombier } else {
3069ef1f84bSDavid du Colombier recid = muxkey;
3079ef1f84bSDavid du Colombier addr = p->dst;
3089ef1f84bSDavid du Colombier }
3099ef1f84bSDavid du Colombier
3109ef1f84bSDavid du Colombier for(c = icmp->conv; *c; c++){
3119ef1f84bSDavid du Colombier s = *c;
3129ef1f84bSDavid du Colombier if(s->lport == recid && ipcmp(s->raddr, addr) == 0){
3139ef1f84bSDavid du Colombier bp = concatblock(bp);
3149ef1f84bSDavid du Colombier if(bp != nil)
3159ef1f84bSDavid du Colombier qpass(s->rq, bp);
3169ef1f84bSDavid du Colombier return;
3179ef1f84bSDavid du Colombier }
3189ef1f84bSDavid du Colombier }
3199ef1f84bSDavid du Colombier
3209ef1f84bSDavid du Colombier freeblist(bp);
3219ef1f84bSDavid du Colombier }
3229ef1f84bSDavid du Colombier
3239ef1f84bSDavid du Colombier static Block *
mkechoreply6(Block * bp,Ipifc * ifc)3249ef1f84bSDavid du Colombier mkechoreply6(Block *bp, Ipifc *ifc)
3259ef1f84bSDavid du Colombier {
3269ef1f84bSDavid du Colombier uchar addr[IPaddrlen];
3279ef1f84bSDavid du Colombier IPICMP *p = (IPICMP *)(bp->rp);
3289ef1f84bSDavid du Colombier
3299ef1f84bSDavid du Colombier ipmove(addr, p->src);
3309ef1f84bSDavid du Colombier if(!isv6mcast(p->dst))
3319ef1f84bSDavid du Colombier ipmove(p->src, p->dst);
3329ef1f84bSDavid du Colombier else if (!ipv6anylocal(ifc, p->src))
3339ef1f84bSDavid du Colombier return nil;
3349ef1f84bSDavid du Colombier ipmove(p->dst, addr);
3359ef1f84bSDavid du Colombier p->type = EchoReplyV6;
3369ef1f84bSDavid du Colombier set_cksum(bp);
3379ef1f84bSDavid du Colombier return bp;
3389ef1f84bSDavid du Colombier }
3399ef1f84bSDavid du Colombier
3409ef1f84bSDavid du Colombier /*
3419ef1f84bSDavid du Colombier * sends out an ICMPv6 neighbor solicitation
3429ef1f84bSDavid du Colombier * suni == SRC_UNSPEC or SRC_UNI,
3439ef1f84bSDavid du Colombier * tuni == TARG_MULTI => multicast for address resolution,
3449ef1f84bSDavid du Colombier * and tuni == TARG_UNI => neighbor reachability.
3459ef1f84bSDavid du Colombier */
3469ef1f84bSDavid du Colombier extern void
icmpns(Fs * f,uchar * src,int suni,uchar * targ,int tuni,uchar * mac)3479ef1f84bSDavid du Colombier icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac)
3489ef1f84bSDavid du Colombier {
3499ef1f84bSDavid du Colombier Block *nbp;
3509ef1f84bSDavid du Colombier Ndpkt *np;
3519ef1f84bSDavid du Colombier Proto *icmp = f->t2p[ICMPv6];
3529ef1f84bSDavid du Colombier Icmppriv6 *ipriv = icmp->priv;
3539ef1f84bSDavid du Colombier
3549ef1f84bSDavid du Colombier nbp = newIPICMP(NDPKTSZ);
3559ef1f84bSDavid du Colombier np = (Ndpkt*) nbp->rp;
3569ef1f84bSDavid du Colombier
3579ef1f84bSDavid du Colombier if(suni == SRC_UNSPEC)
3589ef1f84bSDavid du Colombier memmove(np->src, v6Unspecified, IPaddrlen);
3599ef1f84bSDavid du Colombier else
3609ef1f84bSDavid du Colombier memmove(np->src, src, IPaddrlen);
3619ef1f84bSDavid du Colombier
3629ef1f84bSDavid du Colombier if(tuni == TARG_UNI)
3639ef1f84bSDavid du Colombier memmove(np->dst, targ, IPaddrlen);
3649ef1f84bSDavid du Colombier else
3659ef1f84bSDavid du Colombier ipv62smcast(np->dst, targ);
3669ef1f84bSDavid du Colombier
3679ef1f84bSDavid du Colombier np->type = NbrSolicit;
3689ef1f84bSDavid du Colombier np->code = 0;
3699ef1f84bSDavid du Colombier memmove(np->target, targ, IPaddrlen);
3709ef1f84bSDavid du Colombier if(suni != SRC_UNSPEC) {
3719ef1f84bSDavid du Colombier np->otype = SRC_LLADDR;
3729ef1f84bSDavid du Colombier np->olen = 1; /* 1+1+6 = 8 = 1 8-octet */
3739ef1f84bSDavid du Colombier memmove(np->lnaddr, mac, sizeof(np->lnaddr));
3749ef1f84bSDavid du Colombier } else
3759ef1f84bSDavid du Colombier nbp->wp -= NDPKTSZ - NDISCSZ;
3769ef1f84bSDavid du Colombier
3779ef1f84bSDavid du Colombier set_cksum(nbp);
3789ef1f84bSDavid du Colombier np = (Ndpkt*)nbp->rp;
3799ef1f84bSDavid du Colombier np->ttl = HOP_LIMIT;
3809ef1f84bSDavid du Colombier np->vcf[0] = 0x06 << 4;
3819ef1f84bSDavid du Colombier ipriv->out[NbrSolicit]++;
3829ef1f84bSDavid du Colombier netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ);
3839ef1f84bSDavid du Colombier ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
3849ef1f84bSDavid du Colombier }
3859ef1f84bSDavid du Colombier
3869ef1f84bSDavid du Colombier /*
3879ef1f84bSDavid du Colombier * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
3889ef1f84bSDavid du Colombier */
3899ef1f84bSDavid du Colombier extern void
icmpna(Fs * f,uchar * src,uchar * dst,uchar * targ,uchar * mac,uchar flags)3909ef1f84bSDavid du Colombier icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags)
3919ef1f84bSDavid du Colombier {
3929ef1f84bSDavid du Colombier Block *nbp;
3939ef1f84bSDavid du Colombier Ndpkt *np;
3949ef1f84bSDavid du Colombier Proto *icmp = f->t2p[ICMPv6];
3959ef1f84bSDavid du Colombier Icmppriv6 *ipriv = icmp->priv;
3969ef1f84bSDavid du Colombier
3979ef1f84bSDavid du Colombier nbp = newIPICMP(NDPKTSZ);
3989ef1f84bSDavid du Colombier np = (Ndpkt*)nbp->rp;
3999ef1f84bSDavid du Colombier
4009ef1f84bSDavid du Colombier memmove(np->src, src, IPaddrlen);
4019ef1f84bSDavid du Colombier memmove(np->dst, dst, IPaddrlen);
4029ef1f84bSDavid du Colombier
4039ef1f84bSDavid du Colombier np->type = NbrAdvert;
4049ef1f84bSDavid du Colombier np->code = 0;
4059ef1f84bSDavid du Colombier np->icmpid[0] = flags;
4069ef1f84bSDavid du Colombier memmove(np->target, targ, IPaddrlen);
4079ef1f84bSDavid du Colombier
4089ef1f84bSDavid du Colombier np->otype = TARGET_LLADDR;
4099ef1f84bSDavid du Colombier np->olen = 1;
4109ef1f84bSDavid du Colombier memmove(np->lnaddr, mac, sizeof(np->lnaddr));
4119ef1f84bSDavid du Colombier
4129ef1f84bSDavid du Colombier set_cksum(nbp);
4139ef1f84bSDavid du Colombier np = (Ndpkt*) nbp->rp;
4149ef1f84bSDavid du Colombier np->ttl = HOP_LIMIT;
4159ef1f84bSDavid du Colombier np->vcf[0] = 0x06 << 4;
4169ef1f84bSDavid du Colombier ipriv->out[NbrAdvert]++;
4179ef1f84bSDavid du Colombier netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
4189ef1f84bSDavid du Colombier ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
4199ef1f84bSDavid du Colombier }
4209ef1f84bSDavid du Colombier
421*a3323688SDavid du Colombier /* if free is true, freeblist(bp) before return. */
4229ef1f84bSDavid du Colombier extern void
icmphostunr(Fs * f,Ipifc * ifc,Block * bp,int code,int free)4239ef1f84bSDavid du Colombier icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free)
4249ef1f84bSDavid du Colombier {
425*a3323688SDavid du Colombier int osz, sz;
4269ef1f84bSDavid du Colombier Block *nbp;
4279ef1f84bSDavid du Colombier IPICMP *np;
428*a3323688SDavid du Colombier Icmppriv6 *ipriv;
4299ef1f84bSDavid du Colombier Ip6hdr *p;
430*a3323688SDavid du Colombier Proto *icmp;
4319ef1f84bSDavid du Colombier
432*a3323688SDavid du Colombier osz = BLEN(bp);
433*a3323688SDavid du Colombier sz = MIN(IPICMPSZ + osz, v6MINTU);
434*a3323688SDavid du Colombier icmp = f->t2p[ICMPv6];
435*a3323688SDavid du Colombier ipriv = icmp->priv;
4369ef1f84bSDavid du Colombier p = (Ip6hdr *)bp->rp;
4379ef1f84bSDavid du Colombier if(isv6mcast(p->src))
438*a3323688SDavid du Colombier goto freebl;
4399ef1f84bSDavid du Colombier nbp = newIPICMP(sz);
4409ef1f84bSDavid du Colombier np = (IPICMP *)nbp->rp;
4419ef1f84bSDavid du Colombier
4429ef1f84bSDavid du Colombier rlock(ifc);
443*a3323688SDavid du Colombier if(!ipv6anylocal(ifc, np->src)){
4449ef1f84bSDavid du Colombier netlog(f, Logicmp, "icmphostunr fail -> src %I dst %I\n",
4459ef1f84bSDavid du Colombier p->src, p->dst);
446*a3323688SDavid du Colombier runlock(ifc);
4479ef1f84bSDavid du Colombier freeblist(nbp);
448*a3323688SDavid du Colombier goto freebl;
4499ef1f84bSDavid du Colombier }
4509ef1f84bSDavid du Colombier
451*a3323688SDavid du Colombier netlog(f, Logicmp, "send icmphostunr -> src %I dst %I\n", p->src, p->dst);
4529ef1f84bSDavid du Colombier memmove(np->dst, p->src, IPaddrlen);
4539ef1f84bSDavid du Colombier np->type = UnreachableV6;
4549ef1f84bSDavid du Colombier np->code = code;
4559ef1f84bSDavid du Colombier memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
4569ef1f84bSDavid du Colombier set_cksum(nbp);
4579ef1f84bSDavid du Colombier np->ttl = HOP_LIMIT;
4589ef1f84bSDavid du Colombier np->vcf[0] = 0x06 << 4;
4599ef1f84bSDavid du Colombier ipriv->out[UnreachableV6]++;
4609ef1f84bSDavid du Colombier
4619ef1f84bSDavid du Colombier if(free)
4629ef1f84bSDavid du Colombier ipiput6(f, ifc, nbp);
463*a3323688SDavid du Colombier else
4649ef1f84bSDavid du Colombier ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
4659ef1f84bSDavid du Colombier runlock(ifc);
466*a3323688SDavid du Colombier freebl:
467*a3323688SDavid du Colombier if(free)
4689ef1f84bSDavid du Colombier freeblist(bp);
4699ef1f84bSDavid du Colombier }
4709ef1f84bSDavid du Colombier
4719ef1f84bSDavid du Colombier extern void
icmpttlexceeded6(Fs * f,Ipifc * ifc,Block * bp)4729ef1f84bSDavid du Colombier icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp)
4739ef1f84bSDavid du Colombier {
4749ef1f84bSDavid du Colombier int osz = BLEN(bp);
4759ef1f84bSDavid du Colombier int sz = MIN(IPICMPSZ + osz, v6MINTU);
4769ef1f84bSDavid du Colombier Block *nbp;
4779ef1f84bSDavid du Colombier IPICMP *np;
4789ef1f84bSDavid du Colombier Ip6hdr *p;
4799ef1f84bSDavid du Colombier Proto *icmp = f->t2p[ICMPv6];
4809ef1f84bSDavid du Colombier Icmppriv6 *ipriv = icmp->priv;
4819ef1f84bSDavid du Colombier
4829ef1f84bSDavid du Colombier p = (Ip6hdr *)bp->rp;
4839ef1f84bSDavid du Colombier if(isv6mcast(p->src))
4849ef1f84bSDavid du Colombier return;
4859ef1f84bSDavid du Colombier
4869ef1f84bSDavid du Colombier nbp = newIPICMP(sz);
4879ef1f84bSDavid du Colombier np = (IPICMP *) nbp->rp;
4889ef1f84bSDavid du Colombier if(ipv6anylocal(ifc, np->src))
4899ef1f84bSDavid du Colombier netlog(f, Logicmp, "send icmpttlexceeded6 -> src %I dst %I\n",
4909ef1f84bSDavid du Colombier p->src, p->dst);
4919ef1f84bSDavid du Colombier else {
4929ef1f84bSDavid du Colombier netlog(f, Logicmp, "icmpttlexceeded6 fail -> src %I dst %I\n",
4939ef1f84bSDavid du Colombier p->src, p->dst);
4949ef1f84bSDavid du Colombier return;
4959ef1f84bSDavid du Colombier }
4969ef1f84bSDavid du Colombier
4979ef1f84bSDavid du Colombier memmove(np->dst, p->src, IPaddrlen);
4989ef1f84bSDavid du Colombier np->type = TimeExceedV6;
4999ef1f84bSDavid du Colombier np->code = 0;
5009ef1f84bSDavid du Colombier memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
5019ef1f84bSDavid du Colombier set_cksum(nbp);
5029ef1f84bSDavid du Colombier np->ttl = HOP_LIMIT;
5039ef1f84bSDavid du Colombier np->vcf[0] = 0x06 << 4;
5049ef1f84bSDavid du Colombier ipriv->out[TimeExceedV6]++;
5059ef1f84bSDavid du Colombier ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
5069ef1f84bSDavid du Colombier }
5079ef1f84bSDavid du Colombier
5089ef1f84bSDavid du Colombier extern void
icmppkttoobig6(Fs * f,Ipifc * ifc,Block * bp)5099ef1f84bSDavid du Colombier icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp)
5109ef1f84bSDavid du Colombier {
5119ef1f84bSDavid du Colombier int osz = BLEN(bp);
5129ef1f84bSDavid du Colombier int sz = MIN(IPICMPSZ + osz, v6MINTU);
5139ef1f84bSDavid du Colombier Block *nbp;
5149ef1f84bSDavid du Colombier IPICMP *np;
5159ef1f84bSDavid du Colombier Ip6hdr *p;
5169ef1f84bSDavid du Colombier Proto *icmp = f->t2p[ICMPv6];
5179ef1f84bSDavid du Colombier Icmppriv6 *ipriv = icmp->priv;
5189ef1f84bSDavid du Colombier
5199ef1f84bSDavid du Colombier p = (Ip6hdr *)bp->rp;
5209ef1f84bSDavid du Colombier if(isv6mcast(p->src))
5219ef1f84bSDavid du Colombier return;
5229ef1f84bSDavid du Colombier
5239ef1f84bSDavid du Colombier nbp = newIPICMP(sz);
5249ef1f84bSDavid du Colombier np = (IPICMP *)nbp->rp;
5259ef1f84bSDavid du Colombier if(ipv6anylocal(ifc, np->src))
5269ef1f84bSDavid du Colombier netlog(f, Logicmp, "send icmppkttoobig6 -> src %I dst %I\n",
5279ef1f84bSDavid du Colombier p->src, p->dst);
5289ef1f84bSDavid du Colombier else {
5299ef1f84bSDavid du Colombier netlog(f, Logicmp, "icmppkttoobig6 fail -> src %I dst %I\n",
5309ef1f84bSDavid du Colombier p->src, p->dst);
5319ef1f84bSDavid du Colombier return;
5329ef1f84bSDavid du Colombier }
5339ef1f84bSDavid du Colombier
5349ef1f84bSDavid du Colombier memmove(np->dst, p->src, IPaddrlen);
5359ef1f84bSDavid du Colombier np->type = PacketTooBigV6;
5369ef1f84bSDavid du Colombier np->code = 0;
5379ef1f84bSDavid du Colombier hnputl(np->icmpid, ifc->maxtu - ifc->medium->hsize);
5389ef1f84bSDavid du Colombier memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
5399ef1f84bSDavid du Colombier set_cksum(nbp);
5409ef1f84bSDavid du Colombier np->ttl = HOP_LIMIT;
5419ef1f84bSDavid du Colombier np->vcf[0] = 0x06 << 4;
5429ef1f84bSDavid du Colombier ipriv->out[PacketTooBigV6]++;
5439ef1f84bSDavid du Colombier ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
5449ef1f84bSDavid du Colombier }
5459ef1f84bSDavid du Colombier
5469ef1f84bSDavid du Colombier /*
5479ef1f84bSDavid du Colombier * RFC 2461, pages 39-40, pages 57-58.
5489ef1f84bSDavid du Colombier */
5499ef1f84bSDavid du Colombier static int
valid(Proto * icmp,Ipifc *,Block * bp,Icmppriv6 * ipriv)550*a3323688SDavid du Colombier valid(Proto *icmp, Ipifc *, Block *bp, Icmppriv6 *ipriv)
5519ef1f84bSDavid du Colombier {
552*a3323688SDavid du Colombier int sz, osz, unsp, n, ttl, iplen, pktsz;
553*a3323688SDavid du Colombier uchar *packet;
554*a3323688SDavid du Colombier IPICMP *p;
5559ef1f84bSDavid du Colombier Ndpkt *np;
5569ef1f84bSDavid du Colombier
5579ef1f84bSDavid du Colombier n = blocklen(bp);
5589ef1f84bSDavid du Colombier if(n < IPICMPSZ) {
5599ef1f84bSDavid du Colombier ipriv->stats[HlenErrs6]++;
5609ef1f84bSDavid du Colombier netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
5619ef1f84bSDavid du Colombier goto err;
5629ef1f84bSDavid du Colombier }
5639ef1f84bSDavid du Colombier
564*a3323688SDavid du Colombier packet = bp->rp;
565*a3323688SDavid du Colombier p = (IPICMP *)packet;
566*a3323688SDavid du Colombier pktsz = BLEN(bp);
5679ef1f84bSDavid du Colombier iplen = nhgets(p->ploadlen);
5689ef1f84bSDavid du Colombier if(iplen > n - IP6HDR) {
5699ef1f84bSDavid du Colombier ipriv->stats[LenErrs6]++;
5709ef1f84bSDavid du Colombier netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
5719ef1f84bSDavid du Colombier goto err;
5729ef1f84bSDavid du Colombier }
5739ef1f84bSDavid du Colombier
5749ef1f84bSDavid du Colombier /* Rather than construct explicit pseudoheader, overwrite IPv6 header */
5759ef1f84bSDavid du Colombier if(p->proto != ICMPv6) {
5769ef1f84bSDavid du Colombier /* This code assumes no extension headers!!! */
5779ef1f84bSDavid du Colombier netlog(icmp->f, Logicmp, "icmp error: extension header\n");
5789ef1f84bSDavid du Colombier goto err;
5799ef1f84bSDavid du Colombier }
5809ef1f84bSDavid du Colombier memset(packet, 0, 4);
5819ef1f84bSDavid du Colombier ttl = p->ttl;
5829ef1f84bSDavid du Colombier p->ttl = p->proto;
5839ef1f84bSDavid du Colombier p->proto = 0;
5849ef1f84bSDavid du Colombier if(ptclcsum(bp, 0, iplen + IP6HDR)) {
5859ef1f84bSDavid du Colombier ipriv->stats[CsumErrs6]++;
5869ef1f84bSDavid du Colombier netlog(icmp->f, Logicmp, "icmp checksum error\n");
5879ef1f84bSDavid du Colombier goto err;
5889ef1f84bSDavid du Colombier }
5899ef1f84bSDavid du Colombier p->proto = p->ttl;
5909ef1f84bSDavid du Colombier p->ttl = ttl;
5919ef1f84bSDavid du Colombier
5929ef1f84bSDavid du Colombier /* additional tests for some pkt types */
593*a3323688SDavid du Colombier if (p->type != NbrSolicit && p->type != NbrAdvert &&
594*a3323688SDavid du Colombier p->type != RouterAdvert && p->type != RouterSolicit &&
595*a3323688SDavid du Colombier p->type != RedirectV6)
596*a3323688SDavid du Colombier return 1; /* TODO: unknown, presumed valid; why? */
5979ef1f84bSDavid du Colombier if(p->ttl != HOP_LIMIT) {
5989ef1f84bSDavid du Colombier ipriv->stats[HoplimErrs6]++;
5999ef1f84bSDavid du Colombier goto err;
6009ef1f84bSDavid du Colombier }
6019ef1f84bSDavid du Colombier if(p->code != 0) {
6029ef1f84bSDavid du Colombier ipriv->stats[IcmpCodeErrs6]++;
6039ef1f84bSDavid du Colombier goto err;
6049ef1f84bSDavid du Colombier }
6059ef1f84bSDavid du Colombier
6069ef1f84bSDavid du Colombier switch (p->type) {
6079ef1f84bSDavid du Colombier case NbrSolicit:
6089ef1f84bSDavid du Colombier case NbrAdvert:
6099ef1f84bSDavid du Colombier np = (Ndpkt*) p;
6109ef1f84bSDavid du Colombier if(isv6mcast(np->target)) {
6119ef1f84bSDavid du Colombier ipriv->stats[TargetErrs6]++;
6129ef1f84bSDavid du Colombier goto err;
6139ef1f84bSDavid du Colombier }
6149ef1f84bSDavid du Colombier if(optexsts(np) && np->olen == 0) {
6159ef1f84bSDavid du Colombier ipriv->stats[OptlenErrs6]++;
6169ef1f84bSDavid du Colombier goto err;
6179ef1f84bSDavid du Colombier }
618*a3323688SDavid du Colombier if (p->type == NbrSolicit && ipcmp(np->src, v6Unspecified) == 0)
6199ef1f84bSDavid du Colombier if(!issmcast(np->dst) || optexsts(np)) {
6209ef1f84bSDavid du Colombier ipriv->stats[AddrmxpErrs6]++;
6219ef1f84bSDavid du Colombier goto err;
6229ef1f84bSDavid du Colombier }
623*a3323688SDavid du Colombier if(p->type == NbrAdvert && isv6mcast(np->dst) &&
624*a3323688SDavid du Colombier nhgets(np->icmpid) & Sflag){
6259ef1f84bSDavid du Colombier ipriv->stats[AddrmxpErrs6]++;
6269ef1f84bSDavid du Colombier goto err;
6279ef1f84bSDavid du Colombier }
6289ef1f84bSDavid du Colombier break;
6299ef1f84bSDavid du Colombier case RouterAdvert:
6309ef1f84bSDavid du Colombier if(pktsz - IP6HDR < 16) {
6319ef1f84bSDavid du Colombier ipriv->stats[HlenErrs6]++;
6329ef1f84bSDavid du Colombier goto err;
6339ef1f84bSDavid du Colombier }
6349ef1f84bSDavid du Colombier if(!islinklocal(p->src)) {
6359ef1f84bSDavid du Colombier ipriv->stats[RouterAddrErrs6]++;
6369ef1f84bSDavid du Colombier goto err;
6379ef1f84bSDavid du Colombier }
638*a3323688SDavid du Colombier for (sz = IPICMPSZ + 8; sz+1 < pktsz; sz += 8*osz) {
6399ef1f84bSDavid du Colombier osz = packet[sz+1];
6409ef1f84bSDavid du Colombier if(osz <= 0) {
6419ef1f84bSDavid du Colombier ipriv->stats[OptlenErrs6]++;
6429ef1f84bSDavid du Colombier goto err;
6439ef1f84bSDavid du Colombier }
6449ef1f84bSDavid du Colombier }
6459ef1f84bSDavid du Colombier break;
6469ef1f84bSDavid du Colombier case RouterSolicit:
6479ef1f84bSDavid du Colombier if(pktsz - IP6HDR < 8) {
6489ef1f84bSDavid du Colombier ipriv->stats[HlenErrs6]++;
6499ef1f84bSDavid du Colombier goto err;
6509ef1f84bSDavid du Colombier }
6519ef1f84bSDavid du Colombier unsp = (ipcmp(p->src, v6Unspecified) == 0);
652*a3323688SDavid du Colombier for (sz = IPICMPSZ + 8; sz+1 < pktsz; sz += 8*osz) {
6539ef1f84bSDavid du Colombier osz = packet[sz+1];
654*a3323688SDavid du Colombier if(osz <= 0 || (unsp && packet[sz] == SRC_LLADDR)) {
6559ef1f84bSDavid du Colombier ipriv->stats[OptlenErrs6]++;
6569ef1f84bSDavid du Colombier goto err;
6579ef1f84bSDavid du Colombier }
6589ef1f84bSDavid du Colombier }
6599ef1f84bSDavid du Colombier break;
6609ef1f84bSDavid du Colombier case RedirectV6:
661*a3323688SDavid du Colombier /* TODO: fill in */
6629ef1f84bSDavid du Colombier break;
6639ef1f84bSDavid du Colombier default:
6649ef1f84bSDavid du Colombier goto err;
6659ef1f84bSDavid du Colombier }
6669ef1f84bSDavid du Colombier return 1;
6679ef1f84bSDavid du Colombier err:
6689ef1f84bSDavid du Colombier ipriv->stats[InErrors6]++;
6699ef1f84bSDavid du Colombier return 0;
6709ef1f84bSDavid du Colombier }
6719ef1f84bSDavid du Colombier
6729ef1f84bSDavid du Colombier static int
targettype(Fs * f,Ipifc * ifc,uchar * target)6739ef1f84bSDavid du Colombier targettype(Fs *f, Ipifc *ifc, uchar *target)
6749ef1f84bSDavid du Colombier {
6759ef1f84bSDavid du Colombier Iplifc *lifc;
6769ef1f84bSDavid du Colombier int t;
6779ef1f84bSDavid du Colombier
6789ef1f84bSDavid du Colombier rlock(ifc);
6799ef1f84bSDavid du Colombier if(ipproxyifc(f, ifc, target)) {
6809ef1f84bSDavid du Colombier runlock(ifc);
6819ef1f84bSDavid du Colombier return Tuniproxy;
6829ef1f84bSDavid du Colombier }
6839ef1f84bSDavid du Colombier
6849ef1f84bSDavid du Colombier for(lifc = ifc->lifc; lifc; lifc = lifc->next)
6859ef1f84bSDavid du Colombier if(ipcmp(lifc->local, target) == 0) {
6869ef1f84bSDavid du Colombier t = (lifc->tentative)? Tunitent: Tunirany;
6879ef1f84bSDavid du Colombier runlock(ifc);
6889ef1f84bSDavid du Colombier return t;
6899ef1f84bSDavid du Colombier }
6909ef1f84bSDavid du Colombier
6919ef1f84bSDavid du Colombier runlock(ifc);
6929ef1f84bSDavid du Colombier return 0;
6939ef1f84bSDavid du Colombier }
6949ef1f84bSDavid du Colombier
695*a3323688SDavid du Colombier /* bp needs to be freed with freeblist or passed on. */
6969ef1f84bSDavid du Colombier static void
icmpiput6(Proto * icmp,Ipifc * ipifc,Block * bp)6979ef1f84bSDavid du Colombier icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp)
6989ef1f84bSDavid du Colombier {
699*a3323688SDavid du Colombier int type;
7009ef1f84bSDavid du Colombier char *msg, m2[128];
7019ef1f84bSDavid du Colombier uchar pktflags;
702*a3323688SDavid du Colombier uchar *packet, *src;
7039ef1f84bSDavid du Colombier uchar lsrc[IPaddrlen];
7049ef1f84bSDavid du Colombier Block *r;
705*a3323688SDavid du Colombier IPICMP *p;
706*a3323688SDavid du Colombier Icmppriv6 *ipriv;
7079ef1f84bSDavid du Colombier Iplifc *lifc;
7089ef1f84bSDavid du Colombier Ndpkt* np;
7099ef1f84bSDavid du Colombier Proto *pr;
7109ef1f84bSDavid du Colombier
711*a3323688SDavid du Colombier packet = bp->rp;
712*a3323688SDavid du Colombier p = (IPICMP *)packet;
713*a3323688SDavid du Colombier type = p->type;
714*a3323688SDavid du Colombier ipriv = icmp->priv;
715*a3323688SDavid du Colombier if(!valid(icmp, ipifc, bp, ipriv) || type > Maxtype6)
7169ef1f84bSDavid du Colombier goto raise;
7179ef1f84bSDavid du Colombier
718*a3323688SDavid du Colombier ipriv->in[type]++;
719*a3323688SDavid du Colombier switch(type) {
7209ef1f84bSDavid du Colombier case EchoRequestV6:
721*a3323688SDavid du Colombier bp = concatblock(bp);
7229ef1f84bSDavid du Colombier r = mkechoreply6(bp, ipifc);
7239ef1f84bSDavid du Colombier if(r == nil)
7249ef1f84bSDavid du Colombier goto raise;
7259ef1f84bSDavid du Colombier ipriv->out[EchoReply]++;
7269ef1f84bSDavid du Colombier ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
7279ef1f84bSDavid du Colombier break;
7289ef1f84bSDavid du Colombier case UnreachableV6:
7299ef1f84bSDavid du Colombier if(p->code >= nelem(unreachcode))
7309ef1f84bSDavid du Colombier msg = unreachcode[Icmp6_unknown];
7319ef1f84bSDavid du Colombier else
7329ef1f84bSDavid du Colombier msg = unreachcode[p->code];
7339ef1f84bSDavid du Colombier
7349ef1f84bSDavid du Colombier bp->rp += IPICMPSZ;
7359ef1f84bSDavid du Colombier if(blocklen(bp) < 8){
7369ef1f84bSDavid du Colombier ipriv->stats[LenErrs6]++;
7379ef1f84bSDavid du Colombier goto raise;
7389ef1f84bSDavid du Colombier }
7399ef1f84bSDavid du Colombier p = (IPICMP *)bp->rp;
7409ef1f84bSDavid du Colombier pr = Fsrcvpcolx(icmp->f, p->proto);
7419ef1f84bSDavid du Colombier if(pr != nil && pr->advise != nil) {
7429ef1f84bSDavid du Colombier (*pr->advise)(pr, bp, msg);
7439ef1f84bSDavid du Colombier return;
7449ef1f84bSDavid du Colombier }
7459ef1f84bSDavid du Colombier
7469ef1f84bSDavid du Colombier bp->rp -= IPICMPSZ;
7479ef1f84bSDavid du Colombier goticmpkt6(icmp, bp, 0);
7489ef1f84bSDavid du Colombier break;
7499ef1f84bSDavid du Colombier case TimeExceedV6:
7509ef1f84bSDavid du Colombier if(p->code == 0){
751*a3323688SDavid du Colombier snprint(m2, sizeof m2, "ttl exceeded at %I", p->src);
7529ef1f84bSDavid du Colombier bp->rp += IPICMPSZ;
7539ef1f84bSDavid du Colombier if(blocklen(bp) < 8){
7549ef1f84bSDavid du Colombier ipriv->stats[LenErrs6]++;
7559ef1f84bSDavid du Colombier goto raise;
7569ef1f84bSDavid du Colombier }
7579ef1f84bSDavid du Colombier p = (IPICMP *)bp->rp;
7589ef1f84bSDavid du Colombier pr = Fsrcvpcolx(icmp->f, p->proto);
7599ef1f84bSDavid du Colombier if(pr && pr->advise) {
7609ef1f84bSDavid du Colombier (*pr->advise)(pr, bp, m2);
7619ef1f84bSDavid du Colombier return;
7629ef1f84bSDavid du Colombier }
7639ef1f84bSDavid du Colombier bp->rp -= IPICMPSZ;
7649ef1f84bSDavid du Colombier }
7659ef1f84bSDavid du Colombier goticmpkt6(icmp, bp, 0);
7669ef1f84bSDavid du Colombier break;
7679ef1f84bSDavid du Colombier case RouterAdvert:
7689ef1f84bSDavid du Colombier case RouterSolicit:
7699ef1f84bSDavid du Colombier /* using lsrc as a temp, munge hdr for goticmp6 */
7709ef1f84bSDavid du Colombier if (0) {
7719ef1f84bSDavid du Colombier memmove(lsrc, p->src, IPaddrlen);
7729ef1f84bSDavid du Colombier memmove(p->src, p->dst, IPaddrlen);
7739ef1f84bSDavid du Colombier memmove(p->dst, lsrc, IPaddrlen);
7749ef1f84bSDavid du Colombier }
775*a3323688SDavid du Colombier goticmpkt6(icmp, bp, type);
7769ef1f84bSDavid du Colombier break;
7779ef1f84bSDavid du Colombier case NbrSolicit:
778*a3323688SDavid du Colombier np = (Ndpkt*)p; /* within bp */
7799ef1f84bSDavid du Colombier pktflags = 0;
7809ef1f84bSDavid du Colombier switch (targettype(icmp->f, ipifc, np->target)) {
7819ef1f84bSDavid du Colombier case Tunirany:
7829ef1f84bSDavid du Colombier pktflags |= Oflag;
7839ef1f84bSDavid du Colombier /* fall through */
7849ef1f84bSDavid du Colombier case Tuniproxy:
7859ef1f84bSDavid du Colombier if(ipcmp(np->src, v6Unspecified) != 0) {
7869ef1f84bSDavid du Colombier arpenter(icmp->f, V6, np->src, np->lnaddr,
7879ef1f84bSDavid du Colombier 8*np->olen-2, 0);
7889ef1f84bSDavid du Colombier pktflags |= Sflag;
7899ef1f84bSDavid du Colombier }
790*a3323688SDavid du Colombier if(ipv6local(ipifc, lsrc)) {
791*a3323688SDavid du Colombier src = np->src;
792*a3323688SDavid du Colombier if(ipcmp(src, v6Unspecified) == 0)
793*a3323688SDavid du Colombier src = v6allnodesL;
794*a3323688SDavid du Colombier icmpna(icmp->f, lsrc, src, np->target,
795*a3323688SDavid du Colombier ipifc->mac, pktflags);
7969ef1f84bSDavid du Colombier }
7979ef1f84bSDavid du Colombier break;
798*a3323688SDavid du Colombier case Tunitent:
799*a3323688SDavid du Colombier /*
800*a3323688SDavid du Colombier * not clear what needs to be done. send up
801*a3323688SDavid du Colombier * an icmp mesg saying `don't use this address'?
802*a3323688SDavid du Colombier */
803*a3323688SDavid du Colombier break;
804*a3323688SDavid du Colombier }
805*a3323688SDavid du Colombier freeblist(bp);
806*a3323688SDavid du Colombier break;
8079ef1f84bSDavid du Colombier case NbrAdvert:
8089ef1f84bSDavid du Colombier /*
8099ef1f84bSDavid du Colombier * if the target address matches one of the local interface
8109ef1f84bSDavid du Colombier * addresses and the local interface address has tentative bit
8119ef1f84bSDavid du Colombier * set, insert into ARP table. this is so the duplicate address
8129ef1f84bSDavid du Colombier * detection part of ipconfig can discover duplication through
8139ef1f84bSDavid du Colombier * the arp table.
8149ef1f84bSDavid du Colombier */
815*a3323688SDavid du Colombier np = (Ndpkt*)p; /* within bp */
8169ef1f84bSDavid du Colombier lifc = iplocalonifc(ipifc, np->target);
8179ef1f84bSDavid du Colombier arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2,
818*a3323688SDavid du Colombier lifc && lifc->tentative);
8199ef1f84bSDavid du Colombier freeblist(bp);
8209ef1f84bSDavid du Colombier break;
8219ef1f84bSDavid du Colombier case PacketTooBigV6:
8229ef1f84bSDavid du Colombier default:
8239ef1f84bSDavid du Colombier goticmpkt6(icmp, bp, 0);
8249ef1f84bSDavid du Colombier break;
8259ef1f84bSDavid du Colombier }
8269ef1f84bSDavid du Colombier return;
827*a3323688SDavid du Colombier
8289ef1f84bSDavid du Colombier raise:
8299ef1f84bSDavid du Colombier freeblist(bp);
8309ef1f84bSDavid du Colombier }
8319ef1f84bSDavid du Colombier
8329ef1f84bSDavid du Colombier int
icmpstats6(Proto * icmp6,char * buf,int len)8339ef1f84bSDavid du Colombier icmpstats6(Proto *icmp6, char *buf, int len)
8349ef1f84bSDavid du Colombier {
8359ef1f84bSDavid du Colombier Icmppriv6 *priv;
8369ef1f84bSDavid du Colombier char *p, *e;
8379ef1f84bSDavid du Colombier int i;
8389ef1f84bSDavid du Colombier
8399ef1f84bSDavid du Colombier priv = icmp6->priv;
8409ef1f84bSDavid du Colombier p = buf;
8419ef1f84bSDavid du Colombier e = p+len;
8429ef1f84bSDavid du Colombier for(i = 0; i < Nstats6; i++)
8439ef1f84bSDavid du Colombier p = seprint(p, e, "%s: %lud\n", statnames6[i], priv->stats[i]);
8449ef1f84bSDavid du Colombier for(i = 0; i <= Maxtype6; i++)
8459ef1f84bSDavid du Colombier if(icmpnames6[i])
8469ef1f84bSDavid du Colombier p = seprint(p, e, "%s: %lud %lud\n", icmpnames6[i],
8479ef1f84bSDavid du Colombier priv->in[i], priv->out[i]);
848*a3323688SDavid du Colombier else if (0)
8499ef1f84bSDavid du Colombier p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i],
8509ef1f84bSDavid du Colombier priv->out[i]);
8519ef1f84bSDavid du Colombier return p - buf;
8529ef1f84bSDavid du Colombier }
8539ef1f84bSDavid du Colombier
8549ef1f84bSDavid du Colombier /* import from icmp.c */
8559ef1f84bSDavid du Colombier extern int icmpstate(Conv *c, char *state, int n);
8569ef1f84bSDavid du Colombier extern char* icmpannounce(Conv *c, char **argv, int argc);
8579ef1f84bSDavid du Colombier extern char* icmpconnect(Conv *c, char **argv, int argc);
8589ef1f84bSDavid du Colombier extern void icmpclose(Conv *c);
8599ef1f84bSDavid du Colombier
8609ef1f84bSDavid du Colombier void
icmp6init(Fs * fs)8619ef1f84bSDavid du Colombier icmp6init(Fs *fs)
8629ef1f84bSDavid du Colombier {
8639ef1f84bSDavid du Colombier Proto *icmp6 = smalloc(sizeof(Proto));
8649ef1f84bSDavid du Colombier
8659ef1f84bSDavid du Colombier icmp6->priv = smalloc(sizeof(Icmppriv6));
8669ef1f84bSDavid du Colombier icmp6->name = "icmpv6";
8679ef1f84bSDavid du Colombier icmp6->connect = icmpconnect;
8689ef1f84bSDavid du Colombier icmp6->announce = icmpannounce;
8699ef1f84bSDavid du Colombier icmp6->state = icmpstate;
8709ef1f84bSDavid du Colombier icmp6->create = icmpcreate6;
8719ef1f84bSDavid du Colombier icmp6->close = icmpclose;
8729ef1f84bSDavid du Colombier icmp6->rcv = icmpiput6;
8739ef1f84bSDavid du Colombier icmp6->stats = icmpstats6;
8749ef1f84bSDavid du Colombier icmp6->ctl = icmpctl6;
8759ef1f84bSDavid du Colombier icmp6->advise = icmpadvise6;
8769ef1f84bSDavid du Colombier icmp6->gc = nil;
8779ef1f84bSDavid du Colombier icmp6->ipproto = ICMPv6;
8789ef1f84bSDavid du Colombier icmp6->nc = 16;
8799ef1f84bSDavid du Colombier icmp6->ptclsize = sizeof(Icmpcb6);
8809ef1f84bSDavid du Colombier
8819ef1f84bSDavid du Colombier Fsproto(fs, icmp6);
8829ef1f84bSDavid du Colombier }
883