freebsd-skq/sys/net/if_gif.c
rwatson e13b2df854 Merge linux_ioctl.c:1.128 svr4_sockio.c:1.17 altq_cbq.c:1.3 if_oltr.c:1.38
if_pflog.c:1.14 if_pfsync.c:1.21 if_an.c:1.70 if_ar.c:1.72 if_arl.c:1.11
amrr.c:1.10 onoe.c:1.10 if_ath.c:1.101 awi.c:1.41 if_bfe.c:1.27
if_bge.c:1.93 if_cm_isa.c:1.7 smc90cx6.c:1.16 if_cnw.c:1.20 if_cp.c:1.25
if_cs.c:1.42 if_ct.c:1.26 if_cx.c:1.46 if_ed.c:1.256 if_em.c:1.68
if_en_pci.c:1.37 midway.c:1.66 if_ep.c:1.143 if_ex.c:1.58 if_fatm.c:1.20
if_fe.c:1.93 if_fwe.c:1.38 if_fwip.c:1.8 if_fxp.c:1.244 if_gem.c:1.33
if_hatm.c:1.25 if_hatm_intr.c:1.20 if_hatm_ioctl.c:1.13 if_hatm_rx.c:1.10
if_hatm_tx.c:1.14 if_hme.c:1.39 if_ie.c:1.104 if_ndis.c:1.101
if_ic.c:1.24 if_ipw.c:1.10 if_iwi.c:1.10 if_ixgb.c:1.13 if_lge.c:1.41
if_lnc.c:1.113 if_my.c:1.31 if_nge.c:1.77 if_nve.c:1.10 if_owi.c:1.12
if_patm.c:1.9 if_patm_intr.c:1.6 if_patm_ioctl.c:1.10 if_patm_tx.c:1.10
pdq_ifsubr.c:1.28 if_plip.c:1.38 if_ral.c:1.12 if_ral_pci.c:1.2
if_ray.c:1.81 if_rayvar.h:1.22 if_re.c:1.49 if_sbni.c:1.21 if_sbsh.c:1.14
if_sn.c:1.48 dp83932.c:1.21 if_snc_pccard.c:1.9 if_sr.c:1.70 if_tx.c:1.91
if_txp.c:1.33 if_aue.c:1.92 if_axe.c:1.32 if_cdce.c:1.8 if_cue.c:1.59
if_kue.c:1.66 if_rue.c:1.23 if_udav.c:1.16 if_ural.c:1.12 if_vge.c:1.16
if_vx.c:1.58 if_wi.c:1.185 if_wi_pci.c:1.26 if_wl.c:1.68 if_xe.c:1.60
if_xe_pccard.c:1.30 if_el.c:1.68 i4b_ipr.c:1.35 i4b_isppp.c:1.31
kern_poll.c:1.20 bridge.c:1.94 bridgestp.c:1.4 if_arcsubr.c:1.27
if_atm.h:1.24 if_atmsubr.c:1.40 if_bridge.c:1.16 if_ef.c:1.35
if_ethersubr.c:1.196 if_faith.c:1.37 if_fddisubr.c:1.100 if_fwsubr.c:1.14
if_gif.c:1.54 if_gre.c:1.34 if_iso88025subr.c:1.70 if_loop.c:1.107
if_ppp.c:1.106 if_spppsubr.c:1.121 if_tap.c:1.57 if_tun.c:1.154
if_vlan.c:1.80 ppp_tty.c:1.67 ieee80211_ioctl.c:1.32 atm_if.c:1.31
ng_eiface.c:1.33 ng_ether.c:1.50 ng_fec.c:1.19 ng_iface.c:1.44
ng_sppp.c:1.9 ip_carp.c:1.30 ip_fastfwd.c:1.30 in6.c:1.53 nd6_nbr.c:1.31
natm.c:1.40 if_dc.c:1.162 if_de.c:1.168 if_pcn.c:1.72 if_rl.c:1.154
if_sf.c:1.84 if_sis.c:1.135 if_sk.c:1.108 if_ste.c:1.86 if_ti.c:1.109
if_tl.c:1.101 if_vr.c:1.106 if_wb.c:1.81 if_xl.c:1.194 from HEAD to
RELENG_6:

  Propagate rename of IFF_OACTIVE and IFF_RUNNING to IFF_DRV_OACTIVE and
  IFF_DRV_RUNNING, as well as the move from ifnet.if_flags to
  ifnet.if_drv_flags.  Device drivers are now responsible for
  synchronizing access to these flags, as they are in if_drv_flags.  This
  helps prevent races between the network stack and device driver in
  maintaining the interface flags field.

  Many __FreeBSD__ and __FreeBSD_version checks maintained and continued;
  some less so.

  Reviewed by:    pjd, bz

