Wednesday, May 30, 2012

Set up OpenVPN server on FreeBSD running PF packet filter firewall and run VPN client on Windows 7 and FreeBSD 8.2

Set up OpenVPN server on FreeBSD running PF packet filter firewall and run VPN client on Windows 7 and FreeBSD 8.2

OpenVPN is a free and open source software application that implements virtual private network (VPN) techniques for creating secure point-to-point or site-to-site connections in routed or bridged configurations and remote access facilities. It uses a custom security protocol[2] that utilizes SSL/TLS for key exchange. It is capable of traversing network address translators (NATs) and firewalls. It was written by James Yonan and is published under the GNU General Public License (GPL)

My server has two WAN connections from two different ISP, so the setting in this tutorial allows the VPN clients to connect to the VPN server with my two public IP addresses. However, you can still use this tutorial to set up the OpenVPN server with a single WAN interface.

If you need help on how to set up multiple WAN interfaces with two different ISP:
http://gala4th.blogspot.com/2011/10/multiple-default-routes-gateways-with.html

Running OpenVPN in different scenarios:
1. Single OpenVPN instance on single interface.
2. Multiple OpenVPN instances on single interface. Ex: you want different rules for each set of VPN clients.
3. Single OpenVPN instance on each of multiple interfaces.
4. Multiple OpenVPN instance on each of multiple interfaces.

We will set up OpenVPN in "Single OpenVPN instance on each of multiple interfaces" scenario.

Install OpenVPN:
# cd /usr/ports/security/openvpn
# make config-recursive
# make config-recursive
# make config-recursive

Note: run "make config-recursive" few times. You may enable a dependency the first time through that has its own configuration options.

# make install

# mkdir /usr/local/etc/openvpn

This /usr/local/etc/rc.d/openvpn script supports running multiple instances of openvpn. To run additional instances link this script to something like:

For example:
# ln -s openvpn openvpn_foo

and define additional openvpn_foo_* variables in one of:
/etc/rc.conf, /etc/rc.conf.local or /etc/rc.conf.d/openvpn_foo

A list of avaiable interface on my server:

# ifconfig -l | tr ' ' '\n'
wan0 // IP 11.11.11.11. First WAN interface for my first ISP.
wan1 // IP 22.22.22.22. Second WAN interface for my second ISP.
dmz0 // IP 192.168.100.1 and 192.168.100.2. DMZ interface.
lan0 // IP 192.168.200.1 and 192.168.200.2. LAN interface.
tun0 // IP 10.8.0.1. First VPN interface for VPN clients connecting to first WAN interface.
tun1 // IP 10.9.0.1. Second VPN interface for VPN clients connecting to second WAN interface.

# vim /etc/rc.conf
ifconfig_wan0="inet 11.11.11.11 netmask 255.255.255.0"
ifconfig_wan1="inet 22.22.22.22 netmask 255.255.255.0"

ifconfig_dmz0="inet 192.168.100.1 netmask 255.255.255.0"
ifconfig_dmz0_alias0="inet 192.168.100.2 netmask 255.255.255.255"

ifconfig_lan0="inet 192.168.200.1 netmask 255.255.255.0"
ifconfig_lan0_alias0="inet 192.168.200.2 netmask 255.255.255.255"

// Note: do this if you want to run one instance of OpenVPN on each of two interfaces.
# ln -s /usr/local/etc/rc.d/openvpn /usr/local/etc/rc.d/openvpn_wan0
# ln -s /usr/local/etc/rc.d/openvpn /usr/local/etc/rc.d/openvpn_wan1

Sample configuration files can be found in:
# ls -l /usr/local/share/doc/openvpn/sample-config-files

Copy the OpenVPN config file
# cp /usr/local/share/doc/openvpn/sample-config-files/server.conf /usr/local/etc/openvpn/openvpn_wan0.conf

# cp /usr/local/share/doc/openvpn/sample-config-files/server.conf /usr/local/etc/openvpn/openvpn_wan1.conf

Note: The reason why we need to rename the config filename from server.conf to openvpn_foo.conf is because the /usr/local/etc/rc.d/openvpn script supports running multiple instances of openvpn. The openvpn script will try to read the config from the SCRIPT_NAME.conf. For example:

If you are startup script is called /usr/local/etc/rc.d/openvpn_wan0, it will read config file from /usr/local/etc/openvpn/openvpn_wan0.conf.

Edit OpenVPN config file:
# vim /usr/local/etc/openvpn/openvpn_wan0.conf
### Specify device
dev tun

### Which local IP address should OpenVPN
### listen on? (optional)
local 11.11.11.11

### Certificates for VPN Authentication
ca /usr/local/etc/openvpn/keys/ca.crt
cert /usr/local/etc/openvpn/keys/server.crt
key /usr/local/etc/openvpn/keys/server.key # This file should be kept secret

### Diffie hellman parameters
dh /usr/local/etc/openvpn/keys/dh1024.pem

### Server and client IP and Pool
### The server will take 10.8.0.1 for itself,
### the rest will be made available to clients.
server 10.8.0.0 255.255.255.0

# Maintain a record of client <-> virtual IP address
# associations in this file. If OpenVPN goes down or
# is restarted, reconnecting clients can be assigned
# the same virtual IP address from the pool that was
# previously assigned.
ifconfig-pool-persist ipp.txt

### Routes to push to the client.
### The specified network will be added automatically on the VPN client.
push "route 192.168.0.0 255.255.255.0"

### Enable compression on the VPN link.
### If you enable it here, you must also
### enable it in the client config file.
comp-lzo

### Make the link more resistant to connection failures
keepalive 10 120
persist-tun
persist-key

### optional ?
#ping-timer-rem

# It's a good idea to reduce the OpenVPN
# daemon's privileges after initialization.
#
# You can uncomment this out on non-Windows systems.
# By specifying 'nobody' OpenVPN service privileges are
# dropped to nobody. This increases system security.
user nobody
group nobody

# By default, log messages will go to the syslog (or
# on Windows, if running as a service, they will go to
# the "\Program Files\OpenVPN\log" directory).
# Use log or log-append to override this default.
# "log" will truncate the log file on OpenVPN startup,
# while "log-append" will append to it. Use one
# or the other (but not both).
;log openvpn.log
log-append /var/log/openvpn_wan0.log

# Set the appropriate level of log
# file verbosity.
#
# 0 is silent, except for fatal errors
# 4 is reasonable for general usage
# 5 and 6 can help to debug connection problems
# 9 is extremely verbose
verb 3

For openvpn_wan1.conf, most settings are similar to the previous openvpn_wan0.conf file, except:

# vim /usr/local/etc/openvpn/openvpn_wan1.conf
### Which local IP address should OpenVPN
### listen on? (optional)
local 22.22.22.22

### Server and client IP and Pool
### The server will take 10.9.0.1 for itself,
### the rest will be made available to clients.
server 10.9.0.0 255.255.255.0

log-append /var/log/openvpn_wan1.log

Review the difference between openvpn_wan0.conf and openvpn_wan1.conf:
# diff /usr/local/etc/openvpn/openvpn_wan0.conf /usr/local/etc/openvpn/openvpn_wan1.conf

Logging
# touch /var/log/openvpn_wan0.log
# touch /var/log/openvpn_wan1.log

Rotate OpenVPN log files:
# vim /etc/newsyslog.conf
/var/log/openvpn_wan0.log 600 9 100000 * JC /var/run/openvpn_wan0.pid
/var/log/openvpn_wan1.log 600 9 100000 * JC /var/run/openvpn_wan1.pid

# /etc/rc.d/newsyslog restart

Copy easy-rsa script to the home directory
# cp -r /usr/local/share/doc/openvpn/easy-rsa ~
# find ~/easy-rsa/ -type d -print0 | xargs -0 -I @ chmod 700 @
# find ~/easy-rsa/ -type f -print0 | xargs -0 -I @ chmod 400 @

Make sure these scripts are executable:
# find ~/easy-rsa/2.0/ -type f -print0 | xargs -0 -I @ chmod u+x @

Change follow lines in vars file to reflect your setting:
# cd ~/easy-rsa/2.0/
# vim ~/easy-rsa/2.0/vars
### These are the default values for fields
### which will be placed in the certificate.
### Don't leave any of these fields blank.
export KEY_COUNTRY="CA"
export KEY_PROVINCE="BC"
export KEY_CITY="Vancouver"
export KEY_ORG="Fort-Funston Ltd"
export KEY_EMAIL="me@myhost.mydomain"
export KEY_CN="CommonName_Can_Be_YourName_or_HostName"
export KEY_NAME="YourName"
export KEY_OU="UnitName_or_DepartmentName"
export PKCS11_MODULE_PATH="dummy"
export PKCS11_PIN="A_RANDOM_PASSWORD"

Very Important Step for FreeBSD/TCSH users. FreeBSD ships with tcsh as its native shell, at the time of writing the following scripts do not work. To get around this you must drop down to a bourne shell. To do this just type the following at a prompt:

# sh
# cd ~/easy-rsa/2.0/

Now you can carry on with building the certificates, once you have built them you can exit back out to tcsh.

Sourcing the vars file and other scripts:
# . vars
# ./clean-all
# ./build-ca

You will have to answer a few questions on the last step, once this has been done your CA certs will be created in the keys subdirectory.

Generate certificate & key for server:
# ./build-key-server server
A challenge password []: LEAVE IT BLANK AND PRESS ENTER
An optional company name []: LEAVE IT BLANK AND PRESS ENTER

Again answer the questions and the certs will be placed in the keys subdirectory.

Generate certificates & keys for 3 clients (each client will require their own certificates, if multiple clients log in with the same certs then they will be assigned the same ips and will kick each other off the network):

Generating client certificates is very similar to the previous step. You need to ensure that all your details are the same as for the CA, apart from the common name, which needs to be different for each client. For the sake of clarity this should relate to person who is assigned this vpn certificate. All of these details can be found in keys/server.crt for the server and keys/client*.crt for the client details.

# ./build-key client1
# ./build-key client2
# ./build-key client3

Generate Diffie Hellman parameters

Diffie Hellman parameters must be generated for the OpenVPN server:

# ./build-dh

Exit the sh shell:
# exit

Now move the whole keys directory to /usr/local/etc/openvpn:

# mv ~/easy-rsa/2.0/keys /usr/local/etc/openvpn/

Protect our certificate keys directory and config files for only root can see it.
# find /usr/local/etc/openvpn/keys/ -type d -print0 | xargs -0 -I @ sh -c 'chmod 700 @ ; chown root:wheel @'
# find /usr/local/etc/openvpn/keys/ -type f -print0 | xargs -0 -I @ sh -c 'chmod 400 @ ; chown root:wheel @'
# find /usr/local/etc/openvpn/ccd -type d -print0 | xargs -0 -I {} chmod 755 {}
# find /usr/local/etc/openvpn/ccd -type f -print0 | xargs -0 -I {} chmod 644 {}
# find /usr/local/etc/openvpn/ -type f -name '*.conf' -print0 | xargs -0 -I @ sh -c 'chmod 400 @ ; chown root:wheel @'

Note: ccd directory and files under the ccd directory must be readable.

You need to edit /etc/rc.conf if you set up single instance of OpenVPN with single interface:

Note: I have commented out following lines because I set to start OpenVPN daemon in /etc/rc.local later.

# vim /etc/rc.conf
#openvpn_wan0_enable="YES"
#openvpn_wan0_configfile="/usr/local/etc/openvpn/openvpn_wan0.conf"

#openvpn_wan1_enable="YES"
#openvpn_wan1_configfile="/usr/local/etc/openvpn/openvpn_wan1.conf"

We will be using the setfib command to run our utilities with an different routing table: 

Note: You do not need the setfib command if you are hosting your OpenVPN server with single WAN interface.

