Update: MPLS: Bugfixes: mpls_rmx.c and shortcut

Major cleanup of MPLS based extensions targeting netinet/if_ether.c and
code sections in netmpls/mpls_rmx.c reimplemented.

In netmpls/mpls_arp.c defined mpls_arpresolve() reimplemented entirely.

int
mpls_arpresolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m,
	struct sockaddr *dst, u_char *lla, struct llentry **lle)
{
	int error = EINVAL;

	if ((dst == NULL) || (ifp == NULL)
		|| ((ifp->if_flags & IFF_MPLS) == 0)) {
		m_freem(m);
		return (error);
	}

	if (mpls_arp_in_fec != 0) {
	    if (rt0 != NULL && (rt0->rt_flags & (RTF_MPE|RTF_MPLS)))
	        error = mpls_arpresolve_fec_internal(ifp, rt0, m,
	            dst, lla, lle);
	} else
	    error = mpls_arpresolve_internal(ifp, rt0, m, dst, lla, lle);

    return (error);
}

If

    # sysctl net.link.ether.mpls.use_arp_in_fec=1

may set,

static int
mpls_arpresolve_fec_internal(struct ifnet *ifp, struct rtentry *rt,
        struct mbuf *m, struct sockaddr *dst, u_char *lla,
            struct llentry **lle)
{
	struct sockaddr_storage ss = { 0 };
	struct sockaddr *gateway = rt->rt_gateway;
	int error = EINVAL;

	/*
	 * NH in domain of FEC MPLS routes is got from
	 * annotation behind FTN.
	 */
	if (gateway->sa_family == AF_MPLS)
		gateway = (struct sockaddr *)(satosftn(dst) + 1);

	switch (gateway->sa_family) {
	case AF_INET:
	    bcopy(gateway, &ss, sizeof(struct sockaddr_in));
		error = arpresolve(ifp, rt, m, (struct sockaddr *)&ss,
		    lla, lle);
		break;
	case AF_INET6:
	    bcopy(gateway, &ss, sizeof(struct sockaddr_in6));
		error = nd6_storelladdr(ifp, m, (struct sockaddr *)&ss,
		    (u_char *)lla, lle);
		break;
	default:
		error = EAFNOSUPPORT;
		break;
	}
	return (error);
}

is called for shortcut. Implicitely, code section

	if ((rt0 != NULL) && (rt0->rt_flags & (RTF_MPE|RTF_SWAP)))
	    m->m_flags |= M_MPLS;
	else
	    m->m_flags &= ~M_MPLS;

in arpresolve() (see netinet/if_ether.c) annotates MPI by M_MPLS,
if route denotes either ingress or P (LSR) route. Unsetting M_MPLS
as default should ensure to avoid side effects, if by llentry{}
cached MPI does not in AF_MPLS.

	if (la->la_hold != NULL) {
		struct mbuf *m_hold, *m_hold_next;
		struct route mplsroute = { 0 };
		struct route *ro = NULL;
		struct sockaddr *sa0 = NULL;

		m_hold = la->la_hold;
		la->la_hold = NULL;
		la->la_numheld = 0;
		memcpy(&sa, L3_ADDR(la), sizeof(sa));
		LLE_WUNLOCK(la);

		for (; m_hold != NULL; m_hold = m_hold_next) {
			m_hold_next = m_hold->m_nextpkt;
			m_hold->m_nextpkt = NULL;
			if (m_hold->m_flags & M_MPLS) {
	            ro = &mplsroute;
	            bzero(ro, sizeof(ro));
	/*
	 * Allocate by FEC generated MPLS route.
	 */
				mpls_in_rtalloc(ro, &m_hold);
				if (m_hold == NULL)
				    continue;

				if (((rt = ro->ro_rt) == NULL)
				    || (rt->rt_gateway == NULL)
					|| ((rt->rt_flags & RTF_UP) == 0)
				    || (rt->rt_ifp == NULL)) {
				    m_freem(m_hold);
				    continue;
				}
	/*
	 * Use particular LSP in conjunction with former allocated
	 * route to transmit MPI in AF_MPLS.
	 */
				sa0 = (rt->rt_flags & RTF_MPE) ?
				    &ro->ro_dst : rt->rt_gateway;
	/*
	 * Finally, perform shortcut...
	 */
	            ifatomrt(rt->rt_ifa)->mrt_shortcut = la;
	/*
	 * and remove annotation.
	 */
	            m_hold->m_flags &= ~M_MPLS;
            } else {
		        sa0 = &sa;
				ro = NULL;
			}
		    (*ifp->if_output)(ifp, m_hold, sa0, ro);

			if (ro == &mplsroute)
			    RO_RTFREE(ro);
			(*ifp->if_output)(ifp, m_hold, &sa, NULL);
		}
	} else
		LLE_WUNLOCK(la);

ensures, such that by FEC corrospondig llentry{} cached MPI
(caused by arpresolve()) in AF_MPLS may tx in AF_MPLS, during
runtime of in_arpinput(), when ARPOP_REPLY SPI took place.
    By ND6 performed shortcut may implemented completely, soon.
I have to perform misc. software tests before I start playing
with IPv6.

Bugfixes:
---------

Major cleanup of netmpls/mpls.c and netmpls/mpls_rmx.c caused
implemetation as event handler acting

