if_enc.c revision 1.16
1/*	$OpenBSD: if_enc.c,v 1.16 2000/01/02 09:22:58 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_DEQUEUE(&ifp->if_snd, m);
147        splx(s);
148
149        if (m == NULL)
150          return;
151        else
152          m_freem(m);
153    }
154#else /* IPSEC */
155    struct enc_softc *enc = ifp->if_softc;
156    int err = 0, protoflag;
157    struct mbuf *mp;
158    struct tdb *tdb;
159
160    /* If the interface is not setup, flush the queue */
161    if ((enc->sc_spi == 0) && (enc->sc_sproto == 0) &&
162	((enc->sc_dst.sa.sa_family == AF_INET) ||
163	 (enc->sc_dst.sa.sa_family == AF_INET6)))
164    {
165	DPRINTF(("%s: not initialized with SA\n", ifp->if_xname));
166
167	for (;;)
168	{
169	    s = splimp();
170	    IF_DEQUEUE(&ifp->if_snd, m);
171	    splx(s);
172	    if (m == NULL)
173	      return;
174	    else
175	      m_freem(m);
176	}
177
178	/* Unreachable */
179    }
180
181    /* Find what type of processing we need to do */
182    tdb = gettdb(enc->sc_spi, &(enc->sc_dst), enc->sc_sproto);
183    if (tdb == NULL)
184    {
185	DPRINTF(("%s: SA non-existant\n", ifp->if_xname));
186
187	/* Flush the queue */
188	for (;;)
189	{
190	    s = splimp();
191	    IF_DEQUEUE(&ifp->if_snd, m);
192	    splx(s);
193	    if (m == NULL)
194	      return;
195	    else
196	      m_freem(m);
197	}
198    }
199
200    /* See if we need to notify a key mgmt. daemon to setup SAs */
201    if (ntohl(enc->sc_spi) == SPI_LOCAL_USE)
202    {
203	/*
204	 * XXX Can't do this for now, as there's no way for
205	 * XXX key mgmt. to specify link-layer properties
206	 * XXX (e.g., encrypt everything on this interface)
207	 */
208#ifdef notyet
209	if (tdb->tdb_satype != SADB_X_SATYPE_BYPASS)
210	  pfkeyv2_acquire(tdb, 0); /* No point checking for errors */
211#endif
212
213	/* Flush the queue */
214	for (;;)
215	{
216	    s = splimp();
217	    IF_DEQUEUE(&ifp->if_snd, m);
218	    splx(s);
219	    if (m == NULL)
220	      return;
221	    else
222	      m_freem(m);
223	}
224
225	/* Unreachable */
226    }
227
228    /* IPsec-process all packets in the queue */
229    for (;;)
230    {
231	/* Get a packet from the queue */
232	s = splimp();
233	IF_DEQUEUE(&ifp->if_snd, m);
234	splx(s);
235
236	if (m == NULL) /* Empty queue */
237	  return;
238
239	/* First, we encapsulate in etherip */
240	err = etherip_output(m, tdb, &mp, 0, 0); /* Last 2 args not used */
241	if ((mp == NULL) || err)
242	{
243	    /* Just skip this frame */
244	    if (mp)
245	      m_freem(mp);
246	    continue;
247	}
248	else
249	{
250	    m = mp;
251	    mp = NULL;
252	}
253
254	protoflag = tdb->tdb_dst.sa.sa_family;
255
256	/* IPsec packet processing -- skip encapsulation */
257	err = ipsp_process_packet(m, &mp, tdb, &protoflag, 1);
258	if ((mp == NULL) || err)
259	{
260	    if (mp)
261	      m_freem(mp);
262	    continue;
263	}
264	else
265	{
266	    m = mp;
267	    mp = NULL;
268	}
269
270#ifdef INET
271	/* Send the packet on its way, no point checking for errors here */
272	if (protoflag == AF_INET)
273	  ip_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, NULL, NULL);
274#endif /* INET */
275
276#ifdef INET6
277	/* Send the packet on its way, no point checking for errors here */
278	if (protoflag == AF_INET6)
279	  ip6_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT,
280		     NULL, NULL);
281#endif /* INET6 */
282
283	/* XXX Should find a way to avoid bridging-loops, some mbuf flag ? */
284    }
285#endif /* IPSEC */
286}
287
288/*
289 * Shamelessly stolen from looutput()
290 */
291int
292encoutput(ifp, m, dst, rt)
293struct ifnet *ifp;
294register struct mbuf *m;
295struct sockaddr *dst;
296register struct rtentry *rt;
297{
298    register struct ifqueue *ifq = 0;
299    int s, isr;
300
301    if ((m->m_flags & M_PKTHDR) == 0)
302      panic("encoutput(): no HDR");
303
304    ifp->if_lastchange = time;
305    m->m_pkthdr.rcvif = ifp;
306
307    if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE))
308    {
309	m_freem(m);
310	return (rt->rt_flags & RTF_BLACKHOLE ? 0 :
311		rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
312    }
313
314    ifp->if_opackets++;
315    ifp->if_obytes += m->m_pkthdr.len;
316
317    switch (dst->sa_family)
318    {
319#ifdef INET
320	case AF_INET:
321	    ifq = &ipintrq;
322	    isr = NETISR_IP;
323	    break;
324#endif
325#ifdef INET6
326	case AF_INET6:
327	    ifq = &ip6intrq;
328	    isr = NETISR_IPV6;
329	    break;
330#endif
331#ifdef NS
332	case AF_NS:
333	    ifq = &nsintrq;
334	    isr = NETISR_NS;
335	    break;
336#endif
337#ifdef ISO
338	case AF_ISO:
339	    ifq = &clnlintrq;
340	    isr = NETISR_ISO;
341	    break;
342#endif
343	default:
344	    m_freem(m);
345	    return (EAFNOSUPPORT);
346    }
347
348    s = splimp();
349    if (IF_QFULL(ifq))
350    {
351	IF_DROP(ifq);
352	m_freem(m);
353	splx(s);
354	return (ENOBUFS);
355    }
356
357    IF_ENQUEUE(ifq, m);
358    schednetisr(isr);
359
360    /* Statistics */
361    ifp->if_ipackets++;
362    ifp->if_ibytes += m->m_pkthdr.len;
363    splx(s);
364    return (0);
365}
366
367/* ARGSUSED */
368void
369encrtrequest(cmd, rt, sa)
370int cmd;
371struct rtentry *rt;
372struct sockaddr *sa;
373{
374    if (rt)
375      rt->rt_rmx.rmx_mtu = ENCMTU;
376}
377
378/* ARGSUSED */
379int
380encioctl(ifp, cmd, data)
381register struct ifnet *ifp;
382u_long cmd;
383caddr_t data;
384{
385#ifdef IPSEC
386    struct enc_softc *enc = (struct enc_softc *) ifp->if_softc;
387    struct ifsa *ifsa = (struct ifsa *) data;
388    struct proc *prc = curproc;             /* XXX */
389    struct tdb *tdb;
390    int s, error;
391
392    switch (cmd)
393    {
394	case SIOCSIFADDR:
395	    return EOPNOTSUPP;
396
397	case SIOCGENCSA:
398	    ifsa->sa_spi = enc->sc_spi;
399	    ifsa->sa_proto = enc->sc_sproto;
400	    bcopy(&enc->sc_dst, &ifsa->sa_dst, enc->sc_dst.sa.sa_len);
401	    break;
402
403	case SIOCSENCCLEARSA:
404	    /* Check for superuser */
405	    if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
406	      break;
407
408	    if (ifsa->sa_proto == 0)
409	    {
410		/* Clear SA if requested */
411		if (enc->sc_sproto != 0)
412		{
413		    s = spltdb();
414		    tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto);
415		    if (tdb != NULL)
416		      tdb->tdb_interface = 0;
417		    splx(s);
418		}
419
420		bzero(&enc->sc_dst, sizeof(union sockaddr_union));
421		enc->sc_spi = 0;
422		enc->sc_sproto = 0;
423		break;
424	    }
425
426	    s = spltdb();
427	    tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
428	    if (tdb == NULL)
429	    {
430		splx(s);
431		error = ENOENT;
432		break;
433	    }
434
435	    tdb->tdb_interface = 0;
436	    splx(s);
437	    break;
438
439	case SIOCSENCSRCSA:
440	    /* Check for superuser */
441	    if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
442	      break;
443
444	    if (ifsa->sa_proto == 0)
445	    {
446		error = ENOENT;
447		break;
448	    }
449
450	    s = spltdb();
451	    tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
452	    if (tdb == NULL)
453	    {
454		splx(s);
455		error = ENOENT;
456		break;
457	    }
458
459	    /* Is it already bound ? */
460	    if (tdb->tdb_interface)
461	    {
462		splx(s);
463		error = EEXIST;
464		break;
465	    }
466
467	    tdb->tdb_interface = (caddr_t) ifp;
468	    splx(s);
469	    break;
470
471	case SIOCSENCDSTSA:
472	    /* Check for superuser */
473	    if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
474	      break;
475
476	    /* Check for pre-existing TDB */
477	    if (enc->sc_sproto != 0)
478	    {
479		error = EEXIST;
480		break;
481	    }
482
483	    s = spltdb();
484
485	    if (ifsa->sa_proto != 0)
486	    {
487		tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
488		if (tdb == NULL)
489		{
490		    splx(s);
491		    error = ENOENT;
492		    break;
493		}
494	    }
495	    else
496	    {
497		/* Clear SA if requested */
498		if (enc->sc_sproto != 0)
499		{
500		    tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto);
501		    if (tdb != NULL)
502		      tdb->tdb_interface = 0;
503		}
504
505		bzero(&enc->sc_dst, sizeof(enc->sc_dst));
506		enc->sc_spi = 0;
507		enc->sc_sproto = 0;
508
509		splx(s);
510		break;
511	    }
512
513#ifdef INET
514	    if ((ifsa->sa_dst.sa.sa_family == AF_INET) &&
515		(ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in)))
516	    {
517		splx(s);
518	    	error = EINVAL;
519		break;
520	    }
521#endif /* INET */
522
523#ifdef INET6
524	    if ((ifsa->sa_dst.sa.sa_family == AF_INET6) &&
525		(ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in6)))
526	    {
527		splx(s);
528		error = EINVAL;
529		break;
530	    }
531#endif /* INET6 */
532
533	    bcopy(&ifsa->sa_dst, &enc->sc_dst, ifsa->sa_dst.sa.sa_len);
534	    enc->sc_spi = ifsa->sa_spi;
535	    enc->sc_sproto = ifsa->sa_proto;
536	    tdb->tdb_interface = (caddr_t) ifp;
537
538	    splx(s);
539	    break;
540
541	default:
542	    error = EINVAL;
543	    break;
544    }
545
546    return (error);
547#else /* IPSEC */
548    return EOPNOTSUPP;
549#endif /* IPSEC */
550}
551