From 570ee035836ebc3b16acea1b5da51bee1581435b Mon Sep 17 00:00:00 2001
From: "th.l" <thl-cmk@outlook.com>
Date: Tue, 30 Apr 2024 10:11:01 +0200
Subject: [PATCH] update project

---
 README.md                                     |   2 +-
 mkp/freeradius-0.1.1-20240430.mkp             | Bin 0 -> 14614 bytes
 source/agent_based/freeradius.py              | 467 ++++++++++++++++++
 source/agents/special/agent_freeradius        |  11 +
 source/checkman/.gitkeep                      |   0
 source/checkman/freeradius                    |  45 --
 source/checks/agent_freeradius                |  48 ++
 source/gui/metrics/freeradius.py              | 323 ++++++++++++
 .../cmk/special_agents/agent_freeradius.py    | 168 +++++++
 .../lib/python3/cmk/special_agents/dictionary | 446 +++++++++++++++++
 .../cmk/special_agents/dictionary.freeradius  | 155 ++++++
 source/packages/freeradius                    |  23 +
 source/web/plugins/wato/agent_freeradiusi.py  |  94 ++++
 13 files changed, 1736 insertions(+), 46 deletions(-)
 create mode 100644 mkp/freeradius-0.1.1-20240430.mkp
 create mode 100644 source/agent_based/freeradius.py
 create mode 100755 source/agents/special/agent_freeradius
 delete mode 100644 source/checkman/.gitkeep
 delete mode 100644 source/checkman/freeradius
 create mode 100644 source/checks/agent_freeradius
 create mode 100644 source/gui/metrics/freeradius.py
 create mode 100644 source/lib/python3/cmk/special_agents/agent_freeradius.py
 create mode 100755 source/lib/python3/cmk/special_agents/dictionary
 create mode 100755 source/lib/python3/cmk/special_agents/dictionary.freeradius
 create mode 100644 source/packages/freeradius
 create mode 100644 source/web/plugins/wato/agent_freeradiusi.py

diff --git a/README.md b/README.md
index 9209cf4..3a1724c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[PACKAGE]: ../../raw/master/packagee-0.1.2-20230706.mkp "package-0.1.2-20230706.mkp"
+[PACKAGE]: ../../raw/master/mkp/freeradius-0.1.1-20240430.mkp "freeradius-0.1.1-20240430.mkp"
 # Title
 
 A short description about the plugin
