64 lines
3.0 KiB
TypeScript
64 lines
3.0 KiB
TypeScript
import Refresh from '@mui/icons-material/Refresh'
|
|
import { Alert, Box, Button, Stack } from '@mui/material'
|
|
import { Grid, IPSEC_HEADS, Section, duration, ipsecRows, text } from './RouterPanel.tsx'
|
|
import Toolbar from './Toolbar.tsx'
|
|
import { DETAILS_SHAPE, request, type RouterDetails } from './api.ts'
|
|
import { useRead } from './useRead.ts'
|
|
|
|
/** Reads the router's /config. */
|
|
const readDetails = () => request<RouterDetails>('GET', '/config', DETAILS_SHAPE)
|
|
|
|
/**
|
|
* How long a site-to-site connection has been up: the age of its oldest established IKE SA; "down" without one, and
|
|
* "unknown" when the router could not list its SAs.
|
|
*/
|
|
function uptimeOf(sas: RouterDetails['ipsec']['sas'], name: string) {
|
|
if (sas === null) {
|
|
return 'unknown'
|
|
}
|
|
const ages = sas.filter((sa) => sa.name === name && sa.state === 'ESTABLISHED' && sa.established !== undefined).map((sa) => Number(sa.established))
|
|
return ages.length === 0 ? 'down' : duration(Math.max(...ages))
|
|
}
|
|
|
|
/**
|
|
* The state of the router's site-to-site connections, from its /config: each with the identities and subnets the
|
|
* router resolved for it and how long it has been up, and their IPsec security associations, child SAs, connections
|
|
* and connection children.
|
|
*/
|
|
export default function SiteToSiteStatusPanel() {
|
|
const { data: details, error, busy, refresh } = useRead(readDetails)
|
|
|
|
const names = new Set((details?.network.site_to_site ?? []).map((connection) => connection.name))
|
|
const { sas, childSas, connections, children } = ipsecRows(details, (name) => names.has(name))
|
|
|
|
return (
|
|
<Box component="section">
|
|
<Toolbar description="The state of the site-to-site connections.">
|
|
<Button variant="outlined" startIcon={<Refresh />} disabled={busy} onClick={() => void refresh()}>Refresh</Button>
|
|
</Toolbar>
|
|
{error && <Alert severity="error" sx={{ mb: 2, whiteSpace: 'pre-line' }}>{error}</Alert>}
|
|
{details && (
|
|
<Stack sx={{ gap: 2 }}>
|
|
<Section title="Site-to-site connections">
|
|
<Grid
|
|
head={['Connection', 'Local identity', 'Remote identity', 'Local CIDRs', 'Remote CIDRs', 'Uptime']}
|
|
rows={(details.network.site_to_site ?? []).map((connection) => [
|
|
connection.name,
|
|
text(connection.local_identity),
|
|
text(connection.remote_identity),
|
|
text(connection.local_cidrs),
|
|
text(connection.remote_cidrs),
|
|
uptimeOf(details.ipsec.sas, connection.name),
|
|
])}
|
|
/>
|
|
</Section>
|
|
<Section title="IPsec security associations"><Grid head={IPSEC_HEADS.sas} rows={sas} /></Section>
|
|
<Section title="IPsec child SAs"><Grid head={IPSEC_HEADS.childSas} rows={childSas} /></Section>
|
|
<Section title="IPsec connections"><Grid head={IPSEC_HEADS.connections} rows={connections} /></Section>
|
|
<Section title="IPsec connection children"><Grid head={IPSEC_HEADS.children} rows={children} /></Section>
|
|
</Stack>
|
|
)}
|
|
</Box>
|
|
)
|
|
}
|