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