[prev in list] [next in list] [prev in thread] [next in thread] 

List:       netbsd-tech-net
Subject:    Re: IP Multicast
From:       David Young <dyoung () pobox ! com>
Date:       2008-09-22 23:12:46
Message-ID: 20080922231246.GQ26731 () che ! ojctech ! com
[Download RAW message or body]

On Fri, Sep 19, 2008 at 02:06:29PM -0400, Neel Sheyal wrote:
> Hi,
> 
>     I have two netbsd hosts both with two NICs.
> 
> HOSTA: ex0: 192.168.21.2/24  and ex1: 172.16.18.2/16
> HOSTB: ex0: 192.168.22.2/24  and ex1: 172.18.20.2/16
> 
> There is an application sending multicast traffic on hostA to
> 239.198.1.2. There is another application on hostB which is subsribing
> to this group.
> 
> The application on hostA or hostB does not specific the outgoing
> interface for sending traffic or joining. I have also not send the
> routing table with the multicast group information.
> 
> How does the OS know which interface to send the multicast traffic as in hostA?
> 
> How does the OS know which interface to subscribe to for receiving
> multicast traffic as in hostB?

Once, I figured out a lot about the multicast API on NetBSD, with the
help of the kernel source code, because I was writing a routing daemon.
Let me try to answer your questions with my best recollection of how
the attached code is used.  Please refer to ip(4), also.

Suppose that I want to use a socket, `s', to transmit multicasts on a
particular interface, given by its index.  I call beacon_tx_mcast_setup()
on `s'.

I also want to receive multicasts.  I call beacon_rx_mcast_joinleave() to
join/leave a multicast group on a particular interface.  `s' is a datagram
socket.  `optname' is either IP_ADD_MEMBERSHIP or IP_DROP_MEMBERSHIP,
`addr' is the multicast address, `ifindex' the interface index from,
say, if_nametoindex(3).

I want the kernel to tell my app which interface it reads each multicast
packet from, using the "packet ancillary data".  (See recvmsg(2).)
I call beacon_rx_pktinfo_enable(s, 1):

Dave

-- 
David Young             OJC Technologies
dyoung@ojctech.com      Urbana, IL * (217) 278-3933 ext 24

["ssrv_inet.c" (text/plain)]

/* $Id: ssrv_inet.c 4702 2007-06-22 07:01:28Z dyoung $ */
/*
 * Copyright (c) 2004, 2005, 2006 David Young.  All rights reserved.
 *
 * This file contains code contributed by David Young.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials provided
 *    with the distribution.
 * 3. David Young's name may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DAVID
 * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */
/*
 * Copyright (c) 2004 Urbana-Champaign Independent Media Center.
 * All rights reserved.
 *
 * This code was written by the Champaign-Urbana Community Wireless
 * Network Project and its contributors.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials provided
 *    with the distribution.
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgement:
 *      This product includes software developed by the Urbana-Champaign
 *      Independent Media Center.
 * 4. Urbana-Champaign Independent Media Center's name may not be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT
 * MEDIA CENTER ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT
 * MEDIA CENTER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <err.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <net/if.h>
#include <assert.h>
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "sockaddr.h"
#include "ssrv.h"
#include "log/log.h"
#include "misc/misc.h"

#ifndef lint
__RCSID("$Id: ssrv_inet.c 4702 2007-06-22 07:01:28Z dyoung $");
#endif

static int
beacon_rx_mcast_joinleave(int s, int optname,
	const struct in_addr *addr, u_int ifindex)
{
	struct ip_mreq mreq4;

	MEMZERO(&mreq4);

	mreq4.imr_interface.s_addr = htonl(ifindex);
	mreq4.imr_multiaddr.s_addr = addr->s_addr;

	return setsockopt(s, IPPROTO_IP, optname, (void *)&mreq4,
		(socklen_t)sizeof(mreq4));
}

int
beacon_rx_pktinfo_enable(int s, int enable)
{
	int pktinfo_on = !!enable;

	if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &pktinfo_on,
	    (socklen_t)sizeof(pktinfo_on)) == -1)
		return -1;

	return setsockopt(s, IPPROTO_IP, IP_RECVIF, &pktinfo_on,
	    (socklen_t)sizeof(pktinfo_on));
}

int
beacon_tx_mcastif_set(int s, uint32_t addr)
{
	u_char ttl = 1;
	int rc;

	LOGLIB_LOG(&log_ssrv_tx,
	    "%s: setsockopt(, IP_MULTICAST_IF, addr<%#08" PRIx32 ">, %zu)",
	    __func__, addr, sizeof(addr));

	if ((rc = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &addr,
	    (socklen_t)sizeof(addr))) == -1)
		goto ifseterr;

	rc = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&ttl,
	                (socklen_t)sizeof(ttl));

ifseterr:
	return rc;
}

int
beacon_tx_mcastloop_enable(int s, int enable)
{
	u_char dontloop = !!enable;

	return setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &dontloop,
			  (socklen_t)sizeof(dontloop));

}

int
beacon_tx_header_enable(int s, int enable)
{
	u_int hdrincl = !!enable;

	return setsockopt(s, IPPROTO_IP, IP_HDRINCL, &hdrincl,
		(socklen_t)sizeof(hdrincl));
}

int
beacon_rx_mcast_leave(int s, const struct in_addr *addr, u_int ifindex)
{
	return beacon_rx_mcast_joinleave(s, IP_DROP_MEMBERSHIP, addr, ifindex);
}

int
beacon_rx_mcast_join(int s, const struct in_addr *addr, u_int ifindex)
{
	return beacon_rx_mcast_joinleave(s, IP_ADD_MEMBERSHIP, addr, ifindex);
}

/* Side-effects: Overwrites sockname0. */
int
beacon_rx_socket(const struct sockaddr_in *listenaddr,
    struct sockaddr_in *sockname0, int protocol)
{
	int rc, s;
	struct sockaddr_in *sockname;
	struct sockaddr_in sockname_tgt;
	char buf[256];

	sockaddr_in_init(&sockname_tgt, &in_any, 0);

	sockname = (sockname0 == NULL) ? &sockname_tgt : sockname0;

	s = socket(PF_INET, SOCK_DGRAM, protocol);

	if (s == -1) {
		loglib_warn("%s: socket", __func__);
		return -1;
	}

	/* Bind listenaddr. */

	/* Enable IPv4 packet info (destination addr and interface). */
	/* destination addr only, for now */
	if (beacon_rx_pktinfo_enable(s, 1) == -1) {
		loglib_warn("%s: beacon_rx_pktinfo_enable", __func__);
		goto post_sock_err;
	}


	rc = bind_helper(s, lintfree_const_cast(struct sockaddr *)listenaddr,
	    lintfree_cast(struct sockaddr *)sockname);

	if (rc == -1) {
		loglib_warn("%s: bind_helper", __func__);
		goto post_sock_err;
	}
	if (inet_ntop(sockname->sin_family, &sockname->sin_addr, &buf[0],
	    (socklen_t)sizeof(buf)) == NULL) {
		loglib_warn("%s: inet_ntop", __func__);
		goto post_sock_err;
	}
	LOGLIB_LOG(&log_warn, "%s: bound %s port %d", __func__, buf,
	    ntohs(sockname->sin_port));

	return s;
post_sock_err:
	(void)close(s);
	return -1;
}

typedef int (*sin4_filter_t)(const struct sockaddr_in *);

static int
nonlinklocal4_filter(const struct sockaddr_in *sin4)
{
	return !IN4_IS_ADDR_LINKLOCAL(&sin4->sin_addr);
}

static int
linklocal4_filter(const struct sockaddr_in *sin4)
{
	return IN4_IS_ADDR_LINKLOCAL(&sin4->sin_addr);
}

int 
getifaddr4_byname_filtered(struct sockaddr_in *dst, const char *ifname,
	sin4_filter_t filter)
{
	struct ifaddrs *ifa0, *ifap;

	if (getifaddrs(&ifa0) == -1) {
		loglib_warn("%s: getifaddrs", __func__);
		return -1;
	}

	for (ifap = ifa0; ifap != NULL; ifap = ifap->ifa_next) {
		struct sockaddr_in *tmp;
		if (strcmp(ifname, ifap->ifa_name) != 0)
			continue;
		if (ifap->ifa_addr->sa_family != AF_INET)
			continue;
		tmp = (struct sockaddr_in *)(void *)ifap->ifa_addr;
		if (!(*filter)(tmp))
			continue;
		(void)memcpy(dst, tmp, sizeof(*dst));
		break;
	}

	freeifaddrs(ifa0);

	if (ifap == NULL) {
		errno = ENXIO;
		return -1;
	}
	return 0;
}

