给定CIDR,使用Golang计算主机位置相对于网络地址的方法是什么?

huangapple go评论95阅读模式
英文:

Calculate host position relative to network address in given CIDR, using golang?

问题

如何使用golang计算主机IP地址相对于其所属网络地址的位置?

换句话说,如果要按照数字顺序枚举给定子网中的所有地址,需要枚举多少个地址才能遇到给定的地址?这有助于了解在给定地址之前已经分配了多少地址。

// 示例1:
IP - 172.16.7.173  
子网 - 172.16.4.0/22  
预期主机位置 - 941


// 示例2:
IP - 192.168.0.10  
子网 - 192.168.0.0/24  
预期主机位置 - 10
英文:

How can I calculate the position of a host IP address, relative the network address it belongs to, using golang?

In other words, if you were to enumerate in numerical order all the addresses in a given subnet, how many would you enumerate to encounter the given address? This helps understand how many addresses have been assigned in the subnet prior to the given address.

# Example 1:
IP - 172.16.7.173  
Subnet - 172.16.4.0/22  
Expected host position - 941


# Example 2:
IP - 192.168.0.10  
Subnet - 192.168.0.0/24  
Expected host position - 10

答案1

得分: 1

这两个示例提供了冗余的信息。
IP地址、网络地址及其关联的掩码可以编码在同一个CIDR中。例如:

给定这个CIDR:192.168.0.10/24

可以推断出以下所有信息:

网络地址:192.168.0.0
网络掩码:24
IP地址:192.168.0.10

要计算主机IP地址相对于网络地址IP的位置,只需使用标准库解析CIDR。这将返回所有需要将问题简化为算术问题的信息:

func GetHostPosition(cidr string) uint32 {
	ip, net, err := net.ParseCIDR(cidr)
	if err != nil {
		panic("无效的CIDR")
	}
	hostPosition := ip2int(ip.To4()) - ip2int(net.IP)
	return hostPosition
}

func ip2int(ip net.IP) uint32 {
	if len(ip) == 16 {
		panic("无法将IPv6转换为uint32")
	}
	return binary.BigEndian.Uint32(ip)
}
英文:

The two examples provide redundant information.
Both the IP address, the network address and its associated mask can be encoded in the same CIDR. e.g:

Given this CIDR: 192.168.0.10/24

all the following can be inferred:

NetworkAddress: 192.168.0.0
NetworkMask: 24
IP Address: 192.168.0.10

To calculate position of the host IP address, relative to the network address IP, just parse the CIDR using the standard library. This will return all the information need to reduce the question to an arithmetic problem:

func GetHostPosition(cidr string) uint32 {
	ip, net, err := net.ParseCIDR(cidr)
	if err != nil {
		panic("invalid CIDR")
	}
	hostPosition := ip2int(ip.To4()) - ip2int(net.IP)
	return hostPosition
}

func ip2int(ip net.IP) uint32 {
	if len(ip) == 16 {
		panic("cannot convert IPv6 into uint32")
	}
	return binary.BigEndian.Uint32(ip)
}

答案2

得分: 0

这里是给定CIDR的位置。

package main

import (
	"encoding/binary"
	"fmt"
	"net"
)

func main() {
	ip := "172.16.7.173/22"
	ipAddr, ipNet, _ := net.ParseCIDR(ip)

	ipInt := binary.BigEndian.Uint32(ipAddr.To4())

	fmt.Println(ipInt)

	hostmask := net.IPv4(^ipNet.Mask[0], ^ipNet.Mask[1], ^ipNet.Mask[2], ^ipNet.Mask[3])
	fmt.Println(hostmask)

	maskInt := binary.BigEndian.Uint32(hostmask.To4())
	fmt.Println(maskInt)

	fmt.Println(ipInt & maskInt)
	hostPosition := int(ipInt) & int(maskInt)
	fmt.Println(hostPosition)
}

这段代码可以获取给定CIDR的位置。

英文:
package main

import (
	"encoding/binary"
	"fmt"
	"net"
)

