2023年11月5日 星期日

Python 開發筆記 - L2 Multicast / L3 Multicast 與 IGMP Snooping 測試

幫公司測試了一下 Professional Audiovisual (ProAV) 產品在不同 switch 上的情況,剛好有分別走 L2 Multicast 跟 L3 Multicast 溝通模式,而這些設備會使用 IGMP 通報 switch ,盡可能讓 Multicast 封包不要影響不必要的網路設備,避免俗稱的流量洪水事件。

由於過去經驗都是比較上層應用,這次就多多請教 chatGPT 惡補一下知識,其中 IGMP 的知識倒是被 Cloudflare 的教學文給救贖了一下:


IGMP 是用於 IPv4 的多點傳送通訊協定,是網際網路通訊協定的第四版。IPv6 依賴於多點傳送接聽程式探索 (MLD) 進行多點傳送。IPv6 網路使用 MLD 窺探而不是 IGMP 窺探。

就這句話,我反而在想 L2 Multicast 缺少 L3 (IP層) 的封包資訊,是不是讓 switch 做不到 IGMP snooping 功能?甚至舊款 switch 直接判定是未知 multicast 而進行永久性 broadcast 容錯設計?實測的結果是不同的 switch 行為是不一樣的。

整體上只需要簡單的 python 小程式,透過 scapy 套件,可非常輕鬆拼湊出想製作的 raw socket 來進行測試:

RX app: 

from scapy.all import *
from scapy.contrib.igmp import IGMP

multicastMac = "01:00:5e:00:02:01"
multicastGroupIP = '224.0.2.1'
nicName = '乙太網路'

# IGMPv2 Membership Report
igmpReportPacket = Ether(dst=multicastMac)/IP(dst=multicastGroupIP)/IGMP(type=0x16, mrcode=0, gaddr=multicastGroupIP)
sendp(igmpReportPacket, iface=nicName)

while True:
    multicastReceivePacket = sniff(iface=nicName, filter=f"ether dst {multicastMac}", count=1)
    if multicastReceivePacket:
        print("Received: ", multicastReceivePacket[0][Raw].load.decode("utf-8"))
        #print("Received: ", multicastReceivePacket[0][Raw].load)

TX app:

from scapy.all import *
from scapy.contrib.igmp import IGMP

multicastMac = "01:00:5e:00:02:01"
multicastGroupIP = '224.0.2.1'
nicName = '乙太網路2'

# IGMP Membership Report
igmpReportPacket = Ether(dst=multicastMac)/IP(dst=multicastGroupIP)/IGMP(type=0x16, mrcode=0, gaddr=multicastGroupIP)
sendp(igmpReportPacket, iface=nicName)

i = 0
while True:
    # Multicast
    if i % 3 == 0: 
        multicastTestMessagePacket = Ether(dst=multicastMac, type=0xffff)/"hello"
    elif i % 3 == 1:
        multicastTestMessagePacket = Ether(dst=multicastMac)/IP(dst=multicastGroupIP)/UDP(sport=12345, dport=12345)/"world"
    else:
        multicastTestMessagePacket = Ether(dst=multicastMac)/IP(src="192.168.1.2", dst=multicastGroupIP)/UDP(sport=12345, dport=12345)/"hello world"

    sendp(multicastTestMessagePacket, iface=nicName)

    # IGMPv2 Report
    if i % 600:
        sendp(igmpReportPacket, iface=nicName)
    i += 1

    time.sleep(1/10)

如此,弄一台筆電,讓他上頭有兩張 RJ45 網卡(可靠USB擴充),分別插上待側的 switch ,一邊當 RX 收訊息,另一邊當 TX 送訊息,接著調整 switch 設定後,觀察封包傳遞的狀況。可以的話,其實筆電可以設法弄個 3個網孔?接著另一網孔可以插在 switch 上,看看有沒有流量洪水(traffic floods)

收工

2023年10月12日 星期四

SSD 外接硬碟盒 與 USB3.2 Gen 1 / USB3.2 Gen 2 / USB3.2 Gen 2x2 / USB 4 規格 @ 以 2023 MacBook Air 15吋 M2 使用為例

最近準備升級工作環境,正在評估外接硬碟方案,主因是買的 Type-C 隨身碟很容易掛 :P 先對照著 2023 Macbook Air 15吋 M2 的規格:

兩個 Thunderbolt / USB 4 埠,可支援:

充電
DisplayPort
Thunderbolt 3 (速度最高可達 40Gb/s)
USB 4 (速度最高可達 40Gb/s)
USB 3.1 Gen 2 (速度最高可達 10Gb/s)


這這邊提到 USB4 和 USB 3.1 Gen 2,跟 chatGPT 請教一下,原來 USB3.2 的各代基本上也是用來簡述以前 USB 3.1, USB 3.0 等用法,直接查看 USB 3 WIKI - zh.wikipedia.org/zh-tw/USB_3.0
  • USB 3.2 Gen 1x1 == USB 3.1 Gen 1 == USB 3.0
  • USB 3.2 Gen 1x2
  • USB 3.2 Gen 2x1 == USB 3.2 Gen 2
  • USB 3.2 Gen 2x2
然後頻寬就 USB 3.2 Gen 1 = 5 Gbit/s, USB 3.2 Gen 2 = 10 Gbit/s,因此上述依序就是 5 Gbit/s, 10 Gbit/s, 10 Gbit/s, 20 Gbit/s ,這樣就好記許多。而上述通常會簡化成以下三款:
  • USB 3.2 Gen 1 = 5 Gbit/s 傳輸效率 = 理論上約略每秒 500MB 傳輸效率
  • USB 3.2 Gen 2 = 10 Gbit/s 傳輸效率 = 理論上約略每秒 1000MB 傳輸效率
  • USB 3.2 Gen 2x2 = 20 Gbit/s 傳輸效率 = 理論上約略每秒 2000MB 傳輸效率
而 MacBook Air 15吋 M2 版的規格描述就是 USB4 (40Gb/s) or USB 3.1 Gen 2 (10Gb/s)。若不需衝極值,就買支援 USB 3.1 Gen 2 版的外接硬碟即可。此刻也可以採買到 USB 4 的外接SSD盒,通常會詳加描述自己的散熱系統。

此外,在 SSD 規格上,常見 M.2 PCI Express 2280, M.2 PCI Express 2260, M.2 PCI Express 2242, 後面那串數字 2280, 2260, 2242 代表寬x長等於 22x80, 22x60, 22x42,而 PCI Express 也會看的 PCIe 3.0 和 PCIe 4.0 ,其中 PCIe 3.0 每個通道傳輸效率 8 Gbit/s 而 PCIe 4.0 每個通道傳輸效率 16 Gbit/s,例如一款產品名為 M.2 2280 PCIe Gen3x4 ,代表該 SSD 插槽規格為 M.2 規格,寬長 22x80 ,傳輸速度是 Gen3x4 = 8 Gbit/s x4 = 32 Gbit/s = 理論上傳輸效率約 4 GB/s

而上面的傳輸效率都是 "理論上" ,實際上傳輸速度還會跟 SSD 內部控制器晶片、NAND閃存類型以及操作的檔案大小和數量等有關。

關於 SSD 保固上,常見 5年保固 或 600 TBW 有限保固,後者代表寫入達 600 TB 的資料量概念,通常 SSD 空間越大時,保固的 XXX TBW 數字會越大,例如 1TB SSD 會有 600 TBW ,而 2TB 就會常看到 1000 TBW 等有限保固機制。

最後,回到最初的想法,會思考 SSD 外接盒起源於一顆 512GB USB3.2Gen1 隨身碟經常性備份資料或埋了很多零碎檔案後,容易壞掉 :P 

例如插上 Macbook 無法被偵測,甚至無法被修復,後來交叉用 Windows 11 修復,回來在 macOS 又可以辨識到...但這樣的日子太不安穩了,因此評估一下是否在掏錢買個 SSD + 外接盒方案,如此 SSD 可抽換+保固服務長



2023年10月10日 星期二

Python 開發筆記 - Ping 與 WakeOnLan (WOL) 的應用

這假期恰逢公司機房斷電,復電後在想該怎樣把重要的機器喚起。由於 VPN 等相關機器群健康運作(如套裝 NAS 機器),因此得以遠端回去做事。

下一刻就是回顧有哪些機器要遠端啟動,這時就多虧了強者同事有良好的筆記習慣,平時有撰寫 WIKI 管控機器群(包括 MacAddress),就這樣,可在指定機器上不斷發動 WOL 來喚醒。例如有些 router 本身就提供網頁介面幫忙,可以直接靠他們。若有 Ubuntu 機器,就來個 wakeonlan 工具:

$ dpkg -l | grep wakeonlan
ii  wakeonlan                              0.41-10                                 Sends 'magic packets' to wake-on-LAN enabled ethernet adapters

在這邊 ubuntu.pkgs.org/20.04/ubuntu-main-arm64/wakeonlan_0.41-12_all.deb.html 也能查看到,甚至瀏覽一下工具原始碼 github.com/jpoliv/wakeonlan ,而 WOL 的原理可以查看 WIKI 就習得實作技能 en.wikipedia.org/wiki/Wake-on-LAN 或詢問 chatGPT 也會很佛心。

假期期間,就弄個 python 工具 ping-before-wakeonlan ,目的是為了記錄此次喚起了幾台機器,可以限縮每次喚起的機器數量,避免一堆機器一起起來而造成電力需求過大而跳電,另外再加入把清單亂數排一下,避免前面清單的機器都無法喚起,又讓清單後面的機器沒機會被叫起來:


使用方式,主要為 `pip install ping-before-wakeonlan` 即可,以下是 venv 用法:

% python3 -m venv venv
% source venv/bin/activate
% pip install ping-before-wakeonlan
% ./venv/bin/ping-before-wakeonlan
% cat /tmp/device.json
[
   {
       "ip": "192.168.1.1",
       "mac_address": "00:00:00:00:00:01"
   },
   {
       "ip": "192.168.1.2",
       "mac_address": "00:00:00:00:00:02"
   },
   {
       "ip": "192.168.1.3",
       "mac_address": "00:00:00:00:00:03"
   }, ...
]

% ping-before-wakeonlan --device-info /tmp/device.json
Process: 1 / 7: Device: {'ip': '192.168.1.3', 'mac_address': '00:00:00:00:00:03'}
Process: 2 / 7: Device: {'ip': '192.168.1.2', 'mac_address': '00:00:00:00:00:02'}
Process: 3 / 7: Device: {'ip': '192.168.1.1', 'mac_address': '00:00:00:00:00:01'}
Process: 4 / 7: Device: {'ip': '192.168.1.4', 'mac_address': '00:00:00:00:00:04'}
Process: 5 / 7: Device: {'ip': '192.168.1.5', 'mac_address': '00:00:00:00:00:05'}
{
    "count": 5,
    "device": {
        "failed": [],
        "handled": [
            {
                "ip": "192.168.1.3",
                "mac_address": "00:00:00:00:00:03"
            },
            {
                "ip": "192.168.1.2",
                "mac_address": "00:00:00:00:00:02"
            },...
        ],
        "input": [
            {
                "ip": "192.168.1.3",
                "mac_address": "00:00:00:00:00:03"
            }, ...
        ],
        "online": [],
        "skip": [
            {
                "ip": "192.168.1.6",
                "mac_address": "00:00:00:00:00:06"
            }, ...
        ]
    },
    "info": [
        "..."
    ],
    "maxCount": 5,
    "ping": "ping -c 1 -W 3",
    "status": true,
    "version": "1.0.0"
}

2023年10月4日 星期三

Python 開發筆記 - 使用 Gitlab API 列出 Projects、Branches 和 Commits

剛好碰到需要製作 Gitlab 整合類服務,開發完後就把很基礎的項目整理成小工具來支援 JSON 輸出,未來可以跟 jq 做一堆連續技:

用法:

% virtualenv venv
created virtual environment CPython3.11.5.final.0-64 in 120ms
...

% source venv/bin/activate
(venv) % 
(venv) % pip3 install gitlab-api-helper
(venv) % ./venv/bin/gitlab-api-helper 
{
    "info": [
        "config file not found: .env",
        "--api empty"
    ],
    "result": null,
    "version": "1.0.0"
}
usage: gitlab-api-helper [-h] [--apiSetupConfig APISETUPCONFIG]
                         [--apiAccessToken APIACCESSTOKEN] [--api API]
                         [--sinceType {day,week,month}]
                         [--sinceNumber SINCENUMBER]
                         [--lookup {project,branch,commit}]
                         [--lookupProjectID LOOKUPPROJECTID]
                         [--lookupBranch LOOKUPBRANCH]

A Simple Tool for Gitlab API Usage

