#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
#include <net/if_var.h>
#include <netinet/ip.h>
#include <netproto/mpls/mpls.h>
#include <netproto/mpls/mpls_var.h>
static int mpls_push(struct mbuf **, mpls_label_t,
mpls_s_t, mpls_exp_t, mpls_ttl_t);
static int mpls_swap(struct mbuf *, mpls_label_t);
static int mpls_pop(struct mbuf *, mpls_s_t *);
int
mpls_output(struct mbuf *m, struct rtentry *rt)
{
struct sockaddr_mpls *smpls = NULL;
int error = 0, i;
mpls_s_t stackempty;
mpls_ttl_t ttl = 255;
struct ip *ip;
M_ASSERTPKTHDR(m);
stackempty = rt_key(rt)->sa_family != AF_MPLS ? 1 : 0;
if (stackempty) {
switch (rt_key(rt)->sa_family) {
case AF_INET:
ip = mtod(m, struct ip *);
ttl = ip->ip_ttl;
break;
}
}
for (i=0; i < MPLS_MAXLOPS && rt->rt_shim[i] != NULL; ++i) {
smpls = (struct sockaddr_mpls *)rt->rt_shim[i];
switch (smpls->smpls_op) {
case MPLSLOP_PUSH:
error = mpls_push(&m,
ntohl(smpls->smpls_label),
(stackempty && i == 0) ? 1 : 0,
0,
ttl);
if (error)
return (error);
stackempty = 0;
m->m_flags |= M_MPLSLABELED;
break;
case MPLSLOP_SWAP:
if (stackempty)
return (ENOTSUP);
KKASSERT(m->m_flags & M_MPLSLABELED);
error = mpls_swap(m, ntohl(smpls->smpls_label));
if (error)
return (error);
break;
case MPLSLOP_POP:
if (stackempty)
return (ENOTSUP);
KKASSERT(m->m_flags & M_MPLSLABELED);
error = mpls_pop(m, &stackempty);
if (error)
return (error);
if (stackempty)
m->m_flags &= ~M_MPLSLABELED;
break;
default:
return (ENOTSUP);
}
}
return (error);
}
boolean_t
mpls_output_process(struct mbuf *m, struct rtentry *rt)
{
int error;
if (!(rt->rt_flags & RTF_MPLSOPS))
return TRUE;
error = mpls_output(m, rt);
if (error) {
m_freem(m);
return FALSE;
}
return TRUE;
}
static int
mpls_push(struct mbuf **m, mpls_label_t label, mpls_s_t s, mpls_exp_t exp, mpls_ttl_t ttl) {
struct mpls *mpls;
u_int32_t buf = 0;
M_PREPEND(*m, sizeof(struct mpls), M_NOWAIT);
if (*m == NULL)
return (ENOBUFS);
MPLS_SET_LABEL(buf, label);
MPLS_SET_STACK(buf, s);
MPLS_SET_EXP(buf, exp);
MPLS_SET_TTL(buf, ttl);
mpls = mtod(*m, struct mpls *);
mpls->mpls_shim = htonl(buf);
return (0);
}
static int
mpls_swap(struct mbuf *m, mpls_label_t label) {
struct mpls *mpls;
u_int32_t buf;
mpls_ttl_t ttl;
if (m->m_len < sizeof(struct mpls) &&
(m = m_pullup(m, sizeof(struct mpls))) == NULL)
return (ENOBUFS);
mpls = mtod(m, struct mpls *);
buf = ntohl(mpls->mpls_shim);
ttl = MPLS_TTL(buf);
if (--ttl <= 0) {
mplsstat.mplss_ttlexpired++;
return (ETIMEDOUT);
}
MPLS_SET_LABEL(buf, label);
MPLS_SET_TTL(buf, ttl);
mpls->mpls_shim = htonl(buf);
return (0);
}
static int
mpls_pop(struct mbuf *m, mpls_s_t *sbit) {
struct mpls *mpls;
u_int32_t buf;
if (m->m_len < sizeof(struct mpls)) {
m = m_pullup(m, sizeof(struct mpls));
if (m == NULL)
return (ENOBUFS);
}
mpls = mtod(m, struct mpls *);
buf = ntohl(mpls->mpls_shim);
*sbit = MPLS_STACK(buf);
m_adj(m, sizeof(struct mpls));
return (0);
}