if_enc.c revision 1.17
1/*	$OpenBSD: if_enc.c,v 1.17 2000/01/07 20:14:51 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    switch (cmd)
399    {
400	case SIOCSIFADDR:
401	    return EOPNOTSUPP;
402
403	case SIOCGENCSA:
404	    ifsa->sa_spi = enc->sc_spi;
405	    ifsa->sa_proto = enc->sc_sproto;
406	    bcopy(&enc->sc_dst, &ifsa->sa_dst, enc->sc_dst.sa.sa_len);
407	    break;
408
409	case SIOCSENCCLEARSA:
410	    /* Check for superuser */
411	    if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
412	      break;
413
414	    if (ifsa->sa_proto == 0)
415	    {
416		/* Clear SA if requested */
417		if (enc->sc_sproto != 0)
418		{
419		    s = spltdb();
420		    tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto);
421		    if (tdb != NULL)
422		      tdb->tdb_interface = 0;
423		    splx(s);
424		}
425
426		bzero(&enc->sc_dst, sizeof(union sockaddr_union));
427		enc->sc_spi = 0;
428		enc->sc_sproto = 0;
429		break;
430	    }
431
432	    s = spltdb();
433	    tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
434	    if (tdb == NULL)
435	    {
436		splx(s);
437		error = ENOENT;
438		break;
439	    }
440
441	    tdb->tdb_interface = 0;
442	    splx(s);
443	    break;
444
445	case SIOCSENCSRCSA:
446	    /* Check for superuser */
447	    if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
448	      break;
449
450	    if (ifsa->sa_proto == 0)
451	    {
452		error = ENOENT;
453		break;
454	    }
455
456	    s = spltdb();
457	    tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
458	    if (tdb == NULL)
459	    {
460		splx(s);
461		error = ENOENT;
462		break;
463	    }
464
465	    /* Is it already bound ? */
466	    if (tdb->tdb_interface)
467	    {
468		splx(s);
469		error = EEXIST;
470		break;
471	    }
472
473	    tdb->tdb_interface = (caddr_t) ifp;
474	    splx(s);
475	    break;
476
477	case SIOCSENCDSTSA:
478	    /* Check for superuser */
479	    if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
480	      break;
481
482	    /* Check for pre-existing TDB */
483	    if (enc->sc_sproto != 0)
484	    {
485		error = EEXIST;
486		break;
487	    }
488
489	    s = spltdb();
490
491	    if (ifsa->sa_proto != 0)
492	    {
493		tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
494		if (tdb == NULL)
495		{
496		    splx(s);
497		    error = ENOENT;
498		    break;
499		}
500	    }
501	    else
502	    {
503		/* Clear SA if requested */
504		if (enc->sc_sproto != 0)
505		{
506		    tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto);
507		    if (tdb != NULL)
508		      tdb->tdb_interface = 0;
509		}
510
511		bzero(&enc->sc_dst, sizeof(enc->sc_dst));
512		enc->sc_spi = 0;
513		enc->sc_sproto = 0;
514
515		splx(s);
516		break;
517	    }
518
519#ifdef INET
520	    if ((ifsa->sa_dst.sa.sa_family == AF_INET) &&
521		(ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in)))
522	    {
523		splx(s);
524	    	error = EINVAL;
525		break;
526	    }
527#endif /* INET */
528
529#ifdef INET6
530	    if ((ifsa->sa_dst.sa.sa_family == AF_INET6) &&
531		(ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in6)))
532	    {
533		splx(s);
534		error = EINVAL;
535		break;
536	    }
537#endif /* INET6 */
538
539	    bcopy(&ifsa->sa_dst, &enc->sc_dst, ifsa->sa_dst.sa.sa_len);
540	    enc->sc_spi = ifsa->sa_spi;
541	    enc->sc_sproto = ifsa->sa_proto;
542	    tdb->tdb_interface = (caddr_t) ifp;
543
544	    splx(s);
545	    break;
546
547	default:
548	    error = EINVAL;
549	    break;
550    }
551
552    return (error);
553#else /* IPSEC */
554    return EOPNOTSUPP;
555#endif /* IPSEC */
556}
557