mirror of
https://github.com/kubenetworks/kubevpn.git
synced 2026-04-22 23:17:23 +08:00
263 lines
5.1 KiB
Go
263 lines
5.1 KiB
Go
//go:build darwin
|
|
// +build darwin
|
|
|
|
package pkg
|
|
|
|
import (
|
|
log "github.com/sirupsen/logrus"
|
|
"net"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// sudo ifconfig utun3 down
|
|
func disableDevice(conflict []string) error {
|
|
for _, dev := range conflict {
|
|
if err := exec.Command("sudo", "ifconfig", dev, "down").Run(); err != nil {
|
|
log.Errorf("can not disable interface: %s, err: %v", dev, err)
|
|
return err
|
|
} else {
|
|
rollbackFuncList = append(rollbackFuncList, func() {
|
|
_ = exec.Command("sudo", "ifconfig", dev, "up").Run()
|
|
})
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getRouteTable() (map[string][]*net.IPNet, error) {
|
|
output, err := exec.Command("netstat", "-anr").CombinedOutput()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
split := strings.Split(string(output), "\n")
|
|
routeTable := make(map[string][]*net.IPNet)
|
|
for _, i := range split {
|
|
fields := strings.Fields(i)
|
|
if len(fields) >= 4 {
|
|
cidr := fields[0]
|
|
eth := fields[3]
|
|
if _, ipNet, err := parseCIDR(cidr); err == nil {
|
|
if v, ok := routeTable[eth]; ok {
|
|
routeTable[eth] = append(v, ipNet)
|
|
} else {
|
|
routeTable[eth] = []*net.IPNet{ipNet}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return routeTable, nil
|
|
}
|
|
|
|
const big = 0xFFFFFF
|
|
|
|
// Decimal to integer.
|
|
// Returns number, characters consumed, success.
|
|
func dtoi(s string) (n int, i int, ok bool) {
|
|
n = 0
|
|
for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
|
n = n*10 + int(s[i]-'0')
|
|
if n >= big {
|
|
return big, i, false
|
|
}
|
|
}
|
|
if i == 0 {
|
|
return 0, 0, false
|
|
}
|
|
return n, i, true
|
|
}
|
|
|
|
// Hexadecimal to integer.
|
|
// Returns number, characters consumed, success.
|
|
func xtoi(s string) (n int, i int, ok bool) {
|
|
n = 0
|
|
for i = 0; i < len(s); i++ {
|
|
if '0' <= s[i] && s[i] <= '9' {
|
|
n *= 16
|
|
n += int(s[i] - '0')
|
|
} else if 'a' <= s[i] && s[i] <= 'f' {
|
|
n *= 16
|
|
n += int(s[i]-'a') + 10
|
|
} else if 'A' <= s[i] && s[i] <= 'F' {
|
|
n *= 16
|
|
n += int(s[i]-'A') + 10
|
|
} else {
|
|
break
|
|
}
|
|
if n >= big {
|
|
return 0, i, false
|
|
}
|
|
}
|
|
if i == 0 {
|
|
return 0, i, false
|
|
}
|
|
return n, i, true
|
|
}
|
|
|
|
func parseCIDR(s string) (net.IP, *net.IPNet, error) {
|
|
indexByte6 := strings.Count(s, ":")
|
|
indexByte4 := strings.Count(s, ".")
|
|
if indexByte4 < 0 && indexByte6 < 0 {
|
|
return nil, nil, &net.ParseError{Type: "CIDR address", Text: s}
|
|
}
|
|
|
|
i := strings.IndexByte(s, '/')
|
|
var addr, mask string
|
|
if i < 0 {
|
|
addr = s
|
|
// ipv6
|
|
if indexByte6 > 0 {
|
|
mask = strconv.Itoa((indexByte6 + 1) * 8)
|
|
} else {
|
|
mask = strconv.Itoa((indexByte4 + 1) * 8)
|
|
}
|
|
} else {
|
|
addr, mask = s[:i], s[i+1:]
|
|
}
|
|
iplen := net.IPv4len
|
|
ip := parseIPv4(addr)
|
|
if ip == nil {
|
|
iplen = net.IPv6len
|
|
ip = parseIPv6(addr)
|
|
}
|
|
n, i, ok := dtoi(mask)
|
|
if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen {
|
|
return nil, nil, &net.ParseError{Type: "CIDR address", Text: s}
|
|
}
|
|
m := net.CIDRMask(n, 8*iplen)
|
|
return ip, &net.IPNet{IP: ip.Mask(m), Mask: m}, nil
|
|
}
|
|
|
|
// Parse IPv4 address (d.d.d.d).
|
|
func parseIPv4(s string) net.IP {
|
|
var p [net.IPv4len]byte
|
|
for i := 0; i < net.IPv4len; i++ {
|
|
if len(s) == 0 {
|
|
// Missing octets.
|
|
continue
|
|
}
|
|
if i > 0 {
|
|
if s[0] != '.' {
|
|
return nil
|
|
}
|
|
s = s[1:]
|
|
}
|
|
n, c, ok := dtoi(s)
|
|
if !ok || n > 0xFF {
|
|
return nil
|
|
}
|
|
if c > 1 && s[0] == '0' {
|
|
// Reject non-zero components with leading zeroes.
|
|
return nil
|
|
}
|
|
s = s[c:]
|
|
p[i] = byte(n)
|
|
}
|
|
if len(s) != 0 {
|
|
return nil
|
|
}
|
|
return net.IPv4(p[0], p[1], p[2], p[3])
|
|
}
|
|
|
|
func parseIPv6(s string) (ip net.IP) {
|
|
ip = make(net.IP, net.IPv6len)
|
|
ellipsis := -1 // position of ellipsis in ip
|
|
|
|
// Might have leading ellipsis
|
|
if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
|
|
ellipsis = 0
|
|
s = s[2:]
|
|
// Might be only ellipsis
|
|
if len(s) == 0 {
|
|
return ip
|
|
}
|
|
}
|
|
|
|
// Loop, parsing hex numbers followed by colon.
|
|
i := 0
|
|
for i < net.IPv6len {
|
|
// Hex number.
|
|
n, c, ok := xtoi(s)
|
|
if !ok || n > 0xFFFF {
|
|
i++
|
|
continue
|
|
}
|
|
|
|
// If followed by dot, might be in trailing IPv4.
|
|
if c < len(s) && s[c] == '.' {
|
|
if ellipsis < 0 && i != net.IPv6len-net.IPv4len {
|
|
// Not the right place.
|
|
return nil
|
|
}
|
|
if i+net.IPv4len > net.IPv6len {
|
|
// Not enough room.
|
|
return nil
|
|
}
|
|
ip4 := parseIPv4(s)
|
|
if ip4 == nil {
|
|
return nil
|
|
}
|
|
ip[i] = ip4[12]
|
|
ip[i+1] = ip4[13]
|
|
ip[i+2] = ip4[14]
|
|
ip[i+3] = ip4[15]
|
|
s = ""
|
|
i += net.IPv4len
|
|
break
|
|
}
|
|
|
|
// Save this 16-bit chunk.
|
|
ip[i] = byte(n >> 8)
|
|
ip[i+1] = byte(n)
|
|
i += 2
|
|
|
|
// Stop at end of string.
|
|
s = s[c:]
|
|
if len(s) == 0 {
|
|
break
|
|
}
|
|
|
|
// Otherwise must be followed by colon and more.
|
|
if s[0] != ':' || len(s) == 1 {
|
|
return nil
|
|
}
|
|
s = s[1:]
|
|
|
|
// Look for ellipsis.
|
|
if s[0] == ':' {
|
|
if ellipsis >= 0 { // already have one
|
|
return nil
|
|
}
|
|
ellipsis = i
|
|
s = s[1:]
|
|
if len(s) == 0 { // can be at end
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Must have used entire string.
|
|
if len(s) != 0 {
|
|
return nil
|
|
}
|
|
|
|
// If didn't parse enough, expand ellipsis.
|
|
if i < net.IPv6len {
|
|
if ellipsis < 0 {
|
|
return nil
|
|
}
|
|
n := net.IPv6len - i
|
|
for j := i - 1; j >= ellipsis; j-- {
|
|
ip[j+n] = ip[j]
|
|
}
|
|
for j := ellipsis + n - 1; j >= ellipsis; j-- {
|
|
ip[j] = 0
|
|
}
|
|
} else if ellipsis >= 0 {
|
|
// Ellipsis must represent at least one 0 group.
|
|
return nil
|
|
}
|
|
return ip
|
|
}
|