Browse Source

feat(vpn): implement vpn Update

Mustafa Arici 8 years ago
parent
commit
ee18b4cfad
9 changed files with 298 additions and 31 deletions
  1. 8 0
      api/rpc.go
  2. 1 0
      cmd/ovpm/main.go
  3. 53 0
      cmd/ovpm/vpn.go
  4. 2 0
      pb/user.pb.go
  5. 100 31
      pb/vpn.pb.go
  6. 46 0
      pb/vpn.pb.gw.go
  7. 13 0
      pb/vpn.proto
  8. 40 0
      pb/vpn.swagger.json
  9. 35 0
      vpn.go

+ 8 - 0
api/rpc.go

@@ -210,6 +210,14 @@ func (s *VPNService) Init(ctx context.Context, req *pb.VPNInitRequest) (*pb.VPNI
 	return &pb.VPNInitResponse{}, nil
 	return &pb.VPNInitResponse{}, nil
 }
 }
 
 
+func (s *VPNService) Update(ctx context.Context, req *pb.VPNUpdateRequest) (*pb.VPNUpdateResponse, error) {
+	logrus.Debugf("rpc call: vpn update")
+	if err := ovpm.Update(req.IPBlock, req.DNS); err != nil {
+		logrus.Errorf("server can not be updated: %v", err)
+	}
+	return &pb.VPNUpdateResponse{}, nil
+}
+
 type NetworkService struct{}
 type NetworkService struct{}
 
 
 func (s *NetworkService) List(ctx context.Context, req *pb.NetworkListRequest) (*pb.NetworkListResponse, error) {
 func (s *NetworkService) List(ctx context.Context, req *pb.NetworkListRequest) (*pb.NetworkListResponse, error) {

+ 1 - 0
cmd/ovpm/main.go

@@ -54,6 +54,7 @@ func main() {
 			Subcommands: []cli.Command{
 			Subcommands: []cli.Command{
 				vpnStatusCommand,
 				vpnStatusCommand,
 				vpnInitCommand,
 				vpnInitCommand,
+				vpnUpdateCommand,
 			},
 			},
 		},
 		},
 		{
 		{

+ 53 - 0
cmd/ovpm/vpn.go

@@ -143,3 +143,56 @@ var vpnInitCommand = cli.Command{
 		return nil
 		return nil
 	},
 	},
 }
 }
+
+var vpnUpdateCommand = cli.Command{
+	Name:    "update",
+	Usage:   "Update VPN server.",
+	Aliases: []string{"i"},
+	Flags: []cli.Flag{
+		cli.StringFlag{
+			Name:  "net, n",
+			Usage: fmt.Sprintf("VPN network to give clients IP addresses from, in the CIDR form (default: %s)", ovpm.DefaultVPNNetwork),
+		},
+		cli.StringFlag{
+			Name:  "dns, d",
+			Usage: fmt.Sprintf("DNS server to push to clients (default: %s)", ovpm.DefaultVPNDNS),
+		},
+	},
+	Action: func(c *cli.Context) error {
+		action = "vpn:update"
+
+		ipblock := c.String("net")
+		if ipblock != "" && !govalidator.IsCIDR(ipblock) {
+			fmt.Println("--net takes an ip network in the CIDR form. e.g. 10.9.0.0/24")
+			fmt.Println()
+			fmt.Println(cli.ShowSubcommandHelp(c))
+			os.Exit(1)
+		}
+
+		dns := c.String("dns")
+		if dns != "" && !govalidator.IsIPv4(dns) {
+			fmt.Println("--dns takes an IPv4 address. e.g. 8.8.8.8")
+			fmt.Println()
+			fmt.Println(cli.ShowSubcommandHelp(c))
+			os.Exit(1)
+		}
+
+		if !(ipblock != "" || dns != "") {
+			fmt.Println()
+			fmt.Println(cli.ShowSubcommandHelp(c))
+			os.Exit(1)
+		}
+
+		conn := getConn(c.GlobalString("daemon-port"))
+		defer conn.Close()
+		vpnSvc := pb.NewVPNServiceClient(conn)
+
+		if _, err := vpnSvc.Update(context.Background(), &pb.VPNUpdateRequest{IPBlock: ipblock, DNS: dns}); err != nil {
+			logrus.Errorf("server can not be updated: %v", err)
+			os.Exit(1)
+			return err
+		}
+		logrus.Info("ovpm server updated")
+		return nil
+	},
+}

