最近在学零信任相关的知识,给记录下来方便以后查阅
Iptables原理
iptables就是linux中的防火墙工具,其中的四表五链比较重要,并且用了c的底层编写。
四表
raw 表的作用是将命中规则的包,跳过其它表的处理,它的优先级最高。
mangle 表的作用是根据规则修改数据包的一些标志位,比如 TTL
nat 表的作用是实现网络地址转换
filter 表的作用是过滤某些包,这是防火墙工作的基础
filter(过滤表)
包含三个规则链
INPUT 输入链,处理目标地址为本机 IP 地址的报文。
OUTPUT 输出链,处理本机 IP 地址产生的报文。
FORWARD 转发链,处理经过本机路由的报文。
经过本机转发的报文经过 FORWARD 链,不经过 IPPUT 链和 OUTPUT 链。
nat(网络地址转换表)
先科普一下网络地址转换是什么
NAT的主要功能是将源IP地址和/或目标IP地址在数据包经过网络边界设备(如路由器或防火墙)时进行转换。这种转换可以在不同层级上进行,包括网络层(IP地址转换)和传输层(端口转换)。
源地址转换(Source NAT,SNAT):将私有网络内部主机的源IP地址转换为公共网络上的一个IP地址。这允许私有网络中的主机通过共享单个公共IP地址来访问互联网。
目标地址转换(Destination NAT,DNAT):将公共网络上的目标IP地址转换为私有网络内部主机的一个IP地址。这允许从公共网络访问特定服务或应用程序时,将流量定向到私有网络中的特定主机或服务器。
端口地址转换(Port Address Translation,PAT):在NAT过程中同时转换IP地址和端口号。通过在传输层级别上对端口号进行转换,可以将多个私有网络主机共享单个公共IP地址。
nat 用来完成源/目的地址和端口的转换,当一个报文在创建一个新的连接时进入该表。它也有 3 个内置规则链。
PREROUTING:用于修改到来的报文,只用来做网络地址转换。
OUTPUT:用于修改本机产生的并且在路由处理之前的报文。
POSTROUTING:用于对转换后的数据包进行进一步处理,比如修改数据包的源地址、源端口。
mangle(修改表)
这个表主要用来进行报文修改,有 5 个内置规则链。
PREROUTING:针对到来的报文,在路由之前修改的地方。
INPUT:针对目的地址为网关本身的报文。
FORWARD:针对通过网关路由转发的报文。
POSTROUTING:将要发送出去的报文的地方。
OUTPUT:本机产生报文在路由之前修改的地方。
通常使用该表进行报文修改,以便进行 QoS 和策略路由。通常每一个报文都进入该表,但不使用它来做报文过滤。
raw(原始表)
这个表主要用于配置连接跟踪相关内容,在 ip_conntrack 之前调用。
PREPROUTING:到达本机的报文。
OUTPUT:本机进程产生的报文。
这里是能够在连接跟踪生效前处理报文的地方,你可以标记符合某种条件的报文不被连接跟踪处理。
security 表(新增)
这个表用于安全 Linux 的防火墙规则,是 iptables 最近的新增表,在实际项目中还很少用到。
五链
五链刚刚已经基本上都提到了,下面将根据信息在防火墙中的处理方式来进一步加深理解。
(1)当一个数据包进入网卡时,它首先进入PREROUTING 链,内核根据数据包目的IP判断是否需要转送出去。
(2)如果数据包就是进入本机的,它就会沿着图向下移动,到达INPUT 链。数据包到了INPUT 链后,任何进程都会收到它。本机上运行的程序可以发送数据包,这些数据包会经过OUTPUT 链,然后到达POSTROUTING 链输出。
(3)如果数据包是要转发出去的,且内核允许转发,数据包就会如图所示向右移动,经过FORWARD 链,然后到达POSTROUTING 链输出。
数据接收处理流程:PREROUTING链 -> 路由判断(是本机)-> INPUT链 -> …
本机发送数据包流程:路由选择 -> OUTPUT链 -> POSTROUTING链 -> …
转发数据流程:PREROUTING链 -> 路由判断(不是本设备,找到下一跳) -> FORWARD链 -> POSTROUTING链 -> …
所以四表五链的关系如图所示
解答:多个钩子可以处理不同的应用场景,数据接收,数据发送和数据转发需要不一样的策略。所以不能单单使用一个钩子解决,实际问题是复杂的而不是单一的。
Socket编程
在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。
后记
在防火墙中,锚点是一种机制,用于将一组规则绑定到特定的网络接口、IP地址、协议或服务等。锚点提供了一种有组织的方式来管理和应用规则集,以根据特定的条件对网络流量进行处理。
网络字节序
内存中的多字节数据相对于内存地址有大端和小端之分,发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出,接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存,因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址。
TCP/IP协议规定,网络数据流应采用大端字节序,所以可能需要用到字节序转换
1 2 3 4 5 6 #include <arpa/inet.h> uint32_t htonl (uint32_t hostlong) ; uint16_t htons (uint16_t hostshort) ; uint32_t ntohl (uint32_t netlong) ; uint16_t ntohs (uint16_t netshort) ;
h表示host,n表示network,l表示long,32位长整数,s表示short,16位短整数。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。
IP地址转换函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <arpa/inet.h> int inet_pton (int af, const char *src, void *dst) ;const char *inet_ntop (int af, const void *src, char *dst, socklen_t size) ;
支持IPv4和IPv6
可重入函数
其中inet_pton和inet_ntop不仅可以转换IPv4的in_addr,还可以转换IPv6的in6_addr。
因此函数接口是void *addrptr。
sockaddr结构
1 2 3 4 5 6 7 8 9 10 11 struct sockaddr_in { sa_family_t sin_family; 地址结构类型 in_port_t sin_port; 端口号 struct in_addr sin_addr ; IP地址 unsigned char __pad[__SOCK_SIZE__ - sizeof (short int ) - sizeof (unsigned short int ) - sizeof (struct in_addr)]; }; struct in_addr { __be32 s_addr; };
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct sockaddr_in addr; addr.sin_family = AF_INET/AF_INET6 addr.sin_port = htons(9527); int dst; inet_pton(AF_INET, "192.157.22.45", (void *)&dst); addr.sin_addr.s_addr = dst; 【*】addr.sin_addr.s_addr = htonl(INADDR_ANY); 取出系统中有效的任意IP地址。二进制类型。 bind(fd, (struct sockaddr *)&addr, size);
网络套接字函数
socket函数
1 2 3 #include <sys/types.h> #include <sys/socket.h> int socket (int domain, int type, int protocol) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 domain: AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址 AF_INET6 与上面类似,不过是来用IPv6的地址 AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用 type: SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。 SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。 SOCK_SEQPACKET该协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。 SOCK_RAW socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议) SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序 protocol: 传0 表示使用默认协议。 返回值: 成功:返回指向新创建的socket的文件描述符,失败:返回-1,设置errno
socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1。对于IPv4,domain参数指定为AF_INET。对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,表示面向数据报的传输协议。protocol参数的介绍从略,指定为0即可。
bind函数
服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接,因此服务器需要调用bind绑定一个固定的网络地址和端口号。
1 2 3 #include <sys/types.h> #include <sys/socket.h> int bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen) ;
1 2 3 4 5 6 7 8 9 sockfd: socket文件描述符 addr: 构造出IP地址加端口号 (struct sockaddr *)&addr addrlen: sizeof(addr)长度 返回值: 成功返回0,失败返回-1, 设置errno
listen函数
listen函数用来设置和服务器连接的主机上限,并不是监听!
1 2 3 4 5 6 7 8 9 10 #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int listen(int sockfd, int backlog); sockfd: socket文件描述符 backlog: 连接上限,最大值128 返回值: 成功:返回0 失败:返回-1
accept函数
1 2 3 4 5 6 7 8 9 10 11 #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); sockdf: socket文件描述符 addr: 传出参数,返回链接客户端地址信息,含IP地址和端口号 addrlen: 传入传出参数(值-结果),传入sizeof(addr)大小,函数返回时返回真正接收到地址结构体的大小 返回值: 成功返回一个新的socket文件描述符,用于和客户端通信,失败返回-1,设置errno
accept函数使用示例:
1 2 3 4 5 6 7 while (1 ) { cliaddr_len = sizeof (cliaddr); connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); n = read(connfd, buf, MAXLINE); ...... close(connfd); }
connect函数
1 2 3 4 5 6 7 8 9 10 11 #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); sockdf: socket文件描述符 addr: 传入参数,指定服务器端地址信息,含IP地址和端口号 addrlen: 传入参数,传入sizeof(addr)大小 返回值: 成功返回0,失败返回-1,设置errno
server
下面通过最简单的客户端/服务器程序的实例来学习socket API。
server.c的作用是从客户端读字符,然后将每个字符转换为大写并回送给客户端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define MAXLINE 80 #define SERV_PORT 6666 int main (void ) { struct sockaddr_in servaddr , cliaddr ; socklen_t cliaddr_len; int listenfd, connfd; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; int i, n; listenfd = socket(AF_INET, SOCK_STREAM, 0 ); bzero(&servaddr, sizeof (servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); bind(listenfd, (struct sockaddr *)&servaddr, sizeof (servaddr)); listen(listenfd, 20 ); printf ("Accepting connections ...\n" ); while (1 ) { cliaddr_len = sizeof (cliaddr); connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); n = read(connfd, buf, MAXLINE); printf ("received from %s at PORT %d\n" , inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof (str)), ntohs(cliaddr.sin_port)); for (i = 0 ; i < n; i++) buf[i] = toupper (buf[i]); write(connfd, buf, n); close(connfd); } return 0 ; }
client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #define MAXLINE 80 #define SERV_PORT 6666 int main (int argc, char *argv[]) { struct sockaddr_in servaddr ; char buf[MAXLINE]; int sockfd, n; char *str; if (argc != 2 ) { fputs ("usage: ./client message\n" , stderr ); exit (1 ); } str = argv[1 ]; sockfd = socket(AF_INET, SOCK_STREAM, 0 ); bzero(&servaddr, sizeof (servaddr)); servaddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1" , &servaddr.sin_addr); servaddr.sin_port = htons(SERV_PORT); connect(sockfd, (struct sockaddr *)&servaddr, sizeof (servaddr)); write(sockfd, str, strlen (str)); n = read(sockfd, buf, MAXLINE); printf ("Response from server:\n" ); write(STDOUT_FILENO, buf, n); close(sockfd); return 0 ; }
TCP/IP
OSI各种设备和作用以及对应网络分层
TCP/IP与OSI模型
TCP/IP发送邮件时的分层处理
包中的结构
共享介质型网络
争用方式
疑问
要是一直有一个主机发送过程中一直与其他计算机冲突,该主机一直无法完整发送数据该怎么办?
令牌传递方式
PPP
点对点协议。物理层也需要连接
数据帧格式
IP协议
IP地址
分类:A、B、C、D类
fwknop项目
client分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 struct fko_context { char *rand_val; char *username; time_t timestamp; short message_type; char *message; char *nat_access; char *server_auth; unsigned int client_timeout; short digest_type; short encryption_type; int encryption_mode; short hmac_type; char *version; char *digest; int digest_len; char *raw_digest; short raw_digest_type; int raw_digest_len; char *encoded_msg; int encoded_msg_len; char *encrypted_msg; int encrypted_msg_len; char *msg_hmac; int msg_hmac_len; int added_salted_str; int added_gpg_prefix; unsigned int state; unsigned char initval; #if HAVE_LIBGPGME char *gpg_exe; char *gpg_recipient; char *gpg_signer; char *gpg_home_dir; unsigned char have_gpgme_context; gpgme_ctx_t gpg_ctx; gpgme_key_t recipient_key; gpgme_key_t signer_key; unsigned char verify_gpg_sigs; unsigned char ignore_gpg_sig_error; fko_gpg_sig_t gpg_sigs; gpgme_error_t gpg_err; #endif };
问题1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 int fko_encode_spa_data (fko_ctx_t ctx) { int res, offset = 0 ; char *tbuf; #if HAVE_LIBFIU fiu_return_on("fko_encode_spa_data_init" , FKO_ERROR_CTX_NOT_INITIALIZED); #endif if (!CTX_INITIALIZED(ctx)) return (FKO_ERROR_CTX_NOT_INITIALIZED); #if HAVE_LIBFIU fiu_return_on("fko_encode_spa_data_valid" , FKO_ERROR_INCOMPLETE_SPA_DATA); #endif if ( validate_username(ctx->username) != FKO_SUCCESS || ctx->version == NULL || strnlen(ctx->version, MAX_SPA_VERSION_SIZE) == 0 || ctx->message == NULL || strnlen(ctx->message, MAX_SPA_MESSAGE_SIZE) == 0 ) { return (FKO_ERROR_INCOMPLETE_SPA_DATA); } if (ctx->message_type == FKO_NAT_ACCESS_MSG) { if (ctx->nat_access == NULL || strnlen(ctx->nat_access, MAX_SPA_MESSAGE_SIZE) == 0 ) return (FKO_ERROR_INCOMPLETE_SPA_DATA); } #if HAVE_LIBFIU fiu_return_on("fko_encode_spa_data_calloc" , FKO_ERROR_MEMORY_ALLOCATION); #endif tbuf = calloc (1 , FKO_ENCODE_TMP_BUF_SIZE); if (tbuf == NULL ) return (FKO_ERROR_MEMORY_ALLOCATION); strlcpy(tbuf, ctx->rand_val, FKO_ENCODE_TMP_BUF_SIZE); strlcat(tbuf, ":" , FKO_ENCODE_TMP_BUF_SIZE); if ((res = append_b64(tbuf, ctx->username)) != FKO_SUCCESS) { free (tbuf); return (res); } offset = strlen (tbuf); snprintf (((char *)tbuf+offset), FKO_ENCODE_TMP_BUF_SIZE - offset, ":%u:" , (unsigned int ) ctx->timestamp); strlcat(tbuf, ctx->version, FKO_ENCODE_TMP_BUF_SIZE); fko_set_spa_client_timeout(ctx, ctx->client_timeout); offset = strlen (tbuf); snprintf (((char *)tbuf+offset), FKO_ENCODE_TMP_BUF_SIZE - offset, ":%i:" , ctx->message_type); if ((res = append_b64(tbuf, ctx->message)) != FKO_SUCCESS) { free (tbuf); return (res); } if (ctx->nat_access != NULL ) { strlcat(tbuf, ":" , FKO_ENCODE_TMP_BUF_SIZE); if ((res = append_b64(tbuf, ctx->nat_access)) != FKO_SUCCESS) { free (tbuf); return (res); } } if (ctx->server_auth != NULL ) { strlcat(tbuf, ":" , FKO_ENCODE_TMP_BUF_SIZE); if ((res = append_b64(tbuf, ctx->server_auth)) != FKO_SUCCESS) { free (tbuf); return (res); } } if (ctx->client_timeout > 0 && ctx->message_type != FKO_COMMAND_MSG) { offset = strlen (tbuf); snprintf (((char *)tbuf+offset), FKO_ENCODE_TMP_BUF_SIZE - offset, ":%i" , ctx->client_timeout); } if (ctx->encoded_msg != NULL ) free (ctx->encoded_msg); ctx->encoded_msg = strdup(tbuf); free (tbuf); if (ctx->encoded_msg == NULL ) return (FKO_ERROR_MEMORY_ALLOCATION); ctx->encoded_msg_len = strnlen(ctx->encoded_msg, MAX_SPA_ENCODED_MSG_SIZE); if (! is_valid_encoded_msg_len(ctx->encoded_msg_len)) return (FKO_ERROR_INVALID_DATA_ENCODE_MSGLEN_VALIDFAIL); if ((res = fko_set_spa_digest(ctx)) != FKO_SUCCESS) return (res); FKO_CLEAR_SPA_DATA_MODIFIED(ctx); return (FKO_SUCCESS); }
问题2
为什么一会用strcat一会用snprintf的方式
1 strlcat(tbuf, ":" , FKO_ENCODE_TMP_BUF_SIZE);
1 2 3 4 offset = strlen (tbuf); snprintf (((char *)tbuf+offset), FKO_ENCODE_TMP_BUF_SIZE - offset, ":%u:" , (unsigned int ) ctx->timestamp);
问题3
这个是什么,为什么几乎每个函数前面都有这个
1 2 3 #if HAVE_LIBFIU fiu_return_on("fko_set_spa_digest_init" , FKO_ERROR_CTX_NOT_INITIALIZED); #endif