127 lines
3.2 KiB
Go
127 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/Microsoft/go-winio"
|
|
pb "github.com/grmrgecko/virtual-vxlan/vxlan"
|
|
log "github.com/sirupsen/logrus"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
)
|
|
|
|
// Allows go generate to compile the protobuf to golang.
|
|
//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./vxlan/vxlan.proto
|
|
|
|
// GRPC server structure.
|
|
type GRPCServer struct {
|
|
pb.UnimplementedVxlanServer
|
|
RPCPath string
|
|
server *grpc.Server
|
|
}
|
|
|
|
// Start serving GRPC requests.
|
|
func (s *GRPCServer) Serve(li net.Listener) {
|
|
err := s.server.Serve(li)
|
|
if err != nil {
|
|
log.Errorf("Error serving grpc: %v", err)
|
|
}
|
|
}
|
|
|
|
// Stop GRPC server.
|
|
func (s *GRPCServer) Close() {
|
|
s.server.Stop()
|
|
}
|
|
|
|
// Dialer for named pipes to allow connecting GRPC via named pipes.
|
|
func pipeDialer(ctx context.Context, addr string) (net.Conn, error) {
|
|
return winio.DialPipe(addr, nil)
|
|
}
|
|
|
|
// Verifies the RPC UNIX path is still listening if it exists.
|
|
func RPCCleanPath(rpcPath string) {
|
|
if !strings.HasPrefix(rpcPath, `\\.\`) {
|
|
// Check if the RPC socket already exists.
|
|
_, err := os.Stat(rpcPath)
|
|
if err == nil {
|
|
// If the socket exists, see if its listening.
|
|
l, err := net.Dial("unix", rpcPath)
|
|
|
|
// If its not listening, remove it to allow us to start.
|
|
if err != nil {
|
|
os.Remove(rpcPath)
|
|
} else {
|
|
l.Close()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start GRPC server.
|
|
func NewGRPCServer(rpcPath string) (s *GRPCServer, err error) {
|
|
// Verify another server doesn't exist.
|
|
if app.grpcServer != nil {
|
|
return nil, fmt.Errorf("grpc server is already running")
|
|
}
|
|
|
|
// Connect to RPC path.
|
|
var li net.Listener
|
|
if strings.HasPrefix(rpcPath, `\\.\`) {
|
|
li, err = winio.ListenPipe(rpcPath, nil)
|
|
} else {
|
|
li, err = net.Listen("unix", rpcPath)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to listen on socket: %v", err)
|
|
}
|
|
|
|
// Setup server.
|
|
s = new(GRPCServer)
|
|
s.server = grpc.NewServer()
|
|
|
|
// Register the vxlan service to this server.
|
|
pb.RegisterVxlanServer(s.server, s)
|
|
|
|
// Update the global app gRPC server.
|
|
app.grpcServer = s
|
|
|
|
// Start serving requests.
|
|
go s.Serve(li)
|
|
return s, nil
|
|
}
|
|
|
|
// Start a connection to the gRPC Server.
|
|
func NewGRPCClient() (c pb.VxlanClient, conn *grpc.ClientConn, err error) {
|
|
// Read the minimal config.
|
|
config := ReadMinimalConfig()
|
|
|
|
// Start an gRPC client connection to the unix socket.
|
|
if strings.HasPrefix(config.RPCPath, `\\.\`) {
|
|
// Attempt to connect using named pipes.
|
|
dialOption := grpc.WithContextDialer(pipeDialer)
|
|
|
|
conn, err = grpc.DialContext(
|
|
context.Background(),
|
|
config.RPCPath,
|
|
dialOption,
|
|
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
|
// Add a timeout for the initial connection
|
|
grpc.WithBlock(),
|
|
grpc.WithTimeout(5*time.Second),
|
|
)
|
|
} else {
|
|
conn, err = grpc.NewClient(fmt.Sprintf("unix:%s", config.RPCPath), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
}
|
|
|
|
// If connection is successful, provide client to the vxlan service.
|
|
if err == nil {
|
|
c = pb.NewVxlanClient(conn)
|
|
}
|
|
return
|
|
}
|