1111 lines
26 KiB
C
Executable File
1111 lines
26 KiB
C
Executable File
/*
|
|
* Copyright (c) 1992 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#pragma ident "@(#)ip_multi.c 1.17 95/02/21 SMI"
|
|
|
|
#ifndef MI_HDRS
|
|
#include <sys/types.h>
|
|
#include <sys/stream.h>
|
|
#include <sys/dlpi.h>
|
|
#include <sys/stropts.h>
|
|
#include <sys/strlog.h>
|
|
#include <sys/tihdr.h>
|
|
#include <sys/tiuser.h>
|
|
#include <sys/ddi.h>
|
|
#include <sys/cmn_err.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/systm.h>
|
|
#include <net/route.h>
|
|
#include <netinet/in.h>
|
|
|
|
#include <inet/common.h>
|
|
#include <inet/mi.h>
|
|
#include <inet/nd.h>
|
|
#include <inet/arp.h>
|
|
#include <inet/ip.h>
|
|
#include <inet/ip_if.h>
|
|
#include <inet/ip_ire.h>
|
|
|
|
#include <netinet/igmp.h>
|
|
|
|
#else
|
|
|
|
#include <types.h>
|
|
#include <stream.h>
|
|
#include <dlpi.h>
|
|
#include <stropts.h>
|
|
#include <strlog.h>
|
|
#include <tihdr.h>
|
|
#include <tiuser.h>
|
|
|
|
#include <socket.h>
|
|
#include <if.h>
|
|
#include <if_arp.h>
|
|
#include <sockio.h>
|
|
#include <route.h>
|
|
#include <in.h>
|
|
|
|
#include <common.h>
|
|
#include <mi.h>
|
|
#include <nd.h>
|
|
#include <lsystm.h>
|
|
#include <arp.h>
|
|
#include <ip.h>
|
|
#include <ip_if.h>
|
|
#include <ip_ire.h>
|
|
|
|
#include <igmp.h>
|
|
#endif
|
|
|
|
int ip_addmulti( u32 group, ipif_t * ipif );
|
|
int ip_delmulti( u32 group, ipif_t * ipif );
|
|
void ip_multicast_loopback( queue_t * rq, mblk_t * mp_orig );
|
|
void ip_wput_ctl( queue_t * q, mblk_t * mp_orig );
|
|
ilm_t * ilm_lookup( ill_t * ill, u32 group );
|
|
ilm_t * ilm_lookup_exact( ipif_t * ipif, u32 group );
|
|
staticf int ilm_numentries( ill_t * ill, u32 group );
|
|
void ilm_free( ipif_t * ipif );
|
|
staticf int ilm_add( ipif_t * ipif, u32 group );
|
|
staticf int ilm_delete( ipif_t * ipif, u32 group );
|
|
staticf int ip_ll_addmulti( ipif_t * ipif, u32 group );
|
|
staticf int ip_ll_delmulti( ipif_t * ipif, u32 group );
|
|
staticf int ip_join_allmulti( ipif_t * ipif );
|
|
staticf int ip_leave_allmulti( ipif_t * ipif );
|
|
int ip_opt_add_group( ipc_t * ipc, u32 group, u32 ifaddr );
|
|
int ip_opt_delete_group( ipc_t * ipc, u32 group, u32 ifaddr );
|
|
staticf ire_t *ire_lookup_loop_multi (u32 group);
|
|
boolean_t ilg_member( ipc_t * ipc, u32 group );
|
|
staticf boolean_t ilg_member_exact( ipc_t * ipc, u32 group, ipif_t * ipif );
|
|
staticf int ilg_add( ipc_t * ipc, u32 group, ipif_t * ipif );
|
|
staticf int ilg_delete( ipc_t * ipc, u32 group, ipif_t * ipif );
|
|
void ilg_delete_all( ipc_t * ipc );
|
|
staticf void ipc_delete_multicast_ipif( ipc_t * ipc, caddr_t arg );
|
|
|
|
void reset_ilg_lower( ipif_t * ipif );
|
|
void reset_ipc_multicast_ipif( ipif_t * ipif );
|
|
staticf mblk_t * ill_create_dl( ill_t *ill, u32 dl_primitive, u32 length,
|
|
u32 * addr_lenp, u32 * addr_offp );
|
|
staticf mblk_t * ill_create_squery( ill_t * ill, u32 ipaddr, u32 addrlen,
|
|
u32 addroff, mblk_t * mp_tail );
|
|
ipif_t * ipif_lookup_addr( u32 addr );
|
|
ipif_t * ipif_lookup_interface( u32 if_addr, u32 dst );
|
|
|
|
/*
|
|
* INADDR_ANY means all multicast addresses. This is only used
|
|
* by the multicast router.
|
|
*/
|
|
int
|
|
ip_addmulti (group, ipif)
|
|
u32 group;
|
|
ipif_t * ipif;
|
|
{
|
|
ill_t * ill = ipif->ipif_ill;
|
|
ilm_t * ilm;
|
|
int err;
|
|
|
|
ip1dbg(("ip_addmulti: 0x%x on %s\n", group, ill->ill_name));
|
|
if (!CLASSD(group) && group != INADDR_ANY)
|
|
return EINVAL;
|
|
|
|
if (ilm = ilm_lookup_exact(ipif, group)) {
|
|
ilm->ilm_refcnt++;
|
|
ip1dbg(("ip_addmulti: already there\n"));
|
|
return 0;
|
|
}
|
|
|
|
err = ilm_add(ipif, group);
|
|
if (err)
|
|
return (err);
|
|
|
|
if (group == INADDR_ANY) {
|
|
/* Check how many ipif's that have members in this group -
|
|
* if more then one we should not tell the driver to join
|
|
* this time
|
|
*/
|
|
if (ilm_numentries(ill, group) > 1)
|
|
return 0;
|
|
return (ip_join_allmulti(ipif));
|
|
}
|
|
if ((ipif->ipif_flags & IFF_LOOPBACK) == 0)
|
|
igmp_joingroup(ilm_lookup_exact(ipif, group));
|
|
|
|
/* Check how many ipif's that have members in this group -
|
|
* if more then one we should not tell the driver to join
|
|
* this time
|
|
*/
|
|
if (ilm_numentries(ill, group) > 1)
|
|
return 0;
|
|
return (ip_ll_addmulti(ipif, group));
|
|
}
|
|
|
|
staticf int
|
|
ip_ll_addmulti (ipif, group)
|
|
ipif_t *ipif;
|
|
u32 group;
|
|
{
|
|
ill_t *ill = ipif->ipif_ill;
|
|
mblk_t *mp;
|
|
u32 addrlen, addroff;
|
|
|
|
ip1dbg(("ip_ll_addmulti: 0x%x on %s (0x%x)\n",
|
|
(int)ntohl(group), ill->ill_name,
|
|
(int)ntohl(ipif->ipif_local_addr)));
|
|
|
|
if (ill->ill_subnet_type != IRE_RESOLVER
|
|
|| ipif->ipif_flags & IFF_POINTOPOINT) {
|
|
ip1dbg(("ip_ll_addmulti: not resolver\n"));
|
|
return 0; /* Must be IRE_SUBNET */
|
|
}
|
|
|
|
if (ipif->ipif_flags & IFF_MULTI_BCAST) {
|
|
ip1dbg(("ip_ll_addmulti: MULTI_BCAST\n"));
|
|
return 0;
|
|
}
|
|
if (ill->ill_ipif_up_count == 0) {
|
|
/* Nobody there. All multicast addresses will be re-joined
|
|
* when we get the DL_BIND_ACK bringing the interface up.
|
|
*/
|
|
ip1dbg(("ip_ll_addmulti: nobody up\n"));
|
|
return 0;
|
|
}
|
|
|
|
/* Create a AR_ENTRY_SQUERY message with a dl_enabmulti_req tacked
|
|
* on.
|
|
*/
|
|
mp = ill_create_dl(ill, DL_ENABMULTI_REQ, sizeof(dl_enabmulti_req_t),
|
|
&addrlen, &addroff);
|
|
if (!mp)
|
|
return ENOMEM;
|
|
mp = ill_create_squery(ill, group, addrlen, addroff, mp);
|
|
if (!mp)
|
|
return ENOMEM;
|
|
ip1dbg(("ip_ll_addmulti: putnext 0x%x on %s\n",
|
|
(int)ntohl(group), ill->ill_name));
|
|
putnext(ill->ill_rq, mp);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* INADDR_ANY means all multicast addresses. This is only used
|
|
* by the multicast router.
|
|
*/
|
|
int
|
|
ip_delmulti (group, ipif)
|
|
u32 group;
|
|
ipif_t *ipif;
|
|
{
|
|
ill_t *ill = ipif->ipif_ill;
|
|
ilm_t *ilm;
|
|
|
|
ip1dbg(("ip_delmulti: 0x%x on %s\n", group, ill->ill_name));
|
|
if (!CLASSD(group) && group != INADDR_ANY)
|
|
return EINVAL;
|
|
|
|
if (!(ilm = ilm_lookup_exact(ipif, group))) {
|
|
return ENOENT;
|
|
}
|
|
ilm->ilm_refcnt--;
|
|
if (ilm->ilm_refcnt > 0) {
|
|
ip1dbg(("ip_delmulti: still %d left\n", ilm->ilm_refcnt));
|
|
return 0;
|
|
}
|
|
(void)ilm_delete(ipif, group);
|
|
|
|
if (group == INADDR_ANY) {
|
|
/* Check how many ipif's that have members in this group -
|
|
* if there are still some left then don't tell the driver
|
|
* to drop it.
|
|
*/
|
|
if (ilm_numentries(ill, group) != 0)
|
|
return 0;
|
|
return (ip_leave_allmulti(ipif));
|
|
}
|
|
|
|
if ((ipif->ipif_flags & IFF_LOOPBACK) == 0)
|
|
igmp_leavegroup(ilm);
|
|
/* Check how many ipif's that have members in this group -
|
|
* if there are still some left then don't tell the driver
|
|
* to drop it.
|
|
*/
|
|
if (ilm_numentries(ill, group) != 0)
|
|
return 0;
|
|
return (ip_ll_delmulti(ipif, group));
|
|
}
|
|
|
|
staticf int
|
|
ip_ll_delmulti (ipif, group)
|
|
ipif_t *ipif;
|
|
u32 group;
|
|
{
|
|
ill_t *ill = ipif->ipif_ill;
|
|
mblk_t *mp;
|
|
u32 addrlen, addroff;
|
|
|
|
ip1dbg(("ip_ll_delmulti: 0x%x on %s (0x%x)\n",
|
|
(int)ntohl(group), ill->ill_name,
|
|
(int)ntohl(ipif->ipif_local_addr)));
|
|
|
|
if (ill->ill_subnet_type != IRE_RESOLVER
|
|
|| ipif->ipif_flags & IFF_POINTOPOINT) {
|
|
return 0; /* Must be IRE_SUBNET */
|
|
}
|
|
if (ipif->ipif_flags & IFF_MULTI_BCAST) {
|
|
ip1dbg(("ip_ll_delmulti: MULTI_BCAST\n"));
|
|
return 0;
|
|
}
|
|
if (ill->ill_ipif_up_count == 0) {
|
|
/* Nobody there. All multicast addresses will be re-joined
|
|
* when we get the DL_BIND_ACK bringing the interface up.
|
|
*/
|
|
ip1dbg(("ip_ll_delmulti: nobody up\n"));
|
|
return 0;
|
|
}
|
|
|
|
/* Create a AR_ENTRY_SQUERY message with a dl_disabmulti_req tacked
|
|
* on.
|
|
*/
|
|
mp = ill_create_dl(ill, DL_DISABMULTI_REQ, sizeof(dl_disabmulti_req_t),
|
|
&addrlen, &addroff);
|
|
if (!mp)
|
|
return ENOMEM;
|
|
mp = ill_create_squery(ill, group, addrlen, addroff, mp);
|
|
if (!mp)
|
|
return ENOMEM;
|
|
ip1dbg(("ip_ll_delmulti: putnext 0x%x on %s\n",
|
|
(int)ntohl(group), ill->ill_name));
|
|
putnext(ill->ill_rq, mp);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Make the driver pass up all multicast packets
|
|
*/
|
|
staticf int
|
|
ip_join_allmulti (ipif)
|
|
ipif_t *ipif;
|
|
{
|
|
ill_t *ill = ipif->ipif_ill;
|
|
mblk_t *mp;
|
|
u32 addrlen, addroff;
|
|
|
|
ip1dbg(("ip_join_allmulti: on %s addr 0x%x\n", ill->ill_name,
|
|
(int)ntohl(ipif->ipif_local_addr)));
|
|
|
|
if (ill->ill_subnet_type != IRE_RESOLVER
|
|
|| ipif->ipif_flags & IFF_POINTOPOINT) {
|
|
ip1dbg(("ip_join_allmulti: not resolver\n"));
|
|
|
|
return 0; /* Must be IRE_SUBNET */
|
|
}
|
|
if (ipif->ipif_flags & IFF_MULTI_BCAST)
|
|
return 0;
|
|
|
|
if (ill->ill_ipif_up_count == 0) {
|
|
/* Nobody there. All multicast addresses will be re-joined
|
|
* when we get the DL_BIND_ACK bringing the interface up.
|
|
*/
|
|
return 0;
|
|
}
|
|
/* Create a dl_promiscon_req message */
|
|
mp = ill_create_dl(ill, DL_PROMISCON_REQ, sizeof(dl_promiscon_req_t),
|
|
&addrlen, &addroff);
|
|
if (!mp)
|
|
return ENOMEM;
|
|
/*
|
|
* send this directly to the DLPI provider - not through the resolver
|
|
*/
|
|
putnext(ill->ill_wq, mp);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Make the driver stop passing up all multicast packets
|
|
*/
|
|
staticf int
|
|
ip_leave_allmulti (ipif)
|
|
ipif_t *ipif;
|
|
{
|
|
ill_t *ill = ipif->ipif_ill;
|
|
mblk_t *mp;
|
|
u32 addrlen, addroff;
|
|
|
|
ip1dbg(("ip_leave_allmulti: on %s addr 0x%x\n", ill->ill_name,
|
|
(int)ntohl(ipif->ipif_local_addr)));
|
|
|
|
if (ill->ill_subnet_type != IRE_RESOLVER
|
|
|| ipif->ipif_flags & IFF_POINTOPOINT) {
|
|
ip1dbg(("ip_leave_allmulti: not resolver\n"));
|
|
|
|
return 0; /* Must be IRE_SUBNET */
|
|
}
|
|
if (ipif->ipif_flags & IFF_MULTI_BCAST)
|
|
return 0;
|
|
|
|
if (ill->ill_ipif_up_count == 0) {
|
|
/* Nobody there. All multicast addresses will be re-joined
|
|
* when we get the DL_BIND_ACK bringing the interface up.
|
|
*/
|
|
return 0;
|
|
}
|
|
/* Create a dl_promiscoff_req message */
|
|
mp = ill_create_dl(ill, DL_PROMISCOFF_REQ, sizeof(dl_promiscoff_req_t),
|
|
&addrlen, &addroff);
|
|
if (!mp)
|
|
return ENOMEM;
|
|
/*
|
|
* send this directly to the DLPI provider - not through the resolver
|
|
*/
|
|
putnext(ill->ill_wq, mp);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Copy mp_orig and pass it in as a local message
|
|
* Note the rq should be ill_rq and nothing else - icmp_inbound depends
|
|
* on q_ptr being an ill and not an ipc.
|
|
*/
|
|
void
|
|
ip_multicast_loopback (rq, mp_orig)
|
|
queue_t * rq;
|
|
mblk_t * mp_orig;
|
|
{
|
|
mblk_t * mp;
|
|
|
|
ip1dbg(("ip_multicast_loopback\n"));
|
|
mp = copymsg(mp_orig); /* TODO this could use dup'ed messages
|
|
except for the IP header. */
|
|
if (mp)
|
|
ip_wput_local(rq, (ipha_t *)ALIGN32(mp->b_rptr), mp,
|
|
IRE_BROADCAST, rq);
|
|
#ifdef IP_DEBUG
|
|
else
|
|
ip1dbg(("ip_multicast_loopback: copymsg failed: base 0x%x, limit 0x%x, read 0x%x, write 0x%x\n",
|
|
(int)mp_orig->b_datap->db_base,
|
|
(int)mp_orig->b_datap->db_lim,
|
|
(int)mp_orig->b_rptr, (int)mp_orig->b_wptr));
|
|
#endif /*IP_DEBUG*/
|
|
|
|
}
|
|
|
|
static area_t ip_aresq_template = {
|
|
AR_ENTRY_SQUERY, /* cmd */
|
|
sizeof(area_t)+IP_ADDR_LEN, /* name offset */
|
|
sizeof(area_t), /* name len (filled by ill_arp_alloc) */
|
|
IP_ARP_PROTO_TYPE, /* protocol, from arps perspective */
|
|
sizeof(area_t), /* proto addr offset */
|
|
IP_ADDR_LEN, /* proto addr_length */
|
|
0, /* proto mask offset */
|
|
/* Rest is initialized when used */
|
|
0, /* flags */
|
|
0, /* hw addr offset */
|
|
0, /* hw addr length */
|
|
};
|
|
|
|
|
|
staticf mblk_t *
|
|
ill_create_squery (ill, ipaddr, addrlen, addroff, mp_tail)
|
|
ill_t *ill;
|
|
u32 ipaddr;
|
|
u32 addrlen;
|
|
u32 addroff; /* Offset into mp_tail */
|
|
mblk_t *mp_tail;
|
|
{
|
|
mblk_t *mp;
|
|
area_t *area;
|
|
|
|
mp = ill_arp_alloc(ill,
|
|
(u_char *)&ip_aresq_template,
|
|
ipaddr);
|
|
if (!mp) {
|
|
freemsg(mp_tail);
|
|
return nilp(mblk_t);
|
|
}
|
|
area = (area_t *)ALIGN32(mp->b_rptr);
|
|
area->area_hw_addr_length = addrlen;
|
|
area->area_hw_addr_offset = mp->b_wptr - mp->b_rptr + addroff;
|
|
|
|
mp->b_cont = mp_tail;
|
|
return mp;
|
|
}
|
|
|
|
/*
|
|
* Create a dlpi message with room for phys+sap. When we come back in
|
|
* ip_wput_ctl() we will strip the sap for those primitives which
|
|
* only need a physical address.
|
|
*/
|
|
staticf mblk_t *
|
|
ill_create_dl (ill, dl_primitive, length, addr_lenp, addr_offp)
|
|
ill_t *ill;
|
|
u32 dl_primitive;
|
|
u32 length;
|
|
u32 *addr_lenp;
|
|
u32 *addr_offp;
|
|
{
|
|
mblk_t *mp;
|
|
u32 hw_addr_length;
|
|
char *cp;
|
|
u32 offset;
|
|
u32 size;
|
|
|
|
*addr_lenp = *addr_offp = 0;
|
|
|
|
hw_addr_length = ill->ill_phys_addr_length;
|
|
if (!hw_addr_length) {
|
|
ip0dbg(("ip_create_dl: hw addr length = 0\n"));
|
|
return nilp(mblk_t);
|
|
}
|
|
hw_addr_length += ((ill->ill_sap_length > 0) ? ill->ill_sap_length :
|
|
-ill->ill_sap_length);
|
|
|
|
size = length;
|
|
switch (dl_primitive) {
|
|
case DL_ENABMULTI_REQ:
|
|
case DL_DISABMULTI_REQ:
|
|
case DL_UNITDATA_REQ:
|
|
size += hw_addr_length;
|
|
break;
|
|
case DL_PROMISCON_REQ:
|
|
case DL_PROMISCOFF_REQ:
|
|
break;
|
|
default:
|
|
return nilp(mblk_t);
|
|
}
|
|
mp = allocb(size, BPRI_HI);
|
|
if (!mp)
|
|
return nilp(mblk_t);
|
|
mp->b_wptr += size;
|
|
mp->b_datap->db_type = M_PROTO;
|
|
|
|
cp = (char *)mp->b_rptr;
|
|
offset = length;
|
|
|
|
switch (dl_primitive) {
|
|
case DL_ENABMULTI_REQ: {
|
|
dl_enabmulti_req_t *dl = (dl_enabmulti_req_t *)ALIGN32(cp);
|
|
|
|
dl->dl_primitive = dl_primitive;
|
|
dl->dl_addr_offset = offset;
|
|
dl->dl_addr_length = *addr_lenp = hw_addr_length;
|
|
*addr_offp = offset;
|
|
break;
|
|
}
|
|
case DL_DISABMULTI_REQ: {
|
|
dl_disabmulti_req_t *dl = (dl_disabmulti_req_t *)ALIGN32(cp);
|
|
|
|
dl->dl_primitive = dl_primitive;
|
|
dl->dl_addr_offset = offset;
|
|
dl->dl_addr_length = *addr_lenp = hw_addr_length;
|
|
*addr_offp = offset;
|
|
break;
|
|
}
|
|
case DL_UNITDATA_REQ: {
|
|
dl_unitdata_req_t *dl = (dl_unitdata_req_t *)ALIGN32(cp);
|
|
|
|
dl->dl_primitive = dl_primitive;
|
|
dl->dl_dest_addr_offset = offset;
|
|
dl->dl_dest_addr_length = *addr_lenp = hw_addr_length;
|
|
*addr_offp = offset;
|
|
break;
|
|
}
|
|
case DL_PROMISCON_REQ:
|
|
case DL_PROMISCOFF_REQ: {
|
|
dl_promiscon_req_t *dl = (dl_promiscon_req_t *)ALIGN32(cp);
|
|
|
|
dl->dl_primitive = dl_primitive;
|
|
dl->dl_level = DL_PROMISC_MULTI;
|
|
*addr_lenp = *addr_offp = 0;
|
|
break;
|
|
}
|
|
}
|
|
ip1dbg(("ill_create_dl: addr_len %d, addr_off %d\n",
|
|
*addr_lenp, *addr_offp));
|
|
return (mp);
|
|
}
|
|
|
|
void
|
|
ip_wput_ctl (q, mp_orig)
|
|
queue_t * q;
|
|
mblk_t * mp_orig;
|
|
{
|
|
ill_t * ill = (ill_t *)q->q_ptr;
|
|
mblk_t * mp = mp_orig;
|
|
area_t * area;
|
|
u8 * cp;
|
|
|
|
ip1dbg(("ip_wput_ctl\n"));
|
|
/* Check that we have a AR_ENTRY_SQUERY with a tacked on mblk */
|
|
if ((mp->b_wptr - mp->b_rptr) < sizeof(area_t) ||
|
|
mp->b_cont == nilp(mblk_t)) {
|
|
putnext(q, mp);
|
|
return;
|
|
}
|
|
area = (area_t *)ALIGN32(mp->b_rptr);
|
|
if (area->area_cmd != AR_ENTRY_SQUERY) {
|
|
putnext(q, mp);
|
|
return;
|
|
}
|
|
mp = mp->b_cont;
|
|
cp = (u8 *)mp->b_rptr;
|
|
/* Update dl_addr_length and dl_addr_offset for primitives that
|
|
* have physical addresses as opposed to full saps
|
|
*/
|
|
switch (((union DL_primitives *)ALIGN32(mp->b_rptr))->dl_primitive) {
|
|
case DL_ENABMULTI_REQ: {
|
|
dl_enabmulti_req_t *dl = (dl_enabmulti_req_t *)ALIGN32(cp);
|
|
|
|
/* Remove the sap from the DL address either at the end or
|
|
* in front of the physical address.
|
|
*/
|
|
if (ill->ill_sap_length < 0)
|
|
dl->dl_addr_length += ill->ill_sap_length;
|
|
else {
|
|
dl->dl_addr_offset += ill->ill_sap_length;
|
|
dl->dl_addr_length -= ill->ill_sap_length;
|
|
}
|
|
ip1dbg(("ip_wput_ctl: ENABMULTI\n"));
|
|
break;
|
|
}
|
|
case DL_DISABMULTI_REQ: {
|
|
dl_disabmulti_req_t *dl = (dl_disabmulti_req_t *)ALIGN32(cp);
|
|
|
|
/* Remove the sap from the DL address either at the end or
|
|
* in front of the physical address.
|
|
*/
|
|
if (ill->ill_sap_length < 0)
|
|
dl->dl_addr_length += ill->ill_sap_length;
|
|
else {
|
|
dl->dl_addr_offset += ill->ill_sap_length;
|
|
dl->dl_addr_length -= ill->ill_sap_length;
|
|
}
|
|
ip1dbg(("ip_wput_ctl: DELMULTI\n"));
|
|
break;
|
|
}
|
|
default:
|
|
ip1dbg(("ip_wput_ctl: default\n"));
|
|
break;
|
|
}
|
|
freeb(mp_orig);
|
|
putnext(q, mp);
|
|
}
|
|
|
|
void
|
|
ill_add_multicast (ill)
|
|
ill_t * ill;
|
|
{
|
|
ilm_t *ilm;
|
|
|
|
for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
|
|
/* Check how many ipif's that have members in this group -
|
|
* if more then one we make sure that this entry is first
|
|
* in the list.
|
|
*/
|
|
if (ilm_numentries(ill, ilm->ilm_addr) > 1 &&
|
|
ilm_lookup(ill, ilm->ilm_addr) != ilm)
|
|
continue;
|
|
ip1dbg(("ill_add_multicast: 0x%x\n",
|
|
(int)ntohl(ilm->ilm_addr)));
|
|
if (ilm->ilm_addr == INADDR_ANY)
|
|
(void)ip_join_allmulti(ill->ill_ipif);
|
|
else
|
|
(void)ip_ll_addmulti(ill->ill_ipif, ilm->ilm_addr);
|
|
}
|
|
}
|
|
|
|
void
|
|
ill_delete_multicast (ill)
|
|
ill_t * ill;
|
|
{
|
|
ilm_t *ilm;
|
|
|
|
for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
|
|
/* Check how many ipif's that have members in this group -
|
|
* if more then one we make sure that this entry is first
|
|
* in the list.
|
|
*/
|
|
if (ilm_numentries(ill, ilm->ilm_addr) > 1 &&
|
|
ilm_lookup(ill, ilm->ilm_addr) != ilm)
|
|
continue;
|
|
ip1dbg(("ill_delete_multicast: 0x%x\n",
|
|
(int)ntohl(ilm->ilm_addr)));
|
|
if (ilm->ilm_addr == INADDR_ANY)
|
|
(void)ip_leave_allmulti(ill->ill_ipif);
|
|
else
|
|
(void)ip_ll_delmulti(ill->ill_ipif, ilm->ilm_addr);
|
|
}
|
|
}
|
|
|
|
|
|
ilm_t *
|
|
ilm_lookup (ill, group)
|
|
ill_t *ill;
|
|
u32 group;
|
|
{
|
|
ilm_t *ilm;
|
|
|
|
for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next)
|
|
if (ilm->ilm_addr == group)
|
|
return ilm;
|
|
return nilp(ilm_t);
|
|
}
|
|
|
|
ilm_t *
|
|
ilm_lookup_exact (ipif, group)
|
|
ipif_t *ipif;
|
|
u32 group;
|
|
{
|
|
ill_t *ill = ipif->ipif_ill;
|
|
ilm_t *ilm;
|
|
|
|
for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next)
|
|
if (ilm->ilm_addr == group && ilm->ilm_ipif == ipif)
|
|
return ilm;
|
|
return nilp(ilm_t);
|
|
}
|
|
|
|
staticf int
|
|
ilm_numentries (ill, group)
|
|
ill_t *ill;
|
|
u32 group;
|
|
{
|
|
ilm_t *ilm;
|
|
int i = 0;
|
|
|
|
for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next)
|
|
if (ilm->ilm_addr == group)
|
|
i++;
|
|
return i;
|
|
}
|
|
|
|
#define GETSTRUCT(structure, number) \
|
|
((structure *)ALIGN32(mi_zalloc((u_int)(sizeof(structure) * (number)))))
|
|
|
|
/* Caller guarantees that the group is not on the list */
|
|
staticf int
|
|
ilm_add (ipif, group)
|
|
ipif_t *ipif;
|
|
u32 group;
|
|
{
|
|
ill_t *ill = ipif->ipif_ill;
|
|
ilm_t *ilm;
|
|
|
|
ilm = GETSTRUCT(ilm_t, 1);
|
|
if (ilm == nilp(ilm_t))
|
|
return ENOMEM;
|
|
ilm->ilm_addr = group;
|
|
ilm->ilm_refcnt = 1;
|
|
ilm->ilm_ipif = ipif;
|
|
ilm->ilm_next = ill->ill_ilm;
|
|
ill->ill_ilm = ilm;
|
|
return 0;
|
|
}
|
|
|
|
staticf int
|
|
ilm_delete (ipif, group)
|
|
ipif_t *ipif;
|
|
u32 group;
|
|
{
|
|
ill_t *ill = ipif->ipif_ill;
|
|
ilm_t *ilm;
|
|
ilm_t **ilmp;
|
|
|
|
if (!(ilm = ilm_lookup_exact(ipif, group)))
|
|
return ENOENT;
|
|
for (ilmp = &ill->ill_ilm; *ilmp; ilmp = &(*ilmp)->ilm_next)
|
|
if (*ilmp == ilm) {
|
|
*ilmp = ilm->ilm_next;
|
|
break;
|
|
}
|
|
mi_free((char *)ilm);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ilm_free (ipif)
|
|
ipif_t *ipif;
|
|
{
|
|
ill_t *ill = ipif->ipif_ill;
|
|
ilm_t *ilm, *next_ilm;
|
|
#ifdef lint
|
|
next_ilm = nilp(ilm_t);
|
|
#endif
|
|
for (ilm = ill->ill_ilm; ilm; ilm = next_ilm) {
|
|
next_ilm = ilm->ilm_next;
|
|
if (ilm->ilm_ipif == ipif)
|
|
(void)ilm_delete(ilm->ilm_ipif, ilm->ilm_addr);
|
|
}
|
|
}
|
|
|
|
|
|
staticf ire_t *
|
|
ire_lookup_loop_multi(group)
|
|
u32 group;
|
|
{
|
|
ire_t *ire;
|
|
|
|
ire = ire_lookup_noroute(group);
|
|
while (ire) {
|
|
switch (ire->ire_type) {
|
|
case IRE_GATEWAY:
|
|
case IRE_NET:
|
|
case IRE_ROUTE_ASSOC:
|
|
ire = ire_lookup_interface(ire->ire_gateway_addr,
|
|
IRE_INTERFACE);
|
|
break;
|
|
case IRE_SUBNET:
|
|
case IRE_RESOLVER:
|
|
return (ire);
|
|
default:
|
|
return (nilp(ire_t));
|
|
}
|
|
}
|
|
return (nilp(ire_t));
|
|
}
|
|
|
|
ipif_t *
|
|
ipif_lookup_group(group)
|
|
u32 group;
|
|
{
|
|
ire_t *ire;
|
|
|
|
ire = ire_lookup_loop_multi(group);
|
|
if (ire == nilp(ire_t))
|
|
return (nilp(ipif_t));
|
|
return (ire_interface_to_ipif(ire));
|
|
}
|
|
|
|
/*
|
|
* Look for an ipif with the specified interface address and destination.
|
|
* The destination address is used only for matching point-to-point interfaces.
|
|
*/
|
|
ipif_t *
|
|
ipif_lookup_interface (if_addr, dst)
|
|
u32 if_addr; /* interface address of ipif */
|
|
u32 dst; /* destination address in case of ppp */
|
|
{
|
|
ipif_t * ipif;
|
|
ill_t *ill;
|
|
|
|
|
|
/*
|
|
* First match all the point-to-point interfaces
|
|
* before looking at non-point-to-point interfaces.
|
|
* This is done to avoid returning non-point-to-point
|
|
* ipif instead of an unnumbered point-to-point ipif.
|
|
*/
|
|
for (ill = ill_g_head; ill; ill = ill->ill_next) {
|
|
for (ipif = ill->ill_ipif; ipif; ipif = ipif->ipif_next) {
|
|
/* Allow the ipif to be down */
|
|
if ((ipif->ipif_flags & IFF_POINTOPOINT) &&
|
|
(ipif->ipif_local_addr == if_addr) &&
|
|
(ipif->ipif_pp_dst_addr == dst))
|
|
return (ipif);
|
|
}
|
|
}
|
|
/* lookup the ipif based on interface address */
|
|
ipif = ipif_lookup_addr(if_addr);
|
|
return (ipif);
|
|
}
|
|
|
|
/*
|
|
* Look for an ipif with the specified address. For point-point links
|
|
* we look for matches on either the destination address and the local
|
|
* address, but we ignore the check on the local address if IFF_UNNUMBERED
|
|
* is set.
|
|
*/
|
|
ipif_t *
|
|
ipif_lookup_addr (addr)
|
|
u32 addr;
|
|
{
|
|
ipif_t *ipif;
|
|
ill_t *ill;
|
|
|
|
for (ill = ill_g_head; ill; ill = ill->ill_next)
|
|
for (ipif = ill->ill_ipif; ipif; ipif = ipif->ipif_next) {
|
|
/* Allow the ipif to be down */
|
|
if (ipif->ipif_local_addr == addr &&
|
|
(ipif->ipif_flags & IFF_UNNUMBERED) == 0)
|
|
return (ipif);
|
|
if (ipif->ipif_flags & IFF_POINTOPOINT &&
|
|
ipif->ipif_pp_dst_addr == addr)
|
|
return (ipif);
|
|
}
|
|
return (nilp(ipif_t));
|
|
}
|
|
|
|
/*
|
|
* Look for an ipif that matches the specified remote address i.e. the
|
|
* ipif that would receive the specified packet.
|
|
* First look for directly connected interfaces and then do an ire_lookup
|
|
* and pick the first ipif corresponding to the source address in the ire.
|
|
*/
|
|
ipif_t *
|
|
ipif_lookup_remote (ill, addr)
|
|
ill_t * ill;
|
|
u32 addr;
|
|
{
|
|
ipif_t * ipif;
|
|
ire_t * ire;
|
|
|
|
for (ipif = ill->ill_ipif; ipif; ipif = ipif->ipif_next) {
|
|
/* Allow the ipif to be down */
|
|
if (ipif->ipif_flags & IFF_POINTOPOINT) {
|
|
if (ipif->ipif_pp_dst_addr == addr ||
|
|
ipif->ipif_local_addr == addr)
|
|
return (ipif);
|
|
} else if ((ipif->ipif_local_addr & ipif->ipif_net_mask) ==
|
|
(addr & ipif->ipif_net_mask))
|
|
return (ipif);
|
|
}
|
|
ire = ire_lookup_loop(addr, nilp(u32));
|
|
if ( ire ) {
|
|
/* We know that the IRE has a valid (non-zero) source address */
|
|
addr = ire->ire_src_addr;
|
|
for (ipif = ill->ill_ipif; ipif; ipif = ipif->ipif_next) {
|
|
/* Allow the ipif to be down */
|
|
if (ipif->ipif_local_addr == addr &&
|
|
(ipif->ipif_flags & IFF_UNNUMBERED) == 0)
|
|
return (ipif);
|
|
}
|
|
}
|
|
/* Pick the first interface */
|
|
return ill->ill_ipif;
|
|
}
|
|
|
|
/*
|
|
* These are the handling routines for the current optmgmt calls.
|
|
*/
|
|
int
|
|
ip_opt_add_group (ipc, group, ifaddr)
|
|
ipc_t *ipc;
|
|
u32 group;
|
|
u32 ifaddr;
|
|
{
|
|
ipif_t *ipif;
|
|
|
|
if (!CLASSD(group))
|
|
return EINVAL;
|
|
|
|
if (ifaddr == 0)
|
|
ipif = ipif_lookup_group(group);
|
|
else
|
|
ipif = ipif_lookup_addr(ifaddr);
|
|
|
|
if (ipif == nilp(ipif_t)) {
|
|
ip1dbg(("ip_opt_add_group: no ipif for group 0x%x, ifaddr 0x%x\n",
|
|
(int)ntohl(group), (int)ntohl(ifaddr)));
|
|
return EADDRNOTAVAIL;
|
|
}
|
|
return ilg_add(ipc, group, ipif);
|
|
}
|
|
|
|
int
|
|
ip_opt_delete_group (ipc, group, ifaddr)
|
|
ipc_t *ipc;
|
|
u32 group;
|
|
u32 ifaddr;
|
|
{
|
|
ipif_t *ipif;
|
|
|
|
if (!CLASSD(group))
|
|
return EINVAL;
|
|
|
|
if (ifaddr == 0)
|
|
ipif = ipif_lookup_group(group);
|
|
else
|
|
ipif = ipif_lookup_addr(ifaddr);
|
|
|
|
if (ipif == nilp(ipif_t)) {
|
|
ip1dbg(("ip_opt_delete_group: no ipif for group 0x%x, ifaddr 0x%x\n",
|
|
(int)ntohl(group), (int)ntohl(ifaddr)));
|
|
return EADDRNOTAVAIL;
|
|
}
|
|
return ilg_delete(ipc, group, ipif);
|
|
}
|
|
|
|
/* Group mgmt for upper ipc that passes things down
|
|
* to the interface multicast list (and DLPI)
|
|
* These routines can handle new style options that specify an interface name
|
|
* as opposed to an interface address (needed for general handling of
|
|
* unnumbered interfaces.)
|
|
*/
|
|
|
|
#define ILG_ALLOC_CHUNK 16
|
|
|
|
/* Add a group to an upper ipc group data structure and pass things down
|
|
* to the interface multicast list (and DLPI)
|
|
*/
|
|
staticf int
|
|
ilg_add (ipc, group, ipif)
|
|
ipc_t *ipc;
|
|
u32 group;
|
|
ipif_t *ipif;
|
|
{
|
|
int error;
|
|
|
|
if (!(ipif->ipif_flags & IFF_MULTICAST))
|
|
return EADDRNOTAVAIL;
|
|
|
|
if (ilg_member_exact(ipc, group, ipif))
|
|
return EADDRINUSE;
|
|
|
|
if (error = ip_addmulti(group, ipif))
|
|
return (error);
|
|
if (!ipc->ipc_ilg) {
|
|
/* Allocate first chunk */
|
|
ipc->ipc_ilg_allocated = ILG_ALLOC_CHUNK;
|
|
ipc->ipc_ilg = GETSTRUCT(ilg_t, ILG_ALLOC_CHUNK);
|
|
ipc->ipc_ilg_inuse = 0;
|
|
}
|
|
if (ipc->ipc_ilg_inuse >= ipc->ipc_ilg_allocated) {
|
|
/* Allocate next larger chunk and copy old into new */
|
|
ilg_t *new;
|
|
|
|
new = GETSTRUCT(ilg_t,
|
|
ipc->ipc_ilg_allocated + ILG_ALLOC_CHUNK);
|
|
bcopy((char *)ipc->ipc_ilg, (char *)new,
|
|
sizeof (ilg_t) * ipc->ipc_ilg_allocated);
|
|
mi_free((char *)ipc->ipc_ilg);
|
|
ipc->ipc_ilg = new;
|
|
ipc->ipc_ilg_allocated += ILG_ALLOC_CHUNK;
|
|
}
|
|
ipc->ipc_ilg[ipc->ipc_ilg_inuse].ilg_group = group;
|
|
ipc->ipc_ilg[ipc->ipc_ilg_inuse++].ilg_lower = ipif;
|
|
return (0);
|
|
}
|
|
|
|
boolean_t
|
|
ilg_member (ipc, group)
|
|
ipc_t *ipc;
|
|
u32 group;
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ipc->ipc_ilg_inuse; i++)
|
|
if (ipc->ipc_ilg[i].ilg_group == group)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
staticf boolean_t
|
|
ilg_member_exact (ipc, group, ipif)
|
|
ipc_t *ipc;
|
|
u32 group;
|
|
ipif_t *ipif;
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ipc->ipc_ilg_inuse; i++)
|
|
if (ipc->ipc_ilg[i].ilg_group == group &&
|
|
ipc->ipc_ilg[i].ilg_lower == ipif)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
staticf int
|
|
ilg_delete (ipc, group, ipif)
|
|
ipc_t *ipc;
|
|
u32 group;
|
|
ipif_t *ipif;
|
|
{
|
|
int i;
|
|
|
|
if (!(ipif->ipif_flags & IFF_MULTICAST))
|
|
return EADDRNOTAVAIL;
|
|
|
|
if (!ilg_member_exact(ipc, group, ipif))
|
|
return ENOENT;
|
|
|
|
(void)ip_delmulti(group, ipif);
|
|
|
|
for (i = 0; i < ipc->ipc_ilg_inuse; i++)
|
|
if (ipc->ipc_ilg[i].ilg_group == group &&
|
|
ipc->ipc_ilg[i].ilg_lower == ipif) {
|
|
/* Move other entries up one step */
|
|
ipc->ipc_ilg_inuse--;
|
|
for (; i < ipc->ipc_ilg_inuse; i++)
|
|
ipc->ipc_ilg[i] = ipc->ipc_ilg[i+1];
|
|
break;
|
|
}
|
|
if (ipc->ipc_ilg_inuse == 0) {
|
|
mi_free((char *)ipc->ipc_ilg);
|
|
ipc->ipc_ilg = nilp(ilg_t);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ilg_delete_all (ipc)
|
|
ipc_t *ipc;
|
|
{
|
|
int i;
|
|
|
|
if (!ipc->ipc_ilg_inuse)
|
|
return;
|
|
|
|
for (i = ipc->ipc_ilg_inuse - 1; i >= 0; i--)
|
|
(void)ilg_delete(ipc, ipc->ipc_ilg[i].ilg_group,
|
|
ipc->ipc_ilg[i].ilg_lower);
|
|
}
|
|
|
|
staticf void
|
|
ipc_delete_ilg_lower (ipc, arg)
|
|
ipc_t * ipc;
|
|
caddr_t arg;
|
|
{
|
|
ipif_t * ipif = (ipif_t *)ALIGN32(arg);
|
|
int i;
|
|
|
|
for (i = ipc->ipc_ilg_inuse - 1; i >= 0; i--) {
|
|
if (ipc->ipc_ilg[i].ilg_lower == ipif) {
|
|
/* Blow away the membership */
|
|
ip1dbg(("ipc_delete_ilg_lower: 0x%x on 0x%x (%s)\n",
|
|
(int)ntohl(ipc->ipc_ilg[i].ilg_group),
|
|
(int)ntohl(ipif->ipif_local_addr),
|
|
ipif->ipif_ill->ill_name));
|
|
(void)ilg_delete(ipc,
|
|
ipc->ipc_ilg[i].ilg_group,
|
|
ipc->ipc_ilg[i].ilg_lower);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Called when a lower interface is removed (closed)
|
|
* to make sure that there are no dangling ilg_lower to that ipif.
|
|
*/
|
|
void
|
|
reset_ilg_lower (ipif)
|
|
ipif_t *ipif;
|
|
{
|
|
ipc_walk(ipc_delete_ilg_lower, (caddr_t)ipif);
|
|
}
|
|
|
|
staticf void
|
|
ipc_delete_multicast_ipif (ipc, arg)
|
|
ipc_t *ipc;
|
|
caddr_t arg;
|
|
{
|
|
ipif_t *ipif = (ipif_t *)ALIGN32(arg);
|
|
|
|
if (ipc->ipc_multicast_ipif == ipif)
|
|
/* Revert to late binding */
|
|
ipc->ipc_multicast_ipif = nilp(ipif_t);
|
|
}
|
|
|
|
/* Called when an IPIF is deleted...
|
|
* to make sure that there are no dangling ipc_multicast_ipif to that
|
|
* interface.
|
|
*/
|
|
void
|
|
reset_ipc_multicast_ipif (ipif)
|
|
ipif_t *ipif;
|
|
{
|
|
ipc_walk(ipc_delete_multicast_ipif, (caddr_t)ipif);
|
|
}
|