options:
  -h, --help            show this help message and exit
  --apiSetupConfig APISETUPCONFIG
                        Read Default Info from config file
  --apiAccessToken APIACCESSTOKEN
                        Using Gitlab API with private_token.
  --api API             Gitlab API URL
  --sinceType {day,week,month}
                        commit date range type
  --sinceNumber SINCENUMBER
                        commit date range value
  --lookup {project,branch,commit}
                        query result
  --lookupProjectID LOOKUPPROJECTID
                        Gitlab Project ID
  --lookupBranch LOOKUPBRANCH
                        Branch Name

剩下就看 github.com/changyy/gitlab-api-helper 或 pypi.org/project/gitlab-api-helper/ 的簡介囉

2023年10月3日 星期二

PHP 開發筆記 - 用 Matomo Analytics HTTP API 取代 GA4 Measurement Protocol 服務 @ Ubuntu 22.04


由於 Google Analytics 4 在後端端數據搜集情境已殘,可能起源於 GA4 著重隱私等設計(?),現況數據收集已強調必須先從 Web or App 開始,透過 JS SDK 或 APP SDK 做事,而後端回報為輔助而已,在 Web & App 端可以做隱私宣告。如此,使得純後端回報幾乎無法妥善使用 GA4 的功能,包括無法區分 new user / old user 屬性等等,也不像 GA3 可以把 remote client 的 IP 回報出去而顯示使用者的世界全貌。

架設 Matomo 方式還滿簡單的,就 Web Server + PHP + MySQL ,並且官方也有 docker 安置方式:

由於看到別人的文章提到要記得設置定期分析任務,不然久久登入網頁會很卡的,推論也是從網頁端發送查詢資料,過程中也一同處理資料分析。這個用瀏覽器時,從開發者工具中也可以看到定期在發送 ajax 的 requests。


目前就先弄個 Matomo project 出來,根據上頭的指示很簡單就埋好 js script code 去追蹤網站流量,可以看得到 Matomo 比 GA 提供更多細膩的資訊,這就像隱私的那些規劃,在 GA 上頭只能很粗略地觀看到“趨勢”,而在 Matomo 上頭,則是可以細到把指定的 client 展開出他的 Profile:


我認為跟 GA 相比有滿明顯的設計差距。

接著,關於透過 REST API 回報數據與查詢回報的方式,可以參考:

回報 PageView/Event:

% cat test.php
require 'report.php';

print_r(matomoPageview(
[
[
'url' => 'http://localhost/page1',
'action_name' => 'PageView 01 - Title',
'uid' => '123456789012',
//'cip' => getUserIP(),
],
[
'url' => 'http://localhost/page2',
'action_name' => 'PageView 02 - Title',
'uid' => '123456789012',
//'cip' => getUserIP(),
],
[
'url' => 'http://localhost/page3',
'action_name' => 'PageView 03 - Title',
'uid' => '123456789012',
//'cip' => getUserIP(),
'e_c' => 'Service',
'e_a' => 'Auth',
'e_n' => 'Login',
],

]
,'idSite'
,'MatomoAPI'
));

% php test.php
Array
(
    [status] => 1
    [error] => 
    [info] => Array
        (
            [0] => Array
                (
                    [apiResult] => {"status":"success","tracked":1,"invalid":0}
                )

        )

)

得到 Report:

% cat test.php
require 'report.php';

print_r(matomoQueryReport(
        [   
                'idSite' => 1,
                'period' => 'day',
                'date' => '2023-10-01,2023-10-03',
                'method' => 'VisitsSummary.get',
        ]   
        , 'https://your-matomo.example.com/'
        , 'access_token'
));

% php test.php
Array
(
    [status] => 1
    [data] => Array
        (
            [0] => Array
                (
                    [2023-10-01] => Array
                        (
                        )

                    [2023-10-02] => Array
                        (
                        )

                    [2023-10-03] => Array
                        (
                            [nb_uniq_visitors] => 1
                            [nb_users] => 1
                            [nb_visits] => 1
                            [nb_actions] => 2
                            [nb_visits_converted] => 0
                            [bounce_count] => 0
                            [sum_visit_length] => 1
                            [max_actions] => 2
                            [bounce_rate] => 0%
                            [nb_actions_per_visit] => 2
                            [avg_time_on_site] => 1
                        )

                )

        )

    [error] => 
    [info] => Array
        (
        )

)