blob: 340ef8279bf02dba52689d81d31d8c7e4f678879 [file] [log] [blame]
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
Davide Pesaventoe422f9e2022-06-03 01:30:23 -04003 * Copyright (c) 2014-2022, Regents of the University of California,
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -04004 * 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"
Davide Pesavento2cae8ca2019-04-18 20:48:05 -040027#include "common/config-file.hpp"
28#include "core/network.hpp"
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040029
30#include <fnmatch.h>
31
Davide Pesaventoa9b09b62022-06-04 14:07:25 -040032#include <boost/lexical_cast.hpp>
33
Davide Pesaventoe422f9e2022-06-03 01:30:23 -040034namespace nfd::face {
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040035
36NetworkPredicateBase::NetworkPredicateBase()
37{
38 this->clear();
39}
40
41NetworkPredicateBase::~NetworkPredicateBase() = default;
42
43void
44NetworkPredicateBase::clear()
45{
46 m_whitelist = std::set<std::string>{"*"};
47 m_blacklist.clear();
48}
49
50void
Davide Pesavento19779d82019-02-14 13:40:04 -050051NetworkPredicateBase::parseList(std::set<std::string>& set,
52 const boost::property_tree::ptree& list,
53 const std::string& section)
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040054{
55 set.clear();
56
57 for (const auto& item : list) {
58 if (item.first == "*") {
59 // insert wildcard
60 set.insert(item.first);
61 }
62 else {
63 if (!isRuleSupported(item.first)) {
Davide Pesavento19779d82019-02-14 13:40:04 -050064 NDN_THROW(ConfigFile::Error("Unrecognized rule '" + item.first +
65 "' in section '" + section + "'"));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040066 }
67
68 auto value = item.second.get_value<std::string>();
69 if (!isRuleValid(item.first, value)) {
Davide Pesavento19779d82019-02-14 13:40:04 -050070 NDN_THROW(ConfigFile::Error("Malformed " + item.first + " '" + value +
71 "' in section '" + section + "'"));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040072 }
73 set.insert(value);
74 }
75 }
76}
77
78void
Davide Pesavento19779d82019-02-14 13:40:04 -050079NetworkPredicateBase::parseList(std::set<std::string>& set,
80 std::initializer_list<std::pair<std::string, std::string>> list)
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040081{
82 set.clear();
83
84 for (const auto& item : list) {
85 if (item.first == "*") {
86 // insert wildcard
87 set.insert(item.first);
88 }
89 else {
90 if (!isRuleSupported(item.first)) {
Davide Pesavento19779d82019-02-14 13:40:04 -050091 NDN_THROW(std::runtime_error("Unrecognized rule '" + item.first + "'"));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040092 }
93
94 if (!isRuleValid(item.first, item.second)) {
Davide Pesavento19779d82019-02-14 13:40:04 -050095 NDN_THROW(std::runtime_error("Malformed " + item.first + " '" + item.second + "'"));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040096 }
97 set.insert(item.second);
98 }
99 }
100}
101
102void
103NetworkPredicateBase::parseWhitelist(const boost::property_tree::ptree& list)
104{
105 parseList(m_whitelist, list, "whitelist");
106}
107
108void
109NetworkPredicateBase::parseBlacklist(const boost::property_tree::ptree& list)
110{
111 parseList(m_blacklist, list, "blacklist");
112}
113
114void
115NetworkPredicateBase::assign(std::initializer_list<std::pair<std::string, std::string>> whitelist,
116 std::initializer_list<std::pair<std::string, std::string>> blacklist)
117{
118 parseList(m_whitelist, whitelist);
119 parseList(m_blacklist, blacklist);
120}
121
122bool
123NetworkInterfacePredicate::isRuleSupported(const std::string& key)
124{
125 return key == "ifname" || key == "ether" || key == "subnet";
126}
127
128bool
129NetworkInterfacePredicate::isRuleValid(const std::string& key, const std::string& value)
130{
131 if (key == "ifname") {
132 // very basic sanity check for interface names
133 return !value.empty();
134 }
135 else if (key == "ether") {
136 // validate ethernet address
137 return !ndn::ethernet::Address::fromString(value).isNull();
138 }
139 else if (key == "subnet") {
140 // example subnet: 10.0.0.0/8
141 return Network::isValidCidr(value);
142 }
143 else {
Davide Pesavento19779d82019-02-14 13:40:04 -0500144 NDN_THROW(std::logic_error("Only supported rules are expected"));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -0400145 }
146}
147
148bool
149IpAddressPredicate::isRuleSupported(const std::string& key)
150{
151 return key == "subnet";
152}
153
154bool
155IpAddressPredicate::isRuleValid(const std::string& key, const std::string& value)
156{
157 if (key == "subnet") {
158 // example subnet: 10.0.0.0/8
159 return Network::isValidCidr(value);
160 }
161 else {
Davide Pesavento19779d82019-02-14 13:40:04 -0500162 NDN_THROW(std::logic_error("Only supported rules are expected"));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -0400163 }
164}
165
166bool
167NetworkPredicateBase::operator==(const NetworkPredicateBase& other) const
168{
169 return this->m_whitelist == other.m_whitelist &&
170 this->m_blacklist == other.m_blacklist;
171}
172
173static bool
174doesMatchPattern(const std::string& ifname, const std::string& pattern)
175{
176 // use fnmatch(3) to provide unix glob-style matching for interface names
177 // fnmatch returns 0 if there is a match
178 return ::fnmatch(pattern.data(), ifname.data(), 0) == 0;
179}
180
181static bool
182doesNetifMatchRule(const ndn::net::NetworkInterface& netif, const std::string& rule)
183{
184 // if '/' is in rule, this is a subnet, check if IP in subnet
185 if (rule.find('/') != std::string::npos) {
186 Network n = boost::lexical_cast<Network>(rule);
187 for (const auto& addr : netif.getNetworkAddresses()) {
188 if (n.doesContain(addr.getIp())) {
189 return true;
190 }
191 }
192 }
193
194 return rule == "*" ||
195 doesMatchPattern(netif.getName(), rule) ||
196 netif.getEthernetAddress().toString() == rule;
197}
198
199bool
200NetworkInterfacePredicate::operator()(const ndn::net::NetworkInterface& netif) const
201{
Davide Pesavento412c9822021-07-02 00:21:05 -0400202 return std::any_of(m_whitelist.begin(), m_whitelist.end(),
203 [&netif] (const auto& rule) { return doesNetifMatchRule(netif, rule); }) &&
204 std::none_of(m_blacklist.begin(), m_blacklist.end(),
205 [&netif] (const auto& rule) { return doesNetifMatchRule(netif, rule); });
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -0400206}
207
208static bool
209doesAddressMatchRule(const boost::asio::ip::address& address, const std::string& rule)
210{
211 // if '/' is in rule, this is a subnet, check if IP in subnet
212 if (rule.find('/') != std::string::npos) {
213 Network n = boost::lexical_cast<Network>(rule);
214 if (n.doesContain(address)) {
215 return true;
216 }
217 }
218
219 return rule == "*";
220}
221
222bool
223IpAddressPredicate::operator()(const boost::asio::ip::address& address) const
224{
Davide Pesavento412c9822021-07-02 00:21:05 -0400225 return std::any_of(m_whitelist.begin(), m_whitelist.end(),
226 [&address] (const auto& rule) { return doesAddressMatchRule(address, rule); }) &&
227 std::none_of(m_blacklist.begin(), m_blacklist.end(),
228 [&address] (const auto& rule) { return doesAddressMatchRule(address, rule); });
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -0400229}
230
Davide Pesaventoe422f9e2022-06-03 01:30:23 -0400231} // namespace nfd::face