diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/net/if.c | 180 | ||||
-rw-r--r-- | sys/net/if_ethersubr.c | 53 | ||||
-rw-r--r-- | sys/net/if_var.h | 24 |
3 files changed, 254 insertions, 3 deletions
diff --git a/sys/net/if.c b/sys/net/if.c index 65ec137..e523df8 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)if.c 8.3 (Berkeley) 1/4/94 - * $Id: if.c,v 1.37 1996/12/11 20:38:14 wollman Exp $ + * $Id: if.c,v 1.38 1996/12/13 21:28:37 wollman Exp $ */ #include <sys/param.h> @@ -125,6 +125,7 @@ if_attach(ifp) * this unlikely case. */ TAILQ_INIT(&ifp->if_addrhead); + LIST_INIT(&ifp->if_multiaddrs); microtime(&ifp->if_lastchange); if (ifnet_addrs == 0 || if_index >= if_indexlim) { unsigned n = (if_indexlim <<= 1) * sizeof(ifa); @@ -758,5 +759,182 @@ ifconf(cmd, data) return (error); } +/* + * Just like if_promisc(), but for all-multicast-reception mode. + */ +int +if_allmulti(ifp, onswitch) + struct ifnet *ifp; + int onswitch; +{ + int error = 0; + int s = splimp(); + + if (onswitch) { + if (ifp->if_amcount++ == 0) { + ifp->if_flags |= IFF_ALLMULTI; + error = ifp->if_ioctl(ifp, SIOCSIFFLAGS, 0); + } + } else { + if (ifp->if_amcount > 1) { + ifp->if_amcount--; + } else { + ifp->if_amcount = 0; + ifp->if_flags &= ~IFF_ALLMULTI; + error = ifp->if_ioctl(ifp, SIOCSIFFLAGS, 0); + } + } + splx(s); + return error; +} + +/* + * Add a multicast listenership to the interface in question. + * The link layer provides a routine which converts + */ +int +if_addmulti(ifp, sa) + struct ifnet *ifp; /* interface to manipulate */ + struct sockaddr *sa; /* address to add */ +{ + struct sockaddr *llsa, *dupsa; + int error, s; + struct ifmultiaddr *ifma; + + for (ifma = ifp->if_multiaddrs.lh_first; ifma; + ifma = ifma->ifma_link.le_next) { + if (equal(sa, ifma->ifma_addr)) + break; + } + + if (ifma) { + ifma->ifma_refcount++; + return 0; + } + + /* + * Give the link layer a chance to accept/reject it, and also + * find out which AF_LINK address this maps to, if it isn't one + * already. + */ + if (ifp->if_resolvemulti) { + error = ifp->if_resolvemulti(ifp, &llsa, sa); + if (error) return error; + } else { + llsa = 0; + } + + MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, M_IFMADDR, M_WAITOK); + MALLOC(dupsa, struct sockaddr *, sa->sa_len, M_IFMADDR, M_WAITOK); + bcopy(sa, dupsa, sa->sa_len); + + ifma->ifma_addr = dupsa; + ifma->ifma_lladdr = llsa; + ifma->ifma_ifp = ifp; + ifma->ifma_refcount = 1; + /* + * Some network interfaces can scan the address list at + * interrupt time; lock them out. + */ + s = splimp(); + LIST_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); + splx(s); + + if (llsa != 0) { + for (ifma = ifp->if_multiaddrs.lh_first; ifma; + ifma = ifma->ifma_link.le_next) { + if (equal(ifma->ifma_addr, llsa)) + break; + } + if (ifma) { + ifma->ifma_refcount++; + } else { + MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, + M_IFMADDR, M_WAITOK); + ifma->ifma_addr = llsa; + ifma->ifma_ifp = ifp; + ifma->ifma_refcount = 1; + } + s = splimp(); + LIST_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); + splx(s); + } + /* + * We are certain we have added something, so call down to the + * interface to let them know about it. + */ + s = splimp(); + ifp->if_ioctl(ifp, SIOCADDMULTI, 0); + splx(s); + + return 0; +} + +/* + * Remove a reference to a multicast address on this interface. Yell + * if the request does not match an existing membership. + */ +int +if_delmulti(ifp, sa) + struct ifnet *ifp; + struct sockaddr *sa; +{ + struct ifmultiaddr *ifma; + int s; + + for (ifma = ifp->if_multiaddrs.lh_first; ifma; + ifma = ifma->ifma_link.le_next) + if (equal(sa, ifma->ifma_addr)) + break; + if (ifma == 0) + return ENOENT; + + if (ifma->ifma_refcount > 1) { + ifma->ifma_refcount--; + return 0; + } + + sa = ifma->ifma_lladdr; + s = splimp(); + LIST_REMOVE(ifma, ifma_link); + splx(s); + free(ifma->ifma_addr, M_IFMADDR); + free(ifma, M_IFMADDR); + if (sa == 0) + return 0; + + /* + * Now look for the link-layer address which corresponds to + * this network address. It had been squirreled away in + * ifma->ifma_lladdr for this purpose (so we don't have + * to call ifp->if_resolvemulti() again), and we saved that + * value in sa above. If some nasty deleted the + * link-layer address out from underneath us, we can deal because + * the address we stored was is not the same as the one which was + * in the record for the link-layer address. (So we don't complain + * in that case.) + */ + for (ifma = ifp->if_multiaddrs.lh_first; ifma; + ifma = ifma->ifma_link.le_next) + if (equal(sa, ifma->ifma_addr)) + break; + if (ifma == 0) + return 0; + + if (ifma->ifma_refcount > 1) { + ifma->ifma_refcount--; + return 0; + } + + s = splimp(); + LIST_REMOVE(ifma, ifma_link); + splx(s); + free(ifma->ifma_addr, M_IFMADDR); + free(sa, M_IFMADDR); + free(ifma, M_IFMADDR); + + return 0; +} + SYSCTL_NODE(_net, PF_LINK, link, CTLFLAG_RW, 0, "Link layers"); SYSCTL_NODE(_net_link, 0, generic, CTLFLAG_RW, 0, "Generic link-management"); diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 3aefabe..38d4911 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.28 1996/12/10 07:29:48 davidg Exp $ + * $Id: if_ethersubr.c,v 1.29 1996/12/13 21:28:38 wollman Exp $ */ #include <sys/param.h> @@ -101,6 +101,8 @@ extern u_char at_org_code[ 3 ]; extern u_char aarp_org_code[ 3 ]; #endif NETATALK +static int ether_resolvemulti __P((struct ifnet *, struct sockaddr **, + struct sockaddr *)); u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; #define senderr(e) { error = (e); goto bad;} @@ -651,6 +653,7 @@ ether_ifattach(ifp) ifp->if_addrlen = 6; ifp->if_hdrlen = 14; ifp->if_mtu = ETHERMTU; + ifp->if_resolvemulti = ether_resolvemulti; if (ifp->if_baudrate == 0) ifp->if_baudrate = 10000000; ifa = ifnet_addrs[ifp->if_index - 1]; @@ -946,3 +949,51 @@ ether_ioctl(struct ifnet *ifp, int command, caddr_t data) } return (error); } + +int +ether_resolvemulti(ifp, llsa, sa) + struct ifnet *ifp; + struct sockaddr **llsa; + struct sockaddr *sa; +{ + struct sockaddr_dl *sdl; + struct sockaddr_in *sin; + u_char *e_addr; + + switch(sa->sa_family) { + case AF_LINK: + sdl = (struct sockaddr_dl *)sa; + e_addr = LLADDR(sdl); + if ((e_addr[0] & 1) != 1) + return EADDRNOTAVAIL; + *llsa = 0; + return 0; + +#ifdef INET + case AF_INET: + sin = (struct sockaddr_in *)sa; + if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) + return EADDRNOTAVAIL; + MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, + M_WAITOK); + sdl->sdl_len = sizeof *sdl; + sdl->sdl_family = AF_LINK; + sdl->sdl_index = ifp->if_index; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_nlen = 0; + sdl->sdl_alen = ETHER_ADDR_LEN; + sdl->sdl_slen = 0; + e_addr = LLADDR(sdl); + ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr); + *llsa = (struct sockaddr *)sdl; + return 0; +#endif + + default: + /* + * Well, the text isn't quite right, but it's the name + * that counts... + */ + return EAFNOSUPPORT; + } +} diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 6f9f9e1..98842df 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * From: @(#)if.h 8.1 (Berkeley) 6/10/93 - * $Id: if.h,v 1.41 1996/12/13 21:28:37 wollman Exp $ + * $Id: if_var.h,v 1.1 1997/01/03 19:50:26 wollman Exp $ */ #ifndef _NET_IF_VAR_H_ @@ -77,6 +77,7 @@ struct ether_header; TAILQ_HEAD(ifnethead, ifnet); /* we use TAILQs so that the order of */ TAILQ_HEAD(ifaddrhead, ifaddr); /* instantiation is preserved in the list */ +LIST_HEAD(ifmultihead, ifmultiaddr); /* * Structure defining a queue for a network interface. @@ -109,6 +110,8 @@ struct ifnet { void *if_linkmib; /* link-type-specific MIB data */ size_t if_linkmiblen; /* length of above data */ struct if_data if_data; + struct ifmultihead if_multiaddrs; /* multicast addresses configured */ + int if_amcount; /* number of all-multicast requests */ /* procedure handles */ int (*if_output) /* output routine (enqueue) */ __P((struct ifnet *, struct mbuf *, struct sockaddr *, @@ -131,6 +134,8 @@ struct ifnet { __P((struct ifnet *, struct mbuf *)); void (*if_init) /* Init routine */ __P((void *)); + int (*if_resolvemulti) /* validate/resolve multicast */ + __P((struct ifnet *, struct sockaddr **, struct sockaddr *)); struct ifqueue if_snd; /* output queue */ struct ifqueue *if_poll_slowq; /* input queue for slow devices */ }; @@ -252,6 +257,20 @@ struct ifaddr { }; #define IFA_ROUTE RTF_UP /* route installed */ +/* + * Multicast address structure. This is analogous to the ifaddr + * structure except that it keeps track of multicast addresses. + * Also, the reference count here is a count of requests for this + * address, not a count of pointers to this structure. + */ +struct ifmultiaddr { + LIST_ENTRY(ifmultiaddr) ifma_link; + struct sockaddr *ifma_addr; + struct sockaddr *ifma_lladdr; + struct ifnet *ifma_ifp; + u_int ifma_refcount; +}; + #ifdef KERNEL #define IFAFREE(ifa) \ if ((ifa)->ifa_refcnt <= 0) \ @@ -271,7 +290,10 @@ int ether_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); int ether_ioctl __P((struct ifnet *, int, caddr_t)); +int if_addmulti __P((struct ifnet *, struct sockaddr *)); +int if_allmulti __P((struct ifnet *, int)); void if_attach __P((struct ifnet *)); +int if_delmulti __P((struct ifnet *, struct sockaddr *)); void if_down __P((struct ifnet *)); void if_up __P((struct ifnet *)); #ifdef vax |