Why would recvfrom() under report the packet size? - linux

I am working on a Linux server that listens for UDP messages as part of a discovery protocol. My code for listening follows:
rcd = ::select(
socket_handle + 1,
&wait_list,
0, // no write
0, // no error
&timeout);
if(rcd > 0)
{
if(FD_ISSET(socket_handle,&wait_list))
{
struct sockaddr address;
socklen_t address_size = sizeof(address);
len = ::recvfrom(
socket_handle,
rx_buff,
max_datagram_size,
0, // no flags
&address,
&address_size);
if(len > 0 && address.sa_family == AF_INET)
{
struct sockaddr_in *address_in =
reinterpret_cast<struct sockaddr_in *>(&address);
event_datagram_received::cpost(
this,
rx_buff,
rcd,
ntohl(address_in->sin_addr.s_addr),
ntohs(address_in->sin_port));
}
}
}
In the meantime, I have written a windows client that transmits the UDP messages. I have verified using wireshark that the messages are being transmitted with the right format and length (five bytes). However, when I examine the return value for recvfrom(), this value is always one. The size of my receive buffer (max_datagram_size) is set to 1024. The one byte of the packet that we get appears to have the correct value. My question is: why am I not getting all of the expected bytes?
In case it matters, my Linux server is running under Debian 5 within a VirtualBox virtual machine.

nos answered my question in the first comment. I was using the wrong variable to report the buffer length.

Related

Linux application for reading packets from raw socket stops receiving packets after sometime

I have written a Linux application program that receives UDP packets transmitted from a Desktop with fixed & known IP-address on the network. I am using a raw socket to receive packets on my system and filter the received packets based on the source address.
The problem I am facing is, the program runs fine for some time and I get all the required packets, but after a couple of hours, the application stops getting any packets. If I run the command,
tcpdump -i eth0 src 192.168.20.48 on my system, then I see that the system continues to receive the expected packets. But I am not sure what is causing my program to stop receiving packets.
Below is the code snippet used to open a raw socket, receive packets, and filter out the UDP packets transmitted from the known IP address.
int main()
{
int sockfd;
int one = 1;
struct timeval tv;
socklen_t len;
int bytes;
unsigned char tsptr[2048];
struct sockaddr_in cliaddr;
struct iphdr *iph;
int result=0;
char source_add[50];
char expected_source_add[50];
len = sizeof(struct sockaddr_in);
// Creating socket file descriptor
if ((sockfd = socket(AF_INET , SOCK_RAW , IPPROTO_UDP)) < 0 ) {
BRH_PERROR("socket creation failed");
return 1;
}
tv.tv_sec = 30;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &one, sizeof(one));
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,(char*)&tv,sizeof(tv));
strcpy(expected_source_add, "192.168.20.48");
while (1) {
/*Read fixed data count from socket*/
bytes =recvfrom(sockfd, tsptr, 1500, MSG_WAITALL, (struct sockaddr *)&cliaddr, &len);
iph=(struct iphdr*)tsptr;
//get only UDP packet
if (iph->protocol != 17) continue;
strcpy(source_add,inet_ntoa(cliaddr.sin_addr));
result = strcmp(expected_source_add,source_add);
/*receive data from expected IP address only*/
if( result == 0) {
//Consume the packet
}
}
return 0;
}
Any clue on why the packet receive stops on my application, even though tcpdump shows that packets are being received on the interface, will be helpful.
The code you write here can not see any problem that you describe, I think you should do something like below.
1. using wireshark or tcpdump to see if the nic receive packets successfully
2. beyond the program, do you use any buffer or message queue and are they working good?
3. using tools to see if there exists any memeory leak
4. writing log in every step, especially around recvefrom and strcmp

how to implement tcpdump -i interface arp with libpcap

