- To: Andreas Jaeger <aj at suse dot de>
- Subject: Re: libc/1887: CMSG_NXTHDR will not return last piece of ancillary data if more than one available
- From: "Eric S. Johnson" <esj at cs dot fiu dot edu>
- Date: Thu, 07 Sep 2000 16:36:29 +0000
- Cc: bugs at gnu dot org
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;
}