@iamproz2911
# Week5: Nfacct API
[5th week] Nfacct
# Nội dung:
1.1. Concept
nfacct is the command line tool to create/retrieve/delete accounting objects
Main Features
- listing the objects of the nfacct table in plain text/XML
- automatically get and reset objects of the nfacct table
- adding new objects to the nfacct table
- deleting objects from the nfacct table
1.2. libraries
- libnetfilter_acct is the userspace library providing the interface to extended accounting infrastructure.
- libmnl: is a minimalistic user-space library oriented to Netlink developers. There are a lot of common tasks in parsing, validating, and constructing of both the Netlink header and TLVs that are repetitive and easy to get wrong. This library aims to provide simple helpers that allow you to reuse code and avoid re-inventing the wheel
1.3. Sequence Diagram

2. Yêu cầu triển khai:
2.1. Dựa vào gợi ý về khái niệm và tính năng của Nfacct hãy xây dựng các file cấu hình của Nfacct để thêm/sửa/xoá các tài khoản.
Chú ý kết hợp với iptables để kiểm tra có thực hiện được các thao tác trên hay không?
2.2. Xây dựng API thực thi các công việc dựa vào Sequence Diagram trên
## Nfacct
NNfacct sẽ được sử dụng để tạo và quản lý các tài khoản ghi nhận lưu lượng (accounting objects) trên hệ thống.
Add account nfacct:
sudo nfacct add my_acc1
sudo nfacct add my_acc2
Delete account nfacct:
sudo nfacct delele my_acc2
Show nfacct config:
sudo nfacct list json

