| package main |
| |
| import ( |
| "context" |
| "crypto/x509" |
| "flag" |
| "fmt" |
| "sort" |
| "strings" |
| |
| pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto" |
| "github.com/golang/glog" |
| "google.golang.org/grpc" |
| "google.golang.org/grpc/credentials" |
| ) |
| |
| var ( |
| flagVerifier string |
| flagIdentity string |
| flagRouterID string |
| flagV6 bool |
| flagLocalIP string |
| flagUpstream bool |
| ) |
| |
| func init() { |
| flag.Set("logtostderr", "true") |
| } |
| |
| func main() { |
| flag.StringVar(&flagVerifier, "verifier", "", "Verifier endpoint") |
| flag.StringVar(&flagIdentity, "identity", "", "Router identity") |
| flag.StringVar(&flagRouterID, "router_id", "", "Router ID") |
| flag.StringVar(&flagLocalIP, "local_ip", "", "Local IP") |
| flag.BoolVar(&flagV6, "v6", false, "V6") |
| flag.BoolVar(&flagUpstream, "upstream", false, "Is an upstream router") |
| |
| flag.Parse() |
| |
| if flagVerifier == "" { |
| glog.Exit("verifier must be set") |
| } |
| if flagIdentity == "" { |
| glog.Exit("identity must be set") |
| } |
| if flagRouterID == "" { |
| glog.Exit("router_id must be set") |
| } |
| |
| cpool, _ := x509.SystemCertPool() |
| creds := credentials.NewClientTLSFromCert(cpool, "") |
| conn, err := grpc.Dial(flagVerifier, grpc.WithTransportCredentials(creds)) |
| if err != nil { |
| glog.Exitf("Dial: %v", err) |
| } |
| |
| ctx := context.Background() |
| verifier := pb.NewVerifierClient(conn) |
| |
| req := &pb.RouterHeartbeatRequest{ |
| Name: flagIdentity, |
| } |
| |
| res, err := verifier.RouterHeartbeat(ctx, req) |
| if err != nil { |
| glog.Exitf("RouterHeartbeat: %v", err) |
| } |
| |
| var config string |
| if flagV6 { |
| config = ` |
| log syslog all; |
| |
| router id %s; |
| debug protocols { states, interfaces, events }; |
| |
| timeformat base iso long; |
| timeformat log iso long; |
| timeformat protocol iso long; |
| timeformat route iso long; |
| |
| define ourASN = %d; |
| define filteredNumber = 65666; |
| define customerNumber = 65667; |
| define martian = 1; |
| define prefixTooLong64 = 2; |
| define prefixTooLong24 = 3; |
| define pathBogon = 4; |
| define pathTooLong = 5; |
| define pathFirstNotPeer = 6; |
| define prefixFiltered = 9; |
| |
| protocol device {} |
| |
| function net_martians() { |
| return net ~ [ fc00::/7+, fec0::/10+, ::/128-, ::/0{0,15}, ::/0{49,128} ]; |
| } |
| |
| function generic_in() { |
| if net_martians() then { |
| bgp_large_community.add((ourASN, filteredNumber, martian)); |
| return false; |
| } |
| if bgp_path.len > 64 then { |
| bgp_large_community.add((ourASN, filteredNumber, pathTooLong)); |
| return false; |
| } |
| if net.len > 64 then { |
| bgp_large_community.add((ourASN, filteredNumber, prefixTooLong64)); |
| return false; |
| } |
| return true; |
| } |
| ` |
| config = fmt.Sprintf(config, flagRouterID, 208521) |
| } else { |
| config = ` |
| log syslog all; |
| |
| router id %s; |
| debug protocols { states, interfaces, events }; |
| |
| timeformat base iso long; |
| timeformat log iso long; |
| timeformat protocol iso long; |
| timeformat route iso long; |
| |
| define ourASN = %d; |
| define filteredNumber = 65666; |
| define customerNumber = 65667; |
| define martian = 1; |
| define prefixTooLong64 = 2; |
| define prefixTooLong24 = 3; |
| define pathBogon = 4; |
| define pathTooLong = 5; |
| define pathFirstNotPeer = 6; |
| define prefixFiltered = 9; |
| |
| protocol device {} |
| |
| function net_martians() { |
| return net ~ [ 169.254.0.0/16+, 172.16.0.0/12+, 192.168.0.0/16+, 10.0.0.0/8+, |
| 127.0.0.0/8+, 224.0.0.0/4+, 240.0.0.0/4+, 0.0.0.0/32-, 0.0.0.0/0{25,32}, 0.0.0.0/0{0,7} ]; |
| } |
| |
| function generic_in() { |
| if net_martians() then { |
| bgp_large_community.add((ourASN, filteredNumber, martian)); |
| return false; |
| } |
| if bgp_path.len > 64 then { |
| bgp_large_community.add((ourASN, filteredNumber, pathTooLong)); |
| return false; |
| } |
| if net.len > 24 then { |
| bgp_large_community.add((ourASN, filteredNumber, prefixTooLong24)); |
| return false; |
| } |
| return true; |
| } |
| ` |
| config = fmt.Sprintf(config, flagRouterID, 208521) |
| } |
| |
| if flagUpstream { |
| config += ` |
| protocol kernel { |
| scan time 60; |
| import none; |
| export all; |
| } |
| |
| filter UPSTREAM_C3NOC_IN { |
| if net_martians() then reject; |
| if bgp_path.len > 64 then reject; |
| accept; |
| } |
| |
| filter UPSTREAM_C3NOC_OUT { |
| accept; |
| } |
| |
| ` |
| if flagV6 { |
| config += ` |
| protocol bgp UPSTREAM_C3NOC { |
| import filter UPSTREAM_C3NOC_IN; |
| export filter UPSTREAM_C3NOC_OUT; |
| description "UPSTREAM AS13020"; |
| |
| local 2a0d:eb02:4242:4242::7 as 208521; |
| neighbor 2a0d:eb02:4242:4242::250 as 13020; |
| } |
| ` |
| } else { |
| config += ` |
| protocol bgp UPSTREAM_C3NOC { |
| import filter UPSTREAM_C3NOC_IN; |
| export filter UPSTREAM_C3NOC_OUT; |
| |
| local 185.236.243.7 as 208521; |
| neighbor 185.236.243.250 as 13020; |
| } |
| ` |
| } |
| } |
| |
| sort.Slice(res.AsConfigs, func(i, j int) bool { |
| return res.AsConfigs[i].Asn < res.AsConfigs[j].Asn |
| }) |
| |
| for _, asc := range res.AsConfigs { |
| sort.Slice(asc.Routers, func(i, j int) bool { |
| return asc.Routers[i].Password < asc.Routers[j].Password |
| }) |
| for i, router := range asc.Routers { |
| addr := "" |
| if flagV6 { |
| addr = router.Ipv6 |
| } else { |
| addr = router.Ipv4 |
| } |
| if addr == "" { |
| continue |
| } |
| peerid := fmt.Sprintf("PEER_%d_%d", asc.Asn, i) |
| prefixes := []string{} |
| for _, prefix := range asc.Prefixes { |
| if flagV6 && !strings.Contains(prefix.Prefix, ":") { |
| continue |
| } |
| if !flagV6 && !strings.Contains(prefix.Prefix, ".") { |
| continue |
| } |
| parts := strings.Split(prefix.Prefix, "/") |
| addr := parts[0] |
| bits := parts[1] |
| filter := fmt.Sprintf("%s/%s{%s,%d}", addr, bits, bits, prefix.MaxLength) |
| if fmt.Sprintf("%d", prefix.MaxLength) == bits { |
| filter = fmt.Sprintf("%s/%s", addr, bits) |
| } |
| prefixes = append(prefixes, filter) |
| } |
| if len(prefixes) == 0 { |
| continue |
| } |
| allowed := strings.Join(prefixes, ",") |
| |
| part := ` |
| filter %s_in { |
| if !generic_in() then reject; |
| if bgp_path.first != %d then { |
| bgp_large_community.add((ourASN, filteredNumber, pathFirstNotPeer)); |
| reject; |
| } |
| if bgp_path.last != %d then { |
| bgp_large_community.add((ourASN, filteredNumber, pathFirstNotPeer)); |
| reject; |
| } |
| if ! (net ~ [ %s ]) then { |
| bgp_large_community.add((ourASN, filteredNumber, prefixFiltered)); |
| reject; |
| } |
| bgp_large_community.add((ourASN, customerNumber, 2137)); |
| accept; |
| } |
| ` |
| part = fmt.Sprintf(part, peerid, asc.Asn, asc.Asn, allowed) |
| config += part |
| |
| part = ` |
| filter %s_out { |
| if (ourASN, customerNumber, 2137) ~ bgp_large_community then reject; |
| accept; |
| } |
| ` |
| part = fmt.Sprintf(part, peerid) |
| config += part |
| |
| part = ` |
| protocol bgp %s { |
| local %s as 208521; |
| description "PEER AS%d R%d"; |
| neighbor %s as %d; |
| import filter %s_in; |
| import keep filtered on; |
| export filter %s_out; |
| password "%s"; |
| } |
| ` |
| part = fmt.Sprintf(part, peerid, flagLocalIP, asc.Asn, i, addr, asc.Asn, peerid, peerid, router.Password) |
| config += part |
| } |
| } |
| |
| fmt.Println(config) |
| } |