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;
    (void)printf(" | \n + %s \n", __func__);

	/*
	 * 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)) {
	(void)printf("%s: MPLS label not found\n", __func__);
				    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;
	(void)printf("%s: %s -> %d -> %*D on %s\n",
	    __func__, inet_ntoa(isaddr),
		MPLS_LABEL_GET(satosmpls(sa0)->smpls_label),
		ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
		ifp->if_xname);

            } 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.

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 *rt, struct rt_addrinfo *info)
{
	struct sockaddr_mpls lsp = {
	    .smpls_len =    sizeof(lsp),
	    .smpls_family = AF_MPLS,
	    .smpls_label =  0,
	    .smpls_vprd =   0,
	    .smpls_op =     0,
	};
	struct sockaddr_ftn *sftn;
	struct ifaddr *ifa;
	struct ifnet *ifp0;
	struct rtentry *rt0;
    int error = 0;

	RT_LOCK_ASSERT(rt);

	sftn = satosftn(rt->rt_gateway);
	RT_UNLOCK(rt);

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

	    if (sftn->sftn_len < sizeof(struct sockaddr_ftn))
	        break;

	    lsp.smpls_label = sftn->sftn_label;
	    ifa = mpls_rtifwithilm(smplstosa(&lsp), 1);
		if (ifa == NULL)
			break;

		ifp0 = ifa->ifa_ifp;
		rt0 = rtalloc1_fib(smplstosa(&lsp), 0, 0UL, ifp0->if_fib);
		if (rt0 == NULL) {
		    ifa_free(ifa);
		    break;
		}
	    error = rtexpunge(rt0);
	    if (error != 0) {
	        RTFREE_LOCKED(rt0);
	        ifa_free(ifa);
	        break;
	    }
	    ifa_free(ifa);
	    ifa_ref(rt->rt_ifa);
	    ifa = rt->rt_ifa;
	    ifa->ifa_rtrequest = NULL;
	    ifa_free(ifa);
	    break;
	default:
		break;
	}
	RT_LOCK(rt);
}

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.

Bugfix:
-------

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

Codesection in mpls_rt_output_fib()

	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);
	}

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

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

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

Bugfixes targeting routing and exception handling due to
exceeding TTL.