星期日, 6月 28, 2020

Linux over Windows

「Linux」通常是指兩部份:Linux Kernel 和 GNU userspace。

Windows Subsystem for Linux

  • WSL 1
  • WSL 2

Virtual Box

coLinux

星期三, 6月 24, 2020

Berkeley Packet Filter and Linux Socket Filtering

隨著電腦網路連線發展,需要分析封包傳了什麼,一開始百家爭鳴,直到 Berkeley Packet Filter (BPF) 造成轟動而變成 de-facto 標準。

1992 《The BSD Packet Filter: A New Architecture for User-level Packet Capture》發表,用簡單的虛擬機器改進封包在 kernel 過濾效率。之後一直擴展移植到其它平台和作業系統,成為 Berkeley Packet Filter (BPF)。(1992 好早喔,我那時候左右才知道 TANET,也才在師或長帶領下小用一兩次)

1997 透過 socket 引入 Linux kernel v2.1.75,稱為 Linux Socket Filter (LSF),但還是常稱為 BPF,大部分實作放在 net/core/filter.c

沈寂一段時間後,BPF 持續改善效能,也應用在更多的地方。

2011 Linux v3.0 開始,進一步使用 BPF just-in-time (JIT) 編譯器將 BPF 程式碼轉成原生機器碼的來加速執行。Linux v3.4 用在 SECCOMP,Linux v3.14 新增除錯工具 bpf_asm()、bpf_dbg()。

隨著處理器的演進,BPF 虛擬機器離可運用的原生機器碼越來越遠,對於 BPF 的應用也有更開闊的想法。

2014 Linux v3.15 開始稱為 extended BPF (eBPF) 的全新設計,傳統 BPF 保留為 classical BPF (cBPF)。

在 v3.17 加到 kernel/bpf 下。

Linux 核心封包過濾機制 Linux Socket Filtering (LSF) 源自 Berkeley Packet Filter (BPF),讓 userspace 程式透過 socket SO_ATTACH_FILTER 選項附上過濾碼到任何 socket,給 kernel 過濾封包。此外,也用在 netfilter 的 xt_bpf、kernel qdisc layer 的 cls_bpf、SECCOMP-BPF、team driver、PTP code 等。

高階過濾指令如 `tcpdump -i em1 port 22`,加上參數 -ddd 可用 libpcap內部編譯器產生 SO_ATTACH_FILTER 用的過濾碼。但如果用到 BPF Linux 擴充,或者較為複雜或需要最佳化等,也可以用低階的 BPF 組合語言撰寫,用在 kernel tools/bpf/ 的工具 bpf_asm 編譯。BPF 組語 syntax 很接近原始的 BPF 論文。

BPF 引擎包括下列暫存器:
  • A:32-bit accumulator
  • X:32-bit X register
  • M[16]:16 x 32-bit 記憶體
在 #include <linux/filter.h> 可看到過濾指令的結構:
struct sock_filter { /* Filter block */
  __u16 code;   /* Actual filter code */
  __u8 jt;      /* Jump offset for true */
  __u8 jf;      /* Jump offset for false */
  __u32 k;      /* Generic multiuse field depends on code */
};
code定址模式說明
Load
ld1, 2, 3, 4, 10Load 32-bit into A
ldi4Load word into A
ldh1, 2Load half-word into A
ldb1, 2Load byte into A
ldx3, 4, 5, 10Load word into X
ldxi4Load word into X
ldxb5Load byte into X
Store
st3Store A into M[]
stx3Store X into M[]
Branch
jmp6Jump to label
ja6Jump to label
jeq7, 8Jump on A == k
jneq8Jump on A != k
jne8Jump on A != k
jlt8Jump on A < k
jle8Jump on A <= k
jgt7, 8Jump on A > k
jge7, 8Jump on A >= k
jset7, 8Jump on A & k
ALU
add0, 4A + <x>
sub0, 4A - <x>
mul0, 4A * <x>
div0, 4A / <x>
mod0, 4A % <x>
neg!A
and0, 4A & <x>
or0, 4A | <x>
xor0, 4A ^ <x>
lsh0, 4A << <x>
rsh0, 4A >> <x>
Miscellaneous
taxCopy A into X
txaCopy X into A
Return
ret4, 9Return

