summaryrefslogtreecommitdiffstats
path: root/sys/netinet/ip_mroute.c
diff options
context:
space:
mode:
authorrgrimes <rgrimes@FreeBSD.org>1994-05-24 10:09:53 +0000
committerrgrimes <rgrimes@FreeBSD.org>1994-05-24 10:09:53 +0000
commit8fb65ce818b3e3c6f165b583b910af24000768a5 (patch)
treeba751e4f2166aefec707c9d7401c7ff432506642 /sys/netinet/ip_mroute.c
parenta6ce65d368e623088a4c1a29865889f431b15420 (diff)
downloadFreeBSD-src-8fb65ce818b3e3c6f165b583b910af24000768a5.zip
FreeBSD-src-8fb65ce818b3e3c6f165b583b910af24000768a5.tar.gz
BSD 4.4 Lite Kernel Sources
Diffstat (limited to 'sys/netinet/ip_mroute.c')
-rw-r--r--sys/netinet/ip_mroute.c834
1 files changed, 834 insertions, 0 deletions
diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c
new file mode 100644
index 0000000..1744ec1
--- /dev/null
+++ b/sys/netinet/ip_mroute.c
@@ -0,0 +1,834 @@
+/*
+ * Copyright (c) 1989 Stephen Deering
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_mroute.c 8.2 (Berkeley) 11/15/93
+ */
+
+/*
+ * Procedures for the kernel part of DVMRP,
+ * a Distance-Vector Multicast Routing Protocol.
+ * (See RFC-1075.)
+ *
+ * Written by David Waitzman, BBN Labs, August 1988.
+ * Modified by Steve Deering, Stanford, February 1989.
+ *
+ * MROUTING 1.1
+ */
+
+#ifndef MROUTING
+int ip_mrtproto; /* for netstat only */
+#else
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/raw_cb.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+
+#include <netinet/igmp.h>
+#include <netinet/igmp_var.h>
+#include <netinet/ip_mroute.h>
+
+/* Static forwards */
+static int ip_mrouter_init __P((struct socket *));
+static int add_vif __P((struct vifctl *));
+static int del_vif __P((vifi_t *vifip));
+static int add_lgrp __P((struct lgrplctl *));
+static int del_lgrp __P((struct lgrplctl *));
+static int grplst_member __P((struct vif *, struct in_addr));
+static u_long nethash __P((struct in_addr in));
+static int add_mrt __P((struct mrtctl *));
+static int del_mrt __P((struct in_addr *));
+static struct mrt *mrtfind __P((struct in_addr));
+static void phyint_send __P((struct mbuf *, struct vif *));
+static void tunnel_send __P((struct mbuf *, struct vif *));
+
+#define INSIZ sizeof(struct in_addr)
+#define same(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), INSIZ) == 0)
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+
+/*
+ * Globals. All but ip_mrouter and ip_mrtproto could be static,
+ * except for netstat or debugging purposes.
+ */
+struct socket *ip_mrouter = NULL;
+int ip_mrtproto = IGMP_DVMRP; /* for netstat only */
+
+struct mrt *mrttable[MRTHASHSIZ];
+struct vif viftable[MAXVIFS];
+struct mrtstat mrtstat;
+
+/*
+ * Private variables.
+ */
+static vifi_t numvifs = 0;
+static struct mrt *cached_mrt = NULL;
+static u_long cached_origin;
+static u_long cached_originmask;
+
+/*
+ * Handle DVMRP setsockopt commands to modify the multicast routing tables.
+ */
+int
+ip_mrouter_cmd(cmd, so, m)
+ register int cmd;
+ register struct socket *so;
+ register struct mbuf *m;
+{
+ register int error = 0;
+
+ if (cmd != DVMRP_INIT && so != ip_mrouter)
+ error = EACCES;
+ else switch (cmd) {
+
+ case DVMRP_INIT:
+ error = ip_mrouter_init(so);
+ break;
+
+ case DVMRP_DONE:
+ error = ip_mrouter_done();
+ break;
+
+ case DVMRP_ADD_VIF:
+ if (m == NULL || m->m_len < sizeof(struct vifctl))
+ error = EINVAL;
+ else
+ error = add_vif(mtod(m, struct vifctl *));
+ break;
+
+ case DVMRP_DEL_VIF:
+ if (m == NULL || m->m_len < sizeof(short))
+ error = EINVAL;
+ else
+ error = del_vif(mtod(m, vifi_t *));
+ break;
+
+ case DVMRP_ADD_LGRP:
+ if (m == NULL || m->m_len < sizeof(struct lgrplctl))
+ error = EINVAL;
+ else
+ error = add_lgrp(mtod(m, struct lgrplctl *));
+ break;
+
+ case DVMRP_DEL_LGRP:
+ if (m == NULL || m->m_len < sizeof(struct lgrplctl))
+ error = EINVAL;
+ else
+ error = del_lgrp(mtod(m, struct lgrplctl *));
+ break;
+
+ case DVMRP_ADD_MRT:
+ if (m == NULL || m->m_len < sizeof(struct mrtctl))
+ error = EINVAL;
+ else
+ error = add_mrt(mtod(m, struct mrtctl *));
+ break;
+
+ case DVMRP_DEL_MRT:
+ if (m == NULL || m->m_len < sizeof(struct in_addr))
+ error = EINVAL;
+ else
+ error = del_mrt(mtod(m, struct in_addr *));
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+ return (error);
+}
+
+/*
+ * Enable multicast routing
+ */
+static int
+ip_mrouter_init(so)
+ register struct socket *so;
+{
+ if (so->so_type != SOCK_RAW ||
+ so->so_proto->pr_protocol != IPPROTO_IGMP)
+ return (EOPNOTSUPP);
+
+ if (ip_mrouter != NULL)
+ return (EADDRINUSE);
+
+ ip_mrouter = so;
+
+ return (0);
+}
+
+/*
+ * Disable multicast routing
+ */
+int
+ip_mrouter_done()
+{
+ register vifi_t vifi;
+ register int i;
+ register struct ifnet *ifp;
+ register int s;
+ struct ifreq ifr;
+
+ s = splnet();
+
+ /*
+ * For each phyint in use, free its local group list and
+ * disable promiscuous reception of all IP multicasts.
+ */
+ for (vifi = 0; vifi < numvifs; vifi++) {
+ if (viftable[vifi].v_lcl_addr.s_addr != 0 &&
+ !(viftable[vifi].v_flags & VIFF_TUNNEL)) {
+ if (viftable[vifi].v_lcl_grps)
+ free(viftable[vifi].v_lcl_grps, M_MRTABLE);
+ satosin(&ifr.ifr_addr)->sin_family = AF_INET;
+ satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY;
+ ifp = viftable[vifi].v_ifp;
+ (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
+ }
+ }
+ bzero((caddr_t)viftable, sizeof(viftable));
+ numvifs = 0;
+
+ /*
+ * Free any multicast route entries.
+ */
+ for (i = 0; i < MRTHASHSIZ; i++)
+ if (mrttable[i])
+ free(mrttable[i], M_MRTABLE);
+ bzero((caddr_t)mrttable, sizeof(mrttable));
+ cached_mrt = NULL;
+
+ ip_mrouter = NULL;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Add a vif to the vif table
+ */
+static int
+add_vif(vifcp)
+ register struct vifctl *vifcp;
+{
+ register struct vif *vifp = viftable + vifcp->vifc_vifi;
+ register struct ifaddr *ifa;
+ register struct ifnet *ifp;
+ struct ifreq ifr;
+ register int error, s;
+ static struct sockaddr_in sin = { sizeof(sin), AF_INET };
+
+ if (vifcp->vifc_vifi >= MAXVIFS)
+ return (EINVAL);
+ if (vifp->v_lcl_addr.s_addr != 0)
+ return (EADDRINUSE);
+
+ /* Find the interface with an address in AF_INET family */
+ sin.sin_addr = vifcp->vifc_lcl_addr;
+ ifa = ifa_ifwithaddr((struct sockaddr *)&sin);
+ if (ifa == 0)
+ return (EADDRNOTAVAIL);
+
+ s = splnet();
+
+ if (vifcp->vifc_flags & VIFF_TUNNEL)
+ vifp->v_rmt_addr = vifcp->vifc_rmt_addr;
+ else {
+ /* Make sure the interface supports multicast */
+ ifp = ifa->ifa_ifp;
+ if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ splx(s);
+ return (EOPNOTSUPP);
+ }
+ /*
+ * Enable promiscuous reception of all IP multicasts
+ * from the interface.
+ */
+ satosin(&ifr.ifr_addr)->sin_family = AF_INET;
+ satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY;
+ error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr);
+ if (error) {
+ splx(s);
+ return (error);
+ }
+ }
+
+ vifp->v_flags = vifcp->vifc_flags;
+ vifp->v_threshold = vifcp->vifc_threshold;
+ vifp->v_lcl_addr = vifcp->vifc_lcl_addr;
+ vifp->v_ifp = ifa->ifa_ifp;
+
+ /* Adjust numvifs up if the vifi is higher than numvifs */
+ if (numvifs <= vifcp->vifc_vifi)
+ numvifs = vifcp->vifc_vifi + 1;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Delete a vif from the vif table
+ */
+static int
+del_vif(vifip)
+ register vifi_t *vifip;
+{
+ register struct vif *vifp = viftable + *vifip;
+ register struct ifnet *ifp;
+ register int i, s;
+ struct ifreq ifr;
+
+ if (*vifip >= numvifs)
+ return (EINVAL);
+ if (vifp->v_lcl_addr.s_addr == 0)
+ return (EADDRNOTAVAIL);
+
+ s = splnet();
+
+ if (!(vifp->v_flags & VIFF_TUNNEL)) {
+ if (vifp->v_lcl_grps)
+ free(vifp->v_lcl_grps, M_MRTABLE);
+ satosin(&ifr.ifr_addr)->sin_family = AF_INET;
+ satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY;
+ ifp = vifp->v_ifp;
+ (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
+ }
+
+ bzero((caddr_t)vifp, sizeof (*vifp));
+
+ /* Adjust numvifs down */
+ for (i = numvifs - 1; i >= 0; i--)
+ if (viftable[i].v_lcl_addr.s_addr != 0)
+ break;
+ numvifs = i + 1;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Add the multicast group in the lgrpctl to the list of local multicast
+ * group memberships associated with the vif indexed by gcp->lgc_vifi.
+ */
+static int
+add_lgrp(gcp)
+ register struct lgrplctl *gcp;
+{
+ register struct vif *vifp;
+ register int s;
+
+ if (gcp->lgc_vifi >= numvifs)
+ return (EINVAL);
+
+ vifp = viftable + gcp->lgc_vifi;
+ if (vifp->v_lcl_addr.s_addr == 0 || (vifp->v_flags & VIFF_TUNNEL))
+ return (EADDRNOTAVAIL);
+
+ /* If not enough space in existing list, allocate a larger one */
+ s = splnet();
+ if (vifp->v_lcl_grps_n + 1 >= vifp->v_lcl_grps_max) {
+ register int num;
+ register struct in_addr *ip;
+
+ num = vifp->v_lcl_grps_max;
+ if (num <= 0)
+ num = 32; /* initial number */
+ else
+ num += num; /* double last number */
+ ip = (struct in_addr *)malloc(num * sizeof(*ip),
+ M_MRTABLE, M_NOWAIT);
+ if (ip == NULL) {
+ splx(s);
+ return (ENOBUFS);
+ }
+
+ bzero((caddr_t)ip, num * sizeof(*ip)); /* XXX paranoid */
+ bcopy((caddr_t)vifp->v_lcl_grps, (caddr_t)ip,
+ vifp->v_lcl_grps_n * sizeof(*ip));
+
+ vifp->v_lcl_grps_max = num;
+ if (vifp->v_lcl_grps)
+ free(vifp->v_lcl_grps, M_MRTABLE);
+ vifp->v_lcl_grps = ip;
+
+ splx(s);
+ }
+
+ vifp->v_lcl_grps[vifp->v_lcl_grps_n++] = gcp->lgc_gaddr;
+
+ if (gcp->lgc_gaddr.s_addr == vifp->v_cached_group)
+ vifp->v_cached_result = 1;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Delete the the local multicast group associated with the vif
+ * indexed by gcp->lgc_vifi.
+ */
+
+static int
+del_lgrp(gcp)
+ register struct lgrplctl *gcp;
+{
+ register struct vif *vifp;
+ register int i, error, s;
+
+ if (gcp->lgc_vifi >= numvifs)
+ return (EINVAL);
+ vifp = viftable + gcp->lgc_vifi;
+ if (vifp->v_lcl_addr.s_addr == 0 || (vifp->v_flags & VIFF_TUNNEL))
+ return (EADDRNOTAVAIL);
+
+ s = splnet();
+
+ if (gcp->lgc_gaddr.s_addr == vifp->v_cached_group)
+ vifp->v_cached_result = 0;
+
+ error = EADDRNOTAVAIL;
+ for (i = 0; i < vifp->v_lcl_grps_n; ++i)
+ if (same(&gcp->lgc_gaddr, &vifp->v_lcl_grps[i])) {
+ error = 0;
+ vifp->v_lcl_grps_n--;
+ bcopy((caddr_t)&vifp->v_lcl_grps[i + 1],
+ (caddr_t)&vifp->v_lcl_grps[i],
+ (vifp->v_lcl_grps_n - i) * sizeof(struct in_addr));
+ error = 0;
+ break;
+ }
+
+ splx(s);
+ return (error);
+}
+
+/*
+ * Return 1 if gaddr is a member of the local group list for vifp.
+ */
+static int
+grplst_member(vifp, gaddr)
+ register struct vif *vifp;
+ struct in_addr gaddr;
+{
+ register int i, s;
+ register u_long addr;
+
+ mrtstat.mrts_grp_lookups++;
+
+ addr = gaddr.s_addr;
+ if (addr == vifp->v_cached_group)
+ return (vifp->v_cached_result);
+
+ mrtstat.mrts_grp_misses++;
+
+ for (i = 0; i < vifp->v_lcl_grps_n; ++i)
+ if (addr == vifp->v_lcl_grps[i].s_addr) {
+ s = splnet();
+ vifp->v_cached_group = addr;
+ vifp->v_cached_result = 1;
+ splx(s);
+ return (1);
+ }
+ s = splnet();
+ vifp->v_cached_group = addr;
+ vifp->v_cached_result = 0;
+ splx(s);
+ return (0);
+}
+
+/*
+ * A simple hash function: returns MRTHASHMOD of the low-order octet of
+ * the argument's network or subnet number.
+ */
+static u_long
+nethash(in)
+ struct in_addr in;
+{
+ register u_long n;
+
+ n = in_netof(in);
+ while ((n & 0xff) == 0)
+ n >>= 8;
+ return (MRTHASHMOD(n));
+}
+
+/*
+ * Add an mrt entry
+ */
+static int
+add_mrt(mrtcp)
+ register struct mrtctl *mrtcp;
+{
+ struct mrt *rt;
+ u_long hash;
+ int s;
+
+ if (rt = mrtfind(mrtcp->mrtc_origin)) {
+ /* Just update the route */
+ s = splnet();
+ rt->mrt_parent = mrtcp->mrtc_parent;
+ VIFM_COPY(mrtcp->mrtc_children, rt->mrt_children);
+ VIFM_COPY(mrtcp->mrtc_leaves, rt->mrt_leaves);
+ splx(s);
+ return (0);
+ }
+
+ s = splnet();
+
+ rt = (struct mrt *)malloc(sizeof(*rt), M_MRTABLE, M_NOWAIT);
+ if (rt == NULL) {
+ splx(s);
+ return (ENOBUFS);
+ }
+
+ /*
+ * insert new entry at head of hash chain
+ */
+ rt->mrt_origin = mrtcp->mrtc_origin;
+ rt->mrt_originmask = mrtcp->mrtc_originmask;
+ rt->mrt_parent = mrtcp->mrtc_parent;
+ VIFM_COPY(mrtcp->mrtc_children, rt->mrt_children);
+ VIFM_COPY(mrtcp->mrtc_leaves, rt->mrt_leaves);
+ /* link into table */
+ hash = nethash(mrtcp->mrtc_origin);
+ rt->mrt_next = mrttable[hash];
+ mrttable[hash] = rt;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Delete an mrt entry
+ */
+static int
+del_mrt(origin)
+ register struct in_addr *origin;
+{
+ register struct mrt *rt, *prev_rt;
+ register u_long hash = nethash(*origin);
+ register int s;
+
+ for (prev_rt = rt = mrttable[hash]; rt; prev_rt = rt, rt = rt->mrt_next)
+ if (origin->s_addr == rt->mrt_origin.s_addr)
+ break;
+ if (!rt)
+ return (ESRCH);
+
+ s = splnet();
+
+ if (rt == cached_mrt)
+ cached_mrt = NULL;
+
+ if (prev_rt == rt)
+ mrttable[hash] = rt->mrt_next;
+ else
+ prev_rt->mrt_next = rt->mrt_next;
+ free(rt, M_MRTABLE);
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Find a route for a given origin IP address.
+ */
+static struct mrt *
+mrtfind(origin)
+ struct in_addr origin;
+{
+ register struct mrt *rt;
+ register u_int hash;
+ register int s;
+
+ mrtstat.mrts_mrt_lookups++;
+
+ if (cached_mrt != NULL &&
+ (origin.s_addr & cached_originmask) == cached_origin)
+ return (cached_mrt);
+
+ mrtstat.mrts_mrt_misses++;
+
+ hash = nethash(origin);
+ for (rt = mrttable[hash]; rt; rt = rt->mrt_next)
+ if ((origin.s_addr & rt->mrt_originmask.s_addr) ==
+ rt->mrt_origin.s_addr) {
+ s = splnet();
+ cached_mrt = rt;
+ cached_origin = rt->mrt_origin.s_addr;
+ cached_originmask = rt->mrt_originmask.s_addr;
+ splx(s);
+ return (rt);
+ }
+ return (NULL);
+}
+
+/*
+ * IP multicast forwarding function. This function assumes that the packet
+ * pointed to by "ip" has arrived on (or is about to be sent to) the interface
+ * pointed to by "ifp", and the packet is to be relayed to other networks
+ * that have members of the packet's destination IP multicast group.
+ *
+ * The packet is returned unscathed to the caller, unless it is tunneled
+ * or erroneous, in which case a non-zero return value tells the caller to
+ * discard it.
+ */
+
+#define IP_HDR_LEN 20 /* # bytes of fixed IP header (excluding options) */
+#define TUNNEL_LEN 12 /* # bytes of IP option for tunnel encapsulation */
+
+int
+ip_mforward(m, ifp)
+ register struct mbuf *m;
+ register struct ifnet *ifp;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register struct mrt *rt;
+ register struct vif *vifp;
+ register int vifi;
+ register u_char *ipoptions;
+ u_long tunnel_src;
+
+ if (ip->ip_hl < (IP_HDR_LEN + TUNNEL_LEN) >> 2 ||
+ (ipoptions = (u_char *)(ip + 1))[1] != IPOPT_LSRR ) {
+ /*
+ * Packet arrived via a physical interface.
+ */
+ tunnel_src = 0;
+ } else {
+ /*
+ * Packet arrived through a tunnel.
+ *
+ * A tunneled packet has a single NOP option and a
+ * two-element loose-source-and-record-route (LSRR)
+ * option immediately following the fixed-size part of
+ * the IP header. At this point in processing, the IP
+ * header should contain the following IP addresses:
+ *
+ * original source - in the source address field
+ * destination group - in the destination address field
+ * remote tunnel end-point - in the first element of LSRR
+ * one of this host's addrs - in the second element of LSRR
+ *
+ * NOTE: RFC-1075 would have the original source and
+ * remote tunnel end-point addresses swapped. However,
+ * that could cause delivery of ICMP error messages to
+ * innocent applications on intermediate routing
+ * hosts! Therefore, we hereby change the spec.
+ */
+
+ /*
+ * Verify that the tunnel options are well-formed.
+ */
+ if (ipoptions[0] != IPOPT_NOP ||
+ ipoptions[2] != 11 || /* LSRR option length */
+ ipoptions[3] != 12 || /* LSRR address pointer */
+ (tunnel_src = *(u_long *)(&ipoptions[4])) == 0) {
+ mrtstat.mrts_bad_tunnel++;
+ return (1);
+ }
+
+ /*
+ * Delete the tunnel options from the packet.
+ */
+ ovbcopy((caddr_t)(ipoptions + TUNNEL_LEN), (caddr_t)ipoptions,
+ (unsigned)(m->m_len - (IP_HDR_LEN + TUNNEL_LEN)));
+ m->m_len -= TUNNEL_LEN;
+ ip->ip_len -= TUNNEL_LEN;
+ ip->ip_hl -= TUNNEL_LEN >> 2;
+ }
+
+ /*
+ * Don't forward a packet with time-to-live of zero or one,
+ * or a packet destined to a local-only group.
+ */
+ if (ip->ip_ttl <= 1 ||
+ ntohl(ip->ip_dst.s_addr) <= INADDR_MAX_LOCAL_GROUP)
+ return ((int)tunnel_src);
+
+ /*
+ * Don't forward if we don't have a route for the packet's origin.
+ */
+ if (!(rt = mrtfind(ip->ip_src))) {
+ mrtstat.mrts_no_route++;
+ return ((int)tunnel_src);
+ }
+
+ /*
+ * Don't forward if it didn't arrive from the parent vif for its origin.
+ */
+ vifi = rt->mrt_parent;
+ if (tunnel_src == 0 ) {
+ if ((viftable[vifi].v_flags & VIFF_TUNNEL) ||
+ viftable[vifi].v_ifp != ifp )
+ return ((int)tunnel_src);
+ } else {
+ if (!(viftable[vifi].v_flags & VIFF_TUNNEL) ||
+ viftable[vifi].v_rmt_addr.s_addr != tunnel_src )
+ return ((int)tunnel_src);
+ }
+
+ /*
+ * For each vif, decide if a copy of the packet should be forwarded.
+ * Forward if:
+ * - the ttl exceeds the vif's threshold AND
+ * - the vif is a child in the origin's route AND
+ * - ( the vif is not a leaf in the origin's route OR
+ * the destination group has members on the vif )
+ *
+ * (This might be speeded up with some sort of cache -- someday.)
+ */
+ for (vifp = viftable, vifi = 0; vifi < numvifs; vifp++, vifi++) {
+ if (ip->ip_ttl > vifp->v_threshold &&
+ VIFM_ISSET(vifi, rt->mrt_children) &&
+ (!VIFM_ISSET(vifi, rt->mrt_leaves) ||
+ grplst_member(vifp, ip->ip_dst))) {
+ if (vifp->v_flags & VIFF_TUNNEL)
+ tunnel_send(m, vifp);
+ else
+ phyint_send(m, vifp);
+ }
+ }
+
+ return ((int)tunnel_src);
+}
+
+static void
+phyint_send(m, vifp)
+ register struct mbuf *m;
+ register struct vif *vifp;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register struct mbuf *mb_copy;
+ register struct ip_moptions *imo;
+ register int error;
+ struct ip_moptions simo;
+
+ mb_copy = m_copy(m, 0, M_COPYALL);
+ if (mb_copy == NULL)
+ return;
+
+ imo = &simo;
+ imo->imo_multicast_ifp = vifp->v_ifp;
+ imo->imo_multicast_ttl = ip->ip_ttl - 1;
+ imo->imo_multicast_loop = 1;
+
+ error = ip_output(mb_copy, NULL, NULL, IP_FORWARDING, imo);
+}
+
+static void
+tunnel_send(m, vifp)
+ register struct mbuf *m;
+ register struct vif *vifp;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register struct mbuf *mb_copy, *mb_opts;
+ register struct ip *ip_copy;
+ register int error;
+ register u_char *cp;
+
+ /*
+ * Make sure that adding the tunnel options won't exceed the
+ * maximum allowed number of option bytes.
+ */
+ if (ip->ip_hl > (60 - TUNNEL_LEN) >> 2) {
+ mrtstat.mrts_cant_tunnel++;
+ return;
+ }
+
+ /*
+ * Get a private copy of the IP header so that changes to some
+ * of the IP fields don't damage the original header, which is
+ * examined later in ip_input.c.
+ */
+ mb_copy = m_copy(m, IP_HDR_LEN, M_COPYALL);
+ if (mb_copy == NULL)
+ return;
+ MGETHDR(mb_opts, M_DONTWAIT, MT_HEADER);
+ if (mb_opts == NULL) {
+ m_freem(mb_copy);
+ return;
+ }
+ /*
+ * Make mb_opts be the new head of the packet chain.
+ * Any options of the packet were left in the old packet chain head
+ */
+ mb_opts->m_next = mb_copy;
+ mb_opts->m_len = IP_HDR_LEN + TUNNEL_LEN;
+ mb_opts->m_data += MSIZE - mb_opts->m_len;
+
+ ip_copy = mtod(mb_opts, struct ip *);
+ /*
+ * Copy the base ip header to the new head mbuf.
+ */
+ *ip_copy = *ip;
+ ip_copy->ip_ttl--;
+ ip_copy->ip_dst = vifp->v_rmt_addr; /* remote tunnel end-point */
+ /*
+ * Adjust the ip header length to account for the tunnel options.
+ */
+ ip_copy->ip_hl += TUNNEL_LEN >> 2;
+ ip_copy->ip_len += TUNNEL_LEN;
+ /*
+ * Add the NOP and LSRR after the base ip header
+ */
+ cp = (u_char *)(ip_copy + 1);
+ *cp++ = IPOPT_NOP;
+ *cp++ = IPOPT_LSRR;
+ *cp++ = 11; /* LSRR option length */
+ *cp++ = 8; /* LSSR pointer to second element */
+ *(u_long*)cp = vifp->v_lcl_addr.s_addr; /* local tunnel end-point */
+ cp += 4;
+ *(u_long*)cp = ip->ip_dst.s_addr; /* destination group */
+
+ error = ip_output(mb_opts, NULL, NULL, IP_FORWARDING, NULL);
+}
+#endif
OpenPOWER on IntegriCloud