Skip to content
academia.sh

Lesson 10 / 15

Internet and Address Translation Gateways

A private subnet's instances' relationship with the outside is counted in four layouts: which five jobs stall by name in the no-gateway layout, how many instances' traffic a single gateway routes through one point and in which period it crosses the connection limit, how far a gateway per zone divides the failure radius and what it raises the cost to, and how many items and how many external records a public address per instance opens.

Published Updated

In this lesson

  • Names the jobs the no-gateway layout blocks and works out the instance count able to exit in each layout
  • Measures the single gateway's two separate limits — the concurrent connection bottleneck and the failure radius — and counts how far a gateway per zone divides each
  • Sweeps the four layouts' periodic cost across eight periods and measures the cheapest layout's cost of reversal in external record count
Contents

In the previous two lessons, the gateway appeared as both the fix and the bill: every outbound flow left through it, every flow dropped onto the wrong path fell there, and every new zone asked for one more. The gateway itself was never counted. Once every subnet was made private, the externally reachable item count dropped to one — leaving the question in the opposite direction: getting from inside to outside.

This question does not look like a security question, and it is one. An outbound path creates a dependency even without opening an inbound one: the gateway’s externally visible address gets recorded by the other side, and those records make up the hardest item to reverse. Four layouts are compared under the same workload.

  • CN19. The four layouts, the work list, and the costs belong to the course’s fictional regional measurement network. No real gateway is set up; what is measured is the model built with node.
  • CN20. The gateway’s concurrent connection limit is written by hand as 55,000, and peak-time outbound connections per instance as 90. In reality both vary by provider and workload; it shifts the saturation period without changing whether the bottleneck exists.
  • CN21. Costs are in fictional units, not currency. The fixed component is written per gateway and per address, the flow component per hundred volume units.
  • CN22. Three external services are assumed to write the egress address into their own records. In reality this number grows with the dependency count; it raises the reversal cost without changing its direction.
  • CN23. In the single-gateway layout, three zones’ traffic crosses the zone boundary and is billed separately for it.
  • CN24. The gateway’s own redundancy is counted as the provider’s. “If the gateway drops” means a zone-level loss; it does not change how the failure radius is computed.

The Model of Four Layouts

The model defines a layout with three numbers: how many gateways there are, whether every instance has been given a public address, and which results follow from these. The measure function returns six separate items at once — instances able to exit, externally reachable items, stalled work, peak connections, failure radius, and external records. Cost splits into fixed and flow, because the two grow at different rates with each period.

// gateway.mjs — a private subnet's relationship with the outside; no real gateway is set up, the model is counted
export const INSTANCES = 616, ZONES = 4, PORTS = 4;   // the previous lesson's third layout: everything in private subnets
export const LIMIT = 55000;                     // gateway's concurrent connection limit (fictional, CN20)
export const PEAK_PER_INSTANCE = 90;             // peak-time outbound connections per instance (CN20)
export const VOLUME = 1960;                      // periodic external egress volume (from the previous lesson)
export const EXTERNAL_PARTIES = 3;               // external services that record the gateway's address (CN22)
// Costs are fictional units (CN21); written per 100 volume units, they carry a ratio, not a currency.
export const GATEWAY_FIXED = 40, ADDRESS_RENT = 0.5, EGRESS = 2, PROCESSING = 1, CROSS_ZONE = 1;

export const WORK = ['patching and package retrieval', 'calls to the external measurement service', 'time source',
  'certificate renewal', 'log shipping'];

// [name, gateway count, public address per instance?]
export const LAYOUT = [['no gateway', 0, false], ['single translation gateway', 1, false],
  ['gateway per zone', ZONES, false], ['public address per instance', 0, true]];

