Subject: AW: [magma] IGMPv3 API / RFC3678

From: "Seehofer, Markus" <[email-masked]>
Date: Fri, 20 Jan 2006 16:05:28 +0100

Hello Stig,

thanks for the quick reply.

> I've had a quick look at your code, and it looks ok. I've 
> written some similar code myself and the problem I found on 
> some systems, I think Windows and some version of Linux 
> kernel (not sure if 2.6.12), was that I had to specify interface.
> 
> I've only tried the full state API on Linux, and that was the 
> protocol independent API. I believe that was working fine, 
> except for having to specify interface on some kernel versions.

>> I did two changes to the code.

--snip--
	  if_addr.s_addr = inet_addr(SRC_ADDRESS);
        err = setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, (char
*)&if_addr, sizeof(struct in_addr));
        if(-1 == err)
        {
                perror("ERROR; setsockopt failed");
                exit(1);
        }

        /* Bind socket */
        bzero((void *) &maddr, sizeof(struct sockaddr_in));
        maddr.sin_family = AF_INET;
        maddr.sin_addr.s_addr = htonl(INADDR_ANY);
        maddr.sin_port = htons(UDP_PORT);
--snip--

and 

instead of 
--snip--
set_filter(sockfd, MCAST_ADDRESS, MCAST_EXCLUDE, nsrcs, sources);
--snip--

i use now

--snip--
set_filter_full_state(sockfd, MCAST_ADDRESS, MCAST_EXCLUDE, nsrcs,
sources);
with:
void set_filter_full_state(const unsigned int sockfd, const char
*mcastaddr,
                        int fmode, int nsrcs, char **src)
{
        int err = 0;
        struct in_addr if_addr;
        struct in_addr mc_addr;

        if_addr.s_addr = inet_addr(SRC_ADDRESS);
        mc_addr.s_addr = inet_addr(MCAST_ADDRESS);

        err = setipv4sourcefilter(sockfd, if_addr, mc_addr,
MCAST_EXCLUDE, nsrcs, src);
        if (err!=0)
                perror("Error in setting filter");
}
--snip--

but i still get the sames error message as stated in the previous mail.
Don't know what i'm doing wrong. Do you (or somebody else) have some 
example code?
Does somebody have experience with the full state API under 
windows?

Regards,
 Markus

_______________________________________________
magma mailing list
[email-masked]
https://www1.ietf.org/mailman/listinfo/magma

---

From: Stig Venaas <[email-masked]>
Date: Fri, 20 Jan 2006 16:12:41 +0000

Seehofer, Markus wrote:
...
> Don't know what i'm doing wrong. Do you (or somebody else) have some 

Attached is some quick and dirty code I wrote that works on Linux (at 
least it did at the time I wrote it). It defines some stuff that were 
not in the glibc header files when I tested it, but is available in the 
most recent glibc. It uses the generic API supporting both IPv4 and 
IPv6. I implemented my own setsourcefilter() which was not available in 
glibc at the time.

Stig

> Does somebody have experience with the full state API under 

/* Stig Venaas <[email-masked]>
 * 2004.09.21
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifndef IP_ADD_SOURCE_MEMBERSHIP
struct ip_mreq_source {
    struct in_addr imr_multiaddr;  /* IP address of group */
    struct in_addr imr_interface;  /* IP address of interface */
    struct in_addr imr_sourceaddr; /* IP address of source */
};

#define IP_ADD_SOURCE_MEMBERSHIP        39
#define IP_DROP_SOURCE_MEMBERSHIP       40
#endif

#ifndef MCAST_JOIN_GROUP
struct group_req {
    uint32_t                gr_interface; /* interface index */
    struct sockaddr_storage gr_group;     /* group address */
};

#define MCAST_JOIN_GROUP                42
#endif

#ifndef MCAST_BLOCK_SOURCE
#define MCAST_BLOCK_SOURCE              43
#define MCAST_UNBLOCK_SOURCE            44
#endif

#ifndef MCAST_LEAVE_GROUP
#define MCAST_LEAVE_GROUP               45
#endif

#ifndef MCAST_JOIN_SOURCE_GROUP
struct group_source_req {
    uint32_t                gsr_interface; /* interface index */
    struct sockaddr_storage gsr_group;     /* group address */
    struct sockaddr_storage gsr_source;    /* source address */
};

#define MCAST_JOIN_SOURCE_GROUP         46
#define MCAST_LEAVE_SOURCE_GROUP        47
#endif

#ifndef MCAST_EXCLUDE
#define MCAST_EXCLUDE 0
#endif
#ifndef MCAST_INCLUDE
#define MCAST_INCLUDE 1
#endif

