Clases - Analizador de redes simple

header.png

La programación orientada a objetos (object oriented programming - OOP) es un paradigma de programación que promueve el diseño de programas en el que distintos objetos interactúan entre sí para resolver un problema. C++ es uno de los lenguajes de programación que promueve la programación orientada a objetos, permitiendo que los programadores creen sus propias clases desde cero o derivadas de otras clases existentes. Otros lenguajes que promueven OOP son Java, Python, JavaScript y PHP.

En este experiencia de laboratorio, practicarás la definición de clases y la implementación de sus métodos mientras completas un programa que implementa un analizador de redes (network sniffer) simple. Cada dato que tu computadora recibe y envía a través de la red se estructura como un paquete de red (“network packet”). El analizador de redes es una aplicación comúnmente utilizada por especialistas de redes y hackers para inspeccionar esos paquetes. El software escucha las comunicaciones que pasan por el interfaz de redes de tu computadora y las presenta como datos en formato estructurado. Cada paquete contiene múltiples informaciones necesarias para garantizar su comunicación, tales como la dirección de fuente y destino. Por esto, es común en programación representar los paquetes usando objetos u otros tipos de data estructurada. En los ejercicios de este laboratorio crearás clases para paquetes de red. Una vez las clases y sus métodos hayan sido correctamente implementados, podrás correr la aplicación del analizador de redes y ver con lujo de detalle las comunicaciones que pasan por tu interfaz de redes cada vez que accedes una página web.

Objetivos:

  1. Practicar la declaración e implementación de clases en C++.

  2. Implementar métodos de una clase.

Pre-Lab:

Antes de llegar al laboratorio debes haber:

  1. Repasado la declaración e implementación de clases en C++.

  2. Estudiado los conceptos e instrucciones para la sesión de laboratorio.

  3. Tomado el quiz Pre-Lab, disponible en Moodle.



Clases y objetos en C++

Un objeto es un ente que contiene datos y procedimientos para manipularlos. Al igual que cada variable tiene un tipo de dato asociada a ella, cada objeto tiene una clase asociada que describe las propiedades de los objetos: sus datos (atributos), y los procedimientos con los que se pueden manipular los datos (métodos).

Para definir y utilizar un objeto no hay que saber todos los detalles de los métodos del objeto, pero hay que saber cómo crearlo, y cómo interactuar con él. La información necesaria está disponible en la documentación de la clase. Antes de crear objetos de cualquier clase, debemos familiarizarnos con su documentación. La documentación nos indica, entre otras cosas, qué ente se está tratando de representar con la clase, y cuáles son los interfaces o métodos disponibles para manipular los objetos de la clase.

Dale un vistazo a la documentación de la clase Bird que se encuentra en este enlace para que veas un ejemplo de una clase.

Clases

Una clase es una descripción de los datos y procesos de un objeto. La declaración de una clase establece los atributos que tendrá cada objeto de esa clase y los métodos que pueden invocar.

Los atributos y métodos de una clase pueden tener uno de los siguientes niveles de acceso: private, protected y public. Los miembros de datos que se declaran public pueden ser leídos y modificados desde cualquier función (incluso de funciones externas). Los miembros de datos private solo pueden ser leídos o modificados por las funciones miembro de la clase. Si no se especifica lo contrario, los atributos y métodos definidos en una clase serán privados. Los miembros de datos protected solo pueden ser leídos/modificados por funciones miembro de la clase o de clases hijas.

Lo siguiente es el esqueleto de la declaración de una clase:


  class NombreClase
   {
    // Declaraciones

    private:
      // Declaraciones de variables o atributos y 
      // prototipos de métodos 
      // que sean privados para esta clase

      tipo varPrivada;
      tipo nombreMetodoPrivado(tipo de los parámetros);

    public:
      // Declaraciones de atributos y 
      // prototipos de métodos 
      // que sean públicos para todo el programa

      tipo varPública;
      tipo nombreMetodoPúblico(tipo de los parámetros);
   };


Comunicación entre computadoras