export const measure = ([name, gateways, addressed], instances = INSTANCES, volume = VOLUME) => {
  const addresses = addressed ? instances : 0;                       // one public address per instance
  const canExit = gateways > 0 || addresses > 0;
  const crossZone = gateways === 1 ? volume * (ZONES - 1) / ZONES : 0;   // a single gateway pulls three zones' traffic
  const flow = canExit ? (volume * EGRESS + (gateways ? volume * PROCESSING + crossZone * CROSS_ZONE : 0)) / 100 : 0;
  const fixed = gateways * GATEWAY_FIXED + addresses * ADDRESS_RENT;
  return { name, gateways, addresses, canExit: canExit ? instances : 0, item: addresses * PORTS,
    stalled: canExit ? 0 : WORK.length, peak: gateways ? Math.round(instances * PEAK_PER_INSTANCE / gateways) : 0,
    radius: gateways ? Math.round(instances / gateways) : 0, records: (gateways || addresses) * EXTERNAL_PARTIES,
    flow: Math.round(flow * 10) / 10, fixed, total: Math.round((flow + fixed) * 10) / 10 };
};

The Same Workload, Four Layouts

// run1.mjs — four layouts under the same workload: surface, bottleneck, failure radius, and cost
import { LAYOUT, WORK, LIMIT, INSTANCES, VOLUME, measure } from './gateway.mjs';

console.log(`${INSTANCES} private instances, periodic external volume ${VOLUME}, gateway concurrent connection limit ${LIMIT}`);
console.log(`${'layout'.padEnd(29)} ${'exits'.padStart(6)} ${'open item'.padStart(11)} ${'stalled'.padStart(9)} ` +
  `${'peak conn'.padStart(14)} ${'failure radius'.padStart(15)} ${'records'.padStart(10)} ${'total cost'.padStart(13)}`);
for (const d of LAYOUT) {
  const m = measure(d);
  const peak = m.gateways ? `${m.peak} (${Math.round(m.peak * 100 / LIMIT)}%)` : '-';
  console.log(`${m.name.padEnd(29)} ${String(m.canExit).padStart(6)} ${String(m.item).padStart(11)} ` +
    `${String(m.stalled).padStart(9)} ${peak.padStart(14)} ${String(m.radius ? `${m.radius} instances` : '-').padStart(15)} ` +
    `${String(m.records).padStart(10)} ${String(m.total).padStart(13)}`);
}
console.log(`\nstalled work in the no-gateway layout (${WORK.length} items): ${WORK.join(', ')}`);
const [single, perZone] = [measure(LAYOUT[1]), measure(LAYOUT[2])];
console.log(`cost breakdown  single gateway: fixed ${single.fixed} + flow ${single.flow}   ` +
  `per zone: fixed ${perZone.fixed} + flow ${perZone.flow}`);
console.log(`moving from single gateway to per zone: ${perZone.records - single.records} new external records, the same count on the way back`);
616 private instances, periodic external volume 1960, gateway concurrent connection limit 55000
layout                         exits   open item   stalled      peak conn  failure radius    records    total cost
no gateway                         0           0         5              -               -          0             0
single translation gateway       616           0         0   55440 (101%)   616 instances          3         113.5
gateway per zone                 616           0         0    13860 (25%)   154 instances         12         218.8
public address per instance      616        2464         0              -               -       1848         347.2

stalled work in the no-gateway layout (5 items): patching and package retrieval, calls to the external measurement service, time source, certificate renewal, log shipping
cost breakdown  single gateway: fixed 40 + flow 73.5   per zone: fixed 160 + flow 58.8
moving from single gateway to per zone: 9 new external records, the same count on the way back

The first row is full of zeros, and that does not make it good. The no-gateway layout has no open item, no external record, no cost — and no instance able to exit either. The five items in the stalled work column are written by name: patching and package retrieval, calls to the external measurement service, time source, certificate renewal, log shipping. The first means machines stay unpatched, the third means the clock drifts, the fourth means certificates expire. The zero-cost layout is the layout that gives back every gain made at the machine level, a few periods later.

The single-gateway row carries three numbers at once. It has the lowest cost: 113.5 units, less than half the per-zone gateway. The peak connection column shows the first limit — 55,440, 101% of the limit. In this period the gateway fills, and what happens when it fills shows up not as a failure but as slowdown: new connections queue. The failure radius is the second limit: if the zone the gateway sits in is lost, all 616 instances become unable to exit, because the 462 instances in the other three zones were also routing their traffic through it.

