1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3  * Copyright (c) 2015 Vincent Bernat <vincent@bernat.im>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <stdio.h>
19 #include <stdarg.h>
20 #include <string.h>
21 #include <arpa/inet.h>
22 
23 #include "lldpctl.h"
24 #include "../log.h"
25 #include "atom.h"
26 #include "helpers.h"
27 
28 static lldpctl_map_t chassis_id_subtype_map[] = {
29 	{ LLDP_CHASSISID_SUBTYPE_IFNAME,  "ifname"},
30 	{ LLDP_CHASSISID_SUBTYPE_IFALIAS, "ifalias" },
31 	{ LLDP_CHASSISID_SUBTYPE_LOCAL,   "local" },
32 	{ LLDP_CHASSISID_SUBTYPE_LLADDR,  "mac" },
33 	{ LLDP_CHASSISID_SUBTYPE_ADDR,    "ip" },
34 	{ LLDP_CHASSISID_SUBTYPE_PORT,    "unhandled" },
35 	{ LLDP_CHASSISID_SUBTYPE_CHASSIS, "unhandled" },
36 	{ 0, NULL},
37 };
38 
39 #ifdef ENABLE_LLDPMED
40 
41 static lldpctl_map_t chassis_med_type_map[] = {
42 	{ LLDP_MED_CLASS_I,        "Generic Endpoint (Class I)" },
43 	{ LLDP_MED_CLASS_II,       "Media Endpoint (Class II)" },
44 	{ LLDP_MED_CLASS_III,      "Communication Device Endpoint (Class III)" },
45 	{ LLDP_MED_NETWORK_DEVICE, "Network Connectivity Device" },
46 	{ 0, NULL },
47 };
48 
49 #endif
50 
51 static int
_lldpctl_atom_new_chassis(lldpctl_atom_t * atom,va_list ap)52 _lldpctl_atom_new_chassis(lldpctl_atom_t *atom, va_list ap)
53 {
54 	struct _lldpctl_atom_chassis_t *p =
55 	    (struct _lldpctl_atom_chassis_t *)atom;
56 	p->chassis = va_arg(ap, struct lldpd_chassis*);
57 	p->parent = va_arg(ap, struct _lldpctl_atom_port_t*);
58 	p->embedded = va_arg(ap, int);
59 	if (p->parent && !p->embedded)
60 		lldpctl_atom_inc_ref((lldpctl_atom_t*)p->parent);
61 	return 1;
62 }
63 
64 static void
_lldpctl_atom_free_chassis(lldpctl_atom_t * atom)65 _lldpctl_atom_free_chassis(lldpctl_atom_t *atom)
66 {
67 	struct _lldpctl_atom_chassis_t *p =
68 	    (struct _lldpctl_atom_chassis_t *)atom;
69 	/* When we have a parent, the chassis structure is in fact part of the
70 	 * parent, just decrement the reference count of the parent. Otherwise,
71 	 * we need to free the whole chassis. When embedded, we don't alter the
72 	 * reference count of the parent. Therefore, it's important to also not
73 	 * increase the reference count of this atom. See
74 	 * `_lldpctl_atom_get_atom_chassis' for how to handle that. */
75 	if (p->parent) {
76 		if (!p->embedded)
77 			lldpctl_atom_dec_ref((lldpctl_atom_t*)p->parent);
78 	} else
79 		lldpd_chassis_cleanup(p->chassis, 1);
80 }
81 
82 static lldpctl_atom_t*
_lldpctl_atom_get_atom_chassis(lldpctl_atom_t * atom,lldpctl_key_t key)83 _lldpctl_atom_get_atom_chassis(lldpctl_atom_t *atom, lldpctl_key_t key)
84 {
85 	struct _lldpctl_atom_chassis_t *p =
86 	    (struct _lldpctl_atom_chassis_t *)atom;
87 	struct lldpd_chassis *chassis = p->chassis;
88 
89 	switch (key) {
90 	case lldpctl_k_chassis_mgmt:
91 		return _lldpctl_new_atom(atom->conn, atom_mgmts_list,
92 		    (p->parent && p->embedded)?
93 		    (lldpctl_atom_t *)p->parent:
94 		    (lldpctl_atom_t *)p,
95 		    chassis);
96 	default:
97 		SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
98 		return NULL;
99 	}
100 }
101 
102 static const char*
_lldpctl_atom_get_str_chassis(lldpctl_atom_t * atom,lldpctl_key_t key)103 _lldpctl_atom_get_str_chassis(lldpctl_atom_t *atom, lldpctl_key_t key)
104 {
105 	struct _lldpctl_atom_chassis_t *p =
106 	    (struct _lldpctl_atom_chassis_t *)atom;
107 	struct lldpd_chassis *chassis = p->chassis;
108 	char *ipaddress = NULL; size_t len;
109 
110 	/* Local and remote port */
111 	switch (key) {
112 
113 	case lldpctl_k_chassis_id_subtype:
114 		return map_lookup(chassis_id_subtype_map, chassis->c_id_subtype);
115 	case lldpctl_k_chassis_id:
116 		switch (chassis->c_id_subtype) {
117 		case LLDP_CHASSISID_SUBTYPE_IFNAME:
118 		case LLDP_CHASSISID_SUBTYPE_IFALIAS:
119 		case LLDP_CHASSISID_SUBTYPE_LOCAL:
120 			return chassis->c_id;
121 		case LLDP_CHASSISID_SUBTYPE_LLADDR:
122 			return _lldpctl_dump_in_atom(atom,
123 			    (uint8_t*)chassis->c_id, chassis->c_id_len,
124 			    ':', 0);
125 		case LLDP_CHASSISID_SUBTYPE_ADDR:
126 			switch (chassis->c_id[0]) {
127 			case LLDP_MGMT_ADDR_IP4: len = INET_ADDRSTRLEN + 1; break;
128 			case LLDP_MGMT_ADDR_IP6: len = INET6_ADDRSTRLEN + 1; break;
129 			default: len = 0;
130 			}
131 			if (len > 0) {
132 				ipaddress = _lldpctl_alloc_in_atom(atom, len);
133 				if (!ipaddress) return NULL;
134 				if (inet_ntop((chassis->c_id[0] == LLDP_MGMT_ADDR_IP4)?
135 					AF_INET:AF_INET6,
136 					&chassis->c_id[1], ipaddress, len) == NULL)
137 					break;
138 				return ipaddress;
139 			}
140 			break;
141 		}
142 		SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
143 		return NULL;
144 	case lldpctl_k_chassis_name: return chassis->c_name;
145 	case lldpctl_k_chassis_descr: return chassis->c_descr;
146 
147 #ifdef ENABLE_LLDPMED
148 	case lldpctl_k_chassis_med_type:
149 		return map_lookup(chassis_med_type_map, chassis->c_med_type);
150 	case lldpctl_k_chassis_med_inventory_hw:
151 		return chassis->c_med_hw;
152 	case lldpctl_k_chassis_med_inventory_sw:
153 		return chassis->c_med_sw;
154 	case lldpctl_k_chassis_med_inventory_fw:
155 		return chassis->c_med_fw;
156 	case lldpctl_k_chassis_med_inventory_sn:
157 		return chassis->c_med_sn;
158 	case lldpctl_k_chassis_med_inventory_manuf:
159 		return chassis->c_med_manuf;
160 	case lldpctl_k_chassis_med_inventory_model:
161 		return chassis->c_med_model;
162 	case lldpctl_k_chassis_med_inventory_asset:
163 		return chassis->c_med_asset;
164 #endif
165 
166 	default:
167 		SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
168 		return NULL;
169 	}
170 }
171 
172 static long int
_lldpctl_atom_get_int_chassis(lldpctl_atom_t * atom,lldpctl_key_t key)173 _lldpctl_atom_get_int_chassis(lldpctl_atom_t *atom, lldpctl_key_t key)
174 {
175 	struct _lldpctl_atom_chassis_t *p =
176 	    (struct _lldpctl_atom_chassis_t *)atom;
177 	struct lldpd_chassis *chassis = p->chassis;
178 
179 	/* Local and remote port */
180 	switch (key) {
181 	case lldpctl_k_chassis_index:
182 		return chassis->c_index;
183 	case lldpctl_k_chassis_id_subtype:
184 		return chassis->c_id_subtype;
185 	case lldpctl_k_chassis_cap_available:
186 		return chassis->c_cap_available;
187 	case lldpctl_k_chassis_cap_enabled:
188 		return chassis->c_cap_enabled;
189 #ifdef ENABLE_LLDPMED
190 	case lldpctl_k_chassis_med_type:
191 		return chassis->c_med_type;
192 	case lldpctl_k_chassis_med_cap:
193 		return chassis->c_med_cap_available;
194 #endif
195 	default:
196 		return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
197 	}
198 	return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
199 }
200 
201 static const uint8_t*
_lldpctl_atom_get_buf_chassis(lldpctl_atom_t * atom,lldpctl_key_t key,size_t * n)202 _lldpctl_atom_get_buf_chassis(lldpctl_atom_t *atom, lldpctl_key_t key, size_t *n)
203 {
204 	struct _lldpctl_atom_chassis_t *p =
205 	    (struct _lldpctl_atom_chassis_t *)atom;
206 	struct lldpd_chassis *chassis = p->chassis;
207 
208 	switch (key) {
209 	case lldpctl_k_chassis_id:
210 		*n = chassis->c_id_len;
211 		return (uint8_t*)chassis->c_id;
212 	default:
213 		SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST);
214 		return NULL;
215 	}
216 }
217 
218 static struct atom_builder chassis =
219 	{ atom_chassis, sizeof(struct _lldpctl_atom_chassis_t),
220 	  .init = _lldpctl_atom_new_chassis,
221 	  .free = _lldpctl_atom_free_chassis,
222 	  .get  = _lldpctl_atom_get_atom_chassis,
223 	  .get_str = _lldpctl_atom_get_str_chassis,
224 	  .get_int = _lldpctl_atom_get_int_chassis,
225 	  .get_buffer = _lldpctl_atom_get_buf_chassis };
226 
227 ATOM_BUILDER_REGISTER(chassis, 3);
228