ISCSI Becomes Much More Slower After An Intermediate Layer - c

I'm new to iSCSI protocol and I have a task to catch and interpret the commands between QEMU and iSCSI server when a VM is boot from iSCSI drive.
I setup tgtd on Ubuntu and managed to install a guest OS into a disk image created by qemu-img with a format of raw, which is the backing storage of tgtd.
To achieve my target, I decided to write a transparent layer between QEMU and tgtd, which performs as a server for QEMU and a client for tgtd. After a brief reading of RFC3720, I wrote my transparent layer with the core code as
while(1) {
rt_val = recv(server_conn_sock, BHS, 48, 0);
unsigned int TotalAHSLength = (unsigned int)BHS[4];
unsigned int DataSegmentLength = ((unsigned int)BHS[5] << 16) | ((unsigned int)BHS[6] << 8) | (unsigned int)BHS[7];
unsigned int DataSegmentLengthWithPadding = DataSegmentLength % 4 ? DataSegmentLength + 4 - (DataSegmentLength % 4) : DataSegmentLength;
if(TotalAHSLength > 0) {
rt_val = recv(server_conn_sock, AHS, 4 * TotalAHSLength, 0);
}
if(DataSegmentLengthWithPadding > 0) {
rt_val = recv(server_conn_sock, DataSegment, DataSegmentLengthWithPadding, 0);
}
rt_val = send(client_sock, BHS, 48, 0);
if(TotalAHSLength > 0) {
rt_val = send(client_sock, AHS, 4 * TotalAHSLength, 0);
}
if(DataSegmentLengthWithPadding > 0) {
rt_val = send(client_sock, DataSegment, DataSegmentLengthWithPadding, 0);
}
rt_val = recv(client_sock, BHS, 48, 0);
TotalAHSLength = (unsigned int)BHS[4];
DataSegmentLength = ((unsigned int)BHS[5] << 16) | ((unsigned int)BHS[6] << 8) | (unsigned int)BHS[7];
DataSegmentLengthWithPadding = DataSegmentLength % 4 ? DataSegmentLength + 4 - (DataSegmentLength % 4) : DataSegmentLength;
if(TotalAHSLength > 0) {
rt_val = recv(client_sock, AHS, 4 * TotalAHSLength, 0);
}
if(DataSegmentLengthWithPadding > 0) {
rt_val = recv(client_sock, DataSegment, DataSegmentLengthWithPadding, 0);
}
send(server_conn_sock, BHS, 48, 0);
if(TotalAHSLength > 0) {
send(server_conn_sock, AHS, 4 * TotalAHSLength, 0);
}
if(DataSegmentLengthWithPadding > 0) {
send(server_conn_sock, DataSegment, DataSegmentLengthWithPadding, 0);
}
}
The code is basically forwarding everything between QEMU and tgtd. However, I found that the speed of QEMU to boot the guest OS is more than 100 times slower compared with booting with a direct connection to tgtd. My task required my intermediate layer not to affect the performance of QEMU.
So, what could be a possible reason for the difference in speed of reading disk?

Related

interrupts not working in KVM x86 16-bit guest

