]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
lib/atom-private.c: break it into smaller modules
authorAlexandru Ardelean <ardeleanalex@gmail.com>
Tue, 31 Mar 2015 10:40:52 +0000 (13:40 +0300)
committerAlexandru Ardelean <ardeleanalex@gmail.com>
Thu, 2 Apr 2015 07:14:13 +0000 (10:14 +0300)
This is a huge commit, but 90%+ of it is just splitting
'lib/atom-private.c' into smaller atoms and putting them
into the 'lib/atoms' folder.

Some glue code has been added, for constructing maps and builders.

For the 'struct atom_builder' type a 'nextb' field has been
appended at the end, to help make it a single-linked list of
'struct atom_builders's.
The 'next' field in 'struct atom_builder' is taken (hence 'nextb').

For the maps, a wrapper struct (called 'struct atom_map')
has been added on top of 'lldpctl_map_t' in order to create
single-linked list construct of maps.
The struct looks like:
    struct atom_map {
        int key;             /* lldpctl_key_t equivalent */
        struct atom_map *next; /* next 'struct atom_map' */
        lldpctl_map_t map[]; /* has to be at the end to
                                to be statically init-ed */
    }
The point of this is to register them at load-time so
that 'lldpctl_key_get_map()' can return them.

Each file in 'lib/atoms' registers it's own 'struct atom_builder's
and 'struct atom_map's.

The whole point of this is to be able to add a 'custom' and/or
'custom_list' atom(s) to be able to configure/broadcast other TLVs
in the future.
This mechanism is extensible to add other TLVs defined by
future standards.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
12 files changed:
src/lib/Makefile.am
src/lib/atom-private.c
src/lib/atoms/config.c [new file with mode: 0644]
src/lib/atoms/dot1.c [new file with mode: 0644]
src/lib/atoms/dot3.c [new file with mode: 0644]
src/lib/atoms/interface.c [new file with mode: 0644]
src/lib/atoms/med.c [new file with mode: 0644]
src/lib/atoms/mgmt.c [new file with mode: 0644]
src/lib/atoms/port.c [new file with mode: 0644]
src/lib/helpers.c [new file with mode: 0644]
src/lib/helpers.h [new file with mode: 0644]
src/lib/private.h

index 9ba931bf29a8a7628e2271378b81f4d51ea0236e..ac9160582d83dd36cedebbe1c5f38327f576782a 100644 (file)
@@ -8,7 +8,11 @@ include_HEADERS = lldpctl.h
 noinst_LTLIBRARIES = libfixedpoint.la
 libfixedpoint_la_SOURCES = fixedpoint.h fixedpoint.c
 
-liblldpctl_la_SOURCES = lldpctl.h private.h errors.c connection.c atom.c atom-private.c
+liblldpctl_la_SOURCES = \
+       lldpctl.h private.h errors.c connection.c atom.c helpers.c helpers.h \
+       atom-private.c \
+       atoms/config.c atoms/dot1.c atoms/dot3.c \
+       atoms/interface.c atoms/med.c atoms/mgmt.c atoms/port.c
 liblldpctl_la_LIBADD  = $(top_builddir)/src/libcommon-daemon-lib.la libfixedpoint.la
 liblldpctl_la_LDFLAGS = $(AM_LDFLAGS) -export-symbols-regex '^lldpctl_' -version-info 9:0:5
 
index 976d262bd94ae4ba149a5885dc6b7ade1edd0c27..13bfcf778fcceace26c13486e80536952afd2771 100644 (file)
 #include "private.h"
 #include "fixedpoint.h"
 
-/* Translation from constants to string */
-static lldpctl_map_t lldpd_protocol_map[] = {
-       { LLDPD_MODE_LLDP,      "LLDP" },
-       { LLDPD_MODE_CDPV1,     "CDPv1"},
-       { LLDPD_MODE_CDPV2,     "CDPv2"},
-       { LLDPD_MODE_EDP,       "EDP" },
-       { LLDPD_MODE_FDP,       "FDP"},
-       { LLDPD_MODE_SONMP,     "SONMP"},
-        { 0, NULL }
-};
-
-static lldpctl_map_t chassis_id_subtype_map[] = {
-       { LLDP_CHASSISID_SUBTYPE_IFNAME,  "ifname"},
-       { LLDP_CHASSISID_SUBTYPE_IFALIAS, "ifalias" },
-       { LLDP_CHASSISID_SUBTYPE_LOCAL,   "local" },
-       { LLDP_CHASSISID_SUBTYPE_LLADDR,  "mac" },
-       { LLDP_CHASSISID_SUBTYPE_ADDR,    "ip" },
-       { LLDP_CHASSISID_SUBTYPE_PORT,    "unhandled" },
-       { LLDP_CHASSISID_SUBTYPE_CHASSIS, "unhandled" },
-       { 0, NULL},
-};
-
-static lldpctl_map_t port_id_subtype_map[] = {
-       { LLDP_PORTID_SUBTYPE_IFNAME,   "ifname"},
-       { LLDP_PORTID_SUBTYPE_IFALIAS,  "ifalias" },
-       { LLDP_PORTID_SUBTYPE_LOCAL,    "local" },
-       { LLDP_PORTID_SUBTYPE_LLADDR,   "mac" },
-       { LLDP_PORTID_SUBTYPE_ADDR,     "ip" },
-       { LLDP_PORTID_SUBTYPE_PORT,     "unhandled" },
-       { LLDP_PORTID_SUBTYPE_AGENTCID, "unhandled" },
-       { 0, NULL},
-};
-
-#ifdef ENABLE_DOT3
-static lldpctl_map_t operational_mau_type_values[] = {
-       { 1,    "AUI - no internal MAU, view from AUI" },
-       { 2,    "10Base5 - thick coax MAU" },
-       { 3,    "Foirl - FOIRL MAU" },
-       { 4,    "10Base2 - thin coax MAU" },
-       { 5,    "10BaseT - UTP MAU" },
-       { 6,    "10BaseFP - passive fiber MAU" },
-       { 7,    "10BaseFB - sync fiber MAU" },
-       { 8,    "10BaseFL - async fiber MAU" },
-       { 9,    "10Broad36 - broadband DTE MAU" },
-       { 10,   "10BaseTHD - UTP MAU, half duplex mode" },
-       { 11,   "10BaseTFD - UTP MAU, full duplex mode" },
-       { 12,   "10BaseFLHD - async fiber MAU, half duplex mode" },
-       { 13,   "10BaseFLDF - async fiber MAU, full duplex mode" },
-       { 14,   "10BaseT4 - 4 pair category 3 UTP" },
-       { 15,   "100BaseTXHD - 2 pair category 5 UTP, half duplex mode" },
-       { 16,   "100BaseTXFD - 2 pair category 5 UTP, full duplex mode" },
-       { 17,   "100BaseFXHD - X fiber over PMT, half duplex mode" },
-       { 18,   "100BaseFXFD - X fiber over PMT, full duplex mode" },
-       { 19,   "100BaseT2HD - 2 pair category 3 UTP, half duplex mode" },
-       { 20,   "100BaseT2FD - 2 pair category 3 UTP, full duplex mode" },
-       { 21,   "1000BaseXHD - PCS/PMA, unknown PMD, half duplex mode" },
-       { 22,   "1000BaseXFD - PCS/PMA, unknown PMD, full duplex mode" },
-       { 23,   "1000BaseLXHD - Fiber over long-wavelength laser, half duplex mode" },
-       { 24,   "1000BaseLXFD - Fiber over long-wavelength laser, full duplex mode" },
-       { 25,   "1000BaseSXHD - Fiber over short-wavelength laser, half duplex mode" },
-       { 26,   "1000BaseSXFD - Fiber over short-wavelength laser, full duplex mode" },
-       { 27,   "1000BaseCXHD - Copper over 150-Ohm balanced cable, half duplex mode" },
-       { 28,   "1000BaseCXFD - Copper over 150-Ohm balanced cable, full duplex mode" },
-       { 29,   "1000BaseTHD - Four-pair Category 5 UTP, half duplex mode" },
-       { 30,   "1000BaseTFD - Four-pair Category 5 UTP, full duplex mode" },
-       { 31,   "10GigBaseX - X PCS/PMA, unknown PMD." },
-       { 32,   "10GigBaseLX4 - X fiber over WWDM optics" },
-       { 33,   "10GigBaseR - R PCS/PMA, unknown PMD." },
-       { 34,   "10GigBaseER - R fiber over 1550 nm optics" },
-       { 35,   "10GigBaseLR - R fiber over 1310 nm optics" },
-       { 36,   "10GigBaseSR - R fiber over 850 nm optics" },
-       { 37,   "10GigBaseW - W PCS/PMA, unknown PMD." },
-       { 38,   "10GigBaseEW - W fiber over 1550 nm optics" },
-       { 39,   "10GigBaseLW - W fiber over 1310 nm optics" },
-       { 40,   "10GigBaseSW - W fiber over 850 nm optics" },
-       { 41,   "10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable" },
-       { 42,   "2BaseTL - Voice grade UTP copper, up to 2700m, optional PAF" },
-       { 43,   "10PassTS - Voice grade UTP copper, up to 750m, optional PAF" },
-       { 44,   "100BaseBX10D - One single-mode fiber OLT, long wavelength, 10km" },
-       { 45,   "100BaseBX10U - One single-mode fiber ONU, long wavelength, 10km" },
-       { 46,   "100BaseLX10 - Two single-mode fibers, long wavelength, 10km" },
-       { 47,   "1000BaseBX10D - One single-mode fiber OLT, long wavelength, 10km" },
-       { 48,   "1000BaseBX10U - One single-mode fiber ONU, long wavelength, 10km" },
-       { 49,   "1000BaseLX10 - Two sigle-mode fiber, long wavelength, 10km" },
-       { 50,   "1000BasePX10D - One single-mode fiber EPON OLT, 10km" },
-       { 51,   "1000BasePX10U - One single-mode fiber EPON ONU, 10km" },
-       { 52,   "1000BasePX20D - One single-mode fiber EPON OLT, 20km" },
-       { 53,   "1000BasePX20U - One single-mode fiber EPON ONU, 20km" },
-       { 0, NULL }
-};
-
-static lldpctl_map_t port_dot3_power_devicetype_map[] = {
-       { LLDP_DOT3_POWER_PSE, "PSE" },
-       { LLDP_DOT3_POWER_PD,  "PD" },
-       { 0, NULL }
-};
-
-static lldpctl_map_t port_dot3_power_pairs_map[] = {
-       { LLDP_DOT3_POWERPAIRS_SIGNAL, "signal" },
-       { LLDP_DOT3_POWERPAIRS_SPARE,  "spare" },
-       { 0, NULL }
-};
-
-static lldpctl_map_t port_dot3_power_class_map[] = {
-       { 1, "class 0" },
-       { 2, "class 1" },
-       { 3, "class 2" },
-       { 4, "class 3" },
-       { 5, "class 4" },
-       { 0, NULL }
-};
-
-static lldpctl_map_t port_dot3_power_pse_source_map[] = {
-       { LLDP_DOT3_POWER_SOURCE_BOTH, "PSE + Local" },
-       { LLDP_DOT3_POWER_SOURCE_PSE, "PSE" },
-       { 0, NULL }
-};
-
-static lldpctl_map_t port_dot3_power_pd_source_map[] = {
-       { LLDP_DOT3_POWER_SOURCE_BACKUP, "Backup source" },
-       { LLDP_DOT3_POWER_SOURCE_PRIMARY, "Primary power source" },
-       { 0, NULL }
-};
-#endif
-
-#if defined ENABLE_DOT3 || defined ENABLE_LLDPMED
-static lldpctl_map_t port_dot3_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 },
-};
-#endif
-
-#ifdef ENABLE_LLDPMED
-static lldpctl_map_t chassis_med_type_map[] = {
-       { LLDP_MED_CLASS_I,        "Generic Endpoint (Class I)" },
-       { LLDP_MED_CLASS_II,       "Media Endpoint (Class II)" },
-       { LLDP_MED_CLASS_III,      "Communication Device Endpoint (Class III)" },
-       { LLDP_MED_NETWORK_DEVICE, "Network Connectivity Device" },
-       { 0, NULL },
-};
-
-static lldpctl_map_t port_med_policy_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 lldpctl_map_t port_med_policy_prio_map[] = {
-       { 0, "Background" },
-       { 1, "Spare" },
-       { 2, "Best effort" },
-       { 3, "Excellent effort" },
-       { 4, "Controlled load" },
-       { 5, "Video" },
-       { 6, "Voice" },
-       { 7, "Network control" },
-       { 0, NULL },
-};
-
-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 civic_address_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 lldpctl_map_t port_med_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 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 lldpctl_map_t *port_med_pow_priority_map = port_dot3_power_priority_map;
-#endif
-
-static lldpctl_map_t bond_slave_src_mac_map[] = {
-       { LLDP_BOND_SLAVE_SRC_MAC_TYPE_REAL,   "real"},
-       { LLDP_BOND_SLAVE_SRC_MAC_TYPE_ZERO,   "zero"},
-       { LLDP_BOND_SLAVE_SRC_MAC_TYPE_FIXED,  "fixed"},
-       { LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED, "local" },
-       { LLDP_BOND_SLAVE_SRC_MAC_TYPE_UNKNOWN, NULL},
-};
+static lldpctl_map_t empty_map[] = {{ 0, NULL }};
 
