<varlistentry>
<term><varname>PVID=</varname></term>
<listitem>
- <para>The Port VLAN ID specified here is assigned to all untagged frames at ingress.
- <varname>PVID=</varname> can be used only once. Configuring <varname>PVID=</varname> implicates the use of
- <varname>VLAN=</varname> above and will enable the VLAN ID for ingress as well.</para>
+ <para>The port VLAN ID specified here is assigned to all untagged frames at ingress. Takes an
+ VLAN ID or negative boolean value (e.g. <literal>no</literal>). When false, the currently
+ assigned port VLAN ID will be dropped. Configuring <varname>PVID=</varname> implicates the use of
+ <varname>VLAN=</varname> setting in the above and will enable the VLAN ID for ingress as well.
+ Defaults to unset, and will keep the assigned port VLAN ID if exists.</para>
<xi:include href="version-info.xml" xpointer="v231"/>
</listitem>
return 0;
}
+static uint16_t link_get_pvid(Link *link, bool *ret_untagged) {
+ assert(link);
+ assert(link->network);
+
+ if (vlanid_is_valid(link->network->bridge_vlan_pvid)) {
+ if (ret_untagged)
+ *ret_untagged = is_bit_set(link->network->bridge_vlan_pvid,
+ link->network->bridge_vlan_untagged_bitmap);
+ return link->network->bridge_vlan_pvid;
+ }
+
+ if (link->network->bridge_vlan_pvid == BRIDGE_VLAN_KEEP_PVID) {
+ if (ret_untagged)
+ *ret_untagged = link->bridge_vlan_pvid_is_untagged;
+ return link->bridge_vlan_pvid;
+ }
+
+ if (ret_untagged)
+ *ret_untagged = false;
+ return UINT16_MAX;
+}
+
static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) {
- uint16_t begin = UINT16_MAX;
- bool untagged;
+ uint16_t pvid, begin = UINT16_MAX;
+ bool untagged, pvid_is_untagged;
int r;
assert(link);
assert(link->network);
assert(m);
+ pvid = link_get_pvid(link, &pvid_is_untagged);
+
for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++) {
- if (k == link->network->bridge_vlan_pvid) {
+ if (k == pvid) {
/* PVID needs to be sent alone. Finish previous bits. */
if (begin != UINT16_MAX) {
assert(begin < k);
begin = UINT16_MAX;
}
- untagged = is_bit_set(k, link->network->bridge_vlan_untagged_bitmap);
- r = add_single(m, k, untagged, /* is_pvid = */ true);
+ r = add_single(m, pvid, pvid_is_untagged, /* is_pvid = */ true);
if (r < 0)
return r;
return 0;
}
-int bridge_vlan_set_message(Link *link, sd_netlink_message *m) {
+static int bridge_vlan_append_del_info(Link *link, sd_netlink_message *m) {
+ uint16_t pvid, begin = UINT16_MAX;
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(m);
+
+ pvid = link_get_pvid(link, NULL);
+
+ for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++) {
+
+ if (k == pvid ||
+ !is_bit_set(k, link->bridge_vlan_bitmap) ||
+ is_bit_set(k, link->network->bridge_vlan_bitmap)) {
+ /* This bit is not necessary to be removed. Finish previous bits. */
+ if (begin != UINT16_MAX) {
+ assert(begin < k);
+
+ r = add_range(m, begin, k - 1, /* untagged = */ false);
+ if (r < 0)
+ return r;
+
+ begin = UINT16_MAX;
+ }
+
+ continue;
+ }
+
+ if (begin != UINT16_MAX)
+ continue;
+
+ /* This is the starting point of a new bit sequence. Save the position. */
+ begin = k;
+ }
+
+ /* No pending bit sequence. */
+ assert(begin == UINT16_MAX);
+ return 0;
+}
+
+int bridge_vlan_set_message(Link *link, sd_netlink_message *m, bool is_set) {
int r;
assert(link);
return r;
}
- r = bridge_vlan_append_set_info(link, m);
+ if (is_set)
+ r = bridge_vlan_append_set_info(link, m);
+ else
+ r = bridge_vlan_append_del_info(link, m);
if (r < 0)
return r;
assert(rvalue);
if (isempty(rvalue)) {
- *id = UINT16_MAX;
+ *id = BRIDGE_VLAN_KEEP_PVID;
+ return 0;
+ }
+
+ if (parse_boolean(rvalue) == 0) {
+ *id = BRIDGE_VLAN_REMOVE_PVID;
return 0;
}
***/
#include <inttypes.h>
+#include <stdbool.h>
#include "sd-netlink.h"
#include "conf-parser.h"
+#include "vlan-util.h"
#define BRIDGE_VLAN_BITMAP_MAX 4096
#define BRIDGE_VLAN_BITMAP_LEN (BRIDGE_VLAN_BITMAP_MAX / 32)
+#define BRIDGE_VLAN_KEEP_PVID UINT16_MAX
+#define BRIDGE_VLAN_REMOVE_PVID (UINT16_MAX - 1)
+assert_cc(BRIDGE_VLAN_REMOVE_PVID > VLANID_MAX);
+
typedef struct Link Link;
typedef struct Network Network;
void network_adjust_bridge_vlan(Network *network);
-int bridge_vlan_set_message(Link *link, sd_netlink_message *m);
+int bridge_vlan_set_message(Link *link, sd_netlink_message *m, bool is_set);
int link_update_bridge_vlan(Link *link, sd_netlink_message *m);
bool activated:1;
bool master_set:1;
bool stacked_netdevs_created:1;
+ bool bridge_vlan_set:1;
sd_dhcp_server *dhcp_server;
.priority = LINK_BRIDGE_PORT_PRIORITY_INVALID,
.multicast_router = _MULTICAST_ROUTER_INVALID,
- .bridge_vlan_pvid = UINT16_MAX,
+ .bridge_vlan_pvid = BRIDGE_VLAN_KEEP_PVID,
.lldp_mode = LLDP_MODE_ROUTERS_ONLY,
.lldp_multicast_mode = _SD_LLDP_MULTICAST_MODE_INVALID,
[REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE] = "IPv6LL address generation mode",
[REQUEST_TYPE_SET_LINK_BOND] = "bond configurations",
[REQUEST_TYPE_SET_LINK_BRIDGE] = "bridge configurations",
- [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN] = "bridge VLAN configurations",
+ [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN] = "bridge VLAN configurations (step 1)",
+ [REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN] = "bridge VLAN configurations (step 2)",
[REQUEST_TYPE_SET_LINK_CAN] = "CAN interface configurations",
[REQUEST_TYPE_SET_LINK_FLAGS] = "link flags",
[REQUEST_TYPE_SET_LINK_GROUP] = "interface group",
REQUEST_TYPE_SET_LINK_BOND, /* Setting bond configs. */
REQUEST_TYPE_SET_LINK_BRIDGE, /* Setting bridge configs. */
REQUEST_TYPE_SET_LINK_BRIDGE_VLAN, /* Setting bridge VLAN configs. */
+ REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN, /* Removing bridge VLAN configs. */
REQUEST_TYPE_SET_LINK_CAN, /* Setting CAN interface configs. */
REQUEST_TYPE_SET_LINK_FLAGS, /* Setting IFF_NOARP or friends. */
REQUEST_TYPE_SET_LINK_GROUP, /* Setting interface group. */
}
static int link_set_bridge_vlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) {
+ int r;
+
+ assert(link);
+
+ r = set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL);
+ if (r <= 0)
+ return r;
+
+ link->bridge_vlan_set = true;
+ return 0;
+}
+
+static int link_del_bridge_vlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) {
return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL);
}
return r;
break;
case REQUEST_TYPE_SET_LINK_BRIDGE_VLAN:
- r = bridge_vlan_set_message(link, req);
+ r = bridge_vlan_set_message(link, req, /* is_set = */ true);
+ if (r < 0)
+ return r;
+ break;
+ case REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN:
+ r = bridge_vlan_set_message(link, req, /* is_set = */ false);
if (r < 0)
return r;
break;
r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->master_ifindex);
else if (IN_SET(req->type, REQUEST_TYPE_SET_LINK_CAN, REQUEST_TYPE_SET_LINK_IPOIB))
r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->ifindex);
+ else if (req->type == REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN)
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_DELLINK, link->ifindex);
else
r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_SETLINK, link->ifindex);
if (r < 0)
if (link->network->keep_master && link->master_ifindex <= 0 && !streq_ptr(link->kind, "bridge"))
return false;
-
break;
+ case REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN:
+ return link->bridge_vlan_set;
+
case REQUEST_TYPE_SET_LINK_CAN:
/* Do not check link->set_flgas_messages here, as it is ok even if link->flags
* is outdated, and checking the counter causes a deadlock. */
}
int link_request_to_set_bridge_vlan(Link *link) {
+ int r;
+
assert(link);
assert(link->network);
/* If nothing configured, use the default vlan ID. */
- if (memeqzero(link->network->bridge_vlan_bitmap, BRIDGE_VLAN_BITMAP_LEN * sizeof(uint32_t)))
+ if (memeqzero(link->network->bridge_vlan_bitmap, BRIDGE_VLAN_BITMAP_LEN * sizeof(uint32_t)) &&
+ link->network->bridge_vlan_pvid == BRIDGE_VLAN_KEEP_PVID)
return 0;
if (!link->network->bridge && !streq_ptr(link->kind, "bridge")) {
return 0;
}
- return link_request_set_link(link, REQUEST_TYPE_SET_LINK_BRIDGE_VLAN,
- link_set_bridge_vlan_handler,
- NULL);
+ link->bridge_vlan_set = false;
+
+ r = link_request_set_link(link, REQUEST_TYPE_SET_LINK_BRIDGE_VLAN,
+ link_set_bridge_vlan_handler,
+ NULL);
+ if (r < 0)
+ return r;
+
+ r = link_request_set_link(link, REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN,
+ link_del_bridge_vlan_handler,
+ NULL);
+ if (r < 0)
+ return r;
+
+ return 0;
}
int link_request_to_set_can(Link *link) {