This is Unofficial EPICS BASE Doxygen Site
osdNetIntf.c
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2002 The University of Chicago, as Operator of Argonne
3 * National Laboratory.
4 * Copyright (c) 2002 The Regents of the University of California, as
5 * Operator of Los Alamos National Laboratory.
6 * EPICS BASE Versions 3.13.7
7 * and higher are distributed subject to a Software License Agreement found
8 * in file LICENSE that is included with this distribution.
9 \*************************************************************************/
10 
11 /*
12  * Author: Jeff Hill
13  * Date: 04-05-94
14  */
15 
16 #include <ctype.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 
21 #include "osiSock.h"
22 #include "epicsAssert.h"
23 #include "errlog.h"
24 #include "epicsThread.h"
25 
26 #ifdef DEBUG
27 # define ifDepenDebugPrintf(argsInParen) printf argsInParen
28 #else
29 # define ifDepenDebugPrintf(argsInParen)
30 #endif
31 
32 static osiSockAddr osiLocalAddrResult;
33 static epicsThreadOnceId osiLocalAddrId = EPICS_THREAD_ONCE_INIT;
34 
35 /*
36  * Determine the size of an ifreq structure
37  * Made difficult by the fact that addresses larger than the structure
38  * size may be returned from the kernel.
39  */
40 static size_t ifreqSize ( struct ifreq *pifreq )
41 {
42  size_t size;
43 
44  size = ifreq_size ( pifreq );
45  if ( size < sizeof ( *pifreq ) ) {
46  size = sizeof ( *pifreq );
47  }
48  return size;
49 }
50 
51 /*
52  * Move to the next ifreq structure
53  */
54 static struct ifreq * ifreqNext ( struct ifreq *pifreq )
55 {
56  struct ifreq *ifr;
57 
58  ifr = ( struct ifreq * )( ifreqSize (pifreq) + ( char * ) pifreq );
59  ifDepenDebugPrintf( ("ifreqNext() pifreq %p, size 0x%x, ifr 0x%p\n", pifreq, (unsigned)ifreqSize (pifreq), ifr) );
60  return ifr;
61 }
62 
63 
64 /*
65  * osiSockDiscoverBroadcastAddresses ()
66  */
67 LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses
68  (ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr)
69 {
70  static const unsigned nelem = 100;
71  int status;
72  struct ifconf ifconf;
73  struct ifreq *pIfreqList;
74  struct ifreq *pIfreqListEnd;
75  struct ifreq *pifreq;
76  struct ifreq *pnextifreq;
77  osiSockAddrNode *pNewNode;
78 
79  if ( pMatchAddr->sa.sa_family == AF_INET ) {
80  if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) {
81  pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
82  if ( pNewNode == NULL ) {
83  errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
84  return;
85  }
86  pNewNode->addr.ia.sin_family = AF_INET;
87  pNewNode->addr.ia.sin_port = htons ( 0 );
88  pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
89  ellAdd ( pList, &pNewNode->node );
90  return;
91  }
92  }
93 
94  /*
95  * use pool so that we avoid using too much stack space
96  *
97  * nelem is set to the maximum interfaces
98  * on one machine here
99  */
100  pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pifreq) );
101  if (!pIfreqList) {
102  errlogPrintf ("osiSockDiscoverBroadcastAddresses(): no memory to complete request\n");
103  return;
104  }
105 
106  ifconf.ifc_len = nelem * sizeof(*pifreq);
107  ifconf.ifc_req = pIfreqList;
108  status = socket_ioctl (socket, SIOCGIFCONF, &ifconf);
109  if (status < 0 || ifconf.ifc_len == 0) {
110  errlogPrintf ("osiSockDiscoverBroadcastAddresses(): unable to fetch network interface configuration (%d)\n", status);
111  free (pIfreqList);
112  return;
113  }
114 
115  pIfreqListEnd = (struct ifreq *) (ifconf.ifc_len + (char *) pIfreqList);
116  pIfreqListEnd--;
117 
118  for ( pifreq = pIfreqList; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) {
119  uint32_t current_ifreqsize;
120 
121  /*
122  * find the next ifreq
123  */
124  pnextifreq = ifreqNext (pifreq);
125 
126  /* determine ifreq size */
127  current_ifreqsize = ifreqSize ( pifreq );
128  /* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */
129  memmove(pIfreqList, pifreq, current_ifreqsize);
130 
131  ifDepenDebugPrintf (("osiSockDiscoverBroadcastAddresses(): found IFACE: %s len: 0x%x current_ifreqsize: 0x%x \n",
132  pIfreqList->ifr_name,
133  (unsigned)ifreq_size(pifreq),
134  (unsigned)current_ifreqsize));
135 
136  /*
137  * If its not an internet interface then dont use it
138  */
139  if ( pIfreqList->ifr_addr.sa_family != AF_INET ) {
140  ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) );
141  continue;
142  }
143 
144  /*
145  * if it isnt a wildcarded interface then look for
146  * an exact match
147  */
148  if ( pMatchAddr->sa.sa_family != AF_UNSPEC ) {
149  if ( pMatchAddr->sa.sa_family != AF_INET ) {
150  continue;
151  }
152  if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) {
153  struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr;
154  if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
155  ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) );
156  continue;
157  }
158  }
159  }
160 
161  status = socket_ioctl ( socket, SIOCGIFFLAGS, pIfreqList );
162  if ( status ) {
163  errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf flags fetch for \"%s\" failed\n", pIfreqList->ifr_name);
164  continue;
165  }
166  ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" flags: %x\n", pIfreqList->ifr_name, pIfreqList->ifr_flags) );
167 
168  /*
169  * dont bother with interfaces that have been disabled
170  */
171  if ( ! ( pIfreqList->ifr_flags & IFF_UP ) ) {
172  ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" was down\n", pIfreqList->ifr_name) );
173  continue;
174  }
175 
176  /*
177  * dont use the loop back interface
178  */
179  if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) {
180  ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): ignoring loopback interface: \"%s\"\n", pIfreqList->ifr_name) );
181  continue;
182  }
183 
184  pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
185  if ( pNewNode == NULL ) {
186  errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
187  free ( pIfreqList );
188  return;
189  }
190 
191  /*
192  * If this is an interface that supports
193  * broadcast fetch the broadcast address.
194  *
195  * Otherwise if this is a point to point
196  * interface then use the destination address.
197  *
198  * Otherwise CA will not query through the
199  * interface.
200  */
201  if ( pIfreqList->ifr_flags & IFF_BROADCAST ) {
202  osiSockAddr baddr;
203  status = socket_ioctl (socket, SIOCGIFBRDADDR, pIfreqList);
204  if ( status ) {
205  errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name);
206  free ( pNewNode );
207  continue;
208  }
209  baddr.sa = pIfreqList->ifr_broadaddr;
210  if (baddr.ia.sin_family==AF_INET && baddr.ia.sin_addr.s_addr != INADDR_ANY) {
211  pNewNode->addr.sa = pIfreqList->ifr_broadaddr;
212  ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
213  } else {
214  ifDepenDebugPrintf ( ( "Ignoring broadcast addr = \n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
215  free ( pNewNode );
216  continue;
217  }
218  }
219 #if defined (IFF_POINTOPOINT)
220  else if ( pIfreqList->ifr_flags & IFF_POINTOPOINT ) {
221  status = socket_ioctl ( socket, SIOCGIFDSTADDR, pIfreqList);
222  if ( status ) {
223  ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": pt to pt addr fetch fail\n", pIfreqList->ifr_name) );
224  free ( pNewNode );
225  continue;
226  }
227  pNewNode->addr.sa = pIfreqList->ifr_dstaddr;
228  }
229 #endif
230  else {
231  ifDepenDebugPrintf ( ( "osiSockDiscoverBroadcastAddresses(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) );
232  free ( pNewNode );
233  continue;
234  }
235 
236  ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" found\n", pIfreqList->ifr_name) );
237 
238  /*
239  * LOCK applied externally
240  */
241  ellAdd ( pList, &pNewNode->node );
242  }
243 
244  free ( pIfreqList );
245 }
246 
247 /*
248  * osiLocalAddr ()
249  */
250 static void osiLocalAddrOnce (void *raw)
251 {
252  SOCKET *psocket = raw;
253  const unsigned nelem = 100;
254  osiSockAddr addr;
255  int status;
256  struct ifconf ifconf;
257  struct ifreq *pIfreqList;
258  struct ifreq *pifreq;
259  struct ifreq *pIfreqListEnd;
260  struct ifreq *pnextifreq;
261 
262  memset ( (void *) &addr, '\0', sizeof ( addr ) );
263  addr.sa.sa_family = AF_UNSPEC;
264 
265  pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pIfreqList) );
266  if ( ! pIfreqList ) {
267  errlogPrintf ( "osiLocalAddr(): no memory to complete request\n" );
268  goto fail;
269  }
270 
271  ifconf.ifc_len = nelem * sizeof ( *pIfreqList );
272  ifconf.ifc_req = pIfreqList;
273  status = socket_ioctl ( *psocket, SIOCGIFCONF, &ifconf );
274  if ( status < 0 || ifconf.ifc_len == 0 ) {
275  char sockErrBuf[64];
277  sockErrBuf, sizeof ( sockErrBuf ) );
278  errlogPrintf (
279  "osiLocalAddr(): SIOCGIFCONF ioctl failed because \"%s\"\n",
280  sockErrBuf );
281  goto fail;
282  }
283 
284  pIfreqListEnd = (struct ifreq *) ( ifconf.ifc_len + (char *) ifconf.ifc_req );
285  pIfreqListEnd--;
286 
287  for ( pifreq = ifconf.ifc_req; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) {
288  osiSockAddr addrCpy;
289  uint32_t current_ifreqsize;
290 
291  /*
292  * find the next if req
293  */
294  pnextifreq = ifreqNext ( pifreq );
295 
296  /* determine ifreq size */
297  current_ifreqsize = ifreqSize ( pifreq );
298  /* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */
299  memmove(pIfreqList, pifreq, current_ifreqsize);
300 
301  if ( pIfreqList->ifr_addr.sa_family != AF_INET ) {
302  ifDepenDebugPrintf ( ("osiLocalAddr(): interface %s was not AF_INET\n", pIfreqList->ifr_name) );
303  continue;
304  }
305 
306  addrCpy.sa = pIfreqList->ifr_addr;
307 
308  status = socket_ioctl ( *psocket, SIOCGIFFLAGS, pIfreqList );
309  if ( status < 0 ) {
310  errlogPrintf ( "osiLocalAddr(): net intf flags fetch for %s failed\n", pIfreqList->ifr_name );
311  continue;
312  }
313 
314  if ( ! ( pIfreqList->ifr_flags & IFF_UP ) ) {
315  ifDepenDebugPrintf ( ("osiLocalAddr(): net intf %s was down\n", pIfreqList->ifr_name) );
316  continue;
317  }
318 
319  /*
320  * dont use the loop back interface
321  */
322  if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) {
323  ifDepenDebugPrintf ( ("osiLocalAddr(): ignoring loopback interface: %s\n", pIfreqList->ifr_name) );
324  continue;
325  }
326 
327  ifDepenDebugPrintf ( ("osiLocalAddr(): net intf %s found\n", pIfreqList->ifr_name) );
328 
329  osiLocalAddrResult = addrCpy;
330  free ( pIfreqList );
331  return;
332  }
333 
334  errlogPrintf (
335  "osiLocalAddr(): only loopback found\n");
336 fail:
337  /* fallback to loopback */
338  memset ( (void *) &addr, '\0', sizeof ( addr ) );
339  addr.ia.sin_family = AF_INET;
340  addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
341  osiLocalAddrResult = addr;
342 
343  free ( pIfreqList );
344 }
345 
346 
347 LIBCOM_API osiSockAddr epicsStdCall osiLocalAddr (SOCKET socket)
348 {
349  epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, &socket);
350  return osiLocalAddrResult;
351 }
LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses(ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr)
Definition: osdNetIntf.c:68
#define INADDR_LOOPBACK
Definition: osdSock.h:76
osiSockAddr addr
Definition: osiSock.h:163
pvd::Status status
An EPICS-specific replacement for ANSI C&#39;s assert.
struct sockaddr sa
Definition: osiSock.h:158
struct sockaddr_in ia
Definition: osiSock.h:157
#define NULL
Definition: catime.c:38
#define ifDepenDebugPrintf(argsInParen)
Definition: osdNetIntf.c:29
#define ifreq_size(pifreq)
Definition: osdSock.h:71
void epicsSocketConvertErrnoToString(char *pBuf, unsigned bufSize)
#define socket_ioctl(A, B, C)
Definition: osdSock.h:34
void ellAdd(ELLLIST *pList, ELLNODE *pNode)
Adds a node to the end of a list.
Definition: ellLib.c:24
LIBCOM_API osiSockAddr epicsStdCall osiLocalAddr(SOCKET socket)
Definition: osdNetIntf.c:347
#define EPICS_THREAD_ONCE_INIT
Definition: epicsThread.h:109
LIBCOM_API void epicsStdCall epicsThreadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg)
int SOCKET
Definition: osdSock.h:31
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
ELLNODE node
Definition: osiSock.h:162
if(yy_init)
Definition: scan.c:972
List header type.
Definition: ellLib.h:56
C++ and C descriptions for a thread.