===================================================================
Index: conf/mips/BINARY
***************
*** 41,46 ****
--- 41,48 ----
  options		CDFS
  
  options		DSG
+ options		MULTICAST
+ options		MROUTING
  
  makeoptions	CC="cc"
  makeoptions	LD="ld"
===================================================================
Index: conf/mips/files.mips
***************
*** 106,112 ****
  io/np/np_msg.c                  optional ci device-driver Binary
  io/np/np_subr.c                 optional ci device-driver Binary
  io/np/npvar.c                   optional ci device-driver Binary
! io/netif/if_ln.c		optional ln device-driver Binary 
  io/netif/if_ln_copy.s		optional ln Binary 
  io/netif/if_ne.c	        optional ne device-driver Binary
  io/netif/if_sl.c		optional sl device-driver Binary Unsupported
--- 106,112 ----
  io/np/np_msg.c                  optional ci device-driver Binary
  io/np/np_subr.c                 optional ci device-driver Binary
  io/np/npvar.c                   optional ci device-driver Binary
! io/netif/if_ln.c		optional ln device-driver
  io/netif/if_ln_copy.s		optional ln Binary 
  io/netif/if_ne.c	        optional ne device-driver Binary
  io/netif/if_sl.c		optional sl device-driver Binary Unsupported
***************
*** 187,193 ****
  data/ts_data.c			optional ts or zs device-driver Notbinary
  data/dmb_data.c			optional dmb  device-driver Notbinary
  data/gvp_data.c         	optional ci or bvpssp or msi Notbinary
! data/if_ln_data.c		optional ln Notbinary
  data/if_ne_data.c		optional ne Notbinary
  data/if_qe_data.c		optional qe Notbinary
  data/if_scs_data.c		optional ci inet scsnet Notbinary
--- 187,193 ----
  data/ts_data.c			optional ts or zs device-driver Notbinary
  data/dmb_data.c			optional dmb  device-driver Notbinary
  data/gvp_data.c         	optional ci or bvpssp or msi Notbinary
! data/if_ln_data.c		optional nomulti Notbinary
  data/if_ne_data.c		optional ne Notbinary
  data/if_qe_data.c		optional qe Notbinary
  data/if_scs_data.c		optional ci inet scsnet Notbinary
===================================================================
Index: conf/mips/newvers.sh
***************
*** 3,10 ****
--- 3,15 ----
  DATE=`date`
  awk '	{	version = $1 + 1; }\
  END	{	printf "" > "vers.c";\
+ 		printf "#ifdef MULTICAST\n" >> "vers.c";\
+ 		printf "char version[] = \"ULTRIX V4.2A (Rev. 47) (+ MULTICAST 1.2) System #%d: ", version >> "vers.c";\
+ 		printf "DATE-HERE\\n\";\n" >> "vers.c";\
+ 		printf "#else MULTICAST\n" >> "vers.c";\
  		printf "char version[] = \"ULTRIX V4.2A (Rev. 47) System #%d: ", version >> "vers.c";\
  		printf "DATE-HERE\\n\";\n" >> "vers.c";\
+ 		printf "#endif MULTICAST\n" >> "vers.c";\
  		printf "%d\n", version > "version"; }' < version
  ed - vers.c <<%
  1,\$s/DATE-HERE/$DATE/
===================================================================
Index: conf/files
***************
*** 11,17 ****
  net/dli/dli_open.c	optional dli Binary
  net/dli/dli_output.c	optional dli Binary
  net/dli/dli_proto.c	optional ether or fddi or dli or osi Notbinary
! net/dli/dli_setopt.c	optional dli Binary
  net/dli/dli_timer.c	optional ether or fddi or dli Binary
  net/dli/dli_usrreq.c	optional dli Binary
  net/dli/dli_subr.c	optional ether or fddi or dli Binary
--- 11,17 ----
  net/dli/dli_open.c	optional dli Binary
  net/dli/dli_output.c	optional dli Binary
  net/dli/dli_proto.c	optional ether or fddi or dli or osi Notbinary
! net/dli/dli_setopt.c	optional dli
  net/dli/dli_timer.c	optional ether or fddi or dli Binary
  net/dli/dli_usrreq.c	optional dli Binary
  net/dli/dli_subr.c	optional ether or fddi or dli Binary
***************
*** 48,74 ****
  net/net/af.c		standard Binary
  net/net/conf_net.c	standard 
  net/net/if.c		standard Binary
! net/net/if_loop.c	optional loop Binary
! data/if_loop_data.c	optional loop Notbinary
  net/net/if_to_proto.c	standard Binary
  net/net/pfilt.c		optional packetfilter or ether or fddi Binary
  net/net/gw_screen.c	optional gwscreen Binary
  data/gw_screen_data.c	standard
! net/net/raw_cb.c	standard Binary
  net/net/raw_usrreq.c	standard Binary
  net/net/route.c		standard Binary
  net/net/net_common.c	standard Binary
  net/netdnet/decnet_dummy.c	optional decnet Notbinary
! net/netinet/if_ether.c	optional ether or fddi inet Binary
! net/netinet/in.c	optional inet Binary
! net/netinet/in_pcb.c	optional inet Binary
  net/netinet/in_proto.c	optional inet
! net/netinet/ip_icmp.c	optional inet Binary
  net/netinet/ip_if.c	optional inet Binary
! net/netinet/ip_input.c	optional inet Binary
! net/netinet/ip_output.c	optional inet Binary
  net/netinet/ip_screen.c	optional inet Binary
! net/netinet/raw_ip.c	optional inet Binary
  net/netinet/tcp_debug.c	optional inet Binary
  net/netinet/tcp_input.c	optional inet Binary
  net/netinet/tcp_output.c	optional inet Binary
--- 48,74 ----
  net/net/af.c		standard Binary
  net/net/conf_net.c	standard 
  net/net/if.c		standard Binary
! net/net/if_loop.c	optional loop
! data/if_loop_data.c	optional loop nomulti Notbinary
  net/net/if_to_proto.c	standard Binary
  net/net/pfilt.c		optional packetfilter or ether or fddi Binary
  net/net/gw_screen.c	optional gwscreen Binary
  data/gw_screen_data.c	standard
! net/net/raw_cb.c	standard
  net/net/raw_usrreq.c	standard Binary
  net/net/route.c		standard Binary
  net/net/net_common.c	standard Binary
  net/netdnet/decnet_dummy.c	optional decnet Notbinary
! net/netinet/if_ether.c	optional ether or fddi inet
! net/netinet/in.c	optional inet
! net/netinet/in_pcb.c	optional inet
  net/netinet/in_proto.c	optional inet
! net/netinet/ip_icmp.c	optional inet
  net/netinet/ip_if.c	optional inet Binary
! net/netinet/ip_input.c	optional inet
! net/netinet/ip_output.c	optional inet
  net/netinet/ip_screen.c	optional inet Binary
! net/netinet/raw_ip.c	optional inet
  net/netinet/tcp_debug.c	optional inet Binary
  net/netinet/tcp_input.c	optional inet Binary
  net/netinet/tcp_output.c	optional inet Binary
***************
*** 75,81 ****
  net/netinet/tcp_subr.c	optional inet Binary
  net/netinet/tcp_timer.c	optional inet Binary
  net/netinet/tcp_usrreq.c	optional inet Binary
! net/netinet/udp_usrreq.c	optional inet Binary
  net/netbsc/bsc_pcb.c	optional bsc Binary
  net/netbsc/bsc_proto.c	optional bsc 
  net/netbsc/bsc_states.c	optional bsc Binary
--- 75,83 ----
  net/netinet/tcp_subr.c	optional inet Binary
  net/netinet/tcp_timer.c	optional inet Binary
  net/netinet/tcp_usrreq.c	optional inet Binary
! net/netinet/udp_usrreq.c	optional inet
! net/ipmulticast/igmp.c		optional inet
! net/ipmulticast/ip_mroute.c	optional inet
  net/netbsc/bsc_pcb.c	optional bsc Binary
  net/netbsc/bsc_proto.c	optional bsc 
  net/netbsc/bsc_states.c	optional bsc Binary
===================================================================
Index: h/kmalloc.h
***************
*** 1,5 ****
--- 1,6 ----
  /*	@(#)kmalloc.h	5.1	(ULTRIX)	3/30/91	*/
  
+ 					/* plus MULTICAST 1.1 */
  /*
   * Copyright (c) 1987 Regents of the University of California.
   * All rights reserved.  The Berkeley software License Agreement
***************
*** 20,25 ****
--- 21,29 ----
   * 31 Aug 90	paradis
   *		Added KM_SPU
   *
+  * 91/05/29     Mark J. Steiglitz, Stanford
+  *              IP Multicast modifications from Ultrix 3.1 to Ultrix 4.1
+  *
   * 30 Dec 89	bp
   *		Add state in flags specifically for internal usage 
   *		for pageout and sched.
***************
*** 157,166 ****
  #define KM_VECTOR	41	/* vector data stuctures */
  #define KM_WAN          42	/* x.25 */
  #define KM_NOFILE	43	/* Overflow buffer for open file descriptors */
! #define KM_FREE3	44
! #define KM_FREE4	45
! #define KM_FREE5	46
! #define KM_FREE6	47
  #define KM_FREE7	48
  #define KM_FREE8	49
  #define KM_FREE9	50
--- 161,170 ----
  #define KM_VECTOR	41	/* vector data stuctures */
  #define KM_WAN          42	/* x.25 */
  #define KM_NOFILE	43	/* Overflow buffer for open file descriptors */
! #define KM_IPMOPTS	44	/* internet multicast options */
! #define KM_IPMADDR	45	/* internet multicast address */
! #define KM_IFMADDR	46	/* link-level multicast address */
! #define KM_MRTABLE	47	/* multicast routing tables */
  #define KM_FREE7	48
  #define KM_FREE8	49
  #define KM_FREE9	50
===================================================================
Index: h/mbuf.h
***************
*** 1,4 ****
--- 1,5 ----
  /*	@(#)mbuf.h	5.1	(ULTRIX)	3/30/91	*/
+ 					/* plus MULTICAST 1.1 */
  
  /************************************************************************
   *									*
***************
*** 161,166 ****
--- 162,172 ----
  #define MT_OPT		KM_OPT		/* DECnet optional data */
  #define	MT_IFADDR	KM_IFADDR	/* interface address */
  #define	MT_ACCESS	KM_ACCESS	/* access control info */
+ #define	MT_IPMOPTS	KM_IPMOPTS	/* internet multicast options */
+ #define	MT_IPMADDR	KM_IPMADDR	/* internet multicast address */
+ #define	MT_IFMADDR	KM_IFMADDR	/* link-level multicast address */
+ #define	MT_MRTABLE	KM_MRTABLE	/* multicast routing tables */
+ 
  
  /* flags to m_get */
  #define	M_DONTWAIT	KM_NOWAIT
===================================================================
Index: io/netif/if_ln.c
***************
*** 1,5 ****
--- 1,6 ----
  #ifndef lint
  static char *sccsid = "@(#)if_ln.c	5.4      (ULTRIX)  5/30/91";
+ 						/* plus MULTICAST 1.2 */
  #endif lint
  
  /************************************************************************
***************
*** 133,138 ****
--- 134,142 ----
   * 6/2/89 Uttam Shikarpur
   *      Add support for Ethernet packet filter
   *
+  * April 1989 Tony Mason  Stanford
+  *    Added IP Multicast support.
+  *
   * 9/21/88 U. Sinkewicz
   *      Added locks for SMP.
   *
***************
*** 523,529 ****
  	ifp->lk_softc = &sc->lk_ln_softc;
  	ifp->if_mtu = ETHERMTU;
  	ifp->if_type = IFT_ETHER;
! 	ifp->if_flags |= IFF_BROADCAST | IFF_DYNPROTO | IFF_NOTRAILERS;
  	((struct arpcom *)ifp)->ac_ipaddr.s_addr = 0;
  
  	/*
--- 527,538 ----
  	ifp->lk_softc = &sc->lk_ln_softc;
  	ifp->if_mtu = ETHERMTU;
  	ifp->if_type = IFT_ETHER;
! #ifdef MULTICAST
! 	ifp->if_flags |= IFF_BROADCAST | IFF_DYNPROTO | IFF_NOTRAILERS
! 	               | IFF_MULTICAST;
! #else
!   	ifp->if_flags |= IFF_BROADCAST | IFF_DYNPROTO | IFF_NOTRAILERS;
! #endif
  	((struct arpcom *)ifp)->ac_ipaddr.s_addr = 0;
  
  	/*
***************
*** 1186,1193 ****
--- 1195,1204 ----
  	struct ctrreq *ctr = (struct ctrreq *)data;
  	struct protosw *pr;
  	struct ifaddr *ifa = (struct ifaddr *)data;
+ #ifndef MULTICAST
  	int bitpos;		/* top 6 bits of crc = bit in multicast mask */
  	u_short newmask[4];	/* new value of multicast address mask */
+ #endif MULTICAST
  	int j = -1, s, error=0;
  
  	s = splimp();
***************
*** 1263,1271 ****
--- 1274,1315 ----
  		}
  		break;
  
+ #ifdef MULTICAST
  	case SIOCDELMULTI:
  	case SIOCADDMULTI:
+ 		/*
+ 		 * Update our multicast list.
+ 		 * (When compiled with "MULTICAST" defined, the higher-level
+ 		 * multicast list routines in if_ether.c are used, instead
+ 		 * of the original DEC code, below.)
+ 		 */
+ 		if(cmd == SIOCADDMULTI) {
+ 		    if (lndebug>1) printf("SIOCADDMULTI ");
+ 		    error = ether_addmulti(ifr, &sc->is_ac);
+ 		}
+ 		else {
+ 		    if (lndebug>1) printf("SIOCDELMULTI ");
+ 		    error = ether_delmulti(ifr, &sc->is_ac);
+ 		}
+ 
+ 		if (error == ENETRESET) {
+ 			/*
+ 			 * Multicast list has changed;  set the hardware
+ 			 * filter accordingly.
+ 			 */
+ 			if (ifp->if_flags & IFF_RUNNING) {
+ 				lnrestart(ifp);
+ 				lninit(ifp->if_unit);
+ 			} else {
+ 				lnrestart(ifp);
+ 			}
+ 			error = 0;
+ 		}
+ 		break;
  
+ #else
+ 	case SIOCDELMULTI:
+ 	case SIOCADDMULTI:
                  smp_lock(&sc->lk_ln_softc, LK_RETRY);
  		if (cmd == SIOCDELMULTI) {
  			if (lndebug>1) printf("SIOCDELMULTI ");
***************
*** 1365,1370 ****
--- 1409,1415 ----
  			lnrestart(ifp);
  		}
  		break;
+ #endif MULTICAST
  
  	case SIOCRDCTRS:
  	case SIOCRDZCTRS:
***************
*** 1399,1405 ****
  			 */
  			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
  			break;
! #endif
  
  		default:
  			if (pr=iffamily_to_proto(ifa->ifa_addr.sa_family)) {
--- 1444,1450 ----
  			 */
  			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
  			break;
! #endif INET
  
  		default:
  			if (pr=iffamily_to_proto(ifa->ifa_addr.sa_family)) {
***************
*** 1552,1557 ****
--- 1597,1603 ----
  /*
   * Restart the Lance chip -- this routine is called:
   *   - after changing the multicast address filter mask
+  *   - after setting the if flags (in case IFF_PROMISC or IFF_ALLMULTI change)
   *   - on any loopback mode state change
   *   - on any error which disables the transmitter
   *   - on all missed packet errors
***************
*** 1569,1574 ****
--- 1615,1624 ----
  	register char *initb = sc->initbaddr;
  	register int i, pi;
  	int s;
+ #ifdef MULTICAST
+ 	int bitpos;		/* top 6 bits of crc = bit in multicast mask */
+ 	u_short newmask[4];	/* new value of multicast address mask */
+ #endif MULTICAST
  
  	/*
  	 * stop the chip
***************
*** 1619,1624 ****
--- 1669,1748 ----
  		}
  	}
  
+ #ifdef MULTICAST
+ 	/*
+ 	 * The MULTICAST option enables support for higher-level (e.g., IP)
+ 	 * multicasting, and for the IFF_PROMISC and IFF_ALLMULTI interface
+ 	 * flags.  The code for setting the multicast hash filter has been
+ 	 * moved here from lnioctl() in order to properly synchronize with
+ 	 * the PROMISC and ALLMULTI flags.
+ 	 */
+ 
+ 	sc->ln_initb.ln_mode &= ~LN_PROM;
+ 	for (i=0; i<4; i++) newmask[i] = 0x0000;
+ 
+ 	if (ifp->if_flags & IFF_PROMISC) {
+ 		/*
+ 		 * Enable promiscous reception mode.
+ 		 */
+ 		sc->ln_initb.ln_mode |= LN_PROM;
+ 	}
+ 	else if ( ifp->if_flags & IFF_ALLMULTI ) {
+ 		/*
+ 		 * Turn on all 64 multicast hash bits.
+ 		 */
+ allmulti:	for (i=0; i<4; i++) newmask[i] = 0xFFFF;
+ 	}
+ 	else {
+ 		/*
+ 		 * Recalculate all current multimask crc/bits
+ 		 * and reload multimask info.
+ 		 *
+ 		 * For each currently used multicast address,
+ 		 * calculate CRC, save top 6 bits, load
+ 		 * appropriate mask bit into newmask[i]
+ 		 */
+ 		struct ether_multi *enm;
+ 		struct ether_multistep step;
+ 			
+ 		ETHER_FIRST_MULTI(step, &sc->is_ac, enm);
+ 		while (enm != NULL) {
+ 			if (bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0)
+ 				/*
+ 				 * We must listen to a range of multicast
+ 				 * addresses.  For now, just accept all
+ 				 * multicasts, rather than trying to set
+ 				 * only those filter bits needed to match
+ 				 * the range.  (At this time, the only use
+ 				 * of address ranges is for IP multicast
+ 				 * routing, for which the range is big
+ 				 * enough to require all bits set.)
+ 				 */
+ 				goto allmulti;
+ 
+ 			ln_docrc(enm->enm_addrlo, 0, sc);
+ 			bitpos = ((unsigned int)sc->ln_crc >> 26) & 0x3f;
+ 
+ 			/* 0-15 */
+ 			if (bitpos >= 0 && bitpos < 16)
+ 				newmask[0] |= (1 << (bitpos - 0));
+ 			/* 16-31 */
+ 			else if (bitpos < 32)
+ 				newmask[1] |= (1 << (bitpos - 16));
+ 			/* 32-47 */
+ 			else if (bitpos < 48)
+ 				newmask[2] |= (1 << (bitpos - 32));
+ 			/* 48-63 */
+ 			else if (bitpos < 64)
+ 				newmask[3] |= (1 << (bitpos - 48));
+ 
+ 			ETHER_NEXT_MULTI(step, enm);
+ 		}
+ 	}
+ 	for (i = 0; i < 4; i++)
+ 		sc->ln_initb.ln_multi_mask[i] = newmask[i] & 0xffff;
+ #endif MULTICAST
+ 
  	/*
  	 * reload Lance with init block
  	 */
***************
*** 2021,2026 ****
--- 2145,2160 ----
  		len -= sizeof(struct ether_header);
  	}
  
+ #ifndef MULTICAST
+ 	/*
+ 	 * I wonder if omitting this check is going to break anything?  With
+ 	 * the new multicast list structure, doing such a check on every
+ 	 * incoming multicast packet would be quite expensive.  Given that
+ 	 * higher levels have to be able to recognize unwanted *broadcast*
+ 	 * packets, they ought to be able to deal with unwanted *multicast*
+ 	 * packets, right?
+ 	 */
+ 
   	if (!(sc->is_if.if_flags & IFF_PROMISC) ) { 
  	/*
  	 * Make sure our multicast address filter doesn't hand us
***************
*** 2041,2046 ****
--- 2175,2181 ----
  			}
  		}
  	}
+ #endif !MULTICAST
  	sc->is_if.if_ipackets++;
  
  	eptr->ether_type = ntohs((u_short)eptr->ether_type);
===================================================================
Index: net/dli/dli_setopt.c
***************
*** 1,5 ****
--- 1,6 ----
  #ifndef	lint
  static char *sccsid = "@(#)dli_setopt.c	5.1	ULTRIX	3/30/91";
+                                                 /* plus MULTICAST 1.0 */
  #endif	lint
  
  /*
***************
*** 256,261 ****
--- 257,265 ----
  			if ( pr == NULL || portid == 0 )
  			{
  				*(struct ether_pa *) dreq.ifr_addr.sa_data = *(struct ether_pa *) (mcast_buf+i);
+ #ifdef MULTICAST
+ 			dreq.ifr_addr.sa_family = AF_UNSPEC;
+ #endif MULTICAST
  				CALL_TO_NONSMP_DRIVER( (*ifp), saveaffinity);
  				error = (*ifp->if_ioctl)(ifp, cmd, &dreq);
  				RETURN_FROM_NONSMP_DRIVER( (*ifp), saveaffinity);
===================================================================
Index: net/net/if.h
***************
*** 36,41 ****
--- 36,48 ----
   *									*
   *			Modification History				*
   *
+  *                                                                      *
+  * 91/05/29     Mark J. Steiglitz, Stanford
+  *              IP Multicast modifications from Ultrix 3.1 to Ultrix 4.1
+  *
+  *      Tony Mason, Stanford University, 21-Mar-89                      *
+  *              Added IP Multicast changes                              *
+  *
   *	Chran-Ham Chang 9-Nov-90
   *		Added structure to support FDDI read status 
   *
***************
*** 190,196 ****
  #define	IFF_NOTRAILERS	0x20		/* avoid use of trailers */
  #define	IFF_RUNNING	0x40		/* resources allocated */
  #define	IFF_NOARP	0x80		/* no address resolution protocol */
- #define	IFF_CANTCHANGE	(IFF_BROADCAST | IFF_POINTOPOINT | IFF_RUNNING)
  #define IFF_PROMISC	0x100		/* receive all packets */
  #define IFF_ALLMULTI	0x200		/* receive all multicast packets */
  #define IFF_DYNPROTO	0x400		/* support dynamic proto dispatching */ 
--- 197,202 ----
***************
*** 199,204 ****
--- 205,222 ----
  #define IFF_802HDR	0x2000		/* 802 encapsulation */
  #define	IFF_PFCOPYALL	0x4000		/* pfilt gets packets to this host */
  
+ #define IFF_MULTICAST   0x8000          /* supports multicast */
+ /*
+  * The IFF_MULTICAST flag indicates that the network can support the
+  * transmission and reception of higher-level (e.g., IP) multicast packets.
+  * It is independent of hardware support for multicasting; for example,
+  * point-to-point links or pure broadcast networks may well support
+  * higher-level multicasts.
+  */
+ 
+ #define	IFF_CANTCHANGE	(IFF_BROADCAST | IFF_POINTOPOINT | IFF_RUNNING \
+ 			 | IFF_MULTICAST)
+   
  /* interface types for benefit of parsing media address headers */
  #define IFT_OTHER	0x1		/* none of the following */
  #define IFT_1822	0x2		/* old-style arpanet imp */
===================================================================
Index: net/net/if_loop.c
***************
*** 1,5 ****
--- 1,6 ----
  #ifndef lint
  static  char    *sccsid = "@(#)if_loop.c	5.1  (ULTRIX)                3/30/91";
+ 						/* plus MULTICAST 1.1 */
  #endif lint
  
  /************************************************************************
***************
*** 44,49 ****
--- 45,53 ----
   *	Ursula Sinkewicz 28-Feb-89
   *		SMP/mips. (Added changes from R. Bhanukitsiri 02/06/89)
   *
+  *      Tony Mason, Stanford University, 21-Mar-89                      *
+  *              Added IP Multicast changes                              *
+  *									*
   *	Larry Palmer 15-Jan-88						*
   *		Final 43bsd release (move from netinet)			*
   *									*
***************
*** 80,86 ****
--- 84,94 ----
  		ifp->if_name = "lo";
  		ifp->if_unit = unit;
  		ifp->if_mtu = LOMTU;
+ #ifdef MULTICAST
+ 		ifp->if_flags = IFF_LOOPBACK | IFF_MULTICAST;
+ #else
  		ifp->if_flags = IFF_LOOPBACK;
+ #endif MULTICAST
  		ifp->if_init = loinit;
  		ifp->if_ioctl = loioctl;
  		ifp->if_output = looutput;
***************
*** 195,200 ****
--- 203,211 ----
  	int cmd;
  	caddr_t data;
  {
+ #ifdef MULTICAST
+ 	register struct ifreq *ifr = (struct ifreq *)data;
+ #endif MULTICAST
  	int error = 0, i;
  	register int unit = ifp->if_unit;
  	struct lo_softc *sc = &lo_softc[unit];
***************
*** 227,232 ****
--- 238,258 ----
  		}
  	break;
  
+ #ifdef MULTICAST
+ 	case SIOCADDMULTI:
+ 	case SIOCDELMULTI:
+ 		switch (ifr->ifr_addr.sa_family) {
+ #ifdef INET
+ 		case AF_INET:
+ 			break;
+ #endif INET
+ 		default:
+ 			error = EAFNOSUPPORT;
+ 			break;
+ 		}
+ 		break;
+ #endif MULTICAST
+ 		
  	default:
  		error = EINVAL;
  	}
===================================================================
Index: net/net/raw_cb.c
***************
*** 1,5 ****
--- 1,6 ----
  #ifndef lint
  static	char	*sccsid = "@(#)raw_cb.c	5.1		(ULTRIX)	3/30/91";
+ 						/* plus MULTICAST 1.1 */
  #endif lint
  /************************************************************************
   *									*
***************
*** 36,41 ****
--- 37,45 ----
   *	28-Feb-89	Ursula Sinkewicz
   *    		SMP/mips merge.  (Added R. Bhanukitsiri changes 2/5/89).
   *
+  *      Tony Mason, Stanford University, 21-Mar-89                      *
+  *              Added IP Multicast changes                              *
+  *									*
   *	15-Jan-88	lp
   *		Merge of final 43BSD changes. Use new memory allocation
   *	scheme for mbufs.
***************
*** 145,150 ****
--- 149,165 ----
  	so->ref = 0;
  	if(rp->rcb_options)
  		m_freem(dtom(rp->rcb_options));
+ #ifdef MULTICAST
+ #ifdef INET
+ 	{
+ 	extern struct socket *ip_mrouter;
+ 	if (so == ip_mrouter)
+ 		ip_mrouter_done();
+ 	if (rp->rcb_proto.sp_family == AF_INET)
+ 		ip_freemoptions(rp->rcb_moptions);
+ 	}
+ #endif INET
+ #endif MULTICAST
  	KM_FREE(rp, KM_PCB);
  	sofree(so);
  
===================================================================
Index: net/net/raw_cb.h
***************
*** 1,4 ****
--- 1,5 ----
  /* static	char	*sccsid = "@(#)raw_cb.h	5.1	(ULTRIX)	3/30/91"; */
+ 						/* plus MULTICAST 1.0 */
  /************************************************************************
   *									*
   *			Copyright (c) 1984,1988 by			*
***************
*** 53,58 ****
--- 54,60 ----
  	struct 	mbuf *rcb_options;	/* protocol specific options */
  	struct	route rcb_route;	/* routing information */
  	short	rcb_flags;
+ 	struct	mbuf *rcb_moptions;	/* proto specific multicast options */
  };
  
  /*
===================================================================
Index: net/netinet/if_ether.c
***************
*** 1,5 ****
--- 1,6 ----
  #ifndef lint
  static	char	*sccsid = "@(#)if_ether.c	5.1	(ULTRIX)	3/30/91";
+ 						/* plus MULTICAST 1.1 */
  #endif lint
  
  /************************************************************************
***************
*** 46,51 ****
--- 47,55 ----
   *	3-Mar-89	U. Sinkewicz
   *		Folded in reverse arp (plus pmax/smp).
   *
+  *      20-Mar-89  Tony Mason, Stanford Univ.
+  *              Added IP Multicast modifications.
+  *
   *	15-Jan-88	lp
   *		Merge of final 43BSD changes.
   *
***************
*** 156,161 ****
--- 160,169 ----
  #define	ARPT_KILLI	3	/* kill incomplete entry in 3 minutes */
  
  u_char	etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ #ifdef MULTICAST
