version 1.27.4.1, 2006/06/21 15:11:09 |
version 1.56, 2014/06/02 11:02:20 |
Line 110 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 110 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/systm.h> |
#include <sys/systm.h> |
#include <sys/mbuf.h> |
#include <sys/mbuf.h> |
#include <sys/socket.h> |
#include <sys/socket.h> |
|
#include <sys/socketvar.h> |
#include <sys/protosw.h> |
#include <sys/protosw.h> |
#include <sys/syslog.h> |
#include <sys/syslog.h> |
#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 125 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 127 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <netinet6/ip6_var.h> |
#include <netinet6/ip6_var.h> |
#include <netinet6/scope6_var.h> |
#include <netinet6/scope6_var.h> |
#include <netinet/icmp6.h> |
#include <netinet/icmp6.h> |
|
#include <netinet6/icmp6_private.h> |
#include <netinet6/mld6_var.h> |
#include <netinet6/mld6_var.h> |
|
|
#include <net/net_osdep.h> |
#include <net/net_osdep.h> |
Line 134 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 137 __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; |
Line 163 static struct mld_hdr * mld_allocbuf(str |
|
Line 166 static struct mld_hdr * mld_allocbuf(str |
|
static void mld_sendpkt(struct in6_multi *, int, const struct in6_addr *); |
static void mld_sendpkt(struct in6_multi *, int, const struct in6_addr *); |
static void mld_starttimer(struct in6_multi *); |
static void mld_starttimer(struct in6_multi *); |
static void mld_stoptimer(struct in6_multi *); |
static void mld_stoptimer(struct in6_multi *); |
static void mld_timeo(struct in6_multi *); |
|
static u_long mld_timerresid(struct in6_multi *); |
static u_long mld_timerresid(struct in6_multi *); |
|
|
void |
void |
mld_init() |
mld_init(void) |
{ |
{ |
static u_int8_t hbh_buf[8]; |
static u_int8_t hbh_buf[8]; |
struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf; |
struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf; |
|
|
hbh_buf[3] = 0; |
hbh_buf[3] = 0; |
hbh_buf[4] = IP6OPT_RTALERT; |
hbh_buf[4] = IP6OPT_RTALERT; |
hbh_buf[5] = IP6OPT_RTALERT_LEN - 2; |
hbh_buf[5] = IP6OPT_RTALERT_LEN - 2; |
bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t)); |
memcpy(&hbh_buf[6], (void *)&rtalert_code, sizeof(u_int16_t)); |
|
|
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. */ |
|
|
} |
} |
|
|
static void |
static void |
mld_starttimer(in6m) |
mld_starttimer(struct in6_multi *in6m) |
struct in6_multi *in6m; |
|
{ |
{ |
struct timeval now; |
struct timeval now; |
|
|
Line 204 mld_starttimer(in6m) |
|
Line 205 mld_starttimer(in6m) |
|
} |
} |
|
|
/* start or restart the timer */ |
/* start or restart the timer */ |
callout_reset(in6m->in6m_timer_ch, in6m->in6m_timer, |
callout_schedule(&in6m->in6m_timer_ch, in6m->in6m_timer); |
(void (*) __P((void *)))mld_timeo, in6m); |
|
} |
} |
|
|
static void |
static void |
mld_stoptimer(in6m) |
mld_stoptimer(struct in6_multi *in6m) |
struct in6_multi *in6m; |
|
{ |
{ |
if (in6m->in6m_timer == IN6M_TIMER_UNDEF) |
if (in6m->in6m_timer == IN6M_TIMER_UNDEF) |
return; |
return; |
|
|
callout_stop(in6m->in6m_timer_ch); |
callout_stop(&in6m->in6m_timer_ch); |
|
|
in6m->in6m_timer = IN6M_TIMER_UNDEF; |
in6m->in6m_timer = IN6M_TIMER_UNDEF; |
} |
} |
|
|
static void |
static void |
mld_timeo(in6m) |
mld_timeo(void *arg) |
struct in6_multi *in6m; |
|
{ |
{ |
int s = splsoftnet(); |
struct in6_multi *in6m = arg; |
|
|
in6m->in6m_timer = IN6M_TIMER_UNDEF; |
mutex_enter(softnet_lock); |
|
KERNEL_LOCK(1, NULL); |
|
|
callout_stop(in6m->in6m_timer_ch); |
in6m->in6m_timer = IN6M_TIMER_UNDEF; |
|
|
switch (in6m->in6m_state) { |
switch (in6m->in6m_state) { |
case MLD_REPORTPENDING: |
case MLD_REPORTPENDING: |
|
|
break; |
break; |
} |
} |
|
|
splx(s); |
KERNEL_UNLOCK_ONE(NULL); |
|
mutex_exit(softnet_lock); |
} |
} |
|
|
static u_long |
static u_long |
mld_timerresid(in6m) |
mld_timerresid(struct in6_multi *in6m) |
struct in6_multi *in6m; |
|
{ |
{ |
struct timeval now, diff; |
struct timeval now, diff; |
|
|
Line 264 mld_timerresid(in6m) |
|
Line 263 mld_timerresid(in6m) |
|
} |
} |
|
|
/* return the remaining time in milliseconds */ |
/* return the remaining time in milliseconds */ |
return (((u_long)(diff.tv_sec * 1000000 + diff.tv_usec)) / 1000); |
return diff.tv_sec * 1000 + diff.tv_usec / 1000; |
} |
} |
|
|
static void |
static void |
mld_start_listening(in6m) |
mld_start_listening(struct in6_multi *in6m) |
struct in6_multi *in6m; |
|
{ |
{ |
struct in6_addr all_in6; |
struct in6_addr all_in6; |
|
|
Line 292 mld_start_listening(in6m) |
|
Line 290 mld_start_listening(in6m) |
|
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 301 mld_start_listening(in6m) |
|
Line 299 mld_start_listening(in6m) |
|
} |
} |
|
|
static void |
static void |
mld_stop_listening(in6m) |
mld_stop_listening(struct in6_multi *in6m) |
struct in6_multi *in6m; |
|
{ |
{ |
struct in6_addr allnode, allrouter; |
struct in6_addr allnode, allrouter; |
|
|
Line 326 mld_stop_listening(in6m) |
|
Line 323 mld_stop_listening(in6m) |
|
} |
} |
|
|
void |
void |
mld_input(m, off) |
mld_input(struct mbuf *m, int off) |
struct mbuf *m; |
|
int off; |
|
{ |
{ |
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
struct ip6_hdr *ip6; |
struct mld_hdr *mldh; |
struct mld_hdr *mldh; |
struct ifnet *ifp = m->m_pkthdr.rcvif; |
struct ifnet *ifp = m->m_pkthdr.rcvif; |
struct in6_multi *in6m = NULL; |
struct in6_multi *in6m = NULL; |
struct in6_addr mld_addr, all_in6; |
struct in6_addr mld_addr, all_in6; |
struct in6_ifaddr *ia; |
struct in6_ifaddr *ia; |
int timer = 0; /* timer value in the MLD query header */ |
u_long timer = 0; /* timer value in the MLD query header */ |
|
|
IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh)); |
IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh)); |
if (mldh == NULL) { |
if (mldh == NULL) { |
icmp6stat.icp6s_tooshort++; |
ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
return; |
return; |
} |
} |
|
|
Line 428 mld_input(m, off) |
|
Line 423 mld_input(m, off) |
|
if (ia == NULL) |
if (ia == NULL) |
break; |
break; |
|
|
for (in6m = ia->ia6_multiaddrs.lh_first; |
LIST_FOREACH(in6m, &ia->ia6_multiaddrs, in6m_entry) { |
in6m; |
|
in6m = in6m->in6m_entry.le_next) |
|
{ |
|
if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) || |
if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) || |
IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < |
IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < |
IPV6_ADDR_SCOPE_LINKLOCAL) |
IPV6_ADDR_SCOPE_LINKLOCAL) |
Line 450 mld_input(m, off) |
|
Line 442 mld_input(m, off) |
|
mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL); |
mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL); |
in6m->in6m_state = MLD_IREPORTEDLAST; |
in6m->in6m_state = MLD_IREPORTEDLAST; |
} else if (in6m->in6m_timer == IN6M_TIMER_UNDEF || |
} else if (in6m->in6m_timer == IN6M_TIMER_UNDEF || |
mld_timerresid(in6m) > (u_long)timer) { |
mld_timerresid(in6m) > timer) { |
in6m->in6m_timer = arc4random() % |
in6m->in6m_timer = |
(int)(((long)timer * hz) / 1000); |
1 + (cprng_fast32() % timer) * hz / 1000; |
mld_starttimer(in6m); |
mld_starttimer(in6m); |
} |
} |
} |
} |
Line 500 mld_input(m, off) |
|
Line 492 mld_input(m, off) |
|
} |
} |
|
|
static void |
static void |
mld_sendpkt(in6m, type, dst) |
mld_sendpkt(struct in6_multi *in6m, int type, |
struct in6_multi *in6m; |
const struct in6_addr *dst) |
int type; |
|
const struct in6_addr *dst; |
|
{ |
{ |
struct mbuf *mh; |
struct mbuf *mh; |
struct mld_hdr *mldh; |
struct mld_hdr *mldh; |
Line 552 mld_sendpkt(in6m, type, dst) |
|
Line 542 mld_sendpkt(in6m, type, dst) |
|
im6o.im6o_multicast_loop = (ip6_mrouter != NULL); |
im6o.im6o_multicast_loop = (ip6_mrouter != NULL); |
|
|
/* increment output statictics */ |
/* increment output statictics */ |
icmp6stat.icp6s_outhist[type]++; |
ICMP6_STATINC(ICMP6_STAT_OUTHIST + type); |
icmp6_ifstat_inc(ifp, ifs6_out_msg); |
icmp6_ifstat_inc(ifp, ifs6_out_msg); |
switch (type) { |
switch (type) { |
case MLD_LISTENER_QUERY: |
case MLD_LISTENER_QUERY: |
Line 567 mld_sendpkt(in6m, type, dst) |
|
Line 557 mld_sendpkt(in6m, type, dst) |
|
} |
} |
|
|
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 * |
mld_allocbuf(mh, len, in6m, type) |
mld_allocbuf(struct mbuf **mh, int len, struct in6_multi *in6m, |
struct mbuf **mh; |
int type) |
int len; |
|
struct in6_multi *in6m; |
|
int type; |
|
{ |
{ |
struct mbuf *md; |
struct mbuf *md; |
struct mld_hdr *mldh; |
struct mld_hdr *mldh; |
Line 626 mld_allocbuf(mh, len, in6m, type) |
|
Line 613 mld_allocbuf(mh, len, in6m, type) |
|
* 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(maddr6, ifp, errorp, timer) |
in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp, |
struct in6_addr *maddr6; |
int *errorp, int timer) |
struct ifnet *ifp; |
|
int *errorp, 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 653 in6_addmulti(maddr6, ifp, errorp, timer) |
|
Line 638 in6_addmulti(maddr6, ifp, errorp, timer) |
|
* and link it into the interface's multicast list. |
* and link it into the interface's multicast list. |
*/ |
*/ |
in6m = (struct in6_multi *) |
in6m = (struct in6_multi *) |
malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT); |
malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT|M_ZERO); |
if (in6m == NULL) { |
if (in6m == NULL) { |
splx(s); |
splx(s); |
*errorp = ENOBUFS; |
*errorp = ENOBUFS; |
return (NULL); |
return (NULL); |
} |
} |
|
|
memset(in6m, 0, sizeof(*in6m)); |
|
in6m->in6m_addr = *maddr6; |
in6m->in6m_addr = *maddr6; |
in6m->in6m_ifp = ifp; |
in6m->in6m_ifp = ifp; |
in6m->in6m_refcount = 1; |
in6m->in6m_refcount = 1; |
in6m->in6m_timer = IN6M_TIMER_UNDEF; |
in6m->in6m_timer = IN6M_TIMER_UNDEF; |
in6m->in6m_timer_ch = |
|
malloc(sizeof(*in6m->in6m_timer_ch), M_IPMADDR, M_NOWAIT); |
|
if (in6m->in6m_timer_ch == NULL) { |
|
free(in6m, M_IPMADDR); |
|
splx(s); |
|
return (NULL); |
|
} |
|
IFP_TO_IA6(ifp, ia); |
IFP_TO_IA6(ifp, ia); |
if (ia == NULL) { |
if (ia == NULL) { |
free(in6m, M_IPMADDR); |
free(in6m, M_IPMADDR); |
Line 687 in6_addmulti(maddr6, ifp, errorp, timer) |
|
Line 664 in6_addmulti(maddr6, ifp, errorp, timer) |
|
* 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. |
*/ |
*/ |
memset(&ifr.ifr_addr, 0, sizeof(struct sockaddr_in6)); |
sockaddr_in6_init(&sin6, maddr6, 0, 0, 0); |
ifr.ifr_addr.sin6_family = AF_INET6; |
*errorp = if_mcast_op(ifp, SIOCADDMULTI, sin6tosa(&sin6)); |
ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6); |
|
ifr.ifr_addr.sin6_addr = *maddr6; |
|
if (ifp->if_ioctl == NULL) |
|
*errorp = ENXIO; /* XXX: appropriate? */ |
|
else |
|
*errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, |
|
(caddr_t)&ifr); |
|
if (*errorp) { |
if (*errorp) { |
LIST_REMOVE(in6m, in6m_entry); |
LIST_REMOVE(in6m, in6m_entry); |
free(in6m, M_IPMADDR); |
free(in6m, M_IPMADDR); |
Line 704 in6_addmulti(maddr6, ifp, errorp, timer) |
|
Line 674 in6_addmulti(maddr6, ifp, errorp, timer) |
|
return (NULL); |
return (NULL); |
} |
} |
|
|
callout_init(in6m->in6m_timer_ch); |
callout_init(&in6m->in6m_timer_ch, CALLOUT_MPSAFE); |
|
callout_setfunc(&in6m->in6m_timer_ch, mld_timeo, in6m); |
in6m->in6m_timer = timer; |
in6m->in6m_timer = timer; |
if (in6m->in6m_timer > 0) { |
if (in6m->in6m_timer > 0) { |
in6m->in6m_state = MLD_REPORTPENDING; |
in6m->in6m_state = MLD_REPORTPENDING; |
Line 728 in6_addmulti(maddr6, ifp, errorp, timer) |
|
Line 699 in6_addmulti(maddr6, ifp, errorp, timer) |
|
* Delete a multicast address record. |
* Delete a multicast address record. |
*/ |
*/ |
void |
void |
in6_delmulti(in6m) |
in6_delmulti(struct in6_multi *in6m) |
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 748 in6_delmulti(in6m) |
|
Line 718 in6_delmulti(in6m) |
|
* Unlink from list. |
* Unlink from list. |
*/ |
*/ |
LIST_REMOVE(in6m, in6m_entry); |
LIST_REMOVE(in6m, in6m_entry); |
if (in6m->in6m_ia) { |
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; |
} |
} |
|
|
/* |
/* |
Line 758 in6_delmulti(in6m) |
|
Line 729 in6_delmulti(in6m) |
|
*/ |
*/ |
for (ia = in6_ifaddr; ia; ia = ia->ia_next) { |
for (ia = in6_ifaddr; ia; ia = ia->ia_next) { |
struct in6_multi_mship *imm; |
struct in6_multi_mship *imm; |
LIST_FOREACH(imm, &ia->ia6_memberships, |
LIST_FOREACH(imm, &ia->ia6_memberships, i6mm_chain) { |
i6mm_chain) { |
|
if (imm->i6mm_maddr == in6m) |
if (imm->i6mm_maddr == in6m) |
imm->i6mm_maddr = NULL; |
imm->i6mm_maddr = NULL; |
} |
} |
Line 769 in6_delmulti(in6m) |
|
Line 739 in6_delmulti(in6m) |
|
* Notify the network driver to update its multicast |
* Notify the network driver to update its multicast |
* reception filter. |
* reception filter. |
*/ |
*/ |
memset(&ifr.ifr_addr, 0, sizeof(struct sockaddr_in6)); |
sockaddr_in6_init(&sin6, &in6m->in6m_addr, 0, 0, 0); |
ifr.ifr_addr.sin6_family = AF_INET6; |
if_mcast_op(in6m->in6m_ifp, SIOCDELMULTI, sin6tosa(&sin6)); |
ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6); |
callout_destroy(&in6m->in6m_timer_ch); |
ifr.ifr_addr.sin6_addr = in6m->in6m_addr; |
|
(*in6m->in6m_ifp->if_ioctl)(in6m->in6m_ifp, |
|
SIOCDELMULTI, (caddr_t)&ifr); |
|
free(in6m->in6m_timer_ch, M_IPMADDR); |
|
free(in6m, M_IPMADDR); |
free(in6m, M_IPMADDR); |
} |
} |
splx(s); |
splx(s); |
Line 783 in6_delmulti(in6m) |
|
Line 749 in6_delmulti(in6m) |
|
|
|
|
|
struct in6_multi_mship * |
struct in6_multi_mship * |
in6_joingroup(ifp, addr, errorp, timer) |
in6_joingroup(struct ifnet *ifp, struct in6_addr *addr, |
struct ifnet *ifp; |
int *errorp, int timer) |
struct in6_addr *addr; |
|
int *errorp, timer; |
|
{ |
{ |
struct in6_multi_mship *imm; |
struct in6_multi_mship *imm; |
|
|
imm = malloc(sizeof(*imm), M_IPMADDR, M_NOWAIT); |
imm = malloc(sizeof(*imm), M_IPMADDR, M_NOWAIT|M_ZERO); |
if (!imm) { |
if (imm == NULL) { |
*errorp = ENOBUFS; |
*errorp = ENOBUFS; |
return NULL; |
return NULL; |
} |
} |
|
|
memset(imm, 0, sizeof(*imm)); |
|
imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp, timer); |
imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp, timer); |
if (!imm->i6mm_maddr) { |
if (!imm->i6mm_maddr) { |
/* *errorp is alrady set */ |
/* *errorp is already set */ |
free(imm, M_IPMADDR); |
free(imm, M_IPMADDR); |
return NULL; |
return NULL; |
} |
} |
Line 807 in6_joingroup(ifp, addr, errorp, timer) |
|
Line 770 in6_joingroup(ifp, addr, errorp, timer) |
|
} |
} |
|
|
int |
int |
in6_leavegroup(imm) |
in6_leavegroup(struct in6_multi_mship *imm) |
struct in6_multi_mship *imm; |
|
{ |
{ |
|
|
if (imm->i6mm_maddr) { |
if (imm->i6mm_maddr) { |
Line 826 in6_leavegroup(imm) |
|
Line 788 in6_leavegroup(imm) |
|
* such time as this interface is reconfigured for IPv6. |
* such time as this interface is reconfigured for IPv6. |
*/ |
*/ |
void |
void |
in6_savemkludge(oia) |
in6_savemkludge(struct in6_ifaddr *oia) |
struct in6_ifaddr *oia; |
|
{ |
{ |
struct in6_ifaddr *ia; |
struct in6_ifaddr *ia; |
struct in6_multi *in6m, *next; |
struct in6_multi *in6m; |
|
|
IFP_TO_IA6(oia->ia_ifp, ia); |
IFP_TO_IA6(oia->ia_ifp, ia); |
if (ia) { /* there is another address */ |
if (ia) { /* there is another address */ |
for (in6m = oia->ia6_multiaddrs.lh_first; in6m; in6m = next) { |
KASSERT(ia != oia); |
next = in6m->in6m_entry.le_next; |
while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL) { |
IFAFREE(&in6m->in6m_ia->ia_ifa); |
LIST_REMOVE(in6m, in6m_entry); |
IFAREF(&ia->ia_ifa); |
IFAREF(&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); |
} |
} |
} else { /* last address on this if deleted, save */ |
} else { /* last address on this if deleted, save */ |
struct multi6_kludge *mk; |
struct multi6_kludge *mk; |
|
|
for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) { |
LIST_FOREACH(mk, &in6_mk, mk_entry) { |
if (mk->mk_ifp == oia->ia_ifp) |
if (mk->mk_ifp == oia->ia_ifp) |
break; |
break; |
} |
} |
if (mk == NULL) /* this should not happen! */ |
if (mk == NULL) /* this should not happen! */ |
panic("in6_savemkludge: no kludge space"); |
panic("in6_savemkludge: no kludge space"); |
|
|
for (in6m = oia->ia6_multiaddrs.lh_first; in6m; in6m = next) { |
while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL) { |
next = in6m->in6m_entry.le_next; |
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 866 in6_savemkludge(oia) |
|
Line 828 in6_savemkludge(oia) |
|
* then we re-attach it to the first address configured on the i/f. |
* then we re-attach it to the first address configured on the i/f. |
*/ |
*/ |
void |
void |
in6_restoremkludge(ia, ifp) |
in6_restoremkludge(struct in6_ifaddr *ia, struct ifnet *ifp) |
struct in6_ifaddr *ia; |
|
struct ifnet *ifp; |
|
{ |
{ |
struct multi6_kludge *mk; |
struct multi6_kludge *mk; |
|
struct in6_multi *in6m; |
|
|
for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) { |
LIST_FOREACH(mk, &in6_mk, mk_entry) { |
if (mk->mk_ifp == ifp) { |
if (mk->mk_ifp == ifp) |
struct in6_multi *in6m, *next; |
|
|
|
for (in6m = mk->mk_head.lh_first; in6m; in6m = next) { |
|
next = in6m->in6m_entry.le_next; |
|
in6m->in6m_ia = ia; |
|
IFAREF(&ia->ia_ifa); |
|
LIST_INSERT_HEAD(&ia->ia6_multiaddrs, |
|
in6m, in6m_entry); |
|
} |
|
LIST_INIT(&mk->mk_head); |
|
break; |
break; |
} |
} |
|
if (mk == NULL) |
|
return; |
|
while ((in6m = LIST_FIRST(&mk->mk_head)) != NULL) { |
|
LIST_REMOVE(in6m, in6m_entry); |
|
in6m->in6m_ia = ia; |
|
IFAREF(&ia->ia_ifa); |
|
LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); |
} |
} |
} |
} |
|
|
Line 901 in6_restoremkludge(ia, ifp) |
|
Line 859 in6_restoremkludge(ia, ifp) |
|
* it is a global function. |
* it is a global function. |
*/ |
*/ |
void |
void |
in6_createmkludge(ifp) |
in6_createmkludge(struct ifnet *ifp) |
struct ifnet *ifp; |
|
{ |
{ |
struct multi6_kludge *mk; |
struct multi6_kludge *mk; |
|
|
for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) { |
LIST_FOREACH(mk, &in6_mk, mk_entry) { |
/* If we've already had one, do not allocate. */ |
/* If we've already had one, do not allocate. */ |
if (mk->mk_ifp == ifp) |
if (mk->mk_ifp == ifp) |
return; |
return; |
} |
} |
|
|
mk = malloc(sizeof(*mk), M_IPMADDR, M_WAITOK); |
mk = malloc(sizeof(*mk), M_IPMADDR, M_ZERO|M_WAITOK); |
|
|
memset(mk, 0, sizeof(*mk)); |
|
LIST_INIT(&mk->mk_head); |
LIST_INIT(&mk->mk_head); |
mk->mk_ifp = ifp; |
mk->mk_ifp = ifp; |
LIST_INSERT_HEAD(&in6_mk, mk, mk_entry); |
LIST_INSERT_HEAD(&in6_mk, mk, mk_entry); |
} |
} |
|
|
void |
void |
in6_purgemkludge(ifp) |
in6_purgemkludge(struct ifnet *ifp) |
struct ifnet *ifp; |
|
{ |
{ |
struct multi6_kludge *mk; |
struct multi6_kludge *mk; |
struct in6_multi *in6m; |
struct in6_multi *in6m, *next; |
|
|
for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) { |
LIST_FOREACH(mk, &in6_mk, mk_entry) { |
if (mk->mk_ifp != ifp) |
if (mk->mk_ifp == ifp) |
continue; |
break; |
|
} |
/* leave from all multicast groups joined */ |
if (mk == NULL) |
while ((in6m = LIST_FIRST(&mk->mk_head)) != NULL) { |
return; |
in6_delmulti(in6m); |
|
} |
/* leave from all multicast groups joined */ |
LIST_REMOVE(mk, mk_entry); |
for (in6m = LIST_FIRST(&mk->mk_head); in6m != NULL; in6m = next) { |
free(mk, M_IPMADDR); |
next = LIST_NEXT(in6m, in6m_entry); |
break; |
in6_delmulti(in6m); |
} |
} |
|
LIST_REMOVE(mk, mk_entry); |
|
free(mk, M_IPMADDR); |
} |
} |