1

I'm trying to connect to a remote peer (which I don't have directory access other than connecting to it via socket and ping) via SCTP. Assuming that I have connected succesfully, what should be the value of my sctp_status.sstate if I try calling getsocktopt()? Mine is SCTP_COOKIE_ECHOED(3) according to sctp.h. Is it correct? Shouldn't it be SCTP_ESTABLISHED?

Because I tried sending message to the remote peer with this code:

ret = sctp_sendmsg (connSock, (void *) data, (size_t) strlen (data), (struct sockaddr *) &servaddr, sizeof (servaddr), 46, 0, 0, 0, 0);

It returned the number of bytes I tried sending. Then when I tried catching if there's any response:

ret = sctp_recvmsg (connSock, (void *) reply, sizeof (reply), NULL,
          NULL, NULL, &flags);

It returns -1 with errno of ECONNRESET(104). What are the possible mistakes in my code, or maybe in my flow? Did I miss something?

Thanks in advance for answering. Will gladly appreciate that. :)

Update: Here down below is my client code in connecting to the remote peer. It's actually a node addon for me to use since SCTP is not fully supported in node. Using lksctp-tools package to include the headers.

#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <signal.h>
#define MAX_BUFFER 1024

int connSock = 0;

int connect(char host[], int port, char remote_host[], int remote_port, int timeout) {

  int ret, flags;
  fd_set rset, wset;
  struct sockaddr_in servaddr;
  struct sockaddr_in locaddr;
  struct sctp_initmsg initmsg;
  struct timeval tval;
  struct sctp_status status;
  socklen_t opt_len;

  errno = 0;

  connSock = socket (AF_INET, SOCK_STREAM, IPPROTO_SCTP);
  flags = fcntl(connSock, F_GETFL, 0);
  fcntl(connSock, F_SETFL, flags | O_NONBLOCK);

  if (connSock == -1)
  {
      return (-1);
  }

  memset(&locaddr, 0, sizeof(locaddr));
  locaddr.sin_family = AF_INET;
  locaddr.sin_port = htons(port);
  locaddr.sin_addr.s_addr = inet_addr(host);

  ret = bind(connSock, (struct sockaddr *)&locaddr, sizeof(locaddr));

  if (ret == -1)
  {
      return (-1);
  }

  memset (&initmsg, 0, sizeof (initmsg));
  initmsg.sinit_num_ostreams = 5;
  initmsg.sinit_max_instreams = 5;
  initmsg.sinit_max_attempts = 10;
  ret = setsockopt(connSock, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg));

  if (ret == -1)
  {
      return (-1);
  }

  memset (&servaddr, 0, sizeof (servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons (remote_port);
  servaddr.sin_addr.s_addr = inet_addr (remote_host);

  if((ret = connect (connSock, (struct sockaddr *) &servaddr, sizeof (servaddr))) < 0)
      if (errno != EINPROGRESS)
          return (-1);

  if (ret == 0) {
      fcntl(connSock, F_SETFL, flags);
      return 0;
  }

  FD_ZERO(&rset);
  FD_SET(connSock, &rset);
  wset = rset;
  tval.tv_sec = timeout;
  tval.tv_usec = 0;

  ret = select(connSock+1, &rset, &wset, NULL, timeout ? &tval : NULL);

  if (ret == 0) {
      close(connSock);
      errno = ETIMEDOUT;
      return(-1);
  }
  else if (ret < 0) {
      return(-1);
  }

  fcntl(connSock, F_SETFL, flags);

  opt_len = (socklen_t) sizeof(struct sctp_status);
  getsockopt(connSock, IPPROTO_SCTP, SCTP_STATUS, &status, &opt_len);

  printf ("assoc id  = %d\n", status.sstat_assoc_id);
  printf ("state     = %d\n", status.sstat_state);
  printf ("instrms   = %d\n", status.sstat_instrms);
  printf ("outstrms  = %d\n", status.sstat_outstrms);

  return 0;
}

int sendMessage(char remote_host[], int remote_port, char data[]) {

  int ret, flags;
  struct sockaddr_in servaddr;
  char reply[1024];

  errno = 0;

  memset (&servaddr, 0, sizeof (servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons (remote_port);
  servaddr.sin_addr.s_addr = inet_addr (remote_host);

  printf("\nSending %s (%li bytes)", data, strlen(data));

  ret = sctp_sendmsg (connSock, (void *) data, (size_t) strlen (data),
          (struct sockaddr *) &servaddr, sizeof (servaddr), 46, 0, 0, 0, 0);

  if (ret == -1)
  {
    printf("\nError sending errno(%d)", errno);
    return -1;
  }
  else {
    ret = sctp_recvmsg (connSock, (void *) reply, sizeof (reply), NULL,
          NULL, NULL, &flags);

    if (ret == -1)
    {
      printf("\nError receiving errno(%d)", errno);
      return -1;
    }
    else {
      printf("\nServer replied with %s", reply);
      return 0;
    }
  }
}

int getSocket() {

  return connSock;
}

I don't know if there's anything significant I need to set first before connecting that I missed out. I got the snippet from different sources so it's quite messy.

Another update, here's the tshark log of that code when executed:

3336.919408  local  -> remote SCTP 82 INIT
3337.006690  remote -> local  SCTP 810 INIT_ACK
3337.006727  local  -> remote SCTP 774 COOKIE_ECHO
3337.085390  remote -> local  SCTP 50 COOKIE_ACK
3337.086650  local  -> remote SCTP 94 DATA
3337.087277  remote -> local  SCTP 58 ABORT
3337.165266  remote -> local  SCTP 50 ABORT

Detailed tshark log of this here.

Looks like the remote sent its COOKIE_ACK chunk but my client failed to set its state to ESTABLISHED (I double checked the sstate value of 3 here).

Phenelo
  • 95
  • 1
  • 8

1 Answers1

1

If the association setup processes completed the state should be SCTP_ESTABLISHED. SCTP_COOKIE_ECHOED indicated that association has not completely established. It means that originating side (your localhost in this case) has sent (once or several times) COOKIE_ECHO chunk which has not been acknowledged by COOKIE_ACK from remote end.

You can send messages in this state (SCTP will simply buffer it until it get COOKIE_ACK and resend it later on).

It is hard to say what went wrong based on information you provided. At this stage it is probably will be worth diving into wireshark trace, to see what remote side is replying on your COOKIE_ECHO.

Also if you can share your client/server side code that might help to identify the root cause.

UPDATE #1: It should be also noted that application can abort association them self (e.g. if this association is not configured on that server). If you trying to connect to the random server (rather than your specific one) that is quite possible and actually makes sense in your case. In this case state of association on your side is COOKIE_ECHOED because COOKIE_ACK has not arrived yet (just a race condition). As I said previously SCTP happily accepts your data in this state and just buffers it until it receives COOKIE_ACK. SCTP on remote side sends COOKIE_ACK straight away, even before the application received execution control in accept(). If application decided to terminate the association in ungraceful way, it will send ABORT (that is your first ABORT in wireshark trace). Your side has not received this ABORT yet and sends DATA chunk. Since remote side considers this association as already terminated it cannot process DATA chunk, so it treats it as out of the blue (see RFC 4960 chapter 8.4) and sends another ABORT with t-bit set to 1. I guess this is what happened in your case. You can confirm it easily just by looking into wireshark trace.

  • Hey thanks for answering. I see, that's why sctp_sendmsg is returning those bytes. I could share the my code for this (client) after I clean it and maybe edit my question. And I tried installing Wireshark here in my machine but I think the SSH options form is bugged because whenever I try to load my private key, the file input textbox goes red which in result grays out the Start button. There must be some bug in parsing the file path and I've yet to find the location of Wireshark preferences in this machine (OSX). I'll come back for progress. Thanks. :) – Phenelo Apr 27 '17 at 01:57
  • Updated my question. Thanks. :) – Phenelo Apr 27 '17 at 02:19
  • There is something odd going on. As far as remote side concerns it does not matter what is the state of your localhost. If remote side has acknowledged COOKIE_ECHO by COOKIE_ACK it has to consider association as active and therefore process incoming DATA chunks. Please provide mode details from wireshark trace. Particularly SCTP details from INIT/INIT_ACK/COOKIE_ECHO/COOKIE_ACK/DATA/ABORTs, including ports, verification tags, abort reasons and T-bit value from the ABORT chunks. Btw, what are the OS on local and remote sides? – Alexander Zinovyev Apr 27 '17 at 08:20
  • Hi. It's my first time using tshark so Im'ma find out how to do that. As for the OS, I can only say mine which is Debian Linux because other party is handling the remote side so I can't give you anymore information about that. To be complete, I've installed lksctp-tools and libsctp-dev on the server (Debian) to compile this code and add it as a node addon. I'll come back with the tshark trace in hand. Thanks. – Phenelo Apr 27 '17 at 09:12
  • Updated my question. [Here](https://pastebin.com/5uHmH4CE) is the tshark detailed trace so you can check it right away. Thanks. – Phenelo Apr 27 '17 at 10:15
  • Your wireshark trace confirms my assumption. You can see there were User initiated ABORT, which means that application on remote side decided to terminate the association in ungraceful way. I guess now you need to find a way how to configure such association on remote end. – Alexander Zinovyev Apr 27 '17 at 10:31
  • It makes sense. I don't know firstly how the remote handles the data I'm sending because I'm still at testing phase. But in my design, I just first have to create an association and just listen for this remote server to send data. With the test data I sent, the remote server must've not acknowledged it in some way and terminated my connection. A follow-up question. Even if I won't send message, if the association by my end is not configured on the remote server to accept, there is a chance it will be aborted right? Cause I can't see anything to set in my association except for initmsg. – Phenelo Apr 27 '17 at 10:47
  • It does not matter if you send your data or not. It is not the data that cause the abort, remote side just does not like your localhost for some reasons (I guess because of misconfiguration). You either need to have a look at remote application source code and figure out what it is doing after accept() or contact remote side vendor in order to ask what should be done in order to let server accept your association. P.S. hopefully that can be considered as answer on your original question. – Alexander Zinovyev Apr 27 '17 at 10:57
  • I see. Thanks a bunch for your answers, sorry to drag this a bit. I guess I'll contact the remote party about this if I can't make the association work. Accepting your answer here. *clickadu* Thanks again. – Phenelo Apr 27 '17 at 11:25