This is the mail archive of the libc-alpha@sources.redhat.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

[Various] libc/1887: CMSG_NXTHDR will not return last piece of ancillary data if more than one available



Hi,

Eric sent us the appended report.  I think he's right - but couldn't
verify it here (not IPv6 network at home ;-).  I'm appending some
patches.  Are these ok?  Philip, can you look over it, please?

Thanks,
Andreas

2000-09-13  Andreas Jaeger  <aj@suse.de>

	* sysdeps/unix/sysv/linux/bits/socket.h (__cmsg_nxthdr): Fix test
	for no more entries.
	* sysdeps/unix/sysv/aix/bits/socket.h (__cmsg_nxthdr): Likewise.
	* sysdeps/unix/sysv/linux/mips/bits/socket.h (__cmsg_nxthdr):
	Likewise.
	Reported by Eric S. Johnson <esj@cs.fiu.edu>, closes PR libc/1887.
	
============================================================
Index: sysdeps/unix/sysv/linux/bits/socket.h
--- sysdeps/unix/sysv/linux/bits/socket.h	2000/07/27 15:12:23	1.39
+++ sysdeps/unix/sysv/linux/bits/socket.h	2000/09/13 13:54:15
@@ -267,7 +267,7 @@
   if ((unsigned char *) (__cmsg + 1) >= ((unsigned char *) __mhdr->msg_control
 					 + __mhdr->msg_controllen)
       || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len)
-	  >= ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))
+	  > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))
     /* No more entries.  */
     return 0;
   return __cmsg;
============================================================
Index: sysdeps/unix/sysv/aix/bits/socket.h
--- sysdeps/unix/sysv/aix/bits/socket.h	2000/04/02 08:01:15	1.2
+++ sysdeps/unix/sysv/aix/bits/socket.h	2000/09/13 13:54:15
@@ -243,7 +243,7 @@
   if ((unsigned char *) (__cmsg + 1) >= ((unsigned char *) __mhdr->msg_control
 					 + __mhdr->msg_controllen)
       || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len)
-	  >= ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))
+	  > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))
     /* No more entries.  */
     return 0;
   return __cmsg;
============================================================
Index: sysdeps/unix/sysv/linux/mips/bits/socket.h
--- sysdeps/unix/sysv/linux/mips/bits/socket.h	2000/07/27 15:12:23	1.14
+++ sysdeps/unix/sysv/linux/mips/bits/socket.h	2000/09/13 13:54:15
@@ -267,7 +267,7 @@
   if ((unsigned char *) (__cmsg + 1) >= ((unsigned char *) __mhdr->msg_control
 					 + __mhdr->msg_controllen)
       || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len)
-	  >= ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))
+	  > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))
     /* No more entries.  */
     return 0;
   return __cmsg;


Subject: Topics

Topics:
   Re: libc/1887: CMSG_NXTHDR will not return last piece of ancillary data if more than one available
   Re: libc/1887: CMSG_NXTHDR will not return last piece of ancillary data if more than one available 
   libc/1887: CMSG_NXTHDR will not return last piece of ancillary data if more than one available



The following reply was made to PR libc/1887; it has been noted by GNATS.

From: Andreas Jaeger <aj@suse.de>
To: esj@cs.fiu.edu
Cc: bugs@gnu.org
Subject: Re: libc/1887: CMSG_NXTHDR will not return last piece of ancillary data if more than one available
Date: 07 Sep 2000 09:19:50 +0200

 >>>>> esj  writes:
 
 >> Number:         1887
 >> Category:       libc
 >> Synopsis:       CMSG_NXTHDR will not return last piece of ancillary data if more than one available
 
  > I can provide much more detailed code examples if needed
 
 Please do so - send a complete program to show the problem.
 
 Thanks,
 Andreas
 -- 
  Andreas Jaeger
   SuSE Labs aj@suse.de
    private aj@arthur.inka.de
     http://www.suse.de/~aj







This is a code snipped out of zebra (where I noticed the problem).
Im using a GNU/Linux 2.2.16 kernel with glibc 2.1.3. 

You need to run this on a  machine on a network which has a IPv6
router advertising itself. 

This code opens up a raw v6 socket to receive the router advertisements.
It sets options to receive extra info about the hop limit and interface
the packet arrived on. 

