IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 连接跟踪TCP报文错误检查 -> 正文阅读

[系统运维]连接跟踪TCP报文错误检查

如下函数在接收到TCP报文之后,首先进行错误检查tcp_error。

int nf_conntrack_tcp_packet(struct nf_conn *ct,
                struct sk_buff *skb,
                unsigned int dataoff,
                enum ip_conntrack_info ctinfo,
                const struct nf_hook_state *state)
{
    struct net *net = nf_ct_net(ct);
    struct nf_tcp_net *tn = nf_tcp_pernet(net);
    const struct tcphdr *th;

    th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
    if (th == NULL)
        return -NF_ACCEPT;

    if (tcp_error(th, skb, dataoff, state))
        return -NF_ACCEPT;

如果TCP头部长度字段的值,小于标准的头部长度(20字节),表明是构造错误的包。或者TCP头部和数据的总长度小于标准TCP头部长度,表明为被截断的TCP报文。

static bool tcp_error(const struct tcphdr *th,
              struct sk_buff *skb,
              unsigned int dataoff,
              const struct nf_hook_state *state)
{   
    unsigned int tcplen = skb->len - dataoff;
    u8 tcpflags;
    
    /* Not whole TCP header or malformed packet */
    if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) {
        tcp_error_log(skb, state, "truncated packet");
        return true;
    }

接下来报文校验和的检测受控于nf_conntrack_checksum的值,默认为1。可通过一下PROC文件进行修改。

# modprobe nf_conntrack
# cat /proc/sys/net/netfilter/nf_conntrack_checksum
1

这里仅对ingress进入系统的报文进行checksum检查,hook点不等于NF_INET_PRE_ROUTING的话,不进行检查。checksum正确时nf_checksum返回零。

    /* Checksum invalid? Ignore.
     * We skip checking packets on the outgoing path
     * because the checksum is assumed to be correct.
     */
    /* FIXME: Source route IP option packets --RR */
    if (state->net->ct.sysctl_checksum &&
        state->hook == NF_INET_PRE_ROUTING &&
        nf_checksum(skb, state->hook, dataoff, IPPROTO_TCP, state->pf)) {
        tcp_error_log(skb, state, "bad checksum");
        return true;
    }

在去掉ECN相关的两个标志ECE和CWR,以及PUSH标志之后,对剩余的标志位组合进行检查。

    /* Check TCP flags. */
    tcpflags = (tcp_flag_byte(th) & ~(TCPHDR_ECE|TCPHDR_CWR|TCPHDR_PSH));
    if (!tcp_valid_flags[tcpflags]) {
        tcp_error_log(skb, state, "invalid tcp flag combination");
        return true;
    }
    
    return false;

合法的TCP标志位

tcp_valid_flags数组的大小为:0x37+1,以防某个报文同时设置了所有这些标志位。以下未列出的标志位组合都是非法的。

/* table of valid flag combinations - PUSH, ECE and CWR are always valid */
static const u8 tcp_valid_flags[(TCPHDR_FIN|TCPHDR_SYN|TCPHDR_RST|TCPHDR_ACK|
                 TCPHDR_URG) + 1] =
{
    [TCPHDR_SYN]                = 1,
    [TCPHDR_SYN|TCPHDR_URG]         = 1,
    [TCPHDR_SYN|TCPHDR_ACK]         = 1,
    [TCPHDR_RST]                = 1,
    [TCPHDR_RST|TCPHDR_ACK]         = 1,
    [TCPHDR_FIN|TCPHDR_ACK]         = 1,
    [TCPHDR_FIN|TCPHDR_ACK|TCPHDR_URG]  = 1,
    [TCPHDR_ACK]                = 1,
    [TCPHDR_ACK|TCPHDR_URG]         = 1,
};

#define tcp_flag_byte(th) (((u_int8_t *)th)[13])
        
#define TCPHDR_FIN 0x01
#define TCPHDR_SYN 0x02
#define TCPHDR_RST 0x04
#define TCPHDR_PSH 0x08
#define TCPHDR_ACK 0x10
#define TCPHDR_URG 0x20
#define TCPHDR_ECE 0x40
#define TCPHDR_CWR 0x80
    
#define TCPHDR_SYN_ECN  (TCPHDR_SYN | TCPHDR_ECE | TCPHDR_CWR)

TCP校验和

对于IPv4协议,由函数nf_ip_checksum检查TCP校验和。如果ip_summed等于CHECKSUM_COMPLETE,表明skb结构中的csum已经计算过得部分校验和(不包括伪头部),如果其折叠为16比特之后,取反等于零,表明验证通过。反之,对于TCP或者UDP协议,需要将伪头部计算进csum,最终结果为零,验证通过。

__sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook,
               unsigned int dataoff, u8 protocol)
{   
    const struct iphdr *iph = ip_hdr(skb);
    __sum16 csum = 0;
    
    switch (skb->ip_summed) {
    case CHECKSUM_COMPLETE:
        if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN)
            break;
        if ((protocol != IPPROTO_TCP && protocol != IPPROTO_UDP &&
            !csum_fold(skb->csum)) ||
            !csum_tcpudp_magic(iph->saddr, iph->daddr,
                       skb->len - dataoff, protocol,
                       skb->csum)) {
            skb->ip_summed = CHECKSUM_UNNECESSARY;
            break;
        }
        fallthrough;

对于ip_summed等于CHECKSUM_NONE的情况,校验和还未计算。对于TCP或者UDP,先行计算伪头部的校验和,再行计算协议数据的校验和。

    case CHECKSUM_NONE: 
        if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
            skb->csum = 0;
        else
            skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
                               skb->len - dataoff,
                               protocol, 0);
        csum = __skb_checksum_complete(skb);
    }
    return csum;

第一步先将csum由32bit变为17bit;第二步将其由17bit变为16bit;最后进行取反操作。

/*          
 *  Fold a partial checksum without adding pseudo headers
 */                               
static inline __sum16 csum_fold(__wsum csum)
{       
    u32 sum = (__force u32)csum;
    sum = (sum & 0xffff) + (sum >> 16);
    sum = (sum & 0xffff) + (sum >> 16);
    return (__force __sum16)~sum;
} 

内核版本 5.10

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-12-24 18:55:01  更:2021-12-24 18:56:37 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 5:58:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码