xref: /openbsd-src/sys/net/if_mpe.c (revision f763167468dba5339ed4b14b7ecaca2a397ab0f6)
1 /* $OpenBSD: if_mpe.c,v 1.62 2017/08/14 16:14:02 reyk 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/socket.h>
24 #include <sys/sockio.h>
25 #include <sys/ioctl.h>
26 
27 #include <net/if.h>
28 #include <net/if_dl.h>
29 #include <net/if_var.h>
30 #include <net/if_types.h>
31 #include <net/netisr.h>
32 #include <net/route.h>
33 
34 #include <netinet/in.h>
35 #include <netinet/ip.h>
36 
37 #ifdef INET6
38 #include <netinet/ip6.h>
39 #endif /* INET6 */
40 
41 #include "bpfilter.h"
42 #if NBPFILTER > 0
43 #include <net/bpf.h>
44 #endif
45 
46 #include <netmpls/mpls.h>
47 
48 #ifdef MPLS_DEBUG
49 #define DPRINTF(x)    do { if (mpedebug) printf x ; } while (0)
50 #else
51 #define DPRINTF(x)
52 #endif
53 
54 void	mpeattach(int);
55 int	mpeoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
56 		       struct rtentry *);
57 int	mpeioctl(struct ifnet *, u_long, caddr_t);
58 void	mpestart(struct ifnet *);
59 int	mpe_clone_create(struct if_clone *, int);
60 int	mpe_clone_destroy(struct ifnet *);
61 
62 LIST_HEAD(, mpe_softc)	mpeif_list;
63 struct if_clone	mpe_cloner =
64     IF_CLONE_INITIALIZER("mpe", mpe_clone_create, mpe_clone_destroy);
65 
66 extern int	mpls_mapttl_ip;
67 #ifdef INET6
68 extern int	mpls_mapttl_ip6;
69 #endif
70 
71 void
72 mpeattach(int nmpe)
73 {
74 	LIST_INIT(&mpeif_list);
75 	if_clone_attach(&mpe_cloner);
76 }
77 
78 int
79 mpe_clone_create(struct if_clone *ifc, int unit)
80 {
81 	struct ifnet		*ifp;
82 	struct mpe_softc	*mpeif;
83 
84 	if ((mpeif = malloc(sizeof(*mpeif),
85 	    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
86 		return (ENOMEM);
87 
88 	mpeif->sc_unit = unit;
89 	ifp = &mpeif->sc_if;
90 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "mpe%d", unit);
91 	ifp->if_flags = IFF_POINTOPOINT;
92 	ifp->if_xflags = IFXF_CLONED;
93 	ifp->if_softc = mpeif;
94 	ifp->if_mtu = MPE_MTU;
95 	ifp->if_ioctl = mpeioctl;
96 	ifp->if_output = mpeoutput;
97 	ifp->if_start = mpestart;
98 	ifp->if_type = IFT_MPLS;
99 	ifp->if_hdrlen = MPE_HDRLEN;
100 	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
101 	if_attach(ifp);
102 	if_alloc_sadl(ifp);
103 #if NBPFILTER > 0
104 	bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(u_int32_t));
105 #endif
106 
107 	mpeif->sc_ifa.ifa_ifp = ifp;
108 	mpeif->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
109 	mpeif->sc_smpls.smpls_len = sizeof(mpeif->sc_smpls);
110 	mpeif->sc_smpls.smpls_family = AF_MPLS;
111 
112 	LIST_INSERT_HEAD(&mpeif_list, mpeif, sc_list);
113 
114 	return (0);
115 }
116 
117 int
118 mpe_clone_destroy(struct ifnet *ifp)
119 {
120 	struct mpe_softc	*mpeif = ifp->if_softc;
121 
122 	LIST_REMOVE(mpeif, sc_list);
123 
124 	if (mpeif->sc_smpls.smpls_label) {
125 		rt_ifa_del(&mpeif->sc_ifa, RTF_MPLS,
126 		    smplstosa(&mpeif->sc_smpls));
127 	}
128 
129 	if_detach(ifp);
130 	free(mpeif, M_DEVBUF, sizeof *mpeif);
131 	return (0);
132 }
133 
134 struct sockaddr_storage	 mpedst;
135 /*
136  * Start output on the mpe interface.
137  */
138 void
139 mpestart(struct ifnet *ifp0)
140 {
141 	struct mbuf		*m;
142 	struct sockaddr		*sa = sstosa(&mpedst);
143 	sa_family_t		 af;
144 	struct rtentry		*rt;
145 	struct ifnet		*ifp;
146 
147 	for (;;) {
148 		IFQ_DEQUEUE(&ifp0->if_snd, m);
149 		if (m == NULL)
150 			return;
151 
152 		af = *mtod(m, sa_family_t *);
153 		m_adj(m, sizeof(af));
154 		switch (af) {
155 		case AF_INET:
156 			bzero(sa, sizeof(struct sockaddr_in));
157 			satosin(sa)->sin_family = af;
158 			satosin(sa)->sin_len = sizeof(struct sockaddr_in);
159 			bcopy(mtod(m, caddr_t), &satosin(sa)->sin_addr,
160 			    sizeof(in_addr_t));
161 			m_adj(m, sizeof(in_addr_t));
162 			break;
163 		default:
164 			m_freem(m);
165 			continue;
166 		}
167 
168 		rt = rtalloc(sa, RT_RESOLVE, 0);
169 		if (!rtisvalid(rt)) {
170 			m_freem(m);
171 			rtfree(rt);
172 			continue;
173 		}
174 
175 		ifp = if_get(rt->rt_ifidx);
176 		if (ifp == NULL) {
177 			m_freem(m);
178 			rtfree(rt);
179 			continue;
180 		}
181 
182 #if NBPFILTER > 0
183 		if (ifp0->if_bpf) {
184 			/* remove MPLS label before passing packet to bpf */
185 			m->m_data += sizeof(struct shim_hdr);
186 			m->m_len -= sizeof(struct shim_hdr);
187 			m->m_pkthdr.len -= sizeof(struct shim_hdr);
188 			bpf_mtap_af(ifp0->if_bpf, af, m, BPF_DIRECTION_OUT);
189 			m->m_data -= sizeof(struct shim_hdr);
190 			m->m_len += sizeof(struct shim_hdr);
191 			m->m_pkthdr.len += sizeof(struct shim_hdr);
192 		}
193 #endif
194 		/* XXX lie, but mpls_output will only look at sa_family */
195 		sa->sa_family = AF_MPLS;
196 
197 		mpls_output(ifp, m, sa, rt);
198 		if_put(ifp);
199 		rtfree(rt);
200 	}
201 }
202 
203 int
204 mpeoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
205 	struct rtentry *rt)
206 {
207 	struct shim_hdr	shim;
208 	int		error;
209 	int		off;
210 	u_int8_t	op = 0;
211 
212 #ifdef DIAGNOSTIC
213 	if (ifp->if_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid)) {
214 		printf("%s: trying to send packet on wrong domain. "
215 		    "if %d vs. mbuf %d\n", ifp->if_xname,
216 		    ifp->if_rdomain, rtable_l2(m->m_pkthdr.ph_rtableid));
217 	}
218 #endif
219 	m->m_pkthdr.ph_ifidx = ifp->if_index;
220 	/* XXX assumes MPLS is always in rdomain 0 */
221 	m->m_pkthdr.ph_rtableid = 0;
222 
223 	error = 0;
224 	switch (dst->sa_family) {
225 	case AF_INET:
226 		if (rt && rt->rt_flags & RTF_MPLS) {
227 			shim.shim_label =
228 			    ((struct rt_mpls *)rt->rt_llinfo)->mpls_label;
229 			shim.shim_label |= MPLS_BOS_MASK;
230 			op =  ((struct rt_mpls *)rt->rt_llinfo)->mpls_operation;
231 		}
232 		if (op != MPLS_OP_PUSH) {
233 			m_freem(m);
234 			error = ENETUNREACH;
235 			goto out;
236 		}
237 		if (mpls_mapttl_ip) {
238 			struct ip	*ip;
239 			ip = mtod(m, struct ip *);
240 			shim.shim_label |= htonl(ip->ip_ttl) & MPLS_TTL_MASK;
241 		} else
242 			shim.shim_label |= htonl(mpls_defttl) & MPLS_TTL_MASK;
243 		off = sizeof(sa_family_t) + sizeof(in_addr_t);
244 		M_PREPEND(m, sizeof(shim) + off, M_DONTWAIT);
245 		if (m == NULL) {
246 			error = ENOBUFS;
247 			goto out;
248 		}
249 		*mtod(m, sa_family_t *) = AF_INET;
250 		m_copyback(m, sizeof(sa_family_t), sizeof(in_addr_t),
251 		    (caddr_t)&((satosin(dst)->sin_addr)), M_NOWAIT);
252 		break;
253 	default:
254 		m_freem(m);
255 		error = EPFNOSUPPORT;
256 		goto out;
257 	}
258 
259 	m_copyback(m, off, sizeof(shim), (caddr_t)&shim, M_NOWAIT);
260 
261 	error = if_enqueue(ifp, m);
262 out:
263 	if (error)
264 		ifp->if_oerrors++;
265 	return (error);
266 }
267 
268 int
269 mpeioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
270 {
271 	struct mpe_softc	*ifm;
272 	struct ifreq		*ifr;
273 	struct shim_hdr		 shim;
274 	int			 error = 0;
275 
276 	ifr = (struct ifreq *)data;
277 	switch (cmd) {
278 	case SIOCSIFADDR:
279 		if (!ISSET(ifp->if_flags, IFF_UP))
280 			if_up(ifp);
281 		break;
282 	case SIOCSIFFLAGS:
283 		if (ifp->if_flags & IFF_UP)
284 			ifp->if_flags |= IFF_RUNNING;
285 		else
286 			ifp->if_flags &= ~IFF_RUNNING;
287 		break;
288 	case SIOCSIFMTU:
289 		if (ifr->ifr_mtu < MPE_MTU_MIN ||
290 		    ifr->ifr_mtu > MPE_MTU_MAX)
291 			error = EINVAL;
292 		else
293 			ifp->if_mtu = ifr->ifr_mtu;
294 		break;
295 	case SIOCGETLABEL:
296 		ifm = ifp->if_softc;
297 		shim.shim_label =
298 		    ((ntohl(ifm->sc_smpls.smpls_label & MPLS_LABEL_MASK)) >>
299 		    MPLS_LABEL_OFFSET);
300 		error = copyout(&shim, ifr->ifr_data, sizeof(shim));
301 		break;
302 	case SIOCSETLABEL:
303 		ifm = ifp->if_softc;
304 		if ((error = copyin(ifr->ifr_data, &shim, sizeof(shim))))
305 			break;
306 		if (shim.shim_label > MPLS_LABEL_MAX ||
307 		    shim.shim_label <= MPLS_LABEL_RESERVED_MAX) {
308 			error = EINVAL;
309 			break;
310 		}
311 		shim.shim_label = htonl(shim.shim_label << MPLS_LABEL_OFFSET);
312 		if (ifm->sc_smpls.smpls_label == shim.shim_label)
313 			break;
314 		LIST_FOREACH(ifm, &mpeif_list, sc_list) {
315 			if (ifm != ifp->if_softc &&
316 			    ifm->sc_smpls.smpls_label == shim.shim_label) {
317 				error = EEXIST;
318 				break;
319 			}
320 		}
321 		if (error)
322 			break;
323 		ifm = ifp->if_softc;
324 		if (ifm->sc_smpls.smpls_label) {
325 			/* remove old MPLS route */
326 			rt_ifa_del(&ifm->sc_ifa, RTF_MPLS,
327 			    smplstosa(&ifm->sc_smpls));
328 		}
329 		/* add new MPLS route */
330 		ifm->sc_smpls.smpls_label = shim.shim_label;
331 		error = rt_ifa_add(&ifm->sc_ifa, RTF_MPLS,
332 		    smplstosa(&ifm->sc_smpls));
333 		if (error) {
334 			ifm->sc_smpls.smpls_label = 0;
335 			break;
336 		}
337 		break;
338 	case SIOCSIFRDOMAIN:
339 		/* must readd the MPLS "route" for our label */
340 		ifm = ifp->if_softc;
341 		if (ifr->ifr_rdomainid != ifp->if_rdomain) {
342 			if (ifm->sc_smpls.smpls_label) {
343 				rt_ifa_add(&ifm->sc_ifa, RTF_MPLS,
344 				    smplstosa(&ifm->sc_smpls));
345 			}
346 		}
347 		/* return with ENOTTY so that the parent handler finishes */
348 		return (ENOTTY);
349 	default:
350 		return (ENOTTY);
351 	}
352 
353 	return (error);
354 }
355 
356 void
357 mpe_input(struct mbuf *m, struct ifnet *ifp, struct sockaddr_mpls *smpls,
358     u_int8_t ttl)
359 {
360 	struct ip	*ip;
361 	int		 hlen;
362 
363 	/* label -> AF lookup */
364 
365 	if (mpls_mapttl_ip) {
366 		if (m->m_len < sizeof (struct ip) &&
367 		    (m = m_pullup(m, sizeof(struct ip))) == NULL)
368 			return;
369 		ip = mtod(m, struct ip *);
370 		hlen = ip->ip_hl << 2;
371 		if (m->m_len < hlen) {
372 			if ((m = m_pullup(m, hlen)) == NULL)
373 				return;
374 			ip = mtod(m, struct ip *);
375 		}
376 
377 		if (in_cksum(m, hlen) != 0) {
378 			m_freem(m);
379 			return;
380 		}
381 
382 		/* set IP ttl from MPLS ttl */
383 		ip->ip_ttl = ttl;
384 
385 		/* recalculate checksum */
386 		ip->ip_sum = 0;
387 		ip->ip_sum = in_cksum(m, hlen);
388 	}
389 
390 	/* new receive if and move into correct rtable */
391 	m->m_pkthdr.ph_ifidx = ifp->if_index;
392 	m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
393 
394 #if NBPFILTER > 0
395 	if (ifp->if_bpf)
396 		bpf_mtap_af(ifp->if_bpf, AF_INET, m, BPF_DIRECTION_IN);
397 #endif
398 
399 	ipv4_input(ifp, m);
400 }
401 
402 #ifdef INET6
403 void
404 mpe_input6(struct mbuf *m, struct ifnet *ifp, struct sockaddr_mpls *smpls,
405     u_int8_t ttl)
406 {
407 	struct ip6_hdr *ip6hdr;
408 
409 	/* label -> AF lookup */
410 
411 	if (mpls_mapttl_ip6) {
412 		if (m->m_len < sizeof (struct ip6_hdr) &&
413 		    (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL)
414 			return;
415 
416 		ip6hdr = mtod(m, struct ip6_hdr *);
417 
418 		/* set IPv6 ttl from MPLS ttl */
419 		ip6hdr->ip6_hlim = ttl;
420 	}
421 
422 	/* new receive if and move into correct rtable */
423 	m->m_pkthdr.ph_ifidx = ifp->if_index;
424 	m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
425 
426 #if NBPFILTER > 0
427 	if (ifp->if_bpf)
428 		bpf_mtap_af(ifp->if_bpf, AF_INET6, m, BPF_DIRECTION_IN);
429 #endif
430 
431 	ipv6_input(ifp, m);
432 }
433 #endif	/* INET6 */
434