2015年2月5日 星期四

systemd

system management daemon,是 Linux userspace 第一個 process,可取代 init。systemd 改進軟體相依性的框架、允許更多動作可以在開機時同時處理、減少 shell 計算的 overhead。

延伸閱讀
  • http://www.freedesktop.org/wiki/Software/systemd/
  • http://linoxide.com/linux-command/systemd-vs-sysvinit-cheatsheet/

SIP/RTP NAT Traversal

NAT 後面的設備只知道自己的內部位址,不曉得在公網的位址。SIP 訊息裡包含了用於 signaling 及 media 的 IP 及通訊埠,除非是經過 ALG 的 NAT,不然這些位址並不會轉換,公網上的設備並不認得這些內部位址。

在 SIP/SDP 訊息中會有內部位址地方包括:
  • SIP Message Header 中的「From:」:似乎沒關係。
  • SIP Message Header 中的「Contact:」:SIP 話機註冊時,Contact 欄告知目前的位置,讓其他設備可以查到。若 Contact 用內部位址,在公網並不曉得在哪裡,無法 SIP INVITE。
    • registrar 只需要改用 SIP 封包 IP 層的實際來源位址,而不用 SIP Contact 欄位的位址。
    • 需要維持這個 NAT 通道。註冊之後,NAT 開了一個外面的通訊埠,對應到內部的 SIP 話機,公網的封包就可以透過這個通訊埠進到 SIP 話機,但此通訊埠在 NAT 對應到期後就會關閉了,需要持續送收封包來避免到期。SIP 話機註冊週期短於 NAT 對應過期時間可維持 NAT 通道,但 SIP REGISTER 的處理較費力,會加重伺服器的負擔,最好 SIP 話機可以送處理較省力的 SIP OPTIONS,或改由伺服器送 stateless OPTIONS 給 SIP 話機。
  • SIP Message Header 中的「Via:」:SIP 回應訊息是依據 Via 的路徑反向送回,若 Via 用內部位址,路徑就斷了而無法回送回應訊息。
    • SIP 定義的 received 及 RFC 3581 新增 rport:請求時,Via: 加上 ;rport 來告知支援 rport,伺服器端收到後加上「=<通訊埠號碼>」及「;received=<位址>」。回覆時路徑是依據 received 位址及 rport 通訊埠。這個作法比 STUN 好,支援對稱式 NAT。
  • SIP 內容放 SDP 時,欄位「c」和「m」分別是欲接收 RTP 封包的 IP 位址與通訊埠。若這些欄位填入的是內部位址,則通話建立後的 RTP 影音封包將無法收到。 有下列可能的解決方式:
    • 靜態指定 (Static Mapping) -- 事先設定 NAT 要轉換的 IP 位址並指定要轉換後的通訊埠。
    • STUN -- 取得公網 IP 及 port,不支援對稱式 NAT
    • UPnP -- SIP 話機透過 UPnP 尋找 NAT 並得知其公眾 IP。UA 可以要求 NAT 對閒置的外部通訊埠建立對應關係,用於 SIP 及 SDP 訊息中。
    • Session Border Controller (SBC,或稱為 Session Controller、Back-to-Back User-Agent、B2BUA):由公網上的 SBC 修正 SIP/SDP 訊息中的 IP 與通訊埠,並在 SIP 請求訊息加入 Record-Route:,讓之後的 SIP 訊息都會經過 SBC 修正。
    • 將雙方的 SDP 中的「c」與「m」欄位分別修改為 RTP Proxy 的 IP 與通訊埠,讓原本點對點傳送的傳送方式,變成雙方皆和 RTP Proxy 建立通話,由 RTP Proxy 負責轉送和控制雙方 RTP 封包的傳輸。
參考文獻
  1. Best practices for SIP NAT traversal
延伸閱讀
  1. http://www.cs.nccu.edu.tw/~lien/Writing/NGN/firewall.htm
  2. STUN
  3. TURN
  4. ICE

kamailio dispatcher module

dispatcher 模組:從檔案或資料庫讀取轉送目的,規則可以是 round-robin、weight based load balancing、call load distribution、hashing over SIP 封包內容等。
  • 可作為 stateless load balancer。如果需要自動偵測 gateways 是否正常運作,則需要 tm 模組。
  • lightweight 所以執行速度快
  • 可以 runtime 重新載入 routing records