A gateway per zone fixes both at once and flips the bill. The fixed cost rises from 40 to 160, and the flow cost drops from 73.5 to 58.8 — because no traffic crosses a zone boundary anymore. The total rises from 113.5 to 218.8, roughly double. In exchange, peak fill drops from 101% to 25%, and the failure radius from 616 instances to 154.

The 14.7 units difference in flow cost is small on its own, and where it comes from matters. A single gateway pulls three of the four zones’ traffic into its own zone; that traffic crosses the zone boundary twice, and each crossing is billed separately. This item shows up in no route table, shows up in no gateway count, and shows up only on the bill — and because it depends on which zone the gateway is placed in, it is the result of a single-row decision.

The last row is the fourth layout, and it is a warning on its own. Giving every instance a public address removes the gateway problem entirely: no bottleneck, no failure radius, the lowest flow cost. In exchange, two surfaces open up. The first is 2,464 open items — the number the previous lesson cut to one comes back, and more than comes back. The second is 1,848 external records: every instance’s address sits in three external services’ records.

Across Eight Periods

// run2.mjs — across eight periods: when the single gateway fills, and when each layout turns expensive
import { LAYOUT, LIMIT, VOLUME, INSTANCES, measure } from './gateway.mjs';

// The previous lessons' growth series; period 8 is the value rounded per zone
const SERIES = [[1, 18], [2, 26], [3, 44], [4, 71], [5, 118], [6, 196], [7, 340], [8, 616]];
console.log(`${'period'.padEnd(7)} ${'instances'.padStart(10)} ${'single gw fill'.padStart(18)} ${'per zone'.padStart(12)} ` +
  `${'single gw'.padStart(10)} ${'per zone'.padStart(12)} ${'per instance'.padStart(13)} ${'open item'.padStart(11)}`);
for (const [d, n] of SERIES) {
  const h = Math.round(VOLUME * n / INSTANCES);
  const [single, perZone, perAddr] = [1, 2, 3].map((i) => measure(LAYOUT[i], n, h));
  const pct = (m) => `${Math.round(m.peak * 100 / LIMIT)}%`;
  console.log(`${String(d).padEnd(7)} ${String(n).padStart(10)} ` +
    `${`${single.peak} ${pct(single)}${single.peak > LIMIT ? ' over' : ''}`.padStart(18)} ${pct(perZone).padStart(12)} ` +
    `${String(single.total).padStart(10)} ${String(perZone.total).padStart(12)} ${String(perAddr.total).padStart(13)} ` +
    `${String(perAddr.item).padStart(11)}`);
}
const cheapest = SERIES.map(([d, n]) => {
  const h = Math.round(VOLUME * n / INSTANCES);
  const m = [1, 2, 3].map((i) => measure(LAYOUT[i], n, h));
  return [d, m.reduce((a, b) => (b.total < a.total ? b : a)).name];
});
const records = (n) => measure(LAYOUT[3], n).records;
console.log(`\nthe way back from the public-address-per-instance layout: ${records(71)} external records in period 4, ${records(616)} in period 8`);
console.log('period by period, the cheapest layout:');
for (const [name, periods] of Object.entries(cheapest.reduce((a, [d, name]) => ({ ...a, [name]: [...(a[name] || []), d] }), {})))
  console.log(`  ${name.padEnd(29)} period ${periods.join(', ')}`);
period   instances     single gw fill     per zone  single gw     per zone  per instance   open item
1               18            1620 3%           1%       42.1        161.7          10.1          72
2               26            2340 4%           1%       43.1        162.5          14.7         104
3               44            3960 7%           2%       45.3        164.2          24.8         176
4               71           6390 12%           3%       48.5        166.8            40         284
5              118          10620 19%           5%       54.1        171.3          66.5         472
6              196          17640 32%           8%       63.4        178.7         110.5         784
7              340          30600 56%          14%       80.6        192.5         191.6        1360
8              616    55440 101% over          25%      113.5        218.8         347.2        2464