# cp /etc/ssh/sshd_config /etc/ssh/sshd_config.rtable0
# cp /etc/ssh/sshd_config /etc/ssh/sshd_config.rtable1

Edit sshd_config.* config file:
# vim /etc/ssh/sshd_config.rtable0
### IP address of first WAN interface
ListenAddress 11.11.11.11
### IP address of DMZ interface
ListenAddress 192.168.100.1
### IP address of LAN interface
ListenAddress 192.168.200.1

# vim /etc/ssh/sshd_config.rtable1
### IP address of second WAN interface
ListenAddress 22.22.22.22
### IP address of DMZ interface
ListenAddress 192.168.100.2
### IP address of LAN interface
ListenAddress 192.168.200.2

Note: Multiple ListenAddress options are permitted.

Note: you can let sshd to listen to the IP address of the tunnel interface. However, I do not recommend you do so. Because if OpenVPN did not start properly, sshd will have problem to start as well (because it could not listen to the specified IP address).

Edit rc.local:
# vim /etc/rc.local
### add first routing table for first WAN interface for first ISP network.
/usr/sbin/setfib 0 /sbin/route delete default
/usr/sbin/setfib 0 /sbin/route add default 11.11.11.11

### add second routing table for second WAN interface for second ISP network.
/usr/sbin/setfib 1 /sbin/route delete default
/usr/sbin/setfib 1 /sbin/route add default 22.22.22.22

### start OpenVPN on the first WAN interface.
/usr/sbin/setfib 0 /usr/local/etc/rc.d/openvpn_wan0 onestart

### start OpenVPN on the second WAN interface.
/usr/sbin/setfib 1 /usr/local/etc/rc.d/openvpn_wan1 onestart

### Use different routing table for each sshd daemon.
### Note: we need to start SSH after OpenVPN started
### since the IP address of tunnel interface (for VPN) has been listened by sshd,
### which is defined in the sshd_config.rtable0 and sshd_config.rtable1 config file.
### Note: make sure the line 'sshd_enable="YES"' has bee commented out
### or removed from /etc/rc.conf file. We will start sshd in /etc/rc.local file.
/usr/sbin/setfib 0 /usr/sbin/sshd -f /etc/ssh/sshd_config.rtable0
/usr/sbin/setfib 1 /usr/sbin/sshd -f /etc/ssh/sshd_config.rtable1

### Flush PF rules after OpenVPN and SSH daemons started
### Note: the reason is because there are some settings related to these VPN
### interface tun0 and tun1. So we need to flush the PF rules after OpenVPN loaded.
/sbin/pfctl -f /etc/pf.conf

Comment out or remove sshd_enable="YES" line in /etc/rc.conf:
# vim /etc/rc.conf
### Note: commented out for multiple WAN routes setup. SSH daemon will be started in /etc/rc.local file.
#sshd_enable="YES"

Let's start OpenVPN server manually now:

# setfib 0 /usr/local/etc/rc.d/openvpn_wan0 onestart
Starting openvpn_wan0.
add net 10.8.0.0: gateway 10.8.0.2

# setfib 1 /usr/local/etc/rc.d/openvpn_wan1 onestart
Starting openvpn_wan1.
add net 10.9.0.0: gateway 10.9.0.2

Make sure the network and the gateway have been correctly added:

# setfib 0 netstat -rn | egrep '10.[8|9].0'
10.8.0.0/24 10.8.0.2 UGS 0 0 tun0
10.8.0.1 link#6 UHS 0 0 lo0
10.8.0.2 link#6 UH 0 0 tun0
10.9.0.1 link#7 UHS 0 0 lo0
10.9.0.2 link#7 UH 0 0 tun1

# setfib 1 netstat -rn | egrep '10.[8|9].0'
10.8.0.2 link#6 UH 0 0 tun0
10.9.0.0/24 10.9.0.2 UGS 0 0 tun1
10.9.0.2 link#7 UH 0 0 tun1

In some cases, the network did not be added successfully, you will need to add the network maually:
# setfib 0 route add 10.8.0.0/24 10.8.0.2
# setfib 1 route add 10.9.0.0/24 10.9.0.2

You should have seen two different IP addresses are listening port 1194:

# netstat -an | grep 1194
udp4 0 0 11.11.11.11.1194 *.*
udp4 0 0 22.22.22.22.1194 *.*

You should have seen two different process IDs are running:

# ls -l /var/run/openvpn*.pid
-rw-r--r-- 1 root wheel 6 Dec 5 15:49 /var/run/openvpn_wan1.pid
-rw-r--r-- 1 root wheel 6 Dec 5 15:44 /var/run/openvpn_wan0.pid

# ifconfig | grep tun
tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> metric 0 mtu 1500
tun1: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> metric 0 mtu 1500

Let's start ssh daemon now:
# /usr/sbin/setfib 0 /usr/sbin/sshd -f /etc/ssh/sshd_config.rtable0
# /usr/sbin/setfib 1 /usr/sbin/sshd -f /etc/ssh/sshd_config.rtable1

See port 22 is binding to which IP addresses:
# netstat -an | grep 22
tcp4 0 0 11.11.11.11.22 *.* LISTEN
tcp4 0 0 192.168.100.1.22 *.* LISTEN
tcp4 0 0 192.168.200.1.22 *.* LISTEN
tcp4 0 0 10.8.0.1.22 *.* LISTEN
tcp4 0 0 22.22.22.22.22 *.* LISTEN
tcp4 0 0 192.168.100.2.22 *.* LISTEN
tcp4 0 0 192.168.200.2.22 *.* LISTEN
tcp4 0 0 10.9.0.1.22 *.* LISTEN
tcp4 0 0 *.22 *.* LISTEN

To show listening/opening TCP port:
# sockstat -Ptcp

To show listening/opening UDP port:
# sockstat -Pudp

Note: man sockstat

Restart the machine and re-check everything is fine after reboot:
# sync; sync; sync; reboot

Edit pf.conf PF firewall setting:
# vim /etc/pf.conf
wan_if0 = "wan0"
wan_if1 = "wan1"

dmz_if = "dmz0"
lan_if = "lan0"

### Note: the reason why we specified VPN network below is because we want to avoid
### PF parse host specification error. Since PF runs before OpenVPN starts,
### PF could not figure it out the VPN network until OpenVPN started.
vpn_if0 = "tun0"
vpn_net0 = '"10.8.0.0/24"'
vpn_if1 = "tun1"
vpn_net1 = '"10.9.0.0/24"'

icmp_types = "{echoreq echorep unreach}"

### [NAT] the VPN connections (for access to the remote secure networks)
nat on $wan_if0 from $vpn_net0to any -> $wan_if0
nat on $wan_if1 from $vpn_net1 to any -> $wan_if1

### [VPN] Inbound from Outside
### Establish VPN connections on each WAN interface.
pass in quick on $wan_if0 proto udp from any to $wan_if0 port 1194 keep state rtable 0
pass in quick on $wan_if1 proto udp from any to $wan_if1 port 1194 keep state rtable 1

### [SSH] Inbound from VPN to ANY
pass in quick log on $vpn_if0 proto tcp from $vpn_net0to any port 22 flags S/SA keep state rtable 0
pass in quick log on $vpn_if1 proto tcp from $vpn_net1 to any port 22 flags S/SA keep state rtable 1

### [SSH] Outbound to LAN
pass out quick log on $lan_if proto tcp from {$dmz_if/24 $lan_if/24 $vpn_if0/24} to $lan_if/24 port 22 flags S/SA keep state rtable 0
pass out quick log on $lan_if proto tcp from {$vpn_net1} to $lan_if/24 port 22 flags S/SA keep state rtable 1

### [SSH] Outbound to DMZ
pass out quick log on $dmz_if proto tcp from {$dmz_if/24 $lan_if/24 $vpn_if0/24} to $dmz_if/24 port 22 flags S/SA keep state rtable 0
pass out quick log on $dmz_if proto tcp from {$vpn_net1} to $dmz_if/24 port 22 flags S/SA keep state rtable 1

### [ICMP] Inbound from VPN
pass in quick on $vpn_if0 proto icmp from $vpn_net0to any icmp-type $icmp_types rtable 0
pass in quick on $vpn_if1 proto icmp from $vpn_net1 to any icmp-type $icmp_types rtable 1

### [ICMP] Inbound from Outside
pass in quick log on $wan_if0 proto icmp from any to $wan_if0 icmp-type $icmp_types rtable 0
pass in quick log on $wan_if1 proto icmp from any to $wan_if1 icmp-type $icmp_types rtable 1

### [ICMP] Outbound to Outside
pass out quick on $wan_if0 proto icmp from $wan_if0 to any icmp-type $icmp_types rtable 0
pass out quick on $wan_if1 proto icmp from $wan_if1 to any icmp-type $icmp_types rtable 1

### [ICMP] Outbound to LAN
pass out quick log on $lan_if proto icmp from {$lan_if $dmz_if/24 $vpn_if0/24} to $lan_if/24 icmp-type $icmp_types rtable 0
pass out quick log on $lan_if proto icmp from {$vpn_net1} to $lan_if/24 icmp-type $icmp_types rtable 1

### [ICMP] Outbound to DMZ
pass out quick log on $dmz_if proto icmp from {$dmz_if $lan_if/24 $vpn_if0/24} to $dmz_if/24 icmp-type $icmp_types rtable 0
pass out quick log on $dmz_if proto icmp from {$vpn_net1} to $dmz_if/24 icmp-type $icmp_types rtable 1

Watch the pflog logged packets in real time:
# tcpdump -n -e -ttt -i pflog0

Watch the icmp packect of an interface:
# tcpdump -ni wan0 icmp
# tcpdump -ni wan1 icmp
# tcpdump -ni dmz0 icmp
# tcpdump -ni tun0 icmp
# tcpdump -ni tun1 icmp

# tcpdump -tt -i tun0

# tail /usr/local/etc/openvpn/openvpn-status.log

# tail /var/log/openvpn_wan0.log

# tail /var/log/openvpn_wan1.log

# tail /var/log/messages

Download OpenVPN 2.2.1 client for Windows:
http://openvpn.net/index.php/open-source/downloads.html

Once this is installed you will need to copy the following files from your server /usr/local/etc/openvpn/keys directory to the Windows PC C:\Program Files\Openvpn\config directory (this should be done in as secure a manner as possible, i.e. USB Stick or floppy rather than email!!!):

ca.crt
client1.crt
client1.key

Note: For the next client you will need to copy the client2.crt and client2.key files to prevent issues later.

Create VPN config file:

create a myvpn.ovpn file in C:\Program Files\Openvpn\config and insert the following:

client
remote nic1.myserver.com 1194
#remote nic2.myserver.com 1194
dev tun
comp-lzo

ca ca.crt
cert client1.crt
key client1.key

# Set log file verbosity.
verb 3

Turn off the firewall for the new Interface:

On Windows XP, the firewall can be accessed by:
Control Panel -> Security Center -> Windows Firewall -> Advanced. In the Network Connection Settings control, uncheck the box corresponding to the TAP-Win32 adapter.

Now right-click the OpenVPN Icon in your Taskbar and click "connect". Once connected try pinging the remote interface and check (using tracert) that the remote network is available. Use tcpdump on the server to check traffic too:

On Windows 7, you must run OpenVPN as an administrator by:

right click on openvpn-gui-1.0.3.exe > Property > Shortcut > Run this program as an administrator

or

right click on openvpn-gui-1.0.3.exe > Property > Compatibility > Run this program as an administrator

cmd> tracert

cmd> ping 10.8.0.1
cmd> ping 10.9.0.1

Use Firewall's VPN IP address to connecto the firewall:
cmd> putty.exe username@10.8.0.1
cmd> putty.exe username@10.9.0.1

