net/ipforward, tcp, and udp: Fix a chicken and egg problem by eliminating the check of the arp/neighbor tables before packet transmission

1. For buffered tcp/udp case, if CONFIG_NET_ARP_SEND/CONFIG_NET_ARP_IPIN/CONFIG_NET_ICMPv6_NEIGHBOR isn't enabled and the table doesn't contain ip<->ethaddr mapping yet, the logic will skip the realtransmission and then arp/neighbor can't steal the final buffer to generate arp/icmpv6 packet.
2.for all other case, the tcp layer or user program should already contain the retransmit logic, the check is redundancy and may generate many duplicated packets if arp/icmpv6 response is too slow because the cursor stop forward. If user still concern about the very first packet lost, he could fix the issue by enabling CONFIG_NET_ARP_SEND/CONFIG_NET_ICMPv6_NEIGHBOR at begin.
This commit is contained in:
Xiang Xiao 2019-09-18 12:33:41 -06:00 committed by Gregory Nutt
parent a0ec67bc4c
commit 5fd8f78bf9
5 changed files with 147 additions and 675 deletions

View File

@ -110,84 +110,6 @@ static inline void forward_ipselect(FAR struct forward_s *fwd)
}
#endif
/****************************************************************************
* Name: ipfwd_addrchk
*
* Description:
* Check if the destination IP address is in the IPv4 ARP or IPv6 Neighbor
* tables. If not, then the send won't actually make it out... it will be
* replaced with an ARP request (IPv4) or a Neighbor Solicitation (IPv6).
*
* NOTE 1: This could be an expensive check if there are a lot of
* entries in the ARP or Neighbor tables.
*
* NOTE 2: If we are actually harvesting IP addresses on incoming IP
* packets, then this check should not be necessary; the MAC mapping
* should already be in the ARP table in many cases (IPv4 only).
*
* NOTE 3: If CONFIG_NET_ARP_SEND then we can be assured that the IP
* address mapping is already in the ARP table.
*
* Input Parameters:
* fwd - The forwarding state structure
*
* Returned Value:
* true - The Ethernet MAC address is in the ARP or Neighbor table (OR
* the network device is not Ethernet).
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
#ifdef CONFIG_NET_ETHERNET
static inline bool ipfwd_addrchk(FAR struct forward_s *fwd)
{
DEBUGASSERT(fwd != NULL && fwd->f_iob != NULL && fwd->f_dev != NULL);
/* REVISIT: Could the MAC address not also be in a routing table? */
if (fwd->f_dev->d_lltype != NET_LL_ETHERNET)
{
return true;
}
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (fwd->f_domain == PF_INET)
#endif
{
#if !defined(CONFIG_NET_ARP_IPIN) && !defined(CONFIG_NET_ARP_SEND)
FAR struct ipv4_hdr_s *ipv4 = (FAR struct ipv4_hdr_s *)fwd->f_iob->io_data;
int ret;
ret = arp_find(*(in_addr_t *)ipv4->destipaddr, NULL);
return (ret >= 0);
#else
return true;
#endif
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
#if defined(CONFIG_NET_ICMPv6_NEIGHBOR)
FAR struct ipv6_hdr_s *ipv6 = (FAR struct ipv6_hdr_s *)fwd->f_iob->io_data;
return (neighbor_lookup(ipv6->destipaddr, NULL) >= 0);
#else
return true;
#endif
}
#endif /* CONFIG_NET_IPv6 */
}
#else /* CONFIG_NET_ETHERNET */
# define ipfwd_addrchk(r) (true)
#endif /* CONFIG_NET_ETHERNET */
/****************************************************************************
* Name: ipfwd_eventhandler
*
@ -266,16 +188,6 @@ static uint16_t ipfwd_eventhandler(FAR struct net_driver_s *dev, FAR void *conn,
devif_forward(fwd);
flags &= ~DEVPOLL_MASK;
/* Check if the destination IP address is in the ARP or Neighbor
* table. If not, then the send won't actually make it out... it
* will be replaced with an ARP request or Neighbor Solicitation.
*/
if (!ipfwd_addrchk(fwd))
{
return flags;
}
}
/* Free the allocated callback structure */

View File

@ -291,140 +291,6 @@ static inline void send_ipselect(FAR struct net_driver_s *dev,
}
#endif
/****************************************************************************
* Name: psock_send_addrchck
*
* Description:
* Check if the destination IP address is in the IPv4 ARP or IPv6 Neighbor
* tables. If not, then the send won't actually make it out... it will be
* replaced with an ARP request (IPv4) or a Neighbor Solicitation (IPv6).
*
* NOTE 1: This could be an expensive check if there are a lot of
* entries in the ARP or Neighbor tables.
*
* NOTE 2: If we are actually harvesting IP addresses on incoming IP
* packets, then this check should not be necessary; the MAC mapping
* should already be in the ARP table in many cases (IPv4 only).
*
* NOTE 3: If CONFIG_NET_ARP_SEND then we can be assured that the IP
* address mapping is already in the ARP table.
*
* Input Parameters:
* conn - The TCP connection structure
*
* Returned Value:
* true - The Ethernet MAC address is in the ARP or Neighbor table (OR
* the network device is not Ethernet).
*
* Assumptions:
* The network is locked
*
****************************************************************************/
#ifdef CONFIG_NET_ETHERNET
static inline bool psock_send_addrchck(FAR struct tcp_conn_s *conn)
{
/* Only Ethernet drivers are supported by this function.
*
* REVISIT: Could the MAC address not also be in a routing table?
*/
if (conn->dev->d_lltype != NET_LL_ETHERNET)
{
return true;
}
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET)
#endif
{
/* For historical reasons, we will return true if both the ARP and the
* routing table are disabled.
*/
bool ret = true;
#ifdef CONFIG_NET_ROUTE
in_addr_t router;
#endif
#if !defined(CONFIG_NET_ARP_IPIN) && !defined(CONFIG_NET_ARP_SEND)
if (arp_find(conn->u.ipv4.raddr, NULL) >= 0)
{
/* Return true if the address was found in the ARP table */
return true;
}
/* Otherwise, return false */
ret = false;
#endif
#ifdef CONFIG_NET_ROUTE
if (net_ipv4_router(conn->u.ipv4.raddr, &router) == OK)
{
/* Return true if the address was found in the routing table */
return true;
}
/* Otherwise, return false */
ret = false;
#endif
return ret;
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
/* For historical reasons, we will return true if both the ICMPv6
* neighbor support and the routing table are disabled.
*/
bool ret = true;
#ifdef CONFIG_NET_ROUTE
net_ipv6addr_t router;
#endif
#if !defined(CONFIG_NET_ICMPv6_NEIGHBOR)
if (neighbor_lookup(conn->u.ipv6.raddr, NULL) >= 0)
{
/* Return true if the address was found in the neighbor table */
return true;
}
/* Otherwise, return false */
ret = false;
#endif
#ifdef CONFIG_NET_ROUTE
if (net_ipv6_router(conn->u.ipv6.raddr, router) == OK)
{
/* Return true if the address was found in the routing table */
return true;
}
/* Otherwise, return false */
ret = false;
#endif
return ret;
}
#endif /* CONFIG_NET_IPv6 */
}
#else /* CONFIG_NET_ETHERNET */
# define psock_send_addrchck(r) (true)
#endif /* CONFIG_NET_ETHERNET */
/****************************************************************************
* Name: psock_send_eventhandler
*
@ -826,139 +692,131 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
!(sq_empty(&conn->write_q)) &&
conn->winsize > 0)
{
/* Check if the destination IP address is in the ARP or Neighbor
* table. If not, then the send won't actually make it out... it
* will be replaced with an ARP request or Neighbor Solicitation.
FAR struct tcp_wrbuffer_s *wrb;
uint32_t predicted_seqno;
size_t sndlen;
/* Peek at the head of the write queue (but don't remove anything
* from the write queue yet). We know from the above test that
* the write_q is not empty.
*/
if (psock_send_addrchck(conn))
wrb = (FAR struct tcp_wrbuffer_s *)sq_peek(&conn->write_q);
DEBUGASSERT(wrb);
/* Get the amount of data that we can send in the next packet.
* We will send either the remaining data in the buffer I/O
* buffer chain, or as much as will fit given the MSS and current
* window size.
*/
sndlen = TCP_WBPKTLEN(wrb) - TCP_WBSENT(wrb);
if (sndlen > conn->mss)
{
FAR struct tcp_wrbuffer_s *wrb;
uint32_t predicted_seqno;
size_t sndlen;
sndlen = conn->mss;
}
/* Peek at the head of the write queue (but don't remove anything
* from the write queue yet). We know from the above test that
* the write_q is not empty.
*/
if (sndlen > conn->winsize)
{
sndlen = conn->winsize;
}
wrb = (FAR struct tcp_wrbuffer_s *)sq_peek(&conn->write_q);
DEBUGASSERT(wrb);
ninfo("SEND: wrb=%p pktlen=%u sent=%u sndlen=%u mss=%u "
"winsize=%u\n",
wrb, TCP_WBPKTLEN(wrb), TCP_WBSENT(wrb), sndlen, conn->mss,
conn->winsize);
/* Get the amount of data that we can send in the next packet.
* We will send either the remaining data in the buffer I/O
* buffer chain, or as much as will fit given the MSS and current
* window size.
*/
/* Set the sequence number for this segment. If we are
* retransmitting, then the sequence number will already
* be set for this write buffer.
*/
sndlen = TCP_WBPKTLEN(wrb) - TCP_WBSENT(wrb);
if (sndlen > conn->mss)
{
sndlen = conn->mss;
}
if (TCP_WBSEQNO(wrb) == (unsigned)-1)
{
TCP_WBSEQNO(wrb) = conn->isn + conn->sent;
}
if (sndlen > conn->winsize)
{
sndlen = conn->winsize;
}
/* The TCP stack updates sndseq on receipt of ACK *before*
* this function is called. In that case sndseq will point
* to the next unacknowledged byte (which might have already
* been sent). We will overwrite the value of sndseq here
* before the packet is sent.
*/
ninfo("SEND: wrb=%p pktlen=%u sent=%u sndlen=%u mss=%u "
"winsize=%u\n",
wrb, TCP_WBPKTLEN(wrb), TCP_WBSENT(wrb), sndlen, conn->mss,
conn->winsize);
/* Set the sequence number for this segment. If we are
* retransmitting, then the sequence number will already
* be set for this write buffer.
*/
if (TCP_WBSEQNO(wrb) == (unsigned)-1)
{
TCP_WBSEQNO(wrb) = conn->isn + conn->sent;
}
/* The TCP stack updates sndseq on receipt of ACK *before*
* this function is called. In that case sndseq will point
* to the next unacknowledged byte (which might have already
* been sent). We will overwrite the value of sndseq here
* before the packet is sent.
*/
tcp_setsequence(conn->sndseq, TCP_WBSEQNO(wrb) + TCP_WBSENT(wrb));
tcp_setsequence(conn->sndseq, TCP_WBSEQNO(wrb) + TCP_WBSENT(wrb));
#ifdef NEED_IPDOMAIN_SUPPORT
/* If both IPv4 and IPv6 support are enabled, then we will need to
* select which one to use when generating the outgoing packet.
* If only one domain is selected, then the setup is already in
* place and we need do nothing.
*/
/* If both IPv4 and IPv6 support are enabled, then we will need to
* select which one to use when generating the outgoing packet.
* If only one domain is selected, then the setup is already in
* place and we need do nothing.
*/
send_ipselect(dev, conn);
send_ipselect(dev, conn);
#endif
/* Then set-up to send that amount of data with the offset
* corresponding to the amount of data already sent. (this
* won't actually happen until the polling cycle completes).
*/
/* Then set-up to send that amount of data with the offset
* corresponding to the amount of data already sent. (this
* won't actually happen until the polling cycle completes).
*/
devif_iob_send(dev, TCP_WBIOB(wrb), sndlen, TCP_WBSENT(wrb));
devif_iob_send(dev, TCP_WBIOB(wrb), sndlen, TCP_WBSENT(wrb));
/* Remember how much data we send out now so that we know
* when everything has been acknowledged. Just increment
* the amount of data sent. This will be needed in sequence
* number calculations.
*/
/* Remember how much data we send out now so that we know
* when everything has been acknowledged. Just increment
* the amount of data sent. This will be needed in sequence
* number calculations.
*/
conn->unacked += sndlen;
conn->sent += sndlen;
conn->unacked += sndlen;
conn->sent += sndlen;
/* Below prediction will become true, unless retransmission occurrence */
/* Below prediction will become true, unless retransmission occurrence */
predicted_seqno = tcp_getsequence(conn->sndseq) + sndlen;
predicted_seqno = tcp_getsequence(conn->sndseq) + sndlen;
if ((predicted_seqno > conn->sndseq_max) ||
(tcp_getsequence(conn->sndseq) > predicted_seqno)) /* overflow */
{
conn->sndseq_max = predicted_seqno;
}
ninfo("SEND: wrb=%p nrtx=%u unacked=%u sent=%u\n",
wrb, TCP_WBNRTX(wrb), conn->unacked, conn->sent);
/* Increment the count of bytes sent from this write buffer */
TCP_WBSENT(wrb) += sndlen;
ninfo("SEND: wrb=%p sent=%u pktlen=%u\n",
wrb, TCP_WBSENT(wrb), TCP_WBPKTLEN(wrb));
/* Remove the write buffer from the write queue if the
* last of the data has been sent from the buffer.
*/
DEBUGASSERT(TCP_WBSENT(wrb) <= TCP_WBPKTLEN(wrb));
if (TCP_WBSENT(wrb) >= TCP_WBPKTLEN(wrb))
{
FAR struct tcp_wrbuffer_s *tmp;
ninfo("SEND: wrb=%p Move to unacked_q\n", wrb);
tmp = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q);
DEBUGASSERT(tmp == wrb);
UNUSED(tmp);
/* Put the I/O buffer chain in the un-acked queue; the
* segment is waiting for ACK again
*/
psock_insert_segment(wrb, &conn->unacked_q);
}
/* Only one data can be sent by low level driver at once,
* tell the caller stop polling the other connection.
*/
flags &= ~TCP_POLL;
if ((predicted_seqno > conn->sndseq_max) ||
(tcp_getsequence(conn->sndseq) > predicted_seqno)) /* overflow */
{
conn->sndseq_max = predicted_seqno;
}
ninfo("SEND: wrb=%p nrtx=%u unacked=%u sent=%u\n",
wrb, TCP_WBNRTX(wrb), conn->unacked, conn->sent);
/* Increment the count of bytes sent from this write buffer */
TCP_WBSENT(wrb) += sndlen;
ninfo("SEND: wrb=%p sent=%u pktlen=%u\n",
wrb, TCP_WBSENT(wrb), TCP_WBPKTLEN(wrb));
/* Remove the write buffer from the write queue if the
* last of the data has been sent from the buffer.
*/
DEBUGASSERT(TCP_WBSENT(wrb) <= TCP_WBPKTLEN(wrb));
if (TCP_WBSENT(wrb) >= TCP_WBPKTLEN(wrb))
{
FAR struct tcp_wrbuffer_s *tmp;
ninfo("SEND: wrb=%p Move to unacked_q\n", wrb);
tmp = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q);
DEBUGASSERT(tmp == wrb);
UNUSED(tmp);
/* Put the I/O buffer chain in the un-acked queue; the
* segment is waiting for ACK again
*/
psock_insert_segment(wrb, &conn->unacked_q);
}
/* Only one data can be sent by low level driver at once,
* tell the caller stop polling the other connection.
*/
flags &= ~TCP_POLL;
}
/* Continue waiting */

