sctplab / usrsctp

A portable SCTP userland stack
BSD 3-Clause "New" or "Revised" License
673 stars 280 forks source link

usrsctp not working properly on windows #572

Open delinage opened 3 years ago

delinage commented 3 years ago

Hello,

I'm trying to use ursctp in windows but I'm having multiple issues.

The first one is the fact that I can only send SCTP messages if I encapsulate them on UDP. For instance, if I use this code in Linux, I can capture SCTP messages on wireshark (and connect to my server) but if I use it on windows (same code and the library compiled with no errors) I can't see any SCTP messages on wireshark (and I cannot connect to my server).

#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <sys/types.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#else
#include <io.h>
#endif
#include <usrsctp.h>
#include "programs_helper.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h> 
#include <sys/types.h>

#define RETVAL_CATCHALL     50
#define RETVAL_TIMEOUT      60
#define RETVAL_ECONNREFUSED 61

int done = 0;

static int
receive_cb(struct socket* sock, union sctp_sockstore addr, void* data,
    size_t datalen, struct sctp_rcvinfo rcv, int flags, void* ulp_info)
{
    if (data == NULL)
    {
        done = 1;
        usrsctp_close(sock);
    }
    else
    {
        if (flags & MSG_NOTIFICATION)
        {
            handle_notification((union sctp_notification*)data, datalen);
        }
        else
        {
            for (unsigned int i = 0; i < datalen; i++)
            {
                printf("%02X ", ((unsigned char*)data)[i]);
            }
            printf("\n");
        }
        free(data);
    }
    return (1);
}

struct socket* creatingsocketto(const char* address)
{
    struct socket* sock;
    struct sockaddr* addr;
    socklen_t addr_len;
    struct sockaddr_in addr4;
    struct sockaddr_in6 addr6;
    struct sctp_rtoinfo rtoinfo;
    struct sctp_initmsg initmsg;
    struct sctp_event event;
    int result = 0;
    unsigned int i;
    uint8_t address_family = 0;
    uint16_t event_types[] = { SCTP_ASSOC_CHANGE,
                              SCTP_PEER_ADDR_CHANGE,
                              SCTP_SEND_FAILED_EVENT,
                              SCTP_REMOTE_ERROR,
                              SCTP_SHUTDOWN_EVENT,
                              SCTP_ADAPTATION_INDICATION,
                              SCTP_PARTIAL_DELIVERY_EVENT
    };

    memset((void*)&addr4, 0, sizeof(struct sockaddr_in));
    memset((void*)&addr6, 0, sizeof(struct sockaddr_in6));

    if (inet_pton(AF_INET, address, &addr4.sin_addr) == 1) {
        address_family = AF_INET;

        addr = (struct sockaddr*)&addr4;
        addr_len = sizeof(addr4);

        addr4.sin_family = AF_INET;
        addr4.sin_port = htons(atoi("36412"));
    }
    else if (inet_pton(AF_INET6, address, &addr6.sin6_addr) == 1) {
        address_family = AF_INET6;

        addr = (struct sockaddr*)&addr6;
        addr_len = sizeof(addr6);

        addr6.sin6_family = AF_INET6;
        addr6.sin6_port = htons(atoi("36412"));
    }
    else {
        printf("Unsupported destination address - use IPv4 or IPv6 address\n");
        exit(EXIT_FAILURE);
    }

    usrsctp_init(0, NULL, debug_printf_stack);

    usrsctp_sysctl_set_sctp_blackhole(2);
    usrsctp_sysctl_set_sctp_no_csum_on_loopback(0);

    if ((sock = usrsctp_socket(address_family, SOCK_STREAM, IPPROTO_SCTP, receive_cb, NULL, 0, NULL)) == NULL) {
        perror("usrsctp_socket");
        exit(EXIT_FAILURE);
    }

