#include <sys/globaldata.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <net/if_var.h>
#include <net/netisr.h>
#include <net/route.h>
#include <sys/mplock2.h>
#include <netproto/mpls/mpls.h>
#include <netproto/mpls/mpls_var.h>
struct mpls_stats mplsstats_percpu[MAXCPU];
struct route mplsforward_rt[MAXCPU];
int mplsforwarding = 1;
static void mpls_input_handler(netmsg_t);
static void mpls_forward(struct mbuf *);
void
mpls_init(void)
{
int cpu;
for (cpu = 0; cpu < ncpus; ++cpu) {
bzero(&mplsstats_percpu[cpu], sizeof(struct mpls_stats));
}
netisr_register(NETISR_MPLS, mpls_input_handler, mpls_hashfn);
}
static void
mpls_input_handler(netmsg_t msg)
{
struct mbuf *m = msg->packet.nm_packet;
get_mplock();
mpls_input(m);
rel_mplock();
}
void
mpls_input(struct mbuf *m)
{
struct mpls *mpls = NULL;
mpls_label_t label;
M_ASSERTPKTHDR(m);
mplsstat.mplss_total++;
KASSERT(m->m_pkthdr.len >= sizeof(struct mpls),
("mpls_input: mpls header too small"));
again:
if (m->m_len < sizeof(struct mpls)) {
m = m_pullup(m, sizeof(struct mpls));
if (m == NULL) {
mplsstat.mplss_toosmall++;
return;
}
}
mpls = mtod(m, struct mpls*);
label = MPLS_LABEL(ntohl(mpls->mpls_shim));
switch (label) {
case 0:
if (MPLS_STACK(ntohl(mpls->mpls_shim))) {
m_adj(m, sizeof(struct mpls));
netisr_queue(NETISR_IP, m);
return;
}
goto again;
case 1:
break;
case 2:
if (MPLS_STACK(ntohl(mpls->mpls_shim))) {
m_adj(m, sizeof(struct mpls));
netisr_queue(NETISR_IPV6, m);
return;
}
goto again;
case 3:
break;
default:
if (label <= 15) {
mplsstat.mplss_reserved++;
m_freem(m);
return;
}
if (mplsforwarding) {
mpls_forward(m);
return;
} else {
mplsstat.mplss_cantforward++;
m_freem(m);
return;
}
}
mplsstat.mplss_invalid++;
m_freem(m);
}
static void
mpls_forward(struct mbuf *m)
{
struct sockaddr_mpls *smpls;
struct mpls *mpls;
struct route *cache_rt = &mplsforward_rt[mycpuid];
mpls_label_t label;
struct ifnet *ifp;
struct sockaddr *dst;
int error;
KASSERT(m->m_len >= sizeof(struct mpls),
("mpls_forward: mpls header not in one mbuf"));
mpls = mtod(m, struct mpls *);
label = MPLS_LABEL(ntohl(mpls->mpls_shim));
smpls = (struct sockaddr_mpls *) &cache_rt->ro_dst;
if (cache_rt->ro_rt == NULL || smpls->smpls_label != label) {
if (cache_rt->ro_rt != NULL) {
RTFREE(cache_rt->ro_rt);
cache_rt->ro_rt = NULL;
}
smpls->smpls_family = AF_MPLS;
smpls->smpls_len = sizeof(struct sockaddr_mpls);
smpls->smpls_label = htonl(label);
rtalloc(cache_rt);
if (cache_rt->ro_rt == NULL) {
return;
}
}
ifp = cache_rt->ro_rt->rt_ifp;
dst = cache_rt->ro_rt->rt_gateway;
error = mpls_output(m, cache_rt->ro_rt);
if (error)
goto bad;
error = (*ifp->if_output)(ifp, m, dst, cache_rt->ro_rt);
if (error)
goto bad;
mplsstat.mplss_forwarded++;
return;
bad:
m_freem(m);
}