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