Tags

arm64 bhyve bit blacklistd bluepill bluetooth cu dns dovecot elf envsubst esp8266 filter freebsd iot ipfw lets_encrypt meross openocd opensmtpd openwrt perl prises python shell ssh ssl stm32 template tls unbound wifi yubikey

Powered by

blOg
maRkdown
awK
shEll

30/10/2024

[ iot prises meross python ]

202410300800 iot prises meross python

Alors que je baguenaudais sur un site de vente en ligne à une période propice à moultes réductions et dans un énième moment d'égarement j'ai fait l'acquisition de "prises connectées". Par 'égarement' j'entends qu'aucune recherche préalable de compatibilité avec mon environnement n'a été faite et que le besoin à ce moment était loin d'être justifié. Bref, j'ai craqué.

Le matos

Lot de 2 prises Meross MSS305ZF Smart Wi-Fi Plug 16A

Première impression

Autant le dire tout de suite: port série, Tasmota, firmware alternatif et autres bidouilleries ne seront pas de la partie. Et comme ce genre de bestiole s'accompagne toujours d'une application pour débilophone, l'oubli au fond du tiroir 'okazou' semblait être la destinée finale de cet achat compulsif. Après quelques (tardives) recherches, je m'aperçois que tout n'est pas perdu.

Configuration initiale

Je ne suis pas spécialiste de ce genre de gadget mais la séquence suivante me semble la façon usuelle de procéder:

Problème: mon débilophone est un Samsung S9 sous LineageOS (ce qui le rend tout de suite un peu moins con) qui n'a pas accès aux magasins d'applications.

Solution: Grâces soient rendues à Fabi019 et l'apk qu'il met à disposition depuis https://github.com/Fabi019/MerossBLE et qui s'installe sans problème. 2 minutes plus tard, une première prise est connectée. Le truc à retenir qui servira plus tard: la valeur du champ 'Key' du cartouche '1. Server Configuration' .

Faire mumuse

Je n'ai pas (encore ?) de solution domotique, j'ai vaguement lancé un serveur mqtt (la prise envoie bien des données dessus) mais ce qui m'intérresse c'est d'interagir directement avec la prise. Pour se faire j'ai trouvé des projets en go, nodejs et python. Comme chacun sait, le plus important c'est la doc. Et la doc, pour mon cas, elle est là: https://github.com/arandall/meross/blob/main/doc/protocol.md .

Le protocole n'est pas très compliqué et consiste à l'envoi d'un JSON à une url fixe. J'ai longtemps buté sur un élément:

sign == Signing value equal to md5(messageId + key + timestamp)

car, de ce que j'avais lu, cette 'key' semblait être fournie après l'ouverture d'un compte chez meross. J'ai finalement compris que je pouvais fixer cette valeur depuis l'application du débilophone, comme vu un peu plus haut (le truc à retenir).

Je me suis fendu de quelques lignes de python (car on n'est jamais aussi bien servi que par soi-même et non c'est pas du nih) et je peux désormais contrôler mes prises depuis la VM FreeBSD de ma Freebox Delta.

Reste à faire

La prise en mode "appairage" propose un point d'accès wifi à partir duquel il est possible de pousser la configuration. Je n'ai pas encore testé cette possibilité. Le point d'accès auquel se connectent les prises ne propose pas d'ipv6 et je ne trouve pas trace des adresses MAC des prises dans mon voisinage ipv6. Je pense qu'on peut faire des trucs rigolos avec les '.TriggerX' . Quant à 'Appliance.Control.Upgrade', si bucket-meross-static.meross.com existe, je n'ai pas encore trouvé le moyen d'obtenir le chemin vers une éventuelle mise à jour du firmware.

Conclusion

Est-ce que je regrette mon achat ? Non. Est-ce que je vais surveiller ma consommation électrique afin de la faire baisser ? Je ne crois pas. Est-ce que je vais faire du mqtt ? Je ne sais pas. Est-ce que je vais finir par installer une solution de domotique ? J'en doute mais si j'ai bien compris c'est un engrenage. Et comme j'ai aussi 2 prises 'Amazon smart plug', le mal est peut-être déjà fait.

Commentaires: https://github.com/bsdsx/blog_posts/issues/21


Lien vers ce billet

29/02/2024

[ bhyve wifi openwrt ]

202402290800 bhyve wifi openwrt

J'ai pendant très longtemps utilisé un mini routeur sous OpenWrt car le wifi de mon ancien nuc n'était ni stable ni performant. Les ressources de ma nouvelle machine de travail (AMD Ryzen 7 3750H) étant bien supérieures, l'idée m'est venue de mimer FreeBSD wifibox à la sauce OpenWrt. Un portable Lenovo T450 en a aussi fait les frais.

wifibox

Le principe est assez simple: si la plateforme matérielle le permet, attribuer le périphérique wifi à une machine virtuelle dont le pilote a une meilleure prise en charge que celui de FreeBSD. Le projet a fait le choix l'Alpine Linux.

wifi et bhyve

Je commence par identifier le périphérique:

