ILD

iptables u32 module match packet payload
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2023-11-8 站点:Inside Linux Development

有一个场景,需要匹配udp的内容,为:

{"RPCMethod":"heartbeat","MAC":"3CCD5735541E","IPAddr":"192.168.3.84","LockNetStatus":0}

为了简单,可以匹配前面几个字符就可以了。


查阅资料,发现u32模块可以匹配数据包的内容。于是学习下其语法,还挺复杂的。


u32的32的意思,是从数据包的某一偏移读取4个字节,组成一个32位无符号整数,然后去校验这个整数。


它的基本格式是:

    location = value


    可以出现多次,用 && 连接,表示每个匹配都要满足。


location

     location的最后结果是得到一个整数,用这个整数去比较value。


    基本格式:

    offset [位运算 | 移位运算 | @运算]


    offset是相对于IP头的偏移,假设

        char * A = (IP头的地址)

        unsigned B = offset


    那么,我们得到的32位整数C是:

        C = (*(A+B)<<24) + (*(A+B+1)<<16) + (*(A+B+2)<<8) + *(A+B+3)


    上面的算法,就是从ip头的offset偏移处读取4个字节,得到一个整数。前面的字节处于整数的高位。


    先来看一个简单的例子。

    iptables -I OUTPUT -m u32 --u32 0=0x12345678

    这个应该很简单,从ip头的0偏移处读取0,1,2,3 4个字节,其内容为0x12345678,也就是0字节是0x12,以此类推。


引入运算符 & << >>

    u32总是从数据包读取4个字节,有时候我们只想比较某一个字节,或者某些位,这个时候就需要对得到的整数进行运算。

    u32支持3个运算,与,左移,右移。


    比如 offset & 0xff

    就是offset取出4个字节得到整数C后,进行 C & 0xff,得到最后的整数。这个结果实际上就是取 offset + 3 处一个字节的内容。


    比如 offset >> 24

    取出整数后,右移24位,那就只剩下最高位8个字节了,也就等价于取offset处一个字节。


    运算符可以多个拼接,比如  offset & 0xff >> 24,从左到右依次计算。


引入@

    前面讲到的location,偏移是固定的,但是呢?很多时候偏移不是固定的,比如udp的负载,

    因为ip头的长度是变长的,如果ip头有选项,那ip头的长度就不是20个字节了。


    因此就引入了@,他可以以前面计算出的整数,作为一个偏移,将基础指针地址移动这个偏移。也就是:

    A = A + C,

    然后对@后面的内容,再次执行上述 location相同的计算方式

    

    格式

     <previous location> @ <new location>


    从这个角度看,location的全局格式就变成了手册上描述的:

           location := number | location operator number

           operator := "&" | "<<" | ">>" | "@"


    手册上的描述是很晦涩的,搞不太懂。但是对格式的描述是准确的,完整的,就是

        数字 [远算符 数字] ...


    按照本文格式描述,就可以很好的理解,哪个是offset,哪个是运算符数字 。


    具体的例子,请看下面udp负载例子

value

    value可以是一个数字,也可以是一个范围,数字和范围可以出现多次,用逗号分开。比如

    1

    2:4

    1,2,3,5:8


    这个比较好理解。


udp负载例子

    想要udp负载的第4个字节为数字符 a


    第一步,判断ip协议是udp。

    协议在ip头的第10个字节,也就是偏移9,那我们从偏移6读取4个字节,刚好可以读到偏移9,作为32位的低8位。

    udp协议号是17.

            6 & 0xff = 17

    当然从偏移 7读4个字节,也可以包含偏移9,但是其是倒数第二个字节。那就变成了

            7 >> 8 & 0xff = 17


    第二步计算udp偏移

    ip头的长度是第一个字节的低4位乘以4,为:0 >> 24 & 0xf << 2,左移2就是乘4。也等价于

    0 >> 22 & 0x3c


    udp头的长度是 8个字节,负载的第4个字节,可以从负载的第1个字节读4个字节,作为整数的低8位,因此相对于IP头的偏移是8+1

    所以新的location是:9 & 0xff = 0x61

    字符a的ascii码是0x61


    所以最终的结果是:

     6 & 0xff = 17 && 0 >> 22 & 0x3c @ 9 & 0xff = 0x61


参考

man 8 iptables-extensions



Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.