I'm writing a Linux KVM hypervisor for x86 16-bit guests running in real mode. When doing interrupt calls (int ... instruction), I've encountered the KVM_INTERNAL_ERROR_SIMUL_EX error on Linux kernel 3.13.0. The same code is running fine on Linux kernel 3.16.0. Am I missing something? Is there a workaround I can add to my code to make it work with Linux kernel 3.13.0 (and possibly earlier)?
The test guest calls int 0x18 ... int 0x4f, all of which is handled in the hypervisor (C code after KVM_RUN has returned). When it's working correctly, all of the interrupt calls work. On Linux kernel 3.13.0, int 0x21 starts failing (and then int 0x22, int 0x23 and int 0x24 would also fail).
I was trying to write the shortest example C code to demonstrate the problem, here it is:
/* Based on: https://gist.github.com/zserge/d68683f17c68709818f8baab0ded2d15
* Based on: https://gist.githubusercontent.com/zserge/d68683f17c68709818f8baab0ded2d15/raw/b79033254b092ec9121bb891938b27dd128030d7/kvm-host-simple.c
*
* Compile: gcc -ansi -pedantic -s -O2 -W -Wall -o kvm16 kvm16.c && ./kvm16
*
* Expected correct output (e.g. on Linux 3.16.0 compiled for i386 (i686)):
*
* ...
* info: int 0x4f iret to: ...
* info: success, exiting
*
* Failure output (e.g. on Linux 3.13.0 compiled for amd64 (x86_64)):
*
* info: int 0x20 iret to: cs=0x0070 ip=0x0013
* fatal: KVM internal error suberror=2
*
* // Encounter unexpected simultaneous exceptions.
* #define KVM_INTERNAL_ERROR_SIMUL_EX 2
*/
#define _GNU_SOURCE
#include <fcntl.h>
#include <linux/kvm.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#define INT_NUM 0x21 /* Also works for INT_NUM == 0x20. */
int main(int argc, char *argv[]) {
int kvm_fd, vm_fd, vcpu_fd;
void *mem;
struct kvm_userspace_memory_region region;
struct kvm_run *run;
struct kvm_regs regs;
struct kvm_sregs sregs;
(void)argc; (void)argv;
if ((kvm_fd = open("/dev/kvm", O_RDWR)) < 0) {
perror("failed to open /dev/kvm");
return 1;
}
if ((vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0)) < 0) {
perror("failed to create vm");
return 1;
}
if ((mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0)) == NULL) {
perror("mmap");
return 1;
}
memset(&region, 0, sizeof(region));
region.slot = 0;
region.guest_phys_addr = 0;
region.memory_size = 0x1000;
region.userspace_addr = (uintptr_t)mem;
if (ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, &region) < 0) {
perror("KVM_SET_USER_MEMORY_REGION");
return 1;
}
{ /* 8086 real mode machine code. */
char *p = (char*)mem + 0x700;
unsigned int_num;
for (int_num = 0; int_num < 0x100; ++int_num) {
*(unsigned short*)((char*)mem + int_num * 4) = int_num; /* Interrupt vector INT_NUM offset := INT_NUM. */
*(unsigned short*)((char*)mem + int_num * 4 + 2) = 0x54; /* Interrupt vector INT_NUM segment := 0x54. */
}
*p++ = (char)0xf4; /* hlt. */
for (int_num = 0x18; int_num < 0x50; ++int_num) {
*p++ = (char)0xcd; /* int int_num. */
*p++ = (char)int_num;
}
*p++ = (char)0xf4;
}
memset((char*)mem + 0x540, '\xf4', 0x100); /* 256 times hlt. Interrupt vectors point here. */
if ((vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0)) < 0) {
perror("KVM_CREATE_VCPU");
return 1;
}
{
int kvm_run_mmap_size = ioctl(kvm_fd, KVM_GET_VCPU_MMAP_SIZE, 0);
if (kvm_run_mmap_size < 0) {
perror("KVM_GET_VCPU_MMAP_SIZE");
return 1;
}
run = (struct kvm_run *)mmap(
NULL, kvm_run_mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpu_fd, 0);
if (run == NULL) {
perror("mmap kvm_run");
return 1;
}
}
memset(&regs, '\0', sizeof(regs));
if (ioctl(vcpu_fd, KVM_GET_SREGS, &sregs) < 0) {
perror("KVM_GET_SREGS");
return 1;
}
{
int fd = open("kvm16.sregs", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd < 0) {
perror("open");
return 1;
}
if (write(fd, &sregs, sizeof(sregs)) != sizeof(sregs)) {
perror("write");
return 1;
}
if (close(fd) != 0) {
perror("close");
return 1;
}
}
sregs.cs.base = (sregs.cs.selector = 0x70) << 4;
sregs.ds.base = (sregs.ds.selector = sregs.cs.selector) << 4;
sregs.es.base = (sregs.es.selector = sregs.cs.selector) << 4;
sregs.ss.base = (sregs.ss.selector = sregs.cs.selector) << 4;
if (ioctl(vcpu_fd, KVM_GET_REGS, &regs) < 0) {
perror("KVM_GET_REGS");
return 1;
}
regs.rflags = 1 << 1; /* Reserved bit in EFLAGS. Even needed after KVM_GET_REGS. */
regs.rip = 0;
regs.rsp = 0x1000 - 0x700;
if (ioctl(vcpu_fd, KVM_SET_SREGS, &sregs) < 0) {
perror("KVM_SET_SREGS");
return 1;
}
if (ioctl(vcpu_fd, KVM_SET_REGS, &regs) < 0) {
perror("KVM_SET_REGS");
return 1;
}
for (;;) {
int ret = ioctl(vcpu_fd, KVM_RUN, 0);
unsigned short cs, ip;
if (ret < 0) {
perror("KVM_RUN");
return 1;
}
if (ioctl(vcpu_fd, KVM_GET_SREGS, &sregs) < 0) {
perror("KVM_GET_SREGS");
return 1;
}
if (ioctl(vcpu_fd, KVM_GET_REGS, &regs) < 0) {
perror("KVM_GET_REGS");
return 1;
}
cs = sregs.cs.selector;
ip = regs.rip;
if (run->exit_reason == KVM_EXIT_HLT) {
fprintf(stderr, "info: hlt: cs=0x%04x ip=0x%04x\n", cs, ip - 1);
if (cs == 0x70) {
if (ip != 0 + 1) {
fprintf(stderr, "info: success, exiting\n");
return 0; /* EXIT_SUCCESS after the second `hlt' in the code. */
}
} else if (cs == 0x54) { /* Simulate iret. */
const char *csip_ptr = (const char*)mem + ((unsigned short)sregs.ss.selector << 4) + (unsigned short)regs.rsp;
const unsigned short int_ip = ((const unsigned short*)csip_ptr)[0];
const unsigned short int_cs = ((const unsigned short*)csip_ptr)[1];
const unsigned short int_flags = ((const unsigned short*)csip_ptr)[2];
fprintf(stderr, "info: int 0x%02x iret to: cs=0x%04x ip=0x%04x\n", ip - 1, int_cs, int_ip);
sregs.cs.base = (sregs.cs.selector = int_cs) << 4;
regs.rip = int_ip;
if (int_flags & (1 << 9)) regs.rflags |= (1 << 9); /* Set IF back to 1 if it was 1. */
regs.rsp += 6; /* pop ip, pop cs, popfw . */
if (ioctl(vcpu_fd, KVM_SET_SREGS, &sregs) < 0) {
perror("KVM_SET_SREGS");
return 1;
}
if (ioctl(vcpu_fd, KVM_SET_REGS, &regs) < 0) {
perror("KVM_SET_REGS");
return 1;
}
} else {
fprintf(stderr, "fatal: unexpected hlt: cs=0x%04x ip=0x%04x\n", cs, ip - 1);
return 5;
}
} else if (run->exit_reason == KVM_EXIT_INTERNAL_ERROR) {
fprintf(stderr, "fatal: KVM internal error suberror=%d\n", (unsigned)run->internal.suberror);
return 4;
} else {
fprintf(stderr, "fatal: unexpected KVM exit: exit_reason=%d cs=0x%04x ip=0x%04x\n", run->exit_reason, cs, ip);
return 2;
}
}
}

