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

带你手把手解读rejail沙盒源码(0.9.72版本) (七) fnetfilter

├── fnetfilter
│   ├── Makefile
│   └── main.c

文章目录

    • main.c
      • usage
      • err_exit_cannot_open_file
      • copy
      • process_template
      • main函数
    • 样例
      • 程序功能

main.c


#include "../include/common.h"#define MAXBUF 4098
#define MAXARGS 16
static char *args[MAXARGS] = {0};
static int argcnt = 0;
int arg_quiet = 0;static char *default_filter =
"*filter\n"
":INPUT DROP [0:0]\n"
":FORWARD DROP [0:0]\n"
":OUTPUT ACCEPT [0:0]\n"
"-A INPUT -i lo -j ACCEPT\n"
"-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
"# echo replay is handled by -m state RELATED/ESTABLISHED above\n"
"#-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT\n"
"-A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT\n"
"-A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT\n"
"-A INPUT -p icmp --icmp-type echo-request -j ACCEPT \n"
"# disable STUN\n"
"-A OUTPUT -p udp --dport 3478 -j DROP\n"
"-A OUTPUT -p udp --dport 3479 -j DROP\n"
"-A OUTPUT -p tcp --dport 3478 -j DROP\n"
"-A OUTPUT -p tcp --dport 3479 -j DROP\n"
"COMMIT\n";static void usage(void) {printf("Usage:\n");printf("\tfnetfilter netfilter-command destination-file\n");
}static void err_exit_cannot_open_file(const char *fname) {fprintf(stderr, "Error fnetfilter: cannot open %s\n", fname);exit(1);
}static void copy(const char *src, const char *dest) {FILE *fp1 = fopen(src, "r");if (!fp1)err_exit_cannot_open_file(src);FILE *fp2 = fopen(dest, "w");if (!fp2)err_exit_cannot_open_file(dest);char buf[MAXBUF];while (fgets(buf, MAXBUF, fp1))fprintf(fp2, "%s", buf);fclose(fp1);fclose(fp2);
}static void process_template(char *src, const char *dest) {char *arg_start = strchr(src, ',');assert(arg_start);*arg_start = '\0';arg_start++;if (*arg_start == '\0') {fprintf(stderr, "Error fnetfilter: you need to provide at least one argument\n");exit(1);}// extract the arguments from command linechar *token = strtok(arg_start, ",");while (token) {if (argcnt == MAXARGS) {fprintf(stderr, "Error fnetfilter: only up to %u arguments are supported\n", (unsigned) MAXARGS);exit(1);}// look for abnormal thingsint len = strlen(token);if (strcspn(token, "\\&!?\"'<>%^(){};,*[]") != (size_t)len) {fprintf(stderr, "Error fnetfilter: invalid argument in netfilter command\n");exit(1);}args[argcnt] = token;argcnt++;token = strtok(NULL, ",");}
#if 0
{
printf("argcnt %d\n", argcnt);
int i;
for (i = 0; i < argcnt; i++)printf("%s\n", args[i]);
}
#endif// open the filesFILE *fp1 = fopen(src, "r");if (!fp1)err_exit_cannot_open_file(src);FILE *fp2 = fopen(dest, "w");if (!fp2)err_exit_cannot_open_file(dest);int line = 0;char buf[MAXBUF];while (fgets(buf, MAXBUF, fp1)) {line++;char *ptr = buf;while (*ptr != '\0') {if (*ptr != '$')fputc(*ptr, fp2);else {// parsingint index = 0;int rv = sscanf(ptr, "$ARG%d", &index) ;if (rv != 1) {fprintf(stderr, "Error fnetfilter: invalid template argument on line %d\n", line);exit(1);}// print argumentif (index < 1 || index > argcnt) {fprintf(stderr, "Error fnetfilter: $ARG%d on line %d was not defined\n", index, line);exit(1);}fprintf(fp2, "%s", args[index - 1]);// march to the end of argumentptr += 4;while (isdigit(*ptr))ptr++;ptr--;}ptr++;}}fclose(fp1);fclose(fp2);
}int main(int argc, char **argv) {
#if 0
{
system("cat /proc/self/status");
int i;
for (i = 0; i < argc; i++)printf("*%s* ", argv[i]);
printf("\n");
}
#endifchar *quiet = getenv("FIREJAIL_QUIET");if (quiet && strcmp(quiet, "yes") == 0)arg_quiet = 1;if (argc > 1 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0)) {usage();return 0;}if (argc != 2 && argc != 3) {usage();return 1;}warn_dumpable();char *destfile = (argc == 3)? argv[2]: argv[1];char *command = (argc == 3)? argv[1]: NULL;
//printf("command %s\n", command);
//printf("destfile %s\n", destfile);// destfile is a real filenamereject_meta_chars(destfile, 0);// handle default config (command = NULL, destfile)if (command == NULL) {// create a default filter fileFILE *fp = fopen(destfile, "w");if (!fp)err_exit_cannot_open_file(destfile);fprintf(fp, "%s\n", default_filter);fclose(fp);}else {if (strrchr(command, ','))process_template(command, destfile);elsecopy(command, destfile);}return 0;
}

