Confused by libcap (pcap) and wireless - c

Background: I'm teaching myself about packet sniffing. I run a very simple server in one shell, telnet to it from another, then try different methods to sniff on traffic. When I use raw sockets (IPPROTO_TCP), I capture what I send fine. I capture merely what I send, nothing else from the internet. libcap's behavior confuses me as follows:
(1) First, to check it out, I capture all devices with pcap_findalldevs (see (2) below as well). I find wlan0 fine. If I connect to 'all traffic' (per the man page) using
if ( !( pcap_handle = pcap_open_live(NULL, 4096, 1, 0, errbuf) ) )
I capture what I send (plus more, see (3)).
when I try to connect to it using
if ( !( pcap_handle = pcap_open_live("wlan0", 4096, 1, 0, errbuf) ) )
, which to me seems the proper way of doing this, not 'all', i capture lots of general traffic, but nothing I send. Ideas?
(2) I first find all devices using pcap_findalldevs. As the pcap_if_t structure possibly has several elements, I print all those out, to see the following:
Devices found:
1. eth0 - None:
family: 17, address: 2.0.0.0
2. wlan0 - None:
family: 17, address: 3.0.0.0
family: AF_INET, address: 192.168.0.159
family: 10, address: 0.0.0.0
3. usbmon1 - USB bus number 1:
4. usbmon2 - USB bus number 2:
5. usbmon3 - USB bus number 3:
6. usbmon4 - USB bus number 4:
7. usbmon5 - USB bus number 5:
8. any - Pseudo-device that captures on all interfaces:
9. lo - None:
family: 17, address: 1.0.0.0
family: AF_INET, address: 127.0.0.1
family: 10, address: 0.0.0.0
I am all new to this. Some devices offer capturing of AF_INET (=IPv4), IPv6 (10), and packet (17). when I connect to "wlan0", how is it ensured I connect to the proper of the 'addresses' of some device? Is that related to the problem?
(3) When using raw sockets, I really only capture what I sent to my server. When I use libcap, I also capture what, from the bytes printed out, must be internet headers. I am all new to this. If someone could elaborate what exactly I capture here which i don't capture on raw sockets, this would be appreciated. Are those UDP or ICMP packets which, by definition, my IPPPROTO_TCP socket would not capture, which would be why I didn't see those using raw sockets?
Many thanks.
Edit: I work under Ubuntu 10.04 on a Toshiba netbook, using gcc/gdb combo.

It's somewhat surprising that, when capturing on wlan0, you're not seeing packets you send, if they're actually being sent over your Wi-Fi device. Are you sending them to other machines on your Wi-Fi network? If, for example, you're sending them to other processes on your machine, they'll show up on lo, not on wlan0 (and if you send them to other machines on your Wi-Fi network, rather than to other processes on your machine, they will not show up on lo - no, all traffic doesn't eventually go through the loopback interface).
The list of addresses you get from pcap_findalldevs() is NOT a list of addresses for which you can capture on that interface, it's just a list of network addresses the system has for that interface. You don't get to choose which addresses for which to capture - it captures for all of them. You capture on an interface, not an address.
Libpcap is different from a raw socket; it gives you lower-layer headers than the ones for the data being sent or received, as well as that data. For an Ethernet device, you'll see Ethernet headers; for a Wi-Fi device, what you see depends on the OS you're on and the headers you select (on Linux, which is what you're using, you'll probably see Ethernet headers unless you capture in "monitor mode", in which case you'll either see Wi-Fi headers or some "radio" header such as radiotap headers followed by Wi-Fi headers); for the "any" device, you'll see "Linux cooked headers"; and so on. You'll need to call pcap_datalink() after calling pcap_open_live() to find out the header type for the interface; see the list of link-layer types (pcap_datalink() will return the DLT_ value, as listed there; don't assume the number given there is the same as the DLT_ value, compare with the DLT_ value by name).

Related

How to send SOCK_DGRAM packet using packet_mmap without being concerned about MAC address?

