tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: IP Multicast



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@localhost      Urbana, IL * (217) 278-3933 ext 24
/* $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;
}


Home | Main Index | Thread Index | Old Index