From 64da181e6fe76c326715fc7eec1f4196d11c1416 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 11 Jan 2016 20:59:08 -0500 Subject: [PATCH] expand beacon address list when binding to wildcard --- src/ioc/rsrv/caservertask.c | 259 +++++++++++++++++++++-------------- src/ioc/rsrv/online_notify.c | 9 +- src/ioc/rsrv/server.h | 10 +- 3 files changed, 164 insertions(+), 114 deletions(-) diff --git a/src/ioc/rsrv/caservertask.c b/src/ioc/rsrv/caservertask.c index 0a9873c44..e6b572c95 100644 --- a/src/ioc/rsrv/caservertask.c +++ b/src/ioc/rsrv/caservertask.c @@ -252,6 +252,115 @@ SOCKET* rsrv_grap_tcp(unsigned short *port) return socks; } +static +void rsrv_build_addr_lists(void) +{ + ELLLIST beacon_list = ELLLIST_INIT; + /* expandbcast==0 - add bcast addresses corresponding to the provided interface addresses. + * expandbcast==1 - binding to wildcard. Fill beaconAddrList with all local addresses + */ + int expandbcast = 0, autobeaconlist = 1; + + /* the UDP ports are known at this point, but the TCP port is not */ + assert(ca_beacon_port!=0); + assert(ca_udp_port!=0); + + envGetBoolConfigParam(&EPICS_CAS_AUTO_BEACON_ADDR_LIST, &autobeaconlist); + + ellInit ( &casIntfAddrList ); + ellInit ( &beaconAddrList ); + + if(addAddrToChannelAccessAddressList ( &casIntfAddrList, &EPICS_CAS_INTF_ADDR_LIST, 0, 0 )) + addAddrToChannelAccessAddressList ( &casIntfAddrList, &EPICS_CAS_BEACON_ADDR_LIST, 0, 0 ); + + if (ellCount(&casIntfAddrList) == 0) { + /* default to wildcard 0.0.0.0 */ + osiSockAddrNode *pNode = (osiSockAddrNode *) callocMustSucceed( 1, sizeof(*pNode), "rsrv_init" ); + pNode->addr.ia.sin_family = AF_INET; + pNode->addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); + pNode->addr.ia.sin_port = 0; + ellAdd ( &casIntfAddrList, &pNode->node ); + expandbcast = 1; + + } else { + /* check user provided list */ + osiSockAddrNode *pNode, *pNext; + for(pNode = (osiSockAddrNode*)ellFirst(&casIntfAddrList), + pNext = pNode ? (osiSockAddrNode*)ellNext(&pNode->node) : NULL; + pNode; + pNode = pNext, + pNext = pNext ? (osiSockAddrNode*)ellNext(&pNext->node) : NULL) + { + if(pNode->addr.ia.sin_family==AF_INET && pNode->addr.ia.sin_addr.s_addr==htonl(INADDR_ANY)) + { + if (ellCount(&casIntfAddrList) != 1) { + fprintf(stderr, "CAS address list can not contain 0.0.0.0 and other addresses, ignoring...\n"); + ellDelete(&casIntfAddrList, &pNode->node); + free(pNode); + } else { + expandbcast = 1; + } + } + } + } + + addAddrToChannelAccessAddressList ( &beaconAddrList, + &EPICS_CAS_BEACON_ADDR_LIST, ca_beacon_port, 0 ); + + beaconSocket = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); + if (beaconSocket==INVALID_SOCKET) + cantProceed("socket allocation failed during address list expansion"); + + + { + int intTrue = 1; + if (setsockopt (beaconSocket, SOL_SOCKET, SO_BROADCAST, + (char *)&intTrue, sizeof(intTrue))<0) { + cantProceed("CAS: online socket set up error\n"); + } + } + + if (!autobeaconlist) { + /* only user provided addresses */ + } else if (expandbcast) { + /* add bcast addresses of all local interfaces */ + osiSockAddr match; + memset(&match, 0, sizeof(match)); + match.ia.sin_family = AF_INET; + match.ia.sin_addr.s_addr = htonl(INADDR_ANY); + match.ia.sin_port = htons(ca_beacon_port); + + osiSockDiscoverBroadcastAddresses(&beacon_list, beaconSocket, &match); + + } else { + /* add bcast addresses of only specified interfaces */ + osiSockAddrNode *pNode; + + for(pNode = (osiSockAddrNode*)ellFirst(&casIntfAddrList); + pNode; + pNode = (osiSockAddrNode*)ellNext(&pNode->node)) + { + osiSockDiscoverBroadcastAddresses(&beacon_list, beaconSocket, &pNode->addr); + } + } + + { + /* set the port for any automatically discovered destinations. */ + osiSockAddrNode *pNode; + for(pNode = (osiSockAddrNode*)ellFirst(&beacon_list); + pNode; + pNode = (osiSockAddrNode*)ellNext(&pNode->node)) + { + pNode->addr.ia.sin_port = htons(ca_beacon_port); + } + } + + ellConcat(&beaconAddrList, &beacon_list); + + if (ellCount(&beaconAddrList)==0) + fprintf(stderr, "Warning: RSRV has empty beacon address list\n"); +} + static dbServer rsrv_server = { ELLNODE_INIT, "rsrv", @@ -267,7 +376,6 @@ int rsrv_init (void) { long maxBytesAsALong; long status; - unsigned short udp_port, beacon_port; SOCKET *socks; clientQlock = epicsMutexMustCreate(); @@ -293,14 +401,14 @@ int rsrv_init (void) ca_server_port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, (unsigned short) CA_SERVER_PORT ); } - udp_port = ca_server_port; + ca_udp_port = ca_server_port; if (envGetConfigParamPtr(&EPICS_CAS_BEACON_PORT)) { - beacon_port = envGetInetPortConfigParam (&EPICS_CAS_BEACON_PORT, + ca_beacon_port = envGetInetPortConfigParam (&EPICS_CAS_BEACON_PORT, (unsigned short) CA_REPEATER_PORT ); } else { - beacon_port = envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT, + ca_beacon_port = envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT, (unsigned short) CA_REPEATER_PORT ); } @@ -328,21 +436,11 @@ int rsrv_init (void) } } freeListInitPvt ( &rsrvLargeBufFreeListTCP, rsrvSizeofLargeBufTCP, 1 ); - ellInit ( &casIntfAddrList ); - ellInit ( &beaconAddrList ); pCaBucket = bucketCreate(CAS_HASH_TABLE_SIZE); if (!pCaBucket) cantProceed("RSRV failed to allocate ID lookup table\n"); - addAddrToChannelAccessAddressList ( &casIntfAddrList, - &EPICS_CAS_INTF_ADDR_LIST, ca_server_port, 0 ); - if (ellCount(&casIntfAddrList) == 0) { - osiSockAddrNode *pNode = (osiSockAddrNode *) callocMustSucceed( 1, sizeof(*pNode), "rsrv_init" ); - pNode->addr.ia.sin_family = AF_INET; - pNode->addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); - pNode->addr.ia.sin_port = htons ( ca_server_port ); - ellAdd ( &casIntfAddrList, &pNode->node ); - } + rsrv_build_addr_lists(); castcp_startStopEvent = epicsEventMustCreate(epicsEventEmpty); casudp_startStopEvent = epicsEventMustCreate(epicsEventEmpty); @@ -409,77 +507,7 @@ int rsrv_init (void) ipAddrToDottedIP (&conf->tcpAddr.ia, ifaceName, sizeof(ifaceName)); - conf->udp = conf->udpbcast = conf->udpbeacon = INVALID_SOCKET; - - /* create and bind UDP beacon socket */ - - conf->udpbeacon = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); - if(conf->udpbeacon==INVALID_SOCKET) - cantProceed("rsrv_init ran out of udp sockets for beacon at %s", ifaceName); - - /* beacon sender binds to a random port, and won't actually receive anything */ - conf->udpbeaconRx = conf->tcpAddr; - conf->udpbeaconRx.ia.sin_port = 0; - - if(tryBind(conf->udpbeacon, &conf->udpbeaconRx, "UDP beacon socket")) - goto cleanup; - - - { - int intTrue = 1; - if (setsockopt (conf->udpbeacon, SOL_SOCKET, SO_BROADCAST, - (char *)&intTrue, sizeof(intTrue))<0) { - errlogPrintf ("CAS: online socket set up error\n"); - epicsThreadSuspendSelf (); - } - - /* - * this connect is to supress a warning message on Linux - * when we shutdown the read side of the socket. If it - * fails (and it will on old ip kernels) we just ignore - * the failure. - */ - osiSockAddr sockAddr; - sockAddr.ia.sin_family = AF_UNSPEC; - sockAddr.ia.sin_port = htons ( 0 ); - sockAddr.ia.sin_addr.s_addr = htonl (0); - connect ( conf->udpbeacon, & sockAddr.sa, sizeof ( sockAddr.sa ) ); - shutdown ( conf->udpbeacon, SHUT_RD ); - } - - /* find interface broadcast address */ - { - ELLLIST bcastList = ELLLIST_INIT; - osiSockAddrNode *pNode; - - osiSockDiscoverBroadcastAddresses (&bcastList, - conf->udpbeacon, &conf->udpbeaconRx); // match addr - - if(ellCount(&bcastList)==0) { - cantProceed("Can't find broadcast address of interface %s\n", ifaceName); - } else if(ellCount(&bcastList)>1 && conf->udpbeaconRx.ia.sin_addr.s_addr!=htonl(INADDR_ANY)) { - printf("Interface %s has more than one broadcast address?\n", ifaceName); - } - - pNode = (osiSockAddrNode*)ellFirst(&bcastList); - - /* beacons are sent to a well known port w/ the iface bcast addr */ - conf->udpbeaconTx = conf->udpbeaconRx; - conf->udpbeaconTx.ia.sin_addr = pNode->addr.ia.sin_addr; - conf->udpbeaconTx.ia.sin_port = htons(beacon_port); - - if(connect(conf->udpbeacon, &conf->udpbeaconTx.sa, sizeof(conf->udpbeaconTx))!=0) - { - char sockErrBuf[64], buf[40]; - epicsSocketConvertErrnoToString ( - sockErrBuf, sizeof ( sockErrBuf ) ); - ipAddrToDottedIP (&pNode->addr.ia, buf, sizeof(buf)); - cantProceed( "%s: CA beacon routing (connect to \"%s\") error was \"%s\"\n", - __FILE__, buf, sockErrBuf); - } - - /* TODO: free bcastList */ - } + conf->udp = conf->udpbcast = INVALID_SOCKET; /* create and bind UDP name receiver socket(s) */ @@ -488,7 +516,7 @@ int rsrv_init (void) cantProceed("rsrv_init ran out of udp sockets"); conf->udpAddr = conf->tcpAddr; - conf->udpAddr.ia.sin_port = htons(udp_port); + conf->udpAddr.ia.sin_port = htons(ca_udp_port); epicsSocketEnableAddressUseForDatagramFanout ( conf->udp ); @@ -503,18 +531,36 @@ int rsrv_init (void) * which will then receive only broadcasts. */ if(conf->udpAddr.ia.sin_addr.s_addr!=htonl(INADDR_ANY)) { + /* find interface broadcast address */ + ELLLIST bcastList = ELLLIST_INIT; + osiSockAddrNode *pNode; - conf->udpbcast = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); - if(conf->udpbcast==INVALID_SOCKET) - cantProceed("rsrv_init ran out of udp sockets for bcast"); + osiSockDiscoverBroadcastAddresses (&bcastList, + conf->udp, &conf->udpAddr); // match addr - conf->udpbcastAddr = conf->udpAddr; - conf->udpbcastAddr.ia.sin_addr = conf->udpbeaconTx.ia.sin_addr; + if(ellCount(&bcastList)==0) { + fprintf(stderr, "Warning: Can't find broadcast address of interface %s\n" + " Name lookup may not work on this interface\n", ifaceName); + } else { + if(ellCount(&bcastList)>1 && conf->udpAddr.ia.sin_addr.s_addr!=htonl(INADDR_ANY)) + printf("Interface %s has more than one broadcast address?\n", ifaceName); - epicsSocketEnableAddressUseForDatagramFanout ( conf->udpbcast ); + pNode = (osiSockAddrNode*)ellFirst(&bcastList); - if(tryBind(conf->udpbcast, &conf->udpbcastAddr, "UDP Socket bcast")) - goto cleanup; + conf->udpbcast = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); + if(conf->udpbcast==INVALID_SOCKET) + cantProceed("rsrv_init ran out of udp sockets for bcast"); + + epicsSocketEnableAddressUseForDatagramFanout ( conf->udpbcast ); + + conf->udpbcastAddr = conf->udpAddr; + conf->udpbcastAddr.ia.sin_addr.s_addr = pNode->addr.ia.sin_addr.s_addr; + + if(tryBind(conf->udpbcast, &conf->udpbcastAddr, "UDP Socket bcast")) + goto cleanup; + } + + ellFree(&bcastList); } ellAdd(&servers, &conf->node); @@ -554,7 +600,6 @@ int rsrv_init (void) epicsSocketDestroy(conf->tcp); if(conf->udp!=INVALID_SOCKET) epicsSocketDestroy(conf->udp); if(conf->udpbcast!=INVALID_SOCKET) epicsSocketDestroy(conf->udpbcast); - if(conf->udpbeacon!=INVALID_SOCKET) epicsSocketDestroy(conf->udpbeacon); free(conf); } } @@ -738,10 +783,8 @@ void casr (unsigned level) while (client) { char buf[40]; - printf("Server interface\n"); - ipAddrToDottedIP (&client->tcpAddr.ia, buf, sizeof(buf)); - printf(" TCP listener %s\n", buf); + printf("Server interface %s\n", buf); ipAddrToDottedIP (&client->udpAddr.ia, buf, sizeof(buf)); printf(" UDP receiver 1 %s\n", buf); @@ -753,16 +796,22 @@ void casr (unsigned level) } #endif - ipAddrToDottedIP (&client->udpbeaconRx.ia, buf, sizeof(buf)); - printf(" UDP beacon socket bound %s\n", buf); - - ipAddrToDottedIP (&client->udpbeaconTx.ia, buf, sizeof(buf)); - printf(" UDP beacon destination %s\n", buf); - client = (rsrv_iface_config *) ellNext(&client->node); } } UNLOCK_CLIENTQ + if (level>=2) { + osiSockAddrNode * pAddr; + for(pAddr = (osiSockAddrNode*)ellFirst(&beaconAddrList); + pAddr; + pAddr = (osiSockAddrNode*)ellNext(&pAddr->node)) + { + char buf[40]; + + ipAddrToDottedIP (&pAddr->addr.ia, buf, sizeof(buf)); + printf("Beacon destination %s\n", buf); + } + } if (level>=2u) { bytes_reserved = 0u; diff --git a/src/ioc/rsrv/online_notify.c b/src/ioc/rsrv/online_notify.c index 1362c974e..7c3c57fca 100644 --- a/src/ioc/rsrv/online_notify.c +++ b/src/ioc/rsrv/online_notify.c @@ -80,14 +80,15 @@ void rsrv_online_notify_task(void *pParm) ELLNODE *cur; /* send beacon to each interface */ - for(cur=ellFirst(&servers); cur; cur=ellNext(cur)) + for(cur=ellFirst(&beaconAddrList); cur; cur=ellNext(cur)) { - rsrv_iface_config *conf = CONTAINER(cur, rsrv_iface_config, node); - status = send (conf->udpbeacon, (char *)&msg, sizeof(msg), 0); + osiSockAddrNode *pAddr = CONTAINER(cur, osiSockAddrNode, node); + status = sendto (beaconSocket, (char *)&msg, sizeof(msg), 0, + &pAddr->addr.sa, sizeof(pAddr->addr)); if (status < 0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - ipAddrToDottedIP (&conf->udpbeaconTx.ia, buf, sizeof(buf)); + ipAddrToDottedIP (&pAddr->addr.ia, buf, sizeof(buf)); errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n", __FILE__, buf, sockErrBuf); } diff --git a/src/ioc/rsrv/server.h b/src/ioc/rsrv/server.h index fdd3d452e..5162cc103 100644 --- a/src/ioc/rsrv/server.h +++ b/src/ioc/rsrv/server.h @@ -32,6 +32,7 @@ #include "ellLib.h" #include "epicsTime.h" #include "epicsAssert.h" +#include "osiSock.h" #ifdef rsrvRestore_epicsExportSharedSymbols #define epicsExportSharedSymbols @@ -141,10 +142,8 @@ typedef struct { ELLNODE node; osiSockAddr tcpAddr, /* TCP listener endpoint */ udpAddr, /* UDP name unicast receiver endpoint */ - udpbcastAddr, /* UDP name broadcast receiver endpoint */ - udpbeaconRx, /* UDP beacon receiver endpoint ( doesn't actually receive ) */ - udpbeaconTx; /* UDP beacon destination address */ - SOCKET tcp, udp, udpbcast, udpbeacon; + udpbcastAddr; /* UDP name broadcast receiver endpoint */ + SOCKET tcp, udp, udpbcast; unsigned int startbcast:1; } rsrv_iface_config; @@ -171,11 +170,12 @@ enum ctl {ctlInit, ctlRun, ctlPause, ctlExit}; #endif GLBLTYPE int CASDEBUG; -GLBLTYPE unsigned short ca_server_port; +GLBLTYPE unsigned short ca_server_port, ca_udp_port, ca_beacon_port; GLBLTYPE ELLLIST clientQ; /* (TCP clients) locked by clientQlock */ GLBLTYPE ELLLIST clientQudp; /* locked by clientQlock */ GLBLTYPE ELLLIST servers; /* rsrv_iface_config::node, read-only after rsrv_init() */ GLBLTYPE ELLLIST beaconAddrList; +GLBLTYPE SOCKET beaconSocket; GLBLTYPE ELLLIST casIntfAddrList; GLBLTYPE epicsMutexId clientQlock; GLBLTYPE BUCKET *pCaBucket; /* locked by clientQlock */