It then does a recvmsg, then trys to use the cmsg macro's to retrieve the
both the pktinfo and hoplimit data. But due to the bug in __cmsg_nxthdr
it only receives the pktinfo. A correct __cmsg_nxthdr is ifdef'ed ESJFIX.

cc -o rtadv rtadv.c  

will have it run using normal glibc2.1.3 version of __cmsg_nxthdr.

cc -DESJFIX -o rtadv rtadv.c

will have it run with a internal __cmsg_nxthdr that works correctly
and also has  bunch of debug printfs ;)



E
----

/* Router advertisement
 * Copyright (C) 1999 Kunihiro Ishiguro
 *
 * This file is part of GNU Zebra.
 *
 * GNU Zebra 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, or (at your option) any
 * later version.
 *
 * GNU Zebra is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GNU Zebra; see the file COPYING.  If not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.  
 */


#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

#include <net/if.h>

#include <net/route.h>

#include <netdb.h>

#include <arpa/inet.h>
#include <arpa/telnet.h>


#include <netinet/ip6.h>

#include <netinet/icmp6.h>

/* 

   broken __cmsg_nxthdr from /usr/include/bits/socket.h

struct cmsghdr *
__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)
{
  if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr))

    return 0;

  __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg
                               + ((( __cmsg->cmsg_len ) + sizeof (size_t) - 1) & ~(sizeof (size_t) - 1)) );
  if ((unsigned char *) (__cmsg + 1) >= ((unsigned char *) __mhdr->msg_control
                                         + __mhdr->msg_controllen)
      || ((unsigned char *) __cmsg + ((( __cmsg->cmsg_len ) + sizeof (size_t) - 1) & ~(sizeof (size_t) - 1))
          >= ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))

    return 0;
  return __cmsg;
}
*/


#ifdef ESJFIX

struct cmsghdr *
__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)
{
  printf ("ESJ in my __cmsg_nxthdr\n");
  if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) return 0;
  printf("in __CMSG_NXTHDR -- passed first len check\n");

printf("in __CMSG_NXTHDR -- MHDR __mhdr->msg_control = %x __mhdr->msg_controllen =%d\n",__mhdr->msg_control,__mhdr->msg_controllen);
printf("in __CMSG_NXTHDR -- CMSG __cmsg(old) %x __cmsg->cmsg_len = %d\n", __cmsg,__cmsg->cmsg_len);

  __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg
         + ((( __cmsg->cmsg_len ) 
               + sizeof (size_t) - 1) & ~(sizeof (size_t) - 1)) );

printf("in __CMSG_NXTHDR -- CMSG __cmsg(new)=%x __cmsg->cmsg_len = %d\n", __cmsg,__cmsg->cmsg_len);



printf("in __CMSG_NXTHDR -- before 1st if -- ls = %x\n",(unsigned char *) (__cmsg + 1));
printf("in __CMSG_NXTHDR -- before 1st if -- rs = %x\n",((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen));

  if (
      (unsigned char *) (__cmsg + 1) >= 
      ((unsigned char *) __mhdr->msg_control
                       + __mhdr->msg_controllen)) {
	printf("in __CMSG_NXTHDR -- first if failed\n");
	return 0;
  }
      

printf ("in __CMSG_NXTHDR -- before 2nd if ls = %x\n",((unsigned char *)__cmsg + ((( __cmsg->cmsg_len ) + sizeof (size_t) - 1) & ~(sizeof (size_t) - 1) ) ) );
printf ("in __CMSG_NXTHDR -- before 2nd if rs = %x\n",((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen) );

if  (((unsigned char *) __cmsg + ((( __cmsg->cmsg_len ) + sizeof (size_t) - 1) & ~(sizeof (size_t) - 1))
          > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))
   /* The > above is the the fix. In libc it is >= */
   {
     printf("n __CMSG_NXTHDR -- second if failed\n");
     return 0;
   }

  return __cmsg;
}


#endif


#define ALLNODE   "ff02::1"
#define ALLROUTER "ff02::2"


int
rtadv_recv_packet (int sock, u_char *buf, int buflen,
		   struct sockaddr_in6 *from, unsigned int *ifindex,
		   int *hoplimit)
{
  int ret;
  int cnt;
  struct msghdr msg;
  struct iovec iov;
  struct cmsghdr  *cmsgptr;
  struct in6_addr dst;

  char adata[1024];

  /* Fill in message and iovec. */
  msg.msg_name = (void *) from;
  msg.msg_namelen = sizeof (struct sockaddr_in6);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  msg.msg_control = (void *) adata;
  msg.msg_controllen = sizeof adata;
  iov.iov_base = buf;
  iov.iov_len = buflen;

  /* If recvmsg fail return minus value. */
  ret = recvmsg (sock, &msg, 0);
  if (ret < 0)
    return ret;

printf("in RTADV_RECV_PACKET msg.msg_controllen = %d\n",msg.msg_controllen);
for (cnt=0; cnt < msg.msg_controllen ; cnt++ ) {
	if ( ! (cnt % 16 )) printf("\n");
	printf("%2x ",(unsigned char)adata[cnt]);
}
printf("\n");

  for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
       cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) 
    {

printf("in RTADV_RECV_PACKET main for loop --- cmsgptr->cmsg_len == %d cmsgptr->cmsg_level == %d, cmsgptr->cmsg_type == %d\n",cmsgptr->cmsg_len,cmsgptr->cmsg_level,cmsgptr->cmsg_type);

      /* I want interface index which this packet comes from. */
      if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
	  cmsgptr->cmsg_type == IPV6_PKTINFO) 
	{
	  struct in6_pktinfo *ptr;
	  
	  ptr = (struct in6_pktinfo *) CMSG_DATA (cmsgptr);
	  *ifindex = ptr->ipi6_ifindex;
	  memcpy(&dst, &ptr->ipi6_addr, sizeof(ptr->ipi6_addr));
          printf("pktinfo received \n");
        }

      /* Incoming packet's hop limit. */
      if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
	  cmsgptr->cmsg_type == IPV6_HOPLIMIT) {
	*hoplimit = *((int *) CMSG_DATA (cmsgptr));
        printf("hoplimit received = %d\n",*hoplimit);
      }
    }
  return ret;
}

void
rtadv_process_packet (u_char *buf, int len, unsigned int ifindex, int hoplimit)
{
  struct icmp6_hdr *icmph;
  struct interface *ifp;
  struct zebra_if *zif;

  /* ICMP message length check. */
  if (len < sizeof (struct icmp6_hdr))
    {
      printf ("Invalid ICMPV6 packet length: %d\n", len);
      return;
    }

  icmph = (struct icmp6_hdr *) buf;

  /* ICMP message type check. */
  if (icmph->icmp6_type != ND_ROUTER_SOLICIT &&
      icmph->icmp6_type != ND_ROUTER_ADVERT)
    {
      printf ("Unwanted ICMPV6 message type: %d\n", icmph->icmp6_type);
      return;
    }

  /* Hoplimit check. */
  if (hoplimit != 255)
    {
      printf ("Invalid hoplimit %d for router advertisement ICMP packet\n",
		 hoplimit);
    }

  /* Check ICMP message type. */
  if (icmph->icmp6_type == ND_ROUTER_SOLICIT)
    printf ("router solicit\n");
  else if (icmph->icmp6_type == ND_ROUTER_ADVERT)
    printf ("router advert\n");

  return;
}

int
rtadv_make_socket ()
{
  int sock;
  int ret;
  struct icmp6_filter filter;

  sock = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
  if (sock < 0)
    {
      printf ("can't create router advertisement socket: %s\n", 
		strerror (errno));
      return -1;
    }



  ret = setsockopt_ipv6_pktinfo (sock, 1);
printf("setsockopt_ipv6_pktinfo = %d\n",ret);
  if (ret < 0)
    return ret;


  ret = setsockopt_ipv6_checksum (sock, 2);
printf("setsockopt_ipv6_checksum = %d\n",ret);
  if (ret < 0)
    return ret;

  ret = setsockopt_ipv6_unicast_hops (sock, 255);
printf("setsockopt_ipv6_unicast_hops = %d\n",ret);
  if (ret < 0)
    return ret;

  ret = setsockopt_ipv6_multicast_hops (sock, 255);
printf("setsockopt_ipv6_multicast_hops = %d\n",ret);
  if (ret < 0)
    return ret;

  ret = setsockopt_ipv6_hoplimit (sock, 1);
printf("setsockopt_ipv6_hoplimit = %d\n",ret);
  if (ret < 0)
    return ret;

  ICMP6_FILTER_SETBLOCKALL(&filter);
  ICMP6_FILTER_SETPASS (ND_ROUTER_SOLICIT, &filter);
  ICMP6_FILTER_SETPASS (ND_ROUTER_ADVERT, &filter);

  ret = setsockopt (sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
		    sizeof (struct icmp6_filter));
  if (ret < 0)
    {
      printf ("ICMP6_FILTER set fail: %s\n", strerror (errno));
      return ret;
    }

  return sock;
}

