博客

  • python使用协程循环并发执行http请求

    import asyncio
    import aiohttp
    
    async def do_request(val):
        timeout = aiohttp.ClientTimeout(total=86400)
        async with aiohttp.ClientSession(timeout=timeout) as session:
            async with session.post('http://127.0.0.1:5000'), json={"val": val}) as resp:
            result = await resp.json()
            return result
    
        async def main(count):
            tasks = [do_request(val) from val in range(count)]
            values = await asyncio.gather(*tasks)
            print(values)
    
    asyncio.run(main(10))
    

    参考:
    https://superfastpython.com/asyncio-for-loop/
    https://www.dongwm.com/post/understand-asyncio-1/
    https://www.dongwm.com/post/understand-asyncio-2/

    Views: 2

  • dnsdist降低健康检查的频率

    dnsdist默认会以1Hz的频率向上游服务器发送a.root-servers.net的解析请求,用于健康检查,下面配置10分钟发送一次
    newServer({address="172.30.0.54", pool="rec", checkInterval=600})

    Views: 1

  • dnsdist支持ipv6

    dnsdist.conf中按下面配置,同时Docker开启ipv6支持

    addLocal('0.0.0.0', {reusePort=true,doTCP=true})
    addLocal('::', {reusePort=true,doTCP=true})
    setACL({'0.0.0.0/0', '::/0'})
    webserver("0.0.0.0:8083")
    webserver("[::]:8083")
    

    Views: 1

  • openwrt流量监控

    opkg install luci-app-nlbwmon luci-i18n-nlbwmon-zh-cn nlbwmon

    参考:
    https://github.com/destan19/OpenAppFilter
    https://github.com/jow-/nlbwmon/tree/master

    Views: 1

  • automake交叉编译

    export PATH=$PATH:/opt/toolchains/taowrt-toolchain-1.0.4-mediatek-filogic_gcc-13.3.0_musl.Linux-x86_64/toolchain-aarch64_cortex-a53_gcc-13.3.0_musl/bin/
    ./configure --build=aarch64-openwrt-linux
    make
    

    Views: 1

  • 关于nftables在output hook中设置mark路由不通的问题

    我这里排查到两点原因
    1. output hook只能是rotue链,不能是filter链,而prerouting则只能是filter链
    要这样写
    nft add chain inet fw4 gfw_output { type route hook output priority mangle\; policy accept\; }
    filter的优先级是比route低的
    官方是这么写的

    filter: Supported by arp, bridge, ip, ip6 and inet table families.
    route: Mark packets (like mangle for the output hook, for other hooks use the type filter instead), supported by ip and ip6.
    nat: In order to perform Network Address Translation, supported by ip and ip6.
    2. 本地出口源IP的问题
    可能因为默认路由在wan口上,所以本地发包源IP始终是wan口上的IP,而这个IP是运营商分配的100.64.0.0/10段的IP,这样就导致远端服务器找不到到100.64.0.0/10的路由,数据包有去无回,需要在服务器上配置这个网段的路由
    如果是wireguard在AllowedIPs中添加100.64.0.0/10网段即可。

    参考:
    https://forums.gentoo.org/viewtopic-t-1136379-start-0.html

    Views: 14

  • 计算udp校验和(反码求和)

    C语言1:

    #include <stdio.h>
    #include <arpa/inet.h>
    
    unsigned long checksum(char *buf,int nword)
    {
        unsigned long sum;
        unsigned short *data = (unsigned short *) buf;
    
        for(sum=0; nword>0; nword--)
        {
            sum += *data;
            data++;
            sum = (sum>>16) + (sum&0xffff);
        }
        sum = ~checksum & 0xFFFF;
        return htons(sum); //转为大端字节序
    }
    
    void main(int argc, char *argv[])
    {
        char b []  = "\0d\0e";
        unsigned long ret = checksum(b, 2);
        printf("checksum is: 0x%lx\n", ret);
        printf("checksum is: %lu\n", ret);
    
    }
    

    C语言2(github copilot):

    #include <stdio.h>
    #include <stdint.h>
    
    uint16_t udp_checksum(uint8_t *data, size_t len) {
        uint32_t checksum = 0;
    
        // 如果数据长度是奇数,填充一个字节
        if (len % 2 != 0) {
            data[len] = 0;
            len++;
        }
    
        // 将字节数组分成16位的块并相加
        for (size_t i = 0; i < len; i += 2) {
            uint16_t word = (data[i] << 8) + data[i + 1];
            checksum += word;
            // 处理进位
            checksum = (checksum & 0xFFFF) + (checksum >> 16);
        }
    
        // 取反码
        checksum = ~checksum & 0xFFFF;
        return (uint16_t)checksum;
    }
    
    int main() {
        uint8_t data[] = {'a', 'b', 'c', 'd'};
        size_t len = sizeof(data) / sizeof(data[0]);
        printf("Checksum: 0x%04X\n", udp_checksum(data, len));
        return 0;
    }
    

    Python(Github copilot):

    def udp_checksum(data):
        """下面是一个用Python编写的UDP校验和算法:
        ### 步骤:
        1. 定义一个函数 `udp_checksum`,接受一个字节数组作为参数。
        2. 将字节数组分成16位的块。
        3. 将所有16位块相加。
        4. 如果有进位,则将进位加到结果中。
        5. 取结果的反码。
        6. 返回低16位,就是校验和。
        """
        if len(data) % 2 != 0:
            data += b'\x00'  # 如果数据长度是奇数,填充一个字节
    
        checksum = 0
        for i in range(0, len(data), 2):
            # 这个公式是大端的算法,如果是小端可以写作(data[i+1] << 8) + data[i]
            word = (data[i] << 8) + data[i + 1]
            print(word)
            checksum += word
            checksum = (checksum & 0xFFFF) + (checksum >> 16)  # 处理进位
    
        checksum = ~checksum & 0xFFFF  # 取反码
        return checksum
    
    # 示例用法
    data = b'\0d\0e'
    print(hex(udp_checksum(data)))  # 输出校验和
    

    关于接收端验证的方法:

    把整个需要校验的数据计算校验和(反码求和),如果结果为0x0000则结果就是正确的,或者计算原码求和(累加后不取反),结果为0xffff则就是正确的。网上有的说验证结果为0x0000,有的说验证结果为0xffff要了解其中的原因。

    参考:
    https://blog.csdn.net/weixin_28673511/article/details/130314065
    https://zhuanlan.zhihu.com/p/74381973

    Views: 2

  • dnsmasq配置dhcpv6

    dnsmasq开启dhcpv6需要开启以下两项配置

    enable-ra
    启用icmpv6 ra报文发送功能,这是SLAAC所必须的,实测开不开这一项都会发送ra报文

    dhcp-range=::1,::1000,constructor:br-lan,ra-names
    要开始dhcpv6必须指定起始和结束主机号,这里是::1和::1000,据我测试指定::或单独一个::1都无法启用dhcpv6,不用担心指定主机号范围后无法使用SLAAC功能,实际上指定主机号范围后SLAAC和DHCPV6是同时开启的。
    constructor:br-lan这一项是指定绑定的网卡
    ra-names 这一项是利用dhcpv4的主机名来推导ipv6的主机名,可以与其它mode合并使用
    不指定结束主机号且无其它mode M:0,O:0,仅使用SLAAC,dhcp服务不启动
    指定结束主机号 slaac和dhcpv6同时生效,M:1,O:1
    slaac 实测没有效果
    ra-only 实测没有效果
    ra-stateless M:0,O:1,使用无状态dhcpv6,并禁用有状态dhcpv6,dhcpv6应答会显示无可用地址

    如果是openwrt系统,创建/etc/dnsmasq.d/ipv6.conf,添加以下两行

    enable-ra
    dhcp-range=::1,::1000,constructor:br-lan,ra-names
    

    openwrt系统开启dnsmasq的dhcpv6功能后需要关闭openwrt自带的dhcpv6功能
    merlin系统则可以在/jffs/configs目录下修改dnsmasq配置

    查看租约信息
    cat /var/dhcp.lease

    这样能通过主机名访问到的就是IPv6地址了

    更多的语法参考这里:
    https://thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html
    RDNSS:
    https://datatracker.ietf.org/doc/rfc8106/
    dnsmasq实现的RDNSS只有dns server list,没有dns search list

    Views: 3

  • IPV6实现内网主机名动态解析

    根据RFC4704的说明,只有dhcpv6 statefull才支持主机名解析,也就是dhcp请求报文中的option39字段,stateless及SLAAC都是不支持的,这给主机名解析带来了一些难度,要实现主机名解析可以从以下两个方面入手:
    1. 使用dhcpv6 statefull
    dnsmasq和odhcpd都支持statefull功能,还需要注意以下方面
    – 把RA报文的M和O位设置为1
    其实不是也有的客户端都严格按照RA报文的标志来处理,像Windows10上就是始终会执行SLAAC 和DHCPv6两种请求
    – 客户端请求报文携带option 39字段
    在Windows上保持默认即可,部分Linux系统(Debian)需要在/etc/dhcp/dhclient.conf中 添加send fqdn.fqdn = gethostname();
    据我测试macos上也存在这个问题,目前没有解决办法。

    statefull和slaac是可以同时使用的,RA通告前缀的同时使用dhcpv6提供地址分配,这样客户端会     获得两个IP地址
    
    1. 使用dnsmasq ra-names功能
      ra-names是根据dhcpv4中的主机名再结合mac地址计算出主机名与ipv6地址的绑定关系
      这种方式有两个缺点:
      一是,推算出来的ipv6地址不一定100%正确
      二是,要通过DHCP获取ipv4地址,这对于单栈网络或静态配置的IP地址就不行。
      好在statefull和ra-names功能是可以同时使有的,可以弥补彼此的不足

    参考:
    https://datatracker.ietf.org/doc/rfc4704/
    https://serverfault.com/questions/1033682/dhclient-is-sending-host-name-for-ipv4-but-not-ipv6

    Views: 3

  • prombox启用dhcpv6

    标准的debian系统在
    /etc/network/interface中
    配置

    auto eth0
           iface eth0 inet6 dhcp
           accept_ra 2
           autoconf 1
    

    就好了
    但是prombox上面的network script似乎与debian系统不一样,dhcp不生效。
    可以采用以下配置

    auto vmbr0
    iface vmbr0 inet static
            address 192.168.33.15/24
            gateway 192.168.33.1
            bridge-ports eth0 eth1
            bridge-stp off
            bridge-fd 0
            dns-nameservers 192.168.33.1
            up dhclient -6 -nw -w vmbr0
            down dhclient -6 -r vmbr0
    
    iface vmbr0 inet6 auto
           accept_ra 2
           autoconf 1
    

    如果还是不生效把dhclient -6 -nw -w vmbr0写入到/etc/rc.local中

    Views: 2