SCTP_AUTH_ACTIVE_KEY fails with Invalid Argument, how to?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Good Day,

I am trying to enable the RFC 4895 sctp-auth extension in our communications framework. For this goal I am using standard linux sockets options, setting SCTP_AUTH_XXXX options to my client and server sockets.

The problem I have is when using endpoint shared keys. Although I am able to install it via SCTP_AUTH_KEY option I am NOT able to activate it via SCTP_AUTH_ACTIVE_KEY. I have tried multiple times setting the option at different points of time but I still get the "Invalid Argument" error.

I have created a minimal example where I can reproduce the problem in question. Note that I have not payed much attention to the correct tx and rx paths since the example is enough to illustrate the problem. I have some pcaps as well to understand what goes into the wire. In the one, and only, message that sends the client, I can see that in the shared key id field, AUTH chunk, 0 is being used which means no shared key. What I would like to have is to activate my shared key. Unfortunately I did not find a way to verify which shared key is being used except the field in AUTH chunk.

I have also tried tried use sendmsg() sys call with control data setting SCTP_AUTHINFO but it also does not work so I am out of ideas.

How exactly do I activate an endpoint shared key?

P.S. See attached utils.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/sctp.h>
#include <unistd.h>
#include <fcntl.h>

#define RECVBUFSIZE 2048


int main() {
    char message[] = {"hello"};

    int sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
    if(sd == -1) {
        perror("failure opening socket");
        exit(EXIT_FAILURE);
    }

    {
      int on = 1;
      if (setsockopt(sd, IPPROTO_SCTP, SCTP_NODELAY, &on, sizeof(on)) < 0) {
        perror("fail to set SCTP_NODELAY");
        exit(EXIT_FAILURE);
      }
    }

    {
      sctp_initmsg initmsg;
      initmsg.sinit_num_ostreams   = 2;
      initmsg.sinit_max_instreams  = 2;
      initmsg.sinit_max_attempts   = 10;
      initmsg.sinit_max_init_timeo = 1000;
      if (setsockopt(sd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg)) < 0) {
        perror("fail to set SCTP_INITMSG");
        exit(EXIT_FAILURE);
      }
    }

    {
      int flags;
      if ((flags = fcntl(sd, F_GETFL, 0)) < 0) {
        perror("fail to get flags");
        exit(EXIT_FAILURE);
      }
      flags |= SOCK_NONBLOCK;
      if (fcntl(sd, F_SETFL, 0) < 0) {
        perror("fail to set flags");
        exit(EXIT_FAILURE);
      }
    }

    {
      int flags;
      if ((flags = fcntl(sd, F_GETFL, 0)) < 0) {
        perror("fail to get flags");
        exit(EXIT_FAILURE);
      }
      flags |= SOCK_NONBLOCK;
      if (fcntl(sd, F_SETFL, 0) < 0) {
        perror("fail to set flags");
        exit(EXIT_FAILURE);
      }
    }

    {
      struct sctp_assoc_value asso_auth_opt;
      asso_auth_opt.assoc_id    = SCTP_FUTURE_ASSOC;
      asso_auth_opt.assoc_value = sd;
      if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_SUPPORTED,
                     &asso_auth_opt, sizeof(asso_auth_opt)) < 0) {
        perror("fail to set SCTP_AUTH_SUPPORTED");
        exit(EXIT_FAILURE);
      }

      struct sctp_authchunk datachunktype;
      datachunktype.sauth_chunk = 0;
      if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_CHUNK,
                     &datachunktype, sizeof(sctp_authchunk)) < 0) {
        perror("fail to set SCTP_AUTH_CHUNK");
        exit(EXIT_FAILURE);
      }

      uint8_t numhmacs = 2;
      uint32_t hmacbuflen = sizeof(sctp_hmacalgo) + (numhmacs * sizeof(uint16_t));
      char hmacbuf[hmacbuflen];
      struct sctp_hmacalgo *opthmac = (struct sctp_hmacalgo *) hmacbuf;
      opthmac->shmac_num_idents = numhmacs;
      opthmac->shmac_idents[0] = SCTP_AUTH_HMAC_ID_SHA256;
      opthmac->shmac_idents[1] = SCTP_AUTH_HMAC_ID_SHA1;
      if (setsockopt(sd, SOL_SCTP, SCTP_HMAC_IDENT,
                     opthmac, hmacbuflen) < 0) {
        perror("fail to set SCTP_HMAC_IDENT");
        exit(EXIT_FAILURE);
      }

