[Voyage-linux] wlan0/hostap/kernel drops packets dst for 224.0.0.2 when the src addr does not match the subnet of the listening interface?

LEE, Yui-wah (Clement) (spam-protected)
Thu Jan 26 05:51:17 HKT 2006


Hi,

I am using voyage-linux (0.1sarge) on soekris 4521 and
hit the following strange phenomenon.  Either I did
something wrong, or I may have hit a strange bug
somewhere in the kernel, the wlan0 driver, or hostap.

I have a program "ma" that listens on all the packets
destined to 224.0.0.2 (ALL-ROUTERS.MCAST.NET.) on
wlan0, which is configured to be of address
10.0.0.1/24.  The program issues the usual setsockopt()
call (SOL_IP and IP_ADD_MEMBERSHIP) to tell the
interface to join the multicast group.  The call
completed without any complains from the system.  After
that, the program ma sits on a select() loop waiting
from some packets from the socket.

Then, I noticed that if the client sends a packet
destinated to 224.0.0.2 with a source address that is
in the subnet of 10.0.0.1/24 (e.g. 10.0.0.106), ma
would get the packet.  If, on the other hand, the
client sends a similar packet but with a source address
that is OUTSIDE of the subnet (e.g. 10.0.1.106), then
ma would NOT get the packets.

I tried two different versions of kernels (2.6.8 and
2.4.26) and both gave the same result.

I think this may be a bug somewhere in the wlan0 (or
hostap) or the kernel, because

1. I tried the same program ma on another soekris
   machine running a Redhat (7.0) system with kernel
   2.4.18, and the above problem did NOT occur (i.e. ma
   gets all packets irrespective of the source address)

2. I tried running ma on another interface ("eth0"
   instead of "wlan0"), and the above problem did NOT
   occur.

3. The "ma" expected behavior is required for a
   Mobile-IP mobility agent (RFC3344, Section 2.3, 2nd
   last paragraph of Page 23 -- in case you are
   interested).  Also, I think the multicast join is
   based on the *interface* but not on the *interface
   address*, right?

I also verified that when ma was run, "ip maddr show
wlan0" showed

  workit1:~# ip maddr show wlan0
  5:      wlan0
  	  link  01:00:5e:00:00:02
  	  link  01:00:5e:00:00:01
  	  inet  224.0.0.2
  	  inet  224.0.0.1

indicating that the multicast join instruction had been
made through to the kernel.

I attached my test program ma2.cc in case someone want
to look at the code.

Thanks!

Clement



-------------- next part --------------
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <stdio.h>
#include <string.h>
#include <systools/utils.hh>
#include <net/if.h>

typedef struct {
    unsigned int	ipi_ifindex;
    struct in_addr	ipi_spec_dst;
    struct in_addr	ipi_addr;
} linux_pktinfo;

void processIcmp(int s) {

    unsigned char rawbuf[1024];
    struct sockaddr_in from;
    struct msghdr msg;
    struct iovec iov[1];
    memset(&msg, 0, sizeof(msghdr));
    linux_pktinfo *pktinfo = NULL;

    msg.msg_name = (char *)&from;
    msg.msg_namelen = sizeof(from);

    struct cmsghdr *cmptr;
    union {
	struct cmsghdr cm;
	char	   control[CMSG_SPACE(sizeof(*pktinfo))];
    } ctrl;

    msg.msg_control = ctrl.control;
    msg.msg_controllen = sizeof(ctrl.control);

    memset(rawbuf, 0, sizeof(rawbuf));
    iov[0].iov_base = rawbuf;
    iov[0].iov_len = sizeof(rawbuf);
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;

    int r = recvmsg(s, &msg, 0);

    if ( r<0 ) {
	printf("recvfrom error.\n");
    } else {
	printf("recvfrom got %d byte\n", r);
    }
}

int main(int argc, char *argv[]) {
    int        s;
    char      *listenIfaceString;

    if (argc!=2) {
	printf("Usage: %s [<iface_to_listen_on>]\n", argv[0]);
	return -1;
    }
    listenIfaceString = argv[1];

    if ( (s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0 ) {
	perror("could not open RAW socket");
	return -1;
    }

    long bcast = 1;
    if ( setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&bcast,
		    sizeof(long)) == -1 ) {
	perror("could not setsockopt bcast");
	return -1;
    }

    u_char loop = 0;
    if ( setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loop,
		    sizeof(u_char)) == -1 ) {
	perror("could not setsockopt ip_multicast_loop");
	return -1;
    }

    /* SOL_IP,	IP_PKTINFO ? */
    long icmp_pktinfo = 1;
    if ( setsockopt(s, SOL_IP, IP_PKTINFO, (char *)&icmp_pktinfo,
		    sizeof(long)) < 0 ) {
	perror("could not setsockopt ip_pktinfo");
	return -1;
    }

    struct ip_mreqn imrn;
    memset(&imrn, 0, sizeof(imrn));
    imrn.imr_multiaddr.s_addr = inet_addr("224.0.0.2");
    imrn.imr_ifindex = if_nametoindex(listenIfaceString);
    if ( setsockopt(s, SOL_IP, IP_ADD_MEMBERSHIP, (char *)&imrn,
		    sizeof(imrn)) < 0 ) {
	perror("could not join mcast grp 224.0.0.2");
	return -1;
    }

    for(;;) {
	fd_set readfds;
	FD_ZERO(&readfds);

	FD_SET(s, &readfds);

	Tval next = Tval(0, 50000);
	if ( select(FD_SETSIZE, &readfds, NULL, NULL, &next) < 0 ) {
	    perror("error in select");
	}

	if (FD_ISSET(s, &readfds)) {
	    printf("select() returned on icmp socket\n");
	    processIcmp(s);
	}
    }
    
}


More information about the Voyage-linux mailing list