From 07ef8870a37b1f275963aee79332df4b672335ee Mon Sep 17 00:00:00 2001 From: Hugo Santos Date: Wed, 9 May 2007 05:37:46 +0000 Subject: [PATCH] freebsd compat. layer: multicast support. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21081 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../freebsd_network/compat/net/if_var.h | 3 + src/libs/compat/freebsd_network/device.c | 18 ++- src/libs/compat/freebsd_network/if.c | 107 ++++++++++++++++++ 3 files changed, 125 insertions(+), 3 deletions(-) diff --git a/src/libs/compat/freebsd_network/compat/net/if_var.h b/src/libs/compat/freebsd_network/compat/net/if_var.h index b66e080cef..df9ae53a97 100644 --- a/src/libs/compat/freebsd_network/compat/net/if_var.h +++ b/src/libs/compat/freebsd_network/compat/net/if_var.h @@ -580,6 +580,9 @@ struct ifmultiaddr { struct ifnet *ifma_ifp; /* back-pointer to interface */ u_int ifma_refcount; /* reference count */ void *ifma_protospec; /* protocol-specific state, if any */ + + /* haiku additions, save a allocation -hugo */ + struct sockaddr_dl ifma_addr_storage; }; #ifdef _KERNEL diff --git a/src/libs/compat/freebsd_network/device.c b/src/libs/compat/freebsd_network/device.c index 6d084c9d3b..520e44e517 100644 --- a/src/libs/compat/freebsd_network/device.c +++ b/src/libs/compat/freebsd_network/device.c @@ -309,9 +309,21 @@ compat_control(void *cookie, uint32 op, void *arg, size_t len) case ETHER_ADDMULTI: case ETHER_REMMULTI: - /* TODO */ - UNIMPLEMENTED(); - return B_ERROR; + { + struct sockaddr_dl address; + + if (!(ifp->if_flags & IFF_MULTICAST) == 0) + return EOPNOTSUPP; + + memset(&address, 0, sizeof(address)); + address.sdl_family = AF_LINK; + memcpy(LLADDR(&address), arg, ETHER_ADDR_LEN); + + if (op == ETHER_ADDMULTI) + return if_addmulti(ifp, (struct sockaddr *)&address, NULL); + else + return if_delmulti(ifp, (struct sockaddr *)&address); + } case ETHER_GET_LINK_STATE: { diff --git a/src/libs/compat/freebsd_network/if.c b/src/libs/compat/freebsd_network/if.c index f1b8e71fea..996d4b32ff 100644 --- a/src/libs/compat/freebsd_network/if.c +++ b/src/libs/compat/freebsd_network/if.c @@ -99,6 +99,8 @@ if_attach(struct ifnet *ifp) TAILQ_INIT(&ifp->if_prefixhead); TAILQ_INIT(&ifp->if_multiaddrs); + IF_ADDR_LOCK_INIT(ifp); + ifq_init((struct ifqueue *)&ifp->if_snd, ifp->if_xname); } @@ -106,6 +108,7 @@ if_attach(struct ifnet *ifp) void if_detach(struct ifnet *ifp) { + IF_ADDR_LOCK_DESTROY(ifp); ifq_uninit((struct ifqueue *)&ifp->if_snd); } @@ -147,6 +150,110 @@ if_link_state_change(struct ifnet *ifp, int link_state) } +static struct ifmultiaddr * +if_findmulti(struct ifnet *ifp, struct sockaddr *_address) +{ + struct sockaddr_dl *address = (struct sockaddr_dl *)_address; + struct ifmultiaddr *ifma; + + TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) { + if (memcmp(LLADDR(address), + LLADDR((struct sockaddr_dl *)ifma->ifma_addr), + ETHER_ADDR_LEN) == 0) + return ifma; + } + + return NULL; +} + + +static struct ifmultiaddr * +_if_addmulti(struct ifnet *ifp, struct sockaddr *address) +{ + struct ifmultiaddr *addr = if_findmulti(ifp, address); + + if (addr != NULL) { + addr->ifma_refcount++; + return addr; + } + + addr = (struct ifmultiaddr *)malloc(sizeof(struct ifmultiaddr)); + if (addr == NULL) + return NULL; + + addr->ifma_lladdr = NULL; + addr->ifma_ifp = ifp; + addr->ifma_protospec = NULL; + + memcpy(&addr->ifma_addr_storage, address, sizeof(struct sockaddr_dl)); + addr->ifma_addr = (struct sockaddr *)&addr->ifma_addr_storage; + + addr->ifma_refcount = 1; + + TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, addr, ifma_link); + + return addr; +} + + +int +if_addmulti(struct ifnet *ifp, struct sockaddr *address, + struct ifmultiaddr **out) +{ + struct ifmultiaddr *result; + int refcount = 0; + + IF_ADDR_LOCK(ifp); + result = _if_addmulti(ifp, address); + if (result) + refcount = result->ifma_refcount; + IF_ADDR_UNLOCK(ifp); + + if (result == NULL) + return ENOBUFS; + + if (refcount == 1) + ifp->if_ioctl(ifp, SIOCADDMULTI, NULL); + + if (out) + (*out) = result; + + return 0; +} + + +static void +if_delete_multiaddr(struct ifnet *ifp, struct ifmultiaddr *ifma) +{ + TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link); + free(ifma); +} + + +int +if_delmulti(struct ifnet *ifp, struct sockaddr *address) +{ + struct ifmultiaddr *addr; + int deleted = 0; + + IF_ADDR_LOCK(ifp); + addr = if_findmulti(ifp, address); + if (addr != NULL) { + addr->ifma_refcount--; + if (addr->ifma_refcount == 0) { + if_delete_multiaddr(ifp, addr); + deleted = 1; + } + } + IF_ADDR_UNLOCK(ifp); + + if (deleted) + ifp->if_ioctl(ifp, SIOCDELMULTI, NULL); + + return 0; +} + + int ether_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt0)