+ u_char ether_ipmulticast_min[6] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 };
+ u_char ether_ipmulticast_max[6] = { 0x01, 0x00, 0x5e, 0x7f, 0xff, 0xff };
+ #endif MULTICAST
  extern struct ifnet loif;
  int useloopback = 1;
  
***************
*** 285,290 ****
--- 293,306 ----
  
  	*usetrailers = 0;
  
+ #ifdef MULTICAST
+ 	if (IN_MULTICAST(ntohl(destip->s_addr))) {
+ 		ETHER_MAP_IP_MULTICAST(destip, desten);
+ 		return(1);
+ 	}
+ 	else
+ #endif MULTICAST
+ 
  	if(!smp && at_cache && at_cache->at_iaddr.s_addr == destip->s_addr) {
  		s = splimp();
  		smp_lock(&lk_arptab, LK_RETRY);
***************
*** 841,843 ****
--- 857,1024 ----
          return (0);
  }
  #endif GATEWAY && mips
+ 
+ #ifdef MULTICAST
+ /*
+  * Add an Ethernet multicast address or range of addresses to the list for a
+  * given interface.
+  */
+ ether_addmulti(ifr, ac)
+ 	struct ifreq *ifr;
+ 	register struct arpcom *ac;
+ {
+ 	register struct ether_multi *enm;
+ 	struct sockaddr_in *sin;
+ 	u_char addrlo[6];
+ 	u_char addrhi[6];
+ 	struct mbuf *m;
+ 	int s = splimp();
+ 
+ 	switch (ifr->ifr_addr.sa_family) {
+ 	case AF_UNSPEC:
+ 		bcopy(ifr->ifr_addr.sa_data, addrlo, 6);
+ 		bcopy(addrlo, addrhi, 6);
+ 		break;
+ 
+ #ifdef INET
+ 	case AF_INET:
+ 		sin = (struct sockaddr_in *)&(ifr->ifr_addr);
+ 		if (sin->sin_addr.s_addr == INADDR_ANY) {
+ 			/*
+ 			 * An IP address of INADDR_ANY means listen to all
+ 			 * of the Ethernet multicast addresses used for IP.
+ 			 * (This is for the sake of IP multicast routers.)
+ 			 */
+ 			bcopy(ether_ipmulticast_min, addrlo, 6);
+ 			bcopy(ether_ipmulticast_max, addrhi, 6);
+ 		}
+ 		else {
+ 			ETHER_MAP_IP_MULTICAST(&sin->sin_addr, addrlo);
+ 			bcopy(addrlo, addrhi, 6);
+ 		}
+ 		break;
+ #endif INET
+ 
+ 	default:
+ 		splx(s);
+ 		return(EAFNOSUPPORT);
+ 	}
+ 
+ 	/*
+ 	 * Verify that we have valid Ethernet multicast addresses.
+ 	 */
+ 	if ((addrlo[0] & 0x01) != 1 || (addrhi[0] & 0x01) != 1) {
+ 		splx(s);
+ 		return(EINVAL);
+ 	}
+ 	/*
+ 	 * See if the address range is already in the list.
+ 	 */
+ 	ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm);
+ 	if (enm != NULL) {
+ 		/*
+ 		 * Found it; just increment the reference count.
+ 		 */
+ 		++enm->enm_refcount;
+ 		splx(s);
+ 		return(0);
+ 	}
+ 	/*
+ 	 * New address or range; get an mbuf for a new multicast record
+ 	 * and link it into the interface's multicast list.
+ 	 */
+ 	if ((m = m_getclr(M_DONTWAIT, MT_IFMADDR)) == NULL) {
+ 		splx(s);
+ 		return(ENOBUFS);
+ 	}
+ 	enm = mtod(m, struct ether_multi *);
+ 	bcopy(addrlo, enm->enm_addrlo, 6);
+ 	bcopy(addrhi, enm->enm_addrhi, 6);
+ 	enm->enm_ac = ac;
+ 	enm->enm_refcount = 1;
+ 	enm->enm_next = ac->ac_multiaddrs;
+ 	ac->ac_multiaddrs = enm;
+ 	splx(s);
+ 	/*
+ 	 * Return ENETRESET to inform the driver that the list has changed
+ 	 * and its reception filter should be adjusted accordingly.
+ 	 */
+ 	return(ENETRESET);
+ }
+ 
+ /*
+  * Delete a multicast address record.
+  */
+ ether_delmulti(ifr, ac)
+ 	struct ifreq *ifr;
+ 	register struct arpcom *ac;
+ {
+ 	register struct ether_multi *enm;
+ 	register struct ether_multi **p;
+ 	struct sockaddr_in *sin;
+ 	u_char addrlo[6];
+ 	u_char addrhi[6];
+ 	int s = splimp();
+ 
+ 	switch (ifr->ifr_addr.sa_family) {
+ 
+ 	case AF_UNSPEC:
+ 		bcopy(ifr->ifr_addr.sa_data, addrlo, 6);
+ 		bcopy(addrlo, addrhi, 6);
+ 		break;
+ 
+ #ifdef INET
+ 	case AF_INET:
+ 		sin = (struct sockaddr_in *)&(ifr->ifr_addr);
+ 		if (sin->sin_addr.s_addr == INADDR_ANY) {
+ 			/*
+ 			 * An IP address of INADDR_ANY means stop listening
+ 			 * to the range of Ethernet multicast addresses used
+ 			 * for IP.
+ 			 */
+ 			bcopy(ether_ipmulticast_min, addrlo, 6);
+ 			bcopy(ether_ipmulticast_max, addrhi, 6);
+ 		}
+ 		else {
+ 			ETHER_MAP_IP_MULTICAST(&sin->sin_addr, addrlo);
+ 			bcopy(addrlo, addrhi, 6);
+ 		}
+ 		break;
+ #endif INET
+ 
+ 	default:
+ 		splx(s);
+ 		return(EAFNOSUPPORT);
+ 	}
+ 
+ 	/*
+ 	 * Look up the address in our list.
+ 	 */
+ 	ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm);
+ 	if (enm == NULL) {
+ 		splx(s);
+ 		return(ENXIO);
+ 	}
+ 	if (--enm->enm_refcount != 0) {
+ 		/*
+ 		 * Still some claims to this record.
+ 		 */
+ 		splx(s);
+ 		return(0);
+ 	}
+ 	/*
+ 	 * No remaining claims to this record; unlink and free it.
+ 	 */
+ 	for (p = &enm->enm_ac->ac_multiaddrs;
+ 	     *p != enm;
+ 	     p = &((*p)->enm_next));
+ 	*p = (*p)->enm_next;
+ 	m_free(dtom(enm));
+ 	splx(s);
+ 	/*
+ 	 * Return ENETRESET to inform the driver that the list has changed
+ 	 * and its reception filter should be adjusted accordingly.
+ 	 */
+ 	return(ENETRESET);
+ }
+ #endif MULTICAST
===================================================================
Index: net/netinet/if_ether.h
***************
*** 1,4 ****
--- 1,5 ----
  /* static	char	*sccsid = "@(#)if_ether.h	5.1	(ULTRIX)	3/30/91"; */
+ 						/* plus MULTICAST 1.0 */
  
  /************************************************************************
   *									*
***************
*** 31,36 ****
--- 32,39 ----
  /************************************************************************
   *			Modification History				*
   *									*
+  *      20-Mar-89 Tony Mason, Stanford University
+  *              Added IP Multicast support
   *	
   *	15-Jan-88	lp
   *		Merge of final 43BSD changes.
***************
*** 86,92 ****
--- 89,114 ----
  #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 bits of the Ethernet address are statically assigned,
+  * and the low-order 23 bits 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 KERNEL
+ 
+ /*
   * Ethernet Address Resolution Protocol.
   *
   * See RFC 826 for protocol description.  Structure below is adapted
***************
*** 119,124 ****
--- 141,147 ----
  	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 multicast addrs */
  };
  
  /*
***************
*** 139,141 ****
--- 162,232 ----
  extern int nFDDI; /* defined conf.c */
  char *ether_sprintf();
  #endif