+ 2 - 0
pb/user.pb.go

@@ -20,8 +20,10 @@ It has these top-level messages:
 	UserGenConfigResponse
 	UserGenConfigResponse
 	VPNStatusRequest
 	VPNStatusRequest
 	VPNInitRequest
 	VPNInitRequest
+	VPNUpdateRequest
 	VPNStatusResponse
 	VPNStatusResponse
 	VPNInitResponse
 	VPNInitResponse
+	VPNUpdateResponse
 	NetworkCreateRequest
 	NetworkCreateRequest
 	NetworkListRequest
 	NetworkListRequest
 	NetworkDeleteRequest
 	NetworkDeleteRequest

+ 100 - 31
pb/vpn.pb.go

@@ -98,6 +98,30 @@ func (m *VPNInitRequest) GetDNS() string {
 	return ""
 	return ""
 }
 }
 
 
+type VPNUpdateRequest struct {
+	IPBlock string `protobuf:"bytes,1,opt,name=IPBlock" json:"IPBlock,omitempty"`
+	DNS     string `protobuf:"bytes,2,opt,name=DNS" json:"DNS,omitempty"`
+}
+
+func (m *VPNUpdateRequest) Reset()                    { *m = VPNUpdateRequest{} }
+func (m *VPNUpdateRequest) String() string            { return proto.CompactTextString(m) }
+func (*VPNUpdateRequest) ProtoMessage()               {}
+func (*VPNUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{2} }
+
+func (m *VPNUpdateRequest) GetIPBlock() string {
+	if m != nil {
+		return m.IPBlock
+	}
+	return ""
+}
+
+func (m *VPNUpdateRequest) GetDNS() string {
+	if m != nil {
+		return m.DNS
+	}
+	return ""
+}
+
 type VPNStatusResponse struct {
 type VPNStatusResponse struct {
 	Name         string `protobuf:"bytes,1,opt,name=Name" json:"Name,omitempty"`
 	Name         string `protobuf:"bytes,1,opt,name=Name" json:"Name,omitempty"`
 	SerialNumber string `protobuf:"bytes,2,opt,name=SerialNumber" json:"SerialNumber,omitempty"`
 	SerialNumber string `protobuf:"bytes,2,opt,name=SerialNumber" json:"SerialNumber,omitempty"`
@@ -115,7 +139,7 @@ type VPNStatusResponse struct {
 func (m *VPNStatusResponse) Reset()                    { *m = VPNStatusResponse{} }
 func (m *VPNStatusResponse) Reset()                    { *m = VPNStatusResponse{} }
 func (m *VPNStatusResponse) String() string            { return proto.CompactTextString(m) }
 func (m *VPNStatusResponse) String() string            { return proto.CompactTextString(m) }
 func (*VPNStatusResponse) ProtoMessage()               {}
 func (*VPNStatusResponse) ProtoMessage()               {}
-func (*VPNStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{2} }
+func (*VPNStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} }
 
 
 func (m *VPNStatusResponse) GetName() string {
 func (m *VPNStatusResponse) GetName() string {
 	if m != nil {
 	if m != nil {
@@ -200,13 +224,23 @@ type VPNInitResponse struct {
 func (m *VPNInitResponse) Reset()                    { *m = VPNInitResponse{} }
 func (m *VPNInitResponse) Reset()                    { *m = VPNInitResponse{} }
 func (m *VPNInitResponse) String() string            { return proto.CompactTextString(m) }
 func (m *VPNInitResponse) String() string            { return proto.CompactTextString(m) }
 func (*VPNInitResponse) ProtoMessage()               {}
 func (*VPNInitResponse) ProtoMessage()               {}
-func (*VPNInitResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} }
+func (*VPNInitResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{4} }
+
+type VPNUpdateResponse struct {
+}
+
+func (m *VPNUpdateResponse) Reset()                    { *m = VPNUpdateResponse{} }
+func (m *VPNUpdateResponse) String() string            { return proto.CompactTextString(m) }
+func (*VPNUpdateResponse) ProtoMessage()               {}
+func (*VPNUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} }
 
 
 func init() {
 func init() {
 	proto.RegisterType((*VPNStatusRequest)(nil), "pb.VPNStatusRequest")
 	proto.RegisterType((*VPNStatusRequest)(nil), "pb.VPNStatusRequest")
 	proto.RegisterType((*VPNInitRequest)(nil), "pb.VPNInitRequest")
 	proto.RegisterType((*VPNInitRequest)(nil), "pb.VPNInitRequest")
+	proto.RegisterType((*VPNUpdateRequest)(nil), "pb.VPNUpdateRequest")
 	proto.RegisterType((*VPNStatusResponse)(nil), "pb.VPNStatusResponse")
 	proto.RegisterType((*VPNStatusResponse)(nil), "pb.VPNStatusResponse")
 	proto.RegisterType((*VPNInitResponse)(nil), "pb.VPNInitResponse")
 	proto.RegisterType((*VPNInitResponse)(nil), "pb.VPNInitResponse")
+	proto.RegisterType((*VPNUpdateResponse)(nil), "pb.VPNUpdateResponse")
 	proto.RegisterEnum("pb.VPNProto", VPNProto_name, VPNProto_value)
 	proto.RegisterEnum("pb.VPNProto", VPNProto_name, VPNProto_value)
 }
 }
 
 
@@ -223,6 +257,7 @@ const _ = grpc.SupportPackageIsVersion4
 type VPNServiceClient interface {
 type VPNServiceClient interface {
 	Status(ctx context.Context, in *VPNStatusRequest, opts ...grpc.CallOption) (*VPNStatusResponse, error)
 	Status(ctx context.Context, in *VPNStatusRequest, opts ...grpc.CallOption) (*VPNStatusResponse, error)
 	Init(ctx context.Context, in *VPNInitRequest, opts ...grpc.CallOption) (*VPNInitResponse, error)
 	Init(ctx context.Context, in *VPNInitRequest, opts ...grpc.CallOption) (*VPNInitResponse, error)
+	Update(ctx context.Context, in *VPNUpdateRequest, opts ...grpc.CallOption) (*VPNUpdateResponse, error)
 }
 }
 
 
 type vPNServiceClient struct {
 type vPNServiceClient struct {
@@ -251,11 +286,21 @@ func (c *vPNServiceClient) Init(ctx context.Context, in *VPNInitRequest, opts ..
 	return out, nil
 	return out, nil
 }
 }
 
 
+func (c *vPNServiceClient) Update(ctx context.Context, in *VPNUpdateRequest, opts ...grpc.CallOption) (*VPNUpdateResponse, error) {
+	out := new(VPNUpdateResponse)
+	err := grpc.Invoke(ctx, "/pb.VPNService/Update", in, out, c.cc, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 // Server API for VPNService service
 // Server API for VPNService service
 
 
 type VPNServiceServer interface {
 type VPNServiceServer interface {
 	Status(context.Context, *VPNStatusRequest) (*VPNStatusResponse, error)
 	Status(context.Context, *VPNStatusRequest) (*VPNStatusResponse, error)
 	Init(context.Context, *VPNInitRequest) (*VPNInitResponse, error)
 	Init(context.Context, *VPNInitRequest) (*VPNInitResponse, error)
+	Update(context.Context, *VPNUpdateRequest) (*VPNUpdateResponse, error)
 }
 }
 
 
 func RegisterVPNServiceServer(s *grpc.Server, srv VPNServiceServer) {
 func RegisterVPNServiceServer(s *grpc.Server, srv VPNServiceServer) {
@@ -298,6 +343,24 @@ func _VPNService_Init_Handler(srv interface{}, ctx context.Context, dec func(int
 	return interceptor(ctx, in, info, handler)
 	return interceptor(ctx, in, info, handler)
 }
 }
 
 
+func _VPNService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(VPNUpdateRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(VPNServiceServer).Update(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/pb.VPNService/Update",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(VPNServiceServer).Update(ctx, req.(*VPNUpdateRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 var _VPNService_serviceDesc = grpc.ServiceDesc{
 var _VPNService_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "pb.VPNService",
 	ServiceName: "pb.VPNService",
 	HandlerType: (*VPNServiceServer)(nil),
 	HandlerType: (*VPNServiceServer)(nil),
@@ -310,6 +373,10 @@ var _VPNService_serviceDesc = grpc.ServiceDesc{
 			MethodName: "Init",
 			MethodName: "Init",
 			Handler:    _VPNService_Init_Handler,
 			Handler:    _VPNService_Init_Handler,
 		},
 		},
+		{
+			MethodName: "Update",
+			Handler:    _VPNService_Update_Handler,
+		},
 	},
 	},
 	Streams:  []grpc.StreamDesc{},
 	Streams:  []grpc.StreamDesc{},
 	Metadata: "vpn.proto",
 	Metadata: "vpn.proto",
@@ -318,33 +385,35 @@ var _VPNService_serviceDesc = grpc.ServiceDesc{
 func init() { proto.RegisterFile("vpn.proto", fileDescriptor1) }
 func init() { proto.RegisterFile("vpn.proto", fileDescriptor1) }
 
 
 var fileDescriptor1 = []byte{
 var fileDescriptor1 = []byte{
-	// 434 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xcd, 0x6e, 0xd3, 0x40,
-	0x10, 0xc6, 0x8e, 0xeb, 0xc4, 0x43, 0x14, 0x9c, 0x69, 0x81, 0x25, 0xea, 0xa1, 0xf2, 0x29, 0xca,
-	0x21, 0x16, 0xe5, 0xd6, 0x5b, 0x49, 0x41, 0xed, 0x81, 0x65, 0x95, 0x40, 0xee, 0x4e, 0x59, 0x2a,
-	0xab, 0xe9, 0xee, 0xb2, 0xbb, 0xc9, 0x03, 0xf0, 0x0a, 0xbd, 0xf0, 0x08, 0xbc, 0x0f, 0xaf, 0xc0,
-	0x83, 0x20, 0x8f, 0x9d, 0x3f, 0xa4, 0xde, 0xbe, 0xf9, 0xc6, 0xf3, 0xf9, 0x9b, 0x6f, 0x07, 0x92,
-	0xb5, 0x51, 0x63, 0x63, 0xb5, 0xd7, 0x18, 0x9a, 0xc5, 0xe0, 0xf4, 0x4e, 0xeb, 0xbb, 0xa5, 0xcc,
-	0x0b, 0x53, 0xe6, 0x85, 0x52, 0xda, 0x17, 0xbe, 0xd4, 0xca, 0xd5, 0x5f, 0x64, 0x08, 0xe9, 0x5c,
-	0xf0, 0x99, 0x2f, 0xfc, 0xca, 0x4d, 0xe5, 0x8f, 0x95, 0x74, 0x3e, 0xfb, 0x15, 0x40, 0x6f, 0x2e,
-	0xf8, 0x8d, 0x2a, 0x7d, 0x43, 0xe1, 0x00, 0x3a, 0xd7, 0xda, 0x79, 0x55, 0x3c, 0x48, 0x16, 0x9c,
-	0x05, 0xc3, 0x64, 0xba, 0xad, 0x11, 0x21, 0x12, 0xda, 0x7a, 0x16, 0x12, 0x4f, 0x18, 0x47, 0x90,
-	0x88, 0x4a, 0xdf, 0x58, 0xf9, 0x9d, 0xb5, 0xce, 0x82, 0x61, 0xef, 0xbc, 0x3b, 0x36, 0x8b, 0xf1,
-	0x5c, 0x70, 0xe2, 0xa7, 0xbb, 0x36, 0x32, 0x68, 0xdf, 0x88, 0xf7, 0x4b, 0x7d, 0x7b, 0xcf, 0x22,
-	0x92, 0xd8, 0x94, 0x98, 0x42, 0xeb, 0x8a, 0xcf, 0xd8, 0x11, 0xb1, 0x15, 0xcc, 0x1e, 0x43, 0xe8,
-	0xef, 0xf9, 0x75, 0x46, 0x2b, 0x47, 0x0e, 0xf8, 0xce, 0x19, 0x61, 0xcc, 0xa0, 0x3b, 0x93, 0xb6,
-	0x2c, 0x96, 0x7c, 0xf5, 0xb0, 0x90, 0xb6, 0x71, 0x77, 0xc0, 0x1d, 0x6c, 0xd5, 0x7a, 0x62, 0xab,
-	0x68, 0x6f, 0x2b, 0x84, 0x68, 0x22, 0xad, 0x6f, 0x0c, 0x11, 0xc6, 0x57, 0x10, 0x4f, 0x2e, 0x89,
-	0x8d, 0x89, 0x6d, 0xaa, 0xca, 0x3b, 0x97, 0x9e, 0xb5, 0x6b, 0xef, 0x5c, 0xd2, 0xf4, 0xa7, 0xc2,
-	0xdd, 0xb3, 0x4e, 0x3d, 0x5d, 0x61, 0x3c, 0x85, 0x64, 0x62, 0x65, 0xe1, 0xe5, 0xb7, 0x4b, 0xcf,
-	0x12, 0x6a, 0xec, 0x08, 0x3c, 0x81, 0x23, 0x8a, 0x89, 0x01, 0x75, 0xea, 0x62, 0x93, 0xca, 0xf3,
-	0x5d, 0x2a, 0x7d, 0x78, 0xb1, 0x7d, 0xaf, 0x3a, 0x92, 0xd1, 0x10, 0x3a, 0x9b, 0xac, 0x11, 0x20,
-	0xe6, 0x9f, 0xc5, 0xf4, 0xc3, 0xc7, 0xf4, 0x19, 0xb6, 0xa1, 0xf5, 0xf5, 0x4a, 0xa4, 0x41, 0x05,
-	0xbe, 0x4c, 0x44, 0x1a, 0x9e, 0xff, 0x0e, 0x00, 0xaa, 0x48, 0xa5, 0x5d, 0x97, 0xb7, 0x12, 0x05,
-	0xc4, 0x75, 0xba, 0x78, 0xd2, 0x3c, 0xd8, 0xc1, 0x71, 0x0c, 0x5e, 0xfe, 0xc7, 0xd6, 0xff, 0xcb,
-	0xde, 0xfc, 0xfc, 0xf3, 0xf7, 0x31, 0x3c, 0xce, 0x7a, 0xf9, 0xfa, 0x6d, 0xbe, 0x36, 0x2a, 0x77,
-	0xd4, 0xbf, 0x08, 0x46, 0x78, 0x0d, 0x51, 0x65, 0x0d, 0xb1, 0x99, 0xdc, 0xbb, 0xab, 0xc1, 0xf1,
-	0x01, 0xd7, 0x68, 0xbd, 0x26, 0xad, 0x7e, 0xd6, 0xdd, 0x68, 0x95, 0xaa, 0xf4, 0x17, 0xc1, 0x68,
-	0x11, 0xd3, 0xcd, 0xbe, 0xfb, 0x17, 0x00, 0x00, 0xff, 0xff, 0x72, 0x2f, 0x46, 0x3c, 0xe2, 0x02,
-	0x00, 0x00,
+	// 478 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x53, 0xcd, 0x6e, 0xd3, 0x40,
+	0x10, 0xc6, 0x8e, 0xeb, 0xc6, 0x43, 0x14, 0x9c, 0x49, 0x81, 0x25, 0xea, 0xa1, 0xf2, 0x29, 0xca,
+	0x21, 0x16, 0xe5, 0xd6, 0x03, 0x52, 0x49, 0x41, 0xed, 0x01, 0xb3, 0x4a, 0x68, 0xee, 0x9b, 0x76,
+	0xa9, 0xac, 0xa6, 0xbb, 0x8b, 0xbd, 0xc9, 0x03, 0xf0, 0x0a, 0xbd, 0xf0, 0x5e, 0xbc, 0x02, 0x4f,
+	0xc1, 0x09, 0x79, 0x6c, 0xc7, 0x31, 0x3f, 0xb7, 0x99, 0x6f, 0xfc, 0x7d, 0xfa, 0xe6, 0xdb, 0x31,
+	0x04, 0x5b, 0xa3, 0xa6, 0x26, 0xd3, 0x56, 0xa3, 0x6b, 0x56, 0xa3, 0xe3, 0x3b, 0xad, 0xef, 0xd6,
+	0x32, 0x16, 0x26, 0x8d, 0x85, 0x52, 0xda, 0x0a, 0x9b, 0x6a, 0x95, 0x97, 0x5f, 0x44, 0x08, 0xe1,
+	0x92, 0x27, 0x0b, 0x2b, 0xec, 0x26, 0x9f, 0xcb, 0xaf, 0x1b, 0x99, 0xdb, 0xe8, 0xbb, 0x03, 0xfd,
+	0x25, 0x4f, 0xae, 0x54, 0x6a, 0x2b, 0x08, 0x47, 0xd0, 0xbd, 0xd4, 0xb9, 0x55, 0xe2, 0x41, 0x32,
+	0xe7, 0xc4, 0x19, 0x07, 0xf3, 0x5d, 0x8f, 0x08, 0x1e, 0xd7, 0x99, 0x65, 0x2e, 0xe1, 0x54, 0xe3,
+	0x04, 0x02, 0x5e, 0xe8, 0x9b, 0x4c, 0x7e, 0x61, 0x9d, 0x13, 0x67, 0xdc, 0x3f, 0xed, 0x4d, 0xcd,
+	0x6a, 0xba, 0xe4, 0x09, 0xe1, 0xf3, 0x66, 0x8c, 0x0c, 0x0e, 0xaf, 0xf8, 0xbb, 0xb5, 0xbe, 0xb9,
+	0x67, 0x1e, 0x49, 0xd4, 0x2d, 0x86, 0xd0, 0xb9, 0x48, 0x16, 0xec, 0x80, 0xd0, 0xa2, 0x8c, 0xde,
+	0x92, 0xdd, 0x6b, 0x73, 0x2b, 0xac, 0xac, 0xbd, 0xed, 0xf1, 0x9d, 0x7f, 0xf2, 0xdd, 0x86, 0xff,
+	0xe8, 0xc2, 0x60, 0x6f, 0xdf, 0xdc, 0x68, 0x95, 0xd3, 0x06, 0x49, 0xb3, 0x19, 0xd5, 0x18, 0x41,
+	0x6f, 0x21, 0xb3, 0x54, 0xac, 0x93, 0xcd, 0xc3, 0x4a, 0x66, 0x95, 0x48, 0x0b, 0x6b, 0xa5, 0xd2,
+	0xf9, 0x4f, 0x2a, 0xde, 0x5e, 0x2a, 0x08, 0xde, 0x4c, 0x66, 0xb6, 0x5a, 0x88, 0x6a, 0x7c, 0x01,
+	0xfe, 0xec, 0x9c, 0x50, 0x9f, 0xd0, 0xaa, 0x2b, 0xbc, 0x27, 0xd2, 0xb2, 0xc3, 0xd2, 0x7b, 0x22,
+	0x89, 0xfd, 0x51, 0xe4, 0xf7, 0xac, 0x5b, 0xb2, 0x8b, 0x1a, 0x8f, 0x21, 0x98, 0x65, 0x52, 0x58,
+	0x79, 0x7b, 0x6e, 0x59, 0x40, 0x83, 0x06, 0xc0, 0x23, 0x38, 0xa0, 0x98, 0x19, 0xd0, 0xa4, 0x6c,
+	0xea, 0x54, 0x9e, 0x36, 0xa9, 0x0c, 0xe0, 0xd9, 0xee, 0xbd, 0xcb, 0x48, 0xa2, 0x21, 0xe5, 0x54,
+	0x07, 0x5d, 0x82, 0x93, 0x31, 0x74, 0xeb, 0x07, 0x44, 0x00, 0x3f, 0xf9, 0xc4, 0xe7, 0xef, 0x3f,
+	0x84, 0x4f, 0xf0, 0x10, 0x3a, 0xd7, 0x17, 0x3c, 0x74, 0x8a, 0xe2, 0xf3, 0x8c, 0x87, 0xee, 0xe9,
+	0x2f, 0x07, 0xa0, 0xc8, 0x59, 0x66, 0xdb, 0xf4, 0x46, 0x22, 0x07, 0xbf, 0x8c, 0x1c, 0x8f, 0xaa,
+	0x2b, 0x68, 0x5d, 0xdc, 0xe8, 0xf9, 0x1f, 0x68, 0x65, 0xe2, 0xd5, 0xb7, 0x1f, 0x3f, 0x1f, 0xdd,
+	0x61, 0xd4, 0x8f, 0xb7, 0xaf, 0xe3, 0xad, 0x51, 0x71, 0x4e, 0xf3, 0x33, 0x67, 0x82, 0x97, 0xe0,
+	0x15, 0x7e, 0x11, 0x2b, 0xe6, 0xde, 0xb1, 0x8e, 0x86, 0x2d, 0xac, 0xd2, 0x7a, 0x49, 0x5a, 0x83,
+	0xa8, 0x57, 0x6b, 0xa5, 0x2a, 0xb5, 0x85, 0x12, 0x07, 0xbf, 0x5c, 0x73, 0xe7, 0xad, 0x75, 0x5e,
+	0x3b, 0x6f, 0xed, 0x2c, 0xfe, 0xf6, 0xb6, 0xa1, 0xf9, 0x99, 0x33, 0x59, 0xf9, 0xf4, 0x6b, 0xbd,
+	0xf9, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x98, 0x14, 0x2a, 0x0b, 0x89, 0x03, 0x00, 0x00,
 }
 }

+ 46 - 0
pb/vpn.pb.gw.go

@@ -54,6 +54,19 @@ func request_VPNService_Init_0(ctx context.Context, marshaler runtime.Marshaler,
 
 
 }
 }
 
 
+func request_VPNService_Update_0(ctx context.Context, marshaler runtime.Marshaler, client VPNServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq VPNUpdateRequest
+	var metadata runtime.ServerMetadata
+
+	if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.Update(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
 // RegisterVPNServiceHandlerFromEndpoint is same as RegisterVPNServiceHandler but
 // RegisterVPNServiceHandlerFromEndpoint is same as RegisterVPNServiceHandler but
 // automatically dials to "endpoint" and closes the connection when "ctx" gets done.
 // automatically dials to "endpoint" and closes the connection when "ctx" gets done.
 func RegisterVPNServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
 func RegisterVPNServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
@@ -142,6 +155,35 @@ func RegisterVPNServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn
 
 
 	})
 	})
 
 
+	mux.Handle("POST", pattern_VPNService_Update_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(ctx)
+		defer cancel()
+		if cn, ok := w.(http.CloseNotifier); ok {
+			go func(done <-chan struct{}, closed <-chan bool) {
+				select {
+				case <-done:
+				case <-closed:
+					cancel()
+				}
+			}(ctx.Done(), cn.CloseNotify())
+		}
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_VPNService_Update_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_VPNService_Update_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 	return nil
 }
 }
 
 
@@ -149,10 +191,14 @@ var (
 	pattern_VPNService_Status_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "vpn", "status"}, ""))
 	pattern_VPNService_Status_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "vpn", "status"}, ""))
 
 
 	pattern_VPNService_Init_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "vpn", "init"}, ""))
 	pattern_VPNService_Init_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "vpn", "init"}, ""))
