/*
* 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: kern.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"
#ifdef RAW_OUTPUT_IS_RAW
int curttl = 0;
#endif
/*
* Open/init the multicast routing in the kernel and sets the MRT_ASSERT
* flag in the kernel.
*
*/
void
k_init_pim(socket)
int socket;
{
int v = 1;
if (setsockopt(socket, IPPROTO_IP, MRT_INIT, (char *)&v, sizeof(int)) < 0)
log(LOG_ERR, errno, "cannot enable multicast routing in kernel");
if(setsockopt(socket, IPPROTO_IP, MRT_ASSERT, (char *)&v, sizeof(int)) < 0)
log(LOG_ERR, errno, "cannot set ASSERT flag in kernel");
}
/*
* Stops the multicast routing in the kernel and resets the MRT_ASSERT
* flag in the kernel.
*/
void
k_stop_pim(socket)
int socket;
{
int v = 0;
if (setsockopt(socket, IPPROTO_IP, MRT_DONE, (char *)NULL, 0) < 0)
log(LOG_ERR, errno, "cannot disable multicast routing in kernel");
if(setsockopt(socket, IPPROTO_IP, MRT_ASSERT, (char *)&v, sizeof(int)) < 0)
log(LOG_ERR, errno, "cannot reset ASSERT flag in kernel");
}
/*
* Set the socket receiving buffer. `bufsize` is the preferred size,
* `minsize` is the smallest acceptable size.
*/
void k_set_rcvbuf(socket, bufsize, minsize)
int socket;
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(socket, SOL_SOCKET, SO_RCVBUF,
(char *)&bufsize, sizeof(bufsize)) < 0) {
bufsize -= delta;
while (1) {
iter++;
if (delta > 1)
delta /= 2;
if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF,
(char *)&bufsize, sizeof(bufsize)) < 0) {
bufsize -= delta;
} else {
if (delta < 1024)
break;
bufsize += delta;
}
}
if (bufsize < minsize) {
log(LOG_ERR, 0, "OS-allowed buffer size %u < app min %u",
bufsize, minsize);
/*NOTREACHED*/
}
}
IF_DEBUG(DEBUG_KERN)
log(LOG_DEBUG, 0, "Got %d byte 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(socket, bool)
int socket;
int bool;
{
#ifdef IP_HDRINCL
if (setsockopt(socket, IPPROTO_IP, IP_HDRINCL,
(char *)&bool, sizeof(bool)) < 0)
log(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool);
#endif
}
/*
* Set the default TTL for the multicast packets outgoing from this
* socket.
* TODO: Does it affect the unicast packets?
*/
void k_set_ttl(socket, t)
int socket;
int t;
{
#ifdef RAW_OUTPUT_IS_RAW
curttl = t;
#else
u_char ttl;
ttl = t;
if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL,
(char *)&ttl, sizeof(ttl)) < 0)
log(LOG_ERR, errno, "setsockopt IP_MULTICAST_TTL %u", ttl);
#endif
}
/*
* Set/reset the IP_MULTICAST_LOOP. Set/reset is specified by "flag".
*/
void k_set_loop(socket, flag)
int socket;
int flag;
{
u_char loop;
loop = flag;
if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP,
(char *)&loop, sizeof(loop)) < 0)
log(LOG_ERR, errno, "setsockopt IP_MULTICAST_LOOP %u", loop);
}
/*
* Set the IP_MULTICAST_IF option on local interface ifa.
*/
void k_set_if(socket, ifa)
int socket;
u_int32 ifa;
{
struct in_addr adr;
adr.s_addr = ifa;
if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF,
(char *)&adr, sizeof(adr)) < 0)
log(LOG_ERR, errno, "setsockopt IP_MULTICAST_IF %s",
inet_fmt(ifa, s1));
}
/*
* Join a multicast grp group on local interface ifa.
*/
void k_join(socket, grp, ifa)
int socket;
u_int32 grp;
u_int32 ifa;
{
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = grp;
mreq.imr_interface.s_addr = ifa;
if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&mreq, sizeof(mreq)) < 0)
log(LOG_WARNING, errno, "cannot join group %s on interface %s",
inet_fmt(grp, s1), inet_fmt(ifa, s2));
}
/*
* Leave a multicats grp group on local interface ifa.
*/
void k_leave(socket, grp, ifa)
int socket;
u_int32 grp;
u_int32 ifa;
{
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = grp;
mreq.imr_interface.s_addr = ifa;
if (setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP,
(char *)&mreq, sizeof(mreq)) < 0)
log(LOG_WARNING, errno, "cannot leave group %s on interface %s",
inet_fmt(grp, s1), inet_fmt(ifa, s2));
}
/*
* Add a virtual interface in the kernel.
*/
void k_add_vif(socket, vifi, v)
int socket;
vifi_t vifi;
struct uvif *v;
{
struct vifctl vc;
vc.vifc_vifi = vifi;
/* TODO: only for DVMRP tunnels?
vc.vifc_flags = v->uv_flags & VIFF_KERNEL_FLAGS;
*/
vc.vifc_flags = v->uv_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;
if (setsockopt(socket, IPPROTO_IP, MRT_ADD_VIF,
(char *)&vc, sizeof(vc)) < 0)
log(LOG_ERR, errno, "setsockopt MRT_ADD_VIF on vif %d", vifi);
}
/*
* Delete a virtual interface in the kernel.
*/
void k_del_vif(socket, vifi)
int socket;
vifi_t vifi;
{
if (setsockopt(socket, IPPROTO_IP, MRT_DEL_VIF,
(char *)&vifi, sizeof(vifi)) < 0)
log(LOG_ERR, errno, "setsockopt MRT_DEL_VIF on vif %d", vifi);
}
/*
* Delete all MFC entries for particular routing entry from the kernel.
*/
int
k_del_mfc(socket, source, group)
int socket;
u_int32 source;
u_int32 group;
{
struct mfcctl mc;
mc.mfcc_origin.s_addr = source;
mc.mfcc_mcastgrp.s_addr = group;
if (setsockopt(socket, IPPROTO_IP, MRT_DEL_MFC, (char *)&mc,
sizeof(mc)) < 0) {
log(LOG_WARNING, errno, "setsockopt k_del_mfc");
return FALSE;
}
IF_DEBUG(DEBUG_MFC)
log(LOG_DEBUG, 0, "Deleted MFC entry: src %s, grp %s",
inet_fmt(mc.mfcc_origin.s_addr, s1),
inet_fmt(mc.mfcc_mcastgrp.s_addr, s2));
return(TRUE);
}
/*
* Install/modify a MFC entry in the kernel
*/
int
k_chg_mfc(socket, source, group, iif, oifs)
int socket;
u_int32 source;
u_int32 group;
vifi_t iif;
vifbitmap_t oifs;
{
struct mfcctl mc;
vifi_t vifi;
struct uvif *v;
mc.mfcc_origin.s_addr = source;
#ifdef OLD_KERNEL
mc.mfcc_originmas.s_addr = 0xffffffff; /* Got it from mrouted-3.9 */
#endif /* OLD_KERNEL */
mc.mfcc_mcastgrp.s_addr = group;
mc.mfcc_parent = iif;
for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
if (VIFM_ISSET(vifi, oifs))
mc.mfcc_ttls[vifi] = v->uv_threshold;
else
mc.mfcc_ttls[vifi] = 0;
}
if (setsockopt(socket, IPPROTO_IP, MRT_ADD_MFC, (char *)&mc,
sizeof(mc)) < 0) {
log(LOG_WARNING, errno,
"setsockopt MRT_ADD_MFC for source %s and group %s",
inet_fmt(source, s1), inet_fmt(group, s2));
return(FALSE);
}
return(TRUE);
}
/*
* Get packet counters for particular interface
*/
/*
* XXX: TODO: currently not used, but keep just in case we need it later.
*/
int k_get_vif_count(vifi, retval)
vifi_t vifi;
struct vif_count *retval;
{
struct sioc_vif_req vreq;
vreq.vifi = vifi;
if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&vreq) < 0) {
log(LOG_WARNING, errno, "SIOCGETVIFCNT on vif %d", vifi);
retval->icount = retval->ocount = retval->ibytes =
retval->obytes = 0xffffffff;
return (1);
}
retval->icount = vreq.icount;
retval->ocount = vreq.ocount;
retval->ibytes = vreq.ibytes;
retval->obytes = vreq.obytes;
return (0);
}
/*
* Gets the number of packets, bytes, and number op packets arrived
* on wrong if in the kernel for particular (S,G) entry.
*/
int
k_get_sg_cnt(socket, source, group, retval)
int socket; /* udp_socket */
u_int32 source;
u_int32 group;
struct sg_count *retval;
{
struct sioc_sg_req sgreq;
sgreq.src.s_addr = source;
sgreq.grp.s_addr = group;
if ((ioctl(socket, SIOCGETSGCNT, (char *)&sgreq) < 0)
|| (sgreq.wrong_if == 0xffffffff)) {
/* XXX: ipmulti-3.5 has bug in ip_mroute.c, get_sg_cnt():
* the return code is always 0, so this is why we need to check
* the wrong_if value.
*/
log(LOG_WARNING, errno, "SIOCGETSGCNT on (%s %s)",
inet_fmt(source, s1), inet_fmt(group, s2));
retval->pktcnt = retval->bytecnt = retval->wrong_if = ~0;
return(1);
}
retval->pktcnt = sgreq.pktcnt;
retval->bytecnt = sgreq.bytecnt;
retval->wrong_if = sgreq.wrong_if;
return(0);
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>