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

IP address ranges #834

Closed
candlerb opened this issue Jan 23, 2017 · 37 comments · Fixed by #6760
Closed

IP address ranges #834

candlerb opened this issue Jan 23, 2017 · 37 comments · Fixed by #6760
Assignees
Labels
status: accepted This issue has been accepted for implementation type: feature Introduction of new functionality to the application
Milestone

Comments

@candlerb
Copy link
Contributor

candlerb commented Jan 23, 2017

Feature idea: support for explicit "IP ranges"

An IP range would have a start address and an end address attribute, a name, and a VRF. It would be independent from IP prefixes.

Use case 1: documentation of ranges within a prefix. For example, in our current dumb spreadsheet we have assigned logical ranges for user assignment

  • 10.10.2.151 - 10.10.2.160 : Tester range
  • 10.10.2.180 - 10.10.2.253 : Admin/Developer range

and I prefer not to lose that information when moving to Netbox.

Use case 2 (related to #761): a cleaner way of defining IP pools, rather than enumerating all addresses within a range and setting their status individually to "DHCP Pool". The current model wouldn't work very well if you wanted a large IPv6 pool. Also, generating a DHCP config from this involves iterating over all the addresses and collecting adjacent ones together to form a single range, which is messy.

Use case 2a: for redundancy you have two DHCP servers on a subnet with separate ranges, e.g. DHCP1 has pool .50-.149 and DHCP2 has pool .150-.249

Use case 3 (related to #665): be able to automatically assign the next available address from within a specific range. For example: suppose your address plan for a /24 reserves .1-.9 and .250-.254 for infrastructure. So you define a named range .10-.249 and then on demand assign the next available address from within that range. This would be particularly helpful for automatic assignment via an API.

This last case can also be handled by marking addresses unused addresses in the .1-.9 and .250-.254 ranges as status "reserved". That may be reasonable for IPv4, but might not work well for IPv6, e.g. if your plan had

  • ::0 to ::fff - reserved for infrastructure
  • ::1000 to ::ffff - available for assignment

It also doesn't let you assign from two or more ranges within the same prefix - e.g. going back to use case 1, "assign next Tester address" or "assign next Admin/Developer address"

@jeremystretch jeremystretch changed the title [Feature] IP ranges IP address ranges Jan 23, 2017
@jeremystretch
Copy link
Member

Let's assume have defined 192.0.2.100 through 192.0.2.199 (inclusive) as a DHCP pool. How would you handle a scenario where 192.0.2.123 has become statically assigned and needs to be removed from the pool?

The current model wouldn't work very well if you wanted a large IPv6 pool.

Why wouldn't it work? (I've never used DHCPv6.)

Also, generating a DHCP config from this involves iterating over all the addresses and collecting adjacent ones together to form a single range, which is messy.

The netaddr library (which NetBox uses for IP calculations) makes this super easy.

@candlerb
Copy link
Contributor Author

candlerb commented Jan 23, 2017

Let's assume have defined 192.0.2.100 through 192.0.2.199 (inclusive) as a DHCP pool. How would you handle a scenario where 192.0.2.123 has become statically assigned and needs to be removed from the pool?

Well, I can think of two scenarios.

  1. The statically assigned address is already known to the DHCP server (e.g. via a fixed MAC address to IP address mapping). In this case there is no need to shrink the pool, it can still span the assigned address [^1]

  2. The statically assigned address was was someone stupidly putting a statically-assigned piece of equipment in the middle of a DHCP pool. In this case your options are:

    2a. Same as 1: tell the DHCP server about the static allocation. It should be fine to skip it.
    2b. At worst, split the pool into two pools:

    range 192.0.2.100 192.0.2.122;
    range 192.0.2.124 192.0.2.199;
    

But this doesn't mean you need to create two separate "IP Range" objects in the database.

Let's assume the worst case, and your DHCP server does not support reserving individual addresses. Then, when generating the config, you read out an IP Range object from the database, covering the range 192.0.2.100 to 192.0.2.199

You then also query the database for all Active IP objects which sit within the range. Suppose you find 192.0.2.123 and 192.0.2.150. Then you can easily output the broken-up ranges:

  • 192.0.2.100 to 192.0.2.122
  • 192.0.2.124 to 192.0.2.149
  • 192.0.2.151 to 192.0.2.199

The only difference between this and the current model is:

  • In the current model you create all addresses from 192.0.2.100 to 192.0.2.199 with status "DHCP Pool", and then change .123 and .150 to status "Active"
  • In the range model, you create a range from 192.0.2.100 to 192.0.2.199, and create .123 and .150 with status "Active", or a new status like "DHCP Excluded"

The current model wouldn't work very well if you wanted a large IPv6 pool.

Why wouldn't it work? (I've never used DHCPv6.)

