Implement NetAddr::CIDR#allocate_rfc3531 #14

Closed
bilco105 opened this Issue Apr 28, 2011 · 9 comments

Comments

Projects
None yet
2 participants
@bilco105

Hi,

We're currently using the NetAddr gem to provide our software with supernet, subnet and IP Address management. An example of this as follows:

ruby-1.9.2-p180 :004 > Supernet.create(:network => '192.168.0.0', :cidr => '20')
 => 192.168.0.0/20 
ruby-1.9.2-p180 :005 > Vlan.first.allocate_subnet(29)
 => 192.168.0.0/29 
ruby-1.9.2-p180 :006 > Vlan.first.allocate_subnet(28)
 => 192.168.0.16/28 
ruby-1.9.2-p180 :007 > Vlan.first.allocate_subnet(27)
 => 192.168.0.32/27 
ruby-1.9.2-p180 :008 > Vlan.first.allocate_subnet(29)
 => 192.168.0.8/29 
ruby-1.9.2-p180 :009 > 

We use the allocate_rfc351 function in NetADDR::CIDR to do this currently, but would like to use your library as it has a lot nicer API. Would you be able to duplicate the functionality in your better API?

Thanks

@bluemonk

This comment has been minimized.

Show comment Hide comment
@bluemonk

bluemonk Apr 29, 2011

Collaborator

Hello,

thanks for your compliments.

I didn't fully understand your example here, would you please go deeper into it? I feel it's an interesting function to add to the library.

Best would be to also have some tests to import directly in the code, so I will be sure to meet what you're expecting.

Best regards,
Marco

Collaborator

bluemonk commented Apr 29, 2011

Hello,

thanks for your compliments.

I didn't fully understand your example here, would you please go deeper into it? I feel it's an interesting function to add to the library.

Best would be to also have some tests to import directly in the code, so I will be sure to meet what you're expecting.

Best regards,
Marco

@bilco105

This comment has been minimized.

Show comment Hide comment
@bilco105

bilco105 Apr 29, 2011

Hi Marco,

Basically the above RFC is designed to allow you to slice up your supernets (/21, /20, /19 etc) effectively within an ISP.

A few examples of how it works are..;

ruby-1.9.2-p180 :001 > require 'netaddr'
 => true 
ruby-1.9.2-p180 :002 > cidr = NetAddr::CIDR.create('192.168.0.0/24')
 => 192.168.0.0/24 
ruby-1.9.2-p180 :003 > cidr.allocate_rfc3531(25).sort
 => ["192.168.0.0/25", "192.168.0.128/25"] 
ruby-1.9.2-p180 :004 > cidr.allocate_rfc3531(26).sort
 => ["192.168.0.0/26", "192.168.0.128/26", "192.168.0.192/26", "192.168.0.64/26"] 
ruby-1.9.2-p180 :005 > cidr.allocate_rfc3531(27).sort
 => ["192.168.0.0/27", "192.168.0.128/27", "192.168.0.160/27", "192.168.0.192/27", "192.168.0.224/27", "192.168.0.32/27", "192.168.0.64/27", "192.168.0.96/27"] 
ruby-1.9.2-p180 :006 > ruby-1.9.2-p180 :006 > cidr.allocate_rfc3531(28).count
 => 16 
ruby-1.9.2-p180 :007 > cidr.allocate_rfc3531(29).count
 => 32 
ruby-1.9.2-p180 :008 >

Hi Marco,

Basically the above RFC is designed to allow you to slice up your supernets (/21, /20, /19 etc) effectively within an ISP.

A few examples of how it works are..;

ruby-1.9.2-p180 :001 > require 'netaddr'
 => true 
ruby-1.9.2-p180 :002 > cidr = NetAddr::CIDR.create('192.168.0.0/24')
 => 192.168.0.0/24 
ruby-1.9.2-p180 :003 > cidr.allocate_rfc3531(25).sort
 => ["192.168.0.0/25", "192.168.0.128/25"] 
ruby-1.9.2-p180 :004 > cidr.allocate_rfc3531(26).sort
 => ["192.168.0.0/26", "192.168.0.128/26", "192.168.0.192/26", "192.168.0.64/26"] 
ruby-1.9.2-p180 :005 > cidr.allocate_rfc3531(27).sort
 => ["192.168.0.0/27", "192.168.0.128/27", "192.168.0.160/27", "192.168.0.192/27", "192.168.0.224/27", "192.168.0.32/27", "192.168.0.64/27", "192.168.0.96/27"] 
ruby-1.9.2-p180 :006 > ruby-1.9.2-p180 :006 > cidr.allocate_rfc3531(28).count
 => 16 
ruby-1.9.2-p180 :007 > cidr.allocate_rfc3531(29).count
 => 32 
