| /* |
| * Copyright (c) 2016 Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr.h> |
| #include <tc_util.h> |
| |
| #include <net/net_ip.h> |
| #include <net/nbuf.h> |
| |
| #include <ieee802154_frame.h> |
| #include <ipv6.h> |
| |
| struct ieee802154_pkt_test { |
| char *name; |
| struct in6_addr src; |
| struct in6_addr dst; |
| uint8_t *pkt; |
| uint8_t length; |
| struct { |
| struct ieee802154_fcf_seq *fc_seq; |
| struct ieee802154_address_field *dst_addr; |
| struct ieee802154_address_field *src_addr; |
| } mhr_check; |
| }; |
| |
| uint8_t ns_pkt[] = { |
| 0x41, 0xd8, 0x3e, 0xcd, 0xab, 0xff, 0xff, 0xc2, 0xa3, 0x9e, 0x00, |
| 0x00, 0x4b, 0x12, 0x00, 0x7b, 0x09, 0x3a, 0x20, 0x01, 0x0d, 0xb8, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x02, 0x02, 0x01, 0xff, 0x00, 0x00, 0x01, 0x87, 0x00, 0x2e, 0xad, |
| 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, |
| 0x00, 0x12, 0x4b, 0x00, 0x00, 0x9e, 0xa3, 0xc2, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x3d, 0x74 |
| }; |
| |
| struct ieee802154_pkt_test test_ns_pkt = { |
| .name = "NS frame", |
| .src = { { { 0x20, 0x01, 0xdb, 0x08, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }, |
| .dst = { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x01 } } }, |
| .pkt = ns_pkt, |
| .length = sizeof(ns_pkt), |
| .mhr_check.fc_seq = (struct ieee802154_fcf_seq *)ns_pkt, |
| .mhr_check.dst_addr = (struct ieee802154_address_field *)(ns_pkt + 3), |
| .mhr_check.src_addr = (struct ieee802154_address_field *)(ns_pkt + 7), |
| }; |
| |
| uint8_t ack_pkt[] = { 0x02, 0x10, 0x16, 0xa2, 0x97 }; |
| |
| struct ieee802154_pkt_test test_ack_pkt = { |
| .name = "ACK frame", |
| .pkt = ack_pkt, |
| .length = sizeof(ack_pkt), |
| .mhr_check.fc_seq = (struct ieee802154_fcf_seq *)ack_pkt, |
| .mhr_check.dst_addr = NULL, |
| .mhr_check.src_addr = NULL, |
| }; |
| |
| uint8_t beacon_pkt[] = { |
| 0x00, 0xd0, 0x11, 0xcd, 0xab, 0xc2, 0xa3, 0x9e, 0x00, 0x00, 0x4b, |
| 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
| }; |
| |
| struct ieee802154_pkt_test test_beacon_pkt = { |
| .name = "Empty beacon frame", |
| .pkt = beacon_pkt, |
| .length = sizeof(beacon_pkt), |
| .mhr_check.fc_seq = (struct ieee802154_fcf_seq *)beacon_pkt, |
| .mhr_check.dst_addr = NULL, |
| .mhr_check.src_addr = |
| (struct ieee802154_address_field *) (beacon_pkt + 3), |
| }; |
| |
| struct net_buf *current_buf; |
| struct k_sem driver_lock; |
| struct net_if *iface; |
| |
| static void pkt_hexdump(uint8_t *pkt, uint8_t length) |
| { |
| int i; |
| |
| TC_PRINT(" -> Packet content:\n"); |
| |
| for (i = 0; i < length;) { |
| int j; |
| |
| TC_PRINT("\t"); |
| |
| for (j = 0; j < 10 && i < length; j++, i++) { |
| TC_PRINT("%02x ", *pkt); |
| pkt++; |
| } |
| |
| TC_PRINT("\n"); |
| } |
| } |
| |
| static void ieee_addr_hexdump(uint8_t *addr, uint8_t length) |
| { |
| int i; |
| |
| TC_PRINT(" -> IEEE 802.15.4 Address: "); |
| |
| for (i = 0; i < length-1; i++) { |
| TC_PRINT("%02x:", *addr); |
| addr++; |
| } |
| |
| TC_PRINT("%02x\n", *addr); |
| } |
| |
| static inline int test_packet_parsing(struct ieee802154_pkt_test *t) |
| { |
| struct ieee802154_mpdu mpdu; |
| |
| TC_PRINT("- Parsing packet 0x%p of frame %s\n", t->pkt, t->name); |
| |
| if (!ieee802154_validate_frame(t->pkt, t->length, &mpdu)) { |
| TC_ERROR("*** Could not validate frame %s\n", t->name); |
| return TC_FAIL; |
| } |
| |
| if (mpdu.mhr.fs != t->mhr_check.fc_seq || |
| mpdu.mhr.dst_addr != t->mhr_check.dst_addr || |
| mpdu.mhr.src_addr != t->mhr_check.src_addr) { |
| TC_PRINT("d: %p vs %p -- s: %p vs %p\n", |
| mpdu.mhr.dst_addr, t->mhr_check.dst_addr, |
| mpdu.mhr.src_addr, t->mhr_check.src_addr); |
| TC_ERROR("*** Wrong MPDU information on frame %s\n", |
| t->name); |
| |
| return TC_FAIL; |
| } |
| |
| return TC_PASS; |
| } |
| |
| static inline int test_ns_sending(struct ieee802154_pkt_test *t) |
| { |
| struct ieee802154_mpdu mpdu; |
| |
| TC_PRINT("- Sending NS packet\n"); |
| |
| if (net_ipv6_send_ns(iface, NULL, &t->src, &t->dst, &t->dst, false)) { |
| TC_ERROR("*** Could not create IPv6 NS packet\n"); |
| return TC_FAIL; |
| } |
| |
| k_sem_take(&driver_lock, 10); |
| |
| if (!current_buf->frags) { |
| TC_ERROR("*** Could not send IPv6 NS packet\n"); |
| return TC_FAIL; |
| } |
| |
| pkt_hexdump(net_nbuf_ll(current_buf), net_buf_frags_len(current_buf)); |
| |
| if (!ieee802154_validate_frame(net_nbuf_ll(current_buf), |
| net_buf_frags_len(current_buf), &mpdu)) { |
| TC_ERROR("*** Sent packet is not valid\n"); |
| net_nbuf_unref(current_buf); |
| |
| return TC_FAIL; |
| } |
| |
| net_nbuf_unref(current_buf->frags); |
| current_buf->frags = NULL; |
| |
| return TC_PASS; |
| } |
| |
| static inline int test_ack_reply(struct ieee802154_pkt_test *t) |
| { |
| static uint8_t data_pkt[] = { |
| 0x61, 0xdc, 0x16, 0xcd, 0xab, 0x26, 0x11, 0x32, 0x00, 0x00, 0x4b, |
| 0x12, 0x00, 0x26, 0x18, 0x32, 0x00, 0x00, 0x4b, 0x12, 0x00, 0x7b, |
| 0x00, 0x3a, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x0d, 0xb8, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x02, 0x87, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, |
| 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, |
| 0x16, 0xf0, 0x02, 0xff, 0x16, 0xf0, 0x12, 0xff, 0x16, 0xf0, 0x32, |
| 0xff, 0x16, 0xf0, 0x00, 0xff, 0x16, 0xf0, 0x00, 0xff, 0x16 |
| }; |
| struct ieee802154_mpdu mpdu; |
| struct net_buf *buf, *frag; |
| |
| TC_PRINT("- Sending ACK reply to a data packet\n"); |
| |
| buf = net_nbuf_get_reserve_rx(0, K_FOREVER); |
| frag = net_nbuf_get_reserve_rx(0, K_FOREVER); |
| |
| memcpy(frag->data, data_pkt, sizeof(data_pkt)); |
| frag->len = sizeof(data_pkt); |
| |
| net_buf_frag_add(buf, frag); |
| |
| net_recv_data(iface, buf); |
| |
| k_sem_take(&driver_lock, 20); |
| |
| /* an ACK packet should be in current_buf */ |
| if (!current_buf->frags) { |
| TC_ERROR("*** No ACK reply sent\n"); |
| return TC_FAIL; |
| } |
| |
| pkt_hexdump(net_nbuf_ll(current_buf), net_buf_frags_len(current_buf)); |
| |
| if (!ieee802154_validate_frame(net_nbuf_ll(current_buf), |
| net_buf_frags_len(current_buf), &mpdu)) { |
| TC_ERROR("*** ACK Reply is invalid\n"); |
| return TC_FAIL; |
| } |
| |
| if (memcmp(mpdu.mhr.fs, t->mhr_check.fc_seq, |
| sizeof(struct ieee802154_fcf_seq))) { |
| TC_ERROR("*** ACK Reply does not compare\n"); |
| return TC_FAIL; |
| } |
| |
| net_nbuf_unref(current_buf->frags); |
| current_buf->frags = NULL; |
| |
| return TC_PASS; |
| } |
| |
| static inline int initialize_test_environment(void) |
| { |
| struct device *dev; |
| |
| k_sem_init(&driver_lock, 0, UINT_MAX); |
| |
| current_buf = net_nbuf_get_reserve_rx(0, K_FOREVER); |
| if (!current_buf) { |
| TC_ERROR("*** No buffer to allocate\n"); |
| return TC_FAIL; |
| } |
| |
| dev = device_get_binding("fake_ieee802154"); |
| if (!dev) { |
| TC_ERROR("*** Could not get fake device\n"); |
| return TC_FAIL; |
| } |
| |
| iface = net_if_lookup_by_dev(dev); |
| if (!iface) { |
| TC_ERROR("*** Could not get fake iface\n"); |
| return TC_FAIL; |
| } |
| |
| TC_PRINT("Fake IEEE 802.15.4 network interface ready\n"); |
| |
| ieee_addr_hexdump(iface->link_addr.addr, 8); |
| |
| return TC_PASS; |
| } |
| |
| void main(void) |
| { |
| int status = TC_FAIL; |
| |
| TC_PRINT("Starting ieee802154 stack test\n"); |
| |
| if (initialize_test_environment() != TC_PASS) { |
| goto end; |
| } |
| |
| if (test_packet_parsing(&test_ns_pkt) != TC_PASS) { |
| goto end; |
| } |
| |
| if (test_ns_sending(&test_ns_pkt) != TC_PASS) { |
| goto end; |
| } |
| |
| if (test_packet_parsing(&test_ack_pkt) != TC_PASS) { |
| goto end; |
| } |
| |
| if (test_ack_reply(&test_ack_pkt) != TC_PASS) { |
| goto end; |
| } |
| |
| if (test_packet_parsing(&test_beacon_pkt) != TC_PASS) { |
| goto end; |
| } |
| |
| status = TC_PASS; |
| |
| end: |
| TC_END_RESULT(status); |
| TC_END_REPORT(status); |
| } |