+ 
+ /*
+  * 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 pointer to arpcom        */
+ 	u_int               enm_refcount; /* no. claims to this addr/range */
+ 	struct ether_multi *enm_next;	  /* ptr to next ether_multi       */
+ };
+ 
+ #ifdef KERNEL
+ /*
+  * 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 KERNEL
===================================================================
Index: net/netinet/in.c
***************
*** 1,5 ****
--- 1,6 ----
  #ifndef lint
  static char *sccsid = "@(#)in.c	5.1		(ULTRIX)	3/30/91";
+ 						/* plus MULTICAST 1.2 */
  #endif lint
  
  /************************************************************************
***************
*** 50,55 ****
--- 51,59 ----
   *		Changed kmalloc flag to nowait.  Added assertions of 
   *		lk_in_ifaddr and lk_ifnet.
   *	
+  *      Tony Mason, Stanford University, 21-Mar-89                      *
+  *              Added IP Multicast changes                              *
+  *									*
   *	15-Jan-88	lp
   *		Merge of final 43BSD changes.
   *
***************
*** 170,175 ****
--- 174,183 ----
  		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 MULTICAST
  	else
  		return (0);
  
***************
*** 205,210 ****
--- 213,223 ----
  	} 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 MULTICAST
  	} else
  		return (i);
  
***************
*** 544,549 ****
--- 557,574 ----
  			(int)SIOCADDRT, RTF_UP);
  	}
  	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 MULTICAST
  	return (0);
  }
  
***************
*** 616,620 ****
--- 641,751 ----
  		    lia = ia;
  		}
  }
+ 
+ #ifdef MULTICAST
+ /*
+  * Add an address to the list of IP multicast addresses for a given interface.
+  */
+ struct in_multi *
+ in_addmulti(addr, ifp)
+ 	register struct in_addr addr;
+ 	register struct ifnet *ifp;
+ {
+ 	register struct in_multi *inm;
+ 	struct ifreq ifr;
+ 	struct in_ifaddr *ia;
+ 	struct mbuf *m;
+ 	int s = splnet();
+ 
+ 	/*
+ 	 * See if address already in list.
+ 	 */
+ 	IN_LOOKUP_MULTI(addr, ifp, inm);
+ 	if (inm != NULL) {
+ 		/*
+ 		 * Found it; just increment the reference count.
+ 		 */
+ 		++inm->inm_refcount;
+ 	}
+ 	else {
+ 		/*
+ 		 * New address; get an mbuf for a new multicast record
+ 		 * and link it into the interface's multicast list.
+ 		 */
+ 		if ((m = m_getclr(M_DONTWAIT, MT_IPMADDR)) == NULL) {
+ 			splx(s);
+ 			return(NULL);
+ 		}
+ 		inm = mtod(m, struct in_multi *);
+ 		inm->inm_addr = addr;
+ 		inm->inm_ifp = ifp;
+ 		inm->inm_refcount = 1;
+ 		IFP_TO_IA(ifp, ia);
+ 		if (ia == NULL) {
+ 			m_free(m);
+ 			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 = addr;
+ 		if (ifp->if_ioctl == NULL ||
+ 		    (*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) !=  0) {
+ 			m_free(m);
+ 			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.
+  */
+ 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));
+ 		*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);
+ 		m_free(dtom(inm));
+ 	}
+ 	splx(s);
+ }
+ #endif MULTICAST
  #endif INET
  
===================================================================
Index: net/netinet/in.h
***************
*** 1,4 ****
--- 1,5 ----
  /* static	char	*sccsid = "@(#)in.h	5.1	(ULTRIX)	3/30/91"; */
+ 						/* plus MULTICAST 1.1 */
  
  /************************************************************************
   *									*
***************
*** 35,40 ****
--- 36,44 ----
   *	15-Aug-89	jsd
   *		Add HELLO proto entry
   *
+  *      Tony Mason, Stanford University, 21-Mar-89                      *
+  *              Added IP Multicast changes                              *
+  *									*
   *	15-Jan-88	lp
   *		Merge of final 43BSD changes.
   *
***************
*** 73,79 ****
   */
  #define	IPPROTO_IP		0		/* dummy for IP */
  #define	IPPROTO_ICMP		1		/* control message protocol */
! #define	IPPROTO_GGP		2		/* gateway^2 (deprecated) */
  #define	IPPROTO_TCP		6		/* tcp */
  #define	IPPROTO_EGP		8		/* exterior gateway protocol */
  #define	IPPROTO_PUP		12		/* pup */
--- 77,84 ----
   */
  #define	IPPROTO_IP		0		/* dummy for IP */
  #define	IPPROTO_ICMP		1		/* control message protocol */
! #define	IPPROTO_IGMP		2		/* group mgmt protocol */
! #define	IPPROTO_GGP		3		/* gateway^2 (deprecated) */
  #define	IPPROTO_TCP		6		/* tcp */
  #define	IPPROTO_EGP		8		/* exterior gateway protocol */
  #define	IPPROTO_PUP		12		/* pup */
***************
*** 137,142 ****
--- 142,150 ----
  #define	IN_CLASSC_HOST		0x000000ff
  
  #define IN_CLASSD(i)            (((long)(i) & 0xf0000000) == 0xe0000000)
+ #define	IN_CLASSD_NET		0xf0000000	/* These ones aren't really */
+ #define	IN_CLASSD_NSHIFT	28		/* net and host fields, but */
+ #define	IN_CLASSD_HOST		0x0fffffff	/* routing needn't know.    */
  #define IN_MULTICAST(i)         IN_CLASSD(i)
  
  #define IN_EXPERIMENTAL(i)      (((long)(i) & 0xe0000000) == 0xe0000000)
***************
*** 144,149 ****
--- 152,162 ----
  
  #define	INADDR_ANY		0x00000000
  #define	INADDR_BROADCAST	0xffffffff
+ 
+ #define	INADDR_UNSPEC_GROUP	(u_long)0xe0000000	/* 224.0.0.0   */
+ #define	INADDR_ALLHOSTS_GROUP	(u_long)0xe0000001	/* 224.0.0.1   */
+ #define	INADDR_MAX_LOCAL_GROUP 	(u_long)0xe00000ff	/* 224.0.0.255 */
+ 
  #ifndef KERNEL
  #define INADDR_NONE             0xffffffff              /* -1 return */
  #endif
***************
*** 188,193 ****
--- 201,223 ----
   * Options for use with [gs]etsockopt at the IP level.
   */
  #define IP_OPTIONS      1               /* set/get IP per-packet options */
+ #define	IP_MULTICAST_IF		2	/* set/get IP multicast interface  */
+ #define	IP_MULTICAST_TTL	3	/* set/get IP multicast timetolive */
+ #define	IP_MULTICAST_LOOP	4	/* set/get IP multicast loopback   */
+ #define	IP_ADD_MEMBERSHIP	5	/* add  an IP group membership     */
+ #define	IP_DROP_MEMBERSHIP	6	/* 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 sends 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 __mips
  #ifdef KERNEL
===================================================================
Index: net/netinet/in_pcb.c
***************
*** 1,5 ****
--- 1,6 ----
  #ifndef lint
  static char *sccsid = "@(#)in_pcb.c	5.1	(ULTRIX)		3/30/91";
+ 						/* plus MULTICAST 1.0 */
  #endif lint
  
  /************************************************************************
***************
*** 46,51 ****
--- 47,55 ----
   *	13-Feb-89	Ursula Sinkewicz
   *		Added lk_ifnet and lk_in_ifaddr.	
   *
+  *      Tony Mason, Stanford University, 21-Mar-89                      *
+  *              Added IP Multicast changes                              *
+  *									*
   *	15-Jan-88	lp
   *		Merge of final 43BSD changes. Use new memory allocation
   *	scheme for mbufs.
***************
*** 84,89 ****
--- 88,97 ----
  #include "../net/netinet/tcp_timer.h"	/* SMP */
  #include "../net/netinet/tcp_var.h"		/* SMP */
  #include "../h/protosw.h"
+ #ifdef MULTICAST
+ #include "ip.h"
+ #include "ip_var.h"
+ #endif MULTICAST
  
  struct	in_addr zeroin_addr;
  extern struct  lock_t  lk_udb;
***************
*** 281,286 ****
--- 289,315 ----
  			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;
+ 
+ 			imo = mtod(inp->inp_moptions, struct ip_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 MULTICAST
  		ifaddr = (struct sockaddr_in *)&ia->ia_addr;
  	}
  	if (in_pcblookup(inp->inp_head,
***************
*** 332,337 ****
--- 361,369 ----
  	if (inp->inp_route.ro_rt)
  		rtfree(inp->inp_route.ro_rt);
  	RTUNLOCK();
+ #ifdef MULTICAST
+ 	ip_freemoptions(inp->inp_moptions);
+ #endif MULTICAST
  	remque(inp);
  	KM_FREE(inp, KM_PCB);
  	so->so_pcb = 0;
***************
*** 470,476 ****
--- 502,510 ----
  			else if (inp->inp_laddr.s_addr != laddr.s_addr)
  				continue;
  		} else {
+ #ifndef MULTICAST
  			if (laddr.s_addr != INADDR_ANY)
+ #endif MULTICAST
  				wildcard++;
  		}
  		if (inp->inp_faddr.s_addr != INADDR_ANY) {
===================================================================
Index: net/netinet/in_pcb.h
***************
*** 1,4 ****
--- 1,5 ----
  /* static	char	*sccsid = "@(#)in_pcb.h	5.1	(ULTRIX)	3/30/91"; */
+ 						/* plus MULTICAST 1.0 */
  
  /************************************************************************
   *									*
***************
*** 66,71 ****
--- 67,73 ----
  	caddr_t	inp_ppcb;		/* pointer to per-protocol pcb */
  	struct	route inp_route;	/* placeholder for routing entry */
  	struct  mbuf *inp_options;      /* IP options */
+ 	struct	mbuf *inp_moptions;	/* IP multicast options */
  };
  
  #define	INPLOOKUP_WILDCARD	1
===================================================================
Index: net/netinet/in_proto.c
***************
*** 1,5 ****
--- 1,6 ----
  #ifndef lint
  static	char	*sccsid = "@(#)in_proto.c	5.1	(ULTRIX)	3/30/91";
+ 						/* plus MULTICAST 1.0 */
  #endif lint
  
  /************************************************************************
***************
*** 39,44 ****
--- 40,48 ----
   *      05-May-89       Michael G. Mc Menemy
   *              Add XTI Support.
   *
+  *      Tony Mason, Stanford University, 21-Mar-89                      *
+  *              Added IP Multicast changes                              *
+  *									*
   *	15-Jan-88	lp
   *		Merge of final 43BSD changes.
   *
***************
*** 75,80 ****
--- 79,87 ----
  int	ip_init(),ip_slowtimo(),ip_drain();
  int	ip_ifoutput(),	ip_ifinput(),	ip_ifioctl();
  int	icmp_input();
+ #ifdef MULTICAST
+ int	igmp_init(),igmp_input(),igmp_fasttimo();
+ #endif MULTICAST
  int	udp_input(),udp_ctlinput();
  int	udp_usrreq();
  #ifdef XTI
***************
*** 145,150 ****
--- 152,164 ----
    0,		0,		0,		0,
    0,		0,		0,
  },
+ #ifdef MULTICAST
+ { SOCK_RAW,	&inetdomain,	IPPROTO_IGMP,	PR_ATOMIC|PR_ADDR,
+   igmp_input,	rip_output,	0,		rip_ctloutput,
+   raw_usrreq,
+   igmp_init,	igmp_fasttimo,	0,		0,
+ },
+ #endif MULTICAST
  #ifdef NSIP
  { SOCK_RAW,	&inetdomain,	IPPROTO_IDP,	PR_ATOMIC|PR_ADDR,
    idpip_input,	rip_output,	nsip_ctlinput,	0,
===================================================================
Index: net/netinet/in_var.h
***************
*** 1,4 ****
--- 1,5 ----
  /* sccsid  =  @(#)in_var.h	5.1	ULTRIX		3/30/91 */
+ 						/* plus MULTICAST 1.0 */
  
  /************************************************************************
   *									*
***************
*** 68,73 ****
--- 69,75 ----
  	struct	in_addr ia_netbroadcast; /* broadcast addr for (logical) net */
  	int	ia_flags;
  	struct	in_ifaddr *ia_next;	/* next in list of internet addresses */
+ 	struct	in_multi *ia_multiaddrs /* list of multicast addresses */
  };
  /*
   * Given a pointer to an in_ifaddr (ifaddr),
***************
*** 84,86 ****
--- 86,200 ----
  extern	struct in_ifaddr *in_iaonnetof();
  extern  struct lock_t lk_in_ifaddr; /* SMP: in_ifaddr lock */
  #endif
+ 
+ #ifdef KERNEL
+ /*
+  * 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);						  \
+ 	(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);					  \
+ }
+ #endif KERNEL
+ 
+ /*
+  * 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);				    \
+ }
+ 
+ /*
+  * 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();
+ #endif KERNEL
===================================================================
Index: net/netinet/ip_icmp.c
***************
*** 1,5 ****
--- 1,6 ----
  #ifndef lint
  static  char    *sccsid = "@(#)ip_icmp.c	5.1          (ULTRIX)                3/30/91";
+ 						/* plus MULTICAST 1.0 */
  #endif lint
  
  /************************************************************************
***************
*** 50,55 ****
--- 51,59 ----
   *	3-Mar-89	U. Sinkewicz
   *		Put new directory layout into smp file.
   *	
+  *      Tony Mason, Stanford University, 21-Mar-89                      *
+  *              Added IP Multicast changes                              *
+  *									*
   *	15-Jan-88	lp
   *		Merge of final 43BSD changes.
   *
***************
*** 153,158 ****
--- 157,171 ----
  		goto free;
  	}
  
+ #ifdef MULTICAST
+ 	/*
+ 	 * Don't send error in response to a multicast or broadcast packet.
+ 	 */
+ 	if (IN_MULTICAST(ntohl(oip->ip_dst.s_addr)) ||
+ 	    in_broadcast(oip->ip_dst))
+ 		goto free;
+ #endif MULTICAST
+ 	
  	/*
  	 * First, formulate icmp message
  	 */