$ pciconf -lv
...
iwm0@pci0:2:0:0:        class=0x028000 rev=0x59 hdr=0x00 vendor=0x8086 device=0x095a subvendor=0x8086 subdevice=0x5010
    vendor     = 'Intel Corporation'
    device     = 'Wireless 7265'
    class      = network
...

Il ne doit plus être pris en charge par le pilote iwm mais par le Pci PassThru (ppt). Je dois aussi charger le module vmm et parce que j'ai un processeur AMD activer un sysctl comme indiqué sur le wiki FreeBSD)

$ cat /boot/loader.conf.d/vmm.conf
vmm_load="YES"
pptdevs="2/0/0" # à adapter suivant pciconf -lv
hw.vmm.amdvi.enable=1

Un redémarrage plus tard, on obtient:

$ pciconf -lv
...
ppt0@pci0:2:0:0:	class=0x028000 rev=0x59 hdr=0x00 vendor=0x8086 device=0x095a subvendor=0x8086 subdevice=0x5010
    vendor     = 'Intel Corporation'
    device     = 'Wireless 7265'
    class      = network
...

Machine virtuelle

Je récupère la dernière image d'OpenWrt:

$ fetch https://downloads.openwrt.org/releases/23.05.2/targets/x86/64/openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img.gz
$ gunzip openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img.gz
gunzip: openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img.gz: trailing garbage ignored

Pour que bhyve puisse utiliser une image efi je dois ajouter le paquet suivant:

$ doas pkg install bhyve-firmware

et je vérifie que la machine virtuelle démarre bien:

$ doas bhyve -H -P -m 192M -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 -s 3:0,virtio-blk,./openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd -l com1,stdio -W vm0

Un halt plus tard je peux détruire cette machine virtuelle:

$ doas bhyvectl --destroy --vm=vm0

Utile quand on joue au plus malin et qu'avec 128 Mo de mémoire on obtient:

error: out of memory.
error: out of memory.


Press any key to continue...Press any key to continue...

OpenWrt

Léger problème avec l'image fournie: elle ne contient absolument rien (module, pilote, outil ...) concernant le wifi (c'est voulu par le projet pour la plate-forme x86). Je prépare un disque à ajouter à mon image avec le contenu nécessaire:

$ truncate -s 5M pkgs.img # oui, 5 Mo c'est bien assez
$ newfs_msdos ./pkgs.img
newfs_msdos: warning, ./pkgs.img is not a character device
./pkgs.img: 10192 sectors in 1274 FAT12 clusters (4096 bytes/cluster)
BytesPerSec=512 SecPerClust=8 ResSectors=1 FATs=2 RootDirEnts=512 Sectors=10240 Media=0xf0 FATsecs=4 SecPerTrack=63 Heads=255 HiddenSecs=0
$ doas mdconfig ./pkgs.img 
md0
$ doas mount -t msdosfs /dev/md0 /media

Depuis https://downloads.openwrt.org/releases/23.05.2/packages/x86_64/base/ je récupère les paquets:

hostapd-common_2023-09-08-e5ccbfc6-6_x86_64.ipk
iw_4.14-1_x86_64.ipk
iwinfo_2023-07-01-ca79f641-1_x86_64.ipk
iwlwifi-firmware-iwl7265d_20230804-1_x86_64.ipk
libnl-tiny1_2023-07-27-bc92a280-1_x86_64.ipk
ucode-mod-nl80211_2023-11-07-a6e75e02-1_x86_64.ipk
ucode-mod-rtnl_2023-11-07-a6e75e02-1_x86_64.ipk
ucode-mod-uloop_2023-11-07-a6e75e02-1_x86_64.ipk
wireless-regdb_2024.01.23-1_all.ipk
wpa-cli_2023-09-08-e5ccbfc6-6_x86_64.ipk
wpa-supplicant-mini_2023-09-08-e5ccbfc6-6_x86_64.ipk

et depuis https://downloads.openwrt.org/releases/23.05.2/targets/x86/64/packages/ les modules:

kmod-cfg80211_5.15.137+6.1.24-3_x86_64.ipk
kmod-crypto-aead_5.15.137-1_x86_64.ipk
kmod-crypto-cbc_5.15.137-1_x86_64.ipk
kmod-crypto-ccm_5.15.137-1_x86_64.ipk
kmod-crypto-cmac_5.15.137-1_x86_64.ipk
kmod-crypto-ctr_5.15.137-1_x86_64.ipk
kmod-crypto-gcm_5.15.137-1_x86_64.ipk
kmod-crypto-gf128_5.15.137-1_x86_64.ipk
kmod-crypto-ghash_5.15.137-1_x86_64.ipk
kmod-crypto-hmac_5.15.137-1_x86_64.ipk
kmod-crypto-manager_5.15.137-1_x86_64.ipk
kmod-crypto-null_5.15.137-1_x86_64.ipk
kmod-crypto-rng_5.15.137-1_x86_64.ipk
kmod-crypto-seqiv_5.15.137-1_x86_64.ipk
kmod-crypto-sha512_5.15.137-1_x86_64.ipk
kmod-iwlwifi_5.15.137+6.1.24-3_x86_64.ipk
kmod-mac80211-hwsim_5.15.137+6.1.24-3_x86_64.ipk
kmod-mac80211_5.15.137+6.1.24-3_x86_64.ipk

