{"record":{"id":"3a51f9c3ccfcb3dc","repo":"slackhq/nebula","slug":"invalid-port-d","errorCode":null,"errorMessage":"invalid port: %d","messagePattern":"invalid port: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"calculated_remote.go","lineNumber":31,"sourceCode":")\n\n// This allows us to \"guess\" what the remote might be for a host while we wait\n// for the lighthouse response. See \"lighthouse.calculated_remotes\" in the\n// example config file.\ntype calculatedRemote struct {\n\tipNet netip.Prefix\n\tmask  netip.Prefix\n\tport  uint32\n}\n\nfunc newCalculatedRemote(cidr, maskCidr netip.Prefix, port int) (*calculatedRemote, error) {\n\tif maskCidr.Addr().BitLen() != cidr.Addr().BitLen() {\n\t\treturn nil, fmt.Errorf(\"invalid mask: %s for cidr: %s\", maskCidr, cidr)\n\t}\n\n\tmasked := maskCidr.Masked()\n\tif port < 0 || port > math.MaxUint16 {\n\t\treturn nil, fmt.Errorf(\"invalid port: %d\", port)\n\t}\n\n\treturn &calculatedRemote{\n\t\tipNet: maskCidr,\n\t\tmask:  masked,\n\t\tport:  uint32(port),\n\t}, nil\n}\n\nfunc (c *calculatedRemote) String() string {\n\treturn fmt.Sprintf(\"CalculatedRemote(mask=%v port=%d)\", c.ipNet, c.port)\n}\n\nfunc (c *calculatedRemote) ApplyV4(addr netip.Addr) *V4AddrPort {\n\t// Combine the masked bytes of the \"mask\" IP with the unmasked bytes of the overlay IP\n\tmaskb := net.CIDRMask(c.mask.Bits(), c.mask.Addr().BitLen())\n\tmask := binary.BigEndian.Uint32(maskb[:])\n","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/calculated_remote.go#L13-L49","documentation":"newCalculatedRemote stores the port in a uint32-backed field but the wire format only allows 16-bit ports, so it rejects ports below 0 or above 65535 (math.MaxUint16). This catches out-of-range ports supplied through calculated_remotes config entries.","triggerScenarios":"A calculated_remotes entry whose `port` is negative, exceeds 65535, or a string like \"99999\" parsed via strconv.Atoi then passed to newCalculatedRemote.","commonSituations":"Typo'd port numbers in nebula lighthouse config (e.g. 655360 instead of 65536), negative values from bad templating, or unquoted strings that parse to huge integers.","solutions":["Set the port to a value between 0 and 65535","If the port came from a string, confirm it parses with strconv.Atoi and lands in uint16 range before use","Clamp or validate the port at config-load time in the caller"],"exampleFix":"// before\n- mask: 10.0.0.0/8\n  port: 70000\n// after\n- mask: 10.0.0.0/8\n  port: 4242","handlingStrategy":"validation","validationCode":"func validPort(p any) bool {\n\tswitch v := p.(type) {\n\tcase int:\n\t\treturn v >= 0 && v <= 65535\n\tcase string:\n\t\tn, err := strconv.Atoi(v)\n\t\treturn err == nil && n >= 0 && n <= 65535\n\t}\n\treturn false\n}","typeGuard":"func isUint16Port(n int) bool { return n >= 0 && n <= math.MaxUint16 }","tryCatchPattern":"cr, err := newCalculatedRemote(cidr, maskCidr, port)\nif err != nil {\n\tif strings.HasPrefix(err.Error(), \"invalid port:\") {\n\t\treturn fmt.Errorf(\"port %d out of range 0-65535 for %s\", port, cidr)\n\t}\n\treturn err\n}","preventionTips":["Clamp or range-check ports against math.MaxUint16 at config boundaries","Beware string ports that parse to values >65535 via strconv.Atoi","Use config schema validation with a uint16 port constraint before reload"],"tags":["nebula","config","port","validation","calculated-remotes"],"backgroundTag":"invalid-port-range","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}