]>
Commit | Line | Data |
---|---|---|
43c02e7b VB |
1 | /* |
2 | * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> | |
3 | * | |
4 | * Permission to use, copy, modify, and distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
17 | #include "lldpd.h" | |
a8105c1b | 18 | #include "frame.h" |
43c02e7b VB |
19 | |
20 | #include <unistd.h> | |
21 | #include <errno.h> | |
22 | #include <time.h> | |
23 | #include <sys/types.h> | |
24 | #include <sys/socket.h> | |
25 | #include <sys/ioctl.h> | |
26 | #include <netpacket/packet.h> | |
27 | #include <linux/sockios.h> | |
28 | ||
29 | int | |
77507b69 | 30 | lldp_send(struct lldpd *global, |
a8105c1b | 31 | struct lldpd_hardware *hardware) |
43c02e7b | 32 | { |
a8105c1b | 33 | struct lldpd_port *port; |
77507b69 | 34 | struct lldpd_chassis *chassis; |
a8105c1b VB |
35 | struct lldpd_frame *frame; |
36 | int length; | |
37 | u_int8_t *packet, *pos, *tlv; | |
38 | ||
39 | u_int8_t mcastaddr[] = LLDP_MULTICAST_ADDR; | |
a1347cd8 VB |
40 | #ifdef ENABLE_DOT1 |
41 | const u_int8_t dot1[] = LLDP_TLV_ORG_DOT1; | |
a1347cd8 VB |
42 | struct lldpd_vlan *vlan; |
43 | #endif | |
44 | #ifdef ENABLE_DOT3 | |
45 | const u_int8_t dot3[] = LLDP_TLV_ORG_DOT3; | |
a1347cd8 | 46 | #endif |
89840df0 | 47 | #ifdef ENABLE_LLDPMED |
115ff55c | 48 | int i; |
89840df0 | 49 | const u_int8_t med[] = LLDP_TLV_ORG_MED; |
89840df0 | 50 | #endif |
43c02e7b | 51 | |
a8105c1b | 52 | port = &hardware->h_lport; |
77507b69 | 53 | chassis = port->p_chassis; |
a8105c1b VB |
54 | length = hardware->h_mtu; |
55 | if ((packet = (u_int8_t*)malloc(length)) == NULL) | |
56 | return ENOMEM; | |
57 | memset(packet, 0, length); | |
58 | pos = packet; | |
43c02e7b VB |
59 | |
60 | /* Ethernet header */ | |
a8105c1b VB |
61 | if (!( |
62 | /* LLDP multicast address */ | |
63 | POKE_BYTES(mcastaddr, sizeof(mcastaddr)) && | |
64 | /* Source MAC address */ | |
65 | POKE_BYTES(&hardware->h_lladdr, sizeof(hardware->h_lladdr)) && | |
66 | /* LLDP frame */ | |
67 | POKE_UINT16(ETHERTYPE_LLDP))) | |
68 | goto toobig; | |
43c02e7b VB |
69 | |
70 | /* Chassis ID */ | |
a8105c1b VB |
71 | if (!( |
72 | POKE_START_LLDP_TLV(LLDP_TLV_CHASSIS_ID) && | |
73 | POKE_UINT8(chassis->c_id_subtype) && | |
74 | POKE_BYTES(chassis->c_id, chassis->c_id_len) && | |
75 | POKE_END_LLDP_TLV)) | |
76 | goto toobig; | |
43c02e7b VB |
77 | |
78 | /* Port ID */ | |
a8105c1b VB |
79 | if (!( |
80 | POKE_START_LLDP_TLV(LLDP_TLV_PORT_ID) && | |
81 | POKE_UINT8(port->p_id_subtype) && | |
82 | POKE_BYTES(port->p_id, port->p_id_len) && | |
83 | POKE_END_LLDP_TLV)) | |
84 | goto toobig; | |
43c02e7b VB |
85 | |
86 | /* Time to live */ | |
a8105c1b VB |
87 | if (!( |
88 | POKE_START_LLDP_TLV(LLDP_TLV_TTL) && | |
89 | POKE_UINT16(chassis->c_ttl) && | |
90 | POKE_END_LLDP_TLV)) | |
91 | goto toobig; | |
43c02e7b VB |
92 | |
93 | /* System name */ | |
a8105c1b VB |
94 | if (!( |
95 | POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_NAME) && | |
96 | POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) && | |
97 | POKE_END_LLDP_TLV)) | |
98 | goto toobig; | |
43c02e7b VB |
99 | |
100 | /* System description */ | |
a8105c1b VB |
101 | if (!( |
102 | POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_DESCR) && | |
103 | POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)) && | |
104 | POKE_END_LLDP_TLV)) | |
105 | goto toobig; | |
43c02e7b VB |
106 | |
107 | /* System capabilities */ | |
a8105c1b VB |
108 | if (!( |
109 | POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_CAP) && | |
110 | POKE_UINT16(chassis->c_cap_available) && | |
111 | POKE_UINT16(chassis->c_cap_enabled) && | |
112 | POKE_END_LLDP_TLV)) | |
113 | goto toobig; | |
43c02e7b VB |
114 | |
115 | if (chassis->c_mgmt.s_addr != INADDR_ANY) { | |
116 | /* Management address */ | |
a8105c1b VB |
117 | if (!( |
118 | POKE_START_LLDP_TLV(LLDP_TLV_MGMT_ADDR) && | |
119 | /* Size of the address, including its type */ | |
120 | POKE_UINT8(sizeof(struct in_addr) + 1) && | |
121 | /* Address is IPv4 */ | |
122 | POKE_UINT8(LLDP_MGMT_ADDR_IP4) && | |
123 | POKE_BYTES(&chassis->c_mgmt, sizeof(struct in_addr)))) | |
124 | goto toobig; | |
43c02e7b VB |
125 | |
126 | /* Interface port type, OID */ | |
a8105c1b VB |
127 | if (chassis->c_mgmt_if == 0) { |
128 | if (!( | |
129 | /* We don't know the management interface */ | |
130 | POKE_UINT8(LLDP_MGMT_IFACE_UNKNOWN) && | |
131 | POKE_UINT32(0))) | |
132 | goto toobig; | |
133 | } else { | |
134 | if (!( | |
135 | /* We have the index of the management interface */ | |
136 | POKE_UINT8(LLDP_MGMT_IFACE_IFINDEX) && | |
137 | POKE_UINT32(chassis->c_mgmt_if))) | |
138 | goto toobig; | |
43c02e7b | 139 | } |
a8105c1b VB |
140 | if (!( |
141 | /* We don't provide an OID for management */ | |
142 | POKE_UINT8(0) && | |
143 | POKE_END_LLDP_TLV)) | |
144 | goto toobig; | |
43c02e7b VB |
145 | } |
146 | ||
147 | /* Port description */ | |
a8105c1b VB |
148 | if (!( |
149 | POKE_START_LLDP_TLV(LLDP_TLV_PORT_DESCR) && | |
150 | POKE_BYTES(port->p_descr, strlen(port->p_descr)) && | |
151 | POKE_END_LLDP_TLV)) | |
152 | goto toobig; | |
43c02e7b | 153 | |
a1347cd8 | 154 | #ifdef ENABLE_DOT1 |
43c02e7b | 155 | /* VLANs */ |
a8105c1b VB |
156 | TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) { |
157 | if (!( | |
158 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
159 | POKE_BYTES(dot1, sizeof(dot1)) && | |
160 | POKE_UINT8(LLDP_TLV_DOT1_VLANNAME) && | |
161 | POKE_UINT16(vlan->v_vid) && | |
162 | POKE_UINT8(strlen(vlan->v_name)) && | |
163 | POKE_BYTES(vlan->v_name, strlen(vlan->v_name)) && | |
164 | POKE_END_LLDP_TLV)) | |
165 | goto toobig; | |
43c02e7b | 166 | } |
a1347cd8 | 167 | #endif |
43c02e7b | 168 | |
a1347cd8 | 169 | #ifdef ENABLE_DOT3 |
43c02e7b | 170 | /* Aggregation status */ |
a8105c1b VB |
171 | if (!( |
172 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
173 | POKE_BYTES(dot3, sizeof(dot3)) && | |
174 | POKE_UINT8(LLDP_TLV_DOT3_LA) && | |
175 | /* Bit 0 = capability ; Bit 1 = status */ | |
176 | POKE_UINT8((port->p_aggregid) ? 3:1) && | |
177 | POKE_UINT32(port->p_aggregid) && | |
178 | POKE_END_LLDP_TLV)) | |
179 | goto toobig; | |
43c02e7b VB |
180 | |
181 | /* MAC/PHY */ | |
a8105c1b VB |
182 | if (!( |
183 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
184 | POKE_BYTES(dot3, sizeof(dot3)) && | |
185 | POKE_UINT8(LLDP_TLV_DOT3_MAC) && | |
3fd015c0 VB |
186 | POKE_UINT8(port->p_macphy.autoneg_support | |
187 | (port->p_macphy.autoneg_enabled << 1)) && | |
188 | POKE_UINT16(port->p_macphy.autoneg_advertised) && | |
189 | POKE_UINT16(port->p_macphy.mau_type) && | |
a8105c1b VB |
190 | POKE_END_LLDP_TLV)) |
191 | goto toobig; | |
548109b2 VB |
192 | |
193 | /* MFS */ | |
a8105c1b VB |
194 | if (!( |
195 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
196 | POKE_BYTES(dot3, sizeof(dot3)) && | |
197 | POKE_UINT8(LLDP_TLV_DOT3_MFS) && | |
198 | POKE_UINT16(port->p_mfs) && | |
199 | POKE_END_LLDP_TLV)) | |
200 | goto toobig; | |
42ee7382 VB |
201 | /* Power */ |
202 | if (port->p_power.devicetype) { | |
203 | if (!( | |
204 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
205 | POKE_BYTES(dot3, sizeof(dot3)) && | |
206 | POKE_UINT8(LLDP_TLV_DOT3_POWER) && | |
207 | POKE_UINT8(( | |
208 | (((2 - port->p_power.devicetype) %(1<< 1))<<0) | | |
209 | (( port->p_power.supported %(1<< 1))<<1) | | |
210 | (( port->p_power.enabled %(1<< 1))<<2) | | |
211 | (( port->p_power.paircontrol %(1<< 1))<<3))) && | |
212 | POKE_UINT8(port->p_power.pairs) && | |
608cb51c VB |
213 | POKE_UINT8(port->p_power.class))) |
214 | goto toobig; | |
215 | /* 802.3at */ | |
216 | if (port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { | |
217 | if (!( | |
218 | POKE_UINT8(( | |
219 | (((port->p_power.powertype == | |
220 | LLDP_DOT3_POWER_8023AT_TYPE1)?1:0) << 7) | | |
221 | (((port->p_power.devicetype == | |
222 | LLDP_DOT3_POWER_PSE)?0:1) << 6) | | |
223 | ((port->p_power.source %(1<< 2))<<4) | | |
224 | ((port->p_power.priority %(1<< 2))<<0))) && | |
225 | POKE_UINT16(port->p_power.requested) && | |
226 | POKE_UINT16(port->p_power.allocated))) | |
227 | goto toobig; | |
228 | } | |
229 | if (!(POKE_END_LLDP_TLV)) | |
42ee7382 VB |
230 | goto toobig; |
231 | } | |
a1347cd8 | 232 | #endif |
43c02e7b | 233 | |
89840df0 | 234 | #ifdef ENABLE_LLDPMED |
740593ff | 235 | if (port->p_med_cap_enabled) { |
89840df0 | 236 | /* LLDP-MED cap */ |
a8105c1b VB |
237 | if (!( |
238 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
239 | POKE_BYTES(med, sizeof(med)) && | |
240 | POKE_UINT8(LLDP_TLV_MED_CAP) && | |
241 | POKE_UINT16(chassis->c_med_cap_available) && | |
bc08499a | 242 | POKE_UINT8(chassis->c_med_type) && |
a8105c1b VB |
243 | POKE_END_LLDP_TLV)) |
244 | goto toobig; | |
89840df0 VB |
245 | |
246 | /* LLDP-MED inventory */ | |
a8105c1b | 247 | #define LLDP_INVENTORY(value, subtype) \ |
89840df0 | 248 | if (value) { \ |
a8105c1b VB |
249 | if (!( \ |
250 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && \ | |
251 | POKE_BYTES(med, sizeof(med)) && \ | |
252 | POKE_UINT8(subtype) && \ | |
253 | POKE_BYTES(value, \ | |
254 | (strlen(value)>32)?32:strlen(value)) && \ | |
255 | POKE_END_LLDP_TLV)) \ | |
256 | goto toobig; \ | |
89840df0 | 257 | } |
e809a587 | 258 | |
740593ff | 259 | if (port->p_med_cap_enabled & LLDPMED_CAP_IV) { |
a8105c1b VB |
260 | LLDP_INVENTORY(chassis->c_med_hw, |
261 | LLDP_TLV_MED_IV_HW); | |
262 | LLDP_INVENTORY(chassis->c_med_fw, | |
263 | LLDP_TLV_MED_IV_FW); | |
264 | LLDP_INVENTORY(chassis->c_med_sw, | |
265 | LLDP_TLV_MED_IV_SW); | |
266 | LLDP_INVENTORY(chassis->c_med_sn, | |
267 | LLDP_TLV_MED_IV_SN); | |
268 | LLDP_INVENTORY(chassis->c_med_manuf, | |
269 | LLDP_TLV_MED_IV_MANUF); | |
270 | LLDP_INVENTORY(chassis->c_med_model, | |
271 | LLDP_TLV_MED_IV_MODEL); | |
272 | LLDP_INVENTORY(chassis->c_med_asset, | |
273 | LLDP_TLV_MED_IV_ASSET); | |
e809a587 | 274 | } |
115ff55c VB |
275 | |
276 | /* LLDP-MED location */ | |
277 | for (i = 0; i < LLDPMED_LOCFORMAT_LAST; i++) { | |
740593ff | 278 | if (port->p_med_location[i].format == i + 1) { |
a8105c1b VB |
279 | if (!( |
280 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
281 | POKE_BYTES(med, sizeof(med)) && | |
282 | POKE_UINT8(LLDP_TLV_MED_LOCATION) && | |
283 | POKE_UINT8(port->p_med_location[i].format) && | |
284 | POKE_BYTES(port->p_med_location[i].data, | |
285 | port->p_med_location[i].data_len) && | |
286 | POKE_END_LLDP_TLV)) | |
287 | goto toobig; | |
115ff55c VB |
288 | } |
289 | } | |
86f24df3 VB |
290 | |
291 | /* LLDP-MED network policy */ | |
292 | for (i = 0; i < LLDPMED_APPTYPE_LAST; i++) { | |
293 | if (port->p_med_policy[i].type == i + 1) { | |
294 | if (!( | |
295 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
296 | POKE_BYTES(med, sizeof(med)) && | |
297 | POKE_UINT8(LLDP_TLV_MED_POLICY) && | |
298 | POKE_UINT32(( | |
299 | ((port->p_med_policy[i].type %(1<< 8))<<24) | | |
300 | ((port->p_med_policy[i].unknown %(1<< 1))<<23) | | |
301 | ((port->p_med_policy[i].tagged %(1<< 1))<<22) | | |
302 | /*((0 %(1<< 1))<<21) |*/ | |
303 | ((port->p_med_policy[i].vid %(1<<12))<< 9) | | |
304 | ((port->p_med_policy[i].priority %(1<< 3))<< 6) | | |
305 | ((port->p_med_policy[i].dscp %(1<< 6))<< 0) )) && | |
306 | POKE_END_LLDP_TLV)) | |
307 | goto toobig; | |
308 | } | |
309 | } | |
009ddd23 VB |
310 | |
311 | /* LLDP-MED POE-MDI */ | |
312 | if ((port->p_med_power.devicetype == LLDPMED_POW_TYPE_PSE) || | |
313 | (port->p_med_power.devicetype == LLDPMED_POW_TYPE_PD)) { | |
314 | int devicetype = 0, source = 0; | |
315 | if (!( | |
316 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
317 | POKE_BYTES(med, sizeof(med)) && | |
318 | POKE_UINT8(LLDP_TLV_MED_MDI))) | |
319 | goto toobig; | |
320 | switch (port->p_med_power.devicetype) { | |
321 | case LLDPMED_POW_TYPE_PSE: | |
322 | devicetype = 0; | |
323 | switch (port->p_med_power.source) { | |
324 | case LLDPMED_POW_SOURCE_PRIMARY: source = 1; break; | |
325 | case LLDPMED_POW_SOURCE_BACKUP: source = 2; break; | |
326 | case LLDPMED_POW_SOURCE_RESERVED: source = 3; break; | |
327 | default: source = 0; break; | |
328 | } | |
329 | break; | |
330 | case LLDPMED_POW_TYPE_PD: | |
331 | devicetype = 1; | |
332 | switch (port->p_med_power.source) { | |
333 | case LLDPMED_POW_SOURCE_PSE: source = 1; break; | |
334 | case LLDPMED_POW_SOURCE_LOCAL: source = 2; break; | |
335 | case LLDPMED_POW_SOURCE_BOTH: source = 3; break; | |
336 | default: source = 0; break; | |
337 | } | |
338 | break; | |
339 | } | |
340 | if (!( | |
341 | POKE_UINT8(( | |
342 | ((devicetype %(1<< 2))<<6) | | |
343 | ((source %(1<< 2))<<4) | | |
344 | ((port->p_med_power.priority %(1<< 4))<<0) )) && | |
345 | POKE_UINT16(port->p_med_power.val) && | |
346 | POKE_END_LLDP_TLV)) | |
347 | goto toobig; | |
348 | } | |
89840df0 VB |
349 | } |
350 | #endif | |
351 | ||
43c02e7b | 352 | /* END */ |
a8105c1b VB |
353 | if (!( |
354 | POKE_START_LLDP_TLV(LLDP_TLV_END) && | |
355 | POKE_END_LLDP_TLV)) | |
356 | goto toobig; | |
43c02e7b | 357 | |
6e75df87 VB |
358 | if (hardware->h_ops->send(global, hardware, |
359 | (char *)packet, pos - packet) == -1) { | |
f2dcb180 VB |
360 | LLOG_WARN("unable to send packet on real device for %s", |
361 | hardware->h_ifname); | |
362 | free(packet); | |
363 | return ENETDOWN; | |
43c02e7b VB |
364 | } |
365 | ||
f2dcb180 VB |
366 | hardware->h_tx_cnt++; |
367 | ||
a8105c1b VB |
368 | /* We assume that LLDP frame is the reference */ |
369 | if ((frame = (struct lldpd_frame*)malloc( | |
370 | sizeof(int) + pos - packet)) != NULL) { | |
371 | frame->size = pos - packet; | |
372 | memcpy(&frame->frame, packet, frame->size); | |
77507b69 VB |
373 | if ((hardware->h_lport.p_lastframe == NULL) || |
374 | (hardware->h_lport.p_lastframe->size != frame->size) || | |
375 | (memcmp(hardware->h_lport.p_lastframe->frame, frame->frame, | |
a8105c1b | 376 | frame->size) != 0)) { |
77507b69 VB |
377 | free(hardware->h_lport.p_lastframe); |
378 | hardware->h_lport.p_lastframe = frame; | |
379 | hardware->h_lport.p_lastchange = time(NULL); | |
43c02e7b | 380 | } else |
a8105c1b | 381 | free(frame); |
43c02e7b VB |
382 | } |
383 | ||
a8105c1b | 384 | free(packet); |
43c02e7b | 385 | return 0; |
a8105c1b VB |
386 | |
387 | toobig: | |
388 | free(packet); | |
389 | return E2BIG; | |
43c02e7b VB |
390 | } |
391 | ||
a8105c1b VB |
392 | #define CHECK_TLV_SIZE(x, name) \ |
393 | do { if (tlv_size < (x)) { \ | |
394 | LLOG_WARNX(name " TLV too short received on %s",\ | |
395 | hardware->h_ifname); \ | |
396 | goto malformed; \ | |
397 | } } while (0) | |
398 | ||
43c02e7b VB |
399 | int |
400 | lldp_decode(struct lldpd *cfg, char *frame, int s, | |
401 | struct lldpd_hardware *hardware, | |
402 | struct lldpd_chassis **newchassis, struct lldpd_port **newport) | |
403 | { | |
404 | struct lldpd_chassis *chassis; | |
405 | struct lldpd_port *port; | |
43c02e7b VB |
406 | const char lldpaddr[] = LLDP_MULTICAST_ADDR; |
407 | const char dot1[] = LLDP_TLV_ORG_DOT1; | |
408 | const char dot3[] = LLDP_TLV_ORG_DOT3; | |
6772b237 | 409 | const char med[] = LLDP_TLV_ORG_MED; |
a8105c1b VB |
410 | char orgid[3]; |
411 | int length, gotend = 0; | |
412 | int tlv_size, tlv_type, tlv_subtype; | |
413 | u_int8_t *pos, *tlv; | |
43c02e7b | 414 | char *b; |
a8105c1b | 415 | #ifdef ENABLE_DOT1 |
75b3469d VB |
416 | struct lldpd_vlan *vlan; |
417 | int vlan_len; | |
a8105c1b | 418 | #endif |
43c02e7b VB |
419 | |
420 | if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { | |
421 | LLOG_WARN("failed to allocate remote chassis"); | |
422 | return -1; | |
423 | } | |
424 | if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { | |
425 | LLOG_WARN("failed to allocate remote port"); | |
426 | free(chassis); | |
427 | return -1; | |
428 | } | |
a1347cd8 | 429 | #ifdef ENABLE_DOT1 |
43c02e7b | 430 | TAILQ_INIT(&port->p_vlans); |
a1347cd8 | 431 | #endif |
43c02e7b | 432 | |
a8105c1b VB |
433 | length = s; |
434 | pos = (u_int8_t*)frame; | |
435 | ||
436 | if (length < 2*ETH_ALEN + sizeof(u_int16_t)) { | |
43c02e7b VB |
437 | LLOG_WARNX("too short frame received on %s", hardware->h_ifname); |
438 | goto malformed; | |
439 | } | |
a8105c1b | 440 | if (PEEK_CMP(lldpaddr, ETH_ALEN) != 0) { |
43c02e7b VB |
441 | LLOG_INFO("frame not targeted at LLDP multicast address received on %s", |
442 | hardware->h_ifname); | |
443 | goto malformed; | |
444 | } | |
a8105c1b VB |
445 | PEEK_DISCARD(ETH_ALEN); /* Skip source address */ |
446 | if (PEEK_UINT16 != ETHERTYPE_LLDP) { | |
43c02e7b VB |
447 | LLOG_INFO("non LLDP frame received on %s", |
448 | hardware->h_ifname); | |
449 | goto malformed; | |
450 | } | |
451 | ||
a8105c1b VB |
452 | while (length && (!gotend)) { |
453 | if (length < 2) { | |
43c02e7b VB |
454 | LLOG_WARNX("tlv header too short received on %s", |
455 | hardware->h_ifname); | |
456 | goto malformed; | |
457 | } | |
a8105c1b VB |
458 | tlv_size = PEEK_UINT16; |
459 | tlv_type = tlv_size >> 9; | |
460 | tlv_size = tlv_size & 0x1ff; | |
461 | PEEK_SAVE(tlv); | |
462 | if (length < tlv_size) { | |
463 | LLOG_WARNX("frame too short for tlv received on %s", | |
43c02e7b VB |
464 | hardware->h_ifname); |
465 | goto malformed; | |
466 | } | |
a8105c1b | 467 | switch (tlv_type) { |
43c02e7b | 468 | case LLDP_TLV_END: |
a8105c1b | 469 | if (tlv_size != 0) { |
43c02e7b VB |
470 | LLOG_WARNX("lldp end received with size not null on %s", |
471 | hardware->h_ifname); | |
472 | goto malformed; | |
473 | } | |
a8105c1b | 474 | if (length) |
43c02e7b VB |
475 | LLOG_DEBUG("extra data after lldp end on %s", |
476 | hardware->h_ifname); | |
477 | gotend = 1; | |
478 | break; | |
479 | case LLDP_TLV_CHASSIS_ID: | |
480 | case LLDP_TLV_PORT_ID: | |
a8105c1b VB |
481 | CHECK_TLV_SIZE(2, "Port Id"); |
482 | tlv_subtype = PEEK_UINT8; | |
483 | if ((tlv_subtype == 0) || (tlv_subtype > 7)) { | |
43c02e7b VB |
484 | LLOG_WARNX("unknown subtype for tlv id received on %s", |
485 | hardware->h_ifname); | |
486 | goto malformed; | |
487 | } | |
a8105c1b | 488 | if ((b = (char *)calloc(1, tlv_size - 1)) == NULL) { |
43c02e7b VB |
489 | LLOG_WARN("unable to allocate memory for id tlv " |
490 | "received on %s", | |
491 | hardware->h_ifname); | |
492 | goto malformed; | |
493 | } | |
a8105c1b VB |
494 | PEEK_BYTES(b, tlv_size - 1); |
495 | if (tlv_type == LLDP_TLV_PORT_ID) { | |
496 | port->p_id_subtype = tlv_subtype; | |
43c02e7b | 497 | port->p_id = b; |
a8105c1b | 498 | port->p_id_len = tlv_size - 1; |
43c02e7b | 499 | } else { |
a8105c1b | 500 | chassis->c_id_subtype = tlv_subtype; |
43c02e7b | 501 | chassis->c_id = b; |
a8105c1b | 502 | chassis->c_id_len = tlv_size - 1; |
43c02e7b | 503 | } |
43c02e7b VB |
504 | break; |
505 | case LLDP_TLV_TTL: | |
a8105c1b VB |
506 | CHECK_TLV_SIZE(2, "TTL"); |
507 | chassis->c_ttl = PEEK_UINT16; | |
43c02e7b VB |
508 | break; |
509 | case LLDP_TLV_PORT_DESCR: | |
510 | case LLDP_TLV_SYSTEM_NAME: | |
511 | case LLDP_TLV_SYSTEM_DESCR: | |
a8105c1b | 512 | if (tlv_size < 1) { |
a700e935 | 513 | LLOG_DEBUG("empty tlv received on %s", |
43c02e7b | 514 | hardware->h_ifname); |
a700e935 | 515 | break; |
43c02e7b | 516 | } |
a8105c1b | 517 | if ((b = (char *)calloc(1, tlv_size + 1)) == NULL) { |
43c02e7b VB |
518 | LLOG_WARN("unable to allocate memory for string tlv " |
519 | "received on %s", | |
520 | hardware->h_ifname); | |
521 | goto malformed; | |
522 | } | |
a8105c1b VB |
523 | PEEK_BYTES(b, tlv_size); |
524 | if (tlv_type == LLDP_TLV_PORT_DESCR) | |
43c02e7b | 525 | port->p_descr = b; |
a8105c1b | 526 | else if (tlv_type == LLDP_TLV_SYSTEM_NAME) |
43c02e7b VB |
527 | chassis->c_name = b; |
528 | else chassis->c_descr = b; | |
529 | break; | |
530 | case LLDP_TLV_SYSTEM_CAP: | |
a8105c1b VB |
531 | CHECK_TLV_SIZE(4, "System capabilities"); |
532 | chassis->c_cap_available = PEEK_UINT16; | |
533 | chassis->c_cap_enabled = PEEK_UINT16; | |
43c02e7b VB |
534 | break; |
535 | case LLDP_TLV_MGMT_ADDR: | |
a8105c1b | 536 | CHECK_TLV_SIZE(11, "Management address"); |
43c02e7b | 537 | if ((chassis->c_mgmt.s_addr == INADDR_ANY) && |
a8105c1b VB |
538 | (PEEK_UINT8 == 1+sizeof(struct in_addr)) && |
539 | (PEEK_UINT8 == LLDP_MGMT_ADDR_IP4)) { | |
43c02e7b | 540 | /* We have an IPv4 address, we ignore anything else */ |
a8105c1b | 541 | PEEK_BYTES(&chassis->c_mgmt, sizeof(struct in_addr)); |
43c02e7b VB |
542 | chassis->c_mgmt_if = 0; |
543 | /* We only handle ifIndex subtype */ | |
a8105c1b VB |
544 | if (PEEK_UINT8 == LLDP_MGMT_IFACE_IFINDEX) |
545 | chassis->c_mgmt_if = PEEK_UINT32; | |
43c02e7b | 546 | } |
43c02e7b VB |
547 | break; |
548 | case LLDP_TLV_ORG: | |
a8105c1b VB |
549 | CHECK_TLV_SIZE(4, "Organisational"); |
550 | PEEK_BYTES(orgid, sizeof(orgid)); | |
551 | tlv_subtype = PEEK_UINT8; | |
552 | if (memcmp(dot1, orgid, sizeof(orgid)) == 0) { | |
a1347cd8 | 553 | #ifndef ENABLE_DOT1 |
a1347cd8 VB |
554 | hardware->h_rx_unrecognized_cnt++; |
555 | #else | |
43c02e7b | 556 | /* Dot1 */ |
a8105c1b | 557 | switch (tlv_subtype) { |
75b3469d | 558 | case LLDP_TLV_DOT1_VLANNAME: |
a8105c1b | 559 | CHECK_TLV_SIZE(7, "VLAN"); |
43c02e7b VB |
560 | if ((vlan = (struct lldpd_vlan *)calloc(1, |
561 | sizeof(struct lldpd_vlan))) == NULL) { | |
efe3f9b0 VB |
562 | LLOG_WARN("unable to alloc vlan " |
563 | "structure for " | |
43c02e7b VB |
564 | "tlv received on %s", |
565 | hardware->h_ifname); | |
566 | goto malformed; | |
567 | } | |
a8105c1b VB |
568 | vlan->v_vid = PEEK_UINT16; |
569 | vlan_len = PEEK_UINT8; | |
570 | CHECK_TLV_SIZE(7 + vlan_len, "VLAN"); | |
43c02e7b VB |
571 | if ((vlan->v_name = |
572 | (char *)calloc(1, vlan_len + 1)) == NULL) { | |
573 | LLOG_WARN("unable to alloc vlan name for " | |
574 | "tlv received on %s", | |
575 | hardware->h_ifname); | |
576 | goto malformed; | |
577 | } | |
a8105c1b | 578 | PEEK_BYTES(vlan->v_name, vlan_len); |
43c02e7b VB |
579 | TAILQ_INSERT_TAIL(&port->p_vlans, |
580 | vlan, v_entries); | |
75b3469d VB |
581 | break; |
582 | case LLDP_TLV_DOT1_PVID: | |
a8105c1b VB |
583 | CHECK_TLV_SIZE(6, "PVID"); |
584 | port->p_pvid = PEEK_UINT16; | |
75b3469d VB |
585 | break; |
586 | default: | |
43c02e7b | 587 | /* Unknown Dot1 TLV, ignore it */ |
a1347cd8 VB |
588 | hardware->h_rx_unrecognized_cnt++; |
589 | } | |
590 | #endif | |
a8105c1b | 591 | } else if (memcmp(dot3, orgid, sizeof(orgid)) == 0) { |
a1347cd8 | 592 | #ifndef ENABLE_DOT3 |
a1347cd8 VB |
593 | hardware->h_rx_unrecognized_cnt++; |
594 | #else | |
43c02e7b | 595 | /* Dot3 */ |
a8105c1b | 596 | switch (tlv_subtype) { |
43c02e7b | 597 | case LLDP_TLV_DOT3_MAC: |
a8105c1b | 598 | CHECK_TLV_SIZE(9, "MAC/PHY"); |
3fd015c0 VB |
599 | port->p_macphy.autoneg_support = PEEK_UINT8; |
600 | port->p_macphy.autoneg_enabled = | |
befbdf89 | 601 | port->p_macphy.autoneg_support & 0x2; |
3fd015c0 | 602 | port->p_macphy.autoneg_support = |
befbdf89 | 603 | port->p_macphy.autoneg_support & 0x1; |
3fd015c0 | 604 | port->p_macphy.autoneg_advertised = |
a8105c1b | 605 | PEEK_UINT16; |
3fd015c0 | 606 | port->p_macphy.mau_type = PEEK_UINT16; |
43c02e7b VB |
607 | break; |
608 | case LLDP_TLV_DOT3_LA: | |
a8105c1b | 609 | CHECK_TLV_SIZE(9, "Link aggregation"); |
a204514f | 610 | PEEK_DISCARD_UINT8; |
a8105c1b | 611 | port->p_aggregid = PEEK_UINT32; |
43c02e7b | 612 | break; |
548109b2 | 613 | case LLDP_TLV_DOT3_MFS: |
a8105c1b VB |
614 | CHECK_TLV_SIZE(6, "MFS"); |
615 | port->p_mfs = PEEK_UINT16; | |
548109b2 | 616 | break; |
befbdf89 VB |
617 | case LLDP_TLV_DOT3_POWER: |
618 | CHECK_TLV_SIZE(7, "Power"); | |
619 | port->p_power.devicetype = PEEK_UINT8; | |
620 | port->p_power.supported = | |
621 | port->p_power.devicetype & 0x2; | |
622 | port->p_power.enabled = | |
623 | port->p_power.devicetype & 0x4; | |
624 | port->p_power.paircontrol = | |
625 | port->p_power.devicetype & 0x8; | |
626 | port->p_power.devicetype = | |
627 | (port->p_power.devicetype & 0x1)? | |
628 | LLDP_DOT3_POWER_PSE:LLDP_DOT3_POWER_PD; | |
629 | port->p_power.pairs = PEEK_UINT8; | |
630 | port->p_power.class = PEEK_UINT8; | |
608cb51c VB |
631 | /* 802.3at? */ |
632 | if (tlv_size >= 12) { | |
633 | port->p_power.powertype = PEEK_UINT8; | |
634 | port->p_power.source = | |
635 | (port->p_power.powertype & (1<<5 | 1<<4)) >> 4; | |
636 | port->p_power.priority = | |
637 | (port->p_power.powertype & (1<<1 | 1<<0)); | |
638 | port->p_power.powertype = | |
639 | (port->p_power.powertype & (1<<7))? | |
640 | LLDP_DOT3_POWER_8023AT_TYPE1: | |
641 | LLDP_DOT3_POWER_8023AT_TYPE2; | |
642 | port->p_power.requested = PEEK_UINT16; | |
643 | port->p_power.allocated = PEEK_UINT16; | |
644 | } else | |
645 | port->p_power.powertype = | |
646 | LLDP_DOT3_POWER_8023AT_OFF; | |
befbdf89 | 647 | break; |
43c02e7b VB |
648 | default: |
649 | /* Unknown Dot3 TLV, ignore it */ | |
37387046 | 650 | hardware->h_rx_unrecognized_cnt++; |
43c02e7b | 651 | } |
a1347cd8 | 652 | #endif |
a8105c1b | 653 | } else if (memcmp(med, orgid, sizeof(orgid)) == 0) { |
6772b237 VB |
654 | /* LLDP-MED */ |
655 | #ifndef ENABLE_LLDPMED | |
6772b237 VB |
656 | hardware->h_rx_unrecognized_cnt++; |
657 | #else | |
e3a44efb VB |
658 | u_int32_t policy; |
659 | int loctype; | |
a8105c1b | 660 | int power; |
e3a44efb | 661 | |
a8105c1b | 662 | switch (tlv_subtype) { |
6772b237 | 663 | case LLDP_TLV_MED_CAP: |
a8105c1b VB |
664 | CHECK_TLV_SIZE(7, "LLDP-MED capabilities"); |
665 | chassis->c_med_cap_available = PEEK_UINT16; | |
666 | chassis->c_med_type = PEEK_UINT8; | |
740593ff | 667 | port->p_med_cap_enabled |= |
40ecae87 | 668 | LLDPMED_CAP_CAP; |
6772b237 | 669 | break; |
efe3f9b0 | 670 | case LLDP_TLV_MED_POLICY: |
a8105c1b VB |
671 | CHECK_TLV_SIZE(8, "LLDP-MED policy"); |
672 | policy = PEEK_UINT32; | |
e3a44efb VB |
673 | if (((policy >> 24) < 1) || |
674 | ((policy >> 24) > LLDPMED_APPTYPE_LAST)) { | |
675 | LLOG_INFO("unknown policy field %d " | |
676 | "received on %s", | |
e51da5ec | 677 | policy, |
e3a44efb | 678 | hardware->h_ifname); |
e3a44efb VB |
679 | break; |
680 | } | |
740593ff | 681 | port->p_med_policy[(policy >> 24) - 1].type = |
e3a44efb | 682 | (policy >> 24); |
740593ff | 683 | port->p_med_policy[(policy >> 24) - 1].unknown = |
e3a44efb | 684 | ((policy & 0x800000) != 0); |
740593ff | 685 | port->p_med_policy[(policy >> 24) - 1].tagged = |
e3a44efb | 686 | ((policy & 0x400000) != 0); |
740593ff | 687 | port->p_med_policy[(policy >> 24) - 1].vid = |
e3a44efb | 688 | (policy & 0x001FFE00) >> 9; |
740593ff | 689 | port->p_med_policy[(policy >> 24) - 1].priority = |
e3a44efb | 690 | (policy & 0x1C0) >> 6; |
740593ff | 691 | port->p_med_policy[(policy >> 24) - 1].dscp = |
e3a44efb | 692 | policy & 0x3F; |
740593ff | 693 | port->p_med_cap_enabled |= |
40ecae87 | 694 | LLDPMED_CAP_POLICY; |
efe3f9b0 VB |
695 | break; |
696 | case LLDP_TLV_MED_LOCATION: | |
a8105c1b VB |
697 | CHECK_TLV_SIZE(5, "LLDP-MED Location"); |
698 | loctype = PEEK_UINT8; | |
699 | if ((loctype < 1) || | |
700 | (loctype > LLDPMED_LOCFORMAT_LAST)) { | |
e3a44efb VB |
701 | LLOG_INFO("unknown location type " |
702 | "received on %s", | |
703 | hardware->h_ifname); | |
e3a44efb VB |
704 | break; |
705 | } | |
740593ff | 706 | if ((port->p_med_location[loctype - 1].data = |
a8105c1b | 707 | (char*)malloc(tlv_size - 5)) == NULL) { |
efe3f9b0 VB |
708 | LLOG_WARN("unable to allocate memory " |
709 | "for LLDP-MED location for " | |
710 | "frame received on %s", | |
711 | hardware->h_ifname); | |
712 | goto malformed; | |
713 | } | |
a8105c1b VB |
714 | PEEK_BYTES(port->p_med_location[loctype - 1].data, |
715 | tlv_size - 5); | |
740593ff | 716 | port->p_med_location[loctype - 1].data_len = |
a8105c1b | 717 | tlv_size - 5; |
740593ff | 718 | port->p_med_location[loctype - 1].format = loctype; |
740593ff | 719 | port->p_med_cap_enabled |= |
40ecae87 | 720 | LLDPMED_CAP_LOCATION; |
efe3f9b0 VB |
721 | break; |
722 | case LLDP_TLV_MED_MDI: | |
a8105c1b VB |
723 | CHECK_TLV_SIZE(7, "LLDP-MED PoE-MDI"); |
724 | power = PEEK_UINT8; | |
725 | switch (power & 0xC0) { | |
994812b9 | 726 | case 0x0: |
6d08df0e | 727 | port->p_med_power.devicetype = LLDPMED_POW_TYPE_PSE; |
740593ff | 728 | port->p_med_cap_enabled |= |
a49b3a79 | 729 | LLDPMED_CAP_MDI_PSE; |
a8105c1b | 730 | switch (power & 0x30) { |
994812b9 | 731 | case 0x0: |
6d08df0e | 732 | port->p_med_power.source = |
994812b9 VB |
733 | LLDPMED_POW_SOURCE_UNKNOWN; |
734 | break; | |
735 | case 0x10: | |
6d08df0e | 736 | port->p_med_power.source = |
994812b9 VB |
737 | LLDPMED_POW_SOURCE_PRIMARY; |
738 | break; | |
739 | case 0x20: | |
6d08df0e | 740 | port->p_med_power.source = |
994812b9 VB |
741 | LLDPMED_POW_SOURCE_BACKUP; |
742 | break; | |
743 | default: | |
6d08df0e | 744 | port->p_med_power.source = |
994812b9 VB |
745 | LLDPMED_POW_SOURCE_RESERVED; |
746 | } | |
747 | break; | |
748 | case 0x40: | |
6d08df0e | 749 | port->p_med_power.devicetype = LLDPMED_POW_TYPE_PD; |
740593ff | 750 | port->p_med_cap_enabled |= |
a49b3a79 | 751 | LLDPMED_CAP_MDI_PD; |
a8105c1b | 752 | switch (power & 0x30) { |
994812b9 | 753 | case 0x0: |
6d08df0e | 754 | port->p_med_power.source = |
994812b9 VB |
755 | LLDPMED_POW_SOURCE_UNKNOWN; |
756 | break; | |
757 | case 0x10: | |
6d08df0e | 758 | port->p_med_power.source = |
994812b9 VB |
759 | LLDPMED_POW_SOURCE_PSE; |
760 | break; | |
761 | case 0x20: | |
6d08df0e | 762 | port->p_med_power.source = |
994812b9 VB |
763 | LLDPMED_POW_SOURCE_LOCAL; |
764 | break; | |
765 | default: | |
6d08df0e | 766 | port->p_med_power.source = |
994812b9 VB |
767 | LLDPMED_POW_SOURCE_BOTH; |
768 | } | |
769 | break; | |
770 | default: | |
6d08df0e | 771 | port->p_med_power.devicetype = |
994812b9 VB |
772 | LLDPMED_POW_TYPE_RESERVED; |
773 | } | |
009ddd23 VB |
774 | if (((power & 0x0F) < 0) || |
775 | ((power & 0x0F) > LLDPMED_POW_PRIO_LOW)) | |
6d08df0e | 776 | port->p_med_power.priority = |
994812b9 | 777 | LLDPMED_POW_PRIO_UNKNOWN; |
009ddd23 | 778 | else |
6d08df0e | 779 | port->p_med_power.priority = |
009ddd23 | 780 | power & 0x0F; |
6d08df0e | 781 | port->p_med_power.val = PEEK_UINT16; |
efe3f9b0 | 782 | break; |
6772b237 VB |
783 | case LLDP_TLV_MED_IV_HW: |
784 | case LLDP_TLV_MED_IV_SW: | |
785 | case LLDP_TLV_MED_IV_FW: | |
786 | case LLDP_TLV_MED_IV_SN: | |
787 | case LLDP_TLV_MED_IV_MANUF: | |
788 | case LLDP_TLV_MED_IV_MODEL: | |
789 | case LLDP_TLV_MED_IV_ASSET: | |
a8105c1b | 790 | if (tlv_size <= 4) |
6772b237 VB |
791 | b = NULL; |
792 | else { | |
a8105c1b | 793 | if ((b = (char*)malloc(tlv_size - 3)) == |
efe3f9b0 VB |
794 | NULL) { |
795 | LLOG_WARN("unable to allocate " | |
796 | "memory for LLDP-MED " | |
797 | "inventory for frame " | |
798 | "received on %s", | |
799 | hardware->h_ifname); | |
800 | goto malformed; | |
801 | } | |
a8105c1b VB |
802 | PEEK_BYTES(b, tlv_size - 4); |
803 | b[tlv_size - 4] = '\0'; | |
6772b237 | 804 | } |
a8105c1b | 805 | switch (tlv_subtype) { |
6772b237 VB |
806 | case LLDP_TLV_MED_IV_HW: |
807 | chassis->c_med_hw = b; | |
808 | break; | |
809 | case LLDP_TLV_MED_IV_FW: | |
810 | chassis->c_med_fw = b; | |
811 | break; | |
812 | case LLDP_TLV_MED_IV_SW: | |
813 | chassis->c_med_sw = b; | |
814 | break; | |
815 | case LLDP_TLV_MED_IV_SN: | |
816 | chassis->c_med_sn = b; | |
817 | break; | |
818 | case LLDP_TLV_MED_IV_MANUF: | |
819 | chassis->c_med_manuf = b; | |
820 | break; | |
821 | case LLDP_TLV_MED_IV_MODEL: | |
397dee69 | 822 | chassis->c_med_model = b; |
6772b237 VB |
823 | break; |
824 | case LLDP_TLV_MED_IV_ASSET: | |
72c4c96f | 825 | chassis->c_med_asset = b; |
6772b237 VB |
826 | break; |
827 | default: | |
828 | LLOG_WARNX("should not be there!"); | |
829 | free(b); | |
830 | break; | |
831 | } | |
740593ff | 832 | port->p_med_cap_enabled |= |
40ecae87 | 833 | LLDPMED_CAP_IV; |
6772b237 VB |
834 | break; |
835 | default: | |
836 | /* Unknown LLDP MED, ignore it */ | |
6772b237 VB |
837 | hardware->h_rx_unrecognized_cnt++; |
838 | } | |
839 | #endif /* ENABLE_LLDPMED */ | |
43c02e7b | 840 | } else { |
9ca19b69 | 841 | LLOG_INFO("unknown org tlv received on %s", |
43c02e7b | 842 | hardware->h_ifname); |
37387046 | 843 | hardware->h_rx_unrecognized_cnt++; |
43c02e7b VB |
844 | } |
845 | break; | |
846 | default: | |
847 | LLOG_WARNX("unknown tlv (%d) received on %s", | |
a8105c1b VB |
848 | tlv_type, hardware->h_ifname); |
849 | goto malformed; | |
850 | } | |
851 | if (pos > tlv + tlv_size) { | |
852 | LLOG_WARNX("BUG: already past TLV!"); | |
43c02e7b VB |
853 | goto malformed; |
854 | } | |
a8105c1b | 855 | PEEK_DISCARD(tlv + tlv_size - pos); |
43c02e7b VB |
856 | } |
857 | ||
858 | /* Some random check */ | |
859 | if ((chassis->c_id == NULL) || | |
860 | (port->p_id == NULL) || | |
861 | (chassis->c_ttl == 0) || | |
862 | (gotend == 0)) { | |
863 | LLOG_WARNX("some mandatory tlv are missing for frame received on %s", | |
864 | hardware->h_ifname); | |
865 | goto malformed; | |
866 | } | |
867 | #define NOTRECEIVED "Not received" | |
868 | if (chassis->c_name == NULL) { | |
869 | if ((chassis->c_name = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) { | |
870 | LLOG_WARNX("unable to allocate null chassis name"); | |
871 | goto malformed; | |
872 | } | |
873 | memcpy(chassis->c_name, NOTRECEIVED, strlen(NOTRECEIVED)); | |
874 | } | |
875 | if (chassis->c_descr == NULL) { | |
876 | if ((chassis->c_descr = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) { | |
877 | LLOG_WARNX("unable to allocate null chassis description"); | |
878 | goto malformed; | |
879 | } | |
880 | memcpy(chassis->c_descr, NOTRECEIVED, strlen(NOTRECEIVED)); | |
881 | } | |
882 | if (port->p_descr == NULL) { | |
883 | if ((port->p_descr = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) { | |
884 | LLOG_WARNX("unable to allocate null port description"); | |
885 | goto malformed; | |
886 | } | |
887 | memcpy(port->p_descr, NOTRECEIVED, strlen(NOTRECEIVED)); | |
888 | } | |
889 | *newchassis = chassis; | |
890 | *newport = port; | |
891 | return 1; | |
892 | malformed: | |
77507b69 | 893 | lldpd_chassis_cleanup(chassis, 1); |
9898ac07 | 894 | lldpd_port_cleanup(cfg, port, 1); |
4e624dc2 | 895 | free(port); |
43c02e7b VB |
896 | return -1; |
897 | } |