WIP
This commit is contained in:
		
							
								
								
									
										57
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								README.md
									
									
									
									
									
								
							| @@ -4,8 +4,9 @@ | |||||||
|  |  | ||||||
| * Peer: router: create process for managing the routing table | * Peer: router: create process for managing the routing table | ||||||
| * Peer: router: track mediators, enable / disable ... | * Peer: router: track mediators, enable / disable ... | ||||||
| * Hub: track peer last-seen timestamp | * Hub: track peer last-seen timestamp (?) | ||||||
| * Peer: local peer discovery - part of RoutingProcessor | * Peer: local peer discovery - part of RoutingProcessor | ||||||
|  | * Peer: update hub w/ latest port on startup | ||||||
|  |  | ||||||
| ## Principles | ## Principles | ||||||
|  |  | ||||||
| @@ -13,24 +14,21 @@ | |||||||
| * Simple setup: via setup link from the hub. | * Simple setup: via setup link from the hub. | ||||||
| * Each peer has full network state replicated from the hub. | * Each peer has full network state replicated from the hub. | ||||||
|  |  | ||||||
| ## Design | ## Routing | ||||||
|  |  | ||||||
| * Append nonce to end of packet | * Routing is different for public vs non-public peers | ||||||
|   * Then it's readable whether signed or unsiged |   * Public: routes are initialized via incoming ping requests | ||||||
| * Types of packets to send: |   * NonPub: routes are initialized via incoming ping responses | ||||||
|   * standard: encrypt and send |  | ||||||
|   * Forward via: encrypt, sign and send |  | ||||||
|   * Forward to: send |  | ||||||
| * Type of packeting read from interface: |  | ||||||
|   * Forward to: check signature |  | ||||||
|   * Forwarded, standard |  | ||||||
|  |  | ||||||
| Incoming from net: | A non-public peer needs to maintain connections with every public peer. | ||||||
|   * Data for iface |  | ||||||
|   * Packet for forward | * Sending: | ||||||
|   * Packet for routingHandler |   * Public: send to address | ||||||
| * Incoming from iface: |   * Non-public: send to a mediator | ||||||
|   * Data for peer |  | ||||||
|  | * Pings: | ||||||
|  |   * Servers don't need to ping | ||||||
|  |   * Clients need to ping all public and local peers to keep connections open | ||||||
|  |  | ||||||
| ## Hub Server Configuration | ## Hub Server Configuration | ||||||
|  |  | ||||||
| @@ -84,3 +82,28 @@ journalctl -f -u hub -n 100 | |||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Sign-in and configure. | Sign-in and configure. | ||||||
|  |  | ||||||
|  | ## Peer Configuration | ||||||
|  |  | ||||||
|  | Install the binary somewhere, for example `~/bin/vppn`. | ||||||
|  |  | ||||||
|  | Create systemd file in `/etc/systemd/system/vppn.service`. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | Description=vppn | ||||||
|  | Requires=network.target | ||||||
|  |  | ||||||
|  | [Service] | ||||||
|  | AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN | ||||||
|  | Type=simple | ||||||
|  | User=user | ||||||
|  | WorkingDirectory=/home/user/ | ||||||
|  | ExecStart=/home/user/vppn -name vppn | ||||||
|  | Restart=always | ||||||
|  | RestartSec=8 | ||||||
|  | TimeoutStopSec=24 | ||||||
|  |  | ||||||
|  | [Install] | ||||||
|  | WantedBy=default.target | ||||||
|  | ``` | ||||||
|   | |||||||
| @@ -2,3 +2,4 @@ | |||||||
|  |  | ||||||
| go build | go build | ||||||
| sudo setcap cap_net_admin+iep vppn | sudo setcap cap_net_admin+iep vppn | ||||||
|  | sudo setcap cap_net_bind_service+iep vppn | ||||||
|   | |||||||
| @@ -144,7 +144,7 @@ func (a *API) Session_SignIn(s *Session, pwd string) error { | |||||||
|  |  | ||||||
| type PeerCreateArgs struct { | type PeerCreateArgs struct { | ||||||
| 	Name     string | 	Name     string | ||||||
| 	IP       []byte | 	PublicIP []byte | ||||||
| 	Port     uint16 | 	Port     uint16 | ||||||
| 	Mediator bool | 	Mediator bool | ||||||
| } | } | ||||||
| @@ -209,9 +209,10 @@ func (a *API) Peer_Create(creationCode string) (*m.PeerConfig, error) { | |||||||
|  |  | ||||||
| 	peer := &Peer{ | 	peer := &Peer{ | ||||||
| 		PeerIP:     peerIP, | 		PeerIP:     peerIP, | ||||||
|  | 		Version:    idgen.NextID(0), | ||||||
| 		APIKey:     idgen.NewToken(), | 		APIKey:     idgen.NewToken(), | ||||||
| 		Name:       args.Name, | 		Name:       args.Name, | ||||||
| 		IP:         args.IP, | 		PublicIP:   args.PublicIP, | ||||||
| 		Port:       args.Port, | 		Port:       args.Port, | ||||||
| 		Mediator:   args.Mediator, | 		Mediator:   args.Mediator, | ||||||
| 		EncPubKey:  encPubKey[:], | 		EncPubKey:  encPubKey[:], | ||||||
| @@ -229,7 +230,7 @@ func (a *API) Peer_Create(creationCode string) (*m.PeerConfig, error) { | |||||||
| 		HubAddress:  conf.HubAddress, | 		HubAddress:  conf.HubAddress, | ||||||
| 		APIKey:      peer.APIKey, | 		APIKey:      peer.APIKey, | ||||||
| 		Network:     conf.VPNNetwork, | 		Network:     conf.VPNNetwork, | ||||||
| 		IP:          peer.IP, | 		PublicIP:    peer.PublicIP, | ||||||
| 		Port:        peer.Port, | 		Port:        peer.Port, | ||||||
| 		Mediator:    peer.Mediator, | 		Mediator:    peer.Mediator, | ||||||
| 		EncPubKey:   encPubKey[:], | 		EncPubKey:   encPubKey[:], | ||||||
| @@ -240,6 +241,10 @@ func (a *API) Peer_Create(creationCode string) (*m.PeerConfig, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (a *API) Peer_Update(p *Peer) error { | func (a *API) Peer_Update(p *Peer) error { | ||||||
|  | 	a.lock.Lock() | ||||||
|  | 	defer a.lock.Unlock() | ||||||
|  |  | ||||||
|  | 	p.Version = idgen.NextID(0) | ||||||
| 	return db.Peer_Update(a.db, p) | 	return db.Peer_Update(a.db, p) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -308,16 +308,17 @@ func Session_List( | |||||||
|  |  | ||||||
| type Peer struct { | type Peer struct { | ||||||
| 	PeerIP     byte | 	PeerIP     byte | ||||||
|  | 	Version    int64 | ||||||
| 	APIKey     string | 	APIKey     string | ||||||
| 	Name       string | 	Name       string | ||||||
| 	IP         []byte | 	PublicIP   []byte | ||||||
| 	Port       uint16 | 	Port       uint16 | ||||||
| 	Mediator   bool | 	Mediator   bool | ||||||
| 	EncPubKey  []byte | 	EncPubKey  []byte | ||||||
| 	SignPubKey []byte | 	SignPubKey []byte | ||||||
| } | } | ||||||
|  |  | ||||||
| const Peer_SelectQuery = "SELECT PeerIP,APIKey,Name,IP,Port,Mediator,EncPubKey,SignPubKey FROM peers" | const Peer_SelectQuery = "SELECT PeerIP,Version,APIKey,Name,PublicIP,Port,Mediator,EncPubKey,SignPubKey FROM peers" | ||||||
|  |  | ||||||
| func Peer_Insert( | func Peer_Insert( | ||||||
| 	tx TX, | 	tx TX, | ||||||
| @@ -328,7 +329,7 @@ func Peer_Insert( | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	_, err = tx.Exec("INSERT INTO peers(PeerIP,APIKey,Name,IP,Port,Mediator,EncPubKey,SignPubKey) VALUES(?,?,?,?,?,?,?,?)", row.PeerIP, row.APIKey, row.Name, row.IP, row.Port, row.Mediator, row.EncPubKey, row.SignPubKey) | 	_, err = tx.Exec("INSERT INTO peers(PeerIP,Version,APIKey,Name,PublicIP,Port,Mediator,EncPubKey,SignPubKey) VALUES(?,?,?,?,?,?,?,?,?)", row.PeerIP, row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Mediator, row.EncPubKey, row.SignPubKey) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -341,7 +342,7 @@ func Peer_Update( | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	result, err := tx.Exec("UPDATE peers SET Name=?,IP=?,Port=?,Mediator=? WHERE PeerIP=?", row.Name, row.IP, row.Port, row.Mediator, row.PeerIP) | 	result, err := tx.Exec("UPDATE peers SET Version=?,Name=?,PublicIP=?,Port=?,Mediator=? WHERE PeerIP=?", row.Version, row.Name, row.PublicIP, row.Port, row.Mediator, row.PeerIP) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -369,7 +370,7 @@ func Peer_UpdateFull( | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	result, err := tx.Exec("UPDATE peers SET APIKey=?,Name=?,IP=?,Port=?,Mediator=?,EncPubKey=?,SignPubKey=? WHERE PeerIP=?", row.APIKey, row.Name, row.IP, row.Port, row.Mediator, row.EncPubKey, row.SignPubKey, row.PeerIP) | 	result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,PublicIP=?,Port=?,Mediator=?,EncPubKey=?,SignPubKey=? WHERE PeerIP=?", row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Mediator, row.EncPubKey, row.SignPubKey, row.PeerIP) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -419,8 +420,8 @@ func Peer_Get( | |||||||
| 	err error, | 	err error, | ||||||
| ) { | ) { | ||||||
| 	row = &Peer{} | 	row = &Peer{} | ||||||
| 	r := tx.QueryRow("SELECT PeerIP,APIKey,Name,IP,Port,Mediator,EncPubKey,SignPubKey FROM peers WHERE PeerIP=?", PeerIP) | 	r := tx.QueryRow("SELECT PeerIP,Version,APIKey,Name,PublicIP,Port,Mediator,EncPubKey,SignPubKey FROM peers WHERE PeerIP=?", PeerIP) | ||||||
| 	err = r.Scan(&row.PeerIP, &row.APIKey, &row.Name, &row.IP, &row.Port, &row.Mediator, &row.EncPubKey, &row.SignPubKey) | 	err = r.Scan(&row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Mediator, &row.EncPubKey, &row.SignPubKey) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -434,7 +435,7 @@ func Peer_GetWhere( | |||||||
| ) { | ) { | ||||||
| 	row = &Peer{} | 	row = &Peer{} | ||||||
| 	r := tx.QueryRow(query, args...) | 	r := tx.QueryRow(query, args...) | ||||||
| 	err = r.Scan(&row.PeerIP, &row.APIKey, &row.Name, &row.IP, &row.Port, &row.Mediator, &row.EncPubKey, &row.SignPubKey) | 	err = r.Scan(&row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Mediator, &row.EncPubKey, &row.SignPubKey) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -454,7 +455,7 @@ func Peer_Iterate( | |||||||
| 		defer rows.Close() | 		defer rows.Close() | ||||||
| 		for rows.Next() { | 		for rows.Next() { | ||||||
| 			row := &Peer{} | 			row := &Peer{} | ||||||
| 			err := rows.Scan(&row.PeerIP, &row.APIKey, &row.Name, &row.IP, &row.Port, &row.Mediator, &row.EncPubKey, &row.SignPubKey) | 			err := rows.Scan(&row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Mediator, &row.EncPubKey, &row.SignPubKey) | ||||||
| 			if !yield(row, err) { | 			if !yield(row, err) { | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|   | |||||||
| @@ -44,10 +44,10 @@ func Session_Validate(s *Session) error { | |||||||
|  |  | ||||||
| func Peer_Sanitize(p *Peer) { | func Peer_Sanitize(p *Peer) { | ||||||
| 	p.Name = strings.TrimSpace(p.Name) | 	p.Name = strings.TrimSpace(p.Name) | ||||||
| 	if len(p.IP) != 0 { | 	if len(p.PublicIP) != 0 { | ||||||
| 		addr, ok := netip.AddrFromSlice(p.IP) | 		addr, ok := netip.AddrFromSlice(p.PublicIP) | ||||||
| 		if ok && addr.Is4() { | 		if ok && addr.Is4() { | ||||||
| 			p.IP = addr.AsSlice() | 			p.PublicIP = addr.AsSlice() | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if p.Port == 0 { | 	if p.Port == 0 { | ||||||
| @@ -56,8 +56,8 @@ func Peer_Sanitize(p *Peer) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func Peer_Validate(p *Peer) error { | func Peer_Validate(p *Peer) error { | ||||||
| 	if len(p.IP) > 0 { | 	if len(p.PublicIP) > 0 { | ||||||
| 		_, ok := netip.AddrFromSlice(p.IP) | 		_, ok := netip.AddrFromSlice(p.PublicIP) | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			return ErrInvalidIP | 			return ErrInvalidIP | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -15,9 +15,10 @@ TABLE sessions OF Session NoUpdate ( | |||||||
|  |  | ||||||
| TABLE peers OF Peer ( | TABLE peers OF Peer ( | ||||||
|   PeerIP     byte PK, |   PeerIP     byte PK, | ||||||
|  |   Version    int64, | ||||||
|   APIKey     string NoUpdate, |   APIKey     string NoUpdate, | ||||||
|   Name       string, |   Name       string, | ||||||
|   IP         []byte, |   PublicIP   []byte, | ||||||
|   Port       uint16, |   Port       uint16, | ||||||
|   Mediator   bool, |   Mediator   bool, | ||||||
|   EncPubKey  []byte NoUpdate, |   EncPubKey  []byte NoUpdate, | ||||||
|   | |||||||
| @@ -17,9 +17,10 @@ CREATE INDEX sessions_last_seen_index ON sessions(LastSeenAt); | |||||||
|  |  | ||||||
| CREATE TABLE peers ( | CREATE TABLE peers ( | ||||||
|   PeerIP     INTEGER NOT NULL PRIMARY KEY, -- Final byte. |   PeerIP     INTEGER NOT NULL PRIMARY KEY, -- Final byte. | ||||||
|  |   Version    INTEGER NOT NULL, | ||||||
|   APIKey     TEXT    NOT NULL UNIQUE, |   APIKey     TEXT    NOT NULL UNIQUE, | ||||||
|   Name       TEXT    NOT NULL UNIQUE,      -- For humans. |   Name       TEXT    NOT NULL UNIQUE,      -- For humans. | ||||||
|   IP         BLOB    NOT NULL, |   PublicIP   BLOB    NOT NULL, | ||||||
|   Port       INTEGER NOT NULL, |   Port       INTEGER NOT NULL, | ||||||
|   Mediator   INTEGER NOT NULL DEFAULT 0,   -- Boolean if peer will forward packets. Must also have public address. |   Mediator   INTEGER NOT NULL DEFAULT 0,   -- Boolean if peer will forward packets. Must also have public address. | ||||||
|   EncPubKey  BLOB    NOT NULL, |   EncPubKey  BLOB    NOT NULL, | ||||||
|   | |||||||
| @@ -64,3 +64,18 @@ func (app *App) handleSignedIn(pattern string, fn handlerFunc) { | |||||||
| 		return fn(s, w, r) | 		return fn(s, w, r) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type peerHandlerFunc func(w http.ResponseWriter, r *http.Request) error | ||||||
|  |  | ||||||
|  | func (app *App) handlePeer(pattern string, fn peerHandlerFunc) { | ||||||
|  | 	wrapped := func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		r.ParseForm() | ||||||
|  | 		if err := fn(w, r); err != nil { | ||||||
|  | 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	app.mux.HandleFunc(pattern, | ||||||
|  | 		webutil.WithLogging( | ||||||
|  | 			wrapped)) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -165,7 +165,7 @@ func (a *App) _adminPeerCreateSubmit(s *api.Session, w http.ResponseWriter, r *h | |||||||
| 	args := api.PeerCreateArgs{} | 	args := api.PeerCreateArgs{} | ||||||
| 	err := webutil.NewFormScanner(r.Form). | 	err := webutil.NewFormScanner(r.Form). | ||||||
| 		Scan("Name", &args.Name). | 		Scan("Name", &args.Name). | ||||||
| 		Scan("IP", &ipStr). | 		Scan("PublicIP", &ipStr). | ||||||
| 		Scan("Port", &args.Port). | 		Scan("Port", &args.Port). | ||||||
| 		Scan("Mediator", &args.Mediator). | 		Scan("Mediator", &args.Mediator). | ||||||
| 		Error() | 		Error() | ||||||
| @@ -173,7 +173,7 @@ func (a *App) _adminPeerCreateSubmit(s *api.Session, w http.ResponseWriter, r *h | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if args.IP, err = stringToIP(ipStr); err != nil { | 	if args.PublicIP, err = stringToIP(ipStr); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -247,7 +247,7 @@ func (a *App) _adminPeerEditSubmit(s *api.Session, w http.ResponseWriter, r *htt | |||||||
| 	} | 	} | ||||||
| 	err = webutil.NewFormScanner(r.Form). | 	err = webutil.NewFormScanner(r.Form). | ||||||
| 		Scan("Name", &peer.Name). | 		Scan("Name", &peer.Name). | ||||||
| 		Scan("IP", &ipStr). | 		Scan("PublicIP", &ipStr). | ||||||
| 		Scan("Port", &peer.Port). | 		Scan("Port", &peer.Port). | ||||||
| 		Scan("Mediator", &peer.Mediator). | 		Scan("Mediator", &peer.Mediator). | ||||||
| 		Error() | 		Error() | ||||||
| @@ -255,7 +255,7 @@ func (a *App) _adminPeerEditSubmit(s *api.Session, w http.ResponseWriter, r *htt | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if peer.IP, err = stringToIP(ipStr); err != nil { | 	if peer.PublicIP, err = stringToIP(ipStr); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -298,7 +298,7 @@ func (a *App) _adminPeerDeleteSubmit(s *api.Session, w http.ResponseWriter, r *h | |||||||
| 	return a.redirect(w, r, "/admin/peer/list/") | 	return a.redirect(w, r, "/admin/peer/list/") | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *App) _peerCreate(s *api.Session, w http.ResponseWriter, r *http.Request) error { | func (a *App) _peerCreate(w http.ResponseWriter, r *http.Request) error { | ||||||
| 	code := r.FormValue("Code") | 	code := r.FormValue("Code") | ||||||
| 	conf, err := a.api.Peer_Create(code) | 	conf, err := a.api.Peer_Create(code) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -308,7 +308,7 @@ func (a *App) _peerCreate(s *api.Session, w http.ResponseWriter, r *http.Request | |||||||
| 	return a.sendJSON(w, conf) | 	return a.sendJSON(w, conf) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *App) _peerFetchState(s *api.Session, w http.ResponseWriter, r *http.Request) error { | func (a *App) _peerFetchState(w http.ResponseWriter, r *http.Request) error { | ||||||
| 	_, apiKey, ok := r.BasicAuth() | 	_, apiKey, ok := r.BasicAuth() | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		log.Printf("1") | 		log.Printf("1") | ||||||
| @@ -333,15 +333,16 @@ func (a *App) _peerFetchState(s *api.Session, w http.ResponseWriter, r *http.Req | |||||||
| 		HubAddress: conf.HubAddress, | 		HubAddress: conf.HubAddress, | ||||||
| 		Network:    conf.VPNNetwork, | 		Network:    conf.VPNNetwork, | ||||||
| 		PeerIP:     peer.PeerIP, | 		PeerIP:     peer.PeerIP, | ||||||
| 		IP:         peer.IP, | 		PublicIP:   peer.PublicIP, | ||||||
| 		Port:       peer.Port, | 		Port:       peer.Port, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, p := range peers { | 	for _, p := range peers { | ||||||
| 		state.Peers[p.PeerIP] = &m.Peer{ | 		state.Peers[p.PeerIP] = &m.Peer{ | ||||||
| 			PeerIP:     p.PeerIP, | 			PeerIP:     p.PeerIP, | ||||||
|  | 			Version:    p.Version, | ||||||
| 			Name:       p.Name, | 			Name:       p.Name, | ||||||
| 			IP:         p.IP, | 			PublicIP:   p.PublicIP, | ||||||
| 			Port:       p.Port, | 			Port:       p.Port, | ||||||
| 			Mediator:   p.Mediator, | 			Mediator:   p.Mediator, | ||||||
| 			EncPubKey:  p.EncPubKey, | 			EncPubKey:  p.EncPubKey, | ||||||
|   | |||||||
| @@ -26,6 +26,6 @@ func (a *App) registerRoutes() { | |||||||
| 	a.handleSignedIn("GET  /admin/peer/delete/", a._adminPeerDelete) | 	a.handleSignedIn("GET  /admin/peer/delete/", a._adminPeerDelete) | ||||||
| 	a.handleSignedIn("POST /admin/peer/delete/", a._adminPeerDeleteSubmit) | 	a.handleSignedIn("POST /admin/peer/delete/", a._adminPeerDeleteSubmit) | ||||||
|  |  | ||||||
| 	a.handleNotSignedIn("GET /peer/create/", a._peerCreate) | 	a.handlePeer("GET /peer/create/", a._peerCreate) | ||||||
| 	a.handleNotSignedIn("GET /peer/fetch-state/", a._peerFetchState) | 	a.handlePeer("GET /peer/fetch-state/", a._peerFetchState) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,8 +8,8 @@ | |||||||
|     <input type="text" name="Name"> |     <input type="text" name="Name"> | ||||||
|   </p> |   </p> | ||||||
|   <p> |   <p> | ||||||
|     <label>IP</label><br> |     <label>Public IP</label><br> | ||||||
|     <input type="text" name="IP"> |     <input type="text" name="PublicIP"> | ||||||
|   </p> |   </p> | ||||||
|   <p> |   <p> | ||||||
|     <label>Port</label><br> |     <label>Port</label><br> | ||||||
|   | |||||||
| @@ -13,8 +13,8 @@ | |||||||
|     <input type="text" value="{{.Name}}" disabled> |     <input type="text" value="{{.Name}}" disabled> | ||||||
|   </p> |   </p> | ||||||
|   <p> |   <p> | ||||||
|     <label>IP</label><br> |     <label>Public IP</label><br> | ||||||
|     <input type="text" value="{{ipToString .IP}}" disabled> |     <input type="text" value="{{ipToString .PublicIP}}" disabled> | ||||||
|   </p> |   </p> | ||||||
|   <p> |   <p> | ||||||
|     <label>Port</label><br> |     <label>Port</label><br> | ||||||
|   | |||||||
| @@ -13,8 +13,8 @@ | |||||||
|     <input type="text" name="Name" value="{{.Name}}"> |     <input type="text" name="Name" value="{{.Name}}"> | ||||||
|   </p> |   </p> | ||||||
|   <p> |   <p> | ||||||
|     <label>IP</label><br> |     <label>Public IP</label><br> | ||||||
|     <input type="text" name="IP" value="{{ipToString .IP}}"> |     <input type="text" name="PublicIP" value="{{ipToString .PublicIP}}"> | ||||||
|   </p> |   </p> | ||||||
|   <p> |   <p> | ||||||
|     <label>Port</label><br> |     <label>Port</label><br> | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|     <tr> |     <tr> | ||||||
|       <th>PeerIP</th> |       <th>PeerIP</th> | ||||||
|       <th>Name</th> |       <th>Name</th> | ||||||
|       <th>IP</th> |       <th>Public IP</th> | ||||||
|       <th>Port</th> |       <th>Port</th> | ||||||
|       <th>Mediator</th> |       <th>Mediator</th> | ||||||
|     </tr> |     </tr> | ||||||
| @@ -25,7 +25,7 @@ | |||||||
|       </a> |       </a> | ||||||
|     </td> |     </td> | ||||||
|     <td>{{.Name}}</td> |     <td>{{.Name}}</td> | ||||||
|     <td>{{ipToString .IP}}</td> |     <td>{{ipToString .PublicIP}}</td> | ||||||
|     <td>{{.Port}}</td> |     <td>{{.Port}}</td> | ||||||
|     <td>{{if .Mediator}}T{{else}}F{{end}}</td> |     <td>{{if .Mediator}}T{{else}}F{{end}}</td> | ||||||
|   </tr> |   </tr> | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ | |||||||
| <table class="def-list"> | <table class="def-list"> | ||||||
|   <tr><td>Peer IP</td><td>{{.PeerIP}}</td></tr> |   <tr><td>Peer IP</td><td>{{.PeerIP}}</td></tr> | ||||||
|   <tr><td>Name</td><td>{{.Name}}</td></tr> |   <tr><td>Name</td><td>{{.Name}}</td></tr> | ||||||
|   <tr><td>IP</td><td>{{ipToString .IP}}</td></tr> |   <tr><td>Public IP</td><td>{{ipToString .PublicIP}}</td></tr> | ||||||
|   <tr><td>Port</td><td>{{.Port}}</td></tr> |   <tr><td>Port</td><td>{{.Port}}</td></tr> | ||||||
|   <tr><td>Mediator</td><td>{{if .Mediator}}T{{else}}F{{end}}</td></tr> |   <tr><td>Mediator</td><td>{{if .Mediator}}T{{else}}F{{end}}</td></tr> | ||||||
|   <tr><td>API Key</td><td>{{.APIKey}}</td></tr> |   <tr><td>API Key</td><td>{{.APIKey}}</td></tr> | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ type PeerConfig struct { | |||||||
| 	HubAddress  string | 	HubAddress  string | ||||||
| 	Network     []byte | 	Network     []byte | ||||||
| 	APIKey      string | 	APIKey      string | ||||||
| 	IP          []byte | 	PublicIP    []byte | ||||||
| 	Port        uint16 | 	Port        uint16 | ||||||
| 	Mediator    bool | 	Mediator    bool | ||||||
| 	EncPubKey   []byte | 	EncPubKey   []byte | ||||||
| @@ -17,8 +17,9 @@ type PeerConfig struct { | |||||||
|  |  | ||||||
| type Peer struct { | type Peer struct { | ||||||
| 	PeerIP     byte | 	PeerIP     byte | ||||||
|  | 	Version    int64 | ||||||
| 	Name       string | 	Name       string | ||||||
| 	IP         []byte | 	PublicIP   []byte | ||||||
| 	Port       uint16 | 	Port       uint16 | ||||||
| 	Mediator   bool | 	Mediator   bool | ||||||
| 	EncPubKey  []byte | 	EncPubKey  []byte | ||||||
| @@ -31,7 +32,7 @@ type NetworkState struct { | |||||||
| 	// The requester's data: | 	// The requester's data: | ||||||
| 	Network  []byte | 	Network  []byte | ||||||
| 	PeerIP   byte | 	PeerIP   byte | ||||||
| 	IP      []byte | 	PublicIP []byte | ||||||
| 	Port     uint16 | 	Port     uint16 | ||||||
|  |  | ||||||
| 	// All peer data. | 	// All peer data. | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								peer/conn-states.dot
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								peer/conn-states.dot
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | digraph d { | ||||||
|  |   init -> null; | ||||||
|  |   init -> unconnectedServer; | ||||||
|  |   init -> unconnectedClient; | ||||||
|  |   init -> unconnectedMediated; | ||||||
|  |   unconnectedServer -> connectedServer; | ||||||
|  |   unconnectedClient -> connectedClient; | ||||||
|  |   unconnectedMediated -> connectedMediated; | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								peer/conndata.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								peer/conndata.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | package peer | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/netip" | ||||||
|  | 	"sync/atomic" | ||||||
|  | 	"time" | ||||||
|  | 	"vppn/m" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	pingInterval    = time.Second * 8 | ||||||
|  | 	timeoutInterval = 32 * time.Second | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type connData struct { | ||||||
|  | 	// Shared data. | ||||||
|  | 	routes [256]*atomic.Pointer[route] | ||||||
|  | 	route  *atomic.Pointer[route] | ||||||
|  |  | ||||||
|  | 	// Local data. | ||||||
|  | 	mediatorIP byte | ||||||
|  |  | ||||||
|  | 	// Peer data. | ||||||
|  | 	server     bool   // Never changes. | ||||||
|  | 	peerIP     byte   // Never changes. | ||||||
|  | 	encPrivKey []byte // Never changes. | ||||||
|  |  | ||||||
|  | 	peer         *m.Peer        // From hub. | ||||||
|  | 	encSharedKey []byte         // From hub + private key. | ||||||
|  | 	publicAddr   netip.AddrPort // From hub. | ||||||
|  |  | ||||||
|  | 	pingTimer    *time.Timer | ||||||
|  | 	timeoutTimer *time.Timer | ||||||
|  |  | ||||||
|  | 	// Routing data. | ||||||
|  | 	addr  netip.AddrPort | ||||||
|  | 	viaIP byte | ||||||
|  | 	up    bool | ||||||
|  |  | ||||||
|  | 	// For sending. | ||||||
|  | 	buf    []byte | ||||||
|  | 	sender *safeConnSender | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *connData) Route() *route { | ||||||
|  | 	return &route{ | ||||||
|  | 		PeerIP:       d.peerIP, | ||||||
|  | 		Up:           d.up, | ||||||
|  | 		Mediator:     d.peer.Mediator, | ||||||
|  | 		SignPubKey:   d.peer.SignPubKey, | ||||||
|  | 		EncSharedKey: d.encSharedKey, | ||||||
|  | 		Addr:         d.addr, | ||||||
|  | 		ViaIP:        d.viaIP, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *connData) HandlePeerUpdate(state connState, update peerUpdate) connState { | ||||||
|  | 	if d.peer != nil && update.Peer != nil && d.peer.Version == update.Peer.Version { | ||||||
|  | 		return state | ||||||
|  | 	} | ||||||
|  | 	if d.peer == nil && update.Peer == nil { | ||||||
|  | 		return state | ||||||
|  | 	} | ||||||
|  | 	return newConnStateFromPeer(update, d) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *connData) HandleSendPing() { | ||||||
|  | 	route := d.route.Load() | ||||||
|  | 	req := Ping{SentAt: time.Now().UnixMilli()} | ||||||
|  | 	req.Marshal(d.buf[:PING_SIZE]) | ||||||
|  | 	d.sender.send(PACKET_TYPE_PING, d.buf[:PING_SIZE], route) | ||||||
|  | 	d.pingTimer.Reset(pingInterval) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *connData) sendPong(w wrapper[Ping]) { | ||||||
|  | 	route := d.route.Load() | ||||||
|  | 	pong := Pong{ | ||||||
|  | 		SentAt:  w.T.SentAt, | ||||||
|  | 		RecvdAt: time.Now().UnixMilli(), | ||||||
|  | 	} | ||||||
|  | 	pong.Marshal(d.buf[:PONG_SIZE]) | ||||||
|  | 	d.sender.send(PACKET_TYPE_PONG, d.buf[:PONG_SIZE], route) | ||||||
|  | } | ||||||
							
								
								
									
										121
									
								
								peer/connhandler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								peer/connhandler.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | |||||||
|  | package peer | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"log" | ||||||
|  | 	"runtime/debug" | ||||||
|  | 	"sync/atomic" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type connHandler struct { | ||||||
|  | 	// Communication. | ||||||
|  | 	mediatorUpdates chan byte | ||||||
|  | 	peerUpdates     chan peerUpdate | ||||||
|  | 	pings           chan wrapper[Ping] | ||||||
|  | 	pongs           chan wrapper[Pong] | ||||||
|  |  | ||||||
|  | 	data *connData | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newConnHandler( | ||||||
|  | 	server bool, | ||||||
|  | 	peerIP byte, | ||||||
|  | 	routes [256]*atomic.Pointer[route], | ||||||
|  | 	encPrivKey []byte, | ||||||
|  | 	sender *safeConnSender, | ||||||
|  | ) *connHandler { | ||||||
|  | 	d := &connData{ | ||||||
|  | 		server:       server, | ||||||
|  | 		pingTimer:    time.NewTimer(pingInterval), | ||||||
|  | 		timeoutTimer: time.NewTimer(timeoutInterval), | ||||||
|  | 		routes:       routes, | ||||||
|  | 		route:        routes[peerIP], | ||||||
|  | 		peerIP:       peerIP, | ||||||
|  | 		encPrivKey:   encPrivKey, | ||||||
|  | 		buf:          make([]byte, BUFFER_SIZE), | ||||||
|  | 		sender:       sender, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	h := &connHandler{ | ||||||
|  | 		mediatorUpdates: make(chan byte, 1), | ||||||
|  | 		peerUpdates:     make(chan peerUpdate, 1), | ||||||
|  | 		pings:           make(chan wrapper[Ping], 1), | ||||||
|  | 		pongs:           make(chan wrapper[Pong], 1), | ||||||
|  | 		data:            d, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	go h.mainLoop() | ||||||
|  | 	return h | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (h *connHandler) mainLoop() { | ||||||
|  | 	defer func() { | ||||||
|  | 		if r := recover(); r != nil { | ||||||
|  | 			fmt.Println("stacktrace from panic: \n" + string(debug.Stack())) | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	var ( | ||||||
|  | 		data  = h.data | ||||||
|  | 		state = newConnNull(data) | ||||||
|  | 		name  = state.Name() | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  |  | ||||||
|  | 		case ip := <-h.mediatorUpdates: | ||||||
|  | 			state = state.HandleMediatorUpdate(ip) | ||||||
|  |  | ||||||
|  | 		case update := <-h.peerUpdates: | ||||||
|  | 			state = data.HandlePeerUpdate(state, update) | ||||||
|  |  | ||||||
|  | 		case w := <-h.pings: | ||||||
|  | 			state = state.HandlePing(w) | ||||||
|  |  | ||||||
|  | 		case w := <-h.pongs: | ||||||
|  | 			state = state.HandlePong(w) | ||||||
|  |  | ||||||
|  | 		case <-data.pingTimer.C: | ||||||
|  | 			state.HandleSendPing() | ||||||
|  |  | ||||||
|  | 		case <-data.timeoutTimer.C: | ||||||
|  | 			log.Printf("[%s] Connection timeout.", state.Name()) | ||||||
|  | 			state = state.HandleTimeout() | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if state.Name() != name { | ||||||
|  | 			log.Printf("[%03d] STATE: %s --> %s", data.peerIP, name, state.Name()) | ||||||
|  | 			name = state.Name() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *connHandler) UpdateMediator(ip byte) { | ||||||
|  | 	select { | ||||||
|  | 	case c.mediatorUpdates <- ip: | ||||||
|  | 	default: | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *connHandler) HandlePing(w wrapper[Ping]) { | ||||||
|  | 	select { | ||||||
|  | 	case c.pings <- w: | ||||||
|  | 	default: | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *connHandler) HandlePong(w wrapper[Pong]) { | ||||||
|  | 	select { | ||||||
|  | 	case c.pongs <- w: | ||||||
|  | 	default: | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *connHandler) UpdatePeer(update peerUpdate) { | ||||||
|  | 	select { | ||||||
|  | 	case c.peerUpdates <- update: | ||||||
|  | 	default: | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								peer/connsender.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								peer/connsender.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | package peer | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"log" | ||||||
|  | 	"net" | ||||||
|  | 	"runtime/debug" | ||||||
|  | 	"sync" | ||||||
|  | 	"vppn/fasttime" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type connSender struct { | ||||||
|  | 	conn       *net.UDPConn | ||||||
|  | 	sourceIP   byte | ||||||
|  | 	streamID   byte | ||||||
|  | 	encrypted  []byte | ||||||
|  | 	nonceBuf   []byte | ||||||
|  | 	counterTS  uint64 | ||||||
|  | 	counter    uint64 | ||||||
|  | 	signingKey []byte | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newConnSender(conn *net.UDPConn, srcIP, streamID byte, signingPrivKey []byte) *connSender { | ||||||
|  | 	return &connSender{ | ||||||
|  | 		conn:       conn, | ||||||
|  | 		sourceIP:   srcIP, | ||||||
|  | 		streamID:   streamID, | ||||||
|  | 		encrypted:  make([]byte, BUFFER_SIZE), | ||||||
|  | 		nonceBuf:   make([]byte, NONCE_SIZE), | ||||||
|  | 		signingKey: signingPrivKey, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (cs *connSender) send(packetType byte, packet []byte, route *route) { | ||||||
|  | 	now := uint64(fasttime.Now()) | ||||||
|  | 	if cs.counterTS < now { | ||||||
|  | 		cs.counterTS = now | ||||||
|  | 		cs.counter = now << 30 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cs.counter++ | ||||||
|  |  | ||||||
|  | 	nonce := Nonce{ | ||||||
|  | 		Counter:    cs.counter, | ||||||
|  | 		SourceIP:   cs.sourceIP, | ||||||
|  | 		ViaIP:      route.ViaIP, | ||||||
|  | 		DestIP:     route.PeerIP, | ||||||
|  | 		StreamID:   cs.streamID, | ||||||
|  | 		PacketType: packetType, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	nonce.Marshal(cs.nonceBuf) | ||||||
|  |  | ||||||
|  | 	encrypted := encryptPacket(route.EncSharedKey, cs.nonceBuf, packet, cs.encrypted) | ||||||
|  |  | ||||||
|  | 	var toSend []byte | ||||||
|  | 	if route.ViaIP != 0 { | ||||||
|  | 		toSend = signPacket(cs.signingKey, encrypted, packet) | ||||||
|  | 	} else { | ||||||
|  | 		toSend = encrypted | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Printf("Sending to %v:   %+v", route.Addr, nonce) | ||||||
|  | 	if _, err := cs.conn.WriteToUDPAddrPort(toSend, route.Addr); err != nil { | ||||||
|  | 		log.Fatalf("Failed to write UDP packet: %v\n%s", err, debug.Stack()) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | type safeConnSender struct { | ||||||
|  | 	lock   sync.Mutex | ||||||
|  | 	sender *connSender | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newSafeConnSender(sender *connSender) *safeConnSender { | ||||||
|  | 	return &safeConnSender{sender: sender} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *safeConnSender) send(packetType byte, packet []byte, route *route) { | ||||||
|  | 	s.lock.Lock() | ||||||
|  | 	defer s.lock.Unlock() | ||||||
|  | 	s.sender.send(packetType, packet, route) | ||||||
|  | } | ||||||
							
								
								
									
										393
									
								
								peer/connstate.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										393
									
								
								peer/connstate.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,393 @@ | |||||||
|  | package peer | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"log" | ||||||
|  | 	"net/netip" | ||||||
|  | 	"time" | ||||||
|  | 	"vppn/m" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func logState(s connState, msg string, args ...any) { | ||||||
|  | 	log.Printf("["+s.Name()+"] "+msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | // The connection state corresponds to what we're connected TO. | ||||||
|  | type connState interface { | ||||||
|  | 	Name() string | ||||||
|  | 	HandleMediatorUpdate(ip byte) connState | ||||||
|  | 	HandleSendPing() | ||||||
|  | 	HandlePing(wrapper[Ping]) connState | ||||||
|  | 	HandlePong(wrapper[Pong]) connState | ||||||
|  | 	HandleTimeout() connState | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Helper function. | ||||||
|  |  | ||||||
|  | func newConnStateFromPeer(update peerUpdate, data *connData) connState { | ||||||
|  | 	peer := update.Peer | ||||||
|  |  | ||||||
|  | 	if peer == nil { | ||||||
|  | 		return newConnNull(data) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if _, isPublic := netip.AddrFromSlice(peer.PublicIP); isPublic { | ||||||
|  | 		return newConnUnconnectedServer(data, peer) | ||||||
|  | 	} else if data.server { | ||||||
|  | 		return newConnUnconnectedClient(data, peer) | ||||||
|  | 	} else { | ||||||
|  | 		return newConnUnconnectedMediator(data, peer) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ///////////////////// | ||||||
|  | // Null Connection // | ||||||
|  | ///////////////////// | ||||||
|  |  | ||||||
|  | type connNull struct { | ||||||
|  | 	*connData | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newConnNull(data *connData) connState { | ||||||
|  | 	c := connNull{data} | ||||||
|  | 	c.peer = nil | ||||||
|  | 	c.encSharedKey = nil | ||||||
|  | 	c.publicAddr = netip.AddrPort{} | ||||||
|  | 	c.pingTimer.Stop() | ||||||
|  | 	c.timeoutTimer.Stop() | ||||||
|  | 	c.addr = c.publicAddr | ||||||
|  | 	c.viaIP = 0 | ||||||
|  | 	c.up = false | ||||||
|  | 	c.route.Store(nil) | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connNull) Name() string { | ||||||
|  | 	return "NoPeer" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connNull) HandleMediatorUpdate(ip byte) connState { | ||||||
|  | 	c.mediatorIP = ip | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connNull) HandlePing(w wrapper[Ping]) connState { | ||||||
|  | 	logState(c, "Ignoring ping.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connNull) HandlePong(w wrapper[Pong]) connState { | ||||||
|  | 	logState(c, "Ignoring pong.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connNull) HandleTimeout() connState { | ||||||
|  | 	logState(c, "Unexpected timeout.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //////////////////////// | ||||||
|  | // Unconnected Server // | ||||||
|  | //////////////////////// | ||||||
|  |  | ||||||
|  | type connUnconnectedServer struct { | ||||||
|  | 	*connData | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newConnUnconnectedServer(data *connData, peer *m.Peer) connState { | ||||||
|  | 	addr, _ := netip.AddrFromSlice(peer.PublicIP) | ||||||
|  | 	pubAddr := netip.AddrPortFrom(addr, peer.Port) | ||||||
|  |  | ||||||
|  | 	c := connUnconnectedServer{data} | ||||||
|  | 	c.peer = peer | ||||||
|  | 	c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey) | ||||||
|  | 	c.publicAddr = pubAddr | ||||||
|  | 	c.pingTimer.Reset(time.Millisecond) // Ping right away to bring up. | ||||||
|  | 	c.timeoutTimer.Stop()               // No timeouts yet. | ||||||
|  | 	c.addr = c.publicAddr | ||||||
|  | 	c.viaIP = 0 | ||||||
|  | 	c.up = false | ||||||
|  | 	c.route.Store(c.Route()) | ||||||
|  |  | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedServer) Name() string { | ||||||
|  | 	return "ServerUnconnected" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedServer) HandleMediatorUpdate(ip byte) connState { | ||||||
|  | 	// Server connection doesn't use a mediator. | ||||||
|  | 	c.mediatorIP = ip | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedServer) HandlePing(w wrapper[Ping]) connState { | ||||||
|  | 	logState(c, "Ignoring ping.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedServer) HandlePong(w wrapper[Pong]) connState { | ||||||
|  | 	return newConnConnectedServer(c.connData, w) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedServer) HandleTimeout() connState { | ||||||
|  | 	logState(c, "Unexpected timeout.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ////////////////////// | ||||||
|  | // Connected Server // | ||||||
|  | ////////////////////// | ||||||
|  |  | ||||||
|  | type connConnectedServer struct { | ||||||
|  | 	*connData | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newConnConnectedServer(data *connData, w wrapper[Pong]) connState { | ||||||
|  | 	c := connConnectedServer{data} | ||||||
|  | 	c.pingTimer.Reset(pingInterval) | ||||||
|  | 	c.timeoutTimer.Reset(timeoutInterval) | ||||||
|  | 	c.addr = w.SrcAddr | ||||||
|  | 	c.viaIP = 0 | ||||||
|  | 	c.up = true | ||||||
|  | 	c.route.Store(c.Route()) | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedServer) Name() string { | ||||||
|  | 	return "ServerConnected" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedServer) HandleMediatorUpdate(ip byte) connState { | ||||||
|  | 	// Server connection doesn't use a mediator. | ||||||
|  | 	c.mediatorIP = ip | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedServer) HandlePing(w wrapper[Ping]) connState { | ||||||
|  | 	logState(c, "Ignoring ping.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedServer) HandlePong(w wrapper[Pong]) connState { | ||||||
|  | 	c.timeoutTimer.Reset(timeoutInterval) | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedServer) HandleTimeout() connState { | ||||||
|  | 	return newConnUnconnectedServer(c.connData, c.peer) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //////////////////////// | ||||||
|  | // Unconnected Client // | ||||||
|  | //////////////////////// | ||||||
|  |  | ||||||
|  | type connUnconnectedClient struct { | ||||||
|  | 	*connData | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newConnUnconnectedClient(data *connData, peer *m.Peer) connState { | ||||||
|  | 	addr, _ := netip.AddrFromSlice(peer.PublicIP) | ||||||
|  | 	pubAddr := netip.AddrPortFrom(addr, peer.Port) | ||||||
|  |  | ||||||
|  | 	c := connUnconnectedClient{data} | ||||||
|  | 	c.peer = peer | ||||||
|  | 	c.publicAddr = pubAddr | ||||||
|  | 	c.encPrivKey = data.encPrivKey | ||||||
|  | 	c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey) | ||||||
|  | 	c.addr = c.publicAddr | ||||||
|  | 	c.viaIP = 0 | ||||||
|  | 	c.up = false | ||||||
|  | 	c.route.Store(c.Route()) | ||||||
|  |  | ||||||
|  | 	c.pingTimer.Stop()    // Conncection is from client => pings incoming. | ||||||
|  | 	c.timeoutTimer.Stop() // No timeouts yet. | ||||||
|  |  | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedClient) Name() string { | ||||||
|  | 	return "ClientUnconnected" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedClient) HandleMediatorUpdate(ip byte) connState { | ||||||
|  | 	// Client connection doesn't use a mediator. | ||||||
|  | 	c.mediatorIP = ip | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedClient) HandlePing(w wrapper[Ping]) connState { | ||||||
|  | 	next := newConnConnectedClient(c.connData, w) | ||||||
|  | 	c.sendPong(w) // Have to send after transitionsing so route is ok. | ||||||
|  | 	return next | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedClient) HandlePong(w wrapper[Pong]) connState { | ||||||
|  | 	logState(c, "Ignorning pong.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedClient) HandleTimeout() connState { | ||||||
|  | 	logState(c, "Unexpected timeout.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ////////////////////// | ||||||
|  | // Connected Client // | ||||||
|  | ////////////////////// | ||||||
|  |  | ||||||
|  | type connConnectedClient struct { | ||||||
|  | 	*connData | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newConnConnectedClient(data *connData, w wrapper[Ping]) connState { | ||||||
|  | 	c := connConnectedClient{data} | ||||||
|  | 	c.addr = w.SrcAddr | ||||||
|  | 	c.viaIP = 0 | ||||||
|  | 	c.up = true | ||||||
|  | 	c.route.Store(c.Route()) | ||||||
|  |  | ||||||
|  | 	c.pingTimer.Stop() // Conncection is from client => pings incoming. | ||||||
|  | 	c.timeoutTimer.Reset(timeoutInterval) | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedClient) Name() string { | ||||||
|  | 	return "ClientConnected" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedClient) HandleMediatorUpdate(ip byte) connState { | ||||||
|  | 	// Client connection doesn't use a mediator. | ||||||
|  | 	c.mediatorIP = ip | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedClient) HandlePing(w wrapper[Ping]) connState { | ||||||
|  | 	// The connection is from a client. If the client's address changes, we | ||||||
|  | 	// should follow that change. | ||||||
|  | 	if c.addr != w.SrcAddr { | ||||||
|  | 		c.addr = w.SrcAddr | ||||||
|  | 		c.route.Store(c.Route()) | ||||||
|  | 	} | ||||||
|  | 	c.sendPong(w) | ||||||
|  | 	c.timeoutTimer.Reset(timeoutInterval) | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedClient) HandlePong(w wrapper[Pong]) connState { | ||||||
|  | 	logState(c, "Ignoring pong.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedClient) HandleTimeout() connState { | ||||||
|  | 	return newConnUnconnectedClient(c.connData, c.peer) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ////////////////////////// | ||||||
|  | // Unconnected Mediator // | ||||||
|  | ////////////////////////// | ||||||
|  |  | ||||||
|  | type connUnconnectedMediator struct { | ||||||
|  | 	*connData | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newConnUnconnectedMediator(data *connData, peer *m.Peer) connState { | ||||||
|  | 	addr, _ := netip.AddrFromSlice(peer.PublicIP) | ||||||
|  | 	pubAddr := netip.AddrPortFrom(addr, peer.Port) | ||||||
|  |  | ||||||
|  | 	c := connUnconnectedMediator{data} | ||||||
|  | 	c.peer = peer | ||||||
|  | 	c.publicAddr = pubAddr | ||||||
|  | 	c.encPrivKey = data.encPrivKey | ||||||
|  | 	c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey) | ||||||
|  | 	c.addr = c.publicAddr | ||||||
|  | 	c.viaIP = 0 | ||||||
|  | 	c.up = false | ||||||
|  | 	c.route.Store(c.Route()) | ||||||
|  |  | ||||||
|  | 	c.pingTimer.Stop()    // No pings for mediators. | ||||||
|  | 	c.timeoutTimer.Stop() // No timeouts yet. | ||||||
|  |  | ||||||
|  | 	// If we have a mediator route, we can connect. | ||||||
|  | 	if mRoute := c.routes[c.mediatorIP].Load(); mRoute != nil { | ||||||
|  | 		return newConnConnectedMediator(data, mRoute) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedMediator) Name() string { | ||||||
|  | 	return "MediatorUnconnected" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedMediator) HandleMediatorUpdate(ip byte) connState { | ||||||
|  | 	c.mediatorIP = ip | ||||||
|  | 	if mRoute := c.routes[c.mediatorIP].Load(); mRoute != nil { | ||||||
|  | 		return newConnConnectedMediator(c.connData, mRoute) | ||||||
|  | 	} | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedMediator) HandlePing(w wrapper[Ping]) connState { | ||||||
|  | 	logState(c, "Ignorning ping.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedMediator) HandlePong(w wrapper[Pong]) connState { | ||||||
|  | 	logState(c, "Ignorning pong.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connUnconnectedMediator) HandleTimeout() connState { | ||||||
|  | 	logState(c, "Unexpected timeout.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //////////////////////// | ||||||
|  | // Connected Mediator // | ||||||
|  | //////////////////////// | ||||||
|  |  | ||||||
|  | type connConnectedMediator struct { | ||||||
|  | 	*connData | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newConnConnectedMediator(data *connData, route *route) connState { | ||||||
|  | 	c := connConnectedMediator{data} | ||||||
|  | 	c.addr = route.Addr | ||||||
|  | 	c.viaIP = route.PeerIP | ||||||
|  | 	c.up = true | ||||||
|  | 	c.route.Store(c.Route()) | ||||||
|  |  | ||||||
|  | 	// No pings for mediated routes. | ||||||
|  | 	c.pingTimer.Stop() | ||||||
|  | 	c.timeoutTimer.Stop() | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedMediator) Name() string { | ||||||
|  | 	return "MediatorConnected" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedMediator) HandleMediatorUpdate(ip byte) connState { | ||||||
|  | 	c.mediatorIP = ip | ||||||
|  | 	if mRoute := c.routes[c.mediatorIP].Load(); mRoute != nil { | ||||||
|  | 		return newConnConnectedMediator(c.connData, mRoute) | ||||||
|  | 	} | ||||||
|  | 	return newConnUnconnectedMediator(c.connData, c.peer) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedMediator) HandlePing(w wrapper[Ping]) connState { | ||||||
|  | 	logState(c, "Ignoring ping.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedMediator) HandlePong(w wrapper[Pong]) connState { | ||||||
|  | 	logState(c, "Ignoring pong.") | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c connConnectedMediator) HandleTimeout() connState { | ||||||
|  | 	return newConnUnconnectedMediator(c.connData, c.peer) | ||||||
|  | } | ||||||
| @@ -13,4 +13,10 @@ const ( | |||||||
|  |  | ||||||
| 	// Basic packet types | 	// Basic packet types | ||||||
| 	PACKET_TYPE_DATA = 0 | 	PACKET_TYPE_DATA = 0 | ||||||
|  | 	PACKET_TYPE_PING = 1 | ||||||
|  | 	PACKET_TYPE_PONG = 2 | ||||||
|  |  | ||||||
|  | 	// Packet sizes. | ||||||
|  | 	PING_SIZE = 8 | ||||||
|  | 	PONG_SIZE = 16 | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -10,8 +10,6 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
| 	"runtime/debug" | 	"runtime/debug" | ||||||
| 	"vppn/m" | 	"vppn/m" | ||||||
|  |  | ||||||
| 	_ "net/http/pprof" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func Main() { | func Main() { | ||||||
| @@ -21,11 +19,6 @@ func Main() { | |||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| 	go func() { |  | ||||||
| 		log.Printf("Serving on localhost:6060...") |  | ||||||
| 		log.Println(http.ListenAndServe("localhost:6060", nil)) |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	var ( | 	var ( | ||||||
| 		netName  string | 		netName  string | ||||||
| 		initURL  string | 		initURL  string | ||||||
|   | |||||||
| @@ -2,7 +2,16 @@ package peer | |||||||
|  |  | ||||||
| import "unsafe" | import "unsafe" | ||||||
|  |  | ||||||
| func ParseNonceBytes(nb []byte, nonce *Nonce) { | type Nonce struct { | ||||||
|  | 	Counter    uint64 | ||||||
|  | 	SourceIP   byte | ||||||
|  | 	ViaIP      byte | ||||||
|  | 	DestIP     byte | ||||||
|  | 	StreamID   byte // The stream, see STREAM_* constants | ||||||
|  | 	PacketType byte // The packet type. See PACKET_* constants. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (nonce *Nonce) Parse(nb []byte) { | ||||||
| 	nonce.Counter = *(*uint64)(unsafe.Pointer(&nb[0])) | 	nonce.Counter = *(*uint64)(unsafe.Pointer(&nb[0])) | ||||||
| 	nonce.SourceIP = nb[8] | 	nonce.SourceIP = nb[8] | ||||||
| 	nonce.ViaIP = nb[9] | 	nonce.ViaIP = nb[9] | ||||||
| @@ -11,14 +20,13 @@ func ParseNonceBytes(nb []byte, nonce *Nonce) { | |||||||
| 	nonce.PacketType = nb[12] | 	nonce.PacketType = nb[12] | ||||||
| } | } | ||||||
|  |  | ||||||
| func MarshalNonce(nonce Nonce, buf []byte) { | func (nonce Nonce) Marshal(buf []byte) { | ||||||
| 	*(*uint64)(unsafe.Pointer(&buf[0])) = nonce.Counter | 	*(*uint64)(unsafe.Pointer(&buf[0])) = nonce.Counter | ||||||
| 	buf[8] = nonce.SourceIP | 	buf[8] = nonce.SourceIP | ||||||
| 	buf[9] = nonce.ViaIP | 	buf[9] = nonce.ViaIP | ||||||
| 	buf[10] = nonce.DestIP | 	buf[10] = nonce.DestIP | ||||||
| 	buf[11] = nonce.StreamID | 	buf[11] = nonce.StreamID | ||||||
| 	buf[12] = nonce.PacketType | 	buf[12] = nonce.PacketType | ||||||
| 	clear(buf[13:]) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func CounterTimestamp(counter uint64) int64 { | func CounterTimestamp(counter uint64) int64 { | ||||||
|   | |||||||
| @@ -15,10 +15,10 @@ func TestMarshalParseNonce(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	buf := make([]byte, NONCE_SIZE) | 	buf := make([]byte, NONCE_SIZE) | ||||||
| 	MarshalNonce(nIn, buf) | 	nIn.Marshal(buf) | ||||||
|  |  | ||||||
| 	nOut := Nonce{} | 	nOut := Nonce{} | ||||||
| 	ParseNonceBytes(buf, &nOut) | 	nOut.Parse(buf) | ||||||
| 	if nIn != nOut { | 	if nIn != nOut { | ||||||
| 		t.Fatal(nIn, nOut) | 		t.Fatal(nIn, nOut) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -3,10 +3,7 @@ package peer | |||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log" | 	"log" | ||||||
| 	"net" |  | ||||||
| 	"net/netip" |  | ||||||
| 	"runtime/debug" | 	"runtime/debug" | ||||||
| 	"vppn/fasttime" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func (peer *Peer) ifReader() { | func (peer *Peer) ifReader() { | ||||||
| @@ -17,36 +14,16 @@ func (peer *Peer) ifReader() { | |||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| 	var ( | 	var ( | ||||||
|  | 		sender = newConnSender(peer.conn, peer.ip, STREAM_DATA, peer.signPrivKey) | ||||||
| 		n      int | 		n      int | ||||||
| 		destIP byte | 		destIP byte | ||||||
| 		router = peer.router | 		router = peer.router | ||||||
| 		route  *route | 		route  *route | ||||||
| 		iface  = peer.iface | 		iface  = peer.iface | ||||||
| 		nonce  = Nonce{ |  | ||||||
| 			SourceIP:   peer.ip, |  | ||||||
| 			PacketType: PACKET_TYPE_DATA, |  | ||||||
| 			StreamID:   STREAM_DATA, |  | ||||||
| 		} |  | ||||||
| 		err    error | 		err    error | ||||||
| 		now        uint64 |  | ||||||
| 		counterTS  uint64 |  | ||||||
| 		counter    uint64 |  | ||||||
| 		packet = make([]byte, BUFFER_SIZE) | 		packet = make([]byte, BUFFER_SIZE) | ||||||
| 		encrypted  = make([]byte, BUFFER_SIZE) |  | ||||||
| 		nonceBuf   = make([]byte, NONCE_SIZE) |  | ||||||
| 		toSend     []byte |  | ||||||
| 		signingKey = peer.signPrivKey |  | ||||||
|  |  | ||||||
| 		reqPool   = make(chan udpWriteReq, 1024) |  | ||||||
| 		writeChan = make(chan udpWriteReq, 1024) |  | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	for range cap(reqPool) { |  | ||||||
| 		reqPool <- udpWriteReq{Packet: make([]byte, BUFFER_SIZE)} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	go udpWriter(writeChan, peer.conn, reqPool) |  | ||||||
|  |  | ||||||
| 	for { | 	for { | ||||||
| 		n, err = iface.Read(packet[:BUFFER_SIZE]) | 		n, err = iface.Read(packet[:BUFFER_SIZE]) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @@ -61,54 +38,13 @@ func (peer *Peer) ifReader() { | |||||||
| 		packet = packet[:n] | 		packet = packet[:n] | ||||||
|  |  | ||||||
| 		destIP = packet[19] | 		destIP = packet[19] | ||||||
|  |  | ||||||
| 		route = router.GetRoute(destIP) | 		route = router.GetRoute(destIP) | ||||||
| 		if route == nil { | 		if route == nil || !route.Up { | ||||||
| 			log.Printf("Dropping packet for non-existent IP: %d", destIP) | 			log.Printf("Dropping packet for non-existent IP: %d", destIP) | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		now = uint64(fasttime.Now()) | 		sender.send(PACKET_TYPE_DATA, packet, route) | ||||||
| 		if counterTS < now { |  | ||||||
| 			counterTS = now |  | ||||||
| 			counter = now << 30 |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		counter++ |  | ||||||
|  |  | ||||||
| 		nonce.Counter = counter |  | ||||||
| 		nonce.ViaIP = route.ViaIP |  | ||||||
| 		nonce.DestIP = destIP |  | ||||||
|  |  | ||||||
| 		MarshalNonce(nonce, nonceBuf) |  | ||||||
|  |  | ||||||
| 		encrypted = encryptPacket(route.EncSharedKey, nonceBuf, packet, encrypted) |  | ||||||
|  |  | ||||||
| 		if route.ViaIP != 0 { |  | ||||||
| 			toSend = signPacket(signingKey, encrypted, packet) |  | ||||||
| 		} else { |  | ||||||
| 			toSend = encrypted |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		req := <-reqPool |  | ||||||
| 		req.Addr = route.Addr |  | ||||||
| 		req.Packet = req.Packet[:len(toSend)] |  | ||||||
| 		copy(req.Packet, toSend) |  | ||||||
|  |  | ||||||
| 		writeChan <- req |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type udpWriteReq struct { |  | ||||||
| 	Addr   netip.AddrPort |  | ||||||
| 	Packet []byte |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func udpWriter(in chan udpWriteReq, conn *net.UDPConn, reqPool chan udpWriteReq) { |  | ||||||
| 	var err error |  | ||||||
| 	for req := range in { |  | ||||||
| 		if _, err = conn.WriteToUDPAddrPort(req.Packet, req.Addr); err != nil { |  | ||||||
| 			log.Fatalf("Failed to write UDP packet: %v", err) |  | ||||||
| 		} |  | ||||||
| 		reqPool <- req |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,8 +2,8 @@ package peer | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" |  | ||||||
| 	"log" | 	"log" | ||||||
|  | 	"net/netip" | ||||||
| 	"runtime/debug" | 	"runtime/debug" | ||||||
| 	"vppn/fasttime" | 	"vppn/fasttime" | ||||||
| ) | ) | ||||||
| @@ -17,34 +17,25 @@ func (peer *Peer) netReader() { | |||||||
|  |  | ||||||
| 	var ( | 	var ( | ||||||
| 		n         int | 		n         int | ||||||
| 		//srcAddr   *net.UDPAddr | 		srcAddr   netip.AddrPort | ||||||
| 		nonce     Nonce | 		nonce     Nonce | ||||||
| 		packet    = make([]byte, BUFFER_SIZE) | 		packet    = make([]byte, BUFFER_SIZE) | ||||||
| 		decrypted = make([]byte, BUFFER_SIZE) | 		decrypted = make([]byte, BUFFER_SIZE) | ||||||
| 		toWrite   []byte |  | ||||||
| 		route     *route | 		route     *route | ||||||
| 		ok        bool | 		ok        bool | ||||||
| 		err       error | 		err       error | ||||||
| 		conn      = peer.conn | 		conn      = peer.conn | ||||||
| 		ip        = peer.ip | 		ip        = peer.ip | ||||||
| 		counters  = [2][256]uint64{} // Counter by stream and IP. | 		counters  = [2][256]uint64{} // Counter by stream and IP. | ||||||
|  |  | ||||||
| 		ifaceChan = make(chan []byte, 1024) |  | ||||||
| 		reqPool   = make(chan []byte, 1024) |  | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	for range cap(reqPool) { |  | ||||||
| 		reqPool <- make([]byte, BUFFER_SIZE) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	go ifWriter(ifaceChan, peer.iface, reqPool) |  | ||||||
|  |  | ||||||
| NEXT_PACKET: | NEXT_PACKET: | ||||||
|  |  | ||||||
| 	n, _, err = conn.ReadFromUDPAddrPort(packet[:BUFFER_SIZE]) | 	n, srcAddr, err = conn.ReadFromUDPAddrPort(packet[:BUFFER_SIZE]) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatalf("Failed to read UDP packet: %v", err) | 		log.Fatalf("Failed to read UDP packet: %v", err) | ||||||
| 	} | 	} | ||||||
|  | 	srcAddr = netip.AddrPortFrom(srcAddr.Addr().Unmap(), srcAddr.Port()) | ||||||
|  |  | ||||||
| 	if n < NONCE_SIZE { | 	if n < NONCE_SIZE { | ||||||
| 		log.Printf("Dropping short UDP packet: %d", n) | 		log.Printf("Dropping short UDP packet: %d", n) | ||||||
| @@ -52,8 +43,7 @@ NEXT_PACKET: | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	packet = packet[:n] | 	packet = packet[:n] | ||||||
|  | 	nonce.Parse(packet[n-NONCE_SIZE:]) | ||||||
| 	ParseNonceBytes(packet[n-NONCE_SIZE:], &nonce) |  | ||||||
|  |  | ||||||
| 	// Drop after 8 seconds. | 	// Drop after 8 seconds. | ||||||
| 	if CounterTimestamp(nonce.Counter) < fasttime.Now()-8 { | 	if CounterTimestamp(nonce.Counter) < fasttime.Now()-8 { | ||||||
| @@ -62,13 +52,13 @@ NEXT_PACKET: | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if nonce.StreamID > 1 { | 	if nonce.StreamID > 1 { | ||||||
| 		log.Printf("Dropping invalid stream ID: %v", nonce) | 		log.Printf("Dropping invalid stream ID: %+v", nonce) | ||||||
| 		goto NEXT_PACKET | 		goto NEXT_PACKET | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Check source counter. | 	// Check source counter. | ||||||
| 	if nonce.Counter <= counters[nonce.StreamID][nonce.SourceIP] { | 	if nonce.Counter <= counters[nonce.StreamID][nonce.SourceIP] { | ||||||
| 		log.Printf("Dropping packet with bad counter: %v", nonce) | 		log.Printf("Dropping packet with bad counter: %+v", nonce) | ||||||
| 		goto NEXT_PACKET | 		goto NEXT_PACKET | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -76,7 +66,7 @@ NEXT_PACKET: | |||||||
|  |  | ||||||
| 	route = peer.router.GetRoute(nonce.SourceIP) | 	route = peer.router.GetRoute(nonce.SourceIP) | ||||||
| 	if route == nil { | 	if route == nil { | ||||||
| 		log.Printf("Dropping packet without route: %v", nonce) | 		log.Printf("Dropping packet without route: %+v", nonce) | ||||||
| 		goto NEXT_PACKET | 		goto NEXT_PACKET | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -86,7 +76,7 @@ NEXT_PACKET: | |||||||
| 	case nonce.ViaIP: | 	case nonce.ViaIP: | ||||||
| 		goto VALIDATE_SIGNATURE | 		goto VALIDATE_SIGNATURE | ||||||
| 	default: | 	default: | ||||||
| 		log.Printf("Bad packet: %v", nonce) | 		log.Printf("Bad packet: %+v", nonce) | ||||||
| 		goto NEXT_PACKET | 		goto NEXT_PACKET | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -110,49 +100,41 @@ DECRYPT: | |||||||
|  |  | ||||||
| WRITE_IFACE_DATA: | WRITE_IFACE_DATA: | ||||||
|  |  | ||||||
| 	//toWrite = toWrite[:len(decrypted)] | 	if _, err = peer.iface.Write(decrypted); err != nil { | ||||||
| 	//copy(toWrite, decrypted) | 		log.Fatalf("Failed to write to interface: %v", err) | ||||||
|  | 	} | ||||||
| 	toWrite = <-reqPool |  | ||||||
| 	ifaceChan <- append(toWrite[:0], decrypted...) |  | ||||||
|  |  | ||||||
| 	goto NEXT_PACKET | 	goto NEXT_PACKET | ||||||
|  |  | ||||||
| WRITE_ROUTING_PACKET: | WRITE_ROUTING_PACKET: | ||||||
|  |  | ||||||
| 	//peer.processRoutingPacket(decrypted) | 	peer.router.HandlePacket(srcAddr, nonce, decrypted) | ||||||
| 	//peer.routeManager.ProcessPacket(decrypted) |  | ||||||
|  |  | ||||||
| 	goto NEXT_PACKET | 	goto NEXT_PACKET | ||||||
|  |  | ||||||
| VALIDATE_SIGNATURE: | VALIDATE_SIGNATURE: | ||||||
|  |  | ||||||
| 	// We don't forward twice. |  | ||||||
| 	if route.Mediator { |  | ||||||
| 		log.Printf("Dropping double-forward packet: %v", nonce) |  | ||||||
| 		goto NEXT_PACKET |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	decrypted, ok = openPacket(route.SignPubKey, packet, decrypted) | 	decrypted, ok = openPacket(route.SignPubKey, packet, decrypted) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		log.Printf("Failed to open signed packet: %v", nonce) | 		log.Printf("Failed to open signed packet: %v", nonce) | ||||||
| 		goto NEXT_PACKET | 		goto NEXT_PACKET | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	route = peer.router.GetRoute(nonce.DestIP) | ||||||
|  | 	if route == nil || !route.Up { | ||||||
|  | 		log.Printf("Dropping mediated packet, route not available: %v", nonce) | ||||||
|  | 		goto NEXT_PACKET | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// We don't forward twice. | ||||||
|  | 	if route.ViaIP != 0 { | ||||||
|  | 		log.Printf("Dropping double-forward packet: %v", nonce) | ||||||
|  | 		goto NEXT_PACKET | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if _, err = conn.WriteToUDPAddrPort(decrypted, route.Addr); err != nil { | 	if _, err = conn.WriteToUDPAddrPort(decrypted, route.Addr); err != nil { | ||||||
| 		log.Fatalf("Failed to forward packet: %v", err) | 		log.Fatalf("Failed to forward packet: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	goto NEXT_PACKET | 	goto NEXT_PACKET | ||||||
| } | } | ||||||
|  |  | ||||||
| func ifWriter(in chan []byte, iface io.ReadWriteCloser, out chan []byte) { |  | ||||||
| 	var err error |  | ||||||
| 	for packet := range in { |  | ||||||
| 		if _, err = iface.Write(packet); err != nil { |  | ||||||
| 			log.Printf("Size: %d", len(packet)) |  | ||||||
| 			log.Fatalf("Failed to write to interface: %v", err) |  | ||||||
| 		} |  | ||||||
| 		out <- packet |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								peer/peer.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								peer/peer.go
									
									
									
									
									
								
							| @@ -4,14 +4,15 @@ import ( | |||||||
| 	"io" | 	"io" | ||||||
| 	"log" | 	"log" | ||||||
| 	"net" | 	"net" | ||||||
|  | 	"net/netip" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Peer struct { | type Peer struct { | ||||||
| 	// Immutable data. |  | ||||||
| 	ip          byte // Last byte of IPv4 address. | 	ip          byte // Last byte of IPv4 address. | ||||||
| 	hubAddr     string | 	hubAddr     string | ||||||
| 	apiKey      string | 	apiKey      string | ||||||
|  | 	isServer    bool | ||||||
|  | 	isMediator  bool | ||||||
| 	encPubKey   []byte | 	encPubKey   []byte | ||||||
| 	encPrivKey  []byte | 	encPrivKey  []byte | ||||||
| 	signPubKey  []byte | 	signPubKey  []byte | ||||||
| @@ -31,6 +32,7 @@ func NewPeer(netName, listenIP string, port uint16) (*Peer, error) { | |||||||
| 	peer := &Peer{ | 	peer := &Peer{ | ||||||
| 		ip:          conf.PeerIP, | 		ip:          conf.PeerIP, | ||||||
| 		hubAddr:     conf.HubAddress, | 		hubAddr:     conf.HubAddress, | ||||||
|  | 		isMediator:  conf.Mediator, | ||||||
| 		apiKey:      conf.APIKey, | 		apiKey:      conf.APIKey, | ||||||
| 		encPubKey:   conf.EncPubKey, | 		encPubKey:   conf.EncPubKey, | ||||||
| 		encPrivKey:  conf.EncPrivKey, | 		encPrivKey:  conf.EncPrivKey, | ||||||
| @@ -38,16 +40,16 @@ func NewPeer(netName, listenIP string, port uint16) (*Peer, error) { | |||||||
| 		signPrivKey: conf.SignPrivKey, | 		signPrivKey: conf.SignPrivKey, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	peer.router = NewRouter(conf) | 	_, peer.isServer = netip.AddrFromSlice(conf.PublicIP) | ||||||
|  |  | ||||||
| 	port = determinePort(conf.Port, port) | 	port = determinePort(conf.Port, port) | ||||||
|  |  | ||||||
| 	conn, err := openUDPConn(listenIP, port) | 	peer.conn, err = openUDPConn(listenIP, port) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	peer.conn = conn | 	peer.router = NewRouter(conf, peer.conn) | ||||||
|  |  | ||||||
| 	peer.iface, err = openInterface(conf.Network, conf.PeerIP, netName) | 	peer.iface, err = openInterface(conf.Network, conf.PeerIP, netName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
							
								
								
									
										45
									
								
								peer/router-managemediator.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								peer/router-managemediator.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | package peer | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"math/rand" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (r *Router) manageMediator() { | ||||||
|  | 	var ( | ||||||
|  | 		ip        = byte(0) | ||||||
|  | 		mediators = make([]*route, 0, 32) | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	for range time.Tick(8 * time.Second) { | ||||||
|  | 		// If the current mediator is up, keep it. | ||||||
|  | 		route := r.routes[ip].Load() | ||||||
|  | 		if route != nil && route.Up { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// If the current mediator is up, keep it. | ||||||
|  | 		mediators = mediators[:0] | ||||||
|  |  | ||||||
|  | 		for i := range r.routes { | ||||||
|  | 			route := r.routes[i].Load() | ||||||
|  | 			if route == nil || !route.Mediator { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			mediators = append(mediators, route) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if len(mediators) == 0 { | ||||||
|  | 			ip = 0 | ||||||
|  | 		} else { | ||||||
|  | 			ip = mediators[rand.Intn(len(mediators))].PeerIP | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		for _, conn := range r.conns { | ||||||
|  | 			if conn != nil { | ||||||
|  | 				conn.UpdateMediator(ip) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -1,18 +0,0 @@ | |||||||
| package peer |  | ||||||
|  |  | ||||||
| type RoutingPingReq struct { |  | ||||||
| 	PeerIP byte |  | ||||||
| 	Type   byte // 0 => local, 1 => direct, 2 => Via |  | ||||||
| 	Addr   []byte |  | ||||||
| 	Port   int |  | ||||||
| 	SentAt int64 // unix milli |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type RoutingPingResp struct { |  | ||||||
| 	PeerIP  byte |  | ||||||
| 	Type    byte // 0 => local, 1 => direct, 2 => Via |  | ||||||
| 	Addr    []byte |  | ||||||
| 	Port    int |  | ||||||
| 	SentAt  int64 |  | ||||||
| 	RecvdAt int64 |  | ||||||
| } |  | ||||||
							
								
								
									
										37
									
								
								peer/router-ping_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								peer/router-ping_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | package peer | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestMarshalParsePingReq(t *testing.T) { | ||||||
|  | 	in := Ping{ | ||||||
|  | 		SentAt: 4553252, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	buf := make([]byte, PING_SIZE) | ||||||
|  | 	in.Marshal(buf) | ||||||
|  |  | ||||||
|  | 	out := Ping{} | ||||||
|  | 	out.Parse(buf) | ||||||
|  | 	if !reflect.DeepEqual(in, out) { | ||||||
|  | 		t.Fatal(in, out) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMarshalParsePingResp(t *testing.T) { | ||||||
|  | 	in := Pong{ | ||||||
|  | 		SentAt:  4553252, | ||||||
|  | 		RecvdAt: 4553253, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	buf := make([]byte, PONG_SIZE) | ||||||
|  | 	in.Marshal(buf) | ||||||
|  |  | ||||||
|  | 	out := Pong{} | ||||||
|  | 	out.Parse(buf) | ||||||
|  | 	if !reflect.DeepEqual(in, out) { | ||||||
|  | 		t.Fatal(in, out) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										63
									
								
								peer/router-pollhub.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								peer/router-pollhub.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | package peer | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"io" | ||||||
|  | 	"log" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 	"time" | ||||||
|  | 	"vppn/m" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (r *Router) pollHub() { | ||||||
|  | 	u, err := url.Parse(r.conf.HubAddress) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("Failed to parse hub address %s: %v", r.conf.HubAddress, err) | ||||||
|  | 	} | ||||||
|  | 	u.Path = "/peer/fetch-state/" | ||||||
|  |  | ||||||
|  | 	client := &http.Client{Timeout: 8 * time.Second} | ||||||
|  |  | ||||||
|  | 	req := &http.Request{ | ||||||
|  | 		Method: http.MethodGet, | ||||||
|  | 		URL:    u, | ||||||
|  | 		Header: http.Header{}, | ||||||
|  | 	} | ||||||
|  | 	req.SetBasicAuth("", r.conf.APIKey) | ||||||
|  |  | ||||||
|  | 	// TODO: Before we start polling, load state from the file system. | ||||||
|  | 	r._pollHub(client, req) | ||||||
|  |  | ||||||
|  | 	for range time.Tick(32 * time.Second) { | ||||||
|  | 		r._pollHub(client, req) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *Router) _pollHub(client *http.Client, req *http.Request) { | ||||||
|  | 	var state m.NetworkState | ||||||
|  |  | ||||||
|  | 	log.Printf("Fetching peer state from %s...", r.conf.HubAddress) | ||||||
|  | 	resp, err := client.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Printf("Failed to fetch peer state: %v", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	body, err := io.ReadAll(resp.Body) | ||||||
|  | 	_ = resp.Body.Close() | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Printf("Failed to read body from hub: %v", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := json.Unmarshal(body, &state); err != nil { | ||||||
|  | 		log.Printf("Failed to unmarshal response from hub: %v", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for i, peer := range state.Peers { | ||||||
|  | 		if r.conns[i] != nil { | ||||||
|  | 			r.conns[i].UpdatePeer(peerUpdate{PeerIP: byte(i), Peer: peer}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										74
									
								
								peer/router-types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								peer/router-types.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | package peer | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/netip" | ||||||
|  | 	"unsafe" | ||||||
|  | 	"vppn/m" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | // A route is used by a peer to send or receive packets. A peer won't send | ||||||
|  | // packets on a route that isn't up, but will process incoming packets on | ||||||
|  | // that route. | ||||||
|  | type route struct { | ||||||
|  | 	PeerIP       byte | ||||||
|  | 	Up           bool | ||||||
|  | 	Mediator     bool | ||||||
|  | 	SignPubKey   []byte | ||||||
|  | 	EncSharedKey []byte         // Shared key for encoding / decoding packets. | ||||||
|  | 	Addr         netip.AddrPort // Address to send to. | ||||||
|  | 	ViaIP        byte           // If != 0, this is a forwarding address. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type peerUpdate struct { | ||||||
|  | 	PeerIP  byte | ||||||
|  | 	*m.Peer // nil => delete. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | // Wrapper for routing packets. | ||||||
|  | type wrapper[T any] struct { | ||||||
|  | 	T       T | ||||||
|  | 	Nonce   Nonce | ||||||
|  | 	SrcAddr netip.AddrPort | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newWrapper[T any](srcAddr netip.AddrPort, nonce Nonce) wrapper[T] { | ||||||
|  | 	return wrapper[T]{ | ||||||
|  | 		SrcAddr: srcAddr, | ||||||
|  | 		Nonce:   nonce, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | type Ping struct { | ||||||
|  | 	SentAt int64 // unix milli | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *Ping) Parse(buf []byte) { | ||||||
|  | 	p.SentAt = *(*int64)(unsafe.Pointer(&buf[0])) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p Ping) Marshal(buf []byte) { | ||||||
|  | 	*(*int64)(unsafe.Pointer(&buf[0])) = p.SentAt | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | type Pong struct { | ||||||
|  | 	SentAt  int64 // unix mili | ||||||
|  | 	RecvdAt int64 // unix mili | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *Pong) Parse(buf []byte) { | ||||||
|  | 	p.SentAt = *(*int64)(unsafe.Pointer(&buf[0])) | ||||||
|  | 	p.RecvdAt = *(*int64)(unsafe.Pointer(&buf[8])) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *Pong) Marshal(buf []byte) { | ||||||
|  | 	*(*int64)(unsafe.Pointer(&buf[0])) = p.SentAt | ||||||
|  | 	*(*int64)(unsafe.Pointer(&buf[8])) = p.RecvdAt | ||||||
|  | } | ||||||
							
								
								
									
										143
									
								
								peer/router.go
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								peer/router.go
									
									
									
									
									
								
							| @@ -1,123 +1,86 @@ | |||||||
| package peer | package peer | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" |  | ||||||
| 	"io" |  | ||||||
| 	"log" | 	"log" | ||||||
| 	"net/http" | 	"net" | ||||||
| 	"net/netip" | 	"net/netip" | ||||||
| 	"net/url" |  | ||||||
| 	"sync/atomic" | 	"sync/atomic" | ||||||
| 	"time" |  | ||||||
| 	"vppn/m" | 	"vppn/m" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var zeroAddrPort netip.AddrPort |  | ||||||
|  |  | ||||||
| type routeInfo struct { |  | ||||||
| 	Up    bool |  | ||||||
| 	Route route |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Router struct { | type Router struct { | ||||||
| 	conf m.PeerConfig | 	conf m.PeerConfig | ||||||
| 	routes [256]*atomic.Pointer[routeInfo] |  | ||||||
|  | 	// Routes used by the peer. | ||||||
|  | 	conns  [256]*connHandler | ||||||
|  | 	routes [256]*atomic.Pointer[route] | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewRouter(conf m.PeerConfig) *Router { | func NewRouter(conf m.PeerConfig, conn *net.UDPConn) *Router { | ||||||
| 	rm := &Router{ | 	r := &Router{conf: conf} | ||||||
| 		conf: conf, |  | ||||||
|  | 	for i := range r.routes { | ||||||
|  | 		r.routes[i] = &atomic.Pointer[route]{} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for i := range rm.routes { | 	_, isServer := netip.AddrFromSlice(conf.PublicIP) | ||||||
| 		rm.routes[i] = &atomic.Pointer[routeInfo]{} |  | ||||||
| 		rm.routes[i].Store(&routeInfo{}) | 	sender := newConnSender(conn, conf.PeerIP, STREAM_ROUTING, conf.SignPrivKey) | ||||||
|  |  | ||||||
|  | 	for i := range r.conns { | ||||||
|  | 		if byte(i) != conf.PeerIP { | ||||||
|  | 			r.conns[i] = newConnHandler( | ||||||
|  | 				isServer, | ||||||
|  | 				byte(i), | ||||||
|  | 				r.routes, | ||||||
|  | 				conf.EncPrivKey, | ||||||
|  | 				newSafeConnSender(sender)) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	go rm.pollHub() | 	go r.pollHub() | ||||||
| 	return rm | 	if !isServer { | ||||||
|  | 		go r.manageMediator() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|  | // Peer Methods | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
| func (rm *Router) GetRoute(ip byte) *route { | func (rm *Router) GetRoute(ip byte) *route { | ||||||
| 	if route := rm.routes[ip].Load(); route != nil && route.Up { | 	return rm.routes[ip].Load() | ||||||
| 		return &route.Route |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (rm *Router) pollHub() { | func (r *Router) HandlePacket(src netip.AddrPort, nonce Nonce, data []byte) { | ||||||
| 	u, err := url.Parse(rm.conf.HubAddress) | 	if nonce.SourceIP == r.conf.PeerIP { | ||||||
| 	if err != nil { | 		log.Printf("Packet to self...") | ||||||
| 		log.Fatalf("Failed to parse hub address %s: %v", rm.conf.HubAddress, err) |  | ||||||
| 	} |  | ||||||
| 	u.Path = "/peer/fetch-state/" |  | ||||||
|  |  | ||||||
| 	client := &http.Client{Timeout: 8 * time.Second} |  | ||||||
|  |  | ||||||
| 	req := &http.Request{ |  | ||||||
| 		Method: http.MethodGet, |  | ||||||
| 		URL:    u, |  | ||||||
| 		Header: http.Header{}, |  | ||||||
| 	} |  | ||||||
| 	req.SetBasicAuth("", rm.conf.APIKey) |  | ||||||
|  |  | ||||||
| 	rm._pollHub(client, req) |  | ||||||
|  |  | ||||||
| 	for range time.Tick(time.Minute) { |  | ||||||
| 		rm._pollHub(client, req) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (rm *Router) _pollHub(client *http.Client, req *http.Request) { |  | ||||||
| 	var state m.NetworkState |  | ||||||
|  |  | ||||||
| 	log.Printf("Fetching peer state from %s...", rm.conf.HubAddress) |  | ||||||
| 	resp, err := client.Do(req) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Printf("Failed to fetch peer state: %v", err) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	body, err := io.ReadAll(resp.Body) |  | ||||||
| 	_ = resp.Body.Close() |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Printf("Failed to read body: %v", err) |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := json.Unmarshal(body, &state); err != nil { | 	switch nonce.PacketType { | ||||||
| 		log.Printf("Failed to unmarshal response from hub: %v", err) | 	case PACKET_TYPE_PING: | ||||||
|  | 		if len(data) < PING_SIZE { | ||||||
|  | 			log.Printf("Short ping request: %d", len(data)) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  | 		w := newWrapper[Ping](src, nonce) | ||||||
|  | 		w.T.Parse(data) | ||||||
|  |  | ||||||
| 	for i, peer := range state.Peers { | 		r.conns[nonce.SourceIP].HandlePing(w) | ||||||
| 		if peer == nil { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		route := rm.routes[i].Load() |  | ||||||
| 		rm.routes[i].Store(rm.updateRoute(route, peer)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (rm *Router) updateRoute(routePtr *routeInfo, peer *m.Peer) *routeInfo { | 	case PACKET_TYPE_PONG: | ||||||
| 	if peer == nil { | 		if len(data) < PONG_SIZE { | ||||||
| 		return &routeInfo{} | 			log.Printf("Short ping response: %d", len(data)) | ||||||
|  | 			return | ||||||
| 		} | 		} | ||||||
|  | 		w := newWrapper[Pong](src, nonce) | ||||||
|  | 		w.T.Parse(data) | ||||||
|  | 		r.conns[nonce.SourceIP].HandlePong(w) | ||||||
|  |  | ||||||
| 	route := *routePtr | 	default: | ||||||
|  | 		log.Printf("Unknown routing packet type: %d", nonce.PacketType) | ||||||
| 	addr, ok := netip.AddrFromSlice(peer.IP) |  | ||||||
| 	if !ok { |  | ||||||
| 		return &routeInfo{} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	route.Up = true |  | ||||||
| 	route.Route.Addr = netip.AddrPortFrom(addr, peer.Port) |  | ||||||
| 	route.Route.Mediator = peer.Mediator |  | ||||||
| 	route.Route.ViaIP = 0 |  | ||||||
| 	if len(route.Route.SignPubKey) == 0 { |  | ||||||
| 		route.Route.SignPubKey = peer.SignPubKey |  | ||||||
| 		route.Route.EncSharedKey = computeSharedKey(peer.EncPubKey, rm.conf.EncPrivKey) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &route |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,22 +0,0 @@ | |||||||
| package peer |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"net/netip" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type route struct { |  | ||||||
| 	Mediator     bool // Route is via a mediator. |  | ||||||
| 	SignPubKey   []byte |  | ||||||
| 	EncSharedKey []byte         // Shared key for encoding / decoding packets. |  | ||||||
| 	Addr         netip.AddrPort // Address to send to. |  | ||||||
| 	ViaIP        byte           // If != 0, this is a forwarding address. |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Nonce struct { |  | ||||||
| 	Counter    uint64 |  | ||||||
| 	SourceIP   byte |  | ||||||
| 	ViaIP      byte |  | ||||||
| 	DestIP     byte |  | ||||||
| 	StreamID   byte // The stream, see STREAM_* constants |  | ||||||
| 	PacketType byte // The packet type. See PACKET_* constants. |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user