XRootD
Loading...
Searching...
No Matches
XrdNetPMarkCfg.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d N e t P M a r k C f g . h h */
4/* */
5/* (c) 2021 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 <map>
32#include <set>
33#include <string>
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39#include <sys/types.h>
40
41#include "XrdNet/XrdNetMsg.hh"
44#include "XrdNet/XrdNetUtils.hh"
45#include "XrdOuc/XrdOuca2x.hh"
46#include "XrdOuc/XrdOucJson.hh"
48#include "XrdOuc/XrdOucProg.hh"
51#include "XrdOuc/XrdOucUtils.hh"
53#include "XrdSys/XrdSysError.hh"
55#include "XrdSys/XrdSysTimer.hh"
56#include "XrdSys/XrdSysTrace.hh"
57
58/******************************************************************************/
59/* L o c a l M a c r o s */
60/******************************************************************************/
61
62#define TRACE(txt) \
63 if (doTrace) SYSTRACE(Trace->, client.tident, epName, 0, txt)
64
65#define DEBUG(txt) \
66 if (doDebug) SYSTRACE(Trace->, 0, epName, 0, txt)
67
68#define DBGID(tid, txt) \
69 if (doDebug) SYSTRACE(Trace->, tid, epName, 0, txt)
70
71#define EPName(ep) const char *epName = ep
72
73/******************************************************************************/
74/* s t a t i c O b j e c t s */
75/******************************************************************************/
76
78{
80 {public:
81 std::string Name; // Activity name
82 int Code; // Act code for role/user
83
84 MapInfo() : Code(0) {}
85 MapInfo(const char *name, int code) : Name(name), Code(code) {}
87};
88
90 {public:
91 std::map<std::string, int> actMap;
92 std::map<std::string, MapInfo> r2aMap;
93 std::map<std::string, MapInfo> u2aMap;
94 short Code;
95 short dAct = -1;
96 bool Roles = false;
97 bool Users = false;
98 bool inUse = false;
99
100 ExpInfo(int code=0) : Code(code) {}
102 };
103
104// Permanent maps to determine the experiment
105//
106std::map<std::string, ExpInfo> expMap;
108std::map<std::string, ExpInfo*> v2eMap;
109
110// Other configuration values
111//
113XrdNetMsg *netMsg = 0; // UDP object for collector
114XrdNetMsg *netOrg = 0; // UDP object for origin
117const char *myHostName = "-";
118const char *myDomain = "";
119
120
122
123char *ffDest = 0;
124int ffEcho = 0;
125static
126const int ffPORT = 10514; // The default port
127int ffPortD = 0; // The dest port to use
128int ffPortO = 0; // The reply port to use
129
130static const int domAny = 0;
131static const int domLcl = 1;
132static const int domRmt = 2;
133
135bool tryPath = false;
136bool tryVO = false;
137bool useDefs = false;
138
139bool useFLbl = false;
140signed char useFFly = -1;
141bool addFLFF = false;
142bool useSTag = true;
143
144bool noFail = true;
145bool doDebug = false;
146bool doTrace = false;
147
151
152 static const int pgmOptN = 6;
153 const char *pgmOpts[pgmOptN] = {0};
154
155 int defsTO =30;
156 std::set<std::string> x2aSet;
157 std::set<std::string> x2eSet;
158
161 };
162
164}
165using namespace XrdNetPMarkConfig;
166
167/******************************************************************************/
168/* S t a t i c M e m b e r s */
169/******************************************************************************/
170
171/******************************************************************************/
172/* B e g i n */
173/******************************************************************************/
174
176 const char *path,
177 const char *cgi,
178 const char *app)
179{
180 EPName("PMBegin");
181 XrdOucString altApp;
182 int eCode, aCode;
183
184// If we need to screen out domains, do that
185//
186 if (chkDom)
187 {XrdNetAddrInfo &addrInfo = *client.addrInfo;
188 char domType = (addrInfo.isPrivate() ? domLcl : domRmt);
189 if (domType == domRmt && *myDomain)
190 {const char *urName = addrInfo.Name();
191 if (urName && XrdNetAddrInfo::isHostName(urName))
192 {const char *dot = index(urName, '.');
193 if (dot && !strcmp(dot+1, myDomain)) domType = domLcl;
194 }
195 }
196 if (domType != chkDom)
197 {DBGID(client.tident, "Skipping sending flow info; unwanted domain");
198 return 0;
199 }
200 }
201
202// Now get the experiment and activity code. If we can't get at least the
203// experiment code, then proceed without marking the flow.
204//
205 if (!getCodes(client, path, cgi, eCode, aCode))
206 {TRACE("Unable to determine experiment; flow not marked.");
207 return 0;
208 }
209
210// Obtain the appname overridefrom the cgi
211//
212 if (cgi) // 01234567890123
213 {const char *apP = strstr(cgi, "pmark.appname=");
214 if (apP)
215 {apP += 14;
216 const char* aP = apP;
217 while(*aP && *aP != '&') aP++;
218 int apLen = aP - apP;
219 if (apLen > 0)
220 {altApp = "";
221 altApp.insert(apP, 0, apLen);
222 app = altApp.c_str();
223 }
224 }
225 }
226
227// Continue with successor function to complete the logic
228//
229 XrdNetPMark::Handle handle(app, eCode, aCode);
230 return Begin(*client.addrInfo, handle, client.tident);
231}
232
233/******************************************************************************/
234
236 XrdNetPMark::Handle &handle,
237 const char *tident)
238{
239
240// If we are allowed to use the flow label set on the incoming connection
241// then try to do so. This is only valid for IPv6 connections. Currently,
242// this is not implemented.
243//
244// if (useFLbl && addrInfo.isIPType(XrdNetAddrInfo::IPv6)
245// && !addrInfo.isMapped())
246// {
247// TODO???
248// }
249
250// If we are allowed to use firefly, return a firefly handle
251//
252 if (handle.Valid() && useFFly)
253 {XrdNetPMarkFF *pmFF = new XrdNetPMarkFF(handle, tident);
254 if (pmFF->Start(addrInfo)) return pmFF;
255 delete pmFF;
256 eDest->Emsg("PMark_Begin", "Unable to start pmark for session",tident);
257 } else {
258 if (useFFly)
259 eDest->Emsg("PMark_Begin", "Flow value is invalid; "
260 "pmark disabled for session", tident);
261 }
262
263// All done, nothing will be pmarked
264//
265 return 0;
266}
267
268/******************************************************************************/
269/* C o n f i g */
270/******************************************************************************/
271
273 XrdSysTrace *trc, bool &fatal)
274{
275 class DelCfgInfo
276 {public: DelCfgInfo(CfgInfo *&cfg) : cfgInfo(cfg) {}
277 ~DelCfgInfo() {if (cfgInfo) {delete cfgInfo; cfgInfo = 0;}}
278 private:
279 CfgInfo *&cfgInfo;
280 } cleanup(Cfg);
281
282// If we have not been configured then simply retrn nil
283//
284 if (!Cfg)
285 {useFFly = false;
286 return 0;
287 }
288
289// Save the message handler
290//
291 eDest = eLog;
292 Sched = sched;
293 Trace = trc;
294 fatal = false;
295
296// If firefly is enabled, make sure we have an ffdest
297//
298 if (useFFly < 0)
299 {if (ffPortD || ffPortO)
300 {useFFly = true;
301 if (!ffPortO) ffPortO = ffPORT;
302 } else {
303 useFFly = false;
304 eLog->Say("Config warning: firefly disabled; "
305 "configuration incomplete!");
306 return 0;
307 }
308 } else if (useFFly && !ffPortO) ffPortO = ffPORT;
309
310// Resolve trace and debug settings
311//
312 if (doDebug) doTrace = true;
313
314// Check if we need a defsfile, if so, construct the map.
315//
316 if (Cfg->x2aSet.size() == 0 && Cfg->x2eSet.size() == 0)
317 {if (Cfg->defsFile.length())
318 eLog->Say("Config warning: ignoring defsfile; "
319 "no mappings have been specified!");
320 useDefs = false;
321 } else {
322 if (!Cfg->defsFile.length())
323 {eLog->Say("Config invalid: pmark mappings cannot be resolved "
324 "without specifying defsfile!");
325 fatal = true;
326 return 0;
327 }
328 useDefs = true;
329 if (!ConfigDefs())
330 {if (useDefs)
331 {fatal = true;
332 return 0;
333 }
334 eLog->Say("Config warning: pmark ignoring defsfile; "
335 "unable to process and nofail is in effect!");
336 }
337 }
338
339// At this point either we still enabled or not. We can be disabled for a
340// number of reasons and appropriate messages will have been issued.
341//
342 if (!useFFly) return 0;
343
344// Create a netmsg object for firefly reporting if a dest was specified
345//
346 bool aOK = false;
347 if (ffDest)
348 {XrdNetAddr spec;
349 char buff[1024];
350 const char *eTxt = spec.Set(ffDest, -ffPortD);
351 if (eTxt)
352 {snprintf(buff, sizeof(buff), "%s:%d; %s", ffDest, ffPortD, eTxt);
353 eLog->Emsg("Config", "pmark unable to create UDP tunnel to", buff);
354 useFFly = false;
355 fatal = true;
356 return 0;
357 }
358 if (spec.Format(buff, sizeof(buff)))
359 netMsg = new XrdNetMsg(eDest, buff, &aOK);
360 if (!aOK)
361 {eLog->Emsg("Config", "pmark unable to create UDP tunnel to", ffDest);
362 fatal = true;
363 delete netMsg;
364 netMsg = 0;
365 useFFly= false;
366 return 0;
367 }
368 }
369
370// Handle the firefly messages to origin
371//
372 if (ffPortO)
373 {netOrg = new XrdNetMsg(eDest, 0, &aOK);
374 if (!aOK)
375 {eLog->Emsg("Config","pmark unable to create origin UDP tunnel");
376 fatal = true;
377 useFFly= false;
378 return 0;
379 }
380 }
381
382// Get our host name.
383//
384 myHostName = XrdNetUtils::MyHostName("-"); // Never deleted!
385
386// Setup for domain checking
387//
388 if (chkDom)
389 {const char *dot = index(myHostName, '.');
390 if (dot) myDomain = dot+1;
391 else eDest->Say("Config warning: Unable to determine local domain; "
392 " domain check restricted to IP address type!");
393 }
394
395// Finally, we are done. Return the packet markling stub.
396//
397 return new XrdNetPMarkCfg;
398}
399
400/******************************************************************************/
401/* Private: C o n f i g D e f s */
402/******************************************************************************/
403
404namespace
405{
406bool Recover()
407{
408 if (!noFail) return false;
409 useDefs = false;
410 return true;
411}
412}
413
414bool XrdNetPMarkCfg::ConfigDefs()
415{
416 class Const2Char
417 {public:
418 char *data;
419 Const2Char(const char *str) : data(strdup(str)) {}
420 ~Const2Char() {free(data);}
421 };
422 EPName("ConfigDefs");
423 std::set<std::string>::iterator it;
424 std::map<std::string, ExpInfo>::iterator itE;
425 bool isDload, aOK = true;
426
427// If we need tp fetch the file, do so.
428//
429 if ((isDload = !(Cfg->defsFile.beginswith('/'))) && !FetchFile())
430 return Recover();
431
432// Now parse the defsfile (it is a json file)
433//
434 aOK = LoadFile();
435
436// Get rid of the defsfile if we dowloaded it
437//
438 if (isDload) unlink(Cfg->defsFile.c_str());
439
440// Only continue if all is well
441//
442 if (!aOK) return Recover();
443
444// Configure the experiment mapping
445//
446 for (it = Cfg->x2eSet.begin(); it != Cfg->x2eSet.end(); it++)
447 {Const2Char pv(it->c_str());
448 if (!ConfigPV2E(pv.data)) aOK = false;
449 }
450 Cfg->x2eSet.clear();
451
452// Configure the activity mapping
453//
454 for (it = Cfg->x2aSet.begin(); it != Cfg->x2aSet.end(); it++)
455 {Const2Char ru(it->c_str());
456 if (!ConfigRU2A(ru.data)) aOK = false;
457 }
458 Cfg->x2aSet.clear();
459
460// Eliminate any experiment that we will not be using. This is will restrict
461// flow marking to url passed information as there will be no internal deduction
462//
463 itE = expMap.begin();
464 while(itE != expMap.end())
465 {if (itE->second.inUse)
466 {itE->second.Roles = itE->second.r2aMap.size() != 0;
467 itE->second.Users = itE->second.u2aMap.size() != 0;
468 itE++;
469 } else {
470 DEBUG("Deleting unused experiment '"<<itE->first.c_str()<<"'");
471 itE = expMap.erase(itE);
472 }
473 }
474 if (aOK && expMap.size() == 0)
475 {useDefs = false; useFFly = false;
476 if (useSTag)
477 {eDest->Say("Config warning: No experiments referenced; "
478 "packet marking restricted to scitagged url's!");
479 } else {
480 eDest->Say("Config warning: No experiments referenced and scitags "
481 "not enabled; packet marking has been disabled!");
482 useFFly = false;
483 }
484 } else if (!aOK)
485 {useFFly = false; useDefs = false;
486 } else {tryPath = !p2eMap.isEmpty();
487 tryVO = v2eMap.size() != 0;
488 if (doTrace) Display();
489 }
490 return aOK;
491}
492
493/******************************************************************************/
494/* Private: C o n f i g P V 2 E */
495/******************************************************************************/
496
497namespace
498{
499void Complain(const char *rWho, const char *rName,
500 const char *uWho, const char *uName, const char *eName=0)
501{
502 char *et0P = 0, eText0[256], eText1[256], eText2[256];
503 if (eName)
504 {snprintf(eText0, sizeof(eText0), "experiment %s", eName);
505 et0P = eText0;
506 }
507 snprintf(eText1, sizeof(eText1), "%s '%s'", rWho, rName);
508 snprintf(eText2, sizeof(eText2), "%s '%s'", uWho, uName);
509 eDest->Say("Config failure: ",et0P, eText1," references undefined ",eText2);
510}
511}
512
513// Note: info contains {path <path> | vo <voname> | default default} >exname>
514
515bool XrdNetPMarkCfg::ConfigPV2E(char *info)
516{
517 std::map<std::string, ExpInfo >::iterator itE;
518 std::map<std::string, ExpInfo*>::iterator itV;
519 char *eName, *xName, *xType = info;
520 xName = index(info, ' '); *xName = 0; xName++; // path | vo name
521 eName = index(xName, ' '); *eName = 0; eName++; // experiment name
522
523 if ((itE = expMap.find(std::string(eName))) == expMap.end())
524 {Complain(xType, xName, "experiment", eName);
525 return false;
526 }
527 itE->second.inUse = true;
528
529 if (*xType == 'd')
530 {expDflt = &itE->second;
531 return true;
532 }
533
534 if (*xType == 'p')
535 {XrdOucMapP2X<ExpInfo*> *p2nP = p2eMap.Find(xName);
536 if (p2nP)
537 {p2nP->RepName(eName);
538 p2nP->RepValu(&(itE->second));
539 } else {
541 (xName, eName, &(itE->second)));
542 p2eMap.Insert(px);
543 }
544 } else {
545 itV = v2eMap.find(std::string(xName));
546 if (itV != v2eMap.end()) itV->second = &(itE->second);
547 else v2eMap[xName] = &(itE->second);
548 }
549
550 return true;
551}
552
553/******************************************************************************/
554/* Private: C o n f i g R U 2 A */
555/******************************************************************************/
556
557// Note: info contains <ename> {dflt dflt | {{role | user} <xname>}} <aname>
558
559bool XrdNetPMarkCfg::ConfigRU2A(char *info)
560{
561 std::map<std::string, int>::iterator itA;
562 std::map<std::string, ExpInfo>::iterator itE;
563 std::map<std::string, MapInfo>::iterator itX;
564 char *aName, *eName, *xName, *xType;
565 eName = info; // experiment name
566 xType = index(info, ' '); *xType = 0; xType++; // dflt | role | user
567 xName = index(xType, ' '); *xName = 0; xName++; // role name | user name
568 aName = index(xName, ' '); *aName = 0; aName++; // activity name
569
570 if ((itE = expMap.find(std::string(eName))) == expMap.end())
571 {Complain(xType, xName, "experiment", eName);
572 return false;
573 }
574
575 itA = itE->second.actMap.find(std::string(aName));
576 if (itA == itE->second.actMap.end())
577 {Complain(xType, xName, "activity", aName, eName);
578 return false;
579 }
580
581 if (*xType == 'd') itE->second.dAct = itA->second;
582 else {std::map<std::string, MapInfo> &xMap =
583 (*xType == 'r' ? itE->second.r2aMap : itE->second.u2aMap);
584
585 itX = xMap.find(std::string(xName));
586 if (itX != xMap.end())
587 {itX->second.Name = aName; itX->second.Code = itA->second;}
588 else xMap[std::string(xName)] = MapInfo(aName, itA->second);
589 }
590
591 return true;
592}
593
594/******************************************************************************/
595/* Private: D i s p l a y */
596/******************************************************************************/
597
598namespace
599{
600const char *Code2S(int code)
601{
602 static char buff[16];
603 snprintf(buff, sizeof(buff), " [%d]", code);
604 return buff;
605}
606
607void ShowActs(std::map<std::string, MapInfo>& map, const char *hdr,
608 const char *mName)
609{
610 std::map<std::string, MapInfo>::iterator it;
611
612 for (it = map.begin(); it != map.end(); it++)
613 {eDest->Say(hdr, mName, it->first.c_str(), " activity ",
614 it->second.Name.c_str(), Code2S(it->second.Code));
615 }
616}
617}
618
619void XrdNetPMarkCfg::Display()
620{
621 std::map<std::string, ExpInfo>::iterator itE;
622 std::map<int, std::vector<const char*>> pvRefs;
623 const char *hdr = " ", *hdrplu = " ++ ";
624 char buff[80];
625
626// Build map from path to experiment
627//
628 std::map<int, std::vector<const char*>>::iterator it2E;
630
631 while(p2e)
632 {ExpInfo *expinfo = p2e->theValu();
633 if ((it2E = pvRefs.find(expinfo->Code)) != pvRefs.end())
634 it2E->second.push_back(p2e->thePath());
635 else {std::vector<const char*> vec;
636 vec.push_back(p2e->thePath());
637 pvRefs[expinfo->Code] = vec;
638 }
639 p2e = p2e->theNext();
640 }
641
642// Add in the vo references
643//
644 std::map<std::string, ExpInfo*>::iterator itV;
645 for (itV = v2eMap.begin(); itV != v2eMap.end(); itV++)
646 {int eCode = itV->second->Code;
647 std::string vName = std::string(" ") + itV->first;
648 if ((it2E = pvRefs.find(eCode)) != pvRefs.end())
649 it2E->second.push_back(vName.c_str());
650 else {std::vector<const char*> vec;
651 vec.push_back(vName.c_str());
652 pvRefs[eCode] = vec;
653 }
654 }
655
656
657// Indicate the number of experiments
658//
659 snprintf(buff, sizeof(buff), "%d", static_cast<int>(expMap.size()));
660 const char *txt = (expMap.size() == 1 ? " expirement " : " experiments ");
661 eDest->Say("Config pmark results: ", buff, txt, "directly referenced:");
662
663// Display information
664//
665 for (itE = expMap.begin(); itE != expMap.end(); itE++)
666 {int expCode = itE->second.Code;
667 eDest->Say(hdr, itE->first.c_str(), Code2S(expCode),
668 (&itE->second == expDflt ? " (default)" : 0));
669 if ((it2E = pvRefs.find(expCode)) != pvRefs.end())
670 {std::vector<const char*> &vec = it2E->second;
671 for (int i = 0; i < (int)vec.size(); i++)
672 {const char *rType = (*vec[i] == ' ' ? "vorg" : "path ");
673 eDest->Say(hdrplu, rType, vec[i]);
674 }
675 }
676 if (itE->second.u2aMap.size() != 0)
677 ShowActs(itE->second.u2aMap, hdrplu, "user ");
678 if (itE->second.r2aMap.size() != 0)
679 ShowActs(itE->second.r2aMap, hdrplu, "role ");
680 if (itE->second.dAct >= 0)
681 {std::map<std::string, int>::iterator itA;
682 int aCode = itE->second.dAct;
683 for (itA = itE->second.actMap.begin();
684 itA != itE->second.actMap.end(); itA++)
685 {if (aCode == itA->second)
686 {eDest->Say(hdrplu, "Default activity ",
687 itA->first.c_str(), Code2S(aCode));
688 break;
689 }
690 }
691 if (itA == itE->second.actMap.end()) itE->second.dAct = -1;
692 }
693 }
694}
695
696/******************************************************************************/
697/* Private: E x t r a c t */
698/******************************************************************************/
699
700const char *XrdNetPMarkCfg::Extract(const char *sVec, char *buff, int blen)
701{
702 const char *space;
703
704// If there is only one token in sVec then return it.
705//
706 if (!(space = index(sVec, ' '))) return sVec;
707
708// Extract out the token using the supplied buffer
709//
710 int n = space - sVec;
711 if (!n || n >= blen) return 0;
712 snprintf(buff, blen, "%.*s", n, sVec);
713 return buff;
714}
715
716/******************************************************************************/
717/* Private: F e t c h F i l e */
718/******************************************************************************/
719
720bool XrdNetPMarkCfg::FetchFile()
721{
722 EPName("FetchFile");
723 XrdOucProg fetchJob(eDest);
724 char tmo[16], outfile[512];
725 int rc;
726
727// Setup the job
728//
729 if ((rc = fetchJob.Setup(Cfg->pgmPath.c_str(), eDest)))
730 {eDest->Emsg("Config", rc, "setup job to fetch defsfile");
731 return false;
732 }
733
734// Create the output file name (it willl be written to /tmp)
735//
736 snprintf(outfile, sizeof(outfile), "/tmp/XrdPMark-%ld.json",
737 static_cast<long>(getpid()));
738 unlink(outfile);
739
740// Insert the timeout value argument list and complete it.
741//
742 snprintf(tmo, sizeof(tmo), "%d", Cfg->defsTO);
743 Cfg->pgmOpts[1] = tmo; // 0:-x 1:tmo 2:-y 3:-z 4:outfile 5:defsfile
744 Cfg->pgmOpts[4] = outfile;
745 Cfg->pgmOpts[5] = Cfg->defsFile.c_str();
746
747// Do some debugging
748//
749 if (doDebug)
750 {for (int i = 0; i < CfgInfo::pgmOptN; i++)
751 {Cfg->pgmPath += ' '; Cfg->pgmPath += Cfg->pgmOpts[i];}
752 DEBUG("Running: " <<Cfg->pgmPath.c_str());
753 }
754
755// Run the appropriate fetch command
756//
757 rc = fetchJob.Run(Cfg->pgmOpts, CfgInfo::pgmOptN);
758 if (rc)
759 {snprintf(outfile, sizeof(outfile), "failed with rc=%d", rc);
760 eDest->Emsg("Config", "Fetch via", Cfg->pgmPath.c_str(), outfile);
761 return false;
762 }
763
764// Set the actual output file
765//
766 Cfg->defsFile = outfile;
767 return true;
768}
769
770/******************************************************************************/
771/* Private: g e t C o d e s */
772/******************************************************************************/
773
774bool XrdNetPMarkCfg::getCodes(XrdSecEntity &client, const char *path,
775 const char *cgi, int &ecode, int &acode)
776{
777 ExpInfo* expP = 0;
778
779// If we are allowed to use scitags, then try that first
780//
781 if (useSTag && cgi && XrdNetPMark::getEA(cgi, ecode, acode)) return true;
782
783// If we can use the definitions (i.e. in error) return w/o packet marking
784//
785 if (!useDefs) return false;
786
787// Try to use the path argument.
788//
789 if (tryPath && path)
790 {XrdOucMapP2X<ExpInfo*> *p2nP = p2eMap.Match(path);
791 if (p2nP) expP = p2nP->theValu();
792 }
793
794// If the path did not succeed, then try the vo
795//
796 if (!expP && tryVO && client.vorg)
797 {std::map<std::string, ExpInfo*>::iterator itV;
798 char voBuff[256];
799 const char *VO = Extract(client.vorg, voBuff, sizeof(voBuff));
800 if (VO && (itV = v2eMap.find(std::string(client.vorg))) != v2eMap.end())
801 expP = itV->second;
802 }
803
804// If there is no experiment yet, use the default if one exists
805//
806 if (!expP && expDflt) expP = expDflt;
807
808// If we still have no experiment then fail. We cannot packet mark.
809//
810 if (!expP) return false;
811 ecode = expP->Code;
812
813// If there are user to activity mappings, see if we can use that
814//
815 if (expP->Users && client.name)
816 {std::map<std::string, MapInfo>::iterator itU;
817 itU = expP->u2aMap.find(std::string(client.name));
818 if (itU != expP->u2aMap.end())
819 {acode = itU->second.Code;
820 return true;
821 }
822 }
823
824// If there are role to activity mappings, see if we can use that
825//
826 if (expP->Roles && client.role)
827 {std::map<std::string, MapInfo>::iterator itR;
828 char roBuff[256];
829 const char *RO = Extract(client.role, roBuff, sizeof(roBuff));
830 if (RO)
831 {itR = expP->r2aMap.find(std::string(client.role));
832 if (itR != expP->r2aMap.end())
833 {acode = itR->second.Code;
834 return true;
835 }
836 }
837 }
838
839// If a default activity exists, return that. Otherwise, it's unspecified.
840//
841 acode = (expP->dAct > 0 ? expP->dAct : 1);
842 return true;
843}
844
845/******************************************************************************/
846/* Private: L o a d F i l e */
847/******************************************************************************/
848
849using json = nlohmann::json;
850
851namespace
852{
853const char *MsgTrim(const char *msg)
854{
855 const char *sP;
856 if ((sP = index(msg, ' ')) && *(sP+1)) return sP+1;
857 return msg;
858}
859}
860
861bool XrdNetPMarkCfg::LoadFile()
862{
863 struct fBuff {char *buff; fBuff() : buff(0) {}
864 ~fBuff() {if (buff) free(buff);}
865 } defs;
866 int rc;
867
868// The json file is relatively small so read the whole thing in
869//
870 if (!(defs.buff = XrdOucUtils::getFile(Cfg->defsFile.c_str(), rc)))
871 {eDest->Emsg("Config", rc, "read defsfile", Cfg->defsFile.c_str());
872 return false;
873 }
874
875// Parse the file and return result. The parser may throw an exception
876// so we will catch it here.
877//
878 try {bool result = LoadJson(defs.buff);
879 return result;
880 } catch (json::exception& e)
881 {eDest->Emsg("Config", "Unable to process defsfile;",
882 MsgTrim(e.what()));
883 }
884 return false;
885}
886
887/******************************************************************************/
888/* Private: L o a d J s o n */
889/******************************************************************************/
890
891bool XrdNetPMarkCfg::LoadJson(char *buff)
892{
893 json j;
894 std::map<std::string, ExpInfo>::iterator itE;
895
896// Parse the file; caller will catch any exceptions
897//
898 j = json::parse(buff);
899
900// Extract out modification data
901//
902 std::string modDate;
903 json j_mod = j["modified"];
904 if (j_mod != 0) modDate = j_mod.get<std::string>();
905 else modDate = "*unspecified*";
906
907 eDest->Say("Config using pmark defsfile '", Cfg->defsFile.c_str(),
908 "' last modified on ", modDate.c_str());
909
910// Extract out the experiments
911//
912 json j_exp = j["experiments"];
913 if (j_exp == 0)
914 {eDest->Emsg("Config", "The defsfile does not define any experiments!");
915 return false;
916 }
917
918// Now iterate through all of the experiments and the activities within
919// and define our local maps for each.
920//
921 for (auto it : j_exp)
922 {std::string expName = it["expName"].get<std::string>();
923 if (expName.empty()) continue;
924 if (!it["expId"].is_number() || it["expId"] < minExpID || it["expId"] > maxExpID)
925 {eDest->Say("Config warning: ignoring experiment '", expName.c_str(),
926 "'; associated ID is invalid.");
927 continue;
928 }
929 expMap[expName] = ExpInfo(it["expId"].get<int>());
930
931 if ((itE = expMap.find(expName)) == expMap.end())
932 {eDest->Say("Config warning: ignoring experiment '", expName.c_str(),
933 "'; map insertion failed!");
934 continue;
935 }
936
937 json j_acts = it["activities"];
938 if (j_acts == 0)
939 {eDest->Say("Config warning: ignoring experiment '", expName.c_str(),
940 "'; has no activities!");
941 continue;
942 }
943
944 for (unsigned int i = 0; i < j_acts.size(); i++)
945 {std::string actName = j_acts[i]["activityName"].get<std::string>();
946 if (actName.empty()) continue;
947 if (!j_acts[i]["activityId"].is_number()
948 || j_acts[i]["activityId"] < minActID
949 || j_acts[i]["activityId"] > maxActID)
950 {eDest->Say("Config warning:", "ignoring ", expName.c_str(),
951 " actitivity '", actName.c_str(),
952 "'; associated ID is invalid.");
953 continue;
954 }
955 itE->second.actMap[actName] = j_acts[i]["activityId"].get<int>();
956 }
957 }
958
959// Make sure we have at least one experiment defined
960//
961 if (!expMap.size())
962 {eDest->Say("Config warning: unable to define any experiments via defsfile!");
963 return false;
964 }
965 return true;
966}
967
968/******************************************************************************/
969/* P a r s e */
970/******************************************************************************/
971
973{
974// Parse pmark directive parameters:
975//
976// [[no]debug] [defsfile [[no]fail] {<path> | {curl | wget} [tmo] <url>}]
977// [domain {any | local | remote}] [[no]fail] [ffdest <udpdest>]
978// [ffecho <intvl>]
979// [map2act <ename> {default | {role | user} <name>} <aname>]
980// [map2exp {default | {path <path> | vo <vo>} <ename>}] [[no]trace]
981// [use {[no]flowlabel | flowlabel+ff | [no]firefly | [no]scitag}
982//
983// <udpdest>: {origin[:<port>] | <host>[:port]} [,<udpdest>]
984//
985 std::string name;
986 char *val;
987
988// If this is the first time here, allocate config info object
989//
990 if (!Cfg) Cfg = new CfgInfo;
991
992// Make sure we have something to parse
993//
994 if (!(val = Config.GetWord()))
995 {eLog->Say("Config invalid: pmark argument not specified"); return 1;}
996
997// Parse the directive options
998//
999do{if (!strcmp("debug", val) || !strcmp("nodebug", val))
1000 {doDebug = (*val != 'n');
1001 continue;
1002 }
1003
1004 if (!strcmp("defsfile", val))
1005 {if (!(val = Config.GetWord()))
1006 {eLog->Say("Config invalid: pmark defsfile value not specified");
1007 return 1;
1008 }
1009
1010 if (!strcmp("fail", val) || !strcmp("nofail", val))
1011 {noFail = (*val == 'n');
1012 if (!(val = Config.GetWord()))
1013 {eLog->Say("Config invalid: pmark defsfile location not specified");
1014 return 1;
1015 }
1016 }
1017
1018 if (*val == '/')
1019 {Cfg->defsFile = val;
1020 continue;
1021 }
1022
1023 if (strcmp("curl", val) && strcmp("wget", val))
1024 {eLog->Say("Config invalid: unknown defsfile transfer agent '",val,"'");
1025 return 1;
1026 }
1027 if (!XrdOucUtils::findPgm(val, Cfg->pgmPath))
1028 {eLog->Say("Config invalid: defsfile transfer agent '",val,"' not found.");
1029 return 1;
1030 }
1031
1032 if (*val == 'c')
1033 {Cfg->pgmOpts[0]="-m"; Cfg->pgmOpts[2]="-s"; Cfg->pgmOpts[3]="-o";
1034 } else {
1035 Cfg->pgmOpts[0]="-T"; Cfg->pgmOpts[2]="-q"; Cfg->pgmOpts[3]="-O";
1036 }
1037
1038 val = Config.GetWord();
1039 if (val && isdigit(*val))
1040 {if (XrdOuca2x::a2tm(*eLog,"defsfile timeout",val,&Cfg->defsTO,10))
1041 return 1;
1042 val = Config.GetWord();
1043 }
1044
1045 if (!val) {eLog->Say("Config invalid: pmark defsfile url not specified");
1046 return 1;
1047 }
1048 Cfg->defsFile = val;
1049 continue;
1050 }
1051
1052 if (!strcmp("domain", val))
1053 {if (!(val = Config.GetWord()))
1054 {eLog->Say("Config invalid: pmark domain value not specified");
1055 return 1;
1056 }
1057 if (!strcmp(val, "any" )
1058 || !strcmp(val, "all" )) chkDom = domAny;
1059 else if (!strcmp(val, "local" )) chkDom = domLcl;
1060 else if (!strcmp(val, "remote")) chkDom = domRmt;
1061 else {eLog->Say("Config invalid: pmark invalid domain determinant '",
1062 val, "'");
1063 return 1;
1064 }
1065 continue;
1066 }
1067
1068 if (!strcmp("fail", val) || !strcmp("nofail", val))
1069 {noFail = (*val == 'n');
1070 continue;
1071 }
1072
1073 // We accept 'origin' as a dest for backward compatibility. That is the
1074 // enforced default should 'use firefly' be specified.
1075 //
1076 if (!strcmp("ffdest", val))
1077 {const char *addtxt = "";
1078 char *colon, *comma;
1079 int xPort;
1080 val = Config.GetWord();
1081 do {if (!val || *val == 0 || *val == ',' || *val == ':')
1082 {eLog->Say("Config invalid: pmark ffdest value not specified",
1083 addtxt); return 1;
1084 }
1085 if ((comma = index(val, ','))) *comma++ = 0;
1086 if ((colon = index(val, ':')))
1087 {*colon++ = 0;
1088 if ((xPort = XrdOuca2x::a2p(*eLog, "udp", colon, false)) <= 0)
1089 return 1;
1090 } else xPort = ffPORT;
1091 if (!strcmp(val, "origin")) ffPortO = xPort;
1092 else {if (ffDest) free(ffDest);
1093 ffDest = strdup(val);
1094 ffPortD = xPort;
1095 }
1096 addtxt = " after comma";
1097 } while((val = comma));
1098 if (useFFly < 0) useFFly = 1;
1099 continue;
1100 }
1101
1102 if (!strcmp("ffecho", val))
1103 {if (!(val = Config.GetWord()))
1104 {eLog->Say("Config invalid: pmark ffecho value not specified");
1105 return 1;
1106 }
1107 if (XrdOuca2x::a2tm(*eLog,"ffecho interval", val, &ffEcho, 0)) return 1;
1108 if (ffEcho < 30) ffEcho = 0;
1109 continue;
1110 }
1111
1112 if (!strcmp("map2act", val))
1113 {if (!(val = Config.GetWord()))
1114 {eLog->Say("Config invalid: pmark activity experiment not specified");
1115 return 1;
1116 }
1117 name = val;
1118
1119 if (!(val = Config.GetWord()))
1120 {eLog->Say("Config invalid: pmark activity determinant not specified");
1121 return 1;
1122 }
1123
1124 const char *adet;
1125 if (!strcmp(val, "default")) adet = "dflt";
1126 else if (!strcmp(val, "role")) adet = "role";
1127 else if (!strcmp(val, "user")) adet = "user";
1128 else {eLog->Say("Config invalid: pmark invalid activity determinant '",
1129 val, "'");
1130 return 1;
1131 }
1132 name += ' '; name += val;
1133
1134 if (*adet != 'd' && !(val = Config.GetWord()))
1135 {eLog->Say("Config invalid: pmark activity", adet, "not specified");
1136 return 1;
1137 }
1138 name += ' '; name += val;
1139
1140 if (!(val = Config.GetWord()))
1141 {eLog->Say("Config invalid: pmark", adet, "activity not specified");
1142 return 1;
1143 }
1144 name += ' '; name += val;
1145
1146 Cfg->x2aSet.insert(name);
1147 continue;
1148 }
1149
1150 if (!strcmp("map2exp", val))
1151 {if (!(val = Config.GetWord()))
1152 {eLog->Say("Config invalid: pmark map2exp type not specified");
1153 return 1;
1154 }
1155 if (strcmp("default", val) && strcmp("path", val)
1156 && strcmp("vo", val) && strcmp("vorg", val))
1157 {eLog->Say("Config invalid: invalid pmark map2exp type, '",val,"'.");
1158 return 1;
1159 }
1160 name = val;
1161
1162 if (*val != 'd' && !(val = Config.GetWord()))
1163 {eLog->Say("Config invalid: pmark map2exp ", name.c_str(),
1164 "not specified");
1165 return 1;
1166 }
1167 name += ' '; name += val;
1168
1169 if (!(val = Config.GetWord()))
1170 {eLog->Say("Config invalid: pmark map2exp expirement not specified");
1171 return 1;
1172 }
1173 name += ' '; name += val;
1174
1175 Cfg->x2eSet.insert(name);
1176 continue;
1177 }
1178
1179 if (!strcmp("trace", val) || !strcmp("notrace", val))
1180 {doTrace = (*val != 'n');
1181 continue;
1182 }
1183
1184 if (!strcmp("use", val))
1185 {if (!(val = Config.GetWord()))
1186 {eLog->Say("Config invalid: pmark use argument not specified");
1187 return 1;
1188 }
1189 bool argOK = false;
1190 char *arg;
1191 do {bool theval = strncmp(val, "no", 2) != 0;
1192 arg = (!theval ? val += 2 : val);
1193 if (!strcmp("flowlabel", arg))
1194 {useFLbl = theval; addFLFF = false; argOK = true;}
1195 else if (!strcmp("flowlabel+ff", arg))
1196 {addFLFF = useFLbl = theval; argOK = true;}
1197 else if (!strcmp("firefly", arg))
1198 {useFFly = (theval ? 1 : 0); argOK = true;}
1199 else if (!strcmp("scitag", arg)) {useSTag = theval; argOK = true;}
1200 else if (argOK) {Config.RetToken(); break;}
1201 else {eLog->Say("Config invalid: 'use ",val,"' is invalid");
1202 return 1;
1203 }
1204 } while((val = Config.GetWord()));
1205 if (!val) break;
1206 continue;
1207 }
1208
1209 eLog->Say("Config warning: ignoring unknown pmark argument'",val,"'.");
1210
1211 } while ((val = Config.GetWord()));
1212
1213 return 0;
1214}
#define tident
#define DEBUG(x)
static XrdSysError eDest(0,"crypto_")
#define EPName(ep)
nlohmann::json json
#define DBGID(tid, txt)
#define unlink(a)
Definition XrdPosix.hh:113
#define TRACE(act, x)
Definition XrdTrace.hh:63
static bool isHostName(const char *name)
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
const char * Name(const char *eName=0, const char **eText=0)
const char * Set(const char *hSpec, int pNum=PortInSpec)
static int Parse(XrdSysError *eLog, XrdOucStream &Config)
static XrdNetPMark * Config(XrdSysError *eLog, XrdScheduler *sched, XrdSysTrace *trc, bool &fatal)
XrdNetPMark::Handle * Begin(XrdSecEntity &Client, const char *path=0, const char *cgi=0, const char *app=0) override
std::map< std::string, int > actMap
std::map< std::string, MapInfo > u2aMap
std::map< std::string, MapInfo > r2aMap
MapInfo(const char *name, int code)
bool Start(XrdNetAddrInfo &addr)
static bool getEA(const char *cgi, int &ecode, int &acode)
static const int maxExpID
static const int minActID
static const int maxActID
static const int minExpID
static char * MyHostName(const char *eName="*unknown*", const char **eText=0)
const char * thePath()
void RepName(const char *newname)
void Insert(XrdOucMapP2X< T > *newp)
XrdOucMapP2X< T > * Match(const char *pd, const int pl=0)
XrdOucMapP2X< T > * Find(const char *path)
XrdOucMapP2X< T > * theNext()
void RepValu(T arg)
void insert(const int i, int start=-1)
bool beginswith(char c)
int length() const
const char * c_str() const
static bool findPgm(const char *pgm, XrdOucString &path)
static char * getFile(const char *path, int &rc, int maxsz=10240, bool notempty=true)
static int a2tm(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1)
Definition XrdOuca2x.cc:288
static int a2p(XrdSysError &, const char *ptype, const char *val, bool anyOK=true)
Definition XrdOuca2x.cc:140
char * vorg
Entity's virtual organization(s)
XrdNetAddrInfo * addrInfo
Entity's connection details.
const char * tident
Trace identifier always preset.
char * name
Entity's name.
char * role
Entity's role(s)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)
XrdSysTrace * Trace
std::map< std::string, ExpInfo > expMap
static const int domLcl
std::map< std::string, ExpInfo * > v2eMap
static const int ffPORT
XrdScheduler * Sched
XrdSysError * eDest
const char * myDomain
const char * myHostName
XrdOucMapP2X< ExpInfo * > p2eMap
static const int domAny
static const int domRmt
std::set< std::string > x2eSet
const char * pgmOpts[pgmOptN]
std::set< std::string > x2aSet