XRootD
XrdNetRefresh.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d N e t R e f r e s h . c c */
4 /* */
5 /* (c) 2025 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* All Rights Reserved */
7 /* Produced by Andrew Hanushevsky for Stanford University under contract */
8 /* DE-AC02-76-SFO0515 with the Department of Energy */
9 /* */
10 /* This file is part of the XRootD software suite. */
11 /* */
12 /* XRootD is free software: you can redistribute it and/or modify it under */
13 /* the terms of the GNU Lesser General Public License as published by the */
14 /* Free Software Foundation, either version 3 of the License, or (at your */
15 /* option) any later version. */
16 /* */
17 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20 /* License for more details. */
21 /* */
22 /* You should have received a copy of the GNU Lesser General Public License */
23 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25 /* */
26 /* The copyright holder's institutional names and contributor's names may not */
27 /* be used to endorse or promote products derived from this software without */
28 /* specific prior written permission of the institution or contributor. */
29 /******************************************************************************/
30 
31 #include <cstring>
32 #include <ctime>
33 #include <iostream>
34 #include <map>
35 #include <string>
36 
37 #include <unistd.h>
38 
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 
43 #include "Xrd/XrdScheduler.hh"
44 
45 #include "XrdNet/XrdNetAddr.hh"
46 #include "XrdNet/XrdNetPeer.hh"
47 #include "XrdNet/XrdNetRefresh.hh"
48 #include "XrdNet/XrdNetUtils.hh"
49 
50 #include "XrdSys/XrdSysFD.hh"
51 #include "XrdSys/XrdSysError.hh"
52 #include "XrdSys/XrdSysPthread.hh"
53 
54 /******************************************************************************/
55 /* G l o b a l O b j e c t s */
56 /******************************************************************************/
57 
58 namespace XrdNetSocketCFG
59 {
60 XrdNetRefresh* NetRefresh; // This may not be deleted once allocated
61 
62 int udpRefr = 8*60*60;
63 };
64 using namespace XrdNetSocketCFG;
65 
66 /******************************************************************************/
67 /* L o c a l S t a t i c O b j e c t s */
68 /******************************************************************************/
69 
70 namespace
71 {
73 XrdSysError eDest(0, "XrdNet");
74 
75 struct RefInfo
76  {std::string destHN;
77  unsigned int Instance;
78 
79  RefInfo(const char* hname, unsigned int inum)
80  : destHN(hname), Instance(inum) {}
81  ~RefInfo() {}
82 
83  };
84 
85 std::map<int, RefInfo> fd2Info;
86 
87 XrdSysMutex refMTX;
88 }
89 
90 /******************************************************************************/
91 /* D o I t */
92 /******************************************************************************/
93 
95 {
96 
97 // Run the address updater
98 //
99  Update();
100 
101 // Reschedule ourselves
102 //
103  schedP->Schedule(this, time(0)+udpRefr);
104 }
105 
106 /******************************************************************************/
107 /* Private R e g F a i l */
108 /******************************************************************************/
109 
110 bool XrdNetRefresh::RegFail(const char* why)
111 {
112  eDest.Emsg("Refresh", "Peer cannot be registered;", why);
113  return false;
114 }
115 
116 /******************************************************************************/
117 /* R e g i s t e r */
118 /******************************************************************************/
119 
121 {
122  static unsigned int INum = 0;
123 
124 // Make sure we have a valid hostname
125 //
126  if (!Peer.InetName) return RegFail("hostname missing");
127 
128  if (index(Peer.InetName, '!')) return RegFail("invalid hostname");
129 
130 // Make sure file discriptor is not negative
131 //
132  if (Peer.fd < 0) return RegFail("invalid socket descriptor");
133 
134 // Make sure the file descriptor is a UDP socket
135 //
136  struct stat Stat;
137  fstat(Peer.fd, &Stat);
138  if (!S_ISSOCK(Stat.st_mode)) return RegFail("not a socket");
139 
140  int sockType;
141  socklen_t stLen = sizeof(sockType);
142 
143  if (getsockopt(Peer.fd, SOL_SOCKET, SO_TYPE, (void*)&sockType, &stLen)
144  || sockType != SOCK_DGRAM) return RegFail("not a UDP socket");
145 
146 // Constrcut a new info structure to insert into the map
147 //
148  RefInfo newRef(Peer.InetName, INum++);
149 
150 // Add port number to the registration name (v4 and v6 ports are the same)
151 //
152  newRef.destHN.append(":");
153  newRef.destHN.append(std::to_string(ntohs(Peer.Inet.v4.sin_port)));
154 
155 // Lock the map
156 //
157  XrdSysMutexHelper mHelp(refMTX);
158 
159 // Attempt to insert the element, verify that it happened
160 //
161  auto ret = fd2Info.insert(std::pair<int, RefInfo>(Peer.fd, newRef));
162  if (ret.second==false)
163  {char buff[32];
164  snprintf(buff, sizeof(buff), "(%d)", Peer.fd);
165  eDest.Emsg("Refresh", "Peer cannot be registered"
166  "duplicate socket descriptor", buff);
167  return false;
168  }
169 
170 // All done, it went well
171 //
172  return true;
173 }
174 
175 /******************************************************************************/
176 /* S t a r t */
177 /******************************************************************************/
178 
180 {
181 // Set pointers we will need
182 //
183  eDest.logger(logP);
184  schedP = sP;
185 
186 // We allocate an instance of this object so that we can schedule it.
187 //
188  NetRefresh = new XrdNetRefresh();
189 
190 // Now schedule it to refresh UDP destinations
191 //
192  schedP->Schedule(NetRefresh, time(0)+udpRefr);
193 }
194 
195 /******************************************************************************/
196 /* U n R e g i s t e r */
197 /******************************************************************************/
198 
200 {
201  XrdSysMutexHelper mHelp(refMTX);
202 
203 // Attempt to delete the fd from out set
204 //
205  if (!fd2Info.erase(fd))
206  {char buff[32];
207  snprintf(buff, sizeof(buff), "%d)", fd);
208  eDest.Emsg("Refresh", "Atempt to unregisted non-existent fd:",buff);
209  }
210 }
211 
212 /******************************************************************************/
213 /* Private: S e t D e s t */
214 /******************************************************************************/
215 
216 bool XrdNetRefresh::SetDest(int fd, XrdNetSockAddr& netAddr, const char* hName,
217  bool newFam)
218 {
219 // If we haven't changed families, then we have merely changed addresses so
220 // all we need to co is change the connect address which is an atomic op.
221 //
222  if (!newFam)
223  {if (connect(fd, &netAddr.Addr, sizeof(netAddr.Addr)))
224  {eDest.Emsg("Refresh", errno, "set new UDP address for", hName);
225  return false;
226  }
227  }
228 
229 // Since the family changed, we sill need to replace the whole socket
230 // definition with a new one. First establish what kind of socket is needed.
231 //
232  int sFD, sProt = (netAddr.Addr.sa_family == AF_INET6 ? PF_INET6 : PF_INET);
233 
234 // Create the new socket
235 //
236  if ((sFD = XrdSysFD_Socket(sProt, SOCK_DGRAM, 0)) < 0)
237  {eDest.Emsg("Refresh",errno,"create socket for new UDP address for",hName);
238  return false;
239  }
240 
241 // Now set the destination address for this socket
242 //
243  if (connect(sFD, &netAddr.Addr, sizeof(netAddr.Addr)))
244  {eDest.Emsg("Refresh", errno, "init new UDP address for", hName);
245  close(sFD);
246  return false;
247  }
248 
249 // Displace the old socket with our new socket. This is an atomic operation.
250 //
251  if (XrdSysFD_Dup2(sFD, fd) < 0)
252  {eDest.Emsg("Refresh", errno, "replace old UDP address for", hName);
253  close(sFD);
254  return false;
255  }
256 
257 // We are succcessul
258 //
259  close(sFD);
260  return true;
261 }
262 
263 /******************************************************************************/
264 /* U p d a t e */
265 /******************************************************************************/
266 
267 void XrdNetRefresh::Update()
268 {
269  struct UpdInfo
270  {XrdNetSockAddr destIP;
271  unsigned int Instance;
272  };
273  std::map<int, RefInfo> fd2Info_Copy;
274  std::map<int, UpdInfo> fd2Info_Updt;
275 
276 // Make a copy of our registry
277 //
278  refMTX.Lock();
279  fd2Info_Copy = fd2Info;
280  refMTX.UnLock();
281 
282 // For each entry in our local map, resolve the hostname and put the address
283 // in our update map. This prevents us from hold the registry lock across
284 // multiple DNS resolutions which may be lengthy.
285 //
286  for (auto it = fd2Info_Copy.begin(); it != fd2Info_Copy.end(); ++it)
287  {XrdNetAddr hAddr;
288  const char* hName = it->second.destHN.c_str();
289  const char* eText = hAddr.Set(hName, 0);
290 
291  if (eText) eDest.Emsg("Refresh", hName, "resolution failed;", eText);
292  else {const XrdNetSockAddr* netIP = hAddr.NetAddr();
293  if (!netIP)
294  eDest.Emsg("Refresh","Unable to get addr of", hName);
295  else {UpdInfo newInfo = {*netIP, it->second.Instance};
296  fd2Info_Updt.insert(std::pair<int, UpdInfo>
297  (it->first,newInfo));
298  }
299  }
300  }
301 
302 // Now run through all of the updates to see if anything changed. We need
303 // to run with the registry locked to prevent an FD changes which we run.
304 //
305  refMTX.Lock();
306  int numDiff = 0, numUpdt = 0, numFail = 0;
307  for (auto itu = fd2Info_Updt.begin(); itu != fd2Info_Updt.end(); ++itu)
308  {auto itr = fd2Info.find(itu->first);
309  if (itr != fd2Info.end()
310  && itr->second.Instance == itu->second.Instance)
311  {const char* hName = itr->second.destHN.c_str();
312  XrdNetSockAddr curIP;
313  socklen_t cSize = sizeof(curIP.Addr);
314  if (getpeername(itu->first, &curIP.Addr, &cSize))
315  {eDest.Emsg("Refresh",errno,"get current peername of",hName);
316  numFail++;
317  continue;
318  }
319  XrdNetUtils::IPComp result;
320  result = XrdNetUtils::Compare(curIP, itu->second.destIP);
321  if (result == XrdNetUtils::IPSame) continue;
322  numDiff++;
323  if (result != XrdNetUtils::IPDiff)
324  {eDest.Emsg("Refresh", "IP family exception for", hName);
325  numFail++;
326  continue;
327  }
328  bool newFam = (result == XrdNetUtils::IPDFam);
329  itu->second.destIP.v6.sin6_port = curIP.v6.sin6_port;
330  if (SetDest(itu->first,itu->second.destIP,hName,newFam)) numUpdt++;
331  else numFail++;
332  }
333  }
334  refMTX.UnLock();
335 
336 // Format the final message
337 //
338  char mtext[128];
339  snprintf(mtext, sizeof(mtext), "%d of %d IP changed: %d updt %d fail",
340  numDiff, (int)fd2Info_Updt.size(), numUpdt, numFail);
341  eDest.Emsg("Refresh", "Results:", mtext);
342 
343 // Reschedule ourselves
344 //
345  schedP->Schedule(NetRefresh, time(0)+udpRefr);
346 }
struct stat Stat
Definition: XrdCks.cc:49
static XrdSysError eDest(0,"crypto_")
struct sockaddr_in6 v6
struct sockaddr Addr
struct sockaddr_in v4
#define close(a)
Definition: XrdPosix.hh:48
#define fstat(a, b)
Definition: XrdPosix.hh:62
#define stat(a, b)
Definition: XrdPosix.hh:101
const XrdNetSockAddr * NetAddr()
const char * Set(const char *hSpec, int pNum=PortInSpec)
Definition: XrdNetAddr.cc:216
XrdNetSockAddr Inet
Definition: XrdNetPeer.hh:43
char * InetName
Definition: XrdNetPeer.hh:44
virtual void DoIt() override
static void Start(XrdSysLogger *logP, XrdScheduler *sP)
static bool Register(XrdNetPeer &Peer)
static void UnRegister(int fd)
static IPComp Compare(XrdNetSockAddr &ip1, XrdNetSockAddr &ip2, bool *psame=0)
Definition: XrdNetUtils.cc:70
void Schedule(XrdJob *jp)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
XrdSysLogger * logger(XrdSysLogger *lp=0)
Definition: XrdSysError.hh:141
XrdNetRefresh * NetRefresh
XrdScheduler * schedP