Newsgroup: it.comp.lang.c
Titolo: Ricavare bytes "rimanenti"

Ammettiamo di avere una struttura di questo tipo
--8<---------------cut here---------------start------------->8---
typedef struct myPacket {
    void *packet;
    int len;
    unsigned int seq_no;
    csum_type checksum;
} myPacket;
--8<---------------cut here---------------end--------------->8---
Vorrei avere una funzione che, dato l'mtu del canale, mi assegni il
massimo numero di bytes a *packet.
Il problema è che a seconda del compilatore e architettura le altre
dimensioni cambiano, c'è un modo più astuto di
int size = mtu - (sizeof(int) + ....)
per capire quanto assegnare?


ovvero vuoi che myPacket diventi "grosso" quanto vuoi te aumentato la
dimensione del membro *packet?
Non vedo comunque come ti potrebbe essere utile questa cosa, visto che
non potresti comunque usare la struttura per una read o una write
visto che la memoria di *packet viene allocata a runtime.
Io userei una struttura per i dati "al di fuori" di *packet
utilizzando bit-fields/union (e ovviamente l'apposita direttiva del
tuo compilatore per eliminare allineamenti) in maniera da avere un
sizeof() preciso della struttura. Il resto viene da se (una semplice
sottrazione). Per ogni pacchetto avrai comunque due scritture e due
letture (ovviamente poi, se vuoi, puoi mettere tutto dentro un'unica
struttura "logica" per comodità).
Non so se ho risposto ai tuoi dubbi o c'è dell'altro!
Ciao!

 Ciao!
Sì in sostanza dato un dato di dimensione N, voglio creare K pacchetti
non più grossi di DIM impostando i vari campi al loro interno.
Minimizzare il numero di pacchetti sarebbe ottimo...
Ma non mi è ben chiaro "al di fuori" di packet?
Un esempio semplice semplice per favore? (sono piuttosto scarso a questo
basso livello purtroppo)

 wrote:

Ribadendo che secondo me non è utile, immagino che dalla spiegazione
tu voglia qualcosa del genere:
struct packet_header_s {
    unsigned len:32;
    unsigned seq_no:32;
    unsigned checksum:32;
} __attribute__((__packed__));
typedef struct packet_header_s t_packet_header_s;
#define PACKET_HEADER_SIZE (sizeof(t_packet_header_s))
typedef struct packet_s {
    void *payload;
    t_packet_header_s head;
} t_packet_s;
#define MAX_PAYLOAD_SIZE 50
#define MAX_PAYLOAD_PP (MAX_PAYLOAD_SIZE-PACKET_HEADER_SIZE)
int split_into_packets(void *data, size_T datasize, t_packet_s
**outpackets, int *n_outpackets)
{
    int i, sent;
    /* calculate number of packets */
    *n_outpackets = datasize/MAX_PAYLOAD_PP ;
    if (datasize%MAX_PAYLOAD_PP) ++*n_outpackets;
    /* allocate outpackets */
    if ((*outpackets =
malloc(sizeof(***outpackets)*(*n_outpackets)))==NULL) /* deal with err
*/
    for (i=sent=0; i<*n_outpackets; ++i) {
        /* allocate packets and copy data */
        if (((*outpackets)[i] =
malloc(sizeof(*(*outpackets[i]))))==NULL) /* deal with err */
        (*outpackets)[i].payload = ((unsigned char *)data)+sent;
        (*outpackets)[i].head.len = ((datasize-sent)>MAX_PAYLOAD_PP)?
MAX_PAYLOAD_PP:datasize-sent;
        (*outpackets)[i].head.len += PACKET_HEADER_SIZE; /* if you
want the head len too */
        (*outpackets)[i].head.seq_no = i;
        (*outpackets)[i].head.checksum = /* whatever */
        sent += 50-PACKET_HEADER_SIZE;
    }
    return 0;
}
L'ho scritto tutto di fila senza controllare, quindi non ti aspettare
che compili e funzioni - è solo un'indicazione di massima :)
Ciao!

 tu voglia qualcosa del genere:
Grazie mille, ma ancora non mi è chiarissimo come fare...
In sostanza devo incapsulare in due header diversi, prima un header mio
e poi l'header ipV6 (che se non ho capito male usa 40 bits).
Ora ad esempio mi hanno spiegato che per creare un pacchetto ipv6
definisco l'header e il payload lo "attacco" subito dopo, ma questo
significare fare una union o una struct con l'attributo __packed__?
Inoltre che casini possono risultare dall'utilizzo di __packed__ e la
consegnuente (in teoria) perdita di allineamento?

 consegnuente (in teoria) perdita di allineamento?
Una perdita di prestazioni nell'accesso ai membri della struttura.
Ciao!

 preoccuparti del livello IP, è già gestito.
Eh no purtroppo devo lavorare con 6lowpan e devo costruire il pacchetto
a manina...
Grazie

 Grazie
Ciao!

 consegnuente (in teoria) perdita di allineamento?
indipendentemente da cosa ci metti, se non ho capito
male, tu hai dei pacchetti composti da una parte fissa
(l' header) e una parte variabile(il payload).
In C non puoi definire una struttura dati 'variabile' ma puoi
applicare varie tecniche pe farlo.
ne riporto un esempio, non so' se applicabile al tuo caso:
se il payload e' formato da dati interi definisci due
sttrutture del tipo:
// Struttura dell' header di paccketto (fissa)
typedef struct myPacket {
      int len;   // Length of payload
      unsigned int seq_no;
      csum_type checksum;
}myPacket_hdr;
// Struttura generica del pacchetto -variabile :-)
typedef struct packet{
        myPacket_hdr  hd;
        int           dati[1]; // Trick: allocato a run time...
//
// NB: se il payload puo' essere differente
//     union{
//        uint8_t  d8[1];    // payload dati 8bit
//        uint16_t d16[1];   // payload dati 16bit
//        uint32_t d32[1];   // payload dati 32bit
//     }dati;
}MP_T;
//
// Crea un nuovo pacchetto dati.
// La lunghezza del pacchetto
MP_T* create_packet(int seq, int numOfData, int* pPayBuf)
{
    MP_T*  pPack;
    size_t len;
    len= (sizeof(int)*numOfData);
    if(NULL != (pPack = malloc(len+sizeof(myPacket_hdr))))
    {
         pPack->hd.len=len;
	pPack->hd.seq_no=seq;
         if((NULL != pPayBuf) && (len))
         {
           // Se il payload NON e' vuoto, copialo!!!
            memcpy((void*)&pPack->dati[0], (void*)pPayBuf, len);
         }
         // calcola checksum....
	pPack->hd.checksum=compute_checksum(pPack);
    }
    return(pPack);		
}
bye,
A.

 buffer passo passo e spedisci quello.
In che senso spedisci un dato della struttura alla volta?
Intendi della struttura myPacket?
E perché mai dovrei dividerla in questo modo?
Grazie

[...]
Ottimo grazie della risposta dettagliata.
Ora io mi domando, una volta che ho le mie belle struct definite degli
header.
Posso ricavare in teoria anche a compile time la dimensione max con
--8<---------------cut here---------------start------------->8---
#define MAX_PAYLOAD (1000 - sizeof(struct ipv6) - sizeof(struct myPacket))
--8<---------------cut here---------------end--------------->8---
?

No mi sa che a compile time non è dato saperlo, ma probabilmente non mi
serve nemmeno, quindi anche così potrebbe andare, credo:
--8<---------------cut here---------------start------------->8---
#include <stdlib.h>
#include <netinet/ip6.h>
#include <stdint.h>
// measures in bytes
#define SIZE_IPV6_HEADER 40
#define MAX_CARRIED 127
struct myPacketHeader {
    int len;
    uint8_t seq_no;
    uint8_t ordering_no;
};
typedef struct myPacketHeader myPacketHeader;
// only the final ipv6 packet must be "__packed__".
struct ipv6Packet {
    struct ip6_hdr ip6_hdr;
    myPacketHeader packetHeader;
    void *payload;
} __attribute__((__packed__));
typedef struct ipv6Packet ipv6Packet;
int MAX_PAYLOAD_SIZE = MAX_CARRIED - sizeof(myPacketHeader) - sizeof(struct ip6_hdr);
int main(int argc, char *argv) {
    ipv6Packet v6;
 
    return 0;
}
--8<---------------cut here---------------end--------------->8---
E' corretto mettere __packed__ solo sulla struttura esterna, quella che
effettivamente viene mandata in rete?
Oppure serve anche un altro __packed__ più interno?

 Oppure serve anche un altro __packed__ più interno?
Che io sappia elimina il padding della sola struttura dove è definito
- ovvero non è "ricorsivo" :-)
Ciao!

 Oppure serve anche un altro __packed__ più interno?
