Skip to content

Instantly share code, notes, and snippets.

@jasonacox
Last active January 6, 2025 06:56
Show Gist options
  • Save jasonacox/91479957d0605248d7eadb919585616c to your computer and use it in GitHub Desktop.
Save jasonacox/91479957d0605248d7eadb919585616c to your computer and use it in GitHub Desktop.
Set up RaspberryPi as Network Router to Powerwall Gateway

RaspberryPi - Powerwall Router

This will set up a Raspberry Pi to connect to a Tesla Powerwall Gateway (TEG) and bridge that connection to the ethernet connected LAN.

Network Configuration

 ___________________          __________________________           _______________
[ Powerwall Gateway ]        [      Raspberry Pi        ]         [      Host     ]
[       TEG         ]  WiFi  [__________________________]   LAN   [ Linux/Mac/Win ]
[   WiFi: TEG-xxx   ] <----  [ 192.168.91.x | 10.0.1.55 ] <-----> [   10.0.1.65   ]
[   192.168.91.1    ]        [  WiFi (dhcp) |  Ethernet ]         [      LAN      ]
 ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾          ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾           ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
 

Raspberry Pi

  1. Create or edit /etc/wpa_supplicant/wpa_supplicant.conf:
network={
        ssid="TEG-xxx"
        psk="password"
}
  1. Restart Networking and Test
sudo systemctl restart networking

# Test
ifconfig wlan0
ping -c 1 192.168.91.1
  1. Set up IPv4 Routing and Reboot
# Add IP Forwarding - Uncomment net.ipv4.ip_forward=1
sudo sed -i -e '/^#net\.ipv4\.ip_forward=1/s/^#//' /etc/sysctl.conf
sudo sysctl -w net.ipv4.ip_forward=1

# Restart
sudo reboot

Host

On the host, you need to add a route to use the Raspberry Pi as a gateway to get to the Powerwall Gateway.

# Linux
sudo ip r add 192.168.91.0/24 via 10.0.1.55

# MacOS
sudo route add -host 192.168.91.1 10.0.1.55

# Test
ping -c 1 192.168.91.1
curl -ik https://192.168.91.1
@Nexarian
Copy link

Nexarian commented Jan 5, 2025

@jasonacox I have a fix for this.

Status

The script is still a bit rough/immature. I'm working on figuring out how to make it better and/or integrate it into the server. I also have other variations that work using network namespaces and virtual interfaces, but those seem unnecessary.

Summary

Essentially: Instead of calling 192.168.91.1 directly, set the host in pypowerwall to one of [192.168.92.100, 192.168.92.101] (Subnet choice is arbitrary). Currently my inverters are at [192.168.1.67 , 192.168.92.250] and my raspberry pi is at 192.168.1.225

Steps

  1. Create virtual IP address
  2. Add static route in a unique table
  3. Add DNAT destination rerouting to convert target IP to Tesla internal IP in the OUTPUT (NOT PREROUTING) table.
  4. Add a mangle table entry to MARK anything coming from the virtual IP
  5. Add a rule that anything marked should use the unique table above.
  6. Finally, add a postrouting rule so that this crazy request can find its way back.

This is the reason for this fix that I posted earlier today. Most of TEDAPI worked except firmware version retrieval!

I will update my other PR with this.

Code

Routing Configuration
#!/bin/bash

set -ex

# Function to check if a command succeeded
check_command() {
    if [ $? -ne 0 ]; then
        echo "Error: $1"
        exit 1
    fi
}

# Function to clean up existing rules and configurations
cleanup() {
    echo "Cleaning up existing rules and configurations..."
    
    # Remove virtual IP addresses
    ip addr del 192.168.92.100/24 dev eth0 2>/dev/null || true
    ip addr del 192.168.92.101/24 dev eth0 2>/dev/null || true

    # Flush NAT table
    iptables -t nat -F
    check_command "Failed to flush NAT table"

    # Flush mangle table
    iptables -t mangle -F
    check_command "Failed to flush mangle table"

    # Remove all rules in filter table
    iptables -F
    check_command "Failed to flush filter table"

    # Remove non-default chains
    iptables -X
    check_command "Failed to delete non-default chains"

    # Remove all ip rules (except default)
    ip rule show | grep -v "from all lookup" | cut -d: -f1 | xargs -r -n1 ip rule del prio
    check_command "Failed to remove ip rules"

    # Remove routing tables
    ip route flush table 100 || true
    ip route flush table 101 || true
    check_command "Failed to flush routing tables"

    echo "Cleanup completed successfully."
}

# Main setup function
setup() {
    echo "Setting up routing and NAT rules..."

    # Enable IP forwarding
    echo 1 > /proc/sys/net/ipv4/ip_forward
    check_command "Failed to enable IP forwarding"

    # Add virtual IP addresses
    ip addr add 192.168.92.100/24 dev eth0
    check_command "Failed to add virtual IP 192.168.92.100"
    ip addr add 192.168.92.101/24 dev eth0
    check_command "Failed to add virtual IP 192.168.92.101"

    # Set up routing tables
    ip route add 192.168.91.0/24 via 192.168.1.67 dev eth0 onlink table 100
    check_command "Failed to add route to table 100"
    ip route add 192.168.91.0/24 via 192.168.1.250 dev eth0 onlink table 101
    check_command "Failed to add route to table 101"

    # Set up NAT rules
    iptables -t nat -A OUTPUT -d 192.168.92.100 -j DNAT --to-destination 192.168.91.1
    check_command "Failed to add DNAT rule for 192.168.92.100"
    iptables -t nat -A OUTPUT -d 192.168.92.101 -j DNAT --to-destination 192.168.91.1
    check_command "Failed to add DNAT rule for 192.168.92.101"

    iptables -t nat -A PREROUTING -d 192.168.92.100 -j DNAT --to-destination 192.168.91.1
    check_command "Failed to add DNAT rule for 192.168.92.100"
    iptables -t nat -A PREROUTING -d 192.168.92.101 -j DNAT --to-destination 192.168.91.1
    check_command "Failed to add DNAT rule for 192.168.92.101"

    # Set up packet marking
    iptables -t mangle -A OUTPUT -d 192.168.92.100 -j MARK --set-mark 1
    check_command "Failed to add mark for 192.168.92.100"
    iptables -t mangle -A OUTPUT -d 192.168.92.101 -j MARK --set-mark 2
    check_command "Failed to add mark for 192.168.92.101"

    # Set up ip rules
    ip rule add fwmark 1 table 100
    check_command "Failed to add ip rule for mark 1"
    ip rule add fwmark 2 table 101
    check_command "Failed to add ip rule for mark 2"

    # Set up return traffic NAT
    iptables -t nat -A POSTROUTING -s 192.168.92.100 -d 192.168.91.1 -j SNAT --to-source 192.168.1.225
    check_command "Failed to add return SNAT rule for 192.168.92.100"
    iptables -t nat -A POSTROUTING -s 192.168.92.101 -d 192.168.91.1 -j SNAT --to-source 192.168.1.225
    check_command "Failed to add return SNAT rule for 192.168.92.101"

    echo "Setup completed successfully."
}

# Main execution
if [[ $EUID -ne 0 ]]; then
   echo "This script must be run as root" 
   exit 1
fi

# Cleanup first
cleanup

# Then setup
setup

echo "All operations completed successfully."

@jasonacox
Copy link
Author

Thanks @Nexarian !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment