Replacing a connect() call with a sendto() with MSG_FASTOPEN doesn't initiate a TCP Fast Open connection. There's no TCP option for fast open nor data in the SYN packet, it just does a regular TCP SYN.
A socket must have the TCP_FASTOPEN_CONNECT option set on it for the sendto() call to honor MSG_FASTOPEN.
After the socket() call, enable TCP_FASTOPEN_CONNECT
int sock = socket(AF_INET6, SOCK_STREAM, 0);
const int yes = 1;
setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &yes, sizeof(yes));
This is because sendto was never required to return a failure for unrecognized options, so callers in old code might have been inadvertently setting the MSG_FASTOPEN bit by accident, resulting in erroneous double-writes. Setting TCP_FASTOPEN_CONNECT informs the kernel that your process is willing to attempt TCP fast-open.
Related
I am trying to establish TCP communication between my PC and LWIP MCU. LWIP UDP and ICMP seems that work correctly. Note that LWIP works as TCP sender and PC-Python socket based script as receiver. The first cycle connection accept seems to be acknowledged correctly. However, during TCP packet sending it seems like the PC ACK are not correct - LWIP retransmits the packet.
LWIP code sender's side looks like this (obvious LWIP_Process function to handle received packets and refresh time, the initialization of MCU is not
shown....)
static char first_pld[32] = {"Thank you people"};
/* -----------Set network id ----------*/
IP4_ADDR(&ipaddr, 192u, 168u, 100u, 2u);
IP4_ADDR(&ipaddr_pc, 192u, 168u, 100u, 10u);
/* -----------Force ARP discover routine ----------*/
LWIP_arp_query(&ipaddr_pc);
LWIP_Process();
/* -----------Start TCP communication ----------*/
MyTCP = tcp_new();
LWIP_Process();
tcp_bind(MyTCP, &ipaddr, 504);
LWIP_Process();
tcp_connect(MyTCP, &ipaddr_pc, 504, MyConnectedFn);
LWIP_Process();
tcp_sent(MyTCP, MySentFn);
LWIP_Process();
printf("TCP available to send: %04d\r\n", tcp_sndbuf(MyTCP));
LWIP_Process();
tcp_write(MyTCP, first_pld, strlen(first_pld), 0);
LWIP_Process();
tcp_output(MyTCP);
LWIP_Process();
while (1) {
LWIP_Process();
}
}
Python receiver is:
import socket
import sys
import time
buff_size = 128
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind the socket to the port
modbus_port = 504
this_addres = (b'192.168.100.10', modbus_port)
sock.bind(this_addres)
# Listen for incoming connections
sock.listen(1)
connection_status = 0
while connection_status == 0:
# Wait for a connection
print('waiting for a connection')
connection, client_address = sock.accept()
connection_status = 1
print("Connected to", client_address)
while 1:
payload = connection.recv(buff_size)
print(payload)
The wireshark log shows ARP session, accepting the TCP communication, but
TCP packet send does not look correct - LWIP re-sends..
Wireshark Log Picture https://ibb.co/Q9fbxZ9
Any helpful tip please ? Currently I am trying to find similar solved trouble..
I found the solution how to fix that:
Be careful when creating LWIP netinterface.c/h files.
Because I wanted to omptimize length of passed buffers (of
frames) I created function to schrink arrays including (sometimes)
huge amount of '\0' characters. I did this because my MAC PHY device
in receive mode cannot return array of packet smaller than 60 bytes,
so for example ARP frame (normally length 42) is filled with nulles
in the end.
However, '\0' may be also value of the packet in the end (especially
when TCP ACK). I automatically threw this nulls and LWIP could not
handle this as valid packet. So now, I pass whole array of packets
and let LWIP handle it on its own...
In the TCP three way handshake connection procedure does the client (the one who initiated the connection) send to the server any data payload joined with the ACK packet in the third step ?
The last ACK in the TCP handshake can already contain a payload. But, this is usually not done since the application first calls connect and then will either wait for the server to reply or send its first data. Since the kernel does not know what the application will do next it will already send out the ACK within the connect so that the server knows as fast as possible that the connection is established.
Depending on your OS it might be possible to change this behavior and send the ACK together with the first data. In Linux this can be achieved by explicitly disabling quick ack before connecting:
int off = 0;
setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, &off, sizeof(off));
connect(fd,...)
I have a XPORT (TCP/IP serial tunnel device) connected to my microcontroller (PIC18), this way I can send serial messages which are transformed into TCP/IP packages. I've enabled TCP/IP packaging, so that all characters will be put in one TCP/IP package until nothing is received for a short period of time.
I've succesfully sent a HTTP GET request through the tcp/ip tunnel.
Though when I try to send a HTTP POST request, I either get a "400 Bad request" or my apache server crashes...
I think this behaviour is because of the line-ends not being, "right".
My code:
Delay1KTCYx(160);
xportSendTextNoLine("C192.168.200.18/80\n");//Manual connect to server (xport command)
Delay1KTCYx(160);//Wait for TCP/IP packaging.
xportSendTextNoLine("POST /debug.php HTTP/1.1");
xportSend(0x0D);//Carriage return.
xportSend(0x0A);//New line
xportSendTextNoLine("Host: 192.168.200.18");
xportSend(0x0D);//Carriage return.
xportSend(0x0A);//New line
xportSendTextNoLine("Content-Type: application/x-www-form-urlencoded");
xportSend(0x0D);//Carriage return.
xportSend(0x0A);//New line
xportSend(0x0D);//Carriage return.
xportSend(0x0A);//New line
xportSendTextNoLine("Grower=2&SiteId=99&Time=2015021108291700&Usertag=testuser&Action=0");
xportSend(0x0D);//Carriage return.
xportSend(0x0A);//New line
xportSend(0x0D);//Carriage return.
xportSend(0x0A);//New line
Delay1KTCYx(160);//Wait for TCP/IP packaging.
Wireshark output (simplified):
>POST /debug.php HTTP/1.1[0x0D][0x00][0x0A]
Host: 192.168.200.18[0x0D][0x00][0x0A]
etc.
<HTTP/1.1 400 Bad Request
Wireshark output (complete):
It seems that a 0x0D (Carriage Return) to the Lantronix Xport causes a 0x00 to be sent after it. Which would lead to the webserver not being able to interpret the request.
I've just sent a tech support question to the Lantronix support, but I'd also like to know if anyone can tell me if:
The Lantronix XPORT sends a 0x00 after a 0x0D?
My HTTP POST request is wrong in another way? (Missing content length?)
Verifying the output of the microcontroller:
Settings of the XPORT are the same as TERMINAL and should be the same as the MCU (otherwise it wouldn't receive the same):
MCU serial setup:
RCSTA2bits.SPEN = 1; //Serial port enable
TRISGbits.TRISG2 = 1; //RG2 input (RX)
TRISGbits.TRISG1 = 0; //RG1 output (TX)
ANCON2bits.ANSEL18= 0; //DIGITAL!
ANCON2bits.ANSEL19= 0;
IPR3bits.RC2IP = 1;//High-priority Rx interrupts
PIE3bits.RC2IE = 1;//Enable Rx interrupt
TXSTA2 = 0b00100000;
RCSTA2 = 0b10010000;
BAUDCON2 = 0b01000000;//Receive operation active.
SPBRG2 = 12; // 9615 (0.16% error) (8Mhz)
I have finally found the solution!
After some (logical) thinking I found that it would be the problem of the XPORT.
But what could be the problem? I disabled all features of which I thought that might interfere (even though I thought this would be unlogical to cause this kind of error) and after saving, the POST request just popped up nicely in my server...
See the settings here (might want to open it in a new tab):
in networking what type of socket should i use to guarantee that the receiving application has received all of the data in order?
You will need to use the TCP socket (sometimes called reliable). A sample code of creating such socket:
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Iam trying to create an iterative server based on datagram sockets (UDP).
It calls connect to the first client which it gets from the first recvfrom() call (yes I know this is no real connect).
After having served this client, I disconnect the UDP socket (calling connect with AF_UNSPEC)
Then I call recvfrom() to get the first packet from the next client.
Now the problem is, that the call of recvfrom() in the second iteration of the loop is returning 0. My clients never send empty packets, so what could be going on.
This is what Iam doing (pseudocode):
s = socket(PF_INET, SOCK_DGRAM, 0)
bind(s)
for(;;)
{
recvfrom(s, header, &client_address) // get first packet from client
connect(s,client_address) // connect to this client
serve_client(s);
connect(s, AF_UNSPEC); // disconnect, ready to serve next client
}
EDIT: I found the bug in my client accidently sending an empty packet.
Now my problem is how to make the client wait to get served instead of sending a request into nowhere (server is connected to another client and doesn't serve any other client yet).
connect() is really completely unnecessary on SOCK_DGRAM.
Calling connect does not stop you receiving packets from other hosts, nor does it stop you sending them. Just don't bother, it's not really helpful.
CORRECTION: yes, apparently it does stop you receiving packets from other hosts. But doing this in a server is a bit silly because any other clients would be locked out while you were connect()ed to one. Also you'll still need to catch "chaff" which float around. There are probably some race conditions associated with connect() on a DGRAM socket - what happens if you call connect and packets from other hosts are already in the buffer?
Also, 0 is a valid return value from recvfrom(), as empty (no data) packets are valid and can exist (indeed, people often use them). So you can't check whether something has succeeded that way.
In all likelihood, a zero byte packet was in the queue already.
Your protocol should be engineered to minimise the chance of an errant datagram being misinterpreted; for this reason I'd suggest you don't use empty datagrams, and use a magic number instead.
UDP applications MUST be capable of recognising "chaff" packets and dropping them; they will turn up sooner or later.
man connect:
...
If the initiating socket is not connection-mode, then connect()
shall set the socket’s peer address, and no connection is made.
For SOCK_DGRAM sockets, the peer address identifies where all
datagrams are sent on subsequent send() functions, and limits
the remote sender for subsequent recv() functions. If address
is a null address for the protocol, the socket’s peer address
shall be reset.
...
Just a correction in case anyone stumbples across this like I did. To disconnect connect() needs to be called with the sa_family member of sockaddr set to AF_UNSPEC. Not just passed AF_UNSPEC.