Skip to content

Commit

Permalink
route: Add support for 'seg6 local' encap
Browse files Browse the repository at this point in the history
Code copied from:
Linux kernel: 6995e2de6891c724bfeb2db33d7b87775f913ad1 (tag: v6.4)
iproute2: 43388c729de3124d30ccd39e1e616ed30a1e2477 (tag: v6.4.0)
  • Loading branch information
kogdenko authored and thom311 committed Dec 4, 2023
1 parent aa7353f commit 8123fca
Show file tree
Hide file tree
Showing 10 changed files with 1,504 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,8 @@ lib_libnl_route_3_la_SOURCES = \
lib/route/nexthop_encap.c \
lib/route/nh.c \
lib/route/nh_encap_mpls.c \
lib/route/nh_encap_seg6.c \
lib/route/nh_encap_seg6_local.c \
lib/route/nl-route.h \
lib/route/pktloc.c \
lib/route/qdisc.c \
Expand All @@ -514,6 +516,8 @@ lib_libnl_route_3_la_SOURCES = \
lib/route/route_utils.c \
lib/route/rtnl.c \
lib/route/rule.c \
lib/route/seg6.c \
lib/route/seg6.h \
lib/route/tc-api.h \
lib/route/tc.c \
$(NULL)
Expand Down
15 changes: 15 additions & 0 deletions include/netlink/route/nexthop.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,26 @@ extern int rtnl_route_nh_str2flags(const char *);
/*
* nexthop encapsulations
*/
extern int rtnl_route_nh_get_encap_type(struct rtnl_nexthop *nh);

extern int rtnl_route_nh_encap_mpls(struct rtnl_nexthop *nh,
struct nl_addr *addr,
uint8_t ttl);
extern struct nl_addr * rtnl_route_nh_get_encap_mpls_dst(struct rtnl_nexthop *);
extern uint8_t rtnl_route_nh_get_encap_mpls_ttl(struct rtnl_nexthop *);


extern int rtnl_route_nh_get_encap_seg6_mode(struct rtnl_nexthop * );

struct ipv6_sr_hdr;
extern int rtnl_route_nh_get_encap_seg6_srh(struct rtnl_nexthop *,
const struct ipv6_sr_hdr **);

extern int rtnl_route_nh_get_encap_seg6_local_action(struct rtnl_nexthop *);
extern int rtnl_route_nh_has_encap_seg6_local_attr(struct rtnl_nexthop *, int);
extern int rtnl_route_nh_get_encap_seg6_local_table(struct rtnl_nexthop *);
extern int rtnl_route_nh_get_encap_seg6_local_vrftable(struct rtnl_nexthop *);

#ifdef __cplusplus
}
#endif
Expand Down
10 changes: 10 additions & 0 deletions lib/route/nexthop-encap.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,14 @@ int nh_encap_compare(struct rtnl_nh_encap *a, struct rtnl_nh_encap *b);
* MPLS encap
*/
extern struct nh_encap_ops mpls_encap_ops;

/*
* SEG6_encap
*/
extern struct nh_encap_ops seg6_encap_ops;

/*
* SEG6_LOCAL encap
*/
extern struct nh_encap_ops seg6_local_encap_ops;
#endif
10 changes: 10 additions & 0 deletions lib/route/nexthop.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

#include "nl-default.h"

#include <linux/lwtunnel.h>

#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/rtnl.h>
Expand Down Expand Up @@ -412,4 +414,12 @@ int rtnl_route_nh_str2flags(const char *name)

/** @} */

int rtnl_route_nh_get_encap_type(struct rtnl_nexthop *nh)
{
if (nh->rtnh_encap == NULL || nh->rtnh_encap->ops == NULL)
return LWTUNNEL_ENCAP_NONE;

return nh->rtnh_encap->ops->encap_type;
}

/** @} */
3 changes: 3 additions & 0 deletions lib/route/nexthop_encap.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ static struct lwtunnel_encap_type {
[LWTUNNEL_ENCAP_IP] = { .name = "ip" },
[LWTUNNEL_ENCAP_IP6] = { .name = "ip6" },
[LWTUNNEL_ENCAP_ILA] = { .name = "ila" },
[LWTUNNEL_ENCAP_SEG6] = { .name = "seg6", .ops = &seg6_encap_ops },
[LWTUNNEL_ENCAP_BPF] = { .name = "bpf" },
[LWTUNNEL_ENCAP_SEG6_LOCAL] = { .name = "seg6_local", .ops = &seg6_local_encap_ops },

};

static const char *nh_encap_type2str(unsigned int type)
Expand Down
167 changes: 167 additions & 0 deletions lib/route/nh_encap_seg6.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/* SPDX-License-Identifier: LGPL-2.1-only */

#include "nl-default.h"

#include <linux/lwtunnel.h>
#include <linux/seg6_iptunnel.h>

#include <netlink/attr.h>
#include <netlink/route/nexthop.h>

#include "nl-priv-dynamic-core/nl-core.h"
#include "nl-route.h"
#include "nexthop-encap.h"
#include "seg6.h"

