SCTP: Каким должно быть значение sctp_status.sstate сокета SCTP после успешного вызова connect()?

Я пытаюсь подключиться к удаленному узлу (который у меня нет доступа к каталогу, кроме подключения к нему через сокет и пинг) через SCTP. Предполагая, что я успешно подключился, каким должно быть значение моего sctp_status.sstate, если я попытаюсь вызвать getsocktopt()? Мой - SCTP_COOKIE_ECHOED(3) в соответствии с sctp.h. Это правильно? Разве это не должно быть SCTP_ESTABLISHED?

Потому что я попытался отправить сообщение удаленному узлу с этим кодом:

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

Он вернул количество байтов, которые я пытался отправить. Затем, когда я попытался поймать, есть ли ответ:

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

Возвращает -1 с ошибкой ECONNRESET(104). Каковы возможные ошибки в моем коде, или, возможно, в моем потоке? Я что-то пропустил?

Заранее спасибо за ответ. С удовольствием это оценит.:)

Обновление: здесь внизу мой код клиента при подключении к удаленному узлу. На самом деле это аддон узла для меня, так как SCTP не полностью поддерживается в узле. Использование пакета lksctp-tools для включения заголовков.

#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;
}

Я не знаю, есть ли что-то важное, что мне нужно установить перед подключением, которое я пропустил. Я получил фрагмент из разных источников, так что он довольно грязный.

Еще одно обновление, вот журнал tshark этого кода при выполнении:

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

Подробный журнал об этом здесь.

Похоже, что удаленный отправил свой кусок COOKIE_ACK, но мой клиент не смог установить его состояние в ESTABLISHED (я дважды проверил здесь значение sstate, равное 3).

1 ответ

Решение

Если процессы установки ассоциации завершены, состояние должно быть SCTP_ESTABLISHED. SCTP_COOKIE_ECHOED указал, что связь не полностью установлена. Это означает, что инициирующая сторона (в данном случае ваш localhost) отправила (один или несколько раз) чанк COOKIE_ECHO, который не был подтвержден COOKIE_ACK с удаленного конца.

Вы можете отправлять сообщения в этом состоянии (SCTP просто буферизует его, пока не получит COOKIE_ACK и не отправит его позже).

Трудно сказать, что пошло не так, исходя из предоставленной вами информации. На этом этапе, вероятно, стоит изучить трассировку проволочной акулы, чтобы узнать, какая удаленная сторона отвечает на ваш COOKIE_ECHO.

Также, если вы можете поделиться своим клиент-серверным кодом, который может помочь определить основную причину.

ОБНОВЛЕНИЕ № 1: Следует также отметить, что приложение может прервать сопоставление самостоятельно (например, если это сопоставление не настроено на этом сервере). Если вы пытаетесь подключиться к случайному серверу (а не к вашему конкретному серверу), это вполне возможно и действительно имеет смысл в вашем случае. В этом случае состояние ассоциации на вашей стороне - COOKIE_ECHOED, потому что COOKIE_ACK еще не прибыл (просто состояние гонки). Как я уже говорил, SCTP с радостью принимает ваши данные в этом состоянии и просто буферизует их, пока не получит COOKIE_ACK. SCTP на удаленной стороне отправляет COOKIE_ACK сразу же, даже до того, как приложение получило контроль выполнения в accept(). Если приложение решило прекратить ассоциацию неприличным способом, оно отправит ABORT (это ваш первый ABORT в трассировке Wireshark). Ваша сторона еще не получила этот ABORT и отправляет блок данных. Поскольку удаленная сторона считает эту связь уже прерванной, она не может обрабатывать порцию DATA, поэтому она обрабатывает ее как неожиданно (см. RFC 4960, глава 8.4) и отправляет другой ABORT с t-битом, установленным в 1. Я думаю, это то, что произошло в твой случай. Вы можете легко подтвердить это, просто взглянув на след проволочной акулы.

Другие вопросы по тегам