Skip to content

Commit

Permalink
Merge 34b076d into 465920f
Browse files Browse the repository at this point in the history
  • Loading branch information
tboegi committed Oct 13, 2021
2 parents 465920f + 34b076d commit 37f1727
Show file tree
Hide file tree
Showing 48 changed files with 2,286 additions and 812 deletions.
43 changes: 43 additions & 0 deletions documentation/ipv6-howto.txt
@@ -0,0 +1,43 @@
Start using IPv6:
caget, camonitor can work via IPV6 like this:
$ export EPICS_CA_AUTO_ADDR_LIST=NO
$ export EPICS_CA_ADDR_LIST='[2001:db8:1:0:0:1:2:3]'
cainfo IOC:m1

Using link local addresses:
Find out the link-local address of your IOC.
e.g. fe80::3958:418:65b8:230c
Find out the interface of your client, sitting on the same network
switch as the IOC.
$ ifconfig
# or
$ ip addr show
in my case it is "en0"
# ping the IOC:
$ ping6 fe80::3958:418:65b8:230c
ping6: sendmsg: No route to host
# That didn't work, we new this.
# Compose a proper adress, combining the remote IPv6 with
# our local interface name:
$ ping6 fe80::3958:418:65b8:230c%en0
16 bytes from fe80::3958:418:65b8:230c%en0
# with this knowledge, we can use cainfo:
$ export EPICS_CA_AUTO_ADDR_LIST=NO
$ export EPICS_CA_ADDR_LIST='[fe80::3958:418:65b8:230c%en0]'
$ cainfo IOC:m1


Running an IOC on a Mac:
When using a "wireless only", there are different IPv6 interfaces.
The wireless itself :"en0",
An interface called "en3" (not sure what this is good for)
"awdl0" "llw0" (and some "utun0" and "utun1")
It seems as if our code does not handle this situation correctly (yet).
In order to use the wireless:
$ export EPICS_CAS_BEACON_ADDR_LIST='[ff02::1%en0]'
This will put out beacons on all IPv4 addresses.
IPv6 beacons are send out on en0.

Running an IOC on Linux:
$ export EPICS_CAS_AUTO_BEACON_ADDR_LIST=46
This sends out beacons on all IPv4 and IPv6 interfaces
11 changes: 8 additions & 3 deletions modules/ca/src/client/CAref.html
Expand Up @@ -269,13 +269,13 @@ <h3><a name="EPICS">EPICS Environment Variables</a></h3>
<th>Default</th>
</tr>
<tr>
<td>EPICS_CA_ADDR_LIST</td>
<td>{N.N.N.N N.N.N.N:P ...}</td>
<td>EPICS_CA_ADDR_LIST (IPv6 must use[])</td>
<td>{N.N.N.N N.N.N.N:P [ipv6] [ipv6local%en0]...}</td>
<td>&lt;none&gt;</td>
</tr>
<tr>
<td>EPICS_CA_AUTO_ADDR_LIST</td>
<td>{YES, NO}</td>
<td>{YES, NO, 4, 6, 46}</td>
<td>YES</td>
</tr>
<tr>
Expand Down Expand Up @@ -499,6 +499,11 @@ <h3><a name="Environmen">WAN Environment</a></h3>
"no" or "NO". The typical default is to enable network interface introspection
driven initialization with EPICS_CA_AUTO_ADDR_LIST set to "YES" or "yes".</p>

<p>Using IPv6:
Set EPICS_CA_AUTO_ADDR_LIST to "46" to use both IPv4 and IPv6 in parallel.
Set EPICS_CA_AUTO_ADDR_LIST to "6" to use only IPv6.
Set EPICS_CA_AUTO_ADDR_LIST to "4" to use only IPv4, this is the same as "YES".</p>

