当前位置: 首页 > news >正文

深入解析:如何通过网络命名空间跟踪单个进程的网络活动(C/C++代码实现)

在 Linux 系统中,网络命名空间(Network Namespaces)是一种强大的功能,它允许系统管理员和开发者隔离网络资源,使得每个命名空间都拥有独立的网络协议栈。这种隔离机制不仅用于容器技术如 Docker,也是网络安全和性能分析的重要工具。是一个利用 Linux 网络命名空间来跟踪单个应用程序网络活动的开源工具,它将跟踪结果保存为 pcap 文件,以便后续使用 Wireshark 或 tshark 等工具进行分析。

网络命名空间的基本概念

在监控和分析网络流的过程中,理解和应用网络命名空间的基本概念是至关重要的。以下是一些关键点,可以帮助你更好地掌握这一技术:

  1. 基本概念
  • 定义:网络命名空间(Network Namespace)是一种内核功能,允许系统创建多个隔离的网络环境,每个网络命名空间都有自己独立的网络栈,包括网络设备、IP地址、路由表等。
  • 作用:通过使用网络命名空间,可以在同一台机器上运行多个进程组,每组进程都认为自己拥有独立的网络资源,从而实现网络资源的隔离和管理。
  1. 创建和管理网络命名空间
  • 创建命令:可以使用 ip netns add <namespace_name> 命令来创建一个新的网络命名空间。
  • 查看和管理:通过 ip netns list 命令可以列出所有已创建的网络命名空间,而 ip netns delete <namespace_name> 则用于删除一个命名空间。
  1. 将网络接口添加到命名空间
  • 虚拟以太网对(veth pair):使用 ip link add veth0 type veth peer name veth1 创建一个虚拟以太网对,然后使用 ip link set veth1 netns <namespace_name> 将其中一个接口添加到指定的网络命名空间。
  • 配置 IP 地址:在添加接口到命名空间后,需要为这些接口配置 IP 地址并启用它们,例如 ip addr add 192.168.1.1/24 dev veth1ip link set dev veth1 up
  1. 跨命名空间通信
  • 使用桥接(bridge):为了实现不同网络命名空间之间的通信,可以创建一个桥接设备,并将各个命名空间中的虚拟接口连接到这个桥上。
  • 配置路由:确保各个网络命名空间能够正确路由数据包,可以通过配置静态路由或使用动态路由协议来实现这一点。
  1. 监控和分析
  • 工具使用:利用 tcpdumpWireshark 等工具可以在特定网络命名空间中捕获和分析流量。
  • 脚本自动化:编写脚本定期捕获和保存网络数据,以便后续分析。

将进程关联到特定网络命名空间

要将进程关联到特定的网络命名空间,可以使用以下步骤:

  1. 首先,确保你已经创建了一个网络命名空间。你可以使用ip netns add <namespace_name>命令来创建一个命名空间。例如,要创建一个名为"my_namespace"的命名空间,可以运行以下命令:

    ip netns add my_namespace
    
  2. 接下来,你需要将一个网络接口(如veth对)添加到该命名空间中。首先,创建一个虚拟以太网对,然后将其连接到命名空间。以下是创建和连接虚拟以太网对的命令示例:

    ip link add veth0 type veth peer name veth1
    ip link set veth1 netns my_namespace
    
  3. 在命名空间内部,你需要配置网络接口并启用它。进入命名空间的命令是ip netns exec <namespace_name> /bin/bash。一旦你进入了命名空间,你可以执行以下命令来配置网络接口:

    ip addr add 192.168.1.2/24 dev veth1
    ip link set dev veth1 up
    
  4. 现在,你可以在命名空间内启动你的进程。你可以使用ip netns exec <namespace_name> <command>来执行命令。例如,要在命名空间内启动一个名为"my_process"的进程,可以运行以下命令:

    ip netns exec my_namespace my_process
    
  5. 最后,你可以使用ps命令查看命名空间内的进程列表。要查看特定命名空间中的进程,可以使用ip -n <namespace_name> ps命令。例如,要查看名为"my_namespace"的命名空间中的进程,可以运行以下命令:

    ip -n my_namespace ps
    

通过以上步骤,你可以将进程关联到特定的网络命名空间,并在该命名空间内进行监控和分析。

网络命名空间的配置

1.创建虚拟网络接口

为了使被跟踪的进程能够与外界通信,我们需要在根命名空间和新的命名空间之间创建一对虚拟网络接口(veth对)。然后,我们可以将其中一个接口配置为新命名空间的默认网关。

2.使用iptables进行NAT

为了允许命名空间中的进程访问外部网络,我们可以使用iptables设置网络地址转换(NAT)。这样,所有从命名空间发出的流量都会被自动转换到根命名空间的IP地址。

3.处理DNS解析

在网络命名空间中,DNS解析可能会遇到问题,因为默认的DNS服务器可能无法访问。nsntrace提供了一个选项–use-public-dns,可以覆盖命名空间内的resolv.conf文件,使用公共DNS服务器进行查询。

深入解析:如何通过网络命名空间跟踪单个进程的网络活动(C/C++代码实现)

利用Linux的网络命名空间来跟踪单个进程的网络活动。它通过创建一个新的网络命名空间,在这个隔离的环境中启动目标进程,并使用libpcap库来捕获该进程产生的所有网络数据包,然后将这些数据包保存到pcap文件中,以便后续分析


int nsntrace_capture_start(const char *iface,const char *filter,FILE *fp);void nsntrace_capture_stop();unsigned long nsntrace_capture_packet_count();void nsntrace_capture_flush();char *nsntrace_capture_default_device();int nsntrace_capture_check_device(char *iface);int nsntrace_net_init(pid_t ns_pid,const char *device,struct nsntrace_if_info *info);int nsntrace_net_deinit(pid_t ns_pid,const char *device,struct nsntrace_if_info *info);int nsntrace_net_ns_init(int use_public_dns,struct nsntrace_if_info *info);int nsntrace_net_ip_forward_enabled();int nsntrace_net_get_if_info(pid_t pid,struct nsntrace_if_info *info);
...const int nsntrace_signals[] = {SIGHUP,SIGINT,SIGABRT,SIGTERM,SIGQUIT,SIGSEGV,
};static void
_nsntrace_handle_signals(void (*handler)(int))
{struct sigaction action = { 0 };int s;action.sa_handler = handler;for (s = 0; s < sizeof(nsntrace_signals) / sizeof(int); s++) {sigaction(nsntrace_signals[s], &action, NULL);}
}static int _nsntrace_unlink_cb(const char *fpath,const struct stat *sb,int typeflag,struct FTW *ftwbuf)
{int rv = remove(fpath);if (rv)perror(fpath);return rv;
}static void
_nsntrace_remove_rundir(pid_t pid)
{char path[PATH_MAX] = { 0, };snprintf(path, PATH_MAX, "%s/%d", NSNTRACE_RUN_DIR, getppid());nftw(path, _nsntrace_unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
}static void _nsntrace_remove_ns()
{char netns_path[PATH_MAX];snprintf(netns_path, sizeof(netns_path), "%s/nsntrace-%d", NETNS_RUN_DIR, getpid());umount2(netns_path, MNT_DETACH);if (unlink(netns_path) < 0) {perror("unlink");}_nsntrace_remove_rundir(getppid());
}static void
_nsntrace_cleanup_ns()
{kill(child_pid, SIGTERM);waitpid(child_pid, NULL, 0);_nsntrace_remove_ns();fprintf(where, "Finished capturing %lu packets.\n",nsntrace_capture_packet_count());nsntrace_capture_flush();
}static void
_nsntrace_cleanup_ns_signal_callback()
{_nsntrace_cleanup_ns();exit(EXIT_SUCCESS);
}static void
_nsntrace_cleanup() {wait(NULL);
}static void
_nsntrace_set_name()
{
.../* 如果基本netns目录不存在,则创建它 */if ((ret = mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) < 0) {if (errno != EEXIST) {goto out;}}snprintf(netns_path, PATH_MAX, "%s/nsntrace-%d", NETNS_RUN_DIR, getpid());fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0);if (fd < 0) {ret = fd;goto out;}close(fd);ret = mount(PROC_SELF_NETNS_PATH, netns_path, "none", MS_BIND, NULL);
out:if (ret < 0) {fprintf(stderr, "failed to set namespace name\n");}
}static void
_nsntrace_start_tracer(struct nsntrace_options *options,struct nsntrace_if_info *info)
{int ret;/** 如果outfile是一个单破折号(“-”),用户希望我们输出到STDOUT*/FILE *fp;if (options->outfile[0] == '-' && options->outfile[1] == '\0') {where = stderr;fp = stdout;} else {where = stdout;fp = fopen(options->outfile, "w");if (!fp) {perror("fopen");exit(EXIT_FAILURE);}}fprintf(where, "Starting network trace of '%s' on interface %s.\n""Your IP address in this trace is %s.\n""Use ctrl-c to end at any time.\n\n",options->args[0], options->device, info->ns_ip);ret = nsntrace_capture_start(info->ns_if, options->filter, fp);if (ret != 0) {exit(ret);}
}static void
_nsntrace_start_tracee(struct nsntrace_options *options)
{
...if (sscanf(options->user, "%i", &uid) == 1) {pwd = getpwuid(uid);} else {pwd = getpwnam(options->user);}if (!pwd) {fprintf(stderr,"Cannot find user '%s'\n",options->user);exit(EXIT_FAILURE);}uid = pwd->pw_uid;gid = pwd->pw_gid;setenv("HOME", pwd->pw_dir, 1);setenv("USER", pwd->pw_name, 1);setenv("USERNAME", pwd->pw_name, 1);if (initgroups(options->user, gid) < 0) {fprintf(stderr, "Failed to initialize groups\n");exit(EXIT_FAILURE);}} else {uid = getuid();gid = getgid();}if (setgid(gid) < 0) {fprintf(stderr, "Unable to set process group ID\n");exit(EXIT_FAILURE);}if (setuid(uid) < 0) {fprintf(stderr, "Unable to set process user ID\n");exit(EXIT_FAILURE);}/** 只在STDOUT上输出PCAP数据*/if (options->outfile[0] == '-' && options->outfile[1] == '\0') {dup2(STDERR_FILENO, STDOUT_FILENO);}/* 启动要跟踪的应用程序 */if (execvp(options->args[0], options->args) < 0) {fprintf(stderr, "Unable to start '%s'\n", options->args[0]);exit(EXIT_FAILURE);}
}static int
netns_main(void *arg) {
...if (nsntrace_net_ns_init(options->use_public_dns, &common->if_info) < 0) {fprintf(stderr, "failed to setup network environment\n");return EXIT_FAILURE;}_nsntrace_handle_signals(_nsntrace_cleanup_ns_signal_callback);_nsntrace_start_tracer(common->options, &common->if_info);_nsntrace_set_name();child_pid = fork();if (child_pid < 0) {return EXIT_FAILURE;} else if (child_pid > 0) { /* parent - tracer */struct timespec timeout = { 0 };waitpid(child_pid, &status, 0);if (WIFEXITED(status)) {ret = WEXITSTATUS(status);} else {ret = EXIT_FAILURE;}/** 睡眠,以便处理所有数据包。*/timeout.tv_sec = 2; /* 2 seconds timeout */nanosleep(&timeout, NULL);nsntrace_capture_stop();/* 退出捕获循环,清理 */_nsntrace_cleanup_ns();exit(ret);} else { _nsntrace_start_tracee(common->options);}return ret;
}...
static void
_nsntrace_parse_options(struct nsntrace_options *options,int argc, char **argv)
{int c;opterr = 0;while ((c = getopt_long(argc, argv, short_opt, long_opt, NULL)) > 0) {switch(c) {case -1:case 0:break;case 'o':options->outfile = strdup(optarg);break;case 'd':options->device = strdup(optarg);break;case 'u':options->user = strdup(optarg);break;case 'f':options->filter = strdup(optarg);break;case PUBLIC_DNS:options->use_public_dns = 1;break;case 'h':_nsntrace_usage();exit(EXIT_SUCCESS);break;default:fprintf(stderr, "Invalid option '%c'\n", c);_nsntrace_usage();exit(EXIT_FAILURE);}}if (!options->device) {options->device = nsntrace_capture_default_device();}if (!options->outfile) {options->outfile = strdup(DEFAULT_OUTFILE);}options->args = argv + optind; /* 解析选项后的参数 */if (!options->args[0]) {_nsntrace_usage();exit(EXIT_FAILURE);}
}/*
创建基于pid的运行目录*/
static void
_nsntrace_mkrundir()
{char path[PATH_MAX] = { 0, };if (mkdir(NSNTRACE_RUN_DIR, 0644) < 0) {if (errno != EEXIST) {perror("mkdir");}}snprintf(path, PATH_MAX, "%s/%d", NSNTRACE_RUN_DIR, getpid());if (mkdir(path, 0644) < 0) {if (errno != EEXIST) {perror("mkdir");}}
}int
main(int argc, char **argv)
{
..._nsntrace_parse_options(&options, argc, argv);/* geteuid()返回有效的用户ID,如果是root,则返回0 */if (geteuid() != 0) {fprintf(stderr,"You need root privileges to run this application\n");exit(EXIT_FAILURE);}if (!nsntrace_net_ip_forward_enabled()) {fprintf(stderr, "Please enable IP forwarding:\n");if( NULL == getenv("SUDO_UID") ) {fprintf(stderr, "# sysctl net.ipv4.ip_forward=1\n");} else {fprintf(stderr, "$ sudo sysctl net.ipv4.ip_forward=1\n");}exit(EXIT_FAILURE);}_nsntrace_mkrundir();if (nsntrace_net_get_if_info(getpid(), &common.if_info) < 0) {ret = EXIT_FAILURE;goto out;}/* 在一个新的网络名称空间中创建一个新进程 */pid = clone(netns_main, child_stack + STACK_SIZE,CLONE_NEWNET | SIGCHLD, &common);if (pid < 0) {perror("clone");ret = EXIT_FAILURE;goto out;}.../* 在此处等待,直到跟踪的进程存在或用户中止 */waitpid(pid, &status, 0);if (WIFEXITED(status)) {ret = WEXITSTATUS(status);} else {ret = EXIT_FAILURE;}out:if (pid != 0) {nsntrace_net_deinit(pid, options.device, &common.if_info);}_nsntrace_remove_rundir(getpid());return ret;
}