Je démarre la machine virtuelle avec ce nouveau disque:

$ doas umount /media
$ doas mdconfig -du md0
$ doas bhyve -H -P -m 192M -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 -s 3:0,virtio-blk,./openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img -s 4:0,virtio-blk,./pkgs.img -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd -l com1,stdio -W vm0

et j'installe les paquets:

root@OpenWrt:/# mount -t vfat /dev/vdb /mnt/
root@OpenWrt:/# opkg install /mnt/libnl-tiny1_2023-07-27-bc92a280-1_x86_64.ipk
Package libnl-tiny1 (2023-07-27-bc92a280-1) installed in root is up to date.
root@OpenWrt:/# ln /usr/lib/libnl-tiny.so.1 /usr/lib/libnl-tiny.so # sinon iw va râler

root@OpenWrt:/# opkg install /mnt/hostapd-common_2023-09-08-e5ccbfc6-6_x86_64.ipk /mnt/iw_4.14-1_x86_64.ipk /mnt/iwinfo_2023-07-01-ca79f641-1_x86_64.ipk /mnt/iwlwifi-firmware-iwl7265d_20230804-1_x86_64.ipk /mnt/ucode-mod-* /mnt/wireless-regdb_2024.01.23-1_all.ipk /mnt/wpa-cli_2023-09-08-e5ccbfc6-6_x86_64.ipk /mnt/wpa-supplicant-mini_2023-09-08-e5ccbfc6-6_x86_64.ipk
Installing hostapd-common (2023-09-08-e5ccbfc6-6) to root...
Installing iw (4.14-1) to root...
Installing iwinfo (2023-07-01-ca79f641-1) to root...
Installing iwlwifi-firmware-iwl7265d (20230804-1) to root...
Installing ucode-mod-nl80211 (2023-11-07-a6e75e02-1) to root...
Installing ucode-mod-rtnl (2023-11-07-a6e75e02-1) to root...
Installing ucode-mod-uloop (2023-11-07-a6e75e02-1) to root...
Installing wireless-regdb (2024.01.23-1) to root...
Installing wpa-cli (2023-09-08-e5ccbfc6-6) to root...
Installing wpa-supplicant-mini (2023-09-08-e5ccbfc6-6) to root...
Configuring iwinfo.
Configuring iw.
Configuring ucode-mod-uloop.
Configuring hostapd-common.
Configuring ucode-mod-nl80211.
Configuring ucode-mod-rtnl.
Configuring wpa-supplicant-mini.
Configuring iwlwifi-firmware-iwl7265d.
Configuring wpa-cli.
Configuring wireless-regdb.
root@OpenWrt:/# opkg install /mnt/kmod-*
...
root@OpenWrt:/# umount /mnt
root@OpenWrt:/# halt

Il est temps d'affecter le périphérique wifi à la machine virtuelle (adapter 2/0/0 au pciconf -lv et ne pas oublier l'option -S):

$ doas bhyve -H -P -S -m 192M -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 -s 3:0,virtio-blk,./openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img -s 7:0,passthru,2/0/0 -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd -l com1,stdio -W vm0

Configuration OpenWrt

Pas besoin du délai de grub:

root@OpenWrt:/# grep timeout /boot/grub/grub.cfg 
set timeout="0"

Quelques modules sont chargés au démarrage, je ne garde que le minimum (et encore, je pense que seul 30-fs-vfat est utile pour récupérer la configuration de grub):

root@OpenWrt:/# ls -1 /etc/modules-boot.d/
02-crypto-hash
04-crypto-crc32c
09-crypto-aead
09-crypto-manager
30-fs-vfat

Pareil pour les modules chargés après le démarrage (je doit pourvoir encore en supprimer quelques uns):

root@OpenWrt:/# ls -1 /etc/modules.d/
02-crypto-hash
04-crypto-crc32c
09-crypto-acompress
09-crypto-aead
09-crypto-cbc
09-crypto-ccm
09-crypto-cmac
09-crypto-ctr
09-crypto-gcm
09-crypto-gf128
09-crypto-ghash
09-crypto-hmac
09-crypto-manager
09-crypto-null
09-crypto-rng
09-crypto-seqiv
09-crypto-sha512
25-nls-cp437
25-nls-iso8859-1
25-nls-utf8
30-fs-vfat
iwlwifi
lib-crc-ccitt
lib-crc32c
lib-lzo
mac80211-hwsim

Je désactive les services inutiles:

