destination unreachables with raw sockets and a bind

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi

if you open an ICMP raw socket and then bind to an address (like you do with the -S option to BSD ping or -I to linux ping) it appears that you do not get ICMP unreachables if any are sent to you.

compare the call to __raw_v4_lookup in icmp.c icmp_unreach() to that in raw.c raw_v4_input() to see what i mean.

i've included test code (unreach_send.c, unreach_listen.c) so that you can test it yourself, and a patch that fixes the kernel. the send needs to be run on a seperate machine to trigger the address comparison error.

patch is against linux-2.4.19

--
Matthew Luckie
kluckie@ihug.co.nz
--- icmp.c.orig	Tue Nov 19 13:10:30 2002
+++ icmp.c	Tue Nov 19 13:24:53 2002
@@ -16,6 +16,8 @@
  *	Other than that this module is a complete rewrite.
  *
  *	Fixes:
+ *		Matthew Luckie	:	fix call to __raw_v4_lookup in	 
+ *					icmp_unreach	
  *	Clemens Fruhwirth	:	introduce global icmp rate limiting
  *					with icmp type masking ability instead
  *					of broken per type icmp timeouts.
@@ -649,8 +651,8 @@
 	read_lock(&raw_v4_lock);
 	if ((raw_sk = raw_v4_htable[hash]) != NULL) 
 	{
-		while ((raw_sk = __raw_v4_lookup(raw_sk, protocol, iph->daddr,
-						 iph->saddr, skb->dev->ifindex)) != NULL) {
+		while ((raw_sk = __raw_v4_lookup(raw_sk, protocol, iph->saddr,
+						 iph->daddr, skb->dev->ifindex)) != NULL) {
 			raw_err(raw_sk, skb, info);
 			raw_sk = raw_sk->next;
 			iph = (struct iphdr *)skb->data;
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/unistd.h>
#include <netinet/in.h>
#include <errno.h>

int main(int argc, char *argv[])
{
  fd_set              rfds;
  int                 nfds;
  int                 bound;
  int                 unbound;
  struct sockaddr_in  sin;
  char               *bind_addr = "192.168.1.1";
  u_char              buf[2048];
  size_t              bufsize;
  socklen_t           len;

  bzero(&sin, sizeof(sin));
  sin.sin_family = AF_INET;
  inet_pton(AF_INET, bind_addr, &sin.sin_addr);

  bound   = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  unbound = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

  if(bound == -1 || unbound == -1)
    {
      printf("could not open sockets\n");
      return -1;
    }

  if(bind(bound, (struct sockaddr *)&sin, sizeof(sin)) == -1)
    {
      printf("could not bind to %s, errno %d\n", bind_addr, errno);
      return -1;
    }

  while(1)
    {
      FD_ZERO(&rfds);

      FD_SET(bound,   &rfds);
      FD_SET(unbound, &rfds);

      if(bound < unbound) nfds = unbound;
      else                nfds = bound;

      if(select(nfds+1, &rfds, NULL, NULL, NULL) < 1)
	{
	  return -1;
	}

      if(FD_ISSET(bound, &rfds))
	{
	  printf("got something on the bound socket\n");
	  recvfrom(bound, buf, bufsize, 0, (struct sockaddr *)&sin, &len);
	}

      if(FD_ISSET(unbound, &rfds))
	{
	  printf("got something on the unbound socket\n");
	  recvfrom(unbound, buf, bufsize, 0, (struct sockaddr *)&sin, &len);
	}
    }

  return 0;
}
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/unistd.h>
#include <netinet/in.h>
#include <errno.h>

#define ICMP_DEST_UNREACH	3	/* Destination Unreachable	*/
#define ICMP_HOST_UNREACH	1	/* Host Unreachable		*/

struct icmphdr {
  u_int8_t  type;
  u_int8_t  code;
  u_int16_t checksum;
  u_int16_t id;
  u_int16_t sequence;
};

int main(int argc, char *argv[])
{
  fd_set              rfds;
  int                 nfds;
  int                 sock;
  struct sockaddr_in  sin;
  char               *dest_addr = "192.168.1.1";
  u_char              buf[sizeof(struct icmphdr)];
  size_t              bufsize;
  struct icmphdr     *icmp;
  socklen_t           sa_len = sizeof(struct sockaddr_in);
  int                 len    = sizeof(buf);

  bzero(&sin, sizeof(sin));
  inet_pton(AF_INET, dest_addr, &sin.sin_addr);
  sin.sin_family = AF_INET;

  sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  if(sock == -1)
    {
      printf("could not open socket: errno %d\n", errno);
      return -1;
    }

  icmp = (struct icmphdr *)buf;
  bzero(buf, sizeof(buf));
  icmp->type     = ICMP_DEST_UNREACH;
  icmp->code     = ICMP_HOST_UNREACH;
  icmp->checksum = 0;

  if(sendto(sock, buf, len, 0, (struct sockaddr *)&sin, sa_len) == -1)
    {
      printf("could not sendto, errno %d\n", errno);
      return -1;
    }

  return 0;
}

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux 802.1Q VLAN]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Git]     [Bugtraq]     [Yosemite News and Information]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux PCI]     [Linux Admin]     [Samba]

  Powered by Linux