| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- // Copyright 2020 The Inet.Af AUTHORS. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package netaddr
- import (
- "fmt"
- "runtime"
- "sort"
- "strings"
- )
- // IPSetBuilder builds an immutable IPSet.
- //
- // The zero value is a valid value representing a set of no IPs.
- //
- // The Add and Remove methods add or remove IPs to/from the set.
- // Removals only affect the current membership of the set, so in
- // general Adds should be called first. Input ranges may overlap in
- // any way.
- //
- // Most IPSetBuilder methods do not return errors.
- // Instead, errors are accumulated and reported by IPSetBuilder.IPSet.
- type IPSetBuilder struct {
- // in are the ranges in the set.
- in []IPRange
- // out are the ranges to be removed from 'in'.
- out []IPRange
- // errs are errors accumulated during construction.
- errs multiErr
- }
- // normalize normalizes s: s.in becomes the minimal sorted list of
- // ranges required to describe s, and s.out becomes empty.
- func (s *IPSetBuilder) normalize() {
- const debug = false
- if debug {
- debugf("ranges start in=%v out=%v", s.in, s.out)
- }
- in, ok := mergeIPRanges(s.in)
- if !ok {
- return
- }
- out, ok := mergeIPRanges(s.out)
- if !ok {
- return
- }
- if debug {
- debugf("ranges sort in=%v out=%v", in, out)
- }
- // in and out are sorted in ascending range order, and have no
- // overlaps within each other. We can run a merge of the two lists
- // in one pass.
- min := make([]IPRange, 0, len(in))
- for len(in) > 0 && len(out) > 0 {
- rin, rout := in[0], out[0]
- if debug {
- debugf("step in=%v out=%v", rin, rout)
- }
- switch {
- case !rout.IsValid() || !rin.IsValid():
- // mergeIPRanges should have prevented invalid ranges from
- // sneaking in.
- panic("invalid IPRanges during Ranges merge")
- case rout.entirelyBefore(rin):
- // "out" is entirely before "in".
- //
- // out in
- // f-------t f-------t
- out = out[1:]
- if debug {
- debugf("out before in; drop out")
- }
- case rin.entirelyBefore(rout):
- // "in" is entirely before "out".
- //
- // in out
- // f------t f-------t
- min = append(min, rin)
- in = in[1:]
- if debug {
- debugf("in before out; append in")
- debugf("min=%v", min)
- }
- case rin.coveredBy(rout):
- // "out" entirely covers "in".
- //
- // out
- // f-------------t
- // f------t
- // in
- in = in[1:]
- if debug {
- debugf("in inside out; drop in")
- }
- case rout.inMiddleOf(rin):
- // "in" entirely covers "out".
- //
- // in
- // f-------------t
- // f------t
- // out
- min = append(min, IPRange{from: rin.from, to: rout.from.Prior()})
- // Adjust in[0], not ir, because we want to consider the
- // mutated range on the next iteration.
- in[0].from = rout.to.Next()
- out = out[1:]
- if debug {
- debugf("out inside in; split in, append first in, drop out, adjust second in")
- debugf("min=%v", min)
- }
- case rout.overlapsStartOf(rin):
- // "out" overlaps start of "in".
- //
- // out
- // f------t
- // f------t
- // in
- in[0].from = rout.to.Next()
- // Can't move ir onto min yet, another later out might
- // trim it further. Just discard or and continue.
- out = out[1:]
- if debug {
- debugf("out cuts start of in; adjust in, drop out")
- }
- case rout.overlapsEndOf(rin):
- // "out" overlaps end of "in".
- //
- // out
- // f------t
- // f------t
- // in
- min = append(min, IPRange{from: rin.from, to: rout.from.Prior()})
- in = in[1:]
- if debug {
- debugf("merge out cuts end of in; append shortened in")
- debugf("min=%v", min)
- }
- default:
- // The above should account for all combinations of in and
- // out overlapping, but insert a panic to be sure.
- panic("unexpected additional overlap scenario")
- }
- }
- if len(in) > 0 {
- // Ran out of removals before the end of in.
- min = append(min, in...)
- if debug {
- debugf("min=%v", min)
- }
- }
- s.in = min
- s.out = nil
- }
- // Clone returns a copy of s that shares no memory with s.
- func (s *IPSetBuilder) Clone() *IPSetBuilder {
- return &IPSetBuilder{
- in: append([]IPRange(nil), s.in...),
- out: append([]IPRange(nil), s.out...),
- }
- }
- func (s *IPSetBuilder) addError(msg string, args ...interface{}) {
- se := new(stacktraceErr)
- // Skip three frames: runtime.Callers, addError, and the IPSetBuilder
- // method that called addError (such as IPSetBuilder.Add).
- // The resulting stack trace ends at the line in the user's
- // code where they called into netaddr.
- n := runtime.Callers(3, se.pcs[:])
- se.at = se.pcs[:n]
- se.err = fmt.Errorf(msg, args...)
- s.errs = append(s.errs, se)
- }
- // Add adds ip to s.
- func (s *IPSetBuilder) Add(ip IP) {
- if ip.IsZero() {
- s.addError("Add(IP{})")
- return
- }
- s.AddRange(IPRangeFrom(ip, ip))
- }
- // AddPrefix adds all IPs in p to s.
- func (s *IPSetBuilder) AddPrefix(p IPPrefix) {
- if r := p.Range(); r.IsValid() {
- s.AddRange(r)
- } else {
- s.addError("AddPrefix(%v/%v)", p.IP(), p.Bits())
- }
- }
- // AddRange adds r to s.
- // If r is not Valid, AddRange does nothing.
- func (s *IPSetBuilder) AddRange(r IPRange) {
- if !r.IsValid() {
- s.addError("AddRange(%v-%v)", r.From(), r.To())
- return
- }
- // If there are any removals (s.out), then we need to compact the set
- // first to get the order right.
- if len(s.out) > 0 {
- s.normalize()
- }
- s.in = append(s.in, r)
- }
- // AddSet adds all IPs in b to s.
- func (s *IPSetBuilder) AddSet(b *IPSet) {
- if b == nil {
- return
- }
- for _, r := range b.rr {
- s.AddRange(r)
- }
- }
- // Remove removes ip from s.
- func (s *IPSetBuilder) Remove(ip IP) {
- if ip.IsZero() {
- s.addError("Remove(IP{})")
- } else {
- s.RemoveRange(IPRangeFrom(ip, ip))
- }
- }
- // RemovePrefix removes all IPs in p from s.
- func (s *IPSetBuilder) RemovePrefix(p IPPrefix) {
- if r := p.Range(); r.IsValid() {
- s.RemoveRange(r)
- } else {
- s.addError("RemovePrefix(%v/%v)", p.IP(), p.Bits())
- }
- }
- // RemoveRange removes all IPs in r from s.
- func (s *IPSetBuilder) RemoveRange(r IPRange) {
- if r.IsValid() {
- s.out = append(s.out, r)
- } else {
- s.addError("RemoveRange(%v-%v)", r.From(), r.To())
- }
- }
- // RemoveSet removes all IPs in o from s.
- func (s *IPSetBuilder) RemoveSet(b *IPSet) {
- if b == nil {
- return
- }
- for _, r := range b.rr {
- s.RemoveRange(r)
- }
- }
- // removeBuilder removes all IPs in b from s.
- func (s *IPSetBuilder) removeBuilder(b *IPSetBuilder) {
- b.normalize()
- for _, r := range b.in {
- s.RemoveRange(r)
- }
- }
- // Complement updates s to contain the complement of its current
- // contents.
- func (s *IPSetBuilder) Complement() {
- s.normalize()
- s.out = s.in
- s.in = []IPRange{
- IPPrefix{ip: IPv4(0, 0, 0, 0), bits: 0}.Range(),
- IPPrefix{ip: IPv6Unspecified(), bits: 0}.Range(),
- }
- }
- // Intersect updates s to the set intersection of s and b.
- func (s *IPSetBuilder) Intersect(b *IPSet) {
- var o IPSetBuilder
- o.Complement()
- o.RemoveSet(b)
- s.removeBuilder(&o)
- }
- func discardf(format string, args ...interface{}) {}
- // debugf is reassigned by tests.
- var debugf = discardf
- // IPSet returns an immutable IPSet representing the current state of s.
- //
- // Most IPSetBuilder methods do not return errors.
- // Rather, the builder ignores any invalid inputs (such as an invalid IPPrefix),
- // and accumulates a list of any such errors that it encountered.
- //
- // IPSet also reports any such accumulated errors.
- // Even if the returned error is non-nil, the returned IPSet is usable
- // and contains all modifications made with valid inputs.
- //
- // The builder remains usable after calling IPSet.
- // Calling IPSet clears any accumulated errors.
- func (s *IPSetBuilder) IPSet() (*IPSet, error) {
- s.normalize()
- ret := &IPSet{
- rr: append([]IPRange{}, s.in...),
- }
- if len(s.errs) == 0 {
- return ret, nil
- } else {
- errs := s.errs
- s.errs = nil
- return ret, errs
- }
- }
- // IPSet represents a set of IP addresses.
- //
- // IPSet is safe for concurrent use.
- // The zero value is a valid value representing a set of no IPs.
- // Use IPSetBuilder to construct IPSets.
- type IPSet struct {
- // rr is the set of IPs that belong to this IPSet. The IPRanges
- // are normalized according to IPSetBuilder.normalize, meaning
- // they are a sorted, minimal representation (no overlapping
- // ranges, no contiguous ranges). The implementation of various
- // methods rely on this property.
- rr []IPRange
- }
- // Ranges returns the minimum and sorted set of IP
- // ranges that covers s.
- func (s *IPSet) Ranges() []IPRange {
- return append([]IPRange{}, s.rr...)
- }
- // Prefixes returns the minimum and sorted set of IP prefixes
- // that covers s.
- func (s *IPSet) Prefixes() []IPPrefix {
- out := make([]IPPrefix, 0, len(s.rr))
- for _, r := range s.rr {
- out = append(out, r.Prefixes()...)
- }
- return out
- }
- // Equal reports whether s and o represent the same set of IP
- // addresses.
- func (s *IPSet) Equal(o *IPSet) bool {
- if len(s.rr) != len(o.rr) {
- return false
- }
- for i := range s.rr {
- if s.rr[i] != o.rr[i] {
- return false
- }
- }
- return true
- }
- // Contains reports whether ip is in s.
- // If ip has an IPv6 zone, Contains returns false,
- // because IPSets do not track zones.
- func (s *IPSet) Contains(ip IP) bool {
- if ip.hasZone() {
- return false
- }
- // TODO: data structure permitting more efficient lookups:
- // https://github.com/inetaf/netaddr/issues/139
- i := sort.Search(len(s.rr), func(i int) bool {
- return ip.Less(s.rr[i].from)
- })
- if i == 0 {
- return false
- }
- i--
- return s.rr[i].contains(ip)
- }
- // ContainsRange reports whether all IPs in r are in s.
- func (s *IPSet) ContainsRange(r IPRange) bool {
- for _, x := range s.rr {
- if r.coveredBy(x) {
- return true
- }
- }
- return false
- }
- // ContainsPrefix reports whether all IPs in p are in s.
- func (s *IPSet) ContainsPrefix(p IPPrefix) bool {
- return s.ContainsRange(p.Range())
- }
- // Overlaps reports whether any IP in b is also in s.
- func (s *IPSet) Overlaps(b *IPSet) bool {
- // TODO: sorted ranges lets us do this in O(n+m)
- for _, r := range s.rr {
- for _, or := range b.rr {
- if r.Overlaps(or) {
- return true
- }
- }
- }
- return false
- }
- // OverlapsRange reports whether any IP in r is also in s.
- func (s *IPSet) OverlapsRange(r IPRange) bool {
- // TODO: sorted ranges lets us do this more efficiently.
- for _, x := range s.rr {
- if x.Overlaps(r) {
- return true
- }
- }
- return false
- }
- // OverlapsPrefix reports whether any IP in p is also in s.
- func (s *IPSet) OverlapsPrefix(p IPPrefix) bool {
- return s.OverlapsRange(p.Range())
- }
- // RemoveFreePrefix splits s into a Prefix of length bitLen and a new
- // IPSet with that prefix removed.
- //
- // If no contiguous prefix of length bitLen exists in s,
- // RemoveFreePrefix returns ok=false.
- func (s *IPSet) RemoveFreePrefix(bitLen uint8) (p IPPrefix, newSet *IPSet, ok bool) {
- var bestFit IPPrefix
- for _, r := range s.rr {
- for _, prefix := range r.Prefixes() {
- if prefix.bits > bitLen {
- continue
- }
- if bestFit.ip.IsZero() || prefix.bits > bestFit.bits {
- bestFit = prefix
- if bestFit.bits == bitLen {
- // exact match, done.
- break
- }
- }
- }
- }
- if bestFit.ip.IsZero() {
- return IPPrefix{}, s, false
- }
- prefix := IPPrefix{ip: bestFit.ip, bits: bitLen}
- var b IPSetBuilder
- b.AddSet(s)
- b.RemovePrefix(prefix)
- newSet, _ = b.IPSet()
- return prefix, newSet, true
- }
- type multiErr []error
- func (e multiErr) Error() string {
- var ret []string
- for _, err := range e {
- ret = append(ret, err.Error())
- }
- return strings.Join(ret, "; ")
- }
- // A stacktraceErr combines an error with a stack trace.
- type stacktraceErr struct {
- pcs [16]uintptr // preallocated array of PCs
- at []uintptr // stack trace whence the error
- err error // underlying error
- }
- func (e *stacktraceErr) Error() string {
- frames := runtime.CallersFrames(e.at)
- buf := new(strings.Builder)
- buf.WriteString(e.err.Error())
- buf.WriteString(" @ ")
- for {
- frame, more := frames.Next()
- if !more {
- break
- }
- fmt.Fprintf(buf, "%s:%d ", frame.File, frame.Line)
- }
- return strings.TrimSpace(buf.String())
- }
- func (e *stacktraceErr) Unwrap() error {
- return e.err
- }
|