File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / pimdd / igmp.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: igmp.c,v 1.1.1.1 2017/06/12 07:58:55 misho Exp $
 */
/*
 * Part of this program has been derived from mrouted.
 * The mrouted program is covered by the license in the accompanying file
 * named "LICENSE.mrouted".
 *
 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
 * Leland Stanford Junior University.
 *
 */

#include "defs.h"

/*
 * Exported variables.
 */
char    *igmp_recv_buf;		/* input packet buffer               */
char    *igmp_send_buf;  	/* output packet buffer              */
int     igmp_socket;	      	/* socket for all network I/O        */
u_int32 allhosts_group;	      	/* allhosts  addr in net order       */
u_int32 allrouters_group;	/* All-Routers addr in net order     */

/*
 * Local functions definitions.
 */
static void igmp_read        __P((int i, fd_set *rfd));
static void accept_igmp      __P((int recvlen));


/*
 * Open and initialize the igmp socket, and fill in the non-changing
 * IP header fields in the output packet buffer.
 */
void 
init_igmp()
{
    struct ip *ip;
    
    igmp_recv_buf = malloc(RECV_BUF_SIZE);
    igmp_send_buf = malloc(RECV_BUF_SIZE);

    if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) 
	log(LOG_ERR, errno, "IGMP socket");
    
    k_hdr_include(igmp_socket, TRUE);	/* include IP header when sending */
    k_set_rcvbuf(igmp_socket, SO_RECV_BUF_SIZE_MAX,
		 SO_RECV_BUF_SIZE_MIN); /* lots of input buffering        */
    k_set_ttl(igmp_socket, MINTTL);	/* restrict multicasts to one hop */
    k_set_loop(igmp_socket, FALSE);	/* disable multicast loopback     */
    
    ip         = (struct ip *)igmp_send_buf;
    ip->ip_v   = IPVERSION;
    ip->ip_hl  = (sizeof(struct ip) >> 2);
    ip->ip_tos = 0xc0;                  /* Internet Control   */
    ip->ip_id  = 0;                     /* let kernel fill in */
    ip->ip_off = 0;
    ip->ip_ttl = MAXTTL;		/* applies to unicasts only */
    ip->ip_p   = IPPROTO_IGMP;
#ifdef Linux
    ip->ip_csum = 0;                    /* let kernel fill in               */
#else
    ip->ip_sum = 0;                     /* let kernel fill in               */
#endif /* Linux */

    /* Everywhere in the daemon we use network-byte-order */    
    allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
    allrouters_group = htonl(INADDR_ALLRTRS_GROUP);
    
    if (register_input_handler(igmp_socket, igmp_read) < 0)
	log(LOG_ERR, 0, "Couldn't register igmp_read as an input handler");
}


/* Read an IGMP message */
static void
igmp_read(i, rfd)
    int i;
    fd_set *rfd;
{
    register int igmp_recvlen;
    int dummy = 0;
    
    igmp_recvlen = recvfrom(igmp_socket, igmp_recv_buf, RECV_BUF_SIZE,
			    0, NULL, &dummy);
    
    if (igmp_recvlen < 0) {
	if (errno != EINTR)
	    log(LOG_ERR, errno, "IGMP recvfrom");
	return;
    }
    
    /* TODO: make it as a thread in the future releases */
    accept_igmp(igmp_recvlen);
}


/*
 * Process a newly received IGMP packet that is sitting in the input
 * packet buffer.
 */
