Skip to content

Commit

Permalink
Merge pull request #535 from martijnhoekstra/print-cidr-prefix
Browse files Browse the repository at this point in the history
fixes for cidr equality, validity and consistency
  • Loading branch information
mpilquist authored Nov 4, 2023
2 parents c7e73a2 + 24b1fb8 commit e25d489
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 1 deletion.
34 changes: 33 additions & 1 deletion shared/src/main/scala/com/comcast/ip4s/Cidr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import scala.util.Try
import scala.util.hashing.MurmurHash3

import cats.{Order, Show}
import com.comcast.ip4s.Cidr.Strict

/** Classless Inter-Domain Routing address, which represents an IP address and its routing prefix.
*
Expand All @@ -28,10 +29,24 @@ import cats.{Order, Show}
* @param prefixBits
* number of leading 1s in the routing mask
*/
final class Cidr[+A <: IpAddress] private (val address: A, val prefixBits: Int) extends Product with Serializable {
sealed class Cidr[+A <: IpAddress] protected (val address: A, val prefixBits: Int) extends Product with Serializable {
def copy[AA >: A <: IpAddress](address: AA = this.address, prefixBits: Int = this.prefixBits): Cidr[AA] =
Cidr[AA](address, prefixBits)

/** Returns a normalized cidr range, where the address is truncated to the prefix, so that the returned range
* is (and prints as) a spec-valid cidr range, with no bits outside the routing mask set.
*
* @return a normalized cidr range
*
* @example {{{
* scala> val raw = Cidr(ipv4"10.11.12.13", 8)
* raw: Cidr[Ipv4Address] = 10.11.12.13/8
* scala> raw.normalized
* res0: Cidr.Strict[Ipv4Address] = 10.0.0.0/8
* }}}
*/
def normalized: Strict[A] = Cidr.Strict(this)

/** Returns the routing mask.
*
* @example {{{
Expand Down Expand Up @@ -118,6 +133,23 @@ final class Cidr[+A <: IpAddress] private (val address: A, val prefixBits: Int)

object Cidr {

/** A normalized cidr range, of which the address is identical to the prefix.
*
* This means the address will never have any bits set outside the prefix. For example, a range
* such as 192.168.0.1/31 is not allowed.
*/
final class Strict[+A <: IpAddress] private (override val prefix: A, prefixBits: Int)
extends Cidr[A](prefix, prefixBits) {
override def normalized: this.type = this
}

object Strict {
def apply[A <: IpAddress](cidr: Cidr[A]): Cidr.Strict[A] = cidr match {
case already: Strict[_] => already.asInstanceOf[Strict[A]]
case _ => new Cidr.Strict(cidr.prefix, cidr.prefixBits)
}
}

/** Constructs a CIDR from the supplied IP address and prefix bit count. Note if `prefixBits` is less than 0, the
* built `Cidr` will have `prefixBits` set to 0. Similarly, if `prefixBits` is greater than the bit length of the
* address, it will be set to the bit length of the address.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ object Arbitraries {
implicit def cidrArbitrary[A <: IpAddress](implicit arbIp: Arbitrary[A]): Arbitrary[Cidr[A]] =
Arbitrary(cidrGenerator(arbIp.arbitrary))

implicit def cidrStrictArbitrary[A <: IpAddress](implicit arbIp: Arbitrary[A]): Arbitrary[Cidr.Strict[A]] = Arbitrary(
cidrGenerator(arbIp.arbitrary).map(_.normalized)
)

val portGenerator: Gen[Port] = Gen.chooseNum(0, 65535).map(Port.fromInt(_).get)

implicit val portArbitrary: Arbitrary[Port] = Arbitrary(portGenerator)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2018 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.comcast.ip4s

import org.scalacheck.Prop.forAll
import Arbitraries._

class CidrStrictTest extends BaseTestSuite {
property("prefix and address are identical") {
forAll { (cidr: Cidr.Strict[IpAddress]) => assertEquals(cidr.address, cidr.prefix) }
}
}

0 comments on commit e25d489

Please sign in to comment.