Link to home
Start Free TrialLog in
Avatar of -Thespian-
-Thespian-Flag for Ukraine

asked on

UDP broadcasting from kernel

How to send broad cast message from myne kernel module?
Have wrote a piece of code, but it is not working :(
___________________________________________________
      // Initial properties
      int iRet;

      // Creating socket
      struct socket *pWorkingSocket = NULL;
      iRet = sock_create(PF_INET, SOCK_DGRAM, 0, &pWorkingSocket);
      if (iRet) {
            printk("Cannot create the socket...\n");
            return -1;
      }

      // Allocating memory for remote address
      struct sockaddr_in *pAddress;
      pAddress = kmalloc(sizeof(struct sockaddr_in), GFP_KERNEL);
      if (pAddress == NULL) {
            printk("Cannot allocate kernel memory (ERROR: %d)...\n", -ENOMEM);
            return -ENOMEM;
      }
      memset(pAddress, 0, sizeof(pAddress));
      pAddress->sin_family = AF_INET;
      pAddress->sin_port = htons(DRIVER_SERVICE_PORT);
      pAddress->sin_addr.s_addr = htonl(INADDR_BROADCAST);

      // Connecting socket to remote address
      iRet = pWorkingSocket->ops->connect(pWorkingSocket,
                  (struct sockaddr *) pAddress,
                  sizeof(struct sockaddr), O_RDWR);
      if (iRet) {
            printk("Cannot connect to the remote PC (ERROR: %d)...\n", iRet);
            goto out;
      }
___________________________________________________
It shows me "Cannot connect to the remote PC".
Avatar of Duncan Roe
Duncan Roe
Flag of Australia image

Is this a homework assignment by any chance?
What message number did your printk show?
UDP is connectionless, I'm not surprised the connect failed.
If I were you, I'd get this running as a user-space program first. Then convert it into a kernel module once you're sure the logic is right.
Avatar of -Thespian-

ASKER

Nope, it is not a homework.
This code works good, if I put the target IP instead "htonl(INADDR_BROADCAST);".
Can u give a sample of non connecton code with sending message?
With UDP, use sendto() instead of connect(), send(). Use recvfrom() in preferance to recv(), which shows you from which IP a message came.
Yes now I've looked, I see from the man page that you can use connect() with SOCK_DGRAM. But maybe not to the broadcast address (which would always fail for a SOCK_STREAM socket anyway).
As I said, try it in userspace first - it's so much easier to debug.
I am talking about kernel. There is no sendto() function (can't find symbol). There we have sock_sendmsg().
There is a userspace sendmsg - maybe sock_sendmsg() is its equivalent in kernel. Like sendto(), sendmsg() can specify a destination address as well as a message.
there is sock_sendmsg, but no sock_sendto
I'm hoping sock_sendmsg is like userspace sendmsg - sendmsg() is an alternative to sendto(). Have a look at "man sendmsg".
sendmsg need connect or bind. sendto - don't need it...
Doesn't kernel have a bind? user-space connect is "overloaded" for SOCK_DGRAM to have an unusual meaning as per man page - but bind is always bind. It associates the local end of the socket with a particular NIC
yes. can u show me the full sample code of how can I send broadcast UDP messages and addressed messages?
I don't have sample code - I suggest you try it out in userland restricting yourself to calls available in kernel space, then port it once it's working.
1. bind to a NIC
2. do a sendmsg - params similar to connect & send, except you set them up in a struct.
it is already working, but I can not send broadcast. Even if it is in userspace....
Post the userspace source that can't send broadcasts. We can take it from there.
/******************************************************************************
 * Sending message to selected socket
 * Parameters:
 *   sock - initialized socket
 *   Buffer - data to send
 *   Lenght - size of data to send
 *   bConfirm - set to 1 if u want the confirmation from server
 */
size_t SendBuffer(struct socket *sock,
                  const char *Buffer, size_t Length,
                  int bConfirm)
{
      // Set the iovec structure
      struct iovec iov;
      memset(&iov, 0, sizeof(iov));
      iov.iov_base = (char*) Buffer;
      iov.iov_len = (__kernel_size_t) Length;

      struct msghdr msg;
      memset(&msg, 0, sizeof(msg));
      msg.msg_iov = &iov;
      msg.msg_iovlen = 1;
      msg.msg_flags = MSG_NOSIGNAL;
/*
      if (bConfirm != 1)
            msg.msg_flags = MSG_NOSIGNAL;//0 MSG_DONTWAIT;
      else
            msg.msg_flags = MSG_CONFIRM;
*/
      mm_segment_t oldfs = get_fs(); set_fs(KERNEL_DS);
      int len = sock_sendmsg(sock, &msg, (size_t)(Length));
      set_fs(oldfs);

      log_info("Sended message. Original size: %d. Sended size: %d", Length, len);

      if (bConfirm == 1)
      {
            char* ptr = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
            int ret = RecvBuffer(sock, ptr, BUFFER_LENGTH, 1);
            if (ret != 1 || ptr[0] != 'Y')
                  ret = 0;
            kfree(ptr);
            if (ret == 1)
                  log_info("Message synchronized.");
            else
                  log_warn("Message not synchronized.");
      }

      return len;
}
int sendUDPMessage(const char* sIP, char* cBuffer, int iBufferLength, int bConfirm)
{
      int ret = 0;
      // Initial properties
      int iRet;

      // Creating socket
      struct socket *pWorkingSocket = NULL;
      iRet = sock_create(PF_INET, SOCK_DGRAM, 0, &pWorkingSocket);
      if (iRet) {
            printk("Cannot create the socket...\n");
            return -1;
      }

      // Allocating memory for remote address
      struct sockaddr_in *pAddress;
      pAddress = kmalloc(sizeof(struct sockaddr_in), GFP_KERNEL);
      if (pAddress == NULL) {
            printk("Cannot allocate kernel memory (ERROR: %d)...\n", -ENOMEM);
            return -ENOMEM;
      }
      memset(pAddress, 0, sizeof(pAddress));
      pAddress->sin_family = AF_INET;
      pAddress->sin_port = htons(MOD_SERVICE_PORT);
      if (sIP == NULL) {
            pAddress->sin_addr.s_addr = htonl(INADDR_BROADCAST);
/*            int one = 1;
            iRet = setsockopt(pWorkingSocket, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one));
            if (iRet) {
                  printk("Cannot create broadcasting (ERROR: %d)...\n", iRet);
                  goto out;
            }*/
      } else {
            pAddress->sin_addr.s_addr = IP2InetAddr(sIP);
      }

      // Connecting socket to remote address
      iRet = pWorkingSocket->ops->connect(pWorkingSocket,
                  (struct sockaddr *) pAddress,
                  sizeof(struct sockaddr), O_RDWR);
      if (iRet) {
            printk("Cannot connect to the remote PC (ERROR: %d)...\n", iRet);
            goto out;
      }

      ret = SendBuffer(pWorkingSocket, cBuffer, iBufferLength, bConfirm);

out:      sock_release(pWorkingSocket);
      kfree(pAddress);
      return ret;
}
Hi -Thespian- , I thought from your comment earlier that you had a userspace version? I'd prefer if you would post that, but I guess I can work with the kernel one (will port to userland though).
Please post source for RecvBuffer to save me fuessing - thanks ... Duncan.
fuessing - guessing. Why can't I see 'em before I post? Dunno
Well, my userspace program drew a blank. I can send UDP to a host but not to the broadcast address. When I try to do that, I get "Permission denied", even running as root. Perhaps it's just something you can't do with UDP protocol, at least from userspace - is that why you're doing it in the kernel?
Mind you I *did* get different errors until I did everything else right - bind the socket with a sending port, specify a nonzero port to send to (otherwise you get "Invalid argument"). Maybe something would work, but I'm out of ideas.
FWIW, here's the code. You can change values as you wish with gdb - saves having to write a UI

00:59:45$ cat bcast.c
//SIOCGIFADDR
// ((struct sockaddr_in)ifr.ifr_ifru.ifru_broadaddr).sin_addr
/*
gcc -Wall -Wstrict-prototypes -Wmissing-prototypes -g3 bcast.c -o bcast
*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

/* For test purposes, let's say we know which interface we want to use */

static char*ifname="eth0";

/* Message to broadcast */

char*Buffer="Hi there Tiger";

int main(int argc,char**argv)
{
  struct ifreq ifr;
  int s;
  struct msghdr msg;
  struct iovec iov;
  struct sockaddr addr;
 
  if((s=socket(PF_INET, SOCK_DGRAM, 0))==-1)
  {
    fprintf(stderr,"%s. (PF_INET, SOCK_DGRAM, 0 (socket)\n",
      strerror(errno));
    exit(1);
  }                             /* if((s=socket(PF_INET, SOCK_DGRAM, 0)0==-1) */
  snprintf(ifr.ifr_name,sizeof ifr.ifr_name,"%s",ifname);
  if(ioctl(s,SIOCGIFADDR,&ifr))
  {
    fprintf(stderr,"%s. SIOCGIFADDR %s (ioctl)\n",strerror(errno),
      ifr.ifr_name);
    exit(1);
  }                                /* if(ioctl(SIOCGIFADDR,&ifr)) */
  memcpy(&addr,&ifr.ifr_ifru.ifru_addr,sizeof(struct sockaddr));
  ((struct sockaddr_in*)&addr)->sin_port=htons(12345);
  if(bind(s,&addr,sizeof addr))
  {
    fprintf(stderr,"%s. (bind)\n",strerror(errno));
    exit(1);
  }
  if(ioctl(s,SIOCGIFBRDADDR,&ifr))
  {
    fprintf(stderr,"%s. SIOCGIFBRDADDR %s (ioctl)\n",strerror(errno),
      ifr.ifr_name);
    exit(1);
  }                                /* if(ioctl(SIOCGIFBRDADDR,&ifr)) */
  memset(&iov, 0, sizeof(iov));
  iov.iov_base = (char*) Buffer;
  iov.iov_len = strlen(Buffer);
  memset(&msg,0,sizeof msg);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  msg.msg_flags = 0;
  msg.msg_name=&ifr.ifr_ifru.ifru_broadaddr;
  msg.msg_namelen=sizeof ifr.ifr_ifru.ifru_broadaddr;
  ((struct sockaddr_in*)msg.msg_name)->sin_port=htons(12345);
  if(sendmsg(s,&msg,0)==-1)
  {
    fprintf(stderr,"%s. (sendmsg)\n",strerror(errno));
    exit(1);
  }                                /* if(sendmsg(s,&msg,0)==-1) */
  return 0;
}                                  /* int main(int argc,char**argv) */
I tried adding MSG_DONTROUTE to the flags arg (which you'd certainly want to do) but still get "Permission denied", even as root.
I guess you could use a packet socket - the MAC has to be the broadcast address (6 bytes of 0xff)  (or 1st byte 7f(?)). The source address of your NIC can be had from an ioctl similar to those above. Build the UDP packet according to RFC768
Got it. From reading kernel source, I found that you must set socket option SO_BROADCAST (userspace) or SOCK_BROADCAST (kernel), otherwise you will get EPERM (as I did). Also in the source I could see where it insists on a nonzero destination port. And I could see it do a bind() for you if you haven't already done one (so you don't care about the sending port). The following userspace program sends UDP broadcasts - even as an ordinary user - I'll leave it to you to translate it for the kernel:

20:23:00$ cat bcast.c
/*
gcc -Wall -Wstrict-prototypes -Wmissing-prototypes -g3 bcast.c -o bcast
*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

/* For test purposes, let's say we know which interface we want to use */

static char*ifname="eth0";

/* Message to broadcast */

char*Buffer="Hi there Tiger";

int main(int argc,char**argv)
{
  struct ifreq ifr;
  int s;
  struct msghdr msg;
  struct iovec iov;
  int flags=MSG_DONTROUTE;
  int optval=1;
 
  if((s=socket(PF_INET, SOCK_DGRAM, 0))==-1)
  {
    fprintf(stderr,"%s. (PF_INET, SOCK_DGRAM, 0 (socket)\n",
      strerror(errno));
    exit(1);
  }                             /* if((s=socket(PF_INET, SOCK_DGRAM, 0)0==-1) */
  snprintf(ifr.ifr_name,sizeof ifr.ifr_name,"%s",ifname);
  if(ioctl(s,SIOCGIFBRDADDR,&ifr))
  {
    fprintf(stderr,"%s. SIOCGIFBRDADDR %s (ioctl)\n",strerror(errno),
      ifr.ifr_name);
    exit(1);
  }                                /* if(ioctl(SIOCGIFBRDADDR,&ifr)) */
  if(setsockopt(s,SOL_SOCKET,SO_BROADCAST,&optval,sizeof optval))
  {
    fprintf(stderr,"%s. SO_BROADCAST (setsockopt)\n",strerror(errno));
    exit(1);
  }
  memset(&iov, 0, sizeof(iov));
  iov.iov_base = (char*) Buffer;
  iov.iov_len = strlen(Buffer);
  memset(&msg,0,sizeof msg);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  msg.msg_flags = 0;
  msg.msg_name=&ifr.ifr_ifru.ifru_broadaddr;
  msg.msg_namelen=sizeof ifr.ifr_ifru.ifru_broadaddr;
  ((struct sockaddr_in*)msg.msg_name)->sin_port=htons(2);
  if(sendmsg(s,&msg,flags)==-1)
  {
    fprintf(stderr,"%s. (sendmsg)\n",strerror(errno));
    exit(1);
  }                                /* if(sendmsg(s,&msg,0)==-1) */
  return 0;
}                                  /* int main(int argc,char**argv) */
Thank you for reply, but I have discovered this too, and the problem is: setsockopt - no symbol found (after I start my kernel module). And I need this module to start without kernel recompilation:((

Also, maybe, u can help, how can I get the sender IP, when I receive the message (will inc points to 500 for this).

Thanks.
Oh yes it was in your posted code, but commented out. Would it be available from pWorkingSocket->ops? In kernel source I see sk->sk_prot->setsockopt - maybe you could try that.
You should be using rcvmsg() to receive UDP packets (in userland you could use that or recvfrom()) - either way the sender's address is there as a sockaddr (*msg.name will get filled in)
>Oh yes it was in your posted code, but commented out. Would it be available from pWorkingSocket->ops? In kernel source I see sk->sk_prot->setsockopt - maybe you could try that.
  thanks. will try in the evening.


>You should be using rcvmsg() to receive UDP packets (in userland you could use that or recvfrom()) - either way the sender's address is there as a sockaddr (*msg.name will get filled in)
  I was discovering it, but msg.namesize shows `16` and msg.name was `null`
msg.name is a I-O arg. Before calling sendmsg, set it to point to a buffer lenth sizeof sockaddr. Set namesize to sizeof sockaddr. On return, the buffer addressed by msg.name will be filled in.
I meant, before calling rcvmsg....
It's getting late here :)
>msg.name is a I-O arg. Before calling sendmsg, set it to point to a buffer lenth sizeof sockaddr. Set namesize to sizeof sockaddr. On return, the buffer addressed by msg.name will be filled in.

ah... thanks. will try today or tomorrow. will reply u up to the tomorrow same time.
Thanks, for msg help. It works. I have increased points. Please, give me some time to test broadcasting.
Have a problem with "sock->sk->sk_prot->ioctl(sock,SIOCGIFBRDADDR,&ifr)" string. The first parameter must be int, but it is pointer. I can't find the int field in socket structure. Also, mabe it is undefined because I don't make connect or bind.:((
The first parameter to ioctl is always a file unit number. In this case it should be he file unit number associated with sock.
:( hmm.. what should I put there from my code?
In the kernel, inet_ioctl takes a pointer to struct sock and implements SIOCGIFBRDADDR.
BTW, did you really mean sock->sk->sk_prot->ioctl or sock-->sk_prot->ioctl?
really.

>In the kernel, inet_ioctl takes a pointer to struct sock and implements SIOCGIFBRDADDR.
  it works, but I have a problem with "setsockopt" :(
  there is no "inet_setsockopt" symbol.:(
That's correct. You want udp_setsockopt()
udp_setsockopt symbol unknown too...:(
it's 4:40AM here. So will check for your response in 8hours (will be sleeping).
ASKER CERTIFIED SOLUTION
Avatar of Duncan Roe
Duncan Roe
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Was 11:40 AM here. I'll check around 9PM or so. Sleep well :)
it shows me "Error SIOCGIFBRDADDR (ERROR: -14)." :(
my code:
_________________________________________________________
size_t SendBuffer(struct socket *sock,
                  const char *Buffer, size_t Length,
                  int bConfirm, int bBroadcast)
{
      // Set the iovec structure
      struct iovec iov;
      memset(&iov, 0, sizeof(iov));
      iov.iov_base = (char*) Buffer;
      iov.iov_len = (__kernel_size_t) Length;

      struct msghdr msg;
      memset(&msg, 0, sizeof(msg));
      msg.msg_iov = &iov;
      msg.msg_iovlen = 1;
      msg.msg_flags = MSG_NOSIGNAL;
      if (bBroadcast == 1)
      {
            struct ifreq ifr;
            snprintf(ifr.ifr_name,sizeof ifr.ifr_name,"%s",ifname);
            int iRet;
            iRet = inet_ioctl(sock,SIOCGIFBRDADDR,&ifr);
            if(iRet != 0)
            {
                  log_warn("Error SIOCGIFBRDADDR (ERROR: %d).", iRet);
            }
            else
            {
                  int optval = 1;
                  iRet = sock->ops->setsockopt(sock,
                        SOL_SOCKET, SOCK_BROADCAST, &optval, sizeof optval);
                  if(iRet != 0)
                  {
                        log_warn("Error setsockopt (ERROR: %d).", iRet);
                  } else {
                        msg.msg_name=&ifr.ifr_ifru.ifru_broadaddr;
                        msg.msg_namelen=sizeof ifr.ifr_ifru.ifru_broadaddr;
                        ((struct sockaddr_in*)msg.msg_name)->sin_port=htons(MOD_SERVICE_PORT);
                  }
            }
      }

      if (bConfirm != 1)
            msg.msg_flags = MSG_NOSIGNAL;//0 MSG_DONTWAIT;
      else
            msg.msg_flags = MSG_CONFIRM;

      mm_segment_t oldfs = get_fs(); set_fs(KERNEL_DS);
      int len = sock_sendmsg(sock, &msg, (size_t)(Length));
      set_fs(oldfs);

      log_info("Sended message. Original size: %d. Sended size: %d", Length, len);
/*
      if (bConfirm == 1)
      {
            char* ptr = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
            int ret = RecvBuffer(sock, ptr, BUFFER_LENGTH, 1);
            if (ret != 1 || ptr[0] != 'Y')
                  ret = 0;
            kfree(ptr);
            if (ret == 1)
                  log_info("Message synchronized.");
            else
                  log_warn("Message not synchronized.");
      }
*/
      return len;
}


int sendUDPMessage(const char* sIP, char* cBuffer, int iBufferLength, int bConfirm)
{
      int ret = 0;
      // Initial properties
      int iRet;

      // Creating socket
      struct socket *pWorkingSocket = NULL;
      iRet = sock_create(PF_INET, SOCK_DGRAM, 0, &pWorkingSocket);
      if (iRet) {
            printk("Cannot create the socket...\n");
            return -1;
      }

      // Allocating memory for remote address
      struct sockaddr_in *pAddress = NULL;
      int bBroadcast = 0;
      if (sIP != NULL)
      {
            pAddress = kmalloc(sizeof(struct sockaddr_in), GFP_KERNEL);
            if (pAddress == NULL) {
                  printk("Cannot allocate kernel memory (ERROR: %d)...\n", -ENOMEM);
                  return -ENOMEM;
            }
            memset(pAddress, 0, sizeof(pAddress));
            pAddress->sin_family = AF_INET;
            pAddress->sin_port = htons(MOD_SERVICE_PORT);
            pAddress->sin_addr.s_addr = IP2InetAddr(sIP);
            // Connecting socket to remote address
            iRet = pWorkingSocket->ops->connect(pWorkingSocket,
                        (struct sockaddr *) pAddress,
                        sizeof(struct sockaddr), O_RDWR);
            if (iRet) {
                  printk("Cannot connect to the remote PC (ERROR: %d)...\n", iRet);
                  goto out;
            }
      } else {
            bBroadcast = 1;
      }

      ret = SendBuffer(pWorkingSocket, cBuffer, iBufferLength, bConfirm, bBroadcast);

out:      sock_release(pWorkingSocket);
      if (pAddress != NULL)
            kfree(pAddress);
      return ret;
}
From  /usr/src/linux/include/asm-generic/errno-base.h:
#define EFAULT          14      /* Bad address */
So either sock is a bad pointer or there's something seriously wrong with ifr.name.
What is ifname? I don't see a dcl for it in your code.
as u said earlier it is "eth0"
this function takes int as first param - descriptor of a socket, but I give it a pointer:(
/usr/src/linux/net/ipv4/af_inet.c:745:int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
maybe, the error is in ifr?
You must have linux source, you could be looking up some of this stuff as easily as I.
Ok it looks like ioctl() is a user call:
inet_ioctl calls devinet_ioctl to implement SIOCGIFBRDADDR.
devinet uses copy_from_user() to fetch the ifr struct. I.e. it is taking the address and verifying it to be in userspace - which it isn't because it's in kernel space.
What you need to do is copy the relevant lines from devinet.c to accomplish what you want.
At a guess, I'd say something like this should do the trick:

int junk(struct sockaddr_in *sin)
{
  struct in_ifaddr **ifap = NULL;
  struct in_ifaddr *ifa = NULL;
  int ret = -ENODEV;
  struct in_device *in_dev;
  struct net_device *dev;

#ifdef CONFIG_KMOD
  dev_load(ifname);
#endif
  rtnl_lock();
  if ((dev = __dev_get_by_name(ifname)) == NULL)
    goto done;
  if ((in_dev = __in_dev_get_rtnl(dev)) != NULL)
  {
    for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; ifap = &ifa->ifa_next)
      if (!strcmp(ifname, ifa->ifa_label))
        break;
  }
  ret = -EADDRNOTAVAIL;
  if (!ifa)
    goto done;
  sin->sin_addr.s_addr = ifa->ifa_broadcast;
  ret = 0;
done:
  rtnl_unlock();
  return ret;
}                                  /* int junk() */
> You must have linux source, you could be looking up some of this stuff as easily as I.
I have it. And trying to seek by myself. But I am new to linux, so don't knew how to find aproparate *.h file:(. Even I don't knew how to debug:(.

int junk() - I don't understand what it is doing.. What it for?
have find out what it does...
but have a compilation error: "error: dereferencing pointer to incomplete type"
on lines:
    for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; ifap = &ifa->ifa_next)
and
    sin->sin_addr.s_addr = ifa->ifa_broadcast;
You need the header file that defines struct in_ifaddr. It will be one of the headers included by devinet_ioctl.c
I use this shell script to find variables in the current dir & below (tidied up recently in answer to another EE Qn): hope you find it useful:

#!/bin/sh
#set -x

# Runs the show
main()
{
  try_gnu_args
  set_personality || exit 1

# Had to inline the next bit, coz it uses shift & args are local to functions
if [ -z "$1" ];then
  echo "Usage: $j <string> [<grep option>,...]" >&2
  exit 1
else
  k="$1"
  shift
fi

do_the_grep "$@"
}

# Sets fiddly gnu arg iff we have gnu grep
try_gnu_args()
{
  grep --version >/dev/null 2>&1
  [ $? -eq 0 ] && n=-noleaf || n=
}

# Check for good invocation.
# sfl is designed to be symlinked to - which synlink is used determines behaviour.
# list of symlinks:
# lrwxrwxrwx  1 dunc users    3 2004-09-30 22:12 afl -> sfl
# lrwxrwxrwx  1 dunc users    3 2004-09-30 22:12 aflc -> sfl
# lrwxrwxrwx  1 dunc users    3 2004-09-30 22:12 afll -> sfl
# lrwxrwxrwx  1 dunc users    3 2004-09-30 22:12 afllc -> sfl
# -rwxr-xr-x  1 dunc users 1183 2006-05-04 07:59 sfl
# lrwxrwxrwx  1 root root     3 2004-09-30 22:12 sflc -> sfl
# lrwxrwxrwx  1 root root     3 2004-09-30 22:12 sfll -> sfl
# lrwxrwxrwx  1 root root     3 2004-09-30 22:12 sfllc -> sfl
# lrwxrwxrwx  1 root root     3 2004-09-30 22:12 ufl -> sfl
# lrwxrwxrwx  1 root root     3 2004-09-30 22:12 uflc -> sfl
# lrwxrwxrwx  1 root root     3 2004-09-30 22:12 ufll -> sfl
# lrwxrwxrwx  1 root root     3 2004-09-30 22:12 ufllc -> sfl
set_personality()
{
  j=`basename $0`
  case $j in
  u*)
    f='.. -maxdepth 2 -mindepth 2'
    w=w
    ;;
  s*)
    f=.
    w=w
    ;;
  a*)
    f=.
    w=""
    ;;
  *)
    echo bad symlink of sfl to $j
    return 1
    ;;
  esac
  case $j in
  ?fl)
    i=''
    ;;
  ?fll)
    i=''
    f="$f -follow"
    ;;
  ?flc)
    i=i
    ;;
  ?fllc)
    i=i
    f="$f -follow"
    ;;
  *)
    echo bad symlink of sfl to $j
    return 1
    ;;
  esac
  return 0
}

# Actually do the gep
do_the_grep()
{
  find $f $n -depth -type f \( \
    -name "*.c" \
    -o -name "*.cpp" \
    -o -name "*.cxx" \
    -o -name "*.cc" \
    -o -name "*.f" \
    -o -name "*.for" \
    -o -name "*.S" \
    -o -name "*.h" \
    -o -name "*.java" \
    -o -name "*.exp" \
    -o -name "*.tcl" \
    -o -name "*.expect" \
    -o -name "*.pl" \
    -o -iname "makefile" \
    -o -name "make-file" \
    -o -name "*.i" \
    -o -name "*.l" \
    -o -name "*.lex" \
    -o -name "*.y" \
    -o -name "*.yacc" \
    -o -name "*.sh" \
    \) -print|xargs -n 32 -r -e grep -${i}s${w}n "$@" -- "$k" /dev/null
}
main "$@"


After installing, you could enter "sfl in_ifaddr" or "sfl in_ifaddr|grep struct" &c. I did that already, but hope you'll agree it's more useful to have the tool rather than the answer.
Hi -Thespian- , what's happening? It's several days since my last answer. Is it all working now? Do you have further questions?
Sorry. This thing is not working.:(( And I don't knew what to ask. Please, give me the time up to Mon or Tue.
Sorry for my pause...