參數
  • 檔案:list_file
  • 資料庫:db_url, table_name, setid_col, destination_col, flags_col, priority_col
  • force_dst, flags (影響 dispatcher 行為), use_default, dst_avp, grp_avp, cnt_avp, dstid_avp, attrs_avp, hash_pvar, setid_pvname, attrs_pvname
  • probe:ds_ping_method, ds_ping_from, ds_ping_interval
  • ds_probing_threshold, ds_ping_reply_codes, ds_probing_mode, ds_hash_size, ds_hash_expire, ds_hash_initexpire, ds_hash_check_interval, outbound_proxy
函數
  • ds_select_dst(setid, alg [, limit]):設定 dst_uri 欄位 (也就是 outbound proxy address 或 $du 變數),請求 URI 沒動。
    • setid:destination set ID
    • alg:選擇 destination 使用的演算法
    • limit
  • ds_select_domain(setid, alg [, limit]):改寫請求 URI
  • ds_next_dst(), ds_next_domain()
  • ds_mark_dst()
  • ds_is_from_list(
  • ds_load_update()
  • ds_load_unset()
MI 或 RPC 指令 
  • ds_set_state
  • ds_list
  • ds_reload

應用範例

stateless forwarding
# Dispatch requests
route[DISPATCH] {
        # round robin dispatching on gateways group '1'
        if(!ds_select_dst("1", "4"))
        {
                send_reply("404", "No destination");
                exit;
        }
        xlog("L_DBG", "--- SCRIPT: going to <$ru> via <$du>\n");
        forward("RTF_DISPATCH");
        exit;
}
stateful forwarding
# Dispatch requests
route[DISPATCH] {
        # round robin dispatching on gateways group '1'
        if(!ds_select_dst("1", "4"))
        {
                send_reply("404", "No destination");
                exit;
        }
        xlog("L_DBG", "--- SCRIPT: going to <$ru> via <$du>\n");
        t_on_failure("RTF_DISPATCH");
        t_relay();
        exit;
}

# Sample failure route
failure_route[RTF_DISPATCH] {
        if (t_is_canceled()) {
                exit;
        }
        # next DST - only for 500 or local timeout
        if (t_check_status("500")
                        or (t_branch_timeout() and !t_branch_replied()))
        {
                if(ds_next_dst())
                {
                        t_on_failure("RTF_DISPATCH");
                        t_relay();
                        exit;
                }
        }
}
discard
route {
        );
}
to one host
route {
        forward(1.2.3.4);
}

route {
        rewritehost(1.2.3.4);
        forward();
}

參考來源

  1. dispatcher
  2. 簡報:Kamailio - Load Balancing Load Balancers
延伸閱讀

kamailio 命令搞

從《kamailio 簡易命令檔》可理解到 kamailio 的設定檔就是如何處理 SIP 封包的命令檔,kamailio 讀取後會編譯成內部的 SIP 處理邏輯。撰寫命令檔需要對 SIP 協定有一些基本的了解。命令檔的語法類似 C 語言,使用 kamailio 內建及外掛模組的函數及變數。一般 kamailio 命令檔包括核心參數設定、模組載入、模組參數設定、以及 SIP 協定處理區塊這些部份,如果命令檔有修改,kamailio 需要重啟。

前置處理指引 (Preprocessor Directives)

類似 C 語言,kamailio 也有類似的前置處理。

#!include_file "path_to_file"
  • 插入檔案內容
#!import_file "path_to_file"
  • 跟 include_file 一樣,但檔案不存在沒關係
#!define, #!trydef, #!redefine
  • define
#!ifdef, #!ifndef, #!else, #!endif
  • 選擇性載入
#!subst "/regexp/subst/[flags]"
  • 字串內的取代 (define 只能取代 ID -- alphanumeric tokens not enclosed in quotes)
  • flags:'i' - ignore case; 'g' - global replacement
#!substdef "/ID/subst/":subst 加上 define ID subst

核心參數設定、模組載入、及模組參數設定

命令檔通常都有一些核心參數 (core parameter) 設定,包含要載入哪些模組、以及模組的參數設定,也都是透過核心參數設定達成。有些核心參數會設定成全域變數,在 kamailio 或模組內部可以使用。

check_via (或指令參數 -K)
  • reply 檢查第一個 VIA 是不是自己。
onsend_route_reply
  • reply 也執行 onsend_route 區塊,預設 reply 是不執行 onsend_route
pv_buffer_size, pv_buffer_slots
  • 設定動態字串的 buffer。字串用到虛擬變數需要動態產生。
loadpath (或 mpath), loadmodule "modulepath", modparam:
  • 模組相關,分別用來設定模組路徑、載入模組、及設定模組參數。有些模組有相依性而有載入先後的差別。
mhomed
server_header,server_signature
  • 預設 kamailio 本身產生的 SIP reply 會含 header「Server: Kamailio (<version> (<arch>/<os>))」,server_header 可更改內容,server_signature=no 關閉不產生
其它還有 advertised_address advertised_port alias async_workers auto_aliases auto_bind_ipv6 children chroot corelog debug description disable_core_dump disable_tls enable_tls exit_timeout flags force_rport fork fork_delay group http_reply_parse latency_limit_action latency_limit_db latency_log listen log_facility log_name log_prefix log_stderror maxbuffer sql_buffer_size max_recursive_level max_while_loops mcast_loopback mcast_ttl memdbg memlog mem_join mem_safety mem_summary mlock_pages modinit_delay open_files_limit phone2tel pmtu_discovery port  reply_to_via server_id shm_force_alloc sip_warning (noisy feedback) socket_workers statistics tos udp_mtu udp_mtu_try_proto user user_agent_header workdir

SIP 協定處理區塊

kamailio 是個處理 SIP 封包的程式,SIP 封包分成請求 (SIP request) 跟回覆 (SIP reply) 兩類。基本上命令檔需要告訴 kamailio 如何處理進來的 SIP 請求封包,最後看是要轉送、產生回覆封包、還是要忽略等等。SIP 請求由命令檔中的 route (或 request_route) 區塊負責,有 SIP 請求進來就執行,有點像 C 語言內定的 main 函數。如果收到的是 SIP 回覆封包就執行另一個內定的區塊 -- onreply_route,但這部份不是必要的,因為 kamailio 對於 SIP 回覆預設會移除第一個 VIA,並依據第二個 VIA 的位址轉送。kamailio 還有其它內定的區塊,包括 onsend_route、branch_route、failure_route、及 event_route[module:event],其中 branch_route 跟 failure_route 搭配 tm 模組才會用到,event_route[module:event] 也需要搭配其它模組。

區塊內有內建函數 (function)、內建 keyword、內建 value 等可以利用,可透過載入模組來擴充函數。

Routing blocks

route 或 route[0] 或 request_route
  • 收到 SIP 請求所要做的動作,需明確的處置,看是要回覆、轉送等等,不然預設是丟棄。
onreply_route
  • 收到 SIP 回覆所作的動作,最後有個隱含的動作是依據 Via 轉送,結束、或者中途執行 exit 或 return(0) 而離開,都會執行。如果執行 drop 離開的話,不會執行隱含的動作,而直接丟棄。
onsend_route
  • 送出 SIP 封包前執行,預設只有請求封包,如果命令檔參數 onsend_route_reply 有設,則包括回覆封包。能執行的指令有限制。
branch_route[name]
  • 呼叫 tm 模組的 t_on_branch(name) 時所作的動作,最後隱含的動作是轉送
failure_route
  • tm 模組
event_route

內建函數

exit() 或 return(0)
  • 結束命令檔處理,執行預設動作
return([retcode])
  • 結束函數。回傳的值預設是 1,可用 $retcode 或 $? 取得,邏輯判斷正數為 true,負數為 false。
drop()
  • 結束命令檔處理,並丟棄,不執行預設動作。
forward()
  • stateless 依據 $du 轉送。$du 是 L3 IP 位址或 L4 port,跟 SIP 內容無關。
rewritehost("domain") 或 sethost, seth
rewriteport("port") 或 setport, setp
rewritehostport("domain:port") 或 sethostport, sethp
rewritehostporttrans("...") 或 sethostporttrans, sethpt
rewriteuser("user") 或 setuser, setu
rewriteuserpass("password") 或 setuserpass, setup
rewriteuri("uri") 或 seturi
  • 這些改寫 request URI,包含 L3/L4 位址
revert_uri()
  • 回復 request URI 修改
