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.
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.
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_sock
which 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 layered
programming 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With