Chaining Structs and read them byte wise (ANSI C)

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Guys,

 

It's been a while since i did some programming in C and so i just started a few weeks ago again.

Now i have a almost simple issue i can't really solve...

 

I have several structs:

 

// MAC Adresse
typedef union _mac_a {
	uint8_t  b[6] ;
	uint16_t w[3] ;
} MAC_A;

// IPv4 Header
typedef union _ipv4 {
	uint8_t   b[4] ;
	uint16_t  w[2] ;
	uint32_t  d ;
} IPv4;

typedef struct ethernet_header {
  MAC_A 	Destination;
  MAC_A 	Source;
  uint16_t      Protocol;
  void 	        *Data;
} ETHERNET_HEADER;

typedef struct _ipv4_header {
	uint8_t		Version;
	uint8_t		DSF;
	uint16_t	TLength;
	uint16_t	ID;
	uint16_t	FLFROF;	 //Flags 3 Bit and 13Bit FragmentOffset
	uint8_t		TTL;
	uint8_t		Protocol;
	uint16_t	HeaderCRC;
	IPv4		Src;
	IPv4		Dst;
	void		*Data;
}  IPv4_HEADER;

 

Iwould like to chain them and to pass the pointer of the first struct to a function (together with it's size in bytes (sizeof(...) + sizeof(...)).

And inside the function read the data behind it byte wise, including the real datta of the structre referred in the Pointer pFrame.Data (

 

//Prototype
void DoSomething(unsigned int len, void *pData);

void main(void) {
    MAC_A Src=0;
    MAC_A Dst=0;
    ETHERNET_HEADER pFrame;
    IPv4_HEADER ipdatagram;

    pFrame.Destination = Dst;
	pFrame.Source = Src;
    pFrame.Protocol = little2big16(0x0800);

    // The idea was to chain ipdatagram to the
    // Pointer "Data" and read it in "DoSomething" like it was a char array...
    pFrame.Data = (void *)&ipdatagram;

	ipdatagram.FLFROF = 0;
	ipdatagram.TTL= 64;
	ipdatagram.Version = 0x45;
	ipdatagram.ID = 1;
	ipdatagram.Protocol = 17;
	ipdatagram.HeaderCRC = 0;

	DoSomething(sizeof(ETHERNET_HEADER)+sizeof(IPv4_HEADER), &pFrame)
}

 

But somehow it doesn't work as if i read the pFrame byte wise i get between the real data ofipdatagram and the last data byte of pFrame some extra bytes. Looks like it's the address...

 

What I'm I doing wrong? How would you establish something like i tried to do?

 

Thanks,

Greets,

 

Dragon

This topic has a solution.
Last Edited: Sat. Oct 29, 2016 - 04:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You didn't say anything about your platform. You are probably running this on a system with a pointer size of 4 and your ETHERNET_HEADER has a gap between Protocol and Data (because the pointer Data has to be aligned on a 4-byte boundary).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry man - forgot to say. I'm using an ATmega328p (it's an arduino clone - using eclipse / avrdude and winavr)

 

If we forget about the gap, would it work like that?

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There should be no gaps on ATmega328 (or any other AVR8, for that matter). I'm not sure what kind of "chain" do you expect to happen. Do you expect that your "ipdatagram" will somehow be placed right after "pFrame" in memory? In that case you probably want to put both of them into a wrapper structure and pass a pointer to that structure to the function.

struct {
  ETHERNET_HEADER pFrame;
  IPv4_HEADER ipdatagram;
} wrapper;
...
DoSomething(sizeof(wrapper), &wrapper);

Last Edited: Sat. Oct 29, 2016 - 01:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How about adding a pointer to the second struct in the "chain" to the end of the first struct, and so forth?

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I already thought about preallocate the memory and copying it over...

 

Okay your idea with the wrapper sounds brilliant.

So i would remove the last element of each structure (as the "Data" element becomes obsolete) and wrap them together in a structure.

 

I'll check that out.

 

Thanks a lot ezharkov!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do I interpret your idea correctly that you want to use a single pointer to index over 2 different data structures?

How do you guarantee that the 2 structures form a continuous block of memory?

The very least is to combine your headers into a structure:

typedef struct _total_header{
    ETHERNET_HEADER;
    IPv4_HEADER;
} TOTAL_HEADER;

And then declare & use and instance of that type.

but that is not your direct problem.

In both your EHTERNET_HEADER and in IPv4_HEADER you have declared a "void *Data;" and the compiler dutifully includes those pointers into the structures.

So your "header" structures are not really the headers because of the extra pointer.

 

I haven't worked much with ethernet myself, but the Ethernet stuff (mac addresses etc) is usually handled as a separate layer from the TCP/IP stuff and I think it's good to keep them separated in your software also. IPv4 is IPv4 but the ethernet part will change depending on your hardware (W5100, enc28j60, Realtek, etc).

 

It's also very common these days to connect small uC's to Ethernet and there a multiple libraries available to choose from.

So my advise is to look at a few of them and build upon that. Why reinvent the wheel?

When reasearching this I also would not limit myself to a lib for AVR either. A well written lib for another architecture (PIC, ARM, STM8, etc) is probably easyer to port & use than a badly written lib for the same architecture you are using yourself.

 

Looking at other peoples code can also be very educational. Bad code is easier to spot than your own bad code and good code educates you to write better code yourself.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Last Edited: Sat. Oct 29, 2016 - 01:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If one has appropriately typed pointers,

copying bytes from a tree structure is not hard:

unsigned char *unsafeCopyTypeA(unsigned char *buf, const TypeA *ptrA)
{
    enum { SZ1=offsetof(TypeA, ptrB) } ;
    memcpy(buf, ptrA, SZ1);
    buf=unsafeCopyTypeB(buf+SZ1, ptrA->ptrB);

    enum { SZ2=offsetof(TypeA, ptrC)-offsetof(TypeA, ptrB)-sizeof(TypeB*) };
    memcpy(buf, (unsigned char*)(ptrA->ptrB)+sizeof(TypeB), SZ2);
    buf=unsafeCopyTypeC(buf+SZ2, ptrA->ptrC);

    return buf;
}

Pointers to void do not provide necessary information.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey Guys!

 

Thanks a lot for the great and constructive answers - the wrapper structure works like a charm.

 

Thanks a lot!

 

Greets,

Dragon