diff --git a/mkp/freeradius-0.1.1-20240430.mkp b/mkp/freeradius-0.1.1-20240430.mkp
new file mode 100644
index 0000000000000000000000000000000000000000..44e8693bc4abfbdc28de7507d078175fbda4c086
GIT binary patch
literal 14614
zcmb8WQ*b6s`0X3pb|$uMXJT_=+qP{R6Wg|J+x8p0;UwofsI%{O{rAnYs=F_`x~saX
zf2-C*90LOqQO;}*_H*m^<9yta^zdyX0C_Z-UP7mg^|I`qr+V5dz}B}xG!C@o%yO%=
zgl+OFlL{-jaXQm=Lw4Su-c9k{1(6CSrxpdy*fJV9(3+seNfs~eUO)*ixLfQY@8j@-
z4Eq=SgHQa|zI}-l!2BuJ?BMP*JO`7H4C?<YkGKz<K4jtX?<Daz$D!=*pKy<SgMEsZ
z>TaNP4*L+_c;?K#koI9~I|AW+_YeEa&~+4-!<r6<O2Lxh(RR!NP`Bn~t^C>e=i%*`
z0v5~FSO=-mz7qfYYR!fm-0gI@-&6KTg_q**?+Tk;?$l6^)PaG}=|G5;fI>llE>Oun
z4x1Y0&t2+Ft&fZ?2lkq&Xvn}=xOpF!MJTeuj}#bRiJf2Hyr3@Ot*9B_FL(<to9-HH
zP+X>bUmM=BJAUtP0Tol{CnCM^kl@n$!h^dZN7Ng8J6Nyw`yPplR)L>%qj}p1s6Z(2
z&?SO>EzrE7d&L*rNRW%|>B3&mDuZ_G$~qF~frk-GTfm2Ot!>&QVcyOYtyx98Y|i1V
z)x~y-p$!FHyx(vlwql{7y#xG52Zbk|3#UsBNt%Yjf|M;}Ql)^1Udxx}svY+0M^bcr
zg9uU^QSHKXqdgle|M5cSTJ8~uEC#^IG6G4@j0FK+jTa?-)hfqs8RT{2{@I7HvqhkN
zw^8w@t;Mel<Hf#v`)((bCO3?u6S_<Ky=YsJgdV)56|jV87eV%dlXSj?jQJH4i1-xn
zDFi<M@AfR9SqRVc>}Rn-sN(GBp7qCpwY{ZNmNRB&$vnokNLDO&r1Vgg22D%iK=W1e
z)y>7%T9hfPH^A-X-0UXwA$%jKWjGDa#2{KLi79Q`EP&v6CzCb$#*RmZN%W8eN01(Q
zFmtC=3d{ym5-oBD40hn7i3Sqw;d9yCdT+N!QgWka0xqTqI(%FVD!iONf;$M4_81}@
zbP#k{i-~*S+j917EAkfMoO#`)Z|dx0_WJxh0PHivwJ0wB;q&uHnfOQHJ)oHlPwo4s
zTX?Fc@86e0{0G@L69O~w&vzkP<aqx!$6==xlJ`Ex57kLTPU)@O&&$})?0s8$P`{f=
zx6X>+uOW-IJ(S~p1Xe&<n@_iF`r*vR7*#8mlC!a51^$BG<J}FJ*KfTA;)q-(2Ld0o
zw4?||v0V#v{JBwyC){6tJ={U`HW?Um#(^%DVHi%1_ME(a#oge@`$IF~$f~?@c(%@H
zYyr(0)s4T5_yGhFto6jPwK=ej=kW8$la;Mn!o)f!Xyy)A3e>;HBe_Kfvp9cg3=qX{
z)?>XVOe0%FU_gi``0hV>)h^^Om#mFAKZwN4+`spWT+AQ!V|_7cKw5V19dozn(Qb@>
z7pvOGcvUcbT3y~Zwl-`do3B`goYrEqYw?oI8!?dt2D6|F{n|ncU?qto{1DCy6ark<
z5{9lM_;)T}-0R@Q|LZv8!_yx?Ub`*%8gx!~B?!$&ww%RwD<TwRxbT#*5b5HBo~YU>
z7vszHM9~l^<%LTM_wfm1!D3ay7n9=d;}sOZ>;LEI)D<8Bl=?T`k0Ue}M_jHc)VKc+
z;QBf~4NP!(JT|y)1#HI@lMy+aU)g?piuvaz6aR=7@hTZLQ@Y}J87k7N(He&h49v2Y
zfGWj+ZvfJ;MK>DfMb`k3S6V1g6SBc3%mg9FIH(G}nF!y_TJjQwE1H1E0z)K<8s=0?
zailx?2HMy3x{?$_8tEW{pd3TcbZv2HrC6NO#EC=a!vn_!G!e2^9FNw4q?^jq8U6qy
z?>`EhV+>S2H^H+TyyB4NJd<MkI`l1X7@4Q!LS3&1**gHtjWalJuvtUPun{DF4=5Fb
zQLR{&-{?7V{4*Il+bU0Jo~4wO4^uPrB<(6pX5Q5;F08Pvt!!aBV-^*)d4bi{c~TmR
zd`T7Q?oA3-R#eF=OA6$bfAgg3b@`ICqTEUUrn(b#GwXDreBM;Mp<8T&{Olu-YjV$)
zPc|8cVB%}(YO4AF0D3xTF=%POt)z`e1^?c0pE2oWk>CUxio-}i%=z6vsT$d7{87WS
z>ujLJ!qR|X!PX2NDvFL^Z@D?@do)%*eR`5bNoyj-2uh5m_0tLuYb``j51pLQqEa#B
zXgPuu>E^eVO47yTE1|B7{Kd+THI9ml1z#M6F_@f5D&gX0N-~i?jy=g-3p_>>k#}HJ
zUVFM!LC?<DvlgB2mzB5l@~Q+SG+=_49zUKYU3&OLdw0HPTAZ5!ZM3rv$7zhVhQ!m}
z@&;C7yHZ<bRNvr%O34Z7Dw?gsNS08psc7F0h+7*XrmZ5Wn4(@**XR$BF3wfhMmw7;
zY?2jdVd-Xjw?!|g=r^@yXJ+F6F_UMFFbGcTcu1=`P4l4e)<@MWvPiS$pu@$~j>^S*
zNXvAx-K^5H7~=&f(~a|7XaC`?I3**hBlB&=){3fQ@J;ip6JJC3rv=VW<%hY-G)^y?
z*htWX21|@84|F#XxSFW*Alqshi5q>>5}dg?M`z?YwGza$2HVw2t~m=<acjW0?~GQE
z{y|K4OLH>YsZf?)7uA4Dz)NEVHp$?->Z%yctSr?RR-+$wg*4_H=%5B#s%iT$W6Z%r
z6?2xdv5u{}_toz`5Z5{pwB8-niKgHL3<P+LlKOW%>m`fY@a>Vk;Lo-xV#jm*jq6f&
zNc!m5i^Ur#{e}>-q^IA{hVEX}n56o(Pn!Pex=RYWuRNK$X%Axt8@B7|Qabq_{duUx
zwPN+Qc06U@P&dI{=u){aZ2&h!b3@|#&^n?oDS1-GR1@1d9kU&$5{iuuW1y9$PIkRc
z=0q@?BYy?2o?Z=DdOOdgqeb~4wMy}X0cIGs6_vNqeOrfca$ZGEi9Qw7<;>TBHi#DD
z+AVN+7L@#Bq@KA6<bXKNlGH;kQ{)Beo>xkwAJhzJWO9%aCwGqIwON!5P9*R4PORaS
zvp6?P+wSlqOlF-g?XCdU7Xr3s^CC`9fJXwo;XO_n&WzId6YnN5ns$RI8=$U7K_6^}
z{`XS`OXgL$AGqabD}qM(2~iQ*oh6mQR5`q={uQ!&1-2DFjpMjen5^YZRPWf8R6M4A
z^E~+syOa4QvkqlgUK<q69~lw5?jFD%OzJrd7tlC>+>U<Gh_hQs<Nma@t+$lU$+Sz#
zH5t(y0bil(OX?%_go{XAmdfR_#l?%UvmDRMB+D~>r{^E1n_sErX>R98^!=1SqZoQK
zWV!}#jE>+5XiEZZqr2Y^<5Gq`%^&slO~U<jx@dpS1pLrs;(*v$8H7t-3zlWE%tp|)
zh`7Ic{F4r^*j^zk`0xFVZ28Asm|^g2>3P;cUYtC92#$cjx}Nn}^Ql~juuN_-8lhib
zmd>dp5+ADPc;Dj^<TD*fJeLckIEf8HIoeGFey-8NH}~+B&hX|?DZHXnL@{&0kfc}o
zZTGfZ1=41;z<;X2G%KW(Su@I@+IAz8k&@zhJ_J$EF-JseRj$TC?J&G*<<w1GCDu{T
zaO$TAM{jU{bmq>}e_E)n{Lo73a<l=zT_gv9xf_egGb4@G0nzgmN$%x&ce~DirJ1&x
zWGXP9*(40^o(o=?=9UD>(FSUWC?J}o{k2ysE!^EZARy1@aYtm9-5=8x6*`h!ZwK6i
z^d6*=b2pQSCW-bA9QZ8}hDdWh;R{?nHV!F_-QD^4x<dqcxVw6`#O+vqoA+~Vae92*
zD#)Wrv4{E`fhd#la->?fA6&>ZR~~TTjncORF3xgfsxY|Jmb1IxbMr!#M-q9nVz~->
zuZgvvmQLstSHA8!jUnKt<r7b`aLu`*M(8?XB@#~o!9~!1m<fGyq@yNlGOTP;41`*N
zRxkl$VKU{0B;6;nt|v7~0!XVeNEOmoE4G5`Hc`M3pp^Gpcdr)4b#}{4L$XzYq%GG#
z5c|5JpfQcPfDtiNx;3}9m7xA)0({lcx_U@zY5H3=*GP^86VHUmEOtcK;?AmXeB@TM
z8gFt`=GCmN#oOdq@Icil8HJa^8ST8{v9zl5nFI#`A0yP?lBoOGn0JvWfp@c!i`q`h
zbbpyL*U=G%0?}ANlWIdOglY?9(-{kO*dBzBLB1J8z~ur+u#-fF1_a7+{~_K*%uuA7
zd&HBsy_GtlIIS3nx-MzP?%PTiQ>2<Wai4L|qaK#!n^jasEz?0#d<K)X;jF3KiCGM5
ztfaO4mrYIbmwPpViZEvF#iOufZy+ieJKU@IgbqAGLn;zyG?$dC<MA%q&>F#%lhe64
zy5>!hrSUi)ZT6e5hlFGwqc-seC!Oez(ly=u>eQ5trbeI0Orq)m2RMu%c3LPZdEE1r
zVK7$}rpk%30fE+to*t%tsIAVpj68;c(3X^<_&(i@67ZHzlElDFMx`KFTVS6$VZ>5h
z+CiZ{mnhZ3%+;FOBmX5Q*?qWGXZk+nN<-b&?@D9VuH{NY?mqWwO=fQYgvxr;t*ZUE
z$!$7zeZTx4P20EY59>70jLti-oApmeOg3-E-kP&%0pFlNb^`xgJzsDO;^ptTc)`nM
zp_-KWJod%st#@#$Y|P<|uA_%i{ni8FDEn1xuhlOvjNLy!T)oycC``{4lUt%$T(c(r
z9Ex};`qMGAHoaRRQ@ix9`S9*=S31zMqviPh^#<xdAo22W)x7&vhs>$SJO^zZp%z_&
zYn_fmua?&f%lr#W&&+T=FI$&6v_5k+W<L2pp_7l8yrJnbur#L(dAORncBB^d*jhH_
z)YX97ROMgXx0A>CPzP7TQ~i@A06VeAdM<+=YDNPm0^i!J!|NTuH29r*`Ph2(H=IWg
zuX{io-}l_!TbhX1nlXFtLp%IJGeYmcr+<o}9!Z6{8<{O6rkaw-|6%z82l@F0<obMO
z3t8O{xI63r*};vITzxI*hsW?c`)8BT|F+kB^f{k5XUJ}RypIoz00;g%->X=!?fdx>
zD17*whu%AyY<IX9UE;PpS}oQ4E?W4?@5$Nkp$Z3j6i%+@5hmjL*WiBZY57NHkV=l=
zEm{;%eTn!S&F0uS6OcML^bBHi0Z3OL;*%3Uo<NvfA&b!G^R{l}5qfIp+%GW<T1DyC
zmIfo9`UwhpZIZ_Me4MGc2uQht+MlT~gdFAn8ag9i?bof2SVV-B3x6OqHzYPbq-u*r
z^{Ml>+hSACOl0<IvJj>83N-N$9X|m)Q~DvmQ@5X+2{|9EinV(+l>ZN|tOsnPvN~Yk
zsUXDP?fc1Rpq>A<5+`8wT@mLwpZkXg2g&CQnQ$RQ;lIUh;lp>u{}6?rC<=Q!#f+P+
z4Iq7sn0k@_X*%(S2syt`ahPW!clKIP^RT589)G?YknMf=GLi$nG#-O4I>8tf9}IT_
z%xj0~Z9CrwoBNBYGIAL_%>4g>%8|07=R~j89SYmANbboq!S9`&-8Os5#Y%Z6GXEIe
zqtUCoQ0JQm*aAtZ&%2b4SfTBKjYo$J7k=z)Uomwr)E`0p5#r(<FOkRy@y+tolyTrt
zwt^oX#0}3o6A*G;BTjFL81Oo2LS<Y(D*x}B;j?1e%@^HAPV671R?R|Lf!LJ-e!<n)
zr#D@tQ|*T}Z#9U(AY7oL|IhdE>FW34;ppP&>3!*aI@g0Nq6uZ)uE4wC+iZtn^Tl!t
zZW%(2ks;y%W1SyEE9OF!q6;FR+`HBmFI;%Mo*a6x_*^k$;fdz+>c&)X)M1#DGXQ~e
zbM!TEBNcZ5c`gLcg1aJS+-)~r@{NJ?J9^7=_>VT!ibX_Jvp^htYMx(;gAG)i5k(Dn
z?dZ#BV*ey3UX=Kzqgur__XKa93gfLBdgH=qL}L<Trty^&5U2n2(~6B{YSxB1%2>8`
zPFM4P_JV$N9lKllZx82*r{)5Xc%z>Rb{8vnf8GTQ6~y-QA#^6Cvw%mJ+j0FzUl#T2
znCC)9U)_5CKNjt5RdWIU$ZuumLjB8^^Qm{q;Yycft`St;Jli|&#prp*a+^cidt3Gk
zSnAu;3+_ib0`H3o?jx@BC~sM_I{SZi8jmf$xR_Fj=qrSt4rpFRhbg7Z{$td6Vx1IN
zy--i@=Dk}7?U#%9lR0OCt34Hsm3tWp!O`M^8!x8+NLv=~0n8<n76yw50lJ=bqpOnY
zR`EYHBBSKSk!mgLYu8$!vFW1{GGHtOLxN}{%h<RJIo+s{RP+~Y&&@iY=c%pJZF0TW
zyI&kV2M2y31_{zz(Gw~WT!nAs-m<VSr&fhL^iW`Qx5qZ=vX}lf<BP>)8#Rs`C)O4*
zVAOisJExMAz`8U>6;9Lr4PeRtJlQom@*SSDKT<ZadJ|gpyYgIXiT{n-zaqxxYU!5|
zi~j=u-LpyhQfd?N&_J|u2wj}eFITcnI^QbZEx1a7IKIZMU1rRB+aBoF7~uH@NT$a|
zsGodkU|hjk5FCwA#z#$9=sH!U6FoBv7A}*^HL43S8*0#+(~{t>%i)ju6CKIVHV8n4
zj+4V7{(&usQ>@?<OqZ179iOC)8x%bvsl`U&PLR~l&WZC;R)5wB<51>;r&FLba+08?
zukysHu5F@SxBmYbP4lQohB)2Gh=D&<SC+=LF?Xe_qk#1wwU2E%%}w9xsgMlsHbmTo
zvW=Jzy4T#qH4p2Z`*iChp>^w6ta4ey6tr`#2&@)J%C=~glqFZk15~SKFjq4}bc&m8
zclMXI6_7uSu3bi=?rb7)moaFj81SpZ>@+cFJN?f5i<-MW=uz`LH5!eybI9|aTENID
z>t|cn{qdFryj(YgOBGqqmeFvnM}UUZ2qxpz561Lphe+^X%{q)`hf<T#G!Rx@SI4r~
zt)l5&5C+-Q@=0d0?Suf|PBBf5N-XAz!*iRMqI|^&LEk&eAkTy;z@l#GI^O-$N4Ph8
z8%1E_Xs=vUN^dV(BY-b<(^ZnwJ#yn(Li&L#YLOXVSU65B)ZWKP)kv*BAamQbY09Ez
zz9a{&s;Lh%3|%H1V(3d^2D?;3%t{b*ES@Fan>pj3+>W7)S}E^`$>c<-Is+%BEpqZJ
zjRtd3`jLi$#<%xIR3YWj6@_B11TKm|a#J~~5S44LL<62cS+N*HCd4+^J}pJy3^xZ#
zIcYmBh5y8=Sm71YoMtkCUzWfiD6h7VDtVG6+k~YNoaay-J=*06bFhA?48tn_BAdW8
zQzwXYB(mh8X1%0ANPT5GwysCSD1W|7&(yu*f}~wB4~bQ77Km0o$B~6$)sns8XEjVi
zTzFw=`oXJ*+40BjOfQ_5F%rPe9jPZP9~0K&quh*gY}J6$O(KBRR40{m%M1~h9#+4E
z?mwn?s>&N*Nahk>xoSIAT4~0hB=vS;MU&?k;Jp;zYq-~Cd|qy(i)!zbnS&%ZMYi`G
zEK*4PTGeBsZLM<Pv{pq)aFFfR(UdlNPCNd5#jH0g&0|fjN?4>zewvv)qe)T@sg$o%
znpP+8xBnF4=9WMJu)el7oa$j;6+MkwtIW<3S*yr(>#<RrRm8VZQ|KI6uY;pDGYHR0
z@w5u-$(^n`DbdjoAz5yswUYjZvT}=(m8NR_cb{y4?Gumfm|C(4AazoXW{8$uzJaMH
zUsaf>oss{~NMT)>Bwv|RLe*pwlTh15z;xnZQxjjERzg*!6Eg!_m{QVybz@Z%Uzt!s
zt>qV!s7Xe^R9>4>YNmbfqF3i?R=%k72J=@oR$sOh`l@B4Yt{B$pOG;W-6v^Q$=dXo
zsBtdKv|*F3!cr3|U$XAtFa40gRd1@-CR&{GDZHjs(eD47cXWb4?2=xOx<})=Bw;}2
z6<K{rg*3^NicN_~1349Kqh5+_j%a<~A$j_LYsP=-ki=0EW)R+~v8MFvyouJv3=xu*
zp*lJF#373vWBb`5lN;kBe!T?Q)?s6VX}{Ve@(-W;O-0yTpI-Dy)owS&D5j4mE>Vof
zBP%`D-FYcY`ps3TW!>I3y6LpX8^r9ApI}s>IU|Mg+H^$;CHoU?C2a4FjI}7;crxC?
zWQ!@u<%v#HBzqHMl&{wv#p0Z)Ve5+1o}wGzX-`W;tTVgTYN#6pr)iptV@)xXZ@Jl&
za0pC2p^n*7ApCtSA8U7{&P|GH!)!(MtT)>gPCdeYZ)zHUVtvty#^L6a;q>~VCAIba
z(Y=YIg*=1B53$qC`l2QI5#&_9WTBu~b;8(Qzjpw;x@nEofM5EO9!Mi)Bd3uKAHV`|
zoTZTsKvl9f?)^_i{?j}fSq~>AtR+0v^gGeN;`%;QD)e}D0%n!z@M{Y<tXz4cOa)NR
zaCQ5kWy>1ZUfOJj@VRY&cBggz>|0g0^!4Pq<+aJYbDXr5EzfNVd!c)k>-Dzi{BM3?
z6bBnz{kQ)T%r(7!jtjef0NsHd4-Pi0{rfV08XP|yr`QGvogd%7o3}Y4E<cGyD4@*U
zevV%Q1HwOvF{5ShM6<k%nxQUq*mI>`bM0TY@)*9)TjIv=K`9DTN*y8;Ph{f)CU8>8
zbfJy|Kaf|bd`@5;KFH=d*7z_72av1`mDanQE?i?YEhd5?0??L!JKp>%vA<G==y*Y{
zl{9rebr@Oi1|7eN-y(dQS1TNqA`D+rE^NqQ44P`&OrznK4NxS&7mxBVXVp<=&24C2
zw#ym5ZQHcEzoAAMrRsT&<jC7*{7JLIPB>o~A<PZp>V$>>*Z?5V99;b0B=XY(x*L3z
zdRFrCEY@<N;BA75l>;U%4u04kj_Ab7r{78cU+<YI{$vz8EEsgN?>t&@V*4o*|I6nF
z?(2ZYrJ4GlZG;pK+}In}z8LZl%^o{V{*%E*+{w{WOZS;adRr$$pwTS)nitz;g84kd
zwtR_-l)NvyPwdcWI{}0Q4g)_uODUVpW#};m-ir;#I=)Lz-~-s$TkR=C3)Y9?7ye<5
zycYsl6Z9AMGw#$|f^AsktmOP-L6J<vB)UF}S7L3?fXj`O%~u795Wyx||69`72{zvv
zi1i@CLvq%`6{Btx(=a$)3>BbQQpg1VWF?1@(EBXRp|_OJj49SJ$aKW*l*y_V__R2B
z3W#9ir-0Dd<wSh;%k!^StzWf9ABO{Ly5EP2)0)i5-N?fZ?Jg9|RkqijyuYA(m`u6Q
zxt>IKH^lO3mbGO8JNkV75~+m(S0ox#R(Xh(+dw=Z0a@LQle<Jmu|b{o>%y|7__Jfq
zf&XH#8}RcQpty5xFN__h@Pr)jHe7GtTUnQ~p}%6-+!^gH9_upG!uF2h3;ylB5hqW?
z)AygL)MvI&K1Q7B>Jb*HRBJ@8aqVZqpYNRl=%7)zR<vRU+)B8{-LB>#o*~DFmQYfo
zc_%p-?+N}=rEBTPmN++N93?<&xpoG3HR2=M3)zm`m&jbjPcdKo@5>_uj%a9+;fu-(
z+#z+VeAJnza+!yIJw4K<@TU<KzKo{H)`S+3jUALjk)=MYweJ~U-$wv{!T26ZacNU5
z_o;Z9yvFcY(Y{&T2cKI5bT%TVz{PR_%*A`WD9Th(@MDQG{mF`!F_^sB-LzObyiyxU
zDCriT1^oV0BeGM0&%$O8!ulcGR!D>*-8SNKZ5LY7xWKa($nv)w0{;-eT&uSmEVz>&
zR8;W~{E98Tmz*6lA7!9zYO_@vD<c?zanG`1!3Wpqh}vvU#omd_lIhOMCJ^(jG%Jqg
z^>ISp|LQb02Ezx@k=6ba!|x@_8qq+E2d(NtAY62k3)z>9ds2vG`_4W6bszA9{Pgwn
zx&MPK^^2!~AorVvl%xoRAk;m3$K|UU_Xkt{<KVr+w)frj#)l|eGI*(c7P&m)I0l)x
zj<bH$F(d$~mt`(2kh3}<idMjZXK%#0EpWB=!q<|r_L#mm3QXC_v6T|K`x{&-&f-A)
zOqT-a2(xY3Yxj@|ELyZB5x~|$N<UyHy01{ok|VS>`UZI{P<pUGXv`9ex%}&0tl$z2
zs9CNm4H4I$`LPcv2bLY|Y0HrIb43nq$j*MpZJuFktIg|3KQz#+J_E!V5KU!K{><xO
z37LkV4{fK2uL&BufSdE5)6YnqgcICPl87%YY<{1<c}8;zHjA|6*e;yC<~W-;7`M}v
zav!*~8#WoW&Gm$+Sv~{>`<86qIqZ5R{B4IkEF|<6M1Yo+`E@$rb9X<~nX(kY>ACd}
z<;1BuRN9q|C)rAOtQCPjx^t5>=kj%vGpD`~qu|945pz|?-5_XB^N^uS0?5JMAy8MM
zG_M4Mpg$=^shkPPsk*soqjmJrgB3P#kVc-)$wrT;FZn=_3r)sxgRH+?`Fc<lt2VD+
zw1gDjs*7K4f!aECpW@{o!i9GJdxr)<c5g-OBE4Jr!1!gQYMMyjpZJv2mKwQGwD~N{
zZ!mY2QR%2A2cJY{3z#=R(WPv3$Pi?!-EWmXhWE2yE5KaVj%La4ZFgYEX>Cs~$CT@2
z4;4SnZ+MI>1<xuCC8Lm4E<uHDkIT=xxh+*03}vCiyZ+@LK&si2gVG(Q996(2<W{H3
zVZL!+fpHw(K{S#gQXJ*G2?{U$3s)MZm6^dt5L=4QNEwCz?e=jozFwc?OXAN_Op7MV
z6nB^N!aC|9Va(YQOYXn~6FWO=mZmHr0cUmbzaOdVAL*87Oklu>cmLI=qFD2?+4WEF
zp(FcEwvg+LQN3#IPe8%T&sL75K;YN6XP0cUvV#Ch^Xxa-u0naYnR}aDO6KnFond>s
z7?IrrR$?NNpMK(gGll)#$wCEe`&B5;$ht3NP{VEb!Q5jdMx*fyVjDZ_Fo&O`Fv%Nu
z<v03Fd^$e&m$TAZyjF5(G~hLX-b2QIGaRBjnZlo~Nq$1`%u8zMsJb5lC$K6pjlL&P
z!O4syf2-Yp44!DB)9h301x^Lm{S=&3*~U5J&8ZOOGy|ny(aHEpH+=mZp8IB3pb)_J
zDX8)Yle2HuJ4OKKNda|<vn!I|nUWX|igqS@skDHN?6eknHjqP=DZw8g<0l?=@Q9V{
z7V_M6GKK`%9q4beFN(+0%8)^pGM4A(Gj20q=wpOlrMP(pw^(U!sBqS)deIU$ed550
z!kecD6neaShD#p&X)q+Er2I0wi{-7!Sjyv3n%7nkfp~U-X|a-_Pw?QJ!bX#tp)$&E
z)Z;Z1Ajft<mh$l722m*LkMMOhBvO@L>uSt8!!Z@g6qbDnjHD_wMTtZ2>sW<SBQX$u
zsr^lIi>3WWy+NZ&x2^YJcopwY={o~f(+TQg|Jl|n%UdvOUoV6c-{w)11M3*i4>FVl
z??OaPzF=f*h5!aRUoMqKG1Wmm&GEB{bWdIz@ga0y;LAT{Bh(s9)#m}`R1V|s=~Gbq
zK7>lT(cjg@{e5}7irO#<Gss4U$bAQ6n^?n>hnENp-5VKBK+P*C7=BtD8zW-j!b{3-
zBp`qY^vG#2kAF`zjz@-V_cP(^h@iw}a=ziI5Z+L?Kecnd5_rJGTM^kFXNDy9l%r%e
zwEX=>m4XoZyTkFx5-_UbNLhnWfu=i;)REj^27QlZ0}*(}lJ)idc5`rYe3&$%oYUL|
z2!EpIZVNGtN>#)Br9w+>I91Bn-Ix!T-5wHE&ggsttyYKP5UHFte;aiVxks;t{bL6^
z+>%=jFLG}G%TEbAST($}p<>3Lni?*nPL|LO_s!c4l+i?ok>LXTMQB}Z{uEY>2Lq{Z
z_}a15_nIgEHL`CN?OGXrUndZ*>p7`fF2~nYHuOMPNfFH?XuL1zlNKMp%yD6J$)(Yz
zsc~`EgrH|%D1+I1s9y@l;xj|#ySs4_`%W+<825H;?3eR{3Qbu#Fto|l#l)myU_$E&
zD$|K1d1T5xKLz44Hr$1-V}FB2yerTaH54@Xx+2>oew`cYZzkL%9A3F5@!uYunLE%8
zdRF-1J|iH8C)Hl<E$MJa5J(Hg(J8fPY#=K&xr>`4Bh--E<a;n<F4b`=6)>>5b^&_Y
zY2hxm`SdINBe10z47POn7DEP_0^!!RI}B(1y#GMTD7Rtp_Ov^`8dKP)czd4i4Ah?9
zc=MM5knN3Uo{Q0|gAKIAi0CpoA%<j)7(}|%V_Y>thI$(=mF{ZzX3U(^q#)}}(&Kkf
zgmZ|3!K0Z{s(yWOIwQaIUmEtrO|u>%__FFmRs$>y(HolBoa))hZ}l4eUjKZ#V7D{*
zq$NYlIlg@zMi6NdCzh*E_)K6u{rMhu<L+nN9-ND!QdoB4UK<k7fTUyhVzi6y?@{#!
zl)=#%V`V*SK)9O};dNA;K!-px%r(w2!!h{vwPe3eD9{LZ%CncWM~ZH-?IU{7Pzo@`
za)2;J-H7yzi6m0fE7f~I=RUA!$<cNp7u%;dB4&!W!rkzzaO>h8!ziCIZz0`B=mNu)
zC=H6>dTA84+*E6CvmHyp4zx+|BDz?J?zdDO*ndwut{7UkT5?g8oYi0?#v&wW<7DvK
zqZ!@KR}*6<gs@e*_%pstMtxuFJxZYc@6)jTp<H12ghjBCB2TI&J(e*;DG({bPr?EH
zOBIe~R<{aDdydw(u`J+30h_20^gA)pb{yTck)QEz!sCT#vp6`>*RbCmvrqv=YB3Jg
z<%NQrHl-iQs93U=Bw?d?&M_oiPw9bzVRi;xW;Jc059^zXz3yKvp2jgV*F8n+qH^_N
z?vdD(^$Tof%$JRh!oN(dEO;0kpsLfpsAsSFzlV$Pj7tWXEr>!r$wp_W>d){r_XOOy
zB%^<w!gyo<W+9UZB!xRg<ecB%xH~Z?mlpDs!XJ=t+=Smj-sIGzxf1NC)M;~x?*Q4}
zRlhhF%u^<wzk^}1dxaZka7UAD0roZ$&VnaGW)mjoXod}k1g_>k1&{=dYrsV?xpQ#O
z+9tq(x40l518#4F-;TeGa3YTRlnJ2ZJ}^EW#2a)`pGiX4;8nmZeJwIqwkRb~!EIo|
zECcp7f;z1L%(@Q-9d>QH8>)@sMUgdsDHCcv+T3tQc=<hdH?>i_NfoJyo0pZnh+v2>
z7#PbZZiZc&x6!GTiuG4av0W|qhjNn<1jRRo`O(4X^kES|>{6eZ7%&=lnuPxi=y^gX
z#3y;akdp+*5UDypi6c4ILd8A=7x0=-Q<;X;8%pQMLw{PFti@nO_|7mz5-#!Z(d?v~
zK^<|{-3@fu-y5#oRT$S5&$|XoA23W{J7Iv<#u|TZ21`3GT#kzD;ZuZI5Z66>nr=RV
zB5c(DXTAQ}P)87aMJ7GIt2lanMakSLYs5JGEER|d9Cs$Eg+Z{yUHquJ!9}jaan9bq
ztR(Gw<qQ{7+R|)rMOqRyn3$R^I>>|W3jB-0Z(;&kmFox=DG-Ki@Zpbv%VHdj0tAb7
zn9EW(MYG6v>df7tsEYFxQ!i(J!)Qb+tZ0JC2hsBiw?tcLQ~!$OTSb!--l2gJDn`L{
zhB0_vz7cMiBJD%C`@R0Xoh+lwpcP<Ih!eE430<<OUIQt|P=^@72sHn1s#*5<*?ON_
zfPfsHFR&6PVp=Fq>R1{@Hf=Lc23|anRiy@1uT;4Wa}s!|xcH87d~kIHv8dK5j5XiE
z`*8}F7!fwD(-&b=v2oX+b3t_sAO^xY4Nxg?=lPS&(IDRQuT35nqFf%2vJ6wZZwGuh
z&M`}l#_xhMg}w%nZMl<dDehr_@dLR??=VP-MtbqxG-QQKGy*NLUX>of$X18I<zgE9
z@o2>7p0#Q)K*B{2zF>H}z+#h+J&_Zk!zLUUwu88JCF92rX2-^u`1k^c(l>dFNAwyt
zQM>G^DaBWFgJW83^JDuok<sDgvbeh+j%_P3|2%g$rl`#Ml&|QqI&3Jz=<4ejM+TPZ
zUK%Z;6c?eqo;qV;zrs^;=vSfK1^u+Zs6y4jNfkYO2QzSj#!nH4xWo)`iz{Dn7>n<K
zjm6VKX>IUJ2qUoFduAN&n*`;UduJw$jxD6M$c_f%pUJ`eEC|Qh3EP=!)V-5OSqf{#
zHDxx<d90QKnkvOBHG^r5<-@l%kNvg6x!%&l4ebiYwlX*mh^WU2tCY3v2bTCmoYjO`
zC+jKzgKqhpZL;1eN_!hLl3@su3U^M=G83hrEk@a`o;J)#_9=@qx2(AZ1rAbTS#<sU
zJd&aIBw%GWfeUovo)(>|(^<F~ROM9olW(UF{$9?)6qKMk$QH9m(N5dNK~C={3coVl
zNjyW9<-8ey>@a*-iPzpe$3aZ-2j-s2?}~jE@>DyjScka{9-x?yld3Eb_KHVpTR4+i
zdB*1@fd;sz7SkNx!`Yr(LF2C?!jWfizNybX*77VT8AKkMYC(Yx#<^cF&LJM5+AOG5
zSq%l7wVg%rvSL~92t$xay>xWO>0qvB1Ao(*qIg4sv%Y9xvTwmL4sve{?4)=zY5tYM
zFCKj#hMx#w;Ar+fAus$h!Yv;wNau9-=1Wg$;aD9&bOMT6|1urt+}*U{PbG$h$;Ne}
zY&G$6_mQA(6o)ovQE(hb`IsQtN1UiIvM&f6{e0x6VeSdDMd1mxj5;gMr1lQW{6z9@
zNy7mcIXv_xC+J6U=NuOHY@(E_NJ5n9<OD=TO1Y8#D2ucHsXay8q#1w0YT{!WI<99w
z3b(<f+y*<C&C!!LJs7;PI)oGKa5nAhs8&L=VhblGn4*aabqaRqWu_QhV1>P#h=4?v
zEQ*en9~Jv1O}&xB&^jt?_awv69(-{BNzQF<7gYVSa3=tQXKI>0W<AR4+hSo58xZAB
ze$-QQ`g3*@O4qJZqpVQm(}p}Y-$uw%4vBz{y#u~=2ttfR-am&^zz@s+%f1l+N4!}p
z`dhj~;^N5bCNUElJj)B;^tz$np^&>{$h|$inn-MX-Ng%cxCfYrZ|X#O_#yA@v7*&m
zRLGafloSg()tjX%3Lv9M{}LT=fJG{iPYG=cx-qAG2F4v;hOmy@Dzqa4KEpeQZ%`1T
zMz*6u?Wm3bH_#LB4R*Q**R4LcjC>H#e+9z7zNG$uRS8J@&OK@yu{_#`SIQrb43lp^
z`r$9Zz;<0LqqtPc{VkUl5v=;9dSRnFK@cV7&e2NKkV$}DZ{_V`*})lx@7t=hr@d4*
zkQ&w7lE0U;wN@5LTlq`aeXXcai5krd%nnVP1q9aRzg-}(e*>PY#EZI#U?(<dOzl_^
zo7;(^rp+1-A}rC-uBj>KL4>-Z2HDU)vT;k}HVI{2I`VUrF2XA7b2>0fpV@Kfey4ah
zaUp|>y+%`IWvN(E`5$LeH6@8*(r})6jV+$crC2<7$YEqgpVk*iBvY42YqS(umK9TX
z`v!-a=0}H>i0<SH3d%`LOVb!Qp^f;!g;J}?6+u*@92Y`6j;hj#Cq^e|EeWcrpS*uX
z#)#Vn0fzU35`#n`RAV6jfvf+XbE1Nu@|NKQ5Hf1)XifriSxnr!ssxXm(DbXWjwX%N
zc-7DZPs7M04lhfS7WOoLeI<Qxt-ONuiIf@NiZ0w&wvcY-9q^gZ!B&WSdwrlY-2jY^
zu(>t^&Xw7TY8)iSD^sQd<}a(AWnEig8`wGeS@0NeO5C~URU|VQKj9IV&S>pS0c<4F
z5BWF=V04RxX(geR&v1!FC(QQE2GKaTa>z2wUcnT&Ik8lEJgA5X8bzbMyNrJ+Ke|<i
zbe)mfnnit=k{DEZut=j$s4_TDxb=cY+uu30Dv0(;MhSVRXO65&E<80L&d)W^tIo?f
z!&2^aPM_}ZFjn}VTWD?!@@Q$ETWP*(%thd|WW7A=`@~Hu<PWuGgE1P?mAM53+0@k9
zT4@OMs<Ji~%O38Iq~3Mmw5Fv9zI`hxjp|5bLkPDflP=C~!k~p^nkVyA=<#V?5aE#j
zX{zE_<LZ-yHBb8q4AYea%KrKjt|@g`UNDE?(!I^V(PPX*KuBm5Z(!{ih7rGY)=+NZ
zNk>x=?xK@3w9QFGzRKJq3fJE<TUTl3NvC#&Ek8_0J{UhIhT~em#jl4J;pz<av<sNm
zM*Hx7PdOFTt^+jRmLV-qWfiR@;(4J5$u)XMd>4HG3%DHZuK)QK^2B%gad6<qbLI2v
z`Dx$3*0}!($awwPl;|fZIsWoDa6Vwj_x%JilE;NmUb@c^0>OHo!MKp80CqAnup2|n
zeGT(SkDh}1s?&QX7WbHg{Yd?2o-*l-eIUM$hF{OvkH6{&F53A3w;DIj5}!ej)3WcF
znnlhHZci<%4jv;M;Pm>^Y-wIx^8R!!l)Fw1({#-_NHgED3cuHC6lZU5ZwE~|H?m^Q
zL+Y&NE@lmLkRnnPe$3g8+woWY1$p_t97*JrrMDrQ#q0O$SN(atp^Yq6cq%ofdm+g6
zs6&6#+wpSL@nc@Dm~~fAU=Y+|{6$SNs4_}61E<<mK^Ons#n6KdV(?F{mpaFR*#%X_
zeGR`9X>Pn(5f_xdvM|}{caErezcQ*}x=N(WP7z3RZ19zLldETiG|Bj68)w)BUQ;f)
zP|n8?#`^PF#IcKJLF5A|9I2ll>5{x2F6nmO)}N_CIf`+a^4H)+`4lkH6b>yXf>N#^
zKo%^%-?=B3CxynJgk;%&Bd63igrdy*u#g$N^2DX|EXKeWGqPSwEON=7TK~_|P$UCO
zIC6`ICsJNt3Ynti>z+u39tccv*BOq>1Ep~T%8a+mzCeX9&`0q-^ZItdytk(#*1Ycz
z*OBw#(nT(xkc{V;fM5I)7H?p@M|1}>Dqtk%Gp3~YzsK+vG>lVV7CcI4K=089eGKmh
za>PC8ueGFZ;+Nfxfom;^7<nTKGkbG%7v%qq`RWVFHlsGV3`D7HsLcCi^<{pWQT;38
z;^erfyeZue;OWiX&HN|wWfz+9%w;2#NYxvzzZF08T`>_{Lq73FsVZw8Kz?wIdacN?
zd5d0W_@Dk&X@$yrxvWsLHmp<peHaYAoAFF{A3O^GcD<<FaUf!_4{0LzjHu_RP%txi
zK*7jVuXtwo-#H*Z*h!gOM<Kmi|2cbmX>x3MRoW<DW%1nfgzJx~BhKcQjZPx4cbKl<
z5N@#nB|S^5C(|4u>K#I^h_c4Gi?*x<KG8{Wsa7~JI#YO7+9pDgu1$a~at?mH7Yi=s
z_8&}zDf7NFhlYN<|Am%qsl=!VZ_*eDsq3oaVdB^`_*G2b{A5{$w%Fvdk@{a{ikh3v
z`<*@Xe_Q18+PuhiUGn;=P5I&q5vP;rbMwo0&nbH#4$Ci^w3C+NTw5``pTIN}e_YeQ
zpHP3T=In2{uI3x#hKX6rYu?kOD-SdmUkOOu4Splg@Hd`lBd_nAr3WB8t)5|Q?T=hD
zaxtWinZGaT%z)HA;u4!1reL|Cm35Sm|I;X-OhWgOd^bdDf)B3&|02WB{rCIJ5R;L8
ziRSJ2822a0!*-tmo548ZWDahJSMDdZOA3@|z5FC9+^+E}GGpMw&`lK@`Cqt4I=uRW
zjJUXjDB;jL-&aE|Mbb=@MNQT)2l?aou~$9>8ZL$SUgG*AnsyXaDO&gv`st!Yq?uzQ
z3}t=ixT)2kp?3RC)gSH8Bb#%;kq75M8{Z2NB6<YmaBBt8SFYBa>19KXyg*I&`-CG`
zF>^h``uzn8NKF8l7p>Ge74kE;In*lw+SHBz?;bWd#p<VPmUoI)twtmJEZ<_-PwS{p
zn;f$$vN}25T`o^PE0XJZQrNtNjNj0bJKzcy=dG^1U#T{~w?CgQKfb=uhO&BWb~x8_
zfJt)^W|`&9=337zAb(a04zdPyXcsSQd1~_xVv>#`Hz4!)M?%+}0$7u#O-r|Th$<LK
z=!%MXFXt*^PqvHJA|sP^g1GZ;-}-V|jU)K~=CPAB;JK6!a|LATWUjGmUOrUOOn9BF
zZW2C0R+dF8{3*8nZ4t3pm)%mTaeMwh4B-5)sN2uigOtP8-_w9)&WDGWhJEbUf3B=Q
z?w@a5dq2xu-MvQ8_t1E5zt3KKexS3zunB*{ntNJz4X+pBdKugS%iNtellDyceRpqk
zcL7%jk^-tbKkPTI@&jzpA$JvBKbJlOIs0}D-~rcT?(G$y!Xs{m9R3U|Bg5Fk?vt#!
zxZ7X<xnDeo9;~;?9u~xtd{@Cu+$X+X#~UdXvHY&6m<Fas*s%A>4|6P(PoIh!p0^BQ
z<4<=&^1Tn<N3)>+Ar~x(DOVq?%&-5ad+AU>DbQtNuVRZr&k;wm+`-l3;817V2sv#F
zekul=YxTb-DJrQucdK?gmu(85ZxGW$+_J<F#+Fn~ULSnz?0rcztv)?rG|UaW!yC!P
zfX)Xxt>9n08z!tlxTXK%M8T05d%8qPHc2KP)<PT0rKi3ROk(rp%NJ04SOCbijq-L+
zy+_rqC}^M)E_DJmm-WrZz#CDG216IfMf%6rIKH!lP{d8n-4|$gChk7#nb0P*&4Wy>
zMlEb`?h-EU_-(8zY4XXQCE78v^sb)dRQchOM+y|ThZRza1<YSUURQ^m3aazZ>sbxB
z(|BI^kqN<aq|xj0kY2^sn`>+bsp7f)|MH#~VNt?(RZAEKM|*#dU53#dR6PZqYI$ax
z$kee2urbM2lqnJsl2alhh(Ix0MCi7}eVZx0%de2E8`9fLUS)f6>CoLSQjJ=#CFHT6
z2sQ%BeL@tBI#0Rxj&b@;A6j15oWcG?b~@I;_CDp(yu4ny=N#D(+PQS>a`M@Ifwe1w
z{eY%cm=p~%(7#sB_Cr;kbX6L5Dvx*Tk6D651L9hQn0}ro%<nFYS1{sAT~Y3f(O(By
zo<a_X641rh=gCi0iY@GO^qTdga%1`m9j7K8V|GkZ;h4|c=n|t*YUT-yxN)A<ZY#|Q
z7hpG9|13zv*T?}gJg{i1f7Oz`2-hjo%OY|xT$oSvZ$H9?MGMNKDlc88518R$P)?n?
zsEq!TtCo0OrCq$nFr*)ByjczDDkE5Y3YK^?IU_Z=_5~D^c=x5ly3j0;HOKbr=+n!|
z-)B5pRp(UpFNM{mUWsmGbS1gnC{tLYrL1Ro=vt(Cx(KXazPWu+MX!f5#@%10SkGyG
zR$jkl6vmS%Fiu!M36Y{_47Z9q-wZ$d;3*pofm%=Qne$HV$QxJ8La-rq0NIV}E2w6#
c)F<F*D++`E=jH7@><77a2V4UL1`Oo?0^lT~%>V!Z

literal 0
HcmV?d00001

diff --git a/source/agent_based/freeradius.py b/source/agent_based/freeradius.py
new file mode 100644
index 0000000..74f5425
--- /dev/null
+++ b/source/agent_based/freeradius.py
@@ -0,0 +1,467 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# License: GNU General Public License v2
+#
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 2024-04-29
+# File  : freeradius.py (check plugin)
+
+
+from _collections_abc import Mapping, Sequence
+from json import loads as json_loads, JSONDecodeError
+from time import localtime, mktime, strftime, strptime, time as now_tine
+
+from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+    Result,
+    Service,
+    State,
+    check_levels,
+    register,
+    render,
+    get_rate,
+    GetRateError,
+    get_value_store,
+)
+from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
+    CheckResult,
+    DiscoveryResult,
+    StringTable,
+)
+
+_CMK_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%m %Z'
+_FREERADIUS_TIME_FORMAT = "%b %d %Y %H:%M:%S %Z"
+
+# Authentication attributes
+_FreeRADIUS_Total_Access_Requests = 'FreeRADIUS-Total-Access-Requests'
+_FreeRADIUS_Total_Access_Accepts = 'FreeRADIUS-Total-Access-Accepts'
+_FreeRADIUS_Total_Access_Rejects = 'FreeRADIUS-Total-Access-Rejects'
+_FreeRADIUS_Total_Access_Challenges = 'FreeRADIUS-Total-Access-Challenges'
+_FreeRADIUS_Total_Auth_Responses = 'FreeRADIUS-Total-Auth-Responses'
+_FreeRADIUS_Total_Auth_Duplicate_Requests = 'FreeRADIUS-Total-Auth-Duplicate-Requests'
+_FreeRADIUS_Total_Auth_Malformed_Requests = 'FreeRADIUS-Total-Auth-Malformed-Requests'
+_FreeRADIUS_Total_Auth_Invalid_Requests = 'FreeRADIUS-Total-Auth-Invalid-Requests'
+_FreeRADIUS_Total_Auth_Dropped_Requests = 'FreeRADIUS-Total-Auth-Dropped-Requests'
+_FreeRADIUS_Total_Auth_Unknown_Types = 'FreeRADIUS-Total-Auth-Unknown-Types'
+_FreeRADIUS_Total_Auth_Conflicts = 'FreeRADIUS-Total-Auth-Conflicts'
+# Accounting attributes
+_FreeRADIUS_Total_Accounting_Requests = 'FreeRADIUS-Total-Accounting-Requests'
+_FreeRADIUS_Total_Accounting_Responses = 'FreeRADIUS-Total-Accounting-Responses'
+_FreeRADIUS_Total_Acct_Duplicate_Requests = 'FreeRADIUS-Total-Acct_Duplicate-Requests'
+_FreeRADIUS_Total_Acct_Malformed_Requests = 'FreeRADIUS-Total-Acct-Malformed-Requests'
+_FreeRADIUS_Total_Acct_Invalid_Requests = 'FreeRADIUS-Total-Acct-Invalid-Requests'
+_FreeRADIUS_Total_Acct_Dropped_Requests = 'FreeRADIUS-Total-Acct-Dropped-Requests'
+_FreeRADIUS_Total_Acct_Unknown_Types = 'FreeRADIUS-Total-Acct-Unknown-Types'
+_FreeRADIUS_Total_Acct_Conflicts = 'FreeRADIUS-Total-Acct-Conflicts'
+# Internal Attributes
+_FreeRADIUS_Stats_Start_Time = 'FreeRADIUS-Stats-Start-Time'
+_FreeRADIUS_Stats_HUP_Time = 'FreeRADIUS-Stats-HUP-Time'
+_FreeRADIUS_Queue_Len_Internal = 'FreeRADIUS-Queue-Len-Internal'
+_FreeRADIUS_Queue_Len_Proxy = 'FreeRADIUS-Queue-Len-Proxy'
+_FreeRADIUS_Queue_Len_Auth = 'FreeRADIUS-Queue-Len-Auth'
+_FreeRADIUS_Queue_Len_Acct = 'FreeRADIUS-Queue-Len-Acct'
+_FreeRADIUS_Queue_Len_Detail = 'FreeRADIUS-Queue-Len-Detail'
+_FreeRADIUS_Queue_PPS_In = 'FreeRADIUS-Queue-PPS-In'
+_FreeRADIUS_Queue_PPS_Out = 'FreeRADIUS-Queue-PPS-Out'
+# Proxy-Authentication attributes
+_FreeRADIUS_Total_Proxy_Access_Requests = 'FreeRADIUS-Total-Proxy-Access-Requests'
+_FreeRADIUS_Total_Proxy_Access_Accepts = 'FreeRADIUS-Total-Proxy-Access-Accepts'
+_FreeRADIUS_Total_Proxy_Access_Rejects = 'FreeRADIUS-Total-Proxy-Access-Rejects'
+_FreeRADIUS_Total_Proxy_Access_Challenges = 'FreeRADIUS-Total-Proxy-Access-Challenges'
+_FreeRADIUS_Total_Proxy_Auth_Responses = 'FreeRADIUS-Total-Proxy-Auth-Responses'
+_FreeRADIUS_Total_Proxy_Auth_Duplicate_Requests = 'FreeRADIUS-Total-Proxy-Auth-Duplicate-Requests'
+_FreeRADIUS_Total_Proxy_Auth_Malformed_Requests = 'FreeRADIUS-Total-Proxy-Auth-Malformed-Requests'
+_FreeRADIUS_Total_Proxy_Auth_Invalid_Requests = 'FreeRADIUS-Total-Proxy-Auth-Invalid-Requests'
+_FreeRADIUS_Total_Proxy_Auth_Dropped_Requests = 'FreeRADIUS-Total-Proxy-Auth-Dropped-Requests'
+_FreeRADIUS_Total_Proxy_Auth_Unknown_Types = 'FreeRADIUS-Total-Proxy-Auth-Unknown-Types'
+# Proxy-Accounting attributes
+_FreeRADIUS_Total_Proxy_Accounting_Requests = 'FreeRADIUS-Total-Proxy-Accounting-Requests'
+_FreeRADIUS_Total_Proxy_Accounting_Responses = 'FreeRADIUS-Total-Proxy-Accounting-Responses'
+_FreeRADIUS_Total_Proxy_Acct_Duplicate_Requests = 'FreeRADIUS-Total-Proxy-Acct-Duplicate-Requests'
+_FreeRADIUS_Total_Proxy_Acct_Malformed_Requests = 'FreeRADIUS-Total-Proxy-Acct-Malformed-Requests'
+_FreeRADIUS_Total_Proxy_Acct_Invalid_Requests = 'FreeRADIUS-Total-Proxy-Acct-Invalid-Requests'
+_FreeRADIUS_Total_Proxy_Acct_Dropped_Requests = 'FreeRADIUS-Total-Proxy-Acct-Dropped-Requests'
+_FreeRADIUS_Total_Proxy_Acct_Unknown_Types = 'FreeRADIUS-Total-Proxy-Acct-Unknown-Types'
+
+_FreeRADIUS_Authentication = [
+    _FreeRADIUS_Total_Access_Requests,
+    _FreeRADIUS_Total_Auth_Responses,
+    _FreeRADIUS_Total_Access_Accepts,
+    _FreeRADIUS_Total_Access_Rejects,
+    _FreeRADIUS_Total_Access_Challenges,
+    _FreeRADIUS_Total_Auth_Duplicate_Requests,
+    _FreeRADIUS_Total_Auth_Malformed_Requests,
+    _FreeRADIUS_Total_Auth_Invalid_Requests,
+    _FreeRADIUS_Total_Auth_Dropped_Requests,
+    _FreeRADIUS_Total_Auth_Unknown_Types,
+    _FreeRADIUS_Total_Auth_Conflicts,
+]
+
+_FreeRADIUS_Accounting = [
+    _FreeRADIUS_Total_Accounting_Requests,
+    _FreeRADIUS_Total_Accounting_Responses,
+    _FreeRADIUS_Total_Acct_Duplicate_Requests,
+    _FreeRADIUS_Total_Acct_Malformed_Requests,
+    _FreeRADIUS_Total_Acct_Invalid_Requests,
+    _FreeRADIUS_Total_Acct_Dropped_Requests,
+    _FreeRADIUS_Total_Acct_Unknown_Types,
+    _FreeRADIUS_Total_Acct_Conflicts
+]
+
+_FreeRADIUS_Queue = [
+    _FreeRADIUS_Queue_PPS_In,
+    _FreeRADIUS_Queue_PPS_Out,
+    _FreeRADIUS_Queue_Len_Acct,
+    _FreeRADIUS_Queue_Len_Auth,
+    _FreeRADIUS_Queue_Len_Detail,
+    _FreeRADIUS_Queue_Len_Internal,
+    _FreeRADIUS_Queue_Len_Proxy,
+]
+
+_FreeRADIUS_Total_Proxy_Authentication = [
+    _FreeRADIUS_Total_Proxy_Access_Requests,
+    _FreeRADIUS_Total_Proxy_Auth_Responses,
+    _FreeRADIUS_Total_Proxy_Access_Accepts,
+    _FreeRADIUS_Total_Proxy_Access_Challenges,
+    _FreeRADIUS_Total_Proxy_Access_Rejects,
+    _FreeRADIUS_Total_Proxy_Auth_Dropped_Requests,
+    _FreeRADIUS_Total_Proxy_Auth_Duplicate_Requests,
+    _FreeRADIUS_Total_Proxy_Auth_Invalid_Requests,
+    _FreeRADIUS_Total_Proxy_Auth_Malformed_Requests,
+    _FreeRADIUS_Total_Proxy_Auth_Unknown_Types,
+]
+
+_FreeRADIUS_Total_Proxy_Accounting = [
+    _FreeRADIUS_Total_Proxy_Accounting_Requests,
+    _FreeRADIUS_Total_Proxy_Accounting_Responses,
+    _FreeRADIUS_Total_Proxy_Acct_Dropped_Requests,
+    _FreeRADIUS_Total_Proxy_Acct_Duplicate_Requests,
+    _FreeRADIUS_Total_Proxy_Acct_Invalid_Requests,
+    _FreeRADIUS_Total_Proxy_Acct_Malformed_Requests,
+    _FreeRADIUS_Total_Proxy_Acct_Unknown_Types,
+]
+
+
+def _get_metric(rwa_attribute: str) -> str:
+    return rwa_attribute.lower().replace('-', '_')
+
+
+def _rate_attributes(params: Mapping[str, any], section: Mapping[str, int], attributes: Sequence):
+    def _get_label(raw_attribute: str) -> str:
+        raw_attribute = raw_attribute.replace('FreeRADIUS-Total-Proxy-', '')
+        raw_attribute = raw_attribute.replace('FreeRADIUS-Total-', '')
+        raw_attribute = raw_attribute.replace('Access-', '')
+        raw_attribute = raw_attribute.replace('Auth-', '')
+        raw_attribute = raw_attribute.replace('Accounting-', '')
+        raw_attribute = raw_attribute.replace('Acct-', '')
+        raw_attribute = raw_attribute.replace('-', ' ')
+        raw_attribute = raw_attribute.replace(' Requests', '')
+
+        return raw_attribute
+
+    attribute_params = {attribute: params for attribute, params in params.get('attributes', [])}
+    now = now_tine()
+    value_store = get_value_store()
+
+    for attribute, notice_only, upper, lower in attributes:
+        if (value := section.get(attribute)) is not None:
+            try:
+                value = get_rate(value_store, _get_metric(attribute), now, value, raise_overflow=True)
+            except GetRateError:
+                continue
+            yield from check_levels(
+                value=value,
+                label=_get_label(attribute),
+                render_func=lambda v: f'{v:.2f}/s',
+                metric_name=_get_metric(attribute),
+                notice_only=attribute_params.get(attribute, {}).get('info_line', notice_only),
+                levels_upper=attribute_params.get(attribute, {}).get('upper'),
+                levels_lower=attribute_params.get(attribute, {}).get('lower'),
+            )
+
+
+def _list_attributes(section: Mapping[str, int], attributes: Sequence[str] | None):
+    yield Result(state=State.OK, notice=f'\nAttributes:')
+    for key, value in section.items():
+        if attributes is None or key in attributes:
+            yield Result(state=State.OK, notice=f'{key}:{value}')
+
+
+def parse_freeradius(string_table: StringTable) -> Mapping[str, int] | None:
+    try:
+        return json_loads(string_table[0][0])
+    except (JSONDecodeError, TypeError):
+        return
+
+
+register.agent_section(
+    name="freeradius",
+    parse_function=parse_freeradius,
+)
+
+
+def discover_freeradius(section: Mapping[str, int]) -> DiscoveryResult:
+    yield Service()
+
+
+def check_freeradius(params: Mapping[str, any], section: Mapping[str, int]) -> CheckResult:
+    if 'error' in section:
+        yield Result(state=State.CRIT, summary=section['error'])
+        return
+    if (start_time := section.get(_FreeRADIUS_Stats_Start_Time)) is None or (
+            hup_time := section.get(_FreeRADIUS_Stats_HUP_Time)) is None:
+        yield Result(
+            state=State.WARN,
+            summary=f'Response attributes {_FreeRADIUS_Stats_Start_Time} and {_FreeRADIUS_Stats_Start_Time} not found.'
+        )
+        return
+
+    if isinstance(start_time, str):
+        # str to sec "Apr 29 2024 10:09:54 CEST" -> 1714378194.0
+        start_time = mktime(strptime(start_time, _FREERADIUS_TIME_FORMAT))
+    if isinstance(hup_time, str):
+        hup_time = mktime(strptime(hup_time, _FREERADIUS_TIME_FORMAT))
+
+    yield from check_levels(
+        value=now_tine() - start_time,
+        label='Uptime',
+        render_func=render.timespan,
+        metric_name='uptime',
+    )
+
+    yield from check_levels(
+        value=now_tine() - hup_time,
+        label='Reload',
+        render_func=render.timespan,
+        metric_name='reload',
+    )
+
+    yield Result(
+        state=State.OK,
+        notice=f'Service started at: {strftime(_CMK_TIME_FORMAT, localtime(start_time))}'
+    )
+    if start_time != hup_time:
+        yield Result(
+            state=State.OK,
+            notice=f'Service restarted (HUP) at: {strftime(_CMK_TIME_FORMAT, localtime(hup_time))}'
+        )
+    else:
+        yield Result(state=State.OK, notice='Service restarted (HUP) at: never')
+
+    yield Result(state=State.OK, summary=f'# of status attributes: {len(section)}')
+
+    if params.get('list_attributes'):
+        yield from _list_attributes(section, None)
+
+
+register.check_plugin(
+    name='freeradius',
+    service_name='FreeRADIUS',
+    sections=['freeradius'],
+    discovery_function=discover_freeradius,
+    check_function=check_freeradius,
+    check_default_parameters={},
+    check_ruleset_name='freeradius',
+)
+
+
+def discover_freeradius_queue(section: Mapping[str, int]) -> DiscoveryResult:
+    for attribute in _FreeRADIUS_Queue:
+        if attribute in section:
+            yield Service()
+            break
+
+
+def check_freeradius_queue(params: Mapping[str, any], section: Mapping[str, int]) -> CheckResult:
+    def _get_label(raw_attribute: str) -> str:
+        raw_attribute = raw_attribute.replace('FreeRADIUS-Queue-', '')
+        raw_attribute = raw_attribute.replace('Len-', '')
+        raw_attribute = raw_attribute.replace('Acct', 'Accounting')
+        raw_attribute = raw_attribute.replace('Auth', 'Authentication')
+        raw_attribute = raw_attribute.replace('-', ' ')
+        return raw_attribute
+
+    attribute_params = {attribute: params for attribute, params in params.get('attributes', [])}
+    attributes = [
+        # attribute, notice_only
+        (_FreeRADIUS_Queue_PPS_In, False),
+        (_FreeRADIUS_Queue_PPS_Out, False),
+        (_FreeRADIUS_Queue_Len_Acct, True),
+        (_FreeRADIUS_Queue_Len_Auth, True),
+        (_FreeRADIUS_Queue_Len_Detail, True),
+        (_FreeRADIUS_Queue_Len_Internal, True),
+        (_FreeRADIUS_Queue_Len_Proxy, True),
+    ]
+    for attribute, notice_only in attributes:
+        if (value := section.get(attribute)) is not None:
+            yield from check_levels(
+                value=value,
+                label=_get_label(attribute),
+                render_func=lambda v: f'{v}',
+                metric_name=_get_metric(attribute),
+                notice_only=attribute_params.get(attribute, {}).get('info_line', notice_only),
+                levels_upper=attribute_params.get(attribute, {}).get('upper'),
+                levels_lower=attribute_params.get(attribute, {}).get('lower'),
+            )
+    if params.get('list_attributes'):
+        yield from _list_attributes(section, _FreeRADIUS_Queue)
+
+
+register.check_plugin(
+    name='freeradius_queue',
+    service_name='FreeRADIUS queue',
+    sections=['freeradius'],
+    discovery_function=discover_freeradius_queue,
+    check_function=check_freeradius_queue,
+    check_default_parameters={},
+    check_ruleset_name='freeradius_queue',
+)
+
+
+def discover_freeradius_total_authentication(section: Mapping[str, int]) -> DiscoveryResult:
+    for attribute in _FreeRADIUS_Authentication:
+        if attribute in section:
+            yield Service()
+            break
+
+
+def check_freeradius_total_authentication(params: Mapping[str, any], section: Mapping[str, int]) -> CheckResult:
+    attributes = [
+        # attribute, notice_only
+        (_FreeRADIUS_Total_Access_Requests, False),
+        (_FreeRADIUS_Total_Auth_Responses, False),
+        (_FreeRADIUS_Total_Access_Accepts, True),
+        (_FreeRADIUS_Total_Access_Rejects, True),
+        (_FreeRADIUS_Total_Auth_Dropped_Requests, True),
+        (_FreeRADIUS_Total_Access_Challenges, True),
+        (_FreeRADIUS_Total_Auth_Duplicate_Requests, True),
+        (_FreeRADIUS_Total_Auth_Malformed_Requests, True),
+        (_FreeRADIUS_Total_Auth_Invalid_Requests, True),
+        (_FreeRADIUS_Total_Auth_Unknown_Types, True),
+        (_FreeRADIUS_Total_Auth_Conflicts, True),
+    ]
+    yield from _rate_attributes(params, section, attributes)
+
+    if params.get('list_attributes'):
+        yield from _list_attributes(section, _FreeRADIUS_Authentication)
+
+
+register.check_plugin(
+    name='freeradius_total_authentication',
+    service_name='FreeRADIUS authentication',
+    sections=['freeradius'],
+    discovery_function=discover_freeradius_total_authentication,
+    check_function=check_freeradius_total_authentication,
+    check_default_parameters={},
+    check_ruleset_name='freeradius_total_authentication',
+)
+
+
+def discover_freeradius_total_accounting(section: Mapping[str, int]) -> DiscoveryResult:
+    for attribute in _FreeRADIUS_Accounting:
+        if attribute in section:
+            yield Service()
+            break
+
+
+def check_freeradius_total_accounting(params: Mapping[str, any], section: Mapping[str, int]) -> CheckResult:
+    attributes = [
+        # attribute, notice_only
+        (_FreeRADIUS_Total_Accounting_Requests, False),
+        (_FreeRADIUS_Total_Accounting_Responses, False),
+        (_FreeRADIUS_Total_Acct_Dropped_Requests, True),
+        (_FreeRADIUS_Total_Acct_Duplicate_Requests, True),
+        (_FreeRADIUS_Total_Acct_Malformed_Requests, True),
+        (_FreeRADIUS_Total_Acct_Invalid_Requests, True),
+        (_FreeRADIUS_Total_Acct_Unknown_Types, True),
+        (_FreeRADIUS_Total_Acct_Conflicts, True),
+    ]
+    yield from _rate_attributes(params, section, attributes)
+
+    if params.get('list_attributes'):
+        yield from _list_attributes(section, _FreeRADIUS_Accounting)
+
+
+register.check_plugin(
+    name='freeradius_total_accounting',
+    service_name='FreeRADIUS accounting',
+    sections=['freeradius'],
+    discovery_function=discover_freeradius_total_accounting,
+    check_function=check_freeradius_total_accounting,
+    check_default_parameters={},
+    check_ruleset_name='freeradius_total_accounting',
+)
+
+
+def discover_freeradius_total_proxy_authentication(section: Mapping[str, int]) -> DiscoveryResult:
+    for attribute in _FreeRADIUS_Total_Proxy_Authentication:
+        if attribute in section:
+            yield Service()
+            break
+
+
+def check_freeradius_total_proxy_authentication(params: Mapping[str, any], section: Mapping[str, int]) -> CheckResult:
+    attributes = [
+        # attribute, notice_only
+        (_FreeRADIUS_Total_Proxy_Access_Requests, False),
+        (_FreeRADIUS_Total_Proxy_Auth_Responses, False),
+        (_FreeRADIUS_Total_Proxy_Access_Accepts, True),
+        (_FreeRADIUS_Total_Proxy_Access_Rejects, True),
+        (_FreeRADIUS_Total_Proxy_Access_Challenges, True),
+        (_FreeRADIUS_Total_Proxy_Auth_Duplicate_Requests, True),
+        (_FreeRADIUS_Total_Proxy_Auth_Malformed_Requests, True),
+        (_FreeRADIUS_Total_Proxy_Auth_Invalid_Requests, True),
+        (_FreeRADIUS_Total_Proxy_Auth_Dropped_Requests, True),
+        (_FreeRADIUS_Total_Proxy_Auth_Unknown_Types, True),
+    ]
+    yield from _rate_attributes(params, section, attributes)
+
+    if params.get('list_attributes'):
+        yield from _list_attributes(section, _FreeRADIUS_Total_Proxy_Authentication)
+
+
+register.check_plugin(
+    name='freeradius_total_proxy_authentication',
+    service_name='FreeRADIUS proxy authentication',
+    sections=['freeradius'],
+    discovery_function=discover_freeradius_total_proxy_authentication,
+    check_function=check_freeradius_total_proxy_authentication,
+    check_default_parameters={},
+    check_ruleset_name='freeradius_total_proxy_authentication',
+)
+
+
+def discover_freeradius_total_proxy_accounting(section: Mapping[str, int]) -> DiscoveryResult:
+    for attribute in _FreeRADIUS_Total_Proxy_Accounting:
+        if attribute in section:
+            yield Service()
+            break
+
+
+def check_freeradius_total_proxy_accounting(params: Mapping[str, any], section: Mapping[str, int]) -> CheckResult:
+    attribute = [
+        # attribute, notice_only
+        (_FreeRADIUS_Total_Proxy_Accounting_Requests, False),
+        (_FreeRADIUS_Total_Proxy_Accounting_Responses, False),
+        (_FreeRADIUS_Total_Proxy_Acct_Duplicate_Requests, True),
+        (_FreeRADIUS_Total_Proxy_Acct_Malformed_Requests, True),
+        (_FreeRADIUS_Total_Proxy_Acct_Invalid_Requests, True),
+        (_FreeRADIUS_Total_Proxy_Acct_Dropped_Requests, True),
+        (_FreeRADIUS_Total_Proxy_Acct_Unknown_Types, True),
+    ]
+    yield from _rate_attributes(params, section, attribute)
+
+    if params.get('list_attributes'):
+        yield from _list_attributes(section, _FreeRADIUS_Total_Proxy_Accounting)
+
+
+register.check_plugin(
+    name='freeradius_total_proxy_accounting',
+    service_name='FreeRADIUS proxy accounting',
+    sections=['freeradius'],
+    discovery_function=discover_freeradius_total_proxy_accounting,
+    check_function=check_freeradius_total_proxy_accounting,
+    check_default_parameters={},
+    check_ruleset_name='freeradius_total_proxy_accounting',
+)
diff --git a/source/agents/special/agent_freeradius b/source/agents/special/agent_freeradius
new file mode 100755
index 0000000..f39e8f8
--- /dev/null
+++ b/source/agents/special/agent_freeradius
@@ -0,0 +1,11 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 Checkmk GmbH - License: GNU General Public License v2
+# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
+# conditions defined in the file COPYING, which is part of this source code package.
+
+import sys
+
+from cmk.special_agents.agent_freeradius import main
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/source/checkman/.gitkeep b/source/checkman/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/source/checkman/freeradius b/source/checkman/freeradius
deleted file mode 100644
index 08ef898..0000000
--- a/source/checkman/freeradius
+++ /dev/null
@@ -1,45 +0,0 @@
-title: Dummy check man page - used as template for new check manuals
-agents: linux, windows, aix, solaris, hpux, vms, freebsd, snmp
-catalog: see modules/catalog.py for possible values
-license: GPL
-distribution: check_mk
-description:
- Describe here: (1) what the check actually does, (2) under which
- circumstances it goes warning/critical, (3) which devices are supported
- by the check, (4) if the check requires a separated plugin or
- tool or separate configuration on the target host.
-
-item:
- Describe the syntax and meaning of the check's item here. Provide all
- information one needs if coding a manual check with {checks +=} in {main.mk}.
- Give an example.  If the check uses {None} as sole item,
- then leave out this section.
-
-examples:
- # Give examples for configuration in {main.mk} here. If the check has
- # configuration variable, then give example for them here.
-
- # set default levels to 40 and 60 percent:
- foo_default_values = (40, 60)
-
- # another configuration variable here:
- inventory_foo_filter = [ "superfoo", "superfoo2" ]
-
-perfdata:
- Describe precisely the number and meaning of performance variables
- the check sends. If it outputs no performance data, then leave out this
- section.
-
-inventory:
- Describe how the inventory for the check works. Which items
- will it find? Describe the influence of check specific
- configuration parameters to the inventory.
-
-[parameters]
-foofirst(int): describe the first parameter here (if parameters are grouped
-        as tuple)
-fooother(string): describe another parameter here.
-
-[configuration]
-foo_default_levels(int, int): Describe global configuration variable of
-    foo here. Important: also tell the user how they are preset.
diff --git a/source/checks/agent_freeradius b/source/checks/agent_freeradius
new file mode 100644
index 0000000..171e45e
--- /dev/null
+++ b/source/checks/agent_freeradius
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# License: GNU General Public License v2
+#
+#
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 2024-04-29
+# File  : checks/agent_freeradius
+#
+#
+
+from collections.abc import Mapping, Sequence
+from typing import Any
+
+
+def agent_freeradius_arguments(
+        params: Mapping[str, Any],
+        hostname: str,
+        ipaddress: str | None,
+) -> Sequence[object]:
+    args = []
+
+    if ipaddress is not None:
+        args.extend(['-H', ipaddress])
+    else:
+        args.append('-H $HOSTADDRESS$')
+
+    if (auth_port := params.get("auth_port")) is not None:
+        args.extend(['--auth-port', auth_port])
+
+    if (secret := params.get("secret")) is not None:
+        args.extend(["--secret", passwordstore_get_cmdline("%s", secret)])
+
+    if (timeout := params.get('timeout')) is not None:
+        args.extend(['--timeout', timeout])
+
+    # if (user_name := params.get("user_name")) is not None:
+    #     args.extend([f'--username', user_name])
+    #
+    # if (user_password := params.get("user_password")) is not None:
+    #     args.extend(["--password", passwordstore_get_cmdline("%s", user_password)])
+
+    return args
+
+
+special_agent_info["freeradius"] = agent_freeradius_arguments
diff --git a/source/gui/metrics/freeradius.py b/source/gui/metrics/freeradius.py
new file mode 100644
index 0000000..11d96ed
--- /dev/null
+++ b/source/gui/metrics/freeradius.py
@@ -0,0 +1,323 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# License: GNU General Public License v2
+#
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 2024-04-29
+# File  : freeradius.py (metrics)
+
+from cmk.gui.i18n import _
+
+from cmk.gui.plugins.metrics.utils import (
+    check_metrics,
+    graph_info,
+    metric_info,
+    perfometer_info,
+)
+#
+_color_accepts = '24/a'
+_color_challenges = '42/a'
+_color_rejects = '32/a'
+_color_requests = '16/a'
+_color_responses = '35/b'
+#
+_color_conflicts = '25/a'
+_color_dropped = '43/a'
+_color_duplicate = '13/a'
+_color_invalid = '33/a'
+_color_malformed = '23/a'
+_color_unknown = '53/a'
+# queue
+_color_authentication = '11/a'
+_color_accounting = '21/a'
+_color_detail = '31/a'
+_color_internal = '41/a'
+_color_proxy = '22/a'
+_color_pps_in = '13/a'
+_color_pps_out = '23/b'
+#
+# times
+#
+metric_info['reload'] = {'title': _('Reload'), 'unit': 's', 'color': '13/b', }
+graph_info['freeradius.time'] = {
+    'title': _('FreeRADIUS uptime'),
+    'metrics': [
+        ('reload', 'area'),
+        ('uptime', 'area'),
+    ],
+    'optional_metrics': [
+        'uptime',
+        'reload',
+    ],
+}
+#
+# queue
+#
+_FreeRADIUS_Queue_Len_Internal = 'freeradius_queue_len_internal'
+_FreeRADIUS_Queue_Len_Proxy = 'freeradius_queue_len_proxy'
+_FreeRADIUS_Queue_Len_Auth = 'freeradius_queue_len_auth'
+_FreeRADIUS_Queue_Len_Acct = 'freeradius_queue_len_acct'
+_FreeRADIUS_Queue_Len_Detail = 'freeradius_queue_len_detail'
+_FreeRADIUS_Queue_PPS_In = 'freeradius_queue_pps_in'
+_FreeRADIUS_Queue_PPS_Out = 'freeradius_queue_pps_out'
+
+metric_info[_FreeRADIUS_Queue_Len_Auth] = {
+    'title': _('Authentication'), 'unit': 'count', 'color': _color_authentication
+}
+metric_info[_FreeRADIUS_Queue_Len_Acct] = {'title': _('Accounting'), 'unit': 'count', 'color': _color_accounting}
+metric_info[_FreeRADIUS_Queue_Len_Detail] = {'title': _('Detail'), 'unit': 'count', 'color': _color_detail}
+metric_info[_FreeRADIUS_Queue_Len_Internal] = {'title': _('Internal'), 'unit': 'count', 'color': _color_internal}
+metric_info[_FreeRADIUS_Queue_Len_Proxy] = {'title': _('Proxy'), 'unit': 'count', 'color': _color_proxy}
+metric_info[_FreeRADIUS_Queue_PPS_In] = {'title': _('PPS in'), 'unit': 'count', 'color': _color_pps_in}
+metric_info[_FreeRADIUS_Queue_PPS_Out] = {'title': _('PPS out'), 'unit': 'count', 'color': _color_pps_out}
+
+graph_info['freeradius.queue'] = {
+    'title': _('FreeRADIUS queue length'),
+    'metrics': [
+        (_FreeRADIUS_Queue_Len_Acct, 'line'),
+        (_FreeRADIUS_Queue_Len_Auth, 'line'),
+        (_FreeRADIUS_Queue_Len_Detail, 'line'),
+        (_FreeRADIUS_Queue_Len_Internal, 'line'),
+        (_FreeRADIUS_Queue_Len_Proxy, 'line'),
+    ],
+    'optional_metrics': [
+        _FreeRADIUS_Queue_Len_Auth,
+        _FreeRADIUS_Queue_Len_Acct,
+        _FreeRADIUS_Queue_Len_Detail,
+        _FreeRADIUS_Queue_Len_Internal,
+        _FreeRADIUS_Queue_Len_Proxy,
+    ],
+}
+
+graph_info['freeradius.pps'] = {
+    'title': _('FreeRADIUS queue PPS'),
+    'metrics': [
+        (_FreeRADIUS_Queue_PPS_In, 'area'),
+        (_FreeRADIUS_Queue_PPS_Out, '-area'),
+    ],
+    'optional_metrics': [
+        _FreeRADIUS_Queue_PPS_In,
+        _FreeRADIUS_Queue_PPS_Out,
+    ],
+}
+
+perfometer_info.append(('stacked', [
+    {
+        'type': 'logarithmic',
+        'metric': _FreeRADIUS_Queue_PPS_In,
+        "half_value": 100,
+        "exponent": 3,
+    },
+    {
+        'type': 'logarithmic',
+        'metric': _FreeRADIUS_Queue_PPS_Out,
+        "half_value": 100,
+        "exponent": 3,
+    }
+]))
+#
+# authentication
+#
+_FreeRADIUS_Total_Access_Requests = 'freeradius_total_access_requests'
+_FreeRADIUS_Total_Access_Accepts = 'freeradius_total_access_accepts'
+_FreeRADIUS_Total_Access_Rejects = 'freeradius_total_access_rejects'
+_FreeRADIUS_Total_Access_Challenges = 'freeradius_total_access_challenges'
+_FreeRADIUS_Total_Auth_Responses = 'freeradius_total_auth_responses'
+_FreeRADIUS_Total_Auth_Duplicate_Requests = 'freeradius_total_auth_duplicate_requests'
+_FreeRADIUS_Total_Auth_Malformed_Requests = 'freeradius_total_auth_malformed_requests'
+_FreeRADIUS_Total_Auth_Invalid_Requests = 'freeradius_total_auth_invalid_requests'
+_FreeRADIUS_Total_Auth_Dropped_Requests = 'freeradius_total_auth_dropped_requests'
+_FreeRADIUS_Total_Auth_Unknown_Types = 'freeradius_total_auth_unknown_types'
+_FreeRADIUS_Total_Auth_Conflicts = 'freeradius_total_auth_conflicts'
+
+metric_info[_FreeRADIUS_Total_Access_Requests] = {'title': _('Requests'), 'unit': '1/s', 'color': _color_requests}
+metric_info[_FreeRADIUS_Total_Access_Accepts] = {'title': _('Accepts'), 'unit': '1/s', 'color': _color_accepts}
+metric_info[_FreeRADIUS_Total_Access_Rejects] = {'title': _('Rejects'), 'unit': '1/s', 'color': _color_rejects}
+metric_info[_FreeRADIUS_Total_Access_Challenges] = {'title': _('Challenges'), 'unit': '1/s', 'color': _color_challenges}
+metric_info[_FreeRADIUS_Total_Auth_Responses] = {'title': _('Responses'), 'unit': '1/s', 'color': _color_responses}
+metric_info[_FreeRADIUS_Total_Auth_Duplicate_Requests] = {
+    'title': _('Duplicate'), 'unit': '1/s', 'color': _color_duplicate
+}
+metric_info[_FreeRADIUS_Total_Auth_Malformed_Requests] = {
+    'title': _('Malformed'), 'unit': '1/s', 'color': _color_malformed
+}
+metric_info[_FreeRADIUS_Total_Auth_Invalid_Requests] = {'title': _('Invalid'), 'unit': '1/s', 'color': _color_invalid}
+metric_info[_FreeRADIUS_Total_Auth_Dropped_Requests] = {'title': _('Dropped'), 'unit': '1/s', 'color': _color_dropped}
+metric_info[_FreeRADIUS_Total_Auth_Unknown_Types] = {
+    'title': _('Unknown tyoes'), 'unit': '1/s', 'color': _color_unknown
+}
+metric_info[_FreeRADIUS_Total_Auth_Conflicts] = {'title': _('Conflicts'), 'unit': '1/s', 'color': _color_conflicts}
+
+graph_info['freeradius.authentication'] = {
+    'title': _('FreeRADIUS Authentications'),
+    'metrics': [
+        (_FreeRADIUS_Total_Access_Accepts, 'line'),
+        (_FreeRADIUS_Total_Access_Challenges, 'line'),
+        (_FreeRADIUS_Total_Access_Rejects, 'line'),
+        (_FreeRADIUS_Total_Access_Requests, 'area'),
+        (_FreeRADIUS_Total_Auth_Responses, '-area'),
+    ],
+    'optional_metrics': [
+        _FreeRADIUS_Total_Access_Requests,
+        _FreeRADIUS_Total_Access_Accepts,
+        _FreeRADIUS_Total_Access_Rejects,
+        _FreeRADIUS_Total_Access_Challenges,
+        _FreeRADIUS_Total_Auth_Responses,
+    ],
+}
+
+graph_info['freeradius.authentication.errors'] = {
+    'title': _('FreeRADIUS Authentication Errors'),
+    'metrics': [
+        (_FreeRADIUS_Total_Auth_Conflicts, 'line'),
+        (_FreeRADIUS_Total_Auth_Dropped_Requests, 'line'),
+        (_FreeRADIUS_Total_Auth_Duplicate_Requests, 'line'),
+        (_FreeRADIUS_Total_Auth_Invalid_Requests, 'line'),
+        (_FreeRADIUS_Total_Auth_Malformed_Requests, 'line'),
+        (_FreeRADIUS_Total_Auth_Unknown_Types, 'line'),
+    ],
+    'optional_metrics': [
+        _FreeRADIUS_Total_Auth_Conflicts,
+        _FreeRADIUS_Total_Auth_Dropped_Requests,
+        _FreeRADIUS_Total_Auth_Duplicate_Requests,
+        _FreeRADIUS_Total_Auth_Invalid_Requests,
+        _FreeRADIUS_Total_Auth_Malformed_Requests,
+        _FreeRADIUS_Total_Auth_Unknown_Types,
+    ],
+}
+
+perfometer_info.append(('stacked', [
+    {
+        'type': 'logarithmic',
+        'metric': _FreeRADIUS_Total_Access_Requests,
+        "half_value": 400,
+        "exponent": 3,
+    },
+    {
+        'type': 'logarithmic',
+        'metric': _FreeRADIUS_Total_Auth_Responses,
+        "half_value": 400,
+        "exponent": 3,
+    }
+]))
+#
+# accounting
+#
+_FreeRADIUS_Total_Accounting_Requests = 'freeradius_total_accounting_requests'
+_FreeRADIUS_Total_Accounting_Responses = 'freeradius_total_accounting_responses'
+_FreeRADIUS_Total_Acct_Duplicate_Requests = 'freeradius_total_acct_duplicate_requests'
+_FreeRADIUS_Total_Acct_Malformed_Requests = 'freeradius_total_acct_malformed_requests'
+_FreeRADIUS_Total_Acct_Invalid_Requests = 'freeradius_total_acct_invalid_requests'
+_FreeRADIUS_Total_Acct_Dropped_Requests = 'freeradius_total_acct_dropped_requests'
+_FreeRADIUS_Total_Acct_Unknown_Types = 'freeradius_total_acct_unknown_types'
+_FreeRADIUS_Total_Acct_Conflicts = 'freeradius_total_acct_conflicts'
+
+metric_info[_FreeRADIUS_Total_Accounting_Requests] = {'title': _('Requests'), 'unit': '1/s', 'color': _color_responses}
+metric_info[_FreeRADIUS_Total_Accounting_Responses] = {
+    'title': _('Responses'), 'unit': '1/s', 'color': _color_responses
+}
+metric_info[_FreeRADIUS_Total_Acct_Duplicate_Requests] = {
+    'title': _('Duplicate'), 'unit': '1/s', 'color': _color_duplicate
+}
+metric_info[_FreeRADIUS_Total_Acct_Malformed_Requests] = {
+    'title': _('Malformed'), 'unit': '1/s', 'color': _color_malformed
+}
+metric_info[_FreeRADIUS_Total_Acct_Invalid_Requests] = {'title': _('Invalid'), 'unit': '1/s', 'color': _color_invalid}
+metric_info[_FreeRADIUS_Total_Acct_Dropped_Requests] = {'title': _('Dropped'), 'unit': '1/s', 'color': _color_dropped}
+metric_info[_FreeRADIUS_Total_Acct_Unknown_Types] = {'title': _('Unknown type'), 'unit': '1/s', 'color': _color_unknown}
+metric_info[_FreeRADIUS_Total_Acct_Conflicts] = {'title': _('Conflict'), 'unit': '1/s', 'color': _color_conflicts}
+
+graph_info['freeradius.accounting'] = {
+    'title': _('FreeRADIUS Accounting'),
+    'metrics': [
+        (_FreeRADIUS_Total_Accounting_Requests, 'line'),
+        (_FreeRADIUS_Total_Accounting_Responses, '-line'),
+    ],
+    'optional_metrics': [
+        _FreeRADIUS_Total_Accounting_Requests,
+        _FreeRADIUS_Total_Accounting_Responses,
+    ],
+}
+
+graph_info['freeradius.accounting.errors'] = {
+    'title': _('FreeRADIUS Accounting Errors'),
+    'metrics': [
+        (_FreeRADIUS_Total_Acct_Conflicts, '-line'),
+        (_FreeRADIUS_Total_Acct_Dropped_Requests, 'line'),
+        (_FreeRADIUS_Total_Acct_Duplicate_Requests, 'line'),
+        (_FreeRADIUS_Total_Acct_Invalid_Requests, 'line'),
+        (_FreeRADIUS_Total_Acct_Malformed_Requests, 'line'),
+        (_FreeRADIUS_Total_Acct_Unknown_Types, 'line'),
+    ],
+    'optional_metrics': [
+        _FreeRADIUS_Total_Acct_Conflicts,
+        _FreeRADIUS_Total_Acct_Dropped_Requests,
+        _FreeRADIUS_Total_Acct_Duplicate_Requests,
+        _FreeRADIUS_Total_Acct_Invalid_Requests,
+        _FreeRADIUS_Total_Acct_Malformed_Requests,
+        _FreeRADIUS_Total_Acct_Unknown_Types
+    ],
+}
+
+perfometer_info.append(('stacked', [
+    {
+        'type': 'logarithmic',
+        'metric': _FreeRADIUS_Total_Accounting_Requests,
+        "half_value": 400,
+        "exponent": 3,
+    },
+    {
+        'type': 'logarithmic',
+        'metric': _FreeRADIUS_Total_Accounting_Responses,
+        "half_value": 400,
+        "exponent": 3,
+    }
+]))
+#
+# Proxy-Authentication
+#
+_FreeRADIUS_Total_Proxy_Access_Requests = 'freeradius_total_proxy_access_requests'
+_FreeRADIUS_Total_Proxy_Access_Accepts = 'freeradius_total_proxy_access_accepts'
+_FreeRADIUS_Total_Proxy_Access_Rejects = 'freeradius_total_proxy_access_rejects'
+_FreeRADIUS_Total_Proxy_Access_Challenges = 'freeradius_total_proxy_access_challenges'
+_FreeRADIUS_Total_Proxy_Auth_Responses = 'freeradius_total_proxy_auth_responses'
+_FreeRADIUS_Total_Proxy_Auth_Duplicate_Requests = 'freeradius_total_proxy_auth_duplicate_requests'
+_FreeRADIUS_Total_Proxy_Auth_Malformed_Requests = 'freeradius_total_proxy_auth_malformed_requests'
+_FreeRADIUS_Total_Proxy_Auth_Invalid_Requests = 'freeradius_total_proxy_auth_invalid_requests'
+_FreeRADIUS_Total_Proxy_Auth_Dropped_Requests = 'freeradius_total_proxy_auth_dropped_requests'
+_FreeRADIUS_Total_Proxy_Auth_Unknown_Types = 'freeradius_total_proxy_auth_unknown_types'
+
+check_metrics['check_mk-freeradius_total_proxy_authentication'] = {
+    _FreeRADIUS_Total_Proxy_Access_Requests: {'name': _FreeRADIUS_Total_Access_Requests},
+    _FreeRADIUS_Total_Proxy_Access_Accepts: {'name': _FreeRADIUS_Total_Access_Accepts},
+    _FreeRADIUS_Total_Proxy_Access_Rejects: {'name': _FreeRADIUS_Total_Access_Rejects},
+    _FreeRADIUS_Total_Proxy_Access_Challenges: {'name': _FreeRADIUS_Total_Access_Challenges},
+    _FreeRADIUS_Total_Proxy_Auth_Responses: {'name': _FreeRADIUS_Total_Auth_Responses},
+    _FreeRADIUS_Total_Proxy_Auth_Duplicate_Requests: {'name': _FreeRADIUS_Total_Auth_Duplicate_Requests},
+    _FreeRADIUS_Total_Proxy_Auth_Malformed_Requests: {'name': _FreeRADIUS_Total_Auth_Malformed_Requests},
+    _FreeRADIUS_Total_Proxy_Auth_Invalid_Requests: {'name': _FreeRADIUS_Total_Auth_Invalid_Requests},
+    _FreeRADIUS_Total_Proxy_Auth_Dropped_Requests: {'name': _FreeRADIUS_Total_Auth_Dropped_Requests},
+    _FreeRADIUS_Total_Proxy_Auth_Unknown_Types: {'name': _FreeRADIUS_Total_Auth_Unknown_Types},
+}
+#
+# Proxy-Accounting
+#
+_FreeRADIUS_Total_Proxy_Accounting_Requests = 'freeradius_total_proxy_accounting_requests'
+_FreeRADIUS_Total_Proxy_Accounting_Responses = 'freeradius_total_proxy_accounting_responses'
+_FreeRADIUS_Total_Proxy_Acct_Duplicate_Requests = 'freeradius_total_proxy_acct_duplicate_requests'
+_FreeRADIUS_Total_Proxy_Acct_Malformed_Requests = 'freeradius_total_proxy_acct_malformed_requests'
+_FreeRADIUS_Total_Proxy_Acct_Invalid_Requests = 'freeradius_total_proxy_acct_invalid_requests'
+_FreeRADIUS_Total_Proxy_Acct_Dropped_Requests = 'freeradius_total_proxy_acct_dropped_requests'
+_FreeRADIUS_Total_Proxy_Acct_Unknown_Types = 'freeradius_total_proxy_acct_unknown_types'
+
+check_metrics['check_mk-freeradius_total_proxy_accounting'] = {
+    _FreeRADIUS_Total_Proxy_Accounting_Requests: {'name': _FreeRADIUS_Total_Accounting_Requests},
+    _FreeRADIUS_Total_Proxy_Accounting_Responses: {'name': _FreeRADIUS_Total_Accounting_Responses},
+    _FreeRADIUS_Total_Proxy_Acct_Duplicate_Requests: {'name': _FreeRADIUS_Total_Acct_Duplicate_Requests},
+    _FreeRADIUS_Total_Proxy_Acct_Malformed_Requests: {'name': _FreeRADIUS_Total_Acct_Malformed_Requests},
+    _FreeRADIUS_Total_Proxy_Acct_Invalid_Requests: {'name': _FreeRADIUS_Total_Acct_Invalid_Requests},
+    _FreeRADIUS_Total_Proxy_Acct_Dropped_Requests: {'name': _FreeRADIUS_Total_Acct_Dropped_Requests},
+    _FreeRADIUS_Total_Proxy_Acct_Unknown_Types: {'name': _FreeRADIUS_Total_Acct_Unknown_Types},
+}
diff --git a/source/lib/python3/cmk/special_agents/agent_freeradius.py b/source/lib/python3/cmk/special_agents/agent_freeradius.py
new file mode 100644
index 0000000..cfdda6f
--- /dev/null
+++ b/source/lib/python3/cmk/special_agents/agent_freeradius.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# License: GNU General Public License v2
+#
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 2024-04-29
+# File  : agent_freeradius.py (special agent)
+
+# /etc/freeradius/3.0/radiusd.conf
+# security {
+#     status_server = yes
+# }
+
+#
+# /etc/freeradius/3.0/sites-enabled/status
+#
+# server status {
+#         listen {
+#                 type = status
+#                 # ipaddr = 127.0.0.1
+#                 ipaddr = 0.0.0.0
+#                 port = 18121
+#         }
+#         client cmkbuild {
+#                 ipaddr = 192.168.10.99
+#                 secret = adminsecret
+#         }
+# }
+#
+
+import socket
+import pyrad.client
+from argparse import (
+    Namespace,
+)
+from collections.abc import Mapping, Sequence
+from json import dumps as json_dumps
+from os import environ
+from time import time_ns
+from sys import (
+    exit as sys_exit,
+    stdout as sys_stdout,
+)
+
+from cmk.special_agents.utils.agent_common import (
+    special_agent_main,
+)
+from cmk.special_agents.utils.argument_parsing import create_default_argument_parser
+
+no_radiuslib = False
+try:
+    from pyrad.client import Client as radClient
+    from pyrad.dictionary import Dictionary as radDictionary
+    from pyrad.packet import AccessAccept, AccessReject, AccessRequest, StatusServer
+    from pyrad.client import Timeout as pyTimeout
+except ModuleNotFoundError:
+    no_radiuslib = True
+
+VERSION = '0.1.1-20240430'
+
+
+class Args(Namespace):
+    host: str
+    secret: str
+    username: str
+    password: str
+    auth_port: int
+    timeout: int
+
+
+def write_section(message: Mapping[str, any]):
+    sys_stdout.write('\n<<<freeradius:sep(0)>>>\n')
+    sys_stdout.write(json_dumps(message))
+    sys_stdout.write('\n<<<>>>\n')
+
+
+def parse_arguments(argv: Sequence[str] | None) -> Args:
+    parser = create_default_argument_parser(__doc__)
+    parser.description = 'This is a special agent to collect stats data from FreeRADIS servers.'
+    parser.epilog = (
+        f'(c) thl-cmk[at]outlook[dot], Version: {VERSION}, '
+        f'For more information see: https://thl-cmk.hopto.org'
+    )
+
+    parser.add_argument(
+        '-H', '--host', required=True,
+        help='Host/IP-Address of RADIUS server to query (required)',
+    )
+    parser.add_argument(
+        '--secret', required=True,
+        help='secret RADIUS key',
+    )
+    parser.add_argument(
+        '--auth-port', type=int, default=18121,
+        help='RADIUS authentication port to use.',
+    )
+    parser.add_argument(
+        '--timeout', type=int, default=1,
+        help='RADIUS server timeout',
+    )
+
+    return parser.parse_args(argv)
+
+
+def agent_freeradius_main(args: Args) -> int:
+    if no_radiuslib:
+        write_section(
+            {
+                'error': 'To use this special agent, you must install the Python '
+                         'library “pyrad” in your CMK Python environment.'
+            }
+        )
+        sys_exit(0)
+
+    omd_root = environ["OMD_ROOT"]
+    path_to_dict = f'{omd_root}/local/lib/check_mk/special_agents'
+    # status request
+    status_srv = radClient(
+        server=args.host.strip(),  # not sure where the leading spce comes from
+        authport=args.auth_port,
+        secret=args.secret.encode('utf-8'),
+        dict=radDictionary(f"{path_to_dict}/dictionary"),
+        retries=1,
+        timeout=args.timeout,
+    )
+    status = status_srv.CreateAuthPacket(
+        code=StatusServer,
+        NAS_Identifier="checkmk",
+    )
+    # https://freeradius.org/documentation/freeradius-server/3.2.4/howto/monitoring/statistics.html
+    # status.AddAttribute("FreeRADIUS-Statistics-Type", "Authentication")
+    # status.AddAttribute("FreeRADIUS-Statistics-Type", "Accounting")
+    # status.AddAttribute("FreeRADIUS-Statistics-Type", "Proxy-Authentication")
+    # status.AddAttribute("FreeRADIUS-Statistics-Type", "Proxy-Accounting")
+    # status.AddAttribute("FreeRADIUS-Statistics-Type", "Internal")
+    # status.AddAttribute("FreeRADIUS-Statistics-Type", "Client")
+    # status.AddAttribute('FreeRADIUS-Stats-Client-IP-Address', "192.168.10.99")
+    # status.AddAttribute("FreeRADIUS-Statistics-Type", "Server")
+    # status.AddAttribute("FreeRADIUS-Stats-Server-IP-Address", "192.168.10.95")
+    # status.AddAttribute("FreeRADIUS-Stats-Server-Port", "1645")
+    # status.AddAttribute("FreeRADIUS-Statistics-Type", "Home-Server")
+    status.AddAttribute("FreeRADIUS-Statistics-Type", "All")
+
+    status.add_message_authenticator()
+    try:
+        response = status_srv.SendPacket(status)
+    except pyrad.client.Timeout as e:
+        write_section({'error': f'Special agent request timeout, check configuration!. ({e})'})
+        sys_exit()
+    except socket.gaierror as e:
+        write_section(({'error': f'Special agent network error, check Configuration. ({e})'}))
+        sys_exit()
+
+    if response.code == AccessAccept:
+        attributes = {
+            attribute: response.get(attribute)[0]
+            for attribute in response.keys() if isinstance(attribute, str)}
+        write_section(attributes)
+    else:
+        write_section({'error': 'Response access reject, check configuration'})
+
+    return 0
+
+
+def main() -> int:
+    return special_agent_main(parse_arguments, agent_freeradius_main)
diff --git a/source/lib/python3/cmk/special_agents/dictionary b/source/lib/python3/cmk/special_agents/dictionary
new file mode 100755
index 0000000..125de2f
--- /dev/null
+++ b/source/lib/python3/cmk/special_agents/dictionary
@@ -0,0 +1,446 @@
+#
+# Version $Id: dictionary,v 1.1.1.1 2002/10/11 12:25:39 wichert Exp $
+#
+#   for free radius dictionaries see:  /usr/share/freeradius
+#
+#	This file contains dictionary translations for parsing
+#	requests and generating responses.  All transactions are
+#	composed of Attribute/Value Pairs.  The value of each attribute
+#	is specified as one of 4 data types.  Valid data types are:
+#
+#	string  - 0-253 octets
+#	ipaddr  - 4 octets in network byte order
+#	integer - 32 bit value in big endian order (high byte first)
+#	date    - 32 bit value in big endian order - seconds since
+#					00:00:00 GMT,  Jan.  1,  1970
+#
+#	FreeRADIUS includes extended data types which are not defined
+#	in RFC 2865 or RFC 2866.  These data types are:
+#
+#	abinary - Ascend's binary filter format.
+#	octets  - raw octets, printed and input as hex strings.
+#		  e.g.: 0x123456789abcdef
+#
+#
+#	Enumerated values are stored in the user file with dictionary
+#	VALUE translations for easy administration.
+#
+#	Example:
+#
+#	ATTRIBUTE	  VALUE
+#	---------------   -----
+#	Framed-Protocol = PPP
+#	7		= 1	(integer encoding)
+#
+
+#
+#	Include compatibility dictionary for older users file. Move this
+#	directive to the end of the file if you want to see the old names
+#	in the logfiles too.
+#
+#$INCLUDE dictionary.compat	# compability issues
+#$INCLUDE dictionary.acc
+#$INCLUDE dictionary.ascend
+#$INCLUDE dictionary.bay
+#$INCLUDE dictionary.cisco
+#$INCLUDE dictionary.livingston
+#$INCLUDE dictionary.microsoft
+#$INCLUDE dictionary.quintum
+#$INCLUDE dictionary.redback
+#$INCLUDE dictionary.shasta
+#$INCLUDE dictionary.shiva
+#$INCLUDE dictionary.tunnel
+#$INCLUDE dictionary.usr
+#$INCLUDE dictionary.versanet
+#$INCLUDE dictionary.erx
+$INCLUDE dictionary.freeradius
+#$INCLUDE dictionary.alcatel
+
+#
+#	Following are the proper new names. Use these.
+#
+ATTRIBUTE	User-Name		1	string
+ATTRIBUTE	User-Password		2	string
+ATTRIBUTE	CHAP-Password		3	octets
+ATTRIBUTE	NAS-IP-Address		4	ipaddr
+ATTRIBUTE	NAS-Port		5	integer
+ATTRIBUTE	Service-Type		6	integer
+ATTRIBUTE	Framed-Protocol		7	integer
+ATTRIBUTE	Framed-IP-Address	8	ipaddr
+ATTRIBUTE	Framed-IP-Netmask	9	ipaddr
+ATTRIBUTE	Framed-Routing		10	integer
+ATTRIBUTE	Filter-Id		11	string
+ATTRIBUTE	Framed-MTU		12	integer
+ATTRIBUTE	Framed-Compression	13	integer
+ATTRIBUTE	Login-IP-Host		14	ipaddr
+ATTRIBUTE	Login-Service		15	integer
+ATTRIBUTE	Login-TCP-Port		16	integer
+ATTRIBUTE	Reply-Message		18	string
+ATTRIBUTE	Callback-Number		19	string
+ATTRIBUTE	Callback-Id		20	string
+ATTRIBUTE	Framed-Route		22	string
+ATTRIBUTE	Framed-IPX-Network	23	ipaddr
+ATTRIBUTE	State			24	octets
+ATTRIBUTE	Class			25	octets
+ATTRIBUTE	Vendor-Specific		26	octets
+ATTRIBUTE	Session-Timeout		27	integer
+ATTRIBUTE	Idle-Timeout		28	integer
+ATTRIBUTE	Termination-Action	29	integer
+ATTRIBUTE	Called-Station-Id	30	string
+ATTRIBUTE	Calling-Station-Id	31	string
+ATTRIBUTE	NAS-Identifier		32	string
+ATTRIBUTE	Proxy-State		33	octets
+ATTRIBUTE	Login-LAT-Service	34	string
+ATTRIBUTE	Login-LAT-Node		35	string
+ATTRIBUTE	Login-LAT-Group		36	octets
+ATTRIBUTE	Framed-AppleTalk-Link	37	integer
+ATTRIBUTE	Framed-AppleTalk-Network 38	integer
+ATTRIBUTE	Framed-AppleTalk-Zone	39	string
+
+ATTRIBUTE	Acct-Status-Type	40	integer
+ATTRIBUTE	Acct-Delay-Time		41	integer
+ATTRIBUTE	Acct-Input-Octets	42	integer
+ATTRIBUTE	Acct-Output-Octets	43	integer
+ATTRIBUTE	Acct-Session-Id		44	string
+ATTRIBUTE	Acct-Authentic		45	integer
+ATTRIBUTE	Acct-Session-Time	46	integer
+ATTRIBUTE       Acct-Input-Packets	47	integer
+ATTRIBUTE       Acct-Output-Packets	48	integer
+ATTRIBUTE	Acct-Terminate-Cause	49	integer
+ATTRIBUTE	Acct-Multi-Session-Id	50	string
+ATTRIBUTE	Acct-Link-Count		51	integer
+ATTRIBUTE	Acct-Input-Gigawords    52      integer
+ATTRIBUTE	Acct-Output-Gigawords   53      integer
+ATTRIBUTE	Event-Timestamp         55      date
+
+ATTRIBUTE	CHAP-Challenge		60	string
+ATTRIBUTE	NAS-Port-Type		61	integer
+ATTRIBUTE	Port-Limit		62	integer
+ATTRIBUTE	Login-LAT-Port		63	integer
+ATTRIBUTE   Tunnel-Type             64  integer
+ATTRIBUTE   Tunnel-Medium-Type      65  integer
+ATTRIBUTE   Tunnel-Client-Endpoint  66  string
+ATTRIBUTE   Tunnel-Server-Endpoint  67  string
+ATTRIBUTE	Acct-Tunnel-Connection	68	string
+ATTRIBUTE   Tunnel-Password         69  string
+
+ATTRIBUTE	ARAP-Password           70      string
+ATTRIBUTE	ARAP-Features           71      string
+ATTRIBUTE	ARAP-Zone-Access        72      integer
+ATTRIBUTE	ARAP-Security           73      integer
+ATTRIBUTE	ARAP-Security-Data      74      string
+ATTRIBUTE	Password-Retry          75      integer
+ATTRIBUTE	Prompt                  76      integer
+ATTRIBUTE	Connect-Info		77	string
+ATTRIBUTE	Configuration-Token	78	string
+ATTRIBUTE	EAP-Message		79	string
+ATTRIBUTE	Message-Authenticator	80	octets
+ATTRIBUTE   Tunnel-Private-Group-Id 81  string
+ATTRIBUTE   Tunnel-Assignment-Id    82  string
+ATTRIBUTE   Tunnel-Preference       83  integer
+ATTRIBUTE	ARAP-Challenge-Response	84	string	# 10 octets
+ATTRIBUTE	Acct-Interim-Interval   85      integer
+ATTRIBUTE	NAS-Port-Id		87	string
+ATTRIBUTE	Framed-Pool		88	string
+ATTRIBUTE	NAS-IPv6-Address	95	octets	# really IPv6
+ATTRIBUTE	Framed-Interface-Id	96	octets	# 8 octets
+ATTRIBUTE	Framed-IPv6-Prefix	97	ipv6prefix	# stupid format
+ATTRIBUTE	Login-IPv6-Host		98	octets	# really IPv6
+ATTRIBUTE	Framed-IPv6-Route	99	string
+ATTRIBUTE	Framed-IPv6-Pool	100	string
+ATTRIBUTE   Delegated-IPv6-Prefix   123     ipv6prefix
+
+
+ATTRIBUTE	Digest-Response		206	string
+ATTRIBUTE	Digest-Attributes	207	octets	# stupid format
+
+#
+#	Experimental Non Protocol Attributes used by Cistron-Radiusd
+#
+
+# 	These attributes CAN go in the reply item list.
+ATTRIBUTE	Fall-Through		500	integer
+ATTRIBUTE	Exec-Program		502	string
+ATTRIBUTE	Exec-Program-Wait	503	string
+
+#	These attributes CANNOT go in the reply item list.
+ATTRIBUTE	User-Category		1029	string
+ATTRIBUTE	Group-Name		1030	string
+ATTRIBUTE	Huntgroup-Name		1031	string
+ATTRIBUTE	Simultaneous-Use	1034	integer
+ATTRIBUTE	Strip-User-Name		1035	integer
+ATTRIBUTE	Hint			1040	string
+ATTRIBUTE	Pam-Auth		1041	string
+ATTRIBUTE	Login-Time		1042	string
+ATTRIBUTE	Stripped-User-Name	1043	string
+ATTRIBUTE	Current-Time		1044	string
+ATTRIBUTE	Realm			1045	string
+ATTRIBUTE	No-Such-Attribute	1046	string
+ATTRIBUTE	Packet-Type		1047	integer
+ATTRIBUTE	Proxy-To-Realm		1048	string
+ATTRIBUTE	Replicate-To-Realm	1049	string
+ATTRIBUTE	Acct-Session-Start-Time	1050	date
+ATTRIBUTE	Acct-Unique-Session-Id  1051	string
+ATTRIBUTE	Client-IP-Address	1052	ipaddr
+ATTRIBUTE	Ldap-UserDn		1053	string
+ATTRIBUTE	NS-MTA-MD5-Password	1054	string
+ATTRIBUTE	SQL-User-Name	 	1055	string
+ATTRIBUTE	LM-Password		1057	octets
+ATTRIBUTE	NT-Password		1058	octets
+ATTRIBUTE	SMB-Account-CTRL	1059	integer
+ATTRIBUTE	SMB-Account-CTRL-TEXT	1061	string
+ATTRIBUTE	User-Profile		1062	string
+ATTRIBUTE	Digest-Realm		1063	string
+ATTRIBUTE	Digest-Nonce		1064	string
+ATTRIBUTE	Digest-Method		1065	string
+ATTRIBUTE	Digest-URI		1066	string
+ATTRIBUTE	Digest-QOP		1067	string
+ATTRIBUTE	Digest-Algorithm	1068	string
+ATTRIBUTE	Digest-Body-Digest	1069	string
+ATTRIBUTE	Digest-CNonce		1070	string
+ATTRIBUTE	Digest-Nonce-Count	1071	string
+ATTRIBUTE	Digest-User-Name	1072	string
+ATTRIBUTE	Pool-Name		1073	string
+ATTRIBUTE	Ldap-Group		1074	string
+ATTRIBUTE	Module-Success-Message	1075	string
+ATTRIBUTE	Module-Failure-Message	1076	string
+#		X99-Fast		1077	integer
+
+#
+#	Non-Protocol Attributes
+#	These attributes are used internally by the server
+#
+ATTRIBUTE	Auth-Type		1000	integer
+ATTRIBUTE	Menu			1001	string
+ATTRIBUTE	Termination-Menu	1002	string
+ATTRIBUTE	Prefix			1003	string
+ATTRIBUTE	Suffix			1004	string
+ATTRIBUTE	Group			1005	string
+ATTRIBUTE	Crypt-Password		1006	string
+ATTRIBUTE	Connect-Rate		1007	integer
+ATTRIBUTE	Add-Prefix		1008	string
+ATTRIBUTE	Add-Suffix		1009	string
+ATTRIBUTE	Expiration		1010	date
+ATTRIBUTE	Autz-Type		1011	integer
+
+#
+#	Integer Translations
+#
+
+#	User Types
+
+VALUE		Service-Type		Login-User		1
+VALUE		Service-Type		pdate		2
+VALUE		Service-Type		Callback-Login-User	3
+VALUE		Service-Type		Callback-Framed-User	4
+VALUE		Service-Type		Outbound-User		5
+VALUE		Service-Type		Administrative-User	6
+VALUE		Service-Type		NAS-Prompt-User		7
+VALUE		Service-Type		Authenticate-Only	8
+VALUE		Service-Type		Callback-NAS-Prompt	9
+VALUE		Service-Type		Call-Check		10
+VALUE		Service-Type		Callback-Administrative	11
+
+#	Framed Protocols
+
+VALUE		Framed-Protocol		PPP			1
+VALUE		Framed-Protocol		SLIP			2
+VALUE		Framed-Protocol		ARAP			3
+VALUE		Framed-Protocol		Gandalf-SLML		4
+VALUE		Framed-Protocol		Xylogics-IPX-SLIP	5
+VALUE		Framed-Protocol		X.75-Synchronous	6
+
+#	Framed Routing Values
+
+VALUE		Framed-Routing		None			0
+VALUE		Framed-Routing		Broadcast		1
+VALUE		Framed-Routing		Listen			2
+VALUE		Framed-Routing		Broadcast-Listen	3
+
+#	Framed Compression Types
+
+VALUE		Framed-Compression	None			0
+VALUE		Framed-Compression	Van-Jacobson-TCP-IP	1
+VALUE		Framed-Compression	IPX-Header-Compression	2
+VALUE		Framed-Compression	Stac-LZS		3
+
+#	Login Services
+
+VALUE		Login-Service		Telnet			0
+VALUE		Login-Service		Rlogin			1
+VALUE		Login-Service		TCP-Clear		2
+VALUE		Login-Service		PortMaster		3
+VALUE		Login-Service		LAT			4
+VALUE		Login-Service		X25-PAD			5
+VALUE		Login-Service		X25-T3POS		6
+VALUE		Login-Service		TCP-Clear-Quiet		8
+
+#	Login-TCP-Port		(see /etc/services for more examples)
+
+VALUE		Login-TCP-Port		Telnet			23
+VALUE		Login-TCP-Port		Rlogin			513
+VALUE		Login-TCP-Port		Rsh			514
+
+#	Status Types
+
+VALUE		Acct-Status-Type	Start			1
+VALUE		Acct-Status-Type	Stop			2
+VALUE		Acct-Status-Type	Interim-Update		3
+VALUE		Acct-Status-Type	Alive			3
+VALUE		Acct-Status-Type	Accounting-On		7
+VALUE		Acct-Status-Type	Accounting-Off		8
+#	RFC 2867 Additional Status-Type Values
+VALUE		Acct-Status-Type	Tunnel-Start		9
+VALUE		Acct-Status-Type	Tunnel-Stop		10
+VALUE		Acct-Status-Type	Tunnel-Reject		11
+VALUE		Acct-Status-Type	Tunnel-Link-Start	12
+VALUE		Acct-Status-Type	Tunnel-Link-Stop	13
+VALUE		Acct-Status-Type	Tunnel-Link-Reject	14
+
+#	Authentication Types
+
+VALUE		Acct-Authentic		RADIUS			1
+VALUE		Acct-Authentic		Local			2
+
+#	Termination Options
+
+VALUE		Termination-Action	Default			0
+VALUE		Termination-Action	RADIUS-Request		1
+
+#	NAS Port Types
+
+VALUE		NAS-Port-Type		Async			0
+VALUE		NAS-Port-Type		Sync			1
+VALUE		NAS-Port-Type		ISDN			2
+VALUE		NAS-Port-Type		ISDN-V120		3
+VALUE		NAS-Port-Type		ISDN-V110		4
+VALUE		NAS-Port-Type		Virtual			5
+VALUE		NAS-Port-Type		PIAFS			6
+VALUE		NAS-Port-Type		HDLC-Clear-Channel	7
+VALUE		NAS-Port-Type		X.25			8
+VALUE		NAS-Port-Type		X.75			9
+VALUE		NAS-Port-Type		G.3-Fax			10
+VALUE		NAS-Port-Type		SDSL			11
+VALUE		NAS-Port-Type		ADSL-CAP		12
+VALUE		NAS-Port-Type		ADSL-DMT		13
+VALUE		NAS-Port-Type		IDSL			14
+VALUE		NAS-Port-Type		Ethernet		15
+VALUE		NAS-Port-Type		xDSL			16
+VALUE		NAS-Port-Type		Cable			17
+VALUE		NAS-Port-Type		Wireless-Other		18
+VALUE		NAS-Port-Type		Wireless-802.11		19
+
+#	Acct Terminate Causes, available in 3.3.2 and later
+
+VALUE           Acct-Terminate-Cause    User-Request            1
+VALUE           Acct-Terminate-Cause    Lost-Carrier            2
+VALUE           Acct-Terminate-Cause    Lost-Service            3
+VALUE           Acct-Terminate-Cause    Idle-Timeout            4
+VALUE           Acct-Terminate-Cause    Session-Timeout         5
+VALUE           Acct-Terminate-Cause    Admin-Reset             6
+VALUE           Acct-Terminate-Cause    Admin-Reboot            7
+VALUE           Acct-Terminate-Cause    Port-Error              8
+VALUE           Acct-Terminate-Cause    NAS-Error               9
+VALUE           Acct-Terminate-Cause    NAS-Request             10
+VALUE           Acct-Terminate-Cause    NAS-Reboot              11
+VALUE           Acct-Terminate-Cause    Port-Unneeded           12
+VALUE           Acct-Terminate-Cause    Port-Preempted          13
+VALUE           Acct-Terminate-Cause    Port-Suspended          14
+VALUE           Acct-Terminate-Cause    Service-Unavailable     15
+VALUE           Acct-Terminate-Cause    Callback                16
+VALUE           Acct-Terminate-Cause    User-Error              17
+VALUE           Acct-Terminate-Cause    Host-Request            18
+
+#VALUE		Tunnel-Type		L2TP			3
+#VALUE		Tunnel-Medium-Type	IP			1
+
+VALUE		Prompt			No-Echo			0
+VALUE		Prompt			Echo			1
+
+#
+#	Non-Protocol Integer Translations
+#
+
+VALUE		Auth-Type		Local			0
+VALUE		Auth-Type		System			1
+VALUE		Auth-Type		SecurID			2
+VALUE		Auth-Type		Crypt-Local		3
+VALUE		Auth-Type		Reject			4
+VALUE		Auth-Type		ActivCard		5
+VALUE		Auth-Type		EAP			6
+VALUE		Auth-Type		ARAP			7
+
+#
+#	Cistron extensions
+#
+VALUE		Auth-Type		Ldap			252
+VALUE		Auth-Type		Pam			253
+VALUE		Auth-Type		Accept			254
+
+VALUE		Auth-Type		PAP			1024
+VALUE		Auth-Type		CHAP			1025
+VALUE		Auth-Type		LDAP			1026
+VALUE		Auth-Type		PAM			1027
+VALUE		Auth-Type		MS-CHAP			1028
+VALUE		Auth-Type		Kerberos		1029
+VALUE		Auth-Type		CRAM			1030
+VALUE		Auth-Type		NS-MTA-MD5		1031
+VALUE		Auth-Type		CRAM			1032
+VALUE		Auth-Type		SMB			1033
+
+#
+#	Authorization type, too.
+#
+VALUE		Autz-Type		Local			0
+
+#
+#	Experimental Non-Protocol Integer Translations for Cistron-Radiusd
+#
+VALUE		Fall-Through		No			0
+VALUE		Fall-Through		Yes			1
+
+VALUE		Packet-Type	Access-Request			1
+VALUE		Packet-Type	Access-Accept			2
+VALUE		Packet-Type	Access-Reject			3
+VALUE		Packet-Type	Accounting-Request		4
+VALUE		Packet-Type	Accounting-Response		5
+VALUE		Packet-Type	Accounting-Status		6
+VALUE		Packet-Type	Password-Request		7
+VALUE		Packet-Type	Password-Accept			8
+VALUE		Packet-Type	Password-Reject			9
+VALUE		Packet-Type	Accounting-Message		10
+VALUE		Packet-Type	Access-Challenge		11
+VALUE		Packet-Type	Status-Server			12
+VALUE		Packet-Type	Status-Client			13
+
+#       Tunnel Type
+VALUE   Tunnel-Type                     PPTP                    1
+VALUE   Tunnel-Type                     L2F                     2
+VALUE   Tunnel-Type                     L2TP                    3
+VALUE   Tunnel-Type                     ATMP                    4
+VALUE   Tunnel-Type                     VTP                     5
+VALUE   Tunnel-Type                     AH                      6
+VALUE   Tunnel-Type                     IP                      7
+VALUE   Tunnel-Type                     MIN-IP                  8
+VALUE   Tunnel-Type                     ESP                     9
+VALUE   Tunnel-Type                     GRE                     10
+VALUE   Tunnel-Type                     DVS                     11
+VALUE   Tunnel-Type                     IP-in-IP                12
+
+#       Tunnel Medium Type
+VALUE   Tunnel-Medium-Type              IP                      1
+VALUE   Tunnel-Medium-Type              IPv4                    1
+VALUE   Tunnel-Medium-Type              IPv6                    2
+VALUE   Tunnel-Medium-Type              NSAP                    3
+VALUE   Tunnel-Medium-Type              HDLC                    4
+VALUE   Tunnel-Medium-Type              BBN-1822                5
+VALUE   Tunnel-Medium-Type              IEEE-802                6
+VALUE   Tunnel-Medium-Type              E.163                   7
+VALUE   Tunnel-Medium-Type              E.164                   8
+VALUE   Tunnel-Medium-Type              F.69                    9
+VALUE   Tunnel-Medium-Type              X.121                   10
+VALUE   Tunnel-Medium-Type              IPX                     11
+VALUE   Tunnel-Medium-Type              Appletalk               12
+VALUE   Tunnel-Medium-Type              DecNet-IV               13
+VALUE   Tunnel-Medium-Type              Banyan-Vines            14
+VALUE   Tunnel-Medium-Type              E.164-NSAP              15
diff --git a/source/lib/python3/cmk/special_agents/dictionary.freeradius b/source/lib/python3/cmk/special_agents/dictionary.freeradius
new file mode 100755
index 0000000..0c083ce
--- /dev/null
+++ b/source/lib/python3/cmk/special_agents/dictionary.freeradius
@@ -0,0 +1,155 @@
+# -*- text -*-
+# Copyright (C) 2019 The FreeRADIUS Server project and contributors
+# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
+#
+#       The FreeRADIUS Vendor-Specific dictionary.
+#
+# Version:      $Id: 14fed7af4e18885cf59c7f5211feb58e66c04b77 $
+#
+
+VENDOR          FreeRADIUS                      11344
+
+BEGIN-VENDOR    FreeRADIUS
+
+ATTRIBUTE       FreeRADIUS-Proxied-To                   1       ipaddr
+ATTRIBUTE       FreeRADIUS-Acct-Session-Start-Time      2       date
+
+#
+#  This attribute is really a bitmask.
+#
+ATTRIBUTE       FreeRADIUS-Statistics-Type              127     integer
+
+VALUE   FreeRADIUS-Statistics-Type      None                    0
+VALUE   FreeRADIUS-Statistics-Type      Authentication          1
+VALUE   FreeRADIUS-Statistics-Type      Accounting              2
+VALUE   FreeRADIUS-Statistics-Type      Proxy-Authentication    4
+VALUE   FreeRADIUS-Statistics-Type      Proxy-Accounting        8
+VALUE   FreeRADIUS-Statistics-Type      Internal                0x10
+VALUE   FreeRADIUS-Statistics-Type      Client                  0x20
+VALUE   FreeRADIUS-Statistics-Type      Server                  0x40
+VALUE   FreeRADIUS-Statistics-Type      Home-Server             0x80
+
+VALUE   FreeRADIUS-Statistics-Type      Auth-Acct               0x03
+VALUE   FreeRADIUS-Statistics-Type      Proxy-Auth-Acct         0x0c
+
+VALUE   FreeRADIUS-Statistics-Type      All                     0x1f
+
+#
+#  Global authentication statistics for packets received by the server.
+#
+ATTRIBUTE       FreeRADIUS-Total-Access-Requests        128     integer
+ATTRIBUTE       FreeRADIUS-Total-Access-Accepts         129     integer
+ATTRIBUTE       FreeRADIUS-Total-Access-Rejects         130     integer
+ATTRIBUTE       FreeRADIUS-Total-Access-Challenges      131     integer
+ATTRIBUTE       FreeRADIUS-Total-Auth-Responses         132     integer
+ATTRIBUTE       FreeRADIUS-Total-Auth-Duplicate-Requests 133    integer
+ATTRIBUTE       FreeRADIUS-Total-Auth-Malformed-Requests 134    integer
+ATTRIBUTE       FreeRADIUS-Total-Auth-Invalid-Requests  135     integer
+ATTRIBUTE       FreeRADIUS-Total-Auth-Dropped-Requests  136     integer
+ATTRIBUTE       FreeRADIUS-Total-Auth-Unknown-Types     137     integer
+
+#
+#  Global statistics for auth packets sent by the server to all home servers
+#
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Access-Requests  138     integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Access-Accepts   139     integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Access-Rejects   140     integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Access-Challenges 141    integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Auth-Responses   142     integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Auth-Duplicate-Requests 143      integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Auth-Malformed-Requests 144      integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Auth-Invalid-Requests 145        integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Auth-Dropped-Requests 146        integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Auth-Unknown-Types 147   integer
+
+#
+#  Global accounting statistics for packets received by the server.
+#
+ATTRIBUTE       FreeRADIUS-Total-Accounting-Requests    148     integer
+ATTRIBUTE       FreeRADIUS-Total-Accounting-Responses   149     integer
+ATTRIBUTE       FreeRADIUS-Total-Acct-Duplicate-Requests 150    integer
+ATTRIBUTE       FreeRADIUS-Total-Acct-Malformed-Requests 151    integer
+ATTRIBUTE       FreeRADIUS-Total-Acct-Invalid-Requests  152     integer
+ATTRIBUTE       FreeRADIUS-Total-Acct-Dropped-Requests  153     integer
+ATTRIBUTE       FreeRADIUS-Total-Acct-Unknown-Types     154     integer
+
+#
+#  Global statistics for acct packets sent by the server to all home servers
+#
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Accounting-Requests 155  integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Accounting-Responses 156 integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Acct-Duplicate-Requests 157      integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Acct-Malformed-Requests 158      integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Acct-Invalid-Requests 159        integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Acct-Dropped-Requests 160        integer
+ATTRIBUTE       FreeRADIUS-Total-Proxy-Acct-Unknown-Types 161   integer
+
+#
+#  Internal queues.  Different packet types are put into different queues.
+#
+ATTRIBUTE       FreeRADIUS-Queue-Len-Internal           162     integer
+ATTRIBUTE       FreeRADIUS-Queue-Len-Proxy              163     integer
+ATTRIBUTE       FreeRADIUS-Queue-Len-Auth               164     integer
+ATTRIBUTE       FreeRADIUS-Queue-Len-Acct               165     integer
+ATTRIBUTE       FreeRADIUS-Queue-Len-Detail             166     integer
+
+ATTRIBUTE       FreeRADIUS-Stats-Client-IP-Address      167     ipaddr
+ATTRIBUTE       FreeRADIUS-Stats-Client-Number          168     integer
+ATTRIBUTE       FreeRADIUS-Stats-Client-Netmask         169     integer
+
+ATTRIBUTE       FreeRADIUS-Stats-Server-IP-Address      170     ipaddr
+ATTRIBUTE       FreeRADIUS-Stats-Server-Port            171     integer
+
+ATTRIBUTE       FreeRADIUS-Stats-Server-Outstanding-Requests 172        integer
+ATTRIBUTE       FreeRADIUS-Stats-Server-State           173     integer
+
+VALUE   FreeRADIUS-Stats-Server-State   Alive                   0
+VALUE   FreeRADIUS-Stats-Server-State   Zombie                  1
+VALUE   FreeRADIUS-Stats-Server-State   Dead                    2
+VALUE   FreeRADIUS-Stats-Server-State   Idle                    3
+
+#
+#  When a home server is marked "dead" or "alive"
+#
+ATTRIBUTE       FreeRADIUS-Stats-Server-Time-Of-Death   174     date
+ATTRIBUTE       FreeRADIUS-Stats-Server-Time-Of-Life    175     date
+
+#
+#  When this server was started.  If start == hup, it hasn't been
+#  hup'd yet.  This is friendlier than having hup == 0 on start.
+#
+ATTRIBUTE       FreeRADIUS-Stats-Start-Time             176     date
+ATTRIBUTE       FreeRADIUS-Stats-HUP-Time               177     date
+# ATTRIBUTE       FreeRADIUS-Stats-Start-Time             176     integer
+# ATTRIBUTE       FreeRADIUS-Stats-HUP-Time               177     integer
+
+#
+#  Exponential moving average of home server response time
+#  Window-1 is the average is calculated over "window" packets.
+#  Window-10 is the average is calculated over "10 * window" packets.
+#
+#  Both Window-1 and Window-10 are times in microseconds
+#  (1/1000000 of a second).
+#
+ATTRIBUTE       FreeRADIUS-Server-EMA-Window            178     integer
+ATTRIBUTE       FreeRADIUS-Server-EMA-USEC-Window-1     179     integer
+ATTRIBUTE       FreeRADIUS-Server-EMA-USEC-Window-10    180     integer
+
+ATTRIBUTE       FreeRADIUS-Queue-PPS-In                 181     integer
+ATTRIBUTE       FreeRADIUS-Queue-PPS-Out                182     integer
+ATTRIBUTE       FreeRADIUS-Queue-Use-Percentage         183     integer
+
+ATTRIBUTE       FreeRADIUS-Stats-Last-Packet-Recv       184     date
+ATTRIBUTE       FreeRADIUS-Stats-Last-Packet-Sent       185     date
+
+ATTRIBUTE       FreeRADIUS-Stats-Error                  187     string
+
+ATTRIBUTE       FreeRADIUS-Stats-Client-IPv6-Address    188     ipv6addr
+ATTRIBUTE       FreeRADIUS-Stats-Server-IPv6-Address    189     ipv6addr
+
+# 190 is reserved
+
+ATTRIBUTE       FreeRADIUS-Total-Auth-Conflicts         191     integer
+ATTRIBUTE       FreeRADIUS-Total-Acct-Conflicts         192     integer
+
+END-VENDOR FreeRADIUS
diff --git a/source/packages/freeradius b/source/packages/freeradius
new file mode 100644
index 0000000..beaf0bd
--- /dev/null
+++ b/source/packages/freeradius
@@ -0,0 +1,23 @@
+{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)',
+ 'description': 'FreeRADIUS special agent\n'
+                '\n'
+                'You need to enable the status server on the FreeRADIUS server '
+                'and make it\n'
+                'accessible from the CMK site.\n'
+                '\n'
+                'NOTE: this plugin needs _pyrad_ to work.\n',
+ 'download_url': 'https://thl-cmk.hopto.org',
+ 'files': {'agent_based': ['freeradius.py'],
+           'agents': ['special/agent_freeradius'],
+           'checks': ['agent_freeradius'],
+           'gui': ['metrics/freeradius.py'],
+           'lib': ['python3/cmk/special_agents/agent_freeradius.py',
+                   'python3/cmk/special_agents/dictionary',
+                   'python3/cmk/special_agents/dictionary.freeradius'],
+           'web': ['plugins/wato/agent_freeradiusi.py']},
+ 'name': 'freeradius',
+ 'title': 'FreeRADIUS special agent',
+ 'version': '0.1.1-20240430',
+ 'version.min_required': '2.2.0b1',
+ 'version.packaged': '2.2.0p24',
+ 'version.usable_until': '2.3.0b1'}
diff --git a/source/web/plugins/wato/agent_freeradiusi.py b/source/web/plugins/wato/agent_freeradiusi.py
new file mode 100644
index 0000000..0b14451
--- /dev/null
+++ b/source/web/plugins/wato/agent_freeradiusi.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# License: GNU General Public License v2
+#
+#
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 2024-04-29
+# File  : wato/agent_freeradius.py
+#
+
+
+from cmk.gui.i18n import _
+from cmk.gui.plugins.wato.special_agents.common import RulespecGroupDatasourceProgramsApps
+from cmk.gui.plugins.wato.utils import HostRulespec, IndividualOrStoredPassword, rulespec_registry
+from cmk.gui.valuespec import (
+    Dictionary,
+    Integer,
+    # TextInput,
+    Transform,
+    ValueSpec,
+)
+
+
+def _valuespec_special_agent_freeradius() -> ValueSpec:
+    return Transform(
+        Dictionary(
+            title=_('FreeRADIUS'),
+            help=_(''),
+            elements=[
+                # ('server',
+                #  TextInput(
+                #      title=_('Server IP-address or name'),
+                #      help=_(
+                #          'Hostname or IP-address to monitor. Default is the host name/IP-Address of the monitored host.'
+                #      ),
+                #      size=50,
+                #      allow_empty=False,
+                #      placeholder='i.e 192.168.10.10 or srvrad01.company.intern'
+                #  )),
+                ('auth_port',
+                 Integer(
+                     title=_('Authentication port'),
+                     help=_('The RADIUS port to use for authentication. Default is 18121.'),
+                     size=5,
+                     default_value=18121,
+                     minvalue=1,
+                     maxvalue=65535,
+                 )),
+                ('secret',
+                 IndividualOrStoredPassword(
+                     title=_('Shared secret'),
+                     help=_('The shared secret.'),
+                     allow_empty=False,
+                 )),
+                ('timeout',
+                 Integer(
+                     title=_('Request timeout'),
+                     help=_('The timeout for the RADIUS request.'),
+                     default_value=2,
+                     minvalue=1,
+                     maxvalue=30,
+                     unit='s',
+                 )),
+                # ('user_name',
+                #  TextInput(
+                #      title=_('Username'),
+                #      help=_('The username to use in the request.'),
+                #      size=50,
+                #      placeholder='username to use in the request',
+                #      allow_empty=False,
+                #  )),
+                # ('user_password',
+                #  IndividualOrStoredPassword(
+                #      title=_('User password'),
+                #      help=_('The user password.'),
+                #      allow_empty=False
+                #  )),
+            ],
+            required_keys=[
+                'secret',
+            ]
+        ),
+    )
+
+
+rulespec_registry.register(
+    HostRulespec(
+        group=RulespecGroupDatasourceProgramsApps,
+        name="special_agents:freeradius",
+        valuespec=_valuespec_special_agent_freeradius,
+    )
+)
-- 
GitLab