Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is one structure mapped to another by something like "return (struct a *) b"

I'm trying to understand the linux kernel network code and I come up across many functions like

static inline struct tcp_sock *tcp_sk(const struct sock *sk)
{
        return (struct tcp_sock *)sk;
}

The contents of the structures sock and tcp_sock are quite different. How does this mapping exactly happen? Do the structure members with the same name and type get mapped to each other?

Edit 1: To be more specific, in the function tcp_v4_connect

struct tcp_sock *tp = tcp_sk(sk);
tp->write_seq = secure_tcp_sequence_number(inet->inet_saddr,
                           inet->inet_daddr,
                           inet->inet_sport,
                           usin->sin_port);
err = tcp_connect(sk);

And then in the function tcp_connect called above,

struct tcp_sock *tp = tcp_sk(sk);
tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);

The variable write_seq exists only in the struct tcp_sock so how can it's value have been passed on to the function tcp_connect when only the struct sock *sk was passed to it?

Thank you.

like image 893
Gaurav Suman Avatar asked Oct 15 '25 07:10

Gaurav Suman


2 Answers

Do the structure members with the same name and type get mapped to each other?

No.

struct sock sk; /* our sock struct */
struct tcp_sock *tcp_sk = (struct tcp_sock *)&sk;

The tcp_sk pointer now simply points to memory pointed to by &sk. By accessing tcp_sk fields now you will access the memory assigned to this pointer but in order to get expected result the data stored at both addresses should be valid.

Now, struct sock (network layer representation of sockets) has struct sock_common as it's first member:

306 struct sock {
307         /*
308          * Now struct inet_timewait_sock also uses sock_common, so please just
309          * don't add nothing before this first member (__sk_common) --acme
310          */
311         struct sock_common      __sk_common;

And struct tcp_sock has struct inet_connection_sock as it's first member

struct tcp_sock {
138         /* inet_connection_sock has to be the first member of tcp_sock */
139         struct inet_connection_sock     inet_conn;

and this struct has struct inet_sock as it's first member:

 90 struct inet_connection_sock {
 91         /* inet_sock has to be the first member! */
 92         struct inet_sock          icsk_inet;

and guess what struct is the first member of inet_sock... It is struct sock:

172 struct inet_sock {
173         /* sk and pinet6 has to be the first two members of inet_sock */
174         struct sock             sk;

So by accessing tcp_sk.inet_conn.icsk_inet.sk you are accessing our sock struct.

like image 124
4pie0 Avatar answered Oct 16 '25 23:10

4pie0


As the construct is merely a cast, nothing is "mapped", nothing at the memory location that sk points to is actually changed.

This only works if the structures are similar or are at least starting with the same fields at the same offsets.

In linux kernel code you can see that struct sock and struct tcp_sock have all of the fields of struct sock in common (tcp_sock contains an inet_connection_sockwhich contains an inet_sock which in turn contains a sock as first elements, respectively). So, "the contents of the structures...are quite different", is not quite true. They are actually identical (at least for the size of a struct sock).

In order to be able to do this properly, the system somehow "must know" that it can safely cast a struct sock to a struct tcp_sock - This information is normally hidden somewhere in the common header, in this case in the struct sock part (I haven't checked recently, but I guess here the information is in sk_family)

After your edit:

What is actually passed to your function is a pointer to a memory region. Whatever has there been before is not changed by the cast (as I said above). If we can safely assume that sk was a pointer to a struct tcp_sock some time before, we can safely cast it back to such a beast. This is often done in code that moves around data that has a generic and a specific part (as in this case). All socket (TCP, UDP, generic, Unix sockets,...) data has common data that is collected in a struct sock at the beginning of all specific socket structures. So, on incoming socket data, all of the generic parts of that structures are first handled by functions handling a struct sock, later one there must be some big switch...case that looks into the specific parts, then passing on a specific pointer type.

This allows a layeredprogramming approach, where everything is first cast to a generic struct sock, then the generic part is handled by functions common for all socket types, later on, the pointer is cast back to what it originally was, and the specific part is handled by specialist functions. All the time, the memory area that is basically used as a "backpack" on a struct sock to hold specific data is simply left unchanged.

like image 31
tofro Avatar answered Oct 17 '25 00:10

tofro