the way back from the public-address-per-instance layout: 213 external records in period 4, 1848 in period 8
period by period, the cheapest layout:
  public address per instance   period 1, 2, 3, 4
  single translation gateway    period 5, 6, 7, 8

The sweep shows two curves. The gateway layouts’ cost starts from a fixed base and grows slowly; the address-per-instance layout’s cost starts from zero and grows linearly with the instance count. They cross in period five. For the first four periods, the cheapest layout is the layout with the highest open item count, and in those periods the open item count runs between 72 and 284 — large against the gateway layouts’ zero, but not large enough to trigger an alarm.

The single gateway column’s fill value is the real warning. 56% in period seven, 101% in period eight. Between the two periods the instance count grew 81%, and fill jumped 45 points; on a linear curve, the surprise is not in the curve itself but in where the limit sits. The gateway-per-zone stays at 25% in the same period, meaning it has three more periods of headroom. Moving from a single gateway to per zone costs nine new external records, and that is work that cannot be done at the moment it needs to be done — it depends on the external parties’ calendar.

Fill crossing 100% does not produce a failure record of its own, and it does not show up where it gets looked for either. A gateway pressed against its limit does not reject a connection, it holds it; the side looking from inside sees this as a slowing external call. Three of the five jobs — calls to the external measurement service, certificate renewal, and log shipping — start timing out, and all three read as a failure of their own component. The number measured sits at the gateway; the symptom sits in five other places.

The irreversible item in this lesson is not the gateway itself, but the trace the egress address leaves outside. The gateway gets deleted, rebuilt, its count increased — but once an address is written into three external services’ records, those records are not in hand. This is exactly the reversal cost of the address-per-instance layout, and it grows with the period: 213 records in period four, 1,848 in period eight. The layout that is cheapest for the first four periods is the layout most expensive to abandon in period five.

The item staying with the team is four pieces: gateway count, which zone the gateway sits in, which instances get a public address, and which address the exit is seen from. Three items are handed off — the gateway’s concurrent connection limit, its own redundancy, and the translation operation’s latency. The first of the three handed off decides the entire table for period eight, and it is asked nowhere.

Summary

  • The no-gateway layout produces 0 open items, 0 external records, and 0 cost; in exchange none of the 616 instances can exit, and 5 jobs stall by name: patching and package retrieval, calls to the external measurement service, time source, certificate renewal, log shipping.
  • The single translation gateway is the cheapest gateway layout — 113.5 units — and it carries two limits at once: peak connections at 55,440, 101% of the limit, and a failure radius of 616 instances. The 462 instances in three zones route their traffic through another zone.
  • A gateway per zone drops fill to 25% and the failure radius to 154 instances. The fixed cost rises from 40 to 160, and the flow cost drops from 73.5 to 58.8 since no zone boundary is crossed; the total rises from 113.5 to 218.8, roughly double.
  • A public address per instance zeroes out the bottleneck and the failure radius, and opens 2,464 open items and 1,848 external records in exchange. This is the cheapest layout for the first 4 periods; in period 5 the single gateway takes over.
  • The irreversible item is the trace the egress address leaves outside. Moving from a single gateway to per zone costs 9 new external records; leaving the address-per-instance layout costs 213 records in period 4 and 1,848 in period 8, and none of these are on our own calendar.

Next Step

In all four of the four layouts, outbound traffic was counted, inbound connections were counted — but nowhere was it written which connection is allowed. The open item column assumed every instance listens on four ports, and how many of those ports actually need to stay open was never asked. The next lesson’s question writes the same policy in two separate mechanisms and runs it against a known flow set: how many rules a filter that recognizes return traffic on its own needs, how many rules the same policy needs from a filter that does not recognize it, which flows get caught, which get away, and which get closed by mistake — and when the two mechanisms are used together, which one is evaluated first, and in which case that order produces a result that cannot be understood just by reading what is written.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ ↵ Esc navigate · open · close