<p>Following network interface introspection, any IP addresses specified in the
EPICS environment variable EPICS_CA_ADDR_LIST are added to the list of
destination addresses for CA client name resolution requests. In an EPICS
Expand Down
2 changes: 1 addition & 1 deletion modules/ca/src/client/SearchDest.h
Expand Up @@ -27,7 +27,7 @@ struct SearchDest :
virtual ~Callback () {};
virtual void notify (
const caHdr & msg, const void * pPayload,
const osiSockAddr & addr, const epicsTime & ) = 0;
const osiSockAddr46 & addr46, const epicsTime & ) = 0;
virtual void show (
epicsGuard < epicsMutex > &, unsigned level ) const = 0;
};
Expand Down
39 changes: 24 additions & 15 deletions modules/ca/src/client/caServerID.h
Expand Up @@ -27,28 +27,26 @@

class caServerID {
public:
caServerID ( const struct sockaddr_in & addrIn, unsigned priority );
caServerID ( const osiSockAddr46 & addr46, unsigned priority );
bool operator == ( const caServerID & ) const;
resTableIndex hash () const;
osiSockAddr address () const;
osiSockAddr46 address () const;
unsigned priority () const;
private:
struct sockaddr_in addr;
osiSockAddr46 addr46;
ca_uint8_t pri;
};

inline caServerID::caServerID (
const struct sockaddr_in & addrIn, unsigned priorityIn ) :
addr ( addrIn ), pri ( static_cast <ca_uint8_t> ( priorityIn ) )
const osiSockAddr46 & addrIn46, unsigned priorityIn ) :
addr46 ( addrIn46 ), pri ( static_cast <ca_uint8_t> ( priorityIn ) )
{
assert ( priorityIn <= 0xff );
}

inline bool caServerID::operator == ( const caServerID & rhs ) const
{
if ( this->addr.sin_addr.s_addr == rhs.addr.sin_addr.s_addr &&
this->addr.sin_port == rhs.addr.sin_port &&
this->pri == rhs.pri ) {
if ( sockAddrAreIdentical46 ( &this->addr46, &rhs.addr46 ) ) {
return true;
}
return false;
Expand All @@ -63,18 +61,29 @@ inline resTableIndex caServerID::hash () const
const unsigned caServerMaxIndexBitWidth = 32u;

unsigned index;
index = this->addr.sin_addr.s_addr;
index ^= this->addr.sin_port;
index ^= this->addr.sin_port >> 8u;
index ^= this->pri;
#if EPICS_HAS_IPV6
if ( this->addr46.sa.sa_family == AF_INET6 ) {
index = this->addr46.in6.sin6_addr.s6_addr[15];
index ^= this->addr46.in6.sin6_addr.s6_addr[14] << 8;
index ^= this->addr46.in6.sin6_addr.s6_addr[13] << 16;
index ^= this->addr46.in6.sin6_addr.s6_addr[12] << 24;
index ^= this->addr46.in6.sin6_port;
} else
#endif
{
index = this->addr46.ia.sin_addr.s_addr;
index ^= this->addr46.ia.sin_port;
index ^= this->addr46.ia.sin_port >> 8u;
index ^= this->pri;
}
return integerHash ( caServerMinIndexBitWidth,
caServerMaxIndexBitWidth, index );
}

inline osiSockAddr caServerID::address () const
inline osiSockAddr46 caServerID::address () const
{
osiSockAddr tmp;
tmp.ia = this->addr;
osiSockAddr46 tmp;
tmp = this->addr46;
return tmp;
}

Expand Down
67 changes: 44 additions & 23 deletions modules/ca/src/client/ca_client_context.cpp
Expand Up @@ -87,7 +87,7 @@ ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) :
}
}

this->sock = epicsSocketCreate ( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
this->sock = epicsSocket46Create ( epicsSocket46GetDefaultAddressFamily(), SOCK_DGRAM, IPPROTO_UDP );
if ( this->sock == INVALID_SOCKET ) {
char sockErrBuf[64];
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
Expand Down Expand Up @@ -116,12 +116,22 @@ ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) :
// force a bind to an unconstrained address so we can obtain
// the local port number below
{
osiSockAddr addr;
memset ( (char *)&addr, 0 , sizeof ( addr ) );
addr.ia.sin_family = AF_INET;
addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY );
addr.ia.sin_port = htons ( PORT_ANY );
int status = bind (this->sock, &addr.sa, sizeof (addr) );
osiSockAddr46 addr46;
memset ( (char *)&addr46, 0 , sizeof ( addr46 ) );
addr46.sa.sa_family = epicsSocket46GetDefaultAddressFamily();
#if EPICS_HAS_IPV6
if ( addr46.sa.sa_family == AF_INET6 ) {
addr46.in6.sin6_addr = in6addr_any;
addr46.in6.sin6_port = htons ( PORT_ANY );
}
else
#endif
{
addr46.ia.sin_addr.s_addr = htonl ( INADDR_ANY );
addr46.ia.sin_port = htons ( PORT_ANY );
}

int status = epicsSocket46Bind (this->sock, &addr46.sa, sizeof (addr46) );
if ( status < 0 ) {
char sockErrBuf[64];
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
Expand All @@ -135,22 +145,23 @@ ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) :
}