static int seg6_encap_build_msg(struct nl_msg *msg, void *priv)
{
struct seg6_iptunnel_encap *slwt;

slwt = priv;
return nla_put(msg, SEG6_IPTUNNEL_SRH, SEG6_IPTUN_ENCAP_SIZE(slwt),
slwt);
}

static struct nla_policy seg6_encap_policy[SEG6_IPTUNNEL_MAX + 1] = {
[SEG6_IPTUNNEL_SRH] = { .minlen = sizeof(struct seg6_iptunnel_encap) +
sizeof(struct ipv6_sr_hdr) +
sizeof(struct in6_addr) },
};

/**
* Copied from Linux 6.4: seg6_build_state:net/ipv6/seg6_iptunnel.c
* Author: David Lebrun <[email protected]>
*/
static int seg6_encap_parse_msg(struct nlattr *nla, struct rtnl_nexthop *nh)
{
struct nlattr *tb[SEG6_IPTUNNEL_MAX + 1];
struct seg6_iptunnel_encap *tuninfo;
int err, tuninfo_len;
struct rtnl_nh_encap *rtnh_encap;
struct seg6_iptunnel_encap *slwt;

err = nla_parse_nested(tb, SEG6_IPTUNNEL_MAX, nla, seg6_encap_policy);
if (err < 0)
return err;

if (!tb[SEG6_IPTUNNEL_SRH])
return -NLE_INVAL;

tuninfo = nla_data(tb[SEG6_IPTUNNEL_SRH]);
tuninfo_len = nla_len(tb[SEG6_IPTUNNEL_SRH]);

switch (tuninfo->mode) {
case SEG6_IPTUN_MODE_INLINE:
break;
case SEG6_IPTUN_MODE_ENCAP:
break;
case SEG6_IPTUN_MODE_L2ENCAP:
break;
case SEG6_IPTUN_MODE_ENCAP_RED:
break;
case SEG6_IPTUN_MODE_L2ENCAP_RED:
break;
default:
return -NLE_INVAL;
}

/* verify that SRH is consistent */
if (!seg6_validate_srh(tuninfo->srh, tuninfo_len - sizeof(*tuninfo)))
return -NLE_INVAL;

slwt = malloc(tuninfo_len);
if (slwt == NULL)
return -NLE_NOMEM;

memcpy(slwt, tuninfo, tuninfo_len);

rtnh_encap = calloc(1, sizeof(*rtnh_encap));
if (!rtnh_encap)
return -NLE_NOMEM;

rtnh_encap->priv = slwt;
rtnh_encap->ops = &seg6_encap_ops;

nh_set_encap(nh, rtnh_encap);

return 0;
}

static int seg6_encap_compare(void *a, void *b)
{
struct seg6_iptunnel_encap *slwt_a, *slwt_b;
int len;

slwt_a = a;
slwt_b = b;
len = SEG6_IPTUN_ENCAP_SIZE(slwt_a);

if (len != SEG6_IPTUN_ENCAP_SIZE(slwt_b))
return 1;

return memcmp(slwt_a, slwt_b, len);
}

static const char *seg6_mode_types[] = {
[SEG6_IPTUN_MODE_INLINE] = "inline",
[SEG6_IPTUN_MODE_ENCAP] = "encap",
[SEG6_IPTUN_MODE_L2ENCAP] = "l2encap",
[SEG6_IPTUN_MODE_ENCAP_RED] = "encap.red",
[SEG6_IPTUN_MODE_L2ENCAP_RED] = "l2encap.red"
};

static void seg6_encap_dump(void *priv, struct nl_dump_params *dp)
{
struct seg6_iptunnel_encap *slwt;
const char *mode;

slwt = priv;
if (slwt->mode < 0 || slwt->mode >= ARRAY_SIZE(seg6_mode_types))
mode = "<unknown>";
else
mode = seg6_mode_types[slwt->mode];

nl_dump(dp, "mode %s ", mode);
seg6_dump_srh(dp, slwt->srh);
}

struct nh_encap_ops seg6_encap_ops = {
.encap_type = LWTUNNEL_ENCAP_SEG6,
.build_msg = seg6_encap_build_msg,
.parse_msg = seg6_encap_parse_msg,
.compare = seg6_encap_compare,
.dump = seg6_encap_dump,
};

static struct seg6_iptunnel_encap *get_seg6_slwt(struct rtnl_nexthop *nh)
{
if (!nh->rtnh_encap ||
nh->rtnh_encap->ops->encap_type != LWTUNNEL_ENCAP_SEG6)
return NULL;

return (struct seg6_iptunnel_encap *)nh->rtnh_encap->priv;
}

int rtnl_route_nh_get_encap_seg6_mode(struct rtnl_nexthop *nh)
{
struct seg6_iptunnel_encap *slwt;

slwt = get_seg6_slwt(nh);
if (slwt == NULL)
return -1;

return slwt->mode;
}

int rtnl_route_nh_get_encap_seg6_srh(struct rtnl_nexthop *nh,
const struct ipv6_sr_hdr **psrh)
{
struct seg6_iptunnel_encap *slwt;

slwt = get_seg6_slwt(nh);
if (slwt == NULL)
return -1;

*psrh = slwt->srh;
return IPV6_EXTHDR_LEN(slwt->srh->hdrlen);
}
Loading

0 comments on commit 8123fca

Please sign in to comment.