static int
getifaddr4_byindex_filtered(struct sockaddr_in *dst, u_int ifindex,
	sin4_filter_t filter)
{
	char ifname[IFNAMSIZ];

	if (if_indextoname(ifindex, ifname) == NULL) {
		loglib_warn("%s: if_indextoname(%u,)", __func__, ifindex);
		return -1;
	}

	return getifaddr4_byname_filtered(dst, ifname, filter);
}

int
nonlinklocal4byifname(struct sockaddr_in *dst, const char *ifname)
{
	return getifaddr4_byname_filtered(dst, ifname, nonlinklocal4_filter);
}

int
nonlinklocal4byifindex(struct sockaddr_in *dst, u_int ifindex)
{
	return getifaddr4_byindex_filtered(dst, ifindex, nonlinklocal4_filter);
}

int
linklocal4byifname(struct sockaddr_in *dst, const char *ifname)
{
	return getifaddr4_byname_filtered(dst, ifname, linklocal4_filter);
}

int
linklocal4byifindex(struct sockaddr_in *dst, u_int ifindex)
{
	return getifaddr4_byindex_filtered(dst, ifindex, linklocal4_filter);
}

int
beacon_tx_mcast_setup(int s, uint32_t ifindex)
{
	if (beacon_tx_mcastif_set(s, htonl(ifindex)) == -1) {
		loglib_warn("beacon_tx_mcastif_set in %s", __func__);
		return -1;
	}

	return 0;
}

int
beacon_tx_socket(const struct sockaddr_in *srcaddr_in,
    struct sockaddr *srcaddr_out0, int protocol)
{
	int on, s;
	socklen_t slen, copylen;
	char buf[256];
	struct sockaddr_in srcaddr_out, srcaddr_tmp;

	s = socket(PF_INET, SOCK_DGRAM, protocol);

	if (s == -1) {
		loglib_warn("%s: socket", __func__);
		return -1;
	}

	on = 1;
	if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &on,
	               (socklen_t)sizeof(on)) == -1) {
		loglib_warn("%s: setsockopt(,,SO_REUSEPORT,,)", __func__);
		goto post_sock_err;
	}

	/* Bind any address on the interface named by ifindex. */

	if (bind(s, lintfree_const_cast(struct sockaddr *)srcaddr_in,
	    sockaddr_in_getlen(srcaddr_in)) == -1) {
		loglib_warn("%s: bind", __func__);
		goto post_sock_err;
	}

	slen = sizeof(srcaddr_tmp);
	if (getsockname(s, lintfree_cast(struct sockaddr *)&srcaddr_tmp,
	    &slen) == -1) {
		loglib_warn("%s: getsockname", __func__);
		goto post_sock_err;
	}

	sockaddr_in_init(&srcaddr_out, &srcaddr_tmp.sin_addr,
	    srcaddr_tmp.sin_port);

	if (inet_ntop(srcaddr_out.sin_family, &srcaddr_out.sin_addr,
	    &buf[0], (socklen_t)sizeof(buf)) == NULL) {
		loglib_warn("%s: inet_ntop(%d, ...)", __func__,
		    srcaddr_out.sin_family);
		goto post_sock_err;
	}
	LOGLIB_LOG(&log_warn, "%s: bound %s port %d", __func__, buf,
	    ntohs(srcaddr_out.sin_port));

	if (srcaddr_out0 != NULL) {
		copylen = MIN(sockaddr_getlen(srcaddr_out0),
		    sockaddr_in_getlen(&srcaddr_out));
		(void)memcpy(srcaddr_out0, &srcaddr_out, (size_t)copylen);
	}

	return s;
post_sock_err:
	close(s);
	return -1;
}


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic