{"record":{"id":"e66b4d77a06d3945","repo":"netbirdio/netbird","slug":"add-legacy-routing-rule-w-e66b4d","errorCode":null,"errorMessage":"add legacy routing rule: %w","messagePattern":"add legacy routing rule: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/nftables/router_linux.go","lineNumber":690,"sourceCode":"\t\treturn fmt.Errorf(\"delete rule %s: %w\", ruleKey, err)\n\t}\n\tdelete(r.rules, ruleKey)\n\n\tlog.Debugf(\"removed route rule %s\", ruleKey)\n\n\treturn nil\n}\n\n// AddNatRule appends a nftables rule pair to the nat chain\nfunc (r *router) AddNatRule(pair firewall.RouterPair) error {\n\tif err := r.refreshRulesMap(); err != nil {\n\t\treturn fmt.Errorf(refreshRulesMapError, err)\n\t}\n\n\tif r.legacyManagement {\n\t\tlog.Warnf(\"This peer is connected to a NetBird Management service with an older version. Allowing all traffic for %s\", pair.Destination)\n\t\tif err := r.addLegacyRouteRule(pair); err != nil {\n\t\t\treturn fmt.Errorf(\"add legacy routing rule: %w\", err)\n\t\t}\n\t}\n\n\tif pair.Masquerade {\n\t\tif err := r.addNatRule(pair); err != nil {\n\t\t\treturn fmt.Errorf(\"add nat rule: %w\", err)\n\t\t}\n\n\t\tif err := r.addNatRule(firewall.GetInversePair(pair)); err != nil {\n\t\t\treturn fmt.Errorf(\"add inverse nat rule: %w\", err)\n\t\t}\n\t}\n\n\tif err := r.conn.Flush(); err != nil {\n\t\tr.rollbackRules(pair)\n\t\treturn fmt.Errorf(\"insert rules for %s: %w\", pair.Destination, err)\n\t}\n","sourceCodeStart":672,"sourceCodeEnd":708,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/nftables/router_linux.go#L672-L708","documentation":"Returned by AddNatRule (router_linux.go:689) when addLegacyRouteRule fails. Legacy mode is enabled via SetLegacyManagement for management servers predating route ACLs (the preceding log.Warnf says all traffic is allowed for the destination), and addLegacyRouteRule (line 968) builds an unconditional ACCEPT forwarding rule in netbird-rt-fwd. The wrapped error is one of: applyNetwork source/destination failure (errors 697/698, i.e. ipset creation failing) or removeLegacyRouteRule failure (error 699) while replacing an existing rule.","triggerScenarios":"A NetBird peer managed by an old management version receiving a routed network whose source or destination expands into a prefix set that fails to create (>1500 prefixes, overlapping intervals, EEXIST leftovers), or re-applying a legacy route whose previous flush failed leaving a handle-less rule.","commonSituations":"Mixed-version fleets where a modern agent talks to a pre-route-ACL management; migrations where legacy rules are re-added on every network-map update; large routing ranges hitting set limits in legacy mode.","solutions":["Fix the underlying cause using errors 697/698/699 guidance — this wrapper only re-labels those failures.","Upgrade the management service so the agent leaves legacy mode (GetLegacyManagement()/SetLegacyManagement), replacing the allow-all rule with proper ACL enforcement.","Reduce the routed network's prefix count (merge ranges) so set creation succeeds.","Restart the agent to clear stale handle-less rules if 699 keeps firing."],"exampleFix":"// before\nif r.legacyManagement {\n    if err := r.addLegacyRouteRule(pair); err != nil {\n        return fmt.Errorf(\"add legacy routing rule: %w\", err)\n    }\n}\n\n// after\nif r.legacyManagement {\n    if err := r.addLegacyRouteRule(pair); err != nil {\n        // legacy allow-all is best-effort compat; surface cause but keep NAT path alive\n        log.Errorf(\"add legacy routing rule for %s: %v\", pair.Destination, err)\n        if !isErrno(err, unix.ENOENT, unix.EEXIST) {\n            return fmt.Errorf(\"add legacy routing rule: %w\", err)\n        }\n    }\n}","handlingStrategy":"validation","validationCode":"// Validate the pair before entering legacy mode rule creation\nif r.legacyManagement {\n    if p := pair.Source.Prefix; p.IsValid() && p.Bits() > 0 || pair.Source.IsSet() {\n        // ok\n    } else {\n        return fmt.Errorf(\"invalid legacy source network %v\", pair.Source)\n    }\n}","typeGuard":"func isApplyNetworkErr(err error) bool {\n\treturn strings.Contains(err.Error(), \"apply \") || strings.Contains(err.Error(), \"ipset\")\n}","tryCatchPattern":"if err := r.addLegacyRouteRule(pair); err != nil {\n    if isApplyNetworkErr(err) && isErrno(err, unix.EEXIST, unix.ENOENT) {\n        // set-state issue; refresh and one retry usually clears it\n        _ = r.refreshRulesMap()\n        if rerr := r.addLegacyRouteRule(pair); rerr == nil {\n            return nil\n        }\n    }\n    return fmt.Errorf(\"add legacy routing rule: %w\", err)\n}","preventionTips":["Upgrade management past the route-ACL era so legacy mode is never enabled (SetLegacyManagement stays false).","Keep legacy route networks small and merged; legacy rules share the same set machinery as ACLs.","Log loudly when legacy mode is active — allow-all forwarding deserves visibility.","Test mixed-version fleets (new agent + old management) in CI to catch legacy path regressions."],"tags":["go","nftables","firewall","legacy","routing","compatibility"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}