Files
kubevpn/pkg/util/net.go
naison 6ca22822f9 feat: support restore from local config (#645)
* refactor: optimize dhcp

* feat: support recover from config

* feat: optimize code

* feat: fix bug

* feat: fix bug

* feat: rename
2025-06-14 13:01:24 +08:00

265 lines
6.0 KiB
Go

package util
import (
"context"
"errors"
"fmt"
"math"
"math/rand"
"net"
"os"
"strconv"
"strings"
"time"
"github.com/cilium/ipam/service/allocator"
"github.com/cilium/ipam/service/ipallocator"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/prometheus-community/pro-bing"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
)
func GetTunDevice(ips ...net.IP) (*net.Interface, error) {
interfaces, err := net.Interfaces()
if err != nil {
return nil, err
}
for _, i := range interfaces {
addrList, err := i.Addrs()
if err != nil {
return nil, err
}
for _, addr := range addrList {
if ipNet, ok := addr.(*net.IPNet); ok {
for _, ip := range ips {
if ipNet.IP.Equal(ip) {
return &i, nil
}
}
}
}
}
return nil, fmt.Errorf("can not found any interface with IP %v", ips)
}
func GetTunDeviceByConn(tun net.Conn) (*net.Interface, error) {
var ip net.IP
switch tun.LocalAddr().(type) {
case *net.IPNet:
ip = tun.LocalAddr().(*net.IPNet).IP
case *net.IPAddr:
ip = tun.LocalAddr().(*net.IPAddr).IP
}
return GetTunDevice(ip)
}
func GetTunDeviceIP(tunName string) (net.IP, net.IP, net.IP, error) {
tunIfi, err := net.InterfaceByName(tunName)
if err != nil {
return nil, nil, nil, err
}
addrList, err := tunIfi.Addrs()
if err != nil {
return nil, nil, nil, err
}
var srcIPv4, srcIPv6, dockerSrcIPv4 net.IP
for _, addr := range addrList {
if ipNet, ok := addr.(*net.IPNet); ok {
if config.CIDR.Contains(ipNet.IP) {
srcIPv4 = ipNet.IP
}
if config.CIDR6.Contains(ipNet.IP) {
srcIPv6 = ipNet.IP
}
if config.DockerCIDR.Contains(ipNet.IP) {
dockerSrcIPv4 = ipNet.IP
}
}
}
return srcIPv4, srcIPv6, dockerSrcIPv4, nil
}
func PingOnce(ctx context.Context, srcIP, dstIP string) (bool, error) {
pinger, err := probing.NewPinger(dstIP)
if err != nil {
return false, err
}
pinger.Source = srcIP
pinger.SetLogger(nil)
pinger.SetPrivileged(true)
pinger.Count = 1
pinger.Timeout = time.Second * 1
pinger.ResolveTimeout = time.Second * 1
err = pinger.RunWithContext(ctx) // Blocks until finished.
if err != nil {
return false, err
}
stat := pinger.Statistics()
return stat.PacketsRecv == stat.PacketsSent, err
}
func Ping(ctx context.Context, srcIP, dstIP string) (bool, error) {
pinger, err := probing.NewPinger(dstIP)
if err != nil {
return false, err
}
pinger.Source = srcIP
pinger.SetLogger(nil)
pinger.SetPrivileged(true)
pinger.Count = 4
pinger.Timeout = time.Second * 4
pinger.ResolveTimeout = time.Second * 1
err = pinger.RunWithContext(ctx) // Blocks until finished.
if err != nil {
return false, err
}
stat := pinger.Statistics()
return stat.PacketsRecv == stat.PacketsSent, err
}
func IsIPv4(packet []byte) bool {
return 4 == (packet[0] >> 4)
}
func IsIPv6(packet []byte) bool {
return 6 == (packet[0] >> 4)
}
func ParseIP(packet []byte) (src net.IP, dst net.IP, protocol int, err error) {
if IsIPv4(packet) {
header, err := ipv4.ParseHeader(packet)
if err != nil {
return nil, nil, -1, err
}
return header.Src, header.Dst, header.Protocol, nil
}
if IsIPv6(packet) {
header, err := ipv6.ParseHeader(packet)
if err != nil {
return nil, nil, -1, err
}
return header.Src, header.Dst, header.NextHeader, nil
}
return nil, nil, -1, errors.New("packet is invalid")
}
func GetIPBaseNic() (*net.IPNet, error) {
addrs, _ := net.InterfaceAddrs()
var sum int
for _, addr := range addrs {
for _, b := range getIP(addr) {
sum = sum + int(b)
}
}
dhcp, err := ipallocator.NewAllocatorCIDRRange(config.DockerCIDR, func(max int, rangeSpec string) (allocator.Interface, error) {
return allocator.NewContiguousAllocationMap(max, rangeSpec), nil
})
if err != nil {
return nil, err
}
var next net.IP
for i := 0; i < sum%255; i++ {
next, err = dhcp.AllocateNext()
}
if err != nil {
return nil, err
}
_, bits := config.DockerCIDR.Mask.Size()
return &net.IPNet{IP: next, Mask: net.CIDRMask(bits, bits)}, nil
}
func getIP(addr net.Addr) net.IP {
if addr == nil {
return nil
}
var ip net.IP
switch addr.(type) {
case *net.IPAddr:
ip = addr.(*net.IPAddr).IP
case *net.IPNet:
ip = addr.(*net.IPNet).IP
default:
ip = net.ParseIP(addr.String())
}
return ip
}
func GenICMPPacket(src net.IP, dst net.IP) ([]byte, error) {
buf := gopacket.NewSerializeBuffer()
var id uint16
for _, b := range src {
id += uint16(b)
}
icmpLayer := layers.ICMPv4{
TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoRequest, 0),
Id: id,
Seq: uint16(rand.Intn(math.MaxUint16 + 1)),
}
ipLayer := layers.IPv4{
Version: 4,
SrcIP: src,
DstIP: dst,
Protocol: layers.IPProtocolICMPv4,
Flags: layers.IPv4DontFragment,
TTL: 64,
IHL: 5,
Id: uint16(rand.Intn(math.MaxUint16 + 1)),
}
opts := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
err := gopacket.SerializeLayers(buf, opts, &ipLayer, &icmpLayer)
if err != nil {
return nil, fmt.Errorf("failed to serialize icmp packet, err: %v", err)
}
return buf.Bytes(), nil
}
func GenICMPPacketIPv6(src net.IP, dst net.IP) ([]byte, error) {
buf := gopacket.NewSerializeBuffer()
icmpLayer := layers.ICMPv6{
TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeEchoRequest, 0),
}
ipLayer := layers.IPv6{
Version: 6,
SrcIP: src,
DstIP: dst,
NextHeader: layers.IPProtocolICMPv6,
HopLimit: 255,
}
opts := gopacket.SerializeOptions{
FixLengths: true,
}
err := gopacket.SerializeLayers(buf, opts, &ipLayer, &icmpLayer)
if err != nil {
return nil, fmt.Errorf("failed to serialize icmp6 packet, err: %v", err)
}
return buf.Bytes(), nil
}
func DetectSupportIPv6() (bool, error) {
content, err := os.ReadFile("/proc/sys/net/ipv6/conf/all/disable_ipv6")
if err != nil {
return false, err
}
disableIPv6, err := strconv.Atoi(strings.TrimSpace(string(content)))
if err != nil {
return false, err
}
return disableIPv6 == 0, nil
}
func IsValidCIDR(str string) bool {
_, _, err := net.ParseCIDR(str)
if err != nil {
return false
}
return true
}