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
 | 
						|
}
 |