ip_check.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #ifndef __IP_CHECK_H
  15. #define __IP_CHECK_H
  16. #include <sys/socket.h>
  17. #include <cassert>
  18. #include <functional>
  19. #include <iterator>
  20. #include <utility>
  21. #include <vector>
  22. #include <boost/lexical_cast.hpp>
  23. #include <boost/static_assert.hpp>
  24. #include <boost/scoped_ptr.hpp>
  25. #include <stdint.h>
  26. #include <arpa/inet.h>
  27. #include <netinet/in.h>
  28. #include <acl/check.h>
  29. #include <util/strutil.h>
  30. #include <exceptions/exceptions.h>
  31. namespace isc {
  32. namespace acl {
  33. // Free functions
  34. /// \brief Convert mask size to mask
  35. ///
  36. /// Given a mask size and a data type, return a value of that data type with the
  37. /// most significant "masksize" bits set. For example, if the data type is an
  38. /// uint8_t and the masksize is 3, the function would return a uint8_t holding
  39. /// the binary value 11100000.
  40. ///
  41. /// The function is templated on the data type of the mask.
  42. ///
  43. /// \param masksize Size of the mask. This must be between 0 and 8*sizeof(T).
  44. /// An out of range exception is thrown if this is not the case.
  45. ///
  46. /// \return Value with the most significant "masksize" bits set.
  47. template <typename T>
  48. T createNetmask(size_t masksize) {
  49. if (masksize == 0) {
  50. // Although a mask size of zero is invalid for the IP ACL check
  51. // specification, it simplifies logic elsewhere if this function is
  52. // allowed to handle a mask size of 0.
  53. return (0);
  54. } else if (masksize <= 8 * sizeof(T)) {
  55. // In the following discussion:
  56. //
  57. // w is the width of the data type T in bits.
  58. // m is the value of masksize, the number of most signifcant bits we
  59. // want to set.
  60. // ** is exponentiation (i.e. 2**n is 2 raised to the power of n).
  61. //
  62. // We note that the value of 2**m - 1 gives a value with the least
  63. // significant m bits set. For a data type of width w, this means that
  64. // the most signficant (w-m) bits clear.
  65. //
  66. // Hence the value 2**(w-m) - 1 gives a result with the least signficant
  67. // w-m bits set and the most significant m bits clear. The 1's
  68. // complement of this value gives is the result we want.
  69. //
  70. // Final note: at this point in the logic, m is non-zero, so w-m < m.
  71. // This means 1<<(w-m) will fit into a variable of width w bits. In
  72. // other words, in the expression below, no term will cause an integer
  73. // overflow.
  74. return (~((1 << (8 * sizeof(T) - masksize)) - 1));
  75. }
  76. // Mask size is too large. (Note that masksize is unsigned, so can't be
  77. // negative.)
  78. isc_throw(isc::OutOfRange, "masksize argument must be between 0 and " <<
  79. 8 * sizeof(T));
  80. }
  81. /// \brief Split IP Address
  82. ///
  83. /// Splits an IP address (given in the form of "xxxxxx/n" or "xxxxx" into a
  84. /// string representing the IP address and a number giving the size of the
  85. /// network mask in bits. (In the latter case, the size of the network mask is
  86. /// equal to the width of the data type holding the address.) An exception will
  87. /// be thrown if the string format is invalid or if the network mask size is not
  88. /// an integer.
  89. ///
  90. /// N.B. This function does NOT check that the address component is a valid IP
  91. /// address; this is done elsewhere in the address parsing process.
  92. ///
  93. /// \param addrmask Address and/or address/mask. The string should be passed
  94. /// without leading or trailing spaces.
  95. ///
  96. /// \return Pair of (string, int) holding the address string and the mask
  97. /// size value. The second element is -1 if no mask was given.
  98. std::pair<std::string, int>
  99. splitIPAddress(const std::string& addrmask) {
  100. // Set the default value for the mask size
  101. int masksize = -1;
  102. // Split string into its components. As the tokenising code ignores
  103. // leading, trailing nd consecutive delimiters, be strict here and ensures
  104. // that the string contains at most 0 or 1 slashes.
  105. if (std::count(addrmask.begin(), addrmask.end(), '/') > 1) {
  106. isc_throw(isc::InvalidParameter, "address/masksize of " <<
  107. addrmask << " is not valid");
  108. }
  109. std::vector<std::string> components = isc::util::str::tokens(addrmask, "/");
  110. if (components.size() == 2) {
  111. // There appears to be a mask, try converting it to a number.
  112. try {
  113. masksize = boost::lexical_cast<int>(components[1]);
  114. } catch (boost::bad_lexical_cast&) {
  115. isc_throw(isc::InvalidParameter,
  116. "mask size specified in address/masksize " << addrmask <<
  117. " is not valid");
  118. }
  119. // Ensure that it is positive - a mask size of zero is not a valid
  120. // value.
  121. if (masksize <= 0) {
  122. isc_throw(isc::OutOfRange,
  123. "mask size specified in address/masksize " << addrmask <<
  124. " must be a positive number");
  125. }
  126. } else if (components.size() > 2) {
  127. isc_throw(isc::InvalidParameter, "address/masksize of " <<
  128. addrmask << " is not valid");
  129. }
  130. return (std::make_pair(components[0], masksize));
  131. }
  132. /// \brief A simple representation of IP address
  133. ///
  134. struct IPAddress {
  135. explicit IPAddress(const struct sockaddr& sa);
  136. const int family;
  137. const uint8_t* const data;
  138. const size_t length;
  139. };
  140. /// \brief IP Check
  141. ///
  142. /// This class performs a match between an IP address specified in an ACL
  143. /// (IP address, network mask and a flag indicating whether the check should
  144. /// be for a match or for a non-match) and a given IP address. The check
  145. /// works for both IPV4 and IPV6 addresses.
  146. ///
  147. /// The class is templated on the type of a context structure passed to the
  148. /// matches() method, and a template specialisation for that method must be
  149. /// supplied for the class to be used.
  150. template <typename Context>
  151. class IPCheck : public Check<Context> {
  152. private:
  153. // Size of uint8_t array to hold an IPV6 address, and size of a 32-bit word
  154. // equivalent.
  155. static const size_t IPV6_SIZE8 = sizeof(struct in6_addr);
  156. static const size_t IPV6_SIZE32 = IPV6_SIZE8 / 4;
  157. // Data type to hold the address, regardless of the address family. The
  158. // union allows an IPV4 address to be treated as a sequence of bytes when
  159. // necessary.
  160. union AddressData {
  161. uint32_t word[IPV6_SIZE32]; ///< Address in 32-bit words
  162. uint8_t byte[IPV6_SIZE8]; ///< Address in 8-bit bytes
  163. };
  164. public:
  165. /// \brief Default Constructor
  166. ///
  167. /// Constructs an empty IPCheck object. The address family returned will
  168. /// be zero.
  169. IPCheck() : address_(), netmask_(), masksize_(0), inverse_(false),
  170. family_(0), straddr_()
  171. {
  172. std::fill(address_.word, address_.word + IPV6_SIZE32, 0);
  173. std::fill(netmask_.word, netmask_.word + IPV6_SIZE32, 0);
  174. }
  175. /// \brief IPV4 Constructor
  176. ///
  177. /// Constructs an IPCheck object from a network address given as a
  178. /// 32-bit value in network byte order.
  179. ///
  180. /// \param address IP address to check for (as an address in network-byte
  181. /// order).
  182. /// \param mask The network mask specified as an integer between 1 and
  183. /// 32. This determines the number of bits in the mask to check.
  184. /// An exception will be thrown if the number is not within these
  185. /// bounds.
  186. /// \param inverse If false (the default), matches() returns true if the
  187. /// condition matches. If true, matches() returns true if the
  188. /// condition does not match.
  189. IPCheck(uint32_t address, int masksize = 8 * sizeof(uint32_t),
  190. bool inverse = false):
  191. address_(), netmask_(), masksize_(masksize), inverse_(inverse),
  192. family_(AF_INET), straddr_()
  193. {
  194. address_.word[0] = address;
  195. std::fill(address_.word + 1, address_.word + IPV6_SIZE32, 0);
  196. std::fill(netmask_.word, netmask_.word + IPV6_SIZE32, 0);
  197. setNetmask(masksize_);
  198. }
  199. /// \brief IPV6 Constructor
  200. ///
  201. /// Constructs an IPv6 Check object from a network address given as a
  202. /// 16-byte array in network-byte order.
  203. ///
  204. /// \param address IP address to check for (as an address in network-byte
  205. /// order).
  206. /// \param mask The network mask specified as an integer between 1 and
  207. /// 128 This determines the number of bits in the mask to check.
  208. /// An exception will be thrown if the number is not within these
  209. /// bounds.
  210. /// \param inverse If false (the default), matches() returns true if the
  211. /// condition matches. If true, matches() returns true if the
  212. /// condition does not match.
  213. IPCheck(const uint8_t* address, int masksize = 8 * IPV6_SIZE8,
  214. bool inverse = false):
  215. address_(), netmask_(), masksize_(masksize), inverse_(inverse),
  216. family_(AF_INET6), straddr_()
  217. {
  218. std::copy(address, address + IPV6_SIZE8, address_.byte);
  219. std::fill(netmask_.word, netmask_.word + IPV6_SIZE32, 0);
  220. setNetmask(masksize_);
  221. }
  222. /// \brief String Constructor
  223. ///
  224. /// Constructs an IP Check object from a network address and size of mask
  225. /// given as a string of the form <ip-address>/n".
  226. ///
  227. /// \param address IP address and netmask in the form "<ip-address>/n"
  228. /// (where the "/n" part is optional and should be valid for the
  229. /// address).
  230. /// \param inverse If false (the default), matches() returns true if the
  231. /// condition matches. If true, matches() returns true if the
  232. /// condition does not match.
  233. IPCheck(const std::string& address, bool inverse = false) :
  234. address_(), netmask_(), masksize_(0), inverse_(inverse),
  235. family_(0), straddr_(address)
  236. {
  237. // Initialize.
  238. std::fill(address_.word, address_.word + IPV6_SIZE32, 0);
  239. std::fill(netmask_.word, netmask_.word + IPV6_SIZE32, 0);
  240. // Split the address into address part and mask.
  241. std::pair<std::string, int> result = splitIPAddress(address);
  242. // Try to convert the address. If successful, the result is in
  243. // network-byte order (most significant components at lower addresses).
  244. family_ = AF_INET6;
  245. int status = inet_pton(AF_INET6, result.first.c_str(), address_.byte);
  246. if (status == 0) {
  247. // Not IPV6, try IPv4
  248. family_ = AF_INET;
  249. int status = inet_pton(AF_INET, result.first.c_str(),
  250. address_.word);
  251. if (status == 0) {
  252. isc_throw(isc::InvalidParameter, "address/masksize of " <<
  253. address << " is not valid IP address");
  254. }
  255. }
  256. // All done, so set the network mask.
  257. setNetmask(result.second);
  258. }
  259. /// \brief Copy constructor
  260. ///
  261. /// \param other Object from which the copy is being constructed.
  262. IPCheck(const IPCheck<Context>& other) : address_(), netmask_(),
  263. masksize_(other.masksize_), inverse_(other.inverse_),
  264. family_(other.family_), straddr_(other.straddr_)
  265. {
  266. std::copy(other.address_.word, other.address_.word + IPV6_SIZE32,
  267. address_.word);
  268. std::copy(other.netmask_.word, other.netmask_.word + IPV6_SIZE32,
  269. netmask_.word);
  270. }
  271. /// \brief Assignment operator
  272. ///
  273. /// \param other Source of the assignment.
  274. ///
  275. /// \return Reference to current object.
  276. IPCheck& operator=(const IPCheck<Context>& other) {
  277. if (this != &other) {
  278. Check<Context>::operator=(other);
  279. std::copy(other.address_.word, other.address_.word + IPV6_SIZE32,
  280. address_.word);
  281. std::copy(other.netmask_.word, other.netmask_.word + IPV6_SIZE32,
  282. netmask_.word);
  283. masksize_ = other.masksize_;
  284. inverse_ = other.inverse_;
  285. family_ = other.family_;
  286. straddr_ = other.straddr_;
  287. }
  288. return (*this);
  289. }
  290. /// \brief Destructor
  291. virtual ~IPCheck() {}
  292. /// \brief The check itself
  293. ///
  294. /// Matches the passed argument to the condition stored here. Different
  295. /// specialisations must be provided for different argument types, and the
  296. /// program will fail to compile if a required specialisation is not
  297. /// provided.
  298. ///
  299. /// \param context Information to be matched
  300. virtual bool matches(const Context& context) const;
  301. /// \brief Estimated cost
  302. ///
  303. /// Assume that the cost of the match is linear and depends on the
  304. /// maximum number of comparison operations.
  305. ///
  306. /// \return Estimated cost of the comparison
  307. virtual unsigned cost() const {
  308. return ((family_ == AF_INET) ? 1 : IPV6_SIZE32);
  309. }
  310. ///@{
  311. /// Access methods - mainly for testing
  312. /// \return Stored IP address
  313. std::vector<uint8_t> getAddress() const {
  314. return (std::vector<uint8_t>(address_.byte, address_.byte + IPV6_SIZE8));
  315. }
  316. /// \return Network mask applied to match
  317. std::vector<uint8_t> getNetmask() const {
  318. return (std::vector<uint8_t>(netmask_.byte, netmask_.byte + IPV6_SIZE8));
  319. }
  320. /// \return String passed to constructor
  321. std::string getStringAddress() const {
  322. return (straddr_);
  323. }
  324. /// \return Mask size given to constructor
  325. size_t getMasksize() const {
  326. return (masksize_);
  327. }
  328. /// \return Address family
  329. int getFamily() const {
  330. // Check that a family_ value of 0 does not imply IPV4 or IPV6.
  331. // This avoids confusion if getFamily() is called on an object that
  332. // has been initialized by default.
  333. BOOST_STATIC_ASSERT(AF_INET != 0);
  334. BOOST_STATIC_ASSERT(AF_INET6 != 0);
  335. return (family_);
  336. }
  337. /// \return Setting of inverse flag
  338. bool getInverse() const {
  339. return (inverse_);
  340. }
  341. ///@}
  342. private:
  343. /// \brief Comparison
  344. ///
  345. /// This is the actual comparison function that checks the IP address passed
  346. /// to this class with the matching information in the class itself. It is
  347. /// expected to be called from matches().
  348. ///
  349. /// \param testaddr Address (in network byte order) to test against the
  350. /// check condition in the class. This is expected to
  351. /// be IPV6_SIZE8 bytes long.
  352. ///
  353. /// \return true if the address matches, false if it does not.
  354. virtual bool compare(const uint8_t* testaddr) const {
  355. // To check that the address given matches the stored network address
  356. // and netmask, we check the simple condition that:
  357. //
  358. // address_given & netmask_ == stored_address & netmask_
  359. //
  360. // The result is checked for all bytes for which there are bits set in
  361. // the network mask. We stop at the first non-match (or when we run
  362. // out of bits in the network mask). (Note that the network mask
  363. // represents a contiguous set of bits. As such, as soon as we find
  364. // a netmask byte of zeroes, we have run past the part of the address
  365. // where we need to match.
  366. //
  367. // We can optimise further by casting to a 32-bit array and checking
  368. // 32 bits at a time.
  369. bool match = true;
  370. for (int i = 0; match && (i < IPV6_SIZE8) && (netmask_.byte[i] != 0);
  371. ++i) {
  372. match = ((testaddr[i] & netmask_.byte[i]) ==
  373. (address_.byte[i] & netmask_.byte[i]));
  374. }
  375. // As with the V4 check, return the XOR with the inverse flag.
  376. return (match != inverse_);
  377. }
  378. /// \brief Comparison
  379. ///
  380. /// Convenience comparison for an IPV4 address.
  381. ///
  382. /// \param testaddr Address (in network byte order) to test against the
  383. /// check condition in the class.
  384. ///
  385. /// \return true if the address matches, false if it does not.
  386. virtual bool compare(const uint32_t testaddr) const {
  387. return (((testaddr & netmask_.word[0]) ==
  388. (address_.word[0] & netmask_.word[0])) != inverse_);
  389. }
  390. /// \brief Set Network Mask
  391. ///
  392. /// Sets up the network mask from the mask size. This involves setting
  393. /// an individual mask in each byte of the network mask.
  394. ///
  395. /// The actual allowed value of the mask size depends on the address
  396. /// family.
  397. ///
  398. /// \param requested Requested mask size. If negative, the maximum for
  399. /// the address family is assumed. (A negative value will arise
  400. /// if the string constructor was used and no mask size was given.)
  401. void setNetmask(int requested) {
  402. // Set the maximum mask size allowed.
  403. int maxmask = 8 * ((family_ == AF_INET) ? sizeof(uint32_t) : IPV6_SIZE8);
  404. if (requested < 0) {
  405. requested = maxmask;
  406. }
  407. // Validate that the mask is valid.
  408. if ((requested >= 1) && (requested <= maxmask)) {
  409. masksize_ = requested;
  410. // The netmask array was initialized to zero in the constructor,
  411. // but as an addition check, assert that this is so.
  412. assert(std::find_if(netmask_.word, netmask_.word + IPV6_SIZE32,
  413. std::bind1st(std::not_equal_to<uint32_t>(), 0)) ==
  414. netmask_.word + IPV6_SIZE32);
  415. // Loop, setting the bits in the set of mask bytes until all the
  416. // specified bits have been used up. As both IPV4 and IPV6
  417. // addresses are stored in network-byte order, this works in
  418. // both cases.
  419. size_t bits_left = masksize_; // Bits remaining to set
  420. int i = -1;
  421. while (bits_left > 0) {
  422. if (bits_left >= 8) {
  423. netmask_.byte[++i] = ~0; // All bits set
  424. bits_left -= 8;
  425. } else if (bits_left > 0) {
  426. netmask_.byte[++i] = createNetmask<uint8_t>(bits_left);
  427. bits_left = 0;
  428. }
  429. }
  430. } else {
  431. isc_throw(isc::OutOfRange,
  432. "mask size of " << masksize_ << " is invalid " <<
  433. "for the givem address");
  434. }
  435. }
  436. // Member variables
  437. AddressData address_; ///< Address in binary form
  438. AddressData netmask_; ///< Network mask
  439. size_t masksize_; ///< Mask size passed to constructor
  440. bool inverse_; ///< Test for equality or inequality
  441. int family_; ///< Address family
  442. std::string straddr_; ///< Copy of constructor address string
  443. };
  444. } // namespace acl
  445. } // namespace isc
  446. #endif // __IP_CHECK_H
  447. // Local Variables:
  448. // mode: c++
  449. // End: