2010-10-03 9 views
1

Je suis un peu confus en ce qui concerne l'alignement des données. Sur x86, nous prenons généralement l'alignement pour acquis. Cependant, je suis en train de programmer sur un système très strict et je ferai erreur si j'essaie d'accéder à des données non alignées.Alignement des données avec la programmation réseau

Heres mon problème:

D'abord, je vais vous montrer quelques struct j'ai:

struct sniff_ethernet { 
    u_char ether_dhost[6]; /* Destination host address */ 
    u_char ether_shost[6]; /* Source host address */ 
    u_short ether_type; /* IP? ARP? RARP? etc */ 
}; 

struct sniff_ip { 
    u_char ip_vhl; /* version << 4 | header length >> 2 */ 
    u_char ip_tos; /* type of service */ 
    u_short ip_len; /* total length */ 
    u_short ip_id; /* identification */ 
    u_short ip_off; /* fragment offset field */ 
    u_char ip_ttl; /* time to live */ 
    u_char ip_p; /* protocol */ 
    u_short ip_sum; /* checksum */ 
    struct in_addr ip_src,ip_dst; /* source and dest address */ 
}; 

je traite PPCE. Pcap renvoie un pointeur vers un paquet de données pour moi:

u_char *packet; 

le paquet Lets faire semblant est quelques centaines d'octets. Ce que je fais généralement est de lancer ce paquet vers plusieurs pointeurs de structure, donc je peux accéder directement aux données.

struct sniff_ethernet *seth = (struct sniff_ethernet *) packet; 
struct sniff_ip *sip = (struct sniff_ip *) (packet + 14); // 14 is the size of an ethernet header 

Ok. Donc tout semble bien non? Sur x86, tout semble fonctionner correctement. Sur toute autre architecture qui a un alignement strict, j'ai des problèmes lors de l'accès à certaines valeurs et il en résultera généralement un sigbus. Par exemple:

sip->ip_len = 0x32AA; 

ou

u_short val = sip->ip_len; 

provoque une erreur. Je suppose que c'est parce qu'il est mal aligné dans la mémoire de la distribution. Quelle est généralement la meilleure façon de gérer ce genre de moulage?

+0

Dans gcc, vous avez __attribute __ ((packed)) pour indiquer au compilateur d'aligner les structures de manière stricte, sans aucun remplissage. – Plumenator

+0

'__attribute __ ((packed))' ne ferait aucune différence dans ce cas, car ces structures n'ont pas de remplissage. –

Répondre

2

Le plus simple est d'utiliser memcpy

struct sniff_ip sip; 
memcpy(&sip, packet + 14, sizeof(sip)); 

Cela suppose que vos deux machines utilisent le même ordre d'octets, et ont pris soin de tenir compte du rembourrage de la structure.

La façon de plus en plus générale de gérer cela est de construire les valeurs des octets individuels:

u_short val; 
int offset = 14 + offsetof(sniff_ip, ip_len); 
val = packet[offset] + (packet[offset+1] << 8); // assuming little endian packet 

Bien sûr, vous auriez probablement utiliser une fonction ou macro abstraite cela.

+2

Plutôt que d'utiliser '+' et '<<', vous pouvez faire 'memcpy()' à une variable intermédiaire, puis utiliser 'ntohs()' pour convertir le réseau en ordre d'octets hôte. – caf

+0

Oui, mais cela suppose que les structures ont été initialement remplies en utilisant l'ordre réseau (big endian). J'ai eu l'impression à la question qu'ils étaient remplis en utilisant l'ordre x86 (petit boutiste). –

+0

De la question, "pcap me retournera un pointeur vers un paquet de données" signifie que le code reçoit des paquets; le type Ethernet et les champs dans l'en-tête IP sont tous big-endian, pas little-endian. Le problème concernant x86 et non-x86 est lié aux exigences d'alignement, pas à l'ordre des octets; x86 ne nécessite pas (par défaut) d'alignement, mais * d'autres architectures d'ensembles d'instructions (par exemple, SPARC - qui est par défaut big-endian) l'exige. –