diff --git a/nms/app/packages/magmalte/app/components/GatewayUtils.js b/nms/app/packages/magmalte/app/components/GatewayUtils.js index ba534741f3c0..33c0534d4967 100644 --- a/nms/app/packages/magmalte/app/components/GatewayUtils.js +++ b/nms/app/packages/magmalte/app/components/GatewayUtils.js @@ -187,3 +187,63 @@ export const DynamicServices = Object.freeze({ EVENTD: 'eventd', TD_AGENT_BIT: 'td-agent-bit', }); + +export const DEFAULT_GATEWAY_CONFIG = { + apn_resources: {}, + cellular: { + epc: { + ip_block: '192.168.128.0/24', + nat_enabled: true, + dns_primary: '', + dns_secondary: '', + sgi_management_iface_gw: '', + sgi_management_iface_static_ip: '', + sgi_management_iface_vlan: '', + }, + ran: { + pci: 260, + transmit_enabled: true, + }, + }, + connected_enodeb_serials: [], + description: '', + device: { + hardware_id: '', + key: { + key: '', + key_type: 'SOFTWARE_ECDSA_SHA256', + }, + }, + id: '', + magmad: { + autoupgrade_enabled: true, + autoupgrade_poll_interval: 60, + checkin_interval: 60, + checkin_timeout: 30, + dynamic_services: [DynamicServices.EVENTD, DynamicServices.TD_AGENT_BIT], + }, + name: '', + status: { + platform_info: { + packages: [ + { + version: '', + }, + ], + }, + }, + tier: 'default', +}; +export const DEFAULT_DNS_CONFIG = { + enable_caching: false, + local_ttl: 0, + records: [], +}; +export const DEFAULT_HE_CONFIG = { + enable_encryption: false, + encryption_key: '', + enable_header_enrichment: false, + he_encoding_type: 'BASE64', + he_encryption_algorithm: 'RC4', + he_hash_function: 'MD5', +}; diff --git a/nms/app/packages/magmalte/app/state/lte/EquipmentState.js b/nms/app/packages/magmalte/app/state/lte/EquipmentState.js index 46307b2d6d12..8841b2d4664c 100644 --- a/nms/app/packages/magmalte/app/state/lte/EquipmentState.js +++ b/nms/app/packages/magmalte/app/state/lte/EquipmentState.js @@ -17,6 +17,7 @@ import type {EnodebInfo} from '../../components/lte/EnodebUtils'; import type {EnodebState} from '../../components/context/EnodebContext'; import type { enodeb_serials, + gateway_cellular_configs, gateway_dns_configs, gateway_epc_configs, gateway_id, @@ -130,11 +131,11 @@ export async function FetchEnodebs(props: FetchProps) { enodebSerial: id, }, ); - const newEnb = {[id]: {enb_state: newEnbSt || {}, enb: enb}}; + const newEnb = {[id]: {enb_state: newEnbSt, enb: enb}}; return newEnb; } } catch (e) { - return {[id]: {enb_state: {} || {}, enb: enb}}; + return {[id]: {enb_state: {}, enb: enb}}; } } else { enb = await MagmaV1API.getLteByNetworkIdEnodebs({networkId}); @@ -318,6 +319,7 @@ export type UpdateGatewayProps = { epcConfigs?: gateway_epc_configs, ranConfigs?: gateway_ran_configs, dnsConfig?: gateway_dns_configs, + cellularConfigs?: gateway_cellular_configs, enbs?: enodeb_serials, networkId: network_id, setLteGateways: ({[string]: lte_gateway}) => void, @@ -380,6 +382,16 @@ export async function UpdateGateway(props: UpdateGatewayProps) { }), ); } + + if (props.cellularConfigs) { + requests.push( + MagmaV1API.putLteByNetworkIdGatewaysByGatewayIdCellular({ + networkId, + gatewayId: gatewayId, + config: props.cellularConfigs, + }), + ); + } await Promise.all(requests); const gateways = await MagmaV1API.getLteByNetworkIdGateways({ networkId, diff --git a/nms/app/packages/magmalte/app/views/equipment/EnodebDetailConfigEdit.js b/nms/app/packages/magmalte/app/views/equipment/EnodebDetailConfigEdit.js index 4099c128d6ca..91ea673b2824 100644 --- a/nms/app/packages/magmalte/app/views/equipment/EnodebDetailConfigEdit.js +++ b/nms/app/packages/magmalte/app/views/equipment/EnodebDetailConfigEdit.js @@ -47,7 +47,7 @@ import EnodeConfigEditTdd from './EnodebDetailConfigTdd'; import {AltFormField} from '../../components/FormField'; import {colors, typography} from '../../theme/default'; import {makeStyles} from '@material-ui/styles'; -import {useContext, useState} from 'react'; +import {useContext, useEffect, useState} from 'react'; import {useEnqueueSnackbar} from '@fbcnms/ui/hooks/useSnackbar'; import {useRouter} from '@fbcnms/ui/hooks'; @@ -160,10 +160,14 @@ function EnodeEditDialog(props: DialogProps) { const onClose = () => { // clear existing state - setEnb({}); props.onClose(); }; + useEffect(() => { + setTabPos(editProps ? EditTableType[editProps.editTable] : 0); + setEnb({}); + }, [editProps, open]); + return ( + + editFilter({editTable: 'headerEnrichment'})} + /> + + @@ -405,3 +412,49 @@ function ApnResourcesTable({gwInfo}: {gwInfo: lte_gateway}) { /> ); } + +function GatewayHE({gwInfo}: {gwInfo: lte_gateway}) { + const heEnabled = + gwInfo.cellular.he_config?.enable_header_enrichment ?? false; + const encryptionEnabled = + gwInfo.cellular.he_config?.enable_encryption ?? false; + const EncryptionDetail = () => { + const encryptionConfig: DataRows[] = [ + [ + { + category: 'Encryption Key', + value: gwInfo.cellular.he_config?.encryption_key || '', + obscure: true, + }, + { + category: 'Encoding Type', + value: gwInfo.cellular.he_config?.he_encoding_type || '', + }, + ], + [ + { + category: 'Encryption Algorithm', + value: gwInfo.cellular.he_config?.he_encryption_algorithm || '', + }, + { + category: 'Hash Function', + value: gwInfo.cellular.he_config?.he_hash_function || '', + }, + ], + ]; + return ; + }; + + const heConfig: DataRows[] = [ + [ + { + statusCircle: true, + status: heEnabled, + category: 'Header Enrichment', + value: heEnabled ? 'Enabled' : 'Disabled', + collapse: encryptionEnabled ? : <>, + }, + ], + ]; + return ; +} diff --git a/nms/app/packages/magmalte/app/views/equipment/GatewayDetailConfigEdit.js b/nms/app/packages/magmalte/app/views/equipment/GatewayDetailConfigEdit.js index 41f2af9ccaf6..75bf3b013022 100644 --- a/nms/app/packages/magmalte/app/views/equipment/GatewayDetailConfigEdit.js +++ b/nms/app/packages/magmalte/app/views/equipment/GatewayDetailConfigEdit.js @@ -20,6 +20,7 @@ import type { gateway_device, gateway_dns_configs, gateway_epc_configs, + gateway_he_config, gateway_logging_configs, gateway_ran_configs, lte_gateway, @@ -61,7 +62,12 @@ import Text from '@fbcnms/ui/components/design-system/Text'; import nullthrows from '@fbcnms/util/nullthrows'; import {AltFormField} from '../../components/FormField'; -import {DynamicServices} from '../../components/GatewayUtils'; +import { + DEFAULT_DNS_CONFIG, + DEFAULT_GATEWAY_CONFIG, + DEFAULT_HE_CONFIG, + DynamicServices, +} from '../../components/GatewayUtils'; import {colors, typography} from '../../theme/default'; import {makeStyles} from '@material-ui/styles'; import {useContext, useEffect, useState} from 'react'; @@ -73,52 +79,7 @@ const RAN_TITLE = 'Ran'; const AGGREGATION_TITLE = 'Aggregation'; const EPC_TITLE = 'Epc'; const APN_RESOURCES_TITLE = 'APN Resources'; -const DEFAULT_GATEWAY_CONFIG = { - apn_resources: {}, - cellular: { - epc: { - ip_block: '192.168.128.0/24', - nat_enabled: true, - dns_primary: '', - dns_secondary: '', - sgi_management_iface_gw: '', - sgi_management_iface_static_ip: '', - sgi_management_iface_vlan: '', - }, - ran: { - pci: 260, - transmit_enabled: true, - }, - }, - connected_enodeb_serials: [], - description: '', - device: { - hardware_id: '', - key: { - key: '', - key_type: 'SOFTWARE_ECDSA_SHA256', - }, - }, - id: '', - magmad: { - autoupgrade_enabled: true, - autoupgrade_poll_interval: 60, - checkin_interval: 60, - checkin_timeout: 30, - dynamic_services: [DynamicServices.EVENTD, DynamicServices.TD_AGENT_BIT], - }, - name: '', - status: { - platform_info: { - packages: [ - { - version: '', - }, - ], - }, - }, - tier: 'default', -}; +const HEADER_ENRICHMENT_TITLE = 'Header Enrichment'; const useStyles = makeStyles(_ => ({ appBarBtn: { @@ -161,6 +122,7 @@ const EditTableType = { epc: 2, ran: 3, apnResources: 4, + headerEnrichment: 5, }; export type EditProps = { @@ -222,17 +184,21 @@ function GatewayEditDialog(props: DialogProps) { const {open, editProps} = props; const classes = useStyles(); const {match} = useRouter(); - const [gateway, setGateway] = useState({}); + const [gateway, setGateway] = useState(DEFAULT_GATEWAY_CONFIG); const gatewayId: string = match.params.gatewayId; const [tabPos, setTabPos] = useState( editProps ? EditTableType[editProps.editTable] : 0, ); const ctx = useContext(GatewayContext); const onClose = () => { - setGateway({}); props.onClose(); }; + useEffect(() => { + setTabPos(editProps ? EditTableType[editProps.editTable] : 0); + setGateway(DEFAULT_GATEWAY_CONFIG); + }, [editProps, open]); + return ( + ; {tabPos === 0 && ( { setGateway(gateway); @@ -291,9 +261,7 @@ function GatewayEditDialog(props: DialogProps) { {tabPos === 1 && ( { setGateway(gateway); @@ -308,9 +276,7 @@ function GatewayEditDialog(props: DialogProps) { {tabPos === 2 && ( { setGateway(gateway); @@ -325,9 +291,7 @@ function GatewayEditDialog(props: DialogProps) { {tabPos === 3 && ( { setGateway(gateway); @@ -342,9 +306,22 @@ function GatewayEditDialog(props: DialogProps) { {tabPos === 4 && ( { + setGateway(gateway); + if (editProps) { + onClose(); + } else { + setTabPos(tabPos + 1); + } + }} + /> + )} + {tabPos === 5 && ( + { setGateway(gateway); @@ -361,7 +338,7 @@ function GatewayEditDialog(props: DialogProps) { type Props = { isAdd: boolean, - gateway?: lte_gateway, + gateway: lte_gateway, onClose: () => void, onSave: lte_gateway => void, }; @@ -452,7 +429,7 @@ export function ConfigEdit(props: Props) { placeholder="Enter ID" fullWidth={true} value={gateway.id} - readOnly={props.gateway ? true : false} + readOnly={props.gateway.id !== '' ? true : false} onChange={({target}) => setGateway({...gateway, id: target.value}) } @@ -523,11 +500,10 @@ export function DynamicServicesEdit(props: Props) { const enqueueSnackbar = useEnqueueSnackbar(); const ctx = useContext(GatewayContext); const {match} = useRouter(); - const gatewayId: string = - props.gateway?.id || nullthrows(match.params.gatewayId); + props.gateway.id || nullthrows(match.params.gatewayId); const [config, setConfig] = useState( - props.gateway?.magmad || DEFAULT_GATEWAY_CONFIG.magmad, + props.gateway.magmad, ); const handleChange = (val: boolean, key: string) => { @@ -569,7 +545,7 @@ export function DynamicServicesEdit(props: Props) { } const gateway = { - ...(props.gateway || DEFAULT_GATEWAY_CONFIG), + ...props.gateway, magmad: config, }; await ctx.updateGateway({gatewayId, magmadConfigs: config}); @@ -648,22 +624,20 @@ export function EPCEdit(props: Props) { }; const [EPCConfig, setEPCConfig] = useState( - props.gateway?.cellular.epc || DEFAULT_GATEWAY_CONFIG.cellular.epc, + props.gateway.cellular.epc, ); useEffect(() => { - setEPCConfig( - props.gateway?.cellular.epc || DEFAULT_GATEWAY_CONFIG.cellular.epc, - ); + setEPCConfig(props.gateway.cellular.epc); setError(''); - }, [props.gateway?.cellular.epc]); + }, [props.gateway.cellular.epc]); const onSave = async () => { try { const gateway = { - ...(props.gateway || DEFAULT_GATEWAY_CONFIG), + ...props.gateway, cellular: { - ...DEFAULT_GATEWAY_CONFIG.cellular, + ...props.gateway.cellular, epc: EPCConfig, }, }; @@ -784,12 +758,6 @@ export function EPCEdit(props: Props) { ); } -const DEFAULT_DNS_CONFIG = { - enable_caching: false, - local_ttl: 0, - records: [], -}; - export function RanEdit(props: Props) { const classes = useStyles(); const enqueueSnackbar = useEnqueueSnackbar(); @@ -797,14 +765,13 @@ export function RanEdit(props: Props) { const ctx = useContext(GatewayContext); const enbsCtx = useContext(EnodebContext); const [ranConfig, setRanConfig] = useState( - props.gateway?.cellular.ran || DEFAULT_GATEWAY_CONFIG.cellular.ran, + props.gateway.cellular.ran, ); const [dnsConfig, setDnsConfig] = useState( - props.gateway?.cellular.dns ?? {}, + props.gateway.cellular.dns ?? {}, ); const [connectedEnodebs, setConnectedEnodebs] = useState( - props.gateway?.connected_enodeb_serials || - DEFAULT_GATEWAY_CONFIG.connected_enodeb_serials, + props.gateway.connected_enodeb_serials, ); const handleRanChange = (key: string, val) => { setRanConfig({...ranConfig, [key]: val}); @@ -816,9 +783,9 @@ export function RanEdit(props: Props) { const onSave = async () => { try { const gateway = { - ...(props.gateway || DEFAULT_GATEWAY_CONFIG), + ...props.gateway, cellular: { - ...(props.gateway?.cellular || DEFAULT_GATEWAY_CONFIG.cellular), + ...props.gateway.cellular, ran: ranConfig, dns: {...DEFAULT_DNS_CONFIG, ...dnsConfig}, }, @@ -941,8 +908,7 @@ export function ApnResourcesEdit(props: Props) { const ctx = useContext(GatewayContext); const apnCtx = useContext(ApnContext); const lteCtx = useContext(LteNetworkContext); - const apnResources: apn_resources = - props.gateway?.apn_resources || DEFAULT_GATEWAY_CONFIG.apn_resources; + const apnResources: apn_resources = props.gateway.apn_resources ?? {}; const [apnResourcesRows, setApnResourcesRows] = useState( Object.keys(apnResources).map(apn => apnResources[apn]), ); @@ -970,7 +936,7 @@ export function ApnResourcesEdit(props: Props) { apn => (gatewayApnResources[apn.apn_name] = apn), ); const gateway = { - ...(props.gateway || DEFAULT_GATEWAY_CONFIG), + ...props.gateway, apn_resources: gatewayApnResources, }; await ctx.setState(gateway.id, gateway); @@ -1090,6 +1056,174 @@ export function ApnResourcesEdit(props: Props) { ))} + + + + + + ); +} + +export function HeaderEnrichmentConfig(props: Props) { + const enqueueSnackbar = useEnqueueSnackbar(); + const [error, setError] = useState(''); + const ctx = useContext(GatewayContext); + const [heConfig, setHeConfig] = useState( + props.gateway.cellular.he_config || DEFAULT_HE_CONFIG, + ); + + const handleHEChange = (key: string, val) => { + setHeConfig({...heConfig, [key]: val}); + }; + const heEncodingTypes = ['BASE64', 'HEX2BIN']; + const heEncryptionAlgorithmTypes = [ + 'RC4', + 'AES256_CBC_HMAC_MD5', + 'AES256_ECB_HMAC_MD5', + 'GZIPPED_AES256_ECB_SHA1', + ]; + const heHashFunctionTypes = ['MD5', 'HEX', 'SHA256']; + + const onSave = async () => { + try { + const gateway = { + ...props.gateway, + cellular: { + ...props.gateway.cellular, + he_config: heConfig, + }, + }; + await ctx.updateGateway({ + gatewayId: gateway.id, + cellularConfigs: { + ...props.gateway.cellular, + he_config: heConfig.enable_header_enrichment ? heConfig : undefined, + }, + }); + enqueueSnackbar('Gateway saved successfully', { + variant: 'success', + }); + props.onSave(gateway); + } catch (e) { + setError(e.response?.data?.message ?? e.message); + } + }; + + return ( + <> + + + {error !== '' && ( + + {error} + + )} + + + handleHEChange( + 'enable_header_enrichment', + !(heConfig?.enable_header_enrichment ?? false), + ) + } + checked={heConfig?.enable_header_enrichment ?? false} + /> + + + + + handleHEChange( + 'enable_encryption', + !(heConfig?.enable_encryption ?? false), + ) + } + checked={heConfig?.enable_encryption ?? false} + /> + + + + + handleHEChange('encryption_key', target.value) + } + /> + + + + + + + + + + + + +