I want to send packets using packet_mmap for getting high packet transmission rate. I managed to send packets using packet socket in raw mode, which for that purpose I created L2, L3, and etc in buffer and send it using
sendto(fd_socket, NULL, 0, 0, NULL, sizeof(struct sockaddr_ll));
However, I don't want to bother with destination mac address. So I turned into using Dgram instead. If I want to employ sendto there is an argument for destination MAC Address. Even though this is not what I wished for in terms of not being concerned with arp cache and specifying destination's MAC address:
sendto(fd_socket, NULL, 0, 0, (struct sockaddr *) ps_sockaddr, sizeof(struct sockaddr_ll));
However I found that send is also allowed to be used with packet socket.
http://man7.org/linux/man-pages/man7/packet.7.html
Therefore, I filled the buffer with ip header and so on. In this case send returns zero as nothing is found to be sent, which I expect to be something other than zero, if there is an error with transmission of packet.
Is there a way to use packet_mmap without being concerned about L2 address?
No. When you use a packet socket in SOCK_DGRAM mode, you needn't construct the L2 header, but you must still provide the L2 address and then the system will construct the header for you. (So that would have the benefit of actually building the L2 portion of the packet for you and you wouldn't need to specify the source MAC address but you still need to specify the interface from which you want the packet sent and the target MAC address.)
How would the system know where to send the packet otherwise? I believe you were hoping that the system would look at the IP header you've already constructed in the packet buffer, and then do an interface selection and ARP lookup on your behalf, but by using a packet socket, you're bypassing that part of the network stack. (Also, if the IP address is not on the local LAN, there would be a routing step needed -- usually just finding the default gateway and ARPing for its MAC address.)
Now you can get most of what (I think) you want with a raw socket (http://man7.org/linux/man-pages/man7/raw.7.html). In this case, you're telling the kernel that you're using IP but not relying on it for any of the higher layers (> L3). And you can also construct your own IP header in this case and have the routing decision based on it using the IP_HDRINCL option.

What is the difference between type and protocol in c socket() function?

I'm reading and trying to get an idea of C, and I tried to program a Java chat with UDP and TCP a couple of years back, and as much as I pulled it off... I could not do it.
I want to program sockets and I'm reading tons of documentation, but there is always a part that is unclear, every kicking documentation has a flaw.
For example, there is one about
int socket(int domain, int type, int protocol);
The domain I will use is clearly AF_INET, and if I want a TCP Socket I think type should be SOCK_STREAM, but what is protocol? Documentation says it should be 0... why??? what is it?
From the man page for socket:
The protocol specifies a particular protocol to be used with the socket. Normally only a single protocol exists to support a particular socket type within a given protocol family, in which case protocol can be specified as 0. However, it is possible that many protocols may exist, in which case a particular protocol must be specified in this manner. The protocol number to use is specific to the “communication domain” in which communication is to take place; see protocols(5). See getprotoent(3) on how to map protocol name strings to protocol numbers.
According to the man page for protocols:
This file is a plain ASCII file, describing the various DARPA internet protocols that are available from the TCP/IP subsystem. It should be consulted instead of using the numbers in the ARPA include files, or, even worse, just guessing them. These numbers will occur in the protocol field of any IP header.
Each line is of the following format:
protocol number aliases ...
...
/etc/protocols The protocols definition file.
And in the /etc/protocols file on my linux box:
ip 0 IP # internet protocol, pseudo protocol number
hopopt 0 HOPOPT # hop-by-hop options for ipv6
icmp 1 ICMP # internet control message protocol
igmp 2 IGMP # internet group management protocol
ggp 3 GGP # gateway-gateway protocol
ipencap 4 IP-ENCAP # IP encapsulated in IP (officially ``IP'')
st 5 ST # ST datagram mode
tcp 6 TCP # transmission control protocol
cbt 7 CBT # CBT, Tony Ballardie <A.Ballardie#cs.ucl.ac.uk>
egp 8 EGP # exterior gateway protocol
igp 9 IGP # any private interior gateway (Cisco: for IGRP)
bbn-rcc 10 BBN-RCC-MON # BBN RCC Monitoring
...
And according to the man page for getprotocol:
The getprotobyname() function returns a protoent structure for the entry from the database that matches the protocol name name. A connection is opened to the database if necessary.
...
The protoent structure is defined in as follows:
struct protoent {
char *p_name; /* official protocol name */
char **p_aliases; /* alias list */
int p_proto; /* protocol number */
}
So if you pass "ip" to getprotobyname() it would return 0 which is the number you are using anyway. But using 0 directly is always safe even if you don't know the name of the protocol.
The last protocol parameter of socket() can be used with raw packets. I will try to explain it practically.
If you are using raw sockets to get packets from TCP stack, you can control the amount of packet data you want to send/receive with this parameter.
socket (AF_INET, SOCK_RAW, IPPROTO_TCP);
Above call will give you a raw packet in which kernel will take care of the packet up to IP header. You will have to manually fill in the rest of the packet when sending it or when you will read the packet, kernel will provide the contents of TCP header as well with the data.
On the other hand:
socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
Using IPPROTO_RAW, you can control the packet from IP layer upwards. i.e. kernel will provide you services up to ethernet header, rest of the packet is in your control.
There may be different protocols to support a particular socket type, so that's why you also can specify the protocol in socket(2).
From the manpage (emphasis mine):
The protocol specifies a particular protocol to be used with the
socket. Normally only a single protocol exists to support a
particular socket type within a given protocol family, in which case
protocol can be specified as 0.
However, it is possible that many protocols may exist, in which case a particular protocol must be specified in this manner.
So it is not mandatory to specify the protocol as 0. Actually 0 means that the standard library will figure out the correct protocol for you. But you could specify it as explicitly and it is perfectly valid to do so.
On Linux, you can see the available protocols by doing:
$ cat /etc/protocols
# Internet (IP) protocols
#
# Updated from http://www.iana.org/assignments/protocol-numbers and other
# sources.
# New protocols will be added on request if they have been officially
# assigned by IANA and are not historical.
# If you need a huge list of used numbers please install the nmap package.
ip 0 IP # internet protocol, pseudo protocol number
hopopt 0 HOPOPT # IPv6 Hop-by-Hop Option [RFC1883]
icmp 1 ICMP # internet control message protocol
igmp 2 IGMP # Internet Group Management
ggp 3 GGP # gateway-gateway protocol
ipencap 4 IP-ENCAP # IP encapsulated in IP (officially ``IP'')
st 5 ST # ST datagram mode

Few queries regarding raw sockets in C

I want to make a chat room using raw socket in C. I have following problems:
Q 1 : Can I use select function to handle multiple connections in case of raw sockets ?
Q 2 : Port nos in sockets are real ports or logically implemented for various applications on transport layer??
Q 3 : I am having one computer only so using lo ( local loop) as my interface. So the process which is initiating the chat has send first and then receive call, so it's receiving it's own data. How to restrict it?
Any help would be appreciated since that would help me in increasing my confidence on raw sockets.
Thanks :)
If you want this to be a real, usable chat system, stop. Don't use raw sockets. Huge mistake.
If you are just playing around because you want to put “raw sockets” under the “Experience” section of your résumé, you may read on.
You can use the select function to detect when a raw socket has a packet available to receive, and when it can accept a packet to transmit. You can pass multiple file descriptors to a single call to select if you want to check multiple raw sockets (or whatever) simultaneously.
Port numbers are part of the TCP and UDP protocols (and some other transport layer protocols). The kernel doesn't look for port numbers when receiving packets for raw sockets.
The raw(7) man page‚ states:
All packets or errors matching the protocol number specified for the raw socket are passed to this socket.
And it also states:
A raw socket can be bound to a specific local address using the bind(2) call. If it isn't bound, all packets with the specified IP protocol are received.
Therefore you probably want to at least use different IP addresses for each end of the “connection”, and bind each end to its address.
“But!” you say, “I'm using loopback! I can only use the 127.0.0.1 address!” Not so, my friend. The entire 127.0.0.0/8 address block is reserved for loopback addresses; 127.0.0.1 is merely the most commonly-used loopback address. Linux (but perhaps not other systems) responds to every address in the loopback block. Try this in one window:
nc -v -l 10150
And then in another window:
nc -s 127.0.0.1 127.0.0.2 10150
You will see that you have created a TCP connection from 127.0.0.1 to 127.0.0.2. I think you can also bind your raw sockets to separate addresses. Then, when you receive a packet, you can check whether it's from the other end's IP address to decide whether to process or discard it.
Just curious, why do you want to use raw sockets? Raw sockets (AF_INET, SOCK_RAW) allow you to send out "raw" packets, where you are responsible for crafting everything but the MAC and IP layers.
A1: There are no "connections" with raw sockets. Just packets.
A2: There are no "ports" with raw sockets. Just packets. "Port numbers" as we know them are part of the TCP or UDP protocols, both of which are above the level at which we work with raw sockets.
A3: This is not specific to raw sockets - you would have this issue regardless of your protocol selection. To really answer this, we would need to know much more about your proposed protocol, since right now, you're simply blasting out raw IP packets.