Note: If you want to ssh to the IP address of the firewall's LAN/DMZ interface, make sure you do set up your sshd to listen to these IP addresses with proper routing table.

Use FreeBSD as a VPN client:

# cd /usr/ports/security/openvpn
# make config-recursive
# make config-recursive
# make config-recursive

Note: run "make config-recursive" few times. You may enable a dependency the first time through that has its own configuration options.

# make install

Link the openvpn startup script:
# ln -s /usr/local/etc/rc.d/openvpn /usr/local/etc/rc.d/openvpn_client

Create OpenVPN directory:
# mkdir -p /usr/local/etc/openvpn/keys

Put following three files to /usr/local/etc/openvpn/keys:
ca.crt
client1.crt
client1.key

Protect our certificate keys directory and config files for only root can see it.
# find /usr/local/etc/openvpn/keys/ -type d -print0 | xargs -0 -I @ sh -c 'chmod 700 @ ; chown root:wheel @'
# find /usr/local/etc/openvpn/keys/ -type f -print0 | xargs -0 -I @ sh -c 'chmod 400 @ ; chown root:wheel @'
# find /usr/local/etc/openvpn/ -type f -name '*.conf' -print0 | xargs -0 -I @ sh -c 'chmod 400 @ ; chown root:wheel @'

You will need to copy the client.conf instead of server.conf from /usr/local/share/doc/openvpn/sample-config-files directory:
# cp /usr/local/share/doc/openvpn/sample-config-files/client.conf /usr/local/etc/openvpn/openvpn_client.conf

Edit openvpn_client.conf:
# vim /usr/local/etc/openvpn/openvpn_client.conf
client
dev tun
proto udp

# The hostname/IP and port of the server.
# You can have multiple remote entries
# to load balance between the servers.
remote 11.11.11.11 1194
;remote my-server-2 1194

# Downgrade privileges after initialization (non-Windows only)
user nobody
group nobody

# Try to preserve some state across restarts.
persist-key
persist-tun

# Certificates for VPN Authentication
ca /usr/local/etc/openvpn/keys/ca.crt
cert /usr/local/etc/openvpn/keys/client1.crt
key /usr/local/etc/openvpn/keys/client1.key

# Verify server certificate by checking
# that the certicate has the nsCertType
# field set to "server". This is an
# important precaution to protect against
# a potential attack discussed here:
# http://openvpn.net/howto.html#mitm
ns-cert-type server

# vim /etc/rc.conf
openvpn_client_enable="YES"
openvpn_client_configfile="/usr/local/etc/openvpn/openvpn_client.conf"

Start OpenVPN client:
# /usr/local/etc/rc.d/openvpn_client start

# tail -F /var/log/messages

# ifconfig
tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> metric 0 mtu 1500
options=80000<LINKSTATE>
inet 10.8.0.6 --> 10.8.0.5 netmask 0xffffffff
Opened by PID 98483

Display the full output line of ps process status command:
# ps -auxww | grep openvpn
nobody 1015 /usr/local/sbin/openvpn --cd /usr/local/etc/openvpn --daemon openvpn_wan0 --config /usr/local/etc/openvpn/openvpn_wan0.conf --writepid /var/run/openvpn_wan0.pid
nobody 1035 /usr/local/sbin/openvpn --cd /usr/local/etc/openvpn --daemon openvpn_wan1 --config /usr/local/etc/openvpn/openvpn_wan1.conf --writepid /var/run/openvpn_wan1.pid

# ls -l /var/run/openvpn*
-rw-r--r-- 1 root wheel 6 Dec 21 12:53 /var/run/openvpn_client.pid

===
# If enabled, this directive will configure
# all clients to redirect their default
# network gateway through the VPN, causing
# all IP traffic such as web browsing and
# and DNS lookups to go through the VPN
# (The OpenVPN server machine may need to NAT
# or bridge the TUN/TAP interface to the internet
# in order for this to work properly).

push "redirect-gateway def1 bypass-dhcp"
===
Fixed IP addresses for OpenVPN clients

# vim /usr/local/etc/openvpn.conf
# EXAMPLE: Suppose you want to give
# Thelonious a fixed VPN IP address of 10.9.0.1.
# First uncomment out these lines:
client-config-dir ccd
route 10.8.0.0 255.255.255.252
# Then add this line to ccd/Thelonious:
# ifconfig-push 10.9.0.1 10.9.0.2

# mkdir /usr/local/etc/openvpn/ccd

Modify the permission:
# find /usr/local/etc/openvpn/ccd -type d -print0 | xargs -0 -I {} chmod 755 {}
# find /usr/local/etc/openvpn/ccd -type f -print0 | xargs -0 -I {} chmod 644 {}

The ifconfig push pushes addresses in a /30 network unless you are using
infrastructure mode.

the correct notation should be in a /30 network e.g.

ifconfig-push 10.200.200.9 10.200.200.10
http://openvpn.net/archive/openvpn-users/2007-07/msg00084.html

In default mode openvpn assigns miniature /30 networks to each VPN client:

server
10.8.0.0 : network address
10.8.0.1 : server IP address
10.8.0.2 : virtual remote endpoint; only used for routing
10.8.0.3 : network broadcast address

first client
10.8.0.4 : network address
10.8.0.5 : virtual remote endpoint; non pingable; only used for routing
10.8.0.6 : client IP address
10.8.0.7 : network broadcast address

second client : 10.8.08 - 10.8.0.11
third client : 10.8.012 - 10.8.0.15

etc. So a valid network block for a client is 10.8.0.24 - 10.8.0.27:
10.8.0.24 : network address
10.8.0.25 : virtual remote endpoint; non pingable; only used for routing
10.8.0.26 : client IP address
10.8.0.27 : network broadcast address

to use this network the ccd file needs to contain
Code:
ifconfig-push 10.8.0.26 10.8.0.25

(note that the HOWTO on the openvp.net erroneously swaps the IPs!)
http://forums.openvpn.net/topic8215.html
===
Difference Between TUN and TAP

In computer networking, TUN and TAP are virtual network kernel devices. They are network devices that are supported entirely in software, which is different from ordinary network devices that are backed up by hardware network adapters.

TAP (as in network tap) simulates an Ethernet device and it operates with layer 2 packets such as Ethernet frames. TUN (as in network TUNnel) simulates a network layer device and it operates with layer 3 packets such as IP packets. TAP is used to create a network bridge, while TUN is used with routing.

Packets sent by an operating system via a TUN/TAP device are delivered to a user-space program that attaches itself to the device. A user-space program may also pass packets into a TUN/TAP device. In this case TUN/TAP device delivers (or "injects") these packets to the operating system network stack thus emulating their reception from an external source.
===
As far as I could see, tap allows netbios broadcast to propagate across the
VPN so Windows clients can find network shares by PC name even if no WINS
server is being used; with tun, either you find network shares in some other
way (by IP or DNS, for example) or you need to have WINS running (which often
amounts to "wins proxy = yes" in smb.conf).

On the other hand, I do not feel comfortable in having VPN clients appear as
if physically attached to the LAN (this is what bridging with TAP amounts to)
and prefer routing with tun as it makes easier to write appropriate firewall
rules for VPN clients.
===
if it's ok to create vpn on layer 3 [ one more hop between subnets ] - go for tun.

if you need to bridge two ethernet segments in two different locations - then use tap. in such setup you can have computers in the same ip subnet [ eg 10.0.0.0/24 ] on both ends of vpn, and they'll be able to 'talk' to each other directly without any changes in their routing tables. vpn will act like ethernet switch. this might sound cool and is useful in some cases but i would advice not to go for it unless you really need it. if you choose such layer 2 bridging setup - there will be a bit of 'garbage' [ that is broadcast packets ] going across your vpn.
===
I always set up tun. Tap is used by ethernet bridging in OpenVPN and introduces an unprecendented level of complexity that is simply not worth bothering with. Usually when a VPN needs to be installed, its needed now, and complex deployments don't come fast.
===
I chose "tap" when setting up a VPN for a friend who owned a small business because his office uses a tangle of Windows machines, commercial printers, and a Samba file server. Some of them use pure TCP/IP, some seem to only use NetBIOS (and thus need Ethernet broadcast packets) to communicate, and some I'm not even sure of.

If I had chosen "tun", I would probably have faced lots of broken services — lots of things that worked while you are in the office physically, but then would break when you went off-site and your laptop couldn't "see" the devices on the Ethernet subnet anymore.

But by choosing "tap", I tell the VPN to make remote machines feel exactly like they're on the LAN, with broadcast Ethernet packets and raw Ethernet protocols available for communicating with printers and file servers and for powering their Network Neighborhood display. It works great, and I never get reports of things that don't work offsite!
===
I started out using tun, but switched to tap since I didn't like the use of a /30 subnet for each PC (I need to support Windows). I found that to be wasteful and confusing.

Then I discovered the "topology subnet" option on the server. Works with the 2.1 RCs (not 2.0), but it gives me all the advantages of tun (no bridging, performance, routing, etc) with the convenience of one (sequential) IP address per (windows) machine.
===
What All Ports Are Rrequired By Domain Controllers And Client Computers?

Active Directory communication takes place using several ports. These ports are required by both client computers and Domain Controllers. As an example, when a client computer tries to find a domain controller it always sends a DNS Query over Port 53 to find the name of the domain controller in the domain.

The following is the list of services and their ports used for Active Directory communication:

UDP Port 88 for Kerberos authentication
UDP and TCP Port 135 for domain controllers-to-domain controller and client to domain controller operations.
TCP Port 139 and UDP 138 for File Replication Service between domain controllers.
TCP and UDP Port 389 for LDAP to handle normal queries from client computers to the domain controllers.
TCP and UDP Port 445 for File Replication Service
TCP and UDP Port 464 for Kerberos Password Change
TCP Port 3268 and 3269 for Global Catalog from client to domain controller.
TCP and UDP Port 53 for DNS from client to domain controller and domain controller to domain controller.
Opening above ports in Firewall between client computers and domain controllers, or between domain controllers, will enable Active Directory to function properly.

http://www.windowsnetworking.com/kbase/WindowsTips/Windows2000/AdminTips/ActiveDirectory/WhatAllPortsAreRrequiredByDomainControllersAndClientComputers.html
===
Reference:
http://blog.ijun.org/2011/12/set-up-openvpn-server-on-freebsd.html
http://kuapp.com/2010/07/01/openvpn-with-freebsd-pf-and-windows-xp.html
http://www.secure-computing.net/wiki/index.php/FreeBSD_OpenVPN_Server/Routed
http://en.wikipedia.org/wiki/TUN/TAP
http://openvpn.net/archive/openvpn-users/2007-02/msg00184.html
http://serverfault.com/questions/21157/should-i-use-tap-or-tun-for-openvpn
http://forums.freebsd.org/showthread.php?t=15213
http://en.wikipedia.org/wiki/OpenVPN

Tuesday, May 29, 2012

Game Engine - Unity 3D

Game Engine - Unity 3D

Unity is a feature rich, fully integrated development engine for the creation of interactive 3D content. It provides complete, out-of-the-box functionality to assemble high-quality, high-performing content and publish to multiple platforms. Unity helps indie developers and designers, small and major studios, multinational corporations, students and hobbyists to drastically reduce the time, effort and cost of making games.

遊戲採用 Unity 3D 引擎,而此引擎目前尚未支援 WebGL
應該是自己專有的一套引擎,可支援 DirectX 和 OpenGL

如果是Unity 3D引擎的話,就是跟King's Bounty網頁版的一樣
這引擎是號稱"Develop once, publish everywhere"(跟當年Java的口號很像)
現在網頁遊戲要有不錯的效果都是用這套

http://unity3d.com/
http://www.pcdvd.com.tw/showthread.php?t=969699&page=2&pp=10

學習的自我要求


學習的自我要求

