Enable capability to add and remove vlan filters for a standard linux bridge using netlink. New function virNetlinkBridgeVlanFilterSet can be used to add or remove a vlan filter to a given bridge interface. Signed-off-by: Leigh Brown <leigh@xxxxxxxxxxxxx> Reviewed-by: Laine Stump <laine@xxxxxxxxxx> --- src/util/virnetlink.c | 66 +++++++++++++++++++++++++++++++++++++++++++ src/util/virnetlink.h | 7 +++++ 2 files changed, 73 insertions(+) diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c index 24cd69a385..206646d9d7 100644 --- a/src/util/virnetlink.c +++ b/src/util/virnetlink.c @@ -701,6 +701,72 @@ virNetlinkDelLink(const char *ifname, virNetlinkTalkFallback fallback) return 0; } +/** + * virNetlinkBridgeVlanFilterSet: + * + * @ifname: name of the link + * @cmd: netlink command, either RTM_SETLINK or RTM_DELLINK + * @flags: flags to use when adding the vlan filter + * @vid: vlan id to add or remove + * @error: netlink error code + * + * Add or remove a vlan filter from an interface associated with a + * bridge. + * + * Returns 0 on success, -1 on error. Additionally, if the @error is + * non-zero, then a netlink failure occurred, but no error message + * is generated leaving it up to the caller to handle the condition. + */ +int +virNetlinkBridgeVlanFilterSet(const char *ifname, + int cmd, + const unsigned short flags, + const short vid, + int *error) +{ + struct ifinfomsg ifm = { .ifi_family = PF_BRIDGE }; + struct bridge_vlan_info vinfo = { .flags = flags, .vid = vid }; + struct nlattr *afspec = NULL; + g_autoptr(virNetlinkMsg) nl_msg = NULL; + g_autofree struct nlmsghdr *resp = NULL; + unsigned int resp_len = 0; + + *error = 0; + + if (vid < 1 || vid > 4095) { + virReportError(ERANGE, _("vlanid out of range: %1$d"), vid); + return -1; + } + + if (!(cmd == RTM_SETLINK || cmd == RTM_DELLINK)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Invalid vlan filter command %1$d"), cmd); + return -1; + } + + if (virNetDevGetIndex(ifname, &ifm.ifi_index) < 0) + return -1; + + nl_msg = virNetlinkMsgNew(cmd, NLM_F_REQUEST); + + NETLINK_MSG_APPEND(nl_msg, sizeof(ifm), &ifm); + + NETLINK_MSG_NEST_START(nl_msg, afspec, IFLA_AF_SPEC); + NETLINK_MSG_PUT(nl_msg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); + NETLINK_MSG_NEST_END(nl_msg, afspec); + + if (virNetlinkTalk(ifname, nl_msg, 0, 0, &resp, &resp_len, error, NULL) < 0) + return -1; + + if (resp->nlmsg_type != NLMSG_ERROR && resp->nlmsg_type != NLMSG_DONE) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed netlink response message")); + return -1; + } + + return 0; +} + /** * virNetlinkGetNeighbor: * diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h index 75192f645f..327fb426a1 100644 --- a/src/util/virnetlink.h +++ b/src/util/virnetlink.h @@ -25,6 +25,7 @@ #if defined(WITH_LIBNL) # include <netlink/msg.h> +# include <linux/if_bridge.h> typedef struct nl_msg virNetlinkMsg; G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNetlinkMsg, nlmsg_free); @@ -76,6 +77,12 @@ typedef int (*virNetlinkTalkFallback)(const char *ifname); int virNetlinkDelLink(const char *ifname, virNetlinkTalkFallback fallback); +int virNetlinkBridgeVlanFilterSet(const char *ifname, + int cmd, + const unsigned short flags, + const short vid, + int *error); + int virNetlinkGetErrorCode(struct nlmsghdr *resp, unsigned int recvbuflen); int virNetlinkDumpLink(const char *ifname, int ifindex, -- 2.39.5