Can configure network NAT settings from UI
This commit is contained in:
		@@ -13,10 +13,28 @@ export interface DHCPConfig {
 | 
			
		||||
  hosts: DHCPHost[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type NatSource =
 | 
			
		||||
  | { type: "interface"; name: string }
 | 
			
		||||
  | { type: "ip"; ip: string };
 | 
			
		||||
 | 
			
		||||
export type NatHostPort =
 | 
			
		||||
  | { type: "single"; port: number }
 | 
			
		||||
  | { type: "range"; start: number; end: number };
 | 
			
		||||
 | 
			
		||||
export interface NatEntry {
 | 
			
		||||
  protocol: "TCP" | "UDP" | "Both";
 | 
			
		||||
  host_addr: NatSource;
 | 
			
		||||
  host_port: NatHostPort;
 | 
			
		||||
  guest_addr: string;
 | 
			
		||||
  guest_port: number;
 | 
			
		||||
  comment?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IpConfig {
 | 
			
		||||
  bridge_address: string;
 | 
			
		||||
  prefix: number;
 | 
			
		||||
  dhcp?: DHCPConfig;
 | 
			
		||||
  nat?: NatEntry[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface NetworkInfo {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ export interface ServerConstraints {
 | 
			
		||||
  disk_size: LenConstraint;
 | 
			
		||||
  net_name_size: LenConstraint;
 | 
			
		||||
  net_title_size: LenConstraint;
 | 
			
		||||
  net_nat_comment_size: LenConstraint;
 | 
			
		||||
  dhcp_reservation_host_name: LenConstraint;
 | 
			
		||||
  nwfilter_name_size: LenConstraint;
 | 
			
		||||
  nwfilter_comment_size: LenConstraint;
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ import {
 | 
			
		||||
  ListItemText,
 | 
			
		||||
  Paper,
 | 
			
		||||
  Tooltip,
 | 
			
		||||
  Typography,
 | 
			
		||||
} from "@mui/material";
 | 
			
		||||
import { DHCPConfig, DHCPHost } from "../../api/NetworksApi";
 | 
			
		||||
import { ServerApi } from "../../api/ServerApi";
 | 
			
		||||
@@ -54,6 +55,11 @@ export function NetDHCPHostReservations(p: {
 | 
			
		||||
          </Grid>
 | 
			
		||||
        ))}
 | 
			
		||||
      </Grid>
 | 
			
		||||
      {p.dhcp.hosts.length === 0 && (
 | 
			
		||||
        <Typography style={{ textAlign: "center" }}>
 | 
			
		||||
          You have not set any DHCP host reservations.
 | 
			
		||||
        </Typography>
 | 
			
		||||
      )}
 | 
			
		||||
      {p.editable && (
 | 
			
		||||
        <Button onClick={addHost}>Add new host reservation</Button>
 | 
			
		||||
      )}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										301
									
								
								virtweb_frontend/src/widgets/forms/NetNatConfiguration.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								virtweb_frontend/src/widgets/forms/NetNatConfiguration.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,301 @@
 | 
			
		||||
import DeleteIcon from "@mui/icons-material/Delete";
 | 
			
		||||
import {
 | 
			
		||||
  Button,
 | 
			
		||||
  Card,
 | 
			
		||||
  CardActions,
 | 
			
		||||
  CardContent,
 | 
			
		||||
  Grid,
 | 
			
		||||
  IconButton,
 | 
			
		||||
  Tooltip,
 | 
			
		||||
  Typography,
 | 
			
		||||
} from "@mui/material";
 | 
			
		||||
import React, { PropsWithChildren } from "react";
 | 
			
		||||
import { NatEntry } from "../../api/NetworksApi";
 | 
			
		||||
import { ServerApi } from "../../api/ServerApi";
 | 
			
		||||
import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider";
 | 
			
		||||
import { IPInput } from "./IPInput";
 | 
			
		||||
import { PortInput } from "./PortInput";
 | 
			
		||||
import { RadioGroupInput } from "./RadioGroupInput";
 | 
			
		||||
import { SelectInput } from "./SelectInput";
 | 
			
		||||
import { TextInput } from "./TextInput";
 | 
			
		||||
 | 
			
		||||
export function NetNatConfiguration(p: {
 | 
			
		||||
  editable: boolean;
 | 
			
		||||
  nat: NatEntry[];
 | 
			
		||||
  nicsList: string[];
 | 
			
		||||
  onChange?: (nat: NatEntry[]) => void;
 | 
			
		||||
  version: 4 | 6;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  const confirm = useConfirm();
 | 
			
		||||
 | 
			
		||||
  const addEntry = () => {
 | 
			
		||||
    p.nat.push({
 | 
			
		||||
      host_addr: {
 | 
			
		||||
        type: "ip",
 | 
			
		||||
        ip: p.version === 4 ? "10.0.0.1" : "fd00::",
 | 
			
		||||
      },
 | 
			
		||||
      host_port: { type: "single", port: 80 },
 | 
			
		||||
      guest_addr: p.version === 4 ? "10.0.0.100" : "fd00::",
 | 
			
		||||
      guest_port: 10,
 | 
			
		||||
      protocol: "TCP",
 | 
			
		||||
    });
 | 
			
		||||
    p.onChange?.(p.nat);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const onDelete = async (idx: number) => {
 | 
			
		||||
    if (!(await confirm("Do you really want to delete this entry?"))) return;
 | 
			
		||||
 | 
			
		||||
    p.nat.splice(idx, 1);
 | 
			
		||||
    p.onChange?.(p.nat);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      {p.nat.map((e, num) => (
 | 
			
		||||
        <NatEntryForm
 | 
			
		||||
          key={num}
 | 
			
		||||
          {...p}
 | 
			
		||||
          entry={e}
 | 
			
		||||
          onChange={() => p.onChange?.(p.nat)}
 | 
			
		||||
          onDelete={() => onDelete(num)}
 | 
			
		||||
        />
 | 
			
		||||
      ))}
 | 
			
		||||
 | 
			
		||||
      {p.nat.length === 0 && (
 | 
			
		||||
        <Typography style={{ textAlign: "center" }}>
 | 
			
		||||
          You have not set any NAT entry yet.
 | 
			
		||||
        </Typography>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      {p.editable && <Button onClick={addEntry}>Add a new entry</Button>}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function NatEntryForm(p: {
 | 
			
		||||
  editable: boolean;
 | 
			
		||||
  version: 4 | 6;
 | 
			
		||||
  entry: NatEntry;
 | 
			
		||||
  onChange?: () => void;
 | 
			
		||||
  onDelete: () => void;
 | 
			
		||||
  nicsList: string[];
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  const guestPortEnd =
 | 
			
		||||
    p.entry.host_port.type === "range"
 | 
			
		||||
      ? p.entry.host_port.end - p.entry.host_port.start + p.entry.guest_port
 | 
			
		||||
      : undefined;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Card style={{ margin: "30px" }} elevation={3}>
 | 
			
		||||
      <CardContent>
 | 
			
		||||
        <Grid container>
 | 
			
		||||
          <NATEntryProp>
 | 
			
		||||
            <SelectInput
 | 
			
		||||
              {...p}
 | 
			
		||||
              label="Protocol"
 | 
			
		||||
              options={[
 | 
			
		||||
                { value: "TCP" },
 | 
			
		||||
                { value: "UDP" },
 | 
			
		||||
                { label: "TCP & UDP", value: "Both" },
 | 
			
		||||
              ]}
 | 
			
		||||
              value={p.entry.protocol}
 | 
			
		||||
              onValueChange={(v) => {
 | 
			
		||||
                p.entry.protocol = v as any;
 | 
			
		||||
                p.onChange?.();
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          </NATEntryProp>
 | 
			
		||||
          <NATEntryProp>
 | 
			
		||||
            <TextInput
 | 
			
		||||
              {...p}
 | 
			
		||||
              label="Comment"
 | 
			
		||||
              value={p.entry.comment}
 | 
			
		||||
              onValueChange={(v) => {
 | 
			
		||||
                p.entry.comment = v;
 | 
			
		||||
                p.onChange?.();
 | 
			
		||||
              }}
 | 
			
		||||
              size={ServerApi.Config.constraints.net_nat_comment_size}
 | 
			
		||||
            />
 | 
			
		||||
          </NATEntryProp>
 | 
			
		||||
 | 
			
		||||
          {/* Host conf */}
 | 
			
		||||
          <NATEntryProp label="Host configuration">
 | 
			
		||||
            <SelectInput
 | 
			
		||||
              {...p}
 | 
			
		||||
              label="Host address specification"
 | 
			
		||||
              options={[
 | 
			
		||||
                {
 | 
			
		||||
                  label: "Specific IP",
 | 
			
		||||
                  value: "ip",
 | 
			
		||||
                  description: "Use a pre-defined IP address",
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  label: "Network interface",
 | 
			
		||||
                  value: "interface",
 | 
			
		||||
                  description:
 | 
			
		||||
                    "Use active IP addresses on the selected network interface during network startup to determine host adddress",
 | 
			
		||||
                },
 | 
			
		||||
              ]}
 | 
			
		||||
              value={p.entry.host_addr.type}
 | 
			
		||||
              onValueChange={(v) => {
 | 
			
		||||
                p.entry.host_addr.type = v as any;
 | 
			
		||||
                p.onChange?.();
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
 | 
			
		||||
            {p.entry.host_addr.type === "ip" && (
 | 
			
		||||
              <IPInput
 | 
			
		||||
                {...p}
 | 
			
		||||
                label="Host IP address"
 | 
			
		||||
                value={p.entry.host_addr.ip}
 | 
			
		||||
                onValueChange={(v) => {
 | 
			
		||||
                  if (p.entry.host_addr.type === "ip")
 | 
			
		||||
                    p.entry.host_addr.ip = v!;
 | 
			
		||||
                  p.onChange?.();
 | 
			
		||||
                }}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
 | 
			
		||||
            {p.entry.host_addr.type === "interface" && (
 | 
			
		||||
              <SelectInput
 | 
			
		||||
                {...p}
 | 
			
		||||
                label="Network interface"
 | 
			
		||||
                value={p.entry.host_addr.name}
 | 
			
		||||
                options={p.nicsList.map((n) => {
 | 
			
		||||
                  return {
 | 
			
		||||
                    value: n,
 | 
			
		||||
                  };
 | 
			
		||||
                })}
 | 
			
		||||
                onValueChange={(v) => {
 | 
			
		||||
                  if (p.entry.host_addr.type === "interface")
 | 
			
		||||
                    p.entry.host_addr.name = v!;
 | 
			
		||||
                  p.onChange?.();
 | 
			
		||||
                }}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
          </NATEntryProp>
 | 
			
		||||
 | 
			
		||||
          <NATEntryProp label="Target guest configuration">
 | 
			
		||||
            <IPInput
 | 
			
		||||
              {...p}
 | 
			
		||||
              label="Guest address"
 | 
			
		||||
              value={p.entry.guest_addr}
 | 
			
		||||
              onValueChange={(v) => {
 | 
			
		||||
                p.entry.guest_addr = v!;
 | 
			
		||||
                p.onChange?.();
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          </NATEntryProp>
 | 
			
		||||
 | 
			
		||||
          <NATEntryProp>
 | 
			
		||||
            <RadioGroupInput
 | 
			
		||||
              {...p}
 | 
			
		||||
              options={[
 | 
			
		||||
                { label: "Single port", value: "single" },
 | 
			
		||||
                { label: "Range of ports", value: "range" },
 | 
			
		||||
              ]}
 | 
			
		||||
              value={p.entry.host_port.type}
 | 
			
		||||
              onValueChange={(v) => {
 | 
			
		||||
                p.entry.host_port.type = v as any;
 | 
			
		||||
                p.onChange?.();
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
 | 
			
		||||
            {p.entry.host_port.type === "single" && (
 | 
			
		||||
              <PortInput
 | 
			
		||||
                {...p}
 | 
			
		||||
                label="Host port"
 | 
			
		||||
                value={p.entry.host_port.port}
 | 
			
		||||
                onChange={(v) => {
 | 
			
		||||
                  if (p.entry.host_port.type === "single")
 | 
			
		||||
                    p.entry.host_port.port = v!;
 | 
			
		||||
                  p.onChange?.();
 | 
			
		||||
                }}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
 | 
			
		||||
            {p.entry.host_port.type === "range" && (
 | 
			
		||||
              <div style={{ display: "flex" }}>
 | 
			
		||||
                <PortInput
 | 
			
		||||
                  {...p}
 | 
			
		||||
                  label="Host port start"
 | 
			
		||||
                  value={p.entry.host_port.start}
 | 
			
		||||
                  onChange={(v) => {
 | 
			
		||||
                    if (p.entry.host_port.type === "range")
 | 
			
		||||
                      p.entry.host_port.start = v!;
 | 
			
		||||
                    p.onChange?.();
 | 
			
		||||
                  }}
 | 
			
		||||
                />
 | 
			
		||||
                <PortSpacer />
 | 
			
		||||
                <PortInput
 | 
			
		||||
                  {...p}
 | 
			
		||||
                  label="Host port end"
 | 
			
		||||
                  value={p.entry.host_port.end}
 | 
			
		||||
                  onChange={(v) => {
 | 
			
		||||
                    if (p.entry.host_port.type === "range")
 | 
			
		||||
                      p.entry.host_port.end = v!;
 | 
			
		||||
                    p.onChange?.();
 | 
			
		||||
                  }}
 | 
			
		||||
                />
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
          </NATEntryProp>
 | 
			
		||||
 | 
			
		||||
          <NATEntryProp>
 | 
			
		||||
            <div style={{ display: "flex", height: "100%", alignItems: "end" }}>
 | 
			
		||||
              <PortInput
 | 
			
		||||
                {...p}
 | 
			
		||||
                label={`Guest port ${guestPortEnd ? "start" : ""}`}
 | 
			
		||||
                value={p.entry.guest_port}
 | 
			
		||||
                onChange={(v) => {
 | 
			
		||||
                  p.entry.guest_port = v!;
 | 
			
		||||
                  p.onChange?.();
 | 
			
		||||
                }}
 | 
			
		||||
              />
 | 
			
		||||
              {guestPortEnd && <PortSpacer />}
 | 
			
		||||
              {guestPortEnd && (
 | 
			
		||||
                <PortInput
 | 
			
		||||
                  editable={false}
 | 
			
		||||
                  label={`Guest port end`}
 | 
			
		||||
                  value={guestPortEnd}
 | 
			
		||||
                  onChange={(v) => {
 | 
			
		||||
                    p.entry.guest_port = v!;
 | 
			
		||||
                    p.onChange?.();
 | 
			
		||||
                  }}
 | 
			
		||||
                />
 | 
			
		||||
              )}
 | 
			
		||||
            </div>
 | 
			
		||||
          </NATEntryProp>
 | 
			
		||||
        </Grid>
 | 
			
		||||
      </CardContent>
 | 
			
		||||
      <CardActions>
 | 
			
		||||
        {p.editable && (
 | 
			
		||||
          <Tooltip title="Remove the entry">
 | 
			
		||||
            <IconButton color="error" onClick={p.onDelete}>
 | 
			
		||||
              <DeleteIcon />
 | 
			
		||||
            </IconButton>
 | 
			
		||||
          </Tooltip>
 | 
			
		||||
        )}
 | 
			
		||||
      </CardActions>
 | 
			
		||||
    </Card>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function NATEntryProp(
 | 
			
		||||
  p: PropsWithChildren<{ label?: string }>
 | 
			
		||||
): React.ReactElement {
 | 
			
		||||
  return (
 | 
			
		||||
    <Grid item sm={12} md={6} style={{ padding: "20px" }}>
 | 
			
		||||
      {p.label && (
 | 
			
		||||
        <Typography variant="h6" style={{ marginBottom: "10px" }}>
 | 
			
		||||
          {p.label}
 | 
			
		||||
        </Typography>
 | 
			
		||||
      )}
 | 
			
		||||
      {p.children}
 | 
			
		||||
    </Grid>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function PortSpacer(): React.ReactElement {
 | 
			
		||||
  return <span style={{ width: "20px" }}></span>;
 | 
			
		||||
}
 | 
			
		||||
@@ -14,6 +14,7 @@ export function PortInput(p: {
 | 
			
		||||
      onValueChange={(v) => {
 | 
			
		||||
        p.onChange?.(sanitizePort(v));
 | 
			
		||||
      }}
 | 
			
		||||
      checkValue={(v) => Number(v) <= 65535}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										40
									
								
								virtweb_frontend/src/widgets/forms/RadioGroupInput.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								virtweb_frontend/src/widgets/forms/RadioGroupInput.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
import {
 | 
			
		||||
  RadioGroup,
 | 
			
		||||
  FormControlLabel,
 | 
			
		||||
  Radio,
 | 
			
		||||
  FormControl,
 | 
			
		||||
  FormLabel,
 | 
			
		||||
} from "@mui/material";
 | 
			
		||||
 | 
			
		||||
interface RadioGroupOption {
 | 
			
		||||
  label: string;
 | 
			
		||||
  value: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function RadioGroupInput(p: {
 | 
			
		||||
  editable: boolean;
 | 
			
		||||
  label?: string;
 | 
			
		||||
  options: RadioGroupOption[];
 | 
			
		||||
  value: string;
 | 
			
		||||
  onValueChange: (v: string) => void;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  return (
 | 
			
		||||
    <FormControl>
 | 
			
		||||
      {p.label && <FormLabel>{p.label}</FormLabel>}
 | 
			
		||||
      <RadioGroup
 | 
			
		||||
        row
 | 
			
		||||
        value={p.value}
 | 
			
		||||
        onChange={(_ev, v) => p.onValueChange?.(v)}
 | 
			
		||||
      >
 | 
			
		||||
        {p.options.map((o) => (
 | 
			
		||||
          <FormControlLabel
 | 
			
		||||
            disabled={!p.editable}
 | 
			
		||||
            value={o.value}
 | 
			
		||||
            control={<Radio />}
 | 
			
		||||
            label={o.label}
 | 
			
		||||
          />
 | 
			
		||||
        ))}
 | 
			
		||||
      </RadioGroup>
 | 
			
		||||
    </FormControl>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@@ -9,7 +9,7 @@ import { TextInput } from "./TextInput";
 | 
			
		||||
 | 
			
		||||
export interface SelectOption {
 | 
			
		||||
  value?: string;
 | 
			
		||||
  label: string;
 | 
			
		||||
  label?: string;
 | 
			
		||||
  description?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -23,9 +23,10 @@ export function SelectInput(p: {
 | 
			
		||||
  if (!p.editable && !p.value) return <></>;
 | 
			
		||||
 | 
			
		||||
  if (!p.editable) {
 | 
			
		||||
    const value = p.options.find((o) => o.value === p.value)?.label;
 | 
			
		||||
    const value = p.options.find((o) => o.value === p.value)?.label ?? p.value;
 | 
			
		||||
    return <TextInput label={p.label} editable={p.editable} value={value} />;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <FormControl fullWidth variant="standard" style={{ marginBottom: "15px" }}>
 | 
			
		||||
      <InputLabel>{p.label}</InputLabel>
 | 
			
		||||
@@ -41,7 +42,7 @@ export function SelectInput(p: {
 | 
			
		||||
            style={{ fontStyle: e.value === undefined ? "italic" : undefined }}
 | 
			
		||||
          >
 | 
			
		||||
            <div>
 | 
			
		||||
              {e.label}
 | 
			
		||||
              {e.label ?? e.value}
 | 
			
		||||
              {e.description && (
 | 
			
		||||
                <Typography
 | 
			
		||||
                  component={"div"}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,13 +8,14 @@ import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider";
 | 
			
		||||
import { useSnackbar } from "../../hooks/providers/SnackbarProvider";
 | 
			
		||||
import { AsyncWidget } from "../AsyncWidget";
 | 
			
		||||
import { TabsWidget } from "../TabsWidget";
 | 
			
		||||
import { XMLAsyncWidget } from "../XMLWidget";
 | 
			
		||||
import { EditSection } from "../forms/EditSection";
 | 
			
		||||
import { IPInput } from "../forms/IPInput";
 | 
			
		||||
import { NetDHCPHostReservations } from "../forms/NetDHCPHostReservations";
 | 
			
		||||
import { NetNatConfiguration } from "../forms/NetNatConfiguration";
 | 
			
		||||
import { ResAutostartInput } from "../forms/ResAutostartInput";
 | 
			
		||||
import { SelectInput } from "../forms/SelectInput";
 | 
			
		||||
import { TextInput } from "../forms/TextInput";
 | 
			
		||||
import { NetDHCPHostReservations } from "../forms/NetDHCPHostReservations";
 | 
			
		||||
import { XMLAsyncWidget } from "../XMLWidget";
 | 
			
		||||
 | 
			
		||||
interface DetailsProps {
 | 
			
		||||
  net: NetworkInfo;
 | 
			
		||||
@@ -223,7 +224,7 @@ function NetworkDetailsTabGeneral(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
function NetworkDetailsTabIPv4(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
  return (
 | 
			
		||||
    <IPSection
 | 
			
		||||
      editable={p.editable}
 | 
			
		||||
      {...p}
 | 
			
		||||
      config={p.net.ip_v4}
 | 
			
		||||
      onChange={(c) => {
 | 
			
		||||
        p.net.ip_v4 = c;
 | 
			
		||||
@@ -237,7 +238,7 @@ function NetworkDetailsTabIPv4(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
function NetworkDetailsTabIPv6(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
  return (
 | 
			
		||||
    <IPSection
 | 
			
		||||
      editable={p.editable}
 | 
			
		||||
      {...p}
 | 
			
		||||
      config={p.net.ip_v6}
 | 
			
		||||
      onChange={(c) => {
 | 
			
		||||
        p.net.ip_v6 = c;
 | 
			
		||||
@@ -253,6 +254,7 @@ function IPSection(p: {
 | 
			
		||||
  config?: IpConfig;
 | 
			
		||||
  onChange: (c: IpConfig | undefined) => void;
 | 
			
		||||
  version: 4 | 6;
 | 
			
		||||
  nicsList: string[];
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  const confirm = useConfirm();
 | 
			
		||||
 | 
			
		||||
@@ -260,7 +262,7 @@ function IPSection(p: {
 | 
			
		||||
    if (!!p.config) {
 | 
			
		||||
      if (
 | 
			
		||||
        !(await confirm(
 | 
			
		||||
          `Do you really want to disable IPv${p.version} on this network?`
 | 
			
		||||
          `Do you really want to disable IPv${p.version} on this network? Specific configuration will be deleted!`
 | 
			
		||||
        ))
 | 
			
		||||
      )
 | 
			
		||||
        return;
 | 
			
		||||
@@ -275,8 +277,8 @@ function IPSection(p: {
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const toggleDHCP = (v: boolean) => {
 | 
			
		||||
    if (v)
 | 
			
		||||
  const toggleDHCP = async (v: boolean) => {
 | 
			
		||||
    if (v) {
 | 
			
		||||
      p.config!.dhcp =
 | 
			
		||||
        p.version === 4
 | 
			
		||||
          ? {
 | 
			
		||||
@@ -285,7 +287,32 @@ function IPSection(p: {
 | 
			
		||||
              hosts: [],
 | 
			
		||||
            }
 | 
			
		||||
          : { start: "fd00::100", end: "fd00::f00", hosts: [] };
 | 
			
		||||
    else p.config!.dhcp = undefined;
 | 
			
		||||
    } else {
 | 
			
		||||
      if (
 | 
			
		||||
        !(await confirm(
 | 
			
		||||
          `Do you really want to disable DHCPv${p.version} on this network? Specific configuration will be deleted!`
 | 
			
		||||
        ))
 | 
			
		||||
      )
 | 
			
		||||
        return;
 | 
			
		||||
      p.config!.dhcp = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    p.onChange?.(p.config);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const toggleNAT = async (v: boolean) => {
 | 
			
		||||
    if (v) {
 | 
			
		||||
      p.config!.nat = [];
 | 
			
		||||
    } else {
 | 
			
		||||
      if (
 | 
			
		||||
        (p.config?.nat?.length ?? 0 > 0) &&
 | 
			
		||||
        !(await confirm(
 | 
			
		||||
          `Do you really want to disable NAT port forwarding on this network? Specific configuration will be deleted!`
 | 
			
		||||
        ))
 | 
			
		||||
      )
 | 
			
		||||
        return;
 | 
			
		||||
      p.config!.nat = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    p.onChange?.(p.config);
 | 
			
		||||
  };
 | 
			
		||||
@@ -384,6 +411,31 @@ function IPSection(p: {
 | 
			
		||||
          />
 | 
			
		||||
        </EditSection>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      {p.config && (p.editable || p.config.nat) && (
 | 
			
		||||
        <EditSection
 | 
			
		||||
          title={`NAT v${p.version} ports redirection`}
 | 
			
		||||
          fullWidth
 | 
			
		||||
          actions={
 | 
			
		||||
            <Checkbox
 | 
			
		||||
              disabled={!p.editable}
 | 
			
		||||
              checked={!!p.config.nat}
 | 
			
		||||
              onChange={(_ev, val) => toggleNAT(val)}
 | 
			
		||||
            />
 | 
			
		||||
          }
 | 
			
		||||
        >
 | 
			
		||||
          {p.config.nat && (
 | 
			
		||||
            <NetNatConfiguration
 | 
			
		||||
              {...p}
 | 
			
		||||
              nat={p.config.nat}
 | 
			
		||||
              onChange={(n) => {
 | 
			
		||||
                p.config!.nat = n;
 | 
			
		||||
                p.onChange?.(p.config);
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          )}
 | 
			
		||||
        </EditSection>
 | 
			
		||||
      )}
 | 
			
		||||
    </Grid>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user