diff options
Diffstat (limited to 'usr.sbin/pim6dd/kern.c')
-rw-r--r-- | usr.sbin/pim6dd/kern.c | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/usr.sbin/pim6dd/kern.c b/usr.sbin/pim6dd/kern.c new file mode 100644 index 0000000..170e85e --- /dev/null +++ b/usr.sbin/pim6dd/kern.c @@ -0,0 +1,415 @@ +/* + * 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 1999/08/08 23:30:52 itojun 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. + * + * $FreeBSD$ + */ + +#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_IPV6, + MRT6_INIT, (char *)&v, sizeof(int)) < 0) + log(LOG_ERR, errno, "cannot enable multicast routing in kernel"); + + if(setsockopt(socket, IPPROTO_IPV6, + MRT6_PIM, (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_IPV6, MRT6_PIM, + (char *)&v, sizeof(int)) < 0) + log(LOG_ERR, errno, "cannot reset ASSERT flag in kernel"); + + if (setsockopt(socket, IPPROTO_IPV6, MRT6_DONE, (char *)NULL, 0) < 0) + log(LOG_ERR, 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(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); +} + +#if 0 /* there is no HDRINCL option in IPv6 */ +/* + * 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 +} +#endif /* 0 */ + +/* + * Set the default Hop Limit for the multicast packets outgoing from this + * socket. + */ +void k_set_hlim(socket, h) + int socket; + int h; +{ +#ifdef RAW_OUTPUT_IS_RAW + curttl = h; +#else + int hlim = h; + + if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + (char *)&hlim, sizeof(hlim)) < 0) + log(LOG_ERR, errno, "setsockopt IPV6_MULTICAST_HOPS %u", hlim); +#endif +} + + +/* + * Set/reset the IPV6_MULTICAST_LOOP. Set/reset is specified by "flag". + */ +void k_set_loop(socket, flag) + int socket; + int flag; +{ + u_int loop; + + loop = flag; + if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, + (char *)&loop, sizeof(loop)) < 0) + log(LOG_ERR, errno, "setsockopt IPV6_MULTICAST_LOOP %u", loop); +} + + +/* + * Set the IPV6_MULTICAST_IF option on local interface which has the + * specified index. + */ +void k_set_if(socket, ifindex) + int socket; + u_int ifindex; +{ + if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF, + (char *)&ifindex, sizeof(ifindex)) < 0) + log(LOG_ERR, errno, "setsockopt IPV6_MULTICAST_IF for %s", + ifindex2str(ifindex)); +} + + +/* + * Join a multicast grp group on local interface ifa. + */ +void k_join(socket, grp, ifindex) + int socket; + struct in6_addr *grp; + u_int ifindex; +{ + struct ipv6_mreq mreq; + + mreq.ipv6mr_multiaddr = *grp; + mreq.ipv6mr_interface = ifindex; + + if (setsockopt(socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, + (char *)&mreq, sizeof(mreq)) < 0) + log(LOG_WARNING, errno, "cannot join group %s on interface %s", + inet6_fmt(grp), ifindex2str(ifindex)); +} + + +/* + * Leave a multicats grp group on local interface ifa. + */ +void k_leave(socket, grp, ifindex) + int socket; + struct in6_addr *grp; + u_int ifindex; +{ + struct ipv6_mreq mreq; + + mreq.ipv6mr_multiaddr = *grp; + mreq.ipv6mr_interface = ifindex; + + if (setsockopt(socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP, + (char *)&mreq, sizeof(mreq)) < 0) + log(LOG_WARNING, errno, "cannot leave group %s on interface %s", + inet6_fmt(grp), ifindex2str(ifindex)); +} + + +/* + * Add a virtual interface in the kernel. + */ +void k_add_vif(socket, vifi, v) + int socket; + vifi_t vifi; + struct uvif *v; +{ + struct mif6ctl mc; + + mc.mif6c_mifi = vifi; + /* TODO: only for DVMRP tunnels? + mc.mif6c_flags = v->uv_flags & VIFF_KERNEL_FLAGS; + */ + mc.mif6c_flags = v->uv_flags; +#ifdef notyet + mc.mif6c_rate_limit = v->uv_rate_limit; +#endif + mc.mif6c_pifi = v->uv_ifindex; + + if (setsockopt(socket, IPPROTO_IPV6, MRT6_ADD_MIF, + (char *)&mc, sizeof(mc)) < 0) + log(LOG_ERR, errno, "setsockopt MRT6_ADD_MIF on mif %d", vifi); +} + +/* + * Delete a virtual interface in the kernel. + */ +void k_del_vif(socket, vifi) + int socket; + vifi_t vifi; +{ + if (setsockopt(socket, IPPROTO_IPV6, MRT6_DEL_MIF, + (char *)&vifi, sizeof(vifi)) < 0) + log(LOG_ERR, errno, "setsockopt MRT6_DEL_MIF on mif %d", vifi); +} + + +/* + * Delete all MFC entries for particular routing entry from the kernel. + */ +int +k_del_mfc(socket, source, group) + int socket; + struct sockaddr_in6 *source; + struct sockaddr_in6 *group; +{ + struct mf6cctl mc; + + mc.mf6cc_origin = *source; + mc.mf6cc_mcastgrp = *group; + + if (setsockopt(socket, IPPROTO_IPV6, MRT6_DEL_MFC, (char *)&mc, + sizeof(mc)) < 0) { + log(LOG_WARNING, errno, "setsockopt MRT6_DEL_MFC"); + return FALSE; + } + + IF_DEBUG(DEBUG_MFC) + log(LOG_DEBUG, 0, "Deleted MFC entry: src %s, grp %s", + inet6_fmt(&source->sin6_addr), + inet6_fmt(&group->sin6_addr)); + + return(TRUE); +} + + +/* + * Install/modify a MFC entry in the kernel + */ +int +k_chg_mfc(socket, source, group, iif, oifs) + int socket; + struct sockaddr_in6 *source; + struct sockaddr_in6 *group; + vifi_t iif; + if_set *oifs; +{ + struct mf6cctl mc; + vifi_t vifi; + + mc.mf6cc_origin = *source; + mc.mf6cc_mcastgrp = *group; + mc.mf6cc_parent = iif; + + IF_ZERO(&mc.mf6cc_ifset); + for (vifi = 0; vifi < numvifs; vifi++) { + if (IF_ISSET(vifi, oifs)) + IF_SET(vifi, &mc.mf6cc_ifset); + else + IF_CLR(vifi, &mc.mf6cc_ifset); + } + + if (setsockopt(socket, IPPROTO_IPV6, MRT6_ADD_MFC, (char *)&mc, + sizeof(mc)) < 0) { + log(LOG_WARNING, errno, + "setsockopt MRT6_ADD_MFC for source %s and group %s", + inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr)); + 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_mif_req6 mreq; + + mreq.mifi = vifi; + if (ioctl(udp_socket, SIOCGETMIFCNT_IN6, (char *)&mreq) < 0) { + log(LOG_WARNING, errno, "SIOCGETMIFCNT_IN6 on vif %d", vifi); + retval->icount = retval->ocount = retval->ibytes = + retval->obytes = 0xffffffff; + return (1); + } + retval->icount = mreq.icount; + retval->ocount = mreq.ocount; + retval->ibytes = mreq.ibytes; + retval->obytes = mreq.obytes; + return (0); +} + + +/* + * Gets the number of packets, bytes, and number of 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 */ + struct sockaddr_in6 *source; + struct sockaddr_in6 *group; + struct sg_count *retval; +{ + struct sioc_sg_req6 sgreq; + + sgreq.src = *source; + sgreq.grp = *group; + if (ioctl(socket, SIOCGETSGCNT_IN6, (char *)&sgreq) < 0) { + log(LOG_WARNING, errno, "SIOCGETSGCNT_IN6 on (%s %s)", + inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr)); + retval->pktcnt = retval->bytecnt = retval->wrong_if = ~0; /* XXX */ + return(1); + } + retval->pktcnt = sgreq.pktcnt; + retval->bytecnt = sgreq.bytecnt; + retval->wrong_if = sgreq.wrong_if; + return(0); +} + + + |