mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 11:15:07 +03:00 
			
		
		
		
	Merge pull request #182 from yggdrasil-network/source-interface
Support adding peers on specific interfaces
This commit is contained in:
		
						commit
						7e3426ba93
					
				
					 6 changed files with 91 additions and 35 deletions
				
			
		| 
						 | 
				
			
			@ -112,8 +112,14 @@ func (a *admin) init(c *Core, listenaddr string) {
 | 
			
		|||
		}
 | 
			
		||||
		return admin_info{"sessions": sessions}, nil
 | 
			
		||||
	})
 | 
			
		||||
	a.addHandler("addPeer", []string{"uri"}, func(in admin_info) (admin_info, error) {
 | 
			
		||||
		if a.addPeer(in["uri"].(string)) == nil {
 | 
			
		||||
	a.addHandler("addPeer", []string{"uri", "[interface]"}, func(in admin_info) (admin_info, error) {
 | 
			
		||||
		// Set sane defaults
 | 
			
		||||
		intf := ""
 | 
			
		||||
		// Has interface been specified?
 | 
			
		||||
		if itf, ok := in["interface"]; ok {
 | 
			
		||||
			intf = itf.(string)
 | 
			
		||||
		}
 | 
			
		||||
		if a.addPeer(in["uri"].(string), intf) == nil {
 | 
			
		||||
			return admin_info{
 | 
			
		||||
				"added": []string{
 | 
			
		||||
					in["uri"].(string),
 | 
			
		||||
| 
						 | 
				
			
			@ -390,12 +396,12 @@ func (a *admin) printInfos(infos []admin_nodeInfo) string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// addPeer triggers a connection attempt to a node.
 | 
			
		||||
func (a *admin) addPeer(addr string) error {
 | 
			
		||||
func (a *admin) addPeer(addr string, sintf string) error {
 | 
			
		||||
	u, err := url.Parse(addr)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		switch strings.ToLower(u.Scheme) {
 | 
			
		||||
		case "tcp":
 | 
			
		||||
			a.core.tcp.connect(u.Host)
 | 
			
		||||
			a.core.tcp.connect(u.Host, sintf)
 | 
			
		||||
		case "socks":
 | 
			
		||||
			a.core.tcp.connectSOCKS(u.Host, u.Path[1:])
 | 
			
		||||
		default:
 | 
			
		||||
| 
						 | 
				
			
			@ -407,7 +413,7 @@ func (a *admin) addPeer(addr string) error {
 | 
			
		|||
		if strings.HasPrefix(addr, "tcp:") {
 | 
			
		||||
			addr = addr[4:]
 | 
			
		||||
		}
 | 
			
		||||
		a.core.tcp.connect(addr)
 | 
			
		||||
		a.core.tcp.connect(addr, "")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -504,6 +510,8 @@ func (a *admin) getData_getSwitchPeers() []admin_nodeInfo {
 | 
			
		|||
			{"ip", net.IP(addr[:]).String()},
 | 
			
		||||
			{"coords", fmt.Sprint(coords)},
 | 
			
		||||
			{"port", elem.port},
 | 
			
		||||
			{"bytes_sent", atomic.LoadUint64(&peer.bytesSent)},
 | 
			
		||||
			{"bytes_recvd", atomic.LoadUint64(&peer.bytesRecvd)},
 | 
			
		||||
		}
 | 
			
		||||
		peerInfos = append(peerInfos, info)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,19 +2,20 @@ package config
 | 
			
		|||
 | 
			
		||||
// NodeConfig defines all configuration values needed to run a signle yggdrasil node
 | 
			
		||||
type NodeConfig struct {
 | 
			
		||||
	Listen                      string   `comment:"Listen address for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."`
 | 
			
		||||
	AdminListen                 string   `comment:"Listen address for admin connections Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X."`
 | 
			
		||||
	Peers                       []string `comment:"List of connection strings for static peers in URI format, i.e.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j"`
 | 
			
		||||
	ReadTimeout                 int32    `comment:"Read timeout for connections, specified in milliseconds. If less than 6000 and not negative, 6000 (the default) is used. If negative, reads won't time out."`
 | 
			
		||||
	AllowedEncryptionPublicKeys []string `comment:"List of peer encryption public keys to allow or incoming TCP\nconnections from. If left empty/undefined then all connections\nwill be allowed by default."`
 | 
			
		||||
	EncryptionPublicKey         string   `comment:"Your public encryption key. Your peers may ask you for this to put\ninto their AllowedEncryptionPublicKeys configuration."`
 | 
			
		||||
	EncryptionPrivateKey        string   `comment:"Your private encryption key. DO NOT share this with anyone!"`
 | 
			
		||||
	SigningPublicKey            string   `comment:"Your public signing key. You should not ordinarily need to share\nthis with anyone."`
 | 
			
		||||
	SigningPrivateKey           string   `comment:"Your private signing key. DO NOT share this with anyone!"`
 | 
			
		||||
	MulticastInterfaces         []string `comment:"Regular expressions for which interfaces multicast peer discovery\nshould be enabled on. If none specified, multicast peer discovery is\ndisabled. The default value is .* which uses all interfaces."`
 | 
			
		||||
	IfName                      string   `comment:"Local network interface name for TUN/TAP adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN/TAP."`
 | 
			
		||||
	IfTAPMode                   bool     `comment:"Set local network interface to TAP mode rather than TUN mode if\nsupported by your platform - option will be ignored if not."`
 | 
			
		||||
	IfMTU                       int      `comment:"Maximux Transmission Unit (MTU) size for your local TUN/TAP interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
 | 
			
		||||
	Listen                      string              `comment:"Listen address for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."`
 | 
			
		||||
	AdminListen                 string              `comment:"Listen address for admin connections Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X."`
 | 
			
		||||
	Peers                       []string            `comment:"List of connection strings for static peers in URI format, i.e.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."`
 | 
			
		||||
	InterfacePeers              map[string][]string `comment:"List of connection strings for static peers in URI format, arranged\nby source interface, i.e. { \"eth0\": [ tcp://a.b.c.d:e ] }. Note that\nSOCKS peerings will NOT be affected by this option and should go in\nthe \"Peers\" section instead."`
 | 
			
		||||
	ReadTimeout                 int32               `comment:"Read timeout for connections, specified in milliseconds. If less\nthan 6000 and not negative, 6000 (the default) is used. If negative,\nreads won't time out."`
 | 
			
		||||
	AllowedEncryptionPublicKeys []string            `comment:"List of peer encryption public keys to allow or incoming TCP\nconnections from. If left empty/undefined then all connections\nwill be allowed by default."`
 | 
			
		||||
	EncryptionPublicKey         string              `comment:"Your public encryption key. Your peers may ask you for this to put\ninto their AllowedEncryptionPublicKeys configuration."`
 | 
			
		||||
	EncryptionPrivateKey        string              `comment:"Your private encryption key. DO NOT share this with anyone!"`
 | 
			
		||||
	SigningPublicKey            string              `comment:"Your public signing key. You should not ordinarily need to share\nthis with anyone."`
 | 
			
		||||
	SigningPrivateKey           string              `comment:"Your private signing key. DO NOT share this with anyone!"`
 | 
			
		||||
	MulticastInterfaces         []string            `comment:"Regular expressions for which interfaces multicast peer discovery\nshould be enabled on. If none specified, multicast peer discovery is\ndisabled. The default value is .* which uses all interfaces."`
 | 
			
		||||
	IfName                      string              `comment:"Local network interface name for TUN/TAP adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN/TAP."`
 | 
			
		||||
	IfTAPMode                   bool                `comment:"Set local network interface to TAP mode rather than TUN mode if\nsupported by your platform - option will be ignored if not."`
 | 
			
		||||
	IfMTU                       int                 `comment:"Maximux Transmission Unit (MTU) size for your local TUN/TAP interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
 | 
			
		||||
	//Net                         NetConfig `comment:"Extended options for connecting to peers over other networks."`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -182,8 +182,8 @@ func (c *Core) SetLogger(log *log.Logger) {
 | 
			
		|||
 | 
			
		||||
// Adds a peer. This should be specified in the peer URI format, i.e.
 | 
			
		||||
// tcp://a.b.c.d:e, udp://a.b.c.d:e, socks://a.b.c.d:e/f.g.h.i:j
 | 
			
		||||
func (c *Core) AddPeer(addr string) error {
 | 
			
		||||
	return c.admin.addPeer(addr)
 | 
			
		||||
func (c *Core) AddPeer(addr string, sintf string) error {
 | 
			
		||||
	return c.admin.addPeer(addr, sintf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Adds an expression to select multicast interfaces for peer discovery. This
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -153,6 +153,6 @@ func (m *multicast) listen() {
 | 
			
		|||
		}
 | 
			
		||||
		addr.Zone = from.Zone
 | 
			
		||||
		saddr := addr.String()
 | 
			
		||||
		m.core.tcp.connect(saddr)
 | 
			
		||||
		m.core.tcp.connect(saddr, "")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,13 +64,13 @@ func (iface *tcpInterface) getAddr() *net.TCPAddr {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Attempts to initiate a connection to the provided address.
 | 
			
		||||
func (iface *tcpInterface) connect(addr string) {
 | 
			
		||||
	iface.call(addr, nil)
 | 
			
		||||
func (iface *tcpInterface) connect(addr string, intf string) {
 | 
			
		||||
	iface.call(addr, nil, intf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attempst to initiate a connection to the provided address, viathe provided socks proxy address.
 | 
			
		||||
func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) {
 | 
			
		||||
	iface.call(peeraddr, &socksaddr)
 | 
			
		||||
	iface.call(peeraddr, &socksaddr, "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Initializes the struct.
 | 
			
		||||
| 
						 | 
				
			
			@ -110,20 +110,24 @@ func (iface *tcpInterface) listener() {
 | 
			
		|||
// If the dial is successful, it launches the handler.
 | 
			
		||||
// When finished, it removes the outgoing call, so reconnection attempts can be made later.
 | 
			
		||||
// This all happens in a separate goroutine that it spawns.
 | 
			
		||||
func (iface *tcpInterface) call(saddr string, socksaddr *string) {
 | 
			
		||||
func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) {
 | 
			
		||||
	go func() {
 | 
			
		||||
		callname := saddr
 | 
			
		||||
		if sintf != "" {
 | 
			
		||||
			callname = fmt.Sprintf("%s/%s", saddr, sintf)
 | 
			
		||||
		}
 | 
			
		||||
		quit := false
 | 
			
		||||
		iface.mutex.Lock()
 | 
			
		||||
		if _, isIn := iface.calls[saddr]; isIn {
 | 
			
		||||
		if _, isIn := iface.calls[callname]; isIn {
 | 
			
		||||
			quit = true
 | 
			
		||||
		} else {
 | 
			
		||||
			iface.calls[saddr] = struct{}{}
 | 
			
		||||
			iface.calls[callname] = struct{}{}
 | 
			
		||||
			defer func() {
 | 
			
		||||
				// Block new calls for a little while, to mitigate livelock scenarios
 | 
			
		||||
				time.Sleep(default_tcp_timeout)
 | 
			
		||||
				time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
 | 
			
		||||
				iface.mutex.Lock()
 | 
			
		||||
				delete(iface.calls, saddr)
 | 
			
		||||
				delete(iface.calls, callname)
 | 
			
		||||
				iface.mutex.Unlock()
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -134,6 +138,9 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string) {
 | 
			
		|||
		var conn net.Conn
 | 
			
		||||
		var err error
 | 
			
		||||
		if socksaddr != nil {
 | 
			
		||||
			if sintf != "" {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			var dialer proxy.Dialer
 | 
			
		||||
			dialer, err = proxy.SOCKS5("tcp", *socksaddr, nil, proxy.Direct)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -151,7 +158,39 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string) {
 | 
			
		|||
				},
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			conn, err = net.Dial("tcp", saddr)
 | 
			
		||||
			dialer := net.Dialer{}
 | 
			
		||||
			if sintf != "" {
 | 
			
		||||
				ief, err := net.InterfaceByName(sintf)
 | 
			
		||||
				if err == nil {
 | 
			
		||||
					if ief.Flags & net.FlagUp == 0 {
 | 
			
		||||
				    return
 | 
			
		||||
					}
 | 
			
		||||
					addrs, err := ief.Addrs()
 | 
			
		||||
					if err == nil {
 | 
			
		||||
						dst, err := net.ResolveTCPAddr("tcp", saddr)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							return
 | 
			
		||||
						}
 | 
			
		||||
						for _, addr := range addrs {
 | 
			
		||||
							src, _, err := net.ParseCIDR(addr.String())
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								continue
 | 
			
		||||
							}
 | 
			
		||||
							if (src.To4() != nil) == (dst.IP.To4() != nil) && src.IsGlobalUnicast() {
 | 
			
		||||
								dialer.LocalAddr = &net.TCPAddr{
 | 
			
		||||
									IP:   src,
 | 
			
		||||
									Port: 0,
 | 
			
		||||
								}
 | 
			
		||||
								break
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						if dialer.LocalAddr == nil {
 | 
			
		||||
							return
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			conn, err = dialer.Dial("tcp", saddr)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -307,17 +346,18 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
 | 
			
		|||
		// Put all of our cleanup here...
 | 
			
		||||
		p.core.peers.removePeer(p.port)
 | 
			
		||||
	}()
 | 
			
		||||
	us, _, _ := net.SplitHostPort(sock.LocalAddr().String())
 | 
			
		||||
	them, _, _ := net.SplitHostPort(sock.RemoteAddr().String())
 | 
			
		||||
	themNodeID := getNodeID(&info.box)
 | 
			
		||||
	themAddr := address_addrForNodeID(themNodeID)
 | 
			
		||||
	themAddrString := net.IP(themAddr[:]).String()
 | 
			
		||||
	themString := fmt.Sprintf("%s@%s", themAddrString, them)
 | 
			
		||||
	iface.core.log.Println("Connected:", themString)
 | 
			
		||||
	iface.core.log.Println("Connected:", themString, "source", us)
 | 
			
		||||
	err = iface.reader(sock, in) // In this goroutine, because of defers
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		iface.core.log.Println("Disconnected:", themString)
 | 
			
		||||
		iface.core.log.Println("Disconnected:", themString, "source", us)
 | 
			
		||||
	} else {
 | 
			
		||||
		iface.core.log.Println("Disconnected:", themString, "with error:", err)
 | 
			
		||||
		iface.core.log.Println("Disconnected:", themString, "source", us, "with error:", err)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue