net: tcp: Select correct source address for SYNACK packets

The source address for a TCP SYNACK must (obviously) be the same as
the destination address of the SYN that produced it.  But the existing
IP packet creation routines would simply fill in a default address
from the net_context struct, which is correct for *established*
connections, but for the listening socket is generally INADDR_ANY
(i.e. all zeroes) and will result in an arbitrary choice for source
address (e.g. a link-local address on the same interface) which can
easily be wrong.

So we need to pass the correct address all the way down from the SYN
packet handler code through the net_ipv*_create() packet creation
functions.  This requires lots of API plumbing, but relatively little
logic change.

Change-Id: Ic368f8cef6689f8a27cbafd5933a4964d5cc457e
Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
Andy Ross 2016-12-08 12:16:49 -06:00 committed by Anas Nashif
commit a5b694fbbc
8 changed files with 91 additions and 41 deletions

View file

@ -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));
}

View file

@ -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

View file

@ -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));
}

View file

@ -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);
/**

View file

@ -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

View file

@ -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;
}

View file

@ -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);

View file

@ -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);