cccampix: updates from camp

Change-Id: I77e6d9fb6e91b0b7e2d1f89e80164ee8116b5d50
diff --git a/bgpwtf/cccampix/birdie/birdie.go b/bgpwtf/cccampix/birdie/birdie.go
index c063d87..457b567 100644
--- a/bgpwtf/cccampix/birdie/birdie.go
+++ b/bgpwtf/cccampix/birdie/birdie.go
@@ -20,6 +20,7 @@
 	flagRouterID string
 	flagV6       bool
 	flagLocalIP  string
+	flagUpstream bool
 )
 
 func init() {
@@ -32,6 +33,7 @@
 	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()
 
@@ -77,6 +79,17 @@
 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() {
@@ -84,14 +97,22 @@
 }
 
 function generic_in() {
-	if net_martians() then return false;
-	if bgp_path.len > 64 then return false;
-	if net.len > 64 then return false;
-	if net.len < 12 then return false;
+	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)
+		config = fmt.Sprintf(config, flagRouterID, 208521)
 	} else {
 		config = `
 log syslog all;
@@ -104,6 +125,17 @@
 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() {
@@ -112,14 +144,65 @@
 }
 
 function generic_in() {
-	if net_martians() then return false;
-	if bgp_path.len > 64 then return false;
-	if net.len > 24 then return false;
-	if net.len < 8 then return false;
+	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)
+		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 {
@@ -166,15 +249,28 @@
 			part := `
 filter %s_in {
 	if !generic_in() then reject;
-	if net ~ [ %s ] then accept;
-	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, allowed)
+			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;
 }
 `
@@ -184,12 +280,15 @@
 			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, addr, asc.Asn, peerid, router.Password)
+			part = fmt.Sprintf(part, peerid, flagLocalIP, asc.Asn, i, addr, asc.Asn, peerid, peerid, router.Password)
 			config += part
 		}
 	}
diff --git a/bgpwtf/cccampix/frontend/templates/index.html b/bgpwtf/cccampix/frontend/templates/index.html
index 00f0522..035f822 100644
--- a/bgpwtf/cccampix/frontend/templates/index.html
+++ b/bgpwtf/cccampix/frontend/templates/index.html
@@ -25,6 +25,10 @@
         <p>
             You can use this page to see how your CCCamp IX registration is progressing.
         </p>
+        <h2>Looking Glass</h2>
+        <p>
+            We're running a Looking Glass web interface for our exchange at <a href="https://ix-lg.bgp.wtf/">ix-lg.bgp.wtf</a>.
+        </p>
         <h2>System Status</h2>
         <p>
             If any of the above processors are down, provisioning of new peers might be delayed.
diff --git a/bgpwtf/cccampix/kube/ix.libsonnet b/bgpwtf/cccampix/kube/ix.libsonnet
index ff74f2b..7496dad 100644
--- a/bgpwtf/cccampix/kube/ix.libsonnet
+++ b/bgpwtf/cccampix/kube/ix.libsonnet
@@ -5,10 +5,9 @@
         local ix = self,
         local cfg = ix.cfg,
         cfg:: {
-            image: "registry.k0.hswaw.net/bgpwtf/cccampix:1566475793-53f188c8fe83781ac057a3442830c6aa3dce5269",
+            image: "registry.k0.hswaw.net/bgpwtf/cccampix:1566584484-a2960f526c36de0dbcd911f05ee9db587e63eb9b",
 
-            domain: "ix-status.bgp.wtf",
-            grpcDomain: "ix-grpc.bgp.wtf",
+
             octorpki: {
                 image: cfg.image,
                 storageClassName: "waw-hdd-redundant-2",
@@ -20,6 +19,7 @@
 
             verifier: {
                 image: cfg.image,
+                domain: "ix-grpc.bgp.wtf",
                 db: {
                     host: "public.crdb-waw1.svc.cluster.local",
                     port: 26257,
@@ -49,9 +49,15 @@
             },
 
             frontend: {
+                domain: "ix-status.bgp.wtf",
                 image: cfg.image,
             },
 
+            alice: {
+                domain: "ix-lg.bgp.wtf",
+                image: "registry.k0.hswaw.net/q3k/alice-lg:20190823-1557",
+            },
+
             appName: "ix",
             namespace: error "namespace must be defined",
             prefix: "",
@@ -243,6 +249,148 @@
             ],
         },
 
+        alice: ix.component("alice") {
+            port: 7340,
+            volumes: {
+                config: kube.ConfigMapVolume(ix.alice.configMap),
+                theme: kube.ConfigMapVolume(ix.alice.themeMap),
+            },
+            volumeMounts: {
+                config: {
+                    mountPath: "/etc/alice",
+                },
+                theme: {
+                    mountPath: "/etc/alice-theme",
+                },
+            },
+            args: [
+                "/usr/bin/alice-lg",
+                "-config", "/etc/alice/alice",
+            ],
+
+            themeMap: kube.ConfigMap(ix.name("alice-theme")) {
+                metadata+: ix.metadata("alice-theme"),
+                data: {
+                    "content.js": |||
+                        Alice.updateContent({
+                          header: {
+                              title: "CCCampIX Looking Glass",
+                              tagline: "powered by alice-lg"
+                          },
+                          welcome: {
+                              title: "CCCampIX Looking Glass",
+                              tagline: "BGP to the tent."
+                          }
+                        });
+                    |||,
+                },
+            },
+            configMap: kube.ConfigMap(ix.name("alice")) {
+                metadata+: ix.metadata("alice"),
+                data: {
+                    config: |||
+                        [server]
+                        listen_http = 0.0.0.0:7340
+                        enable_neighbors_status_refresh = false
+                        asn = 208521
+
+                        [housekeeping]
+                        interval = 5
+                        force_release_memory = true
+
+                        [theme]
+                        path = /etc/alice-theme
+
+                        [pagination]
+                        routes_filtered_page_size = 250
+                        routes_accepted_page_size = 250
+                        routes_not_exported_page_size = 250
+
+                        [rejection_reasons]
+                        208521:65666:1 = An IP Bogon was detected
+                        208521:65666:2 = Prefix is longer than 64
+                        208521:65666:3 = Prefix is longer than 24
+                        208521:65666:4 = AS path contains a bogon AS
+                        208521:65666:5 = AS path length is longer than 64
+                        208521:65666:6 = BGP Path invalid (must be only peer)
+                        208521:65666:9 = Prefix not found in RPKI for Origin AS
+
+                        [neighbours_columns]
+                        Description = Description
+                        address = Neighbour
+                        asn = ASN
+                        state = State
+                        Uptime = Uptime
+                        routes_received = Routes Received
+                        routes_filtered = Filtered
+
+                        [routes_columns]
+                        network = Network
+                        gateway = Gateway
+                        interface = Interface
+                        metric = Metric
+                        bgp.as_path = AS Path
+
+                        [lookup_columns]
+                        network = Network
+                        gateway = Gateway
+                        neighbour.asn = ASN
+                        neighbour.description = Description
+                        bgp.as_path = AS Path
+                        routeserver.name = RS
+
+                        [source.rs1-camp-v4]
+                        name = rs1.camp.bgp.wtf (IPv4)
+                        group = Camp
+                        [source.rs1-camp-v4.birdwatcher]
+                        timezone = UTC
+                        api = http://isw01.camp.bgp.wtf:3000/
+                        type = single_table
+                        neighbors_refresh_timeout = 2
+                        servertime = 2006-01-02T15:04:05Z
+                        servertime_short = 2006-01-02 15:04:05
+                        servertime_ext = 2006-01-02 15:04:05
+                        
+                        [source.rs1-camp-v6]
+                        name = rs1.camp.bgp.wtf (IPv6)
+                        group = Camp
+                        [source.rs1-camp-v6.birdwatcher]
+                        timezone = UTC
+                        api = http://isw01.camp.bgp.wtf:3001/
+                        type = single_table
+                        neighbors_refresh_timeout = 2
+                        servertime = 2006-01-02T15:04:05Z
+                        servertime_short = 2006-01-02 15:04:05
+                        servertime_ext = 2006-01-02 15:04:05
+                        
+                        [source.rs2-camp-v4]
+                        name = rs2.camp.bgp.wtf (IPv4)
+                        group = Camp
+                        [source.rs2-camp-v4.birdwatcher]
+                        timezone = UTC
+                        api = http://isw01.camp.bgp.wtf:3002/
+                        type = single_table
+                        neighbors_refresh_timeout = 2
+                        servertime = 2006-01-02T15:04:05Z
+                        servertime_short = 2006-01-02 15:04:05
+                        servertime_ext = 2006-01-02 15:04:05
+                        
+                        [source.rs2-camp-v6]
+                        name = rs2.camp.bgp.wtf (IPv6)
+                        group = Camp
+                        [source.rs2-camp-v6.birdwatcher]
+                        timezone = UTC
+                        api = http://isw01.camp.bgp.wtf:3003/
+                        type = single_table
+                        neighbors_refresh_timeout = 2
+                        servertime = 2006-01-02T15:04:05Z
+                        servertime_short = 2006-01-02 15:04:05
+                        servertime_ext = 2006-01-02 15:04:05
+                    |||,
+                },
+            },
+        },
+
         ripeSync: kube.CronJob(ix.name("ripe-sync")) {
             metadata+: ix.metadata("ripe-sync"),
             spec+: {
@@ -287,11 +435,11 @@
             },
             spec+: {
                 tls: [
-                    { hosts: [cfg.domain], secretName: "public-tls"}
+                    { hosts: [cfg.frontend.domain], secretName: "public-tls"}
                 ],
                 rules: [
                     {
-                        host: cfg.domain,
+                        host: cfg.frontend.domain,
                         http: {
                             paths: [
                                 { path: "/", backend: ix.frontend.svc.name_port },
@@ -302,6 +450,31 @@
             },
         },
 
+        aliceIngress: kube.Ingress("alice") {
+            metadata+: ix.metadata("alice") {
+                annotations+: {
+                    "kubernetes.io/tls-acme": "true",
+                    "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
+                    "nginx.ingress.kubernetes.io/proxy-body-size": "0",
+                },
+            },
+            spec+: {
+                tls: [
+                    { hosts: [cfg.alice.domain], secretName: "alice-tls"}
+                ],
+                rules: [
+                    {
+                        host: cfg.alice.domain,
+                        http: {
+                            paths: [
+                                { path: "/", backend: ix.alice.svc.name_port },
+                            ],
+                        },
+                    },
+                ],
+            },
+        },
+
         grpcIngress: kube.Ingress("grpc") {
             metadata+: ix.metadata("grpc") {
                 annotations+: {
@@ -310,16 +483,15 @@
                     "kubernetes.io/ingress.class": "nginx",
                     "nginx.ingress.kubernetes.io/ssl-redirect": "true",
                     "nginx.ingress.kubernetes.io/backend-protocol": "GRPC",
-                    "nginx.ingress.kubernetes.io/whitelist-source-range": "185.236.240.34/32",
                 },
             },
             spec+: {
                 tls: [
-                    { hosts: [cfg.grpcDomain], secretName: "grpc-tls"}
+                    { hosts: [cfg.verifier.domain], secretName: "grpc-tls"}
                 ],
                 rules: [
                     {
-                        host: cfg.grpcDomain,
+                        host: cfg.verifier.domain,
                         http: {
                             paths: [
                                 { path: "/", backend: ix.verifier.svc.name_port },
diff --git a/bgpwtf/cccampix/verifier/processor_pgp.go b/bgpwtf/cccampix/verifier/processor_pgp.go
index 423cb7e..a0cea61 100644
--- a/bgpwtf/cccampix/verifier/processor_pgp.go
+++ b/bgpwtf/cccampix/verifier/processor_pgp.go
@@ -88,6 +88,7 @@
 			case ok && s.Code() == codes.NotFound:
 				unknownC <- k
 			default:
+				unknownC <- k
 				errC <- err
 			}
 		}(key)
diff --git a/bgpwtf/cccampix/verifier/service.go b/bgpwtf/cccampix/verifier/service.go
index e00b85f..5712471 100644
--- a/bgpwtf/cccampix/verifier/service.go
+++ b/bgpwtf/cccampix/verifier/service.go
@@ -288,7 +288,7 @@
 	for _, router := range pcr.Peer.Routers {
 		if router.V4 != nil {
 			plain += fmt.Sprintf(`
-our addresses: 185.236.243.5 (rs1), 185.236.243.6 (rs2)
+our addresses: 185.236.243.5 (rs1), 185.236.243.6 (rs2), 185.236.243.7 (upstream)
       our asn: 208521
  your address: %s
      your asn: %d
@@ -297,7 +297,7 @@
 		}
 		if router.V6 != nil {
 			plain += fmt.Sprintf(`
-our addresses: 2a0d:eb02:4242:4242::5 (rs1), 2a0d:eb02:4242:4242::6 (rs2)
+our addresses: 2a0d:eb02:4242:4242::5 (rs1), 2a0d:eb02:4242:4242::6 (rs2), 2a0d:eb02:4242:4242::7 (upstream)
       our asn: 208521
  your address: %s
      your asn: %d