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