root@OpenWrt:/# /etc/init.d/dnsmasq     stop && /etc/init.d/dnsmasq     disable
root@OpenWrt:/# /etc/init.d/dropbear    stop && /etc/init.d/dropbear    disable
root@OpenWrt:/# /etc/init.d/firewall    stop && /etc/init.d/firewall    disable
root@OpenWrt:/# /etc/init.d/gpio_switch stop && /etc/init.d/gpio_switch disable
root@OpenWrt:/# /etc/init.d/led         stop && /etc/init.d/led         disable
root@OpenWrt:/# /etc/init.d/sysntpd     stop && /etc/init.d/sysntpd     disable
root@OpenWrt:/# /etc/init.d/uhttpd      stop && /etc/init.d/uhttpd      disable

Je configure le wifi:

root@OpenWrt:/# cat /etc/config/wireless
...
config wifi-device 'radio2'
	option type 'mac80211'
	option path 'pci0000:00/0000:00:07.0'
	option channel '36'
	option band '5g'
	option htmode 'VHT80'
	option disabled '0'

config wifi-iface 'default_radio2'
	option device 'radio2'
	option network 'wan wan6'
	option mode 'sta'
	option ssid 'mon_super_ssid'
	option encryption 'psk2'
	option key 'ma_super_key'

et le réseau:

root@OpenWrt:/# cat /etc/config/network
config interface 'loopback'
	option device 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config interface 'lan'
	option device 'eth0'
	option proto 'static'
	option ipaddr '172.20.99.1'
	option netmask '255.255.255.0'
	option ip6addr '2db8:xxxx:xxxx:xx3:172:20:99:1/112'

config interface 'wan'
	option proto 'static'
	option ipaddr '172.20.62.99'
	option netmask '255.255.255.0'
	option gateway '172.20.62.1'
	list dns '172.30.63.20'
	list dns '172.30.63.10'

config interface 'wan6'
	option proto 'static'
	option ip6addr '2db8:xxxx:xxxx:xx2::99/64'
	option ip6gw '2db8:xxxx:xxxx:xx2::1'
	list dns '2db8:xxxx:xxxx:xx1:172:30:63:20'
	list dns '2db8:xxxx:xxxx:xx1:172:30:63:10'

Configuration FreeBSD

Le bridge:

$ cat /etc/rc.conf.d/bridge
autobridge_interfaces="bridge0"
autobridge_bridge0="tap0"

Le réseau:

$ cat /etc/rc.conf.d/network
cloned_interfaces="bridge0 tap0"

ifconfig_bridge0="inet 172.20.99.10 netmask 255.255.255.0"
ifconfig_bridge0_ipv6="inet6 auto_linklocal"
ifconfig_bridge0_alias0="inet6 2db8:xxxx:xxxx:xx3:172:20:99:10 prefixlen 112"
$ cat /etc/rc.conf.d/routing
defaultrouter="172.20.99.1"
ipv6_defaultrouter="2db8:xxxx:xxxx:xx3:172:20:99:1"

L'accès à la console de la machine virtuelle à l'aide de nmdm:

$ cat /etc/rc.conf.d/kld
kld_list="/boot/modules/amdgpu.ko nmdm"

Le démarrage de la machine virtuelle:

$ cat /etc/rc.local
/usr/sbin/bhyve -H -P -S -m 192M -s 0:0,hostbridge -s 1:0,lpc -s 3:0,virtio-blk,/chemin/complet/vers/openwrt-23.05.2-x86-64-generic-ext4-combined-efi.img -s 7:0,passthru,2/0/0 -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd -l com1,/dev/nmdm0A vm0 &

L'accès à la console de la machine virtuelle se fera à l'aide de cu:

$ doas cu -l /dev/nmdm0B # oui "B", man 4 nmdm

Mon réseau

La configuration réseau présentée est loin d'être la plus commune (pas de dhcp ni de nat, ipv6 sans autoconf). Pour une version plus "traditionnelle" (dhcp, pas d'ipv6), la machine virtuelle devra:

Ce qui devrait peu ou prou correspondre à (pas encore testé):

root@OpenWrt:/# cat /etc/config/network
....
config interface 'wan'
    option proto 'dhcp'

root@OpenWrt:/# /etc/init.d/firewall enable && /etc/init.d/firewall start

Au pire la documentation d'OpenWrt https://openwrt.org/docs/guide-user/network/start est plus que fournie.

Pour conclure

Le iperf est sans appel:

$ iperf3 -6 -c proxy
...
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec   226 MBytes   190 Mbits/sec   19             sender
[  5]   0.00-10.01  sec   226 MBytes   189 Mbits/sec                  receiver

$ iperf3 -6 -c proxy
...
[  5]   0.00-10.00  sec   263 MBytes   221 Mbits/sec    0             sender
[  5]   0.00-10.02  sec   263 MBytes   220 Mbits/sec                  receiver

C'est loin d'être parfait au vu de la configuration du réseau:

freebox delta [ vm proxy sous FreeBSD ] <-- rj45 --> ap wifi Mikrotik hAP ac^2 <- -> nuc

mais c'est toujours mieux que la dizaine de Mbits/sec (dont je me contentais très bien) de mon ancienne installation.

