github.com/osrg/gobgp@v2.0.0+incompatible/cmd/gobgp/global.go (about) 1 // Copyright (C) 2015 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 main 17 18 import ( 19 "encoding/json" 20 "fmt" 21 "net" 22 "regexp" 23 "sort" 24 "strconv" 25 "strings" 26 "time" 27 28 "github.com/spf13/cobra" 29 30 api "github.com/osrg/gobgp/api" 31 "github.com/osrg/gobgp/internal/pkg/apiutil" 32 "github.com/osrg/gobgp/internal/pkg/table" 33 34 "github.com/osrg/gobgp/pkg/packet/bgp" 35 ) 36 37 type extCommType int 38 39 const ( 40 ctAccept extCommType = iota 41 ctDiscard 42 ctRate 43 ctRedirect 44 ctMark 45 ctAction 46 ctRT 47 ctEncap 48 ctESILabel 49 ctRouterMAC 50 ctDefaultGateway 51 ctValid 52 ctNotFound 53 ctInvalid 54 ctColor 55 ) 56 57 var extCommNameMap = map[extCommType]string{ 58 ctAccept: "accept", 59 ctDiscard: "discard", 60 ctRate: "rate-limit", 61 ctRedirect: "redirect", 62 ctMark: "mark", 63 ctAction: "action", 64 ctRT: "rt", 65 ctEncap: "encap", 66 ctESILabel: "esi-label", 67 ctRouterMAC: "router-mac", 68 ctDefaultGateway: "default-gateway", 69 ctValid: "valid", 70 ctNotFound: "not-found", 71 ctInvalid: "invalid", 72 ctColor: "color", 73 } 74 75 var extCommValueMap = map[string]extCommType{ 76 extCommNameMap[ctAccept]: ctAccept, 77 extCommNameMap[ctDiscard]: ctDiscard, 78 extCommNameMap[ctRate]: ctRate, 79 extCommNameMap[ctRedirect]: ctRedirect, 80 extCommNameMap[ctMark]: ctMark, 81 extCommNameMap[ctAction]: ctAction, 82 extCommNameMap[ctRT]: ctRT, 83 extCommNameMap[ctEncap]: ctEncap, 84 extCommNameMap[ctESILabel]: ctESILabel, 85 extCommNameMap[ctRouterMAC]: ctRouterMAC, 86 extCommNameMap[ctDefaultGateway]: ctDefaultGateway, 87 extCommNameMap[ctValid]: ctValid, 88 extCommNameMap[ctNotFound]: ctNotFound, 89 extCommNameMap[ctInvalid]: ctInvalid, 90 extCommNameMap[ctColor]: ctColor, 91 } 92 93 func rateLimitParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { 94 exp := regexp.MustCompile(fmt.Sprintf("^(%s|(%s) (\\d+)(\\.(\\d+))?)( as (\\d+))?$", extCommNameMap[ctDiscard], extCommNameMap[ctRate])) 95 elems := exp.FindStringSubmatch(strings.Join(args, " ")) 96 if len(elems) != 8 { 97 return nil, fmt.Errorf("invalid rate-limit") 98 } 99 var rate float32 100 var as uint64 101 if elems[2] == extCommNameMap[ctRate] { 102 f, err := strconv.ParseFloat(elems[3]+elems[4], 32) 103 if err != nil { 104 return nil, err 105 } 106 rate = float32(f) 107 } 108 if elems[7] != "" { 109 var err error 110 as, err = strconv.ParseUint(elems[7], 10, 16) 111 if err != nil { 112 return nil, err 113 } 114 } 115 return []bgp.ExtendedCommunityInterface{bgp.NewTrafficRateExtended(uint16(as), rate)}, nil 116 } 117 118 func redirectParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { 119 if len(args) < 2 || args[0] != extCommNameMap[ctRedirect] { 120 return nil, fmt.Errorf("invalid redirect") 121 } 122 rt, err := bgp.ParseRouteTarget(strings.Join(args[1:], " ")) 123 if err != nil { 124 return nil, err 125 } 126 switch rt.(type) { 127 case *bgp.TwoOctetAsSpecificExtended: 128 r := rt.(*bgp.TwoOctetAsSpecificExtended) 129 return []bgp.ExtendedCommunityInterface{bgp.NewRedirectTwoOctetAsSpecificExtended(r.AS, r.LocalAdmin)}, nil 130 case *bgp.IPv4AddressSpecificExtended: 131 r := rt.(*bgp.IPv4AddressSpecificExtended) 132 return []bgp.ExtendedCommunityInterface{bgp.NewRedirectIPv4AddressSpecificExtended(r.IPv4.String(), r.LocalAdmin)}, nil 133 case *bgp.FourOctetAsSpecificExtended: 134 r := rt.(*bgp.FourOctetAsSpecificExtended) 135 return []bgp.ExtendedCommunityInterface{bgp.NewRedirectFourOctetAsSpecificExtended(r.AS, r.LocalAdmin)}, nil 136 case *bgp.IPv6AddressSpecificExtended: 137 r := rt.(*bgp.IPv6AddressSpecificExtended) 138 return []bgp.ExtendedCommunityInterface{bgp.NewRedirectIPv6AddressSpecificExtended(r.IPv6.String(), r.LocalAdmin)}, nil 139 } 140 return nil, fmt.Errorf("invalid redirect") 141 } 142 143 func markParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { 144 if len(args) < 2 || args[0] != extCommNameMap[ctMark] { 145 return nil, fmt.Errorf("invalid mark") 146 } 147 dscp, err := strconv.ParseUint(args[1], 10, 8) 148 if err != nil { 149 return nil, fmt.Errorf("invalid mark") 150 } 151 return []bgp.ExtendedCommunityInterface{bgp.NewTrafficRemarkExtended(uint8(dscp))}, nil 152 } 153 154 func actionParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { 155 if len(args) < 2 || args[0] != extCommNameMap[ctAction] { 156 return nil, fmt.Errorf("invalid action") 157 } 158 sample := false 159 terminal := false 160 switch args[1] { 161 case "sample": 162 sample = true 163 case "terminal": 164 terminal = true 165 case "terminal-sample", "sample-terminal": 166 sample = true 167 terminal = true 168 default: 169 return nil, fmt.Errorf("invalid action") 170 } 171 return []bgp.ExtendedCommunityInterface{bgp.NewTrafficActionExtended(terminal, sample)}, nil 172 } 173 174 func rtParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { 175 if len(args) < 2 || args[0] != extCommNameMap[ctRT] { 176 return nil, fmt.Errorf("invalid rt") 177 } 178 exts := make([]bgp.ExtendedCommunityInterface, 0, len(args[1:])) 179 for _, arg := range args[1:] { 180 rt, err := bgp.ParseRouteTarget(arg) 181 if err != nil { 182 return nil, err 183 } 184 exts = append(exts, rt) 185 } 186 return exts, nil 187 } 188 189 func encapParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { 190 if len(args) < 2 || args[0] != extCommNameMap[ctEncap] { 191 return nil, fmt.Errorf("invalid encap") 192 } 193 var typ bgp.TunnelType 194 switch args[1] { 195 case "l2tpv3": 196 typ = bgp.TUNNEL_TYPE_L2TP3 197 case "gre": 198 typ = bgp.TUNNEL_TYPE_GRE 199 case "ip-in-ip": 200 typ = bgp.TUNNEL_TYPE_IP_IN_IP 201 case "vxlan": 202 typ = bgp.TUNNEL_TYPE_VXLAN 203 case "nvgre": 204 typ = bgp.TUNNEL_TYPE_NVGRE 205 case "mpls": 206 typ = bgp.TUNNEL_TYPE_MPLS 207 case "mpls-in-gre": 208 typ = bgp.TUNNEL_TYPE_MPLS_IN_GRE 209 case "vxlan-gre": 210 typ = bgp.TUNNEL_TYPE_VXLAN_GRE 211 default: 212 return nil, fmt.Errorf("invalid encap type") 213 } 214 return []bgp.ExtendedCommunityInterface{bgp.NewEncapExtended(typ)}, nil 215 } 216 217 func esiLabelParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { 218 if len(args) < 2 || args[0] != extCommNameMap[ctESILabel] { 219 return nil, fmt.Errorf("invalid esi-label") 220 } 221 label, err := strconv.ParseUint(args[1], 10, 32) 222 if err != nil { 223 return nil, err 224 } 225 isSingleActive := false 226 if len(args) > 2 { 227 switch args[2] { 228 case "single-active": 229 isSingleActive = true 230 case "all-active": 231 // isSingleActive = false 232 default: 233 return nil, fmt.Errorf("invalid esi-label") 234 } 235 } 236 o := &bgp.ESILabelExtended{ 237 Label: uint32(label), 238 IsSingleActive: isSingleActive, 239 } 240 return []bgp.ExtendedCommunityInterface{o}, nil 241 } 242 243 func routerMacParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { 244 if len(args) < 2 || args[0] != extCommNameMap[ctRouterMAC] { 245 return nil, fmt.Errorf("invalid router's mac") 246 } 247 hw, err := net.ParseMAC(args[1]) 248 if err != nil { 249 return nil, err 250 } 251 o := &bgp.RouterMacExtended{Mac: hw} 252 return []bgp.ExtendedCommunityInterface{o}, nil 253 } 254 255 func defaultGatewayParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { 256 if len(args) < 1 || args[0] != extCommNameMap[ctDefaultGateway] { 257 return nil, fmt.Errorf("invalid default-gateway") 258 } 259 return []bgp.ExtendedCommunityInterface{bgp.NewDefaultGatewayExtended()}, nil 260 } 261 262 func validationParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { 263 if len(args) < 1 { 264 return nil, fmt.Errorf("invalid validation state") 265 } 266 var typ bgp.ValidationState 267 switch args[0] { 268 case "valid": 269 typ = bgp.VALIDATION_STATE_VALID 270 case "not-found": 271 typ = bgp.VALIDATION_STATE_NOT_FOUND 272 case "invalid": 273 typ = bgp.VALIDATION_STATE_INVALID 274 default: 275 return nil, fmt.Errorf("invalid validation state") 276 } 277 return []bgp.ExtendedCommunityInterface{bgp.NewValidationExtended(typ)}, nil 278 } 279 280 func colorParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { 281 if len(args) != 2 || args[0] != extCommNameMap[ctColor] { 282 return nil, fmt.Errorf("invalid color") 283 } 284 color, err := strconv.ParseUint(args[1], 10, 32) 285 if err != nil { 286 return nil, fmt.Errorf("invalid color") 287 } 288 return []bgp.ExtendedCommunityInterface{bgp.NewColorExtended(uint32(color))}, nil 289 } 290 291 var extCommParserMap = map[extCommType]func([]string) ([]bgp.ExtendedCommunityInterface, error){ 292 ctAccept: nil, 293 ctDiscard: rateLimitParser, 294 ctRate: rateLimitParser, 295 ctRedirect: redirectParser, 296 ctMark: markParser, 297 ctAction: actionParser, 298 ctRT: rtParser, 299 ctEncap: encapParser, 300 ctESILabel: esiLabelParser, 301 ctRouterMAC: routerMacParser, 302 ctDefaultGateway: defaultGatewayParser, 303 ctValid: validationParser, 304 ctNotFound: validationParser, 305 ctInvalid: validationParser, 306 ctColor: colorParser, 307 } 308 309 func parseExtendedCommunities(args []string) ([]bgp.ExtendedCommunityInterface, error) { 310 idxs := make([]struct { 311 t extCommType 312 i int 313 }, 0, len(extCommNameMap)) 314 for idx, v := range args { 315 if t, ok := extCommValueMap[v]; ok { 316 idxs = append(idxs, struct { 317 t extCommType 318 i int 319 }{t, idx}) 320 } 321 } 322 exts := make([]bgp.ExtendedCommunityInterface, 0, len(idxs)) 323 for i, idx := range idxs { 324 var a []string 325 f := extCommParserMap[idx.t] 326 if i < len(idxs)-1 { 327 a = args[:idxs[i+1].i-idx.i] 328 args = args[(idxs[i+1].i - idx.i):] 329 } else { 330 a = args 331 args = nil 332 } 333 if f == nil { 334 continue 335 } 336 ext, err := f(a) 337 if err != nil { 338 return nil, err 339 } 340 exts = append(exts, ext...) 341 } 342 if len(args) > 0 { 343 return nil, fmt.Errorf("failed to parse %v", args) 344 } 345 return exts, nil 346 } 347 348 func parseFlowSpecArgs(rf bgp.RouteFamily, args []string) (bgp.AddrPrefixInterface, []string, error) { 349 // Format: 350 // match <rule>... [then <action>...] [rd <rd>] [rt <rt>...] 351 req := 3 // match <key1> <arg1> [<key2> <arg2>...] 352 if len(args) < req { 353 return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args)) 354 } 355 m, err := extractReserved(args, map[string]int{ 356 "match": paramList, 357 "then": paramList, 358 "rd": paramSingle, 359 "rt": paramList}) 360 if err != nil { 361 return nil, nil, err 362 } 363 if len(m["match"]) == 0 { 364 return nil, nil, fmt.Errorf("specify filtering rules with keyword 'match'") 365 } 366 367 var rd bgp.RouteDistinguisherInterface 368 extcomms := m["then"] 369 switch rf { 370 case bgp.RF_FS_IPv4_VPN, bgp.RF_FS_IPv6_VPN, bgp.RF_FS_L2_VPN: 371 if len(m["rd"]) == 0 { 372 return nil, nil, fmt.Errorf("specify rd") 373 } 374 var err error 375 if rd, err = bgp.ParseRouteDistinguisher(m["rd"][0]); err != nil { 376 return nil, nil, fmt.Errorf("invalid rd: %s", m["rd"][0]) 377 } 378 if len(m["rt"]) > 0 { 379 extcomms = append(extcomms, "rt") 380 extcomms = append(extcomms, m["rt"]...) 381 } 382 default: 383 if len(m["rd"]) > 0 { 384 return nil, nil, fmt.Errorf("cannot specify rd for %s", rf.String()) 385 } 386 if len(m["rt"]) > 0 { 387 return nil, nil, fmt.Errorf("cannot specify rt for %s", rf.String()) 388 } 389 } 390 391 rules, err := bgp.ParseFlowSpecComponents(rf, strings.Join(m["match"], " ")) 392 if err != nil { 393 return nil, nil, err 394 } 395 396 var nlri bgp.AddrPrefixInterface 397 switch rf { 398 case bgp.RF_FS_IPv4_UC: 399 nlri = bgp.NewFlowSpecIPv4Unicast(rules) 400 case bgp.RF_FS_IPv6_UC: 401 nlri = bgp.NewFlowSpecIPv6Unicast(rules) 402 case bgp.RF_FS_IPv4_VPN: 403 nlri = bgp.NewFlowSpecIPv4VPN(rd, rules) 404 case bgp.RF_FS_IPv6_VPN: 405 nlri = bgp.NewFlowSpecIPv6VPN(rd, rules) 406 case bgp.RF_FS_L2_VPN: 407 nlri = bgp.NewFlowSpecL2VPN(rd, rules) 408 default: 409 return nil, nil, fmt.Errorf("invalid route family") 410 } 411 412 return nlri, extcomms, nil 413 } 414 415 func parseEvpnEthernetAutoDiscoveryArgs(args []string) (bgp.AddrPrefixInterface, []string, error) { 416 // Format: 417 // esi <esi> etag <etag> label <label> rd <rd> [rt <rt>...] [encap <encap type>] [esi-label <esi-label> [single-active | all-active]] 418 req := 8 419 if len(args) < req { 420 return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args)) 421 } 422 m, err := extractReserved(args, map[string]int{ 423 "esi": paramList, 424 "etag": paramSingle, 425 "label": paramSingle, 426 "rd": paramSingle, 427 "rt": paramList, 428 "encap": paramSingle, 429 "esi-label": paramSingle}) 430 if err != nil { 431 return nil, nil, err 432 } 433 for _, f := range []string{"esi", "etag", "label", "rd"} { 434 for len(m[f]) == 0 { 435 return nil, nil, fmt.Errorf("specify %s", f) 436 } 437 } 438 439 esi, err := bgp.ParseEthernetSegmentIdentifier(m["esi"]) 440 if err != nil { 441 return nil, nil, err 442 } 443 444 e, err := strconv.ParseUint(m["etag"][0], 10, 32) 445 if err != nil { 446 return nil, nil, err 447 } 448 etag := uint32(e) 449 450 l, err := strconv.ParseUint(m["label"][0], 10, 32) 451 if err != nil { 452 return nil, nil, err 453 } 454 label := uint32(l) 455 456 rd, err := bgp.ParseRouteDistinguisher(m["rd"][0]) 457 if err != nil { 458 return nil, nil, err 459 } 460 461 extcomms := make([]string, 0) 462 if len(m["rt"]) > 0 { 463 extcomms = append(extcomms, "rt") 464 extcomms = append(extcomms, m["rt"]...) 465 } 466 if len(m["encap"]) > 0 { 467 extcomms = append(extcomms, "encap", m["encap"][0]) 468 } 469 if len(m["esi-label"]) > 0 { 470 extcomms = append(extcomms, "esi-label") 471 extcomms = append(extcomms, m["esi-label"]...) 472 } 473 474 r := &bgp.EVPNEthernetAutoDiscoveryRoute{ 475 RD: rd, 476 ESI: esi, 477 ETag: etag, 478 Label: label, 479 } 480 return bgp.NewEVPNNLRI(bgp.EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY, r), extcomms, nil 481 } 482 483 func parseEvpnMacAdvArgs(args []string) (bgp.AddrPrefixInterface, []string, error) { 484 // Format: 485 // <mac address> <ip address> [esi <esi>] etag <etag> label <label> rd <rd> [rt <rt>...] [encap <encap type>] [router-mac <mac address>] [default-gateway] 486 // or 487 // <mac address> <ip address> <etag> [esi <esi>] label <label> rd <rd> [rt <rt>...] [encap <encap type>] [router-mac <mac address>] [default-gateway] 488 // or 489 // <mac address> <ip address> <etag> <label> [esi <esi>] rd <rd> [rt <rt>...] [encap <encap type>] [router-mac <mac address>] [default-gateway] 490 req := 6 491 if len(args) < req { 492 return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args)) 493 } 494 m, err := extractReserved(args, map[string]int{ 495 "esi": paramList, 496 "etag": paramSingle, 497 "label": paramSingle, 498 "rd": paramSingle, 499 "rt": paramList, 500 "encap": paramSingle, 501 "router-mac": paramSingle}) 502 if err != nil { 503 return nil, nil, err 504 } 505 if len(m[""]) < 2 { 506 return nil, nil, fmt.Errorf("specify mac and ip address") 507 } 508 macStr := m[""][0] 509 ipStr := m[""][1] 510 eTagStr := "" 511 labelStr := "" 512 if len(m[""]) == 2 { 513 if len(m["etag"]) == 0 || len(m["label"]) == 0 { 514 return nil, nil, fmt.Errorf("specify etag and label") 515 } 516 eTagStr = m["etag"][0] 517 labelStr = m["label"][0] 518 } else if len(m[""]) == 3 { 519 if len(m["label"]) == 0 { 520 return nil, nil, fmt.Errorf("specify label") 521 } 522 eTagStr = m[""][2] 523 labelStr = m["label"][0] 524 } else { 525 eTagStr = m[""][2] 526 labelStr = m[""][3] 527 } 528 if len(m["rd"]) == 0 { 529 return nil, nil, fmt.Errorf("specify rd") 530 } 531 532 mac, err := net.ParseMAC(macStr) 533 if err != nil { 534 return nil, nil, fmt.Errorf("invalid mac address: %s", macStr) 535 } 536 537 ip := net.ParseIP(ipStr) 538 ipLen := 0 539 if ip == nil { 540 return nil, nil, fmt.Errorf("invalid ip address: %s", ipStr) 541 } else if ip.IsUnspecified() { 542 ip = nil 543 } else if ip.To4() != nil { 544 ipLen = net.IPv4len * 8 545 } else { 546 ipLen = net.IPv6len * 8 547 } 548 549 esi, err := bgp.ParseEthernetSegmentIdentifier(m["esi"]) 550 if err != nil { 551 return nil, nil, err 552 } 553 554 eTag, err := strconv.ParseUint(eTagStr, 10, 32) 555 if err != nil { 556 return nil, nil, fmt.Errorf("invalid etag: %s: %s", eTagStr, err) 557 } 558 559 var labels []uint32 560 for _, l := range strings.SplitN(labelStr, ",", 2) { 561 label, err := strconv.ParseUint(l, 10, 32) 562 if err != nil { 563 return nil, nil, fmt.Errorf("invalid label: %s: %s", labelStr, err) 564 } 565 labels = append(labels, uint32(label)) 566 } 567 568 rd, err := bgp.ParseRouteDistinguisher(m["rd"][0]) 569 if err != nil { 570 return nil, nil, err 571 } 572 573 extcomms := make([]string, 0) 574 if len(m["rt"]) > 0 { 575 extcomms = append(extcomms, "rt") 576 extcomms = append(extcomms, m["rt"]...) 577 } 578 if len(m["encap"]) > 0 { 579 extcomms = append(extcomms, "encap", m["encap"][0]) 580 } 581 582 if len(m["router-mac"]) != 0 { 583 _, err := net.ParseMAC(m["router-mac"][0]) 584 if err != nil { 585 return nil, nil, fmt.Errorf("invalid router-mac address: %s", m["router-mac"][0]) 586 } 587 extcomms = append(extcomms, "router-mac", m["router-mac"][0]) 588 } 589 590 for _, a := range args { 591 if a == "default-gateway" { 592 extcomms = append(extcomms, "default-gateway") 593 break 594 } 595 } 596 597 r := &bgp.EVPNMacIPAdvertisementRoute{ 598 RD: rd, 599 ESI: esi, 600 MacAddressLength: 48, 601 MacAddress: mac, 602 IPAddressLength: uint8(ipLen), 603 IPAddress: ip, 604 Labels: labels, 605 ETag: uint32(eTag), 606 } 607 return bgp.NewEVPNNLRI(bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT, r), extcomms, nil 608 } 609 610 func parseEvpnMulticastArgs(args []string) (bgp.AddrPrefixInterface, []string, error) { 611 // Format: 612 // <ip address> etag <etag> rd <rd> [rt <rt>...] [encap <encap type>] 613 // or 614 // <ip address> <etag> rd <rd> [rt <rt>...] [encap <encap type>] 615 req := 4 616 if len(args) < req { 617 return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args)) 618 } 619 m, err := extractReserved(args, map[string]int{ 620 "etag": paramSingle, 621 "rd": paramSingle, 622 "rt": paramList, 623 "encap": paramSingle}) 624 if err != nil { 625 return nil, nil, err 626 } 627 if len(m[""]) < 1 { 628 return nil, nil, fmt.Errorf("specify ip address") 629 } 630 ipStr := m[""][0] 631 eTagStr := "" 632 if len(m[""]) == 1 { 633 if len(m["etag"]) == 0 { 634 return nil, nil, fmt.Errorf("specify etag") 635 } 636 eTagStr = m["etag"][0] 637 } else { 638 eTagStr = m[""][1] 639 } 640 if len(m["rd"]) == 0 { 641 return nil, nil, fmt.Errorf("specify rd") 642 } 643 644 ip := net.ParseIP(ipStr) 645 ipLen := 0 646 if ip == nil { 647 return nil, nil, fmt.Errorf("invalid ip address: %s", ipStr) 648 } else if ip.IsUnspecified() { 649 ip = nil 650 } else if ip.To4() != nil { 651 ipLen = net.IPv4len * 8 652 } else { 653 ipLen = net.IPv6len * 8 654 } 655 656 eTag, err := strconv.ParseUint(eTagStr, 10, 32) 657 if err != nil { 658 return nil, nil, fmt.Errorf("invalid etag: %s: %s", eTagStr, err) 659 } 660 661 rd, err := bgp.ParseRouteDistinguisher(m["rd"][0]) 662 if err != nil { 663 return nil, nil, err 664 } 665 666 extcomms := make([]string, 0) 667 if len(m["rt"]) > 0 { 668 extcomms = append(extcomms, "rt") 669 extcomms = append(extcomms, m["rt"]...) 670 } 671 if len(m["encap"]) > 0 { 672 extcomms = append(extcomms, "encap", m["encap"][0]) 673 } 674 675 r := &bgp.EVPNMulticastEthernetTagRoute{ 676 RD: rd, 677 IPAddressLength: uint8(ipLen), 678 IPAddress: ip, 679 ETag: uint32(eTag), 680 } 681 return bgp.NewEVPNNLRI(bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG, r), extcomms, nil 682 } 683 684 func parseEvpnEthernetSegmentArgs(args []string) (bgp.AddrPrefixInterface, []string, error) { 685 // Format: 686 // <ip address> esi <esi> rd <rd> [rt <rt>...] [encap <encap type>] 687 req := 5 688 if len(args) < req { 689 return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args)) 690 } 691 m, err := extractReserved(args, map[string]int{ 692 "esi": paramList, 693 "rd": paramSingle, 694 "rt": paramList, 695 "encap": paramSingle}) 696 if err != nil { 697 return nil, nil, err 698 } 699 if len(m[""]) < 1 { 700 return nil, nil, fmt.Errorf("specify ip address") 701 } 702 for _, f := range []string{"esi", "rd"} { 703 for len(m[f]) == 0 { 704 return nil, nil, fmt.Errorf("specify %s", f) 705 } 706 } 707 708 ip := net.ParseIP(m[""][0]) 709 ipLen := 0 710 if ip == nil { 711 return nil, nil, fmt.Errorf("invalid ip address: %s", m[""][0]) 712 } else if ip.IsUnspecified() { 713 ip = nil 714 } else if ip.To4() != nil { 715 ipLen = net.IPv4len * 8 716 } else { 717 ipLen = net.IPv6len * 8 718 } 719 720 esi, err := bgp.ParseEthernetSegmentIdentifier(m["esi"]) 721 if err != nil { 722 return nil, nil, err 723 } 724 725 rd, err := bgp.ParseRouteDistinguisher(m["rd"][0]) 726 if err != nil { 727 return nil, nil, err 728 } 729 730 extcomms := make([]string, 0) 731 if len(m["rt"]) > 0 { 732 extcomms = append(extcomms, "rt") 733 extcomms = append(extcomms, m["rt"]...) 734 } 735 if len(m["encap"]) > 0 { 736 extcomms = append(extcomms, "encap", m["encap"][0]) 737 } 738 739 r := &bgp.EVPNEthernetSegmentRoute{ 740 RD: rd, 741 ESI: esi, 742 IPAddressLength: uint8(ipLen), 743 IPAddress: ip, 744 } 745 return bgp.NewEVPNNLRI(bgp.EVPN_ETHERNET_SEGMENT_ROUTE, r), extcomms, nil 746 } 747 748 func parseEvpnIPPrefixArgs(args []string) (bgp.AddrPrefixInterface, []string, error) { 749 // Format: 750 // <ip prefix> [gw <gateway>] [esi <esi>] etag <etag> [label <label>] rd <rd> [rt <rt>...] [encap <encap type>] 751 req := 5 752 if len(args) < req { 753 return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args)) 754 } 755 m, err := extractReserved(args, map[string]int{ 756 "gw": paramSingle, 757 "esi": paramList, 758 "etag": paramSingle, 759 "label": paramSingle, 760 "rd": paramSingle, 761 "rt": paramList, 762 "encap": paramSingle, 763 "router-mac": paramSingle}) 764 if err != nil { 765 return nil, nil, err 766 } 767 if len(m[""]) < 1 { 768 return nil, nil, fmt.Errorf("specify prefix") 769 } 770 for _, f := range []string{"etag", "rd"} { 771 for len(m[f]) == 0 { 772 return nil, nil, fmt.Errorf("specify %s", f) 773 } 774 } 775 776 _, nw, err := net.ParseCIDR(m[""][0]) 777 if err != nil { 778 return nil, nil, err 779 } 780 ones, _ := nw.Mask.Size() 781 782 var gw net.IP 783 if len(m["gw"]) > 0 { 784 gw = net.ParseIP(m["gw"][0]) 785 } 786 787 rd, err := bgp.ParseRouteDistinguisher(m["rd"][0]) 788 if err != nil { 789 return nil, nil, err 790 } 791 792 esi, err := bgp.ParseEthernetSegmentIdentifier(m["esi"]) 793 if err != nil { 794 return nil, nil, err 795 } 796 797 e, err := strconv.ParseUint(m["etag"][0], 10, 32) 798 if err != nil { 799 return nil, nil, fmt.Errorf("invalid etag: %s: %s", m["etag"][0], err) 800 } 801 etag := uint32(e) 802 803 var label uint32 804 if len(m["label"]) > 0 { 805 e, err := strconv.ParseUint(m["label"][0], 10, 32) 806 if err != nil { 807 return nil, nil, fmt.Errorf("invalid label: %s: %s", m["label"][0], err) 808 } 809 label = uint32(e) 810 } 811 812 extcomms := make([]string, 0) 813 if len(m["rt"]) > 0 { 814 extcomms = append(extcomms, "rt") 815 extcomms = append(extcomms, m["rt"]...) 816 } 817 if len(m["encap"]) > 0 { 818 extcomms = append(extcomms, "encap", m["encap"][0]) 819 } 820 if len(m["router-mac"]) > 0 { 821 extcomms = append(extcomms, "router-mac", m["router-mac"][0]) 822 } 823 824 r := &bgp.EVPNIPPrefixRoute{ 825 RD: rd, 826 ESI: esi, 827 ETag: etag, 828 IPPrefixLength: uint8(ones), 829 IPPrefix: nw.IP, 830 GWIPAddress: gw, 831 Label: label, 832 } 833 return bgp.NewEVPNNLRI(bgp.EVPN_IP_PREFIX, r), extcomms, nil 834 } 835 836 func parseEvpnArgs(args []string) (bgp.AddrPrefixInterface, []string, error) { 837 if len(args) < 1 { 838 return nil, nil, fmt.Errorf("lack of args. need 1 but %d", len(args)) 839 } 840 subtype := args[0] 841 args = args[1:] 842 switch subtype { 843 case "a-d": 844 return parseEvpnEthernetAutoDiscoveryArgs(args) 845 case "macadv": 846 return parseEvpnMacAdvArgs(args) 847 case "multicast": 848 return parseEvpnMulticastArgs(args) 849 case "esi": 850 return parseEvpnEthernetSegmentArgs(args) 851 case "prefix": 852 return parseEvpnIPPrefixArgs(args) 853 } 854 return nil, nil, fmt.Errorf("invalid subtype. expect [macadv|multicast|prefix] but %s", subtype) 855 } 856 857 func extractOrigin(args []string) ([]string, bgp.PathAttributeInterface, error) { 858 typ := bgp.BGP_ORIGIN_ATTR_TYPE_INCOMPLETE 859 for idx, arg := range args { 860 if arg == "origin" && len(args) > (idx+1) { 861 switch args[idx+1] { 862 case "igp": 863 typ = bgp.BGP_ORIGIN_ATTR_TYPE_IGP 864 case "egp": 865 typ = bgp.BGP_ORIGIN_ATTR_TYPE_EGP 866 case "incomplete": 867 default: 868 return nil, nil, fmt.Errorf("invalid origin type. expect [igp|egp|incomplete] but %s", args[idx+1]) 869 } 870 args = append(args[:idx], args[idx+2:]...) 871 break 872 } 873 } 874 return args, bgp.NewPathAttributeOrigin(uint8(typ)), nil 875 } 876 877 func toAs4Value(s string) (uint32, error) { 878 if strings.Contains(s, ".") { 879 v := strings.Split(s, ".") 880 upper, err := strconv.ParseUint(v[0], 10, 16) 881 if err != nil { 882 return 0, nil 883 } 884 lower, err := strconv.ParseUint(v[1], 10, 16) 885 if err != nil { 886 return 0, nil 887 } 888 return uint32(upper<<16 | lower), nil 889 } 890 i, err := strconv.ParseUint(s, 10, 32) 891 if err != nil { 892 return 0, err 893 } 894 return uint32(i), nil 895 } 896 897 var ( 898 _regexpASPathGroups = regexp.MustCompile("[{}]") 899 _regexpASPathSegment = regexp.MustCompile(`,|\s+`) 900 ) 901 902 func newAsPath(aspath string) (bgp.PathAttributeInterface, error) { 903 // For the first step, parses "aspath" into a list of uint32 list. 904 // e.g.) "10 20 {30,40} 50" -> [][]uint32{{10, 20}, {30, 40}, {50}} 905 segments := _regexpASPathGroups.Split(aspath, -1) 906 asPathPrams := make([]bgp.AsPathParamInterface, 0, len(segments)) 907 for idx, segment := range segments { 908 if segment == "" { 909 continue 910 } 911 nums := _regexpASPathSegment.Split(segment, -1) 912 asNums := make([]uint32, 0, len(nums)) 913 for _, n := range nums { 914 if n == "" { 915 continue 916 } 917 if asn, err := toAs4Value(n); err != nil { 918 return nil, err 919 } else { 920 asNums = append(asNums, uint32(asn)) 921 } 922 } 923 // Assumes "idx" is even, the given "segment" is of type AS_SEQUENCE, 924 // otherwise AS_SET, because the "segment" enclosed in parentheses is 925 // of type AS_SET. 926 if idx%2 == 0 { 927 asPathPrams = append(asPathPrams, bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, asNums)) 928 } else { 929 asPathPrams = append(asPathPrams, bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, asNums)) 930 } 931 } 932 return bgp.NewPathAttributeAsPath(asPathPrams), nil 933 } 934 935 func extractAsPath(args []string) ([]string, bgp.PathAttributeInterface, error) { 936 for idx, arg := range args { 937 if arg == "aspath" && len(args) > (idx+1) { 938 attr, err := newAsPath(args[idx+1]) 939 if err != nil { 940 return nil, nil, err 941 } 942 args = append(args[:idx], args[idx+2:]...) 943 return args, attr, nil 944 } 945 } 946 return args, nil, nil 947 } 948 949 func extractNexthop(rf bgp.RouteFamily, args []string) ([]string, string, error) { 950 afi, _ := bgp.RouteFamilyToAfiSafi(rf) 951 nexthop := "0.0.0.0" 952 if afi == bgp.AFI_IP6 { 953 nexthop = "::" 954 } 955 for idx, arg := range args { 956 if arg == "nexthop" && len(args) > (idx+1) { 957 if net.ParseIP(args[idx+1]) == nil { 958 return nil, "", fmt.Errorf("invalid nexthop address") 959 } 960 nexthop = args[idx+1] 961 args = append(args[:idx], args[idx+2:]...) 962 break 963 } 964 } 965 return args, nexthop, nil 966 } 967 968 func extractLocalPref(args []string) ([]string, bgp.PathAttributeInterface, error) { 969 for idx, arg := range args { 970 if arg == "local-pref" && len(args) > (idx+1) { 971 metric, err := strconv.ParseUint(args[idx+1], 10, 32) 972 if err != nil { 973 return nil, nil, err 974 } 975 args = append(args[:idx], args[idx+2:]...) 976 return args, bgp.NewPathAttributeLocalPref(uint32(metric)), nil 977 } 978 } 979 return args, nil, nil 980 } 981 982 func extractMed(args []string) ([]string, bgp.PathAttributeInterface, error) { 983 for idx, arg := range args { 984 if arg == "med" && len(args) > (idx+1) { 985 med, err := strconv.ParseUint(args[idx+1], 10, 32) 986 if err != nil { 987 return nil, nil, err 988 } 989 args = append(args[:idx], args[idx+2:]...) 990 return args, bgp.NewPathAttributeMultiExitDisc(uint32(med)), nil 991 } 992 } 993 return args, nil, nil 994 } 995 996 func extractCommunity(args []string) ([]string, bgp.PathAttributeInterface, error) { 997 for idx, arg := range args { 998 if arg == "community" && len(args) > (idx+1) { 999 elems := strings.Split(args[idx+1], ",") 1000 comms := make([]uint32, 0, 1) 1001 for _, elem := range elems { 1002 c, err := table.ParseCommunity(elem) 1003 if err != nil { 1004 return nil, nil, err 1005 } 1006 comms = append(comms, c) 1007 } 1008 args = append(args[:idx], args[idx+2:]...) 1009 return args, bgp.NewPathAttributeCommunities(comms), nil 1010 } 1011 } 1012 return args, nil, nil 1013 } 1014 1015 func extractLargeCommunity(args []string) ([]string, bgp.PathAttributeInterface, error) { 1016 for idx, arg := range args { 1017 if arg == "large-community" && len(args) > (idx+1) { 1018 elems := strings.Split(args[idx+1], ",") 1019 comms := make([]*bgp.LargeCommunity, 0, 1) 1020 for _, elem := range elems { 1021 c, err := bgp.ParseLargeCommunity(elem) 1022 if err != nil { 1023 return nil, nil, err 1024 } 1025 comms = append(comms, c) 1026 } 1027 args = append(args[:idx], args[idx+2:]...) 1028 return args, bgp.NewPathAttributeLargeCommunities(comms), nil 1029 } 1030 } 1031 return args, nil, nil 1032 } 1033 1034 func extractPmsiTunnel(args []string) ([]string, bgp.PathAttributeInterface, error) { 1035 for idx, arg := range args { 1036 if arg == "pmsi" { 1037 pmsi, err := bgp.ParsePmsiTunnel(args[idx+1:]) 1038 if err != nil { 1039 return nil, nil, err 1040 } 1041 if pmsi.IsLeafInfoRequired { 1042 return append(args[:idx], args[idx+5:]...), pmsi, nil 1043 } 1044 return append(args[:idx], args[idx+4:]...), pmsi, nil 1045 } 1046 } 1047 return args, nil, nil 1048 } 1049 1050 func extractAigp(args []string) ([]string, bgp.PathAttributeInterface, error) { 1051 for idx, arg := range args { 1052 if arg == "aigp" { 1053 if len(args) < (idx + 3) { 1054 return nil, nil, fmt.Errorf("invalid aigp format") 1055 } 1056 typ := args[idx+1] 1057 switch typ { 1058 case "metric": 1059 metric, err := strconv.ParseUint(args[idx+2], 10, 64) 1060 if err != nil { 1061 return nil, nil, err 1062 } 1063 aigp := bgp.NewPathAttributeAigp([]bgp.AigpTLVInterface{bgp.NewAigpTLVIgpMetric(uint64(metric))}) 1064 return append(args[:idx], args[idx+3:]...), aigp, nil 1065 default: 1066 return nil, nil, fmt.Errorf("unknown aigp type: %s", typ) 1067 } 1068 } 1069 } 1070 return args, nil, nil 1071 } 1072 1073 func extractAggregator(args []string) ([]string, bgp.PathAttributeInterface, error) { 1074 for idx, arg := range args { 1075 if arg == "aggregator" { 1076 if len(args) < (idx + 1) { 1077 return nil, nil, fmt.Errorf("invalid aggregator format") 1078 } 1079 v := strings.SplitN(args[idx+1], ":", 2) 1080 if len(v) != 2 { 1081 return nil, nil, fmt.Errorf("invalid aggregator format") 1082 } 1083 as, err := strconv.ParseUint(v[0], 10, 32) 1084 if err != nil { 1085 return nil, nil, fmt.Errorf("invalid aggregator format") 1086 } 1087 attr := bgp.NewPathAttributeAggregator(uint32(as), net.ParseIP(v[1]).String()) 1088 return append(args[:idx], args[idx+2:]...), attr, nil 1089 } 1090 } 1091 return args, nil, nil 1092 } 1093 1094 func parsePath(rf bgp.RouteFamily, args []string) (*api.Path, error) { 1095 var nlri bgp.AddrPrefixInterface 1096 var extcomms []string 1097 var err error 1098 attrs := make([]bgp.PathAttributeInterface, 0, 1) 1099 1100 fns := []func([]string) ([]string, bgp.PathAttributeInterface, error){ 1101 extractOrigin, // 1 ORIGIN 1102 extractAsPath, // 2 AS_PATH 1103 extractMed, // 4 MULTI_EXIT_DISC 1104 extractLocalPref, // 5 LOCAL_PREF 1105 extractAggregator, // 7 AGGREGATOR 1106 extractCommunity, // 8 COMMUNITY 1107 extractPmsiTunnel, // 22 PMSI_TUNNEL 1108 extractAigp, // 26 AIGP 1109 extractLargeCommunity, // 32 LARGE_COMMUNITY 1110 } 1111 1112 for _, fn := range fns { 1113 var a bgp.PathAttributeInterface 1114 args, a, err = fn(args) 1115 if err != nil { 1116 return nil, err 1117 } 1118 if a != nil { 1119 attrs = append(attrs, a) 1120 } 1121 } 1122 1123 args, nexthop, err := extractNexthop(rf, args) 1124 if err != nil { 1125 return nil, err 1126 } 1127 1128 switch rf { 1129 case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC: 1130 if len(args) < 1 { 1131 return nil, fmt.Errorf("invalid format") 1132 } 1133 ip, nw, err := net.ParseCIDR(args[0]) 1134 if err != nil { 1135 return nil, err 1136 } 1137 ones, _ := nw.Mask.Size() 1138 if rf == bgp.RF_IPv4_UC { 1139 if ip.To4() == nil { 1140 return nil, fmt.Errorf("invalid ipv4 prefix") 1141 } 1142 nlri = bgp.NewIPAddrPrefix(uint8(ones), ip.String()) 1143 } else { 1144 if ip.To16() == nil { 1145 return nil, fmt.Errorf("invalid ipv6 prefix") 1146 } 1147 nlri = bgp.NewIPv6AddrPrefix(uint8(ones), ip.String()) 1148 } 1149 1150 if len(args) > 2 && args[1] == "identifier" { 1151 id, err := strconv.ParseUint(args[2], 10, 32) 1152 if err != nil { 1153 return nil, fmt.Errorf("invalid format") 1154 } 1155 nlri.SetPathIdentifier(uint32(id)) 1156 extcomms = args[3:] 1157 } else { 1158 extcomms = args[1:] 1159 } 1160 1161 case bgp.RF_IPv4_VPN, bgp.RF_IPv6_VPN: 1162 if len(args) < 5 || args[1] != "label" || args[3] != "rd" { 1163 return nil, fmt.Errorf("invalid format") 1164 } 1165 ip, nw, err := net.ParseCIDR(args[0]) 1166 if err != nil { 1167 return nil, err 1168 } 1169 ones, _ := nw.Mask.Size() 1170 1171 label, err := strconv.ParseUint(args[2], 10, 32) 1172 if err != nil { 1173 return nil, fmt.Errorf("invalid format") 1174 } 1175 mpls := bgp.NewMPLSLabelStack(uint32(label)) 1176 1177 rd, err := bgp.ParseRouteDistinguisher(args[4]) 1178 if err != nil { 1179 return nil, err 1180 } 1181 1182 extcomms = args[5:] 1183 1184 if rf == bgp.RF_IPv4_VPN { 1185 if ip.To4() == nil { 1186 return nil, fmt.Errorf("invalid ipv4 prefix") 1187 } 1188 nlri = bgp.NewLabeledVPNIPAddrPrefix(uint8(ones), ip.String(), *mpls, rd) 1189 } else { 1190 if ip.To16() == nil { 1191 return nil, fmt.Errorf("invalid ipv6 prefix") 1192 } 1193 nlri = bgp.NewLabeledVPNIPv6AddrPrefix(uint8(ones), ip.String(), *mpls, rd) 1194 } 1195 case bgp.RF_IPv4_MPLS, bgp.RF_IPv6_MPLS: 1196 if len(args) < 2 { 1197 return nil, fmt.Errorf("invalid format") 1198 } 1199 1200 ip, nw, err := net.ParseCIDR(args[0]) 1201 if err != nil { 1202 return nil, err 1203 } 1204 ones, _ := nw.Mask.Size() 1205 1206 mpls, err := bgp.ParseMPLSLabelStack(args[1]) 1207 if err != nil { 1208 return nil, err 1209 } 1210 1211 extcomms = args[2:] 1212 1213 if rf == bgp.RF_IPv4_MPLS { 1214 if ip.To4() == nil { 1215 return nil, fmt.Errorf("invalid ipv4 prefix") 1216 } 1217 nlri = bgp.NewLabeledIPAddrPrefix(uint8(ones), ip.String(), *mpls) 1218 } else { 1219 if ip.To4() != nil { 1220 return nil, fmt.Errorf("invalid ipv6 prefix") 1221 } 1222 nlri = bgp.NewLabeledIPv6AddrPrefix(uint8(ones), ip.String(), *mpls) 1223 } 1224 case bgp.RF_EVPN: 1225 nlri, extcomms, err = parseEvpnArgs(args) 1226 case bgp.RF_FS_IPv4_UC, bgp.RF_FS_IPv4_VPN, bgp.RF_FS_IPv6_UC, bgp.RF_FS_IPv6_VPN, bgp.RF_FS_L2_VPN: 1227 nlri, extcomms, err = parseFlowSpecArgs(rf, args) 1228 case bgp.RF_OPAQUE: 1229 m, err := extractReserved(args, map[string]int{ 1230 "key": paramSingle, 1231 "value": paramSingle}) 1232 if err != nil { 1233 return nil, err 1234 } 1235 if len(m["key"]) != 1 { 1236 return nil, fmt.Errorf("opaque nlri key missing") 1237 } 1238 if len(m["value"]) > 0 { 1239 nlri = bgp.NewOpaqueNLRI([]byte(m["key"][0]), []byte(m["value"][0])) 1240 } else { 1241 nlri = bgp.NewOpaqueNLRI([]byte(m["key"][0]), nil) 1242 } 1243 default: 1244 return nil, fmt.Errorf("Unsupported route family: %s", rf) 1245 } 1246 if err != nil { 1247 return nil, err 1248 } 1249 1250 if rf == bgp.RF_IPv4_UC && net.ParseIP(nexthop).To4() != nil { 1251 attrs = append(attrs, bgp.NewPathAttributeNextHop(nexthop)) 1252 } else { 1253 mpreach := bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri}) 1254 attrs = append(attrs, mpreach) 1255 } 1256 1257 if extcomms != nil { 1258 extcomms, err := parseExtendedCommunities(extcomms) 1259 if err != nil { 1260 return nil, err 1261 } 1262 normalextcomms := make([]bgp.ExtendedCommunityInterface, 0) 1263 ipv6extcomms := make([]bgp.ExtendedCommunityInterface, 0) 1264 for _, com := range extcomms { 1265 switch com.(type) { 1266 case *bgp.RedirectIPv6AddressSpecificExtended: 1267 ipv6extcomms = append(ipv6extcomms, com) 1268 default: 1269 normalextcomms = append(normalextcomms, com) 1270 } 1271 } 1272 if len(normalextcomms) != 0 { 1273 p := bgp.NewPathAttributeExtendedCommunities(normalextcomms) 1274 attrs = append(attrs, p) 1275 } 1276 if len(ipv6extcomms) != 0 { 1277 ip6p := bgp.NewPathAttributeIP6ExtendedCommunities(ipv6extcomms) 1278 attrs = append(attrs, ip6p) 1279 } 1280 } 1281 sort.Slice(attrs, func(i, j int) bool { return attrs[i].GetType() < attrs[j].GetType() }) 1282 1283 return apiutil.NewPath(nlri, false, attrs, time.Now()), nil 1284 } 1285 1286 func showGlobalRib(args []string) error { 1287 return showNeighborRib(cmdGlobal, "", args) 1288 } 1289 1290 func modPath(resource string, name, modtype string, args []string) error { 1291 f, err := checkAddressFamily(ipv4UC) 1292 if err != nil { 1293 return err 1294 } 1295 rf := apiutil.ToRouteFamily(f) 1296 path, err := parsePath(rf, args) 1297 if err != nil { 1298 cmdstr := "global" 1299 if resource == cmdVRF { 1300 cmdstr = fmt.Sprintf("vrf %s", name) 1301 } 1302 rdHelpMsgFmt := ` 1303 <RD> : xxx:yyy, xxx.xxx.xxx.xxx:yyy, xxx.xxx:yyy` 1304 ss := make([]string, 0, len(bgp.ProtocolNameMap)) 1305 for _, v := range bgp.ProtocolNameMap { 1306 ss = append(ss, v) 1307 } 1308 sort.SliceStable(ss, func(i, j int) bool { return ss[i] < ss[j] }) 1309 ss = append(ss, "<DEC_NUM>") 1310 ipProtocols := strings.Join(ss, ", ") 1311 ss = make([]string, 0, len(bgp.TCPFlagNameMap)) 1312 for _, v := range bgp.TCPSortedFlags { 1313 ss = append(ss, bgp.TCPFlagNameMap[v]) 1314 } 1315 tcpFlags := strings.Join(ss, ", ") 1316 ss = make([]string, 0, len(bgp.EthernetTypeNameMap)) 1317 for _, v := range bgp.EthernetTypeNameMap { 1318 ss = append(ss, v) 1319 } 1320 sort.SliceStable(ss, func(i, j int) bool { return ss[i] < ss[j] }) 1321 ss = append(ss, "<DEC_NUM>") 1322 etherTypes := strings.Join(ss, ", ") 1323 helpErrMap := map[bgp.RouteFamily]error{} 1324 baseHelpMsgFmt := fmt.Sprintf(`error: %s 1325 usage: %s rib -a %%s %s <PREFIX> %%s [origin { igp | egp | incomplete }] [aspath <ASPATH>] [nexthop <ADDRESS>] [med <NUM>] [local-pref <NUM>] [community <COMMUNITY>] [aigp metric <NUM>] [large-community <LARGE_COMMUNITY>] [aggregator <AGGREGATOR>] 1326 <ASPATH>: <AS>[,<AS>], 1327 <COMMUNITY>: xxx:xxx|internet|planned-shut|accept-own|route-filter-translated-v4|route-filter-v4|route-filter-translated-v6|route-filter-v6|llgr-stale|no-llgr|blackhole|no-export|no-advertise|no-export-subconfed|no-peer, 1328 <LARGE_COMMUNITY>: xxx:xxx:xxx[,<LARGE_COMMUNITY>], 1329 <AGGREGATOR>: <AS>:<ADDRESS>`, 1330 err, 1331 cmdstr, 1332 // <address family> 1333 modtype, 1334 // <label, rd> 1335 ) 1336 helpErrMap[bgp.RF_IPv4_UC] = fmt.Errorf(baseHelpMsgFmt, "ipv4", "[identifier <VALUE>]") 1337 helpErrMap[bgp.RF_IPv6_UC] = fmt.Errorf(baseHelpMsgFmt, "ipv6", "[identifier <VALUE>]") 1338 helpErrMap[bgp.RF_IPv4_VPN] = fmt.Errorf(baseHelpMsgFmt, "vpnv4", "label <LABEL> rd <RD> [rt <RT>]") 1339 helpErrMap[bgp.RF_IPv6_VPN] = fmt.Errorf(baseHelpMsgFmt, "vpnv6", "label <LABEL> rd <RD> [rt <RT>]") 1340 helpErrMap[bgp.RF_IPv4_MPLS] = fmt.Errorf(baseHelpMsgFmt, "ipv4-mpls", "<LABEL>") 1341 helpErrMap[bgp.RF_IPv6_MPLS] = fmt.Errorf(baseHelpMsgFmt, "ipv6-mpls", "<LABEL>") 1342 1343 fsHelpMsgFmt := fmt.Sprintf(`error: %s 1344 usage: %s rib -a %%s %s%%s match <MATCH> then <THEN>%%s%%s%%s 1345 <THEN> : { %s | 1346 %s | 1347 %s <RATE> [as <AS>] | 1348 %s <RT> | 1349 %s <DEC_NUM> | 1350 %s { sample | terminal | sample-terminal } }... 1351 <RT> : xxx:yyy, xxx.xxx.xxx.xxx:yyy, xxxx::xxxx:yyy, xxx.xxx:yyy`, 1352 err, 1353 cmdstr, 1354 // <address family> 1355 modtype, 1356 // "" or " rd <RD>" 1357 // "" or " [rt <RT>]" 1358 // <help message for RD> 1359 // <MATCH> 1360 extCommNameMap[ctAccept], 1361 extCommNameMap[ctDiscard], 1362 extCommNameMap[ctRate], 1363 extCommNameMap[ctRedirect], 1364 extCommNameMap[ctMark], 1365 extCommNameMap[ctAction], 1366 ) 1367 baseFsMatchExpr := fmt.Sprintf(` 1368 <MATCH> : { %s <PREFIX> [<OFFSET>] | 1369 %s <PREFIX> [<OFFSET>] | 1370 %s <PROTOCOLS>... | 1371 %s <FRAGMENTS>... | 1372 %s <TCP_FLAGS>... | 1373 %s <ITEM>... | 1374 %s <ITEM>... | 1375 %s <ITEM>... | 1376 %s <ITEM>... | 1377 %s <ITEM>... | 1378 %s <ITEM>... | 1379 %s <ITEM>... %%s}... 1380 <PROTOCOLS> : [&] [<|<=|>|>=|==|!=] <PROTOCOL> 1381 <PROTOCOL> : %s 1382 <FRAGMENTS> : [&] [=|!|!=] <FRAGMENT> 1383 <FRAGMENT> : dont-fragment, is-fragment, first-fragment, last-fragment, not-a-fragment 1384 <TCP_FLAGS> : [&] [=|!|!=] <TCP_FLAG> 1385 <TCP_FLAG> : %s%%s 1386 <ITEM> : [&] [<|<=|>|>=|==|!=] <DEC_NUM>`, 1387 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_DST_PREFIX], 1388 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_SRC_PREFIX], 1389 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_IP_PROTO], 1390 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_FRAGMENT], 1391 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_TCP_FLAG], 1392 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_PORT], 1393 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_DST_PORT], 1394 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_SRC_PORT], 1395 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_ICMP_TYPE], 1396 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_ICMP_CODE], 1397 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_PKT_LEN], 1398 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_DSCP], 1399 // <additional help messages if exists> 1400 ipProtocols, 1401 tcpFlags, 1402 // <additional help messages if exists> 1403 ) 1404 ipv4FsMatchExpr := fmt.Sprintf(baseFsMatchExpr, "", "") 1405 ipv6FsMatchExpr := fmt.Sprintf(baseFsMatchExpr, fmt.Sprintf(`| 1406 %s <ITEM>... `, 1407 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LABEL]), "") 1408 l2vpnFsMatchExpr := fmt.Sprintf(baseFsMatchExpr, fmt.Sprintf(`| 1409 %s <ITEM>... | 1410 %s <MAC_ADDRESS> | 1411 %s <MAC_ADDRESS> | 1412 %s <ETHER_TYPES>... | 1413 %s <ITEM>... | 1414 %s <ITEM>... | 1415 %s <ITEM>... | 1416 %s <ITEM>... | 1417 %s <ITEM>... | 1418 %s <ITEM>... | 1419 %s <ITEM>... | 1420 %s <ITEM>... `, 1421 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LABEL], 1422 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_DST_MAC], 1423 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_SRC_MAC], 1424 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_ETHERNET_TYPE], 1425 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LLC_DSAP], 1426 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LLC_SSAP], 1427 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LLC_CONTROL], 1428 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_SNAP], 1429 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_VID], 1430 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_COS], 1431 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_INNER_VID], 1432 bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_INNER_COS]), fmt.Sprintf(` 1433 <ETHER_TYPES> : [&] [<|<=|>|>=|==|!=] <ETHER_TYPE> 1434 <ETHER_TYPE> : %s`, 1435 etherTypes)) 1436 helpErrMap[bgp.RF_FS_IPv4_UC] = fmt.Errorf(fsHelpMsgFmt, "ipv4-flowspec", "", "", "", ipv4FsMatchExpr) 1437 helpErrMap[bgp.RF_FS_IPv6_UC] = fmt.Errorf(fsHelpMsgFmt, "ipv6-flowspec", "", "", "", ipv6FsMatchExpr) 1438 helpErrMap[bgp.RF_FS_IPv4_VPN] = fmt.Errorf(fsHelpMsgFmt, "ipv4-l3vpn-flowspec", " rd <RD>", " [rt <RT>]", rdHelpMsgFmt, ipv4FsMatchExpr) 1439 helpErrMap[bgp.RF_FS_IPv6_VPN] = fmt.Errorf(fsHelpMsgFmt, "ipv6-l3vpn-flowspec", " rd <RD>", " [rt <RT>]", rdHelpMsgFmt, ipv6FsMatchExpr) 1440 helpErrMap[bgp.RF_FS_L2_VPN] = fmt.Errorf(fsHelpMsgFmt, "l2vpn-flowspec", " rd <RD>", " [rt <RT>]", rdHelpMsgFmt, l2vpnFsMatchExpr) 1441 helpErrMap[bgp.RF_EVPN] = fmt.Errorf(`error: %s 1442 usage: %s rib %s { a-d <A-D> | macadv <MACADV> | multicast <MULTICAST> | esi <ESI> | prefix <PREFIX> } -a evpn 1443 <A-D> : esi <esi> etag <etag> label <label> rd <rd> [rt <rt>...] [encap <encap type>] [esi-label <esi-label> [single-active | all-active]] 1444 <MACADV> : <mac address> <ip address> [esi <esi>] etag <etag> label <label> rd <rd> [rt <rt>...] [encap <encap type>] [router-mac <mac address>] [default-gateway] 1445 <MULTICAST> : <ip address> etag <etag> rd <rd> [rt <rt>...] [encap <encap type>] [pmsi <type> [leaf-info-required] <label> <tunnel-id>] 1446 <ESI> : <ip address> esi <esi> rd <rd> [rt <rt>...] [encap <encap type>] 1447 <PREFIX> : <ip prefix> [gw <gateway>] [esi <esi>] etag <etag> [label <label>] rd <rd> [rt <rt>...] [encap <encap type>] [router-mac <mac address>]`, 1448 err, 1449 cmdstr, 1450 modtype, 1451 ) 1452 helpErrMap[bgp.RF_OPAQUE] = fmt.Errorf(`error: %s 1453 usage: %s rib %s key <KEY> [value <VALUE>]`, 1454 err, 1455 cmdstr, 1456 modtype, 1457 ) 1458 if err, ok := helpErrMap[rf]; ok { 1459 return err 1460 } 1461 return err 1462 } 1463 1464 r := api.TableType_GLOBAL 1465 if resource == cmdVRF { 1466 r = api.TableType_VRF 1467 } 1468 1469 if modtype == cmdAdd { 1470 _, err = client.AddPath(ctx, &api.AddPathRequest{ 1471 TableType: r, 1472 VrfId: name, 1473 Path: path, 1474 }) 1475 } else { 1476 _, err = client.DeletePath(ctx, &api.DeletePathRequest{ 1477 TableType: r, 1478 VrfId: name, 1479 Path: path, 1480 }) 1481 } 1482 return err 1483 } 1484 1485 func showGlobalConfig() error { 1486 r, err := client.GetBgp(ctx, &api.GetBgpRequest{}) 1487 if err != nil { 1488 return err 1489 } 1490 if globalOpts.Json { 1491 j, _ := json.Marshal(r.Global) 1492 fmt.Println(string(j)) 1493 return nil 1494 } 1495 g := r.Global 1496 fmt.Println("AS: ", g.As) 1497 fmt.Println("Router-ID:", g.RouterId) 1498 if len(g.ListenAddresses) > 0 { 1499 fmt.Printf("Listening Port: %d, Addresses: %s\n", g.ListenPort, strings.Join(g.ListenAddresses, ", ")) 1500 } 1501 if g.UseMultiplePaths { 1502 fmt.Printf("Multipath: enabled") 1503 } 1504 return nil 1505 } 1506 1507 func modGlobalConfig(args []string) error { 1508 m, err := extractReserved(args, map[string]int{ 1509 "as": paramSingle, 1510 "router-id": paramSingle, 1511 "listen-port": paramSingle, 1512 "listen-addresses": paramList, 1513 "use-multipath": paramFlag}) 1514 if err != nil || len(m["as"]) != 1 || len(m["router-id"]) != 1 { 1515 return fmt.Errorf("usage: gobgp global as <VALUE> router-id <VALUE> [use-multipath] [listen-port <VALUE>] [listen-addresses <VALUE>...]") 1516 } 1517 asn, err := strconv.ParseUint(m["as"][0], 10, 32) 1518 if err != nil { 1519 return err 1520 } 1521 id := net.ParseIP(m["router-id"][0]) 1522 if id.To4() == nil { 1523 return fmt.Errorf("invalid router-id format") 1524 } 1525 var port int64 1526 if len(m["listen-port"]) > 0 { 1527 // Note: GlobalConfig.Port is uint32 type, but the TCP/UDP port is 1528 // 16-bit length. 1529 port, err = strconv.ParseInt(m["listen-port"][0], 10, 16) 1530 if err != nil { 1531 return err 1532 } 1533 } 1534 useMultipath := false 1535 if _, ok := m["use-multipath"]; ok { 1536 useMultipath = true 1537 } 1538 _, err = client.StartBgp(ctx, &api.StartBgpRequest{ 1539 Global: &api.Global{ 1540 As: uint32(asn), 1541 RouterId: id.String(), 1542 ListenPort: int32(port), 1543 ListenAddresses: m["listen-addresses"], 1544 UseMultiplePaths: useMultipath, 1545 }, 1546 }) 1547 return err 1548 } 1549 1550 func newGlobalCmd() *cobra.Command { 1551 globalCmd := &cobra.Command{ 1552 Use: cmdGlobal, 1553 Run: func(cmd *cobra.Command, args []string) { 1554 var err error 1555 if len(args) != 0 { 1556 err = modGlobalConfig(args) 1557 } else { 1558 err = showGlobalConfig() 1559 } 1560 if err != nil { 1561 exitWithError(err) 1562 } 1563 }, 1564 } 1565 1566 ribCmd := &cobra.Command{ 1567 Use: cmdRib, 1568 Run: func(cmd *cobra.Command, args []string) { 1569 if err := showGlobalRib(args); err != nil { 1570 exitWithError(err) 1571 } 1572 }, 1573 } 1574 1575 ribCmd.PersistentFlags().StringVarP(&subOpts.AddressFamily, "address-family", "a", "", "address family") 1576 1577 for _, v := range []string{cmdAdd, cmdDel} { 1578 cmd := &cobra.Command{ 1579 Use: v, 1580 Run: func(cmd *cobra.Command, args []string) { 1581 err := modPath(cmdGlobal, "", cmd.Use, args) 1582 if err != nil { 1583 exitWithError(err) 1584 } 1585 }, 1586 } 1587 ribCmd.AddCommand(cmd) 1588 1589 if v == cmdDel { 1590 subcmd := &cobra.Command{ 1591 Use: cmdAll, 1592 Run: func(cmd *cobra.Command, args []string) { 1593 family, err := checkAddressFamily(ipv4UC) 1594 if err != nil { 1595 exitWithError(err) 1596 } 1597 if _, err = client.DeletePath(ctx, &api.DeletePathRequest{ 1598 TableType: api.TableType_GLOBAL, 1599 Family: family, 1600 }); err != nil { 1601 exitWithError(err) 1602 } 1603 }, 1604 } 1605 cmd.AddCommand(subcmd) 1606 } 1607 } 1608 1609 summaryCmd := &cobra.Command{ 1610 Use: cmdSummary, 1611 Run: func(cmd *cobra.Command, args []string) { 1612 if err := showRibInfo(cmdGlobal, ""); err != nil { 1613 exitWithError(err) 1614 } 1615 }, 1616 } 1617 ribCmd.AddCommand(summaryCmd) 1618 1619 policyCmd := &cobra.Command{ 1620 Use: cmdPolicy, 1621 Run: func(cmd *cobra.Command, args []string) { 1622 if len(args) > 0 { 1623 exitWithError(fmt.Errorf("usage: gobgp global policy [{ import | export }]")) 1624 } 1625 for _, v := range []string{cmdImport, cmdExport} { 1626 if err := showNeighborPolicy("", v, 4); err != nil { 1627 exitWithError(err) 1628 } 1629 } 1630 }, 1631 } 1632 1633 for _, v := range []string{cmdImport, cmdExport} { 1634 cmd := &cobra.Command{ 1635 Use: v, 1636 Run: func(cmd *cobra.Command, args []string) { 1637 if err := showNeighborPolicy("", cmd.Use, 0); err != nil { 1638 exitWithError(err) 1639 } 1640 }, 1641 } 1642 1643 for _, w := range []string{cmdAdd, cmdDel, cmdSet} { 1644 subcmd := &cobra.Command{ 1645 Use: w, 1646 Run: func(subcmd *cobra.Command, args []string) { 1647 err := modNeighborPolicy("", cmd.Use, subcmd.Use, args) 1648 if err != nil { 1649 exitWithError(err) 1650 } 1651 }, 1652 } 1653 cmd.AddCommand(subcmd) 1654 } 1655 1656 policyCmd.AddCommand(cmd) 1657 } 1658 1659 delCmd := &cobra.Command{ 1660 Use: cmdDel, 1661 } 1662 1663 allCmd := &cobra.Command{ 1664 Use: cmdAll, 1665 Run: func(cmd *cobra.Command, args []string) { 1666 if _, err := client.StopBgp(ctx, &api.StopBgpRequest{}); err != nil { 1667 exitWithError(err) 1668 } 1669 }, 1670 } 1671 delCmd.AddCommand(allCmd) 1672 1673 globalCmd.AddCommand(ribCmd, policyCmd, delCmd) 1674 return globalCmd 1675 }