这是一个iptables的防火墙规则脚本,下面是每一行的解释:

  • *filter:这是定义过滤表的开始,iptables有多个表,filter是默认表,主要用于过滤数据包。
  • :INPUT DROP [0:0]:设置默认策略,所有进入(INPUT)的数据包默认丢弃(DROP),除非有规则明确允许。[0:0]是计数器,表示匹配到这条规则的数据包和字节数,初始为0。
  • :FORWARD DROP [0:0]:所有转发(FORWARD)的数据包默认丢弃。
  • :OUTPUT ACCEPT [0:0]:所有出去(OUTPUT)的数据包默认接受(ACCEPT)。
  • -A INPUT -i lo -j ACCEPT:允许来自本地回环(lo)接口的数据包。
  • -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT:允许所有已经建立连接或者相关的数据包。
  • -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT:允许ICMP目的地不可达的数据包。
  • -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT:允许ICMP超时的数据包。
  • -A INPUT -p icmp --icmp-type echo-request -j ACCEPT:允许ICMP回显请求的数据包,即允许ping请求。
  • -A OUTPUT -p udp --dport 3478 -j DROP:禁止向外发送目的端口为3478的UDP数据包。
  • -A OUTPUT -p udp --dport 3479 -j DROP:禁止向外发送目的端口为3479的UDP数据包。
  • -A OUTPUT -p tcp --dport 3478 -j DROP:禁止向外发送目的端口为3478的TCP数据包。
  • -A OUTPUT -p tcp --dport 3479 -j DROP:禁止向外发送目的端口为3479的TCP数据包。
  • COMMIT:提交以上规则,使其生效。

这个脚本的主要作用是默认拒绝所有进入和转发的数据包,但允许所有出去的数据包和已经建立的连接。同时,它还允许ICMP和本地回环接口的数据包,但禁止了向外发送到某些特定端口的数据包。这些特定端口通常用于STUN协议,用于NAT穿透。所以这个脚本可能是为了禁止STUN协议而编写的。

usage

  1. static void usage(void) {:声明一个名为usage的静态函数,它没有参数也没有返回值。
  2. printf("Usage:\n");:在标准输出(通常是终端)上打印字符串"Usage:"和换行符。
  3. printf("\tfnetfilter netfilter-command destination-file\n");:在标准输出上打印制表符、字符串"fnetfilter netfilter-command destination-file"和换行符。这行代码用于显示命令的使用方式。
  4. }:结束usage函数。

err_exit_cannot_open_file

  1. static void err_exit_cannot_open_file(const char *fname) {:声明一个名为err_exit_cannot_open_file的静态函数,它接受一个指向字符常量的指针作为参数,并且没有返回值。
  2. fprintf(stderr, "Error fnetfilter: cannot open %s\n", fname);:使用fprintf函数将错误信息(包含文件名fname)写入标准错误输出(通常是终端)。
  3. exit(1);:调用exit函数,使程序立即终止,返回值为1,通常表示出现了错误。
  4. }:结束err_exit_cannot_open_file函数。

copy

  1. static void copy(const char *src, const char *dest) {:声明一个名为copy的静态函数,它接受两个指向字符常量的指针作为参数,并且没有返回值。
  2. FILE *fp1 = fopen(src, "r");:使用fopen函数打开名为src的文件以读取模式。如果文件无法打开,则返回NULL。
  3. if (!fp1) err_exit_cannot_open_file(src);:检查fp1是否为NULL。如果是,则调用err_exit_cannot_open_file函数并传入src作为参数,该函数会打印错误消息并退出程序。
  4. FILE *fp2 = fopen(dest, "w");:使用fopen函数打开名为dest的文件以写入模式。如果文件无法打开,则返回NULL。
  5. if (!fp2) err_exit_cannot_open_file(dest);:检查fp2是否为NULL。如果是,则调用err_exit_cannot_open_file函数并传入dest作为参数,该函数会打印错误消息并退出程序。
  6. char buf[MAXBUF];:定义一个大小为MAXBUF的字符数组buf,用于存储从源文件中读取的内容。
  7. while (fgets(buf, MAXBUF, fp1)) fprintf(fp2, "%s", buf);:使用fgets函数从fp1(源文件)中读取一行内容到buf数组中,直到到达文件末尾或读取到错误。然后使用fprintf函数将buf中的内容写入fp2(目标文件)。这个循环会一直重复,直到读取完源文件的所有内容。
  8. fclose(fp1);:使用fclose函数关闭fp1文件流。
  9. fclose(fp2);:使用fclose函数关闭fp2文件流。
  10. }:结束copy函数。

process_template

当然,我会尽力解释这个函数中的每一行代码。这个函数是用C语言编写的,主要用于处理模板文件。

static void process_template(char *src, const char *dest) {

这是函数的声明,函数名为 process_template,它接受两个参数,srcdest,分别代表源文件和目标文件的路径。

	char *arg_start = strchr(src, ',');

这行代码在 src 字符串中查找第一个逗号 , 的位置,并将其地址赋值给 arg_start

	assert(arg_start);

这行代码使用 assert 函数来确保 arg_start 不为 NULL。如果 arg_startNULL,则程序会打印错误信息并终止执行。

	*arg_start = '\0';

这行代码将 arg_start 指向的字符替换为结束符 \0,从而将 src 字符串分割为两部分。

	arg_start++;

这行代码将 arg_start 指针向后移动一位,使其指向参数列表的开始位置。

	if (*arg_start == '\0') {fprintf(stderr, "Error fnetfilter: you need to provide at least one argument\n");exit(1);}

这段代码检查是否提供了至少一个参数。如果没有提供任何参数,程序会打印错误信息并退出。

当然,让我们通过一个具体的例子来解释这个过程。

假设我们有一个字符串 src,它的内容是 "template,arg1,arg2,arg3"。这个字符串包含一个模板名 "template" 和三个参数 "arg1", "arg2", "arg3",它们之间用逗号 , 分隔。

当我们执行 char *arg_start = strchr(src, ','); 这行代码时,arg_start 指针会指向 src 中第一个逗号 , 的位置。所以,arg_start 现在指向的是字符串 ",arg1,arg2,arg3"

然后,当我们执行 *arg_start = '\0'; 这行代码时,逗号 , 被替换为了结束符 \0。所以,src 现在变成了两个字符串:"template""arg1,arg2,arg3"

最后,当我们执行 arg_start++; 这行代码时,arg_start 指针向后移动了一位,跳过了刚刚插入的结束符 \0,从而指向了 "arg1,arg2,arg3"

所以,通过这两行代码,我们将 src 字符串分割为了两部分,并且得到了参数列表的开始位置。

	char *token = strtok(arg_start, ",");

这行代码使用 strtok 函数来分割参数列表,分隔符为逗号 ,

	while (token) {

这是一个 while 循环,只要 token 不为 NULL,循环就会继续。

		if (argcnt == MAXARGS) {fprintf(stderr, "Error fnetfilter: only up to %u arguments are supported\n", (unsigned) MAXARGS);exit(1);}

这段代码检查参数的数量是否超过了最大限制 MAXARGS。如果超过了,程序会打印错误信息并退出。

		int len = strlen(token);if (strcspn(token, "\\&!?\"'<>%^(){};,*[]") != (size_t)len) {fprintf(stderr, "Error fnetfilter: invalid argument in netfilter command\n");exit(1);}

这段代码检查参数是否包含任何非法字符。如果包含,程序会打印错误信息并退出。

strcspn是C编程语言中的一个函数,用于查找第一个字符串(称为“源字符串”)中不包含第二个字符串(称为“拒绝字符串”)中的任何字符的初始子串的长度。

strcspn函数的语法如下:

size_t strcspn(const char *s1, const char *s2);

其中:

  • s1 是源字符串。
  • s2 是拒绝字符串。
  • 函数返回s1中不包含s2中任何字符的初始子串的长度。

例如,考虑以下代码:

#include <stdio.h>
#include <string.h>int main() {char str1[] = "Hello, World!";char str2[] = "World";size_t result = strcspn(str1, str2);printf("不含str2中字符的str1的初始子串的长度为: %zu\n", result);return 0;
}

在这个例子中,strcspn函数用于查找不包含str2中任何字符的str1的初始子串的长度。程序的输出将是:

不含str2中字符的str1的初始子串的长度为: 6

这是因为str1的前6个字符("Hello, ")不在str2中出现。

		args[argcnt] = token;argcnt++;

这两行代码将参数添加到参数数组 args 中,并将参数计数器 argcnt 加一。

		token = strtok(NULL, ",");}