View File

@ -201,141 +201,6 @@ static inline void tcpsend_ipselect(FAR struct net_driver_s *dev,
}
#endif
/****************************************************************************
* Name: psock_send_addrchck
*
* Description:
* Check if the destination IP address is in the IPv4 ARP or IPv6 Neighbor
* tables. If not, then the send won't actually make it out... it will be
* replaced with an ARP request (IPv4) or a Neighbor Solicitation (IPv6).
*
* NOTE 1: This could be an expensive check if there are a lot of
* entries in the ARP or Neighbor tables.
*
* NOTE 2: If we are actually harvesting IP addresses on incoming IP
* packets, then this check should not be necessary; the MAC mapping
* should already be in the ARP table in many cases (IPv4 only).
*
* NOTE 3: If CONFIG_NET_ARP_SEND then we can be assured that the IP
* address mapping is already in the ARP table.
*
* Input Parameters:
* conn - The TCP connection structure
*
* Returned Value:
* true - The Ethernet MAC address is in the ARP or Neighbor table (OR
* the network device is not Ethernet).
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
#ifdef CONFIG_NET_ETHERNET
static inline bool psock_send_addrchck(FAR struct tcp_conn_s *conn)
{
/* Only Ethernet drivers are supported by this function.
*
* REVISIT: Could the MAC address not also be in a routing table?
*/
if (conn->dev->d_lltype != NET_LL_ETHERNET)
{
/* Return true for non-Ethernet devices. */
return true;
}
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET)
#endif
{
/* For historical reasons, we will return true if both the ARP and the
* routing table are disabled.
*/
bool ret = true;
#ifdef CONFIG_NET_ROUTE
in_addr_t router;
#endif
#if !defined(CONFIG_NET_ARP_IPIN) && !defined(CONFIG_NET_ARP_SEND)
if (arp_find(conn->u.ipv4.raddr, NULL) >= 0)
{
/* Return true if the address was found in the ARP table */
return true;
}
/* Otherwise, return false */
ret = false;
#endif
#ifdef CONFIG_NET_ROUTE
if (net_ipv4_router(conn->u.ipv4.raddr, &router) == OK)
{
/* Return true if the address was found in the routing table */
return true;
}
/* Otherwise, return false */
ret = false;
#endif
return ret;
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
/* For historical reasons, we will return true if both the ICMPv6
* neighbor support and the routing table are disabled.
*/
bool ret = true;
#ifdef CONFIG_NET_ROUTE
net_ipv6addr_t router;
#endif
#if !defined(CONFIG_NET_ICMPv6_NEIGHBOR)
if (neighbor_lookup(conn->u.ipv6.raddr, NULL) >= 0)
{
/* Return true if the address was found in the neighbor table */
return true;
}
/* Otherwise, return false */
ret = false;
#endif
#ifdef CONFIG_NET_ROUTE
if (net_ipv6_router(conn->u.ipv6.raddr, router) == OK)
{
/* Return true if the address was found in the routing table */
return true;
}
/* Otherwise, return false */
ret = false;
#endif
return ret;
}
#endif /* CONFIG_NET_IPv6 */
}
#else /* CONFIG_NET_ETHERNET */
# define psock_send_addrchck(r) (true)
#endif /* CONFIG_NET_ETHERNET */
/****************************************************************************
* Name: tcpsend_eventhandler
*
@ -628,19 +493,11 @@ static uint16_t tcpsend_eventhandler(FAR struct net_driver_s *dev,
devif_send(dev, &pstate->snd_buffer[pstate->snd_sent], sndlen);
/* Check if the destination IP address is in the ARP or Neighbor
* table. If not, then the send won't actually make it out... it
* will be replaced with an ARP request or Neighbor Solicitation.
*/
/* Update the amount of data sent (but not necessarily ACKed) */
if (pstate->snd_sent != 0 || psock_send_addrchck(conn))
{
/* Update the amount of data sent (but not necessarily ACKed) */
pstate->snd_sent += sndlen;
ninfo("SEND: acked=%d sent=%d buflen=%d\n",
pstate->snd_acked, pstate->snd_sent, pstate->snd_buflen);
}
pstate->snd_sent += sndlen;
ninfo("SEND: acked=%d sent=%d buflen=%d\n",
pstate->snd_acked, pstate->snd_sent, pstate->snd_buflen);
}
}
@ -664,6 +521,7 @@ static uint16_t tcpsend_eventhandler(FAR struct net_driver_s *dev,
return flags;
end_wait:
/* Do not allow any further callbacks */
pstate->snd_cb->flags = 0;

View File

@ -258,69 +258,6 @@ static uint16_t ack_eventhandler(FAR struct net_driver_s *dev,
return flags;
}
/****************************************************************************
* Name: sendfile_addrcheck
*
* Description:
* Check if the destination IP address is in the IPv4 ARP or IPv6 Neighbor
* tables. If not, then the send won't actually make it out... it will be
* replaced with an ARP request (IPv4) or a Neighbor Solicitation (IPv6).
*
* NOTE 1: This could be an expensive check if there are a lot of
* entries in the ARP or Neighbor tables.
*
* NOTE 2: If we are actually harvesting IP addresses on incoming IP
* packets, then this check should not be necessary; the MAC mapping
* should already be in the ARP table in many cases (IPv4 only).
*
* NOTE 3: If CONFIG_NET_ARP_SEND then we can be assured that the IP
* address mapping is already in the ARP table.
*
* Input Parameters:
* conn - The TCP connection structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked
*
****************************************************************************/
#ifdef CONFIG_NET_ETHERNET
static inline bool sendfile_addrcheck(FAR struct tcp_conn_s *conn)
{
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET)
#endif
{
#if !defined(CONFIG_NET_ARP_IPIN) && !defined(CONFIG_NET_ARP_SEND)
return (arp_find(conn->u.ipv4.raddr, NULL) >= 0);
#else
return true;
#endif
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
#if !defined(CONFIG_NET_ICMPv6_NEIGHBOR)
return (neighbor_lookup(conn->u.ipv6.raddr, NULL) >= 0);
#else
return true;
#endif
}
#endif /* CONFIG_NET_IPv6 */
}
#else /* CONFIG_NET_ETHERNET */
# sendfile_addrcheck(r) (true)
#endif /* CONFIG_NET_ETHERNET */
/****************************************************************************
* Name: sendfile_eventhandler
*
@ -451,19 +388,11 @@ static uint16_t sendfile_eventhandler(FAR struct net_driver_s *dev,
tcp_setsequence(conn->sndseq, seqno);
/* Check if the destination IP address is in the ARP or Neighbor
* table. If not, then the send won't actually make it out... it
* will be replaced with an ARP request or Neighbor Solicitation.
*/
/* Update the amount of data sent (but not necessarily ACKed) */
if (pstate->snd_sent != 0 || sendfile_addrcheck(conn))
{
/* Update the amount of data sent (but not necessarily ACKed) */
pstate->snd_sent += sndlen;
ninfo("pid: %d SEND: acked=%d sent=%d flen=%d\n", getpid(),
pstate->snd_acked, pstate->snd_sent, pstate->snd_flen);
}
pstate->snd_sent += sndlen;
ninfo("pid: %d SEND: acked=%d sent=%d flen=%d\n", getpid(),
pstate->snd_acked, pstate->snd_sent, pstate->snd_flen);
}
else
{

View File

@ -109,12 +109,6 @@
static inline void sendto_ipselect(FAR struct net_driver_s *dev,
FAR struct udp_conn_s *conn);
#endif
#ifdef CONFIG_NET_ETHERNET
static inline bool sendto_addrcheck(FAR struct udp_conn_s *conn,
FAR struct net_driver_s *dev);
#else
# define sendto_addrcheck(c,d) (true)
#endif
#ifdef CONFIG_NET_SOCKOPTS
static inline int sendto_timeout(FAR struct socket *psock,
FAR struct udp_conn_s *conn);
@ -239,76 +233,6 @@ static inline void sendto_ipselect(FAR struct net_driver_s *dev,
}
#endif
/****************************************************************************
* Name: sendto_addrcheck
*
* Description:
* Check if the destination IP address is in the IPv4 ARP or IPv6 Neighbor
* tables. If not, then the send won't actually make it out... it will be
* replaced with an ARP request (IPv4) or a Neighbor Solicitation (IPv6).
*
* NOTE 1: This could be an expensive check if there are a lot of
* entries in the ARP or Neighbor tables.
*
* NOTE 2: If we are actually harvesting IP addresses on incoming IP
* packets, then this check should not be necessary; the MAC mapping
* should already be in the ARP table in many cases (IPv4 only).
*
* NOTE 3: If CONFIG_NET_ARP_SEND then we can be assured that the IP
* address mapping is already in the ARP table.
*
* Input Parameters:
* conn - The UDP connection structure
* dev - Polling network device
*
* Returned Value:
* true - The Ethernet MAC address is in the ARP or Neighbor table (OR
* the network device is not Ethernet).
*
* Assumptions:
* The network is locked
*
****************************************************************************/
#ifdef CONFIG_NET_ETHERNET
static inline bool sendto_addrcheck(FAR struct udp_conn_s *conn,
FAR struct net_driver_s *dev)
{
/* REVISIT: Could the MAC address not also be in a routing table? */
if (dev->d_lltype != NET_LL_ETHERNET)
{
return true;
}
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET)
#endif
{
#if !defined(CONFIG_NET_ARP_IPIN) && !defined(CONFIG_NET_ARP_SEND)
return (arp_find(conn->u.ipv4.raddr, NULL) >= 0);
#else
return true;
#endif
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
#if !defined(CONFIG_NET_ICMPv6_NEIGHBOR)
return (neighbor_lookup(conn->u.ipv6.raddr, NULL) >= 0);
#else
return true;
#endif
}
#endif /* CONFIG_NET_IPv6 */
}
#endif /* CONFIG_NET_ETHERNET */
/****************************************************************************
* Name: sendto_timeout
*
@ -514,61 +438,52 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev,
if (dev->d_sndlen <= 0 && (flags & UDP_NEWDATA) == 0 &&
(flags & UDP_POLL) != 0 && !sq_empty(&conn->write_q))
{
/* Check if the destination IP address is in the ARP, Neighbor
* table, or routing table. If not, then the send won't actually
* make it out... it will be replaced with an ARP request or
* Neighbor Solicitation.
FAR struct udp_wrbuffer_s *wrb;
size_t sndlen;
/* Peek at the head of the write queue (but don't remove anything
* from the write queue yet). We know from the above test that
* the write_q is not empty.
*/
if (sendto_addrcheck(conn, dev))
{
FAR struct udp_wrbuffer_s *wrb;
size_t sndlen;
wrb = (FAR struct udp_wrbuffer_s *)sq_peek(&conn->write_q);
DEBUGASSERT(wrb != NULL);
/* Peek at the head of the write queue (but don't remove anything
* from the write queue yet). We know from the above test that
* the write_q is not empty.
*/
/* Get the amount of data that we can send in the next packet.
* We will send either the remaining data in the buffer I/O
* buffer chain, or as much as will fit given the MSS and current
* window size.
*/
wrb = (FAR struct udp_wrbuffer_s *)sq_peek(&conn->write_q);
DEBUGASSERT(wrb != NULL);
/* Get the amount of data that we can send in the next packet.
* We will send either the remaining data in the buffer I/O
* buffer chain, or as much as will fit given the MSS and current
* window size.
*/
sndlen = wrb->wb_iob->io_pktlen;
ninfo("wrb=%p sndlen=%u\n", wrb, sndlen);
sndlen = wrb->wb_iob->io_pktlen;
ninfo("wrb=%p sndlen=%u\n", wrb, sndlen);
#ifdef NEED_IPDOMAIN_SUPPORT
/* If both IPv4 and IPv6 support are enabled, then we will need to
* select which one to use when generating the outgoing packet.
* If only one domain is selected, then the setup is already in
* place and we need do nothing.
*/
/* If both IPv4 and IPv6 support are enabled, then we will need to
* select which one to use when generating the outgoing packet.
* If only one domain is selected, then the setup is already in
* place and we need do nothing.
*/
sendto_ipselect(dev, conn);
sendto_ipselect(dev, conn);
#endif
/* Then set-up to send that amount of data with the offset
* corresponding to the size of the IP-dependent address structure.
*/
/* Then set-up to send that amount of data with the offset
* corresponding to the size of the IP-dependent address structure.
*/
devif_iob_send(dev, wrb->wb_iob, sndlen, 0);
devif_iob_send(dev, wrb->wb_iob, sndlen, 0);
/* Free the write buffer at the head of the queue and attempt to
* setup the next transfer.
*/
/* Free the write buffer at the head of the queue and attempt to
* setup the next transfer.
*/
sendto_writebuffer_release(psock, conn);
sendto_writebuffer_release(psock, conn);
/* Only one data can be sent by low level driver at once,
* tell the caller stop polling the other connections.
*/
/* Only one data can be sent by low level driver at once,
* tell the caller stop polling the other connections.
*/
flags &= ~UDP_POLL;
}
flags &= ~UDP_POLL;
}
#ifdef CONFIG_NET_SOCKOPTS