mirror of
https://git.zx2c4.com/wireguard-go
synced 2025-09-18 20:57:50 +02:00
153 lines
3.2 KiB
Go
153 lines
3.2 KiB
Go
package wgcfg
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"net"
|
|
)
|
|
|
|
// IP is an IPv4 or an IPv6 address.
|
|
//
|
|
// Internally the address is always represented in its IPv6 form.
|
|
// IPv4 addresses use the IPv4-in-IPv6 syntax.
|
|
type IP struct {
|
|
Addr [16]byte
|
|
}
|
|
|
|
func (ip IP) String() string { return net.IP(ip.Addr[:]).String() }
|
|
|
|
// IP converts ip into a standard library net.IP.
|
|
func (ip IP) IP() net.IP { return net.IP(ip.Addr[:]) }
|
|
|
|
// Is6 reports whether ip is an IPv6 address.
|
|
func (ip IP) Is6() bool { return !ip.Is4() }
|
|
|
|
// Is4 reports whether ip is an IPv4 address.
|
|
func (ip IP) Is4() bool {
|
|
return ip.Addr[0] == 0 && ip.Addr[1] == 0 &&
|
|
ip.Addr[2] == 0 && ip.Addr[3] == 0 &&
|
|
ip.Addr[4] == 0 && ip.Addr[5] == 0 &&
|
|
ip.Addr[6] == 0 && ip.Addr[7] == 0 &&
|
|
ip.Addr[8] == 0 && ip.Addr[9] == 0 &&
|
|
ip.Addr[10] == 0xff && ip.Addr[11] == 0xff
|
|
}
|
|
|
|
// To4 returns either a 4 byte slice for an IPv4 address, or nil if
|
|
// it's not IPv4.
|
|
func (ip IP) To4() []byte {
|
|
if ip.Is4() {
|
|
return ip.Addr[12:16]
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Equal reports whether ip == x.
|
|
func (ip IP) Equal(x IP) bool {
|
|
return ip == x
|
|
}
|
|
|
|
func (ip IP) MarshalText() ([]byte, error) {
|
|
return []byte(ip.String()), nil
|
|
}
|
|
|
|
func (ip *IP) UnmarshalText(text []byte) error {
|
|
parsedIP, ok := ParseIP(string(text))
|
|
if !ok {
|
|
return fmt.Errorf("wgcfg.IP: UnmarshalText: bad IP address %q", text)
|
|
}
|
|
*ip = parsedIP
|
|
return nil
|
|
}
|
|
|
|
func IPv4(b0, b1, b2, b3 byte) (ip IP) {
|
|
ip.Addr[10], ip.Addr[11] = 0xff, 0xff // IPv4-in-IPv6 prefix
|
|
ip.Addr[12] = b0
|
|
ip.Addr[13] = b1
|
|
ip.Addr[14] = b2
|
|
ip.Addr[15] = b3
|
|
return ip
|
|
}
|
|
|
|
// ParseIP parses the string representation of an address into an IP.
|
|
//
|
|
// It accepts IPv4 notation such as "1.2.3.4" and IPv6 notation like ""::0".
|
|
// The ok result reports whether s was a valid IP and ip is valid.
|
|
func ParseIP(s string) (ip IP, ok bool) {
|
|
netIP := net.ParseIP(s)
|
|
if netIP == nil {
|
|
return IP{}, false
|
|
}
|
|
copy(ip.Addr[:], netIP.To16())
|
|
return ip, true
|
|
}
|
|
|
|
// CIDR is a compact IP address and subnet mask.
|
|
type CIDR struct {
|
|
IP IP
|
|
Mask uint8 // 0-32 for IsIPv4, 4-128 for IsIPv6
|
|
}
|
|
|
|
// ParseCIDR parses CIDR notation into a CIDR type.
|
|
// Typical CIDR strings look like "192.168.1.0/24".
|
|
func ParseCIDR(s string) (CIDR, error) {
|
|
netIP, netAddr, err := net.ParseCIDR(s)
|
|
if err != nil {
|
|
return CIDR{}, err
|
|
}
|
|
var cidr CIDR
|
|
copy(cidr.IP.Addr[:], netIP.To16())
|
|
ones, _ := netAddr.Mask.Size()
|
|
cidr.Mask = uint8(ones)
|
|
|
|
return cidr, nil
|
|
}
|
|
|
|
func (r CIDR) String() string { return r.IPNet().String() }
|
|
|
|
func (r CIDR) IPNet() *net.IPNet {
|
|
bits := 128
|
|
if r.IP.Is4() {
|
|
bits = 32
|
|
}
|
|
return &net.IPNet{IP: r.IP.IP(), Mask: net.CIDRMask(int(r.Mask), bits)}
|
|
}
|
|
|
|
func (r CIDR) Contains(ip IP) bool {
|
|
c := int8(r.Mask)
|
|
i := 0
|
|
if r.IP.Is4() {
|
|
i = 12
|
|
if ip.Is6() {
|
|
return false
|
|
}
|
|
}
|
|
for ; i < 16 && c > 0; i++ {
|
|
var x uint8
|
|
if c < 8 {
|
|
x = 8 - uint8(c)
|
|
}
|
|
m := uint8(math.MaxUint8) >> x << x
|
|
a := r.IP.Addr[i] & m
|
|
b := ip.Addr[i] & m
|
|
if a != b {
|
|
return false
|
|
}
|
|
c -= 8
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (r CIDR) MarshalText() ([]byte, error) {
|
|
return []byte(r.String()), nil
|
|
}
|
|
|
|
func (r *CIDR) UnmarshalText(text []byte) error {
|
|
cidr, err := ParseCIDR(string(text))
|
|
if err != nil {
|
|
return fmt.Errorf("wgcfg.CIDR: UnmarshalText: %v", err)
|
|
}
|
|
*r = cidr
|
|
return nil
|
|
}
|