这行代码获取下一个参数。while 循环在此结束。

strtok是C编程语言中的一个函数,用于将字符串分割成一个个标记(token)。这个函数在处理分隔符时非常有用,例如逗号、空格或其他特定字符。

在你的代码片段中:

token = strtok(NULL, ",");

这是对strtok函数的第二次或后续调用。第一次调用strtok需要提供要分割的原始字符串和作为分隔符的字符数组。例如:

char str[] = "apple,banana,grape";
token = strtok(str, ",");

在这个例子中,str是要分割的原始字符串,,是分隔符。首次调用strtok后,它会返回第一个标记(在这种情况下是"apple"),并将内部状态信息保存下来,以便后续调用。

后续的strtok调用应该传入NULL作为第一个参数,表示要继续处理上次调用留下的剩余字符串。第二个参数仍然是分隔符字符数组。所以:

token = strtok(NULL, ",");

这行代码表示从上次分割的位置开始,继续查找以逗号分隔的下一个标记。这次调用可能会返回"banana",然后再下一次调用会返回"grape",直到没有更多的标记可以找到为止。

	FILE *fp1 = fopen(src, "r");if (!fp1)err_exit_cannot_open_file(src);

这两行代码尝试打开源文件。如果无法打开,程序会打印错误信息并退出。

	FILE *fp2 = fopen(dest, "w");if (!fp2)err_exit_cannot_open_file(dest);