If you need the complete source code, please add the WeChat number (c17865354792)

$ sudo my_nsntrace -d eth1 wget www.google.com
Starting network trace of 'wget' on interface eth1.
Your IP address in this trace is 172.16.42.255.
Use ctrl-c to end at any time.--2024-07-15 12:12:17--  http://www.google.com/
Location: http://www.google.se/?gfe_rd=cr&ei=AbeIV5zZHcaq8wfTlrjgCA [following]
--2024-07-15 12:12:17--  http://www.google.se/?gfe_rd=cr&ei=AbeIV5zZHcaq8wfTlrjgCA
Length: unspecified [text/html]
Saving to: ‘index.html’index.html                                         [ <=>                                                                                                   ]  10.72K  --.-KB/s   in 0.001s2024-07-15 12:12:17 (15.3 MB/s) - ‘index.html’ saved [10980]Finished capturing 42 packets.$ tshark -r my_nsntrace.pcap -Y 'http.response or http.request'
16   0.998839 172.16.42.255 -> 195.249.146.104    HTTP 229 GET http://www.google.com/ HTTP/1.1
20   1.010671    195.249.146.104 -> 172.16.42.255 HTTP 324 HTTP/1.1 302 Moved Temporarily  (text/html)
22   1.010898 172.16.42.255 -> 195.249.146.104    HTTP 263 GET http://www.google.se/?gfe_rd=cr&ei=AbeIV5zZHcaq8wfTlrjgCA HTTP/1.1
31   1.051006    195.249.146.104 -> 172.16.42.255 HTTP 71 HTTP/1.1 200 OK  (text/html)

使用tshark进行实时捕捉

$ sudo my_nsntrace -f tcp -o - wget www.google.com  2> /dev/null | tshark -r -
1   0.000000 172.16.42.255 → 142.250.74.36 TCP 74 5108880 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=1362504482 TSecr=0 WS=128
2   0.014010 142.250.74.36 → 172.16.42.255 TCP 74 8051088 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1430 SACK_PERM=1 TSval=2846449454 Secr=1362504482 WS=256
3   0.014078 172.16.42.255 → 142.250.74.36 TCP 66 5108880 [ACK] Seq=1 Ack=1 Win=64256 Len=0 TSval=1362504496 TSecr=2846449454
4   0.014221 172.16.42.255 → 142.250.74.36 HTTP 207 GET / HTTP/1.15   0.033935 142.250.74.36 → 172.16.42.255 TCP 66 8051088 [ACK] Seq=1 Ack=142 Win=66816 Len=0 TSval=2846449475 TSecr=1362504496
6   0.093989 142.250.74.36 → 172.16.42.255 TCP 1484 HTTP/1.1 200 OK  [TCP segment of a reassembled PDU]
7   0.094022 172.16.42.255 → 142.250.74.36 TCP 66 5108880 [ACK] Seq=142 Ack=1419 Win=63360 Len=0 TSval=1362504576 TSecr=2846449532
8   0.096447 142.250.74.36 → 172.16.42.255 TCP 2902 HTTP/1.1 200 OK  [TCP segment of a reassembled PDU]
9   0.096478 172.16.42.255 → 142.250.74.36 TCP 66 5108880 [ACK] Seq=142 Ack=4255 Win=62208 Len=0 TSval=1362504578 TSecr=2846449532
10   0.099871 142.250.74.36 → 172.16.42.255 HTTP 9626 Continuation[Packet size limited during capture]
11   0.099936 172.16.42.255 → 142.250.74.36 TCP 66 5108880 [ACK] Seq=142 Ack=13815 Win=56320 Len=0 TSval=1362504582 TSecr=2846449532
12   0.100743 172.16.42.255 → 142.250.74.36 TCP 66 5108880 [FIN, ACK] Seq=142 Ack=13815 Win=64128 Len=0 TSval=1362504583 TSecr=2846449532
13   0.113167 142.250.74.36 → 172.16.42.255 TCP 66 8051088 [FIN, ACK] Seq=13815 Ack=143 Win=66816 Len=0 TSval=2846449554 TSecr=1362504583
14   0.113190 172.16.42.255 → 142.250.74.36 TCP 66 5108880 [ACK] Seq=143 Ack=13816 Win=64128 Len=0 TSval=1362504595 TSecr=2846449554

my_nsntrace 应用程序使用 clone 系统调用创建一个新的网络命名空间(CLONE_NEWNET),并在该命名空间中启动目标进程,同时使用 libpcap 开始跟踪。这样可以确保所有跟踪到的数据包都来自该进程。

由于进程在命名空间中是隔离的,无法直接访问其他网络,因此 my_nsntrace 通过创建虚拟网络接口来解决这个问题。其中一个虚拟网络接口保留在根网络命名空间中,另一个放在新创建的命名空间中。然后,将根命名空间中的虚拟设备设置为跟踪命名空间中虚拟设备的默认网关。

为了确保能够访问目标网络,my_nsntrace 使用 iptables 和 NAT 技术将虚拟设备的所有流量转发到默认的网络接口。这样,就可以在不影响系统其他部分的情况下,捕获单个进程与默认网络通信时的数据包。

总结

通过网络命名空间跟踪单个进程的网络活动是一种高级网络监控技术,它允许系统管理员和网络安全专家深入分析特定应用程序或服务的网络行为。通过创建隔离的网络环境并将目标进程移入该环境,我们可以精确地捕捉到该进程生成的所有网络流量,而不受其他进程的干扰。

在实际操作中,这通常涉及使用ip netns命令来管理网络命名空间,利用nsenter工具将进程附加到特定的网络命名空间,并在该命名空间内部署如tcpdump或iftop等网络嗅探工具以捕获数据包。通过这种方式,我们能够获得关于进程通信模式、数据流大小、连接尝试等详细信息,从而为进一步的分析提供坚实的基础。