定址模式Syntax說明
0x/%xRegister X
1[k]BHW at byte offset k in the packet
2[x + k]BHW at the offset X + k in the packet
3M[k]Word at offset k in M[]
4#kLiteral value stored in k
54*([k]&0xf)Lower nibble * 4 at byte offset k in the packet
6LJump label L
7#k,Lt,LfJump to Lt if true, otherwise jump to Lf
8#k,LtJump to Lt if predicate is true
9a/%aAccumulator A
10extensionBPF extension
Linux 還有 BPF 擴充對載入到 A 指令 "overloading" the k argument with a negative offset + a particular extension offset.
  len                      skb->len
  proto                    skb->protocol
  type                     skb->pkt_type
  poff                     Payload start offset
  ifidx                    skb->dev->ifindex
  nla                      Netlink attribute of type X with offset A
  nlan                     Nested Netlink attribute of type X with offset A
  mark                     skb->mark
  queue                    skb->queue_mapping
  hatype                   skb->dev->type
  rxhash                   skb->hash
  cpu                      raw_smp_processor_id()
  vlan_tci                 skb_vlan_tag_get(skb)
  vlan_avail               skb_vlan_tag_present(skb)
  vlan_tpid                skb->vlan_proto
  rand                     prandom_u32()
BPF 組合語言範例:
ARP 封包: (檔案 foo)
      ldh [12]         /* Load half word offset 12 into A */
      jne #0x806, drop /* Jump to drop if != 0x0806 */
      ret #-1
drop: ret #0
經過 bpf_asm 轉換成 bytecode:
$ ./bpf_asm foo
4,40 0 0 12,21 0 1 2054,6 0 0 4294967295,6 0 0 0,
C 語言格式輸出方便複製貼上:
$ ./bpf_asm -c foo
{ 0x28,  0,  0, 0x0000000c },
{ 0x15,  0,  1, 0x00000806 },
{ 0x06,  0,  0, 0xffffffff },
{ 0x06,  0,  0, 0000000000 },

IPv4 TCP packets:
      ldh [12]
      jne #0x800, drop
      ldb [23]
      jneq #6, drop
      ret #-1
drop: ret #0
(Accelerated) VLAN w/ id 10:
ld vlan_tci
jneq #10, drop
ret #-1
drop: ret #0
icmp random packet sampling, 1 in 4
ldh [12]
jne #0x800, drop
ldb [23]
jneq #1, drop
# get a random uint32 number
ld rand
mod #4
jneq #1, drop
ret #-1
drop: ret #0
SECCOMP filter example:
ld [4] /* offsetof(struct seccomp_data, arch) */
jne #0xc000003e, bad /* AUDIT_ARCH_X86_64 */
ld [0] /* offsetof(struct seccomp_data, nr) */
jeq #15, good /* __NR_rt_sigreturn */
jeq #231, good /* __NR_exit_group */
jeq #60, good /* __NR_exit */
jeq #0, good /* __NR_read */
jeq #1, good /* __NR_write */
jeq #5, good /* __NR_fstat */
jeq #9, good /* __NR_mmap */
jeq #14, good /* __NR_rt_sigprocmas
k */ jeq #13, good /* __NR_rt_sigaction */
jeq #35, good /* __NR_nanosleep */
bad: ret #0 /* SECCOMP_RET_KILL_THREAD */
good: ret #0x7fff0000 /* SECCOMP_RET_ALLOW */

參考

  1. BPF - the forgotten bytecode

wireshark packet dissection

就 encapsulated 協定,每個 dissector 負責解碼一部分協定,然後交給下個階段 dissector。整個 dissection 流程,一開始是 Frame dissector 解碼擷取檔封包本身 (例如 timestamps)。然後資料交給 lowest-level data dissector,例如解析 Ethernet header。payload 再交給下一個 dissector (例如 IP) 等等。

