| /* main.c - Application main entry point */ |
| |
| /* |
| * Copyright (c) 2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/types.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <misc/printk.h> |
| |
| #include <ztest.h> |
| |
| #include <net/net_pkt.h> |
| #include <net/net_ip.h> |
| |
| #if defined(CONFIG_NET_DEBUG_NET_PKT) |
| #define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__) |
| #define NET_LOG_ENABLED 1 |
| #else |
| #define DBG(fmt, ...) |
| #endif |
| |
| #define NET_LOG_ENABLED 1 |
| #include "net_private.h" |
| |
| #define LL_RESERVE 28 |
| #define FRAG_COUNT 7 |
| |
| struct ipv6_hdr { |
| u8_t vtc; |
| u8_t tcflow; |
| u16_t flow; |
| u8_t len[2]; |
| u8_t nexthdr; |
| u8_t hop_limit; |
| struct in6_addr src; |
| struct in6_addr dst; |
| } __packed; |
| |
| struct udp_hdr { |
| u16_t src_port; |
| u16_t dst_port; |
| u16_t len; |
| u16_t chksum; |
| } __packed; |
| |
| struct icmp_hdr { |
| u8_t type; |
| u8_t code; |
| u16_t chksum; |
| } __packed; |
| |
| static const char example_data[] = |
| "0123456789abcdefghijklmnopqrstuvxyz!#¤%&/()=?" |
| "0123456789abcdefghijklmnopqrstuvxyz!#¤%&/()=?" |
| "0123456789abcdefghijklmnopqrstuvxyz!#¤%&/()=?" |
| "0123456789abcdefghijklmnopqrstuvxyz!#¤%&/()=?" |
| "0123456789abcdefghijklmnopqrstuvxyz!#¤%&/()=?" |
| "0123456789abcdefghijklmnopqrstuvxyz!#¤%&/()=?"; |
| |
| static void test_ipv6_multi_frags(void) |
| { |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct ipv6_hdr *ipv6; |
| struct udp_hdr *udp; |
| int bytes, remaining = strlen(example_data), pos = 0; |
| |
| /* Example of multi fragment scenario with IPv6 */ |
| pkt = net_pkt_get_reserve_rx(0, K_FOREVER); |
| frag = net_pkt_get_reserve_rx_data(LL_RESERVE, K_FOREVER); |
| |
| /* Place the IP + UDP header in the first fragment */ |
| if (!net_buf_tailroom(frag)) { |
| ipv6 = (struct ipv6_hdr *)(frag->data); |
| udp = (struct udp_hdr *)((void *)ipv6 + sizeof(*ipv6)); |
| if (net_buf_tailroom(frag) < sizeof(ipv6)) { |
| printk("Not enough space for IPv6 header, " |
| "needed %zd bytes, has %zd bytes\n", |
| sizeof(ipv6), net_buf_tailroom(frag)); |
| zassert_true(false, "No space for IPv6 header"); |
| } |
| net_buf_add(frag, sizeof(ipv6)); |
| |
| if (net_buf_tailroom(frag) < sizeof(udp)) { |
| printk("Not enough space for UDP header, " |
| "needed %zd bytes, has %zd bytes\n", |
| sizeof(udp), net_buf_tailroom(frag)); |
| zassert_true(false, "No space for UDP header"); |
| } |
| |
| net_pkt_set_appdata(pkt, (void *)udp + sizeof(*udp)); |
| net_pkt_set_appdatalen(pkt, 0); |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| /* Put some data to rest of the fragments */ |
| frag = net_pkt_get_reserve_rx_data(LL_RESERVE, K_FOREVER); |
| if (net_buf_tailroom(frag) - |
| (CONFIG_NET_BUF_DATA_SIZE - LL_RESERVE)) { |
| printk("Invalid number of bytes available in the buf, " |
| "should be 0 but was %zd - %d\n", |
| net_buf_tailroom(frag), |
| CONFIG_NET_BUF_DATA_SIZE - LL_RESERVE); |
| zassert_true(false, "Invalid byte count"); |
| } |
| |
| if (((int)net_buf_tailroom(frag) - remaining) > 0) { |
| printk("We should have been out of space now, " |
| "tailroom %zd user data len %zd\n", |
| net_buf_tailroom(frag), |
| strlen(example_data)); |
| zassert_true(false, "Still space"); |
| } |
| |
| while (remaining > 0) { |
| int copy; |
| |
| bytes = net_buf_tailroom(frag); |
| copy = remaining > bytes ? bytes : remaining; |
| memcpy(net_buf_add(frag, copy), &example_data[pos], copy); |
| |
| DBG("Remaining %d left %d copy %d\n", remaining, bytes, copy); |
| |
| pos += bytes; |
| remaining -= bytes; |
| if (net_buf_tailroom(frag) - (bytes - copy)) { |
| printk("There should have not been any tailroom left, " |
| "tailroom %zd\n", |
| net_buf_tailroom(frag) - (bytes - copy)); |
| zassert_true(false, "There is still tailroom left"); |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| if (remaining > 0) { |
| frag = net_pkt_get_reserve_rx_data(LL_RESERVE, |
| K_FOREVER); |
| } |
| } |
| |
| bytes = net_pkt_get_len(pkt); |
| if (bytes != strlen(example_data)) { |
| printk("Invalid number of bytes in message, %zd vs %d\n", |
| strlen(example_data), bytes); |
| zassert_true(false, "Invalid number of bytes"); |
| } |
| |
| /* Normally one should not unref the fragment list like this |
| * because it will leave the pkt->frags pointing to already |
| * freed fragment. |
| */ |
| net_pkt_frag_unref(pkt->frags); |
| |
| zassert_not_null(pkt->frags, "Frag list empty"); |
| |
| pkt->frags = NULL; /* to prevent double free */ |
| |
| net_pkt_unref(pkt); |
| } |
| |
| static char buf_orig[200]; |
| static char buf_copy[200]; |
| |
| static void linearize(struct net_pkt *pkt, char *buffer, int len) |
| { |
| struct net_buf *frag; |
| char *ptr = buffer; |
| |
| frag = pkt->frags; |
| |
| while (frag && len > 0) { |
| |
| memcpy(ptr, frag->data, frag->len); |
| ptr += frag->len; |
| len -= frag->len; |
| |
| frag = frag->frags; |
| } |
| } |
| |
| static void test_fragment_copy(void) |
| { |
| struct net_pkt *pkt, *new_pkt; |
| struct net_buf *frag, *new_frag; |
| struct ipv6_hdr *ipv6; |
| struct udp_hdr *udp; |
| size_t orig_len, reserve; |
| int pos; |
| |
| pkt = net_pkt_get_reserve_rx(0, K_FOREVER); |
| frag = net_pkt_get_reserve_rx_data(LL_RESERVE, K_FOREVER); |
| |
| /* Place the IP + UDP header in the first fragment */ |
| if (net_buf_tailroom(frag)) { |
| ipv6 = (struct ipv6_hdr *)(frag->data); |
| udp = (struct udp_hdr *)((void *)ipv6 + sizeof(*ipv6)); |
| if (net_buf_tailroom(frag) < sizeof(*ipv6)) { |
| printk("Not enough space for IPv6 header, " |
| "needed %zd bytes, has %zd bytes\n", |
| sizeof(ipv6), net_buf_tailroom(frag)); |
| zassert_true(false, "No space for IPv6 header"); |
| } |
| net_buf_add(frag, sizeof(*ipv6)); |
| |
| if (net_buf_tailroom(frag) < sizeof(*udp)) { |
| printk("Not enough space for UDP header, " |
| "needed %zd bytes, has %zd bytes\n", |
| sizeof(udp), net_buf_tailroom(frag)); |
| zassert_true(false, "No space for UDP header"); |
| } |
| |
| net_buf_add(frag, sizeof(*udp)); |
| |
| memcpy(net_buf_add(frag, 15), example_data, 15); |
| |
| net_pkt_set_appdata(pkt, (void *)udp + sizeof(*udp) + 15); |
| net_pkt_set_appdatalen(pkt, 0); |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| orig_len = net_pkt_get_len(pkt); |
| |
| DBG("Total copy data len %zd\n", orig_len); |
| |
| linearize(pkt, buf_orig, orig_len); |
| |
| /* Then copy a fragment list to a new fragment list. |
| * Reserve some space in front of the buffers. |
| */ |
| reserve = sizeof(struct ipv6_hdr) + sizeof(struct icmp_hdr); |
| new_frag = net_pkt_copy_all(pkt, reserve, K_FOREVER); |
| zassert_not_null(new_frag, "Cannot copy fragment list"); |
| |
| new_pkt = net_pkt_get_reserve_tx(0, K_FOREVER); |
| new_pkt->frags = net_buf_frag_add(new_pkt->frags, new_frag); |
| |
| DBG("Total new data len %zd\n", net_pkt_get_len(new_pkt)); |
| |
| if ((net_pkt_get_len(pkt) + reserve) != net_pkt_get_len(new_pkt)) { |
| int diff; |
| |
| diff = net_pkt_get_len(new_pkt) - reserve - |
| net_pkt_get_len(pkt); |
| |
| printk("Fragment list missing data, %d bytes not copied " |
| "(%zd vs %zd)\n", diff, |
| net_pkt_get_len(pkt) + reserve, |
| net_pkt_get_len(new_pkt)); |
| zassert_true(false, "Frag list missing"); |
| } |
| |
| if (net_pkt_get_len(new_pkt) != (orig_len + sizeof(struct ipv6_hdr) + |
| sizeof(struct icmp_hdr))) { |
| printk("Fragment list missing data, new pkt len %zd " |
| "should be %zd\n", net_pkt_get_len(new_pkt), |
| orig_len + sizeof(struct ipv6_hdr) + |
| sizeof(struct icmp_hdr)); |
| zassert_true(false, "Frag list missing data"); |
| } |
| |
| linearize(new_pkt, buf_copy, sizeof(buf_copy)); |
| |
| zassert_true(memcmp(buf_orig, buf_copy, sizeof(buf_orig)), |
| "Buffer copy failed, buffers are same"); |
| |
| pos = memcmp(buf_orig, buf_copy + sizeof(struct ipv6_hdr) + |
| sizeof(struct icmp_hdr), orig_len); |
| if (pos) { |
| printk("Buffer copy failed at pos %d\n", pos); |
| zassert_true(false, "Buf copy failed"); |
| } |
| } |
| |
| /* Empty data and test data must be the same size in order the test to work */ |
| static const char test_data[] = { '0', '1', '2', '3', '4', |
| '5', '6', '7' }; |
| |
| static const char sample_data[] = |
| "abcdefghijklmnopqrstuvxyz " |
| "abcdefghijklmnopqrstuvxyz " |
| "abcdefghijklmnopqrstuvxyz " |
| "abcdefghijklmnopqrstuvxyz " |
| "abcdefghijklmnopqrstuvxyz " |
| "abcdefghijklmnopqrstuvxyz " |
| "abcdefghijklmnopqrstuvxyz " |
| "abcdefghijklmnopqrstuvxyz " |
| "abcdefghijklmnopqrstuvxyz " |
| "abcdefghijklmnopqrstuvxyz " |
| "abcdefghijklmnopqrstuvxyz " |
| "abcdefghijklmnopqrstuvxyz " |
| "abcdefghijklmnopqrstuvxyz"; |
| |
| static char test_rw_short[] = |
| "abcdefghijklmnopqrstuvwxyz"; |
| |
| static char test_rw_long[] = |
| "abcdefghijklmnopqrstuvwxyz " |
| "abcdefghijklmnopqrstuvwxyz " |
| "abcdefghijklmnopqrstuvwxyz " |
| "abcdefghijklmnopqrstuvwxyz " |
| "abcdefghijklmnopqrstuvwxyz " |
| "abcdefghijklmnopqrstuvwxyz " |
| "abcdefghijklmnopqrstuvwxyz "; |
| |
| static void test_pkt_read_append(void) |
| { |
| int remaining = strlen(sample_data); |
| u8_t verify_rw_short[sizeof(test_rw_short)]; |
| u8_t verify_rw_long[sizeof(test_rw_long)]; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct net_buf *tfrag; |
| struct ipv6_hdr *ipv6; |
| struct udp_hdr *udp; |
| u8_t data[10]; |
| int pos = 0; |
| int bytes; |
| u16_t off; |
| u16_t tpos; |
| u16_t fail_pos; |
| |
| /* Example of multi fragment read, append and skip APS's */ |
| pkt = net_pkt_get_reserve_rx(0, K_FOREVER); |
| frag = net_pkt_get_reserve_rx_data(LL_RESERVE, K_FOREVER); |
| |
| /* Place the IP + UDP header in the first fragment */ |
| if (!net_buf_tailroom(frag)) { |
| ipv6 = (struct ipv6_hdr *)(frag->data); |
| udp = (struct udp_hdr *)((void *)ipv6 + sizeof(*ipv6)); |
| if (net_buf_tailroom(frag) < sizeof(ipv6)) { |
| printk("Not enough space for IPv6 header, " |
| "needed %zd bytes, has %zd bytes\n", |
| sizeof(ipv6), net_buf_tailroom(frag)); |
| zassert_true(false, "No space for IPv6 header"); |
| } |
| net_buf_add(frag, sizeof(ipv6)); |
| |
| if (net_buf_tailroom(frag) < sizeof(udp)) { |
| printk("Not enough space for UDP header, " |
| "needed %zd bytes, has %zd bytes\n", |
| sizeof(udp), net_buf_tailroom(frag)); |
| zassert_true(false, "No space for UDP header"); |
| } |
| |
| net_pkt_set_appdata(pkt, (void *)udp + sizeof(*udp)); |
| net_pkt_set_appdatalen(pkt, 0); |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| /* Put some data to rest of the fragments */ |
| frag = net_pkt_get_reserve_rx_data(LL_RESERVE, K_FOREVER); |
| if (net_buf_tailroom(frag) - |
| (CONFIG_NET_BUF_DATA_SIZE - LL_RESERVE)) { |
| printk("Invalid number of bytes available in the buf, " |
| "should be 0 but was %zd - %d\n", |
| net_buf_tailroom(frag), |
| CONFIG_NET_BUF_DATA_SIZE - LL_RESERVE); |
| zassert_true(false, "Invalid number of bytes avail"); |
| } |
| |
| if (((int)net_buf_tailroom(frag) - remaining) > 0) { |
| printk("We should have been out of space now, " |
| "tailroom %zd user data len %zd\n", |
| net_buf_tailroom(frag), |
| strlen(sample_data)); |
| zassert_true(false, "Not out of space"); |
| } |
| |
| while (remaining > 0) { |
| int copy; |
| |
| bytes = net_buf_tailroom(frag); |
| copy = remaining > bytes ? bytes : remaining; |
| memcpy(net_buf_add(frag, copy), &sample_data[pos], copy); |
| |
| DBG("Remaining %d left %d copy %d\n", remaining, bytes, copy); |
| |
| pos += bytes; |
| remaining -= bytes; |
| if (net_buf_tailroom(frag) - (bytes - copy)) { |
| printk("There should have not been any tailroom left, " |
| "tailroom %zd\n", |
| net_buf_tailroom(frag) - (bytes - copy)); |
| zassert_true(false, "Still tailroom left"); |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| if (remaining > 0) { |
| frag = net_pkt_get_reserve_rx_data(LL_RESERVE, |
| K_FOREVER); |
| } |
| } |
| |
| bytes = net_pkt_get_len(pkt); |
| if (bytes != strlen(sample_data)) { |
| printk("Invalid number of bytes in message, %zd vs %d\n", |
| strlen(sample_data), bytes); |
| zassert_true(false, "Message size wrong"); |
| } |
| |
| /* Failure cases */ |
| /* Invalid buffer */ |
| tfrag = net_frag_skip(NULL, 10, &fail_pos, 10); |
| zassert_true(!tfrag && fail_pos == 0xffff, "Invalid case NULL buffer"); |
| |
| /* Invalid: Skip more than a buffer length.*/ |
| tfrag = net_buf_frag_last(pkt->frags); |
| tfrag = net_frag_skip(tfrag, tfrag->len - 1, &fail_pos, tfrag->len + 2); |
| if (!(!tfrag && fail_pos == 0xffff)) { |
| printk("Invalid case offset %d length to skip %d," |
| "frag length %d\n", |
| tfrag->len - 1, tfrag->len + 2, tfrag->len); |
| zassert_true(false, "Invalid offset"); |
| } |
| |
| /* Invalid offset */ |
| tfrag = net_buf_frag_last(pkt->frags); |
| tfrag = net_frag_skip(tfrag, tfrag->len + 10, &fail_pos, 10); |
| if (!(!tfrag && fail_pos == 0xffff)) { |
| printk("Invalid case offset %d length to skip %d," |
| "frag length %d\n", |
| tfrag->len + 10, 10, tfrag->len); |
| zassert_true(false, "Invalid offset"); |
| } |
| |
| /* Valid cases */ |
| |
| /* Offset is more than single fragment length */ |
| /* Get the first data fragment */ |
| tfrag = pkt->frags; |
| tfrag = tfrag->frags; |
| off = tfrag->len; |
| tfrag = net_frag_read(tfrag, off + 10, &tpos, 10, data); |
| if (!tfrag || |
| memcmp(sample_data + off + 10, data, 10)) { |
| printk("Failed to read from offset %d, frag length %d " |
| "read length %d\n", |
| tfrag->len + 10, tfrag->len, 10); |
| zassert_true(false, "Fail offset read"); |
| } |
| |
| /* Skip till end of all fragments */ |
| /* Get the first data fragment */ |
| tfrag = pkt->frags; |
| tfrag = tfrag->frags; |
| tfrag = net_frag_skip(tfrag, 0, &tpos, strlen(sample_data)); |
| zassert_true(!tfrag && tpos == 0, |
| "Invalid skip till end of all fragments"); |
| |
| /* Short data test case */ |
| /* Test case scenario: |
| * 1) Cache the current fragment and offset |
| * 2) Append short data |
| * 3) Append short data again |
| * 4) Skip first short data from cached frag or offset |
| * 5) Read short data and compare |
| */ |
| tfrag = net_buf_frag_last(pkt->frags); |
| off = tfrag->len; |
| |
| zassert_true(net_pkt_append_all(pkt, (u16_t)sizeof(test_rw_short), |
| test_rw_short, K_FOREVER), |
| "net_pkt_append failed"); |
| |
| zassert_true(net_pkt_append_all(pkt, (u16_t)sizeof(test_rw_short), |
| test_rw_short, K_FOREVER), |
| "net_pkt_append failed"); |
| |
| tfrag = net_frag_skip(tfrag, off, &tpos, |
| (u16_t)sizeof(test_rw_short)); |
| zassert_not_null(tfrag, "net_frag_skip failed"); |
| |
| tfrag = net_frag_read(tfrag, tpos, &tpos, |
| (u16_t)sizeof(test_rw_short), |
| verify_rw_short); |
| zassert_true(!tfrag && tpos == 0, "net_frag_read failed"); |
| zassert_false(memcmp(test_rw_short, verify_rw_short, |
| sizeof(test_rw_short)), |
| "net_frag_read failed with mismatch data"); |
| |
| /* Long data test case */ |
| /* Test case scenario: |
| * 1) Cache the current fragment and offset |
| * 2) Append long data |
| * 3) Append long data again |
| * 4) Skip first long data from cached frag or offset |
| * 5) Read long data and compare |
| */ |
| tfrag = net_buf_frag_last(pkt->frags); |
| off = tfrag->len; |
| |
| zassert_true(net_pkt_append_all(pkt, (u16_t)sizeof(test_rw_long), |
| test_rw_long, K_FOREVER), |
| "net_pkt_append failed"); |
| |
| zassert_true(net_pkt_append_all(pkt, (u16_t)sizeof(test_rw_long), |
| test_rw_long, K_FOREVER), |
| "net_pkt_append failed"); |
| |
| tfrag = net_frag_skip(tfrag, off, &tpos, |
| (u16_t)sizeof(test_rw_long)); |
| zassert_not_null(tfrag, "net_frag_skip failed"); |
| |
| tfrag = net_frag_read(tfrag, tpos, &tpos, |
| (u16_t)sizeof(test_rw_long), |
| verify_rw_long); |
| zassert_true(!tfrag && tpos == 0, "net_frag_read failed"); |
| zassert_false(memcmp(test_rw_long, verify_rw_long, |
| sizeof(test_rw_long)), |
| "net_frag_read failed with mismatch data"); |
| |
| net_pkt_unref(pkt); |
| |
| DBG("test_pkt_read_append passed\n"); |
| } |
| |
| static void test_pkt_read_write_insert(void) |
| { |
| struct net_buf *read_frag; |
| struct net_buf *temp_frag; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| u8_t read_data[100]; |
| u16_t read_pos; |
| u16_t len; |
| u16_t pos; |
| |
| /* Example of multi fragment read, append and skip APS's */ |
| pkt = net_pkt_get_reserve_rx(0, K_FOREVER); |
| net_pkt_set_ll_reserve(pkt, LL_RESERVE); |
| |
| frag = net_pkt_get_reserve_rx_data(net_pkt_ll_reserve(pkt), |
| K_FOREVER); |
| net_pkt_frag_add(pkt, frag); |
| |
| /* 1) Offset is with in input fragment. |
| * Write app data after IPv6 and UDP header. (If the offset is after |
| * IPv6 + UDP header size, api will create empty space till offset |
| * and write data). |
| */ |
| frag = net_pkt_write(pkt, frag, NET_IPV6UDPH_LEN, &pos, 10, |
| (u8_t *)sample_data, K_FOREVER); |
| zassert_false(!frag || pos != 58, "Usecase 1: Write failed"); |
| |
| read_frag = net_frag_read(frag, NET_IPV6UDPH_LEN, &read_pos, 10, |
| read_data); |
| zassert_false(!read_frag && read_pos == 0xffff, |
| "Usecase 1: Read failed"); |
| |
| zassert_false(memcmp(read_data, sample_data, 10), |
| "Usecase 1: Read data mismatch"); |
| |
| /* 2) Write IPv6 and UDP header at offset 0. (Empty space is created |
| * already in Usecase 1, just need to fill the header, at this point |
| * there shouldn't be any length change). |
| */ |
| frag = net_pkt_write(pkt, frag, 0, &pos, NET_IPV6UDPH_LEN, |
| (u8_t *)sample_data, K_FOREVER); |
| zassert_false(!frag || pos != 48, "Usecase 2: Write failed"); |
| |
| read_frag = net_frag_read(frag, 0, &read_pos, NET_IPV6UDPH_LEN, |
| read_data); |
| zassert_false(!read_frag && read_pos == 0xffff, |
| "Usecase 2: Read failed"); |
| |
| zassert_false(memcmp(read_data, sample_data, NET_IPV6UDPH_LEN), |
| "Usecase 2: Read data mismatch"); |
| |
| net_pkt_unref(pkt); |
| |
| pkt = net_pkt_get_reserve_rx(0, K_FOREVER); |
| net_pkt_set_ll_reserve(pkt, LL_RESERVE); |
| |
| /* 3) Offset is in next to next fragment. |
| * Write app data after 2 fragments. (If the offset far away, api will |
| * create empty fragments(space) till offset and write data). |
| */ |
| frag = net_pkt_write(pkt, pkt->frags, 200, &pos, 10, |
| (u8_t *)sample_data + 10, K_FOREVER); |
| zassert_not_null(frag, "Usecase 3: Write failed"); |
| |
| read_frag = net_frag_read(frag, pos - 10, &read_pos, 10, |
| read_data); |
| zassert_false(!read_frag && read_pos == 0xffff, |
| "Usecase 3: Read failed"); |
| |
| zassert_false(memcmp(read_data, sample_data + 10, 10), |
| "Usecase 3: Read data mismatch"); |
| |
| /* 4) Offset is in next to next fragment (overwrite). |
| * Write app data after 2 fragments. (Space is already available from |
| * Usecase 3, this scenatio doesn't create any space, it just overwrites |
| * the existing data. |
| */ |
| frag = net_pkt_write(pkt, pkt->frags, 190, &pos, 10, |
| (u8_t *)sample_data, K_FOREVER); |
| zassert_not_null(frag, "Usecase 4: Write failed"); |
| |
| read_frag = net_frag_read(frag, pos - 10, &read_pos, 20, |
| read_data); |
| zassert_false(!read_frag && read_pos == 0xffff, |
| "Usecase 4: Read failed"); |
| |
| zassert_false(memcmp(read_data, sample_data, 20), |
| "Usecase 4: Read data mismatch"); |
| |
| net_pkt_unref(pkt); |
| |
| /* 5) Write 20 bytes in fragment which has only 10 bytes space. |
| * API should overwrite on first 10 bytes and create extra 10 bytes |
| * and write there. |
| */ |
| pkt = net_pkt_get_reserve_rx(0, K_FOREVER); |
| net_pkt_set_ll_reserve(pkt, LL_RESERVE); |
| |
| frag = net_pkt_get_reserve_rx_data(net_pkt_ll_reserve(pkt), |
| K_FOREVER); |
| net_pkt_frag_add(pkt, frag); |
| |
| /* Create 10 bytes space. */ |
| net_buf_add(frag, 10); |
| |
| frag = net_pkt_write(pkt, frag, 0, &pos, 20, (u8_t *)sample_data, |
| K_FOREVER); |
| zassert_false(!frag && pos != 20, "Usecase 5: Write failed"); |
| |
| read_frag = net_frag_read(frag, 0, &read_pos, 20, read_data); |
| zassert_false(!read_frag && read_pos == 0xffff, |
| "Usecase 5: Read failed"); |
| |
| zassert_false(memcmp(read_data, sample_data, 20), |
| "USecase 5: Read data mismatch"); |
| |
| net_pkt_unref(pkt); |
| |
| /* 6) First fragment is full, second fragment has 10 bytes tail room, |
| * third fragment has 5 bytes. |
| * Write data (30 bytes) in second fragment where offset is 10 bytes |
| * before the tailroom. |
| * So it should overwrite 10 bytes and create space for another 10 |
| * bytes and write data. Third fragment 5 bytes overwritten and space |
| * for 5 bytes created. |
| */ |
| pkt = net_pkt_get_reserve_rx(0, K_FOREVER); |
| net_pkt_set_ll_reserve(pkt, LL_RESERVE); |
| |
| /* First fragment make it fully occupied. */ |
| frag = net_pkt_get_reserve_rx_data(net_pkt_ll_reserve(pkt), |
| K_FOREVER); |
| net_pkt_frag_add(pkt, frag); |
| |
| len = net_buf_tailroom(frag); |
| net_buf_add(frag, len); |
| |
| /* 2nd fragment last 10 bytes tailroom, rest occupied */ |
| frag = net_pkt_get_reserve_rx_data(net_pkt_ll_reserve(pkt), |
| K_FOREVER); |
| net_pkt_frag_add(pkt, frag); |
| |
| len = net_buf_tailroom(frag); |
| net_buf_add(frag, len - 10); |
| |
| read_frag = temp_frag = frag; |
| read_pos = frag->len - 10; |
| |
| /* 3rd fragment, only 5 bytes occupied */ |
| frag = net_pkt_get_reserve_rx_data(net_pkt_ll_reserve(pkt), |
| K_FOREVER); |
| net_pkt_frag_add(pkt, frag); |
| net_buf_add(frag, 5); |
| |
| temp_frag = net_pkt_write(pkt, temp_frag, temp_frag->len - 10, &pos, |
| 30, (u8_t *) sample_data, K_FOREVER); |
| zassert_not_null(temp_frag, "Use case 6: Write failed"); |
| |
| read_frag = net_frag_read(read_frag, read_pos, &read_pos, 30, |
| read_data); |
| zassert_false(!read_frag && read_pos == 0xffff, |
| "Usecase 6: Read failed"); |
| |
| zassert_false(memcmp(read_data, sample_data, 30), |
| "Usecase 6: Read data mismatch"); |
| |
| net_pkt_unref(pkt); |
| |
| /* 7) Offset is with in input fragment. |
| * Write app data after IPv6 and UDP header. (If the offset is after |
| * IPv6 + UDP header size, api will create empty space till offset |
| * and write data). Insert some app data after IPv6 + UDP header |
| * before first set of app data. |
| */ |
| |
| pkt = net_pkt_get_reserve_rx(0, K_FOREVER); |
| net_pkt_set_ll_reserve(pkt, LL_RESERVE); |
| |
| /* First fragment make it fully occupied. */ |
| frag = net_pkt_get_reserve_rx_data(net_pkt_ll_reserve(pkt), |
| K_FOREVER); |
| net_pkt_frag_add(pkt, frag); |
| |
| frag = net_pkt_write(pkt, frag, NET_IPV6UDPH_LEN, &pos, 10, |
| (u8_t *)sample_data + 10, K_FOREVER); |
| zassert_false(!frag || pos != 58, "Usecase 7: Write failed"); |
| |
| read_frag = net_frag_read(frag, NET_IPV6UDPH_LEN, &read_pos, 10, |
| read_data); |
| zassert_false(!read_frag && read_pos == 0xffff, |
| "Usecase 7: Read failed"); |
| |
| zassert_false(memcmp(read_data, sample_data + 10, 10), |
| "Usecase 7: Read data mismatch"); |
| |
| zassert_true(net_pkt_insert(pkt, frag, NET_IPV6UDPH_LEN, 10, |
| (u8_t *)sample_data, K_FOREVER), |
| "Usecase 7: Insert failed"); |
| |
| read_frag = net_frag_read(frag, NET_IPV6UDPH_LEN, &read_pos, 20, |
| read_data); |
| zassert_false(!read_frag && read_pos == 0xffff, |
| "Usecase 7: Read after failed"); |
| |
| zassert_false(memcmp(read_data, sample_data, 20), |
| "Usecase 7: Read data mismatch after insertion"); |
| |
| /* Insert data outside input fragment length, error case. */ |
| zassert_false(net_pkt_insert(pkt, frag, 70, 10, (u8_t *)sample_data, |
| K_FOREVER), |
| "Usecase 7: False insert failed"); |
| |
| net_pkt_unref(pkt); |
| |
| /* 8) Offset is with in input fragment. |
| * Write app data after IPv6 and UDP header. (If the offset is after |
| * IPv6 + UDP header size, api will create empty space till offset |
| * and write data). Insert some app data after IPv6 + UDP header |
| * before first set of app data. Insertion data is long which will |
| * take two fragments. |
| */ |
| pkt = net_pkt_get_reserve_rx(0, K_FOREVER); |
| net_pkt_set_ll_reserve(pkt, LL_RESERVE); |
| |
| /* First fragment make it fully occupied. */ |
| frag = net_pkt_get_reserve_rx_data(net_pkt_ll_reserve(pkt), |
| K_FOREVER); |
| net_pkt_frag_add(pkt, frag); |
| |
| frag = net_pkt_write(pkt, frag, NET_IPV6UDPH_LEN, &pos, 10, |
| (u8_t *)sample_data + 60, K_FOREVER); |
| zassert_false(!frag || pos != 58, "Usecase 8: Write failed"); |
| |
| read_frag = net_frag_read(frag, NET_IPV6UDPH_LEN, &read_pos, 10, |
| read_data); |
| zassert_false(!read_frag && read_pos == 0xffff, |
| "Usecase 8: Read failed"); |
| |
| zassert_false(memcmp(read_data, sample_data + 60, 10), |
| "Usecase 8: Read data mismatch"); |
| |
| zassert_true(net_pkt_insert(pkt, frag, NET_IPV6UDPH_LEN, 60, |
| (u8_t *)sample_data, K_FOREVER), |
| "Usecase 8: Insert failed"); |
| |
| read_frag = net_frag_read(frag, NET_IPV6UDPH_LEN, &read_pos, 70, |
| read_data); |
| zassert_false(!read_frag && read_pos == 0xffff, |
| "Usecase 8: Read after failed"); |
| |
| zassert_false(memcmp(read_data, sample_data, 70), |
| "Usecase 8: Read data mismatch after insertion"); |
| |
| net_pkt_unref(pkt); |
| |
| DBG("test_pkt_read_write_insert passed\n"); |
| } |
| |
| static int calc_fragments(struct net_pkt *pkt) |
| { |
| struct net_buf *frag = pkt->frags; |
| int count = 0; |
| |
| while (frag) { |
| frag = frag->frags; |
| count++; |
| } |
| |
| return count; |
| } |
| |
| static bool net_pkt_is_compact(struct net_pkt *pkt) |
| { |
| struct net_buf *frag, *last; |
| size_t total = 0, calc; |
| int count = 0; |
| |
| last = NULL; |
| frag = pkt->frags; |
| |
| while (frag) { |
| total += frag->len; |
| count++; |
| |
| last = frag; |
| frag = frag->frags; |
| } |
| |
| NET_ASSERT(last); |
| |
| if (!last) { |
| return false; |
| } |
| |
| calc = count * (last->size) - net_buf_tailroom(last) - |
| count * (net_buf_headroom(last)); |
| |
| if (total == calc) { |
| return true; |
| } |
| |
| NET_DBG("Not compacted total %zu real %zu", total, calc); |
| |
| return false; |
| } |
| |
| static void test_fragment_compact(void) |
| { |
| struct net_pkt *pkt; |
| struct net_buf *frags[FRAG_COUNT], *frag; |
| int i, bytes, total, count; |
| |
| pkt = net_pkt_get_reserve_rx(0, K_FOREVER); |
| frag = NULL; |
| |
| for (i = 0, total = 0; i < FRAG_COUNT; i++) { |
| frags[i] = net_pkt_get_reserve_rx_data(12, K_FOREVER); |
| |
| if (frag) { |
| net_buf_frag_add(frag, frags[i]); |
| } |
| |
| frag = frags[i]; |
| |
| /* Copy character test data in front of the fragment */ |
| memcpy(net_buf_add(frags[i], sizeof(test_data)), |
| test_data, sizeof(test_data)); |
| |
| /* Followed by bytes of zeroes */ |
| memset(net_buf_add(frags[i], sizeof(test_data)), 0, |
| sizeof(test_data)); |
| |
| total++; |
| } |
| |
| if (total != FRAG_COUNT) { |
| printk("There should be %d fragments but was %d\n", |
| FRAG_COUNT, total); |
| zassert_true(false, "Invalid fragment count"); |
| } |
| |
| DBG("step 1\n"); |
| |
| pkt->frags = net_buf_frag_add(pkt->frags, frags[0]); |
| |
| bytes = net_pkt_get_len(pkt); |
| if (bytes != FRAG_COUNT * sizeof(test_data) * 2) { |
| printk("Compact test failed, fragments had %d bytes but " |
| "should have had %zd\n", bytes, |
| FRAG_COUNT * sizeof(test_data) * 2); |
| zassert_true(false, "Invalid fragment bytes"); |
| } |
| |
| zassert_false(net_pkt_is_compact(pkt), |
| "The pkt is definitely not compact"); |
| |
| DBG("step 2\n"); |
| |
| net_pkt_compact(pkt); |
| |
| zassert_true(net_pkt_is_compact(pkt), |
| "The pkt should be in compact form"); |
| |
| DBG("step 3\n"); |
| |
| /* Try compacting again, nothing should happen */ |
| net_pkt_compact(pkt); |
| |
| zassert_true(net_pkt_is_compact(pkt), |
| "The pkt should be compacted now"); |
| |
| total = calc_fragments(pkt); |
| |
| /* Add empty fragment at the end and compact, the last fragment |
| * should be removed. |
| */ |
| frag = net_pkt_get_reserve_rx_data(0, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| count = calc_fragments(pkt); |
| |
| DBG("step 4\n"); |
| |
| net_pkt_compact(pkt); |
| |
| i = calc_fragments(pkt); |
| |
| if (count != (i + 1)) { |
| printk("Last fragment removal failed, chain should have %d " |
| "fragments but had %d\n", i-1, i); |
| zassert_true(false, "Last frag rm fails"); |
| } |
| |
| if (i != total) { |
| printk("Fragments missing, expecting %d but got %d\n", |
| total, i); |
| zassert_true(false, "Frags missing"); |
| } |
| |
| /* Add two empty fragments at the end and compact, the last two |
| * fragment should be removed. |
| */ |
| frag = net_pkt_get_reserve_rx_data(0, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| frag = net_pkt_get_reserve_rx_data(0, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| count = calc_fragments(pkt); |
| |
| DBG("step 5\n"); |
| |
| net_pkt_compact(pkt); |
| |
| i = calc_fragments(pkt); |
| |
| if (count != (i + 2)) { |
| printk("Last two fragment removal failed, chain should have " |
| "%d fragments but had %d\n", i-2, i); |
| zassert_true(false, "Last two frag rm fails"); |
| } |
| |
| if (i != total) { |
| printk("Fragments missing, expecting %d but got %d\n", |
| total, i); |
| zassert_true(false, "Frags missing"); |
| } |
| |
| /* Add empty fragment at the beginning and at the end, and then |
| * compact, the two fragment should be removed. |
| */ |
| frag = net_pkt_get_reserve_rx_data(0, K_FOREVER); |
| |
| net_pkt_frag_insert(pkt, frag); |
| |
| frag = net_pkt_get_reserve_rx_data(0, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| count = calc_fragments(pkt); |
| |
| DBG("step 6\n"); |
| |
| net_pkt_compact(pkt); |
| |
| i = calc_fragments(pkt); |
| |
| if (count != (i + 2)) { |
| printk("Two fragment removal failed, chain should have " |
| "%d fragments but had %d\n", i-2, i); |
| zassert_true(false, "Two frag rm fails"); |
| } |
| |
| if (i != total) { |
| printk("Fragments missing, expecting %d but got %d\n", |
| total, i); |
| zassert_true(false, "Frags missing"); |
| } |
| |
| DBG("test_fragment_compact passed\n"); |
| } |
| |
| static const char frag_data[CONFIG_NET_BUF_DATA_SIZE] = { 42 }; |
| |
| static void test_fragment_split(void) |
| { |
| #define TEST_FRAG_COUNT (FRAG_COUNT - 2) |
| #define FRAGA (FRAG_COUNT - 2) |
| #define FRAGB (FRAG_COUNT - 1) |
| struct net_pkt *pkt; |
| struct net_buf *frags[FRAG_COUNT], *frag, *frag_a, *frag_b; |
| int i, total, split_a, split_b; |
| int ret, frag_size; |
| |
| memset(frags, 0, FRAG_COUNT * sizeof(void *)); |
| |
| pkt = net_pkt_get_reserve_rx(0, K_FOREVER); |
| frag = NULL; |
| |
| for (i = 0, total = 0; i < TEST_FRAG_COUNT; i++) { |
| frags[i] = net_pkt_get_reserve_rx_data(12, K_FOREVER); |
| |
| if (frag) { |
| net_buf_frag_add(frag, frags[i]); |
| } |
| |
| frag = frags[i]; |
| |
| /* Copy some test data in front of the fragment */ |
| memcpy(net_buf_add(frags[i], sizeof(frag_data)), |
| frag_data, sizeof(frag_data)); |
| |
| total++; |
| } |
| |
| if (total != TEST_FRAG_COUNT) { |
| printk("There should be %d fragments but was %d\n", |
| TEST_FRAG_COUNT, total); |
| zassert_true(false, "Frags missing"); |
| } |
| |
| frag_size = frags[0]->size; |
| zassert_true(frag_size > 0, "Invalid frag size"); |
| |
| net_pkt_frag_add(pkt, frags[0]); |
| |
| frag_a = frags[FRAGA]; |
| frag_b = frags[FRAGB]; |
| |
| zassert_is_null(frag_a, "frag_a is not NULL"); |
| zassert_is_null(frag_b, "frag_b is not NULL"); |
| |
| split_a = frag_size * 2 / 3; |
| split_b = frag_size - split_a; |
| |
| zassert_true(split_a > 0, "A size is 0"); |
| zassert_true(split_a > split_b, "A is smaller than B"); |
| |
| /* Test some error cases first */ |
| ret = net_pkt_split(NULL, NULL, 1024, &frag_a, &frag_b, K_NO_WAIT); |
| zassert_equal(ret, -EINVAL, "Invalid buf pointers"); |
| |
| ret = net_pkt_split(pkt, pkt->frags, CONFIG_NET_BUF_DATA_SIZE + 1, |
| &frag_a, &frag_b, K_NO_WAIT); |
| zassert_equal(ret, 0, "Split failed"); |
| |
| ret = net_pkt_split(pkt, pkt->frags, split_a, |
| &frag_a, &frag_b, K_NO_WAIT); |
| zassert_equal(ret, 0, "Cannot split frag"); |
| |
| if (frag_a->len != split_a) { |
| printk("Frag_a len %d not %d\n", frag_a->len, split_a); |
| zassert_equal(frag_a->len, split_a, "Frag_a len wrong"); |
| } |
| |
| if (frag_b->len != split_b) { |
| printk("Frag_b len %d not %d\n", frag_b->len, split_b); |
| zassert_true(false, "Frag_b len wrong"); |
| } |
| |
| zassert_false(memcmp(pkt->frags->data, frag_a->data, split_a), |
| "Frag_a data mismatch"); |
| |
| zassert_false(memcmp(pkt->frags->data + split_a, frag_b->data, split_b), |
| "Frag_b data mismatch"); |
| } |
| |
| void test_main(void) |
| { |
| ztest_test_suite(net_pkt_tests, |
| ztest_unit_test(test_ipv6_multi_frags), |
| ztest_unit_test(test_fragment_copy), |
| ztest_unit_test(test_pkt_read_append), |
| ztest_unit_test(test_pkt_read_write_insert), |
| ztest_unit_test(test_fragment_compact), |
| ztest_unit_test(test_fragment_split) |
| ); |
| |
| ztest_run_test_suite(net_pkt_tests); |
| } |