Approved by:	re (scottl)
2005-08-25 05:01:24 +00:00

902 lines
20 KiB
C

/* $FreeBSD$ */
/* $KAME: if_gif.c,v 1.87 2001/10/19 08:50:27 itojun Exp $ */
/*-
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_mac.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mac.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/protosw.h>
#include <sys/conf.h>
#include <machine/cpu.h>
#include <net/if.h>
#include <net/if_clone.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <net/route.h>
#include <net/bpf.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#ifdef INET
#include <netinet/in_var.h>
#include <netinet/in_gif.h>
#include <netinet/ip_var.h>
#endif /* INET */
#ifdef INET6
#ifndef INET
#include <netinet/in.h>
#endif
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/in6_gif.h>
#include <netinet6/ip6protosw.h>
#endif /* INET6 */
#include <netinet/ip_encap.h>
#include <net/if_gif.h>
#include <net/net_osdep.h>
#define GIFNAME "gif"
/*
* gif_mtx protects the global gif_softc_list.
* XXX: Per-softc locking is still required.
*/
static struct mtx gif_mtx;
static MALLOC_DEFINE(M_GIF, "gif", "Generic Tunnel Interface");
static LIST_HEAD(, gif_softc) gif_softc_list;
void (*ng_gif_input_p)(struct ifnet *ifp, struct mbuf **mp, int af);
void (*ng_gif_input_orphan_p)(struct ifnet *ifp, struct mbuf *m, int af);
void (*ng_gif_attach_p)(struct ifnet *ifp);
void (*ng_gif_detach_p)(struct ifnet *ifp);
static int gif_clone_create(struct if_clone *, int);
static void gif_clone_destroy(struct ifnet *);
IFC_SIMPLE_DECLARE(gif, 0);
static int gifmodevent(module_t, int, void *);
SYSCTL_DECL(_net_link);
SYSCTL_NODE(_net_link, IFT_GIF, gif, CTLFLAG_RW, 0,
"Generic Tunnel Interface");
#ifndef MAX_GIF_NEST
/*
* This macro controls the default upper limitation on nesting of gif tunnels.
* Since, setting a large value to this macro with a careless configuration
* may introduce system crash, we don't allow any nestings by default.
* If you need to configure nested gif tunnels, you can define this macro
* in your kernel configuration file. However, if you do so, please be
* careful to configure the tunnels so that it won't make a loop.
*/
#define MAX_GIF_NEST 1
#endif
static int max_gif_nesting = MAX_GIF_NEST;
SYSCTL_INT(_net_link_gif, OID_AUTO, max_nesting, CTLFLAG_RW,
&max_gif_nesting, 0, "Max nested tunnels");
/*
* By default, we disallow creation of multiple tunnels between the same
* pair of addresses. Some applications require this functionality so
* we allow control over this check here.
*/
#ifdef XBONEHACK
static int parallel_tunnels = 1;
#else
static int parallel_tunnels = 0;
#endif
SYSCTL_INT(_net_link_gif, OID_AUTO, parallel_tunnels, CTLFLAG_RW,
&parallel_tunnels, 0, "Allow parallel tunnels?");
static int
gif_clone_create(ifc, unit)
struct if_clone *ifc;
int unit;
{
struct gif_softc *sc;
sc = malloc(sizeof(struct gif_softc), M_GIF, M_WAITOK | M_ZERO);
GIF2IFP(sc) = if_alloc(IFT_GIF);
if (GIF2IFP(sc) == NULL) {
free(sc, M_GIF);
return (ENOSPC);
}
GIF2IFP(sc)->if_softc = sc;
if_initname(GIF2IFP(sc), ifc->ifc_name, unit);
gifattach0(sc);
mtx_lock(&gif_mtx);
LIST_INSERT_HEAD(&gif_softc_list, sc, gif_list);
mtx_unlock(&gif_mtx);
return (0);
}
void
gifattach0(sc)
struct gif_softc *sc;
{
sc->encap_cookie4 = sc->encap_cookie6 = NULL;
GIF2IFP(sc)->if_addrlen = 0;
GIF2IFP(sc)->if_mtu = GIF_MTU;
GIF2IFP(sc)->if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
#if 0
/* turn off ingress filter */
GIF2IFP(sc)->if_flags |= IFF_LINK2;
#endif
GIF2IFP(sc)->if_ioctl = gif_ioctl;
GIF2IFP(sc)->if_output = gif_output;
GIF2IFP(sc)->if_snd.ifq_maxlen = IFQ_MAXLEN;
if_attach(GIF2IFP(sc));
bpfattach(GIF2IFP(sc), DLT_NULL, sizeof(u_int32_t));
if (ng_gif_attach_p != NULL)
(*ng_gif_attach_p)(GIF2IFP(sc));
}
static void
gif_destroy(struct gif_softc *sc)
{
struct ifnet *ifp = GIF2IFP(sc);
int err;
gif_delete_tunnel(ifp);
#ifdef INET6
if (sc->encap_cookie6 != NULL) {
err = encap_detach(sc->encap_cookie6);
KASSERT(err == 0, ("Unexpected error detaching encap_cookie6"));
}
#endif
#ifdef INET
if (sc->encap_cookie4 != NULL) {
err = encap_detach(sc->encap_cookie4);
KASSERT(err == 0, ("Unexpected error detaching encap_cookie4"));
}
#endif
if (ng_gif_detach_p != NULL)
(*ng_gif_detach_p)(ifp);
bpfdetach(ifp);
if_detach(ifp);
if_free(ifp);
free(sc, M_GIF);
}
static void
gif_clone_destroy(ifp)
struct ifnet *ifp;
{
struct gif_softc *sc = ifp->if_softc;
mtx_lock(&gif_mtx);
LIST_REMOVE(sc, gif_list);
mtx_unlock(&gif_mtx);
gif_destroy(sc);
}
static int
gifmodevent(mod, type, data)
module_t mod;
int type;
void *data;
{
struct gif_softc *sc;
switch (type) {
case MOD_LOAD:
mtx_init(&gif_mtx, "gif_mtx", NULL, MTX_DEF);
LIST_INIT(&gif_softc_list);
if_clone_attach(&gif_cloner);
#ifdef INET6
ip6_gif_hlim = GIF_HLIM;
#endif
break;
case MOD_UNLOAD:
if_clone_detach(&gif_cloner);
mtx_lock(&gif_mtx);
while ((sc = LIST_FIRST(&gif_softc_list)) != NULL) {
LIST_REMOVE(sc, gif_list);
mtx_unlock(&gif_mtx);
gif_destroy(sc);
mtx_lock(&gif_mtx);
}
mtx_unlock(&gif_mtx);
mtx_destroy(&gif_mtx);
#ifdef INET6
ip6_gif_hlim = 0;
#endif
break;
default:
return EOPNOTSUPP;
}
return 0;
}
static moduledata_t gif_mod = {
"if_gif",
gifmodevent,
0
};
DECLARE_MODULE(if_gif, gif_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
MODULE_VERSION(if_gif, 1);
int
gif_encapcheck(m, off, proto, arg)
const struct mbuf *m;
int off;
int proto;
void *arg;
{
struct ip ip;
struct gif_softc *sc;
sc = (struct gif_softc *)arg;
if (sc == NULL)
return 0;
if ((GIF2IFP(sc)->if_flags & IFF_UP) == 0)
return 0;
/* no physical address */
if (!sc->gif_psrc || !sc->gif_pdst)
return 0;
switch (proto) {
#ifdef INET
case IPPROTO_IPV4:
break;
#endif
#ifdef INET6
case IPPROTO_IPV6:
break;
#endif
default:
return 0;
}
/* Bail on short packets */
if (m->m_pkthdr.len < sizeof(ip))
return 0;
m_copydata(m, 0, sizeof(ip), (caddr_t)&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);
#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);
#endif
default:
return 0;
}
}
int
gif_output(ifp, m, dst, rt)
struct ifnet *ifp;
struct mbuf *m;
struct sockaddr *dst;
struct rtentry *rt; /* added in net2 */
{
struct gif_softc *sc = ifp->if_softc;
struct m_tag *mtag;
int error = 0;
int gif_called;
u_int32_t af;
#ifdef MAC
error = mac_check_ifnet_transmit(ifp, m);
if (error) {
m_freem(m);
goto end;
}
#endif
/*
* gif may cause infinite recursion calls when misconfigured.
* We'll prevent this by detecting loops.
*
* High nesting level may cause stack exhaustion.
* We'll prevent this by introducing upper limit.
*/
gif_called = 1;
mtag = m_tag_locate(m, MTAG_GIF, MTAG_GIF_CALLED, NULL);
while (mtag != NULL) {
if (*(struct ifnet **)(mtag + 1) == ifp) {
log(LOG_NOTICE,
"gif_output: loop detected on %s\n",
(*(struct ifnet **)(mtag + 1))->if_xname);
m_freem(m);
error = EIO; /* is there better errno? */
goto end;
}
mtag = m_tag_locate(m, MTAG_GIF, MTAG_GIF_CALLED, mtag);
gif_called++;
}
if (gif_called > max_gif_nesting) {
log(LOG_NOTICE,
"gif_output: recursively called too many times(%d)\n",
gif_called);
m_freem(m);
error = EIO; /* is there better errno? */
goto end;
}
mtag = m_tag_alloc(MTAG_GIF, MTAG_GIF_CALLED, sizeof(struct ifnet *),
M_NOWAIT);
if (mtag == NULL) {
m_freem(m);
error = ENOMEM;
goto end;
}
*(struct ifnet **)(mtag + 1) = ifp;
m_tag_prepend(m, mtag);
m->m_flags &= ~(M_BCAST|M_MCAST);
if (!(ifp->if_flags & IFF_UP) ||
sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
m_freem(m);
error = ENETDOWN;
goto end;
}
/* BPF writes need to be handled specially. */
if (dst->sa_family == AF_UNSPEC) {
bcopy(dst->sa_data, &af, sizeof(af));
dst->sa_family = af;
}
if (ifp->if_bpf) {
af = dst->sa_family;
bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m);
}
ifp->if_opackets++;
ifp->if_obytes += m->m_pkthdr.len;
/* inner AF-specific encapsulation */
/* XXX should we check if our outer source is legal? */
/* dispatch to output logic based on outer AF */
switch (sc->gif_psrc->sa_family) {
#ifdef INET
case AF_INET:
error = in_gif_output(ifp, dst->sa_family, m);
break;
#endif
#ifdef INET6
case AF_INET6:
error = in6_gif_output(ifp, dst->sa_family, m);
break;
#endif
default:
m_freem(m);
error = ENETDOWN;
goto end;
}
end:
if (error)
ifp->if_oerrors++;
return error;
}
void
gif_input(m, af, ifp)
struct mbuf *m;
int af;
struct ifnet *ifp;
{
int isr;
if (ifp == NULL) {
/* just in case */
m_freem(m);
return;
}
m->m_pkthdr.rcvif = ifp;
#ifdef MAC
mac_create_mbuf_from_ifnet(ifp, m);
#endif
if (ifp->if_bpf) {
u_int32_t af1 = af;
bpf_mtap2(ifp->if_bpf, &af1, sizeof(af1), m);
}
if (ng_gif_input_p != NULL) {
(*ng_gif_input_p)(ifp, &m, af);
if (m == NULL)
return;
}
/*
* Put the packet to the network layer input queue according to the
* specified address family.
* Note: older versions of gif_input directly called network layer
* input functions, e.g. ip6_input, here. We changed the policy to
* prevent too many recursive calls of such input functions, which
* might cause kernel panic. But the change may introduce another
* problem; if the input queue is full, packets are discarded.
* The kernel stack overflow really happened, and we believed
* queue-full rarely occurs, so we changed the policy.
*/
switch (af) {
#ifdef INET
case AF_INET:
isr = NETISR_IP;
break;
#endif
#ifdef INET6
case AF_INET6:
isr = NETISR_IPV6;
break;
#endif
default:
if (ng_gif_input_orphan_p != NULL)
(*ng_gif_input_orphan_p)(ifp, m, af);
else
m_freem(m);
return;
}
ifp->if_ipackets++;
ifp->if_ibytes += m->m_pkthdr.len;
netisr_dispatch(isr, m);
}
/* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */
int
gif_ioctl(ifp, cmd, data)
struct ifnet *ifp;
u_long cmd;
caddr_t data;
{
struct gif_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq*)data;
int error = 0, size;
struct sockaddr *dst, *src;
#ifdef SIOCSIFMTU /* xxx */
u_long mtu;
#endif
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
break;
case SIOCSIFDSTADDR:
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
break;
#ifdef SIOCSIFMTU /* xxx */
case SIOCGIFMTU:
break;
case SIOCSIFMTU:
mtu = ifr->ifr_mtu;
if (mtu < GIF_MTU_MIN || mtu > GIF_MTU_MAX)
return (EINVAL);
ifp->if_mtu = mtu;
break;
#endif /* SIOCSIFMTU */
#ifdef INET
case SIOCSIFPHYADDR:
#endif
#ifdef INET6
case SIOCSIFPHYADDR_IN6:
#endif /* INET6 */
case SIOCSLIFPHYADDR:
switch (cmd) {
#ifdef INET
case SIOCSIFPHYADDR:
src = (struct sockaddr *)
&(((struct in_aliasreq *)data)->ifra_addr);
dst = (struct sockaddr *)
&(((struct in_aliasreq *)data)->ifra_dstaddr);
break;
#endif
#ifdef INET6
case SIOCSIFPHYADDR_IN6:
src = (struct sockaddr *)
&(((struct in6_aliasreq *)data)->ifra_addr);
dst = (struct sockaddr *)
&(((struct in6_aliasreq *)data)->ifra_dstaddr);
break;
#endif
case SIOCSLIFPHYADDR:
src = (struct sockaddr *)
&(((struct if_laddrreq *)data)->addr);
dst = (struct sockaddr *)
&(((struct if_laddrreq *)data)->dstaddr);
break;
default:
return EINVAL;
}
/* sa_family must be equal */
if (src->sa_family != dst->sa_family)
return EINVAL;
/* validate sa_len */
switch (src->sa_family) {
#ifdef INET
case AF_INET:
if (src->sa_len != sizeof(struct sockaddr_in))
return EINVAL;
break;
#endif
#ifdef INET6
case AF_INET6:
if (src->sa_len != sizeof(struct sockaddr_in6))
return EINVAL;
break;
#endif
default:
return EAFNOSUPPORT;
}
switch (dst->sa_family) {
#ifdef INET
case AF_INET:
if (dst->sa_len != sizeof(struct sockaddr_in))
return EINVAL;
break;
#endif
#ifdef INET6
case AF_INET6:
if (dst->sa_len != sizeof(struct sockaddr_in6))
return EINVAL;
break;
#endif
default:
return EAFNOSUPPORT;
}
/* check sa_family looks sane for the cmd */
switch (cmd) {
case SIOCSIFPHYADDR:
if (src->sa_family == AF_INET)
break;
return EAFNOSUPPORT;
#ifdef INET6
case SIOCSIFPHYADDR_IN6:
if (src->sa_family == AF_INET6)
break;
return EAFNOSUPPORT;
#endif /* INET6 */
case SIOCSLIFPHYADDR:
/* checks done in the above */
break;
}
error = gif_set_tunnel(GIF2IFP(sc), src, dst);
break;
#ifdef SIOCDIFPHYADDR
case SIOCDIFPHYADDR:
gif_delete_tunnel(GIF2IFP(sc));
break;
#endif
case SIOCGIFPSRCADDR:
#ifdef INET6
case SIOCGIFPSRCADDR_IN6:
#endif /* INET6 */
if (sc->gif_psrc == NULL) {
error = EADDRNOTAVAIL;
goto bad;
}
src = sc->gif_psrc;
switch (cmd) {
#ifdef INET
case SIOCGIFPSRCADDR:
dst = &ifr->ifr_addr;
size = sizeof(ifr->ifr_addr);
break;
#endif /* INET */
#ifdef INET6
case SIOCGIFPSRCADDR_IN6:
dst = (struct sockaddr *)
&(((struct in6_ifreq *)data)->ifr_addr);
size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
break;
#endif /* INET6 */
default:
error = EADDRNOTAVAIL;
goto bad;
}
if (src->sa_len > size)
return EINVAL;
bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
break;
case SIOCGIFPDSTADDR:
#ifdef INET6
case SIOCGIFPDSTADDR_IN6:
#endif /* INET6 */
if (sc->gif_pdst == NULL) {
error = EADDRNOTAVAIL;
goto bad;
}
src = sc->gif_pdst;
switch (cmd) {
#ifdef INET
case SIOCGIFPDSTADDR:
dst = &ifr->ifr_addr;
size = sizeof(ifr->ifr_addr);
break;
#endif /* INET */
#ifdef INET6
case SIOCGIFPDSTADDR_IN6:
dst = (struct sockaddr *)
&(((struct in6_ifreq *)data)->ifr_addr);
size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
break;
#endif /* INET6 */
default:
error = EADDRNOTAVAIL;
goto bad;
}
if (src->sa_len > size)
return EINVAL;
bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
break;
case SIOCGLIFPHYADDR:
if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
error = EADDRNOTAVAIL;
goto bad;
}
/* copy src */
src = sc->gif_psrc;
dst = (struct sockaddr *)
&(((struct if_laddrreq *)data)->addr);
size = sizeof(((struct if_laddrreq *)data)->addr);
if (src->sa_len > size)
return EINVAL;
bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
/* copy dst */
src = sc->gif_pdst;
dst = (struct sockaddr *)
&(((struct if_laddrreq *)data)->dstaddr);
size = sizeof(((struct if_laddrreq *)data)->dstaddr);
if (src->sa_len > size)
return EINVAL;
bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
break;
case SIOCSIFFLAGS:
/* if_ioctl() takes care of it */
break;
default:
error = EINVAL;
break;
}
bad:
return error;
}
/*
* XXXRW: There's a general event-ordering issue here: the code to check
* if a given tunnel is already present happens before we perform a
* potentially blocking setup of the tunnel. This code needs to be
* re-ordered so that the check and replacement can be atomic using
* a mutex.
*/
int
gif_set_tunnel(ifp, src, dst)
struct ifnet *ifp;
struct sockaddr *src;
struct sockaddr *dst;
{
struct gif_softc *sc = ifp->if_softc;
struct gif_softc *sc2;
struct sockaddr *osrc, *odst, *sa;
int s;
int error = 0;
s = splnet();
mtx_lock(&gif_mtx);
LIST_FOREACH(sc2, &gif_softc_list, gif_list) {
if (sc2 == sc)
continue;
if (!sc2->gif_pdst || !sc2->gif_psrc)
continue;
if (sc2->gif_pdst->sa_family != dst->sa_family ||
sc2->gif_pdst->sa_len != dst->sa_len ||
sc2->gif_psrc->sa_family != src->sa_family ||
sc2->gif_psrc->sa_len != src->sa_len)
continue;
/*
* Disallow parallel tunnels unless instructed
* otherwise.
*/
if (!parallel_tunnels &&
bcmp(sc2->gif_pdst, dst, dst->sa_len) == 0 &&
bcmp(sc2->gif_psrc, src, src->sa_len) == 0) {
error = EADDRNOTAVAIL;
mtx_unlock(&gif_mtx);
goto bad;
}
/* XXX both end must be valid? (I mean, not 0.0.0.0) */
}
mtx_unlock(&gif_mtx);
/* XXX we can detach from both, but be polite just in case */
if (sc->gif_psrc)
switch (sc->gif_psrc->sa_family) {
#ifdef INET
case AF_INET:
(void)in_gif_detach(sc);
break;
#endif
#ifdef INET6
case AF_INET6:
(void)in6_gif_detach(sc);
break;
#endif
}
osrc = sc->gif_psrc;
sa = (struct sockaddr *)malloc(src->sa_len, M_IFADDR, M_WAITOK);
bcopy((caddr_t)src, (caddr_t)sa, src->sa_len);
sc->gif_psrc = sa;
odst = sc->gif_pdst;
sa = (struct sockaddr *)malloc(dst->sa_len, M_IFADDR, M_WAITOK);
bcopy((caddr_t)dst, (caddr_t)sa, dst->sa_len);
sc->gif_pdst = sa;
switch (sc->gif_psrc->sa_family) {
#ifdef INET
case AF_INET:
error = in_gif_attach(sc);
break;
#endif
#ifdef INET6
case AF_INET6:
error = in6_gif_attach(sc);
break;
#endif
}
if (error) {
/* rollback */
free((caddr_t)sc->gif_psrc, M_IFADDR);
free((caddr_t)sc->gif_pdst, M_IFADDR);
sc->gif_psrc = osrc;
sc->gif_pdst = odst;
goto bad;
}
if (osrc)
free((caddr_t)osrc, M_IFADDR);
if (odst)
free((caddr_t)odst, M_IFADDR);
if (sc->gif_psrc && sc->gif_pdst)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
else
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
splx(s);
return 0;
bad:
if (sc->gif_psrc && sc->gif_pdst)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
else
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
splx(s);
return error;
}
void
gif_delete_tunnel(ifp)
struct ifnet *ifp;
{
struct gif_softc *sc = ifp->if_softc;
int s;
s = splnet();
if (sc->gif_psrc) {
free((caddr_t)sc->gif_psrc, M_IFADDR);
sc->gif_psrc = NULL;
}
if (sc->gif_pdst) {
free((caddr_t)sc->gif_pdst, M_IFADDR);
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
if (sc->gif_psrc && sc->gif_pdst)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
else
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
splx(s);
}