ipMore.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044 package require msgcat
00045
00046
00047
00048
00049 if {[catch {package require ipMorec}]} {
00050 catch {package require tcllibc}
00051 }
00052
00053 if {[llength [info commands ::ip::prefixToNativec]]} {
00054
00055 interp alias {} ::ip::prefixToNative {} ::ip::prefixToNativec
00056 interp alias {} ::ip::isOverlapNative {} ::ip::isOverlapNativec
00057 } else {
00058
00059 interp alias {} ::ip::prefixToNative {} ::ip::prefixToNativeTcl
00060 interp alias {} ::ip::isOverlapNative {} ::ip::isOverlapNativeTcl
00061 }
00062
00063 namespace ::ip {
00064 ::msgcat::mc
00065 }
00066
00067 if {![llength [info commands lassign]]} {
00068
00069
00070
00071 ret ::ip::lassign (type values , type args) {
00072 uplevel 1 [list foreach $args $values break]
00073 lrange $values [llength $args] end
00074 }
00075 }
00076 if {![llength [info commands lvarpop]]} {
00077
00078
00079
00080 ret ::ip::lvarpop (type upVar , optional index =0) {
00081 upvar $upVar list;
00082 set top [lindex $list $index];
00083 set list [concat [lrange $list 0 [expr $index - 1]] \
00084 [lrange $list [expr $index +1] end]];
00085 return $top;
00086 }
00087 }
00088
00089
00090
00091
00092
00093 interp alias {} ::ip::ToInteger {} ::ip::toInteger
00094 interp alias {} ::ip::ToHex {} ::ip::toHex
00095 interp alias {} ::ip::MaskToInt {} ::ip::maskToInt
00096 interp alias {} ::ip::MaskToLength {} ::ip::maskToLength
00097 interp alias {} ::ip::LengthToMask {} ::ip::lengthToMask
00098 interp alias {} ::ip::IpToLayer2Multicast {} ::ip::ipToLayer2Multicast
00099 interp alias {} ::ip::IpHostFromPrefix {} ::ip::ipHostFromPrefix
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137 ret ip::prefixToNativeTcl (type prefix) {
00138 set plist {}
00139 foreach p $prefix {
00140 set newPrefix [ip::toHex [ip::prefix $p]]
00141 if {[string equal [set mask [ip::mask $p]] ""]} {
00142 set newMask 0xffffffff
00143 } else {
00144 set newMask [format "0x%08x" [ip::maskToInt $mask]]
00145 }
00146 lappend plist [list $newPrefix $newMask]
00147 }
00148 if {[llength $plist]==1} {return [lindex $plist 0]}
00149 return $plist
00150 }
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196 ret ::ip::nativeToPrefix (type nativeList , type args) {
00197 set pList 1
00198 set ipv4 1
00199 while {[llength $args]} {
00200 switch -- [lindex $args 0] {
00201 -ipv4 {set args [lrange $args 1 end]}
00202 default {
00203 return -code error [msgcat::mc "option %s not supported" [lindex $args 0]]
00204 }
00205 }
00206 }
00207
00208 # if a single native element is passed eg {0x01010100 0xffffff00}
00209 # instead of {{0x01010100 0xffffff00} {0x01010100 0xffffff00}...}
00210 # then return a (non-list) single entry
00211 if {[llength [lindex $nativeList 0]]==1} {set pList 0; set nativeList [list $nativeList]}
00212 foreach native $nativeList {
00213 lassign $native ip mask
00214 if {[string equal $mask ""]} {set mask 32}
00215 set pString ""
00216 append pString [ip::ToString [binary format I [expr {$ip}]]]
00217 append pString "/"
00218 append pString [ip::maskToLength $mask]
00219 lappend rList $pString
00220 }
00221 # a multi (listified) entry was given
00222 # return the listified entry
00223 if {$pList} { return $rList }
00224 return $pString
00225 }
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264 ret ::ip::intToString (type int , type args) {
00265 set ipv4 1
00266 while {[llength $args]} {
00267 switch -- [lindex $args 0] {
00268 -ipv4 {set args [lrange $args 1 end]}
00269 default {
00270 return -code error [msgcat::mc "option %s not supported" [lindex $args 0]]
00271 }
00272 }
00273 }
00274 return [ip::ToString [binary format I [expr {$int}]]]
00275 }
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313 ret ::ip::toInteger (type ip) {
00314 binary scan [ip::Normalize4 $ip] I out
00315 return $out
00316 }
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353 ret ::ip::toHex (type ip) {
00354 binary scan [ip::Normalize4 $ip] H8 out
00355 return "0x$out"
00356 }
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394 ret ::ip::maskToInt (type mask) {
00395 if {[string is integer -strict $mask]} {
00396 set maskInt [expr {(0xFFFFFFFF << (32 - $mask))}]
00397 } else {
00398 binary scan [Normalize4 $mask] I maskInt
00399 }
00400 set maskInt [expr {$maskInt & 0xFFFFFFFF}]
00401 return [format %u $maskInt]
00402 }
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446 ret ::ip::broadcastAddress (type prefix , type args) {
00447 set ipv4 1
00448 while {[llength $args]} {
00449 switch -- [lindex $args 0] {
00450 -ipv4 {set args [lrange $args 1 end]}
00451 default {
00452 return -code error [msgcat::mc "option %s not supported" [lindex $args 0]]
00453 }
00454 }
00455 }
00456 if {[llength $prefix] == 2} {
00457 lassign $prefix net mask
00458 } else {
00459 set net [maskToInt [ip::prefix $prefix]]
00460 set mask [maskToInt [ip::mask $prefix]]
00461 }
00462 set ba [expr {$net | ((~$mask)&0xffffffff)}]
00463
00464 if {[llength $prefix]==2} {
00465 return [format "0x%08x" $ba]
00466 }
00467 return [ToString [binary format I $ba]]
00468 }
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511 ret ::ip::maskToLength (type mask , type args) {
00512 set ipv4 1
00513 while {[llength $args]} {
00514 switch -- [lindex $args 0] {
00515 -ipv4 {set args [lrange $args 1 end]}
00516 default {
00517 return -code error [msgcat::mc "option %s not supported" [lindex $args 0]]
00518 }
00519 }
00520 }
00521 #pick the fastest method for either format
00522 if {[string is integer -strict $mask]} {
00523 binary scan [binary format I [expr {$mask}]] B32 maskB
00524 if {[regexp -all {^1+} $maskB ones]} {
00525 return [string length $ones]
00526 } else {
00527 return 0
00528 }
00529 } else {
00530 regexp {\/(.+)} $mask dumb mask
00531 set prefix 0
00532 foreach ipByte [split $mask {.}] {
00533 switch $ipByte {
00534 255 {incr prefix 8; continue}
00535 254 {incr prefix 7}
00536 252 {incr prefix 6}
00537 248 {incr prefix 5}
00538 240 {incr prefix 4}
00539 224 {incr prefix 3}
00540 192 {incr prefix 2}
00541 128 {incr prefix 1}
00542 0 {}
00543 default {
00544 return -code error [msgcat::mc "not an ip mask: %s" $mask]
00545 }
00546 }
00547 break
00548 }
00549 return $prefix
00550 }
00551 }
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590 ret ::ip::lengthToMask (type masklen , type args) {
00591 while {[llength $args]} {
00592 switch -- [lindex $args 0] {
00593 -ipv4 {set args [lrange $args 1 end]}
00594 default {
00595 return -code error [msgcat::mc "option %s not supported" [lindex $args 0]]
00596 }
00597 }
00598 }
00599 # the fastest method is just to look
00600 # thru an array
00601 return $::ip::maskLenToDotted($masklen)
00602 }
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642 ret ::ip::nextNet (type prefix , type mask , type args) {
00643 set count 1
00644 while {[llength $args]} {
00645 switch -- [lindex $args 0] {
00646 -ipv4 {set args [lrange $args 1 end]}
00647 default {
00648 set count [lindex $args 0]
00649 set args [lrange $args 1 end]
00650 }
00651 }
00652 }
00653 if {![string is integer -strict $prefix]} {
00654 set prefix [toInteger $prefix]
00655 }
00656 if {![string is integer -strict $mask] || ($mask < 33 && $mask > 0)} {
00657 set mask [maskToInt $mask]
00658 }
00659
00660 set prefix [expr $prefix + ($mask ^ 0xFFffFFff) + $count ]
00661 return [format "0x%08x" $prefix]
00662 }
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703 ret ::ip::isOverlap (type ip , type args) {
00704 lassign [SplitIp $ip] ip1 mask1
00705 set ip1int [toInteger $ip1]
00706 set mask1int [maskToInt $mask1]
00707
00708 set overLap 0
00709 foreach prefix $args {
00710 lassign [SplitIp $prefix] ip2 mask2
00711 set ip2int [toInteger $ip2]
00712 set mask2int [maskToInt $mask2]
00713 set mask1mask2 [expr {$mask1int & $mask2int}]
00714 if {[expr {$ip1int & $mask1mask2}] == [expr {$ip2int & $mask1mask2}]} {
00715 set overLap 1
00716 break
00717 }
00718 }
00719 return $overLap
00720 }
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774 ret ::ip::isOverlapNativeTcl (type args) {
00775 set all 0
00776 set inline 0
00777 set notOverlap 0
00778 set ipv4 1
00779 foreach sw [lrange $args 0 end-3] {
00780 switch -exact -- $sw {
00781 -all {
00782 set all 1
00783 set allList [list]
00784 }
00785 -inline {set inline 1}
00786 -ipv4 {}
00787 }
00788 }
00789 set args [lassign [lrange $args end-2 end] ip1int mask1int prefixList]
00790 if {$inline} {
00791 set overLap [list]
00792 } else {
00793 set overLap 0
00794 }
00795 set count 0
00796 foreach prefix $prefixList {
00797 incr count
00798 lassign $prefix ip2int mask2int
00799 set mask1mask2 [expr {$mask1int & $mask2int}]
00800 if {[expr {$ip1int & $mask1mask2}] == [expr {$ip2int & $mask1mask2}]} {
00801 if {$inline} {
00802 set overLap [list $prefix]
00803 } else {
00804 set overLap $count
00805 }
00806 if {$all} {
00807 if {$inline} {
00808 lappend allList $prefix
00809 } else {
00810 lappend allList $count
00811 }
00812 } else {
00813 break
00814 }
00815 }
00816 }
00817 if {$all} {return $allList}
00818 return $overLap
00819 }
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854 ret ::ip::ipToLayer2Multicast ( type ipaddr ) {
00855 regexp "\[0-9\]+\.(\[0-9\]+)\.(\[0-9\]+)\.(\[0-9\]+)" $ipaddr junk ip2 ip3 ip4
00856 #remove MSB of 2nd octet of IP address for mcast L2 addr
00857 set mac2 [expr {$ip2 & 127}]
00858 return [format "01.00.5e.%02x.%02x.%02x" $mac2 $ip3 $ip4]
00859 }
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901 ret ::ip::ipHostFromPrefix ( type prefix , type args ) {
00902 set mask [mask $prefix]
00903 set ipaddr [prefix $prefix]
00904 if {[llength $args]} {
00905 array set opts $args
00906 } else {
00907 if {$mask==32} {
00908 return $ipaddr
00909 } else {
00910 return [intToString [expr {[toHex $ipaddr] + 1} ]]
00911 }
00912 }
00913 set format {-ipv4}
00914 # if we got here, then options were set
00915 if {[info exists opts(-exclude)]} {
00916 #basic algo is:
00917 # 1. throw away prefixes that are less specific that $prefix
00918 # 2. of remaining pfx, throw away prefixes that do not overlap
00919 # 3. run reducetoAggregates on specific nets
00920 # 4.
00921
00922 # 1. convert to hex format
00923 set currHex [prefixToNative $prefix ]
00924 set exclHex [prefixToNative $opts(-exclude) ]
00925 # sort the prefixes by their mask, include the $prefix as a marker
00926 # so we know from where to throw away prefixes
00927 set sortedPfx [lsort -integer -index 1 [concat [list $currHex] $exclHex]]
00928 # throw away prefixes that are less specific than $prefix
00929 set specPfx [lrange $sortedPfx [expr {[lsearch -exact $sortedPfx $currHex] +1} ] end]
00930
00931 #2. throw away non-overlapping prefixes
00932 set specPfx [isOverlapNative -all -inline \
00933 [lindex $currHex 0 ] \
00934 [lindex $currHex 1 ] \
00935 $specPfx ]
00936 #3. run reduce aggregates
00937 set specPfx [reduceToAggregates $specPfx]
00938
00939 #4 now have to pick an address that overlaps with $currHex but not with
00940 # $specPfx
00941 # 4.1 find the largest prefix w/ most specific mask and go to the next net
00942
00943
00944 # current ats tcl does not allow this in one command, so
00945 # for now just going to grab the last prefix (list is already sorted)
00946 set sPfx [lindex $specPfx end]
00947 set startPfx $sPfx
00948 # add currHex to specPfx
00949 set oChkPfx [concat $specPfx [list $currHex]]
00950
00951
00952 set notcomplete 1
00953 set overflow 0
00954 while {$notcomplete} {
00955 #::ipMore::log::debug "doing nextnet on $sPfx"
00956 set nextNet [nextNet [lindex $sPfx 0] [lindex $sPfx 1]]
00957 #::ipMore::log::debug "trying $nextNet"
00958 if {$overflow && ($nextNet > $startPfx)} {
00959 #we've gone thru the entire net and didn't find anything.
00960 return -code error [msgcat::mc "ip host could not be found in %s" $prefix]
00961 break
00962 }
00963 set oPfx [isOverlapNative -all -inline \
00964 $nextNet -1 \
00965 $oChkPfx
00966 ]
00967 switch -exact [llength $oPfx] {
00968 0 {
00969 # no overlap at all. meaning we have gone beyond the bounds of
00970 # $currHex. need to overlap and try again
00971 #::ipMore::log::debug {ipHostFromPrefix: overlap done}
00972 set overflow 1
00973 }
00974 1 {
00975 #we've found what we're looking for. pick this address and exit
00976 return [intToString $nextNet]
00977 }
00978 default {
00979 # 2 or more overlaps, need to increment again
00980 set sPfx [lindex $oPfx 0]
00981 }
00982 }
00983 }
00984 }
00985 }
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011
01012
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023
01024 ret ::ip::reduceToAggregates ( type prefixList ) {
01025 #find out format of $prefixeList
01026 set dotConv 0
01027 if {[llength [lindex $prefixList 0]]==1} {
01028 #format is dotted form convert all prefixes to native form
01029 set prefixList [ip::prefixToNative $prefixList]
01030 set dotConv 1
01031 }
01032
01033 set nonOverLapping $prefixList
01034 while {1==1} {
01035 set overlapFound 0
01036 set remaining $nonOverLapping
01037 set nonOverLapping {}
01038 while {[llength $remaining]} {
01039 set current [lvarpop remaining]
01040 set overLap [ip::isOverlapNative [lindex $current 0] [lindex $current 1] $remaining]
01041 if {$overLap} {
01042 #there was a overlap find out which prefix has a the smaller mask, and keep that one
01043 if {[lindex $current 1] > [lindex [lindex $remaining [expr {$overLap -1}]] 1]} {
01044 #current has more restrictive mask, throw that prefix away
01045 # keep other prefix
01046 lappend nonOverLapping [lindex $remaining [expr {$overLap -1}]]
01047 } else {
01048 lappend nonOverLapping $current
01049 }
01050 lvarpop remaining [expr {$overLap -1}]
01051 set overlapFound 1
01052 } else {
01053 #no overlap, keep all prefixes, don't touch the stuff in
01054 # remaining, it is needed for other overlap checking
01055 lappend nonOverLapping $current
01056 }
01057 }
01058 if {$overlapFound==0} {break}
01059 }
01060 if {$dotConv} {return [nativeToPrefix $nonOverLapping]}
01061 return $nonOverLapping
01062 }
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083
01084
01085
01086
01087
01088
01089
01090
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102 ret ::ip::longestPrefixMatch ( type ipaddr , type prefixList , type args) {
01103 set ipv4 1
01104 while {[llength $args]} {
01105 switch -- [lindex $args 0] {
01106 -ipv4 {set args [lrange $args 1 end]}
01107 default {
01108 return -code error [msgcat::mc "option %s not supported" [lindex $args 0]]
01109 }
01110 }
01111 }
01112 #find out format of prefixes
01113 set dotConv 0
01114 if {[llength [lindex $prefixList 0]]==1} {
01115 #format is dotted form convert all prefixes to native form
01116 set prefixList [ip::prefixToNative $prefixList]
01117 set dotConv 1
01118 }
01119 #sort so that most specific prefix is in the front
01120 if {[llength [lindex [lindex $prefixList 0] 1]]} {
01121 set prefixList [lsort -decreasing -integer -index 1 $prefixList]
01122 } else {
01123 set prefixList [list $prefixList]
01124 }
01125 if {![string is integer -strict $ipaddr]} {
01126 set ipaddr [prefixToNative $ipaddr]
01127 }
01128 set best [ip::isOverlapNative -inline \
01129 [lindex $ipaddr 0] [lindex $ipaddr 1] $prefixList]
01130 if {$dotConv && [llength $best]} {
01131 return [nativeToPrefix $best]
01132 }
01133 return $best
01134 }
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145
01146
01147
01148
01149
01150
01151
01152
01153
01154
01155
01156
01157
01158
01159
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169
01170
01171
01172
01173 if {![package vsatisfies [package provide Tcl] 8.4]} {
01174
01175 ret ip::cmpDotIP (type ipaddr1 , type ipaddr2) {
01176 # convert dotted to list of integers
01177 set ipaddr1 [split $ipaddr1 .]
01178 set ipaddr2 [split $ipaddr2 .]
01179 foreach a $ipaddr1 b $ipaddr2 {
01180 #ipMore::log::debug "$ipInt1 $ipInt2"
01181 if { $a < $b} {
01182 return -1
01183 } elseif {$a >$b} {
01184 return 1
01185 }
01186 }
01187 return 0
01188 }
01189 } else {
01190
01191 ret ip::cmpDotIP (type ipaddr1 , type ipaddr2) {
01192 # convert dotted to decimal
01193 set ipInt1 [::ip::toHex $ipaddr1]
01194 set ipInt2 [::ip::toHex $ipaddr2]
01195 #ipMore::log::debug "$ipInt1 $ipInt2"
01196 if { $ipInt1 < $ipInt2} {
01197 return -1
01198 } elseif {$ipInt1 >$ipInt2 } {
01199 return 1
01200 } else {
01201 return 0
01202 }
01203 }
01204 }
01205
01206
01207
01208
01209 namespace ::ip {
01210 variable maskLenToDotted
01211 variable x
01212
01213 for { x = 0} {$x <33} {incr x} {
01214 maskLenToDotted = ($x) [intToString [maskToInt $x]]
01215 }
01216 un x =
01217 }
01218