===================================================================
Index: net/netinet/ip_input.c
***************
*** 1,5 ****
--- 1,6 ----
  #ifndef lint
  static char *sccsid = "@(#)ip_input.c	5.1	(ULTRIX)	3/30/91";
+ 						/* plus MULTICAST 1.1 */
  #endif lint
  
  /************************************************************************
***************
*** 65,70 ****
--- 66,74 ----
   *	13-Feb-89	Ursula Sinkewicz
   *		SMP: Added lk_in_ifaddr and lk_ifnet.
   *	
+  *      Tony Mason, Stanford University, 21-Mar-89                      *
+  *              Added IP Multicast changes                              *
+  *									*
   *	15-Jan-88	lp
   *		Merge of final 43BSD changes. Use new memory allocation
   *	scheme for mbufs.
***************
*** 321,326 ****
--- 325,376 ----
  		    }
  		}
  	}
+ #ifdef MULTICAST
+ 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+ 		struct in_multi *inm;
+ 		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, ifp) != 0) {
+ 				m_freem(dtom(ip));
+ 				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;
+ 		}
+ 		/*
+ 		 * See if we belong to the destination multicast group on the
+ 		 * arrival interface.
+ 		 */
+ 		IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm);
+ 		if (inm == NULL) {
+ 			m_freem(dtom(ip));
+ 			goto next;
+ 		}
+ 		goto ours;
+ 	}
+ #endif MULTICAST
  	if (dst.s_addr == (u_long)INADDR_BROADCAST)
  		goto ours;
  	if (dst.s_addr == INADDR_ANY)
===================================================================
Index: net/netinet/ip_output.c
***************
*** 1,5 ****
--- 1,6 ----
  #ifndef lint
  static	char	*sccsid = "@(#)ip_output.c	5.1	(ULTRIX)	3/30/91";
+ 						/* plus MULTICAST 1.2 */
  #endif lint
  
  /************************************************************************
***************
*** 33,38 ****
--- 34,42 ----
  /************************************************************************
   *			Modification History				*
   *
+  * 91/05/29     Mark J. Steiglitz, Stanford
+  *              IP Multicast modifications from Ultrix 3.1 to Ultrix 4.1
+  *
   *	02-Aug-90	lp
   *		Fix ROUTETOIF case in route cache extensions.
   *
***************
*** 78,83 ****
--- 82,90 ----
   * 	13 Feb 89	Ursula Sinkewicz
   *		SMP: Added lk_in_ifaddr, lk_ifnet.
   *
+  *      Tony Mason, Stanford University, 21-Mar-89                      *
+  *              Added IP Multicast changes                              *
+  *									*
   *	15-Jan-88	lp
   *		Merge of final 43BSD changes.
   *
***************
*** 143,154 ****
--- 150,168 ----
   * control of the socket lock.
   */
  
+ #ifdef MULTICAST
+ ip_output(m, opt, ro, flags, so, mopts)
+ #else
  ip_output(m, opt, ro, flags, so)
+ #endif MULTICAST
  	struct mbuf *m;
  	struct mbuf *opt;
  	struct route *ro;
  	int flags;
  	struct socket *so;
+ #ifdef MULTICAST
+ 	struct mbuf *mopts;
+ #endif MULTICAST
  {
  	register struct ip *ip;
  	register struct ifnet *ifp;
***************
*** 240,245 ****
--- 254,350 ----
  	}
  	RTUNLOCK();
  
+ #ifdef MULTICAST
+ 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+ 		struct ip_moptions *imo;
+ 		struct in_multi *inm;
+ 		extern struct ifnet loif;
+ 		extern struct socket *ip_mrouter;
+ 		/*
+ 		 * 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) && mopts != NULL) {
+ 			imo = mtod(mopts, struct ip_moptions *);
+ 			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);
+ 		}
+ 		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) != 0) {
+ 				m_freem(m);
+ 				goto done;
+ 			}
+ 		}
+ 		/*
+ 		 * 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 MULTICAST
+ 
  	/*
  	 * Look for broadcast address and
  	 * and verify user is allowed to send
***************
*** 280,285 ****
--- 385,393 ----
  			}
  	}
  
+ #ifdef MULTICAST
+ sendit:
+ #endif MULTICAST
  	/*
  	 * If small enough for interface, can just send directly.
  	 */
***************
*** 537,542 ****
--- 645,659 ----
  		case IP_OPTIONS:
  			return (ip_pcbopts(&inp->inp_options, *m));
  
+ #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, &inp->inp_moptions, *m);
+ 			break;
+ #endif MULTICAST
  		default:
  			error = EINVAL;
  			break;
***************
*** 559,564 ****
--- 676,690 ----
  			} else
  				(*m)->m_len = 0;
  			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, m);
+ 			break;
+ #endif MULTICAST
  		default:
  			error = EINVAL;
  			break;
***************
*** 815,817 ****
--- 941,1280 ----
  	}
  	return (1);
  }
+ 
+ #ifdef MULTICAST
+ /*
+  * Set the IP multicast options in response to user setsockopt().
+  */
+ ip_setmoptions(optname, mopts, m)
+ 	int optname;
+ 	struct mbuf **mopts;
+ 	struct mbuf *m;
+ {
+ 	int error = 0;
+ 	struct ip_moptions *imo;
+ 	u_char loop;
+ 	int i;
+ 	struct in_addr addr;
+ 	struct ip_mreq *mreq;
+ 	struct ifnet *ifp;
+ 	struct route ro;
+ 	struct sockaddr_in *dst;
+ 
+ 	if (*mopts == NULL) {
+ 		/*
+ 		 * No multicast option buffer attached to the pcb;
+ 		 * allocate one and initialize to default values.
+ 		 */
+ 		MGET(*mopts, M_WAIT, MT_IPMOPTS);
+ 		if (*mopts == NULL)
+ 			return (ENOBUFS);
+ 		imo = mtod(*mopts, struct ip_moptions *);
+ 		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;
+ 	}
+ 
+ 	imo = mtod(*mopts, struct ip_moptions *);
+ 
+ 	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_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) {
+ 		m_free(*mopts);
+ 		*mopts = NULL;
+ 	}
+ 
+ 	return(error);
+ }
+ 
+ /*
+  * Return the IP multicast options in response to user getsockopt().
+  */
+ ip_getmoptions(optname, mopts, m)
+ 	int optname;
+ 	struct mbuf *mopts;
+ 	struct mbuf **m;
+ {
+ 	u_char *ttl;
+ 	u_char *loop;
+ 	struct in_addr *addr;
+ 	struct ip_moptions *imo;
+ 	struct in_ifaddr *ia;
+ 
+ 	*m = m_get(M_WAIT, MT_IPMOPTS);
+ 
+ 	imo = (mopts == NULL) ? NULL : mtod(mopts, struct ip_moptions *);
+ 
+ 	switch (optname) {
+ 
+ 	case IP_MULTICAST_IF:
+ 		addr = mtod(*m, struct in_addr *);
+ 		(*m)->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(*m, u_char *);
+ 		(*m)->m_len = 1;
+ 		*ttl = (imo == NULL) ? IP_DEFAULT_MULTICAST_TTL
+ 				     : imo->imo_multicast_ttl;
+ 		return(0);
+ 
+ 	case IP_MULTICAST_LOOP:
+ 		loop = mtod(*m, u_char *);
+ 		(*m)->m_len = 1;
+ 		*loop = (imo == NULL) ? IP_DEFAULT_MULTICAST_LOOP
+ 				      : imo->imo_multicast_loop;
+ 		return(0);
+ 
+ 	default:
+ 		return(EOPNOTSUPP);
+ 	}
+ }
+ 
+ /*
+  * Discard the IP multicast options.
+  */
+ ip_freemoptions(mopts)
+ 	struct mbuf *mopts;
+ {
+ 	struct ip_moptions *imo;
+ 	int i;
+ 
+ 	if (mopts != NULL) {
+ 		imo = mtod(mopts, struct ip_moptions *);
+ 		for (i = 0; i < imo->imo_num_memberships; ++i)
+ 			in_delmulti(imo->imo_membership[i]);
+ 		m_free(mopts);
+ 	}
+ }
+ 
+ /*
+  * 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.
+  */
+ 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);
+ 	}
+ }
+ 
+ #endif MULTICAST
===================================================================
Index: net/netinet/ip_var.h
***************
*** 1,4 ****
--- 1,5 ----
  /* static	char	*sccsid = "@(#)ip_var.h	5.1		(ULTRIX)	3/30/91"; */
+ 						/* plus MULTICAST 1.0 */
  
  /************************************************************************
   *									*
***************
*** 41,46 ****
--- 42,50 ----
   *	3-Mar-89	U. Sinkewicz
   *		Added new directory layout, pmax to smp file.
   *	
+  *      Tony Mason, Stanford University, 21-Mar-89                      *
+  *              Added IP Multicast changes                              *
+  *									*
   *	15-Jan-88	lp
   *		Merge of final 43BSD changes.
   *
***************
*** 137,142 ****
--- 141,158 ----
  	char	ipopt_list[MAX_IPOPTLEN];	/* options proper */
  };
  
+ /*
+  * Structure stored in an mbuf 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 */
***************
*** 162,167 ****
--- 178,184 ----
  #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 */
  
===================================================================
Index: net/netinet/raw_ip.c
***************
*** 33,38 ****
--- 33,41 ----
  /************************************************************************
   *			Modification History				*
   *									*
+  * 91/05/29     Mark J. Steiglitz, Stanford
+  *              IP Multicast modifications from Ultrix 3.1 to Ultrix 4.1
+  *
   *	Uttam Shikarpur - 10/17/89    					*
   *		Changed the value of MAXTTL from a manifest constant    *
   *		to an variable (an integer.)				*
***************
*** 53,59 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	raw_ip.c	6.6 (Berkeley) 6/8/85
   */
  
  #include "../h/param.h"
--- 56,62 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	raw_ip.c	6.6 (Berkeley) 6/8/85 plus MULTICAST 1.2
   */
  
  #include "../h/param.h"
***************
*** 72,77 ****
--- 75,84 ----
  #include "../net/netinet/ip.h"
  #include "../net/netinet/ip_var.h"
  
+ #ifdef MULTICAST
+ #include "../net/netinet/in_var.h"
+ #endif MULTICAST
+ 
  /*
   * Raw interface to IP protocol.
   */
***************
*** 114,119 ****
--- 121,136 ----
  			panic("rip_output: not lock owner\n");
  	}
  
+ #ifdef MULTICAST
+ 	/*
+ 	 * If the protocol is IPPROTO_RAW or IPPROTO_IGMP, the user
+ 	 * handed us a complete IP packet.  Otherwise, allocate an
+ 	 * mbuf for a header and fill it in as needed.  This is a
+ 	 * temporary hack, pending support for the IP_HDRINCL sockopt
+ 	 * in 4.4BSD.
+ 	 */
+ 	if (proto != IPPROTO_RAW && proto != IPPROTO_IGMP) {
+ #else
  	/*
  	 * if the protocol is IPPROTO_RAW, the user handed us a
  	 * complete IP packet.  Otherwise, allocate an mbuf for a
***************
*** 120,125 ****
--- 137,143 ----
  	 * header and fill it in as needed.
  	 */
  	if (proto != IPPROTO_RAW) {
+ #endif MULTICAST
  		/*
  		 * Calculate data length and get an mbuf
  		 * for IP header.
***************
*** 150,158 ****
  		ip->ip_p = proto;
  		ip->ip_len = sizeof(struct ip) + len;
  		ip->ip_ttl = maxttl;
! 	} else
  		ip = mtod(m, struct ip *);
! 
  	if (rp->rcb_flags & RAW_LADDR) {
  		sin = (struct sockaddr_in *)&rp->rcb_laddr;
  		if (sin->sin_family != AF_INET) {
--- 168,190 ----
  		ip->ip_p = proto;
  		ip->ip_len = sizeof(struct ip) + len;
  		ip->ip_ttl = maxttl;
! 	} else {
  		ip = mtod(m, struct ip *);
! #ifdef MULTICAST
! 		if (ip->ip_src.s_addr != 0) {
! 			/*
! 			 * Verify that the source address is one of ours.
! 			 */
! 			struct ifnet *ifp;
! 			INADDR_TO_IFP(ip->ip_src, ifp);
! 			if (ifp == NULL) {
! 				error = EADDRNOTAVAIL;
! 				goto bad;
! 			}
! 		}
! 		ip->ip_dst = ((struct sockaddr_in *)&rp->rcb_faddr)->sin_addr;
! #endif MULTICAST
! 	}
  	if (rp->rcb_flags & RAW_LADDR) {
  		sin = (struct sockaddr_in *)&rp->rcb_laddr;
  		if (sin->sin_family != AF_INET) {
***************
*** 163,170 ****
--- 195,208 ----
  	} else
  		ip->ip_src.s_addr = 0;
  	ip->ip_dst = ((struct sockaddr_in *)&rp->rcb_faddr)->sin_addr;
+ #ifdef MULTICAST
  	return (ip_output(m, rp->rcb_options, &rp->rcb_route, 
+ 	   (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST
+ 	   | IP_MULTICASTOPTS, so, rp->rcb_moptions));
+ #else
+ 	return (ip_output(m, rp->rcb_options, &rp->rcb_route, 
  	   (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST, so));
+ #endif MULTICAST
  bad:
  	m_freem(m);
  	return (error);
***************
*** 192,200 ****
--- 230,251 ----
  		case IP_OPTIONS:
  			return (ip_pcbopts(&rp->rcb_options, *m));
  
+ #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->rcb_moptions, *m);
+ 			break;
  		default:
+ 			error = ip_mrouter_cmd(optname, so, *m);
+ 			break;
+ #else
+ 		default:
  			error = EINVAL;
  			break;
+ #endif MULTICAST
  		}
  		break;
  
***************
*** 214,219 ****
--- 265,279 ----
  			} else
  				(*m)->m_len = 0;
  			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->rcb_moptions, m);
+ 			break;
+ #endif MULTICAST
  		default:
  			error = EINVAL;
  			break;
***************
*** 220,226 ****
  		}
  		break;
  	}
! 	if (op == PRCO_SETOPT && *m != NULL)
  		(void)m_free(*m);
  	return (error);
  }
--- 280,286 ----
  		}
  		break;
  	}
! 	if (op == PRCO_SETOPT && *m)
  		(void)m_free(*m);
  	return (error);
  }
===================================================================
Index: net/netinet/udp_usrreq.c
***************
*** 1,5 ****
--- 1,6 ----
  #ifndef lint
  static char *sccsid = "@(#)udp_usrreq.c	5.1    (ULTRIX)        3/30/91";
+ 						/* plus MULTICAST 1.2 */
  #endif lint
  
  /************************************************************************
***************
*** 33,38 ****
--- 34,42 ----
  /************************************************************************
   *			Modification History				*
   *									*
+  * 91/05/29     Mark J. Steiglitz, Stanford
+  *              IP Multicast modifications from Ultrix 3.1 to Ultrix 4.1
+  *
   *      25-Feb-91	jsd
   *              account for all input packets, including error packets
   *		
***************
*** 191,197 ****
--- 195,205 ----
  	struct ip ip;
  	struct	sockaddr_in udp_in; /* SMP */
  	
+ #ifdef MULTICAST
+ 	register struct inpcb *inp;
+ #else
  	volatile struct inpcb *inp;
+ #endif
  
  	udp_in.sin_family = AF_INET;
  	bzero(udp_in.sin_zero, 8);
***************
*** 247,252 ****
--- 255,348 ----
  		mod_ui++;
  	}
  
+ #ifdef MULTICAST
+ 	if (IN_MULTICAST(ntohl(ui->ui_dst.s_addr)) ||
+ 	    in_broadcast(ui->ui_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 = ui->ui_sport;
+ 		udp_in.sin_addr = ui->ui_src;
+ 		m->m_len -= sizeof (struct udpiphdr);
+ 		m->m_off += 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 != ui->ui_dport) {
+ 				continue;
+ 			}
+ 			if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ 				if (inp->inp_laddr.s_addr !=
+ 					ui->ui_dst.s_addr)
+ 					continue;
+ 			}
+ 			if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ 				if (inp->inp_faddr.s_addr !=
+ 					ui->ui_src.s_addr ||
+ 				    inp->inp_fport != ui->ui_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 MULTICAST
  	/*
  	 * Locate pcb for datagram.
  	 */
***************
*** 258,268 ****
--- 354,366 ----
  		INPLOOKUP_WILDCARD);
  loop1:	if (inp == 0) {
  		smp_unlock(&lk_udb);
+ #ifndef MULTICAST
  		/* don't send ICMP response for broadcast packet */
  		if (in_broadcast(ui->ui_dst)) {
  			UDPSTAT(udps_noports++);
  			goto bad;
  		}
+ #endif !MULTICAST
  		if (mod_ui)
  			*(struct ip *)ui = ip;
  		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT,
***************
*** 443,451 ****
--- 541,555 ----
  	((struct ip *)ui)->ip_ttl = udp_ttl;
  	UDPSTAT(udps_total++);
  	UDPSTAT(udps_outdatagrams++);
+ #ifdef MULTICAST
+ 	return (ip_output(m, inp->inp_options, &inp->inp_route,
+ 	 inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)
+ 	 | IP_MULTICASTOPTS, inp->inp_socket, inp->inp_moptions));
+ #else
  	return (ip_output(m, inp->inp_options, &inp->inp_route,
  	 inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST), 
  	 inp->inp_socket));
+ #endif MULTICAST
  }
  
  /* CJ - increased sendspace from 2K, recvspace from 4K */
