Index | Thread | Search

From:
David Gwynne <david@gwynne.id.au>
Subject:
refactor mpls interface input
To:
tech@openbsd.org
Date:
Tue, 25 Nov 2025 12:32:22 +1000

Download raw body.

Thread
this hoists the mpls interface input processing up a bit in the mpls
label processing.

mpw, mpip, and mpe all add entries to the local mpls fib that points to
themselves, and when these labels are "output" via these interfaces they
then go and push the packets into their input processing. this is all
boilerplate, so it can be factored out and better integrated into the
larger network stack. in particular, we can pass struct netstack through
to the input handlers.

there's some small downsides to this. the main one is that using
if_vinput to dispatch to their input handlers means the vinput handling
has to cope with mpls encapsulated packets. this is easy except for mpw,
where ether_ifattach does a lot of setup that has to be tweaked for mpls
encapsualted ethernet packets.

while here, this changes mpe output so it doesnt have to prepend the
mbuf with the sockaddr it uses as the nexthop on the underlay. it only
had to do that to carry the information across the ifq. if we just don't
use ifq for output then this gets simplified a lot. the only downside is
that you can't use altq on mpe interfaces after this. i dont think this
is a huge loss.

ok?

Index: net/if_mpe.c
===================================================================
RCS file: /cvs/src/sys/net/if_mpe.c,v
diff -u -p -r1.107 if_mpe.c
--- net/if_mpe.c	7 Jul 2025 02:28:50 -0000	1.107
+++ net/if_mpe.c	25 Nov 2025 00:58:24 -0000
@@ -52,6 +52,7 @@
 
 struct mpe_softc {
 	struct ifnet		sc_if;		/* the interface */
+	caddr_t			sc_bpf;
 	int			sc_txhprio;
 	int			sc_rxhprio;
 	unsigned int		sc_rdomain;
@@ -73,7 +74,7 @@ int	mpe_ioctl(struct ifnet *, u_long, ca
 void	mpe_start(struct ifnet *);
 int	mpe_clone_create(struct if_clone *, int);
 int	mpe_clone_destroy(struct ifnet *);
-void	mpe_input(struct ifnet *, struct mbuf *);
+void	mpe_input(struct ifnet *, struct mbuf *, struct netstack *);
 
 struct if_clone	mpe_cloner =
     IF_CLONE_INITIALIZER("mpe", mpe_clone_create, mpe_clone_destroy);
@@ -106,8 +107,8 @@ mpe_clone_create(struct if_clone *ifc, i
 	ifp->if_softc = sc;
 	ifp->if_mtu = MPE_MTU;
 	ifp->if_ioctl = mpe_ioctl;
-	ifp->if_bpf_mtap = p2p_bpf_mtap;
-	ifp->if_input = p2p_input;
+	ifp->if_bpf_mtap = bpf_mtap;
+	ifp->if_input = mpe_input;
 	ifp->if_output = mpe_output;
 	ifp->if_start = mpe_start;
 	ifp->if_type = IFT_MPLS;
@@ -120,7 +121,8 @@ mpe_clone_create(struct if_clone *ifc, i
 	if_alloc_sadl(ifp);
 
 #if NBPFILTER > 0
-	bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(u_int32_t));
+	bpfattach(&sc->sc_bpf, ifp, DLT_LOOP, sizeof(u_int32_t));
+	bpfattach(&ifp->if_bpf, ifp, DLT_MPLS, 0);
 #endif
 
 	sc->sc_txhprio = 0;
@@ -167,76 +169,31 @@ mpe_clone_destroy(struct ifnet *ifp)
 void
 mpe_start(struct ifnet *ifp)
 {
-	struct mpe_softc	*sc = ifp->if_softc;
-	struct mbuf		*m;
-	struct sockaddr		*sa;
-	struct sockaddr		smpls = { .sa_family = AF_MPLS };
-	struct rtentry		*rt;
-	struct ifnet		*ifp0;
-
-	while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
-		sa = mtod(m, struct sockaddr *);
-		rt = rtalloc(sa, RT_RESOLVE, sc->sc_rdomain);
-		if (!rtisvalid(rt)) {
-			m_freem(m);
-			rtfree(rt);
-			continue;
-		}
-
-		ifp0 = if_get(rt->rt_ifidx);
-		if (ifp0 == NULL) {
-			m_freem(m);
-			rtfree(rt);
-			continue;
-		}
-
-		m_adj(m, sa->sa_len);
-
-#if NBPFILTER > 0
-		if (ifp->if_bpf) {
-			/* remove MPLS label before passing packet to bpf */
-			m->m_data += sizeof(struct shim_hdr);
-			m->m_len -= sizeof(struct shim_hdr);
-			m->m_pkthdr.len -= sizeof(struct shim_hdr);
-			bpf_mtap_af(ifp->if_bpf, m->m_pkthdr.ph_family,
-			    m, BPF_DIRECTION_OUT);
-			m->m_data -= sizeof(struct shim_hdr);
-			m->m_len += sizeof(struct shim_hdr);
-			m->m_pkthdr.len += sizeof(struct shim_hdr);
-		}
-#endif
-
-		m->m_pkthdr.ph_rtableid = sc->sc_rdomain;
-		CLR(m->m_flags, M_BCAST|M_MCAST);
-
-		mpls_output(ifp0, m, &smpls, rt);
-		if_put(ifp0);
-		rtfree(rt);
-	}
+	ifq_purge(&ifp->if_snd);
 }
 
 int
 mpe_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
-	struct rtentry *rt)
+    struct rtentry *rt)
 {
-	struct mpe_softc *sc;
+	struct mpe_softc *sc = ifp->if_softc;
 	struct rt_mpls	*rtmpls;
+	struct ifnet	*ifp0 = NULL;
+	struct rtentry	*rt0 = NULL;
+	struct sockaddr	smpls = { .sa_family = AF_MPLS };
 	struct shim_hdr	shim;
-	int		error;
 	int		txprio;
 	uint8_t		ttl = mpls_defttl;
 	uint8_t		tos, prio;
 	size_t		ttloff;
-	socklen_t	slen;
+	int		error;
+#if NBPFILTER > 0
+	caddr_t		if_bpf;
+#endif
 
 	if (!rtisvalid(rt) || !ISSET(rt->rt_flags, RTF_MPLS)) {
-		m_freem(m);
-		return (ENETUNREACH);
-	}
-
-	if (dst->sa_family == AF_LINK && ISSET(rt->rt_flags, RTF_LOCAL)) {
-		mpe_input(ifp, m);
-		return (0);
+		error = ENETUNREACH;
+		goto drop;
 	}
 
 #ifdef DIAGNOSTIC
@@ -249,8 +206,8 @@ mpe_output(struct ifnet *ifp, struct mbu
 
 	rtmpls = (struct rt_mpls *)rt->rt_llinfo;
 	if (rtmpls->mpls_operation != MPLS_OP_PUSH) {
-		m_freem(m);
-		return (ENETUNREACH);
+		error = ENETUNREACH;
+		goto drop;
 	}
 
 	error = 0;
@@ -259,7 +216,6 @@ mpe_output(struct ifnet *ifp, struct mbu
 		struct ip *ip = mtod(m, struct ip *);
 		tos = ip->ip_tos;
 		ttloff = offsetof(struct ip, ip_ttl);
-		slen = sizeof(struct sockaddr_in);
 		break;
 	}
 #ifdef INET6
@@ -268,15 +224,35 @@ mpe_output(struct ifnet *ifp, struct mbu
 		uint32_t flow = bemtoh32(&ip6->ip6_flow);
 		tos = flow >> 20;
 		ttloff = offsetof(struct ip6_hdr, ip6_hlim);
-		slen = sizeof(struct sockaddr_in6);
 		break;
 	}
 #endif
 	default:
-		m_freem(m);
-		return (EPFNOSUPPORT);
+		error = EPFNOSUPPORT;
+		goto drop;
+	}
+
+	rt0 = rtalloc(rt->rt_gateway, RT_RESOLVE, sc->sc_rdomain);
+	if (!rtisvalid(rt0)) {
+		error = EHOSTUNREACH;
+		goto rtfree;
 	}
 
+	ifp0 = if_get(rt0->rt_ifidx);
+	if (ifp0 == NULL) {
+		error = ENETUNREACH;
+		goto rtfree;
+	}
+
+	m->m_pkthdr.ph_rtableid = sc->sc_rdomain;
+	CLR(m->m_flags, M_BCAST|M_MCAST);
+
+#if NBPFILTER > 0
+	if_bpf = sc->sc_bpf;
+	if (if_bpf)
+		bpf_mtap_af(if_bpf, dst->sa_family, m, BPF_DIRECTION_OUT);
+#endif
+
 	if (mpls_mapttl_ip) {
 		/* assumes the ip header is already contig */
 		ttl = *(mtod(m, uint8_t *) + ttloff);
@@ -302,25 +278,27 @@ mpe_output(struct ifnet *ifp, struct mbu
 
 	m = m_prepend(m, sizeof(shim), M_NOWAIT);
 	if (m == NULL) {
-		error = ENOMEM;
-		goto out;
+		error = ENOBUFS;
+		goto ifput;
 	}
 	*mtod(m, struct shim_hdr *) = shim;
 
-	m = m_prepend(m, slen, M_WAITOK);
-	if (m == NULL) {
-		error = ENOMEM;
-		goto out;
-	}
-	memcpy(mtod(m, struct sockaddr *), rt->rt_gateway, slen);
-	mtod(m, struct sockaddr *)->sa_len = slen; /* to be sure */
+#if NBPFILTER > 0
+	if_bpf = ifp->if_bpf;
+		bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
+#endif
 
-	m->m_pkthdr.ph_family = dst->sa_family;
+	mpls_output(ifp0, m, &smpls, rt0);
+	if_put(ifp0);
+	rtfree(rt0);
+	return (0);
 
-	error = if_enqueue(ifp, m);
-out:
-	if (error)
-		ifp->if_oerrors++;
+ifput:
+	if_put(ifp0);
+rtfree:
+	rtfree(rt0);
+drop:
+	m_freem(m);
 	return (error);
 }
 
@@ -457,7 +435,7 @@ mpe_ioctl(struct ifnet *ifp, u_long cmd,
 }
 
 void
-mpe_input(struct ifnet *ifp, struct mbuf *m)
+mpe_input(struct ifnet *ifp, struct mbuf *m, struct netstack *ns)
 {
 	struct mpe_softc *sc = ifp->if_softc;
 	struct shim_hdr	*shim;
@@ -465,6 +443,9 @@ mpe_input(struct ifnet *ifp, struct mbuf
 	uint8_t		 ttl, tos;
 	uint32_t	 exp;
 	int rxprio = sc->sc_rxhprio;
+#if NBPFILTER > 0
+	caddr_t		 if_bpf;
+#endif
 
 	shim = mtod(m, struct shim_hdr *);
 	exp = ntohl(shim->shim_label & MPLS_EXP_MASK) >> MPLS_EXP_OFFSET;
@@ -543,7 +524,15 @@ mpe_input(struct ifnet *ifp, struct mbuf
 		break;
 	}
 
-	if_vinput(ifp, m, NULL);
+#if NBPFILTER > 0
+	if_bpf = sc->sc_bpf;
+	if (if_bpf) {
+		bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family, m,
+		    BPF_DIRECTION_IN);
+	}
+#endif
+
+	p2p_input(ifp, m, ns);
 	return;
 drop:
 	m_freem(m);
Index: net/if_mpip.c
===================================================================
RCS file: /cvs/src/sys/net/if_mpip.c,v
diff -u -p -r1.20 if_mpip.c
--- net/if_mpip.c	2 Mar 2025 21:28:32 -0000	1.20
+++ net/if_mpip.c	25 Nov 2025 00:58:24 -0000
@@ -52,6 +52,7 @@ struct mpip_neighbor {
 
 struct mpip_softc {
 	struct ifnet		sc_if;
+	caddr_t			sc_bpf;
 	unsigned int		sc_dead;
 	uint32_t		sc_flow; /* xor for mbuf flowid */
 
@@ -71,6 +72,7 @@ void	mpipattach(int);
 int	mpip_clone_create(struct if_clone *, int);
 int	mpip_clone_destroy(struct ifnet *);
 int	mpip_ioctl(struct ifnet *, u_long, caddr_t);
+void	mpip_input(struct ifnet *, struct mbuf *, struct netstack *);
 int	mpip_output(struct ifnet *, struct mbuf *, struct sockaddr *,
 	    struct rtentry *);
 void	mpip_start(struct ifnet *);
@@ -112,8 +114,8 @@ mpip_clone_create(struct if_clone *ifc, 
 	ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
 	ifp->if_xflags = IFXF_CLONED;
 	ifp->if_ioctl = mpip_ioctl;
-	ifp->if_bpf_mtap = p2p_bpf_mtap;
-	ifp->if_input = p2p_input;
+	ifp->if_bpf_mtap = bpf_mtap;
+	ifp->if_input = mpip_input;
 	ifp->if_output = mpip_output;
 	ifp->if_start = mpip_start;
 	ifp->if_rtrequest = p2p_rtrequest;
@@ -125,7 +127,8 @@ mpip_clone_create(struct if_clone *ifc, 
 	if_alloc_sadl(ifp);
 
 #if NBPFILTER > 0
-	bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t));
+	bpfattach(&sc->sc_bpf, ifp, DLT_LOOP, sizeof(uint32_t));
+	bpfattach(&ifp->if_bpf, ifp, DLT_MPLS, 0);
 #endif
 
 	refcnt_init_trace(&sc->sc_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
@@ -462,14 +465,17 @@ mpip_ioctl(struct ifnet *ifp, u_long cmd
 	return (error);
 }
 
-static void
-mpip_input(struct mpip_softc *sc, struct mbuf *m)
+void
+mpip_input(struct ifnet *ifp, struct mbuf *m, struct netstack *ns)
 {
-	struct ifnet *ifp = &sc->sc_if;
+	struct mpip_softc *sc = ifp->if_softc;
 	int rxprio = sc->sc_rxhprio;
 	uint32_t shim, exp;
 	struct mbuf *n;
 	uint8_t ttl, tos;
+#if NBPFILTER > 0
+	caddr_t if_bpf;
+#endif
 
 	if (!ISSET(ifp->if_flags, IFF_RUNNING))
 		goto drop;
@@ -608,7 +614,16 @@ mpip_input(struct mpip_softc *sc, struct
 		break;
 	}
 
-	if_vinput(ifp, m, NULL);
+#if NBPFILTER > 0
+	if_bpf = sc->sc_bpf;
+	if (if_bpf) {
+		if (bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family,
+		    m, BPF_DIRECTION_OUT))
+			goto drop;
+	}
+#endif /* NBPFILTER */
+
+	p2p_input(ifp, m, ns);
 	return;
 drop:
 	m_freem(m);
@@ -618,15 +633,8 @@ int
 mpip_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
     struct rtentry *rt)
 {
-	struct mpip_softc *sc = ifp->if_softc;
 	int error;
 
-	if (dst->sa_family == AF_LINK &&
-	    rt != NULL && ISSET(rt->rt_flags, RTF_LOCAL)) {
-		mpip_input(sc, m);
-		return (0);
-	}
-
 	if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
 		error = ENETDOWN;
 		goto drop;
@@ -691,7 +699,7 @@ mpip_start(struct ifnet *ifp)
 
 	while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
 #if NBPFILTER > 0
-		caddr_t if_bpf = sc->sc_if.if_bpf;
+		caddr_t if_bpf = sc->sc_bpf;
 		if (if_bpf) {
 			bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family,
 			    m, BPF_DIRECTION_OUT);
@@ -795,6 +803,12 @@ mpip_start(struct ifnet *ifp)
 
 		m->m_pkthdr.ph_rtableid = sc->sc_rdomain;
 		CLR(m->m_flags, M_BCAST|M_MCAST);
+
+#if NBPFILTER > 0
+		if_bpf = ifp->if_bpf;
+		if (if_bpf)
+			bpf_mtap(if_bpf, m, BPF_DIRECTION_OUT);
+#endif /* NBPFILTER */
 
 		mpls_output(ifp0, m, (struct sockaddr *)&smpls, rt);
 	}
Index: net/if_mpw.c
===================================================================
RCS file: /cvs/src/sys/net/if_mpw.c,v
diff -u -p -r1.68 if_mpw.c
--- net/if_mpw.c	7 Jul 2025 02:28:50 -0000	1.68
+++ net/if_mpw.c	25 Nov 2025 00:58:24 -0000
@@ -46,6 +46,7 @@ struct mpw_neighbor {
 struct mpw_softc {
 	struct arpcom		sc_ac;
 #define sc_if			sc_ac.ac_if
+	caddr_t			sc_bpf;
 
 	int			sc_txhprio;
 	int			sc_rxhprio;
@@ -66,9 +67,8 @@ void	mpwattach(int);
 int	mpw_clone_create(struct if_clone *, int);
 int	mpw_clone_destroy(struct ifnet *);
 int	mpw_ioctl(struct ifnet *, u_long, caddr_t);
-int	mpw_output(struct ifnet *, struct mbuf *, struct sockaddr *,
-    struct rtentry *);
 void	mpw_start(struct ifnet *);
+void	mpw_input(struct ifnet *, struct mbuf *, struct netstack *);
 
 struct if_clone mpw_cloner =
     IF_CLONE_INITIALIZER("mpw", mpw_clone_create, mpw_clone_destroy);
@@ -89,35 +89,43 @@ mpw_clone_create(struct if_clone *ifc, i
 	if (sc == NULL)
 		return (ENOMEM);
 
+	ifp = &sc->sc_if;
+
 	sc->sc_flow = arc4random();
 	sc->sc_neighbor = NULL;
 
-	ifp = &sc->sc_if;
+	sc->sc_txhprio = 0;
+	sc->sc_rxhprio = IF_HDRPRIO_PACKET;
+	sc->sc_rdomain = 0;
+	refcnt_init_trace(&sc->sc_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
+	sc->sc_ifa.ifa_ifp = ifp;
+	sc->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
+	sc->sc_smpls.smpls_len = sizeof(sc->sc_smpls);
+	sc->sc_smpls.smpls_family = AF_MPLS;
+
 	snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
 	    ifc->ifc_name, unit);
 	ifp->if_softc = sc;
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_xflags = IFXF_CLONED;
 	ifp->if_ioctl = mpw_ioctl;
-	ifp->if_output = mpw_output;
 	ifp->if_start = mpw_start;
 	ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN;
 	ether_fakeaddr(ifp);
 
 	sc->sc_dead = 0;
 
-	if_counters_alloc(ifp);
-	if_attach(ifp);
 	ether_ifattach(ifp);
+	ifp->if_input = mpw_input;
 
-	sc->sc_txhprio = 0;
-	sc->sc_rxhprio = IF_HDRPRIO_PACKET;
-	sc->sc_rdomain = 0;
-	refcnt_init_trace(&sc->sc_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
-	sc->sc_ifa.ifa_ifp = ifp;
-	sc->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
-	sc->sc_smpls.smpls_len = sizeof(sc->sc_smpls);
-	sc->sc_smpls.smpls_family = AF_MPLS;
+#if NBPFILTER > 0
+	bpfdetach(ifp); /* undo bpfattach in ether_ifattach */
+	bpfattach(&sc->sc_bpf, ifp, DLT_EN10MB, ETHER_HDR_LEN);
+	bpfattach(&ifp->if_bpf, ifp, DLT_MPLS, 0);
+#endif
+
+	if_counters_alloc(ifp);
+	if_attach(ifp);
 
 	return (0);
 }
@@ -505,15 +513,18 @@ mpw_ioctl(struct ifnet *ifp, u_long cmd,
 	return (error);
 }
 
-static void
-mpw_input(struct mpw_softc *sc, struct mbuf *m)
+void
+mpw_input(struct ifnet *ifp, struct mbuf *m, struct netstack *ns)
 {
-	struct ifnet *ifp = &sc->sc_if;
+	struct mpw_softc *sc = ifp->if_softc;
 	struct shim_hdr *shim;
 	struct mbuf *n;
 	uint32_t exp;
 	int rxprio;
 	int off;
+#if NBPFILTER > 0
+	caddr_t if_bpf;
+#endif
 
 	if (!ISSET(ifp->if_flags, IFF_RUNNING))
 		goto drop;
@@ -613,27 +624,18 @@ mpw_input(struct mpw_softc *sc, struct m
 	/* packet has not been processed by PF yet. */
 	KASSERT(m->m_pkthdr.pf.statekey == NULL);
 
-	if_vinput(ifp, m, NULL);
+#if NBPFILTER > 0
+	if_bpf = sc->sc_bpf;
+	if (if_bpf)
+		bpf_mtap(if_bpf, m, BPF_DIRECTION_OUT);
+#endif
+
+	ether_input(ifp, m, ns);
 	return;
 drop:
 	m_freem(m);
 }
 
-int
-mpw_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
-    struct rtentry *rt)
-{
-	struct mpw_softc *sc = ifp->if_softc;
-
-	if (dst->sa_family == AF_LINK &&
-	    rt != NULL && ISSET(rt->rt_flags, RTF_LOCAL)) {
-		mpw_input(sc, m);
-		return (0);
-	}
-
-	return (ether_output(ifp, m, dst, rt));
-}
-
 void
 mpw_start(struct ifnet *ifp)
 {
@@ -672,8 +674,11 @@ mpw_start(struct ifnet *ifp)
 
 	while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
 #if NBPFILTER > 0
-		if (sc->sc_if.if_bpf)
-			bpf_mtap(sc->sc_if.if_bpf, m, BPF_DIRECTION_OUT);
+		caddr_t if_bpf;
+
+		if_bpf = sc->sc_bpf;
+		if (if_bpf)
+			bpf_mtap(if_bpf, m, BPF_DIRECTION_OUT);
 #endif /* NBPFILTER */
 
 		m0 = m_get(M_DONTWAIT, m->m_type);
@@ -731,6 +736,12 @@ mpw_start(struct ifnet *ifp)
 		shim = mtod(m0, struct shim_hdr *);
 		shim->shim_label = htonl(mpls_defttl) & MPLS_TTL_MASK;
 		shim->shim_label |= n->n_rshim.shim_label | exp | bos;
+
+#if NBPFILTER > 0
+		if_bpf = ifp->if_bpf;
+		if (if_bpf)
+			bpf_mtap(if_bpf, m, BPF_DIRECTION_OUT);
+#endif /* NBPFILTER */
 
 		m0->m_pkthdr.ph_rtableid = sc->sc_rdomain;
 		CLR(m0->m_flags, M_BCAST|M_MCAST);
Index: netmpls/mpls_input.c
===================================================================
RCS file: /cvs/src/sys/netmpls/mpls_input.c,v
diff -u -p -r1.80 mpls_input.c
--- netmpls/mpls_input.c	2 Mar 2025 21:28:32 -0000	1.80
+++ netmpls/mpls_input.c	25 Nov 2025 00:58:24 -0000
@@ -44,7 +44,8 @@
 #endif
 
 struct mbuf	*mpls_do_error(struct mbuf *, int, int, int);
-void		 mpls_input_local(struct rtentry *, struct mbuf *);
+static void	 mpls_input_local(struct rtentry *, struct mbuf *,
+		     struct netstack *);
 
 void
 mpls_input(struct ifnet *ifp, struct mbuf *m, struct netstack *ns)
@@ -186,7 +187,7 @@ do_v6:
 	switch (rt_mpls->mpls_operation) {
 	case MPLS_OP_POP:
 		if (ISSET(rt->rt_flags, RTF_LOCAL)) {
-			mpls_input_local(rt, m);
+			mpls_input_local(rt, m, ns);
 			goto done;
 		}
 
@@ -277,8 +278,8 @@ done:
 	rtfree(rt);
 }
 
-void
-mpls_input_local(struct rtentry *rt, struct mbuf *m)
+static void
+mpls_input_local(struct rtentry *rt, struct mbuf *m, struct netstack *ns)
 {
 	struct ifnet *ifp;
 
@@ -288,12 +289,7 @@ mpls_input_local(struct rtentry *rt, str
 		return;
 	}
 
-	/* shortcut sending out the packet */
-	if (!ISSET(ifp->if_xflags, IFXF_MPLS))
-		(*ifp->if_output)(ifp, m, rt->rt_gateway, rt);
-	else
-		(*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt);
-
+	if_vinput(ifp, m, ns);
 	if_put(ifp);
 }