xref: /openbsd-src/sys/net/if_mpe.c (revision b5b8bc53ca00dc14cccb11ede4abd86818e9a05b)
1*b5b8bc53Smvs /* $OpenBSD: if_mpe.c,v 1.105 2024/01/01 18:47:02 mvs Exp $ */
2384d604dSpyr 
3384d604dSpyr /*
4384d604dSpyr  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@spootnik.org>
5384d604dSpyr  *
6384d604dSpyr  * Permission to use, copy, modify, and distribute this software for any
7384d604dSpyr  * purpose with or without fee is hereby granted, provided that the above
8384d604dSpyr  * copyright notice and this permission notice appear in all copies.
9384d604dSpyr  *
10384d604dSpyr  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11384d604dSpyr  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12384d604dSpyr  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13384d604dSpyr  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14384d604dSpyr  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15384d604dSpyr  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16384d604dSpyr  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17384d604dSpyr  */
18b896c3f8Spyr 
19b896c3f8Spyr #include <sys/param.h>
20b896c3f8Spyr #include <sys/systm.h>
21b896c3f8Spyr #include <sys/mbuf.h>
22b896c3f8Spyr #include <sys/socket.h>
23b896c3f8Spyr #include <sys/sockio.h>
24b896c3f8Spyr #include <sys/ioctl.h>
25b896c3f8Spyr 
26b896c3f8Spyr #include <net/if.h>
27c7b7b779Sbluhm #include <net/if_dl.h>
280deb6685Smpi #include <net/if_var.h>
29b896c3f8Spyr #include <net/if_types.h>
305aa9b43bSpyr #include <net/netisr.h>
31b896c3f8Spyr #include <net/route.h>
32b896c3f8Spyr 
33b896c3f8Spyr #include <netinet/in.h>
34b896c3f8Spyr #include <netinet/ip.h>
35b896c3f8Spyr 
36b896c3f8Spyr #ifdef INET6
3766c01e6aSmichele #include <netinet/ip6.h>
38b896c3f8Spyr #endif /* INET6 */
39b896c3f8Spyr 
403dbb74bbSpyr #include "bpfilter.h"
413dbb74bbSpyr #if NBPFILTER > 0
423dbb74bbSpyr #include <net/bpf.h>
433dbb74bbSpyr #endif
443dbb74bbSpyr 
45b896c3f8Spyr #include <netmpls/mpls.h>
46b896c3f8Spyr 
472f1dccd9Sdlg 
482f1dccd9Sdlg 
49b896c3f8Spyr #ifdef MPLS_DEBUG
50b896c3f8Spyr #define DPRINTF(x)    do { if (mpedebug) printf x ; } while (0)
51b896c3f8Spyr #else
52b896c3f8Spyr #define DPRINTF(x)
53b896c3f8Spyr #endif
54b896c3f8Spyr 
552f1dccd9Sdlg struct mpe_softc {
562f1dccd9Sdlg 	struct ifnet		sc_if;		/* the interface */
57f1dea89cSdlg 	int			sc_txhprio;
58a61f5d85Sdlg 	int			sc_rxhprio;
59296ad859Sdlg 	unsigned int		sc_rdomain;
602f1dccd9Sdlg 	struct ifaddr		sc_ifa;
612f1dccd9Sdlg 	struct sockaddr_mpls	sc_smpls;
62119f8e2bSdlg 
63119f8e2bSdlg 	int			sc_dead;
642f1dccd9Sdlg };
652f1dccd9Sdlg 
662f1dccd9Sdlg #define MPE_HDRLEN	sizeof(struct shim_hdr)
672f1dccd9Sdlg #define MPE_MTU		1500
682f1dccd9Sdlg #define MPE_MTU_MIN	256
692f1dccd9Sdlg #define MPE_MTU_MAX	8192
702f1dccd9Sdlg 
71b896c3f8Spyr void	mpeattach(int);
726a969fa2Sdlg int	mpe_output(struct ifnet *, struct mbuf *, struct sockaddr *,
73b896c3f8Spyr 	    struct rtentry *);
746a969fa2Sdlg int	mpe_ioctl(struct ifnet *, u_long, caddr_t);
756a969fa2Sdlg void	mpe_start(struct ifnet *);
76b896c3f8Spyr int	mpe_clone_create(struct if_clone *, int);
77b896c3f8Spyr int	mpe_clone_destroy(struct ifnet *);
78c6de5097Sdlg void	mpe_input(struct ifnet *, struct mbuf *);
79b896c3f8Spyr 
80b896c3f8Spyr struct if_clone	mpe_cloner =
81b896c3f8Spyr     IF_CLONE_INITIALIZER("mpe", mpe_clone_create, mpe_clone_destroy);
82b896c3f8Spyr 
838b3356d2Smichele extern int	mpls_mapttl_ip;
8441a5659aSmikeb #ifdef INET6
858b3356d2Smichele extern int	mpls_mapttl_ip6;
8641a5659aSmikeb #endif
878b3356d2Smichele 
88b896c3f8Spyr void
mpeattach(int nmpe)89b896c3f8Spyr mpeattach(int nmpe)
90b896c3f8Spyr {
91b896c3f8Spyr 	if_clone_attach(&mpe_cloner);
92b896c3f8Spyr }
93b896c3f8Spyr 
94b896c3f8Spyr int
mpe_clone_create(struct if_clone * ifc,int unit)95b896c3f8Spyr mpe_clone_create(struct if_clone *ifc, int unit)
96b896c3f8Spyr {
9726e9ef71Sdlg 	struct mpe_softc	*sc;
98b896c3f8Spyr 	struct ifnet		*ifp;
99b896c3f8Spyr 
100c14615c1Sdlg 	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO);
101c14615c1Sdlg 	if (sc == NULL)
102c14615c1Sdlg 		return (ENOMEM);
103c14615c1Sdlg 
10426e9ef71Sdlg 	ifp = &sc->sc_if;
105b896c3f8Spyr 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "mpe%d", unit);
106807bde9bSpyr 	ifp->if_flags = IFF_POINTOPOINT;
10799bf4b5fSmpi 	ifp->if_xflags = IFXF_CLONED;
10826e9ef71Sdlg 	ifp->if_softc = sc;
109807bde9bSpyr 	ifp->if_mtu = MPE_MTU;
1106a969fa2Sdlg 	ifp->if_ioctl = mpe_ioctl;
1112e3db693Sdlg 	ifp->if_bpf_mtap = p2p_bpf_mtap;
1122e3db693Sdlg 	ifp->if_input = p2p_input;
1136a969fa2Sdlg 	ifp->if_output = mpe_output;
1146a969fa2Sdlg 	ifp->if_start = mpe_start;
115b896c3f8Spyr 	ifp->if_type = IFT_MPLS;
116b896c3f8Spyr 	ifp->if_hdrlen = MPE_HDRLEN;
117119f8e2bSdlg 
118119f8e2bSdlg 	sc->sc_dead = 0;
119119f8e2bSdlg 
120*b5b8bc53Smvs 	if_counters_alloc(ifp);
121b896c3f8Spyr 	if_attach(ifp);
122b896c3f8Spyr 	if_alloc_sadl(ifp);
1232e3db693Sdlg 
124b896c3f8Spyr #if NBPFILTER > 0
125c3dd9049Syasuoka 	bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(u_int32_t));
126b896c3f8Spyr #endif
127b896c3f8Spyr 
128f1dea89cSdlg 	sc->sc_txhprio = 0;
129a61f5d85Sdlg 	sc->sc_rxhprio = IF_HDRPRIO_PACKET;
130296ad859Sdlg 	sc->sc_rdomain = 0;
13118a44669Sbluhm 	refcnt_init_trace(&sc->sc_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
13226e9ef71Sdlg 	sc->sc_ifa.ifa_ifp = ifp;
13326e9ef71Sdlg 	sc->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
13426e9ef71Sdlg 	sc->sc_smpls.smpls_len = sizeof(sc->sc_smpls);
13526e9ef71Sdlg 	sc->sc_smpls.smpls_family = AF_MPLS;
136106d177cSmpi 
137b896c3f8Spyr 	return (0);
138b896c3f8Spyr }
139b896c3f8Spyr 
140b896c3f8Spyr int
mpe_clone_destroy(struct ifnet * ifp)141b896c3f8Spyr mpe_clone_destroy(struct ifnet *ifp)
142b896c3f8Spyr {
14326e9ef71Sdlg 	struct mpe_softc	*sc = ifp->if_softc;
144b896c3f8Spyr 
14518e29477Sdlg 	NET_LOCK();
14618e29477Sdlg 	CLR(ifp->if_flags, IFF_RUNNING);
147119f8e2bSdlg 	sc->sc_dead = 1;
148119f8e2bSdlg 
14926e9ef71Sdlg 	if (sc->sc_smpls.smpls_label) {
150296ad859Sdlg 		rt_ifa_del(&sc->sc_ifa, RTF_MPLS|RTF_LOCAL,
151296ad859Sdlg 		    smplstosa(&sc->sc_smpls), sc->sc_rdomain);
152106d177cSmpi 	}
15318e29477Sdlg 	NET_UNLOCK();
154106d177cSmpi 
15518e29477Sdlg 	ifq_barrier(&ifp->if_snd);
156119f8e2bSdlg 
157b896c3f8Spyr 	if_detach(ifp);
15818a44669Sbluhm 	if (refcnt_rele(&sc->sc_ifa.ifa_refcnt) == 0) {
15918a44669Sbluhm 		panic("%s: ifa refcnt has %u refs", __func__,
16018a44669Sbluhm 		    sc->sc_ifa.ifa_refcnt.r_refs);
16118a44669Sbluhm 	}
16226e9ef71Sdlg 	free(sc, M_DEVBUF, sizeof *sc);
163b896c3f8Spyr 	return (0);
164b896c3f8Spyr }
165b896c3f8Spyr 
166b896c3f8Spyr /*
167b896c3f8Spyr  * Start output on the mpe interface.
168b896c3f8Spyr  */
169b896c3f8Spyr void
mpe_start(struct ifnet * ifp)1706339f48fSdlg mpe_start(struct ifnet *ifp)
171b896c3f8Spyr {
172f0b1bf0bSdlg 	struct mpe_softc	*sc = ifp->if_softc;
173b896c3f8Spyr 	struct mbuf		*m;
1746339f48fSdlg 	struct sockaddr		*sa;
1756339f48fSdlg 	struct sockaddr		smpls = { .sa_family = AF_MPLS };
176769a1feaSclaudio 	struct rtentry		*rt;
1776339f48fSdlg 	struct ifnet		*ifp0;
178b896c3f8Spyr 
1796339f48fSdlg 	while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
1806339f48fSdlg 		sa = mtod(m, struct sockaddr *);
181f0b1bf0bSdlg 		rt = rtalloc(sa, RT_RESOLVE, sc->sc_rdomain);
1823df9a1b0Smpi 		if (!rtisvalid(rt)) {
183769a1feaSclaudio 			m_freem(m);
1843df9a1b0Smpi 			rtfree(rt);
1853df9a1b0Smpi 			continue;
1863df9a1b0Smpi 		}
1873df9a1b0Smpi 
1886339f48fSdlg 		ifp0 = if_get(rt->rt_ifidx);
1896339f48fSdlg 		if (ifp0 == NULL) {
1903df9a1b0Smpi 			m_freem(m);
1913df9a1b0Smpi 			rtfree(rt);
192769a1feaSclaudio 			continue;
193769a1feaSclaudio 		}
1946339f48fSdlg 
1956339f48fSdlg 		m_adj(m, sa->sa_len);
1966339f48fSdlg 
197769a1feaSclaudio #if NBPFILTER > 0
1986339f48fSdlg 		if (ifp->if_bpf) {
199769a1feaSclaudio 			/* remove MPLS label before passing packet to bpf */
200769a1feaSclaudio 			m->m_data += sizeof(struct shim_hdr);
201769a1feaSclaudio 			m->m_len -= sizeof(struct shim_hdr);
202769a1feaSclaudio 			m->m_pkthdr.len -= sizeof(struct shim_hdr);
2036339f48fSdlg 			bpf_mtap_af(ifp->if_bpf, m->m_pkthdr.ph_family,
2046339f48fSdlg 			    m, BPF_DIRECTION_OUT);
205769a1feaSclaudio 			m->m_data -= sizeof(struct shim_hdr);
206769a1feaSclaudio 			m->m_len += sizeof(struct shim_hdr);
207769a1feaSclaudio 			m->m_pkthdr.len += sizeof(struct shim_hdr);
208769a1feaSclaudio 		}
209769a1feaSclaudio #endif
210db4e5757Sdlg 
211db4e5757Sdlg 		m->m_pkthdr.ph_rtableid = sc->sc_rdomain;
212957fd304Sdlg 		CLR(m->m_flags, M_BCAST|M_MCAST);
213db4e5757Sdlg 
2146339f48fSdlg 		mpls_output(ifp0, m, &smpls, rt);
215aa0313afSdlg 		if_put(ifp0);
21627ae666cSmpi 		rtfree(rt);
217b896c3f8Spyr 	}
218b896c3f8Spyr }
219b896c3f8Spyr 
220b896c3f8Spyr int
mpe_output(struct ifnet * ifp,struct mbuf * m,struct sockaddr * dst,struct rtentry * rt)2216a969fa2Sdlg mpe_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
222b896c3f8Spyr 	struct rtentry *rt)
223b896c3f8Spyr {
224f1dea89cSdlg 	struct mpe_softc *sc;
2256339f48fSdlg 	struct rt_mpls	*rtmpls;
226769a1feaSclaudio 	struct shim_hdr	shim;
227b896c3f8Spyr 	int		error;
228f1dea89cSdlg 	int		txprio;
2296339f48fSdlg 	uint8_t		ttl = mpls_defttl;
230f1dea89cSdlg 	uint8_t		tos, prio;
231aa0313afSdlg 	size_t		ttloff;
2326339f48fSdlg 	socklen_t	slen;
233769a1feaSclaudio 
234aa0313afSdlg 	if (!rtisvalid(rt) || !ISSET(rt->rt_flags, RTF_MPLS)) {
235aa0313afSdlg 		m_freem(m);
236aa0313afSdlg 		return (ENETUNREACH);
237aa0313afSdlg 	}
238aa0313afSdlg 
239aa0313afSdlg 	if (dst->sa_family == AF_LINK && ISSET(rt->rt_flags, RTF_LOCAL)) {
240c6de5097Sdlg 		mpe_input(ifp, m);
241c6de5097Sdlg 		return (0);
242c6de5097Sdlg 	}
243c6de5097Sdlg 
244769a1feaSclaudio #ifdef DIAGNOSTIC
2455ee8afe3Smpi 	if (ifp->if_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid)) {
246769a1feaSclaudio 		printf("%s: trying to send packet on wrong domain. "
247769a1feaSclaudio 		    "if %d vs. mbuf %d\n", ifp->if_xname,
2485ee8afe3Smpi 		    ifp->if_rdomain, rtable_l2(m->m_pkthdr.ph_rtableid));
249769a1feaSclaudio 	}
250769a1feaSclaudio #endif
2516339f48fSdlg 
2526339f48fSdlg 	rtmpls = (struct rt_mpls *)rt->rt_llinfo;
2536339f48fSdlg 	if (rtmpls->mpls_operation != MPLS_OP_PUSH) {
2546339f48fSdlg 		m_freem(m);
255aa0313afSdlg 		return (ENETUNREACH);
2566339f48fSdlg 	}
2576339f48fSdlg 
258b896c3f8Spyr 	error = 0;
2593dbb74bbSpyr 	switch (dst->sa_family) {
260f1dea89cSdlg 	case AF_INET: {
261f1dea89cSdlg 		struct ip *ip = mtod(m, struct ip *);
262f1dea89cSdlg 		tos = ip->ip_tos;
263aa0313afSdlg 		ttloff = offsetof(struct ip, ip_ttl);
2646339f48fSdlg 		slen = sizeof(struct sockaddr_in);
265769a1feaSclaudio 		break;
266f1dea89cSdlg 	}
2676339f48fSdlg #ifdef INET6
268f1dea89cSdlg 	case AF_INET6: {
269f1dea89cSdlg 		struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
270f1dea89cSdlg 		uint32_t flow = bemtoh32(&ip6->ip6_flow);
271f1dea89cSdlg 		tos = flow >> 20;
272aa0313afSdlg 		ttloff = offsetof(struct ip6_hdr, ip6_hlim);
2736339f48fSdlg 		slen = sizeof(struct sockaddr_in6);
2746339f48fSdlg 		break;
275f1dea89cSdlg 	}
2766339f48fSdlg #endif
2773dbb74bbSpyr 	default:
278769a1feaSclaudio 		m_freem(m);
279aa0313afSdlg 		return (EPFNOSUPPORT);
280aa0313afSdlg 	}
281aa0313afSdlg 
282aa0313afSdlg 	if (mpls_mapttl_ip) {
283aa0313afSdlg 		/* assumes the ip header is already contig */
284aa0313afSdlg 		ttl = *(mtod(m, uint8_t *) + ttloff);
2853b0a6083Sclaudio 	}
286769a1feaSclaudio 
287f1dea89cSdlg 	sc = ifp->if_softc;
288f1dea89cSdlg 	txprio = sc->sc_txhprio;
289f1dea89cSdlg 
290f1dea89cSdlg 	switch (txprio) {
291f1dea89cSdlg 	case IF_HDRPRIO_PACKET:
292f1dea89cSdlg 		prio = m->m_pkthdr.pf.prio;
293f1dea89cSdlg 		break;
294f1dea89cSdlg 	case IF_HDRPRIO_PAYLOAD:
295f1dea89cSdlg 		prio = IFQ_TOS2PRIO(tos);
296f1dea89cSdlg 		break;
297f1dea89cSdlg 	default:
298f1dea89cSdlg 		prio = txprio;
299f1dea89cSdlg 		break;
300f1dea89cSdlg 	}
301f1dea89cSdlg 
302f1dea89cSdlg 	shim.shim_label = rtmpls->mpls_label | htonl(prio << MPLS_EXP_OFFSET) |
303f1dea89cSdlg 	    MPLS_BOS_MASK | htonl(ttl);
3046339f48fSdlg 
3056339f48fSdlg 	m = m_prepend(m, sizeof(shim), M_NOWAIT);
3066339f48fSdlg 	if (m == NULL) {
3076339f48fSdlg 		error = ENOMEM;
3086339f48fSdlg 		goto out;
3096339f48fSdlg 	}
3106339f48fSdlg 	*mtod(m, struct shim_hdr *) = shim;
3116339f48fSdlg 
3126339f48fSdlg 	m = m_prepend(m, slen, M_WAITOK);
3136339f48fSdlg 	if (m == NULL) {
3146339f48fSdlg 		error = ENOMEM;
3156339f48fSdlg 		goto out;
3166339f48fSdlg 	}
3176339f48fSdlg 	memcpy(mtod(m, struct sockaddr *), rt->rt_gateway, slen);
3186339f48fSdlg 	mtod(m, struct sockaddr *)->sa_len = slen; /* to be sure */
319769a1feaSclaudio 
320aa0313afSdlg 	m->m_pkthdr.ph_family = dst->sa_family;
321aa0313afSdlg 
322c38eb4ffSmpi 	error = if_enqueue(ifp, m);
3233dbb74bbSpyr out:
3243dbb74bbSpyr 	if (error)
3253dbb74bbSpyr 		ifp->if_oerrors++;
326b896c3f8Spyr 	return (error);
327b896c3f8Spyr }
328b896c3f8Spyr 
329b896c3f8Spyr int
mpe_set_label(struct mpe_softc * sc,uint32_t label,unsigned int rdomain)330296ad859Sdlg mpe_set_label(struct mpe_softc *sc, uint32_t label, unsigned int rdomain)
331296ad859Sdlg {
332296ad859Sdlg 	int error;
333296ad859Sdlg 
334119f8e2bSdlg 	if (sc->sc_dead)
335119f8e2bSdlg 		return (ENXIO);
336119f8e2bSdlg 
337296ad859Sdlg 	if (sc->sc_smpls.smpls_label) {
338296ad859Sdlg 		/* remove old MPLS route */
339296ad859Sdlg 		rt_ifa_del(&sc->sc_ifa, RTF_MPLS|RTF_LOCAL,
340296ad859Sdlg 		    smplstosa(&sc->sc_smpls), sc->sc_rdomain);
341296ad859Sdlg 	}
342296ad859Sdlg 
343296ad859Sdlg 	/* add new MPLS route */
344296ad859Sdlg 	sc->sc_smpls.smpls_label = label;
345296ad859Sdlg 	sc->sc_rdomain = rdomain;
346296ad859Sdlg 
347f8890659Skn 	/* only install with a label or mpe_clone_destroy() will ignore it */
348f8890659Skn 	if (sc->sc_smpls.smpls_label == MPLS_LABEL2SHIM(0))
349f8890659Skn 		return 0;
350f8890659Skn 
351296ad859Sdlg 	error = rt_ifa_add(&sc->sc_ifa, RTF_MPLS|RTF_LOCAL,
352296ad859Sdlg 	    smplstosa(&sc->sc_smpls), sc->sc_rdomain);
353296ad859Sdlg 	if (error)
354296ad859Sdlg 		sc->sc_smpls.smpls_label = 0;
355296ad859Sdlg 
356296ad859Sdlg 	return (error);
357296ad859Sdlg }
358296ad859Sdlg 
359296ad859Sdlg int
mpe_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)3606a969fa2Sdlg mpe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
361b896c3f8Spyr {
3621313aa79Sdlg 	struct mpe_softc	*sc = ifp->if_softc;
363b896c3f8Spyr 	struct ifreq		*ifr;
364b896c3f8Spyr 	struct shim_hdr		 shim;
36511c698ddSmpi 	int			 error = 0;
366b896c3f8Spyr 
367b896c3f8Spyr 	ifr = (struct ifreq *)data;
368b896c3f8Spyr 	switch (cmd) {
369b896c3f8Spyr 	case SIOCSIFADDR:
370b896c3f8Spyr 		break;
371b896c3f8Spyr 	case SIOCSIFFLAGS:
372b896c3f8Spyr 		if (ifp->if_flags & IFF_UP)
373b896c3f8Spyr 			ifp->if_flags |= IFF_RUNNING;
374b896c3f8Spyr 		else
375b896c3f8Spyr 			ifp->if_flags &= ~IFF_RUNNING;
376b896c3f8Spyr 		break;
377807bde9bSpyr 	case SIOCSIFMTU:
378807bde9bSpyr 		if (ifr->ifr_mtu < MPE_MTU_MIN ||
379807bde9bSpyr 		    ifr->ifr_mtu > MPE_MTU_MAX)
380807bde9bSpyr 			error = EINVAL;
381807bde9bSpyr 		else
382807bde9bSpyr 			ifp->if_mtu = ifr->ifr_mtu;
383807bde9bSpyr 		break;
384b896c3f8Spyr 	case SIOCGETLABEL:
3851313aa79Sdlg 		shim.shim_label = MPLS_SHIM2LABEL(sc->sc_smpls.smpls_label);
386fc9d54dbSdlg 		if (shim.shim_label == 0) {
387fc9d54dbSdlg 			error = EADDRNOTAVAIL;
388fc9d54dbSdlg 			break;
389fc9d54dbSdlg 		}
39082e354c7Spyr 		error = copyout(&shim, ifr->ifr_data, sizeof(shim));
391b896c3f8Spyr 		break;
392b896c3f8Spyr 	case SIOCSETLABEL:
39318e29477Sdlg 		error = copyin(ifr->ifr_data, &shim, sizeof(shim));
39418e29477Sdlg 		if (error != 0)
395b896c3f8Spyr 			break;
396b3bacb7cSmichele 		if (shim.shim_label > MPLS_LABEL_MAX ||
397b3bacb7cSmichele 		    shim.shim_label <= MPLS_LABEL_RESERVED_MAX) {
398b896c3f8Spyr 			error = EINVAL;
399b896c3f8Spyr 			break;
400b896c3f8Spyr 		}
401a8b0ba14Sdlg 		shim.shim_label = MPLS_LABEL2SHIM(shim.shim_label);
402119f8e2bSdlg 		if (sc->sc_smpls.smpls_label != shim.shim_label) {
403119f8e2bSdlg 			error = mpe_set_label(sc, shim.shim_label,
404119f8e2bSdlg 			    sc->sc_rdomain);
405119f8e2bSdlg 		}
406296ad859Sdlg 		break;
407fc9d54dbSdlg 	case SIOCDELLABEL:
408fc9d54dbSdlg 		if (sc->sc_smpls.smpls_label != MPLS_LABEL2SHIM(0)) {
409fc9d54dbSdlg 			rt_ifa_del(&sc->sc_ifa, RTF_MPLS|RTF_LOCAL,
410fc9d54dbSdlg 			    smplstosa(&sc->sc_smpls), sc->sc_rdomain);
411fc9d54dbSdlg 		}
412b37ade80Skn 		sc->sc_smpls.smpls_label = MPLS_LABEL2SHIM(0);
413fc9d54dbSdlg 		break;
414fc9d54dbSdlg 
415296ad859Sdlg 	case SIOCSLIFPHYRTABLE:
416296ad859Sdlg 		if (ifr->ifr_rdomainid < 0 ||
417296ad859Sdlg 		    ifr->ifr_rdomainid > RT_TABLEID_MAX ||
418296ad859Sdlg 		    !rtable_exists(ifr->ifr_rdomainid) ||
419296ad859Sdlg 		    ifr->ifr_rdomainid != rtable_l2(ifr->ifr_rdomainid)) {
420296ad859Sdlg 			error = EINVAL;
421769a1feaSclaudio 			break;
422106d177cSmpi 		}
423119f8e2bSdlg 		if (sc->sc_rdomain != ifr->ifr_rdomainid) {
424296ad859Sdlg 			error = mpe_set_label(sc, sc->sc_smpls.smpls_label,
425296ad859Sdlg 			    ifr->ifr_rdomainid);
426119f8e2bSdlg 		}
427296ad859Sdlg 		break;
428296ad859Sdlg 	case SIOCGLIFPHYRTABLE:
429296ad859Sdlg 		ifr->ifr_rdomainid = sc->sc_rdomain;
430296ad859Sdlg 		break;
431296ad859Sdlg 
432f1dea89cSdlg 	case SIOCSTXHPRIO:
433b9e5cef3Sdlg 		error = if_txhprio_l3_check(ifr->ifr_hdrprio);
434b9e5cef3Sdlg 		if (error != 0)
435f1dea89cSdlg 			break;
436f1dea89cSdlg 
437f1dea89cSdlg 		sc->sc_txhprio = ifr->ifr_hdrprio;
438f1dea89cSdlg 		break;
439f1dea89cSdlg 	case SIOCGTXHPRIO:
440f1dea89cSdlg 		ifr->ifr_hdrprio = sc->sc_txhprio;
441f1dea89cSdlg 		break;
442f1dea89cSdlg 
443a61f5d85Sdlg 	case SIOCSRXHPRIO:
444b9e5cef3Sdlg 		error = if_rxhprio_l3_check(ifr->ifr_hdrprio);
445b9e5cef3Sdlg 		if (error != 0)
446a61f5d85Sdlg 			break;
447a61f5d85Sdlg 
448a61f5d85Sdlg 		sc->sc_rxhprio = ifr->ifr_hdrprio;
449a61f5d85Sdlg 		break;
450a61f5d85Sdlg 	case SIOCGRXHPRIO:
451a61f5d85Sdlg 		ifr->ifr_hdrprio = sc->sc_rxhprio;
452a61f5d85Sdlg 		break;
453a61f5d85Sdlg 
454b896c3f8Spyr 	default:
455b896c3f8Spyr 		return (ENOTTY);
456b896c3f8Spyr 	}
457b896c3f8Spyr 
458b896c3f8Spyr 	return (error);
459b896c3f8Spyr }
4605aa9b43bSpyr 
4615aa9b43bSpyr void
mpe_input(struct ifnet * ifp,struct mbuf * m)462c6de5097Sdlg mpe_input(struct ifnet *ifp, struct mbuf *m)
4635aa9b43bSpyr {
464a61f5d85Sdlg 	struct mpe_softc *sc = ifp->if_softc;
465c6de5097Sdlg 	struct shim_hdr	*shim;
466b63ffc37Sdlg 	struct mbuf	*n;
467a61f5d85Sdlg 	uint8_t		 ttl, tos;
468a61f5d85Sdlg 	uint32_t	 exp;
469a61f5d85Sdlg 	int rxprio = sc->sc_rxhprio;
4705aa9b43bSpyr 
471c6de5097Sdlg 	shim = mtod(m, struct shim_hdr *);
472a61f5d85Sdlg 	exp = ntohl(shim->shim_label & MPLS_EXP_MASK) >> MPLS_EXP_OFFSET;
473b63ffc37Sdlg 	if (!MPLS_BOS_ISSET(shim->shim_label))
474b63ffc37Sdlg 		goto drop;
475b63ffc37Sdlg 
476c6de5097Sdlg 	ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
477c6de5097Sdlg 	m_adj(m, sizeof(*shim));
478c6de5097Sdlg 
479b63ffc37Sdlg 	n = m;
480b63ffc37Sdlg 	while (n->m_len == 0) {
481b63ffc37Sdlg 		n = n->m_next;
482b63ffc37Sdlg 		if (n == NULL)
483b63ffc37Sdlg 			goto drop;
484b63ffc37Sdlg 	}
485b63ffc37Sdlg 
486b63ffc37Sdlg 	switch (*mtod(n, uint8_t *) >> 4) {
487a61f5d85Sdlg 	case 4: {
488a61f5d85Sdlg 		struct ip *ip;
489a61f5d85Sdlg 		if (m->m_len < sizeof(*ip)) {
490a61f5d85Sdlg 			m = m_pullup(m, sizeof(*ip));
491a61f5d85Sdlg 			if (m == NULL)
492a61f5d85Sdlg 				return;
493a61f5d85Sdlg 		}
494a61f5d85Sdlg 		ip = mtod(m, struct ip *);
495a61f5d85Sdlg 		tos = ip->ip_tos;
496a61f5d85Sdlg 
497b63ffc37Sdlg 		if (mpls_mapttl_ip) {
49848e57f09Sdlg 			m = mpls_ip_adjttl(m, ttl);
499c6de5097Sdlg 			if (m == NULL)
500c6de5097Sdlg 				return;
501c6de5097Sdlg 		}
5022e3db693Sdlg 
503b63ffc37Sdlg 		m->m_pkthdr.ph_family = AF_INET;
504b63ffc37Sdlg 		break;
505a61f5d85Sdlg 	}
506b63ffc37Sdlg #ifdef INET6
507a61f5d85Sdlg 	case 6: {
508a61f5d85Sdlg 		struct ip6_hdr *ip6;
509a61f5d85Sdlg 		uint32_t flow;
510a61f5d85Sdlg 		if (m->m_len < sizeof(*ip6)) {
511a61f5d85Sdlg 			m = m_pullup(m, sizeof(*ip6));
512a61f5d85Sdlg 			if (m == NULL)
513a61f5d85Sdlg 				return;
514a61f5d85Sdlg 		}
515a61f5d85Sdlg 		ip6 = mtod(m, struct ip6_hdr *);
516a61f5d85Sdlg 		flow = bemtoh32(&ip6->ip6_flow);
517a61f5d85Sdlg 		tos = flow >> 20;
518a61f5d85Sdlg 
519b63ffc37Sdlg 		if (mpls_mapttl_ip6) {
52048e57f09Sdlg 			m = mpls_ip6_adjttl(m, ttl);
521b63ffc37Sdlg 			if (m == NULL)
522b63ffc37Sdlg 				return;
523b63ffc37Sdlg 		}
5242e3db693Sdlg 
525b63ffc37Sdlg 		m->m_pkthdr.ph_family = AF_INET6;
526b63ffc37Sdlg 		break;
527a61f5d85Sdlg 	}
528b63ffc37Sdlg #endif /* INET6 */
529b63ffc37Sdlg 	default:
530b63ffc37Sdlg 		goto drop;
5318b3356d2Smichele 	}
5328b3356d2Smichele 
533a61f5d85Sdlg 	switch (rxprio) {
534a61f5d85Sdlg 	case IF_HDRPRIO_PACKET:
535a61f5d85Sdlg 		/* nop */
536a61f5d85Sdlg 		break;
537a61f5d85Sdlg 	case IF_HDRPRIO_OUTER:
538a61f5d85Sdlg 		m->m_pkthdr.pf.prio = exp;
539a61f5d85Sdlg 		break;
540a61f5d85Sdlg 	case IF_HDRPRIO_PAYLOAD:
541a61f5d85Sdlg 		m->m_pkthdr.pf.prio = IFQ_TOS2PRIO(tos);
542a61f5d85Sdlg 		break;
543a61f5d85Sdlg 	default:
544a61f5d85Sdlg 		m->m_pkthdr.pf.prio = rxprio;
545a61f5d85Sdlg 		break;
546a61f5d85Sdlg 	}
547a61f5d85Sdlg 
5482e3db693Sdlg 	if_vinput(ifp, m);
549b63ffc37Sdlg 	return;
550b63ffc37Sdlg drop:
551b63ffc37Sdlg 	m_freem(m);
5525aa9b43bSpyr }
553