force_send_socket(ip [ :port] )
  • 強制使用其它 listen 的 socket 送封包
route(name)
  • 呼叫指定名稱的 route 區塊。指定名稱也可以是字串組合的表示式。
其它還有 add_local_rport avpflags break error exec force_rport add_rport force_tcp_alias isavpflagset isflagset is_int log prefix resetavpflag resetflag set_advertised_address set_advertised_port set_forward_no_connect set_forward_close set_reply_no_connect set_reply_close setavpflag setflag strip strip_tail udp_mtu_try_proto(proto) userphone

Keywords 及 values

keyword 就是 SIP 封包解析後的欄位,通常用在 if 描述 (唯讀?)。另外提供了 value 方便跟 keyword 比較。

af, dst_ip, src_ip, proto, dst_port, src_port, proto
  • 這些是所收到 SIP 封包的 address family、目的/來源 IP 位址、目的/來源 port 及協定
  • af 的值可能是 INET 或 INET6,proto 的值可能是 UDP、TCP、TLS、SCTP、WS、WSS。
method
  • SIP 封包的 method
  • 不確定是否包含 reply
msg:len
uri, status
  • request uri 及 reply 的 status
  • uri 可跟值 myself 作測試,看請求的對象就是自己、還是需要轉送給其它伺服器。myself 是個列表,由設定檔設定的 local IP addresses、hostnames 及 aliases 組成,也就是 kamailio 伺服器服務的 domains。命令檔參數 alias 可加 hostnames、IP addresses、及 aliases 到 myself 列表。 
  • $ru
to_ip, to_port, to_uri, from_uri
  • SIP 封包的 TO 及 FROM 欄位
snd_af, snd_ip, snd_proto, snd_port
  • 類似 af、src_ip、proto、src_port,但是封包要送出的狀況。

Script statements

if-else 或 while 表示式,包括
==相等
!=不相等
=~Posix 正規表示式比對。使用 [[:digit:]]{3} 取代 \d\d\d。
>大於
>=greater or equal
<小於
<=less or equal
&&邏輯 AND
||邏輯 OR
!邏輯 NOT
[ ... ]test operator - 裡面可以是任何算術式。
switch 虛擬變數

Script operations

Assignment (=) 的左邊可以是虛擬變數
  • Unordered List Item AVPs - to set the value of an AVP
    • 附在 SIP 訊息的 Attribute-value pair
  • script variables ($var(…)) - to set the value of a script variable
  • shared variables ($shv(…))
  • $fs - to set send socket
  • $br - to set branch
  • $mf - to set message flags value
  • $sf - to set script flags value
  • $bf - to set branch flags value
字串用「+」串起來,算術運算支援類似 C 語言,有「+」「-」「*」「/」「mod」「|」「&」「^」「~」「<<」「>>」。

Operators

  • (int), (str):type cast
  • eq, ne:字串比較
  • ieq, ine:整數比較
  • ==, !=:字串或整數比較。原則上市轉換成左 operand 的 type,除非左 operand 是 undef
kamailio 的資料型態有整數 (int)、字串 (str)、及 null
一些例子
  • 0 == "" (true)
  • 0 eq "" (false,0 轉成字串是 "0")
  • "a" ieq "b" (true: "a" 跟 "b" 轉成整數都是 0)
  • "a  == "b" (false)

其它

unordered list item AVPs
script variable
shared variable
$null

參考來源

  1. http://www.kamailio.org/wiki/cookbooks/4.2.x/core
  2. http://www.kamailio.org/wiki/cookbooks/4.2.x/pseudovariables
  3. kamailio source code

延伸閱讀

2015年2月4日 星期三

AM335x pinmux

Beaglebone Black 的處理器 AM335x 每個多工接腳都有一個 Pad Control Register 控制,內容如下:

bitfielddescription
2:0MUXMODEpin mux 模式選擇
3PULLUDEN0 = 啟用 pull up/down,1 = 停用
4PULLTYPESEL0 = pull down, 1 = pull up
5RXACTIVE0 = output only, 1 = input or output
6SLEWCTRL選擇 slew rate
  • Pad Control Register 見 TRM 9.2.2 Table 9-1,每個接腳的位址見 TRM 9.3.1 Table 9-7 的 offset 0x800 開始的 conf_<module>_<pin> 暫存器
  • 完整的 pinmux mode 及預設值見 datasheet (Rev. G) 4.2 Table 4-1 Ball Characteristics ,有些會受到開機模式影響
  • pinmux 模式 7 是 GPIO,大部分預設是 GPIO
  • mmc0, mdio, mii1, mii2, I2C0, spi0, uart0, uart1 只有一種選擇
  • USB0 可轉成 UART2,USB1 可轉成 UART3

