subnet.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. * Copyright (c) 2016, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package common
  20. import (
  21. "bufio"
  22. "bytes"
  23. "encoding/binary"
  24. "net"
  25. "sort"
  26. "strings"
  27. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  28. )
  29. // SubnetLookup provides an efficient lookup for individual
  30. // IPv4 addresses within a list of subnets.
  31. type SubnetLookup []net.IPNet
  32. // NewSubnetLookup creates a SubnetLookup from a list of
  33. // subnet CIDRs.
  34. func NewSubnetLookup(CIDRs []string) (SubnetLookup, error) {
  35. subnets := make([]net.IPNet, len(CIDRs))
  36. for i, CIDR := range CIDRs {
  37. _, network, err := net.ParseCIDR(CIDR)
  38. if err != nil {
  39. return nil, errors.Trace(err)
  40. }
  41. subnets[i] = *network
  42. }
  43. lookup := SubnetLookup(subnets)
  44. sort.Sort(lookup)
  45. return lookup, nil
  46. }
  47. // NewSubnetLookupFromRoutes creates a SubnetLookup from text routes
  48. // data. The input format is expected to be text lines where each line
  49. // is, e.g., "1.2.3.0\t255.255.255.0\n"
  50. func NewSubnetLookupFromRoutes(routesData []byte) (SubnetLookup, error) {
  51. // Parse text routes data
  52. var subnets []net.IPNet
  53. scanner := bufio.NewScanner(bytes.NewReader(routesData))
  54. scanner.Split(bufio.ScanLines)
  55. for scanner.Scan() {
  56. s := strings.Split(scanner.Text(), "\t")
  57. if len(s) != 2 {
  58. continue
  59. }
  60. ip := parseIPv4(s[0])
  61. mask := parseIPv4Mask(s[1])
  62. if ip == nil || mask == nil {
  63. continue
  64. }
  65. subnets = append(subnets, net.IPNet{IP: ip.Mask(mask), Mask: mask})
  66. }
  67. if len(subnets) == 0 {
  68. return nil, errors.TraceNew("Routes data contains no networks")
  69. }
  70. lookup := SubnetLookup(subnets)
  71. sort.Sort(lookup)
  72. return lookup, nil
  73. }
  74. func parseIPv4(s string) net.IP {
  75. ip := net.ParseIP(s)
  76. if ip == nil {
  77. return nil
  78. }
  79. return ip.To4()
  80. }
  81. func parseIPv4Mask(s string) net.IPMask {
  82. ip := parseIPv4(s)
  83. if ip == nil {
  84. return nil
  85. }
  86. mask := net.IPMask(ip)
  87. if bits, size := mask.Size(); bits == 0 || size == 0 {
  88. return nil
  89. }
  90. return mask
  91. }
  92. // Len implements Sort.Interface
  93. func (lookup SubnetLookup) Len() int {
  94. return len(lookup)
  95. }
  96. // Swap implements Sort.Interface
  97. func (lookup SubnetLookup) Swap(i, j int) {
  98. lookup[i], lookup[j] = lookup[j], lookup[i]
  99. }
  100. // Less implements Sort.Interface
  101. func (lookup SubnetLookup) Less(i, j int) bool {
  102. return binary.BigEndian.Uint32(lookup[i].IP) < binary.BigEndian.Uint32(lookup[j].IP)
  103. }
  104. // ContainsIPAddress performs a binary search on the sorted subnet
  105. // list to find a network containing the candidate IP address.
  106. func (lookup SubnetLookup) ContainsIPAddress(addr net.IP) bool {
  107. // Search criteria
  108. //
  109. // The following conditions are satisfied when address_IP is in the network:
  110. // 1. address_IP ^ network_mask == network_IP ^ network_mask
  111. // 2. address_IP >= network_IP.
  112. // We are also assuming that network ranges do not overlap.
  113. //
  114. // For an ascending array of networks, the sort.Search returns the smallest
  115. // index idx for which condition network_IP > address_IP is satisfied, so we
  116. // are checking whether or not adrress_IP belongs to the network[idx-1].
  117. // Edge conditions check
  118. //
  119. // idx == 0 means that address_IP is lesser than the first (smallest) network_IP
  120. // thus never satisfies search condition 2.
  121. // idx == array_length means that address_IP is larger than the last (largest)
  122. // network_IP so we need to check the last element for condition 1.
  123. ipv4 := addr.To4()
  124. if ipv4 == nil {
  125. return false
  126. }
  127. addrValue := binary.BigEndian.Uint32(ipv4)
  128. index := sort.Search(len(lookup), func(i int) bool {
  129. networkValue := binary.BigEndian.Uint32(lookup[i].IP)
  130. return networkValue > addrValue
  131. })
  132. return index > 0 && lookup[index-1].IP.Equal(addr.Mask(lookup[index-1].Mask))
  133. }