If you want a pool from 2001:db8::1000 to 2001:db8::1fff, you need to add 4,096 separate entries into your database. If you want a pool from 2001:db8::1000 to 2001:db8::ffff, you need to add 64,512 database records.

This is an inefficient way to represent a pool, and will badly clutter the user interface.

Also, generating a DHCP config from this involves iterating over all the addresses and collecting adjacent ones together to form a single range, which is messy.

The netaddr library (which NetBox uses for IP calculations) makes this super easy.

It still involves a large database query to read all the addresses where status = DHCP Pool.


[^1] At least, I'm pretty sure this works for sane DHCP servers like ISC dhcpd.

The documentation says:

Any IPv6 addresses given to hosts with fixed-address6 are excluded from the range6, as are IPv6 addresses on the server itself.

Admittedly, I can't see an equivalent statement for an ipv4 range, but I very much doubt it allocates a fixed-address to any random device. I've not tested it though. I use static mappings for DHCP all the time, but I pick addresses outside of the pool for clarity.

@candlerb
Copy link
Contributor Author

You then also query the database for all Active IP objects which sit within the range. Suppose you find 192.0.2.123 and 192.0.2.150. Then you can easily output the broken-up ranges

Oh, and this is super-easy too :-)

>>> s = netaddr.IPSet(netaddr.IPRange('192.0.2.100','192.0.2.199'))
>>> list((s - netaddr.IPSet(['192.0.2.123','192.0.2.150'])).iter_ipranges())
[IPRange('192.0.2.100', '192.0.2.122'), IPRange('192.0.2.124', '192.0.2.149'), IPRange('192.0.2.151', '192.0.2.199')]

@jeremystretch
Copy link
Member

Same as 1: tell the DHCP server about the static allocation.

NetBox is responsible for telling the DHCP server about the allocation. It should be possible to generate a DHCP server configuration from scratch using only the information available in NetBox (at least, that's the philosophy).

The implementation of IP ranges boils down to incurring additional processing logic at read time versus additional storage in the database. At the moment, I don't think the trade-off is worth it for IPv4 alone: It's usually going to be safer and more efficient to store discrete IPs directly, rather than storing a range, converting it to discrete IPs, and coalescing it with other ranges and/or sets of IPs. It may be worthwhile given the potentially overwhelming size of IPv6 pools, but that doesn't seem like it'd be a very common use case. Specifically, we're talking about a scenario wherein a) DHCPv6 is in use rather than SLAAC, and b) pools much larger than their IPv4 equivalents are required.

@candlerb
Copy link
Contributor Author

a) DHCPv6 is in use rather than SLAAC

SLAAC is an operational nightmare, due to constantly-changing privacy addresses. DHCPv6 addresses stick with the client for as long as it has a lease. Hence I disable SLAAC everywhere and use DHCPv6 exclusively.

b) pools much larger than their IPv4 equivalents are required.

In a campus network, an IPv4 pool of /22 (of private IPs) is fairly reasonable for a wifi network where users are constantly walking in and out of range, and you don't want to use a ridiculously low lease time. You might have a subnet like that per building.

For IPv6, a pool from ::1000 to ::1fff is a reasonable minimum to avoid any risk of pool exhaustion.

@explody
Copy link
Contributor

explody commented Dec 15, 2017

I'll chime in that I don't think this is purely a v4 vs v6 (i.e. quantity of IPs) issue. IMHO, it's about manageability of IPs inside of subnet boundaries.

Let's assume have defined 192.0.2.100 through 192.0.2.199 (inclusive) as a DHCP pool. How would you handle a scenario where 192.0.2.123 has become statically assigned and needs to be removed from the pool?

Tell whichever engineer not to allocate IPs from a DHCP range ;)
At some point though, one can't wholly protect a system from someone who can write to it. More realistically though, you could disallow assignment of IPs from a defined range.

The netaddr library (which NetBox uses for IP calculations) makes this super easy.

Regarding this - netaddr is great, but consider @candlerb's example of multiple assignments inside a range of IPs one is trying to build into a DHCP range - there's no beginning or end to the range, so how do you validate if the assignment is supposed to be inside the "range"? As shown, one ends up with N DHCP pools, fractured against the "rogue" assigned IPs, instead of one well-defined, construct with contiguous member IPs. Generating and managing configs from data like this is what scares me - not IP calculations. ( also, how do you know the clients are even python? ;) )

