mirror of https://github.com/sipwise/heartbeat.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
842 lines
21 KiB
842 lines
21 KiB
/*
|
|
* mcast.c: implements hearbeat API for UDP multicast communication
|
|
*
|
|
* Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
|
|
* Copyright (C) 2000 Chris Wright <chris@wirex.com>
|
|
*
|
|
* Thanks to WireX for providing hardware to test on.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include <lha_internal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <net/if.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#ifdef HAVE_SYS_SOCKIO_H
|
|
# include <sys/sockio.h>
|
|
#endif
|
|
|
|
#include <HBcomm.h>
|
|
|
|
#define PIL_PLUGINTYPE HB_COMM_TYPE
|
|
#define PIL_PLUGINTYPE_S HB_COMM_TYPE_S
|
|
#define PIL_PLUGIN mcast
|
|
#define PIL_PLUGIN_S "mcast"
|
|
#define PIL_PLUGINLICENSE LICENSE_LGPL
|
|
#define PIL_PLUGINLICENSEURL URL_LGPL
|
|
#include <pils/plugin.h>
|
|
#include <heartbeat.h>
|
|
|
|
struct mcast_private {
|
|
char * interface; /* Interface name */
|
|
struct in_addr mcast; /* multicast address */
|
|
struct sockaddr_in addr; /* multicast addr */
|
|
u_short port;
|
|
int rsocket; /* Read-socket */
|
|
int wsocket; /* Write-socket */
|
|
u_char ttl; /* TTL value for outbound packets */
|
|
u_char loop; /* boolean, loop back outbound packets */
|
|
};
|
|
|
|
|
|
static int mcast_parse(const char* configline);
|
|
static struct hb_media * mcast_new(const char * intf, const char *mcast
|
|
, u_short port, u_char ttl, u_char loop);
|
|
static int mcast_open(struct hb_media* mp);
|
|
static int mcast_close(struct hb_media* mp);
|
|
static void* mcast_read(struct hb_media* mp, int* lenp);
|
|
static int mcast_write(struct hb_media* mp, void* p, int len);
|
|
static int mcast_descr(char** buffer);
|
|
static int mcast_mtype(char** buffer);
|
|
static int mcast_isping(void);
|
|
|
|
|
|
static struct hb_media_fns mcastOps ={
|
|
NULL, /* Create single object function */
|
|
mcast_parse, /* whole-line parse function */
|
|
mcast_open,
|
|
mcast_close,
|
|
mcast_read,
|
|
mcast_write,
|
|
mcast_mtype,
|
|
mcast_descr,
|
|
mcast_isping,
|
|
};
|
|
|
|
PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
|
|
static const PILPluginImports* PluginImports;
|
|
static PILPlugin* OurPlugin;
|
|
static PILInterface* OurInterface;
|
|
static struct hb_media_imports* OurImports;
|
|
static void* interfprivate;
|
|
|
|
#define LOG PluginImports->log
|
|
#define MALLOC PluginImports->alloc
|
|
#define STRDUP PluginImports->mstrdup
|
|
#define FREE PluginImports->mfree
|
|
|
|
PIL_rc
|
|
PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
|
|
|
|
PIL_rc
|
|
PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
|
|
{
|
|
/* Force the compiler to do a little type checking */
|
|
(void)(PILPluginInitFun)PIL_PLUGIN_INIT;
|
|
|
|
PluginImports = imports;
|
|
OurPlugin = us;
|
|
|
|
/* Register ourself as a plugin */
|
|
imports->register_plugin(us, &OurPIExports);
|
|
|
|
/* Register our interface implementation */
|
|
return imports->register_interface(us, PIL_PLUGINTYPE_S
|
|
, PIL_PLUGIN_S
|
|
, &mcastOps
|
|
, NULL /*close */
|
|
, &OurInterface
|
|
, (void*)&OurImports
|
|
, interfprivate);
|
|
}
|
|
|
|
|
|
/* helper functions */
|
|
static int mcast_make_receive_sock(struct hb_media* hbm);
|
|
static int mcast_make_send_sock(struct hb_media * hbm);
|
|
static struct mcast_private *
|
|
new_mcast_private(const char *ifn, const char *mcast, u_short port,
|
|
u_char ttl, u_char loop);
|
|
static int set_mcast_if(int sockfd, char *ifname);
|
|
static int set_mcast_loop(int sockfd, u_char loop);
|
|
static int set_mcast_ttl(int sockfd, u_char ttl);
|
|
static int join_mcast_group(int sockfd, struct in_addr *addr, char *ifname);
|
|
static int if_getaddr(const char *ifname, struct in_addr *addr);
|
|
static int is_valid_dev(const char *dev);
|
|
static int is_valid_mcast_addr(const char *addr);
|
|
static int get_port(const char *port, u_short *p);
|
|
static int get_ttl(const char *ttl, u_char *t);
|
|
static int get_loop(const char *loop, u_char *l);
|
|
|
|
|
|
#define ISMCASTOBJECT(mp) ((mp) && ((mp)->vf == (void*)&mcastOps))
|
|
#define MCASTASSERT(mp) g_assert(ISMCASTOBJECT(mp))
|
|
|
|
static int
|
|
mcast_mtype(char** buffer)
|
|
{
|
|
*buffer = STRDUP(PIL_PLUGIN_S);
|
|
if (!*buffer) {
|
|
return 0;
|
|
}
|
|
|
|
return STRLEN_CONST(PIL_PLUGIN_S);
|
|
}
|
|
|
|
static int
|
|
mcast_descr(char **buffer)
|
|
{
|
|
const char cret[] = "UDP/IP multicast";
|
|
*buffer = STRDUP(cret);
|
|
if (!*buffer) {
|
|
return 0;
|
|
}
|
|
|
|
return STRLEN_CONST(cret);
|
|
}
|
|
|
|
static int
|
|
mcast_isping(void)
|
|
{
|
|
/* nope, this is not a ping device */
|
|
return 0;
|
|
}
|
|
|
|
/* mcast_parse will parse the line in the config file that is
|
|
* associated with the media's type (hb_dev_mtype). It should
|
|
* receive the rest of the line after the mtype. And it needs
|
|
* to call hb_dev_new, add the media to the list of available media.
|
|
*
|
|
* So in this case, the config file line should look like
|
|
* mcast [device] [mcast group] [port] [mcast ttl] [mcast loop]
|
|
* for example:
|
|
* mcast eth0 225.0.0.1 694 1 0
|
|
*/
|
|
|
|
static int
|
|
mcast_parse(const char *line)
|
|
{
|
|
const char * bp = line;
|
|
char dev[MAXLINE];
|
|
char mcast[MAXLINE];
|
|
char token[MAXLINE];
|
|
u_short port = 0; /* Bogus */
|
|
u_char ttl = 10; /* Bogus */
|
|
u_char loop = 10; /* Bogus */
|
|
int toklen;
|
|
struct hb_media * mp;
|
|
|
|
/* Skip over white space, then grab the device */
|
|
bp += strspn(bp, WHITESPACE);
|
|
toklen = strcspn(bp, WHITESPACE);
|
|
strncpy(dev, bp, toklen);
|
|
bp += toklen;
|
|
dev[toklen] = EOS;
|
|
|
|
if (*dev != EOS) {
|
|
if (!is_valid_dev(dev)) {
|
|
PILCallLog(LOG, PIL_CRIT, "mcast device [%s] is invalid or not set up properly", dev);
|
|
return HA_FAIL;
|
|
}
|
|
/* Skip over white space, then grab the multicast group */
|
|
bp += strspn(bp, WHITESPACE);
|
|
toklen = strcspn(bp, WHITESPACE);
|
|
strncpy(mcast, bp, toklen);
|
|
bp += toklen;
|
|
mcast[toklen] = EOS;
|
|
|
|
if (*mcast == EOS) {
|
|
PILCallLog(LOG, PIL_CRIT, "mcast [%s] missing mcast address",
|
|
dev);
|
|
return(HA_FAIL);
|
|
}
|
|
if (!is_valid_mcast_addr(mcast)) {
|
|
PILCallLog(LOG, PIL_CRIT, "mcast [%s] bad addr [%s]", dev, mcast);
|
|
return(HA_FAIL);
|
|
}
|
|
|
|
/* Skip over white space, then grab the port */
|
|
bp += strspn(bp, WHITESPACE);
|
|
toklen = strcspn(bp, WHITESPACE);
|
|
strncpy(token, bp, toklen);
|
|
bp += toklen;
|
|
token[toklen] = EOS;
|
|
|
|
if (*token == EOS) {
|
|
PILCallLog(LOG, PIL_CRIT, "mcast [%s] missing port"
|
|
, dev);
|
|
return(HA_FAIL);
|
|
}
|
|
if (get_port(token, &port) < 0 || port <= 0) {
|
|
PILCallLog(LOG, PIL_CRIT, " mcast [%s] bad port [%d]", dev, port);
|
|
return HA_FAIL;
|
|
}
|
|
|
|
/* Skip over white space, then grab the ttl */
|
|
bp += strspn(bp, WHITESPACE);
|
|
toklen = strcspn(bp, WHITESPACE);
|
|
strncpy(token, bp, toklen);
|
|
bp += toklen;
|
|
token[toklen] = EOS;
|
|
|
|
if (*token == EOS) {
|
|
PILCallLog(LOG, PIL_CRIT, "mcast [%s] missing ttl", dev);
|
|
return(HA_FAIL);
|
|
}
|
|
if (get_ttl(token, &ttl) < 0 || ttl > 4) {
|
|
PILCallLog(LOG, PIL_CRIT, " mcast [%s] bad ttl [%d]", dev, ttl);
|
|
return HA_FAIL;
|
|
}
|
|
|
|
/* Skip over white space, then grab the loop */
|
|
bp += strspn(bp, WHITESPACE);
|
|
toklen = strcspn(bp, WHITESPACE);
|
|
strncpy(token, bp, toklen);
|
|
bp += toklen;
|
|
token[toklen] = EOS;
|
|
|
|
if (*token == EOS) {
|
|
PILCallLog(LOG, PIL_CRIT, "mcast [%s] missing loop", dev);
|
|
return(HA_FAIL);
|
|
}
|
|
if (get_loop(token, &loop) < 0 || loop > 1) {
|
|
PILCallLog(LOG, PIL_CRIT, " mcast [%s] bad loop [%d]", dev, loop);
|
|
return HA_FAIL;
|
|
}
|
|
|
|
if ((mp = mcast_new(dev, mcast, port, ttl, loop)) == NULL) {
|
|
return(HA_FAIL);
|
|
}
|
|
OurImports->RegisterNewMedium(mp);
|
|
}
|
|
|
|
return(HA_OK);
|
|
}
|
|
|
|
/*
|
|
* Create new UDP/IP multicast heartbeat object
|
|
* pass in name of interface, multicast address, port, multicast
|
|
* ttl, and multicast loopback value as parameters.
|
|
* This should get called from hb_dev_parse().
|
|
*/
|
|
static struct hb_media *
|
|
mcast_new(const char * intf, const char *mcast, u_short port,
|
|
u_char ttl, u_char loop)
|
|
{
|
|
struct mcast_private* mcp;
|
|
struct hb_media * ret;
|
|
|
|
/* create new mcast_private struct...hmmm...who frees it? */
|
|
mcp = new_mcast_private(intf, mcast, port, ttl, loop);
|
|
if (mcp == NULL) {
|
|
PILCallLog(LOG, PIL_WARN, "Error creating mcast_private(%s, %s, %d, %d, %d)",
|
|
intf, mcast, port, ttl, loop);
|
|
return(NULL);
|
|
}
|
|
ret = (struct hb_media*) MALLOC(sizeof(struct hb_media));
|
|
if (ret != NULL) {
|
|
char * name;
|
|
memset(ret, 0, sizeof(*ret));
|
|
ret->pd = (void*)mcp;
|
|
name = STRDUP(intf);
|
|
if (name != NULL) {
|
|
ret->name = name;
|
|
}
|
|
else {
|
|
FREE(ret);
|
|
ret = NULL;
|
|
}
|
|
|
|
}
|
|
if(ret == NULL) {
|
|
FREE(mcp->interface);
|
|
FREE(mcp);
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
/*
|
|
* Open UDP/IP multicast heartbeat interface
|
|
*/
|
|
static int
|
|
mcast_open(struct hb_media* hbm)
|
|
{
|
|
struct mcast_private * mcp;
|
|
|
|
MCASTASSERT(hbm);
|
|
mcp = (struct mcast_private *) hbm->pd;
|
|
|
|
if ((mcp->wsocket = mcast_make_send_sock(hbm)) < 0) {
|
|
return(HA_FAIL);
|
|
}
|
|
if (Debug) {
|
|
PILCallLog(LOG, PIL_DEBUG
|
|
, "%s: write socket: %d"
|
|
, __FUNCTION__, mcp->wsocket);
|
|
}
|
|
if ((mcp->rsocket = mcast_make_receive_sock(hbm)) < 0) {
|
|
mcast_close(hbm);
|
|
return(HA_FAIL);
|
|
}
|
|
if (Debug) {
|
|
PILCallLog(LOG, PIL_DEBUG
|
|
, "%s: read socket: %d"
|
|
, __FUNCTION__, mcp->rsocket);
|
|
}
|
|
|
|
PILCallLog(LOG, PIL_INFO, "UDP multicast heartbeat started for group %s "
|
|
"port %d interface %s (ttl=%d loop=%d)" , inet_ntoa(mcp->mcast),
|
|
mcp->port, mcp->interface, mcp->ttl, mcp->loop);
|
|
|
|
return(HA_OK);
|
|
}
|
|
|
|
/*
|
|
* Close UDP/IP multicast heartbeat interface
|
|
*/
|
|
static int
|
|
mcast_close(struct hb_media* hbm)
|
|
{
|
|
struct mcast_private * mcp;
|
|
int rc = HA_OK;
|
|
|
|
MCASTASSERT(hbm);
|
|
mcp = (struct mcast_private *) hbm->pd;
|
|
|
|
if (mcp->rsocket >= 0) {
|
|
if (Debug) {
|
|
PILCallLog(LOG, PIL_DEBUG
|
|
, "%s: Closing socket %d"
|
|
, __FUNCTION__, mcp->rsocket);
|
|
}
|
|
if (close(mcp->rsocket) < 0) {
|
|
rc = HA_FAIL;
|
|
}
|
|
mcp->rsocket = -1;
|
|
|
|
}
|
|
if (mcp->wsocket >= 0) {
|
|
if (Debug) {
|
|
PILCallLog(LOG, PIL_DEBUG
|
|
, "%s: Closing socket %d"
|
|
, __FUNCTION__, mcp->wsocket);
|
|
}
|
|
if (close(mcp->wsocket) < 0) {
|
|
rc = HA_FAIL;
|
|
}
|
|
mcp->rsocket = -1;
|
|
}
|
|
return(rc);
|
|
}
|
|
|
|
/*
|
|
* Receive a heartbeat multicast packet from UDP interface
|
|
*/
|
|
|
|
char mcast_pkt[MAXMSG];
|
|
static void *
|
|
mcast_read(struct hb_media* hbm, int *lenp)
|
|
{
|
|
struct mcast_private * mcp;
|
|
socklen_t addr_len = sizeof(struct sockaddr);
|
|
struct sockaddr_in their_addr; /* connector's addr information */
|
|
int numbytes;
|
|
|
|
MCASTASSERT(hbm);
|
|
mcp = (struct mcast_private *) hbm->pd;
|
|
|
|
if ((numbytes=recvfrom(mcp->rsocket, mcast_pkt, MAXMSG-1, 0
|
|
, (struct sockaddr *)&their_addr, &addr_len)) < 0) {
|
|
if (errno != EINTR) {
|
|
PILCallLog(LOG, PIL_CRIT, "Error receiving from socket: %s"
|
|
, strerror(errno));
|
|
}
|
|
return NULL;
|
|
}
|
|
/* Avoid possible buffer overruns */
|
|
mcast_pkt[numbytes] = EOS;
|
|
|
|
if (Debug >= PKTTRACE) {
|
|
PILCallLog(LOG, PIL_DEBUG, "got %d byte packet from %s"
|
|
, numbytes, inet_ntoa(their_addr.sin_addr));
|
|
}
|
|
if (Debug >= PKTCONTTRACE && numbytes > 0) {
|
|
PILCallLog(LOG, PIL_DEBUG, "%s", mcast_pkt);
|
|
}
|
|
|
|
*lenp = numbytes + 1 ;
|
|
|
|
return mcast_pkt;;
|
|
}
|
|
|
|
/*
|
|
* Send a heartbeat packet over multicast UDP/IP interface
|
|
*/
|
|
|
|
static int
|
|
mcast_write(struct hb_media* hbm, void *pkt, int len)
|
|
{
|
|
struct mcast_private * mcp;
|
|
int rc;
|
|
|
|
MCASTASSERT(hbm);
|
|
mcp = (struct mcast_private *) hbm->pd;
|
|
|
|
if ((rc=sendto(mcp->wsocket, pkt, len, 0
|
|
, (struct sockaddr *)&mcp->addr
|
|
, sizeof(struct sockaddr))) != len) {
|
|
if (!hbm->suppresserrs) {
|
|
PILCallLog(LOG, PIL_CRIT
|
|
, "%s: Unable to send mcast packet [%d]: %s"
|
|
, __FUNCTION__, rc, strerror(errno));
|
|
}
|
|
return(HA_FAIL);
|
|
}
|
|
|
|
if (Debug >= PKTTRACE) {
|
|
PILCallLog(LOG, PIL_DEBUG, "sent %d bytes to %s"
|
|
, rc, inet_ntoa(mcp->addr.sin_addr));
|
|
}
|
|
if (Debug >= PKTCONTTRACE) {
|
|
PILCallLog(LOG, PIL_DEBUG, "%s", (const char *)pkt);
|
|
}
|
|
return(HA_OK);
|
|
}
|
|
|
|
/*
|
|
* Set up socket for sending multicast UDP heartbeats
|
|
*/
|
|
|
|
static int
|
|
mcast_make_send_sock(struct hb_media * hbm)
|
|
{
|
|
int sockfd;
|
|
struct mcast_private * mcp;
|
|
MCASTASSERT(hbm);
|
|
mcp = (struct mcast_private *) hbm->pd;
|
|
|
|
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
|
PILCallLog(LOG, PIL_WARN, "Error getting socket: %s", strerror(errno));
|
|
return(sockfd);
|
|
}
|
|
|
|
if (set_mcast_if(sockfd, mcp->interface) < 0) {
|
|
PILCallLog(LOG, PIL_WARN, "Error setting outbound mcast interface: %s", strerror(errno));
|
|
}
|
|
|
|
if (set_mcast_loop(sockfd, mcp->loop) < 0) {
|
|
PILCallLog(LOG, PIL_WARN, "Error setting outbound mcast loopback value: %s", strerror(errno));
|
|
}
|
|
|
|
if (set_mcast_ttl(sockfd, mcp->ttl) < 0) {
|
|
PILCallLog(LOG, PIL_WARN, "Error setting outbound mcast TTL: %s", strerror(errno));
|
|
}
|
|
|
|
if (fcntl(sockfd,F_SETFD, FD_CLOEXEC)) {
|
|
PILCallLog(LOG, PIL_WARN, "Error setting the close-on-exec flag: %s", strerror(errno));
|
|
}
|
|
return(sockfd);
|
|
}
|
|
|
|
/*
|
|
* Set up socket for listening to heartbeats (UDP multicasts)
|
|
*/
|
|
|
|
#define MAXBINDTRIES 50
|
|
static int
|
|
mcast_make_receive_sock(struct hb_media * hbm)
|
|
{
|
|
struct mcast_private * mcp;
|
|
int sockfd;
|
|
int bindtries;
|
|
int boundyet=0;
|
|
int one=1;
|
|
int rc;
|
|
int binderr=0;
|
|
|
|
MCASTASSERT(hbm);
|
|
mcp = (struct mcast_private *) hbm->pd;
|
|
|
|
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
|
PILCallLog(LOG, PIL_CRIT, "Error getting socket");
|
|
return -1;
|
|
}
|
|
/* set REUSEADDR option on socket so you can bind a multicast */
|
|
/* reader to multiple interfaces */
|
|
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)) < 0){
|
|
PILCallLog(LOG, PIL_CRIT, "Error setsockopt(SO_REUSEADDR)");
|
|
}
|
|
|
|
/* ripped off from udp.c, if we all use SO_REUSEADDR */
|
|
/* this shouldn't be necessary */
|
|
/* Try binding a few times before giving up */
|
|
/* Sometimes a process with it open is exiting right now */
|
|
|
|
for(bindtries=0; !boundyet && bindtries < MAXBINDTRIES; ++bindtries) {
|
|
rc=bind(sockfd, (struct sockaddr *)&mcp->addr, sizeof(mcp->addr));
|
|
binderr=errno;
|
|
if (rc==0) {
|
|
boundyet=1;
|
|
} else if (rc == -1) {
|
|
if (binderr == EADDRINUSE) {
|
|
PILCallLog(LOG, PIL_CRIT, "Can't bind (EADDRINUSE), "
|
|
"retrying");
|
|
sleep(1);
|
|
} else {
|
|
/* don't keep trying if the error isn't caused by */
|
|
/* the address being in use already...real error */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!boundyet) {
|
|
if (binderr == EADDRINUSE) {
|
|
/* This happens with multiple udp or ppp interfaces */
|
|
PILCallLog(LOG, PIL_INFO
|
|
, "Someone already listening on port %d [%s]"
|
|
, mcp->port
|
|
, mcp->interface);
|
|
PILCallLog(LOG, PIL_INFO, "multicast read process exiting");
|
|
close(sockfd);
|
|
cleanexit(0);
|
|
} else {
|
|
PILCallLog(LOG, PIL_WARN, "Unable to bind socket. Giving up: %s", strerror(errno));
|
|
close(sockfd);
|
|
return(-1);
|
|
}
|
|
}
|
|
/* join the multicast group...this is what really makes this a */
|
|
/* multicast reader */
|
|
if (join_mcast_group(sockfd, &mcp->mcast, mcp->interface) == -1) {
|
|
PILCallLog(LOG, PIL_CRIT, "Can't join multicast group %s on interface %s",
|
|
inet_ntoa(mcp->mcast), mcp->interface);
|
|
PILCallLog(LOG, PIL_INFO, "multicast read process exiting");
|
|
close(sockfd);
|
|
cleanexit(0);
|
|
}
|
|
if (ANYDEBUG)
|
|
PILCallLog(LOG, PIL_DEBUG, "Successfully joined multicast group %s on"
|
|
"interface %s", inet_ntoa(mcp->mcast), mcp->interface);
|
|
|
|
if (fcntl(sockfd,F_SETFD, FD_CLOEXEC)) {
|
|
PILCallLog(LOG, PIL_WARN, "Error setting the close-on-exec flag: %s", strerror(errno));
|
|
}
|
|
return(sockfd);
|
|
}
|
|
|
|
static struct mcast_private *
|
|
new_mcast_private(const char *ifn, const char *mcast, u_short port,
|
|
u_char ttl, u_char loop)
|
|
{
|
|
struct mcast_private *mcp;
|
|
|
|
mcp = MALLOCT(struct mcast_private);
|
|
if (mcp == NULL) {
|
|
return NULL;
|
|
}
|
|
memset(mcp, 0, sizeof(*mcp));
|
|
|
|
mcp->interface = (char *)STRDUP(ifn);
|
|
if(mcp->interface == NULL) {
|
|
FREE(mcp);
|
|
return NULL;
|
|
}
|
|
|
|
/* Set up multicast address */
|
|
|
|
if (inet_pton(AF_INET, mcast, (void *)&mcp->mcast) <= 0) {
|
|
FREE(mcp->interface);
|
|
FREE(mcp);
|
|
return NULL;
|
|
}
|
|
|
|
mcp->addr.sin_family = AF_INET; /* host byte order */
|
|
mcp->addr.sin_port = htons(port); /* short, network byte order */
|
|
mcp->addr.sin_addr = mcp->mcast;
|
|
mcp->port = port;
|
|
mcp->wsocket = -1;
|
|
mcp->rsocket = -1;
|
|
mcp->ttl=ttl;
|
|
mcp->loop=loop;
|
|
return(mcp);
|
|
}
|
|
|
|
/* set_mcast_loop takes a boolean flag, loop, which is useful on
|
|
* a writing socket. with loop enabled (the default on a multicast socket)
|
|
* the outbound packet will get looped back and received by the sending
|
|
* interface, if it is listening for the multicast group and port that the
|
|
* packet was sent to. Returns 0 on success -1 on failure.
|
|
*/
|
|
static int set_mcast_loop(int sockfd, u_char loop)
|
|
{
|
|
return setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
|
|
}
|
|
|
|
/* set_mcast_ttl will set the time-to-live value for the writing socket.
|
|
* the socket default is TTL=1. The TTL is used to limit the scope of the
|
|
* packet and can range from 0-255.
|
|
* TTL Scope
|
|
* ----------------------------------------------------------------------
|
|
* 0 Restricted to the same host. Won't be output by any interface.
|
|
* 1 Restricted to the same subnet. Won't be forwarded by a router.
|
|
* <32 Restricted to the same site, organization or department.
|
|
* <64 Restricted to the same region.
|
|
* <128 Restricted to the same continent.
|
|
* <255 Unrestricted in scope. Global.
|
|
*
|
|
* Returns 0 on success -1 on failure.
|
|
*/
|
|
static int
|
|
set_mcast_ttl(int sockfd, u_char ttl)
|
|
{
|
|
return setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
|
|
}
|
|
|
|
/*
|
|
* set_mcast_if takes the name of an interface (i.e. eth0) and then
|
|
* sets that as the interface to use for outbound multicast traffic.
|
|
* If ifname is NULL, then it the OS will assign the interface.
|
|
* Returns 0 on success -1 on faliure.
|
|
*/
|
|
static int
|
|
set_mcast_if(int sockfd, char *ifname)
|
|
{
|
|
int rc;
|
|
struct in_addr addr;
|
|
|
|
/* Zero out the struct... we only care about the address... */
|
|
memset(&addr, 0, sizeof(addr));
|
|
|
|
rc = if_getaddr(ifname, &addr);
|
|
if (rc == -1)
|
|
return -1;
|
|
return setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF
|
|
, (void*)&addr, sizeof(addr));
|
|
}
|
|
|
|
/* join_mcast_group is used to join a multicast group. the group is
|
|
* specified by a class D multicast address 224.0.0.0/8 in the in_addr
|
|
* structure passed in as a parameter. The interface name can be used
|
|
* to "bind" the multicast group to a specific interface (or any
|
|
* interface if ifname is NULL);
|
|
* returns 0 on success, -1 on failure.
|
|
*/
|
|
static int
|
|
join_mcast_group(int sockfd, struct in_addr *addr, char *ifname)
|
|
{
|
|
struct ip_mreq mreq_add;
|
|
|
|
memset(&mreq_add, 0, sizeof(mreq_add));
|
|
memcpy(&mreq_add.imr_multiaddr, addr, sizeof(struct in_addr));
|
|
|
|
if (ifname) {
|
|
if_getaddr(ifname, &mreq_add.imr_interface);
|
|
}
|
|
return setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&mreq_add, sizeof(mreq_add));
|
|
}
|
|
|
|
/* if_getaddr gets the ip address from an interface
|
|
* specified by name and places it in addr.
|
|
* returns 0 on success and -1 on failure.
|
|
*/
|
|
static int
|
|
if_getaddr(const char *ifname, struct in_addr *addr)
|
|
{
|
|
struct ifreq if_info;
|
|
int j;
|
|
int maxtry = 120;
|
|
gboolean gotaddr = FALSE;
|
|
int err = 0;
|
|
|
|
if (!addr) {
|
|
return -1;
|
|
}
|
|
|
|
addr->s_addr = INADDR_ANY;
|
|
|
|
memset(&if_info, 0, sizeof(if_info));
|
|
if (ifname) {
|
|
strncpy(if_info.ifr_name, ifname, IFNAMSIZ-1);
|
|
}else{ /* ifname is NULL, so use any address */
|
|
return 0;
|
|
}
|
|
|
|
if (Debug > 0) {
|
|
PILCallLog(LOG, PIL_DEBUG, "looking up address for %s"
|
|
, if_info.ifr_name);
|
|
}
|
|
for (j=0; j < maxtry && !gotaddr; ++j) {
|
|
int fd;
|
|
if ((fd=socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
|
PILCallLog(LOG, PIL_CRIT, "Error getting socket");
|
|
return -1;
|
|
}
|
|
if (ioctl(fd, SIOCGIFADDR, &if_info) >= 0) {
|
|
gotaddr = TRUE;
|
|
}else{
|
|
err = errno;
|
|
switch(err) {
|
|
case EADDRNOTAVAIL:
|
|
sleep(1);
|
|
break;
|
|
default:
|
|
close(fd);
|
|
goto getout;
|
|
}
|
|
}
|
|
close(fd);
|
|
}
|
|
getout:
|
|
if (!gotaddr) {
|
|
PILCallLog(LOG, PIL_CRIT
|
|
, "Unable to retrieve local interface address"
|
|
" for interface [%s] using ioctl(SIOCGIFADDR): %s"
|
|
, ifname, strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* This #define w/void cast is to quiet alignment errors on some
|
|
* platforms (notably Solaris)
|
|
*/
|
|
#define SOCKADDR_IN(a) ((struct sockaddr_in *)((void*)(a)))
|
|
|
|
memcpy(addr, &(SOCKADDR_IN(&if_info.ifr_addr)->sin_addr)
|
|
, sizeof(struct in_addr));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* returns true or false */
|
|
static int
|
|
is_valid_dev(const char *dev)
|
|
{
|
|
int rc=0;
|
|
if (dev) {
|
|
struct in_addr addr;
|
|
if (if_getaddr(dev, &addr) != -1)
|
|
rc = 1;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* returns true or false */
|
|
#define MCAST_NET 0xf0000000
|
|
#define MCAST_BASE 0xe0000000
|
|
static int
|
|
is_valid_mcast_addr(const char *addr)
|
|
{
|
|
unsigned long mc_addr;
|
|
|
|
/* make sure address is in host byte order */
|
|
mc_addr = ntohl(inet_addr(addr));
|
|
|
|
if ((mc_addr & MCAST_NET) == MCAST_BASE)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* return port number on success, 0 on failure */
|
|
static int
|
|
get_port(const char *port, u_short *p)
|
|
{
|
|
/* not complete yet */
|
|
*p=(u_short)atoi(port);
|
|
return 0;
|
|
}
|
|
|
|
/* returns ttl on succes, -1 on failure */
|
|
static int
|
|
get_ttl(const char *ttl, u_char *t)
|
|
{
|
|
/* not complete yet */
|
|
*t=(u_char)atoi(ttl);
|
|
return 0;
|
|
}
|
|
|
|
/* returns loop on success, -1 on failure */
|
|
static int
|
|
get_loop(const char *loop, u_char *l)
|
|
{
|
|
/* not complete yet */
|
|
*l=(u_char)atoi(loop);
|
|
return 0;
|
|
}
|
|
|