diff --git a/cmd/ips/cmd_dump.go b/cmd/ips/cmd_dump.go index 1b3987e..5779c85 100644 --- a/cmd/ips/cmd_dump.go +++ b/cmd/ips/cmd_dump.go @@ -37,6 +37,7 @@ func init() { dumpCmd.Flags().StringVarP(&readerOption, "input-option", "", "", UsageReaderOption) dumpCmd.Flags().StringVarP(&hybridMode, "hybrid-mode", "", "aggregation", UsageHybridMode) dumpCmd.Flags().StringVarP(&outputFile, "output-file", "o", "", UsageDumpOutputFile) + dumpCmd.Flags().IntVarP(&readerJobs, "reader-jobs", "", 0, UsageReaderJobs) } diff --git a/cmd/ips/cmd_pack.go b/cmd/ips/cmd_pack.go index 71ccbca..40980e6 100644 --- a/cmd/ips/cmd_pack.go +++ b/cmd/ips/cmd_pack.go @@ -37,6 +37,7 @@ func init() { packCmd.Flags().StringVarP(&outputFile, "output-file", "o", "", UsagePackOutputFile) packCmd.Flags().StringVarP(&outputFormat, "output-format", "", "", UsagePackOutputFormat) packCmd.Flags().StringVarP(&writerOption, "output-option", "", "", UsageWriterOption) + packCmd.Flags().IntVarP(&readerJobs, "reader-jobs", "", 0, UsageReaderJobs) } diff --git a/cmd/ips/config.go b/cmd/ips/config.go index 96735aa..f7cceeb 100644 --- a/cmd/ips/config.go +++ b/cmd/ips/config.go @@ -118,6 +118,9 @@ var ( // writerOption specifies the options for the writer. writerOption string + // readerJobs specifies the number of concurrent reader jobs. + readerJobs int + // myip // localAddr specifies the local address (in IP format) that should be used for outbound connections. // Useful in systems with multiple network interfaces. @@ -237,6 +240,10 @@ func GetFlagConfig() *ips.Config { conf.WriterOption = writerOption } + if readerJobs != 0 { + conf.ReaderJobs = readerJobs + } + if len(addr) != 0 { conf.Addr = addr } diff --git a/cmd/ips/const.go b/cmd/ips/const.go index 5e3c9b6..ad4c583 100644 --- a/cmd/ips/const.go +++ b/cmd/ips/const.go @@ -46,6 +46,7 @@ const ( UsageReaderOption = "Additional options for the database reader, if applicable." UsageWriterOption = "Additional options for the database writer, if applicable." UsageHybridMode = "Sets mode for multi-IP source handling; 'comparison' to compare, 'aggregation' to merge data." + UsageReaderJobs = "Set the number of concurrent reader jobs. This parameter controls the parallelism level of reading operations." // Output Flags diff --git a/docs/config.md b/docs/config.md index f1aaf68..236e595 100644 --- a/docs/config.md +++ b/docs/config.md @@ -29,6 +29,7 @@ * [dp_rewriter_files](#dprewriterfiles) * [reader_option](#readeroption) * [writer_option](#writeroption) + * [reader_jobs](#readerjobs) * [myip_count](#myipcount) * [myip_timeout_s](#myiptimeouts) * [addr](#addr) @@ -330,6 +331,16 @@ $ ips config set rewrite_files "/path/to/rewrite1.txt,/path/to/rewrite2.txt" 例如 `mmdb` 数据库的 `select_languages` 等,具体功能请查阅数据库文档。 +### reader_jobs + +`reader_jobs` 参数用于控制读取操作的并发作业数量。它定义了可以同时进行的读取操作的最大数目,从而实现高效的数据处理。 + +默认情况下无需设置 `reader_jobs`,IPS 将根据系统的 CPU 核心数与输出格式自动设置。 + +值得注意的是,如果并发数设置过高,可能导致系统资源竞争加剧,进而影响程序的整体性能表现。 + +在某些情况下,增加读取器的并发数并不会带来性能提升。实际上,任务完成时间取决于读取器和写入器中的较慢的一方,尤其是大部分写入器(例如 IPDB 和 MMDB)目前还不支持并发写入,请根据自身情况选择适合的并发数。 + ### myip_count 在查询本机 IP 地址时,此参数定义了返回相同 IP 地址的最小探测器数量。默认值为 `3`。 diff --git a/docs/config_en.md b/docs/config_en.md index a635fa2..1d9e0ff 100644 --- a/docs/config_en.md +++ b/docs/config_en.md @@ -29,6 +29,7 @@ * [dp_rewriter_files](#dprewriterfiles) * [reader_option](#readeroption) * [writer_option](#writeroption) + * [reader_jobs](#readerjobs) * [myip_count](#myipcount) * [myip_timeout_s](#myiptimeouts) * [addr](#addr) @@ -332,6 +333,16 @@ Some database formats provide additional writing options, which can be set durin For example, `mmdb` database's `select_languages` and so on, please refer to the database documentation for specific functions. +### reader_jobs + +The `reader_jobs` parameter is designed to control the number of concurrent jobs for reading operations. It specifies the maximum number of reading operations that can be performed simultaneously, thereby enhancing the efficiency of data processing. + +By default, setting `reader_jobs` is not necessary, as IPS will automatically determine the appropriate number based on the system's CPU core count and the output format. + +It's important to note that setting an excessively high number of concurrent jobs may lead to intensified competition for system resources, potentially degrading the overall performance of the program. + +In some cases, increasing the concurrency of readers does not result in performance improvement. In fact, the completion time of tasks depends on the slower of the readers and writers. This is particularly relevant as most writers (such as IPDB and MMDB) currently do not support concurrent writing. Therefore, choose a suitable concurrency level based on your specific circumstances. + ### myip_count When querying the local IP address, this parameter defines the minimum number of detectors that return the same IP address. The default value is `3`. diff --git a/format/ipdb/writer.go b/format/ipdb/writer.go index 6fa61c1..92d8e5e 100644 --- a/format/ipdb/writer.go +++ b/format/ipdb/writer.go @@ -221,3 +221,8 @@ func IntToBinaryBE(num, length int) []byte { return []byte{} } } + +// WriterFormat returns the format of the writer. +func (w *Writer) WriterFormat() string { + return DBFormat +} diff --git a/format/mmdb/writer.go b/format/mmdb/writer.go index 0292176..235672c 100644 --- a/format/mmdb/writer.go +++ b/format/mmdb/writer.go @@ -119,6 +119,11 @@ func (w *Writer) WriteTo(iw io.Writer) (int64, error) { return w.writer.WriteTo(iw) } +// WriterFormat returns the format of the writer. +func (w *Writer) WriterFormat() string { + return DBFormat +} + // ConvertMap converts fields and values to a map. func (w *Writer) ConvertMap(fields, values []string) map[string]interface{} { ret := make(map[string]interface{}) diff --git a/format/plain/writer.go b/format/plain/writer.go index deeb5a1..e9393cc 100644 --- a/format/plain/writer.go +++ b/format/plain/writer.go @@ -112,3 +112,8 @@ func (w *Writer) Header() error { return nil } + +// WriterFormat returns the format of the writer. +func (w *Writer) WriterFormat() string { + return DBFormat +} diff --git a/format/writer.go b/format/writer.go index e4c6f92..03d3f5d 100644 --- a/format/writer.go +++ b/format/writer.go @@ -38,6 +38,9 @@ type Writer interface { // WriteTo writes data to io.Writer WriteTo(w io.Writer) (int64, error) + + // WriterFormat returns the format of the writer. + WriterFormat() string } // NewWriter creates a Writer based on its format or file name. diff --git a/internal/ipio/dumper.go b/internal/ipio/dumper.go index 7933b3a..ecaa9c8 100644 --- a/internal/ipio/dumper.go +++ b/internal/ipio/dumper.go @@ -17,16 +17,23 @@ package ipio import ( + "context" "net" - "reflect" + "runtime" + "sync" log "github.com/sirupsen/logrus" "github.com/sjzar/ips/format" + "github.com/sjzar/ips/format/plain" "github.com/sjzar/ips/ipnet" + "github.com/sjzar/ips/pkg/errors" "github.com/sjzar/ips/pkg/model" ) +// ChannelBufferSize why 1000? I don't know :) +const ChannelBufferSize = 1000 + // StandardDumper serves as a standard mechanism to transfer IP database from one format to another. type StandardDumper struct { format.Reader @@ -49,62 +56,179 @@ func NewStandardDumper(r format.Reader, w format.Writer) *StandardDumper { // Dump is a convenience method to transfer IP data from a reader to a writer. // It is equivalent to calling NewStandardDumper(r, w).Dump(). func Dump(r format.Reader, w format.Writer) error { - return NewStandardDumper(r, w).Dump() + return NewStandardDumper(r, w).Dump(0) } // Dump transfers IP data from the Reader to the Writer. -func (d *StandardDumper) Dump() error { - for d.Next() { - if err := d.Insert(d.info); err != nil { - return err +func (d *StandardDumper) Dump(readerJobs int) error { + if readerJobs <= 0 { + switch d.WriterFormat() { + case plain.DBFormat: + readerJobs = 1 + default: + readerJobs = runtime.NumCPU() + } + } + + ipStart, ipEnd := net.IPv4(0, 0, 0, 0), ipnet.LastIPv4 + if d.Meta().IsIPv6Support() { + ipStart, ipEnd = make(net.IP, net.IPv6len), ipnet.LastIPv6 + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + retChan := make(chan *model.IPInfo, ChannelBufferSize) + errChan := make(chan error, readerJobs) + defer close(errChan) + wg := sync.WaitGroup{} + + split := ipnet.SplitIPNet(ipStart, ipEnd, readerJobs) + for i := 0; i < len(split)-1; i++ { + wg.Add(1) + go func(ctx context.Context, start, end net.IP) { + defer wg.Done() + sd := SimpleDumper{ + Reader: d.Reader, + ipStart: start, + ipEnd: end, + } + if err := sd.Dump(ctx, retChan); err != nil { + errChan <- err + return + } + }(ctx, split[i], split[i+1]) + } + + go func() { + wg.Wait() + close(retChan) + }() + + for { + select { + case ipInfo, ok := <-retChan: + if !ok { + return nil + } + if err := d.Insert(ipInfo); err != nil { + log.Debug("StandardDumper Insert() failed ", ipInfo, err) + return err + } + case err := <-errChan: + if err != nil { + log.Debug("StandardDumper Dump2() failed ", err) + return err + } } } - if d.err != nil { - return d.err +} + +// SimpleDumper is a structure that facilitates the extraction of IP information within a specified range. +type SimpleDumper struct { + format.Reader + ipStart net.IP + ipEnd net.IP +} + +// Dump iterates over IP addresses in the specified range, sending IP information to retChan. +// The operation is context-aware and will stop if the context is cancelled. +func (d *SimpleDumper) Dump(ctx context.Context, retChan chan<- *model.IPInfo) error { + // Validate the IP range before proceeding. + if d.ipStart == nil || d.ipEnd == nil || ipnet.IPLess(d.ipEnd, d.ipStart) { + return errors.ErrInvalidIPRange } + + marker := d.ipStart + info, err := d.Find(marker) + if err != nil { + return err + } + + // Adjust marker if it doesn't match the start of the IP range. + if !marker.Equal(info.IPNet.Start) { + marker = ipnet.NextIP(info.IPNet.End.To16()) + } + + // Iterate over IP addresses until the end of the range is reached. + for done := d.done(marker, false); !done; done = d.done(marker, true) { + select { + case <-ctx.Done(): // Check if the operation was cancelled. + return nil + default: + } + info, err = d.next(marker) + if err != nil { + return err + } + if info == nil { + break + } + retChan <- info // Send IP information to the channel. + marker = ipnet.NextIP(info.IPNet.End.To16()) + } + return nil } -// Next fetches the next IP information from the Reader. -func (d *StandardDumper) Next() bool { - if d.done { +// done checks whether the end of the IP range has been reached. +func (d *SimpleDumper) done(marker net.IP, started bool) bool { + if marker == nil { return false } - if d.marker == nil { - if d.Meta().IsIPv6Support() { - d.marker = make(net.IP, net.IPv6len) - } else { - d.marker = net.IPv4(0, 0, 0, 0) - } + + // Check if the current marker is outside the IP range. + if !ipnet.Contains(d.ipStart, d.ipEnd, marker) { + return true } - d.info = nil - // Continuously fetch IP information until either a change in the data is found or the end of the IP range is reached. + // Check if the end of the range is reached. + return started && d.ipEnd.Equal(ipnet.PrevIP(marker)) +} + +// next retrieves the next IP information from the Reader based on the marker. +func (d *SimpleDumper) next(marker net.IP) (*model.IPInfo, error) { + if marker == nil { + marker = d.ipStart + } + + var currentInfo *model.IPInfo for { - info, err := d.Find(d.marker) + info, err := d.Find(marker) if err != nil { - log.Debug("StandardDumper Find() failed ", d.marker, info, err) - d.err = err - return false + return nil, err } - if d.info == nil { - d.info = info + // Determine if a new IP range is encountered. + if currentInfo == nil { + currentInfo = info } else { - if !reflect.DeepEqual(d.info.Values(), info.Values()) { + if !Equal(currentInfo.Values(), info.Values()) { break } - if ok := d.info.IPNet.Join(info.IPNet); !ok { + if ok := currentInfo.IPNet.Join(info.IPNet); !ok { break } } - // Move to the next IP address in the range. - d.marker = ipnet.NextIP(info.IPNet.End) - if ipnet.IsLastIP(info.IPNet.End, d.Meta().IsIPv6Support()) { - d.done = true + marker = ipnet.NextIP(info.IPNet.End) + if d.done(marker, true) { break } } + return currentInfo, nil +} + +// Equal compares two slices of strings for equality. +// FIXME use slices.Equal() when go.mod updates to Go 1.18 +func Equal(s1, s2 []string) bool { + if len(s1) != len(s2) { + return false + } + for i := range s1 { + if s1[i] != s2[i] { + return false + } + } return true } diff --git a/internal/ips/config.go b/internal/ips/config.go index be98ff7..d65066a 100644 --- a/internal/ips/config.go +++ b/internal/ips/config.go @@ -108,6 +108,10 @@ type Config struct { // WriterOption specifies the options for the writer. WriterOption string `mapstructure:"writer_option"` + // ReaderJobs specifies the number of concurrent jobs for the reader. + // It controls how many reading operations can be performed in parallel. + ReaderJobs int `mapstructure:"reader_jobs"` + // MyIP // LocalAddr specifies the local address (in IP format) that should be used for outbound connections. // Useful in systems with multiple network interfaces. diff --git a/internal/ips/pack.go b/internal/ips/pack.go index 5a038e3..3b8e82d 100644 --- a/internal/ips/pack.go +++ b/internal/ips/pack.go @@ -89,7 +89,7 @@ func (m *Manager) Pack(_format, file []string, _outputFormat, outputFile string) // Dump data using the dumper dumper := ipio.NewStandardDumper(reader, writer) - if err := dumper.Dump(); err != nil { + if err := dumper.Dump(m.Conf.ReaderJobs); err != nil { log.Debug("dumper.Dump error: ", err) return err } diff --git a/internal/parser/regexp.go b/internal/parser/regexp.go index 9951f10..88598ff 100644 --- a/internal/parser/regexp.go +++ b/internal/parser/regexp.go @@ -25,11 +25,11 @@ var ( IPv4Regexp = regexp.MustCompile(`(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)){3}`) // IPv6Regexp IPv6 正则表达式 - // [fF][eE]80:(:[0-9a-fA-F]{1,4}){0,4}(%\w+)?| # IPv6 Link-local + // [fF][eE]80:(:[0-9a-fA-F]{1,4}){0,4}(%\w+)?| # IPv6 Link-local (`net.ParseIP` does not support this format) // ([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}| # IPv6 // ::([fF]{4}){1}:(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)){3}| # IPv4-mapped IPv6 address // (([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?::(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})? # IPv6 with two colons - IPv6Regexp = regexp.MustCompile(`[fF][eE]80:(:[0-9a-fA-F]{1,4}){0,4}(%\w+)?|([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::([fF]{4}){1}:(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)){3}|(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?::(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?`) + IPv6Regexp = regexp.MustCompile(`([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::([fF]{4}){1}:(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)){3}|(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?::(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?`) // DomainRegexp 域名正则表达式 // [a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,} diff --git a/ipnet/ipnet.go b/ipnet/ipnet.go index 856456d..fd54a8f 100644 --- a/ipnet/ipnet.go +++ b/ipnet/ipnet.go @@ -45,5 +45,5 @@ func LastIP(ipNet *net.IPNet) net.IP { // Contains checks if the given IP is within the range of start to end (inclusive of start and exclusive of end). func Contains(start, end, ip net.IP) bool { - return !IPLess(ip, start) && IPLess(ip, NextIP(end)) + return !IPLess(ip, start) && (IPLess(ip, end) || ip.Equal(end)) } diff --git a/ipnet/split.go b/ipnet/split.go new file mode 100644 index 0000000..6cf3b7d --- /dev/null +++ b/ipnet/split.go @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2023 shenjunzheng@gmail.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ipnet + +import ( + "math" + "math/big" + "net" +) + +// SplitIPNet splits an IP range into several parts based on the specified number +// and a reference list of base IPs which represent the density distribution of the entire IP range. +// It supports both IPv4 and IPv6 addresses. +func SplitIPNet(start net.IP, end net.IP, num int) []net.IP { + if num < 1 { + return nil // Return nil if the number of parts is negative + } + if IPLess(end, start) { + return nil // Return nil if the end IP is less than the start IP + } + if num == 1 { + return []net.IP{start, end} // Return the original range if the number of parts is 1 + } + + // Split based on IP type + if start.To4() != nil && end.To4() != nil { + return splitIPNetIPv4(start, end, num) + } else if start.To16() != nil && end.To16() != nil { + return splitIPNetIPv6(start, end, num) + } + + return nil // Return nil if IP types are mixed or unrecognized +} + +// splitIPNetIPv4 splits an IPv4 range into several parts. +// It uses a list of base IPv4 addresses to guide the split points based on the IP density distribution. +func splitIPNetIPv4(start net.IP, end net.IP, num int) []net.IP { + startIndex, endIndex := GetIndex(BaseIPv4, start), GetIndex(BaseIPv4, end) + + step := float64(endIndex-startIndex) / float64(num-1) + retIndex := make([]float64, num) + for i := 0; i < num; i++ { + retIndex[i] = float64(startIndex) + step*float64(i) + } + + ret := make([]net.IP, 0, num) + ret = append(ret, start) + switch { + case step >= 1: + for i := 1; i < num-1; i++ { + ret = append(ret, BaseIPv4[int(retIndex[i])]) + } + case 0 < step && step < 1: + startNum := IPToUint32(start) + for i := 1; i < num-1; i++ { + base := IPToUint32(BaseIPv4[int(retIndex[i])]) + if base < startNum { + base = startNum + } + next := IPToUint32(BaseIPv4[int(retIndex[i])+1]) + offset := float64(next-base) * (retIndex[i] - math.Floor(retIndex[i])) + ret = append(ret, Uint32ToIPv4(base+uint32(offset))) + } + case step == 0: + startNum := IPToUint32(start) + endNum := IPToUint32(end) + step := float64(endNum-startNum) / float64(num-1) + for i := 1; i < num-1; i++ { + offset := step * float64(i) + ret = append(ret, Uint32ToIPv4(startNum+uint32(offset))) + } + } + ret = append(ret, end) + + return ret +} + +// splitIPNetIPv6 splits an IPv6 range into several parts. +// Similar to splitIPNetIPv4, it uses a list of base IPv6 addresses for splitting guidance. +func splitIPNetIPv6(start net.IP, end net.IP, num int) []net.IP { + startIndex, endIndex := GetIndex(BaseIPv6, start), GetIndex(BaseIPv6, end) + + step := float64(endIndex-startIndex) / float64(num-1) + retIndex := make([]float64, num) + for i := 0; i < num; i++ { + retIndex[i] = float64(startIndex) + step*float64(i) + } + + ret := make([]net.IP, 0, num) + ret = append(ret, start) + switch { + case step >= 1: + for i := 1; i < num-1; i++ { + ret = append(ret, BaseIPv6[int(retIndex[i])]) + } + case 0 < step && step < 1: + startNum := IPToBigInt(start) + for i := 1; i < num-1; i++ { + base := IPToBigInt(BaseIPv6[int(retIndex[i])]) + if base.Cmp(startNum) < 0 { + base = startNum + } + next := IPToBigInt(BaseIPv6[int(retIndex[i])+1]) + offset, _ := big.NewFloat(0).SetInt(next.Sub(next, base)).Float64() + offset = offset * (retIndex[i] - math.Floor(retIndex[i])) + base = base.Add(base, big.NewInt(int64(offset))) + ret = append(ret, BigIntToIP(base)) + } + case step == 0: + startNum := IPToBigInt(start) + endNum := IPToBigInt(end) + step, _ := big.NewFloat(0).SetInt(endNum.Sub(endNum, startNum)).Float64() + step = step / float64(num-1) + for i := 1; i < num-1; i++ { + base := IPToBigInt(start) + offset := step * float64(i) + base = base.Add(base, big.NewInt(int64(offset))) + ret = append(ret, BigIntToIP(base)) + } + } + ret = append(ret, end) + + return ret +} + +// GetIndex finds the index of the given IP in the base list. +// It returns the highest index of an IP in the base list which is less than or equal to the given IP. +func GetIndex(base []net.IP, ip net.IP) int { + for i := len(base) - 1; i >= 0; i-- { + if !IPLess(ip, base[i]) { + return i + } + } + return 0 +} + +// BaseIPv4 represents a predefined list of IPv4 addresses. +// These addresses can be used as reference points or base IPs for various network operations. +var BaseIPv4 = []net.IP{ + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x4, 0x31, 0x32, 0xb0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc, 0x7, 0xc9, 0xa0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc, 0x7c, 0x72, 0x4f}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc, 0xd3, 0x58, 0x40}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x17, 0x19, 0x96, 0x98}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x18, 0x6a, 0x15, 0xac}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x1b, 0x52, 0x53, 0x70}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x22, 0x72, 0x3b, 0xe0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x26, 0x65, 0xa3, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x28, 0x8d, 0x8b, 0xde}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x2d, 0xd5, 0xee, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x2f, 0x2e, 0x55, 0x30}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x32, 0xa0, 0xd8, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x32, 0xe6, 0xe6, 0x4c}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x3a, 0x5, 0x29, 0xc0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x3c, 0xf1, 0x35, 0x18}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x3e, 0x51, 0x88, 0x78}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x3e, 0xe8, 0xdc, 0x2c}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x3f, 0xa6, 0x7f, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x40, 0x18, 0x7c, 0x94}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x40, 0xa9, 0xdb, 0xf0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x41, 0x40, 0xb6, 0xc8}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x41, 0xbd, 0xa2, 0x4c}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x42, 0x5f, 0x9d, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x42, 0xfb, 0x29, 0xd0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x43, 0x82, 0xb5, 0xcc}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x44, 0x56, 0x2d, 0xdb}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x45, 0x18, 0x2c, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x45, 0xd7, 0x44, 0x8}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x46, 0x5a, 0x76, 0x48}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x47, 0x36, 0x5d, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x48, 0x2b, 0xbb, 0x8}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x4a, 0x55, 0x61, 0x78}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x4b, 0x2c, 0x4, 0x28}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x4c, 0x50, 0x17, 0xf8}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x4d, 0x3c, 0x7e, 0x58}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x4e, 0x64, 0x96, 0xc0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x50, 0x30, 0x2e, 0x80}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x50, 0xc2, 0x26, 0x20}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x51, 0x8f, 0x33, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x52, 0x84, 0x54, 0x90}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x53, 0x1a, 0xe9, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x54, 0x98, 0xae, 0x83}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x55, 0xb8, 0xc2, 0x5b}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x57, 0x66, 0x34, 0x20}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x58, 0x33, 0x4a, 0x18}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x59, 0x27, 0xe2, 0x60}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x5a, 0x73, 0xf, 0x82}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x5c, 0x2e, 0x6e, 0x8}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x5d, 0x33, 0x97, 0xd8}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x5e, 0x57, 0x15, 0x8c}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x60, 0x3, 0xc1, 0x18}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x60, 0xdc, 0xd4, 0x10}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x62, 0x80, 0xb7, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x63, 0x40, 0x57, 0x60}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x64, 0xc, 0x64, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x68, 0x22, 0x23, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x6b, 0x6f, 0xe8, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x6c, 0xf4, 0x7b, 0xb8}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x71, 0x17, 0xe0, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x75, 0xec, 0x6b, 0x10}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x79, 0xb4, 0xb0, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x7a, 0xfc, 0xec, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x7d, 0x13, 0xec, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x81, 0xfe, 0xd, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x89, 0x94, 0xa4, 0x98}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x90, 0xac, 0x52, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x97, 0x4, 0x3b, 0x80}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x9a, 0x36, 0x17, 0x49}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x9f, 0x64, 0x5f, 0x44}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xa4, 0x7f, 0x40, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xac, 0xd, 0x78, 0x48}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xad, 0xa6, 0x6a, 0x18}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xaf, 0xb6, 0xec, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xb2, 0x58, 0x42, 0xa4}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xb5, 0xe0, 0xcc, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xb8, 0x6b, 0x3d, 0x48}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xba, 0xeb, 0xb9, 0x80}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xbc, 0xc9, 0xdc, 0x10}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xbf, 0x5c, 0x85, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc1, 0xc3, 0xb1, 0x54}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc2, 0xca, 0xed, 0xc0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc3, 0x59, 0x94, 0x48}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc4, 0x76, 0x19, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc7, 0xa, 0xc1, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc9, 0x86, 0x55, 0xa0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xcb, 0x71, 0x98, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xce, 0x12, 0xd4, 0x10}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xcf, 0xd6, 0x5a, 0x80}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xd0, 0xfb, 0xb2, 0x38}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xd1, 0xd4, 0x7e, 0x1d}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xd3, 0x28, 0x76, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xd4, 0x91, 0xf3, 0xd0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xd5, 0x2a, 0xb7, 0x20}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xd5, 0xbb, 0x5c, 0x29}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xd8, 0x77, 0xf1, 0xf8}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xd9, 0x2c, 0x61, 0x80}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xd9, 0xcc, 0x74, 0x60}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, +} + +// BaseIPv6 represents a predefined list of IPv6 addresses. +// Similar to BaseIPv4, these addresses serve as reference or base IPs for IPv6 network operations. +var BaseIPv6 = []net.IP{ + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x3d, 0xc0, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0xf2, 0xa2, 0xc8}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x3, 0xe8, 0x84}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x6c, 0x8, 0x16}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0xcb, 0x7a, 0x84}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x8e, 0x37, 0xe0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x47, 0x2b, 0xf9, 0x98}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4a, 0xda, 0x29, 0x14}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f, 0xa1, 0x8a, 0x40}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0x8b, 0xaf, 0x60}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5c, 0x49, 0xa, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x61, 0x4c, 0x47, 0x30}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0xb6, 0xb2, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6d, 0x7a, 0xc, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x76, 0xe9, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x93, 0x0, 0xcb, 0x20}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa3, 0x2f, 0xb1, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb0, 0x23, 0xa4, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb9, 0xb9, 0x1c, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0, 0xa1, 0x6e, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xca, 0x82, 0xbe, 0x20}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd4, 0x73, 0xb4, 0xec}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x2, 0xdd, 0x4c, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x18, 0x67, 0xc0, 0x3c}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x1f, 0xbb, 0x6a, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x2d, 0x59, 0x55, 0x88}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x32, 0xf1, 0xae, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x41, 0xf6, 0x75, 0x40}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x45, 0x49, 0xc4, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x47, 0x5b, 0xe8, 0x18}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x4b, 0x82, 0x3a, 0xdc}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x50, 0xc2, 0xa8, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x57, 0x3d, 0x74, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x5d, 0x61, 0xb8, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x61, 0x52, 0x11, 0x28}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x67, 0x89, 0x64, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x6f, 0xdc, 0x15, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x83, 0xe4, 0x40, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x95, 0x1c, 0xe0, 0x3}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xa7, 0x9a, 0xc8, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xb1, 0x45, 0xc7, 0x80}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xba, 0xd4, 0x2c, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc1, 0xe3, 0x84, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xcc, 0x1c, 0xe0, 0x0}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xd5, 0x3d, 0x83, 0xb4}, + {0x20, 0x1, 0x0, 0x0, 0x5, 0xb8, 0xd9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x18, 0x81, 0x83, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x24, 0x67, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x2e, 0x5d, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x36, 0x25, 0xed, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x42, 0x80, 0x6b, 0xec, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x45, 0xad, 0x92, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x48, 0x2b, 0x48, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x4c, 0x25, 0xdf, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x51, 0xdf, 0x94, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x58, 0x68, 0xde, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x5e, 0x9c, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x61, 0x6c, 0xc2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x68, 0x1c, 0x5c, 0xbb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x73, 0x46, 0x24, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x89, 0x1a, 0x3e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0x96, 0xde, 0xea, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0xac, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0xb2, 0xbe, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0xbb, 0xd0, 0x62, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0xc3, 0x48, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0xce, 0xa2, 0xe9, 0xbd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0x0, 0x0, 0xd8, 0x31, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x1, 0xd, 0xf6, 0x9a, 0x84, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x17, 0xe9, 0x75, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x18, 0xd5, 0x2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x28, 0x63, 0x28, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x32, 0x3a, 0xe3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x40, 0xc, 0x9e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x43, 0xce, 0x71, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x47, 0x21, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x4a, 0x57, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x4e, 0x69, 0xd9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x55, 0x65, 0x32, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x5b, 0x86, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x60, 0x27, 0xb0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x62, 0xa2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x6c, 0x3e, 0xfb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x7d, 0x80, 0xe7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0x90, 0xca, 0x7e, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0xa1, 0x45, 0x51, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0xae, 0x3f, 0x12, 0x7c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0xb8, 0xa1, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0xbf, 0x2, 0x48, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0xc8, 0xbc, 0x9c, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0xd1, 0xde, 0x15, 0x76, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x20, 0x2, 0xdf, 0xb3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x26, 0x0, 0x1f, 0x13, 0x1, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x26, 0x2, 0xfe, 0xf5, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x26, 0x20, 0x0, 0x1d, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x2a, 0x1, 0x63, 0x82, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x2a, 0x2, 0x26, 0xf7, 0xd6, 0xd0, 0x68, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0x2a, 0x5, 0x41, 0x44, 0x5, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, +} + +// IPToBigInt converts an IP address to a big.Int. +// This is useful for performing mathematical operations on IP addresses, +// especially for IPv6 addresses due to their large size. +func IPToBigInt(ip net.IP) *big.Int { + intValue := big.NewInt(0) + intValue.SetBytes(ip.To16()) + return intValue +} + +// BigIntToIP converts a big.Int back to an IP address. +// It ensures that the resulting IP address has the correct length for IPv6 addresses, +// padding with leading zeros if necessary. +func BigIntToIP(intValue *big.Int) net.IP { + b := intValue.Bytes() + + // Check if the length of the byte slice is already IPv6 length + if len(b) == net.IPv6len { + return net.IP(b) + } + + // If not, pad the byte slice with leading zeros to fit IPv6 length + padded := make([]byte, net.IPv6len) + copy(padded[net.IPv6len-len(b):], b) + + return net.IP(padded) +} diff --git a/ipnet/split_test.go b/ipnet/split_test.go new file mode 100644 index 0000000..c55dac6 --- /dev/null +++ b/ipnet/split_test.go @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2023 shenjunzheng@gmail.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ipnet + +import ( + "math/big" + "net" + "testing" +) + +// TestIPToBigInt tests the conversion of an IP address to a big.Int. +func TestIPToBigInt(t *testing.T) { + testCases := []struct { + name string + ip net.IP + expected *big.Int + }{ + { + name: "IPv4 address", + ip: net.ParseIP("192.0.2.1"), + expected: big.NewInt(0).SetBytes([]byte{0xff, 0xff, 192, 0, 2, 1}), + }, + { + name: "IPv6 address", + ip: net.ParseIP("2001:db8::1"), + expected: big.NewInt(0).SetBytes(net.ParseIP("2001:db8::1")), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := IPToBigInt(tc.ip) + if result.Cmp(tc.expected) != 0 { + t.Errorf("IPToBigInt(%v) = %v, want %v", tc.ip, result, tc.expected) + } + }) + } +} + +// TestBigIntToIP tests the conversion of a big.Int to an IP address. +func TestBigIntToIP(t *testing.T) { + testCases := []struct { + name string + intValue *big.Int + expected net.IP + }{ + { + name: "IPv4 address", + intValue: big.NewInt(0).SetBytes(net.ParseIP("192.0.2.1")), + expected: net.ParseIP("192.0.2.1"), + }, + { + name: "IPv6 address", + intValue: big.NewInt(0).SetBytes(net.ParseIP("2001:db8::1")), + expected: net.ParseIP("2001:db8::1"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := BigIntToIP(tc.intValue) + if !result.Equal(tc.expected) { + t.Errorf("BigIntToIP(%v) = %v, want %v", tc.intValue, result, tc.expected) + } + }) + } +} + +func TestSplitIPNet(t *testing.T) { + testCases := []struct { + name string + start net.IP + end net.IP + num int + expect []net.IP + }{ + { + name: "IPv4 range step 0", + start: net.ParseIP("192.0.2.0"), + end: net.ParseIP("192.0.2.255"), + num: 4, + expect: []net.IP{ + net.ParseIP("192.0.2.0"), + net.ParseIP("192.0.2.85"), + net.ParseIP("192.0.2.170"), + net.ParseIP("192.0.2.255"), + }, + }, + { + name: "IPv4 range step > 1", + start: net.ParseIP("0.0.0.0"), + end: net.ParseIP("255.255.255.255"), + num: 10, + expect: []net.IP{ + net.ParseIP("0.0.0.0"), + net.ParseIP("45.213.238.0"), + net.ParseIP("65.64.182.200"), + net.ParseIP("74.85.97.120"), + net.ParseIP("85.184.194.91"), + net.ParseIP("99.64.87.96"), + net.ParseIP("137.148.164.152"), + net.ParseIP("184.107.61.72"), + net.ParseIP("206.18.212.16"), + net.ParseIP("255.255.255.255"), + }, + }, + { + name: "IPv4 range step [0,1]", + start: net.ParseIP("180.0.0.0"), + end: net.ParseIP("185.0.0.0"), + num: 5, + expect: []net.IP{ + net.ParseIP("180.0.0.0"), + net.ParseIP("180.240.102.0"), + net.ParseIP("181.224.204.0"), + net.ParseIP("183.38.4.164"), + net.ParseIP("185.0.0.0"), + }, + }, + { + name: "IPv6 range step 0", + start: net.ParseIP("2001:db8::"), + end: net.ParseIP("2001:db8::ffff"), + num: 4, + expect: []net.IP{ + net.ParseIP("2001:db8::"), + net.ParseIP("2001:db8::5555"), + net.ParseIP("2001:db8::aaaa"), + net.ParseIP("2001:db8::ffff"), + }, + }, + { + name: "IPv6 range step > 1", + start: net.ParseIP("::"), + end: net.ParseIP("ffff::"), + num: 10, + expect: []net.IP{ + net.ParseIP("::"), + net.ParseIP("::568b:af60"), + net.ParseIP("::ca82:be20"), + net.ParseIP("80.194.168.0"), + net.ParseIP("193.227.132.0"), + net.ParseIP("2001:0:4c25:df20::"), + net.ParseIP("2001:0:bbd0:6200::"), + net.ParseIP("2002:4721:1400::"), + net.ParseIP("2002:ae3f:127c::"), + net.ParseIP("ffff::"), + }, + }, + { + name: "IPv6 range step [0,1]", + start: net.ParseIP("::"), + end: net.ParseIP("::2d01:ffff"), + num: 4, + expect: []net.IP{ + net.ParseIP("::"), + net.ParseIP("::1029:6aaa"), + net.ParseIP("::187a:b97"), + net.ParseIP("::2d01:ffff"), + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := SplitIPNet(tc.start, tc.end, tc.num) + if len(result) != len(tc.expect) { + t.Fatalf("SplitIPNet(%v, %v, %d) returned %d IPs, want %d", tc.start, tc.end, tc.num, len(result), len(tc.expect)) + } + for i := range result { + if !result[i].Equal(tc.expect[i]) { + t.Errorf("SplitIPNet(%v, %v, %d)[%d] = %v, want %v", tc.start, tc.end, tc.num, i, result[i], tc.expect[i]) + } + } + }) + } +} diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index 45def2b..bb21d26 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -37,6 +37,7 @@ var ( // IPio ErrNoDatabaseReaders = errors.New("no database readers provided") + ErrInvalidIPRange = errors.New("invalid IP range") // Operate