#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_faith.c,v 1.64 2024/09/07 06:17:37 andvar Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <sys/device.h>
#include <sys/module.h>
#include <sys/atomic.h>
#include <sys/cpu.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/bpf.h>
#include <net/if_faith.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#endif
#ifdef INET6
#ifndef INET
#include <netinet/in.h>
#endif
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#endif
#include "ioconf.h"
static int faithioctl(struct ifnet *, u_long, void *);
static int faithoutput(struct ifnet *, struct mbuf *,
const struct sockaddr *, const struct rtentry *);
static void faithrtrequest(int, struct rtentry *,
const struct rt_addrinfo *);
static int faith_clone_create(struct if_clone *, int);
static int faith_clone_destroy(struct ifnet *);
static struct if_clone faith_cloner =
IF_CLONE_INITIALIZER("faith", faith_clone_create, faith_clone_destroy);
#define FAITHMTU 1500
static u_int faith_count;
void
faithattach(int count)
{
}
static void
faithinit(void)
{
if_clone_attach(&faith_cloner);
}
static int
faithdetach(void)
{
int error = 0;
if (faith_count != 0)
error = EBUSY;
if (error == 0)
if_clone_detach(&faith_cloner);
return error;
}
static int
faith_clone_create(struct if_clone *ifc, int unit)
{
struct ifnet *ifp;
ifp = if_alloc(IFT_FAITH);
if_initname(ifp, ifc->ifc_name, unit);
ifp->if_mtu = FAITHMTU;
ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST;
ifp->if_ioctl = faithioctl;
ifp->if_output = faithoutput;
ifp->if_type = IFT_FAITH;
ifp->if_hdrlen = 0;
ifp->if_addrlen = 0;
ifp->if_dlt = DLT_NULL;
if_attach(ifp);
if_alloc_sadl(ifp);
bpf_attach(ifp, DLT_NULL, sizeof(u_int));
atomic_inc_uint(&faith_count);
return (0);
}
int
faith_clone_destroy(struct ifnet *ifp)
{
bpf_detach(ifp);
if_detach(ifp);
if_free(ifp);
atomic_dec_uint(&faith_count);
return (0);
}
static int
faithoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
const struct rtentry *rt)
{
pktqueue_t *pktq;
size_t pktlen;
int s, error;
uint32_t af;
if ((m->m_flags & M_PKTHDR) == 0)
panic("faithoutput no HDR");
af = dst->sa_family;
if (af == AF_UNSPEC) {
af = *(mtod(m, int *));
m_adj(m, sizeof(int));
}
bpf_mtap_af(ifp, af, m, BPF_D_OUT);
if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
m_freem(m);
return (rt->rt_flags & RTF_BLACKHOLE ? 0 :
rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
}
pktlen = m->m_pkthdr.len;
if_statadd2(ifp, if_opackets, 1, if_obytes, pktlen);
switch (af) {
#ifdef INET
case AF_INET:
pktq = ip_pktq;
break;
#endif
#ifdef INET6
case AF_INET6:
pktq = ip6_pktq;
break;
#endif
default:
m_freem(m);
return EAFNOSUPPORT;
}
KASSERT(pktq != NULL);
m_set_rcvif(m, ifp);
s = splnet();
if (__predict_true(pktq_enqueue(pktq, m, 0))) {
if_statadd2(ifp, if_ipackets, 1, if_ibytes, pktlen);
error = 0;
} else {
m_freem(m);
error = ENOBUFS;
}
splx(s);
return error;
}
static void
faithrtrequest(int cmd, struct rtentry *rt,
const struct rt_addrinfo *info)
{
if (rt)
rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
}
static int
faithioctl(struct ifnet *ifp, u_long cmd, void *data)
{
struct ifaddr *ifa;
struct ifreq *ifr = (struct ifreq *)data;
int error = 0;
switch (cmd) {
case SIOCINITIFADDR:
ifa = (struct ifaddr *)data;
ifa->ifa_rtrequest = faithrtrequest;
ifp->if_flags |= IFF_UP | IFF_RUNNING;
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
if (ifr == 0) {
error = EAFNOSUPPORT;
break;
}
switch (ifr->ifr_addr.sa_family) {
#ifdef INET
case AF_INET:
break;
#endif
#ifdef INET6
case AF_INET6:
break;
#endif
default:
error = EAFNOSUPPORT;
break;
}
break;
default:
if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
error = 0;
break;
}
return (error);
}
#ifdef INET6
int
faithprefix(struct in6_addr *in6)
{
struct rtentry *rt;
struct sockaddr_in6 sin6;
int ret;
if (ip6_keepfaith == 0)
return 0;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_addr = *in6;
rt = rtalloc1((struct sockaddr *)&sin6, 0);
if (rt && rt->rt_ifp && rt->rt_ifp->if_type == IFT_FAITH &&
(rt->rt_ifp->if_flags & IFF_UP) != 0)
ret = 1;
else
ret = 0;
if (rt)
rt_unref(rt);
return ret;
}
#endif
#include "if_module.h"
IF_MODULE(MODULE_CLASS_DRIVER, faith, NULL)