Discussion:
values from a struct
goinsane
2010-09-17 07:10:20 UTC
Permalink
Hi,

I'd like to get values from the struct sockaddr_in which is passed to accept . At the time of entry this struct has still no values. How could I access the struct at return? The usage of arg0 seems useless, as this is a new filedesriptor. Does the copyin at entry make sense at all?

fbt:sockfs:accept:entry
{
newsock = (struct sockaddr_in*)copyin(arg1, arg2); */
}


fbt:sockfs:accept:return
{
/* ??? */
}


thank you,

goinsane

Message was edited by: goinsane
--
This message posted from opensolaris.org
krishnan parthasarathi - Sun Microsystems - Bangalore India
2010-09-17 10:12:05 UTC
Permalink
Hi goinsane,
Post by goinsane
I'd like to get values from the struct sockaddr_in which is passed to accept . At the time of entry this struct has still no values. How could I access the struct at return? The usage of arg0 seems useless, as this is a new filedesriptor. Does the copyin at entry make sense at all?
The copyin at the entry probe does not make sense. Since the address of
the sockaddr_in structure will not be available
in argN builtin variables, you must store the addresses you are
interested in in a thread local variable. On the return probe
you can use copyin builtin method to get the contents of the sockaddr
structure.
The modified script would look like the following,

fbt:sockfs:accept:entry
{
self->sockaddr_ref = arg1;
self->sockaddr_len = arg2;
}

fbt:sockfs:accept:return
{
self->sockaddr_content = (struct sockaddr_in*)
copyin(self->sockaddr_ref, self->sockaddr_len);
/* whatever your script intends to with the sockaddr structure */
}

cheers,
Krishnan
Post by goinsane
fbt:sockfs:accept:entry
{
newsock = (struct sockaddr_in*)copyin(arg1, arg2); */
}
fbt:sockfs:accept:return
{
/* ??? */
}
thank you,
goinsane
Message was edited by: goinsane
goinsane
2010-09-17 11:40:08 UTC
Permalink
Helle Krishnan,

thank you! I thought it's not possible to store an address of a struct.
Do you know how to extract the current size of the struct?
From accept(3socket):

" The argument addrlen is a value-result parameter. Initially,
it contains the amount of space pointed to by addr; on
return it contains the length in bytes of the address
returned. "

Unfortunately, D returns an error at the copyin:

dtrace: error on enabled probe ID 2 (ID 28357: fbt:sockfs:accept:return): out of scratch space in action #1 at DIF offset 52


If I print the value of arg2, it shows incredible 4290770780 .

cheers, chris
--
This message posted from opensolaris.org
James Carlson
2010-09-17 12:16:06 UTC
Permalink
Post by goinsane
Helle Krishnan,
thank you! I thought it's not possible to store an address of a struct.
Do you know how to extract the current size of the struct?
" The argument addrlen is a value-result parameter. Initially,
it contains the amount of space pointed to by addr; on
return it contains the length in bytes of the address
returned. "
dtrace: error on enabled probe ID 2 (ID 28357: fbt:sockfs:accept:return): out of scratch space in action #1 at DIF offset 52
If I print the value of arg2, it shows incredible 4290770780 .
That's because (just like the accept() call itself), it's actually a
pointer to the value, not the value itself. You have to do a
copyin(arg2, 4) first.
--
James Carlson 42.703N 71.076W <carlsonj-dlRbGz2WjHhmlEb+***@public.gmane.org>
goinsane
2010-09-17 13:34:43 UTC
Permalink
Yes, you're right, the manpage expresses this, too.
But how to get the value? copyin returns the address of the scratch buffer. could this be used as argument of the 2nd copyin of fbt:sockfs:accept:return ?

fbt:sockfs:accept:entry
{
self->sockaddr = arg1;
self->socklen = (int*)copyin(arg2, 4);
}