func main() {
	ip := "172.16.7.173/22"
	ipAddr, ipNet, _ := net.ParseCIDR(ip)

ipInt := binary.BigEndian.Uint32(ipAddr.To4())

fmt.Println(ipInt)

hostmask := net.IPv4(^ipNet.Mask[0], ^ipNet.Mask[1], ^ipNet.Mask[2], ^ipNet.Mask[3])
fmt.Println(hostmask)

maskInt := binary.BigEndian.Uint32(hostmask.To4())
fmt.Println(maskInt)

fmt.Println(ipInt & maskInt)
hostPosition := int(ipInt) & int(maskInt)
fmt.Println(hostPosition)
}

Here you get the position of given CIDR.

答案3

得分: 0

以下是使用ipaddress-go库来实现的代码示例。免责声明:我是该库的项目经理。这个第一个函数适用于IPv6和IPv4。返回的位置必须是一个大整数,因为IPv6地址是128位的。

func position(subnetStr, ipStr string) *big.Int {
	subnet := ipaddr.NewIPAddressString(subnetStr).GetAddress()
	ip := ipaddr.NewIPAddressString(ipStr).GetAddress()
	return new(big.Int).Sub(ip.GetValue(), subnet.GetValue())
}

如果你只处理32位的IPv4地址,可以使用uint32,如下所示的代码。

func positionIPv4(subnetStr, ipStr string) uint32 {
	subnet := ipaddr.NewIPAddressString(subnetStr).GetAddress().ToIPv4()
	ip := ipaddr.NewIPAddressString(ipStr).GetAddress().ToIPv4()
	return ip.Uint32Value() - subnet.Uint32Value()
}

使用你的示例地址:

package main

import (
	"fmt"
	"math/big"
	"github.com/seancfoley/ipaddress-go/ipaddr"
)

func main() {
	fmt.Println(position("172.16.4.0/22", "172.16.7.173"))
	fmt.Println(positionIPv4("172.16.4.0/22", "172.16.7.173"))
	fmt.Println(position("192.168.0.0/24", "192.168.0.10"))
	fmt.Println(positionIPv4("192.168.0.0/24", "192.168.0.10"))
	fmt.Println(position("2001:db8::1a20/124", "2001:db8::1a2b"))
}

输出结果:

941
941
10
10
11
英文:

Here is some code that shows how to do this using the ipaddress-go library. Disclaimer: I am the project manager of the library. This first function works with both IPv6 and IPv4. The returned position must be a big int since IPv6 addresses are 128 bits.

func position(subnetStr, ipStr string) *big.Int {
	subnet := ipaddr.NewIPAddressString(subnetStr).GetAddress()
	ip := ipaddr.NewIPAddressString(ipStr).GetAddress()
	return new(big.Int).Sub(ip.GetValue(), subnet.GetValue())
}

If you are dealing with IPv4 addresses only, which are 32 bits, you can use uint32 instead, as shown in the following code.

func positionIPv4(subnetStr, ipStr string) uint32 {
	subnet := ipaddr.NewIPAddressString(subnetStr).GetAddress().ToIPv4()
	ip := ipaddr.NewIPAddressString(ipStr).GetAddress().ToIPv4()
	return ip.Uint32Value() - subnet.Uint32Value()
}

Using your example addresses:

package main

import (
	"fmt"
	"math/big"
	"github.com/seancfoley/ipaddress-go/ipaddr"
)

func main() {
	fmt.Println(position("172.16.4.0/22", "172.16.7.173"))
	fmt.Println(positionIPv4("172.16.4.0/22", "172.16.7.173"))
	fmt.Println(position("192.168.0.0/24", "192.168.0.10"))
	fmt.Println(positionIPv4("192.168.0.0/24", "192.168.0.10"))
	fmt.Println(position("2001:db8::1a20/124", "2001:db8::1a2b"))
}

Output:

941
941
10
10
11

huangapple
  • 本文由 发表于 2023年3月27日 16:26:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75853587.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定