blob: 07de63c2edbcd1410769b655f992fcce0ede4061 [file] [log] [blame]
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
Davide Pesavento412c9822021-07-02 00:21:05 -04003 * Copyright (c) 2014-2021, 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
32namespace nfd {
Davide Pesavento2cae8ca2019-04-18 20:48:05 -040033namespace face {
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040034
35NetworkPredicateBase::NetworkPredicateBase()
36{
37 this->clear();
38}
39
40NetworkPredicateBase::~NetworkPredicateBase() = default;
41
42void
43NetworkPredicateBase::clear()
44{
45 m_whitelist = std::set<std::string>{"*"};
46 m_blacklist.clear();
47}
48
49void
Davide Pesavento19779d82019-02-14 13:40:04 -050050NetworkPredicateBase::parseList(std::set<std::string>& set,
51 const boost::property_tree::ptree& list,
52 const std::string& section)
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040053{
54 set.clear();
55
56 for (const auto& item : list) {
57 if (item.first == "*") {
58 // insert wildcard
59 set.insert(item.first);
60 }
61 else {
62 if (!isRuleSupported(item.first)) {
Davide Pesavento19779d82019-02-14 13:40:04 -050063 NDN_THROW(ConfigFile::Error("Unrecognized rule '" + item.first +
64 "' in section '" + section + "'"));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040065 }
66
67 auto value = item.second.get_value<std::string>();
68 if (!isRuleValid(item.first, value)) {
Davide Pesavento19779d82019-02-14 13:40:04 -050069 NDN_THROW(ConfigFile::Error("Malformed " + item.first + " '" + value +
70 "' in section '" + section + "'"));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040071 }
72 set.insert(value);
73 }
74 }
75}
76
77void
Davide Pesavento19779d82019-02-14 13:40:04 -050078NetworkPredicateBase::parseList(std::set<std::string>& set,
79 std::initializer_list<std::pair<std::string, std::string>> list)
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040080{
81 set.clear();
82
83 for (const auto& item : list) {
84 if (item.first == "*") {
85 // insert wildcard
86 set.insert(item.first);
87 }
88 else {
89 if (!isRuleSupported(item.first)) {
Davide Pesavento19779d82019-02-14 13:40:04 -050090 NDN_THROW(std::runtime_error("Unrecognized rule '" + item.first + "'"));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040091 }
92
93 if (!isRuleValid(item.first, item.second)) {
Davide Pesavento19779d82019-02-14 13:40:04 -050094 NDN_THROW(std::runtime_error("Malformed " + item.first + " '" + item.second + "'"));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -040095 }
96 set.insert(item.second);
97 }
98 }
99}
100
101void
102NetworkPredicateBase::parseWhitelist(const boost::property_tree::ptree& list)
103{
104 parseList(m_whitelist, list, "whitelist");
105}
106
107void
108NetworkPredicateBase::parseBlacklist(const boost::property_tree::ptree& list)
109{
110 parseList(m_blacklist, list, "blacklist");
111}
112
113void
114NetworkPredicateBase::assign(std::initializer_list<std::pair<std::string, std::string>> whitelist,
115 std::initializer_list<std::pair<std::string, std::string>> blacklist)
116{
117 parseList(m_whitelist, whitelist);
118 parseList(m_blacklist, blacklist);
119}
120
121bool
122NetworkInterfacePredicate::isRuleSupported(const std::string& key)
123{
124 return key == "ifname" || key == "ether" || key == "subnet";
125}
126
127bool
128NetworkInterfacePredicate::isRuleValid(const std::string& key, const std::string& value)
129{
130 if (key == "ifname") {
131 // very basic sanity check for interface names
132 return !value.empty();
133 }
134 else if (key == "ether") {
135 // validate ethernet address
136 return !ndn::ethernet::Address::fromString(value).isNull();
137 }
138 else if (key == "subnet") {
139 // example subnet: 10.0.0.0/8
140 return Network::isValidCidr(value);
141 }
142 else {
Davide Pesavento19779d82019-02-14 13:40:04 -0500143 NDN_THROW(std::logic_error("Only supported rules are expected"));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -0400144 }
145}
146
147bool
148IpAddressPredicate::isRuleSupported(const std::string& key)
149{
150 return key == "subnet";
151}
152
153bool
154IpAddressPredicate::isRuleValid(const std::string& key, const std::string& value)
155{
156 if (key == "subnet") {
157 // example subnet: 10.0.0.0/8
158 return Network::isValidCidr(value);
159 }
160 else {
Davide Pesavento19779d82019-02-14 13:40:04 -0500161 NDN_THROW(std::logic_error("Only supported rules are expected"));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -0400162 }
163}
164
165bool
166NetworkPredicateBase::operator==(const NetworkPredicateBase& other) const
167{
168 return this->m_whitelist == other.m_whitelist &&
169 this->m_blacklist == other.m_blacklist;
170}
171
172static bool
173doesMatchPattern(const std::string& ifname, const std::string& pattern)
174{
175 // use fnmatch(3) to provide unix glob-style matching for interface names
176 // fnmatch returns 0 if there is a match
177 return ::fnmatch(pattern.data(), ifname.data(), 0) == 0;
178}
179
180static bool
181doesNetifMatchRule(const ndn::net::NetworkInterface& netif, const std::string& rule)
182{
183 // if '/' is in rule, this is a subnet, check if IP in subnet
184 if (rule.find('/') != std::string::npos) {
185 Network n = boost::lexical_cast<Network>(rule);
186 for (const auto& addr : netif.getNetworkAddresses()) {
187 if (n.doesContain(addr.getIp())) {
188 return true;
189 }
190 }
191 }
192
193 return rule == "*" ||
194 doesMatchPattern(netif.getName(), rule) ||
195 netif.getEthernetAddress().toString() == rule;
196}
197
198bool
199NetworkInterfacePredicate::operator()(const ndn::net::NetworkInterface& netif) const
200{
Davide Pesavento412c9822021-07-02 00:21:05 -0400201 return std::any_of(m_whitelist.begin(), m_whitelist.end(),
202 [&netif] (const auto& rule) { return doesNetifMatchRule(netif, rule); }) &&
203 std::none_of(m_blacklist.begin(), m_blacklist.end(),
204 [&netif] (const auto& rule) { return doesNetifMatchRule(netif, rule); });
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -0400205}
206
207static bool
208doesAddressMatchRule(const boost::asio::ip::address& address, const std::string& rule)
209{
210 // if '/' is in rule, this is a subnet, check if IP in subnet
211 if (rule.find('/') != std::string::npos) {
212 Network n = boost::lexical_cast<Network>(rule);
213 if (n.doesContain(address)) {
214 return true;
215 }
216 }
217
218 return rule == "*";
219}
220
221bool
222IpAddressPredicate::operator()(const boost::asio::ip::address& address) const
223{
Davide Pesavento412c9822021-07-02 00:21:05 -0400224 return std::any_of(m_whitelist.begin(), m_whitelist.end(),
225 [&address] (const auto& rule) { return doesAddressMatchRule(address, rule); }) &&
226 std::none_of(m_blacklist.begin(), m_blacklist.end(),
227 [&address] (const auto& rule) { return doesAddressMatchRule(address, rule); });
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -0400228}
229
Davide Pesavento2cae8ca2019-04-18 20:48:05 -0400230} // namespace face
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -0400231} // namespace nfd