summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorWang Jian <jianjian.wang1@gmail.com>2018-02-03 10:59:29 +0000
committerThomas Haller <thaller@redhat.com>2018-02-12 14:36:15 +0100
commit6b4fc607ba6595e5afdb097699676cd87901c9c6 (patch)
treeb5b95f731d4bc2ee9531bf3d04ec74c1a6fefea7 /lib
parentdf204590c8ea53a5861dc45926938fa1a0053d17 (diff)
downloadlibnl-6b4fc607ba6595e5afdb097699676cd87901c9c6.tar.gz
link: add Geneve support.
Signed-off-by: Wang Jian <jianjian.wang1@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/route/link/geneve.c810
1 files changed, 810 insertions, 0 deletions
diff --git a/lib/route/link/geneve.c b/lib/route/link/geneve.c
new file mode 100644
index 00000000..2afa0dc8
--- /dev/null
+++ b/lib/route/link/geneve.c
@@ -0,0 +1,810 @@
+/*
+ * lib/route/link/geneve.c Geneve Link Info
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2018 Wang Jian <jianjian.wang1@gmail.com>
+ */
+/**
+ * @ingroup link
+ * @defgroup geneve Geneve
+ * Generic Network Virtualization Encapsulation
+ *
+ * @details
+ * \b Link Type Name: "geneve"
+ *
+ * @route_doc{link_geneve, Geneve Documentation}
+ *
+ * @{
+ */
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <netlink/route/link/geneve.h>
+
+
+/** @cond SKIP */
+#define GENEVE_ATTR_ID (1<<0)
+#define GENEVE_ATTR_REMOTE (1<<1)
+#define GENEVE_ATTR_REMOTE6 (1<<2)
+#define GENEVE_ATTR_TTL (1<<3)
+#define GENEVE_ATTR_TOS (1<<4)
+#define GENEVE_ATTR_LABEL (1<<5)
+#define GENEVE_ATTR_PORT (1<<6)
+#define GENEVE_ATTR_FLAGS (1<<7)
+#define GENEVE_ATTR_UDP_CSUM (1<<8)
+#define GENEVE_ATTR_UDP_ZERO_CSUM6_TX (1<<9)
+#define GENEVE_ATTR_UDP_ZERO_CSUM6_RX (1<<10)
+
+struct geneve_info
+{
+ uint32_t id;
+ uint32_t remote;
+ struct in6_addr remote6;
+ uint8_t ttl;
+ uint8_t tos;
+ uint32_t label;
+ uint16_t port;
+ uint8_t flags;
+ uint8_t udp_csum;
+ uint8_t udp_zero_csum6_tx;
+ uint8_t udp_zero_csum6_rx;
+ uint32_t mask;
+};
+
+/** @endcond */
+
+static struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
+ [IFLA_GENEVE_ID] = { .type = NLA_U32 },
+ [IFLA_GENEVE_REMOTE] = { .minlen = sizeof(uint32_t) },
+ [IFLA_GENEVE_REMOTE6] = { .minlen = sizeof(struct in6_addr) },
+ [IFLA_GENEVE_TTL] = { .type = NLA_U8 },
+ [IFLA_GENEVE_TOS] = { .type = NLA_U8 },
+ [IFLA_GENEVE_LABEL] = { .type = NLA_U32 },
+ [IFLA_GENEVE_PORT] = { .type = NLA_U16 },
+ [IFLA_GENEVE_COLLECT_METADATA] = { .type = NLA_FLAG },
+ [IFLA_GENEVE_UDP_CSUM] = { .type = NLA_U8 },
+ [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 },
+ [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 },
+};
+
+static int geneve_alloc(struct rtnl_link *link)
+{
+ struct geneve_info *geneve;
+
+ if (link->l_info)
+ memset(link->l_info, 0, sizeof(*geneve));
+ else {
+ if ((geneve = calloc(1, sizeof(*geneve))) == NULL)
+ return -NLE_NOMEM;
+ link->l_info = geneve;
+ }
+
+ return 0;
+}
+
+static int geneve_parse(struct rtnl_link *link, struct nlattr *data,
+ struct nlattr *xstats)
+{
+ struct nlattr *tb[IFLA_GENEVE_MAX + 1];
+ struct geneve_info *geneve;
+ int err = 0;
+
+ NL_DBG(3, "Parsing Geneve link info\n");
+
+ err = nla_parse_nested(tb, IFLA_GENEVE_MAX, data, geneve_policy);
+ if (err < 0)
+ return err;
+
+ err = geneve_alloc(link);
+ if (err < 0)
+ return err;
+
+ geneve = link->l_info;
+
+ if (tb[IFLA_GENEVE_ID]) {
+ geneve->id = nla_get_u32(tb[IFLA_GENEVE_ID]);
+ geneve->mask |= GENEVE_ATTR_ID;
+ }
+
+ if (tb[IFLA_GENEVE_REMOTE]) {
+ nla_memcpy(&geneve->remote, tb[IFLA_GENEVE_REMOTE],
+ sizeof(geneve->remote));
+ geneve->mask |= GENEVE_ATTR_REMOTE;
+ geneve->mask &= ~GENEVE_ATTR_REMOTE6;
+ }
+ if (tb[IFLA_GENEVE_REMOTE6]) {
+ nla_memcpy(&geneve->remote6, tb[IFLA_GENEVE_REMOTE6],
+ sizeof(geneve->remote6));
+ geneve->mask |= GENEVE_ATTR_REMOTE6;
+ geneve->mask &= ~GENEVE_ATTR_REMOTE;
+ }
+
+ if (tb[IFLA_GENEVE_TTL]) {
+ geneve->ttl = nla_get_u8(tb[IFLA_GENEVE_TTL]);
+ geneve->mask |= GENEVE_ATTR_TTL;
+ }
+
+ if (tb[IFLA_GENEVE_TOS]) {
+ geneve->tos = nla_get_u8(tb[IFLA_GENEVE_TOS]);
+ geneve->mask |= GENEVE_ATTR_TOS;
+ }
+
+ if (tb[IFLA_GENEVE_LABEL]) {
+ geneve->label = nla_get_u32(tb[IFLA_GENEVE_LABEL]);
+ geneve->mask |= GENEVE_ATTR_LABEL;
+ }
+
+ if (tb[IFLA_GENEVE_PORT]) {
+ geneve->port = nla_get_u16(tb[IFLA_GENEVE_PORT]);
+ geneve->mask |= GENEVE_ATTR_PORT;
+ }
+
+ if (tb[IFLA_GENEVE_COLLECT_METADATA])
+ geneve->flags |= RTNL_LINK_GENEVE_F_COLLECT_METADATA;
+
+ if (tb[IFLA_GENEVE_UDP_CSUM]) {
+ geneve->udp_csum = nla_get_u8(tb[IFLA_GENEVE_UDP_CSUM]);
+ geneve->mask |= GENEVE_ATTR_UDP_CSUM;
+ }
+
+ if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) {
+ geneve->udp_zero_csum6_tx = nla_get_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]);
+ geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_TX;
+ }
+
+ if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) {
+ geneve->udp_zero_csum6_rx = nla_get_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]);
+ geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_RX;
+ }
+
+ return err;
+}
+
+static void geneve_free(struct rtnl_link *link)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ free(geneve);
+ link->l_info = NULL;
+}
+
+static void geneve_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ nl_dump(p, "geneve-id %u", geneve->id);
+}
+
+static void geneve_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ struct geneve_info *geneve = link->l_info;
+ char addr[INET6_ADDRSTRLEN];
+
+ nl_dump_line(p, " geneve-id %u\n", geneve->id);
+
+ if (geneve->mask & GENEVE_ATTR_REMOTE) {
+ nl_dump(p, " remote ");
+ if (inet_ntop(AF_INET, &geneve->remote, addr, sizeof(addr)))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ntohs(geneve->remote));
+ } else if (geneve->mask & GENEVE_ATTR_REMOTE6) {
+ nl_dump(p, " remote ");
+ if (inet_ntop(AF_INET6, &geneve->remote6, addr, sizeof(addr)))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", geneve->remote6);
+ }
+
+ if (geneve->mask & GENEVE_ATTR_TTL) {
+ nl_dump(p, " ttl ");
+ nl_dump_line(p, "%u\n", geneve->ttl);
+ }
+
+ if (geneve->mask & GENEVE_ATTR_TOS) {
+ nl_dump(p, " tos ");
+ nl_dump_line(p, "%u\n", geneve->tos);
+ }
+
+ if (geneve->mask & GENEVE_ATTR_PORT) {
+ nl_dump(p, " port ");
+ nl_dump_line(p, "%u\n", ntohs(geneve->port));
+ }
+
+ if (geneve->mask & GENEVE_ATTR_LABEL) {
+ nl_dump(p, " label ");
+ nl_dump_line(p, "%u\n", ntohl(geneve->label));
+ }
+
+ if (geneve->mask & GENEVE_ATTR_UDP_CSUM) {
+ nl_dump(p, " UDP checksum ");
+ if (geneve->udp_csum)
+ nl_dump_line(p, "enabled (%#x)\n", geneve->udp_csum);
+ else
+ nl_dump_line(p, "disabled\n");
+ }
+
+ if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_TX) {
+ nl_dump(p, " udp-zero-csum6-tx ");
+ if (geneve->udp_zero_csum6_tx)
+ nl_dump_line(p, "enabled (%#x)\n", geneve->udp_zero_csum6_tx);
+ else
+ nl_dump_line(p, "disabled\n");
+ }
+
+ if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_RX) {
+ nl_dump(p, " udp-zero-csum6-rx ");
+ if (geneve->udp_zero_csum6_tx)
+ nl_dump_line(p, "enabled (%#x)\n", geneve->udp_zero_csum6_rx);
+ else
+ nl_dump_line(p, "disabled\n");
+ }
+
+ if (geneve->flags & RTNL_LINK_GENEVE_F_COLLECT_METADATA)
+ nl_dump(p, " collect-metadata\n");
+}
+
+static int geneve_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+ struct geneve_info *gdst, *gsrc;
+ int err;
+
+ gsrc = src->l_info;
+ dst->l_info = NULL;
+ err = rtnl_link_set_type(dst, "geneve");
+ if (err < 0)
+ return err;
+
+ gdst = dst->l_info;
+
+ if (!gsrc || !gdst)
+ return -NLE_NOMEM;
+
+ memcpy(gdst, gsrc, sizeof(struct geneve_info));
+
+ return 0;
+}
+
+static int geneve_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+ struct geneve_info *geneve = link->l_info;
+ struct nlattr *data;
+
+ if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+ return -NLE_MSGSIZE;
+
+ if (geneve->mask & GENEVE_ATTR_ID)
+ NLA_PUT_U32(msg, IFLA_GENEVE_ID, geneve->id);
+
+ if (geneve->mask & GENEVE_ATTR_REMOTE)
+ NLA_PUT(msg, IFLA_GENEVE_REMOTE,
+ sizeof(geneve->remote), &geneve->remote);
+
+ if (geneve->mask & GENEVE_ATTR_REMOTE6)
+ NLA_PUT(msg, IFLA_GENEVE_REMOTE6,
+ sizeof(geneve->remote6), &geneve->remote6);
+
+ if (geneve->mask & GENEVE_ATTR_TTL)
+ NLA_PUT_U8(msg, IFLA_GENEVE_TTL, geneve->ttl);
+
+ if (geneve->mask & GENEVE_ATTR_TOS)
+ NLA_PUT_U8(msg, IFLA_GENEVE_TOS, geneve->tos);
+
+ if (geneve->mask & GENEVE_ATTR_LABEL)
+ NLA_PUT_U32(msg, IFLA_GENEVE_LABEL, geneve->label);
+
+ if (geneve->mask & GENEVE_ATTR_PORT)
+ NLA_PUT_U32(msg, IFLA_GENEVE_PORT, geneve->port);
+
+ if (geneve->mask & GENEVE_ATTR_UDP_CSUM)
+ NLA_PUT_U8(msg, IFLA_GENEVE_UDP_CSUM, geneve->udp_csum);
+
+ if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_TX)
+ NLA_PUT_U8(msg, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, geneve->udp_zero_csum6_tx);
+
+ if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_RX)
+ NLA_PUT_U8(msg, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, geneve->udp_zero_csum6_rx);
+
+ if (geneve->flags & RTNL_LINK_GENEVE_F_COLLECT_METADATA)
+ NLA_PUT_FLAG(msg, IFLA_GENEVE_COLLECT_METADATA);
+
+ nla_nest_end(msg, data);
+
+nla_put_failure:
+
+ return 0;
+}
+
+static struct rtnl_link_info_ops geneve_info_ops = {
+ .io_name = "geneve",
+ .io_alloc = geneve_alloc,
+ .io_parse = geneve_parse,
+ .io_dump = {
+ [NL_DUMP_LINE] = geneve_dump_line,
+ [NL_DUMP_DETAILS] = geneve_dump_details,
+ },
+ .io_clone = geneve_clone,
+ .io_put_attrs = geneve_put_attrs,
+ .io_free = geneve_free,
+};
+
+
+/** @cond SKIP */
+#define IS_GENEVE_LINK_ASSERT(link) \
+ if ((link)->l_info_ops != &geneve_info_ops) { \
+ APPBUG("Link is not a geneve link. set type \"geneve\" first."); \
+ return -NLE_OPNOTSUPP; \
+ }
+/** @endcond */
+
+/**
+ * @name Geneve Object
+ * @{
+ */
+
+/**
+ * Allocate link object of type Geneve
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_geneve_alloc(void)
+{
+ struct rtnl_link *link;
+ int err;
+
+ if (!(link = rtnl_link_alloc()))
+ return NULL;
+
+ if ((err = rtnl_link_set_type(link, "geneve")) < 0) {
+ rtnl_link_put(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+/**
+ * Check if link is a Geneve link
+ * @arg link Link object
+ *
+ * @return True if link is a Geneve link, otherwisee false is returned.
+ */
+int rtnl_link_is_geneve(struct rtnl_link *link)
+{
+ return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "geneve");
+}
+
+/**
+ * Set Geneve Network Indentifier
+ * @arg link Link object
+ * @arg id Geneve network identifier
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_id(struct rtnl_link *link, uint32_t id)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ if (id > GENEVE_ID_MAX)
+ return -NLE_INVAL;
+
+ geneve->id = id;
+ geneve->mask |= GENEVE_ATTR_ID;
+
+ return 0;
+}
+
+/**
+ * Get Geneve Network Identifier
+ * @arg link Link object
+ * @arg id Pointer to store network identifier
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_get_id(struct rtnl_link *link, uint32_t *id)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ if (!id)
+ return -NLE_INVAL;
+
+ if (geneve->mask & GENEVE_ATTR_ID)
+ *id = geneve->id;
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Set Geneve unicast destination IP address
+ * @arg link Link object
+ * @arg addr The unicast destination IP address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_remote(struct rtnl_link *link, struct nl_addr *addr)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ if ((nl_addr_get_family(addr) == AF_INET) &&
+ (nl_addr_get_len(addr) == sizeof(geneve->remote))) {
+ memcpy(&geneve->remote, nl_addr_get_binary_addr(addr),
+ sizeof(geneve->remote));
+ geneve->mask |= GENEVE_ATTR_REMOTE;
+ geneve->mask &= ~GENEVE_ATTR_REMOTE6;
+ } else if ((nl_addr_get_family(addr) == AF_INET6) &&
+ (nl_addr_get_len(addr) == sizeof(geneve->remote6))) {
+ memcpy(&geneve->remote6, nl_addr_get_binary_addr(addr),
+ sizeof(geneve->remote6));
+ geneve->mask |= GENEVE_ATTR_REMOTE6;
+ geneve->mask &= ~GENEVE_ATTR_REMOTE;
+ } else
+ return -NLE_INVAL;
+
+ return 0;
+}
+
+/**
+ * Get Geneve unicast destination IP address
+ * @arg link Link object
+ * @arg addr Pointer to store unicast destination IP addree
+ *
+ * @return 0 on success or a a negative error code
+ */
+int rtnl_link_geneve_get_remote(struct rtnl_link *link, struct nl_addr **addr)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ if (!addr)
+ return -NLE_INVAL;
+
+ if (geneve->mask & GENEVE_ATTR_REMOTE)
+ *addr = nl_addr_build(AF_INET, &geneve->remote, sizeof(geneve->remote));
+ else if (geneve->mask & GENEVE_ATTR_REMOTE6)
+ *addr = nl_addr_build(AF_INET6, &geneve->remote6, sizeof(geneve->remote6));
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Set IP TTL value to use for Geneve
+ * @arg link Link object
+ * @arg ttl TTL value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_ttl(struct rtnl_link *link, uint8_t ttl)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ geneve->ttl = ttl;
+ geneve->mask |= GENEVE_ATTR_TTL;
+
+ return 0;
+}
+
+/**
+ * Get IP TTL value to use for Geneve
+ * @arg link Link object
+ *
+ * @return TTL value on success or a negative error code
+ */
+int rtnl_link_geneve_get_ttl(struct rtnl_link *link)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ if (!(geneve->mask & GENEVE_ATTR_TTL))
+ return -NLE_AGAIN;
+
+ return geneve->ttl;
+}
+
+/**
+ * Set IP ToS value to use for Geneve
+ * @arg link Link object
+ * @arg tos ToS value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_tos(struct rtnl_link *link, uint8_t tos)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ geneve->tos = tos;
+ geneve->mask |= GENEVE_ATTR_TOS;
+
+ return 0;
+}
+
+/**
+ * Get IP ToS value to use for Geneve
+ * @arg link Link object
+ *
+ * @return ToS value on success or a negative error code
+ */
+int rtnl_link_geneve_get_tos(struct rtnl_link *link)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ if (!(geneve->mask & GENEVE_ATTR_TOS))
+ return -NLE_AGAIN;
+
+ return geneve->tos;
+}
+
+/**
+ * Set UDP destination port to use for Geneve
+ * @arg link Link object
+ * @arg port Destination port
+ *
+ * @return 0 on success or a negative error code
+ */
+
+int rtnl_link_geneve_set_port(struct rtnl_link *link, uint32_t port)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ geneve->port = htons(port);
+ geneve->mask |= GENEVE_ATTR_PORT;
+
+ return 0;
+}
+
+/**
+ * Get UDP destination port to use for Geneve
+ * @arg link Link object
+ * @arg port Pointer to store destination port
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_get_port(struct rtnl_link *link, uint32_t *port)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ if (!port)
+ return -NLE_INVAL;
+
+ if (!(geneve->mask & GENEVE_ATTR_PORT))
+ return -NLE_NOATTR;
+
+ *port = ntohs(geneve->port);
+
+ return 0;
+}
+
+/**
+ * Set flow label to use for Geneve
+ * @arg link Link object
+ * @arg label Destination label
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_label(struct rtnl_link *link, uint32_t label)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ geneve->label = htonl(label);
+ geneve->mask |= GENEVE_ATTR_LABEL;
+
+ return 0;
+}
+
+/**
+ * Get flow label to use for Geneve
+ * @arg link Link object
+ * @arg label Pointer to store destination label
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_get_label(struct rtnl_link *link, uint32_t *label)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ if (!label)
+ return -NLE_INVAL;
+ if (!(geneve->mask & GENEVE_ATTR_LABEL))
+ return -NLE_NOATTR;
+
+ *label = ntohl(geneve->label);
+
+ return 0;
+}
+
+/**
+ * Set UDP checksum status to use for Geneve
+ * @arg link Link object
+ * @arg csum Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_udp_csum(struct rtnl_link *link, uint8_t csum)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ geneve->udp_csum = csum;
+ geneve->mask |= GENEVE_ATTR_UDP_CSUM;
+
+ return 0;
+}
+
+/**
+ * Get UDP checksum status to use for Geneve
+ * @arg link Link object
+ *
+ * @return status value on success or a negative error code
+ */
+int rtnl_link_geneve_get_udp_csum(struct rtnl_link *link)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ if (!(geneve->mask & GENEVE_ATTR_UDP_CSUM))
+ return -NLE_NOATTR;
+
+ return geneve->udp_csum;
+}
+
+/**
+ * Set skip UDP checksum transmitted over IPv6 status to use for Geneve
+ * @arg link Link object
+ * @arg csum Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_udp_zero_csum6_tx(struct rtnl_link *link, uint8_t csum)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ geneve->udp_zero_csum6_tx = csum;
+ geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_TX;
+
+ return 0;
+}
+
+/**
+ * Get skip UDP checksum transmitted over IPv6 status to use for Geneve
+ * @arg link Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_geneve_get_udp_zero_csum6_tx(struct rtnl_link *link)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ if (!(geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_TX))
+ return -NLE_NOATTR;
+
+ return geneve->udp_zero_csum6_tx;
+}
+
+/**
+ * Set skip UDP checksum received over IPv6 status to use for Geneve
+ * @arg link Link object
+ * @arg csum Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_udp_zero_csum6_rx(struct rtnl_link *link, uint8_t csum)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ geneve->udp_zero_csum6_rx = csum;
+ geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_RX;
+
+ return 0;
+}
+
+/**
+ * Get skip UDP checksum received over IPv6 status to use for Geneve
+ * @arg link Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_geneve_get_udp_zero_csum6_rx(struct rtnl_link *link)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ if (!(geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_RX))
+ return -NLE_NOATTR;
+
+ return geneve->udp_zero_csum6_rx;
+}
+
+/**
+ * Set Geneve flags
+ * @arg link Link object
+ * @arg flags Which flags to set
+ * @arg enable Boolean enabling or disabling flag
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_flags(struct rtnl_link *link, uint8_t flags, int enable)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ if (flags & ~RTNL_LINK_GENEVE_F_COLLECT_METADATA)
+ return -NLE_INVAL;
+
+ if (enable)
+ geneve->flags = flags;
+ else
+ geneve->flags &= ~flags;
+
+ return 0;
+}
+
+/**
+ * Get Geneve flags
+ * @arg link Link object
+ * @arg flags Pointer to store flags
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_get_flags(struct rtnl_link *link, uint8_t *flags)
+{
+ struct geneve_info *geneve = link->l_info;
+
+ IS_GENEVE_LINK_ASSERT(link);
+
+ *flags = geneve->flags;
+ return 0;
+}
+
+/** @} */
+static void __init geneve_init(void)
+{
+ rtnl_link_register_info(&geneve_info_ops);
+}
+
+static void __exit geneve_exit(void)
+{
+ rtnl_link_unregister_info(&geneve_info_ops);
+}
+
+/** @} */