Начну данную заметку с определения понятия сырого сокета, встречаемого в литературе под названием символьный или неструктурированный сокет. Сырой сокет — это сокет, позволяет принимает пакеты, обходя уровни TCP и UDP в стеке TCP/IP и отправляя их непосредственно приложению или отправляя их аналогичным образом.
Ниже представлен пример создания сырого сокета, функционирующего на канальном уровне по протоколу ARP:
int sock = socket(AF_INET, SOCK_PACKET, ETH_P_ARP);
Тип сокета (2-й аргумент) — SOCK_PACKET позволяет осуществляет работу на канальном уровне.
В типе протокола (3-й аргумент) можно указать один из протоколов канального уровня, определения которых представлены в файле if_ether.h. Но стоит иметь ввиду, что имя протокола может быть определено, но при этом не поддерживаться ядром. Кроме того важно помнить, что если при создании сырого сокета определено не нулевое значение протокола, то значение поля протокола полученной дейтаграммы должно совпадать с этим не нулевым значением, иначе дейтаграмма не будет доставлена на данный сокет.
В качестве примера работы с сырым сокетом, напишем простенькое приложение, которое будет отправлять ARP запрос на получение MAC адреса устройства в сети по известному IP адресу.
Первым делом потребуется заполнить заголовок ethernet кадра. Структуру заголовка можно определить самостоятельно или воспользоваться уже определенной — struct ethhdr, расположенной в заголовочном файле if_ether.h.
char datagram[1024]; // буфер исходящего запроса. struct ifreq hw_src; // для хранения MAC-адрес исх. устройства. struct ethhdr *eth_hdr; // указатель на заголовок ethernet кадра. int sock; … … … // заполняем нулями буфера и структуры. memset(&hw_src, 0, sizeof(hw_src)); memset(&datagram, 0, sizeof(datagram)); // запрашиваем MAC адрес сетевой карты (wlan0) на данном машине. strcpy(hw_src.ifr_ifrn.ifrn_name, "wlan0"); sock = socket(AF_INET, SOCK_DGRAM, 0); ioctl(sock, SIOCGIFHWADDR, &hw_src); // Заголовок ethenet кадра помещаем в начало исходящего пакета. eth_hdr = (struct ethhdr *) datagram; // Заполняем поля заголовка ethenet кадра: eth_hdr->h_proto = htons(0x0806); // номер ARP протокола. for (int i = 0; i < ETH_ALEN; i++) { // заполняем адреса отправ. и назнач. eth_hdr->h_source[i] = hw_src.ifr_ifru.ifru_hwaddr.sa_data[i]; eth_hdr->h_dest[i] = 0xFF; // адрес назначения — широковещ. }
Теперь необходимо заполнить заголовок ARP, воспользуемся определенной структурой arphdr — файл if_arp.h:
struct arphdr *arp_hdr; … … … // Заголовок ARP располагаем за заголовком ethenet кадра. arp_hdr = (struct arphdr *) (datagram + sizeof(struct ethhdr)); arp_hdr->ar_hrd = htons(ARPHRD_ETHER); // используем ARP для Ethernet arp_hdr->ar_pro = htons(0x0800); // протокол IPv4 arp_hdr->ar_hln = 6; // длина MAC адреса arp_hdr->ar_pln = 4; // длина IPv4 адреса arp_hdr->ar_op = htons(ARPOP_REQUEST); // тип ARP пакета — запрос.
Далее необходимо заполнить структуру ARP-запроса:
// структура ARP запроса для IPv4. struct arp_reqv4 { unsigned char ar_sha[ETH_ALEN]; // MAC адрес исходящего устройства unsigned char ar_sip[4]; // IP адрес исходящего устройства unsigned char ar_dha[ETH_ALEN]; // MAC адрес устройства назначения unsigned char ar_dip[4]; // IP адрес устройства назначения }; … … … struct arp_reqv4 *arp_req; // указатель на ARP запрос. … … … // располагаем ARP запрос за заголовком ARP. arp_req = (struct arp_reqv4 *) (datagram + sizeof(struct ethhdr) + sizeof(struct arphdr)); // копируем MAC адрес исходящего устройства. memcpy(&arp_req->ar_sha, hw_src.ifr_ifru.ifru_hwaddr.sa_data, 6); memcpy(&arp_req->ar_dip, dst_addr, 4); // Копируем IP адрес назначения. // запрашиваем IP-адрес сетевой карты (wlan0) на данной машине. memset(&hw_src, 0, sizeof(hw_src)); strcpy(hw_src.ifr_ifrn.ifrn_name, "wlan0"); ioctl(sock, SIOCGIFADDR, &hw_src); // и копируем IP адрес его (IP адрес исходящего устройства). memcpy(&arp_req->ar_sip, (hw_src.ifr_ifru.ifru_addr.sa_data + 2), 4);
Запрос полностью готов, теперь необходимо создать сокет на канальном уровне и отправить наш ARP запрос в сеть:
int sock, enable; const int *ref_en; struct sockaddr param; … … … memset(¶m, 0, sizeof(param)); strcpy(param.sa_data, "wlan0"); ref_en = &enable; enable = 1; sock = socket(AF_INET, SOCK_PACKET, ETH_P_ARP); setsockopt(sock, IPPROTO_IP, IP_HDRINCL, ref_en, sizeof(enable)); setsockopt(sock, SOL_SOCKET, SO_BROADCAST, ref_en, sizeof(enable)); if (sendto(sock, datagram, sizeof(ethhdr) + sizeof(arphdr) + sizeof(arp_reqv4), 0, (struct sockaddr *) ¶m, sizeof(param)) < 0) { printf("ARP request was sent unsuccessfully...\n"); } else { printf ("ARP request was sent successfully...\n"); }
Запустим анализатор трафика Wireshark и убедимся в том, что запрос отправляется и что не мало важно приходит ответ - рис. 1. В ниже представленном примере осуществляется запрос MAC адреса роутера с IP адресом 192.168.0.3.
Рис. 1. Запрос MAC адреса роутера по IP.
Комментариев нет:
Отправить комментарий