static void
mpls_rtexpunge(int cmd, struct rtentry *rt0, struct rt_addrinfo *info)
{
	struct sockaddr_storage ss;
	struct sockaddr *sa0, *sa;
	struct ifaddr *ifa;
	struct ifnet *ifp;
	struct rtentry *rt;

	RT_LOCK_ASSERT(rt0);

	switch (cmd) {
	case RTM_DELETE:    /* FALLTHROUGH */
	case RTM_CHANGE:

	    sa = (struct sockaddr *)&ss;
	    bzero(sa, sizeof(*sa));
	    sa0 = rt_key(rt0);
	    bcopy(sa0, sa, sa0->sa_len);
		RT_UNLOCK(rt0);
		if ((ifa = mpls_rtifwithfec(sa, 1)) == NULL) {
			RT_LOCK(rt0);
			break;
		}
		ifp = ifa->ifa_ifp;
		bzero(sa, sizeof(*sa));
		sa->sa_len = sizeof(struct sockaddr_mpls);
		sa->sa_family = AF_MPLS;
		satosmpls(sa)->smpls_label =
            satosmpls(ifa->ifa_addr)->smpls_label;
		ifa_free(ifa);

		rt = rtalloc1_fib(sa, 0, 0UL, ifp->if_fib);
		if (rt == NULL) {
		    RT_LOCK(rt0);
		    break;
		}

	    if (rtexpunge(rt) != 0) {
	        RTFREE_LOCKED(rt);
	        RT_LOCK(rt0);
	        break;
	    }
	    RT_LOCK(rt0);
	    ifa_ref(rt0->rt_ifa);
	    ifa = rt0->rt_ifa;
	    ifa->ifa_rtrequest = NULL;
	    ifa_free(ifa);

	    break;
	default:
		break;
	}
}

This procedure is attached at ifaddr{} (denotes FEC) ifa_rtrequest
hook during runtime of mpls_rt_output(). It terminates to FEC
associated MPLS route, if FEC will be removed.

	Aggregate set of routes are treated as host routes, but
by rtsock set up routes are treated as gateway routes. Codesection

	gateway0 = rt0->rt_gateway;
	if ((IS_MPLS_OP(flags, RTF_POP))
		&& (dst->sa_family == AF_MPLS)) {
		nxthp = gateway0;
		lsp = dst;
	} else if ((IS_MPLS_OP(flags, RTF_PUSH))
		&& (gateway->sa_family == AF_MPLS)) {
			flags &= ~RTF_HOST;
			flags |= RTF_MPE;
			nxthp = gateway0;
			lsp = gateway;
	} else if ((IS_MPLS_OP(flags, RTF_SWAP))
		&& (dst->sa_family == AF_MPLS)
		&& (gateway->sa_family == AF_MPLS)) {
		nxthp = gateway;
		lsp = dst;
	} else {
		RTFREE_LOCKED(rt0);
		return (EINVAL);
	}

in mpls_rt_output_fib() will fix this.
	Well, this looks ugly. It implies, that there is a need
to refactor mpls_rt_output_fib() entirely.

	Reimplementation of

void
mpls_in_rtalloc(struct route *ro, struct mbuf **mp)
{
	struct shim_hdr stack[MPLS_INKERNEL_LOOP_MAX];
	struct sockaddr_in fec;
	struct mbuf *m = *mp;
	struct ip *ip = NULL;
	struct sockaddr *sa;
	struct ifaddr *ifa;
	size_t nstk, stacksiz = 0;

	/*
	 * Strip off MPLS label stack and keep a copy.
	 */
	for (nstk = 0; nstk < MPLS_INKERNEL_LOOP_MAX; nstk++) {
		if (m->m_len < MPLS_HDRLEN) {
		    m = m_pullup(m, sizeof(*ip));
		    if (m == NULL)
				goto bad;
		}
		stack[nstk] = *mtod(m, struct shim_hdr *);
		m_adj(m, MPLS_HDRLEN);
		if (MPLS_BOS_ISSET(stack[nstk].shim_label))
			break;
	}
	stacksiz = (nstk + 1) * MPLS_HDRLEN;

	/*
	 * Basic condition tests.
	 */
	if (m->m_pkthdr.len < sizeof(struct ip))
		goto bad;

	if (m->m_len < sizeof(struct ip)) {
		if ((m = m_pullup(m, sizeof(struct ip))) == NULL)
			goto bad;
	}
	ip = mtod(m, struct ip *);
	if (ip == NULL)
		goto bad;
    /*
	 * Ensure, if SDU in AF_INET.
	 */
	if (ip->ip_v != IPVERSION)
		goto bad;

	sa = sintosa(&fec);
	bzero(sa, sizeof(*sa));
    sa->sa_len = sizeof(*sa);
    satosin(sa)->sin_family = AF_INET;
    satosin(sa)->sin_addr.s_addr = ip->ip_dst.s_addr;

	if ((ifa = mpls_rtifwithfec(sa, 1)) != NULL) {
		sa = &ro->ro_dst;
		sa->sa_len = sizeof(struct sockaddr_mpls);
		sa->sa_family = AF_MPLS;
		satosmpls(sa)->smpls_label =
			satosmpls(ifa->ifa_addr)->smpls_label;
		rtalloc_ign_fib(ro, 0UL, M_GETFIB(m));
		ifa_free(ifa);
	}

	/*
	 * Restore cached MPLS label stack and
	 * original Ethernet protocol id.
	 */
	M_PREPEND(m, stacksiz, M_NOWAIT);
	if (m == NULL)
		goto bad1;

	bcopy(stack, mtod(m, caddr_t), stacksiz);
done:
	*mp = m;
	return;
bad1:
	RO_RTFREE(ro);
bad:
	if (m != NULL) {
		m_freem(m);
		m = NULL;
	}
	goto done;
}

where remains in netmpls/mpls_rmx.c.

Ongoing tasks:
--------------

Testing by cleanup of netmpls/mpls_rmx.c and netmpls/mpls.c
changed implementation.

Bugfixes targeting routing and exception handling due to
exceeding TTL.

Refactoring mpls_rt_output_fib().