Las computadoras se comunican por medio del Internet utilizando el Protocolo de Internet (IP - por sus siglas en inglés). Cuando una computadora envía información a otra computadora, la información se envía por Paquetes de Internet que contienen la dirección fuente ("source address"), que es la dirección de Internet de la computadora que está enviando la información, y la dirección destino ("destination address"), que es la dirección de Internet de la computadora que debe recibir el mensaje. Las direcciones de Internet se usan para guiar la información de una computadora a otra, pero una vez el paquete llega a su destino, ¿quién se supone que reciba la información? ¿Qué aplicación debe recibir la información?

Los paquetes de internet también deben especificar la aplicación que envía la información y la aplicación que debe recibirla. Podemos pensar que las direcciones de Internet son las direcciones de correo de una casa, y que las aplicaciones que envían y reciben la información, son las personas que envían y reciben la correspondencia. Para enviar una carta por correo, hay que especificar a qué persona se le está enviando la carta. Esto corresponde a especificar la aplicación que recibe la información. Para identificar la aplicación fuente y la aplicación de destino, el protocolo de Internet usa lo que se conoce como números de puerto. De este modo, mirando la información del paquete, se pueden identificar las direcciones y puertos de la fuente y del destino.

Por ejemplo, cuando la computadora que usas en el laboratorio se comunica con el servidor donde se encuentra el programa Moodle, los paquetes que llevan la información de tu computadora al servidor contienen la dirección de la fuente, que es la computadora del laboratorio, y la dirección del destinatario, que es el servidor de Moodle. El puerto fuente es el de tu buscador web y el puerto destinatario es el del servidor de Moodle.

Las direcciones de internet ocupan 4 bytes (32 bits) y usualmente se presentan al usuario como cadenas de 4 valores decimales. Cada valor decimal entre 0 y 255 es la representación decimal de uno de los 4 bytes: "(0-255).(0-255).(0-255).(0-255)". Algunos ejemplos de direcciones de IP son: 10.0.1.10, 192.168.10.11, 136.145.54.10.

Los números de puertos están compuestos de 2 bytes (16 bits). Por lo tanto, los números de puertos ocilan entre 0 y 65535. Existen números de puertos asignados a serivicios de aplicaciones conocidas como el número 22 para ssh, 23 para telnet, 25 para smtp, 80 para http, y así sucesivamente.

Para complicar las cosas un poco, cada tarjeta de red de computadoras tiene un identificador único que es usado para la comunicación entre tu computadora y el dispositivo de la red que enruta el tráfico de red de Internet y la red local a tu computadora y vice-versa (protocolo Ethernet). Este identificador único es conocido como la dirección de Hardware (también conocido como dirección MAC), es representado usando 6 bytes (48 bits), y es presentado a los usuarios como una cadena de caracteres de 6 pares de dígitos hexadecimales (cada par de dígitos hexadecimal corresponde a 1 byte). Cada valor hexadecimal es la representación hexadecimal de 6 bytes: "(00-ff):(00-ff):(00-ff):(00-ff):(00-ff):(00-ff)". Por ejemplo, las siguientes son direcciones MAC: e0:f8:47:01:e9:90 y 70:ad:60:ff:fe:dd.



Analizador de redes simple

Un sniffer de paquetes (también conocido como analizador de paquetes - analizador de protocolos - o analizador de redes) es un programa de computadora que puede interceptar y registrar el tráfico que pasa a través de una red digital, o dispositivo de red. Mientras los datos fluyen a través de la red, el sniffer captura cada paquete, y si es necesario, lo descifra para obtener los datos crudos del paquete [1].

Cada paquete capturado por un sniffer tiene una estructura como la que ilustra la Figura 1.


figure1.png

Figura 1. Estructura de cada paquete de Ethernet capturado por un sniffer.


Dentro de la estructura mostrada en la Figura 1 se encuentra:

  1. Destination MAC Address y Source Mac Address: son las direcciones MAC de la fuente y el destino.
  2. Ether Type: se utiliza para indicar el tipo de protocolo utilizado en el payload. Uno de los payloads posibles es un paquete de IP.
  3. Payload: contiene un paquete de IP (en realidad puede contener otras cosas, pero para esta experiencia de laboratorio asumiremos que solamente contiene IP).

Dentro del "payload", el paquete de IP contiene varios campos, entre ellos:

  1. Las direcciones IP fuente y destino
  2. Los números de puerto fuente y destino
  3. El payload del paquete IP. Dentro de este payload estarían los datos que se desean comunicar de una computadora a otra.

