V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
bulay
V2EX  ›  程序员

关于使用 socket 构造 icmp 包报错 Permission denied 的问题

  •  
  •   bulay · 2023-04-17 10:24:01 +08:00 · 1185 次点击
    这是一个创建于 374 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景: 接手别人的项目,添加子网时对子网所有地址进行 ping 测试


    构造子网地址和广播地址 socket 连接时,会报PermissionError: [Errno 13] Permission denied的错误.

    关于为什么要上子网地址和广播地址我也不清楚(代码专门拼接了这两个地址,这两者和对子网所有地址进行 ping 不是重复了吗)

    所有,子网地址和广播地址和应该怎么构造 icmp 的包和建立 socket 连接?

    第 1 条附言  ·  2023-04-17 14:20:19 +08:00

    ping普通地址无权限问题

    附一下代码

    def init_icmp_packet(sequence):
        # 把字节打包成二进制数据
        data_type = 8  # ICMP Echo Request
        data_code = 0  # must be zero
        data_checksum = 0  # "...with value 0 substituted for this field..."
        data_ID = process_id  # Identifier
        payload = b'abcdefghijklmnopqrstuvwabcdefghi'  # data
        icmp_packet = struct.pack('>BBHHH32s', data_type, data_code, data_checksum, data_ID, sequence, payload)
        icmp_checksum = check_sum(icmp_packet)  # 获取校验和
        icmp_packet = struct.pack('>BBHHH32s', data_type, data_code, icmp_checksum, data_ID, sequence, payload)
        return icmp_packet
    
    
    def init_raw_socket(ip_addr, icmp_packet):
        raw_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, 1)
        # 服务端设置非阻塞
        # raw_socket.setblocking(False)
        raw_socket.sendto(icmp_packet, (ip_addr, 1))   # 报错行
        return raw_socket
    
    第 2 条附言  ·  2023-04-17 16:17:51 +08:00

    问题已解决.附上解决后的代码:

    def init_raw_socket(ip_addr, icmp_packet):
        raw_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, 1)
        raw_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)   # 解决问题的代码,允许发送广播数据
        # 服务端设置非阻塞
        # raw_socket.setblocking(False)
        raw_socket.sendto(icmp_packet, (ip_addr, 1))
        return raw_socket
    

    关于setsockopt方法的用法及参数的含义参考这里

    11 条回复    2023-04-18 17:10:49 +08:00
    keakon
        1
    keakon  
       2023-04-17 11:04:57 +08:00
    socket.SOCK_RAW 需要 root 权限,你加个 sudo 试试
    yougg
        2
    yougg  
       2023-04-17 11:09:45 +08:00
    artnowben
        3
    artnowben  
       2023-04-17 11:11:37 +08:00
    构建 icmp 报文请参考一下 测试仪项目 dperf https://github.com/baidu/dperf
    具体代码:
    1. raw socket 参考 https://github.com/pengjianzhang/tcping/blob/main/tcping.c
    2. icmp 封装参考 https://github.com/baidu/dperf/blob/main/src/icmp.c
    julyclyde
        4
    julyclyde  
       2023-04-17 11:27:19 +08:00
    建议还是先学学网络的基本原理,搞明白掩码到底是做啥用的
    再处理权限问题
    qwq11
        5
    qwq11  
       2023-04-17 11:55:39 +08:00
    楼上提到需要 root 权限,其实从内核 2.2 开始就可以单独设置这些权限。你可以用用 sudo setcap cap_net_raw+p PROGRAM 赋予你程序 socket_row 的能力
    julyclyde
        6
    julyclyde  
       2023-04-17 12:21:28 +08:00
    @qwq11 求教那个+p 是啥意思
    之前我看 man 的时候没明白
    qwq11
        7
    qwq11  
       2023-04-17 12:45:42 +08:00
    bulay
        8
    bulay  
    OP
       2023-04-17 14:18:45 +08:00
    @keakon
    @yougg
    @artnowben
    @julyclyde 感谢各位给出的建议,我按照各位的建议使用 root 权限启动,或者为程序添加 root 权限,问题并没有解决.
    我可能描述的不清楚,该程序 ping 普通地址是没问题的,不存在权限问题,但是 ping 子网地址和子网广播地址才会报权限问题.

    @qwq11 谢谢提醒,掩码什么作用我知道
    qbqbqbqb
        9
    qbqbqbqb  
       2023-04-17 14:44:07 +08:00
    子网地址是不能用作目标地址的

    广播地址可以,但是如果要用广播地址作为目标地址的话,需要用 setsockopt 给 socket 加上 SO_BROADCAST 属性
    bulay
        10
    bulay  
    OP
       2023-04-17 16:10:14 +08:00
    @qbqbqbqb 感谢大佬!就是这个问题. 另外,子网地址确实可以 ping 通.用 linux 的命令`ping -b`也是可以用子网地址作为目标地址的
    julyclyde
        11
    julyclyde  
       2023-04-18 17:10:49 +08:00
    你这个功能是新加的??
    我还以为接手的时候已经有这个了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3269 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 14:15 · PVG 22:15 · LAX 07:15 · JFK 10:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.