    memset(&event, 0, sizeof(event));
    event.se_assoc_id = SCTP_ALL_ASSOC;
    event.se_on = 1;
    for (i = 0; i < sizeof(event_types) / sizeof(uint16_t); i++) {
        event.se_type = event_types[i];
        if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof(event)) < 0) {
            perror("setsockopt SCTP_EVENT");
        }
    }

    rtoinfo.srto_assoc_id = 0;
    rtoinfo.srto_initial = 1000;
    rtoinfo.srto_min = 1000;
    rtoinfo.srto_max = 8000;
    if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_RTOINFO, (const void*)&rtoinfo, (socklen_t)sizeof(struct sctp_rtoinfo)) < 0) {
        perror("setsockopt");
        usrsctp_close(sock);
        exit(EXIT_FAILURE);
    }
    initmsg.sinit_num_ostreams = 1;
    initmsg.sinit_max_instreams = 1;
    initmsg.sinit_max_attempts = 5;
    initmsg.sinit_max_init_timeo = 4000;
    if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_INITMSG, (const void*)&initmsg, (socklen_t)sizeof(struct sctp_initmsg)) < 0) {
        perror("setsockopt");
        usrsctp_close(sock);
        exit(EXIT_FAILURE);
    }

    if (usrsctp_connect(sock, addr, addr_len) < 0) {
        if (errno == ECONNREFUSED) {
            result = RETVAL_ECONNREFUSED;
        }
        else if (errno == ETIMEDOUT) {
            result = RETVAL_TIMEOUT;
        }
        else {
            result = RETVAL_CATCHALL;
        }
        perror("usrsctp_connect");
        usrsctp_close(sock);

        exit(EXIT_FAILURE);
    }

    return sock;
}

int main()
{

    struct socket* sock = creatingsocketto("192.168.0.10");

    char msg[5] = "test";
    struct sctp_sndinfo sndinfo;    
    memset(&sndinfo, 0, sizeof(struct sctp_sndinfo));
    sndinfo.snd_ppid = htonl(18);

    if (usrsctp_sendv(sock, msg, sizeof(msg), NULL, 0, &sndinfo, sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, 0) < 0) {
        perror("usrsctp_sendv");
        usrsctp_close(sock);

    }

#ifdef _WIN32
        Sleep(5*1000);
#else
        sleep(5);
#endif   

    usrsctp_finish();

    return 0;
}

One can argue the correctness of this code but the thing that I do not understand is why does this code work over linux but not over Windows.

I have other problem regarding the sending of encapsulated packets on windows but I assume I better explain it in other issue.

tuexen commented 3 years ago

What about using the example programs which are provided? Do they work? Are you using NAT or virtual machines? @weinrank Is there anything special on Windows when using raw sockets?

delinage commented 3 years ago

If I use examples such as the http_client, it works in Windows if I write enough arguments to use UDP encapsulation but it does not work if I write just the arguments necessaries to work without UDP encapsulation... I do not understand this "necessity of encapsulation" in Windows.

I am using a windows computer with the client and server in the same machine and I can't see the SCTP init messages if I do not choose encapsulation. I have also tried through NAT to connect to my actual server and it is the same, no init messages if I do not want them encapsulated.

tuexen commented 3 years ago

@weinrank might know if there is anything special to do for using raw sockets on Windows. Without UDP encapsulation I would avoid middleboxes like NATs or firewalls unless they support SCTP. Do you have any sort of firewall or security software running on Windows which limits non-UDP and non-TCP traffic?

delinage commented 3 years ago

I disabled both antivirus and windows firewall and I have not any special additional security software. I understand avoiding middleboxes and NATs but I am not even getting SCTP INIT messages without encapsulation locally in windows (server and client in the same machine) while if I do that on Linux (server and client in the same machine) it works like a charm...

skrathbun-rainway commented 3 years ago

usrsctp requires the ability to open RAW sockets which cannot be done on windows without administrator privileges. I agree that this is a major limitation, but there's nothing that can really be done about it...

I'm curious what your use-case for SCTP is though. I think it is more common to encapsulate SCTP traffic with some other transport, which can be done by assigning a write callback with usrsctp_init() (this captures all outgoing traffic and lets you send it through some other means) and assigning a read callback with usrsctp_socket() (which captures all inbound traffic on a socket). Using these features you can create your own encapsulation which is quite handy if for example you are writing a webrtc implementation and need some way to pipe SCTP through D/TLS and then onto UDP/TCP...