📜 ⬆️ ⬇️

Creating and testing a firewall in Linux, Part 2.3. Finish the firewall. We process traffic in userspace

Content of the first part:


The content of the second part:

2.1 - Introduction to the second part. We look at the network and protocols. Wireshark.
2.2 - Firewall Tables. Transport Layer. TCP structures, UDP. Extend the firewall.
2.3 - Expanding functionality. We process data in user space. libnetfilter_queue.
2.4 - Bonus. We study the real Buffer Overflow attack and prevent it with the help of our Firewall.

Part 2.3 - Introduction


We finished the last part on
')
if(dest_port == HTTP_PORT || src_port == HTTP_PORT) { printk("HTTP packet\n"); } 

In this part we will see how easy it is to send packets from the kernel space to the user space and back. For example, we take exactly the HTTP connection and add blocking of individual sites. In the bonus part, we consider the existing vulnerability and, as an example, how to deal with it.

libnetfilter_queue.


To our luck, there is a ready-made solution - a low-level library written in C for simple interaction with the netfilter of userspace - libnetfilter_queue . As you can guess from the name, when we receive the next packet and the hook_function we set is called, there is an opportunity to accept a packet ( return NF_ACCEPT ), throw out ( return NF_DROP ), and it is possible to place it in a special queue (queue) by simply running:

 #define HTTP_QUEUE_NUMBER 1 return NF_QUEUE_NR(HTTP_QUEUE_NUMBER); 

HTTP_QUEUE_NUMBER is the queue number I have defined where to send a packet (there may be several queues, which is convenient for simply separating packets into types)
NF_QUEUE_NR is the macro to be used (it makes bit shifts to return the desired number).
By default, you can also write:

return NF_QUEUE

and then all packets will be sent to the queue with the number zero. That is, the only thing we need to change in the code to send all the packets associated with the http protocol is to add this line:

 if(dest_port == HTTP_PORT || src_port == HTTP_PORT) { printk("HTTP packet\n"); return NF_QUEUE_NR(HTTP_QUEUE_NUMBER); } 

Now you need to get these packages in the user space. And here libnetfilter_queue will help us .

In order to use it, you need to download and install the necessary libraries. Details about the installation - here , everything you need to install.

Inside there is also a simple example, and possible installation problems are solved with the help of a search engine (I had to install another package). Let's look at the most important points from the example. In the function main () (I remind you that we are now in the user space!) Various initializations occur

 int main(int argc, char **argv) { … printf("opening library handle\n"); h = nfq_open(); if (!h) { exit(1); } … qh = nfq_create_queue(h, 0, &cb, NULL); if (!qh) { exit(1); } … for (;;) { if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) { printf("pkt received\n"); nfq_handle_packet(h, buf, rv); continue; } … 

It is important to specify the correct number QUEUE . In the example, this is zero, but we change it to our qh = nfq_create_queue (h, HTTP_QUEUE_NUMBER, & cb, NULL);
If all initializations were successful, then we will end up in an infinite loop, where we will “wait” for new packets from kernel to arrive. When such a packet arrives, the cb function is called, to which we also passed a pointer to nfw_create_queue :

 static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) { u_int32_t id = print_pkt(nfa); printf("entering callback\n"); return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL); } 

The function in turn calls print_pkt , which is passed a pointer to the structure containing the package that came to us. Each packet receives, in accordance with the order of receipt, the id number that print_pkt returns (which we will look at here), and at the end the verdict function of this packet is called — in this case, NF_ACCEPT — to accept.

Now to the “heart” of the example - print_pkt :

 static u_int32_t print_pkt(struct nfq_data *tb) 

In the original example, there are many functions for accessing various data via nfq_data * tb , we will consider access to data at those levels that we are familiar with from the previous parts - Application , Transport , Network

Here is the complete function code:

 static u_int32_t print_pkt(struct nfq_data *tb) { int i = 0; int packet_len = 0; unsigned char *data = NULL; int ip_src_array[4] = {0}; int ip_dst_array[4] = {0}; packet_len = nfq_get_payload(tb, &data); ip_dst_array[3] = data[16]; ip_dst_array[2] = data[17]; ip_dst_array[1] = data[18]; ip_dst_array[0] = data[19]; ip_src_array[3] = data[12]; ip_src_array[2] = data[13]; ip_src_array[1] = data[14]; ip_src_array[0] = data[15]; char ip_dst_str[20] = {0}; char ip_src_str[20] = {0}; ip_hl_to_str(ip_dst_array, ip_dst_str); ip_hl_to_str(ip_src_array, ip_src_str); printf("src_ip = %s, dst_ip = %s", ip_src_str, ip_dst_str); printf("\n"); if( packet_len >= 0x34 ) { for(i = 0x34; i < packet_len; ++i){ if(data[i] >= ' ' && data[i] <= '}') printf("%c", (int)data[i]); } } printf("End of packet checking\n"); return NF_ACCEPT; } 

Well, there should be no more questions.

Compile, check:

This is how all the parts look, the http server that will listen for http traffic, the interface — device management, the module — our firewall. Compile the http server, start the firewall , start the http server:



Separately, we go with host2 and this time for interest we connect to host1 on port 80 and send the GET command to get the main page:



We look at the results of what the http server writes. We can see the GET command we sent, and after it the whole page that host1 sent us in response to the request.



Now it is quite obvious how, for example, to block access to individual sites.

Completion of the first and second part


We started the first part by building a simple network of three computers with the linux operating system. Then we wrote a simple device that skillfully counted the number of packets that pass through the network cards of the computer, as well as cut and prohibit their further transmission. All this happened at the core of the operating system. To make it convenient for us to control the device and receive information from it, we added a user interface that could send commands to our device and receive data from it. So we received statistics in a convenient form.

The second part we began with the study of networks and protocols, examining the details of some of them. And then we applied our knowledge, significantly expanding the capabilities of the firewall and gaining access to all the necessary information that is contained in the package at the OSI levels we are interested in. All this happens in the kernel space.

In this part, we sent some of the packages to the user space, thereby greatly expanding the convenience of writing code and its functionality, discovering the possibility of using the many available ready-made C / C ++ libraries. It should be noted that, having done this, we “paid” in performance, but at the same time did not load the core of the operating system and shifted the traffic analysis to user application. Now the work of the firewall has become much slower, but in our case it does not matter.

Modern firewall is a very complex hardware-software complex, where everything is sharpened for speed and quality, and which is installed on the main "entrance" to large networks and processes gigabytes per second. You can imagine that even a slight performance degradation (for example, packet processing) in gigabytes will be very expensive. Therefore, they are constantly looking for weak links and working to improve their performance. In addition, the task of modern firewall includes not only checking the rules, but also much more complex tasks: repelling various types of DOS attacks, viruses, preventing information leakage, hacking attempts to computers, accessing suspicious sites and downloading suspicious content. We will consider the simplest example as a bonus article in the last part - the final one.

Source: https://habr.com/ru/post/316778/


All Articles