/* -*- mode: c; c-file-style: "openbsd" -*- */
/*
* Copyright (c) 2015 Vincent Bernat <vincent@bernat.im>
*
* 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 <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <arpa/inet.h>
#include "lldpctl.h"
#include "../log.h"
#include "atom.h"
#include "helpers.h"
#include "fixedpoint.h"
#ifdef ENABLE_LLDPMED
static lldpctl_map_t port_med_location_map[] = {
{ LLDP_MED_LOCFORMAT_COORD, "Coordinates" },
{ LLDP_MED_LOCFORMAT_CIVIC, "Civic address" },
{ LLDP_MED_LOCFORMAT_ELIN, "ELIN" },
{ 0, NULL },
};
static lldpctl_map_t port_med_pow_devicetype_map[] = {
{ LLDP_MED_POW_TYPE_PSE, "PSE" },
{ LLDP_MED_POW_TYPE_PD, "PD" },
{ 0, NULL },
};
static lldpctl_map_t port_med_pow_source_map[] = {
{ LLDP_MED_POW_SOURCE_PRIMARY, "Primary Power Source" },
{ LLDP_MED_POW_SOURCE_BACKUP, "Backup Power Source / Power Conservation Mode" },
{ LLDP_MED_POW_SOURCE_PSE, "PSE" },
{ LLDP_MED_POW_SOURCE_LOCAL, "Local"},
{ LLDP_MED_POW_SOURCE_BOTH, "PSE + Local"},
{ 0, NULL },
};
static lldpctl_map_t port_med_pow_source_map2[] = {
{ 0, "unknown" },
{ LLDP_MED_POW_SOURCE_PRIMARY, "primary" },
{ LLDP_MED_POW_SOURCE_BACKUP, "backup" },
{ LLDP_MED_POW_SOURCE_PSE, "pse" },
{ LLDP_MED_POW_SOURCE_LOCAL, "local" },
{ LLDP_MED_POW_SOURCE_BOTH, "both" },
{ 0, NULL },
};
static struct atom_map port_med_geoid_map = {
.key = lldpctl_k_med_location_geoid,
.map = {
{ LLDP_MED_LOCATION_GEOID_WGS84, "WGS84" },
{ LLDP_MED_LOCATION_GEOID_NAD83, "NAD83" },
{ LLDP_MED_LOCATION_GEOID_NAD83_MLLW, "NAD83/MLLW" },
{ 0, NULL },
},
};
static struct atom_map civic_address_type_map = {
.key = lldpctl_k_med_civicaddress_type,
.map = {
{ 0, "Language" },
{ 1, "Country subdivision" },
{ 2, "County" },
{ 3, "City" },
{ 4, "City division" },
{ 5, "Block" },
{ 6, "Street" },
{ 16, "Direction" },
{ 17, "Trailing street suffix" },
{ 18, "Street suffix" },
{ 19, "Number" },
{ 20, "Number suffix" },
{ 21, "Landmark" },
{ 22, "Additional" },
{ 23, "Name" },
{ 24, "ZIP" },
{ 25, "Building" },
{ 26, "Unit" },
{ 27, "Floor" },
{ 28, "Room" },
{ 29, "Place type" },
{ 128, "Script" },
{ 0, NULL },
},
};
static struct atom_map port_med_policy_map = {
.key = lldpctl_k_med_policy_type,
.map = {
{ LLDP_MED_APPTYPE_VOICE , "Voice"},
{ LLDP_MED_APPTYPE_VOICESIGNAL, "Voice Signaling"},
{ LLDP_MED_APPTYPE_GUESTVOICE, "Guest Voice"},
{ LLDP_MED_APPTYPE_GUESTVOICESIGNAL, "Guest Voice Signaling"},
{ LLDP_MED_APPTYPE_SOFTPHONEVOICE, "Softphone Voice"},
{ LLDP_MED_APPTYPE_VIDEOCONFERENCE, "Video Conferencing"},
{ LLDP_MED_APPTYPE_VIDEOSTREAM, "Streaming Video"},
{ LLDP_MED_APPTYPE_VIDEOSIGNAL, "Video Signaling"},
{ 0, NULL },
}
};
static struct atom_map port_med_policy_prio_map = {
.key = lldpctl_k_med_policy_priority,
.map = {
{ 1, "Background" },
{ 0, "Best effort" },
{ 2, "Excellent effort" },
{ 3, "Critical applications" },
{ 4, "Video" },
{ 5, "Voice" },
{ 6, "Internetwork control" },
{ 7, "Network control" },
{ 0, NULL },
},
};
static struct atom_map port_med_pow_priority_map = {
.key = lldpctl_k_med_power_priority,
.map = {
{ 0, "unknown" },
{ LLDP_MED_POW_PRIO_CRITICAL, "critical" },
{ LLDP_MED_POW_PRIO_HIGH, "high" },
{ LLDP_MED_POW_PRIO_LOW, "low" },
{ 0, NULL },
},
};
ATOM_MAP_REGISTER(port_med_geoid_map, 7);
ATOM_MAP_REGISTER(civic_address_type_map, 8);
ATOM_MAP_REGISTER(port_med_policy_map, 9);
ATOM_MAP_REGISTER(port_med_policy_prio_map, 10);
ATOM_MAP_REGISTER(port_med_pow_priority_map, 11);
static lldpctl_atom_iter_t*
_lldpctl_atom_iter_med_policies_list(lldpctl_atom_t *atom)
{
int i;
struct _lldpctl_atom_any_list_t *vlist =
(struct _lldpctl_atom_any_list_t *)atom;
for (i = 0; i < LLDP_MED_APPTYPE_LAST; i++)
vlist->parent->port->p_med_policy[i].index = i;
return (lldpctl_atom_iter_t*)&vlist->parent->port->p_med_policy[0];
}
static lldpctl_atom_iter_t*
_lldpctl_atom_next_med_policies_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
{
struct lldpd_med_policy *policy = (struct lldpd_med_policy *)iter;
if (policy->index == LLDP_MED_APPTYPE_LAST - 1) return NULL;
return (lldpctl_atom_iter_t*)(++policy);
}
static lldpctl_atom_t*
_lldpctl_atom_value_med_policies_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
{
struct _lldpctl_atom_any_list_t *vlist =
(struct _lldpctl_atom_any_list_t *)atom;
struct lldpd_med_policy *policy = (struct lldpd_med_policy *)iter;
return _lldpctl_new_atom(atom->conn, atom_med_policy, vlist->parent, policy);
}
static int
_lldpctl_atom_new_med_policy(lldpctl_atom_t *atom, va_list ap)
{
struct _lldpctl_atom_med_policy_t *policy =
(struct _lldpctl_atom_med_policy_t *)atom;
policy->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
policy->policy = va_arg(ap, struct lldpd_med_policy *);
lldpctl_atom_inc_ref((lldpctl_atom_t *)policy->parent);
return 1;
}
static void
_lldpctl_atom_free_med_policy(lldpctl_atom_t *atom)
{
struct _lldpctl_atom_med_policy_t *policy =
(struct _lldpctl_atom_med_policy_t *)atom;
lldpctl_atom_dec_ref((lldpctl_atom_t *)policy->parent);
}
static long int
_lldpctl_atom_get_int_med_policy(lldpctl_atom_t *atom, lldpctl_key_t key)
{
struct _lldpctl_atom_med_policy_t *m =
(struct _lldpctl_atom_med_policy_t *)atom;
/* Local and remote port */
switch (key) {
case lldpctl_k_med_policy_type:
return m->policy->type;
case lldpctl_k_med_policy_unknown:
return m->policy->unknown;
case lldpctl_k_med_policy_tagged:
return m->policy->tagged;
case lldpctl_k_med_policy_vid:
return m->policy->vid;
case lldpctl_k_med_policy_dscp:
return m->policy->dscp;
case lldpctl_k_med_policy_priority:
return m->policy->priority;
default:
return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
}
}
static lldpctl_atom_t*
_lldpctl_atom_set_int_med_policy(lldpctl_atom_t *atom, lldpctl_key_t key,
long int value)
{
struct _lldpctl_atom_med_policy_t *m =
(struct _lldpctl_atom_med_policy_t *)atom;
/* Only local port can be modified */
if (!m->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
switch (key) {
case lldpctl_k_med_policy_type:
/* We let set any policy type, including one whose are not
* compatible with the index. If a policy type is set, the index
* will be ignored. If a policy type is 0, the index will be
* used to know which policy to "erase". */
if (value < 0 || value > LLDP_MED_APPTYPE_LAST) goto bad;
m->policy->type = value;
return atom;
case lldpctl_k_med_policy_unknown:
if (value != 0 && value != 1) goto bad;
m->policy->unknown = value;
return atom;
case lldpctl_k_med_policy_tagged:
if (value != 0 && value != 1) goto bad;
m->policy->tagged = value;
return atom;
case lldpctl_k_med_policy_vid:
if (value < 0 || value > 4094) goto bad;
m->policy->vid = value;
return atom;
case lldpctl_k_med_policy_dscp:
if (value < 0 || value > 63) goto bad;
m->policy->dscp = value;
return atom;
case lldpctl_k_med_policy_priority:
if (value < 0 || value > 7) goto bad;
m->policy->priority = value;
return atom;
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
return atom;
bad:
SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
return NULL;
}
static const char*
_lldpctl_atom_get_str_med_policy(lldpctl_atom_t *atom, lldpctl_key_t key)
{
struct _lldpctl_atom_med_policy_t *m =
(struct _lldpctl_atom_med_policy_t *)atom;
/* Local and remote port */
switch (key) {
case lldpctl_k_med_policy_type:
return map_lookup(port_med_policy_map.map, m->policy->type);
case lldpctl_k_med_policy_priority:
return map_lookup(port_med_policy_prio_map.map, m->policy->priority);
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
}
static lldpctl_atom_t*
_lldpctl_atom_set_str_med_policy(lldpctl_atom_t *atom, lldpctl_key_t key,
const char *value)
{
/* Local and remote port */
switch (key) {
case lldpctl_k_med_policy_type:
return _lldpctl_atom_set_int_med_policy(atom, key,
map_reverse_lookup(port_med_policy_map.map, value));
case lldpctl_k_med_policy_priority:
return _lldpctl_atom_set_int_med_policy(atom, key,
map_reverse_lookup(port_med_policy_prio_map.map, value));
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
}
static lldpctl_atom_iter_t*
_lldpctl_atom_iter_med_locations_list(lldpctl_atom_t *atom)
{
int i;
struct _lldpctl_atom_any_list_t *vlist =
(struct _lldpctl_atom_any_list_t *)atom;
for (i = 0; i < LLDP_MED_LOCFORMAT_LAST; i++)
vlist->parent->port->p_med_location[i].index = i;
return (lldpctl_atom_iter_t*)&vlist->parent->port->p_med_location[0];
}
static lldpctl_atom_iter_t*
_lldpctl_atom_next_med_locations_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
{
struct lldpd_med_loc *location = (struct lldpd_med_loc *)iter;
if (location->index == LLDP_MED_LOCFORMAT_LAST - 1) return NULL;
return (lldpctl_atom_iter_t*)(++location);
}
static lldpctl_atom_t*
_lldpctl_atom_value_med_locations_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
{
struct _lldpctl_atom_any_list_t *vlist =
(struct _lldpctl_atom_any_list_t *)atom;
struct lldpd_med_loc *location = (struct lldpd_med_loc *)iter;
return _lldpctl_new_atom(atom->conn, atom_med_location, vlist->parent, location);
}
static int
_lldpctl_atom_new_med_location(lldpctl_atom_t *atom, va_list ap)
{
struct _lldpctl_atom_med_location_t *location =
(struct _lldpctl_atom_med_location_t *)atom;
location->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
location->location = va_arg(ap, struct lldpd_med_loc *);
lldpctl_atom_inc_ref((lldpctl_atom_t *)location->parent);
return 1;
}
static void
_lldpctl_atom_free_med_location(lldpctl_atom_t *atom)
{
struct _lldpctl_atom_med_location_t *location =
(struct _lldpctl_atom_med_location_t *)atom;
lldpctl_atom_dec_ref((lldpctl_atom_t *)location->parent);
}
static long int
_lldpctl_atom_get_int_med_location(lldpctl_atom_t *atom, lldpctl_key_t key)
{
struct _lldpctl_atom_med_location_t *m =
(struct _lldpctl_atom_med_location_t *)atom;
/* Local and remote port */
switch (key) {
case lldpctl_k_med_location_format:
switch (m->location->format) {
case LLDP_MED_LOCFORMAT_COORD:
if (m->location->data_len != 16) break;
return LLDP_MED_LOCFORMAT_COORD;
case LLDP_MED_LOCFORMAT_CIVIC:
if ((m->location->data_len < 3) ||
(m->location->data_len - 1 <
m->location->data[0])) break;
return LLDP_MED_LOCFORMAT_CIVIC;
case LLDP_MED_LOCFORMAT_ELIN:
return LLDP_MED_LOCFORMAT_ELIN;
default:
return 0;
}
return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
case lldpctl_k_med_location_geoid:
if (m->location->format != LLDP_MED_LOCFORMAT_COORD)
return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return m->location->data[15];
case lldpctl_k_med_location_altitude_unit:
if (m->location->format != LLDP_MED_LOCFORMAT_COORD)
return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return (m->location->data[10] & 0xf0) >> 4;
default:
return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
}
}
static lldpctl_atom_t*
_lldpctl_atom_set_int_med_location(lldpctl_atom_t *atom, lldpctl_key_t key,
long int value)
{
struct _lldpctl_atom_med_location_t *mloc =
(struct _lldpctl_atom_med_location_t *)atom;
/* Only local port can be modified */
if (!mloc->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
switch (key) {
case lldpctl_k_med_location_format:
switch (value) {
case 0: /* Disabling */
case LLDP_MED_LOCFORMAT_COORD:
mloc->location->format = value;
free(mloc->location->data);
mloc->location->data = calloc(1, 16);
if (mloc->location->data == NULL) {
mloc->location->data_len = 0;
SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
return NULL;
}
mloc->location->data_len = 16;
return atom;
case LLDP_MED_LOCFORMAT_CIVIC:
mloc->location->format = value;
free(mloc->location->data);
mloc->location->data = calloc(1, 4);
if (mloc->location->data == NULL) {
mloc->location->data_len = 0;
SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
return NULL;
}
mloc->location->data_len = 4;
mloc->location->data[0] = 3;
mloc->location->data[1] = 2; /* Client */
mloc->location->data[2] = 'U';
mloc->location->data[3] = 'S';
return atom;
case LLDP_MED_LOCFORMAT_ELIN:
mloc->location->format = value;
free(mloc->location->data);
mloc->location->data = NULL;
mloc->location->data_len = 0;
return atom;
default: goto bad;
}
case lldpctl_k_med_location_geoid:
if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
switch (value) {
case 0:
case LLDP_MED_LOCATION_GEOID_WGS84:
case LLDP_MED_LOCATION_GEOID_NAD83:
case LLDP_MED_LOCATION_GEOID_NAD83_MLLW:
mloc->location->data[15] = value;
return atom;
default: goto bad;
}
case lldpctl_k_med_location_altitude_unit:
if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
switch (value) {
case 0:
case LLDP_MED_LOCATION_ALTITUDE_UNIT_METER:
case LLDP_MED_LOCATION_ALTITUDE_UNIT_FLOOR:
mloc->location->data[10] &= 0x0f;
mloc->location->data[10] |= value << 4;
return atom;
default: goto bad;
}
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
return atom;
bad:
SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
return NULL;
}
static const char*
read_fixed_precision(lldpctl_atom_t *atom,
char *buffer, unsigned shift,
unsigned intbits, unsigned fltbits, const char *suffix)
{
struct fp_number fp = fp_buftofp((unsigned char*)buffer, intbits, fltbits, shift);
char *result = fp_fptostr(fp, suffix);
if (result == NULL) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
return NULL;
}
size_t len = strlen(result) + 1;
char *stored = _lldpctl_alloc_in_atom(atom, len);
if (stored == NULL) {
free(result);
return NULL;
}
strlcpy(stored, result, len);
free(result);
return stored;
}
static const char*
_lldpctl_atom_get_str_med_location(lldpctl_atom_t *atom, lldpctl_key_t key)
{
struct _lldpctl_atom_med_location_t *m =
(struct _lldpctl_atom_med_location_t *)atom;
char *value;
/* Local and remote port */
switch (key) {
case lldpctl_k_med_location_format:
return map_lookup(port_med_location_map, m->location->format);
case lldpctl_k_med_location_geoid:
if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
return map_lookup(port_med_geoid_map.map,
m->location->data[15]);
case lldpctl_k_med_location_latitude:
if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
return read_fixed_precision(atom, m->location->data,
0, 9, 25, "NS");
case lldpctl_k_med_location_longitude:
if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
return read_fixed_precision(atom, m->location->data,
40, 9, 25, "EW");
case lldpctl_k_med_location_altitude:
if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
return read_fixed_precision(atom, m->location->data,
84, 22, 8, NULL);
case lldpctl_k_med_location_altitude_unit:
if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
switch (m->location->data[10] & 0xf0) {
case (LLDP_MED_LOCATION_ALTITUDE_UNIT_METER << 4):
return "m";
case (LLDP_MED_LOCATION_ALTITUDE_UNIT_FLOOR << 4):
return "floor";
}
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
case lldpctl_k_med_location_country:
if (m->location->format != LLDP_MED_LOCFORMAT_CIVIC) break;
value = _lldpctl_alloc_in_atom(atom, 3);
if (!value) return NULL;
memcpy(value, m->location->data + 2, 2);
return value;
case lldpctl_k_med_location_elin:
if (m->location->format != LLDP_MED_LOCFORMAT_ELIN) break;
value = _lldpctl_alloc_in_atom(atom, m->location->data_len + 1);
if (!value) return NULL;
memcpy(value, m->location->data, m->location->data_len);
return value;
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
static lldpctl_atom_t*
_lldpctl_atom_set_str_med_location(lldpctl_atom_t *atom, lldpctl_key_t key,
const char *value)
{
struct _lldpctl_atom_med_location_t *mloc =
(struct _lldpctl_atom_med_location_t *)atom;
struct fp_number fp;
char *end = NULL;
/* Only local port can be modified */
if (!mloc->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
switch (key) {
case lldpctl_k_med_location_latitude:
if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
if (value) fp = fp_strtofp(value, &end, 9, 25);
if (!end) goto bad;
if (end && *end != '\0') {
if (*(end+1) != '\0') goto bad;
if (*end == 'S') fp = fp_negate(fp);
else if (*end != 'N') goto bad;
}
fp_fptobuf(fp, (unsigned char*)mloc->location->data, 0);
return atom;
case lldpctl_k_med_location_longitude:
if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
if (value) fp = fp_strtofp(value, &end, 9, 25);
if (!end) goto bad;
if (end && *end != '\0') {
if (*(end+1) != '\0') goto bad;
if (*end == 'W') fp = fp_negate(fp);
else if (*end != 'E') goto bad;
}
fp_fptobuf(fp, (unsigned char*)mloc->location->data, 40);
return atom;
case lldpctl_k_med_location_altitude:
if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
if (value) fp = fp_strtofp(value, &end, 22, 8);
if (!end || *end != '\0') goto bad;
fp_fptobuf(fp, (unsigned char*)mloc->location->data, 84);
return atom;
case lldpctl_k_med_location_altitude_unit:
if (!value) goto bad;
if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
if (!strcmp(value, "m"))
return _lldpctl_atom_set_int_med_location(atom, key,
LLDP_MED_LOCATION_ALTITUDE_UNIT_METER);
if (!strcmp(value, "f") ||
(!strcmp(value, "floor")))
return _lldpctl_atom_set_int_med_location(atom, key,
LLDP_MED_LOCATION_ALTITUDE_UNIT_FLOOR);
goto bad;
break;
case lldpctl_k_med_location_geoid:
return _lldpctl_atom_set_int_med_location(atom, key,
map_reverse_lookup(port_med_geoid_map.map, value));
case lldpctl_k_med_location_country:
if (mloc->location->format != LLDP_MED_LOCFORMAT_CIVIC) goto bad;
if (mloc->location->data == NULL || mloc->location->data_len < 3) goto bad;
if (!value || strlen(value) != 2) goto bad;
memcpy(mloc->location->data + 2, value, 2);
return atom;
case lldpctl_k_med_location_elin:
if (!value) goto bad;
if (mloc->location->format != LLDP_MED_LOCFORMAT_ELIN) goto bad;
free(mloc->location->data);
mloc->location->data = calloc(1, strlen(value));
if (mloc->location->data == NULL) {
mloc->location->data_len = 0;
SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
return NULL;
}
mloc->location->data_len = strlen(value);
memcpy(mloc->location->data, value,
mloc->location->data_len);
return atom;
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
return atom;
bad:
SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
return NULL;
}
static lldpctl_atom_t*
_lldpctl_atom_get_atom_med_location(lldpctl_atom_t *atom, lldpctl_key_t key)
{
struct _lldpctl_atom_med_location_t *m =
(struct _lldpctl_atom_med_location_t *)atom;
/* Local and remote port */
switch (key) {
case lldpctl_k_med_location_ca_elements:
if (m->location->format != LLDP_MED_LOCFORMAT_CIVIC) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
return _lldpctl_new_atom(atom->conn, atom_med_caelements_list, m);
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
}
static lldpctl_atom_t*
_lldpctl_atom_set_atom_med_location(lldpctl_atom_t *atom, lldpctl_key_t key,
lldpctl_atom_t *value)
{
struct _lldpctl_atom_med_location_t *m =
(struct _lldpctl_atom_med_location_t *)atom;
struct _lldpctl_atom_med_caelement_t *el;
uint8_t *new;
/* Only local port can be modified */
if (!m->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
switch (key) {
case lldpctl_k_med_location_ca_elements:
if (value->type != atom_med_caelement) {
SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
return NULL;
}
if (m->location->format != LLDP_MED_LOCFORMAT_CIVIC) goto bad;
if (m->location->data == NULL || m->location->data_len < 3) goto bad;
/* We append this element. */
el = (struct _lldpctl_atom_med_caelement_t *)value;
new = malloc(m->location->data_len + 2 + el->len);
if (new == NULL) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
return NULL;
}
memcpy(new, m->location->data, m->location->data_len);
new[m->location->data_len] = el->type;
new[m->location->data_len + 1] = el->len;
memcpy(new + m->location->data_len + 2, el->value, el->len);
new[0] += 2 + el->len;
free(m->location->data);
m->location->data = (char*)new;
m->location->data_len += 2 + el->len;
return atom;
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
bad:
SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
return NULL;
}
struct ca_iter {
uint8_t *data;
size_t data_len;
};
static lldpctl_atom_iter_t*
_lldpctl_atom_iter_med_caelements_list(lldpctl_atom_t *atom)
{
struct _lldpctl_atom_med_caelements_list_t *plist =
(struct _lldpctl_atom_med_caelements_list_t *)atom;
struct ca_iter *iter = _lldpctl_alloc_in_atom(atom, sizeof(struct ca_iter));
if (!iter) return NULL;
iter->data = (uint8_t*)plist->parent->location->data + 4;
iter->data_len = *(uint8_t*)plist->parent->location->data - 3;
return (lldpctl_atom_iter_t*)iter;
}
static lldpctl_atom_iter_t*
_lldpctl_atom_next_med_caelements_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
{
struct ca_iter *cai = (struct ca_iter *)iter;
int len;
if (cai->data_len < 2) return NULL;
len = *((uint8_t *)cai->data + 1);
if (cai->data_len < 2 + len) return NULL;
cai->data += 2 + len;
cai->data_len -= 2 + len;
return (lldpctl_atom_iter_t*)cai;
}
static lldpctl_atom_t*
_lldpctl_atom_value_med_caelements_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
{
struct _lldpctl_atom_med_caelements_list_t *plist =
(struct _lldpctl_atom_med_caelements_list_t *)atom;
struct ca_iter *cai = (struct ca_iter *)iter;
size_t len;
if (cai->data_len < 2) return NULL;
len = *((uint8_t *)cai->data + 1);
if (cai->data_len < 2 + len) return NULL;
return _lldpctl_new_atom(atom->conn, atom_med_caelement, plist->parent,
(int)*cai->data, cai->data + 2, len);
}
static lldpctl_atom_t*
_lldpctl_atom_create_med_caelements_list(lldpctl_atom_t *atom)
{
struct _lldpctl_atom_med_caelements_list_t *plist =
(struct _lldpctl_atom_med_caelements_list_t *)atom;
return _lldpctl_new_atom(atom->conn, atom_med_caelement, plist->parent,
-1, NULL, 0);
}
static int
_lldpctl_atom_new_med_caelement(lldpctl_atom_t *atom, va_list ap)
{
struct _lldpctl_atom_med_caelement_t *el =
(struct _lldpctl_atom_med_caelement_t *)atom;
el->parent = va_arg(ap, struct _lldpctl_atom_med_location_t *);
el->type = va_arg(ap, int);
el->value = va_arg(ap, uint8_t*);
el->len = va_arg(ap, size_t);
lldpctl_atom_inc_ref((lldpctl_atom_t *)el->parent);
return 1;
}
static void
_lldpctl_atom_free_med_caelement(lldpctl_atom_t *atom)
{
struct _lldpctl_atom_med_caelement_t *el =
(struct _lldpctl_atom_med_caelement_t *)atom;
lldpctl_atom_dec_ref((lldpctl_atom_t *)el->parent);
}
static long int
_lldpctl_atom_get_int_med_caelement(lldpctl_atom_t *atom, lldpctl_key_t key)
{
struct _lldpctl_atom_med_caelement_t *m =
(struct _lldpctl_atom_med_caelement_t *)atom;
switch (key) {
case lldpctl_k_med_civicaddress_type:
return m->type;
default:
return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
}
}
static lldpctl_atom_t*
_lldpctl_atom_set_int_med_caelement(lldpctl_atom_t *atom, lldpctl_key_t key,
long int value)
{
struct _lldpctl_atom_med_caelement_t *el =
(struct _lldpctl_atom_med_caelement_t *)atom;
/* Only local port can be modified */
if (!el->parent->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
switch (key) {
case lldpctl_k_med_civicaddress_type:
if (value < 0 || value > 128) goto bad;
el->type = value;
return atom;
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
return atom;
bad:
SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
return NULL;
}
static const char*
_lldpctl_atom_get_str_med_caelement(lldpctl_atom_t *atom, lldpctl_key_t key)
{
char *value = NULL;
struct _lldpctl_atom_med_caelement_t *m =
(struct _lldpctl_atom_med_caelement_t *)atom;
/* Local and remote port */
switch (key) {
case lldpctl_k_med_civicaddress_type:
return map_lookup(civic_address_type_map.map, m->type);
case lldpctl_k_med_civicaddress_value:
value = _lldpctl_alloc_in_atom(atom, m->len + 1);
if (!value) return NULL;
memcpy(value, m->value, m->len);
return value;
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
}
static lldpctl_atom_t*
_lldpctl_atom_set_str_med_caelement(lldpctl_atom_t *atom, lldpctl_key_t key,
const char *value)
{
struct _lldpctl_atom_med_caelement_t *el =
(struct _lldpctl_atom_med_caelement_t *)atom;
size_t len;
/* Only local port can be modified */
if (!el->parent->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
switch (key) {
case lldpctl_k_med_civicaddress_value:
if (!value) goto bad;
len = strlen(value) + 1;
if (len > 251) goto bad;
el->value = _lldpctl_alloc_in_atom(atom, len);
if (el->value == NULL) return NULL;
strlcpy((char*)el->value, value, len);
el->len = strlen(value);
return atom;
case lldpctl_k_med_civicaddress_type:
return _lldpctl_atom_set_int_med_caelement(atom, key,
map_reverse_lookup(civic_address_type_map.map, value));
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
return atom;
bad:
SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
return NULL;
}
static int
_lldpctl_atom_new_med_power(lldpctl_atom_t *atom, va_list ap)
{
struct _lldpctl_atom_med_power_t *mpow =
(struct _lldpctl_atom_med_power_t *)atom;
mpow->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
lldpctl_atom_inc_ref((lldpctl_atom_t *)mpow->parent);
return 1;
}
static void
_lldpctl_atom_free_med_power(lldpctl_atom_t *atom)
{
struct _lldpctl_atom_med_power_t *mpow =
(struct _lldpctl_atom_med_power_t *)atom;
lldpctl_atom_dec_ref((lldpctl_atom_t *)mpow->parent);
}
static const char*
_lldpctl_atom_get_str_med_power(lldpctl_atom_t *atom, lldpctl_key_t key)
{
struct _lldpctl_atom_med_power_t *mpow =
(struct _lldpctl_atom_med_power_t *)atom;
struct lldpd_port *port = mpow->parent->port;
/* Local and remote port */
switch (key) {
case lldpctl_k_med_power_type:
return map_lookup(port_med_pow_devicetype_map,
port->p_med_power.devicetype);
case lldpctl_k_med_power_source:
return map_lookup(port_med_pow_source_map,
port->p_med_power.source);
case lldpctl_k_med_power_priority:
return map_lookup(port_med_pow_priority_map.map,
port->p_med_power.priority);
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
}
static long int
_lldpctl_atom_get_int_med_power(lldpctl_atom_t *atom, lldpctl_key_t key)
{
struct _lldpctl_atom_med_power_t *dpow =
(struct _lldpctl_atom_med_power_t *)atom;
struct lldpd_port *port = dpow->parent->port;
/* Local and remote port */
switch (key) {
case lldpctl_k_med_power_type:
return port->p_med_power.devicetype;
case lldpctl_k_med_power_source:
return port->p_med_power.source;
case lldpctl_k_med_power_priority:
return port->p_med_power.priority;
case lldpctl_k_med_power_val:
return port->p_med_power.val * 100;
default:
return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
}
}
static lldpctl_atom_t*
_lldpctl_atom_set_int_med_power(lldpctl_atom_t *atom, lldpctl_key_t key,
long int value)
{
struct _lldpctl_atom_med_power_t *dpow =
(struct _lldpctl_atom_med_power_t *)atom;
struct lldpd_port *port = dpow->parent->port;
/* Only local port can be modified */
if (!dpow->parent->local) {
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
switch (key) {
case lldpctl_k_med_power_type:
switch (value) {
case 0:
case LLDP_MED_POW_TYPE_PSE:
case LLDP_MED_POW_TYPE_PD:
port->p_med_power.devicetype = value;
return atom;
default: goto bad;
}
case lldpctl_k_med_power_source:
switch (value) {
case LLDP_MED_POW_SOURCE_PRIMARY:
case LLDP_MED_POW_SOURCE_BACKUP:
if (port->p_med_power.devicetype != LLDP_MED_POW_TYPE_PSE)
goto bad;
port->p_med_power.source = value;
return atom;
case LLDP_MED_POW_SOURCE_PSE:
case LLDP_MED_POW_SOURCE_LOCAL:
case LLDP_MED_POW_SOURCE_BOTH:
if (port->p_med_power.devicetype != LLDP_MED_POW_TYPE_PD)
goto bad;
port->p_med_power.source = value;
return atom;
case LLDP_MED_POW_SOURCE_UNKNOWN:
port->p_med_power.source = value;
return atom;
default: goto bad;
}
case lldpctl_k_med_power_priority:
if (value < 0 || value > 3) goto bad;
port->p_med_power.priority = value;
return atom;
case lldpctl_k_med_power_val:
if (value < 0) goto bad;
port->p_med_power.val = value / 100;
return atom;
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
return atom;
bad:
SET_ERROR(atom->conn, LLDPCTL_ERR_BAD_VALUE);
return NULL;
}
static lldpctl_atom_t*
_lldpctl_atom_set_str_med_power(lldpctl_atom_t *atom, lldpctl_key_t key,
const char *value)
{
switch (key) {
case lldpctl_k_med_power_type:
return _lldpctl_atom_set_int_med_power(atom, key,
map_reverse_lookup(port_med_pow_devicetype_map, value));
case lldpctl_k_med_power_source:
return _lldpctl_atom_set_int_med_power(atom, key,
map_reverse_lookup(port_med_pow_source_map2, value));
case lldpctl_k_med_power_priority:
return _lldpctl_atom_set_int_med_power(atom, key,
map_reverse_lookup(port_med_pow_priority_map.map, value));
default:
SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
return NULL;
}
}
static struct atom_builder med_policies_list =
{ atom_med_policies_list, sizeof(struct _lldpctl_atom_any_list_t),
.init = _lldpctl_atom_new_any_list,
.free = _lldpctl_atom_free_any_list,
.iter = _lldpctl_atom_iter_med_policies_list,
.next = _lldpctl_atom_next_med_policies_list,
.value = _lldpctl_atom_value_med_policies_list };
static struct atom_builder med_policy =
{ atom_med_policy, sizeof(struct _lldpctl_atom_med_policy_t),
.init = _lldpctl_atom_new_med_policy,
.free = _lldpctl_atom_free_med_policy,
.get_int = _lldpctl_atom_get_int_med_policy,
.set_int = _lldpctl_atom_set_int_med_policy,
.get_str = _lldpctl_atom_get_str_med_policy,
.set_str = _lldpctl_atom_set_str_med_policy };
static struct atom_builder med_locations_list =
{ atom_med_locations_list, sizeof(struct _lldpctl_atom_any_list_t),
.init = _lldpctl_atom_new_any_list,
.free = _lldpctl_atom_free_any_list,
.iter = _lldpctl_atom_iter_med_locations_list,
.next = _lldpctl_atom_next_med_locations_list,
.value = _lldpctl_atom_value_med_locations_list };
static struct atom_builder med_location =
{ atom_med_location, sizeof(struct _lldpctl_atom_med_location_t),
.init = _lldpctl_atom_new_med_location,
.free = _lldpctl_atom_free_med_location,
.get = _lldpctl_atom_get_atom_med_location,
.set = _lldpctl_atom_set_atom_med_location,
.get_int = _lldpctl_atom_get_int_med_location,
.set_int = _lldpctl_atom_set_int_med_location,
.get_str = _lldpctl_atom_get_str_med_location,
.set_str = _lldpctl_atom_set_str_med_location };
static struct atom_builder med_caelements_list =
{ atom_med_caelements_list, sizeof(struct _lldpctl_atom_med_caelements_list_t),
.init = _lldpctl_atom_new_any_list,
.free = _lldpctl_atom_free_any_list,
.iter = _lldpctl_atom_iter_med_caelements_list,
.next = _lldpctl_atom_next_med_caelements_list,
.value = _lldpctl_atom_value_med_caelements_list,
.create = _lldpctl_atom_create_med_caelements_list };
static struct atom_builder med_caelement =
{ atom_med_caelement, sizeof(struct _lldpctl_atom_med_caelement_t),
.init = _lldpctl_atom_new_med_caelement,
.free = _lldpctl_atom_free_med_caelement,
.get_int = _lldpctl_atom_get_int_med_caelement,
.set_int = _lldpctl_atom_set_int_med_caelement,
.get_str = _lldpctl_atom_get_str_med_caelement,
.set_str = _lldpctl_atom_set_str_med_caelement };
static struct atom_builder med_power =
{ atom_med_power, sizeof(struct _lldpctl_atom_med_power_t),
.init = _lldpctl_atom_new_med_power,
.free = _lldpctl_atom_free_med_power,
.get_int = _lldpctl_atom_get_int_med_power,
.set_int = _lldpctl_atom_set_int_med_power,
.get_str = _lldpctl_atom_get_str_med_power,
.set_str = _lldpctl_atom_set_str_med_power };
ATOM_BUILDER_REGISTER(med_policies_list, 15);
ATOM_BUILDER_REGISTER(med_policy, 16);
ATOM_BUILDER_REGISTER(med_locations_list, 17);
ATOM_BUILDER_REGISTER(med_location, 18);
ATOM_BUILDER_REGISTER(med_caelements_list, 19);
ATOM_BUILDER_REGISTER(med_caelement, 20);
ATOM_BUILDER_REGISTER(med_power, 21);
#endif