From 19e2ac904f46e8d37d7cd3d2b1186258b4e8f73b Mon Sep 17 00:00:00 2001 From: wollman Date: Mon, 13 Jan 1997 21:26:53 +0000 Subject: Use the new if_multiaddrs list for multicast addresses rather than the previous hackery involving struct in_ifaddr and arpcom. Get rid of the abominable multi_kludge. Update all network interfaces to use the new machanism. Distressingly few Ethernet drivers program the multicast filter properly (assuming the hardware has one, which it usually does). --- sys/net/if.c | 31 +++++++-- sys/net/if.h | 15 +++- sys/net/if_ethersubr.c | 184 +------------------------------------------------ sys/net/route.h | 8 ++- sys/net/rtsock.c | 41 ++++++++++- 5 files changed, 86 insertions(+), 193 deletions(-) (limited to 'sys/net') diff --git a/sys/net/if.c b/sys/net/if.c index b382d48..29e7aca 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -576,12 +576,24 @@ ifioctl(so, cmd, data, p) error = suser(p->p_ucred, &p->p_acflag); if (error) return (error); - if (ifp->if_ioctl == NULL) - return (EOPNOTSUPP); - error = (*ifp->if_ioctl)(ifp, cmd, data); - if (error == 0 ) - microtime(&ifp->if_lastchange); - return(error); + + /* Don't allow group membership on non-multicast interfaces. */ + if ((ifp->if_flags & IFF_MULTICAST) == 0) + return EOPNOTSUPP; + + /* Don't let users screw up protocols' entries. */ + if (ifr->ifr_addr.sa_family != AF_LINK) + return EINVAL; + + if (cmd == SIOCADDMULTI) { + struct ifmultiaddr *ifma; + error = if_addmulti(ifp, &ifr->ifr_addr, &ifma); + } else { + error = if_delmulti(ifp, &ifr->ifr_addr); + } + if (error == 0) + microtime(&ifp->if_lastchange); + return error; default: if (so->so_proto == 0) @@ -835,6 +847,7 @@ if_addmulti(ifp, sa, retifma) ifma->ifma_ifp = ifp; ifma->ifma_refcount = 1; ifma->ifma_protospec = 0; + rt_newmaddrmsg(RTM_NEWMADDR, ifma); /* * Some network interfaces can scan the address list at @@ -856,7 +869,10 @@ if_addmulti(ifp, sa, retifma) } else { MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, M_IFMADDR, M_WAITOK); - ifma->ifma_addr = llsa; + MALLOC(dupsa, struct sockaddr *, llsa->sa_len, + M_IFMADDR, M_WAITOK); + bcopy(llsa, dupsa, llsa->sa_len); + ifma->ifma_addr = dupsa; ifma->ifma_ifp = ifp; ifma->ifma_refcount = 1; } @@ -899,6 +915,7 @@ if_delmulti(ifp, sa) return 0; } + rt_newmaddrmsg(RTM_DELMADDR, ifma); sa = ifma->ifma_lladdr; s = splimp(); LIST_REMOVE(ifma, ifma_link); diff --git a/sys/net/if.h b/sys/net/if.h index 86e4384..4160868 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)if.h 8.1 (Berkeley) 6/10/93 - * $Id: if.h,v 1.41 1996/12/13 21:28:37 wollman Exp $ + * $Id: if.h,v 1.42 1997/01/03 19:50:25 wollman Exp $ */ #ifndef _NET_IF_H_ @@ -124,6 +124,19 @@ struct ifa_msghdr { }; /* + * Message format for use in obtaining information about multicast addresses + * from the routing socket + */ +struct ifma_msghdr { + u_short ifmam_msglen; /* to skip over non-understood messages */ + u_char ifmam_version; /* future binary compatability */ + u_char ifmam_type; /* message type */ + int ifmam_addrs; /* like rtm_addrs */ + int ifmam_flags; /* value of ifa_flags */ + u_short ifmam_index; /* index for associated ifp */ +}; + +/* * Interface request structure used for socket * ioctl's. All interface ioctl's must have parameter * definitions which begin with ifr_name. The diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 38d4911..2dcfde4 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93 - * $Id: if_ethersubr.c,v 1.29 1996/12/13 21:28:38 wollman Exp $ + * $Id: if_ethersubr.c,v 1.30 1997/01/07 19:15:30 wollman Exp $ */ #include @@ -667,188 +667,6 @@ ether_ifattach(ifp) bcopy(((struct arpcom *)ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen); } -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"); int diff --git a/sys/net/route.h b/sys/net/route.h index 0b518df..31fbcae 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)route.h 8.3 (Berkeley) 4/19/94 - * $Id: route.h,v 1.22 1996/08/26 22:04:47 julian Exp $ + * $Id: route.h,v 1.23 1996/10/09 18:35:10 wollman Exp $ */ #ifndef _NET_ROUTE_H_ @@ -203,6 +203,8 @@ struct rt_msghdr { #define RTM_NEWADDR 0xc /* address being added to iface */ #define RTM_DELADDR 0xd /* address being removed from iface */ #define RTM_IFINFO 0xe /* iface going up/down etc. */ +#define RTM_NEWMADDR 0xf /* mcast group membership being added to if */ +#define RTM_DELMADDR 0x10 /* mcast group membership being deleted */ #define RTV_MTU 0x1 /* init or lock _mtu */ #define RTV_HOPCOUNT 0x2 /* init or lock _hopcount */ @@ -262,10 +264,14 @@ extern struct route_cb route_cb; extern struct rtstat rtstat; extern struct radix_node_head *rt_tables[AF_MAX+1]; +/* forward declaration for rt_newmaddrmsg() */ +struct ifmultiaddr; + void route_init __P((void)); void rt_ifmsg __P((struct ifnet *)); void rt_missmsg __P((int, struct rt_addrinfo *, int, int)); void rt_newaddrmsg __P((int, struct ifaddr *, int, struct rtentry *)); +void rt_newmaddrmsg __P((int, struct ifmultiaddr *)); int rt_setgate __P((struct rtentry *, struct sockaddr *, struct sockaddr *)); void rtalloc __P((struct route *)); diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index eb786fa..e831d67 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)rtsock.c 8.5 (Berkeley) 11/2/94 - * $Id: rtsock.c,v 1.21 1996/12/11 20:38:16 wollman Exp $ + * $Id: rtsock.c,v 1.22 1996/12/13 21:28:41 wollman Exp $ */ #include @@ -416,6 +416,11 @@ rt_msg1(type, rtinfo) len = sizeof(struct ifa_msghdr); break; + case RTM_DELMADDR: + case RTM_NEWMADDR: + len = sizeof(struct ifma_msghdr); + break; + case RTM_IFINFO: len = sizeof(struct if_msghdr); break; @@ -637,6 +642,40 @@ rt_newaddrmsg(cmd, ifa, error, rt) } } +/* + * This is the analogue to the rt_newaddrmsg which performs the same + * function but for multicast group memberhips. This is easier since + * there is no route state to worry about. + */ +void +rt_newmaddrmsg(cmd, ifma) + int cmd; + struct ifmultiaddr *ifma; +{ + struct rt_addrinfo info; + struct mbuf *m = 0; + struct ifnet *ifp = ifma->ifma_ifp; + struct ifma_msghdr *ifmam; + + if (route_cb.any_count == 0) + return; + + bzero((caddr_t)&info, sizeof(info)); + ifaaddr = ifma->ifma_addr; + ifpaddr = ifp->if_addrhead.tqh_first->ifa_addr; + /* + * If a link-layer address is present, present it as a ``gateway'' + * (similarly to how ARP entries, e.g., are presented). + */ + gate = ifma->ifma_lladdr; + if ((m = rt_msg1(cmd, &info)) == NULL) + return; + ifmam = mtod(m, struct ifma_msghdr *); + ifmam->ifmam_index = ifp->if_index; + ifmam->ifmam_addrs = info.rti_addrs; + route_proto.sp_protocol = ifma->ifma_addr->sa_family; + raw_input(m, &route_proto, &route_src, &route_dst); +} /* * This is used in dumping the kernel table via sysctl(). -- cgit v1.1