Skip to content

Commit 6ce6c05

Browse files
bgp global command fixes
- Extract the value parsers (parseBool/parseInt/parseNonNegInt32, handler Result) into a shared BgpCliValueParsers.h so sibling BGP dispatchers can reuse them, and add a bounded parseAsn4Byte: local-asn/confed-asn accepted any uint64 and silently persisted out-of-range ASNs (>= 2^32 wrap the i64 field negative). - positionals_at_end() on the global command: CLI11's parent-chain subcommand fallthrough steals value tokens that match a sibling command name (e.g. a policy named "peer-group") and misparses the command. - Integration test base: probe the bgpd unit, and pass -c safe.directory=/etc/coop on the raw git invocations (gitHead / bgpTrackedAtRevision), mirroring the CLI's Git class -- /etc/coop is owned by another user (e.g. coop) on provisioned devices, which git otherwise rejects as dubious ownership and the helpers silently return empty results. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 00c725d commit 6ce6c05

8 files changed

Lines changed: 188 additions & 96 deletions

File tree

cmake/CliFboss2.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,7 @@ add_library(fboss2_config_lib
844844
fboss/cli/fboss2/commands/config/mac/aging_time/CmdConfigMacAgingTime.h
845845
fboss/cli/fboss2/commands/config/protocol/CmdConfigProtocol.cpp
846846
fboss/cli/fboss2/commands/config/protocol/CmdConfigProtocol.h
847+
fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h
847848
fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.cpp
848849
fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h
849850
fboss/cli/fboss2/commands/config/protocol/bgp/CmdConfigProtocolBgp.cpp

fboss/cli/fboss2/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,7 @@ cpp_library(
11971197
"commands/config/mac/CmdConfigMac.h",
11981198
"commands/config/mac/aging_time/CmdConfigMacAgingTime.h",
11991199
"commands/config/protocol/CmdConfigProtocol.h",
1200+
"commands/config/protocol/bgp/BgpCliValueParsers.h",
12001201
"commands/config/protocol/bgp/BgpConfigSession.h",
12011202
"commands/config/protocol/bgp/CmdConfigProtocolBgp.h",
12021203
"commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h",
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright (c) 2004-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
*/
10+
11+
#pragma once
12+
13+
#include <folly/Conv.h>
14+
#include <cstdint>
15+
#include <exception>
16+
#include <limits>
17+
#include <optional>
18+
#include <string>
19+
#include <utility>
20+
21+
/**
22+
* Value parsing helpers shared by the BGP config dispatchers
23+
* (`config protocol bgp global` / `config protocol bgp neighbor`).
24+
*
25+
* All parsers return std::nullopt on invalid input instead of throwing so a
26+
* handler can reject the value with a user-facing message and leave the
27+
* session unpersisted.
28+
*/
29+
namespace facebook::fboss::bgpcli {
30+
31+
// Outcome of an attribute handler: on failure the message is returned to the
32+
// user and the session is NOT persisted, so a rejected value never lands on
33+
// disk.
34+
struct Result {
35+
bool ok;
36+
std::string message;
37+
};
38+
39+
inline Result ok(std::string message) {
40+
return Result{true, std::move(message)};
41+
}
42+
43+
inline Result err(std::string message) {
44+
return Result{false, std::move(message)};
45+
}
46+
47+
inline std::optional<bool> parseBool(const std::string& value) {
48+
if (value == "true" || value == "1" || value == "yes") {
49+
return true;
50+
}
51+
if (value == "false" || value == "0" || value == "no") {
52+
return false;
53+
}
54+
return std::nullopt;
55+
}
56+
57+
template <typename T>
58+
std::optional<T> parseInt(const std::string& value) {
59+
try {
60+
return folly::to<T>(value);
61+
} catch (const std::exception&) {
62+
return std::nullopt;
63+
}
64+
}
65+
66+
// Parse a non-negative value that must fit in int32 (used for second-valued
67+
// timers and min-routes).
68+
inline std::optional<int32_t> parseNonNegInt32(const std::string& value) {
69+
auto parsed = parseInt<int64_t>(value);
70+
if (!parsed || *parsed < 0 || *parsed > std::numeric_limits<int32_t>::max()) {
71+
return std::nullopt;
72+
}
73+
return static_cast<int32_t>(*parsed);
74+
}
75+
76+
// Parse a non-negative int64 (used for route limits).
77+
inline std::optional<int64_t> parseNonNegInt64(const std::string& value) {
78+
auto parsed = parseInt<int64_t>(value);
79+
if (!parsed || *parsed < 0) {
80+
return std::nullopt;
81+
}
82+
return parsed;
83+
}
84+
85+
// Parse a 4-byte ASN (RFC 6793): an unsigned value in [0, 2^32-1]. The thrift
86+
// fields are i64, so an unchecked uint64 parse would let an out-of-range ASN
87+
// wrap or exceed the protocol range and be persisted.
88+
inline std::optional<int64_t> parseAsn4Byte(const std::string& value) {
89+
auto parsed = parseInt<uint64_t>(value);
90+
if (!parsed || *parsed > std::numeric_limits<uint32_t>::max()) {
91+
return std::nullopt;
92+
}
93+
return static_cast<int64_t>(*parsed);
94+
}
95+
96+
} // namespace facebook::fboss::bgpcli

fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.cpp

Lines changed: 30 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,23 @@
1313
#include "fboss/cli/fboss2/CmdHandler.cpp"
1414

1515
#include <fmt/core.h>
16-
#include <folly/Conv.h>
1716
#include <neteng/fboss/bgp/public_tld/configerator/structs/neteng/fboss/bgp/gen-cpp2/bgp_config_types.h>
17+
#include <cstddef>
1818
#include <cstdint>
1919
#include <functional>
20+
#include <iostream>
2021
#include <limits>
2122
#include <map>
22-
#include <optional>
23+
#include <ostream>
24+
#include <stdexcept>
2325
#include <string>
2426
#include <string_view>
2527
#include <vector>
28+
#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h"
2629
#include "fboss/cli/fboss2/session/ConfigSession.h"
30+
#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h"
31+
#include "fboss/cli/fboss2/utils/HostInfo.h"
32+
#include "fmt/format.h"
2733

2834
namespace facebook::fboss {
2935

@@ -49,53 +55,14 @@ constexpr std::string_view kRibAllocatedPathIds = "rib-allocated-path-ids";
4955

5056
using Tokens = std::vector<std::string>;
5157
using BgpConfig = bgp::thrift::BgpConfig;
52-
53-
// Outcome of an attribute handler: on failure the message is returned to the
54-
// user and the session is NOT persisted, so a rejected value never lands on
55-
// disk.
56-
struct Result {
57-
bool ok;
58-
std::string message;
59-
};
60-
61-
Result ok(std::string message) {
62-
return Result{true, std::move(message)};
63-
}
64-
65-
Result err(std::string message) {
66-
return Result{false, std::move(message)};
67-
}
68-
69-
// ---- value parsers (modular, shared across attributes) --------------------
70-
71-
std::optional<bool> parseBool(const std::string& value) {
72-
if (value == "true" || value == "1" || value == "yes") {
73-
return true;
74-
}
75-
if (value == "false" || value == "0" || value == "no") {
76-
return false;
77-
}
78-
return std::nullopt;
79-
}
80-
81-
template <typename T>
82-
std::optional<T> parseInt(const std::string& value) {
83-
try {
84-
return folly::to<T>(value);
85-
} catch (const std::exception&) {
86-
return std::nullopt;
87-
}
88-
}
89-
90-
// Parse a non-negative value that must fit in int32 (used for second-valued
91-
// timers and min-routes).
92-
std::optional<int32_t> parseNonNegInt32(const std::string& value) {
93-
auto parsed = parseInt<int64_t>(value);
94-
if (!parsed || *parsed < 0 || *parsed > std::numeric_limits<int32_t>::max()) {
95-
return std::nullopt;
96-
}
97-
return static_cast<int32_t>(*parsed);
98-
}
58+
// Value parsers and the handler Result type are shared with the neighbor
59+
// dispatcher (see BgpCliValueParsers.h).
60+
using bgpcli::err;
61+
using bgpcli::ok;
62+
using bgpcli::parseBool;
63+
using bgpcli::parseInt;
64+
using bgpcli::parseNonNegInt32;
65+
using bgpcli::Result;
9966

10067
// ---- per-attribute handlers ------------------------------------------------
10168
// Each handler mutates the typed bgp::thrift::BgpConfig directly. Field names
@@ -113,23 +80,31 @@ Result applyLocalAsn(BgpConfig& cfg, const Tokens& values) {
11380
if (values.size() != 1) {
11481
return err("Error: local-asn requires <asn>");
11582
}
116-
auto asn = parseInt<uint64_t>(values[0]);
83+
auto asn = bgpcli::parseAsn4Byte(values[0]);
11784
if (!asn) {
118-
return err(fmt::format("Error: Invalid local-asn value '{}'", values[0]));
85+
return err(
86+
fmt::format(
87+
"Error: Invalid local-asn value '{}'; expected an unsigned "
88+
"4-byte ASN",
89+
values[0]));
11990
}
120-
cfg.local_as_4_byte() = static_cast<int64_t>(*asn);
91+
cfg.local_as_4_byte() = *asn;
12192
return ok(fmt::format("Successfully set BGP local-asn to: {}", *asn));
12293
}
12394

12495
Result applyConfedAsn(BgpConfig& cfg, const Tokens& values) {
12596
if (values.size() != 1) {
12697
return err("Error: confed-asn requires <asn>");
12798
}
128-
auto asn = parseInt<uint64_t>(values[0]);
99+
auto asn = bgpcli::parseAsn4Byte(values[0]);
129100
if (!asn) {
130-
return err(fmt::format("Error: Invalid confed-asn value '{}'", values[0]));
101+
return err(
102+
fmt::format(
103+
"Error: Invalid confed-asn value '{}'; expected an unsigned "
104+
"4-byte ASN",
105+
values[0]));
131106
}
132-
cfg.local_confed_as_4_byte() = static_cast<int64_t>(*asn);
107+
cfg.local_confed_as_4_byte() = *asn;
133108
return ok(
134109
fmt::format("Successfully set BGP confederation AS number to: {}", *asn));
135110
}

fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@
1010

1111
#pragma once
1212

13+
#include <string>
14+
#include <vector>
15+
#include "CLI/App.hpp"
1316
#include "fboss/cli/fboss2/CmdHandler.h"
1417
#include "fboss/cli/fboss2/commands/config/protocol/bgp/CmdConfigProtocolBgp.h"
1518
#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h"
19+
#include "fboss/cli/fboss2/utils/HostInfo.h"
1620

1721
namespace facebook::fboss {
1822

@@ -43,6 +47,10 @@ class BgpGlobalConfig : public utils::BaseObjectArgType<std::string> {
4347
struct CmdConfigProtocolBgpGlobalTraits : public WriteCommandTraits {
4448
using ParentCmd = CmdConfigProtocolBgp;
4549
static void addCliArg(CLI::App& cmd, std::vector<std::string>& args) {
50+
// Stop CLI11's parent-chain subcommand fallthrough from stealing value
51+
// tokens that happen to match a sibling command name (e.g. a policy
52+
// named "peer-group"); see CmdConfigProtocolBgpNeighborTraits.
53+
cmd.positionals_at_end();
4654
cmd.add_option("args", args, "<attribute> <value> [value ...]");
4755
}
4856
using ObjectArgType = BgpGlobalConfig;

fboss/cli/fboss2/test/integration_test/ConfigBgpGlobalTest.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Scope: the BGP *global* tunables only. Each positive test stages the change
77
* AND commits it, then asserts the value landed at the correct thrift field
88
* path in the promoted system config (/etc/coop/bgpcpp/bgpcpp.conf) that the
9-
* bgp_pp daemon consumes. Session-lifecycle behavior (clear / diff / rollback /
9+
* bgpd daemon consumes. Session-lifecycle behavior (clear / diff / rollback /
1010
* commit-restart mechanics) lives in ConfigBgpSessionTest.
1111
*
1212
* - count-confeds-in-as-path-len <true|false>
@@ -19,7 +19,7 @@
1919
* Requirements:
2020
* - The fboss2-dev binary under test (config subcommand tree).
2121
* - HOME is set (the session file lives under $HOME/.fboss2).
22-
* - bgp_pp is installed/active (commit restarts it).
22+
* - bgpd is installed/active (commit restarts it).
2323
*/
2424

2525
#include <gtest/gtest.h>

0 commit comments

Comments
 (0)