Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support nconnect network feature #71

Merged
merged 1 commit into from
Nov 24, 2023
Merged

Conversation

billfort
Copy link
Contributor

  1. nConnect nodes can set up a virtual private network;
  2. nConnect network Manager is a new node role of nConnect, which is the administrator node of the network;
  3. nConnect network Member node can join this Manager's network;
  4. When Manager authorize a member's JoinNetwork request, the member can communicate with other nodes according to the Manager access setting.
  5. Each member node has a network interface that assigned the network's local IP, and members can access each other by this local IP.

admin/server.go Outdated
@@ -10,6 +10,12 @@ import (
tunnel "github.com/nknorg/nkn-tunnel"
)

var networkMemberIAccept []string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't recommend using global variable

Copy link
Contributor Author

@billfort billfort Aug 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modified.

admin/server.go Outdated
continue
}

var perm permission
if isAcceptAddr {
if isAcceptAddr || isNetworkMemberAddr {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should separate the permissions of accept addr and member addr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modified.

arch/tun_test.go Outdated
dnsServers := []string{"192.168.0.1"}
persist := false

OpenTunDevice(name, addr, gw, mask, dnsServers, persist)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only works on Windows and Mac but not Linux. Check the tun code for different platforms

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, got it.

config/config.go Outdated
@@ -123,6 +125,10 @@ type Config struct {
lock sync.RWMutex
AcceptAddrs []string `json:"acceptAddrs"`
AdminAddrs []string `json:"adminAddrs"`

// nconnect network
NodeName string `json:"nodeName,omitempty" long:"node-name" description:"Node name, will be used as nConnect network node name"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(network member only) Node name that will be used as to join a network

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modified.

config/config.go Outdated

// nconnect network
NodeName string `json:"nodeName,omitempty" long:"node-name" description:"Node name, will be used as nConnect network node name"`
ManagerAddress string `json:"managerAddress,omitempty" long:"manager-address" description:"Manager address, will be used as nConnect network manager address"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(network member only) Manager address to connect to when joining a network

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modified.

TunaGeoDBPath: opts.TunaGeoDBPath,
TunaMeasureBandwidth: !opts.TunaDisableMeasureBandwidth,
TunaMeasureStoragePath: opts.TunaMeasureStoragePath,
TunaMeasurementBytesDownLink: opts.TunaMeasureBandwidthBytes,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't modify this section. Maybe keyboard mistake. let me recover it.

err := nc.opts.VerifyClient()
if err != nil {
return err
if !nc.opts.NetworkMember {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say we put this if inside VerifyClient()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VerifyClient is a method of Config, and it can't get the mode(client, server, or network) of nConnect. An alter way is to put VerifyClient into Opts.

util/util.go Outdated
// If paramenter port is 0, start from system returned free port
// The returned port is free in TCP and UDP
func GetFreePort(port int) (int, error) {
for i := 0; i < 100; i++ {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

retry without sleep is not likely to make a difference. We should add sleep (but also reduce the number of retries so it won't take forever)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here each retry will increase port. Such as we first retry 1080, if it is busy, then try 1081, 1082, ... until we get a free port. Is it suitable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the code to increase port? And since you don't need a fixed port, why not a random dynamic port?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when UDP listen fails, the port will be increased to try the next port. if argument port=0, it will try a random dynamic port. This modification expands the use cases of this function: 1. get a free port from system random port; 2 get a specific port or subsequent port, such as 1080, 1081, ...

if err != nil { l.Close() port++ continue }

nconnect.go Outdated
nc.ssClientConfig.Client = from[0]
nc.ssClientConfig.DefaultClient = from[0] // the first config is the default client
} else {
nc.ssClientConfig.Client = "127.0.0.1:0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should mean start ss with a random port right? Or you don't want ss at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ssClientConfig.Client is just a condition for starting ss client. Before we support multi-tunnels, its value is the from source of the tunnel. Now we support multi-tunnel, the from sources will be gotten from the target IP. so ssClientConfig.Client 's value is only for starting client now. the condition is:
if flags.Client != "" { // client mode

nconnect.go Outdated

log.Println("Client socks proxy listen address:", nc.opts.LocalSocksAddr)

if nc.opts.Tun || nc.opts.VPN {
tunDevice, err := arch.OpenTunDevice(nc.opts.TunName, nc.opts.TunAddr, nc.opts.TunGateway, nc.opts.TunMask, nc.opts.TunDNS, true)
tunDevice, err := arch.OpenTunDevice(nc.opts.TunName, nc.opts.TunAddr, nc.opts.TunGateway, nc.opts.TunMask, nc.opts.TunDNS, false)
Copy link
Member

@yilunzhang yilunzhang Jul 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI persist option only works on Linux. On Mac/Win it's not used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. let me restore it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean you can still use it if needed, but you need to make sure the changes will work on all platforms we support

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thanks for the advice.

nc.waitForSignal()

return nil
}

func (nc *nconnect) startSSAndTunnel() {
func (nc *nconnect) startSSAndTunnel(client bool) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend we pass in the config

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modified.

nconnect.go Outdated
}
ssAddr := "127.0.0.1:" + strconv.Itoa(port)

toAddr := strings.Replace(addr, "client.", "", -1) // remove network member's client. prefix, it should be server's address
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have to assume the address format here? Why can't we just store and use other members' tunnel address directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modified. The nconnect server's listen address is registered to the network manager and notified to the network authorized members.

nconnect.go Outdated

toAddr := strings.Replace(addr, "client.", "", -1) // remove network member's client. prefix, it should be server's address
adminAddr := nc.opts.AdminIdentifier + "." + toAddr
if remoteInfo, err := nc.getRemoteInfo(adminAddr); err == nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For network mode, why we need to use other members' local IP at all? We should use the assigned IP address, which doesn't need getRemoteInfo at all

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modified.

nconnect.go Outdated

if len(from) > 0 {
identifier := config.RandomIdentifier()
tunnels, err := tunnel.NewTunnels(nc.account, identifier, from, to, nc.opts.Tuna, nc.tunnelConfig)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, so we are creating a new client each time we update the access address?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I will modify nkn-tunnel to reuse multi-client.


const (
defaultDomain = "nconnect.nkn"
defaultIpStart = "192.168.0.1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't use 192.168 by default, this IP range conflicts with too many routers. I would use 10.X.Y range with a rarely used X and Y

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modified. It is configurable on the manager's web page too.

return nil, err
}

func GetMultiClient(opts *config.Opts) (*nkn.MultiClient, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like there are lots of redundant code here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. removed.

return mc, nil
}

func GetAdminClient(opts *config.Opts) *admin.Client {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there function ever called?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Let me remove it.


var onMessage *nkn.OnMessage
for i := 0; i < 3; i++ {
onMessage, err = mc.Send(nkn.NewStringArray(address), reqBytes, nil)
Copy link
Member

@yilunzhang yilunzhang Jul 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onMessage -> onReply

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modified.

NodeInfo []*nodeInfo `json:"nodeInfo"`
}

func SendMsg(mc *admin.Client, address string, msg interface{}, waitResponse bool) (*managerToMember, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codes in this function seem to be mostly duplicated (admin/client.go:RPCCall())

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modified.

if err := m.saveNetworkData(); err != nil {
return nil, err
}
noti := &managerToMember{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does noti stands for?

Copy link
Contributor Author

@billfort billfort Jul 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, it is an abbreviation for notification, the message initiated from manager to member is set as notification.
I have modified all noti to notification for better readability.

return nil
}

func (m *Manager) UpdateName(address, name string) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should manager also send message to members on update event? (update name, update ip, etc)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I added two multicase functions to notify related members.


for _, n := range m.networkData.Member {
acceptAddresses := m.networkData.AcceptAddress[n.Address]
if len(acceptAddresses) > 0 && acceptAddresses[0] == AcceptAll {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything other than acceptall we should handle?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The manager can set each member's accept nodes. One option is to accept all other members AcceptAll. other options the manager set only some nodes which can accept the designated node.


networkDataFile = "network.json"

AcceptAll = "all"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest we use AllMembers = "allMembers". There might be case when we have public nodes that accept connections from everyone (even not a member).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modified.

return errNameExist
}

var ok1, ok2 bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok1 is defined twice.
Also I would recommend using isMember and isWaiting

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modified.

@billfort billfort force-pushed the network branch 4 times, most recently from ea3932e to 07a0f9c Compare August 11, 2023 20:29
@billfort billfort force-pushed the network branch 6 times, most recently from 8e4b31f to 779a4a9 Compare August 29, 2023 16:57
@billfort billfort force-pushed the network branch 9 times, most recently from b329261 to 579e873 Compare September 7, 2023 02:38
@billfort billfort force-pushed the network branch 2 times, most recently from 61ead41 to bb677e4 Compare September 13, 2023 01:44
@yilunzhang yilunzhang merged commit eecf151 into nknorg:master Nov 24, 2023
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants