PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/local/nagios/venv/lib/python3.13/site-packages/pyroute2
Viewing File: /usr/local/nagios/venv/lib/python3.13/site-packages/pyroute2/wiset.py
''' High level ipset support. When :doc:`ipset` is providing a direct netlink socket with low level functions, a :class:`WiSet` object is built to map ipset objects from kernel. It helps to add/remove entries, list content, etc. For example, adding an entry with :class:`pyroute2.ipset.IPSet` object implies to set a various number of parameters: .. doctest:: :skipif: True >>> ipset = IPSet() >>> ipset.add("foo", "1.2.3.4/24", etype="net") >>> ipset.close() When they are discovered by a :class:`WiSet`: .. doctest:: :skipif: True >>> wiset = load_ipset("foo") >>> wiset.add("1.2.3.4/24") Listing entries is also easier using :class:`WiSet`, since it parses for you netlink messages: .. doctest:: :skipif: True >>> wiset.content {'1.2.3.0/24': IPStats(packets=None, bytes=None, comment=None, timeout=None, skbmark=None, physdev=False)} ''' import errno import uuid from collections import namedtuple from inspect import getcallargs from socket import AF_INET from pyroute2.common import basestring from pyroute2.ipset import IPSet from pyroute2.netlink.exceptions import IPSetError from pyroute2.netlink.nfnetlink.ipset import ( IPSET_FLAG_IFACE_WILDCARD, IPSET_FLAG_PHYSDEV, IPSET_FLAG_WITH_COMMENT, IPSET_FLAG_WITH_COUNTERS, IPSET_FLAG_WITH_SKBINFO, ) from pyroute2.netlink.nfnetlink.nfctsocket import IP_PROTOCOLS # Debug variable to detect netlink socket leaks COUNT = {"count": 0} def need_ipset_socket(fun): """Decorator to create netlink socket if needed. In many of our helpers, we need to open a netlink socket. This can be expensive for someone using many times the functions: instead to have only one socket and use several requests, we will open it again and again. This helper allow our functions to be flexible: the caller can pass an optional socket, or do nothing. In this last case, this decorator will open a socket for the caller (and close it after call) It also help to mix helpers. One helper can call another one: the socket will be opened only once. We just have to pass the ipset variable. Note that all functions using this helper *must* use ipset as variable name for the socket. """ def wrap(*args, **kwargs): callargs = getcallargs(fun, *args, **kwargs) if callargs["sock"] is None: # This variable is used only to debug leak in tests COUNT['count'] += 1 with IPSet() as sock: callargs["sock"] = sock # We must pop kwargs here, else the function will receive # a dict of dict if "kwargs" in callargs: callargs.update(callargs.pop("kwargs")) return fun(**callargs) # pylint:disable=star-args return fun(*args, **kwargs) return wrap class IPStats( namedtuple( "IPStats", [ "packets", "bytes", "comment", "timeout", "skbmark", "physdev", "wildcard", ], ) ): __slots__ = () def __new__( cls, packets, bytes, comment, timeout, skbmark, physdev=False, wildcard=False, ): return super(IPStats, cls).__new__( cls, packets, bytes, comment, timeout, skbmark, physdev=physdev, wildcard=wildcard, ) # pylint: disable=too-many-instance-attributes class WiSet(object): """Main high level ipset manipulation class. Every high level ipset operation should be possible with this class, you probably don't need other helpers of this module, except tools to load data from kernel (:func:`load_all_ipsets` and :func:`load_ipset`) For example, you can create and an entry in a ipset just with: .. doctest:: :skipif: True >>> with WiSet(name="mysuperipset") as myset: >>> myset.create() # add the ipset in the kernel >>> myset.add("198.51.100.1") # add one IP to the set Netlink sockets are opened by __enter__ and __exit__ function, so you don't have to manage it manually if you use the "with" keyword. If you want to manage it manually (for example for long operation in a daemon), you can do the following: .. doctest:: :skipif: True >>> myset = WiSet(name="mysuperipset") >>> myset.open_netlink() >>> # do stuff >>> myset.close_netlink() You can also don't initiate at all any netlink socket, this code will work: .. doctest:: :skipif: True >>> myset = WiSet(name="mysuperipset") >>> myset.create() >>> myset.destroy() But do it very carefully. In that case, a netlink socket will be opened in background for any operation. No socket will be leaked, but that can consume resources. You can also instantiate WiSet objects with :func:`load_all_ipsets` and :func:`load_ipset`: .. doctest:: :skipif: True >>> all_sets_dict = load_all_ipsets() >>> one_set = load_ipset(name="myset") Have a look on content variable if you need list of entries in the Set. """ # pylint: disable=too-many-arguments def __init__( self, name=None, attr_type='hash:ip', family=AF_INET, sock=None, timeout=None, counters=False, comment=False, hashsize=None, revision=None, skbinfo=False, ): self.name = name self.hashsize = hashsize self._attr_type = None self.entry_type = None self.attr_type = attr_type self.family = family self._content = None self.sock = sock self.timeout = timeout self.counters = counters self.comment = comment self.revision = revision self.index = None self.skbinfo = skbinfo def open_netlink(self): """ Open manually a netlink socket. You can use "with WiSet()" statement instead. """ if self.sock is None: self.sock = IPSet() def close_netlink(self): """Clone any opened netlink socket""" if self.sock is not None: self.sock.close() self.sock = None @property def attr_type(self): return self._attr_type @attr_type.setter def attr_type(self, value): self._attr_type = value self.entry_type = value.split(":", 1)[1] def __enter__(self): self.open_netlink() return self def __exit__(self, exc_type, exc_value, traceback): self.close_netlink() @classmethod def from_netlink(cls, ndmsg, content=False): """Create a ipset objects based on a parsed netlink message :param ndmsg: the netlink message to parse :param content: should we fill (and parse) entries info (can be slow on very large set) :type content: bool """ self = cls() self.attr_type = ndmsg.get_attr("IPSET_ATTR_TYPENAME") self.name = ndmsg.get_attr("IPSET_ATTR_SETNAME") self.hashsize = ndmsg.get_attr("IPSET_ATTR_HASHSIZE") self.family = ndmsg.get_attr("IPSET_ATTR_FAMILY") self.revision = ndmsg.get_attr("IPSET_ATTR_REVISION") self.index = ndmsg.get_attr("IPSET_ATTR_INDEX") data = ndmsg.get_attr("IPSET_ATTR_DATA") self.timeout = data.get_attr("IPSET_ATTR_TIMEOUT") flags = data.get_attr("IPSET_ATTR_CADT_FLAGS") if flags is not None: self.counters = bool(flags & IPSET_FLAG_WITH_COUNTERS) self.comment = bool(flags & IPSET_FLAG_WITH_COMMENT) self.skbinfo = bool(flags & IPSET_FLAG_WITH_SKBINFO) if content: self.update_dict_content(ndmsg) return self def update_dict_content(self, ndmsg): """Update a dictionary statistics with values sent in netlink message :param ndmsg: the netlink message :type ndmsg: netlink message """ family = "IPSET_ATTR_IPADDR_IPV4" ip_attr = "IPSET_ATTR_IP_FROM" if self._content is None: self._content = {} timeout = None entries = ndmsg.get_attr("IPSET_ATTR_ADT").get_attrs("IPSET_ATTR_DATA") for entry in entries: key = "" for parse_type in self.entry_type.split(","): if parse_type == "ip": ip = entry.get_attr(ip_attr).get_attr(family) key += ip elif parse_type == "net": ip = entry.get_attr(ip_attr).get_attr(family) key += ip cidr = entry.get_attr("IPSET_ATTR_CIDR") if cidr is not None: key += "/{0}".format(cidr) elif parse_type == "iface": key += entry.get_attr("IPSET_ATTR_IFACE") elif parse_type == "set": key += entry.get_attr("IPSET_ATTR_NAME") elif parse_type == "mark": key += str(hex(entry.get_attr("IPSET_ATTR_MARK"))) elif parse_type == "port": proto = entry.get_attr('IPSET_ATTR_PROTO') if proto is not None: proto = IP_PROTOCOLS.get(proto, str(proto)).lower() key += '{proto}:'.format(proto=proto) key += str(entry.get_attr("IPSET_ATTR_PORT_FROM")) elif parse_type == "mac": key += entry.get_attr("IPSET_ATTR_ETHER") key += "," key = key.strip(",") if self.timeout is not None: timeout = entry.get_attr("IPSET_ATTR_TIMEOUT") skbmark = entry.get_attr("IPSET_ATTR_SKBMARK") if skbmark is not None: # Convert integer to hex for mark/mask # Only display mask if != 0xffffffff if skbmark[1] != (2**32 - 1): skbmark = "/".join([str(hex(mark)) for mark in skbmark]) else: skbmark = str(hex(skbmark[0])) entry_flag_parsed = {"physdev": False} flags = entry.get_attr("IPSET_ATTR_CADT_FLAGS") if flags is not None: entry_flag_parsed["physdev"] = bool(flags & IPSET_FLAG_PHYSDEV) entry_flag_parsed["wildcard"] = bool( flags & IPSET_FLAG_IFACE_WILDCARD ) value = IPStats( packets=entry.get_attr("IPSET_ATTR_PACKETS"), bytes=entry.get_attr("IPSET_ATTR_BYTES"), comment=entry.get_attr("IPSET_ATTR_COMMENT"), skbmark=skbmark, timeout=timeout, **entry_flag_parsed ) self._content[key] = value def create(self, **kwargs): """Insert this Set in the kernel Many options are set with python object attributes (like comments, counters, etc). For non-supported type, kwargs are provided. See :doc:`ipset` documentation for more information. """ create_ipset( self.name, stype=self.attr_type, family=self.family, sock=self.sock, timeout=self.timeout, comment=self.comment, counters=self.counters, hashsize=self.hashsize, skbinfo=self.skbinfo, **kwargs ) def destroy(self): """Destroy this ipset in the kernel list. It does not delete this python object (any content or other stored values are keep in memory). This function will fail if the ipset is still referenced (by example in iptables rules), you have been warned. """ destroy_ipset(self.name, sock=self.sock) def add(self, entry, **kwargs): """Add an entry in this ipset. If counters are enabled on the set, reset by default the value when we add the element. Without this reset, kernel sometimes store old values and can add very strange behavior on counters. """ if isinstance(entry, dict): kwargs.update(entry) entry = kwargs.pop("entry") if self.counters: kwargs["packets"] = kwargs.pop("packets", 0) kwargs["bytes"] = kwargs.pop("bytes", 0) skbmark = kwargs.get("skbmark") if isinstance(skbmark, basestring): skbmark = skbmark.split('/') mark = int(skbmark[0], 16) try: mask = int(skbmark[1], 16) except IndexError: mask = int("0xffffffff", 16) kwargs["skbmark"] = (mark, mask) add_ipset_entry( self.name, entry, etype=self.entry_type, sock=self.sock, **kwargs ) def delete(self, entry, **kwargs): """Delete/remove an entry in this ipset""" delete_ipset_entry( self.name, entry, etype=self.entry_type, sock=self.sock, **kwargs ) def test(self, entry, **kwargs): """Test if an entry is in this ipset""" return test_ipset_entry( self.name, entry, etype=self.entry_type, sock=self.sock, **kwargs ) def test_list(self, entries, **kwargs): """Test if a list of a set of entries is in this ipset Return a set of entries found in the IPSet """ return test_ipset_entries( self.name, entries, etype=self.entry_type, sock=self.sock, **kwargs ) def update_content(self): """Update the content dictionary with values from kernel""" self._content = {} update_wiset_content(self, sock=self.sock) def flush(self): """Flush entries of the ipset""" flush_ipset(self.name, sock=self.sock) @property def content(self): """Dictionary of entries in the set. Keys are IP addresses (as string), values are IPStats tuples. """ if self._content is None: self.update_content() return self._content def insert_list(self, entries): """Just a small helper to reduce the number of loops in main code.""" for entry in entries: self.add(entry) def replace_entries(self, new_list): """Replace the content of an ipset with a new list of entries. This operation is like a flush() and adding all entries one by one. But this call is atomic: it creates a temporary ipset and swap the content. :param new_list: list of entries to add :type new_list: list or :py:class:`set` of basestring or of keyword arguments dict """ temp_name = str(uuid.uuid4())[0:8] # Get a copy of ourself temp = load_ipset(self.name, sock=self.sock) temp.name = temp_name temp.sock = self.sock temp.create() temp.insert_list(new_list) swap_ipsets(self.name, temp_name, sock=self.sock) temp.destroy() @need_ipset_socket def create_ipset( name, stype=None, family=AF_INET, exclusive=False, sock=None, **kwargs ): """Create an ipset.""" sock.create( name, stype=stype, family=family, exclusive=exclusive, **kwargs ) @need_ipset_socket def load_all_ipsets(content=False, sock=None, inherit_sock=False, prefix=None): """List all ipset as WiSet objects. Get full ipset data from kernel and parse it in WiSet objects. Result is a dictionary with ipset names as keys, and WiSet objects as values. :param content: parse the list of entries and fill it in WiSet content dictionary :type content: bool :param inherit_sock: use the netlink sock passed in ipset arg to fill WiSets sock :type inherit_sock: bool :param prefix: filter out all ipset with a name not beginning by this prefix :type prefix: str or None """ res = {} for myset in sock.list(): # on large sets, we can receive data in several messages name = myset.get_attr("IPSET_ATTR_SETNAME") if prefix is not None and not name.startswith(prefix): continue if name not in res: wiset = WiSet.from_netlink(myset, content=content) if inherit_sock: wiset.sock = sock res[wiset.name] = wiset elif content: res[wiset.name].update_dict_content(myset) return res @need_ipset_socket def load_ipset(name, content=False, sock=None, inherit_sock=False): """Get one ipset as WiSet object Helper to get current WiSet object. More efficient that :func:`load_all_ipsets` since the kernel does the filtering itself. Return None if the ipset does not exist :param name: name of the ipset :type name: str :param content: parse or not content and statistics on entries :type content: bool :param inherit_sock: use the netlink sock passed in ipset arg to fill WiSet sock :type inherit_sock: bool """ res = None try: messages = sock.list(name=name) except IPSetError as e: if e.code == errno.ENOENT: return res raise for msg in messages: if res is None: res = WiSet.from_netlink(msg, content=content) if inherit_sock: res.sock = sock elif content: res.update_dict_content(msg) return res @need_ipset_socket def update_wiset_content(wiset, sock=None): """Update content/statistics of a wiset. You should never call yourself this function. It is only a helper to use the :func:`need_ipset_socket` decorator out of WiSet object. """ for msg in sock.list(name=wiset.name): wiset.update_dict_content(msg) @need_ipset_socket def destroy_ipset(name, sock=None): """Remove an ipset in the kernel.""" sock.destroy(name) @need_ipset_socket def add_ipset_entry(name, entry, sock=None, **kwargs): """Add an entry""" sock.add(name, entry, **kwargs) @need_ipset_socket def delete_ipset_entry(name, entry, sock=None, **kwargs): """Remove one entry""" sock.delete(name, entry, **kwargs) @need_ipset_socket def test_ipset_exist(name, sock=None): """Test if the given ipset exist""" try: sock.headers(name) return True except IPSetError as e: if e.code == errno.ENOENT: return False raise @need_ipset_socket def test_ipset_entry(name, entry, sock=None, **kwargs): """Test if an entry is in one ipset""" return sock.test(name, entry, **kwargs) @need_ipset_socket def test_ipset_entries(name, entries, sock=None, **kwargs): """Test a list (or a set) of entries.""" res = set() for entry in entries: if sock.test(name, entry, **kwargs): res.add(entry) return res @need_ipset_socket def flush_ipset(name, sock=None): """Flush all ipset content""" sock.flush(name) @need_ipset_socket def swap_ipsets(name_a, name_b, sock=None): """Swap the content of ipset a and b. ipsets must have compatible content. """ sock.swap(name_a, name_b) def get_ipset_socket(**kwargs): """Get a socket that one can pass to several WiSet objects""" return IPSet(**kwargs)