Mỗi tài khoản trong list nhận các lưu lượng gói tin đi qua khác nhau, phụ thuộc vào cấu hình rules Iptables. Ngoài ra còn các lệnh như Flush, Monitor, Restore... dùng để quản lí các tài khoản cụ thể hơn hay "xóa bỏ" hoặc "đẩy" (flush) dữ liệu hiện tại trong bộ đệm của công cụ.
## Iptables
Khi sử dụng iptables kết hợp với nfacct, có thể phân loại lưu lượng theo các yếu tố như giao thức (TCP, ICMP, UDP), địa chỉ IP nguồn hoặc đích, hoặc cổng (port). Mỗi loại lưu lượng sẽ được chuyển hướng vào một tài khoản tương ứng trong nfacct.
Mỗi tài khoản lưu trữ gói tin qua các chain khác nhau, giao thức, ip hoặc các hành động khác nhau.
## Libraries
Sử dụng 2 thư viện:
Tương tác với hệ thống Netfilter của Linux để quản lý và theo dõi các account.
libnetfilter_acct: cho phép nfacct thực hiện các thao tác như truy xuất và thay đổi dữ liệu trong bảng nfacct.
Sử dụng các hàm như nfacct_alloc, nfacct_attr_set_*, và nfacct_nlmsg_build_* để thao tác với các account.
libmnl: tương tác với Netlink, cung cấp các công cụ xử lý giao thức Netlink.
Các hàm như mnl_socket_open, mnl_socket_bind, và mnl_socket_sendto được sử dụng để tương tác với Netlink.
## Xây dựng API theo sơ đồ sequence
Sơ đồ Sequence gồm có:
Khởi tạo (Create):
Mở socket (mnl_socket_open).
Ràng buộc socket (mnl_socket_bind).
Lấy portid từ socket (mnl_socket_get_portid).
Lấy dữ liệu (Get):
Tạo một thông điệp Netlink (nfacct_nlmsg_build_hdr).
Gửi thông điệp (nfacct_socket_sendto).
Nhận dữ liệu (mnl_socket_recvfrom trong vòng lặp).
Gọi callback (mln_cb_run).
Hủy kết nối (Destroy):
Đóng socket.
Thêm Tài Khoản (Add nfacct):
Code: addNfacct
```
void addAccountingObject(std::vector<nfacct*>& objects, const std::string& name, uint64_t packets = 0, uint64_t bytes = 0, uint32_t flags = 0, uint64_t quota = 0) {
struct nfacct *acct = nfacct_alloc();
if (!acct) {
std::cerr << "Failed to allocate memory for accounting object!" << std::endl;
return;
}
// Thiết lập các thuộc tính của đối tượng kế toán
nfacct_attr_set_str(acct, NFACCT_ATTR_NAME, name.c_str());
nfacct_attr_set_u64(acct, NFACCT_ATTR_PKTS, packets);
nfacct_attr_set_u64(acct, NFACCT_ATTR_BYTES, bytes);
if (flags) {
nfacct_attr_set(acct, NFACCT_ATTR_FLAGS, &flags);
nfacct_attr_set_u64(acct, NFACCT_ATTR_QUOTA, quota);
}
// Thêm đối tượng vào danh sách
objects.push_back(acct);
try {
// Tạo thông điệp Netlink
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq;
seq = time(NULL);
nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_NEW, NLM_F_CREATE | NLM_F_ACK, seq);
nfacct_nlmsg_build_payload(nlh, acct);
// Mở socket Netlink và gửi thông điệp đến kernel
nl = mnl_socket_open(NETLINK_NETFILTER);
if (!nl) {
throw std::system_error(errno, std::generic_category());
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
mnl_socket_close(nl);
throw std::system_error(errno, std::generic_category());
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
mnl_socket_close(nl);
throw std::system_error(errno, std::generic_category());
}
// Nhận và xử lý phản hồi từ kernel
int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, seq, portid, nullptr, nullptr);
if (ret <= 0) {
break;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
if (ret == -1) {
mnl_socket_close(nl);
throw std::system_error(errno, std::generic_category());
}
mnl_socket_close(nl);
} catch (const std::exception& e) {
std::cerr << "Error while adding accounting object to system: " << e.what() << std::endl;
}
}
```
Cấp phát đối tượng:
struct nfacct *acct = nfacct_alloc();
Dùng để cấp phát bộ nhớ cho đối tượng kế toán.
Thiết lập các thuộc tính chính:
Các thuộc tính như Name, Bytes, Flag, Pkt, và Quota được thiết lập cho đối tượng kế toán.
Thêm đối tượng vào danh sách:
objects.push_back(acct);
Thiết lập thông điệp Netlink:
Xây dựng và gửi thông điệp Netlink đến kernel để thêm đối tượng kế toán.
Nhận phản hồi từ kernel:
Nhận và xử lý phản hồi từ kernel để xác nhận việc thêm đối tượng thành công.
Đóng socket Netlink:
mnl_socket_close(nl);
Xóa Tài Khoản (Delete nfacct):
Code: delNfacct
```
void removeAccountingObject(std::vector<nfacct*>& objects, const std::string& name) {
bool objectFound = false;
for (auto it = objects.begin(); it != objects.end();) {
nfacct* acct = *it;
if (nfacct_attr_get_str(acct, NFACCT_ATTR_NAME) == name) {
objectFound = true;
std::cout << "Attempting to remove accounting object: " << name << std::endl;
try {
struct mnl_socket* nl = nullptr;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr* nlh = nullptr;
uint32_t portid = 0, seq = 0;
seq = time(nullptr);
nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_DEL, NLM_F_ACK, seq);
nfacct_nlmsg_build_payload(nlh, acct);
// Mở socket Netlink và gửi yêu cầu xóa
nl = mnl_socket_open(NETLINK_NETFILTER);
if (!nl) {
throw std::runtime_error("Failed to open Netlink socket");
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
mnl_socket_close(nl);
throw std::runtime_error("Failed to bind Netlink socket");
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
mnl_socket_close(nl);
throw std::runtime_error("Failed to send Netlink message");
}
// Nhận và xử lý phản hồi từ kernel
int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, seq, portid, nullptr, nullptr);
if (ret <= 0) {
break;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
if (ret == -1) {
mnl_socket_close(nl);
throw std::runtime_error("Error receiving Netlink response");
}
mnl_socket_close(nl);
std::cout << "Successfully removed accounting object: " << name << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error removing accounting object: " << name << " - " << e.what() << std::endl;
}
// Xóa đối tượng khỏi danh sách và giải phóng bộ nhớ
nfacct_free(acct);
it = objects.erase(it);
} else {
++it;
}
}
if (!objectFound) {
std::cerr << "No accounting object found with name: " << name << std::endl;
}
}
```
Các bước xóa tài khoản:
Tìm và xác định đối tượng cần xóa:
Duyệt qua danh sách objects để tìm đối tượng kế toán theo tên.
Thiết lập thông điệp Netlink yêu cầu xóa tài khoản: Tạo thông điệp và gửi yêu cầu xóa tài khoản đến kernel.
Nhận và xử lý phản hồi từ kernel: Lặp qua phản hồi từ kernel và xử lý.
Xóa đối tượng khỏi danh sách:
Sau khi xóa tài khoản, giải phóng bộ nhớ và loại bỏ đối tượng khỏi danh sách objects.
Liệt Kê Tài Khoản (List nfacct):
Code: GetNfacct
```
void NfAcct::get(std::vector<nfacct*>& objects, bool reset) {
char buf[MNL_SOCKET_BUFFER_SIZE * 2];
struct nlmsghdr *nlh;
std::lock_guard<std::mutex> lock(nlMutex);
uint32_t seq = genSeqUnlocked();
nlh = nfacct_nlmsg_build_hdr(buf, reset ? NFNL_MSG_ACCT_GET_CTRZERO : NFNL_MSG_ACCT_GET, NLM_F_DUMP, seq);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) == -1)
throw std::system_error(errno, std::generic_category());
int ret;
while ((ret = mnl_socket_recvfrom(nl, buf, sizeof(buf))) > 0) {
if ((ret = mnl_cb_run(buf, ret, seq, portId, onListening, &objects)) <= 0)
break;
}
if (ret == -1)
throw std::system_error(errno, std::generic_category());
}
```
Hàm get lấy các đối tượng kế toán từ hệ thống và lưu chúng vào vector objects. Thông qua thông điệp Netlink, hệ thống gửi yêu cầu và nhận dữ liệu, sau đó sử dụng callback để xử lý các đối tượng kế toán.
## Demo kết quả và nhận xét
Sử dụng chức năng add các acccount vào hệ thống

Dùng Iptables thêm my_acc1 vào để thống kê lưu lượng của INPUT
sudo iptables -A INPUT -m nfacct --nfacct-name my_acc1

Xem lại lưu lượng bằng code

Bây giờ, thêm hàng loạt tài khoản và thiết lập Iptables để thống kê lưu lượng mạng cho mỗi chain, protocol ...

Rules IPtables:
sudo iptables -A INPUT -m nfacct --nfacct-name my_acc1
sudo iptables -A INPUT -p tcp -m nfacct --nfacct-name minhquan2
sudo iptables -A INPUT -p icmp -m nfacct --nfacct-name minhquan1
sudo iptables -A INPUT -p udp -m nfacct --nfacct-name minhquan3
sudo iptables -A FORWARD -m nfacct --nfacct-name my_acc2
sudo iptables -A OUTPUT -m nfacct --nfacct-name my_acc3



Nhận xét:
Em đã triển khai code và xây dựng API theo Sequence Diagram, code hoạt động tốt, các chức năng cho ra kết quả chính xác rõ ràng, liên kết với netlink. Thống kê của mỗi tài khoản đưa ra là đầy đủ phù hợp với các rules Iptables đã thêm.
Với chức năng sửa tài khoản, nhóm chưa hiểu là nên xóa luôn tài khoản đó nếu sai hay không (chưa triển khai), ngoài ra thì chưa sử dụng flush config của nfacct /usr/sbin/nfacct flush.
Qua nội dung tuần cá nhân đã hiểu cách sử dụng Nfacct, quản lí các account để thống kê lưu lượng mạng, code C++ để tạo các chức năng đã yêu cầu.
Tham Khảo
[SourceCode ](https://git.netfilter.org/nfacct/tree/src/nfacct.c )
[libnetfilter_acct](https://netfilter.org/projects/libnetfilter_acct/doxygen/html/group__nfacct.html )