blob: d10a5e330bfe5fe21f761de08db556ea505d4337 [file] [log] [blame]
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2014-2018, 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"
27
28#include "config-file.hpp"
29#include "network.hpp"
30
31#include <fnmatch.h>
32
33namespace nfd {
34
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
50NetworkPredicateBase::parseList(std::set<std::string>& set, const boost::property_tree::ptree& list, const std::string& section)
51{
52 set.clear();
53
54 for (const auto& item : list) {
55 if (item.first == "*") {
56 // insert wildcard
57 set.insert(item.first);
58 }
59 else {
60 if (!isRuleSupported(item.first)) {
61 BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized rule \"" + item.first + "\" in \"" + section + "\" section"));
62 }
63
64 auto value = item.second.get_value<std::string>();
65 if (!isRuleValid(item.first, value)) {
66 BOOST_THROW_EXCEPTION(ConfigFile::Error("Malformed " + item.first + " \"" + value + "\" in \"" + section + "\" section"));
67 }
68 set.insert(value);
69 }
70 }
71}
72
73void
74NetworkPredicateBase::parseList(std::set<std::string>& set, std::initializer_list<std::pair<std::string, std::string>> list)
75{
76 set.clear();
77
78 for (const auto& item : list) {
79 if (item.first == "*") {
80 // insert wildcard
81 set.insert(item.first);
82 }
83 else {
84 if (!isRuleSupported(item.first)) {
85 BOOST_THROW_EXCEPTION(std::runtime_error("Unrecognized rule \"" + item.first + "\""));
86 }
87
88 if (!isRuleValid(item.first, item.second)) {
89 BOOST_THROW_EXCEPTION(std::runtime_error("Malformed " + item.first + " \"" + item.second + "\""));
90 }
91 set.insert(item.second);
92 }
93 }
94}
95
96void
97NetworkPredicateBase::parseWhitelist(const boost::property_tree::ptree& list)
98{
99 parseList(m_whitelist, list, "whitelist");
100}
101
102void
103NetworkPredicateBase::parseBlacklist(const boost::property_tree::ptree& list)
104{
105 parseList(m_blacklist, list, "blacklist");
106}
107
108void
109NetworkPredicateBase::assign(std::initializer_list<std::pair<std::string, std::string>> whitelist,
110 std::initializer_list<std::pair<std::string, std::string>> blacklist)
111{
112 parseList(m_whitelist, whitelist);
113 parseList(m_blacklist, blacklist);
114}
115
116bool
117NetworkInterfacePredicate::isRuleSupported(const std::string& key)
118{
119 return key == "ifname" || key == "ether" || key == "subnet";
120}
121
122bool
123NetworkInterfacePredicate::isRuleValid(const std::string& key, const std::string& value)
124{
125 if (key == "ifname") {
126 // very basic sanity check for interface names
127 return !value.empty();
128 }
129 else if (key == "ether") {
130 // validate ethernet address
131 return !ndn::ethernet::Address::fromString(value).isNull();
132 }
133 else if (key == "subnet") {
134 // example subnet: 10.0.0.0/8
135 return Network::isValidCidr(value);
136 }
137 else {
138 BOOST_THROW_EXCEPTION(std::logic_error("Only supported rules are expected"));
139 }
140}
141
142bool
143IpAddressPredicate::isRuleSupported(const std::string& key)
144{
145 return key == "subnet";
146}
147
148bool
149IpAddressPredicate::isRuleValid(const std::string& key, const std::string& value)
150{
151 if (key == "subnet") {
152 // example subnet: 10.0.0.0/8
153 return Network::isValidCidr(value);
154 }
155 else {
156 BOOST_THROW_EXCEPTION(std::logic_error("Only supported rules are expected"));
157 }
158}
159
160bool
161NetworkPredicateBase::operator==(const NetworkPredicateBase& other) const
162{
163 return this->m_whitelist == other.m_whitelist &&
164 this->m_blacklist == other.m_blacklist;
165}
166
167static bool
168doesMatchPattern(const std::string& ifname, const std::string& pattern)
169{
170 // use fnmatch(3) to provide unix glob-style matching for interface names
171 // fnmatch returns 0 if there is a match
172 return ::fnmatch(pattern.data(), ifname.data(), 0) == 0;
173}
174
175static bool
176doesNetifMatchRule(const ndn::net::NetworkInterface& netif, const std::string& rule)
177{
178 // if '/' is in rule, this is a subnet, check if IP in subnet
179 if (rule.find('/') != std::string::npos) {
180 Network n = boost::lexical_cast<Network>(rule);
181 for (const auto& addr : netif.getNetworkAddresses()) {
182 if (n.doesContain(addr.getIp())) {
183 return true;
184 }
185 }
186 }
187
188 return rule == "*" ||
189 doesMatchPattern(netif.getName(), rule) ||
190 netif.getEthernetAddress().toString() == rule;
191}
192
193bool
194NetworkInterfacePredicate::operator()(const ndn::net::NetworkInterface& netif) const
195{
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400196 return std::any_of(m_whitelist.begin(), m_whitelist.end(), bind(&doesNetifMatchRule, std::cref(netif), _1)) &&
197 std::none_of(m_blacklist.begin(), m_blacklist.end(), bind(&doesNetifMatchRule, std::cref(netif), _1));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -0400198}
199
200static bool
201doesAddressMatchRule(const boost::asio::ip::address& address, const std::string& rule)
202{
203 // if '/' is in rule, this is a subnet, check if IP in subnet
204 if (rule.find('/') != std::string::npos) {
205 Network n = boost::lexical_cast<Network>(rule);
206 if (n.doesContain(address)) {
207 return true;
208 }
209 }
210
211 return rule == "*";
212}
213
214bool
215IpAddressPredicate::operator()(const boost::asio::ip::address& address) const
216{
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400217 return std::any_of(m_whitelist.begin(), m_whitelist.end(), bind(&doesAddressMatchRule, std::cref(address), _1)) &&
218 std::none_of(m_blacklist.begin(), m_blacklist.end(), bind(&doesAddressMatchRule, std::cref(address), _1));
Alexander Afanasyeve4d745d2018-04-08 17:55:56 -0400219}
220
221} // namespace nfd