static void 
accept_igmp(recvlen)
    int recvlen;
{
    register u_int32 src, dst, group;
    struct ip *ip;
    struct igmp *igmp;
    int ipdatalen, iphdrlen, igmpdatalen;
    
    if (recvlen < sizeof(struct ip)) {
	log(LOG_WARNING, 0,
	    "received packet too short (%u bytes) for IP header", recvlen);
	return;
    }
    
    ip        = (struct ip *)igmp_recv_buf;
    src       = ip->ip_src.s_addr;
    dst       = ip->ip_dst.s_addr;
    
    /* packets sent up from kernel to daemon have ip->ip_p = 0 */
    if (ip->ip_p == 0) {
	if (src == 0 || dst == 0)
	    log(LOG_WARNING, 0, "kernel request not accurate, src %s dst %s",
		inet_fmt(src, s1), inet_fmt(dst, s2));
	else
	    process_kernel_call();
	return;
    }
    
    iphdrlen  = ip->ip_hl << 2;
#ifdef RAW_INPUT_IS_RAW
    ipdatalen = ntohs(ip->ip_len) - iphdrlen;
#else
    ipdatalen = ip->ip_len;
#endif
    if (iphdrlen + ipdatalen != recvlen) {
	log(LOG_WARNING, 0,
	    "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
	    inet_fmt(src, s1), recvlen, iphdrlen, ipdatalen);
	return;
    }
    
    igmp        = (struct igmp *)(igmp_recv_buf + iphdrlen);
    group       = igmp->igmp_group.s_addr;
    igmpdatalen = ipdatalen - IGMP_MINLEN;
    if (igmpdatalen < 0) {
	log(LOG_WARNING, 0,
	    "received IP data field too short (%u bytes) for IGMP, from %s",
	    ipdatalen, inet_fmt(src, s1));
	return;
    }

/* TODO: too noisy. Remove it? */
#ifdef NOSUCHDEF
    IF_DEBUG(DEBUG_PKT | debug_kind(IPPROTO_IGMP, igmp->igmp_type,
				    igmp->igmp_code))
	log(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
	    packet_kind(IPPROTO_IGMP, igmp->igmp_type, igmp->igmp_code),
	    inet_fmt(src, s1), inet_fmt(dst, s2));
#endif /* NOSUCHDEF */
    
    switch (igmp->igmp_type) {
    case IGMP_MEMBERSHIP_QUERY:
	accept_membership_query(src, dst, group, igmp->igmp_code);
	return;
	
    case IGMP_V1_MEMBERSHIP_REPORT:
    case IGMP_V2_MEMBERSHIP_REPORT:
	accept_group_report(src, dst, group, igmp->igmp_type);
	return;
	
    case IGMP_V2_LEAVE_GROUP:
	accept_leave_message(src, dst, group);
	return;
	
    case IGMP_DVMRP:
	/* XXX: TODO: most of the stuff below is not implemented. We are still
	 * only PIM router.
	 */
	group = ntohl(group);

	switch (igmp->igmp_code) {
	case DVMRP_PROBE:
	    dvmrp_accept_probe(src, dst, (char *)(igmp+1), igmpdatalen, group);
	    return;
	    
	case DVMRP_REPORT:
	    dvmrp_accept_report(src, dst, (char *)(igmp+1), igmpdatalen,
				group);
	    return;

	case DVMRP_ASK_NEIGHBORS:
	    accept_neighbor_request(src, dst);
	    return;

	case DVMRP_ASK_NEIGHBORS2:
	    accept_neighbor_request2(src, dst);
	    return;
	    
	case DVMRP_NEIGHBORS:
	    dvmrp_accept_neighbors(src, dst, (u_char *)(igmp+1), igmpdatalen,
				   group);
	    return;

	case DVMRP_NEIGHBORS2:
	    dvmrp_accept_neighbors2(src, dst, (u_char *)(igmp+1), igmpdatalen,
				    group);
	    return;
	    
	case DVMRP_PRUNE:
	    dvmrp_accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
	    return;
	    
	case DVMRP_GRAFT:
	    dvmrp_accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
	    return;
	    
	case DVMRP_GRAFT_ACK:
	    dvmrp_accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
	    return;
	    
	case DVMRP_INFO_REQUEST:
	    dvmrp_accept_info_request(src, dst, (char *)(igmp+1), igmpdatalen);
	    return;

	case DVMRP_INFO_REPLY:
	    dvmrp_accept_info_reply(src, dst, (char *)(igmp+1), igmpdatalen);
	    return;
	    
	default:
	    log(LOG_INFO, 0,
		"ignoring unknown DVMRP message code %u from %s to %s",
		igmp->igmp_code, inet_fmt(src, s1), inet_fmt(dst, s2));
	    return;
	}
	
    case IGMP_PIM:
	return;    /* TODO: this is PIM v1 message. Handle it?. */
	
    case IGMP_MTRACE_RESP:
	return;    /* TODO: implement it */
	
    case IGMP_MTRACE:
	accept_mtrace(src, dst, group, (char *)(igmp+1), igmp->igmp_code,
		      igmpdatalen);
	return;
	
    default:
	log(LOG_INFO, 0,
	    "ignoring unknown IGMP message type %x from %s to %s",
	    igmp->igmp_type, inet_fmt(src, s1), inet_fmt(dst, s2));
	return;
    }
}

void
send_igmp(buf, src, dst, type, code, group, datalen)
    char *buf;
    u_int32 src, dst;
    int type, code;
    u_int32 group;
    int datalen;
{
    struct sockaddr_in sdst;
    struct ip *ip;
    struct igmp *igmp;
    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) + IGMP_MINLEN + datalen;
    ip->ip_src.s_addr       = src; 
    ip->ip_dst.s_addr       = dst;
    sendlen                 = ip->ip_len;
#ifdef RAW_OUTPUT_IS_RAW
    ip->ip_len              = htons(ip->ip_len);
#endif /* RAW_OUTPUT_IS_RAW */

    igmp                    = (struct igmp *)(buf + sizeof(struct ip));
    igmp->igmp_type         = type;
    igmp->igmp_code         = code;
    igmp->igmp_group.s_addr = group;
    igmp->igmp_cksum        = 0;
    igmp->igmp_cksum        = inet_cksum((u_int16 *)igmp,
					 IGMP_MINLEN + datalen);
    
    if (IN_MULTICAST(ntohl(dst))){
	k_set_if(igmp_socket, src);
	if (type != IGMP_DVMRP || dst == allhosts_group) {
	    setloop = 1;
	    k_set_loop(igmp_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(igmp_socket, igmp_send_buf, sendlen, 0,
	       (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
	if (errno == ENETDOWN)
	    check_vif_state();
	else
	    log(log_level(IPPROTO_IGMP, type, code), errno,
		"sendto to %s on %s", inet_fmt(dst, s1), inet_fmt(src, s2));
	if (setloop)
	    k_set_loop(igmp_socket, FALSE);
	return;
    }
    
    if (setloop)
	k_set_loop(igmp_socket, FALSE);
    
    IF_DEBUG(DEBUG_PKT|debug_kind(IPPROTO_IGMP, type, code))
	log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
	    packet_kind(IPPROTO_IGMP, type, code),
	    src == INADDR_ANY_N ? "INADDR_ANY" :
	    inet_fmt(src, s1), inet_fmt(dst, s2));
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>