Broadcasting UDP packets using multiple NICs

I'm building an embedded system for a camera controller in Linux (not real-time). I'm having a problem getting the networking to do what I want it to do. The system has 3 NICs, 1 100base-T and 2 gigabit ports. I hook the slower one up to the camera (that's all it supports) and the faster ones are point-to-point connections to other machines. What I am attempting to do is get an image from the camera, do a little processing, then broadcast it using UDP to each of the other NICs.
Here is my network configuration:
eth0: addr: 192.168.1.200 Bcast 192.168.1.255 Mask: 255.255.255.0 (this is the 100base-t)
eth1: addr: 192.168.2.100 Bcast 192.168.2.255 Mask: 255.255.255.0
eth2: addr: 192.168.3.100 Bcast 192.168.3.255 Mask: 255.255.255.0
The image is coming in off eth0 in a proprietary protocol, so it's a raw socket. I can broadcast it to eth1 or eth2 just fine. But when I try to broadcast it to both, one after the other, I get lots of network hiccups and errors on eth0.
I initialize the UDP sockets like this:
sock2=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); // Or sock3
sa.sin_family=AF_INET;
sa.sin_port=htons(8000);
inet_aton("192.168.2.255",&sa.sin_addr); // Or 192.168.3.255
setsockopt(sock2, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast));
bind(sock2,(sockaddr*)&sa,sizeof(sa));
sendto(sock2,&data,sizeof(data),0,(sockaddr*)&sa,sizeof(sa)); // sizeof(data)<1100 bytes
I do this for each socket separately, and call sendto separately. When I do one or the other, it's fine. When I try to send on both, eth0 starts getting bad packets.
Any ideas on why this is happening? Is it a configuration error, is there a better way to do this?
EDIT:
Thanks for all the help, I've been trying some things and looking into this more. The issue does not appear to be broadcasting, strictly speaking. I replaced the broadcast code with a unicast command and it has the same behavior. I think I understand the behavior better, but not how to fix it.
Here is what is happening. On eth0 I am supposed to get an image every 50ms. When I send out an image on eth1 (or 2) it takes about 1.5ms to send the image. When I try to send on both eth1 and eth2 at the same time it takes about 45ms, occasionally jumping to 90ms. When this goes beyond the 50ms window, eth0's buffer starts to build. I lose packets when the buffer gets full, of course.
So my revised question. Why would it go from 1.5ms to 45ms just by going from one ethernet port to two?
Here is my initialization code:
sock[i]=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
sa[i].sin_family=AF_INET;
sa[i].sin_port=htons(8000);
inet_aton(ip,&sa[i].sin_addr);
//If Broadcasting
char buffer[]="eth1" // or eth2
setsockopt(sock[i],SOL_SOCKET,SO_BINDTODEVICE,buffer,5);
int b=1;
setsockopt(sock[i],SOL_SOCKET,SO_BROADCAST,&b,sizeof(b));
Here is my sending code:
for(i=0;i<65;i++) {
sendto(sock[0],&data[i],sizeof(data),0,sa[0],sizeof(sa[0]));
sendto(sock[1],&data[i],sizeof(data),0,sa[1],sizeof(sa[1]));
}
It's pretty basic.
Any ideas? Thanks for all your great help!
Paul
Maybe your UDP stack runs out of memory?
(1) Check /proc/sys/net/ipv4/udp_mem (see man 7 udp for details). Make sure that the first number is at least 8x times the image size. This sets the memory for all UDP sockets in the system.
(2) Make sure you per-socket buffer for sending socket is big enough. Use setsockopt(sock2, SOL_SOCKET, SO_SNDBUF, image_size*2) to set send buffer on both sockets. You might need to increase maximumu allowed value in /proc/sys/net/core/wmem_max. See man 7 socket for details.
(3) You might as well increase RX buffer for receiving socket. Write a big number to .../rmem_max, then use SO_RCVBUF to increase the receiving buffer size.
A workaround until this issue is actually solved may be to createa bridge for eth1+eth2 and send the packet to that bridge.
Thus it's only mapped to kernel-memory once and not twice per image.
It's been a long time, but I found the answer to my question, so I thought I would put it here in case anyone else ever finds it.
The two Gigabit Ethernet ports were actually on a PCI bridge off the PCI-express bus. The PCI-express bus was internal to the motherboard, but it was a PCI bus going to the cards. The bridge and the bus did not have enough bandwidth to actually send out the images that fast. With only one NIC enabled the data was sent to the buffer and it looked very quick to me, but it took much longer to actually get through the bus, out the card, and on to the wire. The second NIC was slower because the buffer was full. Although changing the buffer size masked the problem, it did not actually send the data out any faster and I was still getting dropped packets on the third NIC.
In the end, the 100Base-T card was actually built onto the motherboard, therefore had a faster bus to it, resulting in overall faster bandwidth than the gigabit ports.. By switching the camera to a gigabit line and one of the gigabit lines to the 100Base-T line I was able to meet the requirements.
Strange.

