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