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