summaryrefslogtreecommitdiff
path: root/pfinet/linux-src/net/ipv6/icmpv6.c
diff options
context:
space:
mode:
authorStefan Siegl <stesie@brokenpipe.de>2007-10-08 21:12:31 +0000
committerStefan Siegl <stesie@brokenpipe.de>2007-10-08 21:12:31 +0000
commit9ef3092f3172c049beb847d1dca602e2b4b755c5 (patch)
tree7503d041a15e810376fa865425b10afbb3507d74 /pfinet/linux-src/net/ipv6/icmpv6.c
parent4f5ec0e4d340aa0cc9c651a80e8d5af91d98924e (diff)
2007-10-08 Stefan Siegl <stesie@brokenpipe.de>
Import a number of files from Linux 2.2.14. Renamed some of them, to have unique filenames as required by the make system. * linux-src/net/ipv6/addrconf.c: Import file. * linux-src/net/ipv6/af_inet6.c: Likewise. * linux-src/net/ipv6/exthdrs.c: Likewise. * linux-src/net/ipv6/ip6_fib.c: Likewise. * linux-src/net/ipv6/ip6_flowlabel.c: Likewise. * linux-src/net/ipv6/ip6_input.c: Likewise. * linux-src/net/ipv6/ip6_output.c: Likewise. * linux-src/net/ipv6/ipv6_sockglue.c: Likewise. * linux-src/net/ipv6/mcast.c: Likewise. * linux-src/net/ipv6/ndisc.c: Likewise. * linux-src/net/ipv6/reassembly.c: Likewise. * linux-src/net/ipv6/tcp_ipv6.c: Likewise. * linux-src/net/ipv6/datagram_ipv6.c: Import file (was datagram.c). * linux-src/net/ipv6/icmpv6.c: Import file (was icmp.c). * linux-src/net/ipv6/protocol_ipv6.c: Import file (was protocol.c). * linux-src/net/ipv6/raw_ipv6.c: Import file (was raw.c). * linux-src/net/ipv6/route_ipv6.c: Import file (was route.c). * linux-src/net/ipv6/udp_ipv6.c: Import file (was udp.c).
Diffstat (limited to 'pfinet/linux-src/net/ipv6/icmpv6.c')
-rw-r--r--pfinet/linux-src/net/ipv6/icmpv6.c673
1 files changed, 673 insertions, 0 deletions
diff --git a/pfinet/linux-src/net/ipv6/icmpv6.c b/pfinet/linux-src/net/ipv6/icmpv6.c
new file mode 100644
index 00000000..de5e7780
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/icmpv6.c
@@ -0,0 +1,673 @@
+/*
+ * Internet Control Message Protocol (ICMPv6)
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: icmpv6.c,v 1.1 2007/10/08 21:12:30 stesie Exp $
+ *
+ * Based on net/ipv4/icmp.c
+ *
+ * RFC 1885
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * Changes:
+ *
+ * Andi Kleen : exception handling
+ * Andi Kleen add rate limits. never reply to a icmp.
+ * add more length checks and other fixes.
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/icmpv6.h>
+
+#include <net/ip.h>
+#include <net/sock.h>
+
+#include <net/ipv6.h>
+#include <net/checksum.h>
+#include <net/protocol.h>
+#include <net/raw.h>
+#include <net/rawv6.h>
+#include <net/transp_v6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/icmp.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+struct icmpv6_mib icmpv6_statistics;
+
+/*
+ * ICMP socket for flow control.
+ */
+
+struct socket *icmpv6_socket;
+
+int icmpv6_rcv(struct sk_buff *skb, unsigned long len);
+
+static struct inet6_protocol icmpv6_protocol =
+{
+ icmpv6_rcv, /* handler */
+ NULL, /* error control */
+ NULL, /* next */
+ IPPROTO_ICMPV6, /* protocol ID */
+ 0, /* copy */
+ NULL, /* data */
+ "ICMPv6" /* name */
+};
+
+struct icmpv6_msg {
+ struct icmp6hdr icmph;
+ __u8 *data;
+ struct in6_addr *daddr;
+ int len;
+ __u32 csum;
+};
+
+
+
+/*
+ * getfrag callback
+ */
+
+static int icmpv6_getfrag(const void *data, struct in6_addr *saddr,
+ char *buff, unsigned int offset, unsigned int len)
+{
+ struct icmpv6_msg *msg = (struct icmpv6_msg *) data;
+ struct icmp6hdr *icmph;
+ __u32 csum;
+
+ /*
+ * in theory offset must be 0 since we never send more
+ * than IPV6_MIN_MTU bytes on an error or more than the path mtu
+ * on an echo reply. (those are the rules on RFC 1883)
+ *
+ * Luckily, this statement is obsolete after
+ * draft-ietf-ipngwg-icmp-v2-00 --ANK (980730)
+ */
+
+ if (offset) {
+ csum = csum_partial_copy((void *) msg->data +
+ offset - sizeof(struct icmp6hdr),
+ buff, len, msg->csum);
+ msg->csum = csum;
+ return 0;
+ }
+
+ csum = csum_partial_copy((void *) &msg->icmph, buff,
+ sizeof(struct icmp6hdr), msg->csum);
+
+ csum = csum_partial_copy((void *) msg->data,
+ buff + sizeof(struct icmp6hdr),
+ len - sizeof(struct icmp6hdr), csum);
+
+ icmph = (struct icmp6hdr *) buff;
+
+ icmph->icmp6_cksum = csum_ipv6_magic(saddr, msg->daddr, msg->len,
+ IPPROTO_ICMPV6, csum);
+ return 0;
+}
+
+
+/*
+ * Slightly more convenient version of icmpv6_send.
+ */
+void icmpv6_param_prob(struct sk_buff *skb, int code, void *pos)
+{
+ int offset = (u8*)pos - (u8*)skb->nh.ipv6h;
+
+ icmpv6_send(skb, ICMPV6_PARAMPROB, code, offset, skb->dev);
+ kfree_skb(skb);
+}
+
+/*
+ * Figure out, may we reply to this packet with icmp error.
+ *
+ * We do not reply, if:
+ * - it was icmp error message.
+ * - it is truncated, so that it is known, that protocol is ICMPV6
+ * (i.e. in the middle of some exthdr)
+ * - it is not the first fragment. BTW IPv6 specs say nothing about
+ * this case, but it is clear, that our reply would be useless
+ * for sender.
+ *
+ * --ANK (980726)
+ */
+
+static int is_ineligible(struct ipv6hdr *hdr, int len)
+{
+ u8 *ptr;
+ __u8 nexthdr = hdr->nexthdr;
+
+ if (len < (int)sizeof(*hdr))
+ return 1;
+
+ ptr = ipv6_skip_exthdr((struct ipv6_opt_hdr *)(hdr+1), &nexthdr, len - sizeof(*hdr));
+ if (!ptr)
+ return 0;
+ if (nexthdr == IPPROTO_ICMPV6) {
+ struct icmp6hdr *ihdr = (struct icmp6hdr *)ptr;
+ return (ptr - (u8*)hdr) > len || !(ihdr->icmp6_type & 0x80);
+ }
+ return nexthdr == NEXTHDR_FRAGMENT;
+}
+
+int sysctl_icmpv6_time = 1*HZ;
+
+/*
+ * Check the ICMP output rate limit
+ */
+static inline int icmpv6_xrlim_allow(struct sock *sk, int type,
+ struct flowi *fl)
+{
+ struct dst_entry *dst;
+ int res = 0;
+
+ /* Informational messages are not limited. */
+ if (type & 0x80)
+ return 1;
+
+ /* Do not limit pmtu discovery, it would break it. */
+ if (type == ICMPV6_PKT_TOOBIG)
+ return 1;
+
+ /*
+ * Look up the output route.
+ * XXX: perhaps the expire for routing entries cloned by
+ * this lookup should be more aggressive (not longer than timeout).
+ */
+ dst = ip6_route_output(sk, fl);
+ if (dst->error) {
+ ipv6_statistics.Ip6OutNoRoutes++;
+ } else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) {
+ res = 1;
+ } else {
+ struct rt6_info *rt = (struct rt6_info *)dst;
+ int tmo = sysctl_icmpv6_time;
+
+ /* Give more bandwidth to wider prefixes. */
+ if (rt->rt6i_dst.plen < 128)
+ tmo >>= ((128 - rt->rt6i_dst.plen)>>5);
+
+ res = xrlim_allow(dst, tmo);
+ }
+ dst_release(dst);
+ return res;
+}
+
+/*
+ * an inline helper for the "simple" if statement below
+ * checks if parameter problem report is caused by an
+ * unrecognized IPv6 option that has the Option Type
+ * highest-order two bits set to 10
+ */
+
+static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
+{
+ u8 *buff = skb->nh.raw;
+
+ return ( ( *(buff + offset) & 0xC0 ) == 0x80 );
+}
+
+/*
+ * Send an ICMP message in response to a packet in error
+ */
+
+void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
+ struct device *dev)
+{
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct sock *sk = icmpv6_socket->sk;
+ struct in6_addr *saddr = NULL;
+ int iif = 0;
+ struct icmpv6_msg msg;
+ struct flowi fl;
+ int addr_type = 0;
+ int len;
+
+ /*
+ * sanity check pointer in case of parameter problem
+ */
+
+ if (type == ICMPV6_PARAMPROB &&
+ (info > (skb->tail - ((unsigned char *) hdr)))) {
+ printk(KERN_DEBUG "icmpv6_send: bug! pointer > skb\n");
+ return;
+ }
+
+ /*
+ * Make sure we respect the rules
+ * i.e. RFC 1885 2.4(e)
+ * Rule (e.1) is enforced by not using icmpv6_send
+ * in any code that processes icmp errors.
+ */
+
+ addr_type = ipv6_addr_type(&hdr->daddr);
+
+ if (ipv6_chk_addr(&hdr->daddr, skb->dev, 0))
+ saddr = &hdr->daddr;
+
+ /*
+ * Dest addr check
+ */
+
+ if ((addr_type & IPV6_ADDR_MULTICAST || skb->pkt_type != PACKET_HOST)) {
+ if (type != ICMPV6_PKT_TOOBIG &&
+ !(type == ICMPV6_PARAMPROB &&
+ code == ICMPV6_UNK_OPTION &&
+ (opt_unrec(skb, info))))
+ return;
+
+ saddr = NULL;
+ }
+
+ addr_type = ipv6_addr_type(&hdr->saddr);
+
+ /*
+ * Source addr check
+ */
+
+ if (addr_type & IPV6_ADDR_LINKLOCAL)
+ iif = skb->dev->ifindex;
+
+ /*
+ * Must not send if we know that source is Anycast also.
+ * for now we don't know that.
+ */
+ if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
+ printk(KERN_DEBUG "icmpv6_send: addr_any/mcast source\n");
+ return;
+ }
+
+ /*
+ * Never answer to a ICMP packet.
+ */
+ if (is_ineligible(hdr, (u8*)skb->tail - (u8*)hdr)) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "icmpv6_send: no reply to icmp error/fragment\n");
+ return;
+ }
+
+ fl.proto = IPPROTO_ICMPV6;
+ fl.nl_u.ip6_u.daddr = &hdr->saddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.oif = iif;
+ fl.fl6_flowlabel = 0;
+ fl.uli_u.icmpt.type = type;
+ fl.uli_u.icmpt.code = code;
+
+ if (!icmpv6_xrlim_allow(sk, type, &fl))
+ return;
+
+ /*
+ * ok. kick it. checksum will be provided by the
+ * getfrag_t callback.
+ */
+
+ msg.icmph.icmp6_type = type;
+ msg.icmph.icmp6_code = code;
+ msg.icmph.icmp6_cksum = 0;
+ msg.icmph.icmp6_pointer = htonl(info);
+
+ msg.data = skb->nh.raw;
+ msg.csum = 0;
+ msg.daddr = &hdr->saddr;
+
+ len = min((skb->tail - ((unsigned char *) hdr)) + sizeof(struct icmp6hdr),
+ IPV6_MIN_MTU - sizeof(struct ipv6hdr));
+
+ if (len < 0) {
+ printk(KERN_DEBUG "icmp: len problem\n");
+ return;
+ }
+
+ msg.len = len;
+
+ ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
+ MSG_DONTWAIT);
+ if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
+ (&icmpv6_statistics.Icmp6OutDestUnreachs)[type-1]++;
+ icmpv6_statistics.Icmp6OutMsgs++;
+}
+
+static void icmpv6_echo_reply(struct sk_buff *skb)
+{
+ struct sock *sk = icmpv6_socket->sk;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
+ struct in6_addr *saddr;
+ struct icmpv6_msg msg;
+ struct flowi fl;
+ unsigned char *data;
+ int len;
+
+ data = (char *) (icmph + 1);
+
+ saddr = &hdr->daddr;
+
+ if (ipv6_addr_type(saddr) & IPV6_ADDR_MULTICAST)
+ saddr = NULL;
+
+ len = skb->tail - data;
+ len += sizeof(struct icmp6hdr);
+
+ msg.icmph.icmp6_type = ICMPV6_ECHO_REPLY;
+ msg.icmph.icmp6_code = 0;
+ msg.icmph.icmp6_cksum = 0;
+ msg.icmph.icmp6_identifier = icmph->icmp6_identifier;
+ msg.icmph.icmp6_sequence = icmph->icmp6_sequence;
+
+ msg.data = data;
+ msg.csum = 0;
+ msg.len = len;
+ msg.daddr = &hdr->saddr;
+
+ fl.proto = IPPROTO_ICMPV6;
+ fl.nl_u.ip6_u.daddr = &hdr->saddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.oif = skb->dev->ifindex;
+ fl.fl6_flowlabel = 0;
+ fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY;
+ fl.uli_u.icmpt.code = 0;
+
+ ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
+ MSG_DONTWAIT);
+ icmpv6_statistics.Icmp6OutEchoReplies++;
+ icmpv6_statistics.Icmp6OutMsgs++;
+}
+
+static void icmpv6_notify(struct sk_buff *skb,
+ int type, int code, u32 info, unsigned char *buff, int len)
+{
+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+ struct ipv6hdr *hdr = (struct ipv6hdr *) buff;
+ struct inet6_protocol *ipprot;
+ struct sock *sk;
+ u8 *pb;
+ int hash;
+ u8 nexthdr;
+
+ nexthdr = hdr->nexthdr;
+
+ len -= sizeof(struct ipv6hdr);
+ if (len < 0)
+ return;
+
+ /* now skip over extension headers */
+ pb = ipv6_skip_exthdr((struct ipv6_opt_hdr *) (hdr + 1), &nexthdr, len);
+ if (!pb)
+ return;
+
+ /* BUGGG_FUTURE: we should try to parse exthdrs in this packet.
+ Without this we will not able f.e. to make source routed
+ pmtu discovery.
+ Corresponding argument (opt) to notifiers is already added.
+ --ANK (980726)
+ */
+
+ hash = nexthdr & (MAX_INET_PROTOS - 1);
+
+ for (ipprot = (struct inet6_protocol *) inet6_protos[hash];
+ ipprot != NULL;
+ ipprot=(struct inet6_protocol *)ipprot->next) {
+ if (ipprot->protocol != nexthdr)
+ continue;
+
+ if (ipprot->err_handler)
+ ipprot->err_handler(skb, hdr, NULL, type, code, pb, info);
+ }
+
+ sk = raw_v6_htable[hash];
+
+ if (sk == NULL)
+ return;
+
+ while((sk = raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
+ rawv6_err(sk, skb, hdr, NULL, type, code, pb, info);
+ sk = sk->next;
+ }
+}
+
+/*
+ * Handle icmp messages
+ */
+
+int icmpv6_rcv(struct sk_buff *skb, unsigned long len)
+{
+ struct device *dev = skb->dev;
+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+ struct ipv6hdr *orig_hdr;
+ struct icmp6hdr *hdr = (struct icmp6hdr *) skb->h.raw;
+ int ulen;
+ int type;
+
+ icmpv6_statistics.Icmp6InMsgs++;
+
+ if (len < sizeof(struct icmp6hdr))
+ goto discard_it;
+
+ /* Perform checksum. */
+ switch (skb->ip_summed) {
+ case CHECKSUM_NONE:
+ skb->csum = csum_partial((char *)hdr, len, 0);
+ case CHECKSUM_HW:
+ if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6,
+ skb->csum)) {
+ printk(KERN_DEBUG "ICMPv6 checksum failed [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x > %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+ ntohs(saddr->in6_u.u6_addr16[0]),
+ ntohs(saddr->in6_u.u6_addr16[1]),
+ ntohs(saddr->in6_u.u6_addr16[2]),
+ ntohs(saddr->in6_u.u6_addr16[3]),
+ ntohs(saddr->in6_u.u6_addr16[4]),
+ ntohs(saddr->in6_u.u6_addr16[5]),
+ ntohs(saddr->in6_u.u6_addr16[6]),
+ ntohs(saddr->in6_u.u6_addr16[7]),
+ ntohs(daddr->in6_u.u6_addr16[0]),
+ ntohs(daddr->in6_u.u6_addr16[1]),
+ ntohs(daddr->in6_u.u6_addr16[2]),
+ ntohs(daddr->in6_u.u6_addr16[3]),
+ ntohs(daddr->in6_u.u6_addr16[4]),
+ ntohs(daddr->in6_u.u6_addr16[5]),
+ ntohs(daddr->in6_u.u6_addr16[6]),
+ ntohs(daddr->in6_u.u6_addr16[7]));
+ goto discard_it;
+ }
+ default:
+ /* CHECKSUM_UNNECESSARY */
+ };
+
+ /*
+ * length of original packet carried in skb
+ */
+ ulen = skb->tail - (unsigned char *) (hdr + 1);
+
+ type = hdr->icmp6_type;
+
+ if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
+ (&icmpv6_statistics.Icmp6InDestUnreachs)[type-ICMPV6_DEST_UNREACH]++;
+ else if (type >= ICMPV6_ECHO_REQUEST && type <= NDISC_REDIRECT)
+ (&icmpv6_statistics.Icmp6InEchos)[type-ICMPV6_ECHO_REQUEST]++;
+
+ switch (type) {
+
+ case ICMPV6_ECHO_REQUEST:
+ icmpv6_echo_reply(skb);
+ break;
+
+ case ICMPV6_ECHO_REPLY:
+ /* we coulnd't care less */
+ break;
+
+ case ICMPV6_PKT_TOOBIG:
+ /* BUGGG_FUTURE: if packet contains rthdr, we cannot update
+ standard destination cache. Seems, only "advanced"
+ destination cache will allow to solve this problem
+ --ANK (980726)
+ */
+ orig_hdr = (struct ipv6hdr *) (hdr + 1);
+ if (ulen >= sizeof(struct ipv6hdr))
+ rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev,
+ ntohl(hdr->icmp6_mtu));
+
+ /*
+ * Drop through to notify
+ */
+
+ case ICMPV6_DEST_UNREACH:
+ case ICMPV6_TIME_EXCEED:
+ case ICMPV6_PARAMPROB:
+ icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu,
+ (char *) (hdr + 1), ulen);
+ break;
+
+ case NDISC_ROUTER_SOLICITATION:
+ case NDISC_ROUTER_ADVERTISEMENT:
+ case NDISC_NEIGHBOUR_SOLICITATION:
+ case NDISC_NEIGHBOUR_ADVERTISEMENT:
+ case NDISC_REDIRECT:
+ ndisc_rcv(skb, len);
+ break;
+
+ case ICMPV6_MGM_QUERY:
+ igmp6_event_query(skb, hdr, len);
+ break;
+
+ case ICMPV6_MGM_REPORT:
+ igmp6_event_report(skb, hdr, len);
+ break;
+
+ case ICMPV6_MGM_REDUCTION:
+ break;
+
+ default:
+ if (net_ratelimit())
+ printk(KERN_DEBUG "icmpv6: msg of unkown type\n");
+
+ /* informational */
+ if (type & 0x80)
+ break;
+
+ /*
+ * error of unkown type.
+ * must pass to upper level
+ */
+
+ icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu,
+ (char *) (hdr + 1), ulen);
+ };
+ kfree_skb(skb);
+ return 0;
+
+discard_it:
+ icmpv6_statistics.Icmp6InErrors++;
+ kfree_skb(skb);
+ return 0;
+}
+
+int __init icmpv6_init(struct net_proto_family *ops)
+{
+ struct sock *sk;
+ int err;
+
+ icmpv6_socket = sock_alloc();
+ if (icmpv6_socket == NULL) {
+ printk(KERN_ERR
+ "Failed to create the ICMP6 control socket.\n");
+ return -1;
+ }
+ icmpv6_socket->inode->i_uid = 0;
+ icmpv6_socket->inode->i_gid = 0;
+ icmpv6_socket->type = SOCK_RAW;
+
+ if ((err = ops->create(icmpv6_socket, IPPROTO_ICMPV6)) < 0) {
+ printk(KERN_ERR
+ "Failed to initialize the ICMP6 control socket (err %d).\n",
+ err);
+ sock_release(icmpv6_socket);
+ icmpv6_socket = NULL; /* for safety */
+ return err;
+ }
+
+ sk = icmpv6_socket->sk;
+ sk->allocation = GFP_ATOMIC;
+ sk->num = 256; /* Don't receive any data */
+
+ inet6_add_protocol(&icmpv6_protocol);
+
+ return 0;
+}
+
+void icmpv6_cleanup(void)
+{
+ sock_release(icmpv6_socket);
+ icmpv6_socket = NULL; /* For safety. */
+ inet6_del_protocol(&icmpv6_protocol);
+}
+
+static struct icmp6_err {
+ int err;
+ int fatal;
+} tab_unreach[] = {
+ { ENETUNREACH, 0}, /* NOROUTE */
+ { EACCES, 1}, /* ADM_PROHIBITED */
+ { EHOSTUNREACH, 0}, /* Was NOT_NEIGHBOUR, now reserved */
+ { EHOSTUNREACH, 0}, /* ADDR_UNREACH */
+ { ECONNREFUSED, 1}, /* PORT_UNREACH */
+};
+
+int icmpv6_err_convert(int type, int code, int *err)
+{
+ int fatal = 0;
+
+ *err = EPROTO;
+
+ switch (type) {
+ case ICMPV6_DEST_UNREACH:
+ fatal = 1;
+ if (code <= ICMPV6_PORT_UNREACH) {
+ *err = tab_unreach[code].err;
+ fatal = tab_unreach[code].fatal;
+ }
+ break;
+
+ case ICMPV6_PKT_TOOBIG:
+ *err = EMSGSIZE;
+ break;
+
+ case ICMPV6_PARAMPROB:
+ *err = EPROTO;
+ fatal = 1;
+ break;
+
+ case ICMPV6_TIME_EXCEED:
+ *err = EHOSTUNREACH;
+ break;
+ };
+
+ return fatal;
+}