-static lldpctl_map_t lldp_portid_map[] = {
-       { LLDP_PORTID_SUBTYPE_IFNAME,   "ifname"},
-       { LLDP_PORTID_SUBTYPE_LLADDR,   "macaddress"},
-       { LLDP_PORTID_SUBTYPE_LOCAL,    "local"},
-       { LLDP_PORTID_SUBTYPE_UNKNOWN,  NULL},
+static struct atom_map atom_map_list = {
+       .next = NULL
 };
 
-static const char*
-map_lookup(lldpctl_map_t *list, int n)
-{
-
-       unsigned int i;
-
-       for (i = 0; list[i].string != NULL; i++) {
-               if (list[i].value == n) {
-                       return list[i].string;
-               }
-       }
-
-       return "unknown";
-}
-
-#if defined ENABLE_LLDPMED || defined ENABLE_DOT3
-static int
-map_reverse_lookup(lldpctl_map_t *list, const char *string)
-{
-       if (!string) return -1;
-
-       for (unsigned int i = 0; list[i].string != NULL; i++) {
-               if (!strcasecmp(list[i].string, string))
-                       return list[i].value;
-       }
-
-       return -1;
-}
-#endif
-
-static lldpctl_map_t empty_map[] = {{ 0, NULL }};
-
 lldpctl_map_t*
 lldpctl_key_get_map(lldpctl_key_t key)
 {
-       switch (key) {
-#ifdef ENABLE_LLDPMED
-       case lldpctl_k_med_location_geoid:
-               return port_med_geoid_map;
-       case lldpctl_k_med_civicaddress_type:
-               return civic_address_type_map;
-       case lldpctl_k_med_policy_type:
-               return port_med_policy_map;
-       case lldpctl_k_med_policy_priority:
-               return port_med_policy_prio_map;
-       case lldpctl_k_med_power_priority:
-               return port_med_pow_priority_map;
-#endif
-#ifdef ENABLE_DOT3
-       case lldpctl_k_dot3_power_pairs:
-               return port_dot3_power_pairs_map;
-       case lldpctl_k_dot3_power_class:
-               return port_dot3_power_class_map;
-       case lldpctl_k_dot3_power_priority:
-               return port_dot3_power_priority_map;
-#endif
-       case lldpctl_k_config_bond_slave_src_mac_type:
-               return bond_slave_src_mac_map;
-       case lldpctl_k_config_lldp_portid_type:
-               return lldp_portid_map;
-       case lldpctl_k_port_protocol:
-               return lldpd_protocol_map;
-       default: return empty_map;
-       }
-}
-
-/* Atom methods */
-
-static int
-_lldpctl_atom_new_config(lldpctl_atom_t *atom, va_list ap)
-{
-       struct _lldpctl_atom_config_t *c =
-           (struct _lldpctl_atom_config_t *)atom;
-       c->config = va_arg(ap, struct lldpd_config *);
-       return 1;
-}
-
-static void
-_lldpctl_atom_free_config(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_config_t *c =
-           (struct _lldpctl_atom_config_t *)atom;
-       lldpd_config_cleanup(c->config);
-       free(c->config);
-}
-
-static const char*
-_lldpctl_atom_get_str_config(lldpctl_atom_t *atom, lldpctl_key_t key)
-{
-       char *res = NULL;
-       struct _lldpctl_atom_config_t *c =
-           (struct _lldpctl_atom_config_t *)atom;
-       switch (key) {
-       case lldpctl_k_config_mgmt_pattern:
-               res = c->config->c_mgmt_pattern; break;
-       case lldpctl_k_config_iface_pattern:
-               res = c->config->c_iface_pattern; break;
-       case lldpctl_k_config_cid_pattern:
-               res = c->config->c_cid_pattern; break;
-       case lldpctl_k_config_description:
-               res = c->config->c_description; break;
-       case lldpctl_k_config_platform:
-               res = c->config->c_platform; break;
-       case lldpctl_k_config_hostname:
-               res = c->config->c_hostname; break;
-       case lldpctl_k_config_bond_slave_src_mac_type:
-               return map_lookup(bond_slave_src_mac_map,
-                               c->config->c_bond_slave_src_mac_type);
-       case lldpctl_k_config_lldp_portid_type:
-               return map_lookup(lldp_portid_map,
-                   c->config->c_lldp_portid_type);
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-       return res?res:"";
-}
-
-static struct _lldpctl_atom_config_t*
-__lldpctl_atom_set_str_config(struct _lldpctl_atom_config_t *c,
-    char **local, char **global,
-    const char *value) {
-       if (value) {
-               char *aval = NULL;
-               size_t len = strlen(value) + 1;
-               aval = _lldpctl_alloc_in_atom((lldpctl_atom_t *)c, len);
-               if (!aval) return NULL;
-               memcpy(aval, value, len);
-               *local = aval;
-               free(*global); *global = strdup(aval);
-       } else {
-               free(*global);
-               *local = *global = NULL;
-       }
-       return c;
-}
-
-static lldpctl_atom_t*
-_lldpctl_atom_set_str_config(lldpctl_atom_t *atom, lldpctl_key_t key,
-    const char *value)
-{
-       struct _lldpctl_atom_config_t *c =
-           (struct _lldpctl_atom_config_t *)atom;
-       struct lldpd_config config;
-       memcpy(&config, c->config, sizeof(struct lldpd_config));
-       char *canary = NULL;
-       int rc;
-
-       switch (key) {
-       case lldpctl_k_config_iface_pattern:
-               if (!__lldpctl_atom_set_str_config(c,
-                       &config.c_iface_pattern, &c->config->c_iface_pattern,
-                       value))
-                       return NULL;
-               break;
-       case lldpctl_k_config_mgmt_pattern:
-               if (!__lldpctl_atom_set_str_config(c,
-                       &config.c_mgmt_pattern, &c->config->c_mgmt_pattern,
-                       value))
-                       return NULL;
-               break;
-       case lldpctl_k_config_description:
-               if (!__lldpctl_atom_set_str_config(c,
-                       &config.c_description, &c->config->c_description,
-                       value))
-                       return NULL;
-               break;
-       case lldpctl_k_config_platform:
-               if (!__lldpctl_atom_set_str_config(c,
-                       &config.c_platform, &c->config->c_platform,
-                       value))
-                       return NULL;
-               break;
-       case lldpctl_k_config_hostname:
-               if (!__lldpctl_atom_set_str_config(c,
-                       &config.c_hostname, &c->config->c_hostname,
-                       value))
-                       return NULL;
-               break;
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-
-       if (asprintf(&canary, "%d%s", key, value?value:"(NULL)") == -1) {
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
-               return NULL;
-       }
-       rc = _lldpctl_do_something(atom->conn,
-           CONN_STATE_SET_CONFIG_SEND, CONN_STATE_SET_CONFIG_RECV,
-           canary,
-           SET_CONFIG, &config, &MARSHAL_INFO(lldpd_config),
-           NULL, NULL);
-       free(canary);
-       if (rc == 0) return atom;
-
-#undef SET_STR
-
-       return NULL;
-}
-
-static long int
-_lldpctl_atom_get_int_config(lldpctl_atom_t *atom, lldpctl_key_t key)
-{
-       struct _lldpctl_atom_config_t *c =
-           (struct _lldpctl_atom_config_t *)atom;
-       switch (key) {
-       case lldpctl_k_config_paused:
-               return c->config->c_paused;
-       case lldpctl_k_config_tx_interval:
-               return c->config->c_tx_interval;
-       case lldpctl_k_config_receiveonly:
-               return c->config->c_receiveonly;
-       case lldpctl_k_config_advertise_version:
-               return c->config->c_advertise_version;
-       case lldpctl_k_config_ifdescr_update:
-               return c->config->c_set_ifdescr;
-       case lldpctl_k_config_iface_promisc:
-               return c->config->c_promisc;
-#ifdef ENABLE_LLDPMED
-       case lldpctl_k_config_lldpmed_noinventory:
-               return c->config->c_noinventory;
-       case lldpctl_k_config_fast_start_enabled:
-               return c->config->c_enable_fast_start;
-       case lldpctl_k_config_fast_start_interval:
-               return c->config->c_tx_fast_interval;
-#endif
-       case lldpctl_k_config_tx_hold:
-               return c->config->c_tx_hold;
-       default:
-               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-       }
-}
-
-static lldpctl_atom_t*
-_lldpctl_atom_set_int_config(lldpctl_atom_t *atom, lldpctl_key_t key,
-    long int value)
-{
-       int rc;
-       char *canary = NULL;
-       struct _lldpctl_atom_config_t *c =
-           (struct _lldpctl_atom_config_t *)atom;
-       struct lldpd_config config;
-       memcpy(&config, c->config, sizeof(struct lldpd_config));
-
-       switch (key) {
-       case lldpctl_k_config_paused:
-               config.c_paused = c->config->c_paused = value;
-               break;
-       case lldpctl_k_config_tx_interval:
-               config.c_tx_interval = value;
-               if (value > 0) c->config->c_tx_interval = value;
-               break;
-       case lldpctl_k_config_ifdescr_update:
-               config.c_set_ifdescr = c->config->c_set_ifdescr = value;
-               break;
-       case lldpctl_k_config_iface_promisc:
-               config.c_promisc = c->config->c_promisc = value;
-               break;
-#ifdef ENABLE_LLDPMED
-       case lldpctl_k_config_fast_start_enabled:
-               config.c_enable_fast_start = c->config->c_enable_fast_start = value;
-               break;
-       case lldpctl_k_config_fast_start_interval:
-               config.c_tx_fast_interval = c->config->c_tx_fast_interval = value;
-               break;
-#endif
-       case lldpctl_k_config_tx_hold:
-               config.c_tx_hold = value;
-               if (value > 0) c->config->c_tx_hold = value;
-               break;
-       case lldpctl_k_config_bond_slave_src_mac_type:
-               config.c_bond_slave_src_mac_type = value;
-               c->config->c_bond_slave_src_mac_type = value;
-               break;
-       case lldpctl_k_config_lldp_portid_type:
-               config.c_lldp_portid_type = value;
-               c->config->c_lldp_portid_type = value;
-               break;
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-
-       if (asprintf(&canary, "%d%ld", key, value) == -1) {
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
-               return NULL;
-       }
-       rc = _lldpctl_do_something(atom->conn,
-           CONN_STATE_SET_CONFIG_SEND, CONN_STATE_SET_CONFIG_RECV,
-           canary,
-           SET_CONFIG, &config, &MARSHAL_INFO(lldpd_config),
-           NULL, NULL);
-       free(canary);
-       if (rc == 0) return atom;
-       return NULL;
-}
-
-static int
-_lldpctl_atom_new_interfaces_list(lldpctl_atom_t *atom, va_list ap)
-{
-       struct _lldpctl_atom_interfaces_list_t *iflist =
-           (struct _lldpctl_atom_interfaces_list_t *)atom;
-       iflist->ifs = va_arg(ap, struct lldpd_interface_list *);
-       return 1;
-}
-
-static void
-_lldpctl_atom_free_interfaces_list(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_interfaces_list_t *iflist =
-           (struct _lldpctl_atom_interfaces_list_t *)atom;
-       struct lldpd_interface *iface, *iface_next;
-       for (iface = TAILQ_FIRST(iflist->ifs);
-            iface != NULL;
-            iface = iface_next) {
-               /* Don't TAILQ_REMOVE, this is not a real list! */
-               iface_next = TAILQ_NEXT(iface, next);
-               free(iface->name);
-               free(iface);
-       }
-       free(iflist->ifs);
-}
-
-static lldpctl_atom_iter_t*
-_lldpctl_atom_iter_interfaces_list(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_interfaces_list_t *iflist =
-           (struct _lldpctl_atom_interfaces_list_t *)atom;
-       return (lldpctl_atom_iter_t*)TAILQ_FIRST(iflist->ifs);
-}
-
-static lldpctl_atom_iter_t*
-_lldpctl_atom_next_interfaces_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
-{
-       return (lldpctl_atom_iter_t*)TAILQ_NEXT((struct lldpd_interface *)iter, next);
-}
-
-static lldpctl_atom_t*
-_lldpctl_atom_value_interfaces_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
-{
-       struct lldpd_interface *iface = (struct lldpd_interface *)iter;
-       return _lldpctl_new_atom(atom->conn, atom_interface, iface->name);
-}
-
-static int
-_lldpctl_atom_new_interface(lldpctl_atom_t *atom, va_list ap)
-{
-       struct _lldpctl_atom_interface_t *port =
-           (struct _lldpctl_atom_interface_t *)atom;
-       port->name = strdup(va_arg(ap, char *));
-       return (port->name != NULL);
-}
-
-static void
-_lldpctl_atom_free_interface(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_interface_t *port =
-           (struct _lldpctl_atom_interface_t *)atom;
-       free(port->name);
-}
-
-static const char*
-_lldpctl_atom_get_str_interface(lldpctl_atom_t *atom, lldpctl_key_t key)
-{
-       struct _lldpctl_atom_interface_t *port =
-           (struct _lldpctl_atom_interface_t *)atom;
-       switch (key) {
-       case lldpctl_k_interface_name:
-               return port->name;
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-}
-
-static int
-_lldpctl_atom_new_any_list(lldpctl_atom_t *atom, va_list ap)
-{
-       struct _lldpctl_atom_any_list_t *plist =
-           (struct _lldpctl_atom_any_list_t *)atom;
-       plist->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
-       lldpctl_atom_inc_ref((lldpctl_atom_t *)plist->parent);
-       return 1;
-}
-
-static void
-_lldpctl_atom_free_any_list(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_any_list_t *plist =
-           (struct _lldpctl_atom_any_list_t *)atom;
-       lldpctl_atom_dec_ref((lldpctl_atom_t *)plist->parent);
-}
-
-static lldpctl_atom_iter_t*
-_lldpctl_atom_iter_ports_list(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_any_list_t *plist =
-           (struct _lldpctl_atom_any_list_t *)atom;
-       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&plist->parent->hardware->h_rports);
-}
-
-static lldpctl_atom_iter_t*
-_lldpctl_atom_next_ports_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
-{
-       struct lldpd_port *port = (struct lldpd_port *)iter;
-       return (lldpctl_atom_iter_t*)TAILQ_NEXT(port, p_entries);
-}
-
-static lldpctl_atom_t*
-_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,
-           ((struct _lldpctl_atom_any_list_t *)atom)->parent);
-}
-
-static int
-_lldpctl_atom_new_port(lldpctl_atom_t *atom, va_list ap)
-{
-       struct _lldpctl_atom_port_t *port =
-           (struct _lldpctl_atom_port_t *)atom;
-       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*);
-       if (port->parent)
-               lldpctl_atom_inc_ref((lldpctl_atom_t*)port->parent);
-       return 1;
-}
-
-TAILQ_HEAD(chassis_list, lldpd_chassis);
-
-static void
-add_chassis(struct chassis_list *chassis_list,
-       struct lldpd_chassis *chassis)
-{
-       struct lldpd_chassis *one_chassis;
-       TAILQ_FOREACH(one_chassis, chassis_list, c_entries) {
-               if (one_chassis == chassis) return;
-       }
-       TAILQ_INSERT_TAIL(chassis_list,
-           chassis, c_entries);
-}
-
-static void
-_lldpctl_atom_free_port(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_port_t *port =
-           (struct _lldpctl_atom_port_t *)atom;
-       struct lldpd_hardware *hardware = port->hardware;
-       struct lldpd_chassis  *one_chassis, *one_chassis_next;
-       struct lldpd_port     *one_port;
-
-       /* We need to free the whole struct lldpd_hardware: local port, local
-        * chassis and remote ports... The same chassis may be present several
-        * times. We build a list of chassis (we don't use reference count). */
-       struct chassis_list chassis_list;
-       TAILQ_INIT(&chassis_list);
-
-       if (port->parent) lldpctl_atom_dec_ref((lldpctl_atom_t*)port->parent);
-       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;
-               lldpd_port_cleanup(port->port, 1);
-               free(port->port);
-       }
-       if (!hardware) return;
-
-       add_chassis(&chassis_list, port->port->p_chassis);
-       TAILQ_FOREACH(one_port, &hardware->h_rports, p_entries)
-               add_chassis(&chassis_list, one_port->p_chassis);
-
-       /* Free hardware port */
-       lldpd_remote_cleanup(hardware, NULL, 1);
-       lldpd_port_cleanup(port->port, 1);
-       free(port->hardware);
-
-       /* Free list of chassis */
-       for (one_chassis = TAILQ_FIRST(&chassis_list);
-            one_chassis != NULL;
-            one_chassis = one_chassis_next) {
-               one_chassis_next = TAILQ_NEXT(one_chassis, c_entries);
-               lldpd_chassis_cleanup(one_chassis, 1);
-       }
-}
-
-static lldpctl_atom_t*
-_lldpctl_atom_get_atom_port(lldpctl_atom_t *atom, lldpctl_key_t key)
-{
-       struct _lldpctl_atom_port_t *p =
-           (struct _lldpctl_atom_port_t *)atom;
-       struct lldpd_port     *port     = p->port;
-       struct lldpd_hardware *hardware = p->hardware;
-
-       /* Local port only */
-       if (hardware != NULL) {
-               switch (key) {
-               case lldpctl_k_port_neighbors:
-                       return _lldpctl_new_atom(atom->conn, atom_ports_list, p);
-               default: break;
-               }
-       }
-
-       /* Local and remote port */
-       switch (key) {
-       case lldpctl_k_chassis_mgmt:
-               return _lldpctl_new_atom(atom->conn, atom_mgmts_list,
-                   p, port->p_chassis);
-#ifdef ENABLE_DOT3
-       case lldpctl_k_port_dot3_power:
-               return _lldpctl_new_atom(atom->conn, atom_dot3_power,
-                   p);
-#endif
-#ifdef ENABLE_DOT1
-       case lldpctl_k_port_vlans:
-               return _lldpctl_new_atom(atom->conn, atom_vlans_list,
-                   p);
-       case lldpctl_k_port_ppvids:
-               return _lldpctl_new_atom(atom->conn, atom_ppvids_list,
-                   p);
-       case lldpctl_k_port_pis:
-               return _lldpctl_new_atom(atom->conn, atom_pis_list,
-                   p);
-#endif
-#ifdef ENABLE_LLDPMED
-       case lldpctl_k_port_med_policies:
-               return _lldpctl_new_atom(atom->conn, atom_med_policies_list,
-                   p);
-       case lldpctl_k_port_med_locations:
-               return _lldpctl_new_atom(atom->conn, atom_med_locations_list,
-                   p);
-       case lldpctl_k_port_med_power:
-               return _lldpctl_new_atom(atom->conn, atom_med_power, p);
-#endif
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-}
-
-static lldpctl_atom_t*
-_lldpctl_atom_set_atom_port(lldpctl_atom_t *atom, lldpctl_key_t key, lldpctl_atom_t *value)
-{
-       struct _lldpctl_atom_port_t *p =
-           (struct _lldpctl_atom_port_t *)atom;
-       struct lldpd_hardware *hardware = p->hardware;
-       struct lldpd_port_set set = {};
-       int rc;
-       char *canary;
-
-#ifdef ENABLE_DOT3
-       struct _lldpctl_atom_dot3_power_t *dpow;
-#endif
-#ifdef ENABLE_LLDPMED
-       struct _lldpctl_atom_med_power_t *mpow;
-       struct _lldpctl_atom_med_policy_t *mpol;
-       struct _lldpctl_atom_med_location_t *mloc;
-#endif
-
-       /* Local port only */
-       if (hardware == NULL) {
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-
-       switch (key) {
-       case lldpctl_k_port_id:
-               set.local_id = p->port->p_id;
-               break;
-       case lldpctl_k_port_descr:
-               set.local_descr = p->port->p_descr;
-               break;
-#ifdef ENABLE_DOT3
-       case lldpctl_k_port_dot3_power:
-               if (value->type != atom_dot3_power) {
-                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
-                       return NULL;
-               }
-
-               dpow = (struct _lldpctl_atom_dot3_power_t *)value;
-               set.dot3_power = &dpow->parent->port->p_power;
-               break;
-#endif
-#ifdef ENABLE_LLDPMED
-       case lldpctl_k_port_med_power:
-               if (value->type != atom_med_power) {
-                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
-                       return NULL;
-               }
-
-               mpow = (struct _lldpctl_atom_med_power_t *)value;
-               set.med_power = &mpow->parent->port->p_med_power;
-               break;
-       case lldpctl_k_port_med_policies:
-               if (value->type != atom_med_policy) {
-                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
-                       return NULL;
-               }
-               mpol = (struct _lldpctl_atom_med_policy_t *)value;
-               set.med_policy = mpol->policy;
-               break;
-       case lldpctl_k_port_med_locations:
-               if (value->type != atom_med_location) {
-                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
-                       return NULL;
-               }
-               mloc = (struct _lldpctl_atom_med_location_t *)value;
-               set.med_location = mloc->location;
-               break;
-#endif
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-
-       set.ifname = hardware->h_ifname;
-
-       if (asprintf(&canary, "%d%p%s", key, value, set.ifname) == -1) {
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
-               return NULL;
-       }
-       rc = _lldpctl_do_something(atom->conn,
-           CONN_STATE_SET_PORT_SEND, CONN_STATE_SET_PORT_RECV,
-           canary,
-           SET_PORT, &set, &MARSHAL_INFO(lldpd_port_set),
-           NULL, NULL);
-       free(canary);
-       if (rc == 0) return atom;
-       return NULL;
-}
-
-static const char*
-_lldpctl_atom_get_str_port(lldpctl_atom_t *atom, lldpctl_key_t key)
-{
-       struct _lldpctl_atom_port_t *p =
-           (struct _lldpctl_atom_port_t *)atom;
-       struct lldpd_port     *port     = p->port;
-       struct lldpd_hardware *hardware = p->hardware;
-       struct lldpd_chassis  *chassis  = port->p_chassis;
-       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;
-               }
-       }
-
-       /* Local and remote port */
-       switch (key) {
-       case lldpctl_k_port_protocol:
-               return map_lookup(lldpd_protocol_map, port->p_protocol);
-       case lldpctl_k_port_id_subtype:
-               return map_lookup(port_id_subtype_map, port->p_id_subtype);
-       case lldpctl_k_port_id:
-               switch (port->p_id_subtype) {
-               case LLDP_PORTID_SUBTYPE_IFNAME:
-               case LLDP_PORTID_SUBTYPE_IFALIAS:
-               case LLDP_PORTID_SUBTYPE_LOCAL:
-                       return port->p_id;
-               case LLDP_PORTID_SUBTYPE_LLADDR:
-                       return _lldpctl_dump_in_atom(atom,
-                           (uint8_t*)port->p_id, port->p_id_len,
-                           ':', 0);
-               case LLDP_PORTID_SUBTYPE_ADDR:
-                       switch (port->p_id[0]) {
-                       case LLDP_MGMT_ADDR_IP4: len = INET_ADDRSTRLEN + 1; break;
-                       case LLDP_MGMT_ADDR_IP6: len = INET6_ADDRSTRLEN + 1; break;
-                       default: len = 0;
-                       }
-                       if (len > 0) {
-                               ipaddress = _lldpctl_alloc_in_atom(atom, len);
-                               if (!ipaddress) return NULL;
-                               if (inet_ntop((port->p_id[0] == LLDP_MGMT_ADDR_IP4)?
-                                       AF_INET:AF_INET6,
-                                       &port->p_id[1], ipaddress, len) == NULL)
-                                       break;
-                               return ipaddress;
-                       }
-                       break;
-               }
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       case lldpctl_k_port_descr:
-               return port->p_descr;
-
-#ifdef ENABLE_DOT3
-       case lldpctl_k_port_dot3_mautype:
-               return map_lookup(operational_mau_type_values,
-                   port->p_macphy.mau_type);
-#endif
-
-       case lldpctl_k_chassis_id_subtype:
-               return map_lookup(chassis_id_subtype_map, chassis->c_id_subtype);
-       case lldpctl_k_chassis_id:
-               switch (chassis->c_id_subtype) {
-               case LLDP_CHASSISID_SUBTYPE_IFNAME:
-               case LLDP_CHASSISID_SUBTYPE_IFALIAS:
-               case LLDP_CHASSISID_SUBTYPE_LOCAL:
-                       return chassis->c_id;
-               case LLDP_CHASSISID_SUBTYPE_LLADDR:
-                       return _lldpctl_dump_in_atom(atom,
-                           (uint8_t*)chassis->c_id, chassis->c_id_len,
-                           ':', 0);
-               case LLDP_CHASSISID_SUBTYPE_ADDR:
-                       switch (chassis->c_id[0]) {
-                       case LLDP_MGMT_ADDR_IP4: len = INET_ADDRSTRLEN + 1; break;
-                       case LLDP_MGMT_ADDR_IP6: len = INET6_ADDRSTRLEN + 1; break;
-                       default: len = 0;
-                       }
-                       if (len > 0) {
-                               ipaddress = _lldpctl_alloc_in_atom(atom, len);
-                               if (!ipaddress) return NULL;
-                               if (inet_ntop((chassis->c_id[0] == LLDP_MGMT_ADDR_IP4)?
-                                       AF_INET:AF_INET6,
-                                       &chassis->c_id[1], ipaddress, len) == NULL)
-                                       break;
-                               return ipaddress;
-                       }
-                       break;
-               }
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       case lldpctl_k_chassis_name: return chassis->c_name;
-       case lldpctl_k_chassis_descr: return chassis->c_descr;
-
-#ifdef ENABLE_LLDPMED
-       case lldpctl_k_chassis_med_type:
-               return map_lookup(chassis_med_type_map, chassis->c_med_type);
-       case lldpctl_k_chassis_med_inventory_hw:
-               return chassis->c_med_hw;
-       case lldpctl_k_chassis_med_inventory_sw:
-               return chassis->c_med_sw;
-       case lldpctl_k_chassis_med_inventory_fw:
-               return chassis->c_med_fw;
-       case lldpctl_k_chassis_med_inventory_sn:
-               return chassis->c_med_sn;
-       case lldpctl_k_chassis_med_inventory_manuf:
-               return chassis->c_med_manuf;
-       case lldpctl_k_chassis_med_inventory_model:
-               return chassis->c_med_model;
-       case lldpctl_k_chassis_med_inventory_asset:
-               return chassis->c_med_asset;
-#endif
-
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-}
-
-static lldpctl_atom_t*
-_lldpctl_atom_set_str_port(lldpctl_atom_t *atom, lldpctl_key_t key,
-    const char *value)
-{
-       struct _lldpctl_atom_port_t *p =
-           (struct _lldpctl_atom_port_t *)atom;
-       struct lldpd_port     *port     = p->port;
-
-       if (!value || !strlen(value))
-               return NULL;
-
-       switch (key) {
-       case lldpctl_k_port_id:
-               free(port->p_id);
-               port->p_id = strdup(value);
-               port->p_id_len = strlen(value);
-               break;
-       case lldpctl_k_port_descr:
-               free(port->p_descr);
-               port->p_descr = strdup(value);
-               break;
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-
-       return _lldpctl_atom_set_atom_port(atom, key, NULL);
-}
-
-static long int
-_lldpctl_atom_get_int_port(lldpctl_atom_t *atom, lldpctl_key_t key)
-{
-       struct _lldpctl_atom_port_t *p =
-           (struct _lldpctl_atom_port_t *)atom;
-       struct lldpd_port     *port     = p->port;
-       struct lldpd_hardware *hardware = p->hardware;
-       struct lldpd_chassis  *chassis  = port->p_chassis;
-
-       /* Local port only */
-       if (hardware != NULL) {
-               switch (key) {
-               case lldpctl_k_port_index:
-                       return hardware->h_ifindex;
-               case lldpctl_k_tx_cnt:
-                       return hardware->h_tx_cnt;
-               case lldpctl_k_rx_cnt:
-                       return hardware->h_rx_cnt;
-               case lldpctl_k_rx_discarded_cnt:
-                       return hardware->h_rx_discarded_cnt;
-               case lldpctl_k_rx_unrecognized_cnt:
-                       return hardware->h_rx_unrecognized_cnt;
-               case lldpctl_k_ageout_cnt:
-                       return hardware->h_ageout_cnt;
-               case lldpctl_k_insert_cnt:
-                       return hardware->h_insert_cnt;
-               case lldpctl_k_delete_cnt:
-                       return hardware->h_delete_cnt;
-               default: break;
-               }
-       }
-
-       /* Local and remote port */
-       switch (key) {
-       case lldpctl_k_port_protocol:
-               return port->p_protocol;
-       case lldpctl_k_port_age:
-               return port->p_lastchange;
-       case lldpctl_k_port_id_subtype:
-               return port->p_id_subtype;
-       case lldpctl_k_port_hidden:
-               return port->p_hidden_in;
-#ifdef ENABLE_DOT3
-       case lldpctl_k_port_dot3_mfs:
-               if (port->p_mfs > 0)
-                       return port->p_mfs;
-               break;
-       case lldpctl_k_port_dot3_aggregid:
-               if (port->p_aggregid > 0)
-                       return port->p_aggregid;
-               break;
-       case lldpctl_k_port_dot3_autoneg_support:
-               return port->p_macphy.autoneg_support;
-       case lldpctl_k_port_dot3_autoneg_enabled:
-               return port->p_macphy.autoneg_enabled;
-       case lldpctl_k_port_dot3_autoneg_advertised:
-               return port->p_macphy.autoneg_advertised;
-       case lldpctl_k_port_dot3_mautype:
-               return port->p_macphy.mau_type;
-#endif
-#ifdef ENABLE_DOT1
-       case lldpctl_k_port_vlan_pvid:
-               return port->p_pvid;
-#endif
-       case lldpctl_k_chassis_index:
-               return chassis->c_index;
-       case lldpctl_k_chassis_id_subtype:
-               return chassis->c_id_subtype;
-       case lldpctl_k_chassis_cap_available:
-               return chassis->c_cap_available;
-       case lldpctl_k_chassis_cap_enabled:
-               return chassis->c_cap_enabled;
-#ifdef ENABLE_LLDPMED
-       case lldpctl_k_chassis_med_type:
-               return chassis->c_med_type;
-       case lldpctl_k_chassis_med_cap:
-               return chassis->c_med_cap_available;
-#endif
-       default:
-               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-       }
-       return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-}
-
-static const uint8_t*
-_lldpctl_atom_get_buf_port(lldpctl_atom_t *atom, lldpctl_key_t key, size_t *n)
-{
-       struct _lldpctl_atom_port_t *p =
-           (struct _lldpctl_atom_port_t *)atom;
-       struct lldpd_port     *port     = p->port;
-       struct lldpd_chassis  *chassis  = port->p_chassis;
-
-       switch (key) {
-       case lldpctl_k_port_id:
-               *n = port->p_id_len;
-               return (uint8_t*)port->p_id;
-       case lldpctl_k_chassis_id:
-               *n = chassis->c_id_len;
-               return (uint8_t*)chassis->c_id;
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
+       struct atom_map *map;
+       for (map = atom_map_list.next; map ; map = map->next) {
+               if (map->key == key)
+                       return map->map;
        }
+       return empty_map;
 }
 
-static int
-_lldpctl_atom_new_mgmts_list(lldpctl_atom_t *atom, va_list ap)
+void atom_map_register(struct atom_map *map)
 {
-       struct _lldpctl_atom_mgmts_list_t *plist =
-           (struct _lldpctl_atom_mgmts_list_t *)atom;
-       plist->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
-       plist->chassis = va_arg(ap, struct lldpd_chassis *);
-       lldpctl_atom_inc_ref((lldpctl_atom_t *)plist->parent);
-       return 1;
-}
+       struct atom_map* iter = &atom_map_list;
 
-static void
-_lldpctl_atom_free_mgmts_list(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_mgmts_list_t *plist =
-           (struct _lldpctl_atom_mgmts_list_t *)atom;
-       lldpctl_atom_dec_ref((lldpctl_atom_t *)plist->parent);
-}
+       while (iter->next)
+               iter = iter->next;
 
-static lldpctl_atom_iter_t*
-_lldpctl_atom_iter_mgmts_list(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_mgmts_list_t *plist =
-           (struct _lldpctl_atom_mgmts_list_t *)atom;
-       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&plist->chassis->c_mgmt);
+       iter->next = map;
 }
 
-static lldpctl_atom_iter_t*
-_lldpctl_atom_next_mgmts_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
-{
-       struct lldpd_mgmt *mgmt = (struct lldpd_mgmt *)iter;
-       return (lldpctl_atom_iter_t*)TAILQ_NEXT(mgmt, m_entries);
-}
+static struct atom_builder atom_builder_list = {
+       .nextb = NULL
+};
 
-static lldpctl_atom_t*
-_lldpctl_atom_value_mgmts_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+void atom_builder_register(struct atom_builder *builder)
 {
-       struct _lldpctl_atom_mgmts_list_t *plist =
-           (struct _lldpctl_atom_mgmts_list_t *)atom;
-       struct lldpd_mgmt *mgmt = (struct lldpd_mgmt *)iter;
-       return _lldpctl_new_atom(atom->conn, atom_mgmt, plist->parent, mgmt);
-}
+       struct atom_builder* iter = &atom_builder_list;
 
-static int
-_lldpctl_atom_new_mgmt(lldpctl_atom_t *atom, va_list ap)
-{
-       struct _lldpctl_atom_mgmt_t *mgmt =
-           (struct _lldpctl_atom_mgmt_t *)atom;
-       mgmt->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
-       mgmt->mgmt = va_arg(ap, struct lldpd_mgmt *);
-       lldpctl_atom_inc_ref((lldpctl_atom_t *)mgmt->parent);
-       return 1;
-}
+       while (iter->nextb)
+               iter = iter->nextb;
 
-static void
-_lldpctl_atom_free_mgmt(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_mgmt_t *mgmt =
-           (struct _lldpctl_atom_mgmt_t *)atom;
-       lldpctl_atom_dec_ref((lldpctl_atom_t *)mgmt->parent);
+       iter->nextb = builder;
 }
 
-static const char*
-_lldpctl_atom_get_str_mgmt(lldpctl_atom_t *atom, lldpctl_key_t key)
-{
-       char *ipaddress = NULL;
-       size_t len; int af;
-       struct _lldpctl_atom_mgmt_t *m =
-           (struct _lldpctl_atom_mgmt_t *)atom;
-
-       /* Local and remote port */
-       switch (key) {
-       case lldpctl_k_mgmt_ip:
-               switch (m->mgmt->m_family) {
-               case LLDPD_AF_IPV4:
-                       len = INET_ADDRSTRLEN + 1;
-                       af  = AF_INET;
-                       break;
-               case LLDPD_AF_IPV6:
-                       len = INET6_ADDRSTRLEN + 1;
-                       af = AF_INET6;
-                       break;
-               default:
-                       len = 0;
-               }
-               if (len == 0) break;
-               ipaddress = _lldpctl_alloc_in_atom(atom, len);
-               if (!ipaddress) return NULL;
-               if (inet_ntop(af, &m->mgmt->m_addr, ipaddress, len) == NULL)
-                       break;
-               return ipaddress;
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-       SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-       return NULL;
-}
-
-#ifdef ENABLE_DOT3
-static int
-_lldpctl_atom_new_dot3_power(lldpctl_atom_t *atom, va_list ap)
-{
-       struct _lldpctl_atom_dot3_power_t *dpow =
-           (struct _lldpctl_atom_dot3_power_t *)atom;
-       dpow->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
-       lldpctl_atom_inc_ref((lldpctl_atom_t *)dpow->parent);
-       return 1;
-}
-
-static void
-_lldpctl_atom_free_dot3_power(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_dot3_power_t *dpow =
-           (struct _lldpctl_atom_dot3_power_t *)atom;
-       lldpctl_atom_dec_ref((lldpctl_atom_t *)dpow->parent);
-}
-
-static const char*
-_lldpctl_atom_get_str_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key)
-{
-       struct _lldpctl_atom_dot3_power_t *dpow =
-           (struct _lldpctl_atom_dot3_power_t *)atom;
-       struct lldpd_port     *port     = dpow->parent->port;
-
-       /* Local and remote port */
-       switch (key) {
-       case lldpctl_k_dot3_power_devicetype:
-               return map_lookup(port_dot3_power_devicetype_map,
-                   port->p_power.devicetype);
-       case lldpctl_k_dot3_power_pairs:
-               return map_lookup(port_dot3_power_pairs_map,
-                   port->p_power.pairs);
-       case lldpctl_k_dot3_power_class:
-               return map_lookup(port_dot3_power_class_map,
-                   port->p_power.class);
-       case lldpctl_k_dot3_power_source:
-               return map_lookup((port->p_power.devicetype == LLDP_DOT3_POWER_PSE)?
-                   port_dot3_power_pse_source_map:
-                   port_dot3_power_pd_source_map,
-                   port->p_power.source);
-       case lldpctl_k_dot3_power_priority:
-               return map_lookup(port_dot3_power_priority_map,
-                   port->p_power.priority);
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-}
-
-static long int
-_lldpctl_atom_get_int_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key)
-{
-       struct _lldpctl_atom_dot3_power_t *dpow =
-           (struct _lldpctl_atom_dot3_power_t *)atom;
-       struct lldpd_port     *port     = dpow->parent->port;
-
-       /* Local and remote port */
-       switch (key) {
-       case lldpctl_k_dot3_power_devicetype:
-               return port->p_power.devicetype;
-       case lldpctl_k_dot3_power_supported:
-               return port->p_power.supported;
-       case lldpctl_k_dot3_power_enabled:
-               return port->p_power.enabled;
-       case lldpctl_k_dot3_power_paircontrol:
-               return port->p_power.paircontrol;
-       case lldpctl_k_dot3_power_pairs:
-               return port->p_power.pairs;
-       case lldpctl_k_dot3_power_class:
-               return port->p_power.class;
-       case lldpctl_k_dot3_power_type:
-               return port->p_power.powertype;
-       case lldpctl_k_dot3_power_source:
-               return port->p_power.source;
-       case lldpctl_k_dot3_power_priority:
-               return port->p_power.priority;
-       case lldpctl_k_dot3_power_requested:
-               return port->p_power.requested * 100;
-       case lldpctl_k_dot3_power_allocated:
-               return port->p_power.allocated * 100;
-       default:
-               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-       }
-}
-
-static lldpctl_atom_t*
-_lldpctl_atom_set_int_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key,
-    long int value)
-{
-       struct _lldpctl_atom_dot3_power_t *dpow =
-           (struct _lldpctl_atom_dot3_power_t *)atom;
-       struct lldpd_port *port = dpow->parent->port;
-
-       /* Only local port can be modified */
-       if (dpow->parent->hardware == NULL) {
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-
-       switch (key) {
-       case lldpctl_k_dot3_power_devicetype:
-               switch (value) {
-               case 0:         /* Disabling */
-               case LLDP_DOT3_POWER_PSE:
-               case LLDP_DOT3_POWER_PD:
-                       port->p_power.devicetype = value;
-                       return atom;
-               default: goto bad;
-               }
-       case lldpctl_k_dot3_power_supported:
-               switch (value) {
-               case 0:
-               case 1:
-                       port->p_power.supported = value;
-                       return atom;
-               default: goto bad;
-               }
-       case lldpctl_k_dot3_power_enabled:
-               switch (value) {
-               case 0:
-               case 1:
-                       port->p_power.enabled = value;
-                       return atom;
-               default: goto bad;
-               }
-       case lldpctl_k_dot3_power_paircontrol:
-               switch (value) {
-               case 0:
-               case 1:
-                       port->p_power.paircontrol = value;
-                       return atom;
-               default: goto bad;
-               }
-       case lldpctl_k_dot3_power_pairs:
-               switch (value) {
-               case 1:
-               case 2:
-                       port->p_power.pairs = value;
-                       return atom;
-               default: goto bad;
-               }
-       case lldpctl_k_dot3_power_class:
-               if (value < 0 || value > 5)
-                       goto bad;
-               port->p_power.class = value;
-               return atom;
-       case lldpctl_k_dot3_power_type:
-               switch (value) {
-               case LLDP_DOT3_POWER_8023AT_TYPE1:
-               case LLDP_DOT3_POWER_8023AT_TYPE2:
-               case LLDP_DOT3_POWER_8023AT_OFF:
-                       port->p_power.powertype = value;
-                       return atom;
-               default: goto bad;
-               }
-       case lldpctl_k_dot3_power_source:
-               if (value < 0 || value > 3)
-                       goto bad;
-               port->p_power.source = value;
-               return atom;
-       case lldpctl_k_dot3_power_priority:
-               switch (value) {
-               case LLDP_DOT3_POWER_PRIO_UNKNOWN:
-               case LLDP_DOT3_POWER_PRIO_CRITICAL:
-               case LLDP_DOT3_POWER_PRIO_HIGH:
-               case LLDP_DOT3_POWER_PRIO_LOW:
-                       port->p_power.priority = value;
-                       return atom;
-               default: goto bad;
-               }
-       case lldpctl_k_dot3_power_allocated:
-               if (value < 0) goto bad;
-               port->p_power.allocated = value / 100;
-               return atom;
-       case lldpctl_k_dot3_power_requested:
-               if (value < 0) goto bad;
-               port->p_power.requested = 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_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key,
-    const char *value)
-{
-       switch (key) {
-       case lldpctl_k_dot3_power_devicetype:
-               return _lldpctl_atom_set_int_dot3_power(atom, key,
-                   map_reverse_lookup(port_dot3_power_devicetype_map, value));
-       case lldpctl_k_dot3_power_pairs:
-               return _lldpctl_atom_set_int_dot3_power(atom, key,
-                   map_reverse_lookup(port_dot3_power_pairs_map, value));
-       case lldpctl_k_dot3_power_class:
-               return _lldpctl_atom_set_int_dot3_power(atom, key,
-                   map_reverse_lookup(port_dot3_power_class_map, value));
-       case lldpctl_k_dot3_power_priority:
-               return _lldpctl_atom_set_int_dot3_power(atom, key,
-                   map_reverse_lookup(port_dot3_power_priority_map, value));
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-}
-#endif
-
-#ifdef ENABLE_DOT1
-static lldpctl_atom_iter_t*
-_lldpctl_atom_iter_vlans_list(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_any_list_t *vlist =
-           (struct _lldpctl_atom_any_list_t *)atom;
-       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&vlist->parent->port->p_vlans);
-}
-
-static lldpctl_atom_iter_t*
-_lldpctl_atom_next_vlans_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
-{
-       struct lldpd_vlan *vlan = (struct lldpd_vlan *)iter;
-       return (lldpctl_atom_iter_t*)TAILQ_NEXT(vlan, v_entries);
-}
-
-static lldpctl_atom_t*
-_lldpctl_atom_value_vlans_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_vlan *vlan = (struct lldpd_vlan *)iter;
-       return _lldpctl_new_atom(atom->conn, atom_vlan, vlist->parent, vlan);
-}
-
-static int
-_lldpctl_atom_new_vlan(lldpctl_atom_t *atom, va_list ap)
-{
-       struct _lldpctl_atom_vlan_t *vlan =
-           (struct _lldpctl_atom_vlan_t *)atom;
-       vlan->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
-       vlan->vlan = va_arg(ap, struct lldpd_vlan *);
-       lldpctl_atom_inc_ref((lldpctl_atom_t *)vlan->parent);
-       return 1;
-}
-
-static void
-_lldpctl_atom_free_vlan(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_vlan_t *vlan =
-           (struct _lldpctl_atom_vlan_t *)atom;
-       lldpctl_atom_dec_ref((lldpctl_atom_t *)vlan->parent);
-}
-
-static const char*
-_lldpctl_atom_get_str_vlan(lldpctl_atom_t *atom, lldpctl_key_t key)
-{
-       struct _lldpctl_atom_vlan_t *m =
-           (struct _lldpctl_atom_vlan_t *)atom;
-
-       /* Local and remote port */
-       switch (key) {
-       case lldpctl_k_vlan_name:
-               return m->vlan->v_name;
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-}
-
-static long int
-_lldpctl_atom_get_int_vlan(lldpctl_atom_t *atom, lldpctl_key_t key)
-{
-       struct _lldpctl_atom_vlan_t *m =
-           (struct _lldpctl_atom_vlan_t *)atom;
-
-       /* Local and remote port */
-       switch (key) {
-       case lldpctl_k_vlan_id:
-               return m->vlan->v_vid;
-       default:
-               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-       }
-}
-
-static lldpctl_atom_iter_t*
-_lldpctl_atom_iter_ppvids_list(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_any_list_t *vlist =
-           (struct _lldpctl_atom_any_list_t *)atom;
-       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&vlist->parent->port->p_ppvids);
-}
-
-static lldpctl_atom_iter_t*
-_lldpctl_atom_next_ppvids_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
-{
-       struct lldpd_ppvid *ppvid = (struct lldpd_ppvid *)iter;
-       return (lldpctl_atom_iter_t*)TAILQ_NEXT(ppvid, p_entries);
-}
-
-static lldpctl_atom_t*
-_lldpctl_atom_value_ppvids_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_ppvid *ppvid = (struct lldpd_ppvid *)iter;
-       return _lldpctl_new_atom(atom->conn, atom_ppvid, vlist->parent, ppvid);
-}
-
-static int
-_lldpctl_atom_new_ppvid(lldpctl_atom_t *atom, va_list ap)
-{
-       struct _lldpctl_atom_ppvid_t *ppvid =
-           (struct _lldpctl_atom_ppvid_t *)atom;
-       ppvid->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
-       ppvid->ppvid = va_arg(ap, struct lldpd_ppvid *);
-       lldpctl_atom_inc_ref((lldpctl_atom_t *)ppvid->parent);
-       return 1;
-}
-
-static void
-_lldpctl_atom_free_ppvid(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_ppvid_t *ppvid =
-           (struct _lldpctl_atom_ppvid_t *)atom;
-       lldpctl_atom_dec_ref((lldpctl_atom_t *)ppvid->parent);
-}
-
-static long int
-_lldpctl_atom_get_int_ppvid(lldpctl_atom_t *atom, lldpctl_key_t key)
-{
-       struct _lldpctl_atom_ppvid_t *m =
-           (struct _lldpctl_atom_ppvid_t *)atom;
-
-       /* Local and remote port */
-       switch (key) {
-       case lldpctl_k_ppvid_id:
-               return m->ppvid->p_ppvid;
-       case lldpctl_k_ppvid_status:
-               return m->ppvid->p_cap_status;
-       default:
-               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-       }
-}
-
-static lldpctl_atom_iter_t*
-_lldpctl_atom_iter_pis_list(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_any_list_t *vlist =
-           (struct _lldpctl_atom_any_list_t *)atom;
-       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&vlist->parent->port->p_pids);
-}
-
-static lldpctl_atom_iter_t*
-_lldpctl_atom_next_pis_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
-{
-       struct lldpd_pi *pi = (struct lldpd_pi *)iter;
-       return (lldpctl_atom_iter_t*)TAILQ_NEXT(pi, p_entries);
-}
-
-static lldpctl_atom_t*
-_lldpctl_atom_value_pis_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_pi *pi = (struct lldpd_pi *)iter;
-       return _lldpctl_new_atom(atom->conn, atom_pi, vlist->parent, pi);
-}
-
-static int
-_lldpctl_atom_new_pi(lldpctl_atom_t *atom, va_list ap)
-{
-       struct _lldpctl_atom_pi_t *pi =
-           (struct _lldpctl_atom_pi_t *)atom;
-       pi->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
-       pi->pi = va_arg(ap, struct lldpd_pi *);
-       lldpctl_atom_inc_ref((lldpctl_atom_t *)pi->parent);
-       return 1;
-}
-
-static void
-_lldpctl_atom_free_pi(lldpctl_atom_t *atom)
-{
-       struct _lldpctl_atom_pi_t *pi =
-           (struct _lldpctl_atom_pi_t *)atom;
-       lldpctl_atom_dec_ref((lldpctl_atom_t *)pi->parent);
-}
-
-static const uint8_t*
-_lldpctl_atom_get_buf_pi(lldpctl_atom_t *atom, lldpctl_key_t key, size_t *n)
-{
-       struct _lldpctl_atom_pi_t *m =
-           (struct _lldpctl_atom_pi_t *)atom;
-
-       /* Local and remote port */
-       switch (key) {
-       case lldpctl_k_pi_id:
-               *n = m->pi->p_pi_len;
-               return (const uint8_t*)m->pi->p_pi;
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-}
-#endif
-
-#ifdef ENABLE_LLDPMED
-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->hardware == NULL) {
-               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, m->policy->type);
-       case lldpctl_k_med_policy_priority:
-               return map_lookup(port_med_policy_prio_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, value));
-       case lldpctl_k_med_policy_priority:
-               return _lldpctl_atom_set_int_med_policy(atom, key,
-                   map_reverse_lookup(port_med_policy_prio_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->hardware == NULL) {
-               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);
-       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,
-                   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->hardware == NULL) {
-               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, 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->hardware == NULL) {
-               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 = plist->parent->location->data_len - 4;
-       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->hardware == NULL) {
-               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, 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->hardware == NULL) {
-               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, 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,
-                   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->hardware == NULL) {
-               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, value));
-       default:
-               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
-               return NULL;
-       }
-}
-#endif
-
-struct atom_builder builders[] = {
-       { atom_config, sizeof(struct _lldpctl_atom_config_t),
-         .init = _lldpctl_atom_new_config,
-         .free = _lldpctl_atom_free_config,
-         .get_str = _lldpctl_atom_get_str_config,
-         .set_str = _lldpctl_atom_set_str_config,
-         .get_int = _lldpctl_atom_get_int_config,
-         .set_int = _lldpctl_atom_set_int_config },
-       { atom_interfaces_list, sizeof(struct _lldpctl_atom_interfaces_list_t),
-         .init  = _lldpctl_atom_new_interfaces_list,
-         .free  = _lldpctl_atom_free_interfaces_list,
-         .iter  = _lldpctl_atom_iter_interfaces_list,
-         .next  = _lldpctl_atom_next_interfaces_list,
-         .value = _lldpctl_atom_value_interfaces_list },
-       { atom_interface, sizeof(struct _lldpctl_atom_interface_t),
-         .init = _lldpctl_atom_new_interface,
-         .free = _lldpctl_atom_free_interface,
-         .get_str = _lldpctl_atom_get_str_interface },
-       { atom_ports_list, sizeof(struct _lldpctl_atom_any_list_t),
-         .init = _lldpctl_atom_new_any_list,
-         .free = _lldpctl_atom_free_any_list,
-         .iter = _lldpctl_atom_iter_ports_list,
-         .next = _lldpctl_atom_next_ports_list,
-         .value = _lldpctl_atom_value_ports_list },
-       { atom_port, sizeof(struct _lldpctl_atom_port_t),
-         .init = _lldpctl_atom_new_port,
-         .free = _lldpctl_atom_free_port,
-         .get  = _lldpctl_atom_get_atom_port,
-         .set  = _lldpctl_atom_set_atom_port,
-         .get_str = _lldpctl_atom_get_str_port,
-         .set_str = _lldpctl_atom_set_str_port,
-         .get_int = _lldpctl_atom_get_int_port,
-         .get_buffer = _lldpctl_atom_get_buf_port },
-       { atom_mgmts_list, sizeof(struct _lldpctl_atom_mgmts_list_t),
-         .init = _lldpctl_atom_new_mgmts_list,
-         .free = _lldpctl_atom_free_mgmts_list,
-         .iter = _lldpctl_atom_iter_mgmts_list,
-         .next = _lldpctl_atom_next_mgmts_list,
-         .value = _lldpctl_atom_value_mgmts_list },
-       { atom_mgmt, sizeof(struct _lldpctl_atom_mgmt_t),
-         .init = _lldpctl_atom_new_mgmt,
-         .free = _lldpctl_atom_free_mgmt,
-         .get_str = _lldpctl_atom_get_str_mgmt },
-#ifdef ENABLE_DOT3
-       { atom_dot3_power, sizeof(struct _lldpctl_atom_dot3_power_t),
-         .init = _lldpctl_atom_new_dot3_power,
-         .free = _lldpctl_atom_free_dot3_power,
-         .get_int = _lldpctl_atom_get_int_dot3_power,
-         .set_int = _lldpctl_atom_set_int_dot3_power,
-         .get_str = _lldpctl_atom_get_str_dot3_power,
-         .set_str = _lldpctl_atom_set_str_dot3_power },
-#endif
-#ifdef ENABLE_DOT1
-       { atom_vlans_list, sizeof(struct _lldpctl_atom_any_list_t),
-         .init = _lldpctl_atom_new_any_list,
-         .free = _lldpctl_atom_free_any_list,
-         .iter = _lldpctl_atom_iter_vlans_list,
-         .next = _lldpctl_atom_next_vlans_list,
-         .value = _lldpctl_atom_value_vlans_list },
-       { atom_vlan, sizeof(struct _lldpctl_atom_vlan_t),
-         .init = _lldpctl_atom_new_vlan,
-         .free = _lldpctl_atom_free_vlan,
-         .get_str = _lldpctl_atom_get_str_vlan,
-         .get_int = _lldpctl_atom_get_int_vlan },
-       { atom_ppvids_list, sizeof(struct _lldpctl_atom_any_list_t),
-         .init = _lldpctl_atom_new_any_list,
-         .free = _lldpctl_atom_free_any_list,
-         .iter = _lldpctl_atom_iter_ppvids_list,
-         .next = _lldpctl_atom_next_ppvids_list,
-         .value = _lldpctl_atom_value_ppvids_list },
-       { atom_ppvid, sizeof(struct _lldpctl_atom_ppvid_t),
-         .init = _lldpctl_atom_new_ppvid,
-         .free = _lldpctl_atom_free_ppvid,
-         .get_int = _lldpctl_atom_get_int_ppvid },
-       { atom_pis_list, sizeof(struct _lldpctl_atom_any_list_t),
-         .init = _lldpctl_atom_new_any_list,
-         .free = _lldpctl_atom_free_any_list,
-         .iter = _lldpctl_atom_iter_pis_list,
-         .next = _lldpctl_atom_next_pis_list,
-         .value = _lldpctl_atom_value_pis_list },
-       { atom_pi, sizeof(struct _lldpctl_atom_pi_t),
-         .init = _lldpctl_atom_new_pi,
-         .free = _lldpctl_atom_free_pi,
-         .get_buffer = _lldpctl_atom_get_buf_pi },
-#endif
-#ifdef ENABLE_LLDPMED
-       { 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 },
-       { 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 },
-       { 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 },
-       { 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 },
-       { 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 },
-       { 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 },
-       { 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 },
-#endif
-       { 0, 0, .init = NULL, .free = NULL }
-};
-
 lldpctl_atom_t*
 _lldpctl_new_atom(lldpctl_conn_t *conn, atom_t type, ...)
 {
        struct atom_builder *builder;
        struct lldpctl_atom_t *atom;
        va_list(ap);
-       for (builder = builders; builder->size > 0; builder++) {
+       for (builder = atom_builder_list.nextb; builder ; builder = builder->nextb) {
                if (builder->type != type) continue;
                atom = calloc(1, builder->size);
                if (atom == NULL) {
diff --git a/src/lib/atoms/config.c b/src/lib/atoms/config.c
new file mode 100644 (file)
index 0000000..b85bb98
--- /dev/null
@@ -0,0 +1,293 @@
+/* -*- 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 "../lldpd-structs.h"
+#include "../log.h"
+#include "private.h"
+#include "helpers.h"
+
+static struct atom_map bond_slave_src_mac_map = {
+       .key = lldpctl_k_config_bond_slave_src_mac_type,
+       .map = {
+               { LLDP_BOND_SLAVE_SRC_MAC_TYPE_REAL,   "real"},
+               { LLDP_BOND_SLAVE_SRC_MAC_TYPE_ZERO,   "zero"},
+               { LLDP_BOND_SLAVE_SRC_MAC_TYPE_FIXED,  "fixed"},
+               { LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED, "local" },
+               { LLDP_BOND_SLAVE_SRC_MAC_TYPE_UNKNOWN, NULL},
+       },
+};
+
+static struct atom_map lldp_portid_map = {
+       .key = lldpctl_k_config_lldp_portid_type,
+       .map = {
+               { LLDP_PORTID_SUBTYPE_IFNAME,   "ifname"},
+               { LLDP_PORTID_SUBTYPE_LLADDR,   "macaddress"},
+               { LLDP_PORTID_SUBTYPE_LOCAL,    "local"},
+               { LLDP_PORTID_SUBTYPE_UNKNOWN,  NULL},
+       },
+};
+
+ATOM_MAP_REGISTER(bond_slave_src_mac_map, 1);
+ATOM_MAP_REGISTER(lldp_portid_map,        2);
+
+static int
+_lldpctl_atom_new_config(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_config_t *c =
+           (struct _lldpctl_atom_config_t *)atom;
+       c->config = va_arg(ap, struct lldpd_config *);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_config(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_config_t *c =
+           (struct _lldpctl_atom_config_t *)atom;
+       lldpd_config_cleanup(c->config);
+       free(c->config);
+}
+
+static const char*
+_lldpctl_atom_get_str_config(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       char *res = NULL;
+       struct _lldpctl_atom_config_t *c =
+           (struct _lldpctl_atom_config_t *)atom;
+       switch (key) {
+       case lldpctl_k_config_mgmt_pattern:
+               res = c->config->c_mgmt_pattern; break;
+       case lldpctl_k_config_iface_pattern:
+               res = c->config->c_iface_pattern; break;
+       case lldpctl_k_config_cid_pattern:
+               res = c->config->c_cid_pattern; break;
+       case lldpctl_k_config_description:
+               res = c->config->c_description; break;
+       case lldpctl_k_config_platform:
+               res = c->config->c_platform; break;
+       case lldpctl_k_config_hostname:
+               res = c->config->c_hostname; break;
+       case lldpctl_k_config_bond_slave_src_mac_type:
+               return map_lookup(bond_slave_src_mac_map.map,
+                               c->config->c_bond_slave_src_mac_type);
+       case lldpctl_k_config_lldp_portid_type:
+               return map_lookup(lldp_portid_map.map,
+                   c->config->c_lldp_portid_type);
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+       return res?res:"";
+}
+
+static struct _lldpctl_atom_config_t*
+__lldpctl_atom_set_str_config(struct _lldpctl_atom_config_t *c,
+    char **local, char **global,
+    const char *value) {
+       if (value) {
+               char *aval = NULL;
+               size_t len = strlen(value) + 1;
+               aval = _lldpctl_alloc_in_atom((lldpctl_atom_t *)c, len);
+               if (!aval) return NULL;
+               memcpy(aval, value, len);
+               *local = aval;
+               free(*global); *global = strdup(aval);
+       } else {
+               free(*global);
+               *local = *global = NULL;
+       }
+       return c;
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_str_config(lldpctl_atom_t *atom, lldpctl_key_t key,
+    const char *value)
+{
+       struct _lldpctl_atom_config_t *c =
+           (struct _lldpctl_atom_config_t *)atom;
+       struct lldpd_config config;
+       memcpy(&config, c->config, sizeof(struct lldpd_config));
+       char *canary = NULL;
+       int rc;
+
+       switch (key) {
+       case lldpctl_k_config_iface_pattern:
+               if (!__lldpctl_atom_set_str_config(c,
+                       &config.c_iface_pattern, &c->config->c_iface_pattern,
+                       value))
+                       return NULL;
+               break;
+       case lldpctl_k_config_mgmt_pattern:
+               if (!__lldpctl_atom_set_str_config(c,
+                       &config.c_mgmt_pattern, &c->config->c_mgmt_pattern,
+                       value))
+                       return NULL;
+               break;
+       case lldpctl_k_config_description:
+               if (!__lldpctl_atom_set_str_config(c,
+                       &config.c_description, &c->config->c_description,
+                       value))
+                       return NULL;
+               break;
+       case lldpctl_k_config_platform:
+               if (!__lldpctl_atom_set_str_config(c,
+                       &config.c_platform, &c->config->c_platform,
+                       value))
+                       return NULL;
+               break;
+       case lldpctl_k_config_hostname:
+               if (!__lldpctl_atom_set_str_config(c,
+                       &config.c_hostname, &c->config->c_hostname,
+                       value))
+                       return NULL;
+               break;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       if (asprintf(&canary, "%d%s", key, value?value:"(NULL)") == -1) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
+               return NULL;
+       }
+       rc = _lldpctl_do_something(atom->conn,
+           CONN_STATE_SET_CONFIG_SEND, CONN_STATE_SET_CONFIG_RECV,
+           canary,
+           SET_CONFIG, &config, &MARSHAL_INFO(lldpd_config),
+           NULL, NULL);
+       free(canary);
+       if (rc == 0) return atom;
+
+#undef SET_STR
+
+       return NULL;
+}
+
+static long int
+_lldpctl_atom_get_int_config(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_config_t *c =
+           (struct _lldpctl_atom_config_t *)atom;
+       switch (key) {
+       case lldpctl_k_config_paused:
+               return c->config->c_paused;
+       case lldpctl_k_config_tx_interval:
+               return c->config->c_tx_interval;
+       case lldpctl_k_config_receiveonly:
+               return c->config->c_receiveonly;
+       case lldpctl_k_config_advertise_version:
+               return c->config->c_advertise_version;
+       case lldpctl_k_config_ifdescr_update:
+               return c->config->c_set_ifdescr;
+       case lldpctl_k_config_iface_promisc:
+               return c->config->c_promisc;
+#ifdef ENABLE_LLDPMED
+       case lldpctl_k_config_lldpmed_noinventory:
+               return c->config->c_noinventory;
+       case lldpctl_k_config_fast_start_enabled:
+               return c->config->c_enable_fast_start;
+       case lldpctl_k_config_fast_start_interval:
+               return c->config->c_tx_fast_interval;
+#endif
+       case lldpctl_k_config_tx_hold:
+               return c->config->c_tx_hold;
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_int_config(lldpctl_atom_t *atom, lldpctl_key_t key,
+    long int value)
+{
+       int rc;
+       char *canary = NULL;
+       struct _lldpctl_atom_config_t *c =
+           (struct _lldpctl_atom_config_t *)atom;
+       struct lldpd_config config;
+       memcpy(&config, c->config, sizeof(struct lldpd_config));
+
+       switch (key) {
+       case lldpctl_k_config_paused:
+               config.c_paused = c->config->c_paused = value;
+               break;
+       case lldpctl_k_config_tx_interval:
+               config.c_tx_interval = value;
+               if (value > 0) c->config->c_tx_interval = value;
+               break;
+       case lldpctl_k_config_ifdescr_update:
+               config.c_set_ifdescr = c->config->c_set_ifdescr = value;
+               break;
+       case lldpctl_k_config_iface_promisc:
+               config.c_promisc = c->config->c_promisc = value;
+               break;
+#ifdef ENABLE_LLDPMED
+       case lldpctl_k_config_fast_start_enabled:
+               config.c_enable_fast_start = c->config->c_enable_fast_start = value;
+               break;
+       case lldpctl_k_config_fast_start_interval:
+               config.c_tx_fast_interval = c->config->c_tx_fast_interval = value;
+               break;
+#endif
+       case lldpctl_k_config_tx_hold:
+               config.c_tx_hold = value;
+               if (value > 0) c->config->c_tx_hold = value;
+               break;
+       case lldpctl_k_config_bond_slave_src_mac_type:
+               config.c_bond_slave_src_mac_type = value;
+               c->config->c_bond_slave_src_mac_type = value;
+               break;
+       case lldpctl_k_config_lldp_portid_type:
+               config.c_lldp_portid_type = value;
+               c->config->c_lldp_portid_type = value;
+               break;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       if (asprintf(&canary, "%d%ld", key, value) == -1) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
+               return NULL;
+       }
+       rc = _lldpctl_do_something(atom->conn,
+           CONN_STATE_SET_CONFIG_SEND, CONN_STATE_SET_CONFIG_RECV,
+           canary,
+           SET_CONFIG, &config, &MARSHAL_INFO(lldpd_config),
+           NULL, NULL);
+       free(canary);
+       if (rc == 0) return atom;
+       return NULL;
+}
+
+static struct atom_builder config =
+       { atom_config, sizeof(struct _lldpctl_atom_config_t),
+         .init = _lldpctl_atom_new_config,
+         .free = _lldpctl_atom_free_config,
+         .get_str = _lldpctl_atom_get_str_config,
+         .set_str = _lldpctl_atom_set_str_config,
+         .get_int = _lldpctl_atom_get_int_config,
+         .set_int = _lldpctl_atom_set_int_config };
+
+ATOM_BUILDER_REGISTER(config, 1);
+
diff --git a/src/lib/atoms/dot1.c b/src/lib/atoms/dot1.c
new file mode 100644 (file)
index 0000000..45f2ad8
--- /dev/null
@@ -0,0 +1,276 @@
+/* -*- 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 "../lldpd-structs.h"
+#include "../log.h"
+#include "private.h"
+#include "helpers.h"
+
+#ifdef ENABLE_DOT1
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_vlans_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&vlist->parent->port->p_vlans);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_vlans_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_vlan *vlan = (struct lldpd_vlan *)iter;
+       return (lldpctl_atom_iter_t*)TAILQ_NEXT(vlan, v_entries);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_vlans_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_vlan *vlan = (struct lldpd_vlan *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_vlan, vlist->parent, vlan);
+}
+
+static int
+_lldpctl_atom_new_vlan(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_vlan_t *vlan =
+           (struct _lldpctl_atom_vlan_t *)atom;
+       vlan->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       vlan->vlan = va_arg(ap, struct lldpd_vlan *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)vlan->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_vlan(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_vlan_t *vlan =
+           (struct _lldpctl_atom_vlan_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)vlan->parent);
+}
+
+static const char*
+_lldpctl_atom_get_str_vlan(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_vlan_t *m =
+           (struct _lldpctl_atom_vlan_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_vlan_name:
+               return m->vlan->v_name;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static long int
+_lldpctl_atom_get_int_vlan(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_vlan_t *m =
+           (struct _lldpctl_atom_vlan_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_vlan_id:
+               return m->vlan->v_vid;
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_ppvids_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&vlist->parent->port->p_ppvids);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_ppvids_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_ppvid *ppvid = (struct lldpd_ppvid *)iter;
+       return (lldpctl_atom_iter_t*)TAILQ_NEXT(ppvid, p_entries);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_ppvids_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_ppvid *ppvid = (struct lldpd_ppvid *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_ppvid, vlist->parent, ppvid);
+}
+
+static int
+_lldpctl_atom_new_ppvid(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_ppvid_t *ppvid =
+           (struct _lldpctl_atom_ppvid_t *)atom;
+       ppvid->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       ppvid->ppvid = va_arg(ap, struct lldpd_ppvid *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)ppvid->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_ppvid(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_ppvid_t *ppvid =
+           (struct _lldpctl_atom_ppvid_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)ppvid->parent);
+}
+
+static long int
+_lldpctl_atom_get_int_ppvid(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_ppvid_t *m =
+           (struct _lldpctl_atom_ppvid_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_ppvid_id:
+               return m->ppvid->p_ppvid;
+       case lldpctl_k_ppvid_status:
+               return m->ppvid->p_cap_status;
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_pis_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_any_list_t *vlist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&vlist->parent->port->p_pids);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_pis_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_pi *pi = (struct lldpd_pi *)iter;
+       return (lldpctl_atom_iter_t*)TAILQ_NEXT(pi, p_entries);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_pis_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_pi *pi = (struct lldpd_pi *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_pi, vlist->parent, pi);
+}
+
+static int
+_lldpctl_atom_new_pi(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_pi_t *pi =
+           (struct _lldpctl_atom_pi_t *)atom;
+       pi->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       pi->pi = va_arg(ap, struct lldpd_pi *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)pi->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_pi(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_pi_t *pi =
+           (struct _lldpctl_atom_pi_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)pi->parent);
+}
+
+static const uint8_t*
+_lldpctl_atom_get_buf_pi(lldpctl_atom_t *atom, lldpctl_key_t key, size_t *n)
+{
+       struct _lldpctl_atom_pi_t *m =
+           (struct _lldpctl_atom_pi_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_pi_id:
+               *n = m->pi->p_pi_len;
+               return (const uint8_t*)m->pi->p_pi;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static struct atom_builder vlans_list =
+       { atom_vlans_list, sizeof(struct _lldpctl_atom_any_list_t),
+         .init = _lldpctl_atom_new_any_list,
+         .free = _lldpctl_atom_free_any_list,
+         .iter = _lldpctl_atom_iter_vlans_list,
+         .next = _lldpctl_atom_next_vlans_list,
+         .value = _lldpctl_atom_value_vlans_list };
+
+static struct atom_builder vlan =
+       { atom_vlan, sizeof(struct _lldpctl_atom_vlan_t),
+         .init = _lldpctl_atom_new_vlan,
+         .free = _lldpctl_atom_free_vlan,
+         .get_str = _lldpctl_atom_get_str_vlan,
+         .get_int = _lldpctl_atom_get_int_vlan };
+
+static struct atom_builder ppvids_list =
+       { atom_ppvids_list, sizeof(struct _lldpctl_atom_any_list_t),
+         .init = _lldpctl_atom_new_any_list,
+         .free = _lldpctl_atom_free_any_list,
+         .iter = _lldpctl_atom_iter_ppvids_list,
+         .next = _lldpctl_atom_next_ppvids_list,
+         .value = _lldpctl_atom_value_ppvids_list };
+
+static struct atom_builder ppvid =
+       { atom_ppvid, sizeof(struct _lldpctl_atom_ppvid_t),
+         .init = _lldpctl_atom_new_ppvid,
+         .free = _lldpctl_atom_free_ppvid,
+         .get_int = _lldpctl_atom_get_int_ppvid };
+
+static struct atom_builder pis_list =
+       { atom_pis_list, sizeof(struct _lldpctl_atom_any_list_t),
+         .init = _lldpctl_atom_new_any_list,
+         .free = _lldpctl_atom_free_any_list,
+         .iter = _lldpctl_atom_iter_pis_list,
+         .next = _lldpctl_atom_next_pis_list,
+         .value = _lldpctl_atom_value_pis_list };
+
+static struct atom_builder pi =
+       { atom_pi, sizeof(struct _lldpctl_atom_pi_t),
+         .init = _lldpctl_atom_new_pi,
+         .free = _lldpctl_atom_free_pi,
+         .get_buffer = _lldpctl_atom_get_buf_pi };
+
+ATOM_BUILDER_REGISTER(vlans_list,   9);
+ATOM_BUILDER_REGISTER(vlan,        10);
+ATOM_BUILDER_REGISTER(ppvids_list, 11);
+ATOM_BUILDER_REGISTER(ppvid,       12);
+ATOM_BUILDER_REGISTER(pis_list,    13);
+ATOM_BUILDER_REGISTER(pi,          14);
+
+#endif
+
diff --git a/src/lib/atoms/dot3.c b/src/lib/atoms/dot3.c
new file mode 100644 (file)
index 0000000..be2829d
--- /dev/null
@@ -0,0 +1,310 @@
+/* -*- 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 "../lldpd-structs.h"
+#include "../log.h"
+#include "private.h"
+#include "helpers.h"
+
+#ifdef ENABLE_DOT3
+
+static lldpctl_map_t port_dot3_power_devicetype_map[] = {
+       { LLDP_DOT3_POWER_PSE, "PSE" },
+       { LLDP_DOT3_POWER_PD,  "PD" },
+       { 0, NULL }
+};
+
+static lldpctl_map_t port_dot3_power_pse_source_map[] = {
+       { LLDP_DOT3_POWER_SOURCE_BOTH, "PSE + Local" },
+       { LLDP_DOT3_POWER_SOURCE_PSE, "PSE" },
+       { 0, NULL }
+};
+
+static lldpctl_map_t port_dot3_power_pd_source_map[] = {
+       { LLDP_DOT3_POWER_SOURCE_BACKUP, "Backup source" },
+       { LLDP_DOT3_POWER_SOURCE_PRIMARY, "Primary power source" },
+       { 0, NULL }
+};
+
+static struct atom_map port_dot3_power_pairs_map = {
+       .key = lldpctl_k_dot3_power_pairs,
+       .map = {
+               { LLDP_DOT3_POWERPAIRS_SIGNAL, "signal" },
+               { LLDP_DOT3_POWERPAIRS_SPARE,  "spare" },
+               { 0, NULL }
+       },
+};
+
+static struct atom_map port_dot3_power_class_map = {
+       .key = lldpctl_k_dot3_power_class,
+       .map = {
+               { 1, "class 0" },
+               { 2, "class 1" },
+               { 3, "class 2" },
+               { 4, "class 3" },
+               { 5, "class 4" },
+               { 0, NULL }
+       },
+};
+
+static struct atom_map port_dot3_power_priority_map = {
+       .key = lldpctl_k_dot3_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_dot3_power_pairs_map,    4);
+ATOM_MAP_REGISTER(port_dot3_power_class_map,    5);
+ATOM_MAP_REGISTER(port_dot3_power_priority_map, 6);
+
+static int
+_lldpctl_atom_new_dot3_power(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_dot3_power_t *dpow =
+           (struct _lldpctl_atom_dot3_power_t *)atom;
+       dpow->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)dpow->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_dot3_power(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_dot3_power_t *dpow =
+           (struct _lldpctl_atom_dot3_power_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)dpow->parent);
+}
+
+static const char*
+_lldpctl_atom_get_str_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_dot3_power_t *dpow =
+           (struct _lldpctl_atom_dot3_power_t *)atom;
+       struct lldpd_port     *port     = dpow->parent->port;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_dot3_power_devicetype:
+               return map_lookup(port_dot3_power_devicetype_map,
+                   port->p_power.devicetype);
+       case lldpctl_k_dot3_power_pairs:
+               return map_lookup(port_dot3_power_pairs_map.map,
+                   port->p_power.pairs);
+       case lldpctl_k_dot3_power_class:
+               return map_lookup(port_dot3_power_class_map.map,
+                   port->p_power.class);
+       case lldpctl_k_dot3_power_source:
+               return map_lookup((port->p_power.devicetype == LLDP_DOT3_POWER_PSE)?
+                   port_dot3_power_pse_source_map:
+                   port_dot3_power_pd_source_map,
+                   port->p_power.source);
+       case lldpctl_k_dot3_power_priority:
+               return map_lookup(port_dot3_power_priority_map.map,
+                   port->p_power.priority);
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static long int
+_lldpctl_atom_get_int_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_dot3_power_t *dpow =
+           (struct _lldpctl_atom_dot3_power_t *)atom;
+       struct lldpd_port     *port     = dpow->parent->port;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_dot3_power_devicetype:
+               return port->p_power.devicetype;
+       case lldpctl_k_dot3_power_supported:
+               return port->p_power.supported;
+       case lldpctl_k_dot3_power_enabled:
+               return port->p_power.enabled;
+       case lldpctl_k_dot3_power_paircontrol:
+               return port->p_power.paircontrol;
+       case lldpctl_k_dot3_power_pairs:
+               return port->p_power.pairs;
+       case lldpctl_k_dot3_power_class:
+               return port->p_power.class;
+       case lldpctl_k_dot3_power_type:
+               return port->p_power.powertype;
+       case lldpctl_k_dot3_power_source:
+               return port->p_power.source;
+       case lldpctl_k_dot3_power_priority:
+               return port->p_power.priority;
+       case lldpctl_k_dot3_power_requested:
+               return port->p_power.requested * 100;
+       case lldpctl_k_dot3_power_allocated:
+               return port->p_power.allocated * 100;
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_int_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key,
+    long int value)
+{
+       struct _lldpctl_atom_dot3_power_t *dpow =
+           (struct _lldpctl_atom_dot3_power_t *)atom;
+       struct lldpd_port *port = dpow->parent->port;
+
+       /* Only local port can be modified */
+       if (dpow->parent->hardware == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       switch (key) {
+       case lldpctl_k_dot3_power_devicetype:
+               switch (value) {
+               case 0:         /* Disabling */
+               case LLDP_DOT3_POWER_PSE:
+               case LLDP_DOT3_POWER_PD:
+                       port->p_power.devicetype = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_supported:
+               switch (value) {
+               case 0:
+               case 1:
+                       port->p_power.supported = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_enabled:
+               switch (value) {
+               case 0:
+               case 1:
+                       port->p_power.enabled = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_paircontrol:
+               switch (value) {
+               case 0:
+               case 1:
+                       port->p_power.paircontrol = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_pairs:
+               switch (value) {
+               case 1:
+               case 2:
+                       port->p_power.pairs = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_class:
+               if (value < 0 || value > 5)
+                       goto bad;
+               port->p_power.class = value;
+               return atom;
+       case lldpctl_k_dot3_power_type:
+               switch (value) {
+               case LLDP_DOT3_POWER_8023AT_TYPE1:
+               case LLDP_DOT3_POWER_8023AT_TYPE2:
+               case LLDP_DOT3_POWER_8023AT_OFF:
+                       port->p_power.powertype = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_source:
+               if (value < 0 || value > 3)
+                       goto bad;
+               port->p_power.source = value;
+               return atom;
+       case lldpctl_k_dot3_power_priority:
+               switch (value) {
+               case LLDP_DOT3_POWER_PRIO_UNKNOWN:
+               case LLDP_DOT3_POWER_PRIO_CRITICAL:
+               case LLDP_DOT3_POWER_PRIO_HIGH:
+               case LLDP_DOT3_POWER_PRIO_LOW:
+                       port->p_power.priority = value;
+                       return atom;
+               default: goto bad;
+               }
+       case lldpctl_k_dot3_power_allocated:
+               if (value < 0) goto bad;
+               port->p_power.allocated = value / 100;
+               return atom;
+       case lldpctl_k_dot3_power_requested:
+               if (value < 0) goto bad;
+               port->p_power.requested = 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_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key,
+    const char *value)
+{
+       switch (key) {
+       case lldpctl_k_dot3_power_devicetype:
+               return _lldpctl_atom_set_int_dot3_power(atom, key,
+                   map_reverse_lookup(port_dot3_power_devicetype_map, value));
+       case lldpctl_k_dot3_power_pairs:
+               return _lldpctl_atom_set_int_dot3_power(atom, key,
+                   map_reverse_lookup(port_dot3_power_pairs_map.map, value));
+       case lldpctl_k_dot3_power_class:
+               return _lldpctl_atom_set_int_dot3_power(atom, key,
+                   map_reverse_lookup(port_dot3_power_class_map.map, value));
+       case lldpctl_k_dot3_power_priority:
+               return _lldpctl_atom_set_int_dot3_power(atom, key,
+                   map_reverse_lookup(port_dot3_power_priority_map.map, value));
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static struct atom_builder dot3_power =
+       { atom_dot3_power, sizeof(struct _lldpctl_atom_dot3_power_t),
+         .init = _lldpctl_atom_new_dot3_power,
+         .free = _lldpctl_atom_free_dot3_power,
+         .get_int = _lldpctl_atom_get_int_dot3_power,
+         .set_int = _lldpctl_atom_set_int_dot3_power,
+         .get_str = _lldpctl_atom_get_str_dot3_power,
+         .set_str = _lldpctl_atom_set_str_dot3_power };
+
+ATOM_BUILDER_REGISTER(dot3_power, 8);
+
+#endif
+
diff --git a/src/lib/atoms/interface.c b/src/lib/atoms/interface.c
new file mode 100644 (file)
index 0000000..3415f49
--- /dev/null
@@ -0,0 +1,123 @@
+/* -*- 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 "../lldpd-structs.h"
+#include "../log.h"
+#include "private.h"
+#include "helpers.h"
+
+static int
+_lldpctl_atom_new_interfaces_list(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_interfaces_list_t *iflist =
+           (struct _lldpctl_atom_interfaces_list_t *)atom;
+       iflist->ifs = va_arg(ap, struct lldpd_interface_list *);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_interfaces_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_interfaces_list_t *iflist =
+           (struct _lldpctl_atom_interfaces_list_t *)atom;
+       struct lldpd_interface *iface, *iface_next;
+       for (iface = TAILQ_FIRST(iflist->ifs);
+            iface != NULL;
+            iface = iface_next) {
+               /* Don't TAILQ_REMOVE, this is not a real list! */
+               iface_next = TAILQ_NEXT(iface, next);
+               free(iface->name);
+               free(iface);
+       }
+       free(iflist->ifs);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_interfaces_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_interfaces_list_t *iflist =
+           (struct _lldpctl_atom_interfaces_list_t *)atom;
+       return (lldpctl_atom_iter_t*)TAILQ_FIRST(iflist->ifs);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_interfaces_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       return (lldpctl_atom_iter_t*)TAILQ_NEXT((struct lldpd_interface *)iter, next);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_interfaces_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_interface *iface = (struct lldpd_interface *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_interface, iface->name);
+}
+
+static int
+_lldpctl_atom_new_interface(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_interface_t *port =
+           (struct _lldpctl_atom_interface_t *)atom;
+       port->name = strdup(va_arg(ap, char *));
+       return (port->name != NULL);
+}
+
+static void
+_lldpctl_atom_free_interface(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_interface_t *port =
+           (struct _lldpctl_atom_interface_t *)atom;
+       free(port->name);
+}
+
+static const char*
+_lldpctl_atom_get_str_interface(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_interface_t *port =
+           (struct _lldpctl_atom_interface_t *)atom;
+       switch (key) {
+       case lldpctl_k_interface_name:
+               return port->name;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static struct atom_builder interfaces_list =
+       { atom_interfaces_list, sizeof(struct _lldpctl_atom_interfaces_list_t),
+         .init  = _lldpctl_atom_new_interfaces_list,
+         .free  = _lldpctl_atom_free_interfaces_list,
+         .iter  = _lldpctl_atom_iter_interfaces_list,
+         .next  = _lldpctl_atom_next_interfaces_list,
+         .value = _lldpctl_atom_value_interfaces_list };
+
+static struct atom_builder interface =
+       { atom_interface, sizeof(struct _lldpctl_atom_interface_t),
+         .init = _lldpctl_atom_new_interface,
+         .free = _lldpctl_atom_free_interface,
+         .get_str = _lldpctl_atom_get_str_interface };
+
+ATOM_BUILDER_REGISTER(interfaces_list, 2);
+ATOM_BUILDER_REGISTER(interface,       3);
+
diff --git a/src/lib/atoms/med.c b/src/lib/atoms/med.c
new file mode 100644 (file)
index 0000000..11a79b4
--- /dev/null
@@ -0,0 +1,1121 @@
+/* -*- 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 "../lldpd-structs.h"
+#include "../log.h"
+#include "private.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 = {
+               { 0, "Background" },
+               { 1, "Spare" },
+               { 2, "Best effort" },
+               { 3, "Excellent effort" },
+               { 4, "Controlled load" },
+               { 5, "Video" },
+               { 6, "Voice" },
+               { 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->hardware == NULL) {
+               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->hardware == NULL) {
+               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);
+       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->hardware == NULL) {
+               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->hardware == NULL) {
+               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 = plist->parent->location->data_len - 4;
+       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->hardware == NULL) {
+               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->hardware == NULL) {
+               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->hardware == NULL) {
+               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
+
diff --git a/src/lib/atoms/mgmt.c b/src/lib/atoms/mgmt.c
new file mode 100644 (file)
index 0000000..5e2aff7
--- /dev/null
@@ -0,0 +1,144 @@
+/* -*- 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 "../lldpd-structs.h"
+#include "../log.h"
+#include "private.h"
+#include "helpers.h"
+
+static int
+_lldpctl_atom_new_mgmts_list(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_mgmts_list_t *plist =
+           (struct _lldpctl_atom_mgmts_list_t *)atom;
+       plist->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       plist->chassis = va_arg(ap, struct lldpd_chassis *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)plist->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_mgmts_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_mgmts_list_t *plist =
+           (struct _lldpctl_atom_mgmts_list_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)plist->parent);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_mgmts_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_mgmts_list_t *plist =
+           (struct _lldpctl_atom_mgmts_list_t *)atom;
+       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&plist->chassis->c_mgmt);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_mgmts_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_mgmt *mgmt = (struct lldpd_mgmt *)iter;
+       return (lldpctl_atom_iter_t*)TAILQ_NEXT(mgmt, m_entries);
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_value_mgmts_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct _lldpctl_atom_mgmts_list_t *plist =
+           (struct _lldpctl_atom_mgmts_list_t *)atom;
+       struct lldpd_mgmt *mgmt = (struct lldpd_mgmt *)iter;
+       return _lldpctl_new_atom(atom->conn, atom_mgmt, plist->parent, mgmt);
+}
+
+static int
+_lldpctl_atom_new_mgmt(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_mgmt_t *mgmt =
+           (struct _lldpctl_atom_mgmt_t *)atom;
+       mgmt->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       mgmt->mgmt = va_arg(ap, struct lldpd_mgmt *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)mgmt->parent);
+       return 1;
+}
+
+static void
+_lldpctl_atom_free_mgmt(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_mgmt_t *mgmt =
+           (struct _lldpctl_atom_mgmt_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)mgmt->parent);
+}
+
+static const char*
+_lldpctl_atom_get_str_mgmt(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       char *ipaddress = NULL;
+       size_t len; int af;
+       struct _lldpctl_atom_mgmt_t *m =
+           (struct _lldpctl_atom_mgmt_t *)atom;
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_mgmt_ip:
+               switch (m->mgmt->m_family) {
+               case LLDPD_AF_IPV4:
+                       len = INET_ADDRSTRLEN + 1;
+                       af  = AF_INET;
+                       break;
+               case LLDPD_AF_IPV6:
+                       len = INET6_ADDRSTRLEN + 1;
+                       af = AF_INET6;
+                       break;
+               default:
+                       len = 0;
+               }
+               if (len == 0) break;
+               ipaddress = _lldpctl_alloc_in_atom(atom, len);
+               if (!ipaddress) return NULL;
+               if (inet_ntop(af, &m->mgmt->m_addr, ipaddress, len) == NULL)
+                       break;
+               return ipaddress;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+       SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       return NULL;
+}
+
+static struct atom_builder mgmts_list =
+       { atom_mgmts_list, sizeof(struct _lldpctl_atom_mgmts_list_t),
+         .init = _lldpctl_atom_new_mgmts_list,
+         .free = _lldpctl_atom_free_mgmts_list,
+         .iter = _lldpctl_atom_iter_mgmts_list,
+         .next = _lldpctl_atom_next_mgmts_list,
+         .value = _lldpctl_atom_value_mgmts_list };
+
+static struct atom_builder mgmt =
+       { atom_mgmt, sizeof(struct _lldpctl_atom_mgmt_t),
+         .init = _lldpctl_atom_new_mgmt,
+         .free = _lldpctl_atom_free_mgmt,
+         .get_str = _lldpctl_atom_get_str_mgmt };
+
+ATOM_BUILDER_REGISTER(mgmts_list, 6);
+ATOM_BUILDER_REGISTER(mgmt,       7);
+
diff --git a/src/lib/atoms/port.c b/src/lib/atoms/port.c
new file mode 100644 (file)
index 0000000..4d46d0b
--- /dev/null
@@ -0,0 +1,651 @@
+/* -*- 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 "../lldpd-structs.h"
+#include "../log.h"
+#include "private.h"
+#include "helpers.h"
+
+static struct atom_map lldpd_protocol_map = {
+       .key = lldpctl_k_port_protocol,
+       .map = {
+               { LLDPD_MODE_LLDP,      "LLDP" },
+               { LLDPD_MODE_CDPV1,     "CDPv1"},
+               { LLDPD_MODE_CDPV2,     "CDPv2"},
+               { LLDPD_MODE_EDP,       "EDP" },
+               { LLDPD_MODE_FDP,       "FDP"},
+               { LLDPD_MODE_SONMP,     "SONMP"},
+               { 0, NULL },
+       }
+};
+
+ATOM_MAP_REGISTER(lldpd_protocol_map, 3);
+
+static lldpctl_map_t port_id_subtype_map[] = {
+       { LLDP_PORTID_SUBTYPE_IFNAME,   "ifname"},
+       { LLDP_PORTID_SUBTYPE_IFALIAS,  "ifalias" },
+       { LLDP_PORTID_SUBTYPE_LOCAL,    "local" },
+       { LLDP_PORTID_SUBTYPE_LLADDR,   "mac" },
+       { LLDP_PORTID_SUBTYPE_ADDR,     "ip" },
+       { LLDP_PORTID_SUBTYPE_PORT,     "unhandled" },
+       { LLDP_PORTID_SUBTYPE_AGENTCID, "unhandled" },
+       { 0, NULL},
+};
+
+static lldpctl_map_t chassis_id_subtype_map[] = {
+       { LLDP_CHASSISID_SUBTYPE_IFNAME,  "ifname"},
+       { LLDP_CHASSISID_SUBTYPE_IFALIAS, "ifalias" },
+       { LLDP_CHASSISID_SUBTYPE_LOCAL,   "local" },
+       { LLDP_CHASSISID_SUBTYPE_LLADDR,  "mac" },
+       { LLDP_CHASSISID_SUBTYPE_ADDR,    "ip" },
+       { LLDP_CHASSISID_SUBTYPE_PORT,    "unhandled" },
+       { LLDP_CHASSISID_SUBTYPE_CHASSIS, "unhandled" },
+       { 0, NULL},
+};
+
+static lldpctl_map_t operational_mau_type_values[] = {
+       { 1,    "AUI - no internal MAU, view from AUI" },
+       { 2,    "10Base5 - thick coax MAU" },
+       { 3,    "Foirl - FOIRL MAU" },
+       { 4,    "10Base2 - thin coax MAU" },
+       { 5,    "10BaseT - UTP MAU" },
+       { 6,    "10BaseFP - passive fiber MAU" },
+       { 7,    "10BaseFB - sync fiber MAU" },
+       { 8,    "10BaseFL - async fiber MAU" },
+       { 9,    "10Broad36 - broadband DTE MAU" },
+       { 10,   "10BaseTHD - UTP MAU, half duplex mode" },
+       { 11,   "10BaseTFD - UTP MAU, full duplex mode" },
+       { 12,   "10BaseFLHD - async fiber MAU, half duplex mode" },
+       { 13,   "10BaseFLDF - async fiber MAU, full duplex mode" },
+       { 14,   "10BaseT4 - 4 pair category 3 UTP" },
+       { 15,   "100BaseTXHD - 2 pair category 5 UTP, half duplex mode" },
+       { 16,   "100BaseTXFD - 2 pair category 5 UTP, full duplex mode" },
+       { 17,   "100BaseFXHD - X fiber over PMT, half duplex mode" },
+       { 18,   "100BaseFXFD - X fiber over PMT, full duplex mode" },
+       { 19,   "100BaseT2HD - 2 pair category 3 UTP, half duplex mode" },
+       { 20,   "100BaseT2FD - 2 pair category 3 UTP, full duplex mode" },
+       { 21,   "1000BaseXHD - PCS/PMA, unknown PMD, half duplex mode" },
+       { 22,   "1000BaseXFD - PCS/PMA, unknown PMD, full duplex mode" },
+       { 23,   "1000BaseLXHD - Fiber over long-wavelength laser, half duplex mode" },
+       { 24,   "1000BaseLXFD - Fiber over long-wavelength laser, full duplex mode" },
+       { 25,   "1000BaseSXHD - Fiber over short-wavelength laser, half duplex mode" },
+       { 26,   "1000BaseSXFD - Fiber over short-wavelength laser, full duplex mode" },
+       { 27,   "1000BaseCXHD - Copper over 150-Ohm balanced cable, half duplex mode" },
+       { 28,   "1000BaseCXFD - Copper over 150-Ohm balanced cable, full duplex mode" },
+       { 29,   "1000BaseTHD - Four-pair Category 5 UTP, half duplex mode" },
+       { 30,   "1000BaseTFD - Four-pair Category 5 UTP, full duplex mode" },
+       { 31,   "10GigBaseX - X PCS/PMA, unknown PMD." },
+       { 32,   "10GigBaseLX4 - X fiber over WWDM optics" },
+       { 33,   "10GigBaseR - R PCS/PMA, unknown PMD." },
+       { 34,   "10GigBaseER - R fiber over 1550 nm optics" },
+       { 35,   "10GigBaseLR - R fiber over 1310 nm optics" },
+       { 36,   "10GigBaseSR - R fiber over 850 nm optics" },
+       { 37,   "10GigBaseW - W PCS/PMA, unknown PMD." },
+       { 38,   "10GigBaseEW - W fiber over 1550 nm optics" },
+       { 39,   "10GigBaseLW - W fiber over 1310 nm optics" },
+       { 40,   "10GigBaseSW - W fiber over 850 nm optics" },
+       { 41,   "10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable" },
+       { 42,   "2BaseTL - Voice grade UTP copper, up to 2700m, optional PAF" },
+       { 43,   "10PassTS - Voice grade UTP copper, up to 750m, optional PAF" },
+       { 44,   "100BaseBX10D - One single-mode fiber OLT, long wavelength, 10km" },
+       { 45,   "100BaseBX10U - One single-mode fiber ONU, long wavelength, 10km" },
+       { 46,   "100BaseLX10 - Two single-mode fibers, long wavelength, 10km" },
+       { 47,   "1000BaseBX10D - One single-mode fiber OLT, long wavelength, 10km" },
+       { 48,   "1000BaseBX10U - One single-mode fiber ONU, long wavelength, 10km" },
+       { 49,   "1000BaseLX10 - Two sigle-mode fiber, long wavelength, 10km" },
+       { 50,   "1000BasePX10D - One single-mode fiber EPON OLT, 10km" },
+       { 51,   "1000BasePX10U - One single-mode fiber EPON ONU, 10km" },
+       { 52,   "1000BasePX20D - One single-mode fiber EPON OLT, 20km" },
+       { 53,   "1000BasePX20U - One single-mode fiber EPON ONU, 20km" },
+       { 0, NULL }
+};
+
+#ifdef ENABLE_LLDPMED
+
+static lldpctl_map_t chassis_med_type_map[] = {
+       { LLDP_MED_CLASS_I,        "Generic Endpoint (Class I)" },
+       { LLDP_MED_CLASS_II,       "Media Endpoint (Class II)" },
+       { LLDP_MED_CLASS_III,      "Communication Device Endpoint (Class III)" },
+       { LLDP_MED_NETWORK_DEVICE, "Network Connectivity Device" },
+       { 0, NULL },
+};
+
+#endif
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_iter_ports_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_any_list_t *plist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       return (lldpctl_atom_iter_t*)TAILQ_FIRST(&plist->parent->hardware->h_rports);
+}
+
+static lldpctl_atom_iter_t*
+_lldpctl_atom_next_ports_list(lldpctl_atom_t *atom, lldpctl_atom_iter_t *iter)
+{
+       struct lldpd_port *port = (struct lldpd_port *)iter;
+       return (lldpctl_atom_iter_t*)TAILQ_NEXT(port, p_entries);
+}
+
+static lldpctl_atom_t*
+_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,
+           ((struct _lldpctl_atom_any_list_t *)atom)->parent);
+}
+
+static int
+_lldpctl_atom_new_port(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_port_t *port =
+           (struct _lldpctl_atom_port_t *)atom;
+       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*);
+       if (port->parent)
+               lldpctl_atom_inc_ref((lldpctl_atom_t*)port->parent);
+       return 1;
+}
+
+TAILQ_HEAD(chassis_list, lldpd_chassis);
+
+static void
+add_chassis(struct chassis_list *chassis_list,
+       struct lldpd_chassis *chassis)
+{
+       struct lldpd_chassis *one_chassis;
+       TAILQ_FOREACH(one_chassis, chassis_list, c_entries) {
+               if (one_chassis == chassis) return;
+       }
+       TAILQ_INSERT_TAIL(chassis_list,
+           chassis, c_entries);
+}
+
+static void
+_lldpctl_atom_free_port(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_port_t *port =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_hardware *hardware = port->hardware;
+       struct lldpd_chassis  *one_chassis, *one_chassis_next;
+       struct lldpd_port     *one_port;
+
+       /* We need to free the whole struct lldpd_hardware: local port, local
+        * chassis and remote ports... The same chassis may be present several
+        * times. We build a list of chassis (we don't use reference count). */
+       struct chassis_list chassis_list;
+       TAILQ_INIT(&chassis_list);
+
+       if (port->parent) lldpctl_atom_dec_ref((lldpctl_atom_t*)port->parent);
+       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;
+               lldpd_port_cleanup(port->port, 1);
+               free(port->port);
+       }
+       if (!hardware) return;
+
+       add_chassis(&chassis_list, port->port->p_chassis);
+       TAILQ_FOREACH(one_port, &hardware->h_rports, p_entries)
+               add_chassis(&chassis_list, one_port->p_chassis);
+
+       /* Free hardware port */
+       lldpd_remote_cleanup(hardware, NULL, 1);
+       lldpd_port_cleanup(port->port, 1);
+       free(port->hardware);
+
+       /* Free list of chassis */
+       for (one_chassis = TAILQ_FIRST(&chassis_list);
+            one_chassis != NULL;
+            one_chassis = one_chassis_next) {
+               one_chassis_next = TAILQ_NEXT(one_chassis, c_entries);
+               lldpd_chassis_cleanup(one_chassis, 1);
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_get_atom_port(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_port_t *p =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_port     *port     = p->port;
+       struct lldpd_hardware *hardware = p->hardware;
+
+       /* Local port only */
+       if (hardware != NULL) {
+               switch (key) {
+               case lldpctl_k_port_neighbors:
+                       return _lldpctl_new_atom(atom->conn, atom_ports_list, p);
+               default: break;
+               }
+       }
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_chassis_mgmt:
+               return _lldpctl_new_atom(atom->conn, atom_mgmts_list,
+                   p, port->p_chassis);
+#ifdef ENABLE_DOT3
+       case lldpctl_k_port_dot3_power:
+               return _lldpctl_new_atom(atom->conn, atom_dot3_power,
+                   p);
+#endif
+#ifdef ENABLE_DOT1
+       case lldpctl_k_port_vlans:
+               return _lldpctl_new_atom(atom->conn, atom_vlans_list,
+                   p);
+       case lldpctl_k_port_ppvids:
+               return _lldpctl_new_atom(atom->conn, atom_ppvids_list,
+                   p);
+       case lldpctl_k_port_pis:
+               return _lldpctl_new_atom(atom->conn, atom_pis_list,
+                   p);
+#endif
+#ifdef ENABLE_LLDPMED
+       case lldpctl_k_port_med_policies:
+               return _lldpctl_new_atom(atom->conn, atom_med_policies_list,
+                   p);
+       case lldpctl_k_port_med_locations:
+               return _lldpctl_new_atom(atom->conn, atom_med_locations_list,
+                   p);
+       case lldpctl_k_port_med_power:
+               return _lldpctl_new_atom(atom->conn, atom_med_power, p);
+#endif
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_atom_port(lldpctl_atom_t *atom, lldpctl_key_t key, lldpctl_atom_t *value)
+{
+       struct _lldpctl_atom_port_t *p =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_hardware *hardware = p->hardware;
+       struct lldpd_port_set set = {};
+       int rc;
+       char *canary;
+
+#ifdef ENABLE_DOT3
+       struct _lldpctl_atom_dot3_power_t *dpow;
+#endif
+#ifdef ENABLE_LLDPMED
+       struct _lldpctl_atom_med_power_t *mpow;
+       struct _lldpctl_atom_med_policy_t *mpol;
+       struct _lldpctl_atom_med_location_t *mloc;
+#endif
+
+       /* Local port only */
+       if (hardware == NULL) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       switch (key) {
+       case lldpctl_k_port_id:
+               set.local_id = p->port->p_id;
+               break;
+       case lldpctl_k_port_descr:
+               set.local_descr = p->port->p_descr;
+               break;
+#ifdef ENABLE_DOT3
+       case lldpctl_k_port_dot3_power:
+               if (value->type != atom_dot3_power) {
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
+                       return NULL;
+               }
+
+               dpow = (struct _lldpctl_atom_dot3_power_t *)value;
+               set.dot3_power = &dpow->parent->port->p_power;
+               break;
+#endif
+#ifdef ENABLE_LLDPMED
+       case lldpctl_k_port_med_power:
+               if (value->type != atom_med_power) {
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
+                       return NULL;
+               }
+
+               mpow = (struct _lldpctl_atom_med_power_t *)value;
+               set.med_power = &mpow->parent->port->p_med_power;
+               break;
+       case lldpctl_k_port_med_policies:
+               if (value->type != atom_med_policy) {
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
+                       return NULL;
+               }
+               mpol = (struct _lldpctl_atom_med_policy_t *)value;
+               set.med_policy = mpol->policy;
+               break;
+       case lldpctl_k_port_med_locations:
+               if (value->type != atom_med_location) {
+                       SET_ERROR(atom->conn, LLDPCTL_ERR_INCORRECT_ATOM_TYPE);
+                       return NULL;
+               }
+               mloc = (struct _lldpctl_atom_med_location_t *)value;
+               set.med_location = mloc->location;
+               break;
+#endif
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       set.ifname = hardware->h_ifname;
+
+       if (asprintf(&canary, "%d%p%s", key, value, set.ifname) == -1) {
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
+               return NULL;
+       }
+       rc = _lldpctl_do_something(atom->conn,
+           CONN_STATE_SET_PORT_SEND, CONN_STATE_SET_PORT_RECV,
+           canary,
+           SET_PORT, &set, &MARSHAL_INFO(lldpd_port_set),
+           NULL, NULL);
+       free(canary);
+       if (rc == 0) return atom;
+       return NULL;
+}
+
+static const char*
+_lldpctl_atom_get_str_port(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_port_t *p =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_port     *port     = p->port;
+       struct lldpd_hardware *hardware = p->hardware;
+       struct lldpd_chassis  *chassis  = port->p_chassis;
+       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;
+               }
+       }
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_port_protocol:
+               return map_lookup(lldpd_protocol_map.map, port->p_protocol);
+       case lldpctl_k_port_id_subtype:
+               return map_lookup(port_id_subtype_map, port->p_id_subtype);
+       case lldpctl_k_port_id:
+               switch (port->p_id_subtype) {
+               case LLDP_PORTID_SUBTYPE_IFNAME:
+               case LLDP_PORTID_SUBTYPE_IFALIAS:
+               case LLDP_PORTID_SUBTYPE_LOCAL:
+                       return port->p_id;
+               case LLDP_PORTID_SUBTYPE_LLADDR:
+                       return _lldpctl_dump_in_atom(atom,
+                           (uint8_t*)port->p_id, port->p_id_len,
+                           ':', 0);
+               case LLDP_PORTID_SUBTYPE_ADDR:
+                       switch (port->p_id[0]) {
+                       case LLDP_MGMT_ADDR_IP4: len = INET_ADDRSTRLEN + 1; break;
+                       case LLDP_MGMT_ADDR_IP6: len = INET6_ADDRSTRLEN + 1; break;
+                       default: len = 0;
+                       }
+                       if (len > 0) {
+                               ipaddress = _lldpctl_alloc_in_atom(atom, len);
+                               if (!ipaddress) return NULL;
+                               if (inet_ntop((port->p_id[0] == LLDP_MGMT_ADDR_IP4)?
+                                       AF_INET:AF_INET6,
+                                       &port->p_id[1], ipaddress, len) == NULL)
+                                       break;
+                               return ipaddress;
+                       }
+                       break;
+               }
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       case lldpctl_k_port_descr:
+               return port->p_descr;
+
+#ifdef ENABLE_DOT3
+       case lldpctl_k_port_dot3_mautype:
+               return map_lookup(operational_mau_type_values,
+                   port->p_macphy.mau_type);
+#endif
+
+       case lldpctl_k_chassis_id_subtype:
+               return map_lookup(chassis_id_subtype_map, chassis->c_id_subtype);
+       case lldpctl_k_chassis_id:
+               switch (chassis->c_id_subtype) {
+               case LLDP_CHASSISID_SUBTYPE_IFNAME:
+               case LLDP_CHASSISID_SUBTYPE_IFALIAS:
+               case LLDP_CHASSISID_SUBTYPE_LOCAL:
+                       return chassis->c_id;
+               case LLDP_CHASSISID_SUBTYPE_LLADDR:
+                       return _lldpctl_dump_in_atom(atom,
+                           (uint8_t*)chassis->c_id, chassis->c_id_len,
+                           ':', 0);
+               case LLDP_CHASSISID_SUBTYPE_ADDR:
+                       switch (chassis->c_id[0]) {
+                       case LLDP_MGMT_ADDR_IP4: len = INET_ADDRSTRLEN + 1; break;
+                       case LLDP_MGMT_ADDR_IP6: len = INET6_ADDRSTRLEN + 1; break;
+                       default: len = 0;
+                       }
+                       if (len > 0) {
+                               ipaddress = _lldpctl_alloc_in_atom(atom, len);
+                               if (!ipaddress) return NULL;
+                               if (inet_ntop((chassis->c_id[0] == LLDP_MGMT_ADDR_IP4)?
+                                       AF_INET:AF_INET6,
+                                       &chassis->c_id[1], ipaddress, len) == NULL)
+                                       break;
+                               return ipaddress;
+                       }
+                       break;
+               }
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       case lldpctl_k_chassis_name: return chassis->c_name;
+       case lldpctl_k_chassis_descr: return chassis->c_descr;
+
+#ifdef ENABLE_LLDPMED
+       case lldpctl_k_chassis_med_type:
+               return map_lookup(chassis_med_type_map, chassis->c_med_type);
+       case lldpctl_k_chassis_med_inventory_hw:
+               return chassis->c_med_hw;
+       case lldpctl_k_chassis_med_inventory_sw:
+               return chassis->c_med_sw;
+       case lldpctl_k_chassis_med_inventory_fw:
+               return chassis->c_med_fw;
+       case lldpctl_k_chassis_med_inventory_sn:
+               return chassis->c_med_sn;
+       case lldpctl_k_chassis_med_inventory_manuf:
+               return chassis->c_med_manuf;
+       case lldpctl_k_chassis_med_inventory_model:
+               return chassis->c_med_model;
+       case lldpctl_k_chassis_med_inventory_asset:
+               return chassis->c_med_asset;
+#endif
+
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static lldpctl_atom_t*
+_lldpctl_atom_set_str_port(lldpctl_atom_t *atom, lldpctl_key_t key,
+    const char *value)
+{
+       struct _lldpctl_atom_port_t *p =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_port     *port     = p->port;
+
+       if (!value || !strlen(value))
+               return NULL;
+
+       switch (key) {
+       case lldpctl_k_port_id:
+               free(port->p_id);
+               port->p_id = strdup(value);
+               port->p_id_len = strlen(value);
+               break;
+       case lldpctl_k_port_descr:
+               free(port->p_descr);
+               port->p_descr = strdup(value);
+               break;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+
+       return _lldpctl_atom_set_atom_port(atom, key, NULL);
+}
+
+static long int
+_lldpctl_atom_get_int_port(lldpctl_atom_t *atom, lldpctl_key_t key)
+{
+       struct _lldpctl_atom_port_t *p =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_port     *port     = p->port;
+       struct lldpd_hardware *hardware = p->hardware;
+       struct lldpd_chassis  *chassis  = port->p_chassis;
+
+       /* Local port only */
+       if (hardware != NULL) {
+               switch (key) {
+               case lldpctl_k_port_index:
+                       return hardware->h_ifindex;
+               case lldpctl_k_tx_cnt:
+                       return hardware->h_tx_cnt;
+               case lldpctl_k_rx_cnt:
+                       return hardware->h_rx_cnt;
+               case lldpctl_k_rx_discarded_cnt:
+                       return hardware->h_rx_discarded_cnt;
+               case lldpctl_k_rx_unrecognized_cnt:
+                       return hardware->h_rx_unrecognized_cnt;
+               case lldpctl_k_ageout_cnt:
+                       return hardware->h_ageout_cnt;
+               case lldpctl_k_insert_cnt:
+                       return hardware->h_insert_cnt;
+               case lldpctl_k_delete_cnt:
+                       return hardware->h_delete_cnt;
+               default: break;
+               }
+       }
+
+       /* Local and remote port */
+       switch (key) {
+       case lldpctl_k_port_protocol:
+               return port->p_protocol;
+       case lldpctl_k_port_age:
+               return port->p_lastchange;
+       case lldpctl_k_port_id_subtype:
+               return port->p_id_subtype;
+       case lldpctl_k_port_hidden:
+               return port->p_hidden_in;
+#ifdef ENABLE_DOT3
+       case lldpctl_k_port_dot3_mfs:
+               if (port->p_mfs > 0)
+                       return port->p_mfs;
+               break;
+       case lldpctl_k_port_dot3_aggregid:
+               if (port->p_aggregid > 0)
+                       return port->p_aggregid;
+               break;
+       case lldpctl_k_port_dot3_autoneg_support:
+               return port->p_macphy.autoneg_support;
+       case lldpctl_k_port_dot3_autoneg_enabled:
+               return port->p_macphy.autoneg_enabled;
+       case lldpctl_k_port_dot3_autoneg_advertised:
+               return port->p_macphy.autoneg_advertised;
+       case lldpctl_k_port_dot3_mautype:
+               return port->p_macphy.mau_type;
+#endif
+#ifdef ENABLE_DOT1
+       case lldpctl_k_port_vlan_pvid:
+               return port->p_pvid;
+#endif
+       case lldpctl_k_chassis_index:
+               return chassis->c_index;
+       case lldpctl_k_chassis_id_subtype:
+               return chassis->c_id_subtype;
+       case lldpctl_k_chassis_cap_available:
+               return chassis->c_cap_available;
+       case lldpctl_k_chassis_cap_enabled:
+               return chassis->c_cap_enabled;
+#ifdef ENABLE_LLDPMED
+       case lldpctl_k_chassis_med_type:
+               return chassis->c_med_type;
+       case lldpctl_k_chassis_med_cap:
+               return chassis->c_med_cap_available;
+#endif
+       default:
+               return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+       }
+       return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+}
+
+static const uint8_t*
+_lldpctl_atom_get_buf_port(lldpctl_atom_t *atom, lldpctl_key_t key, size_t *n)
+{
+       struct _lldpctl_atom_port_t *p =
+           (struct _lldpctl_atom_port_t *)atom;
+       struct lldpd_port     *port     = p->port;
+       struct lldpd_chassis  *chassis  = port->p_chassis;
+
+       switch (key) {
+       case lldpctl_k_port_id:
+               *n = port->p_id_len;
+               return (uint8_t*)port->p_id;
+       case lldpctl_k_chassis_id:
+               *n = chassis->c_id_len;
+               return (uint8_t*)chassis->c_id;
+       default:
+               SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
+               return NULL;
+       }
+}
+
+static struct atom_builder ports_list =
+       { atom_ports_list, sizeof(struct _lldpctl_atom_any_list_t),
+         .init = _lldpctl_atom_new_any_list,
+         .free = _lldpctl_atom_free_any_list,
+         .iter = _lldpctl_atom_iter_ports_list,
+         .next = _lldpctl_atom_next_ports_list,
+         .value = _lldpctl_atom_value_ports_list };
+
+static struct atom_builder port =
+       { atom_port, sizeof(struct _lldpctl_atom_port_t),
+         .init = _lldpctl_atom_new_port,
+         .free = _lldpctl_atom_free_port,
+         .get  = _lldpctl_atom_get_atom_port,
+         .set  = _lldpctl_atom_set_atom_port,
+         .get_str = _lldpctl_atom_get_str_port,
+         .set_str = _lldpctl_atom_set_str_port,
+         .get_int = _lldpctl_atom_get_int_port,
+         .get_buffer = _lldpctl_atom_get_buf_port };
+
+ATOM_BUILDER_REGISTER(ports_list, 4);
+ATOM_BUILDER_REGISTER(port,       5);
+
diff --git a/src/lib/helpers.c b/src/lib/helpers.c
new file mode 100644 (file)
index 0000000..04967eb
--- /dev/null
@@ -0,0 +1,76 @@
+/* -*- 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 "../lldpd-structs.h"
+#include "../log.h"
+#include "private.h"
+#include "helpers.h"
+
+const char*
+map_lookup(lldpctl_map_t *list, int n)
+{
+
+       unsigned int i;
+
+       for (i = 0; list[i].string != NULL; i++) {
+               if (list[i].value == n) {
+                       return list[i].string;
+               }
+       }
+
+       return "unknown";
+}
+
+#if defined ENABLE_LLDPMED || defined ENABLE_DOT3
+int
+map_reverse_lookup(lldpctl_map_t *list, const char *string)
+{
+       if (!string) return -1;
+
+       for (unsigned int i = 0; list[i].string != NULL; i++) {
+               if (!strcasecmp(list[i].string, string))
+                       return list[i].value;
+       }
+
+       return -1;
+}
+#endif
+
+int
+_lldpctl_atom_new_any_list(lldpctl_atom_t *atom, va_list ap)
+{
+       struct _lldpctl_atom_any_list_t *plist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       plist->parent = va_arg(ap, struct _lldpctl_atom_port_t *);
+       lldpctl_atom_inc_ref((lldpctl_atom_t *)plist->parent);
+       return 1;
+}
+
+void
+_lldpctl_atom_free_any_list(lldpctl_atom_t *atom)
+{
+       struct _lldpctl_atom_any_list_t *plist =
+           (struct _lldpctl_atom_any_list_t *)atom;
+       lldpctl_atom_dec_ref((lldpctl_atom_t *)plist->parent);
+}
+
diff --git a/src/lib/helpers.h b/src/lib/helpers.h
new file mode 100644 (file)
index 0000000..172d7bd
--- /dev/null
@@ -0,0 +1,25 @@
+/* -*- 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.
+ */
+
+const char *map_lookup(lldpctl_map_t *list, int n);
+#if defined ENABLE_LLDPMED || defined ENABLE_DOT3
+int map_reverse_lookup(lldpctl_map_t *list, const char *string);
+#endif
+
+int _lldpctl_atom_new_any_list(lldpctl_atom_t *atom, va_list ap);
+void _lldpctl_atom_free_any_list(lldpctl_atom_t *atom);
+
index 1d691c5f2798787451e6f9beaf7f3b4e8c14aabb..13c0f401cd1a783b509da72c8ef6bd1e94a86d2e 100644 (file)
@@ -250,6 +250,17 @@ struct _lldpctl_atom_med_power_t {
 
 struct lldpctl_atom_t *_lldpctl_new_atom(lldpctl_conn_t *conn, atom_t type, ...);
 
+struct atom_map {
+       int key;
+       struct atom_map *next;
+       lldpctl_map_t   map[];
+};
+
+void atom_map_register(struct atom_map *map);
+
+#define __constructor__(PRIO) __attribute__ ((constructor( PRIO )))
+#define ATOM_MAP_REGISTER(NAME, PRIO) __constructor__(100 + PRIO) void init_ ## NAME() { atom_map_register(& NAME ); }
+
 struct atom_builder {
        atom_t type;    /* Atom type */
        size_t size;    /* Size of structure to allocate */
@@ -270,4 +281,11 @@ struct atom_builder {
        lldpctl_atom_t*      (*set_buffer)(lldpctl_atom_t *, lldpctl_key_t, const u_int8_t *, size_t);
        lldpctl_atom_t*      (*set_int)(lldpctl_atom_t *, lldpctl_key_t, long int);
        lldpctl_atom_t*      (*create)(lldpctl_atom_t *);
+       struct atom_builder  *nextb;
 };
+
+void atom_builder_register(struct atom_builder *builder);
+
+#define __constructor__(PRIO) __attribute__ ((constructor( PRIO )))
+#define ATOM_BUILDER_REGISTER(NAME, PRIO) __constructor__(200 + PRIO) void init_ ## NAME() { atom_builder_register(& NAME ); }
+