xref: /netbsd-src/sys/net/if_mpls.c (revision 63f0d21cf2476e1e881ab1de3b527f058bff3be2)
1*63f0d21cSthorpej /*	$NetBSD: if_mpls.c,v 1.41 2022/09/03 20:29:31 thorpej Exp $ */
2826653c1Skefren 
3826653c1Skefren /*
4826653c1Skefren  * Copyright (c) 2010 The NetBSD Foundation, Inc.
5826653c1Skefren  * All rights reserved.
6826653c1Skefren  *
7826653c1Skefren  * This code is derived from software contributed to The NetBSD Foundation
8826653c1Skefren  * by Mihai Chelaru <kefren@NetBSD.org>
9826653c1Skefren  *
10826653c1Skefren  * Redistribution and use in source and binary forms, with or without
11826653c1Skefren  * modification, are permitted provided that the following conditions
12826653c1Skefren  * are met:
13826653c1Skefren  * 1. Redistributions of source code must retain the above copyright
14826653c1Skefren  *    notice, this list of conditions and the following disclaimer.
15826653c1Skefren  * 2. Redistributions in binary form must reproduce the above copyright
16826653c1Skefren  *    notice, this list of conditions and the following disclaimer in the
17826653c1Skefren  *    documentation and/or other materials provided with the distribution.
18826653c1Skefren  *
19826653c1Skefren  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20826653c1Skefren  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21826653c1Skefren  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22826653c1Skefren  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23826653c1Skefren  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24826653c1Skefren  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25826653c1Skefren  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26826653c1Skefren  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27826653c1Skefren  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28826653c1Skefren  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29826653c1Skefren  * POSSIBILITY OF SUCH DAMAGE.
30826653c1Skefren  */
31826653c1Skefren 
32826653c1Skefren #include <sys/cdefs.h>
33*63f0d21cSthorpej __KERNEL_RCSID(0, "$NetBSD: if_mpls.c,v 1.41 2022/09/03 20:29:31 thorpej Exp $");
34826653c1Skefren 
351c4a50f1Spooka #ifdef _KERNEL_OPT
36826653c1Skefren #include "opt_inet.h"
37826653c1Skefren #include "opt_mpls.h"
381c4a50f1Spooka #endif
39826653c1Skefren 
40826653c1Skefren #include <sys/param.h>
41826653c1Skefren 
42826653c1Skefren #include <sys/errno.h>
43826653c1Skefren #include <sys/malloc.h>
44826653c1Skefren #include <sys/mbuf.h>
45826653c1Skefren #include <sys/sysctl.h>
46826653c1Skefren 
47826653c1Skefren #include <net/bpf.h>
48826653c1Skefren #include <net/if.h>
49826653c1Skefren #include <net/if_types.h>
50826653c1Skefren #include <net/route.h>
511d8e08d4Schristos #include <sys/device.h>
521d8e08d4Schristos #include <sys/module.h>
531d8e08d4Schristos #include <sys/atomic.h>
54826653c1Skefren 
55826653c1Skefren #ifdef INET
56826653c1Skefren #include <netinet/in.h>
57826653c1Skefren #include <netinet/in_systm.h>
58826653c1Skefren #include <netinet/in_var.h>
59826653c1Skefren #include <netinet/ip.h>
606ea8c2e6Sozaki-r #include <netinet/ip_var.h>
61826653c1Skefren #endif
62826653c1Skefren 
63826653c1Skefren #ifdef INET6
64826653c1Skefren #include <netinet/ip6.h>
65826653c1Skefren #include <netinet6/in6_var.h>
66826653c1Skefren #include <netinet6/ip6_var.h>
67826653c1Skefren #endif
68826653c1Skefren 
69826653c1Skefren #include <netmpls/mpls.h>
70826653c1Skefren #include <netmpls/mpls_var.h>
71826653c1Skefren 
72826653c1Skefren #include "if_mpls.h"
73826653c1Skefren 
74e7ae23fdSchristos #include "ioconf.h"
75e7ae23fdSchristos 
76826653c1Skefren static int mpls_clone_create(struct if_clone *, int);
77826653c1Skefren static int mpls_clone_destroy(struct ifnet *);
78826653c1Skefren 
79826653c1Skefren static struct if_clone mpls_if_cloner =
80826653c1Skefren 	IF_CLONE_INITIALIZER("mpls", mpls_clone_create, mpls_clone_destroy);
81826653c1Skefren 
82826653c1Skefren static void mpls_input(struct ifnet *, struct mbuf *);
83826653c1Skefren static int mpls_output(struct ifnet *, struct mbuf *, const struct sockaddr *,
842cf7873bSozaki-r     const struct rtentry *);
85826653c1Skefren static int mpls_ioctl(struct ifnet *, u_long, void *);
862cf7873bSozaki-r static int mpls_send_frame(struct mbuf *, struct ifnet *,
872cf7873bSozaki-r     const struct rtentry *);
88826653c1Skefren static int mpls_lse(struct mbuf *);
89826653c1Skefren 
90826653c1Skefren #ifdef INET
9155408dcfSmaxv static struct mbuf *mpls_unlabel_inet(struct mbuf *, int *error);
92bd098fd9Skefren static struct mbuf *mpls_label_inet(struct mbuf *, union mpls_shim *, uint);
93826653c1Skefren #endif
94826653c1Skefren 
95826653c1Skefren #ifdef INET6
9655408dcfSmaxv static struct mbuf *mpls_unlabel_inet6(struct mbuf *, int *error);
97bd098fd9Skefren static struct mbuf *mpls_label_inet6(struct mbuf *, union mpls_shim *, uint);
98826653c1Skefren #endif
99826653c1Skefren 
100826653c1Skefren static struct mbuf *mpls_prepend_shim(struct mbuf *, union mpls_shim *);
101826653c1Skefren 
102826653c1Skefren extern int mpls_defttl, mpls_mapttl_inet, mpls_mapttl_inet6, mpls_icmp_respond,
103d27b133dSrtr     mpls_forwarding, mpls_frame_accept, mpls_mapprec_inet, mpls_mapclass_inet6,
104326bf6faSkefren     mpls_rfc4182;
105826653c1Skefren 
1061d8e08d4Schristos static u_int mpls_count;
107*63f0d21cSthorpej 
108*63f0d21cSthorpej void	mplsattach(int);
109*63f0d21cSthorpej 
110826653c1Skefren /* ARGSUSED */
111826653c1Skefren void
mplsattach(int count)1121d8e08d4Schristos mplsattach(int count)
1131d8e08d4Schristos {
1141d8e08d4Schristos 	/*
1151d8e08d4Schristos 	 * Nothing to do here, initialization is handled by the
1161d8e08d4Schristos 	 * module initialization code in mplsinit() below).
1171d8e08d4Schristos 	 */
1181d8e08d4Schristos }
1191d8e08d4Schristos 
1201d8e08d4Schristos static void
mplsinit(void)1211d8e08d4Schristos mplsinit(void)
122826653c1Skefren {
123826653c1Skefren 	if_clone_attach(&mpls_if_cloner);
124826653c1Skefren }
125826653c1Skefren 
126826653c1Skefren static int
mplsdetach(void)1271d8e08d4Schristos mplsdetach(void)
1281d8e08d4Schristos {
1291d8e08d4Schristos 	int error = 0;
1301d8e08d4Schristos 
1311d8e08d4Schristos 	if (mpls_count != 0)
1321d8e08d4Schristos 		error = EBUSY;
1331d8e08d4Schristos 
1341d8e08d4Schristos 	if (error == 0)
1351d8e08d4Schristos 		if_clone_detach(&mpls_if_cloner);
1361d8e08d4Schristos 
1371d8e08d4Schristos 	return error;
1381d8e08d4Schristos }
1391d8e08d4Schristos 
1401d8e08d4Schristos static int
mpls_clone_create(struct if_clone * ifc,int unit)141826653c1Skefren mpls_clone_create(struct if_clone *ifc, int unit)
142826653c1Skefren {
143826653c1Skefren 	struct mpls_softc *sc;
144826653c1Skefren 
1451d8e08d4Schristos 	atomic_inc_uint(&mpls_count);
146826653c1Skefren 	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
147826653c1Skefren 
148826653c1Skefren 	if_initname(&sc->sc_if, ifc->ifc_name, unit);
149826653c1Skefren 	sc->sc_if.if_softc = sc;
150826653c1Skefren 	sc->sc_if.if_type = IFT_MPLS;
151826653c1Skefren 	sc->sc_if.if_addrlen = 0;
152826653c1Skefren 	sc->sc_if.if_hdrlen = sizeof(union mpls_shim);
153826653c1Skefren 	sc->sc_if.if_dlt = DLT_NULL;
154826653c1Skefren 	sc->sc_if.if_mtu = 1500;
155826653c1Skefren 	sc->sc_if.if_flags = 0;
1569c4cd063Sozaki-r 	sc->sc_if._if_input = mpls_input;
157826653c1Skefren 	sc->sc_if.if_output = mpls_output;
158826653c1Skefren 	sc->sc_if.if_ioctl = mpls_ioctl;
159826653c1Skefren 
160076e3579Sriastradh 	if_attach(&sc->sc_if);
161826653c1Skefren 	if_alloc_sadl(&sc->sc_if);
162826653c1Skefren 	bpf_attach(&sc->sc_if, DLT_NULL, sizeof(uint32_t));
163826653c1Skefren 	return 0;
164826653c1Skefren }
165826653c1Skefren 
166826653c1Skefren static int
mpls_clone_destroy(struct ifnet * ifp)167826653c1Skefren mpls_clone_destroy(struct ifnet *ifp)
168826653c1Skefren {
169826653c1Skefren 
170826653c1Skefren 	bpf_detach(ifp);
171826653c1Skefren 	if_detach(ifp);
172826653c1Skefren 
173826653c1Skefren 	free(ifp->if_softc, M_DEVBUF);
1741d8e08d4Schristos 	atomic_dec_uint(&mpls_count);
175826653c1Skefren 	return 0;
176826653c1Skefren }
177826653c1Skefren 
178826653c1Skefren static void
mpls_input(struct ifnet * ifp,struct mbuf * m)179826653c1Skefren mpls_input(struct ifnet *ifp, struct mbuf *m)
180826653c1Skefren {
181826653c1Skefren #if 0
182826653c1Skefren 	/*
183826653c1Skefren 	 * TODO - kefren
184826653c1Skefren 	 * I'd love to unshim the packet, guess family
185826653c1Skefren 	 * and pass it to bpf
186826653c1Skefren 	 */
1873cd62456Smsaitoh 	bpf_mtap_af(ifp, AF_MPLS, m, BPF_D_IN);
188826653c1Skefren #endif
189826653c1Skefren 
190826653c1Skefren 	mpls_lse(m);
191826653c1Skefren }
192826653c1Skefren 
193826653c1Skefren void
mplsintr(void * arg __unused)194dd8687ccSthorpej mplsintr(void *arg __unused)
195826653c1Skefren {
1968f4376cbSozaki-r 	struct mbuf *m;
1978f4376cbSozaki-r 
198dd8687ccSthorpej 	while ((m = pktq_dequeue(mpls_pktq)) != NULL) {
199826653c1Skefren 		if (((m->m_flags & M_PKTHDR) == 0) ||
200fe6d4275Sozaki-r 		    (m->m_pkthdr.rcvif_index == 0))
201826653c1Skefren 			panic("mplsintr(): no pkthdr or rcvif");
202826653c1Skefren 
203826653c1Skefren #ifdef MBUFTRACE
204826653c1Skefren 		m_claimm(m, &mpls_owner);
205826653c1Skefren #endif
206fe6d4275Sozaki-r 		mpls_input(m_get_rcvif_NOMPSAFE(m), m);
207826653c1Skefren 	}
208826653c1Skefren }
209826653c1Skefren 
210826653c1Skefren /*
211826653c1Skefren  * prepend shim and deliver
212826653c1Skefren  */
213826653c1Skefren static int
mpls_output(struct ifnet * ifp,struct mbuf * m,const struct sockaddr * dst,const struct rtentry * rt)2142cf7873bSozaki-r mpls_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
2152cf7873bSozaki-r     const struct rtentry *rt)
216826653c1Skefren {
217bd098fd9Skefren 	union mpls_shim mh, *pms;
218826653c1Skefren 	struct rtentry *rt1;
219826653c1Skefren 	int err;
220bd098fd9Skefren 	uint psize = sizeof(struct sockaddr_mpls);
221826653c1Skefren 
2228a9adb2fSbouyer 	KASSERT(KERNEL_LOCKED_P());
2238a9adb2fSbouyer 
224826653c1Skefren 	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
225826653c1Skefren 		m_freem(m);
226826653c1Skefren 		return ENETDOWN;
227826653c1Skefren 	}
228826653c1Skefren 
22900914d6dSkefren 	if (rt_gettag(rt) == NULL || rt_gettag(rt)->sa_family != AF_MPLS) {
230826653c1Skefren 		m_freem(m);
231826653c1Skefren 		return EINVAL;
232826653c1Skefren 	}
233826653c1Skefren 
2343cd62456Smsaitoh 	bpf_mtap_af(ifp, dst->sa_family, m, BPF_D_OUT);
235826653c1Skefren 
236bd098fd9Skefren 	memset(&mh, 0, sizeof(mh));
237826653c1Skefren 	mh.s_addr = MPLS_GETSADDR(rt);
238826653c1Skefren 	mh.shim.bos = 1;
239826653c1Skefren 	mh.shim.exp = 0;
240826653c1Skefren 	mh.shim.ttl = mpls_defttl;
241826653c1Skefren 
242bd098fd9Skefren 	pms = &((struct sockaddr_mpls*)rt_gettag(rt))->smpls_addr;
243bd098fd9Skefren 
244bd098fd9Skefren 	while (psize <= rt_gettag(rt)->sa_len - sizeof(mh)) {
245bd098fd9Skefren 		pms++;
2463837722cSkefren 		if (mh.shim.label != MPLS_LABEL_IMPLNULL &&
2473837722cSkefren 		    ((m = mpls_prepend_shim(m, &mh)) == NULL))
248561daf4fSkefren 			return ENOBUFS;
249bd098fd9Skefren 		memset(&mh, 0, sizeof(mh));
250bd098fd9Skefren 		mh.s_addr = ntohl(pms->s_addr);
251bd098fd9Skefren 		mh.shim.bos = mh.shim.exp = 0;
252bd098fd9Skefren 		mh.shim.ttl = mpls_defttl;
253bd098fd9Skefren 		psize += sizeof(mh);
254bd098fd9Skefren 	}
255bd098fd9Skefren 
256826653c1Skefren 	switch (dst->sa_family) {
257826653c1Skefren #ifdef INET
258826653c1Skefren 	case AF_INET:
259bd098fd9Skefren 		m = mpls_label_inet(m, &mh, psize - sizeof(struct sockaddr_mpls));
260826653c1Skefren 		break;
261826653c1Skefren #endif
262826653c1Skefren #ifdef INET6
263826653c1Skefren 	case AF_INET6:
264bd098fd9Skefren 		m = mpls_label_inet6(m, &mh, psize - sizeof(struct sockaddr_mpls));
265826653c1Skefren 		break;
266826653c1Skefren #endif
267826653c1Skefren 	default:
268826653c1Skefren 		m = mpls_prepend_shim(m, &mh);
269826653c1Skefren 		break;
270826653c1Skefren 	}
271826653c1Skefren 
272826653c1Skefren 	if (m == NULL) {
273826653c1Skefren 		IF_DROP(&ifp->if_snd);
27470b554e6Sthorpej 		if_statinc(ifp, if_oerrors);
275826653c1Skefren 		return ENOBUFS;
276826653c1Skefren 	}
277826653c1Skefren 
27870b554e6Sthorpej 	if_statadd2(ifp, if_opackets, 1, if_obytes, m->m_pkthdr.len);
279826653c1Skefren 
280826653c1Skefren 	if ((rt1 = rtalloc1(rt->rt_gateway, 1)) == NULL) {
281826653c1Skefren 		m_freem(m);
282826653c1Skefren 		return EHOSTUNREACH;
283826653c1Skefren 	}
284826653c1Skefren 
285826653c1Skefren 	err = mpls_send_frame(m, rt1->rt_ifp, rt);
2866fb88806Sozaki-r 	rt_unref(rt1);
287826653c1Skefren 	return err;
288826653c1Skefren }
289826653c1Skefren 
290826653c1Skefren static int
mpls_ioctl(struct ifnet * ifp,u_long cmd,void * data)291826653c1Skefren mpls_ioctl(struct ifnet *ifp, u_long cmd, void *data)
292826653c1Skefren {
293826653c1Skefren 	int error = 0, s = splnet();
294826653c1Skefren 	struct ifreq *ifr = data;
295826653c1Skefren 
296826653c1Skefren 	switch(cmd) {
297826653c1Skefren 	case SIOCINITIFADDR:
298826653c1Skefren 		ifp->if_flags |= IFF_UP | IFF_RUNNING;
299826653c1Skefren 		break;
300826653c1Skefren 	case SIOCSIFMTU:
301826653c1Skefren 		if (ifr != NULL && ifr->ifr_mtu < 576) {
302826653c1Skefren 			error = EINVAL;
303826653c1Skefren 			break;
304826653c1Skefren 		}
305826653c1Skefren 		/* FALLTHROUGH */
306826653c1Skefren 	case SIOCGIFMTU:
307826653c1Skefren 		if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
308826653c1Skefren 			error = 0;
309826653c1Skefren 		break;
310826653c1Skefren 	case SIOCSIFFLAGS:
311826653c1Skefren 		if ((error = ifioctl_common(ifp, cmd, data)) != 0)
312826653c1Skefren 			break;
313826653c1Skefren 		if (ifp->if_flags & IFF_UP)
314826653c1Skefren 			ifp->if_flags |= IFF_RUNNING;
315826653c1Skefren 		break;
316826653c1Skefren 	default:
317826653c1Skefren 		error = ifioctl_common(ifp, cmd, data);
318826653c1Skefren 		break;
319826653c1Skefren 	}
320826653c1Skefren 	splx(s);
321826653c1Skefren 	return error;
322826653c1Skefren }
323826653c1Skefren 
3245eeded08Smaxv static inline struct mbuf *
mpls_trim_label(struct mbuf * m,union mpls_shim * sh)3255eeded08Smaxv mpls_trim_label(struct mbuf *m, union mpls_shim *sh)
3265eeded08Smaxv {
3275eeded08Smaxv 	m_adj(m, sizeof(union mpls_shim));
3285eeded08Smaxv 
3295eeded08Smaxv 	if (m->m_len < sizeof(union mpls_shim) &&
3305eeded08Smaxv 	    (m = m_pullup(m, sizeof(union mpls_shim))) == NULL)
3315eeded08Smaxv 		return NULL;
3325eeded08Smaxv 
3335eeded08Smaxv 	sh->s_addr = ntohl(mtod(m, union mpls_shim *)->s_addr);
3345eeded08Smaxv 
3355eeded08Smaxv 	return m;
3365eeded08Smaxv }
3375eeded08Smaxv 
338826653c1Skefren /*
339826653c1Skefren  * MPLS Label Switch Engine
340826653c1Skefren  */
341826653c1Skefren static int
mpls_lse(struct mbuf * m)342826653c1Skefren mpls_lse(struct mbuf *m)
343826653c1Skefren {
344826653c1Skefren 	struct sockaddr_mpls dst;
345826653c1Skefren 	union mpls_shim tshim, *htag;
346826653c1Skefren 	struct rtentry *rt = NULL;
347826653c1Skefren 	int error = ENOBUFS;
348561daf4fSkefren 	uint psize = sizeof(struct sockaddr_mpls);
349829b00a6Skefren 	bool push_back_alert = false;
350826653c1Skefren 
351ac4e3a2eSmaxv 	/* If we're not accepting MPLS frames, leave now. */
352ac4e3a2eSmaxv 	if (!mpls_frame_accept) {
353ac4e3a2eSmaxv 		error = EINVAL;
354ac4e3a2eSmaxv 		goto done;
355ac4e3a2eSmaxv 	}
356ac4e3a2eSmaxv 
357826653c1Skefren 	if (m->m_len < sizeof(union mpls_shim) &&
358826653c1Skefren 	    (m = m_pullup(m, sizeof(union mpls_shim))) == NULL)
359826653c1Skefren 		goto done;
360826653c1Skefren 
361826653c1Skefren 	dst.smpls_len = sizeof(struct sockaddr_mpls);
362826653c1Skefren 	dst.smpls_family = AF_MPLS;
363826653c1Skefren 	dst.smpls_addr.s_addr = ntohl(mtod(m, union mpls_shim *)->s_addr);
364826653c1Skefren 
365826653c1Skefren 	error = EINVAL;
366826653c1Skefren 
367826653c1Skefren 	/* TTL decrement */
368826653c1Skefren 	if ((m = mpls_ttl_dec(m)) == NULL)
369826653c1Skefren 		goto done;
370826653c1Skefren 
371326bf6faSkefren 	/* RFC 4182 */
37255408dcfSmaxv 	if (mpls_rfc4182 != 0) {
373326bf6faSkefren 		while ((dst.smpls_addr.shim.label == MPLS_LABEL_IPV4NULL ||
374326bf6faSkefren 		    dst.smpls_addr.shim.label == MPLS_LABEL_IPV6NULL) &&
3755eeded08Smaxv 		    __predict_false(dst.smpls_addr.shim.bos == 0)) {
3765eeded08Smaxv 			m = mpls_trim_label(m, &dst.smpls_addr);
3775eeded08Smaxv 			if (m == NULL) {
3785eeded08Smaxv 				goto done;
3795eeded08Smaxv 			}
3805eeded08Smaxv 		}
38155408dcfSmaxv 	}
382829b00a6Skefren 
383829b00a6Skefren 	/* RFC 3032 Section 2.1 Page 4 */
384829b00a6Skefren 	if (__predict_false(dst.smpls_addr.shim.label == MPLS_LABEL_RTALERT) &&
385829b00a6Skefren 	    dst.smpls_addr.shim.bos == 0) {
3865eeded08Smaxv 		m = mpls_trim_label(m, &dst.smpls_addr);
3875eeded08Smaxv 		if (m == NULL) {
3885eeded08Smaxv 			goto done;
3895eeded08Smaxv 		}
390829b00a6Skefren 		push_back_alert = true;
391326bf6faSkefren 	}
392326bf6faSkefren 
393826653c1Skefren 	if (dst.smpls_addr.shim.label <= MPLS_LABEL_RESMAX) {
394826653c1Skefren 		/* Don't swap reserved labels */
395826653c1Skefren 		switch (dst.smpls_addr.shim.label) {
396826653c1Skefren #ifdef INET
397826653c1Skefren 		case MPLS_LABEL_IPV4NULL:
398826653c1Skefren 			/* Pop shim and push mbuf to IP stack */
39955408dcfSmaxv 			if (dst.smpls_addr.shim.bos) {
40055408dcfSmaxv 				m = mpls_unlabel_inet(m, &error);
40155408dcfSmaxv 			}
402826653c1Skefren 			break;
403826653c1Skefren #endif
404826653c1Skefren #ifdef INET6
405826653c1Skefren 		case MPLS_LABEL_IPV6NULL:
406826653c1Skefren 			/* Pop shim and push mbuf to IPv6 stack */
40755408dcfSmaxv 			if (dst.smpls_addr.shim.bos) {
40855408dcfSmaxv 				m = mpls_unlabel_inet6(m, &error);
40955408dcfSmaxv 			}
410826653c1Skefren 			break;
411826653c1Skefren #endif
412826653c1Skefren 		case MPLS_LABEL_RTALERT:	/* Yeah, I'm all alerted */
413826653c1Skefren 		case MPLS_LABEL_IMPLNULL:	/* This is logical only */
414826653c1Skefren 		default:			/* Rest are not allowed */
415826653c1Skefren 			break;
416826653c1Skefren 		}
417826653c1Skefren 		goto done;
418826653c1Skefren 	}
419826653c1Skefren 
420826653c1Skefren 	/* Check if we should do MPLS forwarding */
421826653c1Skefren 	error = EHOSTUNREACH;
422826653c1Skefren 	if (!mpls_forwarding)
423826653c1Skefren 		goto done;
424826653c1Skefren 
425826653c1Skefren 	/* Get a route to dst */
4265eeded08Smaxv 	dst.smpls_addr.shim.ttl = 0;
4275eeded08Smaxv 	dst.smpls_addr.shim.bos = 0;
428826653c1Skefren 	dst.smpls_addr.shim.exp = 0;
429826653c1Skefren 	dst.smpls_addr.s_addr = htonl(dst.smpls_addr.s_addr);
430826653c1Skefren 	if ((rt = rtalloc1((const struct sockaddr*)&dst, 1)) == NULL)
431826653c1Skefren 		goto done;
432826653c1Skefren 
43300914d6dSkefren 	/* MPLS packet with no MPLS tagged route ? */
434826653c1Skefren 	if ((rt->rt_flags & RTF_GATEWAY) == 0 ||
43500914d6dSkefren 	     rt_gettag(rt) == NULL ||
43600914d6dSkefren 	     rt_gettag(rt)->sa_family != AF_MPLS)
437826653c1Skefren 		goto done;
438826653c1Skefren 
439826653c1Skefren 	tshim.s_addr = MPLS_GETSADDR(rt);
440826653c1Skefren 
441826653c1Skefren 	/* Swap labels */
442826653c1Skefren 	if ((m->m_len < sizeof(union mpls_shim)) &&
443826653c1Skefren 	    (m = m_pullup(m, sizeof(union mpls_shim))) == 0) {
444826653c1Skefren 		error = ENOBUFS;
445826653c1Skefren 		goto done;
446826653c1Skefren 	}
447826653c1Skefren 
448826653c1Skefren 	/* Replace only the label */
449826653c1Skefren 	htag = mtod(m, union mpls_shim *);
450826653c1Skefren 	htag->s_addr = ntohl(htag->s_addr);
451826653c1Skefren 	htag->shim.label = tshim.shim.label;
452826653c1Skefren 	htag->s_addr = htonl(htag->s_addr);
453826653c1Skefren 
454561daf4fSkefren 	/* check if there is anything more to prepend */
455561daf4fSkefren 	htag = &((struct sockaddr_mpls*)rt_gettag(rt))->smpls_addr;
456561daf4fSkefren 	while (psize <= rt_gettag(rt)->sa_len - sizeof(tshim)) {
457561daf4fSkefren 		htag++;
458561daf4fSkefren 		memset(&tshim, 0, sizeof(tshim));
459561daf4fSkefren 		tshim.s_addr = ntohl(htag->s_addr);
460561daf4fSkefren 		tshim.shim.bos = tshim.shim.exp = 0;
461561daf4fSkefren 		tshim.shim.ttl = mpls_defttl;
4623837722cSkefren 		if (tshim.shim.label != MPLS_LABEL_IMPLNULL &&
46355408dcfSmaxv 		    ((m = mpls_prepend_shim(m, &tshim)) == NULL)) {
46455408dcfSmaxv 			error = ENOBUFS;
46555408dcfSmaxv 			goto done;
46655408dcfSmaxv 		}
467561daf4fSkefren 		psize += sizeof(tshim);
468561daf4fSkefren 	}
469561daf4fSkefren 
470829b00a6Skefren 	if (__predict_false(push_back_alert == true)) {
471829b00a6Skefren 		/* re-add the router alert label */
472829b00a6Skefren 		memset(&tshim, 0, sizeof(tshim));
473829b00a6Skefren 		tshim.s_addr = MPLS_LABEL_RTALERT;
474829b00a6Skefren 		tshim.shim.bos = tshim.shim.exp = 0;
475829b00a6Skefren 		tshim.shim.ttl = mpls_defttl;
47655408dcfSmaxv 		if ((m = mpls_prepend_shim(m, &tshim)) == NULL) {
47755408dcfSmaxv 			error = ENOBUFS;
47855408dcfSmaxv 			goto done;
47955408dcfSmaxv 		}
480829b00a6Skefren 	}
481829b00a6Skefren 
4822cf7873bSozaki-r 	if ((rt->rt_flags & RTF_GATEWAY) == 0) {
4832cf7873bSozaki-r 		error = EHOSTUNREACH;
4842cf7873bSozaki-r 		goto done;
4852cf7873bSozaki-r 	}
4862cf7873bSozaki-r 
4872cf7873bSozaki-r 	rt->rt_use++;
488826653c1Skefren 	error = mpls_send_frame(m, rt->rt_ifp, rt);
489826653c1Skefren 
490826653c1Skefren done:
491826653c1Skefren 	if (error != 0 && m != NULL)
492826653c1Skefren 		m_freem(m);
493826653c1Skefren 	if (rt != NULL)
4946fb88806Sozaki-r 		rt_unref(rt);
495826653c1Skefren 
496826653c1Skefren 	return error;
497826653c1Skefren }
498826653c1Skefren 
499826653c1Skefren static int
mpls_send_frame(struct mbuf * m,struct ifnet * ifp,const struct rtentry * rt)5002cf7873bSozaki-r mpls_send_frame(struct mbuf *m, struct ifnet *ifp, const struct rtentry *rt)
501826653c1Skefren {
502826653c1Skefren 	union mpls_shim msh;
5038a9adb2fSbouyer 	int ret;
504826653c1Skefren 
505826653c1Skefren 	msh.s_addr = MPLS_GETSADDR(rt);
50687fd7aebSkefren 	if (msh.shim.label == MPLS_LABEL_IMPLNULL ||
50787fd7aebSkefren 	    (m->m_flags & (M_MCAST | M_BCAST))) {
508826653c1Skefren 		m_adj(m, sizeof(union mpls_shim));
509826653c1Skefren 		m->m_pkthdr.csum_flags = 0;
510826653c1Skefren 	}
511826653c1Skefren 
512826653c1Skefren 	switch(ifp->if_type) {
513a91123ebSkefren 	/* only these are supported for now */
514826653c1Skefren 	case IFT_ETHER:
515826653c1Skefren 	case IFT_TUNNEL:
516826653c1Skefren 	case IFT_LOOP:
5176ea8c2e6Sozaki-r #ifdef INET
5189e0f6c5eSozaki-r 		ret = ip_if_output(ifp, m, rt->rt_gateway, rt);
5196ea8c2e6Sozaki-r #else
52010b439dfSknakahara 		ret = if_output_lock(ifp, ifp, m, rt->rt_gateway, rt);
5216ea8c2e6Sozaki-r #endif
5228a9adb2fSbouyer 		return ret;
523826653c1Skefren 		break;
524826653c1Skefren 	default:
525826653c1Skefren 		return ENETUNREACH;
526826653c1Skefren 	}
527826653c1Skefren 	return 0;
528826653c1Skefren }
529826653c1Skefren 
530826653c1Skefren #ifdef INET
53155408dcfSmaxv static struct mbuf *
mpls_unlabel_inet(struct mbuf * m,int * error)53255408dcfSmaxv mpls_unlabel_inet(struct mbuf *m, int *error)
533826653c1Skefren {
534826653c1Skefren 	struct ip *iph;
5355eeded08Smaxv 	union mpls_shim ms;
53660d350cfSrmind 	int iphlen;
537826653c1Skefren 
538826653c1Skefren 	if (mpls_mapttl_inet || mpls_mapprec_inet) {
539826653c1Skefren 		/* get shim info */
5405eeded08Smaxv 		ms.s_addr = ntohl(mtod(m, union mpls_shim *)->s_addr);
541826653c1Skefren 
542826653c1Skefren 		/* and get rid of it */
543826653c1Skefren 		m_adj(m, sizeof(union mpls_shim));
544826653c1Skefren 
545826653c1Skefren 		/* get ip header */
546826653c1Skefren 		if (m->m_len < sizeof(struct ip) &&
54755408dcfSmaxv 		    (m = m_pullup(m, sizeof(struct ip))) == NULL) {
54855408dcfSmaxv 			*error = ENOBUFS;
54955408dcfSmaxv 			return NULL;
55055408dcfSmaxv 		}
55155408dcfSmaxv 
552826653c1Skefren 		iph = mtod(m, struct ip *);
553826653c1Skefren 		iphlen = iph->ip_hl << 2;
554826653c1Skefren 
555826653c1Skefren 		/* get it all */
556826653c1Skefren 		if (m->m_len < iphlen) {
55755408dcfSmaxv 			if ((m = m_pullup(m, iphlen)) == NULL) {
55855408dcfSmaxv 				*error = ENOBUFS;
55955408dcfSmaxv 				return NULL;
56055408dcfSmaxv 			}
561826653c1Skefren 			iph = mtod(m, struct ip *);
562826653c1Skefren 		}
563826653c1Skefren 
564826653c1Skefren 		/* check ipsum */
565826653c1Skefren 		if (in_cksum(m, iphlen) != 0) {
566826653c1Skefren 			m_freem(m);
56755408dcfSmaxv 			*error = EINVAL;
56855408dcfSmaxv 			return NULL;
569826653c1Skefren 		}
570826653c1Skefren 
571826653c1Skefren 		/* set IP ttl from MPLS ttl */
572826653c1Skefren 		if (mpls_mapttl_inet)
5735eeded08Smaxv 			iph->ip_ttl = ms.shim.ttl;
574826653c1Skefren 
575826653c1Skefren 		/* set IP Precedence from MPLS Exp */
576826653c1Skefren 		if (mpls_mapprec_inet) {
577826653c1Skefren 			iph->ip_tos = (iph->ip_tos << 3) >> 3;
5785eeded08Smaxv 			iph->ip_tos |= ms.shim.exp << 5;
579826653c1Skefren 		}
580826653c1Skefren 
581826653c1Skefren 		/* reset ipsum because we modified TTL and TOS */
582826653c1Skefren 		iph->ip_sum = 0;
583826653c1Skefren 		iph->ip_sum = in_cksum(m, iphlen);
58455408dcfSmaxv 	} else {
585826653c1Skefren 		m_adj(m, sizeof(union mpls_shim));
58655408dcfSmaxv 	}
587826653c1Skefren 
588826653c1Skefren 	/* Put it on IP queue */
58960d350cfSrmind 	if (__predict_false(!pktq_enqueue(ip_pktq, m, 0))) {
590826653c1Skefren 		m_freem(m);
59155408dcfSmaxv 		*error = ENOBUFS;
59255408dcfSmaxv 		return NULL;
593826653c1Skefren 	}
59455408dcfSmaxv 
59555408dcfSmaxv 	*error = 0;
59655408dcfSmaxv 	return m;
597826653c1Skefren }
598826653c1Skefren 
599826653c1Skefren /*
600826653c1Skefren  * Prepend MPLS label
601826653c1Skefren  */
602826653c1Skefren static struct mbuf *
mpls_label_inet(struct mbuf * m,union mpls_shim * ms,uint offset)603bd098fd9Skefren mpls_label_inet(struct mbuf *m, union mpls_shim *ms, uint offset)
604826653c1Skefren {
605931515b1Skefren 	struct ip iphdr;
606826653c1Skefren 
607826653c1Skefren 	if (mpls_mapttl_inet || mpls_mapprec_inet) {
6085eeded08Smaxv 		/* XXX Maybe just check m->m_pkthdr.len instead? */
6095eeded08Smaxv 		if ((m->m_len < offset + sizeof(struct ip)) &&
610bd098fd9Skefren 		    (m = m_pullup(m, offset + sizeof(struct ip))) == 0)
6115eeded08Smaxv 			return NULL;
6125eeded08Smaxv 
613931515b1Skefren 		m_copydata(m, offset, sizeof(struct ip), &iphdr);
614826653c1Skefren 
615826653c1Skefren 		/* Map TTL */
616826653c1Skefren 		if (mpls_mapttl_inet)
617931515b1Skefren 			ms->shim.ttl = iphdr.ip_ttl;
618826653c1Skefren 
619826653c1Skefren 		/* Copy IP precedence to EXP */
620826653c1Skefren 		if (mpls_mapprec_inet)
621931515b1Skefren 			ms->shim.exp = ((u_int8_t)iphdr.ip_tos) >> 5;
622826653c1Skefren 	}
623826653c1Skefren 
624826653c1Skefren 	if ((m = mpls_prepend_shim(m, ms)) == NULL)
625826653c1Skefren 		return NULL;
626826653c1Skefren 
627826653c1Skefren 	return m;
628826653c1Skefren }
629826653c1Skefren #endif	/* INET */
630826653c1Skefren 
631826653c1Skefren #ifdef INET6
63255408dcfSmaxv static struct mbuf *
mpls_unlabel_inet6(struct mbuf * m,int * error)63355408dcfSmaxv mpls_unlabel_inet6(struct mbuf *m, int *error)
634826653c1Skefren {
635826653c1Skefren 	struct ip6_hdr *ip6hdr;
636826653c1Skefren 	union mpls_shim ms;
637826653c1Skefren 
638826653c1Skefren 	/* TODO: mapclass */
639826653c1Skefren 	if (mpls_mapttl_inet6) {
640826653c1Skefren 		ms.s_addr = ntohl(mtod(m, union mpls_shim *)->s_addr);
641826653c1Skefren 		m_adj(m, sizeof(union mpls_shim));
642826653c1Skefren 
643826653c1Skefren 		if (m->m_len < sizeof(struct ip6_hdr) &&
64455408dcfSmaxv 		    (m = m_pullup(m, sizeof(struct ip6_hdr))) == 0) {
64555408dcfSmaxv 			*error = ENOBUFS;
64655408dcfSmaxv 			return NULL;
64755408dcfSmaxv 		}
648826653c1Skefren 		ip6hdr = mtod(m, struct ip6_hdr *);
649826653c1Skefren 
650826653c1Skefren 		/* Because we just decremented this in mpls_lse */
651826653c1Skefren 		ip6hdr->ip6_hlim = ms.shim.ttl + 1;
65255408dcfSmaxv 	} else {
653826653c1Skefren 		m_adj(m, sizeof(union mpls_shim));
65455408dcfSmaxv 	}
655826653c1Skefren 
65660d350cfSrmind 	/* Put it back on IPv6 queue. */
65760d350cfSrmind 	if (__predict_false(!pktq_enqueue(ip6_pktq, m, 0))) {
658826653c1Skefren 		m_freem(m);
65955408dcfSmaxv 		*error = ENOBUFS;
66055408dcfSmaxv 		return NULL;
661826653c1Skefren 	}
66255408dcfSmaxv 
66355408dcfSmaxv 	*error = 0;
66455408dcfSmaxv 	return m;
665826653c1Skefren }
666826653c1Skefren 
667826653c1Skefren static struct mbuf *
mpls_label_inet6(struct mbuf * m,union mpls_shim * ms,uint offset)668bd098fd9Skefren mpls_label_inet6(struct mbuf *m, union mpls_shim *ms, uint offset)
669826653c1Skefren {
670931515b1Skefren 	struct ip6_hdr ip6h;
671826653c1Skefren 
672826653c1Skefren 	if (mpls_mapttl_inet6 || mpls_mapclass_inet6) {
6735eeded08Smaxv 		/* XXX Maybe just check m->m_pkthdr.len instead? */
6745eeded08Smaxv 		if ((m->m_len < offset + sizeof(struct ip6_hdr)) &&
675bd098fd9Skefren 		    (m = m_pullup(m, offset + sizeof(struct ip6_hdr))) == 0)
676826653c1Skefren 			return NULL;
6775eeded08Smaxv 
678931515b1Skefren 		m_copydata(m, offset, sizeof(struct ip6_hdr), &ip6h);
679826653c1Skefren 
680826653c1Skefren 		if (mpls_mapttl_inet6)
681931515b1Skefren 			ms->shim.ttl = ip6h.ip6_hlim;
682826653c1Skefren 
683826653c1Skefren 		if (mpls_mapclass_inet6)
684931515b1Skefren 			ms->shim.exp = ip6h.ip6_vfc << 1 >> 5;
685826653c1Skefren 	}
686826653c1Skefren 
687826653c1Skefren 	if ((m = mpls_prepend_shim(m, ms)) == NULL)
688826653c1Skefren 		return NULL;
689826653c1Skefren 
690826653c1Skefren 	return m;
691826653c1Skefren }
692826653c1Skefren #endif	/* INET6 */
693826653c1Skefren 
694826653c1Skefren static struct mbuf *
mpls_prepend_shim(struct mbuf * m,union mpls_shim * ms)695826653c1Skefren mpls_prepend_shim(struct mbuf *m, union mpls_shim *ms)
696826653c1Skefren {
697826653c1Skefren 	union mpls_shim *shim;
698826653c1Skefren 
699826653c1Skefren 	M_PREPEND(m, sizeof(*ms), M_DONTWAIT);
700826653c1Skefren 	if (m == NULL)
701826653c1Skefren 		return NULL;
702826653c1Skefren 
703826653c1Skefren 	if (m->m_len < sizeof(union mpls_shim) &&
704826653c1Skefren 	    (m = m_pullup(m, sizeof(union mpls_shim))) == 0)
705826653c1Skefren 		return NULL;
706826653c1Skefren 
707826653c1Skefren 	shim = mtod(m, union mpls_shim *);
708826653c1Skefren 
709826653c1Skefren 	memcpy(shim, ms, sizeof(*shim));
710826653c1Skefren 	shim->s_addr = htonl(shim->s_addr);
711826653c1Skefren 
712826653c1Skefren 	return m;
713826653c1Skefren }
7141d8e08d4Schristos 
7151d8e08d4Schristos /*
7161d8e08d4Schristos  * Module infrastructure
7171d8e08d4Schristos  */
7181d8e08d4Schristos #include "if_module.h"
7191d8e08d4Schristos 
720befc8be5Spgoyette IF_MODULE(MODULE_CLASS_DRIVER, mpls, NULL)
721