To realise and test the AI model, the Universidad Politecnica de Madrid offered the possibility to use a real network, made of physical switch, routers, network interfaces, cables and computers or create a virtual one, easier to manage, change and reusable in different contexts. For the reason already mentioned, it has been chosen to build a virtual environment both to test the AI model and to eventually use it for pedagogic purposes.
Among the technologies available for the implementation of the virtual network there were Kubernetes and Docker. For the sake of simplicity and under advice of the professor Bordel it has been chosen to work with Docker. 

\section{Docker}

Docker is an open-source platform created to facilitate the deployment, scaling and management of application by means of their automation. Docker is based on the containerization concepts, allowing the application to rely on container properties. Thus, it provides isolation, ensuring that the application environment does not get interference from others and does not influence others; portability, because it has everything the application need to work in whatever host; efficiency, since it share the host system's kernel making them lighter than the traditional virtual machines; scalability allowing an easy way to scale application, both horizontally and vertically \cite{docker_containers} \cite{boettiger2014}.

\begin{wrapfigure}{R}{0.4\textwidth}
    \centering
   \includegraphics[width=0.3\textwidth]{3VirtualNetwork/Docker-Logo.png}
    \caption{Docker's logo}
    \label{fig:dockerLOGO}
\end{wrapfigure}

Inside the container runs an image of the software package we choose. A docker image is, in fact, a lightweight, standalone and executable software that has everything needed to work, going from runtime libraries to environment variables and configuration files. The images are immutable, which means that they cannot be changed after their execution. Docker images works with a layered file-system, in which each layer corresponds to an addition or modification to the already compiled pile. The layered system makes this technology more efficient, allowing images with similar bases to share the same libraries without the need of wasting memory to store an atomic version of the same application. To create a layered image, it is possible to start from a pre-existent image and add new layers with the use of Dockerfile. Dockerfile is a text file that contains the instructions on how to build a Docker image, in which each instruction corresponds to a new layer \cite{docker_containers} \cite{boettiger2014}. 
To achieve the target of realising a virtual network, I relied on docker-compose. Docker-compose is an orchestration tool that simplifies the management of several containers as a single service. In this way, it is possible to combine routing services, servers, administrator node and client ones. Docker-compose configuration is determined inside a YAML file defining the key feature system, such as, definition of the services, management of dependency among the services and deployment in the correct order, network configuration, data volume management, environment variable configuration and command execution \cite{docker-compose-docs}.

To allow a service to perform a task with high privileges, it is possible to set the option \cmd{privileged} as \cmd{true}. This option provides full access to devices on the host and grants access to a set of instruction that otherwise would not be accessible, useful when the node needs to perform low-level system tasks. However, this option exposes the host to security risks breaking the principle of isolation, typical of the containers. Example of these operations could be mounting a host file-system, set network configuration or obtain hardware access to manage USB devices or control GPUs \cite{docker-compose-docs}. 

Another possibility is to use the option \cmd{cap\_add} that stands for additional capabilities. In this way the privileges are more granular and specific purposes compared to \cmd{privileged: true}. Linux divides the privileges of the user root into distinct units of capabilities. The list of the capabilities can be found in Linux manual \cite{docker-compose-docs}.
Use cases of \cmd{cap\_add} can be \cite{linux-capabilities}:
\begin{itemize}
    \item\cmd{NET\_ADMIN}: allows administration task, like configuring the network interfaces, managing routing tables and setting up firewall.
    \item\cmd{SYS\_ADMIN}: enables operations such as mounting filesystems and setting up of namespaces
    \item\cmd{KILL}: bypass the permission checks for sending signals
    \item\cmd{BPF}: also included in \cmd{SYS\_ADMIN}, is weaker and allows BPF operations. If the aim is to set up performance monitoring, it's also necessary to set \cmd{PERFMON}
\end{itemize}

To make the implementation easier and permissions free, has been chosen to proceed with the option \cmd{privileged: true}.

Docker offers the possibility to mount shared volumes between the container and the host. The property \cmd{volumes} allows data persistence and the chance of sharing data between containers and the host. Indeed, it has been used for data sharing, to allow easy updates and to make persistent a storage, since by restarting the containers the image is set at the starting entrypoint instead of the last state. With this option, I have been allowed to change the scripts needed and test them without stopping and rebuilding the container images. To avoid the use of shared-volume, it may be preferred at the final stage of a project, to copy the script inside the image and make it definitive \cite{docker-compose-docs}. 

The \cmd{ports} property is used for port mapping between the host machine and the Docker containers. In this way, it is possible to make service inside the containers accessible from the host and redirect traffic from a host port to a container port \cite{docker-compose-docs}.  

Each image is build by means of a Dockerfile, which is a file consisted of a series of instruction describing how the image is build. As anticipated, each line of the Dockerfile equals a layer of the image, allowing the system to be lighter by sharing common levels and faster to be built in case something is missing (it can be simply added at the end of Dockerfile appending a further level and building just this last layer). To equip a docker-compose file with a Dockerfile, it is necessary to add the option \cmd{build} and the suboption \cmd{dockerfile}, so that when the container will be build it will reference to the Dockerfile. In order to make it more organised, it is possible to specify a different name to the Dockerfile and store it in a folder. Consequently, to communicate the change to the \cmd{docker-compose.yml} it is mandatory to specify the \cmd{context} of the service.

\begin{lstlisting}[language=YAML, caption={Docker Compose Configuration}, label={lst:docker-compose}]
services:  
  admin-fluentd:
    build: 
      context: ./admin-fluentd
      dockerfile: Dockerfile_admin-fluentd
\end{lstlisting}

Dockerfile uses a set of commands to define the layer of the image:
\begin{itemize}
    \item \cmd{FROM}: Specifies the base image
    \item \cmd{RUN}: Executes a command in the shell. Usually used to install packages, clone repositories, build scripts and so on.
    \item \cmd{COPY}: Copies a file from a local repository to the container repository
    \item \cmd{WORKDIR}: The Dockerfile correspondent of \cmd{cd}
    \item \cmd{EXPOSE}: Specifies the port of the container that listens for connections
    \item \cmd{USER}: Set the username or UID to use when running the image
    \item others...
\end{itemize}
Here it is an example of Dockerfile that uses the listed commands:

\begin{lstlisting}[language=bash, caption={Dockerfile Commands with Comments}, label={lst:dockerfile-commands}]
FROM ubuntu:22.04
# Basic packages for iprouting and log
RUN apt-get update && apt-get install -y iproute2 iptables coreutils iputils-ping syslog-ng git

# Packages to run a very basic server
RUN apt-get install -y nodejs npm

# Starting of the very basic server
COPY ./server/package.json /app/package.json
COPY ./server/server.js    /app/server.js    
WORKDIR /app
RUN npm install
EXPOSE 3000 

WORKDIR /

RUN apt-get update && apt-get install kmod
# Copy of the entrypoint in the image
COPY entrypoints/ep_servidor.sh entrypoints/ep_servidor.sh
# Make the scripts executable 
RUN chmod +x /entrypoints/ep_servidor.sh

CMD ["/bin/bash"]
\end{lstlisting}

If some of the commands can not be compiled within the image, there is the possibility to run them in a bash script, used as entry point. As an instance, it can be very useful in case of configuration that needs to be done at run-time or package that need to be always updated. It is possible to make a container start by the entry point, by using the specific option \cmd{entrypoint}.

\begin{lstlisting}[language=yaml, caption={dockercompose.yml example}, label={lst:dockercompose-entrypoint}]
 admin-fluentd:
    build: 
      context: ./admin-fluentd
      dockerfile: Dockerfile_admin-fluentd
    privileged: true
    entrypoint: entrypoints/ep_fluentd.sh
\end{lstlisting}

\section{The virtual network}
To elaborate a complete and efficient system has been chosen a tree topology, with several routers, clients, one server and one administration node. As root has been positioned a router, called RouterB, and on top of it an access router that links the server network and the administrator node. The server network connects the servers that should provide a service to the network. The administrator node, called admin-fluentd, is in charge of managing the network and collecting information about the network. 

\begin{figure}[tbh]
\centerline{\includegraphics[width=0.9\textwidth]{3VirtualNetwork/VirtualNetwork.pdf}}
\caption{Virtual network structure}.\label{fig:virtualNet}
\end{figure}

As visible from the picture \ref{fig:virtualNet}, as leafs have been installed client networks, each of them equipped with a client node. The network on the left (RCnet\_left) is also provided with an attacker node, to test the functioning of the server on stress situations. 
At the edges of the system has been connected 4 client network and on top a server network, with a capacity of $2^8$ nodes for each. To allow the router to hypothetically connect to other network or routers, has been chosen, to provide the same number of nodes. The networks are called in relation to the routers they are connected to, or more in general to the nodes. As an example the server network is called servidor-system, the network between the routerA and the routerB is called RA-RB, finally, the client network on the hard left is called RCnet\_left, exploiting also the topological point of view.

\subsection{The nodes}

\subsubsection*{Admin-fluentd}
The administration node is the one responsible for the study and monitoring of the entire virtual network.
The container has a network interface connected to the network professor-system, with the address \cmd{10.200.0.2}, as it is noticeable by figure \ref{fig:virtualNet}. To perform fast changes on the \cmd{fluent.conf} file, responsible for the configuration of Fluentd and fast reading of the output, some shared directories have been mounted. Specifically, in the mapping \cmd{./admin-fluentd/conf/ fluent.conf:/fluentd/etc/fluent.conf} has been stored the configuration file, in \cmd{./admin-fluentd/shared-volume:/shared-volume} is used for storing the prediction scripts and the text file used to store the live results of the prediction and finally in \cmd{./admin-fluentd/output:/fluentd/output/} the logs coming from the whole network are stored.
Finally, to disclose the container, has been used a custom Dockerfile to build the image and a bash script as entry point.

\begin{lstlisting}[language=bash, caption={admin-fluentd entry point}, basicstyle=\ttfamily\small, keywordstyle=\color{blue}, commentstyle=\color{gray}]

#!/bin/bash

# Rather than using a default route, the whole
# interval of addresses is inserted as a static route.
# This is done to allow the node a connection to the internet.
# ip route change default via 10.200.0.10
ip route add 10.0.0.0/8 via 10.200.0.10

chmod +x shared-volume/prediction/livePrediction/liveLogFileCreator.sh
/shared-volume/prediction/livePrediction/liveLogFileCreator.sh

systemctl enable cron
systemctl start cron

# Start Fluentd with a specific configuration file
/usr/local/bundle/gems/fluentd-1.17.0/bin/fluentd -c /fluentd/etc/fluent.conf &

/bin/sleep infinity
\end{lstlisting}

 The entry point task consist in setting the \cmd{routerA} as default route, start Fluentd and make the node wait to be used by means of the command \cmd{/bin/sleep infinity}. The reason why it has been chosen to add \cmd{10.0.0.0/8} as destination to include the entire network instead of defining a default route through the \cmd{routerA} is due to the loss of the connection of the node to Internet. By setting a default route to the \cmd{routerA}, in fact, it was not possible any more to download packages or perform updates after the deploying of the container. 
 In relation to the image used, it is based on the Fluentd Debian based image, in order to be faster and avoid building it in the image. Since the admin node is responsible for the prediction of the DoS attacks, it needs the packages responsible for the working of the model in the python scripts. Since the scripts are based on \cmd{tensorflow}, \cmd{scikit-learn}, \cmd{numpy} and \cmd{joblib} the relative packages have been installed. During the installation, it came out that the package \cmd{tensorflow} was not available or compatible with this version of the operative system. To solve the problem, a virtual environment has been used. Finally, the entry point is copied and made executable in the image, so that when deployed it will be run at the starting of the container.

As anticipated, this node is in charge of log files management, which are forwarded by all the nodes. To do so, it has been used a data collector called Fluentd.

\begin{wrapfigure}{r}{0.38\textwidth}
    \centering
    \includegraphics[width=0.38\textwidth]{3VirtualNetwork/fluentdLOGO.jpg}
    \caption{Fluentd logo.}
    \label{fig:FluentdLOGO}
\end{wrapfigure}


The program offers a complete solution to simplify the handling of logs from different source, which were complex and coming from fragmented source, forcing the use of different tools for different type of logs. By doing so, its employment could be crucial in matters like cloud computing, big data and distributed systems, especially thanks to the support of over 500 plugins, allowing the integration of different data sources and destination.
Fluentd is based on 4 core blocks:
\begin{itemize}
    \item Input: component responsible for the  data collection from sparse sources. Thanks to different plugins it is possible to ingest data from logs, databases and more, allowing Fluentd to manage whatever type of log data making it a universal data collector.
    \item Buffer: is used to temporarily store data. Different buffer mechanism are supported, including in-memory, file-based and others.
    \item Filter: can modify, enrich or exclude log data based on predefined rules. 
    \item Output: is the block responsible for the formatting of data in exit and the delivery to different destinations like databases, other services, files.
\end{itemize}

To use these components and customise the data collector, a configuration file is prepared. It is based on a simple syntax in which, with various directives, it is possible to define how to manage input, buffering, filtering and output of log data \cite{fluentd-docs}.

\begin{lstlisting}[label={lst:example_fluent.conf}, caption=Fluend configuration example]
 <source>
  @type tail
  path /var/log/syslog
  pos_file /var/log/td-agent/syslog.pos
  tag syslog
  format syslog
</source>

<filter syslog>
  @type grep
  <regexp>
    key message
    pattern ERROR
  </regexp>
</filter>

<match syslog>
  @type elasticsearch
  host localhost
  port 9200
  logstash_format true
</match>
\end{lstlisting}

In this example, Fluentd takes data from the local log, filters the error messages and finally sends them to the Elasticsearch server, thanks to the plugin option. The input block is recognisable because of the wording \cmd{source}, the filter thanks to the block \cmd{filter} and the output with \cmd{match}.

In the case of the experimental network, the configuration file has been set as it follow.
\label{par:fluentdconfFile}
The first source:

\begin{lstlisting}
<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>
\end{lstlisting}

listens on port 24224 for incoming log data, using the forward protocol and accepting connection from any IP address (0.0.0.0). The forward protocol is used to facilitate transfer between Fluentd agents and aggregators.
The second source:
\begin{lstlisting}
<source>
  @type syslog
  port 5140
  tag syslog
  bind 0.0.0.0
  protocol_type tcp
</source>
\end{lstlisting}

accepts syslogs on port 5140 from all the IP address, using a TCP connection, in order to be sure of the reception of the messages.

The first match block:

\begin{lstlisting}[language=yaml, caption={Fluent.conf, connection data output block}, label={lst:network-fluent.conf2}]
<match dos.tracer.logs>
  @type copy
  <store>
    @type exec
    command python3 /shared-volume/prediction/1prediction.py
    <buffer>
      @type file
      path /var/lib/fluentd/predictor_dos_logs_buffer
    </buffer>
    <format>
      @type json
    </format>
  </store>
  <store>
    @type file
    path fluentd/output/DoS/dos.log
    append true
    <buffer>
      @type file
      path /var/lib/fluentd/dos_logs_buffer
      flush_mode immediate
      retry_max_times 5
    </buffer>
  </store>
</match>
\end{lstlisting}

it has been set so that the source with matching tag \cmd{dos.tracer.logs} is used for 2 different output. To branch the log entry into two separate streams, it is used the directive \cmd{copy}. The first branch executes a python script called \cmd{1prediction.py}, that performs a real-time analysis of the log entry and prints the result of the prediction in a file. By means of the \cmd{buffer} block, the entries, organised as a set, are saved in a buffer in the JSON format and finally analysed by the script. The second branch, simply stores the entries in a log file, exploiting the same buffer mechanism of the first branch. The \cmd{flush\_mode} option set the rule of flushing the buffer when the system is started to avoid delays and assuring the data do not get lost in the queue.
The final directive capture all the remaining entries, regardless of their tags:

\begin{lstlisting}
<match **>
  @type file
  path /fluentd/output/output.log
  append true
  <buffer>
    @type file
    path /var/lib/fluentd/buffer  
    flush_mode immediate 
    retry_max_times 5  
  </buffer>
</match>
\end{lstlisting}

\subsubsection*{Router}
The virtual network is provided of one router for each level.
The options \cmd{depends\_on}, \cmd{links} and \cmd{logging} are added to the router to allow the communication with the Fluentd node and assuring the creation of the admin node before the router ones. 
The option \cmd{depends\_on}, ensure the creation of a service after another, in this way the administration node will start before the others, and be able to ensure a connection with the sending log nodes without the risk of losing important logs. \cmd{links} creates a network alias that makes the reference to the admin node easier. Finally, \cmd{logging} configures the logging driver as Fluentd and set the address as the admin node, specifying also the port (which is the one used in the configuration file). The option \cmd{tag} adds a tag to the log and the option \cmd{fluentd-async: 'true'} enables asynchronous logging, sending them in a non-blocking manner. 
All the routers are deployed by means of the same Dockerfile, differentiated by their entry point. To do so, all the entry points have been copied in the image and made executable.

\begin{lstlisting}[language=yaml, caption=Dockerfile\_router, label=lst:Dockerfile_router]
FROM ubuntu:22.04
USER root

# Installed packages for ip operations, routing (frr) and log
RUN apt-get update && apt-get install -y iputils-ping && apt-get install -y iproute2 coreutils iptables && apt-get install -y frr syslog-ng
# Configure frr enabling daemon and setting ports
RUN sed -i 's/^bgpd_options=.*/bgpd_options="  --daemon -A 127.0.0.1"/' /etc/frr/daemons && \
    sed -i 's/^zebra_options=.*/zebra_options=" -s 90000000 --daemon -A 127.0.0.1"/' /etc/frr/daemons && \    
    sed -i 's/^bgpd=.*/zebra=yes\nbgpd=yes/' /etc/frr/daemons && sed -i 's/^ospfd=.*/ospfd=yes/' /etc/frr/daemons &&\
    sed -i 's/^zebra=.*/zebra=yes/' /etc/frr/daemons && \
    sed -i 's/^ospfd_options=.*/ospfd_options="  --daemon -A 127.0.0.1"/' /etc/frr/daemons    
RUN echo "zebrasrv 2600/tcp" > /etc/services && echo "zebra 2601/tcp" >> /etc/services && echo "bgpd 2605/tcp" >> /etc/services \
    && echo "ospfd 2604/tcp" >> /etc/services
# Allow ipforwarding
RUN sed -i 's/^#net.ipv4.ip_forward=1.*/net.ipv4.ip_forward=1/' /etc/sysctl.conf && \
    sed -i 's/^#net.ipv6.conf.all.forwarding=1. */net.ipv6.conf.all.forwarding=1/' /etc/sysctl.conf && sysctl -p
# Redirect syslog to fluentd node
RUN echo 'destination fluentdContainer { tcp("10.200.0.2" port(5140)); };\n' >> /etc/syslog-ng/syslog-ng.conf && \
    echo 'log { source(s_src); filter(f_syslog3); destination(fluentdContainer); };' >> /etc/syslog-ng/syslog-ng.conf


# Add the entryscripts to the common image of the router
COPY entrypoints/ep_routerA.sh entrypoints/ep_routerA.sh
COPY entrypoints/ep_routerB.sh entrypoints/ep_routerB.sh
COPY entrypoints/ep_routerC.sh entrypoints/ep_routerC.sh
COPY entrypoints/ep_routerD.sh entrypoints/ep_routerD.sh
#make the scripts executable 
RUN chmod +x /entrypoints/ep_routerA.sh /entrypoints/ep_routerB.sh /entrypoints/ep_routerC.sh /entrypoints/ep_routerD.sh


CMD ["/bin/bash"]
\end{lstlisting}

As noticeable, the managing of the entry point is the last layer of the image. Before that, there is a series of command aimed to configure a software called FRRouting.

\begin{wrapfigure}{L}{0.4\textwidth}
    \centering
    \includegraphics[width=0.38\textwidth]{3VirtualNetwork/FRRLOGO.png}
    \caption{FRRouting logo.}
    \label{fig:FRRLOGO}
\end{wrapfigure}

FRRouting (FRR) is an open-source routing software forked from the Quagga project. Its modular design allows the integration and customisation of protocols, by simply enabling or disabling the required daemons \cite{frrouting-docs}.
The Zebra Daemon acts as central manager and behave like an interface between the other protocol daemons and the routing table. Each supported protocol has its own daemon. Example of protocols are the BGP (Border Gateway Protocol), used for large-scale inter-domain routing, OSPF (Open Shortest Path First), used for intra-domain routing, RIP (Routing Information Protocol), a distance vector protocol.
The software is provided with a command-line interface, called VTYSH (Virtual TeletYpe SHell), allowing an easy configuration and management of the daemons.
The routers have been configured so that the protocol OSPF is used as routing protocol and zebra is activated in order to allow its intermediate role. Moreover, the IP-forwarding option is set to true, to allow the node to act as a router and forward the traffic to the right destination. To satisfy the requirements of log management, the log of the node are sent to the admin node, by directly redirecting them to a specific port (5140).
As anticipated, each router has a different role which is linked to a different type of node. In order to build the routing table and allow FRR to do its job, it is important to configure FRR in each router node by setting the neighbours' network. To do so, has been used a bash script for each router. 

\begin{lstlisting}[language=bash, caption=Entry point of ra\_router, basicstyle=\ttfamily\small, keywordstyle=\color{blue}, commentstyle=\color{gray}]

#!/bin/bash

service syslog-ng start                     # start the syslog
service frr start                           # start frr (routing service)

# set frr and set the frrRouter log
vtysh << EOF
conf t
log file /shared-volume/frr/frrRouterA.log
router ospf
network 10.100.0.0/24 area 0
network 10.200.0.0/24 area 0
network 10.1.0.0/24 area 0
end
EOF

/bin/sleep infinity
\end{lstlisting}

The \cmd{ra\_router} is the one that links the server, the tree network and the admin node, for this reason the macro-areas of these nodes have been added to the router.
The same happens for the \cmd{rb\_router}, that links the \cmd{ra\_router} to the \cmd{rc\_router} and \cmd{rd\_router}, covering the role of root. The \cmd{rc\_router} and \cmd{rd\_router} instead, link the clients to the network.

The protocol used, OSPF, is an interior gateway protocol (IGP) designed for routing within an autonomous system (AS). It is a link-state routing protocol, meaning each router builds a map of the network topology using Link-State advertisement. The map is used to calculate the shortest path using the Dijkstra algorithm. By using the concept of areas, OSPF supports hierarchical network designs, helping the optimisation and scaling of the routing process, as an instance by reducing the routing table size. The backbone is formed by Area 0 and connects all other areas. Each router within an area shares the same Link-State advertisement and same Link-State Database (LSDB). Other type of areas are the Regular Areas, connected to the backbone, the Stub Areas, which are very internal areas that do not need to receive information about routes external to the OSPF autonomous system. In the entry points shown, all the nodes are inserted in the backbone area, to ensure an easy testing and working of the experiments.
%The protocol BGP, is the actual protocol operated in the global routing system of Internet. It is a standardized exterior gateway protocol designed to exchange routing and reachability information between autonomous systems (AS). An AS is a collection of IP networks and routers under the control of a single organisation. The BGP peers exchange routing information among them, whether they are in the same AS (iBGP) or different ASes (eBGP). The path information contained in the table of network, are updated and exchanged by means of path vector.

\subsubsection*{Clients}
The virtual network is also provided of client nodes called \cmd{redX}, with X going from 1 to 4, that simulate user nodes. Each of them represents a possible node of each leaf network. 
Together with \cmd{red1} and connected to \cmd{RCnet\_left}, there is a node called \cmd{attacker}, identical to the clients, but provided with several attack tools. It will be covered in the Attacker chapter.

                
\subsubsection*{Server}
The server, together with the administrator node, is the most important node of the project. It can be exploited as an example of target for an attacker and to comprehensively test the Fluentd logging system.
To do so, the server has been provided with a shared directory with the prediction package and a server application sample.

In the server container, by means of the \cmd{Dockerfile\_servidor}, is deployed a server application exposed on the port \cmd{3000}.
Finally, with the entry point bash script, the server is started with the command \cmd{npm start}.

\section{Compatibilities problems}
During the developing of the project have been encountered several compatibilities problems, related both to the host operative system and to the installation of different packages or libraries. The most interesting are the ones treated in the next two paragraphs, which are aimed to demonstrate the importance of containerisation. With this approach, in fact, it has been possible to assure the reproducibility of the experiment on different machines.

\subsection{Host OS problems}
During the installation and test of eBPF program (the topic of eBPF has been covered in the chapter Data Collector) some compatibility problem raised in the attempt of installing \cmd{bpftool} and \cmd{libbpf} and of running the programs. 
The first compatibility was related to the use of WSL. Originally, in fact, the chosen operative system for the host was Windows. To work on Windows, Docker uses the Windows Subsystem for Linux, which is a compatibility layer designed to run a Linux environment directly on Windows. The advantages of using WSL are related to: the possibility of using Linux commands directly in the Windows command line; the possibility of using Linux-native tools, script or application (like Docker) directly on the Windows machine; the improvement of the performance, due to a nearer-native kernel; dynamic use of resources. However, WSL still has some limitation. In this case, the WSL did not provide the necessary headers to install any eBPF library and consequently run the scripts.
To overstep the obstacle it has been raised an issue on the official Github repository of WSL \href{https://github.com/microsoft/WSL/issues/10913}{ ``Unable to locate package linux-tools-5.15.133.1-microsoft-standard-WSL2''}. An official operator answered that \begin{quote}
     ``The issue is this: apt-get install -y linux-tools-common linux-tools-\$(uname -r). The WSL kernel doesn't have matching packages in distributions repositories.''
\end{quote}
Since building my own kernel could lead to future compatibility problem, it has been decided to change the host operative system to Ubuntu 22.04. 
The second problem, related to the run of eBPF scripts, was related to a similar problem. In the attempt of installing the linux-headers of the container operative system, the operation, apparently, could not have been done inside the Dockerfile. To circumvent the problem, it has been chosen to install the package at runtime, inside the bash script of the entry point. By doing so, in fact, it has been possible to make the header installation, related to the kernel (\cmd{apt-get install -y linux-headers-\$(uname -r)})
\subsection{Tensorflow installation}
During the installation of Tensorflow appeared a compatibility problem of the package with the operative system of the container. The node in question is the admin-fluent, whose image uses \cmd{Ubuntu 22.04}. To solve the issue, it has been decided to use a Virtual Environment for the installing of the python libraries required. A virtual environment is an isolated Python environment that allows to manage dependencies for different projects separately, so as to avoid conflicts between project dependencies and system-wide packages. When activated, the virtual environment, modifies the shell's 'PATH' variable so that the virtual environment's python and pip are used instead of the system-wide versions.

\begin{lstlisting}[language=bash, basicstyle=\ttfamily, keywordstyle=\color{blue}, caption=Python virtual environment, label=lst:virtualEnv]
RUN apt install -y python3-venv
RUN python3 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Install packages within the virtual environment
RUN pip install tensorflow scikit-learn numpy joblib
\end{lstlisting}

The commands of the code \ref{lst:virtualEnv} are used to install and create a virtual environment. Finally, the packages required are installed.
Other possible solutions were: deeply understanding the compatibility problem and solve it or change the base image, from \cmd{Fluentd} to \cmd{Tensorflow}, with the consequence of installing Fluentd in the Dockerfile. 

                                