#ifndef HAVE_SETSOURCEFILTER
int setsourcefilter(int s, uint32_t interface,
		    struct sockaddr *group, socklen_t grouplen,
		    /* RFC 3678 says numsrc is uint_t! */
		    uint32_t fmode, uint32_t numsrc,
		    struct sockaddr_storage *slist) {
    struct group_req greq;
    struct group_source_req gsreq;
    int slevel, sopt, i;

    slevel = group->sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6;
    
    if (!numsrc || fmode == MCAST_EXCLUDE) {
	/* should we allow numsrc == 0? */
	/* do we need to join group before blocking sources? */
	greq.gr_interface = interface;
	memcpy(&greq.gr_group, group, grouplen);
	if (setsockopt(s, slevel, MCAST_JOIN_GROUP,
		       (char *)&greq, sizeof(greq)) < 0)
	    return -1;

	if (!numsrc)
	    return 0;
    }
    
    /* do we really need to set interface when blocking? Can you join group
       on multiple interfaces and block different sources? */
    gsreq.gsr_interface = interface;
    memcpy(&gsreq.gsr_group, group, grouplen);
    sopt = fmode == MCAST_EXCLUDE ? MCAST_BLOCK_SOURCE : MCAST_JOIN_SOURCE_GROUP;

    for (i = 0; i < numsrc; i++) {
	gsreq.gsr_source = slist[i];
	if (setsockopt(s, slevel, sopt, (char *)&gsreq, sizeof(gsreq)) < 0) {
		/* We should clean up. When blocking, enough to leave group, or
		   unblock sources also? */
	    return -1;
	}
    }
    
    return 0;
}
#endif

extern int errno;
extern int optind;
extern char *optarg;

void usage() {
    fprintf(stderr, "Usage:\tmctest -t [-options] multicast-group [ < in ]\n");
    fprintf(stderr, "\tmctest -r [-options] multicast-group [ > out]\n");
    fprintf(stderr, "Common options:\n");
    fprintf(stderr, "\t-p ##   port number to send to or listen at (default 5001)\n");
    fprintf(stderr, "Options specific to -t:\n");
    fprintf(stderr, "\t-w ##   number of microseconds to wait between each write\n");
    fprintf(stderr, "Options specific to -r:\n");
    fprintf(stderr, "\t-I if   specify the network interface (e.g. eth0) to use\n");
    exit(1);
};

void err(char *s) {
    fprintf(stderr,"mctest: ");
    perror(s);
    fprintf(stderr, "errno=%d\n", errno);
    exit(1);
}

int addrlist_pton(struct sockaddr_storage **nal, char *pal) {
    char *s, *t;
    int i, n, e;
    struct addrinfo hints, *res;
    
    printf("0:%s\n",pal);
    if (!pal)
	return 0;
    
    /* find number of addresses */
    for (n=1, s = pal; *s; s++)
	if (*s == ',')
	    n++;

    *nal = malloc(n * sizeof(struct sockaddr_storage));
    if (!*nal)
	return 0;

    memset(&hints, '\0', sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    
    for (i = 0, s = pal;; i++) {
	printf("1:%s\n",s);
	t = strchr(s, ',');
	if (t)
	    *t = '\0';
	if ((e = getaddrinfo(s, NULL, &hints, &res))) {
	    fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(n));
	    free(*nal);
	    return 0;
	}
	/* We only use first value, a name might resolve to multiple */
	memcpy(*nal+i, res->ai_addr, res->ai_addrlen);
	freeaddrinfo(res);
	if (!t)
	    break;
	s = t + 1;
    }

    return n;
}

int mcaddr(struct sockaddr_storage *ss) {
    switch (((struct sockaddr *)ss)->sa_family) {
    case AF_INET: return IN_MULTICAST(ntohl(((struct sockaddr_in *)ss)->sin_addr.s_addr));
    case AF_INET6: return IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)ss)->sin6_addr);
    }
    return 0;
}

int main(int argc, char **argv) {
    int c;
    int trans;
    char *port = "5001";
    static long wait = 0;          /* usecs to wait between each write */
    char *ifname = NULL;
    struct sockaddr_storage *addrlist, *slist;
    struct sockaddr *group;
    socklen_t grouplen;
    int naddr, nsrc;
    int fmode;
    int s;

    /*
struct sockaddr_storage frominet;
struct addrinfo hints, *res, *res0;
struct ipv6_mreq mreq6;
struct ip_mreq mreq;

int one = 1;

int af =  AF_UNSPEC; */            /* Address family to be determined */

    if (argc < 2)
	usage();

    while ((c = getopt(argc, argv, "rtp:w:I:")) != -1) {
	switch (c) {
	case 'r':
	    trans = 0;
	    break;
	case 't':
	    trans = 1;
	    break;
	case 'p':
	    port = optarg;
	    break;
	case 'w':
	    wait = strtol(optarg, (char **)NULL, 10);
	    break;
	case 'I':
	    ifname = optarg;
	    break;
	default:
	    usage();
	}
    }
    if (optind + 1 != argc)
	usage();

    naddr = addrlist_pton(&addrlist, argv[optind]);
    if (naddr == 0)
	err("No valid names or addresses specified");

    nsrc = naddr - 1;
    
    if (mcaddr(addrlist)) {
	fmode = MCAST_EXCLUDE;
	group = (struct sockaddr *)addrlist;
	slist = nsrc ? addrlist + 1 : NULL;
    } else if (mcaddr(addrlist + nsrc)) {
	fmode = MCAST_INCLUDE;
	group = (struct sockaddr *)(addrlist + nsrc);
	slist = addrlist;
    } else
	err("First or last address must be a multicast address\n");

    grouplen = group->sa_family == AF_INET
	? sizeof(struct sockaddr_in)
	: sizeof(struct sockaddr_in6);
    
    if ((s = socket(group->sa_family, SOCK_DGRAM, 0)) < 0)
	err("socket");

    if (setsourcefilter(s, ifname ? if_nametoindex(ifname) : 0, group, grouplen,
			fmode, nsrc, slist) < 0)
	err("setsourcefilter");
	
    sleep(600);
    return 0;
}