學生時代我一直在想一個問題,究竟是問題導向的學習方式較佳,還是技術導向較佳?舉例來說,我想寫個留言版,我發現要完成最基本的需求,我得學 HTML、PHP 和 MySQL,於是我針對我要做的功能學相關的語法,最後完成留言版。可能資料庫設計沒用到正規化,網站外觀沒用到 CSS,一些動態功能用 PHP 實現而非 JavaScript。相較於從頭學寫網站所需的各項技術,這個作法比較務實,我用較少的時間完成目標。極端地看,用問題導向的解法時,我可能找到相關的程式碼,用它們拼貼出結果,但不確定各塊在做什麼。技術導向的解法,可能要先花一段時間弄清楚寫網站需要的技術,再各別學個基本能力,接著開始實作。當然,世界上不是只有 0 和 1,我們常混著用問題導向和技術導向的方式解問題。這裡我說用問題導向的意思,是指偏向用問題導向。
不只做產品,做研究時也是類似的情況。問題導向就是從問題開始往下挖,看前人做那些方法,將自己不熟的部份補起來,有了必要的知識後,就能回頭解決問題。而技術導向的做法,我可能會加強基礎學科知識,視類別而定可能是機率和線代,或是計算機組織和編譯器,接著才開始思考如何解問題(或重新定義問題)。
以前我沒什麼時間觀念,通常是技術導向的作法,覺得有趣就看我能從多底層開始往上學。比方說使用 Java Collections Framework 時,我會弄清楚我要用的資料結構怎麼運作的的,時間複雜度是多少。實際上大部份的使用情境不需要這些知識,稍微讀一下文件的注意事項即可。要用 machine learning 的工具,就找適合的 machine learning 課程講義來讀,弄清楚要用到的 model 怎麼運作。直到現在,我無法確認這些對於使用工具究竟幫了多少忙,當初花的時間對應到目前的幫助,真的划算嗎?
最近我試著用問題導向的方式解問題,初期進展確實比較快,不過後來遇到一些狀況,才發覺技術導向的好處。有些情況,我們根本不明白問題出在那裡,有太多可能。舉例來說,前幾天我和同事發覺不同使用者登入同一頁面,操作速度卻差了一大截。有許多可能原因:網路連線 、網頁框架、資料庫等。做了一些初步測試,我懷疑是 MySQL 根據歷史記錄做錯 query optimization。於是用 EXPLAIN  看相同的 SQL 配合不同使用者 ID,結果發現 MySQL 執行 query 的方式有細微差異,造成取出某些使用者的資料時,用較慢方式執行 SQL。於是讓 MySQL 重分析表內的資料後,問題就解決了。若不是之前稍微看過 MySQL 執行 query 的相關知識,不會這麼快就直指問題核心。也許就會用別的方式繞開這問題,一輩子都不知道怎麼解它。待發生類似問題時,又用別的方式繞開它,長遠來看浪費開發時間又增加維護成本。
另一個反面的例子是,我一直沒用 lock 的習慣,教科書告訴我們 deadlock 很可怕,所以我會想辦法避開用 lock。結果最近有個小專案因為沒用 lock,真的發生 race condition 造成有一點點資料不正確。實作前我明白會有這樣的狀況,但這個問題對我們的目的沒什麼影響,衡量開發時間後我決定寫下註解強調會有 race condition,而選擇不處理它。對照最近的體悟,我明白這樣下去我不可能學會用 lock,這不是個好現象,所以又找時間回頭看 MySQL 怎麼 lock table,結果比想像中來得簡單,之前多慮了。
有很多類似這種的逃避例子,像多人一起寫程式容易有問題,於是大家傾向將功能切乾淨,每人寫沒有交集的功能,最後再來整合。但是,對照近年來的軟體開發的趨勢,愈早整合愈容易解決問題。一個人開發容易有盲點,互相協助可以降低初期錯誤,以利後期整合。問題是,要能順利地多人共同開發,得做對不少事才行。像是版本管理系統、coding style、天天合併程式等。每一項都需要時間練習。若一個人開發時有好好練習,和別人合作時會減少許多問題,比較容易推動密集的團隊合作。
在面試別人時,我發覺一個問題:有些人學到的技能剛好只能應付他負責的專案。問題在於,若平時我們都處理簡單的專案,要怎麼轉去負責困難的專案?兩者之間有個斷層。像這類的例子不勝杖舉。比方說要從資料庫表 A 取出部份資料塞入表 B,最「簡單」的作法是寫個程式用 SQL 取出資料,用程式做些處理再用 SQL 一筆筆寫入表 B。另一個作法是直接用一個較複雜的 SQL 直接搞定。當資料量大時,後者執行速度會快上不少。並且,學會後者的寫法後,之後只要花一點時間就能處理類似的情況,不用再寫一個小程式。其它像寫程式的習慣、用程式工具的習慣,都是一樣的。多數情況我們可以用最「不費力」的作法滿足需求,但長遠來看卻是毫無長進。實際上有更有效率的作法,這裡的效率包含開發時間和軟體品質。
對照大學的程式來看,這一年來我以為自己程式已寫得頗有品質了,雖然知道一些小問題,但覺得並不迫切,也不知怎麼查相關的解法,就放著它們。最近翻了一下Effective Java 第二版和 Growing Object-Oriented Software Guided by Tests,才發覺還有太多的東西要學,自己和資深的軟體工程師差了一大截,照我目前的學習方式,和他們的差距只會拉大不會縮短。若只專注完成眼前的工作,我永遠無法補足和更難工作之間的差距,這才驚覺問題導向的盲點。
走過天平的兩端後,我現在的體悟是,得雙向夾擊來解決問題。一方面用問題導向解問題以符合時程,確保時間有花在刀口上。另一方面再抽時間用技術導向的方式強化自己的實力。如此一來,在完成當下的專案的同時,也有一點一滴地補足技術斷層,取得挑戰更難專案的機會。題外話,英文也是很重要的「技術」,這一年來我半強迫地讓自己盡量搜英文文件,思考關鍵字比以前敏銳不少,閱讀速度也變快,獲得答案的速度比以前快、品質也較佳。

Reference:
http://fcamel-fc.blogspot.ca/2010/07/blog-post.html

Problem Solving 的技巧 (3):因事制宜


Problem Solving 的技巧 (3):因事制宜

不知不覺,這類文章可以寫到第三篇,感到頗意外的。前兩篇是:
昨天看到 TonyQ 在 Soft_Job 寫的文章:《Re: [閒聊] 你在開發程式時,是重視績效還是品質。覺得深有同感,摘錄幾段如下:
對我們來講很多其實可以是 nice to have 的東西, 都會被我們當成 must have。

這個判斷是經驗跟 domain 累積下來的,沒有公式,沒有法則, 做久了你就是會知道什麼架構之後會一直噴 exception ,

而且你還會知道等他出事時你一定沒辦法好好處理, 所以你要在這個當下把它處理掉。

...

觀察他們影響到哪些地方,你有沒有能力測試他們, 還有你的環境允不允許這件事情帶來的不穩定性。

一般來講,如果是我個人自己的專案,我非常不介意大改, 只要我後續還有時間可以處理這些出來的問題。

對公司或者客戶的專案,我會採取相對保守的態度。

這是對風險管理的策略問題。
在 Plurk 上貼了這篇文章後,Thinker 和 qrtt1 提出另一面看法,強調「早期發現,早期治療」的好處。於是決定借題寫一下自己的看法。
在討論這個議題前,我想先強調,為了方便聚焦討論,我們往往會先偋除一些條件,或是先依自己的假設開始論述。而有爭論的地方,往往卻是這些隱藏的前提。好比說「寫測試碼重不重要?」單單這樣一個命題,只能討論出很模糊的概念,不論支持與否,看起來都有些道理。但加入一些條件後,像是「這是長期開發並有多人參與的專案」或是「後天要交的雛型」,相信大家對此會有不同的答案,也會少一些分歧。相關的想法,可以參考《黑天鵝效應》。 這篇有摘要讀書心得,其中敘事謬誤和戲局謬誤啟發我這個觀點。
我的想法是,在達到必要需求的前提下,依個人以及團隊的能力,看看能提昇多少品質。問題在於,何謂 must have、何謂 nice to have,每個人的見解不同。爭議點在於,每個人的能力不同,導致評估的實作成本不同。一樣是 nice to have,有些被認同可以做,有些則否。
( ps. 這篇文章不討論規格不明確的問題,這是另一個大議題。 )
舉例來說,A 認為現在重構只能提升一點品質,卻要花兩天;B 認同 A 評估的品質,但 B 覺得只要花半天。所以 A 覺得不划算,而 B 持相反意見。從這樣的評估結果很難說 A 或 B 誰對誰錯,也許 A 沒重構經驗不信任重構,也可能 A 有豐富的經驗,估得比 B 準。
在體認到個人能力不同、習慣不同的前提下,對自己的要求是盡量提高自己的能力,多付出時間提高品質,減少日後維護成本並能練功,形成正向循環對其他人來說,看對方是那種人,是「過」或「不及」,再從另一個角度和對方討論。
舉例來說,若團隊成員不熟或不認同 unit test。當下就要出貨了,這時硬要大家學習並實作測試,並不適合 (不如當「負面教材」,待下次專案的開頭提出討論)。但若成員有過相關經驗,同樣時程下,就能針對核心程式補些測試,花最小成本減少最多風險。當成員嘗到測試甜頭,不小心寫過多測試碼時,和對方討論優先順序,減少寫 C/P 值低的測試碼。
回頭看品質的問題,能力愈強、經驗愈豐富,增加 nice to have 的成本愈低,自然能前期處理,減少技術債。反之,在時程的考量下 (ship or die),能力和經驗不夠時,得選擇先放過一些潛在問題,日後仍有需求時,再花更多成本補救。先活下去,才有更多的本錢來還債。
因事制宜說來簡單,只有四個字而已。實務上卻需經年累月的經驗,不止要考量時程、未來需求變化等項目,也要留意每個人的能力和習性,才能在當下找到較為適當的平衡點。而增加經驗的方法,如同杜書伍在《打造將才基因》裡所言,除了比別人投入更多時間外,沒有其它的捷徑。
2011-09-25 更新
看到 Thinker 提了他的相關看法:《程式碼要清的多乾淨?》,裡面提了不錯的建議:
除了能力不同之外,個人的膽量、積極態度和價值觀也同樣影嚮著評估的差異。 我強調的是,積極態度和個人的能力呈正相關。
...
要評估自己的能力,並適度的承受犯錯的風險。
勇於嘗試、有控制性地犯錯,對於學習很有幫助。我覺得在我學習的前一大段生涯裡,過於謹慎而不敢犯錯,以致於學習速度較為緩慢。而我認識一些晚起步、卻成長相當快的朋友,都具有大膽嘗試、不斷犯錯的特質。他們快速地累積經驗,並培養出更多的膽識和更大的企圖心。

Reference:
http://fcamel-fc.blogspot.ca/2011/09/problem-solving-3.html

Problem Solving 的技巧 (2):別把解法當作問題定義


Problem Solving 的技巧 (2):別把解法當作問題定義

《真正的問題是什麼?你想通了嗎?》裡提到一點重要的觀念,別把別人的解法當作問題。最近累積了不少實例,比較能清楚地表達這個想法。

「把解法當作問題」的意思以及它帶來的負面影響