We also undertake the development of program requirements here. If necessary, please follow the WeChat official account 【程序猿编码】and contact me

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • PostgreSQL的walsender和walreceiver进程介绍
  • ubuntu20.04/22.04/24.04 docker 容器安装方法
  • 借助大模型将文档转换为视频
  • 【测试开岗面试】知识点总结
  • JDBC笔记
  • UE5源码Windows编译、运行
  • 办了房屋抵押经营贷,空壳公司不怕被查吗?续贷不上怎么办?
  • Chrome谷歌浏览器登录账号next无反应
  • Renesas R7FA8D1BH (Cortex®-M85)控制SHT20
  • win+linux平台C语言获取进程的线程数量
  • 稠密向量检索、稀疏向量检索、BM25检索三者对比
  • 【Java】【力扣】83.删除排序链表中的重复元素
  • SAM 2: Segment Anything in Images and Videos
  • Android 车联网——汽车模块介绍(附1)
  • Vue3.5+ 侦听器的3个更新
  • 【162天】黑马程序员27天视频学习笔记【Day02-上】
  • android 一些 utils
  • Android组件 - 收藏集 - 掘金
  • Cumulo 的 ClojureScript 模块已经成型
  • Idea+maven+scala构建包并在spark on yarn 运行
  • js ES6 求数组的交集,并集,还有差集
  • js正则,这点儿就够用了
  • MySQL数据库运维之数据恢复
  • Odoo domain写法及运用
  • passportjs 源码分析
  • Spark RDD学习: aggregate函数
  • STAR法则
  • vue2.0项目引入element-ui
  • Zepto.js源码学习之二
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 如何打造100亿SDK累计覆盖量的大数据系统
  • 入门到放弃node系列之Hello Word篇
  • 系统认识JavaScript正则表达式
  • 线性表及其算法(java实现)
  • 一个项目push到多个远程Git仓库
  • - 转 Ext2.0 form使用实例
  • 自制字幕遮挡器
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • ​数据结构之初始二叉树(3)
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (c语言版)滑动窗口 给定一个字符串,只包含字母和数字,按要求找出字符串中的最长(连续)子串的长度
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (附源码)php新闻发布平台 毕业设计 141646
  • (完整代码)R语言中利用SVM-RFE机器学习算法筛选关键因子
  • (学习总结16)C++模版2
  • (转载)Linux网络编程入门
  • (轉)JSON.stringify 语法实例讲解
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)
  • .Net 应用中使用dot trace进行性能诊断
  • .net利用SQLBulkCopy进行数据库之间的大批量数据传递
  • @JsonFormat 和 @DateTimeFormat 的区别
  • [ C++ ] STL---string类的模拟实现
  • [1]从概念到实践:电商智能助手在AI Agent技术驱动下的落地实战案例深度剖析(AI Agent技术打造个性化、智能化的用户助手)