/*
 * Public Release 3
 * 
 * $Id: icmpv6.c,v 1.1.2.2 1999/02/10 14:15:03 wfs Exp $
 */

/*
 * ------------------------------------------------------------------------
 * 
 * Copyright (c) 1996, 1997 The Regents of the University of Michigan
 * All Rights Reserved
 *  
 * Royalty-free licenses to redistribute GateD Release
 * 3 in whole or in part may be obtained by writing to:
 * 
 * 	Merit GateDaemon Project
 * 	4251 Plymouth Road, Suite C
 * 	Ann Arbor, MI 48105
 *  
 * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF THE
 * UNIVERSITY OF MICHIGAN AND MERIT DO NOT WARRANT THAT THE
 * FUNCTIONS CONTAINED IN THE SOFTWARE WILL MEET LICENSEE'S REQUIREMENTS OR
 * THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE. The Regents of the
 * University of Michigan and Merit shall not be liable for
 * any special, indirect, incidental or consequential damages with respect
 * to any claim by Licensee or any third party arising from use of the
 * software. GateDaemon was originated and developed through release 3.0
 * by Cornell University and its collaborators.
 * 
 * Please forward bug fixes, enhancements and questions to the
 * gated mailing list: gated-people@gated.merit.edu.
 * 
 * ------------------------------------------------------------------------
 * 
 * Copyright (c) 1990,1991,1992,1993,1994,1995 by Cornell University.
 *     All rights reserved.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * GateD is based on Kirton's EGP, UC Berkeley's routing
 * daemon	 (routed), and DCN's HELLO routing Protocol.
 * Development of GateD has been supported in part by the
 * National Science Foundation.
 * 
 * ------------------------------------------------------------------------
 * 
 * Portions of this software may fall under the following
 * copyrights:
 * 
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms are
 * permitted provided that the above copyright notice and
 * this paragraph are duplicated in all such forms and that
 * any documentation, advertising materials, and other
 * materials related to such distribution and use
 * acknowledge that the software was developed by the
 * University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote
 * products derived from this software without specific
 * prior written permission.  THIS SOFTWARE IS PROVIDED
 * ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */


/*
 *  Routines for handling ICMPv6 messages
 */

#include "include.h"
#include "inet6.h"
#ifdef  IPV6_NETINET6
#include <netinet6/icmp6.h>
#else
#include <netinet/icmp6.h>
#endif
#include "icmpv6.h"
#include "mld6.h"

#if	defined(PROTO_ICMPV6)

#ifdef	KRT_RT_IOCTL
#define	ICMPV6_PROCESS_REDIRECTS
#endif

task *icmpv6_task = (task *) 0;
trace *icmpv6_trace_options = (trace *) 0;

static int icmpv6_proto = 0;

#ifdef	ICMPV6_SEND
static int icmpv6_unicast_hlim = -1;
static if_addr *icmpv6_multicast_ifap = (if_addr *) 0;           /*the current multicast interface to send */
#endif	/* ICMPV6_SEND */

_PROTOTYPE(icmpv6_methods[256],
	   void,
	   (task *,
	    sockaddr_un *,
	    sockaddr_un *,
	    struct icmp6_hdr *,
	    size_t));

struct icmpv6type {
    const char *typename;
    u_int codes;
    const char *codenames[6];
};

