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