Compare commits

...

28 Commits

Author SHA1 Message Date
naison
fcbe2d64f7 chore: add ut for center install 2025-06-01 18:42:32 +08:00
fengcaiwen
a0c0860051 hotfix: fix center install cause mutate webhook not works 2025-06-01 18:39:02 +08:00
wencaiwulue
e374d6b51d feat: update krew index version to refs/tags/v2.7.12 2025-05-23 11:46:30 +08:00
kubenetworks
9703a12bc2 Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-23 11:46:18 +08:00
naison
f9f52d1001 Merge pull request #612
ut: fix ut
2025-05-23 10:53:37 +08:00
naison
51c16989fe ut: fix ut 2025-05-23 02:52:37 +00:00
naison
75c609211b refactor: use informer to list&watch pod&service ip for adding to route table (#610) 2025-05-23 10:09:06 +08:00
naison
6d545dc5c9 hotfix: remove cidr if contains api-server ip 2025-05-20 22:12:19 +08:00
naison
b17da3cbcb feat: update krew index version to refs/tags/v2.7.11 (#607) 2025-05-18 17:32:49 +08:00
naison
d1108ebd86 Update charts/index.yaml (#608) 2025-05-18 17:32:35 +08:00
naison
792839a2d4 feat: support dump service into hosts in center cluster mode (#605) 2025-05-18 16:20:34 +08:00
fengcaiwen
f493931b41 hotfix: remove job before install 2025-05-18 16:20:13 +08:00
wencaiwulue
7df065ef93 feat: update krew index version to refs/tags/v2.7.10 2025-05-14 21:23:16 +08:00
kubenetworks
c265b3581c Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-14 21:23:03 +08:00
naison
f802e03d01 hotfix: add heartbeat to manager in the pod 2025-05-14 20:22:57 +08:00
wencaiwulue
c08cb461dd feat: update krew index version to refs/tags/v2.7.9 2025-05-12 17:18:47 +08:00
kubenetworks
1a2649a02a Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-12 17:18:34 +08:00
naison
facd6bdb3d hotfix: fix create temp kubeconfig but name container path separator (#599) 2025-05-12 16:28:15 +08:00
naison
a1117dee62 hotfix: handle not found route packet with gVisor instead of drop it 2025-05-12 15:49:40 +08:00
naison
b28eaef6a7 chore(mod): upgrade purego version to v0.8.3 2025-05-12 15:47:48 +08:00
naison
46aebef01f refactor: remove temp kubeconfig before daemon quit 2025-05-12 15:46:21 +08:00
naison
3791f48737 hotfix: fix create temp kubeconfig 2025-05-12 15:32:02 +08:00
wencaiwulue
fc76b70713 feat: update krew index version to refs/tags/v2.7.8 2025-05-11 00:17:22 +08:00
kubenetworks
e990dc1d0f Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-11 00:16:45 +08:00
naison
d636449073 feat: set read/write timeout to 60s for remote tcp conn (#590) 2025-05-10 23:02:31 +08:00
fengcaiwen
e85e1a6c40 refactor: show port-forward log 2025-05-10 18:05:44 +08:00
wencaiwulue
40d09716c4 feat: update krew index version to refs/tags/v2.7.7 2025-05-09 14:44:31 +08:00
kubenetworks
63792172bd Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-09 14:44:17 +08:00
87 changed files with 1572 additions and 2844 deletions

View File

@@ -24,6 +24,8 @@ jobs:
uses: medyagh/setup-minikube@latest
with:
cache: true
cpus: 'max'
memory: 'max'
- name: Kubernetes info
run: |

View File

@@ -41,6 +41,8 @@ jobs:
uses: medyagh/setup-minikube@latest
with:
cache: true
cpus: 'max'
memory: 'max'
- name: Kubernetes info
run: |

View File

@@ -1,6 +1,78 @@
apiVersion: v1
entries:
kubevpn:
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.12
created: "2025-05-23T03:38:02.484001975Z"
description: A Helm chart for KubeVPN
digest: b9e28ceda8bb07b42ec37eb2d6b283496d83645479b2f1f4e921d9c462eeb54e
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.12/kubevpn-2.7.12.tgz
version: 2.7.12
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.11
created: "2025-05-18T09:03:21.60777933Z"
description: A Helm chart for KubeVPN
digest: ee30c2533dff51fa389767e56931583cdfff8c5fca7d6c9698f521c6fc508d42
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.11/kubevpn-2.7.11.tgz
version: 2.7.11
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.10
created: "2025-05-14T13:08:51.09371872Z"
description: A Helm chart for KubeVPN
digest: fd23dd5bf0c3a9343d73276c4997a34027a93c1a88667265d92297630579d165
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.10/kubevpn-2.7.10.tgz
version: 2.7.10
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.9
created: "2025-05-12T09:14:52.66116293Z"
description: A Helm chart for KubeVPN
digest: 56e022017177603290575849553c2e9c19f6a1691288dbd67c32a2fdcbde0834
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.9/kubevpn-2.7.9.tgz
version: 2.7.9
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.8
created: "2025-05-10T15:46:13.342045201Z"
description: A Helm chart for KubeVPN
digest: bfab5a7e4e1e795071a7ce3fd7713b517aa447d967ec58500e5a551564869109
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.8/kubevpn-2.7.8.tgz
version: 2.7.8
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.7
created: "2025-05-09T06:43:01.403047355Z"
description: A Helm chart for KubeVPN
digest: 14b3e7873aa71fa7a380631c83be8df1dfb8d0ccb49eb6746aa4f83e3df934f6
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.7/kubevpn-2.7.7.tgz
version: 2.7.7
- annotations:
app: kubevpn
apiVersion: v2
@@ -503,4 +575,4 @@ entries:
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.2.2/kubevpn-2.2.2.tgz
version: 2.2.2
generated: "2025-05-07T11:46:09.644591701Z"
generated: "2025-05-23T03:38:02.484184006Z"

View File

@@ -31,6 +31,7 @@ func CmdConnect(f cmdutil.Factory) *cobra.Command {
var sshConf = &pkgssh.SshConfig{}
var transferImage, foreground, lite bool
var imagePullSecretName string
var managerNamespace string
cmd := &cobra.Command{
Use: "connect",
Short: i18n.T("Connect to kubernetes cluster network"),
@@ -98,6 +99,7 @@ func CmdConnect(f cmdutil.Factory) *cobra.Command {
Image: config.Image,
ImagePullSecretName: imagePullSecretName,
Level: int32(util.If(config.Debug, log.DebugLevel, log.InfoLevel)),
ManagerNamespace: managerNamespace,
}
// if is foreground, send to sudo daemon server
cli, err := daemon.GetClient(false)
@@ -137,6 +139,7 @@ func CmdConnect(f cmdutil.Factory) *cobra.Command {
handler.AddCommonFlags(cmd.Flags(), &transferImage, &imagePullSecretName, &connect.Engine)
cmd.Flags().BoolVar(&foreground, "foreground", false, "Hang up")
cmd.Flags().BoolVar(&lite, "lite", false, "connect to multiple cluster in lite mode. mode \"lite\": design for only connecting to multiple cluster network. mode \"full\": not only connect to cluster network, it also supports proxy workloads inbound traffic to local PC.")
cmd.Flags().StringVar(&managerNamespace, "manager-namespace", "", "The namespace where the traffic manager is to be found. Only works in cluster mode (install kubevpn server by helm)")
handler.AddExtraRoute(cmd.Flags(), extraRoute)
pkgssh.AddSshFlags(cmd.Flags(), sshConf)

View File

@@ -29,7 +29,7 @@ func CmdDev(f cmdutil.Factory) *cobra.Command {
var sshConf = &pkgssh.SshConfig{}
var transferImage bool
var imagePullSecretName string
var connectNamespace string
var managerNamespace string
cmd := &cobra.Command{
Use: "dev TYPE/NAME [-c CONTAINER] [flags] -- [args...]",
Short: i18n.T("Startup your kubernetes workloads in local Docker container"),
@@ -131,7 +131,7 @@ func CmdDev(f cmdutil.Factory) *cobra.Command {
return err
}
return options.Main(cmd.Context(), sshConf, conf, hostConfig, imagePullSecretName, connectNamespace)
return options.Main(cmd.Context(), sshConf, conf, hostConfig, imagePullSecretName, managerNamespace)
},
}
cmd.Flags().SortFlags = false
@@ -141,7 +141,7 @@ func CmdDev(f cmdutil.Factory) *cobra.Command {
cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc("container", completion.ContainerCompletionFunc(f)))
cmd.Flags().StringVar((*string)(&options.ConnectMode), "connect-mode", string(dev.ConnectModeHost), "Connect to kubernetes network in container or in host, eg: ["+string(dev.ConnectModeContainer)+"|"+string(dev.ConnectModeHost)+"]")
handler.AddCommonFlags(cmd.Flags(), &transferImage, &imagePullSecretName, &options.Engine)
cmd.Flags().StringVarP(&connectNamespace, "connect-namespace", "C", config.DefaultNamespaceKubevpn, "Connect to special namespace which kubevpn server installed by helm in cluster mode")
cmd.Flags().StringVar(&managerNamespace, "manager-namespace", "", "The namespace where the traffic manager is to be found. Only works in cluster mode (install kubevpn server by helm)")
// diy docker options
cmd.Flags().StringVar(&options.DevImage, "dev-image", "", "Use to startup docker container, Default is pod image")

View File

@@ -32,7 +32,7 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command {
var sshConf = &pkgssh.SshConfig{}
var transferImage, foreground bool
var imagePullSecretName string
var connectNamespace string
var managerNamespace string
cmd := &cobra.Command{
Use: "proxy",
Short: i18n.T("Proxy kubernetes workloads inbound traffic into local PC"),
@@ -142,7 +142,7 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command {
ImagePullSecretName: imagePullSecretName,
Level: int32(util.If(config.Debug, log.DebugLevel, log.InfoLevel)),
OriginKubeconfigPath: util.GetKubeConfigPath(f),
ConnectNamespace: connectNamespace,
ManagerNamespace: managerNamespace,
},
)
if err != nil {
@@ -174,7 +174,7 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command {
cmd.Flags().StringArrayVar(&portmap, "portmap", []string{}, "Port map, map container port to local port, format: [tcp/udp]/containerPort:localPort, If not special, localPort will use containerPort. eg: tcp/80:8080 or udp/5000:5001 or 80 or 80:8080")
handler.AddCommonFlags(cmd.Flags(), &transferImage, &imagePullSecretName, &connect.Engine)
cmd.Flags().BoolVar(&foreground, "foreground", false, "foreground hang up")
cmd.Flags().StringVarP(&connectNamespace, "connect-namespace", "C", config.DefaultNamespaceKubevpn, "Connect to special namespace which kubevpn server installed by helm in cluster mode")
cmd.Flags().StringVar(&managerNamespace, "manager-namespace", "", "The namespace where the traffic manager is to be found. Only works in cluster mode (install kubevpn server by helm)")
handler.AddExtraRoute(cmd.Flags(), extraRoute)
pkgssh.AddSshFlags(cmd.Flags(), sshConf)

2
go.mod
View File

@@ -161,7 +161,7 @@ require (
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 // indirect
github.com/ebitengine/purego v0.9.0-alpha.3.0.20250507171635-5047c08daa38 // indirect
github.com/ebitengine/purego v0.8.3 // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect

4
go.sum
View File

@@ -265,8 +265,8 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 h1:8EXxF+tCLqaVk8AOC29zl2mnhQjwyLxxOTuhUazWRsg=
github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4/go.mod h1:I5sHm0Y0T1u5YjlyqC5GVArM7aNZRUYtTjmJ8mPJFds=
github.com/ebitengine/purego v0.9.0-alpha.3.0.20250507171635-5047c08daa38 h1:61WY14WhyU89bEJCjegpt6b8wDNsU+Z1416JGwfEKwI=
github.com/ebitengine/purego v0.9.0-alpha.3.0.20250507171635-5047c08daa38/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc=
github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=

View File

@@ -88,7 +88,11 @@ func (h *gvisorTCPHandler) readFromTCPConnWriteToEndpoint(ctx context.Context, c
h.addToRouteMapTCP(ctx, src, conn)
// inner ip like 198.19.0.100/102/103 connect each other
if config.CIDR.Contains(dst) || config.CIDR6.Contains(dst) {
// for issue 594, sometimes k8s service network CIDR also use CIDR 198.19.151.170
// if we can find dst in route map, just trade packet as inner communicate
// if not find dst in route map, just trade packet as k8s service/pod ip
_, found := h.routeMapTCP.Load(dst.String())
if found && (config.CIDR.Contains(dst) || config.CIDR6.Contains(dst)) {
err = h.handlePacket(ctx, buf, read, src, dst, layers.IPProtocol(ipProtocol).String())
if err != nil {
plog.G(ctx).Errorf("[TCP-GVISOR] Failed to handle packet: %v", err)

View File

@@ -29,7 +29,7 @@ func (h *gvisorUDPHandler) Handle(ctx context.Context, tcpConn net.Conn) {
plog.G(ctx).Errorf("[TUN-UDP] Failed to parse proxy info: %v", err)
return
}
plog.G(ctx).Debugf("[TUN-UDP] LocalPort: %d, LocalAddress: %s, RemotePort: %d, RemoteAddress: %s",
plog.G(ctx).Infof("[TUN-UDP] LocalPort: %d, LocalAddress: %s, RemotePort: %d, RemoteAddress: %s",
endpointID.LocalPort, endpointID.LocalAddress.String(), endpointID.RemotePort, endpointID.RemoteAddress.String(),
)
// 2, dial proxy

View File

@@ -26,7 +26,7 @@ func (h *tunHandler) HandleClient(ctx context.Context, tun net.Conn) {
go device.handlePacket(ctx, h.forward)
go device.readFromTun(ctx)
go device.writeToTun(ctx)
go heartbeats(ctx, device.tun)
go device.heartbeats(ctx)
select {
case <-device.errChan:
case <-ctx.Done():
@@ -73,7 +73,13 @@ func handlePacketClient(ctx context.Context, tunInbound <-chan *Packet, tunOutbo
go func() {
defer util.HandleCrash()
for packet := range tunInbound {
_, err := conn.Write(packet.data[:packet.length])
err := conn.SetWriteDeadline(time.Now().Add(config.KeepAliveTime))
if err != nil {
plog.G(ctx).Errorf("Failed to set write deadline: %v", err)
util.SafeWrite(errChan, errors.Wrap(err, "failed to set write deadline"))
return
}
_, err = conn.Write(packet.data[:packet.length])
config.LPool.Put(packet.data[:])
if err != nil {
plog.G(ctx).Errorf("Failed to write packet to remote: %v", err)
@@ -87,6 +93,12 @@ func handlePacketClient(ctx context.Context, tunInbound <-chan *Packet, tunOutbo
defer util.HandleCrash()
for {
buf := config.LPool.Get().([]byte)[:]
err := conn.SetReadDeadline(time.Now().Add(config.KeepAliveTime))
if err != nil {
plog.G(ctx).Errorf("Failed to set read deadline: %v", err)
util.SafeWrite(errChan, errors.Wrap(err, "failed to set read deadline"))
return
}
n, err := conn.Read(buf[:])
if err != nil {
config.LPool.Put(buf[:])
@@ -168,8 +180,8 @@ func (d *ClientDevice) Close() {
util.SafeClose(d.tunOutbound)
}
func heartbeats(ctx context.Context, tun net.Conn) {
tunIfi, err := util.GetTunDeviceByConn(tun)
func (d *ClientDevice) heartbeats(ctx context.Context) {
tunIfi, err := util.GetTunDeviceByConn(d.tun)
if err != nil {
plog.G(ctx).Errorf("Failed to get tun device: %v", err)
return

View File

@@ -25,10 +25,11 @@ func (svr *Server) ConnectFork(req *rpc.ConnectRequest, resp rpc.Daemon_ConnectF
ctx := resp.Context()
connect := &handler.ConnectOptions{
Namespace: req.Namespace,
Namespace: req.ManagerNamespace,
ExtraRouteInfo: *handler.ParseExtraRouteFromRPC(req.ExtraRoute),
Engine: config.Engine(req.Engine),
OriginKubeconfigPath: req.OriginKubeconfigPath,
OriginNamespace: req.Namespace,
Lock: &svr.Lock,
ImagePullSecretName: req.ImagePullSecretName,
}
@@ -51,7 +52,7 @@ func (svr *Server) ConnectFork(req *rpc.ConnectRequest, resp rpc.Daemon_ConnectF
}
}()
err = connect.InitClient(util.InitFactoryByPath(file, req.Namespace))
err = connect.InitClient(util.InitFactoryByPath(file, req.ManagerNamespace))
if err != nil {
return err
}
@@ -94,6 +95,7 @@ func (svr *Server) redirectConnectForkToSudoDaemon(req *rpc.ConnectRequest, resp
defer plog.WithoutLogger(sshCtx)
connect := &handler.ConnectOptions{
Namespace: req.Namespace,
OriginNamespace: req.Namespace,
ExtraRouteInfo: *handler.ParseExtraRouteFromRPC(req.ExtraRoute),
Engine: config.Engine(req.Engine),
OriginKubeconfigPath: req.OriginKubeconfigPath,
@@ -123,16 +125,18 @@ func (svr *Server) redirectConnectForkToSudoDaemon(req *rpc.ConnectRequest, resp
return err
}
connectNs, err := util.DetectConnectNamespace(sshCtx, connect.GetFactory(), req.Namespace)
if err != nil {
return err
if req.ManagerNamespace == "" {
req.ManagerNamespace, err = util.DetectManagerNamespace(plog.WithLogger(sshCtx, logger), connect.GetFactory(), req.Namespace)
if err != nil {
return err
}
}
if connectNs != "" {
logger.Infof("Use connect namespace %s", connectNs)
connect.Namespace = connectNs
req.Namespace = connectNs
if req.ManagerNamespace != "" {
logger.Infof("Use manager namespace %s", req.ManagerNamespace)
connect.Namespace = req.ManagerNamespace
} else {
logger.Infof("Use special namespace %s", req.Namespace)
req.ManagerNamespace = req.Namespace
}
for _, options := range svr.secondaryConnect {

View File

@@ -28,9 +28,7 @@ func (svr *Server) Connect(req *rpc.ConnectRequest, resp rpc.Daemon_ConnectServe
ctx := resp.Context()
if !svr.t.IsZero() {
s := "Already connected to cluster in full mode, you can use options `--lite` to connect to another cluster"
logger.Debugf(s)
// todo define already connect error?
s := "Only support one cluster connect with full mode, you can use options `--lite` to connect to another cluster"
return status.Error(codes.AlreadyExists, s)
}
defer func() {
@@ -44,10 +42,11 @@ func (svr *Server) Connect(req *rpc.ConnectRequest, resp rpc.Daemon_ConnectServe
}()
svr.t = time.Now()
svr.connect = &handler.ConnectOptions{
Namespace: req.Namespace,
Namespace: req.ManagerNamespace,
ExtraRouteInfo: *handler.ParseExtraRouteFromRPC(req.ExtraRoute),
Engine: config.Engine(req.Engine),
OriginKubeconfigPath: req.OriginKubeconfigPath,
OriginNamespace: req.Namespace,
Lock: &svr.Lock,
ImagePullSecretName: req.ImagePullSecretName,
}
@@ -72,7 +71,7 @@ func (svr *Server) Connect(req *rpc.ConnectRequest, resp rpc.Daemon_ConnectServe
sshCancel()
}
}()
err = svr.connect.InitClient(util.InitFactoryByPath(file, req.Namespace))
err = svr.connect.InitClient(util.InitFactoryByPath(file, req.ManagerNamespace))
if err != nil {
return err
}
@@ -110,6 +109,7 @@ func (svr *Server) redirectToSudoDaemon(req *rpc.ConnectRequest, resp rpc.Daemon
defer plog.WithoutLogger(sshCtx)
connect := &handler.ConnectOptions{
Namespace: req.Namespace,
OriginNamespace: req.Namespace,
ExtraRouteInfo: *handler.ParseExtraRouteFromRPC(req.ExtraRoute),
Engine: config.Engine(req.Engine),
OriginKubeconfigPath: req.OriginKubeconfigPath,
@@ -140,16 +140,18 @@ func (svr *Server) redirectToSudoDaemon(req *rpc.ConnectRequest, resp rpc.Daemon
return err
}
connectNs, err := util.DetectConnectNamespace(plog.WithLogger(sshCtx, logger), connect.GetFactory(), req.Namespace)
if err != nil {
return err
if req.ManagerNamespace == "" {
req.ManagerNamespace, err = util.DetectManagerNamespace(plog.WithLogger(sshCtx, logger), connect.GetFactory(), req.Namespace)
if err != nil {
return err
}
}
if connectNs != "" {
logger.Infof("Use connect namespace %s", connectNs)
connect.Namespace = connectNs
req.Namespace = connectNs
if req.ManagerNamespace != "" {
logger.Infof("Use manager namespace %s", req.ManagerNamespace)
connect.Namespace = req.ManagerNamespace
} else {
logger.Infof("Use special namespace %s", req.Namespace)
req.ManagerNamespace = req.Namespace
}
if svr.connect != nil {
@@ -165,6 +167,9 @@ func (svr *Server) redirectToSudoDaemon(req *rpc.ConnectRequest, resp rpc.Daemon
// same cluster, do nothing
logger.Infof("Connected to cluster")
return nil
} else {
s := "Only support one cluster connect with full mode, you can use options `--lite` to connect to another cluster"
return status.Error(codes.AlreadyExists, s)
}
}

View File

@@ -159,7 +159,7 @@ func disconnect(ctx context.Context, svr *Server, connect *handler.ConnectOption
if svr.connect != nil {
isSameCluster, _ := util.IsSameCluster(
ctx,
svr.connect.GetClientset().CoreV1(), svr.connect.Namespace,
svr.connect.GetClientset().CoreV1(), svr.connect.OriginNamespace,
connect.GetClientset().CoreV1(), connect.Namespace,
)
if isSameCluster {
@@ -173,7 +173,7 @@ func disconnect(ctx context.Context, svr *Server, connect *handler.ConnectOption
options := svr.secondaryConnect[i]
isSameCluster, _ := util.IsSameCluster(
ctx,
options.GetClientset().CoreV1(), options.Namespace,
options.GetClientset().CoreV1(), options.OriginNamespace,
connect.GetClientset().CoreV1(), connect.Namespace,
)
if isSameCluster {

View File

@@ -7,6 +7,8 @@ import (
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/pflag"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"k8s.io/utils/ptr"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
@@ -45,7 +47,7 @@ func (svr *Server) Proxy(req *rpc.ProxyRequest, resp rpc.Daemon_ProxyServer) (e
return err
}
connect := &handler.ConnectOptions{
Namespace: req.ConnectNamespace,
Namespace: req.Namespace,
ExtraRouteInfo: *handler.ParseExtraRouteFromRPC(req.ExtraRoute),
Engine: config.Engine(req.Engine),
OriginKubeconfigPath: req.OriginKubeconfigPath,
@@ -72,48 +74,33 @@ func (svr *Server) Proxy(req *rpc.ProxyRequest, resp rpc.Daemon_ProxyServer) (e
return errors.Wrap(err, "daemon is not available")
}
connectNs, err := util.DetectConnectNamespace(ctx, connect.GetFactory(), req.ConnectNamespace)
plog.G(ctx).Debugf("Connecting to cluster")
var connResp rpc.Daemon_ConnectClient
connResp, err = cli.Connect(ctx, convert(req))
if err != nil {
return err
}
if connectNs != "" {
connect.Namespace = connectNs
}
if svr.connect != nil {
isSameCluster, _ := util.IsSameCluster(
ctx,
svr.connect.GetClientset().CoreV1(), svr.connect.Namespace,
connect.GetClientset().CoreV1(), connect.Namespace,
)
if isSameCluster {
// same cluster, do nothing
plog.G(ctx).Infof("Connected to cluster")
} else {
plog.G(ctx).Infof("Disconnecting from another cluster...")
var disconnectResp rpc.Daemon_DisconnectClient
disconnectResp, err = cli.Disconnect(ctx, &rpc.DisconnectRequest{
ID: ptr.To[int32](0),
})
if err != nil {
return err
}
err = util.CopyAndConvertGRPCStream[rpc.DisconnectResponse, rpc.ConnectResponse](
disconnectResp,
resp,
func(response *rpc.DisconnectResponse) *rpc.ConnectResponse {
return &rpc.ConnectResponse{Message: response.Message}
},
)
if err != nil {
return err
}
err = util.CopyGRPCStream[rpc.ConnectResponse](connResp, resp)
if err != nil {
if status.Code(err) != codes.AlreadyExists {
return err
}
plog.G(ctx).Infof("Disconnecting from another cluster...")
var disconnectResp rpc.Daemon_DisconnectClient
disconnectResp, err = cli.Disconnect(ctx, &rpc.DisconnectRequest{ID: ptr.To[int32](0)})
if err != nil {
return err
}
err = util.CopyAndConvertGRPCStream[rpc.DisconnectResponse, rpc.ConnectResponse](
disconnectResp,
resp,
func(response *rpc.DisconnectResponse) *rpc.ConnectResponse {
return &rpc.ConnectResponse{Message: response.Message}
},
)
if err != nil {
return err
}
}
if svr.connect == nil {
plog.G(ctx).Debugf("Connectting to cluster")
var connResp rpc.Daemon_ConnectClient
connResp, err = cli.Connect(ctx, convert(req))
if err != nil {
return err
@@ -160,5 +147,6 @@ func convert(req *rpc.ProxyRequest) *rpc.ConnectRequest {
Foreground: req.Foreground,
Level: req.Level,
OriginKubeconfigPath: req.OriginKubeconfigPath,
ManagerNamespace: req.ManagerNamespace,
}
}

View File

@@ -73,7 +73,7 @@ func genStatus(connect *handler.ConnectOptions, mode string, index int32) *rpc.S
Cluster: util.GetKubeconfigCluster(connect.GetFactory()),
Mode: mode,
Kubeconfig: connect.OriginKubeconfigPath,
Namespace: connect.Namespace,
Namespace: connect.OriginNamespace,
Status: status,
Netif: tunName,
}
@@ -136,7 +136,7 @@ func gen(ctx context.Context, connect *handler.ConnectOptions, clone *handler.Cl
clusterID = connect.GetClusterID()
cluster = util.GetKubeconfigCluster(connect.GetFactory())
kubeconfig = connect.OriginKubeconfigPath
namespace = connect.Namespace
namespace = connect.OriginNamespace
}
cloneList = append(cloneList, &rpc.Clone{
ClusterID: clusterID,

File diff suppressed because it is too large Load Diff

View File

@@ -55,6 +55,14 @@ message ConnectRequest {
int32 Level = 10;
string OriginKubeconfigPath = 11;
// The namespace where the traffic manager is to be found. Only works in cluster mode (install kubevpn server by helm)
// reason: user helm install kubevpn server, and all users use this one kubevpn-traffic-manager instance
// but if normal user don't have list namespace permission(for: helm list -A), needs to special this options.
// 1. default is namespace kubevpn if kubevpn namespace is exists
// 2. detect which namespace helm installed by (helm list -A), match app name==kubevpn and status is deployed
// 3. otherwise, use options '-n' or '--namespace'
string ManagerNamespace = 12;
}
message ConnectResponse {
@@ -106,12 +114,13 @@ message ProxyRequest {
string OriginKubeconfigPath = 15;
// connect to which namespace, reason: user helm install kubevpn server, and all users use this one instance
// The namespace where the traffic manager is to be found. Only works in cluster mode (install kubevpn server by helm)
// reason: user helm install kubevpn server, and all users use this one kubevpn-traffic-manager instance
// but if normal user don't have list namespace permission(for: helm list -A), needs to special this options.
// 1. default is namespace kubevpn if kubevpn namespace is exists
// 2. detect which namespace helm installed by (helm list -A), match app name==kubevpn and status is deployed
// 3. otherwise, use options '-n' or '--namespace'
string ConnectNamespace = 16;
string ManagerNamespace = 16;
}
message ProxyResponse {

View File

@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v3.21.2
// - protoc v5.29.3
// source: daemon.proto
package rpc

View File

@@ -63,7 +63,7 @@ type Options struct {
rollbackFuncList []func() error
}
func (option *Options) Main(ctx context.Context, sshConfig *pkgssh.SshConfig, config *Config, hostConfig *HostConfig, imagePullSecretName string, connectNamespace string) error {
func (option *Options) Main(ctx context.Context, sshConfig *pkgssh.SshConfig, config *Config, hostConfig *HostConfig, imagePullSecretName string, managerNamespace string) error {
mode := typescontainer.NetworkMode(option.ContainerOptions.netMode.NetworkMode())
if mode.IsContainer() {
plog.G(ctx).Infof("Network mode container is %s", mode.ConnectedContainer())
@@ -80,7 +80,7 @@ func (option *Options) Main(ctx context.Context, sshConfig *pkgssh.SshConfig, co
}
// Connect to cluster, in container or host
err := option.Connect(ctx, sshConfig, imagePullSecretName, hostConfig.PortBindings, connectNamespace)
err := option.Connect(ctx, sshConfig, imagePullSecretName, hostConfig.PortBindings, managerNamespace)
if err != nil {
plog.G(ctx).Errorf("Connect to cluster failed, err: %v", err)
return err
@@ -90,7 +90,7 @@ func (option *Options) Main(ctx context.Context, sshConfig *pkgssh.SshConfig, co
}
// Connect to cluster network on docker container or host
func (option *Options) Connect(ctx context.Context, sshConfig *pkgssh.SshConfig, imagePullSecretName string, portBindings nat.PortMap, connectNamespace string) error {
func (option *Options) Connect(ctx context.Context, sshConfig *pkgssh.SshConfig, imagePullSecretName string, portBindings nat.PortMap, managerNamespace string) error {
if option.ConnectMode == ConnectModeHost {
cli, err := daemon.GetClient(false)
if err != nil {
@@ -119,7 +119,7 @@ func (option *Options) Connect(ctx context.Context, sshConfig *pkgssh.SshConfig,
ImagePullSecretName: imagePullSecretName,
Level: int32(util.If(config.Debug, log.DebugLevel, log.InfoLevel)),
SshJump: sshConfig.ToRPC(),
ConnectNamespace: connectNamespace,
ManagerNamespace: managerNamespace,
}
option.AddRollbackFunc(func() error {
resp, err := cli.Disconnect(ctx, &rpc.DisconnectRequest{
@@ -144,7 +144,7 @@ func (option *Options) Connect(ctx context.Context, sshConfig *pkgssh.SshConfig,
}
if option.ConnectMode == ConnectModeContainer {
name, err := option.CreateConnectContainer(ctx, portBindings, connectNamespace)
name, err := option.CreateConnectContainer(ctx, portBindings, managerNamespace)
if err != nil {
return err
}
@@ -227,7 +227,7 @@ func (option *Options) Dev(ctx context.Context, config *Config, hostConfig *Host
return configList.Run(ctx)
}
func (option *Options) CreateConnectContainer(ctx context.Context, portBindings nat.PortMap, connectNamespace string) (*string, error) {
func (option *Options) CreateConnectContainer(ctx context.Context, portBindings nat.PortMap, managerNamespace string) (*string, error) {
portMap, portSet, err := option.GetExposePort(portBindings)
if err != nil {
return nil, err
@@ -264,7 +264,7 @@ func (option *Options) CreateConnectContainer(ctx context.Context, portBindings
"--kubeconfig", "/root/.kube/config",
"--image", config.Image,
"--netstack", string(option.Engine),
"--connect-namespace", connectNamespace,
"--manager-namespace", managerNamespace,
}
for k, v := range option.Headers {
entrypoint = append(entrypoint, "--headers", fmt.Sprintf("%s=%s", k, v))

View File

@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc v3.21.2
// protoc-gen-go v1.36.6
// protoc v5.29.3
// source: dhcpserver.proto
package rpc
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -21,21 +22,18 @@ const (
)
type RentIPRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
PodName string `protobuf:"bytes,1,opt,name=PodName,proto3" json:"PodName,omitempty"`
PodNamespace string `protobuf:"bytes,2,opt,name=PodNamespace,proto3" json:"PodNamespace,omitempty"`
unknownFields protoimpl.UnknownFields
PodName string `protobuf:"bytes,1,opt,name=PodName,proto3" json:"PodName,omitempty"`
PodNamespace string `protobuf:"bytes,2,opt,name=PodNamespace,proto3" json:"PodNamespace,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *RentIPRequest) Reset() {
*x = RentIPRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_dhcpserver_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
mi := &file_dhcpserver_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RentIPRequest) String() string {
@@ -46,7 +44,7 @@ func (*RentIPRequest) ProtoMessage() {}
func (x *RentIPRequest) ProtoReflect() protoreflect.Message {
mi := &file_dhcpserver_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -76,21 +74,18 @@ func (x *RentIPRequest) GetPodNamespace() string {
}
type RentIPResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
IPv4CIDR string `protobuf:"bytes,1,opt,name=IPv4CIDR,proto3" json:"IPv4CIDR,omitempty"`
IPv6CIDR string `protobuf:"bytes,2,opt,name=IPv6CIDR,proto3" json:"IPv6CIDR,omitempty"`
unknownFields protoimpl.UnknownFields
IPv4CIDR string `protobuf:"bytes,1,opt,name=IPv4CIDR,proto3" json:"IPv4CIDR,omitempty"`
IPv6CIDR string `protobuf:"bytes,2,opt,name=IPv6CIDR,proto3" json:"IPv6CIDR,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *RentIPResponse) Reset() {
*x = RentIPResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_dhcpserver_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
mi := &file_dhcpserver_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RentIPResponse) String() string {
@@ -101,7 +96,7 @@ func (*RentIPResponse) ProtoMessage() {}
func (x *RentIPResponse) ProtoReflect() protoreflect.Message {
mi := &file_dhcpserver_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -131,23 +126,20 @@ func (x *RentIPResponse) GetIPv6CIDR() string {
}
type ReleaseIPRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
PodName string `protobuf:"bytes,1,opt,name=PodName,proto3" json:"PodName,omitempty"`
PodNamespace string `protobuf:"bytes,2,opt,name=PodNamespace,proto3" json:"PodNamespace,omitempty"`
IPv4CIDR string `protobuf:"bytes,3,opt,name=IPv4CIDR,proto3" json:"IPv4CIDR,omitempty"`
IPv6CIDR string `protobuf:"bytes,4,opt,name=IPv6CIDR,proto3" json:"IPv6CIDR,omitempty"`
unknownFields protoimpl.UnknownFields
PodName string `protobuf:"bytes,1,opt,name=PodName,proto3" json:"PodName,omitempty"`
PodNamespace string `protobuf:"bytes,2,opt,name=PodNamespace,proto3" json:"PodNamespace,omitempty"`
IPv4CIDR string `protobuf:"bytes,3,opt,name=IPv4CIDR,proto3" json:"IPv4CIDR,omitempty"`
IPv6CIDR string `protobuf:"bytes,4,opt,name=IPv6CIDR,proto3" json:"IPv6CIDR,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *ReleaseIPRequest) Reset() {
*x = ReleaseIPRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_dhcpserver_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
mi := &file_dhcpserver_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ReleaseIPRequest) String() string {
@@ -158,7 +150,7 @@ func (*ReleaseIPRequest) ProtoMessage() {}
func (x *ReleaseIPRequest) ProtoReflect() protoreflect.Message {
mi := &file_dhcpserver_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -202,20 +194,17 @@ func (x *ReleaseIPRequest) GetIPv6CIDR() string {
}
type ReleaseIPResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *ReleaseIPResponse) Reset() {
*x = ReleaseIPResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_dhcpserver_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
mi := &file_dhcpserver_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ReleaseIPResponse) String() string {
@@ -226,7 +215,7 @@ func (*ReleaseIPResponse) ProtoMessage() {}
func (x *ReleaseIPResponse) ProtoReflect() protoreflect.Message {
mi := &file_dhcpserver_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -250,55 +239,40 @@ func (x *ReleaseIPResponse) GetMessage() string {
var File_dhcpserver_proto protoreflect.FileDescriptor
var file_dhcpserver_proto_rawDesc = []byte{
0x0a, 0x10, 0x64, 0x68, 0x63, 0x70, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x03, 0x72, 0x70, 0x63, 0x22, 0x4d, 0x0a, 0x0d, 0x52, 0x65, 0x6e, 0x74, 0x49,
0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x6f, 0x64, 0x4e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x50, 0x6f, 0x64, 0x4e, 0x61,
0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61,
0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d,
0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x48, 0x0a, 0x0e, 0x52, 0x65, 0x6e, 0x74, 0x49, 0x50,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76, 0x34,
0x43, 0x49, 0x44, 0x52, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x49, 0x50, 0x76, 0x34,
0x43, 0x49, 0x44, 0x52, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76, 0x36, 0x43, 0x49, 0x44, 0x52,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x49, 0x50, 0x76, 0x36, 0x43, 0x49, 0x44, 0x52,
0x22, 0x88, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x50, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12,
0x22, 0x0a, 0x0c, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70,
0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76, 0x34, 0x43, 0x49, 0x44, 0x52, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x49, 0x50, 0x76, 0x34, 0x43, 0x49, 0x44, 0x52, 0x12,
0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76, 0x36, 0x43, 0x49, 0x44, 0x52, 0x18, 0x04, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x49, 0x50, 0x76, 0x36, 0x43, 0x49, 0x44, 0x52, 0x22, 0x2d, 0x0a, 0x11, 0x52,
0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x79, 0x0a, 0x04, 0x44, 0x48,
0x43, 0x50, 0x12, 0x33, 0x0a, 0x06, 0x52, 0x65, 0x6e, 0x74, 0x49, 0x50, 0x12, 0x12, 0x2e, 0x72,
0x70, 0x63, 0x2e, 0x52, 0x65, 0x6e, 0x74, 0x49, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x13, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6e, 0x74, 0x49, 0x50, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x09, 0x52, 0x65, 0x6c, 0x65, 0x61,
0x73, 0x65, 0x49, 0x50, 0x12, 0x15, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61,
0x73, 0x65, 0x49, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x70,
0x63, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x3b, 0x72, 0x70, 0x63, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_dhcpserver_proto_rawDesc = "" +
"\n" +
"\x10dhcpserver.proto\x12\x03rpc\"M\n" +
"\rRentIPRequest\x12\x18\n" +
"\aPodName\x18\x01 \x01(\tR\aPodName\x12\"\n" +
"\fPodNamespace\x18\x02 \x01(\tR\fPodNamespace\"H\n" +
"\x0eRentIPResponse\x12\x1a\n" +
"\bIPv4CIDR\x18\x01 \x01(\tR\bIPv4CIDR\x12\x1a\n" +
"\bIPv6CIDR\x18\x02 \x01(\tR\bIPv6CIDR\"\x88\x01\n" +
"\x10ReleaseIPRequest\x12\x18\n" +
"\aPodName\x18\x01 \x01(\tR\aPodName\x12\"\n" +
"\fPodNamespace\x18\x02 \x01(\tR\fPodNamespace\x12\x1a\n" +
"\bIPv4CIDR\x18\x03 \x01(\tR\bIPv4CIDR\x12\x1a\n" +
"\bIPv6CIDR\x18\x04 \x01(\tR\bIPv6CIDR\"-\n" +
"\x11ReleaseIPResponse\x12\x18\n" +
"\amessage\x18\x01 \x01(\tR\amessage2y\n" +
"\x04DHCP\x123\n" +
"\x06RentIP\x12\x12.rpc.RentIPRequest\x1a\x13.rpc.RentIPResponse\"\x00\x12<\n" +
"\tReleaseIP\x12\x15.rpc.ReleaseIPRequest\x1a\x16.rpc.ReleaseIPResponse\"\x00B\aZ\x05.;rpcb\x06proto3"
var (
file_dhcpserver_proto_rawDescOnce sync.Once
file_dhcpserver_proto_rawDescData = file_dhcpserver_proto_rawDesc
file_dhcpserver_proto_rawDescData []byte
)
func file_dhcpserver_proto_rawDescGZIP() []byte {
file_dhcpserver_proto_rawDescOnce.Do(func() {
file_dhcpserver_proto_rawDescData = protoimpl.X.CompressGZIP(file_dhcpserver_proto_rawDescData)
file_dhcpserver_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_dhcpserver_proto_rawDesc), len(file_dhcpserver_proto_rawDesc)))
})
return file_dhcpserver_proto_rawDescData
}
var file_dhcpserver_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_dhcpserver_proto_goTypes = []interface{}{
var file_dhcpserver_proto_goTypes = []any{
(*RentIPRequest)(nil), // 0: rpc.RentIPRequest
(*RentIPResponse)(nil), // 1: rpc.RentIPResponse
(*ReleaseIPRequest)(nil), // 2: rpc.ReleaseIPRequest
@@ -321,61 +295,11 @@ func file_dhcpserver_proto_init() {
if File_dhcpserver_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_dhcpserver_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RentIPRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_dhcpserver_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RentIPResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_dhcpserver_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReleaseIPRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_dhcpserver_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReleaseIPResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_dhcpserver_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_dhcpserver_proto_rawDesc), len(file_dhcpserver_proto_rawDesc)),
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
@@ -386,7 +310,6 @@ func file_dhcpserver_proto_init() {
MessageInfos: file_dhcpserver_proto_msgTypes,
}.Build()
File_dhcpserver_proto = out.File
file_dhcpserver_proto_rawDesc = nil
file_dhcpserver_proto_goTypes = nil
file_dhcpserver_proto_depIdxs = nil
}

View File

@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v3.21.2
// - protoc v5.29.3
// source: dhcpserver.proto
package rpc

View File

@@ -17,12 +17,8 @@ import (
miekgdns "github.com/miekg/dns"
"github.com/pkg/errors"
v12 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/watch"
v13 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/cache"
"tailscale.com/net/dns"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
@@ -31,10 +27,11 @@ import (
)
type Config struct {
Config *miekgdns.ClientConfig
Ns []string
Services []v12.Service
TunName string
Config *miekgdns.ClientConfig
Ns []string
Services []v12.Service
SvcInformer cache.SharedIndexInformer
TunName string
Hosts []Entry
Lock *sync.Mutex
@@ -45,22 +42,8 @@ type Config struct {
OSConfigurator dns.OSConfigurator
}
func (c *Config) AddServiceNameToHosts(ctx context.Context, serviceInterface v13.ServiceInterface, hosts ...Entry) error {
func (c *Config) AddServiceNameToHosts(ctx context.Context, hosts ...Entry) error {
var serviceList []v12.Service
//listOptions := v1.ListOptions{Limit: 100}
//for {
// services, err := serviceInterface.List(ctx, listOptions)
// if err != nil {
// break
// }
// serviceList = append(serviceList, services.Items...)
// if services.Continue != "" {
// listOptions.Continue = services.Continue
// } else {
// break
// }
//}
c.Lock.Lock()
defer c.Lock.Unlock()
@@ -71,100 +54,65 @@ func (c *Config) AddServiceNameToHosts(ctx context.Context, serviceInterface v13
return err
}
go c.watchServiceToAddHosts(ctx, serviceInterface, hosts)
go c.watchServiceToAddHosts(ctx, hosts)
return nil
}
func (c *Config) watchServiceToAddHosts(ctx context.Context, serviceInterface v13.ServiceInterface, hosts []Entry) {
func (c *Config) watchServiceToAddHosts(ctx context.Context, hosts []Entry) {
defer util.HandleCrash()
ticker := time.NewTicker(time.Second * 15)
defer ticker.Stop()
immediate := make(chan struct{}, 1)
immediate <- struct{}{}
var ErrChanDone = errors.New("watch service chan done")
for ctx.Err() == nil {
err := func() error {
w, err := serviceInterface.Watch(ctx, v1.ListOptions{Watch: true})
if err != nil {
return err
_, err := c.SvcInformer.AddEventHandler(cache.FilteringResourceEventHandler{
FilterFunc: func(obj interface{}) bool {
if svc, ok := obj.(*v12.Service); ok && svc.Namespace == c.Ns[0] {
return true
} else {
return false
}
defer w.Stop()
for {
select {
case <-ctx.Done():
return ctx.Err()
case event, ok := <-w.ResultChan():
if !ok {
return ErrChanDone
}
svc, ok := event.Object.(*v12.Service)
if !ok {
continue
}
if ctx.Err() != nil {
return ctx.Err()
}
if event.Type == watch.Deleted {
if net.ParseIP(svc.Spec.ClusterIP) == nil {
continue
}
var list = []Entry{{
IP: svc.Spec.ClusterIP,
Domain: svc.Name,
}}
err = c.removeHosts(list)
if err != nil {
plog.G(ctx).Errorf("Failed to remove hosts(%s) to hosts: %v", entryList2String(list), err)
}
}
if event.Type == watch.Added {
c.Lock.Lock()
appendHosts := c.generateAppendHosts([]v12.Service{*svc}, hosts)
err = c.appendHosts(appendHosts)
c.Lock.Unlock()
if err != nil {
plog.G(ctx).Errorf("Failed to add hosts(%s) to hosts: %v", entryList2String(appendHosts), err)
}
}
case <-ticker.C:
var list *v12.ServiceList
list, err = serviceInterface.List(ctx, v1.ListOptions{})
if err != nil {
continue
}
c.Lock.Lock()
appendHosts := c.generateAppendHosts(list.Items, hosts)
err = c.appendHosts(appendHosts)
c.Lock.Unlock()
if err != nil {
plog.G(ctx).Errorf("Failed to add hosts(%s) to hosts: %v", entryList2String(appendHosts), err)
}
case <-immediate:
var list *v12.ServiceList
list, err = serviceInterface.List(ctx, v1.ListOptions{})
if err != nil {
continue
}
c.Lock.Lock()
appendHosts := c.generateAppendHosts(list.Items, hosts)
err = c.appendHosts(appendHosts)
c.Lock.Unlock()
if err != nil {
plog.G(ctx).Errorf("Failed to add hosts(%s) to hosts: %v", entryList2String(appendHosts), err)
}
}
},
Handler: cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
ticker.Reset(time.Second * 3)
},
UpdateFunc: func(oldObj, newObj interface{}) {
ticker.Reset(time.Second * 3)
},
DeleteFunc: func(obj interface{}) {
ticker.Reset(time.Second * 3)
},
},
})
if err != nil {
plog.G(ctx).Errorf("Failed to add service event handler: %v", err)
return
}
for ; ctx.Err() == nil; <-ticker.C {
ticker.Reset(time.Second * 15)
serviceList, err := c.SvcInformer.GetIndexer().ByIndex(cache.NamespaceIndex, c.Ns[0])
if err != nil {
plog.G(ctx).Errorf("Failed to list service by namespace %s: %v", c.Ns[0], err)
continue
}
var services []v12.Service
for _, service := range serviceList {
svc, ok := service.(*v12.Service)
if !ok {
continue
}
}()
services = append(services, *svc)
}
if len(services) == 0 {
continue
}
if ctx.Err() != nil {
return
}
if err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, ErrChanDone) {
plog.G(ctx).Debugf("Failed to watch service to add route table: %v", err)
}
if utilnet.IsConnectionRefused(err) || apierrors.IsTooManyRequests(err) || apierrors.IsForbidden(err) {
time.Sleep(time.Second * 1)
} else {
time.Sleep(time.Millisecond * 200)
c.Lock.Lock()
appendHosts := c.generateAppendHosts(services, hosts)
err = c.appendHosts(appendHosts)
c.Lock.Unlock()
if err != nil && !errors.Is(err, context.Canceled) {
plog.G(ctx).Errorf("Failed to add hosts(%s) to hosts: %v", entryList2String(appendHosts), err)
}
}
}

View File

@@ -6,10 +6,10 @@ import (
"bytes"
"context"
"fmt"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"os"
"os/exec"
"path/filepath"
"slices"
"strings"
"time"
@@ -17,6 +17,10 @@ import (
miekgdns "github.com/miekg/dns"
v12 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/cache"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
// https://github.com/golang/go/issues/12524
@@ -32,6 +36,59 @@ var resolv = "/etc/resolv.conf"
// service.namespace.svc.cluster:port
// service.namespace.svc.cluster.local:port
func (c *Config) SetupDNS(ctx context.Context) error {
defer util.HandleCrash()
ticker := time.NewTicker(time.Second * 15)
_, err := c.SvcInformer.AddEventHandler(cache.FilteringResourceEventHandler{
FilterFunc: func(obj interface{}) bool {
if svc, ok := obj.(*v12.Service); ok && svc.Namespace == c.Ns[0] {
return true
} else {
return false
}
},
Handler: cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
ticker.Reset(time.Second * 3)
},
UpdateFunc: func(oldObj, newObj interface{}) {
ticker.Reset(time.Second * 3)
},
DeleteFunc: func(obj interface{}) {
ticker.Reset(time.Second * 3)
},
},
})
if err != nil {
plog.G(ctx).Errorf("Failed to add service event handler: %v", err)
return err
}
go func() {
defer ticker.Stop()
for ; ctx.Err() == nil; <-ticker.C {
ticker.Reset(time.Second * 15)
serviceList, err := c.SvcInformer.GetIndexer().ByIndex(cache.NamespaceIndex, c.Ns[0])
if err != nil {
plog.G(ctx).Errorf("Failed to list service by namespace %s: %v", c.Ns[0], err)
continue
}
var services []v12.Service
for _, service := range serviceList {
svc, ok := service.(*v12.Service)
if !ok {
continue
}
services = append(services, *svc)
}
if len(services) == 0 {
continue
}
if ctx.Err() != nil {
return
}
c.Services = services
c.usingResolver(ctx)
}
}()
c.usingResolver(ctx)
return nil
}
@@ -72,6 +129,9 @@ func (c *Config) usingResolver(ctx context.Context) {
plog.G(ctx).Errorf("Parse resolver %s error: %v", filename, err)
continue
}
if slices.Contains(conf.Servers, clientConfig.Servers[0]) {
continue
}
// insert current name server to first location
conf.Servers = append([]string{clientConfig.Servers[0]}, conf.Servers...)
err = os.WriteFile(filename, []byte(toString(*conf)), 0644)

View File

@@ -58,6 +58,7 @@ func (c *ConnectOptions) Cleanup(ctx context.Context) {
}
if c.clientset != nil {
_ = c.clientset.CoreV1().Pods(c.Namespace).Delete(ctx2, config.CniNetName, v1.DeleteOptions{GracePeriodSeconds: pointer.Int64(0)})
_ = c.clientset.BatchV1().Jobs(c.Namespace).Delete(ctx2, config.ConfigMapPodTrafficManager, v1.DeleteOptions{GracePeriodSeconds: pointer.Int64(0)})
}
// leave proxy resources
err := c.LeaveAllProxyResources(ctx2)

View File

@@ -32,15 +32,16 @@ import (
"k8s.io/apimachinery/pkg/labels"
pkgruntime "k8s.io/apimachinery/pkg/runtime"
pkgtypes "k8s.io/apimachinery/pkg/types"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/cli-runtime/pkg/resource"
runtimeresource "k8s.io/cli-runtime/pkg/resource"
informerv1 "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
v2 "k8s.io/client-go/kubernetes/typed/networking/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/retry"
"k8s.io/kubectl/pkg/cmd/set"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
@@ -67,6 +68,7 @@ type ConnectOptions struct {
Engine config.Engine
Foreground bool
OriginKubeconfigPath string
OriginNamespace string
Lock *sync.Mutex
ImagePullSecretName string
@@ -279,13 +281,14 @@ func (c *ConnectOptions) DoConnect(ctx context.Context, isLite bool, stopChan <-
return
}
plog.G(ctx).Infof("Adding Pod IP and Service IP to route table...")
if err = c.addRouteDynamic(c.ctx); err != nil {
var svcInformer cache.SharedIndexInformer
if svcInformer, _, err = c.addRouteDynamic(c.ctx); err != nil {
plog.G(ctx).Errorf("Add route dynamic failed: %v", err)
return
}
go c.deleteFirewallRule(c.ctx)
plog.G(ctx).Infof("Configuring DNS service...")
if err = c.setupDNS(c.ctx); err != nil {
if err = c.setupDNS(c.ctx, svcInformer); err != nil {
plog.G(ctx).Errorf("Configure DNS failed: %v", err)
return
}
@@ -329,19 +332,19 @@ func (c *ConnectOptions) portForward(ctx context.Context, portPair []string) err
podName := pod.GetName()
// try to detect pod is delete event, if pod is deleted, needs to redo port-forward
go util.CheckPodStatus(childCtx, cancelFunc, podName, c.clientset.CoreV1().Pods(c.Namespace))
go healthCheck(childCtx, cancelFunc, readyChan, strings.Split(portPair[1], ":")[0], fmt.Sprintf("%s.%s", config.ConfigMapPodTrafficManager, c.Namespace))
go func() {
select {
case <-readyChan:
for _, pair := range portPair {
ports := strings.Split(pair, ":")
plog.G(ctx).Infof("Forwarding from %s -> %s", net.JoinHostPort("127.0.0.1", ports[0]), ports[1])
domain := fmt.Sprintf("%s.%s", config.ConfigMapPodTrafficManager, c.Namespace)
go healthCheckPortForward(childCtx, cancelFunc, readyChan, strings.Split(portPair[1], ":")[0], domain, c.localTunIPv4.IP)
go healthCheckTCPConn(childCtx, cancelFunc, readyChan, domain, util.GetPodIP(pod)[0])
if *first {
go func() {
select {
case <-readyChan:
firstCancelFunc()
case <-childCtx.Done():
}
firstCancelFunc()
case <-childCtx.Done():
}
}()
}()
}
out := plog.G(ctx).Out
err = util.PortForwardPod(
c.config,
c.restclient,
@@ -350,8 +353,8 @@ func (c *ConnectOptions) portForward(ctx context.Context, portPair []string) err
portPair,
readyChan,
childCtx.Done(),
nil,
plog.G(ctx).Out,
out,
out,
)
if *first {
util.SafeWrite(errChan, err)
@@ -419,7 +422,7 @@ func (c *ConnectOptions) startLocalTunServer(ctx context.Context, forwardAddress
}
var routes []types.Route
for _, ipNet := range util.RemoveLargerOverlappingCIDRs(cidrList) {
for _, ipNet := range util.RemoveCIDRsContainingIPs(util.RemoveLargerOverlappingCIDRs(cidrList), c.apiServerIPs) {
if ipNet != nil {
routes = append(routes, types.Route{Dst: *ipNet})
}
@@ -489,57 +492,116 @@ func (c *ConnectOptions) startLocalTunServer(ctx context.Context, forwardAddress
}
// Listen all pod, add route if needed
func (c *ConnectOptions) addRouteDynamic(ctx context.Context) error {
podNs, svcNs, err1 := util.GetNsForListPodAndSvc(ctx, c.clientset, []string{v1.NamespaceAll, c.Namespace})
if err1 != nil {
return err1
func (c *ConnectOptions) addRouteDynamic(ctx context.Context) (cache.SharedIndexInformer, cache.SharedIndexInformer, error) {
podNs, svcNs, err := util.GetNsForListPodAndSvc(ctx, c.clientset, []string{v1.NamespaceAll, c.OriginNamespace})
if err != nil {
return nil, nil, err
}
conf := rest.CopyConfig(c.config)
conf.QPS = 1
conf.Burst = 2
clientSet, err := kubernetes.NewForConfig(conf)
if err != nil {
plog.G(ctx).Errorf("Failed to create clientset: %v", err)
return nil, nil, err
}
svcIndexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}
svcInformer := informerv1.NewServiceInformer(clientSet, svcNs, 0, svcIndexers)
svcTicker := time.NewTicker(time.Second * 15)
_, err = svcInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
svcTicker.Reset(time.Second * 3)
},
UpdateFunc: func(oldObj, newObj interface{}) {
svcTicker.Reset(time.Second * 3)
},
DeleteFunc: func(obj interface{}) {
svcTicker.Reset(time.Second * 3)
},
})
if err != nil {
plog.G(ctx).Errorf("Failed to add service event handler: %v", err)
return nil, nil, err
}
go svcInformer.Run(ctx.Done())
go func() {
var listDone bool
for ctx.Err() == nil {
err := func() error {
if !listDone {
err := util.ListService(ctx, c.clientset.CoreV1().Services(svcNs), c.addRoute)
if err != nil {
return err
}
listDone = true
defer svcTicker.Stop()
for ; ctx.Err() == nil; <-svcTicker.C {
svcTicker.Reset(time.Second * 15)
serviceList := svcInformer.GetIndexer().List()
var ips = sets.New[string]()
for _, service := range serviceList {
svc, ok := service.(*v1.Service)
if !ok {
continue
}
err := util.WatchServiceToAddRoute(ctx, c.clientset.CoreV1().Services(svcNs), c.addRoute)
return err
}()
if utilnet.IsConnectionRefused(err) || apierrors.IsTooManyRequests(err) || apierrors.IsForbidden(err) {
time.Sleep(time.Second * 10)
} else {
time.Sleep(time.Second * 2)
ips.Insert(svc.Spec.ClusterIP)
ips.Insert(svc.Spec.ClusterIPs...)
}
if ctx.Err() != nil {
return
}
if ips.Len() == 0 {
continue
}
err := c.addRoute(ips.UnsortedList()...)
if err != nil {
plog.G(ctx).Debugf("Add service IP to route table failed: %v", err)
}
}
}()
podIndexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}
podInformer := informerv1.NewPodInformer(clientSet, podNs, 0, podIndexers)
podTicker := time.NewTicker(time.Second * 15)
_, err = podInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
podTicker.Reset(time.Second * 3)
},
UpdateFunc: func(oldObj, newObj interface{}) {
podTicker.Reset(time.Second * 3)
},
DeleteFunc: func(obj interface{}) {
podTicker.Reset(time.Second * 3)
},
})
if err != nil {
plog.G(ctx).Errorf("Failed to add service event handler: %v", err)
return nil, nil, err
}
go podInformer.Run(ctx.Done())
go func() {
var listDone bool
for ctx.Err() == nil {
err := func() error {
if !listDone {
err := util.ListPod(ctx, c.clientset.CoreV1().Pods(podNs), c.addRoute)
if err != nil {
return err
}
listDone = true
defer podTicker.Stop()
for ; ctx.Err() == nil; <-podTicker.C {
podTicker.Reset(time.Second * 15)
podList := podInformer.GetIndexer().List()
var ips = sets.New[string]()
for _, pod := range podList {
p, ok := pod.(*v1.Pod)
if !ok {
continue
}
err := util.WatchPodToAddRoute(ctx, c.clientset.CoreV1().Pods(podNs), c.addRoute)
return err
}()
if utilnet.IsConnectionRefused(err) || apierrors.IsTooManyRequests(err) || apierrors.IsForbidden(err) {
time.Sleep(time.Second * 10)
} else {
time.Sleep(time.Second * 2)
if p.Spec.HostNetwork {
continue
}
ips.Insert(util.GetPodIP(*p)...)
}
if ctx.Err() != nil {
return
}
if ips.Len() == 0 {
continue
}
err := c.addRoute(ips.UnsortedList()...)
if err != nil {
plog.G(ctx).Debugf("Add pod IP to route table failed: %v", err)
}
}
}()
return nil
return svcInformer, podInformer, nil
}
func (c *ConnectOptions) addRoute(ipStrList ...string) error {
@@ -547,6 +609,7 @@ func (c *ConnectOptions) addRoute(ipStrList ...string) error {
return nil
}
var routes []types.Route
r, _ := netroute.New()
for _, ipStr := range ipStrList {
ip := net.ParseIP(ipStr)
if ip == nil {
@@ -569,7 +632,7 @@ func (c *ConnectOptions) addRoute(ipStrList ...string) error {
} else {
mask = net.CIDRMask(128, 128)
}
if r, err := netroute.New(); err == nil {
if r != nil {
ifi, _, _, err := r.Route(ip)
if err == nil && ifi.Name == c.tunName {
continue
@@ -577,6 +640,9 @@ func (c *ConnectOptions) addRoute(ipStrList ...string) error {
}
routes = append(routes, types.Route{Dst: net.IPNet{IP: ip, Mask: mask}})
}
if len(routes) == 0 {
return nil
}
err := tun.AddRoutes(c.tunName, routes...)
return err
}
@@ -599,7 +665,7 @@ func (c *ConnectOptions) deleteFirewallRule(ctx context.Context) {
util.DeleteBlockFirewallRule(ctx)
}
func (c *ConnectOptions) setupDNS(ctx context.Context) error {
func (c *ConnectOptions) setupDNS(ctx context.Context, svcInformer cache.SharedIndexInformer) error {
const portTCP = 10800
podList, err := c.GetRunningPodList(ctx)
if err != nil {
@@ -640,8 +706,8 @@ func (c *ConnectOptions) setupDNS(ctx context.Context) error {
return err
}
ns := []string{c.Namespace}
list, err := c.clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
ns := []string{c.OriginNamespace}
list, err := c.clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{Limit: 100})
if err == nil {
for _, item := range list.Items {
if !sets.New[string](ns...).Has(item.Name) {
@@ -650,20 +716,15 @@ func (c *ConnectOptions) setupDNS(ctx context.Context) error {
}
}
plog.G(ctx).Infof("Listing namespace %s services...", c.Namespace)
var serviceList []v1.Service
services, err := c.clientset.CoreV1().Services(c.Namespace).List(ctx, metav1.ListOptions{})
if err == nil {
serviceList = append(serviceList, services.Items...)
}
plog.G(ctx).Infof("Listing namespace %s services...", c.OriginNamespace)
c.dnsConfig = &dns.Config{
Config: relovConf,
Ns: ns,
Services: serviceList,
TunName: c.tunName,
Hosts: c.extraHost,
Lock: c.Lock,
Config: relovConf,
Ns: ns,
Services: []v1.Service{},
SvcInformer: svcInformer,
TunName: c.tunName,
Hosts: c.extraHost,
Lock: c.Lock,
HowToGetExternalName: func(domain string) (string, error) {
podList, err := c.GetRunningPodList(ctx)
if err != nil {
@@ -685,9 +746,9 @@ func (c *ConnectOptions) setupDNS(ctx context.Context) error {
if err = c.dnsConfig.SetupDNS(ctx); err != nil {
return err
}
plog.G(ctx).Infof("Dump service in namespace %s into hosts...", c.Namespace)
plog.G(ctx).Infof("Dump service in namespace %s into hosts...", c.OriginNamespace)
// dump service in current namespace for support DNS resolve service:port
err = c.dnsConfig.AddServiceNameToHosts(ctx, c.clientset.CoreV1().Services(c.Namespace), c.extraHost...)
err = c.dnsConfig.AddServiceNameToHosts(ctx, c.extraHost...)
return err
}
@@ -787,18 +848,13 @@ func (c *ConnectOptions) getCIDR(ctx context.Context, m *dhcp.Manager) error {
}
// (2) get CIDR from cni
c.cidrs, err = util.GetCIDR(ctx, c.clientset, c.config, c.Namespace)
c.cidrs = util.RemoveCIDRsContainingIPs(util.RemoveLargerOverlappingCIDRs(c.cidrs), c.apiServerIPs)
if err == nil {
s := sets.New[string]()
for _, cidr := range c.cidrs {
s.Insert(cidr.String())
}
return m.Set(ctx, config.KeyClusterIPv4POOLS, strings.Join(s.UnsortedList(), " "))
cidrs := util.GetCIDR(ctx, c.clientset, c.config, c.Namespace)
c.cidrs = util.RemoveCIDRsContainingIPs(util.RemoveLargerOverlappingCIDRs(cidrs), c.apiServerIPs)
s := sets.New[string]()
for _, cidr := range c.cidrs {
s.Insert(cidr.String())
}
// ignore error
return nil
return m.Set(ctx, config.KeyClusterIPv4POOLS, strings.Join(s.UnsortedList(), " "))
}
func (c *ConnectOptions) addExtraRoute(ctx context.Context, name string) error {
@@ -1206,7 +1262,7 @@ func (c *ConnectOptions) ProxyResources() ProxyList {
return c.proxyWorkloads
}
func healthCheck(ctx context.Context, cancelFunc context.CancelFunc, readyChan chan struct{}, localGvisorUDPPort string, domain string) {
func healthCheckPortForward(ctx context.Context, cancelFunc context.CancelFunc, readyChan chan struct{}, localGvisorUDPPort string, domain string, ipv4 net.IP) {
defer cancelFunc()
ticker := time.NewTicker(time.Second * 60)
defer ticker.Stop()
@@ -1230,7 +1286,7 @@ func healthCheck(ctx context.Context, cancelFunc context.CancelFunc, readyChan c
LocalPort: 53,
LocalAddress: tcpip.AddrFrom4Slice(net.ParseIP("127.0.0.1").To4()),
RemotePort: 0,
RemoteAddress: tcpip.AddrFrom4Slice(net.IPv4zero.To4()),
RemoteAddress: tcpip.AddrFrom4Slice(ipv4.To4()),
})
if err != nil {
return err
@@ -1260,3 +1316,40 @@ func healthCheck(ctx context.Context, cancelFunc context.CancelFunc, readyChan c
}
}
}
func healthCheckTCPConn(ctx context.Context, cancelFunc context.CancelFunc, readyChan chan struct{}, domain string, dnsServer string) {
defer cancelFunc()
ticker := time.NewTicker(time.Second * 60)
defer ticker.Stop()
select {
case <-readyChan:
case <-ticker.C:
plog.G(ctx).Debugf("Wait port-forward to be ready timeout")
return
case <-ctx.Done():
return
}
var healthChecker = func() error {
msg := new(miekgdns.Msg)
msg.SetQuestion(miekgdns.Fqdn(domain), miekgdns.TypeA)
client := miekgdns.Client{Net: "udp", Timeout: time.Second * 10}
_, _, err := client.ExchangeContext(ctx, msg, net.JoinHostPort(dnsServer, "53"))
return err
}
newTicker := time.NewTicker(config.KeepAliveTime / 2)
defer newTicker.Stop()
for ; ctx.Err() == nil; <-newTicker.C {
err := retry.OnError(wait.Backoff{Duration: time.Second * 10, Steps: 6}, func(err error) bool {
return err != nil
}, func() error {
return healthChecker()
})
if err != nil {
plog.G(ctx).Errorf("Failed to query DNS: %v", err)
return
}
}
}

View File

@@ -12,8 +12,8 @@ import (
"testing"
"time"
log "github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/wait"
@@ -23,7 +23,7 @@ import (
"k8s.io/client-go/util/retry"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
pkgconfig "github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
@@ -40,7 +40,7 @@ const (
func TestFunctions(t *testing.T) {
// 1) test connect
Init()
t.Run("init", Init)
t.Run("kubevpnConnect", kubevpnConnect)
t.Run("commonTest", commonTest)
@@ -65,6 +65,37 @@ func TestFunctions(t *testing.T) {
t.Run("commonTest", commonTest)
t.Run("serviceMeshReviewsServiceIP", serviceMeshReviewsServiceIP)
t.Run("kubevpnQuit", kubevpnQuit)
// 5) install centrally in ns test -- connect mode
t.Run("centerKubevpnUninstall", kubevpnUninstall)
t.Run("centerKubevpnInstallInNsKubevpn", kubevpnConnectToNsKubevpn)
t.Run("centerKubevpnConnect", kubevpnConnect)
t.Run("checkServiceShouldNotInNsDefault", checkServiceShouldNotInNsDefault)
t.Run("centerCommonTest", commonTest)
// 6) install centrally in ns test -- proxy mode
t.Run("centerKubevpnProxy", kubevpnProxy)
t.Run("checkServiceShouldNotInNsDefault", checkServiceShouldNotInNsDefault)
t.Run("centerCommonTest", commonTest)
t.Run("centerTestUDP", testUDP)
t.Run("centerProxyServiceReviewsServiceIP", proxyServiceReviewsServiceIP)
t.Run("centerProxyServiceReviewsPodIP", proxyServiceReviewsPodIP)
// 7) install centrally in ns test -- proxy mode with service mesh
t.Run("kubevpnLeave", kubevpnLeave)
t.Run("kubevpnProxyWithServiceMesh", kubevpnProxyWithServiceMesh)
t.Run("checkServiceShouldNotInNsDefault", checkServiceShouldNotInNsDefault)
t.Run("commonTest", commonTest)
t.Run("serviceMeshReviewsServiceIP", serviceMeshReviewsServiceIP)
t.Run("serviceMeshReviewsPodIP", serviceMeshReviewsPodIP)
// 8) install centrally in ns test -- proxy mode with service mesh and gvisor
t.Run("kubevpnQuit", kubevpnQuit)
t.Run("kubevpnProxyWithServiceMeshAndGvisorMode", kubevpnProxyWithServiceMeshAndGvisorMode)
t.Run("checkServiceShouldNotInNsDefault", checkServiceShouldNotInNsDefault)
t.Run("commonTest", commonTest)
t.Run("serviceMeshReviewsServiceIP", serviceMeshReviewsServiceIP)
t.Run("kubevpnQuit", kubevpnQuit)
}
func commonTest(t *testing.T) {
@@ -275,34 +306,37 @@ func proxyServiceReviewsServiceIP(t *testing.T) {
func testUDP(t *testing.T) {
app := "reviews"
port, _ := util.GetAvailableUDPPortOrDie()
go udpServer(port)
ip, err := getPodIP(app)
port, err := util.GetAvailableUDPPortOrDie()
if err != nil {
t.Fatal(err)
}
log.Printf("Dail udp to IP: %s", ip)
go udpServer(t, port)
var ip string
err = retry.OnError(
wait.Backoff{Duration: time.Second, Factor: 2, Jitter: 0.2, Steps: 5},
func(err error) bool {
return err != nil
},
func() error {
return udpClient(ip, port)
ip, err = getPodIP(app)
if err != nil {
t.Fatal(err)
}
t.Logf("Dail udp to IP: %s", ip)
return udpClient(t, ip, port)
})
if err != nil {
t.Fatalf("Failed to access pod IP: %s, port: %v", ip, port)
}
}
func udpClient(ip string, port int) error {
func udpClient(t *testing.T, ip string, port int) error {
udpConn, err := net.DialUDP("udp4", nil, &net.UDPAddr{
IP: net.ParseIP(ip),
Port: port,
})
if err != nil {
fmt.Println("连接失败!", err)
return err
}
defer udpConn.Close()
@@ -312,51 +346,48 @@ func udpClient(ip string, port int) error {
return err
}
// 发送数据
sendData := []byte("hello server!")
_, err = udpConn.Write(sendData)
if err != nil {
fmt.Println("发送数据失败!", err)
t.Logf("Failed to send udp packet: %v", err)
return err
}
// 接收数据
data := make([]byte, 4096)
read, remoteAddr, err := udpConn.ReadFromUDP(data)
if err != nil {
fmt.Println("读取数据失败!", err)
t.Logf("Failed to read udp packet: %v", err)
return err
}
fmt.Println(read, remoteAddr)
fmt.Printf("%s\n", data[0:read])
t.Logf("read data from %v: %v", remoteAddr, string(data[:read]))
return nil
}
func udpServer(port int) {
func udpServer(t *testing.T, port int) {
// 创建监听
udpConn, err := net.ListenUDP("udp4", &net.UDPAddr{
IP: net.IPv4(0, 0, 0, 0),
Port: port,
})
if err != nil {
t.Fatal(err)
return
}
defer udpConn.Close()
data := make([]byte, 4096)
for {
data := make([]byte, 4096)
read, remoteAddr, err := udpConn.ReadFromUDP(data)
read, remoteAddr, err := udpConn.ReadFromUDP(data[:])
if err != nil {
fmt.Println("读取数据失败!", err)
t.Logf("failed to read udp data from %v: %v", remoteAddr, err)
continue
}
fmt.Println(read, remoteAddr)
fmt.Printf("%s\n\n", data[0:read])
t.Logf("read data from %v: %v", remoteAddr, string(data[:read]))
sendData := []byte("hello client!")
_, err = udpConn.WriteToUDP(sendData, remoteAddr)
if err != nil {
fmt.Println("发送数据失败!", err)
t.Logf("failed to send udp data to %v: %v", remoteAddr, err)
return
}
}
@@ -372,6 +403,27 @@ func kubevpnConnect(t *testing.T) {
}
}
func kubevpnConnectToNsKubevpn(t *testing.T) {
_, err := clientset.CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{
ObjectMeta: v1.ObjectMeta{
Name: "kubevpn",
},
}, v1.CreateOptions{})
if err != nil {
t.Fatal(err)
}
cmdConnect := exec.Command("kubevpn", "connect", "--namespace", "kubevpn", "--debug")
cmdQuit := exec.Command("kubevpn", "quit")
for _, cmd := range []*exec.Cmd{cmdConnect, cmdQuit} {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
t.Fatal(err)
}
}
}
func kubevpnProxy(t *testing.T) {
cmd := exec.Command("kubevpn", "proxy", "deployments/reviews", "--debug")
cmd.Stdout = os.Stdout
@@ -442,52 +494,58 @@ func kubevpnQuit(t *testing.T) {
}
}
func kubectl(t *testing.T) {
cmd := exec.Command("kubectl", "get", "pods", "-o", "wide")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
t.Fatal(err)
}
cmd = exec.Command("kubectl", "get", "services", "-o", "wide")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
func checkServiceShouldNotInNsDefault(t *testing.T) {
_, err := clientset.CoreV1().Services(namespace).Get(context.Background(), pkgconfig.ConfigMapPodTrafficManager, v1.GetOptions{})
if !k8serrors.IsNotFound(err) {
t.Fatal(err)
}
}
func Init() {
func kubectl(t *testing.T) {
cmdGetPod := exec.Command("kubectl", "get", "pods", "-o", "wide")
cmdDescribePod := exec.Command("kubectl", "describe", "pods")
cmdGetSvc := exec.Command("kubectl", "get", "services", "-o", "wide")
cmdDescribeSvc := exec.Command("kubectl", "describe", "services")
for _, cmd := range []*exec.Cmd{cmdGetPod, cmdDescribePod, cmdGetSvc, cmdDescribeSvc} {
t.Logf("exec: %v", cmd.Args)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
t.Fatal(err)
}
}
}
func Init(t *testing.T) {
var err error
configFlags := genericclioptions.NewConfigFlags(true)
f := cmdutil.NewFactory(cmdutil.NewMatchVersionFlags(configFlags))
if restconfig, err = f.ToRESTConfig(); err != nil {
plog.G(context.Background()).Fatal(err)
t.Fatal(err)
}
if clientset, err = kubernetes.NewForConfig(restconfig); err != nil {
plog.G(context.Background()).Fatal(err)
t.Fatal(err)
}
if namespace, _, err = f.ToRawKubeConfigLoader().Namespace(); err != nil {
plog.G(context.Background()).Fatal(err)
t.Fatal(err)
}
go startupHttpServer(local)
go startupHttpServer(t, local)
}
func startupHttpServer(str string) {
func startupHttpServer(t *testing.T, str string) {
var health = func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(str))
}
http.HandleFunc("/", health)
http.HandleFunc("/health", health)
log.Println("Start listening http port 9080 ...")
if err := http.ListenAndServe(":9080", nil); err != nil {
panic(err)
t.Log("Start listening http port 9080 ...")
err := http.ListenAndServe(":9080", nil)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -49,6 +49,7 @@ func createOutboundPod(ctx context.Context, clientset *kubernetes.Clientset, nam
_ = clientset.CoreV1().Services(namespace).Delete(ctx, name, options)
_ = clientset.CoreV1().Secrets(namespace).Delete(ctx, name, options)
_ = clientset.CoreV1().Pods(namespace).Delete(ctx, config.CniNetName, options)
_ = clientset.BatchV1().Jobs(namespace).Delete(ctx, name, options)
_ = clientset.AppsV1().Deployments(namespace).Delete(ctx, name, options)
}
defer func() {
@@ -219,8 +220,9 @@ func genMutatingWebhookConfiguration(namespace string, crt []byte) *admissionv1.
},
}},
FailurePolicy: ptr.To(admissionv1.Ignore),
// namespace kubevpn is special, if installed to this namespace, means center install mode
// same as above label ns
NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"ns": namespace}},
NamespaceSelector: util.If(namespace == config.DefaultNamespaceKubevpn, nil, &metav1.LabelSelector{MatchLabels: map[string]string{"ns": namespace}}),
SideEffects: ptr.To(admissionv1.SideEffectClassNone),
TimeoutSeconds: ptr.To[int32](15),
AdmissionReviewVersions: []string{"v1", "v1beta1"},

View File

@@ -45,7 +45,7 @@ func (c *ConnectOptions) Uninstall(ctx context.Context) error {
_ = c.clientset.RbacV1().Roles(ns).Delete(ctx, name, options)
_ = c.clientset.CoreV1().Services(ns).Delete(ctx, name, options)
_ = c.clientset.AppsV1().Deployments(ns).Delete(ctx, name, options)
_ = c.clientset.BatchV1().Jobs(ns).Delete(ctx, name, options)
_ = c.CleanupLocalContainer(ctx)
plog.G(ctx).Info("Done")
return nil

View File

@@ -1,16 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: test
labels:
app: test
spec:
terminationGracePeriodSeconds: 0
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
- name: tomcat
image: tomcat
imagePullPolicy: IfNotPresent
restartPolicy: Always

View File

@@ -1,47 +0,0 @@
package main
import (
"context"
"io"
"net"
"github.com/containernetworking/cni/pkg/types"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/tun"
)
func main() {
ip := net.ParseIP("fe80::cff4:d42c:7e73:e84a")
listener, err := tun.Listener(tun.Config{
Addr: ip.String() + "/64",
MTU: 1350,
Routes: []types.Route{
{
Dst: net.IPNet{
IP: ip,
Mask: net.CIDRMask(64, 128),
},
}, {
Dst: net.IPNet{
IP: net.ParseIP("192.168.0.0"),
Mask: net.CIDRMask(64, 128),
},
},
},
})
if err != nil {
panic(err)
}
var tunConn net.Conn
tunConn, err = listener.Accept()
if err != nil {
plog.G(context.Background()).Fatal(err)
}
defer tunConn.Close()
tcpConn, err := net.Dial("tcp", ":1080")
if err != nil {
plog.G(context.Background()).Fatal(err)
}
go io.Copy(tunConn, tcpConn)
io.Copy(tcpConn, tunConn)
}

View File

@@ -1,33 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: test
labels:
app: test
spec:
terminationGracePeriodSeconds: 0
containers:
- name: traffic-test
image: ghcr.io/kubenetworks/kubevpn:v1.1.28
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
args:
- |
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
echo 1 > /proc/sys/net/ipv4/conf/all/route_localnet
update-alternatives --set iptables /usr/sbin/iptables-legacy
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
ip6tables -t nat -A POSTROUTING -s fe80::cff4:d42c:7e73:e84b/64 -o eth0 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o eth0 -j MASQUERADE
tail -f /dev/null
securityContext:
privileged: true
capabilities:
add:
- NET_ADMIN
restartPolicy: Always

View File

@@ -1,10 +0,0 @@
#!/bin/bash
export KUBECONFIG=~/.kube/vke
export NS=kube-system
kubectl apply -f pod.yaml -n $NS
kubectl wait --for=condition=Ready pod/test -n $NS
cd ./server && GOARCH=amd64 GOOS=linux go build -o main
kubectl cp main test:/app/main -n $NS
rm -fr main
kubectl port-forward pods/test 1080 -n $NS

View File

@@ -1,39 +0,0 @@
package main
import (
"context"
"io"
"net"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/tun"
)
func main() {
ip := net.ParseIP("fe80::cff4:d42c:7e73:e84b")
listener, err := tun.Listener(tun.Config{
Addr: ip.String() + "/64",
MTU: 1350,
})
if err != nil {
panic(err)
}
tunConn, _ := listener.Accept()
tcpListener, err := net.Listen("tcp", ":1080")
if err != nil {
plog.G(context.Background()).Fatal(err)
}
for {
tcpConn, err := tcpListener.Accept()
if err != nil {
panic(err)
}
go func(tcpConn net.Conn) {
defer tcpConn.Close()
go io.Copy(tunConn, tcpConn)
io.Copy(tcpConn, tunConn)
}(tcpConn)
}
}

View File

@@ -33,7 +33,7 @@ import (
// 2) grep cmdline
// 3) create svc + cat *.conflist
// 4) create svc + get pod ip with svc mask
func GetCIDR(ctx context.Context, clientset *kubernetes.Clientset, restconfig *rest.Config, namespace string) ([]*net.IPNet, error) {
func GetCIDR(ctx context.Context, clientset *kubernetes.Clientset, restconfig *rest.Config, namespace string) []*net.IPNet {
defer func() {
_ = clientset.CoreV1().Pods(namespace).Delete(context.Background(), config.CniNetName, v1.DeleteOptions{GracePeriodSeconds: pointer.Int64(0)})
}()
@@ -70,7 +70,7 @@ func GetCIDR(ctx context.Context, clientset *kubernetes.Clientset, restconfig *r
}
}
return result, nil
return result
}
// ParseCIDRFromString

View File

@@ -53,10 +53,10 @@ func TestByDumpClusterInfo(t *testing.T) {
before()
info, err := GetCIDRByDumpClusterInfo(context.Background(), clientset)
if err != nil {
t.Error(err)
t.Log(err.Error())
}
for _, ipNet := range info {
fmt.Println(ipNet.String())
t.Log(ipNet.String())
}
}
@@ -64,19 +64,18 @@ func TestByCreateSvc(t *testing.T) {
before()
info, err := GetServiceCIDRByCreateService(context.Background(), clientset.CoreV1().Services("default"))
if err != nil {
t.Error(err)
t.Log(err.Error())
}
if info != nil {
t.Log(info.String())
}
fmt.Println(info)
}
func TestElegant(t *testing.T) {
before()
elegant, err := GetCIDR(context.Background(), clientset, restconfig, namespace)
if err != nil {
t.Error(err)
}
for _, net := range elegant {
fmt.Println(net.String())
elegant := GetCIDR(context.Background(), clientset, restconfig, namespace)
for _, ipNet := range elegant {
t.Log(ipNet.String())
}
}

View File

@@ -10,6 +10,8 @@ import (
"path/filepath"
"strings"
"time"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
)
func DownloadFileWithName(uri, name string) (string, error) {
@@ -120,7 +122,7 @@ func ParseDirMapping(dir string) (local, remote string, err error) {
}
func CleanupTempKubeConfigFile() error {
return filepath.Walk(os.TempDir(), func(path string, info fs.FileInfo, err error) error {
return filepath.Walk(config.GetTempPath(), func(path string, info fs.FileInfo, err error) error {
if strings.HasSuffix(path, ".kubeconfig") {
return os.Remove(path)
}

View File

@@ -16,26 +16,26 @@ import (
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
// DetectConnectNamespace
// DetectManagerNamespace
// 1. use helm to install kubevpn server, means cluster mode,
// all kubevpn client should connect to this namespace.
// 2. if any error occurs, just ignore and will use options `-n` or `--namespace`
func DetectConnectNamespace(ctx context.Context, f cmdutil.Factory, connectNamespace string) (string, error) {
func DetectManagerNamespace(ctx context.Context, f cmdutil.Factory, namespace string) (string, error) {
clientSet, err := f.KubernetesClientSet()
if err != nil {
return "", err
}
var exists bool
exists, err = DetectPodExists(ctx, clientSet, connectNamespace)
exists, err = DetectPodExists(ctx, clientSet, namespace)
if err != nil && !k8serrors.IsNotFound(err) && !k8serrors.IsForbidden(err) {
return "", err
} else if err != nil {
plog.G(ctx).Debugf("Failed to detect if kubevpn exists in namespace %s: %v", connectNamespace, err)
plog.G(ctx).Debugf("Failed to detect if kubevpn exists in namespace %s: %v", namespace, err)
}
if exists {
plog.G(ctx).Debugf("Find exists kubevpn in namespace %s", connectNamespace)
return connectNamespace, nil
plog.G(ctx).Debugf("Find exists kubevpn in namespace %s", namespace)
return namespace, nil
}
exists, err = DetectPodExists(ctx, clientSet, config.DefaultNamespaceKubevpn)

View File

@@ -8,14 +8,18 @@ import (
"net/url"
"os"
"reflect"
"strings"
"unsafe"
errors2 "github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/kubernetes"
v12 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
@@ -25,6 +29,7 @@ import (
"k8s.io/utils/pointer"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
func GetClusterID(ctx context.Context, client v12.ConfigMapInterface) (types.UID, error) {
@@ -131,8 +136,9 @@ func GetAPIServerFromKubeConfigBytes(kubeconfigBytes []byte) *net.IPNet {
func ConvertToTempKubeconfigFile(kubeconfigBytes []byte) (string, error) {
pattern := "*.kubeconfig"
cluster, ns, _ := GetCluster(kubeconfigBytes)
if cluster != "" {
if cluster != "" && !containerPathSeparator(cluster) && !containerPathSeparator(ns) {
pattern = fmt.Sprintf("%s_%s_%s", cluster, ns, pattern)
pattern = strings.ReplaceAll(pattern, string(os.PathSeparator), "-")
}
temp, err := os.CreateTemp(config.GetTempPath(), pattern)
if err != nil {
@@ -153,6 +159,15 @@ func ConvertToTempKubeconfigFile(kubeconfigBytes []byte) (string, error) {
return temp.Name(), nil
}
func containerPathSeparator(pattern string) bool {
for i := 0; i < len(pattern); i++ {
if os.IsPathSeparator(pattern[i]) {
return true
}
}
return false
}
func GetCluster(kubeConfigBytes []byte) (cluster string, ns string, err error) {
var clientConfig clientcmd.ClientConfig
clientConfig, err = clientcmd.NewClientConfigFromBytes(kubeConfigBytes)
@@ -248,3 +263,48 @@ func GetKubeconfigPath(factory cmdutil.Factory) (string, error) {
}
return file, nil
}
func GetNsForListPodAndSvc(ctx context.Context, clientset *kubernetes.Clientset, nsList []string) (podNs string, svcNs string, err error) {
for _, ns := range nsList {
_, err = clientset.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{Limit: 1})
if errors.IsForbidden(err) {
continue
}
if err != nil {
return
}
podNs = ns
break
}
if err != nil {
err = errors2.Wrap(err, "can not list pod to add it to route table")
return
}
if podNs == "" {
log.G(ctx).Debugf("List all namepsace pods")
} else {
log.G(ctx).Debugf("List namepsace %s pods", podNs)
}
for _, ns := range nsList {
_, err = clientset.CoreV1().Services(ns).List(ctx, metav1.ListOptions{Limit: 1})
if errors.IsForbidden(err) {
continue
}
if err != nil {
return
}
svcNs = ns
break
}
if err != nil {
err = errors2.Wrap(err, "can not list service to add it to route table")
return
}
if svcNs == "" {
log.G(ctx).Debugf("List all namepsace services")
} else {
log.G(ctx).Debugf("List namepsace %s services", svcNs)
}
return
}

View File

@@ -1,169 +0,0 @@
package util
import (
"context"
"fmt"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
v12 "k8s.io/client-go/kubernetes/typed/core/v1"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
func GetNsForListPodAndSvc(ctx context.Context, clientset *kubernetes.Clientset, nsList []string) (podNs string, svcNs string, err error) {
for _, ns := range nsList {
_, err = clientset.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{Limit: 1})
if apierrors.IsForbidden(err) {
continue
}
if err != nil {
return
}
podNs = ns
break
}
if err != nil {
err = errors.Wrap(err, "can not list pod to add it to route table")
return
}
if podNs == "" {
plog.G(ctx).Debugf("List all namepsace pods")
} else {
plog.G(ctx).Debugf("List namepsace %s pods", podNs)
}
for _, ns := range nsList {
_, err = clientset.CoreV1().Services(ns).List(ctx, metav1.ListOptions{Limit: 1})
if apierrors.IsForbidden(err) {
continue
}
if err != nil {
return
}
svcNs = ns
break
}
if err != nil {
err = errors.Wrap(err, "can not list service to add it to route table")
return
}
if svcNs == "" {
plog.G(ctx).Debugf("List all namepsace services")
} else {
plog.G(ctx).Debugf("List namepsace %s services", svcNs)
}
return
}
func ListService(ctx context.Context, lister v12.ServiceInterface, addRouteFunc func(ipStr ...string) error) error {
opts := metav1.ListOptions{Limit: 100, Continue: ""}
for {
serviceList, err := lister.List(ctx, opts)
if err != nil {
return err
}
var ips []string
for _, service := range serviceList.Items {
ips = append(ips, service.Spec.ClusterIP)
}
err = addRouteFunc(ips...)
if err != nil {
plog.G(ctx).Errorf("Failed to add service IP to route table: %v", err)
}
if serviceList.Continue == "" {
return nil
}
opts.Continue = serviceList.Continue
}
}
func WatchServiceToAddRoute(ctx context.Context, watcher v12.ServiceInterface, routeFunc func(ipStr ...string) error) error {
defer func() {
if er := recover(); er != nil {
plog.G(ctx).Error(er)
}
}()
w, err := watcher.Watch(ctx, metav1.ListOptions{Watch: true})
if err != nil {
return err
}
defer w.Stop()
for {
select {
case <-ctx.Done():
return nil
case e, ok := <-w.ResultChan():
if !ok {
return errors.New("watch service chan done")
}
var svc *v1.Service
svc, ok = e.Object.(*v1.Service)
if !ok {
continue
}
_ = routeFunc(svc.Spec.ClusterIP)
}
}
}
func ListPod(ctx context.Context, lister v12.PodInterface, addRouteFunc func(ipStr ...string) error) error {
opts := metav1.ListOptions{Limit: 100, Continue: ""}
for {
podList, err := lister.List(ctx, opts)
if err != nil {
return err
}
var ips []string
for _, pod := range podList.Items {
if pod.Spec.HostNetwork {
continue
}
ips = append(ips, pod.Status.PodIP)
}
err = addRouteFunc(ips...)
if err != nil {
plog.G(ctx).Errorf("Failed to add Pod IP to route table: %v", err)
}
if podList.Continue == "" {
return nil
}
opts.Continue = podList.Continue
}
}
func WatchPodToAddRoute(ctx context.Context, watcher v12.PodInterface, addRouteFunc func(ipStrList ...string) error) error {
defer func() {
if er := recover(); er != nil {
plog.G(ctx).Errorln(er)
}
}()
w, err := watcher.Watch(ctx, metav1.ListOptions{Watch: true})
if err != nil {
return err
}
defer w.Stop()
for {
select {
case <-ctx.Done():
return nil
case e, ok := <-w.ResultChan():
if !ok {
return fmt.Errorf("watch pod chan done")
}
var pod *v1.Pod
pod, ok = e.Object.(*v1.Pod)
if !ok {
continue
}
if pod.Spec.HostNetwork {
continue
}
ip := pod.Status.PodIP
_ = addRouteFunc(ip)
}
}
}

View File

@@ -3,7 +3,7 @@ kind: Plugin
metadata:
name: kubevpn
spec:
version: v2.7.6
version: v2.7.12
homepage: https://github.com/kubenetworks/kubevpn
shortDescription: "KubeVPN offers a Cloud Native Dev Environment that connects to kubernetes cluster network"
description: |
@@ -16,8 +16,8 @@ spec:
matchLabels:
os: windows
arch: amd64
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.6/kubevpn_v2.7.6_windows_amd64.zip
sha256: c05eed78a60e88160e81a10220bc35a8050061ad26602c983c36fb25b19c0ce1
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.12/kubevpn_v2.7.12_windows_amd64.zip
sha256: 6ba7929c168e3890e872295005d54d00e7172f513b3fa410988cf6015e018b91
files:
- from: ./bin/kubevpn.exe
to: .
@@ -28,8 +28,8 @@ spec:
matchLabels:
os: windows
arch: arm64
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.6/kubevpn_v2.7.6_windows_arm64.zip
sha256: 007858a4f0a8053c07066dcb8dc04d2e2cbf29d93948a4d0482d92b5eb1e7a2a
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.12/kubevpn_v2.7.12_windows_arm64.zip
sha256: a266fc82a97301651f957b77294ee0d810b1c2e6c3474f5566d9327c71d20e2a
files:
- from: ./bin/kubevpn.exe
to: .
@@ -40,8 +40,8 @@ spec:
matchLabels:
os: windows
arch: 386
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.6/kubevpn_v2.7.6_windows_386.zip
sha256: 4b9db8811e96997b42ffa79571f7626d9d0d86eee2b7a019dfbf5f626426afb6
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.12/kubevpn_v2.7.12_windows_386.zip
sha256: 49863cf493ff121e6863f7a21975038c3d71b6fa6eafa3ef7a422f1370e1d929
files:
- from: ./bin/kubevpn.exe
to: .
@@ -52,8 +52,8 @@ spec:
matchLabels:
os: linux
arch: amd64
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.6/kubevpn_v2.7.6_linux_amd64.zip
sha256: 5038a4139ba6839c2a0836179457b0d9980ea7eb552480a3ea585dfda0093f86
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.12/kubevpn_v2.7.12_linux_amd64.zip
sha256: 894aca7c4f0ef1626121128e7bca7ee0e2dd56f4a0c518b6f4818c2dbdda8881
files:
- from: ./bin/kubevpn
to: .
@@ -64,8 +64,8 @@ spec:
matchLabels:
os: linux
arch: arm64
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.6/kubevpn_v2.7.6_linux_arm64.zip
sha256: 923a6658da82233ee30b74b282cfc8f9c869b6997a390cb33bcc18587a70be23
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.12/kubevpn_v2.7.12_linux_arm64.zip
sha256: 8692b2f8cf697f3ae2e168bcca3f18ae90657f36be6a0a321ffc4e5a778e706c
files:
- from: ./bin/kubevpn
to: .
@@ -76,8 +76,8 @@ spec:
matchLabels:
os: linux
arch: 386
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.6/kubevpn_v2.7.6_linux_386.zip
sha256: ad18711f3a6ac56a1481daace84a0a24794d4fcc310fd1fdcf39dcdcba32fe5d
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.12/kubevpn_v2.7.12_linux_386.zip
sha256: bf3a96b919fe716527a625c781fe96bdf882f9ba80a37e8a2d90dd76568d07c3
files:
- from: ./bin/kubevpn
to: .
@@ -88,8 +88,8 @@ spec:
matchLabels:
os: darwin
arch: amd64
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.6/kubevpn_v2.7.6_darwin_amd64.zip
sha256: 24219bcbc98cabbb4267b5c8041a7d8a379adf09218b0e4bf503883d85a60adb
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.12/kubevpn_v2.7.12_darwin_amd64.zip
sha256: 97be61dfd1e880f64f3f3d91d026301a0f20cadb04272bfd2e2e3543783c040f
files:
- from: ./bin/kubevpn
to: .
@@ -100,8 +100,8 @@ spec:
matchLabels:
os: darwin
arch: arm64
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.6/kubevpn_v2.7.6_darwin_arm64.zip
sha256: 8c5728d8ba16304eb1dbeaff076123d92257cdecfdcc165cc2c9e1b06c3c95db
uri: https://github.com/kubenetworks/kubevpn/releases/download/v2.7.12/kubevpn_v2.7.12_darwin_arm64.zip
sha256: c6359ff064338d42efd648a793841e09741f22f4449275fc003ba7cabe8d32ce
files:
- from: ./bin/kubevpn
to: .

View File

@@ -1 +1 @@
v2.7.6
v2.7.12

View File

@@ -36,7 +36,7 @@ except for float arguments and return values.
## Example
The example below only showcases purego use for macOS and Linux. The other platforms require special handling which can
be seen in the complete example at [examples/libc](https://github.com/ebitengine/purego/tree/main/examples/libc) which supports FreeBSD and Windows.
be seen in the complete example at [examples/libc](https://github.com/ebitengine/purego/tree/main/examples/libc) which supports Windows and FreeBSD.
```go
package main
@@ -92,7 +92,6 @@ This is a list of the copied files:
* `internal/fakecgo/iscgo.go` from package `runtime/cgo`
* `internal/fakecgo/setenv.go` from package `runtime/cgo`
* `internal/fakecgo/freebsd.go` from package `runtime/cgo`
* `internal/fakecgo/netbsd.go` from package `runtime/cgo`
The files `abi_*.h` and `internal/fakecgo/abi_*.h` are the same because Bazel does not support cross-package use of
`#include` so we need each one once per package. (cf. [issue](https://github.com/bazelbuild/rules_go/issues/3636))

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build cgo && (darwin || freebsd || linux || netbsd)
//go:build cgo && (darwin || freebsd || linux)
package purego

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
//go:build darwin || freebsd || linux || netbsd
//go:build darwin || freebsd || linux
package purego

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build (darwin || freebsd || linux || netbsd) && !android && !faketime
//go:build (darwin || freebsd || linux) && !android && !faketime
package purego

View File

@@ -1,15 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
package purego
// Source for constants: https://github.com/NetBSD/src/blob/trunk/include/dlfcn.h
const (
intSize = 32 << (^uint(0) >> 63) // 32 or 64
RTLD_DEFAULT = 1<<intSize - 2 // Pseudo-handle for dlsym so search for any loaded symbol
RTLD_LAZY = 0x00000001 // Relocations are performed at an implementation-dependent time.
RTLD_NOW = 0x00000002 // Relocations are performed when the object is loaded.
RTLD_LOCAL = 0x00000000 // All symbols are not made available for relocation processing by other modules.
RTLD_GLOBAL = 0x00000100 // All symbols are available for relocation processing of other modules.
)

View File

@@ -1,9 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
package purego
//go:cgo_import_dynamic purego_dlopen dlopen "libc.so"
//go:cgo_import_dynamic purego_dlsym dlsym "libc.so"
//go:cgo_import_dynamic purego_dlerror dlerror "libc.so"
//go:cgo_import_dynamic purego_dlclose dlclose "libc.so"

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || !cgo && (freebsd || linux || netbsd) && !faketime
//go:build darwin || !cgo && (freebsd || linux) && !faketime
#include "textflag.h"

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux || netbsd || windows
//go:build darwin || freebsd || linux || windows
package purego
@@ -10,20 +10,14 @@ import (
"math"
"reflect"
"runtime"
"strconv"
"sync"
"unsafe"
"github.com/ebitengine/purego/internal/strings"
)
var thePool = sync.Pool{New: func() any {
return new(syscall15Args)
}}
// RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name).
// It panics if it can't find the name symbol.
func RegisterLibFunc(fptr any, handle uintptr, name string) {
func RegisterLibFunc(fptr interface{}, handle uintptr, name string) {
sym, err := loadSymbol(handle, name)
if err != nil {
panic(err)
@@ -66,7 +60,7 @@ func RegisterLibFunc(fptr any, handle uintptr, name string) {
//
// There is a special case when the last argument of fptr is a variadic interface (or []interface}
// it will be expanded into a call to the C function as if it had the arguments in that slice.
// This means that using arg ...any is like a cast to the function with the arguments inside arg.
// This means that using arg ...interface{} is like a cast to the function with the arguments inside arg.
// This is not the same as C variadic.
//
// # Memory
@@ -111,7 +105,7 @@ func RegisterLibFunc(fptr any, handle uintptr, name string) {
// defer free(mustFree)
//
// [Cgo rules]: https://pkg.go.dev/cmd/cgo#hdr-Go_references_to_C
func RegisterFunc(fptr any, cfn uintptr) {
func RegisterFunc(fptr interface{}, cfn uintptr) {
fn := reflect.ValueOf(fptr).Elem()
ty := fn.Type()
if ty.Kind() != reflect.Func {
@@ -162,7 +156,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
if is32bit {
panic("purego: floats only supported on 64bit platforms")
}
if floats < numOfFloatRegisters {
if floats < numOfFloats {
floats++
} else {
stack++
@@ -206,8 +200,21 @@ func RegisterFunc(fptr any, cfn uintptr) {
}
}
v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) {
if len(args) > 0 {
if variadic, ok := args[len(args)-1].Interface().([]interface{}); ok {
// subtract one from args bc the last argument in args is []interface{}
// which we are currently expanding
tmp := make([]reflect.Value, len(args)-1+len(variadic))
n := copy(tmp, args[:len(args)-1])
for i, v := range variadic {
tmp[n+i] = reflect.ValueOf(v)
}
args = tmp
}
}
var sysargs [maxArgs]uintptr
var floats [numOfFloatRegisters]uintptr
stack := sysargs[numOfIntegerRegisters():]
var floats [numOfFloats]uintptr
var numInts int
var numFloats int
var numStack int
@@ -215,7 +222,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
// Windows arm64 uses the same calling convention as macOS and Linux
addStack = func(x uintptr) {
sysargs[numOfIntegerRegisters()+numStack] = x
stack[numStack] = x
numStack++
}
addInt = func(x uintptr) {
@@ -248,13 +255,12 @@ func RegisterFunc(fptr any, cfn uintptr) {
addFloat = addStack
}
var keepAlive []any
var keepAlive []interface{}
defer func() {
runtime.KeepAlive(keepAlive)
runtime.KeepAlive(args)
}()
var arm64_r8 uintptr
var syscall syscall15Args
if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
outType := ty.Out(0)
if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {
@@ -266,63 +272,53 @@ func RegisterFunc(fptr any, cfn uintptr) {
if !isAllFloats || numFields > 4 {
val := reflect.New(outType)
keepAlive = append(keepAlive, val)
arm64_r8 = val.Pointer()
syscall.arm64_r8 = val.Pointer()
}
}
}
for i, v := range args {
if variadic, ok := args[i].Interface().([]any); ok {
if i != len(args)-1 {
panic("purego: can only expand last parameter")
for _, v := range args {
switch v.Kind() {
case reflect.String:
ptr := strings.CString(v.String())
keepAlive = append(keepAlive, ptr)
addInt(uintptr(unsafe.Pointer(ptr)))
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
addInt(uintptr(v.Uint()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
addInt(uintptr(v.Int()))
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice:
// There is no need to keepAlive this pointer separately because it is kept alive in the args variable
addInt(v.Pointer())
case reflect.Func:
addInt(NewCallback(v.Interface()))
case reflect.Bool:
if v.Bool() {
addInt(1)
} else {
addInt(0)
}
for _, x := range variadic {
keepAlive = addValue(reflect.ValueOf(x), keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack)
}
continue
case reflect.Float32:
addFloat(uintptr(math.Float32bits(float32(v.Float()))))
case reflect.Float64:
addFloat(uintptr(math.Float64bits(v.Float())))
case reflect.Struct:
keepAlive = addStruct(v, &numInts, &numFloats, &numStack, addInt, addFloat, addStack, keepAlive)
default:
panic("purego: unsupported kind: " + v.Kind().String())
}
if runtime.GOARCH == "arm64" && runtime.GOOS == "darwin" &&
(numInts >= numOfIntegerRegisters() || numFloats >= numOfFloatRegisters) && v.Kind() != reflect.Struct { // hit the stack
fields := make([]reflect.StructField, len(args[i:]))
for j, val := range args[i:] {
if val.Kind() == reflect.String {
ptr := strings.CString(v.String())
keepAlive = append(keepAlive, ptr)
val = reflect.ValueOf(ptr)
args[i+j] = val
}
fields[j] = reflect.StructField{
Name: "X" + strconv.Itoa(j),
Type: val.Type(),
}
}
structType := reflect.StructOf(fields)
structInstance := reflect.New(structType).Elem()
for j, val := range args[i:] {
structInstance.Field(j).Set(val)
}
placeRegisters(structInstance, addFloat, addInt)
break
}
keepAlive = addValue(v, keepAlive, addInt, addFloat, addStack, &numInts, &numFloats, &numStack)
}
syscall := thePool.Get().(*syscall15Args)
defer thePool.Put(syscall)
if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
// Use the normal arm64 calling convention even on Windows
*syscall = syscall15Args{
syscall = syscall15Args{
cfn,
sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5],
sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
sysargs[12], sysargs[13], sysargs[14],
floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7],
arm64_r8,
syscall.arm64_r8,
}
runtime_cgocall(syscall15XABI0, unsafe.Pointer(syscall))
runtime_cgocall(syscall15XABI0, unsafe.Pointer(&syscall))
} else {
*syscall = syscall15Args{}
// This is a fallback for Windows amd64, 386, and arm. Note this may not support floats
syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4],
sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
@@ -361,54 +357,15 @@ func RegisterFunc(fptr any, cfn uintptr) {
// On 32bit platforms syscall.r2 is the upper part of a 64bit return.
v.SetFloat(math.Float64frombits(uint64(syscall.f1)))
case reflect.Struct:
v = getStruct(outType, *syscall)
v = getStruct(outType, syscall)
default:
panic("purego: unsupported return kind: " + outType.Kind().String())
}
if len(args) > 0 {
// reuse args slice instead of allocating one when possible
args[0] = v
return args[:1]
} else {
return []reflect.Value{v}
}
return []reflect.Value{v}
})
fn.Set(v)
}
func addValue(v reflect.Value, keepAlive []any, addInt func(x uintptr), addFloat func(x uintptr), addStack func(x uintptr), numInts *int, numFloats *int, numStack *int) []any {
switch v.Kind() {
case reflect.String:
ptr := strings.CString(v.String())
keepAlive = append(keepAlive, ptr)
addInt(uintptr(unsafe.Pointer(ptr)))
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
addInt(uintptr(v.Uint()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
addInt(uintptr(v.Int()))
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice:
// There is no need to keepAlive this pointer separately because it is kept alive in the args variable
addInt(v.Pointer())
case reflect.Func:
addInt(NewCallback(v.Interface()))
case reflect.Bool:
if v.Bool() {
addInt(1)
} else {
addInt(0)
}
case reflect.Float32:
addFloat(uintptr(math.Float32bits(float32(v.Float()))))
case reflect.Float64:
addFloat(uintptr(math.Float64bits(v.Float())))
case reflect.Struct:
keepAlive = addStruct(v, numInts, numFloats, numStack, addInt, addFloat, addStack, keepAlive)
default:
panic("purego: unsupported kind: " + v.Kind().String())
}
return keepAlive
}
// maxRegAllocStructSize is the biggest a struct can be while still fitting in registers.
// if it is bigger than this than enough space must be allocated on the heap and then passed into
// the function as the first parameter on amd64 or in R8 on arm64.

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux || netbsd || windows
//go:build darwin || freebsd || linux || windows
package purego

View File

@@ -1,12 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
//go:build freebsd || linux || netbsd
//go:build freebsd || linux
package cgo
/*
#cgo !netbsd LDFLAGS: -ldl
#cgo LDFLAGS: -ldl
#include <dlfcn.h>
#include <stdlib.h>

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build freebsd || (linux && !(arm64 || amd64)) || netbsd
//go:build freebsd || (linux && !(arm64 || amd64))
package cgo
@@ -9,7 +9,7 @@ package cgo
// because Cgo and assembly files can't be in the same package.
/*
#cgo !netbsd LDFLAGS: -ldl
#cgo LDFLAGS: -ldl
#include <stdint.h>
#include <dlfcn.h>

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !cgo && (darwin || freebsd || linux || netbsd)
//go:build !cgo && (darwin || freebsd || linux)
package fakecgo

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !cgo && (darwin || freebsd || linux || netbsd)
//go:build !cgo && (darwin || freebsd || linux)
// Package fakecgo implements the Cgo runtime (runtime/cgo) entirely in Go.
// This allows code that calls into C to function properly when CGO_ENABLED=0.

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !cgo && (darwin || freebsd || linux || netbsd)
//go:build !cgo && (darwin || freebsd || linux)
package fakecgo

View File

@@ -1,106 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !cgo && (amd64 || arm64)
package fakecgo
import "unsafe"
//go:nosplit
func _cgo_sys_thread_start(ts *ThreadStart) {
var attr pthread_attr_t
var ign, oset sigset_t
var p pthread_t
var size size_t
var err int
// fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
sigfillset(&ign)
pthread_sigmask(SIG_SETMASK, &ign, &oset)
pthread_attr_init(&attr)
pthread_attr_getstacksize(&attr, &size)
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
ts.g.stackhi = uintptr(size)
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
pthread_sigmask(SIG_SETMASK, &oset, nil)
if err != 0 {
print("fakecgo: pthread_create failed: ")
println(err)
abort()
}
}
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
//
//go:linkname x_threadentry_trampoline threadentry_trampoline
var x_threadentry_trampoline byte
var threadentry_trampolineABI0 = &x_threadentry_trampoline
//go:nosplit
func threadentry(v unsafe.Pointer) unsafe.Pointer {
var ss stack_t
ts := *(*ThreadStart)(v)
free(v)
// On NetBSD, a new thread inherits the signal stack of the
// creating thread. That confuses minit, so we remove that
// signal stack here before calling the regular mstart. It's
// a bit baroque to remove a signal stack here only to add one
// in minit, but it's a simple change that keeps NetBSD
// working like other OS's. At this point all signals are
// blocked, so there is no race.
ss.ss_flags = SS_DISABLE
sigaltstack(&ss, nil)
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
// faking funcs in go is a bit a... involved - but the following works :)
fn := uintptr(unsafe.Pointer(&ts.fn))
(*(*func())(unsafe.Pointer(&fn)))()
return nil
}
// here we will store a pointer to the provided setg func
var setg_func uintptr
//go:nosplit
func x_cgo_init(g *G, setg uintptr) {
var size size_t
var attr *pthread_attr_t
/* The memory sanitizer distributed with versions of clang
before 3.8 has a bug: if you call mmap before malloc, mmap
may return an address that is later overwritten by the msan
library. Avoid this problem by forcing a call to malloc
here, before we ever call malloc.
This is only required for the memory sanitizer, so it's
unfortunate that we always run it. It should be possible
to remove this when we no longer care about versions of
clang before 3.8. The test for this is
misc/cgo/testsanitizers.
GCC works hard to eliminate a seemingly unnecessary call to
malloc, so we actually use the memory we allocate. */
setg_func = setg
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
if attr == nil {
println("fakecgo: malloc failed")
abort()
}
pthread_attr_init(attr)
pthread_attr_getstacksize(attr, &size)
// runtime/cgo uses __builtin_frame_address(0) instead of `uintptr(unsafe.Pointer(&size))`
// but this should be OK since we are taking the address of the first variable in this function.
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
pthread_attr_destroy(attr)
free(unsafe.Pointer(attr))
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !cgo && (darwin || freebsd || linux || netbsd)
//go:build !cgo && (darwin || freebsd || linux)
package fakecgo

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !cgo && (darwin || freebsd || linux || netbsd)
//go:build !cgo && (darwin || freebsd || linux)
package fakecgo

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !cgo && (darwin || freebsd || linux || netbsd)
//go:build !cgo && (darwin || freebsd || linux)
// The runtime package contains an uninitialized definition
// for runtime·iscgo. Override it to tell the runtime we're here.

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !cgo && (darwin || freebsd || linux || netbsd)
//go:build !cgo && (darwin || freebsd || linux)
package fakecgo

View File

@@ -20,7 +20,3 @@ var (
PTHREAD_COND_INITIALIZER = pthread_cond_t{sig: 0x3CB0B1BB}
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{sig: 0x32AAABA7}
)
type stack_t struct {
/* not implemented */
}

View File

@@ -14,7 +14,3 @@ var (
PTHREAD_COND_INITIALIZER = pthread_cond_t(0)
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t(0)
)
type stack_t struct {
/* not implemented */
}

View File

@@ -14,7 +14,3 @@ var (
PTHREAD_COND_INITIALIZER = pthread_cond_t{}
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{}
)
type stack_t struct {
/* not implemented */
}

View File

@@ -1,26 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
//go:build !cgo
package fakecgo
type (
pthread_cond_t uintptr
pthread_mutex_t uintptr
)
var (
PTHREAD_COND_INITIALIZER = pthread_cond_t(0)
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t(0)
)
// Source: https://github.com/NetBSD/src/blob/613e27c65223fd2283b6ed679da1197e12f50e27/sys/compat/linux/arch/m68k/linux_signal.h#L133
type stack_t struct {
ss_sp uintptr
ss_flags int32
ss_size uintptr
}
// Source: https://github.com/NetBSD/src/blob/613e27c65223fd2283b6ed679da1197e12f50e27/sys/sys/signal.h#L261
const SS_DISABLE = 0x004

View File

@@ -1,23 +0,0 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build netbsd
package fakecgo
import _ "unsafe" // for go:linkname
// Supply environ and __progname, because we don't
// link against the standard NetBSD crt0.o and the
// libc dynamic library needs them.
//go:linkname _environ environ
//go:linkname _progname __progname
//go:linkname ___ps_strings __ps_strings
var (
_environ uintptr
_progname uintptr
___ps_strings uintptr
)

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !cgo && (darwin || freebsd || linux || netbsd)
//go:build !cgo && (darwin || freebsd || linux)
package fakecgo

View File

@@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !cgo && (darwin || freebsd || linux || netbsd)
//go:build !cgo && (darwin || freebsd || linux)
package fakecgo
@@ -55,11 +55,6 @@ func abort() {
call5(abortABI0, 0, 0, 0, 0, 0)
}
//go:nosplit
func sigaltstack(ss *stack_t, old_ss *stack_t) int32 {
return int32(call5(sigaltstackABI0, uintptr(unsafe.Pointer(ss)), uintptr(unsafe.Pointer(old_ss)), 0, 0, 0))
}
//go:nosplit
func pthread_attr_init(attr *pthread_attr_t) int32 {
return int32(call5(pthread_attr_initABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
@@ -153,10 +148,6 @@ var nanosleepABI0 = uintptr(unsafe.Pointer(&_nanosleep))
var _abort uint8
var abortABI0 = uintptr(unsafe.Pointer(&_abort))
//go:linkname _sigaltstack _sigaltstack
var _sigaltstack uint8
var sigaltstackABI0 = uintptr(unsafe.Pointer(&_sigaltstack))
//go:linkname _pthread_attr_init _pthread_attr_init
var _pthread_attr_init uint8
var pthread_attr_initABI0 = uintptr(unsafe.Pointer(&_pthread_attr_init))

View File

@@ -14,7 +14,6 @@ package fakecgo
//go:cgo_import_dynamic purego_sigfillset sigfillset "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_nanosleep nanosleep "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_abort abort "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_sigaltstack sigaltstack "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_create pthread_create "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "/usr/lib/libSystem.B.dylib"

View File

@@ -14,7 +14,6 @@ package fakecgo
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.7"
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.7"
//go:cgo_import_dynamic purego_abort abort "libc.so.7"
//go:cgo_import_dynamic purego_sigaltstack sigaltstack "libc.so.7"
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so"
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so"
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so"

View File

@@ -14,7 +14,6 @@ package fakecgo
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.6"
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.6"
//go:cgo_import_dynamic purego_abort abort "libc.so.6"
//go:cgo_import_dynamic purego_sigaltstack sigaltstack "libc.so.6"
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so.0"

View File

@@ -1,30 +0,0 @@
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !cgo
package fakecgo
//go:cgo_import_dynamic purego_malloc malloc "libc.so"
//go:cgo_import_dynamic purego_free free "libc.so"
//go:cgo_import_dynamic purego_setenv setenv "libc.so"
//go:cgo_import_dynamic purego_unsetenv unsetenv "libc.so"
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so"
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so"
//go:cgo_import_dynamic purego_abort abort "libc.so"
//go:cgo_import_dynamic purego_sigaltstack sigaltstack "libc.so"
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so"
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so"
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so"
//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "libpthread.so"
//go:cgo_import_dynamic purego_pthread_self pthread_self "libpthread.so"
//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "libpthread.so"
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so"
//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "libpthread.so"
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so"
//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "libpthread.so"
//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "libpthread.so"
//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "libpthread.so"
//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "libpthread.so"

View File

@@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !cgo && (darwin || freebsd || linux || netbsd)
//go:build !cgo && (darwin || freebsd || linux)
#include "textflag.h"
@@ -37,10 +37,6 @@ TEXT _abort(SB), NOSPLIT|NOFRAME, $0-0
JMP purego_abort(SB)
RET
TEXT _sigaltstack(SB), NOSPLIT|NOFRAME, $0-0
JMP purego_sigaltstack(SB)
RET
TEXT _pthread_attr_init(SB), NOSPLIT|NOFRAME, $0-0
JMP purego_pthread_attr_init(SB)
RET

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !cgo && (darwin || freebsd || linux || netbsd)
//go:build !cgo && (darwin || freebsd || linux)
package purego

View File

@@ -85,7 +85,7 @@ const (
_MEMORY = 0b1111
)
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} {
if v.Type().Size() == 0 {
return keepAlive
}
@@ -120,7 +120,7 @@ func postMerger(t reflect.Type) (passInMemory bool) {
if t.Size() <= 2*8 {
return false
}
return true // Go does not have an SSE/SSEUP type so this is always true
return true // Go does not have an SSE/SEEUP type so this is always true
}
func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) (ok bool) {
@@ -258,7 +258,3 @@ func placeStack(v reflect.Value, addStack func(uintptr)) {
}
}
}
func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
panic("purego: not needed on amd64")
}

View File

@@ -65,7 +65,7 @@ const (
_INT = 0b11
)
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} {
if v.Type().Size() == 0 {
return keepAlive
}
@@ -73,8 +73,8 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl
if hva, hfa, size := isHVA(v.Type()), isHFA(v.Type()), v.Type().Size(); hva || hfa || size <= 16 {
// if this doesn't fit entirely in registers then
// each element goes onto the stack
if hfa && *numFloats+v.NumField() > numOfFloatRegisters {
*numFloats = numOfFloatRegisters
if hfa && *numFloats+v.NumField() > numOfFloats {
*numFloats = numOfFloats
} else if hva && *numInts+v.NumField() > numOfIntegerRegisters() {
*numInts = numOfIntegerRegisters()
}
@@ -107,8 +107,6 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
} else {
f = v.Index(k)
}
align := byte(f.Type().Align()*8 - 1)
shift = (shift + align) &^ align
if shift >= 64 {
shift = 0
flushed = true
@@ -139,11 +137,10 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
val |= f.Uint() << shift
shift += 32
class |= _INT
case reflect.Uint64, reflect.Uint, reflect.Uintptr:
case reflect.Uint64:
addInt(uintptr(f.Uint()))
shift = 0
flushed = true
class = _NO_CLASS
case reflect.Int8:
val |= uint64(f.Int()&0xFF) << shift
shift += 8
@@ -156,11 +153,10 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
val |= uint64(f.Int()&0xFFFF_FFFF) << shift
shift += 32
class |= _INT
case reflect.Int64, reflect.Int:
case reflect.Int64:
addInt(uintptr(f.Int()))
shift = 0
flushed = true
class = _NO_CLASS
case reflect.Float32:
if class == _FLOAT {
addFloat(uintptr(val))
@@ -174,12 +170,6 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
addFloat(uintptr(math.Float64bits(float64(f.Float()))))
shift = 0
flushed = true
class = _NO_CLASS
case reflect.Ptr:
addInt(f.Pointer())
shift = 0
flushed = true
class = _NO_CLASS
case reflect.Array:
place(f)
default:
@@ -197,7 +187,7 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
}
}
func placeStack(v reflect.Value, keepAlive []any, addInt func(uintptr)) []any {
func placeStack(v reflect.Value, keepAlive []interface{}, addInt func(uintptr)) []interface{} {
// Struct is too big to be placed in registers.
// Copy to heap and place the pointer in register
ptrStruct := reflect.New(v.Type())

View File

@@ -7,14 +7,10 @@ package purego
import "reflect"
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} {
panic("purego: struct arguments are not supported")
}
func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
panic("purego: struct returns are not supported")
}
func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
panic("purego: not needed on other platforms")
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux || netbsd
//go:build darwin || freebsd || linux
#include "textflag.h"
#include "abi_amd64.h"

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux || netbsd || windows
//go:build darwin || freebsd || linux || windows
#include "textflag.h"
#include "go_asm.h"

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
//go:build darwin || freebsd || linux || netbsd
//go:build darwin || freebsd || linux
#include "textflag.h"
#include "go_asm.h"

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux || netbsd || windows
//go:build darwin || freebsd || linux || windows
package purego
@@ -13,8 +13,8 @@ package purego
type CDecl struct{}
const (
maxArgs = 15
numOfFloatRegisters = 8 // arm64 and amd64 both have 8 float registers
maxArgs = 15
numOfFloats = 8 // arm64 and amd64 both have 8 float registers
)
type syscall15Args struct {
@@ -27,9 +27,6 @@ type syscall15Args struct {
// There is an internal maximum number of arguments that SyscallN can take. It panics
// when the maximum is exceeded. It returns the result and the libc error code if there is one.
//
// In order to call this function properly make sure to follow all the rules specified in [unsafe.Pointer]
// especially point 4.
//
// NOTE: SyscallN does not properly call functions that have both integer and float parameters.
// See discussion comment https://github.com/ebiten/purego/pull/1#issuecomment-1128057607
// for an explanation of why that is.

View File

@@ -16,6 +16,6 @@ func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a
return cgo.Syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
}
func NewCallback(_ any) uintptr {
func NewCallback(_ interface{}) uintptr {
panic("purego: NewCallback on Linux is only supported on amd64/arm64")
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || (linux && (amd64 || arm64)) || netbsd
//go:build darwin || freebsd || (linux && (amd64 || arm64))
package purego
@@ -31,7 +31,7 @@ func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a
// of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory allocated
// for these callbacks is never released. At least 2000 callbacks can always be created. Although this function
// provides similar functionality to windows.NewCallback it is distinct.
func NewCallback(fn any) uintptr {
func NewCallback(fn interface{}) uintptr {
ty := reflect.TypeOf(fn)
for i := 0; i < ty.NumIn(); i++ {
in := ty.In(i)
@@ -71,7 +71,7 @@ type callbackArgs struct {
result uintptr
}
func compileCallback(fn any) uintptr {
func compileCallback(fn interface{}) uintptr {
val := reflect.ValueOf(fn)
if val.Kind() != reflect.Func {
panic("purego: the type must be a function but was not")
@@ -146,12 +146,12 @@ func callbackWrap(a *callbackArgs) {
var intsN int // intsN represents the number of integer arguments processed
// stack points to the index into frame of the current stack element.
// The stack begins after the float and integer registers.
stack := numOfIntegerRegisters() + numOfFloatRegisters
stack := numOfIntegerRegisters() + numOfFloats
for i := range args {
var pos int
switch fnType.In(i).Kind() {
case reflect.Float32, reflect.Float64:
if floatsN >= numOfFloatRegisters {
if floatsN >= numOfFloats {
pos = stack
stack++
} else {
@@ -169,7 +169,7 @@ func callbackWrap(a *callbackArgs) {
stack++
} else {
// the integers begin after the floats in frame
pos = intsN + numOfFloatRegisters
pos = intsN + numOfFloats
}
intsN++
}

View File

@@ -22,7 +22,7 @@ func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a
// allocated for these callbacks is never released. Between NewCallback and NewCallbackCDecl, at least 1024
// callbacks can always be created. Although this function is similiar to the darwin version it may act
// differently.
func NewCallback(fn any) uintptr {
func NewCallback(fn interface{}) uintptr {
isCDecl := false
ty := reflect.TypeOf(fn)
for i := 0; i < ty.NumIn(); i++ {

View File

@@ -1,6 +1,6 @@
// Code generated by wincallback.go using 'go generate'. DO NOT EDIT.
//go:build darwin || freebsd || linux || netbsd
//go:build darwin || freebsd || linux
// runtime·callbackasm is called by external code to
// execute Go implemented callback function. It is not

View File

@@ -1,6 +1,6 @@
// Code generated by wincallback.go using 'go generate'. DO NOT EDIT.
//go:build darwin || freebsd || linux || netbsd
//go:build darwin || freebsd || linux
// External code calls into callbackasm at an offset corresponding
// to the callback index. Callbackasm is a table of MOV and B instructions.

2
vendor/modules.txt vendored
View File

@@ -673,7 +673,7 @@ github.com/dustin/go-humanize
# github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4
## explicit; go 1.20
github.com/eapache/queue/v2
# github.com/ebitengine/purego v0.9.0-alpha.3.0.20250507171635-5047c08daa38
# github.com/ebitengine/purego v0.8.3
## explicit; go 1.18
github.com/ebitengine/purego
github.com/ebitengine/purego/internal/cgo