File: [local] / src / sys / net / if_enc.c (download)
Revision 1.31, Tue Jun 20 04:17:31 2000 UTC (23 years, 11 months ago) by itojun
Branch: MAIN
CVS Tags: OPENBSD_2_8_BASE, OPENBSD_2_8 Changes since 1.30: +5 -1 lines
initialize mtu/hlim for enc interface at encattach().
backgronud: inbound ipsec packet will have enc* as m->m_pkthdr.rcvif.
when we try to reflect the packet back in the kernel (like icmp6 echo),
we'd generate packet toward enc* interface. icmp6_reflect() will take
hoplimit value from nd_ifinfo[enc*], which was not initialized by the old code.
XXX the change to m->m_pkthdr.rcvif violates IPv6 scoped routing.
we will need to disable it, for at least IPv6.
|
/* $OpenBSD: if_enc.c,v 1.31 2000/06/20 04:17:31 itojun Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr) and
* Niels Provos (provos@physnet.uni-hamburg.de).
*
* This code was written by John Ioannidis for BSD/OS in Athens, Greece,
* in November 1995.
*
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
* by Angelos D. Keromytis.
*
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
* and Niels Provos.
*
* Copyright (C) 1995, 1996, 1997, 1998 by John Ioannidis, Angelos D. Keromytis
* and Niels Provos.
*
* Permission to use, copy, and modify this software without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
/*
* Encapsulation interface driver.
*/
#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/proc.h>
#include <machine/cpu.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <net/route.h>
#include <net/bpf.h>
#include <netinet/ip_ipsp.h>
#include <net/if_enc.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip_var.h>
#include <netinet/ip.h>
#endif
#ifdef INET6
#ifndef INET
#include <netinet/in.h>
#endif
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#endif /* INET6 */
#ifdef ISO
extern struct ifqueue clnlintrq;
#endif
#ifdef NS
extern struct ifqueue nsintrq;
#endif
#include "bpfilter.h"
#include "enc.h"
#ifdef ENCDEBUG
#define DPRINTF(x) do { if (encdebug) printf x ; } while (0)
#else
#define DPRINTF(x)
#endif
#ifndef offsetof
#define offsetof(s, e) ((int)&((s *)0)->e)
#endif
struct enc_softc encif[NENC];
void encattach __P((int));
int encoutput __P((struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *));
int encioctl __P((struct ifnet *, u_long, caddr_t));
void encrtrequest __P((int, struct rtentry *, struct sockaddr *));
void encstart __P((struct ifnet *));
extern int ifqmaxlen;
void
encattach(int nenc)
{
struct ifnet *ifp;
int i;
bzero(encif, sizeof(encif));
for (i = 0; i < NENC; i++)
{
ifp = &encif[i].sc_if;
sprintf(ifp->if_xname, "enc%d", i);
ifp->if_softc = &encif[i];
ifp->if_mtu = ENCMTU;
ifp->if_ioctl = encioctl;
ifp->if_output = encoutput;
ifp->if_start = encstart;
ifp->if_type = IFT_ENC;
ifp->if_snd.ifq_maxlen = ifqmaxlen;
ifp->if_hdrlen = ENC_HDRLEN;
if_attach(ifp);
#if NBPFILTER > 0
bpfattach(&encif[i].sc_if.if_bpf, ifp, DLT_ENC, ENC_HDRLEN);
#endif
#ifdef INET6
nd6_ifattach(ifp);
#endif
}
}
/*
* Start output on the enc interface.
*/
void
encstart(ifp)
struct ifnet *ifp;
{
struct mbuf *m;
int s;
#ifndef IPSEC
for (;;)
{
s = splimp();
IF_DROP(&ifp->if_snd);
IF_DEQUEUE(&ifp->if_snd, m);
splx(s);
if (m == NULL)
return;
else
m_freem(m);
}
#else /* IPSEC */
struct enc_softc *enc = ifp->if_softc;
int err = 0, protoflag;
struct mbuf *mp;
struct tdb *tdb;
/* If the interface is not setup, flush the queue */
if ((enc->sc_spi == 0) && (enc->sc_sproto == 0) &&
(enc->sc_dst.sa.sa_family != AF_INET) &&
(enc->sc_dst.sa.sa_family != AF_INET6))
{
DPRINTF(("%s: not initialized with SA\n", ifp->if_xname));
for (;;)
{
s = splimp();
IF_DROP(&ifp->if_snd);
IF_DEQUEUE(&ifp->if_snd, m);
splx(s);
if (m == NULL)
return;
else
m_freem(m);
}
/* Unreachable */
}
/* Find what type of processing we need to do */
tdb = gettdb(enc->sc_spi, &(enc->sc_dst), enc->sc_sproto);
if (tdb == NULL)
{
DPRINTF(("%s: SA non-existant\n", ifp->if_xname));
/* Flush the queue */
for (;;)
{
s = splimp();
IF_DROP(&ifp->if_snd);
IF_DEQUEUE(&ifp->if_snd, m);
splx(s);
if (m == NULL)
return;
else
m_freem(m);
}
}
/* See if we need to notify a key mgmt. daemon to setup SAs */
if (ntohl(enc->sc_spi) == SPI_LOCAL_USE)
{
#ifdef notyet
/* XXX Currently unsupported */
if (tdb->tdb_satype != SADB_X_SATYPE_BYPASS)
pfkeyv2_acquire(tdb, 0); /* No point checking for errors */
#endif
/* Flush the queue */
for (;;)
{
s = splimp();
IF_DROP(&ifp->if_snd);
IF_DEQUEUE(&ifp->if_snd, m);
splx(s);
if (m == NULL)
return;
else
m_freem(m);
}
/* Unreachable */
}
/* IPsec-process all packets in the queue */
for (;;)
{
/* Get a packet from the queue */
s = splimp();
IF_DEQUEUE(&ifp->if_snd, m);
splx(s);
if (m == NULL) /* Empty queue */
return;
/* Sanity check */
if ((m->m_flags & M_PKTHDR) == 0)
{
m_freem(m);
continue;
}
ifp->if_opackets++;
ifp->if_obytes += m->m_pkthdr.len;
m->m_pkthdr.rcvif = ifp;
mp = NULL;
/* Encapsulate in etherip or ip-in-ip, depending on interface flag */
if (ifp->if_flags & IFF_LINK0)
err = ipip_output(m, tdb, &mp, 0, 0); /* Last 2 args not used */
else
err = etherip_output(m, tdb, &mp, 0, 0); /* Last 2 args not used */
if ((mp == NULL) || err)
{
/* Just skip this frame */
IF_DROP(&ifp->if_snd);
if (mp)
m_freem(mp);
continue;
}
else
{
m = mp;
mp = NULL;
}
#ifdef INET
/* Fix header checksum for IPv4 */
if (tdb->tdb_dst.sa.sa_family == AF_INET)
{
struct ip *ip;
ip = mtod(m, struct ip *);
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
}
#endif
protoflag = tdb->tdb_dst.sa.sa_family;
/* IPsec packet processing -- skip encapsulation */
ipsp_process_packet(m, tdb, protoflag, 1);
/*
* XXX
* Should find a way to avoid bridging/routing-loops,
* perhaps use some mbuf flag ?
*/
}
#endif /* IPSEC */
}
int
encoutput(ifp, m, dst, rt)
struct ifnet *ifp;
register struct mbuf *m;
struct sockaddr *dst;
register struct rtentry *rt;
{
int s;
if ((m->m_flags & M_PKTHDR) == 0)
panic("encoutput(): no HDR");
ifp->if_lastchange = time;
m->m_pkthdr.rcvif = ifp;
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);
}
s = splimp();
if (IF_QFULL(&ifp->if_snd)) {
ifp->if_oerrors++;
m_freem(m);
splx(s);
return 0;
}
IF_ENQUEUE(&ifp->if_snd, m);
splx(s);
(ifp->if_start)(ifp);
return (0);
}
/* ARGSUSED */
void
encrtrequest(cmd, rt, sa)
int cmd;
struct rtentry *rt;
struct sockaddr *sa;
{
if (rt)
rt->rt_rmx.rmx_mtu = ENCMTU;
}
/* ARGSUSED */
int
encioctl(ifp, cmd, data)
register struct ifnet *ifp;
u_long cmd;
caddr_t data;
{
#ifdef IPSEC
struct enc_softc *enc = (struct enc_softc *) ifp->if_softc;
struct ifsa *ifsa = (struct ifsa *) data;
struct proc *prc = curproc; /* XXX */
struct tdb *tdb;
int s, error = 0;
/*
* enc0 does not allow binding of SAs, as it's used for all non-bound
* SAs.
*/
if (ifp->if_softc == &encif[0])
return EOPNOTSUPP;
switch (cmd)
{
case SIOCSIFADDR:
case SIOCAIFADDR:
case SIOCSIFDSTADDR:
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP)
ifp->if_flags |= IFF_RUNNING;
else
ifp->if_flags &= ~IFF_RUNNING;
break;
case SIOCGENCSA:
ifsa->sa_spi = enc->sc_spi;
ifsa->sa_proto = enc->sc_sproto;
bcopy(&enc->sc_dst, &ifsa->sa_dst, enc->sc_dst.sa.sa_len);
break;
case SIOCSENCCLEARSA:
/* Check for superuser */
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
break;
if (ifsa->sa_proto == 0)
{
/* Clear SA if requested */
if (enc->sc_sproto != 0)
{
s = spltdb();
tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto);
if (tdb != NULL)
tdb->tdb_interface = 0;
splx(s);
}
bzero(&enc->sc_dst, sizeof(union sockaddr_union));
enc->sc_spi = 0;
enc->sc_sproto = 0;
break;
}
s = spltdb();
tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
if (tdb == NULL)
{
splx(s);
error = ENOENT;
break;
}
tdb->tdb_interface = 0;
splx(s);
break;
case SIOCSENCSRCSA:
/* Check for superuser */
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
break;
if (ifsa->sa_proto == 0)
{
error = ENOENT;
break;
}
s = spltdb();
tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
if (tdb == NULL)
{
splx(s);
error = ENOENT;
break;
}
/* Is it already bound ? */
if (tdb->tdb_interface)
{
splx(s);
error = EEXIST;
break;
}
tdb->tdb_interface = (caddr_t) ifp;
splx(s);
break;
case SIOCSENCDSTSA:
/* Check for superuser */
if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
break;
/* Check for pre-existing TDB */
if (enc->sc_sproto != 0)
{
error = EEXIST;
break;
}
s = spltdb();
if (ifsa->sa_proto != 0)
{
tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
if (tdb == NULL)
{
splx(s);
error = ENOENT;
break;
}
}
else
{
/* Clear SA if requested */
if (enc->sc_sproto != 0)
{
tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto);
if (tdb != NULL)
tdb->tdb_interface = 0;
}
bzero(&enc->sc_dst, sizeof(enc->sc_dst));
enc->sc_spi = 0;
enc->sc_sproto = 0;
splx(s);
break;
}
#ifdef INET
if ((ifsa->sa_dst.sa.sa_family == AF_INET) &&
(ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in)))
{
splx(s);
error = EINVAL;
break;
}
#endif /* INET */
#ifdef INET6
if ((ifsa->sa_dst.sa.sa_family == AF_INET6) &&
(ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in6)))
{
splx(s);
error = EINVAL;
break;
}
#endif /* INET6 */
bcopy(&ifsa->sa_dst, &enc->sc_dst, ifsa->sa_dst.sa.sa_len);
enc->sc_spi = ifsa->sa_spi;
enc->sc_sproto = ifsa->sa_proto;
tdb->tdb_interface = (caddr_t) ifp;
splx(s);
break;
default:
error = EINVAL;
break;
}
return (error);
#else /* IPSEC */
return EOPNOTSUPP;
#endif /* IPSEC */
}