mirror of https://github.com/jetkvm/kvm.git
188 lines
4.2 KiB
Go
188 lines
4.2 KiB
Go
package network
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net"
|
|
)
|
|
|
|
var (
|
|
netMask32 = net.IPv4Mask(255, 255, 255, 255)
|
|
)
|
|
|
|
func parseIP(ip string, family int) (net.IP, error) {
|
|
addr := net.ParseIP(ip)
|
|
if addr == nil {
|
|
return nil, fmt.Errorf("failed to parse IP: %s", ip)
|
|
}
|
|
|
|
switch family {
|
|
case AF_INET:
|
|
ia := addr.To4()
|
|
if ia == nil {
|
|
return nil, fmt.Errorf("address is not a valid IPv4 address")
|
|
}
|
|
return ia, nil
|
|
case AF_INET6:
|
|
ia := addr.To16()
|
|
if ia == nil {
|
|
return nil, fmt.Errorf("address is not a valid IPv6 address")
|
|
}
|
|
return ia, nil
|
|
default:
|
|
return nil, fmt.Errorf("invalid family: %d", family)
|
|
}
|
|
}
|
|
|
|
func parseAndValidateUnicastIP(ip string, family int) (net.IP, error) {
|
|
addr, err := parseIP(ip, family)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !addr.IsGlobalUnicast() {
|
|
return nil, fmt.Errorf("address is not a global unicast address")
|
|
}
|
|
|
|
return addr, nil
|
|
}
|
|
|
|
func isValidNetMask(s string) bool {
|
|
ip := net.ParseIP(s)
|
|
if ip == nil {
|
|
return false
|
|
}
|
|
var m net.IPMask
|
|
if v4 := ip.To4(); v4 != nil {
|
|
m = net.IPMask(v4) // 4 bytes
|
|
} else {
|
|
v6 := ip.To16()
|
|
if v6 == nil {
|
|
return false
|
|
}
|
|
m = net.IPMask(v6) // 16 bytes
|
|
}
|
|
ones, bits := m.Size()
|
|
// Non-contiguous masks return (0,0). /0 is valid and returns (0, 32|128).
|
|
return !(ones == 0 && bits == 0)
|
|
}
|
|
|
|
type parsedIPv4Config struct {
|
|
address net.IP
|
|
netmask net.IPMask
|
|
network net.IPNet
|
|
gateway net.IP
|
|
}
|
|
|
|
type parsedIPv6Config struct {
|
|
address net.IP
|
|
prefix net.IPNet
|
|
gateway net.IP
|
|
}
|
|
|
|
func parseAndValidateStaticIPv4Config(config *IPv4StaticConfig) (*parsedIPv4Config, error) {
|
|
addr, err := parseAndValidateUnicastIP(config.Address.String, AF_INET)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse address: %w", err)
|
|
}
|
|
|
|
netmask, err := parseIP(config.Netmask.String, AF_INET)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse netmask: %w", err)
|
|
}
|
|
if !isValidNetMask(config.Netmask.String) {
|
|
return nil, fmt.Errorf("netmask is not a valid netmask")
|
|
}
|
|
|
|
gateway, err := parseAndValidateUnicastIP(config.Gateway.String, AF_INET)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse gateway: %w", err)
|
|
}
|
|
|
|
if addr.Equal(gateway) {
|
|
return nil, fmt.Errorf("address and gateway cannot be the same")
|
|
}
|
|
|
|
netMask := net.IPMask(netmask)
|
|
|
|
ipNet := net.IPNet{
|
|
IP: addr,
|
|
Mask: netMask,
|
|
}
|
|
|
|
if !ipNet.Contains(gateway) && !bytes.Equal(ipNet.Mask, netMask32) {
|
|
return nil, fmt.Errorf("address is not in the same subnet as the gateway")
|
|
}
|
|
|
|
return &parsedIPv4Config{
|
|
address: addr,
|
|
netmask: netMask,
|
|
network: ipNet,
|
|
gateway: gateway,
|
|
}, nil
|
|
}
|
|
|
|
func parseAndValidateStaticIPv6Config(config *IPv6StaticConfig) (*parsedIPv6Config, error) {
|
|
addr, prefix, err := net.ParseCIDR(fmt.Sprintf("%s/%d", config.Address.String, config.PrefixLength.Int64))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse prefix: %w", err)
|
|
}
|
|
|
|
if !addr.IsGlobalUnicast() {
|
|
return nil, fmt.Errorf("address is not a global unicast address")
|
|
}
|
|
|
|
gateway, err := parseIP(config.Gateway.String, AF_INET6)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse gateway: %w", err)
|
|
}
|
|
|
|
if addr.Equal(gateway) {
|
|
return nil, fmt.Errorf("address and gateway cannot be the same")
|
|
}
|
|
|
|
if !prefix.Contains(gateway) && !gateway.IsLinkLocalUnicast() {
|
|
return nil, fmt.Errorf("gateway is not in the same subnet as the address or is a link-local address")
|
|
}
|
|
|
|
return &parsedIPv6Config{
|
|
address: addr,
|
|
prefix: *prefix,
|
|
gateway: gateway,
|
|
}, nil
|
|
}
|
|
|
|
func validateIPv4Config(config *NetworkConfig) error {
|
|
switch config.IPv4Mode.String {
|
|
case "static":
|
|
_, err := parseAndValidateStaticIPv4Config(config.IPv4Static)
|
|
return err
|
|
case "dhcp":
|
|
return nil
|
|
case "disabled":
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("invalid IPv4 mode: %s", config.IPv4Mode.String)
|
|
}
|
|
}
|
|
|
|
func validateIPv6Config(config *NetworkConfig) error {
|
|
switch config.IPv6Mode.String {
|
|
case "static":
|
|
_, err := parseAndValidateStaticIPv6Config(config.IPv6Static)
|
|
return err
|
|
case "slaac":
|
|
return nil
|
|
case "dhcpv6":
|
|
return fmt.Errorf("not implemented")
|
|
case "slaac_and_dhcpv6":
|
|
return fmt.Errorf("not implemented")
|
|
case "link_local":
|
|
return nil
|
|
case "disabled":
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("invalid IPv6 mode: %s", config.IPv6Mode.String)
|
|
}
|
|
}
|