diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aaadaeb --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# autoconf / automake generated +aclocal.m4 +autom4te.cache/ +configure +Makefile.in +source/Makefile.in +source/RdkVlanManager/Makefile.in +source/TR-181/Makefile.in +source/TR-181/middle_layer_src/Makefile.in +cfg/compile +cfg/config.guess +cfg/config.h.in +cfg/config.sub +cfg/depcomp +cfg/install-sh +cfg/libtool.m4 +cfg/ltmain.sh +cfg/ltoptions.m4 +cfg/ltsugar.m4 +cfg/ltversion.m4 +cfg/lt~obsolete.m4 +cfg/missing + +# dm_pack generated +source/RdkVlanManager/dm_pack_datamodel.c + +# misc +.lastlogin diff --git a/config/RdkVlanManager.xml b/config/RdkVlanManager.xml index 1cfda42..ee94afe 100644 --- a/config/RdkVlanManager.xml +++ b/config/RdkVlanManager.xml @@ -203,6 +203,12 @@ uint32 true + + UntaggedVlanType + string: MacvlanPrivate(0),MacvlanVepa(1),MacvlanBridge(2),MacvlanPassthru(3),MacvlanSource(4),VlanTag0(5),Bridge(6),Tagged(7) + uint32/mapped + false + X_RDK_BaseInterface string(64) diff --git a/docs/vlan-configuration.md b/docs/vlan-configuration.md new file mode 100644 index 0000000..acba1e7 --- /dev/null +++ b/docs/vlan-configuration.md @@ -0,0 +1,168 @@ +# VlanManager Configuration Guide + +This document describes how to configure VlanManager VLAN termination entries +through PSM, when to choose each VLAN / MACVLAN type, and how the +`UntaggedVlanType` data-model parameter maps to the underlying Linux netdev. + +- [1. PSM configuration](#1-psm-configuration) + - [1.1 Per-entry PSM keys](#11-per-entry-psm-keys) + - [1.2 Country / region PSM (HUB4 only)](#12-country--region-psm-hub4-only) + - [1.3 Example](#13-example) +- [2. Interface type selection](#2-interface-type-selection) + - [2.1 The `UntaggedVlanType` parameter](#21-the-untaggedvlantype-parameter) + - [2.2 Decision guide](#22-decision-guide) +- [3. Data model parameters](#3-data-model-parameters) + +--- + +## 1. PSM configuration + +VlanManager loads all VLAN termination entries from PSM at start-up +(`VlanTerminationInitialize()` in `vlan_internal.c`). Each entry describes one +virtual interface layered on top of an Ethernet base interface. + +### 1.1 Per-entry PSM keys + +All keys are indexed by the 1-based instance number `%d`. + +| PSM key | Type | Description | +|---------|------|-------------| +| `dmsb.vlanmanager.ifcount` | int | Total number of VLAN termination entries | +| `dmsb.vlanmanager.%d.VlanEnable` | `TRUE`/`FALSE` | Enable the entry at boot | +| `dmsb.vlanmanager.%d.alias` | string | Alias / label (e.g. `DATA`, `VOICE`); also the parent ethlink name | +| `dmsb.vlanmanager.%d.name` | string | Resulting interface name (e.g. `erouter0`, `nett\|brwan`) | +| `dmsb.vlanmanager.%d.lowerlayers` | string | TR-181 path to the parent EthLink (`Device.X_RDK_Ethernet.Link.%d`) | +| `dmsb.vlanmanager.%d.vlanid` | int | 802.1Q VLAN id. `> 0` = tagged, `0` = tag-0, `< 0` = untagged | +| `dmsb.vlanmanager.%d.tpid` | uint | Tag protocol id (0x8100 / 0x88a8) | +| `dmsb.vlanmanager.%d.untaggedvlantype` | uint | **New.** Selects how an untagged (`vlanid <= 0`) interface is realised. See [§2](#2-interface-type-selection). | +| `dmsb.vlanmanager.%d.baseinterface` | string | Physical base interface (e.g. `eth0`, `dsl0`) | +| `dmsb.vlanmanager.%d.path` | string | TR-181 path used to report VlanStatus back to WanManager | + +> `untaggedvlantype` is only consulted for **untagged** entries. For tagged +> entries (`vlanid > 0`) it is forced to `Tagged(7)` at load time and never +> read at interface-creation time. + +### 1.2 Country / region PSM (HUB4 only) + +On builds compiled with `_HUB4_PRODUCT_REQ_`, a second, region-indexed PSM +table selects the VLAN id / TPID per router region: + +| PSM key | Description | +|---------|-------------| +| `dmsb.vlanmanager.cfg.count` | Number of regional config rows | +| `dmsb.vlanmanager.cfg.%d.region` | Region string matched against `platform_hal_GetRouterRegion()` | +| `dmsb.vlanmanager.cfg.%d.vlanid` | VLAN id for that region | +| `dmsb.vlanmanager.cfg.%d.tpid` | TPID for that region | + +> **Deprecation note.** This country/region `dmsb.vlanmanager.cfg.*` table is a +> stop-gap that hard-codes the VLAN id per country in PSM. It **can and should +> be removed** once the WAN VLAN is determined dynamically — i.e. after moving +> to *VLAN discovery* (auto-detect the operator VLAN on the link) or to a +> *boot-time configuration* source (cloud/ACS/JSON). At that point VlanManager +> should receive the resolved `vlanid`/`tpid` directly on the per-entry keys and +> the regional `cfg` branch in `VlanTerminationInitialize()` can be deleted. +> +> `untaggedvlantype` is intentionally **not** loaded from the `cfg` table: the +> HUB4 regional flow always uses `UNTAGGED_SIMPLE_BRIDGE` (brctl) for untagged +> VLANs. + +### 1.3 Example + +A single tagged DATA VLAN (id 100) on `eth0`: + +``` +psmcli set dmsb.vlanmanager.ifcount 1 +psmcli set dmsb.vlanmanager.1.VlanEnable TRUE +psmcli set dmsb.vlanmanager.1.alias DATA +psmcli set dmsb.vlanmanager.1.name nettXvlan +psmcli set dmsb.vlanmanager.1.lowerlayers Device.X_RDK_Ethernet.Link.1 +psmcli set dmsb.vlanmanager.1.vlanid 100 +psmcli set dmsb.vlanmanager.1.tpid 33024 +psmcli set dmsb.vlanmanager.1.baseinterface eth0 +``` + +An untagged WAN over a Linux bridge (default type): + +``` +psmcli set dmsb.vlanmanager.1.vlanid -1 +psmcli set dmsb.vlanmanager.1.untaggedvlantype 6 # UNTAGGED_SIMPLE_BRIDGE +psmcli set dmsb.vlanmanager.1.baseinterface eth0 +``` + +--- + +## 2. Interface type selection + +The `vlanid` value picks the **class** of interface; `untaggedvlantype` refines +the untagged class: + +```mermaid +flowchart TD + A[VLAN entry enabled] --> B{vlanid} + B -->|"> 0"| T[Tagged 802.1Q VLAN] + B -->|"== 0"| Z{untaggedvlantype} + B -->|"< 0"| U{untaggedvlantype} + Z -->|VlanTag0 6| T0[VLAN device id 0] + U -->|Bridge 0| BR[brctl bridge] + U -->|Macvlan 1-5| MV[macvlan device] + T -.tagged path.-> TP[Vlan_CreateTaggedInterface] + T0 -.tagged path.-> TP + BR -.untagged path.-> EP[EthLink_CreateUnTaggedInterface] + MV -.untagged path.-> EP +``` + +### 2.1 The `UntaggedVlanType` parameter + +| Value | Name | Realisation | Kernel command | +|-------|------|-------------|----------------| +| 0 | `MacvlanPrivate` (default) | macvlan, endpoints isolated | `ip link add … type macvlan mode private` | +| 1 | `MacvlanVepa` | macvlan, hairpin via external switch | `… mode vepa` | +| 2 | `MacvlanBridge` | macvlan, local forwarding between endpoints | `… mode bridge` | +| 3 | `MacvlanPassthru` | macvlan, single endpoint owns the lower dev | `… mode passthru` | +| 4 | `MacvlanSource` | macvlan, source-MAC filtered | `… mode source` | +| 5 | `VlanTag0` | 802.1Q VLAN with tag id 0 (priority-tagged) | `ip link add … type vlan id 0` | +| 6 | `Bridge` | Linux bridge, base iface enslaved | `brctl addbr` / `addif` | +| 7 | `Tagged` | conventional tagged VLAN (display only) | set automatically when `vlanid > 0` | + +The MACVLAN modes map 1:1 to the kernel `ip-link(8)` macvlan modes. + +### 2.2 Decision guide + +| Use case | Recommended type | +|----------|------------------| +| Operator delivers WAN on a **tagged** VLAN | tagged (`vlanid > 0`) | +| Priority-tagged frames (VID 0, PCP set) with QoS | `VlanTag0 (5)` | +| Plain untagged WAN, want a bridge you can add more ports to later | `Bridge (6)` | +| Untagged WAN needing its **own MAC** distinct from the base iface, isolated | `MacvlanPrivate (0)` — default | +| Multiple virtual endpoints that must talk to each other locally | `MacvlanBridge (2)` | +| Deployment behind a VEPA-capable switch (hairpin) | `MacvlanVepa (1)` | +| One endpoint that must fully own the base iface (e.g. move its MAC) | `MacvlanPassthru (3)` | +| Restrict to a fixed allow-list of source MACs | `MacvlanSource (4)` | + +Notes: +- Only **tagged** and **VlanTag0** interfaces support 802.1p `egress-qos-map`; + bridge and macvlan types cannot carry per-priority PCP marking (see the + sequence-diagram doc, *Markings*). +- `Bridge (6)` inherits the base interface MAC automatically (kernel sets the + bridge MAC to the lowest enslaved MAC). The macvlan types honour the EthLink + `MACAddrOffSet` to derive a distinct MAC. + +--- + +## 3. Data model parameters + +`Device.Ethernet.VLANTermination.{i}.` exposes, among others: + +| Parameter | Type | Access | Backed by | +|-----------|------|--------|-----------| +| `VLANID` | int | RW | `dmsb.vlanmanager.%d.vlanid` | +| `TPID` | uint | RW | `dmsb.vlanmanager.%d.tpid` | +| `UntaggedVlanType` | uint (mapped) | **RO** | `dmsb.vlanmanager.%d.untaggedvlantype` | +| `X_RDK_BaseInterface` | string | RW | `dmsb.vlanmanager.%d.baseinterface` | + +`UntaggedVlanType` is a **read-only, mapped** enum. It is populated from PSM at +init and is not writable from the data model — change it via PSM and restart, or +via boot-time config once available. + +Mapped string values: +`MacvlanPrivate(0),MacvlanVepa(1),MacvlanBridge(2),MacvlanPassthru(3),MacvlanSource(4),VlanTag0(5),Bridge(6),Tagged(7)` diff --git a/docs/vlan-sequence-diagrams.md b/docs/vlan-sequence-diagrams.md new file mode 100644 index 0000000..60234cf --- /dev/null +++ b/docs/vlan-sequence-diagrams.md @@ -0,0 +1,242 @@ +# VlanManager Components & Sequence Diagrams + +This document describes the runtime components of VlanManager and the +create / delete / marking sequences for tagged and untagged VLAN interfaces. + +- [1. Components](#1-components) +- [2. Trigger & threading model](#2-trigger--threading-model) +- [3. Tagged VLAN create (incl. VLAN tag-0)](#3-tagged-vlan-create-incl-vlan-tag-0) +- [4. Untagged VLAN create (bridge / macvlan)](#4-untagged-vlan-create-bridge--macvlan) +- [5. Delete sequences](#5-delete-sequences) +- [6. Markings (QoS)](#6-markings-qos) + +--- + +## 1. Components + +```mermaid +flowchart LR + subgraph WM[WanManager] + WMK[Marking table
SKBMark / EthPriority / DSCP] + end + subgraph VM[VlanManager] + VDML[VLANTermination DML
vlan_dml.c] + VAPI[VLAN entry logic
vlan_apis.c] + EDML[EthLink DML
ethernet_dml.c] + EAPI[EthLink logic
ethernet_apis.c] + end + K[(Linux kernel
ip / brctl / ifconfig)] + PSM[(PSM)] + + PSM --> VAPI + PSM --> EAPI + VDML --> VAPI + VAPI -->|Vlan_SetEthLink| EDML + EDML --> EAPI + WMK -->|EthLink_AddMarking / GetMarking| EAPI + VAPI -->|popen: ip link / QoS| K + EAPI -->|popen: ip link / brctl / ifconfig| K + EAPI -->|VlanStatus Up/Down| WM +``` + +| Component | File | Responsibility | +|-----------|------|----------------| +| VLANTermination DML | `vlan_dml.c` | TR-181 get/set, spawns worker threads on Enable | +| VLAN entry logic | `vlan_apis.c` | Tagged create/delete, QoS apply, status poll | +| EthLink DML | `ethernet_dml.c` | TR-181 get/set for `Device.X_RDK_Ethernet.Link` | +| EthLink logic | `ethernet_apis.c` | Untagged create/delete, marking table, VlanStatus | +| Command runner | `ethernet_apis.c` `EthLink_RunCmd` / `EXEC_CMD` | `popen()` wrapper that logs the command + all stdout/stderr and the exit code | + +**Key architectural point:** the *config* (type, vlanid) lives on the **VLAN +entry**, but untagged interface *creation* happens in the **EthLink** object. +`Vlan_SetEthLink()` bridges the two — `EthLink_GetEntry()` returns the live +`DML_ETHERNET` struct, so the VLAN thread writes `UnTaggedVlanType` directly onto +it before committing. + +--- + +## 2. Trigger & threading model + +Enabling an entry (from PSM at boot, or a DM `Enable=true`) spawns a detached +worker thread. Tagged and untagged both start the same way but diverge on +`vlanid` / `UnTaggedVlanType`. + +```mermaid +flowchart TD + E[Vlan Enable = true] --> C[Vlan_SetParamBoolValue] + C --> P[pthread_create] + P --> EN[Vlan_Enable thread] + EN --> D{vlanid > 0
OR type == VlanTag0?} + D -->|yes| TAG[Tagged path] + D -->|no| UNT[Untagged path] +``` + +--- + +## 3. Tagged VLAN create (incl. VLAN tag-0) + +Both a normal tagged VLAN (`vlanid > 0`) and VLAN tag-0 (`UntaggedVlanType = +VlanTag0`) take this path — a tag-0 interface is a real 802.1Q device, so +create / QoS / delete are identical. + +```mermaid +sequenceDiagram + participant T as Vlan_Enable (VLAN thread) + participant SE as Vlan_SetEthLink + participant EE as EthLink_Enable + participant CI as Vlan_CreateTaggedInterface + participant QM as Vlan_ApplyQoSMarking + participant K as Kernel + participant WM as WanManager + + T->>SE: enable=TRUE, PriTag=TRUE + SE->>SE: sync UnTaggedVlanType to EthLink struct + SE->>EE: EthLink Enable=TRUE, PriorityTagging=TRUE + EE->>EE: EthLink_AddMarking populate table + Note over EE: PriorityTagging=TRUE so skip untagged create + T->>K: if exists, ip link set down and delete + T->>CI: create + CI->>K: ip link add link base name ifname type vlan id N + CI->>K: ip link set ifname up + CI->>K: ip link set dev ifname address mac + T->>QM: apply QoS + QM->>EE: EthLink_GetMarking alias + QM->>K: ip link set ifname type vlan egress-qos-map SKB colon PCP + T->>K: poll IFF_RUNNING 10x 2s + T->>WM: VlanStatus = Up +``` + +--- + +## 4. Untagged VLAN create (bridge / macvlan) + +For `vlanid < 0` with a bridge or macvlan type, creation is delegated to the +EthLink object. `Vlan_SetEthLink` sets `PriorityTagging=FALSE`, which makes +`EthLink_Enable` run the untagged creation switch. + +```mermaid +sequenceDiagram + participant T as Vlan_Enable (VLAN thread) + participant SE as Vlan_SetEthLink + participant EE as EthLink_Enable + participant CU as EthLink_CreateUnTaggedInterface + participant K as Kernel + participant WM as WanManager + + T->>SE: enable=TRUE, PriTag=FALSE + SE->>SE: sync UnTaggedVlanType to live EthLink struct + SE->>EE: EthLink Enable=TRUE, PriorityTagging=FALSE + EE->>EE: EthLink_AddMarking + EE->>CU: create untagged + alt UNTAGGED_SIMPLE_BRIDGE default + CU->>K: brctl addbr ifname + CU->>K: brctl addif ifname base + CU->>K: ifconfig ifname up + else UNTAGGED_MACVLAN private/vepa/bridge/passthru/source + CU->>K: ip link add link base name ifname address mac type macvlan mode m + CU->>K: ip link set ifname allmulticast multicast on, mtu 1500, up + end + EE->>K: poll IFF_RUNNING 10x 2s + EE->>WM: VlanStatus = Up + T->>WM: VlanStatus = Up untagged status re-check +``` + +--- + +## 5. Delete sequences + +Delete mirrors create. The `vlanid > 0 || type == VlanTag0` test again routes +tagged/tag-0 to the VLAN thread and bridge/macvlan to the EthLink object. + +```mermaid +sequenceDiagram + participant T as Vlan_Disable (VLAN thread) + participant SE as Vlan_SetEthLink + participant ED as EthLink_Disable + participant DU as EthLink_DeleteUnTaggedInterface + participant K as Kernel + participant WM as WanManager + + T->>SE: enable=FALSE + SE->>ED: EthLink Enable=FALSE + alt tagged OR VlanTag0 + Note over T: handled in VLAN thread + T->>K: ip link set ifname down + T->>K: ip link delete ifname + else untagged bridge/macvlan + ED->>DU: delete untagged + alt UNTAGGED_SIMPLE_BRIDGE + DU->>K: brctl delif ifname base + DU->>K: ifconfig ifname down + DU->>K: brctl delbr ifname + else UNTAGGED_MACVLAN modes + DU->>K: ip link set ifname down + DU->>K: ip link delete ifname + end + end + T->>WM: VlanStatus = Down +``` + +> Interfaces are always brought **down before delete** (`ip link set … down` +> then `ip link delete …`, or `ifconfig … down` then `brctl delbr …`) so the +> kernel does not reject removal of a running device. + +--- + +## 6. Markings (QoS) + +WanManager owns the marking table; VlanManager only *reads* it and applies the +result to tagged / tag-0 interfaces via the kernel VLAN `egress-qos-map`. + +### 6.1 Data flow + +```mermaid +flowchart LR + subgraph WanManager + DM["Device.X_RDK_WanManager…Marking.{i}
Alias / SKBPort / SKBMark / EthernetPriorityMark"] + end + AM[EthLink_AddMarking] -->|CCSP get| DM + AM --> TBL[DML_ETHERNET.pstDataModelMarking[]] + GM[EthLink_GetMarking] --> TBL + GM --> CFG["vlan_configuration_t.skb_config[]
(vlan_skb_config_t)"] + CFG --> QOS[egress-qos-map] + QOS --> K["ip link set <if> type vlan
egress-qos-map SKBMark:EthPriority"] +``` + +### 6.2 `vlan_skb_config_t` fields + +| Field | Meaning | Used in egress-qos-map? | +|-------|---------|--------------------------| +| `alias` | Label only (`DATA` / `VOICE`) | No — no kernel equivalent | +| `skbMark` | Kernel skb priority (fwmark class) | **Yes** — left side of the map | +| `skbEthPriorityMark` | Outgoing 802.1p PCP (0–7) | **Yes** — right side of the map | +| `skbPort` | Port-based classification | No — would require `tc filter` rules | + +The applied command is: + +``` +ip link set type vlan egress-qos-map : +``` + +### 6.3 Which types get QoS + +| Interface type | egress-qos-map | Reason | +|----------------|:--------------:|--------| +| Tagged VLAN | ✅ | 802.1Q device carries a PCP field | +| VlanTag0 | ✅ | also a 802.1Q device (priority-tagged) | +| Bridge | ❌ | not a VLAN device | +| Macvlan (all modes) | ❌ | no 802.1Q tag at this layer | + +For bridge / macvlan untagged types there is no L2 PCP to set; QoS for those is +handled elsewhere in the stack (WanManager L2/L3 marking framework). + +### 6.4 When QoS is (re)applied + +```mermaid +flowchart TD + A[Tagged/Tag0 create] --> Q1[Vlan_ApplyQoSMarking] + R[EthLink refresh
X_RDK_Refresh=true] --> Q2[EthLink_TriggerVlanRefresh] + Q2 --> Q3[EthLink_SetEgressQoSMap] + Q1 --> MAP[egress-qos-map applied] + Q3 --> MAP +``` diff --git a/source/TR-181/include/ethernet_apis.h b/source/TR-181/include/ethernet_apis.h index cfd56b8..b0e71bf 100644 --- a/source/TR-181/include/ethernet_apis.h +++ b/source/TR-181/include/ethernet_apis.h @@ -34,7 +34,7 @@ #ifndef _ETHERNET_APIS_H #define _ETHERNET_APIS_H - +#include #include "vlan_mgr_apis.h" #include "ssp_global.h" #include "vlan_eth_hal.h" @@ -87,6 +87,32 @@ typedef enum { ETH_IF_ERROR }ethernet_link_status_e; +/* + * Untagged VLAN interface type. + * Selects how VlanManager realises an untagged (VLANID <= 0) virtual + * interface on top of the base interface. + * + * Values map to the DM parameter UntaggedVlanType (uint32, mapped to string): + * 0=MacvlanPrivate, 1=MacvlanVepa, 2=MacvlanBridge, 3=MacvlanPassthru, + * 4=MacvlanSource, 5=VlanTag0, 6=Bridge, 7=TaggedVlan + * + * MACVLAN modes correspond exactly to kernel ip-link(8) macvlan modes. + * UNTAGGED_MACVLAN_PRIVATE (0) is the default — an absent PSM key returns 0. + */ +typedef enum +_UNTAGGED_VLAN_TYPE +{ + UNTAGGED_MACVLAN_PRIVATE = 0, /* Default: macvlan mode private */ + UNTAGGED_MACVLAN_VEPA = 1, /* macvlan mode vepa */ + UNTAGGED_MACVLAN_BRIDGE = 2, /* macvlan mode bridge */ + UNTAGGED_MACVLAN_PASSTHRU = 3, /* macvlan mode passthru */ + UNTAGGED_MACVLAN_SOURCE = 4, /* macvlan mode source */ + UNTAGGED_VLAN_TAG_0 = 5, /* 802.1Q VLAN tag id 0 */ + UNTAGGED_SIMPLE_BRIDGE = 6, /* Linux bridge via brctl */ + TAGGED_VLAN = 7 /* Tagged VLAN (VLANID > 0). DML path only. */ +} +untagged_vlan_type_t; + typedef struct _COSA_DML_MARKING { @@ -111,8 +137,9 @@ _DML_ETHERNET CHAR LowerLayers[1024]; CHAR Path[1024]; CHAR MACAddress[18]; - LONG MACAddrOffSet; // Changed to LONG to support negative offsets + INT MACAddrOffSet; /* Signed offset added to PAM base MAC. INT matches GetParamIntValue handler. */ BOOLEAN PriorityTagging; + untagged_vlan_type_t UnTaggedVlanType; // Untagged VLAN realisation type (bridge/macvlan/tag0) UINT NumberofMarkingEntries; PCOSA_DML_MARKING pstDataModelMarking; } @@ -232,5 +259,9 @@ void* EthLink_RefreshHandleThread(void *Arg); ANSC_STATUS EthLink_GetMacAddr( PDML_ETHERNET pEntry ); ANSC_STATUS EthLink_SendVirtualIfaceVlanStatus(char *path, char *vlanStatus); ANSC_STATUS DmlEthGetParamValues(char *pComponent, char *pBus, char *pParamName, char *pReturnVal); -ANSC_STATUS EthLink_GetMarking(char *ifname, vlan_configuration_t *pVlanCfg); +ANSC_STATUS EthLink_GetMarking(PDML_ETHERNET pEntry, vlan_configuration_t *pVlanCfg); + +int EthLink_RunCmd(const char *caller, int line, const char *fmt, ...); +#define EXEC_CMD(fmt, ...) EthLink_RunCmd(__FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) + #endif diff --git a/source/TR-181/include/vlan_apis.h b/source/TR-181/include/vlan_apis.h index 9753c1b..df29ee0 100644 --- a/source/TR-181/include/vlan_apis.h +++ b/source/TR-181/include/vlan_apis.h @@ -38,6 +38,7 @@ #include "vlan_mgr_apis.h" #include "ssp_global.h" #include "secure_wrapper.h" +#include "ethernet_apis.h" /* * Telemetry Markers */ #define VLAN_MARKER_VLAN_IF_CREATE "RDKB_VLAN_CREATE" @@ -50,6 +51,7 @@ #define PSM_VLANMANAGER_LOWERLAYERS "dmsb.vlanmanager.%d.lowerlayers" #define PSM_VLANMANAGER_VLANID "dmsb.vlanmanager.%d.vlanid" #define PSM_VLANMANAGER_TPID "dmsb.vlanmanager.%d.tpid" +#define PSM_VLANMANAGER_UNTAGGEDVLANTYPE "dmsb.vlanmanager.%d.untaggedvlantype" #define PSM_VLANMANAGER_BASEINTERFACE "dmsb.vlanmanager.%d.baseinterface" #define PSM_VLANMANAGER_PATH "dmsb.vlanmanager.%d.path" @@ -88,6 +90,7 @@ _DML_VLAN CHAR BaseInterface[64]; INT VLANId; UINT TPId; + INT UnTaggedVlanType; /* untagged_vlan_type_t: how an untagged (VLANID<=0) iface is created */ CHAR Path[1024]; } DML_VLAN, *PDML_VLAN; @@ -96,6 +99,7 @@ static inline void DML_VLAN_INIT(PDML_VLAN pVlan) { pVlan->Enable = FALSE; pVlan->Status = VLAN_IF_DOWN; + pVlan->UnTaggedVlanType = UNTAGGED_MACVLAN_PRIVATE; /* default */ } /************************************* diff --git a/source/TR-181/middle_layer_src/ethernet_apis.c b/source/TR-181/middle_layer_src/ethernet_apis.c index 0ec359c..a9ccbde 100644 --- a/source/TR-181/middle_layer_src/ethernet_apis.c +++ b/source/TR-181/middle_layer_src/ethernet_apis.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include #include @@ -96,6 +98,56 @@ extern ANSC_HANDLE g_MessageBusHandle; int sysevent_fd = -1; token_t sysevent_token; +int EthLink_RunCmd(const char *caller, int line, const char *fmt, ...) +{ + char cmd[512] = {0}; + char redir[544] = {0}; + va_list args; + FILE *fp; + char buf[256] = {0}; + int status; + + va_start(args, fmt); + vsnprintf(cmd, sizeof(cmd) - 1, fmt, args); + va_end(args); + + CcspTraceInfo(("%s-%d: exec: %s\n", caller, line, cmd)); + + snprintf(redir, sizeof(redir) - 1, "%s 2>&1", cmd); + fp = popen(redir, "r"); + if (fp == NULL) + { + CcspTraceError(("%s-%d: popen failed for: %s\n", caller, line, cmd)); + return -1; + } + + while (fgets(buf, sizeof(buf), fp) != NULL) + { + size_t len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + if (buf[0] != '\0') + CcspTraceInfo(("%s-%d: [output] %s\n", caller, line, buf)); + } + + status = pclose(fp); + if (status == -1) + { + CcspTraceError(("%s-%d: pclose failed: %s\n", caller, line, cmd)); + return -1; + } + if (WIFEXITED(status)) + { + int rc = WEXITSTATUS(status); + if (rc != 0) + CcspTraceError(("%s-%d: failed (rc=%d): %s\n", caller, line, rc, cmd)); + return rc; + } + if (WIFSIGNALED(status)) + CcspTraceError(("%s-%d: killed by signal %d: %s\n", caller, line, WTERMSIG(status), cmd)); + return -1; +} + static ANSC_STATUS DmlEthSetParamValues(const char *pComponent, char *pBus, char *pParamName, char *pParamVal, enum dataType_e type, unsigned int bCommitFlag); static ANSC_STATUS DmlEthGetParamNames(char *pComponent, char *pBus, char *pParamName, char a2cReturnVal[][256], int *pReturnSize); static int EthLink_SyseventInit( void ); @@ -765,80 +817,103 @@ static ANSC_STATUS EthLink_AddMarking(PDML_ETHERNET pEntry) return ANSC_STATUS_SUCCESS; } -ANSC_STATUS EthLink_GetMarking(char *ifname, vlan_configuration_t *pVlanCfg) +ANSC_STATUS EthLink_GetMarking(PDML_ETHERNET pEntry, vlan_configuration_t *pVlanCfg) { - INT iLoopCount = 0; - BOOL Found = FALSE; ANSC_STATUS returnStatus = ANSC_STATUS_FAILURE; - if ((ifname == NULL) || (pVlanCfg == NULL)) + if ((pEntry == NULL) || (pVlanCfg == NULL)) { CcspTraceError(("%s Invalid Memory\n", __FUNCTION__)); return ANSC_STATUS_FAILURE; } - PDATAMODEL_ETHERNET pMyObject = (PDATAMODEL_ETHERNET)g_pBEManager->hEth; - PDML_ETHERNET p_EthLink = NULL; - - if (pMyObject->ulEthlinkInstanceNumber > 0) + if (pEntry != NULL) { - for(iLoopCount = 0; iLoopCount < pMyObject->ulEthlinkInstanceNumber; iLoopCount++) + //Vlan Marking Info + CcspTraceInfo(("%s-%d: NumberofMarkingEntries=%d \n", __FUNCTION__, __LINE__, pEntry->NumberofMarkingEntries)); + if (pEntry->NumberofMarkingEntries > 0) { - p_EthLink = (PDML_ETHERNET)&(pMyObject->EthLink[iLoopCount]); - if (p_EthLink != NULL) + //allocate memory to vlan_skb_config_t, free it once used. + pVlanCfg->skbMarkingNumOfEntries = pEntry->NumberofMarkingEntries; + pVlanCfg->skb_config = (vlan_skb_config_t*)malloc( pEntry->NumberofMarkingEntries * sizeof(vlan_skb_config_t) ); + + if( NULL == pVlanCfg->skb_config ) { - if (strncmp(p_EthLink->Alias, ifname, strlen(ifname)) == 0) - { - Found = TRUE; - break; - } + CcspTraceError(("%s - %d : Invalid SKB priority\n", __FUNCTION__, __LINE__)); + return ANSC_STATUS_FAILURE; } - } - if (Found && (p_EthLink != NULL)) - { - //Vlan Marking Info - CcspTraceInfo(("%s-%d: NumberofMarkingEntries=%d \n", __FUNCTION__, __LINE__, p_EthLink->NumberofMarkingEntries)); - if (p_EthLink->NumberofMarkingEntries > 0) - { - //allocate memory to vlan_skb_config_t, free it once used. - pVlanCfg->skbMarkingNumOfEntries = p_EthLink->NumberofMarkingEntries; - pVlanCfg->skb_config = (vlan_skb_config_t*)malloc( p_EthLink->NumberofMarkingEntries * sizeof(vlan_skb_config_t) ); - if( NULL == pVlanCfg->skb_config ) + for(int i = 0; i < pEntry->NumberofMarkingEntries; i++) + { + PCOSA_DML_MARKING pDataModelMarking = (PCOSA_DML_MARKING)&(pEntry->pstDataModelMarking[i]); + if ((pDataModelMarking != NULL) && (pVlanCfg->skb_config != NULL)) { - CcspTraceError(("%s - %d : Invalid SKB priority\n", __FUNCTION__, __LINE__)); - return ANSC_STATUS_FAILURE; + strncpy(pVlanCfg->skb_config[i].alias, pDataModelMarking->Alias, sizeof(pVlanCfg->skb_config[i].alias) - 1); + pVlanCfg->skb_config[i].skbPort = pDataModelMarking->SKBPort; + pVlanCfg->skb_config[i].skbMark = pDataModelMarking->SKBMark; + pVlanCfg->skb_config[i].skbEthPriorityMark = pDataModelMarking->EthernetPriorityMark; + CcspTraceInfo(("%s-%d: Ins[%d] Alias[%s] SKBPort[%u] SKBMark[%u] EthernetPriorityMark[%d]\n", __FUNCTION__, + __LINE__, (i + 1), pVlanCfg->skb_config[i].alias, pVlanCfg->skb_config[i].skbPort, + pVlanCfg->skb_config[i].skbMark, pVlanCfg->skb_config[i].skbEthPriorityMark )); } - - for(int i = 0; i < p_EthLink->NumberofMarkingEntries; i++) + else { - PCOSA_DML_MARKING pDataModelMarking = (PCOSA_DML_MARKING)&(p_EthLink->pstDataModelMarking[i]); - if ((pDataModelMarking != NULL) && (pVlanCfg->skb_config != NULL)) - { - strncpy(pVlanCfg->skb_config[i].alias, pDataModelMarking->Alias, sizeof(pVlanCfg->skb_config[i].alias) - 1); - pVlanCfg->skb_config[i].skbPort = pDataModelMarking->SKBPort; - pVlanCfg->skb_config[i].skbMark = pDataModelMarking->SKBMark; - pVlanCfg->skb_config[i].skbEthPriorityMark = pDataModelMarking->EthernetPriorityMark; - CcspTraceInfo(("%s-%d: Ins[%d] Alias[%s] SKBPort[%u] SKBMark[%u] EthernetPriorityMark[%d]\n", __FUNCTION__, - __LINE__, (i + 1), pVlanCfg->skb_config[i].alias, pVlanCfg->skb_config[i].skbPort, - pVlanCfg->skb_config[i].skbMark, pVlanCfg->skb_config[i].skbEthPriorityMark )); - } - else - { - CcspTraceError(("%s-%d: pDataModelMarking Or pVlanCfg->skb_config are Null \n", __FUNCTION__, __LINE__)); - free(pVlanCfg->skb_config); - pVlanCfg->skb_config = NULL; - return ANSC_STATUS_FAILURE; - } + CcspTraceError(("%s-%d: pDataModelMarking Or pVlanCfg->skb_config are Null \n", __FUNCTION__, __LINE__)); + free(pVlanCfg->skb_config); + pVlanCfg->skb_config = NULL; + return ANSC_STATUS_FAILURE; } } - returnStatus = ANSC_STATUS_SUCCESS; } + returnStatus = ANSC_STATUS_SUCCESS; } return returnStatus; } +#if !defined(VLAN_MANAGER_HAL_ENABLED) +static const char * const macvlan_modes[] = { + [UNTAGGED_MACVLAN_PRIVATE] = "private", + [UNTAGGED_MACVLAN_VEPA] = "vepa", + [UNTAGGED_MACVLAN_BRIDGE] = "bridge", + [UNTAGGED_MACVLAN_PASSTHRU] = "passthru", + [UNTAGGED_MACVLAN_SOURCE] = "source", +}; + +static void EthLink_IpLinkCreateMacvlan(const char *base, const char *name, untagged_vlan_type_t type) +{ + const char *mode = ((unsigned)type < sizeof(macvlan_modes)/sizeof(macvlan_modes[0]) && macvlan_modes[type]) + ? macvlan_modes[type] : "private"; + EXEC_CMD("ip link show %s > /dev/null 2>&1 && (ip link set %s down; ip link delete %s)", + name, name, name); + EXEC_CMD("ip link add link %s name %s type macvlan mode %s", base, name, mode); + EXEC_CMD("ip link set %s allmulticast on", name); + EXEC_CMD("ip link set %s multicast on", name); + EXEC_CMD("ip link set %s mtu 1500", name); +} + +static void EthLink_IpLinkCreateBridge(const char *base, const char *name) +{ + EXEC_CMD("ip link show %s > /dev/null 2>&1 || brctl addbr %s", name, name); + if ((strcmp(base, name) != 0) && (base[0] != '\0')) + EXEC_CMD("brctl addif %s %s", name, base); +} + +static void EthLink_IpLinkDeleteMacvlan(const char *name) +{ + EXEC_CMD("ip link set %s down", name); + EXEC_CMD("ip link delete %s", name); +} + +static void EthLink_IpLinkDeleteBridge(const char *base, const char *name) +{ + if ((strcmp(base, name) != 0) && (base[0] != '\0')) + EXEC_CMD("brctl delif %s %s", name, base); + EXEC_CMD("ifconfig %s down", name); + EXEC_CMD("brctl delbr %s", name); +} +#endif /* !VLAN_MANAGER_HAL_ENABLED */ + static ANSC_STATUS EthLink_CreateUnTaggedInterface(PDML_ETHERNET pEntry) { ANSC_STATUS returnStatus = ANSC_STATUS_SUCCESS; @@ -858,7 +933,7 @@ static ANSC_STATUS EthLink_CreateUnTaggedInterface(PDML_ETHERNET pEntry) VlanCfg.VLANId = DEFAULT_VLAN_ID; VlanCfg.TPId = 0; - if (EthLink_GetMarking(pEntry->Alias, &VlanCfg) == ANSC_STATUS_FAILURE) + if (EthLink_GetMarking(pEntry, &VlanCfg) == ANSC_STATUS_FAILURE) { CcspTraceError(("%s Failed to Get Marking, so Can't Create Vlan Interface(%s) \n", __FUNCTION__, pEntry->Alias)); return ANSC_STATUS_FAILURE; @@ -871,48 +946,53 @@ static ANSC_STATUS EthLink_CreateUnTaggedInterface(PDML_ETHERNET pEntry) */ EthLink_CreateBridgeInterface(TRUE); #else - // Create untagged interface using MACVLAN for truly untagged traffic - CcspTraceInfo(("%s-%d: Creating MACVLAN untagged interface %s on base interface %s with MAC offset %ld\n", - __FUNCTION__, __LINE__, pEntry->Name, pEntry->BaseInterface, pEntry->MACAddrOffSet)); - - // Check if interface already exists (as MACVLAN or bridge) and delete it - CcspTraceInfo(("%s-%d: Checking if interface %s already exists\n", __FUNCTION__, __LINE__, pEntry->Name)); - v_secure_system("ip link show %s > /dev/null 2>&1 && (ip link set %s down; ip link delete %s)", - pEntry->Name, pEntry->Name, pEntry->Name); - - // Get MAC address with offset applied - if (ANSC_STATUS_SUCCESS != EthLink_GetMacAddr(pEntry)) + CcspTraceInfo(("%s-%d: Creating untagged interface %s on base interface %s (type=%d, MAC offset=%d)\n", + __FUNCTION__, __LINE__, pEntry->Name, pEntry->BaseInterface, + pEntry->UnTaggedVlanType, pEntry->MACAddrOffSet)); + + switch (pEntry->UnTaggedVlanType) + { + case TAGGED_VLAN: + /* tagged path handled by vlan_apis.c — nothing to do here */ + break; + case UNTAGGED_MACVLAN_PRIVATE: + case UNTAGGED_MACVLAN_VEPA: + case UNTAGGED_MACVLAN_BRIDGE: + case UNTAGGED_MACVLAN_PASSTHRU: + case UNTAGGED_MACVLAN_SOURCE: + { + EthLink_IpLinkCreateMacvlan(pEntry->BaseInterface, pEntry->Name, pEntry->UnTaggedVlanType); + break; + } + case UNTAGGED_SIMPLE_BRIDGE: + default: + { + EthLink_IpLinkCreateBridge(pEntry->BaseInterface, pEntry->Name); + CcspTraceInfo(("%s-%d: Created bridge interface %s\n", + __FUNCTION__, __LINE__, pEntry->Name)); + break; + } + } + + /* Apply MAC address for all interface types. + * EthLink_GetMacAddr computes PAM base + MACAddrOffSet and stores the + * result in pEntry->MACAddress. Set it before bringing the interface up + * so there is no window with an incorrect MAC. */ + if (ANSC_STATUS_SUCCESS == EthLink_GetMacAddr(pEntry)) { - CcspTraceError(("%s-%d: Failed to get MAC address, creating MACVLAN without custom MAC\n", __FUNCTION__, __LINE__)); - // Create MACVLAN without setting custom MAC - kernel will assign one - v_secure_system("ip link add link %s name %s type macvlan mode private", - pEntry->BaseInterface, pEntry->Name); + EXEC_CMD("ip link set dev %s address %s", pEntry->Name, pEntry->MACAddress); + CcspTraceInfo(("%s-%d: MAC %s (offset %d) applied to %s\n", + __FUNCTION__, __LINE__, + pEntry->MACAddress, pEntry->MACAddrOffSet, pEntry->Name)); } else { - CcspTraceInfo(("%s-%d: Using MAC address: %s (offset: %ld)\n", - __FUNCTION__, __LINE__, pEntry->MACAddress, pEntry->MACAddrOffSet)); - - // Create MACVLAN interface with custom MAC - v_secure_system("ip link add link %s name %s address %s type macvlan mode private", - pEntry->BaseInterface, pEntry->Name, pEntry->MACAddress); + CcspTraceInfo(("%s-%d: EthLink_GetMacAddr failed, using default MAC for %s\n", + __FUNCTION__, __LINE__, pEntry->Name)); } - - // Set the allmulticast and multicast on for MACVLAN interface - CcspTraceInfo(("%s-%d: Setting allmulticast amd multicast on for MACVLAN interface %s\n", - __FUNCTION__, __LINE__, pEntry->Name)); - v_secure_system("ip link set %s allmulticast on", pEntry->Name); - v_secure_system("ip link set %s multicast on", pEntry->Name); - // Set MTU to default 1500 - CcspTraceInfo(("%s-%d: Setting MTU to 1500 for MACVLAN interface %s\n", - __FUNCTION__, __LINE__, pEntry->Name)); - v_secure_system("ip link set %s mtu 1500", pEntry->Name); - - v_secure_system("ip link set %s up", pEntry->Name); - - CcspTraceInfo(("%s-%d: Successfully created MACVLAN untagged interface %s\n", - __FUNCTION__, __LINE__, pEntry->Name)); + /* Bring up — common for all interface types. */ + EXEC_CMD("ip link set %s up", pEntry->Name); #endif //Free VlanCfg skb_config memory @@ -936,12 +1016,30 @@ static ANSC_STATUS EthLink_DeleteUnTaggedInterface(PDML_ETHERNET pEntry) return ANSC_STATUS_FAILURE; } - CcspTraceInfo(("%s-%d: Deleting untagged VLAN interface %s\n", - __FUNCTION__, __LINE__, pEntry->Name)); - - v_secure_system("ip link set %s down", pEntry->Name); - v_secure_system("ip link delete %s", pEntry->Name); - + CcspTraceInfo(("%s-%d: Deleting untagged VLAN interface %s (type=%d)\n", + __FUNCTION__, __LINE__, pEntry->Name, pEntry->UnTaggedVlanType)); + + switch (pEntry->UnTaggedVlanType) + { + case TAGGED_VLAN: + break; + case UNTAGGED_MACVLAN_PRIVATE: + case UNTAGGED_MACVLAN_VEPA: + case UNTAGGED_MACVLAN_BRIDGE: + case UNTAGGED_MACVLAN_PASSTHRU: + case UNTAGGED_MACVLAN_SOURCE: + { + EthLink_IpLinkDeleteMacvlan(pEntry->Name); + break; + } + case UNTAGGED_SIMPLE_BRIDGE: + default: + { + EthLink_IpLinkDeleteBridge(pEntry->BaseInterface, pEntry->Name); + break; + } + } + CcspTraceInfo(("%s-%d: Successfully deleted untagged VLAN interface %s\n", __FUNCTION__, __LINE__, pEntry->Name)); @@ -1006,7 +1104,7 @@ static ANSC_STATUS EthLink_TriggerVlanRefresh(PDML_ETHERNET pEntry ) /* TODO: Retry add making if DM gert fails. * Currently we continue to create VLAN link, if Marking get fails to avoid WAN failure. */ - if (EthLink_GetMarking(pEntry->Alias, &VlanCfg) == ANSC_STATUS_FAILURE) + if (EthLink_GetMarking(pEntry, &VlanCfg) == ANSC_STATUS_FAILURE) { CcspTraceError(("%s Failed to Get Marking, Creating Vlan Interface(%s) without marking \n", __FUNCTION__, pEntry->Alias)); } diff --git a/source/TR-181/middle_layer_src/vlan_apis.c b/source/TR-181/middle_layer_src/vlan_apis.c index 54e0c0a..40587b8 100644 --- a/source/TR-181/middle_layer_src/vlan_apis.c +++ b/source/TR-181/middle_layer_src/vlan_apis.c @@ -128,7 +128,8 @@ static ANSC_STATUS Vlan_DeleteInterface(PDML_VLAN p_Vlan) return ANSC_STATUS_FAILURE; } - v_secure_system("ip link delete %s", p_Vlan->Name); + EXEC_CMD("ip link set %s down", p_Vlan->Name); + EXEC_CMD("ip link delete %s", p_Vlan->Name); return ANSC_STATUS_SUCCESS; } @@ -242,6 +243,12 @@ static ANSC_STATUS Vlan_SetEthLink(PDML_VLAN pEntry, BOOL enable, BOOL PriTag) return ANSC_STATUS_FAILURE; } + /* Sync VLAN type to EthLink. TAGGED_VLAN(7) is harmless — EthLink_Enable + * skips untagged creation when PriorityTagging=TRUE. */ + ((PDML_ETHERNET)pNewEntry)->UnTaggedVlanType = (untagged_vlan_type_t)pEntry->UnTaggedVlanType; + CcspTraceInfo(("%s-%d: VLAN type=%d synced to EthLink for %s\n", + __FUNCTION__, __LINE__, pEntry->UnTaggedVlanType, pEntry->Name)); + //Set PriorityTagging. if (enable == TRUE) { @@ -360,18 +367,16 @@ void * Vlan_Disable(void *Arg) pthread_detach(pthread_self()); pthread_mutex_lock(&vlan_access_mutex); - - if (pEntry->VLANId != -1) + //Set EthLink to False. it will take care UnTagged Created Vlan Interface + + BOOL priTag = (pEntry->VLANId > 0 || pEntry->UnTaggedVlanType == UNTAGGED_VLAN_TAG_0) ? TRUE : FALSE; + if (Vlan_SetEthLink(pEntry, FALSE, priTag) == ANSC_STATUS_FAILURE) { - //Set EthLink to False. it will take care UnTagged Created Vlan Interface - if (Vlan_SetEthLink(pEntry, FALSE, FALSE) == ANSC_STATUS_FAILURE) - { - CcspTraceError(("%s-%d: Failed to Disable EthLink\n", __FUNCTION__, __LINE__)); - } + CcspTraceError(("%s-%d: Failed to Disable EthLink\n", __FUNCTION__, __LINE__)); } //Delete Created Tagged Vlan Interface - if (pEntry->VLANId > 0) + if (pEntry->VLANId > 0 || pEntry->UnTaggedVlanType == UNTAGGED_VLAN_TAG_0) { #ifdef FEATURE_MAPT char wan_ifname[BUFLEN_64] = {0}; @@ -406,19 +411,6 @@ void * Vlan_Disable(void *Arg) Vlan_DeleteInterface(pEntry); #endif } - else if (pEntry->VLANId == -1) - { - /* If the VLANID = -1, the VLAN is a bridge, delete the bridge and delete the interface from the bridge */ - if (strcmp(pEntry->BaseInterface, pEntry->Name) != 0) - { - if (pEntry->BaseInterface[0] != '\0') - { - v_secure_system("brctl delif %s %s", pEntry->Name, pEntry->BaseInterface); - } - v_secure_system("ifconfig %s down", pEntry->Name); - v_secure_system("brctl delbr %s", pEntry->Name); - } - } pEntry->Status = VLAN_IF_DOWN; EthLink_SendVirtualIfaceVlanStatus(pEntry->Path, "Down"); @@ -525,6 +517,24 @@ static ANSC_STATUS Vlan_SetMacAddr( PDML_VLAN pEntry ) } #endif + +#ifdef TC_QOS_CLASSIFY +/* Attach clsact egress filters on the VLAN interface. + * fw filters bridge skb->mark -> skb->priority for egress-qos-map, replacing + * iptables CLASSIFY rules. Network control protocols get DATA priority (1). +*/ +static void Vlan_SetTcClassify(const char *iface) +{ + EXEC_CMD("tc qdisc add dev %s clsact", iface); + /* SKBPort-encoded mark -> egress-qos-map priority key via fw filters with mask */ + EXEC_CMD("tc filter add dev %s egress protocol all prio 1 handle 0x100000/0x0ff00000 fw action skbedit priority 1", iface); + EXEC_CMD("tc filter add dev %s egress protocol all prio 1 handle 0x200000/0x0ff00000 fw action skbedit priority 2", iface); + EXEC_CMD("tc filter add dev %s egress protocol all prio 1 handle 0x300000/0x0ff00000 fw action skbedit priority 3", iface); + /* ARP (EtherType 0x0806): match-all gives network control priority via fwmark set in utopia */ + EXEC_CMD("tc filter add dev %s egress protocol arp prio 2 u32 match u32 0 0 action skbedit priority 1", iface); +} +#endif + /********************************************************************** caller: self @@ -566,10 +576,17 @@ static ANSC_STATUS Vlan_CreateTaggedInterface(PDML_VLAN pEntry) VlanCfg.VLANId = pEntry->VLANId; VlanCfg.TPId = pEntry->TPId; - if (EthLink_GetMarking(pEntry->Alias, &VlanCfg) == ANSC_STATUS_FAILURE) { - CcspTraceError(("%s Failed to Get Marking, so Can't Create Vlan Interface(%s) \n", __FUNCTION__, pEntry->Alias)); - return ANSC_STATUS_FAILURE; + INT iEthLinkInstance = -1, EthLinkInstance = -1; + if (strlen(pEntry->LowerLayers) > 0) + sscanf(pEntry->LowerLayers, "Device.X_RDK_Ethernet.Link.%d", &iEthLinkInstance); + PDML_ETHERNET pEthLink = (iEthLinkInstance > 0) ? + (PDML_ETHERNET)EthLink_GetEntry(NULL, (iEthLinkInstance - 1), (PULONG)&EthLinkInstance) : NULL; + if (pEthLink == NULL || EthLink_GetMarking(pEthLink, &VlanCfg) == ANSC_STATUS_FAILURE) + { + CcspTraceError(("%s Failed to Get Marking, so Can't Create Vlan Interface(%s) \n", __FUNCTION__, pEntry->Alias)); + return ANSC_STATUS_FAILURE; + } } vlan_eth_hal_createInterface(&VlanCfg); @@ -587,6 +604,8 @@ static ANSC_STATUS Vlan_CreateTaggedInterface(PDML_VLAN pEntry) static ANSC_STATUS Vlan_CreateTaggedInterface(PDML_VLAN pEntry) { ANSC_STATUS returnStatus = ANSC_STATUS_SUCCESS; + vlan_configuration_t VlanCfg = {0}; + INT i; if (pEntry == NULL) { @@ -594,9 +613,8 @@ static ANSC_STATUS Vlan_CreateTaggedInterface(PDML_VLAN pEntry) return ANSC_STATUS_FAILURE; } - v_secure_system("ip link add link %s name %s type vlan id %u", pEntry->Alias , pEntry->Name, pEntry->VLANId); - - v_secure_system("ip link set %s up", pEntry->Name); + EXEC_CMD("ip link add link %s name %s type vlan id %u", pEntry->BaseInterface, pEntry->Name, pEntry->VLANId); + EXEC_CMD("ip link set %s up", pEntry->Name); if (Vlan_SetMacAddr(pEntry) == ANSC_STATUS_FAILURE) { @@ -604,15 +622,86 @@ static ANSC_STATUS Vlan_CreateTaggedInterface(PDML_VLAN pEntry) return ANSC_STATUS_FAILURE; } + /* Apply 802.1p egress QoS map: maps kernel skb priority (SKBMark) to the + * outgoing VLAN PCP (EthernetPriorityMark) on egress. + * Note: alias is a label only (no kernel equivalent). + * skbPort would need tc-filter rules and is not set here. */ + strncpy(VlanCfg.L3Interface, pEntry->Name, sizeof(VlanCfg.L3Interface) - 1); + VlanCfg.VLANId = pEntry->VLANId; + { + INT iEthLinkInstance = -1, EthLinkInstance = -1; + if (strlen(pEntry->LowerLayers) > 0) + sscanf(pEntry->LowerLayers, "Device.X_RDK_Ethernet.Link.%d", &iEthLinkInstance); + PDML_ETHERNET pEthLink = (iEthLinkInstance > 0) ? + (PDML_ETHERNET)EthLink_GetEntry(NULL, (iEthLinkInstance - 1), (PULONG)&EthLinkInstance) : NULL; + if (pEthLink != NULL) + EthLink_GetMarking(pEthLink, &VlanCfg); + } + if (VlanCfg.skbMarkingNumOfEntries > 0) + { + /* egress-qos-map reads skb->priority; CLASSIFY --set-class in utopia firewall sets priority=SKBPort */ + for (i = 0; i < (INT)VlanCfg.skbMarkingNumOfEntries; i++) + { + CcspTraceInfo(("%s-%d: egress-qos-map %s: SKBPort=%u -> pbit=%d\n", + __FUNCTION__, __LINE__, pEntry->Name, + VlanCfg.skb_config[i].skbPort, + VlanCfg.skb_config[i].skbEthPriorityMark)); + EXEC_CMD("ip link set %s type vlan egress-qos-map %u:%d", + pEntry->Name, + VlanCfg.skb_config[i].skbPort, + VlanCfg.skb_config[i].skbEthPriorityMark); + } + if (VlanCfg.skb_config != NULL) + { + free(VlanCfg.skb_config); + } + } + +#ifdef TC_QOS_CLASSIFY + Vlan_SetTcClassify(pEntry->Name); +#endif + return returnStatus; } #endif +/* Poll interface status (up to 10 retries, 2 s apart) and notify WanManager. */ +static void Vlan_WaitForInterfaceUp(PDML_VLAN pEntry, const char *ifType) +{ + vlan_link_status_e status = VLAN_IF_DOWN; + INT iIterator = 0; + long uptime = 0; + + while (iIterator < 10) + { + if (ANSC_STATUS_FAILURE == Vlan_GetTaggedVlanInterfaceStatus(pEntry->Name, &status)) + { + CcspTraceError(("%s-%d: Failed to get %s Vlan Interface=%s Status\n", + __FUNCTION__, __LINE__, ifType, pEntry->Name)); + } + + if (VLAN_IF_UP == status) + { + EthLink_SendVirtualIfaceVlanStatus(pEntry->Path, "Up"); + CcspTraceInfo(("%s-%d: Successfully updated Vlan Status (%s) for Interface(%s)\n", + __FUNCTION__, __LINE__, ifType, pEntry->Name)); + break; + } + + iIterator++; + sleep(2); + CcspTraceInfo(("%s-%d: %s Interface Status(%d), retry-count=%d\n", + __FUNCTION__, __LINE__, ifType, status, iIterator)); + } + + get_uptime(&uptime); + pEntry->LastChange = uptime; +} + void * Vlan_Enable(void *Arg) { ANSC_STATUS returnStatus = ANSC_STATUS_SUCCESS; - vlan_link_status_e status; - INT iIterator = 0; + vlan_link_status_e status = VLAN_IF_NOTPRESENT; /* safe default: skips deletion on ioctl failure */ PDML_VLAN pEntry = (PDML_VLAN)Arg; if ( NULL == pEntry ) @@ -624,8 +713,12 @@ void * Vlan_Enable(void *Arg) pthread_detach(pthread_self()); pthread_mutex_lock(&vlan_access_mutex); - //Create Vlan Tagged Interface - if(pEntry->VLANId > 0) { + //Create Vlan Tagged or UnTagged Interface + /* UNTAGGED_VLAN_TAG_0 uses the tagged path: it is a 802.1Q VLAN device + * with id 0, so create/QoS/delete all work identically to tagged VLANs. + * PSM must store VLANId=0 for those entries. */ + if (pEntry->VLANId > 0 || pEntry->UnTaggedVlanType == UNTAGGED_VLAN_TAG_0) + { if (Vlan_SetEthLink(pEntry, TRUE, TRUE) == ANSC_STATUS_FAILURE) { CcspTraceError(("%s-%d: Failed to Enable EthLink\n", __FUNCTION__, __LINE__)); @@ -646,8 +739,8 @@ void * Vlan_Enable(void *Arg) } else #else - v_secure_system("ip link set %s down", pEntry->Name); - v_secure_system("ip link delete %s",pEntry->Name); + EXEC_CMD("ip link set %s down", pEntry->Name); + EXEC_CMD("ip link delete %s",pEntry->Name); #endif { CcspTraceInfo(("%s - %s:Successfully deleted VLAN interface %s\n", __FUNCTION__, VLAN_MARKER_VLAN_IF_DELETE, pEntry->Name)); @@ -660,62 +753,7 @@ void * Vlan_Enable(void *Arg) CcspTraceError(("[%s][%d]Failed to create VLAN Tagged interface \n", __FUNCTION__, __LINE__)); } - //Get status of VLAN link - while(iIterator < 10) - { - if (ANSC_STATUS_FAILURE == Vlan_GetTaggedVlanInterfaceStatus(pEntry->Name, &status)) - { - CcspTraceError(("%s-%d: Failed to get Tagged Vlan Interface=%s Status \n", __FUNCTION__, __LINE__, pEntry->Name)); - } - - if (VLAN_IF_UP == status) - { - EthLink_SendVirtualIfaceVlanStatus(pEntry->Path, "Up"); - CcspTraceInfo(("%s-%d: Successfully Updated Vlan Status to WanManager for Interface(%s) \n", __FUNCTION__, __LINE__, pEntry->Name)); - break; - } - - iIterator++; - sleep(2); - CcspTraceInfo(("%s-%d: Interface Status(%d), retry-count=%d \n", __FUNCTION__, __LINE__, status, iIterator)); - } - long uptime = 0; - get_uptime(&uptime); - pEntry->LastChange = uptime; - } - else if (pEntry->VLANId == -1) - { - /* If the VLANID = -1, the VLAN is a bridge, create the bridge and add the interface to the bridge */ - if (strcmp(pEntry->BaseInterface, pEntry->Name) != 0) - { - v_secure_system("ip link show %s > /dev/null 2>&1 || brctl addbr %s", pEntry->Name, pEntry->Name); - v_secure_system("brctl addif %s %s 2>/dev/null", pEntry->Name, pEntry->BaseInterface); - v_secure_system("ifconfig %s up", pEntry->Name); - } - - //Get status of VLAN link - status = VLAN_IF_DOWN; - while(iIterator < 10) - { - if (ANSC_STATUS_FAILURE == Vlan_GetTaggedVlanInterfaceStatus(pEntry->Name, &status)) - { - CcspTraceError(("%s-%d: Failed to get Tagged Vlan Interface=%s Status \n", __FUNCTION__, __LINE__, pEntry->Name)); - } - - if (VLAN_IF_UP == status) - { - EthLink_SendVirtualIfaceVlanStatus(pEntry->Path, "Up"); - CcspTraceInfo(("%s-%d: Successfully Updated Vlan Status to WanManager for Interface(%s) \n", __FUNCTION__, __LINE__, pEntry->Name)); - break; - } - - iIterator++; - sleep(2); - CcspTraceInfo(("%s-%d: Interface Status(%d), retry-count=%d \n", __FUNCTION__, __LINE__, status, iIterator)); - } - long uptime = 0; - get_uptime(&uptime); - pEntry->LastChange = uptime; + Vlan_WaitForInterfaceUp(pEntry, "Tagged"); } else { @@ -724,6 +762,7 @@ void * Vlan_Enable(void *Arg) { CcspTraceError(("%s-%d: Failed to Enable EthLink\n", __FUNCTION__, __LINE__)); } + Vlan_WaitForInterfaceUp(pEntry, "UnTagged"); } pEntry->Status = VLAN_IF_UP; diff --git a/source/TR-181/middle_layer_src/vlan_dml.c b/source/TR-181/middle_layer_src/vlan_dml.c index 49c422d..db65d64 100644 --- a/source/TR-181/middle_layer_src/vlan_dml.c +++ b/source/TR-181/middle_layer_src/vlan_dml.c @@ -360,6 +360,11 @@ Vlan_GetParamUlongValue *puLong = p_Vlan->TPId; return TRUE; } + if (strcmp(ParamName, "UntaggedVlanType") == 0) + { + *puLong = p_Vlan->UnTaggedVlanType; + return TRUE; + } /* CcspTraceWarning(("Unsupported parameter '%s'\n", ParamName)); */ return FALSE; } diff --git a/source/TR-181/middle_layer_src/vlan_internal.c b/source/TR-181/middle_layer_src/vlan_internal.c index 9d9abb9..b851e89 100644 --- a/source/TR-181/middle_layer_src/vlan_internal.c +++ b/source/TR-181/middle_layer_src/vlan_internal.c @@ -35,6 +35,7 @@ #include "vlan_mgr_apis.h" #include "vlan_apis.h" #include "vlan_internal.h" +#include "ethernet_apis.h" #include "plugin_main_apis.h" #include "poam_irepfo_interface.h" #include "sys_definitions.h" @@ -43,11 +44,14 @@ /*TODO *Need to be Reviewed after Unification is finalised. + * Update: The unification is finalised, however to remove the below code, the respective platform have to use VLAN dicovery or boottime VLAN configuration based. on the region to replace this dynamic country based VLAN configuration. */ #define PSM_VLANMANAGER_CFG_COUNT "dmsb.vlanmanager.cfg.count" #define PSM_VLANMANAGER_CFG_REGION "dmsb.vlanmanager.cfg.%d.region" #define PSM_VLANMANAGER_CFG_VLANID "dmsb.vlanmanager.cfg.%d.vlanid" #define PSM_VLANMANAGER_CFG_TPID "dmsb.vlanmanager.cfg.%d.tpid" +/* Note: UntaggedVlanType is not loaded per-cfg for the HUB4 regional flow; + * that flow always uses UNTAGGED_SIMPLE_BRIDGE (brctl) for untagged VLANs. */ extern char g_Subsystem[32]; extern ANSC_HANDLE bus_handle; @@ -278,6 +282,10 @@ static ANSC_STATUS VlanTerminationInitialize( ANSC_HANDLE hThisObject) pVlan[nIndex].TPId = atoi(acPSMValue) ; } + /* Set UntaggedVlanType: tagged VLANs use TAGGED_VLAN (7); untagged VLANs + * default to UNTAGGED_MACVLAN_PRIVATE for the HUB4 regional flow. */ + pVlan[nIndex].UnTaggedVlanType = (pVlan[nIndex].VLANId > 0) ? TAGGED_VLAN : UNTAGGED_MACVLAN_PRIVATE; + /*TODO: *Need to be Removed Path From PSM Once RBUS Support Available in VlanManager and WanManager. */ @@ -377,6 +385,21 @@ static ANSC_STATUS VlanTerminationInitialize( ANSC_HANDLE hThisObject) pVlan[nIndex].TPId = atoi(acPSMValue) ; } + /* Set UntaggedVlanType: tagged VLANs always use TAGGED_VLAN (7). + * For untagged VLANs load the type from PSM; falls back to UNTAGGED_MACVLAN_PRIVATE. */ + if (pVlan[nIndex].VLANId > 0) + { + pVlan[nIndex].UnTaggedVlanType = TAGGED_VLAN; + } + else + { + snprintf( acPSMQuery, sizeof( acPSMQuery ), PSM_VLANMANAGER_UNTAGGEDVLANTYPE, nIndex + 1 ); + if ( CCSP_SUCCESS == DmlVlanGetPSMRecordValue( acPSMQuery, acPSMValue ) ) + { + pVlan[nIndex].UnTaggedVlanType = atoi(acPSMValue) ; + } + } + /* get base interface from psm */ snprintf( acPSMQuery, sizeof( acPSMQuery ), PSM_VLANMANAGER_BASEINTERFACE, nIndex + 1 ); if ( CCSP_SUCCESS == DmlVlanGetPSMRecordValue( acPSMQuery, acPSMValue ) )