The current limitation is that the frames are sent only on one VLAN per port.
+ Deprecate use of lldpctl_watch_callback(). Use
lldpctl_watch_callback2() instead.
+ Upgrade embedded libevent to 2.1.11-stable
+ + Add support of sending LLDP frames on a configured VLAN
lldpd (1.0.5)
* Changes:
return 1;
}
+static int
+cmd_vlan_tx(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, void *arg)
+{
+ lldpctl_atom_t *port;
+ const char *name;
+ const char *vlan_id = cmdenv_get(env, "vlan-tx-id");
+ const char *vlan_prio = cmdenv_get(env, "vlan-tx-prio");
+ const char *vlan_dei = cmdenv_get(env, "vlan-tx-dei");
+
+ /* Default values are used to disable VLAN */
+ int vlan_id_int = -1;
+ int vlan_prio_int = -1;
+ int vlan_dei_int = -1;
+ int vlan_tag = -1;
+
+ if (!arg)
+ log_debug("lldpctl", "lldp disable VLAN tagging of transmitted LLDP frames");
+ else
+ log_debug("lldpctl", "lldp enable VLAN tagging of transmitted LLDP frames with VLAN ID: '%s', Priority: '%s', DEI: '%s'",
+ vlan_id, vlan_prio, vlan_dei);
+
+ if (arg) {
+ if (!vlan_id || !strlen(vlan_id)) {
+ log_warnx("lldpctl", "no VLAN id for TX specified");
+ return 0;
+ } else {
+ const char *errstr;
+ vlan_id_int = strtonum(vlan_id, 0, 4094, &errstr);
+ if (errstr != NULL) {
+ log_warnx("lldpctl", "invalid VLAN ID for TX `%s': %s",
+ vlan_id, errstr);
+ return 0;
+ }
+ }
+
+ if (!vlan_prio || !strlen(vlan_prio)) {
+ log_warnx("lldpctl", "no VLAN priority for TX specified, using default (0)");
+ /* Use default priority */
+ vlan_prio_int = 0;
+ } else {
+ const char *errstr;
+ vlan_prio_int = strtonum(vlan_prio, 0, 7, &errstr);
+ if (errstr != NULL) {
+ log_warnx("lldpctl", "invalid VLAN piority `%s': %s",
+ vlan_prio, errstr);
+ return 0;
+ }
+ }
+
+ if (!vlan_dei || !strlen(vlan_dei)) {
+ log_warnx("lldpctl", "no VLAN Drop eligible indicator (DEI) for TX specified, using default (0)");
+ /* Use default priority */
+ vlan_dei_int = 0;
+ } else {
+ const char *errstr;
+ vlan_dei_int = strtonum(vlan_dei, 0, 1, &errstr);
+ if (errstr != NULL) {
+ log_warnx("lldpctl", "invalid VLAN Drop eligible indicator (DEI) `%s': %s",
+ vlan_dei, errstr);
+ return 0;
+ }
+ }
+ /* Priority(3bits) | DEI(1bit) | VID(12bits) */
+ vlan_tag = ((vlan_prio_int & 0x7) << 13) |
+ ((vlan_dei_int & 0x1) << 12) |
+ (vlan_id_int & 0xfff);
+ }
+
+ while ((port = cmd_iterate_on_ports(conn, env, &name))) {
+ if (lldpctl_atom_set_int(port, lldpctl_k_port_vlan_tx, vlan_tag) == NULL) {
+ log_warnx("lldpctl", "unable to set VLAN TX config on %s."
+ " %s", name, lldpctl_last_strerror(conn));
+ }
+ }
+
+ return 1;
+}
+
#ifdef ENABLE_CUSTOM
static int
cmd_custom_tlv_set(struct lldpctl_conn_t *conn, struct writer *w,
NEWLINE, "Don't enable management addresses advertisement",
NULL, cmd_chassis_mgmt_advertise, NULL);
+ struct cmd_node *vlan_tx = commands_new(
+ commands_new(configure_lldp,
+ "vlan-tx",
+ "Send LLDP frames with a VLAN tag",
+ NULL, NULL, NULL),
+ NULL, "VLAN ID (0-4094)",
+ NULL, cmd_store_env_value, "vlan-tx-id");
+
+ struct cmd_node *vlan_tx_prio = commands_new(
+ commands_new(vlan_tx,
+ "priority",
+ "Also set a priority in a VLAN tag (default 0)",
+ NULL, NULL, NULL),
+ NULL, "Priority to be included in a VLAN tag (0-7)",
+ NULL, cmd_store_env_value, "vlan-tx-prio");
+
+ commands_new(vlan_tx,
+ NEWLINE, "Enable VLAN tagging of transmitted LLDP frames",
+ NULL, cmd_vlan_tx,
+ "enable");
+
+ commands_new(
+ vlan_tx_prio,
+ NEWLINE, "Set VLAN ID and priority for transmitted frames",
+ NULL, cmd_vlan_tx, "enable");
+
+ commands_new(
+ commands_new(
+ commands_new(vlan_tx_prio,
+ "dei",
+ "Also set a Drop eligible indicator (DEI) in a VLAN tag (default 0)",
+ NULL, NULL, NULL),
+ NULL, "Drop eligible indicator (DEI) in a VLAN tag (0-don't drop; 1-drop)",
+ NULL, cmd_store_env_value, "vlan-tx-dei"),
+ NEWLINE, "Set VLAN ID, priority and DEI for transmitted frames",
+ NULL, cmd_vlan_tx, "enable");
+
+ commands_new(
+ commands_new(unconfigure_lldp,
+ "vlan-tx",
+ "Send LLDP frames without VLAN tag",
+ NULL, NULL, NULL),
+ NEWLINE, "Disable VLAN tagging of transmitted LLDP frames",
+ NULL, cmd_vlan_tx, NULL);
+
#ifdef ENABLE_CUSTOM
register_commands_configure_lldp_custom_tlvs(configure_lldp, unconfigure_lldp);
static void
display_port(struct writer *w, lldpctl_atom_t *port, int details)
{
+ int vlan_tx_tag;
+ char buf[5]; /* should be enough for printing */
+
tag_start(w, "port", "Port");
tag_start(w, "id", "PortID");
tag_attr (w, "type", "",
tag_datatag(w, "descr", "PortDescr",
lldpctl_atom_get_str(port, lldpctl_k_port_descr));
+
+ if ((vlan_tx_tag = lldpctl_atom_get_int(port, lldpctl_k_port_vlan_tx)) != -1) {
+ tag_start(w, "vlanTX", "VlanTX");
+ snprintf(buf, sizeof(buf), "%d", vlan_tx_tag & 0xfff);
+ tag_attr (w, "id", "VID", buf);
+ snprintf(buf, sizeof(buf), "%d", (vlan_tx_tag >> 13) & 0x7);
+ tag_attr (w, "prio", "Prio", buf);
+ snprintf(buf, sizeof(buf), "%d", (vlan_tx_tag >> 12) & 0x1);
+ tag_attr (w, "dei", "DEI", buf);
+ tag_end(w);
+ }
+
if (details &&
lldpctl_atom_get_int(port, lldpctl_k_port_ttl) > 0)
tag_datatag(w, "ttl", "TTL",
flag), setting any transmit mode won't have any effect.
.Ed
+.Cd configure
+.Op ports Ar ethX Op ,...
+.Cd lldp
+.Cd vlan-tx Ar vlan_id
+.Op Cd prio Ar priority Op Cd dei Ar dei
+.Bd -ragged -offset XXXXXX
+Configure the given port to send LLDP frames over a specified VLAN. With VLAN Identifier (VID) as
+.Ar vlan_id ,
+Priority Code Point (PCP) as
+.Ar priority ,
+and Drop Eligible Indicator (DEI) as
+.Ar dei .
+.Nm lldpd
+accepts LLDP frames on all VLANs.
+.Ed
+
.Cd configure
.Cd lldp custom-tlv
.Op Cd add | replace
port->p_disable_rx = port->p_disable_tx = 1;
break;
}
+ if (set->vlan_tx_enabled >= -1) {
+ port->p_vlan_tx_enabled = set->vlan_tx_enabled;
+ port->p_vlan_tx_tag = set->vlan_tx_tag;
+ }
#ifdef ENABLE_LLDPMED
if (set->med_policy && set->med_policy->type > 0) {
log_debug("rpc", "requested change to MED policy");
/* LLDP multicast address */
POKE_BYTES(mcastaddr, ETHER_ADDR_LEN) &&
/* Source MAC address */
- POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) &&
+ POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN)))
+ goto toobig;
+
+ /* Insert VLAN tag if needed */
+ if (port->p_vlan_tx_enabled) {
+ if (!(
+ /* VLAN ethertype */
+ POKE_UINT16(ETHERTYPE_VLAN) &&
+ /* VLAN Tag Control Information (TCI) */
+ /* Priority(3bits) | DEI(1bit) | VID(12bit) */
+ POKE_UINT16(port->p_vlan_tx_tag)))
+ goto toobig;
+ }
+
+ if (!(
/* LLDP frame */
POKE_UINT16(ETHERTYPE_LLDP)))
goto toobig;
return NULL;
}
+ set.vlan_tx_enabled = -1;
+
switch (key) {
case lldpctl_k_port_id:
set.local_id = p->port->p_id;
case lldpctl_k_port_status:
set.rxtx = LLDPD_RXTX_FROM_PORT(p->port);
break;
+ case lldpctl_k_port_vlan_tx:
+ set.vlan_tx_tag = p->port->p_vlan_tx_tag;
+ set.vlan_tx_enabled = p->port->p_vlan_tx_enabled;
+ break;
#ifdef ENABLE_DOT3
case lldpctl_k_port_dot3_power:
if (value->type != atom_dot3_power) {
port->p_disable_rx = !LLDPD_RXTX_RXENABLED(value);
port->p_disable_tx = !LLDPD_RXTX_TXENABLED(value);
break;
+ case lldpctl_k_port_vlan_tx:
+ if (value > -1) {
+ port->p_vlan_tx_tag = value;
+ port->p_vlan_tx_enabled = 1;
+ } else
+ port->p_vlan_tx_enabled = 0;
+ break;
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
return port->p_id_subtype;
case lldpctl_k_port_hidden:
return port->p_hidden_in;
+ case lldpctl_k_port_vlan_tx:
+ return port->p_vlan_tx_enabled ? port->p_vlan_tx_tag : -1;
#ifdef ENABLE_DOT3
case lldpctl_k_port_dot3_mfs:
if (port->p_mfs > 0)
lldpctl_k_port_status, /**< `(IS,WO)` Operational status of this (local) port */
lldpctl_k_port_chassis, /**< `(A)` Chassis associated to the port */
lldpctl_k_port_ttl, /**< `(I)` TTL for port, 0 if info is attached to chassis */
+ lldpctl_k_port_vlan_tx, /**< `(I,W)` VLAN tag for TX on port, -1 VLAN disabled */
lldpctl_k_port_dot3_mfs = 1300, /**< `(I)` MFS */
lldpctl_k_port_dot3_aggregid, /**< `(I)` Port aggregation ID */
int p_descr_force; /* Description has been forced by user */
u_int16_t p_mfs;
u_int16_t p_ttl; /* TTL for remote port */
+ int p_vlan_tx_tag;
+ int p_vlan_tx_enabled;
#ifdef ENABLE_DOT3
/* Dot3 stuff */
char *local_id;
char *local_descr;
int rxtx;
+ int vlan_tx_tag;
+ int vlan_tx_enabled;
#ifdef ENABLE_LLDPMED
struct lldpd_med_policy *med_policy;
struct lldpd_med_loc *med_location;
}
END_TEST
+#define ETHERTYPE_OFFSET 2 * ETHER_ADDR_LEN
+#define VLAN_TAG_SIZE 2
+START_TEST (test_send_rcv_vlan_tx)
+{
+ int n;
+ struct packet *pkt;
+ struct lldpd_chassis *nchassis = NULL;
+ struct lldpd_port *nport = NULL;
+ int vlan_id = 100;
+ int vlan_prio = 5;
+ int vlan_dei = 1;
+ unsigned int vlan_tag = 0;
+ unsigned int tmp;
+
+ /* Populate port and chassis */
+ hardware.h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
+ hardware.h_lport.p_id = "FastEthernet 1/5";
+ hardware.h_lport.p_id_len = strlen(hardware.h_lport.p_id);
+ hardware.h_lport.p_descr = "Fake port description";
+ hardware.h_lport.p_mfs = 1516;
+
+ /* Assembly VLAN tag: Priority(3bits) | DEI(1bit) | VID(12bits) */
+ vlan_tag = ((vlan_prio & 0x7) << 13) |
+ ((vlan_dei & 0x1) << 12) |
+ (vlan_id & 0xfff);
+ hardware.h_lport.p_vlan_tx_tag = vlan_tag;
+ hardware.h_lport.p_vlan_tx_enabled = 1;
+ chassis.c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
+ chassis.c_id = macaddress;
+ chassis.c_id_len = ETHER_ADDR_LEN;
+ chassis.c_name = "First chassis";
+ chassis.c_descr = "Chassis description";
+ chassis.c_cap_available = chassis.c_cap_enabled = LLDP_CAP_ROUTER;
+
+ /* Build packet */
+ n = lldp_send(&test_lldpd, &hardware);
+ if (n != 0) {
+ fail("unable to build packet");
+ return;
+ }
+ if (TAILQ_EMPTY(&pkts)) {
+ fail("no packets sent");
+ return;
+ }
+ pkt = TAILQ_FIRST(&pkts);
+ fail_unless(TAILQ_NEXT(pkt, next) == NULL, "more than one packet sent");
+
+ /* Check ETHER_TYPE, should be VLAN */
+ memcpy(&tmp, (unsigned char*) pkt->data + ETHERTYPE_OFFSET, ETHER_TYPE_LEN);
+ ck_assert_uint_eq(ntohl(tmp)>>16, ETHERTYPE_VLAN);
+
+ /* Check VLAN tag */
+ memcpy(&tmp, (unsigned char*) pkt->data + ETHERTYPE_OFFSET + ETHER_TYPE_LEN, VLAN_TAG_SIZE);
+ ck_assert_uint_eq(ntohl(tmp)>>16, vlan_tag);
+
+ /* Remove VLAN ethertype and VLAN tag */
+ memmove((unsigned char*) pkt->data + ETHERTYPE_OFFSET,
+ /* move all after VLAN tag */
+ (unsigned char*) pkt->data + ETHERTYPE_OFFSET + ETHER_TYPE_LEN + VLAN_TAG_SIZE,
+ /* size without src and dst MAC, VLAN tag */
+ pkt->size - (ETHERTYPE_OFFSET + ETHER_TYPE_LEN + VLAN_TAG_SIZE));
+
+ /* Decode the packet without VLAN tag, calling lldp_decode() */
+ fail_unless(lldp_decode(NULL, pkt->data, pkt->size, &hardware,
+ &nchassis, &nport) != -1);
+ if (!nchassis || !nport) {
+ fail("unable to decode packet");
+ return;
+ }
+
+ /* Verify port values (VLAN information is not checked here) */
+ check_received_port(&hardware.h_lport, nport);
+ /* Verify chassis values */
+ check_received_chassis(&chassis, nchassis);
+}
+END_TEST
+
#ifdef ENABLE_DOT1
/* This test case tests send and receive of all DOT1 TLVs(2005 and 2009):
Port Valn ID, VLAN, Port Protocol VLAN ID, Protocol Identity,
tcase_add_checked_fixture(tc_send, pcap_setup, pcap_teardown);
tcase_add_test(tc_send, test_send_rcv_basic);
+ tcase_add_test(tc_send, test_send_rcv_vlan_tx);
#ifdef ENABLE_DOT1
tcase_add_test(tc_send, test_send_rcv_dot1_tlvs);
#endif
assert out == {}
+def test_port_vlan_tx(lldpd1, lldpd, lldpcli, namespaces):
+ with namespaces(1):
+ lldpd()
+ lldpcli("configure", "ports", "eth0", "lldp", "vlan-tx", "100", "priority", "5", "dei", "1")
+ out = lldpcli("-f", "keyvalue", "show", "interfaces", "ports", "eth0")
+ assert out["lldp.eth0.port.vlanTX.id"] == '100'
+ assert out["lldp.eth0.port.vlanTX.prio"] == '5'
+ assert out["lldp.eth0.port.vlanTX.dei"] == '1'
+ # unconfigure VLAN TX
+ lldpcli("unconfigure", "ports", "eth0", "lldp", "vlan-tx")
+ out = lldpcli("-f", "keyvalue", "show", "interfaces", "ports", "eth0")
+ assert not "lldp.eth0.port.vlanTX.id" in out
+ assert not "lldp.eth0.port.vlanTX.prio" in out
+ assert not "lldp.eth0.port.vlanTX.dei" in out
+
+
def test_set_interface_alias(lldpd1, lldpd, lldpcli, namespaces):
with namespaces(1):
lldpcli("configure", "system", "interface", "description")