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);
Related
I made a connection to BitCoin node via WinSock2. I sent the proper "getaddr" message and then the server responds, the replied data are ready to read, because Select() notifies this, but when I call Recv() there are 0 bytes read.
My code is working OK on localhost test server. The incomplete "getaddr" message (less than 24 bytes) is NOT replied by BitCoin node, only proper message, but I can't read the reply with Recv(). After returning 0 bytes, the Select() still returns there are data to read.
My code is divided into DLL which uses Winsock2 and the main() function.
Here are key fragments:
struct CMessageHeader
{
uint32_t magic;
char command[12];
uint32_t payload;
uint32_t checksum;
};
CSocket *sock = new CSocket();
int actual; /* Actually read/written bytes */
sock->connect("109.173.41.43", 8333);
CMessageHeader msg = { 0xf9beb4d9, "getaddr\0\0\0\0", 0, 0x5df6e0e2 }, rcv = { 0 };
actual = sock->send((const char *)&msg, sizeof(msg));
actual = sock->select(2, 0); /* Select read with 2 seconds waiting time */
actual = sock->receive((char *)&rcv, sizeof(rcv));
The key fragment of DLL code:
int CSocket::receive(char *buf, int len)
{
int actual;
if ((actual = ::recv(sock, buf, len, 0)) == SOCKET_ERROR) {
std::ostringstream s;
s << "Nie mozna odebrac " << len << " bajtow.";
throw(CError(s));
}
return(actual);
}
If select() reports the socket is readable, and then recv() returns 0 afterwards, that means the peer gracefully closed the connection on their end (ie, sent a FIN packet to you), so you need to close your socket.
On a side note, recv() can return fewer bytes than requested, so your receive() function should call recv() in a loop until all of the expected bytes have actually been received, or an error occurs (same with send(), too).
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.
I heard that PSM is a library supporting tag-matching.
What is Tag-matching interface? Why is tag-matching important for performance in the context of MPI?
Short intro in tag matching for MPI: https://www.hpcwire.com/2006/08/18/a_critique_of_rdma-1/ section "Matching"
MPI is a two-sided interface with a large matching space: a MPI Recv is associated with a MPI Send according to several criteria such as Sender, Tag, and Context, with the first two possibly ignored (wildcard). Matching is not necessarily in order and, worse, a MPI Send can be posted before the matching MPI Recv ... MPI requires 64 bits of matching information, and MX, Portals, and QsNet provide such a matching capability.
InfiniBand Verbs and other RDMA-based APIs do not support matching at all
So, it sounds like PSM is way to include fast matching to Infiniband-style network adapters (first versions with software matching, but with possibility of moving part of matching to the hardware).
I can't find public documentation of PSM (there are no details in User Guide http://storusint.com/pdf/qlogic/InfiniPath%20User%20Guide%202_0.pdf).
But there are sources of the library: https://github.com/01org/psm
Some details are listed in PSM2 presentation https://www.openfabrics.org/images/eventpresos/2016presentations/304PSM2Features.pdf
What is PSM?
Matched Queue (MQ) component
• Semantically matched to the needs of MPI using tag matching
• Provides calls for communication progress guarantees
• MQ completion semantics (standard vs. synchronized)
PSM API
• Global tag matching API with 64-bit tags
• Scale up to 64K processes per job
• MQ APIs provide point-to-point message passing between endpoints
• e.g. psm_mq_send, psm_mq_irecv
• No “recvfrom” functionality – needed by some applications
So, there are 64-bit tags. Every message has a tag, and Matched Queue has tag (in some tag matching implementations there is also tag mask). According to the source psm_mq_internal.h: mq_req_match() https://github.com/01org/psm/blob/67c0807c74e9d445900d5541358f0f575f22a630/psm_mq_internal.h#L381, there is mask in PSM:
typedef struct psm_mq_req {
...
/* Tag matching vars */
uint64_t tag;
uint64_t tagsel; /* used for receives */
...
} psm_mq_req_t;
mq_req_match(struct mqsq *q, uint64_t tag, int remove)
)
{
psm_mq_req_t *curp;
psm_mq_req_t cur;
for (curp = &q->first; (cur = *curp) != NULL; curp = &cur->next) {
if (!((tag ^ cur->tag) & cur->tagsel)) { /* match! */
if (remove) {
if ((*curp = cur->next) == NULL) /* fix tail */
q->lastp = curp;
cur->next = NULL;
}
return cur;
}
}
So, match is when the incoming tag is xored with tag of receives, posted to the MQ, result anded with tagsel of receive. If after these operations there are only zero bits, the match is found, else next receive is processed.
Comment from psm_mq.h, psm_mq_irecv() function, https://github.com/01org/psm/blob/4abbc60ab02c51efee91575605b3430059f71ab8/psm_mq.h#L206
/* Post a receive to a Matched Queue with tag selection criteria
*
* Function to receive a non-blocking MQ message by providing a preposted
* buffer. For every MQ message received on a particular MQ, the tag and #c
* tagsel parameters are used against the incoming message's send tag as
* described in tagmatch.
*
* [in] mq Matched Queue Handle
* [in] rtag Receive tag
* [in] rtagsel Receive tag selector
* [in] flags Receive flags (None currently supported)
* [in] buf Receive buffer
* [in] len Receive buffer length
* [in] context User context pointer, available in psm_mq_status_t
* upon completion
* [out] req PSM MQ Request handle created by the preposted receive, to
* be used for explicitly controlling message receive
* completion.
*
* [post] The supplied receive buffer is given to MQ to match against incoming
* messages unless it is cancelled via psm_mq_cancel #e before any
* match occurs.
*
* The following error code is returned. Other errors are handled by the PSM
* error handler (psm_error_register_handler).
*
* [retval] PSM_OK The receive buffer has successfully been posted to the MQ.
*/
psm_error_t
psm_mq_irecv(psm_mq_t mq, uint64_t rtag, uint64_t rtagsel, uint32_t flags,
void *buf, uint32_t len, void *context, psm_mq_req_t *req);
Example of encoding data into tag:
* uint64_t tag = ( ((context_id & 0xffff) << 48) |
* ((my_rank & 0xffff) << 32) |
* ((send_tag & 0xffffffff)) );
With tagsel mask we can encode both "match everything", "match tags with some bytes or bits equal to value, and anything in other", "match exactly".
There is newer PSM2 API, open source too - https://github.com/01org/opa-psm2, programmer's guide published at http://www.intel.com/content/dam/support/us/en/documents/network/omni-adptr/sb/Intel_PSM2_PG_H76473_v1_0.pdf.
In PSM2 tags are longer, and the matching rule is defined (stag is "Message Send Tag" - the tag value sent in message, and rtag is tag of receive request):
https://www.openfabrics.org/images/eventpresos/2016presentations/304PSM2Features.pdf#page=7
Tag matching improvement
• Increased tag size to 96 bits
• Fundamentally ((stag ^ rtag) & rtagsel) == 0
• Supports wildcards such as MPI_ANY_SOURCE or MPI_ANY_TAG using zero bits in rtagsel
• Allows for practically unlimited scalability
• Up to 64M processes per job
PSM2 TAG MATCHING
#define PSM_MQ_TAG_ELEMENTS 3
typedef
struct
psm2_mq_tag {
union {
uint32_t tag[PSM_MQ_TAG_ELEMENTS] __attribute__((aligned(16)));
struct {
uint32_t tag0;
uint32_t tag1;
uint32_t tag2;
};
};
} psm2_mq_tag_t;
• Application fills ‘tag’ array or ‘tag0/tag1/tag2’ and passes to PSM
• Both tag and tag mask use the same 96 bit tag type
And actually there is source peer address near matching variables in psm2_mq_req struct: https://github.com/01org/opa-psm2/blob/master/psm_mq_internal.h#L180
/* Tag matching vars */
psm2_epaddr_t peer;
psm2_mq_tag_t tag;
psm2_mq_tag_t tagsel; /* used for receives */
And software list scanning for match, mq_list_scan() called from mq_req_match() https://github.com/01org/opa-psm2/blob/85c07c656198204c4056e1984779fde98b00ba39/psm_mq_recv.c#L188:
psm2_mq_req_t
mq_list_scan(struct mqq *q, psm2_epaddr_t src, psm2_mq_tag_t *tag, int which, uint64_t *time_threshold)
{
psm2_mq_req_t *curp, cur;
for (curp = &q->first;
((cur = *curp) != NULL) && (cur->timestamp < *time_threshold);
curp = &cur->next[which]) {
if ((cur->peer == PSM2_MQ_ANY_ADDR || src == cur->peer) &&
!((tag->tag[0] ^ cur->tag.tag[0]) & cur->tagsel.tag[0]) &&
!((tag->tag[1] ^ cur->tag.tag[1]) & cur->tagsel.tag[1]) &&
!((tag->tag[2] ^ cur->tag.tag[2]) & cur->tagsel.tag[2])) {
*time_threshold = cur->timestamp;
return cur;
}
}
return NULL;
}
I am trying to interface with a Devantech digital compass found here -
http://www.acroname.com/robotics/parts/R117-COMPASS.html
I am using a i2c-usb converter to plug it into my laptop -
http://www.robot-electronics.co.uk/htm/usb_i2c_tech.htm
First of all, I do not know much about electrical engineering. I have a good idea of the bare basics, but after that I get lost.
I am trying to follow this tutorial -
https://xgoat.com/wp/2007/11/11/using-i2c-from-userspace-in-linux/
However I get stuck at the very beginning when I try to set the device address.
if( ioctl( fd, I2C_SLAVE, ADDRESS ) < 0 )
{
fprintf( stderr, "Failed to set slave address: %m\n" );
return 2;
}
returns "Failed to set slave address: Invalid argument"
I originally thought the address should be 0xC0 because a sentence in the manual for the compass reads "First send a start bit, the module address (0xC0)..." but that did not work.
Now I have a loop that just goes from 1 to 100 and tries each one for the address, but they all fail. The loop is -
for(int i=0x0;i<0x100;i++) {
if( ioctl( fd, I2C_SLAVE, i ) < 0 )
fprintf( stderr, "Failed to set slave address for address %i: %m\n", i );
}
I'm not sure what else to try. Right now, I just want to set the address so I can start attempting to read and write. Since the converter is what is actually connected to the pc, should I be using the address for that? And if so, where can I find it on that link with the information for it? If someone has an idea of what I could try or what is wrong that would be great.
EDIT:
Okay I have the code like this now -
#define ADDRESS 0x55
int fd = open("/dev/i2c-0", O_RDWR);
if (fd < 0) {
printf("\n<0, %m", errno);
return -1;
}
if( ioctl( fd, I2C_SLAVE, ADDRESS ) < 0 ) {
fprintf( stderr, "Failed to set slave address: %m\n" );
return 2;
}
if( i2c_smbus_write_byte( fd, 0xAA ) < 0 )
fprintf( stderr, "Failed to write 0xAA to I2C device: %m\n" );
It will set the address, but it won't write anything. Whenever I try to write to it, I get -
Failed to write 0xAA to I2C device: No such device or address
Why are your trying to send 0xAA to the device ?
To my understanding this is not a register for it (for the CMPS03, the only command is register 15 and its usage is not common: change the I2C address, factory reset, ...).
And 0x55 seems definitively not the address of the device ... 0xC0 should be the write one.
Could be that /dev/i2c-0 has not been created properly / is not correct?
How did you get /dev/ic2-0 created ?
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.