IP範囲をクラスレスアドレッシング(CIDR)に変換してGoに戻る

次のインタビューで、彼らは私に小さなテストタスクを尋ね、Goのオープンポートのネットワークスキャナーを書きました。 原則として、このタスクは難しくありませんが、条件の1つは、IPアドレスとネットワークマスク形式のネットワークアドレスの範囲の両方をパラメーターとして渡すことができることでした:192.168.8.0/21。



このトピックは、おそらくネットワークエンジニアや管理者にとって非常に簡単であり、おそらく退屈ですらあります。 私の目標は、IP範囲をネットマスク(以降CIDRと呼びます)に変換し、CIDRからアドレス範囲に戻すためのアルゴリズムを概説することです。



ちょっとした理論ですが、すでに用語に精通している人は、スキップしてアルゴリズムに直接進んでください。



それで、IPとは何か、誰もが説明し、説明する必要はないと思います。 次に、CIDR(またはネットマスク)とは何ですか。 IP:「192.168.11.10」があると想像してください。 実際、これらはドットで区切られた8ビット値であり、個々の部分はいわゆるオクテットです。 IPは32ビットの数値として表現できることは明らかです。



画像



IPパケットで送信されるのはこの番号です。 192.168.11.0から192.168.11.7までの8つのホストで構成されるサブネットがあるとします(注:192.168.11.0はネットワークインターフェイスのアドレスとして使用できません。このアドレスはサブネットの識別子として使用されるため、実際にはアドレス7になりますが、この例ではまだ関係ありません。)

同じ大規模ネットワーク内に複数のサブネットが存在する可能性があることは明らかであり、サブネット内でパケットを直接送信する必要がありますが、パケットをIP上の別のサブネット192.168.11.22に送信する必要がある場合、このパケットをリダイレクトするネットワークルーターに送信する必要があるとします別のサブネット。 サブネットのすべてのアドレスを送信者ホストに保存することは利益がなく無意味なので、ネットワークマスクを保存するだけで、8ホストの上記の指定されたネットワークの場合は-255.255.255.248になります。 ネットマスクをビットに分解すると、29ユニットと3つのゼロが得られます。



画像



サブネットマスクに「1」と「0」を混在させることはできないため、常にシーケンス「1」が最初に来てから、シーケンス「0」が続きます。 これで、指定されたアドレス192.168.11.0から192.168.11.7の上の範囲は、<IPサブネットアドレス> / <サブネットマスク>として表すことができます。 192.168.11.0/29。 これはクラスレスドメイン間ルーティング(CIDR)であり、このコンパクトな形式ではあらゆる範囲のIPアドレスを想像できます。 適切なサブネット化とルーティングに関しては、専門の文献を参照することは理にかなっており、この記事の目的ではありません-ネットワーク管理者が私を許してくれることを願っています。



したがって、コメント付きのアルゴリズム自体を以下に示します。 216.58.192.12-216.58.192.206の範囲では、1つのサブネットに分割する方法はなく、アルゴリズムによって範囲がすぐに複数のサブネットに分割されることをすぐに言う必要があります。

{

「216.58.192.12/30」、

「216.58.192.16/28」、

「216.58.192.32/27」、

「216.58.192.64/26」、

「216.58.192.128/26」、

「216.58.192.192/29」、

「216.58.192.200/30」、

「216.58.192.204/31」、

「216.58.192.206/32」

}



IPv4範囲をCIDRに変換します
// Convert IPv4 range into CIDR func iPv4RangeToCIDR(ipStart string, ipEnd string) (CIDRs []string, err error) { cidr2mask := []uint32{ 0x00000000, 0x80000000, 0xC0000000, 0xE0000000, 0xF0000000, 0xF8000000, 0xFC000000, 0xFE000000, 0xFF000000, 0xFF800000, 0xFFC00000, 0xFFE00000, 0xFFF00000, 0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000, 0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000, 0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0, 0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE, 0xFFFFFFFF, } //  IP    . ipStartUint32 := iPv4ToUint32(ipStart) ipEndUint32 := iPv4ToUint32(ipEnd) //    ,   . if ipStartUint32 > ipEndUint32 { log.Fatalf("start IP:%s must be less than end IP:%s", ipStart, ipEnd) } for ipEndUint32 >= ipStartUint32 { maxSize := 32 //        IP . for maxSize > 0 { maskedBase := ipStartUint32 & cidr2mask[maxSize - 1] if maskedBase != ipStartUint32 { break } maxSize-- } // ,        IP .   ,  . x := math.Log(float64(ipEndUint32 - ipStartUint32 + 1)) / math.Log(2) maxDiff := 32 - int(math.Floor(x)) if maxSize < maxDiff { maxSize = maxDiff } //  CIDR CIDRs = append(CIDRs, uInt32ToIPv4(ipStartUint32) + "/" + strconv.Itoa(maxSize)) //        . ipStartUint32 += uint32(math.Exp2(float64(32 - maxSize))) } return CIDRs, err }
      
      









次に、逆アルゴリズムの場合、同じ範囲のクラスレスアドレッシングを変換してIPに戻す必要があります。 個人的には、テストタスクでこのアルゴリズムは必要ありませんでしたし、ネットワーク管理者の何人かが実際にどれだけ必要かについてコメントするかもしれませんが、両方を一度に書くことにしました。 アルゴリズムは基本的に単純なので、最初のIPサブネットにその次元を追加する必要があります。



CIDRをIPv4範囲に変換します
 // Convert CIDR to IPv4 range func CIDRRangeToIPv4Range(CIDRs []string) (ipStart string, ipEnd string, err error) { var ip uint32 // ip address var ipS uint32 // Start IP address range var ipE uint32 // End IP address range for _, CIDR := range CIDRs { cidrParts := strings.Split(CIDR, "/") ip = iPv4ToUint32(cidrParts[0]) bits, _ := strconv.ParseUint(cidrParts[1], 10, 32) if ipS == 0 || ipS > ip { ipS = ip } ip = ip | (0xFFFFFFFF >> bits) if ipE < ip { ipE = ip } } ipStart = uInt32ToIPv4(ipS) ipEnd = uInt32ToIPv4(ipE) return ipStart, ipEnd, err }
      
      









注: Go開発者のみ:データを形式(IP、* IPNet、エラー)で返す場合、アルゴリズムの生産性をさらに高めることができますが、普遍性のためにデータを文字列として返します。





ネットワークスキャナーのコードはGitHubにあります。 あなたがアスタリスクを入れた場合、私は感謝しますが、私はそれに値する場合のみ:))



参照:




All Articles