version 1.51.6.1, 2011/06/06 09:10:00 |
version 1.61.2.2, 2015/09/22 12:06:11 |
|
|
#include <sys/cdefs.h> |
#include <sys/cdefs.h> |
__KERNEL_RCSID(0, "$NetBSD$"); |
__KERNEL_RCSID(0, "$NetBSD$"); |
|
|
|
#ifdef _KERNEL_OPT |
#include "opt_inet.h" |
#include "opt_inet.h" |
|
#endif |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
Line 116 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 118 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/sysctl.h> |
#include <sys/sysctl.h> |
#include <sys/kernel.h> |
#include <sys/kernel.h> |
#include <sys/callout.h> |
#include <sys/callout.h> |
|
#include <sys/cprng.h> |
|
|
#include <net/if.h> |
#include <net/if.h> |
|
|
Line 136 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 139 __KERNEL_RCSID(0, "$NetBSD$"); |
|
* This structure is used to keep track of in6_multi chains which belong to |
* This structure is used to keep track of in6_multi chains which belong to |
* deleted interface addresses. |
* deleted interface addresses. |
*/ |
*/ |
static LIST_HEAD(, multi6_kludge) in6_mk; /* XXX BSS initialization */ |
static LIST_HEAD(, multi6_kludge) in6_mk = LIST_HEAD_INITIALIZER(in6_mk); |
|
|
struct multi6_kludge { |
struct multi6_kludge { |
LIST_ENTRY(multi6_kludge) mk_entry; |
LIST_ENTRY(multi6_kludge) mk_entry; |
|
|
ip6_opts.ip6po_hbh = hbh; |
ip6_opts.ip6po_hbh = hbh; |
/* We will specify the hoplimit by a multicast option. */ |
/* We will specify the hoplimit by a multicast option. */ |
ip6_opts.ip6po_hlim = -1; |
ip6_opts.ip6po_hlim = -1; |
|
ip6_opts.ip6po_prefer_tempaddr = IP6PO_TEMPADDR_NOTPREFER; |
} |
} |
|
|
static void |
static void |
Line 194 mld_starttimer(struct in6_multi *in6m) |
|
Line 198 mld_starttimer(struct in6_multi *in6m) |
|
{ |
{ |
struct timeval now; |
struct timeval now; |
|
|
|
KASSERT(in6m->in6m_timer != IN6M_TIMER_UNDEF); |
|
|
microtime(&now); |
microtime(&now); |
in6m->in6m_timer_expire.tv_sec = now.tv_sec + in6m->in6m_timer / hz; |
in6m->in6m_timer_expire.tv_sec = now.tv_sec + in6m->in6m_timer / hz; |
in6m->in6m_timer_expire.tv_usec = now.tv_usec + |
in6m->in6m_timer_expire.tv_usec = now.tv_usec + |
Line 226 mld_timeo(void *arg) |
|
Line 232 mld_timeo(void *arg) |
|
mutex_enter(softnet_lock); |
mutex_enter(softnet_lock); |
KERNEL_LOCK(1, NULL); |
KERNEL_LOCK(1, NULL); |
|
|
|
if (in6m->in6m_timer == IN6M_TIMER_UNDEF) |
|
goto out; |
|
|
in6m->in6m_timer = IN6M_TIMER_UNDEF; |
in6m->in6m_timer = IN6M_TIMER_UNDEF; |
|
|
switch (in6m->in6m_state) { |
switch (in6m->in6m_state) { |
Line 237 mld_timeo(void *arg) |
|
Line 246 mld_timeo(void *arg) |
|
break; |
break; |
} |
} |
|
|
|
out: |
KERNEL_UNLOCK_ONE(NULL); |
KERNEL_UNLOCK_ONE(NULL); |
mutex_exit(softnet_lock); |
mutex_exit(softnet_lock); |
} |
} |
Line 289 mld_start_listening(struct in6_multi *in |
|
Line 299 mld_start_listening(struct in6_multi *in |
|
in6m->in6m_state = MLD_OTHERLISTENER; |
in6m->in6m_state = MLD_OTHERLISTENER; |
} else { |
} else { |
mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL); |
mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL); |
in6m->in6m_timer = arc4random() % |
in6m->in6m_timer = cprng_fast32() % |
(MLD_UNSOLICITED_REPORT_INTERVAL * hz); |
(MLD_UNSOLICITED_REPORT_INTERVAL * hz); |
in6m->in6m_state = MLD_IREPORTEDLAST; |
in6m->in6m_state = MLD_IREPORTEDLAST; |
|
|
Line 384 mld_input(struct mbuf *m, int off) |
|
Line 394 mld_input(struct mbuf *m, int off) |
|
* |
* |
* In Non-Listener state, we simply don't have a membership record. |
* In Non-Listener state, we simply don't have a membership record. |
* In Delaying Listener state, our timer is running (in6m->in6m_timer) |
* In Delaying Listener state, our timer is running (in6m->in6m_timer) |
* In Idle Listener state, our timer is not running |
* In Idle Listener state, our timer is not running |
* (in6m->in6m_timer==IN6M_TIMER_UNDEF) |
* (in6m->in6m_timer==IN6M_TIMER_UNDEF) |
* |
* |
* The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if |
* The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if |
Line 443 mld_input(struct mbuf *m, int off) |
|
Line 453 mld_input(struct mbuf *m, int off) |
|
} else if (in6m->in6m_timer == IN6M_TIMER_UNDEF || |
} else if (in6m->in6m_timer == IN6M_TIMER_UNDEF || |
mld_timerresid(in6m) > timer) { |
mld_timerresid(in6m) > timer) { |
in6m->in6m_timer = |
in6m->in6m_timer = |
1 + (arc4random() % timer) * hz / 1000; |
1 + (cprng_fast32() % timer) * hz / 1000; |
mld_starttimer(in6m); |
mld_starttimer(in6m); |
} |
} |
} |
} |
Line 491 mld_input(struct mbuf *m, int off) |
|
Line 501 mld_input(struct mbuf *m, int off) |
|
} |
} |
|
|
static void |
static void |
mld_sendpkt(struct in6_multi *in6m, int type, |
mld_sendpkt(struct in6_multi *in6m, int type, |
const struct in6_addr *dst) |
const struct in6_addr *dst) |
{ |
{ |
struct mbuf *mh; |
struct mbuf *mh; |
Line 556 mld_sendpkt(struct in6_multi *in6m, int |
|
Line 566 mld_sendpkt(struct in6_multi *in6m, int |
|
} |
} |
|
|
ip6_output(mh, &ip6_opts, NULL, ia ? 0 : IPV6_UNSPECSRC, |
ip6_output(mh, &ip6_opts, NULL, ia ? 0 : IPV6_UNSPECSRC, |
&im6o, (struct socket *)NULL, NULL); |
&im6o, NULL, NULL); |
} |
} |
|
|
static struct mld_hdr * |
static struct mld_hdr * |
Line 612 mld_allocbuf(struct mbuf **mh, int len, |
|
Line 622 mld_allocbuf(struct mbuf **mh, int len, |
|
* Add an address to the list of IP6 multicast addresses for a given interface. |
* Add an address to the list of IP6 multicast addresses for a given interface. |
*/ |
*/ |
struct in6_multi * |
struct in6_multi * |
in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp, |
in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp, |
int *errorp, int timer) |
int *errorp, int timer) |
{ |
{ |
struct in6_ifaddr *ia; |
struct in6_ifaddr *ia; |
struct in6_ifreq ifr; |
struct sockaddr_in6 sin6; |
struct in6_multi *in6m; |
struct in6_multi *in6m; |
int s = splsoftnet(); |
int s = splsoftnet(); |
|
|
Line 656 in6_addmulti(struct in6_addr *maddr6, st |
|
Line 666 in6_addmulti(struct in6_addr *maddr6, st |
|
return (NULL); |
return (NULL); |
} |
} |
in6m->in6m_ia = ia; |
in6m->in6m_ia = ia; |
IFAREF(&ia->ia_ifa); /* gain a reference */ |
ifaref(&ia->ia_ifa); /* gain a reference */ |
LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); |
LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); |
|
|
/* |
/* |
* Ask the network driver to update its multicast reception |
* Ask the network driver to update its multicast reception |
* filter appropriately for the new address. |
* filter appropriately for the new address. |
*/ |
*/ |
sockaddr_in6_init(&ifr.ifr_addr, maddr6, 0, 0, 0); |
sockaddr_in6_init(&sin6, maddr6, 0, 0, 0); |
*errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, &ifr); |
*errorp = if_mcast_op(ifp, SIOCADDMULTI, sin6tosa(&sin6)); |
if (*errorp) { |
if (*errorp) { |
LIST_REMOVE(in6m, in6m_entry); |
LIST_REMOVE(in6m, in6m_entry); |
free(in6m, M_IPMADDR); |
free(in6m, M_IPMADDR); |
IFAFREE(&ia->ia_ifa); |
ifafree(&ia->ia_ifa); |
splx(s); |
splx(s); |
return (NULL); |
return (NULL); |
} |
} |
Line 700 in6_addmulti(struct in6_addr *maddr6, st |
|
Line 710 in6_addmulti(struct in6_addr *maddr6, st |
|
void |
void |
in6_delmulti(struct in6_multi *in6m) |
in6_delmulti(struct in6_multi *in6m) |
{ |
{ |
struct in6_ifreq ifr; |
struct sockaddr_in6 sin6; |
struct in6_ifaddr *ia; |
struct in6_ifaddr *ia; |
int s = splsoftnet(); |
int s = splsoftnet(); |
|
|
Line 718 in6_delmulti(struct in6_multi *in6m) |
|
Line 728 in6_delmulti(struct in6_multi *in6m) |
|
*/ |
*/ |
LIST_REMOVE(in6m, in6m_entry); |
LIST_REMOVE(in6m, in6m_entry); |
if (in6m->in6m_ia != NULL) { |
if (in6m->in6m_ia != NULL) { |
IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */ |
ifafree(&in6m->in6m_ia->ia_ifa); /* release reference */ |
in6m->in6m_ia = NULL; |
in6m->in6m_ia = NULL; |
} |
} |
|
|
Line 738 in6_delmulti(struct in6_multi *in6m) |
|
Line 748 in6_delmulti(struct in6_multi *in6m) |
|
* Notify the network driver to update its multicast |
* Notify the network driver to update its multicast |
* reception filter. |
* reception filter. |
*/ |
*/ |
sockaddr_in6_init(&ifr.ifr_addr, &in6m->in6m_addr, 0, 0, 0); |
sockaddr_in6_init(&sin6, &in6m->in6m_addr, 0, 0, 0); |
(*in6m->in6m_ifp->if_ioctl)(in6m->in6m_ifp, SIOCDELMULTI, &ifr); |
if_mcast_op(in6m->in6m_ifp, SIOCDELMULTI, sin6tosa(&sin6)); |
|
|
|
/* Tell mld_timeo we're halting the timer */ |
|
in6m->in6m_timer = IN6M_TIMER_UNDEF; |
|
callout_halt(&in6m->in6m_timer_ch, softnet_lock); |
callout_destroy(&in6m->in6m_timer_ch); |
callout_destroy(&in6m->in6m_timer_ch); |
|
|
free(in6m, M_IPMADDR); |
free(in6m, M_IPMADDR); |
} |
} |
splx(s); |
splx(s); |
Line 748 in6_delmulti(struct in6_multi *in6m) |
|
Line 763 in6_delmulti(struct in6_multi *in6m) |
|
|
|
|
|
struct in6_multi_mship * |
struct in6_multi_mship * |
in6_joingroup(struct ifnet *ifp, struct in6_addr *addr, |
in6_joingroup(struct ifnet *ifp, struct in6_addr *addr, |
int *errorp, int timer) |
int *errorp, int timer) |
{ |
{ |
struct in6_multi_mship *imm; |
struct in6_multi_mship *imm; |
Line 797 in6_savemkludge(struct in6_ifaddr *oia) |
|
Line 812 in6_savemkludge(struct in6_ifaddr *oia) |
|
KASSERT(ia != oia); |
KASSERT(ia != oia); |
while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL) { |
while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL) { |
LIST_REMOVE(in6m, in6m_entry); |
LIST_REMOVE(in6m, in6m_entry); |
IFAREF(&ia->ia_ifa); |
ifaref(&ia->ia_ifa); |
IFAFREE(&in6m->in6m_ia->ia_ifa); |
ifafree(&in6m->in6m_ia->ia_ifa); |
in6m->in6m_ia = ia; |
in6m->in6m_ia = ia; |
LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); |
LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); |
} |
} |
Line 814 in6_savemkludge(struct in6_ifaddr *oia) |
|
Line 829 in6_savemkludge(struct in6_ifaddr *oia) |
|
|
|
while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL) { |
while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL) { |
LIST_REMOVE(in6m, in6m_entry); |
LIST_REMOVE(in6m, in6m_entry); |
IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */ |
ifafree(&in6m->in6m_ia->ia_ifa); /* release reference */ |
in6m->in6m_ia = NULL; |
in6m->in6m_ia = NULL; |
LIST_INSERT_HEAD(&mk->mk_head, in6m, in6m_entry); |
LIST_INSERT_HEAD(&mk->mk_head, in6m, in6m_entry); |
} |
} |
Line 841 in6_restoremkludge(struct in6_ifaddr *ia |
|
Line 856 in6_restoremkludge(struct in6_ifaddr *ia |
|
while ((in6m = LIST_FIRST(&mk->mk_head)) != NULL) { |
while ((in6m = LIST_FIRST(&mk->mk_head)) != NULL) { |
LIST_REMOVE(in6m, in6m_entry); |
LIST_REMOVE(in6m, in6m_entry); |
in6m->in6m_ia = ia; |
in6m->in6m_ia = ia; |
IFAREF(&ia->ia_ifa); |
ifaref(&ia->ia_ifa); |
LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); |
LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); |
} |
} |
} |
} |
Line 896 in6_purgemkludge(struct ifnet *ifp) |
|
Line 911 in6_purgemkludge(struct ifnet *ifp) |
|
LIST_REMOVE(mk, mk_entry); |
LIST_REMOVE(mk, mk_entry); |
free(mk, M_IPMADDR); |
free(mk, M_IPMADDR); |
} |
} |
|
|
|
static int |
|
in6_mkludge_sysctl(SYSCTLFN_ARGS) |
|
{ |
|
struct multi6_kludge *mk; |
|
struct in6_multi *in6m; |
|
int error; |
|
uint32_t tmp; |
|
size_t written; |
|
|
|
if (namelen != 1) |
|
return EINVAL; |
|
|
|
if (oldp == NULL) { |
|
*oldlenp = 0; |
|
LIST_FOREACH(mk, &in6_mk, mk_entry) { |
|
if (mk->mk_ifp->if_index == name[0]) |
|
continue; |
|
LIST_FOREACH(in6m, &mk->mk_head, in6m_entry) { |
|
*oldlenp += sizeof(struct in6_addr) + |
|
sizeof(uint32_t); |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
error = 0; |
|
written = 0; |
|
LIST_FOREACH(mk, &in6_mk, mk_entry) { |
|
if (mk->mk_ifp->if_index == name[0]) |
|
continue; |
|
LIST_FOREACH(in6m, &mk->mk_head, in6m_entry) { |
|
if (written + sizeof(struct in6_addr) + |
|
sizeof(uint32_t) > *oldlenp) |
|
goto done; |
|
error = sysctl_copyout(l, &in6m->in6m_addr, |
|
oldp, sizeof(struct in6_addr)); |
|
if (error) |
|
goto done; |
|
oldp = (char *)oldp + sizeof(struct in6_addr); |
|
written += sizeof(struct in6_addr); |
|
tmp = in6m->in6m_refcount; |
|
error = sysctl_copyout(l, &tmp, oldp, sizeof(tmp)); |
|
if (error) |
|
goto done; |
|
oldp = (char *)oldp + sizeof(tmp); |
|
written += sizeof(tmp); |
|
} |
|
} |
|
|
|
done: |
|
*oldlenp = written; |
|
return error; |
|
} |
|
|
|
static int |
|
in6_multicast_sysctl(SYSCTLFN_ARGS) |
|
{ |
|
struct ifnet *ifp; |
|
struct ifaddr *ifa; |
|
struct in6_ifaddr *ifa6; |
|
struct in6_multi *in6m; |
|
uint32_t tmp; |
|
int error; |
|
size_t written; |
|
|
|
if (namelen != 1) |
|
return EINVAL; |
|
|
|
ifp = if_byindex(name[0]); |
|
if (ifp == NULL) |
|
return ENODEV; |
|
|
|
if (oldp == NULL) { |
|
*oldlenp = 0; |
|
IFADDR_FOREACH(ifa, ifp) { |
|
if (ifa->ifa_addr == NULL) |
|
continue; |
|
if (ifa->ifa_addr->sa_family != AF_INET6) |
|
continue; |
|
ifa6 = (struct in6_ifaddr *)ifa; |
|
LIST_FOREACH(in6m, &ifa6->ia6_multiaddrs, in6m_entry) { |
|
*oldlenp += 2 * sizeof(struct in6_addr) + |
|
sizeof(uint32_t); |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
error = 0; |
|
written = 0; |
|
IFADDR_FOREACH(ifa, ifp) { |
|
if (ifa->ifa_addr == NULL) |
|
continue; |
|
if (ifa->ifa_addr->sa_family != AF_INET6) |
|
continue; |
|
ifa6 = (struct in6_ifaddr *)ifa; |
|
LIST_FOREACH(in6m, &ifa6->ia6_multiaddrs, in6m_entry) { |
|
if (written + 2 * sizeof(struct in6_addr) + |
|
sizeof(uint32_t) > *oldlenp) |
|
goto done; |
|
error = sysctl_copyout(l, &ifa6->ia_addr.sin6_addr, |
|
oldp, sizeof(struct in6_addr)); |
|
if (error) |
|
goto done; |
|
oldp = (char *)oldp + sizeof(struct in6_addr); |
|
written += sizeof(struct in6_addr); |
|
error = sysctl_copyout(l, &in6m->in6m_addr, |
|
oldp, sizeof(struct in6_addr)); |
|
if (error) |
|
goto done; |
|
oldp = (char *)oldp + sizeof(struct in6_addr); |
|
written += sizeof(struct in6_addr); |
|
tmp = in6m->in6m_refcount; |
|
error = sysctl_copyout(l, &tmp, oldp, sizeof(tmp)); |
|
if (error) |
|
goto done; |
|
oldp = (char *)oldp + sizeof(tmp); |
|
written += sizeof(tmp); |
|
} |
|
} |
|
done: |
|
*oldlenp = written; |
|
return error; |
|
} |
|
|
|
SYSCTL_SETUP(sysctl_in6_mklude_setup, "sysctl net.inet6.multicast_kludge subtree setup") |
|
{ |
|
|
|
sysctl_createv(clog, 0, NULL, NULL, |
|
CTLFLAG_PERMANENT, |
|
CTLTYPE_NODE, "inet6", NULL, |
|
NULL, 0, NULL, 0, |
|
CTL_NET, PF_INET6, CTL_EOL); |
|
|
|
sysctl_createv(clog, 0, NULL, NULL, |
|
CTLFLAG_PERMANENT, |
|
CTLTYPE_NODE, "multicast", |
|
SYSCTL_DESCR("Multicast information"), |
|
in6_multicast_sysctl, 0, NULL, 0, |
|
CTL_NET, PF_INET6, CTL_CREATE, CTL_EOL); |
|
|
|
sysctl_createv(clog, 0, NULL, NULL, |
|
CTLFLAG_PERMANENT, |
|
CTLTYPE_NODE, "multicast_kludge", |
|
SYSCTL_DESCR("multicast kludge information"), |
|
in6_mkludge_sysctl, 0, NULL, 0, |
|
CTL_NET, PF_INET6, CTL_CREATE, CTL_EOL); |
|
} |