freebsd-skq/sys/net/if_ethersubr.c
julian ab2eb71c6f Obtained from: netatalk distribution netatalk@itd.umich.edu
Kernel Appletalk protocol support
both CAP and netatalk can make use of this..
still needs some owrk but  it seemd the right tiime to commit it
so other can experiment.
1996-05-24 01:35:45 +00:00

787 lines
20 KiB
C

/*
* Copyright (c) 1982, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93
* $Id: if_ethersubr.c,v 1.15 1996/04/07 17:39:03 bde Exp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/netisr.h>
#include <net/route.h>
#include <net/if_llc.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_var.h>
#endif
#include <netinet/if_ether.h>
#ifdef IPX
#include <netipx/ipx.h>
#include <netipx/ipx_if.h>
#endif
#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif
#ifdef ISO
#include <netiso/argo_debug.h>
#include <netiso/iso.h>
#include <netiso/iso_var.h>
#include <netiso/iso_snpac.h>
#endif
/*#ifdef LLC
#include <netccitt/dll.h>
#include <netccitt/llc_var.h>
#endif*/
#if defined(LLC) && defined(CCITT)
extern struct ifqueue pkintrq;
#endif
#ifdef NETATALK
#include <netatalk/at.h>
#include <netatalk/at_var.h>
#include <netatalk/at_extern.h>
#define llc_snap_org_code llc_un.type_snap.org_code
#define llc_snap_ether_type llc_un.type_snap.ether_type
extern u_char at_org_code[ 3 ];
extern u_char aarp_org_code[ 3 ];
#endif NETATALK
u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
#define senderr(e) { error = (e); goto bad;}
/*
* Ethernet output routine.
* Encapsulate a packet of type family for the local net.
* Use trailer local net encapsulation if enough data in first
* packet leaves a multiple of 512 bytes of data in remainder.
* Assumes that ifp is actually pointer to arpcom structure.
*/
int
ether_output(ifp, m0, dst, rt0)
register struct ifnet *ifp;
struct mbuf *m0;
struct sockaddr *dst;
struct rtentry *rt0;
{
short type;
int s, error = 0;
u_char edst[6];
register struct mbuf *m = m0;
register struct rtentry *rt;
struct mbuf *mcopy = (struct mbuf *)0;
register struct ether_header *eh;
int off, len = m->m_pkthdr.len;
struct arpcom *ac = (struct arpcom *)ifp;
#ifdef NETATALK
struct at_ifaddr *aa;
#endif NETATALK
if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
senderr(ENETDOWN);
ifp->if_lastchange = time;
rt = rt0;
if (rt) {
if ((rt->rt_flags & RTF_UP) == 0) {
rt0 = rt = rtalloc1(dst, 1, 0UL);
if (rt0)
rt->rt_refcnt--;
else
senderr(EHOSTUNREACH);
}
if (rt->rt_flags & RTF_GATEWAY) {
if (rt->rt_gwroute == 0)
goto lookup;
if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
rtfree(rt); rt = rt0;
lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1,
0UL);
if ((rt = rt->rt_gwroute) == 0)
senderr(EHOSTUNREACH);
}
}
if (rt->rt_flags & RTF_REJECT)
if (rt->rt_rmx.rmx_expire == 0 ||
time.tv_sec < rt->rt_rmx.rmx_expire)
senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
}
switch (dst->sa_family) {
#ifdef INET
case AF_INET:
if (!arpresolve(ac, rt, m, dst, edst, rt0))
return (0); /* if not yet resolved */
/* If broadcasting on a simplex interface, loopback a copy */
if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
mcopy = m_copy(m, 0, (int)M_COPYALL);
off = m->m_pkthdr.len - m->m_len;
type = ETHERTYPE_IP;
break;
#endif
#ifdef IPX
case AF_IPX:
type = ETHERTYPE_IPX;
bcopy((caddr_t)&(((struct sockaddr_ipx *)dst)->sipx_addr.x_host),
(caddr_t)edst, sizeof (edst));
if (!bcmp((caddr_t)edst, (caddr_t)&ipx_thishost, sizeof(edst)))
return (looutput(ifp, m, dst, rt));
/* If broadcasting on a simplex interface, loopback a copy */
if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
mcopy = m_copy(m, 0, (int)M_COPYALL);
break;
#endif
#ifdef NETATALK
case AF_APPLETALK:
if (!aarpresolve(ac, m, (struct sockaddr_at *)dst, edst)) {
#ifdef NETATALKDEBUG
extern char *prsockaddr(struct sockaddr *);
printf("aarpresolv: failed for %s\n", prsockaddr(dst));
#endif NETATALKDEBUG
return (0);
}
/*
* ifaddr is the first thing in at_ifaddr
*/
if ((aa = (struct at_ifaddr *)at_ifawithnet(
(struct sockaddr_at *)dst, ifp->if_addrlist))
== 0)
goto bad;
/*
* In the phase 2 case, we need to prepend an mbuf for the llc header.
* Since we must preserve the value of m, which is passed to us by
* value, we m_copy() the first mbuf, and use it for our llc header.
*/
if ( aa->aa_flags & AFA_PHASE2 ) {
struct llc llc;
M_PREPEND(m, sizeof(struct llc), M_WAIT);
len += sizeof(struct llc);
llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP;
llc.llc_control = LLC_UI;
bcopy(at_org_code, llc.llc_snap_org_code, sizeof(at_org_code));
llc.llc_snap_ether_type = htons( ETHERTYPE_AT );
bcopy(&llc, mtod(m, caddr_t), sizeof(struct llc));
type = m->m_pkthdr.len;
} else {
type = ETHERTYPE_AT;
}
break;
#endif NETATALK
#ifdef NS
case AF_NS:
type = ETHERTYPE_NS;
bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
(caddr_t)edst, sizeof (edst));
if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, sizeof(edst)))
return (looutput(ifp, m, dst, rt));
/* If broadcasting on a simplex interface, loopback a copy */
if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
mcopy = m_copy(m, 0, (int)M_COPYALL);
break;
#endif
#ifdef ISO
case AF_ISO: {
int snpalen;
struct llc *l;
register struct sockaddr_dl *sdl;
if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway) &&
sdl->sdl_family == AF_LINK && sdl->sdl_alen > 0) {
bcopy(LLADDR(sdl), (caddr_t)edst, sizeof(edst));
} else if (error =
iso_snparesolve(ifp, (struct sockaddr_iso *)dst,
(char *)edst, &snpalen))
goto bad; /* Not Resolved */
/* If broadcasting on a simplex interface, loopback a copy */
if (*edst & 1)
m->m_flags |= (M_BCAST|M_MCAST);
if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX) &&
(mcopy = m_copy(m, 0, (int)M_COPYALL))) {
M_PREPEND(mcopy, sizeof (*eh), M_DONTWAIT);
if (mcopy) {
eh = mtod(mcopy, struct ether_header *);
bcopy((caddr_t)edst,
(caddr_t)eh->ether_dhost, sizeof (edst));
bcopy((caddr_t)ac->ac_enaddr,
(caddr_t)eh->ether_shost, sizeof (edst));
}
}
M_PREPEND(m, 3, M_DONTWAIT);
if (m == NULL)
return (0);
type = m->m_pkthdr.len;
l = mtod(m, struct llc *);
l->llc_dsap = l->llc_ssap = LLC_ISO_LSAP;
l->llc_control = LLC_UI;
len += 3;
IFDEBUG(D_ETHER)
int i;
printf("unoutput: sending pkt to: ");
for (i=0; i<6; i++)
printf("%x ", edst[i] & 0xff);
printf("\n");
ENDDEBUG
} break;
#endif /* ISO */
#ifdef LLC
/* case AF_NSAP: */
case AF_CCITT: {
register struct sockaddr_dl *sdl =
(struct sockaddr_dl *) rt -> rt_gateway;
if (sdl && sdl->sdl_family == AF_LINK
&& sdl->sdl_alen > 0) {
bcopy(LLADDR(sdl), (char *)edst,
sizeof(edst));
} else goto bad; /* Not a link interface ? Funny ... */
if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1) &&
(mcopy = m_copy(m, 0, (int)M_COPYALL))) {
M_PREPEND(mcopy, sizeof (*eh), M_DONTWAIT);
if (mcopy) {
eh = mtod(mcopy, struct ether_header *);
bcopy((caddr_t)edst,
(caddr_t)eh->ether_dhost, sizeof (edst));
bcopy((caddr_t)ac->ac_enaddr,
(caddr_t)eh->ether_shost, sizeof (edst));
}
}
type = m->m_pkthdr.len;
#ifdef LLC_DEBUG
{
int i;
register struct llc *l = mtod(m, struct llc *);
printf("ether_output: sending LLC2 pkt to: ");
for (i=0; i<6; i++)
printf("%x ", edst[i] & 0xff);
printf(" len 0x%x dsap 0x%x ssap 0x%x control 0x%x\n",
type & 0xff, l->llc_dsap & 0xff, l->llc_ssap &0xff,
l->llc_control & 0xff);
}
#endif /* LLC_DEBUG */
} break;
#endif /* LLC */
case AF_UNSPEC:
eh = (struct ether_header *)dst->sa_data;
(void)memcpy(edst, eh->ether_dhost, sizeof (edst));
type = eh->ether_type;
break;
default:
printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
dst->sa_family);
senderr(EAFNOSUPPORT);
}
if (mcopy)
(void) looutput(ifp, mcopy, dst, rt);
/*
* Add local net header. If no space in first mbuf,
* allocate another.
*/
M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT);
if (m == 0)
senderr(ENOBUFS);
eh = mtod(m, struct ether_header *);
type = htons((u_short)type);
(void)memcpy(&eh->ether_type, &type,
sizeof(eh->ether_type));
(void)memcpy(eh->ether_dhost, edst, sizeof (edst));
(void)memcpy(eh->ether_shost, ac->ac_enaddr,
sizeof(eh->ether_shost));
s = splimp();
/*
* Queue message on interface, and start output if interface
* not yet active.
*/
if (IF_QFULL(&ifp->if_snd)) {
IF_DROP(&ifp->if_snd);
splx(s);
senderr(ENOBUFS);
}
IF_ENQUEUE(&ifp->if_snd, m);
if ((ifp->if_flags & IFF_OACTIVE) == 0)
(*ifp->if_start)(ifp);
splx(s);
ifp->if_obytes += len + sizeof (struct ether_header);
if (m->m_flags & M_MCAST)
ifp->if_omcasts++;
return (error);
bad:
if (m)
m_freem(m);
return (error);
}
/*
* Process a received Ethernet packet;
* the packet is in the mbuf chain m without
* the ether header, which is provided separately.
*/
void
ether_input(ifp, eh, m)
struct ifnet *ifp;
register struct ether_header *eh;
struct mbuf *m;
{
register struct ifqueue *inq;
register struct llc *l;
u_short ether_type;
int s;
if ((ifp->if_flags & IFF_UP) == 0) {
m_freem(m);
return;
}
ifp->if_lastchange = time;
ifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
sizeof(etherbroadcastaddr)) == 0)
m->m_flags |= M_BCAST;
else if (eh->ether_dhost[0] & 1)
m->m_flags |= M_MCAST;
if (m->m_flags & (M_BCAST|M_MCAST))
ifp->if_imcasts++;
ether_type = ntohs(eh->ether_type);
switch (ether_type) {
#ifdef INET
case ETHERTYPE_IP:
schednetisr(NETISR_IP);
inq = &ipintrq;
break;
case ETHERTYPE_ARP:
schednetisr(NETISR_ARP);
inq = &arpintrq;
break;
#endif
#ifdef IPX
case ETHERTYPE_IPX:
schednetisr(NETISR_IPX);
inq = &ipxintrq;
break;
#endif
#ifdef NS
case ETHERTYPE_NS:
schednetisr(NETISR_NS);
inq = &nsintrq;
break;
#endif
#ifdef NETATALK
case ETHERTYPE_AT:
schednetisr(NETISR_ATALK);
inq = &atintrq1;
break;
case ETHERTYPE_AARP:
/* probably this should be done with a NETISR as well */
aarpinput((struct arpcom *)ifp, m); /* XXX */
return;
#endif NETATALK
default:
#if defined (ISO) || defined (LLC) || defined(NETATALK)
if (ether_type > ETHERMTU)
goto dropanyway;
l = mtod(m, struct llc *);
switch (l->llc_dsap) {
#ifdef NETATALK
case LLC_SNAP_LSAP:
switch (l->llc_control) {
case LLC_UI:
if (l->llc_ssap != LLC_SNAP_LSAP)
goto dropanyway;
if (Bcmp(&(l->llc_snap_org_code)[0], at_org_code,
sizeof(at_org_code)) == 0 &&
ntohs(l->llc_snap_ether_type) == ETHERTYPE_AT) {
inq = &atintrq2;
m_adj( m, sizeof( struct llc ));
schednetisr(NETISR_ATALK);
break;
}
if (Bcmp(&(l->llc_snap_org_code)[0], aarp_org_code,
sizeof(aarp_org_code)) == 0 &&
ntohs(l->llc_snap_ether_type) == ETHERTYPE_AARP) {
m_adj( m, sizeof( struct llc ));
aarpinput((struct arpcom *)ifp, m); /* XXX */
return;
}
default:
goto dropanyway;
}
break;
#endif NETATALK
#ifdef ISO
case LLC_ISO_LSAP:
switch (l->llc_control) {
case LLC_UI:
/* LLC_UI_P forbidden in class 1 service */
if ((l->llc_dsap == LLC_ISO_LSAP) &&
(l->llc_ssap == LLC_ISO_LSAP)) {
/* LSAP for ISO */
if (m->m_pkthdr.len > ether_type)
m_adj(m, ether_type - m->m_pkthdr.len);
m->m_data += 3; /* XXX */
m->m_len -= 3; /* XXX */
m->m_pkthdr.len -= 3; /* XXX */
M_PREPEND(m, sizeof *eh, M_DONTWAIT);
if (m == 0)
return;
*mtod(m, struct ether_header *) = *eh;
IFDEBUG(D_ETHER)
printf("clnp packet");
ENDDEBUG
schednetisr(NETISR_ISO);
inq = &clnlintrq;
break;
}
goto dropanyway;
case LLC_XID:
case LLC_XID_P:
if(m->m_len < 6)
goto dropanyway;
l->llc_window = 0;
l->llc_fid = 9;
l->llc_class = 1;
l->llc_dsap = l->llc_ssap = 0;
/* Fall through to */
case LLC_TEST:
case LLC_TEST_P:
{
struct sockaddr sa;
register struct ether_header *eh2;
int i;
u_char c = l->llc_dsap;
l->llc_dsap = l->llc_ssap;
l->llc_ssap = c;
if (m->m_flags & (M_BCAST | M_MCAST))
bcopy((caddr_t)ac->ac_enaddr,
(caddr_t)eh->ether_dhost, 6);
sa.sa_family = AF_UNSPEC;
sa.sa_len = sizeof(sa);
eh2 = (struct ether_header *)sa.sa_data;
for (i = 0; i < 6; i++) {
eh2->ether_shost[i] = c = eh->ether_dhost[i];
eh2->ether_dhost[i] =
eh->ether_dhost[i] = eh->ether_shost[i];
eh->ether_shost[i] = c;
}
ifp->if_output(ifp, m, &sa, NULL);
return;
}
default:
m_freem(m);
return;
}
break;
#endif /* ISO */
#ifdef LLC
case LLC_X25_LSAP:
{
if (m->m_pkthdr.len > ether_type)
m_adj(m, ether_type - m->m_pkthdr.len);
M_PREPEND(m, sizeof(struct sdl_hdr) , M_DONTWAIT);
if (m == 0)
return;
if ( !sdl_sethdrif(ifp, eh->ether_shost, LLC_X25_LSAP,
eh->ether_dhost, LLC_X25_LSAP, 6,
mtod(m, struct sdl_hdr *)))
panic("ETHER cons addr failure");
mtod(m, struct sdl_hdr *)->sdlhdr_len = ether_type;
#ifdef LLC_DEBUG
printf("llc packet\n");
#endif /* LLC_DEBUG */
schednetisr(NETISR_CCITT);
inq = &llcintrq;
break;
}
#endif /* LLC */
dropanyway:
default:
m_freem(m);
return;
}
#else /* ISO || LLC || NETATALK */
m_freem(m);
return;
#endif /* ISO || LLC || NETATALK */
}
s = splimp();
if (IF_QFULL(inq)) {
IF_DROP(inq);
m_freem(m);
} else
IF_ENQUEUE(inq, m);
splx(s);
}
/*
* Perform common duties while attaching to interface list
*/
void
ether_ifattach(ifp)
register struct ifnet *ifp;
{
register struct ifaddr *ifa;
register struct sockaddr_dl *sdl;
ifp->if_type = IFT_ETHER;
ifp->if_addrlen = 6;
ifp->if_hdrlen = 14;
ifp->if_mtu = ETHERMTU;
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) &&
sdl->sdl_family == AF_LINK) {
sdl->sdl_type = IFT_ETHER;
sdl->sdl_alen = ifp->if_addrlen;
bcopy((caddr_t)((struct arpcom *)ifp)->ac_enaddr,
LLADDR(sdl), ifp->if_addrlen);
break;
}
}
static u_char ether_ipmulticast_min[6] =
{ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 };
static u_char ether_ipmulticast_max[6] =
{ 0x01, 0x00, 0x5e, 0x7f, 0xff, 0xff };
/*
* Add an Ethernet multicast address or range of addresses to the list for a
* given interface.
*/
int
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];
int set_allmulti = 0;
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);
set_allmulti = 1;
}
else {
ETHER_MAP_IP_MULTICAST(&sin->sin_addr, addrlo);
bcopy(addrlo, addrhi, 6);
}
break;
#endif
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; malloc a new multicast record
* and link it into the interface's multicast list.
*/
enm = (struct ether_multi *)malloc(sizeof(*enm), M_IFMADDR, M_NOWAIT);
if (enm == NULL) {
splx(s);
return (ENOBUFS);
}
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;
ac->ac_multicnt++;
splx(s);
if (set_allmulti)
ac->ac_if.if_flags |= IFF_ALLMULTI;
/*
* 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.
*/
int
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 unset_allmulti = 0;
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);
unset_allmulti = 1;
}
else {
ETHER_MAP_IP_MULTICAST(&sin->sin_addr, addrlo);
bcopy(addrlo, addrhi, 6);
}
break;
#endif
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)
continue;
*p = (*p)->enm_next;
free(enm, M_IFMADDR);
ac->ac_multicnt--;
splx(s);
if (unset_allmulti)
ac->ac_if.if_flags &= ~IFF_ALLMULTI;
/*
* Return ENETRESET to inform the driver that the list has changed
* and its reception filter should be adjusted accordingly.
*/
return (ENETRESET);
}
SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet");