Push Tracker
ria-ran--sim-drop/plan/design.md

496 lines
14 KiB
Markdown
Raw Permalink Normal View History

# Closed-Loop eSIM Provisioning System for Emergency/NTN 5G Deployment
## Overview
Self-contained eSIM provisioning system for ad-hoc 5G network deployment in emergency or NTN scenarios. Users connect to WiFi AP, automatically receive eSIM profile, then connect to 5G network.
## System Architecture
```
┌─────────────────────────────────────────┐
│ Server Host (srsRAN gNB) │
│ - srsRAN gNB │
│ - Open5GS Core (or free5gc) │
│ - Subscriber database │
└─────────────┬───────────────────────────┘
│ 5G NR
┌─────────────────────────────────────────┐
│ UE WiFi AP Drop │
│ (Android device or Raspberry Pi) │
│ │
│ ┌───────────────────────────────────┐ │
│ │ 5G Modem (pre-provisioned SIM) │ │
│ │ Connects to gNB for backhaul │ │
│ └───────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────┐ │
│ │ WiFi AP (open network) │ │
│ └───────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────┐ │
│ │ eSIM Provisioning Server │ │
│ │ - Profile generator │ │
│ │ - Captive portal │ │
│ │ - Credential database │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
│ WiFi
End User Devices
```
## User Flow
1. User device detects open WiFi network
2. Connects to WiFi → Auto-redirected to captive portal
3. Captive portal displays "Activate eSIM" button
4. Click → Server generates unique IMSI/Ki/OPc credentials
5. Server generates SGP.22 eSIM profile with credentials
6. Profile delivered to device via LPA protocol
7. Device installs eSIM profile
8. User disconnects from WiFi
9. Device connects to 5G network using new eSIM
10. Open5GS/free5gc authenticates user
## Components
### 1. UE WiFi AP Drop Device
**Hardware Options:**
- **Primary**: Android phone (cheapest if app is simple)
- **Fallback**: Raspberry Pi 4 + Quectel RM500Q 5G modem
**Requirements:**
- Pre-provisioned "admin" SIM to connect to gNB
- WiFi hotspot capability
- Runs provisioning server stack
**Software Stack:**
- Cellular connection to gNB (uses pre-provisioned SIM)
- WiFi AP (hostapd or Android hotspot)
- Captive portal (dnsmasq + nginx)
- eSIM provisioning server (Python)
### 2. eSIM Provisioning Server
**Technology:** Python + Flask
**Core Functions:**
- Generate IMSI/Ki/OPc credentials on-demand
- Create SGP.22-compliant eSIM profiles using pySim
- Serve profiles via LPA protocol
- Store generated credentials in SQLite
- Rate limiting (1 profile per MAC address per hour)
- Export credentials for core sync
**Dependencies:**
- `pySim` - SIM/eSIM profile generation
- `Flask` - Web server
- `sqlite3` - Local credential storage
- `cryptography` - Key generation
**Endpoints:**
```
GET / - Captive portal landing page
POST /activate - Trigger eSIM generation
GET /profile/:id - Download eSIM profile (LPA)
GET /export - Export credentials for core sync
```
**Profile Generation Logic:**
```python
# Generate credentials
IMSI = generate_imsi(mcc=302, mnc=720) # Adjust for your network
Ki = random_bytes(16)
OPc = derive_opc(Ki, OP)
# Create eSIM profile
profile = create_esim_profile(
iccid=generate_iccid(),
imsi=IMSI,
ki=Ki,
opc=OPc,
profile_name="Emergency 5G"
)
# Store in database
store_subscriber(imsi, ki, opc, timestamp, mac_address)
# Return profile for LPA download
return profile
```
### 3. Captive Portal
**Technology:** HTML + JavaScript + nginx
**Features:**
- Auto-redirect any HTTP request to portal
- Single-button interface: "Activate eSIM"
- Instructions for eSIM installation
- Minimal UI, works on any device
**DNS Hijacking:**
```
# dnsmasq configuration
address=/#/192.168.4.1
```
**nginx Configuration:**
```nginx
server {
listen 80 default_server;
server_name _;
location / {
return 302 http://192.168.4.1/activate;
}
location /activate {
root /var/www/captive;
index index.html;
}
location /api {
proxy_pass http://127.0.0.1:5000;
}
}
```
### 4. Core Integration Module
**Design:** Adapter pattern for portability
**Supported Cores (initially):**
- Open5GS
- free5gc
**Interface:**
```python
class CoreAdapter:
def add_subscriber(self, imsi, ki, opc):
"""Add subscriber to core network"""
pass
def remove_subscriber(self, imsi):
"""Remove subscriber from core network"""
pass
def list_subscribers(self):
"""List all subscribers"""
pass
```
**Open5GS Implementation:**
```python
class Open5GSAdapter(CoreAdapter):
def add_subscriber(self, imsi, ki, opc):
# Direct MongoDB insertion
db.subscribers.insert_one({
"imsi": imsi,
"security": {
"k": ki,
"opc": opc,
"amf": "8000",
"sqn": 0
},
"ambr": {
"downlink": {"value": 1, "unit": 3}, # 1 Gbps
"uplink": {"value": 1, "unit": 3}
},
"slice": [{
"sst": 1,
"default_indicator": True,
"session": [{
"name": "internet",
"type": 3, # IPv4v6
"ambr": {
"downlink": {"value": 1, "unit": 3},
"uplink": {"value": 1, "unit": 3}
}
}]
}]
})
```
**Sync Options:**
1. **Push**: Provisioning server calls core adapter immediately
2. **Pull**: Core polls provisioning server API periodically
3. **Manual**: Export JSON, manually import to core
**Initial Implementation:** Export JSON format
```json
{
"subscribers": [
{
"imsi": "302720123456789",
"ki": "00112233445566778899aabbccddeeff",
"opc": "63bfa50ee6523365ff14c1f45f88737d",
"timestamp": "2025-02-11T10:30:00Z",
"mac_address": "aa:bb:cc:dd:ee:ff"
}
]
}
```
### 5. eSIM Profile Format
**Standard:** SGP.22 compliant eSIM profile
**Profile Contents:**
- ICCID (SIM card identifier)
- IMSI (subscriber identity)
- Ki (authentication key)
- OPc (derived operator key)
- Profile metadata (name, description)
- APN configuration (if needed)
**Generation Method:**
Use pySim's eUICC profile generator:
```python
from pySim.esim import EsimProfile
profile = EsimProfile(
iccid=iccid,
imsi=imsi,
ki=ki,
opc=opc,
profile_name="Emergency 5G",
carrier_name="Ad-Hoc Network"
)
profile_bytes = profile.encode()
```
**Fallback:** If SGP.22 generation fails, serve simplified profile (may have compatibility issues with some devices)
## Implementation Phases
### Phase 1: Core Server Components
- [ ] eSIM provisioning server (Python/Flask)
- [ ] Credential generation and storage
- [ ] Profile generation using pySim
- [ ] LPA protocol implementation
- [ ] SQLite database schema
### Phase 2: Captive Portal
- [ ] HTML/JS frontend
- [ ] nginx configuration
- [ ] DNS hijacking setup
- [ ] Auto-redirect logic
### Phase 3: Core Integration
- [ ] Open5GS adapter
- [ ] JSON export format
- [ ] Sync script
### Phase 4: UE WiFi AP Drop
- [ ] Android app (if feasible)
- [ ] OR Raspberry Pi setup scripts
- [ ] Auto-connection to gNB
- [ ] WiFi AP configuration
- [ ] Service orchestration
### Phase 5: Testing & Hardening
- [ ] End-to-end flow testing
- [ ] Rate limiting verification
- [ ] Profile compatibility testing
- [ ] Network isolation verification
## Deployment Instructions
### Prerequisites
- srsRAN gNB running and configured
- Open5GS core running (or free5gc)
- One pre-provisioned "admin" SIM for backhaul UE
- UE device (Android phone or Raspberry Pi)
### UE WiFi AP Drop Setup
**If Android:**
1. Install Termux
2. Install required packages (Python, hostapd, dnsmasq, nginx)
3. Deploy provisioning server
4. Configure WiFi hotspot
5. Start services
**If Raspberry Pi:**
1. Install Raspberry Pi OS Lite
2. Install Quectel RM500Q modem drivers
3. Configure modem for gNB connection
4. Install provisioning server stack
5. Configure hostapd + dnsmasq
6. Start services
### Provisioning Server Setup
```bash
# Install dependencies
pip install flask pySim cryptography
# Create directory structure
mkdir -p /opt/esim-provisioning/{server,portal,data}
# Deploy server code
cp provisioning_server.py /opt/esim-provisioning/server/
cp -r templates/ /opt/esim-provisioning/server/
# Deploy captive portal
cp -r portal/* /opt/esim-provisioning/portal/
# Initialize database
python init_db.py
# Start server
systemctl start esim-provisioning
```
### Core Integration
```bash
# Export subscribers from provisioning server
curl http://192.168.4.1:5000/export > subscribers.json
# Import to Open5GS
python open5gs_import.py subscribers.json
# OR for free5gc
python free5gc_import.py subscribers.json
```
## Configuration
### Network Parameters
```python
# config.py
MCC = "302" # Canada (adjust for your region)
MNC = "720" # Adjust for your network
PLMN = f"{MCC}{MNC}"
# IMSI format: MCC + MNC + MSIN
# Example: 302720123456789
IMSI_PREFIX = PLMN
MSIN_LENGTH = 10
# Operator key (OP) - used to derive OPc
OP = bytes.fromhex("63bfa50ee6523365ff14c1f45f88737d")
# WiFi AP configuration
WIFI_SSID = "Emergency-5G"
WIFI_CHANNEL = 6
WIFI_IP = "192.168.4.1"
WIFI_SUBNET = "192.168.4.0/24"
WIFI_DHCP_RANGE = "192.168.4.10,192.168.4.250"
# Rate limiting
MAX_PROFILES_PER_MAC = 1
RATE_LIMIT_HOURS = 1
```
### Open5GS Connection
```python
# open5gs_config.py
MONGODB_URI = "mongodb://localhost:27017"
DATABASE_NAME = "open5gs"
COLLECTION_NAME = "subscribers"
```
## Security Considerations
### Current Implementation (Phase 1)
- Open WiFi network (no authentication)
- Rate limiting by MAC address
- No encryption on provisioning server traffic (local network only)
### Future Enhancements
- Optional WiFi password (distribute via physical signage)
- Pre-qualified activation codes
- HTTPS for provisioning server
- Profile encryption
- Audit logging
## Scalability
### On-Demand Generation
- Generates credentials only when requested
- No pre-generation overhead
- Limited only by:
- IMSI space (10^10 possible MSINs with 10-digit MSIN)
- Storage (SQLite can handle millions of records)
- Processing power (generates ~10 profiles/second on modest hardware)
### Multi-Core Session Handover
**Design Consideration:** Profile format supports core mobility
Current design doesn't preclude future session handover between cores:
- IMSI/Ki/OPc are standardized, portable between cores
- Export format is core-agnostic JSON
- Core adapters can be extended to support handover signaling
**Future Implementation:**
- Add `home_core_id` field to subscriber record
- Export includes core affinity metadata
- Handover protocol exchanges subscriber credentials between cores
## Troubleshooting
### Profile Generation Fails
- Check pySim installation: `python -c "import pySim"`
- Verify OP key is 16 bytes
- Check IMSI format (15 digits total)
### Device Won't Download Profile
- Verify device supports eSIM (eUICC)
- Check LPA protocol implementation
- Verify profile format is SGP.22 compliant
- Try simplified profile format
### Can't Connect to 5G After eSIM Install
- Verify subscriber added to Open5GS/free5gc
- Check IMSI/Ki/OPc match between profile and core
- Verify PLMN configuration matches gNB
- Check AMF/UDM logs for authentication failures
### WiFi Captive Portal Not Triggering
- Verify DNS hijacking: `nslookup google.com 192.168.4.1`
- Check nginx is running: `systemctl status nginx`
- Verify iptables rules for captive portal
## File Structure
```
/opt/esim-provisioning/
├── server/
│ ├── provisioning_server.py # Main Flask application
│ ├── profile_generator.py # eSIM profile generation
│ ├── database.py # SQLite interface
│ ├── core_adapter.py # Core integration adapters
│ └── config.py # Configuration
├── portal/
│ ├── index.html # Captive portal page
│ ├── style.css
│ └── script.js
├── data/
│ ├── subscribers.db # SQLite database
│ └── profiles/ # Generated profiles (temp)
├── scripts/
│ ├── sync_open5gs.py # Open5GS sync script
│ ├── sync_free5gc.py # free5gc sync script
│ └── export_subscribers.py # Export to JSON
└── systemd/
├── esim-provisioning.service # Systemd unit file
└── captive-portal.service # Captive portal service
```
## Next Steps
1. Implement provisioning server (Python/Flask)
2. Implement profile generator using pySim
3. Create captive portal frontend
4. Build Open5GS adapter
5. Test end-to-end flow
6. Deploy to UE WiFi AP drop device
## References
- SGP.22 eSIM Specification: https://www.gsma.com/esim/
- pySim Documentation: https://github.com/osmocom/pysim
- Open5GS Documentation: https://open5gs.org/
- srsRAN Documentation: https://docs.srsran.com/