#if 1
      int keylen = 5;
      uint32_t keybuflen = keylen + sizeof(sctp_authkey);
      char keybuf[keybuflen];
      struct sctp_authkey *opt_key = (struct sctp_authkey *)keybuf;
      opt_key->sca_assoc_id  = SCTP_FUTURE_ASSOC; // for tcp style this is ignored. set something
      opt_key->sca_keylength = keylen;
      opt_key->sca_keynumber = 1;
      memcpy(opt_key->sca_key, "mykey", keylen);
      if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_KEY,
                     &opt_key, keybuflen) < 0) {
        perror("fail to set SCTP_AUTH_KEY");
        exit(EXIT_FAILURE);
      }
#endif

#if 0
        struct sctp_authkeyid actkey;
        actkey.scact_assoc_id  = SCTP_FUTURE_ASSOC; // for tcp style this is ignored. set something
        actkey.scact_keynumber = 1;
        if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_ACTIVE_KEY,
                       &actkey, sizeof(sctp_authkeyid)) < 0) {
            perror("fail to set SCTP_AUTH_ACTIVE_KEY");
            exit(EXIT_FAILURE);
        }
#endif
    }

    {
      int on = 1;
      if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
        perror("fail to set SO_REUSEADDR");
        exit(EXIT_FAILURE);
      }
    }

    {
      int on = 1;
      if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) {
        perror("fail to set SO_REUSEPORT");
        exit(EXIT_FAILURE);
      }
    }

    struct sockaddr_in address;
    bzero((void*)&address, sizeof(address));
    address.sin_family = AF_INET;
    address.sin_port = htons(14242);
    address.sin_addr.s_addr = inet_addr("127.0.0.1");

    int rb = bind(sd, (struct sockaddr *)&address, sizeof(address));
    if(rb == -1) {
        perror("bind error");
        close(sd);
        exit(EXIT_FAILURE);
    }

#if 0
        struct sctp_authkeyid actkey;
        actkey.scact_assoc_id  = SCTP_FUTURE_ASSOC; // for tcp style this is ignored. set something
        actkey.scact_keynumber = 1;
        if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_ACTIVE_KEY,
                       &actkey, sizeof(sctp_authkeyid)) < 0) {
            perror("fail to set SCTP_AUTH_ACTIVE_KEY");
            exit(EXIT_FAILURE);
        }
#endif

    int ret = connect(sd, (sockaddr *)&address, sizeof(address));
    if (ret < 0)
    {
        perror("fail to connect");
        close(sd);
        exit(EXIT_FAILURE);
    }

#if 0
        struct sctp_authkeyid actkey;
        actkey.scact_assoc_id  = SCTP_FUTURE_ASSOC; // for tcp style this is ignored. set something
        actkey.scact_keynumber = 1;
        if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_ACTIVE_KEY,
                       &actkey, sizeof(sctp_authkeyid)) < 0) {
            perror("fail to set SCTP_AUTH_ACTIVE_KEY");
            exit(EXIT_FAILURE);
        }
#endif

    sctp_event_subscribe events = {0};
    events.sctp_association_event = 1;
    events.sctp_data_io_event     = 1;
    events.sctp_shutdown_event    = 1;
    events.sctp_peer_error_event  = 1;
    if (setsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS,
                 &events, sizeof(events)) < 0) {
        perror("fail to set SCTP_EVENTS");
        exit(EXIT_FAILURE);
    }

    sctp_paddrparams heartbeats = {0};
    heartbeats.spp_flags      = SPP_HB_ENABLE | SPP_PMTUD_DISABLE;
    heartbeats.spp_hbinterval = 5000;
    heartbeats.spp_pathmaxrxt = 1;
    if (setsockopt(sd, SOL_SCTP, SCTP_PEER_ADDR_PARAMS,
                 &heartbeats, sizeof(heartbeats)) < 0) {
        perror("fail to set SCTP_EVENTS");
        exit(EXIT_FAILURE);
    }

    sctp_rtoinfo rtoinfo = {0};
    int sctprto = 200;
    rtoinfo.srto_min     = sctprto;
    rtoinfo.srto_max     = 2 * sctprto;
    rtoinfo.srto_initial = (3*sctprto)/2;
    if (setsockopt(sd, IPPROTO_SCTP, SCTP_RTOINFO,
                 &rtoinfo, sizeof(rtoinfo)) < 0) {
        perror("fail to set SCTP_EVENTS");
        exit(EXIT_FAILURE);
    }

    int sent;

#if 0
    struct msghdr msg = {0};

    struct iovec datav;
    datav.iov_base = message;
    datav.iov_len  = sizeof(message);
    msg.msg_iovlen = 1;
    msg.msg_iov    = &datav;

    char ctrlbuf[CMSG_SPACE(sizeof(sctp_sndrcvinfo)) +
                 CMSG_SPACE(sizeof(sctp_authinfo))];

    msg.msg_control    = ctrlbuf;
    msg.msg_controllen = sizeof(ctrlbuf);

    struct cmsghdr *pcmsg = CMSG_FIRSTHDR(&msg);
    pcmsg->cmsg_len    = CMSG_LEN(sizeof(sctp_sndrcvinfo));
    pcmsg->cmsg_level  = SOL_SCTP;
    pcmsg->cmsg_type   = SCTP_SNDRCV;

    struct sctp_sndrcvinfo *srinfo = (struct sctp_sndrcvinfo *) CMSG_DATA(pcmsg);
    srinfo->sinfo_stream = 0;
    srinfo->sinfo_ppid   = 11;

    pcmsg = CMSG_NXTHDR(&msg, pcmsg);
    pcmsg->cmsg_len    = CMSG_LEN(sizeof(sctp_authinfo));
    pcmsg->cmsg_level  = SOL_SCTP;
    pcmsg->cmsg_type   = SCTP_AUTHINFO;
    struct sctp_authinfo *authinfo = (struct sctp_authinfo *) CMSG_DATA(pcmsg);
    authinfo->auth_keynumber = 1;

    if ((sent = sendmsg(sd, &msg, MSG_DONTWAIT)) <= 0)
    {
        perror("fail to sendmsg");
#else
    sctp_sndrcvinfo sinfo = {0};
    sinfo.sinfo_ppid = 11;
    sinfo.sinfo_stream = 0;
    if ((sent = sctp_send(sd, message, sizeof(message), &sinfo, 0)) <= 0)
    {
        perror("fail to sctp_send");
#endif
        exit(EXIT_FAILURE);
    }

    fprintf(stdout, "sent %s/%d\n", message, sent);

    close(sd);

    return EXIT_SUCCESS;
}

Attachment: sctp-auth-default-ok.pcapng
Description: application/pcapng

Attachment: sctp-auth-share-key-fail-1.pcapng
Description: application/pcapng

Attachment: sctp-auth-share-key-fail-2.pcapng
Description: application/pcapng

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/sctp.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

#define RECVBUFSIZE 2048

