* Change:
+ For Linux, switch to libnl3. Be aware of the licensing issues in
case of static linking.
+ + Introduce the notion of default local port. New interfaces will
+ use it as a base. This allows setting various MED stuff.
lldpd (0.7.17)
* Fix:
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 *,
+ struct cmd_env *, const char **);
void cmd_restrict_ports(struct cmd_node *);
void cmd_restrict_protocol(struct cmd_node *);
return iface;
}
+/**
+ * Provide an iterator on all ports contained in "ports", as well as the
+ * default port.
+ *
+ * @warning This function is not reentrant. It uses static variables to keep
+ * track of ports that have already been provided. Moreover, to release all
+ * resources, the iterator should be used until its end.
+ *
+ * @param conn The connection.
+ * @param env The environment.
+ * @param name Name of the interface (for logging purpose)
+ * @return The next interface in the set of ports (or in all ports if no `ports`
+ * variable is present in the environment), including the default port
+ * if no `ports` variable is present in the environment.
+ */
+lldpctl_atom_t*
+cmd_iterate_on_ports(struct lldpctl_conn_t *conn, struct cmd_env *env, const char **name)
+{
+ static int put_default = 0;
+ static lldpctl_atom_t *last_port = NULL;
+ const char *interfaces = cmdenv_get(env, "ports");
+
+ if (last_port) {
+ lldpctl_atom_dec_ref(last_port);
+ last_port = NULL;
+ }
+ if (!put_default) {
+ lldpctl_atom_t *iface = cmd_iterate_on_interfaces(conn, env);
+ if (iface) {
+ *name = lldpctl_atom_get_str(iface, lldpctl_k_interface_name);
+ last_port = lldpctl_get_port(iface);
+ return last_port;
+ }
+ if (!iface && !interfaces) {
+ put_default = 1;
+ *name = "(default)";
+ last_port = lldpctl_get_default_port(conn);
+ return last_port;
+ }
+ return NULL;
+ } else {
+ put_default = 0;
+ return NULL;
+ }
+}
+
/**
* Restrict the command to some ports.
*/
cmd_portid_type_local(struct lldpctl_conn_t *conn, struct writer *w,
struct cmd_env *env, void *arg)
{
- lldpctl_atom_t *iface;
+ lldpctl_atom_t *port;
+ const char *name;
const char *id = cmdenv_get(env, "port-id");
const char *descr = cmdenv_get(env, "port-descr");
return 0;
}
- while ((iface = cmd_iterate_on_interfaces(conn, env))) {
- lldpctl_atom_t *port = lldpctl_get_port(iface);
+ 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."
- " %s", lldpctl_last_strerror(conn));
+ 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."
- " %s", lldpctl_last_strerror(conn));
+ log_warnx("lldpctl", "unable to set LLDP Port Description for %s."
+ " %s", name, lldpctl_last_strerror(conn));
}
- lldpctl_atom_dec_ref(port);
}
return 1;
cmd_custom_tlv_set(struct lldpctl_conn_t *conn, struct writer *w,
struct cmd_env *env, void *arg)
{
- lldpctl_atom_t *iface;
+ 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;
}
set:
- while ((iface = cmd_iterate_on_interfaces(conn, env))) {
- lldpctl_atom_t *port = lldpctl_get_port(iface);
+ 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");
+ 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");
+ 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_dec_ref(custom_tlvs);
}
- lldpctl_atom_dec_ref(port);
}
return 1;
_cmd_medlocation(struct lldpctl_conn_t *conn,
struct cmd_env *env, int format)
{
- lldpctl_atom_t *iface;
- while ((iface = cmd_iterate_on_interfaces(conn, env))) {
- const char *name = lldpctl_atom_get_str(iface, lldpctl_k_interface_name);
- lldpctl_atom_t *port = lldpctl_get_port(iface);
+ lldpctl_atom_t *port;
+ const char *name;
+ while ((port = cmd_iterate_on_ports(conn, env, &name))) {
lldpctl_atom_t *med_location = NULL, *med_locations = NULL;
const char *what = NULL;
int ok = 0;
med_locations = lldpctl_atom_get(port, lldpctl_k_port_med_locations);
if (med_locations == NULL) {
log_warnx("lldpctl", "unable to set LLDP-MED location: support seems unavailable");
- goto end;
+ continue; /* Iterator needs to be run until end */
}
med_location = lldpctl_atom_iter_value(med_locations,
name);
}
- end:
lldpctl_atom_dec_ref(med_location);
lldpctl_atom_dec_ref(med_locations);
- lldpctl_atom_dec_ref(port);
}
return 1;
}
struct cmd_env *env, void *arg)
{
log_debug("lldpctl", "set MED power");
- lldpctl_atom_t *iface;
- while ((iface = cmd_iterate_on_interfaces(conn, env))) {
- const char *name = lldpctl_atom_get_str(iface, lldpctl_k_interface_name);
- lldpctl_atom_t *port = lldpctl_get_port(iface);
+ lldpctl_atom_t *port;
+ const char *name;
+ while ((port = cmd_iterate_on_ports(conn, env, &name))) {
lldpctl_atom_t *med_power;
const char *what = NULL;
med_power = lldpctl_atom_get(port, lldpctl_k_port_med_power);
if (med_power == NULL) {
log_warnx("lldpctl", "unable to set LLDP-MED power: support seems unavailable");
- goto end;
+ continue; /* Need to finish the loop */
}
if ((what = "device type", lldpctl_atom_set_str(med_power,
name);
}
- end:
lldpctl_atom_dec_ref(med_power);
- lldpctl_atom_dec_ref(port);
}
return 1;
}
struct cmd_env *env, void *arg)
{
log_debug("lldpctl", "set dot3 power");
- lldpctl_atom_t *iface;
- while ((iface = cmd_iterate_on_interfaces(conn, env))) {
- const char *name = lldpctl_atom_get_str(iface, lldpctl_k_interface_name);
- lldpctl_atom_t *port = lldpctl_get_port(iface);
+ lldpctl_atom_t *port;
+ const char *name;
+ while ((port = cmd_iterate_on_ports(conn, env, &name))) {
lldpctl_atom_t *dot3_power;
const char *what = NULL;
int ok = 1;
dot3_power = lldpctl_atom_get(port, lldpctl_k_port_dot3_power);
if (dot3_power == NULL) {
log_warnx("lldpctl", "unable to set Dot3 power: support seems unavailable");
- goto end;
+ continue; /* Need to finish the loop */
}
if ((what = "device type", lldpctl_atom_set_str(dot3_power,
name);
}
- end:
lldpctl_atom_dec_ref(dot3_power);
- lldpctl_atom_dec_ref(port);
}
return 1;
}
GET_INTERFACES, /* Get list of interfaces */
GET_CHASSIS, /* Get local chassis */
GET_INTERFACE, /* Get all information related to an interface */
+ GET_DEFAULT_PORT, /* Get all information related to default port */
SET_PORT, /* Set port-related information (location, power, policy) */
SUBSCRIBE, /* Subscribe to neighbor changes */
NOTIFICATION, /* Notification message (sent by lldpd!) */
client_handle_get_local_chassis(struct lldpd *cfg, enum hmsg_type *type,
void *input, int input_len, void **output, int *subscribed)
{
- struct lldpd_chassis *chassis = LOCAL_CHASSIS(cfg);
+ struct lldpd_chassis *chassis = LOCAL_CHASSIS(cfg);
ssize_t output_len;
log_debug("rpc", "client request the local chassis");
return 0;
}
+/* Return all available information related to an interface
+ Input: name of the interface (serialized)
+ Output: Information about the interface (lldpd_hardware)
+*/
+static ssize_t
+client_handle_get_default_port(struct lldpd *cfg, enum hmsg_type *type,
+ void *input, int input_len, void **output, int *subscribed)
+{
+ log_debug("rpc", "client request the default local port");
+ ssize_t output_len = lldpd_port_serialize(cfg->g_default_local_port, output);
+ if (output_len <= 0) {
+ *type = NONE;
+ return 0;
+ }
+ return output_len;
+}
+
static int
_client_handle_set_port(struct lldpd *cfg,
struct lldpd_port *port, struct lldpd_port_set *set)
}
/* Search the appropriate hardware */
- log_debug("rpc", "client request change to port %s", set->ifname);
- TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
- if (!strcmp(hardware->h_ifname, set->ifname)) {
- struct lldpd_port *port = &hardware->h_lport;
- if (_client_handle_set_port(cfg, port, set) == -1)
- goto set_port_finished;
- ret = 1;
- break;
- }
+ if (strlen(set->ifname) == 0) {
+ log_debug("rpc", "client request change to default port");
+ if (_client_handle_set_port(cfg, cfg->g_default_local_port, set) == -1)
+ goto set_port_finished;
+ ret = 1;
+ } else {
+ log_debug("rpc", "client request change to port %s", set->ifname);
+ TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+ if (!strcmp(hardware->h_ifname, set->ifname)) {
+ struct lldpd_port *port = &hardware->h_lport;
+ if (_client_handle_set_port(cfg, port, set) == -1)
+ goto set_port_finished;
+ ret = 1;
+ break;
+ }
+ }
+ }
if (ret == 0)
log_warn("rpc", "no interface %s found", set->ifname);
{ SET_CONFIG, "Set configuration", client_handle_set_configuration },
{ GET_INTERFACES, "Get interfaces", client_handle_get_interfaces },
{ GET_INTERFACE, "Get interface", client_handle_get_interface },
+ { GET_DEFAULT_PORT, "Get default port", client_handle_get_default_port },
{ GET_CHASSIS, "Get local chassis", client_handle_get_local_chassis },
{ SET_PORT, "Set port", client_handle_set_port },
{ SUBSCRIBE, "Subscribe", client_handle_subscribe },
return hardware;
}
+/**
+ * Allocate the default local port. This port will be cloned each time we need a
+ * new local port.
+ */
+static void
+lldpd_alloc_default_local_port(struct lldpd *cfg)
+{
+ struct lldpd_port *port;
+
+ if ((port = (struct lldpd_port *)
+ calloc(1, sizeof(struct lldpd_port))) == NULL)
+ fatal("main", NULL);
+
+#ifdef ENABLE_DOT1
+ TAILQ_INIT(&port->p_vlans);
+ TAILQ_INIT(&port->p_ppvids);
+ TAILQ_INIT(&port->p_pids);
+#endif
+#ifdef ENABLE_CUSTOM
+ TAILQ_INIT(&port->p_custom_list);
+#endif
+ cfg->g_default_local_port = port;
+}
+
+/**
+ * Clone a given port. The destination needs to be already allocated.
+ */
+static int
+lldpd_clone_port(struct lldpd_port *destination, struct lldpd_port *source)
+{
+
+ u_int8_t *output = NULL;
+ ssize_t output_len;
+ struct lldpd_port *cloned = NULL;
+ output_len = lldpd_port_serialize(source, (void**)&output);
+ if (output_len == -1 ||
+ lldpd_port_unserialize(output, output_len, &cloned) <= 0) {
+ log_warnx("alloc", "unable to clone default port");
+ goto end;
+ }
+ memcpy(destination, cloned, sizeof(struct lldpd_port));
+ free(cloned); cloned = NULL;
+#ifdef ENABLE_DOT1
+ marshal_repair_tailq(lldpd_vlan, &destination->p_vlans, v_entries);
+ marshal_repair_tailq(lldpd_ppvid, &destination->p_ppvids, p_entries);
+ marshal_repair_tailq(lldpd_pi, &destination->p_pids, p_entries);
+#endif
+#ifdef ENABLE_CUSTOM
+ marshal_repair_tailq(lldpd_custom, &destination->p_custom_list, next);
+#endif
+ return 0;
+
+end:
+ free(output);
+ if (cloned != NULL) lldpd_port_cleanup(cloned, 1);
+ return -1;
+}
+
struct lldpd_hardware *
lldpd_alloc_hardware(struct lldpd *cfg, char *name, int index)
{
calloc(1, sizeof(struct lldpd_hardware))) == NULL)
return NULL;
+ /* Clone default local port */
+ if (lldpd_clone_port(&hardware->h_lport, cfg->g_default_local_port) == -1) {
+ log_warnx("alloc", "unable to clone default port");
+ free(hardware);
+ return NULL;
+ }
+
hardware->h_cfg = cfg;
strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname));
hardware->h_ifindex = index;
hardware->h_lport.p_med_cap_enabled |= LLDP_MED_CAP_IV;
}
#endif
-#ifdef ENABLE_DOT1
- TAILQ_INIT(&hardware->h_lport.p_vlans);
- TAILQ_INIT(&hardware->h_lport.p_ppvids);
- TAILQ_INIT(&hardware->h_lport.p_pids);
-#endif
-#ifdef ENABLE_CUSTOM
- TAILQ_INIT(&hardware->h_lport.p_custom_list);
-#endif
levent_hardware_init(hardware);
return hardware;
calloc(1, sizeof(struct lldpd))) == NULL)
fatal("main", NULL);
+ lldpd_alloc_default_local_port(cfg);
cfg->g_ctlname = ctlname;
cfg->g_ctl = ctl;
cfg->g_config.c_mgmt_pattern = mgmtp;
struct lldpd_netlink *g_netlink;
#endif
+ struct lldpd_port *g_default_local_port;
#define LOCAL_CHASSIS(cfg) ((struct lldpd_chassis *)(TAILQ_FIRST(&cfg->g_chassis)))
TAILQ_HEAD(, lldpd_chassis) g_chassis;
TAILQ_HEAD(, lldpd_hardware) g_hardware;
# -version-number could be computed from -version-info, mostly major
# is `current` - `age`, minor is `age` and revision is `revision' and
# major.minor should be used when updaing lldpctl.map.
-liblldpctl_la_LDFLAGS = $(AM_LDFLAGS) -version-info 11:0:7
+liblldpctl_la_LDFLAGS = $(AM_LDFLAGS) -version-info 12:0:8
liblldpctl_la_DEPENDENCIES = libfixedpoint.la
if HAVE_LD_VERSION_SCRIPT
&p, &MARSHAL_INFO(lldpd_hardware));
if (rc == 0) {
hardware = p;
- return _lldpctl_new_atom(conn, atom_port,
+ return _lldpctl_new_atom(conn, atom_port, 1,
hardware, &hardware->h_lport, NULL);
}
return NULL;
}
+lldpctl_atom_t*
+lldpctl_get_default_port(lldpctl_conn_t *conn)
+{
+ struct lldpd_port *port;
+ void *p;
+ int rc;
+
+ RESET_ERROR(conn);
+
+ rc = _lldpctl_do_something(conn,
+ CONN_STATE_GET_DEFAULT_PORT_SEND, CONN_STATE_GET_DEFAULT_PORT_RECV, "",
+ GET_DEFAULT_PORT, NULL, NULL,
+ &p, &MARSHAL_INFO(lldpd_port));
+ if (rc == 0) {
+ port = p;
+ return _lldpctl_new_atom(conn, atom_port, 1, NULL, port, NULL);
+ }
+ return NULL;
+}
+
static lldpctl_map_t empty_map[] = {{ 0, NULL }};
static struct atom_map atom_map_list = {
#define CONN_STATE_SET_CONFIG_RECV 11
#define CONN_STATE_GET_CHASSIS_SEND 12
#define CONN_STATE_GET_CHASSIS_RECV 13
+#define CONN_STATE_GET_DEFAULT_PORT_SEND 14
+#define CONN_STATE_GET_DEFAULT_PORT_RECV 15
int state; /* Current state */
char *state_data; /* Data attached to the state. It is used to
* check that we are using the same data as a
struct _lldpctl_atom_port_t {
lldpctl_atom_t base;
- struct lldpd_hardware *hardware; /* Local port only */
+ int local; /* Local or remote port? */
+ struct lldpd_hardware *hardware; /* Local port only (but optional) */
struct lldpd_port *port; /* Local and remote */
struct _lldpctl_atom_port_t *parent; /* Local port if we are a remote port */
lldpctl_atom_t *chassis; /* Internal atom for chassis */
struct lldpd_port *port = dpow->parent->port;
/* Only local port can be modified */
- if (dpow->parent->hardware == NULL) {
+ if (!dpow->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
(struct _lldpctl_atom_med_policy_t *)atom;
/* Only local port can be modified */
- if (m->parent->hardware == NULL) {
+ if (!m->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
(struct _lldpctl_atom_med_location_t *)atom;
/* Only local port can be modified */
- if (mloc->parent->hardware == NULL) {
+ if (!mloc->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
char *end = NULL;
/* Only local port can be modified */
- if (mloc->parent->hardware == NULL) {
+ if (!mloc->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
uint8_t *new;
/* Only local port can be modified */
- if (m->parent->hardware == NULL) {
+ if (!m->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
(struct _lldpctl_atom_med_caelement_t *)atom;
/* Only local port can be modified */
- if (el->parent->parent->hardware == NULL) {
+ if (!el->parent->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
size_t len;
/* Only local port can be modified */
- if (el->parent->parent->hardware == NULL) {
+ if (!el->parent->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
struct lldpd_port *port = dpow->parent->port;
/* Only local port can be modified */
- if (dpow->parent->hardware == NULL) {
+ if (!dpow->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
_lldpctl_atom_value_ports_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
{
struct lldpd_port *port = (struct lldpd_port *)iter;
- return _lldpctl_new_atom(atom->conn, atom_port, NULL, port,
+ return _lldpctl_new_atom(atom->conn, atom_port, 0, NULL, port,
((struct _lldpctl_atom_any_list_t *)atom)->parent);
}
{
struct _lldpctl_atom_port_t *port =
(struct _lldpctl_atom_port_t *)atom;
+ port->local = va_arg(ap, int);
port->hardware = va_arg(ap, struct lldpd_hardware*);
port->port = va_arg(ap, struct lldpd_port*);
port->parent = va_arg(ap, struct _lldpctl_atom_port_t*);
else if (!hardware) {
/* No parent, no hardware, we assume a single neighbor: one
* port, one chassis. */
- lldpd_chassis_cleanup(port->port->p_chassis, 1);
- port->port->p_chassis = NULL;
+ if (port->port->p_chassis) {
+ lldpd_chassis_cleanup(port->port->p_chassis, 1);
+ port->port->p_chassis = NULL;
+ }
lldpd_port_cleanup(port->port, 1);
free(port->port);
}
/* Local and remote port */
switch (key) {
case lldpctl_k_port_chassis:
- return _lldpctl_new_atom(atom->conn, atom_chassis,
- port->p_chassis, p, 0);
+ if (port->p_chassis) {
+ return _lldpctl_new_atom(atom->conn, atom_chassis,
+ port->p_chassis, p, 0);
+ }
+ SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+ return NULL;
#ifdef ENABLE_DOT3
case lldpctl_k_port_dot3_power:
return _lldpctl_new_atom(atom->conn, atom_dot3_power,
#endif
default:
/* Compatibility: query the associated chassis too */
- return lldpctl_atom_get(p->chassis, key);
+ if (port->p_chassis)
+ return lldpctl_atom_get(p->chassis, key);
+ SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+ return NULL;
}
}
struct _lldpctl_atom_custom_t *custom;
#endif
- /* Local port only */
- if (hardware == NULL) {
+ /* Local and default port only */
+ if (!p->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
return NULL;
}
- set.ifname = hardware->h_ifname;
+ set.ifname = hardware ? hardware->h_ifname : "";
if (asprintf(&canary, "%d%p%s", key, value, set.ifname) == -1) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
interface = _lldpctl_new_atom(conn, atom_interface,
change->ifname);
if (interface == NULL) goto end;
- neighbor = _lldpctl_new_atom(conn, atom_port,
+ neighbor = _lldpctl_new_atom(conn, atom_port, 0,
NULL, change->neighbor, NULL);
if (neighbor == NULL) goto end;
conn->watch_cb(conn, type, interface, neighbor, conn->watch_data);
* atom retrieved from an interation on @c lldpctl_get_interfaces().
* @return Atom related to this port which may be used in subsequent functions.
*
- * This functions may have to do IO to get the information related to the given
+ * This function may have to do IO to get the information related to the given
* port. Depending on the IO mode, information may not be available right now
* and the function should be called again later. If @c NULL is returned, check
* what the last error is. If it is @c LLDPCTL_ERR_WOULDBLOCK, try again later
*/
lldpctl_atom_t *lldpctl_get_port(lldpctl_atom_t *port);
+/**
+ * Retrieve the default port information.
+ *
+ * This port contains default settings whenever a new port needs to be created.
+ *
+ * @param conn Previously allocated handler to a connection to lldpd.
+ * @return Atom of the default port which may be used in subsequent functions.
+ *
+ * This function may have to do IO to get the information related to the given
+ * port. Depending on the IO mode, information may not be available right now
+ * and the function should be called again later. If @c NULL is returned, check
+ * what the last error is. If it is @c LLDPCTL_ERR_WOULDBLOCK, try again later
+ * (when more data is available).
+ */
+lldpctl_atom_t *lldpctl_get_default_port(lldpctl_conn_t *conn);
+
/**@}*/
/**
+LIBLLDPCTL_4.8 {
+ global:
+ lldpctl_get_default_port;
+};
+
LIBLLDPCTL_4.7 {
global:
lldpctl_get_local_chassis;