diff --git a/go/src/themaru/http.go b/go/src/themaru/http.go
index 8d08871..4861239 100644
--- a/go/src/themaru/http.go
+++ b/go/src/themaru/http.go
@@ -3,7 +3,6 @@ package main
 import (
 	"html/template"
 	"net/http"
-	"os"
 	"sync"
 
 	"github.com/gin-contrib/multitemplate"
@@ -23,17 +22,9 @@ var indexPage = template.Must(template.New("index.html").Parse(`
 <body>
   <h1>{{.Device}} Configuration</h1>
   <div>
-    <p>System Version: {{.Version}}</p>
+    <p>System Version: {{.falinux_version}}</p>
   </div>
   <hr>
-  <h1>Setting IP Address</h1>
-  <div>
-    <form action="/address" method="post">
-      <input type="text" name="address" value="{{.IP}}" required />
-      <input type="text" name="address2" value="{{.IP2}}" required />
-      <input type="submit" value="Set" />
-    </form>
-  </div>
   <h1>Updating System</h1>
   <div>
     <form action="/system" method="post" enctype="multipart/form-data">
@@ -58,6 +49,101 @@ var indexPage = template.Must(template.New("index.html").Parse(`
 </html>
 `))
 
+var networkPage = template.Must(template.New("network.html").Parse(`
+<html>
+<head>
+  <title>Network Configuration</title>
+  <style type="text/css">
+    div { margin-left: 20px; }
+  </style>
+</head>
+<body>
+  <form action="/network" method="post">
+    <h1>Setting Network</h1>
+    <div>
+      <p>
+        <label for="ethaddr">Ethernet Address</label><br />
+        <input type="text" id="ethaddr" name="ethaddr" value="{{.ethaddr}}" required />
+      </p>
+      <p>
+        <label for="ipaddr">IP Address</label><br />
+        <input type="text" id="ipaddr" name="ipaddr" value="{{.ipaddr}}" required />
+      </p>
+      <p>
+        <label for="netmask">Netmask</label><br />
+        <input type="text" id="netmask" name="netmask" value="{{.netmask}}" required />
+      </p>
+      <p>
+        <label for="gateway">Gateway</label><br />
+        <input type="text" id="gateway" name="gateway" value="{{.gateway}}" required />
+      </p>
+    </div>
+    <label for="submit"><h1>Click to Apply</h1></label>
+    <div>
+      <p><input id="submit" type="submit" value="Set" /></p>
+    </div>
+  </form>
+</body>
+</html>
+`))
+
+var networkPageDual = template.Must(template.New("network.html").Parse(`
+<html>
+<head>
+  <title>Network Configuration</title>
+  <style type="text/css">
+    div { margin-left: 20px; }
+  </style>
+</head>
+<body>
+  <form action="/network" method="post">
+    <h1>Setting Network</h1>
+    <div>
+      <p>
+        <label for="ethaddr">Ethernet Address</label><br />
+        <input type="text" id="ethaddr" name="ethaddr" value="{{.ethaddr}}" required />
+      </p>
+      <p>
+        <label for="ipaddr">IP Address</label><br />
+        <input type="text" id="ipaddr" name="ipaddr" value="{{.ipaddr}}" required />
+      </p>
+      <p>
+        <label for="netmask">Netmask</label><br />
+        <input type="text" id="netmask" name="netmask" value="{{.netmask}}" required />
+      </p>
+      <p>
+        <label for="gateway">Gateway</label><br />
+        <input type="text" id="gateway" name="gateway" value="{{.gateway}}" required />
+      </p>
+    </div>
+    <h1>Setting Network 2</h1>
+    <div>
+      <p>
+        <label for="ethaddr2">Ethernet Address</label><br />
+        <input type="text" id="ethaddr2" name="ethaddr2" value="{{.ethaddr2}}" required />
+      </p>
+      <p>
+        <label for="ipaddr2">IP Address</label><br />
+        <input type="text" id="ipaddr2" name="ipaddr2" value="{{.ipaddr2}}" required />
+      </p>
+      <p>
+        <label for="netmask2">Netmask</label><br />
+        <input type="text" id="netmask2" name="netmask2" value="{{.netmask2}}" required />
+      </p>
+      <p>
+        <label for="gateway2">Gateway</label><br />
+        <input type="text" id="gateway2" name="gateway2" value="{{.gateway2}}" required />
+      </p>
+    </div>
+    <label for="submit"><h1>Click to Apply</h1></label>
+    <div>
+      <p><input id="submit" type="submit" value="Set" /></p>
+    </div>
+  </form>
+</body>
+</html>
+`))
+
 var rebootPage = template.Must(template.New("reboot.html").Parse(`
 <html>
 <head>
@@ -81,11 +167,18 @@ func runHttp() {
 	r.Add("index.html", indexPage)
 	r.Add("reboot.html", rebootPage)
 
+	if hasDualNetwork {
+		r.Add("network.html", networkPageDual)
+	} else {
+		r.Add("network.html", networkPage)
+	}
+
 	g.HTMLRender = r
 
 	g.GET("/", renderIndex)
+	g.GET("/network", renderNetwork)
 
-	g.POST("/address", handleAddress)
+	g.POST("/network", handleNetwork)
 	g.POST("/system", handleSystem)
 	g.POST("/application", handleApplication)
 
@@ -95,19 +188,26 @@ func runHttp() {
 	g.Run(":8988")
 }
 
-func handleAddress(c *gin.Context) {
+func handleNetwork(c *gin.Context) {
 	mu.Lock()
 	defer mu.Unlock()
 
-	addr := c.PostForm("address")
-	addr2 := c.PostForm("address2")
+	keyList := []string{"ethaddr", "ipaddr", "netmask", "gateway"}
+	if hasDualNetwork {
+		keyList = append(keyList, "ethaddr2", "ipaddr2", "netmask2", "gateway2")
+	}
+
+	env := map[string]string{}
+	for _, key := range keyList {
+		env[key] = c.PostForm(key)
+	}
 
-	if err := setIpAddress(addr, addr2); err != nil {
-		c.String(http.StatusBadRequest, "address: setIpAddress: %s", err.Error())
+	if err := setNetwork(env); err != nil {
+		c.String(http.StatusBadRequest, "network: setNetwork: %s", err.Error())
 		return
 	}
 
-	c.HTML(http.StatusOK, "reboot.html", gin.H{"Message": "IP address is updated."})
+	c.HTML(http.StatusOK, "reboot.html", gin.H{"Message": "Network is configured."})
 }
 
 func handleSystem(c *gin.Context) {
@@ -160,17 +260,16 @@ func handleRestore(c *gin.Context) {
 }
 
 func renderIndex(c *gin.Context) {
-	device := "System"
-	if len(os.Args) > 1 {
-		device = os.Args[1]
-	}
+	env, _ := getEnv(1)
+	env["Device"] = deviceName
+
+	c.HTML(http.StatusOK, "index.html", env)
+}
+
+func renderNetwork(c *gin.Context) {
+	env, _ := getEnv(0)
 
-	c.HTML(http.StatusOK, "index.html", gin.H{
-		"Device":  device,
-		"IP":      getIpAddress(),
-		"IP2":     getIpAddress2(),
-		"Version": getVersion(),
-	})
+	c.HTML(http.StatusOK, "network.html", env)
 }
 
 func saveFormFile(c *gin.Context, name string, dst string) error {
diff --git a/go/src/themaru/ip.go b/go/src/themaru/ip.go
deleted file mode 100644
index a3f09bd..0000000
--- a/go/src/themaru/ip.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package main
-
-import (
-	"errors"
-	"regexp"
-	"strconv"
-)
-
-func getIpAddress() string {
-	env, err := getEnv(0)
-	if err != nil {
-		return ""
-	}
-
-	return env["ipaddr"]
-}
-
-func getIpAddress2() string {
-	env, err := getEnv(0)
-	if err != nil {
-		return ""
-	}
-
-	return env["ipaddr2"]
-}
-
-func setIpAddress(address, address2 string) error {
-	address = extractIpAddress(address)
-	if address == "" {
-		return errors.New("invalid IP address")
-	}
-
-	address2 = extractIpAddress(address2)
-	if address2 == "" {
-		return errors.New("invalid IP address2")
-	}
-
-	env, err := getEnv(0)
-	if err != nil {
-		return err
-	}
-
-	env["ipaddr"] = address
-	env["ipaddr2"] = address2
-
-	err = setEnv(0, env)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func extractIpAddress(address string) string {
-	r := regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)`)
-	s := r.FindStringSubmatch(address)
-	if len(s) != 5 {
-		return ""
-	}
-
-	for i, v := range s {
-		if i == 0 {
-			continue
-		}
-
-		u, err := strconv.ParseUint(v, 10, 8)
-		if err != nil {
-			return ""
-		}
-
-		if u > 255 {
-			return ""
-		}
-	}
-
-	return s[0]
-}
diff --git a/go/src/themaru/main.go b/go/src/themaru/main.go
index 241ff1a..1859af4 100644
--- a/go/src/themaru/main.go
+++ b/go/src/themaru/main.go
@@ -1,5 +1,18 @@
 package main
 
+import (
+	"flag"
+)
+
+var deviceName string
+var hasDualNetwork bool
+
+func init() {
+	flag.StringVar(&deviceName, "device", "System", "Set device name")
+	flag.BoolVar(&hasDualNetwork, "dual", false, "Set if it has dual network")
+	flag.Parse()
+}
+
 func main() {
 	runHttp()
 }
diff --git a/go/src/themaru/network.go b/go/src/themaru/network.go
new file mode 100644
index 0000000..e084c16
--- /dev/null
+++ b/go/src/themaru/network.go
@@ -0,0 +1,95 @@
+package main
+
+import (
+	"errors"
+	"regexp"
+	"strconv"
+)
+
+func setNetwork(env map[string]string) error {
+	if ok := validateEthernetAddr(env["ethaddr"]); !ok {
+		return errors.New("invalid ethernet address")
+	}
+
+	if ok := validateIpAddr(env["ipaddr"]); !ok {
+		return errors.New("invalid ip address")
+	}
+
+	if ok := validateIpAddr(env["netmask"]); !ok {
+		return errors.New("invalid netmask")
+	}
+
+	if ok := validateIpAddr(env["gateway"]); !ok {
+		return errors.New("invalid gateway")
+	}
+
+	if hasDualNetwork {
+		if ok := validateEthernetAddr(env["ethaddr2"]); !ok {
+			return errors.New("invalid ethernet address 2")
+		}
+
+		if ok := validateIpAddr(env["ipaddr2"]); !ok {
+			return errors.New("invalid ip address 2")
+		}
+
+		if ok := validateIpAddr(env["netmask2"]); !ok {
+			return errors.New("invalid netmask 2")
+		}
+
+		if ok := validateIpAddr(env["gateway2"]); !ok {
+			return errors.New("invalid gateway 2")
+		}
+	}
+
+	return setEnv(0, env)
+}
+
+func validateEthernetAddr(addr string) (ok bool) {
+	r := regexp.MustCompile(`([0-9a-fA-F]{2}):([0-9a-fA-F]{2}):([0-9a-fA-F]{2}):([0-9a-fA-F]{2}):([0-9a-fA-F]{2}):([0-9a-fA-F]{2})`)
+	s := r.FindStringSubmatch(addr)
+	if len(s) != 7 || s[0] != addr {
+		return false
+	}
+
+	for i, v := range s {
+		if i == 0 {
+			continue
+		}
+
+		u, err := strconv.ParseUint(v, 16, 8)
+		if err != nil {
+			return false
+		}
+
+		if u > 255 {
+			return false
+		}
+	}
+
+	return true
+}
+
+func validateIpAddr(addr string) (ok bool) {
+	r := regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)`)
+	s := r.FindStringSubmatch(addr)
+	if len(s) != 5 || s[0] != addr {
+		return false
+	}
+
+	for i, v := range s {
+		if i == 0 {
+			continue
+		}
+
+		u, err := strconv.ParseUint(v, 10, 8)
+		if err != nil {
+			return false
+		}
+
+		if u > 255 {
+			return false
+		}
+	}
+
+	return true
+}