+
+	pattern_VPNService_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "vpn", "update"}, ""))
 )
 )
 
 
 var (
 var (
 	forward_VPNService_Status_0 = runtime.ForwardResponseMessage
 	forward_VPNService_Status_0 = runtime.ForwardResponseMessage
 
 
 	forward_VPNService_Init_0 = runtime.ForwardResponseMessage
 	forward_VPNService_Init_0 = runtime.ForwardResponseMessage
+
+	forward_VPNService_Update_0 = runtime.ForwardResponseMessage
 )
 )

+ 13 - 0
pb/vpn.proto

@@ -19,6 +19,12 @@ message VPNInitRequest {
   string DNS = 5;
   string DNS = 5;
 }
 }
 
 
+message VPNUpdateRequest {
+  string IPBlock = 1;
+  string DNS = 2;
+}
+
+
 service VPNService {
 service VPNService {
   rpc Status (VPNStatusRequest) returns (VPNStatusResponse) {
   rpc Status (VPNStatusRequest) returns (VPNStatusResponse) {
     option (google.api.http) = {
     option (google.api.http) = {
@@ -30,6 +36,12 @@ service VPNService {
       post: "/v1/vpn/init"
       post: "/v1/vpn/init"
       body: "*"
       body: "*"
     };}
     };}
+  rpc Update (VPNUpdateRequest) returns (VPNUpdateResponse) {
+    option (google.api.http) = {
+      post: "/v1/vpn/update"
+      body: "*"
+    };}
+
 }
 }
 
 
 message VPNStatusResponse {
 message VPNStatusResponse {
@@ -46,3 +58,4 @@ message VPNStatusResponse {
   string DNS = 11;
   string DNS = 11;
 }
 }
 message VPNInitResponse {}
 message VPNInitResponse {}
+message VPNUpdateResponse {}

+ 40 - 0
pb/vpn.swagger.json

@@ -66,6 +66,32 @@
           "VPNService"
           "VPNService"
         ]
         ]
       }
       }
+    },
+    "/v1/vpn/update": {
+      "post": {
+        "operationId": "Update",
+        "responses": {
+          "200": {
+            "description": "",
+            "schema": {
+              "$ref": "#/definitions/pbVPNUpdateResponse"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/pbVPNUpdateRequest"
+            }
+          }
+        ],
+        "tags": [
+          "VPNService"
+        ]
+      }
     }
     }
   },
   },
   "definitions": {
   "definitions": {
@@ -141,6 +167,20 @@
           "type": "string"
           "type": "string"
         }
         }
       }
       }
+    },
+    "pbVPNUpdateRequest": {
+      "type": "object",
+      "properties": {
+        "IPBlock": {
+          "type": "string"
+        },
+        "DNS": {
+          "type": "string"
+        }
+      }
+    },
+    "pbVPNUpdateResponse": {
+      "type": "object"
     }
     }
   }
   }
 }
 }

+ 35 - 0
vpn.go

@@ -276,6 +276,41 @@ func Init(hostname string, port string, proto string, ipblock string, dns string
 	return nil
 	return nil
 }
 }
 
 
+// Update updates VPN server attributes.
+func Update(ipblock string, dns string) error {
+	if !IsInitialized() {
+		return fmt.Errorf("server is not initialized")
+	}
+
+	server, err := GetServerInstance()
+	if err != nil {
+		return err
+	}
+
+	var changed bool
+	if ipblock != "" && govalidator.IsCIDR(ipblock) {
+		var ipnet *net.IPNet
+		_, ipnet, err = net.ParseCIDR(ipblock)
+		if err != nil {
+			return fmt.Errorf("can not parse CIDR %s: %v", ipblock, err)
+		}
+		server.dbServerModel.Net = ipnet.IP.To4().String()
+		server.dbServerModel.Mask = net.IP(ipnet.Mask).To4().String()
+		changed = true
+	}
+
+	if dns != "" && govalidator.IsIPv4(dns) {
+		server.dbServerModel.DNS = dns
+		changed = true
+	}
+	if changed {
+		db.Save(server.dbServerModel)
+		Emit()
+		logrus.Infof("server updated")
+	}
+	return nil
+}
+
 // Deinit deletes the VPN server from the database and frees the allocated resources.
 // Deinit deletes the VPN server from the database and frees the allocated resources.
 func Deinit() error {
 func Deinit() error {
 	if !IsInitialized() {
 	if !IsInitialized() {