Dovrebbe allineare tutti i campi della struttura
inclusi quelli interni.
Col gcc dovresti potere usare anche la direttiva
tipo MS #pragma pack(n) (che preferisco: c'e' meno
da scrivere )
// Allineamento al byte:
#pragma pack(1)
struct ipv6Packet {
      struct ip6_hdr ip6_hdr;
      myPacketHeader packetHeader;
      void *payload;
};
// Per tornare all' allineamento originale:
#pragma pack()
A.


A guardare ad esempio in ip6.h mettono il __packed__ anche all'interno.
Mi sa quindi che c'è da metterlo dappertutto, altrimenti non è detto che
nel ricevente riusciamo a fare il casting del payload alla mia
struttura, giusto?
Mi domando se è meglio fare così (semplificando)
--8<---------------cut here---------------start------------->8---
struct ipv6packet {
      - ipv6header
      - payload
}
struct innerPacket {
       - myHeader
       - payload
}
--8<---------------cut here---------------end--------------->8---
Oppure
--8<---------------cut here---------------start------------->8---
struct ipv6packet {
       - ipv6header
       - myHeader
       - payload
}
--8<---------------cut here---------------end--------------->8---
La prima sembra meglio, ma non sono sicuro che lo sia...

Guardando per esempio qui
http://blacklight.gotdns.org/wiki/index.php/Raw_socket
vedo che l'attributo __packed__ non lo usano proprio, ma fanno
semplicemente delle memcpy una dopo l'altra
--8<---------------cut here---------------start------------->8---
  memcpy (buff, ip, IPLEN);
  memcpy (buff+IPLEN, icmp, ICMPLEN);
  memcpy (buff+IPLEN+ICMPLEN, data, sizeof(data));
--8<---------------cut here---------------end--------------->8---
che mi sembra un po' brutale, ma alla fine è equivalente al mio
approccio (struct con __packed__) ?
E l'endinaness può anche essere un problema mandando i dati in rete?

 E l'endinaness può anche essere un problema mandando i dati in rete?
Certo che sì. Ogni protocollo definisce l'endianess ovunque ce ne sia
bisogno, e anche te per il "tuo" protocollo dovresti fare lo stesso!
Ciao!


Ottimo grazie, diciamo che mi sembra un po' brutale usare i memcpy, e
pensavo tra l'altro che non fosse "packed", invece copia proprio bytes
in sequenza...
Ora però vorrei capire un altra cosa, perché caspita pur settando
l'header ipv6 del mio pacchetto in teoria correttamente, sniffando i
pacchetti non vedo questi valori...
--8<---------------cut here---------------start------------->8---
ip6_hdr genIpv6Header(size_t payload_len) {
    ip6_hdr header;
    printf("payload len = %ld\n", payload_len);
    header.ip6_src = in6addr_loopback;
    header.ip6_dst = in6addr_loopback;
    header.ip6_ctlun.ip6_un1.ip6_un1_plen = payload_len;
    header.ip6_ctlun.ip6_un2_vfc = 6;
    /* header->ip6_src = 0; */
    return header;
}
--8<---------------cut here---------------end--------------->8---
e quello che mi arriva è alla fine solo questo
--8<---------------cut here---------------start------------->8---
###[ Ethernet ]###
  dst= 00:00:00:00:00:00
  src= 00:00:00:00:00:00
  type= 0x86dd
###[ IPv6 ]###
     version= 12L
     tc= 1L
     fl= 1003265L
     plen= 0
     nh= Hop-by-Hop Option Header
     hlim= 0
     src= a00::
     dst= ::1:0:0:ffff:ffff
--8<---------------cut here---------------end--------------->8---