Hi, On Sun, Jan 19, 2014 at 03:04:17PM +0100, Gert Doering wrote: > In case you want to test yourself, the code is appended.
... it was not. As always when people say "I have attached..." :-/
gert
--
USENET is *not* the non-clickable part of WWW!
//www.muc.de/~gert/
Gert Doering - Munich, Germany [email protected]
fax: +49-89-35655025 [email protected]
/*
* mhome.c - test program for multihoming server tests
*
* open UDP socket on port specified on command line, bind IPv4 or IPv6
* to it, log all incoming packets and reply, hopefully with the right
* source address
*
* largely based on OpenVPN sources to test those modules
*/
#define ENABLE_IP_PKTINFO 1
#ifdef linux
/* needed for netinet/in.h */
#define _GNU_SOURCE 1
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <net/if.h>
#include <stdbool.h>
#include <assert.h>
#define ASSERT(x) assert(x)
#define CLEAR(x) memset(&(x), 0, sizeof(x))
/* IP_PKTINFO or IP_RECVDSTADDR are defined in <netinet/in.h> on BSDs
* and Linux just fine, but our configure/ifdef logic is weird */
#ifdef IP_PKTINFO
# define HAVE_IN_PKTINFO 1
#endif
#define msg(a,b) fprintf(stderr,b "\n")
/* glue definitions, copied from syshead.h or socket.h */
typedef int socket_descriptor_t;
# define SF_USE_IP_PKTINFO (1<<0)
#define PS_SHOW_PORT_IF_DEFINED (1<<0)
#define PS_SHOW_PORT (1<<1)
#define PS_SHOW_PKTINFO (1<<2)
#define PS_DONT_SHOW_ADDR (1<<3)
/* OpenVPN sockaddr struct */
struct openvpn_sockaddr
{
union {
struct sockaddr sa;
struct sockaddr_in in4;
struct sockaddr_in6 in6;
} addr;
};
struct link_socket_actual
{
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
struct openvpn_sockaddr dest;
#if ENABLE_IP_PKTINFO
union {
#ifdef HAVE_IN_PKTINFO
struct in_pktinfo in4;
#elif defined(IP_RECVDSTADDR)
struct in_addr in4;
#endif
struct in6_pktinfo in6;
} pi;
#endif
};
/*
* Format IP addresses in ascii
*
* (hacked-together non-safe version of the version in openvpn)
*/
const char *
print_sockaddr_ex (const struct sockaddr *sa,
const char* separator,
const unsigned int flags )
{
static char out[1000];
bool addr_is_defined;
char hostaddr[NI_MAXHOST] = "";
char servname[NI_MAXSERV] = "";
int status;
socklen_t salen;
switch(sa->sa_family)
{
case AF_INET:
strcpy(out, "[AF_INET]");
salen = sizeof (struct sockaddr_in);
addr_is_defined = ((struct sockaddr_in*) sa)->sin_addr.s_addr != 0;
break;
case AF_INET6:
strcpy(out, "[AF_INET6]");
salen = sizeof (struct sockaddr_in6);
addr_is_defined = !IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6*)
sa)->sin6_addr);
break;
case AF_UNSPEC:
return "[AF_UNSPEC]";
default:
ASSERT(0);
}
status = getnameinfo(sa, salen, hostaddr, sizeof (hostaddr),
servname, sizeof(servname), NI_NUMERICHOST | NI_NUMERICSERV);
if(status!=0) {
sprintf(&out[strlen(out)],"[nameinfo() err: %s]",gai_strerror(status));
return out;
}
if (!(flags & PS_DONT_SHOW_ADDR))
{
if (addr_is_defined)
strcat (out, hostaddr);
else
strcat (out, "[undef]");
}
if ((flags & PS_SHOW_PORT) || (flags & PS_SHOW_PORT_IF_DEFINED))
{
if (separator)
strcat (out, separator);
strcat (out, servname);
}
return out;
}
/* ------- mostly unchanged code from socket.c below this line ----- */
static socket_descriptor_t
create_socket_udp (const int af, const unsigned int flags)
{
socket_descriptor_t sd;
if ((sd = socket (af, SOCK_DGRAM, IPPROTO_UDP)) < 0)
msg (M_ERR, "UDP: Cannot create UDP/UDP6 socket");
#if ENABLE_IP_PKTINFO
else if (flags & SF_USE_IP_PKTINFO)
{
int pad = 1;
if(af == AF_INET)
{
#ifdef IP_PKTINFO
fprintf( stderr, "AF_INET/IP_PKTINFO enabled\n");
if (setsockopt (sd, SOL_IP, IP_PKTINFO,
(void*)&pad, sizeof(pad)) < 0)
msg(M_ERR, "UDP: failed setsockopt for IP_PKTINFO");
#elif defined(IP_RECVDSTADDR)
fprintf( stderr, "AF_INET/IP_RECVDSTADDR enabled\n");
if (setsockopt (sd, IPPROTO_IP, IP_RECVDSTADDR,
(void*)&pad, sizeof(pad)) < 0)
msg(M_ERR, "UDP: failed setsockopt for IP_RECVDSTADDR");
#else
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix
syshead.h)
#endif
}
else if (af == AF_INET6 )
{
#ifndef IPV6_RECVPKTINFO /* Some older Darwin platforms require this */
fprintf( stderr, "AF_INET6/IPV6_PKTINFO enabled\n");
if (setsockopt (sd, IPPROTO_IPV6, IPV6_PKTINFO,
(void*)&pad, sizeof(pad)) < 0)
#else
fprintf( stderr, "AF_INET6/IPV6_RECVPKTINFO enabled\n");
if (setsockopt (sd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
(void*)&pad, sizeof(pad)) < 0)
#endif
msg(M_ERR, "UDP: failed setsockopt for IPV6_RECVPKTINFO");
}
}
#endif
return sd;
}
void
socket_bind (socket_descriptor_t sd,
char* port, int ai_family)
{
struct addrinfo hints;
struct addrinfo* cur;
CLEAR(hints);
hints.ai_family = ai_family;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
int s = getaddrinfo( NULL, port, &hints, &cur );
if ( s != 0 )
{ fprintf( stderr, "getaddrinfo failed: %s (%d)\n",
gai_strerror(s), s ); exit(1); }
if (ai_family == AF_INET6)
{
int v6only = 0;
fprintf (stderr, "setsockopt(IPV6_V6ONLY=%d)\n", v6only);
if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)))
{
fprintf (stderr, "Setting IPV6_V6ONLY=%d failed\n", v6only);
}
}
if (bind (sd, cur->ai_addr, cur->ai_addrlen))
{
const int err = errno;
fprintf (stderr, "Socket bind failed on local address %s: %s\n",
print_sockaddr_ex (cur->ai_addr, ":", PS_SHOW_PORT),
strerror(err));
exit(1);
}
fprintf (stderr, "Socket bound to local address %s\n",
print_sockaddr_ex (cur->ai_addr, ":", PS_SHOW_PORT) );
}
const char *
print_link_socket_actual_ex (const struct link_socket_actual *act,
const char *separator,
const unsigned int flags)
{
static char out[1000];
if (act)
{
char ifname[IF_NAMESIZE] = "[undef]";
strcpy (out, print_sockaddr_ex (&act->dest.addr.sa, separator, flags));
#if ENABLE_IP_PKTINFO
if ((flags & PS_SHOW_PKTINFO)) /* && addr_defined_ipi(act))*/
{
switch(act->dest.addr.sa.sa_family)
{
case AF_INET:
{
struct openvpn_sockaddr sa;
CLEAR (sa);
sa.addr.in4.sin_family = AF_INET;
#ifdef IP_PKTINFO
sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst;
if_indextoname(act->pi.in4.ipi_ifindex, ifname);
#elif defined(IP_RECVDSTADDR)
sa.addr.in4.sin_addr = act->pi.in4;
ifname[0]=0;
#else
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix
syshead.h)
#endif
sprintf (&out[strlen(out)], " (via %s%%%s)",
print_sockaddr_ex (&sa.addr.sa, separator, 0),
ifname);
}
break;
case AF_INET6:
{
struct sockaddr_in6 sin6;
char buf[INET6_ADDRSTRLEN] = "[undef]";
CLEAR(sin6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = act->pi.in6.ipi6_addr;
if_indextoname(act->pi.in6.ipi6_ifindex, ifname);
if (getnameinfo((struct sockaddr *)&sin6, sizeof (struct
sockaddr_in6),
buf, sizeof (buf), NULL, 0, NI_NUMERICHOST)
== 0)
sprintf (&out[strlen(out)], " (via %s%%%s)", buf, ifname);
else
sprintf (&out[strlen(out)], " (via [getnameinfo()
err]%%%s)", ifname);
}
break;
}
}
#endif
return out;
}
else
return "[NULL]";
}
#pragma pack(1) /* needed to keep structure size consistent for 32 vs. 64-bit
architectures */
struct openvpn_in4_pktinfo
{
struct cmsghdr cmsghdr;
#ifdef HAVE_IN_PKTINFO
struct in_pktinfo pi4;
#elif defined(IP_RECVDSTADDR)
struct in_addr pi4;
#endif
};
struct openvpn_in6_pktinfo
{
struct cmsghdr cmsghdr;
struct in6_pktinfo pi6;
};
union openvpn_pktinfo {
struct openvpn_in4_pktinfo msgpi4;
struct openvpn_in6_pktinfo msgpi6;
};
#pragma pack()
static socklen_t
link_socket_read_udp_posix_recvmsg (socket_descriptor_t sd,
char *buf,
int maxsize,
struct link_socket_actual *from,
int *r_len)
{
struct iovec iov;
union openvpn_pktinfo opi;
struct msghdr mesg;
socklen_t fromlen = sizeof (from->dest.addr);
iov.iov_base = buf;
iov.iov_len = maxsize;
mesg.msg_iov = &iov;
mesg.msg_iovlen = 1;
mesg.msg_name = &from->dest.addr;
mesg.msg_namelen = fromlen;
mesg.msg_control = &opi;
mesg.msg_controllen = sizeof opi;
*r_len = recvmsg (sd, &mesg, 0);
if (*r_len >= 0)
{
struct cmsghdr *cmsg;
fromlen = mesg.msg_namelen;
cmsg = CMSG_FIRSTHDR (&mesg);
if (cmsg == NULL)
fprintf( stderr, "CMSG_FIRSTHDR()=NULL, no packet info available\n");
else
fprintf( stderr, "CMSG_NXTHDR=%p, level=%d, type=%d\n",
CMSG_NXTHDR (&mesg, cmsg), cmsg->cmsg_level, cmsg->cmsg_type );
if (cmsg != NULL
&& CMSG_NXTHDR (&mesg, cmsg) == NULL
#ifdef IP_PKTINFO
&& cmsg->cmsg_level == SOL_IP
&& cmsg->cmsg_type == IP_PKTINFO
#elif defined(IP_RECVDSTADDR)
&& cmsg->cmsg_level == IPPROTO_IP
&& cmsg->cmsg_type == IP_RECVDSTADDR
#else
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix
syshead.h)
#endif
&& cmsg->cmsg_len >= sizeof (struct openvpn_in4_pktinfo))
{
#ifdef IP_PKTINFO
struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
from->pi.in4.ipi_ifindex = pkti->ipi_ifindex;
from->pi.in4.ipi_spec_dst = pkti->ipi_spec_dst;
fprintf( stderr, "IP_PKTINFO\n" );
#elif defined(IP_RECVDSTADDR)
from->pi.in4 = *(struct in_addr*) CMSG_DATA (cmsg);
fprintf( stderr, "IP_RECVDSTADDR\n" );
#else
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix
syshead.h)
#endif
}
else if (cmsg != NULL
&& CMSG_NXTHDR (&mesg, cmsg) == NULL
&& cmsg->cmsg_level == IPPROTO_IPV6
&& cmsg->cmsg_type == IPV6_PKTINFO
&& cmsg->cmsg_len >= sizeof (struct openvpn_in6_pktinfo))
{
struct in6_pktinfo *pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg);
from->pi.in6.ipi6_ifindex = pkti6->ipi6_ifindex;
from->pi.in6.ipi6_addr = pkti6->ipi6_addr;
fprintf( stderr, "IPV6_PKTINFO\n" );
}
}
return fromlen;
}
size_t
link_socket_write_udp_posix_sendmsg (socket_descriptor_t sd,
char *buf, int len,
struct link_socket_actual *to)
{
struct iovec iov;
struct msghdr mesg;
struct cmsghdr *cmsg;
union openvpn_pktinfo opi;
iov.iov_base = buf;
iov.iov_len = len;
mesg.msg_iov = &iov;
mesg.msg_iovlen = 1;
switch (to->ai_family)
{
case AF_INET:
{
mesg.msg_name = &to->dest.addr.sa;
mesg.msg_namelen = sizeof (struct sockaddr_in);
mesg.msg_control = &opi;
mesg.msg_controllen = sizeof (struct openvpn_in4_pktinfo);
mesg.msg_flags = 0;
cmsg = CMSG_FIRSTHDR (&mesg);
cmsg->cmsg_len = sizeof (struct openvpn_in4_pktinfo);
#ifdef HAVE_IN_PKTINFO
cmsg->cmsg_level = SOL_IP;
cmsg->cmsg_type = IP_PKTINFO;
{
struct in_pktinfo *pkti;
pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
pkti->ipi_ifindex = to->pi.in4.ipi_ifindex;
pkti->ipi_spec_dst = to->pi.in4.ipi_spec_dst;
pkti->ipi_addr.s_addr = 0;
}
#elif defined(IP_RECVDSTADDR)
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_RECVDSTADDR;
*(struct in_addr *) CMSG_DATA (cmsg) = to->pi.in4;
#else
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix
syshead.h)
#endif
break;
}
case AF_INET6:
{
struct in6_pktinfo *pkti6;
mesg.msg_name = &to->dest.addr.sa;
mesg.msg_namelen = sizeof (struct sockaddr_in6);
mesg.msg_control = &opi;
mesg.msg_controllen = sizeof (struct openvpn_in6_pktinfo);
mesg.msg_flags = 0;
cmsg = CMSG_FIRSTHDR (&mesg);
cmsg->cmsg_len = sizeof (struct openvpn_in6_pktinfo);
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg);
pkti6->ipi6_ifindex = to->pi.in6.ipi6_ifindex;
pkti6->ipi6_addr = to->pi.in6.ipi6_addr;
break;
}
default: ASSERT(0);
}
return sendmsg (sd, &mesg, 0);
}
void loop(int sd)
{
int r_len, fromlen, r;
char buf[10000];
struct link_socket_actual from;
while(1)
{
CLEAR(from);
fprintf( stderr, "--\n" );
fromlen = link_socket_read_udp_posix_recvmsg (sd, buf, sizeof(buf),
&from, &r_len);
fprintf( stderr, "read: fromlen=%d, r_len=%d\n", fromlen, r_len );
fprintf( stderr, " from=%s\n",
print_link_socket_actual_ex(&from, ":",
PS_SHOW_PORT|PS_SHOW_PKTINFO) );
#if 0
/* send back response (just mirror packet) */
r = link_socket_write_udp_posix_sendmsg(sd, buf, r_len, &from);
fprintf( stderr, "sendmsg() returns r=%d\n", r );
#endif
}
}
int main(int argc, char **argv)
{
int s;
int af = AF_INET6;
char* port = "50001";
if ( argc > 1 && argv[1][0] == '4' ) { af = AF_INET; }
if ( argc > 2 ) { port = argv[2]; }
s = create_socket_udp( af, SF_USE_IP_PKTINFO );
socket_bind( s, port, af );
loop(s);
exit(0);
}
pgpisdT0waTvC.pgp
Description: PGP signature
