blob: 081661e6490ea8068eaae655b2a408897f78690c [file] [log] [blame]
/* SPDX-License-Identifier: LGPL-2.1-only */
#include <netlink-private/netlink.h>
#include <netlink-private/types.h>
#include <netlink-private/route/nexthop-encap.h>
#include <netlink/route/nexthop.h>
#include <linux/mpls_iptunnel.h>
#include <linux/lwtunnel.h>
struct mpls_iptunnel_encap {
struct nl_addr *dst;
uint8_t ttl;
};
static void mpls_encap_dump(void *priv, struct nl_dump_params *dp)
{
struct mpls_iptunnel_encap *encap_info = priv;
char buf[256];
nl_dump(dp, "%s ", nl_addr2str(encap_info->dst, buf, sizeof(buf)));
if (encap_info->ttl)
nl_dump(dp, "ttl %u ", encap_info->ttl);
}
static int mpls_encap_build_msg(struct nl_msg *msg, void *priv)
{
struct mpls_iptunnel_encap *encap_info = priv;
NLA_PUT_ADDR(msg, MPLS_IPTUNNEL_DST, encap_info->dst);
if (encap_info->ttl)
NLA_PUT_U8(msg, MPLS_IPTUNNEL_TTL, encap_info->ttl);
return 0;
nla_put_failure:
return -NLE_MSGSIZE;
}
static void mpls_encap_destructor(void *priv)
{
struct mpls_iptunnel_encap *encap_info = priv;
nl_addr_put(encap_info->dst);
}
static struct nla_policy mpls_encap_policy[MPLS_IPTUNNEL_MAX + 1] = {
[MPLS_IPTUNNEL_DST] = { .type = NLA_U32 },
[MPLS_IPTUNNEL_TTL] = { .type = NLA_U8 },
};
static int mpls_encap_parse_msg(struct nlattr *nla, struct rtnl_nexthop *nh)
{
struct nlattr *tb[MPLS_IPTUNNEL_MAX + 1];
struct nl_addr *labels;
uint8_t ttl = 0;
int err;
err = nla_parse_nested(tb, MPLS_IPTUNNEL_MAX, nla, mpls_encap_policy);
if (err)
return err;
if (!tb[MPLS_IPTUNNEL_DST])
return -NLE_INVAL;
labels = nl_addr_alloc_attr(tb[MPLS_IPTUNNEL_DST], AF_MPLS);
if (!labels)
return -NLE_NOMEM;
if (tb[MPLS_IPTUNNEL_TTL])
ttl = nla_get_u8(tb[MPLS_IPTUNNEL_TTL]);
err = rtnl_route_nh_encap_mpls(nh, labels, ttl);
nl_addr_put(labels);
return err;
}
static int mpls_encap_compare(void *_a, void *_b)
{
struct mpls_iptunnel_encap *a = _a;
struct mpls_iptunnel_encap *b = _b;
int diff = 0;
diff |= (a->ttl != b->ttl);
diff |= nl_addr_cmp(a->dst, b->dst);
return diff;
}
struct nh_encap_ops mpls_encap_ops = {
.encap_type = LWTUNNEL_ENCAP_MPLS,
.build_msg = mpls_encap_build_msg,
.parse_msg = mpls_encap_parse_msg,
.compare = mpls_encap_compare,
.dump = mpls_encap_dump,
.destructor = mpls_encap_destructor,
};
int rtnl_route_nh_encap_mpls(struct rtnl_nexthop *nh,
struct nl_addr *addr,
uint8_t ttl)
{
struct mpls_iptunnel_encap *mpls_encap;
struct rtnl_nh_encap *rtnh_encap;
if (!addr)
return -NLE_INVAL;
if (!nl_addr_valid(nl_addr_get_binary_addr(addr),
nl_addr_get_len(addr)))
return -NLE_INVAL;
rtnh_encap = calloc(1, sizeof(*rtnh_encap));
if (!rtnh_encap)
return -NLE_NOMEM;
mpls_encap = calloc(1, sizeof(*mpls_encap));
if (!mpls_encap) {
free(rtnh_encap);
return -NLE_NOMEM;
}
mpls_encap->dst = nl_addr_get(addr);
mpls_encap->ttl = ttl;
rtnh_encap->priv = mpls_encap;
rtnh_encap->ops = &mpls_encap_ops;
nh_set_encap(nh, rtnh_encap);
return 0;
}