This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
Re: [PATCH] - DRAFT - Easy access to IP address in struct sockaddr in syscall.bind probe (for now)]
- From: Robin Hack <rhack at redhat dot com>
- To: systemtap at sourceware dot org
- Date: Mon, 2 Dec 2013 14:40:13 +0100
- Subject: Re: [PATCH] - DRAFT - Easy access to IP address in struct sockaddr in syscall.bind probe (for now)]
- Authentication-results: sourceware.org; auth=none
- Reply-to: systemtap at sourceware dot org
Hi all.
New version of patch is released.
Please, take a look and don't hesitate
to comment.
This patch (partially?) adds support for pretty print of abstract sockets.
On this place please look at conv_buf static solution which is not
good, but I'm not able to use (on x86_64) bigger function frame than
512 bytes (-Wframe-larger-than= gcc restriction).
Also I wrote small example (now without documentation).
BTW: What would be nice is to have systemtap function which is not string oriented.
Function _stp_text_str stops on first NUL char and then does'nt
print all bytes in memory region.
Have nice day.
PS 2 Frank: No delimters are supported yet. Maybe delimiters aren't
necessary on this place.
On Sun, Nov 17, 2013 at 11:18:34AM +0100, Robin Hack wrote:
> On Wed, Nov 13, 2013 at 05:18:35PM -0500, Frank Ch. Eigler wrote:
>
> Hi Frank.
>
> Thanks again for comments. I did some changes, but comments and changes
> are still needed.
> >
> > Hi, Robin -
> >
> > rhack wrote:
> >
> > > [...] Then I decided to write this patch. Patch is only draft now
> > > and comments are very welcome.
> >
> > Nice. A few code comments:
> >
> > The use of TMP_STAP_RETVALUE / MAXLEN in the embedded-c function is a
> > little clumsy. Have you considered using ordinarily named variables
> > to track the remainder base/length of STAP_RETVALUE,
> It's fixed.
> > and doing the
> > RETVALUE_INC_SKIP dance inlined instead of as a macro?
> I apologize here, but this part is not clear enought for me.
>
> > What about delimiters between the data corresponding to multiple SA_* selections?
> I changed approach. Now I export values direct to probes.
> Delimiters will be good in tapset library.
> >
> >
> > > Very easy example are attached too.
> >
> > It might be even simpler if the related tapset functions all get this
> > kind of stuff, but perhaps plopped into a macro for easier handling:
> >
> > probe syscall.bind = ... {
> > %( systemtap_v >= "2.5" %?
> > if (_struct_sockaddr_u_sa_family(my_addr_uaddr, addrlen) =~ "AF_INET.*") {
> > uaddr_ip = _struct_sockaddr_u_ip_addr(my_addr_uaddr, addrlen)
> > uaddr_ip_port = ...
> > }
> > %)
> >
> > where the whole %( %) block could be @defined in a macro, kind of like
> > _nfs_data_timestamp in tapset/linux/nfs_proc.stpm.
> >
> >
> > > What is not solved in this patch:
> > > * Error handling if wrong struct is passed - now it's just return empty ("") string. Maybe throw exception will be better.
> I hope, that this will be part of tapset.
> > > * Expansion to syscall.connect probe.
> Done :).
> > > * Write tapset shell for new internal functions.
> In progress.
> >
> > Yup, plus a test case (e.g., a testsuite/buildok file).
> I added some tests.
> >
> > Thanks!
> >
> > - FChE
> diff --git a/tapset/linux/aux_syscalls.stp b/tapset/linux/aux_syscalls.stp
> index 0f9c9e4..1513150 100644
> --- a/tapset/linux/aux_syscalls.stp
> +++ b/tapset/linux/aux_syscalls.stp
> @@ -280,7 +280,7 @@ function _struct_compat_itimerval_u:string(uaddr:long)
> %}
>
> %{
> -// Needed for function _struct_sockaddr_u. Unfortunately cannot be
> +// Needed for function _struct_sockaddr_u_impl. Unfortunately cannot be
> // inlined into the function since these header files define static
> // functions themselves.
> #include <linux/socket.h>
> @@ -288,18 +288,75 @@ function _struct_compat_itimerval_u:string(uaddr:long)
> #include <linux/netlink.h>
> %}
>
> +%{
> +// Enum for _struct_sockaddr_u_* functions.
> +typedef enum {
> + SA_PRETTY = 1,
> + SA_IP_ADDR = 2,
> + SA_TCP_PORT = 4,
> + SA_FAMILY = 8,
> + SA_IPV6_FLOWINFO = 16,
> + SA_IPV6_SCOPE_ID = 32,
> +} sa_dispatch;
> +%}
> +
> +function _struct_sockaddr_u_ip_addr:string(uaddr:long, len:long)
> +{
> + return _struct_sockaddr_u_impl(uaddr, len, %{ SA_IP_ADDR %});
> +}
> +
> +function _struct_sockaddr_u_tcp_port:string(uaddr:long, len:long)
> +{
> + return _struct_sockaddr_u_impl(uaddr, len, %{ SA_TCP_PORT %});
> +}
> +
> +function _struct_sockaddr_u_ip_addr_tcp_port:string(uaddr:long, len:long)
> +{
> + return _struct_sockaddr_u_impl(uaddr, len, %{ SA_IP_ADDR | SA_TCP_PORT %});
> +}
> +
> +function _struct_sockaddr_u_sa_family:string(uaddr:long, len:long)
> +{
> + return _struct_sockaddr_u_impl(uaddr, len, %{ SA_FAMILY %});
> +}
> +
> +function _struct_sockaddr_u_ipv6_flowinfo:string(uaddr:long, len:long)
> +{
> + return _struct_sockaddr_u_impl(uaddr, len, %{ SA_IPV6_FLOWINFO %});
> +}
> +
> +function _struct_sockaddr_u_ipv6_scope_id:string(uaddr:long, len:long)
> +{
> + return _struct_sockaddr_u_impl(uaddr, len, %{ SA_IPV6_SCOPE_ID %});
> +}
> +
> function _struct_sockaddr_u:string(uaddr:long, len:long)
> +{
> + return _struct_sockaddr_u_impl(uaddr, len, %{ SA_PRETTY %});
> +}
> +
> +function _struct_sockaddr_u_impl:string(uaddr:long, len:long, what:long)
> %{ /* pure */
> #include <linux/version.h>
> #include <linux/in6.h>
> #include <linux/un.h>
> #include <linux/if_packet.h>
>
> + sa_dispatch what = (sa_dispatch)STAP_ARG_what;
> +
> char *ptr = (char *)(unsigned long)STAP_ARG_uaddr;
> char buf[128];
> size_t len = STAP_ARG_len < 128 ? STAP_ARG_len : 128;
> struct sockaddr *sa = (struct sockaddr *)buf;
>
> +// Don't use this macro outside of if (what & ... ) statement!
> +// (n - 1) mean: cut of null char
> +#define RETVALUE_INC_SKIP() do {stap_retvalue += (n); maxstringlen -= (n - 1);} while (0)
> + char *stap_retvalue = (char *)(unsigned long)STAP_RETVALUE;
> + int maxstringlen = MAXSTRINGLEN;
> + size_t n;
> +
> +
> if (ptr == NULL)
> {
> strlcpy (STAP_RETVALUE, "NULL", MAXSTRINGLEN);
> @@ -320,60 +377,163 @@ function _struct_sockaddr_u:string(uaddr:long, len:long)
> #define DADDR (&inet->daddr)
> #endif
>
> +
> // Use kernel builtin instead of picking up user space ntohs (function).
> #define _stp_ntohs be16_to_cpu
>
> if ((sa->sa_family == AF_INET) && (len == sizeof(struct sockaddr_in)))
> {
> struct sockaddr_in *sin = (struct sockaddr_in *)buf;
> -#ifndef NIPQUAD_FMT // kver >= 2.6.36
> - snprintf(STAP_RETVALUE, MAXSTRINGLEN, "{AF_INET, %pI4, %d}",
> - &sin->sin_addr, _stp_ntohs(sin->sin_port));
> +
> + if (what & SA_PRETTY)
> + {
> +#ifndef NIPQUAD_FMT // kver >= 2.6.36
> + snprintf(STAP_RETVALUE, MAXSTRINGLEN, "{AF_INET, %pI4, %d}",
> + &sin->sin_addr, _stp_ntohs(sin->sin_port));
> #else
> - snprintf(STAP_RETVALUE, MAXSTRINGLEN,
> - "{AF_INET, " NIPQUAD_FMT ", %d}",
> - NIPQUAD(sin->sin_addr), _stp_ntohs(sin->sin_port));
> -#endif
> + snprintf(STAP_RETVALUE, MAXSTRINGLEN,
> + "{AF_INET, " NIPQUAD_FMT ", %d}",
> + NIPQUAD(sin->sin_addr), _stp_ntohs(sin->sin_port));
> +#endif
> + return;
> + }
> +
> + if (what & SA_FAMILY)
> + {
> + n = strlcpy(stap_retvalue, "AF_INET", maxstringlen);
> + RETVALUE_INC_SKIP();
> + }
> +
> + if (what & SA_IP_ADDR)
> + {
> +#ifndef NIPQUAD_FMT // kver >= 2.6.36
> + n = snprintf(stap_retvalue, maxstringlen, "%pI4", &sin->sin_addr);
> +#else
> + n = snprintf(stap_retvalue, maxstringlen, NIPQUAD_FMT,
> + NIPQUAD(sin->sin_addr));
> +#endif
> + RETVALUE_INC_SKIP();
> + }
> +
> + if (what & SA_TCP_PORT)
> + {
> + n = snprintf(stap_retvalue, maxstringlen, "%d",
> + _stp_ntohs(sin->sin_port));
> + RETVALUE_INC_SKIP();
> + }
> }
> else if ((sa->sa_family == AF_UNIX)
> && (len == sizeof(struct sockaddr_un)))
> - {
> - struct sockaddr_un *sun = (struct sockaddr_un *)buf;
> - snprintf(STAP_RETVALUE, MAXSTRINGLEN, "{AF_UNIX, %s}",
> - sun->sun_path);
> + {
> + struct sockaddr_un *sun = (struct sockaddr_un *)buf;
> +
> + if (what & SA_PRETTY)
> + {
> + snprintf(STAP_RETVALUE, MAXSTRINGLEN, "{AF_UNIX, %s}",
> + sun->sun_path);
> + } else if (what & SA_FAMILY)
> + {
> + strlcpy(STAP_RETVALUE, "AF_UNIX", MAXSTRINGLEN);
> + } else
> + {
> + strlcpy(STAP_RETVALUE, "", MAXSTRINGLEN);
> + }
> }
> else if ((sa->sa_family == AF_NETLINK)
> && (len == sizeof(struct sockaddr_nl)))
> {
> struct sockaddr_nl *nl = (struct sockaddr_nl *)buf;
> - snprintf(STAP_RETVALUE, MAXSTRINGLEN,
> - "{AF_NETLINK, pid=%d, groups=%08x}",
> - nl->nl_pid, nl->nl_groups);
> +
> + if (what & SA_PRETTY) {
> + snprintf(STAP_RETVALUE, MAXSTRINGLEN,
> + "{AF_NETLINK, pid=%d, groups=%08x}",
> + nl->nl_pid, nl->nl_groups);
> + } else if (what & SA_FAMILY)
> + {
> + strlcpy(STAP_RETVALUE, "AF_NETLINK", MAXSTRINGLEN);
> + } else
> + {
> + strlcpy(STAP_RETVALUE, "", MAXSTRINGLEN);
> + }
> }
> else if ((sa->sa_family == AF_INET6)
> && (len == sizeof(struct sockaddr_in6)))
> {
> struct sockaddr_in6 *sin = (struct sockaddr_in6 *)buf;
> +
> + if (what & SA_PRETTY)
> + {
> #ifndef NIP6_FMT // kver >= 2.6.36
> - snprintf(STAP_RETVALUE, MAXSTRINGLEN,
> - "{AF_INET6, %pI6, %d}", &sin->sin6_addr,
> - _stp_ntohs(sin->sin6_port));
> + snprintf(STAP_RETVALUE, MAXSTRINGLEN,
> + "{AF_INET6, %pI6, %d}", &sin->sin6_addr,
> + _stp_ntohs(sin->sin6_port));
> #else
> - snprintf(STAP_RETVALUE, MAXSTRINGLEN,
> - "{AF_INET6, " NIP6_FMT ", %d}", NIP6(sin->sin6_addr),
> - _stp_ntohs(sin->sin6_port));
> + snprintf(STAP_RETVALUE, MAXSTRINGLEN,
> + "{AF_INET6, " NIP6_FMT ", %d}", NIP6(sin->sin6_addr),
> + _stp_ntohs(sin->sin6_port));
> #endif
> + return;
> + }
> +
> + if (what & SA_FAMILY)
> + {
> + n = strlcpy(stap_retvalue, "AF_INET6", maxstringlen);
> + RETVALUE_INC_SKIP();
> + }
> +
> + if (what & SA_IP_ADDR)
> + {
> +#ifndef NIP6_FMT // kver >= 2.6.36
> + n = snprintf(stap_retvalue, maxstringlen,
> + "%pI6", &sin->sin6_addr);
> +#else
> + n = snprintf(stap_retvalue, maxstringlen,
> + NIP6_FMT, NIP6(sin->sin6_addr));
> +#endif
> + RETVALUE_INC_SKIP();
> + }
> +
> + if (what & SA_TCP_PORT)
> + {
> + n = snprintf(stap_retvalue, maxstringlen,
> + "%d", _stp_ntohs(sin->sin6_port));
> + RETVALUE_INC_SKIP();
> + }
> +
> + if (what & SA_IPV6_FLOWINFO)
> + {
> + n = snprintf(stap_retvalue, maxstringlen,
> + "%d", sin->sin6_flowinfo);
> + RETVALUE_INC_SKIP();
> + }
> +
> + if (what & SA_IPV6_SCOPE_ID)
> + {
> + n = snprintf(stap_retvalue, maxstringlen,
> + "%d", sin->sin6_flowinfo);
> + RETVALUE_INC_SKIP();
> + }
> }
> else if ((sa->sa_family == AF_PACKET)
> - && (len == sizeof(struct sockaddr_ll)))
> + && (len == sizeof(struct sockaddr_ll)))
> {
> struct sockaddr_ll *sll = (struct sockaddr_ll *)buf;
> - snprintf(STAP_RETVALUE, MAXSTRINGLEN,
> - "{AF_PACKET, proto=%d, ind=%d, hatype=%d, pkttype=%d, halen=%d, addr=0x%llx}",
> - (int)sll->sll_protocol, sll->sll_ifindex,
> - (int)sll->sll_hatype, (int)sll->sll_pkttype,
> - (int)sll->sll_halen,
> - (long long)(*(uint64_t *)sll->sll_addr));
> +
> + if (what & SA_PRETTY)
> + {
> + snprintf(STAP_RETVALUE, MAXSTRINGLEN,
> + "{AF_PACKET, proto=%d, ind=%d, hatype=%d, pkttype=%d, halen=%d, addr=0x%llx}",
> + (int)sll->sll_protocol, sll->sll_ifindex,
> + (int)sll->sll_hatype, (int)sll->sll_pkttype,
> + (int)sll->sll_halen,
> + (long long)(*(uint64_t *)sll->sll_addr));
> + } else if (what & SA_FAMILY)
> + {
> + strlcpy(STAP_RETVALUE, "AF_PACKET", MAXSTRINGLEN);
> + } else
> + {
> + strlcpy(STAP_RETVALUE, "", MAXSTRINGLEN);
> + }
> }
> else
> {
> @@ -389,6 +549,8 @@ function _struct_sockaddr_u:string(uaddr:long, len:long)
> "{unknown sockaddr with salen=%d}", (int)len);
> }
> }
> +
> +#undef RETVALUE_INC_SKIP
> %}
>
> function _struct_rlimit_u:string(uaddr:long)
> diff --git a/tapset/linux/nd_syscalls.stp b/tapset/linux/nd_syscalls.stp
> index f2daa01..5121751 100644
> --- a/tapset/linux/nd_syscalls.stp
> +++ b/tapset/linux/nd_syscalls.stp
> @@ -247,6 +247,7 @@ probe nd_syscall.bind = kprobe.function("sys_bind") ?
> sockfd = int_arg(1)
> my_addr_uaddr = pointer_arg(2)
> addrlen = int_arg(3)
> + @_af_inet_info_u(my_addr_uaddr, addrlen)
> argstr = sprintf("%d, %s, %d", sockfd, _struct_sockaddr_u(my_addr_uaddr, addrlen), addrlen)
> }
> probe nd_syscall.bind.return = kprobe.function("sys_bind").return ?
> @@ -586,6 +587,7 @@ probe nd_syscall.connect = kprobe.function("sys_connect") ?
> sockfd = int_arg(1)
> serv_addr_uaddr = pointer_arg(2)
> addrlen = int_arg(3)
> + @_af_inet_info_u(serv_addr_uaddr, addrlen)
> argstr = sprintf("%d, %s, %d", sockfd, _struct_sockaddr_u(serv_addr_uaddr, addrlen), addrlen)
> }
> probe nd_syscall.connect.return = kprobe.function("sys_connect").return ?
> diff --git a/tapset/linux/syscalls.stp b/tapset/linux/syscalls.stp
> index 13fb92f..d8e1d05 100644
> --- a/tapset/linux/syscalls.stp
> +++ b/tapset/linux/syscalls.stp
> @@ -199,6 +199,7 @@ probe syscall.bind = kernel.function("sys_bind").call ?
> sockfd = $fd
> my_addr_uaddr = $umyaddr
> addrlen = $addrlen
> + @_af_inet_info_u(my_addr_uaddr, addrlen)
> argstr = sprintf("%d, %s, %d", $fd, _struct_sockaddr_u($umyaddr, $addrlen), $addrlen)
> }
> probe syscall.bind.return = kernel.function("sys_bind").return ?
> @@ -472,6 +473,7 @@ probe syscall.connect = kernel.function("sys_connect").call ?
> sockfd = $fd
> serv_addr_uaddr = $uservaddr
> addrlen = $addrlen
> + @_af_inet_info_u(serv_addr_uaddr, addrlen)
> argstr = sprintf("%d, %s, %d", $fd, _struct_sockaddr_u($uservaddr, $addrlen), $addrlen)
> }
> probe syscall.connect.return = kernel.function("sys_connect").return ?
> diff --git a/tapset/linux/syscalls.stpm b/tapset/linux/syscalls.stpm
> new file mode 100644
> index 0000000..0ab18ab
> --- /dev/null
> +++ b/tapset/linux/syscalls.stpm
> @@ -0,0 +1,24 @@
> +// Macros for syscalls.stp and nd_syscalls.stp
> +// Copyright (C) 2013 Red Hat
> +//
> +// Author : Robin Hack <rhack@redhat.com>
> +//
> +// This file is part of systemtap, and is free software. You can
> +// redistribute it and/or modify it under the terms of the GNU General
> +// Public License (GPL); either version 2, or (at your option) any
> +// later version.
> +
> +@define _af_inet_info_u(my_addr_uaddr, addrlen)
> +%(
> + %( systemtap_v >= "2.5" %?
> + uaddr_af = _struct_sockaddr_u_sa_family(@my_addr_uaddr, @addrlen)
> + if (_struct_sockaddr_u_sa_family(@my_addr_uaddr, @addrlen) =~ "AF_INET.*") {
> + uaddr_ip = _struct_sockaddr_u_ip_addr(@my_addr_uaddr, @addrlen)
> + uaddr_ip_port = _struct_sockaddr_u_tcp_port(@my_addr_uaddr, @addrlen)
> + if (_struct_sockaddr_u_sa_family(@my_addr_uaddr, @addrlen) == "AF_INET6") {
> + uaddr_ipv6_flowinfo = _struct_sockaddr_u_ipv6_flowinfo(@my_addr_uaddr, @addrlen)
> + uaddr_ipv6_scope_id = _struct_sockaddr_u_ipv6_scope_id(@my_addr_uaddr, @addrlen)
> + }
> + }
> + %)
> +%)
> diff --git a/testsuite/buildok/aux_syscalls-embedded.stp b/testsuite/buildok/aux_syscalls-embedded.stp
> index 1fb5114..51b02c1 100755
> --- a/testsuite/buildok/aux_syscalls-embedded.stp
> +++ b/testsuite/buildok/aux_syscalls-embedded.stp
> @@ -14,6 +14,14 @@ probe begin {
> print (_struct_itimerval_u(0))
> print (_struct_compat_itimerval_u(0))
> print (_struct_sockaddr_u(0,0))
> +%( systemtap_v >= "2.5" %?
> + print (_struct_sockaddr_u_ip_addr(0,0))
> + print (_struct_sockaddr_u_tcp_port(0,0))
> + print (_struct_sockaddr_u_sa_family(0,0))
> + print (_struct_sockaddr_u_ipv6_flowinfo(0,0))
> + print (_struct_sockaddr_u_ipv6_scope_id(0,0))
> + print (_struct_sockaddr_u_impl(0,0,0))
> +%)
> print (_struct_rlimit_u(0))
> print (_fildes_index_u(0, 0))
>
> diff --git a/testsuite/buildok/nd_syscalls-detailed.stp b/testsuite/buildok/nd_syscalls-detailed.stp
> index 8152f8d..4c9e0ed 100755
> --- a/testsuite/buildok/nd_syscalls-detailed.stp
> +++ b/testsuite/buildok/nd_syscalls-detailed.stp
> @@ -74,8 +74,13 @@ probe nd_syscall.bdflush.return ?
> probe nd_syscall.bind
> {
> printf("%s, %s\n", name, argstr)
> +%( systemtap_v >= "2.5" %?
> + printf("%d, %p, %d, %s\n", sockfd, my_addr_uaddr, addrlen, uaddr_af)
> +%:
> printf("%d, %p, %d\n", sockfd, my_addr_uaddr, addrlen)
> +%)
> }
> +
> probe nd_syscall.bind.return
> {
> printf("%s, %s\n", name, retstr)
> @@ -194,7 +199,11 @@ probe nd_syscall.close.return
> probe nd_syscall.connect
> {
> printf("%s, %s\n", name, argstr)
> +%( systemtap_v >= "2.5" %?
> + printf("%d, %p, %d, %s\n", sockfd, serv_addr_uaddr, addrlen, uaddr_af)
> +%:
> printf("%d, %p, %d\n", sockfd, serv_addr_uaddr, addrlen)
> +%)
> }
> probe nd_syscall.connect.return
> {
> diff --git a/testsuite/buildok/syscalls-detailed.stp b/testsuite/buildok/syscalls-detailed.stp
> index e1c626c..17544f7 100755
> --- a/testsuite/buildok/syscalls-detailed.stp
> +++ b/testsuite/buildok/syscalls-detailed.stp
> @@ -74,7 +74,12 @@ probe syscall.bdflush.return ?
> probe syscall.bind
> {
> printf("%s, %s\n", name, argstr)
> +
> +%( systemtap_v >= "2.5" %?
> + printf("%d, %p, %d, %s\n", sockfd, my_addr_uaddr, addrlen, uaddr_af)
> +%:
> printf("%d, %p, %d\n", sockfd, my_addr_uaddr, addrlen)
> +%)
> }
> probe syscall.bind.return
> {
> @@ -194,7 +199,11 @@ probe syscall.close.return
> probe syscall.connect
> {
> printf("%s, %s\n", name, argstr)
> +%( systemtap_v >= "2.5" %?
> + printf("%d, %p, %d, %s\n", sockfd, serv_addr_uaddr, addrlen, uaddr_af)
> +%:
> printf("%d, %p, %d\n", sockfd, serv_addr_uaddr, addrlen)
> +%)
> }
> probe syscall.connect.return
> {
diff --git a/tapset/linux/aux_syscalls.stp b/tapset/linux/aux_syscalls.stp
index 0f9c9e4..7b02628 100644
--- a/tapset/linux/aux_syscalls.stp
+++ b/tapset/linux/aux_syscalls.stp
@@ -280,7 +280,7 @@ function _struct_compat_itimerval_u:string(uaddr:long)
%}
%{
-// Needed for function _struct_sockaddr_u. Unfortunately cannot be
+// Needed for function _struct_sockaddr_u_impl. Unfortunately cannot be
// inlined into the function since these header files define static
// functions themselves.
#include <linux/socket.h>
@@ -288,27 +288,87 @@ function _struct_compat_itimerval_u:string(uaddr:long)
#include <linux/netlink.h>
%}
+%{
+// Enum for _struct_sockaddr_u_* functions.
+typedef enum {
+ SA_PRETTY = 1,
+ SA_IP_ADDR = 2,
+ SA_TCP_PORT = 4,
+ SA_FAMILY = 8,
+ SA_IPV6_FLOWINFO = 16,
+ SA_IPV6_SCOPE_ID = 32,
+} sa_dispatch;
+%}
+
+function _struct_sockaddr_u_ip_addr:string(uaddr:long, len:long)
+{
+ return _struct_sockaddr_u_impl(uaddr, len, %{ SA_IP_ADDR %});
+}
+
+function _struct_sockaddr_u_tcp_port:string(uaddr:long, len:long)
+{
+ return _struct_sockaddr_u_impl(uaddr, len, %{ SA_TCP_PORT %});
+}
+
+function _struct_sockaddr_u_ip_addr_tcp_port:string(uaddr:long, len:long)
+{
+ return _struct_sockaddr_u_impl(uaddr, len, %{ SA_IP_ADDR | SA_TCP_PORT %});
+}
+
+function _struct_sockaddr_u_sa_family:string(uaddr:long, len:long)
+{
+ return _struct_sockaddr_u_impl(uaddr, len, %{ SA_FAMILY %});
+}
+
+function _struct_sockaddr_u_ipv6_flowinfo:string(uaddr:long, len:long)
+{
+ return _struct_sockaddr_u_impl(uaddr, len, %{ SA_IPV6_FLOWINFO %});
+}
+
+function _struct_sockaddr_u_ipv6_scope_id:string(uaddr:long, len:long)
+{
+ return _struct_sockaddr_u_impl(uaddr, len, %{ SA_IPV6_SCOPE_ID %});
+}
+
function _struct_sockaddr_u:string(uaddr:long, len:long)
+{
+ return _struct_sockaddr_u_impl(uaddr, len, %{ SA_PRETTY %});
+}
+
+function _struct_sockaddr_u_impl:string(uaddr:long, len:long, what:long)
%{ /* pure */
#include <linux/version.h>
#include <linux/in6.h>
#include <linux/un.h>
#include <linux/if_packet.h>
- char *ptr = (char *)(unsigned long)STAP_ARG_uaddr;
- char buf[128];
- size_t len = STAP_ARG_len < 128 ? STAP_ARG_len : 128;
- struct sockaddr *sa = (struct sockaddr *)buf;
+ sa_dispatch what = (sa_dispatch)STAP_ARG_what;
+
+ char *ptr = (char *)(unsigned long)STAP_ARG_uaddr;
+ // This helps handle variable lenght sockaddr_un.
+ // Some application - like systemd - sends path string
+ // without ending null character. Kernel will handle this.
+ char buf[128] = {'\0'};
+ size_t len = STAP_ARG_len < 128 ? STAP_ARG_len: 128;
+ struct sockaddr *sa = (struct sockaddr *)buf;
+
+ char *stap_retvalue = (char *)(unsigned long)STAP_RETVALUE;
+ int maxstringlen = MAXSTRINGLEN;
+ size_t n;
+
+ // If this will not be static, then gcc will be unhappy.
+ // There is limit to function frame size.
+ static char conv_buf[MAXSTRINGLEN];
if (ptr == NULL)
{
- strlcpy (STAP_RETVALUE, "NULL", MAXSTRINGLEN);
+ strlcpy(STAP_RETVALUE, "NULL", MAXSTRINGLEN);
return;
}
if (_stp_copy_from_user(buf, ptr, len))
{
- strlcpy (STAP_RETVALUE, "[...]", MAXSTRINGLEN);
+ strlcpy(STAP_RETVALUE, "[...]", MAXSTRINGLEN);
return;
}
@@ -326,54 +386,195 @@ function _struct_sockaddr_u:string(uaddr:long, len:long)
if ((sa->sa_family == AF_INET) && (len == sizeof(struct sockaddr_in)))
{
struct sockaddr_in *sin = (struct sockaddr_in *)buf;
-#ifndef NIPQUAD_FMT // kver >= 2.6.36
- snprintf(STAP_RETVALUE, MAXSTRINGLEN, "{AF_INET, %pI4, %d}",
- &sin->sin_addr, _stp_ntohs(sin->sin_port));
+
+ if (what & SA_PRETTY)
+ {
+#ifndef NIPQUAD_FMT // kver >= 2.6.36
+ snprintf(STAP_RETVALUE, MAXSTRINGLEN, "{AF_INET, %pI4, %d}",
+ &sin->sin_addr, _stp_ntohs(sin->sin_port));
#else
- snprintf(STAP_RETVALUE, MAXSTRINGLEN,
- "{AF_INET, " NIPQUAD_FMT ", %d}",
- NIPQUAD(sin->sin_addr), _stp_ntohs(sin->sin_port));
-#endif
+ snprintf(STAP_RETVALUE, MAXSTRINGLEN,
+ "{AF_INET, " NIPQUAD_FMT ", %d}",
+ NIPQUAD(sin->sin_addr), _stp_ntohs(sin->sin_port));
+#endif
+ return;
+ }
+
+ if (what & SA_FAMILY)
+ {
+ n = strlcpy(stap_retvalue, "AF_INET", maxstringlen);
+ // (n - 1) mean: cut of null char
+ stap_retvalue += (n);
+ maxstringlen -= (n - 1);
+ }
+
+ if (what & SA_IP_ADDR)
+ {
+#ifndef NIPQUAD_FMT // kver >= 2.6.36
+ n = snprintf(stap_retvalue, maxstringlen, "%pI4", &sin->sin_addr);
+#else
+ n = snprintf(stap_retvalue, maxstringlen, NIPQUAD_FMT,
+ NIPQUAD(sin->sin_addr));
+#endif
+ // (n - 1) mean: cut of null char
+ stap_retvalue += (n);
+ maxstringlen -= (n - 1);
+ }
+
+ if (what & SA_TCP_PORT)
+ {
+ n = snprintf(stap_retvalue, maxstringlen, "%d",
+ _stp_ntohs(sin->sin_port));
+ // (n - 1) mean: cut of null char
+ stap_retvalue += (n);
+ maxstringlen -= (n - 1);
+ }
}
+ // Why 2 * sizeof (char) here?
+ // Because I want to support abstract sockets with
+ // at least one usable byte after initial \0 char.
+ // Unnamed sockets aren't supported yet.
else if ((sa->sa_family == AF_UNIX)
- && (len == sizeof(struct sockaddr_un)))
- {
- struct sockaddr_un *sun = (struct sockaddr_un *)buf;
- snprintf(STAP_RETVALUE, MAXSTRINGLEN, "{AF_UNIX, %s}",
- sun->sun_path);
+ && ((len == sizeof(struct sockaddr_un))
+ || (len >= ((sizeof(sa_family_t)) + (2 * sizeof(char))))))
+ {
+ struct sockaddr_un *sun = (struct sockaddr_un *)buf;
+ if (what & SA_PRETTY)
+ {
+
+ // Support for abstract sockets
+ if (sun->sun_path[0] == '\0')
+ {
+ // Abstract sockets aren't string oriented.
+ // We need conversion on this place.
+ // No check of ret value, because _stp_text_str returns
+ // "<unknown>" if bad things happen.
+ // Well. There can be NUL chars inside sun_path.
+ // We just stop at first NUL char.
+ _stp_text_str(conv_buf, &sun->sun_path[1],
+ len - sizeof(sa_family_t), MAXSTRINGLEN - 1, 0, 0);
+ snprintf(STAP_RETVALUE, MAXSTRINGLEN, "{AF_UNIX, \\000%s}",
+ conv_buf);
+ } else
+ {
+ // Just cut path if is too long
+ buf[127] = '\0';
+ snprintf(STAP_RETVALUE, MAXSTRINGLEN, "{AF_UNIX, %s}",
+ sun->sun_path);
+ }
+ } else if (what & SA_FAMILY)
+ {
+ strlcpy(STAP_RETVALUE, "AF_UNIX", MAXSTRINGLEN);
+ } else
+ {
+ strlcpy(STAP_RETVALUE, "", MAXSTRINGLEN);
+ }
}
else if ((sa->sa_family == AF_NETLINK)
&& (len == sizeof(struct sockaddr_nl)))
{
struct sockaddr_nl *nl = (struct sockaddr_nl *)buf;
- snprintf(STAP_RETVALUE, MAXSTRINGLEN,
- "{AF_NETLINK, pid=%d, groups=%08x}",
- nl->nl_pid, nl->nl_groups);
- }
+
+ if (what & SA_PRETTY) {
+ snprintf(STAP_RETVALUE, MAXSTRINGLEN,
+ "{AF_NETLINK, pid=%d, groups=%08x}",
+ nl->nl_pid, nl->nl_groups);
+ } else if (what & SA_FAMILY)
+ {
+ strlcpy(STAP_RETVALUE, "AF_NETLINK", MAXSTRINGLEN);
+ } else
+ {
+ strlcpy(STAP_RETVALUE, "", MAXSTRINGLEN);
+ }
+ }
else if ((sa->sa_family == AF_INET6)
&& (len == sizeof(struct sockaddr_in6)))
{
struct sockaddr_in6 *sin = (struct sockaddr_in6 *)buf;
+
+ if (what & SA_PRETTY)
+ {
#ifndef NIP6_FMT // kver >= 2.6.36
- snprintf(STAP_RETVALUE, MAXSTRINGLEN,
- "{AF_INET6, %pI6, %d}", &sin->sin6_addr,
- _stp_ntohs(sin->sin6_port));
+ snprintf(STAP_RETVALUE, MAXSTRINGLEN,
+ "{AF_INET6, %pI6, %d}", &sin->sin6_addr,
+ _stp_ntohs(sin->sin6_port));
#else
- snprintf(STAP_RETVALUE, MAXSTRINGLEN,
- "{AF_INET6, " NIP6_FMT ", %d}", NIP6(sin->sin6_addr),
- _stp_ntohs(sin->sin6_port));
-#endif
+ snprintf(STAP_RETVALUE, MAXSTRINGLEN,
+ "{AF_INET6, " NIP6_FMT ", %d}", NIP6(sin->sin6_addr),
+ _stp_ntohs(sin->sin6_port));
+#endif
+ return;
+ }
+
+ if (what & SA_FAMILY)
+ {
+ n = strlcpy(stap_retvalue, "AF_INET6", maxstringlen);
+ // (n - 1) mean: cut of null char
+ stap_retvalue += (n);
+ maxstringlen -= (n - 1);
+ }
+
+ if (what & SA_IP_ADDR)
+ {
+#ifndef NIP6_FMT // kver >= 2.6.36
+ n = snprintf(stap_retvalue, maxstringlen,
+ "%pI6", &sin->sin6_addr);
+#else
+ n = snprintf(stap_retvalue, maxstringlen,
+ NIP6_FMT, NIP6(sin->sin6_addr));
+#endif
+ // (n - 1) mean: cut of null char
+ stap_retvalue += (n);
+ maxstringlen -= (n - 1);
+ }
+
+ if (what & SA_TCP_PORT)
+ {
+ n = snprintf(stap_retvalue, maxstringlen,
+ "%d", _stp_ntohs(sin->sin6_port));
+ // (n - 1) mean: cut of null char
+ stap_retvalue += (n);
+ maxstringlen -= (n - 1);
+ }
+
+ if (what & SA_IPV6_FLOWINFO)
+ {
+ n = snprintf(stap_retvalue, maxstringlen,
+ "%d", sin->sin6_flowinfo);
+ // (n - 1) mean: cut of null char
+ stap_retvalue += (n);
+ maxstringlen -= (n - 1);
+ }
+
+ if (what & SA_IPV6_SCOPE_ID)
+ {
+ n = snprintf(stap_retvalue, maxstringlen,
+ "%d", sin->sin6_flowinfo);
+ // (n - 1) mean: cut of null char
+ stap_retvalue += (n);
+ maxstringlen -= (n - 1);
+ }
}
else if ((sa->sa_family == AF_PACKET)
- && (len == sizeof(struct sockaddr_ll)))
+ && (len == sizeof(struct sockaddr_ll)))
{
struct sockaddr_ll *sll = (struct sockaddr_ll *)buf;
- snprintf(STAP_RETVALUE, MAXSTRINGLEN,
- "{AF_PACKET, proto=%d, ind=%d, hatype=%d, pkttype=%d, halen=%d, addr=0x%llx}",
- (int)sll->sll_protocol, sll->sll_ifindex,
- (int)sll->sll_hatype, (int)sll->sll_pkttype,
- (int)sll->sll_halen,
- (long long)(*(uint64_t *)sll->sll_addr));
+
+ if (what & SA_PRETTY)
+ {
+ snprintf(STAP_RETVALUE, MAXSTRINGLEN,
+ "{AF_PACKET, proto=%d, ind=%d, hatype=%d, pkttype=%d, halen=%d, addr=0x%llx}",
+ (int)sll->sll_protocol, sll->sll_ifindex,
+ (int)sll->sll_hatype, (int)sll->sll_pkttype,
+ (int)sll->sll_halen,
+ (long long)(*(uint64_t *)sll->sll_addr));
+ } else if (what & SA_FAMILY)
+ {
+ strlcpy(STAP_RETVALUE, "AF_PACKET", MAXSTRINGLEN);
+ } else
+ {
+ strlcpy(STAP_RETVALUE, "", MAXSTRINGLEN);
+ }
}
else
{
diff --git a/tapset/linux/nd_syscalls.stp b/tapset/linux/nd_syscalls.stp
index f2daa01..5121751 100644
--- a/tapset/linux/nd_syscalls.stp
+++ b/tapset/linux/nd_syscalls.stp
@@ -247,6 +247,7 @@ probe nd_syscall.bind = kprobe.function("sys_bind") ?
sockfd = int_arg(1)
my_addr_uaddr = pointer_arg(2)
addrlen = int_arg(3)
+ @_af_inet_info_u(my_addr_uaddr, addrlen)
argstr = sprintf("%d, %s, %d", sockfd, _struct_sockaddr_u(my_addr_uaddr, addrlen), addrlen)
}
probe nd_syscall.bind.return = kprobe.function("sys_bind").return ?
@@ -586,6 +587,7 @@ probe nd_syscall.connect = kprobe.function("sys_connect") ?
sockfd = int_arg(1)
serv_addr_uaddr = pointer_arg(2)
addrlen = int_arg(3)
+ @_af_inet_info_u(serv_addr_uaddr, addrlen)
argstr = sprintf("%d, %s, %d", sockfd, _struct_sockaddr_u(serv_addr_uaddr, addrlen), addrlen)
}
probe nd_syscall.connect.return = kprobe.function("sys_connect").return ?
diff --git a/tapset/linux/syscalls.stp b/tapset/linux/syscalls.stp
index 13fb92f..d8e1d05 100644
--- a/tapset/linux/syscalls.stp
+++ b/tapset/linux/syscalls.stp
@@ -199,6 +199,7 @@ probe syscall.bind = kernel.function("sys_bind").call ?
sockfd = $fd
my_addr_uaddr = $umyaddr
addrlen = $addrlen
+ @_af_inet_info_u(my_addr_uaddr, addrlen)
argstr = sprintf("%d, %s, %d", $fd, _struct_sockaddr_u($umyaddr, $addrlen), $addrlen)
}
probe syscall.bind.return = kernel.function("sys_bind").return ?
@@ -472,6 +473,7 @@ probe syscall.connect = kernel.function("sys_connect").call ?
sockfd = $fd
serv_addr_uaddr = $uservaddr
addrlen = $addrlen
+ @_af_inet_info_u(serv_addr_uaddr, addrlen)
argstr = sprintf("%d, %s, %d", $fd, _struct_sockaddr_u($uservaddr, $addrlen), $addrlen)
}
probe syscall.connect.return = kernel.function("sys_connect").return ?
diff --git a/tapset/linux/syscalls.stpm b/tapset/linux/syscalls.stpm
new file mode 100644
index 0000000..0ab18ab
--- /dev/null
+++ b/tapset/linux/syscalls.stpm
@@ -0,0 +1,24 @@
+// Macros for syscalls.stp and nd_syscalls.stp
+// Copyright (C) 2013 Red Hat
+//
+// Author : Robin Hack <rhack@redhat.com>
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+@define _af_inet_info_u(my_addr_uaddr, addrlen)
+%(
+ %( systemtap_v >= "2.5" %?
+ uaddr_af = _struct_sockaddr_u_sa_family(@my_addr_uaddr, @addrlen)
+ if (_struct_sockaddr_u_sa_family(@my_addr_uaddr, @addrlen) =~ "AF_INET.*") {
+ uaddr_ip = _struct_sockaddr_u_ip_addr(@my_addr_uaddr, @addrlen)
+ uaddr_ip_port = _struct_sockaddr_u_tcp_port(@my_addr_uaddr, @addrlen)
+ if (_struct_sockaddr_u_sa_family(@my_addr_uaddr, @addrlen) == "AF_INET6") {
+ uaddr_ipv6_flowinfo = _struct_sockaddr_u_ipv6_flowinfo(@my_addr_uaddr, @addrlen)
+ uaddr_ipv6_scope_id = _struct_sockaddr_u_ipv6_scope_id(@my_addr_uaddr, @addrlen)
+ }
+ }
+ %)
+%)
diff --git a/testsuite/buildok/aux_syscalls-embedded.stp b/testsuite/buildok/aux_syscalls-embedded.stp
index 1fb5114..51b02c1 100755
--- a/testsuite/buildok/aux_syscalls-embedded.stp
+++ b/testsuite/buildok/aux_syscalls-embedded.stp
@@ -14,6 +14,14 @@ probe begin {
print (_struct_itimerval_u(0))
print (_struct_compat_itimerval_u(0))
print (_struct_sockaddr_u(0,0))
+%( systemtap_v >= "2.5" %?
+ print (_struct_sockaddr_u_ip_addr(0,0))
+ print (_struct_sockaddr_u_tcp_port(0,0))
+ print (_struct_sockaddr_u_sa_family(0,0))
+ print (_struct_sockaddr_u_ipv6_flowinfo(0,0))
+ print (_struct_sockaddr_u_ipv6_scope_id(0,0))
+ print (_struct_sockaddr_u_impl(0,0,0))
+%)
print (_struct_rlimit_u(0))
print (_fildes_index_u(0, 0))
diff --git a/testsuite/buildok/nd_syscalls-detailed.stp b/testsuite/buildok/nd_syscalls-detailed.stp
index 8152f8d..4c9e0ed 100755
--- a/testsuite/buildok/nd_syscalls-detailed.stp
+++ b/testsuite/buildok/nd_syscalls-detailed.stp
@@ -74,8 +74,13 @@ probe nd_syscall.bdflush.return ?
probe nd_syscall.bind
{
printf("%s, %s\n", name, argstr)
+%( systemtap_v >= "2.5" %?
+ printf("%d, %p, %d, %s\n", sockfd, my_addr_uaddr, addrlen, uaddr_af)
+%:
printf("%d, %p, %d\n", sockfd, my_addr_uaddr, addrlen)
+%)
}
+
probe nd_syscall.bind.return
{
printf("%s, %s\n", name, retstr)
@@ -194,7 +199,11 @@ probe nd_syscall.close.return
probe nd_syscall.connect
{
printf("%s, %s\n", name, argstr)
+%( systemtap_v >= "2.5" %?
+ printf("%d, %p, %d, %s\n", sockfd, serv_addr_uaddr, addrlen, uaddr_af)
+%:
printf("%d, %p, %d\n", sockfd, serv_addr_uaddr, addrlen)
+%)
}
probe nd_syscall.connect.return
{
diff --git a/testsuite/buildok/syscalls-detailed.stp b/testsuite/buildok/syscalls-detailed.stp
index e1c626c..17544f7 100755
--- a/testsuite/buildok/syscalls-detailed.stp
+++ b/testsuite/buildok/syscalls-detailed.stp
@@ -74,7 +74,12 @@ probe syscall.bdflush.return ?
probe syscall.bind
{
printf("%s, %s\n", name, argstr)
+
+%( systemtap_v >= "2.5" %?
+ printf("%d, %p, %d, %s\n", sockfd, my_addr_uaddr, addrlen, uaddr_af)
+%:
printf("%d, %p, %d\n", sockfd, my_addr_uaddr, addrlen)
+%)
}
probe syscall.bind.return
{
@@ -194,7 +199,11 @@ probe syscall.close.return
probe syscall.connect
{
printf("%s, %s\n", name, argstr)
+%( systemtap_v >= "2.5" %?
+ printf("%d, %p, %d, %s\n", sockfd, serv_addr_uaddr, addrlen, uaddr_af)
+%:
printf("%d, %p, %d\n", sockfd, serv_addr_uaddr, addrlen)
+%)
}
probe syscall.connect.return
{
diff --git a/testsuite/systemtap.examples/network/connect_stat.stp b/testsuite/systemtap.examples/network/connect_stat.stp
new file mode 100644
index 0000000..e0b59d0
--- /dev/null
+++ b/testsuite/systemtap.examples/network/connect_stat.stp
@@ -0,0 +1,31 @@
+#! /usr/bin/env stap
+
+############################################################
+# connect_stat.stp
+# Author: Robin Hack <rhack@redhat.com>
+# An example script show process tree of process
+# which tried to call connect with specific ip address
+############################################################
+
+function process_tree (ip:string) {
+ cur_proc = task_current();
+ parent_pid = task_pid(task_parent (cur_proc));
+
+ printf ("%s: ", ip);
+ while (parent_pid != 0) {
+ printf ("%s (%d),%d,%d -> ", task_execname(cur_proc), task_pid(cur_proc), task_uid(cur_proc),task_gid (cur_proc));
+ cur_proc = task_parent(cur_proc);
+ parent_pid = task_pid(task_parent (cur_proc));
+ }
+ # init process
+ if (task_pid (cur_proc) == 1) {
+ printf ("%s (%d),%d,%d\n", task_execname(cur_proc), task_pid(cur_proc), task_uid(cur_proc),task_gid (cur_proc));
+ }
+}
+
+probe syscall.connect {
+ if ((uaddr_af !~ "AF_INET*") || (uaddr_ip != @1)) {
+ next;
+ }
+ process_tree (uaddr_ip);
+}
----- End forwarded message -----