xvzf / zyxel-gpon-sfp

Telekom FTTH with OpenWRT/PfSense/VyOS/Mikrotik/... (Reverse engineering Zyxel PMG3000-D20B GPON SFP)
191 stars 26 forks source link

"Hacking" the (Telekom) Zyxel GPON SFP module (PMG3000-D20B)

The SFP can be sourced very easily and is widely available in Germany.

I just want fiber internet on my non-Telekom router

jaseg has written a guide on this on his blog.

TLDR

Checkout the three options for configuring your SFP. When requiring a serial number change, this can be performed by the CLI only. Note, that some SFP NICs don't support hardwireing the speed settings. In this case, you need to connect the GPON fibre link to the module to be able to access it (see https://github.com/xvzf/zyxel-gpon-sfp/issues/8).

1. WEB UI

  1. Configure the ethernet interface the SFP is in with the IP 10.10.1.2/24.
  2. Port-forward the SFPs web interface to your local machine via SSH: ssh -L 127.0.0.1:8080:10.10.1.1:80 <user@router> (maybe you need to add option '-oHostKeyAlgorithms=+ssh-dss' before '-L'to connect).
  3. Access the web-interface on http://localhost:8080, username admin, password 1234.

2. CLI (on the SFP)

Note: The PLOAM ID has to be HEX encoded, in case yours is a 10-character string, you can transform it using python3 -c 'print(hex("<enter PLOAM/SLID between the qotes>"))'. Omit the 0x prefix.

  1. Configure the ethernet interface the SFP is in with the IP 10.10.1.2/24.
  2. SSH into the module using admin@10.10.1.1, password admin.
  3. Login into the CLI with user admin, password 1234.
  4. Change the PLOAM/SLID/Installationskennung by entering following commands followed by a newline:
    • hal
    • password <PLOAM/SLID>
  5. Optional: Change the serial number using set sn ...; the first four characters are ASCII encoded, e.g. SCOM, the rest is followed in hex. Within sw version 'V1.00(ABVJ.0)b3v' you need to use the whole SN as ASCII encoded string (cmd like 'set sn 534312345678').

3. CLI (remote)

Note: requires Python >= 3.8

NAME
    zyxel_gpon_sfp.py --sfp_addr=http://10.10.1.1

SYNOPSIS
    zyxel_gpon_sfp.py --sfp_addr=http://10.10.1.1 - COMMAND

COMMANDS
    COMMAND is one of the following:

     info

     set_slid

     set_sn

Motivation

My ISPs (Deutsche Telekom) FTTH offering uses on a GPON network and distributes ONUs with a 1G (or 2.5G Ethernet) for non-business customers. I intended to run the fiber directly into my Linux router (using one of the SFP+ ports). Looking at the business offerings building upon the same technology revealed SFPs distributed only business customers using the Digitalisierungsbox Premium 2. The mentioned SFP is made by Zyxel with the identifier PMG3000-D20B and sold as Digitalisierungsbox Glasfaser Modem (Telekom only sells it to business customers but it is available online for ~40 Euros).

The module is based on a Lantiq 98035 SoC, datasheet, link to OpenWRT forums discussion on Huawei SFP module based on the same SoC.

Accessing the module

After reverse engineering (this time it has been a fzf through all files, not analysing the binaries) the firmware of Telekom Digitalisierungsbox 2, I've identified the IP address of the module being 10.10.1.1/24 based on a SQL statement with a comment:

-- BS-6456: remove marker 'RESERVED' from static IP used to access the SFP module
UPDATE Ip SET Name="" WHERE IpAddress="10.10.1.2" AND Interface="eth1" AND LogicalInterface="eth1";

Digging a bit further in plaintext SQL statements reveals the credentials.

-- ...
INSERT INTO SshConfiguration VALUES ( 1, 0, 5, 22, 'Access only for authorized persons!', 0, '' );
INSERT INTO SshUser VALUES ( 1, 0, 'admin', 'admin', 0 );
-- ...
INSERT INTO GPONConfig VALUES ( 1, 1, '10.10.1.1', 'admin', '1234', '', '' );

Well, let's give it a try. SSH access sounds like a charm and is confirmed by nmap:

