File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / pimdd / pim.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jun 12 07:58:55 2017 UTC (6 years, 11 months ago) by misho
Branches: pimdd, MAIN
CVS tags: v0_2_1p0, v0_2_1, HEAD
pimdd-dense 0.2.1.0_2

/*
 *  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>