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 }