@takodotid/circuit 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/presets/index.js
CHANGED
|
@@ -162,14 +162,14 @@ function communityScheme(scheme) {
|
|
|
162
162
|
const information = [scheme.learned_from?.function, scheme.learned_at?.function, scheme.learned_from_as].filter((fn) => fn !== undefined);
|
|
163
163
|
const tag = (neighborClass, neighborAs, options = {}) => {
|
|
164
164
|
const { site } = options;
|
|
165
|
-
const
|
|
165
|
+
const isCustomer = scheme.customers?.includes(neighborClass) ?? false;
|
|
166
166
|
if (scheme.learned_at && site === undefined)
|
|
167
167
|
throw new Error(`communityScheme: tag for AS${neighborAs} needs a site, for learned_at`);
|
|
168
168
|
return {
|
|
169
169
|
description: `Learned from AS${neighborAs}`,
|
|
170
170
|
set: {
|
|
171
171
|
remove_communities: ours.standard,
|
|
172
|
-
remove_large_communities:
|
|
172
|
+
remove_large_communities: isCustomer ? information.map((fn) => community(fn, "*")) : ours.large,
|
|
173
173
|
add_large_communities: [
|
|
174
174
|
...scheme.learned_from ? [community(scheme.learned_from.function, scheme.learned_from.classes[neighborClass])] : [],
|
|
175
175
|
...scheme.learned_at ? [community(scheme.learned_at.function, site)] : [],
|
|
@@ -64,8 +64,8 @@ export type CommunityScheme<Class extends string = string> = {
|
|
|
64
64
|
function: number;
|
|
65
65
|
classes: Readonly<Record<Class, number>>;
|
|
66
66
|
};
|
|
67
|
-
/**
|
|
68
|
-
|
|
67
|
+
/** Which of the classes above are your customers. Only a customer may ask you for something, such as not announcing its route somewhere; what anyone else asks is removed when the route comes in. */
|
|
68
|
+
customers?: readonly NoInfer<Class>[];
|
|
69
69
|
/** Set on import: the site a route was learned at, `asn:function:site`. `sites` names the sites, for the catalogue. */
|
|
70
70
|
learned_at?: {
|
|
71
71
|
function: number;
|
|
@@ -87,7 +87,7 @@ export type CommunityScheme<Class extends string = string> = {
|
|
|
87
87
|
/**
|
|
88
88
|
* Policy rules and a catalogue from one community scheme, so the communities your routers set, the ones they act on, and the ones you publish never disagree.
|
|
89
89
|
*
|
|
90
|
-
* - `tag(neighborClass, neighborAs, { site })`: a rule for an import policy. It removes your communities a neighbor set, then adds where the route was learned. A
|
|
90
|
+
* - `tag(neighborClass, neighborAs, { site })`: a rule for an import policy. It removes your communities a neighbor set, then adds where the route was learned. A customer, a class in `customers`, keeps its requests. `site` is needed when the scheme has `learned_at`.
|
|
91
91
|
* - `actions(neighborAs, { blackhole })`: rules for an export policy toward one neighbor. They act on a customer's action communities, then remove all of yours. Place them before the rules that accept. A blackholed route goes no further, unless `blackhole` names the neighbor's own blackhole community: then it is passed on with it, so the neighbor drops the traffic too.
|
|
92
92
|
* - `blackhole(prefixSet)`: a rule for a customer's import policy, before anything that rejects long prefixes. It takes a route the customer asked to blackhole, but only inside `prefixSet`, its own space, and drops traffic to it here.
|
|
93
93
|
* - `catalogue`: every community, for `communities` in `defineNetwork`.
|
package/package.json
CHANGED
package/src/presets/index.ts
CHANGED
|
@@ -226,8 +226,8 @@ export type CommunityScheme<Class extends string = string> = {
|
|
|
226
226
|
asn: number;
|
|
227
227
|
/** Set on import: the kind of neighbor a route came from, `asn:function:class`. */
|
|
228
228
|
learned_from?: { function: number; classes: Readonly<Record<Class, number>> };
|
|
229
|
-
/**
|
|
230
|
-
|
|
229
|
+
/** Which of the classes above are your customers. Only a customer may ask you for something, such as not announcing its route somewhere; what anyone else asks is removed when the route comes in. */
|
|
230
|
+
customers?: readonly NoInfer<Class>[];
|
|
231
231
|
/** Set on import: the site a route was learned at, `asn:function:site`. `sites` names the sites, for the catalogue. */
|
|
232
232
|
learned_at?: { function: number; sites?: Readonly<Record<number, string>> };
|
|
233
233
|
/** Set on import: the AS a route was learned from, `asn:function:asn`. */
|
|
@@ -251,7 +251,7 @@ const article = (word: string) => (/^[aeiou]/i.test(word) ? `an ${word}` : `a ${
|
|
|
251
251
|
/**
|
|
252
252
|
* Policy rules and a catalogue from one community scheme, so the communities your routers set, the ones they act on, and the ones you publish never disagree.
|
|
253
253
|
*
|
|
254
|
-
* - `tag(neighborClass, neighborAs, { site })`: a rule for an import policy. It removes your communities a neighbor set, then adds where the route was learned. A
|
|
254
|
+
* - `tag(neighborClass, neighborAs, { site })`: a rule for an import policy. It removes your communities a neighbor set, then adds where the route was learned. A customer, a class in `customers`, keeps its requests. `site` is needed when the scheme has `learned_at`.
|
|
255
255
|
* - `actions(neighborAs, { blackhole })`: rules for an export policy toward one neighbor. They act on a customer's action communities, then remove all of yours. Place them before the rules that accept. A blackholed route goes no further, unless `blackhole` names the neighbor's own blackhole community: then it is passed on with it, so the neighbor drops the traffic too.
|
|
256
256
|
* - `blackhole(prefixSet)`: a rule for a customer's import policy, before anything that rejects long prefixes. It takes a route the customer asked to blackhole, but only inside `prefixSet`, its own space, and drops traffic to it here.
|
|
257
257
|
* - `catalogue`: every community, for `communities` in `defineNetwork`.
|
|
@@ -267,7 +267,7 @@ export function communityScheme<const Class extends string>(scheme: CommunitySch
|
|
|
267
267
|
|
|
268
268
|
const tag = (neighborClass: Class, neighborAs: number, options: { site?: number } = {}): PolicyRule<never, never> => {
|
|
269
269
|
const { site } = options;
|
|
270
|
-
const
|
|
270
|
+
const isCustomer = scheme.customers?.includes(neighborClass) ?? false;
|
|
271
271
|
if (scheme.learned_at && site === undefined)
|
|
272
272
|
throw new Error(`communityScheme: tag for AS${neighborAs} needs a site, for learned_at`);
|
|
273
273
|
|
|
@@ -275,7 +275,7 @@ export function communityScheme<const Class extends string>(scheme: CommunitySch
|
|
|
275
275
|
description: `Learned from AS${neighborAs}`,
|
|
276
276
|
set: {
|
|
277
277
|
remove_communities: ours.standard,
|
|
278
|
-
remove_large_communities:
|
|
278
|
+
remove_large_communities: isCustomer ? information.map((fn) => community(fn, "*")) : ours.large,
|
|
279
279
|
add_large_communities: [
|
|
280
280
|
...(scheme.learned_from ? [community(scheme.learned_from.function, scheme.learned_from.classes[neighborClass])] : []),
|
|
281
281
|
...(scheme.learned_at ? [community(scheme.learned_at.function, site!)] : []),
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// The edge router: BGP with an IP transit and an internet exchange, the gateway of every tenant, and the filter between them.
|
|
2
|
+
//
|
|
3
|
+
// Hurricane Electric and SGIX stand for your own transit and exchange. Addresses are from the documentation ranges; use the ones your providers give you.
|
|
2
4
|
|
|
3
5
|
import type { Prefix } from "@takodotid/circuit";
|
|
4
6
|
import { defineDevice, merge, secret } from "@takodotid/circuit";
|
|
@@ -29,16 +31,16 @@ export default defineDevice({
|
|
|
29
31
|
vlans: merge(
|
|
30
32
|
{
|
|
31
33
|
mgmt: { id: 99, description: "Management of the switches" },
|
|
32
|
-
transit: { id: 300, description: "IP transit from
|
|
33
|
-
ix: { id: 200, description: "Peering LAN of
|
|
34
|
+
transit: { id: 300, description: "IP transit from Hurricane Electric, AS6939" },
|
|
35
|
+
ix: { id: 200, description: "Peering LAN of SGIX" },
|
|
34
36
|
},
|
|
35
37
|
internetVlans(TENANTS)
|
|
36
38
|
),
|
|
37
39
|
|
|
38
40
|
ports: {
|
|
39
41
|
"1g-1": { description: "Management", addresses: ["10.0.0.1/24"] },
|
|
40
|
-
"25g-1": { description: "
|
|
41
|
-
"25g-2": { description: "
|
|
42
|
+
"25g-1": { description: "Hurricane Electric", access_vlan: "transit", acl: "edge" },
|
|
43
|
+
"25g-2": { description: "SGIX", access_vlan: "ix", acl: "edge" },
|
|
42
44
|
"100g-1": {
|
|
43
45
|
description: "tor-01",
|
|
44
46
|
speed: "40g",
|
|
@@ -64,25 +66,25 @@ export default defineDevice({
|
|
|
64
66
|
asn: ASN,
|
|
65
67
|
networks: [...OURS],
|
|
66
68
|
neighbors: {
|
|
67
|
-
"
|
|
69
|
+
"hurricane-electric": {
|
|
68
70
|
address: "203.0.113.1",
|
|
69
|
-
remote_as:
|
|
71
|
+
remote_as: 6939,
|
|
70
72
|
role: "provider",
|
|
71
73
|
import: "TRANSIT-IN",
|
|
72
74
|
export: "OUT",
|
|
73
75
|
max_prefixes: 1_200_000,
|
|
74
76
|
},
|
|
75
|
-
"
|
|
77
|
+
"sgix-rs-v4": {
|
|
76
78
|
address: "192.0.2.1",
|
|
77
|
-
remote_as:
|
|
79
|
+
remote_as: 55518,
|
|
78
80
|
role: "rs",
|
|
79
81
|
import: "IX-IN",
|
|
80
82
|
export: "OUT",
|
|
81
83
|
max_prefixes: 100_000,
|
|
82
84
|
},
|
|
83
|
-
"
|
|
85
|
+
"sgix-rs-v6": {
|
|
84
86
|
address: "2001:db8:ffff::1",
|
|
85
|
-
remote_as:
|
|
87
|
+
remote_as: 55518,
|
|
86
88
|
role: "rs",
|
|
87
89
|
import: "IX-IN",
|
|
88
90
|
export: "OUT",
|
|
@@ -95,8 +97,8 @@ export default defineDevice({
|
|
|
95
97
|
policies: {
|
|
96
98
|
SANITY: bgpSanity(),
|
|
97
99
|
// A route from the exchange is preferred over the same route from the transit: higher local preference wins.
|
|
98
|
-
"TRANSIT-IN": [{ call: "SANITY" }, communities.tag("transit",
|
|
99
|
-
"IX-IN": [{ call: "SANITY" }, communities.tag("exchange",
|
|
100
|
+
"TRANSIT-IN": [{ call: "SANITY" }, communities.tag("transit", 6939), { set: { local_pref: 100 } }, { action: "accept" }],
|
|
101
|
+
"IX-IN": [{ call: "SANITY" }, communities.tag("exchange", 55518), { set: { local_pref: 200 } }, { action: "accept" }],
|
|
100
102
|
OUT: [
|
|
101
103
|
...OURS.map((prefix) => ({ description: `Announce ${prefix}`, match: { prefix }, action: "accept" as const })),
|
|
102
104
|
{ description: "Nothing else leaves", action: "reject" },
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// The edge router: BGP with an IP transit and an internet exchange, filtered on the way in and on the way out.
|
|
2
|
+
//
|
|
3
|
+
// Hurricane Electric and SGIX stand for your own transit and exchange. Addresses are from the documentation ranges; use the ones your providers give you.
|
|
2
4
|
|
|
3
5
|
import type { Prefix } from "@takodotid/circuit";
|
|
4
6
|
import { defineDevice, secret } from "@takodotid/circuit";
|
|
@@ -25,14 +27,14 @@ export default defineDevice({
|
|
|
25
27
|
management: { allow: [MANAGEMENT], ssh: {} },
|
|
26
28
|
|
|
27
29
|
vlans: {
|
|
28
|
-
transit: { id: 300, description: "IP transit from
|
|
29
|
-
ix: { id: 200, description: "Peering LAN of
|
|
30
|
+
transit: { id: 300, description: "IP transit from Hurricane Electric, AS6939" },
|
|
31
|
+
ix: { id: 200, description: "Peering LAN of SGIX" },
|
|
30
32
|
},
|
|
31
33
|
|
|
32
34
|
ports: {
|
|
33
35
|
"1g-1": { description: "Management", addresses: ["10.0.0.1/24"] },
|
|
34
|
-
"25g-1": { description: "
|
|
35
|
-
"25g-2": { description: "
|
|
36
|
+
"25g-1": { description: "Hurricane Electric", access_vlan: "transit", acl: "edge" },
|
|
37
|
+
"25g-2": { description: "SGIX", access_vlan: "ix", acl: "edge" },
|
|
36
38
|
},
|
|
37
39
|
|
|
38
40
|
interfaces: {
|
|
@@ -48,25 +50,25 @@ export default defineDevice({
|
|
|
48
50
|
asn: ASN,
|
|
49
51
|
networks: [...OURS],
|
|
50
52
|
neighbors: {
|
|
51
|
-
"
|
|
53
|
+
"hurricane-electric": {
|
|
52
54
|
address: "203.0.113.1",
|
|
53
|
-
remote_as:
|
|
55
|
+
remote_as: 6939,
|
|
54
56
|
role: "provider",
|
|
55
57
|
import: "TRANSIT-IN",
|
|
56
58
|
export: "OUT",
|
|
57
59
|
max_prefixes: 1_200_000,
|
|
58
60
|
},
|
|
59
|
-
"
|
|
61
|
+
"sgix-rs-v4": {
|
|
60
62
|
address: "192.0.2.1",
|
|
61
|
-
remote_as:
|
|
63
|
+
remote_as: 55518,
|
|
62
64
|
role: "rs",
|
|
63
65
|
import: "IX-IN",
|
|
64
66
|
export: "OUT",
|
|
65
67
|
max_prefixes: 100_000,
|
|
66
68
|
},
|
|
67
|
-
"
|
|
69
|
+
"sgix-rs-v6": {
|
|
68
70
|
address: "2001:db8:ffff::1",
|
|
69
|
-
remote_as:
|
|
71
|
+
remote_as: 55518,
|
|
70
72
|
role: "rs",
|
|
71
73
|
import: "IX-IN",
|
|
72
74
|
export: "OUT",
|
|
@@ -79,8 +81,8 @@ export default defineDevice({
|
|
|
79
81
|
policies: {
|
|
80
82
|
SANITY: bgpSanity(),
|
|
81
83
|
// A route from the exchange is preferred over the same route from the transit: higher local preference wins.
|
|
82
|
-
"TRANSIT-IN": [{ call: "SANITY" }, communities.tag("transit",
|
|
83
|
-
"IX-IN": [{ call: "SANITY" }, communities.tag("exchange",
|
|
84
|
+
"TRANSIT-IN": [{ call: "SANITY" }, communities.tag("transit", 6939), { set: { local_pref: 100 } }, { action: "accept" }],
|
|
85
|
+
"IX-IN": [{ call: "SANITY" }, communities.tag("exchange", 55518), { set: { local_pref: 200 } }, { action: "accept" }],
|
|
84
86
|
OUT: [
|
|
85
87
|
...OURS.map((prefix) => ({ description: `Announce ${prefix}`, match: { prefix }, action: "accept" as const })),
|
|
86
88
|
{ description: "Nothing else leaves", action: "reject" },
|