if_enc.c revision 1.30
1/*	$OpenBSD: if_enc.c,v 1.30 2000/04/18 06:41:23 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#ifdef notyet
211	/* XXX Currently unsupported */
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	/* Sanity check */
244	if ((m->m_flags & M_PKTHDR) == 0)
245	{
246	    m_freem(m);
247	    continue;
248	}
249
250	ifp->if_opackets++;
251	ifp->if_obytes += m->m_pkthdr.len;
252
253	m->m_pkthdr.rcvif = ifp;
254	mp = NULL;
255
256	/* Encapsulate in etherip or ip-in-ip, depending on interface flag */
257	if (ifp->if_flags & IFF_LINK0)
258	  err = ipip_output(m, tdb, &mp, 0, 0); /* Last 2 args not used */
259	else
260	  err = etherip_output(m, tdb, &mp, 0, 0); /* Last 2 args not used */
261	if ((mp == NULL) || err)
262	{
263	    /* Just skip this frame */
264            IF_DROP(&ifp->if_snd);
265	    if (mp)
266	      m_freem(mp);
267	    continue;
268	}
269	else
270	{
271	    m = mp;
272	    mp = NULL;
273	}
274
275#ifdef INET
276	/* Fix header checksum for IPv4 */
277        if (tdb->tdb_dst.sa.sa_family == AF_INET)
278	{
279	    struct ip *ip;
280
281	    ip = mtod(m, struct ip *);
282	    ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
283        }
284#endif
285
286	protoflag = tdb->tdb_dst.sa.sa_family;
287
288	/* IPsec packet processing -- skip encapsulation */
289	ipsp_process_packet(m, tdb, protoflag, 1);
290
291	/*
292	 * XXX
293	 * Should find a way to avoid bridging/routing-loops,
294	 * perhaps use some mbuf flag ?
295	 */
296    }
297#endif /* IPSEC */
298}
299
300int
301encoutput(ifp, m, dst, rt)
302struct ifnet *ifp;
303register struct mbuf *m;
304struct sockaddr *dst;
305register struct rtentry *rt;
306{
307    int s;
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    s = splimp();
323    if (IF_QFULL(&ifp->if_snd)) {
324	ifp->if_oerrors++;
325	m_freem(m);
326	splx(s);
327	return 0;
328    }
329
330    IF_ENQUEUE(&ifp->if_snd, m);
331    splx(s);
332
333    (ifp->if_start)(ifp);
334
335    return (0);
336}
337
338/* ARGSUSED */
339void
340encrtrequest(cmd, rt, sa)
341int cmd;
342struct rtentry *rt;
343struct sockaddr *sa;
344{
345    if (rt)
346      rt->rt_rmx.rmx_mtu = ENCMTU;
347}
348
349/* ARGSUSED */
350int
351encioctl(ifp, cmd, data)
352register struct ifnet *ifp;
353u_long cmd;
354caddr_t data;
355{
356#ifdef IPSEC
357    struct enc_softc *enc = (struct enc_softc *) ifp->if_softc;
358    struct ifsa *ifsa = (struct ifsa *) data;
359    struct proc *prc = curproc;             /* XXX */
360    struct tdb *tdb;
361    int s, error = 0;
362
363    /*
364     * enc0 does not allow binding of SAs, as it's used for all non-bound
365     * SAs.
366     */
367    if (ifp->if_softc == &encif[0])
368	return EOPNOTSUPP;
369
370    switch (cmd)
371    {
372	case SIOCSIFADDR:
373	case SIOCAIFADDR:
374	case SIOCSIFDSTADDR:
375	case SIOCSIFFLAGS:
376	    if (ifp->if_flags & IFF_UP)
377	      ifp->if_flags |= IFF_RUNNING;
378	    else
379	      ifp->if_flags &= ~IFF_RUNNING;
380	    break;
381
382	case SIOCGENCSA:
383	    ifsa->sa_spi = enc->sc_spi;
384	    ifsa->sa_proto = enc->sc_sproto;
385	    bcopy(&enc->sc_dst, &ifsa->sa_dst, enc->sc_dst.sa.sa_len);
386	    break;
387
388	case SIOCSENCCLEARSA:
389	    /* Check for superuser */
390	    if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
391	      break;
392
393	    if (ifsa->sa_proto == 0)
394	    {
395		/* Clear SA if requested */
396		if (enc->sc_sproto != 0)
397		{
398		    s = spltdb();
399		    tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto);
400		    if (tdb != NULL)
401		      tdb->tdb_interface = 0;
402		    splx(s);
403		}
404
405		bzero(&enc->sc_dst, sizeof(union sockaddr_union));
406		enc->sc_spi = 0;
407		enc->sc_sproto = 0;
408		break;
409	    }
410
411	    s = spltdb();
412	    tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
413	    if (tdb == NULL)
414	    {
415		splx(s);
416		error = ENOENT;
417		break;
418	    }
419
420	    tdb->tdb_interface = 0;
421	    splx(s);
422	    break;
423
424	case SIOCSENCSRCSA:
425	    /* Check for superuser */
426	    if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
427	      break;
428
429	    if (ifsa->sa_proto == 0)
430	    {
431		error = ENOENT;
432		break;
433	    }
434
435	    s = spltdb();
436	    tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
437	    if (tdb == NULL)
438	    {
439		splx(s);
440		error = ENOENT;
441		break;
442	    }
443
444	    /* Is it already bound ? */
445	    if (tdb->tdb_interface)
446	    {
447		splx(s);
448		error = EEXIST;
449		break;
450	    }
451
452	    tdb->tdb_interface = (caddr_t) ifp;
453	    splx(s);
454	    break;
455
456	case SIOCSENCDSTSA:
457	    /* Check for superuser */
458	    if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
459	      break;
460
461	    /* Check for pre-existing TDB */
462	    if (enc->sc_sproto != 0)
463	    {
464		error = EEXIST;
465		break;
466	    }
467
468	    s = spltdb();
469
470	    if (ifsa->sa_proto != 0)
471	    {
472		tdb = gettdb(ifsa->sa_spi, &ifsa->sa_dst, ifsa->sa_proto);
473		if (tdb == NULL)
474		{
475		    splx(s);
476		    error = ENOENT;
477		    break;
478		}
479	    }
480	    else
481	    {
482		/* Clear SA if requested */
483		if (enc->sc_sproto != 0)
484		{
485		    tdb = gettdb(enc->sc_spi, &enc->sc_dst, enc->sc_sproto);
486		    if (tdb != NULL)
487		      tdb->tdb_interface = 0;
488		}
489
490		bzero(&enc->sc_dst, sizeof(enc->sc_dst));
491		enc->sc_spi = 0;
492		enc->sc_sproto = 0;
493
494		splx(s);
495		break;
496	    }
497
498#ifdef INET
499	    if ((ifsa->sa_dst.sa.sa_family == AF_INET) &&
500		(ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in)))
501	    {
502		splx(s);
503	    	error = EINVAL;
504		break;
505	    }
506#endif /* INET */
507
508#ifdef INET6
509	    if ((ifsa->sa_dst.sa.sa_family == AF_INET6) &&
510		(ifsa->sa_dst.sa.sa_len != sizeof(struct sockaddr_in6)))
511	    {
512		splx(s);
513		error = EINVAL;
514		break;
515	    }
516#endif /* INET6 */
517
518	    bcopy(&ifsa->sa_dst, &enc->sc_dst, ifsa->sa_dst.sa.sa_len);
519	    enc->sc_spi = ifsa->sa_spi;
520	    enc->sc_sproto = ifsa->sa_proto;
521	    tdb->tdb_interface = (caddr_t) ifp;
522
523	    splx(s);
524	    break;
525
526	default:
527	    error = EINVAL;
528	    break;
529    }
530
531    return (error);
532#else /* IPSEC */
533    return EOPNOTSUPP;
534#endif /* IPSEC */
535}
536