#define RTADV_MSG_SIZE 4096

int
main (argc,argv)
int argc;
char **argv;
{
  int sock;
  int len;
  u_char buf[RTADV_MSG_SIZE];
  struct sockaddr_in6 from;
  unsigned int ifindex;
  int hoplimit = -1;

  sock=rtadv_make_socket();
  printf("sock = %d\n",sock);

while (1) {
  len = rtadv_recv_packet (sock, buf, BUFSIZ, &from, &ifindex, &hoplimit);

  if (len < 0) 
    {
      printf ("\nrouter solicitation recv failed: %s.\n", strerror (errno));
      return len;
    }

  rtadv_process_packet (buf, len, ifindex, hoplimit);
}

  return 0;
}

/* setsockopt functions
 * Copyright (C) 1999 Kunihiro Ishiguro
 *
 * This file is part of GNU Zebra.
 *
 * GNU Zebra 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, or (at your option) any
 * later version.
 *
 * GNU Zebra is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GNU Zebra; see the file COPYING.  If not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.  
 */

/* Set IPv6 packet info to the socket. */
int
setsockopt_ipv6_pktinfo (int sock, int val)
{
  int ret;
    
  ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
  if (ret < 0)
    printf ("can't setsockopt IPV6_PKTINFO : %s", strerror (errno));
  return ret;
}

/* Set multicast hops val to the socket. */
int
setsockopt_ipv6_checksum (int sock, int val)
{
  int ret;

  ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
  if (ret < 0)
    printf ("can't setsockopt IPV6_CHECKSUM");
  return ret;
}

/* Set multicast hops val to the socket. */
int
setsockopt_ipv6_multicast_hops (int sock, int val)
{
  int ret;

  ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val));
  if (ret < 0)
    printf ("can't setsockopt IPV6_MULTICAST_HOPS");
  return ret;
}

/* Set multicast hops val to the socket. */
int
setsockopt_ipv6_unicast_hops (int sock, int val)
{
  int ret;

  ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
  if (ret < 0)
    printf ("can't setsockopt IPV6_UNICAST_HOPS");
  return ret;
}

int
setsockopt_ipv6_hoplimit (int sock, int val)
{
  int ret;

  ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
  if (ret < 0)
    printf ("can't setsockopt IPV6_HOPLIMIT");
  return ret;
}









>Number:         1887
>Category:       libc
>Synopsis:       CMSG_NXTHDR will not return last piece of ancillary data if more than one available
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    libc-gnats
>State:          open
>Class:          sw-bug
>Submitter-Id:   unknown
>Arrival-Date:   Wed Sep 06 13:20:02 EDT 2000
>Last-Modified:
>Originator:     esj@cs.fiu.edu
>Organization:
net
>Release:        2.1.3
>Environment:
GNU/Linux 2.2.16 kernel RH 6.2 Distro
>Description:
If more than one peice of ancillary data is returned from a recvmsg call
then the last peice is never returned if you loop using CMSG_NXTHDR.

The problem seems to be in the __cmsg_nxthdr function which CMSG_NXTHDR
calls. The last if clause should be >, not >=
>How-To-Repeat:
Try to grab two peices of ancillary data in a recvmsg. (for example IPV6_PKTINFO
and IPV6_HOPLIMIT) Though the kernel returns both, CMSG_NXTHDR will return NULL
after the first.

I can provide much more detailed code examples if needed
>Fix:
>Audit-Trail:
>Unformatted:





-- 
 Andreas Jaeger
  SuSE Labs aj@suse.de
   private aj@arthur.inka.de
    http://www.suse.de/~aj

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]