瀏覽代碼

feat(net): impl. network association/dissociation

Mustafa Arici 8 年之前
父節點
當前提交
58c21011e7
共有 9 個文件被更改,包括 548 次插入194 次删除
  1. 14 1
      api/rpc.go
  2. 5 5
      bindata/bindata.go
  3. 20 10
      cmd/ovpm/net.go
  4. 133 133
      cmd/ovpm/user.go
  5. 138 10
      net.go
  6. 126 8
      net_test.go
  7. 102 24
      pb/network.pb.go
  8. 8 3
      pb/network.proto
  9. 2 0
      pb/user.pb.go

+ 14 - 1
api/rpc.go

@@ -194,6 +194,7 @@ func (s *NetworkService) List(ctx context.Context, req *pb.NetworkListRequest) (
 		nt = append(nt, &pb.Network{
 		nt = append(nt, &pb.Network{
 			Name:      network.GetName(),
 			Name:      network.GetName(),
 			CIDR:      network.GetCIDR(),
 			CIDR:      network.GetCIDR(),
+			Type:      network.GetType().String(),
 			CreatedAt: network.GetCreatedAt(),
 			CreatedAt: network.GetCreatedAt(),
 		})
 		})
 	}
 	}
@@ -203,7 +204,7 @@ func (s *NetworkService) List(ctx context.Context, req *pb.NetworkListRequest) (
 
 
 func (s *NetworkService) Create(ctx context.Context, req *pb.NetworkCreateRequest) (*pb.NetworkCreateResponse, error) {
 func (s *NetworkService) Create(ctx context.Context, req *pb.NetworkCreateRequest) (*pb.NetworkCreateResponse, error) {
 	logrus.Debugf("rpc call: network create: %s", req.Name)
 	logrus.Debugf("rpc call: network create: %s", req.Name)
-	network, err := ovpm.CreateNewNetwork(req.Name, req.CIDR)
+	network, err := ovpm.CreateNewNetwork(req.Name, req.CIDR, ovpm.NetworkTypeFromString(req.Type))
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -211,6 +212,7 @@ func (s *NetworkService) Create(ctx context.Context, req *pb.NetworkCreateReques
 	n := pb.Network{
 	n := pb.Network{
 		Name:      network.GetName(),
 		Name:      network.GetName(),
 		CIDR:      network.GetCIDR(),
 		CIDR:      network.GetCIDR(),
+		Type:      network.GetType().String(),
 		CreatedAt: network.GetCreatedAt(),
 		CreatedAt: network.GetCreatedAt(),
 	}
 	}
 
 
@@ -232,8 +234,19 @@ func (s *NetworkService) Delete(ctx context.Context, req *pb.NetworkDeleteReques
 	n := pb.Network{
 	n := pb.Network{
 		Name:      network.GetName(),
 		Name:      network.GetName(),
 		CIDR:      network.GetCIDR(),
 		CIDR:      network.GetCIDR(),
+		Type:      network.GetType().String(),
 		CreatedAt: network.GetCreatedAt(),
 		CreatedAt: network.GetCreatedAt(),
 	}
 	}
 
 
 	return &pb.NetworkDeleteResponse{Network: &n}, nil
 	return &pb.NetworkDeleteResponse{Network: &n}, nil
 }
 }
+
+func (s *NetworkService) GetAllTypes(ctx context.Context, req *pb.NetworkGetAllTypesRequest) (*pb.NetworkGetAllTypesResponse, error) {
+	logrus.Debugf("rpc call: network get-types")
+	var networkTypes []string
+	for _, nt := range ovpm.GetAllNetworkTypes() {
+		networkTypes = append(networkTypes, nt.String())
+	}
+
+	return &pb.NetworkGetAllTypesResponse{Types: networkTypes}, nil
+}

+ 5 - 5
bindata/bindata.go

@@ -87,7 +87,7 @@ func templateCcdFileTmpl() (*asset, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	info := bindataFileInfo{name: "template/ccd.file.tmpl", size: 74, mode: os.FileMode(420), modTime: time.Unix(1503322186, 0)}
+	info := bindataFileInfo{name: "template/ccd.file.tmpl", size: 74, mode: os.FileMode(420), modTime: time.Unix(1502998813, 0)}
 	a := &asset{bytes: bytes, info: info}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 	return a, nil
 }
 }
@@ -107,7 +107,7 @@ func templateClientOvpnTmpl() (*asset, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	info := bindataFileInfo{name: "template/client.ovpn.tmpl", size: 365, mode: os.FileMode(420), modTime: time.Unix(1503410118, 0)}
+	info := bindataFileInfo{name: "template/client.ovpn.tmpl", size: 365, mode: os.FileMode(420), modTime: time.Unix(1503411871, 0)}
 	a := &asset{bytes: bytes, info: info}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 	return a, nil
 }
 }
@@ -127,7 +127,7 @@ func templateDh4096PemTmpl() (*asset, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	info := bindataFileInfo{name: "template/dh4096.pem.tmpl", size: 1468, mode: os.FileMode(420), modTime: time.Unix(1503322186, 0)}
+	info := bindataFileInfo{name: "template/dh4096.pem.tmpl", size: 1468, mode: os.FileMode(420), modTime: time.Unix(1502796579, 0)}
 	a := &asset{bytes: bytes, info: info}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 	return a, nil
 }
 }
@@ -147,7 +147,7 @@ func templateIptablesTmpl() (*asset, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	info := bindataFileInfo{name: "template/iptables.tmpl", size: 0, mode: os.FileMode(420), modTime: time.Unix(1503322186, 0)}
+	info := bindataFileInfo{name: "template/iptables.tmpl", size: 0, mode: os.FileMode(420), modTime: time.Unix(1502796579, 0)}
 	a := &asset{bytes: bytes, info: info}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 	return a, nil
 }
 }
@@ -167,7 +167,7 @@ func templateServerConfTmpl() (*asset, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	info := bindataFileInfo{name: "template/server.conf.tmpl", size: 9585, mode: os.FileMode(420), modTime: time.Unix(1503322186, 0)}
+	info := bindataFileInfo{name: "template/server.conf.tmpl", size: 9585, mode: os.FileMode(420), modTime: time.Unix(1502796579, 0)}
 	a := &asset{bytes: bytes, info: info}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 	return a, nil
 }
 }

+ 20 - 10
cmd/ovpm/net.go

@@ -6,6 +6,7 @@ import (
 	"os"
 	"os"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/cad/ovpm"
 	"github.com/cad/ovpm/pb"
 	"github.com/cad/ovpm/pb"
 	"github.com/olekukonko/tablewriter"
 	"github.com/olekukonko/tablewriter"
 	"github.com/urfave/cli"
 	"github.com/urfave/cli"
@@ -23,13 +24,28 @@ var netDefineCommand = cli.Command{
 			Name:  "name, n",
 			Name:  "name, n",
 			Usage: "name of the network",
 			Usage: "name of the network",
 		},
 		},
+		cli.StringFlag{
+			Name:  "type, t",
+			Usage: "type of the network (see $ovpm net types)",
+		},
 	},
 	},
 	Action: func(c *cli.Context) error {
 	Action: func(c *cli.Context) error {
 		action = "net:create"
 		action = "net:create"
 		name := c.String("name")
 		name := c.String("name")
 		cidr := c.String("cidr")
 		cidr := c.String("cidr")
+		typ := c.String("type")
+
+		if name == "" || cidr == "" || typ == "" {
+			fmt.Println(cli.ShowSubcommandHelp(c))
+			os.Exit(1)
+		}
 
 
-		if name == "" || cidr == "" {
+		if ovpm.NetworkTypeFromString(typ) == ovpm.UNDEFINEDNET {
+			fmt.Printf("undefined network type %s", typ)
+			fmt.Println()
+			fmt.Println("Network Types:")
+			fmt.Println("    ", ovpm.GetAllNetworkTypes())
+			fmt.Println()
 			fmt.Println(cli.ShowSubcommandHelp(c))
 			fmt.Println(cli.ShowSubcommandHelp(c))
 			os.Exit(1)
 			os.Exit(1)
 		}
 		}
@@ -38,7 +54,7 @@ var netDefineCommand = cli.Command{
 		defer conn.Close()
 		defer conn.Close()
 		netSvc := pb.NewNetworkServiceClient(conn)
 		netSvc := pb.NewNetworkServiceClient(conn)
 
 
-		response, err := netSvc.Create(context.Background(), &pb.NetworkCreateRequest{Name: name, CIDR: cidr})
+		response, err := netSvc.Create(context.Background(), &pb.NetworkCreateRequest{Name: name, CIDR: cidr, Type: typ})
 		if err != nil {
 		if err != nil {
 			logrus.Errorf("network can not be created '%s': %v", name, err)
 			logrus.Errorf("network can not be created '%s': %v", name, err)
 			os.Exit(1)
 			os.Exit(1)
@@ -52,12 +68,6 @@ var netDefineCommand = cli.Command{
 var netListCommand = cli.Command{
 var netListCommand = cli.Command{
 	Name:  "list",
 	Name:  "list",
 	Usage: "List network definitions.",
 	Usage: "List network definitions.",
-	Flags: []cli.Flag{
-		cli.StringFlag{
-			Name:  "cidr, c",
-			Usage: "CIDR of the network",
-		},
-	},
 	Action: func(c *cli.Context) error {
 	Action: func(c *cli.Context) error {
 		action = "net:list"
 		action = "net:list"
 		conn := getConn(c.GlobalString("daemon-port"))
 		conn := getConn(c.GlobalString("daemon-port"))
@@ -71,10 +81,10 @@ var netListCommand = cli.Command{
 			return err
 			return err
 		}
 		}
 		table := tablewriter.NewWriter(os.Stdout)
 		table := tablewriter.NewWriter(os.Stdout)
-		table.SetHeader([]string{"#", "name", "cidr", "created at"})
+		table.SetHeader([]string{"#", "name", "cidr", "type", "users", "created at"})
 		//table.SetBorder(false)
 		//table.SetBorder(false)
 		for i, network := range resp.Networks {
 		for i, network := range resp.Networks {
-			data := []string{fmt.Sprintf("%v", i+1), network.Name, network.CIDR, network.CreatedAt}
+			data := []string{fmt.Sprintf("%v", i+1), network.Name, network.CIDR, network.Type, "-", network.CreatedAt}
 			table.Append(data)
 			table.Append(data)
 		}
 		}
 		table.Render()
 		table.Render()

+ 133 - 133
cmd/ovpm/user.go

@@ -13,114 +13,104 @@ import (
 	"github.com/urfave/cli"
 	"github.com/urfave/cli"
 )
 )
 
 
-var userGenconfigCommand = cli.Command{
-	Name:  "genconfig",
-	Usage: "Generate client config for the user. (.ovpn file)",
-	Flags: []cli.Flag{
-		cli.StringFlag{
-			Name:  "user, u",
-			Usage: "username of the vpn user",
-		},
-		cli.StringFlag{
-			Name:  "out, o",
-			Usage: ".ovpn file output path",
-		},
-	},
+var userListCommand = cli.Command{
+	Name:  "list",
+	Usage: "List VPN users.",
 	Action: func(c *cli.Context) error {
 	Action: func(c *cli.Context) error {
-		action = "user:export-config"
-		username := c.String("user")
-		output := c.String("out")
-
-		if username == "" {
-			fmt.Println(cli.ShowSubcommandHelp(c))
-			os.Exit(1)
-		}
-		if output == "" {
-			output = username + ".ovpn"
-		}
-
-		//conn := getConn(c.String("port"))
+		action = "user:list"
 		conn := getConn(c.GlobalString("daemon-port"))
 		conn := getConn(c.GlobalString("daemon-port"))
 		defer conn.Close()
 		defer conn.Close()
 		userSvc := pb.NewUserServiceClient(conn)
 		userSvc := pb.NewUserServiceClient(conn)
-		pb.NewVPNServiceClient(conn)
+		vpnSvc := pb.NewVPNServiceClient(conn)
 
 
-		res, err := userSvc.GenConfig(context.Background(), &pb.UserGenConfigRequest{Username: username})
+		server, err := vpnSvc.Status(context.Background(), &pb.VPNStatusRequest{})
 		if err != nil {
 		if err != nil {
-			logrus.Errorf("user config can not be exported %s: %v", username, err)
-			return err
-		}
-		emitToFile(output, res.ClientConfig, 0)
-		logrus.Infof("exported to %s", output)
-		return nil
-	},
-}
-
-var userRenewCommand = cli.Command{
-	Name:  "renew",
-	Usage: "Renew VPN user certificates.",
-	Flags: []cli.Flag{
-		cli.StringFlag{
-			Name:  "user, u",
-			Usage: "username of the vpn user",
-		},
-	},
-	Action: func(c *cli.Context) error {
-		action = "user:renew"
-		username := c.String("user")
-
-		if username == "" {
-			fmt.Println(cli.ShowSubcommandHelp(c))
+			logrus.Errorf("can not get server status: %v", err)
 			os.Exit(1)
 			os.Exit(1)
+			return err
 		}
 		}
 
 
-		//conn := getConn(c.String("port"))
-		conn := getConn(c.GlobalString("daemon-port"))
-		defer conn.Close()
-		userSvc := pb.NewUserServiceClient(conn)
-		pb.NewVPNServiceClient(conn)
-
-		_, err := userSvc.Renew(context.Background(), &pb.UserRenewRequest{Username: username})
+		resp, err := userSvc.List(context.Background(), &pb.UserListRequest{})
 		if err != nil {
 		if err != nil {
-			logrus.Errorf("can't renew user cert '%s': %v", username, err)
+			logrus.Errorf("users can not be fetched: %v", err)
 			os.Exit(1)
 			os.Exit(1)
 			return err
 			return err
 		}
 		}
-		logrus.Infof("user cert renewed: '%s'", username)
+		table := tablewriter.NewWriter(os.Stdout)
+		table.SetHeader([]string{"#", "username", "ip", "created at", "valid crt", "no gw"})
+		//table.SetBorder(false)
+		for i, user := range resp.Users {
+			static := ""
+			if user.HostID != 0 {
+				static = "s"
+			}
+			data := []string{fmt.Sprintf("%v", i+1), user.Username, fmt.Sprintf("%s %s", user.IPNet, static), user.CreatedAt, fmt.Sprintf("%t", user.ServerSerialNumber == server.SerialNumber), fmt.Sprintf("%t", user.NoGW)}
+			table.Append(data)
+		}
+		table.Render()
+
 		return nil
 		return nil
 	},
 	},
 }
 }
 
 
-var userDeleteCommand = cli.Command{
-	Name:  "delete",
-	Usage: "Delete a VPN user.",
+var userCreateCommand = cli.Command{
+	Name:  "create",
+	Usage: "Create a VPN user.",
 	Flags: []cli.Flag{
 	Flags: []cli.Flag{
 		cli.StringFlag{
 		cli.StringFlag{
-			Name:  "user, u",
-			Usage: "username of the vpn user",
+			Name:  "username, u",
+			Usage: "username for the vpn user",
+		},
+		cli.StringFlag{
+			Name:  "password, p",
+			Usage: "password for the vpn user",
+		},
+		cli.BoolFlag{
+			Name:  "no-gw",
+			Usage: "don't push vpn server as default gateway for this user",
+		},
+		cli.StringFlag{
+			Name:  "static",
+			Usage: "ip address for the vpn user",
 		},
 		},
 	},
 	},
 	Action: func(c *cli.Context) error {
 	Action: func(c *cli.Context) error {
-		action = "user:delete"
-		username := c.String("user")
+		action = "user:create"
+		username := c.String("username")
+		password := c.String("password")
+		noGW := c.Bool("no-gw")
+		static := c.String("static")
 
 
-		if username == "" {
+		if username == "" || password == "" {
 			fmt.Println(cli.ShowSubcommandHelp(c))
 			fmt.Println(cli.ShowSubcommandHelp(c))
 			os.Exit(1)
 			os.Exit(1)
 		}
 		}
 
 
+		var hostid uint32
+		if static != "" {
+			h := ovpm.IP2HostID(net.ParseIP(static).To4())
+			if h == 0 {
+				fmt.Println("--static flag takes a valid ipv4 address")
+				fmt.Println()
+				fmt.Println(cli.ShowSubcommandHelp(c))
+				os.Exit(1)
+			}
+
+			hostid = h
+		}
+
 		//conn := getConn(c.String("port"))
 		//conn := getConn(c.String("port"))
 		conn := getConn(c.GlobalString("daemon-port"))
 		conn := getConn(c.GlobalString("daemon-port"))
 		defer conn.Close()
 		defer conn.Close()
 		userSvc := pb.NewUserServiceClient(conn)
 		userSvc := pb.NewUserServiceClient(conn)
 
 
-		_, err := userSvc.Delete(context.Background(), &pb.UserDeleteRequest{Username: username})
+		response, err := userSvc.Create(context.Background(), &pb.UserCreateRequest{Username: username, Password: password, NoGW: noGW, HostID: hostid})
 		if err != nil {
 		if err != nil {
-			logrus.Errorf("user can not be deleted '%s': %v", username, err)
+			logrus.Errorf("user can not be created '%s': %v", username, err)
 			os.Exit(1)
 			os.Exit(1)
 			return err
 			return err
 		}
 		}
-		logrus.Infof("user deleted: %s", username)
+		logrus.Infof("user created: %s", response.Users[0].Username)
 		return nil
 		return nil
 	},
 	},
 }
 }
@@ -223,104 +213,114 @@ var userUpdateCommand = cli.Command{
 	},
 	},
 }
 }
 
 
-var userCreateCommand = cli.Command{
-	Name:  "create",
-	Usage: "Create a VPN user.",
+var userDeleteCommand = cli.Command{
+	Name:  "delete",
+	Usage: "Delete a VPN user.",
 	Flags: []cli.Flag{
 	Flags: []cli.Flag{
 		cli.StringFlag{
 		cli.StringFlag{
-			Name:  "username, u",
-			Usage: "username for the vpn user",
-		},
-		cli.StringFlag{
-			Name:  "password, p",
-			Usage: "password for the vpn user",
-		},
-		cli.BoolFlag{
-			Name:  "no-gw",
-			Usage: "don't push vpn server as default gateway for this user",
-		},
-		cli.StringFlag{
-			Name:  "static",
-			Usage: "ip address for the vpn user",
+			Name:  "user, u",
+			Usage: "username of the vpn user",
 		},
 		},
 	},
 	},
 	Action: func(c *cli.Context) error {
 	Action: func(c *cli.Context) error {
-		action = "user:create"
-		username := c.String("username")
-		password := c.String("password")
-		noGW := c.Bool("no-gw")
-		static := c.String("static")
+		action = "user:delete"
+		username := c.String("user")
 
 
-		if username == "" || password == "" {
+		if username == "" {
 			fmt.Println(cli.ShowSubcommandHelp(c))
 			fmt.Println(cli.ShowSubcommandHelp(c))
 			os.Exit(1)
 			os.Exit(1)
 		}
 		}
 
 
-		var hostid uint32
-		if static != "" {
-			h := ovpm.IP2HostID(net.ParseIP(static).To4())
-			if h == 0 {
-				fmt.Println("--static flag takes a valid ipv4 address")
-				fmt.Println()
-				fmt.Println(cli.ShowSubcommandHelp(c))
-				os.Exit(1)
-			}
-
-			hostid = h
-		}
-
 		//conn := getConn(c.String("port"))
 		//conn := getConn(c.String("port"))
 		conn := getConn(c.GlobalString("daemon-port"))
 		conn := getConn(c.GlobalString("daemon-port"))
 		defer conn.Close()
 		defer conn.Close()
 		userSvc := pb.NewUserServiceClient(conn)
 		userSvc := pb.NewUserServiceClient(conn)
 
 
-		response, err := userSvc.Create(context.Background(), &pb.UserCreateRequest{Username: username, Password: password, NoGW: noGW, HostID: hostid})
+		_, err := userSvc.Delete(context.Background(), &pb.UserDeleteRequest{Username: username})
 		if err != nil {
 		if err != nil {
-			logrus.Errorf("user can not be created '%s': %v", username, err)
+			logrus.Errorf("user can not be deleted '%s': %v", username, err)
 			os.Exit(1)
 			os.Exit(1)
 			return err
 			return err
 		}
 		}
-		logrus.Infof("user created: %s", response.Users[0].Username)
+		logrus.Infof("user deleted: %s", username)
 		return nil
 		return nil
 	},
 	},
 }
 }
 
 
-var userListCommand = cli.Command{
-	Name:  "list",
-	Usage: "List VPN users.",
+var userRenewCommand = cli.Command{
+	Name:  "renew",
+	Usage: "Renew VPN user certificates.",
+	Flags: []cli.Flag{
+		cli.StringFlag{
+			Name:  "user, u",
+			Usage: "username of the vpn user",
+		},
+	},
 	Action: func(c *cli.Context) error {
 	Action: func(c *cli.Context) error {
-		action = "user:list"
+		action = "user:renew"
+		username := c.String("user")
+
+		if username == "" {
+			fmt.Println(cli.ShowSubcommandHelp(c))
+			os.Exit(1)
+		}
+
+		//conn := getConn(c.String("port"))
 		conn := getConn(c.GlobalString("daemon-port"))
 		conn := getConn(c.GlobalString("daemon-port"))
 		defer conn.Close()
 		defer conn.Close()
 		userSvc := pb.NewUserServiceClient(conn)
 		userSvc := pb.NewUserServiceClient(conn)
-		vpnSvc := pb.NewVPNServiceClient(conn)
+		pb.NewVPNServiceClient(conn)
 
 
-		server, err := vpnSvc.Status(context.Background(), &pb.VPNStatusRequest{})
+		_, err := userSvc.Renew(context.Background(), &pb.UserRenewRequest{Username: username})
 		if err != nil {
 		if err != nil {
-			logrus.Errorf("can not get server status: %v", err)
+			logrus.Errorf("can't renew user cert '%s': %v", username, err)
 			os.Exit(1)
 			os.Exit(1)
 			return err
 			return err
 		}
 		}
+		logrus.Infof("user cert renewed: '%s'", username)
+		return nil
+	},
+}
 
 
-		resp, err := userSvc.List(context.Background(), &pb.UserListRequest{})
-		if err != nil {
-			logrus.Errorf("users can not be fetched: %v", err)
+var userGenconfigCommand = cli.Command{
+	Name:  "genconfig",
+	Usage: "Generate client config for the user. (.ovpn file)",
+	Flags: []cli.Flag{
+		cli.StringFlag{
+			Name:  "user, u",
+			Usage: "username of the vpn user",
+		},
+		cli.StringFlag{
+			Name:  "out, o",
+			Usage: ".ovpn file output path",
+		},
+	},
+	Action: func(c *cli.Context) error {
+		action = "user:export-config"
+		username := c.String("user")
+		output := c.String("out")
+
+		if username == "" {
+			fmt.Println(cli.ShowSubcommandHelp(c))
 			os.Exit(1)
 			os.Exit(1)
-			return err
 		}
 		}
-		table := tablewriter.NewWriter(os.Stdout)
-		table.SetHeader([]string{"#", "username", "ip", "created at", "valid crt", "no gw"})
-		//table.SetBorder(false)
-		for i, user := range resp.Users {
-			static := ""
-			if user.HostID != 0 {
-				static = "s"
-			}
-			data := []string{fmt.Sprintf("%v", i+1), user.Username, fmt.Sprintf("%s %s", user.IPNet, static), user.CreatedAt, fmt.Sprintf("%t", user.ServerSerialNumber == server.SerialNumber), fmt.Sprintf("%t", user.NoGW)}
-			table.Append(data)
+		if output == "" {
+			output = username + ".ovpn"
 		}
 		}
-		table.Render()
 
 
+		//conn := getConn(c.String("port"))
+		conn := getConn(c.GlobalString("daemon-port"))
+		defer conn.Close()
+		userSvc := pb.NewUserServiceClient(conn)
+		pb.NewVPNServiceClient(conn)
+
+		res, err := userSvc.GenConfig(context.Background(), &pb.UserGenConfigRequest{Username: username})
+		if err != nil {
+			logrus.Errorf("user config can not be exported %s: %v", username, err)
+			return err
+		}
+		emitToFile(output, res.ClientConfig, 0)
+		logrus.Infof("exported to %s", output)
 		return nil
 		return nil
 	},
 	},
 }
 }

+ 138 - 10
net.go

@@ -13,14 +13,63 @@ import (
 	"github.com/jinzhu/gorm"
 	"github.com/jinzhu/gorm"
 )
 )
 
 
+// NetworkType distinguishes different types of networks that is defined in the networks table.
+type NetworkType uint
+
+// NetworkTypes
+const (
+	UNDEFINEDNET NetworkType = iota
+	SERVERNET
+	ROUTE
+)
+
+var networkTypes = [...]struct {
+	Type   NetworkType
+	String string
+}{
+	{UNDEFINEDNET, "UNDEFINEDNET"},
+	{SERVERNET, "SERVERNET"},
+	{ROUTE, "ROUTE"},
+}
+
+// NetworkTypeFromString returns string representation of the network type.
+func NetworkTypeFromString(typ string) NetworkType {
+	for _, v := range networkTypes {
+		if v.String == typ {
+			return v.Type
+		}
+	}
+	return UNDEFINEDNET
+}
+
+// GetAllNetworkTypes returns all network types defined in the system.
+func GetAllNetworkTypes() []NetworkType {
+	var networkTypeList []NetworkType
+	for _, v := range networkTypes {
+		networkTypeList = append(networkTypeList, v.Type)
+	}
+	return networkTypeList
+}
+
+func (nt NetworkType) String() string {
+	for _, v := range networkTypes {
+		if v.Type == nt {
+			return v.String
+		}
+	}
+	return "UNDEFINEDNET"
+}
+
 // DBNetwork is database model for external networks on the VPN server.
 // DBNetwork is database model for external networks on the VPN server.
 type DBNetwork struct {
 type DBNetwork struct {
 	gorm.Model
 	gorm.Model
 	ServerID uint
 	ServerID uint
 	Server   DBServer
 	Server   DBServer
 
 
-	Name string `gorm:"unique_index"`
-	CIDR string
+	Name  string `gorm:"unique_index"`
+	CIDR  string
+	Type  NetworkType
+	Users []*DBUser `gorm:"many2many:network_users;"`
 }
 }
 
 
 // GetNetwork returns a network specified by its name.
 // GetNetwork returns a network specified by its name.
@@ -37,7 +86,7 @@ func GetNetwork(name string) (*DBNetwork, error) {
 	}
 	}
 
 
 	var network DBNetwork
 	var network DBNetwork
-	db.Where(&DBNetwork{Name: name}).First(&network)
+	db.Preload("Users").Where(&DBNetwork{Name: name}).First(&network)
 
 
 	if db.NewRecord(&network) {
 	if db.NewRecord(&network) {
 		return nil, fmt.Errorf("network not found %s", name)
 		return nil, fmt.Errorf("network not found %s", name)
@@ -49,13 +98,13 @@ func GetNetwork(name string) (*DBNetwork, error) {
 // GetAllNetworks returns all networks defined in the system.
 // GetAllNetworks returns all networks defined in the system.
 func GetAllNetworks() ([]*DBNetwork, error) {
 func GetAllNetworks() ([]*DBNetwork, error) {
 	var networks []*DBNetwork
 	var networks []*DBNetwork
-	db.Find(&networks)
+	db.Preload("Users").Find(&networks)
 
 
 	return networks, nil
 	return networks, nil
 }
 }
 
 
 // CreateNewNetwork creates a new network definition in the system.
 // CreateNewNetwork creates a new network definition in the system.
-func CreateNewNetwork(name, cidr string) (*DBNetwork, error) {
+func CreateNewNetwork(name, cidr string, nettype NetworkType) (*DBNetwork, error) {
 	if !IsInitialized() {
 	if !IsInitialized() {
 		return nil, fmt.Errorf("you first need to create server")
 		return nil, fmt.Errorf("you first need to create server")
 	}
 	}
@@ -68,7 +117,11 @@ func CreateNewNetwork(name, cidr string) (*DBNetwork, error) {
 	}
 	}
 
 
 	if !govalidator.IsCIDR(cidr) {
 	if !govalidator.IsCIDR(cidr) {
-		return nil, fmt.Errorf("validation error: `%s` must be a network in the CIDR form", name)
+		return nil, fmt.Errorf("validation error: `%s` must be a network in the CIDR form", cidr)
+	}
+
+	if nettype == UNDEFINEDNET {
+		return nil, fmt.Errorf("validation error: `%s` must be a valid network type", nettype)
 	}
 	}
 
 
 	_, ipnet, err := net.ParseCIDR(cidr)
 	_, ipnet, err := net.ParseCIDR(cidr)
@@ -77,8 +130,10 @@ func CreateNewNetwork(name, cidr string) (*DBNetwork, error) {
 	}
 	}
 
 
 	network := DBNetwork{
 	network := DBNetwork{
-		Name: name,
-		CIDR: ipnet.String(),
+		Name:  name,
+		CIDR:  ipnet.String(),
+		Type:  nettype,
+		Users: []*DBUser{},
 	}
 	}
 	db.Save(&network)
 	db.Save(&network)
 
 
@@ -102,6 +157,71 @@ func (n *DBNetwork) Delete() error {
 	return nil
 	return nil
 }
 }
 
 
+// Associate allows the given user access to this network.
+func (n *DBNetwork) Associate(username string) error {
+	if !IsInitialized() {
+		return fmt.Errorf("you first need to create server")
+	}
+	user, err := GetUser(username)
+	if err != nil {
+		return fmt.Errorf("user can not be fetched: %v", err)
+	}
+
+	var users []DBUser
+	userAssoc := db.Model(&n).Association("Users")
+	userAssoc.Find(&users)
+	var found bool
+	for _, u := range users {
+		if u.ID == user.ID {
+			found = true
+			break
+		}
+	}
+	if found {
+		return fmt.Errorf("user %s is already associated with the network %s", user.Username, n.Name)
+	}
+
+	userAssoc.Append(user)
+	if userAssoc.Error != nil {
+		return fmt.Errorf("association failed: %v", userAssoc.Error)
+	}
+	logrus.Infof("user '%s' is associated with the network '%s'", user.GetUsername(), n.Name)
+	return nil
+}
+
+// Dissociate breaks up the given users association to the said network.
+func (n *DBNetwork) Dissociate(username string) error {
+	if !IsInitialized() {
+		return fmt.Errorf("you first need to create server")
+	}
+
+	user, err := GetUser(username)
+	if err != nil {
+		return fmt.Errorf("user can not be fetched: %v", err)
+	}
+
+	var users []DBUser
+	userAssoc := db.Model(&n).Association("Users")
+	userAssoc.Find(&users)
+	var found bool
+	for _, u := range users {
+		if u.ID == user.ID {
+			found = true
+			break
+		}
+	}
+	if !found {
+		return fmt.Errorf("user %s is already not associated with the network %s", user.Username, n.Name)
+	}
+
+	userAssoc.Delete(user)
+	if userAssoc.Error != nil {
+		return fmt.Errorf("disassociation failed: %v", userAssoc.Error)
+	}
+	logrus.Infof("user '%s' is dissociated with the network '%s'", user.GetUsername(), n.Name)
+	return nil
+}
+
 // GetName returns network's name.
 // GetName returns network's name.
 func (n *DBNetwork) GetName() string {
 func (n *DBNetwork) GetName() string {
 	return n.Name
 	return n.Name
@@ -117,6 +237,16 @@ func (n *DBNetwork) GetCreatedAt() string {
 	return n.CreatedAt.Format(time.UnixDate)
 	return n.CreatedAt.Format(time.UnixDate)
 }
 }
 
 
+// GetType returns network's network type.
+func (n *DBNetwork) GetType() NetworkType {
+	return NetworkType(n.Type)
+}
+
+// GetAssociatedUsers returns network's associated users.
+func (n *DBNetwork) GetAssociatedUsers() []*DBUser {
+	return n.Users
+}
+
 // routedInterface returns a network interface that can route IP
 // routedInterface returns a network interface that can route IP
 // traffic and satisfies flags. It returns nil when an appropriate
 // traffic and satisfies flags. It returns nil when an appropriate
 // network interface is not found. Network must be "ip", "ip4" or
 // network interface is not found. Network must be "ip", "ip4" or
@@ -259,8 +389,6 @@ func enableNat() error {
 
 
 	// Append iptables nat rules.
 	// Append iptables nat rules.
 	ipt.AppendUnique("nat", "POSTROUTING", "-o", rif.Name, "-j", "MASQUERADE")
 	ipt.AppendUnique("nat", "POSTROUTING", "-o", rif.Name, "-j", "MASQUERADE")
-	// TODO(cad): we should use the interface name that we get when we query the system
-	// with the vpn server's internal ip address, instead of default "tun0".
 	ipt.AppendUnique("filter", "FORWARD", "-i", rif.Name, "-o", vpnIfc.Name, "-m", "state", "--state", "RELATED, ESTABLISHED", "-j", "ACCEPT")
 	ipt.AppendUnique("filter", "FORWARD", "-i", rif.Name, "-o", vpnIfc.Name, "-m", "state", "--state", "RELATED, ESTABLISHED", "-j", "ACCEPT")
 	ipt.AppendUnique("filter", "FORWARD", "-i", vpnIfc.Name, "-o", rif.Name, "-j", "ACCEPT")
 	ipt.AppendUnique("filter", "FORWARD", "-i", vpnIfc.Name, "-o", rif.Name, "-j", "ACCEPT")
 	return nil
 	return nil

+ 126 - 8
net_test.go

@@ -1,6 +1,8 @@
 package ovpm
 package ovpm
 
 
-import "testing"
+import (
+	"testing"
+)
 
 
 func TestVPNCreateNewNetwork(t *testing.T) {
 func TestVPNCreateNewNetwork(t *testing.T) {
 	// Initialize:
 	// Initialize:
@@ -13,8 +15,9 @@ func TestVPNCreateNewNetwork(t *testing.T) {
 	// Test:
 	// Test:
 	netName := "testnet"
 	netName := "testnet"
 	cidrStr := "192.168.1.0/24"
 	cidrStr := "192.168.1.0/24"
+	netType := SERVERNET
 
 
-	n, err := CreateNewNetwork(netName, cidrStr)
+	n, err := CreateNewNetwork(netName, cidrStr, netType)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("unexpected error when creating a new network: %v", err)
 		t.Fatalf("unexpected error when creating a new network: %v", err)
 	}
 	}
@@ -42,6 +45,10 @@ func TestVPNCreateNewNetwork(t *testing.T) {
 		t.Fatalf("network CIDR is expected to be '%s' but it's '%s' instead", cidrStr, network.CIDR)
 		t.Fatalf("network CIDR is expected to be '%s' but it's '%s' instead", cidrStr, network.CIDR)
 	}
 	}
 
 
+	if network.Type != netType {
+		t.Fatalf("network CIDR is expected to be '%s' but it's '%s' instead", netType, network.Type)
+	}
+
 }
 }
 
 
 func TestVPNDeleteNetwork(t *testing.T) {
 func TestVPNDeleteNetwork(t *testing.T) {
@@ -55,8 +62,9 @@ func TestVPNDeleteNetwork(t *testing.T) {
 	// Test:
 	// Test:
 	netName := "testnet"
 	netName := "testnet"
 	cidrStr := "192.168.1.0/24"
 	cidrStr := "192.168.1.0/24"
+	netType := SERVERNET
 
 
-	n, err := CreateNewNetwork(netName, cidrStr)
+	n, err := CreateNewNetwork(netName, cidrStr, netType)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("unexpected error when creating a new network: %v", err)
 		t.Fatalf("unexpected error when creating a new network: %v", err)
 	}
 	}
@@ -92,8 +100,9 @@ func TestVPNGetNetwork(t *testing.T) {
 	// Test:
 	// Test:
 	netName := "testnet"
 	netName := "testnet"
 	cidrStr := "192.168.1.0/24"
 	cidrStr := "192.168.1.0/24"
+	netType := SERVERNET
 
 
-	_, err := CreateNewNetwork(netName, cidrStr)
+	_, err := CreateNewNetwork(netName, cidrStr, netType)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("unexpected error when creating a new network: %v", err)
 		t.Fatalf("unexpected error when creating a new network: %v", err)
 	}
 	}
@@ -127,14 +136,15 @@ func TestVPNGetAllNetworks(t *testing.T) {
 	var getallnettests = []struct {
 	var getallnettests = []struct {
 		name    string
 		name    string
 		cidr    string
 		cidr    string
+		netType NetworkType
 		passing bool
 		passing bool
 	}{
 	}{
-		{"testnet1", "192.168.1.0/24", true},
-		{"testnet2", "10.10.0.0/16", true},
-		{"testnet3", "asdkfjadflsa", false},
+		{"testnet1", "192.168.1.0/24", SERVERNET, true},
+		{"testnet2", "10.10.0.0/16", SERVERNET, true},
+		{"testnet3", "asdkfjadflsa", SERVERNET, false},
 	}
 	}
 	for _, tt := range getallnettests {
 	for _, tt := range getallnettests {
-		_, err := CreateNewNetwork(tt.name, tt.cidr)
+		_, err := CreateNewNetwork(tt.name, tt.cidr, tt.netType)
 		if (err == nil) != tt.passing {
 		if (err == nil) != tt.passing {
 			t.Fatalf("unexpected error when creating a new network: %v", err)
 			t.Fatalf("unexpected error when creating a new network: %v", err)
 		}
 		}
@@ -153,10 +163,118 @@ func TestVPNGetAllNetworks(t *testing.T) {
 			if n.CIDR != tt.cidr {
 			if n.CIDR != tt.cidr {
 				t.Fatalf("network CIDR is expected to be '%s' but it's '%s'", tt.cidr, n.CIDR)
 				t.Fatalf("network CIDR is expected to be '%s' but it's '%s'", tt.cidr, n.CIDR)
 			}
 			}
+			if n.Type != tt.netType {
+				t.Fatalf("network CIDR is expected to be '%s' but it's '%s' instead", tt.netType, n.Type)
+			}
 		}
 		}
 	}
 	}
 }
 }
 
 
+func TestNetAssociate(t *testing.T) {
+	// Initialize:
+	setupTestCase()
+	SetupDB("sqlite3", ":memory:")
+	defer CeaseDB()
+	Init("localhost", "")
+
+	// Prepare:
+	// Test:
+	netName := "testnet"
+	cidrStr := "192.168.1.0/24"
+	netType := SERVERNET
+	userName := "testUser2"
+	user, _ := CreateNewUser(userName, "123", false, 0)
+
+	n, _ := CreateNewNetwork(netName, cidrStr, netType)
+	err := n.Associate(user.Username)
+	if err != nil {
+		t.Fatal(err)
+	}
+	n = nil
+
+	n, _ = GetNetwork(netName)
+
+	// Does number of associated users in the network object matches the number that we have created?
+	if count := len(n.Users); count != 1 {
+		t.Fatalf("network.Users count is expexted to be %d, but it's %d", 1, count)
+	}
+	err = n.Associate(user.Username)
+	if err == nil {
+		t.Fatalf("expected to get error but got no error instead")
+	}
+
+}
+
+func TestNetDissociate(t *testing.T) {
+	// Initialize:
+	setupTestCase()
+	SetupDB("sqlite3", ":memory:")
+	defer CeaseDB()
+	Init("localhost", "")
+
+	// Prepare:
+	// Test:
+	netName := "testnet"
+	cidrStr := "192.168.1.0/24"
+	netType := SERVERNET
+	userName := "testUser2"
+	user, _ := CreateNewUser(userName, "123", false, 0)
+
+	n, _ := CreateNewNetwork(netName, cidrStr, netType)
+	n.Associate(user.Username)
+
+	n = nil
+	n, _ = GetNetwork(netName)
+
+	// Does number of associated users in the network object matches the number that we have created?
+	if count := len(n.Users); count != 1 {
+		t.Fatalf("network.Users count is expexted to be %d, but it's %d", 1, count)
+	}
+
+	err := n.Dissociate(user.Username)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+
+	n = nil
+	n, _ = GetNetwork(netName)
+
+	// Does number of associated users in the network object matches the number that we have created?
+	if count := len(n.Users); count != 0 {
+		t.Fatalf("network.Users count is expexted to be %d, but it's %d", 0, count)
+	}
+	err = n.Dissociate(user.Username)
+	if err == nil {
+		t.Fatalf("expected error but got no error instead")
+	}
+}
+
+func TestNetGetAssociatedUsers(t *testing.T) {
+	// Initialize:
+	setupTestCase()
+	SetupDB("sqlite3", ":memory:")
+	defer CeaseDB()
+	Init("localhost", "")
+
+	// Prepare:
+	// Test:
+	netName := "testnet"
+	cidrStr := "192.168.1.0/24"
+	netType := SERVERNET
+	userName := "testUser2"
+	user, _ := CreateNewUser(userName, "123", false, 0)
+
+	n, _ := CreateNewNetwork(netName, cidrStr, netType)
+	n.Associate(user.Username)
+	n = nil
+	n, _ = GetNetwork(netName)
+
+	// Test:
+	if n.GetAssociatedUsers()[0].Username != user.Username {
+		t.Fatalf("returned associated user is expected to be the same user with the one we have created, but its not")
+	}
+}
+
 func init() {
 func init() {
 	// Init
 	// Init
 	Testing = true
 	Testing = true

+ 102 - 24
pb/network.pb.go

@@ -20,6 +20,7 @@ var _ = math.Inf
 type NetworkCreateRequest struct {
 type NetworkCreateRequest struct {
 	Name string `protobuf:"bytes,1,opt,name=Name" json:"Name,omitempty"`
 	Name string `protobuf:"bytes,1,opt,name=Name" json:"Name,omitempty"`
 	CIDR string `protobuf:"bytes,2,opt,name=CIDR" json:"CIDR,omitempty"`
 	CIDR string `protobuf:"bytes,2,opt,name=CIDR" json:"CIDR,omitempty"`
+	Type string `protobuf:"bytes,3,opt,name=Type" json:"Type,omitempty"`
 }
 }
 
 
 func (m *NetworkCreateRequest) Reset()                    { *m = NetworkCreateRequest{} }
 func (m *NetworkCreateRequest) Reset()                    { *m = NetworkCreateRequest{} }
@@ -41,6 +42,13 @@ func (m *NetworkCreateRequest) GetCIDR() string {
 	return ""
 	return ""
 }
 }
 
 
+func (m *NetworkCreateRequest) GetType() string {
+	if m != nil {
+		return m.Type
+	}
+	return ""
+}
+
 type NetworkListRequest struct {
 type NetworkListRequest struct {
 }
 }
 
 
@@ -65,16 +73,25 @@ func (m *NetworkDeleteRequest) GetName() string {
 	return ""
 	return ""
 }
 }
 
 
+type NetworkGetAllTypesRequest struct {
+}
+
+func (m *NetworkGetAllTypesRequest) Reset()                    { *m = NetworkGetAllTypesRequest{} }
+func (m *NetworkGetAllTypesRequest) String() string            { return proto.CompactTextString(m) }
+func (*NetworkGetAllTypesRequest) ProtoMessage()               {}
+func (*NetworkGetAllTypesRequest) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{3} }
+
 type Network struct {
 type Network struct {
 	Name      string `protobuf:"bytes,1,opt,name=Name" json:"Name,omitempty"`
 	Name      string `protobuf:"bytes,1,opt,name=Name" json:"Name,omitempty"`
 	CIDR      string `protobuf:"bytes,2,opt,name=CIDR" json:"CIDR,omitempty"`
 	CIDR      string `protobuf:"bytes,2,opt,name=CIDR" json:"CIDR,omitempty"`
-	CreatedAt string `protobuf:"bytes,3,opt,name=CreatedAt" json:"CreatedAt,omitempty"`
+	Type      string `protobuf:"bytes,3,opt,name=Type" json:"Type,omitempty"`
+	CreatedAt string `protobuf:"bytes,4,opt,name=CreatedAt" json:"CreatedAt,omitempty"`
 }
 }
 
 
 func (m *Network) Reset()                    { *m = Network{} }
 func (m *Network) Reset()                    { *m = Network{} }
 func (m *Network) String() string            { return proto.CompactTextString(m) }
 func (m *Network) String() string            { return proto.CompactTextString(m) }
 func (*Network) ProtoMessage()               {}
 func (*Network) ProtoMessage()               {}
-func (*Network) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{3} }
+func (*Network) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{4} }
 
 
 func (m *Network) GetName() string {
 func (m *Network) GetName() string {
 	if m != nil {
 	if m != nil {
@@ -90,6 +107,13 @@ func (m *Network) GetCIDR() string {
 	return ""
 	return ""
 }
 }
 
 
+func (m *Network) GetType() string {
+	if m != nil {
+		return m.Type
+	}
+	return ""
+}
+
 func (m *Network) GetCreatedAt() string {
 func (m *Network) GetCreatedAt() string {
 	if m != nil {
 	if m != nil {
 		return m.CreatedAt
 		return m.CreatedAt
@@ -104,7 +128,7 @@ type NetworkCreateResponse struct {
 func (m *NetworkCreateResponse) Reset()                    { *m = NetworkCreateResponse{} }
 func (m *NetworkCreateResponse) Reset()                    { *m = NetworkCreateResponse{} }
 func (m *NetworkCreateResponse) String() string            { return proto.CompactTextString(m) }
 func (m *NetworkCreateResponse) String() string            { return proto.CompactTextString(m) }
 func (*NetworkCreateResponse) ProtoMessage()               {}
 func (*NetworkCreateResponse) ProtoMessage()               {}
-func (*NetworkCreateResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{4} }
+func (*NetworkCreateResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{5} }
 
 
 func (m *NetworkCreateResponse) GetNetwork() *Network {
 func (m *NetworkCreateResponse) GetNetwork() *Network {
 	if m != nil {
 	if m != nil {
@@ -120,7 +144,7 @@ type NetworkListResponse struct {
 func (m *NetworkListResponse) Reset()                    { *m = NetworkListResponse{} }
 func (m *NetworkListResponse) Reset()                    { *m = NetworkListResponse{} }
 func (m *NetworkListResponse) String() string            { return proto.CompactTextString(m) }
 func (m *NetworkListResponse) String() string            { return proto.CompactTextString(m) }
 func (*NetworkListResponse) ProtoMessage()               {}
 func (*NetworkListResponse) ProtoMessage()               {}
-func (*NetworkListResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{5} }
+func (*NetworkListResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{6} }
 
 
 func (m *NetworkListResponse) GetNetworks() []*Network {
 func (m *NetworkListResponse) GetNetworks() []*Network {
 	if m != nil {
 	if m != nil {
@@ -136,7 +160,7 @@ type NetworkDeleteResponse struct {
 func (m *NetworkDeleteResponse) Reset()                    { *m = NetworkDeleteResponse{} }
 func (m *NetworkDeleteResponse) Reset()                    { *m = NetworkDeleteResponse{} }
 func (m *NetworkDeleteResponse) String() string            { return proto.CompactTextString(m) }
 func (m *NetworkDeleteResponse) String() string            { return proto.CompactTextString(m) }
 func (*NetworkDeleteResponse) ProtoMessage()               {}
 func (*NetworkDeleteResponse) ProtoMessage()               {}
-func (*NetworkDeleteResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{6} }
+func (*NetworkDeleteResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{7} }
 
 
 func (m *NetworkDeleteResponse) GetNetwork() *Network {
 func (m *NetworkDeleteResponse) GetNetwork() *Network {
 	if m != nil {
 	if m != nil {
@@ -145,14 +169,32 @@ func (m *NetworkDeleteResponse) GetNetwork() *Network {
 	return nil
 	return nil
 }
 }
 
 
+type NetworkGetAllTypesResponse struct {
+	Types []string `protobuf:"bytes,1,rep,name=Types" json:"Types,omitempty"`
+}
+
+func (m *NetworkGetAllTypesResponse) Reset()                    { *m = NetworkGetAllTypesResponse{} }
+func (m *NetworkGetAllTypesResponse) String() string            { return proto.CompactTextString(m) }
+func (*NetworkGetAllTypesResponse) ProtoMessage()               {}
+func (*NetworkGetAllTypesResponse) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{8} }
+
+func (m *NetworkGetAllTypesResponse) GetTypes() []string {
+	if m != nil {
+		return m.Types
+	}
+	return nil
+}
+
 func init() {
 func init() {
 	proto.RegisterType((*NetworkCreateRequest)(nil), "pb.NetworkCreateRequest")
 	proto.RegisterType((*NetworkCreateRequest)(nil), "pb.NetworkCreateRequest")
 	proto.RegisterType((*NetworkListRequest)(nil), "pb.NetworkListRequest")
 	proto.RegisterType((*NetworkListRequest)(nil), "pb.NetworkListRequest")
 	proto.RegisterType((*NetworkDeleteRequest)(nil), "pb.NetworkDeleteRequest")
 	proto.RegisterType((*NetworkDeleteRequest)(nil), "pb.NetworkDeleteRequest")
+	proto.RegisterType((*NetworkGetAllTypesRequest)(nil), "pb.NetworkGetAllTypesRequest")
 	proto.RegisterType((*Network)(nil), "pb.Network")
 	proto.RegisterType((*Network)(nil), "pb.Network")
 	proto.RegisterType((*NetworkCreateResponse)(nil), "pb.NetworkCreateResponse")
 	proto.RegisterType((*NetworkCreateResponse)(nil), "pb.NetworkCreateResponse")
 	proto.RegisterType((*NetworkListResponse)(nil), "pb.NetworkListResponse")
 	proto.RegisterType((*NetworkListResponse)(nil), "pb.NetworkListResponse")
 	proto.RegisterType((*NetworkDeleteResponse)(nil), "pb.NetworkDeleteResponse")
 	proto.RegisterType((*NetworkDeleteResponse)(nil), "pb.NetworkDeleteResponse")
+	proto.RegisterType((*NetworkGetAllTypesResponse)(nil), "pb.NetworkGetAllTypesResponse")
 }
 }
 
 
 // Reference imports to suppress errors if they are not otherwise used.
 // Reference imports to suppress errors if they are not otherwise used.
@@ -169,6 +211,7 @@ type NetworkServiceClient interface {
 	Create(ctx context.Context, in *NetworkCreateRequest, opts ...grpc.CallOption) (*NetworkCreateResponse, error)
 	Create(ctx context.Context, in *NetworkCreateRequest, opts ...grpc.CallOption) (*NetworkCreateResponse, error)
 	List(ctx context.Context, in *NetworkListRequest, opts ...grpc.CallOption) (*NetworkListResponse, error)
 	List(ctx context.Context, in *NetworkListRequest, opts ...grpc.CallOption) (*NetworkListResponse, error)
 	Delete(ctx context.Context, in *NetworkDeleteRequest, opts ...grpc.CallOption) (*NetworkDeleteResponse, error)
 	Delete(ctx context.Context, in *NetworkDeleteRequest, opts ...grpc.CallOption) (*NetworkDeleteResponse, error)
+	GetAllTypes(ctx context.Context, in *NetworkGetAllTypesRequest, opts ...grpc.CallOption) (*NetworkGetAllTypesResponse, error)
 }
 }
 
 
 type networkServiceClient struct {
 type networkServiceClient struct {
@@ -206,12 +249,22 @@ func (c *networkServiceClient) Delete(ctx context.Context, in *NetworkDeleteRequ
 	return out, nil
 	return out, nil
 }
 }
 
 
+func (c *networkServiceClient) GetAllTypes(ctx context.Context, in *NetworkGetAllTypesRequest, opts ...grpc.CallOption) (*NetworkGetAllTypesResponse, error) {
+	out := new(NetworkGetAllTypesResponse)
+	err := grpc.Invoke(ctx, "/pb.NetworkService/GetAllTypes", in, out, c.cc, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 // Server API for NetworkService service
 // Server API for NetworkService service
 
 
 type NetworkServiceServer interface {
 type NetworkServiceServer interface {
 	Create(context.Context, *NetworkCreateRequest) (*NetworkCreateResponse, error)
 	Create(context.Context, *NetworkCreateRequest) (*NetworkCreateResponse, error)
 	List(context.Context, *NetworkListRequest) (*NetworkListResponse, error)
 	List(context.Context, *NetworkListRequest) (*NetworkListResponse, error)
 	Delete(context.Context, *NetworkDeleteRequest) (*NetworkDeleteResponse, error)
 	Delete(context.Context, *NetworkDeleteRequest) (*NetworkDeleteResponse, error)
+	GetAllTypes(context.Context, *NetworkGetAllTypesRequest) (*NetworkGetAllTypesResponse, error)
 }
 }
 
 
 func RegisterNetworkServiceServer(s *grpc.Server, srv NetworkServiceServer) {
 func RegisterNetworkServiceServer(s *grpc.Server, srv NetworkServiceServer) {
@@ -272,6 +325,24 @@ func _NetworkService_Delete_Handler(srv interface{}, ctx context.Context, dec fu
 	return interceptor(ctx, in, info, handler)
 	return interceptor(ctx, in, info, handler)
 }
 }
 
 
+func _NetworkService_GetAllTypes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(NetworkGetAllTypesRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(NetworkServiceServer).GetAllTypes(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/pb.NetworkService/GetAllTypes",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(NetworkServiceServer).GetAllTypes(ctx, req.(*NetworkGetAllTypesRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 var _NetworkService_serviceDesc = grpc.ServiceDesc{
 var _NetworkService_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "pb.NetworkService",
 	ServiceName: "pb.NetworkService",
 	HandlerType: (*NetworkServiceServer)(nil),
 	HandlerType: (*NetworkServiceServer)(nil),
@@ -288,6 +359,10 @@ var _NetworkService_serviceDesc = grpc.ServiceDesc{
 			MethodName: "Delete",
 			MethodName: "Delete",
 			Handler:    _NetworkService_Delete_Handler,
 			Handler:    _NetworkService_Delete_Handler,
 		},
 		},
+		{
+			MethodName: "GetAllTypes",
+			Handler:    _NetworkService_GetAllTypes_Handler,
+		},
 	},
 	},
 	Streams:  []grpc.StreamDesc{},
 	Streams:  []grpc.StreamDesc{},
 	Metadata: "network.proto",
 	Metadata: "network.proto",
@@ -296,23 +371,26 @@ var _NetworkService_serviceDesc = grpc.ServiceDesc{
 func init() { proto.RegisterFile("network.proto", fileDescriptor2) }
 func init() { proto.RegisterFile("network.proto", fileDescriptor2) }
 
 
 var fileDescriptor2 = []byte{
 var fileDescriptor2 = []byte{
-	// 273 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x4b, 0x2d, 0x29,
-	0xcf, 0x2f, 0xca, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2a, 0x48, 0x52, 0xb2, 0xe3,
-	0x12, 0xf1, 0x83, 0x08, 0x3a, 0x17, 0xa5, 0x26, 0x96, 0xa4, 0x06, 0xa5, 0x16, 0x96, 0xa6, 0x16,
-	0x97, 0x08, 0x09, 0x71, 0xb1, 0xf8, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06,
-	0x81, 0xd9, 0x20, 0x31, 0x67, 0x4f, 0x97, 0x20, 0x09, 0x26, 0x88, 0x18, 0x88, 0xad, 0x24, 0xc2,
-	0x25, 0x04, 0xd5, 0xef, 0x93, 0x59, 0x5c, 0x02, 0xd5, 0xad, 0xa4, 0x05, 0x37, 0xd5, 0x25, 0x35,
-	0x27, 0x15, 0xaf, 0xa9, 0x4a, 0xfe, 0x5c, 0xec, 0x50, 0xb5, 0xc4, 0x5a, 0x2a, 0x24, 0xc3, 0xc5,
-	0x09, 0x71, 0x6d, 0x8a, 0x63, 0x89, 0x04, 0x33, 0x58, 0x02, 0x21, 0xa0, 0x64, 0xc7, 0x25, 0x8a,
-	0xe6, 0xa5, 0xe2, 0x82, 0xfc, 0xbc, 0xe2, 0x54, 0x21, 0x55, 0xb8, 0x4d, 0x60, 0x1b, 0xb8, 0x8d,
-	0xb8, 0xf5, 0x0a, 0x92, 0xf4, 0xa0, 0x42, 0x41, 0x30, 0x39, 0x25, 0x3b, 0x2e, 0x61, 0x14, 0x2f,
-	0x41, 0x75, 0xab, 0x73, 0x71, 0x40, 0x85, 0x8b, 0x25, 0x18, 0x15, 0x98, 0xd1, 0xb5, 0xc3, 0x25,
-	0x91, 0xec, 0x87, 0x79, 0x9e, 0x24, 0xfb, 0x8d, 0xce, 0x32, 0x72, 0xf1, 0x41, 0xd9, 0xc1, 0xa9,
-	0x45, 0x65, 0x99, 0xc9, 0xa9, 0x42, 0xf6, 0x5c, 0x6c, 0x10, 0xbf, 0x08, 0x49, 0x20, 0x69, 0x41,
-	0x89, 0x31, 0x29, 0x49, 0x2c, 0x32, 0x10, 0x8b, 0x95, 0x18, 0x84, 0x2c, 0xb9, 0x58, 0x40, 0x9e,
-	0x11, 0x12, 0x43, 0x52, 0x84, 0x14, 0x61, 0x52, 0xe2, 0x18, 0xe2, 0x70, 0xad, 0xf6, 0x5c, 0x6c,
-	0x10, 0x7f, 0xa0, 0xd8, 0x8d, 0x12, 0xaf, 0x28, 0x76, 0xa3, 0x7a, 0x5a, 0x89, 0x21, 0x89, 0x0d,
-	0x9c, 0xda, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf0, 0x5e, 0xe8, 0x7e, 0x02, 0x00,
-	0x00,
+	// 334 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x53, 0xcd, 0x4e, 0x32, 0x41,
+	0x10, 0xe4, 0xef, 0xe3, 0x93, 0x26, 0x7a, 0x68, 0x51, 0x87, 0xf5, 0x27, 0x64, 0x12, 0xa3, 0xf1,
+	0xc0, 0x01, 0x4f, 0x5e, 0x20, 0x04, 0x12, 0x63, 0x62, 0x38, 0x8c, 0xbe, 0x00, 0x60, 0x1f, 0x88,
+	0x2b, 0xbb, 0xee, 0x8c, 0x1a, 0x1f, 0xc6, 0x77, 0x35, 0xf3, 0xb3, 0xeb, 0x0c, 0xa0, 0x89, 0xf1,
+	0xd6, 0x5b, 0xd5, 0x5d, 0x35, 0x5d, 0xe9, 0x85, 0xed, 0x25, 0xa9, 0xb7, 0x24, 0x7b, 0xec, 0xa6,
+	0x59, 0xa2, 0x12, 0xac, 0xa4, 0x33, 0x2e, 0xa0, 0x35, 0xb1, 0xe0, 0x28, 0xa3, 0xa9, 0x22, 0x41,
+	0xcf, 0x2f, 0x24, 0x15, 0x22, 0xd4, 0x26, 0xd3, 0x27, 0x62, 0xe5, 0x4e, 0xf9, 0xbc, 0x21, 0x4c,
+	0xad, 0xb1, 0xd1, 0xcd, 0x58, 0xb0, 0x8a, 0xc5, 0x74, 0xad, 0xb1, 0xfb, 0xf7, 0x94, 0x58, 0xd5,
+	0x62, 0xba, 0xe6, 0x2d, 0x40, 0xa7, 0x79, 0xbb, 0x90, 0xca, 0x29, 0xf2, 0x8b, 0xc2, 0x69, 0x4c,
+	0x31, 0xfd, 0xe8, 0xc4, 0x0f, 0xa1, 0xed, 0x7a, 0xaf, 0x49, 0x0d, 0xe3, 0x58, 0xcb, 0xca, 0x5c,
+	0x68, 0x0e, 0xff, 0x1d, 0xf9, 0x97, 0x57, 0xe2, 0x11, 0x34, 0xec, 0xca, 0x0f, 0x43, 0xc5, 0x6a,
+	0x86, 0xf8, 0x02, 0x78, 0x1f, 0xf6, 0x56, 0x72, 0x91, 0x69, 0xb2, 0x94, 0x84, 0xa7, 0x85, 0xbb,
+	0x71, 0x6d, 0xf6, 0x9a, 0xdd, 0x74, 0xd6, 0x75, 0x90, 0xc8, 0x39, 0xde, 0x87, 0xdd, 0x20, 0x03,
+	0x37, 0x7d, 0x06, 0x5b, 0x0e, 0x96, 0xac, 0xdc, 0xa9, 0xae, 0x8e, 0x17, 0xa4, 0xe7, 0x9f, 0xa7,
+	0xf5, 0x3b, 0xff, 0x1e, 0x44, 0x9b, 0x12, 0x74, 0x22, 0x2d, 0xf8, 0x67, 0x00, 0xf3, 0x86, 0x86,
+	0xb0, 0x1f, 0xbd, 0x8f, 0x0a, 0xec, 0xb8, 0xa1, 0x3b, 0xca, 0x5e, 0x17, 0x73, 0xc2, 0x01, 0xd4,
+	0xed, 0xfe, 0xc8, 0x3c, 0x9b, 0xe0, 0x54, 0xa2, 0xf6, 0x06, 0xc6, 0xfa, 0xf0, 0x12, 0x5e, 0x41,
+	0x4d, 0x07, 0x80, 0xfb, 0x5e, 0x93, 0x77, 0x15, 0xd1, 0xc1, 0x1a, 0x5e, 0x8c, 0x0e, 0xa0, 0x6e,
+	0x77, 0x0f, 0xbc, 0x83, 0xe3, 0x09, 0xbc, 0xc3, 0xa0, 0x78, 0x09, 0x27, 0xd0, 0xf4, 0x96, 0xc7,
+	0x63, 0xaf, 0x77, 0xfd, 0xac, 0xa2, 0x93, 0xef, 0xe8, 0x5c, 0x6f, 0x56, 0x37, 0xbf, 0xcd, 0xe5,
+	0x67, 0x00, 0x00, 0x00, 0xff, 0xff, 0xeb, 0xb1, 0x53, 0x85, 0x47, 0x03, 0x00, 0x00,
 }
 }

+ 8 - 3
pb/network.proto

@@ -5,29 +5,34 @@ package pb;
 message NetworkCreateRequest {
 message NetworkCreateRequest {
   string Name = 1;
   string Name = 1;
   string CIDR = 2;
   string CIDR = 2;
+  string Type = 3;
 }
 }
 message NetworkListRequest {}
 message NetworkListRequest {}
 message NetworkDeleteRequest {
 message NetworkDeleteRequest {
   string Name = 1;
   string Name = 1;
 }
 }
-
+message NetworkGetAllTypesRequest {}
 service NetworkService {
 service NetworkService {
   rpc Create (NetworkCreateRequest) returns (NetworkCreateResponse) {}
   rpc Create (NetworkCreateRequest) returns (NetworkCreateResponse) {}
   rpc List (NetworkListRequest) returns (NetworkListResponse) {}
   rpc List (NetworkListRequest) returns (NetworkListResponse) {}
   rpc Delete (NetworkDeleteRequest) returns (NetworkDeleteResponse) {}
   rpc Delete (NetworkDeleteRequest) returns (NetworkDeleteResponse) {}
+  rpc GetAllTypes(NetworkGetAllTypesRequest) returns (NetworkGetAllTypesResponse) {}
 }
 }
 message Network {
 message Network {
   string Name = 1;
   string Name = 1;
   string CIDR = 2;
   string CIDR = 2;
-  string CreatedAt = 3;
+  string Type = 3;
+  string CreatedAt = 4;
 }
 }
 message NetworkCreateResponse {
 message NetworkCreateResponse {
   Network Network = 1;
   Network Network = 1;
 }
 }
-
 message NetworkListResponse {
 message NetworkListResponse {
   repeated Network Networks = 1;
   repeated Network Networks = 1;
 }
 }
 message NetworkDeleteResponse {
 message NetworkDeleteResponse {
   Network Network = 1;
   Network Network = 1;
 }
 }
+message NetworkGetAllTypesResponse {
+  repeated string Types = 1;
+}

+ 2 - 0
pb/user.pb.go

@@ -25,10 +25,12 @@ It has these top-level messages:
 	NetworkCreateRequest
 	NetworkCreateRequest
 	NetworkListRequest
 	NetworkListRequest
 	NetworkDeleteRequest
 	NetworkDeleteRequest
+	NetworkGetAllTypesRequest
 	Network
 	Network
 	NetworkCreateResponse
 	NetworkCreateResponse
 	NetworkListResponse
 	NetworkListResponse
 	NetworkDeleteResponse
 	NetworkDeleteResponse
+	NetworkGetAllTypesResponse
 */
 */
 package pb
 package pb