Quelques trucs pour faire passer vos paquets par câble/air.
- Le bon .sll_protocol pour la réponse arp est ETH_P_ARP, à partir de
<linux/if_ether.h>
-
Il y avait une erreur d'endianité lors de la définition de ah->arp_op. C'est un champ d'ordre des octets réseau de 2 octets, utilisez donc htons().
-
En général, le code est un peu confus au sujet de l'ordre des octets du réseau et de l'hôte. Il envoie actuellement la réponse très mutilée, mais je ne sais pas s'il s'agit de l'intention malveillante du code ou d'un accident. Dans le cas où vous souhaitez envoyer des adresses IP réelles et correctes, utilisez htonl et htons lors de la construction de la réponse.
Pour corriger l'endianité :
- Inclure correctement
<arpa/inet.h>
- Utilisez htons(), htonl() ntohs() et ntohl(), toujours. Leur mise en œuvre en fait un NOP, s'il n'est pas nécessaire sur votre plate-forme.
- Lors de la configuration des données à envoyer depuis l'hôte, traitez-les toujours avec hton*()
- Lors de l'interprétation des données du réseau, toujours ntoh*() avant de comparer avec les variables locales.
En résumé, les modifications que j'ai apportées étaient 1) .sll_protocol =htons(ETH_P_ARP). (lors de l'envoi de données) 2) ah->arp_op =htons(ARPOP_REPLY) (dans la réponse arp) 3) Suppression de l'insensé ntohs() sur ah->arp_hd et ah->arp_pr. Vous ne voulez pas convertir les données dans l'ordre des octets de l'hôte lors du remplissage du tampon d'envoi (à moins que vous ne le fassiez vraiment) ("sudo...") !
Code complet sur pastebin. Voici une différence :
[email protected]:~/src/so/arp$ diff arp2.c arp_orig.c
13d12
< #include <arpa/inet.h>
20c19
< #define DEVICE "eth1"
---
> #define DEVICE "eth0"
25c24
< int s = -1; /*Socketdescriptor*/
---
> int s = 0; /*Socketdescriptor*/
92c91
< socket_address.sll_protocol = htons(ETH_P_ARP);
---
> socket_address.sll_protocol = htons(ETH_P_IP);
95c94
< socket_address.sll_pkttype = 0; //PACKET_OTHERHOST;
---
> socket_address.sll_pkttype = PACKET_OTHERHOST;
112c111
< if(ntohs(eh->h_proto) == ETH_P_ARP)
---
> if(htons(eh->h_proto) == 0x806)
119c118
< if(ntohs(ah->arp_op) != ARPOP_REQUEST)
---
> if(htons(ah->arp_op) != 0x0001)
139d137
< #if 0
145d142
< #endif
182c179
< eh->h_proto = htons(ETH_P_ARP);
---
> eh->h_proto = ETH_ARP;
200,201c197,198
< //ah->arp_hd = ntohs(ah->arp_hd);
< //ah->arp_pr = ntohs(ah->arp_pr);
---
> ah->arp_hd = ntohs(ah->arp_hd);
> ah->arp_pr = ntohs(ah->arp_pr);
203c200
< ah->arp_op = htons(ARPOP_REPLY);
---
> ah->arp_op = 0x0002;
MODIFIER Quelques conseils Wireshark. Capturez ether proto 0x0806 (ou arp pour faire court). Utilisez le pseudo périphérique qui capture tous les paquets. Vos paquets devraient devenir visibles.
Sous Linux, si vous souhaitez empêcher la pile réseau d'interférer, utilisez :echo "8"> /proc/sys/net/ipv4/conf/all/arp_ignore
MODIFICATION #2 Je ne suis pas complètement sûr de l'ETH_P_ARP. Cela aurait pu être un jugement rapide de ma part. L'utilisation de ETH_P_IP est correcte dans le champ d'en-tête ARP, mais je ne sais pas lequel utiliser pour le socket de paquet sll_protocol. Notez également que socket_address.sll_pkttype = PACKET_OTHERHOST;
n'a aucun effet lors de l'envoi (voir paquet man 7). Aussi l'observation obligatoire du SO, que vous devez toujours utiliser au moins -Wall (lors de l'utilisation de gcc ou clang) comme indicateur de compilation.
MODIFICATION #3 J'ai un peu modifié le programme. et mis à jour la réponse et le diff en conséquence. Étonnamment, il semble en effet que .sll_protocol doive être ETH_P_ARP. Ma copie du paquet homme 7 ne dit même pas qu'il est utilisé pour quoi que ce soit, mais le paquet ne sort pas sur le fil en tant qu'ARP sans lui.
J'ai pris le code de user6343961, j'ai fait du nettoyage et de l'épissage et j'ai implémenté un support pour obtenir automatiquement l'adresse IP de l'interface. De plus, les paramètres proviennent de CLI au lieu de hardcoding.bind() est également utilisé pour obtenir uniquement ARP à partir de l'interface que nous voulons. Amusez-vous. Ce code fonctionne pour moi.
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <arpa/inet.h> //htons etc
#define PROTO_ARP 0x0806
#define ETH2_HEADER_LEN 14
#define HW_TYPE 1
#define MAC_LENGTH 6
#define IPV4_LENGTH 4
#define ARP_REQUEST 0x01
#define ARP_REPLY 0x02
#define BUF_SIZE 60
#define debug(x...) printf(x);printf("\n");
#define info(x...) printf(x);printf("\n");
#define warn(x...) printf(x);printf("\n");
#define err(x...) printf(x);printf("\n");
struct arp_header {
unsigned short hardware_type;
unsigned short protocol_type;
unsigned char hardware_len;
unsigned char protocol_len;
unsigned short opcode;
unsigned char sender_mac[MAC_LENGTH];
unsigned char sender_ip[IPV4_LENGTH];
unsigned char target_mac[MAC_LENGTH];
unsigned char target_ip[IPV4_LENGTH];
};
/*
* Converts struct sockaddr with an IPv4 address to network byte order uin32_t.
* Returns 0 on success.
*/
int int_ip4(struct sockaddr *addr, uint32_t *ip)
{
if (addr->sa_family == AF_INET) {
struct sockaddr_in *i = (struct sockaddr_in *) addr;
*ip = i->sin_addr.s_addr;
return 0;
} else {
err("Not AF_INET");
return 1;
}
}
/*
* Formats sockaddr containing IPv4 address as human readable string.
* Returns 0 on success.
*/
int format_ip4(struct sockaddr *addr, char *out)
{
if (addr->sa_family == AF_INET) {
struct sockaddr_in *i = (struct sockaddr_in *) addr;
const char *ip = inet_ntoa(i->sin_addr);
if (!ip) {
return -2;
} else {
strcpy(out, ip);
return 0;
}
} else {
return -1;
}
}
/*
* Writes interface IPv4 address as network byte order to ip.
* Returns 0 on success.
*/
int get_if_ip4(int fd, const char *ifname, uint32_t *ip) {
int err = -1;
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
if (strlen(ifname) > (IFNAMSIZ - 1)) {
err("Too long interface name");
goto out;
}
strcpy(ifr.ifr_name, ifname);
if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) {
perror("SIOCGIFADDR");
goto out;
}
if (int_ip4(&ifr.ifr_addr, ip)) {
goto out;
}
err = 0;
out:
return err;
}
/*
* Sends an ARP who-has request to dst_ip
* on interface ifindex, using source mac src_mac and source ip src_ip.
*/
int send_arp(int fd, int ifindex, const unsigned char *src_mac, uint32_t src_ip, uint32_t dst_ip)
{
int err = -1;
unsigned char buffer[BUF_SIZE];
memset(buffer, 0, sizeof(buffer));
struct sockaddr_ll socket_address;
socket_address.sll_family = AF_PACKET;
socket_address.sll_protocol = htons(ETH_P_ARP);
socket_address.sll_ifindex = ifindex;
socket_address.sll_hatype = htons(ARPHRD_ETHER);
socket_address.sll_pkttype = (PACKET_BROADCAST);
socket_address.sll_halen = MAC_LENGTH;
socket_address.sll_addr[6] = 0x00;
socket_address.sll_addr[7] = 0x00;
struct ethhdr *send_req = (struct ethhdr *) buffer;
struct arp_header *arp_req = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
int index;
ssize_t ret, length = 0;
//Broadcast
memset(send_req->h_dest, 0xff, MAC_LENGTH);
//Target MAC zero
memset(arp_req->target_mac, 0x00, MAC_LENGTH);
//Set source mac to our MAC address
memcpy(send_req->h_source, src_mac, MAC_LENGTH);
memcpy(arp_req->sender_mac, src_mac, MAC_LENGTH);
memcpy(socket_address.sll_addr, src_mac, MAC_LENGTH);
/* Setting protocol of the packet */
send_req->h_proto = htons(ETH_P_ARP);
/* Creating ARP request */
arp_req->hardware_type = htons(HW_TYPE);
arp_req->protocol_type = htons(ETH_P_IP);
arp_req->hardware_len = MAC_LENGTH;
arp_req->protocol_len = IPV4_LENGTH;
arp_req->opcode = htons(ARP_REQUEST);
debug("Copy IP address to arp_req");
memcpy(arp_req->sender_ip, &src_ip, sizeof(uint32_t));
memcpy(arp_req->target_ip, &dst_ip, sizeof(uint32_t));
ret = sendto(fd, buffer, 42, 0, (struct sockaddr *) &socket_address, sizeof(socket_address));
if (ret == -1) {
perror("sendto():");
goto out;
}
err = 0;
out:
return err;
}
/*
* Gets interface information by name:
* IPv4
* MAC
* ifindex
*/
int get_if_info(const char *ifname, uint32_t *ip, char *mac, int *ifindex)
{
debug("get_if_info for %s", ifname);
int err = -1;
struct ifreq ifr;
int sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (sd <= 0) {
perror("socket()");
goto out;
}
if (strlen(ifname) > (IFNAMSIZ - 1)) {
printf("Too long interface name, MAX=%i\n", IFNAMSIZ - 1);
goto out;
}
strcpy(ifr.ifr_name, ifname);
//Get interface index using name
if (ioctl(sd, SIOCGIFINDEX, &ifr) == -1) {
perror("SIOCGIFINDEX");
goto out;
}
*ifindex = ifr.ifr_ifindex;
printf("interface index is %d\n", *ifindex);
//Get MAC address of the interface
if (ioctl(sd, SIOCGIFHWADDR, &ifr) == -1) {
perror("SIOCGIFINDEX");
goto out;
}
//Copy mac address to output
memcpy(mac, ifr.ifr_hwaddr.sa_data, MAC_LENGTH);
if (get_if_ip4(sd, ifname, ip)) {
goto out;
}
debug("get_if_info OK");
err = 0;
out:
if (sd > 0) {
debug("Clean up temporary socket");
close(sd);
}
return err;
}
/*
* Creates a raw socket that listens for ARP traffic on specific ifindex.
* Writes out the socket's FD.
* Return 0 on success.
*/
int bind_arp(int ifindex, int *fd)
{
debug("bind_arp: ifindex=%i", ifindex);
int ret = -1;
// Submit request for a raw socket descriptor.
*fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (*fd < 1) {
perror("socket()");
goto out;
}
debug("Binding to ifindex %i", ifindex);
struct sockaddr_ll sll;
memset(&sll, 0, sizeof(struct sockaddr_ll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifindex;
if (bind(*fd, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll)) < 0) {
perror("bind");
goto out;
}
ret = 0;
out:
if (ret && *fd > 0) {
debug("Cleanup socket");
close(*fd);
}
return ret;
}
/*
* Reads a single ARP reply from fd.
* Return 0 on success.
*/
int read_arp(int fd)
{
debug("read_arp");
int ret = -1;
unsigned char buffer[BUF_SIZE];
ssize_t length = recvfrom(fd, buffer, BUF_SIZE, 0, NULL, NULL);
int index;
if (length == -1) {
perror("recvfrom()");
goto out;
}
struct ethhdr *rcv_resp = (struct ethhdr *) buffer;
struct arp_header *arp_resp = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
if (ntohs(rcv_resp->h_proto) != PROTO_ARP) {
debug("Not an ARP packet");
goto out;
}
if (ntohs(arp_resp->opcode) != ARP_REPLY) {
debug("Not an ARP reply");
goto out;
}
debug("received ARP len=%ld", length);
struct in_addr sender_a;
memset(&sender_a, 0, sizeof(struct in_addr));
memcpy(&sender_a.s_addr, arp_resp->sender_ip, sizeof(uint32_t));
debug("Sender IP: %s", inet_ntoa(sender_a));
debug("Sender MAC: %02X:%02X:%02X:%02X:%02X:%02X",
arp_resp->sender_mac[0],
arp_resp->sender_mac[1],
arp_resp->sender_mac[2],
arp_resp->sender_mac[3],
arp_resp->sender_mac[4],
arp_resp->sender_mac[5]);
ret = 0;
out:
return ret;
}
/*
*
* Sample code that sends an ARP who-has request on
* interface <ifname> to IPv4 address <ip>.
* Returns 0 on success.
*/
int test_arping(const char *ifname, const char *ip) {
int ret = -1;
uint32_t dst = inet_addr(ip);
if (dst == 0 || dst == 0xffffffff) {
printf("Invalid source IP\n");
return 1;
}
int src;
int ifindex;
char mac[MAC_LENGTH];
if (get_if_info(ifname, &src, mac, &ifindex)) {
err("get_if_info failed, interface %s not found or no IP set?", ifname);
goto out;
}
int arp_fd;
if (bind_arp(ifindex, &arp_fd)) {
err("Failed to bind_arp()");
goto out;
}
if (send_arp(arp_fd, ifindex, mac, src, dst)) {
err("Failed to send_arp");
goto out;
}
while(1) {
int r = read_arp(arp_fd);
if (r == 0) {
info("Got reply, break out");
break;
}
}
ret = 0;
out:
if (arp_fd) {
close(arp_fd);
arp_fd = 0;
}
return ret;
}
int main(int argc, const char **argv) {
int ret = -1;
if (argc != 3) {
printf("Usage: %s <INTERFACE> <DEST_IP>\n", argv[0]);
return 1;
}
const char *ifname = argv[1];
const char *ip = argv[2];
return test_arping(ifname, ip);
}