Full Code of xvzf/zyxel-gpon-sfp for AI

main 187435b0cb1c cached
14 files
19.7 KB
6.6k tokens
7 symbols
1 requests
Download .txt
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)
Download .txt
gitextract_nqxjjm6j/

├── .gitignore
├── Readme.md
├── flashdumps/
│   ├── binwalk.out
│   ├── mtd0
│   ├── mtd1
│   ├── mtd2
│   ├── mtd3
│   ├── mtd4
│   ├── mtd5
│   ├── mtd6
│   ├── mtd7
│   └── sha256.sum
├── requirements.txt
└── zyxel_gpon_sfp.py
Download .txt
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.

Copied to clipboard!