diff --git a/drivers/console/telnet_console.c b/drivers/console/telnet_console.c index 6140b2964aefd..322fdef88cda1 100644 --- a/drivers/console/telnet_console.c +++ b/drivers/console/telnet_console.c @@ -395,6 +395,8 @@ static inline void telnet_handle_input(struct net_pkt *pkt) static void telnet_recv(struct net_context *client, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/drivers/ethernet/eth_dw.c b/drivers/ethernet/eth_dw.c index 55b7b30dc646f..f5c6e80a620ef 100644 --- a/drivers/ethernet/eth_dw.c +++ b/drivers/ethernet/eth_dw.c @@ -86,14 +86,14 @@ static void eth_rx(struct device *dev) frm_len -= sizeof(u32_t); } - pkt = net_pkt_get_reserve_rx(K_NO_WAIT); + pkt = net_pkt_rx_alloc_with_buffer(context->iface, frm_len, + AF_UNSPEC, 0, K_NO_WAIT); if (!pkt) { LOG_ERR("Failed to obtain RX buffer"); goto error; } - if (!net_pkt_append_all(pkt, frm_len, (u8_t *)context->rx_buf, - K_NO_WAIT)) { + if (net_pkt_write_new(pkt, (void *)context->rx_buf, frm_len)) { LOG_ERR("Failed to append RX buffer to context buffer"); net_pkt_unref(pkt); goto error; diff --git a/drivers/ethernet/eth_e1000.c b/drivers/ethernet/eth_e1000.c index c3da959eddfba..de0214d8752ef 100644 --- a/drivers/ethernet/eth_e1000.c +++ b/drivers/ethernet/eth_e1000.c @@ -50,19 +50,6 @@ static enum ethernet_hw_caps e1000_caps(struct device *dev) ETHERNET_LINK_1000BASE_T; } -static size_t e1000_linearize(struct net_pkt *pkt, void *buf, size_t bufsize) -{ - size_t len = 0; - struct net_buf *nb; - - for (nb = pkt->frags; nb; nb = nb->frags) { - memcpy((u8_t *) buf + len, nb->data, nb->len); - len += nb->len; - } - - return len; -} - static int e1000_tx(struct e1000_dev *dev, void *data, size_t data_len) { dev->tx.addr = POINTER_TO_INT(data); @@ -83,7 +70,11 @@ static int e1000_tx(struct e1000_dev *dev, void *data, size_t data_len) static int e1000_send(struct device *device, struct net_pkt *pkt) { struct e1000_dev *dev = device->driver_data; - size_t len = e1000_linearize(pkt, dev->txb, sizeof(dev->txb)); + size_t len = net_pkt_get_len(pkt); + + if (net_pkt_read_new(pkt, dev->txb, len)) { + return -EIO; + } return e1000_tx(dev, dev->txb, len); } @@ -99,19 +90,20 @@ static struct net_pkt *e1000_rx(struct e1000_dev *dev) goto out; } - pkt = net_pkt_get_reserve_rx(K_NO_WAIT); + pkt = net_pkt_rx_alloc_with_buffer(dev->iface, dev->rx.len - 4, + AF_UNSPEC, 0, K_NO_WAIT); if (!pkt) { - LOG_ERR("Out of RX buffers"); + LOG_ERR("Out of buffers"); goto out; } - if (!net_pkt_append_all(pkt, dev->rx.len - 4, - INT_TO_POINTER((u32_t) dev->rx.addr), - K_NO_WAIT)) { + if (net_pkt_write_new(pkt, INT_TO_POINTER((u32_t) dev->rx.addr), + dev->rx.len - 4)) { LOG_ERR("Out of memory for received frame"); net_pkt_unref(pkt); pkt = NULL; } + out: return pkt; } diff --git a/drivers/ethernet/eth_enc28j60.c b/drivers/ethernet/eth_enc28j60.c index d1c33b4111069..f00853f9cfc8b 100644 --- a/drivers/ethernet/eth_enc28j60.c +++ b/drivers/ethernet/eth_enc28j60.c @@ -500,7 +500,6 @@ static int eth_enc28j60_rx(struct device *dev) do { struct net_buf *pkt_buf = NULL; - struct net_buf *last_buf = NULL; u16_t frm_len = 0U; u8_t info[RSV_SIZE]; struct net_pkt *pkt; @@ -538,36 +537,22 @@ static int eth_enc28j60_rx(struct device *dev) lengthfr = frm_len; /* Get the frame from the buffer */ - pkt = net_pkt_get_reserve_rx(config->timeout); + pkt = net_pkt_rx_alloc_with_buffer(context->iface, frm_len, + AF_UNSPEC, 0, + config->timeout); if (!pkt) { LOG_ERR("Could not allocate rx buffer"); eth_stats_update_errors_rx(context->iface); goto done; } + pkt_buf = pkt->buffer; + do { size_t frag_len; u8_t *data_ptr; size_t spi_frame_len; - /* Reserve a data frag to receive the frame */ - pkt_buf = net_pkt_get_frag(pkt, config->timeout); - if (!pkt_buf) { - LOG_ERR("Could not allocate data buffer"); - eth_stats_update_errors_rx(context->iface); - net_pkt_unref(pkt); - - goto done; - } - - if (!last_buf) { - net_pkt_frag_insert(pkt, pkt_buf); - } else { - net_buf_frag_insert(last_buf, pkt_buf); - } - - last_buf = pkt_buf; - data_ptr = pkt_buf->data; /* Review the space available for the new frag */ @@ -585,6 +570,7 @@ static int eth_enc28j60_rx(struct device *dev) /* One fragment has been written via SPI */ frm_len -= spi_frame_len; + pkt_buf = pkt_buf->frags; } while (frm_len > 0); /* Let's pop the useless CRC */ @@ -599,7 +585,9 @@ static int eth_enc28j60_rx(struct device *dev) /* Feed buffer frame to IP stack */ LOG_DBG("Received packet of length %u", lengthfr); - net_recv_data(context->iface, pkt); + if (net_recv_data(context->iface, pkt) < 0) { + net_pkt_unref(pkt); + } done: /* Free buffer memory and decrement rx counter */ eth_enc28j60_set_bank(dev, ENC28J60_REG_ERXRDPTL); diff --git a/drivers/ethernet/eth_mcux.c b/drivers/ethernet/eth_mcux.c index b582b70037a31..79fb01816a445 100644 --- a/drivers/ethernet/eth_mcux.c +++ b/drivers/ethernet/eth_mcux.c @@ -490,16 +490,13 @@ static bool eth_get_ptp_data(struct net_if *iface, struct net_pkt *pkt, static int eth_tx(struct device *dev, struct net_pkt *pkt) { struct eth_context *context = dev->driver_data; - const struct net_buf *frag; - u8_t *dst; + u16_t total_len = net_pkt_get_len(pkt); status_t status; unsigned int imask; #if defined(CONFIG_PTP_CLOCK_MCUX) bool timestamped_frame; #endif - u16_t total_len = net_pkt_get_len(pkt); - k_sem_take(&context->tx_buf_sem, K_FOREVER); /* As context->frame_buf is shared resource used by both eth_tx @@ -507,13 +504,9 @@ static int eth_tx(struct device *dev, struct net_pkt *pkt) */ imask = irq_lock(); - /* Copy the fragments */ - dst = context->frame_buf; - frag = pkt->frags; - while (frag) { - memcpy(dst, frag->data, frag->len); - dst += frag->len; - frag = frag->frags; + if (net_pkt_read_new(pkt, context->frame_buf, total_len)) { + irq_unlock(imask); + return -EIO; } /* FIXME: Dirty workaround. @@ -559,13 +552,11 @@ static int eth_tx(struct device *dev, struct net_pkt *pkt) static void eth_rx(struct device *iface) { struct eth_context *context = iface->driver_data; - struct net_buf *prev_buf; - struct net_pkt *pkt; - const u8_t *src; + u16_t vlan_tag = NET_VLAN_TAG_UNSPEC; u32_t frame_length = 0U; + struct net_pkt *pkt; status_t status; unsigned int imask; - u16_t vlan_tag = NET_VLAN_TAG_UNSPEC; #if defined(CONFIG_PTP_CLOCK_MCUX) enet_ptp_time_data_t ptpTimeData; @@ -588,7 +579,9 @@ static void eth_rx(struct device *iface) goto flush; } - pkt = net_pkt_get_reserve_rx(K_NO_WAIT); + /* Using root iface. It will be updated in net_recv_data() */ + pkt = net_pkt_rx_alloc_with_buffer(context->iface, frame_length, + AF_UNSPEC, 0, K_NO_WAIT); if (!pkt) { goto flush; } @@ -607,39 +600,12 @@ static void eth_rx(struct device *iface) goto error; } - src = context->frame_buf; - prev_buf = NULL; - do { - struct net_buf *pkt_buf; - size_t frag_len; - - pkt_buf = net_pkt_get_frag(pkt, K_NO_WAIT); - if (!pkt_buf) { - irq_unlock(imask); - LOG_ERR("Failed to get fragment buf"); - net_pkt_unref(pkt); - assert(status == kStatus_Success); - goto error; - } - - if (!prev_buf) { - net_pkt_frag_insert(pkt, pkt_buf); - } else { - net_buf_frag_insert(prev_buf, pkt_buf); - } - - prev_buf = pkt_buf; - - frag_len = net_buf_tailroom(pkt_buf); - if (frag_len > frame_length) { - frag_len = frame_length; - } - - memcpy(pkt_buf->data, src, frag_len); - net_buf_add(pkt_buf, frag_len); - src += frag_len; - frame_length -= frag_len; - } while (frame_length > 0); + if (net_pkt_write_new(pkt, context->frame_buf, frame_length)) { + irq_unlock(imask); + LOG_ERR("Unable to write frame into the pkt"); + net_pkt_unref(pkt); + goto error; + } #if defined(CONFIG_NET_VLAN) { diff --git a/drivers/ethernet/eth_native_posix.c b/drivers/ethernet/eth_native_posix.c index 8ebbaf7673d3d..1225bd3011abf 100644 --- a/drivers/ethernet/eth_native_posix.c +++ b/drivers/ethernet/eth_native_posix.c @@ -210,15 +210,12 @@ static void update_gptp(struct net_if *iface, struct net_pkt *pkt, static int eth_send(struct device *dev, struct net_pkt *pkt) { struct eth_context *ctx = dev->driver_data; - struct net_buf *frag; - int count = 0; + int count = net_pkt_get_len(pkt); int ret; - frag = pkt->frags; - while (frag) { - memcpy(ctx->send + count, frag->data, frag->len); - count += frag->len; - frag = frag->frags; + ret = net_pkt_read_new(pkt, ctx->send, count); + if (ret) { + return ret; } update_gptp(net_pkt_iface(pkt), pkt, true); @@ -269,37 +266,24 @@ static inline struct net_if *get_iface(struct eth_context *ctx, static int read_data(struct eth_context *ctx, int fd) { u16_t vlan_tag = NET_VLAN_TAG_UNSPEC; - int count = 0; struct net_if *iface; struct net_pkt *pkt; - struct net_buf *frag; - u32_t pkt_len; - int ret; + int count; - ret = eth_read_data(fd, ctx->recv, sizeof(ctx->recv)); - if (ret <= 0) { + count = eth_read_data(fd, ctx->recv, sizeof(ctx->recv)); + if (count <= 0) { return 0; } - pkt = net_pkt_get_reserve_rx(NET_BUF_TIMEOUT); + pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, count, + AF_UNSPEC, 0, NET_BUF_TIMEOUT); if (!pkt) { return -ENOMEM; } - do { - frag = net_pkt_get_frag(pkt, NET_BUF_TIMEOUT); - if (!frag) { - net_pkt_unref(pkt); - return -ENOMEM; - } - - net_pkt_frag_add(pkt, frag); - - net_buf_add_mem(frag, ctx->recv + count, - min(net_buf_tailroom(frag), ret)); - ret -= frag->len; - count += frag->len; - } while (ret > 0); + if (net_pkt_write_new(pkt, ctx->recv, count)) { + return -ENOBUFS; + } #if defined(CONFIG_NET_VLAN) { @@ -325,9 +309,8 @@ static int read_data(struct eth_context *ctx, int fd) #endif iface = get_iface(ctx, vlan_tag); - pkt_len = net_pkt_get_len(pkt); - LOG_DBG("Recv pkt %p len %d", pkt, pkt_len); + LOG_DBG("Recv pkt %p len %d", pkt, count); update_gptp(iface, pkt, false); diff --git a/drivers/ethernet/eth_stm32_hal.c b/drivers/ethernet/eth_stm32_hal.c index d3b209265aa01..94ae9cdda78fc 100644 --- a/drivers/ethernet/eth_stm32_hal.c +++ b/drivers/ethernet/eth_stm32_hal.c @@ -61,7 +61,6 @@ static int eth_tx(struct device *dev, struct net_pkt *pkt) ETH_HandleTypeDef *heth; u8_t *dma_buffer; int res; - struct net_buf *frag; u16_t total_len; __IO ETH_DMADescTypeDef *dma_tx_desc; @@ -88,11 +87,9 @@ static int eth_tx(struct device *dev, struct net_pkt *pkt) dma_buffer = (u8_t *)(dma_tx_desc->Buffer1Addr); - frag = pkt->frags; - while (frag) { - memcpy(dma_buffer, frag->data, frag->len); - dma_buffer += frag->len; - frag = frag->frags; + if (net_pkt_read_new(pkt, dma_buffer, total_len)) { + res = -EIO; + goto error; } if (HAL_ETH_TransmitFrame(heth, total_len) != HAL_OK) { @@ -146,13 +143,14 @@ static struct net_pkt *eth_rx(struct device *dev) total_len = heth->RxFrameInfos.length; dma_buffer = (u8_t *)heth->RxFrameInfos.buffer; - pkt = net_pkt_get_reserve_rx(K_NO_WAIT); + pkt = net_pkt_rx_alloc_with_buffer(dev_data->iface, total_len, + AF_UNSPEC, 0, K_NO_WAIT); if (!pkt) { LOG_ERR("Failed to obtain RX buffer"); goto release_desc; } - if (!net_pkt_append_all(pkt, total_len, dma_buffer, K_NO_WAIT)) { + if (net_pkt_write_new(pkt, dma_buffer, total_len)) { LOG_ERR("Failed to append RX buffer to context buffer"); net_pkt_unref(pkt); pkt = NULL; diff --git a/drivers/modem/wncm14a2a.c b/drivers/modem/wncm14a2a.c index eda04d446f3fd..7f67dd063f739 100644 --- a/drivers/modem/wncm14a2a.c +++ b/drivers/modem/wncm14a2a.c @@ -777,7 +777,8 @@ static void sockreadrecv_cb_work(struct k_work *work) pkt = sock->recv_pkt; sock->recv_pkt = NULL; if (sock->recv_cb) { - sock->recv_cb(sock->context, pkt, 0, sock->recv_user_data); + sock->recv_cb(sock->context, pkt, NULL, NULL, + 0, sock->recv_user_data); } else { net_pkt_unref(pkt); } diff --git a/drivers/wifi/eswifi/eswifi_offload.c b/drivers/wifi/eswifi/eswifi_offload.c index aa47867650c24..673318299025a 100644 --- a/drivers/wifi/eswifi/eswifi_offload.c +++ b/drivers/wifi/eswifi/eswifi_offload.c @@ -96,7 +96,8 @@ static void eswifi_off_read_work(struct k_work *work) LOG_WRN("Incomplete buffer copy"); } - socket->recv_cb(socket->context, pkt, 0, socket->user_data); + socket->recv_cb(socket->context, pkt, + NULL, NULL, 0, socket->user_data); k_sem_give(&socket->read_sem); k_yield(); diff --git a/drivers/wifi/winc1500/wifi_winc1500.c b/drivers/wifi/winc1500/wifi_winc1500.c index 55532d43f40ce..f2164acd0113f 100644 --- a/drivers/wifi/winc1500/wifi_winc1500.c +++ b/drivers/wifi/winc1500/wifi_winc1500.c @@ -776,6 +776,7 @@ static bool handle_socket_msg_recv(SOCKET sock, if (sd->recv_cb) { sd->recv_cb(sd->context, sd->rx_pkt, + NULL, NULL, 0, sd->recv_user_data); } diff --git a/include/net/net_context.h b/include/net/net_context.h index 6c809ee7df508..fc6a696cc58b7 100644 --- a/include/net/net_context.h +++ b/include/net/net_context.h @@ -76,6 +76,8 @@ struct net_context; * @param pkt Network buffer that is received. If the pkt is not NULL, * then the callback will own the buffer and it needs to to unref the pkt * as soon as it has finished working with it. On EOF, pkt will be NULL. + * @param ip_hdr a pointer to relevant IP (v4 or v6) header. + * @param proto_hdr a pointer to relevant protocol (udp or tcp) header. * @param status Value is set to 0 if some data or the connection is * at EOF, <0 if there was an error receiving data, in this case the * pkt parameter is set to NULL. @@ -83,6 +85,8 @@ struct net_context; */ typedef void (*net_context_recv_cb_t)(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data); @@ -596,6 +600,20 @@ struct net_pkt *net_context_create_ipv4(struct net_context *context, return NULL; } #endif /* CONFIG_NET_IPV4 */ +#if defined(CONFIG_NET_IPV4) +int net_context_create_ipv4_new(struct net_context *context, + struct net_pkt *pkt, + const struct in_addr *src, + const struct in_addr *dst); +#else +static inline int net_context_create_ipv4_new(struct net_context *context, + struct net_pkt *pkt, + const struct in_addr *src, + const struct in_addr *dst) +{ + return -1; +} +#endif /* CONFIG_NET_IPV4 */ /** * @brief Create IPv6 packet in provided net_pkt from context @@ -622,6 +640,20 @@ struct net_pkt *net_context_create_ipv6(struct net_context *context, return NULL; } #endif /* CONFIG_NET_IPV6 */ +#if defined(CONFIG_NET_IPV6) +int net_context_create_ipv6_new(struct net_context *context, + struct net_pkt *pkt, + const struct in6_addr *src, + const struct in6_addr *dst); +#else +static inline int net_context_create_ipv6_new(struct net_context *context, + struct net_pkt *pkt, + const struct in6_addr *src, + const struct in6_addr *dst) +{ + return -1; +} +#endif /* CONFIG_NET_IPV6 */ /** * @brief Assign a socket a local address. @@ -747,6 +779,41 @@ int net_context_send(struct net_pkt *pkt, void *token, void *user_data); +/** + * @brief Send a network buffer to a peer. + * + * @details This function can be used to send network data to a peer + * connection. This function will return immediately if the timeout + * is set to K_NO_WAIT. If the timeout is set to K_FOREVER, the function + * will wait until the network buffer is sent. Timeout value > 0 will + * wait as many ms. After the network buffer is sent, + * a caller-supplied callback is called. The callback is called even + * if timeout was set to K_FOREVER, the callback is called + * before this function will return in this case. The callback is not + * called if the timeout expires. For context of type SOCK_DGRAM, + * the destination address must have been set by the call to + * net_context_connect(). + * This is similar as BSD send() function. + * + * @param context The network context to use. + * @param buf The data buffer to send + * @param len Length of the buffer + * @param cb Caller-supplied callback function. + * @param timeout Timeout for the connection. Possible values + * are K_FOREVER, K_NO_WAIT, >0. + * @param token Caller specified value that is passed as is to callback. + * @param user_data Caller-supplied user data. + * + * @return 0 if ok, < 0 if error + */ +int net_context_send_new(struct net_context *context, + const void *buf, + size_t len, + net_context_send_cb_t cb, + s32_t timeout, + void *token, + void *user_data); + /** * @brief Send a network buffer to a peer specified by address. * @@ -782,6 +849,46 @@ int net_context_sendto(struct net_pkt *pkt, void *token, void *user_data); + +/** + * @brief Send a network buffer to a peer specified by address. + * + * @details This function can be used to send network data to a peer + * specified by address. This variant can only be used for datagram + * connections of type SOCK_DGRAM. This function will return immediately + * if the timeout is set to K_NO_WAIT. If the timeout is set to K_FOREVER, + * the function will wait until the network buffer is sent. Timeout + * value > 0 will wait as many ms. After the network buffer + * is sent, a caller-supplied callback is called. The callback is called + * even if timeout was set to K_FOREVER, the callback is called + * before this function will return. The callback is not called if the + * timeout expires. + * This is similar as BSD sendto() function. + * + * @param context The network context to use. + * @param buf The data buffer to send + * @param len Length of the buffer + * @param dst_addr Destination address. This will override the address + * already set in network buffer. + * @param addrlen Length of the address. + * @param cb Caller-supplied callback function. + * @param timeout Timeout for the connection. Possible values + * are K_FOREVER, K_NO_WAIT, >0. + * @param token Caller specified value that is passed as is to callback. + * @param user_data Caller-supplied user data. + * + * @return numbers of bytes sent on success, a negative errno otherwise + */ +int net_context_sendto_new(struct net_context *context, + const void *buf, + size_t len, + const struct sockaddr *dst_addr, + socklen_t addrlen, + net_context_send_cb_t cb, + s32_t timeout, + void *token, + void *user_data); + /** * @brief Receive network data from a peer specified by context. * diff --git a/include/net/net_ip.h b/include/net/net_ip.h index 9e8fb0e1c6451..8db08ba2ce8ba 100644 --- a/include/net/net_ip.h +++ b/include/net/net_ip.h @@ -312,6 +312,20 @@ struct net_tcp_hdr { u8_t optdata[0]; } __packed; +/** + * This 2 unions are here temporarly, as long as net_context.h will + * be still public and not part of the core only. + */ +union net_ip_header { + struct net_ipv4_hdr *ipv4; + struct net_ipv6_hdr *ipv6; +}; + +union net_proto_header { + struct net_udp_hdr *udp; + struct net_tcp_hdr *tcp; +}; + #define NET_UDPH_LEN 8 /* Size of UDP header */ #define NET_TCPH_LEN 20 /* Size of TCP header */ #define NET_ICMPH_LEN 4 /* Size of ICMP header */ diff --git a/include/net/net_pkt.h b/include/net/net_pkt.h index 308be12eba18d..316aafb260214 100644 --- a/include/net/net_pkt.h +++ b/include/net/net_pkt.h @@ -42,6 +42,15 @@ extern "C" { struct net_context; + +/* buffer cursor used in net_pkt */ +struct net_pkt_cursor { + /** Current net_buf pointer by the cursor */ + struct net_buf *buf; + /** Current position in the data buffer of the net_buf */ + u8_t *pos; +}; + /* Note that if you add new fields into net_pkt, remember to update * net_pkt_clone() function. */ @@ -55,8 +64,14 @@ struct net_pkt { /** Slab pointer from where it belongs to */ struct k_mem_slab *slab; - /** List of buffer fragments holding the packet */ - struct net_buf *frags; + /** buffer holding the packet */ + union { + struct net_buf *frags; + struct net_buf *buffer; + }; + + /** Internal buffer iterator used for reading/writing */ + struct net_pkt_cursor cursor; /** Network connection context */ struct net_context *context; @@ -81,7 +96,6 @@ struct net_pkt { #endif u8_t *appdata; /* application data starts here */ - u8_t *next_hdr; /* where is the next header */ /** Reference counter */ atomic_t atomic_ref; @@ -112,6 +126,8 @@ struct net_pkt { sys_snode_t sent_list; #endif + u8_t overwrite : 1; /* Is packet content being overwritten? */ + u8_t sent_or_eof: 1; /* For outgoing packet: is this sent or not * For incoming packet of a socket: last * packet before EOF @@ -132,11 +148,22 @@ struct net_pkt { u8_t forwarding : 1; /* Are we forwarding this pkt * Used only if defined(CONFIG_NET_ROUTE) */ - u8_t family : 4; /* IPv4 vs IPv6 */ - u8_t ipv4_auto_arp_msg : 1; /* Is this pkt IPv4 autoconf ARP message. - * Used only if - * defined(CONFIG_NET_IPV4_AUTO) - */ + u8_t family : 3; /* IPv4 vs IPv6 */ + + union { + u8_t ipv4_auto_arp_msg : 1; /* Is this pkt IPv4 autoconf ARP + * message. Used only if + * defined(CONFIG_NET_IPV4_AUTO). + * Note: family needs to be + * AF_INET. + */ + u8_t lldp_pkt : 1; /* Is this pkt an LLDP message. + * Used only if + * defined(CONFIG_NET_LLDP). + * Note: family needs to be + * AF_UNSPEC. + */ + }; union { /* IPv6 hop limit or IPv4 ttl for this network packet. @@ -179,6 +206,7 @@ struct net_pkt { #endif /* CONFIG_NET_IPV6_FRAGMENT */ u8_t ipv6_ext_opt_len; /* IPv6 ND option length */ + u8_t ipv6_next_hdr; /* What is the very first next header */ #endif /* CONFIG_NET_IPV6 */ #if defined(CONFIG_IEEE802154) @@ -296,16 +324,6 @@ static inline void net_pkt_set_transport_proto(struct net_pkt *pkt, u8_t proto) pkt->transport_proto = proto; } -static inline u8_t *net_pkt_next_hdr(struct net_pkt *pkt) -{ - return pkt->next_hdr; -} - -static inline void net_pkt_set_next_hdr(struct net_pkt *pkt, u8_t *hdr) -{ - pkt->next_hdr = hdr; -} - static inline u8_t net_pkt_sent(struct net_pkt *pkt) { return pkt->sent_or_eof; @@ -366,6 +384,20 @@ static inline void net_pkt_set_ipv4_ttl(struct net_pkt *pkt, { pkt->ipv4_ttl = ttl; } +#else +static inline u8_t net_pkt_ipv4_ttl(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return 0; +} + +static inline void net_pkt_set_ipv4_ttl(struct net_pkt *pkt, + u8_t ttl) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(ttl); +} #endif #if defined(CONFIG_NET_IPV6) @@ -380,6 +412,16 @@ static inline void net_pkt_set_ipv6_ext_opt_len(struct net_pkt *pkt, pkt->ipv6_ext_opt_len = len; } +static inline u8_t net_pkt_ipv6_next_hdr(struct net_pkt *pkt) +{ + return pkt->ipv6_next_hdr; +} + +static inline void net_pkt_set_ipv6_next_hdr(struct net_pkt *pkt, u8_t next_hdr) +{ + pkt->ipv6_next_hdr = next_hdr; +} + static inline u16_t net_pkt_ipv6_ext_len(struct net_pkt *pkt) { return pkt->ipv6_ext_len; @@ -411,6 +453,75 @@ static inline void net_pkt_set_ipv6_hop_limit(struct net_pkt *pkt, { pkt->ipv6_hop_limit = hop_limit; } +#else /* CONFIG_NET_IPV6 */ +static inline u8_t net_pkt_ipv6_ext_opt_len(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return 0; +} + +static inline void net_pkt_set_ipv6_ext_opt_len(struct net_pkt *pkt, + u8_t len) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(len); +} + +static inline u8_t net_pkt_ipv6_next_hdr(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return 0; +} + +static inline void net_pkt_set_ipv6_next_hdr(struct net_pkt *pkt, u8_t next_hdr) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(next_hdr); +} + +static inline u16_t net_pkt_ipv6_ext_len(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return 0; +} + +static inline void net_pkt_set_ipv6_ext_len(struct net_pkt *pkt, u16_t len) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(len); +} + +static inline u16_t net_pkt_ipv6_hdr_prev(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return 0; +} + +static inline void net_pkt_set_ipv6_hdr_prev(struct net_pkt *pkt, + u16_t offset) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(offset); +} + +static inline u8_t net_pkt_ipv6_hop_limit(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return 0; +} + +static inline void net_pkt_set_ipv6_hop_limit(struct net_pkt *pkt, + u8_t hop_limit) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(hop_limit); +} +#endif /* CONFIG_NET_IPV6 */ #if defined(CONFIG_NET_IPV6_FRAGMENT) static inline u16_t net_pkt_ipv6_fragment_start(struct net_pkt *pkt) @@ -445,11 +556,49 @@ static inline void net_pkt_set_ipv6_fragment_id(struct net_pkt *pkt, { pkt->ipv6_fragment_id = id; } +#else /* CONFIG_NET_IPV6_FRAGMENT */ +static inline u16_t net_pkt_ipv6_fragment_start(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return 0; +} + +static inline void net_pkt_set_ipv6_fragment_start(struct net_pkt *pkt, + u16_t start) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(start); +} + +static inline u16_t net_pkt_ipv6_fragment_offset(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return 0; +} + +static inline void net_pkt_set_ipv6_fragment_offset(struct net_pkt *pkt, + u16_t offset) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(offset); +} + +static inline u32_t net_pkt_ipv6_fragment_id(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return 0; +} + +static inline void net_pkt_set_ipv6_fragment_id(struct net_pkt *pkt, + u32_t id) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(id); +} #endif /* CONFIG_NET_IPV6_FRAGMENT */ -#else /* CONFIG_NET_IPV6 */ -#define net_pkt_ipv6_ext_len(...) 0 -#define net_pkt_set_ipv6_ext_len(...) -#endif /* CONFIG_NET_IPV6 */ #if NET_TC_COUNT > 1 static inline u8_t net_pkt_priority(struct net_pkt *pkt) @@ -676,14 +825,46 @@ static inline void net_pkt_set_ipv4_auto(struct net_pkt *pkt, { pkt->ipv4_auto_arp_msg = is_auto_arp_msg; } -#else +#else /* CONFIG_NET_IPV4_AUTO */ static inline bool net_pkt_ipv4_auto(struct net_pkt *pkt) { + ARG_UNUSED(pkt); + return false; } -#define net_pkt_set_ipv4_auto(...) -#endif +static inline void net_pkt_set_ipv4_auto(struct net_pkt *pkt, + bool is_auto_arp_msg) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(is_auto_arp_msg); +} +#endif /* CONFIG_NET_IPV4_AUTO */ + +#if defined(CONFIG_NET_LLDP) +static inline bool net_pkt_is_lldp(struct net_pkt *pkt) +{ + return pkt->lldp_pkt; +} + +static inline void net_pkt_set_lldp(struct net_pkt *pkt, bool is_lldp) +{ + pkt->lldp_pkt = is_lldp; +} +#else +static inline bool net_pkt_is_lldp(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return false; +} + +static inline void net_pkt_set_lldp(struct net_pkt *pkt, bool is_lldp) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(is_lldp); +} +#endif /* CONFIG_NET_LLDP */ #define NET_IPV6_HDR(pkt) ((struct net_ipv6_hdr *)net_pkt_ip_data(pkt)) #define NET_IPV4_HDR(pkt) ((struct net_ipv4_hdr *)net_pkt_ip_data(pkt)) @@ -695,6 +876,16 @@ static inline void net_pkt_set_src_ipv6_addr(struct net_pkt *pkt) &NET_IPV6_HDR(pkt)->src); } +static inline void net_pkt_set_overwrite(struct net_pkt *pkt, bool overwrite) +{ + pkt->overwrite = overwrite; +} + +static inline bool net_pkt_is_being_overwritten(struct net_pkt *pkt) +{ + return pkt->overwrite; +} + /* @endcond */ /** @@ -1881,6 +2072,499 @@ const char *net_pkt_pool2str(struct net_buf_pool *pool); #define net_pkt_print(...) #endif /* CONFIG_NET_PKT_LOG_LEVEL >= LOG_LEVEL_DBG */ +/* New allocator, and API are defined below. + * This will be simpler when time will come to get rid of former API above. + */ +#if defined(CONFIG_NET_DEBUG_NET_PKT_ALLOC) || \ + (CONFIG_NET_PKT_LOG_LEVEL >= LOG_LEVEL_DBG) + +struct net_pkt *net_pkt_alloc_debug(s32_t timeout, + const char *caller, int line); +#define net_pkt_alloc(_timeout) \ + net_pkt_alloc_debug(_timeout, __func__, __LINE__) + +struct net_pkt *net_pkt_rx_alloc_debug(s32_t timeout, + const char *caller, int line); +#define net_pkt_rx_alloc(_timeout) \ + net_pkt_rx_alloc_debug(_timeout, __func__, __LINE__) + +struct net_pkt *net_pkt_alloc_on_iface_debug(struct net_if *iface, + s32_t timeout, + const char *caller, + int line); +#define net_pkt_alloc_on_iface(_iface, _timeout) \ + net_pkt_alloc_on_iface_debug(_iface, _timeout, __func__, __LINE__) + +struct net_pkt *net_pkt_rx_alloc_on_iface_debug(struct net_if *iface, + s32_t timeout, + const char *caller, + int line); +#define net_pkt_rx_alloc_on_iface(_iface, _timeout) \ + net_pkt_rx_alloc_on_iface_debug(_iface, _timeout, \ + __func__, __LINE__) + +int net_pkt_alloc_buffer_debug(struct net_pkt *pkt, + size_t size, + enum net_ip_protocol proto, + s32_t timeout, + const char *caller, int line); +#define net_pkt_alloc_buffer(_pkt, _size, _proto, _timeout) \ + net_pkt_alloc_buffer_debug(_pkt, _size, _proto, _timeout, \ + __func__, __LINE__) + +struct net_pkt *net_pkt_alloc_with_buffer_debug(struct net_if *iface, + size_t size, + sa_family_t family, + enum net_ip_protocol proto, + s32_t timeout, + const char *caller, + int line); +#define net_pkt_alloc_with_buffer(_iface, _size, _family, \ + _proto, _timeout) \ + net_pkt_alloc_with_buffer_debug(_iface, _size, _family, \ + _proto, _timeout, \ + __func__, __LINE__) + +struct net_pkt *net_pkt_rx_alloc_with_buffer_debug(struct net_if *iface, + size_t size, + sa_family_t family, + enum net_ip_protocol proto, + s32_t timeout, + const char *caller, + int line); +#define net_pkt_rx_alloc_with_buffer(_iface, _size, _family, \ + _proto, _timeout) \ + net_pkt_rx_alloc_with_buffer_debug(_iface, _size, _family, \ + _proto, _timeout, \ + __func__, __LINE__) +#else + +/** + * @brief Allocate an initialized net_pkt + * + * Note: for the time being, 2 pools are used. One for TX and one for RX. + * This allocater has to be used for TX. + * + * @param timeout Maximum time in milliseconds to wait for an allocation. + * + * @return a pointer to a newly allocated net_pkt on success, NULL otherwise. + */ +struct net_pkt *net_pkt_alloc(s32_t timeout); + +/** + * @brief Allocate an initialized net_pkt for RX + * + * Note: for the time being, 2 pools are used. One for TX and one for RX. + * This allocater has to be used for RX. + * + * @param timeout Maximum time in milliseconds to wait for an allocation. + * + * @return a pointer to a newly allocated net_pkt on success, NULL otherwise. + */ +struct net_pkt *net_pkt_rx_alloc(s32_t timeout); + +/** + * @brief Allocate a network packet for a specific network interface. + * + * @param iface The network interface the packet is supposed to go through. + * @param timeout Maximum time in milliseconds to wait for an allocation. + * + * @return a pointer to a newly allocated net_pkt on success, NULL otherwise. + */ +struct net_pkt *net_pkt_alloc_on_iface(struct net_if *iface, s32_t timeout); + +/* Same as above but specifically for RX packet */ +struct net_pkt *net_pkt_rx_alloc_on_iface(struct net_if *iface, s32_t timeout); + +/** + * @brief Allocate buffer for a net_pkt + * + * Note: such allocator will take into account space necessary for headers, + * MTU, and existing buffer (if any). Beware that, due to all these + * criterias, the allocated size might be smaller/bigger than requested + * one. + * + * @param pkt The network packet requiring buffer to be allocated. + * @param size The size of buffer being requested. + * @param proto The IP protocol type (can be 0 for none). + * @param timeout Maximum time in milliseconds to wait for an allocation. + * + * @return 0 on success, negative errno code otherwise. + */ +int net_pkt_alloc_buffer(struct net_pkt *pkt, + size_t size, + enum net_ip_protocol proto, + s32_t timeout); + +/** + * @brief Allocate a network packet and buffer at once + * + * @param iface The network interface the packet is supposed to go through. + * @param size The size of buffer being requested. + * @param family The family to which the packet belongs to. + * @param proto The IP protocol type (can be 0 for none). + * @param timeout Maximum time in milliseconds to wait for an allocation. + * + * @return a pointer to a newly allocated net_pkt on success, NULL otherwise. + */ +struct net_pkt *net_pkt_alloc_with_buffer(struct net_if *iface, + size_t size, + sa_family_t family, + enum net_ip_protocol proto, + s32_t timeout); + +/* Same as above but specifically for RX packet */ +struct net_pkt *net_pkt_rx_alloc_with_buffer(struct net_if *iface, + size_t size, + sa_family_t family, + enum net_ip_protocol proto, + s32_t timeout); + +#endif /* CONFIG_NET_DEBUG_NET_PKT_ALLOC | + * CONFIG_NET_PKT_LOG_LEVEL >= LOG_LEVEL_DBG + */ + +/** + * @brief Append a buffer in packet + * + * @param pkt Network packet where to append the buffer + * @param buffer Buffer to append + */ +void net_pkt_append_buffer(struct net_pkt *pkt, struct net_buf *buffer); + +/** + * @brief Get available buffer space from a pkt + * + * @param pkt The net_pkt which buffer availabality should be evaluated + * + * @return the amount of buffer available + */ +size_t net_pkt_available_buffer(struct net_pkt *pkt); + +/** + * @brief Initialize net_pkt cursor + * + * Note: This will inialize the net_pkt cursor from its buffer. + * + * @param pkt The net_pkt which cursor is going to be initialized + */ +void net_pkt_cursor_init(struct net_pkt *pkt); + +/** + * @brief Backup net_pkt cursor + * + * @param pkt The net_pkt which cursor is going to be backuped + * @param backup The cursor where to backup net_pkt cursor + */ +static inline void net_pkt_cursor_backup(struct net_pkt *pkt, + struct net_pkt_cursor *backup) +{ + backup->buf = pkt->cursor.buf; + backup->pos = pkt->cursor.pos; +} + +/** + * @brief Restore net_pkt cursor from a backup + * + * @param pkt The net_pkt which cursor is going to be restored + * @param backup The cursor from where to restore net_pkt cursor + */ +static inline void net_pkt_cursor_restore(struct net_pkt *pkt, + struct net_pkt_cursor *backup) +{ + pkt->cursor.buf = backup->buf; + pkt->cursor.pos = backup->pos; +} + +/** + * @brief Returns current position of the cursor + * + * @param pkt The net_pkt which cursor's position is going to be returned + * + * @return cursor's position + */ +static inline void *net_pkt_cursor_get_pos(struct net_pkt *pkt) +{ + return pkt->cursor.pos; +} + +/** + * @brief Skip some data from a net_pkt + * + * Note: net_pkt's cursor should be properly initialized + * Cursor will be updated according to parameter. + * Depending on the value of pkt->overwrite bit, this function + * will affect the buffer length or not: if it's 0, skip will + * actually apply the move in the buffer as it had written in it. + * + * @param pkt The net_pkt which cursor will be updated to skip given + * amount of data from the buffer. + * @param length Amount of data to skip in the buffer + * + * @return 0 in success, negative errno code otherwise. + */ +int net_pkt_skip(struct net_pkt *pkt, size_t length); + +/** + * @brief Memset some data in a net_pkt + * + * Note: net_pkt's cursor should be properly initialized and, + * eventally, properly positioned using net_pkt_skip. + * Cursor will be updated according to parameter. + * + * @param pkt The net_pkt which cursor will be updated to skip given + * amount of data from the buffer. + * @param byte The byte to write in memory + * @param length Amount of data to memset with given byte + * + * @return 0 in success, negative errno code otherwise. + */ +int net_pkt_memset(struct net_pkt *pkt, int byte, size_t length); + +/** + * @brief Copy data from a packet into another one. + * + * Note: Both net_pkt cursors should be properly initialized and, + * eventally, properly positioned using net_pkt_skip. + * Cursors will be updated according to parameters. + * + * @param pkt_dst Destination network packet. + * @param pkt_src Source network packet. + * @param length Length of data to be copied. + * + * @return 0 on success, negative errno code otherwise. + */ +int net_pkt_copy_new(struct net_pkt *pkt_dst, + struct net_pkt *pkt_src, + size_t length); + +/** + * @brief Clone pkt and its fragment chain. + * + * @param pkt Original pkt to be cloned + * @param timeout Timeout to wait for free buffer + * + * @return NULL if error, cloned packet otherwise. + */ +struct net_pkt *net_pkt_clone_new(struct net_pkt *pkt, s32_t timeout); + +/** + * @brief Read some data from a net_pkt + * + * Note: net_pkt's cursor should be properly initialized and, + * eventally, properly positioned using net_pkt_skip. + * Cursor will be updated according to parameters. + * @param pkt The network packet from where to read some data + * @param data The destination buffer where to copy the data + * @param length The amount of data to copy + * + * @return 0 on success, negative errno code otherwise. + */ +int net_pkt_read_new(struct net_pkt *pkt, void *data, size_t length); + +/* Read u8_t data data a net_pkt */ +static inline int net_pkt_read_u8_new(struct net_pkt *pkt, u8_t *data) +{ + return net_pkt_read_new(pkt, data, 1); +} + +/** + * @brief Read u16_t big endian data from a net_pkt + * + * Note: net_pkt's cursor should be properly initialized and, + * eventally, properly positioned using net_pkt_skip. + * Cursor will be updated according to parameters. + * + * @param pkt The network packet from where to read + * @param data The destination u16_t where to copy the data + * + * @return 0 on success, negative errno code otherwise. + */ +int net_pkt_read_be16_new(struct net_pkt *pkt, u16_t *data); + +/** + * @brief Read u32_t big endian data from a net_pkt + * + * Note: net_pkt's cursor should be properly initialized and, + * eventally, properly positioned using net_pkt_skip. + * Cursor will be updated according to parameters. + * + * @param pkt The network packet from where to read + * @param data The destination u32_t where to copy the data + * + * @return 0 on success, negative errno code otherwise. + */ +int net_pkt_read_be32_new(struct net_pkt *pkt, u32_t *data); + +/** + * @brief Write data into a net_pkt + * + * Note: net_pkt's cursor should be properly initialized and, + * eventally, properly positioned using net_pkt_skip_read/write. + * Cursor will be updated according to parameters. + * + * @param pkt The network packet where to write + * @param data Data to be written + * @param length Length of the data to be written + * + * @return 0 on success, negative errno code otherwise. + */ +int net_pkt_write_new(struct net_pkt *pkt, const void *data, size_t length); + +/* Write u8_t data into a net_pkt. */ +static inline int net_pkt_write_u8_new(struct net_pkt *pkt, u8_t data) +{ + return net_pkt_write_new(pkt, &data, sizeof(u8_t)); +} + +/* Write u16_t big endian data into a net_pkt. */ +static inline int net_pkt_write_be16_new(struct net_pkt *pkt, u16_t data) +{ + u16_t data_be16 = htons(data); + + return net_pkt_write_new(pkt, &data_be16, sizeof(u16_t)); +} + +/* Write u32_t big endian data into a net_pkt. */ +static inline int net_pkt_write_be32_new(struct net_pkt *pkt, u32_t data) +{ + u32_t data_be32 = htonl(data); + + return net_pkt_write_new(pkt, &data_be32, sizeof(u32_t)); +} + +/* Write u32_t little endian data into a net_pkt. */ +static inline int net_pkt_write_le32_new(struct net_pkt *pkt, u32_t data) +{ + u32_t data_le32 = sys_cpu_to_le32(data); + + return net_pkt_write_new(pkt, &data_le32, sizeof(u32_t)); +} + +/** + * @brief Update the overall length of a packet + * + * Note: Unlike net_pkt_pull_new() below, this does not take packet cursor + * into account. It's mainly a helper dedicated for ipv4 and ipv6 + * input functions. It shrinks the overall length by given parameter. + * + * @param pkt Network packet + * @param length The new length of the packet + * + * @return 0 on success, negative errno code otherwise. + */ +int net_pkt_update_length(struct net_pkt *pkt, size_t length); + +/** + * @brief Remove data from the packet at current location + * + * Note: net_pkt's cursor should be properly initialized and, + * eventally, properly positioned using net_pkt_skip/read/write. + * + * @param pkt Network packet + * @param length Number of bytes to be removed + * + * @return 0 on success, negative errno code otherwise. + */ +int net_pkt_pull_new(struct net_pkt *pkt, size_t length); + +/** + * @brief Get the actual offset in the packet from its cursor + * + * @param pkt Network packet. + * + * @return a valid offset on success, 0 otherwise as there is nothing that + * can be done to evaluate the offset. + */ +u16_t net_pkt_get_current_offset(struct net_pkt *pkt); + +/** + * @brief Check if a data size could fit contiguously + * + * Note: net_pkt's cursor should be properly initialized and, + * eventally, properly positioned using net_pkt_skip_read/write. + * + * @param pkt Network packet. + * @param size The size to check for contiguity + * + * @return true if that is the case, false otherwise. + */ +bool net_pkt_is_contiguous(struct net_pkt *pkt, size_t size); + +struct net_pkt_data_access { +#if !defined(CONFIG_NET_HEADERS_ALWAYS_CONTIGUOUS) + void *data; +#endif + const size_t size; +}; + +#if defined(CONFIG_NET_HEADERS_ALWAYS_CONTIGUOUS) +#define NET_PKT_DATA_ACCESS_DEFINE(_name, _type) \ + struct net_pkt_data_access _name = { \ + .size = sizeof(_type), \ + } + +#define NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(_name, _type) \ + NET_PKT_DATA_ACCESS_DEFINE(_name, _type) + +#else +#define NET_PKT_DATA_ACCESS_DEFINE(_name, _type) \ + _type _hdr_##_name; \ + struct net_pkt_data_access _name = { \ + .data = &_hdr_##_name, \ + .size = sizeof(_type), \ + } + +#define NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(_name, _type) \ + struct net_pkt_data_access _name = { \ + .data = NULL, \ + .size = sizeof(_type), \ + } + +#endif /* CONFIG_NET_HEADERS_ALWAYS_CONTIGUOUS */ + +/** + * @brief Get data from a network packet in a contiguous way + * + * Note: net_pkt's cursor should be properly initialized and, + * eventally, properly positioned using net_pkt_skip_read/write. + * Cursor will be updated according to parameters. + * + * @param pkt The network packet from where to get the data. + * @param access A pointer to a valid net_pkt_data_access describing the + * data to get in a contiguous way. + * + * @return a pointer to the requested contiguous data, NULL otherwise. + */ +void *net_pkt_get_data_new(struct net_pkt *pkt, + struct net_pkt_data_access *access); + +/** + * @brief Set contiguous data into a network packet + * + * Note: net_pkt's cursor should be properly initialized and, + * eventally, properly positioned using net_pkt_skip_read/write. + * Cursor will be updated according to parameters. + * + * @param pkt The network packet to where the data should be set. + * @param access A pointer to a valid net_pkt_data_access describing the + * data to set. + * + * @return 0 on success, a negative errno otherwise. + */ +int net_pkt_set_data(struct net_pkt *pkt, + struct net_pkt_data_access *access); + +/** + * Acknowledge previously contiguous data taken from a network packet + * Packet needs to be set to overwrite mode. + */ +static inline int net_pkt_acknowledge_data(struct net_pkt *pkt, + struct net_pkt_data_access *access) +{ + return net_pkt_skip(pkt, access->size); +} + /** * @} */ diff --git a/samples/bluetooth/ipsp/src/main.c b/samples/bluetooth/ipsp/src/main.c index 63cd8d824c811..0ea4e849892a4 100644 --- a/samples/bluetooth/ipsp/src/main.c +++ b/samples/bluetooth/ipsp/src/main.c @@ -225,6 +225,8 @@ static inline void set_dst_addr(sa_family_t family, static void udp_received(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { @@ -268,6 +270,8 @@ static void setup_udp_recv(struct net_context *udp_recv6) static void tcp_received(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/samples/net/coap_client/src/coap-client.c b/samples/net/coap_client/src/coap-client.c index d84d97118740d..bb5c2658723f5 100644 --- a/samples/net/coap_client/src/coap-client.c +++ b/samples/net/coap_client/src/coap-client.c @@ -62,6 +62,8 @@ static int dump_payload(const struct coap_packet *response) static void udp_receive(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/samples/net/coap_server/src/coap-server.c b/samples/net/coap_server/src/coap-server.c index 3bbe10910c2bc..2a43002e8f0bb 100644 --- a/samples/net/coap_server/src/coap-server.c +++ b/samples/net/coap_server/src/coap-server.c @@ -1229,6 +1229,8 @@ static struct coap_resource *find_resouce_by_observer( static void udp_receive(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/samples/net/leds_demo/src/leds-demo.c b/samples/net/leds_demo/src/leds-demo.c index 7f73992dad78c..2ce6becf55556 100644 --- a/samples/net/leds_demo/src/leds-demo.c +++ b/samples/net/leds_demo/src/leds-demo.c @@ -458,6 +458,8 @@ static struct coap_resource resources[] = { static void udp_receive(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/samples/net/nats/src/nats.c b/samples/net/nats/src/nats.c index 26bc9c503cff5..0b321165bf0ce 100644 --- a/samples/net/nats/src/nats.c +++ b/samples/net/nats/src/nats.c @@ -536,7 +536,11 @@ int nats_publish(const struct nats *nats, }); } -static void receive_cb(struct net_context *ctx, struct net_pkt *pkt, int status, +static void receive_cb(struct net_context *ctx, + struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, + int status, void *user_data) { struct nats *nats = user_data; diff --git a/samples/net/zperf/src/zperf_tcp_receiver.c b/samples/net/zperf/src/zperf_tcp_receiver.c index c4d3355116482..90625619e1b36 100644 --- a/samples/net/zperf/src/zperf_tcp_receiver.c +++ b/samples/net/zperf/src/zperf_tcp_receiver.c @@ -32,6 +32,8 @@ static struct sockaddr_in *in4_addr_my; static void tcp_received(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/samples/net/zperf/src/zperf_udp_receiver.c b/samples/net/zperf/src/zperf_udp_receiver.c index 6111bb9b303a7..524f6471f9ca9 100644 --- a/samples/net/zperf/src/zperf_udp_receiver.c +++ b/samples/net/zperf/src/zperf_udp_receiver.c @@ -126,6 +126,8 @@ static int zperf_receiver_send_stat(const struct shell *shell, static void udp_received(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/samples/net/zperf/src/zperf_udp_uploader.c b/samples/net/zperf/src/zperf_udp_uploader.c index 7fb0aeb7d7aea..338cf743d3b92 100644 --- a/samples/net/zperf/src/zperf_udp_uploader.c +++ b/samples/net/zperf/src/zperf_udp_uploader.c @@ -78,6 +78,8 @@ static inline void zperf_upload_decode_stat(const struct shell *shell, static void stat_received(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/subsys/net/ip/Kconfig b/subsys/net/ip/Kconfig index 989b5dd9cc155..febaf26393c71 100644 --- a/subsys/net/ip/Kconfig +++ b/subsys/net/ip/Kconfig @@ -435,17 +435,55 @@ config NET_BUF_TX_COUNT Each data buffer will occupy CONFIG_NET_BUF_DATA_SIZE + smallish header (sizeof(struct net_buf)) amount of data. +choice + prompt "Network packet data allocator type" + default NET_BUF_FIXED_DATA_SIZE + help + Select the memory allocator for the network buffers that hold the + packet data. + +config NET_BUF_FIXED_DATA_SIZE + bool "Fixed data size buffer" + help + Each buffer comes with a built time configured size. If runtime + requested is bigger than that, it will allocate as many net_buf + as necessary to reach that request. + +config NET_BUF_VARIABLE_DATA_SIZE + bool "Variable data size buffer [EXPERIMENTAL]" + help + The buffer is dynamically allocated from runtime requested size. + +endchoice + config NET_BUF_DATA_SIZE int "Size of each network data fragment" - default 125 if NET_L2_IEEE802154 default 128 + depends on NET_BUF_FIXED_DATA_SIZE + help + This value tells what is the fixed size of each network buffer. + +config NET_BUF_DATA_POOL_SIZE + int "Size of the memory pool where buffers are allocated from" + default 4096 if NET_L2_ETHERNET + default 2048 + depends on NET_BUF_VARIABLE_DATA_SIZE help - This value tells what is the size of the data fragment that is - received from the network. - Example: For IEEE 802.15.4, the network packet is 127 bytes long, - which leaves in worst case 81 bytes for user data (MTU). - In order to be able to receive at least full IPv6 packet which - has a size of 1280 bytes, the one should allocate 16 fragments here. + This value tell what is the size of the memory pool where each + network buffer is allocated from. + +config NET_HEADERS_ALWAYS_CONTIGUOUS + bool + default n + help + This a hidden option, which one should use with a lot of care. + NO bug reports will be accepted if that option is enabled! + You are warned. + If you are 100% sure the headers memore space is always in a + contiguous space, this will save stack usage and ROM in net core. + This is a possible case when using IPv4 only, with + NET_BUF_FIXED_DATA_SIZE enabled and NET_BUF_DATA_SIZE of 128 for + instance. choice prompt "Default Network Interface" diff --git a/subsys/net/ip/Kconfig.debug b/subsys/net/ip/Kconfig.debug index 292b94ab8b910..a727c7ca5f974 100644 --- a/subsys/net/ip/Kconfig.debug +++ b/subsys/net/ip/Kconfig.debug @@ -36,6 +36,17 @@ config NET_DEBUG_NET_PKT_EXTERNALS This value is used when allocating space for tracking the memory allocations. +config NET_DEBUG_NET_PKT_NON_FRAGILE_ACCESS + bool "Reduce r/w fragility by resetting the packet cursor when freed" + default n + select NET_DEBUG_NET_PKT_ALLOC + help + This MUST not be used unless you have an hard to catch bug. This will + reset the pkt cursor when it's freed, so any subsequent r/w operations + will not segfault, but just bail out and hopefuly it will enable you + to know who/where the packet was freed already. Do not set this, by + any means, unless you are actively debugging. + if !NET_RAW_MODE module = NET_CORE diff --git a/subsys/net/ip/connection.c b/subsys/net/ip/connection.c index 11b63bee830c4..8621d0dfddd0d 100644 --- a/subsys/net/ip/connection.c +++ b/subsys/net/ip/connection.c @@ -204,39 +204,24 @@ static s32_t check_hash(enum net_ip_protocol proto, return -ENOENT; } -static inline s32_t get_conn(enum net_ip_protocol proto, - sa_family_t family, - struct net_pkt *pkt, +static inline s32_t get_conn(struct net_pkt *pkt, + union net_ip_header *ip_hdr, + enum net_ip_protocol proto, + u16_t src_port, + u16_t dst_port, u32_t *cache_value) { - struct net_udp_hdr hdr, *udp_hdr; - - udp_hdr = net_udp_get_hdr(pkt, &hdr); - if (!udp_hdr) { - return NET_DROP; - } - -#if defined(CONFIG_NET_IPV4) - if (family == AF_INET) { - return check_hash(proto, family, - &NET_IPV4_HDR(pkt)->src, - &NET_IPV4_HDR(pkt)->dst, - udp_hdr->src_port, - udp_hdr->dst_port, - cache_value); + if (IS_ENABLED(CONFIG_NET_IPV4) && net_pkt_family(pkt) == AF_INET) { + return check_hash(proto, net_pkt_family(pkt), + &ip_hdr->ipv4->src, &ip_hdr->ipv4->dst, + src_port, dst_port, cache_value); } -#endif -#if defined(CONFIG_NET_IPV6) - if (family == AF_INET6) { - return check_hash(proto, family, - &NET_IPV6_HDR(pkt)->src, - &NET_IPV6_HDR(pkt)->dst, - udp_hdr->src_port, - udp_hdr->dst_port, - cache_value); + if (IS_ENABLED(CONFIG_NET_IPV6) && net_pkt_family(pkt) == AF_INET6) { + return check_hash(proto, net_pkt_family(pkt), + &ip_hdr->ipv6->src, &ip_hdr->ipv6->dst, + src_port, dst_port, cache_value); } -#endif return -1; } @@ -282,34 +267,30 @@ static void cache_clear(void) } } -static inline enum net_verdict cache_check(enum net_ip_protocol proto, - struct net_pkt *pkt, +static inline enum net_verdict cache_check(struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, + enum net_ip_protocol proto, + u16_t src_port, + u16_t dst_port, u32_t *cache_value, s32_t *pos) { - *pos = get_conn(proto, net_pkt_family(pkt), pkt, cache_value); + *pos = get_conn(pkt, ip_hdr, proto, src_port, dst_port, cache_value); if (*pos >= 0) { if (conn_cache[*pos].idx >= 0) { /* Connection is in the cache */ - struct net_conn *conn; - struct net_udp_hdr hdr, *udp_hdr; - - udp_hdr = net_udp_get_hdr(pkt, &hdr); - if (!udp_hdr) { - return NET_CONTINUE; - } - - conn = &conns[conn_cache[*pos].idx]; + struct net_conn *conn = &conns[conn_cache[*pos].idx]; NET_DBG("Cache %s listener for pkt %p src port %u " "dst port %u family %d cache[%d] 0x%x", net_proto2str(proto), pkt, - ntohs(udp_hdr->src_port), - ntohs(udp_hdr->dst_port), + src_port, dst_port, net_pkt_family(pkt), *pos, conn_cache[*pos].value); - return conn->cb(conn, pkt, conn->user_data); + return conn->cb(conn, pkt, + ip_hdr, proto_hdr, conn->user_data); } } else if (*cache_value > 0) { if (cache_check_neg(*cache_value)) { @@ -704,6 +685,7 @@ int net_conn_register(enum net_ip_protocol proto, } static bool check_addr(struct net_pkt *pkt, + union net_ip_header *ip_hdr, struct sockaddr *addr, bool is_remote) { @@ -716,9 +698,9 @@ static bool check_addr(struct net_pkt *pkt, struct in6_addr *addr6; if (is_remote) { - addr6 = &NET_IPV6_HDR(pkt)->src; + addr6 = &ip_hdr->ipv6->src; } else { - addr6 = &NET_IPV6_HDR(pkt)->dst; + addr6 = &ip_hdr->ipv6->dst; } if (!net_ipv6_is_addr_unspecified( @@ -738,9 +720,9 @@ static bool check_addr(struct net_pkt *pkt, struct in_addr *addr4; if (is_remote) { - addr4 = &NET_IPV4_HDR(pkt)->src; + addr4 = &ip_hdr->ipv4->src; } else { - addr4 = &NET_IPV4_HDR(pkt)->dst; + addr4 = &ip_hdr->ipv4->dst; } if (net_sin(addr)->sin_addr.s_addr) { @@ -773,105 +755,73 @@ static inline void send_icmp_error(struct net_pkt *pkt) } static bool is_invalid_packet(struct net_pkt *pkt, - u16_t src_port, - u16_t dst_port) + union net_ip_header *ip_hdr, + u16_t src_port, + u16_t dst_port) { - bool my_src_addr = false; + if (src_port != dst_port) { + return false; + } - switch (NET_IPV6_HDR(pkt)->vtc & 0xf0) { -#if defined(CONFIG_NET_IPV6) - case 0x60: - if (net_ipv6_is_my_addr(&NET_IPV6_HDR(pkt)->src)) { - my_src_addr = true; + if (IS_ENABLED(CONFIG_NET_IPV4) && net_pkt_family(pkt) == AF_INET) { + if (net_ipv4_addr_cmp(&ip_hdr->ipv4->src, &ip_hdr->ipv4->dst) || + net_ipv4_is_my_addr(&ip_hdr->ipv4->src)) { + return true; } - break; -#endif -#if defined(CONFIG_NET_IPV4) - case 0x40: - if (net_ipv4_is_my_addr(&NET_IPV4_HDR(pkt)->src)) { - my_src_addr = true; + } + + if (IS_ENABLED(CONFIG_NET_IPV6) && net_pkt_family(pkt) == AF_INET6) { + if (net_ipv6_addr_cmp(&ip_hdr->ipv6->src, &ip_hdr->ipv6->dst) || + net_ipv6_is_my_addr(&ip_hdr->ipv6->src)) { + return true; } - break; -#endif } - return my_src_addr && (src_port == dst_port); + return true; } -enum net_verdict net_conn_input(enum net_ip_protocol proto, struct net_pkt *pkt) +enum net_verdict net_conn_input(struct net_pkt *pkt, + union net_ip_header *ip_hdr, + u8_t proto, + union net_proto_header *proto_hdr) { + struct net_if *pkt_iface = net_pkt_iface(pkt); int i, best_match = -1; s16_t best_rank = -1; - u16_t src_port, dst_port; - struct net_if *pkt_iface = net_pkt_iface(pkt); - + u16_t src_port; + u16_t dst_port; #if defined(CONFIG_NET_CONN_CACHE) enum net_verdict verdict; u32_t cache_value = 0U; s32_t pos; - - verdict = cache_check(proto, pkt, &cache_value, &pos); - if (verdict != NET_CONTINUE) { - return verdict; - } #endif - /* This is only used for getting source and destination ports. - * Because both TCP and UDP header have these in the same - * location, we can check them both using the UDP struct. - */ if (IS_ENABLED(CONFIG_NET_UDP) && proto == IPPROTO_UDP) { - struct net_udp_hdr hdr, *udp_hdr; - - ARG_UNUSED(hdr); - - udp_hdr = net_udp_get_hdr(pkt, &hdr); - if (!udp_hdr) { - return NET_DROP; - } - - src_port = udp_hdr->src_port; - dst_port = udp_hdr->dst_port; + src_port = proto_hdr->udp->src_port; + dst_port = proto_hdr->udp->dst_port; } else if (IS_ENABLED(CONFIG_NET_TCP) && proto == IPPROTO_TCP) { - struct net_tcp_hdr hdr, *tcp_hdr; - - ARG_UNUSED(hdr); - - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); - if (!tcp_hdr) { - return NET_DROP; - } - - src_port = tcp_hdr->src_port; - dst_port = tcp_hdr->dst_port; + src_port = proto_hdr->tcp->src_port; + dst_port = proto_hdr->tcp->dst_port; } else { - NET_DBG("No UDP or TCP configured, dropping packet."); return NET_DROP; } - if (is_invalid_packet(pkt, src_port, dst_port)) { + if (is_invalid_packet(pkt, ip_hdr, src_port, dst_port)) { NET_DBG("Dropping invalid packet"); return NET_DROP; } - if (CONFIG_NET_CONN_LOG_LEVEL >= LOG_LEVEL_DBG) { - int data_len = -1; - - if (IS_ENABLED(CONFIG_NET_IPV4) && - net_pkt_family(pkt) == AF_INET) { - data_len = ntohs(NET_IPV4_HDR(pkt)->len); - } else if (IS_ENABLED(CONFIG_NET_IPV6) && - net_pkt_family(pkt) == AF_INET6) { - data_len = ntohs(NET_IPV6_HDR(pkt)->len); - } - - NET_DBG("Check %s listener for pkt %p src port %u dst port %u " - "family %d len %d", net_proto2str(proto), - pkt, - ntohs(src_port), - ntohs(dst_port), - net_pkt_family(pkt), data_len); +#if defined(CONFIG_NET_CONN_CACHE) + verdict = cache_check(pkt, ip_hdr, proto_hdr, proto, src_port, dst_port, + &cache_value, &pos); + if (verdict != NET_CONTINUE) { + return verdict; } +#endif + + NET_DBG("Check %s listener for pkt %p src port %u dst port %u" + " family %d", net_proto2str(proto), pkt, + ntohs(src_port), ntohs(dst_port), net_pkt_family(pkt)); for (i = 0; i < CONFIG_NET_MAX_CONN; i++) { if (!(conns[i].flags & NET_CONN_IN_USE)) { @@ -897,13 +847,15 @@ enum net_verdict net_conn_input(enum net_ip_protocol proto, struct net_pkt *pkt) } if (conns[i].flags & NET_CONN_REMOTE_ADDR_SET) { - if (!check_addr(pkt, &conns[i].remote_addr, true)) { + if (!check_addr(pkt, ip_hdr, + &conns[i].remote_addr, true)) { continue; } } if (conns[i].flags & NET_CONN_LOCAL_ADDR_SET) { - if (!check_addr(pkt, &conns[i].local_addr, false)) { + if (!check_addr(pkt, ip_hdr, + &conns[i].local_addr, false)) { continue; } } @@ -924,27 +876,6 @@ enum net_verdict net_conn_input(enum net_ip_protocol proto, struct net_pkt *pkt) } if (best_match >= 0) { - - /* If packet has a listener configured, then check also the - * protocol checksum if that checking is enabled. - * If the checksum calculation fails, then discard the message. - */ - if (IS_ENABLED(CONFIG_NET_UDP_CHECKSUM) && - proto == IPPROTO_UDP && - net_if_need_calc_rx_checksum(net_pkt_iface(pkt)) && - net_calc_chksum_udp(pkt) != 0) { - net_stats_update_udp_chkerr(net_pkt_iface(pkt)); - NET_DBG("DROP: UDP checksum mismatch"); - goto drop; - } else if (IS_ENABLED(CONFIG_NET_TCP_CHECKSUM) && - proto == IPPROTO_TCP && - net_if_need_calc_rx_checksum(net_pkt_iface(pkt)) && - net_calc_chksum_tcp(pkt) != 0) { - net_stats_update_tcp_seg_chkerr(net_pkt_iface(pkt)); - NET_DBG("DROP: TCP checksum mismatch"); - goto drop; - } - #if defined(CONFIG_NET_CONN_CACHE) NET_DBG("[%d] match found cb %p ud %p rank 0x%02x cache 0x%x", best_match, @@ -964,8 +895,8 @@ enum net_verdict net_conn_input(enum net_ip_protocol proto, struct net_pkt *pkt) conns[best_match].rank); #endif /* CONFIG_NET_CONN_CACHE */ - if (conns[best_match].cb(&conns[best_match], pkt, - conns[best_match].user_data) == NET_DROP) { + if (conns[best_match].cb(&conns[best_match], pkt, ip_hdr, + proto_hdr, conns[best_match].user_data) == NET_DROP) { goto drop; } @@ -978,22 +909,18 @@ enum net_verdict net_conn_input(enum net_ip_protocol proto, struct net_pkt *pkt) cache_add_neg(cache_value); -#if defined(CONFIG_NET_IPV6) /* If the destination address is multicast address, - * we do not send ICMP error as that makes no sense. + * we will not send an ICMP error as that makes no sense. */ - if (net_pkt_family(pkt) == AF_INET6 && - net_ipv6_is_addr_mcast(&NET_IPV6_HDR(pkt)->dst)) { + if (IS_ENABLED(CONFIG_NET_IPV6) && + net_pkt_family(pkt) == AF_INET6 && + net_ipv6_is_addr_mcast(&ip_hdr->ipv6->dst)) { ; - } else -#endif -#if defined(CONFIG_NET_IPV4) - if (net_pkt_family(pkt) == AF_INET && - net_ipv4_is_addr_mcast(&NET_IPV4_HDR(pkt)->dst)) { + } else if (IS_ENABLED(CONFIG_NET_IPV4) && + net_pkt_family(pkt) == AF_INET && + net_ipv4_is_addr_mcast(&ip_hdr->ipv4->dst)) { ; - } else -#endif - { + } else { send_icmp_error(pkt); if (IS_ENABLED(CONFIG_NET_TCP) && proto == IPPROTO_TCP) { diff --git a/subsys/net/ip/connection.h b/subsys/net/ip/connection.h index 266b1dc5d6fc1..7438375924488 100644 --- a/subsys/net/ip/connection.h +++ b/subsys/net/ip/connection.h @@ -36,6 +36,8 @@ struct net_conn_handle; */ typedef enum net_verdict (*net_conn_cb_t)(struct net_conn *conn, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, void *user_data); /** @@ -133,11 +135,15 @@ int net_conn_change_callback(struct net_conn_handle *handle, * disabled, the function will always return NET_DROP. */ #if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_TCP) -enum net_verdict net_conn_input(enum net_ip_protocol proto, - struct net_pkt *pkt); +enum net_verdict net_conn_input(struct net_pkt *pkt, + union net_ip_header *ip_hdr, + u8_t proto, + union net_proto_header *proto_hdr); #else -static inline enum net_verdict net_conn_input(enum net_ip_protocol proto, - struct net_pkt *pkt) +static inline enum net_verdict net_conn_input(struct net_pkt *pkt, + union net_ip_header *ip_hdr, + u8_t proto, + union net_proto_header *proto_hdr) { return NET_DROP; } diff --git a/subsys/net/ip/dhcpv4.c b/subsys/net/ip/dhcpv4.c index cbb6f97547995..302e7e47ed9a9 100644 --- a/subsys/net/ip/dhcpv4.c +++ b/subsys/net/ip/dhcpv4.c @@ -62,23 +62,21 @@ static const char *dhcpv4_msg_type_name(enum dhcpv4_msg_type msg_type) /* Add magic cookie to DCHPv4 messages */ static inline bool dhcpv4_add_cookie(struct net_pkt *pkt) { - return net_pkt_append_all(pkt, sizeof(magic_cookie), - magic_cookie, K_FOREVER); + if (net_pkt_write_new(pkt, (void *)magic_cookie, + ARRAY_SIZE(magic_cookie))) { + return false; + } + + return true; } /* Add a an option with the form OPTION LENGTH VALUE. */ static bool dhcpv4_add_option_length_value(struct net_pkt *pkt, u8_t option, - u8_t size, const u8_t *value) + u8_t size, const void *value) { - if (!net_pkt_append_u8(pkt, option)) { - return false; - } - - if (!net_pkt_append_u8(pkt, size)) { - return false; - } - - if (!net_pkt_append_all(pkt, size, value, K_FOREVER)) { + if (net_pkt_write_u8_new(pkt, option) || + net_pkt_write_u8_new(pkt, size) || + net_pkt_write_new(pkt, value, size)) { return false; } @@ -97,13 +95,12 @@ static bool dhcpv4_add_msg_type(struct net_pkt *pkt, u8_t type) */ static bool dhcpv4_add_req_options(struct net_pkt *pkt) { - static const u8_t data[5] = { DHCPV4_OPTIONS_REQ_LIST, - 3, /* Length */ - DHCPV4_OPTIONS_SUBNET_MASK, - DHCPV4_OPTIONS_ROUTER, - DHCPV4_OPTIONS_DNS_SERVER }; + static u8_t data[3] = { DHCPV4_OPTIONS_SUBNET_MASK, + DHCPV4_OPTIONS_ROUTER, + DHCPV4_OPTIONS_DNS_SERVER }; - return net_pkt_append_all(pkt, sizeof(data), data, K_FOREVER); + return dhcpv4_add_option_length_value(pkt, DHCPV4_OPTIONS_REQ_LIST, + ARRAY_SIZE(data), data); } static bool dhcpv4_add_server_id(struct net_pkt *pkt, @@ -123,91 +120,76 @@ static bool dhcpv4_add_req_ipaddr(struct net_pkt *pkt, /* Add DHCPv4 Options end, rest of the message can be padded wit zeros */ static inline bool dhcpv4_add_end(struct net_pkt *pkt) { - return net_pkt_append_u8(pkt, DHCPV4_OPTIONS_END); -} - -/* File is empty ATM */ -static inline bool dhcpv4_add_file(struct net_pkt *pkt) -{ - const u16_t size = SIZE_OF_FILE; - - if (net_pkt_append_memset(pkt, size, 0, PKT_WAIT_TIME) != size) { + if (net_pkt_write_u8_new(pkt, DHCPV4_OPTIONS_END)) { return false; } return true; } -/* SNAME is empty ATM */ -static inline bool dhcpv4_add_sname(struct net_pkt *pkt) +/* File is empty ATM */ +static inline bool dhcpv4_add_file(struct net_pkt *pkt) { - const u16_t size = SIZE_OF_SNAME; - - if (net_pkt_append_memset(pkt, size, 0, PKT_WAIT_TIME) != size) { + if (net_pkt_memset(pkt, 0, SIZE_OF_FILE)) { return false; } return true; } -/* Setup UDP header */ -static bool dhcpv4_setup_udp_header(struct net_pkt *pkt) +/* SNAME is empty ATM */ +static inline bool dhcpv4_add_sname(struct net_pkt *pkt) { - struct net_udp_hdr hdr, *udp; - u16_t len; - - udp = net_udp_get_hdr(pkt, &hdr); - if (!udp || udp == &hdr) { - NET_ERR("Could not get UDP header"); + if (net_pkt_memset(pkt, 0, SIZE_OF_SNAME)) { return false; } - len = net_pkt_get_len(pkt) - NET_IPV4H_LEN; - - /* Setup UDP header */ - udp->src_port = htons(DHCPV4_CLIENT_PORT); - udp->dst_port = htons(DHCPV4_SERVER_PORT); - udp->len = htons(len); - - net_udp_set_hdr(pkt, udp); - return true; } -/* Prepare initial DHCPv4 message and add options as per message type */ -static struct net_pkt *dhcpv4_prepare_message(struct net_if *iface, u8_t type, - const struct in_addr *ciaddr, - const struct in_addr *server_addr) +/* Create DHCPv4 message and add options as per message type */ +static struct net_pkt *dhcpv4_create_message(struct net_if *iface, u8_t type, + const struct in_addr *ciaddr, + const struct in_addr *server_addr, + bool server_id, bool requested_ip) { + NET_PKT_DATA_ACCESS_DEFINE(dhcp_access, struct dhcp_msg); const struct in_addr src = INADDR_ANY_INIT; + size_t size = DHCPV4_MESSAGE_SIZE; struct net_pkt *pkt; - struct net_buf *frag; struct dhcp_msg *msg; - pkt = net_pkt_get_reserve_tx(K_FOREVER); - net_pkt_set_iface(pkt, iface); - net_pkt_set_ipv4_ttl(pkt, 0xFF); + if (server_id) { + size += DHCPV4_OLV_MSG_SERVER_ID; + } - net_ipv4_create(pkt, &src, server_addr, iface, IPPROTO_UDP); + if (requested_ip) { + size += DHCPV4_OLV_MSG_REQ_IPADDR; + } + + if (type == DHCPV4_MSG_TYPE_DISCOVER) { + size += DHCPV4_OLV_MSG_REQ_LIST; + } - frag = pkt->frags; + pkt = net_pkt_alloc_with_buffer(iface, size, AF_INET, + IPPROTO_UDP, K_FOREVER); - /* Leave room for UDP header which will be filled in later */ - net_buf_add(frag, NET_UDPH_LEN); + net_pkt_set_ipv4_ttl(pkt, 0xFF); - if (net_buf_tailroom(frag) < sizeof(struct dhcp_msg)) { + if (net_ipv4_create_new(pkt, &src, server_addr) || + net_udp_create(pkt, htons(DHCPV4_CLIENT_PORT), + htons(DHCPV4_SERVER_PORT))) { goto fail; } - msg = (struct dhcp_msg *)(frag->data + NET_IPV4UDPH_LEN); - (void)memset(msg, 0, sizeof(struct dhcp_msg)); + msg = (struct dhcp_msg *)net_pkt_get_data_new(pkt, &dhcp_access); - msg->op = DHCPV4_MSG_BOOT_REQUEST; + (void)memset(msg, 0, sizeof(struct dhcp_msg)); + msg->op = DHCPV4_MSG_BOOT_REQUEST; msg->htype = HARDWARE_ETHERNET_TYPE; - msg->hlen = HARDWARE_ETHERNET_LEN; - - msg->xid = htonl(iface->config.dhcpv4.xid); + msg->hlen = HARDWARE_ETHERNET_LEN; + msg->xid = htonl(iface->config.dhcpv4.xid); msg->flags = htons(DHCPV4_MSG_BROADCAST); if (ciaddr) { @@ -221,7 +203,9 @@ static struct net_pkt *dhcpv4_prepare_message(struct net_if *iface, u8_t type, memcpy(msg->chaddr, net_if_get_link_addr(iface)->addr, net_if_get_link_addr(iface)->len); - net_buf_add(frag, sizeof(struct dhcp_msg)); + if (net_pkt_set_data(pkt, &dhcp_access)) { + goto fail; + } if (!dhcpv4_add_sname(pkt) || !dhcpv4_add_file(pkt) || @@ -230,10 +214,31 @@ static struct net_pkt *dhcpv4_prepare_message(struct net_if *iface, u8_t type, goto fail; } + if ((server_id && + !dhcpv4_add_server_id(pkt, &iface->config.dhcpv4.server_id)) || + (requested_ip && + !dhcpv4_add_req_ipaddr(pkt, &iface->config.dhcpv4.requested_ip))) { + goto fail; + } + + if (type == DHCPV4_MSG_TYPE_DISCOVER && !dhcpv4_add_req_options(pkt)) { + goto fail; + } + + if (!dhcpv4_add_end(pkt)) { + goto fail; + } + + net_pkt_cursor_init(pkt); + + net_ipv4_finalize_new(pkt, IPPROTO_UDP); + return pkt; fail: + NET_DBG("Message creation failed"); net_pkt_unref(pkt); + return NULL; } @@ -279,36 +284,16 @@ static u32_t dhcpv4_send_request(struct net_if *iface) */ ciaddr = &iface->config.dhcpv4.requested_ip; - server_addr = net_ipv4_broadcast_address(); break; } - pkt = dhcpv4_prepare_message(iface, DHCPV4_MSG_TYPE_REQUEST, - ciaddr, server_addr); + pkt = dhcpv4_create_message(iface, DHCPV4_MSG_TYPE_REQUEST, + ciaddr, server_addr, with_server_id, + with_requested_ip); if (!pkt) { goto fail; } - if (with_server_id && - !dhcpv4_add_server_id(pkt, &iface->config.dhcpv4.server_id)) { - goto fail; - } - - if (with_requested_ip && - !dhcpv4_add_req_ipaddr(pkt, &iface->config.dhcpv4.requested_ip)) { - goto fail; - } - - if (!dhcpv4_add_end(pkt)) { - goto fail; - } - - if (!dhcpv4_setup_udp_header(pkt)) { - goto fail; - } - - net_ipv4_finalize(pkt, IPPROTO_UDP); - if (net_send_data(pkt) < 0) { goto fail; } @@ -332,8 +317,6 @@ static u32_t dhcpv4_send_request(struct net_if *iface) return timeout; fail: - NET_DBG("Message preparation failed"); - if (pkt) { net_pkt_unref(pkt); } @@ -349,22 +332,13 @@ static u32_t dhcpv4_send_discover(struct net_if *iface) iface->config.dhcpv4.xid++; - pkt = dhcpv4_prepare_message(iface, DHCPV4_MSG_TYPE_DISCOVER, - NULL, net_ipv4_broadcast_address()); + pkt = dhcpv4_create_message(iface, DHCPV4_MSG_TYPE_DISCOVER, + NULL, net_ipv4_broadcast_address(), + false, false); if (!pkt) { goto fail; } - if (!dhcpv4_add_req_options(pkt) || !dhcpv4_add_end(pkt)) { - goto fail; - } - - if (!dhcpv4_setup_udp_header(pkt)) { - goto fail; - } - - net_ipv4_finalize(pkt, IPPROTO_UDP); - if (net_send_data(pkt) < 0) { goto fail; } @@ -382,13 +356,13 @@ static u32_t dhcpv4_send_discover(struct net_if *iface) return timeout; fail: - NET_DBG("Message preparation failed"); - if (pkt) { net_pkt_unref(pkt); } - return UINT32_MAX; + return iface->config.dhcpv4.xid % + (DHCPV4_INITIAL_DELAY_MAX - DHCPV4_INITIAL_DELAY_MIN) + + DHCPV4_INITIAL_DELAY_MIN; } static void dhcpv4_update_timeout_work(u32_t timeout) @@ -600,36 +574,29 @@ static void dhcpv4_timeout(struct k_work *work) /* Parse DHCPv4 options and retrieve relavant information * as per RFC 2132. */ -static enum net_verdict dhcpv4_parse_options(struct net_if *iface, - struct net_buf *frag, - u16_t offset, - enum dhcpv4_msg_type *msg_type) +static bool dhcpv4_parse_options(struct net_pkt *pkt, + struct net_if *iface, + enum dhcpv4_msg_type *msg_type) { u8_t cookie[4]; u8_t length; u8_t type; - u16_t pos; - - frag = net_frag_read(frag, offset, &pos, sizeof(magic_cookie), - (u8_t *)cookie); - if (!frag || memcmp(magic_cookie, cookie, sizeof(magic_cookie))) { + if (net_pkt_read_new(pkt, cookie, sizeof(cookie)) || + memcmp(magic_cookie, cookie, sizeof(magic_cookie))) { NET_DBG("Incorrect magic cookie"); - return NET_DROP; + return false; } - while (frag) { - frag = net_frag_read_u8(frag, pos, &pos, &type); - + while (!net_pkt_read_u8_new(pkt, &type)) { if (type == DHCPV4_OPTIONS_END) { NET_DBG("options_end"); - return NET_OK; + return true; } - frag = net_frag_read_u8(frag, pos, &pos, &length); - if (!frag) { + if (net_pkt_read_u8_new(pkt, &length)) { NET_ERR("option parsing, bad length"); - return NET_DROP; + return false; } switch (type) { @@ -638,14 +605,12 @@ static enum net_verdict dhcpv4_parse_options(struct net_if *iface, if (length != 4) { NET_ERR("options_subnet_mask, bad length"); - return NET_DROP; + return false; } - frag = net_frag_read(frag, pos, &pos, length, - netmask.s4_addr); - if (!frag && pos) { + if (net_pkt_read_new(pkt, netmask.s4_addr, length)) { NET_ERR("options_subnet_mask, short packet"); - return NET_DROP; + return false; } net_if_ipv4_set_netmask(iface, &netmask); @@ -664,14 +629,13 @@ static enum net_verdict dhcpv4_parse_options(struct net_if *iface, */ if (length % 4 != 0 || length < 4) { NET_ERR("options_router, bad length"); - return NET_DROP; + return false; } - frag = net_frag_read(frag, pos, &pos, 4, router.s4_addr); - frag = net_frag_skip(frag, pos, &pos, length - 4); - if (!frag && pos) { + if (net_pkt_read_new(pkt, router.s4_addr, 4) || + net_pkt_skip(pkt, length - 4)) { NET_ERR("options_router, short packet"); - return NET_DROP; + return false; } NET_DBG("options_router: %s", @@ -695,16 +659,15 @@ static enum net_verdict dhcpv4_parse_options(struct net_if *iface, */ if (length % 4 != 0) { NET_ERR("options_dns, bad length"); - return NET_DROP; + return false; } (void)memset(&dns, 0, sizeof(dns)); - frag = net_frag_read(frag, pos, &pos, 4, - dns.sin_addr.s4_addr); - frag = net_frag_skip(frag, pos, &pos, length - 4); - if (!frag && pos) { + + if (net_pkt_read_new(pkt, dns.sin_addr.s4_addr, 4) || + net_pkt_skip(pkt, length - 4)) { NET_ERR("options_dns, short packet"); - return NET_DROP; + return false; } dns.sin_family = AF_INET; @@ -715,7 +678,7 @@ static enum net_verdict dhcpv4_parse_options(struct net_if *iface, if (status < 0) { NET_DBG("options_dns, failed to set " "resolve address: %d", status); - return NET_DROP; + return false; } break; @@ -724,88 +687,100 @@ static enum net_verdict dhcpv4_parse_options(struct net_if *iface, case DHCPV4_OPTIONS_LEASE_TIME: if (length != 4) { NET_ERR("options_lease_time, bad length"); - return NET_DROP; + return false; + } + + if (net_pkt_read_be32_new( + pkt, &iface->config.dhcpv4.lease_time) || + !iface->config.dhcpv4.lease_time) { + NET_ERR("options_lease_time, wrong value"); + return false; } - frag = net_frag_read_be32(frag, pos, &pos, - &iface->config.dhcpv4.lease_time); NET_DBG("options_lease_time: %u", iface->config.dhcpv4.lease_time); - if (!iface->config.dhcpv4.lease_time) { - return NET_DROP; - } - break; case DHCPV4_OPTIONS_RENEWAL: if (length != 4) { NET_DBG("options_renewal, bad length"); - return NET_DROP; + return false; + } + + if (net_pkt_read_be32_new( + pkt, &iface->config.dhcpv4.renewal_time) || + !iface->config.dhcpv4.renewal_time) { + NET_DBG("options_renewal, wrong value"); + return false; } - frag = net_frag_read_be32(frag, pos, &pos, - &iface->config.dhcpv4.renewal_time); NET_DBG("options_renewal: %u", iface->config.dhcpv4.renewal_time); - if (!iface->config.dhcpv4.renewal_time) { - return NET_DROP; - } - break; case DHCPV4_OPTIONS_REBINDING: if (length != 4) { NET_DBG("options_rebinding, bad length"); - return NET_DROP; + return false; + } + + if (net_pkt_read_be32_new( + pkt, + &iface->config.dhcpv4.rebinding_time) || + !iface->config.dhcpv4.rebinding_time) { + NET_DBG("options_rebinding, wrong value"); + return false; } - frag = net_frag_read_be32(frag, pos, &pos, - &iface->config.dhcpv4.rebinding_time); NET_DBG("options_rebinding: %u", iface->config.dhcpv4.rebinding_time); - if (!iface->config.dhcpv4.rebinding_time) { - return NET_DROP; - } - break; case DHCPV4_OPTIONS_SERVER_ID: if (length != 4) { NET_DBG("options_server_id, bad length"); - return NET_DROP; + return false; + } + + if (net_pkt_read_new( + pkt, + iface->config.dhcpv4.server_id.s4_addr, + length)) { + NET_DBG("options_server_id, read err"); + return false; } - frag = net_frag_read(frag, pos, &pos, length, - iface->config.dhcpv4.server_id.s4_addr); NET_DBG("options_server_id: %s", log_strdup(net_sprint_ipv4_addr( &iface->config.dhcpv4.server_id))); break; case DHCPV4_OPTIONS_MSG_TYPE: { - u8_t v; - if (length != 1) { NET_DBG("options_msg_type, bad length"); - return NET_DROP; + return false; + } + + if (net_pkt_read_u8_new(pkt, (u8_t *)msg_type)) { + NET_DBG("options_msg_type, read err"); + return false; } - frag = net_frag_read_u8(frag, pos, &pos, &v); - *msg_type = v; break; } default: NET_DBG("option unknown: %d", type); - frag = net_frag_skip(frag, pos, &pos, length); - break; - } - if (!frag && pos) { - return NET_DROP; + if (net_pkt_skip(pkt, length)) { + NET_DBG("option unknown, skip err"); + return false; + } + + break; } } /* Invalid case: Options without DHCPV4_OPTIONS_END. */ - return NET_DROP; + return false; } static inline void dhcpv4_handle_msg_offer(struct net_if *iface) @@ -900,22 +875,22 @@ static void dhcpv4_handle_reply(struct net_if *iface, static enum net_verdict net_dhcpv4_input(struct net_conn *conn, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, void *user_data) { + NET_PKT_DATA_ACCESS_DEFINE(dhcp_access, struct dhcp_msg); enum dhcpv4_msg_type msg_type = 0; struct dhcp_msg *msg; - struct net_buf *frag; struct net_if *iface; - u8_t min; - u16_t pos; if (!conn) { NET_DBG("Invalid connection"); return NET_DROP; } - if (!pkt || !pkt->frags) { - NET_DBG("Invalid packet, no fragments"); + if (!pkt) { + NET_DBG("Invalid packet"); return NET_DROP; } @@ -925,19 +900,25 @@ static enum net_verdict net_dhcpv4_input(struct net_conn *conn, return NET_DROP; } - frag = pkt->frags; - min = NET_IPV4UDPH_LEN + sizeof(struct dhcp_msg); - /* If the message is not DHCP then continue passing to * related handlers. */ - if (net_pkt_get_len(pkt) < min) { + if (net_pkt_get_len(pkt) < NET_IPV4UDPH_LEN + sizeof(struct dhcp_msg)) { NET_DBG("Input msg is not related to DHCPv4"); return NET_CONTINUE; } - msg = (struct dhcp_msg *)(frag->data + NET_IPV4UDPH_LEN); + net_pkt_cursor_init(pkt); + + if (net_pkt_skip(pkt, NET_IPV4UDPH_LEN)) { + return NET_DROP; + } + + msg = (struct dhcp_msg *)net_pkt_get_data_new(pkt, &dhcp_access); + if (!msg) { + return NET_DROP; + } NET_DBG("Received dhcp msg [op=0x%x htype=0x%x hlen=%u xid=0x%x " "secs=%u flags=0x%x chaddr=%s", @@ -960,22 +941,22 @@ static enum net_verdict net_dhcpv4_input(struct net_conn *conn, NET_DBG("Unexpected op (%d), xid (%x vs %x) or chaddr", msg->op, iface->config.dhcpv4.xid, ntohl(msg->xid)); - goto drop; + return NET_DROP; } - memcpy(iface->config.dhcpv4.requested_ip.s4_addr, msg->yiaddr, - sizeof(msg->yiaddr)); + memcpy(iface->config.dhcpv4.requested_ip.s4_addr, + msg->yiaddr, sizeof(msg->yiaddr)); + + net_pkt_acknowledge_data(pkt, &dhcp_access); /* SNAME, FILE are not used at the moment, skip it */ - frag = net_frag_skip(frag, min, &pos, SIZE_OF_SNAME + SIZE_OF_FILE); - if (!frag && pos) { + if (net_pkt_skip(pkt, SIZE_OF_SNAME + SIZE_OF_FILE)) { NET_DBG("short packet while skipping sname"); - goto drop; + return NET_DROP; } - if (dhcpv4_parse_options(iface, frag, pos, &msg_type) == NET_DROP) { - NET_DBG("Invalid Options"); - goto drop; + if (!dhcpv4_parse_options(pkt, iface, &msg_type)) { + return NET_DROP; } net_pkt_unref(pkt); @@ -983,9 +964,6 @@ static enum net_verdict net_dhcpv4_input(struct net_conn *conn, dhcpv4_handle_reply(iface, msg_type); return NET_OK; - -drop: - return NET_DROP; } static void dhcpv4_iface_event_handler(struct net_mgmt_event_callback *cb, diff --git a/subsys/net/ip/dhcpv4.h b/subsys/net/ip/dhcpv4.h index 307b4efb13faa..31404d237b4e1 100644 --- a/subsys/net/ip/dhcpv4.h +++ b/subsys/net/ip/dhcpv4.h @@ -7,6 +7,7 @@ /* * Copyright (c) 2017 ARM Ltd. + * Copyright (c) 2018 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ @@ -35,8 +36,9 @@ struct dhcp_msg { u8_t chaddr[16]; /* Client hardware address */ } __packed; -#define SIZE_OF_SNAME 64 -#define SIZE_OF_FILE 128 +#define SIZE_OF_SNAME 64 +#define SIZE_OF_FILE 128 +#define SIZE_OF_MAGIC_COOKIE 4 #define DHCPV4_MSG_BROADCAST 0x8000 #define DHCPV4_MSG_UNICAST 0x0000 @@ -44,11 +46,11 @@ struct dhcp_msg { #define DHCPV4_MSG_BOOT_REQUEST 1 #define DHCPV4_MSG_BOOT_REPLY 2 -#define HARDWARE_ETHERNET_TYPE 1 -#define HARDWARE_ETHERNET_LEN 6 +#define HARDWARE_ETHERNET_TYPE 1 +#define HARDWARE_ETHERNET_LEN 6 -#define DHCPV4_SERVER_PORT 67 -#define DHCPV4_CLIENT_PORT 68 +#define DHCPV4_SERVER_PORT 67 +#define DHCPV4_CLIENT_PORT 68 /* These enumerations represent RFC2131 defined msy type codes, hence * they should not be renumbered. @@ -76,6 +78,21 @@ enum dhcpv4_msg_type { #define DHCPV4_OPTIONS_REBINDING 59 #define DHCPV4_OPTIONS_END 255 +/* Useful size macros */ +#define DHCPV4_OLV_MSG_REQ_IPADDR 6 +#define DHCPV4_OLV_MSG_TYPE_SIZE 3 +#define DHCPV4_OLV_MSG_SERVER_ID 6 +#define DHCPV4_OLV_MSG_REQ_LIST 5 + +#define DHCPV4_OLV_END_SIZE 1 + +#define DHCPV4_MESSAGE_SIZE (sizeof(struct dhcp_msg) + \ + SIZE_OF_SNAME + SIZE_OF_FILE + \ + SIZE_OF_MAGIC_COOKIE + \ + DHCPV4_OLV_MSG_TYPE_SIZE + \ + DHCPV4_OLV_END_SIZE) + + /* TODO: * 1) Support for UNICAST flag (some dhcpv4 servers will not reply if * DISCOVER message contains BROADCAST FLAG). diff --git a/subsys/net/ip/icmpv4.c b/subsys/net/ip/icmpv4.c index 4a7a719a0373f..cf90da9775d10 100644 --- a/subsys/net/ip/icmpv4.c +++ b/subsys/net/ip/icmpv4.c @@ -25,36 +25,6 @@ LOG_MODULE_REGISTER(net_icmpv4, CONFIG_NET_ICMPV4_LOG_LEVEL); static sys_slist_t handlers; -int net_icmpv4_set_hdr(struct net_pkt *pkt, struct net_icmp_hdr *hdr) -{ - struct net_buf *frag; - u16_t pos; - - frag = net_pkt_write(pkt, pkt->frags, - net_pkt_ip_hdr_len(pkt), - &pos, sizeof(*hdr), (u8_t *)hdr, - PKT_WAIT_TIME); - if (pos > 0 && !frag) { - return -EINVAL; - } - - return 0; -} - -int net_icmpv4_get_hdr(struct net_pkt *pkt, struct net_icmp_hdr *hdr) -{ - struct net_buf *frag; - u16_t pos; - - frag = net_frag_read(pkt->frags, net_pkt_ip_hdr_len(pkt), &pos, - sizeof(*hdr), (u8_t *)hdr); - if (pos > 0 && !frag) { - return -EINVAL; - } - - return 0; -} - int net_icmpv4_set_chksum(struct net_pkt *pkt) { u16_t chksum = 0U; @@ -95,70 +65,103 @@ int net_icmpv4_set_chksum(struct net_pkt *pkt) return 0; } -static inline enum net_verdict icmpv4_handle_echo_request(struct net_pkt *pkt) +static int icmpv4_create(struct net_pkt *pkt, u8_t icmp_type, u8_t icmp_code) { - /* Note that we send the same data packets back and just swap - * the addresses etc. - */ - struct net_icmp_hdr icmp_hdr; - struct in_addr addr; - int ret; + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmpv4_access, + struct net_icmp_hdr); + struct net_icmp_hdr *icmp_hdr; + + icmp_hdr = (struct net_icmp_hdr *)net_pkt_get_data_new(pkt, + &icmpv4_access); + if (!icmp_hdr) { + return -ENOBUFS; + } - NET_DBG("Received Echo Request from %s to %s", - log_strdup(net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->src)), - log_strdup(net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->dst))); + icmp_hdr->type = icmp_type; + icmp_hdr->code = icmp_code; + icmp_hdr->chksum = 0; + + return net_pkt_set_data(pkt, &icmpv4_access); +} + +int net_icmpv4_finalize(struct net_pkt *pkt) +{ + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmpv4_access, + struct net_icmp_hdr); + struct net_icmp_hdr *icmp_hdr; + + icmp_hdr = (struct net_icmp_hdr *)net_pkt_get_data_new(pkt, + &icmpv4_access); + if (!icmp_hdr) { + return -ENOBUFS; + } + + icmp_hdr->chksum = net_calc_chksum_icmpv4(pkt); + + return net_pkt_set_data(pkt, &icmpv4_access); +} + +static enum net_verdict icmpv4_handle_echo_request(struct net_pkt *pkt, + struct net_ipv4_hdr *ip_hdr) +{ + struct net_pkt *reply = NULL; - net_ipaddr_copy(&addr, &NET_IPV4_HDR(pkt)->src); - net_ipaddr_copy(&NET_IPV4_HDR(pkt)->src, - net_if_ipv4_select_src_addr(net_pkt_iface(pkt), - &addr)); /* If interface can not select src address based on dst addr * and src address is unspecified, drop the echo request. */ - if (net_ipv4_is_addr_unspecified(&NET_IPV4_HDR(pkt)->src)) { + if (net_ipv4_is_addr_unspecified(&ip_hdr->src)) { NET_DBG("DROP: src addr is unspecified"); - return NET_DROP; + goto drop; } - net_ipaddr_copy(&NET_IPV4_HDR(pkt)->dst, &addr); + NET_DBG("Received Echo Request from %s to %s", + log_strdup(net_sprint_ipv4_addr(&ip_hdr->src)), + log_strdup(net_sprint_ipv4_addr(&ip_hdr->dst))); - icmp_hdr.type = NET_ICMPV4_ECHO_REPLY; - icmp_hdr.code = 0; + net_pkt_cursor_init(pkt); - ret = net_icmpv4_set_hdr(pkt, &icmp_hdr); - if (ret < 0) { - return NET_DROP; + /* Cloning is faster here as echo request might come with data behind */ + reply = net_pkt_clone_new(pkt, PKT_WAIT_TIME); + if (!reply) { + NET_DBG("DROP: No buffer"); + goto drop; + } + + /* Let's keep the original data, + * we will only overwrite what is relevant + */ + net_pkt_set_overwrite(reply, true); + + if (net_ipv4_create_new(reply, &ip_hdr->dst, &ip_hdr->src) || + icmpv4_create(reply, NET_ICMPV4_ECHO_REPLY, 0)) { + NET_DBG("DROP: wrong buffer"); + goto drop; } - net_ipv4_finalize(pkt, IPPROTO_ICMP); + net_pkt_cursor_init(reply); + net_ipv4_finalize_new(reply, IPPROTO_ICMP); NET_DBG("Sending Echo Reply from %s to %s", - log_strdup(net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->src)), - log_strdup(net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->dst))); + log_strdup(net_sprint_ipv4_addr(&ip_hdr->dst)), + log_strdup(net_sprint_ipv4_addr(&ip_hdr->src))); - if (net_send_data(pkt) < 0) { - net_stats_update_icmp_drop(net_pkt_iface(pkt)); - return NET_DROP; + if (net_send_data(reply) < 0) { + goto drop; } - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(net_pkt_iface(reply)); - return NET_OK; -} + net_pkt_unref(pkt); -static struct net_buf *icmpv4_create(struct net_pkt *pkt, u8_t icmp_type, - u8_t icmp_code) -{ - struct net_buf *frag = pkt->frags; - u16_t pos; + return NET_OK; +drop: + if (reply) { + net_pkt_unref(reply); + } - net_buf_add(frag, sizeof(struct net_icmp_hdr)); + net_stats_update_icmp_drop(net_pkt_iface(pkt)); - frag = net_pkt_write_u8_timeout(pkt, frag, net_pkt_ip_hdr_len(pkt), - &pos, icmp_type, PKT_WAIT_TIME); - frag = net_pkt_write_u8_timeout(pkt, frag, pos, &pos, icmp_code, - PKT_WAIT_TIME); - return frag; + return NET_DROP; } int net_icmpv4_send_echo_request(struct net_if *iface, @@ -166,46 +169,52 @@ int net_icmpv4_send_echo_request(struct net_if *iface, u16_t identifier, u16_t sequence) { - struct net_if_ipv4 *ipv4 = iface->config.ip.ipv4; + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmpv4_access, + struct net_icmpv4_echo_req); + int ret = -ENOBUFS; + struct net_icmpv4_echo_req *echo_req; const struct in_addr *src; struct net_pkt *pkt; - int ret; - if (!ipv4) { + if (!iface->config.ip.ipv4) { return -EINVAL; } /* Take the first address of the network interface */ - src = &ipv4->unicast[0].address.in_addr; + src = &iface->config.ip.ipv4->unicast[0].address.in_addr; - pkt = net_pkt_get_reserve_tx(PKT_WAIT_TIME); + pkt = net_pkt_alloc_with_buffer(iface, + sizeof(struct net_icmpv4_echo_req), + AF_INET, IPPROTO_ICMP, + PKT_WAIT_TIME); if (!pkt) { return -ENOMEM; } - net_pkt_set_iface(pkt, iface); - - if (!net_ipv4_create(pkt, src, dst, iface, IPPROTO_ICMP)) { - ret = -ENOMEM; + if (net_ipv4_create_new(pkt, src, dst) || + icmpv4_create(pkt, NET_ICMPV4_ECHO_REQUEST, 0)) { goto drop; } - if (!icmpv4_create(pkt, NET_ICMPV4_ECHO_REQUEST, 0)) { - ret = -ENOMEM; + echo_req = (struct net_icmpv4_echo_req *)net_pkt_get_data_new( + pkt, &icmpv4_access); + if (!echo_req) { goto drop; } - net_buf_add(pkt->frags, sizeof(struct net_icmpv4_echo_req)); + echo_req->identifier = htons(identifier); + echo_req->sequence = htons(sequence); - NET_ICMPV4_ECHO_REQ(pkt)->identifier = htons(identifier); - NET_ICMPV4_ECHO_REQ(pkt)->sequence = htons(sequence); + net_pkt_set_data(pkt, &icmpv4_access); - net_ipv4_finalize(pkt, IPPROTO_ICMP); + net_pkt_cursor_init(pkt); + + net_ipv4_finalize_new(pkt, IPPROTO_ICMP); NET_DBG("Sending ICMPv4 Echo Request type %d from %s to %s", NET_ICMPV4_ECHO_REQUEST, - log_strdup(net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->src)), - log_strdup(net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->dst))); + log_strdup(net_sprint_ipv4_addr(src)), + log_strdup(net_sprint_ipv4_addr(dst))); if (net_send_data(pkt) >= 0) { net_stats_update_icmp_sent(iface); @@ -222,90 +231,75 @@ int net_icmpv4_send_echo_request(struct net_if *iface, return ret; } -#define append(pkt, type, value) \ - do { \ - if (!net_pkt_append_##type##_timeout(pkt, value, \ - PKT_WAIT_TIME)) { \ - err = -ENOMEM; \ - goto drop; \ - } \ - } while (0) - int net_icmpv4_send_error(struct net_pkt *orig, u8_t type, u8_t code) { + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv4_access, struct net_ipv4_hdr); int err = -EIO; + struct net_ipv4_hdr *ip_hdr; struct net_pkt *pkt; - struct net_buf *frag; - struct net_if *iface; size_t copy_len; - const struct in_addr *src, *dst; - iface = net_pkt_iface(orig); + net_pkt_cursor_init(orig); - if (NET_IPV4_HDR(orig)->proto == IPPROTO_ICMP) { - struct net_icmp_hdr icmp_hdr; - - err = net_icmpv4_get_hdr(orig, &icmp_hdr); - if (err < 0 || icmp_hdr.code < 8) { - /* We must not send ICMP errors back */ - goto drop_no_pkt; - } - } - - dst = &NET_IPV4_HDR(orig)->src; - src = &NET_IPV4_HDR(orig)->dst; - - pkt = net_pkt_get_reserve_tx(PKT_WAIT_TIME); - if (!pkt) { - err = -ENOMEM; + ip_hdr = (struct net_ipv4_hdr *)net_pkt_get_data_new(orig, + &ipv4_access); + if (!ip_hdr) { goto drop_no_pkt; } - net_pkt_set_iface(pkt, iface); - - if (!net_ipv4_create(pkt, src, dst, iface, IPPROTO_ICMP)) { - err = -ENOMEM; - goto drop; - } + if (ip_hdr->proto == IPPROTO_ICMP) { + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmpv4_access, + struct net_icmp_hdr); + struct net_icmp_hdr *icmp_hdr; - if (!icmpv4_create(pkt, type, code)) { - err = -ENOMEM; - goto drop; + icmp_hdr = (struct net_icmp_hdr *)net_pkt_get_data_new( + orig, &icmpv4_access); + if (!icmp_hdr || icmp_hdr->code < 8) { + /* We must not send ICMP errors back */ + err = -EINVAL; + goto drop_no_pkt; + } } - /* Appending unused part, filled with 0s */ - append(pkt, be32, 0); - - if (NET_IPV4_HDR(orig)->proto == IPPROTO_UDP) { + if (ip_hdr->proto == IPPROTO_UDP) { copy_len = sizeof(struct net_ipv4_hdr) + sizeof(struct net_udp_hdr); - } else if (NET_IPV4_HDR(orig)->proto == IPPROTO_TCP) { - copy_len = sizeof(struct net_ipv4_hdr); - /* FIXME, add TCP header length too */ + } else if (ip_hdr->proto == IPPROTO_TCP) { + copy_len = sizeof(struct net_ipv4_hdr) + + sizeof(struct net_tcp_hdr); } else { copy_len = 0; } - frag = net_pkt_copy(orig, copy_len, 0, PKT_WAIT_TIME); - if (!frag) { - err = -ENOMEM; - goto drop; + pkt = net_pkt_alloc_with_buffer(net_pkt_iface(orig), + copy_len + NET_ICMPV4_UNUSED_LEN, + AF_INET, IPPROTO_ICMP, + PKT_WAIT_TIME); + if (!pkt) { + err = -ENOMEM; + goto drop_no_pkt; } - net_pkt_frag_add(pkt, frag); + if (net_ipv4_create_new(pkt, &ip_hdr->dst, &ip_hdr->src) || + icmpv4_create(pkt, type, code) || + net_pkt_memset(pkt, 0, NET_ICMPV4_UNUSED_LEN) || + net_pkt_copy_new(pkt, orig, copy_len)) { + goto drop; + } - net_ipv4_finalize(pkt, IPPROTO_ICMP); + net_pkt_cursor_init(pkt); + net_ipv4_finalize_new(pkt, IPPROTO_ICMP); net_pkt_lladdr_dst(pkt)->addr = net_pkt_lladdr_src(orig)->addr; net_pkt_lladdr_dst(pkt)->len = net_pkt_lladdr_src(orig)->len; NET_DBG("Sending ICMPv4 Error Message type %d code %d from %s to %s", type, code, - log_strdup(net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->src)), - log_strdup(net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->dst))); + log_strdup(net_sprint_ipv4_addr(&ip_hdr->src)), + log_strdup(net_sprint_ipv4_addr(&ip_hdr->dst))); if (net_send_data(pkt) >= 0) { - net_stats_update_icmp_sent(iface); + net_stats_update_icmp_sent(net_pkt_iface(orig)); return 0; } @@ -313,9 +307,10 @@ int net_icmpv4_send_error(struct net_pkt *orig, u8_t type, u8_t code) net_pkt_unref(pkt); drop_no_pkt: - net_stats_update_icmp_drop(iface); + net_stats_update_icmp_drop(net_pkt_iface(orig)); return err; + } void net_icmpv4_register_handler(struct net_icmpv4_handler *handler) @@ -328,12 +323,17 @@ void net_icmpv4_unregister_handler(struct net_icmpv4_handler *handler) sys_slist_find_and_remove(&handlers, &handler->node); } -enum net_verdict net_icmpv4_input(struct net_pkt *pkt, bool bcast) +enum net_verdict net_icmpv4_input(struct net_pkt *pkt, + struct net_ipv4_hdr *ip_hdr) { + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmp_access, + struct net_icmp_hdr); + struct net_icmp_hdr *icmp_hdr; struct net_icmpv4_handler *cb; - struct net_icmp_hdr icmp_hdr; - if (net_icmpv4_get_hdr(pkt, &icmp_hdr) < 0) { + icmp_hdr = (struct net_icmp_hdr *)net_pkt_get_data_new(pkt, + &icmp_access); + if (!icmp_hdr) { NET_DBG("DROP: NULL ICMPv4 header"); return NET_DROP; } @@ -343,21 +343,24 @@ enum net_verdict net_icmpv4_input(struct net_pkt *pkt, bool bcast) goto drop; } - if (bcast && (!IS_ENABLED(CONFIG_NET_ICMPV4_ACCEPT_BROADCAST) || - icmp_hdr.type != NET_ICMPV4_ECHO_REQUEST)) { + if (net_ipv4_is_addr_bcast(net_pkt_iface(pkt), &ip_hdr->dst) && + (!IS_ENABLED(CONFIG_NET_ICMPV4_ACCEPT_BROADCAST) || + icmp_hdr->type != NET_ICMPV4_ECHO_REQUEST)) { NET_DBG("DROP: broadcast pkt"); goto drop; } + net_pkt_acknowledge_data(pkt, &icmp_access); + NET_DBG("ICMPv4 packet received type %d code %d", - icmp_hdr.type, icmp_hdr.code); + icmp_hdr->type, icmp_hdr->code); net_stats_update_icmp_recv(net_pkt_iface(pkt)); SYS_SLIST_FOR_EACH_CONTAINER(&handlers, cb, node) { - if (cb->type == icmp_hdr.type && - (cb->code == icmp_hdr.code || cb->code == 0)) { - return cb->handler(pkt); + if (cb->type == icmp_hdr->type && + (cb->code == icmp_hdr->code || cb->code == 0)) { + return cb->handler(pkt, ip_hdr); } } diff --git a/subsys/net/ip/icmpv4.h b/subsys/net/ip/icmpv4.h index 8e47033fd240d..71049f3e601d7 100644 --- a/subsys/net/ip/icmpv4.h +++ b/subsys/net/ip/icmpv4.h @@ -25,16 +25,16 @@ #define NET_ICMPV4_DST_UNREACH_NO_PROTO 2 /* Protocol not supported */ #define NET_ICMPV4_DST_UNREACH_NO_PORT 3 /* Port unreachable */ +#define NET_ICMPV4_UNUSED_LEN 4 + struct net_icmpv4_echo_req { u16_t identifier; u16_t sequence; } __packed; -#define NET_ICMPV4_ECHO_REQ(pkt) \ - ((struct net_icmpv4_echo_req *)((u8_t *)net_pkt_icmp_data(pkt) + \ - sizeof(struct net_icmp_hdr))) - -typedef enum net_verdict (*icmpv4_callback_handler_t)(struct net_pkt *pkt); +typedef enum net_verdict (*icmpv4_callback_handler_t)( + struct net_pkt *pkt, + struct net_ipv4_hdr *ip_hdr); struct net_icmpv4_handler { sys_snode_t node; @@ -73,12 +73,11 @@ void net_icmpv4_register_handler(struct net_icmpv4_handler *handler); void net_icmpv4_unregister_handler(struct net_icmpv4_handler *handler); -enum net_verdict net_icmpv4_input(struct net_pkt *pkt, bool bcast); - -int net_icmpv4_get_hdr(struct net_pkt *pkt, struct net_icmp_hdr *hdr); -int net_icmpv4_set_hdr(struct net_pkt *pkt, struct net_icmp_hdr *hdr); +enum net_verdict net_icmpv4_input(struct net_pkt *pkt, + struct net_ipv4_hdr *ip_hdr); int net_icmpv4_set_chksum(struct net_pkt *pkt); +int net_icmpv4_finalize(struct net_pkt *pkt); #if defined(CONFIG_NET_IPV4) void net_icmpv4_init(void); diff --git a/subsys/net/ip/icmpv6.c b/subsys/net/ip/icmpv6.c index c6fbac16a6f78..88e7be3aa1d4c 100644 --- a/subsys/net/ip/icmpv6.c +++ b/subsys/net/ip/icmpv6.c @@ -68,36 +68,6 @@ void net_icmpv6_unregister_handler(struct net_icmpv6_handler *handler) sys_slist_find_and_remove(&handlers, &handler->node); } -static inline void setup_ipv6_header(struct net_pkt *pkt, u16_t extra_len, - u8_t hop_limit, u8_t icmp_type, - u8_t icmp_code) -{ - struct net_buf *frag = pkt->frags; - const u32_t unused = 0U; - u16_t pos; - - NET_IPV6_HDR(pkt)->vtc = 0x60; - NET_IPV6_HDR(pkt)->tcflow = 0; - NET_IPV6_HDR(pkt)->flow = 0; - - NET_IPV6_HDR(pkt)->len = htons(NET_ICMPH_LEN + extra_len + - NET_ICMPV6_UNUSED_LEN); - - NET_IPV6_HDR(pkt)->nexthdr = IPPROTO_ICMPV6; - NET_IPV6_HDR(pkt)->hop_limit = hop_limit; - - net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr)); - - frag = net_pkt_write(pkt, frag, net_pkt_ip_hdr_len(pkt), &pos, - sizeof(icmp_type), &icmp_type, PKT_WAIT_TIME); - frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(icmp_code), - &icmp_code, PKT_WAIT_TIME); - - /* ICMPv6 header has 4 unused bytes that must be zero, RFC 4443 ch 3.1 - */ - net_pkt_write(pkt, frag, pos, &pos, 4, (u8_t *)&unused, PKT_WAIT_TIME); -} - int net_icmpv6_set_chksum(struct net_pkt *pkt) { u16_t chksum = 0U; @@ -140,254 +110,120 @@ int net_icmpv6_set_chksum(struct net_pkt *pkt) return 0; } -int net_icmpv6_get_hdr(struct net_pkt *pkt, struct net_icmp_hdr *hdr) -{ - struct net_buf *frag; - u16_t pos; - - frag = net_frag_read(pkt->frags, - net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt), - &pos, sizeof(*hdr), (u8_t *)hdr); - if (pos > 0 && !frag) { - NET_ERR("Cannot get the ICMPv6 header");; - return -EINVAL; - } - - return 0; -} - -int net_icmpv6_set_hdr(struct net_pkt *pkt, struct net_icmp_hdr *hdr) -{ - struct net_buf *frag; - u16_t pos; - - frag = net_pkt_write(pkt, pkt->frags, net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt), &pos, - sizeof(*hdr), (u8_t *)hdr, PKT_WAIT_TIME); - if (pos > 0 && !frag) { - NET_ERR("Cannot set the ICMPv6 header"); - return -EINVAL; - } - - return 0; -} - -int net_icmpv6_get_ns_hdr(struct net_pkt *pkt, struct net_icmpv6_ns_hdr *hdr) -{ - struct net_buf *frag; - u16_t pos; - - frag = net_frag_read(pkt->frags, - net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt) + - sizeof(struct net_icmp_hdr), - &pos, sizeof(*hdr), (u8_t *)hdr); - if (pos > 0 && !frag) { - NET_ERR("Cannot get the ICMPv6 NS header");; - return -EINVAL; - } - - return 0; -} - -int net_icmpv6_set_ns_hdr(struct net_pkt *pkt, struct net_icmpv6_ns_hdr *hdr) -{ - struct net_buf *frag; - u16_t pos; - - hdr->reserved = 0U; - - frag = net_pkt_write(pkt, pkt->frags, net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt) + - sizeof(struct net_icmp_hdr), &pos, - sizeof(*hdr), (u8_t *)hdr, PKT_WAIT_TIME); - if (pos > 0 && !frag) { - NET_ERR("Cannot set the ICMPv6 NS header"); - return -EINVAL; - } - - return 0; -} - -int net_icmpv6_get_nd_opt_hdr(struct net_pkt *pkt, - struct net_icmpv6_nd_opt_hdr *hdr) +int net_icmpv6_finalize(struct net_pkt *pkt) { - struct net_buf *frag; - u16_t pos; + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmp_access, + struct net_icmp_hdr); + struct net_icmp_hdr *icmp_hdr; - frag = net_frag_read(pkt->frags, - net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt) + - sizeof(struct net_icmp_hdr) + - net_pkt_ipv6_ext_opt_len(pkt), - &pos, sizeof(*hdr), (u8_t *)hdr); - if (pos > 0 && !frag) { - return -EINVAL; + icmp_hdr = (struct net_icmp_hdr *)net_pkt_get_data_new(pkt, + &icmp_access); + if (!icmp_hdr) { + return -ENOBUFS; } - return 0; -} - -int net_icmpv6_get_na_hdr(struct net_pkt *pkt, struct net_icmpv6_na_hdr *hdr) -{ - struct net_buf *frag; - u16_t pos; - - frag = net_frag_read(pkt->frags, net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt) + - sizeof(struct net_icmp_hdr), - &pos, sizeof(*hdr), (u8_t *)hdr); - - if (pos > 0 && !frag) { - NET_ERR("Cannot get the ICMPv6 NA header"); - return -EINVAL; - } + icmp_hdr->chksum = net_calc_chksum_icmpv6(pkt); - return 0; + return net_pkt_set_data(pkt, &icmp_access); } -int net_icmpv6_set_na_hdr(struct net_pkt *pkt, struct net_icmpv6_na_hdr *hdr) +int net_icmpv6_create(struct net_pkt *pkt, u8_t icmp_type, u8_t icmp_code) { - struct net_buf *frag; - u16_t pos; - - (void)memset(hdr->reserved, 0, sizeof(hdr->reserved)); + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmp_access, + struct net_icmp_hdr); + struct net_icmp_hdr *icmp_hdr; - frag = net_pkt_write(pkt, pkt->frags, - net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt) + - sizeof(struct net_icmp_hdr), - &pos, sizeof(*hdr), (u8_t *)hdr, - PKT_WAIT_TIME); - if (!frag) { - NET_ERR("Cannot set the ICMPv6 NA header"); - return -EINVAL; + icmp_hdr = (struct net_icmp_hdr *)net_pkt_get_data_new(pkt, + &icmp_access); + if (!icmp_hdr) { + return -ENOBUFS; } - return 0; -} - -int net_icmpv6_get_ra_hdr(struct net_pkt *pkt, struct net_icmpv6_ra_hdr *hdr) -{ - struct net_buf *frag; - u16_t pos; + icmp_hdr->type = icmp_type; + icmp_hdr->code = icmp_code; + icmp_hdr->chksum = 0; - frag = net_frag_read(pkt->frags, net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt) + - sizeof(struct net_icmp_hdr), - &pos, sizeof(*hdr), (u8_t *)hdr); - if (pos > 0 && !frag) { - NET_ERR("Cannot get the ICMPv6 RA header"); - return -EINVAL; - } - - return 0; + return net_pkt_set_data(pkt, &icmp_access); } -static enum net_verdict handle_echo_request(struct net_pkt *orig) +static +enum net_verdict icmpv6_handle_echo_request(struct net_pkt *pkt, + struct net_ipv6_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr) { - struct net_icmp_hdr icmp_hdr; - struct net_pkt *pkt; - struct net_buf *frag; - struct net_if *iface; + struct net_pkt *reply = NULL; + const struct in6_addr *src; s16_t payload_len; - int ret; - NET_DBG("Received Echo Request from %s to %s", - log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(orig)->src)), - log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(orig)->dst))); + ARG_UNUSED(icmp_hdr); - iface = net_pkt_iface(orig); + NET_DBG("Received Echo Request from %s to %s", + log_strdup(net_sprint_ipv6_addr(&ip_hdr->src)), + log_strdup(net_sprint_ipv6_addr(&ip_hdr->dst))); - payload_len = ntohs(NET_IPV6_HDR(orig)->len) - - net_pkt_ipv6_ext_len(orig) - NET_ICMPH_LEN; + payload_len = ntohs(ip_hdr->len) - + net_pkt_ipv6_ext_len(pkt) - NET_ICMPH_LEN; if (payload_len < NET_ICMPV6_UNUSED_LEN) { /* No identifier or sequence number present */ - goto drop_no_pkt; - } - - pkt = net_pkt_get_reserve_tx(PKT_WAIT_TIME); - if (!pkt) { - goto drop_no_pkt; - } - - frag = net_pkt_copy_all(orig, 0, PKT_WAIT_TIME); - if (!frag) { goto drop; } - net_pkt_frag_add(pkt, frag); - net_pkt_set_family(pkt, AF_INET6); - net_pkt_set_iface(pkt, iface); - net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr)); - - if (net_pkt_ipv6_ext_len(orig)) { - net_pkt_set_ipv6_ext_len(pkt, net_pkt_ipv6_ext_len(orig)); - } else { - net_pkt_set_ipv6_ext_len(pkt, 0); + reply = net_pkt_alloc_with_buffer(net_pkt_iface(pkt), payload_len, + AF_INET6, IPPROTO_ICMPV6, + PKT_WAIT_TIME); + if (!reply) { + NET_DBG("DROP: No buffer"); + goto drop; } - /* Set up IPv6 Header fields */ - NET_IPV6_HDR(pkt)->vtc = 0x60; - NET_IPV6_HDR(pkt)->tcflow = 0; - NET_IPV6_HDR(pkt)->flow = 0; - NET_IPV6_HDR(pkt)->hop_limit = net_if_ipv6_get_hop_limit(iface); - - if (net_ipv6_is_addr_mcast(&NET_IPV6_HDR(pkt)->dst)) { - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, - &NET_IPV6_HDR(orig)->src); - - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, - net_if_ipv6_select_src_addr(iface, - &NET_IPV6_HDR(orig)->dst)); + if (net_ipv6_is_addr_mcast(&ip_hdr->dst)) { + src = net_if_ipv6_select_src_addr(net_pkt_iface(pkt), + &ip_hdr->dst); } else { - struct in6_addr addr; - - net_ipaddr_copy(&addr, &NET_IPV6_HDR(orig)->src); - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, - &NET_IPV6_HDR(orig)->dst); - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, &addr); + src = &ip_hdr->dst; } - net_pkt_lladdr_src(pkt)->addr = net_pkt_lladdr_dst(orig)->addr; - net_pkt_lladdr_src(pkt)->len = net_pkt_lladdr_dst(orig)->len; - /* We must not set the destination ll address here but trust * that it is set properly using a value from neighbor cache. + * Same for source as it points to original pkt ll src address. */ - net_pkt_lladdr_dst(pkt)->addr = NULL; + net_pkt_lladdr_dst(reply)->addr = NULL; + net_pkt_lladdr_src(reply)->addr = NULL; + + if (net_ipv6_create_new(reply, src, &ip_hdr->src)) { + NET_DBG("DROP: wrong buffer"); + goto drop; + } - /* ICMPv6 fields */ - ret = net_icmpv6_get_hdr(pkt, &icmp_hdr); - if (ret < 0) { + if (net_icmpv6_create(reply, NET_ICMPV6_ECHO_REPLY, 0) || + net_pkt_copy_new(reply, pkt, payload_len)) { + NET_DBG("DROP: wrong buffer"); goto drop; } - icmp_hdr.type = NET_ICMPV6_ECHO_REPLY; - icmp_hdr.code = 0; - icmp_hdr.chksum = 0; - net_icmpv6_set_hdr(pkt, &icmp_hdr); - net_icmpv6_set_chksum(pkt); + net_pkt_cursor_init(reply); + net_ipv6_finalize_new(reply, IPPROTO_ICMPV6); NET_DBG("Sending Echo Reply from %s to %s", - log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->src)), - log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->dst))); + log_strdup(net_sprint_ipv6_addr(src)), + log_strdup(net_sprint_ipv6_addr(&ip_hdr->src))); - if (net_send_data(pkt) < 0) { + if (net_send_data(reply) < 0) { goto drop; } - net_pkt_unref(orig); - net_stats_update_icmp_sent(iface); + net_stats_update_icmp_sent(net_pkt_iface(reply)); + + net_pkt_unref(pkt); return NET_OK; drop: - net_pkt_unref(pkt); + if (reply) { + net_pkt_unref(reply); + } -drop_no_pkt: - net_stats_update_icmp_drop(iface); + net_stats_update_icmp_drop(net_pkt_iface(pkt)); return NET_DROP; } @@ -395,91 +231,85 @@ static enum net_verdict handle_echo_request(struct net_pkt *orig) int net_icmpv6_send_error(struct net_pkt *orig, u8_t type, u8_t code, u32_t param) { - struct net_pkt *pkt; - struct net_buf *frag; - struct net_if *iface = net_pkt_iface(orig); - size_t extra_len, reserve; + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr); int err = -EIO; + struct net_ipv6_hdr *ip_hdr; + const struct in6_addr *src; + struct net_pkt *pkt; + size_t copy_len; - if (NET_IPV6_HDR(orig)->nexthdr == IPPROTO_ICMPV6) { - struct net_icmp_hdr icmp_hdr[1]; + net_pkt_cursor_init(orig); - if (!net_icmpv6_get_hdr(orig, icmp_hdr) || - icmp_hdr->code < 128) { + ip_hdr = (struct net_ipv6_hdr *)net_pkt_get_data_new(orig, + &ipv6_access); + if (!ip_hdr) { + goto drop_no_pkt; + } + + if (ip_hdr->nexthdr == IPPROTO_ICMPV6) { + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmpv6_access, + struct net_icmp_hdr); + struct net_icmp_hdr *icmp_hdr; + + net_pkt_acknowledge_data(orig, &ipv6_access); + + icmp_hdr = (struct net_icmp_hdr *)net_pkt_get_data_new( + orig, &icmpv6_access); + if (!icmp_hdr || icmp_hdr->code < 128) { /* We must not send ICMP errors back */ err = -EINVAL; goto drop_no_pkt; } - } - pkt = net_pkt_get_reserve_tx(PKT_WAIT_TIME); - if (!pkt) { - err = -ENOMEM; - goto drop_no_pkt; + net_pkt_cursor_init(orig); } - /* There is unsed part in ICMPv6 error msg header what we might need - * to store the param variable. - */ - reserve = sizeof(struct net_ipv6_hdr) + sizeof(struct net_icmp_hdr) + - NET_ICMPV6_UNUSED_LEN; - - if (NET_IPV6_HDR(orig)->nexthdr == IPPROTO_UDP) { - extra_len = sizeof(struct net_ipv6_hdr) + + if (ip_hdr->nexthdr == IPPROTO_UDP) { + copy_len = sizeof(struct net_ipv6_hdr) + sizeof(struct net_udp_hdr); - } else if (NET_IPV6_HDR(orig)->nexthdr == IPPROTO_TCP) { - extra_len = sizeof(struct net_ipv6_hdr) + + } else if (ip_hdr->nexthdr == IPPROTO_TCP) { + copy_len = sizeof(struct net_ipv6_hdr) + sizeof(struct net_tcp_hdr); - } else if (NET_IPV6_HDR(orig)->nexthdr == NET_IPV6_NEXTHDR_FRAG) { - extra_len = net_pkt_get_len(orig); } else { - size_t space = CONFIG_NET_BUF_DATA_SIZE; - - if (reserve > space) { - extra_len = 0; - } else { - extra_len = space - reserve; - } + copy_len = net_pkt_get_len(orig); } - /* We only copy minimal IPv6 + next header from original message. - * This is so that the memory pressure is minimized. - */ - frag = net_pkt_copy(orig, extra_len, reserve, PKT_WAIT_TIME); - if (!frag) { + pkt = net_pkt_alloc_with_buffer(net_pkt_iface(orig), + copy_len + NET_ICMPV6_UNUSED_LEN, + AF_INET6, IPPROTO_ICMPV6, + PKT_WAIT_TIME); + if (!pkt) { err = -ENOMEM; - goto drop; + goto drop_no_pkt; } - net_pkt_frag_add(pkt, frag); - net_pkt_set_family(pkt, AF_INET6); - net_pkt_set_iface(pkt, iface); - net_pkt_set_ipv6_ext_len(pkt, 0); + if (net_ipv6_is_addr_mcast(&ip_hdr->dst)) { + src = net_if_ipv6_select_src_addr(net_pkt_iface(pkt), + &ip_hdr->dst); + } else { + src = &ip_hdr->dst; + } - setup_ipv6_header(pkt, extra_len, net_if_ipv6_get_hop_limit(iface), - type, code); + if (net_ipv6_create_new(pkt, src, &ip_hdr->src) || + net_icmpv6_create(pkt, type, code)) { + goto drop; + } /* Depending on error option, we store the param into the ICMP message. */ if (type == NET_ICMPV6_PARAM_PROBLEM) { - sys_put_be32(param, (u8_t *)net_pkt_icmp_data(pkt) + - sizeof(struct net_icmp_hdr)); + err = net_pkt_write_be32_new(pkt, param); + } else { + err = net_pkt_memset(pkt, 0, NET_ICMPV6_UNUSED_LEN); } - if (net_ipv6_is_addr_mcast(&NET_IPV6_HDR(orig)->dst)) { - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, - &NET_IPV6_HDR(orig)->src); - - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, - net_if_ipv6_select_src_addr(iface, - &NET_IPV6_HDR(orig)->dst)); - } else { - struct in6_addr addr; + /* Allocator might not have been able to allocate all requested space, + * so let's copy as much as we can. + */ + copy_len = net_pkt_available_buffer(pkt); - net_ipaddr_copy(&addr, &NET_IPV6_HDR(orig)->src); - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, - &NET_IPV6_HDR(orig)->dst); - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, &addr); + if (err || net_pkt_copy_new(pkt, orig, copy_len)) { + goto drop; } net_pkt_lladdr_src(pkt)->addr = net_pkt_lladdr_dst(orig)->addr; @@ -487,19 +317,16 @@ int net_icmpv6_send_error(struct net_pkt *orig, u8_t type, u8_t code, net_pkt_lladdr_dst(pkt)->addr = net_pkt_lladdr_src(orig)->addr; net_pkt_lladdr_dst(pkt)->len = net_pkt_lladdr_src(orig)->len; - /* Clear and then set the chksum */ - err = net_icmpv6_set_chksum(pkt); - if (err < 0) { - goto drop; - } + net_pkt_cursor_init(pkt); + net_ipv6_finalize_new(pkt, IPPROTO_ICMPV6); NET_DBG("Sending ICMPv6 Error Message type %d code %d param %d" " from %s to %s", type, code, param, - log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->src)), - log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->dst))); + log_strdup(net_sprint_ipv6_addr(src)), + log_strdup(net_sprint_ipv6_addr(&ip_hdr->src))); if (net_send_data(pkt) >= 0) { - net_stats_update_icmp_sent(iface); + net_stats_update_icmp_sent(net_pkt_iface(pkt)); return 0; } @@ -507,102 +334,103 @@ int net_icmpv6_send_error(struct net_pkt *orig, u8_t type, u8_t code, net_pkt_unref(pkt); drop_no_pkt: - net_stats_update_icmp_drop(iface); + net_stats_update_icmp_drop(net_pkt_iface(orig)); return err; } -#define append(pkt, type, value) \ - do { \ - if (!net_pkt_append_##type##_timeout(pkt, value, \ - PKT_WAIT_TIME)) { \ - ret = -ENOMEM; \ - goto drop; \ - } \ - } while (0) - int net_icmpv6_send_echo_request(struct net_if *iface, struct in6_addr *dst, u16_t identifier, u16_t sequence) { + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmpv6_access, + struct net_icmpv6_echo_req); + int ret = -ENOBUFS; + struct net_icmpv6_echo_req *echo_req; const struct in6_addr *src; struct net_pkt *pkt; - int ret; src = net_if_ipv6_select_src_addr(iface, dst); - pkt = net_pkt_get_reserve_tx(PKT_WAIT_TIME); + pkt = net_pkt_alloc_with_buffer(iface, + sizeof(struct net_icmpv6_echo_req), + AF_INET6, IPPROTO_ICMPV6, + PKT_WAIT_TIME); if (!pkt) { return -ENOMEM; } - if (!net_ipv6_create(pkt, src, dst, iface, IPPROTO_ICMPV6)) { - ret = -ENOMEM; + if (net_ipv6_create_new(pkt, src, dst) || + net_icmpv6_create(pkt, NET_ICMPV6_ECHO_REQUEST, 0)) { goto drop; } - net_pkt_set_family(pkt, AF_INET6); - net_pkt_set_iface(pkt, iface); + echo_req = (struct net_icmpv6_echo_req *)net_pkt_get_data_new( + pkt, &icmpv6_access); + if (!echo_req) { + goto drop; + } - append(pkt, u8, NET_ICMPV6_ECHO_REQUEST); - append(pkt, u8, 0); /* code */ - append(pkt, be16, 0); /* checksum */ - append(pkt, be16, identifier); - append(pkt, be16, sequence); + echo_req->identifier = htons(identifier); + echo_req->sequence = htons(sequence); - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, src); - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, dst); + net_pkt_set_data(pkt, &icmpv6_access); - if (net_ipv6_finalize(pkt, IPPROTO_ICMPV6) < 0) { - ret = -ENOMEM; - goto drop; - } + net_ipv6_finalize_new(pkt, IPPROTO_ICMPV6); NET_DBG("Sending ICMPv6 Echo Request type %d from %s to %s", NET_ICMPV6_ECHO_REQUEST, - log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->src)), - log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->dst))); + log_strdup(net_sprint_ipv6_addr(src)), + log_strdup(net_sprint_ipv6_addr(dst))); if (net_send_data(pkt) >= 0) { net_stats_update_icmp_sent(iface); return 0; } + net_stats_update_icmp_drop(iface); + ret = -EIO; drop: net_pkt_unref(pkt); - net_stats_update_icmp_drop(iface); return ret; } -enum net_verdict net_icmpv6_input(struct net_pkt *pkt) +enum net_verdict net_icmpv6_input(struct net_pkt *pkt, + struct net_ipv6_hdr *ip_hdr) { + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmp_access, + struct net_icmp_hdr); + struct net_icmp_hdr *icmp_hdr; struct net_icmpv6_handler *cb; - struct net_icmp_hdr icmp_hdr; + + icmp_hdr = (struct net_icmp_hdr *)net_pkt_get_data_new(pkt, + &icmp_access); + if (!icmp_hdr) { + NET_DBG("DROP: NULL ICMPv6 header"); + return NET_DROP; + } if (net_calc_chksum_icmpv6(pkt) != 0) { NET_DBG("DROP: invalid checksum"); goto drop; } - if (net_icmpv6_get_hdr(pkt, &icmp_hdr)) { - NET_DBG("DROP: NULL ICMPv6 header"); - goto drop; - } + net_pkt_acknowledge_data(pkt, &icmp_access); NET_DBG("ICMPv6 %s received type %d code %d", - net_icmpv6_type2str(icmp_hdr.type), - icmp_hdr.type, icmp_hdr.code); + net_icmpv6_type2str(icmp_hdr->type), + icmp_hdr->type, icmp_hdr->code); net_stats_update_icmp_recv(net_pkt_iface(pkt)); SYS_SLIST_FOR_EACH_CONTAINER(&handlers, cb, node) { - if (cb->type == icmp_hdr.type && - (cb->code == icmp_hdr.code || cb->code == 0)) { - return cb->handler(pkt); + if (cb->type == icmp_hdr->type && + (cb->code == icmp_hdr->code || cb->code == 0)) { + return cb->handler(pkt, ip_hdr, icmp_hdr); } } drop: @@ -614,7 +442,7 @@ enum net_verdict net_icmpv6_input(struct net_pkt *pkt) static struct net_icmpv6_handler echo_request_handler = { .type = NET_ICMPV6_ECHO_REQUEST, .code = 0, - .handler = handle_echo_request, + .handler = icmpv6_handle_echo_request, }; void net_icmpv6_init(void) diff --git a/subsys/net/ip/icmpv6.h b/subsys/net/ip/icmpv6.h index 155e3a92700f7..9a0f4cb8eda9f 100644 --- a/subsys/net/ip/icmpv6.h +++ b/subsys/net/ip/icmpv6.h @@ -48,15 +48,11 @@ struct net_icmpv6_ra_hdr { } __packed; struct net_icmpv6_nd_opt_mtu { - u8_t type; - u8_t len; u16_t reserved; u32_t mtu; } __packed; struct net_icmpv6_nd_opt_prefix_info { - u8_t type; - u8_t len; u8_t prefix_len; u8_t flags; u32_t valid_lifetime; @@ -66,8 +62,6 @@ struct net_icmpv6_nd_opt_prefix_info { } __packed; struct net_icmpv6_nd_opt_6co { - u8_t type; - u8_t len; u8_t context_len; u8_t flag; /*res:3,c:1,cid:4 */ u16_t reserved; @@ -75,6 +69,27 @@ struct net_icmpv6_nd_opt_6co { struct in6_addr prefix; } __packed; +struct net_icmpv6_echo_req { + u16_t identifier; + u16_t sequence; +} __packed; + +struct net_icmpv6_mld_query { + u16_t max_response_code; + u16_t reserved; + struct in6_addr mcast_address; + u16_t flagg; /*S, QRV & QQIC */ + u16_t num_sources; +} __packed; + +struct net_icmpv6_mld_mcast_record { + u8_t record_type; + u8_t aux_data_len; + u16_t num_sources; + struct in6_addr mcast_address; +} __packed; + + #define NET_ICMPV6_ND_O_FLAG(flag) ((flag) & 0x40) #define NET_ICMPV6_ND_M_FLAG(flag) ((flag) & 0x80) @@ -127,7 +142,10 @@ struct net_icmpv6_nd_opt_6co { /* ICMPv6 header has 4 unused bytes that must be zero, RFC 4443 ch 3.1 */ #define NET_ICMPV6_UNUSED_LEN 4 -typedef enum net_verdict (*icmpv6_callback_handler_t)(struct net_pkt *pkt); +typedef enum net_verdict (*icmpv6_callback_handler_t)( + struct net_pkt *pkt, + struct net_ipv6_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr); const char *net_icmpv6_type2str(int icmpv6_type); @@ -170,23 +188,12 @@ int net_icmpv6_send_echo_request(struct net_if *iface, void net_icmpv6_register_handler(struct net_icmpv6_handler *handler); void net_icmpv6_unregister_handler(struct net_icmpv6_handler *handler); -enum net_verdict net_icmpv6_input(struct net_pkt *pkt); - -int net_icmpv6_get_hdr(struct net_pkt *pkt, struct net_icmp_hdr *hdr); -int net_icmpv6_set_hdr(struct net_pkt *pkt, struct net_icmp_hdr *hdr); +enum net_verdict net_icmpv6_input(struct net_pkt *pkt, + struct net_ipv6_hdr *ip_hdr); int net_icmpv6_set_chksum(struct net_pkt *pkt); - -int net_icmpv6_get_ns_hdr(struct net_pkt *pkt, struct net_icmpv6_ns_hdr *hdr); -int net_icmpv6_set_ns_hdr(struct net_pkt *pkt, struct net_icmpv6_ns_hdr *hdr); - -int net_icmpv6_get_nd_opt_hdr(struct net_pkt *pkt, - struct net_icmpv6_nd_opt_hdr *hdr); - -int net_icmpv6_get_na_hdr(struct net_pkt *pkt, struct net_icmpv6_na_hdr *hdr); -int net_icmpv6_set_na_hdr(struct net_pkt *pkt, struct net_icmpv6_na_hdr *hdr); - -int net_icmpv6_get_ra_hdr(struct net_pkt *pkt, struct net_icmpv6_ra_hdr *hdr); +int net_icmpv6_create(struct net_pkt *pkt, u8_t icmp_type, u8_t icmp_code); +int net_icmpv6_finalize(struct net_pkt *pkt); #if defined(CONFIG_NET_IPV6) void net_icmpv6_init(void); diff --git a/subsys/net/ip/ipv4.c b/subsys/net/ip/ipv4.c index efdee74efaab4..55385e9c23fea 100644 --- a/subsys/net/ip/ipv4.c +++ b/subsys/net/ip/ipv4.c @@ -96,6 +96,81 @@ void net_ipv4_finalize(struct net_pkt *pkt, u8_t next_header_proto) } } +int net_ipv4_create_new(struct net_pkt *pkt, + const struct in_addr *src, + const struct in_addr *dst) +{ + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv4_access, struct net_ipv4_hdr); + struct net_ipv4_hdr *ipv4_hdr; + + ipv4_hdr = (struct net_ipv4_hdr *)net_pkt_get_data_new(pkt, + &ipv4_access); + if (!ipv4_hdr) { + return -ENOBUFS; + } + + ipv4_hdr->vhl = 0x45; + ipv4_hdr->tos = 0x00; + ipv4_hdr->len = 0; + ipv4_hdr->id[0] = 0; + ipv4_hdr->id[1] = 0; + ipv4_hdr->offset[0] = 0; + ipv4_hdr->offset[1] = 0; + + ipv4_hdr->ttl = net_pkt_ipv4_ttl(pkt); + if (ipv4_hdr->ttl == 0) { + ipv4_hdr->ttl = net_if_ipv4_get_ttl(net_pkt_iface(pkt)); + } + + ipv4_hdr->proto = 0; + ipv4_hdr->chksum = 0; + + net_ipaddr_copy(&ipv4_hdr->dst, dst); + net_ipaddr_copy(&ipv4_hdr->src, src); + + net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv4_hdr)); + + return net_pkt_set_data(pkt, &ipv4_access); +} + +int net_ipv4_finalize_new(struct net_pkt *pkt, u8_t next_header_proto) +{ + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv4_access, struct net_ipv4_hdr); + struct net_ipv4_hdr *ipv4_hdr; + + net_pkt_set_overwrite(pkt, true); + + ipv4_hdr = (struct net_ipv4_hdr *)net_pkt_get_data_new(pkt, + &ipv4_access); + if (!ipv4_hdr) { + return -ENOBUFS; + } + + ipv4_hdr->len = htons(net_pkt_get_len(pkt)); + ipv4_hdr->proto = next_header_proto; + + if (net_if_need_calc_tx_checksum(net_pkt_iface(pkt)) || + next_header_proto == IPPROTO_ICMP) { + ipv4_hdr->chksum = net_calc_chksum_ipv4(pkt); + + net_pkt_set_data(pkt, &ipv4_access); + + if (IS_ENABLED(CONFIG_NET_UDP) && + next_header_proto == IPPROTO_UDP) { + return net_udp_finalize(pkt); + } else if (IS_ENABLED(CONFIG_NET_TCP) && + next_header_proto == IPPROTO_TCP) { + net_tcp_finalize(pkt); + } else if (next_header_proto == IPPROTO_ICMP) { + net_icmpv4_finalize(pkt); + } + } else { + net_pkt_set_data(pkt, &ipv4_access); + } + + return 0; +} + const struct in_addr *net_ipv4_unspecified_address(void) { static const struct in_addr addr; @@ -110,19 +185,33 @@ const struct in_addr *net_ipv4_broadcast_address(void) return &addr; } -enum net_verdict net_ipv4_process_pkt(struct net_pkt *pkt) +enum net_verdict net_ipv4_input(struct net_pkt *pkt) { - struct net_ipv4_hdr *hdr = NET_IPV4_HDR(pkt); + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv4_access, struct net_ipv4_hdr); + NET_PKT_DATA_ACCESS_DEFINE(udp_access, struct net_udp_hdr); + NET_PKT_DATA_ACCESS_DEFINE(tcp_access, struct net_tcp_hdr); int real_len = net_pkt_get_len(pkt); - int pkt_len = ntohs(hdr->len); enum net_verdict verdict = NET_DROP; + union net_proto_header proto_hdr; + struct net_ipv4_hdr *hdr; + union net_ip_header ip; + int pkt_len; + + net_stats_update_ipv4_recv(net_pkt_iface(pkt)); + + hdr = (struct net_ipv4_hdr *)net_pkt_get_data_new(pkt, &ipv4_access); + if (!hdr) { + NET_DBG("DROP: no buffer"); + goto drop; + } + pkt_len = ntohs(hdr->len); if (real_len < pkt_len) { NET_DBG("DROP: pkt len per hdr %d != pkt real len %d", pkt_len, real_len); goto drop; } else if (real_len > pkt_len) { - net_pkt_pull(pkt, pkt_len, real_len - pkt_len); + net_pkt_update_length(pkt, pkt_len); } if (net_ipv4_is_addr_mcast(&hdr->src)) { @@ -161,23 +250,40 @@ enum net_verdict net_ipv4_process_pkt(struct net_pkt *pkt) net_pkt_set_transport_proto(pkt, hdr->proto); + net_pkt_set_family(pkt, PF_INET); + + net_pkt_acknowledge_data(pkt, &ipv4_access); + switch (hdr->proto) { case IPPROTO_ICMP: - verdict = net_icmpv4_input( - pkt, net_ipv4_is_addr_bcast(net_pkt_iface(pkt), - &hdr->dst)); + verdict = net_icmpv4_input(pkt, hdr); break; case IPPROTO_TCP: - /* Fall through */ + proto_hdr.tcp = net_tcp_input(pkt, &tcp_access); + if (proto_hdr.tcp) { + verdict = NET_OK; + } + break; case IPPROTO_UDP: - verdict = net_conn_input(hdr->proto, pkt); + proto_hdr.udp = net_udp_input(pkt, &udp_access); + if (proto_hdr.udp) { + verdict = NET_OK; + } break; } - if (verdict != NET_DROP) { + if (verdict == NET_DROP) { + goto drop; + } else if (hdr->proto == IPPROTO_ICMP) { return verdict; } + ip.ipv4 = hdr; + + verdict = net_conn_input(pkt, &ip, hdr->proto, &proto_hdr); + if (verdict != NET_DROP) { + return verdict; + } drop: net_stats_update_ipv4_drop(net_pkt_iface(pkt)); return NET_DROP; diff --git a/subsys/net/ip/ipv4.h b/subsys/net/ip/ipv4.h index d38552d84f040..e0db151a69195 100644 --- a/subsys/net/ip/ipv4.h +++ b/subsys/net/ip/ipv4.h @@ -52,4 +52,31 @@ struct net_pkt *net_ipv4_create(struct net_pkt *pkt, */ void net_ipv4_finalize(struct net_pkt *pkt, u8_t next_header_proto); + +/** + * @brief Create IPv4 packet in provided net_pkt. + * + * @param pkt Network packet + * @param src Source IPv4 address + * @param dst Destination IPv4 address + * + * @return 0 on success, negative errno otherwise. + */ +int net_ipv4_create_new(struct net_pkt *pkt, + const struct in_addr *src, + const struct in_addr *dst); + +/** + * @brief Finalize IPv4 packet. It should be called right before + * sending the packet and after all the data has been added into + * the packet. This function will set the length of the + * packet and calculate the higher protocol checksum if needed. + * + * @param pkt Network packet + * @param next_header_proto Protocol type of the next header after IPv4 header. + * + * @return 0 on success, negative errno otherwise. + */ +int net_ipv4_finalize_new(struct net_pkt *pkt, u8_t next_header_proto); + #endif /* __IPV4_H */ diff --git a/subsys/net/ip/ipv4_autoconf.c b/subsys/net/ip/ipv4_autoconf.c index 789d93f642e14..e7e5933443ed9 100644 --- a/subsys/net/ip/ipv4_autoconf.c +++ b/subsys/net/ip/ipv4_autoconf.c @@ -33,32 +33,21 @@ static struct net_pkt *ipv4_autoconf_prepare_arp(struct net_if *iface) { struct net_if_config *cfg = net_if_get_config(iface); struct net_pkt *pkt; - struct net_buf *frag; - pkt = net_pkt_get_reserve_tx(BUF_ALLOC_TIMEOUT); + /* We provide AF_UNSPEC to the allocator: this packet does not + * need space for any IPv4 header. + */ + pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct net_arp_hdr), + AF_UNSPEC, 0, BUF_ALLOC_TIMEOUT); if (!pkt) { - goto fail; + return NULL; } - frag = net_pkt_get_frag(pkt, BUF_ALLOC_TIMEOUT); - if (!frag) { - goto fail; - } - - net_pkt_frag_add(pkt, frag); - net_pkt_set_iface(pkt, iface); net_pkt_set_family(pkt, AF_INET); net_pkt_set_ipv4_auto(pkt, true); return net_arp_prepare(pkt, &cfg->ipv4auto.requested_ip, &cfg->ipv4auto.current_ip); - -fail: - if (pkt) { - net_pkt_unref(pkt); - } - - return NULL; } static void ipv4_autoconf_send_probe(struct net_if_ipv4_autoconf *ipv4auto) diff --git a/subsys/net/ip/ipv6.c b/subsys/net/ip/ipv6.c index 20998b419ac5a..8e22cbb3d910c 100644 --- a/subsys/net/ip/ipv6.c +++ b/subsys/net/ip/ipv6.c @@ -135,9 +135,90 @@ int net_ipv6_finalize(struct net_pkt *pkt, u8_t next_header_proto) return 0; } -static inline struct net_pkt *check_unknown_option(struct net_pkt *pkt, - u8_t opt_type, - u16_t length) +int net_ipv6_create_new(struct net_pkt *pkt, + const struct in6_addr *src, + const struct in6_addr *dst) +{ + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr); + struct net_ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct net_ipv6_hdr *)net_pkt_get_data_new(pkt, + &ipv6_access); + if (!ipv6_hdr) { + return -ENOBUFS; + } + + ipv6_hdr->vtc = 0x60; + ipv6_hdr->tcflow = 0; + ipv6_hdr->flow = 0; + ipv6_hdr->len = 0; + ipv6_hdr->nexthdr = 0; + + /* User can tweak the default hop limit if needed */ + ipv6_hdr->hop_limit = net_pkt_ipv6_hop_limit(pkt); + if (ipv6_hdr->hop_limit == 0) { + ipv6_hdr->hop_limit = + net_if_ipv6_get_hop_limit(net_pkt_iface(pkt)); + } + + net_ipaddr_copy(&ipv6_hdr->dst, dst); + net_ipaddr_copy(&ipv6_hdr->src, src); + + net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr)); + net_pkt_set_ipv6_ext_len(pkt, 0); + + return net_pkt_set_data(pkt, &ipv6_access); +} + +int net_ipv6_finalize_new(struct net_pkt *pkt, u8_t next_header_proto) +{ + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr); + struct net_ipv6_hdr *ipv6_hdr; + + net_pkt_set_overwrite(pkt, true); + + ipv6_hdr = (struct net_ipv6_hdr *)net_pkt_get_data_new(pkt, + &ipv6_access); + if (!ipv6_hdr) { + return -ENOBUFS; + } + + ipv6_hdr->len = htons(net_pkt_get_len(pkt) - + sizeof(struct net_ipv6_hdr)); + + if (net_pkt_ipv6_next_hdr(pkt) != 255) { + ipv6_hdr->nexthdr = net_pkt_ipv6_next_hdr(pkt); + } else { + ipv6_hdr->nexthdr = next_header_proto; + } + + net_pkt_set_data(pkt, &ipv6_access); + + if (net_pkt_ipv6_next_hdr(pkt) != 255 && + net_pkt_skip(pkt, net_pkt_ipv6_ext_len(pkt))) { + return -ENOBUFS; + } + + if (net_if_need_calc_tx_checksum(net_pkt_iface(pkt)) || + next_header_proto == IPPROTO_ICMPV6) { + if (IS_ENABLED(CONFIG_NET_UDP) && + next_header_proto == IPPROTO_UDP) { + net_udp_finalize(pkt); + } else if (IS_ENABLED(CONFIG_NET_TCP) && + next_header_proto == IPPROTO_TCP) { + net_tcp_finalize(pkt); + } else if (next_header_proto == IPPROTO_ICMPV6) { + return net_icmpv6_finalize(pkt); + } + } + + return 0; +} + +static inline bool ipv6_drop_on_unknown_option(struct net_pkt *pkt, + struct net_ipv6_hdr *hdr, + u8_t opt_type, + u16_t length) { /* RFC 2460 chapter 4.2 tells how to handle the unknown * options by the two highest order bits of the option: @@ -153,63 +234,64 @@ static inline struct net_pkt *check_unknown_option(struct net_pkt *pkt, * Problem, Code 2, message to the packet's Source Address, * pointing to the unrecognized Option Type. */ - NET_DBG("Unknown option %d (0x%02x) MSB %d", opt_type, opt_type, - opt_type >> 6); + NET_DBG("Unknown option %d (0x%02x) MSB %d - 0x%02x", + opt_type, opt_type, opt_type >> 6, opt_type & 0xc0); switch (opt_type & 0xc0) { case 0x00: - break; + return false; case 0x40: - return NULL; + break; case 0xc0: - if (net_ipv6_is_addr_mcast(&NET_IPV6_HDR(pkt)->dst)) { - return NULL; + if (net_ipv6_is_addr_mcast(&hdr->dst)) { + break; } + /* passthrough */ case 0x80: net_icmpv6_send_error(pkt, NET_ICMPV6_PARAM_PROBLEM, NET_ICMPV6_PARAM_PROB_OPTION, (u32_t)length); - return NULL; + break; } - return pkt; + return true; } -static inline struct net_buf *handle_ext_hdr_options(struct net_pkt *pkt, - struct net_buf *frag, - int total_len, - u16_t len, - u16_t offset, - u16_t *pos, - enum net_verdict *verdict) +static inline int ipv6_handle_ext_hdr_options(struct net_pkt *pkt, + struct net_ipv6_hdr *hdr, + u16_t pkt_len) { - u8_t opt_type, opt_len; - u16_t length = 0U, loc; + u16_t exthdr_len = 0U; + u16_t length = 0U; - if (len > total_len) { + if (net_pkt_read_u8_new(pkt, (u8_t *)&exthdr_len)) { + return -ENOBUFS; + } + + exthdr_len = exthdr_len * 8 + 8; + if (exthdr_len > pkt_len) { NET_DBG("Corrupted packet, extension header %d too long " - "(max %d bytes)", len, total_len); - *verdict = NET_DROP; - return NULL; + "(max %d bytes)", exthdr_len, pkt_len); + return -EINVAL; } length += 2; - /* Each extension option has type and length */ - frag = net_frag_read_u8(frag, offset, &loc, &opt_type); - if (!frag && loc == 0xffff) { - goto drop; - } + while (length < exthdr_len) { + u8_t opt_type, opt_len; - if (opt_type != NET_IPV6_EXT_HDR_OPT_PAD1) { - frag = net_frag_read_u8(frag, loc, &loc, &opt_len); - if (!frag && loc == 0xffff) { - goto drop; + /* Each extension option has type and length */ + if (net_pkt_read_u8_new(pkt, &opt_type)) { + return -ENOBUFS; + } + + if (opt_type != NET_IPV6_EXT_HDR_OPT_PAD1) { + if (net_pkt_read_u8_new(pkt, &opt_len)) { + return -ENOBUFS; + } } - } - while (frag && (length < len)) { switch (opt_type) { case NET_IPV6_EXT_HDR_OPT_PAD1: length++; @@ -217,58 +299,25 @@ static inline struct net_buf *handle_ext_hdr_options(struct net_pkt *pkt, case NET_IPV6_EXT_HDR_OPT_PADN: NET_DBG("PADN option"); length += opt_len + 2; - loc += opt_len + 2; + break; default: - if (!check_unknown_option(pkt, opt_type, length)) { - goto drop; + if (ipv6_drop_on_unknown_option(pkt, hdr, + opt_type, length)) { + return -ENOTSUP; } - length += opt_len + 2; - - /* No need to +2 here as loc already contains option - * header len. - */ - loc += opt_len; + if (net_pkt_skip(pkt, opt_len)) { + return -ENOBUFS; + } - break; - } + length += opt_len + 2; - if (length >= len) { break; } - - frag = net_frag_read_u8(frag, loc, &loc, &opt_type); - if (!frag && loc == 0xffff) { - goto drop; - } - - if (opt_type != NET_IPV6_EXT_HDR_OPT_PAD1) { - frag = net_frag_read_u8(frag, loc, &loc, &opt_len); - if (!frag && loc == 0xffff) { - goto drop; - } - } } - if (length != len) { - goto drop; - } - - *pos = loc; - - *verdict = NET_CONTINUE; - return frag; - -drop: - *verdict = NET_DROP; - return NULL; -} - -static inline bool is_upper_layer_protocol_header(u8_t proto) -{ - return (proto == IPPROTO_ICMPV6 || proto == IPPROTO_UDP || - proto == IPPROTO_TCP); + return exthdr_len; } #if defined(CONFIG_NET_ROUTE) @@ -292,9 +341,9 @@ static struct net_route_entry *add_route(struct net_if *iface, } #endif /* CONFIG_NET_ROUTE */ -static void no_route_info(struct net_pkt *pkt, - struct in6_addr *src, - struct in6_addr *dst) +static void ipv6_no_route_info(struct net_pkt *pkt, + struct in6_addr *src, + struct in6_addr *dst) { NET_DBG("Will not route pkt %p ll src %s to dst %s between interfaces", pkt, log_strdup(net_sprint_ipv6_addr(src)), @@ -302,7 +351,7 @@ static void no_route_info(struct net_pkt *pkt, } #if defined(CONFIG_NET_ROUTE) -static enum net_verdict route_ipv6_packet(struct net_pkt *pkt, +static enum net_verdict ipv6_route_packet(struct net_pkt *pkt, struct net_ipv6_hdr *hdr) { struct net_route_entry *route; @@ -325,7 +374,7 @@ static enum net_verdict route_ipv6_packet(struct net_pkt *pkt, (net_ipv6_is_ll_addr(&hdr->src) || net_ipv6_is_ll_addr(&hdr->dst))) { /* RFC 4291 ch 2.5.6 */ - no_route_info(pkt, &hdr->src, &hdr->dst); + ipv6_no_route_info(pkt, &hdr->src, &hdr->dst); goto drop; } @@ -348,8 +397,7 @@ static enum net_verdict route_ipv6_packet(struct net_pkt *pkt, pkt, net_pkt_orig_iface(pkt), net_pkt_iface(pkt)); - add_route(net_pkt_orig_iface(pkt), - &NET_IPV6_HDR(pkt)->src, 128); + add_route(net_pkt_orig_iface(pkt), &hdr->src, 128); } ret = net_route_packet(pkt, nexthop); @@ -369,65 +417,81 @@ static enum net_verdict route_ipv6_packet(struct net_pkt *pkt, drop: return NET_DROP; } +#else +static inline enum net_verdict ipv6_route_packet(struct net_pkt *pkt, + struct net_ipv6_hdr *hdr) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(hdr); + + NET_DBG("DROP: Packet %p not for me", pkt); + + return NET_DROP; +} + #endif /* CONFIG_NET_ROUTE */ -enum net_verdict net_ipv6_process_pkt(struct net_pkt *pkt, bool is_loopback) +enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) { - struct net_ipv6_hdr *hdr = NET_IPV6_HDR(pkt); + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr); + NET_PKT_DATA_ACCESS_DEFINE(udp_access, struct net_udp_hdr); + NET_PKT_DATA_ACCESS_DEFINE(tcp_access, struct net_tcp_hdr); + enum net_verdict verdict = NET_DROP; int real_len = net_pkt_get_len(pkt); - int pkt_len = ntohs(hdr->len) + sizeof(*hdr); - struct net_buf *frag; - u8_t start_of_ext, prev_hdr; - u8_t next, next_hdr; - u8_t first_option; - u16_t offset; - u16_t length; - u16_t total_len = 0U; - u8_t ext_bitmap; + u8_t ext_bitmap = 0U; + u16_t ext_len = 0U; + u8_t nexthdr, next_nexthdr; + union net_proto_header proto_hdr; + struct net_ipv6_hdr *hdr; + union net_ip_header ip; + int pkt_len; + + net_stats_update_ipv6_recv(net_pkt_iface(pkt)); + + hdr = (struct net_ipv6_hdr *)net_pkt_get_data_new(pkt, &ipv6_access); + if (!hdr) { + NET_DBG("DROP: no buffer"); + goto drop; + } + pkt_len = ntohs(hdr->len) + sizeof(struct net_ipv6_hdr); if (real_len < pkt_len) { - NET_DBG("IPv6 packet size %d pkt len %d", pkt_len, real_len); - net_stats_update_ipv6_drop(net_pkt_iface(pkt)); + NET_DBG("DROP: pkt len per hdr %d != pkt real len %d", + pkt_len, real_len); goto drop; } else if (real_len > pkt_len) { - net_pkt_pull(pkt, pkt_len, real_len - pkt_len); - real_len = net_pkt_get_len(pkt); + net_pkt_update_length(pkt, pkt_len); } - NET_DBG("IPv6 packet len %d received from %s to %s", real_len, + NET_DBG("IPv6 packet len %d received from %s to %s", pkt_len, log_strdup(net_sprint_ipv6_addr(&hdr->src)), log_strdup(net_sprint_ipv6_addr(&hdr->dst))); - if (!is_loopback && (net_ipv6_is_addr_loopback(&hdr->dst) || - net_ipv6_is_addr_loopback(&hdr->src))) { - NET_DBG("Dropping ::1 packet"); - net_stats_update_ipv6_drop(net_pkt_iface(pkt)); - goto drop; - } - if (net_ipv6_is_addr_mcast(&hdr->src) || net_ipv6_is_addr_mcast_scope(&hdr->dst, 0)) { - NET_DBG("Dropping multicast packet"); - net_stats_update_ipv6_drop(net_pkt_iface(pkt)); + NET_DBG("DROP: multicast packet"); goto drop; } if (!is_loopback) { - bool is_empty_group = net_ipv6_is_addr_mcast_group( - &hdr->dst, net_ipv6_unspecified_address()); + if (net_ipv6_is_addr_loopback(&hdr->dst) || + net_ipv6_is_addr_loopback(&hdr->src)) { + NET_DBG("DROP: ::1 packet"); + goto drop; + } if (net_ipv6_is_addr_mcast_iface(&hdr->dst) || - (is_empty_group && + (net_ipv6_is_addr_mcast_group( + &hdr->dst, net_ipv6_unspecified_address()) && (net_ipv6_is_addr_mcast_site(&hdr->dst) || net_ipv6_is_addr_mcast_org(&hdr->dst)))) { - NET_DBG("Dropping invalid scope multicast packet"); - net_stats_update_ipv6_drop(net_pkt_iface(pkt)); + NET_DBG("DROP: invalid scope multicast packet"); goto drop; } } /* Check extension headers */ - net_pkt_set_next_hdr(pkt, &hdr->nexthdr); + net_pkt_set_ipv6_next_hdr(pkt, hdr->nexthdr); net_pkt_set_ipv6_ext_len(pkt, 0); net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr)); net_pkt_set_ipv6_hop_limit(pkt, NET_IPV6_HDR(pkt)->hop_limit); @@ -435,18 +499,10 @@ enum net_verdict net_ipv6_process_pkt(struct net_pkt *pkt, bool is_loopback) if (!net_ipv6_is_my_addr(&hdr->dst) && !net_ipv6_is_my_maddr(&hdr->dst) && !net_ipv6_is_addr_mcast(&hdr->dst)) { -#if defined(CONFIG_NET_ROUTE) - enum net_verdict verdict; - - verdict = route_ipv6_packet(pkt, hdr); - if (verdict == NET_OK) { + if (ipv6_route_packet(pkt, hdr) == NET_OK) { return NET_OK; } -#else /* CONFIG_NET_ROUTE */ - NET_DBG("IPv6 packet in pkt %p not for me", pkt); -#endif /* CONFIG_NET_ROUTE */ - net_stats_update_ipv6_drop(net_pkt_iface(pkt)); goto drop; } @@ -459,155 +515,131 @@ enum net_verdict net_ipv6_process_pkt(struct net_pkt *pkt, bool is_loopback) !net_ipv6_is_addr_mcast(&hdr->dst) && !net_if_ipv6_addr_lookup_by_iface(net_pkt_iface(pkt), &hdr->dst)) { - no_route_info(pkt, &hdr->src, &hdr->dst); - - net_stats_update_ipv6_drop(net_pkt_iface(pkt)); + ipv6_no_route_info(pkt, &hdr->src, &hdr->dst); goto drop; } - /* Fast path for main upper layer protocols. The handling of extension - * headers can be slow so do this checking here. There cannot - * be any extension headers after the upper layer protocol header. - */ - next = *(net_pkt_next_hdr(pkt)); - if (is_upper_layer_protocol_header(next)) { - goto upper_proto; - } + net_pkt_acknowledge_data(pkt, &ipv6_access); - /* Go through the extensions */ - frag = pkt->frags; - next = hdr->nexthdr; - first_option = next; - length = 0U; - ext_bitmap = 0U; - start_of_ext = 0U; - offset = sizeof(struct net_ipv6_hdr); - prev_hdr = &NET_IPV6_HDR(pkt)->nexthdr - &NET_IPV6_HDR(pkt)->vtc; - - while (frag) { - enum net_verdict verdict; - - if (is_upper_layer_protocol_header(next)) { - NET_DBG("IPv6 next header %d", next); - goto upper_proto; - } + nexthdr = hdr->nexthdr; + while (!net_ipv6_is_nexthdr_upper_layer(nexthdr)) { + u16_t exthdr_len; - if (!start_of_ext) { - start_of_ext = offset; - } + NET_DBG("IPv6 next header %d", nexthdr); - frag = net_frag_read_u8(frag, offset, &offset, &next_hdr); - if (!frag) { + if (net_pkt_read_u8_new(pkt, &next_nexthdr)) { goto drop; } - verdict = NET_OK; - - NET_DBG("IPv6 next header %d", next); - - switch (next) { - case NET_IPV6_NEXTHDR_NONE: - /* There is nothing after this header (see RFC 2460, - * ch 4.7), so we can drop the packet now. - * This is not an error case so do not update drop - * statistics. - */ - goto drop; - - case NET_IPV6_NEXTHDR_DESTO: - frag = net_frag_read_u8(frag, offset, &offset, - (u8_t *)&length); - if (!frag) { - goto drop; - } - length = length * 8 + 8; - total_len += length; - - ext_bitmap |= NET_IPV6_NEXTHDR_DESTO; - - frag = handle_ext_hdr_options(pkt, frag, real_len, - length, offset, &offset, - &verdict); - break; - + switch (nexthdr) { case NET_IPV6_NEXTHDR_HBHO: if (ext_bitmap & NET_IPV6_EXT_HDR_BITMAP_HBHO) { - NET_ERR("Dropping packet with multiple HBHO"); + NET_ERR("DROP: multiple hop-by-hop"); goto drop; } - frag = net_frag_read_u8(frag, offset, &offset, - (u8_t *)&length); - if (!frag) { - goto drop; + /* HBH option needs to be the first one */ + if (nexthdr != hdr->nexthdr) { + goto bad_hdr; } - length = length * 8 + 8; - total_len += length; + ext_bitmap |= NET_IPV6_EXT_HDR_BITMAP_HBHO; + + break; - /* HBH option needs to be the first one */ - if (first_option != NET_IPV6_NEXTHDR_HBHO) { + case NET_IPV6_NEXTHDR_DESTO: + if (ext_bitmap & NET_IPV6_EXT_HDR_BITMAP_DESTO2) { + /* DESTO option cannot appear more than twice */ goto bad_hdr; } - ext_bitmap |= NET_IPV6_EXT_HDR_BITMAP_HBHO; + if (ext_bitmap & NET_IPV6_EXT_HDR_BITMAP_DESTO1) { + ext_bitmap |= NET_IPV6_EXT_HDR_BITMAP_DESTO2; + } else { + ext_bitmap |= NET_IPV6_EXT_HDR_BITMAP_DESTO1; + } - frag = handle_ext_hdr_options(pkt, frag, real_len, - length, offset, &offset, - &verdict); break; -#if defined(CONFIG_NET_IPV6_FRAGMENT) case NET_IPV6_NEXTHDR_FRAG: - net_pkt_set_ipv6_hdr_prev(pkt, prev_hdr); + if (IS_ENABLED(CONFIG_NET_IPV6_FRAGMENT)) { + net_pkt_set_ipv6_fragment_start( + pkt, net_pkt_get_current_offset(pkt)); + return net_ipv6_handle_fragment_hdr(pkt, hdr, + nexthdr); + } - net_pkt_set_ipv6_fragment_start(pkt, - sizeof(struct - net_ipv6_hdr) + - total_len); + goto bad_hdr; + + case NET_IPV6_NEXTHDR_NONE: + /* There is nothing after this header (see RFC 2460, + * ch 4.7), so we can drop the packet now. + * This is not an error case so do not update drop + * statistics. + */ + return NET_DROP; - total_len += 8; - return net_ipv6_handle_fragment_hdr(pkt, frag, real_len, - offset, &offset, - next_hdr); -#endif default: goto bad_hdr; } - if (verdict == NET_DROP) { + exthdr_len = ipv6_handle_ext_hdr_options(pkt, hdr, pkt_len); + if (exthdr_len < 0) { goto drop; } - prev_hdr = start_of_ext; - next = next_hdr; + ext_len += exthdr_len; + nexthdr = next_nexthdr; } -upper_proto: - - net_pkt_set_ipv6_ext_len(pkt, total_len); - net_pkt_set_transport_proto(pkt, next); + net_pkt_set_ipv6_ext_len(pkt, ext_len); + net_pkt_set_transport_proto(pkt, nexthdr); + net_pkt_set_family(pkt, PF_INET6); - switch (next) { + switch (nexthdr) { case IPPROTO_ICMPV6: - return net_icmpv6_input(pkt); - case IPPROTO_UDP: - /* Fall through */ + verdict = net_icmpv6_input(pkt, hdr); + break; case IPPROTO_TCP: - return net_conn_input(next, pkt); + proto_hdr.tcp = net_tcp_input(pkt, &tcp_access); + if (proto_hdr.tcp) { + verdict = NET_OK; + } + break; + case IPPROTO_UDP: + proto_hdr.udp = net_udp_input(pkt, &udp_access); + if (proto_hdr.udp) { + verdict = NET_OK; + } + break; + } + + if (verdict == NET_DROP) { + if (nexthdr == IPPROTO_ICMPV6) { + return verdict; + } + + goto drop; + } + + ip.ipv6 = hdr; + + verdict = net_conn_input(pkt, &ip, nexthdr, &proto_hdr); + if (verdict != NET_DROP) { + return verdict; } drop: + net_stats_update_ipv6_drop(net_pkt_iface(pkt)); return NET_DROP; bad_hdr: - /* Send error message about parameter problem (RFC 2460) - */ + /* Send error message about parameter problem (RFC 2460) */ net_icmpv6_send_error(pkt, NET_ICMPV6_PARAM_PROBLEM, NET_ICMPV6_PARAM_PROB_NEXTHEADER, - offset - 1); + net_pkt_get_current_offset(pkt) - 1); - NET_DBG("Unknown next header type"); + NET_DBG("DROP: Unknown/wrong nexthdr type"); net_stats_update_ip_errors_protoerr(net_pkt_iface(pkt)); return NET_DROP; diff --git a/subsys/net/ip/ipv6.h b/subsys/net/ip/ipv6.h index 7ae6901ab5be0..9aa21e479e1e9 100644 --- a/subsys/net/ip/ipv6.h +++ b/subsys/net/ip/ipv6.h @@ -118,8 +118,8 @@ int net_ipv6_start_dad(struct net_if *iface, struct net_if_addr *ifaddr); #endif int net_ipv6_send_ns(struct net_if *iface, struct net_pkt *pending, - struct in6_addr *src, struct in6_addr *dst, - struct in6_addr *tgt, bool is_my_address); + const struct in6_addr *src, const struct in6_addr *dst, + const struct in6_addr *tgt, bool is_my_address); int net_ipv6_send_rs(struct net_if *iface); int net_ipv6_start_rs(struct net_if *iface); @@ -128,6 +128,13 @@ int net_ipv6_send_na(struct net_if *iface, const struct in6_addr *src, const struct in6_addr *dst, const struct in6_addr *tgt, u8_t flags); + +static inline bool net_ipv6_is_nexthdr_upper_layer(u8_t nexthdr) +{ + return (nexthdr == IPPROTO_ICMPV6 || nexthdr == IPPROTO_UDP || + nexthdr == IPPROTO_TCP); +} + /** * @brief Create IPv6 packet in provided net_pkt. * @@ -159,6 +166,34 @@ struct net_pkt *net_ipv6_create(struct net_pkt *pkt, */ int net_ipv6_finalize(struct net_pkt *pkt, u8_t next_header_proto); + +/** + * @brief Create IPv6 packet in provided net_pkt. + * + * @param pkt Network packet + * @param src Source IPv6 address + * @param dst Destination IPv6 address + * + * @return 0 on success, negative errno otherwise. + */ +int net_ipv6_create_new(struct net_pkt *pkt, + const struct in6_addr *src, + const struct in6_addr *dst); + +/** + * @brief Finalize IPv6 packet. It should be called right before + * sending the packet and after all the data has been added into + * the packet. This function will set the length of the + * packet and calculate the higher protocol checksum if needed. + * + * @param pkt Network packet + * @param next_header_proto Protocol type of the next header after IPv6 header. + * + * @return 0 on success, negative errno otherwise. + */ +int net_ipv6_finalize_new(struct net_pkt *pkt, u8_t next_header_proto); + + #if defined(CONFIG_NET_IPV6_MLD) /** * @brief Join a given multicast group. @@ -204,9 +239,9 @@ typedef void (*net_nbr_cb_t)(struct net_nbr *nbr, void *user_data); * * @param pkt Network packet * - * @return Return network packet to be sent. + * @return Return a verdict. */ -struct net_pkt *net_ipv6_prepare_for_send(struct net_pkt *pkt); +enum net_verdict net_ipv6_prepare_for_send(struct net_pkt *pkt); /** * @brief Look for a neighbor from it's address on an iface @@ -282,9 +317,9 @@ bool net_ipv6_nbr_rm(struct net_if *iface, struct in6_addr *addr); void net_ipv6_nbr_foreach(net_nbr_cb_t cb, void *user_data); #else /* CONFIG_NET_IPV6_NBR_CACHE */ -static inline struct net_pkt *net_ipv6_prepare_for_send(struct net_pkt *pkt) +static inline enum net_verdict net_ipv6_prepare_for_send(struct net_pkt *pkt) { - return pkt; + return NET_OK; } static inline struct net_nbr *net_ipv6_nbr_lookup(struct net_if *iface, @@ -389,34 +424,40 @@ 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 + * @param next_hdr_off Offset of the 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. + * @param last_hdr_off Offset of the last header field in the packet. * This is returned to caller. * - * @return Return 0 if ok or <0 if the packet is malformed. + * @return 0 on success, a negative errno otherwise. */ -int net_ipv6_find_last_ext_hdr(struct net_pkt *pkt, u16_t *next_hdr_idx, - u16_t *last_hdr_idx); +int net_ipv6_find_last_ext_hdr(struct net_pkt *pkt, u16_t *next_hdr_off, + u16_t *last_hdr_off); /** * @brief Handles IPv6 fragmented packets. * - * @param pkt Network head packet. - * @param frag Network packet fragment - * @param total_len Total length of the packet - * @param offset Start of fragment header - * @param loc End of fragment header, this is returned to the caller + * @param pkt Network head packet. + * @param hdr The IPv6 header of the current packet * @param nexthdr IPv6 next header after fragment header part * * @return Return verdict about the packet */ enum net_verdict net_ipv6_handle_fragment_hdr(struct net_pkt *pkt, - struct net_buf *frag, - int total_len, - u16_t buf_offset, - u16_t *loc, + struct net_ipv6_hdr *hdr, u8_t nexthdr); +#else +static inline +enum net_verdict net_ipv6_handle_fragment_hdr(struct net_pkt *pkt, + struct net_ipv6_hdr *hdr, + u8_t nexthdr) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(hdr); + ARG_UNUSED(nexthdr); + + return NET_DROP; +} #endif /* CONFIG_NET_IPV6_FRAGMENT */ #if defined(CONFIG_NET_IPV6) diff --git a/subsys/net/ip/ipv6_fragment.c b/subsys/net/ip/ipv6_fragment.c index 44ef01103d647..8d8634d67f62b 100644 --- a/subsys/net/ip/ipv6_fragment.c +++ b/subsys/net/ip/ipv6_fragment.c @@ -46,133 +46,80 @@ static bool reassembly_init_done; static struct net_ipv6_reassembly reassembly[CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT]; -int net_ipv6_find_last_ext_hdr(struct net_pkt *pkt, u16_t *next_hdr_idx, - u16_t *last_hdr_idx) +int net_ipv6_find_last_ext_hdr(struct net_pkt *pkt, u16_t *next_hdr_off, + u16_t *last_hdr_off) { - struct net_buf *next_hdr_frag; - struct net_buf *last_hdr_frag; - struct net_buf *frag; - u16_t pkt_offset; - u16_t offset; + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr); + struct net_ipv6_hdr *hdr; + u8_t next_nexthdr; + u8_t nexthdr; u16_t length; - u8_t next_hdr; - u8_t next; - if (!pkt || !pkt->frags || !next_hdr_idx || !last_hdr_idx) { + if (!pkt || !pkt->frags || !next_hdr_off || !last_hdr_off) { return -EINVAL; } - next = NET_IPV6_HDR(pkt)->nexthdr; - - /* Initial value if no extension fragments are found */ - *next_hdr_idx = 6U; - *last_hdr_idx = sizeof(struct net_ipv6_hdr); + net_pkt_cursor_init(pkt); - /* 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 || next == NET_IPV6_NEXTHDR_NONE) { - return 0; + hdr = (struct net_ipv6_hdr *)net_pkt_get_data_new(pkt, &ipv6_access); + if (!hdr) { + return -ENOBUFS; } - frag = pkt->frags; - offset = *last_hdr_idx; - *next_hdr_idx = *last_hdr_idx; - next_hdr_frag = last_hdr_frag = frag; + net_pkt_acknowledge_data(pkt, &ipv6_access); - while (frag) { - frag = net_frag_read_u8(frag, offset, &offset, &next_hdr); - if (!frag) { - goto fail; - } + nexthdr = hdr->nexthdr; - switch (next) { - case NET_IPV6_NEXTHDR_FRAG: - frag = net_frag_skip(frag, offset, &offset, 7); - if (!frag) { - goto fail; - } + /* Initial values */ + *next_hdr_off = offsetof(struct net_ipv6_hdr, nexthdr); + *last_hdr_off = sizeof(struct net_ipv6_hdr); - break; + nexthdr = hdr->nexthdr; + while (!net_ipv6_is_nexthdr_upper_layer(nexthdr)) { + if (net_pkt_read_u8_new(pkt, &next_nexthdr)) { + goto fail; + } + switch (nexthdr) { case NET_IPV6_NEXTHDR_HBHO: case NET_IPV6_NEXTHDR_DESTO: length = 0U; - frag = net_frag_read_u8(frag, offset, &offset, - (u8_t *)&length); - if (!frag) { + + if (net_pkt_read_u8_new(pkt, (u8_t *)&length)) { goto fail; } - length = length * 8 + 8; + length = length * 8 + 8 - 2; - frag = net_frag_skip(frag, offset, &offset, length - 2); - if (!frag) { + if (net_pkt_skip(pkt, length)) { goto fail; } break; + case NET_IPV6_NEXTHDR_FRAG: + if (net_pkt_skip(pkt, 7)) { + goto fail; + } + break; case NET_IPV6_NEXTHDR_NONE: - case IPPROTO_ICMPV6: - case IPPROTO_UDP: - case IPPROTO_TCP: goto out; - default: /* TODO: Add more IPv6 extension headers to check */ goto fail; } - *next_hdr_idx = *last_hdr_idx; - next_hdr_frag = last_hdr_frag; - - *last_hdr_idx = offset; - last_hdr_frag = frag; + *next_hdr_off = *last_hdr_off; + *last_hdr_off = net_pkt_get_current_offset(pkt); - next = next_hdr; + nexthdr = next_nexthdr; } - -fail: - return -EINVAL; - out: - /* Current next_hdr_idx offset is based on respective fragment, but we - * need to calculate next_hdr_idx offset based on whole packet. - */ - pkt_offset = 0U; - frag = pkt->frags; - while (frag) { - if (next_hdr_frag == frag) { - *next_hdr_idx += pkt_offset; - break; - } - - pkt_offset += frag->len; - frag = frag->frags; - } - - /* Current last_hdr_idx offset is based on respective fragment, but we - * need to calculate last_hdr_idx offset based on whole packet. - */ - pkt_offset = 0U; - frag = pkt->frags; - while (frag) { - if (last_hdr_frag == frag) { - *last_hdr_idx += pkt_offset; - break; - } - - pkt_offset += frag->len; - frag = frag->frags; - } - return 0; +fail: + return -EINVAL; } - static struct net_ipv6_reassembly *reassembly_get(u32_t id, struct in6_addr *src, struct in6_addr *dst) @@ -286,25 +233,29 @@ static void reassembly_timeout(struct k_work *work) static void reassemble_packet(struct net_ipv6_reassembly *reass) { + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr); + NET_PKT_DATA_ACCESS_DEFINE(frag_access, struct net_ipv6_frag_hdr); + union { + struct net_ipv6_hdr *hdr; + struct net_ipv6_frag_hdr *frag_hdr; + } ipv6; + struct net_pkt *pkt; struct net_buf *last; - struct net_buf *frag; u8_t next_hdr; - int i, len, ret; - u16_t pos; + int i, len; k_delayed_work_cancel(&reass->timer); NET_ASSERT(reass->pkt[0]); - last = net_buf_frag_last(reass->pkt[0]->frags); + last = net_buf_frag_last(reass->pkt[0]->buffer); /* We start from 2nd packet which is then appended to * the first one. */ for (i = 1; i < NET_IPV6_FRAGMENTS_MAX_PKT; i++) { int removed_len; - int ret; pkt = reass->pkt[i]; @@ -315,19 +266,19 @@ static void reassemble_packet(struct net_ipv6_reassembly *reass) sizeof(struct net_ipv6_frag_hdr); NET_DBG("Removing %d bytes from start of pkt %p", - removed_len, pkt->frags); + removed_len, pkt->buffer); - ret = net_pkt_pull(pkt, 0, removed_len); - if (ret) { + if (net_pkt_pull_new(pkt, removed_len)) { NET_ERR("Failed to pull headers"); - NET_ASSERT(ret != 0); + reassembly_cancel(reass->id, &reass->src, &reass->dst); + return; } /* Attach the data to previous pkt */ - last->frags = pkt->frags; - last = net_buf_frag_last(pkt->frags); + last->frags = pkt->buffer; + last = net_buf_frag_last(pkt->buffer); - pkt->frags = NULL; + pkt->buffer = NULL; reass->pkt[i] = NULL; net_pkt_unref(pkt); @@ -339,35 +290,44 @@ static void reassemble_packet(struct net_ipv6_reassembly *reass) /* Next we need to strip away the fragment header from the first packet * and set the various pointers and values in packet. */ + net_pkt_cursor_init(pkt); + + if (net_pkt_skip(pkt, net_pkt_ipv6_fragment_start(pkt))) { + NET_ERR("Failed to move to fragment header"); + goto error; + } - frag = net_frag_read_u8(pkt->frags, net_pkt_ipv6_fragment_start(pkt), - &pos, &next_hdr); - if (!frag && pos == 0xFFFF) { - NET_ERR("Failed to read next header"); - NET_ASSERT(frag); + ipv6.frag_hdr = (struct net_ipv6_frag_hdr *)net_pkt_get_data_new( + pkt, &frag_access); + if (!ipv6.frag_hdr) { + NET_ERR("Failed to get fragment header"); + goto error; } - ret = net_pkt_pull(pkt, net_pkt_ipv6_fragment_start(pkt), - sizeof(struct net_ipv6_frag_hdr)); - if (ret) { - NET_ERR("Failed to pull fragmentation header"); - NET_ASSERT(ret); + next_hdr = ipv6.frag_hdr->nexthdr; + + if (net_pkt_pull_new(pkt, sizeof(struct net_ipv6_frag_hdr))) { + NET_ERR("Failed to remove fragment header"); + goto error; } + net_pkt_cursor_init(pkt); + /* This one updates the previous header's nexthdr value */ - if (!net_pkt_write_u8_timeout(pkt, pkt->frags, - net_pkt_ipv6_hdr_prev(pkt), - &pos, next_hdr, NET_BUF_TIMEOUT)) { - net_pkt_unref(pkt); - return; + if (net_pkt_skip(pkt, net_pkt_ipv6_hdr_prev(pkt)) || + net_pkt_write_u8_new(pkt, next_hdr)) { + goto error; } - if (!net_pkt_compact(pkt)) { - NET_ERR("Cannot compact reassembly packet %p", pkt); - net_pkt_unref(pkt); - return; + net_pkt_cursor_init(pkt); + + ipv6.hdr = (struct net_ipv6_hdr *)net_pkt_get_data_new(pkt, + &ipv6_access); + if (!ipv6.hdr) { + goto error; } + /* Fix the total length of the IPv6 packet. */ len = net_pkt_ipv6_ext_len(pkt); if (len > 0) { @@ -378,7 +338,9 @@ static void reassemble_packet(struct net_ipv6_reassembly *reass) len = net_pkt_get_len(pkt) - sizeof(struct net_ipv6_hdr); - NET_IPV6_HDR(pkt)->len = htons(len); + ipv6.hdr->len = htons(len); + + net_pkt_set_data(pkt, &ipv6_access); NET_DBG("New pkt %p IPv6 len is %d bytes", pkt, len); @@ -388,10 +350,11 @@ 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. */ - ret = net_recv_data(net_pkt_iface(pkt), pkt); - if (ret < 0) { - net_pkt_unref(pkt); + if (net_recv_data(net_pkt_iface(pkt), pkt) >= 0) { + return; } +error: + net_pkt_unref(pkt); } void net_ipv6_frag_foreach(net_ipv6_frag_cb_t cb, void *user_data) @@ -471,18 +434,14 @@ static int shift_packets(struct net_ipv6_reassembly *reass, int pos) } enum net_verdict net_ipv6_handle_fragment_hdr(struct net_pkt *pkt, - struct net_buf *frag, - int total_len, - u16_t buf_offset, - u16_t *loc, + struct net_ipv6_hdr *hdr, u8_t nexthdr) { struct net_ipv6_reassembly *reass = NULL; - u32_t id; - u16_t offset; u16_t flag; - u8_t more; bool found; + u8_t more; + u32_t id; int i; if (!reassembly_init_done) { @@ -497,29 +456,29 @@ enum net_verdict net_ipv6_handle_fragment_hdr(struct net_pkt *pkt, reassembly_init_done = true; } - /* Each fragment has a fragment header. */ - frag = net_frag_skip(frag, buf_offset, loc, 1); /* reserved */ - frag = net_frag_read_be16(frag, *loc, loc, &flag); - frag = net_frag_read_be32(frag, *loc, loc, &id); - if (!frag && *loc == 0xffff) { + /* Each fragment has a fragment header, however since we already + * read the nexthdr part of it, we are not going to use + * net_pkt_get_data_new() and access the header directly: the cursor + * being 1 byte too far, let's just read the next relevant pieces. + */ + if (net_pkt_skip(pkt, 1) || /* reserved */ + net_pkt_read_be16_new(pkt, &flag) || + net_pkt_read_be32_new(pkt, &id)) { goto drop; } - reass = reassembly_get(id, &NET_IPV6_HDR(pkt)->src, - &NET_IPV6_HDR(pkt)->dst); + reass = reassembly_get(id, &hdr->src, &hdr->dst); if (!reass) { NET_DBG("Cannot get reassembly slot, dropping pkt %p", pkt); goto drop; } - offset = flag & 0xfff8; more = flag & 0x01; - - net_pkt_set_ipv6_fragment_offset(pkt, offset); + net_pkt_set_ipv6_fragment_offset(pkt, flag & 0xfff8); if (!reass->pkt[0]) { - NET_DBG("Storing pkt %p to slot %d offset 0x%x", pkt, 0, - offset); + NET_DBG("Storing pkt %p to slot %d offset 0x%x", + pkt, 0, net_pkt_ipv6_fragment_offset(pkt)); reass->pkt[0] = pkt; reassembly_info("Reassembly 1st pkt", reass); @@ -532,28 +491,25 @@ enum net_verdict net_ipv6_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++) { - if (!reass->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) { - continue; - } + if (reass->pkt[i]) { + if (net_pkt_ipv6_fragment_offset(reass->pkt[i]) < + net_pkt_ipv6_fragment_offset(pkt)) { + continue; + } - /* Make room for this fragment, if there is no room, then - * discard the whole reassembly. - */ - if (shift_packets(reass, i)) { - break; + /* Make room for this fragment. If there is no room, + * then it will discard the whole reassembly. + */ + if (shift_packets(reass, i)) { + break; + } } - NET_DBG("Storing %p (offset 0x%x) to [%d]", pkt, offset, i); + NET_DBG("Storing pkt %p to slot %d offset 0x%x", + pkt, i, net_pkt_ipv6_fragment_offset(pkt)); reass->pkt[i] = pkt; found = true; + break; } @@ -615,124 +571,76 @@ enum net_verdict net_ipv6_handle_fragment_hdr(struct net_pkt *pkt, #define BUF_ALLOC_TIMEOUT K_MSEC(100) -static int send_ipv6_fragment(struct net_if *iface, - struct net_pkt *pkt, - struct net_buf **rest, - u16_t ipv6_hdrs_len, +static int send_ipv6_fragment(struct net_pkt *pkt, u16_t fit_len, u16_t frag_offset, + u16_t next_hdr_off, u8_t next_hdr, - u16_t next_hdr_idx, - u8_t last_hdr, - u16_t last_hdr_idx, - u16_t frag_count) + bool final) { - struct net_pkt *ipv6 = NULL; - bool final; - struct net_ipv6_frag_hdr hdr; - struct net_buf *frag; - struct net_buf *temp; - u16_t pos; - bool res; - int ret; - - ipv6 = net_pkt_clone(pkt, BUF_ALLOC_TIMEOUT); - if (!ipv6) { - NET_DBG("Cannot clone %p", ipv6); + NET_PKT_DATA_ACCESS_DEFINE(frag_access, struct net_ipv6_frag_hdr); + int ret = -ENOBUFS; + struct net_ipv6_frag_hdr *frag_hdr; + struct net_pkt *frag_pkt; + + frag_pkt = net_pkt_alloc_with_buffer(net_pkt_iface(pkt), fit_len + + net_pkt_ipv6_ext_len(pkt) + + NET_IPV6_FRAGH_LEN, + AF_INET6, 0, BUF_ALLOC_TIMEOUT); + if (!frag_pkt) { return -ENOMEM; } - /* And we need to update the last header in the IPv6 packet to point to - * fragment header. - */ - temp = net_pkt_write_u8_timeout(ipv6, ipv6->frags, next_hdr_idx, &pos, - NET_IPV6_NEXTHDR_FRAG, - BUF_ALLOC_TIMEOUT); - if (!temp) { - if (pos == 0xffff) { - ret = -EINVAL; - } else { - ret = -ENOMEM; - } - - goto fail; - } + net_pkt_cursor_init(pkt); - /* Update the extension length metadata so that upper layer checksum - * will be calculated properly by net_ipv6_finalize(). + /* We copy original headers back to the fragment packet + * Note that we insert the right next header to point to fragment header */ - net_pkt_set_ipv6_ext_len(ipv6, - net_pkt_ipv6_ext_len(pkt) + - sizeof(struct net_ipv6_frag_hdr)); - - frag = *rest; - if (fit_len < net_buf_frags_len(*rest)) { - ret = net_pkt_split(pkt, frag, fit_len, rest, FRAG_BUF_WAIT); - if (ret < 0) { - net_buf_unref(frag); - goto fail; - } - } else { - *rest = NULL; + if (net_pkt_copy_new(frag_pkt, pkt, next_hdr_off) || + net_pkt_write_u8_new(frag_pkt, NET_IPV6_NEXTHDR_FRAG) || + net_pkt_skip(pkt, 1) || + net_pkt_copy_new(frag_pkt, pkt, net_pkt_ip_hdr_len(pkt) + + net_pkt_ipv6_ext_len(pkt) - next_hdr_off - 1)) { + goto fail; } - final = false; - /* *rest == NULL means no more data to send */ - if (!*rest) { - final = true; + /* And we append the fragmentation header */ + frag_hdr = (struct net_ipv6_frag_hdr *)net_pkt_get_data_new( + frag_pkt, &frag_access); + if (!frag_hdr) { + goto fail; } - /* Append the Fragmentation Header */ - hdr.nexthdr = next_hdr; - hdr.reserved = 0; - hdr.id = net_pkt_ipv6_fragment_id(pkt); - hdr.offset = htons(((frag_offset / 8) << 3) | !final); + frag_hdr->nexthdr = next_hdr; + frag_hdr->reserved = 0; + frag_hdr->id = net_pkt_ipv6_fragment_id(pkt); + frag_hdr->offset = htons(((frag_offset / 8) << 3) | !final); - res = net_pkt_append_all(ipv6, sizeof(struct net_ipv6_frag_hdr), - (u8_t *)&hdr, FRAG_BUF_WAIT); - if (!res) { - net_buf_unref(frag); - ret = EINVAL; + if (net_pkt_set_data(frag_pkt, &frag_access)) { goto fail; } - /* Attach the first part of split payload to end of the packet. And - * "rest" of the packet will be sent in next iteration. - */ - temp = ipv6->frags; - while (1) { - if (!temp->frags) { - temp->frags = frag; - break; - } - - temp = temp->frags; - } + net_pkt_set_ipv6_ext_len(frag_pkt, + net_pkt_ipv6_ext_len(pkt) + + sizeof(struct net_ipv6_frag_hdr)); - res = net_pkt_compact(ipv6); - if (!res) { - ret = -EINVAL; + /* Finally we copy the payload part of this fragment from + * the original packet + */ + if (net_pkt_skip(pkt, frag_offset) || + net_pkt_copy_new(frag_pkt, pkt, fit_len)) { goto fail; } - /* 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(ipv6, NET_IPV6_NEXTHDR_FRAG); - if (ret < 0) { - NET_DBG("Cannot create IPv6 packet (%d)", ret); + net_pkt_cursor_init(frag_pkt); + + if (net_ipv6_finalize_new(frag_pkt, 0) < 0) { goto fail; } - /* If everything has been ok so far, we can send the packet. - * Note that we cannot send this re-constructed packet directly - * as the link layer headers will not be properly set (because - * we recreated the packet). So pass this packet back to TX - * so that the pkt is going back to L2 for setup. - */ - ret = net_send_data(ipv6); + /* If everything has been ok so far, we can send the packet. */ + ret = net_send_data(frag_pkt); if (ret < 0) { - NET_DBG("Cannot send fragment (%d)", ret); goto fail; } @@ -744,9 +652,8 @@ static int send_ipv6_fragment(struct net_if *iface, return 0; fail: - if (ipv6) { - net_pkt_unref(ipv6); - } + NET_DBG("Cannot send fragment (%d)", ret); + net_pkt_unref(frag_pkt); return ret; } @@ -754,97 +661,65 @@ static int send_ipv6_fragment(struct net_if *iface, int net_ipv6_send_fragmented_pkt(struct net_if *iface, struct net_pkt *pkt, u16_t pkt_len) { - struct net_buf *rest = NULL; - struct net_pkt *clone; - struct net_buf *temp; - u16_t next_hdr_idx; - u16_t last_hdr_idx; - u16_t ipv6_hdrs_len; + u16_t next_hdr_off; + u16_t last_hdr_off; u16_t frag_offset; - u16_t frag_count; - u16_t pos; + size_t length; u8_t next_hdr; u8_t last_hdr; int fit_len; - int ret = -EINVAL; - - /* We cannot touch original pkt because it might be used for - * some other purposes, like TCP resend etc. So we need to copy - * the large pkt here and do the fragmenting with the clone. - */ - clone = net_pkt_clone(pkt, BUF_ALLOC_TIMEOUT); - if (!clone) { - NET_DBG("Cannot clone %p", pkt); - return -ENOMEM; - } + int ret; - pkt = clone; net_pkt_set_ipv6_fragment_id(pkt, sys_rand32_get()); - ret = net_ipv6_find_last_ext_hdr(pkt, &next_hdr_idx, &last_hdr_idx); + ret = net_ipv6_find_last_ext_hdr(pkt, &next_hdr_off, &last_hdr_off); if (ret < 0) { - goto fail; + return ret; } - temp = net_frag_read_u8(pkt->frags, next_hdr_idx, &pos, &next_hdr); - if (!temp && pos == 0xffff) { - ret = -EINVAL; - goto fail; - } - - temp = net_frag_read_u8(pkt->frags, last_hdr_idx, &pos, &last_hdr); - if (!temp && pos == 0xffff) { - ret = -EINVAL; - goto fail; - } + net_pkt_cursor_init(pkt); - ipv6_hdrs_len = net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt); - - ret = net_pkt_split(pkt, pkt->frags, ipv6_hdrs_len, &rest, - FRAG_BUF_WAIT); - if (ret < 0 || ipv6_hdrs_len != net_pkt_get_len(pkt)) { - NET_DBG("Cannot split packet (%d)", ret); - goto fail; + if (net_pkt_skip(pkt, next_hdr_off) || + net_pkt_read_u8_new(pkt, &next_hdr) || + net_pkt_skip(pkt, last_hdr_off) || + net_pkt_read_u8_new(pkt, &last_hdr)) { + return -ENOBUFS; } - frag_count = 0U; - frag_offset = 0U; - /* The Maximum payload can fit into each packet after IPv6 header, * Extenstion headers and Fragmentation header. */ - fit_len = NET_IPV6_MTU - NET_IPV6_FRAGH_LEN - ipv6_hdrs_len; + fit_len = NET_IPV6_MTU - NET_IPV6_FRAGH_LEN - + (net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt)); if (fit_len <= 0) { /* Must be invalid extension headers length */ NET_DBG("No room for IPv6 payload MTU %d hdrs_len %d", - NET_IPV6_MTU, NET_IPV6_FRAGH_LEN + ipv6_hdrs_len); - ret = -EINVAL; - goto fail; + NET_IPV6_MTU, NET_IPV6_FRAGH_LEN + + net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt)); + return -EINVAL; } - while (rest) { - ret = send_ipv6_fragment(iface, pkt, &rest, ipv6_hdrs_len, - fit_len, frag_offset, next_hdr, - next_hdr_idx, last_hdr, last_hdr_idx, - frag_count); + frag_offset = 0U; + + length = net_pkt_get_len(pkt) - + (net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt)); + while (length) { + bool final = false; + + if (fit_len >= length) { + final = true; + fit_len = length; + } + + ret = send_ipv6_fragment(pkt, fit_len, frag_offset, + next_hdr_off, next_hdr, final); if (ret < 0) { - goto fail; + return ret; } - frag_count++; + length -= fit_len; frag_offset += fit_len; } - net_pkt_unref(pkt); - return 0; - -fail: - net_pkt_unref(pkt); - - if (rest) { - net_buf_unref(rest); - } - - return ret; } diff --git a/subsys/net/ip/ipv6_mld.c b/subsys/net/ip/ipv6_mld.c index d480c6c9f7229..e729ccd842821 100644 --- a/subsys/net/ip/ipv6_mld.c +++ b/subsys/net/ip/ipv6_mld.c @@ -30,160 +30,155 @@ LOG_MODULE_DECLARE(net_ipv6, CONFIG_NET_IPV6_LOG_LEVEL); #include "net_stats.h" /* Timeout for various buffer allocations in this file. */ -#define NET_BUF_TIMEOUT K_MSEC(50) +#define PKT_WAIT_TIME K_MSEC(50) -#define append(pkt, type, value) \ - do { \ - if (!net_pkt_append_##type##_timeout(pkt, value, \ - NET_BUF_TIMEOUT)) { \ - ret = -ENOMEM; \ - goto drop; \ - } \ - } while (0) - -#define append_all(pkt, size, value) \ - do { \ - if (!net_pkt_append_all(pkt, size, value, \ - NET_BUF_TIMEOUT)) { \ - ret = -ENOMEM; \ - goto drop; \ - } \ - } while (0) +#define MLDv2_MCAST_RECORD_LEN sizeof(struct net_icmpv6_mld_mcast_record) +#define IPV6_OPT_HDR_ROUTER_ALERT_LEN 8 -#define MLDv2_LEN (2 + 1 + 1 + 2 + sizeof(struct in6_addr) * 2) +#define MLDv2_LEN (MLDv2_MCAST_RECORD_LEN + sizeof(struct in6_addr)) -static struct net_pkt *create_mldv2(struct net_pkt *pkt, - const struct in6_addr *addr, - u16_t record_type, - u8_t num_sources) +static int mld_create(struct net_pkt *pkt, + const struct in6_addr *addr, + u8_t record_type, + u16_t num_sources) { - int ret; + NET_PKT_DATA_ACCESS_DEFINE(mld_access, + struct net_icmpv6_mld_mcast_record); + struct net_icmpv6_mld_mcast_record *mld; + + mld = (struct net_icmpv6_mld_mcast_record *) + net_pkt_get_data_new(pkt, &mld_access); + if (!mld) { + return -ENOBUFS; + } - append(pkt, u8, record_type); - append(pkt, u8, 0); /* aux data len */ - append(pkt, be16, num_sources); /* number of addresses */ + mld->record_type = record_type; + mld->aux_data_len = 0; + mld->num_sources = htons(num_sources); - append_all(pkt, sizeof(struct in6_addr), addr->s6_addr); + net_ipaddr_copy(&mld->mcast_address, addr); + + if (net_pkt_set_data(pkt, &mld_access)) { + return -ENOBUFS; + } if (num_sources > 0) { /* All source addresses, RFC 3810 ch 3 */ - append_all(pkt, sizeof(struct in6_addr), - net_ipv6_unspecified_address()->s6_addr); + if (net_pkt_write_new(pkt, + net_ipv6_unspecified_address()->s6_addr, + sizeof(struct in6_addr))) { + return -ENOBUFS; + } } - return pkt; - -drop: - return NULL; + return 0; } -static int send_mldv2_raw(struct net_if *iface, struct net_buf *frags) +static int mld_create_packet(struct net_pkt *pkt, u16_t count) { - struct net_pkt *pkt; struct in6_addr dst; - u16_t pos; - int ret; /* Sent to all MLDv2-capable routers */ net_ipv6_addr_create(&dst, 0xff02, 0, 0, 0, 0, 0, 0, 0x0016); - pkt = net_pkt_get_reserve_tx(NET_BUF_TIMEOUT); - if (!pkt) { - return -ENOMEM; - } + net_pkt_set_ipv6_hop_limit(pkt, 1); /* RFC 3810 ch 7.4 */ - if (!net_ipv6_create(pkt, - net_if_ipv6_select_src_addr(iface, &dst), - &dst, - iface, - NET_IPV6_NEXTHDR_HBHO)) { - ret = -ENOMEM; - goto drop; + if (net_ipv6_create_new(pkt, + net_if_ipv6_select_src_addr( + net_pkt_iface(pkt), &dst), + &dst)) { + return -ENOBUFS; } - NET_IPV6_HDR(pkt)->hop_limit = 1; /* RFC 3810 ch 7.4 */ + /* Add hop-by-hop option and router alert option, RFC 3810 ch 5. */ + if (net_pkt_write_u8_new(pkt, IPPROTO_ICMPV6) || + net_pkt_write_u8_new(pkt, 0)) { + return -ENOBUFS; + } - net_pkt_set_ipv6_hdr_prev(pkt, pkt->frags->len); + /* IPv6 router alert option is described in RFC 2711. + * - 0x0502 RFC 2711 ch 2.1 + * - MLD (value 0) + * - 2 bytes of padding + */ + if (net_pkt_write_be16_new(pkt, 0x0502) || + net_pkt_write_be16_new(pkt, 0) || + net_pkt_write_be16_new(pkt, 0)) { + return -ENOBUFS; + } - /* Add hop-by-hop option and router alert option, RFC 3810 ch 5. */ - append(pkt, u8, IPPROTO_ICMPV6); - append(pkt, u8, 0); /* length (0 means 8 bytes) */ + net_pkt_set_ipv6_ext_len(pkt, IPV6_OPT_HDR_ROUTER_ALERT_LEN); - /* IPv6 router alert option is described in RFC 2711. */ - append(pkt, be16, 0x0502); /* RFC 2711 ch 2.1 */ - append(pkt, be16, 0); /* pkt contains MLD msg */ - append(pkt, u8, 0); /* padding */ - append(pkt, u8, 0); /* padding */ + /* ICMPv6 header + reserved space + count. + * MLDv6 stuff will come right after + */ + if (net_icmpv6_create(pkt, NET_ICMPV6_MLDv2, 0) || + net_pkt_write_be16_new(pkt, 0) || + net_pkt_write_be16_new(pkt, count)) { + return -ENOBUFS; + } - /* ICMPv6 header */ - append(pkt, u8, NET_ICMPV6_MLDv2); /* type */ - append(pkt, u8, 0); /* code */ - append(pkt, be16, 0); /* chksum */ - append(pkt, be16, 0); /* reserved field */ + return 0; +} -#define ROUTER_ALERT_LEN 8 +static int mld_send(struct net_pkt *pkt) +{ + net_pkt_cursor_init(pkt); - net_pkt_set_iface(pkt, iface); + net_ipv6_finalize_new(pkt, NET_IPV6_NEXTHDR_HBHO); - /* Insert the actual multicast record(s) here */ - net_pkt_frag_add(pkt, frags); + /* IPV6 finalization above could not update ICMPv6 checksum + * due to the existence of Router Alert option in between. + * So let's do it here: + */ + net_pkt_skip(pkt, IPV6_OPT_HDR_ROUTER_ALERT_LEN); - ret = net_ipv6_finalize(pkt, NET_IPV6_NEXTHDR_HBHO); - if (ret < 0) { - goto drop; - } + net_icmpv6_finalize(pkt); - net_pkt_set_ipv6_ext_len(pkt, ROUTER_ALERT_LEN); + if (net_send_data(pkt) < 0) { + net_stats_update_icmp_drop(net_pkt_iface(pkt)); + net_stats_update_ipv6_mld_drop(net_pkt_iface(pkt)); - if (!net_pkt_write_be16_timeout(pkt, pkt->frags, - NET_IPV6H_LEN + ROUTER_ALERT_LEN + 2, - &pos, - ntohs(net_calc_chksum_icmpv6(pkt)), - NET_BUF_TIMEOUT)) { - ret = -ENOMEM; - goto drop; - } + net_pkt_unref(pkt); - ret = net_send_data(pkt); - if (ret < 0) { - goto drop; + return -1; } net_stats_update_icmp_sent(net_pkt_iface(pkt)); net_stats_update_ipv6_mld_sent(net_pkt_iface(pkt)); return 0; - -drop: - net_stats_update_icmp_drop(net_pkt_iface(pkt)); - net_stats_update_ipv6_mld_drop(net_pkt_iface(pkt)); - - net_pkt_unref(pkt); - - return ret; } -static int send_mldv2(struct net_if *iface, const struct in6_addr *addr, - u8_t mode) +static int mld_send_generic(struct net_if *iface, + const struct in6_addr *addr, + u8_t mode) { struct net_pkt *pkt; int ret; - pkt = net_pkt_get_reserve_tx(NET_BUF_TIMEOUT); + pkt = net_pkt_alloc_with_buffer(iface, IPV6_OPT_HDR_ROUTER_ALERT_LEN + + NET_ICMPV6_UNUSED_LEN + + MLDv2_MCAST_RECORD_LEN + + sizeof(struct in6_addr), + AF_INET6, IPPROTO_ICMPV6, + PKT_WAIT_TIME); if (!pkt) { return -ENOMEM; } - append(pkt, be16, 1); /* number of records */ - - if (!create_mldv2(pkt, addr, mode, 1)) { - ret = -ENOMEM; + if (mld_create_packet(pkt, 1) || + mld_create(pkt, addr, mode, 1)) { + ret = -ENOBUFS; goto drop; } - ret = send_mldv2_raw(iface, pkt->frags); + ret = mld_send(pkt); + if (ret) { + goto drop; + } - pkt->frags = NULL; + return 0; drop: net_pkt_unref(pkt); @@ -208,7 +203,7 @@ int net_ipv6_mld_join(struct net_if *iface, const struct in6_addr *addr) } } - ret = send_mldv2(iface, addr, NET_IPV6_MLDv2_MODE_IS_EXCLUDE); + ret = mld_send_generic(iface, addr, NET_IPV6_MLDv2_MODE_IS_EXCLUDE); if (ret < 0) { return ret; } @@ -230,7 +225,7 @@ int net_ipv6_mld_leave(struct net_if *iface, const struct in6_addr *addr) return -EINVAL; } - ret = send_mldv2(iface, addr, NET_IPV6_MLDv2_MODE_IS_INCLUDE); + ret = mld_send_generic(iface, addr, NET_IPV6_MLDv2_MODE_IS_INCLUDE); if (ret < 0) { return ret; } @@ -246,42 +241,44 @@ static void send_mld_report(struct net_if *iface) { struct net_if_ipv6 *ipv6 = iface->config.ip.ipv6; struct net_pkt *pkt; - int i, ret, count = 0; + int i, count = 0; NET_ASSERT(ipv6); - pkt = net_pkt_get_reserve_tx(NET_BUF_TIMEOUT); + for (i = 0; i < NET_IF_MAX_IPV6_MADDR; i++) { + if (!ipv6->mcast[i].is_used || !ipv6->mcast[i].is_joined) { + continue; + } + + count++; + } + + pkt = net_pkt_alloc_with_buffer(iface, IPV6_OPT_HDR_ROUTER_ALERT_LEN + + NET_ICMPV6_UNUSED_LEN + + count * MLDv2_MCAST_RECORD_LEN, + AF_INET6, IPPROTO_ICMPV6, + PKT_WAIT_TIME); if (!pkt) { return; } - append(pkt, u8, 0); /* This will be the record count */ + if (mld_create_packet(pkt, count)) { + goto drop; + } for (i = 0; i < NET_IF_MAX_IPV6_MADDR; i++) { if (!ipv6->mcast[i].is_used || !ipv6->mcast[i].is_joined) { continue; } - if (!create_mldv2(pkt, &ipv6->mcast[i].address.in6_addr, - NET_IPV6_MLDv2_MODE_IS_EXCLUDE, 0)) { + if (!mld_create(pkt, &ipv6->mcast[i].address.in6_addr, + NET_IPV6_MLDv2_MODE_IS_EXCLUDE, 0)) { goto drop; } - - count++; } - if (count > 0) { - u16_t pos; - - /* Write back the record count */ - if (!net_pkt_write_u8_timeout(pkt, pkt->frags, 0, &pos, - count, NET_BUF_TIMEOUT)) { - goto drop; - } - - send_mldv2_raw(iface, pkt->frags); - - pkt->frags = NULL; + if (!mld_send(pkt)) { + return; } drop: @@ -298,63 +295,54 @@ static void send_mld_report(struct net_if *iface) #define dbg_addr_recv(pkt_str, src, dst) \ dbg_addr("Received", pkt_str, src, dst) -static enum net_verdict handle_mld_query(struct net_pkt *pkt) +static enum net_verdict handle_mld_query(struct net_pkt *pkt, + struct net_ipv6_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr) { - u16_t total_len = net_pkt_get_len(pkt); - struct in6_addr mcast; - u16_t max_rsp_code, num_src, pkt_len; - u16_t offset, pos; - struct net_buf *frag; - int ret; + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(mld_access, + struct net_icmpv6_mld_query); + u16_t length = net_pkt_get_len(pkt); + struct net_icmpv6_mld_query *mld_query; + u16_t pkt_len; + + mld_query = (struct net_icmpv6_mld_query *) + net_pkt_get_data_new(pkt, &mld_access); + if (!mld_query) { + NET_DBG("DROP: NULL MLD query"); + goto drop; + } + + net_pkt_acknowledge_data(pkt, &mld_access); - dbg_addr_recv("Multicast Listener Query", - &NET_IPV6_HDR(pkt)->src, - &NET_IPV6_HDR(pkt)->dst); + dbg_addr_recv("Multicast Listener Query", &ip_hdr->src, &ip_hdr->dst); net_stats_update_ipv6_mld_recv(net_pkt_iface(pkt)); - /* offset tells now where the ICMPv6 header is starting */ - frag = net_frag_get_pos(pkt, - net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt) + - sizeof(struct net_icmp_hdr), - &offset); - - frag = net_frag_read_be16(frag, offset, &pos, &max_rsp_code); - frag = net_frag_skip(frag, pos, &pos, 2); /* two reserved bytes */ - frag = net_frag_read(frag, pos, &pos, sizeof(mcast), mcast.s6_addr); - frag = net_frag_skip(frag, pos, &pos, 2); /* skip S, QRV & QQIC */ - frag = net_frag_read_be16(pkt->frags, pos, &pos, &num_src); - if (!frag && pos == 0xffff) { - goto drop; - } + mld_query->num_sources = ntohs(mld_query->num_sources); pkt_len = sizeof(struct net_ipv6_hdr) + net_pkt_ipv6_ext_len(pkt) + - sizeof(struct net_icmp_hdr) + (2 + 2 + 16 + 2 + 2) + - sizeof(struct in6_addr) * num_src; - - if ((total_len < pkt_len || pkt_len > NET_IPV6_MTU || - (NET_IPV6_HDR(pkt)->hop_limit != 1))) { - struct net_icmp_hdr icmp_hdr; - - ret = net_icmpv6_get_hdr(pkt, &icmp_hdr); - if (ret < 0 || icmp_hdr.code != 0) { - NET_DBG("Preliminary check failed %u/%u, code %u, " - "hop %u", total_len, pkt_len, - icmp_hdr.code, NET_IPV6_HDR(pkt)->hop_limit); - goto drop; - } + sizeof(struct net_icmp_hdr) + + sizeof(struct net_icmpv6_mld_query) + + sizeof(struct in6_addr) * mld_query->num_sources; + + if (length < pkt_len || pkt_len > NET_IPV6_MTU || + ip_hdr->hop_limit != 1 || icmp_hdr->code != 0) { + goto drop; } - /* Currently we only support a unspecified address query. */ - if (!net_ipv6_addr_cmp(&mcast, net_ipv6_unspecified_address())) { - NET_DBG("Only supporting unspecified address query (%s)", - log_strdup(net_sprint_ipv6_addr(&mcast))); + /* Currently we only support an unspecified address query. */ + if (!net_ipv6_addr_cmp(&mld_query->mcast_address, + net_ipv6_unspecified_address())) { + NET_DBG("DROP: only supporting unspecified address query"); goto drop; } send_mld_report(net_pkt_iface(pkt)); + net_pkt_unref(pkt); + + return NET_OK; + drop: net_stats_update_ipv6_mld_drop(net_pkt_iface(pkt)); diff --git a/subsys/net/ip/ipv6_nbr.c b/subsys/net/ip/ipv6_nbr.c index 7166dd79b846a..015d717ec61f5 100644 --- a/subsys/net/ip/ipv6_nbr.c +++ b/subsys/net/ip/ipv6_nbr.c @@ -118,48 +118,6 @@ static void ipv6_nbr_set_state(struct net_nbr *nbr, net_ipv6_nbr_data(nbr)->state = new_state; } -static inline bool net_is_solicited(struct net_pkt *pkt) -{ - struct net_icmpv6_na_hdr na_hdr; - int ret; - - ret = net_icmpv6_get_na_hdr(pkt, &na_hdr); - if (ret < 0) { - NET_ERR("could not get na_hdr"); - return false; - } - - return na_hdr.flags & NET_ICMPV6_NA_FLAG_SOLICITED; -} - -static inline bool net_is_router(struct net_pkt *pkt) -{ - struct net_icmpv6_na_hdr na_hdr; - int ret; - - ret = net_icmpv6_get_na_hdr(pkt, &na_hdr); - if (ret < 0) { - NET_ERR("could not get na_hdr"); - return false; - } - - return na_hdr.flags & NET_ICMPV6_NA_FLAG_ROUTER; -} - -static inline bool net_is_override(struct net_pkt *pkt) -{ - struct net_icmpv6_na_hdr na_hdr; - int ret; - - ret = net_icmpv6_get_na_hdr(pkt, &na_hdr); - if (ret < 0) { - NET_ERR("could not get na_hdr"); - return false; - } - - return na_hdr.flags & NET_ICMPV6_NA_FLAG_OVERRIDE; -} - static inline struct net_nbr *get_nbr(int idx) { return &net_neighbor_pool[idx].nbr; @@ -685,14 +643,21 @@ static struct in6_addr *check_route(struct net_if *iface, return nexthop; } -struct net_pkt *net_ipv6_prepare_for_send(struct net_pkt *pkt) +enum net_verdict net_ipv6_prepare_for_send(struct net_pkt *pkt) { + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr); struct in6_addr *nexthop = NULL; struct net_if *iface = NULL; + struct net_ipv6_hdr *ip_hdr; struct net_nbr *nbr; int ret; - NET_ASSERT(pkt && pkt->frags); + NET_ASSERT(pkt && pkt->buffer); + + ip_hdr = (struct net_ipv6_hdr *)net_pkt_get_data_new(pkt, &ipv6_access); + if (!ip_hdr) { + return NET_DROP; + } #if defined(CONFIG_NET_IPV6_FRAGMENT) /* If we have already fragmented the packet, the fragment id will @@ -723,7 +688,9 @@ struct net_pkt *net_ipv6_prepare_for_send(struct net_pkt *pkt) * thing to do here and will cause free memory access * if not done. */ - net_pkt_set_sent(pkt, true); + if (IS_ENABLED(CONFIG_NET_TCP)) { + net_pkt_set_sent(pkt, true); + } /* We need to unref here because we simulate the packet * sending. @@ -734,20 +701,12 @@ struct net_pkt *net_ipv6_prepare_for_send(struct net_pkt *pkt) * is now split and its fragments will be sent * separately to network. */ - return NULL; + return NET_CONTINUE; } } ignore_frag_error: #endif /* CONFIG_NET_IPV6_FRAGMENT */ - /* Workaround Linux bug, see: - * https://github.com/zephyrproject-rtos/zephyr/issues/3111 - */ - if (atomic_test_bit(net_pkt_iface(pkt)->if_dev->flags, - NET_IF_POINTOPOINT)) { - return pkt; - } - /* If the IPv6 destination address is not link local, then try to get * the next hop from routing table if we have multi interface routing * enabled. The reason for this is that the neighbor cache will not @@ -756,15 +715,19 @@ struct net_pkt *net_ipv6_prepare_for_send(struct net_pkt *pkt) */ if ((net_pkt_lladdr_dst(pkt)->addr && ((IS_ENABLED(CONFIG_NET_ROUTING) && - net_ipv6_is_ll_addr(&NET_IPV6_HDR(pkt)->dst)) || + net_ipv6_is_ll_addr(&ip_hdr->dst)) || !IS_ENABLED(CONFIG_NET_ROUTING))) || - net_ipv6_is_addr_mcast(&NET_IPV6_HDR(pkt)->dst)) { - return pkt; + net_ipv6_is_addr_mcast(&ip_hdr->dst) || + /* Workaround Linux bug, see: + * https://github.com/zephyrproject-rtos/zephyr/issues/3111 + */ + atomic_test_bit(net_pkt_iface(pkt)->if_dev->flags, + NET_IF_POINTOPOINT)) { + return NET_OK; } - if (net_if_ipv6_addr_onlink(&iface, - &NET_IPV6_HDR(pkt)->dst)) { - nexthop = &NET_IPV6_HDR(pkt)->dst; + if (net_if_ipv6_addr_onlink(&iface, &ip_hdr->dst)) { + nexthop = &ip_hdr->dst; net_pkt_set_iface(pkt, iface); } else { /* We need to figure out where the destination @@ -772,11 +735,9 @@ struct net_pkt *net_ipv6_prepare_for_send(struct net_pkt *pkt) */ bool try_route = false; - nexthop = check_route(NULL, &NET_IPV6_HDR(pkt)->dst, - &try_route); + nexthop = check_route(NULL, &ip_hdr->dst, &try_route); if (!nexthop) { - net_pkt_unref(pkt); - return NULL; + return NET_DROP; } if (try_route) { @@ -839,34 +800,31 @@ struct net_pkt *net_ipv6_prepare_for_send(struct net_pkt *pkt) } } #endif - - return pkt; + return NET_OK; } #if defined(CONFIG_NET_IPV6_ND) /* We need to send NS and wait for NA before sending the packet. */ ret = net_ipv6_send_ns(net_pkt_iface(pkt), pkt, - &NET_IPV6_HDR(pkt)->src, NULL, - nexthop, false); + &ip_hdr->src, NULL, nexthop, false); if (ret < 0) { /* In case of an error, the NS send function will unref * the pkt. */ NET_DBG("Cannot send NS (%d)", ret); - return NULL; } - NET_DBG("pkt %p (frag %p) will be sent later", pkt, pkt->frags); + NET_DBG("pkt %p (buffer %p) will be sent later", pkt, pkt->buffer); + + return NET_CONTINUE; #else ARG_UNUSED(ret); - NET_DBG("pkt %p (frag %p) cannot be sent, dropping it.", pkt, - pkt->frags); + NET_DBG("pkt %p (buffer %p) cannot be sent, dropping it.", + pkt, pkt->buffer); - net_pkt_unref(pkt); + return NET_DROP; #endif /* CONFIG_NET_IPV6_ND */ - - return NULL; } struct net_nbr *net_ipv6_nbr_lookup(struct net_if *iface, @@ -915,54 +873,34 @@ static inline u8_t get_llao_len(struct net_if *iface) return 0; } -static inline void set_llao(struct net_linkaddr *lladdr, - u8_t *llao, u8_t llao_len, u8_t type) +static inline bool set_llao(struct net_pkt *pkt, + struct net_linkaddr *lladdr, + u8_t llao_len, u8_t type) { - llao[NET_ICMPV6_OPT_TYPE_OFFSET] = type; - llao[NET_ICMPV6_OPT_LEN_OFFSET] = llao_len >> 3; - - memcpy(&llao[NET_ICMPV6_OPT_DATA_OFFSET], lladdr->addr, lladdr->len); - - (void)memset(&llao[NET_ICMPV6_OPT_DATA_OFFSET + lladdr->len], 0, - llao_len - lladdr->len - 2); -} - -static void setup_headers(struct net_pkt *pkt, u8_t nd6_len, - u8_t icmp_type) -{ - net_buf_add(pkt->frags, - sizeof(struct net_ipv6_hdr) + - sizeof(struct net_icmp_hdr)); - - NET_IPV6_HDR(pkt)->vtc = 0x60; - NET_IPV6_HDR(pkt)->tcflow = 0; - NET_IPV6_HDR(pkt)->flow = 0; - NET_IPV6_HDR(pkt)->len = htons(NET_ICMPH_LEN + nd6_len); + struct net_icmpv6_nd_opt_hdr opt_hdr = { + .type = type, + .len = llao_len >> 3, + }; - NET_IPV6_HDR(pkt)->nexthdr = IPPROTO_ICMPV6; - NET_IPV6_HDR(pkt)->hop_limit = NET_IPV6_ND_HOP_LIMIT; + if (net_pkt_write_new(pkt, &opt_hdr, + sizeof(struct net_icmpv6_nd_opt_hdr)) || + net_pkt_write_new(pkt, lladdr->addr, lladdr->len) || + net_pkt_memset(pkt, 0, llao_len - lladdr->len - 2)) { + return false; + } - /* In this special case where we know there are no long extension - * headers, so we can use this header cast. - */ - net_pkt_icmp_data(pkt)->type = icmp_type; - net_pkt_icmp_data(pkt)->code = 0; + return true; } static inline struct net_nbr *handle_ns_neighbor(struct net_pkt *pkt, - u8_t ll_len, - u16_t sllao_offset) + u8_t ll_len) { struct net_linkaddr_storage lladdr; struct net_linkaddr nbr_lladdr; - struct net_buf *frag; - u16_t pos; lladdr.len = 8 * ll_len - 2; - frag = net_frag_read(pkt->frags, sllao_offset, - &pos, lladdr.len, lladdr.addr); - if (!frag && pos == 0xffff) { + if (net_pkt_read_new(pkt, lladdr.addr, lladdr.len)) { return NULL; } @@ -985,81 +923,72 @@ int net_ipv6_send_na(struct net_if *iface, const struct in6_addr *src, const struct in6_addr *dst, const struct in6_addr *tgt, u8_t flags) { - struct net_icmpv6_na_hdr na_hdr; + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(na_access, + struct net_icmpv6_na_hdr); + int ret = -ENOBUFS; + struct net_icmpv6_na_hdr *na_hdr; struct net_pkt *pkt; - struct net_buf *frag; u8_t llao_len; - int ret; - pkt = net_pkt_get_reserve_tx(ND_NET_BUF_TIMEOUT); - if (!pkt) { - return -ENOMEM; - } + llao_len = get_llao_len(iface); - frag = net_pkt_get_frag(pkt, ND_NET_BUF_TIMEOUT); - if (!frag) { - net_pkt_unref(pkt); + pkt = net_pkt_alloc_with_buffer(iface, + sizeof(struct net_icmpv6_na_hdr) + + llao_len, + AF_INET6, IPPROTO_ICMPV6, + ND_NET_BUF_TIMEOUT); + if (!pkt) { return -ENOMEM; } - net_pkt_frag_add(pkt, frag); - - net_pkt_set_iface(pkt, iface); - net_pkt_set_family(pkt, AF_INET6); - net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr)); - - llao_len = get_llao_len(iface); - - net_pkt_set_ipv6_ext_len(pkt, 0); + net_pkt_set_ipv6_hop_limit(pkt, NET_IPV6_ND_HOP_LIMIT); - setup_headers(pkt, sizeof(struct net_icmpv6_na_hdr) + llao_len, - NET_ICMPV6_NA); + if (net_ipv6_create_new(pkt, src, dst) || + net_icmpv6_create(pkt, NET_ICMPV6_NA, 0)) { + goto drop; + } - net_buf_add(frag, sizeof(struct net_icmpv6_na_hdr) + llao_len); + na_hdr = (struct net_icmpv6_na_hdr *)net_pkt_get_data_new(pkt, + &na_access); + if (!na_hdr) { + goto drop; + } - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, src); - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, dst); - net_ipaddr_copy(&na_hdr.tgt, tgt); + /* Let's make sure reserved part is full of 0 */ + memset(na_hdr, 0, sizeof(struct net_icmpv6_na_hdr)); - set_llao(net_if_get_link_addr(net_pkt_iface(pkt)), - (u8_t *)net_pkt_icmp_data(pkt) + sizeof(struct net_icmp_hdr) + - sizeof(struct net_icmpv6_na_hdr), - llao_len, NET_ICMPV6_ND_OPT_TLLAO); + na_hdr->flags = flags; + net_ipaddr_copy(&na_hdr->tgt, tgt); - na_hdr.flags = flags; - ret = net_icmpv6_set_na_hdr(pkt, &na_hdr); - if (ret < 0) { - net_pkt_unref(pkt); - return ret; + if (net_pkt_set_data(pkt, &na_access)) { + goto drop; } - pkt->frags->len = NET_IPV6ICMPH_LEN + - sizeof(struct net_icmpv6_na_hdr) + llao_len; - - ret = net_icmpv6_set_chksum(pkt); - if (ret < 0) { - net_pkt_unref(pkt); - return ret; + if (!set_llao(pkt, net_if_get_link_addr(iface), + llao_len, NET_ICMPV6_ND_OPT_TLLAO)) { + goto drop; } - dbg_addr_sent_tgt("Neighbor Advertisement", - &NET_IPV6_HDR(pkt)->src, - &NET_IPV6_HDR(pkt)->dst, - &na_hdr.tgt); + net_pkt_cursor_init(pkt); + net_ipv6_finalize_new(pkt, IPPROTO_ICMPV6); + + dbg_addr_sent_tgt("Neighbor Advertisement", src, dst, &na_hdr->tgt); if (net_send_data(pkt) < 0) { + net_stats_update_ipv6_nd_drop(iface); + ret = -EINVAL; + goto drop; } - net_stats_update_ipv6_nd_sent(net_pkt_iface(pkt)); + net_stats_update_ipv6_nd_sent(iface); return 0; drop: - net_stats_update_ipv6_nd_drop(net_pkt_iface(pkt)); net_pkt_unref(pkt); - return -EINVAL; + return ret; } static void ns_routing_info(struct net_pkt *pkt, @@ -1085,92 +1014,75 @@ static void ns_routing_info(struct net_pkt *pkt, } } -static enum net_verdict handle_ns_input(struct net_pkt *pkt) +static enum net_verdict handle_ns_input(struct net_pkt *pkt, + struct net_ipv6_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr) { - u16_t total_len = net_pkt_get_len(pkt); - u8_t prev_opt_len = 0U; + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ns_access, + struct net_icmpv6_ns_hdr); + NET_PKT_DATA_ACCESS_DEFINE(nd_access, struct net_icmpv6_nd_opt_hdr); + u16_t length = net_pkt_get_len(pkt); u8_t flags = 0U; bool routing = false; - struct net_icmpv6_nd_opt_hdr nd_opt_hdr; - struct net_icmpv6_ns_hdr ns_hdr; + struct net_icmpv6_nd_opt_hdr *nd_opt_hdr; + struct net_icmpv6_ns_hdr *ns_hdr; struct net_if_addr *ifaddr; - struct in6_addr *tgt; const struct in6_addr *src; - size_t left_len; - int ret; + struct in6_addr *tgt; - ret = net_icmpv6_get_ns_hdr(pkt, &ns_hdr); - if (ret < 0) { - NET_ERR("NULL NS header - dropping"); + ns_hdr = (struct net_icmpv6_ns_hdr *)net_pkt_get_data_new(pkt, + &ns_access); + if (!ns_hdr) { + NET_ERR("DROP: NULL NS header"); goto drop; } dbg_addr_recv_tgt("Neighbor Solicitation", - &NET_IPV6_HDR(pkt)->src, - &NET_IPV6_HDR(pkt)->dst, - &ns_hdr.tgt); + &ip_hdr->src, &ip_hdr->dst, &ns_hdr->tgt); net_stats_update_ipv6_nd_recv(net_pkt_iface(pkt)); - if ((total_len < (sizeof(struct net_ipv6_hdr) + + if (((length < (sizeof(struct net_ipv6_hdr) + sizeof(struct net_icmp_hdr) + sizeof(struct net_icmpv6_ns_hdr))) || - (NET_IPV6_HDR(pkt)->hop_limit != NET_IPV6_ND_HOP_LIMIT)) { - if (net_ipv6_is_addr_mcast(&ns_hdr.tgt)) { - struct net_icmp_hdr icmp_hdr; - - ret = net_icmpv6_get_hdr(pkt, &icmp_hdr); - if (ret < 0 || icmp_hdr.code != 0) { - NET_DBG("Preliminary check failed %u/%zu, " - "code %u, hop %u", - total_len, - (sizeof(struct net_ipv6_hdr) + - sizeof(struct net_icmp_hdr) + - sizeof(struct net_icmpv6_ns_hdr)), - icmp_hdr.code, - NET_IPV6_HDR(pkt)->hop_limit); - goto drop; - } - } + (ip_hdr->hop_limit != NET_IPV6_ND_HOP_LIMIT)) && + (net_ipv6_is_addr_mcast(&ns_hdr->tgt) && icmp_hdr->code != 0)) { + goto drop; } + net_pkt_acknowledge_data(pkt, &ns_access); + net_pkt_set_ipv6_ext_opt_len(pkt, sizeof(struct net_icmpv6_ns_hdr)); + length -= (sizeof(struct net_ipv6_hdr) + sizeof(struct net_icmp_hdr)); - left_len = net_pkt_get_len(pkt) - (sizeof(struct net_ipv6_hdr) + - sizeof(struct net_icmp_hdr)); + nd_opt_hdr = (struct net_icmpv6_nd_opt_hdr *) + net_pkt_get_data_new(pkt, &nd_access); - ret = net_icmpv6_get_nd_opt_hdr(pkt, &nd_opt_hdr); + while (nd_opt_hdr && nd_opt_hdr->len > 0 && + net_pkt_ipv6_ext_opt_len(pkt) < length) { + u8_t prev_opt_len; - while (!ret && net_pkt_ipv6_ext_opt_len(pkt) < left_len) { - if (!nd_opt_hdr.len) { - break; - } + net_pkt_acknowledge_data(pkt, &nd_access); - switch (nd_opt_hdr.type) { + switch (nd_opt_hdr->type) { case NET_ICMPV6_ND_OPT_SLLAO: - if (net_ipv6_is_addr_unspecified( - &NET_IPV6_HDR(pkt)->src)) { + if (net_ipv6_is_addr_unspecified(&ip_hdr->src)) { goto drop; } - if (nd_opt_hdr.len > 2) { - NET_ERR("Too long source link-layer address " + if (nd_opt_hdr->len > 2) { + NET_ERR("DROP: Too long source ll address " "in NS option"); goto drop; } - if (!handle_ns_neighbor(pkt, nd_opt_hdr.len, - net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt) + - sizeof(struct net_icmp_hdr) + - net_pkt_ipv6_ext_opt_len(pkt) + - 1 + 1)) { + if (!handle_ns_neighbor(pkt, nd_opt_hdr->len)) { goto drop; } - break; + break; default: - NET_DBG("Unknown ND option 0x%x", nd_opt_hdr.type); + NET_DBG("Unknown ND option 0x%x", nd_opt_hdr->type); break; } @@ -1178,50 +1090,50 @@ static enum net_verdict handle_ns_input(struct net_pkt *pkt) net_pkt_set_ipv6_ext_opt_len(pkt, net_pkt_ipv6_ext_opt_len(pkt) + - (nd_opt_hdr.len << 3)); + (nd_opt_hdr->len << 3)); if (prev_opt_len >= net_pkt_ipv6_ext_opt_len(pkt)) { - NET_ERR("Corrupted NS message"); + NET_ERR("DROP: Corrupted NS message"); goto drop; } - ret = net_icmpv6_get_nd_opt_hdr(pkt, &nd_opt_hdr); + nd_opt_hdr = (struct net_icmpv6_nd_opt_hdr *) + net_pkt_get_data_new(pkt, &nd_access); } if (IS_ENABLED(CONFIG_NET_ROUTING)) { - ifaddr = net_if_ipv6_addr_lookup(&ns_hdr.tgt, NULL); + ifaddr = net_if_ipv6_addr_lookup(&ns_hdr->tgt, NULL); } else { ifaddr = net_if_ipv6_addr_lookup_by_iface(net_pkt_iface(pkt), - &ns_hdr.tgt); + &ns_hdr->tgt); } if (!ifaddr) { if (IS_ENABLED(CONFIG_NET_ROUTING)) { struct in6_addr *nexthop; - nexthop = check_route(NULL, &ns_hdr.tgt, NULL); + nexthop = check_route(NULL, &ns_hdr->tgt, NULL); if (nexthop) { - ns_routing_info(pkt, nexthop, &ns_hdr.tgt); + ns_routing_info(pkt, nexthop, &ns_hdr->tgt); /* Note that the target is not the address of * the "nethop" as that is a link-local address * which is not routable. */ - tgt = &ns_hdr.tgt; + tgt = &ns_hdr->tgt; /* Source address must be one of our real * interface address where the packet was * received. */ src = net_if_ipv6_select_src_addr( - net_pkt_iface(pkt), - &NET_IPV6_HDR(pkt)->src); + net_pkt_iface(pkt), &ip_hdr->src); if (!src) { - NET_DBG("No interface address for " - "dst %s iface %p", + NET_DBG("DROP: No interface address " + "for dst %s iface %p", log_strdup( net_sprint_ipv6_addr( - &NET_IPV6_HDR(pkt)->src)), + &ip_hdr->src)), net_pkt_iface(pkt)); goto drop; } @@ -1231,8 +1143,8 @@ static enum net_verdict handle_ns_input(struct net_pkt *pkt) } } - NET_DBG("No such interface address %s", - log_strdup(net_sprint_ipv6_addr(&ns_hdr.tgt))); + NET_DBG("DROP: No such interface address %s", + log_strdup(net_sprint_ipv6_addr(&ns_hdr->tgt))); goto drop; } else { tgt = &ifaddr->address.in6_addr; @@ -1240,30 +1152,29 @@ static enum net_verdict handle_ns_input(struct net_pkt *pkt) /* As we swap the addresses later, the source will correctly * have our address. */ - src = &NET_IPV6_HDR(pkt)->src; + src = &ip_hdr->src; } nexthop_found: #if !defined(CONFIG_NET_IPV6_DAD) - if (net_ipv6_is_addr_unspecified(&NET_IPV6_HDR(pkt)->src)) { + if (net_ipv6_is_addr_unspecified(&ip_hdr->src)) { goto drop; } #else /* CONFIG_NET_IPV6_DAD */ /* Do DAD */ - if (net_ipv6_is_addr_unspecified(&NET_IPV6_HDR(pkt)->src)) { + if (net_ipv6_is_addr_unspecified(&ip_hdr->src)) { - if (!net_ipv6_is_addr_solicited_node(&NET_IPV6_HDR(pkt)->dst)) { - NET_DBG("Not solicited node addr %s", - log_strdup(net_sprint_ipv6_addr( - &NET_IPV6_HDR(pkt)->dst))); + if (!net_ipv6_is_addr_solicited_node(&ip_hdr->dst)) { + NET_DBG("DROP: Not solicited node addr %s", + log_strdup(net_sprint_ipv6_addr(&ip_hdr->dst))); goto drop; } if (ifaddr->addr_state == NET_ADDR_TENTATIVE) { - NET_DBG("DAD failed for %s iface %p", + NET_DBG("DROP: DAD failed for %s iface %p", log_strdup(net_sprint_ipv6_addr( &ifaddr->address.in6_addr)), net_pkt_iface(pkt)); @@ -1274,27 +1185,25 @@ static enum net_verdict handle_ns_input(struct net_pkt *pkt) } /* We reuse the received packet to send the NA */ - net_ipv6_addr_create_ll_allnodes_mcast(&NET_IPV6_HDR(pkt)->dst); - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, + net_ipv6_addr_create_ll_allnodes_mcast(&ip_hdr->dst); + net_ipaddr_copy(&ip_hdr->src, net_if_ipv6_select_src_addr(net_pkt_iface(pkt), - &NET_IPV6_HDR(pkt)->dst)); + &ip_hdr->dst)); flags = NET_ICMPV6_NA_FLAG_OVERRIDE; goto send_na; } #endif /* CONFIG_NET_IPV6_DAD */ - if (net_ipv6_is_my_addr(&NET_IPV6_HDR(pkt)->src)) { - NET_DBG("Duplicate IPv6 %s address", - log_strdup(net_sprint_ipv6_addr( - &NET_IPV6_HDR(pkt)->src))); + if (net_ipv6_is_my_addr(&ip_hdr->src)) { + NET_DBG("DROP: Duplicate IPv6 %s address", + log_strdup(net_sprint_ipv6_addr(&ip_hdr->src))); goto drop; } /* Address resolution */ - if (net_ipv6_is_addr_solicited_node(&NET_IPV6_HDR(pkt)->dst)) { - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, - &NET_IPV6_HDR(pkt)->src); - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, &ns_hdr.tgt); + if (net_ipv6_is_addr_solicited_node(&ip_hdr->dst)) { + net_ipaddr_copy(&ip_hdr->dst, &ip_hdr->src); + net_ipaddr_copy(&ip_hdr->src, &ns_hdr->tgt); flags = NET_ICMPV6_NA_FLAG_SOLICITED | NET_ICMPV6_NA_FLAG_OVERRIDE; goto send_na; @@ -1307,39 +1216,33 @@ static enum net_verdict handle_ns_input(struct net_pkt *pkt) /* Neighbor Unreachability Detection (NUD) */ if (IS_ENABLED(CONFIG_NET_ROUTING)) { - ifaddr = net_if_ipv6_addr_lookup(&NET_IPV6_HDR(pkt)->dst, - NULL); + ifaddr = net_if_ipv6_addr_lookup(&ip_hdr->dst, NULL); } else { ifaddr = net_if_ipv6_addr_lookup_by_iface(net_pkt_iface(pkt), - &NET_IPV6_HDR(pkt)->dst); + &ip_hdr->dst); } if (ifaddr) { - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, - &NET_IPV6_HDR(pkt)->src); - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, &ns_hdr.tgt); - src = &NET_IPV6_HDR(pkt)->src; + net_ipaddr_copy(&ip_hdr->dst, &ip_hdr->src); + net_ipaddr_copy(&ip_hdr->src, &ns_hdr->tgt); + src = &ip_hdr->src; tgt = &ifaddr->address.in6_addr; flags = NET_ICMPV6_NA_FLAG_SOLICITED | NET_ICMPV6_NA_FLAG_OVERRIDE; goto send_na; } else { - NET_DBG("NUD failed"); + NET_DBG("DROP: NUD failed"); goto drop; } send_na: - ret = net_ipv6_send_na(net_pkt_iface(pkt), - src, - &NET_IPV6_HDR(pkt)->dst, - tgt, - flags); - if (!ret) { + if (!net_ipv6_send_na(net_pkt_iface(pkt), src, + &ip_hdr->dst, tgt, flags)) { net_pkt_unref(pkt); return NET_OK; } - NET_DBG("Cannot send NA (%d)", ret); + NET_DBG("DROP: Cannot send NA"); return NET_DROP; @@ -1512,16 +1415,13 @@ static inline bool handle_na_neighbor(struct net_pkt *pkt, struct net_icmpv6_na_hdr *na_hdr, u16_t tllao_offset) { - bool lladdr_changed = false; - struct net_nbr *nbr; struct net_linkaddr_storage lladdr = { 0 }; + bool lladdr_changed = false; struct net_linkaddr_storage *cached_lladdr; struct net_pkt *pending; - struct net_buf *frag; - u16_t pos; + struct net_nbr *nbr; - nbr = nbr_lookup(&net_neighbor.table, net_pkt_iface(pkt), - &na_hdr->tgt); + nbr = nbr_lookup(&net_neighbor.table, net_pkt_iface(pkt), &na_hdr->tgt); NET_DBG("Neighbor lookup %p iface %p addr %s", nbr, net_pkt_iface(pkt), @@ -1537,9 +1437,10 @@ static inline bool handle_na_neighbor(struct net_pkt *pkt, if (tllao_offset) { lladdr.len = net_if_get_link_addr(net_pkt_iface(pkt))->len; - frag = net_frag_read(pkt->frags, tllao_offset, - &pos, lladdr.len, lladdr.addr); - if (!frag && pos == 0xffff) { + net_pkt_cursor_init(pkt); + + if (net_pkt_skip(pkt, tllao_offset) || + net_pkt_read_new(pkt, lladdr.addr, lladdr.len)) { return false; } } @@ -1594,7 +1495,7 @@ static inline bool handle_na_neighbor(struct net_pkt *pkt, cached_lladdr->len); } - if (net_is_solicited(pkt)) { + if (na_hdr->flags & NET_ICMPV6_NA_FLAG_SOLICITED) { ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_REACHABLE); net_ipv6_nbr_data(nbr)->ns_count = 0U; @@ -1608,7 +1509,8 @@ static inline bool handle_na_neighbor(struct net_pkt *pkt, ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_STALE); } - net_ipv6_nbr_data(nbr)->is_router = net_is_router(pkt); + net_ipv6_nbr_data(nbr)->is_router = + (na_hdr->flags & NET_ICMPV6_NA_FLAG_ROUTER); goto send_pending; } @@ -1616,7 +1518,7 @@ static inline bool handle_na_neighbor(struct net_pkt *pkt, /* We do not update the address if override bit is not set * and we have a valid address in the cache. */ - if (!net_is_override(pkt) && lladdr_changed) { + if (!(na_hdr->flags & NET_ICMPV6_NA_FLAG_OVERRIDE) && lladdr_changed) { if (net_ipv6_nbr_data(nbr)->state == NET_IPV6_NBR_STATE_REACHABLE) { ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_STALE); @@ -1625,8 +1527,9 @@ static inline bool handle_na_neighbor(struct net_pkt *pkt, return false; } - if (net_is_override(pkt) || - (!net_is_override(pkt) && tllao_offset && !lladdr_changed)) { + if (na_hdr->flags & NET_ICMPV6_NA_FLAG_OVERRIDE || + (!(na_hdr->flags & NET_ICMPV6_NA_FLAG_OVERRIDE) && + tllao_offset && !lladdr_changed)) { if (lladdr_changed) { dbg_update_neighbor_lladdr_raw( @@ -1636,7 +1539,7 @@ static inline bool handle_na_neighbor(struct net_pkt *pkt, cached_lladdr->len); } - if (net_is_solicited(pkt)) { + if (na_hdr->flags & NET_ICMPV6_NA_FLAG_SOLICITED) { ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_REACHABLE); /* We might have active timer from PROBE */ @@ -1653,23 +1556,22 @@ static inline bool handle_na_neighbor(struct net_pkt *pkt, } } - if (net_ipv6_nbr_data(nbr)->is_router && !net_is_router(pkt)) { + if (net_ipv6_nbr_data(nbr)->is_router && + !(na_hdr->flags & NET_ICMPV6_NA_FLAG_ROUTER)) { /* Update the routing if the peer is no longer * a router. */ /* FIXME */ } - net_ipv6_nbr_data(nbr)->is_router = net_is_router(pkt); + net_ipv6_nbr_data(nbr)->is_router = + (na_hdr->flags & NET_ICMPV6_NA_FLAG_ROUTER); send_pending: /* Next send any pending messages to the peer. */ pending = net_ipv6_nbr_data(nbr)->pending; - if (pending) { - NET_DBG("Sending pending %p to %s lladdr %s", pending, - log_strdup(net_sprint_ipv6_addr( - &NET_IPV6_HDR(pending)->dst)), + NET_DBG("Sending pending %p to lladdr %s", pending, log_strdup(net_sprint_ll_addr(cached_lladdr->addr, cached_lladdr->len))); @@ -1685,59 +1587,56 @@ static inline bool handle_na_neighbor(struct net_pkt *pkt, return true; } -static enum net_verdict handle_na_input(struct net_pkt *pkt) +static enum net_verdict handle_na_input(struct net_pkt *pkt, + struct net_ipv6_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr) { - u16_t total_len = net_pkt_get_len(pkt); + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(na_access, + struct net_icmpv6_na_hdr); + NET_PKT_DATA_ACCESS_DEFINE(nd_access, struct net_icmpv6_nd_opt_hdr); + u16_t length = net_pkt_get_len(pkt); u16_t tllao_offset = 0U; - u8_t prev_opt_len = 0U; - struct net_icmpv6_nd_opt_hdr nd_opt_hdr; - struct net_icmpv6_na_hdr na_hdr; + struct net_icmpv6_nd_opt_hdr *nd_opt_hdr; + struct net_icmpv6_na_hdr *na_hdr; struct net_if_addr *ifaddr; - size_t left_len; - int ret; - ret = net_icmpv6_get_na_hdr(pkt, &na_hdr); - if (ret < 0) { - NET_ERR("NULL NA header - dropping"); + na_hdr = (struct net_icmpv6_na_hdr *)net_pkt_get_data_new(pkt, + &na_access); + if (!na_hdr) { + NET_ERR("DROP: NULL NA header"); goto drop; } dbg_addr_recv_tgt("Neighbor Advertisement", - &NET_IPV6_HDR(pkt)->src, - &NET_IPV6_HDR(pkt)->dst, - &na_hdr.tgt); + &ip_hdr->src, &ip_hdr->dst, &na_hdr->tgt); net_stats_update_ipv6_nd_recv(net_pkt_iface(pkt)); - if ((total_len < (sizeof(struct net_ipv6_hdr) + - sizeof(struct net_icmp_hdr) + - sizeof(struct net_icmpv6_na_hdr) + - sizeof(struct net_icmpv6_nd_opt_hdr))) || - (NET_IPV6_HDR(pkt)->hop_limit != NET_IPV6_ND_HOP_LIMIT) || - net_ipv6_is_addr_mcast(&na_hdr.tgt) || - (net_is_solicited(pkt) && - net_ipv6_is_addr_mcast(&NET_IPV6_HDR(pkt)->dst))) { - struct net_icmp_hdr icmp_hdr; - - ret = net_icmpv6_get_hdr(pkt, &icmp_hdr); - if (ret < 0 || icmp_hdr.code != 0) { - goto drop; - } + if (((length < (sizeof(struct net_ipv6_hdr) + + sizeof(struct net_icmp_hdr) + + sizeof(struct net_icmpv6_na_hdr) + + sizeof(struct net_icmpv6_nd_opt_hdr))) || + (ip_hdr->hop_limit != NET_IPV6_ND_HOP_LIMIT) || + net_ipv6_is_addr_mcast(&na_hdr->tgt) || + (na_hdr->flags & NET_ICMPV6_NA_FLAG_SOLICITED && + net_ipv6_is_addr_mcast(&ip_hdr->dst))) && + (icmp_hdr->code != 0)) { + goto drop; } - net_pkt_set_ipv6_ext_opt_len(pkt, sizeof(struct net_icmpv6_na_hdr)); + net_pkt_acknowledge_data(pkt, &na_access); - left_len = net_pkt_get_len(pkt) - (sizeof(struct net_ipv6_hdr) + - sizeof(struct net_icmp_hdr)); + net_pkt_set_ipv6_ext_opt_len(pkt, sizeof(struct net_icmpv6_na_hdr)); + length -= (sizeof(struct net_ipv6_hdr) + sizeof(struct net_icmp_hdr)); - ret = net_icmpv6_get_nd_opt_hdr(pkt, &nd_opt_hdr); + nd_opt_hdr = (struct net_icmpv6_nd_opt_hdr *) + net_pkt_get_data_new(pkt, &nd_access); - while (!ret && net_pkt_ipv6_ext_opt_len(pkt) < left_len) { - if (!nd_opt_hdr.len) { - break; - } + while (nd_opt_hdr && nd_opt_hdr->len && + net_pkt_ipv6_ext_opt_len(pkt) < length) { + u8_t prev_opt_len; - switch (nd_opt_hdr.type) { + switch (nd_opt_hdr->type) { case NET_ICMPV6_ND_OPT_TLLAO: tllao_offset = net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt) + @@ -1746,7 +1645,7 @@ static enum net_verdict handle_na_input(struct net_pkt *pkt) break; default: - NET_DBG("Unknown ND option 0x%x", nd_opt_hdr.type); + NET_DBG("Unknown ND option 0x%x", nd_opt_hdr->type); break; } @@ -1754,33 +1653,35 @@ static enum net_verdict handle_na_input(struct net_pkt *pkt) net_pkt_set_ipv6_ext_opt_len(pkt, net_pkt_ipv6_ext_opt_len(pkt) + - (nd_opt_hdr.len << 3)); + (nd_opt_hdr->len << 3)); if (prev_opt_len >= net_pkt_ipv6_ext_opt_len(pkt)) { - NET_ERR("Corrupted NA message"); + NET_ERR("DROP: Corrupted NA message"); goto drop; } - ret = net_icmpv6_get_nd_opt_hdr(pkt, &nd_opt_hdr); + net_pkt_acknowledge_data(pkt, &nd_access); + nd_opt_hdr = (struct net_icmpv6_nd_opt_hdr *) + net_pkt_get_data_new(pkt, &nd_access); } ifaddr = net_if_ipv6_addr_lookup_by_iface(net_pkt_iface(pkt), - &na_hdr.tgt); + &na_hdr->tgt); if (ifaddr) { NET_DBG("Interface %p already has address %s", net_pkt_iface(pkt), - log_strdup(net_sprint_ipv6_addr(&na_hdr.tgt))); + log_strdup(net_sprint_ipv6_addr(&na_hdr->tgt))); #if defined(CONFIG_NET_IPV6_DAD) if (ifaddr->addr_state == NET_ADDR_TENTATIVE) { - dad_failed(net_pkt_iface(pkt), &na_hdr.tgt); + dad_failed(net_pkt_iface(pkt), &na_hdr->tgt); } #endif /* CONFIG_NET_IPV6_DAD */ goto drop; } - if (!handle_na_neighbor(pkt, &na_hdr, tllao_offset)) { + if (!handle_na_neighbor(pkt, na_hdr, tllao_offset)) { goto drop; } @@ -1798,110 +1699,92 @@ static enum net_verdict handle_na_input(struct net_pkt *pkt) int net_ipv6_send_ns(struct net_if *iface, struct net_pkt *pending, - struct in6_addr *src, - struct in6_addr *dst, - struct in6_addr *tgt, + const struct in6_addr *src, + const struct in6_addr *dst, + const struct in6_addr *tgt, bool is_my_address) { - struct net_icmpv6_ns_hdr ns_hdr; - struct net_pkt *pkt; - struct net_buf *frag; + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ns_access, + struct net_icmpv6_ns_hdr); + struct net_pkt *pkt = NULL; + int ret = -ENOBUFS; + struct net_icmpv6_ns_hdr *ns_hdr; + struct in6_addr node_dst; struct net_nbr *nbr; u8_t llao_len; - int ret; - pkt = net_pkt_get_reserve_tx(ND_NET_BUF_TIMEOUT); - if (!pkt) { - return -ENOMEM; + if (!dst) { + net_ipv6_addr_create_solicited_node(tgt, &node_dst); + dst = &node_dst; } - frag = net_pkt_get_frag(pkt, ND_NET_BUF_TIMEOUT); - if (!frag) { - net_pkt_unref(pkt); - return -ENOMEM; - } + llao_len = get_llao_len(iface); - net_pkt_frag_add(pkt, frag); + if (is_my_address) { + src = net_ipv6_unspecified_address(); + llao_len = 0; + } else { + if (!src) { + src = net_if_ipv6_select_src_addr(iface, dst); + } - net_pkt_set_iface(pkt, iface); - 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, 0); + if (net_ipv6_is_addr_unspecified(src)) { + NET_DBG("No source address for NS"); + ret = -EINVAL; - llao_len = get_llao_len(net_pkt_iface(pkt)); + goto drop; + } + } - setup_headers(pkt, sizeof(struct net_icmpv6_ns_hdr) + llao_len, - NET_ICMPV6_NS); + pkt = net_pkt_alloc_with_buffer(iface, + sizeof(struct net_icmpv6_ns_hdr) + + llao_len, + AF_INET6, IPPROTO_ICMPV6, + ND_NET_BUF_TIMEOUT); + if (!pkt) { + ret = -ENOMEM; + goto drop; + } - net_buf_add(frag, sizeof(struct net_icmpv6_ns_hdr)); + net_pkt_set_ipv6_hop_limit(pkt, NET_IPV6_ND_HOP_LIMIT); - if (!dst) { - net_ipv6_addr_create_solicited_node(tgt, - &NET_IPV6_HDR(pkt)->dst); - } else { - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, dst); + if (net_ipv6_create_new(pkt, src, dst) || + net_icmpv6_create(pkt, NET_ICMPV6_NS, 0)) { + goto drop; } - net_ipaddr_copy(&ns_hdr.tgt, tgt); - ret = net_icmpv6_set_ns_hdr(pkt, &ns_hdr); - if (ret < 0) { - net_pkt_unref(pkt); - return ret; + ns_hdr = (struct net_icmpv6_ns_hdr *)net_pkt_get_data_new(pkt, + &ns_access); + if (!ns_hdr) { + goto drop; } - if (is_my_address) { - u16_t len = ntohs(NET_IPV6_HDR(pkt)->len); - /* DAD */ - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, - net_ipv6_unspecified_address()); - NET_IPV6_HDR(pkt)->len = htons(len - llao_len); - } else { - if (src) { - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, src); - } else { - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, - net_if_ipv6_select_src_addr( - net_pkt_iface(pkt), - &NET_IPV6_HDR(pkt)->dst)); - } + ns_hdr->reserved = 0; + net_ipaddr_copy(&ns_hdr->tgt, tgt); - if (net_ipv6_is_addr_unspecified(&NET_IPV6_HDR(pkt)->src)) { - NET_DBG("No source address for NS"); - if (pending) { - net_pkt_unref(pending); - } + if (net_pkt_set_data(pkt, &ns_access)) { + goto drop; + } + if (!is_my_address) { + if (!set_llao(pkt, net_if_get_link_addr(iface), + llao_len, NET_ICMPV6_ND_OPT_SLLAO)) { goto drop; } - - net_buf_add(frag, llao_len); - - set_llao(net_if_get_link_addr(net_pkt_iface(pkt)), - (u8_t *)net_pkt_icmp_data(pkt) + - sizeof(struct net_icmp_hdr) + - sizeof(struct net_icmpv6_ns_hdr), - llao_len, NET_ICMPV6_ND_OPT_SLLAO); } - ret = net_icmpv6_set_chksum(pkt); - if (ret < 0) { - net_pkt_unref(pkt); - return ret; - } + net_pkt_cursor_init(pkt); + net_ipv6_finalize_new(pkt, IPPROTO_ICMPV6); - nbr = nbr_lookup(&net_neighbor.table, net_pkt_iface(pkt), &ns_hdr.tgt); + nbr = nbr_lookup(&net_neighbor.table, iface, &ns_hdr->tgt); if (!nbr) { nbr_print(); - nbr = nbr_new(net_pkt_iface(pkt), &ns_hdr.tgt, false, + nbr = nbr_new(iface, &ns_hdr->tgt, false, NET_IPV6_NBR_STATE_INCOMPLETE); if (!nbr) { NET_DBG("Could not create new neighbor %s", - log_strdup(net_sprint_ipv6_addr(&ns_hdr.tgt))); - if (pending) { - net_pkt_unref(pending); - } - + log_strdup(net_sprint_ipv6_addr(&ns_hdr->tgt))); goto drop; } } @@ -1913,7 +1796,6 @@ int net_ipv6_send_ns(struct net_if *iface, NET_DBG("Packet %p already pending for " "operation. Discarding pending %p and pkt %p", net_ipv6_nbr_data(nbr)->pending, pending, pkt); - net_pkt_unref(pending); goto drop; } @@ -1928,108 +1810,98 @@ int net_ipv6_send_ns(struct net_if *iface, } } - dbg_addr_sent_tgt("Neighbor Solicitation", - &NET_IPV6_HDR(pkt)->src, - &NET_IPV6_HDR(pkt)->dst, - &ns_hdr.tgt); + dbg_addr_sent_tgt("Neighbor Solicitation", src, dst, &ns_hdr->tgt); if (net_send_data(pkt) < 0) { NET_DBG("Cannot send NS %p (pending %p)", pkt, pending); if (pending) { nbr_clear_ns_pending(net_ipv6_nbr_data(nbr)); + pending = NULL; } goto drop; } - net_stats_update_ipv6_nd_sent(net_pkt_iface(pkt)); + net_stats_update_ipv6_nd_sent(iface); return 0; drop: - net_stats_update_ipv6_nd_drop(net_pkt_iface(pkt)); - net_pkt_unref(pkt); + if (pending) { + net_pkt_unref(pending); + } + + if (pkt) { + net_pkt_unref(pkt); + } + + net_stats_update_ipv6_nd_drop(iface); - return -EINVAL; + return ret; } #endif /* CONFIG_NET_IPV6_NBR_CACHE */ #if defined(CONFIG_NET_IPV6_ND) int net_ipv6_send_rs(struct net_if *iface) { - struct net_pkt *pkt; - struct net_buf *frag; - bool unspec_src; u8_t llao_len = 0U; - int ret; + int ret = -ENOBUFS; + const struct in6_addr *src; + struct in6_addr dst; + struct net_pkt *pkt; - pkt = net_pkt_get_reserve_tx(ND_NET_BUF_TIMEOUT); - if (!pkt) { - return -ENOMEM; + net_ipv6_addr_create_ll_allnodes_mcast(&dst); + src = net_if_ipv6_select_src_addr(iface, &dst); + + if (!net_ipv6_is_addr_unspecified(src)) { + llao_len = get_llao_len(iface); } - frag = net_pkt_get_frag(pkt, ND_NET_BUF_TIMEOUT); - if (!frag) { - net_pkt_unref(pkt); + pkt = net_pkt_alloc_with_buffer(iface, + sizeof(struct net_icmpv6_rs_hdr) + + llao_len, + AF_INET6, IPPROTO_ICMPV6, + ND_NET_BUF_TIMEOUT); + if (!pkt) { return -ENOMEM; } - net_pkt_frag_add(pkt, frag); - - net_pkt_set_iface(pkt, iface); - net_pkt_set_family(pkt, AF_INET6); - net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr)); + net_pkt_set_ipv6_hop_limit(pkt, NET_IPV6_ND_HOP_LIMIT); - net_ipv6_addr_create_ll_allnodes_mcast(&NET_IPV6_HDR(pkt)->dst); - - net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, - net_if_ipv6_select_src_addr(iface, - &NET_IPV6_HDR(pkt)->dst)); - - unspec_src = net_ipv6_is_addr_unspecified(&NET_IPV6_HDR(pkt)->src); - if (!unspec_src) { - llao_len = get_llao_len(net_pkt_iface(pkt)); + if (net_ipv6_create_new(pkt, src, &dst) || + net_icmpv6_create(pkt, NET_ICMPV6_RS, 0) || + net_pkt_memset(pkt, 0, sizeof(struct net_icmpv6_rs_hdr))) { + goto drop; } - setup_headers(pkt, sizeof(struct net_icmpv6_rs_hdr) + llao_len, - NET_ICMPV6_RS); - - net_buf_add(frag, sizeof(struct net_icmpv6_rs_hdr)); - - if (!unspec_src) { - net_buf_add(frag, llao_len); - - set_llao(net_if_get_link_addr(net_pkt_iface(pkt)), - (u8_t *)net_pkt_icmp_data(pkt) + - sizeof(struct net_icmp_hdr) + - sizeof(struct net_icmpv6_rs_hdr), - llao_len, NET_ICMPV6_ND_OPT_SLLAO); + if (llao_len > 0) { + if (!set_llao(pkt, net_if_get_link_addr(iface), + llao_len, NET_ICMPV6_ND_OPT_SLLAO)) { + goto drop; + } } - ret = net_icmpv6_set_chksum(pkt); - if (ret < 0) { - net_pkt_unref(pkt); - return ret; - } + net_pkt_cursor_init(pkt); + net_ipv6_finalize_new(pkt, IPPROTO_ICMPV6); - dbg_addr_sent("Router Solicitation", - &NET_IPV6_HDR(pkt)->src, - &NET_IPV6_HDR(pkt)->dst); + dbg_addr_sent("Router Solicitation", src, &dst); if (net_send_data(pkt) < 0) { + net_stats_update_ipv6_nd_drop(iface); + ret = -EINVAL; + goto drop; } - net_stats_update_ipv6_nd_sent(net_pkt_iface(pkt)); + net_stats_update_ipv6_nd_sent(iface); return 0; drop: - net_stats_update_ipv6_nd_drop(net_pkt_iface(pkt)); net_pkt_unref(pkt); - return -EINVAL; + return ret; } int net_ipv6_start_rs(struct net_if *iface) @@ -2037,44 +1909,32 @@ int net_ipv6_start_rs(struct net_if *iface) return net_ipv6_send_rs(iface); } -static inline struct net_buf *handle_ra_neighbor(struct net_pkt *pkt, - struct net_buf *frag, - u8_t len, - u16_t offset, u16_t *pos, - struct net_nbr **nbr) +static inline struct net_nbr *handle_ra_neighbor(struct net_pkt *pkt, u8_t len) { struct net_linkaddr lladdr; struct net_linkaddr_storage llstorage; u8_t padding; - if (!nbr) { - return NULL; - } - llstorage.len = NET_LINK_ADDR_MAX_LENGTH; - lladdr.len = NET_LINK_ADDR_MAX_LENGTH; lladdr.addr = llstorage.addr; + lladdr.len = NET_LINK_ADDR_MAX_LENGTH; if (net_pkt_lladdr_src(pkt)->len < lladdr.len) { lladdr.len = net_pkt_lladdr_src(pkt)->len; } - frag = net_frag_read(frag, offset, pos, lladdr.len, lladdr.addr); - if (!frag && offset) { + if (net_pkt_read_new(pkt, lladdr.addr, lladdr.len)) { return NULL; } padding = len * 8 - 2 - lladdr.len; if (padding) { - frag = net_frag_read(frag, *pos, pos, padding, NULL); - if (!frag && *pos) { + if (net_pkt_skip(pkt, padding)) { return NULL; } } - *nbr = nbr_add(pkt, &lladdr, true, NET_IPV6_NBR_STATE_STALE); - - return frag; + return nbr_add(pkt, &lladdr, true, NET_IPV6_NBR_STATE_STALE); } static inline void handle_prefix_onlink(struct net_pkt *pkt, @@ -2137,12 +1997,11 @@ static inline void handle_prefix_onlink(struct net_pkt *pkt, NET_DBG("Interface %p update prefix %s/%u lifetime %u", net_pkt_iface(pkt), log_strdup(net_sprint_ipv6_addr(&prefix_info->prefix)), - prefix_info->prefix_len, - prefix_info->valid_lifetime); + prefix_info->prefix_len, prefix_info->valid_lifetime); net_if_ipv6_prefix_set_lf(prefix, false); net_if_ipv6_prefix_set_timer(prefix, - prefix_info->valid_lifetime); + prefix_info->valid_lifetime); break; } } @@ -2202,8 +2061,8 @@ static inline void handle_prefix_autonomous(struct net_pkt *pkt, log_strdup(net_sprint_ipv6_addr(&addr)), prefix_info->valid_lifetime); - net_if_ipv6_addr_update_lifetime(ifaddr, - prefix_info->valid_lifetime); + net_if_ipv6_addr_update_lifetime( + ifaddr, prefix_info->valid_lifetime); } else { NET_DBG("Timer updating for address %s " "lifetime %u secs", @@ -2227,219 +2086,168 @@ static inline void handle_prefix_autonomous(struct net_pkt *pkt, } } -static inline struct net_buf *handle_ra_prefix(struct net_pkt *pkt, - struct net_buf *frag, - u8_t len, - u16_t offset, u16_t *pos) +static inline bool handle_ra_prefix(struct net_pkt *pkt) { - struct net_icmpv6_nd_opt_prefix_info prefix_info; - - prefix_info.type = NET_ICMPV6_ND_OPT_PREFIX_INFO; - prefix_info.len = len * 8 - 2; - - frag = net_frag_read(frag, offset, pos, 1, &prefix_info.prefix_len); - frag = net_frag_read(frag, *pos, pos, 1, &prefix_info.flags); - frag = net_frag_read_be32(frag, *pos, pos, &prefix_info.valid_lifetime); - frag = net_frag_read_be32(frag, *pos, pos, - &prefix_info.preferred_lifetime); - /* Skip reserved bytes */ - frag = net_frag_skip(frag, *pos, pos, 4); - frag = net_frag_read(frag, *pos, pos, sizeof(struct in6_addr), - prefix_info.prefix.s6_addr); - if (!frag && *pos) { - return NULL; + NET_PKT_DATA_ACCESS_DEFINE(rapfx_access, + struct net_icmpv6_nd_opt_prefix_info); + struct net_icmpv6_nd_opt_prefix_info *pfx_info; + + pfx_info = (struct net_icmpv6_nd_opt_prefix_info *) + net_pkt_get_data_new(pkt, &rapfx_access); + if (!pfx_info) { + return false; } - if (prefix_info.valid_lifetime >= prefix_info.preferred_lifetime && - !net_ipv6_is_ll_addr(&prefix_info.prefix)) { + net_pkt_acknowledge_data(pkt, &rapfx_access); - if (prefix_info.flags & NET_ICMPV6_RA_FLAG_ONLINK) { - handle_prefix_onlink(pkt, &prefix_info); + pfx_info->valid_lifetime = ntohl(pfx_info->valid_lifetime); + pfx_info->preferred_lifetime = ntohl(pfx_info->preferred_lifetime); + + if (pfx_info->valid_lifetime >= pfx_info->preferred_lifetime && + !net_ipv6_is_ll_addr(&pfx_info->prefix)) { + if (pfx_info->flags & NET_ICMPV6_RA_FLAG_ONLINK) { + handle_prefix_onlink(pkt, pfx_info); } - if ((prefix_info.flags & NET_ICMPV6_RA_FLAG_AUTONOMOUS) && - prefix_info.valid_lifetime && - (prefix_info.prefix_len == NET_IPV6_DEFAULT_PREFIX_LEN)) { - handle_prefix_autonomous(pkt, &prefix_info); + if ((pfx_info->flags & NET_ICMPV6_RA_FLAG_AUTONOMOUS) && + pfx_info->valid_lifetime && + (pfx_info->prefix_len == NET_IPV6_DEFAULT_PREFIX_LEN)) { + handle_prefix_autonomous(pkt, pfx_info); } } - return frag; + return true; } #if defined(CONFIG_NET_6LO_CONTEXT) /* 6lowpan Context Option RFC 6775, 4.2 */ -static inline struct net_buf *handle_ra_6co(struct net_pkt *pkt, - struct net_buf *frag, - u8_t len, - u16_t offset, u16_t *pos) +static inline bool handle_ra_6co(struct net_pkt *pkt, u8_t len) { - struct net_icmpv6_nd_opt_6co context; + NET_PKT_DATA_ACCESS_DEFINE(ctx_access, struct net_icmpv6_nd_opt_6co); + struct net_icmpv6_nd_opt_6co *context; - context.type = NET_ICMPV6_ND_OPT_6CO; - context.len = len * 8 - 2; - - frag = net_frag_read_u8(frag, offset, pos, &context.context_len); + context = (struct net_icmpv6_nd_opt_6co *) + net_pkt_get_data_new(pkt, &ctx_access); + if (!context) { + return false; + } /* RFC 6775, 4.2 * Context Length: 8-bit unsigned integer. The number of leading * bits in the Context Prefix field that are valid. The value ranges * from 0 to 128. If it is more than 64, then the Length MUST be 3. */ - if (context.context_len > 64 && len != 3) { - return NULL; - } - - if (context.context_len <= 64 && len != 2) { - return NULL; - } - - context.context_len = context.context_len / 8; - frag = net_frag_read_u8(frag, *pos, pos, &context.flag); - - /* Skip reserved bytes */ - frag = net_frag_skip(frag, *pos, pos, 2); - frag = net_frag_read_be16(frag, *pos, pos, &context.lifetime); - - /* RFC 6775, 4.2 (Length field). Length can be 2 or 3 depending - * on the length of context prefix field. - */ - if (len == 3) { - frag = net_frag_read(frag, *pos, pos, sizeof(struct in6_addr), - context.prefix.s6_addr); - } else if (len == 2) { - /* If length is 2 means only 64 bits of context prefix - * is available, rest set to zeros. - */ - frag = net_frag_read(frag, *pos, pos, 8, - context.prefix.s6_addr); + if ((context->context_len > 64 && len != 3) || + (context->context_len <= 64 && len != 2)) { + return false; } - if (!frag && *pos) { - return NULL; - } + context->context_len = context->context_len / 8; /* context_len: The number of leading bits in the Context Prefix - * field that are valid. So set remaining data to zero. + * field that are valid. Rest must be set to 0 by the sender and + * ignored by the receiver. But since there is no way to make sure + * the sender followed the rule, let's make sure rest is set to 0. */ - if (context.context_len != sizeof(struct in6_addr)) { - (void)memset(context.prefix.s6_addr + context.context_len, 0, - sizeof(struct in6_addr) - context.context_len); + if (context->context_len != sizeof(struct in6_addr)) { + (void)memset(context->prefix.s6_addr + context->context_len, 0, + sizeof(struct in6_addr) - context->context_len); } - net_6lo_set_context(net_pkt_iface(pkt), &context); + net_6lo_set_context(net_pkt_iface(pkt), context); - return frag; + return true; } #endif -static enum net_verdict handle_ra_input(struct net_pkt *pkt) +static enum net_verdict handle_ra_input(struct net_pkt *pkt, + struct net_ipv6_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr) { - u16_t total_len = net_pkt_get_len(pkt); + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ra_access, + struct net_icmpv6_ra_hdr); + NET_PKT_DATA_ACCESS_DEFINE(nd_access, struct net_icmpv6_nd_opt_hdr); + u16_t length = net_pkt_get_len(pkt); struct net_nbr *nbr = NULL; - struct net_icmpv6_ra_hdr ra_hdr; + struct net_icmpv6_nd_opt_hdr *nd_opt_hdr; + struct net_icmpv6_ra_hdr *ra_hdr; struct net_if_router *router; - struct net_buf *frag; - u16_t router_lifetime; - u32_t reachable_time; - u32_t retrans_timer; - u8_t hop_limit; - u16_t offset; - u8_t length; - u8_t type; u32_t mtu; - int ret; - dbg_addr_recv("Router Advertisement", - &NET_IPV6_HDR(pkt)->src, - &NET_IPV6_HDR(pkt)->dst); + ra_hdr = (struct net_icmpv6_ra_hdr *)net_pkt_get_data_new(pkt, + &ra_access); + if (!ra_hdr) { + NET_ERR("DROP: NULL RA header"); + goto drop; + } + + dbg_addr_recv("Router Advertisement", &ip_hdr->src, &ip_hdr->dst); net_stats_update_ipv6_nd_recv(net_pkt_iface(pkt)); - if ((total_len < (sizeof(struct net_ipv6_hdr) + - sizeof(struct net_icmp_hdr) + - sizeof(struct net_icmpv6_ra_hdr) + - sizeof(struct net_icmpv6_nd_opt_hdr))) || - (NET_IPV6_HDR(pkt)->hop_limit != NET_IPV6_ND_HOP_LIMIT) || - !net_ipv6_is_ll_addr(&NET_IPV6_HDR(pkt)->src)) { - struct net_icmp_hdr icmp_hdr; - - ret = net_icmpv6_get_hdr(pkt, &icmp_hdr); - if (ret < 0 || icmp_hdr.code != 0) { - goto drop; - } + if (((length < (sizeof(struct net_ipv6_hdr) + + sizeof(struct net_icmp_hdr) + + sizeof(struct net_icmpv6_ra_hdr) + + sizeof(struct net_icmpv6_nd_opt_hdr))) || + (ip_hdr->hop_limit != NET_IPV6_ND_HOP_LIMIT) || + !net_ipv6_is_ll_addr(&ip_hdr->src)) && icmp_hdr->code != 0) { + goto drop; } - frag = pkt->frags; - offset = sizeof(struct net_ipv6_hdr) + net_pkt_ipv6_ext_len(pkt) + - sizeof(struct net_icmp_hdr); + net_pkt_acknowledge_data(pkt, &ra_access); - frag = net_frag_read_u8(frag, offset, &offset, &hop_limit); - frag = net_frag_skip(frag, offset, &offset, 1); /* flags */ - if (!frag) { - goto drop; - } + ra_hdr->router_lifetime = ntohs(ra_hdr->router_lifetime); + ra_hdr->reachable_time = ntohl(ra_hdr->reachable_time); + ra_hdr->retrans_timer = ntohl(ra_hdr->retrans_timer); - if (hop_limit) { - net_ipv6_set_hop_limit(net_pkt_iface(pkt), hop_limit); + if (ra_hdr->cur_hop_limit) { + net_ipv6_set_hop_limit(net_pkt_iface(pkt), + ra_hdr->cur_hop_limit); NET_DBG("New hop limit %d", net_if_ipv6_get_hop_limit(net_pkt_iface(pkt))); } - frag = net_frag_read_be16(frag, offset, &offset, &router_lifetime); - frag = net_frag_read_be32(frag, offset, &offset, &reachable_time); - frag = net_frag_read_be32(frag, offset, &offset, &retrans_timer); - if (!frag) { - goto drop; - } - - ret = net_icmpv6_get_ra_hdr(pkt, &ra_hdr); - if (ret < 0) { - NET_ERR("could not get ra_hdr"); - goto drop; - } - - if (reachable_time && reachable_time <= MAX_REACHABLE_TIME && + if (ra_hdr->reachable_time && + ra_hdr->reachable_time <= MAX_REACHABLE_TIME && (net_if_ipv6_get_reachable_time(net_pkt_iface(pkt)) != - ra_hdr.reachable_time)) { + ra_hdr->reachable_time)) { net_if_ipv6_set_base_reachable_time(net_pkt_iface(pkt), - reachable_time); - + ra_hdr->reachable_time); net_if_ipv6_set_reachable_time( net_pkt_iface(pkt)->config.ip.ipv6); } - if (retrans_timer) { + if (ra_hdr->retrans_timer) { net_if_ipv6_set_retrans_timer(net_pkt_iface(pkt), - retrans_timer); + ra_hdr->retrans_timer); } - while (frag) { - frag = net_frag_read(frag, offset, &offset, 1, &type); - frag = net_frag_read(frag, offset, &offset, 1, &length); - if (!frag) { - goto drop; - } + net_pkt_set_ipv6_ext_opt_len(pkt, sizeof(struct net_icmpv6_ra_hdr)); + length -= (sizeof(struct net_ipv6_hdr) + sizeof(struct net_icmp_hdr)); + + nd_opt_hdr = (struct net_icmpv6_nd_opt_hdr *) + net_pkt_get_data_new(pkt, &nd_access); + + while (nd_opt_hdr) { + net_pkt_acknowledge_data(pkt, &nd_access); - switch (type) { + switch (nd_opt_hdr->type) { case NET_ICMPV6_ND_OPT_SLLAO: - frag = handle_ra_neighbor(pkt, frag, length, offset, - &offset, &nbr); - if (!frag && offset) { + nbr = handle_ra_neighbor(pkt, nd_opt_hdr->len); + if (!nbr) { goto drop; } break; case NET_ICMPV6_ND_OPT_MTU: /* MTU has reserved 2 bytes, so skip it. */ - frag = net_frag_skip(frag, offset, &offset, 2); - frag = net_frag_read_be32(frag, offset, &offset, &mtu); - if (!frag && offset) { + if (net_pkt_skip(pkt, 2) || + net_pkt_read_be32_new(pkt, &mtu)) { goto drop; } if (mtu < MIN_IPV6_MTU || mtu > MAX_IPV6_MTU) { - NET_ERR("Unsupported MTU %u, min is %u, " + NET_ERR("DROP: Unsupported MTU %u, min is %u, " "max is %u", mtu, MIN_IPV6_MTU, MAX_IPV6_MTU); goto drop; @@ -2449,9 +2257,7 @@ static enum net_verdict handle_ra_input(struct net_pkt *pkt) break; case NET_ICMPV6_ND_OPT_PREFIX_INFO: - frag = handle_ra_prefix(pkt, frag, length, offset, - &offset); - if (!frag && offset) { + if (!handle_ra_prefix(pkt)) { goto drop; } @@ -2459,50 +2265,49 @@ static enum net_verdict handle_ra_input(struct net_pkt *pkt) #if defined(CONFIG_NET_6LO_CONTEXT) case NET_ICMPV6_ND_OPT_6CO: /* RFC 6775, 4.2 (Length)*/ - if (!(length == 2 || length == 3)) { - NET_ERR("Invalid 6CO length %d", length); + if (!(nd_opt_hdr->len == 2 || nd_opt_hdr->len == 3)) { + NET_ERR("DROP: Invalid 6CO length %d", + nd_opt_hdr->len); goto drop; } - frag = handle_ra_6co(pkt, frag, length, offset, - &offset); - if (!frag && offset) { + if (!handle_ra_6co(pkt, nd_opt_hdr->len)) { goto drop; } break; #endif case NET_ICMPV6_ND_OPT_ROUTE: - NET_DBG("Route option (0x%x) skipped", type); + NET_DBG("Route option skipped"); goto skip; #if defined(CONFIG_NET_IPV6_RA_RDNSS) case NET_ICMPV6_ND_OPT_RDNSS: - NET_DBG("RDNSS option (0x%x) skipped", type); + NET_DBG("RDNSS option skipped"); goto skip; #endif case NET_ICMPV6_ND_OPT_DNSSL: - NET_DBG("DNSSL option (0x%x) skipped", type); + NET_DBG("DNSSL option skipped"); goto skip; default: - NET_DBG("Unknown ND option 0x%x", type); + NET_DBG("Unknown ND option 0x%x", nd_opt_hdr->type); skip: - frag = net_frag_skip(frag, offset, &offset, - length * 8 - 2); - if (!frag && offset) { + if (net_pkt_skip(pkt, nd_opt_hdr->len * 8 - 2)) { goto drop; } break; } + + nd_opt_hdr = (struct net_icmpv6_nd_opt_hdr *) + net_pkt_get_data_new(pkt, &nd_access); } - router = net_if_ipv6_router_lookup(net_pkt_iface(pkt), - &NET_IPV6_HDR(pkt)->src); + router = net_if_ipv6_router_lookup(net_pkt_iface(pkt), &ip_hdr->src); if (router) { - if (!router_lifetime) { + if (!ra_hdr->router_lifetime) { /* TODO: Start rs_timer on iface if no routers * at all available on iface. */ @@ -2512,13 +2317,12 @@ static enum net_verdict handle_ra_input(struct net_pkt *pkt) net_ipv6_nbr_data(nbr)->is_router = true; } - net_if_ipv6_router_update_lifetime(router, - router_lifetime); + net_if_ipv6_router_update_lifetime( + router, ra_hdr->router_lifetime); } } else { net_if_ipv6_router_add(net_pkt_iface(pkt), - &NET_IPV6_HDR(pkt)->src, - router_lifetime); + &ip_hdr->src, ra_hdr->router_lifetime); } if (nbr && net_ipv6_nbr_data(nbr)->pending) { @@ -2548,24 +2352,6 @@ static enum net_verdict handle_ra_input(struct net_pkt *pkt) } #endif /* CONFIG_NET_IPV6_ND */ -#define append(pkt, type, value) \ - do { \ - if (!net_pkt_append_##type##_timeout(pkt, value, \ - NET_BUF_TIMEOUT)) { \ - ret = -ENOMEM; \ - goto drop; \ - } \ - } while (0) - -#define append_all(pkt, size, value) \ - do { \ - if (!net_pkt_append_all(pkt, size, value, \ - NET_BUF_TIMEOUT)) { \ - ret = -ENOMEM; \ - goto drop; \ - } \ - } while (0) - #if defined(CONFIG_NET_IPV6_NBR_CACHE) static struct net_icmpv6_handler ns_input_handler = { .type = NET_ICMPV6_NS, diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index 353f9425238bb..5ed4cd7c02858 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -40,6 +40,8 @@ LOG_MODULE_REGISTER(net_ctx, CONFIG_NET_CONTEXT_LOG_LEVEL); #define EPFNOSUPPORT EPROTONOSUPPORT #endif +#define PKT_WAIT_TIME K_SECONDS(1) + #define NET_MAX_CONTEXT CONFIG_NET_MAX_CONTEXTS static struct net_context contexts[NET_MAX_CONTEXT]; @@ -676,6 +678,31 @@ struct net_pkt *net_context_create_ipv4(struct net_context *context, net_context_get_iface(context), net_context_get_ip_proto(context)); } + +int net_context_create_ipv4_new(struct net_context *context, + struct net_pkt *pkt, + const struct in_addr *src, + const struct in_addr *dst) +{ + NET_ASSERT(((struct sockaddr_in_ptr *)&context->local)->sin_addr); + + if (!src) { + src = ((struct sockaddr_in_ptr *)&context->local)->sin_addr; + } + + if (net_ipv4_is_addr_unspecified(src) + || net_ipv4_is_addr_mcast(src)) { + src = net_if_ipv4_select_src_addr(net_pkt_iface(pkt), + (struct in_addr *)dst); + /* If src address is still unspecified, do not create pkt */ + if (net_ipv4_is_addr_unspecified(src)) { + NET_DBG("DROP: src addr is unspecified"); + return -EINVAL; + } + } + + return net_ipv4_create_new(pkt, src, dst); +} #endif /* CONFIG_NET_IPV4 */ #if defined(CONFIG_NET_IPV6) @@ -702,6 +729,26 @@ struct net_pkt *net_context_create_ipv6(struct net_context *context, net_context_get_iface(context), net_context_get_ip_proto(context)); } + +int net_context_create_ipv6_new(struct net_context *context, + struct net_pkt *pkt, + const struct in6_addr *src, + const struct in6_addr *dst) +{ + NET_ASSERT(((struct sockaddr_in6_ptr *)&context->local)->sin6_addr); + + if (!src) { + src = ((struct sockaddr_in6_ptr *)&context->local)->sin6_addr; + } + + if (net_ipv6_is_addr_unspecified(src) + || net_ipv6_is_addr_mcast(src)) { + src = net_if_ipv6_select_src_addr(net_pkt_iface(pkt), + (struct in6_addr *)dst); + } + + return net_ipv6_create_new(pkt, src, dst); +} #endif /* CONFIG_NET_IPV6 */ int net_context_connect(struct net_context *context, @@ -1196,8 +1243,243 @@ int net_context_sendto(struct net_pkt *pkt, return ret; } +static int context_setup_udp_packet(struct net_context *context, + struct net_pkt *pkt, + const void *buf, + size_t len, + const struct sockaddr *dst_addr, + socklen_t addrlen) +{ + int ret = -EINVAL; + size_t written; + u16_t dst_port; + + if (IS_ENABLED(CONFIG_NET_IPV4) && + net_context_get_family(context) == AF_INET) { + struct sockaddr_in *addr4 = (struct sockaddr_in *)dst_addr; + + dst_port = addr4->sin_port; + + ret = net_context_create_ipv4_new(context, pkt, + NULL, &addr4->sin_addr); + } else if (IS_ENABLED(CONFIG_NET_IPV6) && + net_context_get_family(context) == AF_INET6) { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)dst_addr; + + dst_port = addr6->sin6_port; + + ret = net_context_create_ipv6_new(context, pkt, + NULL, &addr6->sin6_addr); + } + + if (ret < 0) { + return ret; + } + + ret = bind_default(context); + if (ret) { + return ret; + } + + ret = net_udp_create(pkt, + net_sin((struct sockaddr *) + &context->local)->sin_port, + dst_port); + if (ret) { + return ret; + } + + written = net_pkt_available_buffer(pkt); + if (written > len) { + written = len; + } + + ret = net_pkt_write_new(pkt, buf, written); + if (ret) { + return ret; + } + + return written; +} + +static void context_finalize_packet(struct net_context *context, + struct net_pkt *pkt) +{ + /* This function is meant to be temporary: once all moved to new + * API, it will be up to net_send_data() to finalize the packet. + */ + + net_pkt_cursor_init(pkt); + + if (IS_ENABLED(CONFIG_NET_IPV4) && + net_context_get_family(context) == AF_INET) { + net_ipv4_finalize_new(pkt, + net_context_get_ip_proto(context)); + } else if (IS_ENABLED(CONFIG_NET_IPV6) && + net_context_get_family(context) == AF_INET6) { + net_ipv6_finalize_new(pkt, + net_context_get_ip_proto(context)); + } +} + +static int context_sendto_new(struct net_context *context, + const void *buf, + size_t len, + const struct sockaddr *dst_addr, + socklen_t addrlen, + net_context_send_cb_t cb, + s32_t timeout, + void *token, + void *user_data) +{ + int sent = 0; + struct net_pkt *pkt; + int ret; + + NET_ASSERT(PART_OF_ARRAY(contexts, context)); + + if (!net_context_is_used(context)) { + return -EBADF; + } + + if (!dst_addr) { + return -EDESTADDRREQ; + } + + if (IS_ENABLED(CONFIG_NET_IPV4) && + net_context_get_family(context) == AF_INET) { + struct sockaddr_in *addr4 = (struct sockaddr_in *)dst_addr; + + if (addrlen < sizeof(struct sockaddr_in)) { + return -EINVAL; + } + + if (!addr4->sin_addr.s_addr) { + return -EDESTADDRREQ; + } + } else if (IS_ENABLED(CONFIG_NET_IPV6) && + net_context_get_family(context) == AF_INET6) { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)dst_addr; + + if (addrlen < sizeof(struct sockaddr_in6)) { + return -EINVAL; + } + + if (net_ipv6_is_addr_unspecified(&addr6->sin6_addr)) { + return -EDESTADDRREQ; + } + } else { + NET_DBG("Invalid protocol family %d", + net_context_get_family(context)); + return -EINVAL; + } + + pkt = net_pkt_alloc_with_buffer(net_context_get_iface(context), len, + net_context_get_family(context), + net_context_get_ip_proto(context), + PKT_WAIT_TIME); + if (!pkt) { + return -ENOMEM; + } + + net_pkt_set_context(pkt, context); + context->send_cb = cb; + context->user_data = user_data; + net_pkt_set_token(pkt, token); + + if (IS_ENABLED(CONFIG_NET_UDP) && + net_context_get_ip_proto(context) == IPPROTO_UDP) { + ret = context_setup_udp_packet(context, pkt, buf, len, + dst_addr, addrlen); + if (ret < 0) { + goto fail; + } + + context_finalize_packet(context, pkt); + + sent = ret; + ret = net_send_data(pkt); + } else if (IS_ENABLED(CONFIG_NET_TCP) && + net_context_get_ip_proto(context) == IPPROTO_TCP) { + ret = net_pkt_write_new(pkt, buf, len); + if (ret < 0) { + goto fail; + } + + sent = len; + + net_pkt_cursor_init(pkt); + ret = net_tcp_queue_data(context, pkt); + if (ret < 0) { + goto fail; + } + + ret = net_tcp_send_data(context, cb, token, user_data); + } else { + NET_DBG("Unknown protocol while sending packet: %d", + net_context_get_ip_proto(context)); + ret = -EPROTONOSUPPORT; + } + + if (ret < 0) { + goto fail; + } + + return sent; +fail: + net_pkt_unref(pkt); + + return ret; +} + +int net_context_send_new(struct net_context *context, + const void *buf, + size_t len, + net_context_send_cb_t cb, + s32_t timeout, + void *token, + void *user_data) +{ + socklen_t addrlen; + + if (!(context->flags & NET_CONTEXT_REMOTE_ADDR_SET) || + !net_sin(&context->remote)->sin_port) { + return -EDESTADDRREQ; + } + + if (IS_ENABLED(CONFIG_NET_IPV4) && + net_context_get_family(context) == AF_INET) { + addrlen = sizeof(struct sockaddr_in); + } else if (IS_ENABLED(CONFIG_NET_IPV6) && + net_context_get_family(context) == AF_INET6) { + addrlen = sizeof(struct sockaddr_in6); + } else { + addrlen = 0; + } + + return context_sendto_new(context, buf, len, &context->remote, + addrlen, cb, timeout, token, user_data); +} + + +int net_context_sendto_new(struct net_context *context, + const void *buf, + size_t len, + const struct sockaddr *dst_addr, + socklen_t addrlen, + net_context_send_cb_t cb, + s32_t timeout, + void *token, + void *user_data) +{ + return context_sendto_new(context, buf, len, dst_addr, addrlen, + cb, timeout, token, user_data); +} + enum net_verdict net_context_packet_received(struct net_conn *conn, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, void *user_data) { struct net_context *context = find_context(conn); @@ -1231,7 +1513,7 @@ enum net_verdict net_context_packet_received(struct net_conn *conn, net_pkt_appdata(pkt), net_pkt_appdatalen(pkt), net_pkt_get_len(pkt)); - context->recv_cb(context, pkt, 0, user_data); + context->recv_cb(context, pkt, ip_hdr, proto_hdr, 0, user_data); #if defined(CONFIG_NET_CONTEXT_SYNC_RECV) k_sem_give(&context->recv_data_wait); diff --git a/subsys/net/ip/net_core.c b/subsys/net/ip/net_core.c index ee3b1af653dcd..31b69bcb76026 100644 --- a/subsys/net/ip/net_core.c +++ b/subsys/net/ip/net_core.c @@ -102,19 +102,20 @@ static inline enum net_verdict process_data(struct net_pkt *pkt, } } + /* L2 has modified the buffer starting point, it is easier + * to re-initialize the cursor rather than updating it. + */ + net_pkt_cursor_init(pkt); + /* IP version and header length. */ switch (NET_IPV6_HDR(pkt)->vtc & 0xf0) { #if defined(CONFIG_NET_IPV6) case 0x60: - net_stats_update_ipv6_recv(net_pkt_iface(pkt)); - net_pkt_set_family(pkt, PF_INET6); - return net_ipv6_process_pkt(pkt, is_loopback); + return net_ipv6_input(pkt, is_loopback); #endif #if defined(CONFIG_NET_IPV4) case 0x40: - net_stats_update_ipv4_recv(net_pkt_iface(pkt)); - net_pkt_set_family(pkt, PF_INET); - return net_ipv4_process_pkt(pkt); + return net_ipv4_input(pkt); #endif } @@ -296,6 +297,8 @@ int net_send_data(struct net_pkt *pkt) } #endif + net_pkt_cursor_init(pkt); + status = check_ip_addr(pkt); if (status < 0) { return status; @@ -381,6 +384,9 @@ int net_recv_data(struct net_if *iface, struct net_pkt *pkt) return -ENETDOWN; } + net_pkt_set_overwrite(pkt, true); + net_pkt_cursor_init(pkt); + NET_DBG("prio %d iface %p pkt %p len %zu", net_pkt_priority(pkt), iface, pkt, net_pkt_get_len(pkt)); diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index 78cb5219daebf..24831f40c8d67 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -284,11 +284,7 @@ enum net_verdict net_if_send_data(struct net_if *iface, struct net_pkt *pkt) * cache. */ if (net_pkt_family(pkt) == AF_INET6) { - pkt = net_ipv6_prepare_for_send(pkt); - if (!pkt) { - verdict = NET_CONTINUE; - goto done; - } + verdict = net_ipv6_prepare_for_send(pkt); } #endif diff --git a/subsys/net/ip/net_pkt.c b/subsys/net/ip/net_pkt.c index ee9674322de2f..283e910fb78e3 100644 --- a/subsys/net/ip/net_pkt.c +++ b/subsys/net/ip/net_pkt.c @@ -81,12 +81,24 @@ LOG_MODULE_REGISTER(net_pkt, CONFIG_NET_PKT_LOG_LEVEL); #error "Too small net_buf fragment size" #endif -NET_PKT_SLAB_DEFINE(rx_pkts, CONFIG_NET_PKT_RX_COUNT); -NET_PKT_SLAB_DEFINE(tx_pkts, CONFIG_NET_PKT_TX_COUNT); +K_MEM_SLAB_DEFINE(rx_pkts, sizeof(struct net_pkt), CONFIG_NET_PKT_RX_COUNT, 4); +K_MEM_SLAB_DEFINE(tx_pkts, sizeof(struct net_pkt), CONFIG_NET_PKT_TX_COUNT, 4); -/* The data fragment pool is for storing network data. */ -NET_PKT_DATA_POOL_DEFINE(rx_bufs, CONFIG_NET_BUF_RX_COUNT); -NET_PKT_DATA_POOL_DEFINE(tx_bufs, CONFIG_NET_BUF_TX_COUNT); +#if defined(CONFIG_NET_BUF_FIXED_DATA_SIZE) + +NET_BUF_POOL_FIXED_DEFINE(rx_bufs, CONFIG_NET_BUF_RX_COUNT, + CONFIG_NET_BUF_DATA_SIZE, NULL); +NET_BUF_POOL_FIXED_DEFINE(tx_bufs, CONFIG_NET_BUF_TX_COUNT, + CONFIG_NET_BUF_DATA_SIZE, NULL); + +#else /* !CONFIG_NET_BUF_FIXED_DATA_SIZE */ + +NET_BUF_POOL_VAR_DEFINE(rx_bufs, CONFIG_NET_BUF_RX_COUNT, + CONFIG_NET_BUF_DATA_POOL_SIZE, NULL); +NET_BUF_POOL_VAR_DEFINE(tx_bufs, CONFIG_NET_BUF_TX_COUNT, + CONFIG_NET_BUF_DATA_POOL_SIZE, NULL); + +#endif /* CONFIG_NET_BUF_FIXED_DATA_SIZE */ /* Allocation tracking is only available if separately enabled */ #if defined(CONFIG_NET_DEBUG_NET_PKT_ALLOC) @@ -805,6 +817,11 @@ void net_pkt_unref(struct net_pkt *pkt) net_pkt_frag_unref(pkt->frags); } + if (IS_ENABLED(CONFIG_NET_DEBUG_NET_PKT_NON_FRAGILE_ACCESS)) { + pkt->buffer = NULL; + net_pkt_cursor_init(pkt); + } + k_mem_slab_free(pkt->slab, (void **)&pkt); } @@ -2163,7 +2180,6 @@ struct net_pkt *net_pkt_clone(struct net_pkt *pkt, s32_t timeout) sizeof(clone->lladdr_dst)); } - net_pkt_set_next_hdr(clone, NULL); net_pkt_set_ip_hdr_len(clone, net_pkt_ip_hdr_len(pkt)); net_pkt_set_vlan_tag(clone, net_pkt_vlan_tag(pkt)); net_pkt_set_appdatalen(clone, net_pkt_appdatalen(pkt)); @@ -2175,6 +2191,7 @@ struct net_pkt *net_pkt_clone(struct net_pkt *pkt, s32_t timeout) clone->ipv6_ext_len = pkt->ipv6_ext_len; clone->ipv6_ext_opt_len = pkt->ipv6_ext_opt_len; clone->ipv6_prev_hdr_start = pkt->ipv6_prev_hdr_start; + net_pkt_set_ipv6_next_hdr(clone, net_pkt_ipv6_next_hdr(pkt)); #endif NET_DBG("Cloned %p to %p", pkt, clone); @@ -2182,6 +2199,931 @@ struct net_pkt *net_pkt_clone(struct net_pkt *pkt, s32_t timeout) return clone; } +/* New allocator and API starts here */ + +#if defined(CONFIG_NET_BUF_FIXED_DATA_SIZE) + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +static struct net_buf *pkt_alloc_buffer(struct net_buf_pool *pool, + size_t size, s32_t timeout, + const char *caller, int line) +#else +static struct net_buf *pkt_alloc_buffer(struct net_buf_pool *pool, + size_t size, s32_t timeout) +#endif +{ + u32_t alloc_start = k_uptime_get_32(); + struct net_buf *first = NULL; + struct net_buf *current = NULL; + + while (size) { + struct net_buf *new; + + new = net_buf_alloc_fixed(pool, timeout); + if (!new) { + goto error; + } + + if (!first && !current) { + first = new; + } else { + current->frags = new; + } + + current = new; + if (current->size > size) { + current->size = size; + } + + size -= current->size; + + if (timeout != K_NO_WAIT && timeout != K_FOREVER) { + u32_t diff = k_uptime_get_32() - alloc_start; + + timeout -= min(timeout, diff); + } + +#if CONFIG_NET_PKT_LOG_LEVEL >= LOG_LEVEL_DBG + NET_FRAG_CHECK_IF_NOT_IN_USE(new, new->ref + 1); + + net_pkt_alloc_add(new, false, caller, line); + + NET_DBG("%s (%s) [%d] frag %p ref %d (%s():%d)", + pool2str(pool), get_name(pool), get_frees(pool), + new, new->ref, caller, line); +#endif + } + + return first; +error: + if (first) { + net_buf_unref(first); + } + + return NULL; +} + +#else /* !CONFIG_NET_BUF_FIXED_DATA_SIZE */ + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +static struct net_buf *pkt_alloc_buffer(struct net_buf_pool *pool, + size_t size, s32_t timeout, + const char *caller, int line) +#else +static struct net_buf *pkt_alloc_buffer(struct net_buf_pool *pool, + size_t size, s32_t timeout) +#endif +{ + struct net_buf *buf; + + buf = net_buf_alloc_len(pool, size, timeout); + +#if CONFIG_NET_PKT_LOG_LEVEL >= LOG_LEVEL_DBG + NET_FRAG_CHECK_IF_NOT_IN_USE(buf, buf->ref + 1); + + net_pkt_alloc_add(buf, false, caller, line); + + NET_DBG("%s (%s) [%d] frag %p ref %d (%s():%d)", + pool2str(pool), get_name(pool), get_frees(pool), + buf, buf->ref, caller, line); +#endif + + return buf; +} + +#endif /* CONFIG_NET_BUF_FIXED_DATA_SIZE */ + +static size_t pkt_buffer_length(struct net_pkt *pkt, + size_t size, + enum net_ip_protocol proto, + size_t existing) +{ + size_t max_len = net_if_get_mtu(net_pkt_iface(pkt)); + sa_family_t family = net_pkt_family(pkt); + + /* Family vs iface MTU */ + if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) { + max_len = max(max_len, NET_IPV6_MTU); + } else if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) { + max_len = max(max_len, NET_IPV4_MTU); + } + + max_len -= existing; + + return min(size, max_len); +} + +static size_t pkt_estimate_headers_length(struct net_pkt *pkt, + sa_family_t family, + enum net_ip_protocol proto) +{ + size_t hdr_len = 0; + + if (family == AF_UNSPEC) { + return 0; + } + + /* Family header */ + if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) { + hdr_len += NET_IPV6H_LEN; + } else if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) { + hdr_len += NET_IPV4H_LEN; + } + + /* + protocol header */ + if (IS_ENABLED(CONFIG_NET_TCP) && proto == IPPROTO_TCP) { + hdr_len += NET_TCPH_LEN + NET_TCP_MAX_OPT_SIZE; + } else if (IS_ENABLED(CONFIG_NET_UDP) && proto == IPPROTO_UDP) { + hdr_len += NET_UDPH_LEN; + } else if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) { + hdr_len += NET_ICMPH_LEN; + } + + NET_DBG("HDRs length estimation %zu", hdr_len); + + return hdr_len; +} + +static size_t pkt_get_size(struct net_pkt *pkt) +{ + struct net_buf *buf = pkt->buffer; + size_t size = 0; + + while (buf) { + size += buf->size; + buf = buf->frags; + } + + return size; +} + +size_t net_pkt_available_buffer(struct net_pkt *pkt) +{ + if (!pkt) { + return 0; + } + + return pkt_get_size(pkt) - net_pkt_get_len(pkt); +} + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +int net_pkt_alloc_buffer_debug(struct net_pkt *pkt, + size_t size, + enum net_ip_protocol proto, + s32_t timeout, + const char *caller, + int line) +#else +int net_pkt_alloc_buffer(struct net_pkt *pkt, + size_t size, + enum net_ip_protocol proto, + s32_t timeout) +#endif +{ + u32_t alloc_start = k_uptime_get_32(); + size_t alloc_len = 0; + size_t hdr_len = 0; + struct net_buf *buf; + + if (!size && proto == 0 && net_pkt_family(pkt) == AF_UNSPEC) { + return 0; + } + + if (k_is_in_isr()) { + timeout = K_NO_WAIT; + } + + /* Verifying existing buffer and take into account free space there */ + alloc_len = pkt_get_size(pkt) - net_pkt_get_len(pkt); + if (!alloc_len) { + /* In case of no free space, it will account for header + * space estimation + */ + hdr_len = pkt_estimate_headers_length(pkt, + net_pkt_family(pkt), + proto); + } + + /* Calculate the maximum that can be allocated depending on size */ + alloc_len = pkt_buffer_length(pkt, size + hdr_len, proto, alloc_len); + + NET_DBG("Data allocation maximum size %zu (requested %zu)", + alloc_len, size); + + if (timeout != K_NO_WAIT && timeout != K_FOREVER) { + u32_t diff = k_uptime_get_32() - alloc_start; + + timeout -= min(timeout, diff); + } + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + buf = pkt_alloc_buffer(pkt->slab == &tx_pkts ? + &tx_bufs : &rx_bufs, + alloc_len, timeout, + caller, line); +#else + buf = pkt_alloc_buffer(pkt->slab == &tx_pkts ? + &tx_bufs : &rx_bufs, + alloc_len, timeout); +#endif + + if (!buf) { + NET_ERR("Data buffer allocation failed."); + return -ENOMEM; + } + + net_pkt_append_buffer(pkt, buf); + + return 0; +} + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +static struct net_pkt *pkt_alloc(struct k_mem_slab *slab, s32_t timeout, + const char *caller, int line) +#else +static struct net_pkt *pkt_alloc(struct k_mem_slab *slab, s32_t timeout) +#endif +{ + struct net_pkt *pkt; + int ret; + + if (k_is_in_isr()) { + timeout = K_NO_WAIT; + } + + ret = k_mem_slab_alloc(slab, (void **)&pkt, timeout); + if (ret) { + return NULL; + } + + memset(pkt, 0, sizeof(struct net_pkt)); + + pkt->atomic_ref = ATOMIC_INIT(1); + pkt->slab = slab; + + if (IS_ENABLED(CONFIG_NET_IPV6)) { + net_pkt_set_ipv6_next_hdr(pkt, 255); + } + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + net_pkt_alloc_add(pkt, true, caller, line); +#endif + + net_pkt_cursor_init(pkt); + + return pkt; +} + + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +struct net_pkt *net_pkt_alloc_debug(s32_t timeout, + const char *caller, int line) +#else +struct net_pkt *net_pkt_alloc(s32_t timeout) +#endif +{ +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + return pkt_alloc(&tx_pkts, timeout, caller, line); +#else + return pkt_alloc(&tx_pkts, timeout); +#endif +} + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +struct net_pkt *net_pkt_rx_alloc_debug(s32_t timeout, + const char *caller, int line) +#else +struct net_pkt *net_pkt_rx_alloc(s32_t timeout) +#endif +{ +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + return pkt_alloc(&rx_pkts, timeout, caller, line); +#else + return pkt_alloc(&rx_pkts, timeout); +#endif +} + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +static struct net_pkt *pkt_alloc_on_iface(struct k_mem_slab *slab, + struct net_if *iface, s32_t timeout, + const char *caller, int line) +#else +static struct net_pkt *pkt_alloc_on_iface(struct k_mem_slab *slab, + struct net_if *iface, s32_t timeout) + +#endif +{ + struct net_pkt *pkt; + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + pkt = pkt_alloc(slab, timeout, caller, line); +#else + pkt = pkt_alloc(slab, timeout); +#endif + + if (pkt) { + net_pkt_set_iface(pkt, iface); + } + + return pkt; +} + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +struct net_pkt *net_pkt_alloc_on_iface_debug(struct net_if *iface, + s32_t timeout, + const char *caller, + int line) +#else +struct net_pkt *net_pkt_alloc_on_iface(struct net_if *iface, s32_t timeout) +#endif +{ +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + return pkt_alloc_on_iface(&tx_pkts, iface, timeout, caller, line); +#else + return pkt_alloc_on_iface(&tx_pkts, iface, timeout); +#endif +} + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +struct net_pkt *net_pkt_rx_alloc_on_iface_debug(struct net_if *iface, + s32_t timeout, + const char *caller, + int line) +#else +struct net_pkt *net_pkt_rx_alloc_on_iface(struct net_if *iface, s32_t timeout) +#endif +{ +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + return pkt_alloc_on_iface(&rx_pkts, iface, timeout, caller, line); +#else + return pkt_alloc_on_iface(&rx_pkts, iface, timeout); +#endif +} + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +static struct net_pkt * +pkt_alloc_with_buffer(struct k_mem_slab *slab, + struct net_if *iface, + size_t size, + sa_family_t family, + enum net_ip_protocol proto, + s32_t timeout, + const char *caller, + int line) +#else +static struct net_pkt * +pkt_alloc_with_buffer(struct k_mem_slab *slab, + struct net_if *iface, + size_t size, + sa_family_t family, + enum net_ip_protocol proto, + s32_t timeout) +#endif +{ + u32_t alloc_start = k_uptime_get_32(); + struct net_pkt *pkt; + int ret; + + NET_DBG("On iface %p size %zu", iface, size); + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + pkt = pkt_alloc_on_iface(slab, iface, timeout, caller, line); +#else + pkt = pkt_alloc_on_iface(slab, iface, timeout); +#endif + + if (!pkt) { + return NULL; + } + + net_pkt_set_family(pkt, family); + + if (timeout != K_NO_WAIT && timeout != K_FOREVER) { + u32_t diff = k_uptime_get_32() - alloc_start; + + timeout -= min(timeout, diff); + } + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + ret = net_pkt_alloc_buffer_debug(pkt, size, proto, timeout, + caller, line); +#else + ret = net_pkt_alloc_buffer(pkt, size, proto, timeout); +#endif + + if (ret) { + net_pkt_unref(pkt); + return NULL; + } + + return pkt; +} + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +struct net_pkt *net_pkt_alloc_with_buffer_debug(struct net_if *iface, + size_t size, + sa_family_t family, + enum net_ip_protocol proto, + s32_t timeout, + const char *caller, + int line) +#else +struct net_pkt *net_pkt_alloc_with_buffer(struct net_if *iface, + size_t size, + sa_family_t family, + enum net_ip_protocol proto, + s32_t timeout) +#endif +{ +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + return pkt_alloc_with_buffer(&tx_pkts, iface, size, family, + proto, timeout, caller, line); +#else + return pkt_alloc_with_buffer(&tx_pkts, iface, size, family, + proto, timeout); +#endif +} + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +struct net_pkt *net_pkt_rx_alloc_with_buffer_debug(struct net_if *iface, + size_t size, + sa_family_t family, + enum net_ip_protocol proto, + s32_t timeout, + const char *caller, + int line) +#else +struct net_pkt *net_pkt_rx_alloc_with_buffer(struct net_if *iface, + size_t size, + sa_family_t family, + enum net_ip_protocol proto, + s32_t timeout) +#endif +{ +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + return pkt_alloc_with_buffer(&rx_pkts, iface, size, family, + proto, timeout, caller, line); +#else + return pkt_alloc_with_buffer(&rx_pkts, iface, size, family, + proto, timeout); +#endif +} + +void net_pkt_append_buffer(struct net_pkt *pkt, struct net_buf *buffer) +{ + if (!pkt->buffer) { + pkt->buffer = buffer; + net_pkt_cursor_init(pkt); + } else { + net_buf_frag_insert(net_buf_frag_last(pkt->buffer), buffer); + } +} + +void net_pkt_cursor_init(struct net_pkt *pkt) +{ + pkt->cursor.buf = pkt->buffer; + if (pkt->cursor.buf) { + pkt->cursor.pos = pkt->cursor.buf->data; + } else { + pkt->cursor.pos = NULL; + } +} + +static void pkt_cursor_jump(struct net_pkt *pkt, bool write) +{ + struct net_pkt_cursor *cursor = &pkt->cursor; + + cursor->buf = cursor->buf->frags; + while (cursor->buf) { + size_t len = write ? cursor->buf->size : cursor->buf->len; + + if (!len) { + cursor->buf = cursor->buf->frags; + } else { + break; + } + } + + if (cursor->buf) { + cursor->pos = cursor->buf->data; + } else { + cursor->pos = NULL; + } +} + +static void pkt_cursor_advance(struct net_pkt *pkt, bool write) +{ + struct net_pkt_cursor *cursor = &pkt->cursor; + size_t len; + + if (!cursor->buf) { + return; + } + + len = write ? cursor->buf->size : cursor->buf->len; + if ((cursor->pos - cursor->buf->data) == len) { + pkt_cursor_jump(pkt, write); + } +} + +static void pkt_cursor_update(struct net_pkt *pkt, + size_t length, bool write) +{ + struct net_pkt_cursor *cursor = &pkt->cursor; + size_t len; + + if (net_pkt_is_being_overwritten(pkt)) { + write = false; + } + + len = write ? cursor->buf->size : cursor->buf->len; + if (length + (cursor->pos - cursor->buf->data) == len && + !(net_pkt_is_being_overwritten(pkt) && len < cursor->buf->size)) { + pkt_cursor_jump(pkt, write); + } else { + cursor->pos += length; + } +} + +/* Internal function that does all operation (skip/read/write/memset) */ +static int net_pkt_cursor_operate(struct net_pkt *pkt, + void *data, size_t length, + bool copy, bool write) +{ + /* We use such variable to avoid lengthy lines */ + struct net_pkt_cursor *c_op = &pkt->cursor; + + while (c_op->buf && length) { + size_t d_len, len; + + pkt_cursor_advance(pkt, net_pkt_is_being_overwritten(pkt) ? + false : write); + if (c_op->buf == NULL) { + break; + } + + if (write && !net_pkt_is_being_overwritten(pkt)) { + d_len = c_op->buf->size - (c_op->pos - c_op->buf->data); + } else { + d_len = c_op->buf->len - (c_op->pos - c_op->buf->data); + } + + if (!d_len) { + break; + } + + if (length < d_len) { + len = length; + } else { + len = d_len; + } + + if (copy) { + memcpy(write ? c_op->pos : data, + write ? data : c_op->pos, + len); + } else if (data) { + memset(c_op->pos, *(int *)data, len); + } + + if (write && !net_pkt_is_being_overwritten(pkt)) { + net_buf_add(c_op->buf, len); + } + + pkt_cursor_update(pkt, len, write); + + if (copy && data) { + data = (u8_t *) data + len; + } + + length -= len; + } + + if (length) { + NET_DBG("Still some length to go %zu", length); + return -ENOBUFS; + } + + return 0; +} + +int net_pkt_skip(struct net_pkt *pkt, size_t skip) +{ + NET_DBG("pkt %p skip %zu", pkt, skip); + + return net_pkt_cursor_operate(pkt, NULL, skip, false, true); +} + +int net_pkt_memset(struct net_pkt *pkt, int byte, size_t amount) +{ + NET_DBG("pkt %p byte %d amount %zu", pkt, byte, amount); + + return net_pkt_cursor_operate(pkt, &byte, amount, false, true); +} + +int net_pkt_read_new(struct net_pkt *pkt, void *data, size_t length) +{ + NET_DBG("pkt %p data %p length %zu", pkt, data, length); + + return net_pkt_cursor_operate(pkt, data, length, true, false); +} + +int net_pkt_read_be16_new(struct net_pkt *pkt, u16_t *data) +{ + u8_t d16[2]; + int ret; + + ret = net_pkt_read_new(pkt, d16, sizeof(u16_t)); + + *data = d16[0] << 8 | d16[1]; + + return ret; +} + +int net_pkt_read_be32_new(struct net_pkt *pkt, u32_t *data) +{ + u8_t d32[4]; + int ret; + + ret = net_pkt_read_new(pkt, d32, sizeof(u32_t)); + + *data = d32[0] << 24 | d32[1] << 16 | d32[2] << 8 | d32[3]; + + return ret; +} + +int net_pkt_write_new(struct net_pkt *pkt, const void *data, size_t length) +{ + NET_DBG("pkt %p data %p length %zu", pkt, data, length); + + if (data == pkt->cursor.pos && net_pkt_is_contiguous(pkt, length)) { + return net_pkt_skip(pkt, length); + } + + return net_pkt_cursor_operate(pkt, (void *)data, length, true, true); +} + +int net_pkt_copy_new(struct net_pkt *pkt_dst, + struct net_pkt *pkt_src, + size_t length) +{ + struct net_pkt_cursor *c_dst = &pkt_dst->cursor; + struct net_pkt_cursor *c_src = &pkt_src->cursor; + + while (c_dst->buf && c_src->buf && length) { + size_t s_len, d_len, len; + + pkt_cursor_advance(pkt_dst, true); + pkt_cursor_advance(pkt_src, false); + + if (!c_dst->buf || !c_src->buf) { + break; + } + + s_len = c_src->buf->len - (c_src->pos - c_src->buf->data); + d_len = c_dst->buf->size - (c_dst->pos - c_dst->buf->data); + if (length < s_len && length < d_len) { + len = length; + } else { + if (d_len < s_len) { + len = d_len; + } else { + len = s_len; + } + } + + if (!len) { + break; + } + + memcpy(c_dst->pos, c_src->pos, len); + + if (!net_pkt_is_being_overwritten(pkt_dst)) { + net_buf_add(c_dst->buf, len); + } + + pkt_cursor_update(pkt_dst, len, true); + pkt_cursor_update(pkt_src, len, false); + + length -= len; + } + + if (length) { + NET_DBG("Still some length to go %zu", length); + return -ENOBUFS; + } + + return 0; +} + +struct net_pkt *net_pkt_clone_new(struct net_pkt *pkt, s32_t timeout) +{ + struct net_pkt *clone_pkt; + + clone_pkt = net_pkt_alloc_with_buffer(net_pkt_iface(pkt), + net_pkt_get_len(pkt), + AF_UNSPEC, 0, timeout); + if (!clone_pkt) { + return NULL; + } + + net_pkt_cursor_init(pkt); + + if (net_pkt_copy_new(clone_pkt, pkt, net_pkt_get_len(pkt))) { + net_pkt_unref(clone_pkt); + return NULL; + } + + if (clone_pkt->buffer) { + /* The link header pointers are only usable if there is + * a buffer that we copied because those pointers point + * to start of the fragment which we do not have right now. + */ + memcpy(&clone_pkt->lladdr_src, &pkt->lladdr_src, + sizeof(clone_pkt->lladdr_src)); + memcpy(&clone_pkt->lladdr_dst, &pkt->lladdr_dst, + sizeof(clone_pkt->lladdr_dst)); + } + + net_pkt_set_family(clone_pkt, net_pkt_family(pkt)); + net_pkt_set_context(clone_pkt, net_pkt_context(pkt)); + net_pkt_set_token(clone_pkt, net_pkt_token(pkt)); + net_pkt_set_ip_hdr_len(clone_pkt, net_pkt_ip_hdr_len(pkt)); + net_pkt_set_vlan_tag(clone_pkt, net_pkt_vlan_tag(pkt)); + net_pkt_set_timestamp(clone_pkt, net_pkt_timestamp(pkt)); + net_pkt_set_priority(clone_pkt, net_pkt_priority(pkt)); + net_pkt_set_orig_iface(clone_pkt, net_pkt_orig_iface(pkt)); + + if (IS_ENABLED(CONFIG_NET_IPV4) && net_pkt_family(pkt) == AF_INET) { + net_pkt_set_ipv4_ttl(clone_pkt, net_pkt_ipv4_ttl(pkt)); + } else if (IS_ENABLED(CONFIG_NET_IPV6) && + net_pkt_family(pkt) == AF_INET6) { + net_pkt_set_ipv6_hop_limit(clone_pkt, + net_pkt_ipv6_hop_limit(pkt)); + net_pkt_set_ipv6_ext_len(clone_pkt, net_pkt_ipv6_ext_len(pkt)); + net_pkt_set_ipv6_ext_opt_len(clone_pkt, + net_pkt_ipv6_ext_opt_len(pkt)); + net_pkt_set_ipv6_hdr_prev(clone_pkt, + net_pkt_ipv6_hdr_prev(pkt)); + net_pkt_set_ipv6_next_hdr(clone_pkt, + net_pkt_ipv6_next_hdr(pkt)); + } + + net_pkt_cursor_init(clone_pkt); + + NET_DBG("Cloned %p to %p", pkt, clone_pkt); + + return clone_pkt; +} + +int net_pkt_update_length(struct net_pkt *pkt, size_t length) +{ + struct net_buf *buf; + + for (buf = pkt->buffer; buf; buf = buf->frags) { + if (buf->len < length) { + length -= buf->len; + } else { + buf->len = length; + length = 0; + } + } + + return !length ? 0 : -EINVAL; +} + +int net_pkt_pull_new(struct net_pkt *pkt, size_t length) +{ + struct net_pkt_cursor *c_op = &pkt->cursor; + struct net_pkt_cursor backup; + + net_pkt_cursor_backup(pkt, &backup); + + while (length) { + u8_t left, rem; + + pkt_cursor_advance(pkt, false); + + left = c_op->buf->len - (c_op->pos - c_op->buf->data); + if (!left) { + break; + } + + rem = left; + if (rem > length) { + rem = length; + } + + c_op->buf->len -= rem; + left -= rem; + if (left) { + memmove(c_op->pos, c_op->pos+rem, rem); + } + + /* For now, empty buffer are not freed, and there is no + * compaction done either. + * net_pkt_pull_new() is currently used only in very specific + * places where such memory optimization would not make + * that much sense. Let's see in future if it's worth do to it. + */ + + length -= rem; + } + + net_pkt_cursor_restore(pkt, &backup); + + if (length) { + NET_DBG("Still some length to go %zu", length); + return -ENOBUFS; + } + + return 0; +} + +u16_t net_pkt_get_current_offset(struct net_pkt *pkt) +{ + struct net_buf *buf = pkt->buffer; + u16_t offset; + + if (!pkt->cursor.buf || !pkt->cursor.pos) { + return 0; + } + + offset = 0; + + while (buf != pkt->cursor.buf) { + offset += buf->len; + buf = buf->frags; + } + + offset += pkt->cursor.pos - buf->data; + + return offset; +} + +bool net_pkt_is_contiguous(struct net_pkt *pkt, size_t size) +{ + if (pkt->cursor.buf && pkt->cursor.pos) { + size_t len; + + len = net_pkt_is_being_overwritten(pkt) ? + pkt->cursor.buf->len : pkt->cursor.buf->size; + len -= pkt->cursor.pos - pkt->cursor.buf->data; + if (len >= size) { + return true; + } + } + + return false; +} + +void *net_pkt_get_data_new(struct net_pkt *pkt, + struct net_pkt_data_access *access) +{ + if (IS_ENABLED(CONFIG_NET_HEADERS_ALWAYS_CONTIGUOUS)) { + if (!net_pkt_is_contiguous(pkt, access->size)) { + return NULL; + } + + return pkt->cursor.pos; + } else { + if (net_pkt_is_contiguous(pkt, access->size)) { + access->data = pkt->cursor.pos; + } else if (net_pkt_is_being_overwritten(pkt)) { + struct net_pkt_cursor backup; + + if (!access->data) { + NET_ERR("Uncontiguous data" + " cannot be linearized"); + return NULL; + } + + net_pkt_cursor_backup(pkt, &backup); + + if (net_pkt_read_new(pkt, access->data, access->size)) { + net_pkt_cursor_restore(pkt, &backup); + return NULL; + } + + net_pkt_cursor_restore(pkt, &backup); + } + + return access->data; + } + + return NULL; +} + +int net_pkt_set_data(struct net_pkt *pkt, + struct net_pkt_data_access *access) +{ + if (IS_ENABLED(CONFIG_NET_HEADERS_ALWAYS_CONTIGUOUS)) { + return net_pkt_skip(pkt, access->size); + } + + return net_pkt_write_new(pkt, access->data, access->size); +} + void net_pkt_init(void) { #if CONFIG_NET_PKT_LOG_LEVEL >= LOG_LEVEL_DBG diff --git a/subsys/net/ip/net_private.h b/subsys/net/ip/net_private.h index 95bfbde54b148..91052a57beb54 100644 --- a/subsys/net/ip/net_private.h +++ b/subsys/net/ip/net_private.h @@ -43,8 +43,8 @@ extern void net_if_init(void); extern void net_if_post_init(void); extern void net_if_carrier_down(struct net_if *iface); extern void net_context_init(void); -enum net_verdict net_ipv4_process_pkt(struct net_pkt *pkt); -enum net_verdict net_ipv6_process_pkt(struct net_pkt *pkt, bool is_loopback); +enum net_verdict net_ipv4_input(struct net_pkt *pkt); +enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback); extern void net_tc_tx_init(void); extern void net_tc_rx_init(void); extern void net_tc_submit_to_tx_queue(u8_t tc, struct net_pkt *pkt); @@ -147,6 +147,8 @@ void net_pkt_set_appdata_values(struct net_pkt *pkt, enum net_verdict net_context_packet_received(struct net_conn *conn, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, void *user_data); #if defined(CONFIG_NET_IPV4) diff --git a/subsys/net/ip/net_shell.c b/subsys/net/ip/net_shell.c index 8d17f1ac02f50..abc0a9c53bee6 100644 --- a/subsys/net/ip/net_shell.c +++ b/subsys/net/ip/net_shell.c @@ -2741,7 +2741,9 @@ static const struct shell *shell_for_ping; #if defined(CONFIG_NET_IPV6) -static enum net_verdict _handle_ipv6_echo_reply(struct net_pkt *pkt); +static enum net_verdict _handle_ipv6_echo_reply(struct net_pkt *pkt, + struct net_ipv6_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr); static struct net_icmpv6_handler ping6_handler = { .type = NET_ICMPV6_ECHO_REPLY, @@ -2754,11 +2756,15 @@ static inline void _remove_ipv6_ping_handler(void) net_icmpv6_unregister_handler(&ping6_handler); } -static enum net_verdict _handle_ipv6_echo_reply(struct net_pkt *pkt) +static enum net_verdict _handle_ipv6_echo_reply(struct net_pkt *pkt, + struct net_ipv6_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr) { + ARG_UNUSED(icmp_hdr); + PR_SHELL(shell_for_ping, "Received echo reply from %s to %s\n", - net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->src), - net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->dst)); + net_sprint_ipv6_addr(&ip_hdr->src), + net_sprint_ipv6_addr(&ip_hdr->dst)); k_sem_give(&ping_timeout); _remove_ipv6_ping_handler(); @@ -2814,7 +2820,8 @@ static int _ping_ipv6(const struct shell *shell, char *host) #if defined(CONFIG_NET_IPV4) -static enum net_verdict _handle_ipv4_echo_reply(struct net_pkt *pkt); +static enum net_verdict _handle_ipv4_echo_reply(struct net_pkt *pkt, + struct net_ipv4_hdr *ip_hdr); static struct net_icmpv4_handler ping4_handler = { .type = NET_ICMPV4_ECHO_REPLY, @@ -2827,11 +2834,12 @@ static inline void _remove_ipv4_ping_handler(void) net_icmpv4_unregister_handler(&ping4_handler); } -static enum net_verdict _handle_ipv4_echo_reply(struct net_pkt *pkt) +static enum net_verdict _handle_ipv4_echo_reply(struct net_pkt *pkt, + struct net_ipv4_hdr *ip_hdr) { PR_SHELL(shell_for_ping, "Received echo reply from %s to %s\n", - net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->src), - net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->dst)); + net_sprint_ipv4_addr(&ip_hdr->src), + net_sprint_ipv4_addr(&ip_hdr->dst)); k_sem_give(&ping_timeout); _remove_ipv4_ping_handler(); diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 62ce461d574e5..8d02e34485f2e 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -70,23 +70,30 @@ static struct tcp_backlog_entry { * pointers and doesn't understand what a net_context is. */ #define NET_CONN_CB(name) \ - static enum net_verdict _##name(struct net_conn *conn, \ - struct net_pkt *pkt, \ - void *user_data); \ - static enum net_verdict name(struct net_conn *conn, \ - struct net_pkt *pkt, \ - void *user_data) \ - { \ - enum net_verdict result; \ - \ - net_context_ref(user_data); \ - result = _##name(conn, pkt, user_data); \ - net_context_unref(user_data); \ - return result; \ - } \ - static enum net_verdict _##name(struct net_conn *conn, \ - struct net_pkt *pkt, \ - void *user_data) \ + static enum net_verdict _##name(struct net_conn *conn, \ + struct net_pkt *pkt, \ + union net_ip_header *ip_hdr, \ + union net_proto_header *proto_hdr, \ + void *user_data); \ + static enum net_verdict name(struct net_conn *conn, \ + struct net_pkt *pkt, \ + union net_ip_header *ip_hdr, \ + union net_proto_header *proto_hdr, \ + void *user_data) \ + { \ + enum net_verdict result; \ + \ + net_context_ref(user_data); \ + result = _##name(conn, pkt, ip_hdr, \ + proto_hdr, user_data); \ + net_context_unref(user_data); \ + return result; \ + } \ + static enum net_verdict _##name(struct net_conn *conn, \ + struct net_pkt *pkt, \ + union net_ip_header *ip_hdr, \ + union net_proto_header *proto_hdr, \ + void *user_data) \ struct tcp_segment { @@ -109,9 +116,10 @@ static char upper_if_set(char chr, bool set) return chr | 0x20; } -static void net_tcp_trace(struct net_pkt *pkt, struct net_tcp *tcp) +static void net_tcp_trace(struct net_pkt *pkt, + struct net_tcp *tcp, + struct net_tcp_hdr *tcp_hdr) { - struct net_tcp_hdr hdr, *tcp_hdr; u32_t rel_ack, ack; u8_t flags; @@ -119,11 +127,6 @@ static void net_tcp_trace(struct net_pkt *pkt, struct net_tcp *tcp) return; } - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); - if (!tcp_hdr) { - return; - } - flags = NET_TCP_FLAGS(tcp_hdr); ack = sys_get_be32(tcp_hdr->ack); @@ -199,7 +202,8 @@ static void abort_connection(struct net_tcp *tcp) tcp, CONFIG_NET_TCP_RETRY_COUNT, ctx); if (ctx->recv_cb) { - ctx->recv_cb(ctx, NULL, -ECONNRESET, tcp->recv_user_data); + ctx->recv_cb(ctx, NULL, NULL, NULL, -ECONNRESET, + tcp->recv_user_data); } net_context_unref(ctx); @@ -345,40 +349,19 @@ int net_tcp_release(struct net_tcp *tcp) return 0; } -static inline u8_t net_tcp_add_options(struct net_buf *header, size_t len, - void *data) +static int finalize_segment(struct net_pkt *pkt) { - u8_t optlen; - - memcpy(net_buf_add(header, len), data, len); + net_pkt_cursor_init(pkt); - /* Set the length (this value is saved in 4-byte words format) */ - if ((len & 0x3u) != 0u) { - optlen = (len & 0xfffCu) + 4u; - } else { - optlen = len; - } - - return optlen; -} - -static int finalize_segment(struct net_context *context, struct net_pkt *pkt) -{ -#if defined(CONFIG_NET_IPV4) - if (net_pkt_family(pkt) == AF_INET) { - net_ipv4_finalize(pkt, net_context_get_ip_proto(context)); - } else -#endif -#if defined(CONFIG_NET_IPV6) - if (net_pkt_family(pkt) == AF_INET6) { - return net_ipv6_finalize(pkt, - net_context_get_ip_proto(context)); - } -#endif - { + if (IS_ENABLED(CONFIG_NET_IPV4) && + net_pkt_family(pkt) == AF_INET) { + return net_ipv4_finalize_new(pkt, IPPROTO_TCP); + } else if (IS_ENABLED(CONFIG_NET_IPV6) && + net_pkt_family(pkt) == AF_INET6) { + return net_ipv6_finalize_new(pkt, IPPROTO_TCP); } - return 0; + return -EINVAL; } static int prepare_segment(struct net_tcp *tcp, @@ -386,10 +369,10 @@ static int prepare_segment(struct net_tcp *tcp, struct net_pkt *pkt, struct net_pkt **out_pkt) { - struct net_buf *header, *tail = NULL; + NET_PKT_DATA_ACCESS_DEFINE(tcp_access, struct net_tcp_hdr); struct net_context *context = tcp->context; + struct net_buf *tail = NULL; struct net_tcp_hdr *tcp_hdr; - struct net_pkt *alloc_pkt; u16_t dst_port, src_port; bool pkt_allocated; u8_t optlen = 0U; @@ -403,51 +386,53 @@ static int prepare_segment(struct net_tcp *tcp, * the context), and the data after. Rejigger so we * can insert a TCP header cleanly */ - tail = pkt->frags; - pkt->frags = NULL; + tail = pkt->buffer; + pkt->buffer = NULL; pkt_allocated = false; + + status = net_pkt_alloc_buffer(pkt, segment->optlen, + IPPROTO_TCP, ALLOC_TIMEOUT); + if (status) { + goto fail; + } } else { - pkt = net_pkt_get_tx(context, ALLOC_TIMEOUT); + pkt = net_pkt_alloc_with_buffer(net_context_get_iface(context), + segment->optlen, + net_context_get_family(context), + IPPROTO_TCP, ALLOC_TIMEOUT); if (!pkt) { return -ENOMEM; } + net_pkt_set_context(pkt, context); pkt_allocated = true; } -#if defined(CONFIG_NET_IPV4) - if (net_pkt_family(pkt) == AF_INET) { - alloc_pkt = net_context_create_ipv4(context, pkt, - net_sin_ptr(segment->src_addr)->sin_addr, - &(net_sin(segment->dst_addr)->sin_addr)); - if (!alloc_pkt) { - status = -ENOMEM; + if (IS_ENABLED(CONFIG_NET_IPV4) && + net_pkt_family(pkt) == AF_INET) { + status = net_context_create_ipv4_new(context, pkt, + net_sin_ptr(segment->src_addr)->sin_addr, + &(net_sin(segment->dst_addr)->sin_addr)); + if (status < 0) { goto fail; } dst_port = net_sin(segment->dst_addr)->sin_port; src_port = ((struct sockaddr_in_ptr *)&context->local)-> sin_port; - NET_IPV4_HDR(pkt)->proto = IPPROTO_TCP; - } else -#endif -#if defined(CONFIG_NET_IPV6) - if (net_pkt_family(pkt) == AF_INET6) { - alloc_pkt = net_context_create_ipv6(tcp->context, pkt, - net_sin6_ptr(segment->src_addr)->sin6_addr, - &(net_sin6(segment->dst_addr)->sin6_addr)); - if (!alloc_pkt) { - status = -ENOMEM; + } else if (IS_ENABLED(CONFIG_NET_IPV6) && + net_pkt_family(pkt) == AF_INET6) { + status = net_context_create_ipv6_new(context, pkt, + net_sin6_ptr(segment->src_addr)->sin6_addr, + &(net_sin6(segment->dst_addr)->sin6_addr)); + if (status < 0) { goto fail; } dst_port = net_sin6(segment->dst_addr)->sin6_port; src_port = ((struct sockaddr_in6_ptr *)&context->local)-> sin6_port; - NET_IPV6_HDR(pkt)->nexthdr = IPPROTO_TCP; - } else -#endif - { + } else { NET_DBG("[%p] Protocol family %d not supported", tcp, net_pkt_family(pkt)); @@ -455,39 +440,47 @@ static int prepare_segment(struct net_tcp *tcp, goto fail; } - header = net_pkt_get_data(context, ALLOC_TIMEOUT); - if (!header) { - NET_WARN("[%p] Unable to alloc TCP header", tcp); - - status = -ENOMEM; + tcp_hdr = (struct net_tcp_hdr *)net_pkt_get_data_new(pkt, &tcp_access); + if (!tcp_hdr) { + status = -ENOBUFS; goto fail; } - net_pkt_frag_add(pkt, header); - - tcp_hdr = (struct net_tcp_hdr *)net_buf_add(header, NET_TCPH_LEN); - if (segment->options && segment->optlen) { - optlen = net_tcp_add_options(header, segment->optlen, - segment->options); + /* Set the length (this value is saved in 4-byte words format) + */ + if ((segment->optlen & 0x3u) != 0u) { + optlen = (segment->optlen & 0xfffCu) + 4u; + } else { + optlen = segment->optlen; + } } - tcp_hdr->offset = (NET_TCPH_LEN + optlen) << 2; + memset(tcp_hdr, 0, NET_TCPH_LEN); tcp_hdr->src_port = src_port; tcp_hdr->dst_port = dst_port; sys_put_be32(segment->seq, tcp_hdr->seq); sys_put_be32(segment->ack, tcp_hdr->ack); - tcp_hdr->flags = segment->flags; + tcp_hdr->offset = (NET_TCPH_LEN + optlen) << 2; + tcp_hdr->flags = segment->flags; sys_put_be16(segment->wnd, tcp_hdr->wnd); - tcp_hdr->urg[0] = 0; - tcp_hdr->urg[1] = 0; + tcp_hdr->chksum = 0; + tcp_hdr->urg[0] = 0; + tcp_hdr->urg[1] = 0; + + net_pkt_set_data(pkt, &tcp_access); + + if (optlen && + net_pkt_write_new(pkt, segment->options, segment->optlen)) { + goto fail; + } if (tail) { - net_pkt_frag_add(pkt, tail); + net_pkt_append_buffer(pkt, tail); } - status = finalize_segment(context, pkt); + status = finalize_segment(pkt); if (status < 0) { if (pkt_allocated) { net_pkt_unref(pkt); @@ -496,7 +489,7 @@ static int prepare_segment(struct net_tcp *tcp, return status; } - net_tcp_trace(pkt, tcp); + net_tcp_trace(pkt, tcp, tcp_hdr); *out_pkt = pkt; @@ -506,7 +499,8 @@ static int prepare_segment(struct net_tcp *tcp, if (pkt_allocated) { net_pkt_unref(pkt); } else { - pkt->frags = tail; + net_buf_unref(pkt->buffer); + pkt->buffer = tail; } return status; @@ -523,9 +517,9 @@ int net_tcp_prepare_segment(struct net_tcp *tcp, u8_t flags, const struct sockaddr *remote, struct net_pkt **send_pkt) { + struct tcp_segment segment = { 0 }; u32_t seq; u16_t wnd; - struct tcp_segment segment = { 0 }; int status; if (!local) { @@ -864,11 +858,26 @@ static int net_tcp_queue_pkt(struct net_context *context, struct net_pkt *pkt) int net_tcp_send_pkt(struct net_pkt *pkt) { + NET_PKT_DATA_ACCESS_DEFINE(tcp_access, struct net_tcp_hdr); struct net_context *ctx = net_pkt_context(pkt); - struct net_tcp_hdr hdr, *tcp_hdr; + struct net_tcp_hdr *tcp_hdr; bool calc_chksum = false; - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); + if (!ctx || !ctx->tcp) { + NET_ERR("%scontext is not set on pkt %p", + !ctx ? "" : "TCP ", pkt); + return -EINVAL; + } + + net_pkt_cursor_init(pkt); + net_pkt_set_overwrite(pkt, true); + + if (net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt) + + net_pkt_ipv6_ext_len(pkt))) { + return -EMSGSIZE; + } + + tcp_hdr = (struct net_tcp_hdr *)net_pkt_get_data_new(pkt, &tcp_access); if (!tcp_hdr) { NET_ERR("Packet %p does not contain TCP header", pkt); return -EMSGSIZE; @@ -876,6 +885,7 @@ int net_tcp_send_pkt(struct net_pkt *pkt) if (sys_get_be32(tcp_hdr->ack) != ctx->tcp->send_ack) { sys_put_be32(ctx->tcp->send_ack, tcp_hdr->ack); + tcp_hdr->chksum = 0; calc_chksum = true; } @@ -887,11 +897,23 @@ int net_tcp_send_pkt(struct net_pkt *pkt) if (ctx->tcp->sent_ack != ctx->tcp->send_ack && (tcp_hdr->flags & NET_TCP_ACK) == 0) { tcp_hdr->flags |= NET_TCP_ACK; + tcp_hdr->chksum = 0; calc_chksum = true; } + /* As we modified the header, we need to write it back. + */ + net_pkt_set_data(pkt, &tcp_access); + if (calc_chksum) { - net_tcp_set_chksum(pkt, pkt->frags); + net_pkt_cursor_init(pkt); + net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt) + + net_pkt_ipv6_ext_len(pkt)); + + /* No need to get tcp_hdr again */ + tcp_hdr->chksum = net_calc_chksum_tcp(pkt); + + net_pkt_set_data(pkt, &tcp_access); } if (tcp_hdr->flags & NET_TCP_FIN) { @@ -900,10 +922,6 @@ int net_tcp_send_pkt(struct net_pkt *pkt) ctx->tcp->sent_ack = ctx->tcp->send_ack; - /* As we modified the header, we need to write it back. - */ - net_tcp_set_hdr(pkt, tcp_hdr); - /* We must have special handling for some network technologies that * tweak the IP protocol headers during packet sending. This happens * with Bluetooth and IEEE 802.15.4 which use IPv6 header compression @@ -932,7 +950,7 @@ int net_tcp_send_pkt(struct net_pkt *pkt) } if (pkt_in_slist) { - new_pkt = net_pkt_clone(pkt, ALLOC_TIMEOUT); + new_pkt = net_pkt_clone_new(pkt, ALLOC_TIMEOUT); if (!new_pkt) { return -ENOMEM; } @@ -1041,14 +1059,26 @@ bool net_tcp_ack_received(struct net_context *ctx, u32_t ack) } while (!sys_slist_is_empty(list)) { - struct net_tcp_hdr hdr, *tcp_hdr; + NET_PKT_DATA_ACCESS_DEFINE(tcp_access, struct net_tcp_hdr); + struct net_tcp_hdr *tcp_hdr; u32_t last_seq; u32_t seq_len; head = sys_slist_peek_head(list); pkt = CONTAINER_OF(head, struct net_pkt, sent_list); - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); + net_pkt_cursor_init(pkt); + net_pkt_set_overwrite(pkt, true); + + if (net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt) + + net_pkt_ipv6_ext_len(pkt))) { + sys_slist_remove(list, NULL, head); + net_pkt_unref(pkt); + continue; + } + + tcp_hdr = (struct net_tcp_hdr *)net_pkt_get_data_new( + pkt, &tcp_access); if (!tcp_hdr) { /* The pkt does not contain TCP header, this should * not happen. @@ -1227,15 +1257,8 @@ void net_tcp_foreach(net_tcp_cb_t cb, void *user_data) irq_unlock(key); } -bool net_tcp_validate_seq(struct net_tcp *tcp, struct net_pkt *pkt) +bool net_tcp_validate_seq(struct net_tcp *tcp, struct net_tcp_hdr *tcp_hdr) { - struct net_tcp_hdr hdr, *tcp_hdr; - - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); - if (!tcp_hdr) { - return false; - } - return (net_tcp_seq_cmp(sys_get_be32(tcp_hdr->seq), tcp->send_ack) >= 0) && (net_tcp_seq_cmp(sys_get_be32(tcp_hdr->seq), @@ -1246,109 +1269,83 @@ bool net_tcp_validate_seq(struct net_tcp *tcp, struct net_pkt *pkt) struct net_tcp_hdr *net_tcp_get_hdr(struct net_pkt *pkt, struct net_tcp_hdr *hdr) { + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(tcp_access, struct net_tcp_hdr); + struct net_pkt_cursor backup; struct net_tcp_hdr *tcp_hdr; - struct net_buf *frag; - u16_t pos; + bool overwrite; - tcp_hdr = net_pkt_tcp_data(pkt); - if (!tcp_hdr) { - NET_ERR("NULL TCP header!"); - return NULL; - } + tcp_access.data = hdr; - if (net_tcp_header_fits(pkt, tcp_hdr)) { - return tcp_hdr; - } + overwrite = net_pkt_is_being_overwritten(pkt); + net_pkt_set_overwrite(pkt, true); - frag = net_frag_read(pkt->frags, net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt), - &pos, sizeof(hdr->src_port), - (u8_t *)&hdr->src_port); - frag = net_frag_read(frag, pos, &pos, sizeof(hdr->dst_port), - (u8_t *)&hdr->dst_port); - frag = net_frag_read(frag, pos, &pos, sizeof(hdr->seq), hdr->seq); - frag = net_frag_read(frag, pos, &pos, sizeof(hdr->ack), hdr->ack); - frag = net_frag_read_u8(frag, pos, &pos, &hdr->offset); - frag = net_frag_read_u8(frag, pos, &pos, &hdr->flags); - frag = net_frag_read(frag, pos, &pos, sizeof(hdr->wnd), hdr->wnd); - frag = net_frag_read(frag, pos, &pos, sizeof(hdr->chksum), - (u8_t *)&hdr->chksum); - frag = net_frag_read(frag, pos, &pos, sizeof(hdr->urg), hdr->urg); - - if (!frag && pos == 0xffff) { - /* If the pkt is compressed, then this is the typical outcome - * so no use printing error in this case. - */ - if ((CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG) && - !is_6lo_technology(pkt)) { - NET_ASSERT(frag); - } + net_pkt_cursor_backup(pkt, &backup); + net_pkt_cursor_init(pkt); - return NULL; + if (net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt) + + net_pkt_ipv6_ext_len(pkt))) { + tcp_hdr = NULL; + goto out; } - return hdr; + tcp_hdr = (struct net_tcp_hdr *)net_pkt_get_data_new(pkt, &tcp_access); + +out: + net_pkt_cursor_restore(pkt, &backup); + net_pkt_set_overwrite(pkt, overwrite); + + return tcp_hdr; } struct net_tcp_hdr *net_tcp_set_hdr(struct net_pkt *pkt, struct net_tcp_hdr *hdr) { - struct net_buf *frag; - u16_t pos; + NET_PKT_DATA_ACCESS_DEFINE(tcp_access, struct net_tcp_hdr); + struct net_pkt_cursor backup; + struct net_tcp_hdr *tcp_hdr; + bool overwrite; - if (net_tcp_header_fits(pkt, hdr)) { - return hdr; - } + overwrite = net_pkt_is_being_overwritten(pkt); + net_pkt_set_overwrite(pkt, true); - frag = net_pkt_write(pkt, pkt->frags, net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt), - &pos, sizeof(hdr->src_port), - (u8_t *)&hdr->src_port, ALLOC_TIMEOUT); - frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->dst_port), - (u8_t *)&hdr->dst_port, ALLOC_TIMEOUT); - frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->seq), hdr->seq, - ALLOC_TIMEOUT); - frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->ack), hdr->ack, - ALLOC_TIMEOUT); - frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->offset), - &hdr->offset, ALLOC_TIMEOUT); - frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->flags), - &hdr->flags, ALLOC_TIMEOUT); - frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->wnd), hdr->wnd, - ALLOC_TIMEOUT); - frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->chksum), - (u8_t *)&hdr->chksum, ALLOC_TIMEOUT); - frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(hdr->urg), hdr->urg, - ALLOC_TIMEOUT); + net_pkt_cursor_backup(pkt, &backup); + net_pkt_cursor_init(pkt); - if (!frag) { - NET_ASSERT(frag); - return NULL; + if (net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt) + + net_pkt_ipv6_ext_len(pkt))) { + tcp_hdr = NULL; + goto out; + } + + tcp_hdr = (struct net_tcp_hdr *)net_pkt_get_data_new(pkt, &tcp_access); + if (!tcp_hdr) { + goto out; } - return hdr; + memcpy(tcp_hdr, hdr, sizeof(struct net_tcp_hdr)); + + net_pkt_set_data(pkt, &tcp_access); +out: + net_pkt_cursor_restore(pkt, &backup); + net_pkt_set_overwrite(pkt, overwrite); + + return tcp_hdr == NULL ? NULL : hdr; } -u16_t net_tcp_get_chksum(struct net_pkt *pkt, struct net_buf *frag) +int net_tcp_finalize(struct net_pkt *pkt) { - struct net_tcp_hdr *hdr; - u16_t chksum; - u16_t pos; + NET_PKT_DATA_ACCESS_DEFINE(tcp_access, struct net_tcp_hdr); + struct net_tcp_hdr *tcp_hdr; - hdr = net_pkt_tcp_data(pkt); - if (net_tcp_header_fits(pkt, hdr)) { - return hdr->chksum; + tcp_hdr = (struct net_tcp_hdr *)net_pkt_get_data_new(pkt, &tcp_access); + if (!tcp_hdr) { + return -ENOBUFS; } - frag = net_frag_read(frag, - net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt) + - 2 + 2 + 4 + 4 + /* src + dst + seq + ack */ - 1 + 1 + 2 /* offset + flags + wnd */, - &pos, sizeof(chksum), (u8_t *)&chksum); - NET_ASSERT(frag); + tcp_hdr->chksum = 0; + tcp_hdr->chksum = net_calc_chksum_tcp(pkt); - return chksum; + return net_pkt_set_data(pkt, &tcp_access); } struct net_buf *net_tcp_set_chksum(struct net_pkt *pkt, struct net_buf *frag) @@ -1387,21 +1384,14 @@ struct net_buf *net_tcp_set_chksum(struct net_pkt *pkt, struct net_buf *frag) int net_tcp_parse_opts(struct net_pkt *pkt, int opt_totlen, struct net_tcp_options *opts) { - struct net_buf *frag = pkt->frags; - u16_t pos = net_pkt_ip_hdr_len(pkt) - + net_pkt_ipv6_ext_len(pkt) - + sizeof(struct net_tcp_hdr); u8_t opt, optlen; - /* TODO: this should be done for each TCP pkt, on reception */ - if (pos + opt_totlen > net_pkt_get_len(pkt)) { - NET_ERR("Truncated pkt len: %d, expected: %d", - (int)net_pkt_get_len(pkt), pos + opt_totlen); - return -EINVAL; - } - while (opt_totlen) { - frag = net_frag_read(frag, pos, &pos, sizeof(opt), &opt); + if (net_pkt_read_u8_new(pkt, &opt)) { + optlen = 0U; + goto error; + } + opt_totlen--; /* https://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml#tcp-parameters-1 */ @@ -1421,12 +1411,12 @@ int net_tcp_parse_opts(struct net_pkt *pkt, int opt_totlen, goto error; } - frag = net_frag_read(frag, pos, &pos, sizeof(optlen), &optlen); - opt_totlen--; - if (optlen < 2) { + if (net_pkt_read_u8_new(pkt, &optlen) || optlen < 2) { goto error; } + opt_totlen--; + /* Subtract opt/optlen size now to avoid doing this * repeatedly. */ @@ -1440,11 +1430,17 @@ int net_tcp_parse_opts(struct net_pkt *pkt, int opt_totlen, if (optlen != 2) { goto error; } - frag = net_frag_read_be16(frag, pos, &pos, - &opts->mss); + + if (net_pkt_read_be16_new(pkt, &opts->mss)) { + goto error; + } + break; default: - frag = net_frag_skip(frag, pos, &pos, optlen); + if (net_pkt_skip(pkt, optlen)) { + goto error; + } + break; } @@ -1460,9 +1456,17 @@ int net_tcp_parse_opts(struct net_pkt *pkt, int opt_totlen, int tcp_hdr_len(struct net_pkt *pkt) { - struct net_tcp_hdr hdr, *tcp_hdr; + NET_PKT_DATA_ACCESS_DEFINE(tcp_access, struct net_tcp_hdr); + struct net_tcp_hdr *tcp_hdr; + + net_pkt_cursor_init(pkt); - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); + if (net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt) + + net_pkt_ipv6_ext_len(pkt))) { + return 0; + } + + tcp_hdr = (struct net_tcp_hdr *)net_pkt_get_data_new(pkt, &tcp_access); if (tcp_hdr) { return NET_TCP_HDR_LEN(tcp_hdr); } @@ -1577,9 +1581,52 @@ static void backlog_ack_timeout(struct k_work *work) (void)memset(backlog, 0, sizeof(struct tcp_backlog_entry)); } -static int tcp_backlog_find(struct net_pkt *pkt, int *empty_slot) +static void tcp_copy_ip_addr_from_hdr(sa_family_t family, + union net_ip_header *ip_hdr, + struct net_tcp_hdr *tcp_hdr, + struct sockaddr *addr, + bool is_src_addr) +{ + u16_t port; + + if (is_src_addr) { + port = tcp_hdr->src_port; + } else { + port = tcp_hdr->dst_port; + } + + if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) { + struct sockaddr_in *addr4 = net_sin(addr); + + if (is_src_addr) { + net_ipaddr_copy(&addr4->sin_addr, &ip_hdr->ipv4->src); + } else { + net_ipaddr_copy(&addr4->sin_addr, &ip_hdr->ipv4->dst); + } + + addr4->sin_port = port; + addr->sa_family = AF_INET; + } + + if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) { + struct sockaddr_in6 *addr6 = net_sin6(addr); + + if (is_src_addr) { + net_ipaddr_copy(&addr6->sin6_addr, &ip_hdr->ipv6->src); + } else { + net_ipaddr_copy(&addr6->sin6_addr, &ip_hdr->ipv6->dst); + } + + addr6->sin6_port = port; + addr->sa_family = AF_INET6; + } +} + +static int tcp_backlog_find(struct net_pkt *pkt, + union net_ip_header *ip_hdr, + struct net_tcp_hdr *tcp_hdr, + int *empty_slot) { - struct net_tcp_hdr hdr, *tcp_hdr; int i, empty = -1; for (i = 0; i < CONFIG_NET_TCP_BACKLOG_SIZE; i++) { @@ -1592,42 +1639,30 @@ static int tcp_backlog_find(struct net_pkt *pkt, int *empty_slot) continue; } - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); - if (!tcp_hdr) { - return -EINVAL; - } - - switch (net_pkt_family(pkt)) { -#if defined(CONFIG_NET_IPV6) - case AF_INET6: - if (net_sin6(&tcp_backlog[i].remote)->sin6_port != + if (IS_ENABLED(CONFIG_NET_IPV4) && + net_pkt_family(pkt) == AF_INET) { + if (net_sin(&tcp_backlog[i].remote)->sin_port != tcp_hdr->src_port) { continue; } - if (memcmp(&net_sin6(&tcp_backlog[i].remote)->sin6_addr, - &NET_IPV6_HDR(pkt)->src, - sizeof(struct in6_addr))) { + if (memcmp(&net_sin(&tcp_backlog[i].remote)->sin_addr, + &ip_hdr->ipv4->src, + sizeof(struct in_addr))) { continue; } - - break; -#endif -#if defined(CONFIG_NET_IPV4) - case AF_INET: - if (net_sin(&tcp_backlog[i].remote)->sin_port != + } else if (IS_ENABLED(CONFIG_NET_IPV6) && + net_pkt_family(pkt) == AF_INET6) { + if (net_sin6(&tcp_backlog[i].remote)->sin6_port != tcp_hdr->src_port) { continue; } - if (memcmp(&net_sin(&tcp_backlog[i].remote)->sin_addr, - &NET_IPV4_HDR(pkt)->src, - sizeof(struct in_addr))) { + if (memcmp(&net_sin6(&tcp_backlog[i].remote)->sin6_addr, + &ip_hdr->ipv6->src, + sizeof(struct in6_addr))) { continue; } - - break; -#endif } return i; @@ -1640,13 +1675,15 @@ static int tcp_backlog_find(struct net_pkt *pkt, int *empty_slot) return -EADDRNOTAVAIL; } -static int tcp_backlog_syn(struct net_pkt *pkt, struct net_context *context, +static int tcp_backlog_syn(struct net_pkt *pkt, + union net_ip_header *ip_hdr, + struct net_tcp_hdr *tcp_hdr, + struct net_context *context, u16_t send_mss) { int empty_slot = -1; - int ret; - if (tcp_backlog_find(pkt, &empty_slot) >= 0) { + if (tcp_backlog_find(pkt, ip_hdr, tcp_hdr, &empty_slot) >= 0) { return -EADDRINUSE; } @@ -1656,14 +1693,8 @@ static int tcp_backlog_syn(struct net_pkt *pkt, struct net_context *context, tcp_backlog[empty_slot].tcp = context->tcp; - ret = net_pkt_get_src_addr(pkt, &tcp_backlog[empty_slot].remote, - sizeof(tcp_backlog[empty_slot].remote)); - if (ret < 0) { - /* Release the assigned empty slot */ - tcp_backlog[empty_slot].tcp = NULL; - - return ret; - } + tcp_copy_ip_addr_from_hdr(net_pkt_family(pkt), ip_hdr, tcp_hdr, + &tcp_backlog[empty_slot].remote, true); tcp_backlog[empty_slot].send_seq = context->tcp->send_seq; tcp_backlog[empty_slot].send_ack = context->tcp->send_ack; @@ -1676,22 +1707,18 @@ static int tcp_backlog_syn(struct net_pkt *pkt, struct net_context *context, return 0; } -static int tcp_backlog_ack(struct net_pkt *pkt, struct net_context *context) +static int tcp_backlog_ack(struct net_pkt *pkt, + union net_ip_header *ip_hdr, + struct net_tcp_hdr *tcp_hdr, + struct net_context *context) { - struct net_tcp_hdr hdr, *tcp_hdr; int r; - r = tcp_backlog_find(pkt, NULL); - + r = tcp_backlog_find(pkt, ip_hdr, tcp_hdr, NULL); if (r < 0) { return r; } - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); - if (!tcp_hdr) { - return -EINVAL; - } - /* Sent SEQ + 1 needs to be the same as the received ACK */ if (tcp_backlog[r].send_seq + 1 != sys_get_be32(tcp_hdr->ack)) { return -EINVAL; @@ -1709,21 +1736,17 @@ static int tcp_backlog_ack(struct net_pkt *pkt, struct net_context *context) return 0; } -static int tcp_backlog_rst(struct net_pkt *pkt) +static int tcp_backlog_rst(struct net_pkt *pkt, + union net_ip_header *ip_hdr, + struct net_tcp_hdr *tcp_hdr) { - struct net_tcp_hdr hdr, *tcp_hdr; int r; - r = tcp_backlog_find(pkt, NULL); + r = tcp_backlog_find(pkt, ip_hdr, tcp_hdr, NULL); if (r < 0) { return r; } - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); - if (!tcp_hdr) { - return -EINVAL; - } - /* The ACK sent needs to be the same as the received SEQ */ if (tcp_backlog[r].send_ack != sys_get_be32(tcp_hdr->seq)) { return -EINVAL; @@ -1762,8 +1785,8 @@ static void handle_ack_timeout(struct k_work *work) net_tcp_change_state(tcp, NET_TCP_CLOSED); if (tcp->context->recv_cb) { - tcp->context->recv_cb(tcp->context, NULL, 0, - tcp->recv_user_data); + tcp->context->recv_cb(tcp->context, NULL, NULL, NULL, + 0, tcp->recv_user_data); } net_context_unref(tcp->context); @@ -1781,8 +1804,8 @@ static void handle_timewait_timeout(struct k_work *work) net_tcp_change_state(tcp, NET_TCP_CLOSED); if (tcp->context->recv_cb) { - tcp->context->recv_cb(tcp->context, NULL, 0, - tcp->recv_user_data); + tcp->context->recv_cb(tcp->context, NULL, NULL, NULL, + 0, tcp->recv_user_data); } net_context_unref(tcp->context); @@ -1832,12 +1855,12 @@ int net_tcp_unref(struct net_context *context) #define net_tcp_print_recv_info(str, pkt, port) \ if (IS_ENABLED(CONFIG_NET_TCP_LOG_LEVEL_DBG)) { \ - if (net_context_get_family(context) == AF_INET6) { \ + if (net_pkt_family(pkt) == AF_INET6) { \ NET_DBG("%s received from %s port %d", str, \ log_strdup(net_sprint_ipv6_addr( \ &NET_IPV6_HDR(pkt)->src)), \ ntohs(port)); \ - } else if (net_context_get_family(context) == AF_INET) {\ + } else if (net_pkt_family(pkt) == AF_INET) {\ NET_DBG("%s received from %s port %d", str, \ log_strdup(net_sprint_ipv4_addr( \ &NET_IPV4_HDR(pkt)->src)), \ @@ -1847,13 +1870,12 @@ int net_tcp_unref(struct net_context *context) #define net_tcp_print_send_info(str, pkt, port) \ if (IS_ENABLED(CONFIG_NET_TCP_LOG_LEVEL_DBG)) { \ - struct net_context *ctx = net_pkt_context(pkt); \ - if (net_context_get_family(ctx) == AF_INET6) { \ + if (net_pkt_family(pkt) == AF_INET6) { \ NET_DBG("%s sent to %s port %d", str, \ log_strdup(net_sprint_ipv6_addr( \ &NET_IPV6_HDR(pkt)->dst)), \ ntohs(port)); \ - } else if (net_context_get_family(ctx) == AF_INET) { \ + } else if (net_pkt_family(pkt) == AF_INET) { \ NET_DBG("%s sent to %s port %d", str, \ log_strdup(net_sprint_ipv4_addr( \ &NET_IPV4_HDR(pkt)->dst)), \ @@ -1861,15 +1883,27 @@ int net_tcp_unref(struct net_context *context) } \ } -static void print_send_info(struct net_pkt *pkt, const char *msg) +static void print_send_info(struct net_pkt *pkt, + const char *msg, const struct sockaddr *remote) { if (CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG) { - struct net_tcp_hdr hdr, *tcp_hdr; + u16_t port = 0; + + if (IS_ENABLED(CONFIG_NET_IPV4) && + net_pkt_family(pkt) == AF_INET) { + struct sockaddr_in *addr4 = net_sin(remote); - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); - if (tcp_hdr) { - net_tcp_print_send_info(msg, pkt, tcp_hdr->dst_port); + port = addr4->sin_port; } + + if (IS_ENABLED(CONFIG_NET_IPV6) && + net_pkt_family(pkt) == AF_INET6) { + struct sockaddr_in6 *addr6 = net_sin6(remote); + + port = addr6->sin6_port; + } + + net_tcp_print_send_info(msg, pkt, port); } } @@ -1894,7 +1928,7 @@ static inline int send_syn_segment(struct net_context *context, return ret; } - print_send_info(pkt, msg); + print_send_info(pkt, msg, remote); ret = net_send_data(pkt); if (ret < 0) { @@ -1942,7 +1976,7 @@ static int send_ack(struct net_context *context, return ret; } - print_send_info(pkt, "ACK"); + print_send_info(pkt, "ACK", remote); ret = net_tcp_send_pkt(pkt); if (ret < 0) { @@ -1964,7 +1998,7 @@ static int send_reset(struct net_context *context, return ret; } - print_send_info(pkt, "RST"); + print_send_info(pkt, "RST", remote); ret = net_send_data(pkt); if (ret < 0) { @@ -1978,13 +2012,16 @@ static int send_reset(struct net_context *context, * established. The core TCP logic is located here. * * Prototype: - * enum net_verdict tcp_established(struct net_conn *conn, struct net_pkt *pkt, + * enum net_verdict tcp_established(struct net_conn *conn, + * union net_ip_header *ip_hdr, + * union net_proto_header *proto_hdr, + * struct net_pkt *pkt, * void *user_data) */ NET_CONN_CB(tcp_established) { struct net_context *context = (struct net_context *)user_data; - struct net_tcp_hdr hdr, *tcp_hdr; + struct net_tcp_hdr *tcp_hdr = proto_hdr->tcp; enum net_verdict ret = NET_OK; u8_t tcp_flags; u16_t data_len; @@ -1993,12 +2030,6 @@ NET_CONN_CB(tcp_established) NET_ASSERT(context && context->tcp); - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); - if (!tcp_hdr) { - ret = NET_DROP; - goto unlock; - } - if (net_tcp_get_state(context->tcp) < NET_TCP_ESTABLISHED) { NET_ERR("Context %p in wrong state %d", context, net_tcp_get_state(context->tcp)); @@ -2041,7 +2072,7 @@ NET_CONN_CB(tcp_established) */ if (tcp_flags & NET_TCP_RST) { /* We only accept RST packet that has valid seq field. */ - if (!net_tcp_validate_seq(context->tcp, pkt)) { + if (!net_tcp_validate_seq(context->tcp, tcp_hdr)) { net_stats_update_tcp_seg_rsterr(net_pkt_iface(pkt)); ret = NET_DROP; goto unlock; @@ -2052,7 +2083,7 @@ NET_CONN_CB(tcp_established) net_tcp_print_recv_info("RST", pkt, tcp_hdr->src_port); if (context->recv_cb) { - context->recv_cb(context, NULL, -ECONNRESET, + context->recv_cb(context, NULL, NULL, NULL, -ECONNRESET, context->tcp->recv_user_data); } @@ -2065,7 +2096,7 @@ NET_CONN_CB(tcp_established) /* Handle TCP state transition */ if (tcp_flags & NET_TCP_ACK) { if (!net_tcp_ack_received(context, - sys_get_be32(tcp_hdr->ack))) { + sys_get_be32(tcp_hdr->ack))) { return NET_DROP; } @@ -2137,7 +2168,7 @@ NET_CONN_CB(tcp_established) * release the pkt. Otherwise, release the pkt immediately. */ if (data_len > 0) { - ret = net_context_packet_received(conn, pkt, + ret = net_context_packet_received(conn, pkt, ip_hdr, proto_hdr, context->tcp->recv_user_data); } else if (data_len == 0) { net_pkt_unref(pkt); @@ -2159,7 +2190,7 @@ NET_CONN_CB(tcp_established) if (net_tcp_get_state(context->tcp) == NET_TCP_CLOSED) { if (context->recv_cb) { - context->recv_cb(context, NULL, 0, + context->recv_cb(context, NULL, NULL, NULL, 0, context->tcp->recv_user_data); } @@ -2172,17 +2203,18 @@ NET_CONN_CB(tcp_established) return ret; } - /* * Prototype: * enum net_verdict tcp_synack_received(struct net_conn *conn, - * struct net_pkt *pkt, - * void *user_data) + * struct net_pkt *pkt, + * union net_ip_header *ip_hdr, + * union net_proto_header *proto_hdr, + * void *user_data) */ NET_CONN_CB(tcp_synack_received) { struct net_context *context = (struct net_context *)user_data; - struct net_tcp_hdr hdr, *tcp_hdr; + struct net_tcp_hdr *tcp_hdr = proto_hdr->tcp; int ret; NET_ASSERT(context && context->tcp); @@ -2201,14 +2233,9 @@ NET_CONN_CB(tcp_synack_received) NET_ASSERT(net_pkt_iface(pkt)); - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); - if (!tcp_hdr) { - return NET_DROP; - } - if (NET_TCP_FLAGS(tcp_hdr) & NET_TCP_RST) { /* We only accept RST packet that has valid seq field. */ - if (!net_tcp_validate_seq(context->tcp, pkt)) { + if (!net_tcp_validate_seq(context->tcp, tcp_hdr)) { net_stats_update_tcp_seg_rsterr(net_pkt_iface(pkt)); return NET_DROP; } @@ -2239,18 +2266,10 @@ NET_CONN_CB(tcp_synack_received) struct sockaddr local_addr; struct sockaddr remote_addr; - if (net_pkt_get_src_addr( - pkt, &remote_addr, sizeof(remote_addr)) < 0) { - NET_DBG("Cannot parse remote address" - " from received pkt"); - return NET_DROP; - } - - if (net_pkt_get_dst_addr( - pkt, &local_addr, sizeof(local_addr)) < 0) { - NET_DBG("Cannot parse local address from received pkt"); - return NET_DROP; - } + tcp_copy_ip_addr_from_hdr(net_pkt_family(pkt), ip_hdr, tcp_hdr, + &remote_addr, true); + tcp_copy_ip_addr_from_hdr(net_pkt_family(pkt), ip_hdr, tcp_hdr, + &local_addr, false); net_tcp_unregister(context->conn_handler); @@ -2282,37 +2301,28 @@ NET_CONN_CB(tcp_synack_received) return NET_DROP; } -static void pkt_get_sockaddr(sa_family_t family, struct net_pkt *pkt, +static void get_sockaddr_ptr(union net_ip_header *ip_hdr, + struct net_tcp_hdr *tcp_hdr, + sa_family_t family, struct sockaddr_ptr *addr) { - struct net_tcp_hdr hdr, *tcp_hdr; - - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); - if (!tcp_hdr) { - return; - } - (void)memset(addr, 0, sizeof(*addr)); -#if defined(CONFIG_NET_IPV4) - if (family == AF_INET) { + if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) { struct sockaddr_in_ptr *addr4 = net_sin_ptr(addr); addr4->sin_family = AF_INET; addr4->sin_port = tcp_hdr->dst_port; - addr4->sin_addr = &NET_IPV4_HDR(pkt)->dst; + addr4->sin_addr = &ip_hdr->ipv4->dst; } -#endif -#if defined(CONFIG_NET_IPV6) - if (family == AF_INET6) { + if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) { struct sockaddr_in6_ptr *addr6 = net_sin6_ptr(addr); addr6->sin6_family = AF_INET6; addr6->sin6_port = tcp_hdr->dst_port; - addr6->sin6_addr = &NET_IPV6_HDR(pkt)->dst; + addr6->sin6_addr = &ip_hdr->ipv6->dst; } -#endif } #if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) @@ -2332,13 +2342,16 @@ static inline void copy_pool_vars(struct net_context *new_context, * connection. * * Prototype: - * enum net_verdict tcp_syn_rcvd(struct net_conn *conn, struct net_pkt *pkt, - * void *user_data) + * enum net_verdict tcp_syn_rcvd(struct net_conn *conn, + * struct net_pkt *pkt, + * union net_ip_header *ip_hdr, + * union net_proto_header *proto_hdr, + * void *user_data) */ NET_CONN_CB(tcp_syn_rcvd) { struct net_context *context = (struct net_context *)user_data; - struct net_tcp_hdr hdr, *tcp_hdr; + struct net_tcp_hdr *tcp_hdr = proto_hdr->tcp; struct net_tcp *tcp; struct sockaddr_ptr pkt_src_addr; struct sockaddr local_addr; @@ -2367,30 +2380,20 @@ NET_CONN_CB(tcp_syn_rcvd) NET_ASSERT(net_pkt_iface(pkt)); - tcp_hdr = net_tcp_get_hdr(pkt, &hdr); - if (!tcp_hdr) { - return NET_DROP; - } - - if (net_pkt_get_src_addr(pkt, &remote_addr, sizeof(remote_addr)) < 0) { - NET_DBG("Cannot parse remote address from received pkt"); - return NET_DROP; - } - - if (net_pkt_get_dst_addr(pkt, &local_addr, sizeof(local_addr)) < 0) { - NET_DBG("Cannot parse local address from received pkt"); - return NET_DROP; - } + tcp_copy_ip_addr_from_hdr(net_pkt_family(pkt), ip_hdr, tcp_hdr, + &remote_addr, true); + tcp_copy_ip_addr_from_hdr(net_pkt_family(pkt), ip_hdr, tcp_hdr, + &local_addr, false); /* * If we receive SYN, we send SYN-ACK and go to SYN_RCVD state. */ if (NET_TCP_FLAGS(tcp_hdr) == NET_TCP_SYN) { - int r; - int opt_totlen; struct net_tcp_options tcp_opts = { .mss = NET_TCP_DEFAULT_MSS, }; + int opt_totlen; + int r; net_tcp_print_recv_info("SYN", pkt, tcp_hdr->src_port); @@ -2412,7 +2415,8 @@ NET_CONN_CB(tcp_syn_rcvd) /* Get MSS from TCP options here*/ - r = tcp_backlog_syn(pkt, context, tcp_opts.mss); + r = tcp_backlog_syn(pkt, ip_hdr, tcp_hdr, + context, tcp_opts.mss); if (r < 0) { if (r == -EADDRINUSE) { NET_DBG("TCP connection already exists"); @@ -2423,8 +2427,9 @@ NET_CONN_CB(tcp_syn_rcvd) return NET_DROP; } - pkt_get_sockaddr(net_context_get_family(context), - pkt, &pkt_src_addr); + get_sockaddr_ptr(ip_hdr, tcp_hdr, + net_context_get_family(context), + &pkt_src_addr); send_syn_ack(context, &pkt_src_addr, &remote_addr); net_pkt_unref(pkt); return NET_OK; @@ -2436,7 +2441,7 @@ NET_CONN_CB(tcp_syn_rcvd) */ if (NET_TCP_FLAGS(tcp_hdr) & NET_TCP_RST) { - if (tcp_backlog_rst(pkt) < 0) { + if (tcp_backlog_rst(pkt, ip_hdr, tcp_hdr) < 0) { net_stats_update_tcp_seg_rsterr(net_pkt_iface(pkt)); return NET_DROP; } @@ -2474,7 +2479,7 @@ NET_CONN_CB(tcp_syn_rcvd) goto conndrop; } - ret = tcp_backlog_ack(pkt, new_context); + ret = tcp_backlog_ack(pkt, ip_hdr, tcp_hdr, new_context); if (ret < 0) { NET_DBG("Cannot find context from TCP backlog"); @@ -2557,7 +2562,8 @@ NET_CONN_CB(tcp_syn_rcvd) return NET_DROP; } -int net_tcp_accept(struct net_context *context, net_tcp_accept_cb_t cb, +int net_tcp_accept(struct net_context *context, + net_tcp_accept_cb_t cb, void *user_data) { struct sockaddr local_addr; @@ -2670,3 +2676,25 @@ int net_tcp_connect(struct net_context *context, return 0; } + +struct net_tcp_hdr *net_tcp_input(struct net_pkt *pkt, + struct net_pkt_data_access *tcp_access) +{ + struct net_tcp_hdr *tcp_hdr; + + if (IS_ENABLED(CONFIG_NET_TCP_CHECKSUM) && + net_if_need_calc_rx_checksum(net_pkt_iface(pkt)) && + net_calc_chksum_tcp(pkt) != 0) { + NET_DBG("DROP: checksum mismatch"); + goto drop; + } + + tcp_hdr = (struct net_tcp_hdr *)net_pkt_get_data_new(pkt, tcp_access); + if (tcp_hdr && !net_pkt_set_data(pkt, tcp_access)) { + return tcp_hdr; + } + +drop: + net_stats_update_tcp_seg_chkerr(net_pkt_iface(pkt)); + return NULL; +} diff --git a/subsys/net/ip/tcp_internal.h b/subsys/net/ip/tcp_internal.h index 48e61ba554b6a..217678a01cd45 100644 --- a/subsys/net/ip/tcp_internal.h +++ b/subsys/net/ip/tcp_internal.h @@ -393,33 +393,31 @@ static inline enum net_tcp_state net_tcp_get_state(const struct net_tcp *tcp) * @brief Check if the sequence number is valid i.e., it is inside the window. * * @param tcp TCP context - * @param pkt Network packet + * @param tcp_hdr TCP header pointer * * @return true if network packet sequence number is valid, false otherwise */ -bool net_tcp_validate_seq(struct net_tcp *tcp, struct net_pkt *pkt); +bool net_tcp_validate_seq(struct net_tcp *tcp, struct net_tcp_hdr *tcp_hdr); /** - * @brief Set TCP checksum in network packet. + * @brief Finalize TCP packet * * @param pkt Network packet - * @param frag Fragment where to start calculating the offset. - * Typically this is set to pkt->frags by the caller. * - * @return Return the actual fragment where the checksum was written. + * @return 0 on success, negative errno otherwise. */ -struct net_buf *net_tcp_set_chksum(struct net_pkt *pkt, struct net_buf *frag); +int net_tcp_finalize(struct net_pkt *pkt); /** - * @brief Get TCP checksum from network packet. + * @brief Set TCP checksum in network packet. * * @param pkt Network packet * @param frag Fragment where to start calculating the offset. * Typically this is set to pkt->frags by the caller. * - * @return Return the checksum in host byte order. + * @return Return the actual fragment where the checksum was written. */ -u16_t net_tcp_get_chksum(struct net_pkt *pkt, struct net_buf *frag); +struct net_buf *net_tcp_set_chksum(struct net_pkt *pkt, struct net_buf *frag); /** * @brief Parse TCP options from network packet. @@ -547,6 +545,9 @@ int net_tcp_connect(struct net_context *context, net_context_connect_cb_t cb, void *user_data); +struct net_tcp_hdr *net_tcp_input(struct net_pkt *pkt, + struct net_pkt_data_access *tcp_access); + #else static inline struct net_tcp *net_tcp_alloc(struct net_context *context) { @@ -654,18 +655,16 @@ static inline enum net_tcp_state net_tcp_get_state(const struct net_tcp *tcp) } static inline bool net_tcp_validate_seq(struct net_tcp *tcp, - struct net_pkt *pkt) + struct net_tcp_hdr *tcp_hdr) { ARG_UNUSED(tcp); - ARG_UNUSED(pkt); + ARG_UNUSED(tcp_hdr); return false; } -static inline u16_t net_tcp_get_chksum(struct net_pkt *pkt, - struct net_buf *frag) +static inline int net_tcp_finalize(struct net_pkt *pkt) { ARG_UNUSED(pkt); - ARG_UNUSED(frag); return 0; } @@ -774,6 +773,16 @@ static inline int net_tcp_connect(struct net_context *context, return -EPROTONOSUPPORT; } +static inline +struct net_tcp_hdr *net_tcp_input(struct net_pkt *pkt, + struct net_pkt_data_access *tcp_access) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(tcp_access); + + return NULL; +} + #endif #if defined(CONFIG_NET_TCP) diff --git a/subsys/net/ip/udp.c b/subsys/net/ip/udp.c index 275a7c1c6234c..5f7b9bc552c80 100644 --- a/subsys/net/ip/udp.c +++ b/subsys/net/ip/udp.c @@ -13,9 +13,49 @@ LOG_MODULE_REGISTER(net_udp, CONFIG_NET_UDP_LOG_LEVEL); #include "net_private.h" #include "udp_internal.h" +#include "net_stats.h" #define PKT_WAIT_TIME K_SECONDS(1) +int net_udp_create(struct net_pkt *pkt, u16_t src_port, u16_t dst_port) +{ + NET_PKT_DATA_ACCESS_DEFINE(udp_access, struct net_udp_hdr); + struct net_udp_hdr *udp_hdr; + + udp_hdr = (struct net_udp_hdr *)net_pkt_get_data_new(pkt, &udp_access); + if (!udp_hdr) { + return -ENOBUFS; + } + + udp_hdr->src_port = src_port; + udp_hdr->dst_port = dst_port; + udp_hdr->len = 0; + udp_hdr->chksum = 0; + + return net_pkt_set_data(pkt, &udp_access); +} + +int net_udp_finalize(struct net_pkt *pkt) +{ + NET_PKT_DATA_ACCESS_DEFINE(udp_access, struct net_udp_hdr); + struct net_udp_hdr *udp_hdr; + u16_t length; + + udp_hdr = (struct net_udp_hdr *)net_pkt_get_data_new(pkt, &udp_access); + if (!udp_hdr) { + return -ENOBUFS; + } + + length = net_pkt_get_len(pkt) - + net_pkt_ip_hdr_len(pkt) - + net_pkt_ipv6_ext_len(pkt); + + udp_hdr->len = htons(length); + udp_hdr->chksum = net_calc_chksum_udp(pkt); + + return net_pkt_set_data(pkt, &udp_access); +} + struct net_pkt *net_udp_insert(struct net_pkt *pkt, u16_t offset, u16_t src_port, @@ -125,27 +165,6 @@ struct net_buf *net_udp_set_chksum(struct net_pkt *pkt, struct net_buf *frag) return frag; } -u16_t net_udp_get_chksum(struct net_pkt *pkt, struct net_buf *frag) -{ - struct net_udp_hdr *hdr; - u16_t chksum; - u16_t pos; - - hdr = net_pkt_udp_data(pkt); - if (net_udp_header_fits(pkt, hdr)) { - return hdr->chksum; - } - - frag = net_frag_read(frag, - net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt) + - 2 + 2 + 2 /* src + dst + len */, - &pos, sizeof(chksum), (u8_t *)&chksum); - NET_ASSERT(frag); - - return chksum; -} - struct net_udp_hdr *net_udp_get_hdr(struct net_pkt *pkt, struct net_udp_hdr *hdr) { @@ -222,3 +241,25 @@ int net_udp_unregister(struct net_conn_handle *handle) { return net_conn_unregister(handle); } + +struct net_udp_hdr *net_udp_input(struct net_pkt *pkt, + struct net_pkt_data_access *udp_access) +{ + struct net_udp_hdr *udp_hdr; + + if (IS_ENABLED(CONFIG_NET_UDP_CHECKSUM) && + net_if_need_calc_rx_checksum(net_pkt_iface(pkt)) && + net_calc_chksum_udp(pkt) != 0) { + NET_DBG("DROP: checksum mismatch"); + goto drop; + } + + udp_hdr = (struct net_udp_hdr *)net_pkt_get_data_new(pkt, udp_access); + if (udp_hdr && !net_pkt_set_data(pkt, udp_access)) { + return udp_hdr; + } + +drop: + net_stats_update_udp_chkerr(net_pkt_iface(pkt)); + return NULL; +} diff --git a/subsys/net/ip/udp_internal.h b/subsys/net/ip/udp_internal.h index 801512fd50f69..10a471f079c44 100644 --- a/subsys/net/ip/udp_internal.h +++ b/subsys/net/ip/udp_internal.h @@ -40,17 +40,6 @@ extern "C" { */ struct net_buf *net_udp_set_chksum(struct net_pkt *pkt, struct net_buf *frag); -/** - * @brief Get UDP checksum from network packet. - * - * @param pkt Network packet - * @param frag Fragment where to start calculating the offset. - * Typically this is set to pkt->frags by the caller. - * - * @return Return the checksum in host byte order. - */ -u16_t net_udp_get_chksum(struct net_pkt *pkt, struct net_buf *frag); - /** * @brief Insert UDP packet into net_pkt after specific offset. * @@ -67,10 +56,65 @@ struct net_pkt *net_udp_insert(struct net_pkt *pkt, u16_t src_port, u16_t dst_port); +/** + * @brief Create UDP packet into net_pkt + * + * Note: pkt's cursor should be set a the right position. + * (i.e. after IP header) + * + * @param pkt Network packet + * @param src_port Destination port in network byte order. + * @param dst_port Destination port in network byte order. + * + * @return 0 on success, negative errno otherwise. + */ +int net_udp_create(struct net_pkt *pkt, u16_t src_port, u16_t dst_port); + +/** + * @brief Finalize UDP packet + * + * Note: calculates final length and setting up the checksum. + * + * @param pkt Network packet + * + * @return 0 on success, negative errno otherwise. + */ +int net_udp_finalize(struct net_pkt *pkt); + +struct net_udp_hdr *net_udp_input(struct net_pkt *pkt, + struct net_pkt_data_access *udp_access); + #else #define net_udp_insert(pkt, offset, src_port, dst_port) (pkt) -#define net_udp_get_chksum(pkt, frag) (0) #define net_udp_set_chksum(pkt, frag) NULL + +static inline int net_udp_create(struct net_pkt *pkt, + u16_t src_port, u16_t dst_port) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(src_port); + ARG_UNUSED(dst_port); + + return 0; +} + +static inline int net_udp_finalize(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return 0; +} + +static inline +struct net_udp_hdr *net_udp_input(struct net_pkt *pkt, + struct net_pkt_data_access *udp_access) +{ + ARG_UNUSED(pkt); + ARG_UNUSED(udp_access); + + return NULL; +} + #endif /* CONFIG_NET_UDP */ /** diff --git a/subsys/net/ip/utils.c b/subsys/net/ip/utils.c index 27e9a2073bef3..ff8ac6d509c82 100644 --- a/subsys/net/ip/utils.c +++ b/subsys/net/ip/utils.c @@ -390,24 +390,25 @@ int net_addr_pton(sa_family_t family, const char *src, return 0; } -static u16_t calc_chksum(u16_t sum, const u8_t *ptr, u16_t len) +static u16_t calc_chksum(u16_t sum, const u8_t *data, size_t len) { - u16_t tmp; const u8_t *end; + u16_t tmp; - end = ptr + len - 1; + end = data + len - 1; - while (ptr < end) { - tmp = (ptr[0] << 8) + ptr[1]; + while (data < end) { + tmp = (data[0] << 8) + data[1]; sum += tmp; if (sum < tmp) { sum++; } - ptr += 2; + + data += 2; } - if (ptr == end) { - tmp = ptr[0] << 8; + if (data == end) { + tmp = data[0] << 8; sum += tmp; if (sum < tmp) { sum++; @@ -417,50 +418,37 @@ static u16_t calc_chksum(u16_t sum, const u8_t *ptr, u16_t len) return sum; } -static inline u16_t calc_chksum_pkt(u16_t sum, struct net_pkt *pkt, - u16_t upper_layer_len) +static inline u16_t pkt_calc_chksum(struct net_pkt *pkt, u16_t sum) { - u16_t proto_len = net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt); - struct net_buf *frag; - u16_t offset; - s16_t len; - u8_t *ptr; - - ARG_UNUSED(upper_layer_len); - - frag = net_frag_skip(pkt->frags, proto_len, &offset, 0); - if (!frag) { - NET_DBG("Trying to read past pkt len (proto len %d)", - proto_len); - return 0; + struct net_pkt_cursor *cur = &pkt->cursor; + size_t len; + + if (!cur->buf || !cur->pos) { + return sum; } - NET_ASSERT(offset <= frag->len); + len = cur->buf->len - (cur->pos - cur->buf->data); - ptr = frag->data + offset; - len = frag->len - offset; + while (cur->buf) { + sum = calc_chksum(sum, cur->pos, len); - while (frag) { - sum = calc_chksum(sum, ptr, len); - frag = frag->frags; - if (!frag) { + cur->buf = cur->buf->frags; + if (!cur->buf || !cur->buf->len) { break; } - ptr = frag->data; + cur->pos = cur->buf->data; - /* Do we need to take first byte from next fragment */ if (len % 2) { - u16_t tmp = *ptr; - sum += tmp; - if (sum < tmp) { + sum += *cur->pos; + if (sum < *cur->pos) { sum++; } - len = frag->len - 1; - ptr++; + + cur->pos++; + len = cur->buf->len - 1; } else { - len = frag->len; + len = cur->buf->len; } } @@ -469,41 +457,49 @@ static inline u16_t calc_chksum_pkt(u16_t sum, struct net_pkt *pkt, u16_t net_calc_chksum(struct net_pkt *pkt, u8_t proto) { - u16_t upper_layer_len; + size_t len = 0U; u16_t sum = 0U; + struct net_pkt_cursor backup; + bool ow; - switch (net_pkt_family(pkt)) { -#if defined(CONFIG_NET_IPV4) - case AF_INET: - upper_layer_len = ntohs(NET_IPV4_HDR(pkt)->len) - - net_pkt_ipv6_ext_len(pkt) - - net_pkt_ip_hdr_len(pkt); + net_pkt_cursor_backup(pkt, &backup); + net_pkt_cursor_init(pkt); + if (IS_ENABLED(CONFIG_NET_IPV4) && + net_pkt_family(pkt) == AF_INET) { if (proto != IPPROTO_ICMP) { - sum = calc_chksum(upper_layer_len + proto, - (u8_t *)&NET_IPV4_HDR(pkt)->src, - 2 * sizeof(struct in_addr)); + len = 2 * sizeof(struct in_addr); + sum = net_pkt_get_len(pkt) - + net_pkt_ip_hdr_len(pkt) + proto; } - break; -#endif -#if defined(CONFIG_NET_IPV6) - case AF_INET6: - upper_layer_len = ntohs(NET_IPV6_HDR(pkt)->len) - - net_pkt_ipv6_ext_len(pkt); - sum = calc_chksum(upper_layer_len + proto, - (u8_t *)&NET_IPV6_HDR(pkt)->src, - 2 * sizeof(struct in6_addr)); - break; -#endif - default: + } else if (IS_ENABLED(CONFIG_NET_IPV6) && + net_pkt_family(pkt) == AF_INET6) { + len = 2 * sizeof(struct in6_addr); + sum = net_pkt_get_len(pkt) - + net_pkt_ip_hdr_len(pkt) - + net_pkt_ipv6_ext_len(pkt) + proto; + } else { NET_DBG("Unknown protocol family %d", net_pkt_family(pkt)); return 0; } - sum = calc_chksum_pkt(sum, pkt, upper_layer_len); + ow = net_pkt_is_being_overwritten(pkt); + net_pkt_set_overwrite(pkt, true); + + net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt) - len); + + sum = calc_chksum(sum, pkt->cursor.pos, len); + + net_pkt_skip(pkt, len + net_pkt_ipv6_ext_len(pkt)); + + sum = pkt_calc_chksum(pkt, sum); sum = (sum == 0) ? 0xffff : htons(sum); + net_pkt_cursor_restore(pkt, &backup); + + net_pkt_set_overwrite(pkt, ow); + return ~sum; } @@ -512,7 +508,7 @@ u16_t net_calc_chksum_ipv4(struct net_pkt *pkt) { u16_t sum; - sum = calc_chksum(0, (u8_t *)NET_IPV4_HDR(pkt), NET_IPV4H_LEN); + sum = calc_chksum(0, pkt->buffer->data, NET_IPV4H_LEN); sum = (sum == 0) ? 0xffff : htons(sum); diff --git a/subsys/net/l2/ethernet/arp.c b/subsys/net/l2/ethernet/arp.c index ff0d7e792614d..7e754219cc3bf 100644 --- a/subsys/net/l2/ethernet/arp.c +++ b/subsys/net/l2/ethernet/arp.c @@ -235,9 +235,9 @@ static inline struct net_pkt *arp_prepare(struct net_if *iface, struct net_pkt *pending, struct in_addr *current_ip) { - struct net_pkt *pkt; struct net_arp_hdr *hdr; struct in_addr *my_addr; + struct net_pkt *pkt; if (current_ip) { /* This is the IPv4 autoconf case where we have already @@ -245,27 +245,17 @@ static inline struct net_pkt *arp_prepare(struct net_if *iface, */ pkt = pending; } else { - struct net_buf *frag; - - pkt = net_pkt_get_reserve_tx(NET_BUF_TIMEOUT); + pkt = net_pkt_alloc_with_buffer(iface, + sizeof(struct net_arp_hdr), + AF_UNSPEC, 0, NET_BUF_TIMEOUT); if (!pkt) { return NULL; } - - frag = net_pkt_get_frag(pkt, NET_BUF_TIMEOUT); - if (!frag) { - net_pkt_unref(pkt); - return NULL; - } - - net_pkt_frag_add(pkt, frag); - net_pkt_set_iface(pkt, iface); - net_pkt_set_family(pkt, AF_UNSPEC); } net_pkt_set_vlan_tag(pkt, net_eth_get_vlan_tag(iface)); - net_buf_add(pkt->frags, sizeof(struct net_arp_hdr)); + net_buf_add(pkt->buffer, sizeof(struct net_arp_hdr)); hdr = NET_ARP_HDR(pkt); @@ -329,7 +319,7 @@ struct net_pkt *net_arp_prepare(struct net_pkt *pkt, struct arp_entry *entry; struct in_addr *addr; - if (!pkt || !pkt->frags) { + if (!pkt || !pkt->buffer) { return NULL; } @@ -468,24 +458,16 @@ static inline struct net_pkt *arp_prepare_reply(struct net_if *iface, struct net_pkt *req, struct net_eth_hdr *eth_query) { - struct net_pkt *pkt; - struct net_buf *frag; struct net_arp_hdr *hdr, *query; + struct net_pkt *pkt; - pkt = net_pkt_get_reserve_tx(NET_BUF_TIMEOUT); + pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct net_arp_hdr), + AF_UNSPEC, 0, NET_BUF_TIMEOUT); if (!pkt) { - goto fail; - } - - net_pkt_set_iface(pkt, iface); - net_pkt_set_family(pkt, AF_UNSPEC); - - frag = net_pkt_get_frag(pkt, NET_BUF_TIMEOUT); - if (!frag) { - goto fail; + return NULL; } - net_pkt_frag_add(pkt, frag); + net_buf_add(pkt->buffer, sizeof(struct net_arp_hdr)); hdr = NET_ARP_HDR(pkt); query = NET_ARP_HDR(req); @@ -512,13 +494,7 @@ static inline struct net_pkt *arp_prepare_reply(struct net_if *iface, net_pkt_lladdr_dst(pkt)->addr = (u8_t *)&hdr->dst_hwaddr.addr; net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_eth_addr); - net_buf_add(frag, sizeof(struct net_arp_hdr)); - return pkt; - -fail: - net_pkt_unref(pkt); - return NULL; } static bool arp_hdr_check(struct net_arp_hdr *arp_hdr) diff --git a/subsys/net/l2/ethernet/ethernet.c b/subsys/net/l2/ethernet/ethernet.c index 79679307d5c0a..fa6dea91145ef 100644 --- a/subsys/net/l2/ethernet/ethernet.c +++ b/subsys/net/l2/ethernet/ethernet.c @@ -523,6 +523,8 @@ static int ethernet_send(struct net_if *iface, struct net_pkt *pkt) ptype = htons(NET_ETH_PTYPE_IPV6); } else if (IS_ENABLED(CONFIG_NET_GPTP) && net_pkt_is_gptp(pkt)) { ptype = htons(NET_ETH_PTYPE_PTP); + } else if (IS_ENABLED(CONFIG_NET_LLDP) && net_pkt_is_lldp(pkt)) { + ptype = htons(NET_ETH_PTYPE_LLDP); } else if (IS_ENABLED(CONFIG_NET_ARP)) { /* Unktown type: Unqueued pkt is an ARP reply. */ @@ -559,6 +561,8 @@ static int ethernet_send(struct net_if *iface, struct net_pkt *pkt) goto error; } + net_pkt_cursor_init(pkt); + ret = api->send(net_if_get_device(iface), pkt); if (ret != 0) { eth_stats_update_errors_tx(iface); diff --git a/subsys/net/l2/ethernet/gptp/gptp_messages.c b/subsys/net/l2/ethernet/gptp/gptp_messages.c index 048c19f84c621..4c6cbb7522ee3 100644 --- a/subsys/net/l2/ethernet/gptp/gptp_messages.c +++ b/subsys/net/l2/ethernet/gptp/gptp_messages.c @@ -143,40 +143,33 @@ static void gptp_pdelay_response_timestamp_callback(struct net_pkt *pkt) #if defined(CONFIG_NET_DEBUG_NET_PKT_ALLOC) static struct net_pkt *setup_gptp_frame_debug(struct net_if *iface, + size_t extra_header, const char *caller, int line) -#define setup_gptp_frame(iface) \ - setup_gptp_frame_debug(iface, __func__, __LINE__) +#define setup_gptp_frame(iface, extra_header) \ + setup_gptp_frame_debug(iface, extra_header, __func__, __LINE__) #else -static struct net_pkt *setup_gptp_frame(struct net_if *iface) +static struct net_pkt *setup_gptp_frame(struct net_if *iface, + size_t extra_header) #endif { struct net_pkt *pkt; - struct net_buf *frag; #if defined(CONFIG_NET_DEBUG_NET_PKT_ALLOC) + pkt = net_pkt_alloc_with_buffer_debug(iface, sizeof(struct gptp_hdr) + + extra_header, AF_UNSPEC, 0, + NET_BUF_TIMEOUT, caller, line); pkt = net_pkt_get_reserve_tx_debug(NET_BUF_TIMEOUT, caller, line); #else - pkt = net_pkt_get_reserve_tx(NET_BUF_TIMEOUT); + pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct gptp_hdr) + + extra_header, AF_UNSPEC, 0, + NET_BUF_TIMEOUT); #endif if (!pkt) { return NULL; } -#if defined(CONFIG_NET_DEBUG_NET_PKT_ALLOC) - frag = net_pkt_get_reserve_tx_data_debug(NET_BUF_TIMEOUT, caller, - line); -#else - frag = net_pkt_get_reserve_tx_data(NET_BUF_TIMEOUT); -#endif - if (!frag) { - net_pkt_unref(pkt); - return NULL; - } - - net_pkt_frag_add(pkt, frag); - net_pkt_set_iface(pkt, iface); - net_pkt_set_family(pkt, AF_UNSPEC); + net_buf_add(pkt->buffer, sizeof(struct gptp_hdr) + extra_header); net_pkt_set_gptp(pkt, true); net_pkt_lladdr_src(pkt)->addr = (u8_t *)net_if_get_link_addr(iface); @@ -185,8 +178,6 @@ static struct net_pkt *setup_gptp_frame(struct net_if *iface) net_pkt_lladdr_dst(pkt)->addr = (u8_t *)&gptp_multicast_eth_addr; net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_eth_addr); - net_buf_add(frag, sizeof(struct gptp_hdr)); - return pkt; } @@ -202,7 +193,7 @@ struct net_pkt *gptp_prepare_sync(int port) iface = GPTP_PORT_IFACE(port); NET_ASSERT(iface); - pkt = setup_gptp_frame(iface); + pkt = setup_gptp_frame(iface, sizeof(struct gptp_sync)); if (!pkt) { NET_DBG("Cannot get gPTP frame"); return NULL; @@ -239,8 +230,6 @@ struct net_pkt *gptp_prepare_sync(int port) /* PTP configuration. */ (void)memset(&sync->reserved, 0, sizeof(sync->reserved)); - net_buf_add(pkt->frags, sizeof(struct gptp_sync)); - /* Update sequence number. */ port_ds->sync_seq_id++; @@ -259,7 +248,7 @@ struct net_pkt *gptp_prepare_follow_up(int port, struct net_pkt *sync) iface = GPTP_PORT_IFACE(port); NET_ASSERT(iface); - pkt = setup_gptp_frame(iface); + pkt = setup_gptp_frame(iface, sizeof(struct gptp_follow_up)); if (!pkt) { NET_DBG("Cannot get gPTP frame"); return NULL; @@ -296,8 +285,6 @@ struct net_pkt *gptp_prepare_follow_up(int port, struct net_pkt *sync) /* PTP configuration will be set by the MDSyncSend state machine. */ - net_buf_add(pkt->frags, sizeof(struct gptp_follow_up)); - return pkt; } @@ -313,7 +300,7 @@ struct net_pkt *gptp_prepare_pdelay_req(int port) iface = GPTP_PORT_IFACE(port); NET_ASSERT(iface); - pkt = setup_gptp_frame(iface); + pkt = setup_gptp_frame(iface, sizeof(struct gptp_pdelay_req)); if (!pkt) { NET_DBG("Cannot get gPTP frame"); return NULL; @@ -353,8 +340,6 @@ struct net_pkt *gptp_prepare_pdelay_req(int port) (void)memset(&req->reserved1, 0, sizeof(req->reserved1)); (void)memset(&req->reserved2, 0, sizeof(req->reserved2)); - net_buf_add(pkt->frags, sizeof(struct gptp_pdelay_req)); - /* Update sequence number. */ port_ds->pdelay_req_seq_id++; @@ -371,7 +356,7 @@ struct net_pkt *gptp_prepare_pdelay_resp(int port, struct gptp_port_ds *port_ds; struct net_pkt *pkt; - pkt = setup_gptp_frame(iface); + pkt = setup_gptp_frame(iface, sizeof(struct gptp_pdelay_resp)); if (!pkt) { NET_DBG("Cannot get gPTP frame"); return NULL; @@ -419,8 +404,6 @@ struct net_pkt *gptp_prepare_pdelay_resp(int port, memcpy(&pdelay_resp->requesting_port_id, &query->port_id, sizeof(struct gptp_port_identity)); - net_buf_add(pkt->frags, sizeof(struct gptp_pdelay_resp)); - return pkt; } @@ -434,7 +417,8 @@ struct net_pkt *gptp_prepare_pdelay_follow_up(int port, struct gptp_port_ds *port_ds; struct net_pkt *pkt; - pkt = setup_gptp_frame(iface); + pkt = setup_gptp_frame(iface, + sizeof(struct gptp_pdelay_resp_follow_up)); if (!pkt) { NET_DBG("Cannot get gPTP frame"); return NULL; @@ -483,8 +467,6 @@ struct net_pkt *gptp_prepare_pdelay_follow_up(int port, &pdelay_resp->requesting_port_id, sizeof(struct gptp_port_identity)); - net_buf_add(pkt->frags, sizeof(struct gptp_pdelay_resp_follow_up)); - return pkt; } @@ -504,7 +486,8 @@ struct net_pkt *gptp_prepare_announce(int port) iface = GPTP_PORT_IFACE(port); NET_ASSERT(iface); - pkt = setup_gptp_frame(iface); + pkt = setup_gptp_frame(iface, sizeof(struct gptp_announce) - 8 + + ntohs(global_ds->path_trace.len)); if (!pkt) { NET_DBG("Cannot get gPTP frame"); return NULL; @@ -582,17 +565,25 @@ struct net_pkt *gptp_prepare_announce(int port) sizeof(struct gptp_announce) - 8 + ntohs(global_ds->path_trace.len)); - net_buf_add(pkt->frags, sizeof(struct gptp_announce) - 8); - ann->tlv.len = global_ds->path_trace.len; - if (net_pkt_append(pkt, ntohs(global_ds->path_trace.len), - &global_ds->path_trace.path_sequence[0][0], - NET_BUF_TIMEOUT) < - ntohs(global_ds->path_trace.len)) { + net_pkt_cursor_init(pkt); + + /* setup_gptp_frame() already added all the length, so let's not + * add up more with net_pkt_skip/write_new(), let's just overwrite + */ + net_pkt_set_overwrite(pkt, true); + + if (net_pkt_skip(pkt, sizeof(struct gptp_hdr) + + sizeof(struct gptp_announce) - 8) || + net_pkt_write_new(pkt, + &global_ds->path_trace.path_sequence[0][0], + ntohs(global_ds->path_trace.len))) { goto fail; } + net_pkt_set_overwrite(pkt, false); + return pkt; fail: diff --git a/subsys/net/l2/ethernet/lldp/lldp.c b/subsys/net/l2/ethernet/lldp/lldp.c index a30f38faae2f4..1d901a22aaafd 100644 --- a/subsys/net/l2/ethernet/lldp/lldp.c +++ b/subsys/net/l2/ethernet/lldp/lldp.c @@ -88,10 +88,7 @@ static int lldp_send(struct ethernet_lldp *lldp) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e } }; int ret = 0; - struct net_eth_hdr *hdr; struct net_pkt *pkt; - struct net_buf *frag; - u16_t pos; if (!lldp->lldpdu) { /* The ethernet driver has not set the lldpdu pointer */ @@ -100,25 +97,15 @@ static int lldp_send(struct ethernet_lldp *lldp) goto out; } - pkt = net_pkt_get_reserve_tx(BUF_ALLOC_TIMEOUT); + pkt = net_pkt_alloc_with_buffer(lldp->iface, sizeof(struct net_lldpdu), + AF_UNSPEC, 0, BUF_ALLOC_TIMEOUT); if (!pkt) { ret = -ENOMEM; goto out; } - frag = net_pkt_get_frag(pkt, BUF_ALLOC_TIMEOUT); - if (!frag) { - net_pkt_unref(pkt); - ret = -ENOMEM; - goto out; - } - - net_pkt_frag_add(pkt, frag); - - net_buf_add(frag, sizeof(struct net_lldpdu)); - - if (!net_pkt_write(pkt, frag, 0, &pos, sizeof(struct net_lldpdu), - (u8_t *)lldp->lldpdu, BUF_ALLOC_TIMEOUT)) { + if (net_pkt_write_new(pkt, (u8_t *)lldp->lldpdu, + sizeof(struct net_lldpdu))) { net_pkt_unref(pkt); ret = -ENOMEM; goto out; @@ -129,11 +116,6 @@ static int lldp_send(struct ethernet_lldp *lldp) net_pkt_lladdr_dst(pkt)->addr = (u8_t *)lldp_multicast_eth_addr.addr; net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_eth_addr); - hdr = NET_ETH_HDR(pkt); - hdr->type = htons(NET_ETH_PTYPE_LLDP); - - net_pkt_set_iface(pkt, lldp->iface); - if (net_if_send_data(lldp->iface, pkt) == NET_DROP) { net_pkt_unref(pkt); ret = -EIO; diff --git a/subsys/net/lib/dns/llmnr_responder.c b/subsys/net/lib/dns/llmnr_responder.c index 203e6d4dcf16a..0c71af9f1ea67 100644 --- a/subsys/net/lib/dns/llmnr_responder.c +++ b/subsys/net/lib/dns/llmnr_responder.c @@ -548,6 +548,8 @@ static int dns_read(struct net_context *ctx, static void recv_cb(struct net_context *net_ctx, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/subsys/net/lib/dns/mdns_responder.c b/subsys/net/lib/dns/mdns_responder.c index 28e9829a000c6..4c7f6404518df 100644 --- a/subsys/net/lib/dns/mdns_responder.c +++ b/subsys/net/lib/dns/mdns_responder.c @@ -382,6 +382,8 @@ static int dns_read(struct net_context *ctx, static void recv_cb(struct net_context *net_ctx, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/subsys/net/lib/dns/resolve.c b/subsys/net/lib/dns/resolve.c index e123461400c38..63b94a706890a 100644 --- a/subsys/net/lib/dns/resolve.c +++ b/subsys/net/lib/dns/resolve.c @@ -512,6 +512,8 @@ static int dns_read(struct dns_resolve_context *ctx, static void cb_recv(struct net_context *net_ctx, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/subsys/net/lib/sockets/sockets.c b/subsys/net/lib/sockets/sockets.c index d57ac07fb49eb..f3c31c04efbd1 100644 --- a/subsys/net/lib/sockets/sockets.c +++ b/subsys/net/lib/sockets/sockets.c @@ -42,8 +42,12 @@ static inline void *get_sock_vtable( (const struct fd_op_vtable **)vtable); } -static void zsock_received_cb(struct net_context *ctx, struct net_pkt *pkt, - int status, void *user_data); +static void zsock_received_cb(struct net_context *ctx, + struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, + int status, + void *user_data); static inline int _k_fifo_wait_non_empty(struct k_fifo *fifo, int32_t timeout) { @@ -193,8 +197,13 @@ static void zsock_accepted_cb(struct net_context *new_ctx, } } -static void zsock_received_cb(struct net_context *ctx, struct net_pkt *pkt, - int status, void *user_data) { +static void zsock_received_cb(struct net_context *ctx, + struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, + int status, + void *user_data) +{ unsigned int header_len; NET_DBG("ctx=%p, pkt=%p, st=%d, user_data=%p", ctx, pkt, status, @@ -392,51 +401,38 @@ ssize_t zsock_sendto_ctx(struct net_context *ctx, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { - int err; - struct net_pkt *send_pkt; s32_t timeout = K_FOREVER; + int status; if ((flags & ZSOCK_MSG_DONTWAIT) || sock_is_nonblock(ctx)) { timeout = K_NO_WAIT; } - send_pkt = net_pkt_get_tx(ctx, timeout); - if (!send_pkt) { - errno = EAGAIN; - return -1; - } - - len = net_pkt_append(send_pkt, len, buf, timeout); - if (!len) { - net_pkt_unref(send_pkt); - errno = EAGAIN; - return -1; - } - /* Register the callback before sending in order to receive the response * from the peer. */ - err = net_context_recv(ctx, zsock_received_cb, K_NO_WAIT, ctx->user_data); - if (err < 0) { - net_pkt_unref(send_pkt); - errno = -err; + status = net_context_recv(ctx, zsock_received_cb, + K_NO_WAIT, ctx->user_data); + if (status < 0) { + errno = -status; return -1; } if (dest_addr) { - err = net_context_sendto(send_pkt, dest_addr, addrlen, NULL, - timeout, NULL, ctx->user_data); + status = net_context_sendto_new(ctx, buf, len, dest_addr, + addrlen, NULL, timeout, + NULL, ctx->user_data); } else { - err = net_context_send(send_pkt, NULL, timeout, NULL, ctx->user_data); + status = net_context_send_new(ctx, buf, len, NULL, timeout, + NULL, ctx->user_data); } - if (err < 0) { - net_pkt_unref(send_pkt); - errno = -err; + if (status < 0) { + errno = -status; return -1; } - return len; + return status; } ssize_t _impl_zsock_sendto(int sock, const void *buf, size_t len, int flags, diff --git a/tests/net/6lo/src/main.c b/tests/net/6lo/src/main.c index bcec2b872e5cd..1da63cc05d4ce 100644 --- a/tests/net/6lo/src/main.c +++ b/tests/net/6lo/src/main.c @@ -109,8 +109,6 @@ u8_t dst_mac[8] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xaa }; /* 6CO contexts */ static struct net_icmpv6_nd_opt_6co ctx1 = { - .type = 0x22, - .len = 0x02, .context_len = 0x40, .flag = 0x11, .reserved = 0, @@ -123,8 +121,6 @@ static struct net_icmpv6_nd_opt_6co ctx1 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } } static struct net_icmpv6_nd_opt_6co ctx2 = { - .type = 0x22, - .len = 0x03, .context_len = 0x80, .flag = 0x12, .reserved = 0, diff --git a/tests/net/checksum_offload/src/main.c b/tests/net/checksum_offload/src/main.c index a348a644f48f3..2a8fbde9ee30b 100644 --- a/tests/net/checksum_offload/src/main.c +++ b/tests/net/checksum_offload/src/main.c @@ -108,6 +108,33 @@ static void eth_iface_init(struct net_if *iface) ethernet_init(iface); } +static u16_t get_udp_chksum(struct net_pkt *pkt) +{ + NET_PKT_DATA_ACCESS_DEFINE(udp_access, struct net_udp_hdr); + struct net_udp_hdr *udp_hdr; + struct net_pkt_cursor backup; + + net_pkt_set_overwrite(pkt, true); + net_pkt_cursor_backup(pkt, &backup); + net_pkt_cursor_init(pkt); + + /* Let's move the cursor to UDP header */ + if (net_pkt_skip(pkt, sizeof(struct net_eth_hdr) + + net_pkt_ip_hdr_len(pkt) + + net_pkt_ipv6_ext_len(pkt))) { + return 0; + } + + udp_hdr = (struct net_udp_hdr *)net_pkt_get_data_new(pkt, &udp_access); + if (!udp_hdr) { + return 0; + } + + net_pkt_cursor_restore(pkt, &backup); + + return udp_hdr->chksum; +} + static int eth_tx_offloading_disabled(struct device *dev, struct net_pkt *pkt) { struct eth_context *context = dev->driver_data; @@ -177,8 +204,7 @@ static int eth_tx_offloading_disabled(struct device *dev, struct net_pkt *pkt) if (test_started) { u16_t chksum; - /* First frag is always ethernet header, let's skip it */ - chksum = net_udp_get_chksum(pkt, pkt->frags->frags); + chksum = get_udp_chksum(pkt); DBG("Chksum 0x%x offloading disabled\n", chksum); @@ -206,8 +232,7 @@ static int eth_tx_offloading_enabled(struct device *dev, struct net_pkt *pkt) if (test_started) { u16_t chksum; - /* First frag is always ethernet header, let's skip it */ - chksum = net_udp_get_chksum(pkt, pkt->frags->frags); + chksum = get_udp_chksum(pkt); DBG("Chksum 0x%x offloading enabled\n", chksum); @@ -673,15 +698,13 @@ static void tx_chksum_offload_enabled_test_v4(void) static void recv_cb_offload_disabled(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { - struct net_udp_hdr hdr, *udp_hdr; - - udp_hdr = net_udp_get_hdr(pkt, &hdr); - - zassert_not_null(udp_hdr, "UDP header missing"); - zassert_not_equal(udp_hdr->chksum, 0, "Checksum is not set"); + zassert_not_null(proto_hdr->udp, "UDP header missing"); + zassert_not_equal(proto_hdr->udp->chksum, 0, "Checksum is not set"); if (net_pkt_family(pkt) == AF_INET) { struct net_ipv4_hdr *ipv4 = NET_IPV4_HDR(pkt); @@ -697,15 +720,13 @@ static void recv_cb_offload_disabled(struct net_context *context, static void recv_cb_offload_enabled(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { - struct net_udp_hdr hdr, *udp_hdr; - - udp_hdr = net_udp_get_hdr(pkt, &hdr); - - zassert_not_null(udp_hdr, "UDP header missing"); - zassert_equal(udp_hdr->chksum, 0, "Checksum is set"); + zassert_not_null(proto_hdr->udp, "UDP header missing"); + zassert_equal(proto_hdr->udp->chksum, 0, "Checksum is set"); if (net_pkt_family(pkt) == AF_INET) { struct net_ipv4_hdr *ipv4 = NET_IPV4_HDR(pkt); diff --git a/tests/net/context/src/main.c b/tests/net/context/src/main.c index 604e4dea88604..b9682fd693583 100644 --- a/tests/net/context/src/main.c +++ b/tests/net/context/src/main.c @@ -508,6 +508,8 @@ static void net_ctx_sendto_v4(void) static void recv_cb(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { @@ -683,6 +685,8 @@ static void net_ctx_recv_v4_again(void) static void recv_cb_another(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { @@ -738,6 +742,8 @@ static struct k_thread thread_data; static void recv_cb_timeout(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/tests/net/icmpv6/prj.conf b/tests/net/icmpv6/prj.conf index 9a5e878f3497c..8fd3358b8690d 100644 --- a/tests/net/icmpv6/prj.conf +++ b/tests/net/icmpv6/prj.conf @@ -13,3 +13,4 @@ CONFIG_ENTROPY_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_ZTEST=y CONFIG_MAIN_STACK_SIZE=1280 + diff --git a/tests/net/icmpv6/src/main.c b/tests/net/icmpv6/src/main.c index 6e453b7043a00..89546f2bf3ff9 100644 --- a/tests/net/icmpv6/src/main.c +++ b/tests/net/icmpv6/src/main.c @@ -28,7 +28,7 @@ static int handler_status; #define TEST_MSG "foobar devnull" -#define ICMPV6_MSG_SIZE 105 +#define ICMPV6_MSG_SIZE 104 static char icmpv6_echo_req[] = "\x60\x02\xea\x12\x00\x40\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00" \ @@ -60,7 +60,9 @@ static char icmpv6_inval_chksum[] = NET_PKT_TX_SLAB_DEFINE(pkts_slab, 2); NET_BUF_POOL_DEFINE(data_pool, 2, 128, 0, NULL); -static enum net_verdict handle_test_msg(struct net_pkt *pkt) +static enum net_verdict handle_test_msg(struct net_pkt *pkt, + struct net_ipv6_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr) { struct net_buf *last = net_buf_frag_last(pkt->frags); enum net_verdict ret; @@ -94,6 +96,7 @@ void test_icmpv6(void) { k_thread_priority_set(k_current_get(), K_PRIO_COOP(7)); + struct net_ipv6_hdr *hdr; struct net_pkt *pkt; struct net_buf *frag; int ret; @@ -112,10 +115,15 @@ void test_icmpv6(void) net_pkt_set_family(pkt, AF_INET6); net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr)); - memcpy(net_buf_add(frag, sizeof(icmpv6_inval_chksum)), - icmpv6_inval_chksum, sizeof(icmpv6_inval_chksum)); + memcpy(net_buf_add(frag, ICMPV6_MSG_SIZE), + icmpv6_inval_chksum, ICMPV6_MSG_SIZE); - ret = net_icmpv6_input(pkt); + hdr = (struct net_ipv6_hdr *)pkt->buffer->data; + net_pkt_cursor_init(pkt); + net_pkt_set_overwrite(pkt, true); + net_pkt_skip(pkt, sizeof(struct net_ipv6_hdr)); + + ret = net_icmpv6_input(pkt, hdr); /**TESTPOINT: Check input*/ zassert_true(ret == NET_DROP, "Callback not called properly"); @@ -123,10 +131,15 @@ void test_icmpv6(void) handler_status = -1; frag->len = 0; - memcpy(net_buf_add(frag, sizeof(icmpv6_echo_rep)), - icmpv6_echo_rep, sizeof(icmpv6_echo_rep)); + memcpy(net_buf_add(frag, ICMPV6_MSG_SIZE), + icmpv6_echo_rep, ICMPV6_MSG_SIZE); + + hdr = (struct net_ipv6_hdr *)pkt->buffer->data; + net_pkt_cursor_init(pkt); + net_pkt_set_overwrite(pkt, true); + net_pkt_skip(pkt, sizeof(struct net_ipv6_hdr)); - ret = net_icmpv6_input(pkt); + ret = net_icmpv6_input(pkt, hdr); /**TESTPOINT: Check input*/ zassert_true(!(ret == NET_DROP || handler_status != 0), @@ -135,10 +148,15 @@ void test_icmpv6(void) handler_status = -1; frag->len = 0; - memcpy(net_buf_add(frag, sizeof(icmpv6_echo_req)), - icmpv6_echo_req, sizeof(icmpv6_echo_req)); + memcpy(net_buf_add(frag, ICMPV6_MSG_SIZE), + icmpv6_echo_req, ICMPV6_MSG_SIZE); + + hdr = (struct net_ipv6_hdr *)pkt->buffer->data; + net_pkt_cursor_init(pkt); + net_pkt_set_overwrite(pkt, true); + net_pkt_skip(pkt, sizeof(struct net_ipv6_hdr)); - ret = net_icmpv6_input(pkt); + ret = net_icmpv6_input(pkt, hdr); /**TESTPOINT: Check input*/ zassert_true(!(ret == NET_DROP || handler_status != 0), diff --git a/tests/net/ipv6/src/main.c b/tests/net/ipv6/src/main.c index 7987f702a4aaa..9bddaf2e7e353 100644 --- a/tests/net/ipv6/src/main.c +++ b/tests/net/ipv6/src/main.c @@ -1166,10 +1166,12 @@ static enum net_verdict recv_msg(struct in6_addr *src, struct in6_addr *dst) setup_ipv6_udp(pkt, src, dst, 4242, 4321); + net_pkt_cursor_init(pkt); + /* We by-pass the normal packet receiving flow in this case in order * to simplify the testing. */ - return net_ipv6_process_pkt(pkt, false); + return net_ipv6_input(pkt, false); } static int send_msg(struct in6_addr *src, struct in6_addr *dst) @@ -1297,11 +1299,15 @@ static void net_ctx_listen(struct net_context *ctx) static void recv_cb(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { ARG_UNUSED(context); ARG_UNUSED(pkt); + ARG_UNUSED(ip_hdr); + ARG_UNUSED(proto_hdr); ARG_UNUSED(status); ARG_UNUSED(user_data); diff --git a/tests/net/ipv6_fragment/src/main.c b/tests/net/ipv6_fragment/src/main.c index 42fbbaffa6317..0b0c18d9f68f2 100644 --- a/tests/net/ipv6_fragment/src/main.c +++ b/tests/net/ipv6_fragment/src/main.c @@ -1125,6 +1125,8 @@ static void add_nbr(struct net_if *iface, static enum net_verdict udp_data_received(struct net_conn *conn, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, void *user_data) { DBG("Data %p received\n", pkt); @@ -1355,6 +1357,8 @@ static void test_find_last_ipv6_fragment_hbho_2(void) ALLOC_TIMEOUT); zassert_true(ret, "IPv6 header append failed"); + net_pkt_set_overwrite(pkt, true); + net_pkt_lladdr_clear(pkt); ret = net_ipv6_find_last_ext_hdr(pkt, &next_hdr_idx, &last_hdr_pos); @@ -1398,6 +1402,8 @@ static void test_find_last_ipv6_fragment_hbho_3(void) ALLOC_TIMEOUT); zassert_true(ret, "IPv6 header append failed"); + net_pkt_set_overwrite(pkt, true); + net_pkt_lladdr_clear(pkt); ret = net_ipv6_find_last_ext_hdr(pkt, &next_hdr_idx, &last_hdr_pos); @@ -1438,6 +1444,8 @@ static void test_find_last_ipv6_fragment_hbho_frag(void) ALLOC_TIMEOUT); zassert_true(ret, "IPv6 header append failed"); + net_pkt_set_overwrite(pkt, true); + net_pkt_lladdr_clear(pkt); ret = net_ipv6_find_last_ext_hdr(pkt, &next_hdr_idx, &last_hdr_pos); @@ -1477,6 +1485,8 @@ static void test_find_last_ipv6_fragment_hbho_frag_1(void) ipv6_hbho_frag_1, ALLOC_TIMEOUT); zassert_true(ret, "IPv6 header append failed"); + net_pkt_set_overwrite(pkt, true); + net_pkt_lladdr_clear(pkt); ret = net_ipv6_find_last_ext_hdr(pkt, &next_hdr_idx, &last_hdr_pos); @@ -1548,6 +1558,8 @@ static void test_send_ipv6_fragment(void) NET_IPV6_HDR(pkt)->len = htons(total_len); + net_pkt_set_overwrite(pkt, true); + net_udp_set_chksum(pkt, pkt->frags); test_failed = false; @@ -1586,6 +1598,8 @@ static void test_send_ipv6_fragment_large_hbho(void) ipv6_large_hbho, ALLOC_TIMEOUT); zassert_true(ret, "IPv6 header append failed"); + net_pkt_set_overwrite(pkt, true); + net_pkt_lladdr_clear(pkt); total_len = net_pkt_get_len(pkt) - sizeof(struct net_ipv6_hdr); diff --git a/tests/net/mld/src/main.c b/tests/net/mld/src/main.c index 3f155a2d4542e..574fe8a387f47 100644 --- a/tests/net/mld/src/main.c +++ b/tests/net/mld/src/main.c @@ -96,17 +96,29 @@ static void net_test_iface_init(struct net_if *iface) NET_LINK_ETHERNET); } +static struct net_icmp_hdr *get_icmp_hdr(struct net_pkt *pkt) +{ + net_pkt_cursor_init(pkt); + + net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt) + + net_pkt_ipv6_ext_len(pkt)); + + return (struct net_icmp_hdr *)net_pkt_cursor_get_pos(pkt); +} + #define NET_ICMP_HDR(pkt) ((struct net_icmp_hdr *)net_pkt_icmp_data(pkt)) static int tester_send(struct device *dev, struct net_pkt *pkt) { - struct net_icmp_hdr *icmp = NET_ICMP_HDR(pkt); + struct net_icmp_hdr *icmp; if (!pkt->frags) { TC_ERROR("No data to send!\n"); return -ENODATA; } + icmp = get_icmp_hdr(pkt); + if (icmp->type == NET_ICMPV6_MLDv2) { /* FIXME, add more checks here */ @@ -353,12 +365,12 @@ static void send_query(struct net_if *iface) net_pkt_append_be16(pkt, 0); /* Resv, S, QRV and QQIC */ net_pkt_append_be16(pkt, 0); /* number of addresses */ + net_pkt_set_ipv6_ext_len(pkt, ROUTER_ALERT_LEN); + net_ipv6_finalize(pkt, NET_IPV6_NEXTHDR_HBHO); net_pkt_set_iface(pkt, iface); - net_pkt_set_ipv6_ext_len(pkt, ROUTER_ALERT_LEN); - net_pkt_write_be16(pkt, pkt->frags, NET_IPV6H_LEN + ROUTER_ALERT_LEN + 2, &pos, ntohs(net_calc_chksum_icmpv6(pkt))); @@ -367,7 +379,9 @@ static void send_query(struct net_if *iface) } /* We are not really interested to parse the query at this point */ -static enum net_verdict handle_mld_query(struct net_pkt *pkt) +static enum net_verdict handle_mld_query(struct net_pkt *pkt, + struct net_ipv6_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr) { is_query_received = true; diff --git a/tests/net/net_pkt_new/CMakeLists.txt b/tests/net/net_pkt_new/CMakeLists.txt new file mode 100644 index 0000000000000..bcb30403b1a03 --- /dev/null +++ b/tests/net/net_pkt_new/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/net/net_pkt_new/prj.conf b/tests/net/net_pkt_new/prj.conf new file mode 100644 index 0000000000000..764d86554af08 --- /dev/null +++ b/tests/net/net_pkt_new/prj.conf @@ -0,0 +1,14 @@ +CONFIG_NET_TEST=y +CONFIG_ZTEST=y + +CONFIG_NETWORKING=y +CONFIG_NET_L2_ETHERNET=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=n +CONFIG_NET_ARP=y +CONFIG_NET_UDP=y +CONFIG_NET_BUF_LOG=y +CONFIG_NET_LOG=y + +CONFIG_ENTROPY_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y diff --git a/tests/net/net_pkt_new/src/main.c b/tests/net/net_pkt_new/src/main.c new file mode 100644 index 0000000000000..5bd3fbf442392 --- /dev/null +++ b/tests/net/net_pkt_new/src/main.c @@ -0,0 +1,615 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static u8_t mac_addr[sizeof(struct net_eth_addr)]; +static struct net_if *eth_if; +static u8_t small_buffer[512]; + +/************************\ + * FAKE ETHERNET DEVICE * +\************************/ + +static void fake_dev_iface_init(struct net_if *iface) +{ + if (mac_addr[2] == 0) { + /* 00-00-5E-00-53-xx Documentation RFC 7042 */ + mac_addr[0] = 0x00; + mac_addr[1] = 0x00; + mac_addr[2] = 0x5E; + mac_addr[3] = 0x00; + mac_addr[4] = 0x53; + mac_addr[5] = sys_rand32_get(); + } + + net_if_set_link_addr(iface, mac_addr, 6, NET_LINK_ETHERNET); + + eth_if = iface; +} + +static int fake_dev_send(struct device *dev, struct net_pkt *pkt) +{ + return 0; +} + +int fake_dev_init(struct device *dev) +{ + ARG_UNUSED(dev); + + return 0; +} + +#if defined(CONFIG_NET_L2_ETHERNET) +static const struct ethernet_api fake_dev_api = { + .iface_api.init = fake_dev_iface_init, + .send = fake_dev_send, +}; + +#define _ETH_L2_LAYER ETHERNET_L2 +#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(ETHERNET_L2) +#else +static const struct dummy_api fake_dev_api = { + .iface_api.init = fake_dev_iface_init, + .send = fake_dev_send, +}; + +#define _ETH_L2_LAYER DUMMY_L2 +#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(DUMMY_L2) +#endif + +NET_DEVICE_INIT(fake_dev, "fake_dev", + fake_dev_init, NULL, NULL, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &fake_dev_api, _ETH_L2_LAYER, _ETH_L2_CTX_TYPE, 1500); + +/*********************\ + * UTILITY FUNCTIONS * +\*********************/ + +static bool pkt_is_of_size(struct net_pkt *pkt, size_t size) +{ + return (net_pkt_available_buffer(pkt) == size); +} + +static void pkt_print_cursor(struct net_pkt *pkt) +{ + if (!pkt || !pkt->cursor.buf || !pkt->cursor.pos) { + printk("Unknown position\n"); + } else { + printk("Position %zu (%p) in net_buf %p (data %p)\n", + pkt->cursor.pos - pkt->cursor.buf->data, + pkt->cursor.pos, pkt->cursor.buf, + pkt->cursor.buf->data); + } +} + + +/*****************************\ + * HOW TO ALLOCATE - 2 TESTS * +\*****************************/ + +static void test_net_pkt_allocate_wo_buffer(void) +{ + struct net_pkt *pkt; + + /* How to allocate a packet, with no buffer */ + pkt = net_pkt_alloc(K_NO_WAIT); + zassert_true(pkt != NULL, "Pkt not allocated"); + + /* Freeing the packet */ + net_pkt_unref(pkt); + zassert_true(atomic_get(&pkt->atomic_ref) == 0, + "Pkt not properly unreferenced"); + + /* Note that, if you already know the iface to which the packet + * belongs to, you will be able to use net_pkt_alloc_on_iface(). + */ + pkt = net_pkt_alloc_on_iface(eth_if, K_NO_WAIT); + zassert_true(pkt != NULL, "Pkt not allocated"); + + net_pkt_unref(pkt); + zassert_true(atomic_get(&pkt->atomic_ref) == 0, + "Pkt not properly unreferenced"); +} + +static void test_net_pkt_allocate_with_buffer(void) +{ + struct net_pkt *pkt; + + /* How to allocate a packet, with buffer + * a) - with a size that will fit MTU, let's say 512 bytes + * Note: we don't care of the family/protocol for now + */ + pkt = net_pkt_alloc_with_buffer(eth_if, 512, + AF_UNSPEC, 0, K_NO_WAIT); + zassert_true(pkt != NULL, "Pkt not allocated"); + + /* Did we get the requested size? */ + zassert_true(pkt_is_of_size(pkt, 512), "Pkt size is not right"); + + /* Freeing the packet */ + net_pkt_unref(pkt); + zassert_true(atomic_get(&pkt->atomic_ref) == 0, + "Pkt not properly unreferenced"); + + /* + * b) - with a size that will not fit MTU, let's say 1800 bytes + * Note: again we don't care of family/protocol for now. + */ + pkt = net_pkt_alloc_with_buffer(eth_if, 1800, + AF_UNSPEC, 0, K_NO_WAIT); + zassert_true(pkt != NULL, "Pkt not allocated"); + + zassert_false(pkt_is_of_size(pkt, 1800), "Pkt size is not right"); + zassert_true(pkt_is_of_size(pkt, net_if_get_mtu(eth_if)), + "Pkt size is not right"); + + /* Freeing the packet */ + net_pkt_unref(pkt); + zassert_true(atomic_get(&pkt->atomic_ref) == 0, + "Pkt not properly unreferenced"); + + /* + * c) - Now with 512 bytes but on IPv4/UDP + */ + pkt = net_pkt_alloc_with_buffer(eth_if, 512, AF_INET, + IPPROTO_UDP, K_NO_WAIT); + zassert_true(pkt != NULL, "Pkt not allocated"); + + /* Because 512 + NET_IPV4UDPH_LEN fits MTU, total must be that one */ + zassert_true(pkt_is_of_size(pkt, 512 + NET_IPV4UDPH_LEN), + "Pkt overall size does not match"); + + /* Freeing the packet */ + net_pkt_unref(pkt); + zassert_true(atomic_get(&pkt->atomic_ref) == 0, + "Pkt not properly unreferenced"); + + /* + * c) - Now with 1800 bytes but on IPv4/UDP + */ + pkt = net_pkt_alloc_with_buffer(eth_if, 1800, AF_INET, + IPPROTO_UDP, K_NO_WAIT); + zassert_true(pkt != NULL, "Pkt not allocated"); + + /* Because 1800 + NET_IPV4UDPH_LEN won't fit MTU, payload size + * should be MTU + */ + zassert_true(net_pkt_available_buffer(pkt) == + net_if_get_mtu(eth_if), + "Payload buf size does not match for ipv4/udp"); + + /* Freeing the packet */ + net_pkt_unref(pkt); + zassert_true(atomic_get(&pkt->atomic_ref) == 0, + "Pkt not properly unreferenced"); +} + +/********************************\ + * HOW TO R/W A PACKET - TESTS * +\********************************/ + +static void test_net_pkt_basics_of_rw(void) +{ + struct net_pkt *pkt; + int ret; + + pkt = net_pkt_alloc_with_buffer(eth_if, 512, + AF_UNSPEC, 0, K_NO_WAIT); + zassert_true(pkt != NULL, "Pkt not allocated"); + + /* Once newly allocated with buffer, + * a packet has no data accounted for in its buffer + */ + zassert_true(net_pkt_get_len(pkt) == 0, + "Pkt initial length should be 0"); + + /* This is done through net_buf which can distinguish + * the size of a buffer from the length of the data in it. + */ + + /* Let's subsequently write 1 byte, then 2 bytes and 4 bytes + * We write values made of 0s + */ + ret = net_pkt_write_u8_new(pkt, 0); + zassert_true(ret == 0, "Pkt write failed"); + + /* Length should be 1 now */ + zassert_true(net_pkt_get_len(pkt) == 1, "Pkt length mismatch"); + + ret = net_pkt_write_be16_new(pkt, 0); + zassert_true(ret == 0, "Pkt write failed"); + + /* Length should be 3 now */ + zassert_true(net_pkt_get_len(pkt) == 3, "Pkt length mismatch"); + + ret = net_pkt_write_be32_new(pkt, 0); + zassert_true(ret == 0, "Pkt write failed"); + + /* Length should be 7 now */ + zassert_true(net_pkt_get_len(pkt) == 7, "Pkt length mismatch"); + + /* All these writing functions use net_ptk_write(), which works + * this way: + */ + ret = net_pkt_write_new(pkt, small_buffer, 9); + zassert_true(ret == 0, "Pkt write failed"); + + /* Length should be 16 now */ + zassert_true(net_pkt_get_len(pkt) == 16, "Pkt length mismatch"); + + /* Now let's say you want to memset some data */ + ret = net_pkt_memset(pkt, 0, 4); + zassert_true(ret == 0, "Pkt memset failed"); + + /* Length should be 20 now */ + zassert_true(net_pkt_get_len(pkt) == 20, "Pkt length mismatch"); + + /* So memset affects the length exactly as write does */ + + /* Sometimes you might want to advance in the buffer without caring + * what's written there since you'll eventually come back for that. + * net_pkt_skip() is used for it. + * Note: usally you will not have to use that function a lot yourself. + */ + ret = net_pkt_skip(pkt, 20); + zassert_true(ret == 0, "Pkt skip failed"); + + /* Length should be 40 now */ + zassert_true(net_pkt_get_len(pkt) == 40, "Pkt length mismatch"); + + /* Again, skip affected the length also, like a write + * But wait a minute: how to get back then, in order to write at + * the position we just skipped? + * + * So let's introduce the concept of buffer cursor. (which could + * be named 'cursor' if such name has more relevancy. Basically, each + * net_pkt embeds such 'cursor': it's like a head of a tape + * recorder/reader, it holds the current position in the buffer where + * you can r/w. All operations use and update it below. + * There is, however, a catch: buffer is described through net_buf + * and these are like a simple linked-list. + * Which means that unlike a tape recorder/reader: you are not + * able to go backward. Only back from starting point and forward. + * Thus why there is a net_pkt_cursor_init(pkt) which will let you going + * back from the start. We could hold more info in order to avoid that, + * but that would mean growing each an every net_buf. + */ + net_pkt_cursor_init(pkt); + + /* But isn't it so that if I want to go at the previous position I + * skipped, I'll use skip again but then won't it affect again the + * length? + * Answer is yes. Hopefully there is a mean to avoid that. Basically + * for data that already "exists" in the buffer (aka: data accounted + * for in the buffer, through the length) you'll need to set the packet + * to overwrite: all subsequent operations will then work on existing + * data and will not affect the length (it won't add more data) + */ + net_pkt_set_overwrite(pkt, true); + + zassert_true(net_pkt_is_being_overwritten(pkt), + "Pkt is not set to overwrite"); + + /* Ok so previous skipped position was at offset 20 */ + ret = net_pkt_skip(pkt, 20); + zassert_true(ret == 0, "Pkt skip failed"); + + /* Length should _still_ be 40 */ + zassert_true(net_pkt_get_len(pkt) == 40, "Pkt length mismatch"); + + /* And you can write stuff */ + ret = net_pkt_write_le32_new(pkt, 0); + zassert_true(ret == 0, "Pkt write failed"); + + /* Again, length should _still_ be 40 */ + zassert_true(net_pkt_get_len(pkt) == 40, "Pkt length mismatch"); + + /* Let's memset the rest */ + ret = net_pkt_memset(pkt, 0, 16); + zassert_true(ret == 0, "Pkt memset failed"); + + /* Again, length should _still_ be 40 */ + zassert_true(net_pkt_get_len(pkt) == 40, "Pkt length mismatch"); + + /* We are now back at the end of the existing data in the buffer + * Since overwrite is still on, we should not be able to r/w + * anything. + * This is completely nominal, as being set, overwrite allows r/w only + * on existing data in the buffer: + */ + ret = net_pkt_write_be32_new(pkt, 0); + zassert_true(ret != 0, "Pkt write succeeded where it shouldn't have"); + + /* Logically, in order to be able to add new data in the buffer, + * overwrite should be disabled: + */ + net_pkt_set_overwrite(pkt, false); + + /* But it will fail: */ + ret = net_pkt_write_le32_new(pkt, 0); + zassert_true(ret != 0, "Pkt write succeeded?"); + + /* Why is that? + * This is because in case of r/w error: the iterator is invalidated. + * This a design choice, once you get a r/w error it means your code + * messed up requesting smaller buffer than you actually needed, or + * writing too much data than it should have been etc...). + * So you must drop your packet entirely. + */ + + /* Freeing the packet */ + net_pkt_unref(pkt); + zassert_true(atomic_get(&pkt->atomic_ref) == 0, + "Pkt not properly unreferenced"); +} + +void test_net_pkt_advanced_basics(void) +{ + struct net_pkt_cursor backup; + struct net_pkt *pkt; + int ret; + + pkt = net_pkt_alloc_with_buffer(eth_if, 512, + AF_INET, IPPROTO_UDP, K_NO_WAIT); + zassert_true(pkt != NULL, "Pkt not allocated"); + + pkt_print_cursor(pkt); + + /* As stated earlier, initializing the cursor, is the way to go + * back from the start in the buffer (either header or payload then). + * We also showed that using net_pkt_skip() could be used to move + * forward in the buffer. + * But what if you are far in the buffer, you need to go backward, + * and back again to your previous position? + * You could certainly do: + */ + ret = net_pkt_write_new(pkt, small_buffer, 20); + zassert_true(ret == 0, "Pkt write failed"); + + pkt_print_cursor(pkt); + + net_pkt_cursor_init(pkt); + + pkt_print_cursor(pkt); + + /* ... do something here ... */ + + /* And finally go back with overwrite/skip: */ + net_pkt_set_overwrite(pkt, true); + ret = net_pkt_skip(pkt, 20); + zassert_true(ret == 0, "Pkt skip failed"); + net_pkt_set_overwrite(pkt, false); + + pkt_print_cursor(pkt); + + /* In this example, do not focus on the 20 bytes. It is just for + * the sake of the example. + * The other method is backup/restore the packet cursor. + */ + net_pkt_cursor_backup(pkt, &backup); + + net_pkt_cursor_init(pkt); + + /* ... do something here ... */ + + /* and restore: */ + net_pkt_cursor_restore(pkt, &backup); + + pkt_print_cursor(pkt); + + /* Another feature, is how you access your data. Earlier was + * presented basic r/w functions. But sometime you might want to + * access your data directly through a structure/type etc... + * Due to the "fragmented" possible nature of your buffer, you + * need to know if the data you are trying to access is in + * contiguous area. + * For this, you'll use: + */ + ret = (int) net_pkt_is_contiguous(pkt, 4); + zassert_true(ret == 1, "Pkt contiguity check failed"); + + /* If that's successful you should be able to get the actual + * position in the buffer and cast it to the type you want. + */ + { + u32_t *val = (u32_t *)net_pkt_cursor_get_pos(pkt); + + *val = 0; + /* etc... */ + } + + /* However, to advance your cursor, since none of the usual r/w + * functions got used: net_pkt_skip() should be called relevantly: + */ + net_pkt_skip(pkt, 4); + + /* Freeing the packet */ + net_pkt_unref(pkt); + zassert_true(atomic_get(&pkt->atomic_ref) == 0, + "Pkt not properly unreferenced"); + + /* Obviously one will very rarely use these 2 last low level functions + * - net_pkt_is_contiguous() + * - net_pkt_cursor_update() + * + * Let's see why next. + */ +} + +void test_net_pkt_easier_rw_usage(void) +{ + struct net_pkt *pkt; + int ret; + + pkt = net_pkt_alloc_with_buffer(eth_if, 512, + AF_INET, IPPROTO_UDP, K_NO_WAIT); + zassert_true(pkt != NULL, "Pkt not allocated"); + + /* In net core, all goes down in fine to header manipulation. + * Either it's an IP header, UDP, ICMP, TCP one etc... + * One would then prefer to access those directly via there + * descriptors (struct net_udp_hdr, struct net_icmp_hdr, ...) + * rather than building it byte by bytes etc... + * + * As seen earlier, it is possible to cast on current position. + * However, due to the "fragmented" possible nature of the buffer, + * it should also be possible to handle the case the data being + * accessed is scattered on 1+ net_buf. + * + * To avoid redoing the contiguity check, cast or copy on failure, + * a complex type named struct net_pkt_header_access exists. + * It solves both cases (accessing data contiguous or not), without + * the need for runtime allocation (all is on stack) + */ + { + NET_PKT_DATA_ACCESS_DEFINE(ip_access, struct net_ipv4_hdr); + struct net_ipv4_hdr *ip_hdr; + + ip_hdr = (struct net_ipv4_hdr *) + net_pkt_get_data_new(pkt, &ip_access); + zassert_not_null(ip_hdr, "Accessor failed"); + + ip_hdr->tos = 0x00; + + ret = net_pkt_set_data(pkt, &ip_access); + zassert_true(ret == 0, "Accessor failed"); + + zassert_true(net_pkt_get_len(pkt) == NET_IPV4H_LEN, + "Pkt length mismatch"); + } + + /* As you can notice: get/set take also care of handling the cursor + * and updating the packet length relevantly thus why packet length + * has properly grown. + */ + + /* Freeing the packet */ + net_pkt_unref(pkt); + zassert_true(atomic_get(&pkt->atomic_ref) == 0, + "Pkt not properly unreferenced"); +} + +u8_t b5_data[10] = "qrstuvwxyz"; +struct net_buf b5 = { + .ref = 1, + .data = b5_data, + .len = 0, + .size = 0, +}; + +u8_t b4_data[4] = "mnop"; +struct net_buf b4 = { + .frags = &b5, + .ref = 1, + .data = b4_data, + .len = sizeof(b4_data) - 2, + .size = sizeof(b4_data), +}; + +struct net_buf b3 = { + .frags = &b4, + .ref = 1, +}; + +u8_t b2_data[8] = "efghijkl"; +struct net_buf b2 = { + .frags = &b3, + .ref = 1, + .data = b2_data, + .len = 0, + .size = sizeof(b2_data), +}; + +u8_t b1_data[4] = "abcd"; +struct net_buf b1 = { + .frags = &b2, + .ref = 1, + .data = b1_data, + .len = sizeof(b1_data) - 2, + .size = sizeof(b1_data), +}; + +void test_net_pkt_copy(void) +{ + struct net_pkt *pkt_src; + struct net_pkt *pkt_dst; + + pkt_src = net_pkt_alloc_on_iface(eth_if, K_NO_WAIT); + zassert_true(pkt_src != NULL, "Pkt not allocated"); + + pkt_print_cursor(pkt_src); + + /* Let's append the buffers */ + net_pkt_append_buffer(pkt_src, &b1); + + net_pkt_set_overwrite(pkt_src, true); + + /* There should be some space left */ + zassert_true(net_pkt_available_buffer(pkt_src) != 0, "No space left?"); + /* Length should be 4 */ + zassert_true(net_pkt_get_len(pkt_src) == 4, "Wrong length"); + + /* Actual space left is 12 (in b1, b2 and b4) */ + zassert_true(net_pkt_available_buffer(pkt_src) == 12, + "Wrong space left?"); + + pkt_print_cursor(pkt_src); + + /* Now let's clone the pkt + * This will test net_pkt_copy_new() as it usses it for the buffers + */ + pkt_dst = net_pkt_clone_new(pkt_src, K_NO_WAIT); + zassert_true(pkt_dst != NULL, "Pkt not clone"); + + /* Cloning does not take into account left space, + * but only occupied one + */ + zassert_true(net_pkt_available_buffer(pkt_dst) == 0, "Space left"); + zassert_true(net_pkt_get_len(pkt_src) == net_pkt_get_len(pkt_dst), + "Not same amount?"); + + /* It also did not care to copy the net_buf itself, only the content + * so, knowing that the base buffer size is bigger than necessary, + * pkt_dst has only one net_buf + */ + zassert_true(pkt_dst->buffer->frags == NULL, "Not only one buffer?"); + + /* Freeing the packet */ + pkt_src->buffer = NULL; + net_pkt_unref(pkt_src); + zassert_true(atomic_get(&pkt_src->atomic_ref) == 0, + "Pkt not properly unreferenced"); + net_pkt_unref(pkt_dst); + zassert_true(atomic_get(&pkt_dst->atomic_ref) == 0, + "Pkt not properly unreferenced"); +} + +void test_main(void) +{ + eth_if = net_if_get_default(); + + ztest_test_suite(net_pkt_tests, + ztest_unit_test(test_net_pkt_allocate_wo_buffer), + ztest_unit_test(test_net_pkt_allocate_with_buffer), + ztest_unit_test(test_net_pkt_basics_of_rw), + ztest_unit_test(test_net_pkt_advanced_basics), + ztest_unit_test(test_net_pkt_easier_rw_usage), + ztest_unit_test(test_net_pkt_copy) + ); + + ztest_run_test_suite(net_pkt_tests); +} diff --git a/tests/net/net_pkt_new/testcase.yaml b/tests/net/net_pkt_new/testcase.yaml new file mode 100644 index 0000000000000..73cfe9ee1a5bc --- /dev/null +++ b/tests/net/net_pkt_new/testcase.yaml @@ -0,0 +1,7 @@ +common: + depends_on: netif + platform_whitelist: native_posix qemu_x86 qemu_cortex_m3 +tests: + net.packet: + min_ram: 20 + tags: net diff --git a/tests/net/tcp/src/main.c b/tests/net/tcp/src/main.c index 25d1f2661ccc9..600ebf975f78c 100644 --- a/tests/net/tcp/src/main.c +++ b/tests/net/tcp/src/main.c @@ -229,6 +229,8 @@ static struct ud *returned_ud; static enum net_verdict test_ok(struct net_conn *conn, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, void *user_data) { struct ud *ud = (struct ud *)user_data; @@ -254,6 +256,8 @@ static enum net_verdict test_ok(struct net_conn *conn, static enum net_verdict test_fail(struct net_conn *conn, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, void *user_data) { /* This function should never be called as there should not @@ -1451,6 +1455,7 @@ static bool test_tcp_seq_validity(void) struct net_tcp *tcp = v6_ctx->tcp; u8_t flags = NET_TCP_RST; struct net_pkt *pkt = NULL; + struct net_tcp_hdr hdr, *tcp_hdr; int ret; ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL, @@ -1460,34 +1465,40 @@ static bool test_tcp_seq_validity(void) return false; } - tcp->send_ack = sys_get_be32(NET_TCP_HDR(pkt)->seq) - + tcp_hdr = net_tcp_get_hdr(pkt, &hdr); + if (!tcp_hdr) { + DBG("Grabbing TCP hdr failed\n"); + return false; + } + + tcp->send_ack = sys_get_be32(tcp_hdr->seq) - get_recv_wnd(tcp) / 2; - if (!net_tcp_validate_seq(tcp, pkt)) { + if (!net_tcp_validate_seq(tcp, tcp_hdr)) { DBG("1) Sequence validation failed (send_ack %u vs seq %u)\n", - tcp->send_ack, sys_get_be32(NET_TCP_HDR(pkt)->seq)); + tcp->send_ack, sys_get_be32(tcp_hdr->seq)); return false; } - tcp->send_ack = sys_get_be32(NET_TCP_HDR(pkt)->seq); - if (!net_tcp_validate_seq(tcp, pkt)) { + tcp->send_ack = sys_get_be32(tcp_hdr->seq); + if (!net_tcp_validate_seq(tcp, tcp_hdr)) { DBG("2) Sequence validation failed (send_ack %u vs seq %u)\n", - tcp->send_ack, sys_get_be32(NET_TCP_HDR(pkt)->seq)); + tcp->send_ack, sys_get_be32(tcp_hdr->seq)); return false; } - tcp->send_ack = sys_get_be32(NET_TCP_HDR(pkt)->seq) + + tcp->send_ack = sys_get_be32(tcp_hdr->seq) + 2 * get_recv_wnd(tcp); - if (net_tcp_validate_seq(tcp, pkt)) { + if (net_tcp_validate_seq(tcp, tcp_hdr)) { DBG("3) Sequence validation failed (send_ack %u vs seq %u)\n", - tcp->send_ack, sys_get_be32(NET_TCP_HDR(pkt)->seq)); + tcp->send_ack, sys_get_be32(tcp_hdr->seq)); return false; } - tcp->send_ack = sys_get_be32(NET_TCP_HDR(pkt)->seq) - + tcp->send_ack = sys_get_be32(tcp_hdr->seq) - 2 * get_recv_wnd(tcp); - if (net_tcp_validate_seq(tcp, pkt)) { + if (net_tcp_validate_seq(tcp, tcp_hdr)) { DBG("4) Sequence validation failed (send_ack %u vs seq %u)\n", - tcp->send_ack, sys_get_be32(NET_TCP_HDR(pkt)->seq)); + tcp->send_ack, sys_get_be32(tcp_hdr->seq)); return false; } diff --git a/tests/net/traffic_class/src/main.c b/tests/net/traffic_class/src/main.c index 957b4e6d31319..e4d334519c4cb 100644 --- a/tests/net/traffic_class/src/main.c +++ b/tests/net/traffic_class/src/main.c @@ -686,6 +686,8 @@ static void traffic_class_send_data_mix_all_2(void) static void recv_cb(struct net_context *context, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, int status, void *user_data) { diff --git a/tests/net/udp/src/main.c b/tests/net/udp/src/main.c index 50ad4a6af093c..be3f85bb0a5f2 100644 --- a/tests/net/udp/src/main.c +++ b/tests/net/udp/src/main.c @@ -151,6 +151,8 @@ static struct ud *returned_ud; static enum net_verdict test_ok(struct net_conn *conn, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, void *user_data) { struct ud *ud = (struct ud *)user_data; @@ -176,6 +178,8 @@ static enum net_verdict test_ok(struct net_conn *conn, static enum net_verdict test_fail(struct net_conn *conn, struct net_pkt *pkt, + union net_ip_header *ip_hdr, + union net_proto_header *proto_hdr, void *user_data) { /* This function should never be called as there should not