Why doesn't SO_TIMESTAMPING generate ancillary data? [duplicate] - c++

I have been trying and failing to get Linux (kernel 4.1.4) to give me timestamps for when UDP datagrams are sent and received. I have read the original kernel docs (https://www.kernel.org/doc/Documentation/networking/timestamping.txt), along with lots of examples and a number of stackoverflow entries. I can send datagrams between sender and receiver with no problems. But I cannot get timestamps for sending or receiving datagrams, and I can't figure out what I'm doing wrong.
One bizarre thing is that when I use the MSG_ERRQUEUE channel for getting timestamp info on a sent datagram, I do get the original outgoing packet, and I do get the first ancillary message (SOL_IP, IP_RECVERR), but I do not get a second message (which should be level SOL_SOCKET, type SCM_TIMESTAMPING).
In another stackoverflow entry on getting timestamps for sent packets (Timestamp outgoing packets), someone mentioned that some drivers might not implement the call to skb_tx_timestamp, but I checked mine (Realtek), and that call is definitely in there.
Here's how I set up the UDP receiver (error handling code not shown):
inf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
timestampOn = SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_RX_HARDWARE;
r = setsockopt(inf->fd, SOL_SOCKET, SO_TIMESTAMPING, &timestampOn, sizeof(timestampOn));
r = setsockopt(inf->fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
memset(&(inf->local), 0, sizeof(struct sockaddr_in));
inf->local.sin_family = AF_INET;
inf->local.sin_port = htons(port);
inf->local.sin_addr.s_addr = htonl(INADDR_ANY);
r = bind(inf->fd, (struct sockaddr *)&(inf->local), sizeof(struct sockaddr_in));
Using SO_REUSEPORT or not doesn't seem to matter.
For receiving, my understanding is that we don't use MSG_ERRQUEUE. That's only if we want timestamps for sent messages. Besides, when I use MSG_ERRQUEUE with recvmsg, I get "resource temporarily unavailable." Here's how I receive datagrams:
int recv_len;
struct msghdr msg;
struct iovec iov;
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
// Space for control message info plus timestamp
char ctrl[2048];
memset(ctrl, 0, sizeof(ctrl));
//struct cmsghdr *cmsg = (struct cmsghdr *) &ctrl;
// Ancillary data buffer and length
msg.msg_control = (char *) ctrl;
msg.msg_controllen = sizeof(ctrl);
// Dest address info
msg.msg_name = (struct sockaddr *) &(inf->remote);
msg.msg_namelen = sizeof(struct sockaddr_in);
// Array of data buffers (scatter/gather)
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// Data buffer pointer and length
iov.iov_base = buf;
iov.iov_len = len;
recv_len = recvmsg(inf->fd, &msg, 0);
And then I pass a pointer to msg to another function (handle_time) that does this:
struct timespec* ts = NULL;
struct cmsghdr* cmsg;
struct sock_extended_err *ext;
for( cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg,cmsg) ) {
printf("level=%d, type=%d, len=%zu\n", cmsg->cmsg_level, cmsg->cmsg_type, cmsg->cmsg_len);
}
Zero messages are received. So that's the first problem. My setup code above matches like half a dozen other examples I've found on the web, but I'm getting no ancillary data from this.
Next, let's turn to sending datagrams. Here's the setup:
inf->port = port;
inf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
memset(&(inf->remote), 0, sizeof(struct sockaddr_in));
inf->remote.sin_family = AF_INET;
inf->remote.sin_port = htons(port);
timestampOn = SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE;
r = setsockopt(inf->fd, SOL_SOCKET, SO_TIMESTAMPING, &timestampOn, sizeof(timestampOn));
on = 1;
r = setsockopt(inf->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
r = inet_aton(address, &(inf->remote.sin_addr));
And this is how I send a datagram:
int send_len, r, i;
struct msghdr msg;
struct iovec iov;
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
// Space for control message info plus timestamp
char ctrl[2048];
memset(ctrl, 0, sizeof(ctrl));
//struct cmsghdr *cmsg = (struct cmsghdr *) &ctrl;
// Ancillary data buffer and length
//msg.msg_control = (char *) ctrl;
//msg.msg_controllen = sizeof(ctrl);
// Dest address info
msg.msg_name = (struct sockaddr *) &(inf->remote);
msg.msg_namelen = sizeof(struct sockaddr_in);
// Array of data buffers (scatter/gather)
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// Data buffer pointer and length
iov.iov_base = buf;
iov.iov_len = len;
send_len = sendmsg(inf->fd, &msg, 0);
Examples I've seen reuse the msg and iov data structures, but in my experimentation, I added code to make sure things were cleared, just in case the send left anything behind, although it didn't make any difference. Here's the code for getting the timestamp:
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
memset(ctrl, 0, sizeof(ctrl));
msg.msg_control = (char *) ctrl;
msg.msg_controllen = sizeof(ctrl);
msg.msg_name = (struct sockaddr *) &(inf->remote);
msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
iov.iov_base = junk_buf;
iov.iov_len = sizeof(junk_buf);
for (;;) {
r = recvmsg(inf->fd, &msg, MSG_ERRQUEUE);
if (r<0) {
fprintf(stderr, "Didn't get kernel time\n");
return send_len;
}
printf("recvmsg returned %d\n", r);
handle_time(&msg);
}
The data buffer contains the original datagram as expected. The ancillary data I get back includes a single message, which handle_time prints as:
level=0, type=11, len=48
This is level SOL_IP and type IP_RECVERR, which is expected according to the docs. Looking into the payload (a struct sock_extended_err), the errno is 42 (ENOMSG, No message of desired type) and origin is 4 (SO_EE_ORIGIN_TXSTATUS). From the docs, this is supposed to happen and demonstrates that in fact I did manage to inform the kernel that I want TX status messages. But there is no second ancillary message!
I have tried to see if there is any kernel compile option that might disable this, but I haven't found any. So I'm just completely baffled here. Can anyone help me figure out what I'm doing wrong?
Thanks!
UPDATE: I tried running this same code on another Linux machine, this time CentOS 7 (kernel 3.10.0-693.2.2.el7.x86_64). I can't figure out what what kind of NIC that machine has, but when I try to send datagrams, I get some other weird behavior. For the very first datagram, when I start this program, I get back the message and a single ancillary message, just as above. For every subsequent sendmsg call, errno tells me that I get an "Invalid argument" error. This error goes away if I don't enable timestamps on the socket.
UPDATE 2: I discovered that I had not been making an ioctl necessary to enable timestamps in the driver. Unfortunately, when I do this call, I get ENODEV from errno (no such device). Here's how I'm trying to do it (which I'm imitating from https://github.com/majek/openonload/blob/master/src/tests/onload/hwtimestamping/tx_timestamping.c):
struct ifreq ifr;
struct hwtstamp_config hwc;
inf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
memset(&ifr, 0, sizeof(ifr));
hwc.flags = 0;
hwc.tx_type = HWTSTAMP_TX_ON;
hwc.rx_filter = HWTSTAMP_FILTER_ALL;
ifr.ifr_data = (char*)&hwc;
r = ioctl(inf->fd, SIOCSHWTSTAMP, &ifr);
That being said, I'd be relatively happy with software timestamps, which should not need this call. So I'm not sure this is helpful anyhow.
UPDATE 3: A compilable example was requested. The whole program is pretty minimal, so I put it into pastebin here: https://pastebin.com/qd0gspRc
Also, here's the output from ethtool:
Time stamping parameters for eth0:
Capabilities:
software-transmit (SOF_TIMESTAMPING_TX_SOFTWARE)
software-receive (SOF_TIMESTAMPING_RX_SOFTWARE)
software-system-clock (SOF_TIMESTAMPING_SOFTWARE)
PTP Hardware Clock: none
Hardware Transmit Timestamp Modes: none
Hardware Receive Filter Modes: none
Since this obviously doesn't support hardware timestamps, the ioctl is moot. I tried changing the SO_TIMESTAMPING setting to SOF_TIMESTAMPING_TX_SOFTWARE and SOF_TIMESTAMPING_RX_SOFTWARE for sender and receiver. That didn't help.
Then I tried adding SOF_TIMESTAMPING_SOFTWARE to both. I finally started getting something:
level=1, type=37, len=64
Level 1 is SOL_SOCKET, and type 37 is SCM_TIMESTAMPING. I'll go back to the docs and figure out how to interpret this. It says something about passing an array of three time structures. The driver's call to skb_tx_timestamp should have been sufficient so that it wouldn't require that I enable "fake" software timestamps to get something out.

Like I say in comment the use of SOF_TIMESTAMPING_SOFTWARE and SOF_TIMESTAMPING_RAW_HARDWARE is necessary because if I understand correctly the documentation, some bits are to generate the timestamp and some bits are here to report them in control message:
1.3.1 Timestamp Generation
Some bits are requests to the stack to try to generate timestamps. Any
combination of them is valid. Changes to these bits apply to newly
created packets, not to packets already in the stack. As a result, it
is possible to selectively request timestamps for a subset of packets
(e.g., for sampling) by embedding an send() call within two setsockopt
calls, one to enable timestamp generation and one to disable it.
Timestamps may also be generated for reasons other than being
requested by a particular socket, such as when receive timestamping is
enabled system wide, as explained earlier.
1.3.2 Timestamp Reporting
The other three bits control which timestamps will be reported in a
generated control message. Changes to the bits take immediate effect
at the timestamp reporting locations in the stack. Timestamps are only
reported for packets that also have the relevant timestamp generation
request set.
After, to use the data documentation say:
2.1 SCM_TIMESTAMPING records
These timestamps are returned in a control message with cmsg_level
SOL_SOCKET, cmsg_type SCM_TIMESTAMPING, and payload of type
struct scm_timestamping { struct timespec ts[3]; };
...
The structure can return up to three timestamps. This is a legacy
feature. At least one field is non-zero at any time. Most timestamps
are passed in ts[0]. Hardware timestamps are passed in ts[2].
To get transmit timestamp this require some configuration, first you need to know that software timestamp are not always available, I only achieve to get hardware transmit timestamp. But I'm not an expert in these domain, I just try to implemented timestamp with information that I found.
Secondly, I needed to activate hardware feature with linuxptp tool, I use hwstamp_cli:
hwstamp_ctl -i eth0 -r 1 -t 1
With this and some modification on your code I achieve to get hardware transmit timestamp but only with ethX interface because lo interface don't have these feature AFAIK so the final code is:
#include <arpa/inet.h>
#include <errno.h>
#include <inttypes.h>
#include <linux/errqueue.h>
#include <linux/net_tstamp.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#define UDP_MAX_LENGTH 1500
typedef struct {
int fd;
int port;
int err_no;
struct sockaddr_in local;
struct sockaddr_in remote;
struct timeval time_kernel;
struct timeval time_user;
int64_t prev_serialnum;
} socket_info;
static int setup_udp_receiver(socket_info *inf, int port) {
inf->port = port;
inf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (inf->fd < 0) {
inf->err_no = errno;
fprintf(stderr, "setup_udp_server: socket failed: %s\n",
strerror(inf->err_no));
return inf->fd;
}
int timestampOn =
SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE |
// SOF_TIMESTAMPING_OPT_TSONLY |
0;
int r = setsockopt(inf->fd, SOL_SOCKET, SO_TIMESTAMPING, &timestampOn,
sizeof timestampOn);
if (r < 0) {
inf->err_no = errno;
fprintf(stderr, "setup_udp_server: setsockopt failed: %s\n",
strerror(inf->err_no));
return r;
}
int on = 1;
r = setsockopt(inf->fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof on);
if (r < 0) {
inf->err_no = errno;
fprintf(stderr, "setup_udp_server: setsockopt2 failed: %s\n",
strerror(inf->err_no));
return r;
}
inf->local = (struct sockaddr_in){.sin_family = AF_INET,
.sin_port = htons((uint16_t)port),
.sin_addr.s_addr = htonl(INADDR_ANY)};
r = bind(inf->fd, (struct sockaddr *)&inf->local, sizeof inf->local);
if (r < 0) {
inf->err_no = errno;
fprintf(stderr, "setup_udp_server: bind failed: %s\n",
strerror(inf->err_no));
return r;
}
inf->prev_serialnum = -1;
return 0;
}
static int setup_udp_sender(socket_info *inf, int port, char *address) {
inf->port = port;
inf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (inf->fd < 0) {
inf->err_no = errno;
fprintf(stderr, "setup_udp_client: socket failed: %s\n",
strerror(inf->err_no));
return inf->fd;
}
int timestampOn =
SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE |
// SOF_TIMESTAMPING_OPT_TSONLY |
0;
int r = setsockopt(inf->fd, SOL_SOCKET, SO_TIMESTAMPING, &timestampOn,
sizeof timestampOn);
if (r < 0) {
inf->err_no = errno;
fprintf(stderr, "setup_udp_server: setsockopt failed: %s\n",
strerror(inf->err_no));
return r;
}
inf->remote = (struct sockaddr_in){.sin_family = AF_INET,
.sin_port = htons((uint16_t)port)};
r = inet_aton(address, &inf->remote.sin_addr);
if (r == 0) {
fprintf(stderr, "setup_udp_client: inet_aton failed\n");
inf->err_no = 0;
return -1;
}
inf->local = (struct sockaddr_in){.sin_family = AF_INET,
.sin_port = htons(0),
.sin_addr.s_addr = htonl(INADDR_ANY)};
inf->prev_serialnum = -1;
return 0;
}
static void handle_scm_timestamping(struct scm_timestamping *ts) {
for (size_t i = 0; i < sizeof ts->ts / sizeof *ts->ts; i++) {
printf("timestamp: %lld.%.9lds\n", (long long)ts->ts[i].tv_sec,
ts->ts[i].tv_nsec);
}
}
static void handle_time(struct msghdr *msg) {
for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg;
cmsg = CMSG_NXTHDR(msg, cmsg)) {
printf("level=%d, type=%d, len=%zu\n", cmsg->cmsg_level, cmsg->cmsg_type,
cmsg->cmsg_len);
if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) {
struct sock_extended_err *ext =
(struct sock_extended_err *)CMSG_DATA(cmsg);
printf("errno=%d, origin=%d\n", ext->ee_errno, ext->ee_origin);
continue;
}
if (cmsg->cmsg_level != SOL_SOCKET)
continue;
switch (cmsg->cmsg_type) {
case SO_TIMESTAMPNS: {
struct scm_timestamping *ts = (struct scm_timestamping *)CMSG_DATA(cmsg);
handle_scm_timestamping(ts);
} break;
case SO_TIMESTAMPING: {
struct scm_timestamping *ts = (struct scm_timestamping *)CMSG_DATA(cmsg);
handle_scm_timestamping(ts);
} break;
default:
/* Ignore other cmsg options */
break;
}
}
printf("End messages\n");
}
static ssize_t udp_receive(socket_info *inf, char *buf, size_t len) {
char ctrl[2048];
struct iovec iov = (struct iovec){.iov_base = buf, .iov_len = len};
struct msghdr msg = (struct msghdr){.msg_control = ctrl,
.msg_controllen = sizeof ctrl,
.msg_name = &inf->remote,
.msg_namelen = sizeof inf->remote,
.msg_iov = &iov,
.msg_iovlen = 1};
ssize_t recv_len = recvmsg(inf->fd, &msg, 0);
gettimeofday(&inf->time_user, NULL);
if (recv_len < 0) {
inf->err_no = errno;
fprintf(stderr, "udp_receive: recvfrom failed: %s\n",
strerror(inf->err_no));
}
handle_time(&msg);
return recv_len;
}
static ssize_t udp_send(socket_info *inf, char *buf, size_t len) {
struct iovec iov = (struct iovec){.iov_base = buf, .iov_len = len};
struct msghdr msg = (struct msghdr){.msg_name = &inf->remote,
.msg_namelen = sizeof inf->remote,
.msg_iov = &iov,
.msg_iovlen = 1};
gettimeofday(&inf->time_user, NULL);
ssize_t send_len = sendmsg(inf->fd, &msg, 0);
if (send_len < 0) {
inf->err_no = errno;
fprintf(stderr, "udp_send: sendmsg failed: %s\n", strerror(inf->err_no));
}
return send_len;
}
static ssize_t meq_receive(socket_info *inf, char *buf, size_t len) {
struct iovec iov = (struct iovec){.iov_base = buf, .iov_len = len};
char ctrl[2048];
struct msghdr msg = (struct msghdr){.msg_control = ctrl,
.msg_controllen = sizeof ctrl,
.msg_name = &inf->remote,
.msg_namelen = sizeof inf->remote,
.msg_iov = &iov,
.msg_iovlen = 1};
ssize_t recv_len = recvmsg(inf->fd, &msg, MSG_ERRQUEUE);
if (recv_len < 0) {
inf->err_no = errno;
if (errno != EAGAIN) {
fprintf(stderr, "meq_receive: recvmsg failed: %s\n",
strerror(inf->err_no));
}
return recv_len;
}
handle_time(&msg);
return recv_len;
}
typedef struct {
int64_t serialnum;
int64_t user_time_serialnum;
int64_t user_time;
int64_t kernel_time_serialnum;
int64_t kernel_time;
size_t message_bytes;
} message_header;
static const size_t payload_max = UDP_MAX_LENGTH - sizeof(message_header);
static ssize_t generate_random_message(socket_info *inf, char *buf,
size_t len) {
if (len < sizeof(message_header)) {
return -1;
}
message_header *header = (message_header *)buf;
char *payload = (char *)(header + 1);
size_t payload_len = (size_t)random() % (payload_max + 1);
if (payload_len > len - sizeof(message_header)) {
payload_len = len - sizeof(message_header);
}
for (size_t i = 0; i < payload_len; i++) {
payload[i] = (char)random();
}
static int64_t serial_num = 0;
*header = (message_header){
.user_time_serialnum = inf->prev_serialnum,
.user_time = inf->time_user.tv_sec * 1000000000L + inf->time_user.tv_usec,
.kernel_time_serialnum = inf->prev_serialnum,
.kernel_time =
inf->time_kernel.tv_sec * 1000000000L + inf->time_kernel.tv_usec,
.serialnum = serial_num,
.message_bytes = payload_len};
size_t total = payload_len + sizeof *header;
printf("uts%5" PRId64 ": kt=%" PRId64 ", ut=%" PRId64 ", sn=%" PRId64
": s=%zu\n",
header->user_time_serialnum, header->kernel_time, header->user_time,
header->serialnum, total);
inf->prev_serialnum = serial_num++;
return (ssize_t)total;
}
static void sender_loop(char *host) {
socket_info inf;
int ret = setup_udp_sender(&inf, 8000, host);
if (ret < 0) {
return;
}
for (int i = 0; i < 2000; i++) {
useconds_t t = random() % 2000000;
usleep(t);
char packet_buffer[4096];
ssize_t len =
generate_random_message(&inf, packet_buffer, sizeof packet_buffer);
if (len < 0) {
return;
}
udp_send(&inf, packet_buffer, (size_t)len);
while (meq_receive(&inf, packet_buffer, sizeof packet_buffer) != -1) {
}
}
}
static void receiver_loop(void) {
socket_info inf;
int ret = setup_udp_receiver(&inf, 8000);
if (ret < 0) {
return;
}
for (int i = 0; i < 1000; i++) {
char packet_buffer[4096];
udp_receive(&inf, packet_buffer, sizeof packet_buffer);
}
}
#define USAGE "Usage: %s [-r | -s host]\n"
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, USAGE, argv[0]);
return 0;
}
if (0 == strcmp(argv[1], "-s")) {
if (argc < 3) {
fprintf(stderr, USAGE, argv[0]);
return 0;
}
sender_loop(argv[2]);
} else if (0 == strcmp(argv[1], "-r")) {
receiver_loop();
} else {
fprintf(stderr, USAGE, argv[0]);
}
}
Exemple output:
$ ./a.out -r
level=1, type=37, len=64
timestamp: 1511196758.087209387s
timestamp: 0.000000000s
timestamp: 0.000000000s
End messages
level=1, type=37, len=64
timestamp: 1511196759.333507671s
timestamp: 0.000000000s
timestamp: 0.000000000s
End messages
$ ./a.out -s "8.8.8.8"
uts -1: kt=238059712, ut=140918979990070, sn=0: s=482
uts 0: kt=238059712, ut=1511197522000237457, sn=1: s=132
level=1, type=37, len=64
timestamp: 0.000000000s
timestamp: 0.000000000s
timestamp: 1511197359.637050597s
level=0, type=11, len=48
errno=42, origin=4
End messages
uts 1: kt=238059712, ut=1511197523000483805, sn=2: s=1454
level=1, type=37, len=64
timestamp: 0.000000000s
timestamp: 0.000000000s
timestamp: 1511197360.883295397s
level=0, type=11, len=48
errno=42, origin=4
End messages
Live test: sender, receiver

Related

WSASendMsg return error if set UDP source address

Similar to: Howto set the UDP source address on Windows It doesn't work for me. Unable to setup source port and get error:
I have created socket:
bool CUDPTransport::OpenConnection(void) {
// Create UDP socket
this->m_hSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (this->m_hSocket == INVALID_SOCKET) {
return false;
}
// Success
return true;
}
and I am able to send by UDP transport:
bool CUDPTransport::SendMessage(const CString& sBuffer, const CSyslogOptions& oSyslogOptions) {
// Convert using the local code page
CT2A sASCII(sBuffer);
// Create address
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(this->m_nPort);
sin.sin_addr.s_addr = htonl(this->m_nCollectorIPv4);
// Create buffer to send
WSABUF oBuffer;
oBuffer.buf = sASCII.m_psz;
oBuffer.len = (int)strlen(sASCII.m_psz);
// Declare
char aControlData[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))];
memset(&aControlData, 0, sizeof(aControlData));
// Create a message
WSAMSG oMessage;
memset(&oMessage, 0, sizeof(oMessage));
oMessage.name = (struct sockaddr*)&sin;
oMessage.namelen = sizeof(sin);
oMessage.lpBuffers = &oBuffer;
oMessage.dwBufferCount = 1;
/* TODO: set source address
oMessage.Control.buf = (char*)&aControlData;
oMessage.Control.len = sizeof(aControlData);
// Create messge header
WSACMSGHDR* oHeader = WSA_CMSG_FIRSTHDR(&oMessage);
// memset(oHeader, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo)));
oHeader->cmsg_level = IPPROTO_IP;
oHeader->cmsg_type = IP_PKTINFO;
oHeader->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo));
struct in_pktinfo* pktinfo = (struct in_pktinfo*)WSA_CMSG_DATA(oHeader);
pktinfo->ipi_addr.s_addr = htonl(oSyslogOptions.m_nSenderIPv4);
*/
// Declare variable
unsigned long nSentBytes = 0;
// Send an initial buffer
int nResult = WSASendMsg(this->m_hSocket, &oMessage, 0, &nSentBytes, NULL, NULL);
if (nResult == SOCKET_ERROR) {
int nErrorNo = this->getSocketLastOSErrorNo();
CString sErrorMsg = this->getSocketLastOSErrorString(nErrorNo);
closesocket(this->m_hSocket);
return false;
}
return true;
}
When I wish to set up source IP address I uncomment working with control data
bool CUDPTransport::SendMessage(const CString& sBuffer, const CSyslogOptions& oSyslogOptions) {
// Convert using the local code page
CT2A sASCII(sBuffer);
// Create address
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(this->m_nPort);
sin.sin_addr.s_addr = htonl(this->m_nCollectorIPv4);
// Create buffer to send
WSABUF oBuffer;
oBuffer.buf = sASCII.m_psz;
oBuffer.len = (int)strlen(sASCII.m_psz);
// Declare
char aControlData[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))];
memset(&aControlData, 0, sizeof(aControlData));
// Create a message
WSAMSG oMessage;
memset(&oMessage, 0, sizeof(oMessage));
oMessage.name = (struct sockaddr*)&sin;
oMessage.namelen = sizeof(sin);
oMessage.lpBuffers = &oBuffer;
oMessage.dwBufferCount = 1;
oMessage.Control.buf = (char*)&aControlData;
oMessage.Control.len = sizeof(aControlData);
// Create messge header
WSACMSGHDR* oHeader = WSA_CMSG_FIRSTHDR(&oMessage);
// memset(oHeader, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo)));
oHeader->cmsg_level = IPPROTO_IP;
oHeader->cmsg_type = IP_PKTINFO;
oHeader->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo));
struct in_pktinfo* pktinfo = (struct in_pktinfo*)WSA_CMSG_DATA(oHeader);
pktinfo->ipi_addr.s_addr = htonl(oSyslogOptions.m_nSenderIPv4);
// Declare variable
unsigned long nSentBytes = 0;
// Send an initial buffer
int nResult = WSASendMsg(this->m_hSocket, &oMessage, 0, &nSentBytes, NULL, NULL);
if (nResult == SOCKET_ERROR) {
int nErrorNo = this->getSocketLastOSErrorNo();
CString sErrorMsg = this->getSocketLastOSErrorString(nErrorNo);
closesocket(this->m_hSocket);
return false;
}
return true;
}
and get error with errno: 10022 and message: "An invalid argument was supplied". Does anybody can help me to understand the problem? Thanks!
-1.Error resolved by bind:
bool CUDPTransport::OpenConnection(void) {
// Create UDP socket
this->m_hSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (this->m_hSocket == INVALID_SOCKET) {
return false;
}
// Create source address (2130706433 = 127.0.0.1)
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = 0;
sin.sin_addr.s_addr = htonl(2130706433);
// Bind socker
int nResult = bind(this->m_hSocket, (struct sockaddr*)&sin, sizeof(sin));
if (nResult == SOCKET_ERROR) {
int nErrorNo = this->getSocketLastOSErrorNo();
CString sErrorMsg = this->getSocketLastOSErrorString(nErrorNo);
closesocket(this->m_hSocket);
return false;
}
// Success
return true;
}
-2.Send method is working fine.
bool CUDPTransport::SendMessage(const CString& sBuffer, const CSyslogOptions& oSyslogOptions) {
// Convert using the local code page
CT2A sASCII(sBuffer);
// Create address
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(this->m_nPort);
sin.sin_addr.s_addr = htonl(this->m_nCollectorIPv4);
// Create buffer to send
WSABUF oBuffer;
oBuffer.buf = sASCII.m_psz;
oBuffer.len = (int)strlen(sASCII.m_psz);
// Declare
char aControlData[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))];
memset(&aControlData, 0, sizeof(aControlData));
// Create a message
WSAMSG oMessage;
memset(&oMessage, 0, sizeof(oMessage));
oMessage.name = (struct sockaddr*)&sin;
oMessage.namelen = sizeof(sin);
oMessage.lpBuffers = &oBuffer;
oMessage.dwBufferCount = 1;
oMessage.Control.buf = (char*)&aControlData;
oMessage.Control.len = sizeof(aControlData);
// Create message header
WSACMSGHDR* oHeader = WSA_CMSG_FIRSTHDR(&oMessage);
memset(oHeader, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo)));
oHeader->cmsg_level = IPPROTO_IP;
oHeader->cmsg_type = IP_PKTINFO;
oHeader->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo));
struct in_pktinfo* pktinfo = (struct in_pktinfo*)WSA_CMSG_DATA(oHeader);
pktinfo->ipi_addr.s_addr = htonl(oSyslogOptions.m_nSenderIPv4);
// Declare variable
unsigned long nSentBytes = 0;
// Send an initial buffer
int nResult = WSASendMsg(this->m_hSocket, &oMessage, 0, &nSentBytes, NULL, NULL);
if (nResult == SOCKET_ERROR) {
int nErrorNo = this->getSocketLastOSErrorNo();
CString sErrorMsg = this->getSocketLastOSErrorString(nErrorNo);
closesocket(this->m_hSocket);
return false;
}
return true;
}
-3.But source ip address is not changed due to https://security.stackexchange.com/questions/184001/is-ip-spoofing-possible-in-windows-desktop-with-user-privileges

