github.com/osrg/gobgp/v3@v3.30.0/internal/pkg/table/roa.go (about)

     1  // Copyright (C) 2016-2019 Nippon Telegraph and Telephone Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package table
    17  
    18  import (
    19  	"net"
    20  	"sort"
    21  
    22  	"github.com/k-sone/critbitgo"
    23  	"github.com/osrg/gobgp/v3/pkg/config/oc"
    24  	"github.com/osrg/gobgp/v3/pkg/log"
    25  	"github.com/osrg/gobgp/v3/pkg/packet/bgp"
    26  )
    27  
    28  type ROA struct {
    29  	Family  int
    30  	Network *net.IPNet
    31  	MaxLen  uint8
    32  	AS      uint32
    33  	Src     string
    34  }
    35  
    36  func NewROA(family int, prefixByte []byte, prefixLen uint8, maxLen uint8, as uint32, src string) *ROA {
    37  	p := make([]byte, len(prefixByte))
    38  	bits := net.IPv4len * 8
    39  	if family == bgp.AFI_IP6 {
    40  		bits = net.IPv6len * 8
    41  	}
    42  	copy(p, prefixByte)
    43  	return &ROA{
    44  		Family: family,
    45  		Network: &net.IPNet{
    46  			IP:   p,
    47  			Mask: net.CIDRMask(int(prefixLen), bits),
    48  		},
    49  		MaxLen: maxLen,
    50  		AS:     as,
    51  		Src:    src,
    52  	}
    53  }
    54  
    55  func (r *ROA) Equal(roa *ROA) bool {
    56  	if r.MaxLen == roa.MaxLen && r.Src == roa.Src && r.AS == roa.AS {
    57  		return true
    58  	}
    59  	return false
    60  }
    61  
    62  type roaBucket struct {
    63  	network *net.IPNet
    64  	entries []*ROA
    65  }
    66  
    67  func (r *roaBucket) GetEntries() []*ROA {
    68  	return r.entries
    69  }
    70  
    71  type ROATable struct {
    72  	trees  map[bgp.RouteFamily]*critbitgo.Net
    73  	logger log.Logger
    74  }
    75  
    76  func NewROATable(logger log.Logger) *ROATable {
    77  	m := make(map[bgp.RouteFamily]*critbitgo.Net)
    78  	m[bgp.RF_IPv4_UC] = critbitgo.NewNet()
    79  	m[bgp.RF_IPv6_UC] = critbitgo.NewNet()
    80  	return &ROATable{
    81  		trees:  m,
    82  		logger: logger,
    83  	}
    84  }
    85  
    86  func (rt *ROATable) roa2tree(roa *ROA) *critbitgo.Net {
    87  	tree := rt.trees[bgp.RF_IPv4_UC]
    88  	if roa.Family == bgp.AFI_IP6 {
    89  		tree = rt.trees[bgp.RF_IPv6_UC]
    90  	}
    91  	return tree
    92  }
    93  
    94  func (rt *ROATable) getBucket(roa *ROA) *roaBucket {
    95  	tree := rt.roa2tree(roa)
    96  	b, ok, _ := tree.Get(roa.Network)
    97  	if !ok {
    98  		b := &roaBucket{
    99  			network: roa.Network,
   100  			entries: make([]*ROA, 0),
   101  		}
   102  		tree.Add(roa.Network, b)
   103  		return b
   104  	}
   105  	return b.(*roaBucket)
   106  }
   107  
   108  func (rt *ROATable) Add(roa *ROA) {
   109  	b := rt.getBucket(roa)
   110  	for _, r := range b.entries {
   111  		if r.Equal(roa) {
   112  			// we already have the same one
   113  			return
   114  		}
   115  	}
   116  	b.entries = append(b.entries, roa)
   117  	sort.Slice(b.entries, func(i, j int) bool {
   118  		r1 := b.entries[i]
   119  		r2 := b.entries[j]
   120  
   121  		if r1.MaxLen < r2.MaxLen {
   122  			return true
   123  		} else if r1.MaxLen > r2.MaxLen {
   124  			return false
   125  		}
   126  
   127  		if r1.AS < r2.AS {
   128  			return true
   129  		}
   130  		return false
   131  	})
   132  }
   133  
   134  func (rt *ROATable) Delete(roa *ROA) {
   135  	tree := rt.roa2tree(roa)
   136  	if b, ok, _ := tree.Get(roa.Network); ok {
   137  		bucket := b.(*roaBucket)
   138  		for i, r := range bucket.entries {
   139  			if r.Equal(roa) {
   140  				bucket.entries = append(bucket.entries[:i], bucket.entries[i+1:]...)
   141  				return
   142  			}
   143  		}
   144  	}
   145  	rt.logger.Info("Can't withdraw a ROA",
   146  		log.Fields{
   147  			"Topic":      "rpki",
   148  			"Network":    roa.Network.String(),
   149  			"AS":         roa.AS,
   150  			"Max Length": roa.MaxLen})
   151  }
   152  
   153  func (rt *ROATable) DeleteAll(network string) {
   154  	for _, tree := range rt.trees {
   155  		deleteNetworks := make([]*net.IPNet, 0, tree.Size())
   156  		tree.Walk(nil, func(n *net.IPNet, v interface{}) bool {
   157  			b, _ := v.(*roaBucket)
   158  			newEntries := make([]*ROA, 0, len(b.entries))
   159  			for _, r := range b.entries {
   160  				if r.Src != network {
   161  					newEntries = append(newEntries, r)
   162  				}
   163  			}
   164  			if len(newEntries) > 0 {
   165  				b.entries = newEntries
   166  			} else {
   167  				deleteNetworks = append(deleteNetworks, n)
   168  			}
   169  			return true
   170  		})
   171  		for _, key := range deleteNetworks {
   172  			tree.Delete(key)
   173  		}
   174  	}
   175  }
   176  
   177  func (rt *ROATable) Validate(path *Path) *Validation {
   178  	if path.IsWithdraw || path.IsEOR() {
   179  		// RPKI isn't enabled or invalid path
   180  		return nil
   181  	}
   182  	tree, ok := rt.trees[path.GetRouteFamily()]
   183  	if !ok {
   184  		return nil
   185  	}
   186  
   187  	ownAs := path.OriginInfo().source.LocalAS
   188  	asPath := path.GetAsPath()
   189  	var as uint32
   190  
   191  	validation := &Validation{
   192  		Status:          oc.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND,
   193  		Reason:          RPKI_VALIDATION_REASON_TYPE_NONE,
   194  		Matched:         make([]*ROA, 0),
   195  		UnmatchedLength: make([]*ROA, 0),
   196  		UnmatchedAs:     make([]*ROA, 0),
   197  	}
   198  
   199  	if asPath == nil || len(asPath.Value) == 0 {
   200  		as = ownAs
   201  	} else {
   202  		param := asPath.Value[len(asPath.Value)-1]
   203  		switch param.GetType() {
   204  		case bgp.BGP_ASPATH_ATTR_TYPE_SEQ:
   205  			asList := param.GetAS()
   206  			if len(asList) == 0 {
   207  				as = ownAs
   208  			} else {
   209  				as = asList[len(asList)-1]
   210  			}
   211  		case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
   212  			as = ownAs
   213  		default:
   214  			return validation
   215  		}
   216  	}
   217  
   218  	r := nlriToIPNet(path.GetNlri())
   219  	prefixLen, _ := r.Mask.Size()
   220  	var bucket *roaBucket
   221  	tree.WalkMatch(r, func(r *net.IPNet, v interface{}) bool {
   222  		bucket, _ = v.(*roaBucket)
   223  		for _, r := range bucket.entries {
   224  			if prefixLen <= int(r.MaxLen) {
   225  				if r.AS != 0 && r.AS == as {
   226  					validation.Matched = append(validation.Matched, r)
   227  				} else {
   228  					validation.UnmatchedAs = append(validation.UnmatchedAs, r)
   229  				}
   230  			} else {
   231  				validation.UnmatchedLength = append(validation.UnmatchedLength, r)
   232  			}
   233  		}
   234  		return true
   235  	})
   236  
   237  	if len(validation.Matched) != 0 {
   238  		validation.Status = oc.RPKI_VALIDATION_RESULT_TYPE_VALID
   239  		validation.Reason = RPKI_VALIDATION_REASON_TYPE_NONE
   240  	} else if len(validation.UnmatchedAs) != 0 {
   241  		validation.Status = oc.RPKI_VALIDATION_RESULT_TYPE_INVALID
   242  		validation.Reason = RPKI_VALIDATION_REASON_TYPE_AS
   243  	} else if len(validation.UnmatchedLength) != 0 {
   244  		validation.Status = oc.RPKI_VALIDATION_RESULT_TYPE_INVALID
   245  		validation.Reason = RPKI_VALIDATION_REASON_TYPE_LENGTH
   246  	} else {
   247  		validation.Status = oc.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND
   248  		validation.Reason = RPKI_VALIDATION_REASON_TYPE_NONE
   249  	}
   250  
   251  	return validation
   252  }
   253  
   254  func (rt *ROATable) Info(family bgp.RouteFamily) (map[string]uint32, map[string]uint32) {
   255  	records := make(map[string]uint32)
   256  	prefixes := make(map[string]uint32)
   257  
   258  	if tree, ok := rt.trees[family]; ok {
   259  		tree.Walk(nil, func(_ *net.IPNet, v interface{}) bool {
   260  			b, _ := v.(*roaBucket)
   261  			tmpRecords := make(map[string]uint32)
   262  			for _, roa := range b.entries {
   263  				tmpRecords[roa.Src]++
   264  			}
   265  
   266  			for src, r := range tmpRecords {
   267  				if r > 0 {
   268  					records[src] += r
   269  					prefixes[src]++
   270  				}
   271  			}
   272  			return true
   273  		})
   274  	}
   275  	return records, prefixes
   276  }
   277  
   278  func (rt *ROATable) List(family bgp.RouteFamily) ([]*ROA, error) {
   279  	var rfList []bgp.RouteFamily
   280  	switch family {
   281  	case bgp.RF_IPv4_UC:
   282  		rfList = []bgp.RouteFamily{bgp.RF_IPv4_UC}
   283  	case bgp.RF_IPv6_UC:
   284  		rfList = []bgp.RouteFamily{bgp.RF_IPv6_UC}
   285  	default:
   286  		rfList = []bgp.RouteFamily{bgp.RF_IPv4_UC, bgp.RF_IPv6_UC}
   287  	}
   288  	l := make([]*ROA, 0)
   289  	for _, rf := range rfList {
   290  		if tree, ok := rt.trees[rf]; ok {
   291  			tree.Walk(nil, func(_ *net.IPNet, v interface{}) bool {
   292  				b, _ := v.(*roaBucket)
   293  				l = append(l, b.entries...)
   294  				return true
   295  			})
   296  		}
   297  	}
   298  	return l, nil
   299  }