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