[BACK]Return to if_enc.c CVS log [TXT][DIR] Up to [local] / src / sys / net

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 */
}