if_faith.c revision 91317
178064Sume/*	$KAME: if_faith.c,v 1.21 2001/02/20 07:59:26 itojun Exp $	*/
278064Sume
354263Sshin/*
454263Sshin * Copyright (c) 1982, 1986, 1993
554263Sshin *	The Regents of the University of California.  All rights reserved.
654263Sshin *
754263Sshin * Redistribution and use in source and binary forms, with or without
854263Sshin * modification, are permitted provided that the following conditions
954263Sshin * are met:
1054263Sshin * 1. Redistributions of source code must retain the above copyright
1154263Sshin *    notice, this list of conditions and the following disclaimer.
1254263Sshin * 2. Redistributions in binary form must reproduce the above copyright
1354263Sshin *    notice, this list of conditions and the following disclaimer in the
1454263Sshin *    documentation and/or other materials provided with the distribution.
1554263Sshin * 3. All advertising materials mentioning features or use of this software
1654263Sshin *    must display the following acknowledgement:
1754263Sshin *	This product includes software developed by the University of
1854263Sshin *	California, Berkeley and its contributors.
1954263Sshin * 4. Neither the name of the University nor the names of its contributors
2054263Sshin *    may be used to endorse or promote products derived from this software
2154263Sshin *    without specific prior written permission.
2254263Sshin *
2354263Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2454263Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2554263Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2654263Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2754263Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2854263Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2954263Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3054263Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3154263Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3254263Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3354263Sshin * SUCH DAMAGE.
3454263Sshin *
3554263Sshin * $FreeBSD: head/sys/net/if_faith.c 91317 2002-02-26 17:11:37Z dillon $
3654263Sshin */
3754263Sshin/*
3854263Sshin * derived from
3954263Sshin *	@(#)if_loop.c	8.1 (Berkeley) 6/10/93
4054263Sshin * Id: if_loop.c,v 1.22 1996/06/19 16:24:10 wollman Exp
4154263Sshin */
4254263Sshin
4354263Sshin/*
4454263Sshin * Loopback interface driver for protocol testing and timing.
4554263Sshin */
4678064Sume#include "opt_inet.h"
4778064Sume#include "opt_inet6.h"
4854263Sshin
4954263Sshin#include <sys/param.h>
5054263Sshin#include <sys/systm.h>
5154263Sshin#include <sys/kernel.h>
5254263Sshin#include <sys/mbuf.h>
5354263Sshin#include <sys/socket.h>
5478064Sume#include <sys/errno.h>
5554263Sshin#include <sys/sockio.h>
5678064Sume#include <sys/time.h>
5778064Sume#include <sys/queue.h>
5883934Sbrooks#include <sys/types.h>
5983934Sbrooks#include <sys/malloc.h>
6083934Sbrooks#include <machine/bus.h>	/* XXX: Shouldn't really be required! */
6183934Sbrooks#include <sys/rman.h>
6254263Sshin
6354263Sshin#include <net/if.h>
6454263Sshin#include <net/if_types.h>
6554263Sshin#include <net/netisr.h>
6654263Sshin#include <net/route.h>
6754263Sshin#include <net/bpf.h>
6854263Sshin
6978064Sume#ifdef	INET
7078064Sume#include <netinet/in.h>
7178064Sume#include <netinet/in_systm.h>
7278064Sume#include <netinet/in_var.h>
7378064Sume#include <netinet/ip.h>
7478064Sume#endif
7578064Sume
7678064Sume#ifdef INET6
7778064Sume#ifndef INET
7878064Sume#include <netinet/in.h>
7978064Sume#endif
8078064Sume#include <netinet6/in6_var.h>
8178064Sume#include <netinet/ip6.h>
8278064Sume#include <netinet6/ip6_var.h>
8378064Sume#endif
8478064Sume
8571991Speter#include <net/net_osdep.h>
8671991Speter
8783934Sbrooks#define FAITHNAME	"faith"
8883934Sbrooks#define FAITH_MAXUNIT	0x7fff	/* ifp->if_unit is only 15 bits */
8983934Sbrooks
9083934Sbrooksstruct faith_softc {
9183934Sbrooks	struct ifnet sc_if;	/* must be first */
9283934Sbrooks	struct resource *r_unit;
9383934Sbrooks	LIST_ENTRY(faith_softc) sc_list;
9483934Sbrooks};
9583934Sbrooks
9678064Sumestatic int faithioctl __P((struct ifnet *, u_long, caddr_t));
9778064Sumeint faithoutput __P((struct ifnet *, struct mbuf *, struct sockaddr *,
9878064Sume	struct rtentry *));
9985074Srustatic void faithrtrequest __P((int, struct rtentry *, struct rt_addrinfo *));
10091317Sdillon#ifdef INET6
10183934Sbrooksstatic int faithprefix __P((struct in6_addr *));
10291317Sdillon#endif
10354263Sshin
10483934Sbrooksstatic int faithmodevent __P((module_t, int, void *));
10578064Sume
10683934Sbrooksstatic MALLOC_DEFINE(M_FAITH, FAITHNAME, "Firewall Assisted Tunnel Interface");
10783934Sbrooksstatic struct rman faithunits[1];
10889065Smsmithstatic LIST_HEAD(, faith_softc) faith_softc_list;
10954263Sshin
11083934Sbrooksint	faith_clone_create __P((struct if_clone *, int *));
11183934Sbrooksvoid	faith_clone_destroy __P((struct ifnet *));
11283934Sbrooks
11383934Sbrooksstruct if_clone faith_cloner =
11483934Sbrooks    IF_CLONE_INITIALIZER(FAITHNAME, faith_clone_create, faith_clone_destroy);
11583934Sbrooks
11654263Sshin#define	FAITHMTU	1500
11754263Sshin
11883934Sbrooksstatic int
11983934Sbrooksfaithmodevent(mod, type, data)
12083934Sbrooks	module_t mod;
12183934Sbrooks	int type;
12283934Sbrooks	void *data;
12354263Sshin{
12483934Sbrooks	int err;
12554263Sshin
12683934Sbrooks	switch (type) {
12783934Sbrooks	case MOD_LOAD:
12883934Sbrooks		faithunits->rm_type = RMAN_ARRAY;
12983934Sbrooks		faithunits->rm_descr = "configurable if_faith units";
13083934Sbrooks		err = rman_init(faithunits);
13183934Sbrooks		if (err != 0)
13283934Sbrooks			return (err);
13383934Sbrooks		err = rman_manage_region(faithunits, 0, FAITH_MAXUNIT);
13483934Sbrooks		if (err != 0) {
13583934Sbrooks			printf("%s: faithunits: rman_manage_region: "
13683934Sbrooks			    "Failed %d\n", FAITHNAME, err);
13783934Sbrooks			rman_fini(faithunits);
13883934Sbrooks			return (err);
13983934Sbrooks		}
14083934Sbrooks		LIST_INIT(&faith_softc_list);
14183934Sbrooks		if_clone_attach(&faith_cloner);
14283934Sbrooks
14383934Sbrooks#ifdef INET6
14483934Sbrooks		faithprefix_p = faithprefix;
14578064Sume#endif
14683934Sbrooks
14783934Sbrooks		break;
14883934Sbrooks	case MOD_UNLOAD:
14983934Sbrooks#ifdef INET6
15083934Sbrooks		faithprefix_p = NULL;
15178064Sume#endif
15283934Sbrooks
15383934Sbrooks		if_clone_detach(&faith_cloner);
15483934Sbrooks
15583934Sbrooks		while (!LIST_EMPTY(&faith_softc_list))
15683934Sbrooks			faith_clone_destroy(
15783934Sbrooks			    &LIST_FIRST(&faith_softc_list)->sc_if);
15883934Sbrooks
15983934Sbrooks		err = rman_fini(faithunits);
16083934Sbrooks		if (err != 0)
16183934Sbrooks			return (err);
16283934Sbrooks
16383934Sbrooks		break;
16454263Sshin	}
16583934Sbrooks	return 0;
16654263Sshin}
16778064Sume
16883934Sbrooksstatic moduledata_t faith_mod = {
16983934Sbrooks	"if_faith",
17083934Sbrooks	faithmodevent,
17183934Sbrooks	0
17283934Sbrooks};
17383934Sbrooks
17483934SbrooksDECLARE_MODULE(if_faith, faith_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
17583934SbrooksMODULE_VERSION(if_faith, 1);
17683934Sbrooks
17778064Sumeint
17883934Sbrooksfaith_clone_create(ifc, unit)
17983934Sbrooks	struct if_clone *ifc;
18083934Sbrooks	int *unit;
18183934Sbrooks{
18283934Sbrooks	struct resource *r;
18383934Sbrooks	struct faith_softc *sc;
18483934Sbrooks
18583934Sbrooks	if (*unit > FAITH_MAXUNIT)
18683934Sbrooks		return (ENXIO);
18783934Sbrooks
18883934Sbrooks	if (*unit < 0) {
18983934Sbrooks		r = rman_reserve_resource(faithunits, 0, FAITH_MAXUNIT, 1,
19083934Sbrooks		    RF_ALLOCATED | RF_ACTIVE, NULL);
19183934Sbrooks		if (r == NULL)
19283934Sbrooks			return (ENOSPC);
19383934Sbrooks		*unit = rman_get_start(r);
19483934Sbrooks	} else {
19583934Sbrooks		r = rman_reserve_resource(faithunits, *unit, *unit, 1,
19683934Sbrooks		    RF_ALLOCATED | RF_ACTIVE, NULL);
19783934Sbrooks		if (r == NULL)
19883934Sbrooks			return (ENOSPC);
19983934Sbrooks	}
20083934Sbrooks
20183934Sbrooks	sc = malloc(sizeof(struct faith_softc), M_FAITH, M_WAITOK);
20283934Sbrooks	bzero(sc, sizeof(struct faith_softc));
20383934Sbrooks
20483934Sbrooks	sc->sc_if.if_softc = sc;
20583934Sbrooks	sc->sc_if.if_name = FAITHNAME;
20683934Sbrooks	sc->sc_if.if_unit = *unit;
20783934Sbrooks	sc->r_unit = r;
20883934Sbrooks
20983934Sbrooks	sc->sc_if.if_mtu = FAITHMTU;
21083934Sbrooks	/* Change to BROADCAST experimentaly to announce its prefix. */
21183934Sbrooks	sc->sc_if.if_flags = /* IFF_LOOPBACK */ IFF_BROADCAST | IFF_MULTICAST;
21283934Sbrooks	sc->sc_if.if_ioctl = faithioctl;
21383934Sbrooks	sc->sc_if.if_output = faithoutput;
21483934Sbrooks	sc->sc_if.if_type = IFT_FAITH;
21583934Sbrooks	sc->sc_if.if_hdrlen = 0;
21683934Sbrooks	sc->sc_if.if_addrlen = 0;
21788034Sbrooks	sc->sc_if.if_snd.ifq_maxlen = ifqmaxlen;
21883934Sbrooks	if_attach(&sc->sc_if);
21983934Sbrooks	bpfattach(&sc->sc_if, DLT_NULL, sizeof(u_int));
22083934Sbrooks	LIST_INSERT_HEAD(&faith_softc_list, sc, sc_list);
22183934Sbrooks	return (0);
22283934Sbrooks}
22383934Sbrooks
22483934Sbrooksvoid
22583934Sbrooksfaith_clone_destroy(ifp)
22683934Sbrooks	struct ifnet *ifp;
22783934Sbrooks{
22883934Sbrooks	int err;
22983934Sbrooks	struct faith_softc *sc = (void *) ifp;
23083934Sbrooks
23183934Sbrooks	LIST_REMOVE(sc, sc_list);
23283934Sbrooks	bpfdetach(ifp);
23383934Sbrooks	if_detach(ifp);
23483934Sbrooks
23583934Sbrooks	err = rman_release_resource(sc->r_unit);
23683934Sbrooks	KASSERT(err == 0, ("Unexpected error freeing resource"));
23783934Sbrooks
23883934Sbrooks	free(sc, M_FAITH);
23983934Sbrooks}
24083934Sbrooks
24183934Sbrooksint
24278064Sumefaithoutput(ifp, m, dst, rt)
24378064Sume	struct ifnet *ifp;
24478064Sume	struct mbuf *m;
24578064Sume	struct sockaddr *dst;
24678064Sume	struct rtentry *rt;
24778064Sume{
24878064Sume	int isr;
24978064Sume	struct ifqueue *ifq = 0;
25078064Sume
25178064Sume	if ((m->m_flags & M_PKTHDR) == 0)
25278064Sume		panic("faithoutput no HDR");
25383934Sbrooks
25478064Sume	/* BPF write needs to be handled specially */
25578064Sume	if (dst->sa_family == AF_UNSPEC) {
25678064Sume		dst->sa_family = *(mtod(m, int *));
25778064Sume		m->m_len -= sizeof(int);
25878064Sume		m->m_pkthdr.len -= sizeof(int);
25978064Sume		m->m_data += sizeof(int);
26078064Sume	}
26178064Sume
26278064Sume	if (ifp->if_bpf) {
26378064Sume		/*
26478064Sume		 * We need to prepend the address family as
26578064Sume		 * a four byte field.  Cons up a faith header
26678064Sume		 * to pacify bpf.  This is safe because bpf
26778064Sume		 * will only read from the mbuf (i.e., it won't
26878064Sume		 * try to free it or keep a pointer a to it).
26978064Sume		 */
27078064Sume		struct mbuf m0;
27178064Sume		u_int32_t af = dst->sa_family;
27278064Sume
27378064Sume		m0.m_next = m;
27478064Sume		m0.m_len = 4;
27578064Sume		m0.m_data = (char *)&af;
27678064Sume
27778064Sume		bpf_mtap(ifp, &m0);
27878064Sume	}
27978064Sume
28078064Sume	if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
28178064Sume		m_freem(m);
28278064Sume		return (rt->rt_flags & RTF_BLACKHOLE ? 0 :
28378064Sume		        rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
28478064Sume	}
28578064Sume	ifp->if_opackets++;
28678064Sume	ifp->if_obytes += m->m_pkthdr.len;
28778064Sume	switch (dst->sa_family) {
28878064Sume#ifdef INET
28978064Sume	case AF_INET:
29078064Sume		ifq = &ipintrq;
29178064Sume		isr = NETISR_IP;
29278064Sume		break;
29378064Sume#endif
29478064Sume#ifdef INET6
29578064Sume	case AF_INET6:
29678064Sume		ifq = &ip6intrq;
29778064Sume		isr = NETISR_IPV6;
29878064Sume		break;
29978064Sume#endif
30078064Sume	default:
30178064Sume		m_freem(m);
30278064Sume		return EAFNOSUPPORT;
30378064Sume	}
30478064Sume
30578064Sume	/* XXX do we need more sanity checks? */
30678064Sume
30778064Sume	m->m_pkthdr.rcvif = ifp;
30878064Sume	ifp->if_ipackets++;
30978064Sume	ifp->if_ibytes += m->m_pkthdr.len;
31078064Sume	(void) IF_HANDOFF(ifq, m, NULL);
31178064Sume	schednetisr(isr);
31278064Sume	return (0);
31378064Sume}
31478064Sume
31578064Sume/* ARGSUSED */
31678064Sumestatic void
31785074Srufaithrtrequest(cmd, rt, info)
31878064Sume	int cmd;
31978064Sume	struct rtentry *rt;
32085074Sru	struct rt_addrinfo *info;
32178064Sume{
32278064Sume	if (rt) {
32378064Sume		rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; /* for ISO */
32478064Sume		/*
32578064Sume		 * For optimal performance, the send and receive buffers
32678064Sume		 * should be at least twice the MTU plus a little more for
32778064Sume		 * overhead.
32878064Sume		 */
32978064Sume		rt->rt_rmx.rmx_recvpipe =
33078064Sume			rt->rt_rmx.rmx_sendpipe = 3 * FAITHMTU;
33178064Sume	}
33278064Sume}
33378064Sume
33478064Sume/*
33578064Sume * Process an ioctl request.
33678064Sume */
33778064Sume/* ARGSUSED */
33878064Sumestatic int
33978064Sumefaithioctl(ifp, cmd, data)
34078064Sume	struct ifnet *ifp;
34178064Sume	u_long cmd;
34278064Sume	caddr_t data;
34378064Sume{
34478064Sume	struct ifaddr *ifa;
34578064Sume	struct ifreq *ifr = (struct ifreq *)data;
34678064Sume	int error = 0;
34778064Sume
34878064Sume	switch (cmd) {
34978064Sume
35078064Sume	case SIOCSIFADDR:
35178064Sume		ifp->if_flags |= IFF_UP | IFF_RUNNING;
35278064Sume		ifa = (struct ifaddr *)data;
35378064Sume		ifa->ifa_rtrequest = faithrtrequest;
35478064Sume		/*
35578064Sume		 * Everything else is done at a higher level.
35678064Sume		 */
35778064Sume		break;
35878064Sume
35978064Sume	case SIOCADDMULTI:
36078064Sume	case SIOCDELMULTI:
36178064Sume		if (ifr == 0) {
36278064Sume			error = EAFNOSUPPORT;		/* XXX */
36378064Sume			break;
36478064Sume		}
36578064Sume		switch (ifr->ifr_addr.sa_family) {
36678064Sume#ifdef INET
36778064Sume		case AF_INET:
36878064Sume			break;
36978064Sume#endif
37078064Sume#ifdef INET6
37178064Sume		case AF_INET6:
37278064Sume			break;
37378064Sume#endif
37478064Sume
37578064Sume		default:
37678064Sume			error = EAFNOSUPPORT;
37778064Sume			break;
37878064Sume		}
37978064Sume		break;
38078064Sume
38178064Sume#ifdef SIOCSIFMTU
38278064Sume	case SIOCSIFMTU:
38378064Sume		ifp->if_mtu = ifr->ifr_mtu;
38478064Sume		break;
38578064Sume#endif
38678064Sume
38778064Sume	case SIOCSIFFLAGS:
38878064Sume		break;
38978064Sume
39078064Sume	default:
39178064Sume		error = EINVAL;
39278064Sume	}
39378064Sume	return (error);
39478064Sume}
39578064Sume
39679326Sume#ifdef INET6
39778064Sume/*
39878064Sume * XXX could be slow
39978064Sume * XXX could be layer violation to call sys/net from sys/netinet6
40078064Sume */
40183934Sbrooksstatic int
40278064Sumefaithprefix(in6)
40378064Sume	struct in6_addr *in6;
40478064Sume{
40578064Sume	struct rtentry *rt;
40678064Sume	struct sockaddr_in6 sin6;
40778064Sume	int ret;
40878064Sume
40978064Sume	if (ip6_keepfaith == 0)
41078064Sume		return 0;
41178064Sume
41278064Sume	bzero(&sin6, sizeof(sin6));
41378064Sume	sin6.sin6_family = AF_INET6;
41478064Sume	sin6.sin6_len = sizeof(struct sockaddr_in6);
41578064Sume	sin6.sin6_addr = *in6;
41678064Sume	rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
41778064Sume	if (rt && rt->rt_ifp && rt->rt_ifp->if_type == IFT_FAITH &&
41878064Sume	    (rt->rt_ifp->if_flags & IFF_UP) != 0)
41978064Sume		ret = 1;
42078064Sume	else
42178064Sume		ret = 0;
42278064Sume	if (rt)
42378064Sume		RTFREE(rt);
42478064Sume	return ret;
42578064Sume}
42679326Sume#endif
427