Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement NetAddr::CIDR#allocate_rfc3531 #14

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

Implement NetAddr::CIDR#allocate_rfc3531 #14

bilco105 opened this issue Apr 28, 2011 · 9 comments

Comments

@bilco105
Copy link

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
Copy link
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

@bilco105
Copy link
Author

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
Copy link
Author

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
Copy link
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
Copy link
Author

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
Copy link
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
Copy link
Author

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
Copy link
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
Copy link
Author

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.

TheHolyRoger pushed a commit to whatagem/ipaddress that referenced this issue Oct 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants