blob: 61b885f0f4464e48921eb88bd796ac88c5347155 [file]
/* SPDX-License-Identifier: LGPL-2.1-only */
/*
* Copyright (c) 2022 MaxLinear, Inc.
*/
/**
* @ingroup link
* @defgroup bridge Bridging
*
* @details
* @{
*/
#include "nl-default.h"
#include <netlink/route/link/bridge_info.h>
#include "nl-route.h"
#include "link-api.h"
#define BRIDGE_ATTR_VLAN_FILTERING (1 << 0)
#define BRIDGE_ATTR_VLAN_PROTOCOL (1 << 1)
#define BRIDGE_ATTR_VLAN_STATS_ENABLED (1 << 2)
struct bridge_info {
uint32_t ce_mask; /* to support attr macros */
uint16_t b_vlan_protocol;
uint8_t b_vlan_filtering;
uint8_t b_vlan_stats_enabled;
};
static const struct nla_policy bi_attrs_policy[IFLA_BR_MAX + 1] = {
[IFLA_BR_VLAN_FILTERING] = { .type = NLA_U8 },
[IFLA_BR_VLAN_PROTOCOL] = { .type = NLA_U16 },
[IFLA_BR_VLAN_STATS_ENABLED] = { .type = NLA_U8 },
};
static inline struct bridge_info *bridge_info(struct rtnl_link *link)
{
return link->l_info;
}
static int bridge_info_alloc(struct rtnl_link *link)
{
struct bridge_info *bi;
if (link->l_info)
memset(link->l_info, 0, sizeof(*bi));
else {
bi = calloc(1, sizeof(*bi));
if (!bi)
return -NLE_NOMEM;
link->l_info = bi;
}
return 0;
}
static int bridge_info_parse(struct rtnl_link *link, struct nlattr *data,
struct nlattr *xstats)
{
struct nlattr *tb[IFLA_BR_MAX + 1];
struct bridge_info *bi;
int err;
NL_DBG(3, "Parsing Bridge link info\n");
if ((err = nla_parse_nested(tb, IFLA_BR_MAX, data, bi_attrs_policy)) <
0)
return err;
if ((err = bridge_info_alloc(link)) < 0)
return err;
bi = link->l_info;
if (tb[IFLA_BR_VLAN_FILTERING]) {
bi->b_vlan_filtering = nla_get_u8(tb[IFLA_BR_VLAN_FILTERING]);
bi->ce_mask |= BRIDGE_ATTR_VLAN_FILTERING;
}
if (tb[IFLA_BR_VLAN_PROTOCOL]) {
bi->b_vlan_protocol =
ntohs(nla_get_u16(tb[IFLA_BR_VLAN_PROTOCOL]));
bi->ce_mask |= BRIDGE_ATTR_VLAN_PROTOCOL;
}
if (tb[IFLA_BR_VLAN_STATS_ENABLED]) {
bi->b_vlan_stats_enabled =
nla_get_u8(tb[IFLA_BR_VLAN_STATS_ENABLED]);
bi->ce_mask |= BRIDGE_ATTR_VLAN_STATS_ENABLED;
}
return 0;
}
static int bridge_info_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
{
struct bridge_info *bi = link->l_info;
struct nlattr *data;
data = nla_nest_start(msg, IFLA_INFO_DATA);
if (!data)
return -NLE_MSGSIZE;
if (bi->ce_mask & BRIDGE_ATTR_VLAN_FILTERING)
NLA_PUT_U8(msg, IFLA_BR_VLAN_FILTERING, bi->b_vlan_filtering);
if (bi->ce_mask & BRIDGE_ATTR_VLAN_PROTOCOL)
NLA_PUT_U16(msg, IFLA_BR_VLAN_PROTOCOL,
htons(bi->b_vlan_protocol));
if (bi->ce_mask & BRIDGE_ATTR_VLAN_STATS_ENABLED)
NLA_PUT_U8(msg, IFLA_BR_VLAN_STATS_ENABLED,
bi->b_vlan_stats_enabled);
nla_nest_end(msg, data);
return 0;
nla_put_failure:
nla_nest_cancel(msg, data);
return -NLE_MSGSIZE;
}
static void bridge_info_free(struct rtnl_link *link)
{
_nl_clear_free(&link->l_info);
}
static struct rtnl_link_info_ops bridge_info_ops = {
.io_name = "bridge",
.io_alloc = bridge_info_alloc,
.io_parse = bridge_info_parse,
.io_put_attrs = bridge_info_put_attrs,
.io_free = bridge_info_free,
};
#define IS_BRIDGE_INFO_ASSERT(link) \
do { \
if ((link)->l_info_ops != &bridge_info_ops) { \
APPBUG("Link is not a bridge link. Set type \"bridge\" first."); \
} \
} while (0)
/**
* Set VLAN filtering flag
* @arg link Link object of type bridge
* @arg vlan_filtering VLAN_filtering boolean flag to set.
*
* @see rtnl_link_bridge_get_vlan_filtering()
*
* @return void
*/
void rtnl_link_bridge_set_vlan_filtering(struct rtnl_link *link,
uint8_t vlan_filtering)
{
struct bridge_info *bi = bridge_info(link);
IS_BRIDGE_INFO_ASSERT(link);
bi->b_vlan_filtering = vlan_filtering;
bi->ce_mask |= BRIDGE_ATTR_VLAN_FILTERING;
}
/**
* Get VLAN filtering flag
* @arg link Link object of type bridge
* @arg vlan_filtering Output argument.
*
* @see rtnl_link_bridge_set_vlan_filtering()
*
* @return Zero on success, otherwise a negative error code.
* @retval -NLE_NOATTR
* @retval -NLE_INVAL
*/
int rtnl_link_bridge_get_vlan_filtering(struct rtnl_link *link,
uint8_t *vlan_filtering)
{
struct bridge_info *bi = bridge_info(link);
IS_BRIDGE_INFO_ASSERT(link);
if (!(bi->ce_mask & BRIDGE_ATTR_VLAN_FILTERING))
return -NLE_NOATTR;
if (!vlan_filtering)
return -NLE_INVAL;
*vlan_filtering = bi->b_vlan_filtering;
return 0;
}
/**
* Set VLAN protocol
* @arg link Link object of type bridge
* @arg vlan_protocol VLAN protocol to set. The protocol
* numbers is in host byte order.
*
* @see rtnl_link_bridge_get_vlan_protocol()
*
* @return void
*/
void rtnl_link_bridge_set_vlan_protocol(struct rtnl_link *link,
uint16_t vlan_protocol)
{
struct bridge_info *bi = bridge_info(link);
IS_BRIDGE_INFO_ASSERT(link);
bi->b_vlan_protocol = vlan_protocol;
bi->ce_mask |= BRIDGE_ATTR_VLAN_PROTOCOL;
}
/**
* Get VLAN protocol
* @arg link Link object of type bridge
* @arg vlan_protocol Output argument. The protocol number is in host byte order.
*
* @see rtnl_link_bridge_set_vlan_protocol()
*
* @return Zero on success, otherwise a negative error code.
* @retval -NLE_NOATTR
* @retval -NLE_INVAL
*/
int rtnl_link_bridge_get_vlan_protocol(struct rtnl_link *link,
uint16_t *vlan_protocol)
{
struct bridge_info *bi = bridge_info(link);
IS_BRIDGE_INFO_ASSERT(link);
if (!(bi->ce_mask & BRIDGE_ATTR_VLAN_PROTOCOL))
return -NLE_NOATTR;
if (!vlan_protocol)
return -NLE_INVAL;
*vlan_protocol = bi->b_vlan_protocol;
return 0;
}
/**
* Set VLAN stats enabled flag
* @arg link Link object of type bridge
* @arg vlan_stats_enabled VLAN stats enabled flag to set
*
* @see rtnl_link_bridge_get_vlan_stats_enabled()
*
* @return void
*/
void rtnl_link_bridge_set_vlan_stats_enabled(struct rtnl_link *link,
uint8_t vlan_stats_enabled)
{
struct bridge_info *bi = bridge_info(link);
IS_BRIDGE_INFO_ASSERT(link);
bi->b_vlan_stats_enabled = vlan_stats_enabled;
bi->ce_mask |= BRIDGE_ATTR_VLAN_STATS_ENABLED;
}
/**
* Get VLAN stats enabled flag
* @arg link Link object of type bridge
* @arg vlan_stats_enabled Output argument.
*
* @see rtnl_link_bridge_set_vlan_stats_enabled()
*
* @return Zero on success, otherwise a negative error code.
* @retval -NLE_NOATTR
* @retval -NLE_INVAL
*/
int rtnl_link_bridge_get_vlan_stats_enabled(struct rtnl_link *link,
uint8_t *vlan_stats_enabled)
{
struct bridge_info *bi = bridge_info(link);
IS_BRIDGE_INFO_ASSERT(link);
if (!(bi->ce_mask & BRIDGE_ATTR_VLAN_STATS_ENABLED))
return -NLE_NOATTR;
if (!vlan_stats_enabled)
return -NLE_INVAL;
*vlan_stats_enabled = bi->b_vlan_stats_enabled;
return 0;
}
static void _nl_init bridge_info_init(void)
{
rtnl_link_register_info(&bridge_info_ops);
}
static void _nl_exit bridge_info_exit(void)
{
rtnl_link_unregister_info(&bridge_info_ops);
}
/** @} */