Le mini routeur qui trônait fièrement au dessus du nuc devrait finir au fin fond d'un tirroir. Mais comme il possède un lien série, j'en ferais bien un Frankenstein à base de bluetooth ...

Commentaires: https://github.com/bsdsx/blog_posts/issues/20

Correction 2024-03-17

J'ai oublié '-s 2:0,virtio-net,tap0' à chaque commande bhyve.


Lien vers ce billet

10/12/2023

[ stm32 openocd ]

202312100800 stm32 openocd

Faire clignoter une LED avec openocd

J'utilise openocd pour communiquer avec mes cartes stm32. N'utilisant ni tcl ni gdb (désolé pour les afficionados valencians j'ai ajouté ces lignes dans mon fichier de configuration:

gdb_port disabled
tcl_port disabled

Il ne me reste donc plus que le telnet en écoute sur le port 4444 pour dialoguer avec mes bestioles, ce que je fais à l'aide de nc:

$ echo 'reset halt' | nc -N 127.0.0.1 4444
$ echo "flash write_image erase ${PWD}/main.bin 0x8000000" | nc -N 127.0.0.1 4444
$ echo 'reset run' | nc -N 127.0.0.1 4444

Pris d'une inspiration aussi divine que soudaine j'ai dernièrement tenté un:

$ echo help | nc -N 127.0.0.1 4444

et quelle ne fût pas ma surprise devant cette avalanche de commandes ! Je suis loin d'en avoir fait le tour mais les suivantes ont attiré mon attention:

mdb ['phys'] address [count]
      display memory bytes
mdd ['phys'] address [count]
      display memory double-words
mdh ['phys'] address [count]
      display memory half-words
mdw ['phys'] address [count]
      display memory words
mwb ['phys'] address value [count]
      write memory byte
mwd ['phys'] address value [count]
      write memory double-word
mwh ['phys'] address value [count]
      write memory half-word
mww ['phys'] address value [count]
      write memory word
reset [run|halt|init]

Le manuel de référence indique, page 172, que la valeur initiale du Port configuration register high vaut 0x4444 4444. Du fait qu'elle soit facilement reconnaissable, c'est cette valeur que je vais tenter d'afficher. Autre information sur cette page, le décalage à appliquer depuis l'adresse de base du GPIO:

Address offset: 0x04

Page 51, on peut lire que l'adresse de base du GPIO Port C se trouve en 0x4001 1000, le registre de configuration se trouve donc en 0x4001 1004. Je vérifie:

$ echo 'reset halt' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> reset halt
target halted due to debug-request, current mode: Thread 
xPSR: 00000000 pc: 00000000 msp: 00000000

$ echo 'reset init' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> reset init
target halted due to debug-request, current mode: Thread 
xPSR: 00000000 pc: 00000000 msp: 00000000

$ echo 'mdw 0x40011004' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> mdw 0x40011004
0x40011004: 44444444

Comme on dit du côté de Valence: Rodriguez ! Autre valeur particulière, page 1087, le DBGMCU_IDCODE à l'adresse 0xE004 2000:

$ echo 'mdw 0xE0042000' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> mdw 0xE0042000
0xe0042000: 20036410

Il s'agit d'une révision 3 (2003) d'un "medium-density device" (0x410). La taille de la mémoire flash (page 1076) sur 16 bits (half-words):

$ echo 'mdh 0x1FFFF7E0' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> mdh 0x1FFFF7E0
0x1ffff7e0: 0080

Et bien sur l'identifiant de la carte, page (1077-1078), 2 x 16 bits + 2 * 32 bits:

$ echo 'mdh 0x1FFFF7E8 2' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> mdh 0x1FFFF7E8 2
0x1ffff7e8: 0048 0024

$ echo 'mdw 0x1FFFF7EC 2' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> mdw 0x1FFFF7EC 2
0x1ffff7ec: 31333911 41373931

La LED PC13

Pour contrôler la PIN 13 du GPIO C je dois d'abord activer ce dernier. Page 106, dans le "APB2 peripheral reset register", on peut voir que le GPIO C correspond au bit 4. Ce registre se situe à 0x18 de l'adresse de base de RCC (Reset and Clock Control) qui se trouve en 0x4002 1000 (page 50). Je commence par vérifier la valeur initiale puis j'active le GPIO:

$ echo 'mdw 0x40021018' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> mdw 0x40021018
0x40021018: 00000000

$ echo 'mww 0x40021018 0x00000010' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> mww 0x40021018 0x00000010

$ echo 'mdw 0x40021018' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> mdw 0x40021018
0x40021018: 00000010

Maintenant que le GPIO est activé, je peux passer la PIN 13 en sortie (Output) depuis le registre de configuration (0x4001 1004, vu plus haut). Pour se faire, les bits 21 et 20 (le "mode") devront avoir pour valeur respective 1 et 0:

$ echo 'mww 0x40011004 0x44644444' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> mww 0x40011004 0x44644444

A ce moment la LED devrait être allumée. Pour changer l'état de la PIN j'utilise les bits 13 et 29 du registre "Port bit set/reset register", page 173, dont le décalage vaut 0x10:

# bit 13 à 1, état haut, LED éteinte
$ echo 'mww 0x40011010 8192' | nc -N 127.0.0.1 4444

# bit 29 à 1, état bas, LED allumée
$ echo 'mww 0x40011010 536870912' | nc -N 127.0.0.1 4444

Pour conclure

Manipuler les bits et les adresses d'un micro-contrôleur reste pour moi une source d'émerveillement dont je doute me lasser un jour. Il existe un lien naturel entre ce monde et celui de l'informatique "générale" qu'illustre parfaitement cette (célèbre ?) sentence:

Fonctions, variables ? Mensonges ! Tout n'est qu'adresse -- iMil

Il ne me reste plus qu'à lire tranquillement la documentation officielle, la partie concernant Tcl me faisant de l'oeil ...

Commentaires: https://github.com/bsdsx/blog_posts/issues/19


Lien vers ce billet

10/06/2023

[ ssh ]

202306100800 ssh

ssh possède une option assez pratique pour qui veut apporter des modifications à sa configuration sans tout casser:

 -G      Causes ssh to print its configuration after evaluating Host and
         Match blocks and exit.

Pour avoir une idée de ce qu'il est possible de configurer:

$ ssh -F /dev/null -G localhost | wc -l
81

On va donc plutôt filtrer la sortie:

$ ssh -F /dev/null -G localhost | grep -e port -e localhost
host localhost
hostname localhost
port 22
gatewayports no
nohostauthenticationforlocalhost no

AddressFamily

Cette option permet de définir l'usage d'ipv4, d'ipv6 ou des deux. Comme chez moi, le ssh ne fonctionne qu'en ipv6, je peux ajouter dans mon ~/.ssh/config:

AddressFamily inet6

Sauf que:

Je commence par identifier le problème:

$ echo 'AddressFamily inet6' > /tmp/ssh_config
$ ssh -F /tmp/ssh_config github.com
ssh: Could not resolve hostname github.com: Address family for hostname not supported
$ ssh -F /tmp/ssh_config github.com 192.168.0.1
ssh: Could not resolve hostname 192.168.0.1: Name does not resolve

Je distingue ces citoyens de seconde zone avec une directive Match:

$ cat /tmp/ssh_config 
Match host github.com,10.*,192.168.*
AddressFamily inet

Match all
AddressFamily inet6

et je teste:

$ ssh -F /tmp/ssh_config -G github.com | grep address
addressfamily inet
$ ssh -F /tmp/ssh_config -G gitlab.com | grep address
addressfamily inet6
$ ssh -F /tmp/ssh_config -G 192.168.0.1 | grep address
addressfamily inet

En cas de liste, attention à ne pas utiliser d'espace, uniquement la virgule. Pour le réseau 172.16.0.0/12, c'est un peu plus long:

$ grep 172 /tmp/ssh_config
$ Match host github.com,10.*,192.168.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.*

Pour mon usage d'ipv4, je ne souhaite pas polluer mon .ssh/known_hosts:

$ cat /tmp/ssh_config
Match host github.com,192.168.*,10.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.*
AddressFamily inet

Match host 1*
UserKnownHostsFile none

Match all
AddressFamily inet6

IdentityFile

J'ai une clef pour chaque serveur publique sur lequel je me connecte et j'ai la bonne idée de la nommer en fonction du serveur:

$ cat /tmp/ssh_config
Match host github.com,10.*,192.168.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.*
AddressFamily inet

Match host 1*
UserKnownHostsFile none

Match all
AddressFamily inet6

Match host github.com,gitlab.com
IdentityFile ~/.ssh/%h

Je teste:

$ ssh -F /tmp/ssh_config -G github.com | grep -e address -e identityfile
addressfamily inet
identityfile ~/.ssh/%h
$ ssh -F /tmp/ssh_config -G gitlab.com | grep -e address -e identityfile
addressfamily inet6
identityfile ~/.ssh/%h
$ ssh -F /tmp/ssh_config -G localhost | grep -e address -e identityfile
addressfamily inet6
identityfile ~/.ssh/id_rsa
identityfile ~/.ssh/id_ecdsa
identityfile ~/.ssh/id_ecdsa_sk
identityfile ~/.ssh/id_ed25519
identityfile ~/.ssh/id_ed25519_sk
identityfile ~/.ssh/id_xmss
identityfile ~/.ssh/id_dsa

Parce que l'ordre compte

J'utilise toujours une clef sauf pour le tld .ru:

Match host github.com,10.*,192.168.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.*
AddressFamily inet

Match host 1*
UserKnownHostsFile none

Match all
AddressFamily inet6
PreferredAuthentications publickey

Match host github.com,gitlab.com
IdentityFile ~/.ssh/%h

Match host *.ru
PreferredAuthentications password

Pas marche:

$ ssh -F /tmp/ssh_config -G fail.ru | grep -e preferredauthentications
preferredauthentications publickey

On ne peut pas redéfinir une directive, comme indiqué en début de page de manuel:

 Unless noted otherwise, for each parameter, the first obtained value will
 be used.  

Pour mon cas d'usage je peux:

Include et alias

La directive suivante définit un raccourci vers github.com

Host gh
Hostname github.com

que l'on peut placer dans un fichier dédié. Une directive Include :

$ cat /tmp/ssh_config_alias
Host gh
Hostname github.com

$ cat /tmp/ssh_config
Include /tmp/ssh_config_alias # Chemin complet car le fichier de config n'est pas un de ceux par défaut

Match host github.com,10.*,192.168.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.*
AddressFamily inet

Match host 1*
UserKnownHostsFile none

Match all
AddressFamily inet6

Match host github.com,gitlab.com
IdentityFile ~/.ssh/%h

Match host *.ru
PreferredAuthentications password

Match all
PreferredAuthentications publickey

et le tour est joué:

$ ssh -F /tmp/ssh_config -G gh | grep -e address -e identityfile
addressfamily inet
identityfile ~/.ssh/%h

J'ai quelques machines virtuelles qui sont dans un sous-domaine dédié:

Host nbsd2?,al3?,fbsd4?
Hostname %n.xen

Mais ce n'est pas la bonne solution: le token %n n'est pas disponible dans une directive Hostname. Je vais utiliser les directives Canonicalize :

Match host nbsd2? al3? fbsd4?
CanonicalizeHostname yes
CanonicalDomains xen.bsdsx.fr # oui, le domaine complet

Si la configuration DNS est correcte alors:

$ ssh -F /tmp/ssh_config -G al30 | grep -e hostname
hostname al30.ni3.bsdsx.fr
canonicalizehostname true

Control

Quand on doit avoir plusieurs connections vers une machine, on comprend vite l'utilité des directives Control et ça ne coûte pas plus cher de l'activer par défaut:

$ cat /tmp/ssh_config_alias
ControlMaster auto
ControlPath ~/.ssh/.%r@%h:%p
...

Match vs Host

J'ai toujours utilisé la directive Host, c'est pour ce billet que je suis passé à Match. Ces deux directives ne diffèrent que par leur syntaxe:

Host *.ru *.ch
Match host *.ru,*.ch

mais seul Match permet ce genre de chose:

Match host *.prod.mon.domaine user backup localuser !production exec "id -Gn %u | grep -q admin"
PreferredAuthentications publickey
IdentitiesOnly yes
IdentityFile ~/.ssh/id_rsa_backup_with_force_command

Si:

alors on utilise une clef spécifique. Oui cet exemple est plutôt capillotracté (mais aucun drosophile n'a été ... lors de la rédaction de ce billet).

Un dernier pour la route

C'est plus par principe qu'autre chose mais si il y a bien un truc que je ne supporte pas c'est tout ce qui ne sert à rien (oui, je ne supporte pas grand chose :) . Quand je vois tous les fichiers testés par

$ ssh -v ...

je me dis que ces quelques lignes ne peuvent pas faire de mal:

GlobalKnownHostsFile /dev/null # je n'ai jamais créé les fichiers de cette directive
UserKnownHostsFile ~/.ssh/known_hosts # known_hosts2 n'existe pas
# Pour ne pas tester des clefs qui n'existent pas
IdentityFile ~/.ssh/id_rsa
IdentityFile ~/.ssh/id_ecdsa_sk
AddKeysToAgent yes # ajouter les clefs à l'agent lors de leur première utilisation

Enjoy !

Commentaires: https://github.com/bsdsx/blog_posts/issues/18


Lien vers ce billet

04/03/2023

[ bit perl ]

202303040800 bit perl

Les bits

J'ai toujours eu un peu de mal à avoir la représentation binaire d'une valeur, qu'elle soit au format décimal ou hexadécimal (là j'ai vraiment beaucoup de mal). Mais jouer avec des microcontrôleurs impose tôt ou tard de se retrouver avec ce genre de chose:

RCC->APB1ENR |= 0x200000;

APB1ENR est une valeur sur 32 bits décomposés en bit (parfois en groupe de bit) et ici on active un de ces bits. Mais lequel ? Pour me simplifier la vie, je me suis donc fendu d'un script (Perl, what else ?) sans prétention qui m'affiche, pour chaque argument, sa valeur binaire:

$ perl bits.pl 0x200000
  31  30  29  28  27  26  25  24  23  22  21  20  19  18  17  16  15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
--------------------------------------------------------------------------------------------------------------------------------
   0   0   0   0   0   0   0   0   0   0   1   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  0x200000 (2097152)

En consultant la documentation, je comprend que le bit 21 du registre APB1ENR correspond (pour une blue pill) à l'activation de I2C1. Parce que certains registres sont sur 16 bits, je peux aussi réduire le nombre de bit à afficher:

$ perl bits.pl -16 0x800
  15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
----------------------------------------------------------------
   0   0   0   0   1   0   0   0   0   0   0   0   0   0   0   0  0x800 (2048)

ou modifier la largeur de chaque colonne (pas le plus simple à lire, j'en conviens):

$ perl bits.pl -8 -w 1 5 7 9
76543210
--------
00000101  5 (5)
00000111  7 (7)
00001001  9 (9)

Je peux aussi utiliser des valeurs binaires directement à des fins de comparaison:

$ perl bits.pl -8 0b00000111 0b10101010 5 0xa
   7   6   5   4   3   2   1   0
--------------------------------
   0   0   0   0   0   1   1   1  0b00000111 (7)
   1   0   1   0   1   0   1   0  0b10101010 (170)
   0   0   0   0   0   1   0   1  5 (5)
   0   0   0   0   1   0   1   0  0xa (10)

Les opérateurs et fonctions Perl

Avec l'opérateur .. je peux facilement obtenir la liste des nombres de 0 à 15:

$ perl -le 'print 0 .. 15'
0123456789101112131415

Mais pas ceux de 15 à 0:

$ perl -le 'print 15 .. 0'
<rien, nada, que dalle>

Pour afficher mes nombres dans l'ordre décroissant, il me suffit d'utiliser reverse:

$ perl -le 'print reverse 0 .. 15'
1514131211109876543210

Pour afficher une valeur sur 3 caractères j'utilise %3s. Pour formater size bits sur une largeur de width il me suffit d'utiliser l'opérateur x:

$fmt = "%${width}s" x $size-- . '%s'; // on n'oublie pas le '%s' pour le retour à la ligne

Parce que j'utilise reverse, je dois commencer par le retour chariot:

printf $fmt, reverse "\n", 0 .. $size;

Pour connaitre la valeur d'un bit à la position $pos d'une valeur $value, je dois déplacer la valeur 1 de $pos positions:

1 << $pos

faire un et avec la valeur:

$value & (1 << $pos)

et redécaler ce résultat de pos positions (les parenthèses sont nécessaires du fait de la priorité des opérateurs):

($value & (1 << $pos)) >> $pos

Pour obtenir la valeur des $size premiers bits de $register:

map { ($register & (1 << $_)) >> $_ } 0 .. $size;

map va appliquer le block ({ .. }) pour chaque valeur ($_) de la list 0 .. $size

Le script

Comme d'habitude, pour quelques lignes de code, on se trouve avec une gestion des options, de l'aide, de la vérification d'arguments ... et paf, 50 lignes:

$ cat -n bits.pl
 1  #!/usr/bin/perl
 2
 3  use strict;
 4  use warnings FATAL => 'all';
 5  use utf8;
 6
 7  use Getopt::Long;
 8
 9  sub usage {
10      print <<EOT
11  Usage: $0 [-8|-16|-32] [--width=width] [-h] number ...
12
13  Example:
14      $0 42 0xcafe 0b101
15      $0 -8 -w 1 5
16  EOT
17  ;
18      exit;
19  }
20
21  my ($size, $width, $help) = (32, 4);
22  GetOptions(
23       '8' => sub { $size =  8 },
24      '16' => sub { $size = 16 },
25      '32' => sub { $size = 32 },
26
27      'width=i' => \$width,
28      'help'    => \$help,
29  ) or usage();
30
31  if ($help || !scalar @ARGV) { usage(); }
32   
33  my $fmt;
34  sub dump_register {
35      my $register = $_[0] =~ /^0[bx]/i ? oct $_[0] : $_[0];
36      printf $fmt, reverse "  $_[0] ($register)\n", map { ($register & (1 << $_)) >> $_ } 0 .. $size;
37  }
38
39  if ($width < 2 && $size > 8) { $width = 3; } 
40  $fmt = "%${width}s" x $size-- . '%s';
41
42  printf $fmt, reverse "\n", 0 .. $size;
43  printf $fmt, (('-' x $width)) x ($size + 1) , "\n";
44
45  foreach (@ARGV) { dump_register($_); }

Pour finir

Quand je tombe sur ce genre de chose:

GPIOA->CRL &= ~(0xf << (2 * 4));
GPIOA->CRL |=   0xA << (2 * 4);

Il me suffit de rajouter en fin de script:

my $register = $ARGV[0] =~ /^0[bx]/i ? oct $ARGV[0] : $ARGV[0];
$register &= ~(0xf << (2 * 4));
dump_register($register);
$register |=   0xA << (2 * 4);
dump_register($register);

et de le lancer avec 0xffffffff (pour que tous les bits soient à 1):

$ perl bits.pl 0xffffffff
  31  30  29  28  27  26  25  24  23  22  21  20  19  18  17  16  15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
--------------------------------------------------------------------------------------------------------------------------------
   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1  0xffffffff (4294967295)
   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   0   0   0   0   1   1   1   1   1   1   1   1  4294963455 (4294963455)
   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   0   1   0   1   1   1   1   1   1   1   1  4294966015 (4294966015)

La première instruction initialise à zéro le 3ème groupe de 4 bits, la seconde active les bits 1 (9) et 3 (11) de ce 3ème groupe. Et tout est plus clair !

Commentaires: https://github.com/bsdsx/blog_posts/issues/17


Lien vers ce billet