File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mrouted / kern.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:10:48 2012 UTC (12 years, 3 months ago) by misho
Branches: mrouted, MAIN
CVS tags: v3_9_6p0, v3_9_6, v3_9_5, HEAD
mrouted

/*
 * The mrouted program is covered by the license in the accompanying file
 * named "LICENSE".  Use of the mrouted program represents acceptance of
 * the terms and conditions listed in that file.
 *
 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
 * Leland Stanford Junior University.
 */

#include "defs.h"

int curttl = 0;

/*
 * Open/init the multicast routing in the kernel and sets the
 * MRT_PIM (aka MRT_ASSERT) flag in the kernel.
 */
void k_init_dvmrp(void)
{
#ifdef OLD_KERNEL
    if (setsockopt(igmp_socket, IPPROTO_IP, MRT_INIT, (char *)NULL, 0) < 0) {
#else
    int v = 1;

    if (setsockopt(igmp_socket, IPPROTO_IP, MRT_INIT, (char *)&v, sizeof(int)) < 0) {
#endif
	if (errno == EADDRINUSE)
	    logit(LOG_ERR, 0, "Another multicast routing application is already running.");
	else
	    logit(LOG_ERR, errno, "Cannot enable multicast routing in kernel");
    }
}


/*
 * Stops the multicast routing in the kernel and resets the
 * MRT_PIM (aka MRT_ASSERT) flag in the kernel.
 */
void k_stop_dvmrp(void)
{
    if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DONE, (char *)NULL, 0) < 0)
	logit(LOG_WARNING, errno, "Cannot disable multicast routing in kernel");
}


/*
 * Set the socket receiving buffer. `bufsize` is the preferred size,
 * `minsize` is the smallest acceptable size.
 */
void k_set_rcvbuf(int bufsize, int minsize)
{
    int delta = bufsize / 2;
    int iter = 0;

    /*
     * Set the socket buffer.  If we can't set it as large as we want, search around
     * to try to find the highest acceptable value.  The highest acceptable value
     * being smaller than minsize is a fatal error.
     */
    if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(bufsize)) < 0) {
        bufsize -= delta;
        while (1) {
            iter++;
            if (delta > 1)
                delta /= 2;

            if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(bufsize)) < 0) {
                bufsize -= delta;
            } else {
                if (delta < 1024)
                    break;
                bufsize += delta;
            }
        }
        if (bufsize < minsize) {
            logit(LOG_ERR, 0, "OS-allowed recv buffer size %u < app min %u", bufsize, minsize);
            /*NOTREACHED*/
        }
    }
    IF_DEBUG(DEBUG_KERN) {
        logit(LOG_DEBUG, 0, "Got %d byte recv buffer size in %d iterations",
              bufsize, iter);
    }
}


/*
 * Set/reset the IP_HDRINCL option. My guess is we don't need it for raw
 * sockets, but having it here won't hurt. Well, unless you are running
 * an older version of FreeBSD (older than 2.2.2). If the multicast
 * raw packet is bigger than 208 bytes, then IP_HDRINCL triggers a bug
 * in the kernel and "panic". The kernel patch for netinet/ip_raw.c
 * coming with this distribution fixes it.
 */
void k_hdr_include(int bool)
{
#ifdef IP_HDRINCL
    if (setsockopt(igmp_socket, IPPROTO_IP, IP_HDRINCL, (char *)&bool, sizeof(bool)) < 0)
        logit(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool);
#endif
}


/*
 * Set the default TTL for the multicast packets outgoing from this socket.
 */
void k_set_ttl(int t)
{
    u_char ttl;

    ttl = t;
    if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl)) < 0)
	logit(LOG_ERR, errno, "setsockopt IP_MULTICAST_TTL %u", ttl);

    curttl = t;
}


/*
 * Set/reset the IP_MULTICAST_LOOP. Set/reset is specified by "flag".
 */
void k_set_loop(int flag)
{
    u_char loop;

    loop = flag;
    if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loop, sizeof(loop)) < 0)
        logit(LOG_ERR, errno, "setsockopt IP_MULTICAST_LOOP %u", loop);
}


/*
 * Set the IP_MULTICAST_IF option on local interface ifa.
 */