I want to implement command tcpdump -i eth0 arp to observe arp packets on interface eth0 on my ubuntu. I use libpcap, but the return value of function pcap_next_ex is always 0. With tcpdump -i eth0 arp in the same time , it can observe arp packets.
/*
* compile(root): gcc test.c -lpcap
* run : ./a.out
* output : time out
* time out
* time out
* ...
*/
#include <pcap.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define ARP_REQUEST 1
#define ARP_REPLY 2
typedef struct arp_hdr_s arp_hdr_t;
struct arp_hdr_s {
u_int16_t htype;
u_int16_t ptype;
u_char hlen;
u_char plen;
u_int16_t oper;
u_char sha[6];
u_char spa[4];
u_char tha[6];
u_char tpa[4];
};
#define MAXBYTES2CAPTURE 2048
int
main(int argc, char **argv)
{
char err_buf[PCAP_ERRBUF_SIZE];
const unsigned char *packet;
int i;
int ret;
arp_hdr_t *arp_header;
bpf_u_int32 net_addr;
bpf_u_int32 mask;
pcap_t *desrc;
struct pcap_pkthdr *pkthdr;
struct bpf_program filter;
net_addr = 0;
mask = 0;
memset(err_buf, 0, PCAP_ERRBUF_SIZE);
desrc = pcap_open_live("eth0", MAXBYTES2CAPTURE, 0, 512, err_buf);
if (desrc == NULL) {
fprintf(stderr, "error: %s\n", err_buf);
exit(-1);
}
ret = pcap_lookupnet("eth0", &net_addr, &mask, err_buf);
if (ret < 0) {
fprintf(stderr, "error: %s\n", err_buf);
exit(-1);
}
ret = pcap_compile(desrc, &filter, "arp", 1, mask);
if (ret < 0) {
fprintf(stderr, "error: %s\n", pcap_geterr(desrc));
exit(-1);
}
ret = pcap_setfilter(desrc, &filter);
if (ret < 0) {
fprintf(stderr, "errnor: %s\n", pcap_geterr(desrc));
exit(-1);
}
while (1) {
ret = pcap_next_ex(desrc, &pkthdr, &packet);
if (ret == -1) {
printf("%s\n", pcap_geterr(desrc));
exit(1);
} else if (ret == -2) {
printf("no more\n");
} else if (ret == 0) { // here
printf("time out\n");
continue;
}
arp_header = (arp_hdr_t *)(packet + 14);
if (ntohs(arp_header->htype) == 1 && ntohs(arp_header->ptype == 0x0800)) {
printf("src IP: ");
for (i = 0; i < 4; i++) {
printf("%d.", arp_header->spa[i]);
}
printf("dst IP: ");
for (i = 0; i < 4; i++) {
printf("%d.", arp_header->tpa[i]);
}
printf("\n");
}
}
return 0;
}
Without getting too deep in your code, I can see a major problem:
In your use of pcap_open_live(), you do not set promiscuous mode: the third parameter should be non-zero. If the ARP request is not targeted to your interface IP, pcap will not see it without promiscuous mode. tcpdump does, unless specifically told not to do so by using the --no-promiscuous-mode, use promisc (and hence will require CAP_NET_ADMIN privilege, which you'll get by sudo, which your program will require too).
Side note:
1/ Leak: you may want to free your filter using pcap_freecode() after your pcap_setfilter().
2/ I assume you've read the official tuto here:
http://www.tcpdump.org/pcap.html
...if that's not the case you'd be well advised to do that first. I quote:
A note about promiscuous vs. non-promiscuous sniffing: The two
techniques are very different in style. In standard, non-promiscuous
sniffing, a host is sniffing only traffic that is directly related to
it. Only traffic to, from, or routed through the host will be picked
up by the sniffer. Promiscuous mode, on the other hand, sniffs all
traffic on the wire. In a non-switched environment, this could be all
network traffic. [... more stuff on promisc vs non-promisc]
EDIT:
Actually, looking deeper to you code compared to my code running for +1 year at production level (both in-house and at the customer) I can see many more things that could be wrong:
You never call pcap_create()
You never call pcap_set_promisc(), we've talked about this already
You never call pcap_activate(), this may be the core issue here
...pcap is very touchy about the sequence order of operations to first get a pcap_t handle, and then operate on it.
At the moment, the best advice I can give you - otherwise this is going to a live debugging session between you and me, are:
1/ read and play/tweak with the code from the official tutorial:
http://www.tcpdump.org/pcap.html
This is mandatory.
2/ FWIW, my - definitely working - sequence of operations is this:
pcap_lookupnet()
pcap_create()
pcap_set_promisc()
pcap_set_snaplen(), you may or may not need this
pcap_set_buffer_size(), you may or may not need this
pcap_activate() with a note: Very important: first activate, then set non-blocking from PCAP_SETNONBLOCK(3PCAP): When first activated with pcap_activate() or opened with pcap_open_live() , a capture handle is not in non-blocking mode''; a call to pcap_set-nonblock() is required in order to put it intonon-blocking'' mode.
...and then, because I do not use stinking blocking/blocking with timeout, busy looping:
pcap_setnonblock()
pcap_get_selectable_fd()
...then and only then:
- pcap_compile()
- followed by a pcap_setfilter()
- and then as I mentioned a pcap_freecode()
- and then a select() or family on the file'des' I get from pcap_get_selectable_fd(), to pcap_dispatch(), but this is another topic.
pcap is an old API starting back in the 80's, and its really very very touchy. But don't get discouraged! It's great - once you get it right.
It would probably work better if you did
if (ntohs(arp_header->htype) == 1 && ntohs(arp_header->ptype) == 0x0800) {
rather than
if (ntohs(arp_header->htype) == 1 && ntohs(arp_header->ptype == 0x0800)) {
The latter evaluates arp_header->type == 0x0800, which, when running on a little-endian machine (such as a PC), will almost always evaluate to "false", because the value will look like 0x0008, not 0x0800, in an ARP packet - ARP types are big-endian, so they'll look byte-swapped on a little-endian machine). That means it'll evaluate to 0, and byte-swapping 0 gives you zero, so that if condition will evaluate to "false", and the printing code won't be called.
You'll still get lots of timeouts if you fix that, unless there's a flood of ARP packets, but at least you'll get the occasional ARP packet printed out. (I would advise printing nothing on a timeout; pcap-based programs doing live capturing should expect that timeouts should happen, and should not report them as unusual occurrences.)

redirect ethernet packets through sk_buff to localhost

I am writing a Linux kernel module which redirects a packet to the localhost webserver ,which was originally forwarded through this machine using bridge . It also redirects to reply to the client . The client is oblivious of the redirection . So there are 2 parts
1. all forwarded packets through bridge to some webserver outside are redirected to local webserver .
The output of the localhost webserver is channelled to the original client
I am able to do the second part through nf_hook NF_INET_LOCAL_OUT
unsigned int snoop_hook_reply( unsigned int hooknum, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
int(*okfn)( struct sk_buff * ) )
{
int offset, len;
struct ethhdr *ethh;
struct iphdr *iph;
struct tcphdr *tcph;
bool flag = false;
struct net_device *eth1_dev , *lo_dev;
if (!skb) return NF_ACCEPT;
iph = ip_hdr(skb);
if (!iph) return NF_ACCEPT;
skb_set_transport_header(skb, iph->ihl * 4);
tcph = tcp_hdr(skb);
/* skip lo packets */
if (iph->saddr == iph->daddr) return NF_ACCEPT;
if (tcph->dest == htons(80))
flag=true;
if(flag != true)
return NF_ACCEPT;
// correct the IP checksum
iph->check = 0;
ip_send_check (iph);
//correct the TCP checksum
offset = skb_transport_offset(skb);
len = skb->len - offset;
tcph->check = 0;
if(skb->len > 60){
tcph->check = csum_tcpudp_magic((iph->saddr), (iph->daddr), len, IPPROTO_TCP, csum_partial((unsigned char *)tcph,len,0));
}
else{
tcph->check = ~csum_tcpudp_magic((iph->saddr), (iph->daddr), len, IPPROTO_TCP, 0);
}
//send to dev
eth1_dev = dev_get_by_name(&init_net,"eth1");
lo_dev = dev_get_by_name(&init_net,"lo");
skb->dev = eth1_dev;
ethh = (struct ethhdr *) skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);
skb->protocol = ethh->h_proto = htons(ETH_P_IP);
memcpy (ethh->h_source,eth1_dev->dev_addr , ETH_ALEN);
memcpy (ethh->h_dest, d_mac, ETH_ALEN); // d_mac is mac of the gateway
dev_queue_xmit(skb);
return NF_STOLEN;
}
the above code works perfectly for me . One issue is that later on I will mangle the packet so need to create a new sk_buff, probably .
I am not able to do the 1st part through NF_INET_PRE_ROUTING, I am not able to push the packet/sk_buff to the webserver process through the TCP/IP stack. I tried using dev_queue_xmit() function with skb->dev as both eth1 and lo . I am seeing the packets hitting on the lo or eth1 through tcpdump . But the packets are not reaching the localhost webserver. Can anyone help me regarding this or point to some similar answered question . I believe instead of dev_queue_xmit() I need to call some receiving function . Also when packets arrive in NF_INET_PREROUTING, I the ethernet headers are already there so I am not forming it .
I have already accomplished the above tasks in variety of ways , first using raw sockets , then using nf_queue , now I want to see the performance through this method.
Thanks
If you want to receive the packet locally, you cannot call dev_queue_xmit() on eth1 as it will be sent out. You probably need to call netif_rx() after pointing the skb->dev to eth1/lo.
One more point is if the dest-ip is not your local host ip, then you need to avoid routing again otherwise, there will be no use of your interception.
To achieve this, either you need to modify packet's dest ip to eth1/lo IP or
fool the IP layer by using skb_dst_set() to set "rth->dst.input= ip_local_deliver" for packet to be accepted as local packet.

linux raw socket - receive custom packets

I'm trying to receive IEEE1722 packet via a raw Ethernet socket on ubuntu linux.
The socket itself works fine, I receive any single packet (ARP,TCP,SSDP,....) flowing around on the network, with exception of the IEEE1722 packets. They are somehow ignored on my read calls and don't understand why - maybe someone of you has an idea.
The packets are 802.1 frames with VLAN tag and EtherType 0x22f0
Neither switching from ETH_P_ALL to ETH_P_8021Q or to htons(0x22f0) does help. If I change it I don't receive anything anymore.
That's my code - someone with an idea what's wrong?
Creating the socket:
m_socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (m_socket < 0)
{
LOGERROR("EthRawSock", "Start(): SOCK_RAW creation failed! error: %d",errno);
m_socket = NULL;
return ErrorFileOpen;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, m_sznic.ptrz());
if (ioctl(m_socket, SIOCGIFINDEX, &ifr) < 0) {
LOGERROR("EthRawSock", "Start(): ioctl() SIOCGIFINDEX failed! error: %d (NIC: %s)",errno,ifr.ifr_name);
return ErrorFileOpen;
}
struct sockaddr_ll sll;
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons(0x22f0);
if (bind((int)m_socket, (struct sockaddr *) &sll, sizeof(sll)) < 0) {
LOGERROR("EthRawSock", "Start(): bind() failed! error: %d",errno);
return ErrorFileOpen;
}
if (ioctl(m_socket, SIOCGIFHWADDR, &ifr) < 0)
{
LOGERROR("EthRawSock", "Start(): SIOCGIFHWADDR failed! error: %d",errno);
return ErrorFileOpen;
}
struct packet_mreq mr;
memset(&mr, 0, sizeof(mr));
mr.mr_ifindex = sll.sll_ifindex;
mr.mr_type = PACKET_MR_PROMISC;
if (setsockopt(m_socket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) {
LOGERROR("EthRawSock", "Start(): setsockopt() PACKET_ADD_MEMBERSHIP failed! error: %d",errno);
return ErrorFileOpen;
}
Reading via:
nsize = read(m_socket,m_recv_buffer,ETH_FRAME_LEN);
My two cents contribution:
AVTP streams run in a tagged frame, this means that you won't find the ethertype 0x22f0 at the expected offset (12 octets from start of packet, just after destination and source MAC addresses) - it will be 4 octets after that. The ethertype for VLAN tagged frames is normally 0x8100.
Have you tried wireshark - or tshark - on this interface? Wireshark should be able to get those packets fine - nots sure if you need to enable it though. If I'm not mistaken all network ports must support 802.1AS. IEEE 1722 requires hardware support and I think that it would be impossible to help you out without knowing what's how this was set up.

Is it possible that Accept return a same socket value for different tcp connections on the same machine

Is there possible that accept() (on redhat Enterprise 4/linux kernel 2.6) return a same socket value for different tcp connections from the same process of a same application and same machine?
I am so surprised that when I got such a result that many connections have the same socket value on server side when I checked the log file!! How is it possible?!!
By the way, I am using TCP blocking socket to listen.
main(){
int fd, clientfd, len, clientlen;
sockaddr_in address, clientaddress;
fd = socket(PF_INET, SOCK_STREAM, 0);
....
memset(&address, 0, sizeof address);
address.sin_address = AF_INET;
address.sin_port = htons(port);
....
bind(fd, &address, sizeof address);
listen(fd, 100);
do {
clientfd = accept(fd, &clientaddress, &clientlen);
if (clientfd < 0) {
....
}
printf("clientfd = %d", clientfd);
switch(fork()){
case 0:
//do something else
exit(0);
default:
...
}
} while(1);
}
my question is that why printf("clientfd = %d"); prints a same number for different connections!!!
If server runs in multiple processes (like Apache with mpm worker model), then every process has its own file descriptor numbering starting from 0.
In other words, it is quite possible that different processes will get exact same socket file descriptor number. However, fd number it does not really mean anything. They still refer to different underlying objects, and different local TCP ports.
The socket is just a number.It is a hook to a data structure for the kernel.
BTW TCP uses IP. Look up the RFC
That printf() doesn't print any FD at all. It's missing an FD parameter. What you are seeing could be a return address or any other arbitrary junk on the stack.

Resources