En esta experiencia de laboratorio completarás un sniffer de paquetes sencillo que captura todos los paquetes de IP que fluyen a través de tu computadora de laboratorio, y alguna información adicional de los paquetes. Adicionalmente, detecta las solicitudes no encriptadas de imágenes en la web, y despliega las imágenes en el GUI.

En este laboratorio, pensaremos en los paquetes de IP como una especialización de los paquetes de Ethernet. Por lo tanto, implementarás la clase de paquetes IP como derivada de la clase de los paquetes Ethernet (usando herencia). De este modo, la clase para paquetes IP tendrá todos los miembros de datos y métodos de la clase de paquetes de Ethernet (por ejemplo, dirección MAC de fuente y destino) en adición de los datos y métodos particulares a los paquetes de IP (por ejemplo, los puertos de fuente y destino).



Un programador vago te ha dado la siguiente declaración de una clase:
¿Cuál de la siguientes es probablemente un getter?
myClass() int xx() void zz(int someValue) a La contestación correcta es int xx() pues los getters devuelven el valor de alguno de los miembros dato. Por lo tanto tienden an tener un prototipo de función que no es void y no tienen parametros. Acerca del resto de las opciones:
myClass es un constructor (tiene el mismo nombre de la clase)
void zz(int someValue) es muy probablemente un setter ya que es una función void que recibe un parametro de un tipo que coincide con uno de los miembros datos
a es un miembro dato.



Sesión de laboratorio:

La aplicación que completarás hoy, permite a los usuarios analizar el tráfico de red y monitorear las imágenes que están siendo transferidas a través de tu red.

La Figura 2 muestra una foto de la interfaz de la aplicación. Cada fila en la tabla es la información de un paquete capturado. La caja de texto bajo la tabla presenta un resumen del paquete seleccionado en la tabla. La lista en el lado derecho presenta las imágenes que han sido capturadas por el sniffer.


figure2.png

Figura 2. Interfaz de la aplicación Simple Packet Sniffer.


Para crear un sniffer de paquetes puedes usar la librería de pcap que provee una interfaz para accesar la data que está pasando a través de la tarjeta de red de tu computadora. Esta librería contiene una función que devuelve un torrente crudo de los bytes de cada paquete capturado.

La tarea del programador del sniffer es decodificar el torrente en crudo a información legible por humanos. Afortunadamente esta no va a ser tu tarea, pero tu puedes aprender a hacerlo, si quieres, leyendo el código fuente de este laboratorio. Tu tarea es seguir los ejercicios de abajo para que puedas proveerle al sniffer los objetos necesarios (clases) para procesar los paquetes.

Ejercicio 1 - Familiarizarte con la aplicación

Instrucciones

  1. Carga a QtCreator el proyecto SimpleSniffer. Hay dos maneras de hacer esto:

    • Utilizando la máquina virtual, necesitas correr Qt Creator con privilegios de administrador (root): sudo qtcreator /home/eip/labs/classes-simplesniffer/SimpleSniffer.pro
    • Descargando la carpeta del proyecto de Bitbucket: Utiliza un terminal y escribe el comando git clone http:/bitbucket.org/eip-uprrp/classes-simplesniffer para descargar la carpeta classes-simplesniffer de Bitbucket. En esa carpeta, necesitas correr Qt Creator con privilegios de administrador (root): sudo qtcreator ./SimpleSniffer.pro
  2. Configura el proyecto. En esta experiencia de laboratorio trabajarás con los archivos ethernet_hdr.h, ethernet_packet.h, ethernet_packet.cpp, ip_packet.h y ip_packet.cpp.

