From e7331ce92fcde6e408112bb433ba46c48eaf1278 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Sun, 23 Aug 2015 16:01:48 +0200 Subject: [PATCH] Ability to enable/disable RX/TX individually on each port --- src/client/client.h | 2 ++ src/client/commands.c | 6 ++++ src/client/conf-lldp.c | 50 ++++++++++++++++++++++++++++ src/client/lldpcli.8.in | 22 ++++++++++++ src/daemon/client.c | 20 +++++++++++ src/daemon/lldpd.c | 8 +++++ src/lib/atoms/port.c | 74 +++++++++++++++++++++++++++++++++++++---- src/lib/lldpctl.h | 1 + src/lldpd-structs.h | 14 ++++++++ tests/lldpcli.conf | 2 ++ 10 files changed, 193 insertions(+), 6 deletions(-) diff --git a/src/client/client.h b/src/client/client.h index 64de5faa..62174b3c 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -96,6 +96,8 @@ int cmd_store_env_value_and_pop3(struct lldpctl_conn_t *, struct writer *, struct cmd_env *, void *); int cmd_store_something_env_value_and_pop2(const char *, struct cmd_env *, void *); +int cmd_store_something_env_value(const char *, struct cmd_env *, + void *); lldpctl_atom_t* cmd_iterate_on_interfaces(struct lldpctl_conn_t *, struct cmd_env *); lldpctl_atom_t* cmd_iterate_on_ports(struct lldpctl_conn_t *, diff --git a/src/client/commands.c b/src/client/commands.c index de96ea72..e8e47d00 100644 --- a/src/client/commands.c +++ b/src/client/commands.c @@ -664,6 +664,12 @@ cmd_store_something_env_value_and_pop2(const char *what, return (cmdenv_put(env, what, value) != -1 && cmdenv_pop(env, 2) != -1); } +int +cmd_store_something_env_value(const char *what, + struct cmd_env *env, void *value) +{ + return (cmdenv_put(env, what, value) != -1); +} /** * Provide an iterator on all interfaces contained in "ports". diff --git a/src/client/conf-lldp.c b/src/client/conf-lldp.c index 0d21ef73..10cf5dd4 100644 --- a/src/client/conf-lldp.c +++ b/src/client/conf-lldp.c @@ -69,6 +69,31 @@ cmd_txhold(struct lldpctl_conn_t *conn, struct writer *w, return 1; } +static int +cmd_status(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, void *arg) +{ + lldpctl_atom_t *port; + const char *name; + const char *status = cmdenv_get(env, "status"); + + log_debug("lldpctl", "lldp administrative port status set to '%s'", status); + + if (!status || !strlen(status)) { + log_warnx("lldpctl", "no status specified"); + return 0; + } + + while ((port = cmd_iterate_on_ports(conn, env, &name))) { + if (lldpctl_atom_set_str(port, lldpctl_k_port_status, status) == NULL) { + log_warnx("lldpctl", "unable to set LLDP status for %s." + " %s", name, lldpctl_last_strerror(conn)); + } + } + + return 1; +} + static int cmd_portid_type_local(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, void *arg) @@ -330,6 +355,13 @@ register_commands_configure_lldp_custom_tlvs(struct cmd_node *configure_lldp) } #endif /* ENABLE_CUSTOM */ +static int +cmd_store_status_env_value(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, void *value) +{ + return cmd_store_something_env_value("status", env, value); +} + /** * Register `configure lldp` commands. * @@ -369,6 +401,24 @@ register_commands_configure_lldp(struct cmd_node *configure, NEWLINE, "Set LLDP transmit hold", NULL, cmd_txhold, NULL); + struct cmd_node *status = commands_new(configure_lldp, + "status", "Set administrative status", + NULL, NULL, NULL); + + for (lldpctl_map_t *status_map = + lldpctl_key_get_map(lldpctl_k_port_status); + status_map->string; + status_map++) { + const char *tag = strdup(totag(status_map->string)); + commands_new( + commands_new(status, + tag, + status_map->string, + NULL, cmd_store_status_env_value, status_map->string), + NEWLINE, "Set port administrative status", + NULL, cmd_status, NULL); + } + /* Now handle the various portid subtypes we can configure. */ struct cmd_node *configure_lldp_portid_type = commands_new( configure_lldp, diff --git a/src/client/lldpcli.8.in b/src/client/lldpcli.8.in index 20a0dc5a..3abc00b1 100644 --- a/src/client/lldpcli.8.in +++ b/src/client/lldpcli.8.in @@ -403,6 +403,28 @@ value and of the transmit delay. The default value is 4 and therefore the default TTL is 120 seconds. .Ed +.Cd configure +.Op ports Ar ethX Op ,... +.Cd lldp +.Cd status Ar rx-and-tx | rx-only | tx-only | disabled +.Bd -ragged -offset XXXXXX +Configure the administrative status of the given port. By default, all +ports are configured to be in +.Ar rxAndTx +mode. This means they can receive and transmit LLDP frames (as well as +other protocols if needed). In +.Ar rxOnly +mode, they won't emit any frames and in +.Ar txOnly +mode, they won't receive any frames. In +.Ar disabled +mode, no frame will be sent and any incoming frame will be +discarded. This settings do not override the operational mode of the +main daemon. If it is configured in receive-only mode (with the +.Fl r +flag), setting any transmit mode won't have any effect. +.Ed + .Cd configure .Cd lldp custom-tlv oui Ar oui .Cd subtype Ar subtype diff --git a/src/daemon/client.c b/src/daemon/client.c index 80d50104..89520cf4 100644 --- a/src/daemon/client.c +++ b/src/daemon/client.c @@ -333,6 +333,26 @@ _client_handle_set_port(struct lldpd *cfg, free(port->p_descr); port->p_descr = strdup(set->local_descr); } + switch (set->rxtx) { + case LLDPD_RXTX_TXONLY: + log_debug("rpc", "requested TX only mode"); + port->p_disable_rx = 1; + port->p_disable_tx = 0; + break; + case LLDPD_RXTX_RXONLY: + log_debug("rpc", "requested RX only mode"); + port->p_disable_rx = 0; + port->p_disable_tx = 1; + break; + case LLDPD_RXTX_BOTH: + log_debug("rpc", "requested RX/TX mode"); + port->p_disable_rx = port->p_disable_tx = 0; + break; + case LLDPD_RXTX_DISABLED: + log_debug("rpc", "requested disabled mode"); + port->p_disable_rx = port->p_disable_tx = 1; + break; + } #ifdef ENABLE_LLDPMED if (set->med_policy && set->med_policy->type > 0) { log_debug("rpc", "requested change to MED policy"); diff --git a/src/daemon/lldpd.c b/src/daemon/lldpd.c index 935b9c0a..ab1ad613 100644 --- a/src/daemon/lldpd.c +++ b/src/daemon/lldpd.c @@ -950,6 +950,12 @@ lldpd_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd) free(buffer); return; } + if (hardware->h_lport.p_disable_rx) { + log_debug("receive", "RX disabled, ignore the frame on %s", + hardware->h_ifname); + free(buffer); + return; + } if (cfg->g_config.c_paused) { log_debug("receive", "paused, ignore the frame on %s", hardware->h_ifname); @@ -971,6 +977,7 @@ lldpd_send_shutdown(struct lldpd_hardware *hardware) { struct lldpd *cfg = hardware->h_cfg; if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) return; + if (hardware->h_lport.p_disable_tx) return; if ((hardware->h_flags & IFF_RUNNING) == 0) return; @@ -989,6 +996,7 @@ lldpd_send(struct lldpd_hardware *hardware) int i, sent; if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) return; + if (hardware->h_lport.p_disable_tx) return; if ((hardware->h_flags & IFF_RUNNING) == 0) return; diff --git a/src/lib/atoms/port.c b/src/lib/atoms/port.c index 5719616e..046813aa 100644 --- a/src/lib/atoms/port.c +++ b/src/lib/atoms/port.c @@ -51,6 +51,19 @@ static lldpctl_map_t port_id_subtype_map[] = { { 0, NULL}, }; +static struct atom_map port_status_map = { + .key = lldpctl_k_port_status, + .map = { + { LLDPD_RXTX_TXONLY, "TX only" }, + { LLDPD_RXTX_RXONLY, "RX only" }, + { LLDPD_RXTX_DISABLED, "disabled" }, + { LLDPD_RXTX_BOTH, "RX and TX" }, + { 0, NULL }, + } +}; + +ATOM_MAP_REGISTER(port_status_map, 3); + static lldpctl_map_t operational_mau_type_values[] = { { 1, "AUI - no internal MAU, view from AUI" }, { 2, "10Base5 - thick coax MAU" }, @@ -314,6 +327,9 @@ _lldpctl_atom_set_atom_port(lldpctl_atom_t *atom, lldpctl_key_t key, lldpctl_ato case lldpctl_k_port_descr: set.local_descr = p->port->p_descr; break; + case lldpctl_k_port_status: + set.rxtx = LLDPD_RXTX_FROM_PORT(p->port); + break; #ifdef ENABLE_DOT3 case lldpctl_k_port_dot3_power: if (value->type != atom_dot3_power) { @@ -396,12 +412,15 @@ _lldpctl_atom_get_str_port(lldpctl_atom_t *atom, lldpctl_key_t key) char *ipaddress = NULL; size_t len; /* Local port only */ - if (hardware != NULL) { - switch (key) { - case lldpctl_k_port_name: - return hardware->h_ifname; - default: break; - } + switch (key) { + case lldpctl_k_port_name: + if (hardware != NULL) return hardware->h_ifname; + break; + case lldpctl_k_port_status: + if (p->local) return map_lookup(port_status_map.map, + LLDPD_RXTX_FROM_PORT(port)); + break; + default: break; } /* Local and remote port */ @@ -454,6 +473,32 @@ _lldpctl_atom_get_str_port(lldpctl_atom_t *atom, lldpctl_key_t key) } } +static lldpctl_atom_t* +_lldpctl_atom_set_int_port(lldpctl_atom_t *atom, lldpctl_key_t key, + long int value) +{ + struct _lldpctl_atom_port_t *p = + (struct _lldpctl_atom_port_t *)atom; + struct lldpd_port *port = p->port; + + if (p->local) { + switch (key) { + case lldpctl_k_port_status: + port->p_disable_rx = !LLDPD_RXTX_RXENABLED(value); + port->p_disable_tx = !LLDPD_RXTX_TXENABLED(value); + break; + default: + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + } else { + SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); + return NULL; + } + + return _lldpctl_atom_set_atom_port(atom, key, NULL); +} + static lldpctl_atom_t* _lldpctl_atom_set_str_port(lldpctl_atom_t *atom, lldpctl_key_t key, const char *value) @@ -465,6 +510,15 @@ _lldpctl_atom_set_str_port(lldpctl_atom_t *atom, lldpctl_key_t key, if (!value || !strlen(value)) return NULL; + if (p->local) { + switch (key) { + case lldpctl_k_port_status: + return _lldpctl_atom_set_int_port(atom, key, + map_reverse_lookup(port_status_map.map, value)); + default: break; + } + } + switch (key) { case lldpctl_k_port_id: free(port->p_id); @@ -513,6 +567,13 @@ _lldpctl_atom_get_int_port(lldpctl_atom_t *atom, lldpctl_key_t key) default: break; } } + if (p->local) { + switch (key) { + case lldpctl_k_port_status: + return LLDPD_RXTX_FROM_PORT(port); + default: break; + } + } /* Local and remote port */ switch (key) { @@ -587,6 +648,7 @@ static struct atom_builder port = .get_str = _lldpctl_atom_get_str_port, .set_str = _lldpctl_atom_set_str_port, .get_int = _lldpctl_atom_get_int_port, + .set_int = _lldpctl_atom_set_int_port, .get_buffer = _lldpctl_atom_get_buf_port }; ATOM_BUILDER_REGISTER(ports_list, 4); diff --git a/src/lib/lldpctl.h b/src/lib/lldpctl.h index c76ccd5c..3fc52037 100644 --- a/src/lib/lldpctl.h +++ b/src/lib/lldpctl.h @@ -672,6 +672,7 @@ typedef enum { lldpctl_k_port_id, /**< `(BS,WO)` The ID of this port. */ lldpctl_k_port_descr, /**< `(S,WO)` The description of this port. */ lldpctl_k_port_hidden, /**< `(I)` Is this port hidden (or should it be displayed?)? */ + 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_dot3_mfs = 1300, /**< `(I)` MFS */ diff --git a/src/lldpd-structs.h b/src/lldpd-structs.h index 27779b40..26b1cb13 100644 --- a/src/lldpd-structs.h +++ b/src/lldpd-structs.h @@ -243,6 +243,8 @@ struct lldpd_port { u_int8_t p_protocol; /* Protocol used to get this port */ u_int8_t p_hidden_in:1; /* Considered as hidden for reception */ u_int8_t p_hidden_out:2; /* Considered as hidden for emission */ + u_int8_t p_disable_rx:3; /* Should RX be disabled for this port? */ + u_int8_t p_disable_tx:4; /* Should TX be disabled for this port? */ /* Important: all fields that should be ignored to check if a port has * been changed should be before this mark. */ #define LLDPD_PORT_START_MARKER (offsetof(struct lldpd_port, p_id_subtype)) @@ -299,10 +301,22 @@ MARSHAL_SUBTQ(lldpd_port, lldpd_custom, p_custom_list) MARSHAL_END(lldpd_port); /* Used to modify some port related settings */ +#define LLDPD_RXTX_UNCHANGED 0 +#define LLDPD_RXTX_TXONLY 1 +#define LLDPD_RXTX_RXONLY 2 +#define LLDPD_RXTX_DISABLED 3 +#define LLDPD_RXTX_BOTH 4 +#define LLDPD_RXTX_FROM_PORT(p) (((p)->p_disable_rx && (p)->p_disable_tx)?LLDPD_RXTX_DISABLED: \ + ((p)->p_disable_rx && !(p)->p_disable_tx)?LLDPD_RXTX_TXONLY: \ + (!(p)->p_disable_rx && (p)->p_disable_tx)?LLDPD_RXTX_RXONLY: \ + LLDPD_RXTX_BOTH) +#define LLDPD_RXTX_RXENABLED(v) ((v) == LLDPD_RXTX_RXONLY || (v) == LLDPD_RXTX_BOTH) +#define LLDPD_RXTX_TXENABLED(v) ((v) == LLDPD_RXTX_TXONLY || (v) == LLDPD_RXTX_BOTH) struct lldpd_port_set { char *ifname; char *local_id; char *local_descr; + int rxtx; #ifdef ENABLE_LLDPMED struct lldpd_med_policy *med_policy; struct lldpd_med_loc *med_location; diff --git a/tests/lldpcli.conf b/tests/lldpcli.conf index aa63f072..2246a47c 100644 --- a/tests/lldpcli.conf +++ b/tests/lldpcli.conf @@ -29,6 +29,8 @@ configure lldp portidsubtype local Batman configure lldp portidsubtype local Batman description Batman configure lldp tx-interval 30 configure lldp tx-hold 4 +configure lldp ports eth0 status txOnly +configure lldp status rxAndTx configure lldp custom-tlv oui 33,44,55 subtype 44 configure lldp custom-tlv oui 33,44,55 subtype 44 oui-info 45,45,45,45,45 unconfigure lldp custom-tlv -- 2.39.5