@takodotid/circuit 0.40.0 → 0.42.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/chunks/index-6wb41q67.js +62 -0
- package/dist/chunks/{index-k38bby43.js → index-yzw86rc8.js} +4 -62
- package/dist/chunks/{server-ggertnce.js → server-cxr9ynxc.js} +1 -1
- package/dist/cli/index.js +127 -49
- package/dist/helpers/index.js +143 -156
- package/dist/types/core/cabling.d.ts +0 -6
- package/dist/types/core/define.d.ts +1 -1
- package/dist/types/core/validator.d.ts +2 -0
- package/dist/types/helpers/bgp.d.ts +53 -9
- package/dist/types/helpers/cables.d.ts +14 -11
- package/dist/types/helpers/checks.d.ts +4 -0
- package/dist/types/helpers/exchange.d.ts +7 -1
- package/dist/types/helpers/index.d.ts +2 -2
- package/dist/types/helpers/upstreams.d.ts +123 -181
- package/dist/types/platforms/derive.d.ts +1 -1
- package/dist/types/platforms/fortios/routing.d.ts +2 -2
- package/dist/types/platforms/junos/bgp.d.ts +2 -2
- package/dist/types/platforms/routeros/render.d.ts +4 -0
- package/dist/types/schema/device.d.ts +4 -4
- package/dist/types/schema/firewall.d.ts +5 -1
- package/dist/types/schema/interfaces.d.ts +1 -1
- package/dist/types/schema/policy.d.ts +15 -6
- package/dist/types/schema/routing.d.ts +43 -10
- package/package.json +1 -1
- package/src/cli/index.ts +26 -29
- package/src/core/cabling.ts +0 -30
- package/src/core/define.ts +3 -1
- package/src/core/validator.ts +70 -2
- package/src/helpers/bgp.ts +117 -67
- package/src/helpers/cables.ts +20 -15
- package/src/helpers/checks.ts +23 -0
- package/src/helpers/exchange.ts +9 -3
- package/src/helpers/index.ts +2 -2
- package/src/helpers/upstreams.ts +162 -274
- package/src/platforms/derive.ts +23 -1
- package/src/platforms/fortios/routing.ts +2 -2
- package/src/platforms/junos/bgp.ts +2 -2
- package/src/platforms/routeros/TRAPS.md +4 -0
- package/src/platforms/routeros/export.ts +1 -0
- package/src/platforms/routeros/index.ts +11 -2
- package/src/platforms/routeros/plan.ts +3 -0
- package/src/platforms/routeros/render.ts +28 -3
- package/src/schema/device.ts +4 -3
- package/src/schema/firewall.ts +5 -1
- package/src/schema/interfaces.ts +1 -1
- package/src/schema/policy.ts +17 -6
- package/src/schema/routing.ts +46 -10
- package/templates/colocation/checks.ts +14 -11
- package/templates/colocation/edge-01.ts +2 -2
- package/templates/colocation/tenancy.ts +50 -71
- package/templates/colocation/tenants/acme.ts +10 -10
- package/templates/colocation/tenants/index.ts +2 -1
- package/templates/colocation/tor-01.ts +2 -2
- package/templates/project/AGENTS.md +15 -15
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
/** The far end of a port, for a description of your own: the device
|
|
1
|
+
/** The far end of a port, for a description of your own: the device, its port by Circuit's name, and the port as printed on the device when that end names its platform and model. */
|
|
2
2
|
export type FarEnd = {
|
|
3
3
|
device: string;
|
|
4
4
|
port: string;
|
|
5
|
+
label?: string;
|
|
6
|
+
};
|
|
7
|
+
/** One end of a cable entry: the device, and its platform and model if a description should name its ports as printed on it. */
|
|
8
|
+
type End = {
|
|
9
|
+
device: string;
|
|
10
|
+
platform?: string;
|
|
11
|
+
model?: string;
|
|
5
12
|
};
|
|
6
13
|
/** What every cable of one entry shares. */
|
|
7
14
|
type Shared = {
|
|
8
15
|
/** What is between the two ends, such as a carrier's local loop. Both ends get it on their `link`. */
|
|
9
16
|
through?: string;
|
|
10
|
-
/** A port's description from its far end
|
|
17
|
+
/** A port's description from its far end, such as `(far) => \`${far.device} ${far.label}\``. No description when absent. */
|
|
11
18
|
describe?: (far: FarEnd) => string;
|
|
12
19
|
};
|
|
13
20
|
/** A port a cable builds: the other end, the LAG it is in, and a description when `describe` gives one. */
|
|
@@ -31,12 +38,10 @@ export declare class Cables {
|
|
|
31
38
|
* ```
|
|
32
39
|
*/
|
|
33
40
|
static link<const NA extends string, const NB extends string>(spec: Shared & {
|
|
34
|
-
a: {
|
|
35
|
-
device: string;
|
|
41
|
+
a: End & {
|
|
36
42
|
port: NA;
|
|
37
43
|
};
|
|
38
|
-
b: {
|
|
39
|
-
device: string;
|
|
44
|
+
b: End & {
|
|
40
45
|
port: NB;
|
|
41
46
|
};
|
|
42
47
|
}): {
|
|
@@ -47,12 +52,10 @@ export declare class Cables {
|
|
|
47
52
|
* The cables of a LAG, both ends, from one list read from end A: `{ "25g-1": "10g-2" }` is a cable from A's 25g-1 to B's 10g-2. Each port is a member of its end's LAG. Spread each end into its device's `ports`; the LAG itself stays on each device.
|
|
48
53
|
*/
|
|
49
54
|
static lag<const LA extends string, const LB extends string, const C extends Readonly<Record<string, string>>>(spec: Shared & {
|
|
50
|
-
a: {
|
|
51
|
-
device: string;
|
|
55
|
+
a: End & {
|
|
52
56
|
lag: LA;
|
|
53
57
|
};
|
|
54
|
-
b: {
|
|
55
|
-
device: string;
|
|
58
|
+
b: End & {
|
|
56
59
|
lag: LB;
|
|
57
60
|
};
|
|
58
61
|
cables: C;
|
|
@@ -60,7 +63,7 @@ export declare class Cables {
|
|
|
60
63
|
a: Record<keyof C & string, CablePort<LA>>;
|
|
61
64
|
b: Record<C[keyof C], CablePort<LB>>;
|
|
62
65
|
};
|
|
63
|
-
/** A port facing `port` on the far
|
|
66
|
+
/** A port facing `port` on the far end. */
|
|
64
67
|
private static end;
|
|
65
68
|
}
|
|
66
69
|
export {};
|
|
@@ -16,6 +16,10 @@ export declare class Checks {
|
|
|
16
16
|
* Every BGP export ends in a reject that matches everything, so nothing leaves by accident. An export to a neighbor whose `role` is `customer` is left out: sending a customer everything you know is what it pays for, and cannot leak a route to the rest of the internet.
|
|
17
17
|
*/
|
|
18
18
|
static readonly exportsEndInReject: Check;
|
|
19
|
+
/**
|
|
20
|
+
* Every BGP import ends in a rule that decides for every route, an accept or a reject without a match, so what the import takes is written in it and not left to the platform's default.
|
|
21
|
+
*/
|
|
22
|
+
static readonly importsEndInAcceptOrReject: Check;
|
|
19
23
|
/** A GRE or VXLAN tunnel's local address is outside what the neighbors over it are offered, or their replies route back into the tunnel. */
|
|
20
24
|
static readonly tunnelsOutsideOffered: Check;
|
|
21
25
|
/**
|
|
@@ -29,13 +29,19 @@ export type InternetExchange<V extends string> = {
|
|
|
29
29
|
mac_limit?: number;
|
|
30
30
|
/** The only kinds of frame a member port takes in, usually `ipv4`, `ipv6` and `arp`. Every kind when absent. No platform renders it yet, so `validate` refuses it. */
|
|
31
31
|
ethertypes?: readonly Ethertype[];
|
|
32
|
+
/** LLDP on member ports. Off when absent: a member's router has no business learning the fabric. */
|
|
33
|
+
lldp?: boolean;
|
|
34
|
+
/** Spanning tree on member ports. Off when absent: a member's port faces a router, which must not join the fabric's spanning tree. */
|
|
35
|
+
stp?: boolean;
|
|
32
36
|
};
|
|
33
37
|
/** An internet exchange's fabric: its member ports, a check of its members, and its route servers' config. */
|
|
34
38
|
export declare class Exchange {
|
|
35
39
|
/**
|
|
36
40
|
* Member ports and a check for an internet exchange's fabric.
|
|
37
41
|
*
|
|
38
|
-
* - `ports(device)`: every member's port on one switch
|
|
42
|
+
* - `ports(device)`: every member's port on one switch, each written as
|
|
43
|
+
* `{ description: "<name>, AS<asn>", access_vlan: vlan, stp: false, lldp: false, storm_control: { broadcast, multicast, unknown_unicast: { percent: storm_percent } } }`,
|
|
44
|
+
* plus `mac_limit` and `ethertypes` when the exchange sets them. `stp` and `lldp` are the exchange's own when it sets them, and storm control is 1 % when `storm_percent` is absent.
|
|
39
45
|
* - `check`: members never share a name, an AS, an address or a port; every address is inside the peering LAN and not reserved; every member port carries the peering VLAN alone.
|
|
40
46
|
* - `bird(server)`: one route server's BIRD 2 config, with a session to every member. Write it to a file for the server; Circuit never logs in to a route server.
|
|
41
47
|
* - `openbgpd(server)`: the same route server's config for OpenBGPD, its `bgpd.conf`, with the same sessions and the same rules.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { Bgp } from "./bgp.js";
|
|
2
|
-
export type { Announced, CommunityScheme, DefaultOnlyOptions, LearnOptions, SanityLengths } from "./bgp.js";
|
|
2
|
+
export type { AnnounceOptions, Announced, CommunityScheme, DefaultOnlyOptions, LearnOptions, SanityLengths } from "./bgp.js";
|
|
3
3
|
export { Cables } from "./cables.js";
|
|
4
4
|
export type { FarEnd } from "./cables.js";
|
|
5
5
|
export { Checks } from "./checks.js";
|
|
@@ -9,6 +9,6 @@ export type { ExchangeMember, InternetExchange } from "./exchange.js";
|
|
|
9
9
|
export { Filters } from "./filters.js";
|
|
10
10
|
export { RouteServerConfig } from "./route-server.js";
|
|
11
11
|
export { Upstreams } from "./upstreams.js";
|
|
12
|
-
export type { Upstream, UpstreamCatalog,
|
|
12
|
+
export type { NamedVlan, Upstream, UpstreamCatalog, UpstreamOptions, UpstreamPolicy } from "./upstreams.js";
|
|
13
13
|
export type { RouteServer } from "./route-server.js";
|
|
14
14
|
export type { AntiAmplificationOptions } from "./filters.js";
|
|
@@ -1,237 +1,179 @@
|
|
|
1
|
-
import type { Exchange as ExchangeLan, Family, FilterRule, Gre, InterfaceAddress, IP,
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { Exchange as ExchangeLan, Family, FilterRule, Gre, InterfaceAddress, IP, Neighbor, NeighborSettings, PolicyRule, Port, Secret, Vlan, VlanInterface } from "../schema/index.js";
|
|
2
|
+
/** A route policy an upstream's sessions use: its name on the device, and its rules, in order. */
|
|
3
|
+
export type UpstreamPolicy = {
|
|
4
|
+
/** The policy's name, as the device carries it. */
|
|
5
|
+
name: string;
|
|
6
|
+
/** Its rules, in order. Nothing is added to them unless `mapImports` or `mapExports` adds it. */
|
|
7
|
+
rules: readonly PolicyRule<string, string>[];
|
|
8
|
+
};
|
|
9
|
+
/** A VLAN by its name and its ID. */
|
|
10
|
+
export type NamedVlan = {
|
|
11
|
+
name: string;
|
|
12
|
+
id: number;
|
|
13
|
+
};
|
|
14
|
+
/** One network we exchange routes with: a transit we buy, an exchange's route servers, or a peer. Every name it puts on a device is written here. */
|
|
4
15
|
export type Upstream = {
|
|
5
16
|
/** Its name as people write it, for descriptions: `Transit A`. The key when absent. */
|
|
6
17
|
name?: string;
|
|
7
18
|
asn: number;
|
|
8
19
|
/** What it is to us: `provider` for a transit, `rs` for an exchange's route servers, `peer` for a direct session. */
|
|
9
20
|
role: "provider" | "rs" | "peer";
|
|
10
|
-
/** Where it lands on our side: a VLAN,
|
|
21
|
+
/** Where it lands on our side: a VLAN, the router's interface on it, and the switch port it arrives on. A peer over a tunnel, or over an exchange's VLAN, has none. */
|
|
11
22
|
handoff?: {
|
|
12
|
-
/** The VLAN
|
|
13
|
-
vlan:
|
|
14
|
-
/** It sends its VLAN tagged: its port gets
|
|
15
|
-
native?:
|
|
16
|
-
/**
|
|
17
|
-
|
|
23
|
+
/** The VLAN it arrives on. */
|
|
24
|
+
vlan: NamedVlan;
|
|
25
|
+
/** It sends its VLAN tagged: its switch port gets this native VLAN, which nothing else is in. Untagged when absent. */
|
|
26
|
+
native?: NamedVlan;
|
|
27
|
+
/** The router's VLAN interface on it. */
|
|
28
|
+
interface: string;
|
|
18
29
|
/** Our addresses on it. `{ address, peer }` for a point-to-point address whose far end is outside its own prefix. */
|
|
19
30
|
addresses: readonly (InterfaceAddress | {
|
|
20
31
|
address: InterfaceAddress;
|
|
21
32
|
peer: IP;
|
|
22
33
|
})[];
|
|
23
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* The switch port it arrives on. LLDP and spanning tree are off there unless `lldp` or `stp` says otherwise: an upstream's port faces someone else's network, which should neither learn ours nor join our spanning tree.
|
|
36
|
+
*/
|
|
24
37
|
port?: {
|
|
25
38
|
device: string;
|
|
26
39
|
port: string;
|
|
27
40
|
storm_control?: Port<string, string, string, string>["storm_control"];
|
|
41
|
+
/** LLDP on the port. Off when absent. */
|
|
42
|
+
lldp?: boolean;
|
|
43
|
+
/** Spanning tree on the port. Off when absent. */
|
|
44
|
+
stp?: boolean;
|
|
28
45
|
};
|
|
29
46
|
/** An exchange's LAN, for `circuit peeringdb`. */
|
|
30
47
|
exchange?: ExchangeLan;
|
|
31
48
|
};
|
|
32
|
-
/** A GRE tunnel to it
|
|
49
|
+
/** A GRE tunnel to it, and the router's interface for it. */
|
|
33
50
|
tunnel?: {
|
|
51
|
+
interface: string;
|
|
34
52
|
local: IP;
|
|
35
53
|
remote: IP;
|
|
36
54
|
addresses: readonly InterfaceAddress[];
|
|
37
55
|
mtu?: number;
|
|
38
|
-
number?: number;
|
|
39
56
|
};
|
|
40
57
|
/** Our BGP sessions with it, by name: its address, and ours when it is not the interface's. */
|
|
41
58
|
sessions: Readonly<Record<string, {
|
|
42
59
|
address: IP;
|
|
43
60
|
local_address?: IP;
|
|
44
61
|
}>>;
|
|
45
|
-
/**
|
|
62
|
+
/** A BGP group its sessions share, by this name: the group holds the AS, role, policies, table and multihop settings, and each session names it. Each session holds them itself when absent. */
|
|
63
|
+
group?: string;
|
|
64
|
+
/** The sessions are more than one hop away, from this address of ours. */
|
|
46
65
|
multihop?: {
|
|
47
66
|
local_address: IP;
|
|
48
67
|
families?: readonly Family[];
|
|
49
68
|
password?: Secret;
|
|
50
69
|
};
|
|
70
|
+
/** The routing table its sessions install the routes they learn in, one of the router's `routing.tables`: a transit that carries only one block's traffic out. The main table when absent. */
|
|
71
|
+
table?: string;
|
|
51
72
|
/** The most prefixes a session may send, by its family: about twice what it sends. Over it, the session closes until it is cleared. */
|
|
52
73
|
limits?: {
|
|
53
74
|
ipv4?: number;
|
|
54
75
|
ipv6?: number;
|
|
55
76
|
};
|
|
56
|
-
/** What we take. Nothing when absent. */
|
|
57
|
-
import?:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
description?: string;
|
|
62
|
-
};
|
|
63
|
-
/** Its routes, through `Bgp.learn`: the `before` rules, sanity, this preference, the tag. Only the default when absent. */
|
|
64
|
-
learn?: {
|
|
65
|
-
local_pref?: number;
|
|
66
|
-
before?: readonly PolicyRule<never, never>[];
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
|
-
/** Rules of yours in its export, between the community actions and what we announce, such as a prepend. */
|
|
70
|
-
export?: readonly PolicyRule<never, never>[];
|
|
71
|
-
/** What we announce to it. Everything else is rejected. */
|
|
72
|
-
announce: readonly Announced[];
|
|
73
|
-
/** Drop RFC 1918 sources arriving from it. `privateDrops()` lists the VLANs. */
|
|
77
|
+
/** What we take, as its policy. Nothing when absent. */
|
|
78
|
+
import?: UpstreamPolicy;
|
|
79
|
+
/** What we send it, as its policy. */
|
|
80
|
+
export: UpstreamPolicy;
|
|
81
|
+
/** Its VLAN is listed by `privateDrops()`, for an ACL that drops RFC 1918 sources arriving from it. Nothing else is done with it. */
|
|
74
82
|
drop_private?: true;
|
|
75
83
|
};
|
|
76
|
-
/** Upstreams by key.
|
|
84
|
+
/** Upstreams by key. */
|
|
77
85
|
export type UpstreamCatalog = Readonly<Record<string, Upstream>>;
|
|
78
|
-
/**
|
|
79
|
-
type
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
readonly
|
|
84
|
-
readonly peer?: Pattern;
|
|
86
|
+
/** What every upstream's policies share, written once: plain functions over each policy's rules. */
|
|
87
|
+
export type UpstreamOptions = {
|
|
88
|
+
/** Each import's rules as the device gets them, from the rules the upstream writes. The rules as written when absent. */
|
|
89
|
+
mapImports?: (rules: readonly PolicyRule<string, string>[], key: string, upstream: Upstream) => readonly PolicyRule<string, string>[];
|
|
90
|
+
/** Each export's rules as the device gets them, from the rules the upstream writes. The rules as written when absent. */
|
|
91
|
+
mapExports?: (rules: readonly PolicyRule<string, string>[], key: string, upstream: Upstream) => readonly PolicyRule<string, string>[];
|
|
85
92
|
};
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
/** The native VLAN of a handoff sent tagged. `{key}-native` when absent. */
|
|
91
|
-
native?: Pattern;
|
|
92
|
-
/** The router's interface: per role, and `tunnel` for one reached over a tunnel. `{key}` when absent. */
|
|
93
|
-
interface?: Pattern | {
|
|
94
|
-
readonly provider?: Pattern;
|
|
95
|
-
readonly rs?: Pattern;
|
|
96
|
-
readonly peer?: Pattern;
|
|
97
|
-
readonly tunnel?: Pattern;
|
|
98
|
-
};
|
|
99
|
-
/** The import policy. `{KEY}-IN` when absent. */
|
|
100
|
-
import?: Pattern;
|
|
101
|
-
/** The export policy. `{KEY}-OUT` when absent. */
|
|
102
|
-
export?: Pattern;
|
|
103
|
-
/** The BGP group of an exchange's route servers or of multihop sessions. `{key}` when absent. */
|
|
104
|
-
group?: Pattern;
|
|
105
|
-
/** The description of a handoff's switch port. Its `name` when absent. No device names it, so it may be any function. */
|
|
106
|
-
port?: (key: string, upstream: Upstream) => string;
|
|
107
|
-
};
|
|
108
|
-
/** How each upstream's policies are written: shared by every upstream of a catalog. */
|
|
109
|
-
export type UpstreamPolicies<P extends string> = {
|
|
110
|
-
/** A policy holding `Bgp.sanity()` that each import calls. The sanity rules are written in each import when absent. */
|
|
111
|
-
sanity?: P;
|
|
112
|
-
/** Where a route came from, by upstream: a community or a rule, such as `communityScheme(...).tag(...)`. Nothing is tagged when absent. */
|
|
113
|
-
tag?: (key: string, upstream: Upstream) => StandardCommunity | LargeCommunity | PolicyRule<never, never>;
|
|
114
|
-
/** Rules first in each export, such as `communityScheme(...).actions(upstream.asn)`. */
|
|
115
|
-
actions?: (key: string, upstream: Upstream) => readonly PolicyRule<never, never>[];
|
|
116
|
-
/** The source address for routes of its family, one per family. */
|
|
117
|
-
sources?: readonly IP[];
|
|
118
|
-
};
|
|
119
|
-
/** The names a device can refer to, and their patterns when the project gives none. */
|
|
120
|
-
declare const DEFAULTS: {
|
|
121
|
-
readonly vlan: "{key}";
|
|
122
|
-
readonly native: "{key}-native";
|
|
123
|
-
readonly interface: "{key}";
|
|
124
|
-
readonly import: "{KEY}-IN";
|
|
125
|
-
readonly export: "{KEY}-OUT";
|
|
126
|
-
readonly group: "{key}";
|
|
127
|
-
};
|
|
128
|
-
type Defaults = typeof DEFAULTS;
|
|
129
|
-
type Field = keyof Defaults;
|
|
130
|
-
type Key<C> = keyof C & string;
|
|
131
|
-
/** The upstreams that land on a VLAN of ours. */
|
|
132
|
-
type Landed<C> = {
|
|
133
|
-
[K in Key<C>]: C[K] extends {
|
|
134
|
-
handoff: object;
|
|
135
|
-
} ? K : never;
|
|
136
|
-
}[Key<C>];
|
|
137
|
-
/** The upstreams that send their VLAN tagged. */
|
|
138
|
-
type Tagged<C> = {
|
|
139
|
-
[K in Key<C>]: C[K] extends {
|
|
93
|
+
type Values<T> = T[keyof T];
|
|
94
|
+
/** Each VLAN name the catalog writes. */
|
|
95
|
+
type VlanName<C> = Values<{
|
|
96
|
+
[K in keyof C]: C[K] extends {
|
|
140
97
|
handoff: {
|
|
141
|
-
|
|
98
|
+
vlan: {
|
|
99
|
+
name: infer N extends string;
|
|
100
|
+
};
|
|
142
101
|
};
|
|
143
|
-
} ?
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
type
|
|
147
|
-
[K in
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
[K in Ks]: Named<C, N, F, K>;
|
|
196
|
-
}[Ks];
|
|
197
|
-
/** The catalog as written, with any key the shape does not have refused, at every level. */
|
|
198
|
-
type Exact<S, T> = T extends readonly unknown[] | string | number | boolean | Secret | ((...args: never[]) => unknown) ? T : S extends unknown ? {
|
|
199
|
-
[K in keyof T]: K extends keyof S ? Exact<NonNullable<S[K]>, T[K]> : never;
|
|
200
|
-
} : never;
|
|
201
|
-
/** The names as written, with a name or a role Upstreams does not make refused. */
|
|
202
|
-
type ExactNames<N> = {
|
|
203
|
-
[F in keyof N]: F extends keyof UpstreamNames ? N[F] extends string | ((...args: never[]) => unknown) ? N[F] : {
|
|
204
|
-
[R in keyof N[F]]: R extends "provider" | "rs" | "peer" | "tunnel" ? N[F][R] : never;
|
|
205
|
-
} : never;
|
|
206
|
-
};
|
|
102
|
+
} ? N : never;
|
|
103
|
+
}>;
|
|
104
|
+
/** Each native VLAN name. */
|
|
105
|
+
type NativeName<C> = Values<{
|
|
106
|
+
[K in keyof C]: C[K] extends {
|
|
107
|
+
handoff: {
|
|
108
|
+
native: {
|
|
109
|
+
name: infer N extends string;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
} ? N : never;
|
|
113
|
+
}>;
|
|
114
|
+
/** Each router interface on a handoff. */
|
|
115
|
+
type HandoffInterface<C> = Values<{
|
|
116
|
+
[K in keyof C]: C[K] extends {
|
|
117
|
+
handoff: {
|
|
118
|
+
interface: infer N extends string;
|
|
119
|
+
};
|
|
120
|
+
} ? N : never;
|
|
121
|
+
}>;
|
|
122
|
+
/** Each router interface for a tunnel. */
|
|
123
|
+
type TunnelInterface<C> = Values<{
|
|
124
|
+
[K in keyof C]: C[K] extends {
|
|
125
|
+
tunnel: {
|
|
126
|
+
interface: infer N extends string;
|
|
127
|
+
};
|
|
128
|
+
} ? N : never;
|
|
129
|
+
}>;
|
|
130
|
+
/** Each policy name. */
|
|
131
|
+
type PolicyName<C> = Values<{
|
|
132
|
+
[K in keyof C]: (C[K] extends {
|
|
133
|
+
import: {
|
|
134
|
+
name: infer N extends string;
|
|
135
|
+
};
|
|
136
|
+
} ? N : never) | (C[K] extends {
|
|
137
|
+
export: {
|
|
138
|
+
name: infer N extends string;
|
|
139
|
+
};
|
|
140
|
+
} ? N : never);
|
|
141
|
+
}>;
|
|
142
|
+
/** Each BGP group name. */
|
|
143
|
+
type GroupName<C> = Values<{
|
|
144
|
+
[K in keyof C]: C[K] extends {
|
|
145
|
+
group: infer N extends string;
|
|
146
|
+
} ? N : never;
|
|
147
|
+
}>;
|
|
148
|
+
/** Each routing table name. */
|
|
149
|
+
type TableName<C> = Values<{
|
|
150
|
+
[K in keyof C]: C[K] extends {
|
|
151
|
+
table: infer N extends string;
|
|
152
|
+
} ? N : never;
|
|
153
|
+
}>;
|
|
207
154
|
/** A network's upstreams written once, and each device's part built from them. */
|
|
208
155
|
export declare class Upstreams {
|
|
209
156
|
/**
|
|
210
|
-
* Each upstream written once: its AS, where it lands, its sessions, limits and
|
|
211
|
-
*
|
|
212
|
-
* - VLAN `<key>`, and `<key>-native` for one that sends its VLAN tagged. The router's interface is `<key>` too, a VLAN interface or a GRE tunnel.
|
|
213
|
-
* - Policies `<KEY>-IN` and `<KEY>-OUT`; a BGP group `<key>` for an exchange's route servers or multihop sessions.
|
|
157
|
+
* Each upstream written once: its AS, where it lands, its sessions, limits and policies, and every name it puts on a device. Everything a device needs is built from it:
|
|
214
158
|
*
|
|
215
|
-
*
|
|
159
|
+
* - On the router: `vlans()`, `interfaces()`, `uplinks()`, `groups()`, `neighbors()`, `policies()`, `tunnelRules()`, `addresses()`.
|
|
160
|
+
* - On a switch: `vlans(device)`, `ports(device)`, `vlansOn(device)`.
|
|
161
|
+
* - For checks and ACLs: `untrusted()`, `natives()`, `privateDrops()`.
|
|
216
162
|
*
|
|
217
|
-
*
|
|
163
|
+
* An import or export is the rules the upstream writes, in order, passed through `mapImports` or `mapExports` when given: what every upstream shares, such as a call to a sanity policy or the community actions, goes there. Nothing else is added. Lists follow the catalog's order, where order counts on a device. A key the shape doesn't have is refused by the type, by its name.
|
|
218
164
|
*/
|
|
219
|
-
static define<const C extends UpstreamCatalog
|
|
220
|
-
[K in
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
policies: <const P extends string = never>(options?: UpstreamPolicies<P>) => Record<Names<C, N, "export", Key<C>> | Names<C, N, "import", Importing<C>>, PolicyRule<P, never>[]>;
|
|
232
|
-
untrusted: () => (Names<C, N, "native", Tagged<C>> | Names<C, N, "vlan", Landed<C>>)[];
|
|
233
|
-
natives: () => Names<C, N, "native", Tagged<C>>[];
|
|
234
|
-
privateDrops: () => Names<C, N, "vlan", Landed<C>>[];
|
|
165
|
+
static define<const C extends UpstreamCatalog>(catalog: C & UpstreamCatalog, options?: UpstreamOptions): {
|
|
166
|
+
vlans: (device?: string) => { [K in VlanName<C> | NativeName<C>]: Vlan; };
|
|
167
|
+
vlansOn: (device: string) => VlanName<C>[];
|
|
168
|
+
ports: (device: string) => Record<string, Port<NativeName<C> | VlanName<C>, never, never, never>>;
|
|
169
|
+
interfaces: () => { [K in HandoffInterface<C>]: VlanInterface<VlanName<C>, never>; } & { [K in TunnelInterface<C>]: Gre<never>; };
|
|
170
|
+
uplinks: () => HandoffInterface<C>[];
|
|
171
|
+
groups: () => Record<GroupName<C>, NeighborSettings<PolicyName<C>, TableName<C>>>;
|
|
172
|
+
neighbors: () => Record<string, Neighbor<PolicyName<C>, GroupName<C>, TableName<C>>>;
|
|
173
|
+
policies: () => Record<PolicyName<C>, PolicyRule<never, never>[]>;
|
|
174
|
+
untrusted: () => (NativeName<C> | VlanName<C>)[];
|
|
175
|
+
natives: () => NativeName<C>[];
|
|
176
|
+
privateDrops: () => VlanName<C>[];
|
|
235
177
|
tunnelRules: () => FilterRule<never, never>[];
|
|
236
178
|
addresses: () => IP[];
|
|
237
179
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Device } from "../schema/index.js";
|
|
2
2
|
/** A feature a platform renders once proved on one of its devices. */
|
|
3
|
-
export type ProvedFeature = "bfd-timers" | "static-route-bfd" | "multihop-bfd" | "alarm-severity" | "mac-limit" | "ethertype-filter" | "management-vdom";
|
|
3
|
+
export type ProvedFeature = "bfd-timers" | "static-route-bfd" | "multihop-bfd" | "alarm-severity" | "mac-limit" | "ethertype-filter" | "management-vdom" | "routing-tables";
|
|
4
4
|
export declare class Derive {
|
|
5
5
|
/** Each LAG's number: its own `id`, or its position among the device's LAGs. */
|
|
6
6
|
static lagIds(device: Device): Map<string, number>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Device, Family, Neighbor, NeighborSettings, PolicyRule } from "../../schema/index.js";
|
|
2
2
|
import { FortiOsBlock } from "./tree.js";
|
|
3
3
|
type AnyRule = PolicyRule<string, string>;
|
|
4
|
-
type AnyNeighbor = Neighbor<string, string>;
|
|
4
|
+
type AnyNeighbor = Neighbor<string, string, string>;
|
|
5
5
|
/** The lengths a prefix list's rule takes within its prefix. */
|
|
6
6
|
type Lengths = {
|
|
7
7
|
ge?: number;
|
|
@@ -23,7 +23,7 @@ export declare class FortiOsRouting {
|
|
|
23
23
|
readonly ipv6: 128;
|
|
24
24
|
};
|
|
25
25
|
/** A neighbor's settings with its group's beneath them. */
|
|
26
|
-
static neighborSettings(device: Device, neighbor: AnyNeighbor): NeighborSettings<string>;
|
|
26
|
+
static neighborSettings(device: Device, neighbor: AnyNeighbor): NeighborSettings<string, string>;
|
|
27
27
|
/** The families a session carries: its own, or the family of its address. */
|
|
28
28
|
static familiesOfNeighbor(device: Device, neighbor: AnyNeighbor): Family[];
|
|
29
29
|
/** Whether a static route, OSPF or BGP detects a dead neighbor with BFD, which the device then runs. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Device, Neighbor, NeighborSettings } from "../../schema/index.js";
|
|
2
2
|
import { type JunosStatements } from "./render.js";
|
|
3
|
-
type AnyNeighbor = Neighbor<string, string>;
|
|
3
|
+
type AnyNeighbor = Neighbor<string, string, string>;
|
|
4
4
|
/** BGP sessions, the policies they use, prefix sets and RPKI servers, added to one device's statements. */
|
|
5
5
|
export declare class JunosBgp {
|
|
6
6
|
private readonly out;
|
|
@@ -19,7 +19,7 @@ export declare class JunosBgp {
|
|
|
19
19
|
/** Add the device's prefix sets, RPKI servers, BGP sessions and the policies they use. */
|
|
20
20
|
static render(out: JunosStatements, device: Device): void;
|
|
21
21
|
/** A neighbor's settings with its group's beneath them. */
|
|
22
|
-
static settingsOf(device: Device, neighbor: AnyNeighbor): NeighborSettings<string>;
|
|
22
|
+
static settingsOf(device: Device, neighbor: AnyNeighbor): NeighborSettings<string, string>;
|
|
23
23
|
/** A name made of a value's words, for a community or a path-length list: `64500:*` becomes `64500-any`. */
|
|
24
24
|
private static slug;
|
|
25
25
|
/** A community as the platform writes a member: a large community carries the `large:` prefix. */
|
|
@@ -199,6 +199,10 @@ export declare class RouterOsRender {
|
|
|
199
199
|
/** Every switch rule the device's hardware ACLs make, in order: one for each port with an ACL, each rule of it, and each source and destination port or range of the rule. */
|
|
200
200
|
static switchRules(device: Device): SwitchRule[];
|
|
201
201
|
private routing;
|
|
202
|
+
/** A table beside the main one. `fib` puts its routes in the forwarding table, so traffic can be routed by them. */
|
|
203
|
+
private routingTables;
|
|
204
|
+
/** Rules in the order written. `lookup-only-in-table` drops what the table has no route for, and `lookup` falls back to the main table. */
|
|
205
|
+
private routingRules;
|
|
202
206
|
private staticRoutes;
|
|
203
207
|
private rpki;
|
|
204
208
|
/** The default instance exists on every device; it is stated in the form the export prints it. */
|
|
@@ -65,9 +65,9 @@ export type Vrf = {
|
|
|
65
65
|
/**
|
|
66
66
|
* Everything one device runs. What is not written here is removed from the device or returned to its default.
|
|
67
67
|
*
|
|
68
|
-
* Type parameters are the names the device declares, so a reference to a name that does not exist fails to compile: N ports, V VLANs, I interfaces, P policies, S prefix sets, G BGP groups, A ACLs, AS address sets, U users, F VRFs, C certificates.
|
|
68
|
+
* Type parameters are the names the device declares, so a reference to a name that does not exist fails to compile: N ports, V VLANs, I interfaces, P policies, S prefix sets, G BGP groups, A ACLs, AS address sets, U users, F VRFs, C certificates, T routing tables.
|
|
69
69
|
*/
|
|
70
|
-
export type Device<Pl extends string = string, M extends string = string, N extends string = string, V extends string = string, I extends string = string, P extends string = string, S extends string = string, G extends string = string, A extends string = string, AS extends string = string, U extends string = string, F extends string = string, C extends string = string> = {
|
|
70
|
+
export type Device<Pl extends string = string, M extends string = string, N extends string = string, V extends string = string, I extends string = string, P extends string = string, S extends string = string, G extends string = string, A extends string = string, AS extends string = string, U extends string = string, F extends string = string, C extends string = string, T extends string = string> = {
|
|
71
71
|
/** Hostname. */
|
|
72
72
|
name: string;
|
|
73
73
|
/** Operating system. It decides which adapter renders and applies the device. */
|
|
@@ -92,8 +92,8 @@ export type Device<Pl extends string = string, M extends string = string, N exte
|
|
|
92
92
|
interfaces?: Record<I, Interface<NoInfer<V>, NoInfer<F>>>;
|
|
93
93
|
/** Separate routing tables, keyed by name. */
|
|
94
94
|
vrfs?: Record<F, Vrf>;
|
|
95
|
-
/**
|
|
96
|
-
routing?: Routing<NoInfer<P>, G, NoInfer<I | N>, NoInfer<F
|
|
95
|
+
/** Routing tables and rules, static routes, BGP, OSPF and route validation. */
|
|
96
|
+
routing?: Routing<NoInfer<P>, G, NoInfer<I | N>, NoInfer<F>, T>;
|
|
97
97
|
/** Route policies, keyed by name. */
|
|
98
98
|
policies?: Record<P, RoutePolicy<NoInfer<P>, NoInfer<S>>>;
|
|
99
99
|
/** Prefix sets that policies match on, keyed by name. */
|
|
@@ -96,7 +96,11 @@ export type Firewall<I extends string, AS extends string> = {
|
|
|
96
96
|
};
|
|
97
97
|
/** Helpers that run. None when absent. */
|
|
98
98
|
helpers?: readonly Helper[];
|
|
99
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* Traffic to and from these prefixes passes without connection tracking. A flood toward them cannot fill the connection table, and rules see its packets in the state `untracked`. Everything is tracked when absent.
|
|
101
|
+
*
|
|
102
|
+
* Each address NAT uses inside one of these prefixes (a source translation's `to`, a destination translation's `to` or matched `dst`) stays tracked: Circuit writes two accept rules for it, one for traffic to it and one for traffic from it, before the untracked rules. Without them, a reply to that address would skip tracking and never be translated back.
|
|
103
|
+
*/
|
|
100
104
|
untracked?: readonly Prefix[];
|
|
101
105
|
};
|
|
102
106
|
/** A stateless rule evaluated in hardware where traffic arrives, before routing. */
|
|
@@ -109,7 +109,7 @@ export type Port<V extends string, A extends string, L extends string, F extends
|
|
|
109
109
|
acl?: A;
|
|
110
110
|
/** The LAG this port is in. Every other setting goes on the LAG. */
|
|
111
111
|
lag?: L;
|
|
112
|
-
/** Another of our devices at the other end of the cable.
|
|
112
|
+
/** Another of our devices at the other end of the cable. */
|
|
113
113
|
link?: Link;
|
|
114
114
|
};
|
|
115
115
|
/** Settings every interface that is not a physical port shares. */
|