
I will say straight away that what we are going to do here, we do not claim, say, any industrial applicability. Moreover, I admit that my code in this example can be terrible, terrible and unnecessary. And yet - why not intercept packets in the middle of the week? So, slightly.
So, today we are going to do this:
')
1. Implement the simplest passive packet sniffer for TCP and UDP
2. Put it in the C-library as an extension for Python
3. Let us add an iterator interface to all this, so that the bytes fall as if from a horn of plenty
four.…
5. PROFIT!
Why is all this necessary?
Why do we need sniffers? To sniff the network traffic. No, I'm not joking, from English the verb “to sniff” translates to “smell”. Okay, okay, let's be more scientific - with their help, it is possible and necessary to analyze the network packets passing through the network card of the computer.
Sniffer are passive and active. A passive sniffer does precisely and only what is required of it — intercepts for analysis the traffic passing through the network card of the computer on which it is installed. It would seem so much cooler? However, an active sniffer not only monitors the network card, but also tries in every possible way to get to the traffic that walks through the local network and is not intended for other people's eyes. He does this, for example, using
ARP-Spoofing and all sorts of other dirty tricks. By the way, in non-switched networks (based on repeaters and hubs) a passive sniffer can suddenly discover superpowers in itself, because in the absence of switching all packets in such networks are sent to all hosts. Of course, only recipients should accept them, but ... :)
But in order to receive them, one must firstly force the network card to fire its personal secretary, who filters the correspondence, and start reading everything at once. This is scientifically called "
Promiscuous mode ", that is, "Illegible mode." And there is an ambush:

Yes, you need root privileges to put a card into illegible mode. Therefore, our script will ask them to run. So it goes.
Well, what?
As one famous movie character said, “We need to go deeper”. That is, dive deeper into the OSI layers, onto the data link layer. To do this, when creating a socket, you must specify the
PF_PACKET constant instead of
PF_INET :
int s_sock; struct ifreq ifr; strcpy(ifr.ifr_name, IFACE); if ( (s_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) { perror("Error creating socket"); exit(-1); } ifr.ifr_flags |= IFF_PROMISC; if (ioctl(s_sock, SIOCGIFFLAGS, &ifr) < 0) { perror("Unable to set promiscious mode for device"); close(s_sock); exit(-1); }
Usually, when opening such a socket, a specific protocol is indicated over which the interception takes place. If you specify the constant
ETH_P_ALL - all will be intercepted. The
ifreq structure
is used in Linux for
taxiing with a low-level network interface via ioctl () , and
IFACE in this case is just a string with the name of the interface looking in the network, for example,
“eth0” .
Actually, now all we have to do is read the data from the socket in a loop and see what happens:
int n = 0; char buf[MTU]; n = recvfrom(s_sock, buf, sizeof(buf), 0, 0, 0);
The easiest way to set
MTU here is 1500 - this is the standard maximum packet size on
Ethernet networks. For networks built according to another standard, for example,
FDDI , the value may be different.
Since we are working with
Ethernet , the data on the headers of the received packets are the easiest to write to the specially designed
kernel structure -
ethhdr . For different basic protocols down to the transport level, there are similar structures that, no matter how hard to believe, are named like
iphdr ,
tcphdr or even
udphdr (as they say, “I know a great joke about UDP, but not the fact that it comes to you "). As it should be in good old C, it is done something like this:
struct ethhdr eth; memcpy((char *) ð, data, sizeof(struct ethhdr));
Hmm ... And how to shove it in Python?
Everyone knows that in python we all have an object. In the case of the generator / iterator, there will also be no exception - we have to create an object that has a
__iter __ () method and can
next () . The object has a bunch of fields, most of which we do not need, so be careful - in front of a bunch of zeros.
PyTypeObject PyPacketGenerator_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "packgen", sizeof(PacketGeneratorState), 0, (destructor)packgen_dealloc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Py_TPFLAGS_DEFAULT, 0, 0, 0, 0, 0, PyObject_SelfIter, (iternextfunc)packgen_next, 0, 0, 0, 0, 0, 0, 0, 0, 0, PyType_GenericAlloc, packgen_new, };
This announcement shows that the working methods of our future object are
packgen_new () ,
packgen_next () and
packgen_dealloc () . The latter is a destructor and, in general, you can do without it if you really want and there is no extra data in memory. In addition, we need a structure in which we will store data on the state of the object at the current iteration. Since the only thing we need to store is a socket, and declare it:
typedef struct { PyObject_HEAD int s_sock; } PacketGeneratorState;
As I said above, you need to open the socket and configure the network card in spyware mode. The easiest way to do this is directly in the object's initialization method.
static PyObject * packgen_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { int s_sock; struct ifreq ifr; strcpy(ifr.ifr_name, IFACE); if ( (s_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) { perror("Error creating socket"); exit(-1); } ifr.ifr_flags |= IFF_PROMISC; if (ioctl(s_sock, SIOCGIFFLAGS, &ifr) < 0) { perror("Unable to set promiscious mode for device"); close(s_sock); exit(-1); } PacketGeneratorState *pkstate = (PacketGeneratorState *)type->tp_alloc(type, 0); if (!pkstate) return NULL; pkstate->s_sock = s_sock; return (PyObject *)pkstate; }
In principle, it would be realistic to return the packages from the module in the form of classes, but
I was too lazy and decided to make the dictionary easier and return the dictionary (and if someone remakes it in his own way, there will be a good fellow). All that remains to be done is to determine that we have
IP at the network level, and by IP header, in turn, determine the overlaying protocol (if anyone does not remember, the protocols are marked with digits and are spelled out in
/ etc / protocols ). It all works because our packages are similar to the creations of the Burger King chefs - each higher level adds its own title to the lower one. Take the good old brothers -
TCP and
UDP .
PyObject *packet; packet = PyDict_New(); if (!packet) return NULL; if (ntohs(eth.h_proto) == ETH_P_IP) { ip = (struct iphdr *)(data + sizeof(struct ethhdr)); PyDict_SetItemString(packet, "ip_source", PyString_FromFormat("%s", inet_ntoa(ip->saddr))); ... if ((ip->protocol) == IPPROTO_TCP) { tcp = (struct tcphdr *)(data + sizeof(struct ethhdr) + sizeof(struct iphdr)); PyDict_SetItemString(packet, "tcp_source_port", PyString_FromFormat("%d", ntohs(tcp->source))); ... } if ((ip->protocol) == IPPROTO_UDP) { udp = (struct udphdr *)(data + sizeof(struct ethhdr) + sizeof(struct iphdr)); PyDict_SetItemString(packet, "udp_source_port", PyString_FromFormat("%d", ntohs(udp->source))); ... } } return packet;
Voila!
It all looks like this:
Python 2.7.3 (default, Apr 20 2012, 22:44:07)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pysniff
>>> for i in pysniff.packgen():
... print i
...
{'ip_destination': '192.168.1.111', 'tcp_seq': '10972', 'ip_source': '173.194.32.53', 'tcp_offset': '8', 'tcp_source_port': '443', 'tcp_dest_port': '44021'}
{'ip_destination': '173.194.32.53', 'tcp_seq': '47475', 'ip_source': '192.168.1.111', 'tcp_offset': '8', 'tcp_source_port': '44021', 'tcp_dest_port': '443'}
{'ip_destination': '192.168.1.111', 'tcp_seq': '10972', 'ip_source': '173.194.32.53', 'tcp_offset': '8', 'tcp_source_port': '443', 'tcp_dest_port': '44021'}
{'ip_destination': '173.194.32.53', 'tcp_seq': '47475', 'ip_source': '192.168.1.111', 'tcp_offset': '8', 'tcp_source_port': '44021', 'tcp_dest_port': '443'}
For the sake of laughter I created a
repository on github , what if you want to develop an idea? Good luck to you sniffing, do not fall!