All this being said, I'm not 100% sure that we must have ranges as described, but I am certain that there isn't enough metadata about addresses, by default, to work around their absence. For example, we can derive this about an address:

  • Status (includes one "DHCP" option)
  • Whether it's assigned to an interface or not
  • Role (not useful in this context unless it were expanded to more roles)

In our case, to at least distinguish between DHCP reservation ranges and dynamic ranges, we have modified the Status options so we can represent IPs in more states than currently available. So now, we have "DHCP dynamic" and "DHCP reservation" and I rely on trout-slapping my engineers if they hand out IPs with the wrong status - it solves one problem for our case but monkeypatching isn't a great pattern.

Ranges, in contrast, would be multifunction - they can define series of IPs that should or should not be used for any particular purpose, and could potentially affect behavior (read-only ranges) or hold more data about their member IPs without cluttering the Address model. I'd envision them as many-to-one from address to range - no nesting, no membership in multiple ranges, no range overlaps, keep the queries low and keep changes to the address model to a minimum.

For some completely different angles:,

  • Tagging could_almost_ achieve the same thing while being entirely freeform, and still pushing most logic out to the consumer (e.g. "Status: DHCP Tags: Dynamic" or "Status: Reserved Tags: Infrastructure").
  • Make Statuses and Roles user-extensible - perhaps combos of customizable fields would obviate needing additional constructs (and would be way faster than custom fields)

If any of these approaches are appealing, I'm happy to invest work on them.

@jeremystretch jeremystretch added the status: accepted This issue has been accepted for implementation label Jan 26, 2018
@Wouter0100
Copy link

I'm also in search for such a feature, where I'm able to easily assign a range of IP adresses to machines without creating all of them (especially for IPv6).

For example, when a customer leases an additional IP subnet, I'd like to assign all those IP's to his interface (except the gateway) so they show up as "reserved" or "active" and count towards my utilization.

Currently I use the import feature to import a handcrafted CSV list, which adds the IPv4 addresses and links it with the interface - but for IPv6 this isn't really an option..

@sajaltiwari
Copy link

+1
We need this feature for reserving some ip for ex: broadcast ip and Network IP.
If we reserve an IP, other user can change it by mistake.

@candlerb
Copy link
Contributor Author

candlerb commented Oct 9, 2018

As a separate issue, I wonder if Netbox ought to avoid letting users create network or broadcast IP, unless the prefix is /31 or /32 or the role is special like anycast?

@athompson-merlin
Copy link

Adding my $0.02 - ranges are necessary to document existing configuration/behaviour where the DHCP (in particular) range does not fall on any bit boundary, e.g. our convention is to have x.x.x.100-x.x.x.199 handed out via DHCP. I don't care what's in there, but I do care that it's DHCP. I also care, when using Netbox' IPAM features to find the next available address, that the user doesn't think .100 is available for use!
N.b. I'm not generating configs using Netbox, I'm just using it for documentation.

@bluikko
Copy link
Contributor

bluikko commented Apr 5, 2020

Found this issue while pondering how to model our IPAM case.

"Ranges" would be useful for documenting "parts" of a subnet for many things other than DHCP. Subnets may be logically split into different purposes, say inside a /24 the lower /25 would be used for x and upper /25 used for y.

Currently I have created dummy subnets (/25s continuing the above example), but this actually breaks when starting to use the Netbox API for automation. I guess I could somehow mark those dummy subnets and filter them out but I am clearly not alone in needing a proper way to do it.

