2013年8月27日 星期二

Ralink Ethernet Driver

OpenWrt 有 Ralink Ethernet Driver

module init
  • debugfs 初始化 (選擇性)
  • esw 初始化
  • 註冊 platform driver:登記 probe 及 remove 函數
platform driver 的 probe 函數
  • get memory base
  • alloc ether net dev
  • get irq
  • 登記 net dev ops:包括 init, uninit, open, stop, start_xmit, tx_timeout, ... 等函數
  • 設定 net dev priv data
  • 註冊 net dev
net dev 的 init 函數
  • ...
  • hardware init
    • request irq
    • alloc 256 rx_info, 256 tx_dma, 256 rx_dma, one skb per rx_info
    • 初始化 tx_dma, 初始化 rx_info dma_addr (來自 skb), 初始化 rx_dma
    • set macaddr to SoC
    • 初始化 SoC tx_dma, rx_dma
    • set sys_freq to SoC
    • tasklet init: tx & rx
    • other SoC init
net dev 的 open 函數
  • ...
net dev 的 start_xmit 函數
  • ...
net dev 的 tx_timeout 函數
  • ...
封包接收
  • ...
硬體加速
  • Hardware NAT, QoS, TCP/UDP/IP checksum offloading
延伸閱讀

2013年8月21日 星期三

Linux bridge

將電腦當作 Ethernet Switch 使用,除了原有的 Ethernet 介面外,需要再多一個 Ethernet 介面,例如用 USB Ethernet,方便第三者抓封包 (註:用既有的 Ethernet Switch 搭配 ettercap 也可以抓封包,參照《第三者 Ethernet 抓封包》)。

Ethernet Switch 的工具軟體在 bridge-utils
sudo apt-get install bridge-utils

軟體 Ethernet Switch 設定
sudo ifconfig eth0 0.0.0.0
sudo ifconfig eth1 0.0.0.0
sudo brctl addbr br0
sudo brctl addif br0 eth0
sudo brctl addif br0 eth1
sudo dhclient br0

設定在 /etc/network/interfaces 的話,新增
auto br0
iface br0 inet dhcp
然後
sudo vi /etc/network/interfaces
sync && sudo init 6

參考:
  1. http://www.linuxfoundation.org/collaborate/workgroups/networking/bridge
  2. Ubuntu 12.04 & 12.10 調校備忘 的 Network bridge
延伸閱讀

寫程式用字型

下載字型檔會開啟「字型檢視程式」,然後按「安裝」就可以使用。
或者放到 ~/.local/share/fonts/,執行 fc-cache -f -v。

Monaco (來自 Apple Inc.)
Consolas (來自 Microsoft)
  • 搜尋「Consolas.ttf」可以找到
設定
cp /etc/fonts/conf.d/69-language-selector-zh-tw.conf ~/.config/fontconfig/fonts.conf
vi ~/.config/fontconfig/fonts.conf # 修改 monospace 那段如下方
fc-cache -f -v
修改內容,只保留
 <?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
    <!-- Set fonts selection for zh-tw monospace -->
    <match target="pattern">
        <test qual="any" name="family">
            <string>monospace</string>
        </test>
        <test name="lang">
            <string>zh-tw</string>
        </test>
        <edit name="family" mode="prepend" binding="strong">
            <string>Monaco</string>
            <!--string>Microsoft JhengHei</string-->
        </edit>
    </match>
</fontconfig>

註:放字型的目錄
  • ~/.local/share/fonts/
  • /usr/share/fonts/
註:字型相關程式:fc-cache, fc-list 等

參考文章:
  1. Programmer 專用最佳字型 - Monaco font
  2. Monaco + LiHei 字型優化 on Ubuntu 10.04+
  3. Linux 安裝與使用
  4. 電腦漢字體
  5. Ubuntu 14.04 LTS 中文修正 (刪除不需要的設定檔,修改設定檔) 

2013年8月20日 星期二

OpenWrt 套件定義的 KCONFIG

OpenWrt 套件都要定義
define Package/套件名

define KernelPackage/套件名
裡面的一些參數,例如 TITLE:= 是填顯示的標題名稱。

其中有一項是 KCONFIG:=,預設是空的,可用來增加 Kernel 的設定,例如 package/hostap-driver/Makefile 的 define KernelPackage/hostap,就有設定
KCONFIG:=CONFIG_HOSTAP CONFIG_HOSTAP_FIRMWARE=y CONFIG_HOSTAP_FIRMWARE_NVRAM=y
,如果選了 hostap 套件,Kernel 設定就會增加

CONFIG_HOSTAP=m
CONFIG_HOSTAP_FIRMWARE=y
CONFIG_HOSTAP_FIRMWARE_NVRAM=y
,對應到 Kernel 程式,包括外部模組,就會有

#define CONFIG_HOSTAP_MODULE
#define CONFIG_HOSTAP_FIRMWARE
#define CONFIG_HOSTAP_FIRMWARE_NVRAM

的定義。

套件額外的選單設定 define Package/套件名/config 只影響 OpenWrt 的設定,且不會有對應的程式碼定義。

2013年8月17日 星期六

OpenWrt Unified Configuration Interface

Unified Configuration Interface (UCI) 是 OpenWrt 的設定環境,大部分的設定都使用 UCI,然而還有一些設定並不在 UCI

使用 UCI 的設定檔是文字檔,放在 /etc/config/ 目錄下,可以用文字編輯器編譯,也可以透過 uci 指令存取,或者其它程式 API 存取,例如 shell script 的 /lib/config/uci.sh、C library /lib/libuci.so、lua library /usr/lib/lua/uci.so 等,適合用於資源有限不好執行一個資料庫的環境。

uci 設定檔有三層架構
  1. <config>:/etc/config/ 下的設定檔檔名
  2. <section>、<section-type>:每個設定檔內容分成許多 config 開始的 section,section 有 <section-type>。格式是:
    config <section-type> [<section>]
    <section> 是 section 的名稱,可有可無。沒有 section 名稱時用 @<section-type>[index] 的方式存取。
  3. <option>、<value>:每個 section 有 option 或 list。
uci 設定檔格式
config section-type section-name
config section-type
option option-name option-value
list option-name option-value
# 開始為註解註界
config 表示一個 section 開始,type 是必要的,讓程式決定如何應用。可以沒有 name,稱為 anonymous section。
option 提供選項 name 到 value 的對應,list 單一 name 可以有多個 value。

檔案範例 /etc/config/foo
# cat /etc/config/foo

config bar first
        option name 'Mr. First'
config bar
        option name 'Mr. Second'
config bar third
        option name 'Mr. Third'
        list kid aaa
        list kid bbb
首先這裡存在 <config> 檔 foo,內容分成許多 config 開頭的 section,每個 section 有 option 或 list。範例裡 <section-type> 都是 bar。每個 option 定義一對<option> 及 <value>,而 list 讓一個 <option> 有許多 <value>。

<config>、<section>、<section-type>、<option>、<value>

uci 指令:
# uci
Usage: uci [<options>] <command> [<arguments>]

Commands:
        batch  
        export     [<config>]
        import     [<config>]
        changes    [<config>]
        commit     [<config>]
        add        <config> <section-type>
        add_list   <config>.<section>.<option>=<string>
        show       [<config>[.<section>[.<option>]]]
        get        <config>.<section>[.<option>]
        set        <config>.<section>[.<option>]=<value>
        delete     <config>[.<section[.<option>]]
        rename     <config>.<section>[.<option>]=<name>
        revert     <config>[.<section>[.<option>]]
        reorder    <config>.<section>=<position>