Dissection 實作有兩種方式:內建或 plugin。一開始發展可以用 plugin 減少編譯時間。

doc/README.dissector

proto_register_protocol()


https://www.wireshark.org/docs/wsdg_html_chunked/ChapterDissection.html

libpcap

libpcap 是封包擷取的 C/C++ 函式庫,可以過濾網路界面或 pcap 檔的封包,存成 pcap 檔

使用前都要開啟取得 pcap handle,此時可以設定過濾條件,然後才能讀取封包。封包讀取後可以作各種處理,包括存成 pcap 檔。

開啟

無論是網路界面或 pcap 檔,都需要開啟取得 pcap handle -- pcap_t。即使沒有者兩個來源,只是要產生過濾程式碼或寫 pcap 檔,也需要開啟一個「假」的。

  • 網路界面:pcap_create() → 設定選項 → pcap_activate()。
    • pcap_findalldevs() 列出界面,然後 pcap_freealldevs() 釋出。
    • pcap_lookupdev() 取得第一個非「loopback」的界面。
    • 讀取權限
  • pcap 檔案:pcap_open_offline() 或 pcap_fopen_offline()。
  • 「假」的:pcap_open_dead()

關閉 pcap_t 用 pcap_close()。

* pcap handle 設定
* 網路界面 link 信頭 type 及設定

讀取封包

讀取封包:pcap_dispatch(), pacp_loop(), pcap_next(), pcap_next_ex()。

過濾

讀取之前可以先過濾。如果可能的話,過濾網路界面的封包會在 kernel 裡進行,不需要的封包就不用從 kernel 複製出來。

過濾表示式是一個字串,由 pcap_compile() 編譯成虛擬機器程式碼,然後透過 pcap_setfilter() 設給 pcap_t,或者用 pcap_offline_filter() 套用在已讀取的封包看是否符合。

產生的虛擬機器程式碼用 pcap_freecode() 釋出。特定過濾表示式編譯時可能需要 network mask,pcap_lookupnet() 可用來網路界面的位址和 mask。

存封包

寫 pcap 檔:pcap_dump_open() -> pcap_dump() -> pcap_dump_close()。

統計

統計

送封包

pcap_inject() 或 pcap_sendpacket() 送封包到網路界面。兩者相同,只是前者回傳送出幾 byte,後者回傳 0 表成功。

回報錯誤

回報錯誤

取得版本資訊

取得版本資訊

使用 libpcap (或 windows 下 winpcap) 的程式

  • tcpdump:limited protocol decoding but 大多 *NIX 平台有。capture filter
  • wireshark:功能強大的網路分析程式。capture filter。可用 tcpdump 擷取,然後用 wireshark 分析。
  • tshark:wireshark 指令行版本。
  • dumpcap:wireshark 的一部分,只能擷取封包。
  • ettercap:用來 injecting traffic
  • OpenWrt

參考

  1. https://www.tcpdump.org/manpages/pcap.3pcap.html

udpdump

udpdump 是 wireshark 內部接收 UDP 封包的 server,由其它所在擷取的訊息裝載而成 。udpdump 接收後,把 UDP 進前的 Header 拿掉,轉著 EXPORTED_PDU 格式給 wireshark 解析。

EXPORTED_PDU 格式是訊息前面含有一些 TLV 格式的 meta 資訊
  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |      Option Code              |         Option Length         |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 /                       Option Value                            /
 /             variable length, aligned to 32 bits               /
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 /                                                               /
 /                 . . . other options . . .                     /
 /                                                               /
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |   Option Code == opt_endofopt  |  Option Length == 0          |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

依據 udpdump 原始碼,meta 資訊包含協定名稱 (在啟用時設定,預設 data)、實際來源 IP 和port、目的 IP (固定為 127.0.0.1)、實際目的 port (預設 5555)。

wireshark 依據協定名稱對訊息解析。

