sock_var.h 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
#ifndef SOCK_VAR_H
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define SOCK_VAR_H
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include "XNutOS.h"
#include "netbuf.h"
#ifdef __cplusplus
extern "C" { /* Assume C declarations for C++ */
#endif /* __cplusplus */
/******************************************************************************
* *
* G L O B A L D E F I N E S *
* *
******************************************************************************/
/*
* TCP send flags.
*/
#define SO_FIN 0x01 /*!< \brief Socket transmit flag. Send FIN after all data has been transmitted. */
#define SO_SYN 0x02 /*!< \brief Socket transmit flag. Send SYN first. */
#define SO_FORCE 0x08 /*!< \brief Socket transmit flag. Force sending ACK. */
#define SO_ACK 0x10 /*!< \brief Socket transmit flag. Send ACK. */
/******************************************************************************
* *
* S T R U C T U R E D E F I N I T I O N S *
* *
******************************************************************************/
/*!
* \brief UDP socket type.
*/
typedef struct udp_socket UDPSOCKET;
/*!
* \brief UDP socket information structure.
*
* Applications should not rely on the content of this structure.
* It may change without notice.
*/
struct udp_socket {
UDPSOCKET *so_next; /*!< \brief Link to next tcp socket structure. */
u_short so_local_port; /*!< \brief Local port number in net byte order. */
NETBUF *so_rx_nb; /*!< \brief Received, but not read by application. */
NUTHANDLE so_rx_rdy; /*!< \brief Receiver event queue. */
int so_rx_cnt; /*!< \brief Number of data bytes in the receive buffer. */
int so_rx_bsz; /*!< \brief Receive buffer size. */
u_short so_last_error; /*!< \brief Last error of socket */
u_long so_remote_addr; /*!< \brief Remote IP address in net byte order. Important just in case of an error */
u_short so_remote_port; /*!< \brief Remote port number in net byte order. Important just in case of an error */
};
/*!
* \brief TCP socket type.
*/
typedef struct tcp_socket TCPSOCKET;
/*!
* \brief TCP socket information structure.
*
* Applications should not rely on the content of this structure.
* It may change without notice.
*/
struct tcp_socket {
TCPSOCKET *so_next; /*!< \brief Link to next tcp socket structure. */
void *so_device; /*!< \brief Always zero. */
u_char so_devtype; /*!< \brief Device type, always IFTYP_TCPSOCK. */
int (*so_devread) (TCPSOCKET *, void *, int); /*!< \brief Read from device. */
int (*so_devwrite) (TCPSOCKET *, CONST void *, int); /*!< \brief Write to device. */
int (*so_devioctl) (TCPSOCKET *, int, void *); /*!< \brief Driver control function. */
u_short so_devocnt; /*!< \brief Number of data bytes in output buffer. */
u_char *so_devobuf; /*!< \brief Pointer to output buffer. */
u_short so_devobsz; /*!< \brief Output buffer size. */
volatile u_char so_state; /*!< \brief Connection state, see tcp_fsm.h */
u_long so_local_addr; /*!< \brief Local IP address in net byte order. */
u_short so_local_port; /*!< \brief Local port number in net byte order. */
u_long so_remote_addr; /*!< \brief Remote IP address in net byte order. */
u_short so_remote_port; /*!< \brief Remote port number in net byte order. */
u_char so_tx_flags; /*!< \brief Flags used during transmissions - see below */
u_long so_tx_isn; /*!< \brief Initial sequence number. */
u_long so_tx_una; /*!< \brief Unacknowledged sequence number. */
u_long so_tx_nxt; /*!< \brief Next sequence number to send. */
u_long so_tx_wl1; /*!< \brief Sequence number of last window update. */
u_long so_tx_wl2; /*!< \brief Acknowledged sequence of last window update. */
u_short so_tx_win; /*!< \brief Peer's receive window. */
u_char so_tx_dup; /*!< \brief Duplicate ACK counter. */
NETBUF *so_tx_nbq; /*!< \brief Network buffers waiting to be acknowledged. */
NUTHANDLE so_tx_tq; /*!< \brief Threads waiting for transmit buffer space. */
u_long so_rx_isn; /*!< \brief Initial sequence number of remote. */
u_long so_rx_nxt; /*!< \brief Next sequence number to receive. */
u_short so_rx_win; /*!< \brief Local receive window. */
int so_rx_cnt; /*!< \brief Number of data bytes in the receive buffer. */
int so_rx_bsz; /*!< \brief Receive buffer size. */
int so_rd_cnt; /*!< \brief Number of bytes read from buffer top. */
char so_rx_apc; /*!< \brief Number of packets received in advance. */
NETBUF *so_rx_buf; /*!< \brief Data waiting to be read by application. */
NUTHANDLE so_rx_tq; /*!< \brief Threads waiting for received data. */
NETBUF *so_rx_nbq; /*!< \brief Network buffers received in advance. */
u_short so_mss; /*!< \brief MSS, limited by remote option or MTU. */
u_long so_rtt_seq; /*!< \brief Sequence number for RTT calculation. */
u_short so_rtto; /*!< \brief Current retransmission timeout. */
u_short so_retransmits; /*!< \brief Number of retransmits. */
u_short so_time_wait; /*!< \brief Time wait counter. */
u_short so_retran_time; /*!< \brief Retransmit time counter. */
u_short so_last_error; /*!< \brief Last error code. */
NUTHANDLE so_pc_tq; /*!< \brief Listening thread. */
NUTHANDLE so_ac_tq; /*!< \brief Connecting thread. */
u_long so_read_to; /*!< \brief Read timeout. */
u_long so_write_to; /*!< \brief Write timeout. */
u_long so_oos_drop; /*!< \brief Out of sequence dropped. */
};
/******************************************************************************
* *
* G L O B A L V A R I A B L E S - N O I N I T I A L I Z E R S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* G L O B A L V A R I A B L E S - I N I T I A L I Z E R S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
int NutUdpInput(NUTDEVICE * dev, NETBUF *nb);
int NutUdpOutput(UDPSOCKET *sock, u_long dest, u_short port, NETBUF *nb);
int NutTcpOutput(TCPSOCKET *sock, CONST u_char *data, u_short size);
int NutTcpReject(NETBUF *nb);
#ifdef __cplusplus
} /* End of extern "C" { */
#endif /* __cplusplus */
#endif
socket.h 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
#ifndef SOCKET_H
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define SOCKET_H
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include "sock_var.h"
#include "tcp_fsm.h"
#ifdef __cplusplus
extern "C" { /* Assume C declarations for C++ */
#endif /* __cplusplus */
/******************************************************************************
* *
* G L O B A L D E F I N E S *
* *
******************************************************************************/
/*
* Types
*/
#define SOCK_STREAM 1 /*!< \brief Stream socket */
#define SOCK_DGRAM 2 /*!< \brief Datagram socket */
#define SOCK_RAW 3 /*!< \brief Raw-protocol interface */
/*
* Option flags per-socket.
*/
#define SO_DEBUG 0x0001 /*!< \brief Turn on debugging info recording */
#define SO_ACCEPTCONN 0x0002 /*!< \brief Socket has had listen() */
#define SO_REUSEADDR 0x0004 /*!< \brief Allow local address reuse */
#define SO_KEEPALIVE 0x0008 /*!< \brief Keep connections alive */
#define SO_DONTROUTE 0x0010 /*!< \brief Just use interface addresses */
#define SO_BROADCAST 0x0020 /*!< \brief Permit sending of broadcast msgs */
#define SO_USELOOPBACK 0x0040 /*!< \brief Bypass hardware when possible */
#define SO_LINGER 0x0080 /*!< \brief Linger on close if data present */
#define SO_OOBINLINE 0x0100 /*!< \brief Leave received OOB data in line */
#define SO_REUSEPORT 0x0200 /*!< \brief Allow local address & port reuse */
/*
* Additional options, not kept in so_options.
*/
#define SO_SNDBUF 0x1001 /*!< \brief Send buffer size */
#define SO_RCVBUF 0x1002 /*!< \brief Receive buffer size */
#define SO_SNDLOWAT 0x1003 /*!< \brief Send low-water mark */
#define SO_RCVLOWAT 0x1004 /*!< \brief Receive low-water mark */
#define SO_SNDTIMEO 0x1005 /*!< \brief Send timeout */
#define SO_RCVTIMEO 0x1006 /*!< \brief Receive timeout */
#define SO_ERROR 0x1007 /*!< \brief Get error status and clear */
#define SO_TYPE 0x1008 /*!< \brief Get socket type */
/*
* Address families.
*/
#define AF_INET 2 /*!< \brief internetwork: UDP, TCP, etc. */
/******************************************************************************
* *
* S T R U C T U R E D E F I N I T I O N S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* G L O B A L V A R I A B L E S - N O I N I T I A L I Z E R S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* G L O B A L V A R I A B L E S - I N I T I A L I Z E R S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
TCPSOCKET *NutTcpCreateSocket(void);
int NutTcpSetSockOpt(TCPSOCKET *sock, int optname, CONST void *optval, int optlen);
int NutTcpGetSockOpt(TCPSOCKET *sock, int optname, void *optval, int optlen);
int NutTcpConnect(TCPSOCKET *sock, u_long addr, u_short port);
int NutTcpAccept(TCPSOCKET *sock, u_short port);
int NutTcpInput(NUTDEVICE * dev, NETBUF *nb);
int NutTcpSend(TCPSOCKET *sock, CONST void *data, int len);
int NutTcpCloseSocket(TCPSOCKET *sock);
void NutTcpDestroySocket(TCPSOCKET *sock);
int NutTcpReceive(TCPSOCKET *sock, void *data, int size);
TCPSOCKET *NutTcpFindSocket(u_short lport, u_short rport, u_long raddr);
int NutTcpError(TCPSOCKET *sock);
int NutTcpAbortSocket(TCPSOCKET *sock, u_short last_error);
void NutTcpDiscardBuffers(TCPSOCKET * sock);
int NutTcpDeviceRead(TCPSOCKET *sock, void *buffer, int size);
int NutTcpDeviceWrite(TCPSOCKET *sock, CONST void *buffer, int size);
int NutTcpDeviceIOCtl(TCPSOCKET *sock, int cmd, void *param);
int NutTcpDeviceClose(TCPSOCKET *sock);
UDPSOCKET *NutUdpCreateSocket(u_short port);
int NutUdpSendTo(UDPSOCKET *sock, u_long addr, u_short port, void *data, int len);
int NutUdpReceiveFrom(UDPSOCKET *sock, u_long *addr, u_short *port, void *data, int size, u_long timeout);
int NutUdpDestroySocket(UDPSOCKET *sock);
UDPSOCKET *NutUdpFindSocket(u_short port);
int NutUdpSetSockOpt(UDPSOCKET *sock, int optname, CONST void *optval, int optlen);
int NutUdpGetSockOpt(UDPSOCKET *sock, int optname, void *optval, int optlen);
#ifdef NUT_UDP_ICMP_SUPPORT
extern int NutUdpSetSocketError(UDPSOCKET * sock, u_long remote_addr, u_short remote_port, u_short error);
extern int NutUdpError(UDPSOCKET * sock, u_long * addr, u_short * port);
#endif
#ifdef __cplusplus
} /* End of extern "C" { */
#endif /* __cplusplus */
#endif
tcp.h 、、、。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
#ifndef TCP_H
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define TCP_H
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#ifdef __cplusplus
extern "C" { /* Assume C declarations for C++ */
#endif /* __cplusplus */
/******************************************************************************
* *
* G L O B A L D E F I N E S *
* *
******************************************************************************/
#define TH_FIN 0x01 /*!< \brief Finishing transmission. */
#define TH_SYN 0x02 /*!< \brief Synchronizing sequence numbers. */
#define TH_RST 0x04 /*!< \brief Reset connection. */
#define TH_PUSH 0x08 /*!< \brief Push data to application level. */
#define TH_ACK 0x10 /*!< \brief Acknowledge field is valid. */
#define TH_URG 0x20 /*!< \brief Urgent data present. */
/*! \brief TCP flag mask. */
#define TH_FLAGS (TH_FIN | TH_SYN | TH_RST | TH_ACK | TH_URG)
#define TCPOPT_EOL 0 /*!< \brief End of options. */
#define TCPOPT_NOP 1 /*!< \brief Nothing. */
#define TCPOPT_MAXSEG 2 /*!< \brief Maximum segment size. */
#define TCPOLEN_MAXSEG 4 /*!< \brief Maximum segment size length. */
#define TCPOPT_WINDOW 3 /*!< \brief Receive window. */
#define TCPOLEN_WINDOW 3 /*!< \brief Receive window length. */
#define TCP_MSS 536 /*!< \brief Default maximum segment size.
The maximum size of an IP datagram, that will
not become fragmented, is 576. The maximum
IP datagram for Ethernet is 1500. Reduce this
number by 40, 20 bytes TCP header and 20 bytes
IP header.
\showinitializer
*/
#define TCP_WINSIZE 3216 /*!< \brief Default window size.
It's recommended to set this 6 times the maximum
segment size.
\showinitializer
*/
#define TCP_MAXWIN 65535 /*!< \brief Largest value for (unscaled) window. \showinitializer */
#define TTCP_CLIENT_SND_WND 4096 /*!< \brief Default send window for T/TCP client. \showinitializer */
#define TCP_MAX_WINSHIFT 14 /*!< \brief Maximum window shift. \showinitializer */
#define TCP_MAXHLEN (0xf<<2) /*!< \brief Maximum length of header in bytes. */
#define TCP_MAXOLEN (TCP_MAXHLEN - sizeof(struct tcphdr)) /*!< \brief Maximum space left for options. */
/*
* User-settable options.
*/
#define TCP_NODELAY 0x01 /*!< \brief Don't delay send to coalesce segments. */
#define TCP_MAXSEG 0x02 /*!< \brief Set maximum segment size. */
#define TCP_NOPUSH 0x04 /*!< \brief Don't push last block of write. */
#define TCP_NOOPT 0x08 /*!< \brief Don't use TCP options. */
/******************************************************************************
* *
* S T R U C T U R E D E F I N I T I O N S *
* *
******************************************************************************/
/*!
* \typedef TCPHDR
* \brief TCP protocol header type.
*/
#if defined(__ICCARM__)
#pragma diag_suppress = Pa039 // Use of address of unaligned structure member
#pragma pack ( 1 )
#endif
typedef struct tcphdr {
u_short th_sport; /*!< \brief Source port. */
u_short th_dport; /*!< \brief Destination port. */
u_long th_seq; /*!< \brief Sequence number of first octet in this segment. */
u_long th_ack; /*!< \brief Expected sequence number of next octet. */
#ifndef BIG_ENDIAN
u_char th_x2:4, /*!< \brief Unused. */
th_off:4; /*!< \brief Data offset. */
#else /* #ifndef BIG_ENDIAN */
u_char th_off:4, /*!< \brief Data offset. */
th_x2:4; /*!< \brief Unused. */
#endif
u_char th_flags; /*!< \brief Control flags. */
u_short th_win; /*!< \brief Number of acceptable octects. */
u_short th_sum; /*!< \brief 96 byte pseudo header checksum. */
u_short th_urp; /*!< \brief Urgent data pointer. */
} TCPHDR;
#if defined(__ICCARM__)
#pragma pack ( )
#endif
/*!
* \typedef TCPPSEUDOHDR
* \brief TCP pseudo header type.
*/
typedef struct _TCPPSEUDOHDR {
u_long tph_src; /*!< \brief IP address of sender. */
u_long tph_dst; /*!< \brief IP address of target. */
u_char tph_mbz; /*!< \brief mbz */
u_char tph_p; /*!< \brief p */
u_short tph_len; /*!< \brief len */
u_short tph_sum; /*!< \brief Checksum */
} TCPPSEUDOHDR;
/******************************************************************************
* *
* G L O B A L V A R I A B L E S - N O I N I T I A L I Z E R S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* G L O B A L V A R I A B L E S - I N I T I A L I Z E R S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
#ifdef __cplusplus
} /* End of extern "C" { */
#endif /* __cplusplus */
#endif
tcp_fsm.h 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
#ifndef TCP_FSM_H
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define TCP_FSM_H
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#ifdef __cplusplus
extern "C" { /* Assume C declarations for C++ */
#endif /* __cplusplus */
/******************************************************************************
* *
* G L O B A L D E F I N E S *
* *
******************************************************************************/
#define TCP_NSTATES 11 /*!< \brief Total number of possible states. */
/*
* Warning: Do NOT change the values or the order of the TCP states.
* Some functions in tcpsm.c rely on the the order below.
*/
#define TCPS_CLOSED 0 /*!< \brief closed */
#define TCPS_LISTEN 1 /*!< \brief listening for connection */
#define TCPS_SYN_SENT 2 /*!< \brief active, have sent syn */
#define TCPS_SYN_RECEIVED 3 /*!< \brief have sent and received syn */
#define TCPS_ESTABLISHED 4 /*!< \brief established */
#define TCPS_CLOSE_WAIT 5 /*!< \brief rcvd fin, waiting for close */
#define TCPS_FIN_WAIT_1 6 /*!< \brief have closed, sent fin */
#define TCPS_CLOSING 7 /*!< \brief closed xchd FIN; await FIN ACK */
#define TCPS_LAST_ACK 8 /*!< \brief had fin and close; await FIN ACK */
#define TCPS_FIN_WAIT_2 9 /*!< \brief have closed, fin is acked */
#define TCPS_TIME_WAIT 10 /*!< \brief in 2*msl quiet wait after close */
/******************************************************************************
* *
* S T R U C T U R E D E F I N I T I O N S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* G L O B A L V A R I A B L E S - N O I N I T I A L I Z E R S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* G L O B A L V A R I A B L E S - I N I T I A L I Z E R S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
void NutTcpStateMachine(NETBUF *nb);
int NutTcpInitStateMachine(void);
int NutTcpStatePassiveOpenEvent(TCPSOCKET *sock);
int NutTcpStateActiveOpenEvent(TCPSOCKET *sock);
int NutTcpStateCloseEvent(TCPSOCKET *sock);
int NutTcpStateWindowEvent(TCPSOCKET *sock);
int NutTcpStateRetranTimeout(TCPSOCKET *sock);
#ifdef __cplusplus
} /* End of extern "C" { */
#endif /* __cplusplus */
#endif
tcpin.c 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define TCPIN_C
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include "Common.h"
#include "XCore.h"
#include "XNutOS.h"
#include "ip.h"
#include "socket.h"
#include "tcp.h"
#ifdef NUTDEBUG
#include "netdebug.h"
#endif
#if defined(NUTNET)
/******************************************************************************
* *
* L O C A L D E F I N E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L T Y P E D E F S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L I N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L U N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
/*!
* \brief Process incoming TCP segments from IP layer.
*
* \warning The caller must take care not to pass broadcast
* or multicast segments.
*
* \note This routine is called by the IP layer on incoming
* TCP segments. Applications typically do not call
* this function.
*
*/
int NutTcpInput(NUTDEVICE * dev, NETBUF * nb)
{
TCPHDR *th = (TCPHDR *) nb->nb_tp.vp;
/* Process unicasts only. */
if (th && (nb->nb_flags & NBAF_UNICAST) != 0) {
int hdrlen = th->th_off * 4;
/* Check the header length. */
if (hdrlen >= sizeof(TCPHDR) && hdrlen <= nb->nb_tp.sz) {
nb->nb_ap.sz = nb->nb_tp.sz - hdrlen;
if (nb->nb_ap.sz) {
nb->nb_ap.vp = ((u_long *) th) + th->th_off;
}
nb->nb_tp.sz = hdrlen;
/*
** According to RFC1122 we MUST check the checksum
** on incoming segments. Anyway, we rely on lower
** level checksums to save processing resources.
**
** However, we may combine checksum calculation
** with moving data to the receiver buffer.
**/
NutTcpStateMachine(nb);
return 0;
}
}
/* Silently discard this segment. */
NutNetBufFree(nb);
return 0;
}
#endif
tcpout.c 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define TCPOUT_C
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include <string.h>
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include "Common.h"
#include "XCore.h"
#include "XNutOS.h"
#include "errno.h"
#include "in.h"
#include "ip.h"
#include "icmp.h"
#include "ip_icmp.h"
#include "ipcsum.h"
#include "socket.h"
#include "tcp.h"
#ifdef NUTDEBUG
#include "netdebug.h"
#endif
#if defined(NUTNET)
/******************************************************************************
* *
* L O C A L D E F I N E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L T Y P E D E F S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L I N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L U N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
/*!
* \brief Initiate TCP segment transmission.
*
* Check the TCP socket status and send any segment waiting
* for transmission.
*
* The function will not return until the data has been stored in the
* network device hardware for transmission. If the device is not ready
* for transmitting a new packet, the calling thread will be suspended
* until the device becomes ready again.
*
* If the target host is connected through an Ethernet network and if
* the hardware address of that host is currently unknown, an ARP
* request is sent out and the function will block until a response
* is received or an ARP timeout occurs.
*
* Segments containing data or SYN and FIN flags are added to a special
* queue for unacknowledged segments and will be retransmitted by the
* TCP timer thread, if not acknowledged by the remote within a specific
* time. The state machine will remove these segments from the queue
* as soon as they are acknowledged.
*
* \note This function is mainly used by the TCP state machine.
* Applications typically do not call this function but
* use NutTcpSend(), which is part of the TCP socket interface.
*
* \param sock Socket descriptor. This pointer must have been retrieved
* by calling NutTcpCreateSocket().
* \param data Pointer to TCP segment contents.
* \param size TCP segment length.
*
* \return 0 on success, -1 otherwise. Returning 0 does not imply that
* the data has been successfully delivered, because flow control
* and retransmission is still handled in the background.
*/
int NutTcpOutput(TCPSOCKET * sock, CONST u_char * data, u_short size)
{
NETBUF *nb;
NETBUF *nb_clone = 0;
TCPHDR *th;
u_short csum;
u_char hlen;
/*
* Check if anything to send at all.
*/
if (size == 0
&& (sock->so_tx_flags & (SO_SYN | SO_FIN | SO_FORCE)) == 0)
return 0;
/*
* Build TCP header. Add room for MAXSEG option if this is a
* SYN segment.
*/
hlen = sizeof(TCPHDR);
if (sock->so_tx_flags & SO_SYN)
hlen += 4;
if ((nb = NutNetBufAlloc(0, NBAF_TRANSPORT, hlen)) == 0) {
sock->so_last_error = ENOBUFS;
return -1;
}
th = (TCPHDR *) nb->nb_tp.vp;
th->th_sport = sock->so_local_port;
th->th_dport = sock->so_remote_port;
th->th_x2 = 0;
th->th_off = hlen >> 2;
sock->so_tx_flags &= ~SO_FORCE;
/*
* Process ACK flag.
*/
th->th_seq = htonl(sock->so_tx_nxt);
if (sock->so_tx_flags & SO_ACK) {
th->th_flags = TH_ACK;
sock->so_tx_flags &= ~SO_ACK;
th->th_ack = htonl(sock->so_rx_nxt);
} else {
th->th_flags = 0;
th->th_ack = 0;
}
/*
* Any SYN is sent first. Add options too. We rely on the caller
* not to send a SYN segment with data, because this may break
* some old stacks.
*/
if (sock->so_tx_flags & SO_SYN) {
u_char *cp;
u_short n_mss = htons(sock->so_mss);
th->th_flags |= TH_SYN;
sock->so_tx_flags &= ~SO_SYN;
sock->so_tx_nxt++;
cp = (u_char *) (th + 1);
*cp++ = TCPOPT_MAXSEG;
*cp++ = TCPOLEN_MAXSEG;
*cp++ = *(u_char *)&n_mss;
*cp = *((u_char *)(&n_mss) + 1);
}
/*
* Next preference is sending data. Set PUSH flag.
*/
else if (size) {
if ((nb = NutNetBufAlloc(nb, NBAF_APPLICATION, size)) == 0) {
sock->so_last_error = ENOBUFS;
return -1;
}
memcpy(nb->nb_ap.vp, (void *)data, size);
sock->so_tx_nxt += size;
th->th_flags |= TH_PUSH;
}
/*
* If all data sent, transmit any waiting FIN.
*/
else if (sock->so_tx_flags & SO_FIN) {
th->th_flags |= TH_FIN;
sock->so_tx_flags &= ~SO_FIN;
sock->so_tx_nxt++;
//@@@printf ("[%04X]TcpOutput: sending FIN\n", (u_short) sock);
}
/*
* We close our receiver window, if it is
* below the maximum segment size.
*/
if (sock->so_rx_win < sock->so_mss)
th->th_win = 0;
else
th->th_win = htons(sock->so_rx_win);
th->th_sum = 0;
th->th_urp = 0;
/*
* Calculate TCP checksum.
*/
csum =
NutIpPseudoChkSumPartial(sock->so_local_addr, sock->so_remote_addr,
IPPROTO_TCP,
htons(nb->nb_tp.sz + nb->nb_ap.sz));
csum = NutIpChkSumPartial(csum, th, nb->nb_tp.sz);
th->th_sum = NutIpChkSum(csum, nb->nb_ap.vp, nb->nb_ap.sz);
#ifdef NUTDEBUG
if (__tcp_trf)
NutDumpTcpHeader(__tcp_trs, "OUT", sock, nb);
#endif
/*
* To avoid a race condition in tcp state machine, the segment is first
* appended to the transmission que, and then sent to the network.
*/
/*
* Append the segment to our transmission queue.
*/
//@@@printf ("[%04X]TcpOutput: size: %u, flags: %u\n", (u_short) sock, size, th->th_flags);
if (size || ((th->th_flags & (TH_FIN | TH_SYN)))) {
//@@@printf ("[%04X]TcpOutput: appending nb to queue\n", (u_short) sock);
NETBUF *nbp;
nb->nb_next = 0;
if ((nbp = sock->so_tx_nbq) == 0) {
sock->so_tx_nbq = nb;
/*
* First entry, so initialize our retransmission timer.
* It is also set at various places in the state machine,
* but here is the best central point to do it. We may
* carefully check later, if we can remove some in the
* state machine.
*/
sock->so_retran_time = (u_short) NutGetMillis() | 1;
}
else {
while (nbp->nb_next)
nbp = nbp->nb_next;
nbp->nb_next = nb;
}
if (sock->so_rtt_seq == 0)
sock->so_rtt_seq = ntohl (th->th_seq);
nb_clone = NutNetBufClone (nb);
if (nb_clone == NULL) {
sock->so_last_error = ENOBUFS;
return -1;
}
}
else
nb_clone = nb;
/*
* IP output might fail because of routing, ARP or network device
* problems or because the system ran out of memory.
*/
if (NutIpOutput(IPPROTO_TCP, sock->so_remote_addr, nb_clone))
return -1;
NutNetBufFree (nb_clone);
return 0;
}
/*!
* \brief Reject an incoming segment.
*
* Send RST in response to an incoming segment, which should
* be rejected.
*
* The function avoids to send out a RST segment in response to
* an incoming RST segment.
*
* \note This function is mainly used by the TCP state machine.
* Applications typically do not call this function.
*
* \param nb Network buffer structure of the incoming segment.
* Will be released within this function.
*
* \return 0 on success, -1 otherwise.
*/
int NutTcpReject(NETBUF * nb)
{
u_short csum;
IPHDR *ih = (IPHDR *) nb->nb_nw.vp;
TCPHDR *th = (TCPHDR *) nb->nb_tp.vp;
/*
* Never send RST in response to RST.
*/
if (th->th_flags & TH_RST) {
NutNetBufFree(nb);
return 0;
}
/*
* Remove any application data and TCP header options.
*/
nb->nb_ap.sz = 0;
nb->nb_tp.sz = sizeof(TCPHDR);
/*
* Swap ports.
*/
csum = th->th_sport;
th->th_sport = th->th_dport;
th->th_dport = csum;
/*
* If the incoming segment has an ACK field, the reset
* takes its sequence number from the ACK field of the
* segment, otherwise the reset has sequence number zero
* and the ACK field is set to the sum of the sequence
* number and segment length of the incoming segment.
*/
if (th->th_flags & TH_ACK) {
th->th_flags = TH_RST;
th->th_seq = th->th_ack;
th->th_ack = 0;
} else {
if (th->th_flags & TH_SYN)
th->th_ack = htonl(ntohl(th->th_seq) + 1);
else
th->th_ack = th->th_seq;
th->th_seq = 0;
th->th_flags = TH_RST | TH_ACK;
}
th->th_x2 = 0;
th->th_off = sizeof(TCPHDR) / 4;
th->th_win = 0;
th->th_urp = 0;
/*
* Calculate TCP checksum without application data.
*/
th->th_sum = 0;
csum =
NutIpPseudoChkSumPartial(ih->ip_dst, ih->ip_src, IPPROTO_TCP,
htons(nb->nb_tp.sz));
th->th_sum = NutIpChkSum(csum, th, nb->nb_tp.sz);
/*
* Sent segment back to the source.
*/
#ifdef NUTDEBUG
if (__tcp_trf)
NutDumpTcpHeader(__tcp_trs, "REJ", 0, nb);
#endif
if(NutIpOutput(IPPROTO_TCP, ih->ip_src, nb) == 0)
NutNetBufFree(nb);
return 0;
}
#endif
tcpsm.c 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define TCPSM_C
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include "Common.h"
#include "XCore.h"
#include "XNutOS.h"
#include "errno.h"
#include "in.h"
#include "ip.h"
#include "route.h"
#include "socket.h"
#include "tcputil.h"
#include "tcp.h"
#ifdef NUTDEBUG
#include "netdebug.h"
#endif
#if defined(NUTNET)
/******************************************************************************
* *
* L O C A L D E F I N E S *
* *
******************************************************************************/
#ifndef TCP_RETRIES_MAX
#define TCP_RETRIES_MAX 7
#endif
/******************************************************************************
* *
* L O C A L T Y P E D E F S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L I N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
static NUTHANDLE tcpThread = 0;
/******************************************************************************
* *
* L O C A L U N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
extern TCPSOCKET *tcpSocketList;
NUTHANDLE tcp_in_rdy;
NETBUF *volatile tcp_in_nbq;
static u_short tcp_in_cnt;
static size_t tcp_adv_cnt;
static size_t tcp_adv_max = TCP_WINSIZE;
/* ================================================================
* Helper functions
* ================================================================
*/
/*!
* \brief Reads TCP option fields if any, and writes the data to
* the socket descriptor if important for us.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
* \param nb Network buffer structure containing a TCP segment.
*/
static void NutTcpInputOptions(TCPSOCKET * sock, NETBUF * nb)
{
u_char *cp;
u_short s;
/* any options there? */
if (nb->nb_tp.sz <= sizeof (TCPHDR))
return;
/* loop through available options */
for (cp = ((u_char*) nb->nb_tp.vp) + sizeof(TCPHDR); (*cp != TCPOPT_EOL)
&& (cp - (u_char *)nb->nb_tp.vp < (int)nb->nb_tp.sz); )
{
switch (*cp)
{
/* On NOP just proceed to next option */
case TCPOPT_NOP:
cp++;
continue;
/* Read MAXSEG option */
case TCPOPT_MAXSEG:
s = ntohs(((u_short)cp[2] << 8) | cp[3]);
if (s < sock->so_mss)
sock->so_mss = s;
cp += TCPOLEN_MAXSEG;
break;
/* Ignore any other options */
default:
cp += *(u_char*) (cp + 1);
break;
}
}
}
/*!
* \brief Move application data in sync from the network buffer
* structure to the socket's receive buffer.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
* \param nb Network buffer structure containing a TCP segment.
*/
static void NutTcpProcessAppData(TCPSOCKET * sock, NETBUF * nb)
{
/* TODO: Zero windows issue */
if ((((TCPHDR *) (nb->nb_tp.vp))->th_flags & TH_FIN) == 0) {
if (nb->nb_ap.sz > sock->so_rx_win)
nb->nb_ap.sz = sock->so_rx_win;
if (nb->nb_ap.sz == 0 || sock->so_rx_win < sock->so_mss) {
sock->so_tx_flags |= SO_ACK | SO_FORCE;
NutNetBufFree(nb);
NutTcpOutput(sock, 0, 0);
return;
}
}
/*
* Add the NETBUF to the socket's input buffer.
*/
if (sock->so_rx_buf) {
NETBUF *nbp = sock->so_rx_buf;
while (nbp->nb_next)
nbp = nbp->nb_next;
nbp->nb_next = nb;
} else
sock->so_rx_buf = nb;
/*
* Update the number of bytes available in the socket's input buffer
* and the sequence number we expect next.
*/
sock->so_rx_cnt += nb->nb_ap.sz;
sock->so_rx_nxt += nb->nb_ap.sz;
/*
* Reduce our TCP window size.
*/
if (nb->nb_ap.sz >= sock->so_rx_win)
sock->so_rx_win = 0;
else
sock->so_rx_win -= nb->nb_ap.sz;
/*
* Set the socket's ACK flag. This will enable ACK transmission in
* the next outgoing segment. If no more NETBUFs are queued, we
* force immediate transmission of the ACK.
*/
sock->so_tx_flags |= SO_ACK;
if (nb->nb_next)
nb->nb_next = 0;
else
sock->so_tx_flags |= SO_FORCE;
if (++sock->so_rx_apc > 8) {
NETBUF *nbq;
char apc = sock->so_rx_apc;
int cnt = sock->so_rx_cnt;
for (nbq = sock->so_rx_buf; nbq; nbq = nbq->nb_next) {
if (nbq->nb_ap.sz < 256) {
sock->so_rx_apc -= NutNetBufCollect(nbq, cnt);
break;
}
if (--apc < 8) {
break;
}
cnt -= nbq->nb_ap.sz;
}
}
NutTcpOutput(sock, 0, 0);
}
/*
* \param sock Socket descriptor.
*/
static void NutTcpProcessSyn(TCPSOCKET * sock, IPHDR * ih, TCPHDR * th)
{
u_short mss;
NUTDEVICE *dev;
IFNET *nif;
sock->so_local_addr = ih->ip_dst;
sock->so_remote_port = th->th_sport;
sock->so_remote_addr = ih->ip_src;
sock->so_rx_nxt = sock->so_tx_wl1 = sock->so_rx_isn = ntohl(th->th_seq);
sock->so_rx_nxt++;
sock->so_tx_win = ntohs(th->th_win);
/*
* To avoid unnecessary fragmentation, limit the
* maximum segment size to the maximum transfer
* unit of our interface.
*/
if ((dev = NutIpRouteQuery(ih->ip_src, 0)) != 0) {
nif = dev->dev_icb;
mss = nif->if_mtu - sizeof(IPHDR) - sizeof(TCPHDR);
if (sock->so_mss == 0 || sock->so_mss > mss)
sock->so_mss = mss;
/* Limit output buffer size to mms */
if (sock->so_devobsz > sock->so_mss)
sock->so_devobsz = sock->so_mss;
}
}
/*!
* \brief ACK processing.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
*
*/
static int NutTcpProcessAck(TCPSOCKET * sock, TCPHDR * th, u_short length)
{
NETBUF *nb;
u_long h_seq;
u_long h_ack;
/*
* If remote acked something not yet send, reply immediately.
*/
h_ack = ntohl(th->th_ack);
if (SeqIsAfter(h_ack, sock->so_tx_nxt)) {
sock->so_tx_flags |= SO_ACK | SO_FORCE;
return 0;
}
/*
* If the new sequence number or acknowledged sequence number
* is above our last update, we adjust our transmit window.
* Avoid dupe ACK processing on window updates.
*/
if (h_ack == sock->so_tx_una) {
h_seq = ntohl(th->th_seq);
if (SeqIsAfter(h_seq, sock->so_tx_wl1) || (h_seq == sock->so_tx_wl1 && !SeqIsAfter(sock->so_tx_wl2, h_ack))) {
sock->so_tx_win = ntohs(th->th_win);
sock->so_tx_wl1 = h_seq;
sock->so_tx_wl2 = h_ack;
}
}
/*
* Ignore old ACKs but wake up sleeping transmitter threads, because
* the window size may have changed.
*/
if (SeqIsAfter(sock->so_tx_una, h_ack)) {
return 0;
}
/*
* Process duplicate ACKs.
*/
if (h_ack == sock->so_tx_una) {
/*
* Don't count, if nothing is waiting for ACK,
* segment contains data or on SYN/FIN segments.
*/
if (sock->so_tx_nbq && length == 0 && (th->th_flags & (TH_SYN | TH_FIN)) == 0) {
/*
* If dupe counter reaches it's limit, resend
* the oldest unacknowledged netbuf.
*/
if (++sock->so_tx_dup >= 3) {
sock->so_tx_dup = 0;
#ifdef NUTDEBUG
if (__tcp_trf)
NutDumpTcpHeader(__tcp_trs, "RET", sock, sock->so_tx_nbq);
#endif
/*
* Retransmit first unacked packet from queue.
* Actually we got much more trouble if this fails.
*/
if (NutTcpStateRetranTimeout(sock))
return -1;
}
}
return 0;
}
/*
* We're here, so the ACK must have actually acked something
*/
sock->so_tx_dup = 0;
sock->so_tx_una = h_ack;
/*
* Bugfix contributed by Liu Limin: If the remote is slow and this
* line is missing, then Ethernut will send a full data packet even
* if the remote closed the window.
*/
sock->so_tx_win = ntohs(th->th_win);
/*
* Do round trip time calculation.
*/
if (sock->so_rtt_seq && SeqIsAfter(h_ack, sock->so_rtt_seq))
NutTcpCalcRtt (sock);
sock->so_rtt_seq = 0;
/*
* Remove all acknowledged netbufs.
*/
while ((nb = sock->so_tx_nbq) != 0) {
/* Calculate the sequence beyond this netbuf. */
h_seq = ntohl(((TCPHDR *) (nb->nb_tp.vp))->th_seq) + nb->nb_ap.sz;
if (((TCPHDR *) (nb->nb_tp.vp))->th_flags & (TH_SYN | TH_FIN)) {
h_seq++;
}
//@@@printf ("[%04X]*: processack, check seq#: %lu\n", (u_short) sock, h_seq);
if (SeqIsAfter(h_seq, h_ack)) {
break;
}
sock->so_tx_nbq = nb->nb_next;
NutNetBufFree(nb);
}
/*
* Reset retransmit timer and wake up waiting transmissions.
*/
if (sock->so_tx_nbq) {
sock->so_retran_time = (u_short) NutGetMillis() | 1;
} else {
sock->so_retran_time = 0;
}
sock->so_retransmits = 0;
return 0;
}
/* ================================================================
* State changes.
* ================================================================
*/
/*!
* State change, possibly inform application.
*
* \param sock Socket descriptor.
* \param state New state to switch to.
*
* \return 0 on success, -1 on illegal state change attempt.
*/
static int NutTcpStateChange(TCPSOCKET * sock, u_char state)
{
int rc = 0;
ureg_t txf = 0;
switch (sock->so_state) {
/* Handle the most common case first. */
case TCPS_ESTABLISHED:
switch (state) {
case TCPS_FIN_WAIT_1:
/*
* Closed by application.
*/
sock->so_tx_flags |= SO_FIN | SO_ACK;
txf = 1;
#ifdef RTLCONNECTHACK
/*
* Hack alert!
* On the RTL8019AS we got a problem. Because of not handling
* the CHRDY line, the controller drops outgoing packets when
* a browser opens multiple connections concurrently, producing
* several short incoming packets. Empirical test showed, that
* a slight delay during connects and disconnects helped to
* remarkably reduce this problem.
*/
NutDelay(5);
#endif
break;
case TCPS_CLOSE_WAIT:
/*
* FIN received.
*/
sock->so_tx_flags |= SO_ACK | SO_FORCE;
txf = 1;
break;
default:
rc = -1;
break;
}
break;
case TCPS_LISTEN:
/*
* SYN received.
*/
if (state == TCPS_SYN_RECEIVED) {
sock->so_tx_flags |= SO_SYN | SO_ACK;
txf = 1;
#ifdef RTLCONNECTHACK
/*
* Hack alert!
* On the RTL8019AS we got a problem. Because of not handling
* the CHRDY line, the controller drops outgoing packets when
* a browser opens multiple connections concurrently, producing
* several short incoming packets. Empirical test showed, that
* a slight delay during connects and disconnects helped to
* remarkably reduce this problem.
*/
NutDelay(5);
#endif
} else
rc = -1;
break;
case TCPS_SYN_SENT:
switch (state) {
case TCPS_LISTEN:
/*
* RST received on passive socket.
*/
break;
case TCPS_SYN_RECEIVED:
/*
* SYN received.
*/
sock->so_tx_flags |= SO_SYN | SO_ACK;
txf = 1;
break;
case TCPS_ESTABLISHED:
/*
* SYNACK received.
*/
sock->so_tx_flags |= SO_ACK | SO_FORCE;
txf = 1;
break;
default:
rc = -1;
break;
}
break;
case TCPS_SYN_RECEIVED:
switch (state) {
case TCPS_LISTEN:
/*
* RST received on passive socket.
*/
break;
case TCPS_ESTABLISHED:
/*
* ACK of SYN received.
*/
break;
case TCPS_FIN_WAIT_1:
/*
* Closed by application.
*/
sock->so_tx_flags |= SO_FIN;
txf = 1;
break;
case TCPS_CLOSE_WAIT:
/*
* FIN received.
*/
sock->so_tx_flags |= SO_FIN | SO_ACK;
txf = 1;
break;
default:
rc = -1;
break;
}
break;
case TCPS_FIN_WAIT_1:
switch (state) {
case TCPS_FIN_WAIT_1:
case TCPS_FIN_WAIT_2:
/*
* ACK of FIN received.
*/
break;
case TCPS_CLOSING:
/*
* FIN received.
*/
sock->so_tx_flags |= SO_ACK | SO_FORCE;
txf = 1;
break;
case TCPS_TIME_WAIT:
/*
* FIN and ACK of FIN received.
*/
break;
default:
rc = -1;
break;
}
break;
case TCPS_FIN_WAIT_2:
/*
* FIN received.
*/
if (state != TCPS_TIME_WAIT)
rc = -1;
sock->so_tx_flags |= SO_ACK | SO_FORCE;
txf = 1;
break;
case TCPS_CLOSE_WAIT:
/*
* Closed by application.
*/
if (state == TCPS_LAST_ACK) {
sock->so_tx_flags |= SO_FIN | SO_ACK;
txf = 1;
} else
rc = -1;
break;
case TCPS_CLOSING:
/*
* ACK of FIN received.
*/
if (state != TCPS_TIME_WAIT)
rc = -1;
break;
case TCPS_LAST_ACK:
rc = -1;
break;
case TCPS_TIME_WAIT:
rc = -1;
break;
case TCPS_CLOSED:
switch (state) {
case TCPS_LISTEN:
/*
* Passive open by application.
*/
break;
case TCPS_SYN_SENT:
/*
* Active open by application.
*/
sock->so_tx_flags |= SO_SYN;
txf = 1;
break;
default:
rc = -1;
break;
}
break;
}
#ifdef NUTDEBUG
if (__tcp_trf) {
fprintf(__tcp_trs, " %04x-", (u_int) sock);
if (rc)
NutDumpSockState(__tcp_trs, sock->so_state, "**ERR ", "**>");
NutDumpSockState(__tcp_trs, state, "[>", "]");
}
#endif
if (rc == 0) {
sock->so_state = state;
if (txf && NutTcpOutput(sock, 0, 0)) {
if (state == TCPS_SYN_SENT) {
rc = -1;
sock->so_last_error = EHOSTDOWN;
NutEventPostAsync(&sock->so_ac_tq);
}
}
if (state == TCPS_CLOSE_WAIT) {
/*
* Inform application.
*/
NutEventBroadcast(&sock->so_rx_tq);
NutEventBroadcast(&sock->so_pc_tq);
NutEventBroadcast(&sock->so_ac_tq);
}
}
return rc;
}
/* ================================================================
* Application events.
* ================================================================
*/
/*!
* \brief Initiated by the application.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
*
* \return 0 if granted, error code otherwise.
*/
int NutTcpStatePassiveOpenEvent(TCPSOCKET * sock)
{
if (sock->so_state != TCPS_CLOSED)
return (sock->so_last_error = EISCONN);
NutTcpStateChange(sock, TCPS_LISTEN);
/*
* Block application.
*/
NutEventWait(&sock->so_pc_tq, 0);
return 0;
}
/*!
* \brief Initiated by the application.
*
* The caller must make sure, that the socket is in closed state.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
*
* \return 0 if granted, -1 otherwise.
*/
int NutTcpStateActiveOpenEvent(TCPSOCKET * sock)
{
/*
* Switch state to SYN_SENT. This will
* transmit a SYN packet.
*/
NutTcpStateChange(sock, TCPS_SYN_SENT);
/*
* Block application.
*/
if(sock->so_state == TCPS_SYN_SENT)
NutEventWait(&sock->so_ac_tq, 0);
if (sock->so_state != TCPS_ESTABLISHED)
return -1;
return 0;
}
/*!
* \brief Socket close by application.
*
* If socket is in state SYN_RECEIVED or ESTABLISHED,
* it is changed to FINWAIT1.
*
* No further data sending is accepted.
* Receiving is still allowed.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
*/
int NutTcpStateCloseEvent(TCPSOCKET * sock)
{
if (sock == 0)
return -1;
NutThreadYield();
switch (sock->so_state) {
case TCPS_LISTEN:
case TCPS_SYN_SENT:
case TCPS_CLOSED:
/*
* No connection yet, immediately destroy the socket.
*/
NutTcpDestroySocket(sock);
break;
case TCPS_SYN_RECEIVED:
case TCPS_ESTABLISHED:
/*
* Send a FIN and wait for ACK or FIN.
*/
//@@@printf ("[%04X]ESTABLISHED: going to FIN_WAIT_1\n", (u_short) sock);
NutTcpStateChange(sock, TCPS_FIN_WAIT_1);
break;
case TCPS_CLOSE_WAIT:
/*
* RFC 793 is wrong.
*/
//@@@printf("[%04X]CLOSE_WAIT: going to LAST_ACK\n", (u_short) sock);
NutTcpStateChange(sock, TCPS_LAST_ACK);
break;
case TCPS_FIN_WAIT_1:
case TCPS_FIN_WAIT_2:
case TCPS_CLOSING:
case TCPS_LAST_ACK:
case TCPS_TIME_WAIT:
sock->so_last_error = EALREADY;
return -1;
default:
sock->so_last_error = ENOTCONN;
return -1;
}
return 0;
}
/*!
* \brief Initiated by the application.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
*/
int NutTcpStateWindowEvent(TCPSOCKET * sock)
{
if (sock == 0)
return -1;
sock->so_tx_flags |= SO_ACK | SO_FORCE;
NutTcpOutput(sock, 0, 0);
return 0;
}
/* ================================================================
* Timeout events.
* ================================================================
*/
/*!
* \brief Retransmit a segment after ACK timeout.
*
* This function is called by the TCP timer.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
* \returns Nonzero if socket was aborted due to reach of retransmit
* limit or network error.
*
*/
int NutTcpStateRetranTimeout(TCPSOCKET * sock)
{
NETBUF *so_tx_next;
if (sock->so_retransmits++ > TCP_RETRIES_MAX)
{
/* Abort the socket */
NutTcpAbortSocket(sock, ETIMEDOUT);
return -1;
} else {
#ifdef NUTDEBUG
if (__tcp_trf)
NutDumpTcpHeader(__tcp_trs, "RET", sock, sock->so_tx_nbq);
#endif
/* We must save sock->so_tx_nbq->nb_next before calling NutIpOutput,
* because in case of error the NETBUF is release by NutIpOutput and
* not longer available.
*/
so_tx_next = sock->so_tx_nbq->nb_next;
if (NutIpOutput(IPPROTO_TCP, sock->so_remote_addr, sock->so_tx_nbq)) {
/* Adjust packet queue */
sock->so_tx_nbq = so_tx_next;
/* Abort the socket */
NutTcpAbortSocket(sock, ENETDOWN);
return -1;
} else {
/* Restart the retransmission timer. */
sock->so_retran_time = (u_short) NutGetMillis() | 1;
return 0;
}
}
}
/* ================================================================
* Segment arrival events.
* ================================================================
*/
/*!
* \brief
* Process incoming segments in listening state.
*
* Wait for a connection request from a remote socket.
*
* \param sock Socket descriptor.
* \param nb Network buffer structure containing a TCP segment.
*/
static void NutTcpStateListen(TCPSOCKET * sock, u_char flags, TCPHDR * th, NETBUF * nb)
{
/*
* Got a SYN segment. Store relevant data in our socket
* structure and switch to TCPS_SYN_RECEIVED.
*/
if ((flags & (TH_SYN | TH_ACK | TH_RST)) == TH_SYN) {
NutTcpProcessSyn(sock, nb->nb_nw.vp, th);
NutTcpStateChange(sock, TCPS_SYN_RECEIVED);
NutNetBufFree(nb);
} else
NutTcpReject(nb);
}
/*!
* \brief Process incoming segments in SYN-SENT state.
*
* Wait for a matching connection request after having sent ours.
*
* \param sock Socket descriptor.
* \param nb Network buffer structure containing a TCP segment.
*/
static void NutTcpStateSynSent(TCPSOCKET * sock, u_char flags, TCPHDR * th, NETBUF * nb)
{
/*
* Validate ACK, if set.
*/
if (flags & TH_ACK) {
if (!SeqIsBetween(ntohl(th->th_ack), sock->so_tx_isn + 1, sock->so_tx_nxt)) {
NutTcpReject(nb);
return;
}
}
/*
* Handle RST flag. If we were in the LISTEN state,
* then we return to the LISTEN state, otherwise we
* abort the connection and go to the CLOSED state.
*/
if (flags & TH_RST) {
if (flags & TH_ACK) {
/*if (sock->so_pc_tq)
NutTcpStateChange(sock, TCPS_LISTEN);
else */
NutTcpAbortSocket(sock, ECONNREFUSED);
}
NutNetBufFree(nb);
return;
}
/*
* Handle SYN flag. If we got a valid ACK too, then
* our connection is established. Otherwise enter
* SYNRCVD state.
*/
if (flags & TH_SYN) {
NutTcpProcessSyn(sock, nb->nb_nw.vp, th);
if (flags & TH_ACK) {
NutTcpProcessAck(sock, th, nb->nb_ap.sz);
NutTcpStateChange(sock, TCPS_ESTABLISHED);
/* Wake up the actively connecting thread. */
NutEventPost(&sock->so_ac_tq);
} else {
NutTcpStateChange(sock, TCPS_SYN_RECEIVED);
}
}
NutNetBufFree(nb);
}
/*
* \brief
* Process incoming segments in SYN-RECEIVED state.
*
* Waiting for a confirming connection request
* acknowledgment after having both received
* and sent a connection request.
*
* \param sock Socket descriptor.
* \param nb Network buffer structure containing a TCP segment.
*/
static void NutTcpStateSynReceived(TCPSOCKET * sock, u_char flags, TCPHDR * th, NETBUF * nb)
{
/*
* If our previous ack receives a reset response,
* then we fall back to the listening state.
*/
if (flags & TH_RST) {
if (sock->so_pc_tq)
NutTcpStateChange(sock, TCPS_LISTEN);
else
NutTcpAbortSocket(sock, ECONNREFUSED);
NutNetBufFree(nb);
sock->so_retran_time = 0;
NutTcpDiscardBuffers(sock);
return;
}
/*
* Reject SYNs.
*/
if (flags & TH_SYN) {
NutTcpReject(nb);
return;
}
/*
* Silently discard segments without ACK.
*/
if ((flags & TH_ACK) == 0) {
NutNetBufFree(nb);
return;
}
/*
* Reject out of window sequence.
*/
if (!SeqIsBetween(ntohl(th->th_ack), sock->so_tx_una + 1, sock->so_tx_nxt)) {
NutTcpReject(nb);
return;
}
/* Acknowledge processing. */
NutTcpProcessAck(sock, th, nb->nb_ap.sz);
/*
* Even SYN segments may contain application data, which will be stored
* in the socket's input buffer. However, there is no need to post an
* event to any thread waiting for data, because our connection is not
* yet established.
*/
if (nb->nb_ap.sz)
NutTcpProcessAppData(sock, nb);
else
NutNetBufFree(nb);
/*
* Process state change.
*/
if (flags & TH_FIN) {
sock->so_rx_nxt++;
NutTcpStateChange(sock, TCPS_CLOSE_WAIT);
} else {
NutTcpStateChange(sock, TCPS_ESTABLISHED);
NutEventPost(&sock->so_pc_tq);
NutEventPost(&sock->so_ac_tq);
}
}
/*
* \brief Process incoming segments from established connections.
*
* Received application data will be delivered to the application
* until we receive a FIN segment.
*
* \param sock Socket descriptor.
* \param flags TCP flags.
* \param th Pointer to the TCP header within the NETBUF.
* \param nb Network buffer structure containing a TCP segment.
*
* \todo We may remove the unused counter of dropped segments, which
* were out of sequence.
*/
static void NutTcpStateEstablished(TCPSOCKET * sock, u_char flags, TCPHDR * th, NETBUF * nb)
{
if (flags & TH_RST) {
NutNetBufFree(nb);
NutTcpAbortSocket(sock, ECONNRESET);
return;
}
/*
* Reject SYNs. Silently discard late SYNs
* (Thanks to Mike Cornelius).
*/
if (flags & TH_SYN) {
if (ntohl(th->th_seq) != sock->so_rx_isn)
NutTcpReject(nb);
else
NutNetBufFree(nb);
return;
}
/*
* Silently discard segments without ACK.
*/
if ((flags & TH_ACK) == 0) {
NutNetBufFree(nb);
return;
}
NutTcpProcessAck(sock, th, nb->nb_ap.sz);
/*
* If the sequence number of the incoming segment is larger than
* expected, we probably missed one or more previous segments. Let's
* add this one to a linked list of segments received in advance and
* hope that the missing data will arrive later.
*/
if (SeqIsAfter(ntohl(th->th_seq),sock->so_rx_nxt)) {
NETBUF *nbq;
NETBUF **nbqp;
TCPHDR *thq;
u_long th_seq;
u_long thq_seq;
if (nb->nb_ap.sz) {
/* Keep track of the number of bytes used by packets
** received in advance. Honor a global limit. */
tcp_adv_cnt += nb->nb_dl.sz + sizeof(IPHDR) + sizeof (TCPHDR) + nb->nb_ap.sz;
if (tcp_adv_cnt > tcp_adv_max) {
/* Limit reached, discard the packet. */
NutNetBufFree(nb);
tcp_adv_cnt -= nb->nb_dl.sz + sizeof(IPHDR) + sizeof (TCPHDR) + nb->nb_ap.sz;
} else {
nbq = sock->so_rx_nbq;
nbqp = &sock->so_rx_nbq;
while (nbq) {
thq = (TCPHDR *) (nbq->nb_tp.vp);
th_seq = ntohl(th->th_seq);
thq_seq = ntohl(thq->th_seq);
if (SeqIsAfter(thq_seq, th_seq)) {
*nbqp = nb;
nb->nb_next = nbq;
break;
}
if (th_seq == thq_seq) {
NutNetBufFree(nb);
sock->so_tx_flags |= SO_ACK | SO_FORCE;
NutTcpOutput(sock, 0, 0);
return;
}
nbqp = &nbq->nb_next;
nbq = nbq->nb_next;
}
if (nbq == 0) {
*nbqp = nb;
nb->nb_next = 0;
}
}
} else
NutNetBufFree(nb);
sock->so_tx_flags |= SO_ACK | SO_FORCE;
NutTcpOutput(sock, 0, 0);
return;
}
/*
* Acknowledge any sequence numbers not expected,
* even if they do not contain any data. Keepalive
* packets contain a sequence number one less
* than the next data expected and they do not
* contain any data.
*/
if (ntohl(th->th_seq) != sock->so_rx_nxt) {
sock->so_tx_flags |= SO_ACK | SO_FORCE;
/* This seems to be unused. */
sock->so_oos_drop++;
NutNetBufFree(nb);
NutTcpOutput(sock, 0, 0);
}
/*
* The sequence number is exactly what we expected.
*/
else if (nb->nb_ap.sz) {
NutTcpProcessAppData(sock, nb);
/*
* Process segments we may have received in advance.
*/
while ((nb = sock->so_rx_nbq) != 0) {
th = (TCPHDR *) (nb->nb_tp.vp);
if (SeqIsAfter(ntohl(th->th_seq), sock->so_rx_nxt))
break;
sock->so_rx_nbq = nb->nb_next;
/* Update the heap space used by packets
** received in advance. */
tcp_adv_cnt -= nb->nb_dl.sz + sizeof(IPHDR) + sizeof (TCPHDR) + nb->nb_ap.sz;
if (ntohl(th->th_seq) == sock->so_rx_nxt) {
NutTcpProcessAppData(sock, nb);
flags |= th->th_flags;
} else
NutNetBufFree(nb);
}
/* Wake up a thread waiting for data. */
NutEventPost(&sock->so_rx_tq);
} else {
NutNetBufFree(nb);
//sock->so_tx_flags |= SO_ACK | SO_FORCE;
//NutTcpOutput(sock, 0, 0);
}
if (flags & TH_FIN) {
//@@@printf ("[%04X]ESTABLISHED: going to CLOSE_WAIT\n", (u_short) sock);
sock->so_rx_nxt++;
NutTcpStateChange(sock, TCPS_CLOSE_WAIT);
}
}
/*
* \brief Process incoming segments in FIN-WAIT1 state.
*
* Waiting for a connection termination request
* from the remote, or an acknowledgment of the
* connection termination request previously sent.
*
* The application already closed the socket.
*
* \param sock Socket descriptor.
* \param nb Network buffer structure containing a TCP segment.
*
* \todo The out of sync case seems to be ignored. Anyway, do we
* really need to process application data in this state?
*/
static void NutTcpStateFinWait1(TCPSOCKET * sock, u_char flags, TCPHDR * th, NETBUF * nb)
{
//@@@printf ("[%04X]FIN_WAIT_1: incomming segment, IP %04X\n", (u_short) sock, ntohs(((IPHDR*)nb->nb_nw.vp)->ip_id));
if (flags & TH_RST) {
NutNetBufFree(nb);
NutTcpDestroySocket(sock);
return;
}
/*
* Reject SYNs.
*/
if (flags & TH_SYN) {
NutTcpReject(nb);
return;
}
/*
* Silently discard segments without ACK.
*/
if ((flags & TH_ACK) == 0) {
NutNetBufFree(nb);
return;
}
//@@@if (flags & TH_FIN) printf ("[%04X]FIN_WAIT_1: received FIN\n", (u_short) sock);
//@@@printf ("[%04X]FIN_WAIT_1: received ACK: %lu, unack: %lu, next: %lu\n", (u_short) sock, ntohl(th->th_ack), sock->so_tx_una, sock->so_tx_nxt);
//@@@printf ("[%04X]FIN_WAIT_1: pre processack, nbq: %04X\n", (u_short) sock, (u_short) sock->so_tx_nbq);
NutTcpProcessAck(sock, th, nb->nb_ap.sz);
//@@@printf ("[%04X]FIN_WAIT_1: post processack, nbq: %04X\n", (u_short) sock, (u_short) sock->so_tx_nbq);
/*
* All segments had been acknowledged, including our FIN.
*/
if (sock->so_tx_nxt == sock->so_tx_una) {
//@@@printf ("[%04X]FIN_WAIT_1: going to FIN_WAIT_2\n", (u_short) sock);
NutTcpStateChange(sock, TCPS_FIN_WAIT_2);
}
/*
* Process application data and release the network buffer.
* Is this really required?
*/
if (nb->nb_ap.sz) {
NutTcpProcessAppData(sock, nb);
/* Wake up a thread waiting for data. */
NutEventPost(&sock->so_rx_tq);
}
else
NutNetBufFree(nb);
if (flags & TH_FIN) {
sock->so_rx_nxt++;
/*
* Our FIN has been acked.
*/
sock->so_time_wait = 0;
//@@@printf ("[%04X]FIN_WAIT_1: going to CLOSING\n", (u_short) sock);
if (sock->so_state == TCPS_FIN_WAIT_2)
NutTcpStateChange(sock, TCPS_TIME_WAIT);
else
NutTcpStateChange(sock, TCPS_CLOSING);
}
}
/*
* \brief Process incoming segments in FIN-WAIT2 state.
*
* Waiting for a connection termination request
* from the remote.
*
* The application already closed the socket.
*
* \param sock Socket descriptor.
* \param nb Network buffer structure containing a TCP segment.
*
* \todo There's probably no need to process application data.
*/
static void NutTcpStateFinWait2(TCPSOCKET * sock, u_char flags, TCPHDR * th, NETBUF * nb)
{
//@@@printf ("[%04X]FIN_WAIT_2: incomming segment, IP %04X\n", (u_short) sock, ntohs(((IPHDR*)nb->nb_nw.vp)->ip_id));
if (flags & TH_RST) {
NutNetBufFree(nb);
NutTcpDestroySocket(sock);
return;
}
/*
* Reject SYNs.
*/
if (flags & TH_SYN) {
NutTcpReject(nb);
return;
}
/*
* Silently discard segments without ACK.
*/
if ((flags & TH_ACK) == 0) {
NutNetBufFree(nb);
return;
}
//@@@printf ("[%04X]FIN_WAIT_2: received ACK: %lu, unack: %lu, next: %lu\n", (u_short) sock, ntohl(th->th_ack), sock->so_tx_una, sock->so_tx_nxt);
/*
* Process acknowledge and application data and release the
* network buffer.
*/
NutTcpProcessAck(sock, th, nb->nb_ap.sz);
//@@@if (sock->so_tx_nbq) printf ("[%04X]FIN_WAIT_2: xmit buffer not empty!", (u_short) sock);
/* Do we really need this? */
if (nb->nb_ap.sz) {
NutTcpProcessAppData(sock, nb);
/* Wake up a thread waiting for data. */
NutEventPost(&sock->so_rx_tq);
}
else
NutNetBufFree(nb);
if (flags & TH_FIN) {
sock->so_rx_nxt++;
sock->so_time_wait = 0;
//@@@printf ("[%04X]FIN_WAIT_2: going to TIME_WAIT\n", (u_short) sock);
NutTcpStateChange(sock, TCPS_TIME_WAIT);
}
}
/*
* \brief
* Process incoming segments in CLOSE-WAIT state.
*
* Waiting for a connection termination request
* from the local application.
*
* \param sock Socket descriptor.
* \param nb Network buffer structure containing a TCP segment.
*/
static void NutTcpStateCloseWait(TCPSOCKET * sock, u_char flags, TCPHDR * th, NETBUF * nb)
{
//@@@printf ("[%04X]CLOSE_WAIT: incomming segment, IP %04X\n", (u_short) sock, ((IPHDR*)nb->nb_nw.vp)->ip_id);
if (flags & TH_RST) {
NutNetBufFree(nb);
NutTcpAbortSocket(sock, ECONNRESET);
return;
}
/*
* Reject SYNs.
*/
if (flags & TH_SYN) {
NutTcpReject(nb);
return;
}
/*
* Silently discard segments without ACK.
*/
if ((flags & TH_ACK) == 0) {
NutNetBufFree(nb);
return;
}
NutTcpProcessAck(sock, th, nb->nb_ap.sz);
NutNetBufFree(nb);
}
/*
* \brief
* Process incoming segments in CLOSING state.
*
* Waiting for a connection termination request
* acknowledgment from the remote.
*
* The application already closed the socket.
*
* \param sock Socket descriptor.
* \param nb Network buffer structure containing a TCP segment.
*/
static void NutTcpStateClosing(TCPSOCKET * sock, u_char flags, TCPHDR * th, NETBUF * nb)
{
//@@@printf ("[%04X]CLOSING: Incomming segment\n", (u_short) sock);
if (flags & TH_RST) {
NutNetBufFree(nb);
NutTcpDestroySocket(sock);
return;
}
/*
* Reject SYNs.
*/
if (flags & TH_SYN) {
NutTcpReject(nb);
return;
}
/*
* Silently discard segments without ACK.
*/
if ((flags & TH_ACK) == 0) {
NutNetBufFree(nb);
return;
}
NutTcpProcessAck(sock, th, nb->nb_ap.sz);
/*
* All segments had been acknowledged, including our FIN.
*/
if (sock->so_tx_nxt == sock->so_tx_una) {
sock->so_time_wait = 0;
NutTcpStateChange(sock, TCPS_TIME_WAIT);
//@@@printf ("[%04X]CLOSING: Going to TIME_WAIT\n", (u_short) sock);
}
//@@@else printf ("[%04X]CLOSING: NOT changing state\n", (u_short) sock);
NutNetBufFree(nb);
}
/*!
* \brief
* Process incoming segment in LAST_ACK state.
*
* Waiting for an acknowledgment of the connection termination
* request previously sent.
*
* The application already closed the socket.
*
* \param sock Socket descriptor.
* \param flags TCP flags of incoming segment.
* \param th TCP header of incoming segment.
* \param nb Network buffer structure containing a TCP segment.
*/
static void NutTcpStateLastAck(TCPSOCKET * sock, u_char flags, TCPHDR * th, NETBUF * nb)
{
//@@@printf ("[%04X]LAST_ACK: incomming segment, IP %04X\n", (u_short) sock, ((IPHDR*)nb->nb_nw.vp)->ip_id);
if (flags & TH_RST) {
NutNetBufFree(nb);
NutTcpDestroySocket(sock);
return;
}
/*
* Reject SYNs.
*/
if (flags & TH_SYN) {
NutTcpReject(nb);
return;
}
/*
* Silently discard segments without ACK.
*/
if ((flags & TH_ACK) == 0) {
NutNetBufFree(nb);
return;
}
NutTcpProcessAck(sock, th, nb->nb_ap.sz);
NutNetBufFree(nb);
if (sock->so_tx_nxt == sock->so_tx_una)
NutTcpDestroySocket(sock);
//@@@else printf ("[%04X]LAST_ACK: no destroy sock\n", (u_short) sock);
}
/*!
* \brief Process incoming TCP segments.
*
* Processing is based on the current state of the socket connection.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
* \param nb Network buffer structure containing a TCP segment.
* Will be released within this routine.
*/
static void NutTcpStateProcess(TCPSOCKET * sock, NETBUF * nb)
{
u_long tx_win;
u_long tx_una;
TCPHDR *th = (TCPHDR *) nb->nb_tp.vp;
u_char flags = th->th_flags;
#ifdef NUTDEBUG
if (__tcp_trf) {
fprintf(__tcp_trs, " %04x-", (u_int) sock);
NutDumpSockState(__tcp_trs, sock->so_state, "[", ">]");
}
#endif
switch (sock->so_state) {
/* Handle the most common case first. */
case TCPS_ESTABLISHED:
tx_win = sock->so_tx_win;
tx_una = sock->so_tx_una;
NutTcpStateEstablished(sock, flags, th, nb);
/* Wake up all threads waiting for transmit, if something interesting happened. */
if(sock->so_state != TCPS_ESTABLISHED || /* Status changed. */
sock->so_tx_win > tx_win || /* Windows changed. */
sock->so_tx_una != tx_una) { /* Unacknowledged data changed. */
NutEventBroadcast(&sock->so_tx_tq);
}
break;
case TCPS_LISTEN:
NutTcpStateListen(sock, flags, th, nb);
break;
case TCPS_SYN_SENT:
NutTcpStateSynSent(sock, flags, th, nb);
break;
case TCPS_SYN_RECEIVED:
NutTcpStateSynReceived(sock, flags, th, nb);
break;
case TCPS_FIN_WAIT_1:
NutTcpStateFinWait1(sock, flags, th, nb);
break;
case TCPS_FIN_WAIT_2:
NutTcpStateFinWait2(sock, flags, th, nb);
break;
case TCPS_CLOSE_WAIT:
NutTcpStateCloseWait(sock, flags, th, nb);
break;
case TCPS_CLOSING:
NutTcpStateClosing(sock, flags, th, nb);
break;
case TCPS_LAST_ACK:
NutTcpStateLastAck(sock, flags, th, nb);
break;
case TCPS_TIME_WAIT:
/*
* Ignore everything while in TIME_WAIT state.
*/
NutNetBufFree(nb);
break;
case TCPS_CLOSED:
/*
* Reject everything while in CLOSED state.
*/
NutTcpReject(nb);
break;
default:
NutNetBufFree(nb);
break;
}
}
/*! \fn NutTcpSm(void *arg)
* \brief TCP state machine thread.
*
* The TCP state machine serves two purposes: It processes incoming TCP
* segments and handles TCP timers.
*/
THREAD(NutTcpSm, arg)
{
NETBUF *nb;
NETBUF *nbx;
TCPHDR *th;
IPHDR *ih;
TCPSOCKET *sock;
u_char tac = 0;
/*
* It won't help giving us a higher priority than the application
* code. We depend on the speed of the reading application.
*/
NutThreadSetPriority(NUT_THREAD_TCPSM_PRIORITY);
for (;;) {
if (++tac > 3 || NutEventWait(&tcp_in_rdy, 200)) {
tac = 0;
for (sock = tcpSocketList; sock; sock = sock->so_next) {
/*
* Send late acks.
*/
if (sock->so_tx_flags & SO_ACK) {
sock->so_tx_flags |= SO_FORCE;
NutTcpOutput(sock, 0, 0);
}
/*
* Process retransmit timer.
*/
if (sock->so_tx_nbq && sock->so_retran_time) {
if ((u_short)NutGetMillis() - (sock->so_retran_time & ~1) >= sock->so_rtto) {
NutTcpStateRetranTimeout(sock);
}
}
/*
* Destroy sockets after timeout in TIMEWAIT state.
*/
if (sock->so_state == TCPS_TIME_WAIT || sock->so_state == TCPS_FIN_WAIT_2) {
if (sock->so_time_wait++ >= 9) {
NutTcpDestroySocket(sock);
break;
}
}
/*
* Recover from SYN flood attacks.
*/
else if (sock->so_state == TCPS_SYN_RECEIVED) {
if (sock->so_time_wait++ >= 45) {
sock->so_state = TCPS_LISTEN;
sock->so_time_wait = 0;
}
}
}
} else {
nb = tcp_in_nbq;
tcp_in_nbq = 0;
tcp_in_cnt = 0;
while (nb) {
ih = (IPHDR *) nb->nb_nw.vp;
th = (TCPHDR *) nb->nb_tp.vp;
sock = NutTcpFindSocket(th->th_dport, th->th_sport, ih->ip_src);
#ifdef NUTDEBUG
if (__tcp_trf)
NutDumpTcpHeader(__tcp_trs, " IN", sock, nb);
#endif
nbx = nb->nb_next;
if (sock) {
NutTcpInputOptions(sock, nb);
NutTcpStateProcess(sock, nb);
}
/*
* Reject the segment, if no matching socket was found.
*/
else
NutTcpReject(nb);
nb = nbx;
}
}
}
}
/*!
* \brief Process incoming TCP segments.
*
* All incoming TCP packets are passed to this routine. They will
* be added to a global queue and processed by the TCP state
* machine, which is running on a separate thread.
*
* \note This routine is called by the IP layer on incoming
* TCP segments. Applications typically do not call
* this function.
*/
void NutTcpStateMachine(NETBUF * nb)
{
NETBUF *nbp;
u_short size;
nb->nb_next = 0;
/*
* Incoming TCP segments are rejected and released if no TCP
* sockets have been opened. Not doing so would add them
* to the queue and never release the NETBUF. Thanks to
* Ralph Mason for this fix.
*/
if (tcpThread == 0) {
NutTcpReject(nb);
return;
}
if ((nbp = tcp_in_nbq) == 0) {
tcp_in_nbq = nb;
NutEventPost(&tcp_in_rdy);
} else {
size = nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz;
if (tcp_in_cnt + size + 2048 < NutHeapAvailable()) {
tcp_in_cnt += size;
while (nbp->nb_next)
nbp = nbp->nb_next;
nbp->nb_next = nb;
NutEventPost(&tcp_in_rdy);
} else
NutNetBufFree(nb);
}
}
/*!
* \brief Start TCP state machine.
*
* The socket interface will automatically call this routine as
* soon as the first socket is created.
*
* \return 0 on success, -1 otherwise.
*/
int NutTcpInitStateMachine(void)
{
if (tcpThread == 0 && (tcpThread = NutThreadCreate("tcpsm", NutTcpSm, NULL, NUT_THREAD_TCPSM_STACK)) == 0)
return -1;
return 0;
}
/*!
* \brief Closes socket with error.
*
* Aborts any socket activity and sets last error.
*
* \param sock Socket descriptor.
* \param last_error Error number to report
*
* \note This routine is called internally by Nut/Net.
* Applications typically do not call this function.
*
* \return 0 on success, -1 otherwise.
*/
int NutTcpAbortSocket(TCPSOCKET * sock, u_short last_error)
{
sock->so_last_error = last_error;
sock->so_retran_time = 0;
sock->so_time_wait = 0;
/*
* If NutTcpCloseSocket was already called, we have to change
* to TCPS_TIME_WAIT state, otherwise the socket will not be destroyed.
* For the other cases just go to TCPS_CLOSED.
*/
if (sock->so_state >= TCPS_FIN_WAIT_1) /* FIN_WAIT_1, FIN_WAIT_2, CLOSING, LAST_ACK, TIME_WAIT */
sock->so_state = TCPS_TIME_WAIT;
else
sock->so_state = TCPS_CLOSED;
NutTcpDiscardBuffers(sock);
NutEventBroadcast(&sock->so_rx_tq);
NutEventBroadcast(&sock->so_tx_tq);
NutEventBroadcast(&sock->so_pc_tq);
NutEventBroadcast(&sock->so_ac_tq);
return 0;
}
#endif
tcpsock.c 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define TCPSOCK_C
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include <string.h>
#include <stdio.h>
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include "Common.h"
#include "XCore.h"
#include "XNutOS.h"
#include "XLib.h"
#include "errno.h"
#include "route.h"
#include "in.h"
#include "ip.h"
#include "icmp.h"
#include "ip_icmp.h"
#include "ipcsum.h"
#include "socket.h"
#include "tcp.h"
#ifdef NUTDEBUG
#include "netdebug.h"
#endif
#if defined(NUTNET)
/******************************************************************************
* *
* L O C A L D E F I N E S *
* *
******************************************************************************/
#define TICK_RATE 1
/******************************************************************************
* *
* L O C A L T Y P E D E F S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L I N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
TCPSOCKET *tcpSocketList = 0; /*!< Global linked list of all TCP sockets. */
//static volatile u_short last_local_port = 4096; /* Unassigned local port. */
static u_short last_local_port = 4096; /* Unassigned local port. */
static u_char tcpStateRunning = 0;
/******************************************************************************
* *
* L O C A L U N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
void NutTcpDiscardBuffers(TCPSOCKET * sock)
{
NETBUF *nb;
while ((nb = sock->so_rx_buf) != 0) {
sock->so_rx_buf = nb->nb_next;
NutNetBufFree(nb);
}
while ((nb = sock->so_tx_nbq) != 0) {
sock->so_tx_nbq = nb->nb_next;
NutNetBufFree(nb);
}
while ((nb = sock->so_rx_nbq) != 0) {
sock->so_rx_nbq = nb->nb_next;
NutNetBufFree(nb);
}
}
/*!
* \brief Destroy a previously allocated socket.
*
* Remove socket from the socket list and release occupied memory.
*
* Applications must not call this function. It is automatically called
* by a timer after the socket has been closed by NutTcpCloseSocket().
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
*/
void NutTcpDestroySocket(TCPSOCKET * sock)
{
TCPSOCKET *sp;
TCPSOCKET *volatile *spp;
//@@@printf ("[%04X] Calling destroy.\n", (u_short) sock);
/*
* Remove socket from the list.
*/
sp = tcpSocketList;
spp = &tcpSocketList;
while (sp) {
if (sp == sock) {
*spp = sp->so_next;
break;
}
spp = &sp->so_next;
sp = sp->so_next;
}
/*
* Free all memory occupied by the socket.
*/
if (sp) {
NutTcpDiscardBuffers(sock);
if (sock->so_devocnt)
{
NutHeapFree(sock->so_devobuf);
sock->so_devocnt = 0;
}
memset(sock, 0, sizeof(TCPSOCKET));
NutHeapFree(sock);
}
}
/*!
* \brief Find a matching socket.
*
* Loop through all sockets and find a matching connection (prefered)
* or a listening socket.
*
* Applications typically do not call this function.
*
* \param lport Local port number.
* \param rport Remote port number.
* \param raddr Remote IP address in network byte order.
*
* \return Socket descriptor.
*/
TCPSOCKET *NutTcpFindSocket(u_short lport, u_short rport, u_long raddr)
{
TCPSOCKET *sp;
TCPSOCKET *sock = 0;
/*
* Try to find an exact match for the remote
* address and port first.
*/
for (sp = tcpSocketList; sp; sp = sp->so_next) {
if (sp->so_local_port == lport) {
if (sp->so_remote_addr == raddr && sp->so_remote_port == rport && sp->so_state != TCPS_CLOSED) {
sock = sp;
break;
}
}
}
/*
* If no exact match exists, try a listening socket.
* This part had been totally wrong, because it
* didn't check the local port number and accepted
* incoming requests on any port. Thanks to
* Alejandro Lopez, who pointed this out.
*/
if (sock == 0) {
for (sp = tcpSocketList; sp; sp = sp->so_next) {
if (sp->so_state == TCPS_LISTEN && sp->so_local_port == lport) {
sock = sp;
break;
}
}
}
return sock;
}
/*!
* \brief Create a TCP socket.
*
* Allocates a TCPSOCKET structure from heap memory, initializes
* it and returns a pointer to that structure.
*
* The very first call will also start the TCP state machine,
* which is running in a separate thread.
*
* \return Socket descriptor of the newly created TCP socket or
* 0 if there is not enough memory left.
*/
TCPSOCKET *NutTcpCreateSocket(void)
{
TCPSOCKET *sock = 0;
if (tcpStateRunning || (tcpStateRunning = (NutTcpInitStateMachine() == 0))) {
if ((sock = NutHeapAllocClear(sizeof(TCPSOCKET))) != 0) {
sock->so_state = TCPS_CLOSED;
/*
* Initialize the virtual device interface.
*/
sock->so_devtype = IFTYP_TCPSOCK;
sock->so_devread = NutTcpDeviceRead;
sock->so_devwrite = NutTcpDeviceWrite;
sock->so_devioctl = NutTcpDeviceIOCtl;
sock->so_tx_isn = NutGetTickCount(); /* Generate the ISN from the nut_ticks counter */
sock->so_tx_una = sock->so_tx_isn;
sock->so_tx_nxt = sock->so_tx_isn;
sock->so_rx_bsz = sock->so_rx_win = TCP_WINSIZE;
sock->so_mss = TCP_MSS;
sock->so_rtto = 1000; /* Initial retransmission time out */
sock->so_next = tcpSocketList;
sock->so_devobsz = TCP_MSS; /* Default output buffer size is TCP_MSS bytes */
tcpSocketList = sock;
}
}
return sock;
}
/*!
* \brief Set value of a TCP socket option.
*
* The following values can be set:
*
* - #TCP_MAXSEG Maximum segment size (#u_short). Can only be set if
socket is not yet connected.
* - #SO_SNDTIMEO Socket send timeout (#u_long).
* - #SO_RCVTIMEO Socket receive timeout (#u_long).
* - #SO_SNDBUF Socket output buffer size (#u_short).
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
* \param optname Option to set.
* \param optval Pointer to the value.
* \param optlen Length of the value.
* \return 0 on success, -1 otherwise. The specific error code
* can be retrieved by calling NutTcpError().
*/
int NutTcpSetSockOpt(TCPSOCKET * sock, int optname, CONST void *optval, int optlen)
{
int rc = -1;
if (sock == 0)
return -1;
switch (optname) {
case TCP_MAXSEG:
if (optval == 0 || optlen != sizeof(u_short))
sock->so_last_error = EINVAL;
else if (sock->so_state != TCPS_CLOSED)
sock->so_last_error = EISCONN;
else {
sock->so_mss = *((u_short *) optval);
rc = 0;
}
break;
case SO_RCVBUF:
if (optval == 0 || optlen != sizeof(u_short))
sock->so_last_error = EINVAL;
else {
sock->so_rx_bsz = *((u_short *) optval);
sock->so_rx_win = sock->so_rx_bsz;
rc = 0;
}
break;
case SO_SNDTIMEO:
if (optval == 0 || optlen != sizeof(u_long))
sock->so_last_error = EINVAL;
else {
sock->so_write_to = *((u_long *) optval);
rc = 0;
}
break;
case SO_RCVTIMEO:
if (optval == 0 || optlen != sizeof(u_long))
sock->so_last_error = EINVAL;
else {
sock->so_read_to = *((u_long *) optval);
rc = 0;
}
break;
case SO_SNDBUF:
if (optval == 0 || optlen != sizeof(u_short))
sock->so_last_error = EINVAL;
else {
NutTcpDeviceWrite(sock, 0, 0);
sock->so_devobsz = *((u_short *) optval);
rc = 0;
}
break;
default:
sock->so_last_error = ENOPROTOOPT;
break;
}
return rc;
}
/*!
* \brief Get a TCP socket option value.
*
* The following values can be set:
*
* - #TCP_MAXSEG Maximum segment size (#u_short).
* - #SO_SNDTIMEO Socket send timeout (#u_long).
* - #SO_RCVTIMEO Socket receive timeout (#u_long).
* - #SO_SNDBUF Socket output buffer size (#u_short).
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
* \param optname Option to get.
* \param optval Points to a buffer receiving the value.
* \param optlen Length of the value buffer.
*
* \return 0 on success, -1 otherwise. The specific error code
* can be retrieved by calling NutTcpError().
*/
int NutTcpGetSockOpt(TCPSOCKET * sock, int optname, void *optval, int optlen)
{
int rc = -1;
if (sock == 0)
return -1;
switch (optname) {
case TCP_MAXSEG:
if (optval == 0 || optlen != sizeof(u_short))
sock->so_last_error = EINVAL;
else {
*((u_short *) optval) = sock->so_mss;
rc = 0;
}
break;
case SO_RCVBUF:
if (optval == 0 || optlen != sizeof(u_short))
sock->so_last_error = EINVAL;
else {
*((u_short *) optval) = sock->so_rx_bsz;
rc = 0;
}
break;
case SO_SNDTIMEO:
if (optval == 0 || optlen != sizeof(u_long))
sock->so_last_error = EINVAL;
else {
*((u_long *) optval) = sock->so_write_to;
rc = 0;
}
break;
case SO_RCVTIMEO:
if (optval == 0 || optlen != sizeof(u_long))
sock->so_last_error = EINVAL;
else {
*((u_long *) optval) = sock->so_read_to;
rc = 0;
}
break;
case SO_SNDBUF:
if (optval == 0 || optlen != sizeof(u_short))
sock->so_last_error = EINVAL;
else {
*((u_short *) optval) = sock->so_devobsz;
rc = 0;
}
break;
default:
sock->so_last_error = ENOPROTOOPT;
break;
}
return rc;
}
/*!
* \brief Connect to a remote socket.
*
* This function tries to establish a connection to the specified
* remote port of the specified remote server. The calling thread
* will be suspended until a connection is successfully established
* or an error occurs.
*
* This function is typically used by TCP client applications.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
* \param addr IP address of the host to connect (network byte order).
* \param port Port number to connect (host byte order).
*
* \return 0 on success, -1 otherwise. The specific error code
* can be retrieved by calling NutTcpError().
*/
int NutTcpConnect(TCPSOCKET * sock, u_long addr, u_short port)
{
TCPSOCKET *sp;
NUTDEVICE *dev;
if (sock == 0)
return -1;
/*
* Despite RFC793 we do not allow a passive
* open to become active.
*/
if (sock->so_state == TCPS_LISTEN) {
sock->so_last_error = EOPNOTSUPP;
return -1;
} else if (sock->so_state != TCPS_CLOSED) {
sock->so_last_error = EISCONN;
return -1;
}
/*
* Find an unused local port.
*/
do {
if (++last_local_port == 0)
last_local_port = 4096;
sp = tcpSocketList;
while (sp) {
/* Thanks to Ralph Mason for fixing the byte order bug. */
if (sp->so_local_port == htons(last_local_port))
break;
sp = sp->so_next;
}
} while (sp);
/*
* OK - we've got a new port. Now fill
* remaining parts of the socket structure.
*/
sock->so_local_port = htons(last_local_port);
sock->so_remote_port = htons(port);
sock->so_remote_addr = addr;
/*
* Get local address for this destination.
*/
if ((dev = NutIpRouteQuery(addr, 0)) != 0) {
IFNET *nif = dev->dev_icb;
sock->so_local_addr = nif->if_local_ip;
} else {
sock->so_last_error = EHOSTUNREACH;
return -1;
}
/*
* Trigger active open event for the state machine.
* This will suspend the thread until synchronized.
*/
return NutTcpStateActiveOpenEvent(sock);
}
/*!
* \brief Wait for incoming connect from a remote socket.
*
* The calling thread will be suspended until until an
* incoming connection request is received.
*
* This function is typically used by TCP server applications.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
* \param port Port number to listen to (host byte order).
*
* \return 0 on success, -1 otherwise. The specific error code
* can be retrieved by calling NutTcpError().
*/
int NutTcpAccept(TCPSOCKET * sock, u_short port)
{
sock->so_local_port = htons(port);
return NutTcpStatePassiveOpenEvent(sock);
}
/*!
* \brief Send data on a connected TCP socket.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket(). In
* addition a connection must have been established
* by calling NutTcpConnect or NutTcpAccept.
* \param data Pointer to a buffer containing the data to send.
* \param len Number of bytes to be sent.
*
* \return If successful, the number of bytes added to the socket transmit
* buffer. This is limited to the maximum segment size of the
* connection and thus may be less than the specified number of
* bytes to send. The return value -1 indicates a fatal error.
* On time out, a value of 0 is returned.
*/
int NutTcpSend(TCPSOCKET * sock, CONST void *data, int len)
{
u_short unacked;
/*
* Check parameters.
*/
NutThreadYield();
if (sock == 0)
return -1;
if (data == 0 || len == 0)
return 0;
/*
* Limit the transmission size to our maximum segment size.
*/
if (len > sock->so_mss)
len = sock->so_mss;
for (;;) {
/*
* We can only send on an established connection.
*/
if (sock->so_state != TCPS_ESTABLISHED) {
sock->so_last_error = ENOTCONN;
return -1;
}
/*
* Limit the size of unacknowledged data to four full segments.
* Also wait for peer's window open wide enough to take all our
* data. This also avoids silly window syndrome on our side.
*/
unacked = sock->so_tx_nxt - sock->so_tx_una;
if ((unacked >> 2) < sock->so_mss && len <= sock->so_tx_win - unacked) {
break;
}
if (NutEventWait(&sock->so_tx_tq, sock->so_write_to)) {
return 0;
}
}
/*
* The segment will be automatically retransmitted if not
* acknowledged in time. If this returns an error, it's a
* fatal one.
*/
sock->so_tx_flags |= SO_ACK;
if (NutTcpOutput(sock, data, len))
return -1;
return len;
}
/*!
* \brief Receive data on a connected TCP socket.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket(). In
* addition a connection must have been established
* by calling NutTcpConnect or NutTcpAccept.
* \param data Pointer to the buffer that receives the data.
* \param size Size of the buffer.
*
* \return If successful, the number of received data bytes
* is returned. This may be less than the specified
* size of the buffer. The return value 0 indicates
* a timeout, while -1 is returned in case of an error
* or broken connection. Call NutTcpError() to determine
* the specific error code.
*/
int NutTcpReceive(TCPSOCKET * sock, void *data, int size)
{
int i;
NutThreadYield();
/*
* Check parameters.
*/
if (sock == 0)
return -1;
if (sock->so_state != TCPS_ESTABLISHED && sock->so_state != TCPS_CLOSE_WAIT) {
sock->so_last_error = ENOTCONN;
return -1;
}
if (data == 0 || size == 0)
return 0;
/*
* Wait until any data arrived, a timeout occurs
* or the connection terminates.
*/
while (sock->so_rx_cnt - sock->so_rd_cnt == 0) {
if (sock->so_state != TCPS_ESTABLISHED) {
sock->so_last_error = ENOTCONN;
return -1;
}
if (NutEventWait(&sock->so_rx_tq, sock->so_read_to))
return 0;
}
if (size > sock->so_rx_cnt - sock->so_rd_cnt)
size = sock->so_rx_cnt - sock->so_rd_cnt;
if (size) {
NETBUF *nb;
u_short rd_cnt; /* Bytes read from NETBUF. */
u_short nb_cnt; /* Bytes left in NETBUF. */
u_short ab_cnt; /* Total bytes in app buffer. */
u_short mv_cnt; /* Bytes to move to app buffer. */
rd_cnt = sock->so_rd_cnt;
ab_cnt = 0;
while (ab_cnt < size) {
nb = sock->so_rx_buf;
nb_cnt = nb->nb_ap.sz - rd_cnt;
mv_cnt = size - ab_cnt;
if (mv_cnt > nb_cnt)
mv_cnt = nb_cnt;
memcpy((char *) data + ab_cnt, (char *) (nb->nb_ap.vp) + rd_cnt, mv_cnt);
ab_cnt += mv_cnt;
rd_cnt += mv_cnt;
if (mv_cnt >= nb_cnt) {
sock->so_rx_buf = nb->nb_next;
sock->so_rx_cnt -= rd_cnt;
NutNetBufFree(nb);
sock->so_rx_apc--;
nb = sock->so_rx_buf;
rd_cnt = 0;
}
}
sock->so_rd_cnt = rd_cnt;
/*
* Update our receive window.
*/
if (sock->so_state == TCPS_ESTABLISHED) {
i = sock->so_rx_win;
if ((i += size) > sock->so_rx_bsz)
i = sock->so_rx_bsz;
if (sock->so_rx_win <= sock->so_mss && i > sock->so_mss) {
sock->so_rx_win = i;
NutTcpStateWindowEvent(sock);
} else {
sock->so_rx_win = i;
}
}
}
return size;
}
/*!
* \brief Close TCP socket.
*
* Note, that the socket may not be immediately destroyed
* after calling this function. However, the application
* must not use the socket after this call.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
*
* \return 0 on success, -1 otherwise.
*/
int NutTcpCloseSocket(TCPSOCKET * sock)
{
/* Flush buffer first */
//@@@printf ("[%04X] Calling close\n", (u_short) sock);
NutTcpDeviceWrite(sock, 0, 0);
return NutTcpStateCloseEvent(sock);
}
/*!
* \brief Return specific code of the last error.
*
* Possible error codes (net/errno.h) are:
*
* - EWOULDBLOCK: Operation would block
* - EINPROGRESS: Operation now in progress
* - EALREADY: Operation already in progress
* - ENOTSOCK: Socket operation on non-socket
* - EDESTADDRREQ: Destination address required
* - EMSGSIZE: Message too long
* - EPROTOTYPE: Protocol wrong type for socket
* - ENOPROTOOPT: Protocol not available
* - EPROTONOSUPPORT: Protocol not supported
* - ESOCKTNOSUPPORT: Socket type not supported
* - EOPNOTSUPP: Operation not supported on socket
* - EPFNOSUPPORT: Protocol family not supported
* - EAFNOSUPPORT: Address family not supported by protocol family
* - EADDRINUSE: Address already in use
* - EADDRNOTAVAIL: Can't assign requested address
* - ENETDOWN: Network is down
* - ENETUNREACH: Network is unreachable
* - ENETRESET: Network dropped connection on reset
* - ECONNABORTED: Software caused connection abort
* - ECONNRESET: Connection reset by peer
* - ENOBUFS: No buffer space available
* - EISCONN: Socket is already connected
* - ENOTCONN: Socket is not connected
* - ESHUTDOWN: Can't send after socket shutdown
* - ETOOMANYREFS: Too many references: can't splice
* - ETIMEDOUT: Connection timed out
* - ECONNREFUSED: Connection refused
* - ELOOP: Too many levels of symbolic links
* - ENAMETOOLONG: File name too long
* - EHOSTDOWN: Host is down
* - EHOSTUNREACH: No route to host
* - ENOTEMPTY: Directory not empty
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
*
* \note Applications must not call this function to retrieve the
* error code if NutTcpCloseSocket() or NutTcpDestroySocket()
* failed.
*
* \todo Not all error codes are properly set right now. Some socket
* functions return an error without setting an error code.
*/
int NutTcpError(TCPSOCKET * sock)
{
if (sock == 0)
return ENOTSOCK;
return sock->so_last_error;
}
/*!
* \brief Read from virtual socket device.
*
* TCP sockets can be used like other Nut/OS devices. This routine
* is part of the virtual socket device driver.
*
* This function is called by the low level input routines of the
* \ref xrCrtLowio "C runtime library", using the _NUTDEVICE::dev_read
* entry.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
* \param buffer Pointer to the buffer that receives the data.
* \param size Maximum number of bytes to read.
*
* \return The number of bytes read, which may be less than the number
* of bytes specified. A return value of -1 indicates an error,
* while zero is returned in case of a timeout.
*/
int NutTcpDeviceRead(TCPSOCKET * sock, void *buffer, int size)
{
return NutTcpReceive(sock, buffer, size);
}
static int SendBuffer(TCPSOCKET * sock, CONST void *buffer, int size)
{
int rc;
int bite;
for (rc = 0; rc < size; rc += bite) {
if ((bite = NutTcpSend(sock, (u_char *) buffer + rc, size - rc)) <= 0) {
return -1;
}
}
return rc;
}
/*!
* \brief Write to a socket.
*
* TCP sockets can be used like other Nut/OS devices. This routine
* is part of the virtual socket device driver.
*
* This function is called by the low level output routines of the
* \ref xrCrtLowio "C runtime library", using the
* \ref _NUTDEVICE::dev_write entry.
*
* In contrast to NutTcpSend() this routine provides some buffering.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
* \param buf Pointer to the data to be written.
* \param size Number of bytes to write. If zero, then the output buffer
* will be flushed.
*
* \return The number of bytes written. A return value of -1 indicates
* an error.
*
*/
int NutTcpDeviceWrite(TCPSOCKET * sock, CONST void *buf, int size)
{
int rc;
u_short sz;
/* hack alert for ICCAVR */
u_char *buffer = (u_char*) buf;
/*
* Check parameters.
*/
if (sock == 0)
return -1;
if (sock->so_state != TCPS_ESTABLISHED) {
sock->so_last_error = ENOTCONN;
return -1;
}
/* Flush buffer? */
if (size == 0) {
if (sock->so_devocnt) {
if (SendBuffer(sock, sock->so_devobuf, sock->so_devocnt) < 0) {
NutHeapFree(sock->so_devobuf);
sock->so_devocnt = 0;
return -1;
}
NutHeapFree(sock->so_devobuf);
sock->so_devocnt = 0;
}
return 0;
}
/* If we don't have a buffer so far... */
if (sock->so_devocnt == 0) {
/* If new data block is bigger or equal than buffer size
* send first part of data to nic and store remaining
* bytes in buffer
*/
if ((u_short) size >= sock->so_devobsz) {
rc = size % sock->so_devobsz;
if (SendBuffer(sock, buffer, size - rc) < 0)
return -1;
buffer += size - rc;
} else
rc = size;
/* If there are some remainings bytes, allocate buffer
* and store them
*/
if (rc) {
if (!(sock->so_devobuf = NutHeapAlloc(sock->so_devobsz)))
return -1;
memcpy(sock->so_devobuf, buffer, rc);
sock->so_devocnt = rc;
}
return size;
}
/* Check if new data fully fits in output buffer */
if (sock->so_devocnt + size < sock->so_devobsz) {
memcpy(sock->so_devobuf + sock->so_devocnt, buffer, size);
sock->so_devocnt += size;
return size;
}
/* Otherwise store first bytes of new data in buffer and flush
* the buffer
*/
sz = sock->so_devobsz - sock->so_devocnt;
memcpy(sock->so_devobuf + sock->so_devocnt, buffer, sz);
buffer += sz;
if (SendBuffer(sock, sock->so_devobuf, sock->so_devobsz) < 0) {
NutHeapFree(sock->so_devobuf);
sock->so_devocnt = 0;
return -1;
}
/* If remaining data is bigger or equal than buffer size
* send first part of data to nic and later store remaining
* bytes in buffer
*/
sz = size - sz;
if (sz >= sock->so_devobsz) {
rc = size % sock->so_devobsz;
if (SendBuffer(sock, buffer, sz - rc) < 0) {
NutHeapFree(sock->so_devobuf);
sock->so_devocnt = 0;
return -1;
}
buffer += sz - rc;
} else
rc = sz;
/* If there are some remainings bytes, store them in buffer
*/
if (rc)
memcpy(sock->so_devobuf, buffer, rc);
else /* Otherwise free buffer */
NutHeapFree(sock->so_devobuf);
sock->so_devocnt = rc;
return size;
}
/*!
* \brief Driver control function.
*
* Used by the virtual device driver to modify or query device specific
* settings.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutTcpCreateSocket().
* \param cmd Requested control function. May be set to one of the
* following constants:
* - \ref IOCTL_GETFILESIZE
* - \ref IOCTL_GETINBUFCOUNT
* - \ref IOCTL_GETOUTBUFCOUNT
*
* \param param Points to a buffer that contains any data required for
* the given control function or receives data from that
* function.
* \return 0 on success, -1 otherwise.
*/
int NutTcpDeviceIOCtl(TCPSOCKET * sock, int cmd, void *param)
{
u_long *lvp = (u_long *) param;
int rc = 0;
switch (cmd) {
case IOCTL_GETFILESIZE:
case IOCTL_GETINBUFCOUNT:
*lvp = (sock->so_rx_cnt - sock->so_rd_cnt);
break;
case IOCTL_GETOUTBUFCOUNT:
*lvp = (sock->so_devocnt);
break;
default:
rc = -1;
}
return rc;
}
#endif
tcputil.c 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define TCPUTIL_C
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include <stdio.h>
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include "Common.h"
#include "XCore.h"
#include "XNutOS.h"
#include "tcputil.h"
#if defined(NUTNET)
/******************************************************************************
* *
* L O C A L D E F I N E S *
* *
******************************************************************************/
/* Limit retransmission timeout to 200ms as lower and 20secs as upper boundary */
#ifndef TCP_RTTO_MIN
#define TCP_RTTO_MIN 200
#endif
#ifndef TCP_RTTO_MAX
#define TCP_RTTO_MAX 20000
#endif
#define min(a,b) ((a>b)?b:a)
#define max(a,b) ((a>b)?a:b)
/******************************************************************************
* *
* L O C A L T Y P E D E F S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L I N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L U N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
/*
* Calculate round trip time.
*/
void NutTcpCalcRtt(TCPSOCKET * sock)
{
u_short delta;
if (sock->so_retran_time == 0)
return;
delta = (u_short) NutGetMillis() - (sock->so_retran_time & ~1);
/* According to RFC793 (or STD007), page 41, we use 0.8 for ALPHA and 2.0 for BETA. */
sock->so_rtto = min (TCP_RTTO_MAX, max(TCP_RTTO_MIN, (delta * 4 + sock->so_rtto * 8) / 10));
//@@@printf ("[%04X] new retran timeout: %u, delta: %u\n", (u_short) sock, sock->so_rtto, delta);
}
#endif
tcputil.h 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
#ifndef TCPUTIL_H
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define TCPUTIL_H
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include "XNutOS.h"
#include "sock_var.h"
#include "tcp_fsm.h"
#ifdef __cplusplus
extern "C" { /* Assume C declarations for C++ */
#endif /* __cplusplus */
/******************************************************************************
* *
* G L O B A L D E F I N E S *
* *
******************************************************************************/
/*! \brief Wraparound-safe TCP sequence number comparison, (low <= x <= high)
*
* Returns true if x is between low and high inclusive,
* false otherwise.
*/
#define SeqIsBetween(x, low, high) \
((u_long)(x - low) <= (u_long)(high - low))
/*! \brief Wraparound-safe TCP sequence number comparison, (x > low)
*
* Returns true if number x comes after low.
*
* Values between low ... low+1 - (1<<31) are in the past
* Values between low+1 ... low + (1<<31) are in the future
*/
#define SeqIsAfter(x, low) \
((long)(low - x) < 0)
/******************************************************************************
* *
* S T R U C T U R E D E F I N I T I O N S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* G L O B A L V A R I A B L E S - N O I N I T I A L I Z E R S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* G L O B A L V A R I A B L E S - I N I T I A L I Z E R S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
void NutTcpCalcRtt(TCPSOCKET * sock);
#ifdef __cplusplus
} /* End of extern "C" { */
#endif /* __cplusplus */
#endif
udp.h 。。。。。。。。。。。。。。。。。。。。。。。。。。。。
#ifndef UDP_H
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define UDP_H
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include "netbuf.h"
#ifdef __cplusplus
extern "C" { /* Assume C declarations for C++ */
#endif /* __cplusplus */
/******************************************************************************
* *
* G L O B A L D E F I N E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* S T R U C T U R E D E F I N I T I O N S *
* *
******************************************************************************/
/*!
* \typedef UDPHDR
* \brief UDP protocol header type.
*/
typedef struct udphdr {
u_short uh_sport; /*!< \brief Source port */
u_short uh_dport; /*!< \brief Destination port */
u_short uh_ulen; /*!< \brief UDP length */
u_short uh_sum; /*!< \brief UDP checksum */
} UDPHDR;
/******************************************************************************
* *
* G L O B A L V A R I A B L E S - N O I N I T I A L I Z E R S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* G L O B A L V A R I A B L E S - I N I T I A L I Z E R S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
#ifdef __cplusplus
} /* End of extern "C" { */
#endif /* __cplusplus */
#endif
udpin.c 。。。。。。。。。。。。。。。。。。。。。。。。。。
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define UDPIN_C
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include "Common.h"
#include "XCore.h"
#include "XNutOS.h"
#include "udp.h"
#include "socket.h"
#include "ip_icmp.h"
#include "icmp.h"
#if defined(NUTNET)
/******************************************************************************
* *
* L O C A L D E F I N E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L T Y P E D E F S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L I N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L U N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
/*!
* \brief Handle incoming UDP packets.
*
* \note This routine is called by the IP layer on
* incoming UDP packets. Applications typically do
* not call this function.
*
* \param nb Network buffer structure containing the UDP packet.
*/
int NutUdpInput(NUTDEVICE * dev, NETBUF * nb)
{
UDPHDR *uh;
UDPSOCKET *sock;
uh = (UDPHDR *) nb->nb_tp.vp;
/* Make sure that the datagram contains a full header. */
if (uh == NULL || nb->nb_tp.sz < sizeof(UDPHDR)) {
NutNetBufFree(nb);
return 0;
}
nb->nb_ap.sz = nb->nb_tp.sz - sizeof(UDPHDR);
nb->nb_tp.sz = sizeof(UDPHDR);
if (nb->nb_ap.sz) {
nb->nb_ap.vp = uh + 1;
}
/*
* Find a port. If none exists and if this datagram hasn't been
* broadcasted, return an ICMP unreachable.
*/
if ((sock = NutUdpFindSocket(uh->uh_dport)) == 0) {
if ((nb->nb_flags & NBAF_UNICAST) == 0 ||
NutIcmpResponse(ICMP_UNREACH, ICMP_UNREACH_PORT, 0, nb) == 0) {
NutNetBufFree(nb);
}
return 0;
}
/* if buffer size is defined, use packet queue */
if (sock->so_rx_bsz) {
/* New packet fits into the buffer? */
if (sock->so_rx_cnt + nb->nb_ap.sz > sock->so_rx_bsz) {
/* No, so discard it */
NutNetBufFree(nb);
return 0;
} else {
/* if a first packet is already in the queue, find the end
* and add the new packet */
if (sock->so_rx_nb) {
NETBUF *snb;
for (snb = sock->so_rx_nb; snb->nb_next != 0; snb = snb->nb_next);
snb->nb_next = nb;
} else
sock->so_rx_nb = nb;
/* increment input buffer count */
sock->so_rx_cnt += nb->nb_ap.sz;
};
} else { /* no packet queue */
/* if a packet is still buffered, discard it */
if (sock->so_rx_nb) {
NutNetBufFree(sock->so_rx_nb);
}
sock->so_rx_nb = nb;
sock->so_rx_cnt = nb->nb_ap.sz; /* set input buffer count to size of new packet */
};
/* post the event only, if one thread is waiting */
if (sock->so_rx_rdy)
NutEventPost(&sock->so_rx_rdy);
return 0;
}
#endif
udpout.c 。。。。。。。。。。。。。。。。。。。。。。。
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define UDPOUT_C
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include "Common.h"
#include "XCore.h"
#include "in.h"
#include "ip.h"
#include "udp.h"
#include "ipcsum.h"
#include "route.h"
#include "socket.h"
#if defined(NUTNET)
/******************************************************************************
* *
* L O C A L D E F I N E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L T Y P E D E F S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L I N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L U N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
/*!
* \brief Send a UDP packet.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutUdpCreateSocket().
* \param daddr IP address of the remote host in network byte order.
* \param port Remote port number in host byte order.
* \param nb Network buffer structure containing the datagram.
* This buffer will be released if the function returns
* an error.
*
* \note Applications typically do not call this function but
* use the UDP socket interface.
*
* \return 0 on success, -1 otherwise.
*/
int NutUdpOutput(UDPSOCKET * sock, u_long daddr, u_short port, NETBUF * nb)
{
u_long saddr;
u_long csum;
UDPHDR *uh;
NUTDEVICE *dev;
IFNET *nif;
if ((nb = NutNetBufAlloc(nb, NBAF_TRANSPORT, sizeof(UDPHDR))) == 0)
return -1;
uh = nb->nb_tp.vp;
uh->uh_sport = sock->so_local_port;
uh->uh_dport = htons(port);
uh->uh_ulen = htons((u_short)(nb->nb_tp.sz + nb->nb_ap.sz));
/*
* Get local address for this destination.
*/
if ((dev = NutIpRouteQuery(daddr, &saddr)) != 0) {
nif = dev->dev_icb;
saddr = nif->if_local_ip;
} else
saddr = 0;
uh->uh_sum = 0;
csum = NutIpPseudoChkSumPartial(saddr, daddr, IPPROTO_UDP, uh->uh_ulen);
csum = NutIpChkSumPartial(csum, uh, sizeof(UDPHDR));
uh->uh_sum = NutIpChkSum(csum, nb->nb_ap.vp, nb->nb_ap.sz);
return NutIpOutput(IPPROTO_UDP, daddr, nb);
}
#endif
udpsock.c 。。。。。。。。。。。。。。。。。。。。。。。。
/******************************************************************************
* *
* M O D U L E D E F I N E *
* *
******************************************************************************/
#define UDPSOCKET_C
/******************************************************************************
* *
* C O M P I L E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include <string.h>
/******************************************************************************
* *
* U S E R D E F I N E D I N C L U D E F I L E S *
* *
******************************************************************************/
#include "Common.h"
#include "XCore.h"
#include "XNutOS.h"
#include "ip.h"
#include "udp.h"
#include "in.h"
#include "socket.h"
#include "errno.h"
#if defined(NUTNET)
/******************************************************************************
* *
* L O C A L D E F I N E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L T Y P E D E F S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L F U N C T I O N P R O T O T Y P E S *
* *
******************************************************************************/
/* None */
/******************************************************************************
* *
* L O C A L I N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
static u_short last_local_port = 4096; /* Unassigned local port. */
/******************************************************************************
* *
* L O C A L U N I T I A L I Z E D D A T A D E F I N I T I O N S *
* *
******************************************************************************/
UDPSOCKET *udpSocketList; /*!< Global linked list of all UDP sockets. */
/*!
* \brief Create a UDP socket.
*
* \param port Server applications provide the local port number
* with this parameter. Client applications should
* pass zero.
*
* \return Socket descriptor of the newly created UDP socket or
* 0 if there is not enough memory left.
*
*/
UDPSOCKET *NutUdpCreateSocket(u_short port)
{
UDPSOCKET *sock;
if (port == 0) {
do {
if (++last_local_port == 0)
last_local_port = 4096;
port = htons(last_local_port);
sock = udpSocketList;
while (sock) {
if (sock->so_local_port == port)
break;
sock = sock->so_next;
}
} while (sock);
port = last_local_port;
}
if ((sock = NutHeapAllocClear(sizeof(UDPSOCKET))) != 0) {
sock->so_local_port = htons(port);
sock->so_next = udpSocketList;
udpSocketList = sock;
}
return sock;
}
/*!
* \brief Send a UDP datagram.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutUdpCreateSocket().
* \param addr IP address of the remote host in network byte order.
* \param port Remote port number in host byte order.
* \param data Pointer to a buffer containing the data to send.
* \param len Number of bytes to be sent.
*
* \return 0 on success, -1 otherwise. The specific error code
* can be retrieved by calling NutUdpError().
*/
int NutUdpSendTo(UDPSOCKET * sock, u_long addr, u_short port, void *data, int len)
{
int rc;
NETBUF *nb;
#ifdef NUT_UDP_ICMP_SUPPORT
if (sock->so_last_error)
return -1;
#endif
if ((nb = NutNetBufAlloc(0, NBAF_APPLICATION, len)) == 0) {
sock->so_last_error = ENOMEM;
return -1;
}
memcpy(nb->nb_ap.vp, data, len);
/* Bugfix by Ralph Mason. We should not free the NETBUF in case of an error. */
if ((rc = NutUdpOutput(sock, addr, port, nb)) == 0)
NutNetBufFree(nb);
return rc;
}
/*!
* \brief Receive a UDP datagram.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutUdpCreateSocket().
* \param addr IP address of the remote host in network byte order.
* \param port Remote port number in host byte order.
* \param data Pointer to the buffer that receives the data.
* \param size Size of the buffer that receives the data.
* \param timeout Maximum number of milliseconds to wait.
*
* \return The number of bytes received, if successful. The return
* value < 0 indicates an error. The specific error code
* can be retrieved by calling NutUdpError().
*
* \note Timeout is limited to the granularity of the system timer.
*/
/* @@@ 2003-10-24: modified by OS for udp packet queue */
int NutUdpReceiveFrom(UDPSOCKET * sock, u_long * addr, u_short * port, void *data, int size, u_long timeout)
{
IPHDR *ip;
UDPHDR *uh;
NETBUF *nb;
#ifdef NUT_UDP_ICMP_SUPPORT
/* The ICMP handler might have set an error condition. */
if (sock->so_last_error)
return -1;
#endif
if (sock->so_rx_nb == 0)
NutEventWait(&sock->so_rx_rdy, timeout);
#ifdef NUT_UDP_ICMP_SUPPORT
/* An ICMP message might have posted the rx event. So check again */
if (sock->so_last_error)
return -1;
#endif
if ((nb = sock->so_rx_nb) == 0)
return 0;
/* forward the queue's head to the next packet */
sock->so_rx_nb = nb->nb_next;
ip = nb->nb_nw.vp;
*addr = ip->ip_src;
uh = nb->nb_tp.vp;
*port = htons(uh->uh_sport);
if (size > nb->nb_ap.sz)
size = nb->nb_ap.sz;
sock->so_rx_cnt -= nb->nb_ap.sz; /* decrement input buffer count */
memcpy(data, nb->nb_ap.vp, size);
NutNetBufFree(nb);
return size;
}
/*!
* \brief Close UDP socket.
*
* The memory occupied by the socket is immediately released
* after calling this function. The application must not use
* the socket after this call.
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutUdpCreateSocket().
*
* \return 0 on success, -1 otherwise.
*/
/* @@@ 2003-10-24: modified by OS for udp packet queue */
int NutUdpDestroySocket(UDPSOCKET * sock)
{
UDPSOCKET *sp;
UDPSOCKET **spp;
int rc = -1;
NETBUF *nb;
spp = &udpSocketList;
sp = udpSocketList;
while (sp) {
if (sp == sock) {
*spp = sock->so_next;
/* packets may have arrived that the application
did not read before closing the socket. */
while ((nb = sock->so_rx_nb) != 0) {
sock->so_rx_nb = nb->nb_next;
NutNetBufFree(nb);
}
NutHeapFree(sock);
rc = 0;
break;
}
spp = &sp->so_next;
sp = sp->so_next;
}
return rc;
}
/*!
* \brief Return specific code of the last error and the IP address / port of
* the host to which the communication failed
*
* Possible error codes are:
* - ENOTSOCK: Socket operation on non-socket
* - EMSGSIZE: Message too long
* - ENOPROTOOPT: Protocol not available
* - EOPNOTSUPP: Operation not supported on socket
* - ENETUNREACH: Network is unreachable
* - ECONNREFUSED: Connection refused
* - EHOSTDOWN: Host is down
* - EHOSTUNREACH: No route to host
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutUdpCreateSocket().
* \param addr IP address of the remote host in network byte order.
* \param port Remote port number in host byte order.
*
* \todo Not all error codes are properly set right now. Some socket
* functions return an error without setting an error code.
*/
int NutUdpError(UDPSOCKET * sock, u_long * addr, u_short * port)
{
int rc;
if (sock == 0) {
addr = 0;
port = 0;
return ENOTSOCK;
}
if (sock->so_last_error) {
rc = sock->so_last_error;
*port = ntohs(sock->so_remote_port);
*addr = sock->so_remote_addr;
sock->so_last_error = 0;
sock->so_remote_port = 0;
sock->so_remote_addr = 0;
return rc;
}
return 0;
}
/*!
* \brief Find a matching socket.
*
* Loop through all sockets and find a matching one.
*
* \note Applications typically do not need to call this function.
*
* \param port Local port number.
*
* \return Socket descriptor.
*/
UDPSOCKET *NutUdpFindSocket(u_short port)
{
UDPSOCKET *sp;
UDPSOCKET *sock = 0;
for (sp = udpSocketList; sp; sp = sp->so_next) {
if (sp->so_local_port == port) {
sock = sp;
break;
}
}
return sock;
}
/*!
* \brief Set value of a UDP socket option.
*
* The following values can be set:
*
* - #SO_RCVBUF Socket input buffer size (#u_short).
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutUdpCreateSocket().
* \param optname Option to set.
* \param optval Pointer to the value.
* \param optlen Length of the value.
* \return 0 on success, -1 otherwise.
*/
int NutUdpSetSockOpt(UDPSOCKET * sock, int optname, CONST void *optval, int optlen)
{
int rc = -1;
if (sock == 0)
return -1;
switch (optname) {
case SO_RCVBUF:
if (optval != 0 && optlen == sizeof(u_short)) {
sock->so_rx_bsz = *((u_short *) optval);
rc = 0;
}
break;
default:
/* sock->so_last_error = ENOPROTOOPT; */
break;
}
return rc;
}
/*!
* \brief Get a UDP socket option value.
*
* The following values can be set:
*
* - #SO_RCVBUF Socket input buffer size (#u_short).
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutUdpCreateSocket().
* \param optname Option to get.
* \param optval Points to a buffer receiving the value.
* \param optlen Length of the value buffer.
*
* \return 0 on success, -1 otherwise.
*/
int NutUdpGetSockOpt(UDPSOCKET * sock, int optname, void *optval, int optlen)
{
int rc = -1;
if (sock == 0)
return -1;
switch (optname) {
case SO_RCVBUF:
if (optval != 0 && optlen == sizeof(u_short)) {
*((u_short *) optval) = sock->so_rx_bsz;
rc = 0;
}
break;
default:
/* sock->so_last_error = ENOPROTOOPT; */
break;
}
return rc;
}
/*!
* \brief Set a UDP socket error.
*
* This function should only be used together (and from) the icmp input routine
*
* The following values can be set:
*
* - #EHOSTUNREACH Host is unreachable
*
* \param sock Socket descriptor. This pointer must have been
* retrieved by calling NutUdpCreateSocket().
* \param remote_addr remote ip address in network byte order
* \param remote_port remote port in network byte order
* \param error Error number.
*
* \return 0 on success, -1 otherwise.
*/
int NutUdpSetSocketError(UDPSOCKET * sock, u_long remote_addr, u_short remote_port, u_short error)
{
if (sock == 0)
return -1;
sock->so_remote_addr = remote_addr;
sock->so_remote_port = remote_port;
sock->so_last_error = error;
/* post the event only, if a thread is waiting */
if (sock->so_rx_rdy)
NutEventPost(&sock->so_rx_rdy);
return 0;
}
#endif