Discovering the default gateway without DHCP

A crazy question, but is there anyway to discover the default gateway without DHCP?
This would be for a device on a network which does not use DHCP which does not have an IP address as yet. I was thinking if I could discover the default gateway, then i could try to guess an unused ip address then broadcast on the network to see if it's being used.
I may be asking for trouble i understand. E.g. if there is already a computer which has a static IP and is shutdown.
Packet sniff for a while and then apply heuristics. I'm assuming IPv4 and Ethernet for the rest of this. This won't work so well if Ethernet Switches are used rather than HUBs. More on that at the end.
Create your own RARP (reverse address resolution) table based on the Ethernet (or whatever) and IP headers you see as well as actual ARP packets (ignoring broadcast and multicast IP and MAC addresses). Make sure that your table is able to map multiple IP addresses to a single hardware interface. Any IP addresses in the table that are entered or verified by actual ARP packets should be flagged as such.
The gateway(s) is likely to recieve and send more traffic. This traffic is likely to have many IP addresses from many different networks but have the same hardware address. The gateway will show up in your table as one MAC address with lots of IP addresses. It is likely that you will have observed an ARP transaction involving this MAC address address, so you can look for an IP address that aliases that MAC that also has the seen in ARP packet flag set. That is almost certainly a gateway's IP address.
You will probably be able to guess the network address and subnet mask based on the IP address of the gateway, but this might not be something you can trust. If you want to try to make this work on a network with a non-standard netmask you can try to do the following.
Make two IP address sized accumulator variables:
uint32_t acc_and = 0xFFffFFff;
uint32_t acc_or = 0x00000000;
And then for each ARP verified address in your table you do
acc_and &= ip_addr;
acc_or |= ip_addr;
You could also use destination IP addresses (that aren't multicast) in packets that came from the gateway for this, but you would have to know the gateway before you could flag them as such.
finding the netmask and network address
The network portion of the address should stay the same in both (after the first operation), but in acc_and the bottom bits should start clearing out while in the acc_or the bottom bits should start filling up. With enough samples you would be able to determine the network address by:
uint32_t net_addr = acc_and & acc_or;
and the netmask by:
uint32_t net_mask = acc_and ^ acc_or;
Getting enough sampled local IP addresses could take too long, so you can start trying to narrow it down by:
uint32_t almost_net_mask = acc_and ^ acc_or;
int i = 0;
while ( 1 & almost_net_mask ) {
i++;
almost_net_mask >>= 1;
}
uint32_t net_mask = 0xFFffFFff;
while( i-- ) {
net_mask <<=1;
}
This would find you the last 1 bit set. The lowest 0 bit is obviously not part of the local portion of the address. Alternately you could get the same i by:
i = ffs( ~ ( acc_and ^ acc_or ) ) ; // assuming that int on your system is 32 bit. if not use ffsl
If you really really want to be sure of your netmask and network address now you can try some other things. You can try to see if the gateway will attempt to forward packets you send it to what you think should be local addresses to them. For this would would have to go ahead and assign yourself an IP address, which is risky if your netmask isn't right.
choosing an IP for yourself
Try to choose the lowest legal IP address that you haven't seen traffic to or from to increase the chance that it is not out of range. You could spoof an ARP request for that address and look for any replies (you could use your own real MAC address but possibly make up an IP for this. doesn't have to be your IP, which might confuse things for a little bit). After you have found an IP address that you can't ARP claim it for yourself.
It is possible that you ended up with an IP address that is actually too high. This is difficult to know for sure since your netmask is still just a good guess. You're out of luck if this is the case because there is not free slot for you to live.
back to the netmask
To see if your netmask needs adjusting try sending IP packets with destination addresses that you haven't see in the local network but that could be in the local network based on your guesses at the netmask and the network address. If these guesses are off they should have too many bits assigned to the network address, so sending to addresses which change the lower bits from the netmasked portion of the (so far) best guessed network address is what you want. You can try sending these as you normally would by ARPing the IP address and seeing if anyone replies, but since you are guessing addresses and likely to miss, it might be better to try creating the packet(s) with the destination MAC address set to the gateway to see if it would forward it. It may be configured not to, so you could try to see if it would by first doing this for a member of the network which you have already observed in the network and then see if it forwards it for you. If the gateway forwards the packet then you can rely on its idea of the netmask to narrow down your idea of the netmask. If the gateway will not forward for packets destine for known members of the local network then you can either just keep using your idea of the local netmask and network until you have a reason to adjust it or send out ARP requests for addresses in that range (which will only answer your question as either "yes" or "maybe" with no possibility of a sure "no").
if you are using an Ethernet switch, rather than a HUB
If you are using an Ethernet switch things get much more difficult because the switch will not forward Ethernet frames to you if it knows that the frames should go somewhere else, so you will not see them. It will forward many ARP requests, though, since they are broadcast unless the sender still has an entry for that IP in its ARP cache and is just trying for an early renewal of that entry (this may not be done by all systems). This will make building your idea of the network and its members much more difficult, but you will likely be able to do a decent job. You will probably have to rely on the quantity of ARP request for the gateway and from the gateway being higher than the other systems to spot it, though. This is error prone as a file server could have similar traffic.
Could be done but not in easy way (according to my poor knowledge).
Solution I would analyze further:
Detect Gateway - 2 possibilities (all off them related with data sniffing):
capture DHCP offer packets and
retrieve gateway
look for packet which is addressed to outer network (e.g. do arp request on each target IP address - if no answer you have probable mac address of the default gateway - you need to find IP address of this gateway (analyze arp packets, all packets, etc..)
one you have ip address do a arp requests and find you free ip.
sniffing library I think is libpcap or winpcap.
Never done even something similiar but If I wouldn't find any references on google I would go this way I think.
On a Windows network, you could use IP 0.0.0.0 and open a UDP socket with port 67 (or maybe 68) and wait for SMB broadcast messages to learn an IP address. From there, you can use ARP packets to search for devices on addresses close to that IP.
Try to find an unused address that as close to the original IP (identical upper bits) in case the network uses a netmask smaller than 255.255.255.0. Once you find an unused IP, claim it for your device and send a gratuitous ARP.
Now continue your ARP scan, and whenever you find a device that responds, temporarily set it as your default gateway and try to ping a known IP address of a device on the Internet that will respond. If you get a response, you've found your gateway.
That's a lot of work when using DHCP will get you there 99% of the time.

Resources