这两行代码尝试打开目标文件。如果无法打开,程序会打印错误信息并退出。

	int line = 0;char buf[MAXBUF];while (fgets(buf, MAXBUF, fp1)) {

这是一个 while 循环,用于逐行读取源文件。

		line++;

这行代码将行号加一。

		char *ptr = buf;while (*ptr != '\0') {

这是一个 while 循环,用于处理当前行的每一个字符。

			if (*ptr != '$')fputc(*ptr, fp2);

这两行代码检查当前字符是否为 $。如果不是,就将其写入到目标文件中。

			else {int index = 0;int rv = sscanf(ptr, "$ARG%d", &index) ;if (rv != 1) {fprintf(stderr, "Error fnetfilter: invalid template argument on line %d\n", line);exit(1);}

这段代码尝试解析 $ARG 标记。如果解析失败,程序会打印错误信息并退出。

这段代码的作用是在处理模板文件时解析和提取 $ARGn 形式的参数占位符。

  1. 初始化 index 变量为0,这个变量将用于存储提取的参数索引值。
  2. 使用 sscanf() 函数尝试从 ptr 指针指向的字符串中解析格式为 $ARG%d 的参数。&index 是一个输出参数,将存储解析出的整数值。
  3. sscanf() 函数返回实际成功赋值的参数数量。在这里,我们期望它返回1,表示成功解析了一个整数。
  4. 如果 rv 不等于1,说明没有成功解析出一个整数或者解析了多个整数,这与我们期望的 $ARGn 格式不符。在这种情况下,程序通过 fprintf() 函数向标准错误输出打印一条错误消息,指出在哪一行出现了无效的模板参数,并使用 exit(1) 函数终止程序执行,表示出现了错误。

这段代码的主要目的是确保模板文件中的参数占位符 $ARGn 符合预期的格式,以便后续正确替换为实际的参数值。

				if (index < 1 || index > argcnt) {fprintf(stderr, "Error fnetfilter: $ARG%d on line %d was not defined\n", index, line);exit(1);}

这段代码检查 $ARG 标记的索引是否有效。如果无效,程序会打印错误信息并退出。

				fprintf(fp2, "%s", args[index - 1]);

这行代码将 $ARG 标记替换为相应的参数,并写入到目标文件中。

				ptr += 4;while (isdigit(*ptr))ptr++;ptr--;}ptr++;}}

这段代码将指针移动到当前行的下一个字符。

	fclose(fp1);fclose(fp2);
}

这两行代码关闭源文件和目标文件,函数在此结束。希望这个解释对你有所帮助!

main函数

这段代码是用C语言编写的,主要功能是在给定命令和目标文件的情况下,根据特定条件处理它们。以下是逐行解释:

  1. int main(int argc, char **argv):这是程序的主入口点,参数argc表示命令行参数的数量,argv是一个包含所有参数的字符指针数组。

  2. #if 0:这是一个预处理器指令,用于注释掉下面的代码块(在}之前)。

3-4. system("cat /proc/self/status");:运行系统命令cat /proc/self/status以显示进程状态信息。

5-6. int i; for (i = 0; i < argc; i++):声明一个整数变量i并使用它遍历argc中的每个参数。

  1. printf("*%s* ", argv[i]);:打印出每个参数,将其括在星号(*)之间,并在末尾添加空格。

  2. printf("\n");:换行。

  3. #endif:结束预处理器指令。

  4. char *quiet = getenv("FIREJAIL_QUIET");:从环境变量中获取FIREJAIL_QUIET的值,并将其赋给字符指针quiet

  5. if (quiet && strcmp(quiet, "yes") == 0) arg_quiet = 1;:如果quiet不为空且等于字符串"yes",则将arg_quiet设置为1。

12-14. 检查命令行参数是否包含帮助选项(如-h、–help或-?),如果是,则输出帮助信息并返回0。

15-16. 如果参数数量不是2或3,则输出错误消息并返回1。

  1. 调用warn_dumpable()函数。