xvzf@e300 ~ % nmap 10.10.1.1
Starting Nmap 7.80 ( https://nmap.org ) at 2022-02-02 06:31 UTC
Nmap scan report for 10.10.1.1
Host is up (0.00079s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
MAC Address: <redacted> (Zyxel Communications)

Nmap done: 1 IP address (1 host up) scanned in 4.15 seconds

Let's give it a try with ssh admin@10.10.1.1:

#######################################################
#                                                     #
# Please login to CLI mode. Then You can do commands. #
#                                                     #
#######################################################

Entering character mode
Escape character is '^]'.

Login: admin
Password: <not echoed `1234`>
ZYXEL#
ZYXEL# <not echoed `?`>
  linuxshell  Enter linux shell
  show        show
  system
  manufactory
  config
  mib
  sf
  log
  timer
  bsp
  hal
  igmp
  omci
  ssp
ZYXEL# show version
Project Name:              TW2362H-CDEL
Client Product Name:       GTO100I_SFP_ZYXEL
Internal Product Name:     GTO100I_SFP_ZYXEL
Hardware Version:          PMG3000-D20B
Boot Version:              V1.0.0
Client Software Version:   V1.0.0
Internal Software Version: V1.0.0
Build User:                jiangyuanqi
Build Time:                2021-05-08 11:28:36
Build Method:              export ONU=gto100i_sfp_zyxel && cd ../drv && make install && cd .. && make rootfs && make install
GIT Info:                  TW2362H-CDEL_lantiq98035/customize/TW2362H-CDEL_lantiq98035_general_20150131:e057bd83
ZYXEL#

So, we can get a linux shell, nice. My SFP is running a (very old) release of OpenWrt:

ZYXEL# linuxshell
BusyBox v1.19.4 (2014-06-30 12:00:02 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 ATTITUDE ADJUSTMENT (Attitude Adjustment, 12.09_ltq)
 -----------------------------------------------------
  * 1/4 oz Vodka      Pour all ingredients into mixing
  * 1/4 oz Gin        tin with ice, strain into glass.
  * 1/4 oz Amaretto
  * 1/4 oz Triple sec
  * 1/4 oz Peach schnapps
  * 1/4 oz Sour mix
  * 1 splash Cranberry juice
 -----------------------------------------------------
admin@SFP:~# uname -a
Linux SFP 3.10.12 #2 Wed Jul 12 12:01:33 CST 2017 mips GNU/Linux
admin@SFP:~#

Changing GPON Serial Number / PLOAM Password

ZYXEL# hal
Hal#
  linuxshell  Enter linux shell
  show        show HAL configuration
  sn          change ont parameters
  password    change ont password
  set         set ont parameters
  to1         change ont to1 interval
  to2         change ont to2 interval
  berinterval change BER interval
  sfthreshold change SF threshold
  sdthreshold change SD threshold
  tcont       add tcont
  no          delete HAL item
  gemport     add HAL item
  reset       Reset all pon configurations
  get         get
  omci        omci
  stream      stream
  mvlanaction mvlanaction
  uni         PPTP UNI configuration
  mtu         MTU R/W
  multicast   multicast configartion
  iphost      iphost
  init        init
  deny        deny
  permit      permit
  monitor     monitor
  mac         mac
  storm       storm
  print       print
  igmp        igmp
  mcastfilt   McastFilt
Hal# sn
  <string> change ont serial number
Hal# password
  <string> Formate:XXXXXXXXXXXXXXXXXXXX

The password seems to consist of 10 bytes, entered hex encoded. This is likely the PLOAM password / SLID / Installationskennung / whatever you'd like to call it. The sn seems to change the serial number of the ONU (ONT) itself. This works, though it expects the first 4 characters to be ASCII encoded (e.g. for the Telekom Glasfasermodem 2, it likely starts with SCOM (hex:5343 4f4d)

I assumed the CLI is using the configuration interface of OpenWRT under the hood; turns out I was right:

uci show gpon
gpon.ploam=gpon
gpon.ploam.nPassword=0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20
gpon.ploam.nT01=16000
gpon.ploam.nT02=100
gpon.ploam.nEmergencyStopState=0
gpon.ploam.nRogueMsgIdUpstreamReset=255
gpon.ploam.nRogueMsgRepeatUpstreamReset=3
gpon.ploam.nRogueMsgIdDeviceReset=255
gpon.ploam.nRogueMsgRepeatDeviceReset=3
gpon.ploam.nRogueEnable=0
gpon.gtc=gpon
gpon.gtc.bDlosEnable=0
gpon.gtc.bDlosInversion=0
gpon.gtc.nDlosWindowSize=0
gpon.gtc.nDlosTriggerThreshold=0
gpon.gtc.ePower=0
gpon.gtc.nLaserGap=0
gpon.gtc.nLaserOffset=0
gpon.gtc.nLaserEnEndExt=0
gpon.gtc.nLaserEnStartExt=0
gpon.gtc.nDyingGaspHyst=0
gpon.gtc.nDyingGaspMsg=0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
gpon.gtc.nDyingGaspEnable=0
gpon.ethernet=gpon
gpon.ethernet.bUNI_PortEnable0=1
gpon.ethernet.bUNI_PortEnable1=1
gpon.ethernet.bUNI_PortEnable2=1
gpon.ethernet.bUNI_PortEnable3=1
gpon.gpe=gpon
gpon.gpe.nPeNumber=6

Observing the GPON SN and Password in real time

Serial and Password

The onu command helps debugging the system:

Connection state

Connected (curr_state=5)

admin@SFP:~# onu ploamsg
errorcode=0 curr_state=5

Disconnected (curr_state=1):

admin@SFP:~# onu ploamsg
errorcode=0 curr_state=1 previous_state=0 elapsed_msec=16907701

Enable 2.5G

2.5G may not be enabled by default on the SFP. Use the following command to enable 2.5 manually:

ZYXEL# hal
Hal# set speed 2.5g mode full

You may have to disable auto-negotation and set a fixed port speed of 2.5G on your network adapter to make it work.

How to handle with unknown passwords for Zyxel PMG3000-D20B

Disclaimer: Use this on your own risk. I am not responsible for any damages!

Sometimes the default login admin/1234 for Zyxel PMG3000-D20B does not work - this describes how to get the password and how to change it.

Get the system default password:

  1. Open the webinterface (http://10.10.1.1 if the modules default ip has not been changed)
  2. Login with guest/guest
  3. Set the admin password to admin/1234: http://10.10.1.1/cgi/set_admin?rand=0.7041383755617387&type=2&username=admin&password=31rmzl323334m&level=0 - the device responds "1"
  4. Open SSH, Login with admin/admin and for the Zyxel CLI Mode with admin/1234
  5. Call linuxshell
  6. Display device default password: cat /var/config/.user_cfg

If this is not successful:

  1. Write the actual config with http://10.10.1.1/cgi/set_save?rand=0.4798344808717123 - the device responds "1"
  2. Display device default password: grep -ie "admin Password" /var/config/mib.conf

If you would like to change this permanently (survives a factory reset):

The device default password is stored in the uboot-env partition (mtd1). The firmware contains the needed binaries but unfortunately not the needed layout-config (/etc/fw_env.config). Furthermore the /etc directory is not writeable because it is a squashfs filesystem.

  1. Login into the device, start linuxshell
  2. cd /tmp
  3. mkdir /tmp/mount_bind
  4. copy the hole /etc into /tmp/mount_bind: cp -r /etc/ /tmp/mount_bind/
  5. mount the copy for /etc: mount -o bind /tmp/mount_bind/etc/ /etc/

Now the /etc is (up to the next reboot) writeable, because it´s redirected to /tmp/mount_bind/etc.

For creating the needed fw_env.config there is already a script which is called at boot time (and normally fails because of the read-only access).

  1. /etc/init.d/fw_env.sh boot
  2. Show the uboot-env variables: fw_printenv look for remote_account_pwd

It is important that fw_printenv does not complain about checksum errors. If it complains, do not continue!

Be careful changing values in the uboot-env! Laser calibration data is also stored here, they are individual for every module! It´s a good idea to store the output of fw_printenv - alternatively make a backup of your mtd1 partition!

  1. Set the new password: fw_setenv remote_account_pwd 1234
  2. Restore factory-defaults: http://10.10.1.1/cgi/set_default?rand=0.8890542929500389 - the device responds "1"
  3. Reboot: http://10.10.1.1/cgi/reset_onu?rand=0.14418772918225453 - the device responds "1"

Be happy :-)

HTTP API

Only after getting SSH access I discovered the SFP comes with a WebUI and a sort of API. The CLI zyxel_gpon_sfp.py makes use of this API to remotely configure the PLOAM password and possibly SN (again, didn't check it).

TODO