diff --git a/subsys/net/ip/Kconfig.ipv6 b/subsys/net/ip/Kconfig.ipv6 index a9298a18bcd6..41ee0ce23e2f 100644 --- a/subsys/net/ip/Kconfig.ipv6 +++ b/subsys/net/ip/Kconfig.ipv6 @@ -64,7 +64,7 @@ config NET_IPV6_FRAGMENT_MAX_COUNT config NET_IPV6_FRAGMENT_TIMEOUT int "How long to wait the fragments to receive" range 1 60 - default 60 + default 5 depends on NET_IPV6_FRAGMENT help How long to wait for IPv6 fragment to arrive before the reassembly diff --git a/subsys/net/ip/ipv6.c b/subsys/net/ip/ipv6.c index f17fa2bddcad..e1b248548de9 100644 --- a/subsys/net/ip/ipv6.c +++ b/subsys/net/ip/ipv6.c @@ -499,67 +499,93 @@ struct in6_addr *net_ipv6_nbr_lookup_by_index(struct net_if *iface, } #endif /* CONFIG_NET_IPV6_NBR_CACHE */ -int net_ipv6_find_last_ext_hdr(struct net_pkt *pkt) +int net_ipv6_find_last_ext_hdr(struct net_pkt *pkt, u16_t *next_hdr_idx, + u16_t *last_hdr_idx) { struct net_ipv6_hdr *hdr = NET_IPV6_HDR(pkt); struct net_buf *frag = pkt->frags; - int pos = 6; /* Initial value if no extension fragments were found */ - u16_t offset; + int pos = 0; + u16_t offset, prev, tmp; u8_t next_hdr; u8_t length; u8_t next; - offset = sizeof(struct net_ipv6_hdr); next = hdr->nexthdr; + /* Initial value if no extension fragments are found */ + *next_hdr_idx = 6; + + offset = *last_hdr_idx = sizeof(struct net_ipv6_hdr); + + /* First check the simplest case where there is no extension headers + * in the packet. There cannot be any extensions after the normal or + * typical IP protocols + */ + if (next == IPPROTO_ICMPV6 || next == IPPROTO_UDP || + next == IPPROTO_TCP) { + return 0; + } + + prev = pos; + while (frag) { frag = net_frag_read_u8(frag, offset, &offset, &next_hdr); - if (frag != pkt->frags) { - break; + if (!frag && offset == 0xffff) { + goto fail; } frag = net_frag_read_u8(frag, offset, &offset, &length); if (!frag && offset == 0xffff) { - pos = -EINVAL; goto fail; } length = length * 8 + 8; + /* TODO: Add here more IPv6 extension headers to check */ switch (next) { case NET_IPV6_NEXTHDR_NONE: - pos = offset; + *next_hdr_idx = prev; + *last_hdr_idx = offset - 2; goto out; - case NET_IPV6_NEXTHDR_HBHO: - pos = offset; - offset += length; + case NET_IPV6_NEXTHDR_FRAG: + prev = pos; + pos = offset - 2; + offset += 2 + 4; break; - case NET_IPV6_NEXTHDR_FRAG: - pos = offset; - offset += sizeof(struct net_ipv6_frag_hdr); - goto out; + case NET_IPV6_NEXTHDR_HBHO: + prev = pos; + pos = offset - 2; + offset += length - 2; + break; case IPPROTO_ICMPV6: case IPPROTO_UDP: case IPPROTO_TCP: - pos = offset; + prev = pos; + pos = *next_hdr_idx = offset - 2; goto out; default: - pos = -EINVAL; + goto fail; + } + + /* Get the next header value */ + frag = net_frag_read_u8(frag, pos, &tmp, &next); + if (!frag && pos == 0xffff) { goto fail; } } out: - if (!frag || pos > frag->len) { - pos = -EINVAL; - } + *next_hdr_idx = prev; + *last_hdr_idx = offset - 2; + + return 0; fail: - return pos; + return -EINVAL; } const struct in6_addr *net_ipv6_unspecified_address(void) @@ -812,6 +838,7 @@ struct net_pkt *net_ipv6_prepare_for_send(struct net_pkt *pkt) ret = net_ipv6_send_fragmented_pkt(net_pkt_iface(pkt), pkt, pkt_len); if (ret < 0) { + NET_DBG("Cannot fragment IPv6 pkt (%d)", ret); net_pkt_unref(pkt); } @@ -2671,21 +2698,16 @@ static struct net_icmpv6_handler ra_input_handler = { #if defined(CONFIG_NET_IPV6_FRAGMENT_TIMEOUT) #define IPV6_REASSEMBLY_TIMEOUT K_SECONDS(CONFIG_NET_IPV6_FRAGMENT_TIMEOUT) #else -#define IPV6_REASSEMBLY_TIMEOUT K_SECONDS(60) +#define IPV6_REASSEMBLY_TIMEOUT K_SECONDS(5) #endif /* CONFIG_NET_IPV6_FRAGMENT_TIMEOUT */ #define FRAG_BUF_WAIT 10 /* how long to max wait for a buffer */ static void reassembly_timeout(struct k_work *work); +static bool reassembly_init_done; static struct net_ipv6_reassembly -reassembly[CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT] = { - [0 ... (CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT - 1)] = { - .timer = { - .work = K_WORK_INITIALIZER(reassembly_timeout), - }, - }, -}; +reassembly[CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT]; static struct net_ipv6_reassembly *reassembly_get(u32_t id, struct in6_addr *src, @@ -2695,14 +2717,14 @@ static struct net_ipv6_reassembly *reassembly_get(u32_t id, for (i = 0; i < CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT; i++) { - if (k_work_pending(&reassembly[i].timer.work) && + if (k_delayed_work_remaining_get(&reassembly[i].timer) && reassembly[i].id == id && net_ipv6_addr_cmp(src, &reassembly[i].src) && net_ipv6_addr_cmp(dst, &reassembly[i].dst)) { return &reassembly[i]; } - if (k_work_pending(&reassembly[i].timer.work)) { + if (k_delayed_work_remaining_get(&reassembly[i].timer)) { continue; } @@ -2732,11 +2754,12 @@ static bool reassembly_cancel(u32_t id, { int i, j; + NET_DBG("Cancel 0x%x", id); + for (i = 0; i < CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT; i++) { s32_t remaining; - if (!k_work_pending(&reassembly[i].timer.work) || - reassembly[i].id != id || + if (reassembly[i].id != id || !net_ipv6_addr_cmp(src, &reassembly[i].src) || !net_ipv6_addr_cmp(dst, &reassembly[i].dst)) { continue; @@ -2757,8 +2780,8 @@ static bool reassembly_cancel(u32_t id, continue; } - NET_DBG("IPv6 reassembly pkt %p %zd bytes data", - reassembly[i].pkt[j], + NET_DBG("[%d] IPv6 reassembly pkt %p %zd bytes data", + j, reassembly[i].pkt[j], net_pkt_get_len(reassembly[i].pkt[j])); net_pkt_unref(reassembly[i].pkt[j]); @@ -2802,7 +2825,7 @@ static void reassemble_packet(struct net_ipv6_reassembly *reass) struct net_pkt *pkt; struct net_buf *last; u8_t next_hdr; - int i, len; + int i, len, ret; u16_t pos; k_delayed_work_cancel(&reass->timer); @@ -2845,6 +2868,7 @@ static void reassemble_packet(struct net_ipv6_reassembly *reass) } pkt = reass->pkt[0]; + reass->pkt[0] = NULL; /* Next we need to strip away the fragment header from the first packet * and set the various pointers and values in packet. @@ -2870,7 +2894,7 @@ static void reassemble_packet(struct net_ipv6_reassembly *reass) if (!net_pkt_compact(pkt)) { NET_ERR("Cannot compact reassembly packet %p", pkt); - reassembly_cancel(reass->id, &reass->src, &reass->dst); + net_pkt_unref(pkt); return; } @@ -2895,16 +2919,9 @@ static void reassemble_packet(struct net_ipv6_reassembly *reass) * MUST NOT pass it to L2 so there will be a special check for that * in process_data() when handling the packet. */ - net_recv_data(net_pkt_iface(pkt), pkt); - - /* Make room for new packet that can be reassembled */ - k_delayed_work_cancel(&reass->timer); - - /* We do not need to unref the net_pkt as that will be handled - * by the receiving code in upper part of the IP stack. - */ - for (i = 0; i < NET_IPV6_FRAGMENTS_MAX_PKT; i++) { - reass->pkt[i] = NULL; + ret = net_recv_data(net_pkt_iface(pkt), pkt); + if (ret < 0) { + net_pkt_unref(pkt); } } @@ -2912,8 +2929,9 @@ void net_ipv6_frag_foreach(net_ipv6_frag_cb_t cb, void *user_data) { int i; - for (i = 0; i < CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT; i++) { - if (!k_work_pending(&reassembly[i].timer.work)) { + for (i = 0; reassembly_init_done && + i < CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT; i++) { + if (!k_delayed_work_remaining_get(&reassembly[i].timer)) { continue; } @@ -2954,12 +2972,41 @@ static bool fragment_verify(struct net_ipv6_reassembly *reass) return true; } +static int shift_packets(struct net_ipv6_reassembly *reass, int pos) +{ + int i; + + for (i = pos + 1; i < NET_IPV6_FRAGMENTS_MAX_PKT; i++) { + if (!reass->pkt[i]) { + NET_DBG("Moving [%d] %p (offset 0x%x) to [%d]", + pos, reass->pkt[pos], + net_pkt_ipv6_fragment_offset(reass->pkt[pos]), + i); + + /* Do we have enough space in packet array to make + * the move? + */ + if (((i - pos) + 1) > + (NET_IPV6_FRAGMENTS_MAX_PKT - i)) { + return -ENOMEM; + } + + memmove(&reass->pkt[i], &reass->pkt[pos], + sizeof(void *) * (i - pos)); + + return 0; + } + } + + return -EINVAL; +} + static enum net_verdict handle_fragment_hdr(struct net_pkt *pkt, struct net_buf *frag, int total_len, u16_t buf_offset) { - struct net_ipv6_reassembly *reass; + struct net_ipv6_reassembly *reass = NULL; u32_t id; u16_t loc; u16_t offset; @@ -2969,6 +3016,18 @@ static enum net_verdict handle_fragment_hdr(struct net_pkt *pkt, bool found; int i; + if (!reassembly_init_done) { + /* Static initializing does not work here because of the array + * so we must do it at runtime. + */ + for (i = 0; i < CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT; i++) { + k_delayed_work_init(&reassembly[i].timer, + reassembly_timeout); + } + + reassembly_init_done = true; + } + net_pkt_set_ipv6_fragment_start(pkt, frag->data + buf_offset); /* Each fragment has a fragment header. */ @@ -2993,7 +3052,8 @@ static enum net_verdict handle_fragment_hdr(struct net_pkt *pkt, net_pkt_set_ipv6_fragment_offset(pkt, offset); if (!reass->pkt[0]) { - NET_DBG("Storing pkt %p to slot %d", pkt, 0); + NET_DBG("Storing pkt %p to slot %d offset 0x%x", pkt, 0, + offset); reass->pkt[0] = pkt; reassembly_info("Reassembly 1st pkt", reass); @@ -3006,37 +3066,26 @@ static enum net_verdict handle_fragment_hdr(struct net_pkt *pkt, * in reassembly chain in correct order. */ for (i = 0, found = false; i < NET_IPV6_FRAGMENTS_MAX_PKT; i++) { - bool move_done = false; - int j; - if (!reass->pkt[i]) { - NET_DBG("Storing pkt %p to slot %d", pkt, i); + NET_DBG("Storing pkt %p to slot %d offset 0x%x", pkt, + i, offset); reass->pkt[i] = pkt; found = true; break; } - if (net_pkt_ipv6_fragment_offset(reass->pkt[i]) < - offset) { + if (net_pkt_ipv6_fragment_offset(reass->pkt[i]) < offset) { continue; } - for (j = i + 1; j < NET_IPV6_FRAGMENTS_MAX_PKT; j++) { - if (!reass->pkt[j]) { - memmove(reass->pkt[j], reass->pkt[i], - sizeof(void *)); - move_done = true; - break; - } - } - - /* If we do not have any free space in the pkt array, - * then the fragment needs to be discarded. + /* Make room for this fragment, if there is no room, then + * discard the whole reassembly. */ - if (!move_done) { + if (shift_packets(reass, i)) { break; } + NET_DBG("Storing %p (offset 0x%x) to [%d]", pkt, offset, i); reass->pkt[i] = pkt; found = true; break; @@ -3046,7 +3095,7 @@ static enum net_verdict handle_fragment_hdr(struct net_pkt *pkt, /* We could not add this fragment into our saved fragment * list. We must discard the whole packet at this point. */ - reassembly_cancel(reass->id, &reass->src, &reass->dst); + NET_DBG("No slots available for 0x%x", reass->id); goto drop; } @@ -3071,7 +3120,12 @@ static enum net_verdict handle_fragment_hdr(struct net_pkt *pkt, if (!fragment_verify(reass)) { NET_DBG("Reassembled IPv6 verify failed, dropping id %u", reass->id); - reassembly_cancel(reass->id, &reass->src, &reass->dst); + + /* Let the caller release the already inserted pkt */ + if (i < NET_IPV6_FRAGMENTS_MAX_PKT) { + reass->pkt[i] = NULL; + } + goto drop; } @@ -3082,9 +3136,56 @@ static enum net_verdict handle_fragment_hdr(struct net_pkt *pkt, return NET_OK; drop: + if (reass) { + reassembly_cancel(reass->id, &reass->src, &reass->dst); + } + return NET_DROP; } +static int get_next_hdr(struct net_pkt *pkt, u16_t *next_hdr_idx, + u16_t *last_hdr_idx, u8_t *next_hdr) +{ + struct net_buf *buf; + u16_t pos; + int ret; + + /* We need to fix the next header value so find out where + * is the last IPv6 extension header. The next_hdr_idx value is + * offset from the start of the 1st fragment, it is not the + * actual next header value. + */ + ret = net_ipv6_find_last_ext_hdr(pkt, next_hdr_idx, last_hdr_idx); + if (ret < 0) { + NET_DBG("Cannot find the last IPv6 ext header"); + return ret; + } + + /* The IPv6 must fit into first fragment, otherwise the next read + * will fail. + */ + if (*next_hdr_idx > pkt->frags->len) { + NET_DBG("IPv6 header too short (%d vs %d)", *next_hdr_idx, + pkt->frags->len); + return -EINVAL; + } + + if (*last_hdr_idx > pkt->frags->len) { + NET_DBG("IPv6 header too short (%d vs %d)", *last_hdr_idx, + pkt->frags->len); + return -EINVAL; + } + + buf = net_frag_read_u8(pkt->frags, *next_hdr_idx, &pos, next_hdr); + if (!buf && pos == 0xffff) { + NET_DBG("Next header too far (%d vs %d)", *next_hdr_idx, + pkt->frags->len); + return -EINVAL; + } + + return 0; +} + static int send_ipv6_fragment(struct net_if *iface, struct net_pkt *pkt, struct net_buf *orig, @@ -3093,13 +3194,16 @@ static int send_ipv6_fragment(struct net_if *iface, u16_t ipv6_len, u16_t offset, int len, - bool final) + u8_t next_hdr, + u16_t next_hdr_idx, + u16_t last_hdr_idx, + bool final, + int frag_count) { - struct net_pkt *ipv6; struct net_buf *rest = NULL, *end = NULL, *orig_copy = NULL; struct net_ipv6_frag_hdr hdr; - u16_t pos, ext_len; - u8_t prev_hdr; + struct net_pkt *ipv6; + u16_t pos; int ret; /* Prepare the pkt so that the IPv6 packet will be sent properly @@ -3114,6 +3218,7 @@ static int send_ipv6_fragment(struct net_if *iface, } if (!ipv6) { + NET_DBG("Cannot get packet (%d)", -ENOMEM); return -ENOMEM; } @@ -3129,6 +3234,7 @@ static int send_ipv6_fragment(struct net_if *iface, ret = net_pkt_split(pkt, frag, len, &end, &rest, FRAG_BUF_WAIT); if (ret < 0) { + NET_DBG("Cannot split fragment (%d)", ret); goto free_pkts; } } @@ -3154,53 +3260,60 @@ static int send_ipv6_fragment(struct net_if *iface, } end->frags = NULL; + memcpy(ipv6, pkt, sizeof(struct net_pkt)); + /* We must not take the fragments from the original packet (yet) */ + ipv6->frags = NULL; + /* Update the extension length metadata so that upper layer checksum * will be calculated properly by net_ipv6_finalize_raw(). */ - ext_len = net_pkt_ipv6_ext_len(ipv6) + sizeof(struct net_ipv6_frag_hdr); - net_pkt_set_ipv6_ext_len(ipv6, ext_len); + net_pkt_set_ipv6_ext_len(ipv6, + net_pkt_ipv6_ext_len(pkt) + + sizeof(struct net_ipv6_frag_hdr)); orig_copy = net_buf_clone(orig, FRAG_BUF_WAIT); if (!orig_copy) { ret = -ENOMEM; + NET_DBG("Cannot clone IPv6 header (%d)", ret); goto free_pkts; } /* Then add the IPv6 header into the packet. */ net_pkt_frag_insert(ipv6, orig_copy); - /* We need to fix the next header value so find out where - * is the last IPv6 extension header. The returned value is offset - * from the start of the 1st fragment, it is not the actual next - * header value. - */ - prev_hdr = net_ipv6_find_last_ext_hdr(ipv6); - if (prev_hdr < 0) { - ret = -EINVAL; - goto free_pkts; - } - - /* We need to update the next header of the packet. */ - net_frag_read_u8(ipv6->frags, prev_hdr, &pos, &hdr.nexthdr); - - hdr.reserved = 0; - hdr.id = net_pkt_ipv6_fragment_id(pkt); - hdr.offset = htons((offset & 0xfff8) | final); + /* Avoid double free if there is an error later in this function. */ + orig_copy = NULL; /* And we need to update the last header in the IPv6 packet to point to * fragment header. */ - net_pkt_write_u8(ipv6, ipv6->frags, prev_hdr, &pos, + net_pkt_write_u8(ipv6, ipv6->frags, next_hdr_idx, &pos, NET_IPV6_NEXTHDR_FRAG); - /* Then just add the fragmentation header. */ - ret = net_pkt_append_all(ipv6, sizeof(hdr), (u8_t *)&hdr, - FRAG_BUF_WAIT); + NET_ASSERT(pos != next_hdr_idx); + + hdr.reserved = 0; + hdr.id = net_pkt_ipv6_fragment_id(pkt); + hdr.offset = htons(((offset / 8) << 3) | !final); + hdr.nexthdr = next_hdr; + + /* Then add the fragmentation header. */ + ret = net_pkt_insert(ipv6, ipv6->frags, last_hdr_idx, + sizeof(hdr), (u8_t *)&hdr, FRAG_BUF_WAIT); if (!ret) { - ret = -ENOMEM; - goto free_pkts; + /* If we could not insert because we are already at the + * end of fragment, then just append data to the end of + * the IPv6 header. + */ + ret = net_pkt_append_all(ipv6, sizeof(hdr), (u8_t *)&hdr, + FRAG_BUF_WAIT); + if (!ret) { + ret = -ENOMEM; + NET_DBG("Cannot add IPv6 frag header (%d)", ret); + goto free_pkts; + } } /* Tie all the fragments together to form an IPv6 packet. Then @@ -3208,7 +3321,10 @@ static int send_ipv6_fragment(struct net_if *iface, */ net_pkt_frag_add(ipv6, pkt->frags); - ret = net_ipv6_finalize_raw(ipv6, hdr.nexthdr); + /* Note that we must not calculate possible UDP/TCP/ICMPv6 checksum + * as that is already calculated in the non-fragmented packet. + */ + ret = net_ipv6_finalize_raw(ipv6, NET_IPV6_NEXTHDR_FRAG); if (ret < 0) { NET_DBG("Cannot create IPv6 packet (%d)", ret); goto free_pkts; @@ -3224,12 +3340,18 @@ static int send_ipv6_fragment(struct net_if *iface, */ ret = net_send_data(ipv6); if (ret < 0) { + NET_DBG("Cannot send fragment (%d)", ret); goto free_pkts; } /* Then process the rest of the fragments */ pkt->frags = rest; + /* Let this packet to be sent and hopefully it will release + * the memory that can be utilized for next sent IPv6 fragment. + */ + k_yield(); + return 0; free_pkts: @@ -3251,13 +3373,22 @@ int net_ipv6_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt, { struct net_buf *frag = pkt->frags; struct net_buf *prev = NULL; - struct net_buf *orig_ipv6, *rest; + struct net_buf *orig_ipv6 = NULL; + struct net_buf *rest; + int frag_count = 0; int curr_len = 0; int status = false; u16_t ipv6_len = 0, offset = 0; u32_t id = sys_rand32_get(); + u8_t next_hdr; + u16_t next_hdr_idx, last_hdr_idx; int ret; + ret = get_next_hdr(pkt, &next_hdr_idx, &last_hdr_idx, &next_hdr); + if (ret < 0) { + return -EINVAL; + } + /* Split the first fragment that contains the IPv6 header into * two pieces. The "orig_ipv6" will only contain the original IPv6 * header which is copied into each fragment together with @@ -3267,7 +3398,8 @@ int net_ipv6_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt, net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt), &orig_ipv6, &rest, FRAG_BUF_WAIT); if (ret < 0) { - return -ENOMEM; + NET_DBG("Cannot split packet (%d)", ret); + return ret; } ipv6_len = net_buf_frags_len(orig_ipv6); @@ -3301,16 +3433,20 @@ int net_ipv6_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt, status = send_ipv6_fragment(iface, pkt, orig_ipv6, prev, frag, ipv6_len, - offset, fit_len, false); + offset, fit_len, + next_hdr, next_hdr_idx, + last_hdr_idx, false, + frag_count); if (status < 0) { goto out; } - offset += curr_len; + offset += curr_len - (frag->len - fit_len); prev = NULL; frag = pkt->frags; curr_len = 0; + frag_count++; } prev = frag; @@ -3318,12 +3454,16 @@ int net_ipv6_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt, } status = send_ipv6_fragment(iface, pkt, orig_ipv6, prev, prev, - ipv6_len, offset, 0, true); + ipv6_len, offset, 0, next_hdr, + next_hdr_idx, last_hdr_idx, true, + frag_count); net_pkt_unref(pkt); out: - net_pkt_frag_unref(orig_ipv6); + if (orig_ipv6) { + net_pkt_frag_unref(orig_ipv6); + } return status; } diff --git a/subsys/net/ip/ipv6.h b/subsys/net/ip/ipv6.h index 4e26d05c242c..d62115de552e 100644 --- a/subsys/net/ip/ipv6.h +++ b/subsys/net/ip/ipv6.h @@ -374,7 +374,9 @@ static inline void net_ipv6_nbr_set_reachable_timer(struct net_if *iface, * This means that we should receive everything within first two fragments. * The first one being 1280 bytes and the second one 220 bytes. */ +#if !defined(NET_IPV6_FRAGMENTS_MAX_PKT) #define NET_IPV6_FRAGMENTS_MAX_PKT 2 +#endif /** Store pending IPv6 fragment information that is needed for reassembly. */ struct net_ipv6_reassembly { @@ -421,11 +423,15 @@ void net_ipv6_frag_foreach(net_ipv6_frag_cb_t cb, void *user_data); * @brief Find the last IPv6 extension header in the network packet. * * @param pkt Network head packet. + * @param next_hdr_idx Where is the index to next header field that points + * to last header. This is returned to caller. + * @param last_hdr_idx Where is the last header field in the packet. + * This is returned to caller. * - * @return Offset to the extension header within the first fragment of net_pkt. - * Return <0 if the packet is malformed. + * @return Return 0 if ok or <0 if the packet is malformed. */ -int net_ipv6_find_last_ext_hdr(struct net_pkt *pkt); +int net_ipv6_find_last_ext_hdr(struct net_pkt *pkt, u16_t *next_hdr_idx, + u16_t *last_hdr_idx); #if defined(CONFIG_NET_IPV6) void net_ipv6_init(void); diff --git a/subsys/net/ip/net_shell.c b/subsys/net/ip/net_shell.c index 6da57bea585a..0dc16a2a665a 100644 --- a/subsys/net/ip/net_shell.c +++ b/subsys/net/ip/net_shell.c @@ -576,17 +576,37 @@ static void ipv6_frag_cb(struct net_ipv6_reassembly *reass, { int *count = user_data; char src[ADDR_LEN]; + int i; if (!*count) { - printk("\nIPv6 reassembly Id Remain Src\t\t\t\tDst\n"); + printk("\nIPv6 reassembly Id Remain Src \tDst\n"); } snprintk(src, ADDR_LEN, "%s", net_sprint_ipv6_addr(&reass->src)); - printk("%p 0x%08x %5d %s\t%s\n", + printk("%p 0x%08x %5d %16s\t%16s\n", reass, reass->id, k_delayed_work_remaining_get(&reass->timer), src, net_sprint_ipv6_addr(&reass->dst)); + for (i = 0; i < NET_IPV6_FRAGMENTS_MAX_PKT; i++) { + if (reass->pkt[i]) { + struct net_buf *frag = reass->pkt[i]->frags; + + printk("[%d] pkt %p->", i, reass->pkt[i]); + + while (frag) { + printk("%p", frag); + + frag = frag->frags; + if (frag) { + printk("->"); + } + } + + printk("\n"); + } + } + (*count)++; } #endif /* CONFIG_NET_IPV6_FRAGMENT */ diff --git a/tests/net/ipv6_fragment/Makefile b/tests/net/ipv6_fragment/Makefile new file mode 100644 index 000000000000..4b05dca8edb6 --- /dev/null +++ b/tests/net/ipv6_fragment/Makefile @@ -0,0 +1,4 @@ +BOARD ?= qemu_x86 +CONF_FILE = prj.conf + +include $(ZEPHYR_BASE)/Makefile.test diff --git a/tests/net/ipv6_fragment/prj.conf b/tests/net/ipv6_fragment/prj.conf new file mode 100644 index 000000000000..4d3008585668 --- /dev/null +++ b/tests/net/ipv6_fragment/prj.conf @@ -0,0 +1,36 @@ +CONFIG_NETWORKING=y +CONFIG_NET_IPV6=y +CONFIG_NET_UDP=y +CONFIG_NET_TCP=n +CONFIG_NET_IPV4=n +CONFIG_NET_MAX_CONTEXTS=4 +CONFIG_NET_L2_DUMMY=y +CONFIG_NET_LOG=y +CONFIG_SYS_LOG_SHOW_COLOR=y +CONFIG_RANDOM_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_NET_IPV6_DAD=n +CONFIG_NET_IPV6_MLD=n +CONFIG_NET_PKT_TX_COUNT=50 +CONFIG_NET_PKT_RX_COUNT=50 +CONFIG_NET_BUF_RX_COUNT=50 +CONFIG_NET_BUF_TX_COUNT=50 +CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=6 +CONFIG_NET_IPV6_ND=n +CONFIG_NET_IPV6_FRAGMENT=y +#CONFIG_NET_UDP_CHECKSUM=n +#CONFIG_NET_TCP_CHECKSUM=n + +CONFIG_ZTEST=y + +CONFIG_SYS_LOG_NET_LEVEL=4 +CONFIG_SYS_LOG_SHOW_COLOR=y +CONFIG_INIT_STACKS=y +CONFIG_PRINTK=y +CONFIG_NET_STATISTICS=n + +CONFIG_NET_DEBUG_IF=n +CONFIG_NET_DEBUG_CONN=n +CONFIG_NET_DEBUG_IPV6=n +CONFIG_NET_DEBUG_ICMPV6=n +CONFIG_NET_DEBUG_CORE=n diff --git a/tests/net/ipv6_fragment/src/Makefile b/tests/net/ipv6_fragment/src/Makefile new file mode 100644 index 000000000000..cc0883fd51af --- /dev/null +++ b/tests/net/ipv6_fragment/src/Makefile @@ -0,0 +1,4 @@ +obj-y = main.o +ccflags-y += -I${ZEPHYR_BASE}/subsys/net/ip + +include $(ZEPHYR_BASE)/tests/Makefile.test diff --git a/tests/net/ipv6_fragment/src/main.c b/tests/net/ipv6_fragment/src/main.c new file mode 100644 index 000000000000..ab14776148e9 --- /dev/null +++ b/tests/net/ipv6_fragment/src/main.c @@ -0,0 +1,703 @@ +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define NET_LOG_ENABLED 1 +#include "net_private.h" + +#include "ipv6.h" +#include "udp.h" + +#if defined(CONFIG_NET_DEBUG_IPV6) +#define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__) +#else +#define DBG(fmt, ...) +#endif + +/* Interface 1 addresses */ +static struct in6_addr my_addr1 = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; + +/* Interface 2 addresses */ +static struct in6_addr my_addr2 = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x2 } } }; + +/* Extra address is assigned to ll_addr */ +static struct in6_addr ll_addr = { { { 0xfe, 0x80, 0x43, 0xb8, 0, 0, 0, 0, + 0, 0, 0, 0xf2, 0xaa, 0x29, 0x02, + 0x04 } } }; + +static u8_t mac2_addr[] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x02 }; +static struct net_linkaddr ll_addr2 = { + .addr = mac2_addr, + .len = 6, +}; + +/* No extension header */ +static const unsigned char ipv6_udp[] = { +/* IPv6 header starts here */ +0x60, 0x00, 0x00, 0x00, 0x00, 0x36, 0x11, 0x3f, /* `....6.? */ +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, +/* UDP header starts here (checksum is "fixed" in this example) */ +0xaa, 0xdc, 0xbf, 0xd7, 0x00, 0x2e, 0xa2, 0x55, /* ......M. */ +/* User data starts here and is appended in corresponding function */ +}; + +/* IPv6 hop-by-hop option in the message */ +static const unsigned char ipv6_hbho[] = { +/* IPv6 header starts here */ +0x60, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x3f, /* `....6.? */ +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, +/* Hop-by-hop option starts here */ +0x11, 0x00, +/* RPL sub-option starts here */ +0x63, 0x04, 0x80, 0x1e, 0x01, 0x00, /* ..c..... */ +/* UDP header starts here (checksum is "fixed" in this example) */ +0xaa, 0xdc, 0xbf, 0xd7, 0x00, 0x2e, 0xa2, 0x55, /* ......M. */ +/* User data starts here and is appended in corresponding function */ +}; + +/* IPv6 hop-by-hop option in the message */ +static const unsigned char ipv6_hbho_frag[] = { +/* IPv6 header starts here */ +0x60, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x3f, /* `....6.? */ +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, +/* Hop-by-hop option starts here */ +0x2c, 0x00, +/* RPL sub-option starts here */ +0x63, 0x04, 0x80, 0x1e, 0x01, 0x00, /* ..c..... */ +/* IPv6 fragment header */ +0x11, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, +/* UDP header starts here (checksum is "fixed" in this example) */ +0xaa, 0xdc, 0xbf, 0xd7, 0x00, 0x2e, 0xa2, 0x55, /* ......M. */ +/* User data starts here and is appended in corresponding function */ +}; + +static unsigned char ipv6_first_frag[] = { +/* IPv6 header starts here */ +0x60, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x3f, /* `....6.? */ +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, +/* Hop-by-hop option starts here */ +0x2C, 0x00, +/* RPL sub-option starts here */ +0x63, 0x04, 0x80, 0x1e, 0x01, 0x00, /* ..c..... */ +/* IPv6 fragment header */ +0x11, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, +/* UDP header starts here (checksum is "fixed" in this example) */ +0xaa, 0xdc, 0xbf, 0xd7, 0x00, 0x2e, 0xa2, 0x55, /* ......M. */ +/* User data starts here and is appended in corresponding function */ +}; + +static unsigned char ipv6_second_frag[] = { +/* IPv6 header starts here */ +0x60, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x3f, /* `....6.? */ +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, +/* Hop-by-hop option starts here */ +0x2C, 0x00, +/* RPL sub-option starts here */ +0x63, 0x04, 0x80, 0x1e, 0x01, 0x00, /* ..c..... */ +/* IPv6 fragment header */ +0x11, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, +}; + +static int frag_count; + +static struct net_if *iface1; +static struct net_if *iface2; + +static bool test_failed; +static bool test_started; +static struct k_sem wait_data; + +static u16_t pkt_data_len; +static u16_t pkt_recv_data_len; + +#define WAIT_TIME K_SECONDS(1) + +#define ALLOC_TIMEOUT 500 + +struct net_if_test { + u8_t idx; + u8_t mac_addr[sizeof(struct net_eth_addr)]; + struct net_linkaddr ll_addr; +}; + +static int net_iface_dev_init(struct device *dev) +{ + return 0; +} + +static u8_t *net_iface_get_mac(struct device *dev) +{ + struct net_if_test *data = dev->driver_data; + + if (data->mac_addr[2] == 0x00) { + /* 00-00-5E-00-53-xx Documentation RFC 7042 */ + data->mac_addr[0] = 0x00; + data->mac_addr[1] = 0x00; + data->mac_addr[2] = 0x5E; + data->mac_addr[3] = 0x00; + data->mac_addr[4] = 0x53; + data->mac_addr[5] = sys_rand32_get(); + } + + data->ll_addr.addr = data->mac_addr; + data->ll_addr.len = 6; + + return data->mac_addr; +} + +static void net_iface_init(struct net_if *iface) +{ + u8_t *mac = net_iface_get_mac(net_if_get_device(iface)); + + net_if_set_link_addr(iface, mac, sizeof(struct net_eth_addr), + NET_LINK_ETHERNET); +} + +static int verify_fragment(struct net_pkt *pkt) +{ + /* The fragment needs to have + * 1) IPv6 header + * 2) HBH option (if any) + * 3) IPv6 fragment header + * 4) UDP/ICMPv6/TCP header + * 5) data + */ + u16_t offset; + + frag_count++; + + if (frag_count == 1) { + /* First fragment received. Make sure that all the + * things are correct before the fragment header. + */ + size_t total_len = net_pkt_get_len(pkt); + + total_len -= sizeof(struct net_ipv6_hdr); + + ipv6_first_frag[4] = total_len / 256; + ipv6_first_frag[5] = total_len - + ipv6_first_frag[4] * 256; + + if ((total_len / 256) != pkt->frags->data[4]) { + DBG("Invalid length, 1st byte\n"); + return -EINVAL; + } + + if ((total_len - pkt->frags->data[4] * 256) != + pkt->frags->data[5]) { + DBG("Invalid length, 2nd byte\n"); + return -EINVAL; + } + + offset = pkt->frags->data[6 * 8 + 2] * 256 + + (pkt->frags->data[6 * 8 + 3] & 0xfe); + if (offset != 0) { + DBG("Invalid offset\n"); + return -EINVAL; + } + + if ((ipv6_first_frag[6 * 8 + 3] & 0x01) != 1) { + DBG("Invalid MORE flag for first fragment\n"); + return -EINVAL; + } + + pkt_recv_data_len += total_len - 8 /* HBHO */ - 8 /* UDP */ - + sizeof(struct net_ipv6_frag_hdr); + + /* Rewrite the fragment id so that the memcmp() will not fail */ + ipv6_first_frag[6 * 8 + 4] = pkt->frags->data[6 * 8 + 4]; + ipv6_first_frag[6 * 8 + 5] = pkt->frags->data[6 * 8 + 5]; + ipv6_first_frag[6 * 8 + 6] = pkt->frags->data[6 * 8 + 6]; + ipv6_first_frag[6 * 8 + 7] = pkt->frags->data[6 * 8 + 7]; + + if (memcmp(pkt->frags->data, ipv6_first_frag, 7 * 8) != 0) { + net_hexdump("received", pkt->frags->data, 7 * 8); + DBG("\n"); + net_hexdump("expected", ipv6_first_frag, 7 * 8); + + return -EINVAL; + } + } + + if (frag_count == 2) { + /* Second fragment received. */ + size_t total_len = net_pkt_get_len(pkt); + + total_len -= sizeof(struct net_ipv6_hdr); + + ipv6_second_frag[4] = total_len / 256; + ipv6_second_frag[5] = total_len - + ipv6_second_frag[4] * 256; + + if ((total_len / 256) != pkt->frags->data[4]) { + DBG("Invalid length, 1st byte\n"); + return -EINVAL; + } + + if ((total_len - pkt->frags->data[4] * 256) != + pkt->frags->data[5]) { + DBG("Invalid length, 2nd byte\n"); + return -EINVAL; + } + + offset = pkt->frags->data[6 * 8 + 2] * 256 + + (pkt->frags->data[6 * 8 + 3] & 0xfe); + + if (offset != pkt_recv_data_len) { + DBG("Invalid offset %d received %d\n", + offset, pkt_recv_data_len); + return -EINVAL; + } + + /* Make sure the MORE flag is set correctly */ + if ((pkt->frags->data[6 * 8 + 3] & 0x01) != 0) { + DBG("Invalid MORE flag for second fragment\n"); + return -EINVAL; + } + + pkt_recv_data_len += total_len - 8 /* HBHO */ - 8 /* UDP */ - + sizeof(struct net_ipv6_frag_hdr); + + ipv6_second_frag[6 * 8 + 2] = pkt->frags->data[6 * 8 + 2]; + ipv6_second_frag[6 * 8 + 3] = pkt->frags->data[6 * 8 + 3]; + + /* Rewrite the fragment id so that the memcmp() will not fail */ + ipv6_second_frag[6 * 8 + 4] = pkt->frags->data[6 * 8 + 4]; + ipv6_second_frag[6 * 8 + 5] = pkt->frags->data[6 * 8 + 5]; + ipv6_second_frag[6 * 8 + 6] = pkt->frags->data[6 * 8 + 6]; + ipv6_second_frag[6 * 8 + 7] = pkt->frags->data[6 * 8 + 7]; + + if (memcmp(pkt->frags->data, ipv6_second_frag, 7 * 8) != 0) { + net_hexdump("received 2", pkt->frags->data, 7 * 8); + DBG("\n"); + net_hexdump("expected 2", ipv6_second_frag, 7 * 8); + + return -EINVAL; + } + + if (pkt_data_len != pkt_recv_data_len) { + DBG("Invalid amount of data received (%d vs %d)\n", + pkt_data_len, pkt_recv_data_len); + return -EINVAL; + } + } + + return 0; +} + +static int sender_iface(struct net_if *iface, struct net_pkt *pkt) +{ + if (!pkt->frags) { + DBG("No data to send!\n"); + return -ENODATA; + } + + if (test_started) { + struct net_if_test *data = iface->dev->driver_data; + + DBG("Sending at iface %d %p\n", net_if_get_by_iface(iface), + iface); + + if (net_pkt_iface(pkt) != iface) { + DBG("Invalid interface %p, expecting %p\n", + net_pkt_iface(pkt), iface); + test_failed = true; + } + + if (net_if_get_by_iface(iface) != data->idx) { + DBG("Invalid interface %d index, expecting %d\n", + data->idx, net_if_get_by_iface(iface)); + test_failed = true; + } + + /* Verify the fragments */ + if (verify_fragment(pkt) < 0) { + DBG("Fragments cannot be verified\n"); + test_failed = true; + } else { + k_sem_give(&wait_data); + } + } + + zassert_false(test_failed, "Fragment verify failed"); + + net_pkt_unref(pkt); + + return 0; +} + +struct net_if_test net_iface1_data; +struct net_if_test net_iface2_data; + +static struct net_if_api net_iface_api = { + .init = net_iface_init, + .send = sender_iface, +}; + +#define _ETH_L2_LAYER DUMMY_L2 +#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(DUMMY_L2) + +NET_DEVICE_INIT_INSTANCE(net_iface1_test, + "iface1", + iface1, + net_iface_dev_init, + &net_iface1_data, + NULL, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &net_iface_api, + _ETH_L2_LAYER, + _ETH_L2_CTX_TYPE, + 127); + +NET_DEVICE_INIT_INSTANCE(net_iface2_test, + "iface2", + iface2, + net_iface_dev_init, + &net_iface2_data, + NULL, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &net_iface_api, + _ETH_L2_LAYER, + _ETH_L2_CTX_TYPE, + 127); + +static void add_nbr(struct net_if *iface, + struct in6_addr *addr, + struct net_linkaddr *lladdr) +{ + struct net_nbr *nbr; + + nbr = net_ipv6_nbr_add(iface, addr, lladdr, false, + NET_IPV6_NBR_STATE_REACHABLE); + zassert_not_null(nbr, "Cannot add neighbor"); +} + +static enum net_verdict udp_data_received(struct net_conn *conn, + struct net_pkt *pkt, + void *user_data) +{ + DBG("Data %p received\n", pkt); + + net_pkt_unref(pkt); + + return NET_OK; +} + +static void setup_udp_handler(const struct in6_addr *raddr, + const struct in6_addr *laddr, + u16_t remote_port, + u16_t local_port) +{ + static struct net_conn_handle *handle; + struct sockaddr remote_addr = { 0 }; + struct sockaddr local_addr = { 0 }; + int ret; + + net_ipaddr_copy(&net_sin6(&local_addr)->sin6_addr, laddr); + local_addr.family = AF_INET6; + + net_ipaddr_copy(&net_sin6(&remote_addr)->sin6_addr, raddr); + remote_addr.family = AF_INET6; + + ret = net_udp_register(&remote_addr, &local_addr, remote_port, + local_port, udp_data_received, + NULL, &handle); + zassert_equal(ret, 0, "Cannot register UDP handler"); +} + +static void setup(void) +{ + struct net_if_addr *ifaddr; + int idx; + + /* The semaphore is there to wait the data to be received. */ + k_sem_init(&wait_data, 0, UINT_MAX); + + iface1 = net_if_get_by_index(0); + iface2 = net_if_get_by_index(1); + + ((struct net_if_test *)iface1->dev->driver_data)->idx = 0; + ((struct net_if_test *)iface2->dev->driver_data)->idx = 1; + + idx = net_if_get_by_iface(iface1); + zassert_equal(idx, 0, "Invalid index iface1"); + + idx = net_if_get_by_iface(iface2); + zassert_equal(idx, 1, "Invalid index iface2"); + + zassert_not_null(iface1, "Interface 1"); + zassert_not_null(iface2, "Interface 2"); + + ifaddr = net_if_ipv6_addr_add(iface1, &my_addr1, + NET_ADDR_MANUAL, 0); + if (!ifaddr) { + DBG("Cannot add IPv6 address %s\n", + net_sprint_ipv6_addr(&my_addr1)); + zassert_not_null(ifaddr, "addr1"); + } + + /* For testing purposes we need to set the adddresses preferred */ + ifaddr->addr_state = NET_ADDR_PREFERRED; + + ifaddr = net_if_ipv6_addr_add(iface1, &ll_addr, + NET_ADDR_MANUAL, 0); + if (!ifaddr) { + DBG("Cannot add IPv6 address %s\n", + net_sprint_ipv6_addr(&ll_addr)); + zassert_not_null(ifaddr, "ll_addr"); + } + + ifaddr->addr_state = NET_ADDR_PREFERRED; + + net_if_up(iface1); + net_if_up(iface2); + + add_nbr(iface1, &my_addr2, &ll_addr2); + + /* Remote and local are swapped so that we can receive the sent + * packet. + */ + setup_udp_handler(&my_addr1, &my_addr2, 4352, 25348); + + /* The interface might receive data which might fail the checks + * in the iface sending function, so we need to reset the failure + * flag. + */ + test_failed = false; + + test_started = true; +} + +static void find_last_ipv6_fragment_udp(void) +{ + u16_t next_hdr_idx = 0; + u16_t last_hdr_pos = 0; + struct net_pkt *pkt; + int ret; + + pkt = net_pkt_get_reserve_tx(0, ALLOC_TIMEOUT); + zassert_not_null(pkt, "packet"); + + net_pkt_set_iface(pkt, iface1); + net_pkt_set_family(pkt, AF_INET6); + net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr)); + net_pkt_set_ipv6_ext_len(pkt, sizeof(ipv6_udp) - + sizeof(struct net_ipv6_hdr)); + net_pkt_ll_clear(pkt); + + /* Add IPv6 header + UDP */ + ret = net_pkt_append_all(pkt, sizeof(ipv6_udp), ipv6_udp, + ALLOC_TIMEOUT); + zassert_true(ret, "IPv6 header append failed"); + + ret = net_ipv6_find_last_ext_hdr(pkt, &next_hdr_idx, &last_hdr_pos); + zassert_equal(ret, 0, "Cannot find last header"); + + zassert_equal(next_hdr_idx, 6, "Next header index wrong"); + zassert_equal(last_hdr_pos, sizeof(struct net_ipv6_hdr), + "Last header position wrong"); + + zassert_equal(NET_IPV6_HDR(pkt)->nexthdr, 0x11, "Invalid next header"); + zassert_equal(pkt->frags->data[next_hdr_idx], 0x11, "Invalid next " + "header"); + + net_pkt_unref(pkt); +} + +static void find_last_ipv6_fragment_hbho_udp(void) +{ + u16_t next_hdr_idx = 0; + u16_t last_hdr_pos = 0; + struct net_pkt *pkt; + int ret; + + pkt = net_pkt_get_reserve_tx(0, ALLOC_TIMEOUT); + zassert_not_null(pkt, "packet"); + + net_pkt_set_iface(pkt, iface1); + net_pkt_set_family(pkt, AF_INET6); + net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr)); + net_pkt_set_ipv6_ext_len(pkt, sizeof(ipv6_hbho) - + sizeof(struct net_ipv6_hdr)); + net_pkt_ll_clear(pkt); + + /* Add IPv6 header + HBH option */ + ret = net_pkt_append_all(pkt, sizeof(ipv6_hbho), ipv6_hbho, + ALLOC_TIMEOUT); + zassert_true(ret, "IPv6 header append failed"); + + ret = net_ipv6_find_last_ext_hdr(pkt, &next_hdr_idx, &last_hdr_pos); + zassert_equal(ret, 0, "Cannot find last header"); + + zassert_equal(next_hdr_idx, sizeof(struct net_ipv6_hdr), + "Next header index wrong"); + zassert_equal(last_hdr_pos, sizeof(struct net_ipv6_hdr) + 8, + "Last header position wrong"); + + zassert_equal(NET_IPV6_HDR(pkt)->nexthdr, 0, "Invalid next header"); + zassert_equal(pkt->frags->data[next_hdr_idx], 0x11, "Invalid next " + "header"); + + net_pkt_unref(pkt); +} + +static void find_last_ipv6_fragment_hbho_frag(void) +{ + u16_t next_hdr_idx = 0; + u16_t last_hdr_pos = 0; + struct net_pkt *pkt; + int ret; + + pkt = net_pkt_get_reserve_tx(0, ALLOC_TIMEOUT); + zassert_not_null(pkt, "packet"); + + net_pkt_set_iface(pkt, iface1); + net_pkt_set_family(pkt, AF_INET6); + net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr)); + net_pkt_set_ipv6_ext_len(pkt, sizeof(ipv6_hbho_frag) - + sizeof(struct net_ipv6_hdr)); + net_pkt_ll_clear(pkt); + + /* Add IPv6 header + HBH option + fragment header */ + ret = net_pkt_append_all(pkt, sizeof(ipv6_hbho_frag), ipv6_hbho_frag, + ALLOC_TIMEOUT); + zassert_true(ret, "IPv6 header append failed"); + + ret = net_ipv6_find_last_ext_hdr(pkt, &next_hdr_idx, &last_hdr_pos); + zassert_equal(ret, 0, "Cannot find last header"); + + zassert_equal(next_hdr_idx, sizeof(struct net_ipv6_hdr) + 8, + "Next header index wrong"); + zassert_equal(last_hdr_pos, sizeof(struct net_ipv6_hdr) + 8 + 8, + "Last header position wrong"); + + zassert_equal(NET_IPV6_HDR(pkt)->nexthdr, 0, "Invalid next header"); + zassert_equal(pkt->frags->data[next_hdr_idx], 0x11, "Invalid next " + "header"); + + net_pkt_unref(pkt); +} + +static void send_ipv6_fragment(void) +{ +#define MAX_LEN 1600 + static char data[] = "123456789."; + int data_len = sizeof(data) - 1; + int count = MAX_LEN / data_len; + struct net_pkt *pkt; + size_t total_len; + int i, ret; + + pkt_data_len = 0; + + pkt = net_pkt_get_reserve_tx(0, ALLOC_TIMEOUT); + zassert_not_null(pkt, "packet"); + + net_pkt_set_iface(pkt, iface1); + net_pkt_set_family(pkt, AF_INET6); + net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr)); + net_pkt_set_ipv6_ext_len(pkt, 8 + 8); /* hbho + udp */ + net_pkt_ll_clear(pkt); + + /* Add IPv6 header + HBH option */ + ret = net_pkt_append_all(pkt, sizeof(ipv6_hbho), ipv6_hbho, + ALLOC_TIMEOUT); + zassert_true(ret, "IPv6 header append failed"); + + /* Then add some data that is over 1280 bytes long */ + for (i = 0; i < count; i++) { + bool written = net_pkt_append_all(pkt, data_len, data, + ALLOC_TIMEOUT); + zassert_true(written, "Cannot append data"); + + pkt_data_len += data_len; + } + + zassert_equal(pkt_data_len, count * data_len, "Data size mismatch"); + + total_len = net_pkt_get_len(pkt); + total_len -= sizeof(struct net_ipv6_hdr); + + DBG("Sending %zd bytes of which ext %d and data %d bytes\n", + total_len, net_pkt_ipv6_ext_len(pkt), pkt_data_len); + + zassert_equal(total_len - net_pkt_ipv6_ext_len(pkt), pkt_data_len, + "Packet size invalid"); + + NET_IPV6_HDR(pkt)->len[0] = total_len / 256; + NET_IPV6_HDR(pkt)->len[1] = total_len - + NET_IPV6_HDR(pkt)->len[0] * 256; + + NET_UDP_HDR(pkt)->chksum = 0; + NET_UDP_HDR(pkt)->chksum = ~net_calc_chksum_udp(pkt); + + test_failed = false; + + ret = net_send_data(pkt); + if (ret < 0) { + DBG("Cannot send test packet (%d)\n", ret); + zassert_equal(ret, 0, "Cannot send"); + } + + if (k_sem_take(&wait_data, WAIT_TIME)) { + DBG("Timeout while waiting interface data\n"); + zassert_equal(ret, 0, "Timeout"); + } +} + +static void recv_ipv6_fragment(void) +{ + /* TODO: Verify that we can receive individual fragments and + * then reassemble them back. + */ +} + +void test_main(void) +{ + ztest_test_suite(net_ipv6_fragment_test, + ztest_unit_test(setup), + ztest_unit_test(find_last_ipv6_fragment_udp), + ztest_unit_test(find_last_ipv6_fragment_hbho_udp), + ztest_unit_test(find_last_ipv6_fragment_hbho_frag), + ztest_unit_test(send_ipv6_fragment), + ztest_unit_test(recv_ipv6_fragment) + ); + + ztest_run_test_suite(net_ipv6_fragment_test); +} diff --git a/tests/net/ipv6_fragment/testcase.ini b/tests/net/ipv6_fragment/testcase.ini new file mode 100644 index 000000000000..2b07c158c84f --- /dev/null +++ b/tests/net/ipv6_fragment/testcase.ini @@ -0,0 +1,4 @@ +[test] +tags = net +arch_whitelist = x86 +platform_whitelist = qemu_x86