{
osiSockAddr tmpAddr;
osiSocklen_t saddr_length = sizeof ( tmpAddr );
int status = getsockname ( this->sock, & tmpAddr.sa, & saddr_length );
osiSockAddr46 tmpAddr46;
osiSocklen_t saddr_length = sizeof ( tmpAddr46 );
int status = getsockname ( this->sock, & tmpAddr46.sa, & saddr_length );
if ( status < 0 ) {
char sockErrBuf[64];
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
epicsSocketDestroy ( this->sock );
this->printFormated ( "CAC: getsockname () error was \"%s\"\n", sockErrBuf );
throwWithLocation ( noSocket () );
}
if ( tmpAddr.sa.sa_family != AF_INET) {
if ( ! epicsSocket46IsAF_INETorAF_INET6 ( tmpAddr46.sa.sa_family ) ) {
epicsSocketDestroy ( this->sock );
this->printFormated ( "CAC: UDP socket was not inet addr family\n" );
this->printFormated ( "CAC: UDP socket was not inet addr family=%d\n",
tmpAddr46.sa.sa_family);
throwWithLocation ( noSocket () );
}
this->localPort = htons ( tmpAddr.ia.sin_port );
this->localPort = epicsSocket46portFromAddress ( &tmpAddr46 ) ;
}

ca::auto_ptr < CallbackGuard > pCBGuard;
Expand Down Expand Up @@ -547,13 +558,12 @@ int ca_client_context::pendEvent ( const double & timeout )

// remove short udp message sent to wake
// up a file descriptor manager
osiSockAddr tmpAddr;
osiSocklen_t addrSize = sizeof ( tmpAddr.sa );
osiSockAddr46 tmpAddr46;
char buf = 0;
int status = 0;
do {
status = recvfrom ( this->sock, & buf, sizeof ( buf ),
0, & tmpAddr.sa, & addrSize );
status = epicsSocket46Recvfrom ( this->sock, & buf, sizeof ( buf ),
0, & tmpAddr46 );
} while ( status > 0 );
}
while ( this->callbackThreadsPending > 0 ) {
Expand Down Expand Up @@ -621,13 +631,24 @@ void ca_client_context :: _sendWakeupMsg ()
{
// send short udp message to wake up a file descriptor manager
// when a message arrives
osiSockAddr tmpAddr;
tmpAddr.ia.sin_family = AF_INET;
tmpAddr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK );
tmpAddr.ia.sin_port = htons ( this->localPort );
osiSockAddr46 addr46;
int defaultFamily = epicsSocket46GetDefaultAddressFamily();
int status;
int flags = 0;
char buf = 0;
sendto ( this->sock, & buf, sizeof ( buf ),
0, & tmpAddr.sa, sizeof ( tmpAddr.sa ) );
if ( defaultFamily == AF_INET ) {
flags |= EPICSSOCKET_CONNECT_IPV4;
}
#if EPICS_HAS_IPV6
else if ( defaultFamily == AF_INET6 ) {
flags |= EPICSSOCKET_CONNECT_IPV6;
}
#endif
status = aToIPAddr46("localhost", this->localPort, &addr46, flags);
if ( ! status) {
epicsSocket46Sendto ( this->sock, & buf, sizeof ( buf ),
0, & addr46 );
}
}

void ca_client_context::callbackProcessingCompleteNotify ()
Expand Down

0 comments on commit 37f1727

Please sign in to comment.