diff --git a/subsys/net/ip/ipv4.c b/subsys/net/ip/ipv4.c index 6196edd89ea..69251078ec8 100644 --- a/subsys/net/ip/ipv4.c +++ b/subsys/net/ip/ipv4.c @@ -67,22 +67,24 @@ struct net_buf *net_ipv4_create_raw(struct net_buf *buf, struct net_buf *net_ipv4_create(struct net_context *context, struct net_buf *buf, - const struct in_addr *addr) + const struct in_addr *src, + const struct in_addr *dst) { - const struct in_addr *src; - NET_ASSERT(((struct sockaddr_in_ptr *)&context->local)->sin_addr); - src = ((struct sockaddr_in_ptr *)&context->local)->sin_addr; + if (!src) { + src = ((struct sockaddr_in_ptr *)&context->local)->sin_addr; + } - if (net_is_ipv4_addr_unspecified(src) || net_is_ipv4_addr_mcast(src)) { + if (net_is_ipv4_addr_unspecified(src) + || net_is_ipv4_addr_mcast(src)) { src = &net_nbuf_iface(buf)->ipv4.unicast[0].address.in_addr; } return net_ipv4_create_raw(buf, net_nbuf_ll_reserve(buf), src, - addr, + dst, net_context_get_iface(context), net_context_get_ip_proto(context)); } diff --git a/subsys/net/ip/ipv4.h b/subsys/net/ip/ipv4.h index 338e45ae514..6d2de3d6414 100644 --- a/subsys/net/ip/ipv4.h +++ b/subsys/net/ip/ipv4.h @@ -56,13 +56,15 @@ struct net_buf *net_ipv4_create_raw(struct net_buf *buf, * * @param context Network context for a connection * @param buf Network buffer + * @param src_addr Source address, or NULL to choose a default * @param dst_addr Destination IPv4 address * * @return Return network buffer that contains the IPv6 packet. */ struct net_buf *net_ipv4_create(struct net_context *context, struct net_buf *buf, - const struct in_addr *addr); + const struct in_addr *src_addr, + const struct in_addr *dst_addr); /** * @brief Finalize IPv4 packet. It should be called right before diff --git a/subsys/net/ip/ipv6.c b/subsys/net/ip/ipv6.c index 9c00c193d1a..3404e56d07b 100644 --- a/subsys/net/ip/ipv6.c +++ b/subsys/net/ip/ipv6.c @@ -328,23 +328,25 @@ struct net_buf *net_ipv6_create_raw(struct net_buf *buf, struct net_buf *net_ipv6_create(struct net_context *context, struct net_buf *buf, - const struct in6_addr *addr) + const struct in6_addr *src, + const struct in6_addr *dst) { - const struct in6_addr *src; - NET_ASSERT(((struct sockaddr_in6_ptr *)&context->local)->sin6_addr); - src = ((struct sockaddr_in6_ptr *)&context->local)->sin6_addr; + if (!src) { + src = ((struct sockaddr_in6_ptr *)&context->local)->sin6_addr; + } - if (net_is_ipv6_addr_unspecified(src) || net_is_ipv6_addr_mcast(src)) { + if (net_is_ipv6_addr_unspecified(src) + || net_is_ipv6_addr_mcast(src)) { src = net_if_ipv6_select_src_addr(net_nbuf_iface(buf), - (struct in6_addr *)addr); + (struct in6_addr *)dst); } return net_ipv6_create_raw(buf, net_nbuf_ll_reserve(buf), src, - addr, + dst, net_context_get_iface(context), net_context_get_ip_proto(context)); } diff --git a/subsys/net/ip/ipv6.h b/subsys/net/ip/ipv6.h index 04c8cdce125..a4caf6338b3 100644 --- a/subsys/net/ip/ipv6.h +++ b/subsys/net/ip/ipv6.h @@ -154,12 +154,14 @@ struct net_buf *net_ipv6_create_raw(struct net_buf *buf, * * @param context Network context for a connection * @param buf Network buffer + * @param src_addr Source address, or NULL to choose a default from context * @param dst_addr Destination IPv6 address * * @return Return network buffer that contains the IPv6 packet. */ struct net_buf *net_ipv6_create(struct net_context *context, struct net_buf *buf, + const struct in6_addr *src_addr, const struct in6_addr *dst_addr); /** diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index fcf3db6104f..d2baff9b095 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -583,14 +583,15 @@ int net_context_listen(struct net_context *context, int backlog) #endif /* CONFIG_NET_DEBUG_CONTEXT */ static inline int send_control_segment(struct net_context *context, + const struct sockaddr_ptr *local, const struct sockaddr *remote, int flags, const char *msg) { struct net_buf *buf = NULL; int ret; - ret = net_tcp_prepare_segment(context->tcp, flags, - NULL, 0, remote, &buf); + ret = net_tcp_prepare_segment(context->tcp, flags, NULL, 0, + local, remote, &buf); if (ret) { return ret; } @@ -610,13 +611,14 @@ static inline int send_syn(struct net_context *context, { net_tcp_change_state(context->tcp, NET_TCP_SYN_SENT); - return send_control_segment(context, remote, NET_TCP_SYN, "SYN"); + return send_control_segment(context, NULL, remote, NET_TCP_SYN, "SYN"); } static inline int send_syn_ack(struct net_context *context, + struct sockaddr_ptr *local, struct sockaddr *remote) { - return send_control_segment(context, remote, + return send_control_segment(context, local, remote, NET_TCP_SYN | NET_TCP_ACK, "SYN_ACK"); } @@ -624,13 +626,13 @@ static inline int send_syn_ack(struct net_context *context, static inline int send_fin(struct net_context *context, struct sockaddr *remote) { - return send_control_segment(context, remote, NET_TCP_FIN, "FIN"); + return send_control_segment(context, NULL, remote, NET_TCP_FIN, "FIN"); } static inline int send_fin_ack(struct net_context *context, struct sockaddr *remote) { - return send_control_segment(context, remote, + return send_control_segment(context, NULL, remote, NET_TCP_FIN | NET_TCP_ACK, "FIN_ACK"); } @@ -1098,6 +1100,32 @@ static void ack_timeout(struct k_work *work) net_tcp_change_state(tcp, NET_TCP_LISTEN); } +static void buf_get_sockaddr(sa_family_t family, struct net_buf *buf, + struct sockaddr_ptr *addr) +{ + memset(addr, 0, sizeof(*addr)); + +#if defined(CONFIG_NET_IPV4) + if (family == AF_INET) { + struct sockaddr_in_ptr *addr4 = net_sin_ptr(addr); + + addr4->sin_family = AF_INET; + addr4->sin_port = NET_TCP_BUF(buf)->dst_port; + addr4->sin_addr = &NET_IPV4_BUF(buf)->dst; + } +#endif + +#if defined(CONFIG_NET_IPV6) + if (family == AF_INET6) { + struct sockaddr_in6_ptr *addr6 = net_sin6_ptr(addr); + + addr6->sin6_family = AF_INET6; + addr6->sin6_port = NET_TCP_BUF(buf)->dst_port; + addr6->sin6_addr = &NET_IPV6_BUF(buf)->dst; + } +#endif +} + /* This callback is called when we are waiting connections and we receive * a packet. We need to check if we are receiving proper msg (SYN) here. * The ACK could also be received, in which case we have an established @@ -1109,6 +1137,7 @@ static enum net_verdict tcp_syn_rcvd(struct net_conn *conn, { struct net_context *context = (struct net_context *)user_data; struct net_tcp *tcp; + struct sockaddr_ptr buf_src_addr; NET_ASSERT(context && context->tcp); @@ -1150,7 +1179,9 @@ static enum net_verdict tcp_syn_rcvd(struct net_conn *conn, sys_get_be32(NET_TCP_BUF(buf)->seq) + 1; context->tcp->recv_max_ack = context->tcp->send_seq + 1; - send_syn_ack(context, remote); + buf_get_sockaddr(net_context_get_family(context), + buf, &buf_src_addr); + send_syn_ack(context, &buf_src_addr, remote); /* We might be entering this section multiple times * if the SYN is sent more than once. So we need to cancel @@ -1546,7 +1577,7 @@ static int create_udp_packet(struct net_context *context, if (net_nbuf_family(buf) == AF_INET6) { struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)dst_addr; - buf = net_ipv6_create(context, buf, &addr6->sin6_addr); + buf = net_ipv6_create(context, buf, NULL, &addr6->sin6_addr); buf = net_udp_append(context, buf, ntohs(addr6->sin6_port)); buf = net_ipv6_finalize(context, buf); } else @@ -1556,7 +1587,7 @@ static int create_udp_packet(struct net_context *context, if (net_nbuf_family(buf) == AF_INET) { struct sockaddr_in *addr4 = (struct sockaddr_in *)dst_addr; - buf = net_ipv4_create(context, buf, &addr4->sin_addr); + buf = net_ipv4_create(context, buf, NULL, &addr4->sin_addr); buf = net_udp_append(context, buf, ntohs(addr4->sin_port)); buf = net_ipv4_finalize(context, buf); } else diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 5ee076d9a82..7f2b2bada7a 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -251,6 +251,7 @@ static struct net_buf *prepare_segment(struct net_tcp *tcp, #if defined(CONFIG_NET_IPV4) if (net_nbuf_family(buf) == AF_INET) { net_ipv4_create(context, buf, + net_sin_ptr(segment->src_addr)->sin_addr, &(net_sin(segment->dst_addr)->sin_addr)); dst_port = net_sin(segment->dst_addr)->sin_port; src_port = ((struct sockaddr_in_ptr *)&context->local)-> @@ -261,6 +262,7 @@ static struct net_buf *prepare_segment(struct net_tcp *tcp, #if defined(CONFIG_NET_IPV6) if (net_nbuf_family(buf) == AF_INET6) { net_ipv6_create(tcp->context, buf, + net_sin6_ptr(segment->src_addr)->sin6_addr, &(net_sin6(segment->dst_addr)->sin6_addr)); dst_port = net_sin6(segment->dst_addr)->sin6_port; src_port = ((struct sockaddr_in6_ptr *)&context->local)-> @@ -342,6 +344,7 @@ static inline bool seq_greater(uint32_t seq1, uint32_t seq2) int net_tcp_prepare_segment(struct net_tcp *tcp, uint8_t flags, void *options, size_t optlen, + const struct sockaddr_ptr *local, const struct sockaddr *remote, struct net_buf **send_buf) { @@ -349,6 +352,10 @@ int net_tcp_prepare_segment(struct net_tcp *tcp, uint8_t flags, uint16_t wnd; struct tcp_segment segment = { 0 }; + if (!local) { + local = &tcp->context->local; + } + seq = tcp->send_seq; if (flags & NET_TCP_ACK) { @@ -388,7 +395,7 @@ int net_tcp_prepare_segment(struct net_tcp *tcp, uint8_t flags, wnd = get_recv_wnd(tcp); - segment.src_addr = &tcp->context->local; + segment.src_addr = (struct sockaddr_ptr *)local; segment.dst_addr = remote; segment.seq = tcp->send_seq; segment.ack = tcp->send_ack; @@ -495,7 +502,7 @@ int net_tcp_prepare_ack(struct net_tcp *tcp, const struct sockaddr *remote, net_tcp_set_syn_opt(tcp, options, &optionlen); net_tcp_prepare_segment(tcp, NET_TCP_SYN | NET_TCP_ACK, - options, optionlen, remote, buf); + options, optionlen, NULL, remote, buf); break; case NET_TCP_FIN_WAIT_1: @@ -506,11 +513,12 @@ int net_tcp_prepare_ack(struct net_tcp *tcp, const struct sockaddr *remote, tcp->send_seq--; net_tcp_prepare_segment(tcp, NET_TCP_FIN | NET_TCP_ACK, - 0, 0, remote, buf); + 0, 0, NULL, remote, buf); break; default: - net_tcp_prepare_segment(tcp, NET_TCP_ACK, 0, 0, remote, buf); + net_tcp_prepare_segment(tcp, NET_TCP_ACK, 0, 0, NULL, remote, + buf); break; } @@ -596,7 +604,7 @@ int tcp_queue_data(struct net_context *context, struct net_buf *buf) * coalesce packets. */ ret = net_tcp_prepare_segment(context->tcp, NET_TCP_PSH | NET_TCP_ACK, - NULL, 0, &conn->remote_addr, &buf); + NULL, 0, NULL, &conn->remote_addr, &buf); if (ret) { return ret; } diff --git a/subsys/net/ip/tcp.h b/subsys/net/ip/tcp.h index a25de75d77c..f3c53292cc7 100644 --- a/subsys/net/ip/tcp.h +++ b/subsys/net/ip/tcp.h @@ -231,6 +231,8 @@ int net_tcp_release(struct net_tcp *tcp); * @param flags TCP flags * @param options Pointer TCP options, NULL if no options. * @param optlen Length of the options. + * @param local Source address, or NULL to use the local address of + * the TCP context * @param remote Peer address * @param send_buf Full IP + TCP header that is to be sent. * @@ -238,6 +240,7 @@ int net_tcp_release(struct net_tcp *tcp); */ int net_tcp_prepare_segment(struct net_tcp *tcp, uint8_t flags, void *options, size_t optlen, + const struct sockaddr_ptr *local, const struct sockaddr *remote, struct net_buf **send_buf); diff --git a/tests/net/tcp/src/main.c b/tests/net/tcp/src/main.c index 405f6dc073f..909901c6ade 100644 --- a/tests/net/tcp/src/main.c +++ b/tests/net/tcp/src/main.c @@ -129,9 +129,9 @@ static void v6_send_syn_ack(struct net_if *iface, struct net_buf *req) int ret; ret = net_tcp_prepare_segment(reply_v6_ctx->tcp, - NET_TCP_SYN | NET_TCP_ACK, - NULL, 0, - (struct sockaddr *)&my_v6_addr, &rsp); + NET_TCP_SYN | NET_TCP_ACK, NULL, 0, + NULL, (struct sockaddr *)&my_v6_addr, + &rsp); if (ret) { DBG("TCP packet creation failed\n"); return; @@ -768,7 +768,7 @@ static bool test_create_v6_reset_packet(void) struct net_buf *buf = NULL; int ret; - ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, + ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL, (struct sockaddr *)&peer_v6_addr, &buf); if (ret) { printk("Prepare segment failed (%d)\n", ret); @@ -799,7 +799,7 @@ static bool test_create_v4_reset_packet(void) struct net_buf *buf = NULL; int ret; - ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, + ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL, (struct sockaddr *)&peer_v4_addr, &buf); if (ret) { printk("Prepare segment failed (%d)\n", ret); @@ -830,7 +830,7 @@ static bool test_create_v6_syn_packet(void) struct net_buf *buf = NULL; int ret; - ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, + ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL, (struct sockaddr *)&peer_v6_addr, &buf); if (ret) { printk("Prepare segment failed (%d)\n", ret); @@ -861,7 +861,7 @@ static bool test_create_v4_syn_packet(void) struct net_buf *buf = NULL; int ret; - ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, + ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL, (struct sockaddr *)&peer_v4_addr, &buf); if (ret) { printk("Prepare segment failed (%d)\n", ret); @@ -892,7 +892,7 @@ static bool test_create_v6_synack_packet(void) struct net_buf *buf = NULL; int ret; - ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, + ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL, (struct sockaddr *)&peer_v6_addr, &buf); if (ret) { printk("Prepare segment failed (%d)\n", ret); @@ -924,7 +924,7 @@ static bool test_create_v4_synack_packet(void) struct net_buf *buf = NULL; int ret; - ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, + ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL, (struct sockaddr *)&peer_v4_addr, &buf); if (ret) { printk("Prepare segment failed (%d)\n", ret); @@ -956,7 +956,7 @@ static bool test_create_v6_fin_packet(void) struct net_buf *buf = NULL; int ret; - ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, + ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL, (struct sockaddr *)&peer_v6_addr, &buf); if (ret) { printk("Prepare segment failed (%d)\n", ret); @@ -987,7 +987,7 @@ static bool test_create_v4_fin_packet(void) struct net_buf *buf = NULL; int ret; - ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, + ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL, (struct sockaddr *)&peer_v4_addr, &buf); if (ret) { printk("Prepare segment failed (%d)\n", ret); @@ -1019,7 +1019,7 @@ static bool test_v6_seq_check(void) uint32_t seq; int ret; - ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, + ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL, (struct sockaddr *)&peer_v6_addr, &buf); if (ret) { printk("Prepare segment failed (%d)\n", ret); @@ -1051,7 +1051,7 @@ static bool test_v4_seq_check(void) uint32_t seq; int ret; - ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, + ret = net_tcp_prepare_segment(tcp, flags, NULL, 0, NULL, (struct sockaddr *)&peer_v4_addr, &buf); if (ret) { printk("Prepare segment failed (%d)\n", ret);