]>
Commit | Line | Data |
---|---|---|
4b292b55 | 1 | /* -*- mode: c; c-file-style: "openbsd" -*- */ |
43c02e7b VB |
2 | /* |
3 | * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> | |
4 | * | |
51434125 | 5 | * Permission to use, copy, modify, and/or distribute this software for any |
43c02e7b VB |
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 | ||
031118c4 | 18 | /* We also supports FDP which is very similar to CDPv1 */ |
43c02e7b | 19 | #include "lldpd.h" |
a8105c1b | 20 | #include "frame.h" |
43c02e7b | 21 | |
4bad1937 VB |
22 | #if defined (ENABLE_CDP) || defined (ENABLE_FDP) |
23 | ||
aff85bf3 | 24 | #include <stdio.h> |
a8105c1b | 25 | #include <unistd.h> |
43c02e7b VB |
26 | #include <errno.h> |
27 | #include <arpa/inet.h> | |
28 | ||
8888d191 | 29 | static int |
77507b69 | 30 | cdp_send(struct lldpd *global, |
43c02e7b VB |
31 | struct lldpd_hardware *hardware, int version) |
32 | { | |
96beef68 | 33 | const char *platform = "Unknown"; |
77507b69 | 34 | struct lldpd_chassis *chassis; |
e6b36c87 | 35 | struct lldpd_mgmt *mgmt; |
619c3799 | 36 | struct lldpd_port *port; |
031118c4 VB |
37 | u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR; |
38 | u_int8_t llcorg[] = LLC_ORG_CISCO; | |
6d07eaee | 39 | #ifdef ENABLE_FDP |
bf517088 | 40 | char *capstr; |
6d07eaee | 41 | #endif |
a8105c1b | 42 | u_int16_t checksum; |
f1e5d0cd | 43 | int length, i; |
a8105c1b VB |
44 | u_int32_t cap; |
45 | u_int8_t *packet; | |
46 | u_int8_t *pos, *pos_len_eh, *pos_llc, *pos_cdp, *pos_checksum, *tlv, *end; | |
43c02e7b | 47 | |
6f8925be VB |
48 | log_debug("cdp", "send CDP frame on %s", hardware->h_ifname); |
49 | ||
619c3799 MS |
50 | port = &(hardware->h_lport); |
51 | chassis = port->p_chassis; | |
77507b69 | 52 | |
6d07eaee | 53 | #ifdef ENABLE_FDP |
031118c4 | 54 | if (version == 0) { |
a8105c1b | 55 | /* With FDP, change multicast address and LLC PID */ |
031118c4 VB |
56 | const u_int8_t fdpmcastaddr[] = FDP_MULTICAST_ADDR; |
57 | const u_int8_t fdpllcorg[] = LLC_ORG_FOUNDRY; | |
58 | memcpy(mcastaddr, fdpmcastaddr, sizeof(mcastaddr)); | |
59 | memcpy(llcorg, fdpllcorg, sizeof(llcorg)); | |
60 | } | |
6d07eaee | 61 | #endif |
031118c4 | 62 | |
a8105c1b | 63 | length = hardware->h_mtu; |
b0cb07f7 | 64 | if ((packet = (u_int8_t*)calloc(1, length)) == NULL) |
a8105c1b | 65 | return ENOMEM; |
a8105c1b VB |
66 | pos = packet; |
67 | ||
68 | /* Ethernet header */ | |
69 | if (!( | |
70 | POKE_BYTES(mcastaddr, sizeof(mcastaddr)) && | |
ed409ccd | 71 | POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) && |
a8105c1b VB |
72 | POKE_SAVE(pos_len_eh) && /* We compute the len later */ |
73 | POKE_UINT16(0))) | |
74 | goto toobig; | |
75 | ||
76 | /* LLC */ | |
77 | if (!( | |
78 | POKE_SAVE(pos_llc) && | |
79 | POKE_UINT8(0xaa) && /* SSAP */ | |
80 | POKE_UINT8(0xaa) && /* DSAP */ | |
81 | POKE_UINT8(0x03) && /* Control field */ | |
82 | POKE_BYTES(llcorg, sizeof(llcorg)) && | |
83 | POKE_UINT16(LLC_PID_CDP))) | |
84 | goto toobig; | |
43c02e7b VB |
85 | |
86 | /* CDP header */ | |
a8105c1b VB |
87 | if (!( |
88 | POKE_SAVE(pos_cdp) && | |
a8db6007 | 89 | POKE_UINT8((version == 0)?1:version) && |
a8105c1b VB |
90 | POKE_UINT8(chassis->c_ttl) && |
91 | POKE_SAVE(pos_checksum) && /* Save checksum position */ | |
92 | POKE_UINT16(0))) | |
93 | goto toobig; | |
43c02e7b VB |
94 | |
95 | /* Chassis ID */ | |
cdc32f2c | 96 | const char *chassis_name = chassis->c_name?chassis->c_name:""; |
a8105c1b VB |
97 | if (!( |
98 | POKE_START_CDP_TLV(CDP_TLV_CHASSIS) && | |
cdc32f2c | 99 | POKE_BYTES(chassis_name, strlen(chassis_name)) && |
a8105c1b VB |
100 | POKE_END_CDP_TLV)) |
101 | goto toobig; | |
43c02e7b VB |
102 | |
103 | /* Adresses */ | |
f1e5d0cd VB |
104 | /* See: |
105 | * http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#xtocid12 | |
106 | * | |
107 | * It seems that Cisco implies that CDP supports IPv6 using | |
108 | * 802.2 address format with 0xAAAA03 0x000000 0x0800, but | |
109 | * 0x0800 is the Ethernet protocol type for IPv4. Therefore, | |
110 | * we support only IPv4. */ | |
111 | i = 0; | |
112 | TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) | |
113 | if (mgmt->m_family == LLDPD_AF_IPV4) i++; | |
114 | if (i > 0) { | |
115 | if (!( | |
116 | POKE_START_CDP_TLV(CDP_TLV_ADDRESSES) && | |
117 | POKE_UINT32(i))) | |
118 | goto toobig; | |
119 | TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) { | |
120 | switch (mgmt->m_family) { | |
121 | case LLDPD_AF_IPV4: | |
122 | if (!( | |
123 | POKE_UINT8(1) && /* Type: NLPID */ | |
124 | POKE_UINT8(1) && /* Length: 1 */ | |
125 | POKE_UINT8(CDP_ADDRESS_PROTO_IP) && /* IP */ | |
126 | POKE_UINT16(sizeof(struct in_addr)) && /* Address length */ | |
127 | POKE_BYTES(&mgmt->m_addr, sizeof(struct in_addr)))) | |
128 | goto toobig; | |
129 | break; | |
e6b36c87 | 130 | } |
e6b36c87 | 131 | } |
f1e5d0cd VB |
132 | if (!(POKE_END_CDP_TLV)) |
133 | goto toobig; | |
e6b36c87 | 134 | } |
43c02e7b VB |
135 | |
136 | /* Port ID */ | |
cdc32f2c | 137 | const char *port_descr = hardware->h_lport.p_descr?hardware->h_lport.p_descr:""; |
a8105c1b VB |
138 | if (!( |
139 | POKE_START_CDP_TLV(CDP_TLV_PORT) && | |
cdc32f2c | 140 | POKE_BYTES(port_descr, strlen(port_descr)) && |
a8105c1b VB |
141 | POKE_END_CDP_TLV)) |
142 | goto toobig; | |
43c02e7b | 143 | |
031118c4 VB |
144 | /* Capabilities */ |
145 | if (version != 0) { | |
a8105c1b | 146 | cap = 0; |
031118c4 | 147 | if (chassis->c_cap_enabled & LLDP_CAP_ROUTER) |
a8105c1b | 148 | cap |= CDP_CAP_ROUTER; |
031118c4 | 149 | if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE) |
e1933880 VB |
150 | cap |= CDP_CAP_SWITCH; |
151 | cap |= CDP_CAP_HOST; | |
a8105c1b VB |
152 | if (!( |
153 | POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) && | |
154 | POKE_UINT32(cap) && | |
155 | POKE_END_CDP_TLV)) | |
156 | goto toobig; | |
6d07eaee | 157 | #ifdef ENABLE_FDP |
bf517088 VB |
158 | } else { |
159 | /* With FDP, it seems that a string is used in place of an int */ | |
bf517088 VB |
160 | if (chassis->c_cap_enabled & LLDP_CAP_ROUTER) |
161 | capstr = "Router"; | |
162 | else if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE) | |
163 | capstr = "Switch"; | |
164 | else if (chassis->c_cap_enabled & LLDP_CAP_REPEATER) | |
165 | capstr = "Bridge"; | |
166 | else | |
167 | capstr = "Host"; | |
a8105c1b VB |
168 | if (!( |
169 | POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) && | |
170 | POKE_BYTES(capstr, strlen(capstr)) && | |
171 | POKE_END_CDP_TLV)) | |
172 | goto toobig; | |
6d07eaee | 173 | #endif |
031118c4 | 174 | } |
2a19e9ea VB |
175 | |
176 | /* Native VLAN */ | |
177 | #ifdef ENABLE_DOT1 | |
edcce8ce | 178 | if (version >=2 && hardware->h_lport.p_pvid != 0) { |
2a19e9ea VB |
179 | if (!( |
180 | POKE_START_CDP_TLV(CDP_TLV_NATIVEVLAN) && | |
181 | POKE_UINT16(hardware->h_lport.p_pvid) && | |
182 | POKE_END_CDP_TLV)) | |
183 | goto toobig; | |
184 | } | |
185 | #endif | |
806eaef4 | 186 | |
43c02e7b | 187 | /* Software version */ |
cdc32f2c | 188 | const char * chassis_descr = chassis->c_descr?chassis->c_descr:""; |
a8105c1b VB |
189 | if (!( |
190 | POKE_START_CDP_TLV(CDP_TLV_SOFTWARE) && | |
cdc32f2c | 191 | POKE_BYTES(chassis_descr, strlen(chassis_descr)) && |
a8105c1b VB |
192 | POKE_END_CDP_TLV)) |
193 | goto toobig; | |
43c02e7b VB |
194 | |
195 | /* Platform */ | |
96beef68 | 196 | if (global && global->g_config.c_platform) platform = global->g_config.c_platform; |
fc37317f | 197 | |
a8105c1b VB |
198 | if (!( |
199 | POKE_START_CDP_TLV(CDP_TLV_PLATFORM) && | |
d4afb919 | 200 | POKE_BYTES(platform, strlen(platform)) && |
a8105c1b VB |
201 | POKE_END_CDP_TLV)) |
202 | goto toobig; | |
619c3799 MS |
203 | |
204 | #ifdef ENABLE_LLDPMED | |
205 | /* Power use */ | |
206 | if ((version >= 2) && | |
207 | port->p_med_cap_enabled && | |
208 | (port->p_med_power.source != LLDP_MED_POW_SOURCE_LOCAL) && | |
209 | (port->p_med_power.val > 0) && | |
210 | (port->p_med_power.val <= 655)) { | |
211 | if (!( | |
212 | POKE_START_CDP_TLV(CDP_TLV_POWER_CONSUMPTION) && | |
213 | POKE_UINT16(port->p_med_power.val * 100) && | |
214 | POKE_END_CDP_TLV)) | |
215 | goto toobig; | |
216 | } | |
217 | #endif | |
2d1fe392 | 218 | (void)POKE_SAVE(end); |
43c02e7b VB |
219 | |
220 | /* Compute len and checksum */ | |
a8105c1b VB |
221 | POKE_RESTORE(pos_len_eh); |
222 | if (!(POKE_UINT16(end - pos_llc))) goto toobig; | |
223 | checksum = frame_checksum(pos_cdp, end - pos_cdp, (version != 0) ? 1 : 0); | |
224 | POKE_RESTORE(pos_checksum); | |
63e52965 | 225 | if (!(POKE_UINT16(checksum))) goto toobig; |
43c02e7b | 226 | |
5347914e | 227 | if (interfaces_send_helper(global, hardware, |
6e75df87 | 228 | (char *)packet, end - packet) == -1) { |
6f8925be | 229 | log_warn("cdp", "unable to send packet on real device for %s", |
43c02e7b | 230 | hardware->h_ifname); |
a8105c1b | 231 | free(packet); |
43c02e7b VB |
232 | return ENETDOWN; |
233 | } | |
234 | ||
235 | hardware->h_tx_cnt++; | |
236 | ||
a8105c1b | 237 | free(packet); |
43c02e7b | 238 | return 0; |
a8105c1b VB |
239 | toobig: |
240 | free(packet); | |
241 | return -1; | |
43c02e7b VB |
242 | } |
243 | ||
a8105c1b VB |
244 | #define CHECK_TLV_SIZE(x, name) \ |
245 | do { if (tlv_len < (x)) { \ | |
6f8925be | 246 | log_warnx("cdp", name " CDP/FDP TLV too short received on %s", \ |
a8105c1b VB |
247 | hardware->h_ifname); \ |
248 | goto malformed; \ | |
249 | } } while (0) | |
031118c4 | 250 | /* cdp_decode also decodes FDP */ |
43c02e7b VB |
251 | int |
252 | cdp_decode(struct lldpd *cfg, char *frame, int s, | |
253 | struct lldpd_hardware *hardware, | |
254 | struct lldpd_chassis **newchassis, struct lldpd_port **newport) | |
255 | { | |
256 | struct lldpd_chassis *chassis; | |
257 | struct lldpd_port *port; | |
e6b36c87 JV |
258 | struct lldpd_mgmt *mgmt; |
259 | struct in_addr addr; | |
4ea50809 | 260 | #if 0 |
43c02e7b | 261 | u_int16_t cksum; |
4ea50809 | 262 | #endif |
a8105c1b VB |
263 | u_int8_t *software = NULL, *platform = NULL; |
264 | int software_len = 0, platform_len = 0, proto, version, nb, caps; | |
43c02e7b | 265 | const unsigned char cdpaddr[] = CDP_MULTICAST_ADDR; |
6d07eaee | 266 | #ifdef ENABLE_FDP |
031118c4 | 267 | const unsigned char fdpaddr[] = CDP_MULTICAST_ADDR; |
6d07eaee VB |
268 | int fdp = 0; |
269 | #endif | |
a8105c1b VB |
270 | u_int8_t *pos, *tlv, *pos_address, *pos_next_address; |
271 | int length, len_eth, tlv_type, tlv_len, addresses_len, address_len; | |
aff85bf3 VB |
272 | #ifdef ENABLE_DOT1 |
273 | struct lldpd_vlan *vlan; | |
274 | #endif | |
43c02e7b | 275 | |
6f8925be VB |
276 | log_debug("cdp", "decode CDP frame received on %s", |
277 | hardware->h_ifname); | |
278 | ||
43c02e7b | 279 | if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { |
6f8925be | 280 | log_warn("cdp", "failed to allocate remote chassis"); |
43c02e7b VB |
281 | return -1; |
282 | } | |
e6b36c87 | 283 | TAILQ_INIT(&chassis->c_mgmt); |
43c02e7b | 284 | if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { |
6f8925be | 285 | log_warn("cdp", "failed to allocate remote port"); |
43c02e7b VB |
286 | free(chassis); |
287 | return -1; | |
288 | } | |
a1347cd8 | 289 | #ifdef ENABLE_DOT1 |
43c02e7b | 290 | TAILQ_INIT(&port->p_vlans); |
a1347cd8 | 291 | #endif |
43c02e7b | 292 | |
a8105c1b VB |
293 | length = s; |
294 | pos = (u_int8_t*)frame; | |
295 | ||
4e5f34c5 | 296 | if (length < 2*ETHER_ADDR_LEN + sizeof(u_int16_t) /* Ethernet */ + |
a8105c1b | 297 | 8 /* LLC */ + 4 /* CDP header */) { |
6f8925be | 298 | log_warn("cdp", "too short CDP/FDP frame received on %s", hardware->h_ifname); |
43c02e7b VB |
299 | goto malformed; |
300 | } | |
301 | ||
a8105c1b | 302 | if (PEEK_CMP(cdpaddr, sizeof(cdpaddr)) != 0) { |
6d07eaee | 303 | #ifdef ENABLE_FDP |
a8105c1b VB |
304 | PEEK_RESTORE((u_int8_t*)frame); |
305 | if (PEEK_CMP(fdpaddr, sizeof(fdpaddr)) != 0) | |
031118c4 VB |
306 | fdp = 1; |
307 | else { | |
6d07eaee | 308 | #endif |
6f8925be | 309 | log_info("cdp", "frame not targeted at CDP/FDP multicast address received on %s", |
031118c4 VB |
310 | hardware->h_ifname); |
311 | goto malformed; | |
6d07eaee | 312 | #ifdef ENABLE_FDP |
031118c4 | 313 | } |
6d07eaee | 314 | #endif |
43c02e7b | 315 | } |
4e5f34c5 | 316 | PEEK_DISCARD(ETHER_ADDR_LEN); /* Don't care of source address */ |
a8105c1b VB |
317 | len_eth = PEEK_UINT16; |
318 | if (len_eth > length) { | |
6f8925be | 319 | log_warnx("cdp", "incorrect 802.3 frame size reported on %s", |
348f6df6 VB |
320 | hardware->h_ifname); |
321 | goto malformed; | |
43c02e7b | 322 | } |
a8105c1b VB |
323 | PEEK_DISCARD(6); /* Skip beginning of LLC */ |
324 | proto = PEEK_UINT16; | |
325 | if (proto != LLC_PID_CDP) { | |
326 | if ((proto != LLC_PID_DRIP) && | |
327 | (proto != LLC_PID_PAGP) && | |
328 | (proto != LLC_PID_PVSTP) && | |
329 | (proto != LLC_PID_UDLD) && | |
330 | (proto != LLC_PID_VTP) && | |
331 | (proto != LLC_PID_DTP) && | |
332 | (proto != LLC_PID_STP)) | |
6f8925be | 333 | log_debug("cdp", "incorrect LLC protocol ID received on %s", |
0d2c6d16 | 334 | hardware->h_ifname); |
43c02e7b VB |
335 | goto malformed; |
336 | } | |
a8105c1b | 337 | |
4ea50809 | 338 | #if 0 |
a8105c1b VB |
339 | /* Check checksum */ |
340 | cksum = frame_checksum(pos, len_eth - 8, | |
9ec6b111 VB |
341 | #ifdef ENABLE_FDP |
342 | !fdp /* fdp = 0 -> cisco checksum */ | |
343 | #else | |
344 | 1 /* cisco checksum */ | |
345 | #endif | |
346 | ); | |
806eaef4 | 347 | if (cksum != 0) { |
6f8925be | 348 | log_info("cdp", "incorrect CDP/FDP checksum for frame received on %s (%d)", |
43c02e7b VB |
349 | hardware->h_ifname, cksum); |
350 | goto malformed; | |
351 | } | |
4ea50809 | 352 | #endif |
43c02e7b | 353 | |
a8105c1b VB |
354 | /* Check version */ |
355 | version = PEEK_UINT8; | |
356 | if ((version != 1) && (version != 2)) { | |
6f8925be | 357 | log_warnx("cdp", "incorrect CDP/FDP version (%d) for frame received on %s", |
a8105c1b VB |
358 | version, hardware->h_ifname); |
359 | goto malformed; | |
360 | } | |
361 | chassis->c_ttl = PEEK_UINT8; /* TTL */ | |
362 | PEEK_DISCARD_UINT16; /* Checksum, already checked */ | |
363 | ||
364 | while (length) { | |
365 | if (length < 4) { | |
6f8925be | 366 | log_warnx("cdp", "CDP/FDP TLV header is too large for " |
43c02e7b VB |
367 | "frame received on %s", |
368 | hardware->h_ifname); | |
369 | goto malformed; | |
370 | } | |
a8105c1b VB |
371 | tlv_type = PEEK_UINT16; |
372 | tlv_len = PEEK_UINT16 - 4; | |
2d1fe392 | 373 | (void)PEEK_SAVE(tlv); |
a8105c1b | 374 | if ((tlv_len < 0) || (length < tlv_len)) { |
6f8925be | 375 | log_warnx("cdp", "incorrect size in CDP/FDP TLV header for frame " |
43c02e7b VB |
376 | "received on %s", |
377 | hardware->h_ifname); | |
378 | goto malformed; | |
379 | } | |
a8105c1b | 380 | switch (tlv_type) { |
43c02e7b | 381 | case CDP_TLV_CHASSIS: |
a8105c1b | 382 | if ((chassis->c_name = (char *)calloc(1, tlv_len + 1)) == NULL) { |
6f8925be | 383 | log_warn("cdp", "unable to allocate memory for chassis name"); |
43c02e7b VB |
384 | goto malformed; |
385 | } | |
a8105c1b | 386 | PEEK_BYTES(chassis->c_name, tlv_len); |
43c02e7b | 387 | chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL; |
a8105c1b | 388 | if ((chassis->c_id = (char *)malloc(tlv_len)) == NULL) { |
6f8925be | 389 | log_warn("cdp", "unable to allocate memory for chassis ID"); |
43c02e7b VB |
390 | goto malformed; |
391 | } | |
a8105c1b VB |
392 | memcpy(chassis->c_id, chassis->c_name, tlv_len); |
393 | chassis->c_id_len = tlv_len; | |
43c02e7b VB |
394 | break; |
395 | case CDP_TLV_ADDRESSES: | |
a8105c1b VB |
396 | CHECK_TLV_SIZE(4, "Address"); |
397 | addresses_len = tlv_len - 4; | |
398 | for (nb = PEEK_UINT32; nb > 0; nb--) { | |
2d1fe392 | 399 | (void)PEEK_SAVE(pos_address); |
a8105c1b VB |
400 | /* We first try to get the real length of the packet */ |
401 | if (addresses_len < 2) { | |
6f8925be | 402 | log_warn("cdp", "too short address subframe " |
a8105c1b VB |
403 | "received on %s", |
404 | hardware->h_ifname); | |
43c02e7b VB |
405 | goto malformed; |
406 | } | |
a8105c1b VB |
407 | PEEK_DISCARD_UINT8; addresses_len--; |
408 | address_len = PEEK_UINT8; addresses_len--; | |
409 | if (addresses_len < address_len + 2) { | |
6f8925be | 410 | log_warn("cdp", "too short address subframe " |
a8105c1b VB |
411 | "received on %s", |
412 | hardware->h_ifname); | |
43c02e7b VB |
413 | goto malformed; |
414 | } | |
a8105c1b VB |
415 | PEEK_DISCARD(address_len); |
416 | addresses_len -= address_len; | |
417 | address_len = PEEK_UINT16; addresses_len -= 2; | |
418 | if (addresses_len < address_len) { | |
6f8925be | 419 | log_warn("cdp", "too short address subframe " |
a8105c1b VB |
420 | "received on %s", |
421 | hardware->h_ifname); | |
422 | goto malformed; | |
423 | } | |
424 | PEEK_DISCARD(address_len); | |
2d1fe392 | 425 | (void)PEEK_SAVE(pos_next_address); |
a8105c1b VB |
426 | /* Next, we go back and try to extract |
427 | IPv4 address */ | |
428 | PEEK_RESTORE(pos_address); | |
429 | if ((PEEK_UINT8 == 1) && (PEEK_UINT8 == 1) && | |
430 | (PEEK_UINT8 == CDP_ADDRESS_PROTO_IP) && | |
e6b36c87 JV |
431 | (PEEK_UINT16 == sizeof(struct in_addr))) { |
432 | PEEK_BYTES(&addr, sizeof(struct in_addr)); | |
433 | mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &addr, | |
434 | sizeof(struct in_addr), 0); | |
435 | if (mgmt == NULL) { | |
9221b5c2 VB |
436 | if (errno == ENOMEM) |
437 | log_warn("cdp", | |
438 | "unable to allocate memory for management address"); | |
439 | else | |
440 | log_warn("cdp", | |
441 | "too large management address received on %s", | |
442 | hardware->h_ifname); | |
e6b36c87 JV |
443 | goto malformed; |
444 | } | |
445 | TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries); | |
446 | } | |
a8105c1b VB |
447 | /* Go to the end of the address */ |
448 | PEEK_RESTORE(pos_next_address); | |
43c02e7b VB |
449 | } |
450 | break; | |
451 | case CDP_TLV_PORT: | |
506273e9 | 452 | if (tlv_len == 0) { |
0d19619a | 453 | log_warn("cdp", "too short port description received"); |
506273e9 VB |
454 | goto malformed; |
455 | } | |
a8105c1b | 456 | if ((port->p_descr = (char *)calloc(1, tlv_len + 1)) == NULL) { |
6f8925be | 457 | log_warn("cdp", "unable to allocate memory for port description"); |
43c02e7b VB |
458 | goto malformed; |
459 | } | |
a8105c1b VB |
460 | PEEK_BYTES(port->p_descr, tlv_len); |
461 | port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; | |
462 | if ((port->p_id = (char *)calloc(1, tlv_len)) == NULL) { | |
6f8925be | 463 | log_warn("cdp", "unable to allocate memory for port ID"); |
43c02e7b VB |
464 | goto malformed; |
465 | } | |
a8105c1b VB |
466 | memcpy(port->p_id, port->p_descr, tlv_len); |
467 | port->p_id_len = tlv_len; | |
43c02e7b VB |
468 | break; |
469 | case CDP_TLV_CAPABILITIES: | |
6d07eaee | 470 | #ifdef ENABLE_FDP |
031118c4 | 471 | if (fdp) { |
bf517088 | 472 | /* Capabilities are string with FDP */ |
a8105c1b | 473 | if (!strncmp("Router", (char*)pos, tlv_len)) |
bf517088 | 474 | chassis->c_cap_enabled = LLDP_CAP_ROUTER; |
a8105c1b | 475 | else if (!strncmp("Switch", (char*)pos, tlv_len)) |
bf517088 | 476 | chassis->c_cap_enabled = LLDP_CAP_BRIDGE; |
a8105c1b | 477 | else if (!strncmp("Bridge", (char*)pos, tlv_len)) |
bf517088 VB |
478 | chassis->c_cap_enabled = LLDP_CAP_REPEATER; |
479 | else | |
480 | chassis->c_cap_enabled = LLDP_CAP_STATION; | |
481 | chassis->c_cap_available = chassis->c_cap_enabled; | |
031118c4 VB |
482 | break; |
483 | } | |
6d07eaee | 484 | #endif |
a8105c1b VB |
485 | CHECK_TLV_SIZE(4, "Capabilities"); |
486 | caps = PEEK_UINT32; | |
487 | if (caps & CDP_CAP_ROUTER) | |
43c02e7b | 488 | chassis->c_cap_enabled |= LLDP_CAP_ROUTER; |
a8105c1b | 489 | if (caps & 0x0e) |
43c02e7b VB |
490 | chassis->c_cap_enabled |= LLDP_CAP_BRIDGE; |
491 | if (chassis->c_cap_enabled == 0) | |
492 | chassis->c_cap_enabled = LLDP_CAP_STATION; | |
493 | chassis->c_cap_available = chassis->c_cap_enabled; | |
43c02e7b VB |
494 | break; |
495 | case CDP_TLV_SOFTWARE: | |
a8105c1b | 496 | software_len = tlv_len; |
2d1fe392 | 497 | (void)PEEK_SAVE(software); |
43c02e7b VB |
498 | break; |
499 | case CDP_TLV_PLATFORM: | |
a8105c1b | 500 | platform_len = tlv_len; |
2d1fe392 | 501 | (void)PEEK_SAVE(platform); |
43c02e7b | 502 | break; |
aff85bf3 VB |
503 | #ifdef ENABLE_DOT1 |
504 | case CDP_TLV_NATIVEVLAN: | |
505 | CHECK_TLV_SIZE(2, "Native VLAN"); | |
506 | if ((vlan = (struct lldpd_vlan *)calloc(1, | |
507 | sizeof(struct lldpd_vlan))) == NULL) { | |
6f8925be | 508 | log_warn("cdp", "unable to alloc vlan " |
aff85bf3 VB |
509 | "structure for " |
510 | "tlv received on %s", | |
511 | hardware->h_ifname); | |
512 | goto malformed; | |
513 | } | |
514 | vlan->v_vid = port->p_pvid = PEEK_UINT16; | |
515 | if (asprintf(&vlan->v_name, "VLAN #%d", vlan->v_vid) == -1) { | |
6f8925be | 516 | log_warn("cdp", "unable to alloc VLAN name for " |
aff85bf3 VB |
517 | "TLV received on %s", |
518 | hardware->h_ifname); | |
519 | free(vlan); | |
520 | goto malformed; | |
521 | } | |
522 | TAILQ_INSERT_TAIL(&port->p_vlans, | |
523 | vlan, v_entries); | |
524 | break; | |
525 | #endif | |
43c02e7b | 526 | default: |
6f8925be | 527 | log_debug("cdp", "unknown CDP/FDP TLV type (%d) received on %s", |
a8105c1b | 528 | ntohs(tlv_type), hardware->h_ifname); |
37387046 | 529 | hardware->h_rx_unrecognized_cnt++; |
43c02e7b | 530 | } |
a8105c1b | 531 | PEEK_DISCARD(tlv + tlv_len - pos); |
43c02e7b VB |
532 | } |
533 | if (!software && platform) { | |
534 | if ((chassis->c_descr = (char *)calloc(1, | |
535 | platform_len + 1)) == NULL) { | |
6f8925be | 536 | log_warn("cdp", "unable to allocate memory for chassis description"); |
43c02e7b VB |
537 | goto malformed; |
538 | } | |
539 | memcpy(chassis->c_descr, platform, platform_len); | |
540 | } else if (software && !platform) { | |
541 | if ((chassis->c_descr = (char *)calloc(1, | |
542 | software_len + 1)) == NULL) { | |
6f8925be | 543 | log_warn("cdp", "unable to allocate memory for chassis description"); |
43c02e7b VB |
544 | goto malformed; |
545 | } | |
546 | memcpy(chassis->c_descr, software, software_len); | |
547 | } else if (software && platform) { | |
548 | #define CONCAT_PLATFORM " running on\n" | |
549 | if ((chassis->c_descr = (char *)calloc(1, | |
550 | software_len + platform_len + | |
551 | strlen(CONCAT_PLATFORM) + 1)) == NULL) { | |
6f8925be | 552 | log_warn("cdp", "unable to allocate memory for chassis description"); |
43c02e7b VB |
553 | goto malformed; |
554 | } | |
555 | memcpy(chassis->c_descr, platform, platform_len); | |
556 | memcpy(chassis->c_descr + platform_len, | |
557 | CONCAT_PLATFORM, strlen(CONCAT_PLATFORM)); | |
558 | memcpy(chassis->c_descr + platform_len + strlen(CONCAT_PLATFORM), | |
559 | software, software_len); | |
560 | } | |
561 | if ((chassis->c_id == NULL) || | |
562 | (port->p_id == NULL) || | |
563 | (chassis->c_name == NULL) || | |
564 | (chassis->c_descr == NULL) || | |
565 | (port->p_descr == NULL) || | |
566 | (chassis->c_ttl == 0) || | |
567 | (chassis->c_cap_enabled == 0)) { | |
6f8925be | 568 | log_warnx("cdp", "some mandatory CDP/FDP tlv are missing for frame received on %s", |
43c02e7b VB |
569 | hardware->h_ifname); |
570 | goto malformed; | |
571 | } | |
572 | *newchassis = chassis; | |
573 | *newport = port; | |
574 | return 1; | |
575 | ||
576 | malformed: | |
77507b69 | 577 | lldpd_chassis_cleanup(chassis, 1); |
4b292b55 | 578 | lldpd_port_cleanup(port, 1); |
4e624dc2 | 579 | free(port); |
43c02e7b VB |
580 | return -1; |
581 | } | |
582 | ||
6d07eaee | 583 | #ifdef ENABLE_CDP |
43c02e7b | 584 | int |
77507b69 | 585 | cdpv1_send(struct lldpd *global, |
43c02e7b VB |
586 | struct lldpd_hardware *hardware) |
587 | { | |
77507b69 | 588 | return cdp_send(global, hardware, 1); |
43c02e7b VB |
589 | } |
590 | ||
591 | int | |
77507b69 | 592 | cdpv2_send(struct lldpd *global, |
43c02e7b VB |
593 | struct lldpd_hardware *hardware) |
594 | { | |
77507b69 | 595 | return cdp_send(global, hardware, 2); |
43c02e7b | 596 | } |
6d07eaee | 597 | #endif |
43c02e7b | 598 | |
6d07eaee | 599 | #ifdef ENABLE_FDP |
031118c4 | 600 | int |
77507b69 | 601 | fdp_send(struct lldpd *global, |
031118c4 VB |
602 | struct lldpd_hardware *hardware) |
603 | { | |
77507b69 | 604 | return cdp_send(global, hardware, 0); |
031118c4 | 605 | } |
6d07eaee | 606 | #endif |
031118c4 | 607 | |
6d07eaee | 608 | #ifdef ENABLE_CDP |
8888d191 | 609 | static int |
a8105c1b | 610 | cdp_guess(char *pos, int length, int version) |
43c02e7b VB |
611 | { |
612 | const u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR; | |
4e5f34c5 | 613 | if (length < 2*ETHER_ADDR_LEN + sizeof(u_int16_t) /* Ethernet */ + |
a8105c1b | 614 | 8 /* LLC */ + 4 /* CDP header */) |
43c02e7b | 615 | return 0; |
4e5f34c5 | 616 | if (PEEK_CMP(mcastaddr, ETHER_ADDR_LEN) != 0) |
43c02e7b | 617 | return 0; |
4e5f34c5 | 618 | PEEK_DISCARD(ETHER_ADDR_LEN); PEEK_DISCARD_UINT16; /* Ethernet */ |
a8105c1b VB |
619 | PEEK_DISCARD(8); /* LLC */ |
620 | return (PEEK_UINT8 == version); | |
43c02e7b VB |
621 | } |
622 | ||
623 | int | |
624 | cdpv1_guess(char *frame, int len) | |
625 | { | |
626 | return cdp_guess(frame, len, 1); | |
627 | } | |
628 | ||
629 | int | |
630 | cdpv2_guess(char *frame, int len) | |
631 | { | |
632 | return cdp_guess(frame, len, 2); | |
633 | } | |
6d07eaee | 634 | #endif |
4bad1937 VB |
635 | |
636 | #endif /* defined (ENABLE_CDP) || defined (ENABLE_FDP) */ |