/*
* Copyright (c) 1998 by the University of Southern California.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and
* its documentation in source and binary forms for lawful
* purposes and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both
* the copyright notice and this permission notice appear in supporting
* documentation, and that any documentation, advertising materials,
* and other materials related to such distribution and use acknowledge
* that the software was developed by the University of Southern
* California and/or Information Sciences Institute.
* The name of the University of Southern California may not
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
* ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
* PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
* NON-INFRINGEMENT.
*
* IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
* TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
* THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Other copyrights might apply to parts of this software and are so
* noted when applicable.
*/
/*
* Questions concerning this software should be directed to
* Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
*
* $Id: pim.c,v 1.1.1.1 2017/06/12 07:58:55 misho Exp $
*/
#include "defs.h"
/*
* Exported variables.
*/
char *pim_recv_buf; /* input packet buffer */
char *pim_send_buf; /* output packet buffer */
u_int32 allpimrouters_group; /* ALL_PIM_ROUTERS group (net order) */
int pim_socket; /* socket for PIM control msgs */
/*
* Local function definitions.
*/
static void pim_read __P((int f, fd_set *rfd));
static void accept_pim __P((int recvlen));
void
init_pim()
{
struct ip *ip;
if ((pim_socket = socket(AF_INET, SOCK_RAW, IPPROTO_PIM)) < 0)
log(LOG_ERR, errno, "PIM socket");
k_hdr_include(pim_socket, TRUE); /* include IP header when sending */
k_set_rcvbuf(pim_socket, SO_RECV_BUF_SIZE_MAX,
SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */
k_set_ttl(pim_socket, MINTTL); /* restrict multicasts to one hop */
k_set_loop(pim_socket, FALSE); /* disable multicast loopback */
allpimrouters_group = htonl(INADDR_ALL_PIM_ROUTERS);
pim_recv_buf = malloc(RECV_BUF_SIZE);
pim_send_buf = malloc(RECV_BUF_SIZE);
/* One time setup in the buffers */
ip = (struct ip *)pim_send_buf;
ip->ip_v = IPVERSION;
ip->ip_hl = (sizeof(struct ip) >> 2);
ip->ip_tos = 0; /* TODO: setup?? */
ip->ip_id = 0; /* let kernel fill in */
ip->ip_off = 0;
ip->ip_p = IPPROTO_PIM;
ip->ip_sum = 0; /* let kernel fill in */
if (register_input_handler(pim_socket, pim_read) < 0)
log(LOG_ERR, 0, "cannot register pim_read() as an input handler");
VIFM_CLRALL(nbr_vifs);
}
/* Read a PIM message */
static void
pim_read(f, rfd)
int f;
fd_set *rfd;
{
register int pim_recvlen;
int dummy = 0;
#ifdef SYSV
sigset_t block, oblock;
#else
register int omask;
#endif
pim_recvlen = recvfrom(pim_socket, pim_recv_buf, RECV_BUF_SIZE,
0, NULL, &dummy);
if (pim_recvlen < 0) {
if (errno != EINTR)
log(LOG_ERR, errno, "PIM recvfrom");
return;
}
#ifdef SYSV
(void)sigemptyset(&block);
(void)sigaddset(&block, SIGALRM);
if (sigprocmask(SIG_BLOCK, &block, &oblock) < 0)
log(LOG_ERR, errno, "sigprocmask");
#else
/* Use of omask taken from main() */
omask = sigblock(sigmask(SIGALRM));
#endif /* SYSV */
accept_pim(pim_recvlen);
#ifdef SYSV
(void)sigprocmask(SIG_SETMASK, &oblock, (sigset_t *)NULL);
#else
(void)sigsetmask(omask);
#endif /* SYSV */
}
static void
accept_pim(recvlen)
int recvlen;
{
u_int32 src, dst;
register struct ip *ip;
register pim_header_t *pim;
int iphdrlen, pimlen;
if (recvlen < sizeof(struct ip)) {
log(LOG_WARNING, 0, "packet too short (%u bytes) for IP header",
recvlen);
return;
}
ip = (struct ip *)pim_recv_buf;
src = ip->ip_src.s_addr;
dst = ip->ip_dst.s_addr;
iphdrlen = ip->ip_hl << 2;
pim = (pim_header_t *)(pim_recv_buf + iphdrlen);
pimlen = recvlen - iphdrlen;
if (pimlen < sizeof(pim)) {
log(LOG_WARNING, 0,
"IP data field too short (%u bytes) for PIM header, from %s to %s",
pimlen, inet_fmt(src, s1), inet_fmt(dst, s2));
return;
}
#ifdef NOSUCHDEF /* TODO: delete. Too noisy */
IF_DEBUG(DEBUG_PIM_DETAIL) {
IF_DEBUG(DEBUG_PIM) {
log(LOG_DEBUG, 0, "Receiving %s from %-15s to %s ",
packet_kind(IPPROTO_PIM, pim->pim_type, 0),
inet_fmt(src, s1), inet_fmt(dst, s2));
log(LOG_DEBUG, 0, "PIM type is %u", pim->pim_type);
}
}
#endif /* NOSUCHDEF */
/* TODO: Check PIM version */
/* TODO: check the dest. is ALL_PIM_ROUTERS (if multicast address) */
/* TODO: Checksum verification is done in each of the processing functions.
* No need for chechsum, if already done in the kernel?
*/
switch (pim->pim_type) {
case PIM_HELLO:
receive_pim_hello(src, dst, (char *)(pim), pimlen);
break;
case PIM_REGISTER:
log(LOG_INFO, 0, "ignore %s from %s to %s",
packet_kind(IPPROTO_PIM, pim->pim_type, 0), inet_fmt(src, s1),
inet_fmt(dst, s2));
break;
case PIM_REGISTER_STOP:
log(LOG_INFO, 0, "ignore %s from %s to %s",
packet_kind(IPPROTO_PIM, pim->pim_type, 0), inet_fmt(src, s1),
inet_fmt(dst, s2));
break;
case PIM_JOIN_PRUNE:
receive_pim_join_prune(src, dst, (char *)(pim), pimlen);
break;
case PIM_BOOTSTRAP:
log(LOG_INFO, 0, "ignore %s from %s to %s",
packet_kind(IPPROTO_PIM, pim->pim_type, 0), inet_fmt(src, s1),
inet_fmt(dst, s2));
break;
case PIM_ASSERT:
receive_pim_assert(src, dst, (char *)(pim), pimlen);
break;
case PIM_GRAFT:
case PIM_GRAFT_ACK:
receive_pim_graft(src, dst, (char *)(pim), pimlen, pim->pim_type);
break;
case PIM_CAND_RP_ADV:
log(LOG_INFO, 0, "ignore %s from %s to %s",
packet_kind(IPPROTO_PIM, pim->pim_type, 0), inet_fmt(src, s1),
inet_fmt(dst, s2));
break;
default:
log(LOG_INFO, 0,
"ignore unknown PIM message code %u from %s to %s",
pim->pim_type, inet_fmt(src, s1), inet_fmt(dst, s2));
break;
}
}
/*
* Send a multicast PIM packet from src to dst, PIM message type = "type"
* and data length (after the PIM header) = "datalen"
*/
void
send_pim(buf, src, dst, type, datalen)
char *buf;
u_int32 src, dst;
int type, datalen;
{
struct sockaddr_in sdst;
struct ip *ip;
pim_header_t *pim;
int sendlen;
#ifdef RAW_OUTPUT_IS_RAW
extern int curttl;
#endif /* RAW_OUTPUT_IS_RAW */
int setloop = 0;
/* Prepare the IP header */
ip = (struct ip *)buf;
ip->ip_len = sizeof(struct ip) + sizeof(pim_header_t) + datalen;
ip->ip_src.s_addr = src;
ip->ip_dst.s_addr = dst;
ip->ip_ttl = MAXTTL; /* applies to unicast only */
sendlen = ip->ip_len;
#ifdef RAW_OUTPUT_IS_RAW
ip->ip_len = htons(ip->ip_len);
#endif /* RAW_OUTPUT_IS_RAW */
/* Prepare the PIM packet */
pim = (pim_header_t *)(buf + sizeof(struct ip));
pim->pim_type = type;
pim->pim_vers = PIM_PROTOCOL_VERSION;
pim->reserved = 0;
pim->pim_cksum = 0;
/* TODO: XXX: if start using this code for PIM_REGISTERS, exclude the
* encapsulated packet from the checsum.
*/
pim->pim_cksum = inet_cksum((u_int16 *)pim,
sizeof(pim_header_t) + datalen);
if (IN_MULTICAST(ntohl(dst))) {
k_set_if(pim_socket, src);
if ((dst == allhosts_group) || (dst == allrouters_group) ||
(dst == allpimrouters_group)) {
setloop = 1;
k_set_loop(pim_socket, TRUE);
}
#ifdef RAW_OUTPUT_IS_RAW
ip->ip_ttl = curttl;
} else {
ip->ip_ttl = MAXTTL;
#endif /* RAW_OUTPUT_IS_RAW */
}
bzero((void *)&sdst, sizeof(sdst));
sdst.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
sdst.sin_len = sizeof(sdst);
#endif
sdst.sin_addr.s_addr = dst;
if (sendto(pim_socket, buf, sendlen, 0,
(struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
if (errno == ENETDOWN)
check_vif_state();
else
log(LOG_WARNING, errno, "sendto from %s to %s",
inet_fmt(src, s1), inet_fmt(dst, s2));
if (setloop)
k_set_loop(pim_socket, FALSE);
return;
}
if (setloop)
k_set_loop(pim_socket, FALSE);
IF_DEBUG(DEBUG_PIM_DETAIL) {
IF_DEBUG(DEBUG_PIM) {
log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
packet_kind(IPPROTO_PIM, type, 0),
src == INADDR_ANY_N ? "INADDR_ANY" :
inet_fmt(src, s1), inet_fmt(dst, s2));
}
}
}
u_int pim_send_cnt = 0;
#define SEND_DEBUG_NUMBER 50
/* TODO: This can be merged with the above procedure */
/*
* Send an unicast PIM packet from src to dst, PIM message type = "type"
* and data length (after the PIM common header) = "datalen"
*/
void
send_pim_unicast(buf, src, dst, type, datalen)
char *buf;
u_int32 src, dst;
int type, datalen;
{
struct sockaddr_in sdst;
struct ip *ip;
pim_header_t *pim;
int sendlen;
#ifdef RAW_OUTPUT_IS_RAW
extern int curttl;
#endif /* RAW_OUTPUT_IS_RAW */
/* Prepare the IP header */
ip = (struct ip *)buf;
ip->ip_len = sizeof(struct ip) + sizeof(pim_header_t) + datalen;
ip->ip_src.s_addr = src;
ip->ip_dst.s_addr = dst;
sendlen = ip->ip_len;
/* TODO: XXX: setup the TTL from the inner mcast packet? */
ip->ip_ttl = MAXTTL;
#ifdef RAW_OUTPUT_IS_RAW
ip->ip_len = htons(ip->ip_len);
#endif /* RAW_OUTPUT_IS_RAW */
/* Prepare the PIM packet */
pim = (pim_header_t *)(buf + sizeof(struct ip));
pim->pim_vers = PIM_PROTOCOL_VERSION;
pim->pim_type = type;
pim->reserved = 0;
pim->pim_cksum = 0;
bzero((void *)&sdst, sizeof(sdst));
sdst.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
sdst.sin_len = sizeof(sdst);
#endif
sdst.sin_addr.s_addr = dst;
if (sendto(pim_socket, buf, sendlen, 0,
(struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
if (errno == ENETDOWN)
check_vif_state();
else
log(LOG_WARNING, errno, "sendto from %s to %s",
inet_fmt(src, s1), inet_fmt(dst, s2));
}
IF_DEBUG(DEBUG_PIM_DETAIL) {
IF_DEBUG(DEBUG_PIM) {
/* TODO: use pim_send_cnt ?
if (++pim_send_cnt > SEND_DEBUG_NUMBER) {
pim_send_cnt = 0;
log(LOG_DEBUG, 0, "sending %s from %-15s to %s",
packet_kind(IPPROTO_PIM, type, 0),
inet_fmt(src, s1), inet_fmt(dst, s2));
}
*/
log(LOG_DEBUG, 0, "sending %s from %-15s to %s",
packet_kind(IPPROTO_PIM, type, 0),
inet_fmt(src, s1), inet_fmt(dst, s2));
}
}
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>