Skip to content

Commit be51ebc

Browse files
Add IPv6 key item support to request parser (sonic-net#1449)
**What I did** Add support for parsing IPv6 key items in request parser when the key separator is `:` (e.g. in AppDB). Note: if a key contains an IPv6 address/prefix, it must be the last item in the key to be properly parsed. **Why I did it** Some components that use the request parser are being updated to support IPv6 key items (notably VnetOrch). These components also communicate with AppDB, which uses `:` as the delimiter for key items. Since IPv6 addresses are also separated with `:`, the original parsing logic will fragment an IPv6 address into multiple key items. **How I verified it** Added unit tests to verify correct parsing behavior for the following key formats: * `<string>:<valid ipv6 address or prefix>` * `<string>:<invalid ipv6 address or prefix>` (an exception should be thrown when the parser attempts to construct an `IpAddress` or `IpPrefix` object from the invalid key) * `<empty string>:<valid ipv6 address or prefix>` * `<valid ipv6 address or prefix>` **Details if related** The following 3 conditions must be met before request parser will attempt to parse an IPv6 address: * The key separator being used is `:` (IPv6 addresses can already be parsed with other key separators) * The number of parsed key items is greater than the number of expected key items (this indicates that the `:` within an IPv6 address may have been interpreted as a key separator). * The last key item type is either an IP address or an IP prefix (in any current key formats that contain an IP address, the IP address is always the last member of the key) Signed-off-by: Lawrence Lee <lawlee@microsoft.com>
1 parent 76e2251 commit be51ebc

File tree

2 files changed

+331
-7
lines changed

2 files changed

+331
-7
lines changed

orchagent/request_parser.cpp

+38-7
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,47 @@ void Request::parseKey(const KeyOpFieldsValuesTuple& request)
6060

6161
// split the key by separator
6262
std::vector<std::string> key_items;
63-
size_t f_position = 0;
64-
size_t e_position = full_key_.find(key_separator_);
65-
while (e_position != std::string::npos)
63+
size_t key_item_start = 0;
64+
size_t key_item_end = full_key_.find(key_separator_);
65+
while (key_item_end != std::string::npos)
6666
{
67-
key_items.push_back(full_key_.substr(f_position, e_position - f_position));
68-
f_position = e_position + 1;
69-
e_position = full_key_.find(key_separator_, f_position);
67+
key_items.push_back(full_key_.substr(key_item_start, key_item_end - key_item_start));
68+
key_item_start = key_item_end + 1;
69+
key_item_end = full_key_.find(key_separator_, key_item_start);
7070
}
71-
key_items.push_back(full_key_.substr(f_position, full_key_.length()));
71+
key_items.push_back(full_key_.substr(key_item_start, full_key_.length()));
72+
73+
/*
74+
* Attempt to parse an IPv6 address only if the following conditions are met:
75+
* - The key separator is ":"
76+
* - The above logic will already correctly parse IPv6 addresses using other key separators
77+
* - Special consideration is only needed for ":" key separators since IPv6 addresses also use ":" as the field separator
78+
* - The number of parsed key items exceeds the number of expected key items
79+
* - If we have too many key items and the last key item is supposed to be an IP or prefix, there is a chance that it was an
80+
* IPv6 address that got segmented during parsing
81+
* - The above logic will always break an IPv6 address into at least 2 parts, so if an IPv6 address has been parsed incorrectly it will
82+
* always increase the number of key items
83+
* - The last key item is an IP address or prefix
84+
* - This runs under the assumption that an IPv6 address, if present, will always be the last key item
85+
*/
86+
if (key_separator_ == ':' and
87+
key_items.size() > number_of_key_items_ and
88+
(request_description_.key_item_types.back() == REQ_T_IP or request_description_.key_item_types.back() == REQ_T_IP_PREFIX))
89+
{
90+
// Remove key_items so that key_items.size() is correct, then assemble the removed items into an IPv6 address
91+
std::vector<std::string> ip_addr_groups(--key_items.begin() + number_of_key_items_, key_items.end());
92+
key_items.erase(--key_items.begin() + number_of_key_items_, key_items.end());
93+
94+
std::string ip_string;
95+
96+
for (const auto &i : ip_addr_groups)
97+
{
98+
ip_string += i + ":";
99+
}
100+
ip_string.pop_back(); // remove extra ":" from end of string
72101

102+
key_items.push_back(ip_string);
103+
}
73104
if (key_items.size() != number_of_key_items_)
74105
{
75106
throw std::invalid_argument(std::string("Wrong number of key items. Expected ")

tests/request_parser_ut.cpp

+293
Original file line numberDiff line numberDiff line change
@@ -1298,3 +1298,296 @@ TEST(request_parser, wrong_key_ip_prefix)
12981298
FAIL() << "Expected std::logic_error, not other exception";
12991299
}
13001300
}
1301+
1302+
const request_description_t request_description_ipv6_addr = {
1303+
{ REQ_T_STRING, REQ_T_IP },
1304+
{ },
1305+
{ } // no mandatory attributes
1306+
};
1307+
1308+
class TestRequestIpv6Addr : public Request
1309+
{
1310+
public:
1311+
TestRequestIpv6Addr() : Request(request_description_ipv6_addr, ':') { }
1312+
};
1313+
1314+
const request_description_t request_description_ipv6_prefix = {
1315+
{ REQ_T_STRING, REQ_T_IP_PREFIX },
1316+
{ },
1317+
{ } // no mandatory attributes
1318+
};
1319+
1320+
class TestRequestIpv6Prefix : public Request
1321+
{
1322+
public:
1323+
TestRequestIpv6Prefix() : Request(request_description_ipv6_prefix, ':') { }
1324+
};
1325+
1326+
const request_description_t request_desc_ipv6_addr_only = {
1327+
{ REQ_T_IP },
1328+
{ },
1329+
{ }
1330+
};
1331+
1332+
class TestRequestIpv6AddrOnly: public Request
1333+
{
1334+
public:
1335+
TestRequestIpv6AddrOnly() : Request(request_desc_ipv6_addr_only, ':') { }
1336+
};
1337+
1338+
const request_description_t request_desc_ipv6_prefix_only = {
1339+
{ REQ_T_IP_PREFIX },
1340+
{ },
1341+
{ }
1342+
};
1343+
1344+
class TestRequestIpv6PrefixOnly: public Request
1345+
{
1346+
public:
1347+
TestRequestIpv6PrefixOnly() : Request(request_desc_ipv6_prefix_only, ':') { }
1348+
};
1349+
1350+
std::vector<std::string> ipv6_addresses = {"2001:db8:3c4d:0015:0000:0000:1a2f:1a2b", "2001:db8:3c4d:0015::1a2f:1a2b", "::2001:db8:3c4d:0015:1a2f:1a2b", "2001:db8:3c4d:0015:1a2f:1a2b::", "::"};
1351+
std::vector<std::string> ipv6_addresses_invalid = {"2001:db8:0015:0000:1a2f:1a2b", "5552001:db8:3c4d:0015::1a2f:1a2b", "::2001:zdb8:3c4d:0015:1a2f:1a2b", "2001:db8:3c4d:0015::::1a2f:1aeer2b::"};
1352+
std::vector<std::string> ipv6_prefixes = {"2001:db8:3c4d:0015:0000:0000:1a2f:1a2b/16", "2001:db8:3c4d:0015::1a2f:1a2b/32", "::2001:db8:3c4d:0015:1a2f:1a2b/24", "2001:db8:3c4d:0015:1a2f:1a2b::/8", "::/16"};
1353+
std::vector<std::string> ipv6_prefixes_invalid = {"2001:db8:0015:0000:1a2f:1a2b/16", "5552001:db8:3c4d:0015::1a2f:1a2b/32", "::2001:zdb8:3c4d:0015:1a2f:1a2b/24", "2001:db8:3c4d:0015::::1a2f:1aeer2b::/8"};
1354+
1355+
TEST(request_parser, ipv6_addr_key_item)
1356+
{
1357+
for (const std::string &ipv6_addr : ipv6_addresses)
1358+
{
1359+
std::string key_string = "key1:" + ipv6_addr;
1360+
KeyOpFieldsValuesTuple t {key_string, "SET",
1361+
{ }
1362+
};
1363+
1364+
try
1365+
{
1366+
TestRequestIpv6Addr request;
1367+
1368+
EXPECT_NO_THROW(request.parse(t));
1369+
1370+
EXPECT_STREQ(request.getOperation().c_str(), "SET");
1371+
EXPECT_STREQ(request.getFullKey().c_str(), key_string.c_str());
1372+
EXPECT_STREQ(request.getKeyString(0).c_str(), "key1");
1373+
EXPECT_EQ(request.getKeyIpAddress(1), IpAddress(ipv6_addr));
1374+
EXPECT_FALSE(request.getKeyIpAddress(1).isV4());
1375+
}
1376+
catch (const std::exception& e)
1377+
{
1378+
FAIL() << "Got unexpected exception " << e.what();
1379+
}
1380+
catch (...)
1381+
{
1382+
FAIL() << "Got unexpected exception";
1383+
}
1384+
}
1385+
}
1386+
1387+
TEST(request_parser, ipv6_addr_key_item_empty_str)
1388+
{
1389+
for (const std::string &ipv6_addr : ipv6_addresses)
1390+
{
1391+
std::string key_string = ":" + ipv6_addr;
1392+
KeyOpFieldsValuesTuple t {key_string, "SET",
1393+
{ }
1394+
};
1395+
1396+
try
1397+
{
1398+
TestRequestIpv6Addr request;
1399+
1400+
EXPECT_NO_THROW(request.parse(t));
1401+
1402+
EXPECT_STREQ(request.getOperation().c_str(), "SET");
1403+
EXPECT_STREQ(request.getFullKey().c_str(), key_string.c_str());
1404+
EXPECT_STREQ(request.getKeyString(0).c_str(), "");
1405+
EXPECT_EQ(request.getKeyIpAddress(1), IpAddress(ipv6_addr));
1406+
EXPECT_FALSE(request.getKeyIpAddress(1).isV4());
1407+
}
1408+
catch (const std::exception& e)
1409+
{
1410+
FAIL() << "Got unexpected exception " << e.what();
1411+
}
1412+
catch (...)
1413+
{
1414+
FAIL() << "Got unexpected exception";
1415+
}
1416+
}
1417+
}
1418+
1419+
TEST(request_parser, ipv6_addr_key_item_only)
1420+
{
1421+
for (const std::string &ipv6_addr : ipv6_addresses)
1422+
{
1423+
KeyOpFieldsValuesTuple t {ipv6_addr, "SET",
1424+
{ }
1425+
};
1426+
1427+
try
1428+
{
1429+
TestRequestIpv6AddrOnly request;
1430+
1431+
EXPECT_NO_THROW(request.parse(t));
1432+
1433+
EXPECT_STREQ(request.getOperation().c_str(), "SET");
1434+
EXPECT_STREQ(request.getFullKey().c_str(), ipv6_addr.c_str());
1435+
EXPECT_EQ(request.getKeyIpAddress(0), IpAddress(ipv6_addr));
1436+
EXPECT_FALSE(request.getKeyIpAddress(0).isV4());
1437+
}
1438+
catch (const std::exception& e)
1439+
{
1440+
FAIL() << "Got unexpected exception " << e.what();
1441+
}
1442+
catch (...)
1443+
{
1444+
FAIL() << "Got unexpected exception";
1445+
}
1446+
}
1447+
}
1448+
1449+
TEST(request_parser, ipv6_prefix_key_item)
1450+
{
1451+
for (const std::string &ipv6_prefix : ipv6_prefixes)
1452+
{
1453+
std::string key_string = "key1:" + ipv6_prefix;
1454+
KeyOpFieldsValuesTuple t {key_string, "SET",
1455+
{ }
1456+
};
1457+
1458+
try
1459+
{
1460+
TestRequestIpv6Prefix request;
1461+
1462+
EXPECT_NO_THROW(request.parse(t));
1463+
1464+
EXPECT_STREQ(request.getOperation().c_str(), "SET");
1465+
EXPECT_STREQ(request.getFullKey().c_str(), key_string.c_str());
1466+
EXPECT_STREQ(request.getKeyString(0).c_str(), "key1");
1467+
EXPECT_EQ(request.getKeyIpPrefix(1), IpPrefix(ipv6_prefix));
1468+
EXPECT_FALSE(request.getKeyIpPrefix(1).isV4());
1469+
}
1470+
catch (const std::exception& e)
1471+
{
1472+
FAIL() << "Got unexpected exception " << e.what();
1473+
}
1474+
catch (...)
1475+
{
1476+
FAIL() << "Got unexpected exception";
1477+
}
1478+
}
1479+
}
1480+
1481+
TEST(request_parser, ipv6_prefix_key_item_empty_str)
1482+
{
1483+
for (const std::string &ipv6_prefix: ipv6_prefixes)
1484+
{
1485+
std::string key_string = ":" + ipv6_prefix;
1486+
KeyOpFieldsValuesTuple t {key_string, "SET",
1487+
{ }
1488+
};
1489+
1490+
try
1491+
{
1492+
TestRequestIpv6Prefix request;
1493+
1494+
EXPECT_NO_THROW(request.parse(t));
1495+
1496+
EXPECT_STREQ(request.getOperation().c_str(), "SET");
1497+
EXPECT_STREQ(request.getFullKey().c_str(), key_string.c_str());
1498+
EXPECT_STREQ(request.getKeyString(0).c_str(), "");
1499+
EXPECT_EQ(request.getKeyIpPrefix(1), IpPrefix(ipv6_prefix));
1500+
EXPECT_FALSE(request.getKeyIpPrefix(1).isV4());
1501+
}
1502+
catch (const std::exception& e)
1503+
{
1504+
FAIL() << "Got unexpected exception " << e.what();
1505+
}
1506+
catch (...)
1507+
{
1508+
FAIL() << "Got unexpected exception";
1509+
}
1510+
}
1511+
}
1512+
1513+
TEST(request_parser, ipv6_prefix_key_item_only)
1514+
{
1515+
for (const std::string &ipv6_prefix : ipv6_prefixes)
1516+
{
1517+
KeyOpFieldsValuesTuple t {ipv6_prefix, "SET",
1518+
{ }
1519+
};
1520+
1521+
try
1522+
{
1523+
TestRequestIpv6PrefixOnly request;
1524+
1525+
EXPECT_NO_THROW(request.parse(t));
1526+
1527+
EXPECT_STREQ(request.getOperation().c_str(), "SET");
1528+
EXPECT_STREQ(request.getFullKey().c_str(), ipv6_prefix.c_str());
1529+
EXPECT_EQ(request.getKeyIpPrefix(0), IpPrefix(ipv6_prefix));
1530+
EXPECT_FALSE(request.getKeyIpPrefix(0).isV4());
1531+
}
1532+
catch (const std::exception& e)
1533+
{
1534+
FAIL() << "Got unexpected exception " << e.what();
1535+
}
1536+
catch (...)
1537+
{
1538+
FAIL() << "Got unexpected exception";
1539+
}
1540+
}
1541+
}
1542+
1543+
TEST(request_parser, invalid_ipv6_prefix_key_item)
1544+
{
1545+
for (const std::string &ipv6_prefix : ipv6_prefixes_invalid)
1546+
{
1547+
std::string key_string = "key1:" + ipv6_prefix;
1548+
KeyOpFieldsValuesTuple t {key_string, "SET",
1549+
{ }
1550+
};
1551+
1552+
try
1553+
{
1554+
TestRequestIpv6Prefix request;
1555+
1556+
EXPECT_THROW(request.parse(t), std::invalid_argument);
1557+
}
1558+
catch (const std::exception& e)
1559+
{
1560+
FAIL() << "Got unexpected exception " << e.what();
1561+
}
1562+
catch (...)
1563+
{
1564+
FAIL() << "Got unexpected exception";
1565+
}
1566+
}
1567+
}
1568+
1569+
TEST(request_parser, invalid_ipv6_addr_key_item)
1570+
{
1571+
for (const std::string &ipv6_addr : ipv6_addresses_invalid)
1572+
{
1573+
std::string key_string = "key1:" + ipv6_addr;
1574+
KeyOpFieldsValuesTuple t {key_string, "SET",
1575+
{ }
1576+
};
1577+
1578+
try
1579+
{
1580+
TestRequestIpv6Addr request;
1581+
1582+
EXPECT_THROW(request.parse(t), std::invalid_argument);
1583+
}
1584+
catch (const std::exception& e)
1585+
{
1586+
FAIL() << "Got unexpected exception " << e.what();
1587+
}
1588+
catch (...)
1589+
{
1590+
FAIL() << "Got unexpected exception";
1591+
}
1592+
}
1593+
}

0 commit comments

Comments
 (0)