Options:
        -c <path>  set the search path for config files (default: /etc/config)
        -d <str>   set the delimiter for list values in uci show
         -f <file>  use <file> as input instead of stdin
        -L         do not load any plugins
        -m         when importing, merge data into an existing package
        -n         name unnamed sections on export (default)
        -N         don't name unnamed sections
        -p <path>  add a search path for config change files
        -P <path>  add a search path for config change files and use as default
        -q         quiet mode (don't print error messages)
        -s         force strict mode (stop on parser errors, default)
        -S         disable strict mode
        -X         do not use extended syntax on 'show'

透過 <config>.<section>.<option> 存取 <value>,而透過 <config>.<section> 取到的是 <section-type>。<section> 可用特定 <section-type> 的 index 表示法取代,index 從 0 開始,第一個是 @<section-type>[0]。也可以從後面數,此時 index 是負的,從 -1 開始,最後一個是 @<section-type>[-1]。
# uci get foo.first
bar
# uci get foo.first.name
Mr. First
# uci get foo.@bar[0].name
Mr. First
# uci get foo.@bar[1].name
Mr. Second
# uci get foo.@bar[-1].name
Mr. Third
# uci get foo.third.kid
aaa bbb
要注意的是 uci show [<config>] 時,會顯示所有的 <section-type> 及 <value>,<section-type> 的 index 不計有 <section> 的,所以可能會有所不同。
# uci show foo
foo.first=bar
foo.first.name=Mr. First
foo.@bar[0]=bar
foo.@bar[0].name=Mr. Second
foo.third=bar
foo.third.name=Mr. Third
foo.third.kid=aaa bbb


新增
uci add <config> <section-type>
uci set <config>.<section>=<section-type>

touch /etc/config/pbx
uci set pbx.201=user
uci set pbx.201.CFUC=202
uci commit pbx

參考:
  1. http://wiki.openwrt.org/doc/uci
  2. http://wiki.openwrt.org/doc/techref/uci
  3. Using UCI as stand alone (使用範例)
  4.  https://openwrt.org/docs/guide-user/base-system/uci
待續:
  • 設定/新增
    • uci  set <config>.<section>[.<option>]=<value>
    • uci add <config> <section-type>
    • uci add_list <config>.<section>.<option>=<string>
    • uci commit [<config>]
    • uci revert     <config>[.<section>[.<option>]]
  • 其它
    • uci batch
    • uci export [<config>]
    • import     [<config>]
    • changes    [<config>]
    • delete     <config>[.<section[.<option>]]
    • rename     <config>.<section>[.<option>]=<name>
    • reorder    <config>.<section>=<position>

2013年8月16日 星期五

Linux MODULE_LICENSE

MODULE_LICENSE("許可證") 會在產生 .ko 檔裡面的 .modinfo section 放 "lincense=許可證" 的資訊,可用 objdump 去看。

其中,只有許可證是 "GPL", "GPL v2", "GPL and additional rights", "Dual BSD/GPL", "Dual MIT/GPL", "Dual MPL/GPL" 之一,才認為是 GPL 相容。

如果不是 GPL 相容,有下列影響:
  • 關掉 debug_locks (作用不是很清楚),並顯示「Disabling lock debugging due to kernel taint」
  • 不能用 EXPORT_SYMBOL_GPL 的函數,這個應該是最大的限制
  • 污點 (存在 tainted_mask) 紀錄有使用非 GPL 相容的模組。其它污點有模組強迫載入等,只是除錯用的紀錄。

Network Interface Checksum Offload

如果網路界面硬體有提供計算 TCP/UDP/IP Checksum 功能,這樣軟體就可以省下計算的負擔。

TCP/UDP/IP Checksum 基本上是使用 16-bit 1's complement 相加,也就是用一般的 16-bit 相加,再把進位加進來。這對 32-bit 處理器而言,使用 16-bit 相加,全部進位會累積在高位元的 16-bit,最後再把高位元的 16-bit 加進來,有可能再進位所以再加一次高位元的 16-bit。

IP checksum 只計算 IP header 的部份 (20 bytes),任何一個欄位改變,例如 time-to-live 每經過一個 router 減一,就要重算。而 TCP/UDP checksum 計算的範圍包括 TCP/UDP header 跟 data,以及來自 IP header 的一些欄位 (pseudo header),計算份量較多。雖然經過 router 不需要重算,但經過 NAT router 時 IP 變了則需要重算,這會對原本處理器效能就不高的 NAT home router 造成一些負擔。不過 UDP checksum 是選擇性的,可以不算,填 0 就好。TCP checksum 是不能省略的。

ICMP header checksum?

Tx Checksum Offload

首先需設定 net_device features,告訴 Kernel 硬體有什麼 checksum 功能,Kernel 就不會去算:
  • NETIF_F_IP_CSUM:TCP/UDP over IPv4 的 checksum
  • NETIF_F_IPV6_CSUM:TCP/UDP over IPv6 的 checkcum
  • NETIF_F_HW_CSUM:以上兩者
再來 hard_start_xmit() 或 ndo_start_xmit() 時,如果 skb 的 ip_summed 有設 CHECKSUM_PARTIAL,表示需要硬體去產生 checksum。

Rx Checksum Offload

netif_rx()netif_receive_skb() 前,將 skb ip_summed 設為 CHECKSUM_UNNECESSARY,表示不需要再算 checksum,包括 loopback 界面也不需要算 checksum。

net_device features 可設定 NETIF_F_RXCSUM 來紀錄有 Rx Checksum Offload 功能。

參考

  1. Linux kernel source
  2. TCP/IP Illustrated, Volume 1 The Protocols, W. Richard Stevens

相關文章

最後更新 20200721

TUN and TAP: Virtual Network Device

在某些情況,需要軟體提供虛擬的網路界面,方便網路應用程式使用,例如 VPN 或 Virtual Machine 等。

VPN 是對 WAN 網路連線做加密等保護,讓你覺得好像在使用 LAN 環境一樣安全,所以 VPN 程式會去攔截 WAN 界面所有往來的封包,加以改良,加上 VPN header、資料加密等。但為了讓本來就有的網路應用程式不用更改,VPN 程式提供另一個軟體虛擬的網路界面給網路應用程式使用。

Virtual Machine 是用軟體模擬的一台電腦,常常需要提供網路界面給外界存取,又很有可能沒有多餘的、實體的網路界面可用,於是提供虛擬的網路界面,搭上母體電腦的 bridge 或 router。

虛擬網路界面一般有兩種,TAP 跟 TUN。TAP (network tap) 就是 Ethernet 插座的意思,模擬 Ethernet 設備的網路介面,可送收 L2 封包作為 bridge。而 TUN (network tunnel) 模擬成網路層的設備,可送收 L3 封包,跟 routing 搭配使用。

在 Linux,TUN 跟 TAP 都是由 tun 驅動程式實作,載入時會建立一個 /dev/net/tun 的 char device 。當 user-space 程式 (通常是常駐的 daemon 程式) 開啟 /dev/net/tun 並 ioctl TUNSETIFF 做設定,便會建立虛擬的網路介面。此時網路應用程式就可以透過 ifconfig 設定 IP 等啟用建立的虛擬網路介面,開始送收封包。

參考文獻
  1. TUN/TAP [Wikipedia]
  2. tun 驅動程式原始碼:drivers/net/tun.c 
相關文章

Linux timeout by jiffies

之前這篇提到,Linux 的 udelay() 以迴圈延遲 1µs。每個 tick 可以跑 loops_per_jiffy 個延遲迴圈,一秒就是 loops_per_jiffy * HZ 個迴圈,1µs 就是 loops_per_jiffy * HZ / 1000000 個迴圈。mdelay() 或 ndelay() 也是類似的作法。但這些函數都是 busy waiting,等待時不能做其它事。

jiffies 是 Linux 全域變數,每 1/HZ 秒會自動加一,可用來判斷 timeout。範例如下:

unsigned long timeout;

timeout = jiffies + HZ/2; /* 500 ms timeout */

do{
        /* do something */
        if (some_state)
                break;
} while (time_after(timeout, jiffies)); /* 500ms timeout */

jiffies 跟 time_after 定義在 inclue/linux/jiffies.h。

相關文章

2013年8月14日 星期三

Network Interface Packet Reception

網路界面驅動程式有兩種接收封包的方式 -- 中斷驅動和 NAPI。傳統是用中斷驅動,在中斷服務程式裡接收封包,但封包量大時會有中斷次數太多而效能變差的問題。NAPI (意思是 New API) 改成中斷服務程式只通知有封包,Kernel 再排程一次接收多個封包來提昇效能。

中斷驅動方式
  • 收到 frame 呼叫中斷服務程式
  • 在中斷服務程式通常依封包大小 dev_alloc_skb() 取得 sk_buff 放入封包,struct net_device *dev 指到網路界面、辨別 protocol (什麼時候用到?)、填寫ip_summed 等參數,最後呼叫 netif_rx()。
  • 每個處理器有個 softnet_data,netif_rx() 將 sk_buff 放到執行中斷處理器的 softnet_data 的 input_pkt_queue。如果原本 input_pkt_queue 是空的,執行netif_rx_schedule() 將 backlog_dev 加到 softnet_data 的 poll_list 並啟用 NET_RX_SOFTIRQ。如果 input_pkt_queue 大於 netdev_max_backlog 表示滿了,則 drop 並回傳 NET_RX_DROP,可知道壅塞程度。
  • 技巧:預先以最大封包為考量取得 skb,讓封包直接接收到 skb。change_mtu() 可知道最大封包大小。
NAPI 用 polling 方式接收封包,底層需要有數個封包的 buffer 放新進封包,當已知有封包進來的情況下,kernel 才擇時去執行 net_device 的 poll 函數並一次將 buffer 中的全部封包接收。這段時間如有新封包再進來,不需要再通知 kernel。當封包流量大時,例如每秒幾千個封包,可以大大減少中斷的次數,降低 kernel 分心。
  • 初始化:net_device 設 poll 函數及 weight,weight 說明界面的重要度,不可超過可 buffer 封包的數目。
  • 封包進來的中斷呼叫 netif_rx_schedule() 就好,kernel 會排程用 poll 去取封包。
  • poll 函數一次最多接收 budget 或 quota 個封包,一樣呼叫 dev_alloc_skb() 取得 skb,放進收到的封包,設定 skb dev、protocol、ip_summed 等參數,但最後是呼叫 netif_receive_skb()。如果接收的封包都處理了,呼叫 netif_rx_complete(),並啟用中斷。
NAPI 有另一個好處,需要拋棄封包時,也可以在收進 kernel 之前就處理掉。例如網路子系統壅塞,可以只收額定的封包。

NAPI 還有更新的改進,從 net_device 獨立出 napi_struct 資料結構,改由驅動程式提供。好處是一個界面可以有多個 napi_struct 來對應硬體支援 multiple receive queues。此外使用的函數也大多不同,如下表:
\中斷驅動NAPINew NAPI
初始化-
  • 登記 net_device poll 函數
  • netif_napi_add() 加入一個 napi_struct 並登記 poll 函數
  • napi_enable
中斷服務程式dev_alloc_skb()
複製封包、設定參數
netif_rx
netif_rx_schedule()napi_schedule()
接收封包dev_alloc_skb()
複製封包、設定參數
netif_receive_skb
buffer 處理完畢-netif_rx_complete()napi_complete()
結束使用--napi_disable()
netif_napi_del()
註:skb 可以事先準備,在初始化時就 dev_alloc_skb

參考:
  1. LDD3 ch17
  2. Newer, newer NAPI
  3. 每個接收封包會在 netif_rx() 或 netif_receive_skb() 執行 netpoll_rx() 一次。
  4. Understanding the Linux Kernel, 3rd Edition 的 4.7 Softirqs and Tasklets (說明 softirq)
相關文章
延伸閱讀
  • Linux Network Scaling: Receiving Packets
    • RSS (Receive Side Scaling)
    • RPS (Receive Packet Steering)
    • RFS (Receive Flow Steering)
    • aRFS (Accelerated Receive Flow Steering)
  • Linux network stack, Packet ingress flow, NAPI interruption coalescence, zero-copy (DMA, mmap())
最後更新 20200721

GPT

GUID Partition Table (GPT) 是一種新的硬碟分割表,擺脫 MBR 分割表 2.2TB 的限制,使用 GUID (Globally Unique IDentifier) 來表示分割的用途。

MBR 分割表使用 32-bit logic block addressing (LBA),所以有 512 bytes x 232 = 2.2 TB 的限制,除非改用較大的 logic block (也就是 sector 大小)。而 GPT 使用 64-bit LBA,大大地擺脫這個限制。

雖然 GPT 是 UEFI 的一部分,但可以獨立使用。64-bit 的Windows Vista/7/8 支援 GPT,並且必須是 UEFI 模式開機。而 Linux 支援 GPT,可用 Legend BIOS 開機;UEFI 開機的話,需要 64-bit Linux。
圖片來自 Wikipedia

GPT 的結構如圖,LBA 0 放 MBR,以便於跟 MBR 相容,當作 MBR 硬碟使用。接下來 LBA 1 放 GPT Header,內含指示分割表 (Partition Entry Array) 在哪裡,通常是 LBA 2。然後從 LBA 2 開始就是分割表,至少預留 16,384 bytes。每個 Partition Entry 預設為最小的 128 bytes,可有 16,384 ÷ 128 = 128 entries,用來表示 128 個分割。

如果 sector 大小是 512 bytes,16,384 bytes 需要 32 sectors,所以分割表佔 LBA 2 到 LBA 33,分割就從 LBA 34 開始。但實際上為了對齊,可能預留更大的分割表空間,例如從 LBA 40 (對齊 4KB sector)、LBA 63 (對齊 cylinder boundary)、或 LBA 2048 (對齊 1 MiB) 開始。在硬碟的最後面,會有 GPT Header 跟分割表的備份。

參考:
  1. GUID Partition Table
相關文章:

2013年8月7日 星期三

OpenWrt telnet

要能 telnet,telnetd 需要執行。我想設計的理念是剛安裝完,root 沒密碼,此時便執行 telnetd,所以 telnetd 在特定條件下才會執行。

/etc/init.d/telnet:

#!/bin/sh /etc/rc.common
# Copyright (C) 2006-2011 OpenWrt.org

START=50

has_root_pwd() {
 local pwd=$([ -f "$1" ] && cat "$1")
       pwd="${pwd#*root:}"
       pwd="${pwd%%:*}"

 test -n "${pwd#[\!x]}"
}

get_root_home() {
 local homedir=$([ -f "$1" ] && cat "$1")
 homedir="${homedir#*:*:0:0:*:}"

 echo "${homedir%%:*}"
}

has_ssh_pubkey() {
 ( /etc/init.d/dropbear enabled 2> /dev/null && grep -qs "^ssh-" /etc/dropbear/authorized_keys ) || \
 ( /etc/init.d/sshd enabled 2> /dev/null && grep -qs "^ssh-" "$(get_root_home /etc/passwd)"/.ssh/authorized_keys )
}

start() {
 if ( ! has_ssh_pubkey && \
      ! has_root_pwd /etc/passwd && ! has_root_pwd /etc/shadow ) || \
    ( ! /etc/init.d/dropbear enabled 2> /dev/null && ! /etc/init.d/sshd enabled 2> /dev/null );
 then
  service_start /usr/sbin/telnetd -l /bin/login.sh
 fi
}

stop() {
 service_stop /usr/sbin/telnetd
}

包括:
  1. 沒有 ssh pubkey 且
  2. 沒有 root password 且
  3. ssh server (dropbear 或 sshd) 未啟用 (註:)
註:未啟用 (not enabled) 未必未執行 (not started),有無啟用是檢查對應的 /etc/rc.d/Sxxx 是否存在

再來,telnet 進來的 login script 是 /bin/login.sh

#!/bin/sh
# Copyright (C) 2006-2011 OpenWrt.org

if ( ! grep -qs '^root:[!x]\?:' /etc/shadow || \
     ! grep -qs '^root:[!x]\?:' /etc/passwd ) && \
   [ -z "$FAILSAFE" ]
then
 echo "Login failed."
 exit 0
else
cat << EOF
 === IMPORTANT ============================
  Use 'passwd' to set your login password
  this will disable telnet and enable SSH
 ------------------------------------------
EOF
fi

exec /bin/ash --login
會再檢查一次 root passwd (但方式不同)

OpenWrt package Config.in

有時候套件需要額外設定,可在 menuconfig 選單新增選項
  1. package 的 Makefile 新增定義 define Package/套件名稱/config 或 define KernelPackage/套件名稱/config,例如
  2. 
    define Package/uhttpd/config
      config PACKAGE_uhttpd_debug
        bool "Build with debug messages"
        default n
    endef
    

    或者
    
    define Package/busybox/config
            source "$(SOURCE)/Config.in"
    endef
    

    可以直接寫在裡面,或者寫在另一個檔案。$(SOURCE) 是 Makefile 同一目錄。
  3. package 的 Makefile 可以對新增的選項參數做 ifdef-endif 等判斷,可能用來決定一些編譯參數等。
  4. make menuconfig 套件的選擇下會多出 Configuration  --->
  5. 預設設定如果要跟設定檔不同,且要存下來的話,可能只能自己存一個 .config 到其它地方,裡面有填好 TARGET 及你要的特別設定,複製到 .config,然後 make defconfig 產生完整的 .config,包含其它預設設定。[參考]

OpenWrt package 範例:
  1. package/busybox -- 可能是最大的範例
  2. 搜尋,用「grep 'define .*\/config\>' package/*/Makefile」

SIP header Via

所有 SIP 訊息 都要有 Via,縮寫 v。一開始的 UAC 和後續途經的每個 proxy 都會疊加一個 Via 放傳送的位址,依序作為回應的路徑。 格式 sent-protocol sent-by [ ;branch= branch ][ ; 參數 ...] s...