fbt:sockfs:accept:return
{
self->sockcont = (struct sockaddr_in*)copyin(self->sockaddr, self->socklen);
}


Unforntunately, I' m getting an error within the 1st probe:

dtrace: error on enabled probe ID 1 (ID 28356: fbt:sockfs:accept:entry): invalid address (0x0) in action #2 at DIF offset 52
--
This message posted from opensolaris.org
James Carlson
2010-09-17 13:47:06 UTC
Permalink
Post by goinsane
Yes, you're right, the manpage expresses this, too.
But how to get the value? copyin returns the address of the scratch buffer. could this be used as argument of the 2nd copyin of fbt:sockfs:accept:return ?
fbt:sockfs:accept:entry
{
self->sockaddr = arg1;
self->socklen = (int*)copyin(arg2, 4);
}
It's an in-out parameter. The value at entry is just the _maximum_ size
of the caller's buffer, and not the _actual_ size of the returned data.
Post by goinsane
fbt:sockfs:accept:return
{
self->sockcont = (struct sockaddr_in*)copyin(self->sockaddr, self->socklen);
}
I would do this in the entry clause:

self->sockaddr = arg1;
self->socklenp = arg2;

and then this in the return clause:

self->socklen = *(int *)copyin(self->socklenp, 4);
self->sockcont = (struct sockaddr_in *)copyin(self->sockaddr,
self->socklen);

(This assumes the application is 32-bit or at least that the system is
little-endian. Things get more complicated if you have to deal with
64-bit big-endian systems.)

To be really robust, you could have multiple return causes -- one to get
the return length, another to check it and fetch the contents, still
another to look at the sa_family, and so on.
--
James Carlson 42.703N 71.076W <carlsonj-dlRbGz2WjHhmlEb+***@public.gmane.org>
goinsane
2010-09-17 15:14:04 UTC
Permalink
Post by goinsane
Post by goinsane
Yes, you're right, the manpage expresses this, too.
But how to get the value? copyin returns the
address of the scratch buffer. could this be used as
argument of the 2nd copyin of
fbt:sockfs:accept:return ?
Post by goinsane
fbt:sockfs:accept:entry
{
self->sockaddr = arg1;
self->socklen = (int*)copyin(arg2, 4);
}
It's an in-out parameter. The value at entry is just
the _maximum_ size
of the caller's buffer, and not the _actual_ size of
the returned data.
Post by goinsane
fbt:sockfs:accept:return
{
self->sockcont = (struct
sockaddr_in*)copyin(self->sockaddr, self->socklen);
Post by goinsane
}
self->sockaddr = arg1;
self->socklenp = arg2;
self->socklen = *(int *)copyin(self->socklenp, 4);
Oh, I forgot to derefence it. I should really refresh my knowledge of C ...
Post by goinsane
self->sockcont = (struct sockaddr_in
n *)copyin(self->sockaddr,
self->socklen);
(This assumes the application is 32-bit or at least
that the system is
little-endian. Things get more complicated if you
have to deal with
64-bit big-endian systems.)
Hm, that's a point I had not noticed yet. I expected the D scripts running independent from the system's arch. But as it becomes more complex, it seems this has to be handled, too.
Right now, I'm running at 64 bit i86pc . I'm getting a value of the port (have to verify it), but no value of the ip address.

self->port = self->sockcont->sin_port;
self->ip_int = (uint_t)self->sockcont->sin_addr.s_addr;
Post by goinsane
To be really robust, you could have multiple return
causes -- one to get
the return length, another to check it and fetch the
contents, still
another to look at the sa_family, and so on.
That's the one I'll try next.