void k_set_if(u_int32 ifa)
{
    struct in_addr adr;

    adr.s_addr = ifa;
    if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_IF, (char *)&adr, sizeof(adr)) < 0) {
        if (errno == EADDRNOTAVAIL || errno == EINVAL)
            return;
        logit(LOG_ERR, errno, "setsockopt IP_MULTICAST_IF %s",
              inet_fmt(ifa, s1, sizeof(s1)));
    }
}


/*
 * Join a multicast group.
 */
void k_join(u_int32 grp, u_int32 ifa)
{
    struct ip_mreq mreq;

    mreq.imr_multiaddr.s_addr = grp;
    mreq.imr_interface.s_addr = ifa;

    if (setsockopt(igmp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0)
	logit(LOG_WARNING, errno, "Cannot join group %s on interface %s",
	      inet_fmt(grp, s1, sizeof(s1)), inet_fmt(ifa, s2, sizeof(s2)));
}


/*
 * Leave a multicast group.
 */
void k_leave(u_int32 grp, u_int32 ifa)
{
    struct ip_mreq mreq;

    mreq.imr_multiaddr.s_addr = grp;
    mreq.imr_interface.s_addr = ifa;

    if (setsockopt(igmp_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0)
	logit(LOG_WARNING, errno, "Cannot leave group %s on interface %s",
	      inet_fmt(grp, s1, sizeof(s1)), inet_fmt(ifa, s2, sizeof(s2)));
}

/*
 * Fill struct vifctl using corresponding fields from struct uvif.
 */
static void uvif_to_vifctl(struct vifctl *vc, struct uvif *v)
{
    vc->vifc_flags           = v->uv_flags & VIFF_KERNEL_FLAGS;
    vc->vifc_threshold       = v->uv_threshold;
    vc->vifc_rate_limit	     = v->uv_rate_limit;
    vc->vifc_lcl_addr.s_addr = v->uv_lcl_addr;
    vc->vifc_rmt_addr.s_addr = v->uv_rmt_addr;
}

/*
 * Add a virtual interface in the kernel.
 */
void k_add_vif(vifi_t vifi, struct uvif *v)
{
    struct vifctl vc;

    vc.vifc_vifi = vifi;
    uvif_to_vifctl(&vc, v);
    if (setsockopt(igmp_socket, IPPROTO_IP, MRT_ADD_VIF, (char *)&vc, sizeof(vc)) < 0)
	logit(LOG_ERR, errno, "setsockopt MRT_ADD_VIF on vif %d", vifi);
}


/*
 * Delete a virtual interface in the kernel.
 */
void k_del_vif(vifi_t vifi, struct uvif UNUSED *v)
{
    /*
     * Unfortunately Linux setsocopt MRT_DEL_VIF API differs a bit from the *BSD one.
     * It expects to receive a pointer to struct vifctl that corresponds to the VIF
     * we're going to delete.  *BSD systems on the other hand exepect only the index
     * of that VIF.
     */
#ifdef __linux__
    struct vifctl vc;

    vc.vifc_vifi = vifi;
    uvif_to_vifctl(&vc, v);

    if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DEL_VIF, (char *)&vc, sizeof(vc)) < 0)
#else /* *BSD et al. */
    if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DEL_VIF, (char *)&vifi, sizeof(vifi)) < 0)
#endif /* !__linux__ */
    {
        if (errno == EADDRNOTAVAIL || errno == EINVAL)
            return;

	logit(LOG_ERR, errno, "setsockopt MRT_DEL_VIF on vif %d", vifi);
    }
}


/*
 * Adds a (source, mcastgrp) entry to the kernel
 */
void k_add_rg(u_int32 origin, struct gtable *g)
{
    struct mfcctl mc;
    vifi_t i;

#ifdef DEBUG_MFC
    md_log(MD_ADD, origin, g->gt_mcastgrp);
#endif
    /* copy table values so that setsockopt can process it */
    mc.mfcc_origin.s_addr = origin;
#ifdef OLD_KERNEL
    mc.mfcc_originmask.s_addr = 0xffffffff;
#endif
    mc.mfcc_mcastgrp.s_addr = g->gt_mcastgrp;
    mc.mfcc_parent = g->gt_route ? g->gt_route->rt_parent : NO_VIF;
    for (i = 0; i < numvifs; i++)
	mc.mfcc_ttls[i] = g->gt_ttls[i];

    /* write to kernel space */
    if (setsockopt(igmp_socket, IPPROTO_IP, MRT_ADD_MFC,
		   (char *)&mc, sizeof(mc)) < 0) {
#ifdef DEBUG_MFC
	md_log(MD_ADD_FAIL, origin, g->gt_mcastgrp);
#endif
	logit(LOG_WARNING, errno, "setsockopt MRT_ADD_MFC",
		inet_fmt(origin, s1, sizeof(s1)), inet_fmt(g->gt_mcastgrp, s2, sizeof(s2)));
    }
}


