pcap_open_dead to simulate full UDP packets capture - c

Following up on my question about pcap file creation, I now would like to simulate the saving of a full UDP packet, including the Ethernet, IP and UDP headers.
Which DLT_XXX type should I use? I believe pcap_dump() skips the Ethernet header when using pcap_open_dead(DLT_RAW, 65535).

If you want to simulate a full UDP-over-IP-over-Ethernet packet, you want DLT_EN10MB (the "10MB" in the name is historical; DLT_EN10MB really means "all types of Ethernet").
(DLT_RAW is for packets where the lowest-level headers are for IP; it doesn't skip the Ethernet header, it means that you don't have to provide an Ethernet header and, in fact, it requires that you don't provide one - if you do provide one, it'll be written to the file, which will confuse programs reading the file, as they'll expect the packets to begin with an IPv4 or IPv6 header, not an Ethernet header.)

Related

Purpose of sendto address for C raw socket?

I'm sending some ping packets via a raw socket in C, on my linux machine.
int sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
This means that I specify the IP packet header when I write to the socket (IP_HDRINCL is implied).
Writing to the socket with send fails, telling me I need to specify an address.
If I use sendto then it works. For sendto I must specify a sockaddr_in struct to use, which includes the fields sin_family, sin_port and sin_addr.
However, I have noticed a few things:
The sin_family is AF_INET - which was already specified when the socket was created.
The sin_port is naturally unused (ports are not a concept for IP).
It doesn't matter what address I use, so long as it is an external address (the IP packet specifies 8.8.8.8 and the sin_addr specifies 1.1.1.1).
It seems none of the extra fields in sendto are actually used to great extent. So, is there a technical reason why I have to use sendto instead of send or is it just an oversight in the API?
Writing to the socket with send fails, telling me I need to specify an address.
It fails, because the send() function can only be used on connected sockets (as stated here). Usually you would use send() for TCP communication (connection-oriented) and sendto() can be used to send UDP datagrams (connectionless).
Since you want to send "ping" packets, or more correctly ICMP datagrams, which are clearly connectionless, you have to use the sendto() function.
It seems none of the extra fields in sendto are actually used to great
extent. So, is there a technical reason why I have to use sendto
instead of send or is it just an oversight in the API?
Short answer:
When you are not allowed to use send(), then there is only one option left, called sendto().
Long answer:
It is not just an oversight in the API. If you want to send a UDP datagram by using an ordinary socket (e.g. SOCK_DGRAM), sendto() needs the information about the destination address and port, which you provided in the struct sockaddr_in, right? The kernel will insert that information into the resulting IP header, since the struct sockaddr_in is the only place where you specified who the receiver will be. Or in other words: in this case the kernel has to take the destination info from your struct as you don't provide an additional IP header.
Because sendto() is not only used for UDP but also raw sockets, it has to be a more or less "generic" function which can cover all the different use cases, even when some parameters like the port number are not relevant/used in the end.
For instance, by using IPPROTO_RAW (which automatically implies IP_HDRINCL), you show your intention that you want to create the IP header on your own. Thus the last two arguments of sendto() are actually redundant information, because they're already included in the data buffer you pass to sendto() as the second argument. Note that, even when you use IP_HDRINCL with your raw socket, the kernel will fill in the source address and checksum of your IP datagram if you set the corresponding fields to 0.
If you want to write your own ping program, you could also change the last argument in your socket() function from IPPROTO_RAW to IPPROTO_ICMP and let the kernel create the IP header for you, so you have one thing less to worry about. Now you can easily see how the two sendto()-parameters *dest_addr and addrlen become significant again because it's the only place where you provide a destination address.
The language and APIs are very old and have grown over time. Some APIs can look weird from todays perspective but you can't change the old interfaces without breaking a huge amount of existing code. Sometimes you just have to get used to things that were defined/designed many years or decades ago.
Hope that answers your question.
The send() call is used when the sockets are in a TCP SOCK_STREAM connected state.
From the man page:
the send() call may be used only when the socket is in a connected
state (so that the intended recipient is known).
Since your application obviously does not connect with any other socket, we cannot expect send() to work.
In addition to InvertedHeli's answer, the dest_addr passed in sendto() will be used by kernel to determine which network interface to used.
For example, if dest_addr has ip 127.0.0.1 and the raw packet has dest address 8.8.8.8, your packet will still be routed to the lo interface.

send ipv6 jumbograms in c (linux) : How to change packet headers

I am sorry if the question is too naive, but I am confused. I want to send IPv6 jumbograms (to be able to multicast packets of size > 64 KB). I have been able to multicast normal IPv6 UDP packets successfully.
For sending jumbograms, from RFC 2675, I get that I have to make the following changes :
set payload length to 0
set next header to hop-by-hop
But, I don't get how to implement these in c socket programming (which function calls to make etc.). Do I have to create a custom header or are there functions like sendto available to send jumbograms?
You could use raw sockets if you are making your own headers. For more information, type man -s7 raw or look here. Note you will effectively need to implement your own IP stack that way.
However, my understanding is that linux itself supports IPv6 jumbograms natively so you don't need to bother. Try ifconfig lo mtu 100000 and do some tests over the loopback device to check.
I suspect the issue might be that your network adaptor and everything on the path (end to end) needs to support the jumbograms too.

OSI Layers on local host

I wrote a small application to try to display the protocol headers of captured packets. All my packets are captured with libpcap's pcap_loop. The way my program works is as follows: I wrote my own headers based of the structures defined in if_ether.h ip.h and tcp.h. pcap_loop sets a char pointer to the beginning of the packet, and I then step through the packet, casting to the appropriate structure each time, and incrementing the pointer by the size of the header. Now it's important to remember my question isn't code specific; my code works but there are logical flaws I dont undestand; Keep in mind my packets are sent over the same machine, different port(I wrote tiny python server that I send data to with telnet):
1.the ethernet header doesn't display anything that looks correct when packets are sent over localhost (When I use my program on internet packets, MAC adresses are dosplayed correctly though)
2.Through trial and error, I've determined that the structure iphdr starts exactly 16 bytes after the start of the packet buffer, as opposed to the expected 14 bytes, the size of the ethernet header
Those observations lead me to ask the following questions:
When packets are sent over local host, do we use another protocol on layer 2?
Is there anything at all that separates the packet headers?
Are the iphdr and tcphdr structures defined in ip.h and tcp.h obsolete?
When packets are sent over local host, do we use another protocol on layer 2?
There really isn't a layer 2 protocol, as there's no real network adapter.
However, there are fake layer 2 headers provided to programs that capture traffic. What fake headers are provided are operating-system-dependent.
On Linux, the fake layer 2 headers are fake Ethernet headers.
On *BSD, OS X, iOS, and, I think, Solaris 11, they're either DLT_NULL or DLT_LOOP headers, as described in the list of libpcap/WinPcap/pcap/pcap-ng link-layer header types.
However:
Through trial and error, I've determined that the structure iphdr starts exactly 16 bytes after the start of the packet buffer
if you're capturing on the "any" device, the headers are DLT_LINUX_SLL headers, which are 16 bytes long.
If you are using pcap or any pcap wrapper, you MUST, without exception, call pcap_datalink(), or the wrapper's equivalent, before trying to parse any packets you capture or read from a savefile. You MUST NOT assume the packets will have ANY particular link-layer header type.

Why is the IP layer not removed by the kernel in the ping program

I am looking into the standard ping implementation. Here the icmp structure is created and the data is filled in. The IP layer is added by the kernel. However when we receive a message using the function http://linux.die.net/man/2/recvfrom I observe that they are first parsing the IP packet and then parsing the ICMp packet. Why is this happening. The code I am referrring to is the standard ping implementation available online.
It's because the header is always included when receiving an IPv4 packet on a raw socket. Notice the following in raw(7) (emphasis mine):
The IPv4 layer generates an IP header when sending a packet unless the IP_HDRINCL socket option is enabled on the socket. When it is enabled, the packet must contain an IP header. For receiving the IP header is always included in the packet.
Since the header is always included and has variable length (for IPv4), it must be parsed to figure out where the ICMP data starts.
As for why the header isn't removed (sorry if that was the only thing you were wondering), I don't know. My wild guess is that enough programs that deal in raw IPv4 want to look at the header that it didn't seem worthwhile to include stripping it as an option. From a quick look it seems the header is stripped for IPv6.
The standard ping and ping6 come from iputils by the way, where ping_common.c, ping.c, and ping6.c are the most relevant source files.

C / determining content of received UDP packet

When receiving a UDP packet, where I do not know neither content nor structure, how can I find out what the content is? Is that possible somehow? Wireshark tells me the data is:
12:03:01:00:00:00:00:00:1f:44:fe:a2:07:e2:4c:28:00:15:e1:e0:00:80:12:61
The question is just about the data itself, not header of lower layers.
Thanks in advance for your help.
Wireshark shows UDP header. There is 2 port numbers. Usually another port number is reserved for the used protocol (not always).
You may look from the port reservation table which is the used protocol.
And if you are lucky, you find the protocol specification from Web and you can open the content of the packet.

Resources