In short, I would also need this feature for creating logical "ranges" inside subnets for strictly documentation purposes (that's what Netbox is for after all?).

Edit: I ended up putting the dummy subnets into a special VRF in Netbox. That is obviously not optimal and the dummy subnets have other problems.

@bluikko
Copy link
Contributor

bluikko commented Jun 19, 2020

The "range" functionality would be useful for VLANs also.

@jeremystretch jeremystretch added type: feature Introduction of new functionality to the application needs milestone Awaiting prioritization for inclusion with a future NetBox release and removed type: minor feature status: accepted This issue has been accepted for implementation labels Jul 24, 2020
@jscmenezes
Copy link

This feature is necessary to represent DHCP. Create DHCP Pools, independently of the Prefix is a common task for network admins.

@jeremystretch
Copy link
Member

@jscmenezes DHCP pools can already be represented by creating the appropriate set of individual IP addresses and setting their status to DHCP.

@jeremystretch
Copy link
Member

It looks like this feature request needs to be revisited, as a concrete database model for representing IP ranges has not yet been proposed. I get the feeling different people have different ideas of what this entails. Happy to continue the discussion here, or reboot the discussion in a new issue. Either way, we'll need to nail down exactly what changes are being proposed before work can commence.

@jeremystretch jeremystretch added status: under review Further discussion is needed to determine this issue's scope and/or implementation and removed API change needs milestone Awaiting prioritization for inclusion with a future NetBox release labels Dec 21, 2020
@stale
Copy link

stale bot commented Mar 19, 2021

This issue has been automatically closed due to lack of activity. In an effort to reduce noise, please do not comment any further. Note that the core maintainers may elect to reopen this issue at a later date if deemed necessary.

@stale stale bot closed this as completed Mar 19, 2021
@th0mcat
Copy link

th0mcat commented Apr 8, 2021

I think child pools and subnets should be nested under a parent subnet as "types" of a range. For example, once you're viewing the Prefixes page, clicking on an arrow beside 10.134.204.0/22 would open a pull down and display information about ranges .

10.134.204.0/22 
        Type Range Status Utilization ...
	Pool 10.134.204.2-32 Active 75%
	Pool 10.134.204.33-64 Active 15%
	Pool 10.134.204.65-127 Active 85%
	Subnet 10.134.204.128/25
	Pool 10.134.205.3-75 Active 75%
	Pool 10.134.205.76-108 Active 75%
	Unused 10.134.205.109-10.134.206.3 0%
	Pool 10.134.206.4-111 Active 75%
	Pool 10.134.206.112-254 Active 75%
	Pool 10.134.206.255-10.134.207.126 Active 75%
	Pool 10.134.207.127-150 Active 75%
	Pool 10.134.207.151-254 Active 75%

Clicking on the range would then send you to https:///ipam/prefixes/<#>/ip-addresses/ where you can see individual IP usage.

@marioland
Copy link

Being a IPAM tool this feature to support IP ranges is needed to get a more complete picture in this sense.

@jeremystretch jeremystretch removed the pending closure Requires immediate attention to avoid being closed for inactivity label Jun 4, 2021
@jeremystretch
Copy link
Member

I'd like to revisit the ideas here. It would be a great addition to the IPAM component, however we need to devise a workable model.

@jeremystretch jeremystretch reopened this Jun 4, 2021
@athompson-merlin
Copy link

I see two options:

  1. make ranges an object subsidiary to subnets, that can overlap with hosts but don't have to, or
  2. make ranges a sort of UI syntactic sugar, where they really exist as a set of n host objects but there's an "annotation" (in Java terms) to cause the UI to display the range by default as a collapsed... um... range. object that isn't really an object. Or better yet, make it a sparse thing, along these same UI lines. So the range isn't really part of the core data model at all here, but it does need to be tracked.

I have clients who have assigned ranges 192.168.10-19, .20-29, .30-39, .40-49 etc. for each site. There's no sane way to represent that in most IPAM tools other than a set of (usually) three subnets of different length. Or 10x /24s, which is a PITA and doesn't help me understand the allocation philosophy (*cough*) when I look at it in an IPAM tool.

@candlerb
Copy link
Contributor Author

candlerb commented Jun 4, 2021

From a data model point of view, I think it's just:

  • start address
  • end address (inclusive, i.e. closed interval)
  • basic fields like IPAddress: VRF, description, status
  • name/slug (optional)
  • custom fields

This would be equivalent to netaddr.IPRange, and would require the same internal model validation: i.e. end >= start, same address family.

I also think that the range should have an "allocatable" flag which, if enabled, gives the option to click to allocate an address within the range - in the same way as you can already click on a synthetic range of non-existent addresses in the UI. In that way, you can mark a range as being for a particular allocation purpose (e.g. "IP Phones"), whilst having other ranges which are reserved/DHCP. However that wouldn't prevent you from explicitly creating an IP address within the range. (At the end of the day, Netbox requires you to know what you're doing).

Open questions include:

  • What the UI looks like
  • Validation with respect to other ranges: can ranges overlap partially or totally? If such overlaps are completely disallowed then it most likely simplifies the UI logic
  • How and if ranges are counted towards prefix utilization (maybe that's also a flag on the range, otherwise certain status values like 'Reserved' or 'DHCP' can be treated as utilized)
  • Any API changes: e.g. it would be a nice-to-have feature to be able to request allocation of an IP from within a named range. But this doesn't need to be implemented on day one.

@marioland
Copy link

From what I came in contact with IP ranges I saw 2 use cases:

  1. DHCP ranges
  2. In Firewall to select only the servers and exclude gateway and broadcast addresses

Data model-wise I would have to comments:

  • An IP range should have a relation to the prefix it is part of
  • No VRF on the IP range as the VRF should be inherited from the prefix

@candlerb
Copy link
Contributor Author

candlerb commented Jun 7, 2021

I disagree with that. In the Netbox data model there is no explicit linkage between IPAddress and Prefix, so I think it would be consistent to treat IPRanges the same way.

@marioland
Copy link

Your are right. IPAddress is only possibly linked via the VRF to each other. In order to be consistent we should do it the same way for the IPRange.

I am just wondering do you know the reason to this design decision?
It seems to me a logical relation that an IP can be directly part of a prefix. These OSI layer 3 objects do have a relation in its nature.
In larger environments were we need to manage overlapping IP scopes it is good to be able to separate the environments. E.g. overlay/underlay/K8S-internal networks etc.
There we have the 2 options of VRFs or tenants as far as I know.

@jeremystretch
Copy link
Member

I think @candlerb's rough proposal above is spot on. As for the open questions:

What the UI looks like

Something I've been stuck on for a long time is how to integrate IP ranges with individual addresses e.g. in a prefix's child IPs list. But maybe I'm overthinking it. Just having the IP range model in place, even without any especially convenient integration with individual IPs, is pretty valuable.

Validation with respect to other ranges: can ranges overlap partially or totally? If such overlaps are completely disallowed then it most likely simplifies the UI logic

I think we should disallow overlaps within a VRF entirely, since I expect that to be almost always the desired behavior, and as you mention it greatly simplifies validation.

How and if ranges are counted towards prefix utilization

I suggest that they follow the same rules as individual IPs.

Any API changes: e.g. it would be a nice-to-have feature to be able to request allocation of an IP from within a named range. But this doesn't need to be implemented on day one.

The basic REST API functionality should be pretty straightforward; I don't see us trying to integrate with the IPAddress or Prefix endpoints directly at all. We should be able to provide a "next available" feature as exists for prefixes today.

If we accept the introduction of IPRange as a new model separate from the Prefix and IPAddress models, with its own parallel views and API endpoints, this becomes very straightforward to implement, and I'd be open to tackling it for v3.0.

@candlerb
Copy link
Contributor Author

How and if ranges are counted towards prefix utilization

I suggest that they follow the same rules as individual IPs.

I can think of use cases for both:

  • If a range is a "DHCP range", then you'd certainly want it to be counted towards utilization
  • If a range is a range from which other IPs are manually allocated (say "switches" or "printers") then you arguably might not want it to count towards total utilization.

But in practice, I think that counting it towards utilization is fine. If your subnet is 95% used, but that's because you've reserved a bunch of address for switches and for printers, then that's an accurate view (i.e. only 5% is available for general allocation).

And equally: even if your prefix is only 50% used, you might have reached 100% within your switches range, or 100% within your printers range; then actually you're "full" for those purposes.

This makes me think of a couple of things:

  1. When viewing a range, it would be helpful to see its utilization figure (i.e. IPs within that range)
  2. I think that there should be no enforcement of name uniqueness. Being able to create multiple ranges with the same name is equivalent to creating "disjoint ranges". Application: say you have .100-.109 reserved for printers, and you use it all up, you might want to add a second range for printers.

From the API point of view, I think it's desiriable to scope requests within a prefix, so you can say "allocate me an IP from the 'printers' range within prefix 10.11.12.0/24". The server would filter the matching ranges to exclude any outside the given prefix.

@jeremystretch
Copy link
Member

We could always punt the problem to the user by including a "counts as utilized space" boolean on the IPRange model.

I think that there should be no enforcement of name uniqueness.

How about sticking with an (optional) description instead of a name, like we do with prefixes and IP addresses?

@candlerb
Copy link
Contributor Author

We could always punt the problem to the user by including a "counts as utilized space" boolean on the IPRange model.

That'll work too.

I think that there should be no enforcement of name uniqueness.

How about sticking with an (optional) description instead of a name, like we do with prefixes and IP addresses?

I think ranges need a name, so you can make an API request which is "assign me an IP address from range Foo". slug would work too, except that would have to be unique.

@jeremystretch jeremystretch added needs milestone Awaiting prioritization for inclusion with a future NetBox release and removed status: under review Further discussion is needed to determine this issue's scope and/or implementation labels Jul 6, 2021
@jeremystretch jeremystretch added status: accepted This issue has been accepted for implementation and removed needs milestone Awaiting prioritization for inclusion with a future NetBox release labels Jul 16, 2021
@jeremystretch jeremystretch added this to the v3.0 milestone Jul 16, 2021
@jeremystretch jeremystretch self-assigned this Jul 16, 2021
jeremystretch added a commit that referenced this issue Jul 19, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 18, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
status: accepted This issue has been accepted for implementation type: feature Introduction of new functionality to the application
Projects
None yet
Development

Successfully merging a pull request may close this issue.