XRootD
Loading...
Searching...
No Matches
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"
48#include "XrdNet/XrdNetUtils.hh"
49
50#include "XrdSys/XrdSysFD.hh"
51#include "XrdSys/XrdSysError.hh"
53
54/******************************************************************************/
55/* G l o b a l O b j e c t s */
56/******************************************************************************/
57
58namespace XrdNetSocketCFG
59{
60XrdNetRefresh* NetRefresh; // This may not be deleted once allocated
61
62int udpRefr = 8*60*60;
63};
64using 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
70namespace
71{
72XrdScheduler* schedP;
73XrdSysError eDest(0, "XrdNet");
74
75struct 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
85std::map<int, RefInfo> fd2Info;
86
87XrdSysMutex 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
110bool 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//
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
216bool 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
267void 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//
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
friend class XrdScheduler
Definition XrdJob.hh:44
const XrdNetSockAddr * NetAddr()
const char * Set(const char *hSpec, int pNum=PortInSpec)
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)
void Schedule(XrdJob *jp)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
XrdNetRefresh * NetRefresh
XrdScheduler * schedP