1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5
6#include <algorithm>
7#include <cstdlib>
8#include <iterator>
9#include <map>
10
11#include <fcntl.h>
12#include <netdb.h>
13#include <net/if.h>
14#include <netinet/in.h>
15#include <arpa/inet.h>
16
17#include <string.h>
18
19#include "net_util.h"
20
21namespace net {
22
23#ifndef INET6_ADDRSTRLEN  /* for non IPv6 machines */
24#define INET6_ADDRSTRLEN 46
25#endif
26
27bool ParseIPLiteralToNumber(const std::string& ip_literal,
28                            IPAddressNumber* ip_number) {
29  char buf[sizeof(struct in6_addr)];
30  int size = sizeof(struct in_addr);
31  int mode = AF_INET;
32  if (ip_literal.find(':') != std::string::npos) {
33    mode = AF_INET6;
34    size = sizeof(struct in6_addr);
35  }
36  if (inet_pton(mode, ip_literal.c_str(), buf) != 1) {
37    return false;
38  }
39  ip_number->resize(size);
40  for (int i = 0; i < size; i++) {
41    (*ip_number)[i] = buf[i];
42  }
43  return true;
44}
45
46IPAddressNumber ConvertIPv4NumberToIPv6Number(
47    const IPAddressNumber& ipv4_number) {
48  // IPv4-mapped addresses are formed by:
49  // <80 bits of zeros>  + <16 bits of ones> + <32-bit IPv4 address>.
50  IPAddressNumber ipv6_number;
51  ipv6_number.reserve(16);
52  ipv6_number.insert(ipv6_number.end(), 10, 0);
53  ipv6_number.push_back(0xFF);
54  ipv6_number.push_back(0xFF);
55  ipv6_number.insert(ipv6_number.end(), ipv4_number.begin(), ipv4_number.end());
56  return ipv6_number;
57}
58
59bool ParseCIDRBlock(const std::string& cidr_literal,
60                    IPAddressNumber* ip_number,
61                    size_t* prefix_length_in_bits) {
62  // We expect CIDR notation to match one of these two templates:
63  //   <IPv4-literal> "/" <number of bits>
64  //   <IPv6-literal> "/" <number of bits>
65
66  std::vector<std::string> parts;
67  size_t split = cidr_literal.find('/');
68  if (split == std::string::npos)
69    return false;
70  parts.push_back(cidr_literal.substr(0, split));
71  parts.push_back(cidr_literal.substr(split + 1));
72  if (parts[1].find('/') != std::string::npos)
73    return false;
74
75  // Parse the IP address.
76  if (!ParseIPLiteralToNumber(parts[0], ip_number))
77    return false;
78
79  // Parse the prefix length.
80  int number_of_bits = atoi(parts[1].c_str());
81
82  // Make sure the prefix length is in a valid range.
83  if (number_of_bits < 0 ||
84      number_of_bits > static_cast<int>(ip_number->size() * 8))
85    return false;
86
87  *prefix_length_in_bits = static_cast<size_t>(number_of_bits);
88  return true;
89}
90
91bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number,
92                           const IPAddressNumber& ip_prefix,
93                           size_t prefix_length_in_bits) {
94  // Both the input IP address and the prefix IP address should be
95  // either IPv4 or IPv6.
96
97  // In case we have an IPv6 / IPv4 mismatch, convert the IPv4 addresses to
98  // IPv6 addresses in order to do the comparison.
99  if (ip_number.size() != ip_prefix.size()) {
100    if (ip_number.size() == 4) {
101      return IPNumberMatchesPrefix(ConvertIPv4NumberToIPv6Number(ip_number),
102                                   ip_prefix, prefix_length_in_bits);
103    }
104    return IPNumberMatchesPrefix(ip_number,
105                                 ConvertIPv4NumberToIPv6Number(ip_prefix),
106                                 96 + prefix_length_in_bits);
107  }
108
109  // Otherwise we are comparing two IPv4 addresses, or two IPv6 addresses.
110  // Compare all the bytes that fall entirely within the prefix.
111  int num_entire_bytes_in_prefix = prefix_length_in_bits / 8;
112  for (int i = 0; i < num_entire_bytes_in_prefix; ++i) {
113    if (ip_number[i] != ip_prefix[i])
114      return false;
115  }
116
117  // In case the prefix was not a multiple of 8, there will be 1 byte
118  // which is only partially masked.
119  int remaining_bits = prefix_length_in_bits % 8;
120  if (remaining_bits != 0) {
121    unsigned char mask = 0xFF << (8 - remaining_bits);
122    int i = num_entire_bytes_in_prefix;
123    if ((ip_number[i] & mask) != (ip_prefix[i] & mask))
124      return false;
125  }
126
127  return true;
128}
129
130}  // namespace net
131