aboutsummaryrefslogtreecommitdiffstats
path: root/sys/netinet
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/if_ether.c8
-rw-r--r--sys/netinet/if_ether.h100
-rw-r--r--sys/netinet/igmp.c321
-rw-r--r--sys/netinet/igmp.h60
-rw-r--r--sys/netinet/igmp_var.h80
-rw-r--r--sys/netinet/in.c132
-rw-r--r--sys/netinet/in.h37
-rw-r--r--sys/netinet/in_pcb.c93
-rw-r--r--sys/netinet/in_pcb.h7
-rw-r--r--sys/netinet/in_pcb.old.c503
-rw-r--r--sys/netinet/in_pcb.orig.c498
-rw-r--r--sys/netinet/in_proto.c12
-rw-r--r--sys/netinet/in_var.h117
-rw-r--r--sys/netinet/ip_icmp.c7
-rw-r--r--sys/netinet/ip_input.c53
-rw-r--r--sys/netinet/ip_mroute.c1056
-rw-r--r--sys/netinet/ip_mroute.h174
-rw-r--r--sys/netinet/ip_output.c494
-rw-r--r--sys/netinet/ip_var.h17
-rw-r--r--sys/netinet/raw_ip.c54
-rw-r--r--sys/netinet/tcp_usrreq.c7
-rw-r--r--sys/netinet/udp_usrreq.c104
22 files changed, 3896 insertions, 38 deletions
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c
index ebad56b12dcf..47efad34eaed 100644
--- a/sys/netinet/if_ether.c
+++ b/sys/netinet/if_ether.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)if_ether.c 7.13 (Berkeley) 10/31/90
- * $Id: if_ether.c,v 1.4 1993/11/25 01:34:57 wollman Exp $
+ * $Id: if_ether.c,v 1.5 1994/05/17 22:31:00 jkh Exp $
*/
/*
@@ -197,6 +197,12 @@ arpresolve(ac, m, destip, desten, usetrailers)
sizeof(etherbroadcastaddr));
return (1);
}
+#ifdef MULTICAST
+ if (m->m_flags & M_MCAST) { /* multicast */
+ ETHER_MAP_IP_MULTICAST(destip, desten);
+ return(1);
+ }
+#endif
lna = in_lnaof(*destip);
/* if for us, use software loopback driver if up */
for (ia = in_ifaddr; ia; ia = ia->ia_next)
diff --git a/sys/netinet/if_ether.h b/sys/netinet/if_ether.h
index 787b561e6fdf..fdf595924c43 100644
--- a/sys/netinet/if_ether.h
+++ b/sys/netinet/if_ether.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)if_ether.h 7.5 (Berkeley) 6/28/90
- * $Id: if_ether.h,v 1.4 1993/11/25 01:35:01 wollman Exp $
+ * $Id: if_ether.h,v 1.5 1994/05/17 22:31:02 jkh Exp $
*/
#ifndef _NETINET_IF_ETHER_H_
@@ -61,6 +61,25 @@ struct ether_header {
#define ETHERMTU 1500
#define ETHERMIN (60-14)
+#ifdef KERNEL
+/*
+ * Macro to map an IP multicast address to an Ethernet multicast address.
+ * The high-order 25 bites of the Ethernet address are staticall assigned,
+ * and the low-rder 23 bites are taken from the low end of the IP address.
+ */
+#define ETHER_MAP_IP_MULTICAST(ipaddr, enaddr) \
+ /* struct in_addr *ipaddr; */ \
+ /* u_char enaddr[6]; */ \
+{ \
+ (enaddr)[0] = 0x01; \
+ (enaddr)[1] = 0x00; \
+ (enaddr)[2] = 0x5e; \
+ (enaddr)[3] = ((u_char *)ipaddr)[1] & 0x7f; \
+ (enaddr)[4] = ((u_char *)ipaddr)[2]; \
+ (enaddr)[5] = ((u_char *)ipaddr)[3]; \
+}
+#endif
+
/*
* Ethernet Address Resolution Protocol.
*
@@ -91,8 +110,11 @@ struct arpcom {
struct ifnet ac_if; /* network-visible interface */
u_char ac_enaddr[6]; /* ethernet hardware address */
struct in_addr ac_ipaddr; /* copy of ip address- XXX */
+ struct ether_multi *ac_multiaddrs; /* list of ether m'cast addrs */
+ int ac_multicnt; /* length of ac_multiaddrs list */
};
+
/*
* Internet to ethernet address resolution table.
*/
@@ -106,10 +128,84 @@ struct arptab {
#ifdef KERNEL
extern u_char etherbroadcastaddr[6]; /* defined in net/if_ethersubr.c */
+#if defined(ISO) && !defined(MULTICAST)
+#define MULTICAST 1
+#endif
+#ifdef MULTICAST
+u_char ether_ipmulticast_min[6];
+u_char ether_ipmulticast_max[6];
+#endif
struct arptab *arptnew();
extern void ether_input(struct ifnet *, struct ether_header *, struct mbuf *);
extern char *ether_sprintf(u_char *);
-#endif
+#ifdef MULTICAST
+/*
+ * Ethernet multicast address structure. There is one of these for each
+ * multicast address or range of multicast addresses that we are supposed
+ * to listen to on a particular interface. They are kept in a linked list,
+ * rooted in the interface's arpcom structure. (This really has nothing to
+ * do with ARP, or with the Internet address family, but this appears to be
+ * the minimally-disrupting place to put it.)
+ */
+struct ether_multi {
+ u_char enm_addrlo[6]; /* low or only address of range */
+ u_char enm_addrhi[6]; /* high or only address of range */
+ struct arpcom *enm_ac; /* back point to arpcom */
+ u_int enm_refcount; /* no. claims to this addr/range */
+ struct ether_multi *enm_next; /* ptr to next ether_multi */
+} ;
+
+/*
+ * Structure used by macros below to remember position when stepping through
+ * all of the ether_multi records.
+ */
+struct ether_multistep {
+ struct ether_multi *e_enm;
+};
+
+/*
+ * Macro for looking up the ether_multi record for a given range of Ethernet
+ * multicast addresses connected to a given arpcom structure. If no matching
+ * record is found, "enm" returns NULL.
+ */
+#define ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm) \
+ /* u_char addrlo[6]; */ \
+ /* u_char addrhi[6]; */ \
+ /* struct arpcom *ac; */ \
+ /* struct ether_multi *enm; */ \
+{ \
+ for ((enm) = (ac)->ac_multiaddrs; \
+ (enm) != NULL && \
+ (bcmp((enm)->enm_addrlo, (addrlo), 6) != 0 || \
+ bcmp((enm)->enm_addrhi, (addrhi), 6) != 0); \
+ (enm) = (enm)->enm_next); \
+}
+
+/*
+ * Macro to step through all of the ether_multi records, one at a time.
+ * The current position is remembered in "step", which the caller must
+ * provide. ETHER_FIRST_MULTI(), below, must be called to initialize "step"
+ * and get the first record. Both macros return a NULL "enm" when there
+ * are no remaining records.
+ */
+#define ETHER_NEXT_MULTI(step, enm) \
+ /* struct ether_multistep step; */ \
+ /* struct ether_multi *enm; */ \
+{ \
+ if (((enm) = (step).e_enm) != NULL) \
+ (step).e_enm = (enm)->enm_next; \
+}
+
+#define ETHER_FIRST_MULTI(step, ac, enm) \
+ /* struct ether_multistep step; */ \
+ /* struct arpcom *ac; */ \
+ /* struct ether_multi *enm; */ \
+{ \
+ (step).e_enm = (ac)->ac_multiaddrs; \
+ ETHER_NEXT_MULTI((step), (enm)); \
+}
+#endif /* _MULTICAST */
+#endif /* KERNEL */
#endif /* _NETINET_IF_ETHER_H_ */
diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c
new file mode 100644
index 000000000000..d3bdd933730a
--- /dev/null
+++ b/sys/netinet/igmp.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 1988 Stephen Deering.
+ * Copyright (c) 1992, 1993 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.
+ *
+ * @(#)igmp.c 7.2 (Berkeley) 10/11/92
+ */
+
+/* Internet Group Management Protocol (IGMP) routines. */
+
+#ifdef MULTICAST
+
+#include "param.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "protosw.h"
+
+#include "net/if.h"
+#include "net/route.h"
+
+#include "in.h"
+#include "in_var.h"
+#include "in_systm.h"
+#include "ip.h"
+#include "ip_var.h"
+#include "igmp.h"
+#include "igmp_var.h"
+#include "machine/cpufunc.h"
+
+extern struct ifnet loif;
+
+static int igmp_timers_are_running = 0;
+static u_long igmp_all_hosts_group;
+
+static void igmp_sendreport __P((struct in_multi *));
+
+void
+igmp_init()
+{
+ /*
+ * To avoid byte-swapping the same value over and over again.
+ */
+ igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
+}
+
+void
+igmp_input(m, iphlen)
+ register struct mbuf *m;
+ register int iphlen;
+{
+ register struct igmp *igmp;
+ register struct ip *ip;
+ register int igmplen;
+ register struct ifnet *ifp = m->m_pkthdr.rcvif;
+ register int minlen;
+ register struct in_multi *inm;
+ register struct in_ifaddr *ia;
+ struct in_multistep step;
+
+ ++igmpstat.igps_rcv_total;
+
+ ip = mtod(m, struct ip *);
+ igmplen = ip->ip_len;
+
+ /*
+ * Validate lengths
+ */
+ if (igmplen < IGMP_MINLEN) {
+ ++igmpstat.igps_rcv_tooshort;
+ m_freem(m);
+ return;
+ }
+ minlen = iphlen + IGMP_MINLEN;
+ if ((m->m_flags & M_EXT || m->m_len < minlen) &&
+ (m = m_pullup(m, minlen)) == 0) {
+ ++igmpstat.igps_rcv_tooshort;
+ return;
+ }
+
+ /*
+ * Validate checksum
+ */
+ m->m_data += iphlen;
+ m->m_len -= iphlen;
+ igmp = mtod(m, struct igmp *);
+ if (in_cksum(m, igmplen)) {
+ ++igmpstat.igps_rcv_badsum;
+ m_freem(m);
+ return;
+ }
+ m->m_data -= iphlen;
+ m->m_len += iphlen;
+ ip = mtod(m, struct ip *);
+
+ switch (igmp->igmp_type) {
+
+ case IGMP_HOST_MEMBERSHIP_QUERY:
+ ++igmpstat.igps_rcv_queries;
+
+ if (ifp == &loif)
+ break;
+
+ if (ip->ip_dst.s_addr != igmp_all_hosts_group) {
+ ++igmpstat.igps_rcv_badqueries;
+ m_freem(m);
+ return;
+ }
+
+ /*
+ * Start the timers in all of our membership records for
+ * the interface on which the query arrived, except those
+ * that are already running and those that belong to the
+ * "all-hosts" group.
+ */
+ IN_FIRST_MULTI(step, inm);
+ while (inm != NULL) {
+ if (inm->inm_ifp == ifp && inm->inm_timer == 0 &&
+ inm->inm_addr.s_addr != igmp_all_hosts_group) {
+ inm->inm_timer =
+ IGMP_RANDOM_DELAY(inm->inm_addr);
+ igmp_timers_are_running = 1;
+ }
+ IN_NEXT_MULTI(step, inm);
+ }
+
+ break;
+
+ case IGMP_HOST_MEMBERSHIP_REPORT:
+ ++igmpstat.igps_rcv_reports;
+
+ if (ifp == &loif)
+ break;
+
+ if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
+ igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
+ ++igmpstat.igps_rcv_badreports;
+ m_freem(m);
+ return;
+ }
+
+ /*
+ * KLUDGE: if the IP source address of the report has an
+ * unspecified (i.e., zero) subnet number, as is allowed for
+ * a booting host, replace it with the correct subnet number
+ * so that a process-level multicast routing demon can
+ * determine which subnet it arrived from. This is necessary
+ * to compensate for the lack of any way for a process to
+ * determine the arrival interface of an incoming packet.
+ */
+ if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) {
+ IFP_TO_IA(ifp, ia);
+ if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
+ }
+
+ /*
+ * If we belong to the group being reported, stop
+ * our timer for that group.
+ */
+ IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
+ if (inm != NULL) {
+ inm->inm_timer = 0;
+ ++igmpstat.igps_rcv_ourreports;
+ }
+
+ break;
+ }
+
+ /*
+ * Pass all valid IGMP packets up to any process(es) listening
+ * on a raw IGMP socket.
+ */
+ rip_input(m);
+}
+
+void
+igmp_joingroup(inm)
+ struct in_multi *inm;
+{
+ register int s = splnet();
+
+ if (inm->inm_addr.s_addr == igmp_all_hosts_group ||
+ inm->inm_ifp == &loif)
+ inm->inm_timer = 0;
+ else {
+ igmp_sendreport(inm);
+ inm->inm_timer = IGMP_RANDOM_DELAY(inm->inm_addr);
+ igmp_timers_are_running = 1;
+ }
+ splx(s);
+}
+
+void
+igmp_leavegroup(inm)
+ struct in_multi *inm;
+{
+ /*
+ * No action required on leaving a group.
+ */
+}
+
+void
+igmp_fasttimo()
+{
+ register struct in_multi *inm;
+ register int s;
+ struct in_multistep step;
+
+ /*
+ * Quick check to see if any work needs to be done, in order
+ * to minimize the overhead of fasttimo processing.
+ */
+ if (!igmp_timers_are_running)
+ return;
+
+ s = splnet();
+ igmp_timers_are_running = 0;
+ IN_FIRST_MULTI(step, inm);
+ while (inm != NULL) {
+ if (inm->inm_timer == 0) {
+ /* do nothing */
+ } else if (--inm->inm_timer == 0) {
+ igmp_sendreport(inm);
+ } else {
+ igmp_timers_are_running = 1;
+ }
+ IN_NEXT_MULTI(step, inm);
+ }
+ splx(s);
+}
+
+static void
+igmp_sendreport(inm)
+ register struct in_multi *inm;
+{
+ register struct mbuf *m;
+ register struct igmp *igmp;
+ register struct ip *ip;
+ register struct ip_moptions *imo;
+ struct ip_moptions simo;
+ extern struct socket *ip_mrouter;
+
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ return;
+ /*
+ * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
+ * is smaller than mbuf size returned by MGETHDR.
+ */
+ m->m_data += max_linkhdr;
+ m->m_len = sizeof(struct ip) + IGMP_MINLEN;
+ m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
+
+ ip = mtod(m, struct ip *);
+ ip->ip_tos = 0;
+ ip->ip_len = sizeof(struct ip) + IGMP_MINLEN;
+ ip->ip_off = 0;
+ ip->ip_p = IPPROTO_IGMP;
+ ip->ip_src.s_addr = INADDR_ANY;
+ ip->ip_dst = inm->inm_addr;
+
+ m->m_data += sizeof(struct ip);
+ m->m_len -= sizeof(struct ip);
+ igmp = mtod(m, struct igmp *);
+ igmp->igmp_type = IGMP_HOST_MEMBERSHIP_REPORT;
+ igmp->igmp_code = 0;
+ igmp->igmp_group = inm->inm_addr;
+ igmp->igmp_cksum = 0;
+ igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
+ m->m_data -= sizeof(struct ip);
+ m->m_len += sizeof(struct ip);
+
+ imo = &simo;
+ bzero((caddr_t)imo, sizeof(*imo));
+ imo->imo_multicast_ifp = inm->inm_ifp;
+ imo->imo_multicast_ttl = 1;
+ /*
+ * Request loopback of the report if we are acting as a multicast
+ * router, so that the process-level routing demon can hear it.
+ */
+#ifdef MROUTING
+ imo->imo_multicast_loop = (ip_mrouter != NULL);
+#else
+ imo->imo_multicast_loop = 0;
+#endif
+
+ ip_output(m, NULL, NULL, IP_MULTICASTOPTS, imo);
+
+ ++igmpstat.igps_snd_reports;
+}
+#endif
diff --git a/sys/netinet/igmp.h b/sys/netinet/igmp.h
new file mode 100644
index 000000000000..176941914de7
--- /dev/null
+++ b/sys/netinet/igmp.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1988 Stephen Deering.
+ * Copyright (c) 1992 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.
+ *
+ * @(#)igmp.h 7.1 (Berkeley) 7/8/92
+ */
+
+/* Internet Group Management Protocol (IGMP) definitions. */
+
+/*
+ * IGMP packet format.
+ */
+struct igmp {
+ u_char igmp_type; /* version & type of IGMP message */
+ u_char igmp_code; /* unused, should be zero */
+ u_short igmp_cksum; /* IP-style checksum */
+ struct in_addr igmp_group; /* group address being reported */
+}; /* (zero for queries) */
+
+#define IGMP_MINLEN 8
+
+#define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* message types, incl. version */
+#define IGMP_HOST_MEMBERSHIP_REPORT 0x12
+#define IGMP_DVMRP 0x13 /* for experimental multicast */
+ /* routing protocol */
+
+#define IGMP_MAX_HOST_REPORT_DELAY 10 /* max delay for response to */
+ /* query (in seconds) */
diff --git a/sys/netinet/igmp_var.h b/sys/netinet/igmp_var.h
new file mode 100644
index 000000000000..01e967f99ea2
--- /dev/null
+++ b/sys/netinet/igmp_var.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1988 Stephen Deering.
+ * Copyright (c) 1992 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.
+ *
+ * @(#)igmp_var.h 7.1 (Berkeley) 7/8/92
+ */
+
+/*
+ * Internet Group Management Protocol (IGMP),
+ * implementation-specific definitions.
+ *
+ * Written by Steve Deering, Stanford, May 1988.
+ *
+ * MULTICAST 1.1
+ */
+
+struct igmpstat {
+ u_int igps_rcv_total; /* total IGMP messages received */
+ u_int igps_rcv_tooshort; /* received with too few bytes */
+ u_int igps_rcv_badsum; /* received with bad checksum */
+ u_int igps_rcv_queries; /* received membership queries */
+ u_int igps_rcv_badqueries; /* received invalid queries */
+ u_int igps_rcv_reports; /* received membership reports */
+ u_int igps_rcv_badreports; /* received invalid reports */
+ u_int igps_rcv_ourreports; /* received reports for our groups */
+ u_int igps_snd_reports; /* sent membership reports */
+};
+
+#ifdef KERNEL
+struct igmpstat igmpstat;
+
+/*
+ * Macro to compute a random timer value between 1 and (IGMP_MAX_REPORTING_
+ * DELAY * countdown frequency). We assume that the routine random()
+ * is defined somewhere (and that it returns a positive number).
+ */
+#define IGMP_RANDOM_DELAY(multiaddr) \
+ /* struct in_addr multiaddr; */ \
+ (random() % (IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ) + 1)
+
+
+void igmp_init __P(());
+void igmp_input __P((struct mbuf *, int));
+void igmp_joingroup __P((struct in_multi *));
+void igmp_leavegroup __P((struct in_multi *));
+void igmp_fasttimo __P(());
+#endif
+
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
index 2c662e2d6336..46f423b71bbd 100644
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)in.c 7.17 (Berkeley) 4/20/91
- * $Id: in.c,v 1.8 1994/01/15 14:29:21 davidg Exp $
+ * $Id: in.c,v 1.9 1994/05/17 22:31:03 jkh Exp $
*/
#include "param.h"
@@ -93,6 +93,10 @@ in_netof(in)
net = i & IN_CLASSB_NET;
else if (IN_CLASSC(i))
net = i & IN_CLASSC_NET;
+#ifdef MULTICAST
+ else if (IN_CLASSD(i))
+ net = i & IN_CLASSD_NET;
+#endif
else
return (0);
@@ -174,6 +178,11 @@ in_lnaof(in)
} else if (IN_CLASSC(i)) {
net = i & IN_CLASSC_NET;
host = i & IN_CLASSC_HOST;
+#ifdef MULTICAST
+ } else if (IN_CLASSD(i)) {
+ net = i & IN_CLASSD_NET;
+ host = i & IN_CLASSD_HOST;
+#endif
} else
return (i);
@@ -557,6 +566,18 @@ in_ifinit(ifp, ia, sin, scrub)
}
if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0)
ia->ia_flags |= IFA_ROUTE;
+#ifdef MULTICAST
+ /*
+ * If the interface supports multicast, join the "all hosts"
+ * multicast group on that interface.
+ */
+ if (ifp->if_flags & IFF_MULTICAST) {
+ struct in_addr addr;
+
+ addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+ in_addmulti(&addr, ifp);
+ }
+#endif
return (error);
}
@@ -603,4 +624,113 @@ in_broadcast(in)
return (1);
return (0);
}
+
+#ifdef MULTICAST
+/*
+ * Add an address to the list of IP multicast addresses for a given interface.
+ */
+struct in_multi *
+in_addmulti(ap, ifp)
+ register struct in_addr *ap;
+ register struct ifnet *ifp;
+{
+ register struct in_multi *inm;
+ struct ifreq ifr;
+ struct in_ifaddr *ia;
+ int s = splnet();
+
+ /*
+ * See if address already in list.
+ */
+ IN_LOOKUP_MULTI(*ap, ifp, inm);
+ if (inm != NULL) {
+ /*
+ * Found it; just increment the reference count.
+ */
+ ++inm->inm_refcount;
+ }
+ else {
+ /*
+ * New address; allocate a new multicast record
+ * and link it into the interface's multicast list.
+ */
+ inm = (struct in_multi *)malloc(sizeof(*inm),
+ M_IPMADDR, M_NOWAIT);
+ if (inm == NULL) {
+ splx(s);
+ return (NULL);
+ }
+ inm->inm_addr = *ap;
+ inm->inm_ifp = ifp;
+ inm->inm_refcount = 1;
+ IFP_TO_IA(ifp, ia);
+ if (ia == NULL) {
+ free(inm, M_IPMADDR);
+ splx(s);
+ return (NULL);
+ }
+ inm->inm_ia = ia;
+ inm->inm_next = ia->ia_multiaddrs;
+ ia->ia_multiaddrs = inm;
+ /*
+ * Ask the network driver to update its multicast reception
+ * filter appropriately for the new address.
+ */
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr = *ap;
+ if (ifp->if_ioctl == NULL ||
+ (*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) {
+ ia->ia_multiaddrs = inm->inm_next;
+ free(inm, M_IPMADDR);
+ splx(s);
+ return (NULL);
+ }
+ /*
+ * Let IGMP know that we have joined a new IP multicast group.
+ */
+ igmp_joingroup(inm);
+ }
+ splx(s);
+ return (inm);
+}
+
+/*
+ * Delete a multicast address record.
+ */
+int
+in_delmulti(inm)
+ register struct in_multi *inm;
+{
+ register struct in_multi **p;
+ struct ifreq ifr;
+ int s = splnet();
+
+ if (--inm->inm_refcount == 0) {
+ /*
+ * No remaining claims to this record; let IGMP know that
+ * we are leaving the multicast group.
+ */
+ igmp_leavegroup(inm);
+ /*
+ * Unlink from list.
+ */
+ for (p = &inm->inm_ia->ia_multiaddrs;
+ *p != inm;
+ p = &(*p)->inm_next)
+ continue;
+ *p = (*p)->inm_next;
+ /*
+ * Notify the network driver to update its multicast reception
+ * filter.
+ */
+ ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_family = AF_INET;
+ ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr =
+ inm->inm_addr;
+ (*inm->inm_ifp->if_ioctl)(inm->inm_ifp, SIOCDELMULTI,
+ (caddr_t)&ifr);
+ free(inm, M_IPMADDR);
+ }
+ splx(s);
+}
+#endif
#endif
diff --git a/sys/netinet/in.h b/sys/netinet/in.h
index 48f24b39ede4..96a9f88cea77 100644
--- a/sys/netinet/in.h
+++ b/sys/netinet/in.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)in.h 7.11 (Berkeley) 4/20/91
- * $Id: in.h,v 1.4 1993/12/19 00:52:35 wollman Exp $
+ * $Id: in.h,v 1.8 1994/05/26 22:42:14 jkh Exp $
*/
#ifndef _NETINET_IN_H_
@@ -47,6 +47,7 @@
*/
#define IPPROTO_IP 0 /* dummy for IP */
#define IPPROTO_ICMP 1 /* control message protocol */
+#define IPPROTO_IGMP 2 /* ground control protocol */
#define IPPROTO_GGP 3 /* gateway^2 (deprecated) */
#define IPPROTO_TCP 6 /* tcp */
#define IPPROTO_EGP 8 /* exterior gateway protocol */
@@ -100,20 +101,37 @@ struct in_addr {
#define IN_CLASSC_HOST 0x000000ffUL
#define IN_CLASSD(i) (((u_long)(i) & 0xf0000000UL) == 0xe0000000UL)
+#define IN_CLASSD_NET 0xf0000000UL /* These ones aren't really */
+#define IN_CLASSD_NSHIFT 28 /* net and host fields, bit */
+#define IN_CLASSD_HOST 0x0fffffffUL /* routing needn't know. */
#define IN_MULTICAST(i) IN_CLASSD(i)
#define IN_EXPERIMENTAL(i) (((u_long)(i) & 0xe0000000UL) == 0xe0000000UL)
#define IN_BADCLASS(i) (((u_long)(i) & 0xf0000000UL) == 0xf0000000UL)
#define INADDR_ANY 0x00000000UL
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK 0x7f000001UL
+#endif
#define INADDR_BROADCAST 0xffffffffUL /* must be masked */
#ifndef KERNEL
#define INADDR_NONE 0xffffffffUL /* -1 return */
#endif
+#define INADDR_UNSPEC_GROUP 0xe0000000UL /* 224.0.0.0 */
+#define INADDR_ALLHOSTS_GROUP 0xe0000001UL /* 244.0.0.1 */
+#define INADDR_MAX_LOCAL_GROUP 0xe00000ffUL /* 244.0.0.255 */
+
#define IN_LOOPBACKNET 127 /* official! */
/*
+ * Define a macro to stuff the loopback address into an Internet address.
+ */
+#define IN_SET_LOOPBACK_ADDR(a) { \
+ (a)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); \
+ (a)->sin_family = AF_INET; }
+
+/*
* Socket address, internet style.
*/
struct sockaddr_in {
@@ -148,6 +166,23 @@ struct ip_opts {
#define IP_RECVRETOPTS 6 /* bool; receive IP options for response */
#define IP_RECVDSTADDR 7 /* bool; receive IP dst addr w/datagram */
#define IP_RETOPTS 8 /* ip_opts; set/get IP per-packet options */
+#define IP_MULTICAST_IF 9 /* set/get IP multicast interfcae */
+#define IP_MULTICAST_TTL 10 /* set/get IP multicast timetolive */
+#define IP_MULTICAST_LOOP 11 /* set/get IP m'cast loopback */
+#define IP_ADD_MEMBERSHIP 12 /* add an IP group membership */
+#define IP_DROP_MEMBERSHIP 13 /* drop an IP group membership */
+
+#define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */
+#define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sens if a member */
+#define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */
+
+/*
+ * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
+ */
+struct ip_mreq {
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+} ;
#ifdef KERNEL
/* From in.c: */
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index cc404e6f1510..af5d742f1bac 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)in_pcb.c 7.14 (Berkeley) 4/20/91
- * $Id: in_pcb.c,v 1.5 1993/12/19 00:52:37 wollman Exp $
+ * $Id: in_pcb.c,v 1.6 1994/05/17 22:31:05 jkh Exp $
*/
#include "param.h"
@@ -51,6 +51,9 @@
#include "ip.h"
#include "in_pcb.h"
#include "in_var.h"
+#ifdef MULTICAST
+#include "ip_var.h"
+#endif
int
in_pcballoc(so, head)
@@ -70,6 +73,53 @@ in_pcballoc(so, head)
so->so_pcb = (caddr_t)inp;
return (0);
}
+/*
+ * return 1 if there's a pcb whose addresses 'confict' with the
+ * supplied addresses. Only exact matches (address with address
+ * or wildcard with wildcard) are considered to be in conflict
+ * since in_pcblookup will resolve anything else via 'best match'.
+ */
+int
+in_pcbconflict(head, faddr, laddr, fport, lport)
+ register struct inpcb *head;
+ register u_long faddr, laddr;
+ register u_short fport, lport;
+{
+ register struct inpcb *inp = head;
+
+ while ((inp = inp->inp_next) != head)
+ if (inp->inp_lport == lport &&
+ (fport == 0 || inp->inp_fport == fport) &&
+ (faddr == 0 || inp->inp_faddr.s_addr == faddr) &&
+ (laddr == 0 || inp->inp_laddr.s_addr == laddr))
+ return (1);
+ return (0);
+}
+
+/*
+ * Chose a unique (non-conflicting) local port for the inpcb list
+ * starting at 'head'. (A 'rover' is kept in the lport field of
+ * the list head to make N calls to this routine O(N^2) instead of
+ * O(N^3)). The port will always be
+ * IPPORT_RESERVED <= lport <= IPPORT_USERRESERVED
+ */
+u_short
+in_uniqueport(head, faddr, laddr, fport)
+ register struct inpcb *head;
+ register u_long faddr, laddr;
+ register u_short fport;
+{
+ register u_short lport = head->inp_lport;
+
+ do {
+ ++lport;
+ if (lport < IPPORT_RESERVED || lport > IPPORT_USERRESERVED)
+ lport = IPPORT_RESERVED;
+ } while (in_pcbconflict(head, faddr, laddr, fport, htons(lport)));
+ head->inp_lport = lport;
+ return (htons(lport));
+}
+
int
in_pcbbind(inp, nam)
@@ -107,6 +157,7 @@ in_pcbbind(inp, nam)
if (aport < IPPORT_RESERVED && (so->so_state & SS_PRIV) == 0)
return (EACCES);
/* even GROSSER, but this is the Internet */
+#ifdef notdef
if ((so->so_options & SO_REUSEADDR) == 0 &&
((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
(so->so_options & SO_ACCEPTCONN) == 0))
@@ -114,10 +165,16 @@ in_pcbbind(inp, nam)
if (in_pcblookup(head,
zeroin_addr, 0, sin->sin_addr, lport, wild))
return (EADDRINUSE);
+#else
+ if ((inp->inp_socket->so_options & SO_REUSEADDR) == 0 &&
+ in_pcbconflict(head, 0, sin->sin_addr.s_addr, 0, lport))
+ return (EADDRINUSE);
+#endif
}
inp->inp_laddr = sin->sin_addr;
noname:
if (lport == 0)
+#ifdef notdef
do {
if (head->inp_lport++ < IPPORT_RESERVED ||
head->inp_lport > IPPORT_USERRESERVED)
@@ -125,6 +182,10 @@ noname:
lport = htons(head->inp_lport);
} while (in_pcblookup(head,
zeroin_addr, 0, inp->inp_laddr, lport, 0));
+#else
+ lport = in_uniqueport(head, inp->inp_faddr.s_addr,
+ inp->inp_laddr, inp->inp_fport);
+#endif
inp->inp_lport = lport;
return (0);
}
@@ -148,8 +209,13 @@ in_pcbconnect(inp, nam)
return (EINVAL);
if (sin->sin_family != AF_INET)
return (EAFNOSUPPORT);
+#ifdef MULTICAST
+ if (sin->sin_port == 0 && !IN_MULTICAST(sin->sin_addr.s_addr))
+ return(EADDRNOTAVAIL);
+#else
if (sin->sin_port == 0)
return (EADDRNOTAVAIL);
+#endif
if (in_ifaddr) {
/*
* If the destination address is INADDR_ANY,
@@ -217,6 +283,28 @@ in_pcbconnect(inp, nam)
if (ia == 0)
return (EADDRNOTAVAIL);
}
+#ifdef MULTICAST
+ /*
+ * If the destination address is multicast and an outgoing
+ * interface has been set as a multicast option, use the
+ * address of that interface as our source address.
+ */
+ if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
+ inp->inp_moptions != NULL) {
+ struct ip_moptions *imo;
+ struct ifnet *ifp;
+
+ imo = inp->inp_moptions;
+ if (imo->imo_multicast_ifp != NULL) {
+ ifp = imo->imo_multicast_ifp;
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+ }
+#endif
ifaddr = (struct sockaddr_in *)&ia->ia_addr;
}
if (in_pcblookup(inp->inp_head,
@@ -269,6 +357,9 @@ in_pcbdetach(inp)
(void)m_free(inp->inp_options);
if (inp->inp_route.ro_rt)
rtfree(inp->inp_route.ro_rt);
+#ifdef MULTICAST
+ ip_freemoptions(inp->inp_moptions);
+#endif
remque(inp);
(void) m_free(dtom(inp));
}
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index e4adb614e6d4..dd4527e768d1 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1982, 1986, 1990 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)in_pcb.h 7.6 (Berkeley) 6/28/90
- * $Id: in_pcb.h,v 1.5 1993/11/25 01:35:06 wollman Exp $
+ * $Id: in_pcb.h,v 1.6 1994/05/17 22:31:06 jkh Exp $
*/
#ifndef _NETINET_IN_PCB_H_
@@ -67,6 +67,7 @@ struct inpcb {
/* function to call when MTU may have
* changed */
#endif /* MTUDISC */
+ struct ip_moptions *inp_moptions; /* IP multicast options */
};
/* flags in inp_flags: */
diff --git a/sys/netinet/in_pcb.old.c b/sys/netinet/in_pcb.old.c
new file mode 100644
index 000000000000..1946b9edb34a
--- /dev/null
+++ b/sys/netinet/in_pcb.old.c
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 1982, 1986, 1991 Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * from: @(#)in_pcb.c 7.14 (Berkeley) 4/20/91
+ * $Id: in_pcb.old.c,v 1.1 1994/05/17 23:16:40 jkh Exp $
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "malloc.h"
+#include "mbuf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "ioctl.h"
+
+#include "../net/if.h"
+#include "../net/route.h"
+
+#include "in.h"
+#include "in_systm.h"
+#include "ip.h"
+#include "in_pcb.h"
+#include "in_var.h"
+#ifdef MULTICAST
+#include "ip_var.h"
+#endif
+
+int
+in_pcballoc(so, head)
+ struct socket *so;
+ struct inpcb *head;
+{
+ struct mbuf *m;
+ register struct inpcb *inp;
+
+ m = m_getclr(M_DONTWAIT, MT_PCB);
+ if (m == NULL)
+ return (ENOBUFS);
+ inp = mtod(m, struct inpcb *);
+ inp->inp_head = head;
+ inp->inp_socket = so;
+ insque(inp, head);
+ so->so_pcb = (caddr_t)inp;
+ return (0);
+}
+
+int
+in_pcbbind(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct socket *so = inp->inp_socket;
+ register struct inpcb *head = inp->inp_head;
+ register struct sockaddr_in *sin;
+ u_short lport = 0;
+
+ if (in_ifaddr == 0)
+ return (EADDRNOTAVAIL);
+ if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
+ return (EINVAL);
+ if (nam == 0)
+ goto noname;
+ sin = mtod(nam, struct sockaddr_in *);
+ if (nam->m_len != sizeof (*sin))
+ return (EINVAL);
+ if (sin->sin_addr.s_addr != INADDR_ANY) {
+ int tport = sin->sin_port;
+
+ sin->sin_port = 0; /* yech... */
+ if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
+ return (EADDRNOTAVAIL);
+ sin->sin_port = tport;
+ }
+ lport = sin->sin_port;
+ if (lport) {
+ u_short aport = ntohs(lport);
+ int wild = 0;
+
+ /* GROSS */
+ if (aport < IPPORT_RESERVED && (so->so_state & SS_PRIV) == 0)
+ return (EACCES);
+ /* even GROSSER, but this is the Internet */
+ if ((so->so_options & SO_REUSEADDR) == 0 &&
+ ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
+ (so->so_options & SO_ACCEPTCONN) == 0))
+ wild = INPLOOKUP_WILDCARD;
+ if (in_pcblookup(head,
+ zeroin_addr, 0, sin->sin_addr, lport, wild))
+ return (EADDRINUSE);
+ }
+ inp->inp_laddr = sin->sin_addr;
+noname:
+ if (lport == 0)
+ do {
+ if (head->inp_lport++ < IPPORT_RESERVED ||
+ head->inp_lport > IPPORT_USERRESERVED)
+ head->inp_lport = IPPORT_RESERVED;
+ lport = htons(head->inp_lport);
+ } while (in_pcblookup(head,
+ zeroin_addr, 0, inp->inp_laddr, lport, 0));
+ inp->inp_lport = lport;
+ return (0);
+}
+
+/*
+ * Connect from a socket to a specified address.
+ * Both address and port must be specified in argument sin.
+ * If don't have a local address for this socket yet,
+ * then pick one.
+ */
+int
+in_pcbconnect(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ struct in_ifaddr *ia;
+ struct sockaddr_in *ifaddr = 0;
+ register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
+
+ if (nam->m_len != sizeof (*sin))
+ return (EINVAL);
+ if (sin->sin_family != AF_INET)
+ return (EAFNOSUPPORT);
+#ifdef MULTICAST
+ if (sin->sin_port == 0 && !IN_MULTICAST(sin->sin_addr.s_addr))
+ return (EADDRNOTAVAIL);
+#else
+ if (sin->sin_port == 0)
+ return (EADDRNOTAVAIL);
+#endif
+ if (in_ifaddr) {
+ /*
+ * If the destination address is INADDR_ANY,
+ * use the primary local address.
+ * If the supplied address is INADDR_BROADCAST,
+ * and the primary interface supports broadcast,
+ * choose the broadcast address for that interface.
+ */
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+ if (sin->sin_addr.s_addr == INADDR_ANY)
+ sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
+ else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
+ (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
+ sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
+ }
+ if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ register struct route *ro;
+ struct ifnet *ifp;
+
+ ia = (struct in_ifaddr *)0;
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ ro = &inp->inp_route;
+ if (ro->ro_rt &&
+ (satosin(&ro->ro_dst)->sin_addr.s_addr !=
+ sin->sin_addr.s_addr ||
+ inp->inp_socket->so_options & SO_DONTROUTE)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
+ (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
+ /* No route yet, so try to acquire one */
+ ro->ro_dst.sa_family = AF_INET;
+ ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
+ ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
+ sin->sin_addr;
+ rtalloc(ro);
+ }
+ /*
+ * If we found a route, use the address
+ * corresponding to the outgoing interface
+ * unless it is the loopback (in case a route
+ * to our address on another net goes to loopback).
+ */
+ if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp) &&
+ (ifp->if_flags & IFF_LOOPBACK) == 0)
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0) {
+ int fport = sin->sin_port;
+
+ sin->sin_port = 0;
+ ia = (struct in_ifaddr *)
+ ifa_ifwithdstaddr((struct sockaddr *)sin);
+ sin->sin_port = fport;
+ if (ia == 0)
+ ia = in_iaonnetof(in_netof(sin->sin_addr));
+ if (ia == 0)
+ ia = in_ifaddr;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+#ifdef MULTICAST
+ /*
+ * If the destination address is multicast and an outgoing
+ * interface has been set as a multicast option, use the
+ * address of that interface as our source address.
+ */
+ if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
+ inp->inp_moptions != NULL) {
+ struct ip_moptions *imo;
+ struct ifnet *ifp;
+
+ imo = inp->inp_moptions;
+ if (imo->imo_multicast_ifp != NULL) {
+ ifp = imo->imo_multicast_ifp;
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+ }
+#endif
+ ifaddr = (struct sockaddr_in *)&ia->ia_addr;
+ }
+ if (in_pcblookup(inp->inp_head,
+ sin->sin_addr,
+ sin->sin_port,
+ inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
+ inp->inp_lport,
+ 0))
+ return (EADDRINUSE);
+ if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ if (inp->inp_lport == 0)
+ (void)in_pcbbind(inp, (struct mbuf *)0);
+ inp->inp_laddr = ifaddr->sin_addr;
+ }
+ inp->inp_faddr = sin->sin_addr;
+ inp->inp_fport = sin->sin_port;
+#ifdef MTUDISC
+ /*
+ * If the upper layer asked for PMTU discovery services, see
+ * if we can get an idea of what the MTU should be...
+ */
+ in_pcbmtu(inp);
+#endif /* MTUDISC */
+ return (0);
+}
+
+void
+in_pcbdisconnect(inp)
+ struct inpcb *inp;
+{
+
+ inp->inp_faddr.s_addr = INADDR_ANY;
+ inp->inp_fport = 0;
+#ifdef MTUDISC
+ inp->inp_flags &= ~INP_MTUDISCOVERED;
+#endif
+ if (inp->inp_socket->so_state & SS_NOFDREF)
+ in_pcbdetach(inp);
+}
+
+void
+in_pcbdetach(inp)
+ struct inpcb *inp;
+{
+ struct socket *so = inp->inp_socket;
+
+ so->so_pcb = 0;
+ sofree(so);
+ if (inp->inp_options)
+ (void)m_free(inp->inp_options);
+ if (inp->inp_route.ro_rt)
+ rtfree(inp->inp_route.ro_rt);
+#ifdef MULTICAST
+ ip_freemoptions(inp->inp_moptions);
+#endif
+ remque(inp);
+ (void) m_free(dtom(inp));
+}
+
+void
+in_setsockaddr(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *sin;
+
+ nam->m_len = sizeof (*sin);
+ sin = mtod(nam, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_port = inp->inp_lport;
+ sin->sin_addr = inp->inp_laddr;
+}
+
+void
+in_setpeeraddr(inp, nam)
+ struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *sin;
+
+ nam->m_len = sizeof (*sin);
+ sin = mtod(nam, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_port = inp->inp_fport;
+ sin->sin_addr = inp->inp_faddr;
+}
+
+/*
+ * Pass some notification to all connections of a protocol
+ * associated with address dst. The local address and/or port numbers
+ * may be specified to limit the search. The "usual action" will be
+ * taken, depending on the ctlinput cmd. The caller must filter any
+ * cmds that are uninteresting (e.g., no error in the map).
+ * Call the protocol specific routine (if any) to report
+ * any errors for each matching socket.
+ *
+ * Must be called at splnet.
+ */
+void
+in_pcbnotify(head, dst, fport, laddr, lport, cmd, notify)
+ struct inpcb *head;
+ struct sockaddr *dst;
+ u_short fport, lport;
+ struct in_addr laddr;
+ int cmd;
+ void (*notify)(struct inpcb *, int);
+{
+ register struct inpcb *inp, *oinp;
+ struct in_addr faddr;
+ int errno;
+
+ if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
+ return;
+ faddr = ((struct sockaddr_in *)dst)->sin_addr;
+ if (faddr.s_addr == INADDR_ANY)
+ return;
+
+ /*
+ * Redirects go to all references to the destination,
+ * and use in_rtchange to invalidate the route cache.
+ * Dead host indications: notify all references to the destination.
+ * MTU change indications: same thing.
+ * Otherwise, if we have knowledge of the local port and address,
+ * deliver only to that socket.
+ */
+ if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD
+ || cmd == PRC_MTUCHANGED) {
+ fport = 0;
+ lport = 0;
+ laddr.s_addr = 0;
+ if (cmd != PRC_HOSTDEAD && cmd != PRC_MTUCHANGED)
+ notify = in_rtchange;
+ }
+ errno = inetctlerrmap[cmd];
+ for (inp = head->inp_next; inp != head;) {
+ if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_socket == 0 ||
+ (lport && inp->inp_lport != lport) ||
+ (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
+ (fport && inp->inp_fport != fport)) {
+ inp = inp->inp_next;
+ continue;
+ }
+ oinp = inp;
+ inp = inp->inp_next;
+ if (notify)
+ (*notify)(oinp, errno);
+ }
+}
+
+/*
+ * Check for alternatives when higher level complains
+ * about service problems. For now, invalidate cached
+ * routing information. If the route was created dynamically
+ * (by a redirect), time to try a default gateway again.
+ */
+void
+in_losing(inp)
+ struct inpcb *inp;
+{
+ register struct rtentry *rt;
+
+ if ((rt = inp->inp_route.ro_rt)) {
+ rt_missmsg(RTM_LOSING, &inp->inp_route.ro_dst,
+ rt->rt_gateway, (struct sockaddr *)rt_mask(rt),
+ (struct sockaddr *)0, rt->rt_flags, 0);
+ if (rt->rt_flags & RTF_DYNAMIC)
+ (void) rtrequest(RTM_DELETE, rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags,
+ (struct rtentry **)0);
+ inp->inp_route.ro_rt = 0;
+ rtfree(rt);
+
+#ifdef MTUDISC
+ /*
+ * When doing MTU discovery, we want to find out as
+ * quickly as possible what the MTU of the new route is.
+ */
+ in_pcbmtu(inp);
+#endif /* MTUDISC */
+ }
+}
+
+/*
+ * After a routing change, flush old routing
+ * and allocate a (hopefully) better one.
+ */
+void
+in_rtchange(inp, errno)
+ register struct inpcb *inp;
+ int errno;
+{
+ if (inp->inp_route.ro_rt) {
+ rtfree(inp->inp_route.ro_rt);
+ inp->inp_route.ro_rt = 0;
+#ifdef MTUDISC
+ /*
+ * A new route can be allocated the next time
+ * output is attempted, but make sure to let
+ * MTU discovery know about it.
+ */
+ in_pcbmtu(inp);
+#endif /* MTUDISC */
+ }
+}
+
+struct inpcb *
+in_pcblookup(head, faddr, fport, laddr, lport, flags)
+ struct inpcb *head;
+ struct in_addr faddr, laddr;
+ u_short fport, lport;
+ int flags;
+{
+ register struct inpcb *inp, *match = 0;
+ int matchwild = 3, wildcard;
+
+ for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
+ if (inp->inp_lport != lport)
+ continue;
+ wildcard = 0;
+ if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ if (laddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_laddr.s_addr != laddr.s_addr)
+ continue;
+ } else {
+ if (laddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ if (faddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_fport != fport)
+ continue;
+ } else {
+ if (faddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
+ continue;
+ if (wildcard < matchwild) {
+ match = inp;
+ matchwild = wildcard;
+ if (matchwild == 0)
+ break;
+ }
+ }
+ return (match);
+}
diff --git a/sys/netinet/in_pcb.orig.c b/sys/netinet/in_pcb.orig.c
new file mode 100644
index 000000000000..badedf559fda
--- /dev/null
+++ b/sys/netinet/in_pcb.orig.c
@@ -0,0 +1,498 @@
+/*
+ * Copyright (c) 1982, 1986, 1991 Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * from: @(#)in_pcb.c 7.14 (Berkeley) 4/20/91
+ * $Id: in_pcb.orig.c,v 1.1 1994/05/17 23:16:41 jkh Exp $
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "malloc.h"
+#include "mbuf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "ioctl.h"
+
+#include "../net/if.h"
+#include "../net/route.h"
+
+#include "in.h"
+#include "in_systm.h"
+#include "ip.h"
+#include "in_pcb.h"
+#include "in_var.h"
+#ifdef MULTICAST
+#include "ip_var.h"
+#endif
+
+int
+in_pcballoc(so, head)
+ struct socket *so;
+ struct inpcb *head;
+{
+ struct mbuf *m;
+ register struct inpcb *inp;
+
+ m = m_getclr(M_DONTWAIT, MT_PCB);
+ if (m == NULL)
+ return (ENOBUFS);
+ inp = mtod(m, struct inpcb *);
+ inp->inp_head = head;
+ inp->inp_socket = so;
+ insque(inp, head);
+ so->so_pcb = (caddr_t)inp;
+ return (0);
+}
+
+int
+in_pcbbind(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct socket *so = inp->inp_socket;
+ register struct inpcb *head = inp->inp_head;
+ register struct sockaddr_in *sin;
+ u_short lport = 0;
+
+ if (in_ifaddr == 0)
+ return (EADDRNOTAVAIL);
+ if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
+ return (EINVAL);
+ if (nam == 0)
+ goto noname;
+ sin = mtod(nam, struct sockaddr_in *);
+ if (nam->m_len != sizeof (*sin))
+ return (EINVAL);
+ if (sin->sin_addr.s_addr != INADDR_ANY) {
+ int tport = sin->sin_port;
+
+ sin->sin_port = 0; /* yech... */
+ if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
+ return (EADDRNOTAVAIL);
+ sin->sin_port = tport;
+ }
+ lport = sin->sin_port;
+ if (lport) {
+ u_short aport = ntohs(lport);
+ int wild = 0;
+
+ /* GROSS */
+ if (aport < IPPORT_RESERVED && (so->so_state & SS_PRIV) == 0)
+ return (EACCES);
+ /* even GROSSER, but this is the Internet */
+ if ((so->so_options & SO_REUSEADDR) == 0 &&
+ ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
+ (so->so_options & SO_ACCEPTCONN) == 0))
+ wild = INPLOOKUP_WILDCARD;
+ if (in_pcblookup(head,
+ zeroin_addr, 0, sin->sin_addr, lport, wild))
+ return (EADDRINUSE);
+ }
+ inp->inp_laddr = sin->sin_addr;
+noname:
+ if (lport == 0)
+ do {
+ if (head->inp_lport++ < IPPORT_RESERVED ||
+ head->inp_lport > IPPORT_USERRESERVED)
+ head->inp_lport = IPPORT_RESERVED;
+ lport = htons(head->inp_lport);
+ } while (in_pcblookup(head,
+ zeroin_addr, 0, inp->inp_laddr, lport, 0));
+ inp->inp_lport = lport;
+ return (0);
+}
+
+/*
+ * Connect from a socket to a specified address.
+ * Both address and port must be specified in argument sin.
+ * If don't have a local address for this socket yet,
+ * then pick one.
+ */
+int
+in_pcbconnect(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ struct in_ifaddr *ia;
+ struct sockaddr_in *ifaddr = 0;
+ register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
+
+ if (nam->m_len != sizeof (*sin))
+ return (EINVAL);
+ if (sin->sin_family != AF_INET)
+ return (EAFNOSUPPORT);
+ if (sin->sin_port == 0)
+ return (EADDRNOTAVAIL);
+ if (in_ifaddr) {
+ /*
+ * If the destination address is INADDR_ANY,
+ * use the primary local address.
+ * If the supplied address is INADDR_BROADCAST,
+ * and the primary interface supports broadcast,
+ * choose the broadcast address for that interface.
+ */
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+ if (sin->sin_addr.s_addr == INADDR_ANY)
+ sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
+ else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
+ (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
+ sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
+ }
+ if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ register struct route *ro;
+ struct ifnet *ifp;
+
+ ia = (struct in_ifaddr *)0;
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ ro = &inp->inp_route;
+ if (ro->ro_rt &&
+ (satosin(&ro->ro_dst)->sin_addr.s_addr !=
+ sin->sin_addr.s_addr ||
+ inp->inp_socket->so_options & SO_DONTROUTE)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
+ (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
+ /* No route yet, so try to acquire one */
+ ro->ro_dst.sa_family = AF_INET;
+ ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
+ ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
+ sin->sin_addr;
+ rtalloc(ro);
+ }
+ /*
+ * If we found a route, use the address
+ * corresponding to the outgoing interface
+ * unless it is the loopback (in case a route
+ * to our address on another net goes to loopback).
+ */
+ if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp) &&
+ (ifp->if_flags & IFF_LOOPBACK) == 0)
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0) {
+ int fport = sin->sin_port;
+
+ sin->sin_port = 0;
+ ia = (struct in_ifaddr *)
+ ifa_ifwithdstaddr((struct sockaddr *)sin);
+ sin->sin_port = fport;
+ if (ia == 0)
+ ia = in_iaonnetof(in_netof(sin->sin_addr));
+ if (ia == 0)
+ ia = in_ifaddr;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+#ifdef MULTICAST
+ /*
+ * If the destination address is multicast and an outgoing
+ * interface has been set as a multicast option, use the
+ * address of that interface as our source address.
+ */
+ if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
+ inp->inp_moptions != NULL) {
+ struct ip_moptions *imo;
+ struct ifnet *ifp;
+
+ imo = inp->inp_moptions;
+ if (imo->imo_multicast_ifp != NULL) {
+ ifp = imo->imo_multicast_ifp;
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+ }
+#endif
+ ifaddr = (struct sockaddr_in *)&ia->ia_addr;
+ }
+ if (in_pcblookup(inp->inp_head,
+ sin->sin_addr,
+ sin->sin_port,
+ inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
+ inp->inp_lport,
+ 0))
+ return (EADDRINUSE);
+ if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ if (inp->inp_lport == 0)
+ (void)in_pcbbind(inp, (struct mbuf *)0);
+ inp->inp_laddr = ifaddr->sin_addr;
+ }
+ inp->inp_faddr = sin->sin_addr;
+ inp->inp_fport = sin->sin_port;
+#ifdef MTUDISC
+ /*
+ * If the upper layer asked for PMTU discovery services, see
+ * if we can get an idea of what the MTU should be...
+ */
+ in_pcbmtu(inp);
+#endif /* MTUDISC */
+ return (0);
+}
+
+void
+in_pcbdisconnect(inp)
+ struct inpcb *inp;
+{
+
+ inp->inp_faddr.s_addr = INADDR_ANY;
+ inp->inp_fport = 0;
+#ifdef MTUDISC
+ inp->inp_flags &= ~INP_MTUDISCOVERED;
+#endif
+ if (inp->inp_socket->so_state & SS_NOFDREF)
+ in_pcbdetach(inp);
+}
+
+void
+in_pcbdetach(inp)
+ struct inpcb *inp;
+{
+ struct socket *so = inp->inp_socket;
+
+ so->so_pcb = 0;
+ sofree(so);
+ if (inp->inp_options)
+ (void)m_free(inp->inp_options);
+ if (inp->inp_route.ro_rt)
+ rtfree(inp->inp_route.ro_rt);
+#ifdef MULTICAST
+ ip_freemoptions(inp->inp_moptions);
+#endif
+ remque(inp);
+ (void) m_free(dtom(inp));
+}
+
+void
+in_setsockaddr(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *sin;
+
+ nam->m_len = sizeof (*sin);
+ sin = mtod(nam, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_port = inp->inp_lport;
+ sin->sin_addr = inp->inp_laddr;
+}
+
+void
+in_setpeeraddr(inp, nam)
+ struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *sin;
+
+ nam->m_len = sizeof (*sin);
+ sin = mtod(nam, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_port = inp->inp_fport;
+ sin->sin_addr = inp->inp_faddr;
+}
+
+/*
+ * Pass some notification to all connections of a protocol
+ * associated with address dst. The local address and/or port numbers
+ * may be specified to limit the search. The "usual action" will be
+ * taken, depending on the ctlinput cmd. The caller must filter any
+ * cmds that are uninteresting (e.g., no error in the map).
+ * Call the protocol specific routine (if any) to report
+ * any errors for each matching socket.
+ *
+ * Must be called at splnet.
+ */
+void
+in_pcbnotify(head, dst, fport, laddr, lport, cmd, notify)
+ struct inpcb *head;
+ struct sockaddr *dst;
+ u_short fport, lport;
+ struct in_addr laddr;
+ int cmd;
+ void (*notify)(struct inpcb *, int);
+{
+ register struct inpcb *inp, *oinp;
+ struct in_addr faddr;
+ int errno;
+
+ if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
+ return;
+ faddr = ((struct sockaddr_in *)dst)->sin_addr;
+ if (faddr.s_addr == INADDR_ANY)
+ return;
+
+ /*
+ * Redirects go to all references to the destination,
+ * and use in_rtchange to invalidate the route cache.
+ * Dead host indications: notify all references to the destination.
+ * MTU change indications: same thing.
+ * Otherwise, if we have knowledge of the local port and address,
+ * deliver only to that socket.
+ */
+ if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD
+ || cmd == PRC_MTUCHANGED) {
+ fport = 0;
+ lport = 0;
+ laddr.s_addr = 0;
+ if (cmd != PRC_HOSTDEAD && cmd != PRC_MTUCHANGED)
+ notify = in_rtchange;
+ }
+ errno = inetctlerrmap[cmd];
+ for (inp = head->inp_next; inp != head;) {
+ if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_socket == 0 ||
+ (lport && inp->inp_lport != lport) ||
+ (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
+ (fport && inp->inp_fport != fport)) {
+ inp = inp->inp_next;
+ continue;
+ }
+ oinp = inp;
+ inp = inp->inp_next;
+ if (notify)
+ (*notify)(oinp, errno);
+ }
+}
+
+/*
+ * Check for alternatives when higher level complains
+ * about service problems. For now, invalidate cached
+ * routing information. If the route was created dynamically
+ * (by a redirect), time to try a default gateway again.
+ */
+void
+in_losing(inp)
+ struct inpcb *inp;
+{
+ register struct rtentry *rt;
+
+ if ((rt = inp->inp_route.ro_rt)) {
+ rt_missmsg(RTM_LOSING, &inp->inp_route.ro_dst,
+ rt->rt_gateway, (struct sockaddr *)rt_mask(rt),
+ (struct sockaddr *)0, rt->rt_flags, 0);
+ if (rt->rt_flags & RTF_DYNAMIC)
+ (void) rtrequest(RTM_DELETE, rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags,
+ (struct rtentry **)0);
+ inp->inp_route.ro_rt = 0;
+ rtfree(rt);
+
+#ifdef MTUDISC
+ /*
+ * When doing MTU discovery, we want to find out as
+ * quickly as possible what the MTU of the new route is.
+ */
+ in_pcbmtu(inp);
+#endif /* MTUDISC */
+ }
+}
+
+/*
+ * After a routing change, flush old routing
+ * and allocate a (hopefully) better one.
+ */
+void
+in_rtchange(inp, errno)
+ register struct inpcb *inp;
+ int errno;
+{
+ if (inp->inp_route.ro_rt) {
+ rtfree(inp->inp_route.ro_rt);
+ inp->inp_route.ro_rt = 0;
+#ifdef MTUDISC
+ /*
+ * A new route can be allocated the next time
+ * output is attempted, but make sure to let
+ * MTU discovery know about it.
+ */
+ in_pcbmtu(inp);
+#endif /* MTUDISC */
+ }
+}
+
+struct inpcb *
+in_pcblookup(head, faddr, fport, laddr, lport, flags)
+ struct inpcb *head;
+ struct in_addr faddr, laddr;
+ u_short fport, lport;
+ int flags;
+{
+ register struct inpcb *inp, *match = 0;
+ int matchwild = 3, wildcard;
+
+ for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
+ if (inp->inp_lport != lport)
+ continue;
+ wildcard = 0;
+ if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ if (laddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_laddr.s_addr != laddr.s_addr)
+ continue;
+ } else {
+ if (laddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ if (faddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_fport != fport)
+ continue;
+ } else {
+ if (faddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
+ continue;
+ if (wildcard < matchwild) {
+ match = inp;
+ matchwild = wildcard;
+ if (matchwild == 0)
+ break;
+ }
+ }
+ return (match);
+}
diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c
index dc147f090b51..c94cdd0a101b 100644
--- a/sys/netinet/in_proto.c
+++ b/sys/netinet/in_proto.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)in_proto.c 7.5 (Berkeley) 6/28/90
- * $Id: in_proto.c,v 1.3 1993/12/19 00:52:38 wollman Exp $
+ * $Id: in_proto.c,v 1.4 1994/05/17 22:31:07 jkh Exp $
*/
#include "param.h"
@@ -55,6 +55,9 @@
#include "udp.h"
#include "udp_var.h" /* UDP prototypes */
+#include "igmp.h"
+#include "igmp_var.h" /* IGMP prototypes */
+
#include "tcp.h"
#include "tcp_fsm.h"
#include "tcp_seq.h"
@@ -160,6 +163,13 @@ struct in_protosw inetsw[] = {
eonprotoinit, 0, 0, 0,
},
#endif
+#ifdef MULTICAST
+{ SOCK_RAW, &inetdomain, IPPROTO_IGMP, PR_ATOMIC|PR_ADDR,
+ igmp_input, rip_output, 0, rip_ctloutput,
+ rip_usrreq,
+ igmp_init, igmp_fasttimo, 0, 0,
+},
+#endif
#ifdef NSIP
{ SOCK_RAW, &inetdomain, IPPROTO_IDP, PR_ATOMIC|PR_ADDR,
idpip_input, rip_output, nsip_ctlinput, 0,
diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h
index 9e03a296adb4..70d7a8185f01 100644
--- a/sys/netinet/in_var.h
+++ b/sys/netinet/in_var.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)in_var.h 7.6 (Berkeley) 6/28/90
- * $Id: in_var.h,v 1.6 1993/12/19 21:43:26 wollman Exp $
+ * $Id: in_var.h,v 1.7 1994/05/17 22:31:08 jkh Exp $
*/
#ifndef _NETINET_IN_VAR_H_
@@ -58,6 +58,7 @@ struct in_ifaddr {
struct sockaddr_in ia_dstaddr; /* reserve space for broadcast addr */
#define ia_broadaddr ia_dstaddr
struct sockaddr_in ia_sockmask; /* reserve space for general netmask */
+ struct in_multi *ia_multiaddrs; /* list of multicast addresses */
};
struct in_aliasreq {
@@ -144,5 +145,119 @@ extern int in_routemtu(struct route *);
extern void in_mtureduce(struct in_addr, unsigned);
extern void in_mtutimer(caddr_t, int);
#endif /* MTUDISC */
+
+/*
+ * Macro for finding the interface (ifnet structure) corresponding to one
+ * of our IP addresses.
+ */
+#define INADDR_TO_IFP(addr, ifp) \
+ /* struct in_addr addr; */ \
+ /* struct ifnet *ifp; */ \
+{ \
+ register struct in_ifaddr *ia; \
+\
+ for (ia = in_ifaddr; \
+ ia != NULL && IA_SIN(ia)->sin_addr.s_addr != (addr).s_addr; \
+ ia = ia->ia_next) \
+ continue; \
+ (ifp) = (ia == NULL) ? NULL : ia->ia_ifp; \
+}
+
+/*
+ * Macro for finding the internet address structure (in_ifaddr) corresponding
+ * to a given interface (ifnet structure).
+ */
+#define IFP_TO_IA(ifp, ia) \
+ /* struct ifnet *ifp; */ \
+ /* struct in_ifaddr *ia; */ \
+{ \
+ for ((ia) = in_ifaddr; \
+ (ia) != NULL && (ia)->ia_ifp != (ifp); \
+ (ia) = (ia)->ia_next) \
+ continue; \
+}
+#endif
+
+/*
+ * Internet multicast address structure. There is one of these for each IP
+ * multicast group to which this host belongs on a given network interface.
+ * They are kept in a linked list, rooted in the interface's in_ifaddr
+ * structure.
+ */
+struct in_multi {
+ struct in_addr inm_addr; /* IP multicast address */
+ struct ifnet *inm_ifp; /* back pointer to ifnet */
+ struct in_ifaddr *inm_ia; /* back pointer to in_ifaddr */
+ u_int inm_refcount; /* no. membership claims by sockets */
+ u_int inm_timer; /* IGMP membership report timer */
+ struct in_multi *inm_next; /* ptr to next multicast address */
+};
+
+#ifdef KERNEL
+/*
+ * Structure used by macros below to remember position when stepping through
+ * all of the in_multi records.
+ */
+struct in_multistep {
+ struct in_ifaddr *i_ia;
+ struct in_multi *i_inm;
+};
+
+/*
+ * Macro for looking up the in_multi record for a given IP multicast address
+ * on a given interface. If no matching record is found, "inm" returns NULL.
+ */
+#define IN_LOOKUP_MULTI(addr, ifp, inm) \
+ /* struct in_addr addr; */ \
+ /* struct ifnet *ifp; */ \
+ /* struct in_multi *inm; */ \
+{ \
+ register struct in_ifaddr *ia; \
+\
+ IFP_TO_IA((ifp), ia); \
+ if (ia == NULL) \
+ (inm) = NULL; \
+ else \
+ for ((inm) = ia->ia_multiaddrs; \
+ (inm) != NULL && (inm)->inm_addr.s_addr != (addr).s_addr; \
+ (inm) = inm->inm_next) \
+ continue; \
+}
+
+/*
+ * Macro to step through all of the in_multi records, one at a time.
+ * The current position is remembered in "step", which the caller must
+ * provide. IN_FIRST_MULTI(), below, must be called to initialize "step"
+ * and get the first record. Both macros return a NULL "inm" when there
+ * are no remaining records.
+ */
+#define IN_NEXT_MULTI(step, inm) \
+ /* struct in_multistep step; */ \
+ /* struct in_multi *inm; */ \
+{ \
+ if (((inm) = (step).i_inm) != NULL) \
+ (step).i_inm = (inm)->inm_next; \
+ else \
+ while ((step).i_ia != NULL) { \
+ (inm) = (step).i_ia->ia_multiaddrs; \
+ (step).i_ia = (step).i_ia->ia_next; \
+ if ((inm) != NULL) { \
+ (step).i_inm = (inm)->inm_next; \
+ break; \
+ } \
+ } \
+}
+
+#define IN_FIRST_MULTI(step, inm) \
+ /* struct in_multistep step; */ \
+ /* struct in_multi *inm; */ \
+{ \
+ (step).i_ia = in_ifaddr; \
+ (step).i_inm = NULL; \
+ IN_NEXT_MULTI((step), (inm)); \
+}
+
+struct in_multi *in_addmulti __P((struct in_addr *, struct ifnet *));
+int in_delmulti __P((struct in_multi *));
#endif /* KERNEL */
#endif /* _NETINET_IN_VAR_H_ */
diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c
index ed9b199e1db4..c192ef2d908b 100644
--- a/sys/netinet/ip_icmp.c
+++ b/sys/netinet/ip_icmp.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)ip_icmp.c 7.15 (Berkeley) 4/20/91
- * $Id: ip_icmp.c,v 1.6 1993/12/19 00:52:42 wollman Exp $
+ * $Id: ip_icmp.c,v 1.7 1994/05/17 22:31:09 jkh Exp $
*/
#include "param.h"
@@ -156,6 +156,11 @@ icmp_error(n, type, code, dest, mtu)
icmpstat.icps_oldicmp++;
goto freeit;
}
+#ifdef MULTICAST
+ /* Don't send error in response to a multicast or broadcast packet */
+ if(n->m_flags & (M_MCAST | M_BCAST))
+ goto freeit;
+#endif
/*
* First, formulate icmp message
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index 6c45aed63234..426e757824d5 100644
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)ip_input.c 7.19 (Berkeley) 5/25/91
- * $Id: ip_input.c,v 1.8 1994/01/04 17:47:13 ache Exp $
+ * $Id: ip_input.c,v 1.9 1994/05/17 22:31:10 jkh Exp $
*/
#include "param.h"
@@ -240,6 +240,53 @@ next:
goto ours;
}
}
+#ifdef MULTICAST
+ if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+ struct in_multi *inm;
+#ifdef MROUTING
+ extern struct socket *ip_mrouter;
+
+ if (ip_mrouter) {
+ /*
+ * If we are acting as a multicast router, all
+ * incoming multicast packets are passed to the
+ * kernel-level multicast forwarding function.
+ * The packet is returned (relatively) intact; if
+ * ip_mforward() returns a non-zero value, the packet
+ * must be discarded, else it may be accepted below.
+ *
+ * (The IP ident field is put in the same byte order
+ * as expected when ip_mforward() is called from
+ * ip_output().)
+ */
+ ip->ip_id = htons(ip->ip_id);
+ if (ip_mforward(ip, m->m_pkthdr.rcvif, m) != 0) {
+ m_freem(m);
+ goto next;
+ }
+ ip->ip_id = ntohs(ip->ip_id);
+
+ /*
+ * The process-level routing demon needs to receive
+ * all multicast IGMP packets, whether or not this
+ * host belongs to their destination groups.
+ */
+ if (ip->ip_p == IPPROTO_IGMP)
+ goto ours;
+ }
+#endif
+ /*
+ * See if we belong to the destination multicast group on the
+ * arrival interface.
+ */
+ IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
+ if (inm == NULL) {
+ m_freem(m);
+ goto next;
+ }
+ goto ours;
+ }
+#endif
if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
goto ours;
if (ip->ip_dst.s_addr == INADDR_ANY)
diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c
new file mode 100644
index 000000000000..7ce8361ea3b9
--- /dev/null
+++ b/sys/netinet/ip_mroute.c
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (c) 1989 Stephen Deering
+ * Copyright (c) 1992 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 7.4 (Berkeley) 11/19/92
+ */
+
+/*
+ * 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 "param.h"
+#include "errno.h"
+#include "ioctl.h"
+#include "malloc.h"
+#include "mbuf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "time.h"
+
+#ifndef __FreeBSD__
+#include "net/af.h"
+#else
+#include "systm.h"
+#endif
+#include "net/if.h"
+#include "net/route.h"
+#include "net/raw_cb.h"
+
+#include "in.h"
+#include "in_systm.h"
+#include "ip.h"
+#include "in_pcb.h"
+#include "in_var.h"
+#include "ip_var.h"
+
+#include "igmp.h"
+#include "igmp_var.h"
+#include "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((u_long in));
+static int add_mrt __P((struct mrtctl *));
+static int del_mrt __P((struct in_addr *));
+static struct mrt *mrtfind __P((u_long));
+static void phyint_send __P((struct ip *, struct vif *, struct mbuf *));
+static void srcrt_send __P((struct ip *, struct vif *, struct mbuf *));
+static void encap_send __P((struct ip *, struct vif *, struct mbuf *));
+static void multiencap_decap __P((struct mbuf *, int hlen));
+
+#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;
+
+/*
+ * 'Interfaces' associated with decapsulator (so we can tell
+ * packets that went through it from ones that get reflected
+ * by a broken gateway). These interfaces are never linked into
+ * the system ifnet list & no routes point to them. I.e., packets
+ * can't be sent this way. They only exist as a placeholder for
+ * multicast source verification.
+ */
+struct ifnet multicast_decap_if[MAXVIFS];
+
+#define ENCAP_TTL 64
+#define ENCAP_PROTO 4
+
+/* prototype IP hdr for encapsulated packets */
+struct ip multicast_encap_iphdr = {
+#if defined(ultrix) || defined(i386)
+ sizeof(struct ip) >> 2, IPVERSION,
+#else
+ IPVERSION, sizeof(struct ip) >> 2,
+#endif
+ 0, /* tos */
+ sizeof(struct ip), /* total length */
+ 0, /* id */
+ 0, /* frag offset */
+ ENCAP_TTL, ENCAP_PROTO,
+ 0, /* checksum */
+};
+
+/*
+ * Private variables.
+ */
+static vifi_t numvifs = 0;
+static struct mrt *cached_mrt = NULL;
+static u_long cached_origin;
+static u_long cached_originmask;
+
+static void (*encap_oldrawip)();
+
+/*
+ * one-back cache used by multiencap_decap to locate a tunnel's vif
+ * given a datagram's src ip address.
+ */
+static u_long last_encap_src;
+static struct vif *last_encap_vif;
+
+/*
+ * A simple hash function: returns MRTHASHMOD of the low-order octet of
+ * the argument's network or subnet number.
+ */
+static u_long
+nethash(n)
+ u_long n;
+{
+ struct in_addr in;
+
+ in.s_addr = n;
+ n = in_netof(in);
+ while ((n & 0xff) == 0)
+ n >>= 8;
+ return (MRTHASHMOD(n));
+}
+
+/*
+ * this is a direct-mapped cache used to speed the mapping from a
+ * datagram source address to the associated multicast route. Note
+ * that unlike mrttable, the hash is on IP address, not IP net number.
+ */
+#define MSRCHASHSIZ 1024
+#define MSRCHASH(a) ((((a) >> 20) ^ ((a) >> 10) ^ (a)) & (MSRCHASHSIZ - 1))
+struct mrt *mrtsrchash[MSRCHASHSIZ];
+
+/*
+ * Find a route for a given origin IP address.
+ */
+#define MRTFIND(o, rt) { \
+ register u_int _mrhash = o; \
+ _mrhash = MSRCHASH(_mrhash); \
+ ++mrtstat.mrts_mrt_lookups; \
+ rt = mrtsrchash[_mrhash]; \
+ if (rt == NULL || \
+ (o & rt->mrt_originmask.s_addr) != rt->mrt_origin.s_addr) \
+ if ((rt = mrtfind(o)) != NULL) \
+ mrtsrchash[_mrhash] = rt; \
+}
+
+static struct mrt *
+mrtfind(origin)
+ u_long origin;
+{
+ register struct mrt *rt;
+ register u_int hash;
+
+ mrtstat.mrts_mrt_misses++;
+
+ hash = nethash(origin);
+ for (rt = mrttable[hash]; rt; rt = rt->mrt_next) {
+ if ((origin & rt->mrt_originmask.s_addr) ==
+ rt->mrt_origin.s_addr)
+ return (rt);
+ }
+ return (NULL);
+}
+
+/*
+ * 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));
+ bzero((caddr_t)mrtsrchash, sizeof(mrtsrchash));
+
+ 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);
+ ifp = ifa->ifa_ifp;
+
+ if (vifcp->vifc_flags & VIFF_TUNNEL) {
+ if ((vifcp->vifc_flags & VIFF_SRCRT) == 0) {
+ /*
+ * An encapsulating tunnel is wanted. If we
+ * haven't done so already, put our decap routine
+ * in front of raw_input so we have a chance to
+ * decapsulate incoming packets. Then set the
+ * arrival 'interface' to be the decapsulator.
+ */
+ if (encap_oldrawip == 0) {
+ register int pr = ip_protox[ENCAP_PROTO];
+
+ encap_oldrawip = inetsw[pr].pr_input;
+ inetsw[pr].pr_input = multiencap_decap;
+ for (s = 0; s < MAXVIFS; ++s) {
+ multicast_decap_if[s].if_name =
+ "mdecap";
+ multicast_decap_if[s].if_unit = s;
+ }
+ }
+ ifp = &multicast_decap_if[vifcp->vifc_vifi];
+ } else {
+ ifp = 0;
+ }
+ } else {
+ /* Make sure the interface supports multicast */
+ if ((ifp->if_flags & IFF_MULTICAST) == 0)
+ return EOPNOTSUPP;
+
+ /*
+ * Enable promiscuous reception of all
+ * IP multicasts from the if
+ */
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
+ INADDR_ANY;
+ s = splnet();
+ error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr);
+ splx(s);
+ if (error)
+ return error;
+ }
+
+ s = splnet();
+ 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;
+ vifp->v_rmt_addr = vifcp->vifc_rmt_addr;
+ splx(s);
+
+ /* 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);
+ }
+ if (vifp == last_encap_vif) {
+ last_encap_vif = 0;
+ last_encap_src = 0;
+ }
+ 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;
+ }
+
+ 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;
+ for (; i < vifp->v_lcl_grps_n; ++i)
+ vifp->v_lcl_grps[i] = vifp->v_lcl_grps[i + 1];
+ 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);
+}
+
+/*
+ * 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.s_addr)) {
+ /* 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.s_addr);
+ 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->s_addr);
+ register struct mrt **cmrt, **cmrtend;
+ 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();
+
+ cmrt = mrtsrchash;
+ cmrtend = cmrt + MSRCHASHSIZ;
+ for ( ; cmrt < cmrtend; ++cmrt)
+ if (*cmrt == rt)
+ *cmrt = 0;
+
+ 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);
+}
+
+/*
+ * 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(ip, ifp, m)
+ register struct ip *ip;
+ register struct ifnet *ifp;
+ register struct mbuf *m;
+{
+ 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.
+ */
+ MRTFIND(ip->ip_src.s_addr, rt)
+ if (rt == NULL) {
+ mrtstat.mrts_no_route++;
+ return ((int)tunnel_src);
+ }
+
+ /*
+ * Don't forward if it didn't arrive from the
+ * parent vif for its origin.
+ *
+ * Notes: v_ifp is zero for src route tunnels, multicast_decap_if
+ * for encapsulated tunnels and a real ifnet for non-tunnels so
+ * the first part of the if catches wrong physical interface or
+ * tunnel type; v_rmt_addr is zero for non-tunneled packets so
+ * the 2nd part catches both packets that arrive via a tunnel
+ * that shouldn't and packets that arrive via the wrong tunnel.
+ */
+ vifi = rt->mrt_parent;
+ if (viftable[vifi].v_ifp != ifp ||
+ (ifp == 0 && viftable[vifi].v_rmt_addr.s_addr != tunnel_src)) {
+ /* came in the wrong interface */
+ ++mrtstat.mrts_wrong_if;
+ 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_SRCRT)
+ srcrt_send(ip, vifp, m);
+ else if (vifp->v_flags & VIFF_TUNNEL)
+ encap_send(ip, vifp, m);
+ else
+ phyint_send(ip, vifp, m);
+ }
+ }
+ return ((int)tunnel_src);
+}
+
+static void
+phyint_send(ip, vifp, m)
+ register struct ip *ip;
+ register struct vif *vifp;
+ register struct mbuf *m;
+{
+ 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|IP_MULTICASTOPTS, imo);
+}
+
+static void
+srcrt_send(ip, vifp, m)
+ register struct ip *ip;
+ register struct vif *vifp;
+ register struct mbuf *m;
+{
+ 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;
+ }
+
+ mb_copy = m_copy(m, 0, M_COPYALL);
+ if (mb_copy == NULL)
+ return;
+ ip_copy = mtod(mb_copy, struct 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;
+ MGETHDR(mb_opts, M_DONTWAIT, MT_HEADER);
+ if (mb_opts == NULL) {
+ m_freem(mb_copy);
+ return;
+ }
+ /*
+ * 'Delete' the base ip header from the mb_copy chain
+ */
+ mb_copy->m_len -= IP_HDR_LEN;
+ mb_copy->m_data += IP_HDR_LEN;
+ /*
+ * 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;
+ /*
+ * Copy the base ip header from the mb_copy chain to the new head mbuf
+ */
+ bcopy((caddr_t)ip_copy, mtod(mb_opts, caddr_t), IP_HDR_LEN);
+ /*
+ * Add the NOP and LSRR after the base ip header
+ */
+ cp = mtod(mb_opts, u_char *) + IP_HDR_LEN;
+ *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);
+}
+
+static void
+encap_send(ip, vifp, m)
+ register struct ip *ip;
+ register struct vif *vifp;
+ register struct mbuf *m;
+{
+ register struct mbuf *mb_copy;
+ register struct ip *ip_copy;
+ register int i, len = ip->ip_len;
+
+ /*
+ * copy the old packet & pullup it's IP header into the
+ * new mbuf so we can modify it. Try to fill the new
+ * mbuf since if we don't the ethernet driver will.
+ */
+ MGETHDR(mb_copy, M_DONTWAIT, MT_HEADER);
+ if (mb_copy == NULL)
+ return;
+ mb_copy->m_data += 16;
+ mb_copy->m_len = sizeof(multicast_encap_iphdr);
+ if ((mb_copy->m_next = m_copy(m, 0, M_COPYALL)) == NULL) {
+ m_freem(mb_copy);
+ return;
+ }
+ i = MHLEN - 16;
+ if (i > len)
+ i = len;
+ mb_copy = m_pullup(mb_copy, i);
+ if (mb_copy == NULL)
+ return;
+
+ /*
+ * fill in the encapsulating IP header.
+ */
+ ip_copy = mtod(mb_copy, struct ip *);
+ *ip_copy = multicast_encap_iphdr;
+ ip_copy->ip_id = htons(ip_id++);
+ ip_copy->ip_len += len;
+ ip_copy->ip_src = vifp->v_lcl_addr;
+ ip_copy->ip_dst = vifp->v_rmt_addr;
+
+ /*
+ * turn the encapsulated IP header back into a valid one.
+ */
+ ip = (struct ip *)((caddr_t)ip_copy + sizeof(multicast_encap_iphdr));
+ --ip->ip_ttl;
+ HTONS(ip->ip_len);
+ HTONS(ip->ip_off);
+ ip->ip_sum = 0;
+#if defined(LBL) && !defined(ultrix) && !defined(i386)
+ ip->ip_sum = ~oc_cksum((caddr_t)ip, ip->ip_hl << 2, 0);
+#else
+ mb_copy->m_data += sizeof(multicast_encap_iphdr);
+ ip->ip_sum = in_cksum(mb_copy, ip->ip_hl << 2);
+ mb_copy->m_data -= sizeof(multicast_encap_iphdr);
+#endif
+ ip_output(mb_copy, (struct mbuf *)0, (struct route *)0,
+ IP_FORWARDING, (struct mbuf *)0);
+}
+
+/*
+ * De-encapsulate a packet and feed it back through ip input (this
+ * routine is called whenever IP gets a packet with proto type
+ * ENCAP_PROTO and a local destination address).
+ */
+static void
+multiencap_decap(m, hlen)
+ register struct mbuf *m;
+ int hlen;
+{
+ struct ifnet *ifp;
+ register struct ip *ip = mtod(m, struct ip *);
+ register int s;
+ register struct ifqueue *ifq;
+ register struct vif *vifp;
+
+ if (ip->ip_p != ENCAP_PROTO) {
+ (*encap_oldrawip)(m, hlen);
+ return;
+ }
+ /*
+ * dump the packet if it's not to a multicast destination or if
+ * we don't have an encapsulating tunnel with the source.
+ * Note: This code assumes that the remote site IP address
+ * uniquely identifies the tunnel (i.e., that this site has
+ * at most one tunnel with the remote site).
+ */
+ if (! IN_MULTICAST(ntohl(((struct ip *)((char *)ip + hlen))->ip_dst.s_addr))) {
+ ++mrtstat.mrts_bad_tunnel;
+ m_freem(m);
+ return;
+ }
+ if (ip->ip_src.s_addr != last_encap_src) {
+ register struct vif *vife;
+
+ vifp = viftable;
+ vife = vifp + numvifs;
+ last_encap_src = ip->ip_src.s_addr;
+ last_encap_vif = 0;
+ for ( ; vifp < vife; ++vifp)
+ if (vifp->v_rmt_addr.s_addr == ip->ip_src.s_addr) {
+ if ((vifp->v_flags & (VIFF_TUNNEL|VIFF_SRCRT))
+ == VIFF_TUNNEL)
+ last_encap_vif = vifp;
+ break;
+ }
+ }
+ if ((vifp = last_encap_vif) == 0) {
+ mrtstat.mrts_cant_tunnel++; /*XXX*/
+ m_freem(m);
+ return;
+ }
+ ifp = vifp->v_ifp;
+ m->m_data += hlen;
+ m->m_len -= hlen;
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len -= hlen;
+ ifq = &ipintrq;
+ s = splimp();
+ if (IF_QFULL(ifq)) {
+ IF_DROP(ifq);
+ m_freem(m);
+ } else {
+ IF_ENQUEUE(ifq, m);
+ /*
+ * normally we would need a "schednetisr(NETISR_IP)"
+ * here but we were called by ip_input and it is going
+ * to loop back & try to dequeue the packet we just
+ * queued as soon as we return so we avoid the
+ * unnecessary software interrrupt.
+ */
+ }
+ splx(s);
+}
+#endif
diff --git a/sys/netinet/ip_mroute.h b/sys/netinet/ip_mroute.h
new file mode 100644
index 000000000000..4b3c753998df
--- /dev/null
+++ b/sys/netinet/ip_mroute.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 1989 Stephen Deering.
+ * Copyright (c) 1992 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.h 7.2 (Berkeley) 7/8/92
+ */
+
+/*
+ * Definitions 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.0
+ */
+
+
+/*
+ * DVMRP-specific setsockopt commands.
+ */
+#define DVMRP_INIT 100
+#define DVMRP_DONE 101
+#define DVMRP_ADD_VIF 102
+#define DVMRP_DEL_VIF 103
+#define DVMRP_ADD_LGRP 104
+#define DVMRP_DEL_LGRP 105
+#define DVMRP_ADD_MRT 106
+#define DVMRP_DEL_MRT 107
+
+
+/*
+ * Types and macros for handling bitmaps with one bit per virtual interface.
+ */
+#define MAXVIFS 32
+typedef u_long vifbitmap_t;
+typedef u_short vifi_t; /* type of a vif index */
+
+#define VIFM_SET(n, m) ((m) |= (1 << (n)))
+#define VIFM_CLR(n, m) ((m) &= ~(1 << (n)))
+#define VIFM_ISSET(n, m) ((m) & (1 << (n)))
+#define VIFM_CLRALL(m) ((m) = 0x00000000)
+#define VIFM_COPY(mfrom, mto) ((mto) = (mfrom))
+#define VIFM_SAME(m1, m2) ((m1) == (m2))
+
+
+/*
+ * Agument structure for DVMRP_ADD_VIF.
+ * (DVMRP_DEL_VIF takes a single vifi_t argument.)
+ */
+struct vifctl {
+ vifi_t vifc_vifi; /* the index of the vif to be added */
+ u_char vifc_flags; /* VIFF_ flags defined below */
+ u_char vifc_threshold; /* min ttl required to forward on vif */
+ struct in_addr vifc_lcl_addr; /* local interface address */
+ struct in_addr vifc_rmt_addr; /* remote address (tunnels only) */
+};
+
+#define VIFF_TUNNEL 0x1 /* vif represents a tunnel end-point */
+#define VIFF_SRCRT 0x2 /* tunnel uses IP src routing */
+
+
+/*
+ * Argument structure for DVMRP_ADD_LGRP and DVMRP_DEL_LGRP.
+ */
+struct lgrplctl {
+ vifi_t lgc_vifi;
+ struct in_addr lgc_gaddr;
+};
+
+
+/*
+ * Argument structure for DVMRP_ADD_MRT.
+ * (DVMRP_DEL_MRT takes a single struct in_addr argument, containing origin.)
+ */
+struct mrtctl {
+ struct in_addr mrtc_origin; /* subnet origin of multicasts */
+ struct in_addr mrtc_originmask; /* subnet mask for origin */
+ vifi_t mrtc_parent; /* incoming vif */
+ vifbitmap_t mrtc_children; /* outgoing children vifs */
+ vifbitmap_t mrtc_leaves; /* subset of outgoing children vifs */
+};
+
+
+#ifdef KERNEL
+
+/*
+ * The kernel's virtual-interface structure.
+ */
+struct vif {
+ u_char v_flags; /* VIFF_ flags defined above */
+ u_char v_threshold; /* min ttl required to forward on vif */
+ struct in_addr v_lcl_addr; /* local interface address */
+ struct in_addr v_rmt_addr; /* remote address (tunnels only) */
+ struct ifnet *v_ifp; /* pointer to interface */
+ struct in_addr *v_lcl_grps; /* list of local grps (phyints only) */
+ int v_lcl_grps_max; /* malloc'ed number of v_lcl_grps */
+ int v_lcl_grps_n; /* used number of v_lcl_grps */
+ u_long v_cached_group; /* last grp looked-up (phyints only) */
+ int v_cached_result; /* last look-up result (phyints only) */
+};
+
+/*
+ * The kernel's multicast route structure.
+ */
+struct mrt {
+ struct in_addr mrt_origin; /* subnet origin of multicasts */
+ struct in_addr mrt_originmask; /* subnet mask for origin */
+ vifi_t mrt_parent; /* incoming vif */
+ vifbitmap_t mrt_children; /* outgoing children vifs */
+ vifbitmap_t mrt_leaves; /* subset of outgoing children vifs */
+ struct mrt *mrt_next; /* forward link */
+};
+
+
+#define MRTHASHSIZ 256
+#if (MRTHASHSIZ & (MRTHASHSIZ - 1)) == 0 /* from sys:route.h */
+#define MRTHASHMOD(h) ((h) & (MRTHASHSIZ - 1))
+#else
+#define MRTHASHMOD(h) ((h) % MRTHASHSIZ)
+#endif
+
+/*
+ * The kernel's multicast routing statistics.
+ */
+struct mrtstat {
+ u_long mrts_mrt_lookups; /* # multicast route lookups */
+ u_long mrts_mrt_misses; /* # multicast route cache misses */
+ u_long mrts_grp_lookups; /* # group address lookups */
+ u_long mrts_grp_misses; /* # group address cache misses */
+ u_long mrts_no_route; /* no route for packet's origin */
+ u_long mrts_bad_tunnel; /* malformed tunnel options */
+ u_long mrts_cant_tunnel; /* no room for tunnel options */
+ u_long mrts_wrong_if; /* arrived on wrong interface */
+};
+
+int ip_mrouter_cmd __P((int, struct socket *, struct mbuf *));
+int ip_mrouter_done __P(());
+
+#endif /* KERNEL */
+
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index e9b459e870b8..f1b9975133a8 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)ip_output.c 7.23 (Berkeley) 11/12/90
- * $Id: ip_output.c,v 1.5 1993/12/19 00:52:45 wollman Exp $
+ * $Id: ip_output.c,v 1.7 1994/05/26 22:42:15 jkh Exp $
*/
#include "param.h"
@@ -58,6 +58,7 @@
#endif
struct mbuf *ip_insertoptions();
+void ip_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in *));
/*
* IP output. The packet in mbuf chain m contains a skeletal IP
@@ -66,11 +67,18 @@ struct mbuf *ip_insertoptions();
* The mbuf opt, if present, will not be freed.
*/
int
-ip_output(m0, opt, ro, flags)
+ip_output(m0, opt, ro, flags
+#ifdef MULTICAST
+ , imo
+#endif
+ )
struct mbuf *m0;
struct mbuf *opt;
struct route *ro;
int flags;
+#ifdef MULTICAST
+ struct ip_moptions *imo;
+#endif
{
register struct ip *ip, *mhip;
register struct ifnet *ifp;
@@ -152,6 +160,97 @@ ip_output(m0, opt, ro, flags)
if (ro->ro_rt->rt_flags & RTF_GATEWAY)
dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway;
}
+#ifdef MULTICAST
+ if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+ struct in_multi *inm;
+ extern struct ifnet loif;
+ extern struct socket *ip_mrouter;
+
+ m->m_flags |= M_MCAST;
+ /*
+ * IP destination address is multicast. Make sure "dst"
+ * still points to the address in "ro". (It may have been
+ * changed to point to a gateway address, above.)
+ */
+ dst = (struct sockaddr_in *)&ro->ro_dst;
+ /*
+ * See if the caller provided any multicast options
+ */
+ if ((flags & IP_MULTICASTOPTS) && imo != NULL) {
+ ip->ip_ttl = imo->imo_multicast_ttl;
+ if (imo->imo_multicast_ifp != NULL)
+ ifp = imo->imo_multicast_ifp;
+ } else {
+ imo = NULL;
+ ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL;
+ }
+ /*
+ * Confirm that the outgoing interface supports multicast.
+ */
+ if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = ENETUNREACH;
+ goto bad;
+ }
+ /*
+ * If source address not specified yet, use address
+ * of outgoing interface.
+ */
+ if (ip->ip_src.s_addr == INADDR_ANY) {
+ register struct in_ifaddr *ia;
+
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp) {
+ ip->ip_src = IA_SIN(ia)->sin_addr;
+ break;
+ }
+ }
+
+ IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm);
+ if (inm != NULL &&
+ (imo == NULL || imo->imo_multicast_loop)) {
+ /*
+ * If we belong to the destination multicast group
+ * on the outgoing interface, and the caller did not
+ * forbid loopback, loop back a copy.
+ */
+ ip_mloopback(ifp, m, dst);
+ }
+#ifdef MROUTING
+ else if (ip_mrouter && (flags & IP_FORWARDING) == 0) {
+ /*
+ * If we are acting as a multicast router, perform
+ * multicast forwarding as if the packet had just
+ * arrived on the interface to which we are about
+ * to send. The multicast forwarding function
+ * recursively calls this function, using the
+ * IP_FORWARDING flag to prevent infinite recursion.
+ *
+ * Multicasts that are looped back by ip_mloopback(),
+ * above, will be forwarded by the ip_input() routine,
+ * if necessary.
+ */
+ if (ip_mforward(ip, ifp, m) != 0) {
+ m_freem(m);
+ goto done;
+ }
+ }
+#endif
+ /*
+ * Multicasts with a time-to-live of zero may be looped-
+ * back, above, but must not be transmitted on a network.
+ * Also, multicasts addressed to the loopback interface
+ * are not sent -- the above call to ip_mloopback() will
+ * loop back a copy if this host actually belongs to the
+ * destination group on the loopback interface.
+ */
+ if (ip->ip_ttl == 0 || ifp == &loif) {
+ m_freem(m);
+ goto done;
+ }
+
+ goto sendit;
+ }
+#endif
#ifndef notdef
/*
* If source address not specified yet, use address
@@ -192,7 +291,9 @@ ip_output(m0, opt, ro, flags)
}
m->m_flags |= M_BCAST;
}
-
+#ifdef MULTICAST
+sendit:
+#endif
/*
* If small enough for interface, can just send directly.
*/
@@ -413,7 +514,24 @@ ip_ctloutput(op, so, level, optname, mp)
case IP_RECVRETOPTS:
case IP_RECVDSTADDR:
if (m->m_len != sizeof(int))
+#if defined(MULTICAST) && defined(OLD_VAT_COMPAT)
+ {
+ optname += IP_RETOPTS-IP_OPTIONS;
+ switch(optname) {
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ if (m->m_len == sizeof(char))
+ goto multicast_setopt;
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ if (m->m_len == sizeof(struct ip_mreq))
+ goto multicast_setopt;
+ }
+ error = EINVAL;
+ }
+#else
error = EINVAL;
+#endif
else {
optval = *mtod(m, int *);
switch (optname) {
@@ -446,7 +564,25 @@ ip_ctloutput(op, so, level, optname, mp)
}
break;
#undef OPTSET
-
+#ifdef MULTICAST
+#ifdef OLD_VAT_COMPAT
+ case IP_HDRINCL:
+ if (m->m_len != sizeof(struct in_addr)) {
+ error = EINVAL;
+ break;
+ }
+ optname = IP_MULTICAST_IF;
+ /* FALLTHRU */
+multicast_setopt:
+#endif
+ case IP_MULTICAST_IF:
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ error = ip_setmoptions(optname, &inp->inp_moptions, m);
+ break;
+#endif
default:
error = EINVAL;
break;
@@ -501,7 +637,15 @@ ip_ctloutput(op, so, level, optname, mp)
}
*mtod(m, int *) = optval;
break;
-
+#ifdef MULTICAST
+ case IP_MULTICAST_IF:
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ error = ip_getmoptions(optname, inp->inp_moptions, mp);
+ break;
+#endif
default:
error = EINVAL;
break;
@@ -617,3 +761,339 @@ bad:
(void)m_free(m);
return (EINVAL);
}
+
+#ifdef MULTICAST
+/*
+ * Set the IP multicast options in response to user setsockopt().
+ */
+int
+ip_setmoptions(optname, imop, m)
+ int optname;
+ struct ip_moptions **imop;
+ struct mbuf *m;
+{
+ register int error = 0;
+ u_char loop;
+ register int i;
+ struct in_addr addr;
+ register struct ip_mreq *mreq;
+ register struct ifnet *ifp;
+ register struct ip_moptions *imo = *imop;
+ struct route ro;
+ register struct sockaddr_in *dst;
+
+ if (imo == NULL) {
+ /*
+ * No multicast option buffer attached to the pcb;
+ * allocate one and initialize to default values.
+ */
+ imo = (struct ip_moptions*)malloc(sizeof(*imo), M_IPMOPTS,
+ M_WAITOK);
+
+ if (imo == NULL)
+ return (ENOBUFS);
+ *imop = imo;
+ imo->imo_multicast_ifp = NULL;
+ imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
+ imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
+ imo->imo_num_memberships = 0;
+ }
+
+ switch (optname) {
+
+ case IP_MULTICAST_IF:
+ /*
+ * Select the interface for outgoing multicast packets.
+ */
+ if (m == NULL || m->m_len != sizeof(struct in_addr)) {
+ error = EINVAL;
+ break;
+ }
+ addr = *(mtod(m, struct in_addr *));
+ /*
+ * INADDR_ANY is used to remove a previous selection.
+ * When no interface is selected, a default one is
+ * chosen every time a multicast packet is sent.
+ */
+ if (addr.s_addr == INADDR_ANY) {
+ imo->imo_multicast_ifp = NULL;
+ break;
+ }
+ /*
+ * The selected interface is identified by its local
+ * IP address. Find the interface and confirm that
+ * it supports multicasting.
+ */
+ INADDR_TO_IFP(addr, ifp);
+ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ imo->imo_multicast_ifp = ifp;
+ break;
+
+ case IP_MULTICAST_TTL:
+ /*
+ * Set the IP time-to-live for outgoing multicast packets.
+ */
+ if (m == NULL || m->m_len != 1) {
+ error = EINVAL;
+ break;
+ }
+ imo->imo_multicast_ttl = *(mtod(m, u_char *));
+ break;
+
+ case IP_MULTICAST_LOOP:
+ /*
+ * Set the loopback flag for outgoing multicast packets.
+ * Must be zero or one.
+ */
+ if (m == NULL || m->m_len != 1 ||
+ (loop = *(mtod(m, u_char *))) > 1) {
+ error = EINVAL;
+ break;
+ }
+ imo->imo_multicast_loop = loop;
+ break;
+
+ case IP_ADD_MEMBERSHIP:
+ /*
+ * Add a multicast group membership.
+ * Group must be a valid IP multicast address.
+ */
+ if (m == NULL || m->m_len != sizeof(struct ip_mreq)) {
+ error = EINVAL;
+ break;
+ }
+ mreq = mtod(m, struct ip_mreq *);
+ if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) {
+ error = EINVAL;
+ break;
+ }
+ /*
+ * If no interface address was provided, use the interface of
+ * the route to the given multicast address.
+ */
+ if (mreq->imr_interface.s_addr == INADDR_ANY) {
+ ro.ro_rt = NULL;
+ dst = (struct sockaddr_in *)&ro.ro_dst;
+ dst->sin_len = sizeof(*dst);
+ dst->sin_family = AF_INET;
+ dst->sin_addr = mreq->imr_multiaddr;
+ rtalloc(&ro);
+ if (ro.ro_rt == NULL) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ ifp = ro.ro_rt->rt_ifp;
+ rtfree(ro.ro_rt);
+ }
+ else {
+ INADDR_TO_IFP(mreq->imr_interface, ifp);
+ }
+ /*
+ * See if we found an interface, and confirm that it
+ * supports multicast.
+ */
+ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ /*
+ * See if the membership already exists or if all the
+ * membership slots are full.
+ */
+ for (i = 0; i < imo->imo_num_memberships; ++i) {
+ if (imo->imo_membership[i]->inm_ifp == ifp &&
+ imo->imo_membership[i]->inm_addr.s_addr
+ == mreq->imr_multiaddr.s_addr)
+ break;
+ }
+ if (i < imo->imo_num_memberships) {
+ error = EADDRINUSE;
+ break;
+ }
+ if (i == IP_MAX_MEMBERSHIPS) {
+ error = ETOOMANYREFS;
+ break;
+ }
+ /*
+ * Everything looks good; add a new record to the multicast
+ * address list for the given interface.
+ */
+ if ((imo->imo_membership[i] =
+ in_addmulti(&mreq->imr_multiaddr, ifp)) == NULL) {
+ error = ENOBUFS;
+ break;
+ }
+ ++imo->imo_num_memberships;
+ break;
+
+ case IP_DROP_MEMBERSHIP:
+ /*
+ * Drop a multicast group membership.
+ * Group must be a valid IP multicast address.
+ */
+ if (m == NULL || m->m_len != sizeof(struct ip_mreq)) {
+ error = EINVAL;
+ break;
+ }
+ mreq = mtod(m, struct ip_mreq *);
+ if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) {
+ error = EINVAL;
+ break;
+ }
+ /*
+ * If an interface address was specified, get a pointer
+ * to its ifnet structure.
+ */
+ if (mreq->imr_interface.s_addr == INADDR_ANY)
+ ifp = NULL;
+ else {
+ INADDR_TO_IFP(mreq->imr_interface, ifp);
+ if (ifp == NULL) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ }
+ /*
+ * Find the membership in the membership array.
+ */
+ for (i = 0; i < imo->imo_num_memberships; ++i) {
+ if ((ifp == NULL ||
+ imo->imo_membership[i]->inm_ifp == ifp) &&
+ imo->imo_membership[i]->inm_addr.s_addr ==
+ mreq->imr_multiaddr.s_addr)
+ break;
+ }
+ if (i == imo->imo_num_memberships) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ /*
+ * Give up the multicast address record to which the
+ * membership points.
+ */
+ in_delmulti(imo->imo_membership[i]);
+ /*
+ * Remove the gap in the membership array.
+ */
+ for (++i; i < imo->imo_num_memberships; ++i)
+ imo->imo_membership[i-1] = imo->imo_membership[i];
+ --imo->imo_num_memberships;
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ /*
+ * If all options have default values, no need to keep the mbuf.
+ */
+ if (imo->imo_multicast_ifp == NULL &&
+ imo->imo_multicast_ttl == IP_DEFAULT_MULTICAST_TTL &&
+ imo->imo_multicast_loop == IP_DEFAULT_MULTICAST_LOOP &&
+ imo->imo_num_memberships == 0) {
+ free(*imop, M_IPMOPTS);
+ *imop = NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * Return the IP multicast options in response to user getsockopt().
+ */
+int
+ip_getmoptions(optname, imo, mp)
+ int optname;
+ register struct ip_moptions *imo;
+ register struct mbuf **mp;
+{
+ u_char *ttl;
+ u_char *loop;
+ struct in_addr *addr;
+ struct in_ifaddr *ia;
+
+ *mp = m_get(M_WAIT, MT_SOOPTS);
+
+ switch (optname) {
+
+ case IP_MULTICAST_IF:
+ addr = mtod(*mp, struct in_addr *);
+ (*mp)->m_len = sizeof(struct in_addr);
+ if (imo == NULL || imo->imo_multicast_ifp == NULL)
+ addr->s_addr = INADDR_ANY;
+ else {
+ IFP_TO_IA(imo->imo_multicast_ifp, ia);
+ addr->s_addr = (ia == NULL) ? INADDR_ANY
+ : IA_SIN(ia)->sin_addr.s_addr;
+ }
+ return (0);
+
+ case IP_MULTICAST_TTL:
+ ttl = mtod(*mp, u_char *);
+ (*mp)->m_len = 1;
+ *ttl = (imo == NULL) ? IP_DEFAULT_MULTICAST_TTL
+ : imo->imo_multicast_ttl;
+ return (0);
+
+ case IP_MULTICAST_LOOP:
+ loop = mtod(*mp, u_char *);
+ (*mp)->m_len = 1;
+ *loop = (imo == NULL) ? IP_DEFAULT_MULTICAST_LOOP
+ : imo->imo_multicast_loop;
+ return (0);
+
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
+/*
+ * Discard the IP multicast options.
+ */
+void
+ip_freemoptions(imo)
+ register struct ip_moptions *imo;
+{
+ register int i;
+
+ if (imo != NULL) {
+ for (i = 0; i < imo->imo_num_memberships; ++i)
+ in_delmulti(imo->imo_membership[i]);
+ free(imo, M_IPMOPTS);
+ }
+}
+
+/*
+ * Routine called from ip_output() to loop back a copy of an IP multicast
+ * packet to the input queue of a specified interface. Note that this
+ * calls the output routine of the loopback "driver", but with an interface
+ * pointer that might NOT be &loif -- easier than replicating that code here.
+ */
+void
+ip_mloopback(ifp, m, dst)
+ struct ifnet *ifp;
+ register struct mbuf *m;
+ register struct sockaddr_in *dst;
+{
+ register struct ip *ip;
+ struct mbuf *copym;
+
+ copym = m_copy(m, 0, M_COPYALL);
+ if (copym != NULL) {
+ /*
+ * We don't bother to fragment if the IP length is greater
+ * than the interface's MTU. Can this possibly matter?
+ */
+ ip = mtod(copym, struct ip *);
+ ip->ip_len = htons((u_short)ip->ip_len);
+ ip->ip_off = htons((u_short)ip->ip_off);
+ ip->ip_sum = 0;
+ ip->ip_sum = in_cksum(copym, ip->ip_hl << 2);
+ (void) looutput(ifp, copym, (struct sockaddr *)dst, 0);
+ }
+}
+#endif
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
index 47d76fe01b5a..9452d8c9a5d5 100644
--- a/sys/netinet/ip_var.h
+++ b/sys/netinet/ip_var.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1982, 1986 Regents of the University of California.
+ * Copyright (c) 1982, 1986, 1993 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)ip_var.h 7.7 (Berkeley) 6/28/90
- * $Id: ip_var.h,v 1.3 1993/11/07 17:48:00 wollman Exp $
+ * $Id: ip_var.h,v 1.4 1994/05/17 22:31:12 jkh Exp $
*/
#ifndef _NETINET_IP_VAR_H_
@@ -103,6 +103,18 @@ struct ipoption {
char ipopt_list[MAX_IPOPTLEN]; /* options proper */
};
+/*
+ * Structure attached to inpcb.ip_moptions and
+ * passed to ip_output when IP multicast options are in use.
+ */
+struct ip_moptions {
+ struct ifnet *imo_multicast_ifp; /* ifp for outgoing multicasts */
+ u_char imo_multicast_ttl; /* TTL for outgoing multicasts */
+ u_char imo_multicast_loop; /* 1 => hear sends if a member */
+ u_short imo_num_memberships; /* no. memberships this socket */
+ struct in_multi *imo_membership[IP_MAX_MEMBERSHIPS];
+};
+
struct ipstat {
long ips_total; /* total packets received */
long ips_badsum; /* checksum bad */
@@ -129,6 +141,7 @@ struct ipstat {
#ifdef KERNEL
/* flags passed to ip_output as last parameter */
#define IP_FORWARDING 0x1 /* most of ip header exists */
+#define IP_MULTICASTOPTS 0x2 /* multicast opts present */
#define IP_ROUTETOIF SO_DONTROUTE /* bypass routing tables */
#define IP_ALLOWBROADCAST SO_BROADCAST /* can send broadcast packets */
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
index 1618339176ae..a98cc1a71efe 100644
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)raw_ip.c 7.8 (Berkeley) 7/25/90
- * $Id: raw_ip.c,v 1.4 1993/12/19 00:52:46 wollman Exp $
+ * $Id: raw_ip.c,v 1.5 1994/05/17 22:31:13 jkh Exp $
*/
#include "param.h"
@@ -118,7 +118,11 @@ rip_output(m, so)
return (ip_output(m,
(rp->rinp_flags & RINPF_HDRINCL)? (struct mbuf *)0: rp->rinp_options,
&rp->rinp_route,
- (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST));
+ (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST
+#ifdef MULTICAST
+ | IP_MULTICASTOPTS, rp->rinp_rcb.rcb_moptions
+#endif
+ ));
}
/*
@@ -154,10 +158,27 @@ rip_ctloutput(op, so, level, optname, m)
else
rp->rinp_flags &= ~RINPF_HDRINCL;
break;
-
+#ifdef MULTICAST
+ case IP_MULTICAST_IF:
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ error = ip_setmoptions(optname,
+ &rp->rinp_rcb.rcb_moptions, *m);
+ break;
+ default:
+#ifdef MROUTING
+ error = ip_mrouter_cmd(optname, so, *m);
+#else
+ error = EINVAL;
+#endif
+ break;
+#else
default:
error = EINVAL;
break;
+#endif
}
break;
@@ -178,7 +199,16 @@ rip_ctloutput(op, so, level, optname, m)
(*m)->m_len = sizeof (int);
*mtod(*m, int *) = rp->rinp_flags & RINPF_HDRINCL;
break;
-
+#ifdef MULTICAST
+ case IP_MULTICAST_IF:
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ error = ip_getmoptions(optname,
+ rp->rinp_rcb.rcb_moptions, m);
+ break;
+#endif
default:
error = EINVAL;
m_freem(*m);
@@ -201,7 +231,9 @@ rip_usrreq(so, req, m, nam, control)
{
register int error = 0;
register struct raw_inpcb *rp = sotorawinpcb(so);
-
+#if defined(MULTICAST) && defined(MROUTING)
+ extern struct socket *ip_mrouter;
+#endif
switch (req) {
case PRU_ATTACH:
@@ -217,8 +249,16 @@ rip_usrreq(so, req, m, nam, control)
case PRU_DETACH:
if (rp == 0)
panic("rip_detach");
+#if defined(MULTICAST) && defined(MROUTING)
+ if (so == ip_mrouter)
+ ip_mrouter_done();
+#endif
if (rp->rinp_options)
m_freem(rp->rinp_options);
+#ifdef MULTICAST
+ if (rp->rinp_rcb.rcb_moptions)
+ ip_freemoptions(rp->rinp_rcb.rcb_moptions);
+#endif
if (rp->rinp_route.ro_rt)
RTFREE(rp->rinp_route.ro_rt);
if (rp->rinp_rcb.rcb_laddr)
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index b6f377690856..e258f5f6d022 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)tcp_usrreq.c 7.15 (Berkeley) 6/28/90
- * $Id: tcp_usrreq.c,v 1.5 1994/01/24 05:12:36 davidg Exp $
+ * $Id: tcp_usrreq.c,v 1.6 1994/05/22 23:18:40 ache Exp $
*/
#include "param.h"
@@ -351,12 +351,15 @@ tcp_ctloutput(op, so, level, optname, mp)
{
int error = 0;
struct inpcb *inp = sotoinpcb(so);
- register struct tcpcb *tp = intotcpcb(inp);
+ register struct tcpcb *tp;
register struct mbuf *m;
if (level != IPPROTO_TCP)
return (ip_ctloutput(op, so, level, optname, mp));
+ if (!inp || !(tp = intotcpcb(inp)))
+ return (EINVAL);
+
switch (op) {
case PRCO_SETOPT:
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index 44e244c31c95..12b5b3ece29e 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* from: @(#)udp_usrreq.c 7.20 (Berkeley) 4/20/91
- * $Id: udp_usrreq.c,v 1.7 1994/02/07 19:53:25 ache Exp $
+ * $Id: udp_usrreq.c,v 1.8 1994/05/17 22:31:14 jkh Exp $
*/
#include "param.h"
@@ -145,6 +145,93 @@ udp_input(m, iphlen)
return;
}
}
+#ifdef MULTICAST
+ if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
+ in_broadcast(ip->ip_dst)) {
+ struct socket *last;
+ /*
+ * Deliver a multicast or broadcast datagram to *all* sockets
+ * for which the local and remote addresses and ports match
+ * those of the incoming datagram. This allows more than
+ * one process to receive multi/broadcasts on the same port.
+ * (This really ought to be done for unicast datagrams as
+ * well, but that would cause problems with existing
+ * applications that open both address-specific sockets and
+ * a wildcard socket listening to the same port -- they would
+ * end up receiving duplicates of every unicast datagram.
+ * Those applications open the multiple sockets to overcome an
+ * inadequacy of the UDP socket interface, but for backwards
+ * compatibility we avoid the problem here rather than
+ * fixing the interface. Maybe 4.4BSD will remedy this?)
+ */
+
+ /*
+ * Construct sockaddr format source address.
+ */
+ udp_in.sin_port = uh->uh_sport;
+ udp_in.sin_addr = ip->ip_src;
+ m->m_len -= sizeof (struct udpiphdr);
+ m->m_data += sizeof (struct udpiphdr);
+ /*
+ * Locate pcb(s) for datagram.
+ * (Algorithm copied from raw_intr().)
+ */
+ last = NULL;
+ for (inp = udb.inp_next; inp != &udb; inp = inp->inp_next) {
+ if (inp->inp_lport != uh->uh_dport)
+ continue;
+ if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ if (inp->inp_laddr.s_addr !=
+ ip->ip_dst.s_addr)
+ continue;
+ }
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ if (inp->inp_faddr.s_addr !=
+ ip->ip_src.s_addr ||
+ inp->inp_fport != uh->uh_sport)
+ continue;
+ }
+
+ if (last != NULL) {
+ struct mbuf *n;
+
+ if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
+ if (sbappendaddr(&last->so_rcv,
+ (struct sockaddr *)&udp_in,
+ n, (struct mbuf *)0) == 0)
+ m_freem(n);
+ else
+ sorwakeup(last);
+ }
+ }
+ last = inp->inp_socket;
+ /*
+ * Don't look for additional matches if this one
+ * does not have the SO_REUSEADDR socket option set.
+ * This heuristic avoids searching through all pcbs
+ * in the common case of a non-shared port. It
+ * assumes that an application will never clear
+ * the SO_REUSEADDR option after setting it.
+ */
+ if ((last->so_options & SO_REUSEADDR) == 0)
+ break;
+ }
+
+ if (last == NULL) {
+ /*
+ * No matching pcb found; discard datagram.
+ * (No need to send an ICMP Port Unreachable
+ * for a broadcast or multicast datgram.)
+ */
+ goto bad;
+ }
+ if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in,
+ m, (struct mbuf *)0) == 0)
+ goto bad;
+ sorwakeup(last);
+ return;
+ }
+#endif
/*
* Locate pcb for datagram.
@@ -163,10 +250,13 @@ udp_input(m, iphlen)
if (inp == 0) {
/* don't send ICMP response for broadcast packet */
udpstat.udps_noport++;
- if (m->m_flags & M_BCAST) {
+#ifndef MULTICAST
+ /* XXX why don't we do this with MULTICAST? */
+ if (m->m_flags & (M_BCAST | M_MCAST)) {
udpstat.udps_noportbcast++;
goto bad;
}
+#endif
*ip = save_ip;
ip->ip_len += iphlen;
{
@@ -355,7 +445,11 @@ udp_output(inp, m, addr, control)
((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */
udpstat.udps_opackets++;
error = ip_output(m, inp->inp_options, &inp->inp_route,
- inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST));
+ inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)
+#ifdef MULTICAST
+ | IP_MULTICASTOPTS, inp->inp_moptions
+#endif
+ );
if (addr) {
in_pcbdisconnect(inp);