added --p2p.allowed-port flag (#6263)

Regarding https://github.com/ledgerwatch/erigon/issues/6260

added flag `--p2p.allowed-ports=<porta>,<portb>` to restrict which ports
to use for sentries for different protocol versions.

Default for this flag is `30303, 30304` (first port is inherited from
`--port` flag defaults.
If `--port` is changed and it's new value is not presented in allowed
port list, provided port will be allowed as well as list provided via
`--p2p.allowed-ports`

Port picking is straightforward, we create sentry gRPC server for
protocol over first allowed port that is not already taken.
If there are no allowed ports left, erigon exits with hint.
This commit is contained in:
awskii 2022-12-12 13:25:47 +00:00 committed by GitHub
parent c0d2523b06
commit a124bcf6ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 66 additions and 1 deletions

View File

@ -28,6 +28,7 @@ var (
discoveryDNS []string
nodiscover bool // disable sentry's discovery mechanism
protocol uint
allowedPorts []uint
netRestrict string // CIDR to restrict peering to
maxPeers int
maxPendPeers int
@ -46,6 +47,7 @@ func init() {
rootCmd.Flags().StringSliceVar(&discoveryDNS, utils.DNSDiscoveryFlag.Name, []string{}, utils.DNSDiscoveryFlag.Usage)
rootCmd.Flags().BoolVar(&nodiscover, utils.NoDiscoverFlag.Name, false, utils.NoDiscoverFlag.Usage)
rootCmd.Flags().UintVar(&protocol, utils.P2pProtocolVersionFlag.Name, utils.P2pProtocolVersionFlag.Value.Value()[0], utils.P2pProtocolVersionFlag.Usage)
rootCmd.Flags().UintSliceVar(&allowedPorts, utils.P2pProtocolAllowedPorts.Name, utils.P2pProtocolAllowedPorts.Value.Value(), utils.P2pProtocolAllowedPorts.Usage)
rootCmd.Flags().StringVar(&netRestrict, utils.NetrestrictFlag.Name, utils.NetrestrictFlag.Value, utils.NetrestrictFlag.Usage)
rootCmd.Flags().IntVar(&maxPeers, utils.MaxPeersFlag.Name, utils.MaxPeersFlag.Value, utils.MaxPeersFlag.Usage)
rootCmd.Flags().IntVar(&maxPendPeers, utils.MaxPendingPeersFlag.Name, utils.MaxPendingPeersFlag.Value, utils.MaxPendingPeersFlag.Usage)
@ -82,6 +84,7 @@ var rootCmd = &cobra.Command{
trustedPeers,
uint(port),
protocol,
allowedPorts,
)
if err != nil {
return err

View File

@ -478,6 +478,11 @@ var (
Usage: "Version of eth p2p protocol",
Value: cli.NewUintSlice(nodecfg.DefaultConfig.P2P.ProtocolVersion...),
}
P2pProtocolAllowedPorts = cli.UintSliceFlag{
Name: "p2p.allowed-ports",
Usage: "Allowed ports to pick for different eth p2p protocol versions as follows <porta>,<portb>,..,<porti>",
Value: cli.NewUintSlice(uint(ListenPortFlag.Value), 30304, 30305, 30306, 30307),
}
SentryAddrFlag = cli.StringFlag{
Name: "sentry.api.addr",
Usage: "comma separated sentry addresses '<host>:<port>,<host>:<port>'",
@ -850,6 +855,7 @@ func NewP2PConfig(
trustedPeers []string,
port,
protocol uint,
allowedPorts []uint,
) (*p2p.Config, error) {
var enodeDBPath string
switch protocol {
@ -876,6 +882,7 @@ func NewP2PConfig(
Name: nodeName,
Log: log.New(),
NodeDatabase: enodeDBPath,
AllowedPorts: allowedPorts,
}
if netRestrict != "" {
cfg.NetRestrict = new(netutil.Netlist)
@ -923,6 +930,26 @@ func setListenAddress(ctx *cli.Context, cfg *p2p.Config) {
if ctx.IsSet(SentryAddrFlag.Name) {
cfg.SentryAddr = SplitAndTrim(ctx.String(SentryAddrFlag.Name))
}
// TODO cli lib doesn't store defaults for UintSlice properly so we have to get value directly
cfg.AllowedPorts = P2pProtocolAllowedPorts.Value.Value()
if ctx.IsSet(P2pProtocolAllowedPorts.Name) {
cfg.AllowedPorts = ctx.UintSlice(P2pProtocolAllowedPorts.Name)
}
if ctx.IsSet(ListenPortFlag.Name) {
// add non-default port to allowed port list
lp := ctx.Int(ListenPortFlag.Name)
found := false
for _, p := range cfg.AllowedPorts {
if int(p) == lp {
found = true
break
}
}
if !found {
cfg.AllowedPorts = append([]uint{uint(lp)}, cfg.AllowedPorts...)
}
}
}
// setNAT creates a port mapper from command line flags.

View File

@ -23,6 +23,7 @@ import (
"fmt"
"io/fs"
"math/big"
"net"
"os"
"path/filepath"
"strconv"
@ -316,11 +317,31 @@ func New(stack *node.Node, config *ethconfig.Config, logger log.Logger) (*Ethere
if err != nil {
return nil, err
}
var pi int // points to next port to be picked from refCfg.AllowedPorts
for _, protocol := range refCfg.ProtocolVersion {
cfg := refCfg
cfg.NodeDatabase = filepath.Join(stack.Config().Dirs.Nodes, eth.ProtocolToString[protocol])
// pick port from allowed list
var picked bool
for ; pi < len(refCfg.AllowedPorts) && !picked; pi++ {
pc := int(refCfg.AllowedPorts[pi])
if !checkPortIsFree(fmt.Sprintf("%s:%d", listenHost, pc)) {
log.Warn("bind protocol to port has failed: port is busy", "protocol", fmt.Sprintf("eth/%d", protocol), "port", pc)
continue
}
if listenPort != pc {
listenPort = pc
}
pi++
picked = true
}
if !picked {
return nil, fmt.Errorf("run out of allowed ports for p2p eth protocols %v. Extend allowed port list via --p2p.allowed-ports", cfg.AllowedPorts)
}
cfg.ListenAddr = fmt.Sprintf("%s:%d", listenHost, listenPort)
listenPort++
server := sentry.NewGrpcServer(backend.sentryCtx, discovery, readNodeInfo, &cfg, protocol)
backend.sentryServers = append(backend.sentryServers, server)
@ -1066,3 +1087,12 @@ func RemoveContents(dir string) error {
}
return nil
}
func checkPortIsFree(addr string) (free bool) {
c, err := net.DialTimeout("tcp", addr, 200*time.Millisecond)
if err != nil {
return true
}
c.Close()
return false
}

View File

@ -139,6 +139,10 @@ type Config struct {
// the server is started.
ListenAddr string
// AllowedPorts is list of ports allowed to pick to create Listener on it (see ListenAddr)
// for different protocol versions
AllowedPorts []uint
// eth/66, eth/67, etc
ProtocolVersion []uint

View File

@ -91,6 +91,7 @@ var DefaultFlags = []cli.Flag{
&utils.TorrentVerbosityFlag,
&utils.ListenPortFlag,
&utils.P2pProtocolVersionFlag,
&utils.P2pProtocolAllowedPorts,
&utils.NATFlag,
&utils.NoDiscoverFlag,
&utils.DiscoveryV5Flag,