I'm trying to experiment with using splice (man 2 splice) to copy data from a UDP socket directly to a file. Unfortunately the first call to splice() returns EINVAL.
The man page states:
EINVAL Target file system doesn't support splicing; target file is opened in
append mode; neither of the descriptors refers to a pipe; or offset
given for nonseekable device.
However, I believe none of those conditions apply. I'm using Fedora 15 (kernel 2.6.40-4) so I believe splice() is supported on all filesystems. The target file should be irrelevant in the first call to splice, but for completeness I'm opening it via open(path, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR). Both calls use a pipe and neither call uses an offset besides NULL.
Here's my sample code:
int sz = splice(sock_fd, 0, mPipeFds[1], 0, 8192, SPLICE_F_MORE);
if (-1 == sz)
{
int err = errno;
LOG4CXX_ERROR(spLogger, "splice from: " << strerror(err));
return 0;
}
sz = splice(mPipeFds[0], 0, file_fd, 0, sz, SPLICE_F_MORE);
if (-1 == sz)
{
int err = errno;
LOG4CXX_ERROR(spLogger, "splice to: " << strerror(err));
}
return 0;
sock_fd is initialized by the following psuedocode:
int sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK);
bind(sock_fd, ...);
Possibly related is that this code snippet is running inside a libevent loop. libevent is using epoll() to determine if the UDP socket is hot.
Found my answer. tl;dr - UDP isn't supported on the inbound side.
After enough Googling I stumbled upon a forum discussion and some test code which prints out a table of in/out fd types and their support:
$ ./a.out
in\out pipe reg chr unix tcp udp
pipe yes yes yes yes yes yes
reg yes no no no no no
chr yes no no no no no
unix no no no no no no
tcp yes no no no no no
udp no no no no no no
Yeah, it is definitely not supported for reading from a UDP socket, even in the latest kernels. References to the kernel source follow.
splice invokes do_splice in the kernel, which calls do_splice_to, which calls the splice_read member in the file_operations structure for the file.
For sockets, that structure is defined as socket_file_ops in net/socket.c, which initializes the splice_read field to sock_splice_read.
That function, in turn, contains this line of code:
if (unlikely(!sock->ops->splice_read))
return -EINVAL;
The ops field of the socket is a struct proto_ops. For an IPv4 UDP socket, it is initialized to inet_dgram_ops in net/ipv4/af_inet.c. Finally, that structure does not explicitly initialize the splice_read field of struct proto_ops; i.e., it initializes it to zero.
So sock_splice_read returns -EINVAL, and that propagates up.
Related
I'm still a bit new to c++ and I'm having trouble understanding what this piece of code is doing:
#include <net/sock.h>
BEGIN
{
printf("%-8s %-6s %-16s %-2s %-16s %-5s\n", "TIME", "PID", "COMM",
"IP", "RADDR", "RPORT");
}
kprobe:ip4_datagram_connect,
kprobe:ip6_datagram_connect
{
$sk = (struct sock *)arg0;
$sa = (struct sockaddr *)arg1;
if (($sa->sa_family == AF_INET || $sa->sa_family == AF_INET6) &&
$sk->sk_protocol == IPPROTO_UDP) {
time("%H:%M:%S ");
if ($sa->sa_family == AF_INET) {
$s = (struct sockaddr_in *)arg1;
$port = ($s->sin_port >> 8) |
(($s->sin_port << 8) & 0xff00);
printf("%-6d %-16s 4 %-16s %-5d\n", pid, comm,
ntop(AF_INET, $s->sin_addr.s_addr), $port);
} else {
$s6 = (struct sockaddr_in6 *)arg1;
$port = ($s6->sin6_port >> 8) |
(($s6->sin6_port << 8) & 0xff00);
printf("%-6d %-16s 6 %-16s %-5d\n", pid, comm,
ntop(AF_INET6, $s6->sin6_addr.in6_u.u6_addr8),
$port);
}
}
}
I think the "BEGIN" portion of this is defining a macro, but I'm not fully sure. What I'm really confused about is the
kprobe:ip4_datagram_connect,
kprobe:ip6_datagram_connect
{
What is this doing? It seems like a function declaration, but what does the single colon mean in this context? Is this some sort initialization list but for functions? Is this setting both ip4 and ip6 to that function?
Also, do the dollar sign variable names have any sort of significance? or are they just a valid way of declaring variables?
This is the code for udpconnect.bt from bcc tools. I'm try to convert it to python.
As mentioned in the comments, this is a script for bpftrace. You probably want to look at the reference guide for this tool.
The BEGIN block is run at the start of your program (just like with awk scripts, if you are familiar with it). In this case it is used to print the header row for an array to the console output.
Then the block:
kprobe:ip4_datagram_connect,
kprobe:ip6_datagram_connect
{
... defines instructions that will be translated to an eBPF program (with an intermediary step as LLVM intermediate representation), and attached to one or several hooks in the Linux kernel. In the current case, the kprobe:... define the hooks: The program will run as a kprobe function for the functions ip4_datagram_connect and its IPv6 counterpart in the kernel. In other words, it will run each time the kernel enters those functions.
On a quick look, I think the program is supposed to print, each time a UDP flow starts, the time, PID and name of the process opening the socket, the IP address, remote address and remote port. The first if checks that the packet is (IPv4 or IPv6) and UDP, the if/else below splits the two cases IPv4/IPv6.
Edit: I solved UART communication problem but I have new problem getting pwm signal after receiving Transmit Data. I can blink led I can drive relay with transmitted data but I could not produce PWM signal.
maps(120, 1, 1, 250, RxData[4]);
ADC_Left = Yx; __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,ADC_Left);
I used __HAL_TIM_SET_COMPARE function but it doesnt work. I can observe ADC_Left’s value on Debug site but its not work.
I am trying to realize UART communication between 2 stm32. I know there are several topic related with but my question focused another one.
I am reading 2 adc value on stm32 which is only transmit these value and other one only receive these 2 adc value. To do this
MX_USART1_UART_Init();
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); // Interrupt Enable
__HAL_UART_ENABLE_IT(&huart1, UART_IT_TC);
char TxData1[10];
..............
TxData1[0] = 0xEA;
TxData1[1] = wData.Byte_1;
TxData1[2] = wData.Byte_2;
TxData1[3] = wData.Byte_3;
TxData1[4] = wData.Right_Adc_Val;
TxData1[5] = wData.Left_Adc_Val;
TxData1[6] = wData.Byte_6;
for(uint8_t i = 1 ; i < 7; i++)
{
wData.Checksum = wData.Checksum + TxData1[i];
}
wData.Checksum_H = (wData.Checksum >> 8)&0xFF;
wData.Checksum_L = (wData.Checksum)&0xFF;
TxData1[7] = wData.Checksum_H;
TxData1[8] = wData.Checksum_L;
TxData1[9] = 0xAE;
HAL_UART_Transmit_IT(&huart1,(uint8_t*) &TxData1,10);
............
This block sent them I can observate them on Debug screen and using TTL module's Tx Rx pins.
MX_USART1_UART_Init();
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); // Interrupt Enable
__HAL_UART_ENABLE_IT(&huart1, UART_IT_TC);
char RxData[10];
while(1){
HAL_UART_Receive_IT(&huart1,(uint8_t*) &RxData,10);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
HAL_UART_Receive_IT(&huart1,(uint8_t*) &RxData,10);
}
There is no problem up to here but when i getting RxData 0. index , it gives EA . Of course it should be give EA. When the adc data change all the ranking is changing. RxData[0] gives meaningless data. adc value is jumping over the all RxData array.
data locations must always be in the same index. How Can I get these data in stability for ex.
RxData[0]=EA
.
.
RxData[4]= should give adc value. so on.
..
Edit: I tried other mode of UART, DMA (in circular mode) and direct mode were used. I cant receive even 1 byte with DMA .
In your example code, you have an extra & that needs to be removed from both the transmit and receive HAL method calls. Example:
HAL_UART_Transmit_IT(&huart1,(uint8_t*) &TxData1,10);
HAL_UART_Transmit_IT(&huart1,(uint8_t*) TxData1,10);
To avoid this type of error in the future, recommend not using the cast and try something like the following:
uint8_t TxData1[10];
...
HAL_UART_Transmit_IT(&huart1, TxData1, sizeof(TxData1);
CONTEXT
I'm using a code written to work with a GPS module that connects to the Arduino through serial communication. The module starts each packet with a header (0xb5, 0x62), continues with the information you requested and ends with to bytes of checksum, CK_A, and CK_B. I don't understand the code that calculates that checksum. More info about the algorithm of checksum (8-Bit Fletcher Algorithm) in the module protocol (https://www.u-blox.com/sites/default/files/products/documents/u-blox7-V14_ReceiverDescriptionProtocolSpec_%28GPS.G7-SW-12001%29_Public.pdf), page 74 (87 with index).
MORE INFO
Just wanted to understand the code, it works fine. In the UBX protocol, I mentioned there is also a piece of code that explains how it works (isn't write in c++)
struct NAV_POSLLH {
//Here goes the struct
};
NAV_POSLLH posllh;
void calcChecksum(unsigned char* CK) {
memset(CK, 0, 2);
for (int i = 0; i < (int)sizeof(NAV_POSLLH); i++) {
CK[0] += ((unsigned char*)(&posllh))[i];
CK[1] += CK[0];
}
}
In the link you provide, you can find a link to RFC 1145, containing that Fletcher 8 bit algorithm as well and explaining
It can be shown that at the end of the loop A will contain the 8-bit
1's complement sum of all octets in the datagram, and that B will
contain (n)*D[0] + (n-1)*D[1] + ... + D[n-1].
n = sizeof byte D[];
Quote adjusted to C syntax
Try it with a couple of bytes, pen and paper, and you'll see :)
The standard says that:
The perror() function shall not change the orientation of the standard error stream.
This is the implementation of perror() in GNU libc.
Following are the tests when stderr is wide-oriented, multibyte-oriented and not oriented, prior to calling perror().
Tests 1) and 2) are OK. The issue is in test 3).
1) stderr is wide-oriented:
#include <stdio.h>
#include <wchar.h>
#include <errno.h>
int main(void)
{
fwide(stderr, 1);
errno = EINVAL;
perror("");
int x = fwide(stderr, 0);
printf("fwide: %d\n",x);
return 0;
}
$ ./a.out
Invalid argument
fwide: 1
$ ./a.out 2>/dev/null
fwide: 1
2) stderr is multibyte-oriented:
#include <stdio.h>
#include <wchar.h>
#include <errno.h>
int main(void)
{
fwide(stderr, -1);
errno = EINVAL;
perror("");
int x = fwide(stderr, 0);
printf("fwide: %d\n",x);
return 0;
}
$ ./a.out
Invalid argument
fwide: -1
$ ./a.out 2>/dev/null
fwide: -1
3) stderr is not oriented:
#include <stdio.h>
#include <wchar.h>
#include <errno.h>
int main(void)
{
printf("initial fwide: %d\n", fwide(stderr, 0));
errno = EINVAL;
perror("");
int x = fwide(stderr, 0);
printf("fwide: %d\n", x);
return 0;
}
$ ./a.out
initial fwide: 0
Invalid argument
fwide: 0
$ ./a.out 2>/dev/null
initial fwide: 0
fwide: -1
Why perror() changes orientation of stream if it is redirected? Is it proper behavior?
How does this code work? What is this __dup trick all about?
TL;DR: Yes, it's a bug in glibc. If you care about it, you should report it.
The quoted requirement that perror not change the stream orientation is in Posix, but does not seem to be required by the C standard itself. However, Posix seems quite insistent that the orientation of stderr not be changed by perror, even if stderr is not yet oriented. XSH 2.5 Standard I/O Streams:
The perror(), psiginfo(), and psignal() functions shall behave as described above for the byte output functions if the stream is already byte-oriented, and shall behave as described above for the wide-character output functions if the stream is already wide-oriented. If the stream has no orientation, they shall behave as described for the byte output functions except that they shall not change the orientation of the stream.
And glibc attempts to implement Posix semantics. Unfortunately, it doesn't quite get it right.
Of course, it is impossible to write to a stream without setting its orientation. So in an attempt to comply with this curious requirement, glibc attempts to make a new stream based on the same fd as stderr, using the code pointed to at the end of the OP:
58 if (__builtin_expect (_IO_fwide (stderr, 0) != 0, 1)
59 || (fd = __fileno (stderr)) == -1
60 || (fd = __dup (fd)) == -1
61 || (fp = fdopen (fd, "w+")) == NULL)
62 { ...
which, stripping out the internal symbols, is essentially equivalent to:
if (fwide (stderr, 0) != 0
|| (fd = fileno (stderr)) == -1
|| (fd = dup (fd)) == -1
|| (fp = fdopen (fd, "w+")) == NULL)
{
/* Either stderr has an orientation or the duplication failed,
* so just write to stderr
*/
if (fd != -1) close(fd);
perror_internal(stderr, s, errnum);
}
else
{
/* Write the message to fp instead of stderr */
perror_internal(fp, s, errnum);
fclose(fp);
}
fileno extracts the fd from a standard C library stream. dup takes an fd, duplicates it, and returns the number of the copy. And fdopen creates a standard C library stream from an fd. In short, that doesn't reopen stderr; rather, it creates (or attempts to create) a copy of stderr which can be written to without affecting the orientation of stderr.
Unfortunately, it doesn't work reliably because of the mode:
fp = fdopen(fd, "w+");
That attempts to open a stream which allows both reading and writing. And it will work with the original stderr, which is just a copy of the console fd, originally opened for both reading and writing. But when you bind stderr to some other device with a redirect:
$ ./a.out 2>/dev/null
you are passing the executable an fd opened only for output. And fdopen won't let you get away with that:
The application shall ensure that the mode of the stream as expressed by the mode argument is allowed by the file access mode of the open file description to which fildes refers.
The glibc implementation of fdopen actually checks, and returns NULL with errno set to EINVAL if you specify a mode which requires access rights not available to the fd.
So you could get your test to pass if you redirect stderr for both reading and writing:
$ ./a.out 2<>/dev/null
But what you probably wanted in the first place was to redirect stderr in append mode:
$ ./a.out 2>>/dev/null
and as far as I know, bash does not provide a way to read/append redirect.
I don't know why the glibc code uses "w+" as a mode argument, since it has no intention of reading from stderr. "w" should work fine, although it probably won't preserve append mode, which might have unfortunate consequences.
I'm not sure if there's a good answer to "why" without asking the glibc developers - it may just be a bug - but the POSIX requirement seems to conflict with ISO C, which reads in 7.21.2, ¶4:
Each stream has an orientation. After a stream is associated with an external file, but before any operations are performed on it, the stream is without orientation. Once a wide character input/output function has been applied to a stream without orientation, the stream becomes a wide-oriented stream. Similarly, once a byte input/output function has been applied to a stream without orientation, the stream becomes a byte-oriented stream. Only a call to the freopen function or the fwide function can otherwise alter the orientation of a stream. (A successful call to freopen removes any orientation.)
Further, perror seems to qualify as a "byte I/O function" since it takes a char * and, per 7.21.10.4 ¶2, "writes a sequence of characters".
Since POSIX defers to ISO C in the event of a conflict, there is an argument to be made that the POSIX requirement here is void.
As for the actual examples in the question:
Undefined behavior. A byte I/O function is called on a wide-oriented stream.
Nothing at all controversial. The orientation was correct for calling perror and did not change as a result of the call.
Calling perror oriented the stream to byte orientation. This seems to be required by ISO C but disallowed by POSIX.
I have used the ICMP example provided in the ASIO documentation to create a simple ping utility. However, the example covers IPv4 only and I have a hard time to make it work with IPv6.
Upgrading the ICMP header class to support IPv6 requires a minor change - the only difference between ICMP and ICMPv6 header is the different enumeration of ICMP types. However, I have a problem computing the checksum that needs to be incorporated in the ICMPv6 header.
For IPv4 the checksum is based on the ICMP header and payload. However, for IPv6 the checksum should include the IPv6 pseudo-header before the ICMPv6 header and payload. The ICMPv6 checksum function needs to know the source and destination address that will be in the IPv6 header. However, we have no control over what goes into the IPv6 header. How can this be done in Asio-Boost?
For reference please find below the function for IPv4 checksum calculation.
void compute_checksum(icmp_header& header, Iterator body_begin, Iterator body_end)
{
unsigned int sum = (header.type() << 8) + header.code()
+ header.identifier() + header.sequence_number();
Iterator body_iter = body_begin;
while (body_iter != body_end)
{
sum += (static_cast<unsigned char>(*body_iter++) << 8);
if (body_iter != body_end)
sum += static_cast<unsigned char>(*body_iter++);
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
header.checksum(static_cast<unsigned short>(~sum));
}
[EDIT]
What are the consequences if the checksum is not calculated correctly? Will the target host send echo reply if the echo request has invalid checksum?
If the checksum is incorrect, a typical IPv6 implementation will drop the packet. So, it is a serious issue.
If you insist on crafting the packet yourself, you'll have to do it
completely. This incldues finding the source IP address, to put it in
the pseudo-header before computing the checksum. Here is how I do it
in C, by calling connect() for my intended destination address
(even when I use UDP, so it should work for ICMP):
/* Get the source IP addresse chosen by the system (for verbose display, and
* for checksumming) */
if (connect(sd, destination->ai_addr, destination->ai_addrlen) < 0) {
fprintf(stderr, "Cannot connect the socket: %s\n", strerror(errno));
abort();
}
source = malloc(sizeof(struct addrinfo));
source->ai_addr = malloc(sizeof(struct sockaddr_storage));
source_len = sizeof(struct sockaddr_storage);
if (getsockname(sd, source->ai_addr, &source_len) < 0) {
fprintf(stderr, "Cannot getsockname: %s\n", strerror(errno));
abort();
}
then, later:
sockaddr6 = (struct sockaddr_in6 *) source->ai_addr;
op6.ip.ip6_src = sockaddr6->sin6_addr;
and:
op6.udp.check =
checksum6(op6.ip, op6.udp, (u_int8_t *) & message, messagesize);