ruby-1.9.2-p180 :008 >
@bilco105

This comment has been minimized.

Show comment Hide comment
@bilco105

bilco105 Apr 29, 2011

NetAddr::CIDR#cmp would also be a great feature to implement: http://netaddr.rubyforge.org/classes/NetAddr/CIDR.html#M000062

These are the features we use the most in NetAddr

NetAddr::CIDR#cmp would also be a great feature to implement: http://netaddr.rubyforge.org/classes/NetAddr/CIDR.html#M000062

These are the features we use the most in NetAddr

@bluemonk

This comment has been minimized.

Show comment Hide comment
@bluemonk

bluemonk May 3, 2011

Collaborator

Hello Rob,
I understand better now, thank you.

I think you may have a look at IPv4#subnet, as it does quite the same with a different interface. You need to specify the number of subnets rather than the new prefix, but the result is the same:

IPAddress("192.168.0.0/24").subnet(4).map {|i| i.to_string}
=> ["192.168.0.0/26", "192.168.0.64/26", "192.168.0.128/26", "192.168.0.192/26"]

If you want, you can still specify the new subnet with a workaround

irb(main):003:0> a = IPAddress("192.168.0.0/24")
=> #<IPAddress::IPv4:0xb768362c @address="192.168.0.0", @octets=[192, 168, 0, 0], @prefix=24, @u32=3232235520>
irb(main):004:0> a.subnet(2**(26-a.prefix.to_i)).map {|i| i.to_string}
=> ["192.168.0.0/26", "192.168.0.64/26", "192.168.0.128/26", "192.168.0.192/26"]

I will maybe include a new method in the next release to be able to specify the new prefix without the workaround.

Collaborator

bluemonk commented May 3, 2011

Hello Rob,
I understand better now, thank you.

I think you may have a look at IPv4#subnet, as it does quite the same with a different interface. You need to specify the number of subnets rather than the new prefix, but the result is the same:

IPAddress("192.168.0.0/24").subnet(4).map {|i| i.to_string}
=> ["192.168.0.0/26", "192.168.0.64/26", "192.168.0.128/26", "192.168.0.192/26"]

If you want, you can still specify the new subnet with a workaround

irb(main):003:0> a = IPAddress("192.168.0.0/24")
=> #<IPAddress::IPv4:0xb768362c @address="192.168.0.0", @octets=[192, 168, 0, 0], @prefix=24, @u32=3232235520>
irb(main):004:0> a.subnet(2**(26-a.prefix.to_i)).map {|i| i.to_string}
=> ["192.168.0.0/26", "192.168.0.64/26", "192.168.0.128/26", "192.168.0.192/26"]

I will maybe include a new method in the next release to be able to specify the new prefix without the workaround.

@bilco105

This comment has been minimized.

Show comment Hide comment
@bilco105

bilco105 May 3, 2011

Hi Marco,

The reason we need to do it the way NetADDR does it, is because we only know what size subnet someone wants (i.e. /29). We therefore take each of our supernets, make as many /29s out of them and loop through the individual subnets until we find one that's free, and then allocate that to the customer.

None of the work arounds really do the above functionality.

bilco105 commented May 3, 2011

Hi Marco,

The reason we need to do it the way NetADDR does it, is because we only know what size subnet someone wants (i.e. /29). We therefore take each of our supernets, make as many /29s out of them and loop through the individual subnets until we find one that's free, and then allocate that to the customer.

None of the work arounds really do the above functionality.

@bluemonk

This comment has been minimized.

Show comment Hide comment
@bluemonk

bluemonk May 3, 2011

Collaborator

Hi Rob,
there are some things that are not clear to me.

When you say "we", you mean the library (NetADDR) or your application using the library? And again, when you say "until we find one that's free", what does that mean?

Thanks for helping me understand better, I'm always interested in adding some nice functionality to IPAddress :)

Maybe, if you want, you can provide me with some unit tests that I can integrate in the library. It would me much clearer in this way.

Best regards,
Marco

Collaborator

bluemonk commented May 3, 2011

Hi Rob,
there are some things that are not clear to me.

When you say "we", you mean the library (NetADDR) or your application using the library? And again, when you say "until we find one that's free", what does that mean?

Thanks for helping me understand better, I'm always interested in adding some nice functionality to IPAddress :)

Maybe, if you want, you can provide me with some unit tests that I can integrate in the library. It would me much clearer in this way.

Best regards,
Marco

@bilco105

This comment has been minimized.

Show comment Hide comment
@bilco105

bilco105 May 3, 2011

Hi Marco,

Sorry, I'll clarify.

We have an application which is responsible for allocating subnets to customers. To do this, we assign supernets (/19, /20 etc) to the application. An administrator then selects a customers VLAN, and selects 'Allocate subnet' and requests a paticular size of subnet, i.e. a /29.

