summaryrefslogtreecommitdiff
path: root/sys/netinet6
diff options
context:
space:
mode:
authorMartin Pieuchot <mpi@cvs.openbsd.org>2013-11-28 10:16:45 +0000
committerMartin Pieuchot <mpi@cvs.openbsd.org>2013-11-28 10:16:45 +0000
commitdd2b8403a85186728d2280050afeae817692a102 (patch)
tree2be6ccc8bb7e213382b01dd964b542fc91a7a1e9 /sys/netinet6
parentcba989db5ef2c262d2eeb2ee6f56d8c75d717d38 (diff)
Change the way protocol multicast addresses are linked to an interface.
Instead of linking multicast records to the first configured address of the corresponding protocol, making this address and its position in the global list special, add them to a new list directly linked to the interface descriptor. This new multicast address list is similar to the address list, all its elements contain a protocol agnostic part. This design allows us to be able to join a multicast group without necessarily having a configured address. That means IPv6 multicast kludges are no longer needed. Another benefit is to be able to add and remove an IP address from an interface without worrying about multicast records. That means that the global IPv4 list is no longer needed since the first configured address of an interface is no longer special. This new list might also be extended in the future to contain the link-layer addresses used to configure hardware filters. Tested by sthen@ and weerd@, ok mikeb@
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/in6.c216
-rw-r--r--sys/netinet6/in6_ifattach.c17
-rw-r--r--sys/netinet6/in6_var.h70
-rw-r--r--sys/netinet6/mld6.c23
4 files changed, 83 insertions, 243 deletions
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index 715476a6811..d9a472f8466 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6.c,v 1.125 2013/11/22 07:59:09 mpi Exp $ */
+/* $OpenBSD: in6.c,v 1.126 2013/11/28 10:16:44 mpi Exp $ */
/* $KAME: in6.c,v 1.372 2004/06/14 08:14:21 itojun Exp $ */
/*
@@ -129,18 +129,6 @@ const struct sockaddr_in6 sa6_any = {
};
/*
- * This structure is used to keep track of in6_multi chains which belong to
- * deleted interface addresses.
- */
-static LIST_HEAD(, multi6_kludge) in6_mk; /* XXX BSS initialization */
-
-struct multi6_kludge {
- LIST_ENTRY(multi6_kludge) mk_entry;
- struct ifnet *mk_ifp;
- struct in6_multihead mk_head;
-};
-
-/*
* Subroutine for in6_ifaddloop() and in6_ifremloop().
* This routine does actual work.
*/
@@ -1244,10 +1232,6 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
TAILQ_REMOVE(&in6_ifaddr, ia, ia_list);
- if (!LIST_EMPTY(&ia->ia6_multiaddrs)) {
- in6_savemkludge(ia);
- }
-
/* Release the reference to the base prefix. */
if (ia->ia6_ndpr == NULL) {
char addr[INET6_ADDRSTRLEN];
@@ -1567,141 +1551,19 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, int newhost)
in6_ifaddloop(&(ia->ia_ifa));
}
- if (ifp->if_flags & IFF_MULTICAST)
- in6_restoremkludge(ia, ifp);
-
return (error);
}
/*
- * Multicast address kludge:
- * If there were any multicast addresses attached to this interface address,
- * either move them to another address on this interface, or save them until
- * such time as this interface is reconfigured for IPv6.
- */
-void
-in6_savemkludge(struct in6_ifaddr *oia)
-{
- struct in6_ifaddr *ia;
- struct in6_multi *in6m, *next;
-
- IFP_TO_IA6(oia->ia_ifp, ia);
- if (ia) { /* there is another address */
- for (in6m = LIST_FIRST(&oia->ia6_multiaddrs);
- in6m != NULL; in6m = next) {
- next = LIST_NEXT(in6m, in6m_entry);
- ifafree(&in6m->in6m_ia->ia_ifa);
- ia->ia_ifa.ifa_refcnt++;
- in6m->in6m_ia = ia;
- LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
- }
- } else { /* last address on this if deleted, save */
- struct multi6_kludge *mk;
-
- LIST_FOREACH(mk, &in6_mk, mk_entry) {
- if (mk->mk_ifp == oia->ia_ifp)
- break;
- }
- if (mk == NULL) /* this should not happen! */
- panic("in6_savemkludge: no kludge space");
-
- for (in6m = LIST_FIRST(&oia->ia6_multiaddrs);
- in6m != NULL; in6m = next) {
- next = LIST_NEXT(in6m, in6m_entry);
- ifafree(&in6m->in6m_ia->ia_ifa); /* release reference */
- in6m->in6m_ia = NULL;
- LIST_INSERT_HEAD(&mk->mk_head, in6m, in6m_entry);
- }
- }
-}
-
-/*
- * Continuation of multicast address hack:
- * If there was a multicast group list previously saved for this interface,
- * then we re-attach it to the first address configured on the i/f.
- */
-void
-in6_restoremkludge(struct in6_ifaddr *ia, struct ifnet *ifp)
-{
- struct multi6_kludge *mk;
-
- LIST_FOREACH(mk, &in6_mk, mk_entry) {
- if (mk->mk_ifp == ifp) {
- struct in6_multi *in6m, *next;
-
- for (in6m = LIST_FIRST(&mk->mk_head); in6m != NULL;
- in6m = next) {
- next = LIST_NEXT(in6m, in6m_entry);
- in6m->in6m_ia = ia;
- ia->ia_ifa.ifa_refcnt++;
- LIST_INSERT_HEAD(&ia->ia6_multiaddrs,
- in6m, in6m_entry);
- }
- LIST_INIT(&mk->mk_head);
- break;
- }
- }
-}
-
-/*
- * Allocate space for the kludge at interface initialization time.
- * Formerly, we dynamically allocated the space in in6_savemkludge() with
- * malloc(M_WAITOK). However, it was wrong since the function could be called
- * under an interrupt context (software timer on address lifetime expiration).
- * Also, we cannot just give up allocating the strucutre, since the group
- * membership structure is very complex and we need to keep it anyway.
- * Of course, this function MUST NOT be called under an interrupt context.
- * Specifically, it is expected to be called only from in6_ifattach(), though
- * it is a global function.
- */
-void
-in6_createmkludge(struct ifnet *ifp)
-{
- struct multi6_kludge *mk;
-
- LIST_FOREACH(mk, &in6_mk, mk_entry) {
- /* If we've already had one, do not allocate. */
- if (mk->mk_ifp == ifp)
- return;
- }
-
- mk = malloc(sizeof(*mk), M_IPMADDR, M_WAITOK | M_ZERO);
-
- LIST_INIT(&mk->mk_head);
- mk->mk_ifp = ifp;
- LIST_INSERT_HEAD(&in6_mk, mk, mk_entry);
-}
-
-void
-in6_purgemkludge(struct ifnet *ifp)
-{
- struct multi6_kludge *mk;
- struct in6_multi *in6m;
-
- LIST_FOREACH(mk, &in6_mk, mk_entry) {
- if (mk->mk_ifp != ifp)
- continue;
-
- /* leave from all multicast groups joined */
- while ((in6m = LIST_FIRST(&mk->mk_head)) != NULL)
- in6_delmulti(in6m);
- LIST_REMOVE(mk, mk_entry);
- free(mk, M_IPMADDR);
- break;
- }
-}
-
-/*
* Add an address to the list of IP6 multicast addresses for a
* given interface.
*/
struct in6_multi *
in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp, int *errorp)
{
- struct in6_ifaddr *ia;
struct in6_ifreq ifr;
struct in6_multi *in6m;
- int s = splsoftnet();
+ int s;
*errorp = 0;
/*
@@ -1712,60 +1574,53 @@ in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp, int *errorp)
/*
* Found it; just increment the refrence count.
*/
- in6m->in6m_refcount++;
+ in6m->in6m_refcnt++;
} else {
+ if (ifp->if_ioctl == NULL) {
+ *errorp = ENXIO; /* XXX: appropriate? */
+ return (NULL);
+ }
+
/*
* New address; allocate a new multicast record
* and link it into the interface's multicast list.
*/
- in6m = (struct in6_multi *)
- malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT);
+ in6m = malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT);
if (in6m == NULL) {
- splx(s);
*errorp = ENOBUFS;
return (NULL);
}
- in6m->in6m_addr = *maddr6;
+
+ in6m->in6m_sin.sin6_len = sizeof(struct sockaddr_in6);
+ in6m->in6m_sin.sin6_family = AF_INET6;
+ in6m->in6m_sin.sin6_addr = *maddr6;
+ in6m->in6m_refcnt = 1;
in6m->in6m_ifp = ifp;
- in6m->in6m_refcount = 1;
- IFP_TO_IA6(ifp, ia);
- if (ia == NULL) {
- free(in6m, M_IPMADDR);
- splx(s);
- *errorp = EADDRNOTAVAIL; /* appropriate? */
- return (NULL);
- }
- in6m->in6m_ia = ia;
- ia->ia_ifa.ifa_refcnt++; /* gain a reference */
- LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
+ in6m->in6m_ifma.ifma_addr = sin6tosa(&in6m->in6m_sin);
/*
* Ask the network driver to update its multicast reception
* filter appropriately for the new address.
*/
- bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6));
- ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
- ifr.ifr_addr.sin6_family = AF_INET6;
- 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);
+ memcpy(&ifr.ifr_addr, &in6m->in6m_sin, sizeof(in6m->in6m_sin));
+ *errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr);
if (*errorp) {
- LIST_REMOVE(in6m, in6m_entry);
free(in6m, M_IPMADDR);
- ifafree(&ia->ia_ifa);
- splx(s);
return (NULL);
}
+
+ s = splsoftnet();
+ TAILQ_INSERT_HEAD(&ifp->if_maddrlist, &in6m->in6m_ifma,
+ ifma_list);
+ splx(s);
+
/*
* Let MLD6 know that we have joined a new IP6 multicast
* group.
*/
mld6_start_listening(in6m);
}
- splx(s);
+
return (in6m);
}
@@ -1776,22 +1631,16 @@ void
in6_delmulti(struct in6_multi *in6m)
{
struct in6_ifreq ifr;
- int s = splsoftnet();
+ struct ifnet *ifp;
+ int s;
- if (--in6m->in6m_refcount == 0) {
+ if (--in6m->in6m_refcnt == 0) {
/*
* No remaining claims to this record; let MLD6 know
* that we are leaving the multicast group.
*/
mld6_stop_listening(in6m);
-
- /*
- * Unlink from list.
- */
- LIST_REMOVE(in6m, in6m_entry);
- if (in6m->in6m_ia) {
- ifafree(&in6m->in6m_ia->ia_ifa); /* release reference */
- }
+ ifp = in6m->in6m_ifp;
/*
* Notify the network driver to update its multicast
@@ -1801,11 +1650,14 @@ in6_delmulti(struct in6_multi *in6m)
ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
ifr.ifr_addr.sin6_family = AF_INET6;
ifr.ifr_addr.sin6_addr = in6m->in6m_addr;
- (*in6m->in6m_ifp->if_ioctl)(in6m->in6m_ifp,
- SIOCDELMULTI, (caddr_t)&ifr);
+ (*ifp->if_ioctl)(in6m->in6m_ifp, SIOCDELMULTI, (caddr_t)&ifr);
+
+ s = splsoftnet();
+ TAILQ_REMOVE(&ifp->if_maddrlist, &in6m->in6m_ifma, ifma_list);
+ splx(s);
+
free(in6m, M_IPMADDR);
}
- splx(s);
}
struct in6_multi_mship *
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
index 2f508db892b..dfd723596c6 100644
--- a/sys/netinet6/in6_ifattach.c
+++ b/sys/netinet6/in6_ifattach.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6_ifattach.c,v 1.63 2013/11/19 09:00:43 mpi Exp $ */
+/* $OpenBSD: in6_ifattach.c,v 1.64 2013/11/28 10:16:44 mpi Exp $ */
/* $KAME: in6_ifattach.c,v 1.124 2001/07/18 08:32:51 jinmei Exp $ */
/*
@@ -41,6 +41,7 @@
#include <crypto/md5.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
@@ -594,9 +595,6 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
return;
}
- /* create a multicast kludge storage (if we have not had one) */
- in6_createmkludge(ifp);
-
/*
* quirks based on interface type
*/
@@ -652,6 +650,7 @@ void
in6_ifdetach(struct ifnet *ifp)
{
struct ifaddr *ifa, *next;
+ struct ifmaddr *ifma, *mnext;
struct rtentry *rt;
struct sockaddr_in6 sin6;
@@ -670,8 +669,14 @@ in6_ifdetach(struct ifnet *ifp)
in6_purgeaddr(ifa);
}
- /* cleanup multicast address kludge table, if there is any */
- in6_purgemkludge(ifp);
+
+ TAILQ_FOREACH_SAFE(ifma, &ifp->if_maddrlist, ifma_list, mnext) {
+ if (ifma->ifma_addr->sa_family != AF_INET6)
+ continue;
+
+ ifma->ifma_refcnt = 1;
+ in6_delmulti(ifmatoin6m(ifma));
+ }
/*
* remove neighbor management table. we call it twice just to make
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index 919ff997f9f..4b504494a20 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6_var.h,v 1.45 2013/11/22 07:59:09 mpi Exp $ */
+/* $OpenBSD: in6_var.h,v 1.46 2013/11/28 10:16:44 mpi Exp $ */
/* $KAME: in6_var.h,v 1.55 2001/02/16 12:49:45 itojun Exp $ */
/*
@@ -106,8 +106,6 @@ struct in6_ifaddr {
struct sockaddr_in6 ia_dstaddr; /* space for destination addr */
struct sockaddr_in6 ia_prefixmask; /* prefix mask */
TAILQ_ENTRY(in6_ifaddr) ia_list; /* list of IP6 addresses */
- LIST_HEAD(in6_multihead, in6_multi) ia6_multiaddrs;
- /* list of multicast addresses */
int ia6_flags;
struct in6_addrlifetime ia6_lifetime;
@@ -458,24 +456,6 @@ do { \
} while (0)
/*
- * Macro for finding the internet address structure (in6_ifaddr) corresponding
- * to a given interface (ifnet structure).
- */
-#define IFP_TO_IA6(ifp, ia) \
-/* struct ifnet *ifp; */ \
-/* struct in6_ifaddr *ia; */ \
-do { \
- struct ifaddr *ifa; \
- TAILQ_FOREACH(ifa, &(ifp)->if_addrlist, ifa_list) { \
- if (!ifa->ifa_addr) \
- continue; \
- if (ifa->ifa_addr->sa_family == AF_INET6) \
- break; \
- } \
- (ia) = (struct in6_ifaddr *)ifa; \
-} while (0)
-
-/*
* Multi-cast membership entry. One for each group/ifp that a PCB
* belongs to.
*/
@@ -484,27 +464,23 @@ struct in6_multi_mship {
LIST_ENTRY(in6_multi_mship) i6mm_chain; /* multicast options chain */
};
-struct in6_multi {
- LIST_ENTRY(in6_multi) in6m_entry; /* list glue */
- struct in6_addr in6m_addr; /* IP6 multicast address */
- struct ifnet *in6m_ifp; /* back pointer to ifnet */
- struct in6_ifaddr *in6m_ia; /* back pointer to in6_ifaddr */
- u_int in6m_refcount; /* # membership claims by sockets */
- u_int in6m_state; /* state of the membership */
- u_int in6m_timer; /* MLD6 listener report timer */
+struct in6_multi {
+ struct ifmaddr in6m_ifma; /* Protocol-independent info */
+#define in6m_refcnt in6m_ifma.ifma_refcnt
+#define in6m_ifp in6m_ifma.ifma_ifp
+
+ struct sockaddr_in6 in6m_sin; /* IPv6 multicast address */
+#define in6m_addr in6m_sin.sin6_addr
+
+ u_int in6m_state; /* state of membership */
+ u_int in6m_timer; /* MLD6 membership report timer */
};
-/*
- * Macro for iterating over all the in6_multi records linked to a given
- * interface.
- */
-#define IN6_FOREACH_MULTI(ia, ifp, in6m) \
- /* struct in6_ifaddr *ia; */ \
- /* struct ifnet *ifp; */ \
- /* struct in6_multi *in6m; */ \
- IFP_TO_IA6((ifp), ia); \
- if (ia != NULL) \
- LIST_FOREACH((in6m), &ia->ia6_multiaddrs, in6m_entry) \
+static __inline struct in6_multi *
+ifmatoin6m(struct ifmaddr *ifma)
+{
+ return ((struct in6_multi *)(ifma));
+}
/*
* Macros for looking up the in6_multi record for a given IP6 multicast
@@ -516,12 +492,16 @@ struct in6_multi {
/* struct ifnet *ifp; */ \
/* struct in6_multi *in6m; */ \
do { \
- struct in6_ifaddr *ia; \
+ struct ifmaddr *ifma; \
\
(in6m) = NULL; \
- IN6_FOREACH_MULTI(ia, ifp, in6m) \
- if (IN6_ARE_ADDR_EQUAL(&(in6m)->in6m_addr, &(addr))) \
+ TAILQ_FOREACH(ifma, &(ifp)->if_maddrlist, ifma_list) \
+ if (ifma->ifma_addr->sa_family == AF_INET6 && \
+ IN6_ARE_ADDR_EQUAL(&ifmatoin6m(ifma)->in6m_addr, \
+ &(addr))) { \
+ (in6m) = ifmatoin6m(ifma); \
break; \
+ } \
} while (/* CONSTCOND */ 0)
struct in6_multi *in6_addmulti(struct in6_addr *, struct ifnet *, int *);
@@ -533,13 +513,9 @@ int in6_update_ifa(struct ifnet *, struct in6_aliasreq *,
struct in6_ifaddr *);
void in6_purgeaddr(struct ifaddr *);
int in6if_do_dad(struct ifnet *);
-void in6_savemkludge(struct in6_ifaddr *);
void in6_setmaxmtu(void);
void *in6_domifattach(struct ifnet *);
void in6_domifdetach(struct ifnet *, void *);
-void in6_restoremkludge(struct in6_ifaddr *, struct ifnet *);
-void in6_createmkludge(struct ifnet *);
-void in6_purgemkludge(struct ifnet *);
struct in6_ifaddr *in6ifa_ifpforlinklocal(struct ifnet *, int);
struct in6_ifaddr *in6ifa_ifpwithaddr(struct ifnet *, struct in6_addr *);
int in6_ifpprefix(const struct ifnet *, const struct in6_addr *);
diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c
index 4a52ccc5b46..534493806f4 100644
--- a/sys/netinet6/mld6.c
+++ b/sys/netinet6/mld6.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mld6.c,v 1.33 2013/11/14 23:30:23 patrick Exp $ */
+/* $OpenBSD: mld6.c,v 1.34 2013/11/28 10:16:44 mpi Exp $ */
/* $KAME: mld6.c,v 1.26 2001/02/16 14:50:35 itojun Exp $ */
/*
@@ -74,6 +74,7 @@
#include <dev/rndvar.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
@@ -146,6 +147,8 @@ mld6_start_listening(struct in6_multi *in6m)
void
mld6_stop_listening(struct in6_multi *in6m)
{
+ int s = splsoftnet();
+
mld_all_nodes_linklocal.s6_addr16[1] =
htons(in6m->in6m_ifp->if_index); /* XXX */
mld_all_routers_linklocal.s6_addr16[1] =
@@ -156,6 +159,7 @@ mld6_stop_listening(struct in6_multi *in6m)
__IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > __IPV6_ADDR_SCOPE_INTFACELOCAL)
mld6_sendpkt(in6m, MLD_LISTENER_DONE,
&mld_all_routers_linklocal);
+ splx(s);
}
void
@@ -165,7 +169,7 @@ mld6_input(struct mbuf *m, int off)
struct mld_hdr *mldh;
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct in6_multi *in6m;
- struct in6_ifaddr *ia;
+ struct ifmaddr *ifma;
int timer; /* timer value in the MLD query header */
IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
@@ -227,9 +231,6 @@ mld6_input(struct mbuf *m, int off)
* - Use the value specified in the query message as
* the maximum timeout.
*/
- IFP_TO_IA6(ifp, ia);
- if (ia == NULL)
- break;
/*
* XXX: System timer resolution is too low to handle Max
@@ -243,7 +244,10 @@ mld6_input(struct mbuf *m, int off)
mld_all_nodes_linklocal.s6_addr16[1] =
htons(ifp->if_index); /* XXX */
- LIST_FOREACH(in6m, &ia->ia6_multiaddrs, in6m_entry) {
+ TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
+ if (ifma->ifma_addr->sa_family != AF_INET6)
+ continue;
+ in6m = ifmatoin6m(ifma);
if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr,
&mld_all_nodes_linklocal) ||
__IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
@@ -343,11 +347,14 @@ void
mld6_checktimer(struct ifnet *ifp)
{
struct in6_multi *in6m;
- struct in6_ifaddr *ia;
+ struct ifmaddr *ifma;
splsoftassert(IPL_SOFTNET);
- IN6_FOREACH_MULTI(ia, ifp, in6m) {
+ TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
+ if (ifma->ifma_addr->sa_family != AF_INET6)
+ continue;
+ in6m = ifmatoin6m(ifma);
if (in6m->in6m_timer == 0) {
/* do nothing */
} else if (--in6m->in6m_timer == 0) {