if_enc.c revision 1.18
1/*	$OpenBSD: if_enc.c,v 1.18 2000/01/15 19:38:09 angelos Exp $	*/
2
3/*
4 * The authors of this code are John Ioannidis (ji@tla.org),
5 * Angelos D. Keromytis (kermit@csd.uch.gr) and
6 * Niels Provos (provos@physnet.uni-hamburg.de).
7 *
8 * This code was written by John Ioannidis for BSD/OS in Athens, Greece,
9 * in November 1995.
10 *
11 * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
12 * by Angelos D. Keromytis.
13 *
14 * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
15 * and Niels Provos.
16 *
17 * Copyright (C) 1995, 1996, 1997, 1998 by John Ioannidis, Angelos D. Keromytis
18 * and Niels Provos.
19 *
20 * Permission to use, copy, and modify this software without fee
21 * is hereby granted, provided that this entire notice is included in
22 * all copies of any software which is or includes a copy or
23 * modification of this software.
24 * You may use this code under the GNU public license if you so wish. Please
25 * contribute changes back to the authors under this freer than GPL license
26 * so that we may further the use of strong encryption without limitations to
27 * all.
28 *
29 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
30 * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
31 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
32 * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
33 * PURPOSE.
34 */
35
36/*
37 * Encapsulation interface driver.
38 */
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/kernel.h>
43#include <sys/mbuf.h>
44#include <sys/socket.h>
45#include <sys/errno.h>
46#include <sys/ioctl.h>
47#include <sys/proc.h>
48
49#include <net/if.h>
50#include <net/if_types.h>
51#include <net/netisr.h>
52#include <net/route.h>
53#include <net/bpf.h>
54
55#include <netinet/ip_ipsp.h>
56#include <net/if_enc.h>
57
58#ifdef	INET
59#include <netinet/in.h>
60#include <netinet/in_systm.h>
61#include <netinet/in_var.h>
62#include <netinet/ip_var.h>
63#include <netinet/ip.h>
64#endif
65
66#ifdef INET6
67#include <netinet6/in6.h>
68#include <netinet6/ip6.h>
69#include <netinet6/ip6_var.h>
70#endif /* INET6 */
71
72#ifdef ISO
73extern struct ifqueue clnlintrq;
74#endif
75
76#ifdef NS
77extern struct ifqueue nsintrq;
78#endif
79
80#include "bpfilter.h"
81#include "enc.h"
82
83#ifdef ENCDEBUG
84#define DPRINTF(x)    do { if (encdebug) printf x ; } while (0)
85#else
86#define DPRINTF(x)
87#endif
88
89#ifndef offsetof
90#define offsetof(s, e) ((int)&((s *)0)->e)
91#endif
92
93struct enc_softc encif[NENC];
94
95void	encattach __P((int));
96int	encoutput __P((struct ifnet *, struct mbuf *, struct sockaddr *,
97	    	       struct rtentry *));
98int	encioctl __P((struct ifnet *, u_long, caddr_t));
99void	encrtrequest __P((int, struct rtentry *, struct sockaddr *));
100void	encstart __P((struct ifnet *));
101
102extern int ifqmaxlen;
103
104void
105encattach(int nenc)
106{
107    struct ifnet *ifp;
108    int i;
109
110    bzero(encif, sizeof(encif));
111
112    for (i = 0; i < NENC; i++)
113    {
114	ifp = &encif[i].sc_if;
115	sprintf(ifp->if_xname, "enc%d", i);
116	ifp->if_softc = &encif[i];
117	ifp->if_mtu = ENCMTU;
118	ifp->if_ioctl = encioctl;
119	ifp->if_output = encoutput;
120	ifp->if_start = encstart;
121	ifp->if_type = IFT_ENC;
122	ifp->if_snd.ifq_maxlen = ifqmaxlen;
123	ifp->if_hdrlen = ENC_HDRLEN;
124	if_attach(ifp);
125
126#if NBPFILTER > 0
127	bpfattach(&encif[i].sc_if.if_bpf, ifp, DLT_ENC, ENC_HDRLEN);
128#endif
129    }
130}
131
132/*
133 * Start output on the enc interface.
134 */
135void
136encstart(ifp)
137struct ifnet *ifp;
138{
139    struct mbuf *m;
140    int s;
141
142#ifndef IPSEC
143    for (;;)
144    {
145        s = splimp();
146	IF_DROP(&ifp->if_snd);
147        IF_DEQUEUE(&ifp->if_snd, m);
148        splx(s);
149
150        if (m == NULL)
151          return;
152        else
153          m_freem(m);
154    }
155#else /* IPSEC */
156    struct enc_softc *enc = ifp->if_softc;
157    int err = 0, protoflag;
158    struct mbuf *mp;
159    struct tdb *tdb;
160
161    /* If the interface is not setup, flush the queue */
162    if ((enc->sc_spi == 0) && (enc->sc_sproto == 0) &&
163	((enc->sc_dst.sa.sa_family == AF_INET) ||
164	 (enc->sc_dst.sa.sa_family == AF_INET6)))
165    {
166	DPRINTF(("%s: not initialized with SA\n", ifp->if_xname));
167
168	for (;;)
169	{
170	    s = splimp();
171            IF_DROP(&ifp->if_snd);
172	    IF_DEQUEUE(&ifp->if_snd, m);
173	    splx(s);
174	    if (m == NULL)
175	      return;
176	    else
177	      m_freem(m);
178	}
179
180	/* Unreachable */
181    }
182
183    /* Find what type of processing we need to do */
184    tdb = gettdb(enc->sc_spi, &(enc->sc_dst), enc->sc_sproto);
185    if (tdb == NULL)
186    {
187	DPRINTF(("%s: SA non-existant\n", ifp->if_xname));
188
189	/* Flush the queue */
190	for (;;)
191	{
192	    s = splimp();
193            IF_DROP(&ifp->if_snd);
194	    IF_DEQUEUE(&ifp->if_snd, m);
195	    splx(s);
196	    if (m == NULL)
197	      return;
198	    else
199	      m_freem(m);
200	}
201    }
202
203    /* See if we need to notify a key mgmt. daemon to setup SAs */
204    if (ntohl(enc->sc_spi) == SPI_LOCAL_USE)
205    {
206	/*
207	 * XXX Can't do this for now, as there's no way for
208	 * XXX key mgmt. to specify link-layer properties
209	 * XXX (e.g., encrypt everything on this interface)
210	 */
211#ifdef notyet
212	if (tdb->tdb_satype != SADB_X_SATYPE_BYPASS)
213	  pfkeyv2_acquire(tdb, 0); /* No point checking for errors */
214#endif
215
216	/* Flush the queue */
217	for (;;)
218	{
219	    s = splimp();
220            IF_DROP(&ifp->if_snd);
221	    IF_DEQUEUE(&ifp->if_snd, m);
222	    splx(s);
223	    if (m == NULL)
224	      return;
225	    else
226	      m_freem(m);
227	}
228
229	/* Unreachable */
230    }
231
232    /* IPsec-process all packets in the queue */
233    for (;;)
234    {
235	/* Get a packet from the queue */
236	s = splimp();
237	IF_DEQUEUE(&ifp->if_snd, m);
238	splx(s);
239
240	if (m == NULL) /* Empty queue */
241	  return;
242
243	/* First, we encapsulate in etherip */
244	err = etherip_output(m, tdb, &mp, 0, 0); /* Last 2 args not used */
245	if ((mp == NULL) || err)
246	{
247	    /* Just skip this frame */
248            IF_DROP(&ifp->if_snd);
249	    if (mp)
250	      m_freem(mp);
251	    continue;
252	}
253	else
254	{
255	    m = mp;
256	    mp = NULL;
257	}
258
259	protoflag = tdb->tdb_dst.sa.sa_family;
260
261	/* IPsec packet processing -- skip encapsulation */
262	err = ipsp_process_packet(m, &mp, tdb, &protoflag, 1);
263	if ((mp == NULL) || err)
264	{
265            IF_DROP(&ifp->if_snd);
266	    if (mp)
267	      m_freem(mp);
268	    continue;
269	}
270	else
271	{
272	    m = mp;
273	    mp = NULL;
274	}
275
276#ifdef INET
277	/* Send the packet on its way, no point checking for errors here */
278	if (protoflag == AF_INET)
279	  ip_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, NULL, NULL);
280#endif /* INET */
281
282#ifdef INET6
283	/* Send the packet on its way, no point checking for errors here */
284	if (protoflag == AF_INET6)
285	  ip6_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT,
286		     NULL, NULL);
287#endif /* INET6 */
288
289	/* XXX Should find a way to avoid bridging-loops, some mbuf flag ? */
290    }
291#endif /* IPSEC */
292}
293
294/*
295 * Shamelessly stolen from looutput()
296 */
297int
298encoutput(ifp, m, dst, rt)
299struct ifnet *ifp;
300register struct mbuf *m;
301struct sockaddr *dst;
302register struct rtentry *rt;
303{
304    register struct ifqueue *ifq = 0;
305    int s, isr;
306
307    if ((m->m_flags & M_PKTHDR) == 0)
308      panic("encoutput(): no HDR");
309
310    ifp->if_lastchange = time;
311    m->m_pkthdr.rcvif = ifp;
312
313    if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE))
314    {
315	m_freem(m);
316	return (rt->rt_flags & RTF_BLACKHOLE ? 0 :
317		rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
318    }
319
320    ifp->if_opackets++;
321    ifp->if_obytes += m->m_pkthdr.len;
322
323    switch (dst->sa_family)
324    {
325#ifdef INET
326	case AF_INET:
327	    ifq = &ipintrq;
328	    isr = NETISR_IP;
329	    break;
330#endif
331#ifdef INET6
332	case AF_INET6:
333	    ifq = &ip6intrq;
334	    isr = NETISR_IPV6;
335	    break;
336#endif
337#ifdef NS
338	case AF_NS:
339	    ifq = &nsintrq;
340	    isr = NETISR_NS;
341	    break;
342#endif
343#ifdef ISO
344	case AF_ISO:
345	    ifq = &clnlintrq;
346	    isr = NETISR_ISO;
347	    break;
348#endif
349	default:
350	    m_freem(m);
351	    return (EAFNOSUPPORT);
352    }
353
354    s = splimp();
355    if (IF_QFULL(ifq))
356    {
357	IF_DROP(ifq);
358	m_freem(m);
359	splx(s);
360	return (ENOBUFS);
361    }
362
363    IF_ENQUEUE(ifq, m);
364    schednetisr(isr);
365
366    /* Statistics */
367    ifp->if_ipackets++;
368    ifp->if_ibytes += m->m_pkthdr.len;
369    splx(s);
370    return (0);
371}
372
373/* ARGSUSED */
374void
375encrtrequest(cmd, rt, sa)
376int cmd;
377struct rtentry *rt;
378struct sockaddr *sa;
379{
380    if (rt)
381      rt->rt_rmx.rmx_mtu = ENCMTU;
382}
383
384/* ARGSUSED */
385int
386encioctl(ifp, cmd, data)
387register struct ifnet *ifp;
388u_long cmd;
389caddr_t data;
390{
391#ifdef IPSEC
392    struct enc_softc *enc = (struct enc_softc *) ifp->if_softc;
393    struct ifsa *ifsa = (struct ifsa *) data;
394    struct proc *prc = curproc;             /* XXX */
395    struct tdb *tdb;
396    int s, error;
397
398    /*
399     * enc0 does not allow binding of SAs, as it's used for all non-bound
400     * SAs.
401     */
402    if (ifp->if_softc == &encif[0])
403	return EOPNOTSUPP;
404
405    switch (cmd)
406    {
407	case SIOCSIFADDR:
408	    return EOPNOTSUPP;
409
410	case SIOCGENCSA:
411	    ifsa->sa_spi = enc->sc_spi;
412	    ifsa->sa_proto = enc->sc_sproto;
413	    bcopy(&enc->sc_dst, &ifsa->sa_dst, enc->sc_dst.sa.sa_len);
414	    break;
415
416	case SIOCSENCCLEARSA:
417	    /* Check for superuser */
418	    if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
419	      break;
420
421	    if (ifsa->sa_proto == 0)
422	    {
423		/* Clear SA if requested */
424		if (enc->sc_sproto != 0)
425		{
426		    s = spltdb();
427		    tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto);
428		    if (tdb != NULL)
429		      tdb->tdb_interface = 0;
430		    splx(s);
431		}
432
433		bzero(&enc->sc_dst, sizeof(union sockaddr_union));
434		enc->sc_spi = 0;
435		enc->sc_sproto = 0;
436		break;
437	    }
438
439	    s = spltdb();
440	    tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
441	    if (tdb == NULL)
442	    {
443		splx(s);
444		error = ENOENT;
445		break;
446	    }
447
448	    tdb->tdb_interface = 0;
449	    splx(s);
450	    break;
451
452	case SIOCSENCSRCSA:
453	    /* Check for superuser */
454	    if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
455	      break;
456
457	    if (ifsa->sa_proto == 0)
458	    {
459		error = ENOENT;
460		break;
461	    }
462
463	    s = spltdb();
464	    tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
465	    if (tdb == NULL)
466	    {
467		splx(s);
468		error = ENOENT;
469		break;
470	    }
471
472	    /* Is it already bound ? */
473	    if (tdb->tdb_interface)
474	    {
475		splx(s);
476		error = EEXIST;
477		break;
478	    }
479
480	    tdb->tdb_interface = (caddr_t) ifp;
481	    splx(s);
482	    break;
483
484	case SIOCSENCDSTSA:
485	    /* Check for superuser */
486	    if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
487	      break;
488
489	    /* Check for pre-existing TDB */
490	    if (enc->sc_sproto != 0)
491	    {
492		error = EEXIST;
493		break;
494	    }
495
496	    s = spltdb();
497
498	    if (ifsa->sa_proto != 0)
499	    {
500		tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
501		if (tdb == NULL)
502		{
503		    splx(s);
504		    error = ENOENT;
505		    break;
506		}
507	    }
508	    else
509	    {
510		/* Clear SA if requested */
511		if (enc->sc_sproto != 0)
512		{
513		    tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto);
514		    if (tdb != NULL)
515		      tdb->tdb_interface = 0;
516		}
517
518		bzero(&enc->sc_dst, sizeof(enc->sc_dst));
519		enc->sc_spi = 0;
520		enc->sc_sproto = 0;
521
522		splx(s);
523		break;
524	    }
525
526#ifdef INET
527	    if ((ifsa->sa_dst.sa.sa_family == AF_INET) &&
528		(ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in)))
529	    {
530		splx(s);
531	    	error = EINVAL;
532		break;
533	    }
534#endif /* INET */
535
536#ifdef INET6
537	    if ((ifsa->sa_dst.sa.sa_family == AF_INET6) &&
538		(ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in6)))
539	    {
540		splx(s);
541		error = EINVAL;
542		break;
543	    }
544#endif /* INET6 */
545
546	    bcopy(&ifsa->sa_dst, &enc->sc_dst, ifsa->sa_dst.sa.sa_len);
547	    enc->sc_spi = ifsa->sa_spi;
548	    enc->sc_sproto = ifsa->sa_proto;
549	    tdb->tdb_interface = (caddr_t) ifp;
550
551	    splx(s);
552	    break;
553
554	default:
555	    error = EINVAL;
556	    break;
557    }
558
559    return (error);
560#else /* IPSEC */
561    return EOPNOTSUPP;
562#endif /* IPSEC */
563}
564