int main() {

    int sd;

    sd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);

    if(sd == -1) {
        perror("failure opening socket");
        exit(EXIT_FAILURE);
    }

    struct sockaddr_in serverAddress;
    bzero((void *)&serverAddress, sizeof(serverAddress));
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = htonl(0x7f000001);
    serverAddress.sin_port = htons(14242);

    {
      int on = 1;
      if (setsockopt(sd, IPPROTO_SCTP, SCTP_NODELAY, &on, sizeof(on)) < 0) {
        perror("fail to set SCTP_NODELAY");
        exit(EXIT_FAILURE);
      }
    }

    {
      sctp_initmsg initmsg;
      initmsg.sinit_num_ostreams   = 2;
      initmsg.sinit_max_instreams  = 2;
      initmsg.sinit_max_attempts   = 10;
      initmsg.sinit_max_init_timeo = 1000;
      if (setsockopt(sd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg)) < 0) {
        perror("fail to set SCTP_INITMSG");
        exit(EXIT_FAILURE);
      }
    }

    {
      int flags;
      if ((flags = fcntl(sd, F_GETFL, 0)) < 0) {
        perror("fail to get flags");
        exit(EXIT_FAILURE);
      }
      flags |= SOCK_NONBLOCK;
      if (fcntl(sd, F_SETFL, 0) < 0) {
        perror("fail to set flags");
        exit(EXIT_FAILURE);
      }
    }

    {
      struct sctp_assoc_value asso_auth_opt;
      asso_auth_opt.assoc_id    = SCTP_FUTURE_ASSOC;
      asso_auth_opt.assoc_value = sd;
      if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_SUPPORTED,
                     &asso_auth_opt, sizeof(asso_auth_opt)) < 0) {
        perror("fail to set SCTP_AUTH_SUPPORTED");
        exit(EXIT_FAILURE);
      }

      struct sctp_authchunk datachunktype;
      datachunktype.sauth_chunk = 0;
      if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_CHUNK,
                     &datachunktype, sizeof(sctp_authchunk)) < 0) {
        perror("fail to set SCTP_AUTH_CHUNK");
        exit(EXIT_FAILURE);
      }

      uint8_t numhmacs = 2;
      uint32_t hmacbuflen = sizeof(sctp_hmacalgo) + (numhmacs * sizeof(uint16_t));
      char hmacbuf[hmacbuflen];
      struct sctp_hmacalgo *opthmac = (struct sctp_hmacalgo *) hmacbuf;
      opthmac->shmac_num_idents = numhmacs;
      opthmac->shmac_idents[0] = SCTP_AUTH_HMAC_ID_SHA256;
      opthmac->shmac_idents[1] = SCTP_AUTH_HMAC_ID_SHA1;
      if (setsockopt(sd, SOL_SCTP, SCTP_HMAC_IDENT,
                     opthmac, hmacbuflen) < 0) {
        perror("fail to set SCTP_HMAC_IDENT");
        exit(EXIT_FAILURE);
      }

#if 1
      int keylen = 5;
      uint32_t keybuflen = keylen + sizeof(sctp_authkey);
      char keybuf[keybuflen];
      struct sctp_authkey *opt_key = (struct sctp_authkey *)keybuf;
      opt_key->sca_assoc_id  = SCTP_FUTURE_ASSOC; // for tcp style this is ignored. set something
      opt_key->sca_keylength = keylen;
      opt_key->sca_keynumber = 1;
      memcpy(opt_key->sca_key, "mykey", keylen);
      if (setsockopt(sd, SOL_SCTP, SCTP_AUTH_KEY,
                     &opt_key, keybuflen) < 0) {
        perror("fail to set SCTP_AUTH_KEY");
        exit(EXIT_FAILURE);
      }
