/* SPDX-License-Identifier: LGPL-2.1-only */ /* * Copyright (c) 2018 Wang Jian */ /** * @ingroup link * @defgroup geneve Geneve * Generic Network Virtualization Encapsulation * * @details * \b Link Type Name: "geneve" * * @route_doc{link_geneve, Geneve Documentation} * * @{ */ #include #include #include #include #include #include #include /** @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 "); nl_dump_line(p, "%s\n", _nl_inet_ntop(AF_INET, &geneve->remote, addr)); } else if (geneve->mask & GENEVE_ATTR_REMOTE6) { nl_dump(p, " remote "); nl_dump_line(p, "%s\n", _nl_inet_ntop(AF_INET6, &geneve->remote6, addr)); } 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_rx) 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; if (!(link = rtnl_link_alloc())) return NULL; if (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 > RTNL_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); } /** @} */