summaryrefslogtreecommitdiff
path: root/pfinet/linux-inet/icmp.c
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1995-07-12 15:42:50 +0000
committerMichael I. Bushnell <mib@gnu.org>1995-07-12 15:42:50 +0000
commita81cd86c8d93236ffccfbee44b5818ba21523463 (patch)
treef098368d79e9ed9b39907730c28ed3182b69519d /pfinet/linux-inet/icmp.c
parentc7923f6aa252a29ccb4f16bd91469c9000a2bd94 (diff)
entered into RCS
Diffstat (limited to 'pfinet/linux-inet/icmp.c')
-rw-r--r--pfinet/linux-inet/icmp.c774
1 files changed, 774 insertions, 0 deletions
diff --git a/pfinet/linux-inet/icmp.c b/pfinet/linux-inet/icmp.c
new file mode 100644
index 00000000..c023eab2
--- /dev/null
+++ b/pfinet/linux-inet/icmp.c
@@ -0,0 +1,774 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Internet Control Message Protocol (ICMP)
+ *
+ * Version: @(#)icmp.c 1.0.11 06/02/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Stefan Becker, <stefanb@yello.ping.de>
+ *
+ * Fixes:
+ * Alan Cox : Generic queue usage.
+ * Gerhard Koerting: ICMP addressing corrected
+ * Alan Cox : Use tos/ttl settings
+ * Alan Cox : Protocol violations
+ * Alan Cox : SNMP Statistics
+ * Alan Cox : Routing errors
+ * Alan Cox : Changes for newer routing code
+ * Alan Cox : Removed old debugging junk
+ * Alan Cox : Fixed the ICMP error status of net/host unreachable
+ * Gerhard Koerting : Fixed broadcast ping properly
+ * Ulrich Kunitz : Fixed ICMP timestamp reply
+ * A.N.Kuznetsov : Multihoming fixes.
+ * Laco Rusnak : Multihoming fixes.
+ * Alan Cox : Tightened up icmp_send().
+ * Alan Cox : Multicasts.
+ * Stefan Becker : ICMP redirects in icmp_send().
+ *
+ *
+ *
+ * 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.
+ */
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include "snmp.h"
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "icmp.h"
+#include "tcp.h"
+#include "snmp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+
+/*
+ * Statistics
+ */
+
+struct icmp_mib icmp_statistics={0,};
+
+
+/* An array of errno for error messages from dest unreach. */
+struct icmp_err icmp_err_convert[] = {
+ { ENETUNREACH, 0 }, /* ICMP_NET_UNREACH */
+ { EHOSTUNREACH, 0 }, /* ICMP_HOST_UNREACH */
+ { ENOPROTOOPT, 1 }, /* ICMP_PROT_UNREACH */
+ { ECONNREFUSED, 1 }, /* ICMP_PORT_UNREACH */
+ { EOPNOTSUPP, 0 }, /* ICMP_FRAG_NEEDED */
+ { EOPNOTSUPP, 0 }, /* ICMP_SR_FAILED */
+ { ENETUNREACH, 1 }, /* ICMP_NET_UNKNOWN */
+ { EHOSTDOWN, 1 }, /* ICMP_HOST_UNKNOWN */
+ { ENONET, 1 }, /* ICMP_HOST_ISOLATED */
+ { ENETUNREACH, 1 }, /* ICMP_NET_ANO */
+ { EHOSTUNREACH, 1 }, /* ICMP_HOST_ANO */
+ { EOPNOTSUPP, 0 }, /* ICMP_NET_UNR_TOS */
+ { EOPNOTSUPP, 0 } /* ICMP_HOST_UNR_TOS */
+};
+
+
+/*
+ * Send an ICMP message in response to a situation
+ *
+ * Fixme: Fragment handling is wrong really.
+ */
+
+void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info, struct device *dev)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ int offset;
+ struct icmphdr *icmph;
+ int len;
+ struct device *ndev=NULL; /* Make this =dev to force replies on the same interface */
+ unsigned long our_addr;
+ int atype;
+
+ /*
+ * Find the original IP header.
+ */
+
+ iph = (struct iphdr *) (skb_in->data + dev->hard_header_len);
+
+ /*
+ * No replies to MAC multicast
+ */
+
+ if(skb_in->pkt_type!=PACKET_HOST)
+ return;
+
+ /*
+ * No replies to IP multicasting
+ */
+
+ atype=ip_chk_addr(iph->daddr);
+ if(atype==IS_BROADCAST || IN_MULTICAST(iph->daddr))
+ return;
+
+ /*
+ * Only reply to first fragment.
+ */
+
+ if(ntohs(iph->frag_off)&IP_OFFSET)
+ return;
+
+ /*
+ * We must NEVER NEVER send an ICMP error to an ICMP error message
+ */
+
+ if(type==ICMP_DEST_UNREACH||type==ICMP_REDIRECT||type==ICMP_SOURCE_QUENCH||type==ICMP_TIME_EXCEEDED)
+ {
+
+ /*
+ * Is the original packet an ICMP packet?
+ */
+
+ if(iph->protocol==IPPROTO_ICMP)
+ {
+ icmph = (struct icmphdr *) ((char *) iph +
+ 4 * iph->ihl);
+ /*
+ * Check for ICMP error packets (Must never reply to
+ * an ICMP error).
+ */
+
+ if (icmph->type == ICMP_DEST_UNREACH ||
+ icmph->type == ICMP_SOURCE_QUENCH ||
+ icmph->type == ICMP_REDIRECT ||
+ icmph->type == ICMP_TIME_EXCEEDED ||
+ icmph->type == ICMP_PARAMETERPROB)
+ return;
+ }
+ }
+ icmp_statistics.IcmpOutMsgs++;
+
+ /*
+ * This needs a tidy.
+ */
+
+ switch(type)
+ {
+ case ICMP_DEST_UNREACH:
+ icmp_statistics.IcmpOutDestUnreachs++;
+ break;
+ case ICMP_SOURCE_QUENCH:
+ icmp_statistics.IcmpOutSrcQuenchs++;
+ break;
+ case ICMP_REDIRECT:
+ icmp_statistics.IcmpOutRedirects++;
+ break;
+ case ICMP_ECHO:
+ icmp_statistics.IcmpOutEchos++;
+ break;
+ case ICMP_ECHOREPLY:
+ icmp_statistics.IcmpOutEchoReps++;
+ break;
+ case ICMP_TIME_EXCEEDED:
+ icmp_statistics.IcmpOutTimeExcds++;
+ break;
+ case ICMP_PARAMETERPROB:
+ icmp_statistics.IcmpOutParmProbs++;
+ break;
+ case ICMP_TIMESTAMP:
+ icmp_statistics.IcmpOutTimestamps++;
+ break;
+ case ICMP_TIMESTAMPREPLY:
+ icmp_statistics.IcmpOutTimestampReps++;
+ break;
+ case ICMP_ADDRESS:
+ icmp_statistics.IcmpOutAddrMasks++;
+ break;
+ case ICMP_ADDRESSREPLY:
+ icmp_statistics.IcmpOutAddrMaskReps++;
+ break;
+ }
+ /*
+ * Get some memory for the reply.
+ */
+
+ len = dev->hard_header_len + sizeof(struct iphdr) + sizeof(struct icmphdr) +
+ sizeof(struct iphdr) + 32; /* amount of header to return */
+
+ skb = (struct sk_buff *) alloc_skb(len, GFP_ATOMIC);
+ if (skb == NULL)
+ {
+ icmp_statistics.IcmpOutErrors++;
+ return;
+ }
+ skb->free = 1;
+
+ /*
+ * Build Layer 2-3 headers for message back to source.
+ */
+
+ our_addr = dev->pa_addr;
+ if (iph->daddr != our_addr && ip_chk_addr(iph->daddr) == IS_MYADDR)
+ our_addr = iph->daddr;
+ offset = ip_build_header(skb, our_addr, iph->saddr,
+ &ndev, IPPROTO_ICMP, NULL, len,
+ skb_in->ip_hdr->tos,255);
+ if (offset < 0)
+ {
+ icmp_statistics.IcmpOutErrors++;
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /*
+ * Re-adjust length according to actual IP header size.
+ */
+
+ skb->len = offset + sizeof(struct icmphdr) + sizeof(struct iphdr) + 8;
+
+ /*
+ * Fill in the frame
+ */
+
+ icmph = (struct icmphdr *) (skb->data + offset);
+ icmph->type = type;
+ icmph->code = code;
+ icmph->checksum = 0;
+ icmph->un.gateway = info; /* This might not be meant for
+ this form of the union but it will
+ be right anyway */
+ memcpy(icmph + 1, iph, sizeof(struct iphdr) + 8);
+
+ icmph->checksum = ip_compute_csum((unsigned char *)icmph,
+ sizeof(struct icmphdr) + sizeof(struct iphdr) + 8);
+
+ /*
+ * Send it and free it once sent.
+ */
+ ip_queue_xmit(NULL, ndev, skb, 1);
+}
+
+
+/*
+ * Handle ICMP_UNREACH and ICMP_QUENCH.
+ */
+
+static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb)
+{
+ struct inet_protocol *ipprot;
+ struct iphdr *iph;
+ unsigned char hash;
+ int err;
+
+ err = (icmph->type << 8) | icmph->code;
+ iph = (struct iphdr *) (icmph + 1);
+
+ switch(icmph->code & 7)
+ {
+ case ICMP_NET_UNREACH:
+ break;
+ case ICMP_HOST_UNREACH:
+ break;
+ case ICMP_PROT_UNREACH:
+ printk("ICMP: %s:%d: protocol unreachable.\n",
+ in_ntoa(iph->daddr), ntohs(iph->protocol));
+ break;
+ case ICMP_PORT_UNREACH:
+ break;
+ case ICMP_FRAG_NEEDED:
+ printk("ICMP: %s: fragmentation needed and DF set.\n",
+ in_ntoa(iph->daddr));
+ break;
+ case ICMP_SR_FAILED:
+ printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Get the protocol(s).
+ */
+
+ hash = iph->protocol & (MAX_INET_PROTOS -1);
+
+ /*
+ * This can't change while we are doing it.
+ */
+
+ ipprot = (struct inet_protocol *) inet_protos[hash];
+ while(ipprot != NULL)
+ {
+ struct inet_protocol *nextip;
+
+ nextip = (struct inet_protocol *) ipprot->next;
+
+ /*
+ * Pass it off to everyone who wants it.
+ */
+ if (iph->protocol == ipprot->protocol && ipprot->err_handler)
+ {
+ ipprot->err_handler(err, (unsigned char *)(icmph + 1),
+ iph->daddr, iph->saddr, ipprot);
+ }
+
+ ipprot = nextip;
+ }
+ kfree_skb(skb, FREE_READ);
+}
+
+
+/*
+ * Handle ICMP_REDIRECT.
+ */
+
+static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb,
+ struct device *dev, unsigned long source)
+{
+ struct rtable *rt;
+ struct iphdr *iph;
+ unsigned long ip;
+
+ /*
+ * Get the copied header of the packet that caused the redirect
+ */
+
+ iph = (struct iphdr *) (icmph + 1);
+ ip = iph->daddr;
+
+ switch(icmph->code & 7)
+ {
+ case ICMP_REDIR_NET:
+ /*
+ * This causes a problem with subnetted networks. What we should do
+ * is use ICMP_ADDRESS to get the subnet mask of the problem route
+ * and set both. But we don't..
+ */
+#ifdef not_a_good_idea
+ ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY),
+ ip, 0, icmph->un.gateway, dev,0, 0);
+ break;
+#endif
+ case ICMP_REDIR_HOST:
+ /*
+ * Add better route to host.
+ * But first check that the redirect
+ * comes from the old gateway..
+ * And make sure it's an ok host address
+ * (not some confused thing sending our
+ * address)
+ */
+ rt = ip_rt_route(ip, NULL, NULL);
+ if (!rt)
+ break;
+ if (rt->rt_gateway != source || ip_chk_addr(icmph->un.gateway))
+ break;
+ printk("redirect from %s\n", in_ntoa(source));
+ ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY),
+ ip, 0, icmph->un.gateway, dev,0, 0);
+ break;
+ case ICMP_REDIR_NETTOS:
+ case ICMP_REDIR_HOSTTOS:
+ printk("ICMP: cannot handle TOS redirects yet!\n");
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Discard the original packet
+ */
+
+ kfree_skb(skb, FREE_READ);
+}
+
+
+/*
+ * Handle ICMP_ECHO ("ping") requests.
+ */
+
+static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+ unsigned long saddr, unsigned long daddr, int len,
+ struct options *opt)
+{
+ struct icmphdr *icmphr;
+ struct sk_buff *skb2;
+ struct device *ndev=NULL;
+ int size, offset;
+
+ icmp_statistics.IcmpOutEchoReps++;
+ icmp_statistics.IcmpOutMsgs++;
+
+ size = dev->hard_header_len + 64 + len;
+ skb2 = alloc_skb(size, GFP_ATOMIC);
+
+ if (skb2 == NULL)
+ {
+ icmp_statistics.IcmpOutErrors++;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+ skb2->free = 1;
+
+ /* Build Layer 2-3 headers for message back to source */
+ offset = ip_build_header(skb2, daddr, saddr, &ndev,
+ IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
+ if (offset < 0)
+ {
+ icmp_statistics.IcmpOutErrors++;
+ printk("ICMP: Could not build IP Header for ICMP ECHO Response\n");
+ kfree_skb(skb2,FREE_WRITE);
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /*
+ * Re-adjust length according to actual IP header size.
+ */
+
+ skb2->len = offset + len;
+
+ /*
+ * Build ICMP_ECHO Response message.
+ */
+ icmphr = (struct icmphdr *) (skb2->data + offset);
+ memcpy((char *) icmphr, (char *) icmph, len);
+ icmphr->type = ICMP_ECHOREPLY;
+ icmphr->code = 0;
+ icmphr->checksum = 0;
+ icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);
+
+ /*
+ * Ship it out - free it when done
+ */
+ ip_queue_xmit((struct sock *)NULL, ndev, skb2, 1);
+
+ /*
+ * Free the received frame
+ */
+
+ kfree_skb(skb, FREE_READ);
+}
+
+/*
+ * Handle ICMP Timestamp requests.
+ */
+
+static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+ unsigned long saddr, unsigned long daddr, int len,
+ struct options *opt)
+{
+ struct icmphdr *icmphr;
+ struct sk_buff *skb2;
+ int size, offset;
+ unsigned long *timeptr, midtime;
+ struct device *ndev=NULL;
+
+ if (len != 20)
+ {
+ printk(
+ "ICMP: Size (%d) of ICMP_TIMESTAMP request should be 20!\n",
+ len);
+ icmp_statistics.IcmpInErrors++;
+#if 1
+ /* correct answers are possible for everything >= 12 */
+ if (len < 12)
+#endif
+ return;
+ }
+
+ size = dev->hard_header_len + 84;
+
+ if (! (skb2 = alloc_skb(size, GFP_ATOMIC)))
+ {
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ icmp_statistics.IcmpOutErrors++;
+ return;
+ }
+ skb2->free = 1;
+
+/*
+ * Build Layer 2-3 headers for message back to source
+ */
+
+ offset = ip_build_header(skb2, daddr, saddr, &ndev, IPPROTO_ICMP, opt, len,
+ skb->ip_hdr->tos, 255);
+ if (offset < 0)
+ {
+ printk("ICMP: Could not build IP Header for ICMP TIMESTAMP Response\n");
+ kfree_skb(skb2, FREE_WRITE);
+ kfree_skb(skb, FREE_READ);
+ icmp_statistics.IcmpOutErrors++;
+ return;
+ }
+
+ /*
+ * Re-adjust length according to actual IP header size.
+ */
+ skb2->len = offset + 20;
+
+ /*
+ * Build ICMP_TIMESTAMP Response message.
+ */
+
+ icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset);
+ memcpy((char *) icmphr, (char *) icmph, 12);
+ icmphr->type = ICMP_TIMESTAMPREPLY;
+ icmphr->code = icmphr->checksum = 0;
+
+ /* fill in the current time as ms since midnight UT: */
+ midtime = (xtime.tv_sec % 86400) * 1000 + xtime.tv_usec / 1000;
+ timeptr = (unsigned long *) (icmphr + 1);
+ /*
+ * the originate timestamp (timeptr [0]) is still in the copy:
+ */
+ timeptr [1] = timeptr [2] = htonl(midtime);
+
+ icmphr->checksum = ip_compute_csum((unsigned char *) icmphr, 20);
+
+ /*
+ * Ship it out - free it when done
+ */
+
+ ip_queue_xmit((struct sock *) NULL, ndev, skb2, 1);
+ icmp_statistics.IcmpOutTimestampReps++;
+ kfree_skb(skb, FREE_READ);
+}
+
+
+
+
+/*
+ * Handle the ICMP INFORMATION REQUEST.
+ */
+
+static void icmp_info(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+ unsigned long saddr, unsigned long daddr, int len,
+ struct options *opt)
+{
+ /* Obsolete */
+ kfree_skb(skb, FREE_READ);
+}
+
+
+/*
+ * Handle ICMP_ADDRESS_MASK requests.
+ */
+
+static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+ unsigned long saddr, unsigned long daddr, int len,
+ struct options *opt)
+{
+ struct icmphdr *icmphr;
+ struct sk_buff *skb2;
+ int size, offset;
+ struct device *ndev=NULL;
+
+ icmp_statistics.IcmpOutMsgs++;
+ icmp_statistics.IcmpOutAddrMaskReps++;
+
+ size = dev->hard_header_len + 64 + len;
+ skb2 = alloc_skb(size, GFP_ATOMIC);
+ if (skb2 == NULL)
+ {
+ icmp_statistics.IcmpOutErrors++;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+ skb2->free = 1;
+
+ /*
+ * Build Layer 2-3 headers for message back to source
+ */
+
+ offset = ip_build_header(skb2, daddr, saddr, &ndev,
+ IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
+ if (offset < 0)
+ {
+ icmp_statistics.IcmpOutErrors++;
+ printk("ICMP: Could not build IP Header for ICMP ADDRESS Response\n");
+ kfree_skb(skb2,FREE_WRITE);
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /*
+ * Re-adjust length according to actual IP header size.
+ */
+
+ skb2->len = offset + len;
+
+ /*
+ * Build ICMP ADDRESS MASK Response message.
+ */
+
+ icmphr = (struct icmphdr *) (skb2->data + offset);
+ icmphr->type = ICMP_ADDRESSREPLY;
+ icmphr->code = 0;
+ icmphr->checksum = 0;
+ icmphr->un.echo.id = icmph->un.echo.id;
+ icmphr->un.echo.sequence = icmph->un.echo.sequence;
+ memcpy((char *) (icmphr + 1), (char *) &dev->pa_mask, sizeof(dev->pa_mask));
+
+ icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);
+
+ /* Ship it out - free it when done */
+ ip_queue_xmit((struct sock *)NULL, ndev, skb2, 1);
+
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+}
+
+
+/*
+ * Deal with incoming ICMP packets.
+ */
+
+int icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt,
+ unsigned long daddr, unsigned short len,
+ unsigned long saddr, int redo, struct inet_protocol *protocol)
+{
+ struct icmphdr *icmph;
+ unsigned char *buff;
+
+ /*
+ * Drop broadcast packets. IP has done a broadcast check and ought one day
+ * to pass on that information.
+ */
+
+ icmp_statistics.IcmpInMsgs++;
+
+
+ /*
+ * Grab the packet as an icmp object
+ */
+
+ buff = skb1->h.raw;
+ icmph = (struct icmphdr *) buff;
+
+ /*
+ * Validate the packet first
+ */
+
+ if (ip_compute_csum((unsigned char *) icmph, len))
+ {
+ /* Failed checksum! */
+ icmp_statistics.IcmpInErrors++;
+ printk("ICMP: failed checksum from %s!\n", in_ntoa(saddr));
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ }
+
+ /*
+ * Parse the ICMP message
+ */
+
+ if (ip_chk_addr(daddr) != IS_MYADDR)
+ {
+ if (icmph->type != ICMP_ECHO)
+ {
+ icmp_statistics.IcmpInErrors++;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ }
+ daddr=dev->pa_addr;
+ }
+
+ switch(icmph->type)
+ {
+ case ICMP_TIME_EXCEEDED:
+ icmp_statistics.IcmpInTimeExcds++;
+ icmp_unreach(icmph, skb1);
+ return 0;
+ case ICMP_DEST_UNREACH:
+ icmp_statistics.IcmpInDestUnreachs++;
+ icmp_unreach(icmph, skb1);
+ return 0;
+ case ICMP_SOURCE_QUENCH:
+ icmp_statistics.IcmpInSrcQuenchs++;
+ icmp_unreach(icmph, skb1);
+ return(0);
+ case ICMP_REDIRECT:
+ icmp_statistics.IcmpInRedirects++;
+ icmp_redirect(icmph, skb1, dev, saddr);
+ return(0);
+ case ICMP_ECHO:
+ icmp_statistics.IcmpInEchos++;
+ icmp_echo(icmph, skb1, dev, saddr, daddr, len, opt);
+ return 0;
+ case ICMP_ECHOREPLY:
+ icmp_statistics.IcmpInEchoReps++;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ case ICMP_TIMESTAMP:
+ icmp_statistics.IcmpInTimestamps++;
+ icmp_timestamp(icmph, skb1, dev, saddr, daddr, len, opt);
+ return 0;
+ case ICMP_TIMESTAMPREPLY:
+ icmp_statistics.IcmpInTimestampReps++;
+ kfree_skb(skb1,FREE_READ);
+ return 0;
+ /* INFO is obsolete and doesn't even feature in the SNMP stats */
+ case ICMP_INFO_REQUEST:
+ icmp_info(icmph, skb1, dev, saddr, daddr, len, opt);
+ return 0;
+ case ICMP_INFO_REPLY:
+ skb1->sk = NULL;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ case ICMP_ADDRESS:
+ icmp_statistics.IcmpInAddrMasks++;
+ icmp_address(icmph, skb1, dev, saddr, daddr, len, opt);
+ return 0;
+ case ICMP_ADDRESSREPLY:
+ /*
+ * We ought to set our netmask on receiving this, but
+ * experience shows it's a waste of effort.
+ */
+ icmp_statistics.IcmpInAddrMaskReps++;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ default:
+ icmp_statistics.IcmpInErrors++;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ }
+ /*NOTREACHED*/
+ kfree_skb(skb1, FREE_READ);
+ return(-1);
+}
+
+
+/*
+ * Perform any ICMP-related I/O control requests.
+ * [to vanish soon]
+ */
+
+int icmp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ switch(cmd)
+ {
+ default:
+ return(-EINVAL);
+ }
+ return(0);
+}