It appear there are very many functions that can send SCTP data on Linux, including general functions such as send()
and write()
but also sctp_send
and sctp_sendmsg
. The latter enable some "special SCTP functionality". But how many different functions are there, and how do they differ?
I've got an existing codebase that contains a sctp_recvmsg
call:
int flags = 0;
sctp_sndrcvinfo zeroed { 0 };
struct {
uint32_t frame_nr;
uint16_t sample_rate;
uint16_t num_samples;
uint8_t samples[1500];
} buffer;
int result = sctp_recvmsg(sock,
&buffer, sizeof(buffer), // Actual data
nullptr, 0, // We don't care about the sender
&zeroed, // SCTP Metadata
&flags); // Must be zero before call
if (result <= 0) // Not good.
{
std::cout << "sctp_recvmsg() error " << errno << std::endl;
}
This successfully receives messages from a closed-source device. I'm trying to build a simulator that sends the same type of SCTP messages. I can successfully connect the socket, but after that the transmission of data fails.
#include <iostream>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/sctp.h>
#include <unistd.h>
#include <arpa/inet.h>
struct {
uint32_t frame_nr;
uint16_t sample_rate;
uint16_t num_samples;
uint8_t samples[1500];
} buffer {0};
int main()
{
// SCTP server-side setup
int ls = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
if (ls == -1) {
perror("failure opening listen socket");
exit(-3);
}
struct sockaddr_in serverAddress { AF_INET };
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(19000);
int br = bind(ls, (struct sockaddr *)&serverAddress, sizeof(serverAddress));
if (br == -1) {
perror("bind error");
close(ls);
exit(-4);
}
// Set up 2 streams.
struct sctp_initmsg initMsg { 0 };
initMsg.sinit_num_ostreams = 2; // Matches receiver
initMsg.sinit_max_instreams = 2; // Matches receiver
initMsg.sinit_max_attempts = 0;
initMsg.sinit_max_init_timeo = 0;
setsockopt(ls, IPPROTO_SCTP, SCTP_INITMSG, &initMsg, sizeof(initMsg));
int rl = listen(ls, 1);
if(rl < 0) {
perror("failed to listen for connection");
close(ls);
exit(-5);
}
struct sockaddr_in recv_addr;
socklen_t len = sizeof(recv_addr);
int ss = accept(ls, reinterpret_cast<struct sockaddr *>(&recv_addr), &len);
if (ss == -1) {
perror("failed to accept connection");
close(ls);
exit(-6);
}
std::cout << "Accepted incoming connection" << std::endl;
// Attempt 1. Causes ENOENT in receiver (undocumented error code)
// send(ss, &buffer, sizeof(buffer), 0); // Note: last msg is same size, but not all samples are meaningful.
// Attempt 2. Causes EINVAL in receiver (another undocumented error code)
// sctp_sendmsg(ss, &buffer, sizeof(buffer),
// NULL, 0, // Do not override destination address
// 0,0,0,0,0); // No other special options or fields.
// Attempt 3: plain old write. Also causes EINVAL.
write(ss, &buffer, sizeof(buffer));
}
Just to be clear: the existing codebase with the sctp_recvmsg
can't change. I have to write code to work with that. The part that's under my control is the sending code, and that's the bit for which I've added the MCVE. What SCTP send function should that use, and why? (Bonus Q: why do the other ways to send data cause these errors on the receiver side?)