if_enc.c revision 1.49
1/*	$OpenBSD: if_enc.c,v 1.49 2010/06/29 21:28:37 reyk Exp $	*/
2
3/*
4 * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include "enc.h"
20#include "bpfilter.h"
21
22#include <sys/param.h>
23#include <sys/systm.h>
24#include <sys/kernel.h>
25#include <sys/malloc.h>
26#include <sys/socket.h>
27#include <sys/sockio.h>
28#include <sys/mbuf.h>
29
30#include <net/if.h>
31#include <net/if_enc.h>
32#include <net/if_types.h>
33#include <net/route.h>
34#if NBPFILTER > 0
35#include <net/bpf.h>
36#endif
37
38TAILQ_HEAD(__enchead, enc_softc) enc_list;	/* all enc interfaces */
39struct ifnet			**enc_ifps;	/* rdomain-mapped enc ifs */
40u_int				 enc_max_id;
41
42void	 encattach(int);
43
44int	 enc_clone_create(struct if_clone *, int);
45int	 enc_clone_destroy(struct ifnet *);
46void	 enc_start(struct ifnet *);
47int	 enc_output(struct ifnet *, struct mbuf *, struct sockaddr *,
48	    struct rtentry *);
49int	 enc_ioctl(struct ifnet *, u_long, caddr_t);
50
51int	 enc_setif(struct ifnet *, u_int);
52void	 enc_unsetif(struct ifnet *);
53
54struct if_clone enc_cloner =
55    IF_CLONE_INITIALIZER("enc", enc_clone_create, enc_clone_destroy);
56
57void
58encattach(int count)
59{
60	TAILQ_INIT(&enc_list);
61
62	/* Create enc0 by default */
63	(void)enc_clone_create(&enc_cloner, 0);
64
65	if_clone_attach(&enc_cloner);
66}
67
68int
69enc_clone_create(struct if_clone *ifc, int unit)
70{
71	struct enc_softc	*sc;
72	struct ifnet		*ifp;
73
74	if ((sc = malloc(sizeof(struct enc_softc),
75	    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
76		return (ENOMEM);
77
78	sc->sc_unit = unit;
79
80	ifp = &sc->sc_if;
81	ifp->if_softc = sc;
82	ifp->if_type = IFT_ENC;
83	ifp->if_start = enc_start;
84	ifp->if_output = enc_output;
85	ifp->if_ioctl = enc_ioctl;
86	ifp->if_hdrlen = ENC_HDRLEN;
87
88	snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
89	    ifc->ifc_name, unit);
90
91	if_attach(ifp);
92	if_alloc_sadl(ifp);
93
94#if NBPFILTER > 0
95	bpfattach(&ifp->if_bpf, ifp, DLT_ENC, ENC_HDRLEN);
96#endif
97
98	if (enc_setif(ifp, 0) != 0) {
99		if_detach(ifp);
100		free(sc, M_DEVBUF);
101		return (-1);
102	}
103
104	TAILQ_INSERT_TAIL(&enc_list, sc, sc_entry);
105	return (0);
106}
107
108int
109enc_clone_destroy(struct ifnet *ifp)
110{
111	struct enc_softc	*sc = ifp->if_softc;
112	int			 s;
113
114	/* Protect users from removing enc0 */
115	if (sc->sc_unit == 0)
116		return (EPERM);
117
118	s = splnet();
119	TAILQ_REMOVE(&enc_list, sc, sc_entry);
120	enc_unsetif(ifp);
121	if_detach(ifp);
122	free(sc, M_DEVBUF);
123	splx(s);
124
125	return (0);
126}
127
128void
129enc_start(struct ifnet *ifp)
130{
131	struct mbuf	*m;
132
133	for (;;) {
134		IF_DROP(&ifp->if_snd);
135		IF_DEQUEUE(&ifp->if_snd, m);
136		if (m == NULL)
137			break;
138		m_freem(m);
139	}
140}
141
142int
143enc_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
144    struct rtentry *rt)
145{
146	m_freem(m);	/* drop packet */
147	return (0);
148}
149
150int
151enc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
152{
153	struct ifreq	*ifr = (struct ifreq *)data;
154	int		 error = 0;
155
156	switch (cmd) {
157	case SIOCAIFADDR:
158	case SIOCSIFADDR:
159	case SIOCSIFDSTADDR:
160	case SIOCSIFFLAGS:
161		if (ifp->if_flags & IFF_UP)
162			ifp->if_flags |= IFF_RUNNING;
163		else
164			ifp->if_flags &= ~IFF_RUNNING;
165		break;
166	case SIOCSIFRTABLEID:
167		if ((error = enc_setif(ifp, ifr->ifr_rdomainid)) != 0)
168			return (error);
169		/* FALLTHROUGH */
170	default:
171		return (ENOTTY);
172	}
173
174	return (0);
175}
176
177struct ifnet *
178enc_getif(u_int id)
179{
180	if (enc_ifps == NULL)
181		return (NULL);
182	else if (id > RT_TABLEID_MAX)
183		return (NULL);
184	else if (id > enc_max_id)
185		return (NULL);
186	return (enc_ifps[id]);
187}
188
189int
190enc_setif(struct ifnet *ifp, u_int id)
191{
192	struct ifnet	**new;
193	size_t		 newlen;
194
195	enc_unsetif(ifp);
196
197	/*
198	 * There can only be one default encif per rdomain -
199	 * Don't overwrite the existing enc iface that is stored
200	 * for this rdomain, so only the first enc interface that
201	 * was added for this rdomain becomes the default.
202	 */
203	if (enc_getif(id) != NULL)
204		return (0);
205
206	if (id > RT_TABLEID_MAX)
207		return (-1);
208
209	if (id == 0 || id > enc_max_id) {
210		newlen = sizeof(struct ifnet *) * (id + 1);
211
212		if ((new = malloc(newlen, M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
213			return (-1);
214		if (enc_ifps != NULL) {
215			memcpy(new, enc_ifps,
216			    sizeof(struct ifnet *) * (enc_max_id + 1));
217			free(enc_ifps, M_DEVBUF);
218		}
219		enc_ifps = new;
220		enc_max_id = id;
221	}
222
223	enc_ifps[id] = ifp;
224
225	/* Indicate that this interface is the rdomain default */
226	ifp->if_link_state = LINK_STATE_UP;
227
228	return (0);
229}
230
231void
232enc_unsetif(struct ifnet *ifp)
233{
234	u_int			 id = ifp->if_rdomain;
235	struct ifnet		*oifp;
236	struct enc_softc	*sc;
237
238	if ((oifp = enc_getif(id)) == NULL || oifp != ifp)
239		return;
240
241	/* Clear slot for this rdomain */
242	enc_ifps[id] = NULL;
243	ifp->if_link_state = LINK_STATE_UNKNOWN;
244
245	/*
246	 * Now find the next available encif to be the default interface
247	 * for this rdomain.
248	 */
249	TAILQ_FOREACH(sc, &enc_list, sc_entry) {
250		if (&sc->sc_if == ifp || sc->sc_if.if_rdomain != id)
251			continue;
252
253		enc_ifps[id] = &sc->sc_if;
254		sc->sc_if.if_link_state = LINK_STATE_UP;
255		break;
256	}
257}
258