在分配工作時,為了減少溝通成本,常會隱藏一些訊息,只告訴合作同伴他「應該」知道的事,而省略了原始的動機和考量。問題在於,實際執行的人通常比對方清楚細節,很多問題在執行後才會浮現。但分配到工作的人不清楚前因後果,即使查覺不對勁,也無法 (或不願) 做出進一步修正。
好一點的情況,在分工前會先進行討論。然而,討論常會陷入各執一詞的局面,討論雙方作法優缺點後,會發覺找不到共識。實際上這只是眾多解法的兩個提案而已,若深入討論雙方作法背後的動機,各自預先假定的前提,會發覺尋找共識並不困難,畢竟,雙方是在解同一個問題。
在需求一樣,假設一致的情況下,兩個頭腦清楚願意討論的人,沒道理找不到共識,只是我們習慣省略前提,也沒先釐清需求,直接討論解法,才會覺得有個看不見的牆擋在中間,對話難以有交集,我不認同你認同的點,你也不認同我認同的點。
當我開始不預設立場後,看清楚很多事,我第一件事不是評斷大家作法的好壞,而是先問這樣做的考量為何?從考量的點會問出需求,從需求和考量的點會發覺矛盾,從而發現隱藏的假設。接著就能討論假設是否成立,或是不成立又會如何?很可能所有的點會重新洗牌,然後聚焦出更清楚的需求。
按部就班從需求討論各種作法,尋找大家認同的假設,接著在假設下收斂可能的作法,或是列出有影響但還不確定的點,然後評估何時由誰來釐清不確定的點,再來做更細的決定。這樣做下來,通常會有大家都滿意的成果,互動的過程中也會相處愉快。

以開發網站為例

舉例來說,A、B 要合寫一個網站,A 想用 PHP 直接寫,因為他覺得大家都會寫 PHP,寫起來也快;B 想用 Python + Django,因為他覺得日後比較好維護。
A 和 B 的想法都沒錯,但是一討論就陷入死結,這是很吊詭的事,兩個人想法都對,也在做同一件事,為什麼不會有共識?
這表示上述的論點一定有其中一點是錯的,仔細思考會發覺,其實 A 和 B 「沒有在討論同一個問題」,也沒有在同樣的立足點 (假設) 上討論。
  • A 可能假設時程很趕,沒有時間慢慢從頭學 Django,若他不會 Python,還得多學 Python,而且他可能不想學新東西,覺得 PHP 用起來沒什麼問題,為什麼要自找麻煩用別的作法?
  • B 可能假設學習 Python + Django 很簡單,即使時程很趕也還好,或是時程可以再談,沒有人說一定要什麼時候出來,在 B 認為可以來得及做完的前提下,B 認為要認真考量維護的事。
從 A 和 B 隱藏的假設可看出一些衝突:學習新東西花的時間成本、對於時程的認知、是否需要重視維護。而這些假設大概和 A、B 各自過去的經驗有關,比方說 B 收拾過用 PHP 開發的爛攤子,A 沒有和多人共同開發過稍具規模的專案。若 A、B 能和對方說明自己的前提,就有機會順著前提再討論為何有這樣的前提,彼此較能接受對方的看法。
然後再對照目前的需求,會發覺有些前提不再成立,或是更為重要。像是 A 當初收拾的爛攤子是別的因素造成的,可能是時程太趕或開發者習慣不好,主因不是 PHP。
將這些事都攤出來討論後,會發覺幾個立基不牢靠的推論:
  • 為了方便維護,要用 Python + Django。
  • 為了快速開發,要用 PHP。
這是將需求和解法綁在一起討論,但是實際的情況是,方便維護的作法不只有 Python + Django,快速開發的作法也不只有 PHP。視開發人員的經驗,將兩者反過來陳述也可能成立。
所以,真正考量的點應該是:
  • 先確定時程和功能需求,才能判斷開發速度要多快。
  • 確定日後是否需要擴充,擴充的程度和時程,從而判斷維護的成本。
然後列出相關選項,需注意的是,要依現有人員的能力列,而不是「一般」的認知。若時程不是太緊,可以先排時間評估各種方案,像是 Python 不只有 Django 一家 framework,也許可以考慮 Flasky 或 Pyramid;PHP 也不是沒 framework,若願意犠牲多一點執行速度,可以研究 PHP 的 framework;或是另找別的路,用 Ruby on Rails 或用 Java 體系的解法。
要注意的是,過猶不及,當考量的點過於發散時,不確定的點太多,沒有心力一一確認,討論會流於空泛。這時要列好需求,依現有人員的能力做些假設,先達成局部共識再繼續討論,比較容易聚焦。像是因為內部人員最熟 PHP 和 Python,用這兩者風險較低,所以只考慮這兩個體系的解法。

結語

類似的例子很常發生在日常生活的討論裡,不限於技術方面。隱藏的前提不見得都是技術議題,人性是很複雜的。關鍵在於別把別人提的解法當問題,自己也別預設立場著重在說明解法,而沒說明問題需求和自己的假設 (前提)。
在討論過於發散時,要先取得共識排除一些考量。即使共識的原因只是「不為什麼,我們都覺得不重要」,也是不錯的作法。可以先聚焦往後討論,待討論到後面有更明確的想法,或是再和第三個人討論時,可能會查覺原本的前提有問題,這時再回頭修正它們,重新沙盤推演一遍,得到更確實的解法。有了紮實的共識,清楚各項決策的前因後果後,之後遇到各種變動,都能迅速明確地處理。

Reference:
http://fcamel-fc.blogspot.ca/2011/04/problem-solving-2_6909.html

Sunday, May 27, 2012

Problem Solving 的技巧 (1):系統設計是一連串的取捨


費曼和杜書伍可說是影響我思考方式最深的人,即使重覆閱讀、重新思考他們話,仍能獲得不少新體悟。今天偶然發現《真正的問題是什麼?你想通了嗎?》的推薦序是杜書伍寫的,看了以後收獲良多,看了這麼多書,這還是頭一回看了推薦序而覺得有用。
文中提到:
釐清問題的真實性後,面對真正的問題時,須有一個認知:甚少問題能以單一方案解決,而須由不同面向,分頭淡化問題。...這些不同面向的解法,單獨用都只能解決局部問題,但配套提出後,卻能大幅降低問題的嚴重性,到一可接受的範圍內。
企業經營無時不刻面臨問題的發生與解決,在問題決的過程中會發現,現實生活中並沒有可「百分之百」被解決的問題。誠如書中所言:「每一個解決方案都是下一個問題的根源」,一個有利於某面向的方案,代價往往是犠牲另一面向的利益。因此,如何透過溝通、妥協的過程,尋求最適的解法而非完美的解法,將問題的衝擊降到多數人可接受的範圍內,即為好的解決方式,否則反而可能適得其反,滋生新的問題。
看了這段話,更明白杜書伍花了多少歲月與心力實踐他在《打造將才基因》裡說的方法,完全是實實在在的真工夫。用精簡的文字描述出極為深刻的道理,實在是太厲害啦!
舉例來說,使用資料庫時,需同時擔心寫入速度和確保不會損失資料。若每次寫入時都寫入硬碟,可以確保資料正確,但卻會很慢;若先寫入記憶體,集合成一批再一起寫入硬碟,可以大幅提昇速度,但若資料庫伺服器當掉,卻會喪失大批資料。
一個配套解法是每次都寫入硬碟確保資料正確,接著使用 RAID 提昇寫入速度。但是用上 RAID 仍是操作硬碟,提昇有限,於是再加上帶有 write cache 的 RAID,這樣就能大幅提昇速度。但這又造成新的問題:雖然資料庫伺服器掛了不會影響資料,但若機房停電,仍會喪失 RAID cache 內的資料。於是在 RAID 裡加上電池,就能克服這問題 (附帶一提,我很喜歡這句話:「系統設計是一連串的取捨」)。
整個串起來就是「資料庫伺服器每次都寫入硬碟,並使用帶有 write cache + 電池的RAID 機器」,這樣能同時兼顧寫入速度並確保不會損失資料,不過就變成大幅提昇開銷了。若將支出也列入考量點的話,配套解法可能又不同,也許是降低寫入速度的要求,也許是容許損失多少時間內的資料。
以這麼技術的問題做為例子,其實有損杜書伍提的解題思維、以及《真正的問題是什麼?你想通了嗎?》的價值。真正在解決問題時,需要思考多個面向,而不能只局限在定死的框架裡以技術本位思考。以資工的術語類比的話,用 BFS (Breadth-first search) 的方式思考會比 DFS (Depth-first search) 容易找出好方案。比方說可以考慮修改規格或換方式滿足需求,或是重新定義問題,找出真正的需求和剛好到位的解法。問題往往來自於期望和感受之間出現落差,有時換個角度看問題,就能剛好繞開原本的技術難題,並且沒有犠牲任何事情。
以我最近在做的事為例,我在找網站壓力測試的工具,雖然能找到一些好用的工具,但它們卻沒提供良好的登入機制,讓我無法開始測試。若硬往技術方向思考,解決問題的選項不外乎再找下一個工具,或是改工具的程式碼,加入一個前置登入的機制。但其實退一步思考,我真正的需求是產生大量連線,以「如同使用者登入」的方式進行壓力測試,我並不需要用正規的方式登入自家網站,大可另寫一個網址,開後門避開繁瑣的登入流程,直接登入測試帳號。於是,我只需要評估壓力測試工具,不用在意它是否有完備的登入機制。
但思考面向變廣後,比起定好其它因素、單純考量技術來說,問題也變得更複雜。要有效地在不同面向間穿梭,需要更多的經驗和練習,就像以往專精學一件事一樣,只是變得更困難。杜書伍在《打造將才基因》裡有不少這方面的說明,強烈推薦閱讀。概念是先專精一個技能,接著專精第二個技能,並找出和先前學習經驗的異同點,融會兩者成為自己的思考系統。接著專精第三個、第四個技能,找異同點,漸漸拼出多元且深入的思考方式。
最近這一年有點開竅,開始會朝多元化思考,先記錄目前的體悟。待有更多經驗後,再來寫心得吧。

Reference:
http://fcamel-fc.blogspot.ca/2010/11/problem-solving.html

Friday, May 25, 2012

Use grep to search a string in files recursively and store the grep results in a quickfix window

Use grep to search a string in files recursively and store the grep results in a quickfix window
:grep -r 'pattern' . --include '*.c'

:copen             " open the quickfix window
:cw                " open the quickfix window if there are entries (so if your grep has no results, it won't appear).

<ENTER>            " Open the line of a file in a new buffer.
Ctrl-W <ENTER>     " Open the file/line in a new window.
:.cc               " Open the line of a file in a new buffer.

:cfile debug.txt   " read the content in debug.txt to a quickfix window.
                   " Note: follow this format: "filename:line number:error message"

:cgetfile debug.txt " Just like "cfile" but don't jump to the first entry.

:cclose            " close the quickfix window.
:quit              " close the quickfix window.

Edit ~/.vimrc:
" Find String Search String.
" Open quickfix window automatically after running grep command.
command! -nargs=+ Mygrep execute "silent grep! <args>" | copen

:Mygrep -r 'pattern' . --include '*.c'

Performing a customized grep search on the word under the cursor:
map <F4> :execute " grep -srnw --binary-files=without-match --exclude-dir=.git --exclude-from=exclude.list . -e " . expand("<cword>") . " " <bar> cwindow<CR>

:help grep
:help copen
:help quickfix

Reference
http://stackoverflow.com/questions/1234394/selecting-resulting-files-from-grep-in-vim
http://stackoverflow.com/questions/6373293/vim-how-to-store-the-grep-results-in-a-buffer
http://vim.wikia.com/wiki/Find_in_files_within_Vim

Wednesday, May 23, 2012

Disabling FreeBSD background fsck

If you are familiar with unix systems, you probably know already fsck, if not feel free to take a look at the wikipedia article about it to learn more.

