pax_global_header00006660000000000000000000000064147606660550014531gustar00rootroot0000000000000052 comment=e260fd6efe6dd195dc5f8a0aa80861eb5a7a2f70 ap51-flash-2025.0/000077500000000000000000000000001476066605500134625ustar00rootroot00000000000000ap51-flash-2025.0/.gitattributes000066400000000000000000000003151476066605500163540ustar00rootroot00000000000000# SPDX-License-Identifier: CC0-1.0 # SPDX-FileCopyrightText: Sven Eckelmann * text=auto *.c text diff=cpp *.h text diff=cpp *.rst text *.py text diff=python *.rst text Makefile text ap51-flash-2025.0/.github/000077500000000000000000000000001476066605500150225ustar00rootroot00000000000000ap51-flash-2025.0/.github/pull_request_template000066400000000000000000000006371476066605500213720ustar00rootroot00000000000000Thanks for your contribution to ap51-flash! * Prefer small & digestible over long "all in one" patches. * Add a "Signed-off-by: Your Name " line to the patch message to make the ownership of the patch clear. * use "ap51-flash: " as prefix in your commit message's subject * make sure that you have updated docs/ to reflect your changes Please remove this message before posting the pull request. ap51-flash-2025.0/.github/pull_request_template.license000066400000000000000000000002461476066605500230070ustar00rootroot00000000000000# SPDX-License-Identifier: CC0-1.0 # SPDX-FileCopyrightText: Marek Lindner # SPDX-FileCopyrightText: Sven Eckelmann ap51-flash-2025.0/.github/workflows/000077500000000000000000000000001476066605500170575ustar00rootroot00000000000000ap51-flash-2025.0/.github/workflows/build-linux.yml000066400000000000000000000014051476066605500220360ustar00rootroot00000000000000# SPDX-FileCopyrightText: Sven Eckelmann # # SPDX-License-Identifier: CC0-1.0 name: Build Linux on: [push, pull_request] jobs: run: strategy: matrix: cc: ["gcc", "clang"] cflags: ["", "-DDEBUG", "-DCLEAR_SCREEN", "-DCLEAR_SCREEN -DDEBUG"] runs-on: ubuntu-latest steps: - name: Install dependencies run: | sudo apt update sudo apt install -y binutils libc-bin - uses: actions/checkout@v3 - name: Test run: | dd if=/dev/urandom of=test.img count=10000 bs=1024 make clean V=s && make CC="${{ matrix.cc }}" V=s make clean V=s && make CC="${{ matrix.cc }}" EMBED_CI="test.img" V=s env: CFLAGS: ${{ matrix.cflags }} ap51-flash-2025.0/.github/workflows/build-macos.yml000066400000000000000000000011161476066605500220000ustar00rootroot00000000000000# SPDX-FileCopyrightText: Sven Eckelmann # # SPDX-License-Identifier: CC0-1.0 name: Build MacOS on: [push, pull_request] jobs: run: runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: Test run: | dd if=/dev/urandom of=test.img count=10000 bs=1024 make clean V=s && make V=s ap51-flash-osx make clean V=s && CPPFLAGS="-DDEBUG" make V=s ap51-flash-osx make clean V=s && make V=s EMBED_CI="test.img" ap51-flash-osx make clean V=s && CFLAGS="-flto -O2" make V=s ap51-flash-osx ap51-flash-2025.0/.github/workflows/build-mingw64.yml000066400000000000000000000035511476066605500221760ustar00rootroot00000000000000# SPDX-FileCopyrightText: Sven Eckelmann # # SPDX-License-Identifier: CC0-1.0 name: Build MinGW-w64 on: [push, pull_request] jobs: run: runs-on: ubuntu-latest steps: - name: Install dependencies run: | sudo apt update sudo apt install -y curl unzip mingw-w64-tools gcc-mingw-w64 - uses: actions/checkout@v3 - name: Get WinPCAP run: | curl https://npcap.com/dist/npcap-sdk-1.12.zip -o npcap-sdk-1.12.zip unzip npcap-sdk-1.12.zip -d npcap-sdk rm -f npcap-sdk-1.12.zip mkdir -p npcap-sdk/Lib-delayed cat > npcap-sdk/Lib-delayed/wpcap.def < # # SPDX-License-Identifier: CC0-1.0 name: Build musl on: [push, pull_request] jobs: run: runs-on: ubuntu-latest steps: - name: Install dependencies run: | sudo apt update sudo apt install -y git musl-tools - uses: actions/checkout@v3 - name: Get kernel-headers run: | git clone --depth=1 https://github.com/sabotage-linux/kernel-headers -b v3.12.6-5 kernel-headers - name: Test run: | make clean V=s && make V=s CC=musl-gcc && mv ap51-flash ap51-flash-x86_64-linux env: CPPFLAGS: "-Ikernel-headers/x86/include/" CFLAGS: "-static -m64 -flto -O2" DFLAGS: "-Wl,-m -Wl,elf_x86_64" ap51-flash-2025.0/.github/workflows/build-mxe-npcap-legacy.yml000066400000000000000000000035401476066605500240330ustar00rootroot00000000000000# SPDX-FileCopyrightText: Sven Eckelmann # # SPDX-License-Identifier: CC0-1.0 name: Build MXE Npcap WinPcap mode on: [push, pull_request] jobs: run: runs-on: ubuntu-20.04 steps: - name: add mxe deb repository uses: myci-actions/add-deb-repo@10 with: repo: deb http://pkg.mxe.cc/repos/apt/ focal main repo-name: mxe key-server: keyserver.ubuntu.com keys: C6BF758A33A3A276 - name: Install dependencies run: | sudo apt update sudo apt install -y curl unzip mxe-i686-w64-mingw32.static-gcc mxe-i686-w64-mingw32.static-pkgconf - uses: actions/checkout@v3 - name: Get WinPCAP run: | curl https://npcap.com/dist/npcap-sdk-1.12.zip -o npcap-sdk-1.12.zip unzip npcap-sdk-1.12.zip -d npcap-sdk rm -f npcap-sdk-1.12.zip - name: Test run: | dd if=/dev/urandom of=test.img count=10000 bs=1024 export PATH="/usr/lib/mxe/usr/bin/:$PATH" make clean V=s && make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe make clean V=s && CPPFLAGS="-DCLEAR_SCREEN" make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe make clean V=s && CPPFLAGS="-DCLEAR_SCREEN -DDEBUG" make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe make clean V=s && CPPFLAGS="-DDEBUG" make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe make clean V=s && make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" EMBED_CI="test.img" ap51-flash.exe make clean V=s && CFLAGS="-flto -O2" make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe && mv ap51-flash.exe ap51-flash-i686-winpcap.exe env: MXE_CPU: i686 WINPCAP_LDLIBS: "-Lnpcap-sdk/Lib/ -lwpcap" WINPCAP_CFLAGS: "-Inpcap-sdk/Include/" ap51-flash-2025.0/.github/workflows/build-mxe-npcap.yml000066400000000000000000000045201476066605500225700ustar00rootroot00000000000000# SPDX-FileCopyrightText: Sven Eckelmann # # SPDX-License-Identifier: CC0-1.0 name: Build MXE Npcap mordern mode on: [push, pull_request] jobs: run: runs-on: ubuntu-20.04 steps: - name: add mxe deb repository uses: myci-actions/add-deb-repo@10 with: repo: deb http://pkg.mxe.cc/repos/apt/ focal main repo-name: mxe key-server: keyserver.ubuntu.com keys: C6BF758A33A3A276 - name: Install dependencies run: | sudo apt update sudo apt install -y curl unzip mxe-i686-w64-mingw32.static-gcc mxe-i686-w64-mingw32.static-pkgconf - uses: actions/checkout@v3 - name: Get WinPCAP run: | curl https://npcap.com/dist/npcap-sdk-1.12.zip -o npcap-sdk-1.12.zip unzip npcap-sdk-1.12.zip -d npcap-sdk rm -f npcap-sdk-1.12.zip mkdir -p npcap-sdk/Lib-delayed cat > npcap-sdk/Lib-delayed/wpcap.def < # # SPDX-License-Identifier: CC0-1.0 name: Build MXE WinPCAP on: [push, pull_request] jobs: run: runs-on: ubuntu-20.04 steps: - name: add mxe deb repository uses: myci-actions/add-deb-repo@10 with: repo: deb http://pkg.mxe.cc/repos/apt/ focal main repo-name: mxe key-server: keyserver.ubuntu.com keys: C6BF758A33A3A276 - name: Install dependencies run: | sudo apt update sudo apt install -y curl unzip mxe-i686-w64-mingw32.static-gcc mxe-i686-w64-mingw32.static-pkgconf - uses: actions/checkout@v3 - name: Get WinPCAP run: | curl https://www.winpcap.org/install/bin/WpdPack_4_1_2.zip -o WpdPack_4_1_2.zip unzip WpdPack_4_1_2.zip rm -f WpdPack_4_1_2.zip - name: Test run: | dd if=/dev/urandom of=test.img count=10000 bs=1024 export PATH="/usr/lib/mxe/usr/bin/:$PATH" make clean V=s && make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe make clean V=s && CPPFLAGS="-DCLEAR_SCREEN" make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe make clean V=s && CPPFLAGS="-DCLEAR_SCREEN -DDEBUG" make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe make clean V=s && CPPFLAGS="-DDEBUG" make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe make clean V=s && make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" EMBED_CI="test.img" ap51-flash.exe make clean V=s && CFLAGS="-flto -O2" make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe && mv ap51-flash.exe ap51-flash-i686-winpcap.exe env: MXE_CPU: i686 WINPCAP_LDLIBS: "-LWpdPack/Lib/ -lwpcap" WINPCAP_CFLAGS: "-IWpdPack/Include/" ap51-flash-2025.0/.github/workflows/build-mxe.yml000066400000000000000000000030201476066605500214630ustar00rootroot00000000000000# SPDX-FileCopyrightText: Sven Eckelmann # # SPDX-License-Identifier: CC0-1.0 name: Build MXE on: [push, pull_request] jobs: run: runs-on: ubuntu-20.04 steps: - name: add mxe deb repository uses: myci-actions/add-deb-repo@10 with: repo: deb http://pkg.mxe.cc/repos/apt/ focal main repo-name: mxe key-server: keyserver.ubuntu.com keys: C6BF758A33A3A276 - name: Install dependencies run: | sudo apt update sudo apt install -y mxe-i686-w64-mingw32.static-gcc mxe-i686-w64-mingw32.static-winpcap - uses: actions/checkout@v3 - name: Test run: | dd if=/dev/urandom of=test.img count=10000 bs=1024 export PATH="/usr/lib/mxe/usr/bin/:$PATH" make clean V=s && make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe make clean V=s && CPPFLAGS="-DCLEAR_SCREEN" make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe make clean V=s && CPPFLAGS="-DCLEAR_SCREEN -DDEBUG" make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe make clean V=s && CPPFLAGS="-DDEBUG" make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe make clean V=s && make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" EMBED_CI="test.img" ap51-flash.exe make clean V=s && CFLAGS="-flto -O2" make V=s CROSS="${MXE_CPU}-w64-mingw32.static-" ap51-flash.exe && mv ap51-flash.exe ap51-flash-i686-static.exe env: MXE_CPU: i686 ap51-flash-2025.0/.github/workflows/codeql-analysis.yml000066400000000000000000000016311476066605500226730ustar00rootroot00000000000000# SPDX-License-Identifier: CC0-1.0 # SPDX-FileCopyrightText: Sven Eckelmann name: "CodeQL" on: push: branches: [ main ] pull_request: # The branches below must be a subset of the branches above branches: [ main ] schedule: - cron: '15 23 * * 5' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'cpp' ] steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} - name: Autobuild uses: github/codeql-action/autobuild@v1 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 ap51-flash-2025.0/.github/workflows/reuse.yml000066400000000000000000000005151476066605500207260ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. # # SPDX-License-Identifier: CC0-1.0 name: REUSE Compliance Check on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: REUSE Compliance Check uses: fsfe/reuse-action@v1 ap51-flash-2025.0/.gitignore000066400000000000000000000003271476066605500154540ustar00rootroot00000000000000# SPDX-License-Identifier: CC0-1.0 # SPDX-FileCopyrightText: Sven Eckelmann *.d *.o /ap51-flash /ap51-flash*.exe /ap51-flash-osx /ap51-flash-res /docs/_build/ /npcap-sdk/ /WpdPack/ /test.img ap51-flash-2025.0/CHANGELOG.rst000066400000000000000000000034631476066605500155110ustar00rootroot00000000000000.. SPDX-License-Identifier: CC0-1.0 .. SPDX-FileCopyrightText: Sven Eckelmann 2025.0 (2025-03-01) =================== * fixed maximum size of fwupgrade.cfg file * coding style cleanups and refactoring * added support for: - Plasma Cloud PAX5400 2022.1 (2022-12-31) =================== * Refactoring of tftp-server code * added support for: - Plasma Cloud PAX1800 v2 2022.0 (2022-03-16) =================== * Improve listing of ethernet devices under Linux * Add support for modern Npcap DLLs * Fix embedding of images with modern GCC versions * coding style cleanups and refactoring * added support for: - Datto AP440 - Datto AP840 - Datto AP840E - Datto TW420 - Plasma Cloud PAX1800 2019.0.1 (2019-09-14) ===================== * Fix support for flashing of embedded images 2019.0 (2019-08-30) =================== * improved Zyxel firmware detection * introduced optional MAC address filtering * added support for: - Plasma Cloud PA300 - Plasma Cloud PA1200 - Plasma Cloud PA2200 2018.0 (2018-06-19) =================== * added support for: - Alfa Network AP121F - Dlink DIR-300 - Engenius EOC-1650 - Engenius EOC-2610 - Engenius EOC-2610p - Engenius 3660 - FON La Fonera (2100) - Open Mesh A40 - Open Mesh A42 - Open Mesh A60 - Open Mesh A62 - Open Mesh D200 - Open Mesh G200 - Open Mesh MR500 - Open Mesh MR600 (v1, v2) - Open Mesh MR900 (v1, v2) - Open Mesh MR1750 (v1, v2) - Open Mesh OM1P - Open Mesh OM2P (v1, v2, v4) - Open Mesh OM2P-HS (v1, v2, v3, v4) - Open Mesh OM2P-LC - Open Mesh OM5P - Open Mesh OM5P-AN - Open Mesh OM5P-AC (v1, v2) - Ubiquiti Bullet2 & HP - Ubiquiti NanoStation2 - Ubiquiti NanoStation5 - Ubiquiti Pico2 & HP - Ubiquiti RouterStation - UniAppliance Colibrì (!UniData) - Zyxel NBG6817 ap51-flash-2025.0/LICENSES/000077500000000000000000000000001476066605500146675ustar00rootroot00000000000000ap51-flash-2025.0/LICENSES/CC0-1.0.txt000066400000000000000000000154041476066605500162750ustar00rootroot00000000000000Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. ap51-flash-2025.0/LICENSES/GPL-3.0-or-later.txt000066400000000000000000001032461476066605500201010ustar00rootroot00000000000000GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ap51-flash-2025.0/LICENSES/MIT.txt000066400000000000000000000021241476066605500160600ustar00rootroot00000000000000MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ap51-flash-2025.0/Makefile000066400000000000000000000071641476066605500151320ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: Marek Lindner # enable debug output # CPPFLAGS += -DDEBUG # clear screen after each subsequent flash # CPPFLAGS += -DCLEAR_SCREEN # define $EMBED_IMG=/path/to/image to have your image included # into the binary where $EMBED_IMG is one of the following: # * EMBED_CI # * EMBED_CE # * EMBED_UBNT # * EMBED_UBOOT # * EMBED_ZYXEL BINARY_NAME = ap51-flash OBJ += commandline.o OBJ += flash.o OBJ += fwcfg.o OBJ += proto.o OBJ += router_images.o OBJ += router_redboot.o OBJ += router_tftp_client.o OBJ += router_tftp_server.o OBJ += router_netconsole.o OBJ += router_types.o OBJ += socket.o AP51_RC = ap51-flash-res BINARY_TARGET_NAMES += $(BINARY_NAME) BINARY_TARGET_NAMES += $(BINARY_NAME).exe BINARY_TARGET_NAMES += $(BINARY_NAME)-osx # ap51-flash flags and options CFLAGS += -Wall -W -std=gnu99 -fno-strict-aliasing $(EXTRA_CFLAGS) -MD -MP CPPFLAGS += -D_GNU_SOURCE LDLIBS += # disable verbose output ifneq ($(findstring $(MAKEFLAGS),s),s) ifndef V Q_CC = @echo ' ' CC $@; Q_LD = @echo ' ' LD $@; Q_SILENT = @ export Q_CC export Q_LD export Q_SILENT endif endif CC = $(CROSS)gcc RM ?= rm -f STRIP = $(CROSS)strip OBJCOPY = $(CROSS)objcopy WINDRES = $(CROSS)windres COMPILE.c = $(Q_CC)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c LINK.o = $(Q_LD)$(CC) $(CFLAGS) $(LDFLAGS) $(TARGET_ARCH) ifeq ($(MAKECMDGOALS),) PLATFORM = LINUX else ifeq ($(MAKECMDGOALS),$(BINARY_NAME)) PLATFORM = LINUX else ifeq ($(MAKECMDGOALS),$(BINARY_NAME).exe) PLATFORM = WIN32 else ifeq ($(MAKECMDGOALS),$(BINARY_NAME)-osx) PLATFORM = OSX endif ifneq ($(PLATFORM),) CPPFLAGS += -D$(PLATFORM) endif ifeq ($(PLATFORM),LINUX) BINARY_SUFFIX = else ifeq ($(PLATFORM),WIN32) BINARY_SUFFIX = .exe CPPFLAGS += -D_CONSOLE -D_MBCS -D__USE_MINGW_ANSI_STDIO=1 ifeq ($(origin PKG_CONFIG), undefined) PKG_CONFIG = $(CROSS)pkg-config ifeq ($(shell which $(PKG_CONFIG) 2>/dev/null),) $(error $(PKG_CONFIG) not found) endif endif ifeq ($(origin WINPCAP_CFLAGS) $(origin WINPCAP_LDLIBS), undefined undefined) WINPCAP_NAME ?= winpcap ifeq ($(shell $(PKG_CONFIG) --modversion $(WINPCAP_NAME) 2>/dev/null),) $(error No $(WINPCAP_NAME) development libraries found!) endif WINPCAP_CFLAGS += $(shell $(PKG_CONFIG) --cflags $(WINPCAP_NAME)) WINPCAP_LDLIBS += $(shell $(PKG_CONFIG) --libs $(WINPCAP_NAME)) endif CFLAGS += $(WINPCAP_CFLAGS) LDLIBS += $(WINPCAP_LDLIBS) else ifeq ($(PLATFORM),OSX) BINARY_SUFFIX = -osx LDLIBS += -lpcap endif EMBEDDED_IMAGES += $(EMBED_CI) EMBEDDED_IMAGES += $(EMBED_CE) EMBEDDED_IMAGES += $(EMBED_UBNT) EMBEDDED_IMAGES += $(EMBED_UBOOT) EMBEDDED_IMAGES += $(EMBED_ZYXEL) Makefile: embed_image.mk include embed_image.mk $(eval $(call embed_image,CI,ci)) $(eval $(call embed_image,CE,ce)) $(eval $(call embed_image,UBNT,ubnt)) $(eval $(call embed_image,UBOOT,uboot)) $(eval $(call embed_image,ZYXEL,zyxel)) # try to generate revision REVISION= $(shell \ if [ -d .git ]; then \ echo $$(git describe --always --dirty --match "v*" |sed 's/^v//' 2> /dev/null || echo "[unknown]"); \ fi) ifneq ($(REVISION),) CPPFLAGS += -DSOURCE_VERSION=\"$(REVISION)\" endif # standard build rules .SUFFIXES: .o .c .c.o: $(COMPILE.c) -o $@ $< all: $(BINARY_NAME)$(BINARY_SUFFIX) $(BINARY_TARGET_NAMES): $(OBJ) $(LINK.o) $^ $(LDLIBS) -o $@ $(STRIP) $@ $(OBJ): Makefile $(AP51_RC).o: $(AP51_RC) $(Q_CC)$(WINDRES) -i $(AP51_RC) -I. -o $@ clean: $(RM) *.o *.d *~ $(BINARY_TARGET_NAMES) $(AP51_RC) # load dependencies DEP = $(OBJ:.o=.d) -include $(DEP) .PHONY: all clean .DELETE_ON_ERROR: .DEFAULT_GOAL := all ap51-flash-2025.0/README.rst000066400000000000000000000017741476066605500151620ustar00rootroot00000000000000.. SPDX-License-Identifier: CC0-1.0 .. SPDX-FileCopyrightText: Sven Eckelmann ========== ap51-flash ========== .. image:: https://img.shields.io/coverity/scan/14627.svg :target: https://scan.coverity.com/projects/ap51-flash-ap51-flash .. image:: https://img.shields.io/readthedocs/ap51-flash.svg :target: https://ap51-flash.readthedocs.io/ .. image:: https://img.shields.io/travis/ap51-flash/ap51-flash/master.svg :target: https://travis-ci.org/ap51-flash/ap51-flash firmware flasher for ethernet connected routers and access points ap51-flash is a tool to simplify the automatic firmware deployment for a multitude of home routers and wireless access points. ap51-flash can identify target device(s), select the correct firmware image and perform the required communication to carry out the installation procedure. It works without the need for a local TFTP server or manual, target device specific network configuration. See https://ap51-flash.readthedocs.io/ for more information. ap51-flash-2025.0/ap51-flash-res.h000066400000000000000000000005201476066605500162600ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-FileCopyrightText: Marek Lindner */ #ifndef __AP51_FLASH_RES_H__ #define __AP51_FLASH_RES_H__ #define IDR_CI_IMG 101 #define IDR_CE_IMG 102 #define IDR_UBNT_IMG 103 #define IDR_UBOOT_IMG 104 #define IDR_ZYXEL_IMG 105 #endif /* __AP51_FLASH_RES_H__ */ ap51-flash-2025.0/ap51-flash.h000066400000000000000000000005051476066605500154740ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Marek Lindner */ #ifndef __AP51_FLASH_AP51_FLASH_H__ #define __AP51_FLASH_AP51_FLASH_H__ #define DESC_MAX_LENGTH 30 #define FILE_NAME_MAX_LENGTH 33 #define FLASH_PAGE_SIZE 0x10000 #endif /* __AP51_FLASH_AP51_FLASH_H__ */ ap51-flash-2025.0/commandline.c000066400000000000000000000076411476066605500161240ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-FileCopyrightText: Marek Lindner */ #include "commandline.h" #include #include #include #include #include "flash.h" #include "router_images.h" #include "router_types.h" #include "socket.h" #ifndef SOURCE_VERSION #define SOURCE_VERSION "2025.0" #endif static void usage(const char *prgname) { fprintf(stderr, "Usage:\n"); fprintf(stderr, "%s interface image\t\t\tflash device with given image\n", prgname); fprintf(stderr, "%s [-m ...] interface image\tflash device at given MAC address(es)\n", prgname); fprintf(stderr, "%s -h\t\t\t\t\tshow usage/help\n", prgname); fprintf(stderr, "%s -v\t\t\t\t\tprints version information\n", prgname); fprintf(stderr, "\nOne or multiple images of the following type can be specified:\n"); router_images_print_desc(); fprintf(stderr, "\nThe interface has to be one of the devices that are part of the supported device list which follows.\nYou can either specify its name or the interface number.\n"); socket_print_all_ifaces(); } #if defined(WIN32) && defined(NPCAP) #include #include static FARPROC WINAPI npcap_load_delayed_failure_hook(unsigned dliNotify, PDelayLoadInfo pdli) { if (dliNotify == dliFailLoadLib) fprintf(stderr, "Unable to load Npcap library: %s\n", pdli->szDll); else if (dliNotify == dliFailGetProc) fprintf(stderr, "Unable to load find procedure %s in %s\n", pdli->dlp.szProcName, pdli->szDll); return FALSE; } static int npcap_load_delayed(void) { _TCHAR npcap_dir[512]; UINT len; len = GetSystemDirectory(npcap_dir, 480); if (!len) { fprintf(stderr, "Error in GetSystemDirectory: %lx", GetLastError()); return -1; } _tcscat_s(npcap_dir, 512, _T("\\Npcap")); if (SetDllDirectory(npcap_dir) == 0) { fprintf(stderr, "Error in SetDllDirectory: %lx", GetLastError()); return -1; } __pfnDliFailureHook2 = npcap_load_delayed_failure_hook; return 0; } #endif int main(int argc, char* argv[]) { bool print_help = false; bool print_version = false; int c; char *iface = NULL; int ret = -1; bool load_embedded = true; const char *progname = "ap51-flash"; static struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'v'}, {"mac", required_argument, 0, 'm'}, {} }; #if defined(WIN32) && defined(NPCAP) npcap_load_delayed(); #endif while ((c = getopt_long(argc, argv, "hvm:", long_options, NULL)) != -1) { switch (c) { case 'h': print_help = true; ret = 0; break; case 'v': print_version = true; ret = 0; break; case 'm': if (mac_allowlist_add(optarg) == 0) break; /* fall-through */ case '?': print_help = true; break; } } progname = argv[0]; if (print_help) { usage(progname); goto out; } if (print_version) { #if defined(EMBEDDED_DESC) printf("ap51-flash (%s) [embedded: %s]\n", SOURCE_VERSION, EMBEDDED_DESC); #else printf("ap51-flash (%s)\n", SOURCE_VERSION); #endif goto out; } argc -= optind; argv += optind; if (argc < 1) { fprintf(stderr, "Error - no interface specified\n"); usage(progname); goto out; } iface = socket_find_iface_by_index(argv[0]); if (!iface) iface = argv[0]; argc -= 1; argv += 1; router_images_init(); while (argc > 0) { ret = router_images_verify_path(argv[0]); if (ret < 0) goto out; argc -= 1; argv += 1; load_embedded = false; } if (load_embedded) { router_images_init_embedded(); } else { #if defined(EMBED_UBOOT) || defined(EMBED_UBNT) || defined(EMBED_CI) || defined(EMBED_CE) || defined(EMBED_ZYXEL) printf("Embedded image disabled\n"); #endif } #if defined(DEBUG) printf("Listening on interface: %s\n", iface); #endif if (!router_images_available()) { fprintf(stderr, "Error - no images specified\n"); usage(progname); goto out; } ret = flash_start(iface); out: return ret; } ap51-flash-2025.0/commandline.h000066400000000000000000000004171476066605500161230ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Marek Lindner */ #ifndef __AP51_FLASH_COMMANDLINE_H__ #define __AP51_FLASH_COMMANDLINE_H__ int main(int argc, char* argv[]); #endif /* __AP51_FLASH_COMMANDLINE_H__ */ ap51-flash-2025.0/compat.h000066400000000000000000000076121476066605500151240ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Marek Lindner */ #ifndef __AP51_FLASH_COMPAT_H__ #define __AP51_FLASH_COMPAT_H__ #if defined(LINUX) #include #include #include #include #include #include #include #include #include #include #include #include #define O_BINARY 0 #define USE_PCAP 0 #elif defined(OSX) #include #include #include #include #include #include #include #define O_BINARY 0 #define USE_PCAP 1 #define ETH_ALEN 6 #define ETH_HLEN 14 #define ETH_DATA_LEN 1500 #elif defined(WIN32) #define USE_PCAP 1 #include #define ntohs(x) __swab16(x) #define htons(x) __swab16(x) #define htonl(x) __swab32(x) #define ntohl(x) __swab32(x) #define ETH_ALEN 6 #define ETH_HLEN 14 #define ETH_DATA_LEN 1500 struct ether_header { uint8_t ether_dhost[ETH_ALEN]; /* destination eth addr */ uint8_t ether_shost[ETH_ALEN]; /* source ether addr */ uint16_t ether_type; /* packet type ID field */ } __attribute__((packed)); struct arphdr { uint16_t ar_hrd; /* format of hardware address */ uint16_t ar_pro; /* format of protocol address */ uint8_t ar_hln; /* length of hardware address */ uint8_t ar_pln; /* length of protocol address */ uint16_t ar_op; /* ARP opcode (command) */ } __attribute__((packed)); struct ether_arp { struct arphdr ea_hdr; /* fixed-size header */ uint8_t arp_sha[ETH_ALEN]; /* sender hardware address */ uint8_t arp_spa[4]; /* sender protocol address */ uint8_t arp_tha[ETH_ALEN]; /* target hardware address */ uint8_t arp_tpa[4]; /* target protocol address */ } __attribute__((packed)); #endif #ifndef IPPORT_TFTP #define IPPORT_TFTP 69 #endif #ifndef IPPORT_ICMP #define IPPORT_ICMP 1 #endif #if USE_PCAP #define ETH_P_IP 0x0800 #define ETH_P_ARP 0x0806 #define IPPROTO_TCP 6 #define IPPROTO_UDP 17 #define IPPORT_TFTP 69 #define ARPOP_REQUEST 1 #define ARPOP_REPLY 2 #define __swab16(x) ((uint16_t)( \ (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \ (((uint16_t)(x) & (uint16_t)0xff00U) >> 8))) #define __swab32(x) ((uint32_t)( \ (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \ (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \ (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \ (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24))) #define iphdr iphdr_linux #define udphdr udphdr_linux #define tcphdr tcphdr_linux #define icmphdr icmphdr_linux struct iphdr_linux { uint8_t ihl:4; uint8_t version:4; uint8_t tos; uint16_t tot_len; uint16_t id; uint16_t frag_off; uint8_t ttl; uint8_t protocol; uint16_t check; uint32_t saddr; uint32_t daddr; }; struct udphdr_linux { uint16_t source; uint16_t dest; uint16_t len; uint16_t check; }; struct tcphdr_linux { uint16_t source; uint16_t dest; uint32_t seq; uint32_t ack_seq; uint16_t res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1; uint16_t window; uint16_t check; uint16_t urg_ptr; }; struct icmphdr_linux { uint8_t type; /* message type */ uint8_t code; /* type sub-code */ uint16_t checksum; union { struct { uint16_t id; uint16_t sequence; } echo; /* echo datagram */ uint32_t gateway; /* gateway address */ struct { uint16_t __unused; uint16_t mtu; } frag; /* path mtu discovery */ } un; }; #endif #endif /* __AP51_FLASH_COMPAT_H__ */ ap51-flash-2025.0/contrib/000077500000000000000000000000001476066605500151225ustar00rootroot00000000000000ap51-flash-2025.0/contrib/iwyu/000077500000000000000000000000001476066605500161175ustar00rootroot00000000000000ap51-flash-2025.0/contrib/iwyu/compat_map.iwyu000066400000000000000000000036711476066605500211650ustar00rootroot00000000000000[ { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, { include: [ "", "private", "\"compat.h\"", "public" ] }, ] ap51-flash-2025.0/contrib/iwyu/compat_map.iwyu.license000066400000000000000000000001351476066605500225760ustar00rootroot00000000000000SPDX-License-Identifier: CC0-1.0 SPDX-FileCopyrightText: Sven Eckelmann ap51-flash-2025.0/docs/000077500000000000000000000000001476066605500144125ustar00rootroot00000000000000ap51-flash-2025.0/docs/Makefile000066400000000000000000000005741476066605500160600ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: Sven Eckelmann SPHINXOPTS = SPHINXBUILD = sphinx-build SOURCEDIR = . BUILDDIR = _build help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile ap51-flash-2025.0/docs/conf.py000066400000000000000000000114531476066605500157150ustar00rootroot00000000000000# -*- coding: utf-8 -*- # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: Sven Eckelmann # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Project information ----------------------------------------------------- project = u'ap51-flash' copyright = u'ap51-flash Team' author = u'ap51-flash Team' # The short X.Y version version = u'2025.0' # The full version, including alpha/beta/rc tags release = u'2025.0' # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # The default sidebars (for documents that don't match any pattern) are # defined by theme itself. Builtin themes are using these templates by # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', # 'searchbox.html']``. # # html_sidebars = {} # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = 'ap51-flashdoc' # -- Options for LaTeX output ------------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'ap51-flash.tex', u'ap51-flash Documentation', u'ap51-flash Team', 'manual'), ] # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'ap51-flash', u'ap51-flash Documentation', [author], 1) ] # -- Options for Texinfo output ---------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'ap51-flash', u'ap51-flash Documentation', author, 'ap51-flash', 'One line description of project.', 'Miscellaneous'), ] # -- Options for Epub output ------------------------------------------------- # Bibliographic Dublin Core info. epub_title = project # The unique identifier of the text. This can be a ISBN number # or the project homepage. # # epub_identifier = '' # A unique identification for the text. # # epub_uid = '' # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] ap51-flash-2025.0/docs/index.rst000066400000000000000000000004461476066605500162570ustar00rootroot00000000000000.. SPDX-License-Identifier: GPL-3.0-or-later .. SPDX-FileCopyrightText: Marek Lindner .. SPDX-FileCopyrightText: Sven Eckelmann ========== ap51-flash ========== .. toctree:: :maxdepth: 2 :caption: Contents: supported-devices/index ap51-flash-2025.0/docs/supported-devices/000077500000000000000000000000001476066605500200575ustar00rootroot00000000000000ap51-flash-2025.0/docs/supported-devices/index.rst000066400000000000000000000033221476066605500217200ustar00rootroot00000000000000.. SPDX-License-Identifier: GPL-3.0-or-later .. SPDX-FileCopyrightText: Saverio Proto .. SPDX-FileCopyrightText: Marek Lindner .. SPDX-FileCopyrightText: Antonio Quartulli .. SPDX-FileCopyrightText: Sven Eckelmann ================= Supported devices ================= In general you can flash all devices which use the redboot bootloader that has the reflashing function enabled. ap51-flash will automatically detect redboot when you turn on the device. If your device is not in the "known to work" list which follows you still can try it and inform us about the result. ap51-flash also supports plain tftp flashing without redboot. This method is quite common amongst a variety of devices. Tested ====== * Alfa Network - AP121F * Datto - AP440 - AP840 - AP840E - TW420 * Dlink - DIR-300 (after installing a reflash-enabled redboot) * Engenius - EOC-1650 - EOC-2610 - EOC-2610p - 3660 * FON - La Fonera (2100) * Open Mesh - A40 - A42 - A60 - A62 - D200 - G200 - MR500 - MR600 (v1, v2) - MR900 (v1, v2) - MR1750 (v1, v2) - OM1P - OM2P (v1, v2, v4) - OM2P-HS (v1, v2, v3, v4) - OM2P-LC - OM5P - OM5P-AN - OM5P-AC (v1, v2) * Plasma Cloud - PA300 - PA1200 - PA2200 - PAX1800 (v1, v2) - PAX5400 * Ubiquiti - Bullet2 & HP - NanoStation2 - NanoStation5 - Pico2 & HP - RouterStation * UniAppliance - Colibrì (!UniData) * Zyxel - NBG6817 Other Devices ============= There are many different devices out there which differ slighty in their way of doing things. Even if your device does not work out of the box it might require only small changes to support it. ap51-flash-2025.0/embed_image.mk000066400000000000000000000026421476066605500162350ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: Marek Lindner # SPDX-FileCopyrightText: Sven Eckelmann $(AP51_RC):: Makefile $(Q_SILENT)echo '#include "ap51-flash-res.h"' > $(AP51_RC) ifneq ($(filter-out ,$(EMBEDDED_IMAGES)),) ifeq ($(PLATFORM),WIN32) OBJ += $(AP51_RC).o endif ifeq ($(PLATFORM),LINUX) ifeq ($(OBJCP_OUT),) ifeq ($(shell getconf LONG_BIT),64) OBJCP_OUT = elf64-x86-64 else OBJCP_OUT = elf32-i386 endif endif endif ifneq ($(DESC),) CPPFLAGS += -DEMBEDDED_DESC=\"$(DESC)\" endif endif # automatically generate embedding images via: # $(call embed_image,TYPE_UPPER,TYPE_LOWER)) define embed_image ifneq ($(EMBED_$(1)),) EMBED_$(1)_SYM = _binary_$(shell echo $(EMBED_$(1)) | sed 's@[-/.]@_@g') CPPFLAGS += -DEMBED_$(1) ifeq ($(PLATFORM),LINUX) OBJ += img_$(2).o img_$(2).o: $(EMBED_$(1)) $(Q_CC)$(OBJCOPY) -B i386 -I binary $(EMBED_$(1)) -O $(OBJCP_OUT) \ --redefine-sym $$(EMBED_$(1)_SYM)_start=_binary_img_$(2)_start \ --redefine-sym $$(EMBED_$(1)_SYM)_end=_binary_img_$(2)_end \ --strip-symbol $$(EMBED_$(1)_SYM)_size img_$(2).o else ifeq ($(PLATFORM),WIN32) $(AP51_RC):: $(EMBED_$(1)) $(Q_SILENT)[ -z "$(EMBED_$(1))" ] || echo 'IDR_$(1)_IMG RCDATA DISCARDABLE "$(EMBED_$(1))"' >> $(AP51_RC) else ifeq ($(PLATFORM),OSX) LDFLAGS += -sectcreate __DATA _binary_img_$(2) $(EMBED_$(1)) endif endif endef # embed_image ap51-flash-2025.0/flash.c000066400000000000000000000103721476066605500147260ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-FileCopyrightText: Marek Lindner */ #include "flash.h" #include #include #include #include #include "compat.h" #include "list.h" #include "proto.h" #include "router_tftp_client.h" #include "router_types.h" #include "socket.h" static int running = 1; static DECLARE_LIST_HEAD(node_list); static uint8_t our_mac[] = {0x00, 0xba, 0xbe, 0xca, 0xff, 0x00}; #if defined(CLEAR_SCREEN) int num_nodes_flashed = 0; #endif #define PACKET_BUFF_LEN 2000 #define READ_SLEEP_SEC 0 #define READ_SLEEP_USEC 250000 struct node *node_list_get(const uint8_t *mac_addr) { struct node *node; list_for_each_entry(node, &node_list, list) { if (memcmp(node->his_mac_addr, mac_addr, ETH_ALEN) != 0) continue; return node; } node = malloc(sizeof(struct node) + router_types_priv_size); if (!node) return NULL; memset(node, 0, sizeof(struct node) + router_types_priv_size); memcpy(node->his_mac_addr, mac_addr, ETH_ALEN); node->image_state.fd = -1; list_add(&node->list, &node_list); return node; } static void node_list_free(void) { struct node *node, *node_s; list_for_each_entry_safe(node, node_s, &node_list, list) { list_del(&node->list); free(node); } } static void node_list_maintain(void) { struct node *node, *node_s; int ret; list_for_each_entry_safe(node, node_s, &node_list, list) { switch (node->status) { case NODE_STATUS_UNKNOWN: case NODE_STATUS_RESET_SENT: case NODE_STATUS_DETECTING: /* ignored */ break; case NODE_STATUS_DETECTED: switch (node->flash_mode) { case FLASH_MODE_TFTP_SERVER: tftp_init_upload(node); break; case FLASH_MODE_REDBOOT: telnet_handle_connection(node); break; case FLASH_MODE_TFTP_CLIENT: case FLASH_MODE_NETCONSOLE: /* ignored; handled in handle_udp_packet */ break; case FLASH_MODE_UKNOWN: fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: Error, flash mode unknown.\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5]); break; } break; case NODE_STATUS_FLASHING: /* if (node->flash_mode == FLASH_MODE_REDBOOT) telnet_keep_alive(node); */ break; case NODE_STATUS_FINISHED: if (node->flash_mode != FLASH_MODE_TFTP_CLIENT) break; ret = tftp_client_flash_completed(node); if (ret == 0) break; fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: flash complete. Device ready to unplug.\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc); node->status = NODE_STATUS_REBOOTED; #if defined(CLEAR_SCREEN) num_nodes_flashed++; #endif break; case NODE_STATUS_REBOOTED: case NODE_STATUS_NO_FLASH: /* check timeout and call _node_list_free(list); */ break; } } } void our_mac_set(struct node *node) { memcpy(node->our_mac_addr, our_mac, ETH_ALEN); /* TODO: 256 addresses might not be sufficient */ our_mac[5]++; } static void sig_handler(int signal) { switch (signal) { case SIGINT: case SIGTERM: running = 0; break; } } int flash_start(const char *iface) { char *packet_buff_align; char *packet_buff; int ret, sleep_sec, sleep_usec; ret = socket_open(iface); if (ret < 0) goto out; packet_buff_align = malloc(PACKET_BUFF_LEN + NET_IP_ALIGN); if (!packet_buff_align) goto list_free; packet_buff = &packet_buff_align[NET_IP_ALIGN]; ret = proto_init(); if (ret < 0) goto pack_free; ret = router_types_init(); if (ret < 0) goto proto_free; signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); sleep_sec = READ_SLEEP_SEC; sleep_usec = READ_SLEEP_USEC; while (running) { ret = socket_read(packet_buff, PACKET_BUFF_LEN, &sleep_sec, &sleep_usec); if (ret == 0) { router_types_detect_pre(our_mac); node_list_maintain(); } if (ret <= 0) goto reset_sleep; handle_eth_packet(packet_buff, ret); continue; reset_sleep: sleep_sec = READ_SLEEP_SEC; sleep_usec = READ_SLEEP_USEC; } ret = 0; proto_free: proto_free(); pack_free: free(packet_buff_align); list_free: node_list_free(); socket_close(iface); out: return ret; } ap51-flash-2025.0/flash.h000066400000000000000000000022231476066605500147270ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Marek Lindner */ #ifndef __AP51_FLASH_FLASH_H__ #define __AP51_FLASH_FLASH_H__ #include #include "list.h" #include "proto.h" enum flash_mode { FLASH_MODE_UKNOWN, FLASH_MODE_REDBOOT, FLASH_MODE_TFTP_SERVER, FLASH_MODE_TFTP_CLIENT, FLASH_MODE_NETCONSOLE, }; enum node_status { NODE_STATUS_UNKNOWN, NODE_STATUS_DETECTING, NODE_STATUS_DETECTED, NODE_STATUS_FLASHING, NODE_STATUS_FINISHED, NODE_STATUS_RESET_SENT, NODE_STATUS_REBOOTED, NODE_STATUS_NO_FLASH, }; struct node { struct list_head list; uint8_t his_mac_addr[6]; uint8_t our_mac_addr[6]; uint32_t his_ip_addr; uint32_t our_ip_addr; enum node_status status; enum flash_mode flash_mode; struct router_type *router_type; struct image_state image_state; struct tcp_state tcp_state; void *router_priv; /* priv declarations are added at runtime */ }; #if defined(CLEAR_SCREEN) extern int num_nodes_flashed; #endif struct node *node_list_get(const uint8_t *mac_addr); void our_mac_set(struct node *node); int flash_start(const char *iface); #endif /* __AP51_FLASH_FLASH_H__ */ ap51-flash-2025.0/fwcfg.c000066400000000000000000000065351476066605500147330ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-FileCopyrightText: Sven Eckelmann */ #include "fwcfg.h" #include #include #include #include #include #include #include #include #include #include "compat.h" #include "router_images.h" static void rtrim(char *s) { size_t len = strlen(s); char *t = &s[len]; while (t-- && t >= s) { if (!isspace(*t)) break; *t = '\0'; } } static unsigned int fwcfg_parse_sizes(struct router_image *router_image, char *content) { char *line, *str_start, *saveptr; size_t line_len; char *value, *type, *tv_delim; unsigned int size = 0; const char *section = NULL; struct file_info *file_info; /* parse */ for (str_start = content; ; str_start = NULL) { line = strtok_r(str_start, "\n", &saveptr); if (!line) break; if (strlen(line) == 0) continue; if (!section && line[0] != '[') { fprintf(stderr, "Found line before section: %s\n", line); return 0; } if (line[0] == '[') { /* section */ rtrim(line); line_len = strlen(line); if (line[line_len - 1] != ']') { fprintf(stderr, "Found section line without delimiter: %s\n", line); return 0; } line[line_len - 1] = '\0'; section = &line[1]; } else { /* type value pair */ type = line; tv_delim = strchr(line, '='); if (!tv_delim) { fprintf(stderr, "Found type=value line without '=': %s\n", line); return 0; } tv_delim[0] = '\0'; value = &tv_delim[1]; if (strcmp("filename", type) != 0) continue; file_info = router_image_get_file_info(router_image, value); if (!file_info) { fprintf(stderr, "Failed to find file %s referenced in fwupgrade.cfg\n", value); return 0; } size += file_info->file_size; } } return size; } unsigned int fwupgrade_cfg_read_sizes(struct router_image *router_image, const struct file_info *file_info) { int fd = -1; int size = 0; int read_len; char *dst = NULL; uint8_t *file_data; off_t reto; /* * WARNING only call when calle first verified that image size is * correct and offset/size of files don't violate the size */ read_len = file_info->file_size; dst = malloc(read_len + 1); if (!dst) { fprintf(stderr, "Error - allocate memory for '%s': %s\n", file_info->file_name, strerror(errno)); goto out; } if (router_image->path) { fd = open(router_image->path, O_RDONLY | O_BINARY); if (fd < 0) { fprintf(stderr, "Error - can't open image file '%s': %s\n", router_image->path, strerror(errno)); goto out; } if (read_len > 0) { reto = lseek(fd, file_info->file_offset, SEEK_SET); if (reto == (off_t) -1) { fprintf(stderr, "Error - seeking in file '%s': %s\n", router_image->path, strerror(errno)); goto out; } if (read_len != read(fd, dst, read_len)) { fprintf(stderr, "Error - reading from file '%s': %s\n", router_image->path, strerror(errno)); goto out; } } } else if (router_image->embedded_img) { file_data = (uint8_t *)router_image->embedded_img; file_data += file_info->file_offset; if (read_len > 0) memcpy(dst, file_data, read_len); } dst[read_len] = '\0'; size = fwcfg_parse_sizes(router_image, dst); out: if (fd >= 0) close(fd); free(dst); return size; } ap51-flash-2025.0/fwcfg.h000066400000000000000000000005641476066605500147340ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Sven Eckelmann */ #ifndef __AP51_FLASH_FWCFG_H__ #define __AP51_FLASH_FWCFG_H__ struct file_info; struct router_image; unsigned int fwupgrade_cfg_read_sizes(struct router_image *router_image, const struct file_info *file_info); #endif /* __AP51_FLASH_FWCFG_H__ */ ap51-flash-2025.0/list.h000066400000000000000000000633661476066605500146240ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ /* Minimal Linux-like double-linked list helper functions * * SPDX-FileCopyrightText: Sven Eckelmann */ #ifndef __LINUX_LIKE_LIST_H__ #define __LINUX_LIKE_LIST_H__ #ifdef __cplusplus extern "C" { #endif #include #if defined(__GNUC__) #define LIST_TYPEOF_USE 1 #endif #if defined(_MSC_VER) #define __inline__ __inline #endif /** * container_of() - Calculate address of object that contains address ptr * @ptr: pointer to member variable * @type: type of the structure containing ptr * @member: name of the member variable in struct @type * * Return: @type pointer of object containing ptr */ #ifndef container_of #ifdef LIST_TYPEOF_USE #define container_of(ptr, type, member) __extension__ ({ \ const __typeof__(((type *)0)->member) *__pmember = (ptr); \ (type *)((char *)__pmember - offsetof(type, member)); }) #else #define container_of(ptr, type, member) \ ((type *)((char *)(ptr) - offsetof(type, member))) #endif #endif /** * struct list_head - Head and node of a double-linked list * @prev: pointer to the previous node in the list * @next: pointer to the next node in the list * * The simple double-linked list consists of a head and nodes attached to * this head. Both node and head share the same struct type. The list_* * functions and macros can be used to access and modify this data structure. * * The @prev pointer of the list head points to the last list node of the * list and @next points to the first list node of the list. For an empty list, * both member variables point to the head. * * The list nodes are usually embedded in a container structure which holds the * actual data. Such an container object is called entry. The helper list_entry * can be used to calculate the object address from the address of the node. */ struct list_head { struct list_head *prev; struct list_head *next; }; /** * DECLARE_LIST_HEAD - Declare list head and initialize it * @head: name of the new object */ #define DECLARE_LIST_HEAD(head) \ struct list_head head = { &(head), &(head) } /** * INIT_LIST_HEAD() - Initialize empty list head * @head: pointer to list head * * This can also be used to initialize a unlinked list node. * * A node is usually linked inside a list, will be added to a list in * the near future or the entry containing the node will be free'd soon. * * But an unlinked node may be given to a function which uses list_del(_init) * before it ends up in a previously mentioned state. The list_del(_init) on an * initialized node is well defined and safe. But the result of a * list_del(_init) on an uninitialized node is undefined (unrelated memory is * modified, crashes, ...). */ static __inline__ void INIT_LIST_HEAD(struct list_head *head) { head->next = head; head->prev = head; } /** * list_add() - Add a list node to the beginning of the list * @node: pointer to the new node * @head: pointer to the head of the list */ static __inline__ void list_add(struct list_head *node, struct list_head *head) { struct list_head *next = head->next; next->prev = node; node->next = next; node->prev = head; head->next = node; } /** * list_add_tail() - Add a list node to the end of the list * @node: pointer to the new node * @head: pointer to the head of the list */ static __inline__ void list_add_tail(struct list_head *node, struct list_head *head) { struct list_head *prev = head->prev; prev->next = node; node->next = head; node->prev = prev; head->prev = node; } /** * list_add_before() - Add a list node before another node to the list * @new_node: pointer to the new node * @node: pointer to the reference node in the list * * WARNING this functionality is not available in the Linux list implementation */ #define list_add_before(new_node, node) \ list_add_tail(new_node, node) /** * list_add_behind() - Add a list node behind another node to the list * @new_node: pointer to the new node * @node: pointer to the reference node in the list * * WARNING this functionality is not available in the Linux list implementation */ #define list_add_behind(new_node, node) \ list_add(new_node, node) /** * list_del() - Remove a list node from the list * @node: pointer to the node * * The node is only removed from the list. Neither the memory of the removed * node nor the memory of the entry containing the node is free'd. The node * has to be handled like an uninitialized node. Accessing the next or prev * pointer of the node is not safe. * * Unlinked, initialized nodes are also uninitialized after list_del. * * LIST_POISONING can be enabled during build-time to provoke an invalid memory * access when the memory behind the next/prev pointer is used after a list_del. * This only works on systems which prohibit access to the predefined memory * addresses. */ static __inline__ void list_del(struct list_head *node) { struct list_head *next = node->next; struct list_head *prev = node->prev; next->prev = prev; prev->next = next; #ifdef LIST_POISONING node->prev = (struct list_head *)(0x00100100); node->next = (struct list_head *)(0x00200200); #endif } /** * list_del_init() - Remove a list node from the list and reinitialize it * @node: pointer to the node * * The removed node will not end up in an uninitialized state like when using * list_del. Instead the node is initialized again to the unlinked state. */ static __inline__ void list_del_init(struct list_head *node) { list_del(node); INIT_LIST_HEAD(node); } /** * list_empty() - Check if list head has no nodes attached * @head: pointer to the head of the list * * Return: 0 - list is not empty !0 - list is empty */ static __inline__ int list_empty(const struct list_head *head) { return (head->next == head); } /** * list_is_singular() - Check if list head has exactly one node attached * @head: pointer to the head of the list * * Return: 0 - list is not singular !0 -list has exactly one entry */ static __inline__ int list_is_singular(const struct list_head *head) { return (!list_empty(head) && head->prev == head->next); } /** * list_splice() - Add list nodes from a list to beginning of another list * @list: pointer to the head of the list with the node entries * @head: pointer to the head of the list * * All nodes from @list are added to to the beginning of the list of @head. * It is similar to list_add but for multiple nodes. The @list head is not * modified and has to be initialized to be used as a valid list head/node * again. */ static __inline__ void list_splice(struct list_head *list, struct list_head *head) { struct list_head *head_first = head->next; struct list_head *list_first = list->next; struct list_head *list_last = list->prev; if (list_empty(list)) return; head->next = list_first; list_first->prev = head; list_last->next = head_first; head_first->prev = list_last; } /** * list_splice_tail() - Add list nodes from a list to end of another list * @list: pointer to the head of the list with the node entries * @head: pointer to the head of the list * * All nodes from @list are added to to the end of the list of @head. * It is similar to list_add_tail but for multiple nodes. The @list head is not * modified and has to be initialized to be used as a valid list head/node * again. */ static __inline__ void list_splice_tail(struct list_head *list, struct list_head *head) { struct list_head *head_last = head->prev; struct list_head *list_first = list->next; struct list_head *list_last = list->prev; if (list_empty(list)) return; head->prev = list_last; list_last->next = head; list_first->prev = head_last; head_last->next = list_first; } /** * list_splice_init() - Move list nodes from a list to beginning of another list * @list: pointer to the head of the list with the node entries * @head: pointer to the head of the list * * All nodes from @list are added to to the beginning of the list of @head. * It is similar to list_add but for multiple nodes. * * The @list head will not end up in an uninitialized state like when using * list_splice. Instead the @list is initialized again to the an empty * list/unlinked state. */ static __inline__ void list_splice_init(struct list_head *list, struct list_head *head) { list_splice(list, head); INIT_LIST_HEAD(list); } /** * list_splice_tail_init() - Move list nodes from a list to end of another list * @list: pointer to the head of the list with the node entries * @head: pointer to the head of the list * * All nodes from @list are added to to the end of the list of @head. * It is similar to list_add_tail but for multiple nodes. * * The @list head will not end up in an uninitialized state like when using * list_splice. Instead the @list is initialized again to the an empty * list/unlinked state. */ static __inline__ void list_splice_tail_init(struct list_head *list, struct list_head *head) { list_splice_tail(list, head); INIT_LIST_HEAD(list); } /** * list_cut_position() - Move beginning of a list to another list * @head_to: pointer to the head of the list which receives nodes * @head_from: pointer to the head of the list * @node: pointer to the node in which defines the cutting point * * All entries from the beginning of the list @head_from to (including) the * @node is moved to @head_from. * * @head_to is replaced when @head_from is not empty. @node must be a real * list node from @head_from or the behavior is undefined. */ static __inline__ void list_cut_position(struct list_head *head_to, struct list_head *head_from, struct list_head *node) { struct list_head *head_from_first = head_from->next; if (list_empty(head_from)) return; if (head_from == node) { INIT_LIST_HEAD(head_to); return; } head_from->next = node->next; head_from->next->prev = head_from; head_to->prev = node; node->next = head_to; head_to->next = head_from_first; head_to->next->prev = head_to; } /** * list_move() - Move a list node to the beginning of the list * @node: pointer to the node * @head: pointer to the head of the list * * The @node is removed from its old position/node and add to the beginning of * @head */ static __inline__ void list_move(struct list_head *node, struct list_head *head) { list_del(node); list_add(node, head); } /** * list_move_tail() - Move a list node to the end of the list * @node: pointer to the node * @head: pointer to the head of the list * * The @node is removed from its old position/node and add to the end of @head */ static __inline__ void list_move_tail(struct list_head *node, struct list_head *head) { list_del(node); list_add_tail(node, head); } /** * list_entry() - Calculate address of entry that contains list node * @node: pointer to list node * @type: type of the entry containing the list node * @member: name of the list_head member variable in struct @type * * Return: @type pointer of entry containing node */ #define list_entry(node, type, member) container_of(node, type, member) /** * list_first_entry() - get first entry of the list * @head: pointer to the head of the list * @type: type of the entry containing the list node * @member: name of the list_head member variable in struct @type * * Return: @type pointer of first entry in list */ #define list_first_entry(head, type, member) \ list_entry((head)->next, type, member) /** * list_last_entry() - get last entry of the list * @head: pointer to the head of the list * @type: type of the entry containing the list node * @member: name of the list_head member variable in struct @type * * Return: @type pointer of last entry in list */ #define list_last_entry(head, type, member) \ list_entry((head)->prev, type, member) /** * list_for_each - iterate over list nodes * @node: list_head pointer used as iterator * @head: pointer to the head of the list * * The nodes and the head of the list must must be kept unmodified while * iterating through it. Any modifications to the the list will cause undefined * behavior. */ #define list_for_each(node, head) \ for (node = (head)->next; \ node != (head); \ node = node->next) /** * list_for_each_entry_t - iterate over list entries * @entry: @type pointer used as iterator * @head: pointer to the head of the list * @type: type of the entries containing the list nodes * @member: name of the list_head member variable in struct @type * * The nodes and the head of the list must must be kept unmodified while * iterating through it. Any modifications to the the list will cause undefined * behavior. * * WARNING this functionality is not available in the Linux list implementation */ #define list_for_each_entry_t(entry, head, type, member) \ for (entry = list_entry((head)->next, type, member); \ &entry->member != (head); \ entry = list_entry(entry->member.next, type, member)) /** * list_for_each_entry - iterate over list entries * @entry: pointer used as iterator * @head: pointer to the head of the list * @member: name of the list_head member variable in struct type of @entry * * The nodes and the head of the list must must be kept unmodified while * iterating through it. Any modifications to the the list will cause undefined * behavior. */ #ifdef LIST_TYPEOF_USE #define list_for_each_entry(entry, head, member) \ list_for_each_entry_t(entry, head, __typeof__(*entry), member) #endif /** * list_for_each_safe - iterate over list nodes and allow deletes * @node: list_head pointer used as iterator * @safe: list_head pointer used to store info for next entry in list * @head: pointer to the head of the list * * The current node (iterator) is allowed to be removed from the list. Any * other modifications to the the list will cause undefined behavior. */ #define list_for_each_safe(node, safe, head) \ for (node = (head)->next, safe = node->next; \ node != (head); \ node = safe, safe = node->next) /** * list_for_each_entry_safe_t - iterate over list entries and allow deletes * @entry: @type pointer used as iterator * @safe: @type pointer used to store info for next entry in list * @head: pointer to the head of the list * @type: type of the entries containing the list nodes * @member: name of the list_head member variable in struct @type * * The current node (iterator) is allowed to be removed from the list. Any * other modifications to the the list will cause undefined behavior. * * WARNING this functionality is not available in the Linux list implementation */ #define list_for_each_entry_safe_t(entry, safe, head, type, member) \ for (entry = list_entry((head)->next, type, member), \ safe = list_entry(entry->member.next, type, member); \ &entry->member != (head); \ entry = safe, \ safe = list_entry(safe->member.next, type, member)) /** * list_for_each_entry_safe - iterate over list entries and allow deletes * @entry: pointer used as iterator * @safe: @type pointer used to store info for next entry in list * @head: pointer to the head of the list * @member: name of the list_head member variable in struct type of @entry * * The current node (iterator) is allowed to be removed from the list. Any * other modifications to the the list will cause undefined behavior. */ #ifdef LIST_TYPEOF_USE #define list_for_each_entry_safe(entry, safe, head, member) \ list_for_each_entry_safe_t(entry, safe, head, __typeof__(*entry), \ member) #endif /** * struct hlist_node - Node of a double-linked list with single pointer head * @next: pointer to the next node in the list * @pprev: pointer to @next of the previous node in the hlist * * The double-linked list with single pointer head consists of a head and nodes * attached to this head. The hlist_* functions and macros can be used to access * and modify this data structure. * * The @pprev pointer is used to find the previous node (or head) in the list * when doing hlist_del operations * * The hlist nodes are usually embedded in a container structure which holds the * actual data. Such an container object is called entry. The helper hlist_entry * can be used to calculate the object address from the address of the node. */ struct hlist_node { struct hlist_node *next; struct hlist_node **pprev; }; /** * struct hlist_head - Head of a double-linked list with single pointer head * @first: pointer to the first node in the hlist * * The hlist doesn't have a pointer to the last node. This makes it harder to * access or modify the tail of the list. But the single pointer to the first * entry makes it well suited for implementation of hash tables because it * cuts the size cost of the head pointers by half compared to the list_head. */ struct hlist_head { struct hlist_node *first; }; /** * HLIST_HEAD - Declare hlist head and initialize it * @head: name of the new object */ #define HLIST_HEAD(head) \ struct hlist_head head = { NULL } /** * INIT_HLIST_HEAD() - Initialize empty hlist head * @head: pointer to hlist head */ static __inline__ void INIT_HLIST_HEAD(struct hlist_head *head) { head->first = NULL; } /** * INIT_HLIST_NODE() - Initialize unhashed hlist node * @node: pointer to hlist node * * A hlist_node is usually linked inside a hlist, will be added to a hlist in * the near future or the entry containing the node will be free'd soon. * * But an unlinked node may be given to a function which uses hlist_del(_init) * before it ends up in a previously mentioned state. The hlist_del(_init) on an * initialized node is well defined and safe. But the result of a * hlist_del(_init) on an uninitialized node is undefined (unrelated memory is * modified, crashes, ...). */ static __inline__ void INIT_HLIST_NODE(struct hlist_node *node) { node->next = NULL; node->pprev = NULL; } /** * hlist_add_head() - Add a hlist node to the beginning of the hlist * @node: pointer to the new node * @head: pointer to the head of the hlist */ static __inline__ void hlist_add_head(struct hlist_node *node, struct hlist_head *head) { struct hlist_node *first = head->first; head->first = node; node->next = first; node->pprev = &head->first; if (first) first->pprev = &node->next; } /** * hlist_add_before() - Add a hlist node before another node to the hlist * @new_node: pointer to the new node * @node: pointer to the reference node in the hlist */ static __inline__ void hlist_add_before(struct hlist_node *new_node, struct hlist_node *node) { struct hlist_node **pprev = node->pprev; *pprev = new_node; new_node->next = node; new_node->pprev = pprev; node->pprev = &new_node->next; } /** * hlist_add_behind() - Add a hlist node behind another node to the hlist * @new_node: pointer to the new node * @node: pointer to the reference node in the hlist */ static __inline__ void hlist_add_behind(struct hlist_node *new_node, struct hlist_node *node) { struct hlist_node *next = node->next; node->next = new_node; new_node->pprev = &node->next; new_node->next = next; if (next) next->pprev = &new_node->next; } /** * hlist_del() - Remove a hlist node from the hlist * @node: pointer to the node * * The node is only removed from the hlist. Neither the memory of the removed * node nor the memory of the entry containing the node is free'd. The node * has to be handled like an uninitialized node. Accessing the next or pprev * pointer of the node is not safe. * * Unlinked, initialized nodes are also uninitialized after hlist_del. * * LIST_POISONING can be enabled during build-time to provoke an invalid memory * access when the memory behind the next/prev pointer is used after an * hlist_del. This only works on systems which prohibit access to the predefined * memory addresses. */ static __inline__ void hlist_del(struct hlist_node *node) { struct hlist_node *next = node->next; struct hlist_node **pprev = node->pprev; if (pprev) *pprev = next; if (next) next->pprev = pprev; #ifdef LIST_POISONING node->pprev = (struct hlist_node **)(0x00100100); node->next = (struct hlist_node *)(0x00200200); #endif } /** * hlist_del_init() - Remove a hlist node from the hlist and reinitialize it * @node: pointer to the node * * The removed node will not end up in an uninitialized state like when using * hlist_del. Instead the node is initialized again to the unlinked state. */ static __inline__ void hlist_del_init(struct hlist_node *node) { hlist_del(node); INIT_HLIST_NODE(node); } /** * hlist_empty() - Check if hlist head has no nodes attached * @head: pointer to the head of the hlist * * Return: 0 - hlist is not empty !0 - hlist is empty */ static __inline__ int hlist_empty(const struct hlist_head *head) { return !head->first; } /** * hlist_move_list() - Move hlist nodes from a hlist head new hlist head * @list: pointer to the head of the hlist with the node entries * @head: pointer to the head of the hlist * * All nodes from @list are added to to the beginning of the list of @head. * @head can be uninitialized or an empty, initialized hlist. All entries of * a non-empty hlist @head would be lost after this operation. * * The @list head will not end up in an uninitialized state. Instead the @list * is initialized again to an empty hlist. */ static __inline__ void hlist_move_list(struct hlist_head *list, struct hlist_head *head) { head->first = list->first; if (head->first) head->first->pprev = &head->first; INIT_HLIST_HEAD(list); } /** * hlist_entry() - Calculate address of entry that contains hlist node * @node: pointer to hlist node * @type: type of the entry containing the hlist node * @member: name of the hlist_node member variable in struct @type * * Return: @type pointer of entry containing node */ #define hlist_entry(node, type, member) container_of(node, type, member) /** * hlist_entry_safe() - Calculate address of entry that contains hlist node * @node: pointer to hlist node or (struct hlist_node *)NULL * @type: type of the entry containing the hlist node * @member: name of the hlist_node member variable in struct @type * * Return: @type pointer of entry containing node or NULL */ #ifdef LIST_TYPEOF_USE #define hlist_entry_safe(node, type, member) __extension__ ({ \ __typeof__(node) __node = (node); \ !__node ? NULL : hlist_entry(__node, type, member); }) #else #define hlist_entry_safe(node, type, member) \ (node) ? hlist_entry(node, type, member) : NULL #endif /** * hlist_for_each - iterate over hlist nodes * @node: hlist_node pointer used as iterator * @head: pointer to the head of the hlist * * The nodes and the head of the hlist must must be kept unmodified while * iterating through it. Any modifications to the the hlist will cause undefined * behavior. */ #define hlist_for_each(node, head) \ for (node = (head)->first; \ node; \ node = node->next) /** * hlist_for_each_entry_t - iterate over hlist entries * @entry: @type pointer used as iterator * @head: pointer to the head of the hlist * @type: type of the entries containing the hlist nodes * @member: name of the hlist_node member variable in struct @type * * The nodes and the head of the hlist must must be kept unmodified while * iterating through it. Any modifications to the the hlist will cause undefined * behavior. * * WARNING this functionality is not available in the Linux list implementation */ #define hlist_for_each_entry_t(entry, head, type, member) \ for (entry = hlist_entry_safe((head)->first, type, member); \ entry; \ entry = hlist_entry_safe(entry->member.next, type, member)) /** * hlist_for_each_entry - iterate over hlist entries * @entry: pointer used as iterator * @head: pointer to the head of the hlist * @member: name of the hlist_node member variable in struct type of @entry * * The nodes and the head of the hlist must must be kept unmodified while * iterating through it. Any modifications to the the hlist will cause undefined * behavior. */ #ifdef LIST_TYPEOF_USE #define hlist_for_each_entry(entry, head, member) \ hlist_for_each_entry_t(entry, head, __typeof__(*entry), member) #endif /** * hlist_for_each_safe - iterate over hlist nodes and allow deletes * @node: hlist_node pointer used as iterator * @safe: hlist_node pointer used to store info for next entry in hlist * @head: pointer to the head of the hlist * * The current node (iterator) is allowed to be removed from the hlist. Any * other modifications to the the hlist will cause undefined behavior. */ #define hlist_for_each_safe(node, safe, head) \ for (node = (head)->first; \ node && ((safe = node->next) || 1); \ node = safe) /** * hlist_for_each_entry_safe_t - iterate over hlist entries and allow deletes * @entry: @type pointer used as iterator * @safe: hlist_node pointer used to store info for next entry in hlist * @head: pointer to the head of the hlist * @type: type of the entries containing the hlist nodes * @member: name of the hlist_node member variable in struct @type * * The current node (iterator) is allowed to be removed from the hlist. Any * other modifications to the the hlist will cause undefined behavior. * * WARNING this functionality is not available in the Linux list implementation */ #define hlist_for_each_entry_safe_t(entry, safe, head, type, member) \ for (entry = hlist_entry_safe((head)->first, type, member); \ entry && ((safe = entry->member.next) || 1); \ entry = hlist_entry_safe(safe, type, member)) /** * hlist_for_each_entry_safe - iterate over hlist entries and allow deletes * @entry: pointer used as iterator * @safe: hlist_node pointer used to store info for next entry in hlist * @head: pointer to the head of the hlist * @member: name of the hlist_node member variable in struct type of @entry * * The current node (iterator) is allowed to be removed from the hlist. Any * other modifications to the the hlist will cause undefined behavior. */ #ifdef LIST_TYPEOF_USE #define hlist_for_each_entry_safe(entry, safe, head, member) \ hlist_for_each_entry_safe_t(entry, safe, head, __typeof__(*entry),\ member) #endif #ifdef __cplusplus } #endif #endif /* __LINUX_LIKE_LIST_H__ */ ap51-flash-2025.0/man/000077500000000000000000000000001476066605500142355ustar00rootroot00000000000000ap51-flash-2025.0/man/ap51-flash.8000066400000000000000000000056001476066605500161700ustar00rootroot00000000000000.\" SPDX-License-Identifier: GPL-3.0-or-later .\" SPDX-FileCopyrightText: Sven Eckelmann .TH "AP51-FLASH" "8" "August 29, 2019" .\" Please adjust this date whenever revising the manpage. .\" -------------------------------------------------------------------------- .\" Process this file with .\" groff -man man/ap51-flash.8 -Tutf8 .\" Retrieve format warnings with .\" LC_ALL=en_US.UTF-8 MANROFFSEQ='' MANWIDTH=80 man --warnings -E UTF-8 -l -Tutf8 -Z man/ap51-flash.8 >/dev/null .\" -------------------------------------------------------------------------- .SH NAME ap51\-flash \- firmware flasher for ethernet connected routers and access points .SH SYNOPSIS .na .B ap51\-flash [ .B \-vh ] [ .B \-m .I mac ] [ .I interface .I image .I ... ] .br .ad .SH DESCRIPTION \fBap51\-flash\fP listens on traffic of a network \fIinterface\fP, detects remote devices and checks the traffic for known identification markers. These markers are send out automatically by supported devices during the initial boot process of the embedded boot loader. .PP On success, it will search the list of provided \fIimage\fPs for a matching firmware. A device specific communication procedure is initiated to begin the flashing process. The firmware data is extracted from the selected \fIimage\fP and transferred to the device in TFTP server or client mode. .PP \fBap51\-flash\fP will run in foreground and continue using the \fIinterface\fP until it is interrupted by an SIGINT signal (typically generated by control-C) or SIGTERM signal (for example generated by the .BR kill(1) command). .PP The \fIinterface\fP doesn't require a valid layer 3 network configuration but has to be set in the UP state (using the .BR ip\-link(8) command). \fBap51\-flash\fP requires enough privileges to capture and inject raw ethernet packets on the selected \fIinterface\fP. These are often only accessible to the root user. .PP While \fBap51\-flash\fP doesn't require a target device specific network configuration or TFTP client/server on the same machine, it is recommended to disable any other TFTP client or server on the system running \fBap51\-flash\fP to avoid interferences by a different TFTP program. .SH OPTIONS Listed below are the command line options for \fBap51\-flash\fP: .TP \fB\-m\fP \fImac\fP, \fB\--mac\fP \fImac\fP Adds \fImac\fP address to the allowlist filter. Only these addresses will be tried to be flashed when one or more addresses are supplied. The option has to be supplied multiple times to add multiple addresses to the allowlist filter. If not specified, all detected devices will be flashed. .TP .BR \-v ", " \-\-version print ap51\-flash version and exit .TP .BR \-h ", " \-\-help print ap51\-flash help and exit .SH AUTHOR ap51-flash was written by Marek Lindner and others. This manual page was written by Sven Eckelmann . It is licensed under the terms of the GNU GPL (version 3 or later). ap51-flash-2025.0/proto.c000066400000000000000000000632301476066605500147750ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-FileCopyrightText: Marek Lindner */ #include "proto.h" #include #include #include #include #include "ap51-flash.h" #include "compat.h" #include "flash.h" #include "router_images.h" #include "router_redboot.h" #include "router_tftp_client.h" #include "router_netconsole.h" #include "router_types.h" #include "socket.h" #define TFTP_SRC_PORT 13337 #define REDBOOT_TELNET_SPORT 13337 #define REDBOOT_TELNET_DPORT 9000 #define TFTP_PAYLOAD_SIZE 512 enum tcp_packet_type { TCP_SYN, TCP_ACK, TCP_DATA, }; #define PACKET_BUFF_LEN 2000 #define ARP_LEN (sizeof(struct ether_header) + sizeof(struct ether_arp)) #define MAX_TCP_PAYLOAD (ETH_DATA_LEN - ETH_HLEN - sizeof(struct iphdr) - \ sizeof(struct tcphdr)) static char *out_packet_buff; static char *out_packet_buff_align; static struct ether_header *out_ethhdr; static struct ether_arp *out_arphdr; static struct iphdr *out_iphdr; static struct udphdr *out_udphdr; static char *out_tftp_data; static struct icmphdr *out_icmphdr; static unsigned short chksum(unsigned short sum, const unsigned char *data, unsigned short len) { unsigned short t; const unsigned char *dataptr, *last_byte; dataptr = data; last_byte = data + len - 1; while (dataptr < last_byte) { t = (dataptr[0] << 8) + dataptr[1]; sum += t; if(sum < t) sum++; dataptr += 2; } if (dataptr == last_byte) { t = (dataptr[0] << 8) + 0; sum += t; if(sum < t) sum++; } return sum; } static void arp_init(const uint8_t *src_mac, const uint8_t *dst_mac, unsigned int src_ip, unsigned int dst_ip, unsigned short arp_type) { memcpy(out_ethhdr->ether_shost, src_mac, ETH_ALEN); memcpy(out_ethhdr->ether_dhost, dst_mac, ETH_ALEN); out_ethhdr->ether_type = htons(ETH_P_ARP); out_arphdr->ea_hdr.ar_hrd = htons(0x0001); /* ethernet */ out_arphdr->ea_hdr.ar_pro = htons(ETH_P_IP); /* IPv4 */ out_arphdr->ea_hdr.ar_hln = ETH_ALEN; out_arphdr->ea_hdr.ar_pln = 4; /* IPv4 addr len */ out_arphdr->ea_hdr.ar_op = htons(arp_type); memcpy(out_arphdr->arp_sha, src_mac, ETH_ALEN); store_ip_addr(out_arphdr->arp_spa, src_ip); store_ip_addr(out_arphdr->arp_tpa, dst_ip); } int arp_req_send(const uint8_t *src_mac, const uint8_t *dst_mac, unsigned int src_ip, unsigned int dst_ip) { arp_init(src_mac, dst_mac, src_ip, dst_ip, ARPOP_REQUEST); return socket_write(out_packet_buff, ARP_LEN); } static int arp_rep_send(const uint8_t *src_mac, const uint8_t *dst_mac, unsigned int src_ip, unsigned int dst_ip) { arp_init(src_mac, dst_mac, src_ip, dst_ip, ARPOP_REPLY); /* fprintf(stderr, "arp_rep_send() to: %02x:%02x:%02x:%02x:%02x:%02x from %02x:%02x:%02x:%02x:%02x:%02x\n", dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5], src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5]);*/ return socket_write(out_packet_buff, ARP_LEN); } static void tftp_packet_init(struct node *node, unsigned short src_port, unsigned short dst_port) { memcpy(out_ethhdr->ether_shost, node->our_mac_addr, ETH_ALEN); memcpy(out_ethhdr->ether_dhost, node->his_mac_addr, ETH_ALEN); out_ethhdr->ether_type = htons(ETH_P_IP); out_iphdr->version = 4; out_iphdr->ihl = 5; out_iphdr->tos = 0; out_iphdr->id = 0; out_iphdr->frag_off = 0; out_iphdr->ttl = 50; out_iphdr->protocol = IPPROTO_UDP; out_iphdr->saddr = node->our_ip_addr; out_iphdr->daddr = node->his_ip_addr; out_udphdr->source = src_port; out_udphdr->dest = dst_port; } static int tftp_packet_send_data(struct node *node, unsigned short src_port, unsigned short dst_port, int tftp_data_len) { unsigned short sum; tftp_packet_init(node, src_port, dst_port); out_udphdr->len = htons(8 + tftp_data_len); /* UDP checksum */ out_udphdr->check = 0; sum = ntohs(out_udphdr->len) + out_iphdr->protocol; sum = chksum(sum, (void *)&out_iphdr->saddr, 2 * sizeof(out_iphdr->saddr)); sum = chksum(sum, (void *)out_udphdr, ntohs(out_udphdr->len)); out_udphdr->check = ~(htons(sum)); out_iphdr->tot_len = htons(20 + 8 + tftp_data_len); out_iphdr->check = 0; out_iphdr->check = ~(htons(chksum(0, (void *)out_iphdr, sizeof(struct iphdr)))); return socket_write(out_packet_buff, ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + tftp_data_len); } int tftp_init_upload(struct node *node) { int data_len; /* TFTP write request */ *((unsigned short *)out_tftp_data) = htons(2); data_len = 2; data_len += sprintf(out_tftp_data + data_len, "\"%s\"", "flash_update"); data_len += sprintf(out_tftp_data + data_len + 1, "%s", "octet"); data_len += 2; /* sprintf does not count \0 */ return tftp_packet_send_data(node, htons(TFTP_SRC_PORT), htons(IPPORT_TFTP), data_len); } int netconsole_init_upload(struct node *node) { int data_len; /* TFTP start command and reset (for subsequential reboot) */ data_len = sprintf(out_tftp_data, "run fw_upg; reset\n"); return tftp_packet_send_data(node, htons(IPPORT_NETCONSOLE), htons(IPPORT_NETCONSOLE), data_len); } static void handle_arp_packet(const char *packet_buff, int packet_buff_len, struct node *node) { struct ether_arp *arphdr; int ret; if (!len_check(packet_buff_len, sizeof(struct ether_arp), "ARP")) return; arphdr = (struct ether_arp *)packet_buff; switch (ntohs(arphdr->ea_hdr.ar_op)) { case ARPOP_REQUEST: case ARPOP_REPLY: #if defined(DEBUG) fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: received ARP %s, device status: %d, sender hw addr: %02x:%02x:%02x:%02x:%02x:%02x, target hw addr: %02x:%02x:%02x:%02x:%02x:%02x\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], ntohs(arphdr->ea_hdr.ar_op) == ARPOP_REQUEST ? "request" : "reply", node->status, arphdr->arp_sha[0], arphdr->arp_sha[1], arphdr->arp_sha[2], arphdr->arp_sha[3], arphdr->arp_sha[4], arphdr->arp_sha[5], arphdr->arp_tha[0], arphdr->arp_tha[1], arphdr->arp_tha[2], arphdr->arp_tha[3], arphdr->arp_tha[4], arphdr->arp_tha[5]); #endif break; default: fprintf(stderr, "ARP, unknown op code: %i, status: %d\n", ntohs(arphdr->ea_hdr.ar_op), node->status); return; } switch (node->status) { case NODE_STATUS_UNKNOWN: node->status = NODE_STATUS_DETECTING; /* fall through */ case NODE_STATUS_DETECTING: ret = router_types_detect_main(node, packet_buff, packet_buff_len); if (ret != 1) break; node->status = NODE_STATUS_DETECTED; /* fall through */ case NODE_STATUS_DETECTED: case NODE_STATUS_FLASHING: if (ntohs(arphdr->ea_hdr.ar_op) != ARPOP_REQUEST) break; arp_rep_send(node->our_mac_addr, arphdr->arp_sha, load_ip_addr(arphdr->arp_tpa), load_ip_addr(arphdr->arp_spa)); break; case NODE_STATUS_RESET_SENT: case NODE_STATUS_FINISHED: if (node->flash_mode != FLASH_MODE_NETCONSOLE) { fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: flash complete. Device ready to unplug.\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc); node->status = NODE_STATUS_REBOOTED; #if defined(CLEAR_SCREEN) num_nodes_flashed++; #endif } break; case NODE_STATUS_REBOOTED: case NODE_STATUS_NO_FLASH: break; } } static void handle_udp_packet(const char *packet_buff, int packet_buff_len, struct node *node) { struct udphdr *udphdr; struct file_info *file_info; unsigned short opcode, block; const char *file_name; int ret, data_len; static const char fwupgradecfg[] = "fwupgrade.cfg"; if (!len_check(packet_buff_len, sizeof(struct udphdr), "UDP")) return; udphdr = (struct udphdr *)packet_buff; switch (node->flash_mode) { case FLASH_MODE_NETCONSOLE: if (udphdr->dest == htons(IPPORT_NETCONSOLE)) { size_t len = sizeof(*udphdr); handle_netconsole_packet(packet_buff + len, packet_buff_len - len, node); return; } /* fall through */ case FLASH_MODE_REDBOOT: case FLASH_MODE_TFTP_CLIENT: if (udphdr->dest != htons(IPPORT_TFTP)) return; break; case FLASH_MODE_TFTP_SERVER: if (udphdr->source != htons(IPPORT_TFTP)) return; break; default: return; } opcode = ntohs(*(unsigned short *)(packet_buff + sizeof(struct udphdr))); block = ntohs(*(unsigned short *)(packet_buff + sizeof(struct udphdr) + 2)); /* fprintf(stderr, "tftp opcode=%d, block=%d, len=%i\n", opcode, block, htons(rcv_udphdr->len) - sizeof(struct udphdr)); */ switch (opcode) { /* TFTP read request */ case 1: file_name = packet_buff + sizeof(struct udphdr) + 2; switch (node->flash_mode) { case FLASH_MODE_UKNOWN: /* ignore */ break; case FLASH_MODE_TFTP_SERVER: /* ignored; handled in node_list_maintain */ break; case FLASH_MODE_REDBOOT: case FLASH_MODE_TFTP_CLIENT: case FLASH_MODE_NETCONSOLE: file_info = router_image_get_file(node->router_type, file_name); if (!file_info) { fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: tftp client asks for '%s' - file not found ...\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc, file_name); goto out; } if (node->image_state.fd <= 0) { ret = router_images_open_path(node); if (ret < 0) goto out; node->status = NODE_STATUS_FLASHING; } fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: tftp client asks for '%s', serving %s portion of: %s (%i blocks) ...\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc, file_name,file_info->file_name, node->router_type->image->path ? node->router_type->image->path : "embedded image", ((file_info->file_fsize + TFTP_PAYLOAD_SIZE - 1) / TFTP_PAYLOAD_SIZE)); node->image_state.file_size = file_info->file_size; node->image_state.flash_size = file_info->file_fsize; node->image_state.offset = file_info->file_offset; break; } block = 0; node->image_state.bytes_sent = 0; node->image_state.last_packet_size = 0; if (strncmp(file_name, fwupgradecfg, strlen(fwupgradecfg)) == 0) node->image_state.count_globally = 0; else node->image_state.count_globally = 1; /* fall through - start sending data */ /* TFTP ack */ case 4: if (block == 0) { if (node->flash_mode == FLASH_MODE_TFTP_SERVER) { ret = router_images_open_path(node); if (ret < 0) return; node->status = NODE_STATUS_FLASHING; node->image_state.file_size = node->router_type->image->file_size; node->image_state.flash_size = ((node->router_type->image->file_size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE; node->image_state.offset = 0; fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: connection to tftp server established - uploading %i blocks ...\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc, ((node->image_state.flash_size + TFTP_PAYLOAD_SIZE - 1) / TFTP_PAYLOAD_SIZE)); } node->image_state.block_acked = 0; node->image_state.block_sent = 0; } else if (block != node->image_state.block_sent) { if (block < node->image_state.block_sent) fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: tftp repeat block %d, last received ack: %d\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc, block + 1, node->image_state.block_acked); else fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: tftp acks unsent block %d (last sent block: %d)\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc, block, node->image_state.block_sent); block = node->image_state.block_acked; node->image_state.bytes_sent -= node->image_state.last_packet_size; } else { /* nothing more to send */ if (node->image_state.last_packet_size != TFTP_PAYLOAD_SIZE) { /* don't count this file as payload? */ if (!node->image_state.count_globally) goto out; node->image_state.total_bytes_sent += node->image_state.bytes_sent; if (node->image_state.total_bytes_sent >= router_image_get_size(node->router_type)) { switch (node->flash_mode) { case FLASH_MODE_TFTP_SERVER: case FLASH_MODE_TFTP_CLIENT: case FLASH_MODE_NETCONSOLE: fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: image successfully transmitted - writing image to flash ...\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc); router_images_close_path(node); if (node->flash_mode == FLASH_MODE_TFTP_CLIENT) tftp_client_flash_time_set(node); node->status = NODE_STATUS_FINISHED; break; case FLASH_MODE_REDBOOT: /* ignored; handled in REDBOOT_STATE_EXECY */ break; case FLASH_MODE_UKNOWN: /* ignore */ break; } } goto out; } node->image_state.block_acked = block; } block++; /* TFTP DATA packet */ *((unsigned short *)out_tftp_data) = htons(3); *((unsigned short *)(out_tftp_data + 2)) = htons(block); data_len = router_images_read_data(out_tftp_data + 4, node); if (data_len < 0) break; data_len += 4; /* opcode size */ ret = tftp_packet_send_data(node, udphdr->dest, udphdr->source, data_len); if (ret < 0) return; node->image_state.last_packet_size = data_len - 4; /* opcode size */ node->image_state.bytes_sent += node->image_state.last_packet_size; node->image_state.block_sent = block; /* printf("tftp data out: tftp_sent=%lu, remaining_size=%lu, data_len=%i, block=%d\n", tftp_sent, tftp_xfer_size - tftp_sent, tftp_data_len - 4, block); */ break; /* TFTP error */ case 5: if ((block == 2) && (htons(udphdr->len) - sizeof(struct udphdr) > 4)) fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: received TFTP error: %s\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc, (packet_buff + sizeof(struct udphdr) + 4)); else fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: received TFTP error code: %d\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc, block); break; default: fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: unexpected TFTP opcode: %d\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc, opcode); break; } out: return; } static void tcp_init_state(struct node *node) { struct ether_header *ethhdr; struct iphdr *iphdr; struct tcphdr *tcphdr; node->tcp_state.packet_buff = malloc(PACKET_BUFF_LEN); if (!node->tcp_state.packet_buff) goto out; memset(node->tcp_state.packet_buff, 0, PACKET_BUFF_LEN); ethhdr = (struct ether_header *)node->tcp_state.packet_buff; memcpy(ethhdr->ether_shost, node->our_mac_addr, ETH_ALEN); memcpy(ethhdr->ether_dhost, node->his_mac_addr, ETH_ALEN); ethhdr->ether_type = htons(ETH_P_IP); iphdr = (struct iphdr *)(node->tcp_state.packet_buff + ETH_HLEN); iphdr->version = 4; iphdr->ihl = 5; iphdr->tos = 0; iphdr->id = 0; iphdr->frag_off = 0; iphdr->ttl = 50; iphdr->protocol = IPPROTO_TCP; iphdr->saddr = node->our_ip_addr; iphdr->daddr = node->his_ip_addr; tcphdr = (struct tcphdr *)(node->tcp_state.packet_buff + ETH_HLEN + sizeof(struct iphdr)); tcphdr->source = htons(REDBOOT_TELNET_SPORT); tcphdr->dest = htons(REDBOOT_TELNET_DPORT); out: return; } static int tcp_send(struct node *node, int tcp_data_len, enum tcp_packet_type flags) { struct iphdr *iphdr; struct tcphdr *tcphdr; unsigned short sum; unsigned int mss_option = htonl(0x02040000 | MAX_TCP_PAYLOAD); iphdr = (struct iphdr *)(node->tcp_state.packet_buff + ETH_HLEN); tcphdr = (struct tcphdr *)(node->tcp_state.packet_buff + ETH_HLEN + sizeof(struct iphdr)); /* tcp header */ tcphdr->seq = htonl(node->tcp_state.my_seq); tcphdr->ack_seq = htonl(node->tcp_state.my_ack_seq); tcphdr->window = htons(MAX_TCP_PAYLOAD); tcphdr->doff = 5; /* always set PUSH flag if sending data */ if (tcp_data_len > 0) tcphdr->psh = 1; else tcphdr->psh = 0; switch (flags) { case TCP_SYN: tcphdr->syn = 1; tcphdr->ack = 0; /* send MSS option */ tcp_data_len += 4; memcpy((unsigned char *)(tcphdr + 1), &mss_option, sizeof(mss_option)); tcphdr->doff++; break; case TCP_ACK: case TCP_DATA: tcphdr->syn = 0; tcphdr->ack = 1; break; } /* TCP checksum */ tcphdr->check = 0; if (flags == TCP_SYN) sum = (tcphdr->doff * 4) + iphdr->protocol; else sum = (tcphdr->doff * 4) + tcp_data_len + iphdr->protocol; sum = chksum(sum, (void *)&iphdr->saddr, 2 * sizeof(iphdr->saddr)); sum = chksum(sum, (void *)tcphdr, sizeof(struct tcphdr) + tcp_data_len); tcphdr->check = ~(htons(sum)); iphdr->tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr) + tcp_data_len); iphdr->check = 0; iphdr->check = ~(htons(chksum(0, (void *)iphdr, sizeof(struct iphdr)))); return socket_write(node->tcp_state.packet_buff, ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr) + tcp_data_len); } static int tcp_send_syn(struct node *node) { return tcp_send(node, 0, TCP_SYN); } static int tcp_send_ack(struct node *node) { return tcp_send(node, 0, TCP_ACK); } static int tcp_send_data(struct node *node, int tcp_data_len) { return tcp_send(node, tcp_data_len, TCP_DATA); } static int tcp_resend_data(struct node *node) { char *packet_buff; packet_buff = node->tcp_state.packet_buff + ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr); return tcp_send(node, (int)strlen(packet_buff), TCP_DATA); } void telnet_handle_connection(struct node *node) { if (!node->tcp_state.packet_buff) tcp_init_state(node); if (!node->tcp_state.packet_buff) goto out; /* keep sending SYN packets until we get a reply */ switch (node->tcp_state.status) { case TCP_STATUS_SYN_SENT: tcp_send_syn(node); break; case TCP_STATUS_ESTABLISHED: case TCP_STATUS_TELNET_READY: /* TODO: check timer if we need to resend */ break; } out: return; } int telnet_send_cmd(struct node *node, const char *cmd) { char *packet_buff; size_t buflen = PACKET_BUFF_LEN; packet_buff = node->tcp_state.packet_buff + ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr); buflen -= ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr); strncpy(packet_buff, cmd, buflen); packet_buff[buflen - 1] = '\0'; return tcp_send_data(node, (int)strlen(cmd)); } static void handle_tcp_packet(char *packet_buff, int packet_buff_len, struct node *node) { struct tcphdr *tcphdr; unsigned int data_len; char *buff; if (!len_check(packet_buff_len, sizeof(struct tcphdr), "TCP")) goto out; tcphdr = (struct tcphdr *)packet_buff; /* not telnet */ if (tcphdr->source != htons(REDBOOT_TELNET_DPORT)) goto out; if (tcphdr->dest != htons(REDBOOT_TELNET_SPORT)) goto out; if (tcphdr->ack != 1) goto out; data_len = packet_buff_len - (tcphdr->doff * 4); switch (node->tcp_state.status) { case TCP_STATUS_SYN_SENT: if (tcphdr->syn != 1) goto out; node->tcp_state.status = TCP_STATUS_ESTABLISHED; node->tcp_state.my_ack_seq = ntohl(tcphdr->seq) + 1; node->tcp_state.my_seq = ntohl(tcphdr->ack_seq); tcp_send_ack(node); break; case TCP_STATUS_ESTABLISHED: if (tcphdr->syn != 0) goto out; packet_buff[packet_buff_len - 1] = '\0'; buff = (char *)(tcphdr + 1); node->tcp_state.status = TCP_STATUS_TELNET_READY; node->tcp_state.my_ack_seq += data_len; /* send CTRL + C */ buff = node->tcp_state.packet_buff + ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr); buff[0] = 0x03; tcp_send_data(node, 1); break; case TCP_STATUS_TELNET_READY: if (tcphdr->syn != 0) goto out; /* check for retransmission */ if ((tcphdr->seq == node->tcp_state.his_seq) && (tcphdr->ack_seq == node->tcp_state.his_ack_seq) && (data_len == node->tcp_state.his_last_len)) { /* printf("retransmission received: seq = %u, ack_seq: %u, len: %u\n", ntohl(tcphdr->seq), ntohl(tcphdr->ack_seq), data_len); */ tcp_resend_data(node); goto out; } node->tcp_state.his_seq = tcphdr->seq; node->tcp_state.his_ack_seq = tcphdr->ack_seq; node->tcp_state.his_last_len = data_len; if (node->tcp_state.his_last_len == 0) { /* TODO probably should do something useful with the ACK */ /* printf("received simple ACK (no text)\n"); */ goto out; } node->tcp_state.my_ack_seq += data_len; if (ntohl(tcphdr->ack_seq) > node->tcp_state.my_seq) node->tcp_state.my_seq = ntohl(tcphdr->ack_seq); packet_buff[packet_buff_len] = '\0'; redboot_main(node, (char *)(tcphdr + 1)); break; default: return; } out: return; } static void handle_icmp_packet(char *packet_buff, int packet_buff_len, struct node *node) { struct icmphdr *icmphdr; size_t len; if (!len_check(packet_buff_len, sizeof(struct icmphdr), "ICMP")) goto out; icmphdr = (struct icmphdr *)packet_buff; /* not echo request */ if (icmphdr->type != 8) goto out; if (icmphdr->code != 0) goto out; len = 0; memcpy(out_ethhdr->ether_dhost, node->his_mac_addr, ETH_ALEN); memcpy(out_ethhdr->ether_shost, node->our_mac_addr, ETH_ALEN); out_ethhdr->ether_type = htons(ETH_P_IP); len += sizeof(*out_ethhdr); out_iphdr->version = 4; out_iphdr->ihl = 5; out_iphdr->tos = 0; out_iphdr->id = 0; out_iphdr->frag_off = 0; out_iphdr->ttl = 50; out_iphdr->protocol = IPPROTO_ICMP; out_iphdr->saddr = node->our_ip_addr; out_iphdr->daddr = node->his_ip_addr; out_iphdr->tot_len = htons(sizeof(*out_iphdr) + sizeof(*out_icmphdr)); out_iphdr->check = 0; out_iphdr->check = ~(htons(chksum(0, (void *)out_iphdr, sizeof(*out_iphdr)))); len += sizeof(*out_iphdr); out_icmphdr->type = 0; out_icmphdr->code = 0; out_icmphdr->un.echo.id = icmphdr->un.echo.id; out_icmphdr->un.echo.sequence = icmphdr->un.echo.sequence; out_icmphdr->checksum = 0; out_icmphdr->checksum = ~(htons(chksum(0, (void *)out_icmphdr, sizeof(*out_icmphdr)))); len += sizeof(*out_icmphdr); socket_write(out_packet_buff, len); out: return; } static void handle_ip_packet(char *packet_buff, int packet_buff_len, struct node *node) { struct iphdr *iphdr; size_t iphdr_len; int length; if (!len_check(packet_buff_len, sizeof(struct iphdr), "IPv4")) return; iphdr = (struct iphdr *)packet_buff; if (iphdr->ihl < 5) return; iphdr_len = iphdr->ihl * 4; if (!len_check(packet_buff_len, iphdr_len, "IPv4 full")) return; switch (node->status) { case NODE_STATUS_DETECTED: case NODE_STATUS_FLASHING: case NODE_STATUS_RESET_SENT: break; case NODE_STATUS_FINISHED: /* in netconsole mode a 'reset' command is still required to * reboot the router */ if (node->flash_mode == FLASH_MODE_NETCONSOLE) break; case NODE_STATUS_UNKNOWN: case NODE_STATUS_DETECTING: case NODE_STATUS_NO_FLASH: default: return; } if (iphdr->saddr != node->his_ip_addr) return; if (iphdr->daddr != node->our_ip_addr) return; length = ntohs(iphdr->tot_len); if (length > packet_buff_len) length = packet_buff_len; if (length < (int)iphdr_len) return; switch (iphdr->protocol) { case IPPROTO_UDP: handle_udp_packet(packet_buff + iphdr_len, length - iphdr_len, node); break; case IPPROTO_TCP: if (node->flash_mode != FLASH_MODE_REDBOOT) break; handle_tcp_packet(packet_buff + iphdr_len, length - iphdr_len, node); break; case IPPROTO_ICMP: handle_icmp_packet(packet_buff + iphdr_len, length - iphdr_len, node); break; } return; } void handle_eth_packet(char *packet_buff, int packet_buff_len) { struct ether_header *eth_hdr; struct node *node; uint8_t bcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; if (!len_check(packet_buff_len, ETH_HLEN, "ethernet")) return; eth_hdr = (struct ether_header *)packet_buff; if (memcmp(eth_hdr->ether_shost, bcast_addr, ETH_ALEN) == 0) return; switch (ntohs(eth_hdr->ether_type)) { case ETH_P_ARP: node = node_list_get(eth_hdr->ether_shost); if (!node) return; handle_arp_packet(packet_buff + ETH_HLEN, packet_buff_len - ETH_HLEN, node); break; case ETH_P_IP: if (memcmp(eth_hdr->ether_dhost, bcast_addr, ETH_ALEN) == 0) return; node = node_list_get(eth_hdr->ether_shost); if (!node) return; handle_ip_packet(packet_buff + ETH_HLEN, packet_buff_len - ETH_HLEN, node); break; default: /* silently drop packet */ break; } } int proto_init(void) { int ret = -1; out_packet_buff_align = malloc(PACKET_BUFF_LEN + NET_IP_ALIGN); if (!out_packet_buff_align) goto out; out_packet_buff = &out_packet_buff_align[NET_IP_ALIGN]; out_ethhdr = (struct ether_header *)out_packet_buff; out_arphdr = (struct ether_arp *)(out_packet_buff + ETH_HLEN); out_iphdr = (struct iphdr *)(out_packet_buff + ETH_HLEN); out_udphdr = (struct udphdr *)(out_packet_buff + ETH_HLEN + sizeof(struct iphdr)); out_tftp_data = (void *)(out_packet_buff + ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr)); out_icmphdr = (struct icmphdr *)(out_packet_buff + ETH_HLEN + sizeof(struct iphdr)); ret = 0; out: return ret; } void proto_free(void) { free(out_packet_buff_align); } ap51-flash-2025.0/proto.h000066400000000000000000000036301476066605500150000ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Marek Lindner */ #ifndef __AP51_FLASH_PROTO_H__ #define __AP51_FLASH_PROTO_H__ #include #include #include struct node; #define NET_IP_ALIGN 2 enum tcp_status { TCP_STATUS_SYN_SENT, TCP_STATUS_ESTABLISHED, TCP_STATUS_TELNET_READY, }; struct tcp_state { char *packet_buff; enum tcp_status status; unsigned int his_seq; unsigned int his_ack_seq; unsigned int his_last_len; unsigned int my_seq; unsigned int my_ack_seq; }; struct image_state { int fd; unsigned int bytes_sent; unsigned int file_size; unsigned int total_bytes_sent; unsigned int flash_size; unsigned int offset; unsigned short last_packet_size; unsigned short block_acked; unsigned short block_sent; /* flags */ unsigned char count_globally:1; }; int arp_req_send(const uint8_t *src_mac, const uint8_t *dst_mac, unsigned int src_ip, unsigned int dst_ip); int tftp_init_upload(struct node *node); int netconsole_init_upload(struct node *node); void telnet_handle_connection(struct node *node); int telnet_send_cmd(struct node *node, const char *cmd); void handle_eth_packet(char *packet_buff, int packet_buff_len); int proto_init(void); void proto_free(void); static inline void store_ip_addr(void *dst, uint32_t ip) { memcpy(dst, &ip, sizeof(ip)); } static inline uint32_t load_ip_addr(void *src) { uint32_t ip; memcpy(&ip, src, sizeof(ip)); return ip; } #if defined(DEBUG) static inline int len_check(int buff_len, int req_len, char *desc) #else static inline int len_check(int buff_len, int req_len, char (*desc)__attribute__((unused))) #endif { if (buff_len >= req_len) return 1; #if defined(DEBUG) fprintf(stderr, "Warning - dropping received %s packet as it is smaller than expected: %i (required: %i)\n", desc, buff_len, req_len); #endif return 0; } #endif /* __AP51_FLASH_PROTO_H__ */ ap51-flash-2025.0/router_images.c000066400000000000000000000543101476066605500164760ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-FileCopyrightText: Marek Lindner * SPDX-FileCopyrightText: Sven Eckelmann */ #include "router_images.h" #include #include #include #include #include #include #include #include #include #include #include "ap51-flash.h" #include "ap51-flash-res.h" #include "compat.h" #include "flash.h" #include "fwcfg.h" #include "list.h" #include "proto.h" #include "router_types.h" static const char fwupgradecfg[] = "fwupgrade.cfg"; static const char fwupgradecfgsig[] = "fwupgrade.cfg.sig"; #define TFTP_PAYLOAD_SIZE 512 #if defined(EMBED_UBOOT) && defined(LINUX) extern unsigned long _binary_img_uboot_start; extern unsigned long _binary_img_uboot_end; #endif #if defined(EMBED_UBNT) && defined(LINUX) extern unsigned long _binary_img_ubnt_start; extern unsigned long _binary_img_ubnt_end; #endif #if defined(EMBED_CI) && defined(LINUX) extern unsigned long _binary_img_ci_start; extern unsigned long _binary_img_ci_end; #endif #if defined(EMBED_CE) && defined(LINUX) extern unsigned long _binary_img_ce_start; extern unsigned long _binary_img_ce_end; #endif #if defined(EMBED_ZYXEL) && defined(LINUX) extern unsigned long _binary_img_zyxel_start; extern unsigned long _binary_img_zyxel_end; #endif struct router_info *router_image_router_get(struct router_image *router_image, const char *router_desc) { struct router_info *router_info; list_for_each_entry(router_info, &router_image->router_list, list) { if (strcasecmp(router_info->router_name, router_desc) != 0) continue; return router_info; } return NULL; } static struct router_info *router_image_router_add(struct router_image *router_image, const char *router_desc) { struct router_info *router_info; router_info = router_image_router_get(router_image, router_desc); if (router_info) return router_info; router_info = malloc(sizeof(struct router_info)); if (!router_info) return NULL; memset(router_info, 0, sizeof(struct router_info)); strncpy(router_info->router_name, router_desc, sizeof(router_info->router_name)); router_info->router_name[sizeof(router_info->router_name) - 1] = '\0'; router_info->file_size = 0; list_add(&router_info->list, &router_image->router_list); return router_info; } static struct file_info *_router_image_get_file(struct list_head *file_list, const char *file_name) { struct file_info *file_info; list_for_each_entry(file_info, file_list, list) { if (strcasecmp(file_info->file_name, file_name) != 0) continue; return file_info; } return NULL; } static const char *router_get_file_postfix(const struct router_type *router_type) { if (router_type->image_desc) return router_type->image_desc; return router_type->desc; } struct file_info *router_image_get_file(struct router_type *router_type, const char *file_name) { struct file_info *file_info = NULL; char file_name_buff[FILE_NAME_MAX_LENGTH]; int ret; if (strcmp(file_name, fwupgradecfg) == 0) { ret = snprintf(file_name_buff, FILE_NAME_MAX_LENGTH, "%s-%s", file_name, router_get_file_postfix(router_type)); if (ret >= FILE_NAME_MAX_LENGTH) return NULL; file_info = _router_image_get_file(&router_type->image->file_list, file_name_buff); } if (strcmp(file_name, fwupgradecfgsig) == 0) { ret = snprintf(file_name_buff, FILE_NAME_MAX_LENGTH, "%s-%s.sig", fwupgradecfg, router_get_file_postfix(router_type)); if (ret >= FILE_NAME_MAX_LENGTH) return NULL; file_info = _router_image_get_file(&router_type->image->file_list, file_name_buff); } if (!file_info) file_info = _router_image_get_file(&router_type->image->file_list, file_name); return file_info; } static struct file_info *_router_image_add_file(struct router_image *router_image, const char *file_name) { struct file_info *file_info; file_info = _router_image_get_file(&router_image->file_list, file_name); if (file_info) return file_info; file_info = malloc(sizeof(struct file_info)); if (!file_info) return NULL; memset(file_info, 0, sizeof(struct file_info)); strncpy(file_info->file_name, file_name, sizeof(file_info->file_name)); file_info->file_name[sizeof(file_info->file_name) - 1] = '\0'; list_add(&file_info->list, &router_image->file_list); return file_info; } static int router_image_add_file(struct router_image *router_image, const char *file_name, int file_size, int file_fsize, int file_offset) { struct file_info *file_info; file_info = _router_image_add_file(router_image, file_name); if (!file_info) return 1; file_info->file_size = file_size; file_info->file_fsize = file_fsize; file_info->file_offset = file_offset; return 0; } unsigned int router_image_get_size(struct router_type *router_type) { const char *router_desc; const struct router_info *router_info; if (router_type->image_desc) router_desc = router_type->image_desc; else router_desc = router_type->desc; router_info = router_image_router_get(router_type->image, router_desc); if (router_info && router_info->file_size) return router_info->file_size; return router_type->image->file_size; } struct file_info *router_image_get_file_info(struct router_image *router_image, const char *file_name) { struct file_info *file_info; file_info = _router_image_get_file(&router_image->file_list, file_name); if (!file_info) return NULL; return file_info; } static void router_image_set_size(struct router_image *router_image, const char *router_desc, unsigned int size) { struct router_info *router_info; list_for_each_entry(router_info, &router_image->router_list, list) { if (router_desc && strcasecmp(router_info->router_name, router_desc) != 0) continue; if (!router_desc && router_info->file_size) continue; router_info->file_size = size; } } static int uboot_verify(struct router_image *router_image, const char *buff, unsigned int buff_len, int size) { int ret; if (buff_len < 4) return 0; /* uboot magic header */ if ((buff[0] != 0x27) || (buff[1] != 0x05) || (buff[2] != 0x19) || (buff[3] != 0x56)) return 0; ret = router_image_add_file(router_image, "mr500.bin", size, size, 0); if (ret) return 0; ret = router_image_add_file(router_image, "firmware.bin", size, size, 0); if (ret) return 0; router_image->file_size = size; return 1; } static int ubnt_verify(struct router_image *router_image, const char *buff, unsigned int buff_len, int size) { if (buff_len < 4) return 0; /* ubnt magic header */ if ((strncmp(buff, "UBNT", 4) != 0) && (strncmp(buff, "OPEN", 4) != 0)) return 0; router_image->file_size = size; return 1; } static int ci_verify(struct router_image *router_image, const char *buff, unsigned int buff_len, int size) { unsigned int kernel_size, rootfs_size; int ret; if (buff_len < 64) return 0; /* combined image magic header */ if ((buff[0] != 'C') || (buff[1] != 'I')) return 0; sscanf(buff, "CI%08x%08x", &kernel_size, &rootfs_size); if ((!kernel_size) || (!rootfs_size)) return 0; ret = router_image_add_file(router_image, "kernel", kernel_size, ((kernel_size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE, 64 * 1024); if (ret) return 0; ret = router_image_add_file(router_image, "rootfs", rootfs_size, ((rootfs_size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE, (64 * 1024) + kernel_size); if (ret) return 0; router_image->file_size = size - (64 * 1024); return 1; } static int strendswith(const char *str, const char *end) { size_t end_len = strlen(end); size_t str_len = strlen(str); if (end_len > str_len) return 0; if (strcmp(&str[str_len - end_len], end) == 0) return 1; return 0; } static void ce_calculate_router_file_size(struct router_image *router_image) { struct file_info *file_info; const char *router_desc; const char *file_name; unsigned int size; list_for_each_entry(file_info, &router_image->file_list, list) { file_name = file_info->file_name; if (strncmp(file_name, fwupgradecfg, strlen(fwupgradecfg)) != 0) continue; if (strendswith(file_name, ".sig")) continue; router_desc = NULL; if (file_name[strlen(fwupgradecfg)] == '-') router_desc = &file_name[strlen(fwupgradecfg) + 1]; size = fwupgrade_cfg_read_sizes(router_image, file_info); if (!size) continue; router_image_set_size(router_image, router_desc, size); } } static int ce_verify(struct router_image *router_image, const char *buff, unsigned int buff_len, int size) { char name_buff[33], *name_ptr, md5_buff[33]; unsigned int num_files, hdr_offset, file_offset, file_size = 0; unsigned image_size = 0; unsigned int ce_version = 0, hdr_offset_sec; int ret; file_offset = 64 * 1024; if (buff_len < 100) return 0; /* ext combined image magic header */ if ((buff[0] != 'C') || (buff[1] != 'E')) return 0; /* the old format does not have a version field */ if (isxdigit(buff[2]) && isxdigit(buff[3])) { ret = sscanf(buff, "CE%02x", &ce_version); if (ret != 1) return 0; } switch (ce_version) { case 0: ret = sscanf(buff, "CE%10s%02x", name_buff, &num_files); if (ret != 2) return 0; hdr_offset = 14; hdr_offset_sec = 28; break; case 1: ret = sscanf(buff, "CE%*02x%32s%02x", name_buff, &num_files); if (ret != 2) return 0; hdr_offset = 38; hdr_offset_sec = 72; break; default: /* unsupported version */ return 0; } name_ptr = strtok(name_buff, ","); while (name_ptr) { router_image_router_add(router_image, name_ptr); name_ptr = strtok(NULL, ","); } while (num_files > 0) { if (hdr_offset + hdr_offset_sec > buff_len) { fprintf(stderr, "Error - buffer too small to parse CE header\n"); return 0; } switch (ce_version) { case 0: ret = sscanf(buff + hdr_offset, "%20s%08x", name_buff, &file_size); if (ret != 2) return 0; break; case 1: ret = sscanf(buff + hdr_offset, "%32s%08x%32s", name_buff, &file_size, md5_buff); if (ret != 3) return 0; break; } ret = router_image_add_file(router_image, name_buff, file_size, file_size, file_offset); if (ret) return 0; file_offset += file_size; hdr_offset += hdr_offset_sec; num_files--; if (strncmp(name_buff, fwupgradecfg, strlen(fwupgradecfg)) == 0) { if (strlen(fwupgradecfg) + 1 < strlen(name_buff) && !strendswith(name_buff, ".sig")) router_image_router_add(router_image, &name_buff[strlen(fwupgradecfg) + 1]); /*** * Don't add fwupgrade.cfg* files to the file size in * order to detect the end-of-flash correctly. */ continue; } image_size += file_size; } if (image_size > (unsigned)size) { fprintf(stderr, "Error - bogus CE image: claimed size bigger than actual size\n"); return 0; } router_image->file_size = image_size; /* calculate size of files referenced by fwupgrade.cfg of each router */ ce_calculate_router_file_size(router_image); return 1; } static int zyxel_verify(struct router_image *router_image, const char *buff, unsigned int buff_len, int size) { /* zyxel header: * 4 bytes: checksum of the rootfs image * 4 bytes: length of the contained rootfs image file (big endian) * 32 bytes: Firmware Version string (NUL terminated, 0xff padded) * 4 bytes: checksum over the header partition (big endian - see below) * 64 bytes: Model (e.g. "NBG6617", NUL termiated, 0xff padded) * 4 bytes: checksum of the kernel partition * 4 bytes: length of the contained kernel image file (big endian) * rest: 0xff padding (To erase block size) */ struct zyxel_header { uint32_t rootfs_chksum; uint32_t rootfs_size; char firmware_version[32]; uint32_t header_chksum; char model[64]; uint32_t kernel_chksum; uint32_t kernel_size; }; struct zyxel_header *zyxel_header; unsigned kernel_size, rootfs_size; unsigned zyxel_hdr_size = 64 * 1024; int ret; if (buff_len < sizeof(*zyxel_header)) return 0; zyxel_header = (struct zyxel_header *)buff; kernel_size = ntohl(zyxel_header->kernel_size); rootfs_size = ntohl(zyxel_header->rootfs_size); if ((unsigned)size != zyxel_hdr_size + kernel_size + rootfs_size) return 0; ret = router_image_add_file(router_image, "ras.bin", size, size, 0); if (ret) return 0; router_image->file_size = size; return 1; } static int router_image_init_embedded(struct router_image *router_image) { int ret = 0; #if defined(LINUX) || defined(OSX) router_image->embedded_img = router_image->embedded_img_pre_check; ret = router_image->image_verify(router_image, router_image->embedded_img_pre_check, (unsigned)router_image->embedded_file_size, (unsigned)router_image->embedded_file_size); if (ret != 1) router_image->embedded_img = NULL; #elif defined(WIN32) HGLOBAL hGlobal; HRSRC hRsrc; int size; char *buff; hRsrc = FindResource(NULL, MAKEINTRESOURCE(router_image->embedded_img_res), RT_RCDATA); if (hRsrc) { hGlobal = LoadResource(NULL, hRsrc); buff = LockResource(hGlobal); size = SizeofResource(NULL, hRsrc); router_image->embedded_img = buff; ret = router_image->image_verify(router_image, buff, size, size); if (ret != 1) router_image->embedded_img = NULL; } #endif return ret; } struct router_image img_uboot = { .type = IMAGE_TYPE_UBOOT, .desc = "uboot image", .image_verify = uboot_verify, }; struct router_image img_ubnt = { .type = IMAGE_TYPE_UBNT, .desc = "ubiquiti image", .image_verify = ubnt_verify, }; struct router_image img_ci = { .type = IMAGE_TYPE_CI, .desc = "combined image", .image_verify = ci_verify, }; struct router_image img_ce = { .type = IMAGE_TYPE_CE, .desc = "combined ext image", .image_verify = ce_verify, }; struct router_image img_zyxel = { .type = IMAGE_TYPE_ZYXEL, .desc = "Zyxel image", .image_verify = zyxel_verify, }; static struct router_image *router_images[] = { &img_uboot, &img_ubnt, &img_ci, &img_ce, &img_zyxel, NULL, }; void router_images_init(void) { struct router_image **router_image; for (router_image = router_images; *router_image; ++router_image) { INIT_LIST_HEAD(&(*router_image)->file_list); INIT_LIST_HEAD(&(*router_image)->router_list); } } void router_images_init_embedded(void) { struct router_image **router_image; int ret; for (router_image = router_images; *router_image; ++router_image) { if ((*router_image)->path || (*router_image)->embedded_img) continue; if (!(*router_image)->image_verify) continue; switch ((*router_image)->type) { case IMAGE_TYPE_UNKNOWN: continue; case IMAGE_TYPE_UBOOT: #if defined(EMBED_UBOOT) #if defined(LINUX) (*router_image)->embedded_img_pre_check = (char *)&_binary_img_uboot_start; (*router_image)->embedded_file_size = (uintptr_t)&_binary_img_uboot_end - (uintptr_t)&_binary_img_uboot_start; #elif defined(OSX) (*router_image)->embedded_img_pre_check = getsectdata("__DATA", "_binary_img_uboot", &(*router_image)->embedded_file_size); if ((*router_image)->embedded_img_pre_check) (*router_image)->embedded_img_pre_check += _dyld_get_image_vmaddr_slide(0); #elif defined(WIN32) (*router_image)->embedded_img_res = IDR_UBOOT_IMG; #endif #endif break; case IMAGE_TYPE_UBNT: #if defined(EMBED_UBNT) #if defined(LINUX) (*router_image)->embedded_img_pre_check = (char *)&_binary_img_ubnt_start; (*router_image)->embedded_file_size = (uintptr_t)&_binary_img_ubnt_end - (uintptr_t)&_binary_img_ubnt_start; #elif defined(OSX) (*router_image)->embedded_img_pre_check = getsectdata("__DATA", "_binary_img_ubnt", &(*router_image)->embedded_file_size); if ((*router_image)->embedded_img_pre_check) (*router_image)->embedded_img_pre_check += _dyld_get_image_vmaddr_slide(0); #elif defined(WIN32) (*router_image)->embedded_img_res = IDR_UBNT_IMG; #endif #endif break; case IMAGE_TYPE_CI: #if defined(EMBED_CI) #if defined(LINUX) (*router_image)->embedded_img_pre_check = (char *)&_binary_img_ci_start; (*router_image)->embedded_file_size = (uintptr_t)&_binary_img_ci_end - (uintptr_t)&_binary_img_ci_start; #elif defined(OSX) (*router_image)->embedded_img_pre_check = getsectdata("__DATA", "_binary_img_ci", &(*router_image)->embedded_file_size); if ((*router_image)->embedded_img_pre_check) (*router_image)->embedded_img_pre_check += _dyld_get_image_vmaddr_slide(0); #elif defined(WIN32) (*router_image)->embedded_img_res = IDR_CI_IMG; #endif #endif break; case IMAGE_TYPE_CE: #if defined(EMBED_CE) #if defined(LINUX) (*router_image)->embedded_img_pre_check = (char *)&_binary_img_ce_start; (*router_image)->embedded_file_size = (uintptr_t)&_binary_img_ce_end - (uintptr_t)&_binary_img_ce_start; #elif defined(OSX) (*router_image)->embedded_img_pre_check = getsectdata("__DATA", "_binary_img_ce", &(*router_image)->embedded_file_size); if ((*router_image)->embedded_img_pre_check) (*router_image)->embedded_img_pre_check += _dyld_get_image_vmaddr_slide(0); #elif defined(WIN32) (*router_image)->embedded_img_res = IDR_CE_IMG; #endif #endif break; case IMAGE_TYPE_ZYXEL: #if defined(EMBED_ZYXEL) #if defined(LINUX) (*router_image)->embedded_img_pre_check = (char *)&_binary_img_zyxel_start; (*router_image)->embedded_file_size = (uintptr_t)&_binary_img_zyxel_end - (uintptr_t)&_binary_img_zyxel_start; #elif defined(OSX) (*router_image)->embedded_img_pre_check = getsectdata("__DATA", "_binary_img_zyxel", &(*router_image)->embedded_file_size); if ((*router_image)->embedded_img_pre_check) (*router_image)->embedded_img_pre_check += _dyld_get_image_vmaddr_slide(0); #elif defined(WIN32) (*router_image)->embedded_img_res = IDR_ZYXEL_IMG; #endif #endif break; } ret = router_image_init_embedded(*router_image); if (ret != 1) continue; #if defined(DEBUG) printf("init embedded image: %s found (%u bytes)\n", (*router_image)->desc, (*router_image)->file_size); #endif } } void router_images_print_desc(void) { struct router_image **router_image; for (router_image = router_images; *router_image; ++router_image) fprintf(stderr, " * %s\n", (*router_image)->desc); } int router_images_verify_path(const char *image_path) { struct router_image **router_image; char *file_buff = NULL, found_consumer = 0; unsigned int file_buff_size = 64 * 1024; // max CE hdr size int fd, file_size, ret = -1, len; file_buff = malloc(file_buff_size); if (!file_buff) goto out; fd = open(image_path, O_RDONLY | O_BINARY); if (fd < 0) { fprintf(stderr, "Error - can't open image file '%s': %s\n", image_path, strerror(errno)); goto out; } ret = (int)read(fd, file_buff, file_buff_size); if (ret < 0) { fprintf(stderr, "Error - can't read image file '%s': %s\n", image_path, strerror(errno)); goto close_fd; } len = ret; for (router_image = router_images; *router_image; ++router_image) { if ((*router_image)->path || (*router_image)->embedded_img) continue; if (!(*router_image)->image_verify) continue; file_size = (int)lseek(fd, 0, SEEK_END); if (file_size < 0) { fprintf(stderr, "Unable to retrieve file size of '%s': %s\n", image_path, strerror(errno)); continue; } (*router_image)->path = image_path; ret = (*router_image)->image_verify((*router_image), file_buff, len, file_size); if (ret != 1) { (*router_image)->path = NULL; continue; } found_consumer = 1; #if defined(DEBUG) printf("verify image path: %s: %s (%i bytes)\n", image_path, (*router_image)->desc, (*router_image)->file_size); #endif } if (!found_consumer) fprintf(stderr, "Unsupported image '%s': ignoring file\n", image_path); ret = 0; close_fd: close(fd); out: free(file_buff); return ret; } int router_images_open_path(struct node *node) { /* embedded image */ if ((!node->router_type->image->path) && (node->router_type->image->embedded_img) && (node->router_type->image->file_size > 0)) { node->image_state.fd = 1; goto out; } node->image_state.fd = open(node->router_type->image->path, O_RDONLY | O_BINARY); if (node->image_state.fd < 0) fprintf(stderr, "Error - can't open image file '%s': %s\n", node->router_type->image->path, strerror(errno)); out: return node->image_state.fd; } int router_images_read_data(char *dst, struct node *node) { int len = TFTP_PAYLOAD_SIZE, read_len; uint8_t *file_data; off_t reto; if (node->image_state.flash_size - node->image_state.bytes_sent < TFTP_PAYLOAD_SIZE) len = node->image_state.flash_size - node->image_state.bytes_sent; read_len = len; if (node->image_state.file_size < node->image_state.bytes_sent) read_len = 0; else if (node->image_state.file_size < node->image_state.bytes_sent + len) read_len = node->image_state.file_size - node->image_state.bytes_sent; if (node->router_type->image->path) { if (node->image_state.fd < 0) { #if defined(DEBUG) fprintf(stderr, "router_images_read_data(): image has file path but no open fd ??\n"); #endif goto err; } if (read_len > 0) { reto = lseek(node->image_state.fd, node->image_state.bytes_sent + node->image_state.offset, SEEK_SET); if (reto == (off_t) -1) { fprintf(stderr, "Error - seeking in file '%s': %s\n", node->router_type->image->path, strerror(errno)); return -1; } if (read_len != read(node->image_state.fd, dst, read_len)) { fprintf(stderr, "Error - reading from file '%s': %s\n", node->router_type->image->path, strerror(errno)); return -1; } } if (read_len != len) memset(dst + read_len, 0, len - read_len); return len; } else if (node->router_type->image->embedded_img) { file_data = (uint8_t *)node->router_type->image->embedded_img; file_data += node->image_state.bytes_sent; file_data += node->image_state.offset; if (read_len > 0) memcpy(dst, file_data, read_len); if (read_len != len) memset(dst + read_len, 0, len - read_len); return len; } err: return -1; } bool router_images_available(void) { struct router_image **router_image; for (router_image = router_images; *router_image; ++router_image) { if ((*router_image)->path || (*router_image)->embedded_img) return true; } return false; } void router_images_close_path(struct node *node) { if ((node->router_type->image->path) && (node->image_state.fd > 0)) close(node->image_state.fd); node->image_state.fd = -1; } ap51-flash-2025.0/router_images.h000066400000000000000000000043211476066605500165000ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Marek Lindner */ #ifndef __AP51_FLASH_ROUTER_IMAGES_H__ #define __AP51_FLASH_ROUTER_IMAGES_H__ #include #include "ap51-flash.h" #include "list.h" struct node; struct router_type; enum image_type { IMAGE_TYPE_UNKNOWN, IMAGE_TYPE_UBOOT, IMAGE_TYPE_UBNT, IMAGE_TYPE_CI, IMAGE_TYPE_CE, IMAGE_TYPE_ZYXEL, }; struct router_image { struct list_head list; enum image_type type; char desc[DESC_MAX_LENGTH]; int (*image_verify)(struct router_image *router_image, const char *buff, unsigned int buff_len, int size); const char *path; char *embedded_img; #if defined(LINUX) char *embedded_img_pre_check; unsigned int embedded_file_size; #elif defined(OSX) char *embedded_img_pre_check; unsigned long embedded_file_size; #elif defined(WIN32) unsigned int embedded_img_res; #endif unsigned int file_size; struct list_head file_list; struct list_head router_list; }; struct router_info { struct list_head list; char router_name[DESC_MAX_LENGTH]; unsigned int file_size; }; struct file_info { struct list_head list; char file_name[FILE_NAME_MAX_LENGTH]; unsigned int file_offset; unsigned int file_size; unsigned int file_fsize; }; struct router_info *router_image_router_get(struct router_image *router_image, const char *router_desc); struct file_info *router_image_get_file(struct router_type *router_type, const char *file_name); void router_images_init(void); void router_images_init_embedded(void); bool router_images_available(void); void router_images_print_desc(void); int router_images_verify_path(const char *image_path); int router_images_open_path(struct node *node); int router_images_read_data(char *dst, struct node *node); void router_images_close_path(struct node *node); unsigned int router_image_get_size(struct router_type *router_type); struct file_info *router_image_get_file_info(struct router_image *router_image, const char *file_name); extern struct router_image img_uboot; extern struct router_image img_ubnt; extern struct router_image img_ci; extern struct router_image img_ce; extern struct router_image img_zyxel; #endif /* __AP51_FLASH_ROUTER_IMAGES_H__ */ ap51-flash-2025.0/router_netconsole.c000066400000000000000000000065451476066605500174110ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-FileCopyrightText: Antonio Quartulli */ #include "router_netconsole.h" #include #include "compat.h" #include "flash.h" #include "proto.h" #include "router_images.h" #include "router_types.h" static const unsigned int ap121f_ip = 3232235777UL; /* 192.168.1.1 */ static const unsigned int my_ip = 3232235778UL; /* 192.168.1.2 */ static const uint8_t zero_mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const uint8_t ap121f_mac[] = { 0x00, 0x03, 0x7f, 0x09, 0x0b, 0xad }; enum netconsole_state { NETCONSOLE_STATE_WAITING, NETCONSOLE_STATE_STARTED, NETCONSOLE_STATE_DONE, NETCONSOLE_STATE_RESET, }; struct netconsole_priv { enum netconsole_state state; }; static int ap121f_detect_main(const struct router_type *router_type __attribute__((unused)), void (*priv)__attribute__((unused)), const char *packet_buff, int packet_buff_len) { struct ether_arp *arphdr; int ret = 0; if (!len_check(packet_buff_len, sizeof(struct ether_arp), "ARP")) goto out; arphdr = (struct ether_arp *)packet_buff; if (arphdr->ea_hdr.ar_op != htons(ARPOP_REQUEST)) goto out; if (*((unsigned int *)arphdr->arp_spa) != htonl(ap121f_ip)) goto out; if (*((unsigned int *)arphdr->arp_tpa) != htonl(my_ip)) goto out; if (memcmp(arphdr->arp_sha, ap121f_mac, ETH_ALEN)) goto out; if (memcmp(arphdr->arp_tha, zero_mac, ETH_ALEN)) goto out; ret = 1; out: return ret; } static void netconsole_detect_post(struct node *node, const char *packet_buff, int packet_buff_len) { struct netconsole_priv *priv = node->router_priv; struct ether_arp *arphdr; if (!len_check(packet_buff_len, sizeof(struct ether_arp), "ARP")) goto out; arphdr = (struct ether_arp *)packet_buff; node->flash_mode = FLASH_MODE_NETCONSOLE; node->his_ip_addr = load_ip_addr(arphdr->arp_spa); node->our_ip_addr = load_ip_addr(arphdr->arp_tpa); priv->state = NETCONSOLE_STATE_WAITING; out: return; } const struct router_type ap121f = { .desc = "Alfa Network AP121F", .detect_main = ap121f_detect_main, .detect_post = netconsole_detect_post, .image = &img_uboot, .priv_size = sizeof(struct netconsole_priv), }; void handle_netconsole_packet(const char *packet_buff, int packet_buff_len, struct node *node) { struct netconsole_priv *priv = node->router_priv; #define PROMPT_STR "u-boot> " #define DONE_STR "DONE!" // fprintf(stderr, "received netconsole packet: '%s'\n", packet_buff); switch (priv->state) { case NETCONSOLE_STATE_WAITING: if (packet_buff_len < (int)strlen(PROMPT_STR)) return; if (strncmp(packet_buff, PROMPT_STR, strlen(PROMPT_STR)) != 0) return; netconsole_init_upload(node); priv->state = NETCONSOLE_STATE_STARTED; break; case NETCONSOLE_STATE_STARTED: /* check if we received the completion message */ if (packet_buff_len < (int)strlen(DONE_STR)) return; if (strncmp(packet_buff, DONE_STR, strlen(DONE_STR)) != 0) return; fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: flash complete. Rebooting\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc); priv->state = NETCONSOLE_STATE_DONE; node->status = NODE_STATUS_REBOOTED; #if defined(CLEAR_SCREEN) num_nodes_flashed++; #endif break; default: break; } } ap51-flash-2025.0/router_netconsole.h000066400000000000000000000006671476066605500174150ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Antonio Quartulli */ #ifndef __AP51_FLASH_ROUTER_NETCONSOLE_H__ #define __AP51_FLASH_ROUTER_NETCONSOLE_H__ #define IPPORT_NETCONSOLE 6666 struct node; extern const struct router_type ap121f; void handle_netconsole_packet(const char *packet_buff, int packet_buff_len, struct node *node); #endif /* __AP51_FLASH_ROUTER_NETCONSOLE_H__ */ ap51-flash-2025.0/router_redboot.c000066400000000000000000000255271476066605500166770ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-FileCopyrightText: Marek Lindner */ #include "router_redboot.h" #include #include #include #include #include #include "ap51-flash.h" #include "compat.h" #include "flash.h" #include "proto.h" #include "router_images.h" #include "router_types.h" enum redboot_state { REDBOOT_STATE_INIT, REDBOOT_STATE_VERSION, REDBOOT_STATE_IP_ADDR, REDBOOT_STATE_LD_KERNEL, REDBOOT_STATE_FL_KERNEL, REDBOOT_STATE_LD_ROOTFS, REDBOOT_STATE_FL_ROOTFS, REDBOOT_STATE_BSCRIPT, REDBOOT_STATE_BSCRIPTK, REDBOOT_STATE_EXEC, REDBOOT_STATE_EXECY, REDBOOT_STATE_RESET, REDBOOT_STATE_FINISHED, REDBOOT_STATE_FIS_INIT, REDBOOT_STATE_FIS_INITY, REDBOOT_STATE_FAILED, }; static const unsigned int ubnt_ip = 3232235796UL; /* 192.168.1.20 */ struct redboot_priv { int arp_count; enum redboot_state redboot_state; struct redboot_type *redboot_type; char *version_info; }; static int redboot_8mb_detect(struct node *node) { struct redboot_priv *redboot_priv = node->router_priv; unsigned long device_size = 0; char *flash_str; int num_blocks = 0, ret = 0; flash_str = strstr(redboot_priv->version_info, "FLASH:"); if (!flash_str) goto out; sscanf(flash_str, "FLASH: 0x%*08x - 0x%*08x, %d blocks of 0x%08lx bytes each", &num_blocks, &device_size); if ((!num_blocks) || (!device_size)) goto out; if (num_blocks * device_size != 0x800000) goto out; #if defined(DEBUG) fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: flash size of 8 MB was detected ...\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc); #endif ret = 1; out: return ret; } #if defined(DEBUG) static int redboot_4mb_detect(struct node *node) #else static int redboot_4mb_detect(struct node (*node)__attribute__((unused))) #endif { /* default redboot type */ #if defined(DEBUG) fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: flash size of 4 MB was detected (default) ...\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc); #endif return 1; } static const struct redboot_type redboot_8mb = { .flash_size = 0x7A0000, .freememlo = 0x80041000, /* %{FREEMEMLO} provokes errors on the meraki mini */ .flash_addr = 0xa8030000, .kernel_load_addr = 0x80041000, .detect = redboot_8mb_detect, }; static const struct redboot_type redboot_4mb = { .flash_size = 0x3A0000, .freememlo = 0, /* we can use %{FREEMEMLO} */ .flash_addr = 0xbfc30000, .kernel_load_addr = 0x80041000, .detect = redboot_4mb_detect, }; static const struct redboot_type *redboot_types[] = { &redboot_8mb, &redboot_4mb, NULL, }; static int redboot_type_detect(struct node *node) { struct redboot_priv *redboot_priv = node->router_priv; const struct redboot_type **redboot_type; int ret = 0; for (redboot_type = redboot_types; *redboot_type; ++redboot_type) { if (!(*redboot_type)->detect) continue; ret = (*redboot_type)->detect(node); if (ret != 1) continue; redboot_priv->redboot_type = (struct redboot_type *)(*redboot_type); break; } return ret; } void redboot_main(struct node *node, const char *telnet_msg) { struct redboot_priv *redboot_priv = node->router_priv; struct file_info *file_info; unsigned long req_flash_size; char buff[100]; switch (redboot_priv->redboot_state) { case REDBOOT_STATE_INIT: redboot_priv->redboot_state = REDBOOT_STATE_VERSION; telnet_send_cmd(node, "version\n"); break; case REDBOOT_STATE_VERSION: redboot_priv->version_info = malloc(strlen(telnet_msg) + 1); if (!redboot_priv->version_info) goto redboot_failure; strncpy(redboot_priv->version_info, telnet_msg, strlen(telnet_msg) + 1); redboot_priv->version_info[strlen(telnet_msg)] = '\0'; redboot_type_detect(node); req_flash_size = ((node->router_type->image->file_size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE; if (redboot_priv->redboot_type->flash_size < req_flash_size) { fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: %s: image size '%s' of 0x%08lx exceeds device capacity: 0x%08lx\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc, node->router_type->image->path, req_flash_size, redboot_priv->redboot_type->flash_size); goto redboot_failure; } sprintf(buff, "ip_addr -l %d.%d.%d.%d/8 -h %d.%d.%d.%d\n", ((unsigned char *)&node->his_ip_addr)[0], ((unsigned char *)&node->his_ip_addr)[1], ((unsigned char *)&node->his_ip_addr)[2], ((unsigned char *)&node->his_ip_addr)[3], ((unsigned char *)&node->our_ip_addr)[0], ((unsigned char *)&node->our_ip_addr)[1], ((unsigned char *)&node->our_ip_addr)[2], ((unsigned char *)&node->our_ip_addr)[3]); printf("[%02x:%02x:%02x:%02x:%02x:%02x]: %s: setting IP address ...\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc); telnet_send_cmd(node, buff); redboot_priv->redboot_state = REDBOOT_STATE_IP_ADDR; break; case REDBOOT_STATE_IP_ADDR: if (redboot_priv->redboot_type->freememlo) sprintf(buff, "load -r -b 0x%08lx -m tftp kernel\n", redboot_priv->redboot_type->freememlo); else sprintf(buff, "load -r -b %%{FREEMEMLO} -m tftp kernel\n"); telnet_send_cmd(node, buff); redboot_priv->redboot_state = REDBOOT_STATE_LD_KERNEL; break; case REDBOOT_STATE_LD_KERNEL: if ((unsigned int)node->image_state.bytes_sent < node->image_state.flash_size) { fprintf(stderr, "Error transferring kernel, send: %d, expected: %d\n", node->image_state.bytes_sent, node->image_state.flash_size); goto redboot_failure; } printf("[%02x:%02x:%02x:%02x:%02x:%02x]: %s: initializing partitions ...\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc); telnet_send_cmd(node, "fis init\n"); redboot_priv->redboot_state = REDBOOT_STATE_FIS_INIT; break; case REDBOOT_STATE_FIS_INIT: telnet_send_cmd(node, "y\n"); redboot_priv->redboot_state = REDBOOT_STATE_FIS_INITY; break; case REDBOOT_STATE_FIS_INITY: sprintf(buff, "fis create -e 0x%08lx -r 0x%08lx vmlinux.bin.l7\n", redboot_priv->redboot_type->kernel_load_addr, redboot_priv->redboot_type->kernel_load_addr); printf("[%02x:%02x:%02x:%02x:%02x:%02x]: %s: flashing kernel ...\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc); telnet_send_cmd(node, buff); redboot_priv->redboot_state = REDBOOT_STATE_FL_KERNEL; break; case REDBOOT_STATE_FL_KERNEL: if (redboot_priv->redboot_type->freememlo) sprintf(buff, "load -r -b 0x%08lx -m tftp rootfs\n", redboot_priv->redboot_type->freememlo); else sprintf(buff, "load -r -b %%{FREEMEMLO} -m tftp rootfs\n"); telnet_send_cmd(node, buff); redboot_priv->redboot_state = REDBOOT_STATE_LD_ROOTFS; break; case REDBOOT_STATE_LD_ROOTFS: file_info = router_image_get_file(node->router_type, "kernel"); if (!file_info) return; sprintf(buff, "fis create -f 0x%08lx -l 0x%08lx rootfs\n", redboot_priv->redboot_type->flash_addr + file_info->file_fsize, redboot_priv->redboot_type->flash_size - file_info->file_fsize); printf("[%02x:%02x:%02x:%02x:%02x:%02x]: %s: flashing rootfs ...\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc); telnet_send_cmd(node, buff); redboot_priv->redboot_state = REDBOOT_STATE_FL_ROOTFS; break; case REDBOOT_STATE_FL_ROOTFS: printf("[%02x:%02x:%02x:%02x:%02x:%02x]: %s: setting boot_script_data ...\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc); telnet_send_cmd(node, "fconfig -d boot_script_data\n"); redboot_priv->redboot_state = REDBOOT_STATE_BSCRIPT; break; case REDBOOT_STATE_BSCRIPT: telnet_send_cmd(node, "fis load -l vmlinux.bin.l7\n"); redboot_priv->redboot_state = REDBOOT_STATE_BSCRIPTK; break; case REDBOOT_STATE_BSCRIPTK: telnet_send_cmd(node, "exec\n\n"); redboot_priv->redboot_state = REDBOOT_STATE_EXEC; break; case REDBOOT_STATE_EXEC: telnet_send_cmd(node, "y\n"); redboot_priv->redboot_state = REDBOOT_STATE_EXECY; break; case REDBOOT_STATE_EXECY: telnet_send_cmd(node, "reset\n"); redboot_priv->redboot_state = REDBOOT_STATE_FINISHED; node->status = NODE_STATUS_RESET_SENT; break; default: break; } return; redboot_failure: redboot_priv->redboot_state = REDBOOT_STATE_FAILED; // TODO: close telnet connection ? return; } static int redboot_detect_main(const struct router_type *router_type __attribute__((unused)), void *priv, const char *packet_buff, int packet_buff_len) { struct ether_arp *arphdr; struct redboot_priv *redboot_priv = priv; int ret = 0; if (!len_check(packet_buff_len, sizeof(struct ether_arp), "ARP")) goto out; arphdr = (struct ether_arp *)packet_buff; if (arphdr->ea_hdr.ar_op != htons(ARPOP_REQUEST)) goto out; /* we are waiting for gratuitous ARP requests */ if (load_ip_addr(arphdr->arp_spa) != load_ip_addr(arphdr->arp_tpa)) goto out; /** * use gratuitous ARP requests from ubnt devices with care * the ubnt pico can be in redboot or tftp server mode */ if (load_ip_addr(arphdr->arp_spa) == htonl(ubnt_ip)) { if (redboot_priv->arp_count < 5) { redboot_priv->arp_count++; goto out; } } /* printf("redboot_detect_main(): receiving packet: len: %i (arp: %i, arp_count: %i)\n", packet_buff_len, sizeof(struct ether_arp), redboot_priv->arp_count);*/ ret = 1; out: return ret; } static void redboot_detect_post(struct node *node, const char *packet_buff, int packet_buff_len) { struct ether_arp *arphdr; if (!len_check(packet_buff_len, sizeof(struct ether_arp), "ARP")) goto out; arphdr = (struct ether_arp *)packet_buff; if (arphdr->arp_tpa[3] == 20) arphdr->arp_tpa[3] = 1; else arphdr->arp_tpa[3] = 20; node->flash_mode = FLASH_MODE_REDBOOT; node->his_ip_addr = load_ip_addr(arphdr->arp_spa); node->our_ip_addr = load_ip_addr(arphdr->arp_tpa); out: return; } const struct router_type redboot = { .desc = "redboot", .detect_pre = NULL, .detect_main = redboot_detect_main, .detect_post = redboot_detect_post, .image = &img_ci, .priv_size = sizeof(struct redboot_priv), }; ap51-flash-2025.0/router_redboot.h000066400000000000000000000010321476066605500166650ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Marek Lindner */ #ifndef __AP51_FLASH_ROUTER_REDBOOT_H__ #define __AP51_FLASH_ROUTER_REDBOOT_H__ struct node; struct redboot_type { unsigned long flash_size; unsigned long freememlo; unsigned long flash_addr; unsigned long kernel_load_addr; int (*detect)(struct node *node); }; extern const struct router_type redboot; void redboot_main(struct node *node, const char *telnet_msg); #endif /* __AP51_FLASH_ROUTER_REDBOOT_H__ */ ap51-flash-2025.0/router_tftp_client.c000066400000000000000000000463551476066605500175560ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-FileCopyrightText: Marek Lindner * SPDX-FileCopyrightText: Sven Eckelmann */ #include "router_tftp_client.h" #include #include #include #include #include "compat.h" #include "flash.h" #include "proto.h" #include "router_images.h" #include "router_types.h" #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) #endif #define MR500_IP 3232260872UL /* 192.168.99.8 */ #define OM2P_IP 3232261128UL /* 192.168.100.8 */ #define ZYXEL_IP 3232235875UL /* 192.168.1.99 */ struct mr500_priv { time_t start_flash; }; struct om2p_priv { time_t start_flash; }; static int tftp_client_detect_main(const struct router_type *router_type, void (*priv)__attribute__((unused)), const char *packet_buff, int packet_buff_len) { const struct mac_accept_entry *mac_accept_entry; struct router_tftp_client *tftp_client; struct ether_arp *arphdr; uint8_t arp_u8; uint8_t mac_u8; bool mismatch; size_t i; size_t j; tftp_client = container_of(router_type, struct router_tftp_client, router_type); if (!len_check(packet_buff_len, sizeof(struct ether_arp), "ARP")) return 0; arphdr = (struct ether_arp *)packet_buff; if (arphdr->ea_hdr.ar_op != htons(ARPOP_REQUEST)) return 0; if (*((unsigned int *)arphdr->arp_tpa) != htonl(tftp_client->ip)) return 0; if (tftp_client->mac_accept_entries_num == 0) return 1; for (i = 0; i < tftp_client->mac_accept_entries_num; i++) { mac_accept_entry = &tftp_client->mac_accept_entries[i]; mismatch = false; for (j = 0; j < 6; j++) { arp_u8 = arphdr->arp_tha[j] & mac_accept_entry->mask[j]; mac_u8 = mac_accept_entry->mac[j] & mac_accept_entry->mask[j]; if (arp_u8 != mac_u8) { mismatch = true; break; } } if (!mismatch) return 1; } return 0; } static void tftp_client_detect_post(struct node *node, const char *packet_buff, int packet_buff_len) { struct ether_arp *arphdr; if (!len_check(packet_buff_len, sizeof(struct ether_arp), "ARP")) goto out; arphdr = (struct ether_arp *)packet_buff; node->flash_mode = FLASH_MODE_TFTP_CLIENT; node->his_ip_addr = load_ip_addr(arphdr->arp_spa); node->our_ip_addr = load_ip_addr(arphdr->arp_tpa); out: return; } void tftp_client_flash_time_set(struct node *node) { struct mr500_priv *mr500_priv; struct om2p_priv *om2p_priv; if (node->router_type == &mr500.router_type) { mr500_priv = node->router_priv; mr500_priv->start_flash = time(NULL); } else if ((node->router_type == &mr600.router_type) || (node->router_type == &mr900.router_type) || (node->router_type == &mr1750.router_type) || (node->router_type == &a40.router_type) || (node->router_type == &a42.router_type) || (node->router_type == &a60.router_type) || (node->router_type == &a62.router_type) || (node->router_type == &ap440.router_type) || (node->router_type == &ap840.router_type) || (node->router_type == &ap840e.router_type) || (node->router_type == &om2p.router_type) || (node->router_type == &om5p.router_type) || (node->router_type == &om5pac.router_type) || (node->router_type == &om5pan.router_type) || (node->router_type == &p60.router_type) || (node->router_type == &d200.router_type) || (node->router_type == &g200.router_type) || (node->router_type == &pa300.router_type) || (node->router_type == &pa1200.router_type) || (node->router_type == &pa2200.router_type) || (node->router_type == &pax1800.router_type) || (node->router_type == &pax1800v2.router_type) || (node->router_type == &pax5400.router_type) || (node->router_type == &tw420.router_type) || (node->router_type == &zyxel.router_type)) { om2p_priv = node->router_priv; om2p_priv->start_flash = time(NULL); } } int tftp_client_flash_completed(struct node *node) { struct mr500_priv *mr500_priv; struct om2p_priv *om2p_priv; time_t time2flash; if (node->router_type == &mr500.router_type) { mr500_priv = node->router_priv; time2flash = mr500_priv->start_flash + 45 + (node->image_state.total_bytes_sent / 65536); } else if ((node->router_type == &mr600.router_type) || (node->router_type == &mr900.router_type) || (node->router_type == &mr1750.router_type) || (node->router_type == &a40.router_type) || (node->router_type == &a42.router_type) || (node->router_type == &a60.router_type) || (node->router_type == &a62.router_type) || (node->router_type == &ap440.router_type) || (node->router_type == &ap840.router_type) || (node->router_type == &ap840e.router_type) || (node->router_type == &om2p.router_type) || (node->router_type == &om5p.router_type) || (node->router_type == &om5pac.router_type) || (node->router_type == &om5pan.router_type) || (node->router_type == &p60.router_type) || (node->router_type == &d200.router_type) || (node->router_type == &g200.router_type) || (node->router_type == &pa300.router_type) || (node->router_type == &pa1200.router_type) || (node->router_type == &pa2200.router_type) || (node->router_type == &pax1800.router_type) || (node->router_type == &pax1800v2.router_type) || (node->router_type == &pax5400.router_type) || (node->router_type == &tw420.router_type) || (node->router_type == &zyxel.router_type)) { om2p_priv = node->router_priv; time2flash = om2p_priv->start_flash + 10 + (node->image_state.total_bytes_sent / 65536); } else { return 0; } if (time(NULL) < time2flash) return 0; return 1; } const struct router_tftp_client mr500 = { .router_type = { .desc = "MR500 router", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_uboot, .priv_size = sizeof(struct mr500_priv), }, .ip = MR500_IP, }; static const struct mac_accept_entry mr600_mac_accept[] = { { .mac = {'M', 'R', '6', '0', '0', 0}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, }, }; const struct router_tftp_client mr600 = { .router_type = { .desc = "MR600", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = mr600_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(mr600_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry mr900_mac_accept[] = { { .mac = {'M', 'R', '9', '0', '0', 0}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, }, }; const struct router_tftp_client mr900 = { .router_type = { .desc = "MR900", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = mr900_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(mr900_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry mr1750_mac_accept[] = { { .mac = {'M', 'R', '1', '7', '5', '0'}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client mr1750 = { .router_type = { .desc = "MR1750", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = mr1750_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(mr1750_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry om2p_mac_accept[] = { { .mac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, { .mac = {'O', 'M', '2', 'P', 'V', '4'}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client om2p = { .router_type = { .desc = "OM2P", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = om2p_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(om2p_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry a40_mac_accept[] = { { .mac = {'A', '4', '0', 0x00, 0x00, 0x00}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client a40 = { .router_type = { .desc = "A40", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .image_desc = "A60", .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = a40_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(a40_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry a60_mac_accept[] = { { .mac = {'A', '6', '0', 0x00, 0x00, 0x00}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client a60 = { .router_type = { .desc = "A60", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = a60_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(a60_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry a42_mac_accept[] = { { .mac = {'A', '4', '2', 0x00, 0x00, 0x00}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client a42 = { .router_type = { .desc = "A42", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = a42_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(a42_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry a62_mac_accept[] = { { .mac = {'A', '6', '2', 0x00, 0x00, 0x00}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client a62 = { .router_type = { .desc = "A62", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = a62_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(a62_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry ap840_mac_accept[] = { { .mac = {0xf8, 0xd9, 0xb8, 0x00, 0x01, 0x01}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client ap840 = { .router_type = { .desc = "AP840", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = ap840_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(ap840_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry ap440_mac_accept[] = { { .mac = {0xF8, 0xD9, 0xB8, 0x00, 0x03, 0x01}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client ap440 = { .router_type = { .desc = "AP440", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = ap440_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(ap440_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry ap840e_mac_accept[] = { { .mac = {0xf8, 0xd9, 0xb8, 0x00, 0x02, 0x01}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client ap840e = { .router_type = { .desc = "AP840E", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = ap840e_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(ap840e_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry tw420_mac_accept[] = { { .mac = {0xF8, 0xD9, 0xB8, 0x00, 0x04, 0x01}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client tw420 = { .router_type = { .desc = "TW420", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = tw420_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(tw420_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry om5p_mac_accept[] = { { .mac = {'O', 'M', '5', 'P', 0x00, 0x00}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client om5p = { .router_type = { .desc = "OM5P", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = om5p_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(om5p_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry om5pan_mac_accept[] = { { .mac = {'O', 'M', '5', 'P', 'A', 'N'}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client om5pan = { .router_type = { .desc = "OM5P-AN", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .image_desc = "OM5P", .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = om5pan_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(om5pan_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry om5pac_mac_accept[] = { { .mac = {'O', 'M', '5', 'P', 'A', 'C'}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client om5pac = { .router_type = { .desc = "OM5P-AC", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .image_desc = "OM5PAC", .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = om5pac_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(om5pac_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry p60_mac_accept[] = { { .mac = {'P', '6', '0', 0x00, 0x00, 0x00}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client p60 = { .router_type = { .desc = "P60", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .image_desc = "P60", .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = p60_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(p60_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry d200_mac_accept[] = { { .mac = {'D', '2', '0', '0', 0x00, 0x00}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client d200 = { .router_type = { .desc = "D200", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .image_desc = "D200", .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = d200_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(d200_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry g200_mac_accept[] = { { .mac = {'G', '2', '0', '0', 0x00, 0x00}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client g200 = { .router_type = { .desc = "G200", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .image_desc = "G200", .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = g200_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(g200_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry pa300_mac_accept[] = { { .mac = {'P', 'A', '3', '0', '0', 0x00}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client pa300 = { .router_type = { .desc = "PA300", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = pa300_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(pa300_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry pa1200_mac_accept[] = { { .mac = {'R', 'K', '1', '2', '0', '0'}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, { .mac = {'P', 'A', '1', '2', '0', '0'}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client pa1200 = { .router_type = { .desc = "PA1200", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = pa1200_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(pa1200_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry pa2200_mac_accept[] = { { .mac = {'R', 'K', '2', '1', '0', '0'}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, { .mac = {'P', 'A', '2', '2', '0', '0'}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client pa2200 = { .router_type = { .desc = "PA2200", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = pa2200_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(pa2200_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry pax1800_mac_accept[] = { { .mac = {'P', 'A', 'X', '1', '8', '0'}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client pax1800 = { .router_type = { .desc = "PAX1800", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = pax1800_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(pax1800_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry pax1800v2_mac_accept[] = { { .mac = {'P', 'A', 'X', '1', '8', '2'}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client pax1800v2 = { .router_type = { .desc = "PAX1800v2", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = pax1800v2_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(pax1800v2_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry pax5400_mac_accept[] = { { .mac = {'P', 'A', 'X', '5', '4', '0'}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client pax5400 = { .router_type = { .desc = "PAX5400", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_ce, .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = pax5400_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(pax5400_mac_accept), .ip = OM2P_IP, }; static const struct mac_accept_entry zyxel_mac_accept[] = { { .mac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, .mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }; const struct router_tftp_client zyxel = { .router_type = { .desc = "Zyxel", .detect_pre = NULL, .detect_main = tftp_client_detect_main, .detect_post = tftp_client_detect_post, .image = &img_zyxel, .image_desc = "Zyxel", .priv_size = sizeof(struct om2p_priv), }, .mac_accept_entries = zyxel_mac_accept, .mac_accept_entries_num = ARRAY_SIZE(zyxel_mac_accept), .ip = ZYXEL_IP, }; ap51-flash-2025.0/router_tftp_client.h000066400000000000000000000034501476066605500175500ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Marek Lindner */ #ifndef __AP51_FLASH_ROUTER_TFTP_CLIENT_H__ #define __AP51_FLASH_ROUTER_TFTP_CLIENT_H__ #include "router_types.h" struct node; struct mac_accept_entry { uint8_t mac[6]; uint8_t mask[6]; }; struct router_tftp_client { struct router_type router_type; const struct mac_accept_entry *mac_accept_entries; size_t mac_accept_entries_num; unsigned int ip; }; extern const struct router_tftp_client a40; extern const struct router_tftp_client a42; extern const struct router_tftp_client a60; extern const struct router_tftp_client a62; extern const struct router_tftp_client ap440; extern const struct router_tftp_client ap840; extern const struct router_tftp_client ap840e; extern const struct router_tftp_client d200; extern const struct router_tftp_client g200; extern const struct router_tftp_client mr1750; extern const struct router_tftp_client mr500; extern const struct router_tftp_client mr600; extern const struct router_tftp_client mr900; extern const struct router_tftp_client om2p; extern const struct router_tftp_client om5p; extern const struct router_tftp_client om5pac; extern const struct router_tftp_client om5pan; extern const struct router_tftp_client p60; extern const struct router_tftp_client pa300; extern const struct router_tftp_client pa1200; extern const struct router_tftp_client pa2200; extern const struct router_tftp_client pax1800; extern const struct router_tftp_client pax1800v2; extern const struct router_tftp_client pax5400; extern const struct router_tftp_client tw420; extern const struct router_tftp_client zyxel; void tftp_client_flash_time_set(struct node *node); int tftp_client_flash_completed(struct node *node); #endif /* __AP51_FLASH_ROUTER_TFTP_CLIENT_H__ */ ap51-flash-2025.0/router_tftp_server.c000066400000000000000000000044451476066605500176000ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-FileCopyrightText: Marek Lindner */ #include "router_tftp_server.h" #include #include "compat.h" #include "flash.h" #include "proto.h" #include "router_images.h" #define UBNT_IP 3232235796UL /* 192.168.1.20 */ static const unsigned int my_ip = 3232235801UL; /* 192.168.1.25 */ struct tftp_server_priv { int arp_count; }; static void tftp_server_detect_pre(const struct router_type *router_type, const uint8_t *our_mac) { uint8_t bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; struct router_tftp_server *tftp_server; tftp_server = container_of(router_type, struct router_tftp_server, router_type); arp_req_send(our_mac, bcast_mac, htonl(my_ip), htonl(tftp_server->ip)); } static int tftp_server_detect_main(const struct router_type *router_type, void *priv, const char *packet_buff, int packet_buff_len) { struct tftp_server_priv *server_priv = priv; struct router_tftp_server *tftp_server; struct ether_arp *arphdr; int ret = 0; tftp_server = container_of(router_type, struct router_tftp_server, router_type); if (!len_check(packet_buff_len, sizeof(struct ether_arp), "ARP")) goto out; arphdr = (struct ether_arp *)packet_buff; if (arphdr->ea_hdr.ar_op != htons(ARPOP_REPLY)) goto out; if (load_ip_addr(arphdr->arp_spa) != htonl(tftp_server->ip)) goto out; if (server_priv->arp_count < tftp_server->wait_arp_count) { server_priv->arp_count++; goto out; } ret = 1; out: return ret; } static void tftp_server_detect_post(struct node *node, const char *packet_buff, int packet_buff_len) { struct ether_arp *arphdr; if (!len_check(packet_buff_len, sizeof(struct ether_arp), "ARP")) goto out; arphdr = (struct ether_arp *)packet_buff; node->flash_mode = FLASH_MODE_TFTP_SERVER; node->his_ip_addr = load_ip_addr(arphdr->arp_spa); node->our_ip_addr = load_ip_addr(arphdr->arp_tpa); out: return; } const struct router_tftp_server ubnt = { .router_type = { .desc = "ubiquiti", .detect_pre = tftp_server_detect_pre, .detect_main = tftp_server_detect_main, .detect_post = tftp_server_detect_post, .image = &img_ubnt, .image_desc = "ubiquiti", .priv_size = sizeof(struct tftp_server_priv), }, .ip = UBNT_IP, .wait_arp_count = 20, }; ap51-flash-2025.0/router_tftp_server.h000066400000000000000000000006661476066605500176060ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Marek Lindner */ #ifndef __AP51_FLASH_ROUTER_TFTP_SERVER_H__ #define __AP51_FLASH_ROUTER_TFTP_SERVER_H__ #include "router_types.h" struct router_tftp_server { struct router_type router_type; unsigned int ip; uint8_t wait_arp_count; }; extern const struct router_tftp_server ubnt; #endif /* __AP51_FLASH_ROUTER_TFTP_SERVER_H__ */ ap51-flash-2025.0/router_types.c000066400000000000000000000135231476066605500163760ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-FileCopyrightText: Marek Lindner */ #include "router_types.h" #include #include #include #include "compat.h" #include "flash.h" #include "list.h" #include "router_images.h" #include "router_redboot.h" #include "router_tftp_client.h" #include "router_tftp_server.h" #include "router_netconsole.h" #if defined(CLEAR_SCREEN) #if defined(LINUX) || defined(WIN32) #include #endif #endif int router_types_priv_size = 0; static DECLARE_LIST_HEAD(mac_allowlist); static const struct router_type *router_types[] = { &a40.router_type, &a42.router_type, &a60.router_type, &a62.router_type, &ap440.router_type, &ap840.router_type, &ap840e.router_type, &d200.router_type, &g200.router_type, &mr1750.router_type, &mr500.router_type, &mr600.router_type, &mr900.router_type, &om2p.router_type, &om5p.router_type, &om5pac.router_type, &om5pan.router_type, &p60.router_type, &redboot, &tw420.router_type, &ubnt.router_type, &zyxel.router_type, &ap121f, &pa300.router_type, &pa1200.router_type, &pa2200.router_type, &pax1800.router_type, &pax1800v2.router_type, &pax5400.router_type, NULL, }; static int read_mac(uint8_t mac[ETH_ALEN], const char *macstr) { int ret; ret = sscanf(macstr, "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); if (ret != 6) ret = sscanf(macstr, "%02hhX-%02hhX-%02hhX-%02hhX-%02hhX-%02hhX", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); return (ret == 6); } int mac_allowlist_add(const char *macstr) { uint8_t mac[ETH_ALEN]; struct mac_allowlist_entry *new_entry; if (!read_mac(mac, macstr)) { fprintf(stderr, "Error - could not add MAC address to allowlist: %s\n", macstr); return -EINVAL; } new_entry = malloc(sizeof(*new_entry)); if (!new_entry) { fprintf(stderr, "Error - could not allocate memory for new MAC allowlist entry\n"); return -ENOMEM; } memcpy(new_entry->mac, mac, ETH_ALEN); list_add(&new_entry->list, &mac_allowlist); return 0; } static bool mac_allowlist_find(const uint8_t *mac) { struct mac_allowlist_entry *current; list_for_each_entry(current, &mac_allowlist, list) { if (memcmp(mac, current->mac, ETH_ALEN) == 0) return true; } return false; } int router_types_init(void) { int ret = -1; const struct router_type **router_type; for (router_type = router_types; *router_type; ++router_type) { if (!(*router_type)->image) { fprintf(stderr, "Error - can't have router definition without image attribute set: %s\n", (*router_type)->desc); goto out; } router_types_priv_size += (*router_type)->priv_size; } ret = 0; out: return ret; } void router_types_detect_pre(const uint8_t *our_mac) { const struct router_type **router_type; for (router_type = router_types; *router_type; ++router_type) { if (!(*router_type)->detect_pre) continue; (*router_type)->detect_pre(*router_type, our_mac); } } int router_types_detect_main(struct node *node, const char *packet_buff, int packet_buff_len) { const struct router_type **router_type; struct router_info *router_info; void *priv = node + 1; int ret = 0; for (router_type = router_types; *router_type; ++router_type) { if (!(*router_type)->detect_main) goto next; ret = (*router_type)->detect_main(*router_type, priv, packet_buff, packet_buff_len); if (ret != 1) goto next; /* we detected a router that we have no image for */ if ((*router_type)->image->file_size < 1) { fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: is of type '%s' that we have no image for\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], (*router_type)->desc); node->status = NODE_STATUS_NO_FLASH; ret = 0; break; } /* we detected a router whose MAC is not allowlisted */ if (!list_empty(&mac_allowlist) && !mac_allowlist_find(node->his_mac_addr)) { fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: is of type '%s' but MAC does not match MAC filter\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], (*router_type)->desc); node->status = NODE_STATUS_NO_FLASH; ret = 0; break; } if ((*router_type)->image->type == IMAGE_TYPE_CE) { router_info = router_image_router_get((*router_type)->image, (*router_type)->image_desc ? (char *)(*router_type)->image_desc : (char *)(*router_type)->desc); if (!router_info) { fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: is of type '%s' that we have no image for (ce)\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], (*router_type)->desc); node->status = NODE_STATUS_NO_FLASH; ret = 0; break; } } our_mac_set(node); node->router_type = (struct router_type *)(*router_type); node->router_priv = priv; #if defined(CLEAR_SCREEN) #if defined(LINUX) if (num_nodes_flashed > 0) ret = system("clear"); #elif defined(WIN32) if (num_nodes_flashed > 0) ret = system("cls"); #else #error CLEAR_SCREEN is not supported on your OS #endif /* keep gcc happy by retrieving the return value of the system() call */ ret = 1; #endif fprintf(stderr, "[%02x:%02x:%02x:%02x:%02x:%02x]: device type '%s' detected\n", node->his_mac_addr[0], node->his_mac_addr[1], node->his_mac_addr[2], node->his_mac_addr[3], node->his_mac_addr[4], node->his_mac_addr[5], node->router_type->desc); if (!(*router_type)->detect_post) break; (*router_type)->detect_post(node, packet_buff, packet_buff_len); break; next: priv = (char *)priv + (*router_type)->priv_size; } return ret; } ap51-flash-2025.0/router_types.h000066400000000000000000000030171476066605500164000ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Marek Lindner */ #ifndef __AP51_FLASH_ROUTER_TYPES_H__ #define __AP51_FLASH_ROUTER_TYPES_H__ #include #include "ap51-flash.h" #include "list.h" #include "compat.h" struct node; /** * each router type has to declare a router_type struct * and add a pointer to the router_types array * * detect_pre: called by the scheduler in regular intervals * can be used to send ARP requests * detect_main: called when an ARP packet is received * return 1 if the router has been detected * detect_post: called to let the router_type configure * the node#s settings (e.g. IP) */ struct router_type { char desc[DESC_MAX_LENGTH]; void (*detect_pre)(const struct router_type *router_type, const uint8_t *our_mac); int (*detect_main)(const struct router_type *router_type, void *priv, const char *packet_buff, int packet_buff_len); void (*detect_post)(struct node *node, const char *packet_buff, int packet_buff_len); struct router_image *image; char *image_desc; int priv_size; }; struct mac_allowlist_entry { struct list_head list; uint8_t mac[ETH_ALEN]; }; int router_types_init(void); void router_types_detect_pre(const uint8_t *our_mac); int router_types_detect_main(struct node *node, const char *packet_buff, int packet_buff_len); int mac_allowlist_add(const char *macstr); extern int router_types_priv_size; #endif /* __AP51_FLASH_ROUTER_TYPES_H__ */ ap51-flash-2025.0/socket.c000066400000000000000000000330411476066605500151170ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-FileCopyrightText: Marek Lindner */ #include "socket.h" #include #include #include #include #include #include #include #include "compat.h" #ifdef USE_PCAP #include "list.h" #endif enum listdump_action { /** @LISTDUMP_OK: continue processing */ LISTDUMP_OK, /** @LISTDUMP_STOP: stop processing */ LISTDUMP_STOP, }; #if defined(LINUX) static int raw_sock = -1; static int socket_rtnl_recvmsg(int sock, struct nlmsghdr **nh, unsigned int *len) { ssize_t rlen; ssize_t ret; peek_retry: rlen = recv(sock, NULL, 0, MSG_PEEK | MSG_TRUNC); if (rlen < 0) { if (errno == EINTR) goto peek_retry; fprintf(stderr, "Error - unable to retrieve netlink response: %s\n", strerror(errno)); return rlen; } *nh = malloc(rlen); if (!*nh) return -ENOMEM; recv_retry: ret = recv(sock, *nh, rlen, 0); if (ret < 0) { if (errno == EINTR) goto recv_retry; fprintf(stderr, "Error - unable to receive netlink request: %s\n", strerror(errno)); goto free_resp; } *len = ret; return 0; free_resp: free(*nh); *nh = NULL; return -1; } static char *sock_rta_find_name(struct ifinfomsg *ifinfomsg, size_t attr_len) { struct rtattr *rta = IFLA_RTA(ifinfomsg); /* search the name */ for (; RTA_OK(rta, attr_len); rta = RTA_NEXT(rta, attr_len)) { char *rta_data = RTA_DATA(rta); size_t rta_payload = RTA_PAYLOAD(rta); if (rta_payload <= 0) continue; if (rta->rta_type != IFLA_IFNAME) continue; rta_data[rta_payload - 1] = '\0'; return rta_data; } return NULL; } static enum listdump_action socket_rtnl_parse(struct nlmsghdr *resp, unsigned int len, enum listdump_action (*dump)(const char *name, unsigned int index, const char *description, void *arg), void *arg) { struct ifinfomsg *ifinfomsg; enum listdump_action action; struct nlmsgerr *nlme; struct nlmsghdr *nh; size_t attr_len; char *name; if (resp->nlmsg_type == NLMSG_ERROR) { nlme = NLMSG_DATA(resp); fprintf(stderr, "Error - netlink complained: %i\n", nlme->error); return LISTDUMP_STOP; } for (nh = resp; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { if (nh->nlmsg_type == NLMSG_DONE) return LISTDUMP_STOP; if (nh->nlmsg_type != RTM_NEWLINK) continue; ifinfomsg = NLMSG_DATA(nh); attr_len = IFLA_PAYLOAD(nh); if (ifinfomsg->ifi_type != ARPHRD_ETHER) continue; name = sock_rta_find_name(ifinfomsg, attr_len); if (!name) continue; action = dump(name, ifinfomsg->ifi_index, NULL, arg); if (action == LISTDUMP_STOP) return action; } return LISTDUMP_OK; } static int socket_dump_ifaces(enum listdump_action (*dump)(const char *name, unsigned int index, const char *description, void *arg), void *arg) { struct { struct nlmsghdr nh; struct ifinfomsg ifinfomsg; } req; enum listdump_action action; struct sockaddr_nl nl; struct nlmsghdr *resp; int ret = -1, sock; unsigned int len; sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (sock < 0) { fprintf(stderr, "Error - can't create netlink socket: %s\n", strerror(errno)); return sock; } memset(&nl, 0, sizeof(nl)); nl.nl_family = AF_NETLINK; ret = bind(sock, (struct sockaddr *)&nl, sizeof(nl)); if (ret < 0) { fprintf(stderr, "Error - can't bind netlink socket: %s\n", strerror(errno)); goto close_sock; } memset(&req, 0, sizeof(req)); req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfomsg)); req.nh.nlmsg_type = RTM_GETLINK; req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; req.ifinfomsg.ifi_family = AF_UNSPEC; ret = send(sock, &req, sizeof(req), 0); if (ret < 0) { fprintf(stderr, "Error - unable to send netlink request: %s\n", strerror(errno)); goto close_sock; } while (1) { resp = NULL; ret = socket_rtnl_recvmsg(sock, &resp, &len); if (ret < 0) goto close_sock; action = socket_rtnl_parse(resp, len, dump, arg); free(resp); if (action == LISTDUMP_STOP) break; } close(sock); return 0; close_sock: close(sock); return ret; } #elif USE_PCAP pcap_t *pcap_fp = NULL; static int socket_dump_ifaces(enum listdump_action (*dump)(const char *name, unsigned int index, const char *description, void *arg), void *arg) { char errbuf[PCAP_ERRBUF_SIZE]; enum listdump_action action; unsigned int if_count = 0; pcap_if_t *alldevs = NULL; pcap_if_t *dev; int ret; ret = pcap_findalldevs(&alldevs, errbuf); if (ret < 0) { fprintf(stderr, "Error - unable to retrieve interface list: %s\n", errbuf); return ret; } for (dev = alldevs; dev; dev = dev->next) { if (dev->flags & PCAP_IF_LOOPBACK) continue; if_count++; action = dump(dev->name, if_count, dev->description, arg); if (action == LISTDUMP_STOP) break; } if (alldevs) pcap_freealldevs(alldevs); return 0; } #else #error socket_dump_ifaces() is not supported on your OS #endif struct socket_find_iface_by_index_arg { unsigned int index; char *name; }; static enum listdump_action compare_interface(const char *name, unsigned int index, const char *description __attribute__((unused)), void *arg) { struct socket_find_iface_by_index_arg *find_arg = arg; if (index != find_arg->index) return LISTDUMP_OK; find_arg->name = strdup(name); return LISTDUMP_STOP; } char *socket_find_iface_by_index(const char *iface_number) { struct socket_find_iface_by_index_arg find_arg = { .name = NULL, }; char *endptr; long if_num; if_num = strtol(iface_number, &endptr, 10); if (!endptr || iface_number == endptr || *endptr != '\0') return NULL; if (if_num < 1) return NULL; find_arg.index = if_num; socket_dump_ifaces(compare_interface, &find_arg); return find_arg.name; } static void print_description_sanitized(const char *description) { unsigned char last_char; unsigned char *ptr; unsigned char c; if (!description || strlen(description) == 0) { fprintf(stderr, "\t(No description available)\n"); return; } fprintf(stderr, "\t(Description: "); ptr = (unsigned char *)description; last_char = 0; while (' ' <= *ptr) { c = *ptr; ptr++; /* skip multiple spaces */ if (last_char == ' ' && last_char == c) continue; fprintf(stderr, "%c", c); last_char = c; } fprintf(stderr, ")\n"); } static enum listdump_action print_interface(const char *name, unsigned int index, const char *description, void *arg __attribute__((unused))) { fprintf(stderr, "\n%i: %s\n", index, name); print_description_sanitized(description); return LISTDUMP_OK; } void socket_print_all_ifaces(void) { socket_dump_ifaces(print_interface, NULL); } int socket_open(const char *iface) { #if defined(LINUX) struct sockaddr_ll addr; struct ifreq req; int ret, sock_opts; if (strlen(iface) > IFNAMSIZ - 1) { fprintf(stderr, "Error - interface name too long: %s\n", iface); goto out; } raw_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (raw_sock < 0) { fprintf(stderr, "Error - can't create raw socket: %s\n", strerror(errno)); goto out; } memset(&req, 0, sizeof (struct ifreq)); strncpy(req.ifr_name, iface, IFNAMSIZ); req.ifr_name[sizeof(req.ifr_name) - 1] = '\0'; ret = ioctl(raw_sock, SIOCGIFFLAGS, &req); if (ret < 0) { if (errno == ENODEV) fprintf(stderr, "Error - interface does not exist: %s\n", iface); else fprintf(stderr, "Error - can't get interface flags (SIOCGIFFLAGS): %s\n", strerror(errno)); goto close_sock; } if (!(req.ifr_flags & (IFF_UP | IFF_RUNNING))) { fprintf(stderr, "Error - interface is not up & running: %s\n", iface); goto close_sock; } req.ifr_flags |= IFF_PROMISC; ret = ioctl(raw_sock, SIOCSIFFLAGS, &req); if (ret < 0) { fprintf(stderr, "Error - can't set interface flags (SIOCSIFFLAGS): %s\n", strerror(errno)); goto close_sock; } ret = ioctl(raw_sock, SIOCGIFINDEX, &req); if (ret < 0) { fprintf(stderr, "Error - can't get interface index (SIOCGIFINDEX): %s\n", strerror(errno)); goto close_sock; } addr.sll_family = AF_PACKET; addr.sll_protocol = htons(ETH_P_ALL); addr.sll_ifindex = req.ifr_ifindex; ret = bind(raw_sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_ll)); if (ret < 0) { fprintf(stderr, "Error - can't bind raw socket: %s\n", strerror(errno)); goto close_sock; } sock_opts = fcntl(raw_sock, F_GETFL, 0); if (sock_opts == -1) { fprintf(stderr, "Error - can't read socket flags: %s\n", strerror(errno)); goto close_sock; } ret = fcntl(raw_sock, F_SETFL, sock_opts | O_NONBLOCK); if (ret < 0) { fprintf(stderr, "Error - can't set socket flags: %s\n", strerror(errno)); goto close_sock; } return 0; close_sock: close(raw_sock); raw_sock = -1; out: return -1; #elif USE_PCAP char error[PCAP_ERRBUF_SIZE]; #if WIN32 pcap_fp = pcap_open_live(iface, 1500, 1, 250, error); if (!pcap_fp) { fprintf(stderr, "Error opening adapter: %s\n", error); return -1; } if (pcap_setmintocopy(pcap_fp, 1) < 0) { fprintf(stderr, "Error setting mintocopy: %s\n", error); return 1; } #else // For Mac OS X, and maybe others in the future, // we take the long way around and set individual options on pcap // in order to be able to set immediate mode before activating the pcap // handle. int ret; pcap_fp = pcap_create(iface, error); if (!pcap_fp) { fprintf(stderr, "Error opening adapter: %s\n", error); return -1; } ret = pcap_set_snaplen(pcap_fp, 1500); if (ret != 0) { fprintf(stderr, "Error setting pcap snaplen: %s\n", error); return -1; } ret = pcap_set_promisc(pcap_fp, 1); if (ret != 0) { fprintf(stderr, "Error setting pcap promiscuous mode: %s\n", error); return -1; } ret = pcap_set_timeout(pcap_fp, 250); if (ret != 0) { fprintf(stderr, "Error setting pcap timeout: %s\n", error); return -1; } ret = pcap_set_immediate_mode(pcap_fp, 1); if (ret != 0) { fprintf(stderr, "Error setting pcap immediate mode: %s\n", error); return -1; } ret = pcap_activate(pcap_fp); if (ret != 0) { fprintf(stderr, "Error activating pcap handle\n"); return -1; } #endif return 0; #else #error socket_open() is not supported on your OS return -1; #endif } #if defined(USE_PCAP) int socket_read(char *packet_buff, int packet_buff_len, int (*sleep_sec)__attribute__((unused)), int (*sleep_usec)__attribute__((unused))) #else int socket_read(char *packet_buff, int packet_buff_len, int *sleep_sec, int *sleep_usec) #endif { #if defined(LINUX) struct timeval tv; fd_set watched_fds; ssize_t read_len; int ret = -1; if (raw_sock < 0) { fprintf(stderr, "Error reading from network: raw socket not initialized yet\n"); goto out; } FD_ZERO(&watched_fds); FD_SET(raw_sock, &watched_fds); tv.tv_sec = *sleep_sec; tv.tv_usec = *sleep_usec; ret = select(raw_sock + 1, &watched_fds, NULL, NULL, &tv); *sleep_sec = tv.tv_sec; *sleep_usec = tv.tv_usec; if (ret < 0) { if (errno != EINTR) fprintf(stderr, "Error waiting for data from network: %s", strerror(errno)); } if (ret <= 0) goto out; read_len = read(raw_sock, packet_buff, packet_buff_len - 1); if (read_len < 0) { if ((errno != EWOULDBLOCK) && (errno != EINTR)) fprintf(stderr, "Error reading data from network: %s", strerror(errno)); } ret = (int)read_len; packet_buff[read_len] = '\0'; out: return ret; #elif USE_PCAP struct pcap_pkthdr hdr; const unsigned char *tmp_packet; int ret = -1; if (!pcap_fp) { fprintf(stderr, "Error reading from network: pcap socket not initialized yet\n"); goto out; } ret = 0; tmp_packet = pcap_next(pcap_fp, &hdr); if ((tmp_packet) && (hdr.len > 0)) { ret = hdr.len; if (ret > packet_buff_len) ret = packet_buff_len; memcpy(packet_buff, tmp_packet, ret); packet_buff[ret] = '\0'; } out: return ret; #else #error socket_read() is not supported on your OS return 0; #endif } int socket_write(const char *buff, int len) { #if defined(LINUX) int ret = -1; if (raw_sock < 0) { fprintf(stderr, "Error writing to network: raw socket not initialized yet\n"); goto out; } ret = write(raw_sock, buff, len); if (ret < 0) fprintf(stderr, "Error - can't write to raw socket: %s\n", strerror(errno)); out: return ret; #elif USE_PCAP int ret = -1; if (!pcap_fp) { fprintf(stderr, "Error writing to network: pcap socket not initialized yet\n"); goto out; } ret = pcap_sendpacket(pcap_fp, (unsigned char *)buff, len); if (ret < 0) fprintf(stderr, "Error - can't write to pcap socket\n"); out: return ret; #else #error socket_write() is not supported on your OS return 0; #endif } void socket_close(const char *iface) { #if defined(LINUX) struct ifreq req; int ret; if (raw_sock < 0) goto out; memset(&req, 0, sizeof (struct ifreq)); strncpy(req.ifr_name, iface, IFNAMSIZ); req.ifr_name[sizeof(req.ifr_name) - 1] = '\0'; ret = ioctl(raw_sock, SIOCGIFFLAGS, &req); if (ret < 0) { fprintf(stderr, "Error - can't get interface flags (SIOCGIFFLAGS): %s (%i)\n", strerror(errno), raw_sock); goto close_sock; } req.ifr_flags &= ~IFF_PROMISC; ret = ioctl(raw_sock, SIOCSIFFLAGS, &req); if (ret < 0) { fprintf(stderr, "Error - can't set interface flags (SIOCSIFFLAGS): %s\n", strerror(errno)); goto close_sock; } close_sock: close(raw_sock); raw_sock = -1; out: return; #elif USE_PCAP if (!pcap_fp) { fprintf(stderr, "Error closing adapter '%s': pcap socket not initialized yet\n", iface); goto out; } pcap_close(pcap_fp); out: return; #else #error socket_close() is not supported on your OS #endif } ap51-flash-2025.0/socket.h000066400000000000000000000010211476066605500151150ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-FileCopyrightText: Marek Lindner */ #ifndef __AP51_FLASH_SOCKET_H__ #define __AP51_FLASH_SOCKET_H__ void socket_print_all_ifaces(void); char *socket_find_iface_by_index(const char *iface_number); int socket_open(const char *iface); int socket_read(char *packet_buff, int packet_buff_len, int *sleep_sec, int *sleep_usec); int socket_write(const char *buff, int len); void socket_close(const char *iface); #endif /* __AP51_FLASH_SOCKET_H__ */