2 This file is part of systemd.
4 Copyright (C) 2014 Tom Gundersen
5 Copyright (C) 2014 Susant Sahani
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <arpa/inet.h>
22 #include <net/ethernet.h>
29 #include "alloc-util.h"
31 #include "lldp-network.h"
35 #include "string-util.h"
37 #define TEST_LLDP_PORT "em1"
38 #define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp"
39 #define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc"
41 static int test_fd
[2];
43 static struct ether_addr mac_addr
= {
44 .ether_addr_octet
= {'A', 'B', 'C', '1', '2', '3'}
47 static int lldp_build_tlv_packet(tlv_packet
**ret
) {
48 _cleanup_(sd_lldp_packet_unrefp
) tlv_packet
*m
= NULL
;
49 const uint8_t lldp_dst
[] = LLDP_MULTICAST_ADDR
;
50 struct ether_header ether
= {
51 .ether_type
= htons(ETHERTYPE_LLDP
),
54 /* Append Ethernet header */
55 memcpy(ðer
.ether_dhost
, lldp_dst
, ETHER_ADDR_LEN
);
56 memcpy(ðer
.ether_shost
, &mac_addr
, ETHER_ADDR_LEN
);
58 assert_se(tlv_packet_new(&m
) >= 0);
60 assert_se(tlv_packet_append_bytes(m
, ðer
, sizeof(struct ether_header
)) >= 0);
62 assert_se(lldp_tlv_packet_open_container(m
, LLDP_TYPE_CHASSIS_ID
) >= 0);
64 assert_se(tlv_packet_append_u8(m
, LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS
) >= 0);
65 assert_se(tlv_packet_append_bytes(m
, &mac_addr
, ETHER_ADDR_LEN
) >= 0);
67 assert_se(lldp_tlv_packet_close_container(m
) >= 0);
70 assert_se(lldp_tlv_packet_open_container(m
, LLDP_TYPE_PORT_ID
) >= 0);
72 assert_se(tlv_packet_append_u8(m
, LLDP_PORT_SUBTYPE_INTERFACE_NAME
) >= 0);
73 assert_se(tlv_packet_append_bytes(m
, TEST_LLDP_PORT
, strlen(TEST_LLDP_PORT
) + 1) >= 0);
75 assert_se(lldp_tlv_packet_close_container(m
) >= 0);
78 assert_se(lldp_tlv_packet_open_container(m
, LLDP_TYPE_TTL
) >= 0);
80 assert_se(tlv_packet_append_u16(m
, 170) >= 0);
82 assert_se(lldp_tlv_packet_close_container(m
) >= 0);
85 assert_se(lldp_tlv_packet_open_container(m
, LLDP_TYPE_SYSTEM_NAME
) >= 0);
87 assert_se(tlv_packet_append_bytes(m
, TEST_LLDP_TYPE_SYSTEM_NAME
,
88 strlen(TEST_LLDP_TYPE_SYSTEM_NAME
)) >= 0);
89 assert_se(lldp_tlv_packet_close_container(m
) >= 0);
91 /* system descrition */
92 assert_se(lldp_tlv_packet_open_container(m
, LLDP_TYPE_SYSTEM_DESCRIPTION
) >= 0);
94 assert_se(tlv_packet_append_bytes(m
, TEST_LLDP_TYPE_SYSTEM_DESC
,
95 strlen(TEST_LLDP_TYPE_SYSTEM_DESC
)) >= 0);
97 assert_se(lldp_tlv_packet_close_container(m
) >= 0);
99 /* Mark end of packet */
100 assert_se(lldp_tlv_packet_open_container(m
, LLDP_TYPE_END
) >= 0);
101 assert_se(lldp_tlv_packet_close_container(m
) >= 0);
110 static int lldp_parse_chassis_tlv(tlv_packet
*m
, uint8_t *type
) {
114 assert_se(lldp_tlv_packet_enter_container(m
, LLDP_TYPE_CHASSIS_ID
) >= 0);
115 assert_se(tlv_packet_read_u8(m
, &subtype
) >= 0);
118 case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS
:
120 *type
= LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS
;
121 assert_se(tlv_packet_read_bytes(m
, &p
, &length
) >= 0);
123 assert_se(memcmp(p
, &mac_addr
.ether_addr_octet
, ETHER_ADDR_LEN
) == 0);
127 assert_not_reached("Unhandled option");
130 assert_se(lldp_tlv_packet_exit_container(m
) >= 0);
135 static int lldp_parse_port_id_tlv(tlv_packet
*m
) {
136 _cleanup_free_
char *p
= NULL
;
141 assert_se(lldp_tlv_packet_enter_container(m
, LLDP_TYPE_PORT_ID
) >= 0);
143 assert_se(tlv_packet_read_u8(m
, &subtype
) >= 0);
146 case LLDP_PORT_SUBTYPE_INTERFACE_NAME
:
147 assert_se(tlv_packet_read_string(m
, &str
, &length
) >= 0);
149 p
= strndup(str
, length
-1);
152 assert_se(streq(p
, TEST_LLDP_PORT
) == 1);
155 assert_not_reached("Unhandled option");
158 assert_se(lldp_tlv_packet_exit_container(m
) >= 0);
163 static int lldp_parse_system_name_tlv(tlv_packet
*m
) {
164 _cleanup_free_
char *p
= NULL
;
168 assert_se(lldp_tlv_packet_enter_container(m
, LLDP_TYPE_SYSTEM_NAME
) >= 0);
169 assert_se(tlv_packet_read_string(m
, &str
, &length
) >= 0);
171 p
= strndup(str
, length
);
174 assert_se(streq(p
, TEST_LLDP_TYPE_SYSTEM_NAME
) == 1);
176 assert_se(lldp_tlv_packet_exit_container(m
) >= 0);
181 static int lldp_parse_system_desc_tlv(tlv_packet
*m
) {
182 _cleanup_free_
char *p
= NULL
;
186 assert_se(lldp_tlv_packet_enter_container(m
, LLDP_TYPE_SYSTEM_DESCRIPTION
) >= 0);
187 assert_se(tlv_packet_read_string(m
, &str
, &length
) >= 0);
189 p
= strndup(str
, length
);
192 assert_se(streq(p
, TEST_LLDP_TYPE_SYSTEM_DESC
) == 1);
194 assert_se(lldp_tlv_packet_exit_container(m
) >= 0);
199 static int lldp_parse_ttl_tlv(tlv_packet
*m
) {
202 assert_se(lldp_tlv_packet_enter_container(m
, LLDP_TYPE_TTL
) >= 0);
203 assert_se(tlv_packet_read_u16(m
, &ttl
) >= 0);
205 assert_se(ttl
== 170);
207 assert_se(lldp_tlv_packet_exit_container(m
) >= 0);
212 static int lldp_get_destination_type(tlv_packet
*m
) {
215 assert_se(sd_lldp_packet_get_destination_type(m
, &dest
) >= 0);
216 assert_se(dest
== SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE
);
221 static int lldp_parse_tlv_packet(tlv_packet
*m
, int len
) {
224 assert_se(tlv_packet_parse_pdu(m
, len
) >= 0);
225 assert_se(lldp_parse_chassis_tlv(m
, &subtype
) >= 0);
226 assert_se(lldp_parse_port_id_tlv(m
) >= 0);
227 assert_se(lldp_parse_system_name_tlv(m
) >= 0);
228 assert_se(lldp_parse_ttl_tlv(m
) >= 0);
229 assert_se(lldp_parse_system_desc_tlv(m
) >= 0);
231 assert_se(lldp_get_destination_type(m
) >= 0);
236 static void test_parser(void) {
237 _cleanup_(sd_lldp_packet_unrefp
) tlv_packet
*tlv
= NULL
;
240 lldp_build_tlv_packet(&tlv
);
241 /* parse the packet */
242 tlv_packet_parse_pdu(tlv
, tlv
->length
);
244 lldp_parse_tlv_packet(tlv
, tlv
->length
);
247 int lldp_network_bind_raw_socket(int ifindex
) {
248 if (socketpair(AF_UNIX
, SOCK_DGRAM
| SOCK_NONBLOCK
, 0, test_fd
) < 0)
254 static int lldp_handler_calls
;
255 static void lldp_handler (sd_lldp
*lldp
, int event
, void *userdata
) {
256 lldp_handler_calls
++;
259 static int start_lldp(sd_lldp
**lldp
, sd_event
*e
, sd_lldp_cb_t cb
, void *cb_data
) {
262 r
= sd_lldp_new(42, "dummy", &mac_addr
, lldp
);
266 r
= sd_lldp_attach_event(*lldp
, e
, 0);
270 r
= sd_lldp_set_callback(*lldp
, cb
, cb_data
);
274 r
= sd_lldp_start(*lldp
);
281 static int stop_lldp(sd_lldp
*lldp
) {
284 r
= sd_lldp_stop(lldp
);
288 r
= sd_lldp_detach_event(lldp
);
293 safe_close(test_fd
[1]);
298 static void test_receive_basic_packet(sd_event
*e
) {
300 sd_lldp_packet
**packets
;
302 uint16_t length
, ttl
;
306 /* Ethernet header */
307 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/
308 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
309 0x88, 0xcc, /* Ethertype */
310 /* LLDP mandatory TLVs */
311 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
313 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */
314 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds*/
315 /* LLDP optional TLVs */
316 0x08, 0x04, 0x50, 0x6f, 0x72, 0x74, /* Port Description: "Port" */
317 0x0a, 0x03, 0x53, 0x59, 0x53, /* System Name: "SYS" */
318 0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00, /* System Description: "foo" (NULL-terminated) */
319 0x00, 0x00 /* End Of LLDPDU */
322 lldp_handler_calls
= 0;
323 assert_se(start_lldp(&lldp
, e
, lldp_handler
, NULL
) == 0);
325 assert_se(write(test_fd
[1], frame
, sizeof(frame
)) == sizeof(frame
));
327 assert_se(lldp_handler_calls
== 1);
328 assert_se(sd_lldp_get_packets(lldp
, &packets
) == 1);
330 assert_se(sd_lldp_packet_read_chassis_id(packets
[0], &type
, &data
, &length
) == 0);
331 assert_se(type
== LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS
);
332 assert_se(length
== ETH_ALEN
);
333 assert_se(!memcmp(data
, "\x00\x01\x02\x03\x04\x05", ETH_ALEN
));
335 assert_se(sd_lldp_packet_read_port_id(packets
[0], &type
, &data
, &length
) == 0);
336 assert_se(type
== LLDP_PORT_SUBTYPE_INTERFACE_NAME
);
337 assert_se(length
== 3);
338 assert_se(strneq((char *) data
, "1/3", 3));
340 assert_se(sd_lldp_packet_read_port_description(packets
[0], &str
, &length
) == 0);
341 assert_se(length
== 4);
342 assert_se(strneq(str
, "Port", 4));
344 assert_se(sd_lldp_packet_read_system_name(packets
[0], &str
, &length
) == 0);
345 assert_se(length
== 3);
346 assert_se(strneq(str
, "SYS", 3));
348 assert_se(sd_lldp_packet_read_system_description(packets
[0], &str
, &length
) == 0);
349 assert_se(length
== 4); /* This is the real length in the TLV packet */
350 assert_se(strneq(str
, "foo", 3));
352 assert_se(sd_lldp_packet_read_ttl(packets
[0], &ttl
) == 0);
353 assert_se(ttl
== 120);
355 assert_se(sd_lldp_packet_get_destination_type(packets
[0], &dest_type
) == 0);
356 assert_se(dest_type
== SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE
);
358 sd_lldp_packet_unref(packets
[0]);
361 assert_se(stop_lldp(lldp
) == 0);
364 static void test_receive_incomplete_packet(sd_event
*e
) {
366 sd_lldp_packet
**packets
;
368 /* Ethernet header */
369 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/
370 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
371 0x88, 0xcc, /* Ethertype */
372 /* LLDP mandatory TLVs */
373 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
375 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */
377 0x00, 0x00 /* End Of LLDPDU */
380 lldp_handler_calls
= 0;
381 assert_se(start_lldp(&lldp
, e
, lldp_handler
, NULL
) == 0);
383 assert_se(write(test_fd
[1], frame
, sizeof(frame
)) == sizeof(frame
));
385 assert_se(lldp_handler_calls
== 0);
386 assert_se(sd_lldp_get_packets(lldp
, &packets
) == 0);
388 assert_se(stop_lldp(lldp
) == 0);
391 static void test_receive_oui_packet(sd_event
*e
) {
393 sd_lldp_packet
**packets
;
399 /* Ethernet header */
400 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/
401 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
402 0x88, 0xcc, /* Ethertype */
403 /* LLDP mandatory TLVs */
404 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
406 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port TLV: interface name, "1/3" */
407 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds*/
408 /* LLDP optional TLVs */
409 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* Port VLAN ID: 0x1234 */
411 0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, /* Port and protocol: flag 1, PPVID 0x7788 */
413 0xfe, 0x0d, 0x00, 0x80, 0xc2, 0x03, /* VLAN Name: ID 0x1234, name "Vlan51" */
414 0x12, 0x34, 0x06, 0x56, 0x6c, 0x61,
416 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x06, /* Management VID: 0x0102 */
418 0xfe, 0x09, 0x00, 0x80, 0xc2, 0x07, /* Link aggregation: status 1, ID 0x00140012 */
419 0x01, 0x00, 0x14, 0x00, 0x12,
420 0x00, 0x00 /* End of LLDPDU */
423 lldp_handler_calls
= 0;
424 assert_se(start_lldp(&lldp
, e
, lldp_handler
, NULL
) == 0);
426 assert_se(write(test_fd
[1], frame
, sizeof(frame
)) == sizeof(frame
));
428 assert_se(lldp_handler_calls
== 1);
429 assert_se(sd_lldp_get_packets(lldp
, &packets
) == 1);
431 assert_se(sd_lldp_packet_read_port_vlan_id(packets
[0], &id16
) == 0);
432 assert_se(id16
== 0x1234);
434 assert_se(sd_lldp_packet_read_port_protocol_vlan_id(packets
[0], &flags
, &id16
) == 0);
435 assert_se(flags
== 1);
436 assert_se(id16
== 0x7788);
438 assert_se(sd_lldp_packet_read_vlan_name(packets
[0], &id16
, &str
, &len
) == 0);
439 assert_se(id16
== 0x1234);
441 assert_se(strneq(str
, "Vlan51", 6));
443 assert_se(sd_lldp_packet_read_management_vid(packets
[0], &id16
) == 0);
444 assert_se(id16
== 0x0102);
446 assert_se(sd_lldp_packet_read_link_aggregation(packets
[0], &flags
, &id32
) == 0);
447 assert_se(flags
== 1);
448 assert_se(id32
== 0x00140012);
450 sd_lldp_packet_unref(packets
[0]);
453 assert_se(stop_lldp(lldp
) == 0);
456 int main(int argc
, char *argv
[]) {
457 _cleanup_(sd_event_unrefp
) sd_event
*e
= NULL
;
461 /* LLDP reception tests */
462 assert_se(sd_event_new(&e
) == 0);
463 test_receive_basic_packet(e
);
464 test_receive_incomplete_packet(e
);
465 test_receive_oui_packet(e
);