Subject: Re: getting UDP destination port/addresses
To: Michael Richardson <mcr@sandelman.ottawa.on.ca>
From: Andrew Brown <atatat@atatdot.net>
List: tech-net
Date: 01/07/2001 22:00:32
--h31gzZEtNLTqOjlF
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

>    Andrew> using IP_RECVDSTADDR and recvmsg(2) (instead of read(2), recv(2), or
>    Andrew> recvfrom(2)) works just fine for me.
>
>  that's the thing I'm talking about.
>  Where is it documented? recvmsg(2) doesn't mention it...

um...er...i...i don't remember exactly where i reaad about it.  or
when, for that matter.  but i do remember trying it out and it worked.
try the ip(4) man page.  that's got a bit in it.

>  How do I get at it? In the msg_control?

yeah, that's it.

>  I have always found getting documentation on specific ioctls/setsockopts/flags
>to be a problem.

i also.  i sometimes end up just perusing the kernel looking for how
it works and then decide if it will do what i want.

ps - the "proof of concept" program that i wrote is attached.  when
started, it listens on udp *.12345 for inbound packets.  it doesn't
read the packets, or send anything back, but merely print how many
bytes it got, from where, and on which local address.  nc is good for
flinging random udp data around on a whim.

-- 
|-----< "CODE WARRIOR" >-----|
codewarrior@daemon.org             * "ah!  i see you have the internet
twofsonet@graffiti.com (Andrew Brown)                that goes *ping*!"
andrew@crossbar.com       * "information is power -- share the wealth."

--h31gzZEtNLTqOjlF
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="udps.c"

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <err.h>
#include <arpa/inet.h>

struct in_addr to;

#if 0
struct msghdr {
        caddr_t msg_name;               /* optional address */
        u_int   msg_namelen;            /* size of address */
        struct  iovec *msg_iov;         /* scatter/gather array */
        u_int   msg_iovlen;             /* # elements in msg_iov */
        caddr_t msg_control;            /* ancillary data, see below */
        u_int   msg_controllen;         /* ancillary data buffer len */
        int     msg_flags;              /* flags on received message */
};
#endif

#define MYBUF 16384

ssize_t
recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from,
	 socklen_t *fromlen)
{
  size_t rc;
  struct msghdr msg;
  u_char address[MYBUF];
  struct iovec iov;
  struct cmsghdr *cmsg;

  iov.iov_base=buf;
  iov.iov_len=len;

  msg.msg_name=(caddr_t)from;
  msg.msg_namelen=(uint)*fromlen;
  msg.msg_iov=&iov;
  msg.msg_iovlen=1;
  msg.msg_control=address;
  msg.msg_controllen=sizeof(address);
  msg.msg_flags=0;

  if ((rc=recvmsg(s,&msg,flags))!=-1) {
    /* "copy" these back to the caller */
    *fromlen=msg.msg_namelen;
    /* and arrange for IP_RECVDSTADDR to be available */
    if (msg.msg_controllen>0)
      for (cmsg=CMSG_FIRSTHDR(&msg);cmsg;cmsg=CMSG_NXTHDR(&msg,cmsg)) {
	void *cdata=CMSG_DATA(cmsg);
	if (cmsg->cmsg_type==IP_RECVDSTADDR) {
	  struct in_addr *i=cdata;
	  to=*i;
	}
      }
  }
  return rc;
}
  
int
main(int argc,char *argv[])
{
  int s,l;
  ssize_t rc;
  struct sockaddr_in addr;
  u_char buf[MYBUF];

  s=socket(PF_INET,SOCK_DGRAM,0);
  if (s==-1)
    errx(1,"socket(1): %s",strerror(errno));

  rc=1;
  if (setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&rc,sizeof(rc))==-1)
    errx(1,"setsockopt(1) SO_REUSEADDR: %s",strerror(errno));
  if (setsockopt(s,IPPROTO_IP,IP_RECVDSTADDR,&rc,sizeof(rc))==-1)
    errx(1,"setsockopt IP_RECVDSTADDR: %s",strerror(errno));

  bzero(&addr,sizeof(addr));
  addr.sin_family=AF_INET;
  addr.sin_len=sizeof(addr);
  addr.sin_port=htons(12345);

  if (bind(s,(struct sockaddr*)&addr,sizeof(addr))==-1)
    errx(1,"bind(1): %s",strerror(errno));

  do {
    l=sizeof(addr);
    if ((rc=recvfrom(s,buf,sizeof(buf),0,(struct sockaddr*)&addr,&l))==-1)
      errx(1,"recvfrom: %s",strerror(errno));
    printf("received %d bytes from %s.%d\n",rc,inet_ntoa(addr.sin_addr),
	   ntohs(addr.sin_port));
    printf("received at %s\n",inet_ntoa(to));
  } while (1);

  exit(0);
}

--h31gzZEtNLTqOjlF--