FreeBSD has a nice feature in its UFS2 filesystem called background fsck, which allow dirty filesystems (filesystems that hadn't been unmounted properly/cleanly) to be mounted even if they need fsck to check them. Once mounted, a fsck process is started in the background, checking that partition.

This is really nice because your server will not took ages to reboot after a crash, because of fsck running before finishing the boot process (before mounting the partitions exactly). You can learn more about background fsck in this article from Kirk McKusick.

But, background fsck could be a problem in certain situations. For example, it happened to one of my servers this week that, after some crashes the server just reboot fine, then started the backgrounds fsck. It went ok for a while, but when fsck is called to run on the biggest partition (600Gb, mounted in /home, with probably 45% of the space used) the machine crashed again (kernel panic).

That server is a high-load mail server with a lot of disk writes, so I thought that perhaps disabling the background fsck feature could help. It would be even better if I could get the machine to perform an offline fsck by itself and then boot cleanly (yes, that was background fsck is supposedly to avoid).

To do it I added these two lines to /etc/rc.conf:

fsck_y_enable="YES"
background_fsck="NO"

The first one sets yes as the default answer for all the questions fsck could make during a check, while the second one disables the background fsck feature.

Then I rebooted the server, and it took quite a while to finish the reboot, but the filesystems were clean and the machine didn't freeze anymore.

(Hope it helps any of you).

Reference:
http://blog.e-shell.org/266

DB problems installing 7.8: PDOException: SQLSTATE[42S02]: Base table or view not found

DB problems installing 7.8: PDOException: SQLSTATE[42S02]: Base table or view not found

OK, for completeness sake, my additional problems were caused by having:
apc.include_once_override = 1

instead of:
apc.include_once_override = 0

in my php.ini.

Now working fine.

Reference:
http://drupal.org/node/1321656

Install OpenVPN Remote Access (TLS + User Auth) on pfSense 2.0

Install OpenVPN Remote Access (TLS + User Auth) on pfSense 2.0

Install OpenVPN Client Export Utility Package
Go to System > Packages > "Available Packages" tab > install "OpenVPN Client Export Utility" package.

Create CA (Certificate Authority)
Go to System > Cert Manager > "CAs" tab:

Descriptive name: Road Warrior CA
Method: "Create an internal Certificate Authority".
Key length: 4096 bits.
Lifetime: 3650 days.
Country Code: CA
State or Province: British Columbia
City: Port Coquitlam
Organization: YOUR_COMPANY_NAME Inc
Email Address: test@example.com
Common Name: Road Warrior CA

Create a OpenVPN Group
Go to System > User Manager > "Groups" tab > Add Group:

Group name: OpenVPN
Description: OpenVPN

Click on "Save" button.

Create a OpenVPN User
Go to System > User Manager > "Users" tab > Add User:

Username: openvpn_client1
Password: ********
Group Memberships: OpenVPN

Click on "Save" button.

Edit the user we just created > User Certificates: Add:

Method: Create an internal Certificate
Descriptive name: openvpn_client1
Certificate authority: Road Warrior CA
Key length: 4096 bits
Certificate Type: User Certificate
Lifetime: 3650 days

Distinguished name:
Country Code: CA
State or Province: British Columbia
City: Port Coquitlam
Organization: YOUR_COMPANY_NAME Inc
Email Address: test@example.com
Common Name: openvpn_client1

Click on "Save" button.

Service configuration
Go to VPN > click on "OpenVPN" > click on "Wizards" tab > Type of Server "Local User Access" > click on "Next" button
> Certificate Authority "Road Warrior CA" > click on "Next" button
> for server certificate, click on "Add new Certificate" button:

Descriptive Name: Road Warrior Server Certificate
Key length: 4096 bits
Lifetime: 3650
Country Code: CA
State or Province: British Columbia
City: Port Coquitlam
Organization: YOUR_COMPANY_NAME Inc
Email Address: test@example.com

Click on "Craete New Certificate" button.

Cryptographic Settings:
TLS Authentication: check "enable authentication of TLS packets".
Generate TLS Key: check "Automatically generate a shared TLS authentication key".
DH Parameters Length: 4096 bit.
Encryption Algorithm: AES-256-CBC (256-bit)
Hardware Crypto: No Hardware Crypto Acceleration.

Tunnel Settings:
Tunnel Network: 10.0.8.0/24
Redirect Gateway: check "Force all client generated traffic through the tunnel".
Concurrent connections: 10
Compression: check "Compress tunnel packets using the LZO algorithm".

Client Settings:
Dynamic IP: check "Allow connected clients to retain their connections if their IP address changes".
Address Pool: check "Provide a virtual adapter IP address to clients (see Tunnel Network)".
DNS Servers: check "Provide a DNS server list to clients".
DNS Servers: Server #1: 8.8.8.8
DNS Servers: Server #2: 8.8.4.4

Click on "Next" button.

Firewall Rule: check "Add a rule to permit traffic from clients on the Internet to the OpenVPN server process.

Make sure following is unchecked:
OpenVPN Rule: uncheck "Add a rule to allow all traffic from connected clients to pass across the VPN tunnel.

Click on "Next" button.
Click on "Finish" button.

OpenVPN: Client Export Utility
Go to VPN > OpenVPN > "Client Export" Tab > look for a user > click on "Configuration archive" link.

Firewall Rules
Go to Firewall > Rules > OpenVPN tab > Add rule:
Interface: OpenVPN
Protocol: any
Source: any
Destination: check "not"
Destination: type "LAN subnet" (we don't want VPN clients to access LAN subnet).
Description: OpenVPN Road Warrior

Click on "Save" Button.

Go to Firewall > NAT > Outbound tab > click on "Manual outbound NAT rule generation" > Save Button > Add a new mapping rule:
Interface: WAN.
Protocol: any.
Source: type "network".
Source: address "10.0.8.0 / 24".
Destination: check "not" (we don't want VPN clients to access LAN subnet).
Destination: type "Network".
Destination: address 192.168.1.0 / 24.

Click on "Save" button.
Click on "Apply changes" button.

Reference:
http://www.packetwatch.net/documents/guides/2012050801.php

Install Postfix Dovecot mail server automatically with iRedMail

Install Postfix Dovecot mail server automatically with iRedMail

What iRedMail is
A ZERO COST, fully fledged, full-featured mail server solution. All used packages are free and open source, provided by the Linux/BSD distribution venders you trust.
An open source project, released under GPLv2, hosted on BitBucket.

What iRedMail does
Install and configure mail server related BINARY packages automatically from the official software repositories provided by Linux/BSD distribution venders.

Reference:
http://code.google.com/p/iredmail/
http://www.iredmail.org/

Monday, May 14, 2012

wkhtmltopdf - convert HTML web page to PDF


wkhtmltopdf and wkhtmltoimage are powerful utilities to convert HTML to PDF
or an image file using a patched static build of Qt and the webkit rendering
engine.

A webpage can be downloaded directly from the web and rendered into a PDF
document or an image file (multiple formats are supported).

Features of the static version:
* Convert web pages into PDF documents (or images) using webkit
* Adding headers and footers
* TOC generation
* Batch mode conversions
* XServer is not required (however the X11 client libs must be installed)

For proper functionality you may need to install the following port(s):
x11-fonts/webfonts

WWW: http://code.google.com/p/wkhtmltopdf/


Snappy - is a PHP5 library allowing thumbnail, snapshot or PDF generation from a url or a html page.
It uses the excellent webkit-based wkhtmltopdf and wkhtmltoimage available on OSX, linux, windows.


Reference:
https://github.com/knplabs/snappy
http://code.google.com/p/wkhtmltopdf
http://code.google.com/p/wkhtmltopdf/wiki/IntegrationWithPhp
http://www.freebsd.org/cgi/url.cgi?ports/converters/wkhtmltopdf/pkg-descr
http://stackoverflow.com/questions/391005/convert-html-css-to-pdf-with-php

Friday, May 11, 2012

Drupal Custom User Login Form

Drupal Custom User Login Form

Method 1:
<?php
/**
 * Implementation of hook_theme().
 */
function my_blockGen_theme(&$existing, $type, $theme, $path) {
  global $theme;

  return array(
    'user_login_block' => array(
      'template' => 'templates/user_login_block',
      'arguments' => array('form' => NULL),
    ),
  );
}

function my_blockGen_preprocess_user_login_block(&$variables) {
  $variables['form']['links'] = array('#value' => '');  // remove "Request new password" link.
  $variables['user_login_block_rendered'] = drupal_render($variables['form']);

  ### [DEBUG]
  file_put_contents('/tmp/debug1', print_r($variables, TRUE) . PHP_EOL, FILE_APPEND);
}
?>

Create a new template file under current module directory:
# touch /www/drupal6/sites/all/modules/custom/my_blockGen/templates/user_login_block.tpl.php

Edit my_blockGen/templates/user_login_block.tpl.php:
<?php print $user_login_block_rendered; ?>

Method 2:
<?php
/**
 * Implementation of hook_preprocess_page().
 */
function my_blockGen_preprocess_page(&$variables) {
  global $user;

  if (isset($_GET['q']) && $_GET['q'] === 'bg/cust_err403' && $user->uid == 0) {
    $variables['template_files'][] = 'user_login_block';
  }
}


/**
 * Implementation of hook_form_alter().
 */
function my_blockGen_form_alter(&$form, $form_state, $form_id) {
  if ($form_id === 'user_login_block') {
    $form['links'] = array('#value' => '');  // remove "Request new password" link.
  }
}
?>

Create a new template file under current theme directory:
# touch /www/drupal6/sites/all/themes/mytheme/templates/user_login_block.tpl.php

Edit /www/drupal6/sites/all/themes/mytheme/templates/user_login_block.tpl.php:
<?php print $content_top; ?>

Thursday, May 10, 2012

Apple's Inspirational Note to New Hires

Apple's New Employee Welcome Letter

Apple's Inspirational Note to New Hires

For those of us who will probably never work at Apple, this little inspirational new-hire note gives some insight into the company culture and philosophy. Apparently this greets all new employees upon their first day with the company, it reads:

There's work and there's your life's work.

The kind of work that has your fingerprints all over it. The kind of work that you'd never compromise on. That you'd sacrifice a weekend for. You can do that kind of work at Apple. People don't come here to play it safe. They come to swim in the deep end.

They want their work to add up to something.

Something big. Something that couldn't happen anywhere else.

Welcome to Apple.

Reference:
http://osxdaily.com/2012/05/07/apple-inspirational-note-to-new-hires/

Thursday, May 3, 2012

關閉 Windows 7 休眠功能並刪除 hiberfil.sys,還給 C 槽空間

自從 Windows XP 開始支援 ACPI 以來,Windows 作業系統便有一個叫做「休眠」的功能。當我們對電腦下達休眠指令的時候,它會把目前所有的工作狀態(也就是記憶體裡的資料)儲存到硬碟裡的某個檔案然後關機,當下次電腦啟動時便可以還原關機前的最後工作狀態。
通常休眠所需要的檔案大小是與記憶體的容量相同,當記憶體容量越大,休眠所佔用的檔案大小也就越大。但並不是每個人都會用到這項功能,在這種情形 下,休眠檔案便會浪費硬碟空間。尤其是即將上市的 Windows 7 預設會啟用休眠功能,但是卻沒有能將這項功能關閉的設定介面。所以安裝完 Windows 7 之後,C 槽可能就會因此而少了好幾 GB 的空間。
▼ 如果電腦支援 ACPI 的話,Windows 7 預設會開啟「休眠」功能
開始選單的「休眠」指令
在 Windows 7 裡,會使用 C 槽裡一個名稱為「hiberfil.sys」的檔案來儲存休眠時記憶體裡的資料。不過這個檔案的屬性是隱藏,所以必須在資料夾選項裡設定「顯示隱藏的檔案」才會看得到。目前的記憶體都是以 GB 計算,所以 hiberfil.sys 的檔案大小也就跟著「水漲船高」▼
hiberfil.sys 檔案大小
如果你沒有習慣讓電腦休眠的話,可以把這項功能關閉,C 槽還會因此而騰出幾 GB 的空間。

● 如何關閉 Windows 7 的休眠功能,並刪除 hiberfil.sys?

第一步、在開始的功能表的搜尋欄位輸入 cmd,然後在搜尋結果中的 cmd 按下右鍵,點選【以系統管理員身分執行】▼
以系統管理員身份執行 cmd
第二步、在命令提示字元視窗裡輸入下面指令然後按下 Enter:
powercfg -h off
輸入關閉休眠功能的指令
完成上面的動作之後重新啟動電腦,hiberfil.sys 會在電腦重啟後自動被刪除。

● [順便一提]「睡眠模式」與「休眠模式」有什麼不同?

老實說,我以前常常搞不清楚睡眠與休眠的差異在哪裡。也許是中文字義太過相近,容易使人產生混淆。
睡眠(Sleep Mode)」在 Windows XP 裡又稱為「待機(Standby)」,當電腦進入睡眠模式時,它並不會完全關機,而是關閉螢幕、硬碟以及其它一些用不到的裝置,以極低的耗電量保存記憶體 裡的資料,達到「節能減碳」的目的。之後當我們「喚醒」它的時候,又可以在極短的時間內讓電腦恢復之前的工作內容。
休眠模式(Hibernate Mode)」與睡眠不同的是,它會讓電腦完全關機,但是在關機之前會把工作內容儲存到硬碟裡,以 Windows 7 來說就是 hiberfil.sys。當下次開機時便可以從這個檔案原還最後的工作內容。

Reference:
http://blog.joaoko.net/archives/1963
windows 7改善了許多電源管理的方式。windows 7 比windows xp執行較少的背景活動,如此CPU才能省下部分耗用的電力,另外也能關閉未使用連接埠的電源,並能更準確的指出電池的剩餘電力。這樣你的筆電,才不會在 明明還有電力的情況下,動不動就進入休眠模式。

休眠、睡眠與交互式睡眠 的區別和優缺點比較
windows xp系統有所謂的待機與休眠模式,而windows 7還有一種「睡眠」的模式,可是很多人還是習慣在不用電腦的時候將其整個關機。在我使用windows xp的時候,其實就只用它的休眠模式來關機,這樣的好處是,與完全關機一樣,完全不浪費電力。但是回復工作時,又比重新開機要來的快速。

這三種模式的區別如下:

睡眠(Sleep) 將系統切換到該模式後,然後使計算機進入低功耗狀態,除保留必要電力,如記憶體,電腦其他設備的供電都將中斷,這樣當使用者希望恢復作業的時候,就可以直接恢復到睡眠前狀態。這種模式的恢復速度是最快的,一般五秒之內就可以恢復。

這種模式並非完全不耗電,但所耗的電力極低, 只有讓電腦持續執行所需電量的十分之一,行動電腦在睡眠模式中,通常每小時使用 1% 至 2% 的電池電力

但必須小心的是,如果在睡眠的狀態下供電發生異常(例如停電),那麼下一次就只能重新開機,所以睡眠前未保存的資料都會遺失。


休眠(Hibernate) 將系統切換到該模式後,系統會自動將記憶體中的資料,全部存到硬碟上,然後中斷對所有設備的供電,就如同關機一樣,不會耗用任何電力。當恢復作業的時候, 系統會從硬碟將記憶體的內容直接讀入,並恢復到休眠之前的狀態。這種模式不怕休眠後供電異常,但代價是這種模式的恢復速度較慢,一般大約要1至2分鐘左 右。


交互式睡眠 (又稱混合式睡眠) 「交互式睡眠」主要是針對桌上型電腦所設計的功能 。交互式睡眠是睡眠與休眠的組合,它會將記憶體的內容同時存到硬碟,然後再使電腦進入低電力狀態,這樣就可以快速地繼續工作,同時不用害怕電力發生中斷。 因為如果發生電源中斷,Windows 可以從硬碟還原您的工作。桌上型電腦的交互式睡眠通常預設為開啟。這是因為桌上型電腦,通常沒有像筆記型電腦一樣的電池當備用電力。因此如果意外發生電源 中斷,標準睡眠模式中的桌上型電腦可能會發生資料遺失。


在windows 7開啟或關閉混合式睡眠的方法:
  1. 請依序按: [開始]→[控制台]→[系統及安全性]→ [電源選項]。
  2. 在 [選取電源計劃] 頁面上,按一下所選取計劃底下的 [變更計劃設定]。
  3. 在 [變更計劃的設定] 頁面上,按一下 [變更進階電源設定]。
  4. 在 [進階設定] 索引標籤中,展開 [睡眠],再展開 [允許混合式睡眠],然後執行下列其中一項動作:
    如果您使用的是行動電腦,按一下 [電池使用中] 或 [一般電源] (或兩個都按),在設定的地方按下拉箭頭, 選擇[開啟]。
    如果您使用的是桌上型電腦,在 [設定]的地方按一下下拉箭頭,然後選 [開啟]。
  5. 按一下 [確定],然後按一下 [儲存變更]。

睡眠與休眠的喚醒 在多數電腦上,按下電腦電源按鈕即可繼續工作。不過,你還可以設定用鍵盤的任意鍵、或移動一下滑鼠按鈕或打開筆記型電腦的螢幕,來喚醒電腦。
但是用滑鼠在喚醒電腦,有時候不小心動到滑鼠,就打開電腦。所以最好是設定用鍵盤來喚醒就好。


控制台→ 硬體及音效→裝置管理員

win7xp_005.jpg

選擇支援喚醒的裝置→滑鼠右鍵→內容
win7xp_006.jpg

再按下「電源管理」→在「允許這個裝置喚醒電腦」打勾。
win7xp_004.jpg


電源選項中S1,S2,S3,S4,S5的含義

ACPI(Advanced Configuration and Power Interface)。這種電源管理可以通過如軟體控制"開關"系統,亦可以用硬體信號喚醒和關閉系統。

ACPI有以下幾種模式:
  1. S0 正常。
  2. S1 CPU停止工作。
  3. S2 CPU關閉。
  4. S3 除了記憶體以外的配件都停止工作,即使是風扇也不會轉動。(windows 7預設的睡眠模式為S3)
  5. S4 記憶體資料寫入硬碟,所有配件停止工作。(windows 7預設的休眠模式為S4)
    就是把 windows 記憶體中的資料完整的存在硬碟中。等開機時就直接從硬碟讀到記憶體,因為不需像開機一樣執行一堆應用程式,因此速度比正常開機要快許多。
  6. S5 關閉。
判斷系統是處於何種模式,最簡單的辦法是仔細觀察系統的情況:在ACPI的S1,S2的模式下,只有CPU停止工作,其他設備仍處於供電狀態。
而在S3模式(BIOS->電源管理->Suspend to RAM設為Enable),除記憶體外其他設備均處於斷電狀態。
所以我們只需按一下光碟上的彈出鈕即可,不能打開光碟門則處於S3狀態。
還有一種比較簡單的方法是: 在S3休眠模式下,系統完全是安靜的,連風扇都不會運轉,聽聽風扇運轉的聲音就可判斷

另外如果您按下睡眠模式,發現不是您預期的狀態,例如說風扇仍然在轉動,那麼有幾個解決方式,首先您必須確定主機板的BIOS是最新的,若不是請更新,因為BIOS若不能完整的支援睡眠模式的話,那一切都是白搭。 再一個重點是在 BIOS 裡找到電源選項(ACPI),先確定ACPI已開啟,然後再看看有沒有一個選項,用來設定說休眠模式為何,是 S1,S2,還是 S3 。如果您想在最省電的狀態之下,當然要選 S3。



如果預計有一段時間不使用電腦,應該將它關機或使其睡眠?
有人可能懷疑,長時間保持在睡眠模式(例如一個晚上),是否仍會消耗很多電力,根據微軟官方的說明文件 :
如果只是幾個小時或甚至過一個晚上,通常使電腦進入睡眠比較有效率,只要按下 [開始] 功能表上的 [電源] 按鈕或闔上行動電腦的電腦螢幕即可 (有些電腦的外殼上也有專用的睡眠按鈕)。

與關閉電源相比,選擇睡眠有幾個好處:
  1. 您所有的工作,包括您正使用的程式資訊 (例如視窗位置與大小等) 都會自動儲存。
  2. 當您將電腦從睡眠狀態喚醒時,不需要重新啟動程式或重新開啟檔案;但如果是關閉電源就必須這麼做。
  3. 雖然在睡眠模式時 Windows 會使用一些電力, 但是耗電量很低:只有讓電腦持續執行所需電量的十分之一。行動電腦在睡眠模式中,通常每小時使用 1% 至 2% 的電池電力。
  4. 當 Windows 在睡眠時,依然可以下載與安裝更新,以及執行其他例行維護工作。因此,有些企業會要求員工下班時,使電腦進入睡眠狀態,而不是關機。
但是有些情況下,您應該整個關閉電腦電源;例如,當您安裝新的記憶卡或其他硬體時。如果您計畫好幾天或更久時間不使用電腦,應該將它關機。


幾個注意事項
  1. 如果睡眠或休眠無法使用,可能是下列其中一或多個原因:
    • 您的顯示卡可能不支援睡眠。或必須更新顯示卡的驅動程式。
    • 有些設定是由系統管理員管理。
    • 在電腦的基本輸出入系統 (BIOS) 中,關閉睡眠及其他省電狀態,請將之開啟。
    • 如果找不到休眠選項,可能使用powercfg關閉休眠功能。
      powercfg -h off,會關閉休眠功能,windows 7休眠時,會在系統磁碟寫入一塊與記憶體同樣大小的硬碟空間,如果你從不使用休眠關機,可以將它關掉,可以減少硬碟的佔用空間。要開啟時為powercfg -h on。
    • 如果找不到休眠選項,可能是已開啟交互式睡眠。
    • 有些應用程式可能會阻止睡眠或休眠,例如影音播放軟體、燒錄軟體、P2P下載軟體(例如我在使用訊雷下載檔案時,windows 7就不能依設定的時間進入睡眠模式),這些軟體有些有設定阻止休眠的選項,您可以將它關閉。
  2. 如果在燒錄狀態下,要注意將內定的 "讓電腦睡眠"時間改為 "永不"。否則windows 7並不會理會燒錄狀態,時間到了,依然會進入睡眠模式,可能會造成燒錄失敗。
  3. 在「開始」工具列,以滑鼠右鍵→內容。看到的「電源按鈕動作」,只是開始功能表的預設「顯示關機項目」,並不是真的按電腦電源鈕,所執行的關機動作。
    sleep_002.jpg
  4. 真正設定電源按鈕動作的地方為: [開始]→[控制台]→[系統及安全性]→ [電源選項]→[選擇按下電源按鈕時的行為],在此所設定的休眠或睡眠,將會在您按下電源鈕時實際執行該動作。

    sleep_003.jpg

PowerCfg -- Windows 7的進階電源設定工具
PowerCfg 是windows 的進階的電源設定工具,因指令煩多,較不常使用,但在 Windows 7 的 PowerCfg 新增一個電源效率報告功能,主要用來分析軟硬體和周邊設備,配合windows 7的電源管理運作情形,以增加電腦省電的效能。例如報告中可以列出影響 CPU 進入省電模式的裝置或驅動程式、在系統閒置時,有那些程序佔用過多的系統資源、並且可以列出您的電腦支援的睡眠模式(S1,S2,S3....等)。

在命令列模式下(必須使用系統管理員權限執行),輸入powercfg /energy
sleep_001.jpg

它會產生一個報告,在c:\windows\system32\energy-report.html,你可以用瀏 
覽器打開來看。你可以看到許多有用的資訊,例如我標出紅色部分:有記錄硬體的錯誤,高cpu使用率的程式,以及支援的休眠或睡眠模式等。

電源效率診斷報告
電腦名稱 HOME-03
掃描時間 2009-11-18T21:59:36Z
掃描期間 60 秒
系統製造商 ASUSTeK Computer INC.
系統產品名稱 B202
BIOS 日期 09/03/2009
BIOS 版本 1114
作業系統組建 7600
平台角色 PlatformRoleDesktop
一般電源 true
處理程序計數 66
執行緒計數 762
報告 GUID {1e69d323-19f9-4fea-81f0-6528788ae93f}
分析結果
錯誤
USB 暫停:USB 裝置未進入暫停狀態
USB 裝置未進入暫停狀態。如果 USB 裝置在停止使用時未進入暫停狀態,可能是因為停用了處理器電源管理。
裝置名稱 USB Composite Device
主機控制器識別碼 PCI\VEN_8086&DEV_27C8
主機控制器位置 PCI bus 0, device 29, function 0
裝置識別碼 USB\VID_13BA&PID_0017
連接埠路徑 1
USB 暫停:USB 裝置未進入暫停狀態
USB 裝置未進入暫停狀態。如果 USB 裝置在停止使用時未進入暫停狀態,可能是因為停用了處理器電源管理。
裝置名稱 USB Root Hub
主機控制器識別碼 PCI\VEN_8086&DEV_27C8
主機控制器位置 PCI bus 0, device 29, function 0
裝置識別碼 USB\VID_8086&PID_27C8
連接埠路徑
CPU 使用率:高處理器使用率
追蹤期間的平均處理器使用率很高。當平均的處理器使用率非常低時,系統會耗用較少量電源。檢閱個別處理程序的處理器使用率,判斷佔總處理器使用率最高的應用程式與服務。
平均使用率 (%) 15.86
平台電源管理容量:無法驗證 ACPI _PSD 物件
這部電腦上的 ACPI _PSD 物件定義有問題。安裝最新版的系統韌體 (BIOS) 即可解決這個問題。
群組 0
索引 0
平台電源管理容量:無法驗證 ACPI _PSD 物件
這部電腦上的 ACPI _PSD 物件定義有問題。安裝最新版的系統韌體 (BIOS) 即可解決這個問題。
群組 0
索引 1
警告
平台計時器解析度:平台計時器解析度
預設的平台計時器解析度是 15.6ms (15625000ns),且應在每次系統閒置時使用。如果計時器解析度增加,則處理器電源管理技術就會沒有效率。計時器解析度可能會因為播放多媒體或圖形動畫而增加。
目前的計時器解析度 (100ns 個單位) 10000
最大計時器期間 (100ns 個單位) 156001
平台計時器解析度:未執行的計時器要求
程式或服務要求的計時器解析度小於平台最大計時器解析度。
要求的期間 30000
正在要求處理程序識別碼 2984
正在要求處理程序路徑 \Device\HarddiskVolume1\Program Files\Mozilla Firefox\firefox.exe
平台計時器解析度:未執行的計時器要求
程式或服務要求的計時器解析度小於平台最大計時器解析度。
要求的期間 10000
正在要求處理程序識別碼 6088
正在要求處理程序路徑 \Device\HarddiskVolume1\Program Files\USB Safely Remove\USBSafelyRemove.exe
平台計時器解析度:未執行的計時器要求
程式或服務要求的計時器解析度小於平台最大計時器解析度。
要求的期間 10000
正在要求處理程序識別碼 1604
正在要求處理程序路徑 \Device\HarddiskVolume1\Program Files\Google\Quick Search Box\GoogleQuickSearchBox.exe
電源原則:802.11 無線電波電源原則是最高效能 (一般電源)
目前 802.11 相容無線網路介面卡的電源原則未設定為使用低電源模式。
CPU 使用率:具有高處理器使用率的個別處理程序。
這個處理程序佔追蹤期間所記錄之總處理器使用率的一大部分。
處理程序名稱 firefox.exe
PID 2984
平均使用率 (%) 6.78
模組 平均模組使用率 (%)
\SystemRoot\system32\ntkrnlpa.exe 1.79
\Device\HarddiskVolume1\Program Files\Mozilla Firefox\xul.dll 1.29
\Device\HarddiskVolume1\Windows\System32\Macromed\Flash\NPSWF32.dll 0.94
CPU 使用率:具有高處理器使用率的個別處理程序。
這個處理程序佔追蹤期間所記錄之總處理器使用率的一大部分。
處理程序名稱 TeaTimer.exe
PID 2632
平均使用率 (%) 2.28
模組 平均模組使用率 (%)
\SystemRoot\system32\ntkrnlpa.exe 1.37
\Device\HarddiskVolume1\Windows\System32\ntdll.dll 0.29
\Device\HarddiskVolume1\Program Files\Spybot - Search & Destroy\TeaTimer.exe 0.18
CPU 使用率:具有高處理器使用率的個別處理程序。
這個處理程序佔追蹤期間所記錄之總處理器使用率的一大部分。
處理程序名稱 explorer.exe
PID 1552
平均使用率 (%) 1.05
模組 平均模組使用率 (%)
\SystemRoot\system32\ntkrnlpa.exe 0.42
\SystemRoot\System32\win32k.sys 0.35
\SystemRoot\system32\halmacpi.dll 0.08
CPU 使用率:具有高處理器使用率的個別處理程序。
這個處理程序佔追蹤期間所記錄之總處理器使用率的一大部分。
處理程序名稱 conhost.exe
PID 5028
平均使用率 (%) 0.56
模組 平均模組使用率 (%)
\SystemRoot\System32\win32k.sys 0.40
\SystemRoot\system32\ntkrnlpa.exe 0.08
\SystemRoot\system32\halmacpi.dll 0.03
CPU 使用率:具有高處理器使用率的個別處理程序。
這個處理程序佔追蹤期間所記錄之總處理器使用率的一大部分。
處理程序名稱 ProcessGovernor.exe
PID 2168
平均使用率 (%) 0.51
模組 平均模組使用率 (%)
\SystemRoot\system32\ntkrnlpa.exe 0.32
\Device\HarddiskVolume1\Windows\System32\ntdll.dll 0.06
\Device\HarddiskVolume1\Windows\System32\rpcrt4.dll 0.04
CPU 使用率:具有高處理器使用率的個別處理程序。
這個處理程序佔追蹤期間所記錄之總處理器使用率的一大部分。
處理程序名稱 csrss.exe
PID 504
平均使用率 (%) 0.50
模組 平均模組使用率 (%)
\SystemRoot\System32\cdd.dll 0.23
\SystemRoot\system32\ntkrnlpa.exe 0.11
\SystemRoot\System32\win32k.sys 0.09
CPU 使用率:具有高處理器使用率的個別處理程序。
這個處理程序佔追蹤期間所記錄之總處理器使用率的一大部分。
處理程序名稱 System
PID 4
平均使用率 (%) 0.37
模組 平均模組使用率 (%)
\SystemRoot\system32\ntkrnlpa.exe 0.21
\SystemRoot\System32\Drivers\cng.sys 0.06
\SystemRoot\system32\halmacpi.dll 0.02
CPU 使用率:具有高處理器使用率的個別處理程序。
這個處理程序佔追蹤期間所記錄之總處理器使用率的一大部分。
處理程序名稱 GoogleQuickSearchBox.exe
PID 1604
平均使用率 (%) 0.24
模組 平均模組使用率 (%)
\SystemRoot\system32\ntkrnlpa.exe 0.14
\Device\HarddiskVolume1\Windows\System32\ntdll.dll 0.02
\SystemRoot\system32\halmacpi.dll 0.01
CPU 使用率:具有高處理器使用率的個別處理程序。
這個處理程序佔追蹤期間所記錄之總處理器使用率的一大部分。
處理程序名稱 Q-Dir.exe
PID 4388
平均使用率 (%) 0.24
模組 平均模組使用率 (%)
\SystemRoot\system32\ntkrnlpa.exe 0.08
\Device\HarddiskVolume1\Windows\System32\user32.dll 0.05
\SystemRoot\System32\win32k.sys 0.04
資訊
平台計時器解析度:計時器要求堆疊
此處理程序中負責最低平台計時器設定的模組堆疊。
要求的期間 10000
正在要求處理程序識別碼 2984
正在要求處理程序路徑 \Device\HarddiskVolume1\Program Files\Mozilla Firefox\firefox.exe
呼叫模組堆疊 \Device\HarddiskVolume1\Windows\System32\ntdll.dll
\Device\HarddiskVolume1\Windows\System32\winmm.dll
\Device\HarddiskVolume1\Program Files\Mozilla Firefox\js3250.dll
\Device\HarddiskVolume1\Program Files\Mozilla Firefox\xul.dll
平台計時器解析度:計時器要求堆疊
此處理程序中負責最低平台計時器設定的模組堆疊。
要求的期間 10000
正在要求處理程序識別碼 6088
正在要求處理程序路徑 \Device\HarddiskVolume1\Program Files\USB Safely Remove\USBSafelyRemove.exe
呼叫模組堆疊 \Device\HarddiskVolume1\Windows\System32\ntdll.dll
\Device\HarddiskVolume1\Windows\System32\winmm.dll
\Device\HarddiskVolume1\Program Files\USB Safely Remove\USBSafelyRemove.exe
不明模組
平台計時器解析度:計時器要求堆疊
此處理程序中負責最低平台計時器設定的模組堆疊。
要求的期間 10000
正在要求處理程序識別碼 1604
正在要求處理程序路徑 \Device\HarddiskVolume1\Program Files\Google\Quick Search Box\GoogleQuickSearchBox.exe
呼叫模組堆疊 \Device\HarddiskVolume1\Windows\System32\ntdll.dll
\Device\HarddiskVolume1\Windows\System32\winmm.dll
\Device\HarddiskVolume1\Program Files\Google\Quick Search Box\bin\1.2.1150.162\qsb.dll
\Device\HarddiskVolume1\Program Files\Google\Quick Search Box\GoogleQuickSearchBox.exe
電源原則:使用中電源計劃
目前正在使用的電源計劃
計劃名稱 OEM 平衡
計劃 GUID {381b4222-f694-41f0-9685-ff5bb260df2e}
電源原則:電源計劃特質 (一般電源)
目前的電源計劃在系統使用一般電源時的特質。
特質 平衡
電源原則:視訊品質 (一般電源)
讓 Windows Media Player 在播放視訊時最佳化品質或節省電力。
品質模式 最佳化視訊品質
系統可用性要求:分析成功
分析成功。未發現任何電源效率問題。沒有傳回任何資訊。
電池:分析成功
分析成功。未發現任何電源效率問題。沒有傳回任何資訊。
平台電源管理容量:支援的睡眠狀態
睡眠狀態允許電腦在一段時間不活動之後進入低電源模式。S3 睡眠狀態是 Windows 平台的預設睡眠狀態。S3 睡眠狀態只會消耗足以保留記憶體內容的電源,並允許電腦快速恢復運作。只有極少數平台支援 S1 或 S2 睡眠狀態。
支援 S1 睡眠狀態 true
支援 S2 睡眠狀態 false
支援 S3 睡眠狀態 true
支援 S4 睡眠狀態 true
平台電源管理容量:處理器電源管理容量
有效率的處理器電源管理可讓電腦自動平衡效能與電源消耗。
群組 0
索引 0
閒置 (C) 狀態計數 1
效能 (P) 狀態計數 4
節流閥 (T) 狀態計數 8
平台電源管理容量:處理器電源管理容量
有效率的處理器電源管理可讓電腦自動平衡效能與電源消耗。
群組 0
索引 1
閒置 (C) 狀態計數 1
效能 (P) 狀態計數 4
節流閥 (T) 狀態計數 8


Reference:
http://save-coco.blogspot.ca/2009/11/windows-7.html