GPIO
有 4 個 GPIO module,每個 module 有 32 個 GPIO,總共 128 個 GPIO,其中只有部份接到 BBB 的 expansion header P8 及 P9。透過 GPIO 暫存器,可存取資料、設定為中斷等。

待整理
  • cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pins 可看到 pinmux 的設定
  • https://github.com/derekmolloy/boneDeviceTree/blob/master/docs/BeagleboneBlackP8HeaderTable.pdf?raw=true
  • https://github.com/derekmolloy/boneDeviceTree/blob/master/docs/BeagleboneBlackP9HeaderTable.pdf?raw=true
  • device tree
  • cat /sys/kernel/debug/pinctrl/pinctrl-handles
  • /sys/kernel/debug/omap_mux
參考來源:
  1. http://derekmolloy.ie/beaglebone/beaglebone-gpio-programming-on-arm-embedded-linux/

2015年2月3日 星期二

kamailio 簡易 proxy

kamailio 執行時透過命令檔載入需要的模組、設定模組的參數、以及提供 SIP 封包的處理方式。SIP 封包處理語法類似 C 語言,使用 kamailio 核心內建及外掛模組的函數及變數。

下面是一個簡易的 stateless proxy,含免認證 REGISTRAR,可給 SIP 話機註冊後互撥,不用建立帳號。
#!KAMAILIO

# 載入模組
loadmodule "sl.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"

# 處理 SIP 請求,預設是丟棄
route {
    # REGISTER 則 save() 執行 SIP 註冊動作後結束。
    if (method=="REGISTER") {
        if (!save("location"))
            sl_reply_error();
        exit;
    }

    # 其它則 lookup() 查是否有註冊。
    if (!lookup("location")) {
        # 查詢失敗,回 404 等之後結束。
        switch ($rc) {
        case -1:
        # 查不到
        case -3:
        # 內部錯誤
            sl_send_reply("404", "Not Found");
            exit;
        case -2:
        # 有查到,但 Method 不支援
            sl_send_reply("405", "Method Not Allowed");
            exit;
        }
    }

    # 如有查到會置換 uri,呼叫內建函數 forward() 將封包轉送。
    forward();
}

# SIP 回應預設依 Via 轉送,所以不需要回應處理區塊。
載入的模組說明
  • sl:stateless replier,提供 sl_send_reply(code, reason) 及 sl_send_error() 等回覆 SIP status code 的函數。
    • sl_send_reply(code, reason):送 SIP 回覆
    • sl_send_error():依據錯誤碼送 SIP 回覆
    • sl_forward_reply([ code [, reason ] ]):轉送 SIP 回覆,可更改 code 或 reason
    • send_reply(code, reason):取決是否是在 tm 的 transaction 中而 stateful 或 stateless 送 SIP 回覆。
  • usrloc:提供 location database 給 registrar 等其它模組使用
  • registrar:使用 usrloc database 來提供 SIP 註冊功能,這裡用到下列兩個函數
    • save(domain[, flag[, uri]]):儲存註冊資料到 usrloc database,並回 200 OK
    • lookup(domain[, uri]):查詢 usrloc database 取代 uri
問題
  • 如果 INVITE 收到多個不同 To tag 的回應會怎樣?可以怎樣處理?
延伸閱讀

2015年2月2日 星期一

kamailio on Ubuntu

要在 Ubuntu 跑 kamailio 很簡單:
  1. 安裝
    sudo apt-get install kamailio
  2. 編輯 /etc/default/kamailio
    sudo vi /etc/default/kamailio
    將 #RUN_KAMAILIO=yes 行首的 # 移除
  3. 啟動
    sudo service kamailio start
然後不用建帳號或密碼就可以讓網路電話註冊,註冊後就可以互打。

使用的命令檔是 /etc/kamailio/kamailio.cfg。

延伸閱讀

SIP header Via

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