[PATCH] (1/2) inode trimming
[opensuse:kernel.git] / net / ipv6 / icmp.c
1 /*
2  *      Internet Control Message Protocol (ICMPv6)
3  *      Linux INET6 implementation
4  *
5  *      Authors:
6  *      Pedro Roque             <roque@di.fc.ul.pt>
7  *
8  *      $Id: icmp.c,v 1.37 2001/09/18 22:29:10 davem Exp $
9  *
10  *      Based on net/ipv4/icmp.c
11  *
12  *      RFC 1885
13  *
14  *      This program is free software; you can redistribute it and/or
15  *      modify it under the terms of the GNU General Public License
16  *      as published by the Free Software Foundation; either version
17  *      2 of the License, or (at your option) any later version.
18  */
19
20 /*
21  *      Changes:
22  *
23  *      Andi Kleen              :       exception handling
24  *      Andi Kleen                      add rate limits. never reply to a icmp.
25  *                                      add more length checks and other fixes.
26  *      yoshfuji                :       ensure to sent parameter problem for
27  *                                      fragments.
28  */
29
30 #define __NO_VERSION__
31 #include <linux/module.h>
32 #include <linux/errno.h>
33 #include <linux/types.h>
34 #include <linux/socket.h>
35 #include <linux/in.h>
36 #include <linux/kernel.h>
37 #include <linux/sched.h>
38 #include <linux/sockios.h>
39 #include <linux/net.h>
40 #include <linux/skbuff.h>
41 #include <linux/init.h>
42
43 #include <linux/inet.h>
44 #include <linux/netdevice.h>
45 #include <linux/icmpv6.h>
46
47 #include <net/ip.h>
48 #include <net/sock.h>
49
50 #include <net/ipv6.h>
51 #include <net/checksum.h>
52 #include <net/protocol.h>
53 #include <net/raw.h>
54 #include <net/rawv6.h>
55 #include <net/transp_v6.h>
56 #include <net/ip6_route.h>
57 #include <net/addrconf.h>
58 #include <net/icmp.h>
59
60 #include <asm/uaccess.h>
61 #include <asm/system.h>
62
63 struct icmpv6_mib icmpv6_statistics[NR_CPUS*2];
64
65 /*
66  *      ICMP socket for flow control.
67  */
68
69 struct socket *icmpv6_socket;
70
71 int icmpv6_rcv(struct sk_buff *skb);
72
73 static struct inet6_protocol icmpv6_protocol = 
74 {
75         icmpv6_rcv,             /* handler              */
76         NULL,                   /* error control        */
77         NULL,                   /* next                 */
78         IPPROTO_ICMPV6,         /* protocol ID          */
79         0,                      /* copy                 */
80         NULL,                   /* data                 */
81         "ICMPv6"                /* name                 */
82 };
83
84 struct icmpv6_msg {
85         struct icmp6hdr         icmph;
86         struct sk_buff          *skb;
87         int                     offset;
88         struct in6_addr         *daddr;
89         int                     len;
90         __u32                   csum;
91 };
92
93
94 static int icmpv6_xmit_holder = -1;
95
96 static int icmpv6_xmit_lock_bh(void)
97 {
98         if (!spin_trylock(&icmpv6_socket->sk->lock.slock)) {
99                 if (icmpv6_xmit_holder == smp_processor_id())
100                         return -EAGAIN;
101                 spin_lock(&icmpv6_socket->sk->lock.slock);
102         }
103         icmpv6_xmit_holder = smp_processor_id();
104         return 0;
105 }
106
107 static __inline__ int icmpv6_xmit_lock(void)
108 {
109         int ret;
110         local_bh_disable();
111         ret = icmpv6_xmit_lock_bh();
112         if (ret)
113                 local_bh_enable();
114         return ret;
115 }
116
117 static void icmpv6_xmit_unlock_bh(void)
118 {
119         icmpv6_xmit_holder = -1;
120         spin_unlock(&icmpv6_socket->sk->lock.slock);
121 }
122
123 static __inline__ void icmpv6_xmit_unlock(void)
124 {
125         icmpv6_xmit_unlock_bh();
126         local_bh_enable();
127 }
128
129
130
131 /*
132  *      getfrag callback
133  */
134
135 static int icmpv6_getfrag(const void *data, struct in6_addr *saddr, 
136                            char *buff, unsigned int offset, unsigned int len)
137 {
138         struct icmpv6_msg *msg = (struct icmpv6_msg *) data;
139         struct icmp6hdr *icmph;
140         __u32 csum;
141
142         if (offset) {
143                 csum = skb_copy_and_csum_bits(msg->skb, msg->offset +
144                                               (offset - sizeof(struct icmp6hdr)),
145                                               buff, len, msg->csum);
146                 msg->csum = csum;
147                 return 0;
148         }
149
150         csum = csum_partial_copy_nocheck((void *) &msg->icmph, buff,
151                                          sizeof(struct icmp6hdr), msg->csum);
152
153         csum = skb_copy_and_csum_bits(msg->skb, msg->offset,
154                                       buff + sizeof(struct icmp6hdr),
155                                       len - sizeof(struct icmp6hdr), csum);
156
157         icmph = (struct icmp6hdr *) buff;
158
159         icmph->icmp6_cksum = csum_ipv6_magic(saddr, msg->daddr, msg->len,
160                                              IPPROTO_ICMPV6, csum);
161         return 0; 
162 }
163
164
165 /* 
166  * Slightly more convenient version of icmpv6_send.
167  */
168 void icmpv6_param_prob(struct sk_buff *skb, int code, int pos)
169 {
170         icmpv6_send(skb, ICMPV6_PARAMPROB, code, pos, skb->dev);
171         kfree_skb(skb);
172 }
173
174 /*
175  * Figure out, may we reply to this packet with icmp error.
176  *
177  * We do not reply, if:
178  *      - it was icmp error message.
179  *      - it is truncated, so that it is known, that protocol is ICMPV6
180  *        (i.e. in the middle of some exthdr)
181  *
182  *      --ANK (980726)
183  */
184
185 static int is_ineligible(struct sk_buff *skb)
186 {
187         int ptr = (u8*)(skb->nh.ipv6h+1) - skb->data;
188         int len = skb->len - ptr;
189         __u8 nexthdr = skb->nh.ipv6h->nexthdr;
190
191         if (len < 0)
192                 return 1;
193
194         ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr, len);
195         if (ptr < 0)
196                 return 0;
197         if (nexthdr == IPPROTO_ICMPV6) {
198                 u8 type;
199                 if (skb_copy_bits(skb, ptr+offsetof(struct icmp6hdr, icmp6_type),
200                                   &type, 1)
201                     || !(type & 0x80))
202                         return 1;
203         }
204         return 0;
205 }
206
207 int sysctl_icmpv6_time = 1*HZ; 
208
209 /* 
210  * Check the ICMP output rate limit 
211  */
212 static inline int icmpv6_xrlim_allow(struct sock *sk, int type,
213                                      struct flowi *fl)
214 {
215         struct dst_entry *dst;
216         int res = 0;
217
218         /* Informational messages are not limited. */
219         if (type & 0x80)
220                 return 1;
221
222         /* Do not limit pmtu discovery, it would break it. */
223         if (type == ICMPV6_PKT_TOOBIG)
224                 return 1;
225
226         /* 
227          * Look up the output route.
228          * XXX: perhaps the expire for routing entries cloned by
229          * this lookup should be more aggressive (not longer than timeout).
230          */
231         dst = ip6_route_output(sk, fl);
232         if (dst->error) {
233                 IP6_INC_STATS(Ip6OutNoRoutes);
234         } else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) {
235                 res = 1;
236         } else {
237                 struct rt6_info *rt = (struct rt6_info *)dst;
238                 int tmo = sysctl_icmpv6_time;
239
240                 /* Give more bandwidth to wider prefixes. */
241                 if (rt->rt6i_dst.plen < 128)
242                         tmo >>= ((128 - rt->rt6i_dst.plen)>>5);
243
244                 res = xrlim_allow(dst, tmo);
245         }
246         dst_release(dst);
247         return res;
248 }
249
250 /*
251  *      an inline helper for the "simple" if statement below
252  *      checks if parameter problem report is caused by an
253  *      unrecognized IPv6 option that has the Option Type 
254  *      highest-order two bits set to 10
255  */
256
257 static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
258 {
259         u8 optval;
260
261         offset += skb->nh.raw - skb->data;
262         if (skb_copy_bits(skb, offset, &optval, 1))
263                 return 1;
264         return (optval&0xC0) == 0x80;
265 }
266
267 /*
268  *      Send an ICMP message in response to a packet in error
269  */
270
271 void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, 
272                  struct net_device *dev)
273 {
274         struct ipv6hdr *hdr = skb->nh.ipv6h;
275         struct sock *sk = icmpv6_socket->sk;
276         struct in6_addr *saddr = NULL;
277         int iif = 0;
278         struct icmpv6_msg msg;
279         struct flowi fl;
280         int addr_type = 0;
281         int len;
282
283         if ((u8*)hdr < skb->head || (u8*)(hdr+1) > skb->tail)
284                 return;
285
286         /*
287          *      Make sure we respect the rules 
288          *      i.e. RFC 1885 2.4(e)
289          *      Rule (e.1) is enforced by not using icmpv6_send
290          *      in any code that processes icmp errors.
291          */
292         addr_type = ipv6_addr_type(&hdr->daddr);
293
294         if (ipv6_chk_addr(&hdr->daddr, skb->dev))
295                 saddr = &hdr->daddr;
296
297         /*
298          *      Dest addr check
299          */
300
301         if ((addr_type & IPV6_ADDR_MULTICAST || skb->pkt_type != PACKET_HOST)) {
302                 if (type != ICMPV6_PKT_TOOBIG &&
303                     !(type == ICMPV6_PARAMPROB && 
304                       code == ICMPV6_UNK_OPTION && 
305                       (opt_unrec(skb, info))))
306                         return;
307
308                 saddr = NULL;
309         }
310
311         addr_type = ipv6_addr_type(&hdr->saddr);
312
313         /*
314          *      Source addr check
315          */
316
317         if (addr_type & IPV6_ADDR_LINKLOCAL)
318                 iif = skb->dev->ifindex;
319
320         /*
321          *      Must not send if we know that source is Anycast also.
322          *      for now we don't know that.
323          */
324         if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
325                 if (net_ratelimit())
326                         printk(KERN_DEBUG "icmpv6_send: addr_any/mcast source\n");
327                 return;
328         }
329
330         /* 
331          *      Never answer to a ICMP packet.
332          */
333         if (is_ineligible(skb)) {
334                 if (net_ratelimit())
335                         printk(KERN_DEBUG "icmpv6_send: no reply to icmp error\n"); 
336                 return;
337         }
338
339         fl.proto = IPPROTO_ICMPV6;
340         fl.nl_u.ip6_u.daddr = &hdr->saddr;
341         fl.nl_u.ip6_u.saddr = saddr;
342         fl.oif = iif;
343         fl.fl6_flowlabel = 0;
344         fl.uli_u.icmpt.type = type;
345         fl.uli_u.icmpt.code = code;
346
347         if (icmpv6_xmit_lock())
348                 return;
349
350         if (!icmpv6_xrlim_allow(sk, type, &fl))
351                 goto out;
352
353         /*
354          *      ok. kick it. checksum will be provided by the 
355          *      getfrag_t callback.
356          */
357
358         msg.icmph.icmp6_type = type;
359         msg.icmph.icmp6_code = code;
360         msg.icmph.icmp6_cksum = 0;
361         msg.icmph.icmp6_pointer = htonl(info);
362
363         msg.skb = skb;
364         msg.offset = skb->nh.raw - skb->data;
365         msg.csum = 0;
366         msg.daddr = &hdr->saddr;
367
368         len = skb->len - msg.offset + sizeof(struct icmp6hdr);
369         len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr));
370
371         if (len < 0) {
372                 if (net_ratelimit())
373                         printk(KERN_DEBUG "icmp: len problem\n");
374                 goto out;
375         }
376
377         msg.len = len;
378
379         ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
380                        MSG_DONTWAIT);
381         if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
382                 (&(icmpv6_statistics[smp_processor_id()*2].Icmp6OutDestUnreachs))[type-1]++;
383         ICMP6_INC_STATS_BH(Icmp6OutMsgs);
384 out:
385         icmpv6_xmit_unlock();
386 }
387
388 static void icmpv6_echo_reply(struct sk_buff *skb)
389 {
390         struct sock *sk = icmpv6_socket->sk;
391         struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
392         struct in6_addr *saddr;
393         struct icmpv6_msg msg;
394         struct flowi fl;
395
396         saddr = &skb->nh.ipv6h->daddr;
397
398         if (ipv6_addr_type(saddr) & IPV6_ADDR_MULTICAST)
399                 saddr = NULL;
400
401         msg.icmph.icmp6_type = ICMPV6_ECHO_REPLY;
402         msg.icmph.icmp6_code = 0;
403         msg.icmph.icmp6_cksum = 0;
404         msg.icmph.icmp6_identifier = icmph->icmp6_identifier;
405         msg.icmph.icmp6_sequence = icmph->icmp6_sequence;
406
407         msg.skb = skb;
408         msg.offset = 0;
409         msg.csum = 0;
410         msg.len = skb->len + sizeof(struct icmp6hdr);
411         msg.daddr =  &skb->nh.ipv6h->saddr;
412
413         fl.proto = IPPROTO_ICMPV6;
414         fl.nl_u.ip6_u.daddr = msg.daddr;
415         fl.nl_u.ip6_u.saddr = saddr;
416         fl.oif = skb->dev->ifindex;
417         fl.fl6_flowlabel = 0;
418         fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY;
419         fl.uli_u.icmpt.code = 0;
420
421         if (icmpv6_xmit_lock_bh())
422                 return;
423
424         ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1,
425                        MSG_DONTWAIT);
426         ICMP6_INC_STATS_BH(Icmp6OutEchoReplies);
427         ICMP6_INC_STATS_BH(Icmp6OutMsgs);
428
429         icmpv6_xmit_unlock_bh();
430 }
431
432 static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info)
433 {
434         struct in6_addr *saddr, *daddr;
435         struct inet6_protocol *ipprot;
436         struct sock *sk;
437         int inner_offset;
438         int hash;
439         u8 nexthdr;
440
441         if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
442                 return;
443
444         nexthdr = ((struct ipv6hdr *)skb->data)->nexthdr;
445         if (ipv6_ext_hdr(nexthdr)) {
446                 /* now skip over extension headers */
447                 inner_offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, skb->len - sizeof(struct ipv6hdr));
448                 if (inner_offset<0)
449                         return;
450         } else {
451                 inner_offset = sizeof(struct ipv6hdr);
452         }
453
454         /* Checkin header including 8 bytes of inner protocol header. */
455         if (!pskb_may_pull(skb, inner_offset+8))
456                 return;
457
458         saddr = &skb->nh.ipv6h->saddr;
459         daddr = &skb->nh.ipv6h->daddr;
460
461         /* BUGGG_FUTURE: we should try to parse exthdrs in this packet.
462            Without this we will not able f.e. to make source routed
463            pmtu discovery.
464            Corresponding argument (opt) to notifiers is already added.
465            --ANK (980726)
466          */
467
468         hash = nexthdr & (MAX_INET_PROTOS - 1);
469
470         for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; 
471              ipprot != NULL; 
472              ipprot=(struct inet6_protocol *)ipprot->next) {
473                 if (ipprot->protocol != nexthdr)
474                         continue;
475
476                 if (ipprot->err_handler)
477                         ipprot->err_handler(skb, NULL, type, code, inner_offset, info);
478         }
479
480         read_lock(&raw_v6_lock);
481         if ((sk = raw_v6_htable[hash]) != NULL) {
482                 while((sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
483                         rawv6_err(sk, skb, NULL, type, code, inner_offset, info);
484                         sk = sk->next;
485                 }
486         }
487         read_unlock(&raw_v6_lock);
488 }
489   
490 /*
491  *      Handle icmp messages
492  */
493
494 int icmpv6_rcv(struct sk_buff *skb)
495 {
496         struct net_device *dev = skb->dev;
497         struct in6_addr *saddr, *daddr;
498         struct ipv6hdr *orig_hdr;
499         struct icmp6hdr *hdr;
500         int type;
501
502         ICMP6_INC_STATS_BH(Icmp6InMsgs);
503
504         saddr = &skb->nh.ipv6h->saddr;
505         daddr = &skb->nh.ipv6h->daddr;
506
507         /* Perform checksum. */
508         if (skb->ip_summed == CHECKSUM_HW) {
509                 skb->ip_summed = CHECKSUM_UNNECESSARY;
510                 if (csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6,
511                                     skb->csum)) {
512                         if (net_ratelimit())
513                                 printk(KERN_DEBUG "ICMPv6 hw checksum failed\n");
514                         skb->ip_summed = CHECKSUM_NONE;
515                 }
516         }
517         if (skb->ip_summed == CHECKSUM_NONE) {
518                 if (csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6,
519                                     skb_checksum(skb, 0, skb->len, 0))) {
520                         if (net_ratelimit())
521                                 printk(KERN_DEBUG "ICMPv6 checksum failed [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x > %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
522                                        ntohs(saddr->in6_u.u6_addr16[0]),
523                                        ntohs(saddr->in6_u.u6_addr16[1]),
524                                        ntohs(saddr->in6_u.u6_addr16[2]),
525                                        ntohs(saddr->in6_u.u6_addr16[3]),
526                                        ntohs(saddr->in6_u.u6_addr16[4]),
527                                        ntohs(saddr->in6_u.u6_addr16[5]),
528                                        ntohs(saddr->in6_u.u6_addr16[6]),
529                                        ntohs(saddr->in6_u.u6_addr16[7]),
530                                        ntohs(daddr->in6_u.u6_addr16[0]),
531                                        ntohs(daddr->in6_u.u6_addr16[1]),
532                                        ntohs(daddr->in6_u.u6_addr16[2]),
533                                        ntohs(daddr->in6_u.u6_addr16[3]),
534                                        ntohs(daddr->in6_u.u6_addr16[4]),
535                                        ntohs(daddr->in6_u.u6_addr16[5]),
536                                        ntohs(daddr->in6_u.u6_addr16[6]),
537                                        ntohs(daddr->in6_u.u6_addr16[7]));
538                         goto discard_it;
539                 }
540         }
541
542         if (!pskb_pull(skb, sizeof(struct icmp6hdr)))
543                 goto discard_it;
544
545         hdr = (struct icmp6hdr *) skb->h.raw;
546
547         type = hdr->icmp6_type;
548
549         if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
550                 (&icmpv6_statistics[smp_processor_id()*2].Icmp6InDestUnreachs)[type-ICMPV6_DEST_UNREACH]++;
551         else if (type >= ICMPV6_ECHO_REQUEST && type <= NDISC_REDIRECT)
552                 (&icmpv6_statistics[smp_processor_id()*2].Icmp6InEchos)[type-ICMPV6_ECHO_REQUEST]++;
553
554         switch (type) {
555         case ICMPV6_ECHO_REQUEST:
556                 icmpv6_echo_reply(skb);
557                 break;
558
559         case ICMPV6_ECHO_REPLY:
560                 /* we coulnd't care less */
561                 break;
562
563         case ICMPV6_PKT_TOOBIG:
564                 /* BUGGG_FUTURE: if packet contains rthdr, we cannot update
565                    standard destination cache. Seems, only "advanced"
566                    destination cache will allow to solve this problem
567                    --ANK (980726)
568                  */
569                 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
570                         goto discard_it;
571                 hdr = (struct icmp6hdr *) skb->h.raw;
572                 orig_hdr = (struct ipv6hdr *) (hdr + 1);
573                 rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev,
574                                    ntohl(hdr->icmp6_mtu));
575
576                 /*
577                  *      Drop through to notify
578                  */
579
580         case ICMPV6_DEST_UNREACH:
581         case ICMPV6_TIME_EXCEED:
582         case ICMPV6_PARAMPROB:
583                 icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu);
584                 break;
585
586         case NDISC_ROUTER_SOLICITATION:
587         case NDISC_ROUTER_ADVERTISEMENT:
588         case NDISC_NEIGHBOUR_SOLICITATION:
589         case NDISC_NEIGHBOUR_ADVERTISEMENT:
590         case NDISC_REDIRECT:
591                 if (skb_is_nonlinear(skb) &&
592                     skb_linearize(skb, GFP_ATOMIC) != 0) {
593                         kfree_skb(skb);
594                         return 0;
595                 }
596
597                 ndisc_rcv(skb);
598                 break;
599
600         case ICMPV6_MGM_QUERY:
601                 igmp6_event_query(skb);
602                 break;
603
604         case ICMPV6_MGM_REPORT:
605                 igmp6_event_report(skb);
606                 break;
607
608         case ICMPV6_MGM_REDUCTION:
609                 break;
610
611         default:
612                 if (net_ratelimit())
613                         printk(KERN_DEBUG "icmpv6: msg of unkown type\n");
614
615                 /* informational */
616                 if (type & 0x80)
617                         break;
618
619                 /* 
620                  * error of unkown type. 
621                  * must pass to upper level 
622                  */
623
624                 icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu);
625         };
626         kfree_skb(skb);
627         return 0;
628
629 discard_it:
630         ICMP6_INC_STATS_BH(Icmp6InErrors);
631         kfree_skb(skb);
632         return 0;
633 }
634
635 int __init icmpv6_init(struct net_proto_family *ops)
636 {
637         struct sock *sk;
638         int err;
639
640         err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, &icmpv6_socket);
641         if (err < 0) {
642                 printk(KERN_ERR
643                        "Failed to initialize the ICMP6 control socket (err %d).\n",
644                        err);
645                 icmpv6_socket = NULL; /* for safety */
646                 return err;
647         }
648
649         sk = icmpv6_socket->sk;
650         sk->allocation = GFP_ATOMIC;
651         sk->sndbuf = SK_WMEM_MAX*2;
652         sk->prot->unhash(sk);
653
654         inet6_add_protocol(&icmpv6_protocol);
655
656         return 0;
657 }
658
659 void icmpv6_cleanup(void)
660 {
661         sock_release(icmpv6_socket);
662         icmpv6_socket = NULL; /* For safety. */
663         inet6_del_protocol(&icmpv6_protocol);
664 }
665
666 static struct icmp6_err {
667         int err;
668         int fatal;
669 } tab_unreach[] = {
670         { ENETUNREACH,  0},     /* NOROUTE              */
671         { EACCES,       1},     /* ADM_PROHIBITED       */
672         { EHOSTUNREACH, 0},     /* Was NOT_NEIGHBOUR, now reserved */
673         { EHOSTUNREACH, 0},     /* ADDR_UNREACH         */
674         { ECONNREFUSED, 1},     /* PORT_UNREACH         */
675 };
676
677 int icmpv6_err_convert(int type, int code, int *err)
678 {
679         int fatal = 0;
680
681         *err = EPROTO;
682
683         switch (type) {
684         case ICMPV6_DEST_UNREACH:
685                 fatal = 1;
686                 if (code <= ICMPV6_PORT_UNREACH) {
687                         *err  = tab_unreach[code].err;
688                         fatal = tab_unreach[code].fatal;
689                 }
690                 break;
691
692         case ICMPV6_PKT_TOOBIG:
693                 *err = EMSGSIZE;
694                 break;
695                 
696         case ICMPV6_PARAMPROB:
697                 *err = EPROTO;
698                 fatal = 1;
699                 break;
700
701         case ICMPV6_TIME_EXCEED:
702                 *err = EHOSTUNREACH;
703                 break;
704         };
705
706         return fatal;
707 }