Я пытаюсь создать тестовую программу в C на Linux (Ubuntu 18.04), которая отправляет пустой UDP-пакет через сокет AF_PACKET / PF_PACKET, используя SOCK_RAW
. В этой программе я знаю адреса и IP-адреса источника и получателя MA C. Тем не менее, это не работает, и я не уверен, что я делаю неправильно. Я не смог найти много ресурсов в Интернете по этой проблеме, к сожалению, поскольку большинство потоков, которые я обнаружил, больше предназначены для приема пакетов через сокеты AF_PACKET. Программа также заявляет, что отправляет правильное количество байтов в пункт назначения. Хотя я не вижу пакетов при использовании tcpdump
как на исходной, так и на целевой виртуальной машине.
Вот код программы:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <linux/if.h>
#include <linux/if_packet.h>
#include <linux/udp.h>
#include <net/ethernet.h>
#include <string.h>
#include <error.h>
#include <errno.h>
#include <inttypes.h>
#define REDIRECT_HEADER
#include "csum.h"
#define MAX_PCKT_LENGTH 65535
int main()
{
int sockfd;
struct sockaddr_ll dst;
char pckt[MAX_PCKT_LENGTH];
sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
if (sockfd <= 0)
{
perror("socket");
exit(1);
}
dst.sll_family = AF_PACKET;
dst.sll_protocol = ETH_P_IP;
if ((dst.sll_ifindex = if_nametoindex("ens18")) == 0)
{
fprintf(stdout, "Interface 'ens18' not found.\n");
exit(1);
}
// Do destination ethernet MAC (ae:21:14:4b:3a:6d).
dst.sll_addr[0] = 0xAE;
dst.sll_addr[1] = 0x21;
dst.sll_addr[2] = 0x14;
dst.sll_addr[3] = 0x4B;
dst.sll_addr[4] = 0x3A;
dst.sll_addr[5] = 0x6D;
dst.sll_halen = 6;
// I tried doing this with and without bind. Still not working.
if (bind(sockfd, (struct sockaddr *)&dst, sizeof(dst)) < 0)
{
perror("bind");
exit(1);
}
struct ethhdr *ethhdr = (struct ethhdr *) (pckt);
struct iphdr *iphdr = (struct iphdr *) (pckt + sizeof(struct ethhdr));
struct udphdr *udphdr = (struct udphdr *) (pckt + sizeof(struct ethhdr) + sizeof(struct iphdr));
unsigned char *data = (unsigned char *) (pckt + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr));
// Do source ethernet MAC (1a:c4:df:70:d8:a6).
ethhdr->h_source[0] = 0x1A;
ethhdr->h_source[1] = 0xC4;
ethhdr->h_source[2] = 0xDF;
ethhdr->h_source[3] = 0x70;
ethhdr->h_source[4] = 0xD8;
ethhdr->h_source[5] = 0xA6;
// Copy destination MAC to sockaddr_ll.
memcpy(ethhdr->h_dest, dst.sll_addr, 6);
// Protocol.
ethhdr->h_proto = ETH_P_IP;
// Fill out ip header.
iphdr->ihl = 5;
iphdr->version = 4;
iphdr->frag_off = 0;
iphdr->id = rand();
iphdr->protocol = IPPROTO_UDP;
iphdr->tos = 0x0;
iphdr->ttl = 64;
iphdr->saddr = inet_addr("10.50.0.3");
iphdr->daddr = inet_addr("10.50.0.4");
iphdr->tot_len = sizeof(struct iphdr) + sizeof(struct udphdr);
iphdr->check = 0;
iphdr->check = ip_fast_csum(iphdr, iphdr->ihl);
// Fill out UDP header.
udphdr->source = htons(27000);
udphdr->dest = htons(27015);
udphdr->len = htons(sizeof(struct udphdr));
udphdr->check = 0;
udphdr->check = csum_tcpudp_magic(iphdr->saddr, iphdr->daddr, sizeof(struct udphdr), IPPROTO_UDP, csum_partial(udphdr, sizeof(struct udphdr), 0));
// Send packet
uint16_t sent;
if ((sent = sendto(sockfd, pckt, iphdr->tot_len + sizeof(struct ethhdr), 0, (struct sockaddr *)&dst, sizeof(dst))) < 0)
{
perror("sendto");
}
fprintf(stdout, "Sent %d of data.\n", sent);
close(sockfd);
exit(0);
}
И исходный, и целевой серверы Виртуальные машины на моем домашнем сервере (оба работают под управлением Ubuntu 18.04).
Вот основной интерфейс моей исходной виртуальной машины:
ens18: flags=323<UP,BROADCAST,RUNNING,PROMISC> mtu 1500
inet 10.50.0.3 netmask 255.255.255.0 broadcast 10.50.0.255
inet6 fe80::18c4:dfff:fe70:d8a6 prefixlen 64 scopeid 0x20<link>
ether 1a:c4:df:70:d8:a6 txqueuelen 1000 (Ethernet)
RX packets 1959766813 bytes 1896024793559 (1.8 TB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1936101432 bytes 1333123918522 (1.3 TB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Вот основной интерфейс моей целевой виртуальной машины:
ens18: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.50.0.4 netmask 255.255.255.0 broadcast 10.50.0.255
inet6 fe80::ac21:14ff:fe4b:3a6d prefixlen 64 scopeid 0x20<link>
ether ae:21:14:4b:3a:6d txqueuelen 1000 (Ethernet)
RX packets 1032069029 bytes 1251754298166 (1.2 TB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 74446483 bytes 9498785163 (9.4 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Я попытался установить интерфейс «ens18» исходной виртуальной машины в беспорядочный режим, но это не имело никакого значения. Я не думаю, что в этом случае это будет иметь значение. Я также хочу использовать SOCK_RAW
в этом случае, потому что я хочу получить больше опыта и не хочу, чтобы ядро что-либо делало с пакетами (я читал, что использование AF_PACKET
+ SOCK_RAW
приведет к тому, что ядро не возиться с пакетами).
С учетом сказанного у меня есть дополнительный вопрос. Как бы я go узнал о MA C IP-адресе, который не находится в сети / не привязан к интерфейсу (например, IP-адрес назначения отправляется на сервер за пределами моей сети)? Я бы предположил, что мне нужно отправить запрос ARP. Если это так, я просто отправляю ARP-запрос на сервер назначения и получаю адрес MA C перед отправкой каждого пакета через AF_PACKET
+ SOCK_RAW
?
Любая помощь высоко ценится, и спасибо за ваше время!