IPv6 multicast not working in LAN

I tried to send an IPv6 multicast packet through my network. The sending seems to work, since it arrives on the destination PC - at least it appears in the logged network traffic in WireShark. But it does not arrive in my server program. When I send a packet from the same PC that should receive it, it does work though.
This is the code for sending (removed error checking for better readability):
UDPBroadcastSocket = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
BOOL Yes = 1;
setsockopt(UDPBroadcastSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&Yes, sizeof(BOOL));
int32_t hops = 50;
setsockopt(UDPBroadcastSocket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char*)&hops, sizeof(hops));
uint32_t IF = 0;
setsockopt(UDPBroadcastSocket, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char*)&IF, sizeof(IF));
struct sockaddr_in6 sock_in;
struct addrinfo *result = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_NUMERICHOST;
getaddrinfo("FF18::1243", "12346", &hints, &result);
unsigned char buffer[MAXBUF];
int PacketSize = 8;
int sinlen = int(result->ai_addrlen);
memcpy(&sock_in, result->ai_addr, result->ai_addrlen);
freeaddrinfo(result);
sendto(UDPBroadcastSocket, (char*)buffer, PacketSize, 0, (sockaddr *)&sock_in, sinlen);
And this is the code for receiving the packet (removed error checking for better readability):
std::vector<uint32_t> GetNetworkInterfaceIndices(){
std::vector<uint32_t> Result;
/* Declare and initialize variables */
DWORD dwSize = 0;
DWORD dwRetVal = 0;
unsigned int i = 0;
// Set the flags to pass to GetAdaptersAddresses
ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
// default to unspecified address family (both)
ULONG family = AF_UNSPEC;
LPVOID lpMsgBuf = NULL;
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
ULONG outBufLen = 0;
ULONG Iterations = 0;
PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;
PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;
IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL;
IP_ADAPTER_PREFIX *pPrefix = NULL;
family = AF_INET6;
// Allocate a 15 KB buffer to start with.
outBufLen = WORKING_BUFFER_SIZE;
do {
pAddresses = (IP_ADAPTER_ADDRESSES *)MALLOC(outBufLen);
if (pAddresses == NULL) {
return{ 0 };
}
dwRetVal =
GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
FREE(pAddresses);
pAddresses = NULL;
}
else {
break;
}
Iterations++;
} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));
if (dwRetVal == NO_ERROR) {
// If successful, output some information from the data we received
pCurrAddresses = pAddresses;
while (pCurrAddresses) {
Result.emplace_back(pCurrAddresses->IfIndex);
pCurrAddresses = pCurrAddresses->Next;
}
}
else {
return{ 0 };
}
if (pAddresses) {
FREE(pAddresses);
}
return Result;
}
UDPSocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
sockaddr_in6 UDP_Sock_in;
memset(&UDP_Sock_in, 0, sizeof(sockaddr_in6));
UDP_Sock_in.sin6_addr = in6addr_any;
UDP_Sock_in.sin6_port = htons(Settings::GetPort()+1);
UDP_Sock_in.sin6_family = PF_INET6;
setsockopt(UDPSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&No, sizeof(BOOL));
bind(UDPSocket, (sockaddr*)&UDP_Sock_in, sizeof(UDP_Sock_in));
ipv6_mreq BroadcastGroup;
memset(&BroadcastGroup, 0, sizeof(ipv6_mreq));
const auto IfIndices = GetNetworkInterfaceIndices();
BroadcastGroup.ipv6mr_multiaddr.u.Byte[0] = 0xFF;
BroadcastGroup.ipv6mr_multiaddr.u.Byte[1] = 0x18;
BroadcastGroup.ipv6mr_multiaddr.u.Byte[14] = 0x12;
BroadcastGroup.ipv6mr_multiaddr.u.Byte[15] = 0x43;
for (const auto& Index : IfIndices) {
BroadcastGroup.ipv6mr_interface = Index;
setsockopt(UDPSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char*)&BroadcastGroup, sizeof(ipv6_mreq));
}
socklen_t fromLength = sizeof(sockaddr_in6);
pollfd PollFd;
PollFd.events = POLLIN;
PollFd.fd = UDPSocket;
PollFd.revents = -1;
WSAPoll(&PollFd, 1, -1);
recvfrom(UDPSocket, (char*)buffer, MAXBUF, 0, (sockaddr*)&from, &fromLength);
I basically tried specifying every single network interface index and the packet still does not arrive in the server. I have no idea what could be wrong. And why does it work when sender and receiver are on the same PC? I don't understand this. Does anyone have an idea? It's not the firewall, I turned it off and nothing changed. When I specify the IP address of the receiving PC directly, it does work, too.
I found a workaround: Simply switch to IPv4, because IPv6 multicast currently doesn't seem to work on Windows (at least in my use case, see comments). My code had worked, but stopped working some time ago and I didn't notice until I tested it again.
I tried a bunch of different addresses and apparently none of them worked on 3 different devices. Then I switched to IPv4 and it simply worked - I changed only the IPv6 related stuff to their IPv4 equivalent and removed the hops and interface options on the sender.
My receiver even appeared in the multicast groups list (which you can see using the command "netsh interface ipv6 show joins"), but still didn't receive the packets sent to its address, so I concluded that it likely is a bug, since every example code I found did not work for me and I could not find any other option for setsockopt that I could have missed. Feel free to comment if you have any idea what might have caused this problem or how to fix it without switching back to the old IPv4 standard.