socket recv() returns weird value

I have a weird problem when writing a simple socket server and client in C.
The logic is very simple, the client sends 2 messages every time:
The data size (like a header indicating the data size it will send).
the data itself.
The server can successfully receive the first couple of messages, but after a while the data size message becomes some weird value.
This is the code and output.
server.c
uint32_t toUInt32(uint8_t *buf) {
return ((uint32_t) buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}
int recvall(int fd, uint8_t *data, int size) {
int BUFF_SIZE = 1024;
int cur_size = 0;
while(cur_size < size) {
int size_to_recv = BUFF_SIZE;
if (size - cur_size < BUFF_SIZE) {
size_to_recv = size - cur_size;
}
char *temp = malloc(size_to_recv);
int recv_size = recv(fd, temp, size_to_recv, 0);
memcpy(data + cur_size, temp, size_to_recv);
cur_size += size_to_recv;
free(temp);
}
return cur_size;
}
while(1)
{
uint8_t size_message[4];
memset(size_message, 0 , 4);
read_size = recv(client_sock , size_message, 4 , 0);
if (read_size == 0) {
return 0;
}
printf("bytes are %d %d %d %d\n", size_message[0], size_message[1], size_message[2], size_message[3]);
uint32_t size = toUInt32(size_message);
printf("size is %d\n", size);
uint8_t *data = malloc(size);
read_size = recvall(client_sock , data , size);
free(data);
}
server output:
bytes are 0 1 83 91
size is 86875
bytes are 0 1 130 117
size is 98933
bytes are 0 1 208 101
size is 118885
bytes are 0 2 141 253
size is 167421
bytes are 43 244 25 88
size is 737417560
client.c
static inline void
buffer_write32be(uint8_t *buf, uint32_t value) {
buf[0] = value >> 24;
buf[1] = value >> 16;
buf[2] = value >> 8;
buf[3] = value;
}
while(1) {
uint32_t size = (uint32_t)packet->size;
uint8_t *buf = malloc(sizeof(uint32_t));
buffer_write32be(buf, size);
if (send(sock , buf , sizeof(uint32_t) , 0 ) < 0)
{
return false;
}
if (send(sock , packet->data , packet->size, 0 ) < 0)
{
return false;
}
free(buf);
sleep(1);
}
client output:
packet size is 86875, header size 4
packet size is 98933, header size 4
packet size is 118885, header size 4
packet size is 167421, header size 4
packet size is 167847, header size 4
packet size is 169004, header size 4
packet size is 169811, header size 4
You can see that the data size that the server receives suddenly becomes a weird large value, but the client sent out the right value.
What is going on here?

How to use the MODBUS TCP with STM32F3?

As a result of large research, I found this source code for STM32f1 this link and I changed it for STM32f3. And the build and install to my STM32. My ethernet cable connect between my computer and enc28j60 module. If I debugging this code my code stack in main.c and while loop:
while (1)
{
eMBPoll();
led_poll();
/* 从网络设备读取一个IP包,返回数据长度 */
uip_len = tapdev_read();
/* 收到数据 */
**if (uip_len > 0)**
{
/* 处理IP数据包 */
if (BUF->type == htons(UIP_ETHTYPE_IP))
{
uip_arp_ipin();
uip_input();
if (uip_len > 0)
{
uip_arp_out();
tapdev_send();
}
}
/* 处理ARP报文 */
else if (BUF->type == htons(UIP_ETHTYPE_ARP))
{
uip_arp_arpin();
if (uip_len > 0)
{
tapdev_send();
}
}
}
I stuck if (uip_len > 0) line because uip_len return 0 for this line:
(My code same as bellow github link so i dont share all of code )
enc28j_60.c in the unsigned int enc28j60_packet_receive(unsigned char *packet, unsigned int maxlen) function:
unsigned int enc28j60_packet_receive(unsigned char *packet, unsigned int maxlen)
{
unsigned int rxstat;
unsigned int len;
if (enc28_read(EPKTCNT) == 0)
{
return (0);
}
enc28_write(ERDPTL, (next_pack_ptr));
enc28_write(ERDPTH, (next_pack_ptr) >> 8);
next_pack_ptr = enc28_readOp(ENC28J60_READ_BUF_MEM, 0);
next_pack_ptr |= enc28_readOp(ENC28J60_READ_BUF_MEM, 0) << 8;
len = enc28_readOp(ENC28J60_READ_BUF_MEM, 0);
len |= enc28_readOp(ENC28J60_READ_BUF_MEM, 0) << 8;
len -= 4;
rxstat = enc28_readOp(ENC28J60_READ_BUF_MEM, 0);
rxstat |= enc28_readOp(ENC28J60_READ_BUF_MEM, 0) << 8;
if (len > maxlen - 1)
{
len = maxlen - 1;
}
**if ((rxstat & 0x80) == 0)
{
GPIO_SetBits(GPIOE, GPIO_Pin_9);
len = 0;
}**
else
{
des_enc28_readBuffer(packet, len);
}
enc28_write(ERXRDPTL, (next_pack_ptr));
enc28_write(ERXRDPTH, (next_pack_ptr) >> 8);
enc28_writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
return (len);
}
Why is the rxstat & 0x80) == 0? I do not understand.
According to the ENC28J60 datasheet, it seems like RXSTAT flag should be at bit 12:
I am not exactly sure if des_enc28_readOp(ENC28J60_READ_BUF_MEM, 0) is reading the right thing, but I believe you should have something like:
unsigned PHSTAT2 = des_enc28_readOp(ENC28J60_READ_BUF_MEM, 0);
PHSTAT2 |= des_enc28_readOp(ENC28J60_READ_BUF_MEM, 0) << 8;
unsigned RXSTAT = (PHSTAT2 & 0x1000) != 0;
if (RXSTAT)
{
// RXSTAT flag is set
des_enc28_readBuffer(packet, len);
}
else
{
...
}
I would also dump the values of this register to a log or serial port, to make sure you understand what its contents actually are:
// I noticed serialprint in your other question, so I am presuming this is your log func
serialprint("PHSTAT2 = 0x%04x\n", PHSTAT2);

NTP implementation C

Can someone find what I'm missing on my ntp implementation?
I'm programming an 32-bit microcontroller ARM Cortex M3.
I have three functions - wifiSend which calls the bsdUdpClient and bsdUdpServer. The bsdUdpClient sends data to the server and bsdUdpServer listens to the reserved NTP port and receive the data of the NTP server.
I got no error messages at all but the buffer received is empty.
static void wifiSend(xTimerHandle xTimer){
uint16_t AddrSize = sizeof(SlSockAddrIn_t);
if (STATUS_OK != bsdUdpClient(SERVER_PORT, AddrSize)){
printf("Failed to send udp packet\n\r");
assert(false);
}
if (STATUS_OK != bsdUdpServer(SERVER_PORT, AddrSize))
printf("Failed to receive udp packet\n\r");
}
static returnTypes_t bsdUdpClient(uint16_t port, uint16_t AddrSize){
int16_t Status = (int16_t) ZERO;
memset(packetBuffer, 0, NTP_PACKET_SIZE);
packetBuffer[0] = 0xE3; //0b11100011; // LI, Version, Mode
packetBuffer[1] = 0x00; // Stratum, or type of clock
packetBuffer[2] = 0x06; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
Addr.sin_family = SL_AF_INET;
Addr.sin_port = sl_Htons((uint16_t) port);
Addr.sin_addr.s_addr = sl_Htonl(SERVER_IP);
SockID = sl_Socket(SL_AF_INET, SL_SOCK_DGRAM, (uint32_t) ZERO);
if (SockID < (int16_t) ZERO)
return (SOCKET_ERROR);
Status = sl_SendTo(SockID, packetBuffer, NTP_PACKET_SIZE * sizeof(uint8_t), (uint32_t) ZERO, (SlSockAddr_t *) &Addr, AddrSize);
if (Status <= (int16_t) ZERO) {
Status = sl_Close(SockID);
if (Status < 0)
return (SEND_ERROR);
return (SEND_ERROR);
}
Status = sl_Close(SockID);
if (Status < 0)
return (SEND_ERROR);
return (STATUS_OK);
}
static returnTypes_t bsdUdpServer(uint16_t port, uint16_t AddrSize){
int16_t Status = (int16_t) ZERO;
LocalAddr.sin_family = SL_AF_INET;
LocalAddr.sin_port = sl_Htons(5001);
LocalAddr.sin_addr.s_addr = 0;
SockID = sl_Socket(SL_AF_INET,SL_SOCK_STREAM, (uint32_t) ZERO);
if (SockID < 0){
printf("error on sl_Socket\n\r");
return SOCKET_ERROR;
}
Status = sl_Bind(SockID, (SlSockAddr_t *) &LocalAddr, AddrSize);
if (Status < 0){
printf("problem on sl_Bind\n\r");
return SOCKET_ERROR;
}
Status = sl_RecvFrom(SockID, packetBuffer, NTP_PACKET_SIZE * sizeof(uint8_t), (uint32_t) ZERO, (SlSockAddr_t *) &Addr, &AddrSize);
if (Status < (int16_t) ZERO){
printf("error - no bytes received: %d\n\r", (int16_t)Status);
return SOCKET_ERROR;
}
Status = sl_Close(SockID);
if (Status < 0)
printf("problem on sl_Close\n\r");
uint8_t index3 = packetBuffer[40];
uint8_t index2 = packetBuffer[41];
uint8_t index1 = packetBuffer[42];
uint8_t index0 = packetBuffer[43];
uint16_t highWord = index3 << 16 | index2;
uint16_t lowWord = index1 << 16 | index0;
uint32_t secondsSince1900 = highWord << 16 | lowWord;
printf("Seconds since 1 Janeiro de 1900: %ld\n\r", secondsSince1900);
return (STATUS_OK);
}
Your client code sends a query, but then closes the socket and never listens for a reply.
Your server code waits for a query, but then never sends any response.
Neither of them appear to do anything particularly useful. You should complete the client code so that it listens for a reply before it closes the socket. Note that you should use a timeout so that you don't wait forever in the event that either the query or the response is lost.
Thanks to #DavidSchwartz, I have now a working solution.
I also fixed a bug I had when I was trying to extract the number of seconds since 1st January of 1900 from the packet received.
I hope this can help someone.
TO BE IMPROVED: add a timeout to avoid a blocking state if there is no response from the server
#define NTP_PACKET_SIZE 48
uint8_t packetBuffer[ NTP_PACKET_SIZE];
static returnTypes_t bsdUdpClient(uint16_t AddrSize){
int16_t Status = (int16_t) ZERO;
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
packetBuffer[0] = 0xE3; //0b11100011; // LI, Version, Mode
packetBuffer[1] = 0x00; // Stratum, or type of clock
packetBuffer[2] = 0x06; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
SockID = sl_Socket(SL_AF_INET, SL_SOCK_DGRAM, (uint32_t) ZERO);
if (SockID < (int16_t) ZERO)
return (SOCKET_ERROR);
/*make the request to the server*/
Status = sl_SendTo(SockID, packetBuffer, NTP_PACKET_SIZE * sizeof(uint8_t), (uint32_t) ZERO, (SlSockAddr_t *) &Addr, AddrSize);
/*Check if 0 transmitted bytes sent or error condition*/
if (Status <= (int16_t) ZERO) {
sl_Close(SockID);
return (SEND_ERROR);
}
else
printf("request sent successfully\n\r");
/* receive the reply from the server*/
Status = sl_RecvFrom(SockID, packetBuffer, NTP_PACKET_SIZE * sizeof(uint8_t), (uint32_t) ZERO, (SlSockAddr_t *) &Addr, &AddrSize);
if (Status < (int16_t) ZERO){
printf("error - no bytes received: %d\n\r", (int16_t)Status);
return SOCKET_ERROR;
}
else
printf("reply received\n\r");
Status = sl_Close(SockID);
if (Status < 0)
printf("problem on sl_Close\n\r");
uint8_t index3 = packetBuffer[40];
uint8_t index2 = packetBuffer[41];
uint8_t index1 = packetBuffer[42];
uint8_t index0 = packetBuffer[43];
uint16_t highWord = index3 << 8 | index2;
uint16_t lowWord = index1 << 8 | index0;
uint32_t secondsSince1900 = highWord << 16 | lowWord;
printf("Seconds since 1 Janeiro de 1900: %lu\n\r", secondsSince1900);
return (STATUS_OK);
}

Server-client in C: Weird behaviour when sending bytes

I'm trying to write a server-client program in C wherein the client will send a bunch of messages in the form of 5 bytes: the first byte will contain a command, and the next four will contain a key. It looks something like this:
rc = write(sockfd, &op, 1);
if (rc != 1)
{
printf("error! write() failed: %s\n", strerror(errno));
break;
}
uint32_t net_num = htonl(num);
int nsent = 0;
while (nsent < 4)
{
rc = write(sockfd, &net_num + nsent, 4 - nsent);
if (rc <= 0)
{
printf("error! write() failed: %s\n", strerror(errno));
break;
}
nsent += rc;
}
if (rc <= 0)
break;
}
On the receiving end, I have:
while((bytes = recv(socket,buffer,5,0)) > 0)
{
//printf("%d\t%d\t%d\t%d\t%d\n",(int)buffer[0],(int)buffer[1], (int)buffer[2],(int)buffer[3],(int)buffer[4]);
key = ((buffer[4] << 24) | (buffer[3] << 16) | (buffer[2] << 8) | (buffer[1]));
if((int)buffer[0] == 0)
{
do command 0, etc...
The problem I'm having is that I cant get the key. I've tried switching the order of the shifts, but all I'm getting are numbers that don't match the keys that the client is sending. I'm at a loss.
Even stranger, is that if I compile the server without the print under the while, I get seg-faulted. If I uncomment the printf, it works fine. This seems super strange.
Does anyone know what might be causing this?
Thanks in advance.
rc = write(sockfd, &net_num + nsent, 4 - nsent); is wrong, &net_num is a pointer to a 32 bits object, so &net_num+1 would point to the next 32bits object. That is beyond the object. You could cast to a char pointer, or copy to a small char buffer before sending, like below:
uint32_t net_num = htonl(num);
char buff[sizeof net_num];
memcpy (buff, &net_num, sizeof buff);
int nsent;
for nsent=0; nsent < sizeof buff; nsent += rc;)
{
rc = write(sockfd, buff + nsent, 4 - nsent);
if (rc == -1 && errno == EAGAIN) { rc=0; continue; }
if (rc <= 0)
{
printf("error! write() failed: %s\n", strerror(errno));
break;
}
}

Resources