xref: /openbsd-src/sys/net/if_mpe.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /* $OpenBSD: if_mpe.c,v 1.9 2008/05/08 09:52:36 pyr Exp $ */
2 
3 /*
4  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@spootnik.org>
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 #include "mpe.h"
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/mbuf.h>
23 #include <sys/proc.h>
24 #include <sys/socket.h>
25 #include <sys/sockio.h>
26 #include <sys/ioctl.h>
27 
28 #include <net/if.h>
29 #include <net/if_types.h>
30 #include <net/netisr.h>
31 #include <net/route.h>
32 
33 #ifdef	INET
34 #include <netinet/in.h>
35 #include <netinet/in_var.h>
36 #include <netinet/in_systm.h>
37 #include <netinet/ip.h>
38 #endif
39 
40 #ifdef INET6
41 #ifndef INET
42 #include <netinet/in.h>
43 #endif
44 #include <netinet6/nd6.h>
45 #endif /* INET6 */
46 
47 #include "bpfilter.h"
48 #if NBPFILTER > 0
49 #include <net/bpf.h>
50 #endif
51 
52 #include <netmpls/mpls.h>
53 
54 #ifdef MPLS_DEBUG
55 #define DPRINTF(x)    do { if (mpedebug) printf x ; } while (0)
56 #else
57 #define DPRINTF(x)
58 #endif
59 
60 void	mpeattach(int);
61 int	mpeoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
62 	    	       struct rtentry *);
63 int	mpeioctl(struct ifnet *, u_long, caddr_t);
64 void	mpestart(struct ifnet *);
65 int	mpe_clone_create(struct if_clone *, int);
66 int	mpe_clone_destroy(struct ifnet *);
67 
68 LIST_HEAD(, mpe_softc)	mpeif_list;
69 struct if_clone	mpe_cloner =
70     IF_CLONE_INITIALIZER("mpe", mpe_clone_create, mpe_clone_destroy);
71 
72 void
73 mpeattach(int nmpe)
74 {
75 	LIST_INIT(&mpeif_list);
76 	if_clone_attach(&mpe_cloner);
77 }
78 
79 int
80 mpe_clone_create(struct if_clone *ifc, int unit)
81 {
82 	struct ifnet 		*ifp;
83 	struct mpe_softc	*mpeif;
84 	int 			 s;
85 
86 	if ((mpeif = malloc(sizeof(*mpeif),
87 	    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
88 		return (ENOMEM);
89 
90 	mpeif->sc_shim.shim_label = MPLS_BOS_MASK | htonl(mpls_defttl);
91 	mpeif->sc_unit = unit;
92 	ifp = &mpeif->sc_if;
93 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "mpe%d", unit);
94 	ifp->if_flags = IFF_POINTOPOINT;
95 	ifp->if_softc = mpeif;
96 	ifp->if_mtu = MPE_MTU;
97 	ifp->if_ioctl = mpeioctl;
98 	ifp->if_output = mpeoutput;
99 	ifp->if_start = mpestart;
100 	ifp->if_type = IFT_MPLS;
101 	ifp->if_snd.ifq_maxlen = ifqmaxlen;
102 	ifp->if_hdrlen = MPE_HDRLEN;
103 	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
104 	IFQ_SET_READY(&ifp->if_snd);
105 	if_attach(ifp);
106 	if_alloc_sadl(ifp);
107 #if NBPFILTER > 0
108 	bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, MPE_HDRLEN);
109 #endif
110 
111 	s = splnet();
112 	LIST_INSERT_HEAD(&mpeif_list, mpeif, sc_list);
113 	splx(s);
114 
115 	return (0);
116 }
117 
118 int
119 mpe_clone_destroy(struct ifnet *ifp)
120 {
121 	struct mpe_softc	*mpeif = ifp->if_softc;
122 	int			 s;
123 
124 	s = splnet();
125 	LIST_REMOVE(mpeif, sc_list);
126 	splx(s);
127 
128 #if NBPFILTER > 0
129 	bpfdetach(ifp);
130 #endif
131 	if_detach(ifp);
132 	free(mpeif, M_DEVBUF);
133 	return (0);
134 }
135 
136 /*
137  * Start output on the mpe interface.
138  */
139 void
140 mpestart(struct ifnet *ifp)
141 {
142 	struct mbuf 		*m;
143 	struct mpe_softc	*ifm;
144 	struct shim_hdr		 shim;
145 	int			 s;
146 
147 	for (;;) {
148 		s = splnet();
149 		IFQ_DEQUEUE(&ifp->if_snd, m);
150 		splx(s);
151 
152 		if (m == NULL)
153 			return;
154 
155 #if NBPFILTER > 0
156 	if (ifp->if_bpf)
157 		bpf_mtap_af(ifp->if_bpf, AF_INET, m, BPF_DIRECTION_OUT);
158 #endif
159 		ifm = ifp->if_softc;
160 		shim.shim_label = ifm->sc_shim.shim_label;
161 		M_PREPEND(m, sizeof(shim), M_DONTWAIT);
162 		m_copyback(m, 0, sizeof(shim), (caddr_t)&shim);
163 		if (m == NULL) {
164 			ifp->if_ierrors++;
165 			continue;
166 		}
167 		m->m_pkthdr.rcvif = ifp;
168 		mpls_input(m);
169 	}
170 }
171 
172 int
173 mpeoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
174 	struct rtentry *rt)
175 {
176 	int	s;
177 	int	error;
178 
179 	error = 0;
180 	switch (dst->sa_family) {
181 	case AF_INET:
182 		break;
183 	case AF_MPLS:
184 		/*
185 		 * drop MPLS packets entering here. This is a hack to prevent
186 		 * loops because of misconfiguration.
187 		 */
188 		m_freem(m);
189 		error = ENETUNREACH;
190 		return (error);
191 	default:
192 		error = ENETDOWN;
193 		goto out;
194 	}
195 	s = splnet();
196 	IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error);
197 	if (error) {
198 		/* mbuf is already freed */
199 		splx(s);
200 		return (error);
201 	}
202 	if_start(ifp);
203 	splx(s);
204 out:
205 	if (error)
206 		ifp->if_oerrors++;
207 	return (error);
208 }
209 
210 /* ARGSUSED */
211 int
212 mpeioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
213 {
214 	int			 error;
215 	struct mpe_softc	*ifm;
216 	struct ifreq		*ifr;
217 	struct shim_hdr		 shim;
218 	u_int32_t		 ttl = htonl(mpls_defttl);
219 
220 	ifr = (struct ifreq *)data;
221 	error = 0;
222 	switch (cmd) {
223 	case SIOCSIFADDR:
224 		ifp->if_flags |= IFF_UP;
225 		break;
226 	case SIOCSIFFLAGS:
227 		if (ifp->if_flags & IFF_UP)
228 			ifp->if_flags |= IFF_RUNNING;
229 		else
230 			ifp->if_flags &= ~IFF_RUNNING;
231 		break;
232 	case SIOCSIFMTU:
233 		if (ifr->ifr_mtu < MPE_MTU_MIN ||
234 		    ifr->ifr_mtu > MPE_MTU_MAX)
235 			error = EINVAL;
236 		else
237 			ifp->if_mtu = ifr->ifr_mtu;
238 		break;
239 	case SIOCGETLABEL:
240 		ifm = ifp->if_softc;
241 		shim.shim_label =
242 		    ((ntohl(ifm->sc_shim.shim_label & MPLS_LABEL_MASK)) >>
243 		    MPLS_LABEL_OFFSET);
244 		error = copyout(&shim, ifr->ifr_data, sizeof(shim));
245 		break;
246 	case SIOCSETLABEL:
247 		ifm = ifp->if_softc;
248 		if ((error = copyin(ifr->ifr_data, &shim, sizeof(shim))))
249 			break;
250 		if (shim.shim_label > MPLS_LABEL_MAX) {
251 			error = EINVAL;
252 			break;
253 		}
254 		shim.shim_label = (htonl(shim.shim_label << MPLS_LABEL_OFFSET))
255 		    | MPLS_BOS_MASK | ttl;
256 		if (ifm->sc_shim.shim_label == shim.shim_label)
257 			break;
258 		LIST_FOREACH(ifm, &mpeif_list, sc_list) {
259 			if (ifm != ifp->if_softc &&
260 			    ifm->sc_shim.shim_label == shim.shim_label) {
261 				error = EEXIST;
262 				break;
263 			}
264 		}
265 		if (error)
266 			break;
267 		ifm = ifp->if_softc;
268 		ifm->sc_shim.shim_label = shim.shim_label;
269 		break;
270 	default:
271 		return (ENOTTY);
272 	}
273 
274 	return (error);
275 }
276 
277 void
278 mpe_input(struct mbuf *m, struct ifnet *ifp, struct sockaddr_mpls *smpls,
279     u_int32_t ttl)
280 {
281 	int		 s;
282 
283 	/* fixup ttl */
284 	/* label -> AF lookup */
285 
286 #if NBPFILTER > 0
287 	if (ifp->if_bpf)
288 		bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
289 #endif
290 	s = splnet();
291 	/*
292 	 * assume we only get fed ipv4 packets for now.
293 	 */
294 	IF_ENQUEUE(&ipintrq, m);
295 	schednetisr(NETISR_IP);
296 	splx(s);
297 }
298