問題:協定如果是網路封包

參考來源
  • https://www.wireshark.org/docs/man-pages/udpdump.html 
  • https://code.wireshark.org/review/gitweb?p=wireshark.git;a=blob;f=epan/exported_pdu.h;hb=refs/heads/master
  •  https://www.wireshark.org/docs/wsdg_html_chunked/
  • https://www.wireshark.org/docs/wsdg_html_chunked/PartDevelopment.html

wireshark

wireshark 是功能強大的圖形界面網路分析程式。用 libpcap 或 winpcap 擷取封包

擷取過濾

顯示過濾

星期五, 6月 19, 2020

Thinkpad X1C 7th

規格:
  • 處理器:Intel Core i5-10210U 四核心 (1.6GHz)
  • 記憶體:8G LPDDR3
  • 硬碟:512G PCIe SSD
  • 螢幕:14吋 FHD IPS (1920x1080) 400Nit
  • 1.08 Kg,323 mm x 217 mm x 14.95 mm
  • $40900
還原 (=回復?)
  • 啟動電腦後,按 F11 或「單鍵回復按鈕」來啟動儲存於硬碟或回復光碟中的還原程式。
  • 備份:如何備份整個硬碟呢?接 AC 電源、插 16GB USB。搜尋 recovery,按建立修復磁碟機。允許 Recovery Media Creator 工具程式啟動。依指示建立。
  • 接 AC 電源、插回復 USB、啟動電腦。按 F12 開啟 Boot Menu。選取 USB 開機。疑難排解顯示選用的回復解決方案。視需要選取回復解決方案,依指示完成程序。
擴充
  • 加硬碟?M.2固態硬碟*•混合式固態硬碟*(搭載Intel®Optane™記憶體)
  • 擴充基座:ThinkPad Basic Docking Station、ThinkPad Pro Docking Station、ThinkPad Ultra Docking Station
  • 乙太網路擴充接頭Gen2
  • USB
安裝 Ubuntu
參考
https://certification.ubuntu.com/hardware/201904-26993
https://hackmd.io/7hyw0e1lQUmV3mY7G_gQ0w
http://alan1968.blogspot.com/2019/08/thinkpad-x1-yoga-3rd-add-second-ssd.html

星期日, 6月 14, 2020

inet_pton() and inet_ntop()

IPv4 或 IPv6 位址表示字串,如:
  • 204.152.189.116 (dotted-decimal 的 IPv4 位址)
  • ::1 (colon-separated hexadecimal 的 IPv6 位址)
  • ::FFFF:204.152.189.116 (IPv4-mapped 的 IPv6 位址)
inet_pton() 可轉換成為二進位格式 (struct in_addr 或 strut in6_addr)。而 inet_ntop() 反之。 函數名稱中的 p 是「presentation」,指文字表示字串;n 是「network」。
#include <arpa/inet.h>

// 轉換 IPv4 或 IPv6 數字位址 (表示字串) src_str 為二進位格式 addrptr
int inet_pton(
    int family, // AF_INET 或 AF_INET6
    const char *src_str,
    void *addrptr);
// 回傳 1:成功、0:src_str 格式不是表示字串、–1 on error

// 轉換二進位格式 addrptr 為表示字串 dst_str
const char *inet_ntop(
    int family,
    const void *addrptr,
    char *dst_str,
    size_t len);
// 回傳 指向 dst_str 的文字字串: 成功, NULL: 錯誤
  • family 可以是 AF_INET 或 AF_INET6。擴充並捨棄 inet_aton() 與 inet_ntoa() (只能用在 IPv4)。問題:程式如何判斷位址是 IPv4 或 IPv6?
  • addrptr 依據 family 可指到 struct in_addr 或 struct in6_addr。
  • 呼叫 inet_ntop() 需提供長度 len 的 dst_str buffer,適當的長度是 INET_ADDRSTRLEN (16) 或 INET6_ADDRSTRLEN (46)。

參考來源

TLPI §59.6 §59.13.1