Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,29 @@ docker rm -f backend1 backend2 orion-proxy

For detailed Docker configuration options, see [docker/README.md](docker/README.md).

## Examples and Demos

### TLV Listener Filter Demo

Orion includes a comprehensive TLV (Type-Length-Value) listener filter demo compatible with the Kmesh project for service mesh integration. This demo provides end-to-end testing of the TLV filter functionality.

To test the TLV filter:

```bash
cd examples/tlv-filter-demo
./test_tlv_config.sh
```

This demo will:
- Start Orion with TLV filter configuration matching Kmesh format
- Load the TLV listener filter using TypedStruct configuration
- Send actual TLV packets to test the filter functionality
- Extract and verify original destination information from TLV data
- Show debug logs confirming successful TLV processing
- Verify compatibility with Kmesh TLV configuration format


For detailed information, see [examples/tlv-filter-demo/README.md](examples/tlv-filter-demo/README.md).

<!-- ## Contributing -->
<!-- If you're interested in being a contributor and want to get involved in developing Orion Proxy, please see [CONTRIBUTING](CONTRIBUTING.md) for more details on submitting patches and the contribution workflow. -->
Expand Down
9 changes: 8 additions & 1 deletion envoy-data-plane-api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ use glob::glob;
/// std::env::set_var("PROTOC", The Path of Protoc);
fn main() -> std::io::Result<()> {
let descriptor_path = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("proto_descriptor.bin");
let protos: Vec<PathBuf> = glob("data-plane-api/envoy/**/v3/*.proto").unwrap().filter_map(Result::ok).collect();
let mut protos: Vec<PathBuf> = glob("data-plane-api/envoy/**/v3/*.proto").unwrap().filter_map(Result::ok).collect();

let udpa_protos: Vec<PathBuf> = glob("xds/udpa/**/*.proto").unwrap().filter_map(Result::ok).collect();
protos.extend(udpa_protos);

let custom_protos: Vec<PathBuf> = glob("../proto/**/*.proto").unwrap().filter_map(Result::ok).collect();
protos.extend(custom_protos);

let include_paths = [
"data-plane-api/",
Expand All @@ -17,6 +23,7 @@ fn main() -> std::io::Result<()> {
"prometheus-client-model/",
"cel-spec/proto",
"protobuf/src/",
"../proto/",
];

let mut config = prost_build::Config::new();
Expand Down
85 changes: 85 additions & 0 deletions examples/tlv-filter-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# TLV Filter Demo

This demo showcases Orion's TLV (Type-Length-Value) listener filter implementation, compatible with Kmesh configurations.

## Quick Test

Run the automated test script:

```bash
./test_tlv_config.sh
```

This script:
1. Validates Orion configuration loading
2. Tests TLV filter integration
3. Optionally tests end-to-end packet processing (if client is available)

## Configuration

The TLV filter is configured in `orion-config.yaml`:

```yaml
listeners:
- name: tlv_demo_listener
address: 0.0.0.0:9000
filter_chains:
- filters:
- name: envoy.listener.kmesh_tlv
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.listener.kmesh_tlv.v3.KmeshTlv
```

## Manual Testing

### Start Orion
```bash
../../target/debug/orion -c orion-config.yaml
```

### Send TLV Packets
```bash
rustc send_tlv.rs -o send_tlv
./send_tlv <ip> <port>
```

The client constructs TLV packets containing original destination information and sends them to the Orion listener for testing the filter's TLV processing capabilities.

## TLV Client

The included Rust client (`send_tlv.rs`) is used for testing the TLV filter:

- **Purpose**: Constructs and sends TLV packets to test the filter's processing
- **Functionality**: Encodes original destination IP/port in TLV format and sends to Orion listener
- **Usage**: Compile with `rustc send_tlv.rs -o send_tlv` then run `./send_tlv <ip> <port>`
- **Protocol**: Supports both IPv4 and IPv6 addresses in TLV packets

### Protocol Buffer
```protobuf
syntax = "proto3";
package envoy.extensions.filters.listener.kmesh_tlv.v3;

message KmeshTlv {}
```

### Configuration Parameters
- **Filter Name**: `envoy.listener.kmesh_tlv`
- **Type URL**: `type.googleapis.com/envoy.extensions.filters.listener.kmesh_tlv.v3.KmeshTlv`

### TLV Protocol Support
- **TLV_TYPE_SERVICE_ADDRESS (0x1)**: Service address information
- **TLV_TYPE_ENDING (0xfe)**: End marker
- **Maximum TLV Length**: 256 bytes

## Architecture

```
Client → Orion Listener → TLV Filter → Filter Chains → Backend
TLV Processing
(Extract original destination)
```

## Kmesh Compatibility

Fully compatible with Kmesh project configurations using identical protobuf package names and TypedStruct configuration pattern.
67 changes: 67 additions & 0 deletions examples/tlv-filter-demo/orion-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Orion Configuration with TLV Listener Filter Enabled

runtime:
num_cpus: 1
num_runtimes: 1

logging:
log_level: "debug"

envoy_bootstrap:
admin:
address:
socket_address:
address: "127.0.0.1"
port_value: 9901

static_resources:
listeners:
- name: "tlv_demo_listener"
address:
socket_address:
address: "0.0.0.0"
port_value: 10000
listener_filters:
- name: "envoy.listener.kmesh_tlv"
typed_config:
"@type": "type.googleapis.com/udpa.type.v1.TypedStruct"
"type_url": "type.googleapis.com/envoy.extensions.filters.listener.kmesh_tlv.v3.KmeshTlv"
"value": {}
filter_chains:
- name: "default_filter_chain"
filters:
- name: "envoy.filters.network.http_connection_manager"
typedConfig:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
codec_type: AUTO
stat_prefix: tlv_demo
httpFilters:
- name: "envoy.filters.http.router"
typedConfig:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
routeConfig:
name: "local_route"
virtualHosts:
- name: "demo"
domains: ["*"]
routes:
- match:
prefix: "/"
direct_response:
status: 200
body:
inline_string: "TLV Filter Test - Success!"

clusters:
- name: "dummy_cluster"
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: "127.0.0.1"
port_value: 8080
96 changes: 96 additions & 0 deletions examples/tlv-filter-demo/send_tlv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// SPDX-FileCopyrightText: © 2025 kmesh authors
// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2025 kmesh authors
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//

use std::env;
use std::io::Write;
use std::net::{TcpStream, IpAddr};

fn construct_tlv_packet(ip: &str, port: u16) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
// Parse IP address
let ip_addr: IpAddr = ip.parse()?;
let (ip_bytes, content_len) = match ip_addr {
IpAddr::V4(ipv4) => (ipv4.octets().to_vec(), 6u32), // 4 bytes IP + 2 bytes port
IpAddr::V6(ipv6) => (ipv6.octets().to_vec(), 18u32), // 16 bytes IP + 2 bytes port
};

// Pack port as big-endian
let port_bytes = port.to_be_bytes();

// TLV structure:
// Type: 0x01 (service address)
// Length: 4 bytes (big-endian)
// Content: IP + Port
// End: 0xfe

let content = [ip_bytes, port_bytes.to_vec()].concat();
let length = content_len.to_be_bytes();

let mut tlv_packet = vec![0x01]; // Type
tlv_packet.extend_from_slice(&length); // Length
tlv_packet.extend_from_slice(&content); // Content

// End marker: Type 0xfe, Length 0
tlv_packet.push(0xfe); // End type
tlv_packet.extend_from_slice(&0u32.to_be_bytes()); // End length (0)

Ok(tlv_packet)
}

fn send_tlv_packet(ip: &str, port: u16, listener_host: &str, listener_port: u16) -> Result<(), Box<dyn std::error::Error>> {
// Create TLV packet
let tlv_data = construct_tlv_packet(ip, port)?;
println!("Constructed TLV packet: {:02x?}", tlv_data);

// Create HTTP request to follow
let http_request = b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";

// Combine TLV + HTTP
let mut packet = tlv_data;
packet.extend_from_slice(http_request);

// Send to listener
let mut stream = TcpStream::connect((listener_host, listener_port))?;
stream.write_all(&packet)?;

println!("✅ Sent TLV packet with original destination {}:{}", ip, port);
Ok(())
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = env::args().collect();

if args.len() != 3 {
eprintln!("Usage: {} <ip> <port>", args[0]);
std::process::exit(1);
}

let ip = &args[1];
let port: u16 = args[2].parse()?;

match send_tlv_packet(ip, port, "127.0.0.1", 10000) {
Ok(()) => {
std::process::exit(0);
}
Err(e) => {
eprintln!("❌ Failed to send TLV packet: {}", e);
std::process::exit(1);
}
}
}
Loading
Loading