Tank you very much, James!
Post by goinsane
--
James Carlson 42.703N 71.076W
_______________________________________
dtrace-discuss mailing list
--
This message posted from opensolaris.org
James Carlson
2010-09-17 15:31:17 UTC
Permalink
Post by goinsane
Post by James Carlson
(This assumes the application is 32-bit or at least
that the system is
little-endian. Things get more complicated if you
have to deal with
64-bit big-endian systems.)
Hm, that's a point I had not noticed yet. I expected the D scripts running independent from the system's arch. But as it becomes more complex, it seems this has to be handled, too.
D scripts run in the context of the kernel. If you've got a 64-bit
kernel, then your D scripts run as (effectively) 64-bit applications.
User-space applications on a 64-bit system, though, may be either 32-bit
or 64-bit; the system supports both simultaneously. That's what adds in
a bit of complexity. Things running in the kernel (such as D scripts)
need to be aware of what flavor of application is out there when they go
mucking around with application data structures -- as with copyin.
Post by goinsane
Right now, I'm running at 64 bit i86pc . I'm getting a value of the port (have to verify it), but no value of the ip address.
self->port = self->sockcont->sin_port;
self->ip_int = (uint_t)self->sockcont->sin_addr.s_addr;
Don't forget that the port number will be in network (big-endian) byte
order.

As for the problem with the IP address, I don't know what could be going
on. You might want to try a truss -vall to see if the values you're
getting are the ones you're expecting.

The alignment of sockaddr_in is such that I'd expect it to work fine
over the 32/64 boundary without special handling, so if you're seeing
unexpected behavior here, it's time to start dumping everything -- start
by printing out the length of the structure that's being copied (perhaps
that's wrong) and the first few bytes of the structure to see if it's
what you expect.
--
James Carlson 42.703N 71.076W <carlsonj-dlRbGz2WjHhmlEb+***@public.gmane.org>
goinsane
2010-09-24 09:19:25 UTC
Permalink
This becomes interesting. truss shows an ipv6 address for some processes, like sshd (ipv4 encapsulated in ipv6), although there is no configured ipv6 interface.

909: accept(3, 0x08047A10, 0x08047E78, SOV_DEFAULT) = 4
909: AF_INET6 name = ::ffff:172.31.70.6 port = 64625


When the probe fbt:sockfs:accept:return is fired , af_family is set to 26 (INET6) instead of 2 (INET). The return of an ipv6 address seems to be the reason for the value of 0 of (uint_t)self->sockcont->sin_addr.s_addr . I still have no glue how the struct sin_addr for ipv6 does look like, if and how it differs from the original ipv4 one.

Is there a way to find out the structs getting accessed when a probe is fired?



I've create created a perlscript, which uses IO::Socket::INET to create a listening socket. truss return the address family AF_INET and the ipv4 address.

23523: accept(4, 0x080475E8, 0x08047A00, SOV_DEFAULT) = 5
23523: AF_INET name = 172.31.70.6 port = 64622

With the following D I could catch the source ip and port. You're right, I've to distinguish the endianness. So it won't work on sparc.

What predicate could be used to differentiate between little and big endian?
Do you see a simpler way of the bit operations I'm doing to get the port and ip?


#!/usr/sbin/dtrace -qCs

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

fbt:sockfs:accept:entry
/ execname == "tcps.pl" /
{
self->sockaddr = arg1;
self->socklenp = arg2;
}


fbt:sockfs:accept:return
/ self->socklenp && execname == "tcps.pl" /
{
self->socklen = *(int *)copyin(self->socklenp, 4);
self->sockcont = (struct sockaddr_in *)copyin(self->sockaddr, self->socklen);
self->sockcont_nofamily = (struct sockaddr *)copyin(self->sockaddr, self->socklen);

self->port = self->sockcont->sin_port;
self->portB1 = self->port & 0xFF00;
self->portB1 >>= 8;
self->portB2 = self->port & 0xFF;
self->portB2 <<= 8;

self->portOk = self->portB2 | self->portB1;
self->ip = (uint_t)self->sockcont->sin_addr.s_addr;
oct4 = self->ip >> 24;
oct3 = self->ip >> 16;
oct2 = self->ip >> 8;
oct1 = self->ip;
oct1 &= 255;
oct2 &= 255;
oct3 &= 255;
oct4 &= 255;

printf("Process %s , Pid: %d got request from %d.%d.%d.%d (src Port %d)\n", execname, pid, oct1, oct2, oct3, oct4, self->portOk);

/*
printf("address family const got : %d\n", self->sockcont_nofamily->sa_family);
printf("address family const of ipv6: %d\n", AF_INET6);

printf("len %d\n", self->socklen);
printf("struct size: %d\n", sizeof( self->sockcont));
printf("port: %d ip: %d port1: %d port2: %d portall: %d\n", self->port, self->ip, self->portB1, self->portB2, self->portOk);
printf("socklen: %d \n", self->socklen);
printf("struct addrr: %x \n", self->sockaddr);
printf("struct len: %x\n", self->socklenp);
*/

}
--
This message posted from opensolaris.org
James Carlson
2010-09-24 12:44:03 UTC
Permalink
Post by goinsane
This becomes interesting. truss shows an ipv6 address for some processes, like sshd (ipv4 encapsulated in ipv6), although there is no configured ipv6 interface.
909: accept(3, 0x08047A10, 0x08047E78, SOV_DEFAULT) = 4
909: AF_INET6 name = ::ffff:172.31.70.6 port = 64625
When the probe fbt:sockfs:accept:return is fired , af_family is set to 26 (INET6) instead of 2 (INET). The return of an ipv6 address seems to be the reason for the value of 0 of (uint_t)self->sockcont->sin_addr.s_addr . I still have no glue how the struct sin_addr for ipv6 does look like, if and how it differs from the original ipv4 one.
Is there a way to find out the structs getting accessed when a probe is fired?
No. The program itself doesn't even know that. That's why sa_family
exists. You're supposed to switch out based on that value.

In a D script, you "switch out" by having multiple probes with different
conditions.
Post by goinsane
With the following D I could catch the source ip and port. You're right, I've to distinguish the endianness. So it won't work on sparc.
What predicate could be used to differentiate between little and big endian?
Do you see a simpler way of the bit operations I'm doing to get the port and ip?
There are two distinct endian issues here.

On SPARC, the network values (port numbers and addresses) will all be in
the right order, but you have to pay attention to 32/64. I've forgotten
how to do that, but I'm sure there's someone on this list who knows.

On x86, the network values will all be in reverse order, because network
data is big-endian. That means you have to swap it yourself.

I'd recommend having this clause first:

fbt:sockfs:accept:entry
{
self->socklenp = 0;
}

That way, all tests after the first one can be "/self->socklenp/" rather
than doing a string compare on execname. It should be a little better
performing.
Post by goinsane
fbt:sockfs:accept:return
/ self->socklenp && execname == "tcps.pl" /
{
self->socklen = *(int *)copyin(self->socklenp, 4);
self->sockcont = (struct sockaddr_in *)copyin(self->sockaddr, self->socklen);
That's where I think you're going wrong. You need to copy in the family
first, so something like this:

self->family = *(short *)copyin(self->sockaddr, sizeof (short));

Then just end that clause and start a new one:

fbt:sockfs:accept:return
/self->socklenp && self->family == 2/
{
/* this is the AF_INET case */

You can then copy in the full sockaddr_in and drive on.

For AF_INET6, you have to use sockaddr_in6 and sin6_addr. See the
netinet/in.h header file and the inet6(7P) man page.

D trace clauses are like 'if' statements in other languages. You can
have multiple, and they're executed in the order they appear in the file.
--
James Carlson 42.703N 71.076W <carlsonj-dlRbGz2WjHhmlEb+***@public.gmane.org>
goinsane
2010-10-26 12:24:39 UTC
Permalink
James, thank you very much for making the stuff much more clear.
--
This message posted from opensolaris.org
Loading...