Phenelo
Phenelo

Reputation: 105

SCTP: What should be the sctp_status.sstate value of an SCTP socket after succesful connect() call?

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).

Upvotes: 1

Views: 557

Answers (1)

Alexander Zinovyev
Alexander Zinovyev

Reputation: 338

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.

Upvotes: 1

Related Questions