Repository: xvzf/zyxel-gpon-sfp
Branch: main
Commit: 187435b0cb1c
Files: 14
Total size: 19.7 KB
Directory structure:
gitextract_nqxjjm6j/
├── .gitignore
├── Readme.md
├── flashdumps/
│ ├── binwalk.out
│ ├── mtd0
│ ├── mtd1
│ ├── mtd2
│ ├── mtd3
│ ├── mtd4
│ ├── mtd5
│ ├── mtd6
│ ├── mtd7
│ └── sha256.sum
├── requirements.txt
└── zyxel_gpon_sfp.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
venv/
================================================
FILE: Readme.md
================================================
# "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](https://github.com/jaseg/) has written a guide on this [on his blog](https://jaseg.de/blog/telekom-gpon-sfp/).
## 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](https://www.telekom.de/)) 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_](https://www.telekom.de/hilfe/geraete-zubehoer/router/digitalisierungsbox/premium-2#e_745060).
The mentioned SFP is made by Zyxel with the identifier `PMG3000-D20B` and sold as [_Digitalisierungsbox Glasfaser Modem_](https://geschaeftskunden.telekom.de/internet-dsl/produkt/digitalisierungsbox-glasfasermodem-kaufen) (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](https://www.electronicsdatasheets.com/download/51c42036e34e246e4900009c.pdf?format=pdf), [link to OpenWRT forums discussion on Huawei SFP module based on the same SoC](https://forum.openwrt.org/t/support-ma5671a-sfp-gpon/48042).
## 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:
```sql
-- 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.
```sql
-- ...
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:
```bash
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](https://openwrt.org):
```bash
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:
- `onu gtcpg`: Retrieve password
- `onu gtcsng`: Retrieve serial number
- `onu gtcsns`: Set serial number
### Connection state
**Connected** (`curr_state=5`)
```bash
admin@SFP:~# onu ploamsg
errorcode=0 curr_state=5
```
**Disconnected** (`curr_state=1`):
```bash
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 its 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!
Its 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
- [ ] Prometheus exporter
- [ ] Integrate into OpenWRT
================================================
FILE: flashdumps/binwalk.out
================================================
Scan Time: 2022-02-14 09:07:48
Target File: /Users/xvzf/gh/xvzf/zyxel-gpon-sfp/flashdumps/mtd0
MD5 Checksum: 073455446a6d5f611cb083897f596c2a
Signatures: 411
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
173708 0x2A68C CRC32 polynomial table, little endian
Scan Time: 2022-02-14 09:07:48
Target File: /Users/xvzf/gh/xvzf/zyxel-gpon-sfp/flashdumps/mtd1
MD5 Checksum: 9e8a8ec86a89b832696fc75b5202f317
Signatures: 411
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
Scan Time: 2022-02-14 09:07:48
Target File: /Users/xvzf/gh/xvzf/zyxel-gpon-sfp/flashdumps/mtd2
MD5 Checksum: 1ccc7807624108778f53ea5599acbafa
Signatures: 411
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
512 0x200 uImage header, header size: 64 bytes, header CRC: 0x4839EC66, created: 2017-07-12 04:01:47, image size: 1184511 bytes, Data Address: 0x80002000, Entry Point: 0x80002000, data CRC: 0xD4AF7C71, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "OpenWrtLinux-3.10.12-svn"
576 0x240 LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 3481980 bytes
1310720 0x140000 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 2289342 bytes, 727 inodes, blocksize: 262144 bytes, created: 2021-05-08 03:28:38
Scan Time: 2022-02-14 09:07:48
Target File: /Users/xvzf/gh/xvzf/zyxel-gpon-sfp/flashdumps/mtd3
MD5 Checksum: 1ccc7807624108778f53ea5599acbafa
Signatures: 411
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
512 0x200 uImage header, header size: 64 bytes, header CRC: 0x4839EC66, created: 2017-07-12 04:01:47, image size: 1184511 bytes, Data Address: 0x80002000, Entry Point: 0x80002000, data CRC: 0xD4AF7C71, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "OpenWrtLinux-3.10.12-svn"
576 0x240 LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 3481980 bytes
1310720 0x140000 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 2289342 bytes, 727 inodes, blocksize: 262144 bytes, created: 2021-05-08 03:28:38
Scan Time: 2022-02-14 09:07:49
Target File: /Users/xvzf/gh/xvzf/zyxel-gpon-sfp/flashdumps/mtd4
MD5 Checksum: 7a870b94b05dc6d2a37212fc0c8fa6e8
Signatures: 411
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JFFS2 filesystem, big endian
Scan Time: 2022-02-14 09:07:49
Target File: /Users/xvzf/gh/xvzf/zyxel-gpon-sfp/flashdumps/mtd5
MD5 Checksum: 40f25c5f48fbf1df0286e3a80bc05911
Signatures: 411
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
Scan Time: 2022-02-14 09:07:49
Target File: /Users/xvzf/gh/xvzf/zyxel-gpon-sfp/flashdumps/mtd6
MD5 Checksum: 08c5b23a7cf0a1d97dcdca96ceb62c5b
Signatures: 411
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 2289342 bytes, 727 inodes, blocksize: 262144 bytes, created: 2021-05-08 03:28:38
Scan Time: 2022-02-14 09:07:49
Target File: /Users/xvzf/gh/xvzf/zyxel-gpon-sfp/flashdumps/mtd7
MD5 Checksum: 41d2e2c0c0edfccf76fa1c3e38bc1cf2
Signatures: 411
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
Scan Time: 2022-02-14 09:07:49
Target File: /Users/xvzf/gh/xvzf/zyxel-gpon-sfp/flashdumps/sha256.sum
MD5 Checksum: 934ac986d9125173b5478f23e37f84dc
Signatures: 411
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
================================================
FILE: flashdumps/mtd5
================================================
contains serial number, PLOAM etc
================================================
FILE: flashdumps/mtd7
================================================
================================================
FILE: flashdumps/sha256.sum
================================================
e7abc5af0c769c7a5f94fc54467b8c44c9a704b1b6c7390e113a5eee9cc89582 mtd0
2e5f53b7435d2cc6be28a4c4a1ce10797a499a903ef79ff0bd7d44cb25d626b3 mtd1
513ecc010db160f3ead5938dc2835eaa298d4aa390c5e4fdb7b8855b45067646 mtd2
513ecc010db160f3ead5938dc2835eaa298d4aa390c5e4fdb7b8855b45067646 mtd3
4a8f38484ae405693807c198d6dcf51037bddce5991d11d9d4a98b796ea269d2 mtd4
720eb0caef7a4a6b5796f235b40ed0085b1af439f28592205f50abe37d7bf9c5 mtd5
81e085ea6601838740e728b9310e6b62da54cbcbf42b02a2805625b6db3c5878 mtd6
b5a41c3758763bbec72769fab4a2533bf2db0b6312d93d25a695f9e4b9e02260 mtd7
================================================
FILE: requirements.txt
================================================
demjson==1.6
fire==0.4.0
requests==2.27.1
================================================
FILE: zyxel_gpon_sfp.py
================================================
import fire
import requests
import random
import demjson
from binascii import hexlify
def is_hex(a):
""" checks if a is a properly formated hex string """
try:
int(a, 16) # Just check if we can properly parse the hex
return len(a) % 2 == 0 # We're dealing with strings hex encoded
except ValueError:
return False
class SFP:
def __init__(self, sfp_addr, username="admin", password="1234"):
self._sfp_addr = sfp_addr
self._user = username
self._pass = password
def _req(self, path, method="GET", headers={}):
""" Helper to perform request on the SFPs HTTP API """
full_path = f"{self._sfp_addr}{path}"
auth = (self._user, self._pass)
if method == "GET":
return requests.get(full_path, auth=auth, headers=headers)
elif method == "POST":
return requests.post(full_path, auth=auth, headers=headers)
def info(self):
resp_sn = self._req(path="/cgi/get_sn")
resp_gpon = self._req(path="/cgi/get_gpon_info")
# WARNING: This is a javascript object, not JSON...
test = demjson.decode(resp_sn.text) | demjson.decode(resp_gpon.text)
return test
def set_slid(self, slid, string=False):
# Transform to a valid parameter
_slid = hexlify(slid).decode(
"ascii").lower() if string else slid.lower()
if not is_hex(_slid):
return f"[!] Invalid SLID `{_slid}` (HEX)"
print(f"[ ] Applying SLID `{_slid}` (HEX)")
resp = self._req(
path=f"/cgi/set_sn?mode=1&pass={_slid}", method="POST")
if resp.status_code == 200 and resp.text == "1":
return f"[+] Applied SLID `{_slid}`, a reboot of the SFP is required."
def set_sn(self, sn, string):
return "Untested"
# _sn = hexlify(sn) if string else sn
# f not is_hex(_sn):
# return f"[!] Invalid SN `{_sn}` (HEX)"
# print(f"[ ] Applying SN `{_sn}` (HEX)")
# resp = self._req(path=f"/cgi/set_sn?mode=1?sn={sn}", method="POST")
# if resp.status_code == 200 and resp.text == "1":
# return f"[+] Applied SN `{_sn}`, a reboot of the SFP is required."
if __name__ == "__main__":
fire.Fire(SFP)
gitextract_nqxjjm6j/ ├── .gitignore ├── Readme.md ├── flashdumps/ │ ├── binwalk.out │ ├── mtd0 │ ├── mtd1 │ ├── mtd2 │ ├── mtd3 │ ├── mtd4 │ ├── mtd5 │ ├── mtd6 │ ├── mtd7 │ └── sha256.sum ├── requirements.txt └── zyxel_gpon_sfp.py
SYMBOL INDEX (7 symbols across 1 files)
FILE: zyxel_gpon_sfp.py
function is_hex (line 8) | def is_hex(a):
class SFP (line 17) | class SFP:
method __init__ (line 18) | def __init__(self, sfp_addr, username="admin", password="1234"):
method _req (line 23) | def _req(self, path, method="GET", headers={}):
method info (line 33) | def info(self):
method set_slid (line 42) | def set_slid(self, slid, string=False):
method set_sn (line 56) | def set_sn(self, sn, string):
Condensed preview — 14 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (21K chars).
[
{
"path": ".gitignore",
"chars": 6,
"preview": "venv/\n"
},
{
"path": "Readme.md",
"chars": 12960,
"preview": "# \"Hacking\" the (Telekom) Zyxel GPON SFP module (PMG3000-D20B)\n> The SFP can be sourced very easily and is widely availa"
},
{
"path": "flashdumps/binwalk.out",
"chars": 4267,
"preview": "\nScan Time: 2022-02-14 09:07:48\nTarget File: /Users/xvzf/gh/xvzf/zyxel-gpon-sfp/flashdumps/mtd0\nMD5 Checksum: 073"
},
{
"path": "flashdumps/mtd5",
"chars": 34,
"preview": "contains serial number, PLOAM etc\n"
},
{
"path": "flashdumps/mtd7",
"chars": 0,
"preview": ""
},
{
"path": "flashdumps/sha256.sum",
"chars": 568,
"preview": "e7abc5af0c769c7a5f94fc54467b8c44c9a704b1b6c7390e113a5eee9cc89582 mtd0\n2e5f53b7435d2cc6be28a4c4a1ce10797a499a903ef79ff0b"
},
{
"path": "requirements.txt",
"chars": 42,
"preview": "demjson==1.6\nfire==0.4.0\nrequests==2.27.1\n"
},
{
"path": "zyxel_gpon_sfp.py",
"chars": 2279,
"preview": "import fire\nimport requests\nimport random\nimport demjson\nfrom binascii import hexlify\n\n\ndef is_hex(a):\n \"\"\" checks if"
}
]
// ... and 6 more files (download for full content)
About this extraction
This page contains the full source code of the xvzf/zyxel-gpon-sfp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 14 files (19.7 KB), approximately 6.6k tokens, and a symbol index with 7 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.