Compare commits

...
48 Commits
Author SHA1 Message Date
cpq f12e44b0b1 Add frdm-mcxw71 support 2026-10-20 10:40:07 +01:00
cpq a6b6a0d779 Add frdm-mcxw71 support 2026-10-19 14:10:48 +01:00
cpq 38c326644f Add params for MX2 2026-10-19 14:10:23 +01:00
cpq e6e7a690a7 New Cube pack 2026-10-19 14:06:49 +01:00
cpq c76f092ba1 Change build env reference 2026-10-17 16:04:45 +01:00
Sergio R. Caprile 60840d0d89 Merge pull request #3739 from cesanta/post3732
make TLS error reporting consistent
2026-10-16 17:07:13 -03:00
Sergio R. Caprile 298ee549da make error reporting consistent 2026-10-16 15:59:20 -03:00
Sergio R. Caprile 5e4ae64e1b Merge pull request #3738 from cesanta/iserror
Misc fixes and regression unbreaks
2026-10-16 14:56:40 -03:00
Sergio R. Caprile c5778aa608 Misc fixes and regression unbreaks 2026-10-16 13:51:58 -03:00
Sergio R. Caprile 6d32a7c238 Merge pull request #3736 from cesanta/esp8266
Remove esp8266 support
2026-10-15 17:49:50 -03:00
cpq 497a3f5e73 Remove esp8266 support 2026-10-15 21:14:31 +01:00
Sergey Lyubka 10e67fdf2b Merge pull request #3734 from cesanta/esp32
Add TLS and health backtrace to esp32
2026-10-15 21:05:47 +01:00
cpq 2f2a5f2f02 Add TLS and health backtrace to esp32 2026-10-15 20:57:10 +01:00
Sergio R. Caprile aa21ed443a Merge pull request #3735 from cesanta/bsd
fix new bsd test
2026-10-15 14:43:33 -03:00
Sergio R. Caprile 10adde595c fix new bsd test 2026-10-15 14:39:37 -03:00
Sergey Lyubka c957f29f23 Merge pull request #3732 from cesanta/tls_p256
Drop x25519, use p256 only
2026-10-15 15:23:56 +01:00
cpq 37f3ed5ba4 Drop x25519, use p256 only 2026-10-15 13:36:24 +01:00
cpq b68da1548b Restore logging, make WiFi creds settable from make 2026-10-15 12:03:04 +01:00
novlean a9a94a6904 Merge pull request #3733 from cesanta/novlean-patch-1
Add new articles to README links section
2026-10-14 15:36:45 +02:00
novlean 3a4660cd40 Add new articles to README links section 2026-10-14 15:36:25 +02:00
Sergey Lyubka fc478a70f7 Merge pull request #3700 from cesanta/bsd
Reimplement BSD layer
2026-10-14 11:30:26 +01:00
Sergio R. Caprile a6b5565e9d Merge pull request #3727 from cesanta/mgerror
correctly close TCP on mg_error()
2026-10-13 12:04:15 -03:00
Sergio R. Caprile e7113408a9 correctly close TCP on mg_error() 2026-10-13 12:00:20 -03:00
Sergio R. Caprile fdf12844e3 Reimplement BSD layer 2026-10-12 18:09:41 -03:00
Sergio R. Caprile bc5afa4519 Merge pull request #3731 from cesanta/iserror
close connection besides logging, when gateway is down
2026-10-12 17:52:09 -03:00
Sergio R. Caprile a4607933a4 close connection besides logging, when gateway is down 2026-10-12 17:47:25 -03:00
cpq fd7db76324 Fix clean target 2026-10-11 09:17:16 +01:00
Sergey Lyubka b1e6cb320f Merge pull request #3729 from cesanta/unify
Unify bare metal projects
2026-10-11 09:09:42 +01:00
cpq 2b9d805d84 Unify bare metal projects 2026-10-11 08:42:00 +01:00
Sergey Lyubka a9a843d6e2 Merge pull request #3728 from cesanta/min
Minimise build size
2026-10-11 03:17:29 +01:00
cpq aae179177c Minimise build size 2026-10-10 12:25:44 +01:00
robertc2000 300011a882 Merge pull request #3726 from cesanta/h5
Add H5 and H7 to health monitor
2026-10-09 09:40:31 +03:00
cpq eda9a5726c Add H5 and H7 to health monitor 2026-10-08 15:15:11 +01:00
Sergey Lyubka 16a5a7d9fb Merge pull request #3712 from jnnrgd/feat/mqtt5-connack-props
mqtt: parse properties in MQTT 5.0 CONNACK packets
2026-10-08 04:37:45 +01:00
Sergey Lyubka 69c3bb8d12 Merge pull request #3725 from cesanta/protoclean
move embedded tutorials out of protocol tree
2026-10-08 04:33:52 +01:00
Sergio R. Caprile 8fc5651044 move embedded tutorials out of protocol tree 2026-10-07 16:10:28 -03:00
Sergio R. Caprile 338756d0b0 Merge pull request #3724 from cesanta/tls
Fix RSA signing
2026-10-07 11:37:25 -03:00
cpq c30afffcee Fix RSA signing 2026-10-07 11:36:37 -03:00
cpq 76c0953210 Update readme 2026-10-07 10:13:23 +01:00
Sergio R. Caprile 381e31198b Merge pull request #3722 from cesanta/fixmdashenable
fix mdash enablement trashing tests
2026-10-06 12:40:58 -03:00
Sergio R. Caprile 28ba381bd9 fix mdash enablement trashing tests 2026-10-06 12:24:19 -03:00
cpq 567963b034 Nit in mqtt example 2026-10-06 15:41:31 +01:00
Sergio R. Caprile 7d3fe4f9cb Merge pull request #3721 from cesanta/tls
Built-in TLS: accept a chain that contains the configured CA
2026-10-06 08:58:36 -03:00
cpq c8bf39dcd8 Built-in TLS: accept a chain that contains the configured CA 2026-10-06 12:41:25 +01:00
cpq 393c811f31 Fixed cert emptiness check 2026-10-06 12:33:02 +01:00
robert be40827e17 Add links for STM32 cubemx projects 2026-10-06 12:32:46 +03:00
cpq ad45248327 Fix URL 2026-10-05 18:20:28 +01:00
jnnrgd 1f2b14bb66 mqtt: parse properties in MQTT 5.0 CONNACK packets
Under MQTT 5.0, decode the property length and set props_start and
props_size for MQTT_CMD_CONNACK. Add unit test coverage.
2026-09-30 10:51:41 +02:00
517 changed files with 25909 additions and 37484 deletions

No files matched your search

+28 -26
View File
@@ -165,7 +165,7 @@ jobs:
- run: | - run: |
sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
if [[ "${{ matrix.target }}" == *port* ]]; then source ./test/setup_ga_network.sh ; export ENV="-e Tmp=. -e WINEDEBUG=-all -e HOST_IP -e HOST_IPV6 -e HOST_IFC" ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf; cat test/mosquitto.conf; ./test/setup_mqtt_server.sh ; make -C test port_tap_bridge ; test/port_tap_bridge & sleep 2 ; fi if [[ "${{ matrix.target }}" == *port* ]]; then source ./test/setup_ga_network.sh ; export ENV="-e Tmp=. -e WINEDEBUG=-all -e HOST_IP -e HOST_IPV6 -e HOST_IFC" ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf; cat test/mosquitto.conf; ./test/setup_mqtt_server.sh ; make -C test port_tap_bridge ; test/port_tap_bridge & sleep 2 ; fi
make -C test ${{ matrix.target }} IPV6=${{ matrix.ipv6 }} SSL=${{ matrix.ssl }} MULTIREC=NO make -C test ${{ matrix.target }} IPV6=${{ matrix.ipv6 }} SSL=${{ matrix.ssl }}
miprtxbe: miprtxbe:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -180,7 +180,7 @@ jobs:
- run: | - run: |
sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
source ./test/setup_ga_network.sh ; export ENV="-e Tmp=. -e WINEDEBUG=-all -e HOST_IP -e HOST_IPV6 -e HOST_IFC -e RTX" ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf ; ./test/setup_mqtt_server.sh ; make -C test port_tap_bridge ; test/port_tap_bridge & sleep 2 source ./test/setup_ga_network.sh ; export ENV="-e Tmp=. -e WINEDEBUG=-all -e HOST_IP -e HOST_IPV6 -e HOST_IFC -e RTX" ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf ; ./test/setup_mqtt_server.sh ; make -C test port_tap_bridge ; test/port_tap_bridge & sleep 2
make -C test mip_port_s390 MULTIREC=NO make -C test mip_port_s390
l2: l2:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -303,6 +303,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
target: [bsd, bsd_nortos]
cc: [gcc, clang, g++, clang++] cc: [gcc, clang, g++, clang++]
name: bsd CC=${{ matrix.cc }} name: bsd CC=${{ matrix.cc }}
steps: steps:
@@ -316,7 +317,7 @@ jobs:
make -C test/bsd shm_tap_bridge make -C test/bsd shm_tap_bridge
test/bsd/shm_tap_bridge & test/bsd/shm_tap_bridge &
sleep 2 sleep 2
make -C test/bsd CC=${{ matrix.cc }} > log make -C test/bsd ${{ matrix.target }} CC=${{ matrix.cc }} > log
- if: success() || failure() - if: success() || failure()
run: | run: |
cat log cat log
@@ -353,7 +354,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- run: sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && ./test/setup_mqtt_server.sh && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make -C test s390 SSL=${{ matrix.ssl }} MULTIREC=NO - run: sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && ./test/setup_mqtt_server.sh && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make -C test s390 SSL=${{ matrix.ssl }}
armhf: armhf:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
@@ -366,7 +367,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- run: sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && ./test/setup_mqtt_server.sh && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make -C test armhf SSL=${{ matrix.ssl }} MULTIREC=NO - run: sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && ./test/setup_mqtt_server.sh && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make -C test armhf SSL=${{ matrix.ssl }}
unamalgamated-mg_prefix: unamalgamated-mg_prefix:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
@@ -589,10 +590,12 @@ jobs:
stm32/nucleo-f746zg/minimal stm32/nucleo-f746zg/minimal
stm32/nucleo-f746zg/minimal-pppoe stm32/nucleo-f746zg/minimal-pppoe
stm32/nucleo-f756zg/minimal stm32/nucleo-f756zg/minimal
stm32/nucleo-h563zi/minimal
stm32/nucleo-h723zg/minimal stm32/nucleo-h723zg/minimal
stm32/nucleo-h723zg/minimal-bsd-baremetal
stm32/nucleo-h723zg/minimal-bsd-freertos
- group: stm32-2 - group: stm32-2
examples: | examples: |
stm32/nucleo-h563zi/minimal
stm32/nucleo-h743zi/minimal stm32/nucleo-h743zi/minimal
stm32/nucleo-n657x0-q/minimal stm32/nucleo-n657x0-q/minimal
stm32/nucleo-u5a5zj-q/minimal stm32/nucleo-u5a5zj-q/minimal
@@ -633,8 +636,7 @@ jobs:
matrix: matrix:
example: example:
- path: micropython/esp32 - path: micropython/esp32
- path: http/uart-bridge/esp32 - path: esp32/uart-bridge
- path: http/http-client/esp8266/http-client-server
name: ${{ matrix.example.path }} name: ${{ matrix.example.path }}
env: env:
GO: 0 GO: 0
@@ -652,7 +654,7 @@ jobs:
include: include:
- group: other - group: other
examples: | examples: |
mqtt/mqtt-client/microchip/same54-xpro microchip/same54-xpro/mqtt-client
rp/pico-rmii rp/pico-rmii
stm32/nucleo-f746zg/minimal-rm2 stm32/nucleo-f746zg/minimal-rm2
stm32/nucleo-f746zg/minimal-rndis stm32/nucleo-f746zg/minimal-rndis
@@ -691,13 +693,13 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
example: example:
- path: esp32 - path: esp32/device-dashboard
name: ${{ matrix.example.path }} name: ${{ matrix.example.path }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- run: echo # nothing specific to install or do - run: echo # nothing specific to install or do
- run: make -C tutorials/http/device-dashboard/${{ matrix.example.path }} build - run: make -C tutorials/${{ matrix.example.path }} build
device_dashboard_examples_arm: device_dashboard_examples_arm:
@@ -709,12 +711,12 @@ jobs:
- group: none - group: none
cflags: "" cflags: ""
examples: | examples: |
http/device-dashboard/microchip/same54-xpro microchip/same54-xpro/device-dashboard
renesas/ek-ra6m4-make-baremetal-builtin renesas/ek-ra6m4-make-baremetal-builtin
- group: builtin - group: builtin
cflags: -DMG_TLS=MG_TLS_BUILTIN cflags: -DMG_TLS=MG_TLS_BUILTIN
examples: | examples: |
http/device-dashboard/microchip/same54-xpro microchip/same54-xpro/device-dashboard
renesas/ek-ra6m4-make-baremetal-builtin renesas/ek-ra6m4-make-baremetal-builtin
name: device dashboard ARM ${{ matrix.group }} name: device dashboard ARM ${{ matrix.group }}
steps: steps:
@@ -740,19 +742,19 @@ jobs:
done done
exit $failed exit $failed
# mqtt_dashboard_examples: mqtt_dashboard_examples_arm:
# runs-on: ubuntu-latest runs-on: ubuntu-latest
# strategy: strategy:
# fail-fast: false fail-fast: false
# matrix: matrix:
# example: example:
# - path: - path: rp/pico-rndis-device
# name: ${{ matrix.example.path }} name: ${{ matrix.example.path }}
# steps: steps:
# - uses: actions/checkout@v5 - uses: actions/checkout@v5
# with: { fetch-depth: 2 } with: { fetch-depth: 2 }
# - run: echo # nothing specific to install or do - run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
# - run: make -C tutorials/${{ matrix.example.path }} build - run: make -C tutorials/${{ matrix.example.path }} build
# dual_examples: # dual_examples:
+2 -2
View File
@@ -98,8 +98,8 @@ jobs:
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- name: baremetal - name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C tutorials/http/device-dashboard/microchip/same54-xpro test VCON_API_KEY=${{secrets.VCON_API_KEY}} - run: make -C tutorials/microchip/same54-xpro/device-dashboard test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C tutorials/mqtt/mqtt-client/microchip/same54-xpro test VCON_API_KEY=${{secrets.VCON_API_KEY}} - run: make -C tutorials/microchip/same54-xpro/mqtt-client test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_pico_w5500: test_pico_w5500:
runs-on: ubuntu-latest runs-on: ubuntu-latest
+28 -7
View File
@@ -158,7 +158,8 @@ jobs:
- path: stm32/nucleo-f746zg/minimal - path: stm32/nucleo-f746zg/minimal
- path: stm32/nucleo-h563zi/minimal - path: stm32/nucleo-h563zi/minimal
- path: stm32/nucleo-h723zg/minimal - path: stm32/nucleo-h723zg/minimal
- path: stm32/nucleo-h723zg/minimal-bsd - path: stm32/nucleo-h723zg/minimal-bsd-baremetal
- path: stm32/nucleo-h723zg/minimal-bsd-freertos
- path: stm32/nucleo-h743zi/minimal - path: stm32/nucleo-h743zi/minimal
- path: stm32/stm32h573i-dk/minimal - path: stm32/stm32h573i-dk/minimal
- path: ti/ek-tm4c1294xl-make-baremetal-builtin - path: ti/ek-tm4c1294xl-make-baremetal-builtin
@@ -184,8 +185,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
example: example:
- path: http/uart-bridge/esp32 - path: esp32/uart-bridge
- path: http/http-client/esp8266/http-client-server
name: ${{ matrix.example.path }} name: ${{ matrix.example.path }}
env: env:
GO: 0 GO: 0
@@ -227,7 +227,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
example: example:
- path: esp32 - path: esp32/device-dashboard
name: ${{ matrix.example.path }} name: ${{ matrix.example.path }}
env: env:
GO: 0 GO: 0
@@ -235,12 +235,12 @@ jobs:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- run: | - run: |
if ./test/match_changed_files.sh '^src|tutorials/http/device-dashboard'; then if ./test/match_changed_files.sh '^src|^tutorials/http/device-dashboard|^tutorials/${{ matrix.example.path }}'; then
echo GO=1 >> $GITHUB_ENV echo GO=1 >> $GITHUB_ENV
# nothing specific to install or do # nothing specific to install or do
fi fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C tutorials/http/device-dashboard/${{ matrix.example.path }} build run: make -C tutorials/${{ matrix.example.path }} build
device_dashboard_examples_arm: device_dashboard_examples_arm:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -248,7 +248,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
example: example:
- path: http/device-dashboard/microchip/same54-xpro - path: microchip/same54-xpro/device-dashboard
- path: renesas/ek-ra6m4-make-baremetal-builtin - path: renesas/ek-ra6m4-make-baremetal-builtin
- path: rp/pico-rndis - path: rp/pico-rndis
- path: rp/pico-w - path: rp/pico-w
@@ -267,3 +267,24 @@ jobs:
fi fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C tutorials/${{ matrix.example.path }} build run: make -C tutorials/${{ matrix.example.path }} build
mqtt_dashboard_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: rp/pico-rndis-device
name: ${{ matrix.example.path }}
env:
GO: 0
steps:
- uses: actions/checkout@v5
with: { fetch-depth: 2 }
- run: |
if ./test/match_changed_files.sh '^src|^tutorials/mqtt/mqtt-dashboard/device|^tutorials/${{ matrix.example.path }}'; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: make -C tutorials/${{ matrix.example.path }} build
-1
View File
@@ -179,7 +179,6 @@ jobs:
example: example:
- path: esp32/uart-bridge - path: esp32/uart-bridge
- path: esp32/micropython - path: esp32/micropython
- path: esp8266/http-client-server
name: ${{ matrix.example.path }} name: ${{ matrix.example.path }}
env: env:
GO: 0 GO: 0
+7
View File
@@ -222,6 +222,13 @@ Technical guides and deep dives into embedded web servers, WebUI integration and
- [lwIP vs Mongoose - TCP/IP Stack Integration Benchmark](https://mongoose.ws/articles/lwip-vs-mongoose-tcpip-stack-integration/) - [lwIP vs Mongoose - TCP/IP Stack Integration Benchmark](https://mongoose.ws/articles/lwip-vs-mongoose-tcpip-stack-integration/)
- [Embedded TCP/IP Stacks Comparison](https://mongoose.ws/embedded-tcpip-stack-comparison/) - [Embedded TCP/IP Stacks Comparison](https://mongoose.ws/embedded-tcpip-stack-comparison/)
- [STM32H5 File System on Internal Flash: LittleFS + Mongoose](https://mongoose.ws/articles/stm32h5-file-system-on-internal-flash/) - [STM32H5 File System on Internal Flash: LittleFS + Mongoose](https://mongoose.ws/articles/stm32h5-file-system-on-internal-flash/)
- [Firmware OTA and remote control on NXP FRDM-RW612](https://mongoose.ws/articles/mdash/)
- [25000 WebSocket messages per second on STM32 Nucleo-H723ZG](https://mongoose.ws/articles/stm32-websocket-benchmark/)
- [How to connect STM32 to AWS IoT Core](https://mongoose.ws/articles/stm32-aws-iot/)
- [How to connect STM32 to Azure IoT Hub](https://mongoose.ws/articles/stm32-azure-iot-hub/)
- [How to connect STM32 to EMQX](https://mongoose.ws/articles/stm32-emqx/)
- [STM32 Device Management deep dive: Secure OTA, Remote Control, health monitor](https://mongoose.ws/articles/stm32-mdash-deep-dive/")
- [STM32 TCP/IP footprint: how much flash and RAM does Mongoose take?](https://mongoose.ws/articles/tcp-ip-footprint/)
## Contributions ## Contributions
+9208 -5174
View File
File diff suppressed because it is too large. Load diff
+502 -833
View File
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,32 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"version": "0.0.1",
"componentid": "::Mongoose:Networking Core",
"type": "object",
"grammarVersion": "3.8.0",
"license": "Copyright (c) Cesanta Software Limited",
"properties": {
"parameters": {
"type": "object",
"properties": {
"info": {
"type": "object",
"title": "General information",
"properties": {
"description": {
"title": "Middleware description",
"type": "string",
"actions": [
{
"type": "disable",
"condition": "true"
}
],
"default": "Mongoose networking library: TCP/IP, TLS, HTTP, WebSocket, MQTT, OTA. Edit mongoose_config.h to configure it."
}
}
}
}
}
}
}
@@ -1,13 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<package schemaVersion="1.7.40" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="PACK.xsd"> <package schemaVersion="1.7.56" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:stm32="https://developer.st.com/schemas/stm32cube/1.0.0" xsi:noNamespaceSchemaLocation="PACK.xsd">
<vendor>Cesanta</vendor> <vendor>Cesanta</vendor>
<name>I-CUBE-Mongoose</name> <name>I-CUBE-Mongoose</name>
<description>Mongoose - a lightweight networking library providing TCP/IP stack, TLS, HTTP (Embedded Web Server), WebSocket, MQTT, and Firmware OTA updates. Build production-ready web device dashboards and MQTT cloud connectivity without stitching multiple components together. <description>Mongoose - a full-stack networking library for microcontrollers: Ethernet and Wi-Fi drivers, TCP/IP stack, TLS 1.3, HTTP, WebSocket, MQTT, Modbus, OTA updates and device management, in a two-file C/C++ library, on any RTOS or bare metal.</description>
</description>
<url>https://mongoose.ws/downloads/stm32pack/</url> <url>https://mongoose.ws/downloads/stm32pack/</url>
<license>License.txt</license> <license>License.txt</license>
<supportContact>https://mongoose.ws/contact/</supportContact> <supportContact>https://mongoose.ws/contact/</supportContact>
<environments>
<environment name="STM32Cube">
<stm32:pack type="MW" friendlyName="Mongoose" shortDesc="Mongoose full-stack networking library">
<relocation-info parent="middleware"/>
<no-compatibility-constraint/>
</stm32:pack>
</environment>
</environments>
<releases> <releases>
<release date="2026-10-19" version="7.23.2">See https://github.com/cesanta/mongoose/releases/tag/7.23</release>
<release date="2026-09-28" version="7.23.1">See https://github.com/cesanta/mongoose/releases/tag/7.23</release> <release date="2026-09-28" version="7.23.1">See https://github.com/cesanta/mongoose/releases/tag/7.23</release>
<release date="2026-05-11" version="7.21.7">See https://github.com/cesanta/mongoose/releases/tag/7.21</release> <release date="2026-05-11" version="7.21.7">See https://github.com/cesanta/mongoose/releases/tag/7.21</release>
<release date="2026-05-05" version="7.21.5">See https://github.com/cesanta/mongoose/releases/tag/7.21</release> <release date="2026-05-05" version="7.21.5">See https://github.com/cesanta/mongoose/releases/tag/7.21</release>
@@ -20,7 +28,7 @@
<keyword>IMPLEMENTED_WITH_STM32CUBEMX</keyword> <keyword>IMPLEMENTED_WITH_STM32CUBEMX</keyword>
</keywords> </keywords>
<components> <components>
<component Cclass="Mongoose" Cgroup="Networking Core" Cversion="7.23.1" maxInstances="1"> <component Cclass="Mongoose" Cgroup="Networking Core" Cversion="7.23.2" maxInstances="1">
<description>Networking Core</description> <description>Networking Core</description>
<files> <files>
<file category="doc" name="README.md"/> <file category="doc" name="README.md"/>
@@ -28,7 +36,15 @@
<file category="sourceC" name="mongoose.c" /> <file category="sourceC" name="mongoose.c" />
<file category="header" name="mongoose_config.h"/> <file category="header" name="mongoose_config.h"/>
<file category="utility" name="README.md" public="false"/> <file category="utility" name="README.md" public="false"/>
<file category="genAsset" name=".config/mongoose_parameters.json"/>
</files> </files>
<environments>
<environment name="STM32Cube">
<stm32:component>
<middleware-entry-point id="Mongoose" name="Mongoose" category="TCP/IP"/>
</stm32:component>
</environment>
</environments>
</component> </component>
</components> </components>
</package> </package>
+24 -4
View File
@@ -1,10 +1,16 @@
VERSION = 7.23.1 VERSION = 7.23.2
REPO_ROOT = ../.. REPO_ROOT = ../..
MX ?= /Applications/STMicroelectronics/STM32CubeMX.app/Contents/MacOs/STM32CubeMX MX ?= /Applications/STMicroelectronics/STM32CubeMX.app/Contents/MacOs/STM32CubeMX
FILES = Cesanta.I-CUBE-Mongoose.pdsc Release_Notes.html mongoose_config.h README.md FILES = Cesanta.I-CUBE-Mongoose.pdsc Release_Notes.html mongoose_config.h README.md .config/mongoose_parameters.json
GENERATED = License.txt mongoose.h mongoose.c GENERATED = License.txt mongoose.h mongoose.c
MX2 ?= $(HOME)/STMicroelectronics/STM32CubeMX2_1.1.2/.bin/cube
PACKSDIR ?= $(HOME)/Library/Application Support/stm32cube/packs
MX2CPN ?= STM32C5A3ZGT6
MX2PROJ = $(CURDIR)/mx2test/test.ioc2
NAME = Cesanta.I-CUBE-Mongoose.$(VERSION)
PACK = $(NAME).pack
PACK = Cesanta.I-CUBE-Mongoose.$(VERSION).pack
all: $(PACK) all: $(PACK)
Cesanta.I-CUBE-Mongoose.$(VERSION).pack: $(FILES) $(GENERATED) Cesanta.I-CUBE-Mongoose.$(VERSION).pack: $(FILES) $(GENERATED)
@@ -44,5 +50,19 @@ mongoose.h: Mongoose
mongoose.c: Mongoose mongoose.c: Mongoose
cp $(REPO_ROOT)/mongoose.c $@ cp $(REPO_ROOT)/mongoose.c $@
test_mx2: $(PACK)
-$(MX2) pack remove --cmsis-pack-root "$(PACKSDIR)" $(NAME)
rm -f "$(PACKSDIR)/.Download/$(PACK)"
$(MX2) pack install --cmsis-pack-root "$(PACKSDIR)" "$(CURDIR)/$(PACK)"
export PATH="$(dir $(MX2)):$$PATH"; \
PORT=$$(cube lifecycle-mx start-only | sed -n 's/^BACKEND_PORT *//p'); \
cube mx project close --port $$PORT -p "$(MX2PROJ)" --discard > /dev/null 2>&1; \
rm -rf mx2test && mkdir mx2test && \
cube mx project create-from-mcu --port $$PORT --cpn $(MX2CPN) -l "$(CURDIR)/mx2test" -n test --empty-project && \
cube mx middleware enable-middleware --port $$PORT -p "$(MX2PROJ)" -n Mongoose | tee mx2test/enable.json && \
grep -q '"status": "success"' mx2test/enable.json && \
cube mx project save --port $$PORT -p "$(MX2PROJ)" && \
cube mx project close --port $$PORT -p "$(MX2PROJ)" --discard
clean: clean:
rm -rf *.pack STM32CubeMX/*.$(VERSION)_*.xml $(GENERATED) rm -rf *.pack STM32CubeMX/*.$(VERSION)_*.xml $(GENERATED) mx2test
+4 -3
View File
@@ -13,9 +13,10 @@ Primary files:
- `src/tls_dummy.c` - `src/tls_dummy.c`
- `src/tls_mbed.c` - `src/tls_mbed.c`
- `src/tls_openssl.c` - `src/tls_openssl.c`
- `src/tls_p256_cortex.c`
- `src/tls_p256.c`
- `src/tls_p384.c`
- `src/tls_rsa.c` - `src/tls_rsa.c`
- `src/tls_uecc.c`
- `src/tls_x25519.c`
Perform a focused security review centered on these files and this security surface. Perform a focused security review centered on these files and this security surface.
@@ -38,6 +39,6 @@ Trace attacker-controlled TLS peer input through normal client and server execut
- Verify that wildcard matching is applied only to certificate presented identifiers, not to arbitrary reference identifiers or general DNS glob matching. - Verify that wildcard matching is applied only to certificate presented identifiers, not to arbitrary reference identifiers or general DNS glob matching.
- Review `mg_match` or other glob/matching helpers if used for certificate identity checks, authorization checks, filesystem paths, routing, or topic matching. Flag cases where glob semantics are broader than the security policy requires. - Review `mg_match` or other glob/matching helpers if used for certificate identity checks, authorization checks, filesystem paths, routing, or topic matching. Flag cases where glob semantics are broader than the security policy requires.
- Check certificate parsing for DER/PEM boundary errors, length overflows, malformed ASN.1, extension parsing mistakes, name constraints, SAN vs CN precedence, embedded NUL, and invalid string type handling. - Check certificate parsing for DER/PEM boundary errors, length overflows, malformed ASN.1, extension parsing mistakes, name constraints, SAN vs CN precedence, embedded NUL, and invalid string type handling.
- Review TLS record parsing, handshake transcript handling, key schedule, random generation, ECC/X25519 operations, AES-GCM nonce/tag handling, and error handling for memory safety and authentication failures. - Review TLS record parsing, handshake transcript handling, key schedule, random generation, ECC (P-256, P-384) operations, AES-GCM nonce/tag handling, and error handling for memory safety and authentication failures.
- Flag any behavior that silently falls back from verified TLS to unverified TLS or plaintext. - Flag any behavior that silently falls back from verified TLS to unverified TLS or plaintext.
- Check that TLS receive/send error paths cannot corrupt connection state, reuse freed buffers, or continue processing unauthenticated plaintext as encrypted data. - Check that TLS receive/send error paths cannot corrupt connection state, reuse freed buffers, or continue processing unauthenticated plaintext as encrypted data.
+26 -20
View File
@@ -3,9 +3,9 @@
"bomFormat": "CycloneDX", "bomFormat": "CycloneDX",
"specVersion": "1.6", "specVersion": "1.6",
"serialNumber": "urn:uuid:d3f8b1e2-4a7c-4d9e-bf2a-1c3e5f7a9b0d", "serialNumber": "urn:uuid:d3f8b1e2-4a7c-4d9e-bf2a-1c3e5f7a9b0d",
"version": 1, "version": 2,
"metadata": { "metadata": {
"timestamp": "2026-08-12T00:00:00Z", "timestamp": "2026-10-13T00:00:00Z",
"component": { "component": {
"type": "library", "type": "library",
"bom-ref": "mongoose", "bom-ref": "mongoose",
@@ -34,14 +34,31 @@
"components": [ "components": [
{ {
"type": "library", "type": "library",
"bom-ref": "micro-ecc", "bom-ref": "bearssl-ec",
"author": "Kenneth MacKay", "author": "Thomas Pornin",
"name": "micro-ecc", "name": "BearSSL EC (P-256, P-384, ECDSA)",
"description": "ECC point operations on GF(p) curves (secp256r1). Used by Mongoose built-in TLS 1.3 for ECDH key exchange and ECDSA signatures. Source: src/tls_uecc.c, src/tls_uecc.h.", "description": "P-256 (ec_p256_m31) and P-384 (ec_prime_i31) elliptic curve arithmetic, ECDSA sign and verify, and the i31 bignum code they use, derived from BearSSL. Used by Mongoose built-in TLS 1.3 for ECDHE key exchange and ECDSA signatures, by JWT ES256 and by OTA firmware signature verification. Source: src/tls_p256.c, src/tls_p256.h, src/tls_p384.c, src/tls_p384.h.",
"licenses": [ { "expression": "BSD-2-Clause" } ], "licenses": [ { "expression": "MIT" } ],
"purl": "pkg:github/kmackay/micro-ecc",
"externalReferences": [ "externalReferences": [
{ "type": "vcs", "url": "https://github.com/kmackay/micro-ecc" } { "type": "website", "url": "https://bearssl.org" }
],
"properties": [
{ "name": "upstream-commit", "value": "7bea48e5e850ab4cafbe68d3765cdaba13a86d6f" }
]
},
{
"type": "library",
"bom-ref": "p256-cortex-m4",
"author": "Emil Lenngren, Shortcut Labs AB",
"name": "P256-Cortex-M4",
"description": "P-256 ECDH and ECDSA in Cortex-M assembly. Used by Mongoose built-in TLS 1.3 instead of the BearSSL P-256 code on Cortex-M cores with the DSP extension (M4, M7, M33, M55, M85). Source: src/tls_p256_cortex.c.",
"licenses": [ { "expression": "MIT" } ],
"purl": "pkg:github/Emill/P256-Cortex-M4@ecd3ec2222fc9e18b6b44c86bb7183971a2041fc",
"externalReferences": [
{ "type": "vcs", "url": "https://github.com/Emill/P256-Cortex-M4" }
],
"properties": [
{ "name": "upstream-commit", "value": "ecd3ec2222fc9e18b6b44c86bb7183971a2041fc" }
] ]
}, },
{ {
@@ -55,17 +72,6 @@
{ "type": "website", "url": "https://bearssl.org" } { "type": "website", "url": "https://bearssl.org" }
] ]
}, },
{
"type": "library",
"bom-ref": "strobe-x25519",
"author": "Cryptography Research, Inc. / Mike Hamburg",
"name": "STROBE x25519 (adapted)",
"description": "X25519 Diffie-Hellman function adapted from the STROBE project. Used by Mongoose built-in TLS 1.3 for ECDH key exchange. Source: src/tls_x25519.c.",
"licenses": [ { "expression": "MIT" } ],
"externalReferences": [
{ "type": "website", "url": "https://strobe.sourceforge.io/" }
]
},
{ {
"type": "library", "type": "library",
"bom-ref": "sha1-reid", "bom-ref": "sha1-reid",
-2
View File
@@ -4,7 +4,6 @@
#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ... #define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ...
#define MG_ARCH_WIN32 2 // Windows #define MG_ARCH_WIN32 2 // Windows
#define MG_ARCH_ESP32 3 // ESP32 #define MG_ARCH_ESP32 3 // ESP32
#define MG_ARCH_ESP8266 4 // ESP8266
#define MG_ARCH_FREERTOS 5 // FreeRTOS <-- DEPRECATED !!! #define MG_ARCH_FREERTOS 5 // FreeRTOS <-- DEPRECATED !!!
@@ -50,7 +49,6 @@
#include "arch_armgcc.h" #include "arch_armgcc.h"
#include "arch_cube.h" #include "arch_cube.h"
#include "arch_esp32.h" #include "arch_esp32.h"
#include "arch_esp8266.h"
#include "arch_rtx.h" #include "arch_rtx.h"
#include "arch_threadx.h" #include "arch_threadx.h"
#include "arch_unix.h" #include "arch_unix.h"
+4 -53
View File
@@ -22,10 +22,8 @@
#include <esp_system.h> #include <esp_system.h>
#include <esp_mac.h> #include <esp_mac.h>
#include <esp_idf_version.h> #include <esp_idf_version.h>
#include <esp_ota_ops.h> // #include <esp_memory_utils.h> // For health record coredumps
#include <esp_task_wdt.h> // #include <esp_core_dump.h> // For health record coredumps
#include <soc/soc.h>
#include <soc/rtc_cntl_reg.h>
#define MG_PATH_MAX 128 #define MG_PATH_MAX 128
@@ -33,55 +31,8 @@
#define MG_ENABLE_POSIX_FS 1 #define MG_ENABLE_POSIX_FS 1
#endif #endif
#ifndef MG_OTA_ESP32_WDT_TIMEOUT_MS #ifndef MG_HEALTH_DATA_SIZE
#define MG_OTA_ESP32_WDT_TIMEOUT_MS 10000U #define MG_HEALTH_DATA_SIZE 20
#endif
static inline void mg_ota_esp32_timer_start(void) {
#if ESP_IDF_VERSION_MAJOR >= 5
esp_task_wdt_config_t cfg = {0};
esp_err_t err;
cfg.timeout_ms = MG_OTA_ESP32_WDT_TIMEOUT_MS;
cfg.idle_core_mask = 0;
cfg.trigger_panic = true;
err = esp_task_wdt_init(&cfg);
if (err == ESP_ERR_INVALID_STATE) esp_task_wdt_reconfigure(&cfg);
#else
esp_task_wdt_init(MG_OTA_ESP32_WDT_TIMEOUT_MS / 1000U, true);
#endif
esp_task_wdt_add(NULL);
esp_task_wdt_reset();
}
static inline uint32_t mg_ota_esp32_state_set(uint32_t state) {
if (state == 0) esp_ota_mark_app_valid_cancel_rollback();
REG_WRITE(RTC_CNTL_STORE0_REG, state);
return REG_READ(RTC_CNTL_STORE0_REG);
}
#define MG_OTA_STATE_GET() \
REG_READ(RTC_CNTL_STORE0_REG)
#define MG_OTA_STATE_SET(v) mg_ota_esp32_state_set((uint32_t) (v))
#ifndef MG_OTA_ROLLBACK_TIMER_START
#define MG_OTA_ROLLBACK_TIMER_START() mg_ota_esp32_timer_start()
#endif
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
#define MG_OTA_ROLLBACK_TIMER_FEED() \
do { \
(void) esp_task_wdt_reset(); \
} while (0)
#endif
#ifndef MG_OTA_ROLLBACK
#define MG_OTA_ROLLBACK() \
do { \
(void) esp_ota_mark_app_invalid_rollback_and_reboot(); \
} while (0)
#endif #endif
#endif #endif
-26
View File
@@ -1,26 +0,0 @@
#pragma once
#if MG_ARCH == MG_ARCH_ESP8266
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <esp_system.h>
#define MG_PATH_MAX 128
#endif
+858 -698
View File
File diff suppressed because it is too large. Load diff
+114 -71
View File
@@ -4,22 +4,64 @@
#include "net_builtin.h" #include "net_builtin.h"
#if MG_ENABLE_BSD_SOCKETS #if MG_ENABLE_BSD_SOCKETS
void mg_bsd_init(struct mg_mgr *);
void mg_bsd_poll(struct mg_mgr *);
#ifndef MG_ENABLE_BSD_PROTOTYPES
#include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h> #ifndef EAGAIN
#include <netinet/tcp.h> #define EAGAIN 11
#include <poll.h> #endif
#include <sys/select.h> #ifndef EWOULDBLOCK
#include <sys/socket.h> #define EWOULDBLOCK EAGAIN
#include <sys/time.h> #endif
#include <sys/un.h> #ifndef EINPROGRESS
#else #define EINPROGRESS 36
#include <errno.h> #endif
#ifndef ENOTCONN
#define ENOTCONN 107
#endif
#ifndef ECONNREFUSED
#define ECONNREFUSED 111
#endif
#ifndef ECONNRESET
#define ECONNRESET 104
#endif
#ifndef EADDRINUSE
#define EADDRINUSE 98
#endif
#ifndef ETIMEDOUT
#define ETIMEDOUT 110
#endif
#ifndef EBADF
#define EBADF 9
#endif
#ifndef EMFILE
#define EMFILE 24
#endif
#ifndef ENOMEM
#define ENOMEM 12
#endif
#ifndef EIO
#define EIO 5
#endif
#ifndef EINVAL
#define EINVAL 22
#endif
#ifndef EPERM
#define EPERM 1
#endif
#ifndef EPIPE
#define EPIPE 32
#endif
#ifndef EPROTONOSUPPORT
#define EPROTONOSUPPORT 93
#endif
#if MG_ENABLE_BSD_PROTOTYPES
typedef unsigned int socklen_t; typedef unsigned int socklen_t;
typedef unsigned long nfds_t;
typedef uint32_t in_addr_t; typedef uint32_t in_addr_t;
struct in_addr { in_addr_t s_addr; }; struct in_addr { in_addr_t s_addr; };
struct in6_addr { uint8_t s6_addr[16]; }; struct in6_addr { uint8_t s6_addr[16]; };
@@ -45,7 +87,6 @@ struct hostent {
int h_length; int h_length;
char **h_addr_list; char **h_addr_list;
}; };
#define h_addr h_addr_list[0]
struct addrinfo { struct addrinfo {
int ai_flags; int ai_flags;
int ai_family; int ai_family;
@@ -57,6 +98,32 @@ struct addrinfo {
struct addrinfo *ai_next; struct addrinfo *ai_next;
}; };
struct pollfd { int fd; short events; short revents; }; struct pollfd { int fd; short events; short revents; };
#if !defined(FD_SET) && !defined(FD_ZERO) && !defined(FD_ISSET)
#define MG_BSD_SELECT_H
typedef unsigned long __fd_mask;
#ifndef FD_SETSIZE
#define FD_SETSIZE 64
#endif
#define _NFDBITS ((int) sizeof(__fd_mask) * 8)
#define _howmany(x, y) (((x) + ((y) - 1)) / (y))
typedef struct fd_set {
__fd_mask __fds_bits[_howmany(FD_SETSIZE, _NFDBITS)];
} fd_set;
#define __fdset_mask(n) ((__fd_mask) 1 << ((n) % _NFDBITS))
#define FD_CLR(n, p) ((p)->__fds_bits[(n) / _NFDBITS] &= ~__fdset_mask(n))
#define FD_ISSET(n, p) \
(((p)->__fds_bits[(n) / _NFDBITS] & __fdset_mask(n)) != 0)
#define FD_SET(n, p) ((p)->__fds_bits[(n) / _NFDBITS] |= __fdset_mask(n))
#define FD_ZERO(p) \
do { \
fd_set *_p = (p); \
size_t _n = _howmany(FD_SETSIZE, _NFDBITS); \
while (_n > 0) _p->__fds_bits[--_n] = 0; \
} while (0)
#endif
#define h_addr h_addr_list[0]
#define AF_INET 2 #define AF_INET 2
#define AF_INET6 10 #define AF_INET6 10
#define AF_UNSPEC 0 #define AF_UNSPEC 0
@@ -69,6 +136,7 @@ struct pollfd { int fd; short events; short revents; };
#define IPPROTO_UDP 17 #define IPPROTO_UDP 17
#define INADDR_ANY 0 #define INADDR_ANY 0
#define INADDR_LOOPBACK 0x7f000001 #define INADDR_LOOPBACK 0x7f000001
#define INET_ADDRSTRLEN 16
#define SOL_SOCKET 0xffff #define SOL_SOCKET 0xffff
#define SO_REUSEADDR 2 #define SO_REUSEADDR 2
#define SO_REUSEPORT 15 #define SO_REUSEPORT 15
@@ -77,79 +145,37 @@ struct pollfd { int fd; short events; short revents; };
#define SO_BROADCAST 6 #define SO_BROADCAST 6
#define SO_RCVBUF 8 #define SO_RCVBUF 8
#define SO_SNDBUF 7 #define SO_SNDBUF 7
#define SO_NOSIGPIPE 0x1022
#define TCP_NODELAY 1 #define TCP_NODELAY 1
#define MSG_DONTWAIT 0x40 #define MSG_DONTWAIT 0x40
#define MSG_NOSIGNAL 0x4000 #define MSG_NOSIGNAL 0x4000
#define MSG_PEEK 0x02 #define MSG_PEEK 0x02
#ifndef F_GETFL
#define F_GETFL 3 #define F_GETFL 3
#endif
#ifndef F_SETFL
#define F_SETFL 4 #define F_SETFL 4
#define O_NONBLOCK 0x0004 #endif
#ifndef O_NONBLOCK
#define O_NONBLOCK 0x4000
#endif
#define SHUT_RD 0 #define SHUT_RD 0
#define SHUT_WR 1 #define SHUT_WR 1
#define SHUT_RDWR 2 #define SHUT_RDWR 2
#define AI_PASSIVE 0x0001 #define AI_PASSIVE 0x0001
#define NI_MAXHOST 1025 #define NI_MAXHOST 1025
#define NI_MAXSERV 32 #define NI_MAXSERV 32
#ifndef EAGAIN
#define EAGAIN 11
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK EAGAIN
#endif
#ifndef EINPROGRESS
#define EINPROGRESS 36
#endif
#ifndef ENOTCONN
#define ENOTCONN 107
#endif
#ifndef ECONNREFUSED
#define ECONNREFUSED 111
#endif
#ifndef ECONNRESET
#define ECONNRESET 104
#endif
#ifndef EADDRINUSE
#define EADDRINUSE 98
#endif
#ifndef ETIMEDOUT
#define ETIMEDOUT 110
#endif
#define POLLIN 0x001 #define POLLIN 0x001
#define POLLOUT 0x004 #define POLLOUT 0x004
#define POLLERR 0x008 #define POLLERR 0x008
#define POLLHUP 0x010 #define POLLHUP 0x010
#define POLLNVAL 0x020 #define POLLNVAL 0x020
uint16_t htons(uint16_t);
uint16_t ntohs(uint16_t);
uint32_t htonl(uint32_t);
uint32_t ntohl(uint32_t);
#endif // MG_ENABLE_BSD_PROTOTYPES
#define closesocket(a) close(a) #define htons(x) mg_ntohs(x)
#define ntohs(x) mg_ntohs(x)
#define htonl(x) mg_ntohl(x)
#define ntohl(x) mg_ntohl(x)
#ifndef MG_BSD_BACKLOG
#define MG_BSD_BACKLOG 5
#endif
#ifndef MG_BSD_CONNECT_TIMEOUT_MS
#define MG_BSD_CONNECT_TIMEOUT_MS 10000
#endif
// Mongoose-specific API
void mg_bsd_init(void); // must be called before socket()
void mg_bsd_poll(struct mg_mgr *); // process pending BSD commands
// Transport backend: implement these OR define MG_ENABLE_FREERTOS for the built-in backend
void *mg_bsd_transport_new(int domain, int type, int proto);
void mg_bsd_transport_free(void *t);
int mg_bsd_transport_listen(void *t, const struct sockaddr_in *addr);
void *mg_bsd_transport_accept(void *t, struct sockaddr_in *peer, bool nonblock);
ssize_t mg_bsd_transport_recv(void *t, void *buf, size_t len, bool nonblock);
ssize_t mg_bsd_transport_send(void *t, const void *buf, size_t len, bool nonblock);
int mg_bsd_transport_connect(void *t, const struct sockaddr_in *addr, bool nonblock);
void mg_bsd_transport_close(void *t);
// Standard BSD socket API
int socket(int, int, int); int socket(int, int, int);
int bind(int, const struct sockaddr *, socklen_t); int bind(int, const struct sockaddr *, socklen_t);
int listen(int, int); int listen(int, int);
@@ -157,23 +183,40 @@ int accept(int, struct sockaddr *, socklen_t *);
int connect(int, const struct sockaddr *, socklen_t); int connect(int, const struct sockaddr *, socklen_t);
ssize_t send(int, const void *, size_t, int); ssize_t send(int, const void *, size_t, int);
ssize_t recv(int, void *, size_t, int); ssize_t recv(int, void *, size_t, int);
ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *,
socklen_t);
ssize_t recvfrom(int, void *, size_t, int, struct sockaddr *, socklen_t *); ssize_t recvfrom(int, void *, size_t, int, struct sockaddr *, socklen_t *);
int close(int); int close(int);
int shutdown(int, int); int shutdown(int, int);
int fcntl(int, int, int);
int setsockopt(int, int, int, const void *, socklen_t); int setsockopt(int, int, int, const void *, socklen_t);
int getsockopt(int, int, int, void *, socklen_t *); int getsockopt(int, int, int, void *, socklen_t *);
int getsockname(int, struct sockaddr *, socklen_t *); int getsockname(int, struct sockaddr *, socklen_t *);
int getpeername(int, struct sockaddr *, socklen_t *); int getpeername(int, struct sockaddr *, socklen_t *);
int select(int, fd_set *, fd_set *, fd_set *, struct timeval *); int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
int poll(struct pollfd *, unsigned int, int); int poll(struct pollfd *, nfds_t, int);
struct hostent *gethostbyname(const char *); struct hostent *gethostbyname(const char *);
int getaddrinfo(const char *, const char *, const struct addrinfo *, struct addrinfo **); int getaddrinfo(const char *, const char *, const struct addrinfo *,
struct addrinfo **);
void freeaddrinfo(struct addrinfo *); void freeaddrinfo(struct addrinfo *);
int inet_pton(int, const char *, void *); int inet_pton(int, const char *, void *);
const char *inet_ntop(int, const void *, char *, socklen_t); const char *inet_ntop(int, const void *, char *, socklen_t);
in_addr_t inet_addr(const char *); in_addr_t inet_addr(const char *);
char *inet_ntoa(struct in_addr); char *inet_ntoa(struct in_addr);
#else
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <poll.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#endif // MG_ENABLE_BSD_PROTOTYPES
#include <sys/time.h>
#else
#define mg_bsd_init(mgr)
#define mg_bsd_poll(mgr)
#endif // MG_ENABLE_BSD_SOCKETS #endif // MG_ENABLE_BSD_SOCKETS
+26
View File
@@ -65,8 +65,13 @@
#endif #endif
#ifndef MG_ENABLE_IPV6 #ifndef MG_ENABLE_IPV6
#if (defined(MG_ENABLE_DRIVER_MCXW71) && MG_ENABLE_DRIVER_MCXW71) || \
(defined(MG_ENABLE_DRIVER_RW612_154) && MG_ENABLE_DRIVER_RW612_154)
#define MG_ENABLE_IPV6 1 // 802.15.4 radio: 6LoWPAN carries IPv6 only
#else
#define MG_ENABLE_IPV6 0 #define MG_ENABLE_IPV6 0
#endif #endif
#endif
#ifndef MG_IPV6_V6ONLY #ifndef MG_IPV6_V6ONLY
#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address #define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address
@@ -211,6 +216,8 @@
#endif #endif
// Set the interface hardware address. Ethernet: 6-byte MAC. 6LoWPAN: 8-byte
// EUI-64, the IPv6 interface ID is made from it. Not set: random per boot
#ifndef MG_SET_MAC_ADDRESS #ifndef MG_SET_MAC_ADDRESS
#define MG_SET_MAC_ADDRESS(mac) #define MG_SET_MAC_ADDRESS(mac)
#endif #endif
@@ -223,10 +230,29 @@
#define MG_ENABLE_TCPIP_PRINT_DEBUG_STATS 0 #define MG_ENABLE_TCPIP_PRINT_DEBUG_STATS 0
#endif #endif
#ifndef MG_TLS_RECORD_SIZE
#define MG_TLS_RECORD_SIZE 16384 // Max plaintext per sent TLS record, builtin
#endif
#ifndef MG_ENABLE_CHACHA20 #ifndef MG_ENABLE_CHACHA20
#define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN #define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN
#endif #endif
// P-256 implementation, for MG_TLS_BUILTIN
#define MG_P256_LENNGREN 1 // Cortex-M4/M7/M33 asm, see tls_p256_cortex.c
#define MG_P256_BEAR 3 // Portable C, BearSSL, see tls_p256.c
#ifndef MG_P256
#if defined(__GNUC__) && defined(__ARM_ARCH_PROFILE) && \
__ARM_ARCH_PROFILE == 'M' && defined(__ARM_FEATURE_DSP) && \
!defined(__ARM_BIG_ENDIAN)
#define MG_P256 MG_P256_LENNGREN // Cortex-M with DSP: M4, M7, M33, M55, M85
#else
#define MG_P256 MG_P256_BEAR
#endif
#endif
#ifndef MG_ENABLE_HEALTH #ifndef MG_ENABLE_HEALTH
#define MG_ENABLE_HEALTH 0 #define MG_ENABLE_HEALTH 0
#endif #endif
+1 -16
View File
@@ -533,21 +533,6 @@ static void mg_dash_handle_add(struct mg_connection *c, struct mg_dash *dash,
} }
} }
// Firmware upload callback for mg_http_stream_body(). Uses *p as a flag:
// non-NULL means an OTA session is open and must be closed by mg_ota_end()
static bool otacb(struct mg_http_message *hm, struct mg_str *data, void **p) {
if (hm != NULL) { // Start
if (!mg_ota_begin(hm->body.len)) return false;
*p = (void *) (ptrdiff_t) 1;
return true;
} else if (data != NULL) { // Next chunk
return mg_ota_write(data->buf, data->len);
}
if (*p == NULL) return false; // End. Begin failed, nothing to finish
*p = NULL;
return mg_ota_end();
}
static void mg_serve_fs(struct mg_connection *c, struct mg_http_message *hm, static void mg_serve_fs(struct mg_connection *c, struct mg_http_message *hm,
struct mg_dash_user *u, bool stream) { struct mg_dash_user *u, bool stream) {
struct mg_dash *dash = (struct mg_dash *) c->fn_data; struct mg_dash *dash = (struct mg_dash *) c->fn_data;
@@ -596,7 +581,7 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
} else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) { } else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
// Authenticated above. If streaming started, the stream handler owns // Authenticated above. If streaming started, the stream handler owns
// the connection and c->data, so skip the marker check below // the connection and c->data, so skip the marker check below
if (mg_http_stream_body(c, hm, otacb, NULL)) return; if (mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) return;
} }
if (d->marker != '\0') mg_log_http_req(c, hm); if (d->marker != '\0') mg_log_http_req(c, hm);
} else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') { } else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') {
+24 -3
View File
@@ -173,6 +173,15 @@ bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
return true; return true;
} }
#if MG_ENABLE_TCPIP && MG_ENABLE_IPV6
// NAT64 prefix of the built-in stack interface, or NULL if none
static const uint8_t *nat64_prefix(struct mg_mgr *mgr) {
static const uint8_t zero[12] = {0};
if (mgr->ifp == NULL || memcmp(mgr->ifp->nat64, zero, 12) == 0) return NULL;
return mgr->ifp->nat64;
}
#endif
static void dns_cb(struct mg_connection *c, int ev, void *ev_data) { static void dns_cb(struct mg_connection *c, int ev, void *ev_data) {
struct dns_data *d, *tmp; struct dns_data *d, *tmp;
struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests; struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests;
@@ -196,6 +205,14 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data) {
// MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid)); // MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid));
if (dm.txnid != d->txnid) continue; if (dm.txnid != d->txnid) continue;
if (d->c->is_resolving) { if (d->c->is_resolving) {
#if MG_ENABLE_TCPIP && MG_ENABLE_IPV6
if (dm.resolved && !dm.addr.is_ip6 && nat64_prefix(c->mgr) != NULL) {
uint32_t ip4 = dm.addr.addr.ip4; // DNS64: prefix + IPv4, RFC 6052
memcpy(dm.addr.addr.ip, nat64_prefix(c->mgr), 12);
memcpy(&dm.addr.addr.ip[12], &ip4, 4);
dm.addr.is_ip6 = true;
}
#endif
if (dm.resolved) { if (dm.resolved) {
dm.addr.port = d->c->rem.port; // Save port dm.addr.port = d->c->rem.port; // Save port
d->c->rem = dm.addr; // Copy resolved address d->c->rem = dm.addr; // Copy resolved address
@@ -263,14 +280,14 @@ static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name,
bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc); bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc);
bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc) { bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc) {
if (dnsc->url == NULL) { if (dnsc->url == NULL) {
mg_error(0, "DNS server URL is NULL. Call mg_mgr_init()"); MG_ERROR(("DNS server URL is NULL. Call mg_mgr_init()"));
return false; return false;
} }
if (dnsc->c == NULL) { if (dnsc->c == NULL) {
dnsc->c = mg_connect(mgr, dnsc->url, NULL, NULL); dnsc->c = mg_connect(mgr, dnsc->url, NULL, NULL);
if (dnsc->c == NULL) return false; if (dnsc->c == NULL) return false;
dnsc->c->pfn = dns_cb;
} }
dnsc->c->pfn = dns_cb; // do it always, DHCP-assigned DNS bootstrap needs it
return true; return true;
} }
@@ -314,7 +331,11 @@ void mg_resolve(struct mg_connection *c, const char *url) {
} else { } else {
// host is not an IP nor a .local, send DNS resolution request // host is not an IP nor a .local, send DNS resolution request
struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4; struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4;
sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6); bool aaaa = c->mgr->use_dns6;
#if MG_ENABLE_TCPIP && MG_ENABLE_IPV6
if (nat64_prefix(c->mgr) != NULL) aaaa = false; // DNS64: ask for A
#endif
sendnsreq(c, &host, c->mgr->dnstimeout, dns, aaaa);
} }
} }
+180
View File
@@ -0,0 +1,180 @@
#include "net_builtin.h"
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
MG_ENABLE_DRIVER_MCXW71
// ZLL, 802.15.4 link layer. See MCXW71 reference manual and NXP
// ieee_802_15_4 PHY sources: PhyPacketProcessor.c, PhyPlmeData.c, PhyISR.c
struct mg_zll {
volatile uint32_t IRQSTS, PHY_CTRL, EVENT_TMR, TIMESTAMP, T1CMP, T2CMP,
T2PRIMECMP, T3CMP, T4CMP, PA_PWR, CHANNEL_NUM0, LQI_AND_RSSI,
MACSHORTADDRS0, MACLONGADDRS0_LSB, MACLONGADDRS0_MSB, RX_FRAME_FILTER,
CCA_LQI_CTRL, CCA2_CTRL, RESERVED_0, DSM_CTRL, RESERVED_1,
MACSHORTADDRS1, MACLONGADDRS1_LSB, MACLONGADDRS1_MSB, DUAL_PAN_CTRL,
CHANNEL_NUM1, SAM_CTRL, SAM_TABLE, SAM_MATCH, SAM_FREE_IDX,
SEQ_CTRL_STS, ACKDELAY, FILTERFAIL_CODE, RX_WTR_MARK, RESERVED_2,
SLOT_PRELOAD, SEQ_STATE, TMR_PRESCALE, LENIENCY_LSB, LENIENCY_MSB,
PART_ID, COEX_CTRL, COEX_PRIORITY, ENHACK_CTRL0;
};
#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE & 2)
#define MGI_ZLL ((struct mg_zll *) 0x58A01000)
#define MGI_PKT_RAM ((volatile uint8_t *) 0x58A08000)
#else
#define MGI_ZLL ((struct mg_zll *) 0x48A01000)
#define MGI_PKT_RAM ((volatile uint8_t *) 0x48A08000)
#endif
#define MGI_TX_PB MGI_PKT_RAM // Length byte (incl. FCS), then frame
#define MGI_RX_PB (MGI_PKT_RAM + 0x80) // Received frame, no length byte
#define MGI_IRQSTS_SEQIRQ MG_BIT(0)
#define MGI_IRQSTS_RXIRQ MG_BIT(2)
#define MGI_IRQSTS_RX_LEN(x) (((x) >> 24) & 0x7f) // Frame length incl. FCS
#define MGI_PHY_CTRL_XCVSEQ 7U
#define MGI_PHY_CTRL_AUTOACK MG_BIT(3)
#define MGI_PHY_CTRL_RXACKRQD MG_BIT(4)
#define MGI_PHY_CTRL_CCABFRTX MG_BIT(5)
#define MGI_PHY_CTRL_SLOTTED MG_BIT(6)
#define MGI_PHY_CTRL_PROMISCUOUS MG_BIT(25)
#define MGI_PHY_CTRL_ALL_IRQ_MASKS 0x8007ff00U // Bits 8..18, 31: mask all IRQs
#define MGI_SEQ_STATE_MASK 0x1fU
#define MGI_SEQ_STATE_RX_WU 0x10U // RX warm up
#define MGI_SEQ_CTRL_NO_RX_RECYCLE MG_BIT(5)
#define MGI_FRAME_FILTER 0x70bU // Beacon, data, command; versions 0..2
#define MGI_ACKDELAY_MASK 0x7fU
#define MGI_ACKDELAY_VALUE 0x7cU // -4, 802.15.4 turnaround. See PhyHwInit()
enum { MGI_SEQ_IDLE = 0, MGI_SEQ_RX = 1, MGI_SEQ_TX = 2, MGI_SEQ_TR = 4 }; // PHY_CTRL[XCVSEQ]
#define MGI_FCF_ACK_REQUEST 0x20U // First frame byte: acknowledgment request
#define MGI_TX_TIMEOUT 400U // Symbols, 16 us: 127 bytes, then ACK wait
#define MGI_TX_TRIES 10 // Tries for unacknowledged frames
#define MGI_TX_BACKOFF 64U // Symbols, 1 ms: backoff grows by this per try
// Symbol timer, 16 us, 24 bits
static uint32_t mg_mcxw71_symbols(void) {
return MGI_ZLL->EVENT_TMR >> 8;
}
static void mg_mcxw71_delay(uint32_t symbols) {
uint32_t start = mg_mcxw71_symbols();
while (((mg_mcxw71_symbols() - start) & 0xffffffU) < symbols) (void) 0;
}
// Wait for the sequence to end, up to MGI_TX_TIMEOUT symbols
static bool mg_mcxw71_wait(void) {
uint32_t start = mg_mcxw71_symbols();
while (((mg_mcxw71_symbols() - start) & 0xffffffU) < MGI_TX_TIMEOUT) {
if (MGI_ZLL->IRQSTS & MGI_IRQSTS_SEQIRQ) return true;
}
return false;
}
// Stop the current sequence, wait for the sequencer to go idle. See PhyAbort()
static void mg_mcxw71_idle(void) {
if (MGI_ZLL->PHY_CTRL & MGI_PHY_CTRL_XCVSEQ) {
while ((MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) == MGI_SEQ_STATE_RX_WU) (void) 0;
MGI_ZLL->PHY_CTRL &= ~MGI_PHY_CTRL_XCVSEQ;
while (MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) (void) 0;
}
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS; // Clear all flags, write 1 to clear
}
// Start a new sequence: MGI_SEQ_RX or MGI_SEQ_TX
static void mg_mcxw71_start(uint32_t seq) {
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
MGI_ZLL->PHY_CTRL |= seq;
}
// Program our addresses for hardware filtering and auto-ACK: PAN ID, short
// address and EUI-64 (little endian), when they change. The L2 sets them
static void mg_mcxw71_addrs(struct mg_tcpip_if *ifp) {
struct lowpan_data *l = &ifp->l2data.lowpan;
const uint8_t *e = ifp->mac;
uint32_t s = ((uint32_t) l->short_addr << 16) | l->pan_id;
uint32_t lo = (uint32_t) (e[7] | (e[6] << 8) | (e[5] << 16)) |
((uint32_t) e[4] << 24);
uint32_t hi = (uint32_t) (e[3] | (e[2] << 8) | (e[1] << 16)) |
((uint32_t) e[0] << 24);
if (MGI_ZLL->MACSHORTADDRS0 != s || MGI_ZLL->MACLONGADDRS0_LSB != lo ||
MGI_ZLL->MACLONGADDRS0_MSB != hi) {
mg_mcxw71_idle();
MGI_ZLL->MACSHORTADDRS0 = s;
MGI_ZLL->MACLONGADDRS0_LSB = lo, MGI_ZLL->MACLONGADDRS0_MSB = hi;
mg_mcxw71_start(MGI_SEQ_RX);
}
}
static bool mg_mcxw71_init(struct mg_tcpip_if *ifp) {
uint8_t channel = ifp->l2data.lowpan.channel;
if (ifp->l2type != MG_TCPIP_L2_LOWPAN || channel < 11 || channel > 26) {
MG_ERROR(("Use MG_TCPIP_L2_LOWPAN, channel 11..26"));
return false;
}
MGI_ZLL->SEQ_CTRL_STS |= MGI_SEQ_CTRL_NO_RX_RECYCLE;
// Mask all interrupts, we poll. Filter frames by address, auto-ACK
MGI_ZLL->PHY_CTRL = MGI_PHY_CTRL_ALL_IRQ_MASKS | MGI_PHY_CTRL_AUTOACK;
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
MGI_ZLL->SAM_CTRL = 0;
MGI_ZLL->RX_FRAME_FILTER = MGI_FRAME_FILTER;
MGI_ZLL->ACKDELAY = (MGI_ZLL->ACKDELAY & ~MGI_ACKDELAY_MASK) | MGI_ACKDELAY_VALUE;
MGI_ZLL->TMR_PRESCALE = 5; // 16 us timebase, one symbol
MGI_ZLL->CHANNEL_NUM0 = channel;
MGI_ZLL->PA_PWR = 0x3f; // PA slices, max output power
mg_mcxw71_start(MGI_SEQ_RX);
mg_mcxw71_addrs(ifp);
return true;
}
static size_t mg_mcxw71_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
const uint8_t *p = (const uint8_t *) buf;
size_t i;
bool ok = false, ack = len > 0 && (p[0] & MGI_FCF_ACK_REQUEST);
int tries;
if (len == 0 || len > 125) return 0; // 127 bytes max, incl. 2 bytes FCS
mg_mcxw71_idle();
MGI_TX_PB[0] = (uint8_t) (len + 2); // FCS is added by hardware
for (i = 0; i < len; i++) MGI_TX_PB[i + 1] = p[i]; // 8-bit access only
MGI_ZLL->PHY_CTRL &= ~(MGI_PHY_CTRL_CCABFRTX | MGI_PHY_CTRL_RXACKRQD | MGI_PHY_CTRL_SLOTTED);
if (ack) MGI_ZLL->PHY_CTRL |= MGI_PHY_CTRL_RXACKRQD;
// Frames with ACK request: transmit, then receive the ACK (TR sequence),
// retry with growing backoff. See PhyPdDataRequest(), PhyISR.c
for (tries = 0; tries < (ack ? MGI_TX_TRIES : 1) && !ok; tries++) {
if (tries > 0) mg_mcxw71_delay(MGI_TX_BACKOFF * (uint32_t) tries);
mg_mcxw71_start(ack ? MGI_SEQ_TR : MGI_SEQ_TX);
ok = mg_mcxw71_wait() && (!ack || (MGI_ZLL->IRQSTS & MGI_IRQSTS_RXIRQ));
mg_mcxw71_idle();
}
MGI_ZLL->PHY_CTRL &= ~MGI_PHY_CTRL_RXACKRQD;
mg_mcxw71_start(MGI_SEQ_RX);
if (!ok) MG_DEBUG(("TX failed, %d tries", tries));
(void) ifp;
return ok ? len : 0;
}
static size_t mg_mcxw71_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
uint32_t irqsts = MGI_ZLL->IRQSTS;
size_t i, n = 0;
mg_mcxw71_addrs(ifp);
if ((irqsts & MGI_IRQSTS_SEQIRQ) == 0) return 0; // Still receiving
if (irqsts & MGI_IRQSTS_RXIRQ) {
n = MGI_IRQSTS_RX_LEN(irqsts);
n = n >= 2 ? n - 2 : 0; // Strip FCS
if (n > len) n = len;
for (i = 0; i < n; i++) ((uint8_t *) buf)[i] = MGI_RX_PB[i];
}
mg_mcxw71_idle();
mg_mcxw71_start(MGI_SEQ_RX);
(void) ifp;
return n;
}
static bool mg_mcxw71_poll(struct mg_tcpip_if *ifp, bool s1) {
(void) ifp, (void) s1;
return true; // Radio link is always up
}
struct mg_tcpip_driver mg_tcpip_driver_mcxw71 = {mg_mcxw71_init, mg_mcxw71_tx,
mg_mcxw71_rx, mg_mcxw71_poll};
#endif
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
MG_ENABLE_DRIVER_MCXW71
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer. Frames passed to tx() and
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
// The transceiver (XCVR) must be set up for 802.15.4 before mg_tcpip_init(),
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
// ifp->l2data.lowpan and ifp->mac. No driver_data
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_if mif_; \
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
mif_.driver = &mg_tcpip_driver_mcxw71; \
mif_.l2data.lowpan.channel = 15; \
mif_.l2data.lowpan.pan_id = 0xabcd; \
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
mg_tcpip_init(mgr, &mif_); \
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
MG_INFO(("Driver: mcxw71, 6LoWPAN")); \
} while (0)
#endif
+471
View File
@@ -0,0 +1,471 @@
#include "net_builtin.h"
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
MG_ENABLE_DRIVER_RW612_154
// The 802.15.4 radio runs NXP combo firmware on CPU2, an OpenThread RCP. We
// are CPU3. Messages go through the IMU mailbox: IMU messages carry IMUMC
// messages, IMUMC messages carry HDLC framed Spinel. See NXP sources:
// mcuxsdk-core drivers/imu/fsl_imu.c, mcux-component
// imu_adapter/fsl_adapter_imu.c, connectivity framework
// platform/rw61x/fwk_platform_hdlc.c; OpenThread src/lib/spinel/spinel.h
// IMU message FIFO registers, one block per direction. See fsl_imu.h
struct mg_rw612_imu {
volatile uint32_t WR_MSG, RD_MSG, STATUS, CNTL, RD_MSG_DBG;
};
#define MGI_IMU ((struct mg_rw612_imu *) 0x443ff804) // CPU3 -> CPU2
#define MGI_IMU_PEER ((struct mg_rw612_imu *) 0x443ff818) // CPU2 -> CPU3
#define MGI_IMU_LOCKED MG_BIT(0) // STATUS: FIFO locked
#define MGI_IMU_ALMOST_FULL MG_BIT(1) // STATUS: FIFO almost full
#define MGI_IMU_EMPTY MG_BIT(3) // STATUS: FIFO empty
#define MGI_IMU_WR_PTR(s) (((s) >> 16) & 15U)
#define MGI_IMU_INT_CLR MG_BIT(0) // CNTL: clear message ready IRQ
#define MGI_IMU_FLUSH MG_BIT(16) // CNTL: flush send FIFO
#define MGI_IMU_WAIT_FOR_ACK MG_BIT(17) // CNTL: lock send FIFO
#define MGI_IMU_WATERMARK(x) ((uint32_t) (x) << 20)
#define MGI_IMU_WATERMARK_MASK (15U << 20)
// IMU message: header, then payload words. See IMU_Msg_t
struct mg_rw612_msg {
uint8_t type, subtype, seq, len; // len: number of payload words
uint32_t payload[8];
};
// Mailbox in SMU2 shared memory, at the address CPU2 firmware expects
struct mg_rw612_mbox {
uint32_t magic[2]; // Sync pattern: [0] CPU2, [1] us
struct mg_rw612_msg q[2][16]; // Message slots: [0] CPU2 -> us, [1] ours
};
#define MGI_MBOX ((volatile struct mg_rw612_mbox *) 0x443c0000)
#define MGI_TXBUF 0x443c1508U // Our IMUMC buffers: 16 x 264 bytes
#define MGI_TXBUF_CNT 16
#define MGI_TXBUF_LEN 264U
#define MGI_SLEEP_FLAG (*(volatile uint32_t *) 0x443c2588)
#define MGI_SYNC_MAGIC 0xabcdef89U
// IMUMC message, carried in IMU payload words. See imumc_std_msg
struct mg_rw612_imumc {
uint32_t src, dst; // Endpoint addresses
uint16_t rfu, idx; // Reserved
uint16_t len; // Payload length, bytes
uint16_t flags; // Message flags
uint32_t data; // Payload buffer address
};
#define MGI_EPT_LOCAL 10 // Our 802.15.4 endpoint
#define MGI_EPT_REMOTE 20 // Firmware 802.15.4 endpoint
enum { MGI_MSG_CONTROL = 0, MGI_MSG_IMUMC = 8 }; // IMU message types
enum { // Control message subtypes
MGI_CTL_SYNC = 1,
MGI_CTL_SYNC_ACK = 2,
MGI_CTL_ACK_FOR_UNLOCK = 3,
MGI_CTL_CMD_BUF_ADDR = 6,
MGI_CTL_EPT_QUERY = 11,
MGI_CTL_EPT_QUERY_RSP = 12,
MGI_CTL_BUF_FREE = 13
};
// Spinel commands, properties and statuses we use
enum { MGI_CMD_RESET = 1, MGI_CMD_GET = 2, MGI_CMD_SET = 3, MGI_CMD_IS = 6 };
enum {
MGI_PROP_LAST_STATUS = 0,
MGI_PROP_NCP_VERSION = 2,
MGI_PROP_PHY_ENABLED = 0x20,
MGI_PROP_PHY_CHAN = 0x21,
MGI_PROP_LADDR = 0x34,
MGI_PROP_SADDR = 0x35,
MGI_PROP_PANID = 0x36,
MGI_PROP_RAW_STREAM = 0x37,
MGI_PROP_STREAM_RAW = 0x71
};
#define MGI_RESET_STACK 2 // RESET argument: reset the RCP stack only
#define MGI_STATUS_RESET_MIN 112 // LAST_STATUS: RCP has reset
#define MGI_STATUS_RESET_MAX 127
#define MGI_TX_TIMEOUT_MS 200 // Wait for transmit done, incl. retries
#define MGI_RSP_TIMEOUT_MS 500
#define MGI_RXQ_CNT 4
// Driver state, one radio
struct mg_rw612 {
bool up; // IMU link synchronised
bool ept_ready; // IMUMC endpoints connected
bool reset; // RCP reported a reset
uint8_t seq; // IMU message sequence number
uint16_t free_bufs; // Free IMUMC buffers, bit per buffer
uint8_t hdlc[300]; // HDLC frame being received, unescaped
size_t hlen; // Bytes in hdlc
bool esc; // Previous byte was an escape
uint8_t tid; // Spinel transaction ID of the pending request
bool rsp; // Response to the pending request received
uint32_t rsp_status; // Its status, if LAST_STATUS, else 0
char version[64]; // RCP version string
uint8_t rxq[MGI_RXQ_CNT][127]; // Received frames, no FCS
uint8_t rxlen[MGI_RXQ_CNT]; // Their lengths
uint8_t rxhead, rxtail; // Queue indexes
uint16_t pan_id, short_addr; // Addresses programmed in the RCP
uint8_t eui[8];
uint8_t channel;
};
static struct mg_rw612 s_rw612;
static void mg_rw612_imu_send(uint8_t type, uint8_t subtype,
const uint32_t *payload, uint8_t len) {
volatile struct mg_rw612_msg *m;
uint64_t expire = mg_millis() + 100;
uint8_t i;
while ((MGI_IMU->STATUS & (MGI_IMU_ALMOST_FULL | MGI_IMU_LOCKED)) &&
mg_millis() < expire) {
(void) 0;
}
m = &MGI_MBOX->q[1][MGI_IMU_WR_PTR(MGI_IMU->STATUS)];
m->type = type, m->subtype = subtype, m->seq = s_rw612.seq++, m->len = len;
for (i = 0; i < len; i++) m->payload[i] = payload[i];
MGI_IMU->WR_MSG = (uint32_t) (uintptr_t) m;
}
static void mg_rw612_control(uint8_t subtype, uint32_t a, uint32_t b,
uint8_t len) {
uint32_t payload[2] = {a, b};
mg_rw612_imu_send(MGI_MSG_CONTROL, subtype, payload, len);
}
static void mg_rw612_queue_rx(const uint8_t *frame, size_t len) {
struct mg_rw612 *s = &s_rw612;
uint8_t next = (uint8_t) ((s->rxhead + 1) % MGI_RXQ_CNT);
if (next == s->rxtail || len > sizeof(s->rxq[0])) return; // Full: drop
memcpy(s->rxq[s->rxhead], frame, len);
s->rxlen[s->rxhead] = (uint8_t) len;
s->rxhead = next;
}
// Spinel packed unsigned integer: 7 bits per byte, LSB first
static size_t mg_rw612_unpack(const uint8_t *p, size_t len, uint32_t *v) {
size_t i;
*v = 0;
for (i = 0; i < len && i < 4; i++) {
*v |= (uint32_t) (p[i] & 0x7f) << (7 * i);
if ((p[i] & 0x80) == 0) return i + 1;
}
return 0;
}
// Handle a received Spinel frame: header, command, property, value
static void mg_rw612_spinel(const uint8_t *f, size_t len) {
struct mg_rw612 *s = &s_rw612;
uint32_t cmd, prop, status = 0;
size_t n, ofs = 1;
uint8_t tid = f[0] & 15;
if (len < 3 || (n = mg_rw612_unpack(f + ofs, len - ofs, &cmd)) == 0) return;
ofs += n;
if ((n = mg_rw612_unpack(f + ofs, len - ofs, &prop)) == 0) return;
ofs += n;
if (cmd != MGI_CMD_IS) return;
if (prop == MGI_PROP_STREAM_RAW && tid == 0 && len - ofs >= 2) {
size_t flen = (size_t) (f[ofs] | (f[ofs + 1] << 8)); // Incl. FCS
if (flen >= 2 && ofs + 2 + flen <= len) {
mg_rw612_queue_rx(f + ofs + 2, flen - 2);
}
} else if (prop == MGI_PROP_LAST_STATUS) {
mg_rw612_unpack(f + ofs, len - ofs, &status);
if (tid == 0 && status >= MGI_STATUS_RESET_MIN &&
status <= MGI_STATUS_RESET_MAX) {
s->reset = true;
}
} else if (prop == MGI_PROP_NCP_VERSION) {
n = len - ofs < sizeof(s->version) - 1 ? len - ofs : sizeof(s->version) - 1;
memcpy(s->version, f + ofs, n);
s->version[n] = '\0';
}
if (tid != 0 && tid == s->tid) s->rsp = true, s->rsp_status = status;
}
// HDLC-lite: 0x7e delimits frames, 0x7d escapes, FCS-16 at the end
static uint16_t mg_rw612_fcs(uint16_t fcs, uint8_t c) {
int i;
fcs ^= c;
for (i = 0; i < 8; i++) fcs = (fcs & 1) ? (fcs >> 1) ^ 0x8408 : fcs >> 1;
return fcs;
}
static void mg_rw612_hdlc_rx(const uint8_t *p, size_t len) {
struct mg_rw612 *s = &s_rw612;
size_t i, j;
for (i = 0; i < len; i++) {
uint8_t c = p[i];
if (c == 0x7e) {
if (s->hlen >= 3) {
uint16_t fcs = 0xffff;
for (j = 0; j < s->hlen; j++) fcs = mg_rw612_fcs(fcs, s->hdlc[j]);
if (fcs == 0xf0b8) mg_rw612_spinel(s->hdlc, s->hlen - 2);
}
s->hlen = 0, s->esc = false;
} else if (c == 0x7d) {
s->esc = true;
} else if (s->hlen < sizeof(s->hdlc)) {
s->hdlc[s->hlen++] = s->esc ? c ^ 0x20 : c;
s->esc = false;
}
}
}
static void mg_rw612_control_rx(volatile struct mg_rw612_msg *m) {
struct mg_rw612 *s = &s_rw612;
switch (m->subtype) {
case MGI_CTL_ACK_FOR_UNLOCK:
MGI_IMU->CNTL &= ~MGI_IMU_WAIT_FOR_ACK;
break;
case MGI_CTL_SYNC: // As HAL_ImuCtrlHandler(): acknowledge twice
mg_rw612_control(MGI_CTL_SYNC_ACK, 0, 0, 0);
mg_rw612_control(MGI_CTL_SYNC_ACK, 0, 0, 0);
s->up = true;
break;
case MGI_CTL_SYNC_ACK:
case MGI_CTL_CMD_BUF_ADDR:
s->up = true;
break;
case MGI_CTL_EPT_QUERY: { // Firmware asks if our endpoint exists
bool ours = m->payload[0] == MGI_EPT_LOCAL;
mg_rw612_control(MGI_CTL_EPT_QUERY_RSP, m->payload[0], ours, 2);
if (ours) s->ept_ready = true;
break;
}
case MGI_CTL_EPT_QUERY_RSP:
if (m->payload[0] == MGI_EPT_REMOTE && m->payload[1] != 0) {
s->ept_ready = true;
}
break;
case MGI_CTL_BUF_FREE: { // Firmware is done with one of our buffers
uint32_t i = (m->payload[0] - MGI_TXBUF) / MGI_TXBUF_LEN;
if (i < MGI_TXBUF_CNT) s->free_bufs |= (uint16_t) MG_BIT(i);
break;
}
default:
break;
}
}
// Process all received IMU messages. See HAL_ImuReceive()
static void mg_rw612_poll_imu(void) {
bool unlock = false;
while ((MGI_IMU_PEER->STATUS & MGI_IMU_EMPTY) == 0) {
struct mg_rw612_msg m;
volatile struct mg_rw612_msg *p;
uint8_t i;
MGI_SLEEP_FLAG &= ~1U;
p = (volatile struct mg_rw612_msg *) (uintptr_t) MGI_IMU->RD_MSG;
if (MGI_IMU_PEER->STATUS & MGI_IMU_LOCKED) unlock = true;
if (p == NULL) continue;
m.type = p->type, m.subtype = p->subtype, m.len = p->len;
for (i = 0; i < 8; i++) m.payload[i] = p->payload[i];
if (m.type == MGI_MSG_CONTROL) {
mg_rw612_control_rx(&m);
} else if (m.type == MGI_MSG_IMUMC) {
struct mg_rw612_imumc im;
memcpy(&im, m.payload, sizeof(im));
if (im.dst == MGI_EPT_LOCAL) {
mg_rw612_hdlc_rx((const uint8_t *) (uintptr_t) im.data, im.len);
}
mg_rw612_control(MGI_CTL_BUF_FREE, im.data, 0, 1);
}
}
MGI_IMU->CNTL |= MGI_IMU_INT_CLR;
MGI_SLEEP_FLAG |= 1U;
if (unlock) mg_rw612_control(MGI_CTL_ACK_FOR_UNLOCK, 0, 0, 0);
}
// Send bytes to the firmware endpoint, in IMUMC buffer sized chunks
static bool mg_rw612_imumc_send(const uint8_t *p, size_t len) {
struct mg_rw612 *s = &s_rw612;
while (len > 0) {
size_t n = len < MGI_TXBUF_LEN ? len : MGI_TXBUF_LEN;
uint64_t expire = mg_millis() + 100;
struct mg_rw612_imumc im;
uint32_t i;
while (s->free_bufs == 0) {
mg_rw612_poll_imu();
if (mg_millis() > expire) return false;
}
for (i = 0; (s->free_bufs & MG_BIT(i)) == 0; i++) (void) 0;
s->free_bufs &= (uint16_t) ~MG_BIT(i);
memset(&im, 0, sizeof(im));
im.src = MGI_EPT_LOCAL, im.dst = MGI_EPT_REMOTE, im.len = (uint16_t) n;
im.data = MGI_TXBUF + i * MGI_TXBUF_LEN;
memcpy((void *) (uintptr_t) im.data, p, n);
mg_rw612_imu_send(MGI_MSG_IMUMC, 0, (uint32_t *) &im, sizeof(im) / 4);
p += n, len -= n;
}
return true;
}
// Send a Spinel frame, HDLC encoded, and wait for the response with the
// same transaction ID. Return false on timeout
static bool mg_rw612_request(uint8_t *f, size_t len, uint32_t timeout_ms) {
struct mg_rw612 *s = &s_rw612;
uint8_t buf[2 * 160 + 6];
uint16_t fcs = 0xffff;
size_t i, n = 0;
uint64_t expire = mg_millis() + timeout_ms;
s->tid = (uint8_t) (s->tid % 15 + 1);
f[0] = (uint8_t) (0x80 | s->tid); // Header: flag, interface 0, TID
s->rsp = false;
buf[n++] = 0x7e;
for (i = 0; i < len + 2 && n + 3 < sizeof(buf); i++) {
uint8_t c;
if (i < len) {
c = f[i], fcs = mg_rw612_fcs(fcs, c);
} else {
c = (uint8_t) (i == len ? ~fcs & 0xff : ~fcs >> 8);
}
if (c == 0x7e || c == 0x7d || c == 0x11 || c == 0x13 || c == 0xf8) {
buf[n++] = 0x7d, c ^= 0x20;
}
buf[n++] = c;
}
buf[n++] = 0x7e;
if (!mg_rw612_imumc_send(buf, n)) return false;
while (!s->rsp && mg_millis() < expire) mg_rw612_poll_imu();
return s->rsp;
}
static bool mg_rw612_set(uint32_t prop, const uint8_t *val, size_t len) {
uint8_t f[16];
if (len > sizeof(f) - 3) return false;
f[1] = MGI_CMD_SET, f[2] = (uint8_t) prop; // Properties used are < 0x80
memcpy(f + 3, val, len);
return mg_rw612_request(f, len + 3, MGI_RSP_TIMEOUT_MS) &&
s_rw612.rsp_status == 0;
}
// Program channel, PAN ID, short address and EUI-64 when they change
static void mg_rw612_addrs(struct mg_tcpip_if *ifp) {
struct mg_rw612 *s = &s_rw612;
struct lowpan_data *l = &ifp->l2data.lowpan;
uint8_t v[2];
if (s->channel != l->channel) {
s->channel = l->channel;
if (!mg_rw612_set(MGI_PROP_PHY_CHAN, &l->channel, 1)) MG_ERROR(("chan"));
}
if (s->pan_id != l->pan_id) {
s->pan_id = l->pan_id;
v[0] = (uint8_t) l->pan_id, v[1] = (uint8_t) (l->pan_id >> 8);
if (!mg_rw612_set(MGI_PROP_PANID, v, 2)) MG_ERROR(("panid"));
}
if (s->short_addr != l->short_addr) {
s->short_addr = l->short_addr;
v[0] = (uint8_t) l->short_addr, v[1] = (uint8_t) (l->short_addr >> 8);
if (!mg_rw612_set(MGI_PROP_SADDR, v, 2)) MG_ERROR(("saddr"));
}
if (memcmp(s->eui, ifp->mac, 8) != 0) {
memcpy(s->eui, ifp->mac, 8);
if (!mg_rw612_set(MGI_PROP_LADDR, s->eui, 8)) MG_ERROR(("laddr"));
}
}
// Log an init error, return false
static bool mg_rw612_fail(const char *msg) {
MG_ERROR(("%s", msg));
return false;
}
static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
struct mg_rw612 *s = &s_rw612;
uint8_t f[8], one = 1;
uint64_t expire;
if (ifp->l2type != MG_TCPIP_L2_LOWPAN || ifp->l2data.lowpan.channel < 11 ||
ifp->l2data.lowpan.channel > 26) {
MG_ERROR(("Use MG_TCPIP_L2_LOWPAN, channel 11..26"));
return false;
}
if (MGI_MBOX->magic[0] != MGI_SYNC_MAGIC) {
MG_ERROR(("Radio firmware is not running on CPU2"));
return false;
}
memset(s, 0, sizeof(*s));
s->free_bufs = 0xffff;
s->pan_id = s->short_addr = 0xffff; // Force programming
// Reset our send FIFO, drop stale messages. See IMU_Init()
MGI_IMU->CNTL |= MGI_IMU_FLUSH;
MGI_IMU->CNTL &= ~(MGI_IMU_FLUSH | MGI_IMU_WAIT_FOR_ACK);
MGI_IMU->CNTL = (MGI_IMU->CNTL & ~MGI_IMU_WATERMARK_MASK) |
MGI_IMU_WATERMARK(15);
while ((MGI_IMU_PEER->STATUS & MGI_IMU_EMPTY) == 0) (void) MGI_IMU->RD_MSG;
// Synchronise the IMU link, then connect the IMUMC endpoints
MGI_MBOX->magic[1] = MGI_SYNC_MAGIC;
mg_rw612_control(MGI_CTL_SYNC, 0, 0, 0);
expire = mg_millis() + 1000;
while (!s->up && mg_millis() < expire) mg_rw612_poll_imu();
if (!s->up) return mg_rw612_fail("IMU sync timeout");
expire = mg_millis() + 2000;
while (!s->ept_ready && mg_millis() < expire) {
uint64_t retry = mg_millis() + 100;
mg_rw612_control(MGI_CTL_EPT_QUERY, MGI_EPT_REMOTE, 0, 1);
while (!s->ept_ready && mg_millis() < retry) mg_rw612_poll_imu();
}
if (!s->ept_ready) return mg_rw612_fail("IMUMC endpoint timeout");
// Reset the RCP stack, wait for its reset status, read its version
f[1] = MGI_CMD_RESET, f[2] = MGI_RESET_STACK;
mg_rw612_request(f, 3, MGI_RSP_TIMEOUT_MS);
expire = mg_millis() + 1000;
while (!s->reset && mg_millis() < expire) mg_rw612_poll_imu();
if (!s->reset) return mg_rw612_fail("RCP reset timeout");
f[1] = MGI_CMD_GET, f[2] = MGI_PROP_NCP_VERSION;
mg_rw612_request(f, 3, MGI_RSP_TIMEOUT_MS);
MG_INFO(("RCP: %s", s->version));
if (!mg_rw612_set(MGI_PROP_PHY_ENABLED, &one, 1)) {
return mg_rw612_fail("PHY enable failed");
}
mg_rw612_addrs(ifp);
if (!mg_rw612_set(MGI_PROP_RAW_STREAM, &one, 1)) {
return mg_rw612_fail("Raw stream enable failed");
}
return true;
}
static size_t mg_rw612_154_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
uint8_t f[3 + 2 + 127 + 4];
size_t n = 3;
if (len == 0 || len > 125) return 0; // 127 bytes max, incl. 2 bytes FCS
f[1] = MGI_CMD_SET, f[2] = MGI_PROP_STREAM_RAW;
f[n++] = (uint8_t) (len + 2), f[n++] = 0; // PSDU length, incl. FCS
memcpy(f + n, buf, len), n += len;
f[n++] = 0, f[n++] = 0; // FCS, filled in by the radio
f[n++] = ifp->l2data.lowpan.channel;
f[n++] = 4; // Max CSMA backoffs
f[n++] = 3; // Max frame retries, for frames with ACK request
f[n++] = 1; // CSMA-CA enabled
if (!mg_rw612_request(f, n, MGI_TX_TIMEOUT_MS)) {
MG_ERROR(("TX timeout"));
return 0;
}
if (s_rw612.rsp_status != 0) {
MG_VERBOSE(("TX failed, status %lu", (unsigned long) s_rw612.rsp_status));
return 0;
}
return len;
}
static size_t mg_rw612_154_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
struct mg_rw612 *s = &s_rw612;
size_t n = 0;
mg_rw612_poll_imu();
mg_rw612_addrs(ifp);
if (s->rxtail != s->rxhead) {
n = s->rxlen[s->rxtail];
if (n > len) n = len;
memcpy(buf, s->rxq[s->rxtail], n);
s->rxtail = (uint8_t) ((s->rxtail + 1) % MGI_RXQ_CNT);
}
return n;
}
static bool mg_rw612_154_poll(struct mg_tcpip_if *ifp, bool s1) {
(void) ifp, (void) s1;
return s_rw612.up;
}
struct mg_tcpip_driver mg_tcpip_driver_rw612_154 = {
mg_rw612_154_init, mg_rw612_154_tx, mg_rw612_154_rx, mg_rw612_154_poll};
#endif
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
MG_ENABLE_DRIVER_RW612_154
// NXP RW612 IEEE 802.15.4 radio. The radio runs on CPU2: NXP BLE/802.15.4
// combo firmware, an OpenThread radio co-processor (RCP). The firmware must
// be loaded and running before mg_tcpip_init(), see the FRDM-RW612 lowpan
// tutorial. The driver talks to it over the CPU2 <-> CPU3 IMU mailbox:
// IMUMC messages carrying HDLC framed Spinel. Frames passed to tx() and
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
// ifp->l2data.lowpan and ifp->mac. No driver_data
#ifndef MG_TCPIP_DRIVER_INIT
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_if mif_; \
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
mif_.driver = &mg_tcpip_driver_rw612_154; \
mif_.l2data.lowpan.channel = 15; \
mif_.l2data.lowpan.pan_id = 0xabcd; \
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
mg_tcpip_init(mgr, &mif_); \
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
MG_INFO(("Driver: rw612_154, 6LoWPAN")); \
} while (0)
#endif
#endif
+3 -4
View File
@@ -4,7 +4,7 @@
#include "ota.h" #include "ota.h"
#ifdef MG_OTA_PUBLIC_KEY #ifdef MG_OTA_PUBLIC_KEY
#include "sha256.h" #include "sha256.h"
#include "tls_uecc.h" #include "tls_p256.h"
#endif #endif
#if MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM #if MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM
@@ -76,9 +76,8 @@ bool mg_ota_flash_end(struct mg_flash *flash) {
uint8_t hash[32]; uint8_t hash[32];
size_t fw_size = s_size - 68; // strip 64-byte sig + 4-byte magic size_t fw_size = s_size - 68; // strip 64-byte sig + 4-byte magic
mg_sha256(hash, (uint8_t *) base, fw_size); mg_sha256(hash, (uint8_t *) base, fw_size);
ok = mg_uecc_verify(s_pubkey, hash, sizeof(hash), ok = mg_p256_verify(s_pubkey, hash, sizeof(hash),
(uint8_t *) base + fw_size, (uint8_t *) base + fw_size) == 1;
mg_uecc_secp256r1()) == 1;
MG_INFO(("Signature: %s", ok ? "ok" : "fail")); MG_INFO(("Signature: %s", ok ? "ok" : "fail"));
} else { } else {
ok = false; ok = false;
+51 -3
View File
@@ -6,19 +6,19 @@
struct mg_health mg_health_record MG_HEALTH_RAM; struct mg_health mg_health_record MG_HEALTH_RAM;
// STM32 H723 support. Needs the _estack symbol from the linker script // STM32 H723 support. Needs the _estack symbol from the linker script
#if MG_ENABLE_HEALTH && defined(STM32H723xx_H) #if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5))
// Fault handler body. Runs in exception context: no printf, no malloc, no // Fault handler body. Runs in exception context: no printf, no malloc, no
// blocking calls. Copies the unwind registers and raw stack into the health // blocking calls. Copies the unwind registers and raw stack into the health
// record, then resets. // record, then resets.
// "used" keeps the linker from garbage-collecting this section: the only // "used" keeps the linker from garbage-collecting this section: the only
// reference is the "b fault_c" branch in the naked handler below // reference is the "b fault_c" branch in the naked handler below
__attribute__((used, noinline)) static void fault_c(uint32_t *frame, __attribute__((used, noinline)) static void fault_c(uint32_t *frame,
uint32_t exc_return) { uint32_t exc_return) {
extern uint32_t _estack; extern uint32_t _estack;
uint32_t *stack = frame + 8 + ((exc_return & (1U << 4)) ? 0 : 18); uint32_t *stack = frame + 8 + ((exc_return & (1U << 4)) ? 0 : 18);
uint32_t *r = mg_health_record.data; uint32_t *r = mg_health_record.data;
if (frame[7] & (1U << 9)) stack++; // Eight-byte stack alignment padding if (frame[7] & (1U << 9)) stack++; // Eight-byte stack alignment padding
r[0] = (uint32_t) (uintptr_t) stack; // SP r[0] = (uint32_t) (uintptr_t) stack; // SP
r[1] = frame[5]; // LR r[1] = frame[5]; // LR
r[2] = frame[6]; // PC r[2] = frame[6]; // PC
@@ -48,6 +48,54 @@ void BusFault_Handler(void) __attribute__((alias("HardFault_Handler")));
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler"))); void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
#endif #endif
// Start a new boot: validate the record, count the boot, store the reset
// reason. The current boot moves to prev_* slots first, so the previous
// boot's uptime and reason stay readable. Called by MG_HEALTH_INIT()
void mg_health_init(void) {
char magic[] = MG_HEALTH_MAGIC;
// struct mg_health *h = mg_health_get();
if (!mg_health_valid()) { // First boot, or RAM lost its contents
memset(&mg_health_record, 0, sizeof(mg_health_record));
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
}
#if 0 && MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
{
// After a crash, copy the backtrace from the core dump in flash: the same
// addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...".
// The core dump stays in flash, so check the reset reason, otherwise an
// old crash is reported on every boot
static esp_core_dump_summary_t s; // Big on RISC-V, keep it off the stack
esp_reset_reason_t rr = esp_reset_reason();
uint32_t *out = mg_health_record.data;
size_t n = 0, max = MG_HEALTH_DATA_SIZE;
if (rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT && rr != ESP_RST_TASK_WDT)
return; // Not a crash that went through the panic handler
if (max == 0 || esp_core_dump_get_summary(&s) != ESP_OK) return;
mg_health_record.cpuid = 42; // ESP32
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
memset(out, 0, sizeof(mg_health_record.data));
out[n++] = s.exc_pc;
#if CONFIG_IDF_TARGET_ARCH_XTENSA
for (uint32_t i = 0; i < s.exc_bt_info.depth && n < max; i++) {
if (s.exc_bt_info.bt[i] != s.exc_pc) out[n++] = s.exc_bt_info.bt[i];
}
#elif CONFIG_IDF_TARGET_ARCH_RISCV
// No unwinder: take the return address, then words on the stack that
// point into executable memory (a heuristic, may include stale values)
if (n < max) out[n++] = s.ex_info.ra;
for (uint32_t i = 0; i + 4 <= s.exc_bt_info.dump_size && n < max; i += 4) {
uint32_t w;
memcpy(&w, &s.exc_bt_info.stackdump[i], sizeof(w)); // Byte array
if (esp_ptr_executable((void *) (uintptr_t) w)) out[n++] = w;
}
#endif
}
#endif
}
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) { static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
size_t n = va_arg(*ap, size_t); size_t n = va_arg(*ap, size_t);
uint32_t *p = va_arg(*ap, uint32_t *); uint32_t *p = va_arg(*ap, uint32_t *);
+13 -16
View File
@@ -9,6 +9,18 @@
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version #define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
#endif #endif
#if !defined(MG_HEALTH_RAM) && (defined(STM32H7_DEV_ID) || defined(STM32H5))
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
#endif
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5))
#define MG_HEALTH_DATA_SIZE 250
#endif
#ifndef MG_HEALTH_DATA_SIZE
#define MG_HEALTH_DATA_SIZE 10
#endif
// Places the record in RAM that survives a reset. Define in mongoose_config.h // Places the record in RAM that survives a reset. Define in mongoose_config.h
// to match the linker script, e.g. __attribute__((section(".mg_health"))) // to match the linker script, e.g. __attribute__((section(".mg_health")))
#ifndef MG_HEALTH_RAM #ifndef MG_HEALTH_RAM
@@ -29,10 +41,6 @@ enum mg_health_reason {
MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack
}; };
#ifndef MG_HEALTH_DATA_SIZE
#define MG_HEALTH_DATA_SIZE 20
#endif
struct mg_health { struct mg_health {
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
uint32_t reset_reason; // enum mg_health_reason that started this boot uint32_t reset_reason; // enum mg_health_reason that started this boot
@@ -68,17 +76,6 @@ static inline const char *mg_health_reason_str(int reason) {
} }
} }
// Start a new boot: validate the record, count the boot, store the reset
// reason. The current boot moves to prev_* slots first, so the previous
// boot's uptime and reason stay readable. Called by MG_HEALTH_INIT()
static inline void mg_health_init(void) {
char magic[] = MG_HEALTH_MAGIC;
// struct mg_health *h = mg_health_get();
if (!mg_health_valid()) { // First boot, or RAM lost its contents
memset(&mg_health_record, 0, sizeof(mg_health_record));
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
}
}
#define MG_HEALTH_INIT() mg_health_init() #define MG_HEALTH_INIT() mg_health_init()
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *); extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
extern void mg_health_init(void);
+4 -30
View File
@@ -7,7 +7,7 @@
#include "util.h" #include "util.h"
#if MG_TLS == MG_TLS_BUILTIN #if MG_TLS == MG_TLS_BUILTIN
#include "tls_uecc.h" #include "tls_p256.h"
#endif #endif
static size_t jwt_split(struct mg_str jwt, struct mg_str *h, struct mg_str *p, static size_t jwt_split(struct mg_str jwt, struct mg_str *h, struct mg_str *p,
@@ -95,39 +95,14 @@ size_t mg_jwt_verify_hs256(struct mg_str jwt, const struct mg_jwt_opts *opts,
} }
#if MG_TLS == MG_TLS_BUILTIN #if MG_TLS == MG_TLS_BUILTIN
typedef struct {
MG_UECC_HashContext uECC;
mg_sha256_ctx sha256;
} jwt_hash_ctx;
static void jwt_hash_init(const MG_UECC_HashContext *ctx) {
jwt_hash_ctx *c = (jwt_hash_ctx *) ctx;
mg_sha256_init(&c->sha256);
}
static void jwt_hash_update(const MG_UECC_HashContext *ctx, const uint8_t *msg,
unsigned len) {
jwt_hash_ctx *c = (jwt_hash_ctx *) ctx;
mg_sha256_update(&c->sha256, msg, len);
}
static void jwt_hash_finish(const MG_UECC_HashContext *ctx, uint8_t *hash) {
jwt_hash_ctx *c = (jwt_hash_ctx *) ctx;
mg_sha256_final(hash, &c->sha256);
}
size_t mg_jwt_sign_es256(const struct mg_jwt_opts *opts, char *buf, size_t mg_jwt_sign_es256(const struct mg_jwt_opts *opts, char *buf,
size_t len) { size_t len) {
uint8_t hash[32], sig[64], tmp[2 * 32 + 64]; uint8_t hash[32], sig[64];
jwt_hash_ctx ctx = {
{jwt_hash_init, jwt_hash_update, jwt_hash_finish, 64, 32, tmp},
{{0}, 0, 0, {0}}};
size_t n = jwt_header_payload("ES256", opts, buf, len); size_t n = jwt_header_payload("ES256", opts, buf, len);
size_t m; size_t m;
if (n == 0 || n + 1 >= len) return 0; if (n == 0 || n + 1 >= len) return 0;
mg_sha256(hash, (uint8_t *) buf, n); mg_sha256(hash, (uint8_t *) buf, n);
if (mg_uecc_sign_deterministic(opts->private_key, hash, sizeof(hash), if (mg_p256_sign(opts->private_key, hash, sizeof(hash), sig) != 1)
&ctx.uECC, sig, mg_uecc_secp256r1()) != 1)
return 0; return 0;
buf[n++] = '.'; buf[n++] = '.';
m = mg_base64url_encode(sig, sizeof(sig), buf + n, len - n); m = mg_base64url_encode(sig, sizeof(sig), buf + n, len - n);
@@ -143,8 +118,7 @@ size_t mg_jwt_verify_es256(struct mg_str jwt, const struct mg_jwt_opts *opts,
if (mg_base64url_decode(s.buf, s.len, (char *) sig, sizeof(sig)) != 64) if (mg_base64url_decode(s.buf, s.len, (char *) sig, sizeof(sig)) != 64)
return 0; return 0;
mg_sha256(hash, (uint8_t *) jwt.buf, n); mg_sha256(hash, (uint8_t *) jwt.buf, n);
if (mg_uecc_verify(opts->public_key, hash, sizeof(hash), sig, if (mg_p256_verify(opts->public_key, hash, sizeof(hash), sig) != 1)
mg_uecc_secp256r1()) != 1)
return 0; return 0;
return mg_base64url_decode(p.buf, p.len, buf, len); return mg_base64url_decode(p.buf, p.len, buf, len);
} }
+25 -10
View File
@@ -56,6 +56,19 @@ extern uint8_t *mg_l2_pppoe_header(struct mg_tcpip_if *, enum mg_l2proto, struct
extern size_t mg_l2_pppoe_trailer(struct mg_tcpip_if *, size_t, uint8_t *); extern size_t mg_l2_pppoe_trailer(struct mg_tcpip_if *, size_t, uint8_t *);
extern bool mg_l2_pppoe_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *); extern bool mg_l2_pppoe_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
extern void mg_l2_lowpan_init(struct mg_tcpip_if *);
extern bool mg_l2_lowpan_poll(struct mg_tcpip_if *, bool);
extern uint8_t *mg_l2_lowpan_header(struct mg_tcpip_if *, enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *);
extern size_t mg_l2_lowpan_trailer(struct mg_tcpip_if *, size_t, uint8_t *);
extern bool mg_l2_lowpan_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
extern struct mg_l2addr *mg_l2_lowpan_getaddr(struct mg_tcpip_if *, uint8_t *);
extern struct mg_l2addr *mg_l2_lowpan_mapip(enum mg_l2addrtype, struct mg_addr *);
#if MG_ENABLE_IPV6
extern bool mg_l2_lowpan_genip6(uint64_t *, uint8_t, struct mg_l2addr *);
extern bool mg_l2_lowpan_ip6get(struct mg_l2addr *, uint8_t *, uint8_t);
extern uint8_t mg_l2_lowpan_ip6put(struct mg_l2addr *, uint8_t *);
#endif
typedef void (*l2_init_fn)(struct mg_tcpip_if *); typedef void (*l2_init_fn)(struct mg_tcpip_if *);
typedef bool (*l2_poll_fn)(struct mg_tcpip_if *, bool); typedef bool (*l2_poll_fn)(struct mg_tcpip_if *, bool);
typedef uint8_t *((*l2_header_fn)(struct mg_tcpip_if *, enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *)); typedef uint8_t *((*l2_header_fn)(struct mg_tcpip_if *, enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *));
@@ -71,25 +84,27 @@ typedef uint8_t (*l2_ip6put_fn)(struct mg_l2addr *, uint8_t *);
// clang-format on // clang-format on
static const l2_init_fn l2_init[] = {mg_l2_eth_init, mg_l2_ppp_init, static const l2_init_fn l2_init[] = {mg_l2_eth_init, mg_l2_ppp_init,
mg_l2_pppoe_init}; mg_l2_pppoe_init, mg_l2_lowpan_init};
static const l2_poll_fn l2_poll[] = {mg_l2_eth_poll, mg_l2_ppp_poll, static const l2_poll_fn l2_poll[] = {mg_l2_eth_poll, mg_l2_ppp_poll,
mg_l2_pppoe_poll}; mg_l2_pppoe_poll, mg_l2_lowpan_poll};
static const l2_header_fn l2_header[] = {mg_l2_eth_header, mg_l2_ppp_header, static const l2_header_fn l2_header[] = {mg_l2_eth_header, mg_l2_ppp_header,
mg_l2_pppoe_header}; mg_l2_pppoe_header,
mg_l2_lowpan_header};
static const l2_trailer_fn l2_trailer[] = {mg_l2_eth_trailer, mg_l2_ppp_trailer, static const l2_trailer_fn l2_trailer[] = {mg_l2_eth_trailer, mg_l2_ppp_trailer,
mg_l2_pppoe_trailer}; mg_l2_pppoe_trailer, mg_l2_lowpan_trailer};
static const l2_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx, mg_l2_pppoe_rx}; static const l2_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx, mg_l2_pppoe_rx,
mg_l2_lowpan_rx};
static const l2_getaddr_fn l2_getaddr[] = {mg_l2_eth_getaddr, mg_l2_ppp_getaddr, static const l2_getaddr_fn l2_getaddr[] = {mg_l2_eth_getaddr, mg_l2_ppp_getaddr,
mg_l2_ppp_getaddr}; mg_l2_ppp_getaddr, mg_l2_lowpan_getaddr};
static const l2_mapip_fn l2_mapip[] = {mg_l2_eth_mapip, mg_l2_ppp_mapip, static const l2_mapip_fn l2_mapip[] = {mg_l2_eth_mapip, mg_l2_ppp_mapip,
mg_l2_ppp_mapip}; mg_l2_ppp_mapip, mg_l2_lowpan_mapip};
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
static const l2_genip6_fn l2_genip6[] = {mg_l2_eth_genip6, mg_l2_ppp_genip6, static const l2_genip6_fn l2_genip6[] = {mg_l2_eth_genip6, mg_l2_ppp_genip6,
mg_l2_ppp_genip6}; mg_l2_ppp_genip6, mg_l2_lowpan_genip6};
static const l2_ip6get_fn l2_ip6get[] = {mg_l2_eth_ip6get, mg_l2_ppp_ip6get, static const l2_ip6get_fn l2_ip6get[] = {mg_l2_eth_ip6get, mg_l2_ppp_ip6get,
mg_l2_ppp_ip6get}; mg_l2_ppp_ip6get, mg_l2_lowpan_ip6get};
static const l2_ip6put_fn l2_ip6put[] = {mg_l2_eth_ip6put, mg_l2_ppp_ip6put, static const l2_ip6put_fn l2_ip6put[] = {mg_l2_eth_ip6put, mg_l2_ppp_ip6put,
mg_l2_ppp_ip6put}; mg_l2_ppp_ip6put, mg_l2_lowpan_ip6put};
#endif #endif
void mg_l2_init(struct mg_tcpip_if *ifp) { void mg_l2_init(struct mg_tcpip_if *ifp) {
+24 -2
View File
@@ -6,7 +6,13 @@
#if MG_ENABLE_TCPIP #if MG_ENABLE_TCPIP
// no config defaults to 0 => Ethernet // no config defaults to 0 => Ethernet
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE, MG_TCPIP_L2_USER }; enum mg_l2type {
MG_TCPIP_L2_ETH = 0,
MG_TCPIP_L2_PPP,
MG_TCPIP_L2_PPPoE,
MG_TCPIP_L2_LOWPAN,
MG_TCPIP_L2_USER
};
#if defined(__DCC__) #if defined(__DCC__)
#pragma pack(1) #pragma pack(1)
@@ -29,10 +35,27 @@ struct eth_data {
struct pppoe_data { // (struct eth_data *) &pppoe_data = pppoedata.eth struct pppoe_data { // (struct eth_data *) &pppoe_data = pppoedata.eth
struct eth_data eth; struct eth_data eth;
}; };
struct lowpan_data {
uint16_t pan_id; // User-set PAN ID
uint8_t channel; // User-set channel, 11..26
uint16_t short_addr; // Our short address, 0xfffe if none
uint8_t seq; // MAC sequence number
uint8_t proto; // Protocol of the frame being sent, enum mg_l2proto
uint8_t dst[8]; // Destination of the frame being sent
uint16_t tag; // Next fragmentation datagram tag
uint8_t *buf; // Reassembly and decompression buffer
uint16_t size; // Datagram being reassembled: size, 0 if none
uint16_t received; // Bytes received so far
uint16_t tag_rx; // Datagram tag
uint8_t src[8]; // Source address
bool udp; // UDP header was compressed
uint64_t expire; // Reassembly deadline, ms
};
union mg_l2data { union mg_l2data {
struct eth_data eth; struct eth_data eth;
struct pppoe_data pppoe; struct pppoe_data pppoe;
struct lowpan_data lowpan;
}; };
#if defined(__DCC__) #if defined(__DCC__)
@@ -41,7 +64,6 @@ union mg_l2data {
#pragma pack(pop) #pragma pack(pop)
#endif #endif
enum mg_l2proto { enum mg_l2proto {
MG_TCPIP_L2PROTO_IPV4 = 0, MG_TCPIP_L2PROTO_IPV4 = 0,
MG_TCPIP_L2PROTO_IPV6, MG_TCPIP_L2PROTO_IPV6,
+551
View File
@@ -0,0 +1,551 @@
#include "l2.h"
#include "log.h"
#include "net.h"
#include "net_builtin.h"
#include "printf.h"
#include "util.h"
// IPv6 over IEEE 802.15.4, 6LoWPAN. RFC 4944: frame format, fragmentation.
// RFC 6282: IPHC header compression, UDP next header compression.
// Driver tx()/rx() carry raw 802.15.4 frames: MAC header, payload, no FCS.
//
// L2 addresses are 8 bytes, struct mg_l2addr. Extended address: EUI-64, big
// endian. Short address S: 0000:00ff:fe00:SSSS, which is also its IPv6
// interface identifier, RFC 6282 3.2.2. Broadcast is short address 0xffff.
// No link-layer security: secured frames are dropped
#if MG_ENABLE_TCPIP && MG_ENABLE_IPV6
#define MGI_LOWPAN_FRAME_MAX 125 // 802.15.4 frame without FCS: 127 - 2
#define MGI_LOWPAN_MTU 1280 // IPv6 minimum MTU, RFC 8200 5
#define MGI_LOWPAN_REASM_MS 5000 // Drop incomplete datagrams after this
#define MGI_FCF_DATA 1U // Frame control: data frame
#define MGI_FCF_SECURITY 0x08U // Frame control: security enabled
#define MGI_FCF_ACK_REQUEST 0x20U // Frame control: acknowledgment request
#define MGI_FCF_PANID_COMP 0x40U // Frame control: PAN ID compression
#define MGI_FCF_IE 0x200U // Frame control: IE present, 2015 frames
#define MGI_FCF_VERSION_2006 0x1000U
#define MGI_AM_SHORT 2U // Addressing mode: 16-bit short address
#define MGI_AM_EXT 3U // Addressing mode: 64-bit extended address
#define MGI_DISPATCH_IPV6 0x41 // Uncompressed IPv6, RFC 4944 5.1
#define MGI_DISPATCH_FRAG1 0xc0 // First fragment, 11000xxx
#define MGI_DISPATCH_FRAGN 0xe0 // Subsequent fragment, 11100xxx
// Parsed 802.15.4 MAC header
struct lowpan_mhr {
uint8_t dst[8], src[8]; // Addresses, struct mg_l2addr format
uint16_t dst_pan; // Destination PAN ID
size_t len; // MAC header length, without security header
bool secured; // Security enabled
};
static struct mg_l2addr s_rxsrc; // Source address returned by getaddr()
static void set_short(uint8_t *a, uint16_t s) {
a[0] = a[1] = a[2] = a[5] = 0, a[3] = 0xff, a[4] = 0xfe;
a[6] = (uint8_t) (s >> 8), a[7] = (uint8_t) s;
}
static bool is_zero(const uint8_t *p, size_t n) {
while (n-- > 0)
if (*p++ != 0) return false;
return true;
}
// Read an address field, over-the-air byte order is little endian
static size_t read_addr(const uint8_t *p, unsigned mode, uint8_t *a) {
int i;
if (mode == MGI_AM_SHORT) {
set_short(a, (uint16_t) (p[0] | (p[1] << 8)));
return 2;
}
for (i = 0; i < 8; i++) a[i] = p[7 - i];
return 8;
}
// Parse MAC header of a data frame, 2003/2006 format
static bool parse_mhr(const uint8_t *f, size_t n, struct lowpan_mhr *h) {
unsigned fcf, dm, sm, ver;
size_t i = 3;
if (n < 3) return false;
fcf = (unsigned) (f[0] | (f[1] << 8));
dm = (fcf >> 10) & 3, sm = (fcf >> 14) & 3, ver = (fcf >> 12) & 3;
if ((fcf & 7) != MGI_FCF_DATA || (fcf & MGI_FCF_IE) || ver > 1) return false;
h->secured = (fcf & MGI_FCF_SECURITY) != 0;
if (dm < MGI_AM_SHORT || sm < MGI_AM_SHORT) return false; // Need both addresses
if (i + 2 > n) return false;
h->dst_pan = (uint16_t) (f[i] | (f[i + 1] << 8)), i += 2;
if (i + (dm == MGI_AM_EXT ? 8 : 2) > n) return false;
i += read_addr(f + i, dm, h->dst);
if ((fcf & MGI_FCF_PANID_COMP) == 0) i += 2; // Source PAN ID, if present
if (i + (sm == MGI_AM_EXT ? 8 : 2) > n) return false;
i += read_addr(f + i, sm, h->src);
h->len = i;
return true;
}
static bool is_short(const uint8_t *a) {
return a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0xff && a[4] == 0xfe &&
a[5] == 0;
}
static bool is_bcast(const uint8_t *a) {
return is_short(a) && a[6] == 0xff && a[7] == 0xff;
}
// IPv6 interface identifier for an L2 address, RFC 6282 3.2.2
static void make_iid(const uint8_t *a, uint8_t *iid) {
memcpy(iid, a, 8);
if (!is_short(a)) iid[0] ^= 2; // EUI-64: flip universal/local bit
}
static size_t write_addr(uint8_t *p, const uint8_t *a) {
int i;
if (is_short(a)) {
p[0] = a[7], p[1] = a[6];
return 2;
}
for (i = 0; i < 8; i++) p[i] = a[7 - i];
return 8;
}
// Write MAC header for a data frame from us to dst. Return its length
static size_t write_mhr(struct mg_tcpip_if *ifp, const uint8_t *dst,
uint8_t *f) {
struct lowpan_data *d = &ifp->l2data.lowpan;
unsigned fcf = MGI_FCF_DATA | MGI_FCF_PANID_COMP | MGI_FCF_VERSION_2006;
size_t i = 3;
if (!is_bcast(dst)) fcf |= MGI_FCF_ACK_REQUEST; // Unicast: MAC retries
fcf |= (is_short(dst) ? MGI_AM_SHORT : MGI_AM_EXT) << 10;
fcf |= (is_short(ifp->mac) ? MGI_AM_SHORT : MGI_AM_EXT) << 14;
f[0] = (uint8_t) fcf, f[1] = (uint8_t) (fcf >> 8), f[2] = d->seq++;
f[i++] = (uint8_t) d->pan_id, f[i++] = (uint8_t) (d->pan_id >> 8);
i += write_addr(f + i, dst);
i += write_addr(f + i, ifp->mac);
return i;
}
// Compress a unicast IPv6 address: 2 bits for the SAM/DAM field, inline
// bytes go to out. RFC 6282 3.1.1, SAC/DAC = 0
static unsigned compress_addr(const uint8_t *ip, const uint8_t *l2,
uint8_t *out, size_t *n) {
static const uint8_t ll[8] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0};
uint8_t iid[8];
if (memcmp(ip, ll, 8) != 0) {
memcpy(out + *n, ip, 16), *n += 16;
return 0;
}
make_iid(l2, iid);
if (memcmp(ip + 8, iid, 8) == 0) return 3; // Derived from L2 address
if (is_short(ip + 8)) { // 0000:00ff:fe00:XXXX
memcpy(out + *n, ip + 14, 2), *n += 2;
return 2;
}
memcpy(out + *n, ip + 8, 8), *n += 8;
return 1;
}
// Compress IPv6 header to IPHC, RFC 6282 3.1. Next header is carried inline.
// Return compressed header length
static size_t iphc_compress(const uint8_t *ip, const uint8_t *src_l2,
const uint8_t *dst_l2, uint8_t *out) {
unsigned tc = (unsigned) (((ip[0] & 0x0f) << 4) | (ip[1] >> 4));
unsigned ecn = tc & 3, dscp = tc >> 2, tf, hl;
uint32_t flow = ((uint32_t) (ip[1] & 0x0f) << 16) |
((uint32_t) ip[2] << 8) | ip[3];
size_t n = 2;
if (tc == 0 && flow == 0) {
tf = 3;
} else if (flow == 0) {
tf = 2, out[n++] = (uint8_t) ((ecn << 6) | dscp);
} else if (dscp == 0) {
tf = 1, out[n++] = (uint8_t) ((ecn << 6) | (flow >> 16));
out[n++] = (uint8_t) (flow >> 8), out[n++] = (uint8_t) flow;
} else {
tf = 0, out[n++] = (uint8_t) ((ecn << 6) | dscp);
out[n++] = (uint8_t) (flow >> 16), out[n++] = (uint8_t) (flow >> 8);
out[n++] = (uint8_t) flow;
}
out[n++] = ip[6]; // Next header, inline
hl = ip[7] == 1 ? 1 : ip[7] == 64 ? 2 : ip[7] == 255 ? 3 : 0;
if (hl == 0) out[n++] = ip[7];
out[0] = (uint8_t) (0x60 | (tf << 3) | hl);
out[1] = 0;
if (is_zero(ip + 8, 16)) {
out[1] |= 0x40; // SAC = 1, SAM = 00: unspecified address
} else {
out[1] |= (uint8_t) (compress_addr(ip + 8, src_l2, out, &n) << 4);
}
if (ip[24] == 0xff) { // Multicast, M = 1
out[1] |= 0x08;
if (ip[25] == 0x02 && is_zero(ip + 26, 13)) { // ff02::00XX
out[1] |= 3, out[n++] = ip[39];
} else if (is_zero(ip + 26, 11)) { // ffXX::00XX:XXXX
out[1] |= 2, out[n++] = ip[25];
memcpy(out + n, ip + 37, 3), n += 3;
} else if (is_zero(ip + 26, 9)) { // ffXX::00XX:XXXX:XXXX
out[1] |= 1, out[n++] = ip[25];
memcpy(out + n, ip + 35, 5), n += 5;
} else {
memcpy(out + n, ip + 24, 16), n += 16;
}
} else {
out[1] |= (uint8_t) compress_addr(ip + 24, dst_l2, out, &n);
}
return n;
}
// Decompress a unicast address. Prefix is the context prefix for SAC/DAC = 1,
// or NULL for link-local. Return inline bytes used
static size_t decompress_addr(unsigned mode, const uint8_t *p, size_t n,
const uint8_t *l2, const uint8_t *prefix,
uint8_t *ip) {
static const size_t sizes[] = {16, 8, 2, 0};
if (sizes[mode] > n) return n + 1; // Truncated: caller sees i > n
if (mode == 0) {
memcpy(ip, p, 16);
} else {
memset(ip, 0, 16), ip[0] = 0xfe, ip[1] = 0x80;
if (prefix != NULL) memcpy(ip, prefix, 8);
if (mode == 1) memcpy(ip + 8, p, 8);
if (mode == 2) set_short(ip + 8, (uint16_t) ((p[0] << 8) | p[1]));
if (mode == 3) make_iid(l2, ip + 8);
}
return sizes[mode];
}
// Decompress IPHC or uncompressed IPv6 header, RFC 6282 3.1, 4.3.
// Write IPv6 header (and UDP header if compressed) to ip. Return its length,
// or 0 on error. Set *used to the number of compressed bytes consumed
static size_t iphc_decompress(const struct lowpan_mhr *h, const uint8_t *p,
size_t n, uint8_t *ip, size_t *used, bool *udp) {
static const size_t tf_sizes[] = {4, 3, 1, 0};
unsigned b0, b1, tf, tc = 0, sam, dam;
uint32_t flow = 0;
size_t i = 2;
*udp = false;
if (n > 0 && p[0] == MGI_DISPATCH_IPV6) {
if (n < 41) return 0;
memcpy(ip, p + 1, 40);
*used = 41;
return 40;
}
if (n < 2 || (p[0] & 0xe0) != 0x60) return 0;
b0 = p[0], b1 = p[1], tf = (b0 >> 3) & 3, sam = (b1 >> 4) & 3, dam = b1 & 3;
if (b1 & 0x80) return 0; // CID: no compression contexts
if (i + tf_sizes[tf] + ((b0 & 4) ? 0 : 1) + ((b0 & 3) ? 0 : 1) > n)
return 0; // TF, next header, hop limit
if (tf == 0 || tf == 2) tc = (unsigned) (((p[i] & 0x3f) << 2) | (p[i] >> 6));
if (tf == 1) tc = p[i] >> 6;
if (tf == 0) flow = ((uint32_t) (p[i + 1] & 0x0f) << 16) |
((uint32_t) p[i + 2] << 8) | p[i + 3];
if (tf == 1) flow = ((uint32_t) (p[i] & 0x0f) << 16) |
((uint32_t) p[i + 1] << 8) | p[i + 2];
i += tf_sizes[tf];
ip[0] = (uint8_t) (0x60 | (tc >> 4));
ip[1] = (uint8_t) ((tc << 4) | (flow >> 16));
ip[2] = (uint8_t) (flow >> 8), ip[3] = (uint8_t) flow;
ip[4] = ip[5] = 0; // Payload length, set by caller
if ((b0 & 4) == 0) ip[6] = p[i++]; // Next header inline
if ((b0 & 3) == 0) ip[7] = p[i++];
if ((b0 & 3) != 0) ip[7] = (b0 & 3) == 1 ? 1 : (b0 & 3) == 2 ? 64 : 255;
// Source address
if ((b1 & 0x40) && sam == 0) { // SAC = 1, SAM = 00: unspecified address
memset(ip + 8, 0, 16);
} else {
if (b1 & 0x40) return 0; // SAC = 1: no compression contexts
i += decompress_addr(sam, p + i, n - i, h->src, NULL, ip + 8);
if (i > n) return 0;
}
// Destination address
if ((b1 & 0x0c) == 0x08) { // Multicast, stateless
static const size_t sizes[] = {16, 6, 4, 1};
if (i + sizes[dam] > n) return 0;
memset(ip + 24, 0, 16), ip[24] = 0xff;
if (dam == 0) memcpy(ip + 24, p + i, 16);
if (dam == 1) ip[25] = p[i], memcpy(ip + 35, p + i + 1, 5);
if (dam == 2) ip[25] = p[i], memcpy(ip + 37, p + i + 1, 3);
if (dam == 3) ip[25] = 2, ip[39] = p[i];
i += sizes[dam];
} else if ((b1 & 0x08) == 0) { // Unicast
if (b1 & 0x04) return 0; // DAC = 1: no compression contexts
i += decompress_addr(dam, p + i, n - i, h->dst, NULL, ip + 24);
if (i > n) return 0;
} else {
return 0; // Stateful multicast is not supported
}
if (b0 & 4) { // Next header compressed: only UDP, RFC 6282 4.3
static const size_t port_sizes[] = {4, 3, 3, 1};
uint8_t *u = ip + 40;
unsigned nhc;
if (i >= n || (p[i] & 0xf8) != 0xf0) return 0;
nhc = p[i++];
if (nhc & 4) return 0; // Elided checksum is not supported
if (i + port_sizes[nhc & 3] + 2 > n) return 0; // Ports, checksum
switch (nhc & 3) {
case 0: memcpy(u, p + i, 4), i += 4; break;
case 1: memcpy(u, p + i, 2), u[2] = 0xf0, u[3] = p[i + 2], i += 3; break;
case 2: u[0] = 0xf0, u[1] = p[i], memcpy(u + 2, p + i + 1, 2), i += 3; break;
default:
u[0] = u[2] = 0xf0, u[1] = (uint8_t) (0xb0 | (p[i] >> 4));
u[3] = (uint8_t) (0xb0 | (p[i] & 0x0f)), i += 1;
break;
}
u[4] = u[5] = 0; // UDP length, set by caller
memcpy(u + 6, p + i, 2), i += 2; // Checksum
ip[6] = 17;
*udp = true;
}
if (i > n) return 0;
*used = i;
return *udp ? 48 : 40;
}
// Set IPv6 payload length and, for compressed UDP, UDP length
static void set_lengths(uint8_t *ip, size_t len, bool udp) {
size_t plen = len - 40;
ip[4] = (uint8_t) (plen >> 8), ip[5] = (uint8_t) plen;
if (udp) ip[44] = (uint8_t) (plen >> 8), ip[45] = (uint8_t) plen;
}
void mg_l2_lowpan_init(struct mg_tcpip_if *ifp) {
struct lowpan_data *d = &ifp->l2data.lowpan;
if (is_zero(ifp->mac, 8)) {
ifp->mac[0] = 0x02; // Locally administered EUI-64
mg_random(&ifp->mac[1], 7);
MG_INFO(("EUI-64 not set. Generated random: %M", mg_print_hex, 8,
ifp->mac));
}
ifp->l2mtu = MGI_LOWPAN_MTU;
ifp->framesize = MGI_LOWPAN_MTU; // TX buffer holds an uncompressed packet
// Polling drivers receive one raw 802.15.4 frame at a time into the queue
// buffer, so it does not need to be framesize: 127 bytes max
if (ifp->recv_queue.size == 0) ifp->recv_queue.size = 128;
d->short_addr = 0xfffe;
d->buf = (uint8_t *) mg_calloc(1, MGI_LOWPAN_MTU);
if (d->buf == NULL) MG_ERROR(("OOM"));
}
bool mg_l2_lowpan_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
struct lowpan_data *d = &ifp->l2data.lowpan;
if (expired_1000ms && d->size != 0 && ifp->now > d->expire) d->size = 0;
return true;
}
uint8_t *mg_l2_lowpan_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
struct mg_l2addr *src, struct mg_l2addr *dst,
uint8_t *frame) {
struct lowpan_data *d = &ifp->l2data.lowpan;
d->proto = (uint8_t) proto;
memcpy(d->dst, dst, sizeof(d->dst));
(void) src;
return frame; // Uncompressed packet starts at the frame start
}
// Compress the packet, send all fragments but the last directly, put the
// last fragment into the TX buffer for the stack to send
size_t mg_l2_lowpan_trailer(struct mg_tcpip_if *ifp, size_t len,
uint8_t *cur) {
struct lowpan_data *d = &ifp->l2data.lowpan;
uint8_t *ip = cur - len, hdr[48], f[MGI_LOWPAN_FRAME_MAX];
const uint8_t *dst = d->dst;
size_t hlen, mlen, avail, chunk, off, n;
if (d->proto != MG_TCPIP_L2PROTO_IPV6 || len < 40 || len > MGI_LOWPAN_MTU)
return 0;
hlen = iphc_compress(ip, ifp->mac, dst, hdr);
mlen = write_mhr(ifp, dst, f);
avail = MGI_LOWPAN_FRAME_MAX - mlen;
if (hlen + len - 40 <= avail) { // Fits into one frame
memcpy(f + mlen, hdr, hlen);
memcpy(f + mlen + hlen, ip + 40, len - 40);
n = mlen + hlen + len - 40;
} else { // Fragment, RFC 4944 5.3. Offsets are in uncompressed bytes
uint16_t tag = d->tag++;
chunk = (avail - 4 - hlen) & ~(size_t) 7; // 40 + chunk: multiple of 8
f[mlen] = (uint8_t) (MGI_DISPATCH_FRAG1 | (len >> 8));
f[mlen + 1] = (uint8_t) len;
f[mlen + 2] = (uint8_t) (tag >> 8), f[mlen + 3] = (uint8_t) tag;
memcpy(f + mlen + 4, hdr, hlen);
memcpy(f + mlen + 4 + hlen, ip + 40, chunk);
n = mlen + 4 + hlen + chunk;
ifp->driver->tx(f, n, ifp);
for (off = 40 + chunk;;) {
mlen = write_mhr(ifp, dst, f);
chunk = len - off;
if (chunk > ((avail - 5) & ~(size_t) 7)) chunk = (avail - 5) & ~(size_t) 7;
f[mlen] = (uint8_t) (MGI_DISPATCH_FRAGN | (len >> 8));
f[mlen + 1] = (uint8_t) len;
f[mlen + 2] = (uint8_t) (tag >> 8), f[mlen + 3] = (uint8_t) tag;
f[mlen + 4] = (uint8_t) (off / 8);
memcpy(f + mlen + 5, ip + off, chunk);
n = mlen + 5 + chunk;
off += chunk;
if (off >= len) break;
ifp->driver->tx(f, n, ifp);
}
}
memcpy(ifp->tx.buf, f, n);
return n;
}
bool mg_l2_lowpan_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
struct mg_str *pay, struct mg_str *raw) {
struct lowpan_data *d = &ifp->l2data.lowpan;
uint8_t *p = (uint8_t *) raw->buf;
size_t n = raw->len, hlen, used, size, off;
struct lowpan_mhr h;
bool udp;
if (d->buf == NULL || !parse_mhr(p, n, &h)) return false;
if (h.dst_pan != d->pan_id && h.dst_pan != 0xffff) return false;
if (!is_bcast(h.dst) && memcmp(h.dst, ifp->mac, 8) != 0 &&
!(is_short(h.dst) && d->short_addr != 0xfffe &&
MG_LOAD_BE16(h.dst + 6) == d->short_addr))
return false;
if (h.secured) return false; // No link-layer security
p += h.len, n -= h.len;
if (n < 1) return false;
if ((p[0] & 0xf8) == MGI_DISPATCH_FRAG1 || (p[0] & 0xf8) == MGI_DISPATCH_FRAGN) {
bool first = (p[0] & 0xf8) == MGI_DISPATCH_FRAG1;
uint16_t tag;
if (n < (first ? 4U : 5U)) return false;
size = (size_t) (((p[0] & 7) << 8) | p[1]);
tag = (uint16_t) ((p[2] << 8) | p[3]);
if (size < 40 || size > MGI_LOWPAN_MTU) return false;
if (first) {
p += 4, n -= 4;
hlen = iphc_decompress(&h, p, n, d->buf, &used, &udp);
if (hlen == 0 || hlen + n - used > size) return false;
memcpy(d->buf + hlen, p + used, n - used);
d->size = (uint16_t) size, d->tag_rx = tag, d->udp = udp;
d->received = (uint16_t) (hlen + n - used);
memcpy(d->src, h.src, sizeof(d->src));
d->expire = ifp->now + MGI_LOWPAN_REASM_MS;
} else {
off = (size_t) p[4] * 8;
p += 5, n -= 5;
if (d->size != size || d->tag_rx != tag ||
memcmp(d->src, h.src, sizeof(d->src)) != 0 || off + n > size)
return false; // Not the datagram being reassembled
memcpy(d->buf + off, p, n);
d->received = (uint16_t) (d->received + n);
}
if (d->size == 0 || d->received < d->size) return false;
set_lengths(d->buf, d->size, d->udp);
pay->buf = (char *) d->buf, pay->len = d->size;
d->size = 0;
} else {
hlen = iphc_decompress(&h, p, n, d->buf, &used, &udp);
if (hlen == 0 || hlen + n - used > MGI_LOWPAN_MTU) return false;
memcpy(d->buf + hlen, p + used, n - used);
set_lengths(d->buf, hlen + n - used, udp);
pay->buf = (char *) d->buf, pay->len = hlen + n - used;
}
*proto = MG_TCPIP_L2PROTO_IPV6;
return true;
}
struct mg_l2addr *mg_l2_lowpan_getaddr(struct mg_tcpip_if *ifp,
uint8_t *frame) {
struct lowpan_mhr h;
memset(&s_rxsrc, 0, sizeof(s_rxsrc));
if (parse_mhr(frame, MGI_LOWPAN_FRAME_MAX, &h))
memcpy(&s_rxsrc, h.src, sizeof(s_rxsrc));
(void) ifp;
return &s_rxsrc;
}
extern struct mg_l2addr s_mapip;
// All broadcast and multicast goes to the 802.15.4 broadcast address
struct mg_l2addr *mg_l2_lowpan_mapip(enum mg_l2addrtype addrtype,
struct mg_addr *addr) {
set_short((uint8_t *) &s_mapip, 0xffff);
(void) addrtype, (void) addr;
return &s_mapip;
}
bool mg_l2_lowpan_genip6(uint64_t *ip6, uint8_t prefix_len,
struct mg_l2addr *l2addr) {
if (prefix_len > 64) {
MG_ERROR(("Prefix length > 64, UNSUPPORTED"));
return false;
}
ip6[0] = 0;
make_iid((uint8_t *) l2addr, (uint8_t *) &ip6[1]); // RFC 6282 3.2.2
return true;
}
// Link-layer address of a neighbour from its IPv6 address: its interface ID
// is made from it, RFC 6282 3.2.2. make_iid() is its own inverse
void mg_l2_lowpan_ip6l2(const uint64_t *ip6, uint8_t *l2) {
make_iid((const uint8_t *) &ip6[1], l2);
}
// Link-layer address option, RFC 4944 8: length 2 for EUI-64, 1 for short
bool mg_l2_lowpan_ip6get(struct mg_l2addr *l2addr, uint8_t *opts,
uint8_t len) {
if (len == 2) {
memcpy(l2addr, opts, 8);
} else if (len == 1) {
set_short((uint8_t *) l2addr, (uint16_t) ((opts[0] << 8) | opts[1]));
} else {
return false;
}
return true;
}
uint8_t mg_l2_lowpan_ip6put(struct mg_l2addr *l2addr, uint8_t *opts) {
uint8_t *a = (uint8_t *) l2addr;
if (is_short(a)) {
opts[0] = a[6], opts[1] = a[7];
return 1;
}
memcpy(opts, a, 8);
return 2;
}
#elif MG_ENABLE_TCPIP
// No IPv6: 6LoWPAN is not available. Stubs for the L2 dispatch tables
extern struct mg_l2addr s_mapip;
void mg_l2_lowpan_init(struct mg_tcpip_if *ifp) {
MG_ERROR(("6LoWPAN needs MG_ENABLE_IPV6"));
(void) ifp;
}
bool mg_l2_lowpan_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
(void) ifp, (void) expired_1000ms;
return false; // Link is never up
}
uint8_t *mg_l2_lowpan_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
struct mg_l2addr *src, struct mg_l2addr *dst,
uint8_t *frame) {
(void) ifp, (void) proto, (void) src, (void) dst;
return frame;
}
size_t mg_l2_lowpan_trailer(struct mg_tcpip_if *ifp, size_t len,
uint8_t *cur) {
(void) ifp, (void) len, (void) cur;
return 0; // Nothing to send
}
bool mg_l2_lowpan_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
struct mg_str *pay, struct mg_str *raw) {
(void) ifp, (void) proto, (void) pay, (void) raw;
return false;
}
struct mg_l2addr *mg_l2_lowpan_getaddr(struct mg_tcpip_if *ifp,
uint8_t *frame) {
(void) ifp, (void) frame;
return &s_mapip;
}
struct mg_l2addr *mg_l2_lowpan_mapip(enum mg_l2addrtype addrtype,
struct mg_addr *addr) {
(void) addrtype, (void) addr;
return &s_mapip;
}
#endif
+8 -1
View File
@@ -156,14 +156,21 @@ void mg_mdash_init(struct mg_mgr *mgr) {
} }
void mg_mdash_poll(struct mg_mgr *mgr) { void mg_mdash_poll(struct mg_mgr *mgr) {
#if MG_TLS == MG_TLS_NONE
(void) mgr, (void) mg_mdash_fn; // mdash needs wss://, nothing to do
#else
static uint64_t timer = 0; static uint64_t timer = 0;
if (mg_mdash_conn == NULL && mg_timer_expired(&timer, 1000, mg_millis())) { // No RPC handlers means mdash was never set up, or mg_mdash_free() ran.
// No key means mdash would reject us, so do not connect at all
if (mg_mdash_conn == NULL && mgr->rpcs != NULL && MG_MDASH_KEY[0] != '\0' &&
mg_timer_expired(&timer, 1000, mg_millis())) {
MG_INFO(("Connecting to %s", MG_MDASH_URL)); MG_INFO(("Connecting to %s", MG_MDASH_URL));
mg_mdash_conn = mg_ws_connect(mgr, MG_MDASH_URL, mg_mdash_fn, NULL, mg_mdash_conn = mg_ws_connect(mgr, MG_MDASH_URL, mg_mdash_fn, NULL,
"Origin: %s\r\n" "Origin: %s\r\n"
"Authorization: Bearer %s\r\n", "Authorization: Bearer %s\r\n",
MG_MDASH_URL, MG_MDASH_KEY); MG_MDASH_URL, MG_MDASH_KEY);
} }
#endif
} }
void mg_mdash_free(struct mg_mgr *mgr) { void mg_mdash_free(struct mg_mgr *mgr) {
+10
View File
@@ -438,6 +438,16 @@ int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version,
case MQTT_CMD_CONNACK: case MQTT_CMD_CONNACK:
if (end - p < 2) return MQTT_MALFORMED; if (end - p < 2) return MQTT_MALFORMED;
m->ack = p[1]; m->ack = p[1];
p += 2;
if (version == 5) {
if (p >= end) return MQTT_MALFORMED;
len_len = (uint32_t) decode_varint(p, (size_t) (end - p), &tmp);
if (!len_len) return MQTT_MALFORMED;
m->props_size = (size_t) tmp;
m->props_start = (size_t) (p + len_len - buf);
p += len_len + m->props_size;
if (p > end) return MQTT_MALFORMED;
}
break; break;
case MQTT_CMD_PUBACK: case MQTT_CMD_PUBACK:
case MQTT_CMD_PUBREC: case MQTT_CMD_PUBREC:
+9 -3
View File
@@ -211,7 +211,13 @@ struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url,
if (url == NULL || url[0] == '\0') { if (url == NULL || url[0] == '\0') {
MG_ERROR(("null url")); MG_ERROR(("null url"));
#if MG_ENABLE_TCPIP #if MG_ENABLE_TCPIP
} else if (mgr->ifp != NULL && mgr->ifp->state != MG_TCPIP_STATE_READY) { } else if (mgr->ifp != NULL && mgr->ifp->state != MG_TCPIP_STATE_READY
#if MG_ENABLE_IPV6
// IPv6-only links: a link-local address is enough for local peers
&& (mgr->ifp->state6 < MG_TCPIP_STATE_UP ||
mgr->ifp->ip6ll[0] == 0)
#endif
) {
MG_ERROR(("Network is down")); MG_ERROR(("Network is down"));
#endif #endif
} else if ((c = mg_alloc_conn(mgr)) == NULL) { } else if ((c = mg_alloc_conn(mgr)) == NULL) {
@@ -299,10 +305,10 @@ void mg_mgr_free(struct mg_mgr *mgr) {
while (t != NULL) tmp = t->next, mg_free(t), t = tmp; while (t != NULL) tmp = t->next, mg_free(t), t = tmp;
mgr->timers = NULL; // Important. Next call to poll won't touch timers mgr->timers = NULL; // Important. Next call to poll won't touch timers
for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1; for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
mg_mgr_poll(mgr, 0);
#if MG_ENABLE_MDASH #if MG_ENABLE_MDASH
mg_mdash_free(mgr); mg_mdash_free(mgr); // Before the final poll, so mdash does not reconnect
#endif #endif
mg_mgr_poll(mgr, 0);
#if MG_ENABLE_FREERTOS_TCP #if MG_ENABLE_FREERTOS_TCP
FreeRTOS_DeleteSocketSet(mgr->ss); FreeRTOS_DeleteSocketSet(mgr->ss);
#endif #endif
+2 -1
View File
@@ -92,11 +92,12 @@ struct mg_connection {
unsigned is_mqtt5 : 1; // MQTT5 connection (set by mg_mqtt_login) unsigned is_mqtt5 : 1; // MQTT5 connection (set by mg_mqtt_login)
unsigned is_hexdumping : 1; // Log hex dump of all in/out traffic unsigned is_hexdumping : 1; // Log hex dump of all in/out traffic
unsigned is_draining : 1; // Flush send buffer, then close unsigned is_draining : 1; // Flush send buffer, then close
unsigned is_closing : 1; // Close and free immediately on next poll unsigned is_closing : 1; // Request Close and free on next poll
unsigned is_full : 1; // Pause incoming reads until cleared unsigned is_full : 1; // Pause incoming reads until cleared
unsigned is_tls_throttled : 1; // TLS write was throttled; retry pending unsigned is_tls_throttled : 1; // TLS write was throttled; retry pending
unsigned is_readable : 1; // Socket is ready to read (epoll/select) unsigned is_readable : 1; // Socket is ready to read (epoll/select)
unsigned is_writable : 1; // Socket is ready to write (epoll/select) unsigned is_writable : 1; // Socket is ready to write (epoll/select)
unsigned is_bsd : 1; // Built-in TCP/IP stack BSD socket
}; };
// Runs one iteration of the event loop. // Runs one iteration of the event loop.
+409 -115
View File
@@ -1,5 +1,7 @@
#include "net_builtin.h" #include "net_builtin.h"
#include "bsd.h"
#include "profile.h" #include "profile.h"
#include "url.h"
#if MG_ENABLE_TCPIP #if MG_ENABLE_TCPIP
#define MG_EPHEMERAL_PORT_BASE 32768 #define MG_EPHEMERAL_PORT_BASE 32768
@@ -31,16 +33,16 @@
#endif #endif
struct connstate { struct connstate {
uint32_t seq, ack; // TCP seq/ack counters uint32_t seq, ack; // TCP seq/ack counters
uint64_t timer; // TCP timer (see 'ttype' below) uint64_t timer; // TCP timer (see 'ttype' below)
uint64_t txq_timer; // TCP retransmission timer (RFC-6298, 5) uint64_t txq_timer; // TCP retransmission timer (RFC-6298, 5)
uint32_t acked; // Last ACK-ed number uint32_t acked; // Last ACK-ed number
size_t unacked; // Not acked bytes size_t unacked; // Not acked bytes
uint32_t maxseq; // Max send seq (ack + window) uint32_t maxseq; // Max send seq (ack + window)
uint32_t txq_seq; // Sequence of first txq record (RFC-9293, 3.8) uint32_t txq_seq; // Sequence of first txq record (RFC-9293, 3.8)
uint32_t txq_una; // Oldest unacknowledged sequence (RFC-9293, 3.4) uint32_t txq_una; // Oldest unacknowledged sequence (RFC-9293, 3.4)
uint16_t win; // destination current window size uint16_t win; // destination current window size
uint16_t dmss; // destination MSS (from TCP opts) uint16_t dmss; // destination MSS (from TCP opts)
uint8_t mac[sizeof(struct mg_l2addr)]; // Peer hw address uint8_t mac[sizeof(struct mg_l2addr)]; // Peer hw address
uint8_t ttype; // Timer type: uint8_t ttype; // Timer type:
#define MIP_TTYPE_KEEPALIVE 0 // Connection is idle for long, send keepalive #define MIP_TTYPE_KEEPALIVE 0 // Connection is idle for long, send keepalive
@@ -54,6 +56,8 @@ struct connstate {
bool twclosure; // 3-way closure done bool twclosure; // 3-way closure done
bool retransmit; // Retain sent data until acknowledged bool retransmit; // Retain sent data until acknowledged
bool is_full; // Advertise a zero receive window bool is_full; // Advertise a zero receive window
bool is_closing; // Finished, remove connection (TCP after FIN)
bool send_rst; // Send RST before removing a TCP connection
struct mg_iobuf txq; // Length-prefixed sent TCP segments (RFC-9293, 3.8) struct mg_iobuf txq; // Length-prefixed sent TCP segments (RFC-9293, 3.8)
}; };
@@ -310,6 +314,8 @@ static bool tcpcsum_ok(void *d, void *t) {
0); 0);
} }
#define MG_IPISLOCAL(i, a) ((a & i->mask) == (i->ip & i->mask))
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
static uint16_t p6csum(void *d, void *p, uint8_t proto, size_t plen) { static uint16_t p6csum(void *d, void *p, uint8_t proto, size_t plen) {
uint32_t sum; uint32_t sum;
@@ -418,12 +424,13 @@ static void onstatechange(struct mg_tcpip_if *ifp) {
ifp->is_ip_changed = false; ifp->is_ip_changed = false;
} }
} else if (ifp->state == MG_TCPIP_STATE_IP) { } else if (ifp->state == MG_TCPIP_STATE_IP) {
if (ifp->gw != 0 && ifp->l2type == MG_TCPIP_L2_ETH) if (ifp->l2type == MG_TCPIP_L2_ETH) {
mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request ifp->gw_ready = false; // remove any prior state
if (ifp->gw != 0) // send an unsolicited GW ARP request
mg_tcpip_arp_request(ifp, ifp->gw, NULL);
}
} else if (ifp->state == MG_TCPIP_STATE_UP) { } else if (ifp->state == MG_TCPIP_STATE_UP) {
srand((unsigned int) mg_millis()); srand((unsigned int) mg_millis());
} else if (ifp->state == MG_TCPIP_STATE_DOWN) {
MG_ERROR(("Link down"));
} }
mg_tcpip_call(ifp, MG_TCPIP_EV_STATE_CHANGE, &ifp->state); mg_tcpip_call(ifp, MG_TCPIP_EV_STATE_CHANGE, &ifp->state);
} }
@@ -672,6 +679,7 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
} }
static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip); static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip);
static void conndns4(struct mg_tcpip_if *ifp, uint32_t ip);
static bool dhcp_opt_len_ok(uint8_t len, uint8_t *p, uint8_t *end) { static bool dhcp_opt_len_ok(uint8_t len, uint8_t *p, uint8_t *end) {
return (len >= 4 && (len & 3) == 0 && p + 6 < end); return (len >= 4 && (len & 3) == 0 && p + 6 < end);
@@ -732,16 +740,15 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
uint64_t rand; uint64_t rand;
ifp->lease_expire = ifp->now + (uint64_t) lease * 1000; ifp->lease_expire = ifp->now + (uint64_t) lease * 1000;
MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000));
// assume DHCP server = router until ARP resolves
memcpy(ifp->gwmac, mg_l2_getaddr(ifp, pkt->l2), sizeof(ifp->gwmac));
ifp->gw_ready = true; // NOTE(): actual gw ARP won't retry now
if (ifp->ip != ip) ifp->is_ip_changed = true; if (ifp->ip != ip) ifp->is_ip_changed = true;
ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; ifp->ip = ip, ifp->gw = gw, ifp->mask = mask;
ifp->state = MG_TCPIP_STATE_IP; // BOUND state ifp->state = MG_TCPIP_STATE_IP; // BOUND state
mg_random(&rand, sizeof(rand)); mg_random(&rand, sizeof(rand));
srand((unsigned int) (rand + mg_millis())); srand((unsigned int) (rand + mg_millis()));
if (ifp->enable_req_dns && dns != 0) { if (ifp->enable_req_dns && dns != 0) {
setdns4(ifp, &dns); setdns4(ifp, &dns); // configure the provided DNS
if (MG_IPISLOCAL(ifp, dns)) // connect to a local DNS at this point
conndns4(ifp, dns);
mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_DNS, &dns); mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_DNS, &dns);
} }
if (ifp->enable_req_sntp && sntp != 0) if (ifp->enable_req_sntp && sntp != 0)
@@ -792,7 +799,7 @@ static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
res.xid = pkt->dhcp->xid; res.xid = pkt->dhcp->xid;
mac = mg_l2_getaddr(ifp, pkt->l2); mac = mg_l2_getaddr(ifp, pkt->l2);
if (ifp->enable_get_gateway) { if (ifp->enable_get_gateway) {
ifp->gw = res.yiaddr; // set gw IP, best-effort gwmac as DHCP server's ifp->gw = res.yiaddr; // set gw IP, and MAC
memcpy(ifp->gwmac, mac, sizeof(ifp->gwmac)); memcpy(ifp->gwmac, mac, sizeof(ifp->gwmac));
} }
tx_udp4(ifp, mac, ifp->ip, mg_htons(67), op == 1 ? ~0U : res.yiaddr, tx_udp4(ifp, mac, ifp->ip, mg_htons(67), op == 1 ? ~0U : res.yiaddr,
@@ -809,7 +816,7 @@ static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
struct ip6 *ip6 = (struct ip6 *) mg_l2_header(ifp, MG_TCPIP_L2PROTO_IPV6, struct ip6 *ip6 = (struct ip6 *) mg_l2_header(ifp, MG_TCPIP_L2PROTO_IPV6,
ifp->mac, l2_dst, l2p); ifp->mac, l2_dst, l2p);
memset(ip6, 0, sizeof(*ip6)); memset(ip6, 0, sizeof(*ip6));
ip6->ver = (uint8_t) (0x60 | (dscp >> 2)); // Version 6, traffic class ip6->ver = (uint8_t) (0x60 | (dscp >> 2)); // Version 6, traffic class
ip6->label[0] = (uint8_t) (dscp << 6); ip6->label[0] = (uint8_t) (dscp << 6);
ip6->plen = mg_htons((uint16_t) plen); ip6->plen = mg_htons((uint16_t) plen);
ip6->next = next; ip6->next = next;
@@ -963,7 +970,7 @@ static void tx_ndp_rs(struct mg_tcpip_if *ifp) {
(struct mg_addr *) &ip6_allrouters), (struct mg_addr *) &ip6_allrouters),
ifp->ip6ll, (uint64_t *) ip6_allrouters.addr.ip6, 133, 0, payload, ifp->ip6ll, (uint64_t *) ip6_allrouters.addr.ip6, 133, 0, payload,
payload_len); payload_len);
MG_DEBUG(("NDP Router Solicitation sent")); MG_VERBOSE(("NDP Router Solicitation sent"));
} }
static void fill_prefix(uint8_t *dst, uint8_t *src, uint8_t len) { static void fill_prefix(uint8_t *dst, uint8_t *src, uint8_t len) {
@@ -1002,9 +1009,10 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
uint8_t *opts = (uint8_t *) (ra + 1); uint8_t *opts = (uint8_t *) (ra + 1);
size_t opt_left = pkt->pay.len - sizeof(*ra); size_t opt_left = pkt->pay.len - sizeof(*ra);
bool gotl2addr = false, gotprefix = false, changed = false; bool gotl2addr = false, gotprefix = false, changed = false;
bool ip6only = ifp->ip == 0 && !ifp->enable_dhcp_client;
uint8_t l2[sizeof(struct mg_l2addr)]; uint8_t l2[sizeof(struct mg_l2addr)];
uint32_t mtu = 0; uint32_t mtu = 0;
uint8_t *prefix = NULL, prefix_len = 0; uint8_t *prefix = NULL, prefix_len = 0, *dns = NULL;
if (pkt->pay.len < sizeof(*ra)) return; if (pkt->pay.len < sizeof(*ra)) return;
if (ifp->state6 == MG_TCPIP_STATE_UP) { if (ifp->state6 == MG_TCPIP_STATE_UP) {
@@ -1040,15 +1048,26 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (prefix_len != ifp->prefix_len || if (prefix_len != ifp->prefix_len ||
!match_prefix(prefix, ifp->prefix, ifp->prefix_len)) !match_prefix(prefix, ifp->prefix, ifp->prefix_len))
changed = true; changed = true;
} else if (type == 38 && length >= 16 && ip6only && (opts[3] & 7) == 0) {
memcpy(ifp->nat64, opts + 4, sizeof(ifp->nat64)); // RFC-8781, /96
} else if (type == 25 && length >= 24 && ip6only) {
dns = opts + 8; // DNS server, RFC-8106
} }
opts += length; opts += length;
opt_left -= length; opt_left -= length;
} }
if (dns != NULL && ifp->dns6_url == NULL &&
(ifp->dns6_url = (char *) mg_calloc(1, 64)) != NULL) { // First wins
mg_snprintf(ifp->dns6_url, 64, "udp://%M:53", mg_print_ip6, dns);
ifp->mgr->dns6.url = ifp->dns6_url, ifp->mgr->use_dns6 = true;
if (ifp->mgr->dns6.c != NULL) mg_close_conn(ifp->mgr->dns6.c);
}
// fill prefix and global // fill prefix and global
if (gotprefix && !fill_global(ifp, prefix, prefix_len)) return; if (gotprefix && !fill_global(ifp, prefix, prefix_len)) return;
if (changed) ifp->is_ip6_changed = true; if (changed) ifp->is_ip6_changed = true;
ifp->gw6[0] = pkt->ip6->src[0], ifp->gw6[1] = pkt->ip6->src[1]; ifp->gw6[0] = pkt->ip6->src[0], ifp->gw6[1] = pkt->ip6->src[1];
ifp->gw6_ready = false; // Remove any prior state
if (gotl2addr) memcpy(ifp->gw6mac, l2, sizeof(ifp->gw6mac)); if (gotl2addr) memcpy(ifp->gw6mac, l2, sizeof(ifp->gw6mac));
if (gotl2addr || ifp->l2type == MG_TCPIP_L2_PPP || if (gotl2addr || ifp->l2type == MG_TCPIP_L2_PPP ||
ifp->l2type == MG_TCPIP_L2_PPPoE) { ifp->l2type == MG_TCPIP_L2_PPPoE) {
@@ -1063,14 +1082,214 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
mtu)); mtu));
ifp->mtu = (uint16_t) mtu; ifp->mtu = (uint16_t) mtu;
} }
if (ifp->state6 != MG_TCPIP_STATE_READY) { if (ifp->state6 != MG_TCPIP_STATE_READY)
tx_ndp_ns(ifp, ifp->gw6, NULL); // unsolicited GW hwaddr resolution
ifp->state6 = MG_TCPIP_STATE_IP; ifp->state6 = MG_TCPIP_STATE_IP;
}
onstate6change(ifp); onstate6change(ifp);
} }
} }
// Routing between two interfaces, see mg_tcpip_route()
void mg_l2_lowpan_ip6l2(const uint64_t *ip6, uint8_t *l2);
// Interface with a user-set IPv6 prefix, which the router advertises
static bool has_static_prefix(struct mg_tcpip_if *ifp) {
return ifp->route != NULL && !ifp->enable_slaac && ifp->prefix_len > 0 &&
ifp->prefix_len <= 64 && ifp->ip6[0] != 0;
}
// Router Advertisement, 4.2: our static prefix (on-link, SLAAC, we are the
// default router), and a route to the other interface's static prefix
static void tx_ndp_ra(struct mg_tcpip_if *ifp) {
struct mg_tcpip_if *peer = ifp->route;
uint8_t buf[sizeof(struct ndp_ra) + 16 + 32 + 16 + 16 + 24], *o;
struct ndp_ra *ra = (struct ndp_ra *) buf;
bool own = has_static_prefix(ifp), other = has_static_prefix(peer);
bool nat64 = own && (ifp->nat64[0] | ifp->nat64[1]) != 0; // Prefix set
size_t n = sizeof(*ra);
if (!own && !other) return;
memset(buf, 0, sizeof(buf));
ra->cur_hop_limit = 64;
ra->router_lifetime = mg_htons(own ? 1800 : 0);
buf[n] = 1; // Source link-layer address, 4.6.1
buf[n + 1] = mg_l2_ip6put(ifp->l2type, ifp->mac, buf + n + 2);
n += (size_t) (8 * buf[n + 1]);
if (own) { // Prefix information, 4.6.2
o = buf + n, n += 32;
o[0] = 3, o[1] = 4, o[2] = ifp->prefix_len, o[3] = 0xc0; // L, A
MG_STORE_BE32(o + 4, 86400); // Valid lifetime
MG_STORE_BE32(o + 8, 14400); // Preferred lifetime
memcpy(o + 16, ifp->prefix, sizeof(ifp->prefix));
}
if (other) { // Route information, RFC-4191 2.3
o = buf + n, n += 16;
o[0] = 24, o[1] = 2, o[2] = peer->prefix_len;
MG_STORE_BE32(o + 4, 1800); // Route lifetime
memcpy(o + 8, peer->prefix, sizeof(peer->prefix));
}
if (nat64) { // We translate to IPv4: NAT64 prefix and a DNS server
struct mg_addr a;
o = buf + n, n += 16; // NAT64 prefix, RFC-8781
o[0] = 38, o[1] = 2;
MG_STORE_BE16(o + 2, (1800 / 8) << 3); // Lifetime in 8s units, PLC 0: /96
memcpy(o + 4, ifp->nat64, sizeof(ifp->nat64));
o = buf + n, n += 24; // DNS server, RFC-8106: NAT64 prefix + IPv4 DNS
o[0] = 25, o[1] = 3;
MG_STORE_BE32(o + 4, 1800); // Lifetime
memcpy(o + 8, ifp->nat64, sizeof(ifp->nat64));
if (!mg_aton(mg_url_host(peer->mgr->dns4.url), &a) || a.is_ip6)
a.addr.ip4 = MG_U32(8, 8, 8, 8);
memcpy(o + 20, &a.addr.ip4, 4);
}
tx_icmp6(ifp,
mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_MCAST6,
(struct mg_addr *) &ip6_allnodes),
ifp->ip6ll, (uint64_t *) ip6_allnodes.addr.ip6, 134, 0, buf, n);
}
// Remember the hardware address of a sender, to forward replies to it
static void nb6_learn(struct mg_tcpip_if *ifp, uint64_t *ip, uint8_t *l2) {
size_t i, n = sizeof(ifp->nb6) / sizeof(ifp->nb6[0]);
for (i = 0; i < n && !MG_IP6MATCH(ifp->nb6[i].ip, ip); i++) (void) 0;
if (i == n) i = ifp->nb6_next, ifp->nb6_next = (uint8_t) ((i + 1) % n);
ifp->nb6[i].ip[0] = ip[0], ifp->nb6[i].ip[1] = ip[1];
memcpy(ifp->nb6[i].l2, l2, sizeof(ifp->nb6[i].l2));
}
// Hardware address to send a forwarded packet to: a learned neighbour, the
// 6LoWPAN address made from the interface ID, or our default router
static bool nb6_l2(struct mg_tcpip_if *ifp, uint64_t *ip, uint8_t *l2) {
size_t i, n = sizeof(ifp->nb6) / sizeof(ifp->nb6[0]);
for (i = 0; i < n && !MG_IP6MATCH(ifp->nb6[i].ip, ip); i++) (void) 0;
if (i < n) {
memcpy(l2, ifp->nb6[i].l2, sizeof(ifp->nb6[i].l2));
} else if (ifp->l2type == MG_TCPIP_L2_LOWPAN) {
mg_l2_lowpan_ip6l2(ip, l2);
} else if (ifp->gw6_ready) {
memcpy(l2, ifp->gw6mac, sizeof(ifp->gw6mac));
} else {
return false;
}
return true;
}
// NAT64, RFC-6146: translate a TCP or UDP packet sent to our nat64 prefix to
// IPv4, send it from the other interface. Sessions are kept in ifp->nat64s,
// when it is full the oldest one is replaced
static void nat64_out(struct mg_tcpip_if *ifp, struct pkt *pkt, size_t len) {
struct mg_tcpip_if *out = ifp->route;
struct mg_tcpip_nat64 *e = NULL;
struct ip *ip;
uint64_t src[2], dst[2];
uint8_t *p, next = pkt->ip6->next;
size_t i, n = sizeof(ifp->nat64s) / sizeof(ifp->nat64s[0]);
size_t plen = len - sizeof(*pkt->ip6);
struct tcp *tcp = (struct tcp *) (pkt->ip6 + 1);
struct udp *udp = (struct udp *) tcp; // Ports are at the same place
uint32_t dst4;
if ((next != 6 && next != 17) ||
plen < (next == 6 ? sizeof(*tcp) : sizeof(*udp)) ||
sizeof(*ip) + plen > out->mtu || out->state != MG_TCPIP_STATE_READY)
return;
src[0] = pkt->ip6->src[0], src[1] = pkt->ip6->src[1];
dst[0] = pkt->ip6->dst[0], dst[1] = pkt->ip6->dst[1];
memcpy(&dst4, (uint8_t *) dst + 12, sizeof(dst4)); // RFC-6052, /96
for (i = 0; i < n; i++) {
e = &ifp->nat64s[i];
if (e->proto == next && e->port6 == udp->sport &&
MG_IP6MATCH(e->ip6, src) && e->port4 == udp->dport && e->ip4 == dst4)
break;
}
if (i == n) { // New session
e = &ifp->nat64s[ifp->nat64_next];
ifp->nat64_next = (uint8_t) ((ifp->nat64_next + 1) % n);
if (out->eport < MG_EPHEMERAL_PORT_BASE) out->eport = MG_EPHEMERAL_PORT_BASE;
e->ip6[0] = src[0], e->ip6[1] = src[1], e->ip4 = dst4, e->proto = next;
e->port6 = udp->sport, e->port4 = udp->dport;
e->lport = mg_htons(out->eport++);
MG_DEBUG(("NAT64 %M:%hu -> %M:%hu", mg_print_ip6, src,
mg_ntohs(udp->sport), mg_print_ip4, &dst4, mg_ntohs(udp->dport)));
}
ip = tx_ip(out, out->gwmac, next, 0, out->ip, dst4, plen);
p = (uint8_t *) (ip + 1);
memcpy(p, tcp, plen);
tcp = (struct tcp *) p, udp = (struct udp *) p;
udp->sport = e->lport;
if (next == 6) tcp->csum = 0, tcp->csum = pcsum(ip, p, plen);
if (next == 17) udp->csum = 0, udp->csum = pcsum(ip, p, plen);
driver_output(out, mg_l2_trailer(out, sizeof(*ip) + plen, p + plen));
}
// NAT64: translate an IPv4 packet of a NAT64 session back to IPv6, send it to
// the node. Return true if the packet was taken
static bool nat64_in(struct mg_tcpip_if *ifp, struct pkt *pkt) {
struct mg_tcpip_if *out = ifp->route;
struct mg_tcpip_nat64 *e = NULL;
struct udp *udp = pkt->udp != NULL ? pkt->udp : (struct udp *) pkt->tcp;
struct ip6 *ip6;
uint64_t src[2];
uint8_t l2[sizeof(struct mg_l2addr)], *p, next = pkt->ip->proto;
size_t i, n = sizeof(ifp->nat64s) / sizeof(ifp->nat64s[0]);
size_t plen = PDIFF(udp, pkt->pay.buf + pkt->pay.len);
if (out == NULL || pkt->ip->dst != ifp->ip) return false;
for (i = 0; i < n; i++) {
e = &out->nat64s[i];
if (e->proto == next && e->lport == udp->dport &&
e->port4 == udp->sport && e->ip4 == pkt->ip->src)
break;
}
if (i == n) return false;
if (sizeof(*ip6) + plen > out->mtu || !nb6_l2(out, e->ip6, l2))
return true; // Drop
memcpy(src, out->nat64, sizeof(out->nat64));
memcpy((uint8_t *) src + 12, &pkt->ip->src, 4);
ip6 = tx_ip6(out, l2, next, 0, src, e->ip6, plen);
p = (uint8_t *) (ip6 + 1);
memcpy(p, udp, plen);
((struct udp *) p)->dport = e->port6;
if (next == 6) {
struct tcp *tcp = (struct tcp *) p;
tcp->csum = 0, tcp->csum = p6csum(ip6, p, 6, plen);
} else {
udp = (struct udp *) p;
udp->csum = 0, udp->csum = p6csum(ip6, p, 17, plen);
if (udp->csum == 0) udp->csum = 0xffff; // 0 is not allowed in IPv6
}
driver_output(out, mg_l2_trailer(out, sizeof(*ip6) + plen, p + plen));
return true;
}
// Forward a packet that is not addressed to us out the other interface.
// Return true if the packet was taken: forwarded or dropped
static bool fwd_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt, size_t len) {
struct mg_tcpip_if *out = ifp->route;
uint64_t src[2], dst[2];
uint8_t l2[sizeof(struct mg_l2addr)], *d = (uint8_t *) dst, *p;
src[0] = pkt->ip6->src[0], src[1] = pkt->ip6->src[1];
dst[0] = pkt->ip6->dst[0], dst[1] = pkt->ip6->dst[1];
if (d[0] == 0xff || (d[0] == 0xfe && (d[1] & 0xc0) == 0x80))
return false; // Multicast or link-local: not forwarded
if (MG_IP6MATCH(dst, ifp->ip6) || MG_IP6MATCH(dst, ifp->ip6ll))
return false; // Ours
if (src[0] != 0 || src[1] != 0) {
nb6_learn(ifp, src, mg_l2_getaddr(ifp, pkt->l2));
}
if ((ifp->nat64[0] | ifp->nat64[1]) != 0 && // NAT64 prefix set, and ours
memcmp(dst, ifp->nat64, sizeof(ifp->nat64)) == 0) {
nat64_out(ifp, pkt, len);
return true;
}
if (MG_IP6MATCH(dst, out->ip6) || out->state6 < MG_TCPIP_STATE_UP ||
pkt->ip6->hops <= 1 || len > out->mtu || !nb6_l2(out, dst, l2)) {
return true; // Drop. TODO(): ICMPv6 errors, RFC-4443
}
p = mg_l2_header(out, MG_TCPIP_L2PROTO_IPV6, out->mac, l2,
(uint8_t *) out->tx.buf);
memcpy(p, pkt->ip6, len);
((struct ip6 *) p)->hops--;
driver_output(out, mg_l2_trailer(out, len, p + len));
return true;
}
static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) { static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (!icmp6csum_ok(pkt->ip6, pkt->icmp6, sizeof(*pkt->icmp6) + pkt->pay.len)) if (!icmp6csum_ok(pkt->ip6, pkt->icmp6, sizeof(*pkt->icmp6) + pkt->pay.len))
return; return;
@@ -1094,6 +1313,9 @@ static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
tx_icmp6(ifp, l2addr, target, ips.addr.ip6, 129, 0, pkt->pay.buf, plen); tx_icmp6(ifp, l2addr, target, ips.addr.ip6, 129, 0, pkt->pay.buf, plen);
} }
} break; } break;
case 133: // Router Solicitation: answer if we route
if (ifp->route != NULL) tx_ndp_ra(ifp);
break;
case 134: // Router Advertisement case 134: // Router Advertisement
rx_ndp_ra(ifp, pkt); rx_ndp_ra(ifp, pkt);
break; break;
@@ -1182,7 +1404,7 @@ static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
} else } else
#endif #endif
{ {
if (ifp->ip != 0 && ((rem->addr.ip4 & ifp->mask) == (ifp->ip & ifp->mask))) if (ifp->ip != 0 && MG_IPISLOCAL(ifp, rem->addr.ip4))
return mg_l2_getaddr(ifp, pkt->l2); // same LAN, get from frame return mg_l2_getaddr(ifp, pkt->l2); // same LAN, get from frame
if (ifp->gw_ready) // use the router if (ifp->gw_ready) // use the router
return ifp->gwmac; // ignore source address in frame return ifp->gwmac; // ignore source address in frame
@@ -1192,6 +1414,20 @@ static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
return NULL; return NULL;
} }
static void proto_error(struct mg_connection *c, bool r, const char *f, ...) {
char buf[64];
va_list ap;
struct connstate *s = (struct connstate *) (c + 1);
va_start(ap, f);
mg_vsnprintf(buf, sizeof(buf), f, &ap);
va_end(ap);
MG_ERROR(("%lu %ld %s", c->id, c->fd, buf));
c->is_closing = 1; // Stop Mongoose I/O
s->is_closing = 1; // Transport-terminal; do not send FIN
if (r && !c->is_udp) s->send_rst = true; // honot TCP RST request
mg_call(c, MG_EV_ERROR, buf);
}
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) { static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
struct mg_connection *c = getpeer(ifp->mgr, pkt, true); struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
struct connstate *s; struct connstate *s;
@@ -1214,10 +1450,10 @@ static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
return false; // safety net for lousy networks return false; // safety net for lousy networks
memcpy(s->mac, l2addr, sizeof(s->mac)); memcpy(s->mac, l2addr, sizeof(s->mac));
if (c->recv.len >= MG_MAX_RECV_SIZE) { if (c->recv.len >= MG_MAX_RECV_SIZE) {
mg_error(c, "max_recv_buf_size reached"); proto_error(c, false, "max_recv_buf_size reached");
} else if (c->recv.size - c->recv.len < pkt->pay.len && } else if (c->recv.size - c->recv.len < pkt->pay.len &&
!mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) { !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
mg_error(c, "oom"); proto_error(c, false, "oom");
} else { } else {
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len); memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
c->recv.len += pkt->pay.len; c->recv.len += pkt->pay.len;
@@ -1278,7 +1514,7 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
tcp->off = (uint8_t) (hlen / 4 << 4); tcp->off = (uint8_t) (hlen / 4 << 4);
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
if (ip_dst->is_ip6) { if (ip_dst->is_ip6) {
tcp->csum = p6csum(ip6, tcp, 6, hlen + len); tcp->csum = p6csum(ip6, tcp, 6, hlen + len);
} else } else
#endif #endif
{ {
@@ -1341,9 +1577,9 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
if (lsn->loc.is_ip6) { if (lsn->loc.is_ip6) {
c->rem.addr.ip6[0] = pkt->ip6->src[0], c->rem.addr.ip6[0] = pkt->ip6->src[0],
c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true; c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true;
c->loc.addr.ip6[0] = c->mgr->ifp->ip6[0], // Reply from the address the peer connected to: link-local or global
c->loc.addr.ip6[1] = c->mgr->ifp->ip6[1], c->loc.is_ip6 = true; c->loc.addr.ip6[0] = pkt->ip6->dst[0],
// TODO(): compare lsn to link-local, or rem as link-local: use ll instead c->loc.addr.ip6[1] = pkt->ip6->dst[1], c->loc.is_ip6 = true;
} else } else
#endif #endif
{ {
@@ -1491,7 +1727,7 @@ static void handle_tls_recv(struct mg_connection *c) {
size_t min = avail > MG_MAX_RECV_SIZE ? MG_MAX_RECV_SIZE : avail; size_t min = avail > MG_MAX_RECV_SIZE ? MG_MAX_RECV_SIZE : avail;
struct mg_iobuf *io = &c->recv; // allocated on first avail > 0 struct mg_iobuf *io = &c->recv; // allocated on first avail > 0
if (io->size - io->len < min && !mg_iobuf_resize(io, io->len + min)) { if (io->size - io->len < min && !mg_iobuf_resize(io, io->len + min)) {
mg_error(c, "oom"); proto_error(c, true, "oom");
} else { } else {
// Decrypt data directly into c->recv. If io->buf = NULL or // Decrypt data directly into c->recv. If io->buf = NULL or
// io->len = io->size (no room), there can be outstanding data that can't be // io->len = io->size (no room), there can be outstanding data that can't be
@@ -1500,7 +1736,7 @@ static void handle_tls_recv(struct mg_connection *c) {
long n = mg_tls_recv(c, io->buf != NULL ? &io->buf[io->len] : io->buf, long n = mg_tls_recv(c, io->buf != NULL ? &io->buf[io->len] : io->buf,
io->size - io->len); io->size - io->len);
if (n == MG_IO_ERR) { if (n == MG_IO_ERR) {
mg_error(c, "TLS recv error"); proto_error(c, true, "TLS recv error");
} else if (n > 0) { } else if (n > 0) {
// Decrypted successfully - trigger MG_EV_READ // Decrypted successfully - trigger MG_EV_READ
io->len += (size_t) n; io->len += (size_t) n;
@@ -1544,7 +1780,7 @@ static void retransmit(struct mg_connection *c) {
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_PUSH | TH_ACK, tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_PUSH | TH_ACK,
mg_htonl(s->txq_seq), mg_htonl(s->ack), mg_htonl(s->txq_seq), mg_htonl(s->ack),
s->is_full ? 0 : MG_TCPIP_WIN, buf, len) == (size_t) -1) { s->is_full ? 0 : MG_TCPIP_WIN, buf, len) == (size_t) -1) {
mg_error(c, "retransmit"); proto_error(c, false, "retransmit"); // should RST, but sending just failed
return; return;
} }
// Back off RTO after every timeout (RFC-6298, 5.5) // Back off RTO after every timeout (RFC-6298, 5.5)
@@ -1577,15 +1813,11 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len + 1); s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len + 1);
s->fin_rcvd = true; s->fin_rcvd = true;
if (c->is_draining && s->ttype == MIP_TTYPE_FIN) { if (c->is_draining && s->ttype == MIP_TTYPE_FIN) {
if (s->seq == mg_htonl(pkt->tcp->ack)) { // Simultaneous closure ? // Simultaneous closure ?
s->seq++; // Yes. Increment our SEQ
} else { // Otherwise,
s->seq = mg_htonl(pkt->tcp->ack); // Set to peer's ACK
}
s->twclosure = true; s->twclosure = true;
} else { } else {
// Peer closed first: send ACK only, enter CLOSE_WAIT. // Peer closed first: send ACK only, enter CLOSE_WAIT.
// The connection loop will call init_closure after pending send data // The connection loop will initiate closure after pending send data
// is flushed, then send our FIN. // is flushed, then send our FIN.
c->is_draining = 1; c->is_draining = 1;
} }
@@ -1619,18 +1851,11 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
s->twclosure = true; s->twclosure = true;
} }
if (pkt->pay.len == 0) return; if (pkt->pay.len == 0) return;
if (io->size - io->len < pkt->pay.len && if (!c->is_closing && io->size - io->len < pkt->pay.len &&
!mg_iobuf_resize(io, io->len + pkt->pay.len)) { !mg_iobuf_resize(io, io->len + pkt->pay.len)) {
mg_error(c, "oom"); proto_error(c, true, "oom");
return; // drop it return; // drop it
} }
// Copy TCP payload into the IO buffer. If the connection is plain text,
// we copy to c->recv. If the connection is TLS, this data is encrypted,
// therefore we copy that encrypted data to the c->rtls iobuffer instead,
// and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will
// call back mg_io_recv() which grabs raw data from c->rtls
memcpy(&io->buf[io->len], pkt->pay.buf, pkt->pay.len);
io->len += pkt->pay.len;
MG_VERBOSE(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack)); MG_VERBOSE(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack));
if (!(pkt->tcp->flags & TH_FIN)) { // FIN already advanced s->ack if (!(pkt->tcp->flags & TH_FIN)) { // FIN already advanced s->ack
// Advance ACK counter // Advance ACK counter
@@ -1638,7 +1863,8 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
s->unacked += pkt->pay.len; s->unacked += pkt->pay.len;
} }
// size_t diff = s->acked <= s->ack ? s->ack - s->acked : s->ack; // size_t diff = s->acked <= s->ack ? s->ack - s->acked : s->ack;
if (s->unacked > MG_TCPIP_WIN / 2 && s->acked != s->ack) { if ((s->unacked > MG_TCPIP_WIN / 2 && s->acked != s->ack) ||
s->ttype == MIP_TTYPE_FIN) {
// Send ACK immediately // Send ACK immediately
MG_VERBOSE(("%lu imm ACK %lu", c->id, s->acked)); MG_VERBOSE(("%lu imm ACK %lu", c->id, s->acked));
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
@@ -1651,6 +1877,14 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) {
// if not already running, setup a timer to send an ACK later // if not already running, setup a timer to send an ACK later
if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK); if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK);
} }
if (c->is_closing) return; // we have a "closed socket", ignore data
// Copy TCP payload into the IO buffer. If the connection is plain text,
// we copy to c->recv. If the connection is TLS, this data is encrypted,
// therefore we copy that encrypted data to the c->rtls iobuffer instead,
// and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will
// call back mg_io_recv() which grabs raw data from c->rtls
memcpy(&io->buf[io->len], pkt->pay.buf, pkt->pay.len);
io->len += pkt->pay.len;
if (c->is_tls) { if (c->is_tls) {
c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c); c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c);
} else { } else {
@@ -1759,11 +1993,11 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (c->is_tls_hs) mg_tls_handshake(c); if (c->is_tls_hs) mg_tls_handshake(c);
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init() if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
} else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) { } else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) {
mg_error(c, "connection refused"); proto_error(c, false, "connection refused");
} else if (c != NULL && pkt->tcp->flags & TH_RST) { } else if (c != NULL && pkt->tcp->flags & TH_RST) {
uint32_t seqno = mg_ntohl(pkt->tcp->seq); uint32_t seqno = mg_ntohl(pkt->tcp->seq);
if (seqno >= s->ack && seqno < (s->ack + MG_TCPIP_WIN)) // RFC-9293 3.5.3 if (seqno >= s->ack && seqno < (s->ack + MG_TCPIP_WIN)) // RFC-9293 3.5.3
mg_error(c, "peer RST"); // RFC-1122 4.2.2.13 proto_error(c, false, "peer RST"); // RFC-1122 4.2.2.13
} else if (c != NULL) { } else if (c != NULL) {
// process segment // process segment
s->tmiss = 0; // Reset missed keep-alive counter s->tmiss = 0; // Reset missed keep-alive counter
@@ -1845,7 +2079,7 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->pay.buf); if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->pay.buf);
if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf); if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf);
c = getpeer(ifp->mgr, pkt, false); c = getpeer(ifp->mgr, pkt, false);
if (c) mg_error(c, "Received fragmented packet"); if (c) proto_error(c, false, "Received fragmented packet");
} else if (pkt->ip->proto == 1) { } else if (pkt->ip->proto == 1) {
pkt->icmp = (struct icmp *) (pkt->pay.buf); pkt->icmp = (struct icmp *) (pkt->pay.buf);
if (pkt->pay.len < sizeof(*pkt->icmp)) return; if (pkt->pay.len < sizeof(*pkt->icmp)) return;
@@ -1862,6 +2096,9 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src, MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst,
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len)); mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
#if MG_ENABLE_IPV6
if (nat64_in(ifp, pkt)) return;
#endif
if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(68) && if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(68) &&
len >= offsetof(struct dhcp, options)) { len >= offsetof(struct dhcp, options)) {
pkt->dhcp = (struct dhcp *) (pkt->udp + 1); pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
@@ -1887,6 +2124,9 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src, MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst,
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len)); mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
#if MG_ENABLE_IPV6
if (nat64_in(ifp, pkt)) return;
#endif
rx_tcp(ifp, pkt); rx_tcp(ifp, pkt);
} else { } else {
MG_DEBUG(("Unknown IP proto %x", (int) pkt->ip->proto)); MG_DEBUG(("Unknown IP proto %x", (int) pkt->ip->proto));
@@ -1904,6 +2144,8 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if ((pkt->ip6->ver >> 4) != 0x6) return; // Not IPv6 if ((pkt->ip6->ver >> 4) != 0x6) return; // Not IPv6
plen = mg_ntohs(pkt->ip6->plen); plen = mg_ntohs(pkt->ip6->plen);
if (plen > (pkt->pay.len - sizeof(*pkt->ip6))) return; // malformed if (plen > (pkt->pay.len - sizeof(*pkt->ip6))) return; // malformed
if (ifp->route != NULL && fwd_ip6(ifp, pkt, sizeof(*pkt->ip6) + plen))
return;
next = pkt->ip6->next; next = pkt->ip6->next;
nhdr = (uint8_t *) (pkt->ip6 + 1); nhdr = (uint8_t *) (pkt->ip6 + 1);
while (loop) { while (loop) {
@@ -1913,7 +2155,7 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
case 43: // Routing 4.4 case 43: // Routing 4.4
case 60: // Destination Options 4.6 case 60: // Destination Options 4.6
case 51: // Authentication RFC-4302 case 51: // Authentication RFC-4302
MG_INFO(("IPv6 extension header %d", (int) next)); MG_DEBUG(("IPv6 extension header %d", (int) next));
if (((uint32_t) len + 2) > plen) return; // nhdr[0, 1]; malformed if (((uint32_t) len + 2) > plen) return; // nhdr[0, 1]; malformed
next = nhdr[0]; next = nhdr[0];
hlen = (uint16_t) (8 * (nhdr[1] + 1)); hlen = (uint16_t) (8 * (nhdr[1] + 1));
@@ -1928,7 +2170,7 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (nhdr[0] == 17) pkt->udp = (struct udp *) (pkt->pay.buf); if (nhdr[0] == 17) pkt->udp = (struct udp *) (pkt->pay.buf);
if (nhdr[0] == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf); if (nhdr[0] == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf);
c = getpeer(ifp->mgr, pkt, false); c = getpeer(ifp->mgr, pkt, false);
if (c) mg_error(c, "Received fragmented packet"); if (c) proto_error(c, false, "Received fragmented packet");
} }
return; return;
case 59: // No Next Header 4.7 case 59: // No Next Header 4.7
@@ -1946,8 +2188,8 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
pkt->icmp6 = (struct icmp6 *) (pkt->pay.buf); pkt->icmp6 = (struct icmp6 *) (pkt->pay.buf);
if (pkt->pay.len < sizeof(*pkt->icmp6)) return; if (pkt->pay.len < sizeof(*pkt->icmp6)) return;
mkpay(pkt, pkt->icmp6 + 1); mkpay(pkt, pkt->icmp6 + 1);
MG_DEBUG(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src, MG_VERBOSE(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src,
mg_print_ip6, &pkt->ip6->dst, (int) pkt->pay.len)); mg_print_ip6, &pkt->ip6->dst, (int) pkt->pay.len));
rx_icmp6(ifp, pkt); rx_icmp6(ifp, pkt);
} else if (next == 17) { } else if (next == 17) {
pkt->udp = (struct udp *) (pkt->pay.buf); pkt->udp = (struct udp *) (pkt->pay.buf);
@@ -1957,9 +2199,9 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (len < sizeof(*pkt->udp) || len > pkt->pay.len) return; // malformed if (len < sizeof(*pkt->udp) || len > pkt->pay.len) return; // malformed
pkt->pay.len = len; // strip excess data pkt->pay.len = len; // strip excess data
mkpay(pkt, pkt->udp + 1); mkpay(pkt, pkt->udp + 1);
MG_DEBUG(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src, MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
mg_ntohs(pkt->udp->sport), mg_print_ip6, &pkt->ip6->dst, mg_ntohs(pkt->udp->sport), mg_print_ip6, &pkt->ip6->dst,
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len)); mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
if (ifp->enable_dhcp6_client && pkt->udp->dport == mg_htons(546)) { if (ifp->enable_dhcp6_client && pkt->udp->dport == mg_htons(546)) {
pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1); pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
mkpay(pkt, pkt->dhcp6 + 1); mkpay(pkt, pkt->dhcp6 + 1);
@@ -1981,9 +2223,9 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (off < sizeof(*pkt->tcp) / 4 || pkt->pay.len < (uint16_t) (4 * off)) if (off < sizeof(*pkt->tcp) / 4 || pkt->pay.len < (uint16_t) (4 * off))
return; return;
mkpay(pkt, (uint32_t *) pkt->tcp + off); mkpay(pkt, (uint32_t *) pkt->tcp + off);
MG_DEBUG(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src, MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
mg_ntohs(pkt->tcp->sport), mg_print_ip6, &pkt->ip6->dst, mg_ntohs(pkt->tcp->sport), mg_print_ip6, &pkt->ip6->dst,
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len)); mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
rx_tcp(ifp, pkt); rx_tcp(ifp, pkt);
} else { } else {
MG_DEBUG(("Unknown IPv6 next hdr %x", (int) next)); MG_DEBUG(("Unknown IPv6 next hdr %x", (int) next));
@@ -2061,13 +2303,18 @@ static void mg_ip6_poll(struct mg_tcpip_if *ifp, bool s1) {
if (ifp->state6 < MG_TCPIP_STATE_UP) return; if (ifp->state6 < MG_TCPIP_STATE_UP) return;
if (ifp->enable_slaac && s1 && ifp->state6 == MG_TCPIP_STATE_UP) if (ifp->enable_slaac && s1 && ifp->state6 == MG_TCPIP_STATE_UP)
tx_ndp_rs(ifp); tx_ndp_rs(ifp);
if (ifp->route != NULL && s1 && ifp->now >= ifp->ra_expire) {
tx_ndp_ra(ifp); // Unsolicited router advertisements, 6.2.4
ifp->ra_expire = ifp->now + 10000;
}
} }
static void mg_ip6_link(struct mg_tcpip_if *ifp, bool drv_up, bool l2_up) { static void mg_ip6_link(struct mg_tcpip_if *ifp, bool drv_up, bool l2_up) {
bool cur_drv = (ifp->state6 != MG_TCPIP_STATE_DOWN); bool cur_drv = (ifp->state6 != MG_TCPIP_STATE_DOWN);
bool cur_l2 = (ifp->state6 >= MG_TCPIP_STATE_UP); bool cur_l2 = (ifp->state6 >= MG_TCPIP_STATE_UP);
const uint8_t px[8] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0}; // RFC-4291 2.5.6 const uint8_t px[8] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0}; // RFC-4291 2.5.6
if (drv_up != cur_drv || l2_up != cur_l2) { // link/L2 state has changed if (drv_up != cur_drv || l2_up != cur_l2) { // link/L2 state has changed
if (ifp->l2type == MG_TCPIP_L2_ETH && l2_up && ifp->ip6ll[0] == 0 && if ((ifp->l2type == MG_TCPIP_L2_ETH || ifp->l2type == MG_TCPIP_L2_LOWPAN) &&
l2_up && ifp->ip6ll[0] == 0 &&
ifp->ip6ll[1] == 0) { // gen ll address ifp->ip6ll[1] == 0) { // gen ll address
mg_l2_genip6(ifp->l2type, ifp->ip6ll, 64, ifp->mac); mg_l2_genip6(ifp->l2type, ifp->ip6ll, 64, ifp->mac);
memcpy(ifp->ip6ll, px, 8); // RFC-2464 5 memcpy(ifp->ip6ll, px, 8); // RFC-2464 5
@@ -2122,7 +2369,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
} }
// Handle gw ARP request timeout, order is important // Handle gw ARP request timeout, order is important
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_IP) { if (expired_1000ms && ifp->state == MG_TCPIP_STATE_IP) {
ifp->state = MG_TCPIP_STATE_READY; // keep best-effort MAC or poison mark ifp->state = MG_TCPIP_STATE_READY; // gw MAC not resolved, we'll retry
onstatechange(ifp); onstatechange(ifp);
} }
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw_ready && if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw_ready &&
@@ -2131,10 +2378,10 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
// Handle gw NS/NA req/resp timeout, order is important // Handle gw NS/NA req/resp timeout, order is important
if (expired_1000ms && ifp->state6 == MG_TCPIP_STATE_IP) { if (expired_1000ms && ifp->state6 == MG_TCPIP_STATE_IP) {
ifp->state6 = MG_TCPIP_STATE_READY; // keep best-effort MAC or poison mark ifp->state6 = MG_TCPIP_STATE_READY; // gw6 MAC not resolved, we'll retry
onstate6change(ifp); onstate6change(ifp);
} }
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw6_ready && if (expired_1000ms && ifp->state6 == MG_TCPIP_STATE_READY && !ifp->gw6_ready &&
(ifp->gw6[0] != 0 || ifp->gw6[1] != 0)) (ifp->gw6[0] != 0 || ifp->gw6[1] != 0))
tx_ndp_ns(ifp, ifp->gw6, NULL); // retry GW hwaddr resolution tx_ndp_ns(ifp, ifp->gw6, NULL); // retry GW hwaddr resolution
#endif #endif
@@ -2193,7 +2440,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
} }
if (ifp->now > s->timer) { if (ifp->now > s->timer) {
if (s->ttype == MIP_TTYPE_ARP) { if (s->ttype == MIP_TTYPE_ARP) {
mg_error(c, "ARP timeout"); proto_error(c, false, "ARP timeout");
} else if (c->is_udp) { } else if (c->is_udp) {
continue; continue;
} else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) { } else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) {
@@ -2202,13 +2449,13 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN, NULL, 0); mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN, NULL, 0);
s->acked = s->ack; s->acked = s->ack;
} else if (s->ttype == MIP_TTYPE_SYN) { } else if (s->ttype == MIP_TTYPE_SYN) {
mg_error(c, "Connection timeout"); proto_error(c, false, "Connection timeout");
} else if (s->ttype == MIP_TTYPE_FIN) { } else if (s->ttype == MIP_TTYPE_FIN) {
c->is_closing = 1; s->is_closing = 1;
continue; continue;
} else { } else {
if (s->tmiss++ > 2) { if (s->tmiss++ > 2) {
mg_error(c, "keepalive"); proto_error(c, false, "keepalive");
} else { } else {
MG_VERBOSE(("%lu keepalive", c->id)); MG_VERBOSE(("%lu keepalive", c->id));
tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
@@ -2259,8 +2506,8 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
ifp->mgr = mgr; ifp->mgr = mgr;
mgr->extraconnsize = sizeof(struct connstate); mgr->extraconnsize = sizeof(struct connstate);
if (ifp->ip == 0 && ifp->l2type != MG_TCPIP_L2_PPP && if (ifp->ip == 0 && ifp->l2type != MG_TCPIP_L2_PPP &&
ifp->l2type != MG_TCPIP_L2_PPPoE) ifp->l2type != MG_TCPIP_L2_PPPoE && ifp->l2type != MG_TCPIP_L2_LOWPAN)
ifp->enable_dhcp_client = true; ifp->enable_dhcp_client = true; // No IPv4 over 6LoWPAN
mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535 mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535
ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from
// MG_EPHEMERAL_PORT_BASE to 65535 // MG_EPHEMERAL_PORT_BASE to 65535
@@ -2268,14 +2515,32 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
MG_DEBUG(("Builtin TCP/IP init done. Queue size: %zu, extra conn size: %zu", MG_DEBUG(("Builtin TCP/IP init done. Queue size: %zu, extra conn size: %zu",
ifp->recv_queue.size, mgr->extraconnsize)); ifp->recv_queue.size, mgr->extraconnsize));
} }
mg_bsd_init(mgr);
}
static void close_conn(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1);
mg_iobuf_free(&s->txq);
mg_close_conn(c);
} }
void mg_tcpip_free(struct mg_tcpip_if *ifp) { void mg_tcpip_free(struct mg_tcpip_if *ifp) {
while (ifp->mgr->conns != NULL) close_conn(ifp->mgr->conns); // TCP FIN wait
mg_free(ifp->recv_queue.buf); mg_free(ifp->recv_queue.buf);
mg_free(ifp->tx.buf); mg_free(ifp->tx.buf);
mg_free(ifp->dns4_url); mg_free(ifp->dns4_url);
#if MG_ENABLE_IPV6
mg_free(ifp->dns6_url);
#endif
} }
#if MG_ENABLE_IPV6
void mg_tcpip_route(struct mg_tcpip_if *if1, struct mg_tcpip_if *if2) {
if1->route = if2, if2->route = if1;
if1->ra_expire = if2->ra_expire = 0; // Advertise at the next poll
}
#endif
static void send_syn(struct mg_connection *c) { static void send_syn(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port)); uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port));
@@ -2337,12 +2602,12 @@ void mg_connect_resolved(struct mg_connection *c) {
memcpy(s->mac, ifp->gw6mac, sizeof(s->mac)); memcpy(s->mac, ifp->gw6mac, sizeof(s->mac));
l2addr_resolved(c); l2addr_resolved(c);
} else { } else {
MG_ERROR(("No IPv6 gateway, can't connect")); mg_error(c, "No IPv6 gateway, can't connect");
} }
#endif #endif
} else { } else {
uint32_t rem_ip = c->rem.addr.ip4; uint32_t rem_ip = c->rem.addr.ip4;
if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask)) && if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) &&
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP) rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
// If we're in the same LAN, fire an ARP lookup. // If we're in the same LAN, fire an ARP lookup.
MG_DEBUG(("%lu ARP lookup...", c->id)); MG_DEBUG(("%lu ARP lookup...", c->id));
@@ -2353,7 +2618,7 @@ void mg_connect_resolved(struct mg_connection *c) {
memcpy(s->mac, ifp->gwmac, sizeof(s->mac)); memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
l2addr_resolved(c); l2addr_resolved(c);
} else { } else {
MG_ERROR(("No gateway, can't connect")); mg_error(c, "No gateway, can't connect");
} }
} }
} }
@@ -2372,29 +2637,13 @@ static void write_conn(struct mg_connection *c) {
: mg_io_send(c, c->send.buf, c->send.len); : mg_io_send(c, c->send.buf, c->send.len);
// TODO(): mg_tls_send() may return 0 forever on steady OOM // TODO(): mg_tls_send() may return 0 forever on steady OOM
if (len == MG_IO_ERR) { if (len == MG_IO_ERR) {
mg_error(c, "tx err"); proto_error(c, false, "tx err");
} else if (len > 0) { } else if (len > 0) {
mg_iobuf_del(&c->send, 0, (size_t) len); mg_iobuf_del(&c->send, 0, (size_t) len);
mg_call(c, MG_EV_WRITE, &len); mg_call(c, MG_EV_WRITE, &len);
} }
} }
static void init_closure(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1);
if (c->is_listening == false && c->is_connecting == false) {
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_FIN | TH_ACK,
mg_htonl(s->seq), mg_htonl(s->ack), s->is_full ? 0 : MG_TCPIP_WIN,
NULL, 0);
settmout(c, MIP_TTYPE_FIN);
}
}
static void close_conn(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1);
mg_iobuf_free(&s->txq);
mg_close_conn(c);
}
static bool can_write(struct mg_connection *c) { static bool can_write(struct mg_connection *c) {
return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 && return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 &&
c->is_tls_hs == 0 && c->is_arplooking == 0; c->is_tls_hs == 0 && c->is_arplooking == 0;
@@ -2415,40 +2664,64 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
!c->is_listening && !c->is_connecting; !c->is_listening && !c->is_connecting;
tmp = c->next; tmp = c->next;
mg_call(c, MG_EV_POLL, &now); mg_call(c, MG_EV_POLL, &now);
MG_VERBOSE(("%lu .. %c%c%c%c%c %lu %lu", c->id, c->is_tls ? 'T' : 't', MG_VERBOSE(("%lu .. %c%c%c%c%c%c %lu %lu", c->id, c->is_tls ? 'T' : 't',
c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h',
c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c', c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c',
mg_tls_pending(c), c->rtls.len)); s->is_closing ? 'F' : 'f', mg_tls_pending(c), c->rtls.len));
// order is important, TLS conn close with > 1 record in buffer (below) if (!c->is_closing) {
if (is_tls && (c->rtls.len > 0 || mg_tls_pending(c) > 0)) // order is important, TLS conn close with > 1 record in buffer (below)
if (is_tls && (c->rtls.len > 0 || mg_tls_pending(c) > 0))
c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c); c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c);
if (can_write(c)) write_conn(c); if (can_write(c)) write_conn(c);
if (is_tls && c->send.len == 0) flush = mg_tls_flush(c); if (is_tls && c->send.len == 0) flush = mg_tls_flush(c);
if (flush == MG_IO_ERR) mg_error(c, "tx err");
if (c->is_draining && c->send.len == 0) {
if (c->is_udp) c->is_closing = 1;
if (!c->is_udp && flush == 0 && s->txq.len == 0 &&
s->ttype != MIP_TTYPE_FIN)
init_closure(c);
} }
if (flush == MG_IO_ERR) proto_error(c, false, "tx err"); // should RST but
if (c->is_draining && c->send.len == 0 &&
(c->is_udp ||
(flush == 0 && s->txq.len == 0 && s->ttype != MIP_TTYPE_FIN)))
c->is_closing = 1;
// For non-TLS, close immediately upon completing the 3-way closure // For non-TLS, close immediately upon completing the 3-way closure
// For TLS, handle any pending data (above) until MIP_TTYPE_FIN expires // For TLS, handle any pending data (above) until MIP_TTYPE_FIN expires
if (s->twclosure && if (s->twclosure &&
(!c->is_tls || (c->rtls.len == 0 && mg_tls_pending(c) == 0))) (!c->is_tls || (c->rtls.len == 0 && mg_tls_pending(c) == 0))) {
c->is_closing = 1; s->is_closing = 1;
if (c->is_closing) close_conn(c); } else if (c->is_closing && !s->is_closing) {
if (c->is_udp || c->is_listening || c->is_resolving || c->is_arplooking ||
c->is_connecting) {
close_conn(c);
continue;
} else if (s->ttype != MIP_TTYPE_FIN) { // active TCP: initiate closure
size_t tx = tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp,
TH_FIN | TH_ACK, mg_htonl(s->seq), mg_htonl(s->ack),
s->is_full ? 0 : MG_TCPIP_WIN, NULL, 0);
if (tx == (size_t) -1) {
s->is_closing = 1;
} else if (tx > 0) {
s->seq++;
settmout(c, MIP_TTYPE_FIN);
} // else tx = 0, should recover later or we have worse problems
}
}
if (s->send_rst) // proto_error requested TCP RST, send it and close
tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_RST,
mg_htonl(s->seq), 0, 0, NULL, 0);
if (s->is_closing) close_conn(c); // then removes the conn
} }
mg_bsd_poll(mgr);
(void) ms; (void) ms;
} }
bool mg_send(struct mg_connection *c, const void *buf, size_t len) { bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
struct mg_tcpip_if *ifp = c->mgr->ifp; struct mg_tcpip_if *ifp = c->mgr->ifp;
bool res = false; bool res = false;
if (!c->loc.is_ip6 && (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) { // While resolving, the address family is unknown yet: just queue the data
mg_error(c, "net down"); if (!c->is_resolving && !c->loc.is_ip6 &&
(ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) {
proto_error(c, false, "net down");
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
} else if (c->loc.is_ip6 && ifp->state6 != MG_TCPIP_STATE_READY) { } else if (!c->is_resolving && c->loc.is_ip6 &&
mg_error(c, "net down"); ifp->state6 != MG_TCPIP_STATE_READY) {
proto_error(c, false, "net down");
#endif #endif
} else if (c->is_udp && (c->is_arplooking || c->is_resolving)) { } else if (c->is_udp && (c->is_arplooking || c->is_resolving)) {
// Fail to send, no target MAC or IP // Fail to send, no target MAC or IP
@@ -2482,10 +2755,31 @@ static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip) {
MG_DEBUG(("Set DNS URL to %s", dnsc->url)); MG_DEBUG(("Set DNS URL to %s", dnsc->url));
if (ifp->mgr->use_dns6) return; if (ifp->mgr->use_dns6) return;
if (dnsc->c != NULL) mg_close_conn(dnsc->c); if (dnsc->c != NULL) mg_close_conn(dnsc->c);
if (!mg_dnsc_init(ifp->mgr, dnsc)) // create DNS connection
MG_ERROR(("DNS connection creation failed"));
} }
static void conndns4(struct mg_tcpip_if *ifp, uint32_t ip) {
struct mg_connection *c;
struct mg_dns *dnsc = &ifp->mgr->dns4;
if (ifp->mgr->use_dns6) return;
if ((c = mg_alloc_conn(ifp->mgr)) == NULL) {
MG_ERROR(("OOM, DNS connection creation failed"));
return;
} // mg_connect_svc() would block us, so we bypass it here
LIST_ADD_HEAD(struct mg_connection, &ifp->mgr->conns, c);
c->is_udp = true;
c->fd = (void *) (size_t) MG_INVALID_SOCKET;
c->is_client = true;
dnsc->c = c;
mg_call(c, MG_EV_OPEN, (void *) dnsc->url);
MG_DEBUG(("%lu %ld %s", c->id, c->fd, dnsc->url));
c->rem.addr.ip4 = ip;
c->rem.port = mg_htons(53);
mg_connect_resolved(c);
mg_dnsc_init(ifp->mgr, dnsc); // will fill c->pfn = dns_cb
}
void mg_tcpip_mapip(struct mg_connection *c, struct mg_addr *ip) { void mg_tcpip_mapip(struct mg_connection *c, struct mg_addr *ip) {
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
uint8_t *l2addr = tcpip_mapip(c->mgr->ifp, ip); uint8_t *l2addr = tcpip_mapip(c->mgr->ifp, ip);
+38
View File
@@ -44,6 +44,24 @@ enum {
}; };
// clang-format on // clang-format on
#if MG_ENABLE_IPV6
// IPv6 neighbour learned from forwarded packets, see mg_tcpip_route()
struct mg_tcpip_nb6 {
uint64_t ip[2]; // Neighbour IPv6 address
uint8_t l2[sizeof(struct mg_l2addr)]; // Its hardware address
};
// NAT64 session, see mg_tcpip_if::nat64
struct mg_tcpip_nat64 {
uint64_t ip6[2]; // Node's IPv6 address
uint32_t ip4; // IPv4 server address
uint16_t port6; // Node's port
uint16_t port4; // IPv4 server port
uint16_t lport; // Our port on the IPv4 interface
uint8_t proto; // 6: TCP, 17: UDP, 0: unused entry
};
#endif
// Network interface. Zero-initialise, then set the fields marked "user-set" // Network interface. Zero-initialise, then set the fields marked "user-set"
// before calling mg_tcpip_init(). Fields in the "internal state" section // before calling mg_tcpip_init(). Fields in the "internal state" section
// below are managed by the stack; read them freely but do not modify them. // below are managed by the stack; read them freely but do not modify them.
@@ -78,6 +96,8 @@ struct mg_tcpip_if {
uint8_t prefix[8]; // Global prefix bytes uint8_t prefix[8]; // Global prefix bytes
uint8_t prefix_len; // Global prefix length in bits uint8_t prefix_len; // Global prefix length in bits
uint64_t gw6[2]; // IPv6 default gateway uint64_t gw6[2]; // IPv6 default gateway
uint8_t nat64[12]; // NAT64 /96 prefix, RFC 6052. If set, DNS asks
// for IPv4 (A) and makes IPv6 from it (DNS64)
bool enable_slaac; // Enable SLAAC (stateless address autoconfiguration) bool enable_slaac; // Enable SLAAC (stateless address autoconfiguration)
bool enable_dhcp6_client; // Enable DHCPv6 client (not yet fully implemented) bool enable_dhcp6_client; // Enable DHCPv6 client (not yet fully implemented)
bool is_ip6_changed; // Set by stack when IPv6 address changes bool is_ip6_changed; // Set by stack when IPv6 address changes
@@ -109,6 +129,13 @@ struct mg_tcpip_if {
uint8_t gw6mac[sizeof(struct mg_l2addr)]; // IPv6 gateway hardware address uint8_t gw6mac[sizeof(struct mg_l2addr)]; // IPv6 gateway hardware address
uint8_t state6; // Current IPv6 link/IP state (MG_TCPIP_STATE_*) uint8_t state6; // Current IPv6 link/IP state (MG_TCPIP_STATE_*)
bool gw6_ready; // IPv6 gateway MAC address has been resolved bool gw6_ready; // IPv6 gateway MAC address has been resolved
struct mg_tcpip_if *route; // Forward to this interface, see mg_tcpip_route()
struct mg_tcpip_nb6 nb6[4]; // Neighbours learned from forwarded packets
uint8_t nb6_next; // Next nb6 entry to replace
struct mg_tcpip_nat64 nat64s[8]; // NAT64 sessions, if nat64 is set
uint8_t nat64_next; // Next nat64s entry to replace
uint64_t ra_expire; // Time of the next router advertisement, ms
char *dns6_url; // DNS server URL from router advertisement
#endif #endif
}; };
@@ -122,6 +149,15 @@ void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);
// Frees the TX buffer and RX queue allocated by mg_tcpip_init(). // Frees the TX buffer and RX queue allocated by mg_tcpip_init().
void mg_tcpip_free(struct mg_tcpip_if *); void mg_tcpip_free(struct mg_tcpip_if *);
#if MG_ENABLE_IPV6
// Forwards IPv6 packets between two interfaces: packets not addressed to us
// go out the other interface. An interface with a static IPv6 prefix (ip6,
// prefix and prefix_len set, SLAAC off) gets router advertisements for it,
// the other interface gets a route to it, RFC 4191. Call after both
// mg_tcpip_init(). Replies to the other side need its sender seen first
void mg_tcpip_route(struct mg_tcpip_if *if1, struct mg_tcpip_if *if2);
#endif
// Pushes a received raw frame into ifp->recv_queue. Used by interrupt-driven // Pushes a received raw frame into ifp->recv_queue. Used by interrupt-driven
// or DMA-based drivers (those with rx=NULL) to hand frames to the stack from // or DMA-based drivers (those with rx=NULL) to hand frames to the stack from
// an ISR or DMA completion callback. Drops the frame and increments ndrop if // an ISR or DMA completion callback. Drops the frame and increments ndrop if
@@ -154,6 +190,8 @@ extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6; extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
extern struct mg_tcpip_driver mg_tcpip_driver_atcmd; extern struct mg_tcpip_driver mg_tcpip_driver_atcmd;
extern struct mg_tcpip_driver mg_tcpip_driver_netc; extern struct mg_tcpip_driver mg_tcpip_driver_netc;
extern struct mg_tcpip_driver mg_tcpip_driver_mcxw71;
extern struct mg_tcpip_driver mg_tcpip_driver_rw612_154;
// SPI bus abstraction for SPI-attached network chips (e.g. W5500). // SPI bus abstraction for SPI-attached network chips (e.g. W5500).
// Populate and assign to the driver's driver_data field. // Populate and assign to the driver's driver_data field.
+15
View File
@@ -243,6 +243,21 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *json_url,
} }
} }
// Uses *p as a flag: non-NULL means an OTA session is open and must be
// closed by mg_ota_end()
bool mg_http_ota_cb(struct mg_http_message *hm, struct mg_str *data, void **p) {
if (hm != NULL) { // Start
if (!mg_ota_begin(hm->body.len)) return false;
*p = (void *) (ptrdiff_t) 1;
return true;
} else if (data != NULL) { // Next chunk
return mg_ota_write(data->buf, data->len);
}
if (*p == NULL) return false; // End. Begin failed, nothing to finish
*p = NULL;
return mg_ota_end();
}
void mg_ota_poll(struct mg_mgr *mgr) { void mg_ota_poll(struct mg_mgr *mgr) {
static uint64_t t = 5000; // Fire first time 5 sec after boot static uint64_t t = 5000; // Fire first time 5 sec after boot
+15
View File
@@ -26,6 +26,7 @@
#pragma once #pragma once
#include "arch.h" #include "arch.h"
#include "http.h"
#include "net.h" #include "net.h"
// Supported OTA targets // Supported OTA targets
@@ -43,6 +44,7 @@
#define MG_OTA_RT1170 304 // IMXRT1170 #define MG_OTA_RT1170 304 // IMXRT1170
#define MG_OTA_RT1180 305 // IMXRT1180 #define MG_OTA_RT1180 305 // IMXRT1180
#define MG_OTA_MCXN 310 // MCXN947 #define MG_OTA_MCXN 310 // MCXN947
#define MG_OTA_MCXW71 311 // MCXW71
#define MG_OTA_RW612 320 // FRDM-RW612 #define MG_OTA_RW612 320 // FRDM-RW612
#define MG_OTA_FLASH 900 // OTA via internal flash #define MG_OTA_FLASH 900 // OTA via internal flash
#define MG_OTA_ESP32 910 // ESP32 OTA implementation #define MG_OTA_ESP32 910 // ESP32 OTA implementation
@@ -72,6 +74,19 @@ bool mg_ota_write(const void *buf, size_t len);
// Ends the OTA process and commits the written firmware. // Ends the OTA process and commits the written firmware.
bool mg_ota_end(void); bool mg_ota_end(void);
// Push-based OTA: mg_http_stream_body() callback that flashes the request
//
// ```c
// if (ev == MG_EV_HTTP_HDRS) {
// struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// if (mg_match(hm->uri, mg_str("/ota"), NULL) &&
// !mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) {
// mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
// }
// }
// ```
bool mg_http_ota_cb(struct mg_http_message *hm, struct mg_str *data, void **p);
// MG_OTA_ROLLBACK: swap firmware banks and reset. On flash-based OTA, the // MG_OTA_ROLLBACK: swap firmware banks and reset. On flash-based OTA, the
// default calls swap_fn() (no-op on single-bank) then resets. Override in // default calls swap_fn() (no-op on single-bank) then resets. Override in
// mongoose_config.h for platforms without mg_flash (ESP32, PicoSDK, etc.). // mongoose_config.h for platforms without mg_flash (ESP32, PicoSDK, etc.).
+183
View File
@@ -0,0 +1,183 @@
#include "flash.h"
#include "log.h"
#include "ota.h"
#if MG_OTA == MG_OTA_MCXW71
// MCXW71SRM, section 11.2: ROM flash API, 8 KB sectors, 16-byte phrases,
// 128-byte pages. Only the CM33 application flash is used; RF flash and IFR
// (including the write-once ROM configuration) are left alone.
struct mg_mcxw71_flash_config {
struct {
uint32_t base, size, blocks;
uint32_t ifr_base, ifr_size;
} mem[2];
};
struct mg_mcxw71_flash_api {
uint32_t (*init)(struct mg_mcxw71_flash_config *);
uint32_t (*erase)(struct mg_mcxw71_flash_config *, void *, uint32_t, uint32_t,
uint32_t);
uint32_t (*phrase)(struct mg_mcxw71_flash_config *, void *, uint32_t,
uint32_t *, uint32_t);
uint32_t (*page)(struct mg_mcxw71_flash_config *, void *, uint32_t,
uint32_t *, uint32_t);
};
// The flash API pointer is the second entry in the ROM API table.
#define MG_MCXW71_FLASH_API \
(*(const struct mg_mcxw71_flash_api *const *) 0x14816fe4U)
#define MG_MCXW71_ERASE_KEY 0x6b65666cU // FOUR_CHAR_CODE('l', 'f', 'e', 'k')
static struct mg_mcxw71_flash_config s_mcxw71_config;
static size_t s_mcxw71_size, s_mcxw71_received, s_mcxw71_pending;
static uint32_t s_mcxw71_buf[4];
static bool s_mcxw71_failed;
static bool mg_mcxw71_write(void *, const void *, size_t);
static bool mg_mcxw71_swap(void);
static struct mg_flash s_mg_flash_mcxw71 = {
(void *) 0, 0x100000, 8192, 16, mg_mcxw71_write, mg_mcxw71_swap};
struct mg_flash *mg_flash = &s_mg_flash_mcxw71;
// Everything called while flash is busy, or after the application has been
// erased, must execute from RAM or ROM. Do not log or call libc here.
MG_IRAM static bool mg_mcxw71_write(void *addr, const void *buf, size_t len) {
uintptr_t dst = (uintptr_t) addr;
const volatile uint8_t *src = (const volatile uint8_t *) buf;
uint32_t irq, status = 0;
if (dst >= s_mg_flash_mcxw71.size || len > s_mg_flash_mcxw71.size - dst ||
(dst % 16) != 0 || (len % 16) != 0)
return false;
irq = __get_PRIMASK();
__disable_irq();
while (len > 0 && status == 0) {
uint32_t words[32]; // ROM needs a word-aligned source in RAM
volatile uint8_t *p = (volatile uint8_t *) words;
size_t i, n = (dst % 128) == 0 && len >= 128 ? 128 : 16;
for (i = 0; i < n; i++) p[i] = src[i];
// Match the MCUXpresso ROM wrappers' cache maintenance. Interrupts stay
// masked until the flash command completes and stale cache data is gone.
MCM->CPCR2 |= MCM_CPCR2_CCBC_MASK;
__DSB();
__ISB();
if ((dst % 8192) == 0) {
status = MG_MCXW71_FLASH_API->erase(
&s_mcxw71_config, FMU0, (uint32_t) dst, 8192, MG_MCXW71_ERASE_KEY);
}
if (status == 0) {
if (n == 128) {
status = MG_MCXW71_FLASH_API->page(&s_mcxw71_config, FMU0,
(uint32_t) dst, words, 128);
} else {
status = MG_MCXW71_FLASH_API->phrase(&s_mcxw71_config, FMU0,
(uint32_t) dst, words, 16);
}
}
MCM->CPCR2 |= MCM_CPCR2_CCBC_MASK;
__DSB();
__ISB();
dst += n, src += n, len -= n;
}
__set_PRIMASK(irq);
return status == 0;
}
static bool mg_mcxw71_swap(void) {
// The common helper verifies the staged image before the RAM copy below.
// A failed write must never commit, even if all expected bytes arrived.
return !s_mcxw71_failed;
}
MG_IRAM static void mg_mcxw71_apply(size_t size) {
size_t ofs, half = s_mg_flash_mcxw71.size / 2;
__disable_irq();
SysTick->CTRL = 0;
for (ofs = 0; ofs < size; ofs += 8192) {
if (!mg_mcxw71_write((void *) (uintptr_t) ofs,
(const void *) (uintptr_t) (half + ofs), 8192)) {
// The application may already be erased. Stay in RAM for debugger
// recovery rather than return to partially overwritten flash.
for (;;) __NOP();
}
}
__DSB();
SCB->AIRCR = 0x05fa0004U;
__DSB();
for (;;) __NOP();
}
bool mg_ota_begin(size_t new_firmware_size) {
uint32_t status;
if (s_mcxw71_size != 0 || new_firmware_size == 0) return false;
status = MG_MCXW71_FLASH_API->init(&s_mcxw71_config);
if (status != 0 || s_mcxw71_config.mem[0].base != 0 ||
s_mcxw71_config.mem[0].size < 16384 ||
(s_mcxw71_config.mem[0].size % 16384) != 0) {
MG_ERROR(("Flash init failed: %lu", (unsigned long) status));
return false;
}
s_mg_flash_mcxw71.size = s_mcxw71_config.mem[0].size;
if (!mg_ota_flash_begin(new_firmware_size, &s_mg_flash_mcxw71)) return false;
s_mcxw71_size = new_firmware_size;
s_mcxw71_received = s_mcxw71_pending = 0;
s_mcxw71_failed = false;
return true;
}
bool mg_ota_write(const void *buf, size_t len) {
const uint8_t *p = (const uint8_t *) buf;
uint8_t *pending = (uint8_t *) s_mcxw71_buf;
if (s_mcxw71_size == 0 || s_mcxw71_failed) return false;
if (len > s_mcxw71_size - s_mcxw71_received || (len > 0 && buf == NULL)) {
s_mcxw71_failed = true;
return false;
}
s_mcxw71_received += len;
if (s_mcxw71_pending > 0) {
size_t n = 16 - s_mcxw71_pending;
if (n > len) n = len;
if (n > 0) memcpy(pending + s_mcxw71_pending, p, n);
s_mcxw71_pending += n;
if (n > 0) p += n;
len -= n;
if (s_mcxw71_pending == 16) {
s_mcxw71_failed = !mg_ota_flash_write(pending, 16, &s_mg_flash_mcxw71);
s_mcxw71_pending = 0;
}
}
if (!s_mcxw71_failed && len >= 16) {
size_t n = MG_ROUND_DOWN(len, 16);
s_mcxw71_failed = !mg_ota_flash_write(p, n, &s_mg_flash_mcxw71);
p += n, len -= n;
}
if (!s_mcxw71_failed && len > 0) {
memcpy(pending + s_mcxw71_pending, p, len);
s_mcxw71_pending += len;
}
return !s_mcxw71_failed;
}
bool mg_ota_end(void) {
bool ok;
size_t size = s_mcxw71_size;
if (size == 0) return false;
if (s_mcxw71_received != size) s_mcxw71_failed = true;
if (!s_mcxw71_failed && s_mcxw71_pending > 0) {
s_mcxw71_failed =
!mg_ota_flash_write(s_mcxw71_buf, s_mcxw71_pending, &s_mg_flash_mcxw71);
}
ok = mg_ota_flash_end(&s_mg_flash_mcxw71);
s_mcxw71_size = s_mcxw71_pending = 0;
if (ok) {
// Like RW612, this overwrites the application. It does not provide
// rollback or power-loss recovery during the copy. Keep power connected.
MG_INFO(("Applying %lu bytes from staging flash", (unsigned long) size));
mg_mcxw71_apply(size);
}
return false;
}
#endif
+7 -1
View File
@@ -71,8 +71,10 @@ char *mg_mprintf(const char *fmt, ...) {
} }
void mg_pfn_stdout(char c, void *param) { void mg_pfn_stdout(char c, void *param) {
#if MG_ENABLE_POSIX_FS
putchar(c); putchar(c);
(void) param; #endif
(void) param, (void) c;
} }
static size_t print_ip4(void (*out)(char, void *), void *arg, uint8_t *p) { static size_t print_ip4(void (*out)(char, void *), void *arg, uint8_t *p) {
@@ -136,6 +138,10 @@ size_t mg_print_l2addr(void (*out)(char, void *), void *arg, va_list *ap) {
uint8_t *p = va_arg(*ap, uint8_t *); uint8_t *p = va_arg(*ap, uint8_t *);
return print_mac(out, arg, p); return print_mac(out, arg, p);
} break; } break;
case MG_TCPIP_L2_LOWPAN: {
uint8_t *p = va_arg(*ap, uint8_t *);
return print_ieee64(out, arg, p);
} break;
default: break; default: break;
} }
return 0; return 0;
-1
View File
@@ -29,7 +29,6 @@ uint64_t mg_now(void);
// MG_INFO(("every second")); // MG_INFO(("every second"));
// } // }
// Full examples: // Full examples:
// tutorials/http/http-server/arduino/teensy41-http,
// tutorials/mqtt/mqtt-client // tutorials/mqtt/mqtt-client
// Related APIs: // Related APIs:
// mg_millis(), mg_now() // mg_millis(), mg_now()
+1 -1
View File
@@ -90,7 +90,7 @@ void mg_tls_ctx_free(struct mg_mgr *);
#if MG_TLS == MG_TLS_BUILTIN #if MG_TLS == MG_TLS_BUILTIN
// Extracts a 32-byte P-256 private scalar from EC PRIVATE KEY or PKCS#8 PRIVATE // Extracts a 32-byte P-256 private scalar from EC PRIVATE KEY or PKCS#8 PRIVATE
// KEY input. Accepts PEM or DER. Returns key size, or 0 on error. // KEY input. Accepts PEM or DER. Returns key size, or 0 on error.
size_t mg_uecc_parse_private_key(struct mg_str key, uint8_t *buf, size_t len); size_t mg_p256_parse_private_key(struct mg_str key, uint8_t *buf, size_t len);
#endif #endif
// Low-level IO primitives used by TLS layer // Low-level IO primitives used by TLS layer
+221 -188
View File
@@ -6,8 +6,8 @@
#include "tls_aes128.h" #include "tls_aes128.h"
#include "tls_chacha20.h" #include "tls_chacha20.h"
#include "tls_rsa.h" #include "tls_rsa.h"
#include "tls_uecc.h" #include "tls_p256.h"
#include "tls_x25519.h" #include "tls_p384.h"
#include "util.h" #include "util.h"
#if MG_TLS == MG_TLS_BUILTIN #if MG_TLS == MG_TLS_BUILTIN
@@ -61,6 +61,7 @@ enum mg_tls_hs_state {
// Server state machine: // Server state machine:
MG_TLS_STATE_SERVER_START, // Wait for ClientHello MG_TLS_STATE_SERVER_START, // Wait for ClientHello
MG_TLS_STATE_SERVER_RETRY, // Wait for ClientHello after HelloRetry
MG_TLS_STATE_SERVER_WAIT_CERT, // Wait for Certificate MG_TLS_STATE_SERVER_WAIT_CERT, // Wait for Certificate
MG_TLS_STATE_SERVER_WAIT_CV, // Wait for CertificateVerify MG_TLS_STATE_SERVER_WAIT_CV, // Wait for CertificateVerify
MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finish MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finish
@@ -106,9 +107,9 @@ struct tls_data {
uint8_t random[32]; // client random from ClientHello uint8_t random[32]; // client random from ClientHello
uint8_t session_id[32]; // client session ID between the handshake states uint8_t session_id[32]; // client session ID between the handshake states
uint8_t x25519_cli[32]; // client X25519 key between the handshake states
uint8_t p256_cli[32]; // client P-256 key between the handshake states uint8_t p256_cli[32]; // client P-256 key between the handshake states
uint8_t x25519_sec[32]; // ECDHE secret (X25519 or P-256) between the states uint8_t p256_peer[64]; // server: client P-256 public key X || Y
uint8_t ecdhe_sec[32]; // P-256 ECDHE shared secret between the states
bool skip_verification; // do not perform checks on server certificate bool skip_verification; // do not perform checks on server certificate
bool cert_requested; // client received a CertificateRequest bool cert_requested; // client received a CertificateRequest
@@ -132,6 +133,8 @@ struct tls_data {
size_t pubkeysz; // size of the server public key size_t pubkeysz; // size of the server public key
uint8_t sighash[32]; // calculated signature verification hash uint8_t sighash[32]; // calculated signature verification hash
uint8_t sighash384[48]; // same, SHA-384, for ecdsa_secp384r1_sha384 uint8_t sighash384[48]; // same, SHA-384, for ecdsa_secp384r1_sha384
uint64_t hs_start; // mg_millis() when the handshake started
uint64_t hs_proc; // ms spent processing handshake messages
struct tls_enc enc; // actual keys in use at this time struct tls_enc enc; // actual keys in use at this time
struct tls_enc app_keys; // storage during two-way auth handshake struct tls_enc app_keys; // storage during two-way auth handshake
@@ -323,7 +326,7 @@ static void mg_tls_derive_secret(const char *label, uint8_t *key, size_t keysz,
memmove(hash, secret, hashsz); memmove(hash, secret, hashsz);
} }
// at this point we have x25519 shared secret, we can generate a set of derived // at this point we have ECDHE shared secret, we can generate a set of derived
// handshake encryption keys // handshake encryption keys
static void mg_tls_generate_handshake_keys(struct mg_connection *c) { static void mg_tls_generate_handshake_keys(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls; struct tls_data *tls = (struct tls_data *) c->tls;
@@ -344,12 +347,11 @@ static void mg_tls_generate_handshake_keys(struct mg_connection *c) {
mg_tls_derive_secret("tls13 derived", early_secret, 32, zeros_sha256_digest, mg_tls_derive_secret("tls13 derived", early_secret, 32, zeros_sha256_digest,
32, pre_extract_secret, 32); 32, pre_extract_secret, 32);
mg_hmac_sha256(tls->enc.handshake_secret, pre_extract_secret, mg_hmac_sha256(tls->enc.handshake_secret, pre_extract_secret,
sizeof(pre_extract_secret), tls->x25519_sec, sizeof(pre_extract_secret), tls->ecdhe_sec,
sizeof(tls->x25519_sec)); sizeof(tls->ecdhe_sec));
// ECDHE keys and secret are not needed anymore, wipe for forward secrecy // ECDHE keys and secret are not needed anymore, wipe for forward secrecy
mg_bzero(tls->x25519_cli, sizeof(tls->x25519_cli));
mg_bzero(tls->p256_cli, sizeof(tls->p256_cli)); mg_bzero(tls->p256_cli, sizeof(tls->p256_cli));
mg_bzero(tls->x25519_sec, sizeof(tls->x25519_sec)); mg_bzero(tls->ecdhe_sec, sizeof(tls->ecdhe_sec));
mg_tls_hexdump("hs secret", tls->enc.handshake_secret, 32); mg_tls_hexdump("hs secret", tls->enc.handshake_secret, 32);
// mg_sha256_final is not idempotent, need to copy sha256 context to calculate // mg_sha256_final is not idempotent, need to copy sha256 context to calculate
@@ -467,7 +469,7 @@ static bool mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
c->is_client ? tls->enc.client_write_iv : tls->enc.server_write_iv; c->is_client ? tls->enc.client_write_iv : tls->enc.server_write_iv;
if (msgsz > 16384) { if (msgsz > 16384) {
MG_ERROR(("msg longer than recordsz")); mg_error(c, "msg longer than recordsz");
return false; return false;
} }
@@ -512,19 +514,20 @@ static bool mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
static void verbose_alert(uint8_t *buf) { static void verbose_alert(uint8_t *buf) {
uint8_t level = buf[0], desc = buf[1]; uint8_t level = buf[0], desc = buf[1];
MG_INFO(("TLS ALERT received: level=%d, desc=%d (%s)", level, desc, MG_LOG(desc == 0 ? MG_LL_VERBOSE : MG_LL_INFO, // close_notify is normal
desc == 0 ? "close_notify" ("TLS ALERT received: level=%d, desc=%d (%s)", level, desc,
: desc == 10 ? "unexpected_message" desc == 0 ? "close_notify"
: desc == 20 ? "bad_record_mac" : desc == 10 ? "unexpected_message"
: desc == 21 ? "decryption_failed" : desc == 20 ? "bad_record_mac"
: desc == 40 ? "handshake_failure" : desc == 21 ? "decryption_failed"
: desc == 42 ? "bad_certificate" : desc == 40 ? "handshake_failure"
: desc == 43 ? "unsupported_certificate" : desc == 42 ? "bad_certificate"
: desc == 44 ? "certificate_revoked" : desc == 43 ? "unsupported_certificate"
: desc == 45 ? "certificate_expired" : desc == 44 ? "certificate_revoked"
: desc == 46 ? "certificate_unknown" : desc == 45 ? "certificate_expired"
: desc == 48 ? "unknown_ca" : desc == 46 ? "certificate_unknown"
: "unknown")); : desc == 48 ? "unknown_ca"
: "unknown"));
} }
// read an encrypted record, decrypt it in place // read an encrypted record, decrypt it in place
@@ -659,7 +662,8 @@ static void mg_tls_calc_cert_verify_hash(struct mg_connection *c,
if (hash384 != NULL) mg_sha384(hash384, sig_content, sizeof(sig_content)); if (hash384 != NULL) mg_sha384(hash384, sig_content, sizeof(sig_content));
} }
// read and parse ClientHello record // read and parse ClientHello record. Return 0 if it has a P-256 key share,
// 1 if it does not and HelloRetryRequest must be sent, negative otherwise
static int mg_tls_server_recv_hello(struct mg_connection *c) { static int mg_tls_server_recv_hello(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls; struct tls_data *tls = (struct tls_data *) c->tls;
struct mg_iobuf *rio = &c->rtls; struct mg_iobuf *rio = &c->rtls;
@@ -669,9 +673,13 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) {
uint16_t ext_len; uint16_t ext_len;
uint8_t *ext; uint8_t *ext;
uint16_t msgsz; uint16_t msgsz;
bool have_p256 = false;
bool is_retry = tls->state == MG_TLS_STATE_SERVER_RETRY;
if (!mg_tls_got_record(c)) { for (;;) {
return MG_IO_WAIT; if (!mg_tls_got_record(c)) return MG_IO_WAIT;
if (!is_retry || rio->buf[0] != MG_TLS_CHANGE_CIPHER) break;
mg_tls_drop_record(c); // compatibility mode CCS before 2nd ClientHello
} }
if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) { if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) {
mg_error(c, "not a client hello packet"); mg_error(c, "not a client hello packet");
@@ -715,16 +723,21 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) {
goto fail; goto fail;
for (k = 0; k < key_exchange_len;) { for (k = 0; k < key_exchange_len;) {
uint16_t m = MG_LOAD_BE16(key_exchange + k + 2); uint16_t m = MG_LOAD_BE16(key_exchange + k + 2);
uint16_t group = MG_LOAD_BE16(key_exchange + k);
if (((uint32_t) m + k + 4) > key_exchange_len) goto fail; if (((uint32_t) m + k + 4) > key_exchange_len) goto fail;
if (m == 32 && key_exchange[k] == 0x00 && key_exchange[k + 1] == 0x1d) { // secp256r1, uncompressed point 0x04 || X || Y
memmove(tls->x25519_cli, key_exchange + k + 4, m); if (group == 0x0017 && m == 65 && key_exchange[k + 4] == 0x04) {
mg_tls_drop_record(c); memmove(tls->p256_peer, key_exchange + k + 5, 64);
return 0; have_p256 = true;
} }
k += (uint16_t) (m + 4); k += (uint16_t) (m + 4);
} }
j += (uint16_t) (n + 4); j += (uint16_t) (n + 4);
} }
if (have_p256 || !is_retry) { // No P-256 share: ask for it, but only once
mg_tls_drop_record(c);
return have_p256 ? 0 : 1;
}
fail: fail:
mg_error(c, "bad client hello"); mg_error(c, "bad client hello");
return -1; return -1;
@@ -738,58 +751,98 @@ fail:
static bool mg_tls_server_send_hello(struct mg_connection *c) { static bool mg_tls_server_send_hello(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls; struct tls_data *tls = (struct tls_data *) c->tls;
struct mg_iobuf *wio = &tls->send; struct mg_iobuf *wio = &tls->send;
uint8_t hello[155], pub[65], prv[32], hdr[5] = {0x16, 0x03, 0x03, 0, 0};
size_t n = sizeof(hello); // ServerHello length, including 4-byte header
// clang-format off // calculate P-256 keyshare and the shared secret
uint8_t msg_server_hello[122] = { pub[0] = 0x04; // uncompressed point 0x04 || X || Y
// server hello, tls 1.2 if (!mg_p256_make_key(pub + 1, prv) ||
0x02, 0x00, 0x00, 0x76, 0x03, 0x03, !mg_p256_shared_secret(tls->p256_peer, prv, tls->ecdhe_sec)) {
// random (32 bytes) mg_bzero(prv, sizeof(prv));
PLACEHOLDER_32B,
// session ID length + session ID (32 bytes)
0x20, PLACEHOLDER_32B,
#if MG_ENABLE_CHACHA20
// TLS_CHACHA20_POLY1305_SHA256 + no compression
0x13, 0x03, 0x00,
#else
// TLS_AES_128_GCM_SHA256 + no compression
0x13, 0x01, 0x00,
#endif
// extensions + keyshare
0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20,
// x25519 keyshare
PLACEHOLDER_32B,
// supported versions (tls1.3 == 0x304)
0x00, 0x2b, 0x00, 0x02, 0x03, 0x04};
// clang-format on
// calculate keyshare
uint8_t x25519_pub[X25519_BYTES];
uint8_t x25519_prv[X25519_BYTES];
if (!mg_random(x25519_prv, sizeof(x25519_prv))) mg_error(c, "RNG");
if( mg_tls_x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1) < 0 || mg_tls_x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1) < 0) {
mg_error(c, "bad key"); mg_error(c, "bad key");
return false; return false;
} }
mg_tls_hexdump("s x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec)); mg_bzero(prv, sizeof(prv));
mg_tls_hexdump("s ecdhe sec", tls->ecdhe_sec, sizeof(tls->ecdhe_sec));
// fill in the gaps: random + session ID + keyshare hello[0] = 0x02; // server hello, tls 1.2
memmove(msg_server_hello + 6, tls->random, sizeof(tls->random)); MG_STORE_BE24(hello + 1, n - 4);
memmove(msg_server_hello + 39, tls->session_id, sizeof(tls->session_id)); hello[4] = 0x03, hello[5] = 0x03;
memmove(msg_server_hello + 84, x25519_pub, sizeof(x25519_pub)); if (!mg_random(hello + 6, 32)) { // server random, RFC 8446 4.1.3
mg_error(c, "RNG");
return false;
}
hello[38] = 0x20; // session ID length + session ID (32 bytes)
memmove(hello + 39, tls->session_id, sizeof(tls->session_id));
#if MG_ENABLE_CHACHA20
hello[71] = 0x13, hello[72] = 0x03; // TLS_CHACHA20_POLY1305_SHA256
#else
hello[71] = 0x13, hello[72] = 0x01; // TLS_AES_128_GCM_SHA256
#endif
hello[73] = 0x00; // no compression
MG_STORE_BE16(hello + 74, 65 + 14); // extensions length
MG_STORE_BE16(hello + 76, 0x0033); // key share
MG_STORE_BE16(hello + 78, 65 + 4);
MG_STORE_BE16(hello + 80, 0x0017); // secp256r1
MG_STORE_BE16(hello + 82, 65);
memmove(hello + 84, pub, sizeof(pub));
memmove(hello + 149, "\x00\x2b\x00\x02\x03\x04", 6); // tls1.3 == 0x304
MG_STORE_BE16(hdr + 3, n);
// server hello message // server hello message
if (mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5) == 0 || if (mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) == 0 ||
mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello)) == mg_iobuf_add(wio, wio->len, hello, n) == 0)
0)
return false; return false;
mg_sha256_update(&tls->sha256, msg_server_hello, sizeof(msg_server_hello)); mg_sha256_update(&tls->sha256, hello, n);
// change cipher message // change cipher message, unless it was already sent after HelloRetryRequest
if (mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0) if (tls->state != MG_TLS_STATE_SERVER_RETRY &&
mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0)
return false; return false;
return true; return true;
} }
// HelloRetryRequest random, SHA-256 of "HelloRetryRequest", RFC 8446 4.1.3
static const uint8_t hrr_random[32] = {
0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, 0xbe, 0x1d, 0x8c,
0x02, 0x1e, 0x65, 0xb8, 0x91, 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb,
0x8c, 0x5e, 0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c};
// put HelloRetryRequest record into wio buffer, asking for a P-256 key share.
// Replace ClientHello in the transcript with message_hash, RFC 8446 4.4.1
static bool mg_tls_server_send_hrr(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
struct mg_iobuf *wio = &tls->send;
uint8_t hrr[88], hdr[5] = {0x16, 0x03, 0x03, 0, sizeof(hrr)};
uint8_t mh[4 + 32] = {0xfe, 0, 0, 32}; // message_hash(ClientHello1)
hrr[0] = 0x02; // server hello, tls 1.2
MG_STORE_BE24(hrr + 1, sizeof(hrr) - 4);
hrr[4] = 0x03, hrr[5] = 0x03;
memmove(hrr + 6, hrr_random, sizeof(hrr_random));
hrr[38] = 0x20; // session ID length + session ID (32 bytes)
memmove(hrr + 39, tls->session_id, sizeof(tls->session_id));
#if MG_ENABLE_CHACHA20
hrr[71] = 0x13, hrr[72] = 0x03; // TLS_CHACHA20_POLY1305_SHA256
#else
hrr[71] = 0x13, hrr[72] = 0x01; // TLS_AES_128_GCM_SHA256
#endif
hrr[73] = 0x00; // no compression
// extensions: supported versions tls1.3, key share secp256r1 (group only)
memmove(hrr + 74, "\x00\x0c\x00\x2b\x00\x02\x03\x04\x00\x33\x00\x02\x00\x17",
14);
mg_sha256_final(mh + 4, &tls->sha256);
mg_sha256_init(&tls->sha256);
mg_sha256_update(&tls->sha256, mh, sizeof(mh));
mg_sha256_update(&tls->sha256, hrr, sizeof(hrr));
// HelloRetryRequest message, followed by change cipher message
return mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) != 0 &&
mg_iobuf_add(wio, wio->len, hrr, sizeof(hrr)) != 0 &&
mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) != 0;
}
static bool mg_tls_server_send_ext(struct mg_connection *c) { static bool mg_tls_server_send_ext(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls; struct tls_data *tls = (struct tls_data *) c->tls;
// server extensions // server extensions
@@ -799,14 +852,10 @@ static bool mg_tls_server_send_ext(struct mg_connection *c) {
} }
// signature algorithms we actually support: ecdsa_secp256r1_sha256, // signature algorithms we actually support: ecdsa_secp256r1_sha256,
// ecdsa_secp384r1_sha384 (if enabled), rsa_pss_rsae_sha256, rsa_pkcs1_sha256 // ecdsa_secp384r1_sha384, rsa_pss_rsae_sha256, rsa_pkcs1_sha256
static const uint8_t secp256r1_sig_algs[] = { static const uint8_t secp256r1_sig_algs[] = {
#if MG_UECC_SUPPORTS_secp384r1
0x00, 0x0d, 0x00, 0x0a, 0x00, 0x08, 0x04, 0x03, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x08, 0x04, 0x03,
0x05, 0x03, 0x08, 0x04, 0x04, 0x01}; 0x05, 0x03, 0x08, 0x04, 0x04, 0x01};
#else
0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01};
#endif
static bool mg_tls_server_send_cert_request(struct mg_connection *c) { static bool mg_tls_server_send_cert_request(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls; struct tls_data *tls = (struct tls_data *) c->tls;
@@ -878,28 +927,6 @@ static bool mg_tls_send_cert(struct mg_connection *c, bool is_client) {
return res; return res;
} }
// type adapter between uECC hash context and our sha256 implementation
typedef struct SHA256_HashContext {
MG_UECC_HashContext uECC;
mg_sha256_ctx ctx;
} SHA256_HashContext;
static void init_SHA256(const MG_UECC_HashContext *base) {
SHA256_HashContext *c = (SHA256_HashContext *) base;
mg_sha256_init(&c->ctx);
}
static void update_SHA256(const MG_UECC_HashContext *base,
const uint8_t *message, unsigned message_size) {
SHA256_HashContext *c = (SHA256_HashContext *) base;
mg_sha256_update(&c->ctx, message, message_size);
}
static void finish_SHA256(const MG_UECC_HashContext *base,
uint8_t *hash_result) {
SHA256_HashContext *c = (SHA256_HashContext *) base;
mg_sha256_final(hash_result, &c->ctx);
}
static void mg_tls_mgf1(uint8_t *mask, size_t mask_len, const uint8_t *seed, static void mg_tls_mgf1(uint8_t *mask, size_t mask_len, const uint8_t *seed,
size_t seed_len) { size_t seed_len) {
uint32_t counter = 0; uint32_t counter = 0;
@@ -1103,19 +1130,19 @@ static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
// Check bounds // Check bounds
if (emlen > sizeof(em) || verifysz > sizeof(verify)) { if (emlen > sizeof(em) || verifysz > sizeof(verify)) {
MG_ERROR(("RSA key too large for static buffers")); mg_error(c, "RSA key too large for static buffers");
return false; return false;
} }
if (!mg_tls_pss_encode(hash, sizeof(hash), &rn, em)) { if (!mg_tls_pss_encode(hash, sizeof(hash), &rn, em)) {
MG_ERROR(("Failed PSS encode")); mg_error(c, "Failed PSS encode");
return false; return false;
} }
// Validate PSS encoded message format // Validate PSS encoded message format
if (em[emlen - 1] != 0xbc) { if (em[emlen - 1] != 0xbc) {
MG_ERROR(("Invalid PSS encoding: last byte is 0x%02x, expected 0xbc", mg_error(c, "Invalid PSS encoding: last byte is 0x%02x, expected 0xbc",
em[emlen - 1])); em[emlen - 1]);
return false; return false;
} }
@@ -1128,7 +1155,7 @@ static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
// Sign directly into the verify buffer (verify + 8 = signature location) // Sign directly into the verify buffer (verify + 8 = signature location)
memset(verify + 8, 0, emlen); // Initialize signature area memset(verify + 8, 0, emlen); // Initialize signature area
if (!mg_tls_rsa_sign(tls, em, emlen, verify + 8)) { if (!mg_tls_rsa_sign(tls, em, emlen, verify + 8)) {
MG_ERROR(("Failed RSA sign")); mg_error(c, "Failed RSA sign");
return false; return false;
} }
@@ -1142,15 +1169,13 @@ static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
} else { } else {
// EC certificate verify packet // EC certificate verify packet
uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00}; uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00};
uint8_t tmp[2 * 32 + 64] = {0};
struct SHA256_HashContext ctx = {
{&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp},
{{0}, 0, 0, {0}}};
size_t sigsz, verifysz = 0; size_t sigsz, verifysz = 0;
int neg1, neg2; int neg1, neg2;
uint8_t sig[64] = {0}; uint8_t sig[64] = {0};
mg_uecc_sign_deterministic(tls->ec_key, hash, sizeof(hash), &ctx.uECC, sig, if (!mg_p256_sign(tls->ec_key, hash, sizeof(hash), sig)) {
mg_uecc_secp256r1()); mg_error(c, "P-256 sign");
return false;
}
neg1 = !!(sig[0] & 0x80); neg1 = !!(sig[0] & 0x80);
neg2 = !!(sig[32] & 0x80); neg2 = !!(sig[32] & 0x80);
@@ -1213,9 +1238,6 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls; struct tls_data *tls = (struct tls_data *) c->tls;
struct mg_iobuf *wio = &tls->send; struct mg_iobuf *wio = &tls->send;
uint8_t x25519_pub[X25519_BYTES];
uint8_t p256_pub[64]; // X || Y, sent as an uncompressed point 0x04 || X || Y
// - "signature algorithms we actually support", see above // - "signature algorithms we actually support", see above
// uint8_t secp256r1_sig_algs[] // uint8_t secp256r1_sig_algs[]
// - all popular signature algorithms (if we don't care about verification) // - all popular signature algorithms (if we don't care about verification)
@@ -1227,11 +1249,11 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) {
0xfe, 0x00, 0x00, 0xfe}; 0xfe, 0x00, 0x00, 0xfe};
// clang-format off // clang-format off
uint8_t msg_client_hello[216] = { uint8_t msg_client_hello[178] = {
// TLS Client Hello header reported as TLS1.2 (5) // TLS Client Hello header reported as TLS1.2 (5)
0x16, 0x03, 0x03, 0x00, 0xfe, 0x16, 0x03, 0x03, 0x00, 0xd8,
// client hello, tls 1.2 (6) // client hello, tls 1.2 (6)
0x01, 0x00, 0x00, 0x8c, 0x03, 0x03, 0x01, 0x00, 0x00, 0xd4, 0x03, 0x03,
// random (32 bytes) // random (32 bytes)
PLACEHOLDER_32B, PLACEHOLDER_32B,
// session ID length + session ID (32 bytes) // session ID length + session ID (32 bytes)
@@ -1248,17 +1270,15 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) {
0x01, 0x00, 0x01, 0x00,
// extensions + keyshare // extensions + keyshare
0x00, 0xfe, 0x00, 0xfe,
// keyshare: x25519
0x00, 0x33, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x1d, 0x00, 0x20,
PLACEHOLDER_32B,
// keyshare: secp256r1, uncompressed point // keyshare: secp256r1, uncompressed point
0x00, 0x33, 0x00, 0x47, 0x00, 0x45,
0x00, 0x17, 0x00, 0x41, 0x04, PLACEHOLDER_32B, PLACEHOLDER_32B, 0x00, 0x17, 0x00, 0x41, 0x04, PLACEHOLDER_32B, PLACEHOLDER_32B,
// supported groups (x25519, secp256r1) // supported groups (secp256r1)
0x00, 0x0a, 0x00, 0x06, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x17,
// supported versions (tls1.3 == 0x304) // supported versions (tls1.3 == 0x304)
0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04, 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04,
// session ticket (none) // session ticket (none)
0x00, 0x23, 0x00, 0x00, // 215 bytes till here 0x00, 0x23, 0x00, 0x00, // 177 bytes till here
}; };
// clang-format on // clang-format on
const char *hostname = tls->hostname; const char *hostname = tls->hostname;
@@ -1271,11 +1291,11 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) {
// patch ClientHello with correct hostname ext length (if any) // patch ClientHello with correct hostname ext length (if any)
MG_STORE_BE16(msg_client_hello + 3, MG_STORE_BE16(msg_client_hello + 3,
hostname_extsz + 254 - 9 - 34 + sig_alg_sz); hostname_extsz + 216 - 9 - 34 + sig_alg_sz);
MG_STORE_BE16(msg_client_hello + 7, MG_STORE_BE16(msg_client_hello + 7,
hostname_extsz + 250 - 9 - 34 + sig_alg_sz); hostname_extsz + 212 - 9 - 34 + sig_alg_sz);
MG_STORE_BE16(msg_client_hello + 82, MG_STORE_BE16(msg_client_hello + 82,
hostname_extsz + 175 - 9 - 34 + sig_alg_sz); hostname_extsz + 137 - 9 - 34 + sig_alg_sz);
if (hostnamesz > 0) { if (hostnamesz > 0) {
MG_STORE_BE16(server_name_ext + 2, hostnamesz + 5); MG_STORE_BE16(server_name_ext + 2, hostnamesz + 5);
@@ -1283,21 +1303,20 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) {
MG_STORE_BE16(server_name_ext + 7, hostnamesz); MG_STORE_BE16(server_name_ext + 7, hostnamesz);
} }
// calculate keyshares // calc+fill keyshare; X || Y, sent as an uncompressed point 0x04 || X || Y
if (!mg_random(tls->x25519_cli, sizeof(tls->x25519_cli))) mg_error(c, "RNG"); if (!mg_p256_make_key(msg_client_hello + 95, tls->p256_cli)) {
mg_tls_x25519(x25519_pub, tls->x25519_cli, X25519_BASE_POINT, 1);
if (!mg_uecc_make_key(p256_pub, tls->p256_cli, mg_uecc_secp256r1())) {
mg_error(c, "P-256 key"); mg_error(c, "P-256 key");
return false; // Do not send uninitialised p256_pub return false;
} }
// fill in the gaps: random + session ID + keyshares // fill in the gaps: random + session ID
if (!mg_random(tls->session_id, sizeof(tls->session_id))) mg_error(c, "RNG"); if (!mg_random(tls->session_id, sizeof(tls->session_id)) ||
if (!mg_random(tls->random, sizeof(tls->random))) mg_error(c, "RNG"); !mg_random(tls->random, sizeof(tls->random))) {
mg_error(c, "RNG");
return false;
}
memmove(msg_client_hello + 11, tls->random, sizeof(tls->random)); memmove(msg_client_hello + 11, tls->random, sizeof(tls->random));
memmove(msg_client_hello + 44, tls->session_id, sizeof(tls->session_id)); memmove(msg_client_hello + 44, tls->session_id, sizeof(tls->session_id));
memmove(msg_client_hello + 94, x25519_pub, sizeof(x25519_pub));
memmove(msg_client_hello + 131, p256_pub, sizeof(p256_pub));
// client hello message // client hello message
if (mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)) == if (mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)) ==
@@ -1370,26 +1389,17 @@ static int mg_tls_client_recv_hello(struct mg_connection *c) {
key_exchange_len = MG_LOAD_BE16(ext + j + 6); key_exchange_len = MG_LOAD_BE16(ext + j + 6);
key_exchange = ext + j + 8; key_exchange = ext + j + 8;
if (key_exchange_len > ext_len2 - 4) goto fail; if (key_exchange_len > ext_len2 - 4) goto fail;
if (group == 0x001d) { // x25519 if (group != 0x0017) { // secp256r1, uncompressed point 0x04 || X || Y
if (key_exchange_len != 32 ||
mg_tls_x25519(tls->x25519_sec, tls->x25519_cli, key_exchange, 1) <
0) {
mg_error(c, "bad key");
return -1;
}
} else if (group == 0x0017) { // secp256r1, uncompressed point 0x04 || X || Y
if (key_exchange_len != 65 || key_exchange[0] != 0x04 ||
!mg_uecc_valid_public_key(key_exchange + 1, mg_uecc_secp256r1()) ||
!mg_uecc_shared_secret(key_exchange + 1, tls->p256_cli,
tls->x25519_sec, mg_uecc_secp256r1())) {
mg_error(c, "bad key");
return -1;
}
} else {
mg_error(c, "bad key exchange group"); mg_error(c, "bad key exchange group");
return -1; return -1;
} }
// mg_tls_hexdump("c ecdhe sec", tls->x25519_sec, 32); if (key_exchange_len != 65 || key_exchange[0] != 0x04 ||
!mg_p256_shared_secret(key_exchange + 1, tls->p256_cli,
tls->ecdhe_sec)) { // Validates the point
mg_error(c, "bad key");
return -1;
}
// mg_tls_hexdump("c ecdhe sec", tls->ecdhe_sec, 32);
mg_tls_drop_record(c); mg_tls_drop_record(c);
/* generate handshake keys */ /* generate handshake keys */
mg_tls_generate_handshake_keys(c); mg_tls_generate_handshake_keys(c);
@@ -1714,7 +1724,6 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
MG_ERROR(("cert verification error")); MG_ERROR(("cert verification error"));
return 0; return 0;
} }
#if MG_UECC_SUPPORTS_secp256r1
if (issuer->pubkey.len == 64) { if (issuer->pubkey.len == 64) {
const uint32_t N = 32; const uint32_t N = 32;
if (a.len > N) a.value += (a.len - N), a.len = N; // padding if (a.len > N) a.value += (a.len - N), a.len = N; // padding
@@ -1723,13 +1732,9 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
memmove(sig + (N - a.len), a.value, a.len); memmove(sig + (N - a.len), a.value, a.len);
memset(sig + N, 0, N - b.len); memset(sig + N, 0, N - b.len);
memmove(sig + N + (N - b.len), b.value, b.len); memmove(sig + N + (N - b.len), b.value, b.len);
return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, return mg_p256_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
(unsigned) cert->tbshashsz, sig, cert->tbshashsz, sig);
mg_uecc_secp256r1()); } else if (issuer->pubkey.len == 96) {
} else
#endif
#if MG_UECC_SUPPORTS_secp384r1
if (issuer->pubkey.len == 96) {
const uint32_t N = 48; const uint32_t N = 48;
if (a.len > N) a.value += (a.len - N), a.len = N; // padding if (a.len > N) a.value += (a.len - N), a.len = N; // padding
if (b.len > N) b.value += (b.len - N), b.len = N; if (b.len > N) b.value += (b.len - N), b.len = N;
@@ -1737,12 +1742,9 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
memmove(sig + (N - a.len), a.value, a.len); memmove(sig + (N - a.len), a.value, a.len);
memset(sig + N, 0, N - b.len); memset(sig + N, 0, N - b.len);
memmove(sig + N + (N - b.len), b.value, b.len); memmove(sig + N + (N - b.len), b.value, b.len);
return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, return mg_p384_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
(unsigned) cert->tbshashsz, sig, cert->tbshashsz, sig);
mg_uecc_secp384r1()); } else {
} else
#endif
{
MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len)); MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len));
return 0; return 0;
} }
@@ -1931,6 +1933,18 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
} // else r = 0 or -2 => subj not found or pubkey not matching } // else r = 0 or -2 => subj not found or pubkey not matching
} }
// Single CA: accept the chain if the CA itself is in it, e.g. a
// cross-signed copy of the root. Same subject and same public key
// means the same trusted key, and the links below ci are verified
if (tls->ca_der.len > 0 && ca.subj.len == ci->subj.len &&
memcmp(ca.subj.value, ci->subj.value, ca.subj.len) == 0 &&
ca.pubkey.len == ci->pubkey.len &&
memcmp(ca.pubkey.buf, ci->pubkey.buf, ca.pubkey.len) == 0) {
found_ca = true;
MG_VERBOSE(("CA found in chain at depth %d", certnum - 1));
break;
}
if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) { if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) {
mg_error(c, "too many certificates in the chain"); mg_error(c, "too many certificates in the chain");
return -1; return -1;
@@ -2028,8 +2042,7 @@ static int mg_tls_recv_cert_verify(struct mg_connection *c) {
return -1; return -1;
} }
MG_VERBOSE(("certificate verification successful (RSA)")); MG_VERBOSE(("certificate verification successful (RSA)"));
} else if (sigalg == 0x0403 || } else if (sigalg == 0x0403 || sigalg == 0x0503) {
(sigalg == 0x0503 && MG_UECC_SUPPORTS_secp384r1)) {
// ecdsa_secp256r1_sha256 or ecdsa_secp384r1_sha384. Extract certificate // ecdsa_secp256r1_sha256 or ecdsa_secp384r1_sha384. Extract certificate
// signature and verify it using pubkey and sighash // signature and verify it using pubkey and sighash
bool is384 = sigalg == 0x0503; bool is384 = sigalg == 0x0503;
@@ -2062,15 +2075,12 @@ static int mg_tls_recv_cert_verify(struct mg_connection *c) {
memmove(sig + (N - r.len), r.value, r.len); memmove(sig + (N - r.len), r.value, r.len);
memmove(sig + N + (N - s.len), s.value, s.len); memmove(sig + N + (N - s.len), s.value, s.len);
#if MG_UECC_SUPPORTS_secp384r1
if (is384) { if (is384) {
ok = mg_uecc_verify(tls->pubkey, tls->sighash384, ok = mg_p384_verify(tls->pubkey, tls->sighash384,
sizeof(tls->sighash384), sig, mg_uecc_secp384r1()); sizeof(tls->sighash384), sig);
} else } else {
#endif ok = mg_p256_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash),
{ sig);
ok = mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash),
sig, mg_uecc_secp256r1());
} }
if (ok != 1) { if (ok != 1) {
mg_error(c, "failed to verify EC certificate (certverify)"); mg_error(c, "failed to verify EC certificate (certverify)");
@@ -2087,8 +2097,8 @@ static int mg_tls_recv_cert_verify(struct mg_connection *c) {
// 0809 = rsa_pss_pss_sha256 // 0809 = rsa_pss_pss_sha256
// 080A = rsa_pss_pss_sha384 // 080A = rsa_pss_pss_sha384
// 080B = rsa_pss_pss_sha512 // 080B = rsa_pss_pss_sha512
MG_ERROR(("unsupported certverify signature scheme: %x of %d bytes", mg_error(c, "unsupported certverify signature scheme: %x of %d bytes",
sigalg, siglen)); sigalg, siglen);
return -1; return -1;
} }
} }
@@ -2174,16 +2184,23 @@ static bool mg_tls_client_handshake(struct mg_connection *c) {
static bool mg_tls_server_handshake(struct mg_connection *c) { static bool mg_tls_server_handshake(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls; struct tls_data *tls = (struct tls_data *) c->tls;
int r;
switch (tls->state) { switch (tls->state) {
case MG_TLS_STATE_SERVER_START: case MG_TLS_STATE_SERVER_START:
if (mg_tls_server_recv_hello(c) < 0) break; case MG_TLS_STATE_SERVER_RETRY:
if ((r = mg_tls_server_recv_hello(c)) < 0) break;
if (r == 1) { // No P-256 key share, ask for it
if (!mg_tls_server_send_hrr(c)) return false;
tls->state = MG_TLS_STATE_SERVER_RETRY;
break;
}
if (!mg_tls_server_send_hello(c)) return false; if (!mg_tls_server_send_hello(c)) return false;
mg_tls_generate_handshake_keys(c); mg_tls_generate_handshake_keys(c);
if (!mg_tls_server_send_ext(c)) return false; if (!mg_tls_server_send_ext(c)) return false;
if (tls->is_twoway && !mg_tls_server_send_cert_request(c)) return false; if (tls->is_twoway && !mg_tls_server_send_cert_request(c)) return false;
if (!mg_tls_send_cert(c, false) || !mg_tls_send_cert_verify(c, false) || if (!mg_tls_send_cert(c, false)) return false;
!mg_tls_server_send_finish(c)) if (!mg_tls_send_cert_verify(c, false)) return false;
return false; if (!mg_tls_server_send_finish(c)) return false;
if (tls->is_twoway) { if (tls->is_twoway) {
// generate application keys at this point, keep using handshake keys // generate application keys at this point, keep using handshake keys
struct tls_enc hs_keys = tls->enc; struct tls_enc hs_keys = tls->enc;
@@ -2224,16 +2241,27 @@ void mg_tls_handshake(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls; struct tls_data *tls = (struct tls_data *) c->tls;
long n; long n;
bool res; bool res;
uint64_t t;
if (c->is_closing) return; // we don't clear rx buf, so ignore what's left if (c->is_closing) return; // we don't clear rx buf, so ignore what's left
if (tls->is_sntp_pending) return; if (tls->is_sntp_pending) return;
t = mg_millis();
if (c->is_client) { if (c->is_client) {
// will clear is_hs when sending last chunk // will clear is_hs when sending last chunk
res = mg_tls_client_handshake(c); res = mg_tls_client_handshake(c);
} else { } else {
res = mg_tls_server_handshake(c); res = mg_tls_server_handshake(c);
} }
if (!res) { tls->hs_proc += mg_millis() - t;
mg_error(c, "TLS OOM"); if (res && !c->is_tls_hs) {
// CPU: key exchange, signing, certificate verification. Wait: the rest of
// the total, waiting for the peer and the network
unsigned long total = (unsigned long) (mg_millis() - tls->hs_start);
unsigned long cpu = (unsigned long) tls->hs_proc;
MG_DEBUG(("%lu TLS handshake done in %lu ms: %lu ms CPU, %lu ms peer wait",
c->id, total, cpu, total > cpu ? total - cpu : 0));
}
if (!res) { // already mg_error()ed at handshake helper ?
if (!c->is_closing) mg_error(c, "TLS OOM"); // No, do it
return; return;
} }
while (tls->send.len > 0 && while (tls->send.len > 0 &&
@@ -2650,7 +2678,7 @@ static int mg_parse_pem_certs(const struct mg_str pem, struct mg_str **ders) {
return count; return count;
} }
size_t mg_uecc_parse_private_key(struct mg_str key, uint8_t *buf, size_t len) { size_t mg_p256_parse_private_key(struct mg_str key, uint8_t *buf, size_t len) {
struct mg_str der = mg_str_n(NULL, 0); struct mg_str der = mg_str_n(NULL, 0);
size_t n = 0; size_t n = 0;
if (buf == NULL || len < 32) return 0; if (buf == NULL || len < 32) return 0;
@@ -2690,6 +2718,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
mg_error(c, "tls oom"); mg_error(c, "tls oom");
return; return;
} }
tls->hs_start = mg_millis();
tls->state = tls->state =
c->is_client ? MG_TLS_STATE_CLIENT_START : MG_TLS_STATE_SERVER_START; c->is_client ? MG_TLS_STATE_CLIENT_START : MG_TLS_STATE_SERVER_START;
@@ -2733,7 +2762,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
if (!c->is_client) tls->is_twoway = true; // server + CA: two-way auth if (!c->is_client) tls->is_twoway = true; // server + CA: two-way auth
} }
if (opts->cert.buf == NULL) { if (opts->cert.buf == NULL || opts->cert.len == 0) {
MG_VERBOSE(("No certificate provided")); MG_VERBOSE(("No certificate provided"));
goto xit; goto xit;
} }
@@ -2762,7 +2791,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
} }
// parse PEM or DER EC key // parse PEM or DER EC key
if (opts->key.buf == NULL) { if (opts->key.buf == NULL || opts->key.len == 0) {
mg_error(c, "Certificate provided without a private key"); mg_error(c, "Certificate provided without a private key");
return; return;
} }
@@ -2875,11 +2904,15 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
long n = MG_IO_WAIT; long n = MG_IO_WAIT;
bool was_throttled = c->is_tls_throttled; // see #3074 bool was_throttled = c->is_tls_throttled; // see #3074
if (!was_throttled) { // encrypt new data if (!was_throttled) { // encrypt new data
size_t i, rs = MG_TLS_RECORD_SIZE > 16384 ? 16384 : MG_TLS_RECORD_SIZE;
if (len > MG_IO_SIZE) len = MG_IO_SIZE; if (len > MG_IO_SIZE) len = MG_IO_SIZE;
if (len > 16384) len = 16384; for (i = 0; i < len; i += rs) { // Split into records, send them at once
if (!mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA)) size_t m = len - i < rs ? len - i : rs;
return 0; // returning 0 means an OOM condition (iobuf couldn't resize), if (!mg_tls_encrypt(c, (const uint8_t *) buf + i, m, MG_TLS_APP_DATA))
// yet this is so far recoverable, let the caller decide return 0; // returning 0 means an OOM condition (iobuf couldn't
// resize), yet this is so far recoverable, let the caller
// decide
}
} // else, resend outstanding encrypted data in tls->send } // else, resend outstanding encrypted data in tls->send
while (tls->send.len > 0 && while (tls->send.len > 0 &&
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) { (n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
-3
View File
@@ -60,9 +60,6 @@ static PORTABLE_8439_DECL void poly1305_finish(poly1305_context *ctx,
// ******* END: poly1305-donna/poly1305-donna.h ******** // ******* END: poly1305-donna/poly1305-donna.h ********
// ******* BEGIN: chacha-portable.c ******** // ******* BEGIN: chacha-portable.c ********
#include <assert.h>
#include <string.h>
// this is a fresh implementation of chacha20, based on the description in // this is a fresh implementation of chacha20, based on the description in
// rfc8349 it's such a nice compact algorithm that it is easy to do. In // rfc8349 it's such a nice compact algorithm that it is easy to do. In
// relationship to other c implementation this implementation: // relationship to other c implementation this implementation:
+2553
View File
File diff suppressed because it is too large. Load diff
+181
View File
@@ -0,0 +1,181 @@
// P-256 API, mg_p256_*(), used by built-in TLS, JWT and OTA. Implemented by
// BearSSL code below (MG_P256=MG_P256_BEAR) or tls_p256_cortex.c (LENNGREN)
// BearSSL code: https://bearssl.org commit 7bea48e. Shared bignum and ECDSA
// code is always compiled, it is also used by P-384, see tls_p384.c
//
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
//
// 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 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.
//
// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, as are the
// constant-time helpers from inner.h (NOT, MUX, EQ, ...). Structs are not
// typedef-ed. ECDSA signing takes the caller's k instead of HMAC-DRBG, k is
// computed by mg_p256_sign() (RFC 6979). ECDSA functions take the curve id and
// public key directly
#pragma once
#include "arch.h"
#include "config.h"
#include "str.h"
#include "tls.h"
#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
#define MG_BR_EC_secp256r1 23 // Curve IDs, same as TLS named groups
#define MG_BR_EC_secp384r1 24
#define MG_BR_MAX_EC_SIZE 384 // Largest supported curve, bits
// From BearSSL inc/bearssl_ec.h: an EC implementation
struct mg_br_ec_impl {
uint32_t supported_curves; // Bit mask of curve IDs
const unsigned char *(*generator)(int curve, size_t *len);
const unsigned char *(*order)(int curve, size_t *len);
size_t (*xoff)(int curve, size_t *len);
uint32_t (*mul)(unsigned char *G, size_t Glen, const unsigned char *x,
size_t xlen, int curve);
size_t (*mulgen)(unsigned char *R, const unsigned char *x, size_t xlen,
int curve);
uint32_t (*muladd)(unsigned char *A, const unsigned char *B, size_t len,
const unsigned char *x, size_t xlen,
const unsigned char *y, size_t ylen, int curve);
};
// From BearSSL src/inner.h: constant-time helpers
static inline uint32_t MG_BR_NOT(uint32_t ctl) {
return ctl ^ 1;
}
static inline uint32_t MG_BR_MUX(uint32_t ctl, uint32_t x, uint32_t y) {
return y ^ (-ctl & (x ^ y));
}
static inline uint32_t MG_BR_EQ(uint32_t x, uint32_t y) {
uint32_t q = x ^ y;
return MG_BR_NOT((q | -q) >> 31);
}
static inline uint32_t MG_BR_NEQ(uint32_t x, uint32_t y) {
uint32_t q = x ^ y;
return (q | -q) >> 31;
}
static inline uint32_t MG_BR_GT(uint32_t x, uint32_t y) {
uint32_t z = y - x;
return (z ^ ((x ^ y) & (x ^ z))) >> 31;
}
#define MG_BR_GE(x, y) MG_BR_NOT(MG_BR_GT(y, x))
#define MG_BR_LT(x, y) MG_BR_GT(y, x)
static inline int32_t MG_BR_CMP(uint32_t x, uint32_t y) {
return (int32_t) MG_BR_GT(x, y) | -(int32_t) MG_BR_GT(y, x);
}
static inline uint32_t MG_BR_BIT_LENGTH(uint32_t x) {
uint32_t k, c;
k = MG_BR_NEQ(x, 0);
c = MG_BR_GT(x, 0xFFFF), x = MG_BR_MUX(c, x >> 16, x), k += c << 4;
c = MG_BR_GT(x, 0x00FF), x = MG_BR_MUX(c, x >> 8, x), k += c << 3;
c = MG_BR_GT(x, 0x000F), x = MG_BR_MUX(c, x >> 4, x), k += c << 2;
c = MG_BR_GT(x, 0x0003), x = MG_BR_MUX(c, x >> 2, x), k += c << 1;
k += MG_BR_GT(x, 0x0001);
return k;
}
#define MG_BR_MUL31(x, y) ((uint64_t) (x) * (uint64_t) (y))
#define MG_BR_MUL31_lo(x, y) \
(((uint32_t) (x) * (uint32_t) (y)) & (uint32_t) 0x7FFFFFFF)
#define MG_BR_CCOPY mg_br_ccopy
void mg_br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len);
uint32_t mg_br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r);
static inline uint32_t mg_br_rem(uint32_t hi, uint32_t lo, uint32_t d) {
uint32_t r;
mg_br_divrem(hi, lo, d, &r);
return r;
}
static inline uint32_t mg_br_div(uint32_t hi, uint32_t lo, uint32_t d) {
uint32_t r;
return mg_br_divrem(hi, lo, d, &r);
}
static inline void mg_br_enc32be(void *dst, uint32_t x) {
unsigned char *buf = (unsigned char *) dst;
buf[0] = (unsigned char) (x >> 24), buf[1] = (unsigned char) (x >> 16);
buf[2] = (unsigned char) (x >> 8), buf[3] = (unsigned char) x;
}
static inline void mg_br_i31_zero(uint32_t *x, uint32_t bit_len) {
*x++ = bit_len;
memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x);
}
// Big integers, "i31" format, see BearSSL src/inner.h
uint32_t mg_br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl);
uint32_t mg_br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl);
uint32_t mg_br_i31_bit_length(uint32_t *x, size_t xlen);
uint32_t mg_br_i31_decode_mod(uint32_t *x, const void *src, size_t len,
const uint32_t *m);
void mg_br_i31_decode(uint32_t *x, const void *src, size_t len);
void mg_br_i31_encode(void *dst, size_t len, const uint32_t *x);
void mg_br_i31_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i);
void mg_br_i31_to_monty(uint32_t *x, const uint32_t *m);
uint32_t mg_br_i31_iszero(const uint32_t *x);
void mg_br_i31_modpow(uint32_t *x, const unsigned char *e, size_t elen,
const uint32_t *m, uint32_t m0i, uint32_t *t1,
uint32_t *t2);
void mg_br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y,
const uint32_t *m, uint32_t m0i);
void mg_br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m);
uint32_t mg_br_i31_ninv31(uint32_t x);
void mg_br_i31_rshift(uint32_t *x, int count);
// ECDSA, see BearSSL src/ec/ecdsa_i31_*.c. q is an uncompressed point
void mg_br_ecdsa_i31_bits2int(uint32_t *x, const void *src, size_t len,
uint32_t ebitlen);
uint32_t mg_br_ecdsa_i31_vrfy_raw(const struct mg_br_ec_impl *impl, int curve,
const void *hash, size_t hash_len,
const unsigned char *q, size_t qlen,
const void *sig, size_t sig_len);
size_t mg_br_ecdsa_i31_sign_raw(const struct mg_br_ec_impl *impl, int curve,
const void *hash, size_t hash_len,
const unsigned char *x, size_t xlen,
const unsigned char *k, void *sig);
// P-256 (secp256r1) API. Keys and signatures are big endian byte strings,
// a public key is X || Y, a signature is r || s. Functions return 1 on
// success, 0 on failure
// Generate a random key pair, using mg_random()
int mg_p256_make_key(uint8_t public_key[64], uint8_t private_key[32]);
// Compute public key from a private key. Return 0 if private key is invalid
int mg_p256_compute_public_key(const uint8_t private_key[32],
uint8_t public_key[64]);
// ECDH. Validates the peer public key, returns 0 if it is not on the curve
int mg_p256_shared_secret(const uint8_t public_key[64],
const uint8_t private_key[32], uint8_t secret[32]);
// ECDSA sign with a deterministic k, RFC 6979 with HMAC-SHA256
int mg_p256_sign(const uint8_t private_key[32], const uint8_t *hash,
size_t hash_size, uint8_t signature[64]);
// ECDSA verify. Validates the public key
int mg_p256_verify(const uint8_t public_key[64], const uint8_t *hash,
size_t hash_size, const uint8_t signature[64]);
// Internal: ECDSA sign with a given k, 32 bytes big endian, in [1, n-1].
// Provided by the selected implementation, used by mg_p256_sign()
int mg_p256_sign_with_k(const uint8_t private_key[32], const uint8_t *hash,
size_t hash_size, const uint8_t k[32],
uint8_t signature[64]);
#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
File diff suppressed because it is too large. Load diff
+834
View File
@@ -0,0 +1,834 @@
// P-384 from BearSSL, https://bearssl.org commit 7bea48e
// P-384 signature verification, used by built-in TLS for certificates and
// CertificateVerify. Shared bignum and ECDSA code is in tls_p256.c
//
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
//
// 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 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.
//
// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, static
// functions with p384_. Trimmed to P-384. Structs are not typedef-ed
#include "tls_p384.h"
#include "tls_p256.h"
#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
// From BearSSL src/ec/ec_secp384r1.c
static const unsigned char P384_N[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF,
0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A,
0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73
};
static const unsigned char P384_G[] = {
0x04, 0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05,
0x37, 0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD,
0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B,
0x98, 0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A,
0x38, 0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29,
0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A,
0xB7, 0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C,
0x6F, 0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC,
0x29, 0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14,
0x7C, 0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8,
0xC0, 0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81,
0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E,
0x5F
};
// From BearSSL src/ec/ec_prime_i31.c
/*
* Parameters for supported curves (field modulus, and 'b' equation
* parameter; both values use the 'i31' format, and 'b' is in Montgomery
* representation).
*/
static const uint32_t P384_P[] = {
0x0000018C,
0x7FFFFFFF, 0x00000001, 0x00000000, 0x7FFFFFF8,
0x7FFFFFEF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
0x00000FFF
};
static const uint32_t P384_R2[] = {
0x0000018C,
0x00000000, 0x00000080, 0x7FFFFE00, 0x000001FF,
0x00000800, 0x00000000, 0x7FFFE000, 0x00001FFF,
0x00008000, 0x00008000, 0x00000000, 0x00000000,
0x00000000
};
static const uint32_t P384_B[] = {
0x0000018C,
0x6E666840, 0x070D0392, 0x5D810231, 0x7651D50C,
0x17E218D6, 0x1B192002, 0x44EFE441, 0x3A524E2B,
0x2719BA5F, 0x41F02209, 0x36C5643E, 0x5813EFFE,
0x000008A5
};
struct p384_curve_params {
const uint32_t *p;
const uint32_t *b;
const uint32_t *R2;
uint32_t p0i;
size_t point_len;
};
static inline const struct p384_curve_params *
p384_id_to_curve(int curve)
{
static const struct p384_curve_params pp = {
P384_P, P384_B, P384_R2, 0x00000001, 97
};
(void)curve;
return &pp;
}
#define I31_LEN ((MG_BR_MAX_EC_SIZE + 61) / 31)
/*
* Type for a point in Jacobian coordinates:
* -- three values, x, y and z, in Montgomery representation
* -- affine coordinates are X = x / z^2 and Y = y / z^3
* -- for the point at infinity, z = 0
*/
struct p384_jacobian {
uint32_t c[3][I31_LEN];
};
/*
* We use a custom interpreter that uses a dozen registers, and
* only six operations:
* MSET(d, a) copy a into d
* MADD(d, a) d = d+a (modular)
* MSUB(d, a) d = d-a (modular)
* MMUL(d, a, b) d = a*b (Montgomery multiplication)
* MINV(d, a, b) invert d modulo p; a and b are used as scratch registers
* MTZ(d) clear return value if d = 0
* Destination of MMUL (d) must be distinct from operands (a and b).
* There is no such constraint for MSUB and MADD.
*
* Registers include the operand coordinates, and temporaries.
*/
#define MSET(d, a) (0x0000 + ((d) << 8) + ((a) << 4))
#define MADD(d, a) (0x1000 + ((d) << 8) + ((a) << 4))
#define MSUB(d, a) (0x2000 + ((d) << 8) + ((a) << 4))
#define MMUL(d, a, b) (0x3000 + ((d) << 8) + ((a) << 4) + (b))
#define MINV(d, a, b) (0x4000 + ((d) << 8) + ((a) << 4) + (b))
#define MTZ(d) (0x5000 + ((d) << 8))
#define ENDCODE 0
/*
* Registers for the input operands.
*/
#define P1x 0
#define P1y 1
#define P1z 2
#define P2x 3
#define P2y 4
#define P2z 5
/*
* Alternate names for the first input operand.
*/
#define Px 0
#define Py 1
#define Pz 2
/*
* Temporaries.
*/
#define t1 6
#define t2 7
#define t3 8
#define t4 9
#define t5 10
#define t6 11
#define t7 12
/*
* Extra scratch registers available when there is no second operand (e.g.
* for "double" and "affine").
*/
#define t8 3
#define t9 4
#define t10 5
/*
* Doubling formulas are:
*
* s = 4*x*y^2
* m = 3*(x + z^2)*(x - z^2)
* x' = m^2 - 2*s
* y' = m*(s - x') - 8*y^4
* z' = 2*y*z
*
* If y = 0 (P has order 2) then this yields infinity (z' = 0), as it
* should. This case should not happen anyway, because our curves have
* prime order, and thus do not contain any point of order 2.
*
* If P is infinity (z = 0), then again the formulas yield infinity,
* which is correct. Thus, this code works for all points.
*
* Cost: 8 multiplications
*/
static const uint16_t p384_code_double[] = {
/*
* Compute z^2 (in t1).
*/
MMUL(t1, Pz, Pz),
/*
* Compute x-z^2 (in t2) and then x+z^2 (in t1).
*/
MSET(t2, Px),
MSUB(t2, t1),
MADD(t1, Px),
/*
* Compute m = 3*(x+z^2)*(x-z^2) (in t1).
*/
MMUL(t3, t1, t2),
MSET(t1, t3),
MADD(t1, t3),
MADD(t1, t3),
/*
* Compute s = 4*x*y^2 (in t2) and 2*y^2 (in t3).
*/
MMUL(t3, Py, Py),
MADD(t3, t3),
MMUL(t2, Px, t3),
MADD(t2, t2),
/*
* Compute x' = m^2 - 2*s.
*/
MMUL(Px, t1, t1),
MSUB(Px, t2),
MSUB(Px, t2),
/*
* Compute z' = 2*y*z.
*/
MMUL(t4, Py, Pz),
MSET(Pz, t4),
MADD(Pz, t4),
/*
* Compute y' = m*(s - x') - 8*y^4. Note that we already have
* 2*y^2 in t3.
*/
MSUB(t2, Px),
MMUL(Py, t1, t2),
MMUL(t4, t3, t3),
MSUB(Py, t4),
MSUB(Py, t4),
ENDCODE
};
/*
* Addtions formulas are:
*
* u1 = x1 * z2^2
* u2 = x2 * z1^2
* s1 = y1 * z2^3
* s2 = y2 * z1^3
* h = u2 - u1
* r = s2 - s1
* x3 = r^2 - h^3 - 2 * u1 * h^2
* y3 = r * (u1 * h^2 - x3) - s1 * h^3
* z3 = h * z1 * z2
*
* If both P1 and P2 are infinity, then z1 == 0 and z2 == 0, implying that
* z3 == 0, so the result is correct.
* If either of P1 or P2 is infinity, but not both, then z3 == 0, which is
* not correct.
* h == 0 only if u1 == u2; this happens in two cases:
* -- if s1 == s2 then P1 and/or P2 is infinity, or P1 == P2
* -- if s1 != s2 then P1 + P2 == infinity (but neither P1 or P2 is infinity)
*
* Thus, the following situations are not handled correctly:
* -- P1 = 0 and P2 != 0
* -- P1 != 0 and P2 = 0
* -- P1 = P2
* All other cases are properly computed. However, even in "incorrect"
* situations, the three coordinates still are properly formed field
* elements.
*
* The returned flag is cleared if r == 0. This happens in the following
* cases:
* -- Both points are on the same horizontal line (same Y coordinate).
* -- Both points are infinity.
* -- One point is infinity and the other is on line Y = 0.
* The third case cannot happen with our curves (there is no valid point
* on line Y = 0 since that would be a point of order 2). If the two
* source points are non-infinity, then remains only the case where the
* two points are on the same horizontal line.
*
* This allows us to detect the "P1 == P2" case, assuming that P1 != 0 and
* P2 != 0:
* -- If the returned value is not the point at infinity, then it was properly
* computed.
* -- Otherwise, if the returned flag is 1, then P1+P2 = 0, and the result
* is indeed the point at infinity.
* -- Otherwise (result is infinity, flag is 0), then P1 = P2 and we should
* use the 'double' code.
*
* Cost: 16 multiplications
*/
static const uint16_t p384_code_add[] = {
/*
* Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3).
*/
MMUL(t3, P2z, P2z),
MMUL(t1, P1x, t3),
MMUL(t4, P2z, t3),
MMUL(t3, P1y, t4),
/*
* Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4).
*/
MMUL(t4, P1z, P1z),
MMUL(t2, P2x, t4),
MMUL(t5, P1z, t4),
MMUL(t4, P2y, t5),
/*
* Compute h = u2 - u1 (in t2) and r = s2 - s1 (in t4).
*/
MSUB(t2, t1),
MSUB(t4, t3),
/*
* Report cases where r = 0 through the returned flag.
*/
MTZ(t4),
/*
* Compute u1*h^2 (in t6) and h^3 (in t5).
*/
MMUL(t7, t2, t2),
MMUL(t6, t1, t7),
MMUL(t5, t7, t2),
/*
* Compute x3 = r^2 - h^3 - 2*u1*h^2.
* t1 and t7 can be used as scratch registers.
*/
MMUL(P1x, t4, t4),
MSUB(P1x, t5),
MSUB(P1x, t6),
MSUB(P1x, t6),
/*
* Compute y3 = r*(u1*h^2 - x3) - s1*h^3.
*/
MSUB(t6, P1x),
MMUL(P1y, t4, t6),
MMUL(t1, t5, t3),
MSUB(P1y, t1),
/*
* Compute z3 = h*z1*z2.
*/
MMUL(t1, P1z, P2z),
MMUL(P1z, t1, t2),
ENDCODE
};
/*
* Check that the point is on the curve. This code snippet assumes the
* following conventions:
* -- Coordinates x and y have been freshly decoded in P1 (but not
* converted to Montgomery coordinates yet).
* -- P2x, P2y and P2z are set to, respectively, R^2, b*R and 1.
*/
static const uint16_t p384_code_check[] = {
/* Convert x and y to Montgomery representation. */
MMUL(t1, P1x, P2x),
MMUL(t2, P1y, P2x),
MSET(P1x, t1),
MSET(P1y, t2),
/* Compute x^3 in t1. */
MMUL(t2, P1x, P1x),
MMUL(t1, P1x, t2),
/* Subtract 3*x from t1. */
MSUB(t1, P1x),
MSUB(t1, P1x),
MSUB(t1, P1x),
/* Add b. */
MADD(t1, P2y),
/* Compute y^2 in t2. */
MMUL(t2, P1y, P1y),
/* Compare y^2 with x^3 - 3*x + b; they must match. */
MSUB(t1, t2),
MTZ(t1),
/* Set z to 1 (in Montgomery representation). */
MMUL(P1z, P2x, P2z),
ENDCODE
};
/*
* Conversion back to affine coordinates. This code snippet assumes that
* the z coordinate of P2 is set to 1 (not in Montgomery representation).
*/
static const uint16_t p384_code_affine[] = {
/* Save z*R in t1. */
MSET(t1, P1z),
/* Compute z^3 in t2. */
MMUL(t2, P1z, P1z),
MMUL(t3, P1z, t2),
MMUL(t2, t3, P2z),
/* Invert to (1/z^3) in t2. */
MINV(t2, t3, t4),
/* Compute y. */
MSET(t3, P1y),
MMUL(P1y, t2, t3),
/* Compute (1/z^2) in t3. */
MMUL(t3, t2, t1),
/* Compute x. */
MSET(t2, P1x),
MMUL(P1x, t2, t3),
ENDCODE
};
static uint32_t
p384_run_code(struct p384_jacobian *P1, const struct p384_jacobian *P2,
const struct p384_curve_params *cc, const uint16_t *code)
{
uint32_t r;
uint32_t t[13][I31_LEN];
size_t u;
r = 1;
/*
* Copy the two operands in the dedicated registers.
*/
memcpy(t[P1x], P1->c, 3 * I31_LEN * sizeof(uint32_t));
memcpy(t[P2x], P2->c, 3 * I31_LEN * sizeof(uint32_t));
/*
* Run formulas.
*/
for (u = 0;; u ++) {
unsigned op, d, a, b;
op = code[u];
if (op == 0) {
break;
}
d = (op >> 8) & 0x0F;
a = (op >> 4) & 0x0F;
b = op & 0x0F;
op >>= 12;
switch (op) {
uint32_t ctl;
size_t plen;
unsigned char tp[(MG_BR_MAX_EC_SIZE + 7) >> 3];
case 0:
memcpy(t[d], t[a], I31_LEN * sizeof(uint32_t));
break;
case 1:
ctl = mg_br_i31_add(t[d], t[a], 1);
ctl |= MG_BR_NOT(mg_br_i31_sub(t[d], cc->p, 0));
mg_br_i31_sub(t[d], cc->p, ctl);
break;
case 2:
mg_br_i31_add(t[d], cc->p, mg_br_i31_sub(t[d], t[a], 1));
break;
case 3:
mg_br_i31_montymul(t[d], t[a], t[b], cc->p, cc->p0i);
break;
case 4:
plen = (cc->p[0] - (cc->p[0] >> 5) + 7) >> 3;
mg_br_i31_encode(tp, plen, cc->p);
tp[plen - 1] -= 2;
mg_br_i31_modpow(t[d], tp, plen,
cc->p, cc->p0i, t[a], t[b]);
break;
default:
r &= ~mg_br_i31_iszero(t[d]);
break;
}
}
/*
* Copy back result.
*/
memcpy(P1->c, t[P1x], 3 * I31_LEN * sizeof(uint32_t));
return r;
}
static void
p384_set_one(uint32_t *x, const uint32_t *p)
{
size_t plen;
plen = (p[0] + 63) >> 5;
memset(x, 0, plen * sizeof *x);
x[0] = p[0];
x[1] = 0x00000001;
}
static void
p384_point_zero(struct p384_jacobian *P, const struct p384_curve_params *cc)
{
memset(P, 0, sizeof *P);
P->c[0][0] = P->c[1][0] = P->c[2][0] = cc->p[0];
}
static inline void
p384_point_double(struct p384_jacobian *P, const struct p384_curve_params *cc)
{
p384_run_code(P, P, cc, p384_code_double);
}
static inline uint32_t
p384_point_add(struct p384_jacobian *P1, const struct p384_jacobian *P2, const struct p384_curve_params *cc)
{
return p384_run_code(P1, P2, cc, p384_code_add);
}
static void
p384_point_mul(struct p384_jacobian *P, const unsigned char *x, size_t xlen,
const struct p384_curve_params *cc)
{
/*
* We do a simple double-and-add ladder with a 2-bit window
* to make only one add every two doublings. We thus first
* precompute 2P and 3P in some local buffers.
*
* We always perform two doublings and one addition; the
* addition is with P, 2P and 3P and is done in a temporary
* array.
*
* The addition code cannot handle cases where one of the
* operands is infinity, which is the case at the start of the
* ladder. We therefore need to maintain a flag that controls
* this situation.
*/
uint32_t qz;
struct p384_jacobian P2, P3, Q, T, U;
memcpy(&P2, P, sizeof P2);
p384_point_double(&P2, cc);
memcpy(&P3, P, sizeof P3);
p384_point_add(&P3, &P2, cc);
p384_point_zero(&Q, cc);
qz = 1;
while (xlen -- > 0) {
int k;
for (k = 6; k >= 0; k -= 2) {
uint32_t bits;
uint32_t bnz;
p384_point_double(&Q, cc);
p384_point_double(&Q, cc);
memcpy(&T, P, sizeof T);
memcpy(&U, &Q, sizeof U);
bits = (*x >> k) & (uint32_t)3;
bnz = MG_BR_NEQ(bits, 0);
MG_BR_CCOPY(MG_BR_EQ(bits, 2), &T, &P2, sizeof T);
MG_BR_CCOPY(MG_BR_EQ(bits, 3), &T, &P3, sizeof T);
p384_point_add(&U, &T, cc);
MG_BR_CCOPY(bnz & qz, &Q, &T, sizeof Q);
MG_BR_CCOPY(bnz & ~qz, &Q, &U, sizeof Q);
qz &= ~bnz;
}
x ++;
}
memcpy(P, &Q, sizeof Q);
}
/*
* Decode point into Jacobian coordinates. This function does not support
* the point at infinity. If the point is invalid then this returns 0, but
* the coordinates are still set to properly formed field elements.
*/
static uint32_t
p384_point_decode(struct p384_jacobian *P, const void *src, size_t len, const struct p384_curve_params *cc)
{
/*
* Points must use uncompressed format:
* -- first byte is 0x04;
* -- coordinates X and Y use unsigned big-endian, with the same
* length as the field modulus.
*
* We don't support hybrid format (uncompressed, but first byte
* has value 0x06 or 0x07, depending on the least significant bit
* of Y) because it is rather useless, and explicitly forbidden
* by PKIX (RFC 5480, section 2.2).
*
* We don't support compressed format either, because it is not
* much used in practice (there are or were patent-related
* concerns about point compression, which explains the lack of
* generalised support). Also, point compression support would
* need a bit more code.
*/
const unsigned char *buf;
size_t plen, zlen;
uint32_t r;
struct p384_jacobian Q;
buf = (const unsigned char *)src;
p384_point_zero(P, cc);
plen = (cc->p[0] - (cc->p[0] >> 5) + 7) >> 3;
if (len != 1 + (plen << 1)) {
return 0;
}
r = mg_br_i31_decode_mod(P->c[0], buf + 1, plen, cc->p);
r &= mg_br_i31_decode_mod(P->c[1], buf + 1 + plen, plen, cc->p);
/*
* Check first byte.
*/
r &= MG_BR_EQ(buf[0], 0x04);
/* obsolete
r &= MG_BR_EQ(buf[0], 0x04) | (MG_BR_EQ(buf[0] & 0xFE, 0x06)
& ~(uint32_t)(buf[0] ^ buf[plen << 1]));
*/
/*
* Convert coordinates and check that the point is valid.
*/
zlen = ((cc->p[0] + 63) >> 5) * sizeof(uint32_t);
memcpy(Q.c[0], cc->R2, zlen);
memcpy(Q.c[1], cc->b, zlen);
p384_set_one(Q.c[2], cc->p);
r &= ~p384_run_code(P, &Q, cc, p384_code_check);
return r;
}
/*
* Encode a point. This method assumes that the point is correct and is
* not the point at infinity. Encoded size is always 1+2*plen, where
* plen is the field modulus length, in bytes.
*/
static void
p384_point_encode(void *dst, const struct p384_jacobian *P, const struct p384_curve_params *cc)
{
unsigned char *buf;
uint32_t xbl;
size_t plen;
struct p384_jacobian Q, T;
buf = (unsigned char *)dst;
xbl = cc->p[0];
xbl -= (xbl >> 5);
plen = (xbl + 7) >> 3;
buf[0] = 0x04;
memcpy(&Q, P, sizeof *P);
p384_set_one(T.c[2], cc->p);
p384_run_code(&Q, &T, cc, p384_code_affine);
mg_br_i31_encode(buf + 1, plen, Q.c[0]);
mg_br_i31_encode(buf + 1 + plen, plen, Q.c[1]);
}
static const unsigned char *
p384_api_generator(int curve, size_t *len)
{
(void)curve;
*len = sizeof P384_G;
return P384_G;
}
static const unsigned char *
p384_api_order(int curve, size_t *len)
{
(void)curve;
*len = sizeof P384_N;
return P384_N;
}
static size_t
p384_api_xoff(int curve, size_t *len)
{
p384_api_generator(curve, len);
*len >>= 1;
return 1;
}
static uint32_t
p384_api_mul(unsigned char *G, size_t Glen,
const unsigned char *x, size_t xlen, int curve)
{
uint32_t r;
const struct p384_curve_params *cc;
struct p384_jacobian P;
cc = p384_id_to_curve(curve);
if (Glen != cc->point_len) {
return 0;
}
r = p384_point_decode(&P, G, Glen, cc);
p384_point_mul(&P, x, xlen, cc);
p384_point_encode(G, &P, cc);
return r;
}
static size_t
p384_api_mulgen(unsigned char *R,
const unsigned char *x, size_t xlen, int curve)
{
const unsigned char *G;
size_t Glen;
G = p384_api_generator(curve, &Glen);
memcpy(R, G, Glen);
p384_api_mul(R, Glen, x, xlen, curve);
return Glen;
}
static uint32_t
p384_api_muladd(unsigned char *A, const unsigned char *B, size_t len,
const unsigned char *x, size_t xlen,
const unsigned char *y, size_t ylen, int curve)
{
uint32_t r, t, z;
const struct p384_curve_params *cc;
struct p384_jacobian P, Q;
/*
* TODO: see about merging the two ladders. Right now, we do
* two independent point multiplications, which is a bit
* wasteful of CPU resources (but yields short code).
*/
cc = p384_id_to_curve(curve);
if (len != cc->point_len) {
return 0;
}
r = p384_point_decode(&P, A, len, cc);
if (B == NULL) {
size_t Glen;
B = p384_api_generator(curve, &Glen);
}
r &= p384_point_decode(&Q, B, len, cc);
p384_point_mul(&P, x, xlen, cc);
p384_point_mul(&Q, y, ylen, cc);
/*
* We want to compute P+Q. Since the base points A and B are distinct
* from infinity, and the multipliers are non-zero and lower than the
* curve order, then we know that P and Q are non-infinity. This
* leaves two special situations to test for:
* -- If P = Q then we must use p384_point_double().
* -- If P+Q = 0 then we must report an error.
*/
t = p384_point_add(&P, &Q, cc);
p384_point_double(&Q, cc);
z = mg_br_i31_iszero(P.c[2]);
/*
* If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we
* have the following:
*
* z = 0, t = 0 return P (normal addition)
* z = 0, t = 1 return P (normal addition)
* z = 1, t = 0 return Q (a 'double' case)
* z = 1, t = 1 report an error (P+Q = 0)
*/
MG_BR_CCOPY(z & ~t, &P, &Q, sizeof Q);
p384_point_encode(A, &P, cc);
r &= ~(z & t);
return r;
}
static const struct mg_br_ec_impl mg_br_ec_prime_i31 = {
(uint32_t)1 << MG_BR_EC_secp384r1,
&p384_api_generator,
&p384_api_order,
&p384_api_xoff,
&p384_api_mul,
&p384_api_mulgen,
&p384_api_muladd
};
// See tls_p384.h
int mg_p384_verify(const uint8_t public_key[96], const uint8_t *hash,
size_t hash_size, const uint8_t signature[96]) {
unsigned char q[97];
q[0] = 0x04;
memcpy(q + 1, public_key, 96);
return mg_br_ecdsa_i31_vrfy_raw(&mg_br_ec_prime_i31, MG_BR_EC_secp384r1, hash,
hash_size, q, sizeof(q), signature, 96) == 1;
}
// Undefine local macros, mongoose.c is one translation unit
#undef I31_LEN
#undef MSET
#undef MADD
#undef MSUB
#undef MMUL
#undef MINV
#undef MTZ
#undef ENDCODE
#undef P1x
#undef P1y
#undef P1z
#undef P2x
#undef P2y
#undef P2z
#undef Px
#undef Py
#undef Pz
#undef t1
#undef t2
#undef t3
#undef t4
#undef t5
#undef t6
#undef t7
#undef t8
#undef t9
#undef t10
#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
+40
View File
@@ -0,0 +1,40 @@
// P-384 from BearSSL, https://bearssl.org commit 7bea48e
// P-384 signature verification, used by built-in TLS for certificates and
// CertificateVerify. Shared bignum and ECDSA code is in tls_p256.c
//
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
//
// 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 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.
//
// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, static
// functions with p384_. Trimmed to P-384. Structs are not typedef-ed
#pragma once
#include "arch.h"
#include "config.h"
#include "tls.h"
#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
// Verify ECDSA P-384 signature r || s over hash. Public key is X || Y.
// Return 1 on success, 0 on failure
int mg_p384_verify(const uint8_t public_key[96], const uint8_t *hash,
size_t hash_size, const uint8_t signature[96]);
#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
+578 -604
View File
File diff suppressed because it is too large. Load diff
-3304
View File
File diff suppressed because it is too large. Load diff
-659
View File
@@ -1,659 +0,0 @@
#pragma once
#include "arch.h"
/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */
#ifndef _UECC_H_
#define _UECC_H_
/* Platform selection options.
If MG_UECC_PLATFORM is not defined, the code will try to guess it based on
compiler macros. Possible values for MG_UECC_PLATFORM are defined below: */
#define mg_uecc_arch_other 0
#define mg_uecc_x86 1
#define mg_uecc_x86_64 2
#define mg_uecc_arm 3
#define mg_uecc_arm_thumb 4
#define mg_uecc_arm_thumb2 5
#define mg_uecc_arm64 6
#define mg_uecc_avr 7
/* If desired, you can define MG_UECC_WORD_SIZE as appropriate for your platform
(1, 4, or 8 bytes). If MG_UECC_WORD_SIZE is not explicitly defined then it will
be automatically set based on your platform. */
/* Optimization level; trade speed for code size.
Larger values produce code that is faster but larger.
Currently supported values are 0 - 4; 0 is unusably slow for most
applications. Optimization level 4 currently only has an effect ARM platforms
where more than one curve is enabled. */
#ifndef MG_UECC_OPTIMIZATION_LEVEL
#define MG_UECC_OPTIMIZATION_LEVEL 2
#endif
/* MG_UECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a
specific function to be used for (scalar) squaring instead of the generic
multiplication function. This can make things faster somewhat faster, but
increases the code size. */
#ifndef MG_UECC_SQUARE_FUNC
#define MG_UECC_SQUARE_FUNC 0
#endif
/* MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will
switch to native little-endian format for *all* arrays passed in and out of the
public API. This includes public and private keys, shared secrets, signatures
and message hashes. Using this switch reduces the amount of call stack memory
used by uECC, since less intermediate translations are required. Note that this
will *only* work on native little-endian processors and it will treat the
uint8_t arrays passed into the public API as word arrays, therefore requiring
the provided byte arrays to be word aligned on architectures that do not support
unaligned accesses. IMPORTANT: Keys and signatures generated with
MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=1 are incompatible with keys and signatures
generated with MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=0; all parties must use the same
endianness. */
#ifndef MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
#define MG_UECC_VLI_NATIVE_LITTLE_ENDIAN 0
#endif
/* Curve support selection. Set to 0 to remove that curve. */
#ifndef MG_UECC_SUPPORTS_secp160r1
#define MG_UECC_SUPPORTS_secp160r1 0
#endif
#ifndef MG_UECC_SUPPORTS_secp192r1
#define MG_UECC_SUPPORTS_secp192r1 0
#endif
#ifndef MG_UECC_SUPPORTS_secp224r1
#define MG_UECC_SUPPORTS_secp224r1 0
#endif
#ifndef MG_UECC_SUPPORTS_secp256r1
#define MG_UECC_SUPPORTS_secp256r1 1
#endif
#ifndef MG_UECC_SUPPORTS_secp256k1
#define MG_UECC_SUPPORTS_secp256k1 0
#endif
#ifndef MG_UECC_SUPPORTS_secp384r1
#define MG_UECC_SUPPORTS_secp384r1 1
#endif
/* Specifies whether compressed point format is supported.
Set to 0 to disable point compression/decompression functions. */
#ifndef MG_UECC_SUPPORT_COMPRESSED_POINT
#define MG_UECC_SUPPORT_COMPRESSED_POINT 1
#endif
struct MG_UECC_Curve_t;
typedef const struct MG_UECC_Curve_t *MG_UECC_Curve;
#ifdef __cplusplus
extern "C" {
#endif
#if MG_UECC_SUPPORTS_secp160r1
MG_UECC_Curve mg_uecc_secp160r1(void);
#endif
#if MG_UECC_SUPPORTS_secp192r1
MG_UECC_Curve mg_uecc_secp192r1(void);
#endif
#if MG_UECC_SUPPORTS_secp224r1
MG_UECC_Curve mg_uecc_secp224r1(void);
#endif
#if MG_UECC_SUPPORTS_secp256r1
MG_UECC_Curve mg_uecc_secp256r1(void);
#endif
#if MG_UECC_SUPPORTS_secp256k1
MG_UECC_Curve mg_uecc_secp256k1(void);
#endif
#if MG_UECC_SUPPORTS_secp384r1
MG_UECC_Curve mg_uecc_secp384r1(void);
#endif
/* MG_UECC_RNG_Function type
The RNG function should fill 'size' random bytes into 'dest'. It should return 1
if 'dest' was filled with random data, or 0 if the random data could not be
generated. The filled-in values should be either truly random, or from a
cryptographically-secure PRNG.
A correctly functioning RNG is required before calling mg_uecc_make_key() or
mg_uecc_sign().
Setting a correctly functioning RNG function improves the resistance to
side-channel attacks for mg_uecc_shared_secret() and
mg_uecc_sign_deterministic().
*/
typedef int (*MG_UECC_RNG_Function)(uint8_t *dest, unsigned size);
// Mongoose: mg_uecc_make_key() and mg_uecc_sign() use mg_random() when no
// explicit uECC RNG is installed. If mg_random() cannot provide a strong source,
// those operations fail. Deterministic signing works without RNG, but only an
// explicit uECC RNG enables its optional side-channel blinding.
/* mg_uecc_set_rng() function.
Set the function that will be used to generate random bytes. The RNG function
should return 1 if the random data was generated, or 0 if the random data could
not be generated.
Mongoose: this overrides the default mg_random() fallback used by
mg_uecc_make_key() and mg_uecc_sign(). Do not call mg_uecc_set_rng() unless the
application must provide its own cryptographic RNG.
Inputs:
rng_function - The function that will be used to generate random bytes.
*/
void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function);
/* mg_uecc_get_rng() function.
Returns the function that will be used to generate random bytes.
*/
MG_UECC_RNG_Function mg_uecc_get_rng(void);
/* mg_uecc_curve_private_key_size() function.
Returns the size of a private key for the curve in bytes.
*/
int mg_uecc_curve_private_key_size(MG_UECC_Curve curve);
/* mg_uecc_curve_public_key_size() function.
Returns the size of a public key for the curve in bytes.
*/
int mg_uecc_curve_public_key_size(MG_UECC_Curve curve);
/* mg_uecc_make_key() function.
Create a public/private key pair.
Outputs:
public_key - Will be filled in with the public key. Must be at least 2 *
the curve size (in bytes) long. For example, if the curve is secp256r1,
public_key must be 64 bytes long. private_key - Will be filled in with the
private key. Must be as long as the curve order; this is typically the same as
the curve size, except for secp160r1. For example, if the curve is secp256r1,
private_key must be 32 bytes long.
For secp160r1, private_key must be 21 bytes long! Note that
the first byte will almost always be 0 (there is about a 1 in 2^80 chance of it
being non-zero).
Returns 1 if the key pair was generated successfully, 0 if an error occurred.
*/
int mg_uecc_make_key(uint8_t *public_key, uint8_t *private_key,
MG_UECC_Curve curve);
/* mg_uecc_shared_secret() function.
Compute a shared secret given your secret key and someone else's public key. If
the public key is not from a trusted source and has not been previously
verified, you should verify it first using mg_uecc_valid_public_key(). Note: It
is recommended that you hash the result of mg_uecc_shared_secret() before using
it for symmetric encryption or HMAC.
Inputs:
public_key - The public key of the remote party.
private_key - Your private key.
Outputs:
secret - Will be filled in with the shared secret value. Must be the same
size as the curve size; for example, if the curve is secp256r1, secret must be
32 bytes long.
Returns 1 if the shared secret was generated successfully, 0 if an error
occurred.
*/
int mg_uecc_shared_secret(const uint8_t *public_key, const uint8_t *private_key,
uint8_t *secret, MG_UECC_Curve curve);
#if MG_UECC_SUPPORT_COMPRESSED_POINT
/* mg_uecc_compress() function.
Compress a public key.
Inputs:
public_key - The public key to compress.
Outputs:
compressed - Will be filled in with the compressed public key. Must be at
least (curve size + 1) bytes long; for example, if the curve is secp256r1,
compressed must be 33 bytes long.
*/
void mg_uecc_compress(const uint8_t *public_key, uint8_t *compressed,
MG_UECC_Curve curve);
/* mg_uecc_decompress() function.
Decompress a compressed public key.
Inputs:
compressed - The compressed public key.
Outputs:
public_key - Will be filled in with the decompressed public key.
*/
void mg_uecc_decompress(const uint8_t *compressed, uint8_t *public_key,
MG_UECC_Curve curve);
#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */
/* mg_uecc_valid_public_key() function.
Check to see if a public key is valid.
Note that you are not required to check for a valid public key before using any
other uECC functions. However, you may wish to avoid spending CPU time computing
a shared secret or verifying a signature using an invalid public key.
Inputs:
public_key - The public key to check.
Returns 1 if the public key is valid, 0 if it is invalid.
*/
int mg_uecc_valid_public_key(const uint8_t *public_key, MG_UECC_Curve curve);
/* mg_uecc_compute_public_key() function.
Compute the corresponding public key for a private key.
Inputs:
private_key - The private key to compute the public key for
Outputs:
public_key - Will be filled in with the corresponding public key
Returns 1 if the key was computed successfully, 0 if an error occurred.
*/
int mg_uecc_compute_public_key(const uint8_t *private_key, uint8_t *public_key,
MG_UECC_Curve curve);
/* mg_uecc_sign() function.
Generate an ECDSA signature for a given hash value.
Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and
pass it in to this function along with your private key.
Inputs:
private_key - Your private key.
message_hash - The hash of the message to sign.
hash_size - The size of message_hash in bytes.
Outputs:
signature - Will be filled in with the signature value. Must be at least 2 *
curve size long. For example, if the curve is secp256r1, signature must be 64
bytes long.
Returns 1 if the signature generated successfully, 0 if an error occurred.
*/
int mg_uecc_sign(const uint8_t *private_key, const uint8_t *message_hash,
unsigned hash_size, uint8_t *signature, MG_UECC_Curve curve);
/* MG_UECC_HashContext structure.
This is used to pass in an arbitrary hash function to
mg_uecc_sign_deterministic(). The structure will be used for multiple hash
computations; each time a new hash is computed, init_hash() will be called,
followed by one or more calls to update_hash(), and finally a call to
finish_hash() to produce the resulting hash.
The intention is that you will create a structure that includes
MG_UECC_HashContext followed by any hash-specific data. For example:
typedef struct SHA256_HashContext {
MG_UECC_HashContext uECC;
SHA256_CTX ctx;
} SHA256_HashContext;
void init_SHA256(MG_UECC_HashContext *base) {
SHA256_HashContext *context = (SHA256_HashContext *)base;
SHA256_Init(&context->ctx);
}
void update_SHA256(MG_UECC_HashContext *base,
const uint8_t *message,
unsigned message_size) {
SHA256_HashContext *context = (SHA256_HashContext *)base;
SHA256_Update(&context->ctx, message, message_size);
}
void finish_SHA256(MG_UECC_HashContext *base, uint8_t *hash_result) {
SHA256_HashContext *context = (SHA256_HashContext *)base;
SHA256_Final(hash_result, &context->ctx);
}
... when signing ...
{
uint8_t tmp[32 + 32 + 64];
SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64,
32, tmp}}; mg_uecc_sign_deterministic(key, message_hash, &ctx.uECC, signature);
}
*/
typedef struct MG_UECC_HashContext {
void (*init_hash)(const struct MG_UECC_HashContext *context);
void (*update_hash)(const struct MG_UECC_HashContext *context,
const uint8_t *message, unsigned message_size);
void (*finish_hash)(const struct MG_UECC_HashContext *context,
uint8_t *hash_result);
unsigned
block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */
unsigned
result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */
uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size +
block_size) bytes. */
} MG_UECC_HashContext;
/* mg_uecc_sign_deterministic() function.
Generate an ECDSA signature for a given hash value, using a deterministic
algorithm (see RFC 6979). You do not need to set the RNG using mg_uecc_set_rng()
before calling this function; however, if the RNG is defined it will improve
resistance to side-channel attacks.
Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and
pass it to this function along with your private key and a hash context. Note
that the message_hash does not need to be computed with the same hash function
used by hash_context.
Inputs:
private_key - Your private key.
message_hash - The hash of the message to sign.
hash_size - The size of message_hash in bytes.
hash_context - A hash context to use.
Outputs:
signature - Will be filled in with the signature value.
Returns 1 if the signature generated successfully, 0 if an error occurred.
*/
int mg_uecc_sign_deterministic(const uint8_t *private_key,
const uint8_t *message_hash, unsigned hash_size,
const MG_UECC_HashContext *hash_context,
uint8_t *signature, MG_UECC_Curve curve);
/* mg_uecc_verify() function.
Verify an ECDSA signature.
Usage: Compute the hash of the signed data using the same hash as the signer and
pass it to this function along with the signer's public key and the signature
values (r and s).
Inputs:
public_key - The signer's public key.
message_hash - The hash of the signed data.
hash_size - The size of message_hash in bytes.
signature - The signature value.
Returns 1 if the signature is valid, 0 if it is invalid.
*/
int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash,
unsigned hash_size, const uint8_t *signature,
MG_UECC_Curve curve);
#ifdef __cplusplus
} /* end of extern "C" */
#endif
#endif /* _UECC_H_ */
/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
#ifndef _UECC_TYPES_H_
#define _UECC_TYPES_H_
#ifndef MG_UECC_PLATFORM
#if defined(__AVR__) && __AVR__
#define MG_UECC_PLATFORM mg_uecc_avr
#elif defined(__thumb2__) || \
defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */
#define MG_UECC_PLATFORM mg_uecc_arm_thumb2
#elif defined(__thumb__)
#define MG_UECC_PLATFORM mg_uecc_arm_thumb
#elif defined(__arm__) || defined(_M_ARM)
#define MG_UECC_PLATFORM mg_uecc_arm
#elif defined(__aarch64__)
#define MG_UECC_PLATFORM mg_uecc_arm64
#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || \
defined(__I86__)
#define MG_UECC_PLATFORM mg_uecc_x86
#elif defined(__amd64__) || defined(_M_X64)
#define MG_UECC_PLATFORM mg_uecc_x86_64
#else
#define MG_UECC_PLATFORM mg_uecc_arch_other
#endif
#endif
#ifndef MG_UECC_ARM_USE_UMAAL
#if (MG_UECC_PLATFORM == mg_uecc_arm) && (__ARM_ARCH >= 6)
#define MG_UECC_ARM_USE_UMAAL 1
#elif (MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && (__ARM_ARCH >= 6) && \
(!defined(__ARM_ARCH_7M__) || !__ARM_ARCH_7M__)
#define MG_UECC_ARM_USE_UMAAL 1
#else
#define MG_UECC_ARM_USE_UMAAL 0
#endif
#endif
#ifndef MG_UECC_WORD_SIZE
#if MG_UECC_PLATFORM == mg_uecc_avr
#define MG_UECC_WORD_SIZE 1
#elif (MG_UECC_PLATFORM == mg_uecc_x86_64 || MG_UECC_PLATFORM == mg_uecc_arm64)
#define MG_UECC_WORD_SIZE 8
#else
#define MG_UECC_WORD_SIZE 4
#endif
#endif
#if (MG_UECC_WORD_SIZE != 1) && (MG_UECC_WORD_SIZE != 4) && \
(MG_UECC_WORD_SIZE != 8)
#error "Unsupported value for MG_UECC_WORD_SIZE"
#endif
#if ((MG_UECC_PLATFORM == mg_uecc_avr) && (MG_UECC_WORD_SIZE != 1))
#pragma message("MG_UECC_WORD_SIZE must be 1 for AVR")
#undef MG_UECC_WORD_SIZE
#define MG_UECC_WORD_SIZE 1
#endif
#if ((MG_UECC_PLATFORM == mg_uecc_arm || \
MG_UECC_PLATFORM == mg_uecc_arm_thumb || \
MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && \
(MG_UECC_WORD_SIZE != 4))
#pragma message("MG_UECC_WORD_SIZE must be 4 for ARM")
#undef MG_UECC_WORD_SIZE
#define MG_UECC_WORD_SIZE 4
#endif
typedef int8_t wordcount_t;
typedef int16_t bitcount_t;
typedef int8_t cmpresult_t;
#if (MG_UECC_WORD_SIZE == 1)
typedef uint8_t mg_uecc_word_t;
typedef uint16_t mg_uecc_dword_t;
#define HIGH_BIT_SET 0x80
#define MG_UECC_WORD_BITS 8
#define MG_UECC_WORD_BITS_SHIFT 3
#define MG_UECC_WORD_BITS_MASK 0x07
#elif (MG_UECC_WORD_SIZE == 4)
typedef uint32_t mg_uecc_word_t;
typedef uint64_t mg_uecc_dword_t;
#define HIGH_BIT_SET 0x80000000
#define MG_UECC_WORD_BITS 32
#define MG_UECC_WORD_BITS_SHIFT 5
#define MG_UECC_WORD_BITS_MASK 0x01F
#elif (MG_UECC_WORD_SIZE == 8)
typedef uint64_t mg_uecc_word_t;
#define HIGH_BIT_SET 0x8000000000000000U
#define MG_UECC_WORD_BITS 64
#define MG_UECC_WORD_BITS_SHIFT 6
#define MG_UECC_WORD_BITS_MASK 0x03F
#endif /* MG_UECC_WORD_SIZE */
#endif /* _UECC_TYPES_H_ */
/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
#ifndef _UECC_VLI_H_
#define _UECC_VLI_H_
// #include "types.h"
// #include "uECC.h"
/* Functions for raw large-integer manipulation. These are only available
if uECC.c is compiled with MG_UECC_ENABLE_VLI_API defined to 1. */
#ifndef MG_UECC_ENABLE_VLI_API
#define MG_UECC_ENABLE_VLI_API 0
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if MG_UECC_ENABLE_VLI_API
void mg_uecc_vli_clear(mg_uecc_word_t *vli, wordcount_t num_words);
/* Constant-time comparison to zero - secure way to compare long integers */
/* Returns 1 if vli == 0, 0 otherwise. */
mg_uecc_word_t mg_uecc_vli_isZero(const mg_uecc_word_t *vli,
wordcount_t num_words);
/* Returns nonzero if bit 'bit' of vli is set. */
mg_uecc_word_t mg_uecc_vli_testBit(const mg_uecc_word_t *vli, bitcount_t bit);
/* Counts the number of bits required to represent vli. */
bitcount_t mg_uecc_vli_numBits(const mg_uecc_word_t *vli,
const wordcount_t max_words);
/* Sets dest = src. */
void mg_uecc_vli_set(mg_uecc_word_t *dest, const mg_uecc_word_t *src,
wordcount_t num_words);
/* Constant-time comparison function - secure way to compare long integers */
/* Returns one if left == right, zero otherwise */
mg_uecc_word_t mg_uecc_vli_equal(const mg_uecc_word_t *left,
const mg_uecc_word_t *right,
wordcount_t num_words);
/* Constant-time comparison function - secure way to compare long integers */
/* Returns sign of left - right, in constant time. */
cmpresult_t mg_uecc_vli_cmp(const mg_uecc_word_t *left,
const mg_uecc_word_t *right, wordcount_t num_words);
/* Computes vli = vli >> 1. */
void mg_uecc_vli_rshift1(mg_uecc_word_t *vli, wordcount_t num_words);
/* Computes result = left + right, returning carry. Can modify in place. */
mg_uecc_word_t mg_uecc_vli_add(mg_uecc_word_t *result,
const mg_uecc_word_t *left,
const mg_uecc_word_t *right,
wordcount_t num_words);
/* Computes result = left - right, returning borrow. Can modify in place. */
mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result,
const mg_uecc_word_t *left,
const mg_uecc_word_t *right,
wordcount_t num_words);
/* Computes result = left * right. Result must be 2 * num_words long. */
void mg_uecc_vli_mult(mg_uecc_word_t *result, const mg_uecc_word_t *left,
const mg_uecc_word_t *right, wordcount_t num_words);
/* Computes result = left^2. Result must be 2 * num_words long. */
void mg_uecc_vli_square(mg_uecc_word_t *result, const mg_uecc_word_t *left,
wordcount_t num_words);
/* Computes result = (left + right) % mod.
Assumes that left < mod and right < mod, and that result does not overlap
mod. */
void mg_uecc_vli_modAdd(mg_uecc_word_t *result, const mg_uecc_word_t *left,
const mg_uecc_word_t *right, const mg_uecc_word_t *mod,
wordcount_t num_words);
/* Computes result = (left - right) % mod.
Assumes that left < mod and right < mod, and that result does not overlap
mod. */
void mg_uecc_vli_modSub(mg_uecc_word_t *result, const mg_uecc_word_t *left,
const mg_uecc_word_t *right, const mg_uecc_word_t *mod,
wordcount_t num_words);
/* Computes result = product % mod, where product is 2N words long.
Currently only designed to work for mod == curve->p or curve_n. */
void mg_uecc_vli_mmod(mg_uecc_word_t *result, mg_uecc_word_t *product,
const mg_uecc_word_t *mod, wordcount_t num_words);
/* Calculates result = product (mod curve->p), where product is up to
2 * curve->num_words long. */
void mg_uecc_vli_mmod_fast(mg_uecc_word_t *result, mg_uecc_word_t *product,
MG_UECC_Curve curve);
/* Computes result = (left * right) % mod.
Currently only designed to work for mod == curve->p or curve_n. */
void mg_uecc_vli_modMult(mg_uecc_word_t *result, const mg_uecc_word_t *left,
const mg_uecc_word_t *right, const mg_uecc_word_t *mod,
wordcount_t num_words);
/* Computes result = (left * right) % curve->p. */
void mg_uecc_vli_modMult_fast(mg_uecc_word_t *result,
const mg_uecc_word_t *left,
const mg_uecc_word_t *right, MG_UECC_Curve curve);
/* Computes result = left^2 % mod.
Currently only designed to work for mod == curve->p or curve_n. */
void mg_uecc_vli_modSquare(mg_uecc_word_t *result, const mg_uecc_word_t *left,
const mg_uecc_word_t *mod, wordcount_t num_words);
/* Computes result = left^2 % curve->p. */
void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result,
const mg_uecc_word_t *left,
MG_UECC_Curve curve);
/* Computes result = (1 / input) % mod.*/
void mg_uecc_vli_modInv(mg_uecc_word_t *result, const mg_uecc_word_t *input,
const mg_uecc_word_t *mod, wordcount_t num_words);
#if MG_UECC_SUPPORT_COMPRESSED_POINT
/* Calculates a = sqrt(a) (mod curve->p) */
void mg_uecc_vli_mod_sqrt(mg_uecc_word_t *a, MG_UECC_Curve curve);
#endif
/* Converts an integer in uECC native format to big-endian bytes. */
void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes,
const mg_uecc_word_t *native);
/* Converts big-endian bytes to an integer in uECC native format. */
void mg_uecc_vli_bytesToNative(mg_uecc_word_t *native, const uint8_t *bytes,
int num_bytes);
unsigned mg_uecc_curve_num_words(MG_UECC_Curve curve);
unsigned mg_uecc_curve_num_bytes(MG_UECC_Curve curve);
unsigned mg_uecc_curve_num_bits(MG_UECC_Curve curve);
unsigned mg_uecc_curve_num_n_words(MG_UECC_Curve curve);
unsigned mg_uecc_curve_num_n_bytes(MG_UECC_Curve curve);
unsigned mg_uecc_curve_num_n_bits(MG_UECC_Curve curve);
const mg_uecc_word_t *mg_uecc_curve_p(MG_UECC_Curve curve);
const mg_uecc_word_t *mg_uecc_curve_n(MG_UECC_Curve curve);
const mg_uecc_word_t *mg_uecc_curve_G(MG_UECC_Curve curve);
const mg_uecc_word_t *mg_uecc_curve_b(MG_UECC_Curve curve);
int mg_uecc_valid_point(const mg_uecc_word_t *point, MG_UECC_Curve curve);
/* Multiplies a point by a scalar. Points are represented by the X coordinate
followed by the Y coordinate in the same array, both coordinates are
curve->num_words long. Note that scalar must be curve->num_n_words long (NOT
curve->num_words). */
void mg_uecc_point_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point,
const mg_uecc_word_t *scalar, MG_UECC_Curve curve);
/* Generates a random integer in the range 0 < random < top.
Both random and top have num_words words. */
int mg_uecc_generate_random_int(mg_uecc_word_t *random,
const mg_uecc_word_t *top,
wordcount_t num_words);
#endif /* MG_UECC_ENABLE_VLI_API */
#ifdef __cplusplus
} /* end of extern "C" */
#endif
#endif /* _UECC_VLI_H_ */
// End of uecc BSD-2
-262
View File
@@ -1,262 +0,0 @@
/**
* Adapted from STROBE: https://strobe.sourceforge.io/
* Copyright (c) 2015-2016 Cryptography Research, Inc.
* Author: Mike Hamburg
* License: MIT License
*/
#include "tls.h"
#include "tls_x25519.h"
#include "util.h"
#if MG_TLS == MG_TLS_BUILTIN
const uint8_t X25519_BASE_POINT[X25519_BYTES] = {9};
#define X25519_WBITS 32
typedef uint32_t limb_t;
typedef uint64_t dlimb_t;
typedef int64_t sdlimb_t;
#define NLIMBS (256 / X25519_WBITS)
typedef limb_t mg_fe[NLIMBS];
static limb_t umaal(limb_t *carry, limb_t acc, limb_t mand, limb_t mier) {
dlimb_t tmp = (dlimb_t) mand * mier + acc + *carry;
*carry = (limb_t) (tmp >> X25519_WBITS);
return (limb_t) tmp;
}
// These functions are implemented in terms of umaal on ARM
static limb_t adc(limb_t *carry, limb_t acc, limb_t mand) {
dlimb_t total = (dlimb_t) *carry + acc + mand;
*carry = (limb_t) (total >> X25519_WBITS);
return (limb_t) total;
}
static limb_t adc0(limb_t *carry, limb_t acc) {
dlimb_t total = (dlimb_t) *carry + acc;
*carry = (limb_t) (total >> X25519_WBITS);
return (limb_t) total;
}
// - Precondition: carry is small.
// - Invariant: result of propagate is < 2^255 + 1 word
// - In particular, always less than 2p.
// - Also, output x >= min(x,19)
static void propagate(mg_fe x, limb_t over) {
unsigned i;
limb_t carry;
over = x[NLIMBS - 1] >> (X25519_WBITS - 1) | over << 1;
x[NLIMBS - 1] &= ~((limb_t) 1 << (X25519_WBITS - 1));
carry = over * 19;
for (i = 0; i < NLIMBS; i++) {
x[i] = adc0(&carry, x[i]);
}
}
static void add(mg_fe out, const mg_fe a, const mg_fe b) {
unsigned i;
limb_t carry = 0;
for (i = 0; i < NLIMBS; i++) {
out[i] = adc(&carry, a[i], b[i]);
}
propagate(out, carry);
}
static void sub(mg_fe out, const mg_fe a, const mg_fe b) {
unsigned i;
sdlimb_t carry = -38;
for (i = 0; i < NLIMBS; i++) {
carry = carry + a[i] - b[i];
out[i] = (limb_t) carry;
carry >>= X25519_WBITS;
}
propagate(out, (limb_t) (1 + carry));
}
// `b` can contain less than 8 limbs, thus we use `limb_t *` instead of `mg_fe`
// to avoid build warnings
static void mul(mg_fe out, const mg_fe a, const limb_t *b, unsigned nb) {
limb_t accum[2 * NLIMBS] = {0};
unsigned i, j;
limb_t carry2;
for (i = 0; i < nb; i++) {
limb_t mand = b[i];
carry2 = 0;
for (j = 0; j < NLIMBS; j++) {
limb_t tmp; // "a" may be misaligned
memcpy(&tmp, &a[j], sizeof(tmp)); // So make an aligned copy
accum[i + j] = umaal(&carry2, accum[i + j], mand, tmp);
}
accum[i + j] = carry2;
}
carry2 = 0;
for (j = 0; j < NLIMBS; j++) {
out[j] = umaal(&carry2, accum[j], 38, accum[j + NLIMBS]);
}
propagate(out, carry2);
}
static void sqr(mg_fe out, const mg_fe a) {
mul(out, a, a, NLIMBS);
}
static void mul1(mg_fe out, const mg_fe a) {
mul(out, a, out, NLIMBS);
}
static void sqr1(mg_fe a) {
mul1(a, a);
}
static void condswap(limb_t a[2 * NLIMBS], limb_t b[2 * NLIMBS],
limb_t doswap) {
unsigned i;
for (i = 0; i < 2 * NLIMBS; i++) {
limb_t xor_ab = (a[i] ^ b[i]) & doswap;
a[i] ^= xor_ab;
b[i] ^= xor_ab;
}
}
// Canonicalize a field element x, reducing it to the least residue which is
// congruent to it mod 2^255-19
// - Precondition: x < 2^255 + 1 word
static limb_t canon(mg_fe x) {
// First, add 19.
unsigned i;
limb_t carry0 = 19;
limb_t res;
sdlimb_t carry;
for (i = 0; i < NLIMBS; i++) {
x[i] = adc0(&carry0, x[i]);
}
propagate(x, carry0);
// Here, 19 <= x2 < 2^255
// - This is because we added 19, so before propagate it can't be less
// than 19. After propagate, it still can't be less than 19, because if
// propagate does anything it adds 19.
// - We know that the high bit must be clear, because either the input was ~
// 2^255 + one word + 19 (in which case it propagates to at most 2 words) or
// it was < 2^255. So now, if we subtract 19, we will get back to something in
// [0,2^255-19).
carry = -19;
res = 0;
for (i = 0; i < NLIMBS; i++) {
carry += x[i];
res |= x[i] = (limb_t) carry;
carry >>= X25519_WBITS;
}
return (limb_t) (((dlimb_t) res - 1) >> X25519_WBITS);
}
static const limb_t a24[1] = {121665};
static void ladder_part1(mg_fe xs[5]) {
limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4];
add(t1, x2, z2); // t1 = A
sub(z2, x2, z2); // z2 = B
add(x2, x3, z3); // x2 = C
sub(z3, x3, z3); // z3 = D
mul1(z3, t1); // z3 = DA
mul1(x2, z2); // x3 = BC
add(x3, z3, x2); // x3 = DA+CB
sub(z3, z3, x2); // z3 = DA-CB
sqr1(t1); // t1 = AA
sqr1(z2); // z2 = BB
sub(x2, t1, z2); // x2 = E = AA-BB
mul(z2, x2, a24, sizeof(a24) / sizeof(a24[0])); // z2 = E*a24
add(z2, z2, t1); // z2 = E*a24 + AA
}
static void ladder_part2(mg_fe xs[5], const mg_fe x1) {
limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4];
sqr1(z3); // z3 = (DA-CB)^2
mul1(z3, x1); // z3 = x1 * (DA-CB)^2
sqr1(x3); // x3 = (DA+CB)^2
mul1(z2, x2); // z2 = AA*(E*a24+AA)
sub(x2, t1, x2); // x2 = BB again
mul1(x2, t1); // x2 = AA*BB
}
static void x25519_core(mg_fe xs[5], const uint8_t scalar[X25519_BYTES],
const uint8_t *x1, int clamp) {
int i;
mg_fe x1_limbs;
limb_t swap = 0;
limb_t *x2 = xs[0], *x3 = xs[2], *z3 = xs[3];
memset(xs, 0, 4 * sizeof(mg_fe));
x2[0] = z3[0] = 1;
for (i = 0; i < NLIMBS; i++) {
x3[i] = x1_limbs[i] =
MG_U32(x1[i * 4 + 3], x1[i * 4 + 2], x1[i * 4 + 1], x1[i * 4]);
}
for (i = 255; i >= 0; i--) {
uint8_t bytei = scalar[i / 8];
limb_t doswap;
if (clamp) {
if (i / 8 == 0) {
bytei &= (uint8_t) ~7U;
} else if (i / 8 == X25519_BYTES - 1) {
bytei &= 0x7F;
bytei |= 0x40;
}
}
doswap = 0 - (limb_t) ((bytei >> (i % 8)) & 1);
condswap(x2, x3, swap ^ doswap);
swap = doswap;
ladder_part1(xs);
ladder_part2(xs, (const limb_t *) x1_limbs);
}
condswap(x2, x3, swap);
}
int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES],
const uint8_t x1[X25519_BYTES], int clamp) {
int i, ret;
mg_fe xs[5], out_limbs;
limb_t *x2, *z2, *z3, *prev;
static const struct {
uint8_t a, c, n;
} steps[13] = {{2, 1, 1}, {2, 1, 1}, {4, 2, 3}, {2, 4, 6}, {3, 1, 1},
{3, 2, 12}, {4, 3, 25}, {2, 3, 25}, {2, 4, 50}, {3, 2, 125},
{3, 1, 2}, {3, 1, 2}, {3, 1, 1}};
x25519_core(xs, scalar, x1, clamp);
// Precomputed inversion chain
x2 = xs[0];
z2 = xs[1];
z3 = xs[3];
prev = z2;
for (i = 0; i < 13; i++) {
int j;
limb_t *a = xs[steps[i].a];
for (j = steps[i].n; j > 0; j--) {
sqr(a, prev);
prev = a;
}
mul1(a, xs[steps[i].c]);
}
// Here prev = z3
// x2 /= z2
mul(out_limbs, x2, z3, NLIMBS);
ret = (int) canon(out_limbs);
if (!clamp) ret = 0;
for (i = 0; i < NLIMBS; i++) {
uint32_t n = out_limbs[i];
out[i * 4] = (uint8_t) (n & 0xff);
out[i * 4 + 1] = (uint8_t) ((n >> 8) & 0xff);
out[i * 4 + 2] = (uint8_t) ((n >> 16) & 0xff);
out[i * 4 + 3] = (uint8_t) ((n >> 24) & 0xff);
}
return ret;
}
#endif
-13
View File
@@ -1,13 +0,0 @@
#ifndef TLS_X15519_H
#define TLS_X15519_H
#include "arch.h"
#define X25519_BYTES 32
extern const uint8_t X25519_BASE_POINT[X25519_BYTES];
int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES],
const uint8_t x1[X25519_BYTES], int clamp);
#endif /* TLS_X15519_H */
+22 -19
View File
@@ -1,5 +1,6 @@
#include "util.h" #include "util.h"
#include "log.h" #include "log.h"
#include "net.h"
// Not using memset for zeroing memory, cause it can be dropped by compiler // Not using memset for zeroing memory, cause it can be dropped by compiler
// See https://github.com/cesanta/mongoose/pull/1265 // See https://github.com/cesanta/mongoose/pull/1265
@@ -43,11 +44,12 @@ bool mg_random(void *buf, size_t len) {
success = true; success = true;
#elif MG_ARCH == MG_ARCH_CUBE && defined(HAL_RNG_MODULE_ENABLED) #elif MG_ARCH == MG_ARCH_CUBE && defined(HAL_RNG_MODULE_ENABLED)
extern RNG_HandleTypeDef hrng; extern RNG_HandleTypeDef hrng;
success = true;
for (size_t n = 0; n < len; n += sizeof(uint32_t)) { for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
uint32_t r = HAL_RNG_ReadLastRandomNumber(&hrng); uint32_t r = 0;
if (HAL_RNG_GenerateRandomNumber(&hrng, &r) != HAL_OK) success = false;
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r)); memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
} }
success = true;
#elif MG_ARCH == MG_ARCH_PICOSDK #elif MG_ARCH == MG_ARCH_PICOSDK
while (len--) *p++ = (unsigned char) (get_rand_32() & 255); while (len--) *p++ = (unsigned char) (get_rand_32() & 255);
success = true; success = true;
@@ -142,22 +144,21 @@ uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len) {
return (uint16_t) ~c; return (uint16_t) ~c;
} }
static int isbyte(int n) { // Parse "IP" or "IP/BITS" into net and mask. The whole spec must be valid
return n >= 0 && n <= 255; static bool parse_net(struct mg_str spec, uint32_t *net, uint32_t *mask) {
} struct mg_str caps[3], ip = spec, bits = mg_str("32");
struct mg_addr addr;
static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { uint8_t n = 0;
int n, a, b, c, d, slash = 32, len = 0; if (mg_match(spec, mg_str("*/*"), caps)) ip = caps[0], bits = caps[1];
if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 || // ip.len == 0 check: mg_aton() parses an empty string as 0.0.0.0
sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && if (ip.len == 0 || !mg_aton(ip, &addr) || addr.is_ip6 ||
isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 && !mg_str_to_num(bits, 10, &n, sizeof(n)) || n > 32) {
slash < 33) { return false;
len = n;
*net = ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) |
(uint32_t) d;
*mask = slash ? (uint32_t) (0xffffffffU << (32 - slash)) : (uint32_t) 0;
} }
return len; memcpy(net, addr.addr.ip, sizeof(*net));
*net = mg_ntohl(*net);
*mask = n ? (uint32_t) (0xffffffffU << (32 - n)) : (uint32_t) 0;
return true;
} }
int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) { int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) {
@@ -171,7 +172,9 @@ int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) {
while (mg_span(acl, &entry, &acl, ',')) { while (mg_span(acl, &entry, &acl, ',')) {
uint32_t net, mask; uint32_t net, mask;
if (entry.buf[0] != '+' && entry.buf[0] != '-') return -1; if (entry.buf[0] != '+' && entry.buf[0] != '-') return -1;
if (parse_net(&entry.buf[1], &net, &mask) == 0) return -2; if (!parse_net(mg_str_n(entry.buf + 1, entry.len - 1), &net, &mask)) {
return -2;
}
if ((mg_ntohl(remote_ip4) & mask) == net) allowed = entry.buf[0]; if ((mg_ntohl(remote_ip4) & mask) == net) allowed = entry.buf[0];
} }
} }
@@ -197,7 +200,7 @@ bool mg_path_is_sane(const struct mg_str path) {
#if MG_ENABLE_CUSTOM_MILLIS #if MG_ENABLE_CUSTOM_MILLIS
#else #else
uint64_t mg_millis(void) { uint64_t mg_millis(void) {
#if MG_ARCH == MG_ARCH_ESP8266 || MG_ARCH == MG_ARCH_ESP32 || MG_ENABLE_FREERTOS #if MG_ARCH == MG_ARCH_ESP32 || MG_ENABLE_FREERTOS
return xTaskGetTickCount() * portTICK_PERIOD_MS; return xTaskGetTickCount() * portTICK_PERIOD_MS;
#elif MG_ARCH == MG_ARCH_THREADX #elif MG_ARCH == MG_ARCH_THREADX
return tx_time_get() * (1000 /* MS per SEC */ / TX_TIMER_TICKS_PER_SECOND); return tx_time_get() * (1000 /* MS per SEC */ / TX_TIMER_TICKS_PER_SECOND);
+10 -19
View File
@@ -16,9 +16,9 @@ IPV6 ?= 1
MDASH ?= 1 MDASH ?= 1
ASAN ?= -fsanitize=address,undefined,alignment -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common ASAN ?= -fsanitize=address,undefined,alignment -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common
ASAN_OPTIONS ?= detect_leaks=1 ASAN_OPTIONS ?= detect_leaks=1
# This does not include ESP32, ESP8266, Microchip; all examples that are directly placed under an OS tutorial. Also, it is needed that way... TUTORIALS_EMBEDDED := $(dir $(wildcard ../tutorials/esp32/*/Makefile)) $(dir $(wildcard ../tutorials/infineon/*/Makefile)) $(dir $(wildcard ../tutorials/microchip/*/*/Makefile)) $(dir $(wildcard ../tutorials/nordic/*/Makefile)) $(dir $(wildcard ../tutorials/nxp/*/*/Makefile)) $(dir $(wildcard ../tutorials/pico-sdk/*/Makefile)) $(dir $(wildcard ../tutorials/rp/*/Makefile)) $(dir $(wildcard ../tutorials/renesas/*/Makefile)) $(dir $(wildcard ../tutorials/stm32/*/Makefile)) $(dir $(wildcard ../tutorials/stm32/*/*/Makefile)) $(dir $(wildcard ../tutorials/ti/*/Makefile)) $(dir $(wildcard ../tutorials/wch/*/Makefile))
TUTORIALS_EMBEDDED := $(dir $(wildcard ../tutorials/infineon/*/Makefile)) $(dir $(wildcard ../tutorials/nxp/*/Makefile)) $(dir $(wildcard ../tutorials/rp/*/Makefile)) $(dir $(wildcard ../tutorials/renesas/*/Makefile)) $(dir $(wildcard ../tutorials/stm32/*/*/Makefile)) $(dir $(wildcard ../tutorials/ti/*/Makefile)) $(dir $(wildcard ../tutorials/wch/*/Makefile)) TUTORIALS_ZEPHYR := $(dir $(wildcard ../tutorials/zephyr/*/Makefile))
TUTORIALS := ../tutorials/micropython/ $(filter-out $(TUTORIALS_EMBEDDED), $(dir $(wildcard ../tutorials/*/*/Makefile))) TUTORIALS := ../tutorials/micropython/ $(filter-out $(TUTORIALS_EMBEDDED) $(TUTORIALS_ZEPHYR), $(dir $(wildcard ../tutorials/*/*/Makefile)))
TUTORIALS_QUICK := $(filter-out ../tutorials/micropython/ ../tutorials/tcpip/pcap-driver/, $(TUTORIALS)) TUTORIALS_QUICK := $(filter-out ../tutorials/micropython/ ../tutorials/tcpip/pcap-driver/, $(TUTORIALS))
TUTORIALS_MAC := $(filter-out ../tutorials/micropython/ ../tutorials/tcpip/pcap-driver/ ../tutorials/tcpip/tap-driver/, $(TUTORIALS)) TUTORIALS_MAC := $(filter-out ../tutorials/micropython/ ../tutorials/tcpip/pcap-driver/ ../tutorials/tcpip/tap-driver/, $(TUTORIALS))
TUTORIALS_WIN := $(dir $(wildcard ../tutorials/http/file-*/Makefile) $(wildcard ../tutorials/http/http-*/Makefile) $(wildcard ../tutorials/mqtt/*/Makefile) $(wildcard ../tutorials/websocket/*/Makefile) $(wildcard ../tutorials/webui/*/Makefile)) TUTORIALS_WIN := $(dir $(wildcard ../tutorials/http/file-*/Makefile) $(wildcard ../tutorials/http/http-*/Makefile) $(wildcard ../tutorials/mqtt/*/Makefile) $(wildcard ../tutorials/websocket/*/Makefile) $(wildcard ../tutorials/webui/*/Makefile))
@@ -76,19 +76,10 @@ LDFLAGS += -L$(WOLFSSL)/lib -lwolfssl
endif endif
ifeq "$(SSL)" "BUILTIN" ifeq "$(SSL)" "BUILTIN"
DEFS += -DMG_TLS=MG_TLS_BUILTIN # Small TLS records: several records per TCP segment, tests record handling
DEFS += -DMG_TLS=MG_TLS_BUILTIN -DMG_TLS_RECORD_SIZE=256
endif endif
tls_multirec/server: FORCE
ifneq "$(SSL)" ""
ifeq "$(MULTIREC)" ""
$(MAKE) -C tls_multirec CC=gcc
endif
endif
true
FORCE:
true
all: all:
@@ -107,13 +98,13 @@ mongoose.c: Makefile $(wildcard ../src/*.c) $(wildcard ../src/drivers/*.c)
cd .. && (export LC_ALL=C ; cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c src/drivers/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@ cd .. && (export LC_ALL=C ; cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c src/drivers/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
mongoose.h: $(HDRS) Makefile mongoose.h: $(HDRS) Makefile
cd .. && (export LC_ALL=C ; cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/os_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/profile.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/lfs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_x25519.h src/tls_aes128.h src/tls_uecc.h src/tls_chacha20.h src/tls_rsa.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/modbus.h src/json.h src/jwt.h src/rpc.h src/dash.h src/ota.h src/health.h src/mdash.h src/flash.h src/wifi.h src/l2.h src/net_builtin.h src/bsd.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@ cd .. && (export LC_ALL=C ; cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/os_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/profile.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/lfs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_aes128.h src/tls_p256.h src/tls_p384.h src/tls_chacha20.h src/tls_rsa.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/modbus.h src/json.h src/jwt.h src/rpc.h src/dash.h src/ota.h src/health.h src/mdash.h src/flash.h src/wifi.h src/l2.h src/net_builtin.h src/bsd.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
# Check that all external (exported) symbols have "mg_" prefix # Check that all external (exported) symbols have "mg_" prefix
mg_prefix: mongoose.c mongoose.h mg_prefix: mongoose.c mongoose.h
$(CC) mongoose.c $(CFLAGS) -c -o /tmp/x.o && nm /tmp/x.o | grep ' T ' | grep -v 'mg_' ; test $$? = 1 $(CC) mongoose.c $(CFLAGS) -c -o /tmp/x.o && nm /tmp/x.o | grep ' T ' | grep -v 'mg_' ; test $$? = 1
test: Makefile mongoose.h $(SRCS) tls_multirec/server test: Makefile mongoose.h $(SRCS)
$(CC) $(SRCS) $(CFLAGS) $(LDFLAGS) -o unit_test $(CC) $(SRCS) $(CFLAGS) $(LDFLAGS) -o unit_test
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./unit_test ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./unit_test
@@ -263,7 +254,6 @@ packed_fs.c: Makefile data/ssi.h fuzz.c data/a.txt data/range.txt data/ca.pem ce
clean: clean_tutorials clean_tutorials_embedded clean: clean_tutorials clean_tutorials_embedded
rm -rf $(PROG) *.exe *.o *.dSYM *_test ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* packed_fs.c pack rm -rf $(PROG) *.exe *.o *.dSYM *_test ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* packed_fs.c pack
$(MAKE) -C tls_multirec $@
$(MAKE) -C ../resources/I-CUBE-Mongoose $@ $(MAKE) -C ../resources/I-CUBE-Mongoose $@
#find tutorials -maxdepth 3 -name zephyr -prune -o -name Makefile -print | xargs dirname | xargs -n1 make clean -C #find tutorials -maxdepth 3 -name zephyr -prune -o -name Makefile -print | xargs dirname | xargs -n1 make clean -C
@@ -287,7 +277,7 @@ atcmd_test: atcmd_test.c mongoose.c mongoose.h Makefile
$(CC) atcmd_test.c $(CFLAGS) $(LDFLAGS) -o $@ $(CC) atcmd_test.c $(CFLAGS) $(LDFLAGS) -o $@
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@ ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
mip_tap_test: mip_x_test.c mip_tap_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_multirec/server mip_tap_test: mip_x_test.c mip_tap_test.c mongoose.c mongoose.h packed_fs.c Makefile
$(CC) mip_tap_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@ $(CC) mip_tap_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@ ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
@@ -300,7 +290,7 @@ port_tap_bridge: port_tap_bridge.c
cc port_tap_bridge.c -o $@ cc port_tap_bridge.c -o $@
# requires port_tap_bridge to be running # requires port_tap_bridge to be running
mip_port_test: mip_x_test.c mip_port_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_multirec/server mip_port_test: mip_x_test.c mip_port_test.c mongoose.c mongoose.h packed_fs.c Makefile
$(CC) mip_port_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@ $(CC) mip_port_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@ ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
@@ -309,6 +299,7 @@ mip_diabish: CC = gcc
mip_diabish: CFLAGS += -std=gnu89 -Werror=declaration-after-statement -Werror=vla -Wno-pedantic mip_diabish: CFLAGS += -std=gnu89 -Werror=declaration-after-statement -Werror=vla -Wno-pedantic
mip_diabish: mip_test mip_diabish: mip_test
mip_vc98: MDASH = 0
mip_vc98: mip_test.c mongoose.c mongoose.h packed_fs.c Makefile mip_vc98: mip_test.c mongoose.c mongoose.h packed_fs.c Makefile
$(DOCKER) mdashnet/vc98 wine cl mip_test.c packed_fs.c $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe $(DOCKER) mdashnet/vc98 wine cl mip_test.c packed_fs.c $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe
$(DOCKER) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe $(DOCKER) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe
+7 -7
View File
@@ -29,20 +29,20 @@ teensy-board:
touch teensy-board touch teensy-board
seeeduino-xiao: arduino seeeduino-xiao-board seeeduino-xiao: arduino seeeduino-xiao-board
cd $(ROOT)/tutorials/mqtt/mqtt-client/arduino/sim800-mqtt && $(ARD)/arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 sim800-mqtt.ino cd $(ROOT)/tutorials/arduino/sim800-mqtt && $(ARD)/arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 sim800-mqtt.ino
cd $(ROOT)/tutorials/http/http-server/arduino/w5500-http && $(ARD)/arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 w5500-http.ino cd $(ROOT)/tutorials/arduino/w5500-http && $(ARD)/arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 w5500-http.ino
cd $(ROOT)/tutorials/mqtt/mqtt-client/arduino/w5500-mqtt && $(ARD)/arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 w5500-mqtt.ino cd $(ROOT)/tutorials/arduino/w5500-mqtt && $(ARD)/arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 w5500-mqtt.ino
arduino-nano: arduino arduino-nano: arduino
cd $(ROOT)/tutorials/mqtt/mqtt-client/arduino/nano-w5500-mqtt && $(ARD)/arduino/arduino --pref compiler.cpp.extra_flags=-DMG_ENABLE_LOG=0 --verbose --verify --board arduino:avr:nano nano-w5500-mqtt.ino cd $(ROOT)/tutorials/arduino/nano-w5500-mqtt && $(ARD)/arduino/arduino --pref compiler.cpp.extra_flags=-DMG_ENABLE_LOG=0 --verbose --verify --board arduino:avr:nano nano-w5500-mqtt.ino
esp32: arduino esp32-board esp32: arduino esp32-board
cd $(ROOT)/tutorials/mqtt/mqtt-client/arduino/esp32-mqtt && $(ARD)/arduino/arduino --verbose --verify --board esp32:esp32:node32s esp32-mqtt.ino cd $(ROOT)/tutorials/arduino/esp32-mqtt && $(ARD)/arduino/arduino --verbose --verify --board esp32:esp32:node32s esp32-mqtt.ino
# Needs a board with 'LED_BUILTIN', yet somehow invocation is missing that definition (lack of include somehow amended by the IDE) # Needs a board with 'LED_BUILTIN', yet somehow invocation is missing that definition (lack of include somehow amended by the IDE)
#cd $(ROOT)/tutorials/http/http-server/arduino/esp32-http && $(ARD)/arduino/arduino --verbose --verify --board esp32:esp32:esp32 esp32-http.ino #cd $(ROOT)/tutorials/arduino/esp32-http && $(ARD)/arduino/arduino --verbose --verify --board esp32:esp32:esp32 esp32-http.ino
teensy41: arduino teensy-board teensy41: arduino teensy-board
cd $(ROOT)/tutorials/http/http-server/arduino/teensy41-http && $(ARD)/arduino/arduino --verbose --verify --board teensy41-http.ino cd $(ROOT)/tutorials/arduino/teensy41-http && $(ARD)/arduino/arduino --verbose --verify --board teensy41-http.ino
+28 -5
View File
@@ -13,18 +13,21 @@ else
C_WARN ?= -Wno-deprecated C_WARN ?= -Wno-deprecated
endif endif
.PHONY: all bsd_test bsd_client shm_tap_bridge shm_port_bridge bsd_s390 clean .PHONY: all bsd bsd_test bsd_client bsd_nortos bsd_nortos_test shm_tap_bridge \
shm_port_bridge bsd_s390 bsd_nortos_s390 clean
TEST_SOURCES = bsd_shm_test.c TEST_SOURCES = bsd_shm_test.c
# A C++ application compiles its sources as C++, linking Mongoose and FreeRTOS as C. # A C++ application compiles its sources as C++, linking Mongoose and FreeRTOS as C.
# FreeRTOS does not build as C++, so C sources are compiled separately. # FreeRTOS does not build as C++, so C sources are compiled separately.
C_SOURCES = ../../mongoose.c C_SOURCES = ../../mongoose.c
C_SOURCES += FreeRTOS-Kernel/portable/MemMang/heap_4.c FREERTOS_SOURCES = FreeRTOS-Kernel/portable/MemMang/heap_4.c
C_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/port.c FREERTOS_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/port.c
C_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c FREERTOS_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c
C_SOURCES += $(wildcard FreeRTOS-Kernel/*.c) FREERTOS_SOURCES += $(wildcard FreeRTOS-Kernel/*.c)
C_OBJECTS = $(notdir $(C_SOURCES:.c=.o)) C_OBJECTS = $(notdir $(C_SOURCES:.c=.o))
FREERTOS_OBJECTS = $(notdir $(FREERTOS_SOURCES:.c=.o))
C_INPUTS = $(foreach S,$(C_SOURCES),-x c $(S)) C_INPUTS = $(foreach S,$(C_SOURCES),-x c $(S))
FREERTOS_INPUTS = $(foreach S,$(FREERTOS_SOURCES),-x c $(S))
CFLAGS += -IFreeRTOS-Kernel/include CFLAGS += -IFreeRTOS-Kernel/include
CFLAGS += -IFreeRTOS-Kernel/portable/ThirdParty/GCC/Posix -Wno-conversion -Wno-undef CFLAGS += -IFreeRTOS-Kernel/portable/ThirdParty/GCC/Posix -Wno-conversion -Wno-undef
@@ -47,8 +50,19 @@ CFLAGS_MONGOOSE += -DMG_ARCH=MG_ARCH_CUSTOM
all bsd: shm_tap_bridge bsd_client FreeRTOS-Kernel bsd_test all bsd: shm_tap_bridge bsd_client FreeRTOS-Kernel bsd_test
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS) ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS)
# also
bsd_nortos: shm_tap_bridge bsd_client bsd_nortos_test
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_nortos_test $(ARGS)
# requires shm_tap_bridge to be running # requires shm_tap_bridge to be running
bsd_test: CFLAGS_MONGOOSE += -DMG_ENABLE_FREERTOS=1
bsd_test: FreeRTOS-Kernel bsd_test: FreeRTOS-Kernel
$(CC) $(C_INPUTS) $(FREERTOS_INPUTS) -c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
$(CC) -x $(TEST_LANGUAGE) -c $(TEST_SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
$(CC) $(C_OBJECTS) $(FREERTOS_OBJECTS) $(TEST_SOURCES:.c=.o) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@
bsd_nortos_test: CFLAGS_MONGOOSE += -DMG_ENABLE_FREERTOS=0
bsd_nortos_test:
$(CC) $(C_INPUTS) -c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(CC) $(C_INPUTS) -c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
$(CC) -x $(TEST_LANGUAGE) -c $(TEST_SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(CC) -x $(TEST_LANGUAGE) -c $(TEST_SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
$(CC) $(C_OBJECTS) $(TEST_SOURCES:.c=.o) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@ $(CC) $(C_OBJECTS) $(TEST_SOURCES:.c=.o) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@
@@ -61,12 +75,21 @@ shm_tap_bridge: shm_tap_bridge.c shm_queue.h
$(CC) shm_tap_bridge.c -pthread -o $@ $(CC) shm_tap_bridge.c -pthread -o $@
# requires test/port_tap_bridge to be running # requires test/port_tap_bridge to be running
bsd_s390: CFLAGS_MONGOOSE += -DMG_ENABLE_BSD_PROTOTYPES=0
bsd_s390: ASAN= bsd_s390: ASAN=
bsd_s390: CC = $(DOCKER) mdashnet/s390 cc bsd_s390: CC = $(DOCKER) mdashnet/s390 cc
bsd_s390: RUN = $(DOCKER) --init mdashnet/s390 bsd_s390: RUN = $(DOCKER) --init mdashnet/s390
bsd_s390: shm_port_bridge bsd_client FreeRTOS-Kernel bsd_test bsd_s390: shm_port_bridge bsd_client FreeRTOS-Kernel bsd_test
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc' $(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
#also
bsd_nortos_s390: CFLAGS_MONGOOSE += -DMG_ENABLE_BSD_PROTOTYPES=0
bsd_nortos_s390: ASAN=
bsd_nortos_s390: CC = $(DOCKER) mdashnet/s390 cc
bsd_nortos_s390: RUN = $(DOCKER) --init mdashnet/s390
bsd_nortos_s390: shm_port_bridge bsd_client bsd_nortos_test
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_nortos_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
shm_port_bridge: shm_port_bridge.c shm_queue.h shm_port_bridge: shm_port_bridge.c shm_queue.h
$(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@ $(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@
+64 -5
View File
@@ -1,4 +1,5 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h>
#include <pthread.h> #include <pthread.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -91,22 +92,80 @@ fail:
exit(1); exit(1);
} }
enum wait_mode { WAIT_ABORT, WAIT_DRAIN, WAIT_HOLD, WAIT_CLOSE };
// Wait for output, optionally draining through the final marker.
static int wait_data(const char *host, enum wait_mode mode) {
struct sockaddr_in sa;
struct timeval timeout;
char buf[512];
ssize_t n;
size_t i;
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) return 1;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(PORT);
if (inet_pton(AF_INET, host, &sa.sin_addr) != 1) goto fail;
timeout.tv_sec = 5, timeout.tv_usec = 0;
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0)
goto fail;
if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) != 0) goto fail;
if (mode == WAIT_HOLD) {
usleep(100000); // Keep the peer connected while the server fails TX
close(fd);
return 0;
}
if (mode == WAIT_CLOSE) {
n = recv(fd, buf, sizeof(buf), 0);
close(fd);
return n == 0 || (n < 0 && errno == ECONNRESET) ? 0 : 1;
}
for (;;) {
n = recv(fd, buf, sizeof(buf), 0);
if (n <= 0) goto fail;
if (mode == WAIT_ABORT) break;
for (i = 0; i < (size_t) n; i++) {
if (buf[i] == '\1') break;
}
if (i < (size_t) n) break;
}
close(fd);
return 0;
fail:
close(fd);
return 1;
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
pthread_t threads[NUM_CONNECTIONS]; pthread_t threads[NUM_CONNECTIONS];
int i; int connections = NUM_CONNECTIONS, i;
if (argc != 2) { if (argc == 3 && strcmp(argv[2], "waitandabort") == 0)
fprintf(stderr, "usage: %s IP\n", argv[0]); return wait_data(argv[1], WAIT_ABORT);
if (argc == 3 && strcmp(argv[2], "waitanddrain") == 0)
return wait_data(argv[1], WAIT_DRAIN);
if (argc == 3 && strcmp(argv[2], "hold") == 0)
return wait_data(argv[1], WAIT_HOLD);
if (argc == 3 && strcmp(argv[2], "expectclose") == 0)
return wait_data(argv[1], WAIT_CLOSE);
if (argc == 3) connections = atoi(argv[2]);
if ((argc != 2 && argc != 3) || connections < 1 ||
connections > NUM_CONNECTIONS) {
fprintf(stderr, "usage: %s IP [connections|waitandabort|waitanddrain|hold|expectclose]\n",
argv[0]);
return 1; return 1;
} }
srandom(1); srandom(1);
for (i = 0; i < NUM_CONNECTIONS; i++) { for (i = 0; i < connections; i++) {
if (pthread_create(&threads[i], NULL, worker, argv[1]) != 0) return 1; if (pthread_create(&threads[i], NULL, worker, argv[1]) != 0) return 1;
} }
for (i = 0; i < NUM_CONNECTIONS; i++) { for (i = 0; i < connections; i++) {
if (pthread_join(threads[i], NULL) != 0) return 1; if (pthread_join(threads[i], NULL) != 0) return 1;
} }
+814 -71
View File
@@ -1,11 +1,11 @@
#define BSDTEST_USING_DHCP 1 #define BSDTEST_USING_DHCP 1
#include <errno.h>
#include <spawn.h> #include <spawn.h>
#include <sys/wait.h> #include <sys/wait.h>
static int s_num_tests = 0; static int s_num_tests = 0;
static bool s_error = false; static bool s_error = false;
static volatile bool s_done = false, s_wait;
#ifdef NO_ABORT #ifdef NO_ABORT
static int s_abort = 0; static int s_abort = 0;
@@ -30,15 +30,177 @@ static int s_abort = 0;
} \ } \
} while (0) } while (0)
#define DASHBOARD(x) \
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true")
static pid_t start_client(const char *ip, const char *connections) {
char *cmd[4];
pid_t pid;
extern char **environ;
cmd[0] = (char *) "./bsd_client";
cmd[1] = (char *) ip;
cmd[2] = (char *) connections;
cmd[3] = NULL;
ASSERT(posix_spawn(&pid, cmd[0], NULL, NULL, cmd, environ) == 0);
return pid;
}
static bool client_done(pid_t pid, int *status) {
pid_t r = waitpid(pid, status, WNOHANG);
ASSERT(r >= 0);
return r == pid;
}
static void check_client(int status) {
ASSERT(WIFEXITED(status));
ASSERT(WEXITSTATUS(status) == 0);
}
static int open_listener(int backlog, bool nonblock) {
int lfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(1234);
sa.sin_addr.s_addr = INADDR_ANY;
ASSERT(lfd >= 0);
ASSERT(bind(lfd, (struct sockaddr *) &sa, sizeof(sa)) == 0);
ASSERT(listen(lfd, backlog) == 0);
if (nonblock) ASSERT(fcntl(lfd, F_SETFL, O_NONBLOCK) == 0);
else ASSERT((fcntl(lfd, F_GETFL, 0) & O_NONBLOCK) == 0);
MG_INFO(("BSD echo server on :1234"));
return lfd;
}
static void wait_ready(struct mg_mgr *mgr) {
// Stack initialization, Network configuration (DHCP lease, ...)
#if BSDTEST_USING_DHCP == 0
MG_INFO(("MIF configuration: Static IP"));
ASSERT(mgr->ifp->ip != 0); // Check we have a satic IP assigned
mg_mgr_poll(mgr, 0); // For initialisation
#else
MG_INFO(("MIF configuration: DHCP"));
ASSERT(mgr->ifp->ip == 0); // Check we are set for DHCP
while (mgr->ifp->ip == 0 && mg_millis() < 5000) mg_mgr_poll(mgr, 0);
if (mgr->ifp->ip == 0) MG_ERROR(("No ip assigned (DHCP lease may have failed).\n"));
ASSERT(mgr->ifp->ip != 0); // We have an IP (lease or static)
#endif
while (mgr->ifp->state != MG_TCPIP_STATE_READY) mg_mgr_poll(mgr, 0);
MG_INFO(("Interface started, starting main loop"));
}
#if !MG_ENABLE_FREERTOS
static bool s_fail_alloc;
void *mg_calloc(size_t count, size_t size) {
return s_fail_alloc ? NULL : calloc(count, size);
}
void mg_free(void *ptr) {
free(ptr);
}
static uint64_t s_bootmillis = 0;
uint64_t mg_millis(void) {
struct timespec ts = {0, 0};
// See #1615 - prefer monotonic clock
#if defined(CLOCK_MONOTONIC_RAW)
// Raw hardware-based time that is not subject to NTP adjustment
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
#elif defined(CLOCK_MONOTONIC)
// Affected by the incremental adjustments performed by adjtime and NTP
clock_gettime(CLOCK_MONOTONIC, &ts);
#else
// Affected by discontinuous jumps in the system time and by the incremental
// adjustments performed by adjtime and NTP
clock_gettime(CLOCK_REALTIME, &ts);
#endif
return ((uint64_t) ts.tv_sec * 1000 + (uint64_t) ts.tv_nsec / 1000000) - s_bootmillis;
}
#endif
#if MG_ENABLE_FREERTOS
static volatile bool s_done = false, s_wait, s_accept_waiting;
static volatile bool s_accept_done, s_dontwait_done, s_nonblock_done;
static volatile bool s_select_waiting, s_select_done;
static volatile bool s_poll_waiting, s_poll_done;
static volatile bool s_ready_waiting, s_ready_done;
static volatile bool s_backlog_ready, s_backlog_full, s_backlog_accept;
static volatile bool s_tx_held, s_tx_waits, s_tx_fail, s_tx_error;
static volatile bool s_send_waiting;
static volatile bool s_send_done, s_send_expect_error;
static volatile bool s_recv_start, s_recv_waiting, s_recv_done;
static volatile bool s_recv_expect_error;
static size_t (*s_tx)(const void *, size_t, struct mg_tcpip_if *);
struct client_data { struct client_data {
char hostname[64]; char hostname[64];
uint32_t ip; uint32_t ip;
bool done; bool done;
}; };
static void echo_task(void *args);
static void echo_dontwait_task(void *args);
static void echo_nonblock_task(void *args);
static void send_task(void *args);
static void recv_wait_task(void *args);
static void select_task(void *args);
static void readiness_task(void *args);
static void backlog_task(void *args);
static void tx_gate_task(void *args);
struct atask_args {
bool listener_nonblock;
bool worker_nonblock;
bool once;
bool receiver;
TaskFunction_t task;
TaskFunction_t gate;
};
static const struct atask_args s_echo_server = {true, false, false,
false, echo_task, NULL};
static const struct atask_args s_accept_server = {false, false, true,
false, echo_task, NULL};
static const struct atask_args s_nonblock_accept_server = {
true, false, true, false, echo_task, NULL};
static const struct atask_args s_dontwait_server = {
true, false, false, false, echo_dontwait_task, NULL};
static const struct atask_args s_nonblock_server = {
true, true, false, false, echo_nonblock_task, NULL};
static const struct atask_args s_send_server = {true, false, false,
true, send_task, tx_gate_task};
static const struct atask_args s_send_error_server = {
true, false, false, false, send_task, tx_gate_task};
static size_t tx_gate(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
if (s_tx_fail) return (size_t) -1; // Force MG_IO_ERR after send() waits
if (s_tx_held) {
s_tx_waits = true;
return 0; // Gate TX: MG_IO_WAIT leaves c->send queued
}
return s_tx(buf, len, ifp);
}
static void run_client(const char *ip, const char *connections) {
int status;
pid_t pid = start_client(ip, connections);
while (!client_done(pid, &status)) {
vTaskDelay(pdMS_TO_TICKS(10));
}
check_client(status);
}
// client_task: connects to hostname:80, fetches "/", logs response length. // client_task: connects to hostname:80, fetches "/", logs response length.
static void client_task(void *args) { static void client_task(void *args) {
int fd, c; fd_set w, e;
struct pollfd p;
int fd;
struct client_data *d = (struct client_data *) args; struct client_data *d = (struct client_data *) args;
char req[256]; char req[256];
fd = socket(AF_INET, SOCK_STREAM, 0); fd = socket(AF_INET, SOCK_STREAM, 0);
@@ -47,14 +209,20 @@ static void client_task(void *args) {
sa.sin_family = AF_INET; sa.sin_family = AF_INET;
sa.sin_port = htons(80); sa.sin_port = htons(80);
memcpy(&sa.sin_addr, &d->ip, 4); memcpy(&sa.sin_addr, &d->ip, 4);
c = connect(fd, (struct sockaddr *) &sa, sizeof(sa)); ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
ASSERT(c >= 0); ASSERT(connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 &&
if (c < 0) { errno == EINPROGRESS);
MG_ERROR(("connect failed")); FD_ZERO(&w);
close(fd); FD_ZERO(&e);
vTaskDelete(NULL); FD_SET(fd, &w);
return; FD_SET(fd, &e);
} ASSERT(select(fd + 1, NULL, &w, &e, NULL) == 1);
ASSERT(FD_ISSET(fd, &w));
ASSERT(!FD_ISSET(fd, &e));
p.fd = fd, p.events = POLLOUT, p.revents = 0;
ASSERT(poll(&p, 1, 0) == 1);
ASSERT(p.revents == POLLOUT);
ASSERT(fcntl(fd, F_SETFL, 0) == 0);
snprintf(req, sizeof(req),"GET / HTTP/1.0\r\nHost: %s\r\n\r\n", d->hostname); snprintf(req, sizeof(req),"GET / HTTP/1.0\r\nHost: %s\r\n\r\n", d->hostname);
send(fd, req, strlen(req), 0); send(fd, req, strlen(req), 0);
size_t total = 0; size_t total = 0;
@@ -69,84 +237,475 @@ static void client_task(void *args) {
(void) args; (void) args;
} }
// echo_task: one per accepted connection. Echoes data back. // Echo data back from one accepted connection.
static void echo_task(void *args) { static void echo_task_common(int fd, int flags, volatile bool *done) {
int fd = (int) (uintptr_t) args; bool nonblock = (fcntl(fd, F_GETFL, 0) & O_NONBLOCK) != 0;
bool dontwait = (flags & MSG_DONTWAIT) != 0;
bool echoed = false, eagain = false;
static const size_t sizes[] = {1, 7, 17, 255, 256, 257, 511, 512}; static const size_t sizes[] = {1, 7, 17, 255, 256, 257, 511, 512};
char buf[512]; char buf[512];
size_t i = 0; size_t i = 0;
ssize_t n; ssize_t n;
MG_INFO(("Echo task started: %d", fd)); MG_INFO(("Echo task started: %d", fd));
while ((n = recv(fd, buf, sizes[i++ % (sizeof(sizes) / sizeof(sizes[0]))], 0)) > 0) for (;;) {
send(fd, buf, (size_t) n, 0); n = recv(fd, buf, sizes[i++ % (sizeof(sizes) / sizeof(sizes[0]))], flags);
if (n > 0) {
if (!nonblock) {
ssize_t w = send(fd, buf, (size_t) n, 0);
if (done != NULL) ASSERT(w == n);
echoed = w == n;
} else {
size_t sent = 0;
while (sent < (size_t) n) {
ssize_t w = send(fd, buf + sent, (size_t) n - sent, 0);
if (w > 0) sent += (size_t) w;
else if (w < 0 && errno == EAGAIN) vTaskDelay(1);
else {
ASSERT(w > 0);
break;
}
}
if (sent != (size_t) n) break;
echoed = true;
}
} else if ((nonblock || dontwait) && n < 0 && errno == EAGAIN) {
if (echoed) eagain = true;
vTaskDelay(1);
} else {
break;
}
}
if (done != NULL) {
ASSERT(eagain);
*done = true;
}
close(fd); close(fd);
vTaskDelete(NULL); vTaskDelete(NULL);
} }
static void echo_task(void *args) {
echo_task_common((int) (uintptr_t) args, 0, NULL);
}
static void echo_dontwait_task(void *args) {
echo_task_common((int) (uintptr_t) args, MSG_DONTWAIT, &s_dontwait_done);
}
static void echo_nonblock_task(void *args) {
echo_task_common((int) (uintptr_t) args, 0, &s_nonblock_done);
}
static void tx_gate_task(void *args) {
// receiver/sender > network task > gate: final send is waiting
while (!s_send_waiting) vTaskDelay(1);
ASSERT(!s_send_done);
if (s_send_expect_error) s_tx_error = s_tx_fail = true;
else s_tx_held = false;
vTaskDelete(NULL);
(void) args;
}
static void recv_wait_task(void *args) {
char buf;
int fd = (int) (uintptr_t) args;
ssize_t n;
while (!s_recv_start) vTaskDelay(1);
s_recv_waiting = true; // send_task starts after recv() is waiting
n = recv(fd, &buf, sizeof(buf), 0);
if (s_recv_expect_error) {
ASSERT(n < 0 && errno == EIO); // bsd_client aborts with unread data
} else {
ASSERT(n == 0); // bsd_client drains, then closes
}
s_recv_done = true;
close(fd); // Receiver owns the shared socket close
vTaskDelete(NULL);
}
static void send_task(void *args) {
static const char buf[512] = {0};
static const char marker = '\1';
int fd = (int) (uintptr_t) args;
ssize_t n;
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
s_tx_held = true; // Gate TX until c->send fills
do {
n = send(fd, buf, sizeof(buf), 0);
} while (n == (ssize_t) sizeof(buf));
ASSERT(n < 0 && errno == EAGAIN); // Full c->send: O_NONBLOCK
while (!s_tx_waits) vTaskDelay(1); // Next write_conn hits the gate
ASSERT(fcntl(fd, F_SETFL, 0) == 0);
n = send(fd, buf, sizeof(buf), MSG_DONTWAIT);
ASSERT(n < 0 && errno == EAGAIN); // Same full c->send: MSG_DONTWAIT
if (!s_send_expect_error) {
s_recv_start = true; // O_NONBLOCK is per socket; now start the receiver
while (!s_recv_waiting) vTaskDelay(1);
}
s_send_waiting = true; // Gate task releases TX after this send blocks
n = send(fd, &marker, sizeof(marker), 0);
if (s_send_expect_error) ASSERT(n < 0 && errno == EPIPE);
else ASSERT(n == (ssize_t) sizeof(marker));
s_send_done = true;
vTaskDelete(NULL);
}
// accept loop. Waits for incoming connections on port 1234 and spawns // accept loop. Waits for incoming connections on port 1234 and spawns
// an echo_task for each one, allowing concurrent clients. // an echo_task for each one, allowing concurrent clients.
static void atask(void *args) { static void atask(void *args) {
const struct atask_args *cfg = (const struct atask_args *) args;
BaseType_t result; BaseType_t result;
mg_bsd_init(); int lfd = open_listener(5, cfg->listener_nonblock);
int lfd = socket(AF_INET, SOCK_STREAM, 0); if (cfg->once && cfg->listener_nonblock) {
struct sockaddr_in sa; ASSERT(accept(lfd, NULL, NULL) < 0 && errno == EAGAIN);
memset(&sa, 0, sizeof(sa)); s_accept_waiting = true;
sa.sin_family = AF_INET; }
sa.sin_port = htons(1234);
sa.sin_addr.s_addr = INADDR_ANY;
bind(lfd, (struct sockaddr *) &sa, sizeof(sa));
listen(lfd, 5);
fcntl(lfd, F_SETFL, O_NONBLOCK);
MG_INFO(("BSD echo server on :1234"));
while (!s_wait) { while (!s_wait) {
int fd = accept(lfd, NULL, NULL); int fd;
if (fd < 0) { vTaskDelay(pdMS_TO_TICKS(10)); continue; }
// echo_task at higher priority than task1 if (cfg->once && !cfg->listener_nonblock) s_accept_waiting = true;
result = xTaskCreate(echo_task, "echo", 512, (void *) (uintptr_t) fd, fd = accept(lfd, NULL, NULL);
if (fd < 0) {
if (!cfg->listener_nonblock) {
ASSERT(fd >= 0);
break;
}
vTaskDelay(pdMS_TO_TICKS(10));
continue;
}
// Accepted sockets start blocking, regardless of listener flags
ASSERT((fcntl(fd, F_GETFL, 0) & O_NONBLOCK) == 0);
if (cfg->worker_nonblock) ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
// Worker tasks run at higher priority than task1
if (cfg->receiver) {
result = xTaskCreate(recv_wait_task, "recv", 512,
(void *) (uintptr_t) fd,
configMAX_PRIORITIES - 1, NULL);
ASSERT(result == pdPASS);
}
if (cfg->gate != NULL) {
result = xTaskCreate(cfg->gate, "gate", 256, NULL,
configMAX_PRIORITIES - 3, NULL);
ASSERT(result == pdPASS);
}
result = xTaskCreate(cfg->task, "echo", 512, (void *) (uintptr_t) fd,
configMAX_PRIORITIES - 1, NULL); configMAX_PRIORITIES - 1, NULL);
MG_INFO(("Echo task %d: %s", fd, result == pdPASS ? "created" : "FAILED")); MG_INFO(("Echo task %d: %s", fd, result == pdPASS ? "created" : "FAILED"));
ASSERT(result == pdPASS); ASSERT(result == pdPASS);
if (cfg->once) break;
} }
close(lfd); close(lfd);
s_wait = false; if (cfg->once) s_accept_done = true;
else s_wait = false;
vTaskDelete(NULL); vTaskDelete(NULL);
(void) args;
} }
static void select_task(void *args) {
struct timeval zero = {0, 0};
fd_set r, w, e;
int fd, lfd, n;
(void) args;
lfd = open_listener(5, true);
FD_ZERO(&r);
FD_SET(lfd, &r);
ASSERT(select(lfd, &r, NULL, NULL, &zero) == 0);
ASSERT(!FD_ISSET(lfd, &r));
FD_ZERO(&r);
FD_SET(lfd + 1, &r);
ASSERT(select(lfd + 2, &r, NULL, NULL, &zero) < 0 && errno == EBADF);
FD_ZERO(&r);
FD_ZERO(&w);
FD_ZERO(&e);
FD_SET(lfd, &r);
FD_SET(lfd, &w);
FD_SET(lfd, &e);
s_select_waiting = true;
n = select(lfd + 1, &r, &w, &e, NULL);
ASSERT(n == 1);
ASSERT(FD_ISSET(lfd, &r));
ASSERT(!FD_ISSET(lfd, &w));
ASSERT(!FD_ISSET(lfd, &e));
fd = accept(lfd, NULL, NULL);
ASSERT(fd >= 0);
ASSERT(close(fd) == 0);
ASSERT(close(lfd) == 0);
s_select_done = true;
vTaskDelete(NULL);
}
static void poll_task(void *args) {
struct pollfd p;
int fd, lfd;
(void) args;
lfd = open_listener(5, true);
p.fd = lfd, p.events = POLLIN, p.revents = 0;
ASSERT(poll(&p, 1, 0) == 0);
ASSERT(p.revents == 0);
p.fd = lfd + 1, p.revents = 0;
ASSERT(poll(&p, 1, 0) == 1);
ASSERT(p.revents == POLLNVAL);
p.fd = lfd, p.revents = 0;
s_poll_waiting = true;
ASSERT(poll(&p, 1, -1) == 1);
ASSERT(p.revents == POLLIN);
fd = accept(lfd, NULL, NULL);
ASSERT(fd >= 0);
ASSERT(close(fd) == 0);
ASSERT(close(lfd) == 0);
s_poll_done = true;
vTaskDelete(NULL);
}
static void readiness_task(void *args) {
struct pollfd p;
fd_set r, w;
int fd, lfd;
(void) args;
lfd = open_listener(5, true);
s_ready_waiting = true;
while ((fd = accept(lfd, NULL, NULL)) < 0) {
ASSERT(errno == EAGAIN);
vTaskDelay(1);
}
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
FD_ZERO(&r);
FD_SET(fd, &r);
ASSERT(select(fd + 1, &r, NULL, NULL, NULL) == 1);
ASSERT(FD_ISSET(fd, &r));
p.fd = fd, p.events = POLLIN, p.revents = 0;
ASSERT(poll(&p, 1, 0) == 1);
ASSERT(p.revents == POLLIN);
FD_ZERO(&w);
FD_SET(fd, &w);
ASSERT(select(fd + 1, NULL, &w, NULL, NULL) == 1);
ASSERT(FD_ISSET(fd, &w));
p.events = POLLOUT, p.revents = 0;
ASSERT(poll(&p, 1, 0) == 1);
ASSERT(p.revents == POLLOUT);
ASSERT(close(lfd) == 0);
echo_task_common(fd, 0, &s_ready_done);
}
static void backlog_task(void *args) {
struct pollfd p;
BaseType_t result;
int fd, lfd;
(void) args;
lfd = open_listener(1, true);
p.fd = lfd, p.events = POLLIN, p.revents = 0;
s_backlog_ready = true;
while (poll(&p, 1, 0) == 0) vTaskDelay(1);
ASSERT(p.revents == POLLIN);
s_backlog_full = true; // One orphan occupies the BSD backlog
while (!s_backlog_accept) vTaskDelay(1);
ASSERT((fd = accept(lfd, NULL, NULL)) >= 0);
result = xTaskCreate(echo_task, "echo", 512, (void *) (uintptr_t) fd,
configMAX_PRIORITIES - 1, NULL);
ASSERT(result == pdPASS);
ASSERT(close(lfd) == 0);
vTaskDelete(NULL);
}
// task2: tests owner. // task2: tests owner.
static void task2(void *args) { static void task2(void *args) {
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
char *ip, *cmd[3]; char *ip;
pid_t pid;
int status; int status;
extern char **environ; pid_t first, second;
#define DASHBOARD(x) printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false":"true");
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
s_error = false; s_error = false;
s_wait = false; s_wait = false;
// atask at higher priority than task1
xTaskCreate(atask, "atask", 256, NULL, configMAX_PRIORITIES - 1, NULL);
ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip); ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip);
cmd[0] = (char *) "./bsd_client"; s_poll_waiting = s_poll_done = false;
cmd[1] = ip; ASSERT(xTaskCreate(poll_task, "poll", 512, NULL,
cmd[2] = NULL; configMAX_PRIORITIES - 1, NULL) == pdPASS);
ASSERT(posix_spawn(&pid, cmd[0], NULL, NULL, cmd, environ) == 0); while (!s_poll_waiting) vTaskDelay(1);
for (;;) { ASSERT(!s_poll_done);
pid_t r = waitpid(pid, &status, WNOHANG); run_client(ip, "hold");
ASSERT(r >= 0); while (!s_poll_done) vTaskDelay(1);
if (r == pid) break; DASHBOARD("poll_read");
vTaskDelay(pdMS_TO_TICKS(10));
} s_select_waiting = s_select_done = false;
ASSERT(WIFEXITED(status)); ASSERT(xTaskCreate(select_task, "select", 512, NULL,
ASSERT(WEXITSTATUS(status) == 0); configMAX_PRIORITIES - 1, NULL) == pdPASS);
mg_free(ip); while (!s_select_waiting) vTaskDelay(1);
ASSERT(!s_select_done);
run_client(ip, "hold");
while (!s_select_done) vTaskDelay(1);
DASHBOARD("select_read");
s_error = false;
s_ready_waiting = s_ready_done = false;
ASSERT(xTaskCreate(readiness_task, "ready", 512, NULL,
configMAX_PRIORITIES - 1, NULL) == pdPASS);
while (!s_ready_waiting) vTaskDelay(1);
run_client(ip, "1");
while (!s_ready_done) vTaskDelay(1);
DASHBOARD("select_child_read");
DASHBOARD("poll_child_read");
DASHBOARD("select_child_write");
DASHBOARD("poll_child_write");
#if 0
s_error = false;
s_backlog_ready = s_backlog_full = s_backlog_accept = false;
ASSERT(xTaskCreate(backlog_task, "backlog", 512, NULL,
configMAX_PRIORITIES - 1, NULL) == pdPASS);
while (!s_backlog_ready) vTaskDelay(1);
first = start_client(ip, "1"); // Fills listen(fd, 1)'s pending accept
while (!s_backlog_full) vTaskDelay(1);
second = start_client(ip, "expectclose"); // Rejected over the backlog
while (!client_done(second, &status)) vTaskDelay(1);
check_client(status);
s_backlog_accept = true;
while (!client_done(first, &status)) vTaskDelay(1);
check_client(status);
DASHBOARD("listen_backlog");
#else
(void) first;
(void) second;
(void) status;
(void) backlog_task;
#endif
s_error = false;
// atask at higher priority than task1
s_accept_waiting = s_accept_done = false;
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_accept_server,
configMAX_PRIORITIES - 1, NULL) == pdPASS);
while (!s_accept_waiting) vTaskDelay(pdMS_TO_TICKS(10));
ASSERT(!s_accept_done);
run_client(ip, "1");
while (!s_accept_done) vTaskDelay(pdMS_TO_TICKS(10));
DASHBOARD("blocking_accept");
s_error = false;
s_wait = false;
s_accept_waiting = s_accept_done = false;
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_nonblock_accept_server,
configMAX_PRIORITIES - 1, NULL) == pdPASS);
while (!s_accept_waiting) vTaskDelay(pdMS_TO_TICKS(10));
run_client(ip, "1");
while (!s_accept_done) vTaskDelay(pdMS_TO_TICKS(10));
DASHBOARD("nonblocking_accept");
s_error = false;
s_wait = false;
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_echo_server,
configMAX_PRIORITIES - 1, NULL) == pdPASS);
run_client(ip, NULL);
s_wait = true; s_wait = true;
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10)); while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
DASHBOARD("accept"); DASHBOARD("accept");
s_error = false;
s_wait = false;
s_dontwait_done = false;
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_dontwait_server,
configMAX_PRIORITIES - 1, NULL) == pdPASS);
run_client(ip, "1");
while (!s_dontwait_done) vTaskDelay(pdMS_TO_TICKS(10));
s_wait = true;
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
DASHBOARD("dontwait_recv");
s_error = false;
s_wait = false;
s_nonblock_done = false;
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_nonblock_server,
configMAX_PRIORITIES - 1, NULL) == pdPASS);
run_client(ip, "1");
while (!s_nonblock_done) vTaskDelay(pdMS_TO_TICKS(10));
s_wait = true;
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
DASHBOARD("nonblocking_recv");
s_error = false;
s_wait = s_tx_held = s_tx_waits = s_tx_fail = s_tx_error = false;
s_send_waiting = false;
s_send_done = s_send_expect_error = false;
s_recv_start = s_recv_waiting = s_recv_done = s_recv_expect_error = false;
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_send_server,
configMAX_PRIORITIES - 1, NULL) == pdPASS);
run_client(ip, "waitanddrain"); // Peer drains through the final marker
ASSERT(s_send_done);
DASHBOARD("nonblocking_send");
DASHBOARD("blocking_send");
while (!s_recv_done) vTaskDelay(pdMS_TO_TICKS(10));
DASHBOARD("blocking_recv");
DASHBOARD("shared_socket");
s_wait = true;
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
s_error = false;
s_wait = s_tx_held = s_tx_waits = s_tx_fail = s_tx_error = false;
s_send_waiting = false;
s_send_done = s_send_expect_error = false;
s_recv_start = s_recv_waiting = s_recv_done = false;
s_recv_expect_error = true;
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_send_server,
configMAX_PRIORITIES - 1, NULL) == pdPASS);
run_client(ip, "waitandabort"); // Peer leaves data unread and sends RST
ASSERT(s_send_done);
while (!s_recv_done) vTaskDelay(pdMS_TO_TICKS(10));
DASHBOARD("error_wakeup");
s_wait = true;
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
s_error = false;
s_wait = s_tx_held = s_tx_waits = s_tx_fail = s_tx_error = false;
s_send_waiting = false;
s_send_done = false;
s_send_expect_error = true;
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_send_error_server,
configMAX_PRIORITIES - 1, NULL) == pdPASS);
run_client(ip, "hold"); // Peer stays connected while the driver fails TX
while (!s_tx_error) vTaskDelay(pdMS_TO_TICKS(10));
ASSERT(s_send_done);
DASHBOARD("send_error_wakeup");
s_wait = true;
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
s_tx_held = s_tx_fail = s_tx_error = s_send_expect_error = false;
mg_free(ip);
s_error = false;
{
fd_set e;
struct pollfd p;
int fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(1234);
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
ASSERT(connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 &&
errno == EINPROGRESS);
FD_ZERO(&e);
FD_SET(fd, &e);
ASSERT(select(fd + 1, NULL, NULL, &e, NULL) == 1);
ASSERT(FD_ISSET(fd, &e));
p.fd = fd, p.events = 0, p.revents = 0;
ASSERT(poll(&p, 1, -1) == 1);
ASSERT((p.revents & POLLERR) != 0);
p.events = POLLOUT, p.revents = 0;
ASSERT(poll(&p, 1, 0) == 1);
ASSERT((p.revents & POLLERR) != 0);
ASSERT((p.revents & POLLOUT) == 0);
ASSERT(close(fd) == 0);
}
DASHBOARD("select_error");
DASHBOARD("poll_error");
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
s_error = false; s_error = false;
{ {
@@ -188,6 +747,7 @@ static void task2(void *args) {
while (!cd[0].done || !cd[1].done || !cd[2].done) vTaskDelay(pdMS_TO_TICKS(10)); while (!cd[0].done || !cd[1].done || !cd[2].done) vTaskDelay(pdMS_TO_TICKS(10));
} }
DASHBOARD("connect"); DASHBOARD("connect");
DASHBOARD("select_write");
// End task 1 // End task 1
s_done = true; s_done = true;
while (s_done) vTaskDelay(pdMS_TO_TICKS(10)); while (s_done) vTaskDelay(pdMS_TO_TICKS(10));
@@ -206,35 +766,205 @@ static void task2(void *args) {
static void task1(void *args) { static void task1(void *args) {
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
// Stack initialization, Network configuration (DHCP lease, ...) wait_ready(mgr);
#if BSDTEST_USING_DHCP == 0
MG_INFO(("MIF configuration: Static IP"));
ASSERT(mif.ip != 0); // Check we have a satic IP assigned
mg_mgr_poll(&mgr, 0); // For initialisation
#else
MG_INFO(("MIF configuration: DHCP"));
ASSERT(mgr->ifp->ip == 0); // Check we are set for DHCP
while (mgr->ifp->ip == 0 && mg_millis() < 5000) {
mg_mgr_poll(mgr, 0);
}
if (mgr->ifp->ip == 0) MG_ERROR(("No ip assigned (DHCP lease may have failed).\n"));
ASSERT(mgr->ifp->ip != 0); // We have an IP (lease or static)
#endif
while (mgr->ifp->state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(mgr, 0);
}
MG_INFO(("Interface started, starting main loop"));
xTaskCreate(task2, "task2", 256, mgr, configMAX_PRIORITIES - 2, NULL); xTaskCreate(task2, "task2", 256, mgr, configMAX_PRIORITIES - 2, NULL);
while (!s_done) { while (!s_done) {
mg_mgr_poll(mgr, 0); mg_mgr_poll(mgr, 0);
mg_bsd_poll(mgr);
vTaskDelay(1); // Let idle reclaim deleted echo tasks vTaskDelay(1); // Let idle reclaim deleted echo tasks
} }
s_done = false; s_done = false;
vTaskDelete(NULL); vTaskDelete(NULL);
} }
#else
#define NORTOS_ECHO_CLIENTS 3
struct nortos_echo_client {
int fd;
size_t len, sent;
bool eof;
char buf[512];
};
static int nortos_accept(struct mg_mgr *mgr, int lfd) {
int fd;
while ((fd = accept(lfd, NULL, NULL)) < 0) {
ASSERT(errno == EAGAIN);
mg_mgr_poll(mgr, 0);
}
return fd;
}
static void nortos_wait_client(struct mg_mgr *mgr, pid_t pid) {
int status;
while (!client_done(pid, &status)) mg_mgr_poll(mgr, 0);
check_client(status);
}
static void nortos_echo(struct mg_mgr *mgr, struct nortos_echo_client *clients,
size_t count, pid_t pid) {
struct pollfd p[NORTOS_ECHO_CLIENTS];
fd_set r;
size_t i, live = count;
int maxfd, result;
ssize_t n;
while (live > 0) {
for (i = 0; i < count; i++) {
struct nortos_echo_client *c = &clients[i];
if (!c->eof && c->sent < c->len) {
n = send(c->fd, c->buf + c->sent, c->len - c->sent, 0);
if (n > 0) c->sent += (size_t) n;
else ASSERT(n < 0 && errno == EAGAIN);
}
}
FD_ZERO(&r);
maxfd = -1;
for (i = 0; i < count; i++) {
struct nortos_echo_client *c = &clients[i];
p[i].fd = -1, p[i].events = POLLIN, p[i].revents = 0;
if (!c->eof && c->sent == c->len) {
p[i].fd = c->fd;
FD_SET(c->fd, &r);
if (c->fd > maxfd) maxfd = c->fd;
}
}
if (maxfd >= 0) {
result = poll(p, (nfds_t) count, 0);
ASSERT(result >= 0);
if (result > 0) {
ASSERT(select(maxfd + 1, &r, NULL, NULL, NULL) == result);
for (i = 0; i < count; i++) {
struct nortos_echo_client *c = &clients[i];
bool readable = (p[i].revents & POLLIN) != 0;
ASSERT(p[i].revents == (readable ? POLLIN : 0));
ASSERT(FD_ISSET(c->fd, &r) == readable);
if (readable) {
n = recv(c->fd, c->buf, sizeof(c->buf), 0);
ASSERT(n >= 0);
if (n > 0) c->len = (size_t) n, c->sent = 0;
else c->eof = true, live--;
}
}
}
}
mg_mgr_poll(mgr, 0);
}
nortos_wait_client(mgr, pid);
}
static void nortos_test(struct mg_mgr *mgr) {
static const char bootstrap = '\0';
static const char grow[512] = {0};
static const char marker = '\1';
struct sockaddr_in sa;
struct timeval zero = {0, 0};
struct pollfd p;
fd_set r, w, e;
char buf;
char *ip;
pid_t pid;
size_t i, j;
int fd, lfd, n;
struct nortos_echo_client clients[NORTOS_ECHO_CLIENTS];
ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip);
ASSERT(ip != NULL);
lfd = open_listener(5, true);
p.fd = lfd, p.events = POLLIN, p.revents = 0;
ASSERT(poll(&p, 1, 10) == 0);
ASSERT(p.revents == 0);
p.fd = lfd + 1, p.revents = 0;
ASSERT(poll(&p, 1, 10) == 1);
ASSERT(p.revents == POLLNVAL);
p.fd = lfd, p.revents = 0;
FD_ZERO(&r);
FD_SET(lfd, &r);
ASSERT(select(lfd, &r, NULL, NULL, &zero) == 0);
ASSERT(!FD_ISSET(lfd, &r));
FD_ZERO(&r);
FD_SET(lfd + 1, &r);
ASSERT(select(lfd + 2, &r, NULL, NULL, &zero) < 0 && errno == EBADF);
ASSERT(accept(lfd, NULL, NULL) < 0 && errno == EAGAIN);
DASHBOARD("nortos_nonblocking_accept");
pid = start_client(ip, "waitanddrain");
do {
p.revents = 0;
n = poll(&p, 1, 10);
if (n == 0) mg_mgr_poll(mgr, 0);
} while (n == 0);
ASSERT(n == 1);
ASSERT(p.revents == POLLIN);
DASHBOARD("nortos_poll_read");
do {
FD_ZERO(&r);
FD_ZERO(&w);
FD_ZERO(&e);
FD_SET(lfd, &r);
FD_SET(lfd, &w);
FD_SET(lfd, &e);
n = select(lfd + 1, &r, &w, &e, NULL);
if (n == 0) mg_mgr_poll(mgr, 0);
} while (n == 0);
ASSERT(n == 1);
ASSERT(FD_ISSET(lfd, &r));
ASSERT(!FD_ISSET(lfd, &w));
ASSERT(!FD_ISSET(lfd, &e));
DASHBOARD("nortos_select_read");
fd = nortos_accept(mgr, lfd);
ASSERT(recv(fd, &buf, sizeof(buf), MSG_DONTWAIT) < 0 && errno == EAGAIN);
DASHBOARD("nortos_dontwait_recv");
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
ASSERT(recv(fd, &buf, sizeof(buf), 0) < 0 && errno == EAGAIN);
DASHBOARD("nortos_nonblocking_recv");
ASSERT(send(fd, &bootstrap, sizeof(bootstrap), 0) ==
(ssize_t) sizeof(bootstrap));
s_fail_alloc = true; // Fail growth after c->send has been bootstrapped
ASSERT(send(fd, grow, sizeof(grow), 0) < 0 && errno == EAGAIN);
s_fail_alloc = false;
ASSERT(send(fd, &marker, sizeof(marker), 0) == (ssize_t) sizeof(marker));
nortos_wait_client(mgr, pid);
ASSERT(close(fd) == 0);
DASHBOARD("nortos_nonblocking_send");
memset(clients, 0, sizeof(clients));
pid = start_client(ip, "3");
for (i = 0; i < NORTOS_ECHO_CLIENTS; i++) {
clients[i].fd = nortos_accept(mgr, lfd);
ASSERT(fcntl(clients[i].fd, F_SETFL, O_NONBLOCK) == 0);
for (j = 0; j < i; j++) ASSERT(clients[i].fd != clients[j].fd);
}
nortos_echo(mgr, clients, NORTOS_ECHO_CLIENTS, pid);
for (i = 0; i < NORTOS_ECHO_CLIENTS; i++)
ASSERT(close(clients[i].fd) == 0);
ASSERT(close(lfd) == 0);
DASHBOARD("nortos_select_children");
DASHBOARD("nortos_poll_children");
DASHBOARD("nortos_echo");
fd = socket(AF_INET, SOCK_STREAM, 0);
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(1234);
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
ASSERT(connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 &&
errno == EINPROGRESS);
ASSERT(close(fd) == 0);
mg_mgr_poll(mgr, 0);
DASHBOARD("nortos_nonblocking_connect");
mg_free(ip);
}
#endif // MG_ENABLE_FREERTOS
static bool bsd_x_test(struct mg_tcpip_driver *driver, void *driver_data, const char *mac, const char *debug_level) { static bool bsd_x_test(struct mg_tcpip_driver *driver, void *driver_data, const char *mac, const char *debug_level) {
if (debug_level == NULL) debug_level = "3"; if (debug_level == NULL) debug_level = "3";
mg_log_set(atoi(debug_level)); mg_log_set(atoi(debug_level));
@@ -263,8 +993,21 @@ static bool bsd_x_test(struct mg_tcpip_driver *driver, void *driver_data, const
MG_INFO(("Init done, starting interface")); MG_INFO(("Init done, starting interface"));
usleep(200000); // 200 ms usleep(200000); // 200 ms
#if MG_ENABLE_FREERTOS
s_tx = driver->tx;
driver->tx = tx_gate;
xTaskCreate(task1, "task1", 2048, &mgr, configMAX_PRIORITIES - 2, NULL); xTaskCreate(task1, "task1", 2048, &mgr, configMAX_PRIORITIES - 2, NULL);
vTaskStartScheduler(); vTaskStartScheduler();
driver->tx = s_tx;
#else
s_bootmillis = mg_millis();
wait_ready(&mgr);
nortos_test(&mgr);
s_error = false;
mg_mgr_free(&mgr);
ASSERT(mgr.conns == NULL);
printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false" : "true");
#endif
#ifdef NO_ABORT #ifdef NO_ABORT
if (s_abort != 0) return false; if (s_abort != 0) return false;
+11 -7
View File
@@ -1,18 +1,13 @@
#pragma once #pragma once
// See https://mongoose.ws/documentation/#build-options // See https://mongoose.ws/docs/getting-started/build-environment/
//#undef MG_ARCH
//#define MG_ARCH MG_ARCH_UNIX
#define MG_ENABLE_FREERTOS 1
#define MG_ENABLE_TCPIP 1 #define MG_ENABLE_TCPIP 1
#define MG_ENABLE_TCPIP_DRIVER_INIT 0 #define MG_ENABLE_TCPIP_DRIVER_INIT 0
#define MG_ENABLE_BSD_SOCKETS 1 #define MG_ENABLE_BSD_SOCKETS 1
#define MG_ENABLE_BSD_LOG 1
#define MG_ENABLE_LINES 1 #define MG_ENABLE_LINES 1
#define MG_ENABLE_CUSTOM_RANDOM 1 #define MG_ENABLE_CUSTOM_RANDOM 1
//#include <ctype.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h> #include <limits.h>
#include <stdarg.h> #include <stdarg.h>
@@ -24,7 +19,16 @@
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#if MG_ENABLE_FREERTOS
#else
#define MG_ENABLE_CUSTOM_CALLOC 1
#define MG_ENABLE_CUSTOM_MILLIS 1
#endif
+1 -1
View File
@@ -1,5 +1,5 @@
ROOT ?= $(realpath $(CURDIR)/../..) ROOT ?= $(realpath $(CURDIR)/../..)
PROJECTS ?= ../../tutorials/http/device-dashboard/esp32/ ../../tutorials/micropython/esp32/ ../../tutorials/http/uart-bridge/esp32/ PROJECTS ?= ../../tutorials/esp32/device-dashboard/ ../../tutorials/micropython/esp32/ ../../tutorials/esp32/uart-bridge/
TARGET ?= Debug TARGET ?= Debug
DOCKER = docker run --rm -v $(ROOT):$(ROOT) -v $(CURDIR):/root -w $(CURDIR) DOCKER = docker run --rm -v $(ROOT):$(ROOT) -v $(CURDIR):/root -w $(CURDIR)
IMAGE ?= espressif/idf IMAGE ?= espressif/idf
+6 -7
View File
@@ -176,7 +176,6 @@ static void fcb(struct mg_connection *c, int ev, void *ev_data) {
snprintf(fd->buf, FETCH_BUF_SIZE, "%.*s", (int) hm->message.len, snprintf(fd->buf, FETCH_BUF_SIZE, "%.*s", (int) hm->message.len,
hm->message.buf); hm->message.buf);
fd->code = atoi(hm->uri.buf); fd->code = atoi(hm->uri.buf);
fd->closed = 1;
c->is_closing = 1; c->is_closing = 1;
MG_DEBUG(("CODE: %d, MSG: %.*s", fd->code, (int) hm->message.len, MG_DEBUG(("CODE: %d, MSG: %.*s", fd->code, (int) hm->message.len,
hm->message.buf)); hm->message.buf));
@@ -214,7 +213,8 @@ static int fetch(struct mg_mgr *mgr, char *buf, const char *url,
// given enough traffic, the timer expires before we get a // given enough traffic, the timer expires before we get a
// chance to see the response // chance to see the response
} }
if (!fd.closed) c->is_closing = 1; if (!fd.closed) // failed, force closure and conn removal, and loop end
((struct connstate *) (c + 1))->is_closing = 1;
mg_mgr_poll(mgr, 0); mg_mgr_poll(mgr, 0);
return fd.code; return fd.code;
} }
@@ -480,8 +480,7 @@ static void test_http_server(struct mg_mgr *mgr) {
} }
static void test_tls(struct mg_mgr *mgr) { static void test_tls(struct mg_mgr *mgr) {
#if MG_TLS #if MG_TLS && 0 // TODO(cpq): restore multi-record TLS test
char *url;
char buf[FETCH_BUF_SIZE]; // make sure it can hold Makefile char buf[FETCH_BUF_SIZE]; // make sure it can hold Makefile
mg_mem_files = mg_packed_files; // Use generated packed filesystem mg_mem_files = mg_packed_files; // Use generated packed filesystem
struct mg_str data = mg_unpacked("/Makefile"); struct mg_str data = mg_unpacked("/Makefile");
@@ -496,7 +495,8 @@ static void test_tls(struct mg_mgr *mgr) {
// - Fire patched server, test multiple TLS records per TCP segment handling // - Fire patched server, test multiple TLS records per TCP segment handling
// skip other TLS stacks to avoid "bad client hello", we are 1.3 only // skip other TLS stacks to avoid "bad client hello", we are 1.3 only
if (access("tls_multirec/server", X_OK) == 0) { if (access("tls_multirec/server", X_OK) == 0) {
url = mg_mprintf("https://%s:8443", host_ip); char url[256];
mg_snrintf(url, sizeof(url), "https://%s:8443", host_ip);
ASSERT(system("tls_multirec/server -d tls_multirec &") == 0); ASSERT(system("tls_multirec/server -d tls_multirec &") == 0);
sleep(1); sleep(1);
// fetch() needs to loop enough times in order to process all TLS records; // fetch() needs to loop enough times in order to process all TLS records;
@@ -504,7 +504,6 @@ static void test_tls(struct mg_mgr *mgr) {
ASSERT(fetch(mgr, buf, url, "GET /thefile HTTP/1.0\n\n") == 200); ASSERT(fetch(mgr, buf, url, "GET /thefile HTTP/1.0\n\n") == 200);
ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile
ASSERT(system("killall tls_multirec/server") == 0); ASSERT(system("killall tls_multirec/server") == 0);
free(url);
} else { } else {
printf("SKIPPED TLS MULTIPLE RECORDS TEST, tls_multirec/server NOT PRESENT\n"); printf("SKIPPED TLS MULTIPLE RECORDS TEST, tls_multirec/server NOT PRESENT\n");
} }
@@ -512,7 +511,6 @@ static void test_tls(struct mg_mgr *mgr) {
printf("SKIPPED TLS MULTIPLE RECORDS TEST, not a known TLS 1.3 stack\n"); printf("SKIPPED TLS MULTIPLE RECORDS TEST, not a known TLS 1.3 stack\n");
(void) cmpbody("", ""); (void) cmpbody("", "");
(void) mgr; (void) mgr;
(void) url; // these three: NO SIMPLE TLS TEST
(void) buf; (void) buf;
(void) data; (void) data;
#endif #endif
@@ -593,6 +591,7 @@ bool mip_x_test(struct mg_mgr *mgr) {
// Clear // Clear
s_error = false; s_error = false;
mg_mgr_poll(mgr, 0); // prevent mDash from trashing mg_mgr_free()
mg_mgr_free(mgr); mg_mgr_free(mgr);
ASSERT(mgr->conns == NULL); // Deconstruction OK ASSERT(mgr->conns == NULL); // Deconstruction OK
printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false":"true"); printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false":"true");
-9
View File
@@ -1,9 +0,0 @@
CFLAGS = -g -I. # This is a known version, no need to check
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_IPV6=1
CFLAGS_EXTRA ?= -DMG_TLS=MG_TLS_BUILTIN
server: main.c patched_mongoose.c mongoose.h Makefile
$(CC) main.c patched_mongoose.c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@
clean:
rm -rf server *.exe *.o *.dSYM
-184
View File
@@ -1,184 +0,0 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
#include <signal.h>
#include "mongoose.h"
static int s_debug_level = MG_LL_INFO;
static const char *s_root_dir = ".";
static const char *s_addr1 = "http://0.0.0.0:8000";
static const char *s_addr2 = "https://0.0.0.0:8443";
static const char *s_enable_hexdump = "no";
static const char *s_ssi_pattern = "#.html";
static const char *s_upload_dir = NULL; // File uploads disabled by default
// Self signed certificates, see
// https://github.com/cesanta/mongoose/blob/master/test/certs/generate.sh
#ifdef TLS_TWOWAY
static const char *s_tls_ca =
"-----BEGIN CERTIFICATE-----\n"
"MIIBFTCBvAIJAMNTFtpfcq8NMAoGCCqGSM49BAMCMBMxETAPBgNVBAMMCE1vbmdv\n"
"b3NlMB4XDTI0MDUwNzE0MzczNloXDTM0MDUwNTE0MzczNlowEzERMA8GA1UEAwwI\n"
"TW9uZ29vc2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASuP+86T/rOWnGpEVhl\n"
"fxYZ+pjMbCmDZ+vdnP0rjoxudwRMRQCv5slRlDK7Lxue761sdvqxWr0Ma6TFGTNg\n"
"epsRMAoGCCqGSM49BAMCA0gAMEUCIQCwb2CxuAKm51s81S6BIoy1IcandXSohnqs\n"
"us64BAA7QgIgGGtUrpkgFSS0oPBlCUG6YPHFVw42vTfpTC0ySwAS0M4=\n"
"-----END CERTIFICATE-----\n";
#endif
static const char *s_tls_cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIBMTCB2aADAgECAgkAluqkgeuV/zUwCgYIKoZIzj0EAwIwEzERMA8GA1UEAwwI\n"
"TW9uZ29vc2UwHhcNMjQwNTA3MTQzNzM2WhcNMzQwNTA1MTQzNzM2WjARMQ8wDQYD\n"
"VQQDDAZzZXJ2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASo3oEiG+BuTt5y\n"
"ZRyfwNr0C+SP+4M0RG2pYkb2v+ivbpfi72NHkmXiF/kbHXtgmSrn/PeTqiA8M+mg\n"
"BhYjDX+zoxgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwIDRwAw\n"
"RAIgTXW9MITQSwzqbNTxUUdt9DcB+8pPUTbWZpiXcA26GMYCIBiYw+DSFMLHmkHF\n"
"+5U3NXW3gVCLN9ntD5DAx8LTG8sB\n"
"-----END CERTIFICATE-----\n";
static const char *s_tls_key =
"-----BEGIN EC PRIVATE KEY-----\n"
"MHcCAQEEIAVdo8UAScxG7jiuNY2UZESNX/KPH8qJ0u0gOMMsAzYWoAoGCCqGSM49\n"
"AwEHoUQDQgAEqN6BIhvgbk7ecmUcn8Da9Avkj/uDNERtqWJG9r/or26X4u9jR5Jl\n"
"4hf5Gx17YJkq5/z3k6ogPDPpoAYWIw1/sw==\n"
"-----END EC PRIVATE KEY-----\n";
// Handle interrupts, like Ctrl-C
static int s_signo;
static void signal_handler(int signo) {
s_signo = signo;
}
// Event handler for the listening connection.
// Simply serve static files from `s_root_dir`
static void cb(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_ACCEPT && c->fn_data != NULL) {
struct mg_tls_opts opts;
memset(&opts, 0, sizeof(opts));
#ifdef TLS_TWOWAY
opts.ca = mg_str(s_tls_ca);
#endif
opts.cert = mg_str(s_tls_cert);
opts.key = mg_str(s_tls_key);
mg_tls_init(c, &opts);
}
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/upload"), NULL)) {
// Serve file upload
if (s_upload_dir == NULL) {
mg_http_reply(c, 403, "", "Denied: file upload directory not set\n");
} else {
struct mg_http_part part;
size_t pos = 0, total_bytes = 0, num_files = 0;
while ((pos = mg_http_next_multipart(hm->body, pos, &part)) > 0) {
char path[MG_PATH_MAX];
MG_INFO(("Chunk name: [%.*s] filename: [%.*s] length: %lu bytes",
part.name.len, part.name.buf, part.filename.len,
part.filename.buf, part.body.len));
mg_snprintf(path, sizeof(path), "%s/%.*s", s_upload_dir,
part.filename.len, part.filename.buf);
if (mg_path_is_sane(mg_str(path))) {
mg_file_write(&mg_fs_posix, path, part.body.buf, part.body.len);
total_bytes += part.body.len;
num_files++;
} else {
MG_ERROR(("Rejecting dangerous path %s", path));
}
}
mg_http_reply(c, 200, "", "Uploaded %lu files, %lu bytes\n", num_files,
total_bytes);
}
} else {
// Serve web root directory
struct mg_http_serve_opts opts = {0};
opts.root_dir = s_root_dir;
opts.ssi_pattern = s_ssi_pattern;
mg_http_serve_dir(c, hm, &opts);
}
// Log request
MG_INFO(("%.*s %.*s %lu -> %.*s %lu", hm->method.len, hm->method.buf,
hm->uri.len, hm->uri.buf, hm->body.len, 3, c->send.buf + 9,
c->send.len));
}
}
static void usage(const char *prog) {
fprintf(stderr,
"Mongoose v.%s\n"
"Usage: %s OPTIONS\n"
" -H yes|no - enable traffic hexdump, default: '%s'\n"
" -S PAT - SSI filename pattern, default: '%s'\n"
" -d DIR - directory to serve, default: '%s'\n"
" -l ADDR - listening address, default: '%s'\n"
" -u DIR - file upload directory, default: unset\n"
" -v LEVEL - debug level, from 0 to 4, default: %d\n",
MG_VERSION, prog, s_enable_hexdump, s_ssi_pattern, s_root_dir,
s_addr1, s_debug_level);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
char path[MG_PATH_MAX] = ".";
struct mg_mgr mgr;
struct mg_connection *c;
int i;
// Parse command-line flags
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-d") == 0) {
s_root_dir = argv[++i];
} else if (strcmp(argv[i], "-H") == 0) {
s_enable_hexdump = argv[++i];
} else if (strcmp(argv[i], "-S") == 0) {
s_ssi_pattern = argv[++i];
} else if (strcmp(argv[i], "-l") == 0) {
s_addr1 = argv[++i];
} else if (strcmp(argv[i], "-l2") == 0) {
s_addr2 = argv[++i];
} else if (strcmp(argv[i], "-u") == 0) {
s_upload_dir = argv[++i];
} else if (strcmp(argv[i], "-v") == 0) {
s_debug_level = atoi(argv[++i]);
} else {
usage(argv[0]);
}
}
// Root directory must not contain double dots. Make it absolute
// Do the conversion only if the root dir spec does not contain overrides
if (strchr(s_root_dir, ',') == NULL) {
realpath(s_root_dir, path);
s_root_dir = path;
}
// Initialise stuff
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
mg_log_set(s_debug_level);
mg_mgr_init(&mgr);
if ((c = mg_http_listen(&mgr, s_addr1, cb, NULL)) == NULL) {
MG_ERROR(("Cannot listen on %s. Use http://ADDR:PORT or :PORT",
s_addr1));
exit(EXIT_FAILURE);
}
if ((c = mg_http_listen(&mgr, s_addr2, cb, (void *) 1)) == NULL) {
MG_ERROR(("Cannot listen on %s. Use http://ADDR:PORT or :PORT",
s_addr2));
exit(EXIT_FAILURE);
}
if (mg_casecmp(s_enable_hexdump, "yes") == 0) c->is_hexdumping = 1;
// Start infinite event loop
MG_INFO(("Mongoose version : v%s", MG_VERSION));
MG_INFO(("HTTP listener : %s", s_addr1));
MG_INFO(("HTTPS listener : %s", s_addr2));
MG_INFO(("Web root : [%s]", s_root_dir));
MG_INFO(("Upload dir : [%s]", s_upload_dir ? s_upload_dir : "unset"));
while (s_signo == 0) mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr);
MG_INFO(("Exiting on signal %d", s_signo));
return 0;
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
-1
View File
@@ -1 +0,0 @@
../Makefile
-22
View File
@@ -1,22 +0,0 @@
diff --git a/mongoose.c b/mongoose.c
index 44539e81..b3f1c83a 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -11300,9 +11300,16 @@ void mg_tls_free(struct mg_connection *c) {
long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
struct tls_data *tls = (struct tls_data *) c->tls;
long n = MG_IO_WAIT;
+ size_t maxsize = 256, encrypted = 0;
if (len > MG_IO_SIZE) len = MG_IO_SIZE;
if (len > 16384) len = 16384;
- mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA);
+ while (encrypted < len) {
+ const uint8_t *chunk = (const uint8_t *) buf + encrypted;
+ size_t chunksize = len - encrypted;
+ if (chunksize > maxsize) chunksize = maxsize;
+ mg_tls_encrypt(c, chunk, chunksize, MG_TLS_APP_DATA);
+ encrypted += chunksize;
+ }
while (tls->send.len > 0 &&
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
mg_iobuf_del(&tls->send, 0, (size_t) n);
+58 -40
View File
@@ -803,11 +803,18 @@ static void test_mqtt_parse(void) {
struct mg_mqtt_prop prop; struct mg_mqtt_prop prop;
// MQTT5 PUBLISH with Subscription Identifier = 5 // MQTT5 PUBLISH with Subscription Identifier = 5
const uint8_t sub_id_pkt[] = {0x30, 0x06, 0x00, 0x01, 'x', 0x02, 0x0B, 0x05}; const uint8_t sub_id_pkt[] = {0x30, 0x06, 0x00, 0x01, 'x', 0x02, 0x0B, 0x05};
// MQTT5 CONNACK with Server Keep Alive = 60
const uint8_t connack_pkt[] = {0x20, 0x06, 0x00, 0x00, 0x03, 0x13, 0x00, 0x3C};
memset(&mm, 0, sizeof(struct mg_mqtt_message)); memset(&mm, 0, sizeof(struct mg_mqtt_message));
memset(&prop, 0, sizeof(struct mg_mqtt_prop)); memset(&prop, 0, sizeof(struct mg_mqtt_prop));
ASSERT(mg_mqtt_parse(sub_id_pkt, sizeof(sub_id_pkt), 5, &mm) == MQTT_OK); ASSERT(mg_mqtt_parse(sub_id_pkt, sizeof(sub_id_pkt), 5, &mm) == MQTT_OK);
mg_mqtt_next_prop(&mm, &prop, 0); mg_mqtt_next_prop(&mm, &prop, 0);
ASSERT(mg_mqtt_parse(connack_pkt, sizeof(connack_pkt), 5, &mm) == MQTT_OK);
ASSERT(mm.ack == 0);
ASSERT(mg_mqtt_next_prop(&mm, &prop, 0) > 0);
ASSERT(prop.id == MQTT_PROP_SERVER_KEEP_ALIVE && prop.iv == 60);
} }
static void test_mqtt(void) { static void test_mqtt(void) {
@@ -918,6 +925,10 @@ static void fcb(struct mg_connection *c, int ev, void *ev_data) {
} }
} }
// Max number of mg_mgr_poll() calls fetch() waits for a response. Large
// transfers over builtin TLS with small records (MG_TLS_RECORD_SIZE) need more
static int s_fetch_polls = 100;
static int fetch(struct mg_mgr *mgr, char *buf, const char *url, static int fetch(struct mg_mgr *mgr, char *buf, const char *url,
const char *fmt, ...) { const char *fmt, ...) {
struct fetch_data fd = {buf, 0, 0}; struct fetch_data fd = {buf, 0, 0};
@@ -946,7 +957,7 @@ static int fetch(struct mg_mgr *mgr, char *buf, const char *url,
mg_vprintf(c, fmt, &ap); mg_vprintf(c, fmt, &ap);
va_end(ap); va_end(ap);
buf[0] = '\0'; buf[0] = '\0';
for (i = 0; i < 100 && buf[0] == '\0'; i++) mg_mgr_poll(mgr, 1); for (i = 0; i < s_fetch_polls && buf[0] == '\0'; i++) mg_mgr_poll(mgr, 1);
if (!fd.closed) c->is_closing = 1; if (!fd.closed) c->is_closing = 1;
mg_mgr_poll(mgr, 1); mg_mgr_poll(mgr, 1);
return fd.code; return fd.code;
@@ -1515,7 +1526,6 @@ static void test_http_404(void) {
} }
static void test_tls(void) { static void test_tls(void) {
// return;
#if MG_TLS #if MG_TLS
struct mg_mgr mgr; struct mg_mgr mgr;
struct mg_connection *c; struct mg_connection *c;
@@ -1538,6 +1548,7 @@ static void test_tls(void) {
// POST a large file, several MSS but less than max TLS record length // POST a large file, several MSS but less than max TLS record length
// make sure we drain TLS buffers and read all, #2619 // make sure we drain TLS buffers and read all, #2619
ASSERT(data.len > 3100 && data.len < 16384); // pick another file on failure ASSERT(data.len > 3100 && data.len < 16384); // pick another file on failure
s_fetch_polls = 5000; // Many small TLS records, one is decrypted per poll
ASSERT(fetch(&mgr, buf, url, ASSERT(fetch(&mgr, buf, url,
"POST /body HTTP/1.0\n" "POST /body HTTP/1.0\n"
"Content-Length: %lu\n\n" "Content-Length: %lu\n\n"
@@ -1554,29 +1565,7 @@ static void test_tls(void) {
"%s", "%s",
bd.len, bd.buf) == 200); bd.len, bd.buf) == 200);
ASSERT(cmpbody(buf, bd.buf) == 0); ASSERT(cmpbody(buf, bd.buf) == 0);
#if MG_TLS == MG_TLS_BUILTIN && defined(__linux__) && \ s_fetch_polls = 100;
MG_ENABLE_CHACHA20 // skip for non-CHACHA tests
// fire patched server, test multiple TLS records per TCP segment handling
// skip other TLS stacks to avoid "bad client hello", we are 1.3 only
if (access("tls_multirec/server", X_OK) == 0) {
ASSERT(system("tls_multirec/server -d tls_multirec &") == 0);
sleep(1);
// fetch() needs to loop enough times in order to process all TLS records;
// otherwise it will end with 200 and shorter file contents
ASSERT(fetch(&mgr, buf, "https://localhost:8443",
"GET /thefile HTTP/1.0\n\n") == 200);
data = mg_unpacked("/Makefile");
ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile
ASSERT(system("killall tls_multirec/server") == 0);
} else {
printf("\nSKIPPED TLS MULTIPLE RECORDS TEST, tls_multirec/server NOT PRESENT\n");
}
#else
printf(
"\n Skipping multiple TLS records per TCP segment handling test, server "
"is 1.3 only; re-enable when other stacks can be easily configured for "
"1.3\n");
#endif
// Repeat the simplest test with two-way authentication // Repeat the simplest test with two-way authentication
opts.ca = mg_unpacked("/certs/ca.crt"); // configure the server for two-way opts.ca = mg_unpacked("/certs/ca.crt"); // configure the server for two-way
@@ -3387,7 +3376,7 @@ static void test_http_chunked_case(mg_event_handler_t s, mg_event_handler_t c,
struct mg_mgr mgr; struct mg_mgr mgr;
uint32_t i, crc = 0, expected_crc = mg_crc32(0, expected, strlen(expected)); uint32_t i, crc = 0, expected_crc = mg_crc32(0, expected, strlen(expected));
struct mg_connection *conn; struct mg_connection *conn;
static uint16_t port = 32344; // To prevent bind errors on Windows static uint16_t port = 32344; // To prevent bind errors on Windows
mg_snprintf(url, sizeof(url), "http://127.0.0.1:%d", port++); mg_snprintf(url, sizeof(url), "http://127.0.0.1:%d", port++);
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
mg_http_listen(&mgr, url, s, NULL); mg_http_listen(&mgr, url, s, NULL);
@@ -3656,6 +3645,14 @@ static void test_check_ip_acl(void) {
ASSERT(mg_check_ip_acl(mg_str("-0.0.0.0/0,+1.0.0.0/8"), &ip) == 1); ASSERT(mg_check_ip_acl(mg_str("-0.0.0.0/0,+1.0.0.0/8"), &ip) == 1);
ASSERT(mg_check_ip_acl(mg_str("-0.0.0.0/0,+1.2.3.4"), &ip) == 1); ASSERT(mg_check_ip_acl(mg_str("-0.0.0.0/0,+1.2.3.4"), &ip) == 1);
ASSERT(mg_check_ip_acl(mg_str("-0.0.0.0/0,+1.0.0.0/16"), &ip) == 0); ASSERT(mg_check_ip_acl(mg_str("-0.0.0.0/0,+1.0.0.0/16"), &ip) == 0);
ASSERT(mg_check_ip_acl(mg_str("+1.2.3.4xyz"), &ip) == -2); // junk
ASSERT(mg_check_ip_acl(mg_str("+1.2.3.4/32abc"), &ip) == -2); // junk
ASSERT(mg_check_ip_acl(mg_str("+1.2.3.4/"), &ip) == -2);
ASSERT(mg_check_ip_acl(mg_str("+1.2.3.4/33"), &ip) == -2);
ASSERT(mg_check_ip_acl(mg_str("+1.2.3.256"), &ip) == -2);
ASSERT(mg_check_ip_acl(mg_str("+"), &ip) == -2);
// Not NUL-terminated: must not read past the end of the ACL
ASSERT(mg_check_ip_acl(mg_str_n("+1.2.3.4xyz", 8), &ip) == 1);
ip.is_ip6 = true; ip.is_ip6 = true;
ASSERT(mg_check_ip_acl(mg_str("-0.0.0.0/0"), &ip) == ASSERT(mg_check_ip_acl(mg_str("-0.0.0.0/0"), &ip) ==
-1); // not yet supported -1); // not yet supported
@@ -4409,18 +4406,39 @@ static void test_split(void) {
ASSERT(mg_strcmp(b, mg_str("")) == 0); ASSERT(mg_strcmp(b, mg_str("")) == 0);
} }
static void test_x25519(void) { // RFC 6979 A.2.5: P-256, SHA-256, message "sample"
static void test_p256(void) {
#if MG_TLS == MG_TLS_BUILTIN #if MG_TLS == MG_TLS_BUILTIN
uint8_t key[X25519_BYTES]; static const uint8_t x[32] = {
uint8_t buf[X25519_BYTES]; 0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21,
char tmp[100]; 0x57, 0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8,
size_t i; 0x9b, 0x12, 0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21};
for (i = 0; i < sizeof(key); i++) key[i] = (uint8_t) i; static const uint8_t ux[8] = {0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31};
for (i = 0; i < sizeof(buf); i++) buf[i] = 0; static const uint8_t uy[8] = {0x79, 0x03, 0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99};
mg_tls_x25519(buf, key, X25519_BASE_POINT, 1); static const uint8_t rs[64] = {
mg_snprintf(tmp, sizeof(tmp), "%M", mg_print_hex, sizeof(buf), buf); 0xef, 0xd4, 0x8b, 0x2a, 0xac, 0xb6, 0xa8, 0xfd, 0x11, 0x40, 0xdd, 0x9c,
MG_INFO(("%s", tmp)); 0xd4, 0x5e, 0x81, 0xd6, 0x9d, 0x2c, 0x87, 0x7b, 0x56, 0xaa, 0xf9, 0x91,
ASSERT(mg_strcmp(mg_str("8f40c5adb6"), mg_str_n(tmp, 10)) == 0); 0xc3, 0x4d, 0x0e, 0xa8, 0x4e, 0xaf, 0x37, 0x16, 0xf7, 0xcb, 0x1c, 0x94,
0x2d, 0x65, 0x7c, 0x41, 0xd4, 0x36, 0xc7, 0xa1, 0xb6, 0xe2, 0x9f, 0x65,
0xf3, 0xe9, 0x00, 0xdb, 0xb9, 0xaf, 0xf4, 0x06, 0x4d, 0xc4, 0xab, 0x2f,
0x84, 0x3a, 0xcd, 0xa8};
uint8_t pub[64], hash[32], sig[64], prv2[32], pub2[64], sec1[32], sec2[32];
mg_sha256(hash, (uint8_t *) "sample", 6);
ASSERT(mg_p256_compute_public_key(x, pub) == 1);
ASSERT(memcmp(pub, ux, sizeof(ux)) == 0);
ASSERT(memcmp(pub + 32, uy, sizeof(uy)) == 0);
ASSERT(mg_p256_sign(x, hash, sizeof(hash), sig) == 1);
ASSERT(memcmp(sig, rs, sizeof(rs)) == 0);
ASSERT(mg_p256_verify(pub, hash, sizeof(hash), sig) == 1);
sig[10] ^= 1;
ASSERT(mg_p256_verify(pub, hash, sizeof(hash), sig) == 0);
// ECDH: both sides compute the same secret
ASSERT(mg_p256_make_key(pub2, prv2) == 1);
ASSERT(mg_p256_shared_secret(pub2, x, sec1) == 1);
ASSERT(mg_p256_shared_secret(pub, prv2, sec2) == 1);
ASSERT(memcmp(sec1, sec2, sizeof(sec1)) == 0);
pub2[63] ^= 1; // Not on the curve anymore
ASSERT(mg_p256_shared_secret(pub2, x, sec1) == 0);
#endif #endif
} }
@@ -4643,8 +4661,8 @@ static void test_jwt(void) {
opts.kid = mg_str("ec-key-1"); opts.kid = mg_str("ec-key-1");
opts.private_key = key; opts.private_key = key;
opts.public_key = pub; opts.public_key = pub;
ASSERT(mg_uecc_parse_private_key(mg_str(key_pem), key, sizeof(key)) == 32); ASSERT(mg_p256_parse_private_key(mg_str(key_pem), key, sizeof(key)) == 32);
ASSERT(mg_uecc_compute_public_key(key, pub, mg_uecc_secp256r1()) == 1); ASSERT(mg_p256_compute_public_key(key, pub) == 1);
n = mg_jwt_sign_es256(&opts, jwt, sizeof(jwt)); n = mg_jwt_sign_es256(&opts, jwt, sizeof(jwt));
ASSERT(n > 0 && n + 1 < sizeof(jwt)); ASSERT(n > 0 && n + 1 < sizeof(jwt));
jwt[n] = '\0'; jwt[n] = '\0';
@@ -4666,7 +4684,7 @@ static void test_crypto(void) {
test_sha1(); test_sha1();
test_sha256(); test_sha256();
test_sha384(); test_sha384();
test_x25519(); test_p256();
test_rsa(); test_rsa();
test_jwt(); test_jwt();
} }
+5 -4
View File
@@ -9,13 +9,13 @@ The fastest way to quickly get Mongoose running on your hardware is to follow ou
Steps to get the most out of this code base: Steps to get the most out of this code base:
- Most code here has a README.md file that points to a verbose tutorial describing how to get the intended functionality out of Mongoose Network Library. Code there runs as is in Windows, Ubuntu Linux, and Mac (very few cases don't run in Windows). - Most workstation code here has a README.md file that points to a verbose tutorial describing how to get the intended functionality out of Mongoose Network Library. Code there runs as is in Windows, Ubuntu Linux, and Mac (very few cases don't run in Windows).
- Navigate your way to the desired protocol or function, read the README.md file - Navigate your way to the desired protocol or function, read the README.md file
- Follow the linked tutorial - Follow the linked tutorial
- In cases were there is no tutorial: - In cases were there is no tutorial:
- Follow the [Build Tools](https://mongoose.ws/documentation/tutorials/tools/) tutorial to setup your development environment. - Follow the [Build Tools](https://mongoose.ws/documentation/tutorials/tools/) tutorial to setup your development environment.
- run `make all` - run `make all`
- Embedded hardware tutorials are conveniently linked from OS tutorials, or grouped by vendor and board. - Embedded hardware tutorials are grouped by vendor and board.
- Navigate your way to the desired board or the closest one, read the README.md file. In most cases you'll find a link to our [Wizard](https://mongoose.ws/wizard/); for others, there is a link to a [Tutorial](https://mongoose.ws/documentation/#tutorials), as described at the top of this text. The Wizard will also link you to the tutorials you'll need. - Navigate your way to the desired board or the closest one, read the README.md file. In most cases you'll find a link to our [Wizard](https://mongoose.ws/wizard/); for others, there is a link to a [Tutorial](https://mongoose.ws/documentation/#tutorials), as described at the top of this text. The Wizard will also link you to the tutorials you'll need.
- In cases were there you don't find a link to a tutorial: - In cases were there you don't find a link to a tutorial:
- Follow the [Build Tools](https://mongoose.ws/documentation/tutorials/tools/) tutorial to setup your development environment. - Follow the [Build Tools](https://mongoose.ws/documentation/tutorials/tools/) tutorial to setup your development environment.
@@ -28,7 +28,8 @@ Steps to get the most out of this code base:
Rules for creating a new example: Rules for creating a new example:
- Makefile golden reference for desktop/server examples: [http-server](http/http-server/) - Makefile golden reference for desktop/server examples: [http-server](http/http-server/)
- Makefile golden reference for embedded examples: [stm32/nucleo-f746zg-baremetal-builtin-rndis](stm32/nucleo-f746zg-baremetal-builtin-rndis/) - Makefile golden reference for embedded examples: [stm32/nucleo-f746zg/minimal](stm32/nucleo-f746zg/minimal/)
- Desktop/server examples compile `$(REPO_ROOT)/mongoose.c` and add `$(REPO_ROOT)` to their include path
- Examples must build on Windows, Mac and Ubuntu Linux systems - Examples must build on Windows, Mac and Ubuntu Linux systems
- Assume that users installed their tools according to the [Build Tools](https://mongoose.ws/documentation/tutorials/tools/) tutorial - Assume that users installed their tools according to the [Build Tools](https://mongoose.ws/documentation/tutorials/tools/) tutorial
- Makefiles must not include other Makefiles - Makefiles must not include other Makefiles
@@ -37,5 +38,5 @@ Rules for creating a new example:
- Use `$(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)` to compile - Use `$(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)` to compile
- If an external repository is required, download it on demand using git shallow clone. See the embedded examples golden reference - If an external repository is required, download it on demand using git shallow clone. See the embedded examples golden reference
- Keep Makefiles as short as possible, but verbose to understand them easily - Keep Makefiles as short as possible, but verbose to understand them easily
- Symlink files when required, like `mongoose.c`, `ca.pem`, etc. Make no copies - Symlink files when required, like `ca.pem`, etc. Make no copies
- Examples README.md files should contain only the title and the link to the corresponding mongoose.ws tutorial page - Examples README.md files should contain only the title and the link to the corresponding mongoose.ws tutorial page
-1
View File
@@ -1 +0,0 @@
../http/http-server/arduino/esp32-http
-1
View File
@@ -1 +0,0 @@
../mqtt/mqtt-client/arduino/esp32-mqtt
-1
View File
@@ -1 +0,0 @@
../mqtt/mqtt-client/arduino/sim800-mqtt/
-1
View File
@@ -1 +0,0 @@
../http/http-server/arduino/teensy41-http/
+1
View File
@@ -0,0 +1 @@
../../http/device-dashboard/net.c
+1
View File
@@ -0,0 +1 @@
../../http/device-dashboard/net.h
+1
View File
@@ -0,0 +1 @@
../../http/device-dashboard/packed_fs.c
-1
View File
@@ -1 +0,0 @@
../http/http-server/arduino/w5500-http
Loaded 100 of 517 files, more files were not shown because too many files have changed in this diff. Show more