]>
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" | |
18 | ||
19 | #include <unistd.h> | |
20 | #include <errno.h> | |
21 | #include <time.h> | |
22 | #include <sys/types.h> | |
23 | #include <sys/socket.h> | |
24 | #include <sys/ioctl.h> | |
25 | #include <netpacket/packet.h> | |
26 | #include <linux/sockios.h> | |
27 | ||
28 | int | |
29 | lldp_send(struct lldpd *global, struct lldpd_chassis *chassis, | |
30 | struct lldpd_hardware *hardware) | |
31 | { | |
32 | struct ether_header eh; | |
33 | const u_int8_t mcastaddr[] = LLDP_MULTICAST_ADDR; | |
43c02e7b | 34 | struct iovec *iov = NULL; |
43c02e7b VB |
35 | struct lldp_id chid, pid; |
36 | struct lldp_ttl ttl; | |
37 | struct lldp_end end; | |
38 | struct lldp_string name; | |
39 | struct lldp_string descr; | |
40 | struct lldp_string str; | |
41 | struct lldp_cap cap; | |
42 | struct lldp_mgmt mgmt; | |
a1347cd8 VB |
43 | #ifdef ENABLE_DOT1 |
44 | const u_int8_t dot1[] = LLDP_TLV_ORG_DOT1; | |
45 | struct lldp_vlan *ovlan = NULL; | |
46 | int v; | |
47 | struct lldpd_vlan *vlan; | |
48 | #endif | |
49 | #ifdef ENABLE_DOT3 | |
50 | const u_int8_t dot3[] = LLDP_TLV_ORG_DOT3; | |
43c02e7b VB |
51 | struct lldp_aggreg aggreg; |
52 | struct lldp_macphy macphy; | |
a1347cd8 | 53 | #endif |
89840df0 | 54 | #ifdef ENABLE_LLDPMED |
115ff55c | 55 | int i; |
89840df0 VB |
56 | const u_int8_t med[] = LLDP_TLV_ORG_MED; |
57 | struct lldpmed_cap medcap; | |
58 | struct lldp_org medhw, medfw, medsw, medsn, | |
115ff55c | 59 | medmodel, medasset, medmanuf, medloc[3]; |
89840df0 | 60 | #endif |
43c02e7b | 61 | struct lldpd_port *port = &hardware->h_lport; |
a1347cd8 | 62 | u_int c = -1, len = 0; |
43c02e7b VB |
63 | struct lldpd_frame *buffer; |
64 | ||
65 | #define IOV_NEW \ | |
66 | if ((iov = (struct iovec*)realloc(iov, (++c + 1) * \ | |
67 | sizeof(struct iovec))) == NULL) \ | |
68 | fatal(NULL); | |
69 | ||
70 | /* Ethernet header */ | |
71 | memset(&eh, 0, sizeof(eh)); | |
72 | memcpy(&eh.ether_shost, &hardware->h_lladdr, | |
73 | sizeof(eh.ether_shost)); | |
74 | memcpy(&eh.ether_dhost, &mcastaddr, | |
75 | sizeof(eh.ether_dhost)); | |
76 | eh.ether_type = htons(ETHERTYPE_LLDP); | |
77 | IOV_NEW; | |
78 | iov[c].iov_base = &eh; | |
79 | iov[c].iov_len = sizeof(struct ether_header); | |
80 | ||
81 | /* Chassis ID */ | |
82 | memset(&chid, 0, sizeof(chid)); | |
83 | len = chassis->c_id_len + sizeof(chid); | |
84 | chid.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_CHASSIS_ID, | |
85 | len - sizeof(struct lldp_tlv_head)); | |
86 | chid.tlv_id_subtype = chassis->c_id_subtype; | |
87 | IOV_NEW; | |
88 | iov[c].iov_base = &chid; | |
89 | iov[c].iov_len = sizeof(chid); | |
90 | IOV_NEW; | |
91 | iov[c].iov_base = chassis->c_id; | |
92 | iov[c].iov_len = chassis->c_id_len; | |
93 | ||
94 | /* Port ID */ | |
95 | memset(&pid, 0, sizeof(pid)); | |
96 | len = port->p_id_len + sizeof(pid); | |
97 | pid.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_PORT_ID, | |
98 | len - sizeof(struct lldp_tlv_head)); | |
99 | pid.tlv_id_subtype = port->p_id_subtype; | |
100 | IOV_NEW; | |
101 | iov[c].iov_base = &pid; | |
102 | iov[c].iov_len = sizeof(pid); | |
103 | IOV_NEW; | |
104 | iov[c].iov_base = port->p_id; | |
105 | iov[c].iov_len = port->p_id_len; | |
106 | ||
107 | /* Time to live */ | |
108 | memset(&ttl, 0, sizeof(ttl)); | |
109 | len = sizeof(ttl); | |
110 | ttl.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_TTL, | |
111 | len - sizeof(struct lldp_tlv_head)); | |
112 | ttl.tlv_ttl = htons(chassis->c_ttl); | |
113 | IOV_NEW; | |
114 | iov[c].iov_base = &ttl; | |
115 | iov[c].iov_len = sizeof(ttl); | |
116 | ||
117 | /* System name */ | |
118 | memset(&name, 0, sizeof(name)); | |
119 | len = sizeof(name) + strlen(chassis->c_name); | |
120 | name.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_SYSTEM_NAME, | |
121 | len - sizeof(struct lldp_tlv_head)); | |
122 | IOV_NEW; | |
123 | iov[c].iov_base = &name; | |
124 | iov[c].iov_len = sizeof(name); | |
125 | IOV_NEW; | |
126 | iov[c].iov_base = chassis->c_name; | |
127 | iov[c].iov_len = strlen(chassis->c_name); | |
128 | ||
129 | /* System description */ | |
130 | memset(&descr, 0, sizeof(descr)); | |
131 | len = sizeof(descr) + strlen(chassis->c_descr); | |
132 | descr.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_SYSTEM_DESCR, | |
133 | len - sizeof(struct lldp_tlv_head)); | |
134 | IOV_NEW; | |
135 | iov[c].iov_base = &descr; | |
136 | iov[c].iov_len = sizeof(descr); | |
137 | IOV_NEW; | |
138 | iov[c].iov_base = chassis->c_descr; | |
139 | iov[c].iov_len = strlen(chassis->c_descr); | |
140 | ||
141 | /* System capabilities */ | |
142 | memset(&cap, 0, sizeof(cap)); | |
143 | len = sizeof(cap); | |
144 | cap.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_SYSTEM_CAP, | |
145 | len - sizeof(struct lldp_tlv_head)); | |
146 | cap.tlv_cap_available = htons(chassis->c_cap_available); | |
147 | cap.tlv_cap_enabled = htons(chassis->c_cap_enabled); | |
148 | IOV_NEW; | |
149 | iov[c].iov_base = ∩ | |
150 | iov[c].iov_len = len; | |
151 | ||
152 | if (chassis->c_mgmt.s_addr != INADDR_ANY) { | |
153 | /* Management address */ | |
154 | memset(&mgmt, 0, sizeof(mgmt)); | |
155 | len = sizeof(mgmt); | |
156 | mgmt.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_MGMT_ADDR, | |
157 | len - sizeof(struct lldp_tlv_head)); | |
158 | mgmt.mgmt_len = sizeof(struct in_addr) + sizeof(u_int8_t); | |
159 | mgmt.mgmt_subtype = LLDP_MGMT_ADDR_IP4; | |
160 | memcpy(&mgmt.mgmt_addr, &chassis->c_mgmt, | |
161 | sizeof(struct in_addr)); | |
162 | ||
163 | /* Interface port type, OID */ | |
164 | if (chassis->c_mgmt_if == 0) | |
165 | mgmt.mgmt_iface_subtype = | |
166 | LLDP_MGMT_IFACE_UNKNOWN; | |
167 | else { | |
168 | mgmt.mgmt_iface_subtype = | |
169 | LLDP_MGMT_IFACE_IFINDEX; | |
170 | mgmt.mgmt_iface_id = | |
171 | htonl(chassis->c_mgmt_if); | |
172 | } | |
173 | IOV_NEW; | |
174 | iov[c].iov_base = &mgmt; | |
175 | iov[c].iov_len = len; | |
176 | } | |
177 | ||
178 | /* Port description */ | |
179 | memset(&str, 0, sizeof(str)); | |
180 | len = sizeof(str) + strlen(port->p_descr); | |
181 | str.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_PORT_DESCR, | |
182 | len - sizeof(struct lldp_tlv_head)); | |
183 | IOV_NEW; | |
184 | iov[c].iov_base = &str; | |
185 | iov[c].iov_len = sizeof(str); | |
186 | IOV_NEW; | |
187 | iov[c].iov_base = port->p_descr; | |
188 | iov[c].iov_len = strlen(port->p_descr); | |
189 | ||
a1347cd8 | 190 | #ifdef ENABLE_DOT1 |
43c02e7b VB |
191 | /* VLANs */ |
192 | v = 0; | |
193 | TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) | |
194 | v++; | |
195 | if ((v > 0) && | |
196 | ((ovlan = (struct lldp_vlan*)malloc(v*sizeof(struct lldp_vlan))) == NULL)) | |
197 | LLOG_WARN("no room for vlans"); | |
198 | else { | |
199 | TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) { | |
200 | v--; | |
201 | memset(&ovlan[v], 0, sizeof(ovlan[v])); | |
202 | ovlan[v].tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG, | |
203 | sizeof(ovlan[v].tlv_org_id) + | |
204 | sizeof(ovlan[v].tlv_org_subtype) + sizeof(ovlan[v].vid) + | |
205 | sizeof(ovlan[v].len) + strlen(vlan->v_name)); | |
206 | memcpy(ovlan[v].tlv_org_id, dot1, sizeof(ovlan[v].tlv_org_id)); | |
207 | ovlan[v].tlv_org_subtype = LLDP_TLV_DOT1_VLANNAME; | |
208 | ovlan[v].vid = htons(vlan->v_vid); | |
209 | ovlan[v].len = strlen(vlan->v_name); | |
210 | IOV_NEW; | |
211 | iov[c].iov_base = &ovlan[v]; | |
212 | iov[c].iov_len = sizeof(ovlan[v]); | |
213 | IOV_NEW; | |
214 | iov[c].iov_base = vlan->v_name; | |
215 | iov[c].iov_len = strlen(vlan->v_name); | |
216 | } | |
217 | } | |
a1347cd8 | 218 | #endif |
43c02e7b | 219 | |
a1347cd8 | 220 | #ifdef ENABLE_DOT3 |
43c02e7b VB |
221 | /* Aggregation status */ |
222 | memset(&aggreg, 0, sizeof(aggreg)); | |
223 | aggreg.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG, | |
224 | sizeof(aggreg.tlv_org_id) + | |
225 | sizeof(aggreg.tlv_org_subtype) + | |
226 | sizeof(aggreg.status) + sizeof(aggreg.id)); | |
227 | memcpy(aggreg.tlv_org_id, dot3, sizeof(aggreg.tlv_org_id)); | |
228 | aggreg.tlv_org_subtype = LLDP_TLV_DOT3_LA; | |
229 | aggreg.status = (port->p_aggregid) ? 3:1; /* Bit 0 = capability ; Bit 1 = status */ | |
230 | aggreg.id = htonl(port->p_aggregid); | |
231 | IOV_NEW; | |
232 | iov[c].iov_base = &aggreg; | |
233 | iov[c].iov_len = sizeof(aggreg); | |
234 | ||
235 | /* MAC/PHY */ | |
236 | memset(&macphy, 0, sizeof(macphy)); | |
237 | macphy.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG, | |
238 | sizeof(macphy.tlv_org_id) + | |
239 | sizeof(macphy.tlv_org_subtype) + | |
240 | sizeof(macphy.autoneg) + sizeof(macphy.advertised) + | |
241 | sizeof(macphy.mau)); | |
242 | memcpy(macphy.tlv_org_id, dot3, sizeof(macphy.tlv_org_id)); | |
243 | macphy.tlv_org_subtype = LLDP_TLV_DOT3_MAC; | |
244 | macphy.autoneg = port->p_autoneg_support | | |
245 | (port->p_autoneg_enabled << 1); | |
246 | macphy.advertised = htons(port->p_autoneg_advertised); | |
247 | macphy.mau = htons(port->p_mau_type); | |
248 | IOV_NEW; | |
249 | iov[c].iov_base = &macphy; | |
250 | iov[c].iov_len = sizeof(macphy); | |
a1347cd8 | 251 | #endif |
43c02e7b | 252 | |
89840df0 | 253 | #ifdef ENABLE_LLDPMED |
40ecae87 | 254 | if (global->g_lchassis.c_med_cap_enabled) { |
89840df0 VB |
255 | /* LLDP-MED cap */ |
256 | memset(&medcap, 0, sizeof(medcap)); | |
257 | medcap.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG, | |
258 | sizeof(medcap.tlv_org_id) + | |
259 | sizeof(medcap.tlv_org_subtype) + | |
260 | sizeof(medcap.tlv_cap) + sizeof(medcap.tlv_type)); | |
261 | memcpy(medcap.tlv_org_id, med, sizeof(medcap.tlv_org_id)); | |
262 | medcap.tlv_org_subtype = LLDP_TLV_MED_CAP; | |
40ecae87 | 263 | medcap.tlv_cap = htons(global->g_lchassis.c_med_cap_available); |
89840df0 VB |
264 | medcap.tlv_type = global->g_lchassis.c_med_type; |
265 | IOV_NEW; | |
266 | iov[c].iov_base = &medcap; | |
267 | iov[c].iov_len = sizeof(medcap); | |
268 | ||
269 | /* LLDP-MED inventory */ | |
270 | #define LLDP_INVENTORY(value, target, subtype) \ | |
271 | if (value) { \ | |
272 | memset(&target, 0, sizeof(target)); \ | |
273 | len = (strlen(value)>32)?32:strlen(value); \ | |
274 | target.tlv_head.type_len = \ | |
275 | LLDP_TLV_HEAD(LLDP_TLV_ORG, \ | |
276 | sizeof(target.tlv_org_id) + \ | |
277 | sizeof(target.tlv_org_subtype) + \ | |
278 | len); \ | |
279 | memcpy(target.tlv_org_id, med, \ | |
280 | sizeof(target.tlv_org_id)); \ | |
281 | target.tlv_org_subtype = subtype; \ | |
282 | IOV_NEW; \ | |
283 | iov[c].iov_base = ⌖ \ | |
284 | iov[c].iov_len = sizeof(target); \ | |
285 | IOV_NEW; \ | |
286 | iov[c].iov_base = value; \ | |
287 | iov[c].iov_len = len; \ | |
288 | } | |
e809a587 | 289 | |
40ecae87 | 290 | if (global->g_lchassis.c_med_cap_enabled & LLDPMED_CAP_IV) { |
e809a587 VB |
291 | LLDP_INVENTORY(global->g_lchassis.c_med_hw, |
292 | medhw, LLDP_TLV_MED_IV_HW); | |
293 | LLDP_INVENTORY(global->g_lchassis.c_med_fw, | |
294 | medfw, LLDP_TLV_MED_IV_FW); | |
295 | LLDP_INVENTORY(global->g_lchassis.c_med_sw, | |
296 | medsw, LLDP_TLV_MED_IV_SW); | |
297 | LLDP_INVENTORY(global->g_lchassis.c_med_sn, | |
298 | medsn, LLDP_TLV_MED_IV_SN); | |
299 | LLDP_INVENTORY(global->g_lchassis.c_med_manuf, | |
300 | medmanuf, LLDP_TLV_MED_IV_MANUF); | |
301 | LLDP_INVENTORY(global->g_lchassis.c_med_model, | |
302 | medmodel, LLDP_TLV_MED_IV_MODEL); | |
303 | LLDP_INVENTORY(global->g_lchassis.c_med_asset, | |
304 | medasset, LLDP_TLV_MED_IV_ASSET); | |
305 | } | |
115ff55c VB |
306 | |
307 | /* LLDP-MED location */ | |
308 | for (i = 0; i < LLDPMED_LOCFORMAT_LAST; i++) { | |
309 | if (global->g_lchassis.c_med_location[i].format == i + 1) { | |
310 | memset(&medloc[i], 0, sizeof(struct lldp_org)); | |
311 | medloc[i].tlv_head.type_len = | |
312 | LLDP_TLV_HEAD(LLDP_TLV_ORG, | |
313 | sizeof(medloc[i].tlv_org_id) + | |
314 | sizeof(medloc[i].tlv_org_subtype) + 1 + | |
315 | global->g_lchassis.c_med_location[i].data_len); | |
316 | memcpy(medloc[i].tlv_org_id, med, | |
317 | sizeof(medloc[i].tlv_org_id)); | |
318 | medloc[i].tlv_org_subtype = LLDP_TLV_MED_LOCATION; | |
319 | IOV_NEW; | |
320 | iov[c].iov_base = &medloc[i]; | |
321 | iov[c].iov_len = sizeof(medloc[i]); | |
322 | IOV_NEW; | |
323 | iov[c].iov_base = | |
324 | &global->g_lchassis.c_med_location[i].format; | |
325 | iov[c].iov_len = 1; | |
326 | IOV_NEW; | |
327 | iov[c].iov_base = | |
328 | global->g_lchassis.c_med_location[i].data; | |
329 | iov[c].iov_len = | |
330 | global->g_lchassis.c_med_location[i].data_len; | |
331 | } | |
332 | } | |
89840df0 VB |
333 | } |
334 | #endif | |
335 | ||
43c02e7b VB |
336 | /* END */ |
337 | memset(&end, 0, sizeof(end)); | |
338 | IOV_NEW; | |
339 | iov[c].iov_base = &end; | |
340 | iov[c].iov_len = sizeof(end); | |
341 | ||
342 | c++; | |
343 | if (!global->g_multi || | |
344 | (hardware->h_mode == LLDPD_MODE_ANY) || | |
345 | (hardware->h_mode == LLDPD_MODE_LLDP)) { | |
346 | ||
347 | if (writev((hardware->h_raw_real > 0) ? hardware->h_raw_real : | |
348 | hardware->h_raw, iov, c) == -1) { | |
349 | LLOG_WARN("unable to send packet on real device for %s", | |
350 | hardware->h_ifname); | |
351 | free(iov); | |
a1347cd8 | 352 | #ifdef ENABLE_DOT1 |
43c02e7b | 353 | free(ovlan); |
a1347cd8 | 354 | #endif |
43c02e7b VB |
355 | return ENETDOWN; |
356 | } | |
357 | ||
358 | hardware->h_tx_cnt++; | |
359 | } | |
360 | ||
361 | iov_dump(&buffer, iov, c); | |
362 | free(iov); | |
a1347cd8 | 363 | #ifdef ENABLE_DOT1 |
43c02e7b | 364 | free(ovlan); |
a1347cd8 | 365 | #endif |
43c02e7b VB |
366 | if (buffer != NULL) { |
367 | ||
368 | /* We assume that LLDP frame is the reference */ | |
369 | if ((hardware->h_llastframe == NULL) || | |
370 | (hardware->h_llastframe->size != buffer->size) || | |
371 | (memcmp(hardware->h_llastframe->frame, buffer->frame, | |
372 | buffer->size) != 0)) { | |
373 | free(hardware->h_llastframe); | |
374 | hardware->h_llastframe = buffer; | |
375 | hardware->h_llastchange = time(NULL); | |
376 | } else | |
377 | free(buffer); | |
378 | } | |
379 | ||
380 | return 0; | |
381 | } | |
382 | ||
383 | int | |
384 | lldp_decode(struct lldpd *cfg, char *frame, int s, | |
385 | struct lldpd_hardware *hardware, | |
386 | struct lldpd_chassis **newchassis, struct lldpd_port **newport) | |
387 | { | |
388 | struct lldpd_chassis *chassis; | |
389 | struct lldpd_port *port; | |
390 | struct ether_header *ether; | |
391 | const char lldpaddr[] = LLDP_MULTICAST_ADDR; | |
392 | const char dot1[] = LLDP_TLV_ORG_DOT1; | |
393 | const char dot3[] = LLDP_TLV_ORG_DOT3; | |
6772b237 | 394 | const char med[] = LLDP_TLV_ORG_MED; |
43c02e7b VB |
395 | int f; /* Current position in frame */ |
396 | int size, type, subtype; /* TLV header */ | |
397 | char *b; | |
398 | int gotend = 0; | |
399 | ||
400 | if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { | |
401 | LLOG_WARN("failed to allocate remote chassis"); | |
402 | return -1; | |
403 | } | |
404 | if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { | |
405 | LLOG_WARN("failed to allocate remote port"); | |
406 | free(chassis); | |
407 | return -1; | |
408 | } | |
a1347cd8 | 409 | #ifdef ENABLE_DOT1 |
43c02e7b | 410 | TAILQ_INIT(&port->p_vlans); |
a1347cd8 | 411 | #endif |
43c02e7b VB |
412 | |
413 | if (s < sizeof(struct ether_header)) { | |
414 | LLOG_WARNX("too short frame received on %s", hardware->h_ifname); | |
415 | goto malformed; | |
416 | } | |
417 | ether = (struct ether_header *)frame; | |
418 | if (memcmp(ether->ether_dhost, lldpaddr, sizeof(lldpaddr)) != 0) { | |
419 | LLOG_INFO("frame not targeted at LLDP multicast address received on %s", | |
420 | hardware->h_ifname); | |
421 | goto malformed; | |
422 | } | |
423 | if (ETHERTYPE_LLDP != ntohs(ether->ether_type)) { | |
424 | LLOG_INFO("non LLDP frame received on %s", | |
425 | hardware->h_ifname); | |
426 | goto malformed; | |
427 | } | |
428 | ||
429 | f = sizeof(struct ether_header); | |
430 | while ((f < s) && (!gotend)) { | |
431 | if (f + 2 > s) { | |
432 | LLOG_WARNX("tlv header too short received on %s", | |
433 | hardware->h_ifname); | |
434 | goto malformed; | |
435 | } | |
436 | size = ntohs(*(u_int16_t*)(frame + f)) & 0x1ff; | |
437 | type = ntohs(*(u_int16_t*)(frame + f)) >> 9; | |
438 | f += 2; | |
439 | if (f + size > s) { | |
440 | LLOG_WARNX("tlv header too short received on %s", | |
441 | hardware->h_ifname); | |
442 | goto malformed; | |
443 | } | |
444 | switch (type) { | |
445 | case LLDP_TLV_END: | |
446 | if (size != 0) { | |
447 | LLOG_WARNX("lldp end received with size not null on %s", | |
448 | hardware->h_ifname); | |
449 | goto malformed; | |
450 | } | |
451 | if (f != s) | |
452 | LLOG_DEBUG("extra data after lldp end on %s", | |
453 | hardware->h_ifname); | |
454 | gotend = 1; | |
455 | break; | |
456 | case LLDP_TLV_CHASSIS_ID: | |
457 | case LLDP_TLV_PORT_ID: | |
458 | if (size < 2) { | |
459 | LLOG_WARNX("tlv id too small received on %s", | |
460 | hardware->h_ifname); | |
461 | goto malformed; | |
462 | } | |
463 | subtype = *(u_int8_t*)(frame + f); | |
464 | f++; | |
465 | if ((subtype == 0) || (subtype > 7)) { | |
466 | LLOG_WARNX("unknown subtype for tlv id received on %s", | |
467 | hardware->h_ifname); | |
468 | goto malformed; | |
469 | } | |
470 | if ((b = (char *)calloc(1, size - 1)) == NULL) { | |
471 | LLOG_WARN("unable to allocate memory for id tlv " | |
472 | "received on %s", | |
473 | hardware->h_ifname); | |
474 | goto malformed; | |
475 | } | |
476 | memcpy(b, frame + f, size - 1); | |
477 | if (type == LLDP_TLV_PORT_ID) { | |
478 | port->p_id_subtype = subtype; | |
479 | port->p_id = b; | |
480 | port->p_id_len = size - 1; | |
481 | } else { | |
482 | chassis->c_id_subtype = subtype; | |
483 | chassis->c_id = b; | |
484 | chassis->c_id_len = size - 1; | |
485 | } | |
486 | f += size - 1; | |
487 | break; | |
488 | case LLDP_TLV_TTL: | |
c790233e VB |
489 | if (size < 2) { |
490 | LLOG_WARNX("too short frame for ttl tlv received on %s", | |
43c02e7b VB |
491 | hardware->h_ifname); |
492 | goto malformed; | |
493 | } | |
494 | chassis->c_ttl = ntohs(*(u_int16_t*)(frame + f)); | |
c790233e | 495 | f += size; |
43c02e7b VB |
496 | break; |
497 | case LLDP_TLV_PORT_DESCR: | |
498 | case LLDP_TLV_SYSTEM_NAME: | |
499 | case LLDP_TLV_SYSTEM_DESCR: | |
500 | if (size < 1) { | |
a700e935 | 501 | LLOG_DEBUG("empty tlv received on %s", |
43c02e7b | 502 | hardware->h_ifname); |
a700e935 | 503 | break; |
43c02e7b VB |
504 | } |
505 | if ((b = (char *)calloc(1, size + 1)) == NULL) { | |
506 | LLOG_WARN("unable to allocate memory for string tlv " | |
507 | "received on %s", | |
508 | hardware->h_ifname); | |
509 | goto malformed; | |
510 | } | |
511 | memcpy(b, frame + f, size); | |
512 | f += size; | |
513 | if (type == LLDP_TLV_PORT_DESCR) | |
514 | port->p_descr = b; | |
515 | else if (type == LLDP_TLV_SYSTEM_NAME) | |
516 | chassis->c_name = b; | |
517 | else chassis->c_descr = b; | |
518 | break; | |
519 | case LLDP_TLV_SYSTEM_CAP: | |
c790233e VB |
520 | if (size < 4) { |
521 | LLOG_WARNX("too short system cap tlv " | |
efe3f9b0 | 522 | "received on %s", |
43c02e7b VB |
523 | hardware->h_ifname); |
524 | goto malformed; | |
525 | } | |
526 | chassis->c_cap_available = ntohs(*(u_int16_t*)(frame + f)); | |
527 | f += 2; | |
528 | chassis->c_cap_enabled = ntohs(*(u_int16_t*)(frame + f)); | |
c790233e | 529 | f += size - 2; |
43c02e7b VB |
530 | break; |
531 | case LLDP_TLV_MGMT_ADDR: | |
532 | if (size < 11) { | |
533 | LLOG_WARNX("too short management tlv received on %s", | |
534 | hardware->h_ifname); | |
535 | goto malformed; | |
536 | } | |
537 | if ((chassis->c_mgmt.s_addr == INADDR_ANY) && | |
538 | (*(u_int8_t*)(frame + f) == 5) && | |
539 | (*(u_int8_t*)(frame + f + 1) == 1)) { | |
540 | /* We have an IPv4 address, we ignore anything else */ | |
541 | memcpy(&chassis->c_mgmt, frame + f + 2, sizeof(struct in_addr)); | |
542 | chassis->c_mgmt_if = 0; | |
543 | /* We only handle ifIndex subtype */ | |
544 | if (*(u_int8_t*)(frame + f + 6) == LLDP_MGMT_IFACE_IFINDEX) | |
545 | chassis->c_mgmt_if = ntohl(*(u_int32_t*)(frame + f + 7)); | |
546 | } | |
547 | f += size; | |
548 | break; | |
549 | case LLDP_TLV_ORG: | |
550 | if (size < 4) { | |
551 | LLOG_WARNX("too short org tlv received on %s", | |
552 | hardware->h_ifname); | |
553 | goto malformed; | |
554 | } | |
555 | if (memcmp(dot1, frame + f, 3) == 0) { | |
a1347cd8 VB |
556 | #ifndef ENABLE_DOT1 |
557 | f += size; | |
558 | hardware->h_rx_unrecognized_cnt++; | |
559 | #else | |
43c02e7b VB |
560 | /* Dot1 */ |
561 | if ((*(u_int8_t*)(frame + f + 3)) == | |
562 | LLDP_TLV_DOT1_VLANNAME) { | |
563 | struct lldpd_vlan *vlan; | |
564 | int vlan_len; | |
565 | ||
566 | if ((size < 7) || | |
c790233e | 567 | (size < 7 + *(u_int8_t*)(frame + f + 6))) { |
efe3f9b0 VB |
568 | LLOG_WARNX("too short vlan tlv " |
569 | "received on %s", | |
43c02e7b VB |
570 | hardware->h_ifname); |
571 | goto malformed; | |
572 | } | |
573 | f += 4; | |
574 | if ((vlan = (struct lldpd_vlan *)calloc(1, | |
575 | sizeof(struct lldpd_vlan))) == NULL) { | |
efe3f9b0 VB |
576 | LLOG_WARN("unable to alloc vlan " |
577 | "structure for " | |
43c02e7b VB |
578 | "tlv received on %s", |
579 | hardware->h_ifname); | |
580 | goto malformed; | |
581 | } | |
582 | vlan->v_vid = ntohs(*(u_int16_t*)(frame + f)); | |
583 | f += 2; | |
584 | vlan_len = *(u_int8_t*)(frame + f); | |
585 | f += 1; | |
586 | if ((vlan->v_name = | |
587 | (char *)calloc(1, vlan_len + 1)) == NULL) { | |
588 | LLOG_WARN("unable to alloc vlan name for " | |
589 | "tlv received on %s", | |
590 | hardware->h_ifname); | |
591 | goto malformed; | |
592 | } | |
593 | memcpy(vlan->v_name, frame + f, | |
594 | vlan_len); | |
595 | TAILQ_INSERT_TAIL(&port->p_vlans, | |
596 | vlan, v_entries); | |
c790233e | 597 | f += size - 7; |
a1347cd8 | 598 | } else { |
43c02e7b VB |
599 | /* Unknown Dot1 TLV, ignore it */ |
600 | f += size; | |
a1347cd8 VB |
601 | hardware->h_rx_unrecognized_cnt++; |
602 | } | |
603 | #endif | |
43c02e7b | 604 | } else if (memcmp(dot3, frame + f, 3) == 0) { |
a1347cd8 VB |
605 | #ifndef ENABLE_DOT3 |
606 | f += size; | |
607 | hardware->h_rx_unrecognized_cnt++; | |
608 | #else | |
43c02e7b VB |
609 | /* Dot3 */ |
610 | subtype = *(u_int8_t*)(frame + f + 3); | |
611 | switch (subtype) { | |
612 | case LLDP_TLV_DOT3_MAC: | |
613 | f += 4; | |
c790233e | 614 | if (size < 9) { |
43c02e7b VB |
615 | LLOG_WARNX("too short mac/phy tlv " |
616 | "received on %s", | |
617 | hardware->h_ifname); | |
618 | goto malformed; | |
619 | } | |
620 | port->p_autoneg_support = | |
621 | *(u_int8_t*)(frame + f) && 0x1; | |
622 | port->p_autoneg_enabled = | |
623 | *(u_int8_t*)(frame + f) && 0x2; | |
624 | f += 1; | |
625 | port->p_autoneg_advertised = | |
626 | ntohs(*(u_int16_t*)(frame + f)); | |
627 | f += 2; | |
628 | port->p_mau_type = | |
629 | ntohs(*(u_int16_t*)(frame + f)); | |
c790233e | 630 | f += size - 7; |
43c02e7b VB |
631 | break; |
632 | case LLDP_TLV_DOT3_LA: | |
c790233e | 633 | if (size < 9) { |
43c02e7b VB |
634 | LLOG_WARNX("too short aggreg tlv " |
635 | "received on %s", | |
636 | hardware->h_ifname); | |
637 | goto malformed; | |
638 | } | |
639 | port->p_aggregid = | |
640 | ntohl(*(u_int32_t*)(frame + f + 5)); | |
c790233e | 641 | f += size; |
43c02e7b VB |
642 | break; |
643 | default: | |
644 | /* Unknown Dot3 TLV, ignore it */ | |
645 | f += size; | |
37387046 | 646 | hardware->h_rx_unrecognized_cnt++; |
43c02e7b | 647 | } |
a1347cd8 | 648 | #endif |
6772b237 VB |
649 | } else if (memcmp(med, frame + f, 3) == 0) { |
650 | /* LLDP-MED */ | |
651 | #ifndef ENABLE_LLDPMED | |
652 | f += size; | |
653 | hardware->h_rx_unrecognized_cnt++; | |
654 | #else | |
e3a44efb VB |
655 | u_int32_t policy; |
656 | int loctype; | |
657 | ||
6772b237 VB |
658 | subtype = *(u_int8_t*)(frame + f + 3); |
659 | switch (subtype) { | |
660 | case LLDP_TLV_MED_CAP: | |
661 | f += 4; | |
c790233e | 662 | if (size < 7) { |
efe3f9b0 | 663 | LLOG_WARNX("too short LLDP-MED cap " |
6772b237 VB |
664 | "tlv received on %s", |
665 | hardware->h_ifname); | |
666 | goto malformed; | |
667 | } | |
40ecae87 | 668 | chassis->c_med_cap_available = |
6772b237 VB |
669 | ntohs(*(u_int16_t*)(frame + f)); |
670 | f += 2; | |
671 | chassis->c_med_type = | |
672 | *(u_int8_t*)(frame + f); | |
c790233e | 673 | f += size - 6; |
40ecae87 VB |
674 | chassis->c_med_cap_enabled |= |
675 | LLDPMED_CAP_CAP; | |
6772b237 | 676 | break; |
efe3f9b0 VB |
677 | case LLDP_TLV_MED_POLICY: |
678 | f += 4; | |
c790233e | 679 | if (size < 8) { |
efe3f9b0 VB |
680 | LLOG_WARNX("too short LLDP-MED policy " |
681 | "tlv received on %s", | |
682 | hardware->h_ifname); | |
683 | goto malformed; | |
684 | } | |
e3a44efb VB |
685 | policy = ntohl(*((u_int32_t *)(frame + f))); |
686 | if (((policy >> 24) < 1) || | |
687 | ((policy >> 24) > LLDPMED_APPTYPE_LAST)) { | |
688 | LLOG_INFO("unknown policy field %d " | |
689 | "received on %s", | |
690 | hardware->h_ifname); | |
691 | f += 4; | |
692 | break; | |
693 | } | |
694 | chassis->c_med_policy[(policy >> 24) - 1].type = | |
695 | (policy >> 24); | |
696 | chassis->c_med_policy[(policy >> 24) - 1].unknown = | |
697 | ((policy & 0x800000) != 0); | |
698 | chassis->c_med_policy[(policy >> 24) - 1].tagged = | |
699 | ((policy & 0x400000) != 0); | |
700 | chassis->c_med_policy[(policy >> 24) - 1].vid = | |
701 | (policy & 0x001FFE00) >> 9; | |
702 | chassis->c_med_policy[(policy >> 24) - 1].priority = | |
703 | (policy & 0x1C0) >> 6; | |
704 | chassis->c_med_policy[(policy >> 24) - 1].dscp = | |
705 | policy & 0x3F; | |
c790233e | 706 | f += size - 4; |
40ecae87 VB |
707 | chassis->c_med_cap_enabled |= |
708 | LLDPMED_CAP_POLICY; | |
efe3f9b0 VB |
709 | break; |
710 | case LLDP_TLV_MED_LOCATION: | |
711 | f += 4; | |
712 | if (size <= 5) { | |
713 | LLOG_WARNX("too short LLDP-MED location " | |
714 | "tlv received on %s", | |
715 | hardware->h_ifname); | |
716 | goto malformed; | |
717 | } | |
e3a44efb | 718 | loctype = *(u_int8_t*)(frame + f); |
efe3f9b0 | 719 | f += 1; |
e3a44efb VB |
720 | if ((loctype < 1) || (loctype > LLDPMED_LOCFORMAT_LAST)) { |
721 | LLOG_INFO("unknown location type " | |
722 | "received on %s", | |
723 | hardware->h_ifname); | |
724 | f += size - 5; | |
725 | break; | |
726 | } | |
727 | if ((chassis->c_med_location[loctype - 1].data = | |
b530152e | 728 | (char*)malloc(size - 5)) == NULL) { |
efe3f9b0 VB |
729 | LLOG_WARN("unable to allocate memory " |
730 | "for LLDP-MED location for " | |
731 | "frame received on %s", | |
732 | hardware->h_ifname); | |
733 | goto malformed; | |
734 | } | |
e3a44efb | 735 | memcpy(chassis->c_med_location[loctype - 1].data, |
efe3f9b0 VB |
736 | (char*)(frame + f), |
737 | size - 5); | |
e3a44efb VB |
738 | chassis->c_med_location[loctype - 1].data_len = |
739 | size - 5; | |
740 | chassis->c_med_location[loctype - 1].format = loctype; | |
efe3f9b0 | 741 | f += size - 5; |
40ecae87 VB |
742 | chassis->c_med_cap_enabled |= |
743 | LLDPMED_CAP_LOCATION; | |
efe3f9b0 VB |
744 | break; |
745 | case LLDP_TLV_MED_MDI: | |
746 | f += 4; | |
c790233e | 747 | if (size < 7) { |
efe3f9b0 VB |
748 | LLOG_WARNX("too short LLDP-MED PoE-MDI " |
749 | "tlv received on %s", | |
750 | hardware->h_ifname); | |
751 | goto malformed; | |
752 | } | |
994812b9 VB |
753 | switch (*(u_int8_t*)(frame + f) & 0xC0) { |
754 | case 0x0: | |
755 | chassis->c_med_pow_devicetype = LLDPMED_POW_TYPE_PSE; | |
a49b3a79 VB |
756 | chassis->c_med_cap_enabled |= |
757 | LLDPMED_CAP_MDI_PSE; | |
994812b9 VB |
758 | switch (*(u_int8_t*)(frame + f) & 0x30) { |
759 | case 0x0: | |
760 | chassis->c_med_pow_source = | |
761 | LLDPMED_POW_SOURCE_UNKNOWN; | |
762 | break; | |
763 | case 0x10: | |
764 | chassis->c_med_pow_source = | |
765 | LLDPMED_POW_SOURCE_PRIMARY; | |
766 | break; | |
767 | case 0x20: | |
768 | chassis->c_med_pow_source = | |
769 | LLDPMED_POW_SOURCE_BACKUP; | |
770 | break; | |
771 | default: | |
772 | chassis->c_med_pow_source = | |
773 | LLDPMED_POW_SOURCE_RESERVED; | |
774 | } | |
775 | break; | |
776 | case 0x40: | |
777 | chassis->c_med_pow_devicetype = LLDPMED_POW_TYPE_PD; | |
a49b3a79 VB |
778 | chassis->c_med_cap_enabled |= |
779 | LLDPMED_CAP_MDI_PD; | |
994812b9 VB |
780 | switch (*(u_int8_t*)(frame + f) & 0x30) { |
781 | case 0x0: | |
782 | chassis->c_med_pow_source = | |
783 | LLDPMED_POW_SOURCE_UNKNOWN; | |
784 | break; | |
785 | case 0x10: | |
786 | chassis->c_med_pow_source = | |
787 | LLDPMED_POW_SOURCE_PSE; | |
788 | break; | |
789 | case 0x20: | |
790 | chassis->c_med_pow_source = | |
791 | LLDPMED_POW_SOURCE_LOCAL; | |
792 | break; | |
793 | default: | |
794 | chassis->c_med_pow_source = | |
795 | LLDPMED_POW_SOURCE_BOTH; | |
796 | } | |
797 | break; | |
798 | default: | |
799 | chassis->c_med_pow_devicetype = | |
800 | LLDPMED_POW_TYPE_RESERVED; | |
801 | } | |
802 | switch (*(u_int8_t*)(frame + f) & 0x0F) { | |
803 | case 0x0: | |
804 | chassis->c_med_pow_priority = | |
805 | LLDPMED_POW_PRIO_UNKNOWN; | |
806 | break; | |
807 | case 0x1: | |
808 | chassis->c_med_pow_priority = | |
809 | LLDPMED_POW_PRIO_CRITICAL; | |
810 | break; | |
811 | case 0x2: | |
812 | chassis->c_med_pow_priority = | |
813 | LLDPMED_POW_PRIO_HIGH; | |
814 | break; | |
815 | case 0x3: | |
816 | chassis->c_med_pow_priority = | |
817 | LLDPMED_POW_PRIO_LOW; | |
818 | break; | |
819 | default: | |
820 | chassis->c_med_pow_priority = | |
821 | LLDPMED_POW_PRIO_UNKNOWN; | |
822 | } | |
efe3f9b0 | 823 | f += 1; |
994812b9 | 824 | chassis->c_med_pow_val = |
efe3f9b0 | 825 | ntohs(*(u_int16_t*)(frame + f)); |
c790233e | 826 | f += size - 5; |
efe3f9b0 | 827 | break; |
6772b237 VB |
828 | case LLDP_TLV_MED_IV_HW: |
829 | case LLDP_TLV_MED_IV_SW: | |
830 | case LLDP_TLV_MED_IV_FW: | |
831 | case LLDP_TLV_MED_IV_SN: | |
832 | case LLDP_TLV_MED_IV_MANUF: | |
833 | case LLDP_TLV_MED_IV_MODEL: | |
834 | case LLDP_TLV_MED_IV_ASSET: | |
835 | f += 4; | |
836 | if (size <= 4) | |
837 | b = NULL; | |
838 | else { | |
efe3f9b0 VB |
839 | if ((b = (char*)malloc(size - 3)) == |
840 | NULL) { | |
841 | LLOG_WARN("unable to allocate " | |
842 | "memory for LLDP-MED " | |
843 | "inventory for frame " | |
844 | "received on %s", | |
845 | hardware->h_ifname); | |
846 | goto malformed; | |
847 | } | |
6772b237 VB |
848 | strlcpy(b, |
849 | (char*)(frame + f), | |
44cbe568 | 850 | size - 3); |
6772b237 VB |
851 | } |
852 | switch (subtype) { | |
853 | case LLDP_TLV_MED_IV_HW: | |
854 | chassis->c_med_hw = b; | |
855 | break; | |
856 | case LLDP_TLV_MED_IV_FW: | |
857 | chassis->c_med_fw = b; | |
858 | break; | |
859 | case LLDP_TLV_MED_IV_SW: | |
860 | chassis->c_med_sw = b; | |
861 | break; | |
862 | case LLDP_TLV_MED_IV_SN: | |
863 | chassis->c_med_sn = b; | |
864 | break; | |
865 | case LLDP_TLV_MED_IV_MANUF: | |
866 | chassis->c_med_manuf = b; | |
867 | break; | |
868 | case LLDP_TLV_MED_IV_MODEL: | |
869 | chassis->c_med_fw = b; | |
870 | break; | |
871 | case LLDP_TLV_MED_IV_ASSET: | |
72c4c96f | 872 | chassis->c_med_asset = b; |
6772b237 VB |
873 | break; |
874 | default: | |
875 | LLOG_WARNX("should not be there!"); | |
876 | free(b); | |
877 | break; | |
878 | } | |
879 | f += size - 4; | |
40ecae87 VB |
880 | chassis->c_med_cap_enabled |= |
881 | LLDPMED_CAP_IV; | |
6772b237 VB |
882 | break; |
883 | default: | |
884 | /* Unknown LLDP MED, ignore it */ | |
885 | f += size; | |
886 | hardware->h_rx_unrecognized_cnt++; | |
887 | } | |
888 | #endif /* ENABLE_LLDPMED */ | |
43c02e7b | 889 | } else { |
9ca19b69 | 890 | LLOG_INFO("unknown org tlv received on %s", |
43c02e7b | 891 | hardware->h_ifname); |
37387046 | 892 | hardware->h_rx_unrecognized_cnt++; |
9ca19b69 | 893 | f += size; |
43c02e7b VB |
894 | } |
895 | break; | |
896 | default: | |
897 | LLOG_WARNX("unknown tlv (%d) received on %s", | |
898 | type, hardware->h_ifname); | |
899 | goto malformed; | |
900 | } | |
901 | } | |
902 | ||
903 | /* Some random check */ | |
904 | if ((chassis->c_id == NULL) || | |
905 | (port->p_id == NULL) || | |
906 | (chassis->c_ttl == 0) || | |
907 | (gotend == 0)) { | |
908 | LLOG_WARNX("some mandatory tlv are missing for frame received on %s", | |
909 | hardware->h_ifname); | |
910 | goto malformed; | |
911 | } | |
912 | #define NOTRECEIVED "Not received" | |
913 | if (chassis->c_name == NULL) { | |
914 | if ((chassis->c_name = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) { | |
915 | LLOG_WARNX("unable to allocate null chassis name"); | |
916 | goto malformed; | |
917 | } | |
918 | memcpy(chassis->c_name, NOTRECEIVED, strlen(NOTRECEIVED)); | |
919 | } | |
920 | if (chassis->c_descr == NULL) { | |
921 | if ((chassis->c_descr = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) { | |
922 | LLOG_WARNX("unable to allocate null chassis description"); | |
923 | goto malformed; | |
924 | } | |
925 | memcpy(chassis->c_descr, NOTRECEIVED, strlen(NOTRECEIVED)); | |
926 | } | |
927 | if (port->p_descr == NULL) { | |
928 | if ((port->p_descr = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) { | |
929 | LLOG_WARNX("unable to allocate null port description"); | |
930 | goto malformed; | |
931 | } | |
932 | memcpy(port->p_descr, NOTRECEIVED, strlen(NOTRECEIVED)); | |
933 | } | |
934 | *newchassis = chassis; | |
935 | *newport = port; | |
936 | return 1; | |
937 | malformed: | |
d3322e23 VB |
938 | lldpd_chassis_cleanup(chassis); |
939 | lldpd_port_cleanup(port); | |
43c02e7b VB |
940 | return -1; |
941 | } |