static const struct icmpv6type icmpv6_types[256] =
{
    {"", 0, {""}},
    {"UnReachable", 4,
      {"NoRoute", "Admin", "RouteFailure", "Address", "Port"}},
    {"PacketTooBig", 0, {""}},
    {"TimeExceeded", 1,
      {"InTransit", "Reassembly"}},
    {"ParamProblem", 2,
      {"BadHeader", "NextHeader", "BadOption"}},
    {"ReDirect", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"EchoRequest", 0, {""}},
    {"EchoReply", 0, {""}},
    {"GroupQuery", 0, {""}},
    {"GroupReport", 0, {""}},
    {"GroupTerm", 0, {""}},
    {"RouterSol", 0, {""}},
    {"RouterAdv", 0, {""}},
    {"NeighborSol", 0, {""}},
    {"NeighborAdv", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
    {"", 0, {""}},
};

static const flag_t icmpv6_trace_masks[256] = {
    TR_ALL,			/* 0 - Invalid */
    TR_ICMPV6_DETAIL_ERROR,	/* 1 - Unreachable */
    TR_ICMPV6_DETAIL_INFO,	/* 2 - Packet too Big */
    TR_ICMPV6_DETAIL_ERROR,	/* 3 - Time exceeded */
    TR_ICMPV6_DETAIL_ERROR,	/* 4 - Parameter Problem */
    TR_ICMPV6_DETAIL_REDIRECT,	/* 5 - Redirect */
    TR_ALL,			/* 6 - Invalid */
    TR_ALL,			/* 7 - Invalid */
    TR_ALL,			/* 8 - Invalid */
    TR_ALL,			/* 9 - Invalid */
    TR_ALL,			/* 10 - Invalid */
    TR_ALL,			/* 11 - Invalid */
    TR_ALL,			/* 12 - Invalid */
    TR_ALL,			/* 13 - Invalid */
    TR_ALL,			/* 14 - Invalid */
    TR_ALL,			/* 15 - Invalid */
    TR_ALL,			/* 16 - Invalid */
    TR_ALL,			/* 17 - Invalid */
    TR_ALL,			/* 18 - Invalid */
    TR_ALL,			/* 19 - Invalid */
    TR_ALL,			/* 20 - Invalid */
    TR_ALL,			/* 21 - Invalid */
    TR_ALL,			/* 22 - Invalid */
    TR_ALL,			/* 23 - Invalid */
    TR_ALL,			/* 24 - Invalid */
    TR_ALL,			/* 25 - Invalid */
    TR_ALL,			/* 26 - Invalid */
    TR_ALL,			/* 27 - Invalid */
    TR_ALL,			/* 28 - Invalid */
    TR_ALL,			/* 29 - Invalid */
    TR_ALL,			/* 30 - Invalid */
    TR_ALL,			/* 31 - Invalid */
    TR_ALL,			/* 32 - Invalid */
    TR_ALL,			/* 33 - Invalid */
    TR_ALL,			/* 34 - Invalid */
    TR_ALL,			/* 35 - Invalid */
    TR_ALL,			/* 36 - Invalid */
    TR_ALL,			/* 37 - Invalid */
    TR_ALL,			/* 38 - Invalid */
    TR_ALL,			/* 39 - Invalid */
    TR_ALL,			/* 40 - Invalid */
    TR_ALL,			/* 41 - Invalid */
    TR_ALL,			/* 42 - Invalid */
    TR_ALL,			/* 43 - Invalid */
    TR_ALL,			/* 44 - Invalid */
    TR_ALL,			/* 45 - Invalid */
    TR_ALL,			/* 46 - Invalid */
    TR_ALL,			/* 47 - Invalid */
    TR_ALL,			/* 48 - Invalid */
    TR_ALL,			/* 49 - Invalid */
    TR_ALL,			/* 50 - Invalid */
    TR_ALL,			/* 51 - Invalid */
    TR_ALL,			/* 52 - Invalid */
    TR_ALL,			/* 53 - Invalid */
    TR_ALL,			/* 54 - Invalid */
    TR_ALL,			/* 55 - Invalid */
    TR_ALL,			/* 56 - Invalid */
    TR_ALL,			/* 57 - Invalid */
    TR_ALL,			/* 58 - Invalid */
    TR_ALL,			/* 59 - Invalid */
    TR_ALL,			/* 60 - Invalid */
    TR_ALL,			/* 61 - Invalid */
    TR_ALL,			/* 62 - Invalid */
    TR_ALL,			/* 63 - Invalid */
    TR_ALL,			/* 64 - Invalid */
    TR_ALL,			/* 65 - Invalid */
    TR_ALL,			/* 66 - Invalid */
    TR_ALL,			/* 67 - Invalid */
    TR_ALL,			/* 68 - Invalid */
    TR_ALL,			/* 69 - Invalid */
    TR_ALL,			/* 70 - Invalid */
    TR_ALL,			/* 71 - Invalid */
    TR_ALL,			/* 72 - Invalid */
    TR_ALL,			/* 73 - Invalid */
    TR_ALL,			/* 74 - Invalid */
    TR_ALL,			/* 75 - Invalid */
    TR_ALL,			/* 76 - Invalid */
    TR_ALL,			/* 77 - Invalid */
    TR_ALL,			/* 78 - Invalid */
    TR_ALL,			/* 79 - Invalid */
    TR_ALL,			/* 80 - Invalid */
    TR_ALL,			/* 81 - Invalid */
    TR_ALL,			/* 82 - Invalid */
    TR_ALL,			/* 83 - Invalid */
    TR_ALL,			/* 84 - Invalid */
    TR_ALL,			/* 85 - Invalid */
    TR_ALL,			/* 86 - Invalid */
    TR_ALL,			/* 87 - Invalid */
    TR_ALL,			/* 88 - Invalid */
    TR_ALL,			/* 89 - Invalid */
    TR_ALL,			/* 90 - Invalid */
    TR_ALL,			/* 91 - Invalid */
    TR_ALL,			/* 92 - Invalid */
    TR_ALL,			/* 93 - Invalid */
    TR_ALL,			/* 94 - Invalid */
    TR_ALL,			/* 95 - Invalid */
    TR_ALL,			/* 96 - Invalid */
    TR_ALL,			/* 97 - Invalid */
    TR_ALL,			/* 98 - Invalid */
    TR_ALL,			/* 99 - Invalid */
    TR_ALL,			/* 100 - Invalid */
    TR_ALL,			/* 101 - Invalid */
    TR_ALL,			/* 102 - Invalid */
    TR_ALL,			/* 103 - Invalid */
    TR_ALL,			/* 104 - Invalid */
    TR_ALL,			/* 105 - Invalid */
    TR_ALL,			/* 106 - Invalid */
    TR_ALL,			/* 107 - Invalid */
    TR_ALL,			/* 108 - Invalid */
    TR_ALL,			/* 109 - Invalid */
    TR_ALL,			/* 110 - Invalid */
    TR_ALL,			/* 111 - Invalid */
    TR_ALL,			/* 112 - Invalid */
    TR_ALL,			/* 113 - Invalid */
    TR_ALL,			/* 114 - Invalid */
    TR_ALL,			/* 115 - Invalid */
    TR_ALL,			/* 116 - Invalid */
    TR_ALL,			/* 117 - Invalid */
    TR_ALL,			/* 118 - Invalid */
    TR_ALL,			/* 119 - Invalid */
    TR_ALL,			/* 120 - Invalid */
    TR_ALL,			/* 121 - Invalid */
    TR_ALL,			/* 122 - Invalid */
    TR_ALL,			/* 123 - Invalid */
    TR_ALL,			/* 124 - Invalid */
    TR_ALL,			/* 125 - Invalid */
    TR_ALL,			/* 126 - Invalid */
    TR_ALL,			/* 127 - Invalid */
    TR_ICMPV6_DETAIL_INFO,	/* 128 - Echo Request */
    TR_ICMPV6_DETAIL_INFO,	/* 129 - Echo Reply */
    TR_ICMPV6_DETAIL_INFO,	/* 130 - Group Query */
    TR_ICMPV6_DETAIL_INFO,	/* 131 - Group Report */
    TR_ICMPV6_DETAIL_INFO,	/* 132 - Group Termination */
    TR_ICMPV6_DETAIL_ROUTER,	/* 133 - Router Solicitation */
    TR_ICMPV6_DETAIL_ROUTER,	/* 134 - Router Advertisement */
    TR_ICMPV6_DETAIL_INFO,	/* 135 - Neighbor Solicitation */
    TR_ICMPV6_DETAIL_INFO,	/* 136 - Neighbor Advertisement */
    TR_ALL,			/* 137 - Invalid */
    TR_ALL,			/* 138 - Invalid */
    TR_ALL,			/* 139 - Invalid */
    TR_ALL,			/* 140 - Invalid */
    TR_ALL,			/* 141 - Invalid */
    TR_ALL,			/* 142 - Invalid */
    TR_ALL,			/* 143 - Invalid */
    TR_ALL,			/* 144 - Invalid */
    TR_ALL,			/* 145 - Invalid */
    TR_ALL,			/* 146 - Invalid */
    TR_ALL,			/* 147 - Invalid */
    TR_ALL,			/* 148 - Invalid */
    TR_ALL,			/* 149 - Invalid */
    TR_ALL,			/* 150 - Invalid */
    TR_ALL,			/* 151 - Invalid */
    TR_ALL,			/* 152 - Invalid */
    TR_ALL,			/* 153 - Invalid */
    TR_ALL,			/* 154 - Invalid */
    TR_ALL,			/* 155 - Invalid */
    TR_ALL,			/* 156 - Invalid */
    TR_ALL,			/* 157 - Invalid */
    TR_ALL,			/* 158 - Invalid */
    TR_ALL,			/* 159 - Invalid */
    TR_ALL,			/* 160 - Invalid */
    TR_ALL,			/* 161 - Invalid */
    TR_ALL,			/* 162 - Invalid */
    TR_ALL,			/* 163 - Invalid */
    TR_ALL,			/* 164 - Invalid */
    TR_ALL,			/* 165 - Invalid */
    TR_ALL,			/* 166 - Invalid */
    TR_ALL,			/* 167 - Invalid */
    TR_ALL,			/* 168 - Invalid */
    TR_ALL,			/* 169 - Invalid */
    TR_ALL,			/* 170 - Invalid */
    TR_ALL,			/* 171 - Invalid */
    TR_ALL,			/* 172 - Invalid */
    TR_ALL,			/* 173 - Invalid */
    TR_ALL,			/* 174 - Invalid */
    TR_ALL,			/* 175 - Invalid */
    TR_ALL,			/* 176 - Invalid */
    TR_ALL,			/* 177 - Invalid */
    TR_ALL,			/* 178 - Invalid */
    TR_ALL,			/* 179 - Invalid */
    TR_ALL,			/* 180 - Invalid */
    TR_ALL,			/* 181 - Invalid */
    TR_ALL,			/* 182 - Invalid */
    TR_ALL,			/* 183 - Invalid */
    TR_ALL,			/* 184 - Invalid */
    TR_ALL,			/* 185 - Invalid */
    TR_ALL,			/* 186 - Invalid */
    TR_ALL,			/* 187 - Invalid */
    TR_ALL,			/* 188 - Invalid */
    TR_ALL,			/* 189 - Invalid */
    TR_ALL,			/* 190 - Invalid */
    TR_ALL,			/* 191 - Invalid */
    TR_ALL,			/* 192 - Invalid */
    TR_ALL,			/* 193 - Invalid */
    TR_ALL,			/* 194 - Invalid */
    TR_ALL,			/* 195 - Invalid */
    TR_ALL,			/* 196 - Invalid */
    TR_ALL,			/* 197 - Invalid */
    TR_ALL,			/* 198 - Invalid */
    TR_ALL,			/* 199 - Invalid */
    TR_ALL,			/* 200 - Invalid */
    TR_ALL,			/* 201 - Invalid */
    TR_ALL,			/* 202 - Invalid */
    TR_ALL,			/* 203 - Invalid */
    TR_ALL,			/* 204 - Invalid */
    TR_ALL,			/* 205 - Invalid */
    TR_ALL,			/* 206 - Invalid */
    TR_ALL,			/* 207 - Invalid */
    TR_ALL,			/* 208 - Invalid */
    TR_ALL,			/* 209 - Invalid */
    TR_ALL,			/* 210 - Invalid */
    TR_ALL,			/* 211 - Invalid */
    TR_ALL,			/* 212 - Invalid */
    TR_ALL,			/* 213 - Invalid */
    TR_ALL,			/* 214 - Invalid */
    TR_ALL,			/* 215 - Invalid */
    TR_ALL,			/* 216 - Invalid */
    TR_ALL,			/* 217 - Invalid */
    TR_ALL,			/* 218 - Invalid */
    TR_ALL,			/* 219 - Invalid */
    TR_ALL,			/* 220 - Invalid */
    TR_ALL,			/* 221 - Invalid */
    TR_ALL,			/* 222 - Invalid */
    TR_ALL,			/* 223 - Invalid */
    TR_ALL,			/* 224 - Invalid */
    TR_ALL,			/* 225 - Invalid */
    TR_ALL,			/* 226 - Invalid */
    TR_ALL,			/* 227 - Invalid */
    TR_ALL,			/* 228 - Invalid */
    TR_ALL,			/* 229 - Invalid */
    TR_ALL,			/* 230 - Invalid */
    TR_ALL,			/* 231 - Invalid */
    TR_ALL,			/* 232 - Invalid */
    TR_ALL,			/* 233 - Invalid */
    TR_ALL,			/* 234 - Invalid */
    TR_ALL,			/* 235 - Invalid */
    TR_ALL,			/* 236 - Invalid */
    TR_ALL,			/* 237 - Invalid */
    TR_ALL,			/* 238 - Invalid */
    TR_ALL,			/* 239 - Invalid */
    TR_ALL,			/* 240 - Invalid */
    TR_ALL,			/* 241 - Invalid */
    TR_ALL,			/* 242 - Invalid */
    TR_ALL,			/* 243 - Invalid */
    TR_ALL,			/* 244 - Invalid */
    TR_ALL,			/* 245 - Invalid */
    TR_ALL,			/* 246 - Invalid */
    TR_ALL,			/* 247 - Invalid */
    TR_ALL,			/* 248 - Invalid */
    TR_ALL,			/* 249 - Invalid */
    TR_ALL,			/* 250 - Invalid */
    TR_ALL,			/* 251 - Invalid */
    TR_ALL,			/* 252 - Invalid */
    TR_ALL,			/* 253 - Invalid */
    TR_ALL,			/* 254 - Invalid */
    TR_ALL,			/* 255 - Invalid */
};

const bits icmpv6_trace_types[] = {
    { TR_DETAIL,		"detail packets" },
    { TR_DETAIL_SEND,	"detail send packets" },
    { TR_DETAIL_RECV,	"detail recv packets" },
    { TR_PACKET,		"packets" },
    { TR_PACKET_SEND,	"send packets" },
    { TR_PACKET_RECV,	"recv packets" },
    { TR_DETAIL_1,	"detail redirect" },
    { TR_DETAIL_SEND_1,	"detail send redirect" },
    { TR_DETAIL_RECV_1,	"detail recv redirect" },
    { TR_PACKET_1,	"redirect" },
    { TR_PACKET_SEND_1,	"send redirect" },
    { TR_PACKET_RECV_1,	"recv redirect" },
    { TR_DETAIL_2,	"detail router" },
    { TR_DETAIL_SEND_2,	"detail send router" },
    { TR_DETAIL_RECV_2,	"detail recv router" },
    { TR_PACKET_2,	"router" },
    { TR_PACKET_SEND_2,	"send router" },
    { TR_PACKET_RECV_2,	"recv router" },
    { TR_DETAIL_3,	"detail info" },
    { TR_DETAIL_SEND_3,	"detail send info" },
    { TR_DETAIL_RECV_3,	"detail recv info" },
    { TR_PACKET_3,	"info" },
    { TR_PACKET_SEND_3,	"send info" },
    { TR_PACKET_RECV_3,	"recv info" },
    { TR_DETAIL_4,	"detail error" },
    { TR_DETAIL_SEND_4,	"detail send error" },
    { TR_DETAIL_RECV_4,	"detail recv error" },
    { TR_PACKET_4,	"error" },
    { TR_PACKET_SEND_4,	"send error" },
    { TR_PACKET_RECV_4,	"recv error" },
    { 0, NULL }
};


/*
 * Trace received ICMPv6 messages
 */
static void
icmpv6_trace __PF5(from, sockaddr_un *,
		   to, sockaddr_un *,
		   dir, const char *,
		   icmp, struct icmp6_hdr *,
		   detail, int)
{
    const char *type_name, *code_name;
    const struct icmpv6type *itp;
    struct nd_redirect *red;
    struct mld6_hdr *mld6;
    struct nd_neighbor_advert *nd_nbr;

    itp = &icmpv6_types[icmp->icmp6_type];
    type_name = itp->typename;
    if (icmp->icmp6_code <= itp->codes) {
      code_name = itp->codenames[icmp->icmp6_code];
    } else {
      code_name = "Invalid";
    }

    tracef("ICMPv6 %s %A ",
	   dir,
	   from);

    if (to) {
	tracef("-> %A ",
	       to);
    }

    tracef("type %s(%u) code %s(%u)",
	   type_name,
	   icmp->icmp6_type,
	   code_name,
	   icmp->icmp6_code);

    if (detail) {
	int do_dest = FALSE;
	int do_idseq = FALSE;
    
	switch (icmp->icmp6_type) {
	case ICMP6_DST_UNREACH:
	case ICMP6_PACKET_TOO_BIG:
	case ICMP6_TIME_EXCEEDED:
	case ICMP6_PARAM_PROB:
	    do_dest = TRUE;
	    break;

	case ND_REDIRECT:
	    red = (struct nd_redirect *)icmp;
	    tracef(" dest %A via %A",
		   sockbuild_in6(0, (byte *) &red->nd_rd_dst),
		   sockbuild_in6(0, (byte *) &red->nd_rd_target));
	    break;

	case ICMP6_ECHO_REQUEST:
	case ICMP6_ECHO_REPLY:
	    do_idseq = TRUE;
	    break;

#ifdef MLD6_LISTENER_QUERY
	case MLD6_LISTENER_QUERY:
	case MLD6_LISTENER_REPORT:
	case MLD6_LISTENER_DONE:
#else
	case ICMP6_MEMBERSHIP_QUERY:
	case ICMP6_MEMBERSHIP_REPORT:
	case ICMP6_MEMBERSHIP_REDUCTION :
#endif
#ifdef 0
	    mld6 = (struct mld6_hdr *)icmp;
	    tracef(" group %A",
		   sockbuild_in6(0, (byte *) &mld6->mld6_addr));
	    break;
#endif
	case ND_ROUTER_SOLICIT:
	    break;

	case ND_ROUTER_ADVERT:
	    {
		trace_only_tf(icmpv6_trace_options,
			      0,
			      (NULL));
		tracef("ICMPv6 %s ...",
		       dir);
	    }
	    break;

	case ND_NEIGHBOR_SOLICIT:
	case ND_NEIGHBOR_ADVERT:
	    nd_nbr = (struct nd_neighbor_advert *)icmp;
	    tracef(" target %A",
		   sockbuild_in6(0, (byte *) &nd_nbr->nd_na_target));
	    break;
	}

	if (do_idseq) {
	    tracef(" id %u sequence %u",
		   ntohs(icmp->icmp6_id),
		   ntohs(icmp->icmp6_seq));
	}
#if 0
	if (do_dest) {
	    tracef(" dest %A protocol %s(%u)",
		   sockbuild_in6(0, (byte *) &icmp->icmp6_ip.ip6_dst),
		   trace_value(inet6_proto_bits, icmp->icmp6_ip.ip6_nh),
		   icmp->icmp6_ip.ip6_nh);
	}
#endif
    }
    trace_only_tf(icmpv6_trace_options,
		  TRC_NL_AFTER,
		  (NULL));
}


#ifdef	ICMPV6_PROCESS_REDIRECTS
/**/

static task_job *icmpv6_redirect_job = (task_job *) 0;
static block_t icmpv6_block_index;

struct icmpv6_redirect_entry {
    struct icmpv6_redirect_entry *ire_forw, *ire_back;
    struct in6_addr ire_author;		/* Source of the packet */
    struct in6_addr ire_router;		/* New gateway */
    struct in6_addr ire_dest;		/* Destination to redirect */
} ;

static struct icmpv6_redirect_entry icmpv6_redirect_list = { &icmpv6_redirect_list, &icmpv6_redirect_list };


static void
icmpv6_redirect_process __PF1(jp, task_job *)
{
    register struct icmpv6_redirect_entry *rep;

    while ((rep = icmpv6_redirect_list.ire_forw) != &icmpv6_redirect_list) {
	sockaddr_un *author, *router, *dest;

	author = sockdup(sockbuild_in6(0, (byte *) &rep->ire_author));
	router = sockdup(sockbuild_in6(0, (byte *) &rep->ire_router));
	dest = sockdup(sockbuild_in6(0, (byte *) &rep->ire_dest));

	redirect(dest, inet6_mask_host, router, author);

	sockfree(dest);
	sockfree(author);
	sockfree(router);

	REMQUE(rep);
	task_block_free(icmpv6_block_index, (void_t) rep);
    }

    icmpv6_redirect_job = (task_job *) 0;
}


static void
icmpv6_recv_redirect __PF5(tp, task *,
			   src, sockaddr_un *,
			   dst, sockaddr_un *,
			   icmp, struct icmpv6 *,
			   len, size_t)
{
    register struct icmpv6_redirect_entry *rep;
    struct icmpv6_redirect_entry re;
    struct icmpv6_redirect_entry *il;

    COPY_ADDR6(sock2in6(src), re.ire_author);
    COPY_ADDR6(icmp->icmp6_tgt, re.ire_router);
    COPY_ADDR6(icmp->icmp6_rdst, re.ire_dest);

    /* Check for duplicate */
    for (rep = icmpv6_redirect_list.ire_forw; rep != &icmpv6_redirect_list; rep = rep->ire_forw) {
	if (CMP_ADDR6(rep->ire_author, re.ire_author)
	    && CMP_ADDR6(rep->ire_router, re.ire_router)
	    && CMP_ADDR6(rep->ire_dest, re.ire_dest)) {
	    /* Found a duplicate */

	    return;
	}
    }

    /* Add to list */
    il = (struct icmpv6_redirect_entry *) task_block_alloc(icmpv6_block_index);
    *il = re;	/* struct copy */
    INSQUE(il, icmpv6_redirect_list.ire_back);

    if (!icmpv6_redirect_job) {
	/* Create the job */
	
	icmpv6_redirect_job = task_job_create(tp,
					      TASK_JOB_FG,
					      "icmpv6_redirect_job",
					      icmpv6_redirect_process,
					      (void_t) 0);
    }
}
#endif	/* ICMPV6_PROCESS_REDIRECTS */


/*
 * icmpv6_recv() handles ICMPv6 redirect messages.
 */
static void
icmpv6_recv __PF1(tp, task *)
{
    int n_packets = TASK_PACKET_LIMIT * 10;
    size_t count;

    while (n_packets-- && !task_receive_packet(tp, &count)) {
	struct icmp6_hdr *icmp;
	sockaddr_un *dst;

	icmp = task_get_recv_buffer(struct icmp6_hdr *);
	if (task_recv_dstaddr
	    && !IS_ANYADDR6(sock2in6(task_recv_dstaddr))) {
	    /* Destination address is valid */
	    dst = task_recv_dstaddr;
	} else {
	    /* Destination address is not valid */
	    dst = (sockaddr_un *) 0;
	}

	if (TRACE_PACKET_RECV_TP(tp,
				 icmp->icmp6_type,
				 ICMP6_MAXTYPE,
				 icmpv6_trace_masks)) {
	    icmpv6_trace(task_recv_srcaddr,
			 dst,
			 "RECV",
			 icmp,
			 TRACE_DETAIL_RECV_TP(tp,
					      icmp->icmp6_type,
					      ICMP6_MAXTYPE,
					      icmpv6_trace_masks));
	}

	if (icmpv6_methods[icmp->icmp6_type]) {
	    /* Call the routine to process this packet */

	    icmpv6_methods[icmp->icmp6_type](tp,
					     task_recv_srcaddr,
					     dst,
					     icmp,
					     count);
	}
    }
}


#ifdef	ICMPV6_SEND
/* 
 *	Send an ICMPv6 packet
 */
int
icmpv6_send __PF6(icmp, struct icmp6_hdr *,
		  len, size_t,
		  src, sockaddr_un *,
		  dest, sockaddr_un *,
		  ifap, if_addr *,
		  flags, flag_t)
{
    int rc;

    /* Checksum will be calculated by the kernel */
    icmp->icmp6_cksum = 0;

    if (IN6_IS_ADDR_MULTICAST(&sock2in6(dest))) { /* multicast processing */
	
	/* Multicast sends fail if MSG_DONTROUTE is set */
	BIT_RESET(flags, MSG_DONTROUTE);
	
	if (icmpv6_multicast_ifap != ifap) {
	    /* reset the interface on which to multicast */
	    IFA_FREE(icmpv6_multicast_ifap);
	    IFA_ALLOC(icmpv6_multicast_ifap = ifap);
	    if (task_set_option(icmpv6_task,
				TASKOPTION_MULTI_IF,
				icmpv6_multicast_ifap) < 0) {
		task_quit(errno);
	    }
	}
    } else {			/* Unicast processing */
	int hlim = 1;

	if (hlim != icmpv6_unicast_hlim) {
	    /* Set the host limit */

	    (void) task_set_option(icmpv6_task,
				   TASKOPTION_TTL,
				   icmpv6_unicast_hlim = hlim);
	}
    }
    
    /* bind the source */
    if (task_addr_local(icmpv6_task, src)) {
    	task_quit(errno);
    }

    /* send the packet */
    rc = task_send_message(icmpv6_task, 
			   (void_t) icmp,
			   len,
			   flags,
			   dest,
			   ifap);

    if (TRACE_PACKET_SEND_TP(icmpv6_task,
			     icmp->icmp6_type,
			     255,
			     icmpv6_trace_masks)) {
	icmpv6_trace(ifap->ifa_addr_local,
		     dest,
		     "SEND",
		     icmp,
		     TRACE_DETAIL_SEND_TP(icmpv6_task,
					  icmp->icmp6_type,
					  255,
					  icmpv6_trace_masks));
	}
    return rc;
}


/*
 *  Initialize ICMPv6 socket and ICMPv6 task
 */

static void
icmpv6_ifachange __PF2(tp, task *,
		       ifap, if_addr *)
{
    switch (ifap->ifa_change) {
    case IFC_NOCHANGE:
    case IFC_ADD:
	break;
    
    case IFC_DELETE:
	break;
    
    case IFC_DELETE|IFC_UPDOWN:
    Down:
	/* mark addr as down */
	if (ifap == icmpv6_multicast_ifap) {
	    IFA_FREE(icmpv6_multicast_ifap);
	    icmpv6_multicast_ifap = (if_addr *) 0;
	}	
	break;
    
    default:
	/* Something has changed */
  
	if (BIT_TEST(ifap->ifa_change, IFC_UPDOWN)) {
	    if (!BIT_TEST(ifap->ifa_state, IFS_UP)) {
		/* Transition to DOWN */
	
		goto Down;
	    }
	}
    }
}
#endif	/* ICMPV6_SEND */


/*
 *  Status dump
 */
static void
icmpv6_dump __PF2(tp, task *,
		  fp, FILE *)
{
    mld6_dump(tp, fp);
}


void
icmpv6_var_init __PF0(void)
{
    int i;

    for (i = 0; i < 256; i++) {
	icmpv6_methods[i] = NULL;
    }
    mld6_var_init();
}


static void
icmpv6_cleanup __PF1(tp, task *)
{
#if defined(ICMPV6_SEND)
    if (icmpv6_multicast_ifap) {
	IFA_FREE(icmpv6_multicast_ifap);
	icmpv6_multicast_ifap = (if_addr *) 0;
    }
#endif

    trace_freeup(icmpv6_trace_options);
    trace_freeup(tp->task_trace);
}


static void
icmpv6_reinit __PF1(tp, task *)
{
    trace_inherit_global(icmpv6_trace_options, icmpv6_trace_types, (flag_t) 0);
    trace_set(tp->task_trace, icmpv6_trace_options);
#ifdef ICMPV6_PROCESS_REDIRECTS
    icmpv6_methods[ICMPV6_REDIRECT] = icmpv6_recv_redirect;
#endif
#ifdef MLD6_LISTENER_QUERY
    icmpv6_methods[MLD6_LISTENER_QUERY] = mld6_recv;
    icmpv6_methods[MLD6_LISTENER_REPORT] = mld6_recv;
    icmpv6_methods[MLD6_LISTENER_DONE] = mld6_recv;
#else
    icmpv6_methods[ICMP6_MEMBERSHIP_QUERY] = mld6_recv;
    icmpv6_methods[ICMP6_MEMBERSHIP_REPORT] = mld6_recv;
    icmpv6_methods[ICMP6_MEMBERSHIP_REDUCTION] = mld6_recv;
#endif

}


static void
icmpv6_terminate __PF1(tp, task *)
{
    icmpv6_cleanup(tp);

    task_delete(tp);
}


void
icmpv6_init __PF0(void)
{

    if (!icmpv6_task) {
	trace_inherit_global(icmpv6_trace_options, icmpv6_trace_types, (flag_t) 0);
	if (!icmpv6_proto) {
	    icmpv6_proto = task_get_proto(icmpv6_trace_options,
					  "icmpv6",
					  IPPROTO_ICMPV6);
	}
	icmpv6_task = task_alloc("ICMPV6",
				 TASKPRI_ICMP,
				 icmpv6_trace_options);
	icmpv6_task->task_proto = icmpv6_proto;
	task_set_recv(icmpv6_task, icmpv6_recv);
	task_set_cleanup(icmpv6_task, icmpv6_cleanup);
	task_set_reinit(icmpv6_task, icmpv6_reinit);
	task_set_terminate(icmpv6_task, icmpv6_terminate);
	task_set_dump(icmpv6_task, icmpv6_dump);
#ifdef	ICMPV6_SEND
	task_set_ifachange(icmpv6_task, icmpv6_ifachange);
#endif	/* ICMPV6_SEND */
	if ((icmpv6_task->task_socket = task_get_socket(icmpv6_task, AF_INET6, SOCK_RAW, icmpv6_proto)) < 0) {
	    task_quit(errno);
	}
	if (!task_create(icmpv6_task)) {
	    task_quit(EINVAL);
	}

        if (task_set_option(icmpv6_task,
			    TASKOPTION_NONBLOCKING,
			    TRUE) < 0) {
	    task_quit(errno);
        }
	/* Disable reception of our own packets */
#ifndef PROTO_INET6
	if (task_set_option(icmpv6_task,
			    TASKOPTION_MULTI_LOOP,
			    FALSE) < 0) {
	    task_quit(errno);
	}
	/* Assume for now that only local wire multicasts will be sent */
	if (task_set_option(icmpv6_task,
			    TASKOPTION_MULTI_TTL,
			    1) < 0) {
	    task_quit(errno);
	}
#endif
	task_alloc_recv(icmpv6_task, ICMPV6_PACKET_MAX);

	/* Do all we can to avoid losing packets */
	if (task_set_option(icmpv6_task,
			    TASKOPTION_RECVBUF,
			    task_maxpacket) < 0) {
	    task_quit(errno);
	}
	/* Initialize Multicast Listener Discovery */
	mld6_init();

#ifdef	ICMPV6_PROCESS_REDIRECTS
	icmpv6_block_index = task_block_init(sizeof (struct icmpv6_redirect_entry), "icmpv6_redirect_entry");
#endif	/* ICMPV6_PROCESS_REDIRECTS */
    }
}

#endif	/* defined(PROTO_ICMPV6) */