#endif
    }

    {
      int on = 1;
      if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) {
        perror("fail to set SO_REUSEPORT");
        exit(EXIT_FAILURE);
      }
    }

    int rb = bind(sd, (struct sockaddr *)&serverAddress, sizeof(serverAddress));
    if(rb == -1) {
        perror("bind error");
        close(sd);
        exit(EXIT_FAILURE);
    }

    int rl = listen(sd, 1);
    if(rl < 0) {
        perror("failed to listen for connection");
        close(sd);
        exit(EXIT_FAILURE);
    }

    char buf[RECVBUFSIZE];

    struct sctp_sndrcvinfo sinfo = {0};

    int newsd, rcvdlen;

    for(;;) {
        struct sockaddr_in  client_addr = {0};
        socklen_t socklen = sizeof(client_addr);

        newsd = accept(sd, (sockaddr*)&client_addr, &socklen);
        if (newsd <= 0)
        {
          switch (errno)
          {
            case EWOULDBLOCK:
              perror("fail to accept block");
              return EXIT_FAILURE;
            case EINTR:
              continue;
            default:
              perror("fail to sctp_recvmsg");
              return EXIT_FAILURE;
          }
        }

        {
          int flags;
          if ((flags = fcntl(newsd, F_GETFL, 0)) < 0) {
            perror("fail to get flags");
            exit(EXIT_FAILURE);
          }
          flags |= SOCK_NONBLOCK;
          if (fcntl(newsd, F_SETFL, 0) < 0) {
            perror("fail to set flags");
            exit(EXIT_FAILURE);
          }

          sctp_event_subscribe events = {0};
          events.sctp_association_event = 1;
          events.sctp_data_io_event     = 1;
          events.sctp_shutdown_event    = 1;
          events.sctp_peer_error_event  = 1;
          if (setsockopt(newsd, IPPROTO_SCTP, SCTP_EVENTS,
                         &events, sizeof(events)) < 0) {
            perror("fail to set SCTP_EVENTS");
            exit(EXIT_FAILURE);
          }

          sctp_paddrparams heartbeats = {0};
          heartbeats.spp_flags      = SPP_HB_ENABLE | SPP_PMTUD_DISABLE;
          heartbeats.spp_hbinterval = 5000;
          heartbeats.spp_pathmaxrxt = 1;
          if (setsockopt(newsd, SOL_SCTP, SCTP_PEER_ADDR_PARAMS,
                         &heartbeats, sizeof(heartbeats)) < 0) {
            perror("fail to set SCTP_EVENTS");
            exit(EXIT_FAILURE);
          }

          sctp_rtoinfo rtoinfo = {0};
          int sctprto = 200;
          rtoinfo.srto_min     = sctprto;
          rtoinfo.srto_max     = 2 * sctprto;
          rtoinfo.srto_initial = (3*sctprto)/2;
          if (setsockopt(newsd, IPPROTO_SCTP, SCTP_RTOINFO,
                         &rtoinfo, sizeof(rtoinfo)) < 0) {
            perror("fail to set SCTP_EVENTS");
            exit(EXIT_FAILURE);
          }

          int rflags;
          while (1)
          {
            if ((rcvdlen = sctp_recvmsg(newsd, buf, sizeof(buf), 0, 0,
                                        &sinfo, &rflags)) <= 0)
            {
              switch (errno)
              {
                case EWOULDBLOCK:
                  perror("fail to sctp_recvmsg woudlblock");
                  return EXIT_FAILURE;
                case EINTR:
                  continue;
                default:
                  perror("fail to sctp_recvmsg");
                  return EXIT_FAILURE;
              }
            }
            if (rflags & MSG_NOTIFICATION)
            {
              fprintf(stdout, "sctp event - contine\n");
              continue;
            }

            fprintf(stdout, "rcvd %d\n", rcvdlen);
            if (rcvdlen)
            {
              buf[rcvdlen] = 0;
              fprintf(stdout, "rcvd msg %s bytes %d\n", buf, rcvdlen);
            }
          }
        }
    }

    return EXIT_SUCCESS;
}

[Index of Archives]     [Linux Networking Development]     [Linux OMAP]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     SCTP

  Powered by Linux