diff --git a/src/ioc/rsrv/cast_server.c b/src/ioc/rsrv/cast_server.c index 2932f2b62..6089a078a 100644 --- a/src/ioc/rsrv/cast_server.c +++ b/src/ioc/rsrv/cast_server.c @@ -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); diff --git a/src/ioc/rsrv/online_notify.c b/src/ioc/rsrv/online_notify.c index 6ccc5e862..ea60d9b1d 100644 --- a/src/ioc/rsrv/online_notify.c +++ b/src/ioc/rsrv/online_notify.c @@ -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) { diff --git a/src/ioc/rsrv/server.h b/src/ioc/rsrv/server.h index c45728593..21016fab8 100644 --- a/src/ioc/rsrv/server.h +++ b/src/ioc/rsrv/server.h @@ -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;