Loading...
Searching...
No Matches
network-predicate.cpp
Go to the documentation of this file.
1/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2014-2023, Regents of the University of California,
4 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include "network-predicate.hpp"
28#include "core/network.hpp"
29
30#include <fnmatch.h>
31
32#include <boost/lexical_cast.hpp>
33
34namespace nfd::face {
35
40
41void
43{
44 m_whitelist = std::set<std::string>{"*"};
45 m_blacklist.clear();
46}
47
48void
49NetworkPredicateBase::parseList(std::set<std::string>& set,
50 const boost::property_tree::ptree& list,
51 const std::string& section)
52{
53 set.clear();
54
55 for (const auto& item : list) {
56 if (item.first == "*") {
57 // insert wildcard
58 set.insert(item.first);
59 }
60 else {
61 if (!isRuleSupported(item.first)) {
62 NDN_THROW(ConfigFile::Error("Unrecognized rule '" + item.first +
63 "' in section '" + section + "'"));
64 }
65
66 auto value = item.second.get_value<std::string>();
67 if (!isRuleValid(item.first, value)) {
68 NDN_THROW(ConfigFile::Error("Malformed " + item.first + " '" + value +
69 "' in section '" + section + "'"));
70 }
71 set.insert(value);
72 }
73 }
74}
75
76void
77NetworkPredicateBase::parseList(std::set<std::string>& set,
78 std::initializer_list<std::pair<std::string, std::string>> list)
79{
80 set.clear();
81
82 for (const auto& item : list) {
83 if (item.first == "*") {
84 // insert wildcard
85 set.insert(item.first);
86 }
87 else {
88 if (!isRuleSupported(item.first)) {
89 NDN_THROW(std::runtime_error("Unrecognized rule '" + item.first + "'"));
90 }
91
92 if (!isRuleValid(item.first, item.second)) {
93 NDN_THROW(std::runtime_error("Malformed " + item.first + " '" + item.second + "'"));
94 }
95 set.insert(item.second);
96 }
97 }
98}
99
100void
101NetworkPredicateBase::parseWhitelist(const boost::property_tree::ptree& list)
102{
103 parseList(m_whitelist, list, "whitelist");
104}
105
106void
107NetworkPredicateBase::parseBlacklist(const boost::property_tree::ptree& list)
108{
109 parseList(m_blacklist, list, "blacklist");
110}
111
112void
113NetworkPredicateBase::assign(std::initializer_list<std::pair<std::string, std::string>> whitelist,
114 std::initializer_list<std::pair<std::string, std::string>> blacklist)
115{
116 parseList(m_whitelist, whitelist);
117 parseList(m_blacklist, blacklist);
118}
119
120bool
121NetworkInterfacePredicate::isRuleSupported(const std::string& key)
122{
123 return key == "ifname" || key == "ether" || key == "subnet";
124}
125
126bool
127NetworkInterfacePredicate::isRuleValid(const std::string& key, const std::string& value)
128{
129 if (key == "ifname") {
130 // very basic sanity check for interface names
131 return !value.empty();
132 }
133 else if (key == "ether") {
134 // validate ethernet address
135 return !ndn::ethernet::Address::fromString(value).isNull();
136 }
137 else if (key == "subnet") {
138 // example subnet: 10.0.0.0/8
139 return Network::isValidCidr(value);
140 }
141 else {
142 NDN_THROW(std::logic_error("Only supported rules are expected"));
143 }
144}
145
146bool
147IpAddressPredicate::isRuleSupported(const std::string& key)
148{
149 return key == "subnet";
150}
151
152bool
153IpAddressPredicate::isRuleValid(const std::string& key, const std::string& value)
154{
155 if (key == "subnet") {
156 // example subnet: 10.0.0.0/8
157 return Network::isValidCidr(value);
158 }
159 else {
160 NDN_THROW(std::logic_error("Only supported rules are expected"));
161 }
162}
163
164static bool
165doesMatchPattern(const std::string& ifname, const std::string& pattern)
166{
167 // use fnmatch(3) to provide unix glob-style matching for interface names
168 // fnmatch returns 0 if there is a match
169 return ::fnmatch(pattern.data(), ifname.data(), 0) == 0;
170}
171
172static bool
173doesNetifMatchRule(const ndn::net::NetworkInterface& netif, const std::string& rule)
174{
175 // if '/' is in rule, this is a subnet, check if IP in subnet
176 if (rule.find('/') != std::string::npos) {
177 Network n = boost::lexical_cast<Network>(rule);
178 for (const auto& addr : netif.getNetworkAddresses()) {
179 if (n.doesContain(addr.getIp())) {
180 return true;
181 }
182 }
183 }
184
185 return rule == "*" ||
186 doesMatchPattern(netif.getName(), rule) ||
187 netif.getEthernetAddress().toString() == rule;
188}
189
190bool
191NetworkInterfacePredicate::operator()(const ndn::net::NetworkInterface& netif) const
192{
193 return std::any_of(m_whitelist.begin(), m_whitelist.end(),
194 [&netif] (const auto& rule) { return doesNetifMatchRule(netif, rule); }) &&
195 std::none_of(m_blacklist.begin(), m_blacklist.end(),
196 [&netif] (const auto& rule) { return doesNetifMatchRule(netif, rule); });
197}
198
199static bool
200doesAddressMatchRule(const boost::asio::ip::address& address, const std::string& rule)
201{
202 // if '/' is in rule, this is a subnet, check if IP in subnet
203 if (rule.find('/') != std::string::npos) {
204 Network n = boost::lexical_cast<Network>(rule);
205 if (n.doesContain(address)) {
206 return true;
207 }
208 }
209
210 return rule == "*";
211}
212
213bool
214IpAddressPredicate::operator()(const boost::asio::ip::address& address) const
215{
216 return std::any_of(m_whitelist.begin(), m_whitelist.end(),
217 [&address] (const auto& rule) { return doesAddressMatchRule(address, rule); }) &&
218 std::none_of(m_blacklist.begin(), m_blacklist.end(),
219 [&address] (const auto& rule) { return doesAddressMatchRule(address, rule); });
220}
221
222} // namespace nfd::face
static bool isValidCidr(std::string_view cidr) noexcept
Definition network.cpp:66
bool doesContain(const boost::asio::ip::address &address) const noexcept
Definition network.hpp:45
bool operator()(const boost::asio::ip::address &address) const
bool operator()(const ndn::net::NetworkInterface &netif) const
void clear()
Set the whitelist to "*" and clear the blacklist.
void parseWhitelist(const boost::property_tree::ptree &list)
void assign(std::initializer_list< std::pair< std::string, std::string > > whitelist, std::initializer_list< std::pair< std::string, std::string > > blacklist)
std::set< std::string > m_whitelist
void parseBlacklist(const boost::property_tree::ptree &list)
std::set< std::string > m_blacklist
static bool doesNetifMatchRule(const ndn::net::NetworkInterface &netif, const std::string &rule)
static bool doesAddressMatchRule(const boost::asio::ip::address &address, const std::string &rule)
static bool doesMatchPattern(const std::string &ifname, const std::string &pattern)