Ejercicio 2 - Completar la clase ethernet_packet

  1. Estudia el archivo ethernet_hdr.h. Este archivo contiene la siguiente definición de la estructura de datos que representa un encabezado de Ethernet:

     #define ETHER_ADDR_LEN 6
    
     struct sniff_ethernet {
       u_char  ether_dhost[ETHER_ADDR_LEN];    /* direccion destino */
       u_char  ether_shost[ETHER_ADDR_LEN];    /* direccion fuente */
       u_short ether_type;                     /* IP? ARP? RARP? etc */
     };

    El encabezado de Ethernet de arriba es usado para decodificar la parte ethernet de los datos crudos en cada paquete. Este se compone de la dirección MAC fuente (ether_shost, 6 bytes), la dirección MAC destino (ether_dhost, 6 bytes), y el tipo de paquete de Ethernet (ether_type, 2 bytes) que es usado para determinar si el paquete es un paquete de IP.

    Como sabes, no es una buena idea enseñar este formato de información a un usuario regular. Tu primera tarea es definir los métodos de la clase de C++ que traducen la información de las direcciones MAC a cadenas de caracteres legibles por humanos.

  2. El siguiente código es la definición de la clase ethernet_packet, que se encuentra en el archivo ethernet_packet.h:

     class ethernet_packet
     {
       sniff_ethernet ethernet ;
    
       // Devuelve una direccion de 6 bytes MAC en una cadena de caracteres.
       string mac2string(u_char []) ;
    
     public:
       ethernet_packet();  // Constructor por defecto
    
       // Ajusta la variable miembro ether_dhost a los valores
       // recibidos en el arreglo
       void setEtherDHost(u_char []) ;
    
       // Ajusta la variable miembro  ether_shost a los valores
       // recibidos en el arreglo
       void setEtherSHost(u_char []) ;
    
       // Ajusta el ethernet type al valor recibido.
       void setEtherType(u_short) ;
    
       // Devuelve la representación en cadenas de caracteres de las direcciones
       // Ethernet
       string getEtherDHost() ;
       string getEtherSHost() ;
    
       // Devuelve el tipo de ethernet.
       u_short getEtherType() ; 
     };

    Nota que cada objeto de clase ethernet_packet solo tiene el siguiente atributo: una estructura tipo sniff_ethernet llamada ethernet.

  3. El resto son métodos que actúan como interfaz al atributo:

    • void setEtherDHost(u_char []): es un setter para el campo ether_dhost del atributo ethernet
    • void setEtherSHost(u_char []): es un setter para el campo ether_shost del atributo ethernet
    • void setEtherType(u_short): es un setter para el campo ether_type del atributo ethernet
    • getEtherDHost() y getEtherSHost() son getters que devuelven los valores de ether_dhost y ether_shost en formato legible por humanos, e.g. 6 pares de dígitos hexadecimales (por ejemplo, e0:f8:47:01:e9:90).
    • getEtherType() es un getter que devuelve el valor de ether_type como unsigned char.
    • el método privado string mac2string(u_char []) recibe un arreglo de seis unsigned characters y devuelve el string correspondiente a su representación hexadecimal. Por ejemplo, si recibe { 0x8A, 0x11, 0xAB, 0xFF, 0x12, 0x34} debe devolver el string "8A:11:AB:FF:12:34".
  4. Tu tarea en este ejercicio es implementar las siete funciones listadas arriba, en el archivo ethetnet_packet.cpp. Los encabezados de algunas de las funciones están provistos en el archivo.

Ejercicio 3 - Construir la declaración de ip_packet

  1. Estudia las definiciones de las funciones de la clase ip_packet que se encuentra en el archivo ip_packet.cpp

  2. Tu tarea es crear la declaración de la clase ip_packet en el archivo ip_packet.h tomando como base los métodos que aparecen en el archivo ip_packet.cpp. Los atributos de la clase ip_packet deben ser:

    • dos objetos de clase string para almacenar las direcciones de IP fuente y destino
    • una variable de un byte (char) para almacenar el tipo de protocolo IP
    • dos variables unsigned short para almacenar el puerto fuente y destino
    • un objeto de la clase string para almacenar la carga (payload) del paquete.

      En la declaración de la clase ip_packet debes especificar que es una clase derivada de la clase ethernet_packet.



Entregas

  1. Utiliza "Entrega 1" en Moodle para entregar el archivo ethernet_packet.cpp que completaste en esta experiencia de laboratorio.

  2. Utiliza "Entrega 2" en Moodle para entregar el archivo ip_packet.h que completaste en esta experiencia de laboratorio.

Recuerda utilizar buenas prácticas de programación, incluye el nombre de los programadores y documenta tu programa.



Referencias

[1] http://en.wikipedia.org/wiki/Packet_analyzer

results matching ""

    No results matching ""