preserve gif(4) configs by psref(9) like vlan(4) and l2tp(4).

After Tx side does not use softint, gif(4) can use psref(9) for config
preservation like vlan(4) and l2tp(4).

update locking notes later.
This commit is contained in:
knakahara 2017-11-27 05:02:22 +00:00
parent 44451ae059
commit 493e35e35d
6 changed files with 468 additions and 331 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_gif.c,v 1.132 2017/11/16 03:07:18 ozaki-r Exp $ */
/* $NetBSD: if_gif.c,v 1.133 2017/11/27 05:02:22 knakahara Exp $ */
/* $KAME: if_gif.c,v 1.76 2001/08/20 02:01:02 kjc Exp $ */
/*
@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.132 2017/11/16 03:07:18 ozaki-r Exp $");
__KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.133 2017/11/27 05:02:22 knakahara Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@ -57,6 +57,9 @@ __KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.132 2017/11/16 03:07:18 ozaki-r Exp $")
#include <sys/xcall.h>
#include <sys/device.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/pserialize.h>
#include <sys/psref.h>
#include <net/if.h>
#include <net/if_types.h>
@ -102,6 +105,9 @@ static struct {
kmutex_t lock;
} gif_softcs __cacheline_aligned;
pserialize_t gif_psz __read_mostly;
struct psref_class *gv_psref_class __read_mostly;
static void gif_ro_init_pc(void *, void *, struct cpu_info *);
static void gif_ro_fini_pc(void *, void *, struct cpu_info *);
@ -110,6 +116,7 @@ static int gif_output(struct ifnet *, struct mbuf *,
const struct sockaddr *, const struct rtentry *);
static void gif_start(struct ifnet *);
static int gif_transmit(struct ifnet *, struct mbuf *);
static int gif_transmit_direct(struct gif_variant *, struct mbuf *);
static int gif_ioctl(struct ifnet *, u_long, void *);
static int gif_set_tunnel(struct ifnet *, struct sockaddr *,
struct sockaddr *);
@ -119,9 +126,10 @@ static int gif_clone_create(struct if_clone *, int);
static int gif_clone_destroy(struct ifnet *);
static int gif_check_nesting(struct ifnet *, struct mbuf *);
static int gif_encap_attach(struct gif_softc *);
static int gif_encap_detach(struct gif_softc *);
static void gif_encap_pause(struct gif_softc *);
static int gif_encap_attach(struct gif_variant *);
static int gif_encap_detach(struct gif_variant *);
static void gif_update_variant(struct gif_softc *, struct gif_variant *);
static struct if_clone gif_cloner =
IF_CLONE_INITIALIZER("gif", gif_clone_create, gif_clone_destroy);
@ -216,6 +224,9 @@ gifinit(void)
LIST_INIT(&gif_softcs.list);
if_clone_attach(&gif_cloner);
gif_psz = pserialize_create();
gv_psref_class = psref_class_create("gifvar", IPL_SOFTNET);
gif_sysctl_setup();
}
@ -231,6 +242,9 @@ gifdetach(void)
}
if (error == 0) {
psref_class_destroy(gv_psref_class);
pserialize_destroy(gif_psz);
if_clone_detach(&gif_cloner);
sysctl_teardown(&gif_sysctl);
}
@ -242,6 +256,7 @@ static int
gif_clone_create(struct if_clone *ifc, int unit)
{
struct gif_softc *sc;
struct gif_variant *var;
int rv;
sc = kmem_zalloc(sizeof(struct gif_softc), KM_SLEEP);
@ -254,6 +269,12 @@ gif_clone_create(struct if_clone *ifc, int unit)
return rv;
}
var = kmem_zalloc(sizeof(*var), KM_SLEEP);
var->gv_softc = sc;
psref_target_init(&var->gv_psref, gv_psref_class);
sc->gif_var = var;
mutex_init(&sc->gif_lock, MUTEX_DEFAULT, IPL_NONE);
sc->gif_ro_percpu = percpu_alloc(sizeof(struct gif_ro));
percpu_foreach(sc->gif_ro_percpu, gif_ro_init_pc, NULL);
@ -268,8 +289,6 @@ gifattach0(struct gif_softc *sc)
{
int rv;
sc->encap_cookie4 = sc->encap_cookie6 = NULL;
sc->gif_if.if_addrlen = 0;
sc->gif_if.if_mtu = GIF_MTU;
sc->gif_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
@ -325,6 +344,7 @@ static int
gif_clone_destroy(struct ifnet *ifp)
{
struct gif_softc *sc = (void *) ifp;
struct gif_variant *var;
LIST_REMOVE(sc, gif_list);
@ -335,6 +355,10 @@ gif_clone_destroy(struct ifnet *ifp)
percpu_foreach(sc->gif_ro_percpu, gif_ro_fini_pc, NULL);
percpu_free(sc->gif_ro_percpu, sizeof(struct gif_ro));
mutex_destroy(&sc->gif_lock);
var = sc->gif_var;
kmem_free(var, sizeof(*var));
kmem_free(sc, sizeof(struct gif_softc));
return 0;
@ -346,6 +370,9 @@ gif_encapcheck(struct mbuf *m, int off, int proto, void *arg)
{
struct ip ip;
struct gif_softc *sc;
struct gif_variant *var;
struct psref psref;
int ret = 0;
sc = arg;
if (sc == NULL)
@ -355,9 +382,13 @@ gif_encapcheck(struct mbuf *m, int off, int proto, void *arg)
!= (IFF_UP|IFF_RUNNING))
return 0;
var = gif_getref_variant(sc, &psref);
if (var->gv_psrc == NULL || var->gv_pdst == NULL)
goto out;
/* no physical address */
if (!sc->gif_psrc || !sc->gif_pdst)
return 0;
if (!var->gv_psrc || !var->gv_pdst)
goto out;
switch (proto) {
#ifdef INET
@ -369,36 +400,42 @@ gif_encapcheck(struct mbuf *m, int off, int proto, void *arg)
break;
#endif
default:
return 0;
goto out;
}
/* Bail on short packets */
KASSERT(m->m_flags & M_PKTHDR);
if (m->m_pkthdr.len < sizeof(ip))
return 0;
goto out;
m_copydata(m, 0, sizeof(ip), &ip);
switch (ip.ip_v) {
#ifdef INET
case 4:
if (sc->gif_psrc->sa_family != AF_INET ||
sc->gif_pdst->sa_family != AF_INET)
return 0;
return gif_encapcheck4(m, off, proto, arg);
if (var->gv_psrc->sa_family != AF_INET ||
var->gv_pdst->sa_family != AF_INET)
goto out;
ret = gif_encapcheck4(m, off, proto, var);
break;
#endif
#ifdef INET6
case 6:
if (m->m_pkthdr.len < sizeof(struct ip6_hdr))
return 0;
if (sc->gif_psrc->sa_family != AF_INET6 ||
sc->gif_pdst->sa_family != AF_INET6)
return 0;
return gif_encapcheck6(m, off, proto, arg);
goto out;
if (var->gv_psrc->sa_family != AF_INET6 ||
var->gv_pdst->sa_family != AF_INET6)
goto out;
ret = gif_encapcheck6(m, off, proto, var);
break;
#endif
default:
return 0;
goto out;
}
out:
gif_putref_variant(var, &psref);
return ret;
}
#endif
@ -444,6 +481,8 @@ gif_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
const struct rtentry *rt)
{
struct gif_softc *sc = ifp->if_softc;
struct gif_variant *var = NULL;
struct psref psref;
int error = 0;
IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family);
@ -453,16 +492,22 @@ gif_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
goto end;
}
m->m_flags &= ~(M_BCAST|M_MCAST);
if (((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) ||
sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
m_freem(m);
error = ENETDOWN;
goto end;
}
var = gif_getref_variant(sc, &psref);
if (var->gv_psrc == NULL || var->gv_pdst == NULL) {
m_freem(m);
error = ENETDOWN;
goto end;
}
/* XXX should we check if our outer source is legal? */
m->m_flags &= ~(M_BCAST|M_MCAST);
/* use DLT_NULL encapsulation here to pass inner af type */
M_PREPEND(m, sizeof(int), M_DONTWAIT);
if (!m) {
@ -475,8 +520,10 @@ gif_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
m->m_pkthdr.csum_flags = 0;
m->m_pkthdr.csum_data = 0;
error = if_transmit_lock(ifp, m);
end:
error = gif_transmit_direct(var, m);
end:
if (var != NULL)
gif_putref_variant(var, &psref);
if (error)
ifp->if_oerrors++;
return error;
@ -486,12 +533,17 @@ static void
gif_start(struct ifnet *ifp)
{
struct gif_softc *sc;
struct gif_variant *var;
struct mbuf *m;
struct psref psref;
int family;
int len;
int error;
sc = ifp->if_softc;
var = gif_getref_variant(sc, &psref);
KASSERT(var->gv_output != NULL);
/* output processing */
while (1) {
@ -513,33 +565,7 @@ gif_start(struct ifnet *ifp)
len = m->m_pkthdr.len;
/* dispatch to output logic based on outer AF */
switch (sc->gif_psrc->sa_family) {
#ifdef INET
case AF_INET:
/* XXX
* To add mutex_enter(softnet_lock) or
* KASSERT(mutex_owned(softnet_lock)) here, we shold
* coordinate softnet_lock between in6_if_up() and
* in6_purgeif().
*/
error = in_gif_output(ifp, family, m);
break;
#endif
#ifdef INET6
case AF_INET6:
/* XXX
* the same as in_gif_output()
*/
error = in6_gif_output(ifp, family, m);
break;
#endif
default:
m_freem(m);
error = ENETDOWN;
break;
}
error = var->gv_output(var, family, m);
if (error)
ifp->if_oerrors++;
else {
@ -547,14 +573,16 @@ gif_start(struct ifnet *ifp)
ifp->if_obytes += len;
}
}
gif_putref_variant(var, &psref);
}
static int
gif_transmit(struct ifnet *ifp, struct mbuf *m)
{
struct gif_softc *sc;
int family;
int len;
struct gif_variant *var;
struct psref psref;
int error;
sc = ifp->if_softc;
@ -563,6 +591,24 @@ gif_transmit(struct ifnet *ifp, struct mbuf *m)
if (m == NULL)
return EINVAL;
var = gif_getref_variant(sc, &psref);
error = gif_transmit_direct(var, m);
gif_putref_variant(var, &psref);
return error;
}
static int
gif_transmit_direct(struct gif_variant *var, struct mbuf *m)
{
struct ifnet *ifp = &var->gv_softc->gif_if;
int error;
int family;
int len;
KASSERT(gif_heldref_variant(var));
KASSERT(var->gv_output != NULL);
/* grab and chop off inner af type */
if (sizeof(int) > m->m_len) {
m = m_pullup(m, sizeof(int));
@ -577,33 +623,7 @@ gif_transmit(struct ifnet *ifp, struct mbuf *m)
len = m->m_pkthdr.len;
/* dispatch to output logic based on outer AF */
switch (sc->gif_psrc->sa_family) {
#ifdef INET
case AF_INET:
/* XXX
* To add mutex_enter(softnet_lock) or
* KASSERT(mutex_owned(softnet_lock)) here, we shold
* coordinate softnet_lock between in6_if_up() and
* in6_purgeif().
*/
error = in_gif_output(ifp, family, m);
break;
#endif
#ifdef INET6
case AF_INET6:
/* XXX
* the same as in_gif_output()
*/
error = in6_gif_output(ifp, family, m);
break;
#endif
default:
m_freem(m);
error = ENETDOWN;
break;
}
error = var->gv_output(var, family, m);
if (error)
ifp->if_oerrors++;
else {
@ -673,8 +693,10 @@ gif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
struct gif_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq*)data;
struct ifaddr *ifa = (struct ifaddr*)data;
int error = 0, size;
int error = 0, size, bound;
struct sockaddr *dst, *src;
struct gif_variant *var;
struct psref psref;
switch (cmd) {
case SIOCINITIFADDR:
@ -794,13 +816,20 @@ gif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
/* checks done in the above */
break;
}
/*
* calls gif_getref_variant() for other softcs to check
* address pair duplicattion
*/
bound = curlwp_bind();
error = gif_set_tunnel(&sc->gif_if, src, dst);
curlwp_bindx(bound);
break;
#ifdef SIOCDIFPHYADDR
case SIOCDIFPHYADDR:
bound = curlwp_bind();
gif_delete_tunnel(&sc->gif_if);
curlwp_bindx(bound);
break;
#endif
@ -808,11 +837,15 @@ gif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
#ifdef INET6
case SIOCGIFPSRCADDR_IN6:
#endif /* INET6 */
if (sc->gif_psrc == NULL) {
bound = curlwp_bind();
var = gif_getref_variant(sc, &psref);
if (var->gv_psrc == NULL) {
gif_putref_variant(var, &psref);
curlwp_bindx(bound);
error = EADDRNOTAVAIL;
goto bad;
}
src = sc->gif_psrc;
src = var->gv_psrc;
switch (cmd) {
#ifdef INET
case SIOCGIFPSRCADDR:
@ -828,23 +861,34 @@ gif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
break;
#endif /* INET6 */
default:
gif_putref_variant(var, &psref);
curlwp_bindx(bound);
error = EADDRNOTAVAIL;
goto bad;
}
if (src->sa_len > size)
if (src->sa_len > size) {
gif_putref_variant(var, &psref);
curlwp_bindx(bound);
return EINVAL;
}
memcpy(dst, src, src->sa_len);
gif_putref_variant(var, &psref);
curlwp_bindx(bound);
break;
case SIOCGIFPDSTADDR:
#ifdef INET6
case SIOCGIFPDSTADDR_IN6:
#endif /* INET6 */
if (sc->gif_pdst == NULL) {
bound = curlwp_bind();
var = gif_getref_variant(sc, &psref);
if (var->gv_pdst == NULL) {
gif_putref_variant(var, &psref);
curlwp_bindx(bound);
error = EADDRNOTAVAIL;
goto bad;
}
src = sc->gif_pdst;
src = var->gv_pdst;
switch (cmd) {
#ifdef INET
case SIOCGIFPDSTADDR:
@ -860,37 +904,56 @@ gif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
break;
#endif /* INET6 */
default:
gif_putref_variant(var, &psref);
curlwp_bindx(bound);
error = EADDRNOTAVAIL;
goto bad;
}
if (src->sa_len > size)
if (src->sa_len > size) {
gif_putref_variant(var, &psref);
curlwp_bindx(bound);
return EINVAL;
}
memcpy(dst, src, src->sa_len);
gif_putref_variant(var, &psref);
curlwp_bindx(bound);
break;
case SIOCGLIFPHYADDR:
if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
bound = curlwp_bind();
var = gif_getref_variant(sc, &psref);
if (var->gv_psrc == NULL || var->gv_pdst == NULL) {
gif_putref_variant(var, &psref);
curlwp_bindx(bound);
error = EADDRNOTAVAIL;
goto bad;
}
/* copy src */
src = sc->gif_psrc;
src = var->gv_psrc;
dst = (struct sockaddr *)
&(((struct if_laddrreq *)data)->addr);
size = sizeof(((struct if_laddrreq *)data)->addr);
if (src->sa_len > size)
if (src->sa_len > size) {
gif_putref_variant(var, &psref);
curlwp_bindx(bound);
return EINVAL;
}
memcpy(dst, src, src->sa_len);
/* copy dst */
src = sc->gif_pdst;
src = var->gv_pdst;
dst = (struct sockaddr *)
&(((struct if_laddrreq *)data)->dstaddr);
size = sizeof(((struct if_laddrreq *)data)->dstaddr);
if (src->sa_len > size)
if (src->sa_len > size) {
gif_putref_variant(var, &psref);
curlwp_bindx(bound);
return EINVAL;
}
memcpy(dst, src, src->sa_len);
gif_putref_variant(var, &psref);
curlwp_bindx(bound);
break;
default:
@ -901,22 +964,22 @@ gif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
}
static int
gif_encap_attach(struct gif_softc *sc)
gif_encap_attach(struct gif_variant *var)
{
int error;
if (sc == NULL || sc->gif_psrc == NULL)
if (var == NULL || var->gv_psrc == NULL)
return EINVAL;
switch (sc->gif_psrc->sa_family) {
switch (var->gv_psrc->sa_family) {
#ifdef INET
case AF_INET:
error = in_gif_attach(sc);
error = in_gif_attach(var);
break;
#endif
#ifdef INET6
case AF_INET6:
error = in6_gif_attach(sc);
error = in6_gif_attach(var);
break;
#endif
default:
@ -928,22 +991,22 @@ gif_encap_attach(struct gif_softc *sc)
}
static int
gif_encap_detach(struct gif_softc *sc)
gif_encap_detach(struct gif_variant *var)
{
int error;
if (sc == NULL || sc->gif_psrc == NULL)
if (var == NULL || var->gv_psrc == NULL)
return EINVAL;
switch (sc->gif_psrc->sa_family) {
switch (var->gv_psrc->sa_family) {
#ifdef INET
case AF_INET:
error = in_gif_detach(sc);
error = in_gif_detach(var);
break;
#endif
#ifdef INET6
case AF_INET6:
error = in6_gif_detach(sc);
error = in6_gif_detach(var);
break;
#endif
default:
@ -954,50 +1017,12 @@ gif_encap_detach(struct gif_softc *sc)
return error;
}
static void
gif_encap_pause(struct gif_softc *sc)
{
struct ifnet *ifp;
uint64_t where;
if (sc == NULL || sc->gif_psrc == NULL)
return;
ifp = &sc->gif_if;
if ((ifp->if_flags & IFF_RUNNING) == 0)
return;
switch (sc->gif_psrc->sa_family) {
#ifdef INET
case AF_INET:
(void)in_gif_pause(sc);
break;
#endif
#ifdef INET6
case AF_INET6:
(void)in6_gif_pause(sc);
break;
#endif
}
ifp->if_flags &= ~IFF_RUNNING;
/* membar_sync() is done in xc_broadcast(). */
/*
* Wait for softint_execute()(ipintr() or ip6intr())
* completion done by other CPUs which already run over if_flags
* check in in_gif_input() or in6_gif_input().
* Furthermore, wait for gif_output() completion too.
*/
where = xc_broadcast(0, (xcfunc_t)nullop, NULL, NULL);
xc_wait(where);
}
static int
gif_set_tunnel(struct ifnet *ifp, struct sockaddr *src, struct sockaddr *dst)
{
struct gif_softc *sc = ifp->if_softc;
struct gif_softc *sc2;
struct gif_variant *ovar, *nvar;
struct sockaddr *osrc, *odst;
struct sockaddr *nsrc, *ndst;
int error;
@ -1014,78 +1039,84 @@ gif_set_tunnel(struct ifnet *ifp, struct sockaddr *src, struct sockaddr *dst)
return error;
}
nsrc = sockaddr_dup(src, M_WAITOK);
ndst = sockaddr_dup(dst, M_WAITOK);
nvar = kmem_alloc(sizeof(*nvar), KM_SLEEP);
mutex_enter(&sc->gif_lock);
ovar = sc->gif_var;
if ((ovar->gv_pdst && sockaddr_cmp(ovar->gv_pdst, dst) == 0) &&
(ovar->gv_psrc && sockaddr_cmp(ovar->gv_psrc, src) == 0)) {
/* address and port pair not changed. */
error = 0;
goto out;
}
mutex_enter(&gif_softcs.lock);
LIST_FOREACH(sc2, &gif_softcs.list, gif_list) {
struct gif_variant *var2;
struct psref psref;
if (sc2 == sc)
continue;
if (!sc2->gif_pdst || !sc2->gif_psrc)
var2 = gif_getref_variant(sc, &psref);
if (!var2->gv_pdst || !var2->gv_psrc) {
gif_putref_variant(var2, &psref);
continue;
}
/* can't configure same pair of address onto two gifs */
if (sockaddr_cmp(sc2->gif_pdst, dst) == 0 &&
sockaddr_cmp(sc2->gif_psrc, src) == 0) {
if (sockaddr_cmp(var2->gv_pdst, dst) == 0 &&
sockaddr_cmp(var2->gv_psrc, src) == 0) {
/* continue to use the old configureation. */
gif_putref_variant(var2, &psref);
mutex_exit(&gif_softcs.lock);
error = EADDRNOTAVAIL;
goto out;
}
gif_putref_variant(var2, &psref);
/* XXX both end must be valid? (I mean, not 0.0.0.0) */
}
mutex_exit(&gif_softcs.lock);
nsrc = sockaddr_dup(src, M_WAITOK);
ndst = sockaddr_dup(dst, M_WAITOK);
osrc = ovar->gv_psrc;
odst = ovar->gv_pdst;
gif_encap_pause(sc);
*nvar = *ovar;
nvar->gv_psrc = nsrc;
nvar->gv_pdst = ndst;
nvar->gv_encap_cookie4 = NULL;
nvar->gv_encap_cookie6 = NULL;
error = gif_encap_attach(nvar);
if (error)
goto out;
psref_target_init(&nvar->gv_psref, gv_psref_class);
membar_producer();
gif_update_variant(sc, nvar);
/* Firstly, clear old configurations. */
/* XXX we can detach from both, but be polite just in case */
if (sc->gif_psrc)
(void)gif_encap_detach(sc);
mutex_exit(&sc->gif_lock);
/*
* Secondly, try to set new configurations.
*/
osrc = sc->gif_psrc;
odst = sc->gif_pdst;
sc->gif_psrc = nsrc;
sc->gif_pdst = ndst;
error = gif_encap_attach(sc);
if (error && osrc != NULL && odst != NULL) {
/*
* Thirdly, when error occured, rollback to old configurations,
* if last setting is valid.
*/
sc->gif_psrc = osrc;
sc->gif_pdst = odst;
osrc = nsrc; /* to free */
odst = ndst; /* to free */
error = gif_encap_attach(sc);
}
if (error) {
/*
* Fourthly, even rollback failed or last setting is not valid,
* clear configurations.
*/
osrc = sc->gif_psrc; /* to free */
odst = sc->gif_pdst; /* to free */
sc->gif_psrc = NULL;
sc->gif_pdst = NULL;
sockaddr_free(nsrc);
sockaddr_free(ndst);
}
(void)gif_encap_detach(ovar);
encap_lock_exit();
if (osrc)
sockaddr_free(osrc);
if (odst)
sockaddr_free(odst);
kmem_free(ovar, sizeof(*ovar));
if (sc->gif_psrc && sc->gif_pdst)
ifp->if_flags |= IFF_RUNNING;
else
ifp->if_flags &= ~IFF_RUNNING;
#ifndef GIF_MPSAFE
splx(s);
#endif
return 0;
out:
sockaddr_free(nsrc);
sockaddr_free(ndst);
kmem_free(nvar, sizeof(*nvar));
mutex_exit(&sc->gif_lock);
encap_lock_exit();
#ifndef GIF_MPSAFE
splx(s);
@ -1097,6 +1128,8 @@ static void
gif_delete_tunnel(struct ifnet *ifp)
{
struct gif_softc *sc = ifp->if_softc;
struct gif_variant *ovar, *nvar;
struct sockaddr *osrc, *odst;
int error;
#ifndef GIF_MPSAFE
int s;
@ -1111,34 +1144,71 @@ gif_delete_tunnel(struct ifnet *ifp)
return;
}
gif_encap_pause(sc);
if (sc->gif_psrc) {
sockaddr_free(sc->gif_psrc);
sc->gif_psrc = NULL;
}
if (sc->gif_pdst) {
sockaddr_free(sc->gif_pdst);
sc->gif_pdst = NULL;
}
/* it is safe to detach from both */
#ifdef INET
(void)in_gif_detach(sc);
#endif
#ifdef INET6
(void)in6_gif_detach(sc);
#endif
nvar = kmem_alloc(sizeof(*nvar), KM_SLEEP);
if (sc->gif_psrc && sc->gif_pdst)
ifp->if_flags |= IFF_RUNNING;
else
ifp->if_flags &= ~IFF_RUNNING;
mutex_enter(&sc->gif_lock);
ovar = sc->gif_var;
osrc = ovar->gv_psrc;
odst = ovar->gv_pdst;
if (osrc == NULL || odst == NULL) {
/* address pair not changed. */
mutex_exit(&sc->gif_lock);
encap_lock_exit();
kmem_free(nvar, sizeof(*nvar));
return;
}
*nvar = *ovar;
nvar->gv_psrc = NULL;
nvar->gv_pdst = NULL;
nvar->gv_encap_cookie4 = NULL;
nvar->gv_encap_cookie6 = NULL;
nvar->gv_output = NULL;
psref_target_init(&nvar->gv_psref, gv_psref_class);
membar_producer();
gif_update_variant(sc, nvar);
mutex_exit(&sc->gif_lock);
gif_encap_detach(ovar);
encap_lock_exit();
sockaddr_free(osrc);
sockaddr_free(odst);
kmem_free(ovar, sizeof(*ovar));
#ifndef GIF_MPSAFE
splx(s);
#endif
}
/*
* gif_variant update API.
*
* Assumption:
* reader side dereferences sc->gif_var in reader critical section only,
* that is, all of reader sides do not reader the sc->gif_var after
* pserialize_perform().
*/
static void
gif_update_variant(struct gif_softc *sc, struct gif_variant *nvar)
{
struct ifnet *ifp = &sc->gif_if;
struct gif_variant *ovar = sc->gif_var;
KASSERT(mutex_owned(&sc->gif_lock));
sc->gif_var = nvar;
pserialize_perform(gif_psz);
psref_target_destroy(&ovar->gv_psref, gv_psref_class);
if (nvar->gv_psrc != NULL && nvar->gv_pdst != NULL)
ifp->if_flags |= IFF_RUNNING;
else
ifp->if_flags &= ~IFF_RUNNING;
}
/*
* Module infrastructure
*/

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_gif.h,v 1.27 2017/09/21 09:48:15 knakahara Exp $ */
/* $NetBSD: if_gif.h,v 1.28 2017/11/27 05:02:22 knakahara Exp $ */
/* $KAME: if_gif.h,v 1.23 2001/07/27 09:21:42 itojun Exp $ */
/*
@ -39,6 +39,9 @@
#include <sys/queue.h>
#include <sys/percpu.h>
#ifdef _KERNEL
#include <sys/psref.h>
#endif
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@ -47,6 +50,8 @@
#include <netinet/in.h>
/* xxx sigh, why route have struct route instead of pointer? */
extern struct psref_class *gv_psref_class;
struct encaptab;
struct gif_ro {
@ -54,14 +59,26 @@ struct gif_ro {
kmutex_t gr_lock;
};
struct gif_variant {
struct gif_softc *gv_softc;
struct sockaddr *gv_psrc; /* Physical src addr */
struct sockaddr *gv_pdst; /* Physical dst addr */
const struct encaptab *gv_encap_cookie4;
const struct encaptab *gv_encap_cookie6;
int (*gv_output)(struct gif_variant *, int, struct mbuf *);
struct psref_target gv_psref;
};
struct gif_softc {
struct ifnet gif_if; /* common area - must be at the top */
struct sockaddr *gif_psrc; /* Physical src addr */
struct sockaddr *gif_pdst; /* Physical dst addr */
percpu_t *gif_ro_percpu; /* struct gif_ro */
int gif_flags;
const struct encaptab *encap_cookie4;
const struct encaptab *encap_cookie6;
struct ifnet gif_if; /* common area - must be at the top */
percpu_t *gif_ro_percpu; /* struct gif_ro */
struct gif_variant *gif_var; /*
* reader must use gif_getref_variant()
* instead of direct dereference.
*/
kmutex_t gif_lock; /* writer lock for gif_var */
LIST_ENTRY(gif_softc) gif_list; /* list of all gifs */
};
#define GIF_ROUTE_TTL 10
@ -70,6 +87,45 @@ struct gif_softc {
#define GIF_MTU_MIN (1280) /* Minimum MTU */
#define GIF_MTU_MAX (8192) /* Maximum MTU */
/*
* Get gif_variant from gif_softc.
*
* Never return NULL by contract.
* gif_variant itself is protected not to be freed by gv_psref.
* Once a reader dereference sc->sc_var by this API, the reader must not
* re-dereference form sc->sc_var.
*/
static inline struct gif_variant *
gif_getref_variant(struct gif_softc *sc, struct psref *psref)
{
struct gif_variant *var;
int s;
s = pserialize_read_enter();
var = sc->gif_var;
KASSERT(var != NULL);
membar_datadep_consumer();
psref_acquire(psref, &var->gv_psref, gv_psref_class);
pserialize_read_exit(s);
return var;
}
static inline void
gif_putref_variant(struct gif_variant *var, struct psref *psref)
{
KASSERT(var != NULL);
psref_release(psref, &var->gv_psref, gv_psref_class);
}
static inline bool
gif_heldref_variant(struct gif_variant *var)
{
return psref_held(&var->gv_psref, gv_psref_class);
}
/* Prototypes */
void gif_input(struct mbuf *, int, struct ifnet *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: in_gif.c,v 1.89 2017/11/15 10:42:41 knakahara Exp $ */
/* $NetBSD: in_gif.c,v 1.90 2017/11/27 05:02:22 knakahara Exp $ */
/* $KAME: in_gif.c,v 1.66 2001/07/29 04:46:09 itojun Exp $ */
/*
@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: in_gif.c,v 1.89 2017/11/15 10:42:41 knakahara Exp $");
__KERNEL_RCSID(0, "$NetBSD: in_gif.c,v 1.90 2017/11/27 05:02:22 knakahara Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@ -67,7 +67,7 @@ __KERNEL_RCSID(0, "$NetBSD: in_gif.c,v 1.89 2017/11/15 10:42:41 knakahara Exp $"
#include <net/net_osdep.h>
static int gif_validate4(const struct ip *, struct gif_softc *,
static int gif_validate4(const struct ip *, struct gif_variant *,
struct ifnet *);
int ip_gif_ttl = GIF_TTL;
@ -79,19 +79,26 @@ static const struct encapsw in_gif_encapsw = {
}
};
int
in_gif_output(struct ifnet *ifp, int family, struct mbuf *m)
static int
in_gif_output(struct gif_variant *var, int family, struct mbuf *m)
{
struct rtentry *rt;
struct route *ro;
struct gif_ro *gro;
struct gif_softc *sc = ifp->if_softc;
struct sockaddr_in *sin_src = satosin(sc->gif_psrc);
struct sockaddr_in *sin_dst = satosin(sc->gif_pdst);
struct gif_softc *sc;
struct sockaddr_in *sin_src;
struct sockaddr_in *sin_dst;
struct ifnet *ifp;
struct ip iphdr; /* capsule IP header, host byte ordered */
int proto, error;
u_int8_t tos;
KASSERT(gif_heldref_variant(var));
sin_src = satosin(var->gv_psrc);
sin_dst = satosin(var->gv_pdst);
ifp = &var->gv_softc->gif_if;
if (sin_src == NULL || sin_dst == NULL ||
sin_src->sin_family != AF_INET ||
sin_dst->sin_family != AF_INET) {
@ -167,10 +174,11 @@ in_gif_output(struct ifnet *ifp, int family, struct mbuf *m)
return ENOBUFS;
bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip));
sc = var->gv_softc;
gro = percpu_getref(sc->gif_ro_percpu);
mutex_enter(&gro->gr_lock);
ro = &gro->gr_ro;
if ((rt = rtcache_lookup(ro, sc->gif_pdst)) == NULL) {
if ((rt = rtcache_lookup(ro, var->gv_pdst)) == NULL) {
mutex_exit(&gro->gr_lock);
percpu_putref(sc->gif_ro_percpu);
m_freem(m);
@ -197,39 +205,45 @@ in_gif_output(struct ifnet *ifp, int family, struct mbuf *m)
void
in_gif_input(struct mbuf *m, int off, int proto, void *eparg)
{
struct ifnet *gifp = eparg;
struct gif_softc *sc = eparg;
struct ifnet *gifp = &sc->gif_if;
const struct ip *ip;
int af;
u_int8_t otos;
KASSERT(gifp != NULL);
KASSERT(sc != NULL);
ip = mtod(m, const struct ip *);
gifp = &sc->gif_if;
if ((gifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
m_freem(m);
ip_statinc(IP_STAT_NOGIF);
return;
}
#ifndef GIF_ENCAPCHECK
struct gif_softc *sc = (struct gif_softc *)gifp->if_softc;
struct psref psref_var;
struct gif_variant *var = gif_getref_variant(sc, &psref_var);
/* other CPU do delete_tunnel */
if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
if (var->gv_psrc == NULL || var->gv_pdst == NULL) {
gif_putref_variant(var, &psref_var);
m_freem(m);
ip_statinc(IP_STAT_NOGIF);
return;
}
struct ifnet *rcvif;
struct psref psref;
rcvif = m_get_rcvif_psref(m, &psref);
if (!gif_validate4(ip, sc, rcvif)) {
m_put_rcvif_psref(rcvif, &psref);
struct psref psref_rcvif;
rcvif = m_get_rcvif_psref(m, &psref_rcvif);
if (!gif_validate4(ip, var, rcvif)) {
m_put_rcvif_psref(rcvif, &psref_rcvif);
gif_putref_variant(var, &psref_var);
m_freem(m);
ip_statinc(IP_STAT_NOGIF);
return;
}
m_put_rcvif_psref(rcvif, &psref);
m_put_rcvif_psref(rcvif, &psref_rcvif);
gif_putref_variant(var, &psref_var);
#endif
otos = ip->ip_tos;
m_adj(m, off);
@ -286,14 +300,14 @@ in_gif_input(struct mbuf *m, int off, int proto, void *eparg)
* validate outer address.
*/
static int
gif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp)
gif_validate4(const struct ip *ip, struct gif_variant *var, struct ifnet *ifp)
{
struct sockaddr_in *src, *dst;
struct in_ifaddr *ia4;
int s;
src = satosin(sc->gif_psrc);
dst = satosin(sc->gif_pdst);
src = satosin(var->gv_psrc);
dst = satosin(var->gv_pdst);
/* check for address match */
if (src->sin_addr.s_addr != ip->ip_dst.s_addr ||
@ -320,7 +334,7 @@ gif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp)
pserialize_read_exit(s);
/* ingress filters on outer source */
if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
if ((var->gv_softc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
union {
struct sockaddr sa;
struct sockaddr_in sin;
@ -332,7 +346,8 @@ gif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp)
if (rt == NULL || rt->rt_ifp != ifp) {
#if 0
log(LOG_WARNING, "%s: packet from 0x%x dropped "
"due to ingress filter\n", if_name(&sc->gif_if),
"due to ingress filter\n",
if_name(&var->gv_softc->gif_if),
(u_int32_t)ntohl(u.sin.sin_addr.s_addr));
#endif
if (rt != NULL)
@ -351,22 +366,19 @@ gif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp)
* matched the physical addr family. see gif_encapcheck().
*/
int
gif_encapcheck4(struct mbuf *m, int off, int proto, void *arg)
gif_encapcheck4(struct mbuf *m, int off, int proto, struct gif_variant *var)
{
struct ip ip;
struct gif_softc *sc;
struct ifnet *ifp = NULL;
int r;
struct psref psref;
/* sanity check done in caller */
sc = arg;
m_copydata(m, 0, sizeof(ip), &ip);
if ((m->m_flags & M_PKTHDR) != 0)
ifp = m_get_rcvif_psref(m, &psref);
r = gif_validate4(&ip, sc, ifp);
r = gif_validate4(&ip, var, ifp);
m_put_rcvif_psref(ifp, &psref);
return r;
@ -374,7 +386,7 @@ gif_encapcheck4(struct mbuf *m, int off, int proto, void *arg)
#endif
int
in_gif_attach(struct gif_softc *sc)
in_gif_attach(struct gif_variant *var)
{
#ifndef GIF_ENCAPCHECK
struct sockaddr_in mask4;
@ -383,40 +395,33 @@ in_gif_attach(struct gif_softc *sc)
mask4.sin_len = sizeof(struct sockaddr_in);
mask4.sin_addr.s_addr = ~0;
if (!sc->gif_psrc || !sc->gif_pdst)
if (!var->gv_psrc || !var->gv_pdst)
return EINVAL;
sc->encap_cookie4 = encap_attach(AF_INET, -1, sc->gif_psrc,
(struct sockaddr *)&mask4, sc->gif_pdst, (struct sockaddr *)&mask4,
&in_gif_encapsw, sc);
var->gv_encap_cookie4 = encap_attach(AF_INET, -1, var->gv_psrc,
(struct sockaddr *)&mask4, var->gv_pdst, (struct sockaddr *)&mask4,
&in_gif_encapsw, var->gv_softc);
#else
sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck,
&in_gif_encapsw, sc);
var->gv_encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck,
&in_gif_encapsw, var->gv_softc);
#endif
if (sc->encap_cookie4 == NULL)
if (var->gv_encap_cookie4 == NULL)
return EEXIST;
var->gv_output = in_gif_output;
return 0;
}
int
in_gif_detach(struct gif_softc *sc)
in_gif_detach(struct gif_variant *var)
{
int error;
struct gif_softc *sc = var->gv_softc;
error = in_gif_pause(sc);
error = encap_detach(var->gv_encap_cookie4);
if (error == 0)
var->gv_encap_cookie4 = NULL;
percpu_foreach(sc->gif_ro_percpu, gif_rtcache_free_pc, NULL);
return error;
}
int
in_gif_pause(struct gif_softc *sc)
{
int error;
error = encap_detach(sc->encap_cookie4);
if (error == 0)
sc->encap_cookie4 = NULL;
return error;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: in_gif.h,v 1.17 2017/11/15 10:42:41 knakahara Exp $ */
/* $NetBSD: in_gif.h,v 1.18 2017/11/27 05:02:22 knakahara Exp $ */
/* $KAME: in_gif.h,v 1.6 2001/07/25 00:55:48 itojun Exp $ */
/*
@ -37,14 +37,12 @@
extern int ip_gif_ttl;
struct gif_softc;
struct gif_variant;
void in_gif_input(struct mbuf *, int, int, void *);
int in_gif_output(struct ifnet *, int, struct mbuf *);
#ifdef GIF_ENCAPCHECK
int gif_encapcheck4(struct mbuf *, int, int, void *);
int gif_encapcheck4(struct mbuf *, int, int, struct gif_variant *);
#endif
int in_gif_attach(struct gif_softc *);
int in_gif_detach(struct gif_softc *);
int in_gif_pause(struct gif_softc *);
int in_gif_attach(struct gif_variant *);
int in_gif_detach(struct gif_variant *);
#endif /* !_NETINET_IN_GIF_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: in6_gif.c,v 1.87 2017/11/15 10:42:41 knakahara Exp $ */
/* $NetBSD: in6_gif.c,v 1.88 2017/11/27 05:02:22 knakahara Exp $ */
/* $KAME: in6_gif.c,v 1.62 2001/07/29 04:27:25 itojun Exp $ */
/*
@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: in6_gif.c,v 1.87 2017/11/15 10:42:41 knakahara Exp $");
__KERNEL_RCSID(0, "$NetBSD: in6_gif.c,v 1.88 2017/11/27 05:02:22 knakahara Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@ -71,7 +71,7 @@ __KERNEL_RCSID(0, "$NetBSD: in6_gif.c,v 1.87 2017/11/15 10:42:41 knakahara Exp $
#include <net/net_osdep.h>
static int gif_validate6(const struct ip6_hdr *, struct gif_softc *,
static int gif_validate6(const struct ip6_hdr *, struct gif_variant *,
struct ifnet *);
int ip6_gif_hlim = GIF_HLIM;
@ -82,19 +82,26 @@ static const struct encapsw in6_gif_encapsw;
* family - family of the packet to be encapsulate.
*/
int
in6_gif_output(struct ifnet *ifp, int family, struct mbuf *m)
static int
in6_gif_output(struct gif_variant *var, int family, struct mbuf *m)
{
struct rtentry *rt;
struct route *ro;
struct gif_ro *gro;
struct gif_softc *sc = ifp->if_softc;
struct sockaddr_in6 *sin6_src = satosin6(sc->gif_psrc);
struct sockaddr_in6 *sin6_dst = satosin6(sc->gif_pdst);
struct gif_softc *sc;
struct sockaddr_in6 *sin6_src;
struct sockaddr_in6 *sin6_dst;
struct ifnet *ifp;
struct ip6_hdr *ip6;
int proto, error;
u_int8_t itos, otos;
KASSERT(gif_heldref_variant(var));
sin6_src = satosin6(var->gv_psrc);
sin6_dst = satosin6(var->gv_pdst);
ifp = &var->gv_softc->gif_if;
if (sin6_src == NULL || sin6_dst == NULL ||
sin6_src->sin6_family != AF_INET6 ||
sin6_dst->sin6_family != AF_INET6) {
@ -173,10 +180,11 @@ in6_gif_output(struct ifnet *ifp, int family, struct mbuf *m)
ip6->ip6_flow &= ~ntohl(0xff00000);
ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
sc = ifp->if_softc;
gro = percpu_getref(sc->gif_ro_percpu);
mutex_enter(&gro->gr_lock);
ro = &gro->gr_ro;
rt = rtcache_lookup(ro, sc->gif_pdst);
rt = rtcache_lookup(ro, var->gv_pdst);
if (rt == NULL) {
mutex_exit(&gro->gr_lock);
percpu_putref(sc->gif_ro_percpu);
@ -214,24 +222,28 @@ int
in6_gif_input(struct mbuf **mp, int *offp, int proto, void *eparg)
{
struct mbuf *m = *mp;
struct ifnet *gifp = eparg;
struct gif_softc *sc = eparg;
struct ifnet *gifp;
struct ip6_hdr *ip6;
int af = 0;
u_int32_t otos;
KASSERT(gifp != NULL);
KASSERT(sc != NULL);
ip6 = mtod(m, struct ip6_hdr *);
gifp = &sc->gif_if;
if ((gifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
m_freem(m);
IP6_STATINC(IP6_STAT_NOGIF);
return IPPROTO_DONE;
}
#ifndef GIF_ENCAPCHECK
struct gif_softc *sc = (struct gif_softc *)gifp->if_softc;
struct psref psref_var;
struct gif_variant *var = gif_getref_variant(sc, &psref_var);
/* other CPU do delete_tunnel */
if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
if (var->gv_psrc == NULL || var->gv_pdst == NULL) {
gif_putref_variant(var, &psref_var);
m_freem(m);
IP6_STATINC(IP6_STAT_NOGIF);
return IPPROTO_DONE;
@ -239,13 +251,15 @@ in6_gif_input(struct mbuf **mp, int *offp, int proto, void *eparg)
struct psref psref;
struct ifnet *rcvif = m_get_rcvif_psref(m, &psref);
if (rcvif == NULL || !gif_validate6(ip6, sc, rcvif)) {
if (rcvif == NULL || !gif_validate6(ip6, var, rcvif)) {
m_put_rcvif_psref(rcvif, &psref);
gif_putref_variant(var, &psref_var);
m_freem(m);
IP6_STATINC(IP6_STAT_NOGIF);
return IPPROTO_DONE;
}
m_put_rcvif_psref(rcvif, &psref);
gif_putref_variant(var, &psref_var);
#endif
otos = ip6->ip6_flow;
@ -304,13 +318,13 @@ in6_gif_input(struct mbuf **mp, int *offp, int proto, void *eparg)
* validate outer address.
*/
static int
gif_validate6(const struct ip6_hdr *ip6, struct gif_softc *sc,
gif_validate6(const struct ip6_hdr *ip6, struct gif_variant *var,
struct ifnet *ifp)
{
const struct sockaddr_in6 *src, *dst;
src = satosin6(sc->gif_psrc);
dst = satosin6(sc->gif_pdst);
src = satosin6(var->gv_psrc);
dst = satosin6(var->gv_pdst);
/* check for address match */
if (!IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6->ip6_dst) ||
@ -320,7 +334,7 @@ gif_validate6(const struct ip6_hdr *ip6, struct gif_softc *sc,
/* martian filters on outer source - done in ip6_input */
/* ingress filters on outer source */
if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
if ((var->gv_softc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
union {
struct sockaddr sa;
struct sockaddr_in6 sin6;
@ -334,7 +348,8 @@ gif_validate6(const struct ip6_hdr *ip6, struct gif_softc *sc,
#if 0
char ip6buf[INET6_ADDRSTRLEN];
log(LOG_WARNING, "%s: packet from %s dropped "
"due to ingress filter\n", if_name(&sc->gif_if),
"due to ingress filter\n",
if_name(&var->gv_softc->gif_if),
IN6_PRINT(ip6buf, &u.sin6.sin6_addr));
#endif
if (rt != NULL)
@ -353,22 +368,18 @@ gif_validate6(const struct ip6_hdr *ip6, struct gif_softc *sc,
* matched the physical addr family. see gif_encapcheck().
*/
int
gif_encapcheck6(struct mbuf *m, int off, int proto, void *arg)
gif_encapcheck6(struct mbuf *m, int off, int proto, struct gif_variant *var)
{
struct ip6_hdr ip6;
struct gif_softc *sc;
struct ifnet *ifp = NULL;
int r;
struct psref psref;
/* sanity check done in caller */
sc = arg;
m_copydata(m, 0, sizeof(ip6), (void *)&ip6);
if ((m->m_flags & M_PKTHDR) != 0)
ifp = m_get_rcvif_psref(m, &psref);
r = gif_validate6(&ip6, sc, ifp);
r = gif_validate6(&ip6, var, ifp);
m_put_rcvif_psref(ifp, &psref);
return r;
@ -376,7 +387,7 @@ gif_encapcheck6(struct mbuf *m, int off, int proto, void *arg)
#endif
int
in6_gif_attach(struct gif_softc *sc)
in6_gif_attach(struct gif_variant *var)
{
#ifndef GIF_ENCAPCHECK
struct sockaddr_in6 mask6;
@ -386,52 +397,47 @@ in6_gif_attach(struct gif_softc *sc)
mask6.sin6_addr.s6_addr32[0] = mask6.sin6_addr.s6_addr32[1] =
mask6.sin6_addr.s6_addr32[2] = mask6.sin6_addr.s6_addr32[3] = ~0;
if (!sc->gif_psrc || !sc->gif_pdst)
if (!var->gv_psrc || !var->gv_pdst)
return EINVAL;
sc->encap_cookie6 = encap_attach(AF_INET6, -1, sc->gif_psrc,
sin6tosa(&mask6), sc->gif_pdst, sin6tosa(&mask6),
(const void *)&in6_gif_encapsw, sc);
var->gv_encap_cookie6 = encap_attach(AF_INET6, -1, var->gv_psrc,
sin6tosa(&mask6), var->gv_pdst, sin6tosa(&mask6),
(const void *)&in6_gif_encapsw, var->gv_softc);
#else
sc->encap_cookie6 = encap_attach_func(AF_INET6, -1, gif_encapcheck,
&in6_gif_encapsw, sc);
var->gv_encap_cookie6 = encap_attach_func(AF_INET6, -1, gif_encapcheck,
&in6_gif_encapsw, var->gv_softc);
#endif
if (sc->encap_cookie6 == NULL)
if (var->gv_encap_cookie6 == NULL)
return EEXIST;
var->gv_output = in6_gif_output;
return 0;
}
int
in6_gif_detach(struct gif_softc *sc)
in6_gif_detach(struct gif_variant *var)
{
int error;
struct gif_softc *sc = var->gv_softc;
error = in6_gif_pause(sc);
error = encap_detach(var->gv_encap_cookie6);
if (error == 0)
var->gv_encap_cookie6 = NULL;
percpu_foreach(sc->gif_ro_percpu, gif_rtcache_free_pc, NULL);
return error;
}
int
in6_gif_pause(struct gif_softc *sc)
{
int error;
error = encap_detach(sc->encap_cookie6);
if (error == 0)
sc->encap_cookie6 = NULL;
return error;
}
void *
in6_gif_ctlinput(int cmd, const struct sockaddr *sa, void *d, void *eparg)
{
struct gif_softc *sc = eparg;
struct gif_variant *var;
struct ip6ctlparam *ip6cp = NULL;
struct ip6_hdr *ip6;
const struct sockaddr_in6 *dst6;
struct route *ro;
struct psref psref;
if (sa->sa_family != AF_INET6 ||
sa->sa_len != sizeof(struct sockaddr_in6))
@ -457,8 +463,12 @@ in6_gif_ctlinput(int cmd, const struct sockaddr *sa, void *d, void *eparg)
if ((sc->gif_if.if_flags & IFF_RUNNING) == 0)
return NULL;
if (sc->gif_psrc->sa_family != AF_INET6)
var = gif_getref_variant(sc, &psref);
if (var->gv_psrc->sa_family != AF_INET6) {
gif_putref_variant(var, &psref);
return NULL;
}
gif_putref_variant(var, &psref);
ro = percpu_getref(sc->gif_ro_percpu);
dst6 = satocsin6(rtcache_getdst(ro));

View File

@ -1,4 +1,4 @@
/* $NetBSD: in6_gif.h,v 1.16 2017/11/15 10:42:41 knakahara Exp $ */
/* $NetBSD: in6_gif.h,v 1.17 2017/11/27 05:02:22 knakahara Exp $ */
/* $KAME: in6_gif.h,v 1.7 2001/07/26 06:53:16 jinmei Exp $ */
/*
@ -36,16 +36,14 @@
#define GIF_HLIM 30
extern int ip6_gif_hlim; /* Hop limit for gif encap packet */
struct gif_softc;
struct gif_variant;
struct sockaddr;
int in6_gif_input(struct mbuf **, int *, int, void *);
int in6_gif_output(struct ifnet *, int, struct mbuf *);
#ifdef GIF_ENCAPCHECK
int gif_encapcheck6(struct mbuf *, int, int, void *);
int gif_encapcheck6(struct mbuf *, int, int, struct gif_variant *);
#endif
int in6_gif_attach(struct gif_softc *);
int in6_gif_detach(struct gif_softc *);
int in6_gif_pause(struct gif_softc *);
int in6_gif_attach(struct gif_variant *);
int in6_gif_detach(struct gif_variant *);
void *in6_gif_ctlinput(int, const struct sockaddr *, void *, void *);
#endif /* !_NETINET6_IN6_GIF_H_ */