void warn_dumpable(void) {if (getuid() != 0 && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 1 && getenv("FIREJAIL_PLUGIN")) {fprintf(stderr, "Error: dumpable process\n");// best effort to provide detailed debug information// cannot use process name, it is just a file descriptor numberchar path[BUFLEN];ssize_t len = readlink("/proc/self/exe", path, BUFLEN - 1);if (len < 0)return;path[len] = '\0';// path can refer to a sandbox mount namespace, use basename onlyconst char *base = gnu_basename(path);struct stat s;if (stat("/proc/self/exe", &s) == 0 && s.st_uid != 0)fprintf(stderr, "Change owner of %s executable to root\n", base);else if (access("/proc/self/exe", R_OK) == 0)fprintf(stderr, "Remove read permission on %s executable\n", base);}
}

此文件来自 src\lib\common.c

这段代码是一个C语言函数,名为warn_dumpable,其功能是检查当前进程是否可被核心转储(dumpable),并根据需要提供调试信息和警告。

函数执行以下操作:

  1. 使用getuid函数检查当前进程的用户ID是否为0(即root用户)。如果不是,则继续后续检查。
  2. 调用prctl函数,参数为PR_GET_DUMPABLE,用于获取当前进程的dumpable状态。如果返回值为1,表示进程是可dumpable的。
  3. 检查环境变量FIREJAIL_PLUGIN是否存在。如果存在,则继续后续警告和调试信息输出。
  4. 输出错误信息到标准错误流(stderr):“Error: dumpable process”。
  5. 努力提供详细的调试信息:
    a. 使用readlink函数从/proc/self/exe读取当前进程的可执行文件路径,并将其存储在path缓冲区中。
    b. 如果读取失败,则返回。
    c. 使用GNU的basename函数从path中提取出基名(不含路径的部分),并存储在base变量中。
  6. 使用stat函数获取/proc/self/exe的文件状态信息,并检查其所有者用户ID是否为0(即root用户)。如果不是,则输出警告信息,建议将可执行文件的所有者改为root用户。
  7. 如果上述stat函数调用失败或所有者已经是root用户,使用access函数检查当前用户是否有读取/proc/self/exe的权限。如果有,则输出警告信息,建议移除对可执行文件的读取权限。

总的来说,这个函数主要用于在非root用户运行的进程中检测dumpable状态,并在必要时提供警告和调试信息,以增强系统的安全性。

======================================================================================

"是否可被核心转储"指的是一个进程或者系统在遇到严重错误或崩溃时,是否允许操作系统生成核心转储文件。核心转储文件是一个包含进程在崩溃时刻内存状态的详细快照,包括程序数据、堆栈信息、打开的文件描述符、内存映射等。如果一个进程是"可被核心转储"的,那么当它因为某种原因异常终止时,操作系统会自动或根据配置生成一个核心转储文件。这个文件对于调试和分析程序崩溃的原因非常有用,因为它提供了崩溃瞬间程序内部状态的详细视图。

相反,如果一个进程不可被核心转储,那么在它崩溃时,操作系统不会生成核心转储文件。这可能是由于安全考虑、资源限制或者是为了防止敏感信息泄露。

在Linux和其他类Unix系统中,可以通过prctl()系统调用的PR_SET_DUMPABLEPR_GET_DUMPABLE选项来设置和查询进程的dumpable状态。默认情况下,新创建的进程通常是可被核心转储的,但这个行为可以根据需要进行修改。

  1. 根据参数数量分配destfilecommand变量。

19-20. 禁止在目标文件名中使用元字符。

void reject_meta_chars(const char *fname, int globbing) {assert(fname);reject_cntrl_chars(fname);const char *reject = "\\&!?\"<>%^{};,*[]";if (globbing)reject = "\\&!\"<>%^{};,"; // file globbing ('*?[]') is allowedconst char *c = strpbrk(fname, reject);if (c) {fprintf(stderr, "Error: \"%s\" is an invalid filename: rejected character: \"%c\"\n", fname, *c);exit(1);}
}

此文件来自 src\lib\common.c

21-23. 如果没有提供命令(即command == NULL),则创建一个默认的过滤器文件。

24-26. 如果提供了命令,则根据需要调用process_template()copy()函数来处理命令和目标文件。

  1. 返回0,表示程序执行成功。

样例

程序功能

这个程序的作用是处理和配置Netfilter(Linux中的网络包过滤系统)的规则。它通过以下方式实现这一功能:

  1. 接收命令行参数,包括Netfilter命令和目标文件名。
  2. 如果只提供了目标文件名,程序将创建一个包含默认过滤规则的文件(定义在default_filter变量中)。
  3. 如果提供了Netfilter命令和目标文件名,程序将执行以下操作:
    • 如果命令中包含逗号(,),程序将解析命令作为模板,并使用后续的参数替换模板中的 $ARGn 标记。处理后的规则将写入目标文件。
    • 如果命令中不包含逗号,程序将简单地将命令内容复制到目标文件。

在处理过程中,程序会进行一些错误检查,如确保文件可以打开和读取,检查提供的参数是否有效,以及确保 $ARGn 标记在模板中正确使用。如果发生错误,程序将打印错误消息并退出。

以下是一些不同的输入情况及其结果:

  1. 只提供目标文件名:
fnetfilter /etc/netfilter/rules.conf

在这种情况下,程序将创建一个名为rules.conf的文件,并将默认的Netfilter规则写入该文件。默认规则如下:

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
-A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT
-A OUTPUT -p udp --dport 3478 -j DROP
-A OUTPUT -p udp --dport 3479 -j DROP
-A OUTPUT -p tcp --dport 3478 -j DROP
-A OUTPUT -p tcp --dport 3479 -j DROP
COMMIT
  1. 提供Netfilter命令和目标文件名,命令中不包含逗号:
fnetfilter "iptables -A INPUT -p tcp --dport 80 -j ACCEPT" /etc/netfilter/rules.conf

在这种情况下,程序将把"iptables -A INPUT -p tcp --dport 80 -j ACCEPT"这条命令直接复制到rules.conf文件中。

  1. 提供Netfilter命令和目标文件名,命令中包含逗号(模板形式):
fnetfilter "iptables -A INPUT -p $ARG1 --dport $ARG2 -j ACCEPT" ,tcp,80 /etc/netfilter/rules.conf

在这种情况下,程序将解析命令模板并用提供的参数替换 $ARGn 标记。因此,rules.conf 文件将包含以下规则:

iptables -A INPUT -p tcp --dport 80 -j ACCEPT

注意,这里的逗号后紧跟参数列表,参数之间用逗号分隔。在这个例子中,$ARG1 被替换为 tcp$ARG2 被替换为 80

相关文章:

  • 将 OpenLDAP 与 IBM Spectrum LSF 集成
  • 工业数据的特殊性和安全防护体系探索思考
  • 数据结构--图
  • VuePress安装及使用——使用 Markdown 创建你自己的博客网站和电子书
  • 23.ACL
  • LINUX SD卡备份的镜像+烧录启动时自动扩展最后一个分区
  • 【数据结构】什么是堆?
  • 2023年第四届 “赣网杯” 网络安全大赛 gwb-web3 Write UP【PHP 临时函数名特性 + 绕过trim函数】
  • 八股文打卡day2——计算机网络(2)
  • Maven知识
  • 【QT】解决QTableView鼠标点击合并单元格高亮显示问题
  • Jenkins在window下配置Android打包配置
  • 2023年超声波清洗机实测!清洁力强的超声波清洗机到底如何选择?
  • JWT令牌的作用和生成
  • 【Python】使用进程池Pool创建进程
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • Elasticsearch 参考指南(升级前重新索引)
  • ES6系统学习----从Apollo Client看解构赋值
  • exports和module.exports
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • Javascript基础之Array数组API
  • JS专题之继承
  • SQLServer插入数据
  • tweak 支持第三方库
  • ViewService——一种保证客户端与服务端同步的方法
  • 构建二叉树进行数值数组的去重及优化
  • 机器学习中为什么要做归一化normalization
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 使用API自动生成工具优化前端工作流
  • 小李飞刀:SQL题目刷起来!
  • 新书推荐|Windows黑客编程技术详解
  • 一些css基础学习笔记
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • Prometheus VS InfluxDB
  • (10)工业界推荐系统-小红书推荐场景及内部实践【排序模型的特征】
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (Redis使用系列) SpirngBoot中关于Redis的值的各种方式的存储与取出 三
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (论文阅读26/100)Weakly-supervised learning with convolutional neural networks
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (十八)SpringBoot之发送QQ邮件
  • *2 echo、printf、mkdir命令的应用
  • .net 4.0发布后不能正常显示图片问题
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .NET 使用 XPath 来读写 XML 文件
  • .netcore 如何获取系统中所有session_ASP.NET Core如何解决分布式Session一致性问题
  • .net网站发布-允许更新此预编译站点
  • /使用匿名内部类来复写Handler当中的handlerMessage()方法