For a small tool that I am building for OSX, I want to capture the lengths of packets send and received from a certain ethernet controller.
When I fetch the ethernet cards I also get extra information like maximum packet sizes, link speeds etc.
When I start the (what I call) 'trafficMonitor' I launch it like this:
static void initializeTrafficMonitor(const char* interfaceName, int packetSize) {
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t* sessionHandle = pcap_open_live(interfaceName, packetSize, 1, 100, errbuf);
if (sessionHandle == NULL)
{
printf("Error opening session for device %s: %s\n", interfaceName, errbuf);
return;
}
pcap_loop(sessionHandle, -1, packetReceived, NULL);
}
The supplied interfaceName is the BSD name of the interface, for example en0. The packetSize variable is an integer where I supply the maximum packetsize for that ethernet adapter (that seemed logical at that time). For example the packetsize for my WiFi adapter is 1538.
My callback method is called packetReceived and looks like this:
void packetReceived(u_char* args, const struct pcap_pkthdr* header, const u_char* packet) {
struct pcap_work_item* item = malloc(sizeof(struct pcap_pkthdr) + header->caplen);
item->header = *header;
memcpy(item->data, packet, header->caplen);
threadpool_add(threadPool, handlePacket, item, 0);
}
I stuff all the properties for my packet in a new struct and launch a worker thread to analyze the packet and process the results. This is to not keep pcap waiting and is an attempt to fix this problem which already existed before adding this worker thread method.
The handlePacket method is like this:
void handlePacket(void* args) {
const struct pcap_work_item* workItem = args;
const struct sniff_ethernet* ethernet = (struct sniff_ethernet*)(workItem->data);
u_int size_ip;
const struct sniff_ip* ip = (struct sniff_ip*)(workItem->data + SIZE_ETHERNET);
size_ip = IP_HL(ip) * 4;
if (size_ip < 20) {
return;
}
const u_int16_t type = ether_packet(&workItem->header, workItem->data);
switch (ntohs(type)) {
case ETHERTYPE_IP: {
char sourceIP[INET_ADDRSTRLEN];
char destIP[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &ip->ip_src, sourceIP, sizeof(sourceIP));
inet_ntop(AF_INET, &ip->ip_dst, destIP, sizeof(destIP));
[refToSelf registerPacketTransferFromSource:sourceIP destinationIP:destIP packetLength:workItem->header.caplen packetType:ethernet->ether_type];
break;
}
case ETHERTYPE_IPV6: {
// handle v6
char sourceIP[INET6_ADDRSTRLEN];
char destIP[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &ip->ip_src, sourceIP, sizeof(sourceIP));
inet_ntop(AF_INET6, &ip->ip_dst, destIP, sizeof(destIP));
[refToSelf registerPacketTransferFromSource:sourceIP destinationIP:destIP packetLength:workItem->header.caplen packetType:ethernet->ether_type];
break;
}
}
}
Based on the type of ethernet packet I try to figure out if it is an packet send using an IPv4 or IPv6 address. After that is determined I send some details to an objectiveC method (Source IP address, Destination IP address and packet length).
I cast the packet to the structs explained on the website of tcpdump (http://www.tcpdump.org/pcap.html).
The problem is that pcap either does not seem to keep up with the packets received/send. Either I am not sniffing all the packets or the packet lengths are wrong.
Does anyone have any pointers where I need to adjust my code to make pcap catch them all or where I have some sort of problem.
These methods are called from my objectiveC application and the refToSelf is a reference to a objC class.
Edit: I am calling the initializeTrafficMonitor in a background thread, because the pcap_loop is blocking.
On which version of OS X is this? In releases prior to Lion, the default buffer size for libpcap on systems using BPF, such as OS X, was 32K bytes; 1992 called, they want their 4MB workstations and 10Mb Ethernets back. In Lion, Apple updated libpcap to version 1.1.1; in libpcap 1.1.0, the default BPF buffer size was increased to 512MB (the maximum value in most if not all systems that have BPF).
If this is Snow Leopard, try switching to the new pcap_create()/pcap_activate() API, and use pcap_set_buffer_size() to set the buffer size to 512MB. If this is Lion or later, that won't make a difference.
That won't help if your program can't keep up with the average packet rate, but it will, at least, mean fewer packet drops if there are temporary bursts that exceed the average.
If your program can't keep up with the average packet rate, then, if you only want the IP addresses of the packets, try setting the snapshot length (which you call "packetSize"`) to a value large enough to capture only the Ethernet header and the IP addresses for IPv4 and IPv6. For IPv4, 34 bytes would be sufficient (libpcap or BPF might round that up to a larger value), as that's 14 bytes of Ethernet header + 20 bytes of IPv4 header without options. For IPv6, it's 54 bytes, for 14 bytes of Ethernet header and 40 bytes of IPv6 header. So use a packetSize value of 54.
Note that, in this case, you should use the len field, NOT the caplen field, of the struct pcap_pkthdr, to calculate the packet length. caplen is the amount of data that was captured, and will be no larger than the specified snapshot length; len is the length "on the wire".
Also, you might want to try running pcap_loop() and all the processing in the same thread, and avoid allocating a buffer for the packet data and copying it, to see if that speeds the processing up. If you have to do them in separate threads, make sure you free the packet data when you're done with it.
Related
I'm aware of the need to call send() in a loop until the desired amount of bytes have been sent. Also on the receiving side.
Here is a part of wrapper around recv I wrote:
do{
result = socket->receiveData(m_recvBuf + recv_len , BUF_LENGTH - recv_len);
recv_len += result;
.....
I'm a little confused about couple of things, so here goes:
if the send() returns 10 bytes, are those ten bytes still only at the sender side, but ready to be sent. Or have the bytes physically arrived to the receiver computer?
If the answer to the above is yes, does then calling recv() always return those 10 bytes as they have already arrived?
I could also put it this way; if send has been called three times each time returning 10, so total bytes sent supposedly being 30. Does then calling recv(), one time, return 30 bytes?
Question 1. edited as "still only at the receiver side" should be "still only at the sender side".
Scenario:
My program in pc1 calls send();
send() returns 1;
My code things that one byte has been sent to the receiver program in pc2.
The network cable got eaten by a dog the moment after my send() function returned 1.
If that is the case in real life, I surely have misunderstood the benefits of TCP vs UDP.
Thanks all for giving time in answering.
I'll try:
You don't know. All that is known is that the network stack has accepted the data, and will do its best to transfer it.
N/A
No, there are no such guarantees. TCP/IP is stream-oriented, it's two streams of bytes with no further structure (messages, packets, writes, frames, whatever). You must make sure your data serialization format supports finding the message boundaries so the receiver can interpret the data.
if send() returns x bytes, does recv() get the same amount of bytes in one call?
In general, certainly no !!
For example, for TCP/IP sockets (see tcp(7) & socket(7)) going through wifi routers and/or intercontinental routers, packets could be fragmented and/or reassembled. So a given send can correspond to several recv and vice versa, and the "boundaries" of messages are not respected. Hence, for applications, TCP is a stream of bytes without any message boundaries. Read also about sliding window protocol and TCP congestiion control used inside TCP.
In practice, you might observe, e.g. between two computers on the same Ethernet cable, that packets are not fragmented or reassembled. But you should not code with that hypothesis.
Concretely, application level protocols like HTTP or SMTP or JSONRPC or X11 protocols should be designed to define message boundaries and both server and client sides should do buffering.
You'll want to use poll(2), see this answer.
if the send() returns 10 bytes, are those ten bytes still only at the receiver side, but ready to be sent.
It is not easy to define what "being still at the reciever side" really means (because you don't really care about what happens inside the kernel, or inside the network controller, or on intercontinental cables). Therefore the above sentence is meaningless.
Your application code should only care about system calls (listed in syscalls(2)...) like poll(2), send(2) and related, write(2), recv(2) and related, read(2), socket(2), accept(2), connect(2), bind(2) etc...
You might want to use messaging libraries like 0mq.
The network cable got eaten by a dog the moment after my send() function returned 1.
Why do you care that much about such a scenario. Your dog could also have dropen your laptop, or have pee-ed on it. Once send has told your application than ten bytes have been emitted, you should trust your kernel. But the receiving program might not yet have gotten these bytes (on another continent, you'll need to wait dozens of milliseconds, which is a quite big delay for a computer). Very probably, the ten bytes are in the middle of the ocean when your dog have bitten your Ethernet cable (and you can reasonably code as if they have been emitted).
The TCP protocol will detect that the link has been interrupted, but that error would be given to your program much later (perhaps as an error for the next call to send happening ten seconds after).
(there are some large macroscopic delays in the TCP definition, perhaps as large as 128 seconds -I forgot the details- and these delays are too small for interplanetary communication; so TCP can't be used to Mars)
You should (most of the time) simply reason at the system call level.
(of course, in some cases -think of remote neurosurgical robots- that might not be enough)
I surely have misunderstood the benefits of TCP vs UDP.
If you just used UDP, a given packet could be fragmented, lost or received several times. With TCP, that cannot reasonably happen (at least when the next packets have been successfully sent and received).
if the send() returns 10 bytes, are those ten bytes still only at the
sender side, but ready to be sent. Or have the bytes physically
arrived to the receiver computer?
You cannot tell where the 10 bytes are exactly, some of them can be waiting somewhere in the sending machine, some over some wire and some waiting somewhere in the receiving machine.
If the answer to the above is yes, does then calling recv() always
return those 10 bytes as they have already arrived?
N/A
I could also put it this way; if send has been called three times each
time returning 10, so total bytes sent supposedly being 30. Does then
calling recv(), one time, return 30 bytes?
You cannot tell! The only thing you can say, in TCP mode, is that bytes are received in the same order that they have being sent.
Scenario: My program in pc1 calls send(); send() returns 1; My code
things that one byte has been sent to the receiver program in pc2. The
network cable got eaten by a dog the moment after my send() function
returned 1.
Then you cannot say anything...
If that is the case in real life, I surely have misunderstood the benefits of TCP vs UDP.
UDP is datagram-oriented semantic, like postal sytem (no preserved order, no guaranty of any kind, loss is possible, duplicate, etc)
TCP is stream-oriented semantic, like phone system (preserved order and no loss).
Of course in case of hard network failure, TCP cannot ensure anything!
As TCP is build on top of IP (datagram) what is sent over TCP is fragmented to be sent via IP, and you don't control such fragmentation (I forgot to tell about caching, etc).
my motto: "if in doubt, try it out".
This is a complete program that demonstrates that on my machine, an entire packet of one million bytes does not even make it through the loopback adapter without being buffered into separate reads (and possibly writes, as I have used the composite function asio::write() :
#include <thread>
#include <mutex>
#include <condition_variable>
#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <vector>
#include <iostream>
namespace asio
{
using namespace boost::asio;
using boost::system::error_code;
}
using protocol = asio::ip::tcp;
struct event
{
std::mutex mutex;
std::condition_variable cv;
bool notified = false;
void notify()
{
auto lock = std::unique_lock<std::mutex>(mutex);
notified = true;
lock.unlock();
cv.notify_all();
}
void wait()
{
auto lock = std::unique_lock<std::mutex>(mutex);
cv.wait(lock, [this] { return this->notified; });
}
};
struct emitter
{
emitter(std::ostream& os) : os(os) {}
template<class...Ts>
void operator()(Ts&&...ts)
{
auto lock = std::unique_lock<std::mutex>(m);
auto emit_item = [&os = this->os](auto&& x)
{
os << x;
};
using expand = int[];
void(expand { 0,
(emit_item(ts),0)...
});
os << std::endl;
}
std::ostream& os;
std::mutex m;
};
event rx_ready;
emitter stdout_emit { std::cout };
void sender()
{
asio::io_service executor;
auto big_buffer = std::vector<char>(1000000, 'a');
protocol::socket sock(executor);
rx_ready.wait();
asio::error_code ec;
if(sock.connect(protocol::endpoint(asio::ip::address_v4(0x7f000001), 12345)), ec) {
stdout_emit("connect failure: ", ec.message());
return;
}
auto written = asio::write(sock, asio::buffer(big_buffer), ec);
stdout_emit("wrote: ", written);
if (ec) {
stdout_emit("write failure: ", ec.message());
}
sock.shutdown(protocol::socket::shutdown_send, ec);
if (ec) {
stdout_emit("shutdown failure: ", ec.message());
}
sock.close(ec);
if (ec) {
stdout_emit("close failure: ", ec.message());
}
}
void start_receiving(protocol::socket& s)
{
auto huge_buffer_ptr = std::make_shared<std::vector<char>>(1000000);
s.async_read_some(asio::buffer(*huge_buffer_ptr), [huge_buffer_ptr, &s](asio::error_code ec, std::size_t size)
{
stdout_emit("read ", size, " bytes");
if (ec)
{
stdout_emit("read error: ", ec.message());
}
else
{
start_receiving(s);
}
});
}
void receiver()
{
asio::io_service executor;
protocol::acceptor acceptor(executor);
auto ep = protocol::endpoint(protocol::v4(), 12345);
acceptor.open(ep.protocol());
acceptor.bind(ep);
acceptor.listen();
protocol::socket s(executor);
acceptor.async_accept(s, [&](asio::error_code ec){
if (ec) {
stdout_emit("accept: ", ec.message());
}
else
{
start_receiving(s);
}
});
rx_ready.notify();
executor.run();
}
int main()
{
auto t = std::thread(receiver);
sender();
t.join();
}
sample results:
read 393216 bytes
wrote: 1000000
read 606784 bytes
read 0 bytes
read error: End of file
Process finished with exit code 0
changing the read and write buffers to 10,000,000 bytes gave me this:
read 393216 bytes
read 638820 bytes
read 639028 bytes
read 639028 bytes
read 639028 bytes
read 638820 bytes
read 639028 bytes
read 639028 bytes
read 639028 bytes
read 638820 bytes
read 639028 bytes
read 639028 bytes
read 639028 bytes
read 638820 bytes
wrote: 10000000
read 639028 bytes
read 639028 bytes
read 22196 bytes
read 0 bytes
read error: End of file
Process finished with exit code 0
This is more of a request for confirmation than a question, so I'll keep it brief. (I am away from my PC and so can't simply implement this solution to test).
I'm writing a program to send an image file taken via webcam (along with meta data) from a raspberryPi to my PC.
I've worked out that the image is roughly around 130kb, the packet header is 12b and the associated meta data another 24b. Though I may increase the image size in future, once I have a working prototype.
At the moment I am not able to retrieve this whole packet successfully as, after sending it to the PC I only ever get approx 64kb recv'd in the buffer.
I have assumed that this is because for whatever reason the default buffer size for a socket declared like:
SOCKET sock = socket(PF_INET, SOCK_STREAM, 0);
is 64kb (please could someone clarify this if you're 'in the know')
So - to fix this problem I intend to increase the socket size to 1024kb via the setsockopt(x..) command.
Please could someone confirm that my diagnosis of the problem, and proposed solution are correct?
I ask this question as I am away form my PC right now and am unable to try it until I get back home.
This most likely has nothing to do with the socket buffers, but with the fact that recv() and send() do not have to receive and send all the data you want. Check the return value of those function calls, it indicates how many bytes have actually been sent and received.
The best way to deal with "short" reads/writes is to put them in a loop, like so:
char *buf; // pointer to your data
size_t len; // length of your data
int fd; // the socket filedescriptor
size_t offset = 0;
ssize_t result;
while (offset < len) {
result = send(fd, buf + offset, len - offset, 0);
if (result < 0) {
// Deal with errors here
}
offset += result;
}
Use a similar construction for receiving data. Note that one possible error condition is that the function call was interrupted (errno = EAGAIN or EWOULDBLOCK), in that case you should retry the send command, in all other cases you should exit the loop.
I am capturing some audio from my microphone using SFML.
The data is being stored in samples of type Int16*.
Int16* samples;
My question is. What should I do to this samples to stream it over a socket to be played in another place? I ask in relation of data type. Do I need to convert this Int16 array to another type? Or can I just send this Int16* as it is?
EDIT
void BroadcastRecorder::loadBufferFromSamples()
{
//m_samples is of type vector<Int16*>
if (!m_samples.empty()){
m_buffer.loadFromSamples(&m_samples[0], m_samples.size(), 1, getSampleRate());
m_samples.clear();
}
}
void Broadcaster::Send()
{
//load the buffer with the samples
if(!m_recorder->empty()){
m_recorder->loadBufferFromSamples();
const sf::SoundBuffer& buffer = m_recorder->getBuffer();
size_t dataLength = m_recorder->GetSamplesSize();
wxSocketClient * socket = new wxSocketClient(wxSOCKET_NOWAIT);
socket->Notify(false);
// ------------- DATA----------------------
wxString data = "";
wxString strToPrepend(_("--myboundary\r\nContent-Type: audio/wav\r\n"));
wxString strToAppend(_("\r\n--myboundary\r\n"));
// ------------- HEADER -----------------------
wxString header = "";
header.append("POST ");
header.append("/cgi-bin/operator/transmit");
header.append(" HTTP/1.0\r\n");
header.append("Content-Type: multipart/form-data; boundary=--myboundary\r\n");
header.append("Content-Length: " + wxString::Format(wxT("%i"),(dataLength + strToPrepend.Len() + strToAppend.Len()) ) + "\r\n");
header.append("Authorization: Basic keykeykeykey\r\n");
header.append("\r\n");
//-------------- CONNECTION ---------------
wxString host = _("192.168.50.11");
wxIPV4address * address = new wxIPV4address();
address->Hostname(host);
address->Service(8084);
if (socket->Connect(*address)){
//Write header
socket->Write(header.c_str(),header.Len());
//Write data
socket->Write(strToPrepend.c_str(),strToPrepend.Len());
const sf::Int16* samples = buffer.getSamples();
const char* bytesData = reinterpret_cast<const char*>(samples);
socket->Write(bytesData,dataLength);
socket->Write(strToAppend.c_str(),strToAppend.Len());
socket->Close();
}
delete socket;
delete address;
}
}
I am getting only some noises between gaps.
BTW. The audio is being sent to an IP camera p2 connector.
The data format is just the way your application treats them. After all you send raw bytes over a socket. And you can do it with anything you want
Int16 data;
const char* pBytesOfData = (const char*) &data;
int size = sizeof (Int16);
send( socket, pBytesOfdata, size, flags);
When the bytes arrive on the second end it is up to you to interpret them correctly. Probably you will want again treat them as Int16. You need to have a protocol (common way of communication) to do it right (maybe send size of the data at the begining of the transmission, etc).
You can also take a look on libraries that ease serialization: Boost.Asio and Boost.Serialization.
Firstly, You need to create and bind a socket. Then you have to send the data stored in "samples" to another peer by using socket API. For using socket API to send the data, you need to convert this data to char*. As send API of socket takes input of data you need to send as char*. For more information about sending you can go through this link. This is for windows. For Unix you can check the manpage for send API for unix.
Int16* is a pointer. The samples you get should also have an associated length. Your data will likely be between addresses: [samples, samples + length) (where samples is the address to the first sample).
To play the samples remotely (actual code will depend on what APIs you use):
open socket
in a loop
get samples from your microphone
transmit the data over socket
on the server, you will have to read samples in a loop and send them to whatever sound output API you use.
Sockets work with bytes, so in the end you will send bytes. As long as the way you interpret these bytes on the receiving side matches the data you sent, you can send whatever you want in those bytes.
In this case sending the samples directly without conversion seems the most trivial thing to do, but you will probably need to send the size of the sample before, most likely in a fixed length format, for example:
[size on 4 bytes][sample on `size` bytes]
[] [] [] [][] [] [] [] [] []
I have a trouble, my server application sends packet 8 bytes length - AABBCC1122334455 but my application receives this packet in two parts AABBCC1122 and 334455, via "recv" function, how can i fix that?
Thanks!
To sum up a liitle bit:
TCP connection doesn't operate with packets or messages on the application level, you're dealing with stream of bytes. From this point of view it's similar to writing and reading from a file.
Both send and recv can send and receive less data than provided in the argument. You have to deal with it correctly (usually by applying proper loop around the call).
As you're dealing with streams, you have to find the way to convert it to meaningful data in your application. In other words, you have to design serialisation protocol.
From what you've already mentioned, you most probably want to send some kind of messages (well, it's usually what people do). The key thing is to discover the boundaries of messages properly. If your messages are of fixed size, you simply grab the same amount of data from the stream and translate it to your message; otherwise, you need a different approach:
If you can come up with a character which cannot exist in your message, it could be your delimiter. You can then read the stream until you reach the character and it'll be your message. If you transfer ASCII characters (strings) you can use zero as a separator.
If you transfer binary data (raw integers etc.), all characters can appear in your message, so nothing can act as a delimiter. Probably the most common approach in this case is to use fixed-size prefix containing size of your message. Size of this extra field depends on the max size of your message (you will be probably safe with 4 bytes, but if you know what is the maximum size, you can use lower values). Then your packet would look like SSSS|PPPPPPPPP... (stream of bytes), where S is the additional size field and P is your payload (the real message in your application, number of P bytes is determined by value of S). You know every packet starts with 4 special bytes (S bytes), so you can read them as an 32-bit integer. Once you know the size of the encapsulated message, you read all the P bytes. After you're done with one packet, you're ready to read another one from the socket.
Good news though, you can come up with something completely different. All you need to know is how to deserialise your message from a stream of bytes and how send/recv behave. Good luck!
EDIT:
Example of function receiving arbitrary number of bytes into array:
bool recv_full(int sock, char *buffer, size_t size)
{
size_t received = 0;
while (received < size)
{
ssize_t r = recv(sock, buffer + received, size - received, 0);
if (r <= 0) break;
received += r;
}
return received == size;
}
And example of receiving packet with 2-byte prefix defining size of payload (size of payload is then limited to 65kB):
uint16_t msgSize = 0;
char msg[0xffff];
if (recv_full(sock, reinterpret_cast<char *>(&msgSize), sizeof(msgSize)) &&
recv_full(sock, msg, msgSize))
{
// Got the message in msg array
}
else
{
// Something bad happened to the connection
}
That's just how recv() works on most platforms. You have to check the number of bytes you receive and continue calling it in a loop until you get the number that you need.
You "fix" that by reading from TCP socket in a loop until you get enough bytes to make sense to your application.
my server application sends packet 8 bytes length
Not really. Your server sends 8 individual bytes, not a packet 8 bytes long. TCP data is sent over a byte stream, not a packet stream. TCP neither respects nor maintains any "packet" boundary that you might have in mind.
If you know that your data is provided in quanta of N bytes, then call recv in a loop:
std::vector<char> read_packet(int N) {
std::vector buffer(N);
int total = 0, count;
while ( total < N && (count = recv(sock_fd, &buffer[N], N-total, 0)) > 0 )
total += count;
return buffer;
}
std::vector<char> packet = read_packet(8);
If your packet is variable length, try sending it before the data itself:
int read_int() {
std::vector<char> buffer = read_packet(sizeof (int));
int result;
memcpy((void*)&result, (void*)&buffer[0], sizeof(int));
return result;
}
int length = read_int();
std::vector<char> data = read_buffer(length);
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);
Convert your struct into an array of characters before sending them - this is call serialisation
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
One the recieving end read the bytes and reconstruct the struct.
This will ensure that you will not have any hassle at all.
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 :)
You could even consider using serialization techniques: perhaps JSON, XDR, YAML could be relevant. or libraries like s11n, jansson, etc.
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);
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)