/math/randlib/

td class='logo' rowspan='2'>cgit logo index : fastalg-wiki
Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogblamecommitdiffstats
path: root/NetfilterUserspaceQueue.asciidoc
blob: 0df9ff2442c2f02bb4d8994493837ab08920caa5 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15














                                                                                                                                                                                                     





                                              

                     
                                           









                                                                                                                                

                                                                                           
































                                                                                                                                                                                                                                                 
== 關於 Netlink
 * *Netlink* 是 Linux 下的一種 socket (`AF_NETLINK`),用來在 user 和 kernel 之間溝通
 * *Netlink* 就是個 datagram socket,可用 `send` 和 `recv`,詳細請參考 netlink(7)
 * *Netlink* 資料傳送格式請參考 Linux source 的 https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/tree/include/net/netlink.h?id=refs/tags/v3.14.5[include/net/netlink.h]
 * 為方便操作,我們可以使用 *libmnl* 幫我們處理 Netlink 的 protocol

== 運作原理與操作方式
[WARNING]
操作與測試過程中可能導致網路斷線,強烈建議直接在本機 console 上操作。

 . 先讓封包進入 kernel 提供的指定的 queue 中
 . 接著會有一個 daemon 將封包從 queue 中取出,並決定此封包應轉送到何處
 . 此 daemon 會將封包加上標記,並告知 kernel 重跑整個 chain (`NF_REPEAT`)
 . 於是我們的 NFTables 設定可能會長這樣:
------------------------------------------------------------------------
table ip filter {
  chain prerouting {
    type filter hook prerouting priority -155;
    mark < 1 queue num 0 options bypass
  }
}
table ip nat {
  chain prerounting {
    type nat hook prerouting priority -150;
    meta mark 1 dnat my_http_server_1:80
    meta mark 2 dnat my_http_server_2:80
    meta mark 3 dnat my_http_server_3:80
    ...
  }
}
------------------------------------------------------------------------
  * queue 和 mark 編號可以自行指定,這裡我們使用第 0 個 queue
  * 封包剛進來時並沒有 mark,所以會進入 queue 中讓 daemon 處理
  * 重跑此 chain 時,因為 mark 已改變,不再進入 queue,所以我們可以以此為依據 DNAT 到不同的機器
  * dnat 指令一定要放在 type nat 的 chain 裡。
  * nat 只會收到整個連線的第一個封包,所以 queue 指令要放在 filter。

== 查看封包內容
執行完 `nfq_nlmsg_parse(nlh, attr)` 以後,我們可以在 `attr[NFQA_PAYLOAD]` 拿到封包內容(可能是 IPv4 或 IPv6),於是我們就能使用以下一些 *libnetfilter_queue* 提供的 helper functions 查看封包資料。
[source,c]
// 取出整個 IPv4 封包
char *ipv4_payload = mnl_attr_get_payload (attr[NFQA_PAYLOAD]);
uint16_t ipv4_payload_len = mnl_attr_get_payload_len (attr[NFQA_PAYLOAD]);
struct iphdr *ipv4_hdr = (struct iphdr*)(ipv4_payload);
// 把封包丟進 pkt_buff
struct pkt_buff *ipv4_pktb = pktb_alloc (AF_INET, ipv4_payload, ipv4_payload_len, 0);
nfq_ip_set_transport_header (pktb, ipv4_hdr);
// 看看是 TCP 還是 UDP
switch (ipv4_hdr->protocol) {
  case 6: // TCP
    struct tcphdr *th = nfq_tcp_get_hdr (ipv4_pktb);
    char* tp = nfq_tcp_get_payload (th, ipv4_pktb);
    // 做點事情 ......
    break;
  case 17: // UDP
    struct udphdr *uh = nfq_udp_get_hdr(ipv4_pktb);
    char* up = nfq_udp_get_payload (uh, ipv4_pktb);
    // 做點事情 ......
    break;
  default:
    // 做點事情 ......
}
pktb_free (ipv4_pktb);

== 加上 mark 並送回 kernel
------------------------------------------------------------------------
nfq_nlmsg_verdict_put(nlh, id, NF_REPEAT);
nfq_nlmsg_verdict_put_mark(nlh, 喜歡的編號);
------------------------------------------------------------------------