patch-2.1.120 linux/net/ipv6/icmp.c
Next file: linux/net/ipv6/ip6_fib.c
Previous file: linux/net/ipv6/exthdrs.c
Back to the patch index
Back to the overall index
- Lines: 418
- Date:
Thu Aug 27 19:33:09 1998
- Orig file:
v2.1.119/linux/net/ipv6/icmp.c
- Orig date:
Thu May 14 19:47:45 1998
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/icmp.c linux/net/ipv6/icmp.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: icmp.c,v 1.18 1998/05/07 15:42:59 davem Exp $
+ * $Id: icmp.c,v 1.19 1998/08/26 12:04:52 davem Exp $
*
* Based on net/ipv4/icmp.c
*
@@ -58,16 +58,15 @@
#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, struct device *dev,
- struct in6_addr *saddr, struct in6_addr *daddr,
- struct ipv6_options *opt, unsigned short len,
- int redo, struct inet6_protocol *protocol);
+int icmpv6_rcv(struct sk_buff *skb, unsigned long len);
static struct inet6_protocol icmpv6_protocol =
{
@@ -80,8 +79,6 @@
"ICMPv6" /* name */
};
-
-
struct icmpv6_msg {
struct icmp6hdr icmph;
__u8 *data;
@@ -105,8 +102,11 @@
/*
* in theory offset must be 0 since we never send more
- * than 576 bytes on an error or more than the path mtu
+ * 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) {
@@ -143,13 +143,36 @@
kfree_skb(skb);
}
-static inline int is_icmp(struct ipv6hdr *hdr, int len)
+/*
+ * 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 nexthdr = hdr->nexthdr;
+ u8 *ptr;
+ __u8 nexthdr = hdr->nexthdr;
+
+ if (len < (int)sizeof(*hdr))
+ return 1;
- if (!ipv6_skip_exthdr((struct ipv6_opt_hdr *)(hdr+1), &nexthdr, len))
- return 0;
- return nexthdr == IPPROTO_ICMP;
+ 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;
@@ -160,31 +183,37 @@
static inline int icmpv6_xrlim_allow(struct sock *sk, int type,
struct flowi *fl)
{
-#if 0
- struct dst_entry *dst;
- int allow = 0;
-#endif
+ struct dst_entry *dst;
+ int res = 0;
+
/* Informational messages are not limited. */
if (type & 0x80)
- return 1;
+ return 1;
-#if 0 /* not yet, first fix routing COW */
+ /* 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, 1);
- if (dst->error)
+ dst = ip6_route_output(sk, fl);
+ if (dst->error)
ipv6_statistics.Ip6OutNoRoutes++;
- else
- allow = xrlim_allow(dst, sysctl_icmpv6_time);
+ 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 allow;
-#else
- return 1;
-#endif
+ return res;
}
/*
@@ -196,7 +225,7 @@
static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
{
- char *buff = skb->nh.raw;
+ u8 *buff = skb->nh.raw;
return ( ( *(buff + offset) & 0xC0 ) == 0x80 );
}
@@ -215,7 +244,6 @@
struct icmpv6_msg msg;
struct flowi fl;
int addr_type = 0;
- int optlen;
int len;
/*
@@ -237,7 +265,7 @@
addr_type = ipv6_addr_type(&hdr->daddr);
- if (ipv6_chk_addr(&hdr->daddr, NULL, 0))
+ if (ipv6_chk_addr(&hdr->daddr, skb->dev, 0))
saddr = &hdr->daddr;
/*
@@ -275,8 +303,9 @@
/*
* Never answer to a ICMP packet.
*/
- if (is_icmp(hdr, (u8*)skb->tail - (u8*)hdr)) {
- printk(KERN_DEBUG "icmpv6_send: no reply to icmp\n");
+ 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;
}
@@ -303,34 +332,22 @@
msg.data = skb->nh.raw;
msg.csum = 0;
msg.daddr = &hdr->saddr;
- /*
- if (skb->opt)
- optlen = skb->opt->optlen;
- else
- */
-
- optlen = 0;
-
- len = min(skb->tail - ((unsigned char *) hdr),
- 576 - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr)
- - optlen);
+
+ len = min((skb->tail - ((unsigned char *) hdr)) + sizeof(struct icmp6hdr),
+ IPV6_MIN_MTU - sizeof(struct icmp6hdr));
if (len < 0) {
printk(KERN_DEBUG "icmp: len problem\n");
return;
}
- len += sizeof(struct icmp6hdr);
-
msg.len = len;
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
MSG_DONTWAIT);
-
- /* Oops! We must purge cached dst, otherwise
- all the following ICMP messages will go there :) --ANK
- */
- dst_release(xchg(&sk->dst_cache, NULL));
+ 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)
@@ -374,38 +391,41 @@
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
MSG_DONTWAIT);
-
- /* Oops! We must purge cached dst, otherwise
- all the following ICMP messages will go there :) --ANK
- */
- dst_release(xchg(&sk->dst_cache, NULL));
+ icmpv6_statistics.Icmp6OutEchoReplies++;
+ icmpv6_statistics.Icmp6OutMsgs++;
}
static void icmpv6_notify(struct sk_buff *skb,
- int type, int code, unsigned char *buff, int len,
- struct in6_addr *saddr, struct in6_addr *daddr,
- struct inet6_protocol *protocol)
+ int type, int code, 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;
- struct ipv6_opt_hdr *pb;
+ u8 *pb;
__u32 info = 0;
int hash;
u8 nexthdr;
nexthdr = hdr->nexthdr;
- pb = (struct ipv6_opt_hdr *) (hdr + 1);
len -= sizeof(struct ipv6hdr);
if (len < 0)
return;
/* now skip over extension headers */
- pb = ipv6_skip_exthdr(pb, &nexthdr, len);
+ 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];
@@ -414,9 +434,8 @@
if (ipprot->protocol != nexthdr)
continue;
- if (ipprot->err_handler)
- ipprot->err_handler(skb, type, code, (u8*)pb, info,
- saddr, daddr, ipprot);
+ if (ipprot->err_handler)
+ ipprot->err_handler(skb, hdr, NULL, type, code, pb, info);
return;
}
@@ -428,7 +447,7 @@
return;
while((sk = raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
- rawv6_err(sk, type, code, (char*)pb, saddr, daddr);
+ rawv6_err(sk, skb, hdr, NULL, type, code, pb, info);
sk = sk->next;
}
}
@@ -437,14 +456,17 @@
* Handle icmp messages
*/
-int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
- struct in6_addr *saddr, struct in6_addr *daddr,
- struct ipv6_options *opt, unsigned short len,
- int redo, struct inet6_protocol *protocol)
+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++;
/* Perform checksum. */
switch (skb->ip_summed) {
@@ -480,8 +502,15 @@
* length of original packet carried in skb
*/
ulen = skb->tail - (unsigned char *) (hdr + 1);
-
- switch (hdr->icmp6_type) {
+
+ 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);
@@ -492,9 +521,14 @@
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, dev,
+ rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev,
ntohl(hdr->icmp6_mtu));
/*
@@ -504,10 +538,8 @@
case ICMPV6_DEST_UNREACH:
case ICMPV6_TIME_EXCEED:
case ICMPV6_PARAMPROB:
-
- icmpv6_notify(skb, hdr->icmp6_type, hdr->icmp6_code,
- (char *) (hdr + 1), ulen,
- saddr, daddr, protocol);
+ icmpv6_notify(skb, type, hdr->icmp6_code,
+ (char *) (hdr + 1), ulen);
break;
case NDISC_ROUTER_SOLICITATION:
@@ -515,7 +547,7 @@
case NDISC_NEIGHBOUR_SOLICITATION:
case NDISC_NEIGHBOUR_ADVERTISEMENT:
case NDISC_REDIRECT:
- ndisc_rcv(skb, dev, saddr, daddr, opt, len);
+ ndisc_rcv(skb, len);
break;
case ICMPV6_MGM_QUERY:
@@ -530,23 +562,26 @@
break;
default:
- printk(KERN_DEBUG "icmpv6: msg of unkown type\n");
+ if (net_ratelimit())
+ printk(KERN_DEBUG "icmpv6: msg of unkown type\n");
/* informational */
- if (hdr->icmp6_type & 0x80)
- goto discard_it;
+ if (type & 0x80)
+ break;
/*
* error of unkown type.
* must pass to upper level
*/
- icmpv6_notify(skb, hdr->icmp6_type, hdr->icmp6_code,
- (char *) (hdr + 1), ulen,
- saddr, daddr, protocol);
+ icmpv6_notify(skb, type, hdr->icmp6_code,
+ (char *) (hdr + 1), ulen);
};
+ kfree_skb(skb);
+ return 0;
discard_it:
+ icmpv6_statistics.Icmp6InErrors++;
kfree_skb(skb);
return 0;
}
@@ -597,7 +632,7 @@
} tab_unreach[] = {
{ ENETUNREACH, 0}, /* NOROUTE */
{ EACCES, 1}, /* ADM_PROHIBITED */
- { EOPNOTSUPP, 1}, /* NOT_NEIGHBOUR */
+ { 0, 0}, /* Was NOT_NEIGHBOUR, now reserved */
{ EHOSTUNREACH, 0}, /* ADDR_UNREACH */
{ ECONNREFUSED, 1}, /* PORT_UNREACH */
};
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov