@takodotid/circuit 0.31.1 → 0.32.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/cli/index.js CHANGED
@@ -1318,7 +1318,7 @@ function ruleGaps(device, where, rule) {
1318
1318
  const set = rule.set ?? {};
1319
1319
  if ((rule.match || rule.set) && !rule.action)
1320
1320
  found.push(`${where}, a rule without action: the platform stops at the first rule that matches`);
1321
- for (const field of ["as_path_length", "rpki", "large_community"]) {
1321
+ for (const field of ["as_path_length", "through_as", "rpki", "large_community"]) {
1322
1322
  if (match[field] !== undefined)
1323
1323
  found.push(`${where}.match.${field}`);
1324
1324
  }
@@ -4351,6 +4351,11 @@ function term(out, device, term, rule, family, splitFamily) {
4351
4351
  out.set(`policy-options as-path ${name}`, quote2(`.{${min},${max ?? ""}}`));
4352
4352
  from.push(["as-path", name]);
4353
4353
  }
4354
+ if (match.through_as !== undefined) {
4355
+ const name = `circuit-through-${match.through_as}`;
4356
+ out.set(`policy-options as-path ${name}`, quote2(`.* ${match.through_as} .*`));
4357
+ from.push(["as-path", name]);
4358
+ }
4354
4359
  if (match.rpki)
4355
4360
  from.push(["validation-database", VALIDATION[match.rpki]]);
4356
4361
  const wanted = [
@@ -7086,6 +7091,8 @@ function conditions(rule) {
7086
7091
  tests.push(`bgp-path-len>${match.as_path_length.min - 1}`);
7087
7092
  if (match.as_path_length?.max !== undefined)
7088
7093
  tests.push(`bgp-path-len<${match.as_path_length.max + 1}`);
7094
+ if (match.through_as !== undefined)
7095
+ tests.push(`bgp-as-path ${match.through_as}`);
7089
7096
  if (match.rpki)
7090
7097
  tests.push(`rpki ${match.rpki}`);
7091
7098
  if (match.community)
@@ -33,6 +33,8 @@ export type Match<S extends string> = {
33
33
  min?: number;
34
34
  max?: number;
35
35
  };
36
+ /** The AS path holds this AS anywhere: the route came from it or through it. */
37
+ through_as?: number;
36
38
  /** The route's origin validation state. */
37
39
  rpki?: "valid" | "invalid" | "not-found";
38
40
  /** Has this standard community, `asn:value`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takodotid/circuit",
3
- "version": "0.31.1",
3
+ "version": "0.32.0",
4
4
  "description": "Declarative, vendor-neutral network configuration: describe each device in TypeScript, and Circuit makes the device match. Alpha.",
5
5
  "keywords": [
6
6
  "network",
@@ -418,7 +418,7 @@ function ruleGaps(device: Device, where: string, rule: AnyRule): string[] {
418
418
  if ((rule.match || rule.set) && !rule.action)
419
419
  found.push(`${where}, a rule without action: the platform stops at the first rule that matches`);
420
420
 
421
- for (const field of ["as_path_length", "rpki", "large_community"] as const) {
421
+ for (const field of ["as_path_length", "through_as", "rpki", "large_community"] as const) {
422
422
  if (match[field] !== undefined) found.push(`${where}.match.${field}`);
423
423
  }
424
424
 
@@ -210,6 +210,12 @@ function term(out: Statements, device: Device, term: string, rule: AnyRule, fami
210
210
  from.push(["as-path", name]);
211
211
  }
212
212
 
213
+ if (match.through_as !== undefined) {
214
+ const name = `circuit-through-${match.through_as}`;
215
+ out.set(`policy-options as-path ${name}`, quote(`.* ${match.through_as} .*`));
216
+ from.push(["as-path", name]);
217
+ }
218
+
213
219
  if (match.rpki) from.push(["validation-database", VALIDATION[match.rpki]]);
214
220
 
215
221
  // Members of one named community must all be present, so a rule matching both kinds names them together.
@@ -1033,6 +1033,8 @@ function conditions(rule: AnyRule): string[] {
1033
1033
  if (match.prefix_length?.max !== undefined) tests.push(`dst-len<${match.prefix_length.max + 1}`);
1034
1034
  if (match.as_path_length?.min !== undefined) tests.push(`bgp-path-len>${match.as_path_length.min - 1}`);
1035
1035
  if (match.as_path_length?.max !== undefined) tests.push(`bgp-path-len<${match.as_path_length.max + 1}`);
1036
+ // The platform's AS path regexp matches each AS as a whole number, so a bare number is that AS anywhere in the path.
1037
+ if (match.through_as !== undefined) tests.push(`bgp-as-path ${match.through_as}`);
1036
1038
  if (match.rpki) tests.push(`rpki ${match.rpki}`);
1037
1039
  if (match.community) tests.push(`bgp-communities includes ${match.community}`);
1038
1040
  if (match.large_community) tests.push(`bgp-large-communities includes ${match.large_community}`);
@@ -32,6 +32,8 @@ export type Match<S extends string> = {
32
32
  prefix_length?: { min?: number; max?: number };
33
33
  /** AS path length within this range, inclusive. Every AS counts, prepends included. */
34
34
  as_path_length?: { min?: number; max?: number };
35
+ /** The AS path holds this AS anywhere: the route came from it or through it. */
36
+ through_as?: number;
35
37
  /** The route's origin validation state. */
36
38
  rpki?: "valid" | "invalid" | "not-found";
37
39
  /** Has this standard community, `asn:value`. */