xref: /openbsd-src/sys/net/if_mpe.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /* $OpenBSD: if_mpe.c,v 1.15 2009/01/28 22:18:44 michele 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 #include <netinet/ip6.h>
42 #ifndef INET
43 #include <netinet/in.h>
44 #endif
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 extern int	mpls_mapttl_ip;
73 extern int	mpls_mapttl_ip6;
74 
75 void
76 mpeattach(int nmpe)
77 {
78 	LIST_INIT(&mpeif_list);
79 	if_clone_attach(&mpe_cloner);
80 }
81 
82 int
83 mpe_clone_create(struct if_clone *ifc, int unit)
84 {
85 	struct ifnet 		*ifp;
86 	struct mpe_softc	*mpeif;
87 	int 			 s;
88 
89 	if ((mpeif = malloc(sizeof(*mpeif),
90 	    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
91 		return (ENOMEM);
92 
93 	mpeif->sc_shim.shim_label = MPLS_BOS_MASK | htonl(mpls_defttl);
94 	mpeif->sc_unit = unit;
95 	ifp = &mpeif->sc_if;
96 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "mpe%d", unit);
97 	ifp->if_flags = IFF_POINTOPOINT;
98 	ifp->if_softc = mpeif;
99 	ifp->if_mtu = MPE_MTU;
100 	ifp->if_ioctl = mpeioctl;
101 	ifp->if_output = mpeoutput;
102 	ifp->if_start = mpestart;
103 	ifp->if_type = IFT_MPLS;
104 	ifp->if_snd.ifq_maxlen = ifqmaxlen;
105 	ifp->if_hdrlen = MPE_HDRLEN;
106 	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
107 	IFQ_SET_READY(&ifp->if_snd);
108 	if_attach(ifp);
109 	if_alloc_sadl(ifp);
110 #if NBPFILTER > 0
111 	bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, MPE_HDRLEN);
112 #endif
113 
114 	s = splnet();
115 	LIST_INSERT_HEAD(&mpeif_list, mpeif, sc_list);
116 	splx(s);
117 
118 	return (0);
119 }
120 
121 int
122 mpe_clone_destroy(struct ifnet *ifp)
123 {
124 	struct mpe_softc	*mpeif = ifp->if_softc;
125 	int			 s;
126 
127 	s = splnet();
128 	LIST_REMOVE(mpeif, sc_list);
129 	splx(s);
130 
131 #if NBPFILTER > 0
132 	bpfdetach(ifp);
133 #endif
134 	if_detach(ifp);
135 	free(mpeif, M_DEVBUF);
136 	return (0);
137 }
138 
139 /*
140  * Start output on the mpe interface.
141  */
142 void
143 mpestart(struct ifnet *ifp)
144 {
145 	struct mbuf 		*m;
146 	struct mpe_softc	*ifm;
147 	struct shim_hdr		 shim;
148 	int			 s;
149 
150 	for (;;) {
151 		s = splnet();
152 		IFQ_DEQUEUE(&ifp->if_snd, m);
153 		splx(s);
154 
155 		if (m == NULL)
156 			return;
157 
158 #if NBPFILTER > 0
159 		if (ifp->if_bpf)
160 			bpf_mtap_af(ifp->if_bpf, AF_INET, m, BPF_DIRECTION_OUT);
161 #endif
162 		ifm = ifp->if_softc;
163 		shim.shim_label = ifm->sc_shim.shim_label;
164 		M_PREPEND(m, sizeof(shim), M_DONTWAIT);
165 		m_copyback(m, 0, sizeof(shim), (caddr_t)&shim);
166 		if (m == NULL) {
167 			ifp->if_ierrors++;
168 			continue;
169 		}
170 		m->m_pkthdr.rcvif = ifp;
171 		mpls_output(m, NULL);
172 	}
173 }
174 
175 int
176 mpeoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
177 	struct rtentry *rt)
178 {
179 	int	s;
180 	int	error;
181 
182 	error = 0;
183 	switch (dst->sa_family) {
184 	case AF_INET:
185 		break;
186 	case AF_MPLS:
187 		/*
188 		 * drop MPLS packets entering here. This is a hack to prevent
189 		 * loops because of misconfiguration.
190 		 */
191 		m_freem(m);
192 		error = ENETUNREACH;
193 		return (error);
194 	default:
195 		error = ENETDOWN;
196 		goto out;
197 	}
198 	s = splnet();
199 	IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error);
200 	if (error) {
201 		/* mbuf is already freed */
202 		splx(s);
203 		return (error);
204 	}
205 	if_start(ifp);
206 	splx(s);
207 out:
208 	if (error)
209 		ifp->if_oerrors++;
210 	return (error);
211 }
212 
213 /* ARGSUSED */
214 int
215 mpeioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
216 {
217 	int			 error;
218 	struct mpe_softc	*ifm;
219 	struct ifreq		*ifr;
220 	struct shim_hdr		 shim;
221 	u_int32_t		 ttl = htonl(mpls_defttl);
222 
223 	ifr = (struct ifreq *)data;
224 	error = 0;
225 	switch (cmd) {
226 	case SIOCSIFADDR:
227 		ifp->if_flags |= IFF_UP;
228 		break;
229 	case SIOCSIFFLAGS:
230 		if (ifp->if_flags & IFF_UP)
231 			ifp->if_flags |= IFF_RUNNING;
232 		else
233 			ifp->if_flags &= ~IFF_RUNNING;
234 		break;
235 	case SIOCSIFMTU:
236 		if (ifr->ifr_mtu < MPE_MTU_MIN ||
237 		    ifr->ifr_mtu > MPE_MTU_MAX)
238 			error = EINVAL;
239 		else
240 			ifp->if_mtu = ifr->ifr_mtu;
241 		break;
242 	case SIOCGETLABEL:
243 		ifm = ifp->if_softc;
244 		shim.shim_label =
245 		    ((ntohl(ifm->sc_shim.shim_label & MPLS_LABEL_MASK)) >>
246 		    MPLS_LABEL_OFFSET);
247 		error = copyout(&shim, ifr->ifr_data, sizeof(shim));
248 		break;
249 	case SIOCSETLABEL:
250 		ifm = ifp->if_softc;
251 		if ((error = copyin(ifr->ifr_data, &shim, sizeof(shim))))
252 			break;
253 		if (shim.shim_label > MPLS_LABEL_MAX ||
254 		    shim.shim_label <= MPLS_LABEL_RESERVED_MAX) {
255 			error = EINVAL;
256 			break;
257 		}
258 		shim.shim_label = (htonl(shim.shim_label << MPLS_LABEL_OFFSET))
259 		    | MPLS_BOS_MASK | ttl;
260 		if (ifm->sc_shim.shim_label == shim.shim_label)
261 			break;
262 		LIST_FOREACH(ifm, &mpeif_list, sc_list) {
263 			if (ifm != ifp->if_softc &&
264 			    ifm->sc_shim.shim_label == shim.shim_label) {
265 				error = EEXIST;
266 				break;
267 			}
268 		}
269 		if (error)
270 			break;
271 		ifm = ifp->if_softc;
272 		ifm->sc_shim.shim_label = shim.shim_label;
273 		break;
274 	default:
275 		return (ENOTTY);
276 	}
277 
278 	return (error);
279 }
280 
281 void
282 mpe_input(struct mbuf *m, struct ifnet *ifp, struct sockaddr_mpls *smpls,
283     u_int8_t ttl)
284 {
285 	struct ip	*ip;
286 	int		 s, hlen;
287 
288 	/* label -> AF lookup */
289 
290 	if (mpls_mapttl_ip) {
291 		if (m->m_len < sizeof (struct ip) &&
292 		    (m = m_pullup(m, sizeof(struct ip))) == NULL)
293 			return;
294 		ip = mtod(m, struct ip *);
295 		hlen = ip->ip_hl << 2;
296 		if (m->m_len < hlen) {
297 			if ((m = m_pullup(m, hlen)) == NULL)
298 				return;
299 			ip = mtod(m, struct ip *);
300 		}
301 
302 		if (in_cksum(m, hlen) != 0) {
303 			m_free(m);
304 			return;
305 		}
306 
307 		/* set IP ttl from MPLS ttl */
308 		ip->ip_ttl = ttl;
309 
310 		/* recalculate checksum */
311 		ip->ip_sum = 0;
312 		ip->ip_sum = in_cksum(m, hlen);
313 	}
314 
315 #if NBPFILTER > 0
316 	if (ifp && ifp->if_bpf)
317 		bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
318 #endif
319 	s = splnet();
320 	IF_ENQUEUE(&ipintrq, m);
321 	schednetisr(NETISR_IP);
322 	splx(s);
323 }
324 
325 void
326 mpe_input6(struct mbuf *m, struct ifnet *ifp, struct sockaddr_mpls *smpls,
327     u_int8_t ttl)
328 {
329 	struct ip6_hdr *ip6hdr;
330 	int s;
331 
332 	/* label -> AF lookup */
333 
334 	if (mpls_mapttl_ip6) {
335 		if (m->m_len < sizeof (struct ip6_hdr) &&
336 		    (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL)
337 			return;
338 
339 		ip6hdr = mtod(m, struct ip6_hdr *);
340 
341 		/* set IPv6 ttl from MPLS ttl */
342 		ip6hdr->ip6_hlim = ttl;
343 	}
344 
345 #if NBPFILTER > 0
346 	if (ifp && ifp->if_bpf)
347 		bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
348 #endif
349 	s = splnet();
350 	IF_ENQUEUE(&ip6intrq, m);
351 	schednetisr(NETISR_IPV6);
352 	splx(s);
353 }
354