/* -*- mode: c; c-file-style: "openbsd" -*- */
/*
* Copyright (c) 2013 Vincent Bernat <bernat@luffy.cx>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include "client.h"
#include "../log.h"
static int
cmd_txdelay(struct lldpctl_conn_t *conn, struct writer *w,
struct cmd_env *env, void *arg)
{
log_debug("lldpctl", "set transmit delay");
lldpctl_atom_t *config = lldpctl_get_configuration(conn);
if (config == NULL) {
log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
lldpctl_last_strerror(conn));
return 0;
}
if (lldpctl_atom_set_str(config,
lldpctl_k_config_tx_interval, cmdenv_get(env, "tx-interval")) == NULL) {
log_warnx("lldpctl", "unable to set transmit delay. %s",
lldpctl_last_strerror(conn));
lldpctl_atom_dec_ref(config);
return 0;
}
log_info("lldpctl", "transmit delay set to new value");
lldpctl_atom_dec_ref(config);
return 1;
}
static int
cmd_txhold(struct lldpctl_conn_t *conn, struct writer *w,
struct cmd_env *env, void *arg)
{
log_debug("lldpctl", "set transmit hold");
lldpctl_atom_t *config = lldpctl_get_configuration(conn);
if (config == NULL) {
log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
lldpctl_last_strerror(conn));
return 0;
}
if (lldpctl_atom_set_str(config,
lldpctl_k_config_tx_hold, cmdenv_get(env, "tx-hold")) == NULL) {
log_warnx("lldpctl", "unable to set transmit hold. %s",
lldpctl_last_strerror(conn));
lldpctl_atom_dec_ref(config);
return 0;
}
log_info("lldpctl", "transmit hold set to new value %s", cmdenv_get(env, "tx-hold"));
lldpctl_atom_dec_ref(config);
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_agent_type(struct lldpctl_conn_t *conn, struct writer *w,
struct cmd_env *env, void *arg)
{
const char *str = arg;
int value = -1;
log_debug("lldpctl", "set agent type to '%s'", str);
lldpctl_atom_t *config = lldpctl_get_configuration(conn);
if (config == NULL) {
log_warnx("lldpctl",
"unable to get configuration from lldpd. %s",
lldpctl_last_strerror(conn));
return 0;
}
for (lldpctl_map_t *b_map =
lldpctl_key_get_map(lldpctl_k_config_lldp_agent_type);
b_map->string; b_map++) {
if (!strcmp(b_map->string, str)) {
value = b_map->value;
break;
}
}
if (value == -1) {
log_warnx("lldpctl", "invalid value");
lldpctl_atom_dec_ref(config);
return 0;
}
if (lldpctl_atom_set_int(config,
lldpctl_k_config_lldp_agent_type, value) == NULL) {
log_warnx("lldpctl", "unable to set LLDP agent type."
" %s", lldpctl_last_strerror(conn));
lldpctl_atom_dec_ref(config);
return 0;
}
log_info("lldpctl", "agent type set to new value : %s", str);
lldpctl_atom_dec_ref(config);
return 1;
}
static int
cmd_portid_type_local(struct lldpctl_conn_t *conn, struct writer *w,
struct cmd_env *env, void *arg)
{
lldpctl_atom_t *port;
const char *name;
const char *id = cmdenv_get(env, "port-id");
const char *descr = cmdenv_get(env, "port-descr");
log_debug("lldpctl", "lldp PortID TLV Subtype Local port-id '%s' port-descr '%s'", id, descr);
if (!id || !strlen(id)) {
log_warnx("lldpctl", "no id specified");
return 0;
}
while ((port = cmd_iterate_on_ports(conn, env, &name))) {
if (lldpctl_atom_set_str(port, lldpctl_k_port_id, id) == NULL) {
log_warnx("lldpctl", "unable to set LLDP PortID for %s."
" %s", name, lldpctl_last_strerror(conn));
}
if (descr && lldpctl_atom_set_str(port, lldpctl_k_port_descr, descr) == NULL) {
log_warnx("lldpctl", "unable to set LLDP Port Description for %s."
" %s", name, lldpctl_last_strerror(conn));
}
}
return 1;
}
static int
cmd_port_descr(struct lldpctl_conn_t *conn, struct writer *w,
struct cmd_env *env, void *arg)
{
lldpctl_atom_t *port;
const char *name;
const char *descr = cmdenv_get(env, "port-descr");
log_debug("lldpctl", "lldp port-descr '%s'", descr);
while ((port = cmd_iterate_on_ports(conn, env, &name))) {
if (descr && lldpctl_atom_set_str(port, lldpctl_k_port_descr, descr) == NULL) {
log_warnx("lldpctl", "unable to set LLDP Port Description for %s."
" %s", name, lldpctl_last_strerror(conn));
}
}
return 1;
}
static int
cmd_portid_type(struct lldpctl_conn_t *conn, struct writer *w,
struct cmd_env *env, void *arg)
{
char *value_str;
int value = -1;
log_debug("lldpctl", "lldp PortID TLV Subtype");
lldpctl_atom_t *config = lldpctl_get_configuration(conn);
if (config == NULL) {
log_warnx("lldpctl",
"unable to get configuration from lldpd. %s",
lldpctl_last_strerror(conn));
return 0;
}
value_str = arg;
for (lldpctl_map_t *b_map =
lldpctl_key_get_map(lldpctl_k_config_lldp_portid_type);
b_map->string; b_map++) {
if (!strcmp(b_map->string, value_str)) {
value = b_map->value;
break;
}
}
if (value == -1) {
log_warnx("lldpctl", "invalid value");
lldpctl_atom_dec_ref(config);
return 0;
}
if (lldpctl_atom_set_int(config,
lldpctl_k_config_lldp_portid_type, value) == NULL) {
log_warnx("lldpctl", "unable to set LLDP PortID type."
" %s", lldpctl_last_strerror(conn));
lldpctl_atom_dec_ref(config);
return 0;
}
log_info("lldpctl", "LLDP PortID TLV type set to new value : %s", value_str);
lldpctl_atom_dec_ref(config);
return 1;
}
static int
cmd_chassis_cap_advertise(struct lldpctl_conn_t *conn, struct writer *w,
struct cmd_env *env, void *arg)
{
log_debug("lldpctl", "lldp capabilities-advertisements %s", arg?"enable":"disable");
lldpctl_atom_t *config = lldpctl_get_configuration(conn);
if (config == NULL) {
log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
lldpctl_last_strerror(conn));
return 0;
}
if (lldpctl_atom_set_int(config,
lldpctl_k_config_chassis_cap_advertise,
arg?1:0) == NULL) {
log_warnx("lldpctl", "unable to %s chassis capabilities advertisement: %s",
arg?"enable":"disable",
lldpctl_last_strerror(conn));
lldpctl_atom_dec_ref(config);
return 0;
}
log_info("lldpctl", "chassis capabilities advertisement %s",
arg?"enabled":"disabled");
lldpctl_atom_dec_ref(config);
return 1;
}
/* FIXME: see about compressing this with other functions */
static int
cmd_chassis_mgmt_advertise(struct lldpctl_conn_t *conn, struct writer *w,
struct cmd_env *env, void *arg)
{
log_debug("lldpctl", "lldp management-addresses-advertisements %s", arg?"enable":"disable");
lldpctl_atom_t *config = lldpctl_get_configuration(conn);
if (config == NULL) {
log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
lldpctl_last_strerror(conn));
return 0;
}
if (lldpctl_atom_set_int(config,
lldpctl_k_config_chassis_mgmt_advertise,
arg?1:0) == NULL) {
log_warnx("lldpctl", "unable to %s management addresses advertisement: %s",
arg?"enable":"disable",
lldpctl_last_strerror(conn));
lldpctl_atom_dec_ref(config);
return 0;
}
log_info("lldpctl", "management addresses advertisement %s",
arg?"enabled":"disabled");
lldpctl_atom_dec_ref(config);
return 1;
}
#ifdef ENABLE_CUSTOM
static int
cmd_custom_tlv_set(struct lldpctl_conn_t *conn, struct writer *w,
struct cmd_env *env, void *arg)
{
lldpctl_atom_t *port;
const char *s;
const char *name;
uint8_t oui[LLDP_TLV_ORG_OUI_LEN];
uint8_t oui_info[LLDP_TLV_ORG_OUI_INFO_MAXLEN];
int oui_info_len = 0;
uint16_t subtype = 0;
char *op = "add";
if (!arg || !strcmp(arg, "remove"))
op = "remove";
log_debug("lldpctl", "lldp custom-tlv(s) %s", op);
if (!arg)
goto set;
s = cmdenv_get(env, "oui");
if (!s || (
sscanf(s, "%02hhx,%02hhx,%02hhx", &oui[0], &oui[1], &oui[2]) != 3 &&
sscanf(s, "%02hhX,%02hhX,%02hhX", &oui[0], &oui[1], &oui[2]) != 3) ) {
log_warnx("lldpctl", "invalid OUI value '%s'", s);
return 0;
}
s = cmdenv_get(env, "subtype");
if (!s) {
log_warnx("lldpctl", "no subtype specified");
return 0;
} else {
const char *errstr;
subtype = strtonum(s, 0, 255, &errstr);
if (errstr != NULL) {
log_warnx("lldpctl", "invalid subtype value `%s': %s",
s, errstr);
return 0;
}
}
s = cmdenv_get(env, "oui-info");
/* This info is optional */
if (s) {
const char delim[] = ",";
char *s_copy = strdup(s);
char *token = strtok(s_copy, delim);
while (token != NULL) {
if (sscanf(token, "%02hhx", &oui_info[oui_info_len]) == 1 ||
sscanf(token, "%02hhX", &oui_info[oui_info_len]) == 1)
oui_info_len++;
if (oui_info_len >= sizeof(oui_info))
break;
token = strtok(NULL, delim);
}
free(s_copy);
}
s = cmdenv_get(env, "replace");
/* This info is optional */
if (s) op = "replace";
set:
while ((port = cmd_iterate_on_ports(conn, env, &name))) {
lldpctl_atom_t *custom_tlvs;
if (!arg) {
lldpctl_atom_set(port, lldpctl_k_custom_tlvs_clear, NULL);
} else if (!(custom_tlvs = lldpctl_atom_get(port, lldpctl_k_custom_tlvs))) {
log_warnx("lldpctl", "unable to get custom TLVs for port %s", name);
} else {
lldpctl_atom_t *tlv = lldpctl_atom_create(custom_tlvs);
if (!tlv) {
log_warnx("lldpctl", "unable to create new custom TLV for port %s",
name);
} else {
/* Configure custom TLV */
lldpctl_atom_set_buffer(tlv, lldpctl_k_custom_tlv_oui, oui, sizeof(oui));
lldpctl_atom_set_int(tlv, lldpctl_k_custom_tlv_oui_subtype, subtype);
lldpctl_atom_set_buffer(tlv, lldpctl_k_custom_tlv_oui_info_string, oui_info, oui_info_len);
lldpctl_atom_set_str(tlv, lldpctl_k_custom_tlv_op, op);
/* Assign it to port */
lldpctl_atom_set(port, lldpctl_k_custom_tlv, tlv);
lldpctl_atom_dec_ref(tlv);
}
lldpctl_atom_dec_ref(custom_tlvs);
}
}
return 1;
}
static int
cmd_check_no_add_env(struct cmd_env *env, void *arg)
{
const char *what = arg;
if (cmdenv_get(env, "add")) return 0;
if (cmdenv_get(env, what)) return 0;
return 1;
}
static int
cmd_check_no_replace_env(struct cmd_env *env, void *arg)
{
const char *what = arg;
if (cmdenv_get(env, "replace")) return 0;
if (cmdenv_get(env, what)) return 0;
return 1;
}
void
register_commands_configure_lldp_custom_tlvs(struct cmd_node *configure_lldp,
struct cmd_node *unconfigure_lldp)
{
struct cmd_node *configure_custom_tlvs;
struct cmd_node *unconfigure_custom_tlvs;
struct cmd_node *configure_custom_tlvs_basic;
struct cmd_node *unconfigure_custom_tlvs_basic;
configure_custom_tlvs =
commands_new(configure_lldp,
"custom-tlv",
"Add custom TLV(s) to be broadcast on ports",
NULL, NULL, NULL);
unconfigure_custom_tlvs =
commands_new(unconfigure_lldp,
"custom-tlv",
"Remove ALL custom TLV(s)",
NULL, NULL, NULL);
commands_new(unconfigure_custom_tlvs,
NEWLINE, "Remove ALL custom TLV",
NULL, cmd_custom_tlv_set, NULL);
commands_new(configure_custom_tlvs,
"add", "Add custom TLV",
cmd_check_no_replace_env, cmd_store_env_and_pop, "add");
commands_new(configure_custom_tlvs,
"replace", "Replace custom TLV",
cmd_check_no_add_env, cmd_store_env_and_pop, "replace");
/* Basic form: 'configure lldp custom-tlv oui 11,22,33 subtype 44' */
configure_custom_tlvs_basic =
commands_new(
commands_new(
commands_new(
commands_new(configure_custom_tlvs,
"oui", "Organizationally Unique Identifier",
NULL, NULL, NULL),
NULL, "Organizationally Unique Identifier",
NULL, cmd_store_env_value, "oui"),
"subtype", "Organizationally Defined Subtype",
NULL, NULL, NULL),
NULL, "Organizationally Defined Subtype",
NULL, cmd_store_env_value, "subtype");
commands_new(configure_custom_tlvs_basic,
NEWLINE, "Add custom TLV(s) to be broadcast on ports",
NULL, cmd_custom_tlv_set, "enable");
/* Basic form: 'unconfigure lldp custom-tlv oui 11,22,33 subtype 44' */
unconfigure_custom_tlvs_basic =
commands_new(
commands_new(
commands_new(
commands_new(unconfigure_custom_tlvs,
"oui", "Organizationally Unique Identifier",
NULL, NULL, NULL),
NULL, "Organizationally Unique Identifier",
NULL, cmd_store_env_value, "oui"),
"subtype", "Organizationally Defined Subtype",
NULL, NULL, NULL),
NULL, "Organizationally Defined Subtype",
NULL, cmd_store_env_value, "subtype");
commands_new(unconfigure_custom_tlvs_basic,
NEWLINE, "Remove specific custom TLV",
NULL, cmd_custom_tlv_set, "remove");
/* Extended form: 'configure custom-tlv lldp oui 11,22,33 subtype 44 oui-info 55,66,77,...' */
commands_new(
commands_new(
commands_new(configure_custom_tlvs_basic,
"oui-info", "Organizationally Unique Identifier",
NULL, NULL, NULL),
NULL, "OUI Info String",
NULL, cmd_store_env_value, "oui-info"),
NEWLINE, "Add custom TLV(s) to be broadcast on ports",
NULL, cmd_custom_tlv_set, "enable");
}
#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.
*
* Those are the commands that are related to the LLDP protocol but not
* Dot1/Dot3/MED. Commands not related to LLDP should go in system instead.
*/
void
register_commands_configure_lldp(struct cmd_node *configure,
struct cmd_node *unconfigure)
{
struct cmd_node *configure_lldp = commands_new(
configure,
"lldp", "LLDP configuration",
NULL, NULL, NULL);
struct cmd_node *unconfigure_lldp = commands_new(
unconfigure,
"lldp", "LLDP configuration",
NULL, NULL, NULL);
commands_new(
commands_new(
commands_new(configure_lldp,
"tx-interval", "Set LLDP transmit delay",
cmd_check_no_env, NULL, "ports"),
NULL, "LLDP transmit delay in seconds",
NULL, cmd_store_env_value, "tx-interval"),
NEWLINE, "Set LLDP transmit delay",
NULL, cmd_txdelay, NULL);
commands_new(
commands_new(
commands_new(configure_lldp,
"tx-hold", "Set LLDP transmit hold",
cmd_check_no_env, NULL, "ports"),
NULL, "LLDP transmit hold in seconds",
NULL, cmd_store_env_value, "tx-hold"),
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));
SUPPRESS_LEAK(tag);
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);
}
/* Configure the various agent type we can configure. */
struct cmd_node *configure_lldp_agent_type = commands_new(
configure_lldp,
"agent-type",
"LLDP agent type",
NULL, NULL, NULL);
for (lldpctl_map_t *b_map =
lldpctl_key_get_map(lldpctl_k_config_lldp_agent_type);
b_map->string; b_map++) {
const char *tag = strdup(totag(b_map->string));
SUPPRESS_LEAK(tag);
commands_new(
commands_new(configure_lldp_agent_type,
tag,
b_map->string,
NULL, NULL, NULL),
NEWLINE, "Set LLDP agent type",
NULL, cmd_agent_type, b_map->string);
}
/* Now handle the various portid subtypes we can configure. */
struct cmd_node *configure_lldp_portid_type = commands_new(
configure_lldp,
"portidsubtype", "LLDP PortID TLV Subtype",
NULL, NULL, NULL);
for (lldpctl_map_t *b_map =
lldpctl_key_get_map(lldpctl_k_config_lldp_portid_type);
b_map->string; b_map++) {
if (!strcmp(b_map->string, "ifname")) {
commands_new(
commands_new(configure_lldp_portid_type,
b_map->string, "Interface Name",
cmd_check_no_env, NULL, "ports"),
NEWLINE, NULL,
NULL, cmd_portid_type,
b_map->string);
} else if (!strcmp(b_map->string, "local")) {
struct cmd_node *port_id = commands_new(
commands_new(configure_lldp_portid_type,
b_map->string, "Local",
NULL, NULL, NULL),
NULL, "Port ID",
NULL, cmd_store_env_value, "port-id");
commands_new(port_id,
NEWLINE, "Set local port ID",
NULL, cmd_portid_type_local,
b_map->string);
commands_new(
commands_new(
commands_new(port_id,
"description",
"Also set port description",
NULL, NULL, NULL),
NULL, "Port description",
NULL, cmd_store_env_value, "port-descr"),
NEWLINE, "Set local port ID and description",
NULL, cmd_portid_type_local, NULL);
} else if (!strcmp(b_map->string, "macaddress")) {
commands_new(
commands_new(configure_lldp_portid_type,
b_map->string, "MAC Address",
cmd_check_no_env, NULL, "ports"),
NEWLINE, NULL,
NULL, cmd_portid_type,
b_map->string);
}
}
commands_new(
commands_new(
commands_new(configure_lldp,
"portdescription",
"Port Description",
NULL, NULL, NULL),
NULL, "Port description",
NULL, cmd_store_env_value, "port-descr"),
NEWLINE, "Set port description",
NULL, cmd_port_descr, NULL);
commands_new(
commands_new(configure_lldp,
"capabilities-advertisements",
"Enable chassis capabilities advertisement",
cmd_check_no_env, NULL, "ports"),
NEWLINE, "Enable chassis capabilities advertisement",
NULL, cmd_chassis_cap_advertise, "enable");
commands_new(
commands_new(unconfigure_lldp,
"capabilities-advertisements",
"Don't enable chassis capabilities advertisement",
NULL, NULL, NULL),
NEWLINE, "Don't enable chassis capabilities advertisement",
NULL, cmd_chassis_cap_advertise, NULL);
commands_new(
commands_new(configure_lldp,
"management-addresses-advertisements",
"Enable management addresses advertisement",
NULL, NULL, NULL),
NEWLINE, "Enable management addresses advertisement",
NULL, cmd_chassis_mgmt_advertise, "enable");
commands_new(
commands_new(unconfigure_lldp,
"management-addresses-advertisements",
"Don't enable management addresses advertisement",
NULL, NULL, NULL),
NEWLINE, "Don't enable management addresses advertisement",
NULL, cmd_chassis_mgmt_advertise, NULL);
#ifdef ENABLE_CUSTOM
register_commands_configure_lldp_custom_tlvs(configure_lldp, unconfigure_lldp);
#endif
}