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/ipset.py
''' ipset support. This module is tested with hash:ip, hash:net, list:set and several other ipset structures (like hash:net,iface). There is no guarantee that this module is working with all available ipset modules. It supports almost all kernel commands (create, destroy, flush, rename, swap, test...) ''' import errno import socket from pyroute2.common import basestring from pyroute2.netlink import ( NETLINK_NETFILTER, NLM_F_ACK, NLM_F_DUMP, NLM_F_EXCL, NLM_F_REQUEST, NLMSG_ERROR, ) from pyroute2.netlink.exceptions import IPSetError, NetlinkError from pyroute2.netlink.nfnetlink import NFNL_SUBSYS_IPSET from pyroute2.netlink.nfnetlink.ipset import ( IPSET_CMD_ADD, IPSET_CMD_CREATE, IPSET_CMD_DEL, IPSET_CMD_DESTROY, IPSET_CMD_FLUSH, IPSET_CMD_GET_BYINDEX, IPSET_CMD_GET_BYNAME, IPSET_CMD_HEADER, IPSET_CMD_LIST, IPSET_CMD_PROTOCOL, IPSET_CMD_RENAME, IPSET_CMD_SWAP, IPSET_CMD_TEST, IPSET_CMD_TYPE, IPSET_ERR_BUSY, IPSET_ERR_COMMENT, IPSET_ERR_COUNTER, IPSET_ERR_EXIST, IPSET_ERR_EXIST_SETNAME2, IPSET_ERR_FIND_TYPE, IPSET_ERR_INVALID_CIDR, IPSET_ERR_INVALID_FAMILY, IPSET_ERR_INVALID_MARKMASK, IPSET_ERR_INVALID_NETMASK, IPSET_ERR_IPADDR_IPV4, IPSET_ERR_IPADDR_IPV6, IPSET_ERR_MAX_SETS, IPSET_ERR_PROTOCOL, IPSET_ERR_REFERENCED, IPSET_ERR_SKBINFO, IPSET_ERR_TIMEOUT, IPSET_ERR_TYPE_MISMATCH, IPSET_FLAG_IFACE_WILDCARD, IPSET_FLAG_PHYSDEV, IPSET_FLAG_WITH_COMMENT, IPSET_FLAG_WITH_COUNTERS, IPSET_FLAG_WITH_FORCEADD, IPSET_FLAG_WITH_SKBINFO, ipset_msg, ) from pyroute2.netlink.nlsocket import NetlinkSocket def _nlmsg_error(msg): return msg['header']['type'] == NLMSG_ERROR class PortRange(object): """A simple container for port range with optional protocol Note that optional protocol parameter is not supported by all kernel ipset modules using ports. On the other hand, it's sometimes mandatory to set it (like for hash:net,port ipsets) Example:: udp_proto = socket.getprotobyname("udp") port_range = PortRange(1000, 2000, protocol=udp_proto) ipset.create("foo", stype="hash:net,port") ipset.add("foo", ("192.0.2.0/24", port_range), etype="net,port") ipset.test("foo", ("192.0.2.0/24", port_range), etype="net,port") """ def __init__(self, begin, end, protocol=None): self.begin = begin self.end = end self.protocol = protocol class PortEntry(object): """A simple container for port entry with optional protocol""" def __init__(self, port, protocol=None): self.port = port self.protocol = protocol class IPSet(NetlinkSocket): ''' NFNetlink socket (family=NETLINK_NETFILTER). Implements API to the ipset functionality. ''' policy = { IPSET_CMD_PROTOCOL: ipset_msg, IPSET_CMD_LIST: ipset_msg, IPSET_CMD_TYPE: ipset_msg, IPSET_CMD_HEADER: ipset_msg, IPSET_CMD_GET_BYNAME: ipset_msg, IPSET_CMD_GET_BYINDEX: ipset_msg, } attr_map = { 'iface': 'IPSET_ATTR_IFACE', 'mark': 'IPSET_ATTR_MARK', 'set': 'IPSET_ATTR_NAME', 'mac': 'IPSET_ATTR_ETHER', 'port': 'IPSET_ATTR_PORT', ('ip_from', 1): 'IPSET_ATTR_IP_FROM', ('ip_from', 2): 'IPSET_ATTR_IP2', ('cidr', 1): 'IPSET_ATTR_CIDR', ('cidr', 2): 'IPSET_ATTR_CIDR2', ('ip_to', 1): 'IPSET_ATTR_IP_TO', ('ip_to', 2): 'IPSET_ATTR_IP2_TO', } def __init__(self, version=None, attr_revision=None, nfgen_family=2): super(IPSet, self).__init__(family=NETLINK_NETFILTER) policy = dict( [ (x | (NFNL_SUBSYS_IPSET << 8), y) for (x, y) in self.policy.items() ] ) self.register_policy(policy) self._nfgen_family = nfgen_family if version is None: msg = self.get_proto_version() version = msg[0].get_attr('IPSET_ATTR_PROTOCOL') self._proto_version = version self._attr_revision = attr_revision def request( self, msg, msg_type, msg_flags=NLM_F_REQUEST | NLM_F_DUMP, terminate=None, ): msg['nfgen_family'] = self._nfgen_family try: return tuple( self.nlm_request( msg, msg_type | (NFNL_SUBSYS_IPSET << 8), msg_flags, terminate=terminate, ) ) except NetlinkError as err: raise _IPSetError(err.code, cmd=msg_type) def headers(self, name, **kwargs): ''' Get headers of the named ipset. It can be used to test if one ipset exists, since it returns a no such file or directory. ''' return self._list_or_headers(IPSET_CMD_HEADER, name=name, **kwargs) def get_proto_version(self, version=6): ''' Get supported protocol version by kernel. version parameter allow to set mandatory (but unused?) IPSET_ATTR_PROTOCOL netlink attribute in the request. ''' msg = ipset_msg() msg['attrs'] = [['IPSET_ATTR_PROTOCOL', version]] return self.request(msg, IPSET_CMD_PROTOCOL) def list(self, *argv, **kwargs): ''' List installed ipsets. If `name` is provided, list the named ipset or return an empty list. Be warned: netlink does not return an error if given name does not exit, you will receive an empty list. ''' if argv: kwargs['name'] = argv[0] return self._list_or_headers(IPSET_CMD_LIST, **kwargs) def _list_or_headers(self, cmd, name=None, flags=None): msg = ipset_msg() msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version]] if name is not None: msg['attrs'].append(['IPSET_ATTR_SETNAME', name]) if flags is not None: msg['attrs'].append(['IPSET_ATTR_FLAGS', flags]) return self.request(msg, cmd) def destroy(self, name=None): ''' Destroy one (when name is set) or all ipset (when name is None) ''' msg = ipset_msg() msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version]] if name is not None: msg['attrs'].append(['IPSET_ATTR_SETNAME', name]) return self.request( msg, IPSET_CMD_DESTROY, msg_flags=NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL, terminate=_nlmsg_error, ) def create( self, name, stype='hash:ip', family=socket.AF_INET, exclusive=True, counters=False, comment=False, maxelem=None, forceadd=False, hashsize=None, timeout=None, bitmap_ports_range=None, size=None, skbinfo=False, ): ''' Create an ipset `name` of type `stype`, by default `hash:ip`. Common ipset options are supported: * exclusive -- if set, raise an error if the ipset exists * counters -- enable data/packets counters * comment -- enable comments capability * maxelem -- max size of the ipset * forceadd -- you should refer to the ipset manpage * hashsize -- size of the hashtable (if any) * timeout -- enable and set a default value for entries (if not None) * bitmap_ports_range -- set the specified inclusive portrange for the bitmap ipset structure (0, 65536) * size -- Size of the list:set, the default is 8 * skbinfo -- enable skbinfo capability ''' excl_flag = NLM_F_EXCL if exclusive else 0 msg = ipset_msg() cadt_flags = 0 if counters: cadt_flags |= IPSET_FLAG_WITH_COUNTERS if comment: cadt_flags |= IPSET_FLAG_WITH_COMMENT if forceadd: cadt_flags |= IPSET_FLAG_WITH_FORCEADD if skbinfo: cadt_flags |= IPSET_FLAG_WITH_SKBINFO if stype == 'bitmap:port' and bitmap_ports_range is None: raise ValueError('Missing value bitmap_ports_range') data = {'attrs': []} if cadt_flags: data['attrs'] += [['IPSET_ATTR_CADT_FLAGS', cadt_flags]] if maxelem is not None: data['attrs'] += [['IPSET_ATTR_MAXELEM', maxelem]] if hashsize is not None: data['attrs'] += [["IPSET_ATTR_HASHSIZE", hashsize]] elif size is not None and stype == 'list:set': data['attrs'] += [['IPSET_ATTR_SIZE', size]] if timeout is not None: data['attrs'] += [["IPSET_ATTR_TIMEOUT", timeout]] if bitmap_ports_range is not None and stype == 'bitmap:port': # Set the bitmap range A bitmap type of set # can store up to 65536 entries if isinstance(bitmap_ports_range, PortRange): data['attrs'] += [ ['IPSET_ATTR_PORT_FROM', bitmap_ports_range.begin] ] data['attrs'] += [ ['IPSET_ATTR_PORT_TO', bitmap_ports_range.end] ] else: data['attrs'] += [ ['IPSET_ATTR_PORT_FROM', bitmap_ports_range[0]] ] data['attrs'] += [ ['IPSET_ATTR_PORT_TO', bitmap_ports_range[1]] ] if self._attr_revision is None: # Get the last revision supported by kernel revision = self.get_supported_revisions(stype)[1] else: revision = self._attr_revision msg['attrs'] = [ ['IPSET_ATTR_PROTOCOL', self._proto_version], ['IPSET_ATTR_SETNAME', name], ['IPSET_ATTR_TYPENAME', stype], ['IPSET_ATTR_FAMILY', family], ['IPSET_ATTR_REVISION', revision], ["IPSET_ATTR_DATA", data], ] return self.request( msg, IPSET_CMD_CREATE, msg_flags=NLM_F_REQUEST | NLM_F_ACK | excl_flag, terminate=_nlmsg_error, ) @staticmethod def _family_to_version(family): if family is not None: if family == socket.AF_INET: return 'IPSET_ATTR_IPADDR_IPV4' elif family == socket.AF_INET6: return 'IPSET_ATTR_IPADDR_IPV6' elif family == socket.AF_UNSPEC: return None raise TypeError('unknown family') def _entry_to_data_attrs(self, entry, etype, ip_version): attrs = [] ip_count = 0 if etype == 'set': attrs += [['IPSET_ATTR_NAME', entry]] return attrs # We support string (for one element, and for users calling this # function like a command line), and tuple/list if isinstance(entry, basestring): entry = entry.split(',') if isinstance(entry, (int, PortRange, PortEntry)): entry = [entry] for e, t in zip(entry, etype.split(',')): if t in ('ip', 'net'): ip_count += 1 if t == 'net': if '/' in e: e, cidr = e.split('/') attrs += [ [self.attr_map[('cidr', ip_count)], int(cidr)] ] elif '-' in e: e, to = e.split('-') attrs += [ [ self.attr_map[('ip_to', ip_count)], {'attrs': [[ip_version, to]]}, ] ] attrs += [ [ self.attr_map[('ip_from', ip_count)], {'attrs': [[ip_version, e]]}, ] ] elif t == "port": if isinstance(e, PortRange): attrs += [['IPSET_ATTR_PORT_FROM', e.begin]] attrs += [['IPSET_ATTR_PORT_TO', e.end]] if e.protocol is not None: attrs += [['IPSET_ATTR_PROTO', e.protocol]] elif isinstance(e, PortEntry): attrs += [['IPSET_ATTR_PORT', e.port]] if e.protocol is not None: attrs += [['IPSET_ATTR_PROTO', e.protocol]] else: attrs += [[self.attr_map[t], e]] else: attrs += [[self.attr_map[t], e]] return attrs def _add_delete_test( self, name, entry, family, cmd, exclusive, comment=None, timeout=None, etype="ip", packets=None, bytes=None, skbmark=None, skbprio=None, skbqueue=None, wildcard=False, physdev=False, ): excl_flag = NLM_F_EXCL if exclusive else 0 adt_flags = 0 if wildcard: adt_flags |= IPSET_FLAG_IFACE_WILDCARD if physdev: adt_flags |= IPSET_FLAG_PHYSDEV ip_version = self._family_to_version(family) data_attrs = self._entry_to_data_attrs(entry, etype, ip_version) if comment is not None: data_attrs += [ ["IPSET_ATTR_COMMENT", comment], ["IPSET_ATTR_CADT_LINENO", 0], ] if timeout is not None: data_attrs += [["IPSET_ATTR_TIMEOUT", timeout]] if bytes is not None: data_attrs += [["IPSET_ATTR_BYTES", bytes]] if packets is not None: data_attrs += [["IPSET_ATTR_PACKETS", packets]] if skbmark is not None: data_attrs += [["IPSET_ATTR_SKBMARK", skbmark]] if skbprio is not None: data_attrs += [["IPSET_ATTR_SKBPRIO", skbprio]] if skbqueue is not None: data_attrs += [["IPSET_ATTR_SKBQUEUE", skbqueue]] if adt_flags: data_attrs += [["IPSET_ATTR_CADT_FLAGS", adt_flags]] msg = ipset_msg() msg['attrs'] = [ ['IPSET_ATTR_PROTOCOL', self._proto_version], ['IPSET_ATTR_SETNAME', name], ['IPSET_ATTR_DATA', {'attrs': data_attrs}], ] return self.request( msg, cmd, msg_flags=NLM_F_REQUEST | NLM_F_ACK | excl_flag, terminate=_nlmsg_error, ) def add( self, name, entry, family=socket.AF_INET, exclusive=True, comment=None, timeout=None, etype="ip", skbmark=None, skbprio=None, skbqueue=None, wildcard=False, **kwargs ): ''' Add a member to the ipset. etype is the entry type that you add to the ipset. It's related to the ipset type. For example, use "ip" for one hash:ip or bitmap:ip ipset. When your ipset store a tuple, like "hash:net,iface", you must use a comma a separator (etype="net,iface") entry is a string for "ip" and "net" objects. For ipset with several dimensions, you must use a tuple (or a list) of objects. "port" type is specific, since you can use integer of specialized containers like :class:`PortEntry` and :class:`PortRange` Examples:: ipset = IPSet() ipset.create("foo", stype="hash:ip") ipset.add("foo", "198.51.100.1", etype="ip") ipset = IPSet() ipset.create("bar", stype="bitmap:port", bitmap_ports_range=(1000, 2000)) ipset.add("bar", 1001, etype="port") ipset.add("bar", PortRange(1500, 2000), etype="port") ipset = IPSet() import socket protocol = socket.getprotobyname("tcp") ipset.create("foobar", stype="hash:net,port") port_entry = PortEntry(80, protocol=protocol) ipset.add("foobar", ("198.51.100.0/24", port_entry), etype="net,port") wildcard option enable kernel wildcard matching on interface name for net,iface entries. ''' return self._add_delete_test( name, entry, family, IPSET_CMD_ADD, exclusive, comment=comment, timeout=timeout, etype=etype, skbmark=skbmark, skbprio=skbprio, skbqueue=skbqueue, wildcard=wildcard, **kwargs ) def delete( self, name, entry, family=socket.AF_INET, exclusive=True, etype="ip" ): ''' Delete a member from the ipset. See :func:`add` method for more information on etype. ''' return self._add_delete_test( name, entry, family, IPSET_CMD_DEL, exclusive, etype=etype ) def test(self, name, entry, family=socket.AF_INET, etype="ip"): ''' Test if entry is part of an ipset See :func:`add` method for more information on etype. ''' try: self._add_delete_test( name, entry, family, IPSET_CMD_TEST, False, etype=etype ) return True except IPSetError as e: if e.code == IPSET_ERR_EXIST: return False raise e def swap(self, set_a, set_b): ''' Swap two ipsets. They must have compatible content type. ''' msg = ipset_msg() msg['attrs'] = [ ['IPSET_ATTR_PROTOCOL', self._proto_version], ['IPSET_ATTR_SETNAME', set_a], ['IPSET_ATTR_TYPENAME', set_b], ] return self.request( msg, IPSET_CMD_SWAP, msg_flags=NLM_F_REQUEST | NLM_F_ACK, terminate=_nlmsg_error, ) def flush(self, name=None): ''' Flush all ipsets. When name is set, flush only this ipset. ''' msg = ipset_msg() msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version]] if name is not None: msg['attrs'].append(['IPSET_ATTR_SETNAME', name]) return self.request( msg, IPSET_CMD_FLUSH, msg_flags=NLM_F_REQUEST | NLM_F_ACK, terminate=_nlmsg_error, ) def rename(self, name_src, name_dst): ''' Rename the ipset. ''' msg = ipset_msg() msg['attrs'] = [ ['IPSET_ATTR_PROTOCOL', self._proto_version], ['IPSET_ATTR_SETNAME', name_src], ['IPSET_ATTR_TYPENAME', name_dst], ] return self.request( msg, IPSET_CMD_RENAME, msg_flags=NLM_F_REQUEST | NLM_F_ACK, terminate=_nlmsg_error, ) def _get_set_by(self, cmd, value): # Check that IPSet version is supported if self._proto_version < 7: raise NotImplementedError() msg = ipset_msg() if cmd == IPSET_CMD_GET_BYNAME: msg['attrs'] = [ ['IPSET_ATTR_PROTOCOL', self._proto_version], ['IPSET_ATTR_SETNAME', value], ] if cmd == IPSET_CMD_GET_BYINDEX: msg['attrs'] = [ ['IPSET_ATTR_PROTOCOL', self._proto_version], ['IPSET_ATTR_INDEX', value], ] return self.request(msg, cmd) def get_set_byname(self, name): ''' Get a set by its name ''' return self._get_set_by(IPSET_CMD_GET_BYNAME, name) def get_set_byindex(self, index): ''' Get a set by its index ''' return self._get_set_by(IPSET_CMD_GET_BYINDEX, index) def get_supported_revisions(self, stype, family=socket.AF_INET): ''' Return minimum and maximum of revisions supported by the kernel. Each ipset module (like hash:net, hash:ip, etc) has several revisions. Newer revisions often have more features or more performances. Thanks to this call, you can ask the kernel the list of supported revisions. You can manually set/force revisions used in IPSet constructor. Example:: ipset = IPSet() ipset.get_supported_revisions("hash:net") ipset.get_supported_revisions("hash:net,port,net") ''' msg = ipset_msg() msg['attrs'] = [ ['IPSET_ATTR_PROTOCOL', self._proto_version], ['IPSET_ATTR_TYPENAME', stype], ['IPSET_ATTR_FAMILY', family], ] response = self.request( msg, IPSET_CMD_TYPE, msg_flags=NLM_F_REQUEST | NLM_F_ACK, terminate=_nlmsg_error, ) min_revision = response[0].get_attr("IPSET_ATTR_PROTOCOL_MIN") max_revision = response[0].get_attr("IPSET_ATTR_REVISION") return min_revision, max_revision class _IPSetError(IPSetError): ''' Proxy class to not import all specifics ipset code in exceptions.py Out of the ipset module, a caller should use parent class instead ''' def __init__(self, code, msg=None, cmd=None): if code in self.base_map: msg = self.base_map[code] elif cmd in self.cmd_map: error_map = self.cmd_map[cmd] if code in error_map: msg = error_map[code] super(_IPSetError, self).__init__(code, msg) base_map = { IPSET_ERR_PROTOCOL: "Kernel error received:" " ipset protocol error", IPSET_ERR_INVALID_CIDR: "The value of the CIDR parameter of" " the IP address is invalid", IPSET_ERR_TIMEOUT: "Timeout cannot be used: set was created" " without timeout support", IPSET_ERR_IPADDR_IPV4: "An IPv4 address is expected, but" " not received", IPSET_ERR_IPADDR_IPV6: "An IPv6 address is expected, but" " not received", IPSET_ERR_COUNTER: "Packet/byte counters cannot be used:" " set was created without counter support", IPSET_ERR_COMMENT: "Comment string is too long!", IPSET_ERR_SKBINFO: "Skbinfo mapping cannot be used: " " set was created without skbinfo support", } c_map = { errno.EEXIST: "Set cannot be created: set with the same" " name already exists", IPSET_ERR_FIND_TYPE: "Kernel error received: " "set type not supported", IPSET_ERR_MAX_SETS: "Kernel error received: maximal number of" " sets reached, cannot create more.", IPSET_ERR_INVALID_NETMASK: "The value of the netmask parameter" " is invalid", IPSET_ERR_INVALID_MARKMASK: "The value of the markmask parameter" " is invalid", IPSET_ERR_INVALID_FAMILY: "Protocol family not supported by the" " set type", } destroy_map = { IPSET_ERR_BUSY: "Set cannot be destroyed: it is in use" " by a kernel component" } r_map = { IPSET_ERR_EXIST_SETNAME2: "Set cannot be renamed: a set with the" " new name already exists", IPSET_ERR_REFERENCED: "Set cannot be renamed: it is in use by" " another system", } s_map = { IPSET_ERR_EXIST_SETNAME2: "Sets cannot be swapped: the second set" " does not exist", IPSET_ERR_TYPE_MISMATCH: "The sets cannot be swapped: their type" " does not match", } a_map = { IPSET_ERR_EXIST: "Element cannot be added to the set: it's" " already added" } del_map = { IPSET_ERR_EXIST: "Element cannot be deleted from the set:" " it's not added" } cmd_map = { IPSET_CMD_CREATE: c_map, IPSET_CMD_DESTROY: destroy_map, IPSET_CMD_RENAME: r_map, IPSET_CMD_SWAP: s_map, IPSET_CMD_ADD: a_map, IPSET_CMD_DEL: del_map, }