consideration to speedup tcp/udp connection

I'm using winsock2 library to write a SDK(Software Developement Kit) for a digital board with 1Gbps ethernet on it. the problem I'm facing is that I can not get enough transfer rate(TX/RX) through ethernet uing TCP/UDP. i rather use UDP-RX as a case study to understand each parameter than can speed up my connection. with the code below i can send data from my PC to my device in just ~350 Mbps.
Start Listening:
//Initialize winsock library
if (WSAStartup(MAKEWORD(2, 2), &m_wsa) != 0)
{
error = WSAGetLastError();
}
//Create a socket
if ((m_serversocket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
{
error = WSAGetLastError();
WSACleanup();
return;
}
int buffersize = 0x200000;
int buffersizelen = sizeof(buffersize);
setsockopt(m_serversocket, SOL_SOCKET, SO_RCVBUF, (char*)&buffersize, buffersizelen);
//Prepare the sockaddr_in structure
memset(&m_serveraddress, 0, sizeof(m_serveraddress));
m_serveraddress.sin_family = AF_INET;
inet_pton(AF_INET, SERVER_Address, &m_serveraddress.sin_addr.s_addr);
m_serveraddress.sin_port = htons(SEND_PORT_number);
// Bind the socket to any address and the specified port.
if (bind(m_serversocket, (struct sockaddr *)&m_serveraddress, sizeof(m_serveraddress)) == SOCKET_ERROR)
{
error = WSAGetLastError();
return;
}
Sending file:
sendchunk = 1000;
// Read File and fill the buffer
if ((m_err = fopen_s(&m_send_file, m_send_filename.toLocal8Bit().data(), "rb")) == 0)
{
// start to send buffer
while (!(feof(m_send_file) || m_stopsend))
{
remainsize = fread(read_data, 1, sendfilesize / count, m_send_file);
sndsize_ProgBar += remainsize;
sentsize = 0;
timer.start();
while ((remainsize >= sendchunk) && !(m_stopsend))
{
sendto(m_serversocket, read_data + sentsize, sendchunk, 0, (struct sockaddr *)&m_serveraddress, addrlengh); // 1024
sentsize += sendchunk;
remainsize -= sendchunk;
}
elapsedtimemicrosec = timer.getElapsedTimeInMicroSec();
timer.stop();
rcvRate = (float)(((remainsize + sentsize)) / elapsedtimemicrosec);
emit UpdateSendRate(rcvRate);
}
}
any suggestion to speedup connection?
P.S: i already tried different sendchunk and choose the best.

Windows Socket unable to bind on VPN IP address

I am trying to bind to particular IP which is over a VPN network and I am able to ping it, connect it and also able to telnet on particular port but my windows MFC program gives error code 10049 and I am not able to go further any help in debugging this problem will be appreciated, I am running this on Visual Studio 2012 Win 7 and remote client is running on Linux variant.
This is part of code where I am getting error basically IP address is configurable but I am hardcoding it to debug.
CStarDoc *p_doc = (CStarDoc*) lpparam;
BOOL fFlag = TRUE;
const int MAX_MSGLEN = max(sizeof(DISP_INFO_T ), sizeof(REASON_STRING_T ));
char buffer[MAX_MSGLEN];
DISP_INFO_T *p_disp_info_buffer = (DISP_INFO_T *) buffer;
DISP_INFO_T disp_info_combined; //receiving combined butter
DISP_INFO_T_1 *p_disp_info_buffer1; //receiving buffer pointer for DispInfo1
DISP_INFO_T_2 *p_disp_info_buffer2; //receiving buffer pointer for DispInfo2
int msgReceived = 0; // Initially, is 0.
// For the same msgNumber, when the program receives the first portion of buffer, set to 1,
// When the program receives both portions, set it to 0.
// When the program misses any portion for the same msgNumber, set to 0 also.
int currentMsgNum1 = 0;
int currentMsgNum2 = 0;
int err;
CString msg;
SOCKADDR_IN saUDPPortIn;
SOCKADDR_IN From;
struct addrinfo *result = NULL;
struct addrinfo *ptr = NULL;
struct addrinfo hints;
::memset( &hints,0, sizeof(hints) );
hints.ai_family = AF_UNSPEC;
//hints.ai_socktype = SOCK_DGRAM;
//hints.ai_protocol = IPPROTO_UDP;
char asideip[] = "192.168.1.129";
BOOL OtherSideIsStandby = FALSE;
static BOOL DoFirstMsg = TRUE;
// p_disp_info_combined = & disp_info_combined;
p_doc->ThreadRunning = TRUE;
p_doc->udpsocket = socket(AF_INET, SOCK_DGRAM, 0);
if (INVALID_SOCKET == p_doc->udpsocket)
{
CString msg = "Invalid socket: "+ WSAGetLastError();
AfxMessageBox(msg);
return(-1);
}
long ip = 0;
int sockbufsize = 0;
int timeout = 2000;
// This is the IP that matches the IP of the QNX machines in all but the last octet.
// Note: it is in host byte format.
int errcode = getaddrinfo(asideip,NULL,&hints,&result);
for(ptr = result;ptr != NULL ;ptr=ptr->ai_next)
{
switch (ptr->ai_family)
{
default: break;
case AF_INET :
ip = p_doc->MyIP;
saUDPPortIn.sin_family = AF_INET;
saUDPPortIn.sin_addr.s_addr = (((SOCKADDR_IN*) ptr->ai_addr)->sin_addr).s_addr;
saUDPPortIn.sin_port = htons(p_doc->port_addr );
int length = sizeof(buffer) *2;
//err = setsockopt(p_doc->udpsocket,SOL_SOCKET, SO_REUSEADDR, (char *)&fFlag, sizeof(fFlag));
//err = setsockopt(p_doc->udpsocket,SOL_SOCKET, SO_BROADCAST, (char *)&fFlag, sizeof(fFlag));
err = setsockopt(p_doc->udpsocket, SOL_SOCKET, SO_RCVBUF, (char *)&length, sizeof(length));
// Keep from hanging forever.
err = setsockopt(p_doc->udpsocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
err = bind(p_doc->udpsocket, (SOCKADDR FAR *)&saUDPPortIn, sizeof(SOCKADDR_IN));
if (err == SOCKET_ERROR)
{
int errcode = WSAGetLastError();
closesocket(p_doc->udpsocket);
/* msg.Format("Network Connectivity failed, Please Check Network. ");
AfxMessageBox(msg);
closesocket(p_doc->udpsocket);
p_doc->udpsocket = -1; // task is trying to attach to the port.
return(1);*/
}
}
}
Thanks
You can not bind to remote address and as your error shows, it is such case. You use bind system call with local IP and Port.
Here is what MSDN says about your error:
WSAEADDRNOTAVAIL 10049
Cannot assign requested address. The requested address is not valid in
its context. This normally results from an attempt to bind to an
address that is not valid for the local computer. This can also result
from connect, sendto, WSAConnect, WSAJoinLeaf, or WSASendTo when the
remote address or port is not valid for a remote computer (for
example, address or port 0).

How to detect "Over Current" event of an USB device?

I have to detect the event "over current" of an USB device.
I'm developing in a Linux system and C/C++ language.
How do I do that?
You can use uevents. Here is some tutorial.
For watching uevents you have to bind specific NETLINK_KOBJECT_UEVENT:
int create_socket()
{
int sock = -1;
int result = 0;
struct sockaddr_nl snl;
memset(&snl, 0x00, sizeof(struct sockaddr_nl));
snl.nl_family = AF_NETLINK;
snl.nl_pid = getpid();
snl.nl_groups = -1;
sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (sock != -1)
{
result = bind(sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl));
if (result < 0)
{
dbg("bind failed, exit\n");
close(sock);
sock = -1;
}
}
else
dbg("error getting socket, exit\n");
return sock;
}
int main() {
...
sock = create_socket();
while(sock != -1)
{
buflen = recv(sock, &buffer, sizeof(buffer), 0);
// parse buffer for event description
...
}
}
One of the events for sure will indicate the over-current state.
It might help you to look at the source code for your particular root port / host controller.
For example: http://www.spinics.net/lists/linux-usb/msg49451.html explains the overcurrent detection using a particular host controller driver.