===================================================================
Index: net/ipmulticast/igmp.c
***************
*** 0 ****
--- 1,288 ----
+ /*
+  * Internet Group Management Protocol (IGMP) routines.
+  *
+  * Written by Steve Deering, Stanford, May 1988.
+  *
+  * MULTICAST 1.1
+  *
+  * 91/05/29     Mark J. Steiglitz, Stanford
+  *              IP Multicast modifications from Ultrix 3.1 to Ultrix 4.1
+  */
+  
+ #ifdef MULTICAST
+ 
+ #include "../h/param.h"
+ #include "../h/mbuf.h"
+ #include "../h/socket.h"
+ #include "../h/protosw.h"
+ 
+ #include "../net/if.h"
+ #include "../net/route.h"
+ 
+ #include "../netinet/in.h"
+ #include "../netinet/in_var.h"
+ #include "../netinet/in_systm.h"
+ #include "../netinet/ip.h"
+ #include "../netinet/ip_var.h"
+ #include "../ipmulticast/igmp.h"
+ #include "../ipmulticast/igmp_var.h"
+ 
+ extern struct ifnet loif;
+ 
+ static struct sockproto   igmpproto = { AF_INET, IPPROTO_IGMP };
+ static struct sockaddr_in igmpsrc   = { AF_INET };
+ static struct sockaddr_in igmpdst   = { AF_INET };
+ 
+ static int                igmp_timers_are_running = 0;
+ static u_long             igmp_all_hosts_group;
+ 
+ igmp_init()
+ {
+ 	/*
+ 	 * To avoid byte-swapping the same value over and over again.
+ 	 */
+ 	igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
+ }
+ 
+ igmp_input(m, ifp)
+ 	register struct mbuf *m;
+ 	register struct ifnet *ifp;
+ {
+ 	register struct igmp *igmp;
+ 	register struct ip *ip;
+ 	register int igmplen;
+ 	register int iphlen;
+ 	register int minlen;
+ 	struct in_multi *inm;
+ 	struct in_multistep step;
+ 	struct in_ifaddr *ia;
+ 
+ 	++igmpstat.igps_rcv_total;
+ 
+ 	ip = mtod(m, struct ip *);
+ 	iphlen = ip->ip_hl << 2;
+ 	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_off > MMAXOFF || m->m_len < minlen) &&
+ 	    (m = m_pullup(m, minlen)) == 0) {
+ 		++igmpstat.igps_rcv_tooshort;
+ 		return;
+ 	}
+ 
+ 	/*
+ 	 * Validate checksum
+ 	 */
+ 	m->m_off += 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_off -= 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.
+ 	 */
+ 	igmpsrc.sin_addr = ip->ip_src;
+ 	igmpdst.sin_addr = ip->ip_dst;
+ 	raw_input(m, &igmpproto,
+ 		    (struct sockaddr *)&igmpsrc, (struct sockaddr *)&igmpdst);
+ }
+ 
+ igmp_joingroup(inm)
+ 	struct in_multi *inm;
+ {
+ 	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);
+ }
+ 
+ igmp_leavegroup(inm)
+ 	struct in_multi *inm;
+ {
+ 	/*
+ 	 * No action required on leaving a group.
+ 	 */
+ }
+ 
+ igmp_fasttimo()
+ {
+ 	register struct in_multi *inm;
+ 	struct in_multistep step;
+ 	int s;
+ 
+ 	/*
+ 	 * 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);
+ }
+ 
+ igmp_sendreport(inm)
+ 	struct in_multi *inm;
+ {
+ 	struct mbuf *m;
+ 	struct igmp *igmp;
+ 	struct ip *ip;
+ 	struct mbuf *mopts;
+ 	struct ip_moptions *imo;
+ 	extern struct socket *ip_mrouter;
+ 
+ 	MGET(m, M_DONTWAIT, MT_HEADER);
+ 	if (m == NULL)
+ 		return;
+ 	MGET(mopts, M_DONTWAIT, MT_IPMOPTS);
+ 	if (mopts == NULL) {
+ 		m_free(m);
+ 		return;
+ 	}
+ 	m->m_off = MMAXOFF - IGMP_MINLEN;
+ 	m->m_len = IGMP_MINLEN;
+ 	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_off -= sizeof(struct ip);
+ 	m->m_len += sizeof(struct ip);
+ 	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        = igmp->igmp_group;
+ 
+ 	imo = mtod(mopts, struct ip_moptions *);
+ 	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.
+ 	 */
+ 	imo->imo_multicast_loop = (ip_mrouter != NULL);
+ 
+ 	ip_output(m, (struct mbuf *)0, (struct route *)0,
+ 			IP_MULTICASTOPTS, (struct socket *)0, mopts);
+ 
+ 	m_free(mopts);
+ 	++igmpstat.igps_snd_reports;
+ }
+ 
+ #endif MULTICAST
===================================================================
Index: net/ipmulticast/igmp.h
***************
*** 0 ****
--- 1,27 ----
+ /*
+  * Internet Group Management Protocol (IGMP) definitions.
+  *
+  * Written by Steve Deering, Stanford, May 1988.
+  *
+  * MULTICAST 1.1
+  */
+ 
+ /*
+  * 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)          */
===================================================================
Index: net/ipmulticast/igmp_var.h
***************
*** 0 ****
--- 1,41 ----
+ /*
+  * 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 generate a "random" number by adding
+  * the total number of IP packets received, our primary IP address, and the
+  * multicast address being timed-out.  The 4.3 random() routine really
+  * ought to be available in the kernel!
+  */
+ #define IGMP_RANDOM_DELAY(multiaddr)					\
+ 	/* struct in_addr multiaddr; */					\
+ 	( (ipstat.ips_total +						\
+ 	   ntohl(IA_SIN(in_ifaddr)->sin_addr.s_addr) +			\
+ 	   ntohl((multiaddr).s_addr)					\
+ 	  )								\
+ 	  % (IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ) + 1		\
+ 	)
+ 
+ #endif KERNEL
===================================================================
Index: net/ipmulticast/ip_mroute.c
***************
*** 0 ****
--- 1,877 ----
+ /*
+  * 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
+  *
+  * 91/05/29     Mark J. Steiglitz, Stanford
+  *              IP Multicast modifications from Ultrix 3.1 to Ultrix 4.1
+  */
+ 
+ #ifdef MULTICAST
+ 
+ #include "../h/param.h"
+ #include "../h/mbuf.h"
+ #include "../h/socket.h"
+ #include "../h/socketvar.h"
+ #include "../h/protosw.h"
+ #include "../h/errno.h"
+ #include "../h/time.h"
+ #include "../h/ioctl.h"
+ #include "../h/syslog.h"
+ #include "../net/af.h"
+ #include "../net/if.h"
+ #include "../net/route.h"
+ #include "../net/raw_cb.h"
+ #include "../netinet/in.h"
+ #include "../netinet/in_pcb.h"
+ #include "../netinet/in_var.h"
+ #include "../netinet/in_systm.h"
+ #include "../netinet/ip.h"
+ #include "../netinet/ip_var.h"
+ #include "../ipmulticast/igmp.h"
+ #include "../ipmulticast/igmp_var.h"
+ #include "../ipmulticast/ip_mroute.h"
+ 
+ 
+ #ifndef MROUTING
+ /*
+  * Dummy routines and globals used when multicast routing is not compiled in.
+  */
+ 
+ struct socket  *ip_mrouter  = NULL;
+ u_int		ip_mrtproto = 0;
+ 
+ int
+ ip_mrouter_cmd(cmd, so, m)
+ 	int cmd;
+ 	struct socket *so;
+ 	struct mbuf *m;
+ {
+ 	return(EOPNOTSUPP);
+ }
+ 
+ int
+ ip_mrouter_done()
+ {
+ 	return(0);
+ }
+ 
+ int
+ ip_mforward(ip, ifp)
+ 	struct ip *ip;
+ 	struct ifnet *ifp;
+ {
+ 	return(0);
+ }
+ #else MROUTING
+ 
+ 
+ #define INSIZ		sizeof(struct in_addr)
+ #define	same(a1, a2) \
+ 	(bcmp((caddr_t)(a1), (caddr_t)(a2), INSIZ) == 0)
+ 
+ /*
+  * 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 mbuf    *mrttable[MRTHASHSIZ];
+ struct vif	viftable[MAXVIFS];
+ struct mrtstat	mrtstat;
+ u_int		mrtdebug = 0;	  /* debug level */
+ 
+ /*
+  * Private variables.
+  */
+ static vifi_t	   numvifs = 0;
+ static struct mrt *cached_mrt = NULL;
+ static u_long	   cached_origin;
+ static u_long	   cached_originmask;
+ 
+ /*
+  * Handle DVMRP setsockopt commands to modify the multicast routing tables.
+  */
+ int
+ ip_mrouter_cmd(cmd, so, m)
+     int cmd;
+     struct socket *so;
+     struct mbuf *m;
+ {
+    if (cmd != DVMRP_INIT && so != ip_mrouter) return EACCES;
+ 
+     switch (cmd) {
+ 	case DVMRP_INIT:     return ip_mrouter_init(so);
+ 	case DVMRP_DONE:     return ip_mrouter_done();
+ 	case DVMRP_ADD_VIF:  return add_vif (mtod(m, struct vifctl *));
+ 	case DVMRP_DEL_VIF:  return del_vif (mtod(m, short *));
+ 	case DVMRP_ADD_LGRP: return add_lgrp(mtod(m, struct lgrplctl *));
+ 	case DVMRP_DEL_LGRP: return del_lgrp(mtod(m, struct lgrplctl *));
+ 	case DVMRP_ADD_MRT:  return add_mrt (mtod(m, struct mrtctl *));
+ 	case DVMRP_DEL_MRT:  return del_mrt (mtod(m, struct mrtctl *));
+ 	default:             return EOPNOTSUPP;
+     }
+ }
+ 
+ /*
+  * Enable multicast routing
+  */
+ static int
+ ip_mrouter_init(so)
+     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;
+ 
+     if (mrtdebug)
+ 	log(LOG_DEBUG, "ip_mrouter_init");
+ 
+     return 0;
+ }
+ 
+ /*
+  * Disable multicast routing
+  */
+ int
+ ip_mrouter_done()
+ {
+     vifi_t vifi;
+     int i;
+     struct ifnet *ifp;
+     struct ifreq ifr;
+     int s;
+ 
+     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)) {
+ 	    m_freem(viftable[vifi].v_lcl_groups);
+ 	    ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_family = AF_INET;
+ 	    ((struct sockaddr_in *)&(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++)
+         m_freem(mrttable[i]);
+     bzero((caddr_t)mrttable, sizeof(mrttable));
+     cached_mrt = NULL;
+ 
+     ip_mrouter = NULL;
+ 
+     splx(s);
+ 
+     if (mrtdebug)
+ 	log(LOG_DEBUG, "ip_mrouter_done");
+ 
+     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;
+     static struct sockaddr_in sin = {AF_INET};
+     struct ifaddr *ifa;
+     struct ifnet *ifp;
+     struct ifreq ifr;
+     int error, s;
+ 
+     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(&sin);
+     if (ifa == 0) return EADDRNOTAVAIL;
+ 
+     s = splnet();
+ 
+     if (vifcp->vifc_flags & VIFF_TUNNEL) {
+ 	vifp->v_rmt_addr  = vifcp->vifc_rmt_addr;
+     }
+     else {
+ 	/* Make sure the interface supports multicast */
+ 	ifp = ifa->ifa_ifp;
+ 	if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ 	    splx(s);
+ 	    return EOPNOTSUPP;
+ 	}
+ 	/* Enable promiscuous reception of all IP multicasts from the if */
+ 	((struct sockaddr_in *)&(ifr.ifr_addr))->sin_family = AF_INET;
+ 	((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
+ 	error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr);
+ 	if (error) {
+ 	    splx(s);
+ 	    return error;
+ 	}
+     }
+ 
+     vifp->v_flags     = vifcp->vifc_flags;
+     vifp->v_threshold = vifcp->vifc_threshold;
+     vifp->v_lcl_addr  = vifcp->vifc_lcl_addr;
+     vifp->v_ifp       = ifa->ifa_ifp;
+ 
+     /* Adjust numvifs up if the vifi is higher than numvifs */
+     if (numvifs <= vifcp->vifc_vifi) numvifs = vifcp->vifc_vifi + 1;
+ 
+     splx(s);
+ 
+     if (mrtdebug)
+ 	log(LOG_DEBUG, "add_vif #%d, lcladdr %x, %s %x, thresh %x",
+ 	    vifcp->vifc_vifi, 
+ 	    ntohl(vifcp->vifc_lcl_addr.s_addr),
+ 	    (vifcp->vifc_flags & VIFF_TUNNEL) ? "rmtaddr" : "mask",
+ 	    ntohl(vifcp->vifc_rmt_addr.s_addr),
+ 	    vifcp->vifc_threshold);
+     
+     return 0;
+ }
+ 
+ /*
+  * Delete a vif from the vif table
+  */
+ static int
+ del_vif(vifip)
+     vifi_t *vifip;
+ {
+     register struct vif *vifp = viftable + *vifip;
+     register vifi_t vifi;
+     struct ifnet *ifp;
+     struct ifreq ifr;
+     int s;
+ 
+     if (*vifip >= numvifs) return EINVAL;
+     if (vifp->v_lcl_addr.s_addr == 0) return EADDRNOTAVAIL;
+ 
+     s = splnet();
+ 
+     if (!(vifp->v_flags & VIFF_TUNNEL)) {
+ 	m_freem(vifp->v_lcl_groups);
+ 	((struct sockaddr_in *)&(ifr.ifr_addr))->sin_family = AF_INET;
+ 	((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
+ 	ifp = vifp->v_ifp;
+ 	(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
+     }
+ 
+     bzero((caddr_t)vifp, sizeof (*vifp));
+ 
+     /* Adjust numvifs down */
+     for (vifi = numvifs - 1; vifi >= 0; vifi--)
+       if (viftable[vifi].v_lcl_addr.s_addr != 0) break;
+     numvifs = vifi + 1;
+ 
+     splx(s);
+ 
+     if (mrtdebug)
+       log(LOG_DEBUG, "del_vif %d, numvifs %d", *vifip, numvifs);
+ 
+     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 = viftable + gcp->lgc_vifi;
+     register struct mbuf *lp, *prev_lp;
+     int s;
+ 
+     if (mrtdebug)
+       log(LOG_DEBUG,"add_lgrp %x on %d",
+ 	  ntohl(gcp->lgc_gaddr.s_addr), gcp->lgc_vifi);
+ 
+     if (gcp->lgc_vifi >= numvifs) return EINVAL;
+     if (vifp->v_lcl_addr.s_addr == 0 ||
+        (vifp->v_flags & VIFF_TUNNEL)) return EADDRNOTAVAIL;
+ 
+     /* try to find a group list mbuf that has free space left */
+     for (lp = vifp->v_lcl_groups, prev_lp = NULL
+ 	 ; lp && (lp->m_len / INSIZ) == GRPLSTLEN
+          ; prev_lp = lp, lp = lp->m_next)
+       ;
+ 
+     s = splnet();
+ 
+     if (lp == NULL) {	    /* no group list mbuf with free space was found */
+ 	MGET(lp, M_DONTWAIT, MT_MRTABLE);
+ 	if (lp == NULL) {
+ 	  splx(s);
+ 	  return ENOBUFS;
+ 	}
+ 	if (prev_lp == NULL)
+ 	  vifp->v_lcl_groups = lp;
+ 	else
+ 	  prev_lp->m_next = lp;
+ 	lp->m_len = 0;
+     }
+ 
+     (mtod(lp,struct grplst *)->gl_gaddr)[lp->m_len / INSIZ].s_addr
+       = gcp->lgc_gaddr.s_addr;
+     lp->m_len += INSIZ;
+ 
+     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.
+  * Does not pullup the values from other mbufs in the list unless the
+  * current mbuf is totally empty.  This is a ~bug.
+  */
+ 
+ static int
+ del_lgrp(gcp)
+     register struct lgrplctl *gcp;
+ {
+     register struct vif *vifp = viftable + gcp->lgc_vifi;
+     register u_long i;
+     register struct mbuf *lp, *next_lp, *prev_lp = NULL;
+     register struct grplst *glp;
+     int cnt, s;
+ 
+     if (mrtdebug)
+       log(LOG_DEBUG,"del_lgrp %x on %d",
+ 	  ntohl(gcp->lgc_gaddr.s_addr), gcp->lgc_vifi);
+ 
+     if (gcp->lgc_vifi >= numvifs) return EINVAL;
+     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;
+ 
+     /* for all group list mbufs */
+     for (lp = vifp->v_lcl_groups; lp; lp = next_lp) {
+ 	/* for all group addrs in an mbuf */
+ 	for (cnt = lp->m_len / INSIZ, i = 0; i < cnt; i++)
+ 	  /* if this is the addr to delete */
+ 	  if (same(&gcp->lgc_gaddr,
+ 		   &mtod(lp,struct grplst *)->gl_gaddr[i])) {
+ 	      lp->m_len -= INSIZ;
+ 	      cnt--;       
+ 	      if (lp->m_len == 0)  {
+ 		  /* the mbuf is now empty */
+ 		  if (prev_lp) {
+ 		      MFREE(lp, prev_lp->m_next);
+ 		  } else {
+ 		      MFREE(lp, vifp->v_lcl_groups);
+ 		  }
+ 	      } else
+ 		/* move all other group addresses down one address */
+ 		/* djw- could use ovbcopy? */
+ 		for (glp = mtod(lp, struct grplst *); i < cnt; i++)
+ 		  glp->gl_gaddr[i] = glp->gl_gaddr[i + 1];
+ 	      splx(s);
+ 	      return 0;
+ 	  }
+ 	prev_lp = lp;
+ 	next_lp = lp->m_next;
+     }
+     splx(s);
+     return EADDRNOTAVAIL;		/* not found */
+ }
+ 
+ /*
+  * Return 1 if gaddr is a member of the local group list for vifp.
+  */
+ static int
+ grplst_member(vifp, gaddr)
+     struct vif *vifp;
+     struct in_addr gaddr;
+ {
+     register int i;
+     register u_long addr;
+     register struct in_addr *gl;
+     register struct mbuf *mb_gl;
+     int s;
+ 
+     mrtstat.mrts_grp_lookups++;
+ 
+     addr = gaddr.s_addr;
+     if (addr == vifp->v_cached_group)
+ 	return (vifp->v_cached_result);
+ 
+     mrtstat.mrts_grp_misses++;
+ 
+     for (mb_gl = vifp->v_lcl_groups; mb_gl; mb_gl = mb_gl->m_next) {
+       for (gl = mtod(mb_gl, struct in_addr *), i = mb_gl->m_len / INSIZ;
+ 	   i; gl++, i--) {
+ 	if (addr == gl->s_addr) {
+ 	  s = splnet();
+ 	  vifp->v_cached_group  = addr;
+ 	  vifp->v_cached_result = 1;
+ 	  splx(s);
+ 	  return 1;
+ 	}
+       }
+     }
+     s = splnet();
+     vifp->v_cached_group  = addr;
+     vifp->v_cached_result = 0;
+     splx(s);
+     return 0;
+ }
+ 
+ /*
+  * A simple hash function: returns MRTHASHMOD of the low-order octet of
+  * the argument's network or subnet number.
+  */ 
+ static u_long
+ nethash(in)
+     struct in_addr in;
+ {
+     register u_long n;
+ 
+     n = in_netof(in);
+     while ((n & 0xff) == 0) n >>= 8;
+     return (MRTHASHMOD(n));
+ }
+ 
+ /*
+  * Add an mrt entry
+  */
+ static int
+ add_mrt(mrtcp)
+     struct mrtctl *mrtcp;
+ {
+     struct mrt *rt, *mrtfind();
+     struct mbuf *mb_rt;
+     u_long hash;
+     int s;
+ 
+     if (rt = mrtfind(mrtcp->mrtc_origin)) {
+ 	if (mrtdebug)
+ 	  log(LOG_DEBUG,"add_mrt update o %x m %x p %x c %x l %x",
+ 	      ntohl(mrtcp->mrtc_origin.s_addr),
+ 	      ntohl(mrtcp->mrtc_originmask.s_addr),
+ 	      mrtcp->mrtc_parent, mrtcp->mrtc_children, mrtcp->mrtc_leaves);
+ 
+ 	/* 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;
+     }
+ 
+     if (mrtdebug)
+       log(LOG_DEBUG,"add_mrt o %x m %x p %x c %x l %x",
+ 	  ntohl(mrtcp->mrtc_origin.s_addr),
+ 	  ntohl(mrtcp->mrtc_originmask.s_addr),
+ 	  mrtcp->mrtc_parent, mrtcp->mrtc_children, mrtcp->mrtc_leaves);
+ 
+     s = splnet();
+ 
+     MGET(mb_rt, M_DONTWAIT, MT_MRTABLE);
+     if (mb_rt == 0) {
+ 	splx(s);
+ 	return ENOBUFS;
+     }
+     rt = mtod(mb_rt, struct mrt *);
+ 
+     /*
+      * 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);
+     mb_rt->m_next  = mrttable[hash];
+     mrttable[hash] = mb_rt;
+ 
+     splx(s);
+ 
+     return 0;
+ }
+ 
+ /*
+  * Delete an mrt entry
+  */
+ static int
+ del_mrt(origin)
+     struct in_addr *origin;
+ {
+     register struct mrt *rt;
+     struct mbuf *mb_rt, *prev_mb_rt;
+     register u_long hash = nethash(*origin);
+     int s;
+ 
+     if (mrtdebug)
+       log(LOG_DEBUG,"del_mrt orig %x",
+ 	  ntohl(origin->s_addr));
+ 
+     for (prev_mb_rt = mb_rt = mrttable[hash]
+ 	 ; mb_rt
+ 	 ; prev_mb_rt = mb_rt, mb_rt = mb_rt->m_next) {
+         rt = mtod(mb_rt, struct mrt *);
+ 	if (origin->s_addr == rt->mrt_origin.s_addr)
+ 	    break;
+     }
+     if (!mb_rt) {
+ 	return ESRCH;
+     }
+ 
+     s = splnet();
+ 
+     if (rt == cached_mrt)
+         cached_mrt = NULL;
+ 
+     if (prev_mb_rt != mb_rt) {	/* if moved past head of list */
+ 	MFREE(mb_rt, prev_mb_rt->m_next);
+     } else			/* delete head of list, it is in the table */
+         mrttable[hash] = m_free(mb_rt);
+ 
+     splx(s);
+ 
+     return 0;
+ }
+ 
+ /*
+  * Find a route for a given origin IP address.
+  */
+ static struct mrt *
+ mrtfind(origin)
+     struct in_addr origin;
+ {
+     register struct mbuf *mb_rt;
+     register struct mrt *rt;
+     register u_int hash;
+     int s;
+ 
+     mrtstat.mrts_mrt_lookups++;
+ 
+     if (cached_mrt != NULL &&
+ 	(origin.s_addr & cached_originmask) == cached_origin)
+ 	return (cached_mrt);
+ 
+     mrtstat.mrts_mrt_misses++;
+ 
+     hash = nethash(origin);
+     for (mb_rt = mrttable[hash]; mb_rt; mb_rt = mb_rt->m_next) {
+ 	rt = mtod(mb_rt, struct mrt *);
+ 	if ((origin.s_addr & rt->mrt_originmask.s_addr) ==
+ 					rt->mrt_origin.s_addr) {
+ 	    s = splnet();
+ 	    cached_mrt        = rt;
+ 	    cached_origin     = rt->mrt_origin.s_addr;
+ 	    cached_originmask = rt->mrt_originmask.s_addr;
+ 	    splx(s);
+ 	    return (rt);
+ 	}
+     }
+     return NULL;
+ }
+ 
+ /*
+  * IP multicast forwarding function. This function assumes that the packet
+  * pointed to by "ip" has arrived on (or is about to be sent to) the interface
+  * pointed to by "ifp", and the packet is to be relayed to other networks
+  * that have members of the packet's destination IP multicast group.
+  *
+  * The packet is returned unscathed to the caller, unless it is tunneled
+  * or erroneous, in which case a non-zero return value tells the caller to
+  * discard it.
+  */
+ 
+ #define IP_HDR_LEN  20	/* # bytes of fixed IP header (excluding options) */
+ #define TUNNEL_LEN  12  /* # bytes of IP option for tunnel encapsulation  */
+ 
+ int
+ ip_mforward(ip, ifp)
+     register struct ip *ip;
+     struct ifnet *ifp;
+ {
+     register struct mrt *rt;
+     register struct vif *vifp;
+     register int vifi;
+     register u_char *ipoptions;
+     u_long tunnel_src;
+ 
+     if (mrtdebug > 1)
+       log(LOG_DEBUG, "ip_mforward: src %x, dst %x, ifp %x",
+ 	  ntohl(ip->ip_src.s_addr), ntohl(ip->ip_dst.s_addr), ifp);
+ 
+     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++;
+ 	    if (mrtdebug)
+ 		log(LOG_DEBUG,
+ 		"ip_mforward: bad tunnel from %u (%x %x %x %x %x %x)",
+ 		ntohl(ip->ip_src.s_addr),
+ 		ipoptions[0], ipoptions[1], ipoptions[2], ipoptions[3],
+ 		*(u_long *)(&ipoptions[4]), *(u_long *)(&ipoptions[8]));
+ 	    return 1;
+ 	}
+ 
+ 	/*
+ 	 * Delete the tunnel options from the packet.
+ 	 */
+   	ovbcopy((caddr_t)(ipoptions + TUNNEL_LEN), (caddr_t)ipoptions,
+ 	      (unsigned)(dtom(ip)->m_len - (IP_HDR_LEN + TUNNEL_LEN)));
+ 	dtom(ip)->m_len -= TUNNEL_LEN;
+ 	ip->ip_len      -= TUNNEL_LEN;
+ 	ip->ip_hl       -= TUNNEL_LEN >> 2;
+     }
+ 
+     /*
+      * Don't forward a packet with time-to-live of zero or one,
+      * or a packet destined to a local-only group.
+      */
+     if (ip->ip_ttl <= 1 ||
+ 	ntohl(ip->ip_dst.s_addr) <= INADDR_MAX_LOCAL_GROUP)
+ 	return (int)tunnel_src;
+ 
+     /*
+      * Don't forward if we don't have a route for the packet's origin.
+      */
+     if (!(rt = mrtfind(ip->ip_src))) {
+ 	mrtstat.mrts_no_route++;
+ 	if (mrtdebug)
+ 	    log(LOG_DEBUG, "ip_mforward: no route for %u",
+ 				ntohl(ip->ip_src.s_addr));
+ 	return (int)tunnel_src;
+     }
+ 
+     /*
+      * Don't forward if it didn't arrive from the parent vif for its origin.
+      */
+     vifi = rt->mrt_parent;
+     if (tunnel_src == 0 ) {
+ 	if ((viftable[vifi].v_flags & VIFF_TUNNEL) ||
+ 	    viftable[vifi].v_ifp != ifp )
+ 	    return (int)tunnel_src;
+     }
+     else {
+ 	if (!(viftable[vifi].v_flags & VIFF_TUNNEL) ||
+ 	    viftable[vifi].v_rmt_addr.s_addr != tunnel_src )
+ 	    return (int)tunnel_src;
+     }
+ 
+     /*
+      * For each vif, decide if a copy of the packet should be forwarded.
+      * Forward if:
+      *		- the ttl exceeds the vif's threshold AND
+      *		- the vif is a child in the origin's route AND
+      *		- ( the vif is not a leaf in the origin's route OR
+      *		    the destination group has members on the vif )
+      *
+      * (This might be speeded up with some sort of cache -- someday.)
+      */
+     for (vifp = viftable, vifi = 0; vifi < numvifs; vifp++, vifi++) {
+ 	if (ip->ip_ttl > vifp->v_threshold &&
+ 	    VIFM_ISSET(vifi, rt->mrt_children) &&
+ 	    (!VIFM_ISSET(vifi, rt->mrt_leaves) ||
+ 	     grplst_member(vifp, ip->ip_dst))) {
+ 		if (vifp->v_flags & VIFF_TUNNEL) tunnel_send(ip, vifp);
+ 		else				 phyint_send(ip, vifp);
+ 	}
+     }
+ 
+     return (int)tunnel_src;
+ }
+ 
+ static
+ phyint_send(ip, vifp)
+     struct ip *ip;
+     struct vif *vifp;
+ {
+     register struct mbuf *mb_copy;
+     register struct mbuf *mopts;
+     register struct ip_moptions *imo;
+     int error;
+ 
+     mb_copy = m_copy(dtom(ip), 0, M_COPYALL);
+     if (mb_copy == NULL)
+ 	return;
+ 
+     MGET(mopts, M_DONTWAIT, MT_IPMOPTS);
+     if (mopts == NULL) {
+ 	m_freem(mb_copy);
+ 	return;
+     }
+ 
+     imo = mtod(mopts, struct ip_moptions *);
+     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, (struct mbuf *)0, (struct route *)0,
+ 				IP_FORWARDING|IP_MULTICASTOPTS,
+ 		                (struct socket *)0, mopts);
+     m_free(mopts);
+     if (mrtdebug > 1)
+ 	log(LOG_DEBUG, "phyint_send on vif %d err %d", vifp-viftable, error);
+ }
+ 
+ static
+ tunnel_send(ip, vifp)
+     struct ip *ip;
+     struct vif *vifp;
+ {
+     struct mbuf *mb_copy, *mb_opts;
+     register struct ip *ip_copy;
+     int error;
+     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++;
+ 	if (mrtdebug)
+ 	    log(LOG_DEBUG, "tunnel_send: no room for tunnel options, from %u",
+ 					ntohl(ip->ip_src.s_addr));
+ 	return;
+     }
+ 
+     mb_copy = m_copy(dtom(ip), 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;
+     MGET(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_off += 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_off = MMAXOFF - IP_HDR_LEN - TUNNEL_LEN;
+     mb_opts->m_len = IP_HDR_LEN + TUNNEL_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, (struct mbuf *)0, (struct route *)0,
+ 				IP_FORWARDING,
+ 		                (struct socket *)0, (struct mbuf *)0);
+     if (mrtdebug > 1)
+ 	log(LOG_DEBUG, "tunnel_send on vif %d err %d", vifp-viftable, error);
+ }
+ 
+ #endif MROUTING
+ 
+ #ifdef ultrix
+ /*
+  * Ultrix doesn't support the BSD log() call so we have to simulate it for the
+  * mrouter code.
+  *
+  * - tm 3/89
+  */
+ 
+ /*VARARGS2*/
+ log(level, fmt, x1)
+    char *fmt;
+    unsigned x1;
+ {
+   if(level == LOG_DEBUG)
+       mprintf(fmt, x1);
+   else
+       printf(fmt, x1);
+ }
+ 
+ #endif ultrix
+ 
+ #endif MULTICAST
===================================================================
Index: net/ipmulticast/ip_mroute.h
***************
*** 0 ****
--- 1,137 ----
+ /*
+  * 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 */
+ 
+ 
+ /*
+  * 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 mbuf	  *v_lcl_groups;    /* list of local groups (phyints only) */
+     u_long	   v_cached_group;  /* last group looked-up (phyints only) */
+     int		   v_cached_result; /* last look-up result  (phyints only) */
+ };
+ 
+ #define GRPLSTLEN (MLEN/4)	/* number of groups in a grplst */
+ 
+ struct grplst {
+     struct in_addr gl_gaddr[GRPLSTLEN];
+ };
+ 
+ 
+ /*
+  * 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 */
+ };
+ 
+ 
+ #define MRTHASHSIZ	64
+ #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      */
+ };
+ 
+ 
+ #endif /* KERNEL */
+ 
