Abdullah
Abdullah

Reputation: 541

Byte Alignment when sending or receiving a struct

I am facing a bit of issue in writing a network software. When I try to send or receive a struct that contains a data type of 8 bytes the next sent or received struct is somehow affected. I have a few things in mind but first I wanted to confirm one thing before I get into debugging. I am using 32-bit Ubuntu 11.04 (silly me) on a 64-bit x-86 system. Does this has anything to do with the byte alignment problems?

I am developing a controller to communicate with the Open Flow switch. The openflow protocol defines a set of specs based on which switches are built. The problem is when I try to communicate with the switch everything goes fine until I send or receive a struct that contains a 64 bit date type (uint64_t). The specific structs that are used for sending and receiving features are

estruct ofp_header {
uint8_t version;    /* OFP_VERSION. */
uint8_t type;       /* One of the OFPT_ constants. */
uint16_t length;    /* Length including this ofp_header. */
uint32_t xid;       /* Transaction id associated with this packet.
                       Replies use the same id as was in the request
                       to facilitate pairing. */};   
 assert(sizeof(struct ofp_header) == 8);




/* Switch features. */
struct ofp_switch_features {
struct ofp_header header;
uint64_t datapath_id; /* Datapath unique ID. The lower 48-bits are for a MAC address, while the upper 16-bits are implementer-defined. */
uint32_t n_buffers; /* Max packets buffered at once. */
uint8_t n_tables; /* Number of tables supported by datapath. */
uint8_t pad[3]; /* Align to 64-bits. */

/* Features. */ /* Bitmap of support "ofp_capabilities". */
uint32_t capabilities; /* Bitmap of supported "ofp_action_type"s. */
uint32_t actions; 

/* Port info.*/
struct ofp_phy_port ports[0];  /* Port definitions. The number of ports is inferred from the length field in the header. */
};
assert(sizeof(struct ofp_switch_features) == 32);

The problem is when I communicate using any other structs that have data types less than 64-bit everything goes fine. When I receive features reply it shows the right values but after that if I receive any other struct it shows garbage values. Even if I receive features reply again I get garbage values. In short if at any point of code I receive features request or any other struct defined in the specs that has a data type of 64-bit the next structs receive garbage values. The code used for sending and receiving features request is as follows

////// features request and reply  ////////////

ofp_header features_req;

features_req.version=OFP_VERSION;
features_req.type=OFPT_FEATURES_REQUEST;
features_req.length= htons(sizeof features_req);
features_req.xid = htonl(rcv_hello.xid);


if (send(connected, &features_req, sizeof(features_req), 0)==-1) {
printf("Error in sending message\n");
exit(-1);
}
printf("features req sent!\n");



ofp_switch_features features_rep={0};

if (recv(connected, &features_rep, sizeof(features_rep), 0)==-1) {
printf("Error in receiving message\n");
exit(-1);
}

printf("message type : %d\n",features_rep.header.type);
printf("version : %d\n",features_rep.header.version);
printf("message length: %d\n",ntohs(features_rep.header.length));
printf("xid : %d\n",ntohl(features_rep.header.xid));
printf("buffers: %d\n",ntohl(features_rep.n_buffers));
printf("tables: %d\n",features_rep.n_tables);

Upvotes: 0

Views: 797

Answers (5)

Abdullah
Abdullah

Reputation: 541

I got help from Daniweb.com and all credit goes to a guy with a nick NEZACHEM. His answer was, and I quote :

The problem has nothing to do with 64 bit types. Values you read are not garbage, but a very valuable port definitions:

 struct ofp_phy_port ports[0];  /* Port definitions. The number of ports is inferred from the length field in the header. */

Which means, once you've

recv(connected, &features_rep, sizeof(features_rep), 0)

you need to inspect features_rep.header.length, figure out how many struct ofp_phy_port follow, allocate memory for them and read those data.

I did that and thanks to him my problems were solved and all went well :) thanx for everyone that replied. cheers :)

Upvotes: 1

alk
alk

Reputation: 70911

1 In case you stick to sending the structures you should make sure they are byte aligned.

To do so use the pragma pack like this:

#pragma pack(1)
struct mystruct{
  uint8_t  myint8;   
  uint16_t myint16;   
};
#pragma pack()

Doing so you makes sure this structure does use 3 bytes only.

2 For converting 64bit values from host order to network order this post reads interessing: Is there any "standard" htonl-like function for 64 bits integers in C++? (no, it only starts with c++ and ends with C also)

Upvotes: 0

Ed Heal
Ed Heal

Reputation: 59997

Here is what is want

features_req.version=OFP_VERSION; 
features_req.type=OFPT_FEATURES_REQUEST; 
features_req.length= htons(sizeof features_req); 
features_req.xid = htonl(rcv_hello.xid); 

char data[8];
data[0] = features_req.version;
data[1] = features_req.type;
memcpy(data + 2, &features_req.length, 2);
memcpy(data + 4, &features_req.xid, 4);

if (send(connected, data, 8) ....

On the receving end

char data[8];

if (recv(conncted, data, 8) ...
features_req.version = data[0];
features_req.type = data[1];
memcpy(&features_req.length, data + 2, 2);
memcpy(&features_req.xid, data + 4, 4);

features_req.length = ntohs(features_req.length);
features_req.xid= ntohl(features_req.xid);

Upvotes: 1

You could even consider using serialization techniques: perhaps JSON, XDR, YAML could be relevant. or libraries like s11n, jansson, etc.

Upvotes: 0

Ed Heal
Ed Heal

Reputation: 59997

  1. Convert your struct into an array of characters before sending them - this is call serialisation
  2. Use the family of functions htons etc to ensure that integers are sent in network order. Saves hassle on the endians of the various machines
  3. One the recieving end read the bytes and reconstruct the struct.

This will ensure that you will not have any hassle at all.

Upvotes: 6

Related Questions