rsrv: when binding to a specific interface also bind to iface bcast
An oddness of BSD sockets (not winsock) is that binding to INADDR_ANY will receive unicast and broadcast, but binding to a specific interface address receives only unicast. The trick is to bind a second socket to the interface broadcast address, which will then receive only broadcasts.
This commit is contained in:
committed by
Michael Davidsaver
parent
77a5f0db77
commit
991ff308e0
@@ -115,30 +115,31 @@ static void clean_addrq(void)
|
||||
*/
|
||||
void cast_server(void *pParm)
|
||||
{
|
||||
osiSockAddrNode *paddrNode;
|
||||
cast_config *conf = pParm;
|
||||
osiSockAddr *paddrNode = &conf->pAddr;
|
||||
struct sockaddr_in sin;
|
||||
int status;
|
||||
int count=0;
|
||||
struct sockaddr_in new_recv_addr;
|
||||
osiSocklen_t recv_addr_size;
|
||||
osiSockIoctl_t nchars;
|
||||
SOCKET recv_sock;
|
||||
|
||||
recv_addr_size = sizeof(new_recv_addr);
|
||||
|
||||
if( IOC_cast_sock!=0 && IOC_cast_sock!=INVALID_SOCKET ) {
|
||||
epicsSocketDestroy ( IOC_cast_sock );
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the socket.
|
||||
* Use ARPA Internet address format and datagram socket.
|
||||
*/
|
||||
|
||||
if ( ( IOC_cast_sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0) ) == INVALID_SOCKET ) {
|
||||
if ( ( recv_sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0) ) == INVALID_SOCKET ) {
|
||||
epicsPrintf ("CAS: cast socket creation error\n");
|
||||
epicsThreadSuspendSelf ();
|
||||
}
|
||||
|
||||
if(conf->reply_sock==INVALID_SOCKET)
|
||||
conf->reply_sock = recv_sock; /* assume that the socket capable of unicast send is created first */
|
||||
|
||||
/*
|
||||
* some concern that vxWorks will run out of mBuf's
|
||||
* if this change is made
|
||||
@@ -164,19 +165,17 @@ void cast_server(void *pParm)
|
||||
}
|
||||
#endif
|
||||
|
||||
epicsSocketEnableAddressUseForDatagramFanout ( IOC_cast_sock );
|
||||
epicsSocketEnableAddressUseForDatagramFanout ( recv_sock );
|
||||
|
||||
paddrNode = (osiSockAddrNode *) ellFirst ( &casIntfAddrList );
|
||||
|
||||
memcpy(&sin, &paddrNode->addr.ia, sizeof (sin));
|
||||
memcpy(&sin, &paddrNode->ia, sizeof (sin));
|
||||
|
||||
/* get server's Internet address */
|
||||
if( bind(IOC_cast_sock, (struct sockaddr *)&sin, sizeof (sin)) < 0){
|
||||
if( bind(recv_sock, (struct sockaddr *)&sin, sizeof (sin)) < 0){
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
epicsPrintf ("CAS: UDP server port bind error was \"%s\"\n", sockErrBuf );
|
||||
epicsSocketDestroy ( IOC_cast_sock );
|
||||
epicsSocketDestroy ( recv_sock );
|
||||
epicsThreadSuspendSelf ();
|
||||
}
|
||||
|
||||
@@ -186,7 +185,7 @@ void cast_server(void *pParm)
|
||||
*
|
||||
*/
|
||||
while ( TRUE ) {
|
||||
prsrv_cast_client = create_client ( IOC_cast_sock, IPPROTO_UDP );
|
||||
prsrv_cast_client = create_client ( conf->reply_sock, IPPROTO_UDP );
|
||||
if ( prsrv_cast_client ) {
|
||||
break;
|
||||
}
|
||||
@@ -200,11 +199,15 @@ void cast_server(void *pParm)
|
||||
*/
|
||||
rsrv_version_reply ( prsrv_cast_client );
|
||||
|
||||
/* these pointers become invalid after signaling casudp_startStopEvent */
|
||||
conf = NULL;
|
||||
paddrNode = NULL;
|
||||
|
||||
epicsEventSignal(casudp_startStopEvent);
|
||||
|
||||
while (TRUE) {
|
||||
status = recvfrom (
|
||||
IOC_cast_sock,
|
||||
recv_sock,
|
||||
prsrv_cast_client->recv.buf,
|
||||
prsrv_cast_client->recv.maxstk,
|
||||
0,
|
||||
@@ -292,7 +295,7 @@ void cast_server(void *pParm)
|
||||
* allow messages to batch up if more are comming
|
||||
*/
|
||||
nchars = 0; /* supress purify warning */
|
||||
status = socket_ioctl(IOC_cast_sock, FIONREAD, &nchars);
|
||||
status = socket_ioctl(recv_sock, FIONREAD, &nchars);
|
||||
if (status<0) {
|
||||
errlogPrintf ("CA cast server: Unable to fetch N characters pending\n");
|
||||
cas_send_dg_msg (prsrv_cast_client);
|
||||
|
||||
@@ -74,7 +74,6 @@ void rsrv_online_notify_task(void *pParm)
|
||||
char buf[16];
|
||||
unsigned priorityOfUDP;
|
||||
epicsThreadBooleanStatus tbs;
|
||||
epicsThreadId tid;
|
||||
|
||||
taskwdInsert (epicsThreadGetIdSelf(),NULL,NULL);
|
||||
|
||||
@@ -223,14 +222,59 @@ void rsrv_online_notify_task(void *pParm)
|
||||
casudp_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
casudp_ctl = ctlPause;
|
||||
|
||||
tid = epicsThreadCreate ( "CAS-UDP", priorityOfUDP,
|
||||
epicsThreadGetStackSize (epicsThreadStackMedium),
|
||||
cast_server, 0 );
|
||||
if ( tid == 0 ) {
|
||||
epicsPrintf ( "CAS: unable to start UDP daemon thread\n" );
|
||||
{
|
||||
/* casudp_startStopEvent ensures that this struct
|
||||
* lives until the cast_server thread(s) are done with it.
|
||||
*/
|
||||
cast_config config;
|
||||
|
||||
config.pAddr = ((osiSockAddrNode *) ellFirst ( &casIntfAddrList ))->addr;
|
||||
config.reply_sock = INVALID_SOCKET;
|
||||
|
||||
epicsThreadMustCreate ( "CAS-UDP", priorityOfUDP,
|
||||
epicsThreadGetStackSize (epicsThreadStackMedium),
|
||||
cast_server, &config );
|
||||
|
||||
epicsEventMustWait(casudp_startStopEvent);
|
||||
|
||||
#if !defined(_WIN32)
|
||||
/* An oddness of BSD sockets (not winsock) is that binding to
|
||||
* INADDR_ANY will receive unicast and broadcast, but binding to
|
||||
* a specific interface address receives only unicast. The trick
|
||||
* is to bind a second socket to the interface broadcast address,
|
||||
* which will then receive only broadcasts.
|
||||
*/
|
||||
if (config.pAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) {
|
||||
ELLLIST bcastList = ELLLIST_INIT;
|
||||
|
||||
osiSockDiscoverBroadcastAddresses (&bcastList,
|
||||
sock, &config.pAddr); // match addr
|
||||
|
||||
if(ellCount(&bcastList)==0) {
|
||||
errlogPrintf("CAS UDP: failed to find interface broadcast address\n");
|
||||
|
||||
} else {
|
||||
osiSockAddrNode *pNode = (osiSockAddrNode*)ellFirst(&bcastList);
|
||||
assert(config.pAddr.sa.sa_family==pNode->addr.sa.sa_family);
|
||||
|
||||
if (config.pAddr.ia.sin_addr.s_addr != pNode->addr.ia.sin_addr.s_addr) {
|
||||
|
||||
/* copy the address, keep the port */
|
||||
config.pAddr.ia.sin_addr.s_addr = pNode->addr.ia.sin_addr.s_addr;
|
||||
|
||||
epicsThreadMustCreate ( "CAS-UDPB", priorityOfUDP,
|
||||
epicsThreadGetStackSize (epicsThreadStackMedium),
|
||||
cast_server, &config );
|
||||
|
||||
epicsEventMustWait(casudp_startStopEvent);
|
||||
}
|
||||
}
|
||||
|
||||
ellFree(&bcastList);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
epicsEventMustWait(casudp_startStopEvent);
|
||||
epicsEventSignal(beacon_startStopEvent);
|
||||
|
||||
while (TRUE) {
|
||||
|
||||
@@ -137,6 +137,10 @@ struct event_ext {
|
||||
char modified; /* mod & ev flw ctrl enbl */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
osiSockAddr pAddr;
|
||||
SOCKET reply_sock;
|
||||
} cast_config;
|
||||
|
||||
enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
|
||||
|
||||
@@ -161,7 +165,6 @@ enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
|
||||
|
||||
GLBLTYPE int CASDEBUG;
|
||||
GLBLTYPE SOCKET IOC_sock;
|
||||
GLBLTYPE SOCKET IOC_cast_sock;
|
||||
GLBLTYPE unsigned short ca_server_port;
|
||||
GLBLTYPE ELLLIST clientQ; /* locked by clientQlock */
|
||||
GLBLTYPE ELLLIST beaconAddrList;
|
||||
|
||||
Reference in New Issue
Block a user