How to send a SNMP-Packet via UDP - c

The idea of my socket is receiving and sending SNMP-packets - GetRequest/GetResponse. However, the socket already receive SNMP-packets
...
unsigned char buf[8192];
...
for (;;) {
// Receive snmp message from snmp manager
recv_len = recvfrom(my_socket, buf, BUFSIZE, 0, (struct sockaddr
*)&remote_addr, &addr_len);
...
Now I want answer the request by a SNMP-Packet which is represents as string. Note: The response contains a GetResponse-PDU (a2).
unsigned char * packet = "302902010004067075626c6963a21c...";
int r = sendto(my_socket, packet, strlen(packet), 0, (struct sockaddr *)&remote_addr, addr_len);
The return value or r is 4 and is definitely wrong because my responding SNMP-packet packet is larger than 4. Why, because it's a pointer?.
Further, the response SNMP-Packet packet is not send as SNMP-Packet, it looks different compare to packet and is not interpret as a SNMP-Packet in Wireshark. It looks like that:
How can I send a valid SNMP-Packet?

Your problem is the strlen(packet) value, which is 4 due to the 00 byte.
strlen is meant to be used with ASCII strings, not binary strings.

Related

Best practice of sending data of different sizes over the network

I want to send data in varying sizes over UDP. The size of data to be sent is not fixed. I have the following scenario:
unsigned char buffer[BUFFERSIZE];
int bytes = fill_buffer(buffer, sizeof(buffer)): // Returns number of filled bytes.
sendto(socket, buffer, bytes, 0, (struct sockaddr *)&server, sizeof(server))
In the example above, receiving side does not know how many bytes to receive. I also thought of first sending the number of bytes to receive and then sending the data. But in that case, I don't know what would happen if the packets arrive out-of-order.
Sender side would be
sendto(socket, &bytes, sizeof(bytes), 0, (struct sockaddr *)&server, sizeof(server))
sendto(socket, buffer, bytes, 0, (struct sockaddr *)&server, sizeof(server))
Receving side would be
recvfrom(socket, &bytes, sizeof(bytes), 0, NULL, NULL)
recvfrom(socket, buffer, bytes, 0, NULL, NULL)
But could it be that sent data comes out-of-order?
I think you can send both in a single datagram if you add a message header.
The sender only sends the amount of payload data it has.
The receiver always requests the maximum payload size but examines the header and the return from recvfrom to determine the actual length.
Here's some rough code that illustrates what I'm thinking of:
struct header {
u32 magic_number;
u32 seq_no;
u32 msg_type;
u32 payload_length;
} __attribute__((__packed__));
#define MAXPAYLOAD 1024
struct message {
struct header info;
unsigned char payload[MAXPAYLOAD];
} __attribute__((__packed__));
void
sendone(int sockfd,const void *buf,size_t buflen)
{
struct message msg;
static u32 seqno = 0;
memcpy(&msg.payload[0],buf,buflen);
msg.info.magic_number = 0xDEADADDE;
msg.info.seq_no = seqno++;
msg.info.payload_length = buflen;
sendto(sockfd,&msg,sizeof(struct header) + buflen,...);
}
ssize_t
getone(int sockfd,void *buf,size_t buflen)
{
struct message msg;
ssize_t rawlen;
ssize_t paylen;
static u32 seqno = 0;
rawlen = recvfrom(sockfd,&msg,sizeof(struct header) + MAXPAYLOAD,...);
paylen = msg.info.payload_length;
if (rawlen != (sizeof(struct header) + paylen))
// error ...
memcpy(buf,&msg.payload[0],paylen);
return paylen;
}
The receiver can check the magic number and sequence number to look for corruption or missing/dropped packets, etc.
In fact, you can probably get more efficiency by using sendmsg and recvmsg since they allow you to send a single message using a scatter/gather list. (i.e.) The data would not have to be copied in/out using memcpy from the message struct [you'd only need struct header], so closer to zero copy buffering.
Another option may be to use the MSG_PEEK flag with the recvfrom/recvmsg. I've never used this myself, but it would be something like:
Do recvmsg with length of sizeof(struct header) with MSG_PEEK flag
Do second recvmsg with length of sizeof(struct header) + msg.info.payload_length
This is just a nicety of not having to always provide a maximum sized buffer. Since it involves two syscalls, it may be a bit slower. But, it might allow allow some tricks with selecting a payload buffer from a pool, based on the type of message and/or length
Unlike TCP which is a stream-based protocol, meaning that calls to recv don't exactly correspond to a call to send, UDP is packet based meaning that each recvfrom matches with exactly one sendto. This also means you need to take care of how large each message you send is.
If you send a UDP datagram that is larger that what can be contained in a IP packet, the UDP message will be fragmented across multiple UDP packets, increasing the chance of data loss. That's something you want to avoid. Also, if you're using IPv6, you'll get an error when you attempt to send because IPv6 doesn't support fragmentation.
What does this mean in relation to what you're doing? It means that, roughly speaking, your messages shouldn't be any larger than about 1450 bytes, so you can use that value as the size of your input buffer. Then you can use the return value of recvfrom to see how many bytes were actually read. If your messages are larger than that, you should break them up into multiple messages.
As with any UDP based protocol, you need to account for the case where messages get lost and they need to be retransmitted, or if messages come out of order.
Actually answer to this question was quite simple.
Given:
unsigned char buffer[BUFFERSIZE];
int bytes = fill_buffer(buffer, sizeof(buffer)): // Returns number of filled bytes.
sendto(socket, buffer, bytes, 0, (struct sockaddr *)&server, sizeof(server))
The return value of recvfrom tells us how many bytes are received, although we make a full read,
int bytesReceived = recvfrom(socket, buffer, sizeof(buffer), 0, NULL, NULL);
// Process bytesReceived number of bytes in the buffer

Populate UDP payload in socket programming with my own structure?

I want to send a variable of custom struct over udp sockets using sendto(). This struct contains UDP payload. When I googled a bit, I found that it is possible to create raw socket in c using the flag, SOCK_RAW, while creating the socket. But I think, then i will have to populate ip header as well as udp header too. I want to avoid that.
But when I tried sending the custom structure over socket as mentioned above, I receive different data ( not the structure contents) when sniffed with wireshark. Please help.
I tried changing the struct to normal uint32_t variable and used bit shifting operations to populate this variable. I confirmed that this variable has exactly the contents that I intended to add to it.
But again, I am not able to send this too over sockets. Wireshark displays different content. Is it because in sendto(), we provide the address of the buffer and not its value?
uint32_t disc_req[1];
//disc_req is populated with bit shift operations in the following function
create_discovery_req(disc_req);
//now disc_req has exactly the required binary format - I confirmed this
// But the following sendto() sends different data. Confirmed over wireshark.
sendto(sockfd, &disc_req, sizeof(disc_req),
MSG_CONFIRM, (const struct sockaddr *) &servaddr,
sizeof(servaddr));
Expeced output is value of disc_req sent over socket and the same captured on wireshark. But the actual value of disc_req is something different.
The different content you are observing may be due to the sender and receiver having different endianess (host vs network). If so you will want to use hton...() and ntoh...() when working with multi-byte values.
In general, when you send an array or struct over UDP, treat it like a byte array.
Create a UDP socket, not a RAW socket:
sockfd = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
Setup your destination address using the method you prefer (i.e. gethostbyname() )
Then send the payload:
sendto( sockfd, ( char* ) &disc_req[0], sizeof( disc_req ), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr));
To receive the payload:
recvfrom( sockfd, ( char* ) &disc_req[0], sizeof( disc_req ), 0, (const struct sockaddr *) &servaddr, sizeof(servaddr) );
The following is a complete example sending an NTP packet using UDP:
https://lettier.github.io/posts/2016-04-26-lets-make-a-ntp-client-in-c.html

Udp readfrom until the end

Socket Used : udp
I have a client who sends 5000 bytes and a server with this code :
Client code :
cod = sendto (sFd, (void *) buffer, 5000, 0, (struct sockaddr *)
&soc, sizeof (struct sockaddr_in));
Server code :
//leghtBuffer = 5000
while (lenghtBuffer > 0 )
{
//I will insert a signal if pass more than 30 s ...
cod = recvfrom (sFd, buffer,256 , 0, (struct sockaddr *) &clientSoc, &lungimeSocket);
printf("Am received %d bytes " ,cod );
lenghtBuffer = lenghtBuffer - cod;
}
How can I read more than 1 time 256 bytes from this while (still using Udp socket)?
UDP is a message (datagram) based transport protocol and not stream based like TCP. Each sendto/recvfrom works in terms of complete messages.
On the sending end, each call to sendto() creates a datagram of the specified size and data, which in your case is 5000 bytes.
On the receiving end, each call to recvfrom() copies the next message in the receive buffer. Since your datagrams are 5000 bytes, but you're only providing a 256 byte buffer, your datagram is being truncated to 256 bytes as it's copied.
The recvfrom() OpenGroup specification clarifies the behavior in this case:
For message-based sockets such as SOCK_DGRAM and SOCK_SEQPACKET, the entire message must be read in a single operation. If a message is too long to fit in the supplied buffer, and MSG_PEEK is not set in the flags argument, the excess bytes are discarded.
You'll want to increase your buffer on the receive end to be 5000 bytes to account for the entire datagram

Is this the correct way to send binary data over UDP using C?

I've been set a task to write some software to communicate from our embedded linux device to a server. The server that I'm to communicate with uses a strict protocol, the protocol is very obscure and proprietary - I wouldn't like to name it on here as the information could be sensitive to the company I work for.
So the data must be in either the form of 4 bit Nibbles (N), 32 bit unsigned ints (U), 32 bit signed ints (S), 8 bit unsigned ints (X) and chars (C). So for example a simplified login structure might be NNNN-User ID followed by XX-some more data, CCCC-access code. So I need to send NNNNXXCCCC in that order to login.
The data needs to be sent via UDP, and then listen for an acknowledgement on the same port. So the way I've done this is I've written a send_and_receive function, and then coded a struct, to send the struct through to the server.
typedef u_int8_t NN;
typedef u_int8_t X;
typedef int32_t S;
typedef u_int32_t U;
typedef char C;
#pragma pack(1)
typedef struct{
NN user_id[2];
X some_data[2];
C access_code[4];
} LogOnRequest;
I then declare and fill in the information for the struct and send it using this function:
void send_and_receive(void* message, void* reply, int do_send, int expect_reply){
struct sockaddr_in serv_addr;
int sockfd, i, slen=sizeof(serv_addr);
int buflen = BUFLEN;
void* buf = NULL;
if ( (strlen(message)) >= BUFLEN)
err("Message too big");
buf = malloc(buflen);
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
err("socket");
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_aton(IP_ADDRESS, &serv_addr.sin_addr)==0)
err("inet_aton() failed\n");
if(do_send == TRUE){
strcpy(buf, message);
if (sendto(sockfd, buf, buflen, 0, (struct sockaddr*)&serv_addr, slen)==-1)
err("sendto()");
}
if (expect_reply == TRUE){
if (recvfrom(sockfd, buf, buflen, 0, (struct sockaddr*)&serv_addr, &slen)==-1)
err("recvfrom()");
}
memcpy(reply, buf, BUFLEN);
close(sockfd);
free(buf);
}
Now when I do this I get no reply, and no notice that the packet has been received on the server. Basically I would like to know if I'm doing this correctly, is there a better way to do it? Would it be possible to do the same thing using bash scripts?
Any feedback would be great, because I'm feeling out of my depth on this one.
You need to wait some amount of time for a reply. If you don't get one, you need to retransmit the query and wait again. UDP is a "best effort" delivery service and does not have any built in retries.
You also need to make sure you are sending multi-byte values the same way the other end expects them. Which byte comes first?
As usual in such cases, I'd like to encourage you to use WireShark or any other sniffer software. This will de-cypher all the magic happens after your sendto() attempt: bytes order, actual address/port applied, any feedback received etc. And yes, as #wildplasser said in the comments, you shouldn't send all the buffer. So you have to add "message size" as an input parameter.

when the first datagram is received, recvfrom() doesn't fill the structur `sockaddr`

I have two machines A and B in LAN
I have a UDP client on A
and a UDP server on B, like
for(;;){
n = recvfrom(sockfd, mesg, 10000, 0, (struct sockaddr *)&cliaddr, &len);
....}
I notice when the UDP client sends the first datagram
I can get the datagram data payload through mesg correctly
but the structure cliaddr is not filled, it is with the original value
e.g, if I use bzero(&cliaddr, sizeof(cliaddr));,
in gdb, I got
$1 = {sin_family = 0, sin_port = 0, sin_addr = {s_addr = 0}, sin_zero =
"\000\000\000\000\000\000\000"}
what is the reason when the first datagram is received, recvfrom() doesn't fill the structure cliaddr?
for the sebsequent datagram, the valid info can be obtained.
Before calling recvfrom you must properly initialize the len argument.
E.g.
len = sizeof(struct sockaddr_in);
n = recvfrom(..., &len);
The recvfrom function uses the length to help determine what kind of structure the sockaddr pointer actually points to.

Resources