/*
 * Deletes a (source, mcastgrp) entry from the kernel
 */
int k_del_rg(u_int32 origin, struct gtable *g)
{
    struct mfcctl mc;

#ifdef DEBUG_MFC
    md_log(MD_DEL, origin, g->gt_mcastgrp);
#endif
    /* copy table values so that setsockopt can process it */
    mc.mfcc_origin.s_addr = origin;
#ifdef OLD_KERNEL
    mc.mfcc_originmask.s_addr = 0xffffffff;
#endif
    mc.mfcc_mcastgrp.s_addr = g->gt_mcastgrp;

    /* write to kernel space */
    if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DEL_MFC, (char *)&mc, sizeof(mc)) < 0) {
#ifdef DEBUG_MFC
	md_log(MD_DEL_FAIL, origin, g->gt_mcastgrp);
#endif
	logit(LOG_WARNING, errno, "setsockopt MRT_DEL_MFC of (%s %s)",
	      inet_fmt(origin, s1, sizeof(s1)), inet_fmt(g->gt_mcastgrp, s2, sizeof(s2)));

	return -1;
    }

    return 0;
}	

/*
 * Get the kernel's idea of what version of mrouted needs to run with it.
 */
int k_get_version(void)
{
#ifdef OLD_KERNEL
    return -1;
#else
    int vers;
    socklen_t len = sizeof(vers);

    if (getsockopt(igmp_socket, IPPROTO_IP, MRT_VERSION, (char *)&vers, &len) < 0)
	logit(LOG_ERR, errno, "getsockopt MRT_VERSION: perhaps your kernel is too old?");

    return vers;
#endif
}

#if 0
/*
 * Get packet counters
 */
int k_get_vif_count(vifi_t vifi, int *icount, int *ocount, int *ibytes, int *obytes)
{
    struct sioc_vif_req vreq;
    int retval = 0;

    vreq.vifi = vifi;
    if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&vreq) < 0) {
	logit(LOG_WARNING, errno, "SIOCGETVIFCNT on vif %d", vifi);
	vreq.icount = vreq.ocount = vreq.ibytes =
		vreq.obytes = 0xffffffff;
	retval = 1;
    }
    if (icount)
	*icount = vreq.icount;
    if (ocount)
	*ocount = vreq.ocount;
    if (ibytes)
	*ibytes = vreq.ibytes;
    if (obytes)
	*obytes = vreq.obytes;
    return retval;
}

/*
 * Get counters for a desired source and group.
 */
int k_get_sg_count(u_int32 src, u_int32 grp, int *pktcnt, int *bytecnt, int *wrong_if)
{
    struct sioc_sg_req sgreq;
    int retval = 0;

    sgreq.src.s_addr = src;
    sgreq.grp.s_addr = grp;
    if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sgreq) < 0) {
	logit(LOG_WARNING, errno, "SIOCGETSGCNT on (%s %s)",
	    inet_fmt(src, s1, sizeof(s1)), inet_fmt(grp, s2, sizeof(s2)));
	sgreq.pktcnt = sgreq.bytecnt = sgreq.wrong_if = 0xffffffff;
	return 1;
    }
    if (pktcnt)
    	*pktcnt = sgreq.pktcnt;
    if (bytecnt)
    	*bytecnt = sgreq.bytecnt;
    if (wrong_if)
    	*wrong_if = sgreq.wrong_if;

    return retval;
}
#endif

/**
 * Local Variables:
 *  version-control: t
 *  indent-tabs-mode: t
 *  c-file-style: "ellemtel"
 *  c-basic-offset: 4
 * End:
 */

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