The code we use for this is as follows..

## Create a new subnet allocation of the desired size
def allocate_subnet(size)
  return false unless size.is_a?(Integer)

  Supernet.all.each do |supernet|
    allocated_subnets = supernet.subnets.all.map(&:cidr_object) ## Returns an array of NetADDR::CIDR object for each allocated subnet
    NetAddr::CIDR.create(supernet.to_s).allocate_rfc3531(size, :Objectify => true).sort.each do |potential_subnet|
      free_subnet = true
      allocated_subnets.each do |allocated_subnet|
        free_subnet = false unless potential_subnet.cmp(allocated_subnet).nil?
      end
      if free_subnet
        return self.subnets.create!(:network => potential_subnet.network, :cidr => size, :supernet => supernet)
      end
    end
  end
  return nil
end

This basically loops through each Supernet, making as many /29s or whatever subnet size was requested out of it. It then loops through each created subnet and checks whether the network IP address has been used in another subnet. If not, then we deem that subnet to be free and allocate it to the customer.

I'm intending to write tests for our application this afternoon, so should be able to provide tests which show the functionality we're after.

bilco105 commented May 3, 2011

Hi Marco,

Sorry, I'll clarify.

We have an application which is responsible for allocating subnets to customers. To do this, we assign supernets (/19, /20 etc) to the application. An administrator then selects a customers VLAN, and selects 'Allocate subnet' and requests a paticular size of subnet, i.e. a /29.

The code we use for this is as follows..

## Create a new subnet allocation of the desired size
def allocate_subnet(size)
  return false unless size.is_a?(Integer)

  Supernet.all.each do |supernet|
    allocated_subnets = supernet.subnets.all.map(&:cidr_object) ## Returns an array of NetADDR::CIDR object for each allocated subnet
    NetAddr::CIDR.create(supernet.to_s).allocate_rfc3531(size, :Objectify => true).sort.each do |potential_subnet|
      free_subnet = true
      allocated_subnets.each do |allocated_subnet|
        free_subnet = false unless potential_subnet.cmp(allocated_subnet).nil?
      end
      if free_subnet
        return self.subnets.create!(:network => potential_subnet.network, :cidr => size, :supernet => supernet)
      end
    end
  end
  return nil
end

This basically loops through each Supernet, making as many /29s or whatever subnet size was requested out of it. It then loops through each created subnet and checks whether the network IP address has been used in another subnet. If not, then we deem that subnet to be free and allocate it to the customer.

I'm intending to write tests for our application this afternoon, so should be able to provide tests which show the functionality we're after.

@bluemonk

This comment has been minimized.

Show comment Hide comment
@bluemonk

bluemonk May 5, 2011

Collaborator

Hello Rob,
I've installed NetAddr and played a little with it.

It seems to me that NetAddr::CIDR#allocate_rfc3531 and IPAddress::IPv4#subnet do exactly the same thing.

If you want the same interface, you can do

module IPAddress
  class IPv4
    def allocate_rfc3531(new_prefix)
      subnet(2**(new_prefix-prefix.to_i))
    end
  end
end

Then you can call it like you used to do

> a = IPAddress("192.168.0.0/24")
 => 192.168.0.0 
> a.allocate_rfc3531(26)
 => [192.168.0.0, 192.168.0.64, 192.168.0.128, 192.168.0.192] 
Collaborator

bluemonk commented May 5, 2011

Hello Rob,
I've installed NetAddr and played a little with it.

It seems to me that NetAddr::CIDR#allocate_rfc3531 and IPAddress::IPv4#subnet do exactly the same thing.

If you want the same interface, you can do

module IPAddress
  class IPv4
    def allocate_rfc3531(new_prefix)
      subnet(2**(new_prefix-prefix.to_i))
    end
  end
end

Then you can call it like you used to do

> a = IPAddress("192.168.0.0/24")
 => 192.168.0.0 
> a.allocate_rfc3531(26)
 => [192.168.0.0, 192.168.0.64, 192.168.0.128, 192.168.0.192] 
@bilco105

This comment has been minimized.

Show comment Hide comment
@bilco105

bilco105 May 5, 2011

Yep, it's pretty much just a different API, without needing to know how many subnets you want, which is ideal for us. Is this something you'd look to add into your library, or should we just extend it?

We'd also like something the same as NetAddr::CIDR#cmp if at all possible, as we use that widely too.

bilco105 commented May 5, 2011

Yep, it's pretty much just a different API, without needing to know how many subnets you want, which is ideal for us. Is this something you'd look to add into your library, or should we just extend it?

We'd also like something the same as NetAddr::CIDR#cmp if at all possible, as we use that widely too.

@bluemonk bluemonk closed this in 8599c87 May 15, 2011

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment