Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support root-less ICMP pings on MacOS and Linux #10

Closed
mjpieters opened this issue Aug 23, 2019 · 8 comments
Closed

Support root-less ICMP pings on MacOS and Linux #10

mjpieters opened this issue Aug 23, 2019 · 8 comments
Assignees

Comments

@mjpieters
Copy link

mjpieters commented Aug 23, 2019

Provided the Linux kernel is configured to allow non-raw ICMP for your group ID (on macOS it is available by default, you can use a SOCK_DGRAM socket to ping without being root.

I can demonstrate this on my macOS system:

>>> import os
>>> os.getuid()  # not root
501
>>> os.uname()   # on MacOS
posix.uname_result(sysname='Darwin', nodename='Martijns-MacBook-Pro-2.local', release='17.7.0', version='Darwin Kernel Version 17.7.0: Thu Dec 20 21:47:19 PST 2018; root:xnu-4570.71.22~1/RELEASE_X86_64', machine='x86_64')
>>> import socket, threading, ping3
>>> ping3.DEBUG = True
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_ICMP)
>>> sock.setsockopt(socket.SOL_IP, socket.IP_TTL, 64)
>>> icmp_id = threading.current_thread().ident % 0xFFFF
>>> if True:
...     ping3.send_one_ping(sock=sock, dest_addr='192.168.1.160', icmp_id=icmp_id, seq=0, size=56)
...     res = ping3.receive_one_ping(sock=sock, icmp_id=icmp_id, seq=0, timeout=4)
...
[DEBUG] IP HEADER: {'version': 69, 'tos': 0, 'len': 14336, 'id': 63036, 'flags': 0, 'ttl': 64, 'protocol': 1, 'checksum': 65435, 'src_addr': 3232235936, 'dest_addr': 3232236008}
[DEBUG] ICMP HEADER: {'type': 0, 'code': 0, 'checksum': 8346, 'id': 61668, 'seq': 0}
>>> res
0.0025510787963867188

Linux enforces a suitable ICMP ID and checksum for you; there is no point in generating your own checksum (it is ignored and re-calculated) and you should not check for the ICMP ID when receiving packets as the kernel routes the packets to your socket based on their internal bookkeeping, instead.

I haven't found any proof that macOS does this however, so some platform-specific code would be needed to handle this difference. This can be as simple as ignoring the ICMP id when on Linux and not root.

Also see the Linux man 7 icmp documentation.

Could support for this be implemented in ping3?

@kyan001
Copy link
Owner

kyan001 commented Aug 25, 2019

Thanks for the information. I will look into this issue and see what I can do with it.

In another hand, I'd love to keep ping3 as an universal solution on all platforms by using python standard libraries. I had to investigate carefully before implementing any platform-specified codes.

@kyan001 kyan001 self-assigned this Aug 25, 2019
@dimitrismistriotis
Copy link

In favour of being root-less if possible. Want to use an "if ping then do X on server" approach and would not feel comfortable running as root, specially when pinging from command line works.

Forced to use a workaround like opening a subprocess: https://stackoverflow.com/questions/2953462/pinging-servers-in-python

@clockzhong
Copy link

It also needs root privilege in linux, I catch the following errors in my Ubuntu 18.04:

>>> ping("localhost")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/dist-packages/ping3.py", line 79, in wrapper
    func_return = func(*args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/ping3.py", line 275, in ping
    with socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) as sock:
  File "/usr/lib/python3.6/socket.py", line 144, in __init__
    _socket.socket.__init__(self, family, type, proto, fileno)
PermissionError: [Errno 1] Operation not permitted

@mjpieters
Copy link
Author

@clockzhong please read my post carefully and verify that your kernel and groups have been configured correctly:

Provided the Linux kernel is configured to allow non-raw ICMP for your group ID

@clockzhong
Copy link

@mjpieters Thanks for your response. Ok, got it, it means even we could use "ping" command for any user in linux, but it has some special work done in linux "ping" command, we also need do the same work for our own python ping program.

@kyan001
Copy link
Owner

kyan001 commented Jul 10, 2021

On Ubuntu, if socket.SOCK_DGRAM is used, the ICMP ID is rewrited to sock.getsockname()[1] and no IP Header in received data, only ICMP_HEADER ICMP_PAYLOAD. But on macOS, the IP Header is provided if socket type is socket.SOCK_DGRAM, and the ICMP ID is not included in sock.getsockname(). According to this answer there is no need to check ICMP ID but on macOS the ICMP ID check is still needed.

@kyan001
Copy link
Owner

kyan001 commented Jul 10, 2021

Now with a fallback strategy, socket will be created as socket.SOCK_RAW at start, and if this failed, socket.SOCK_DGRAM will be applied. If SOCK_DGRAM failed again with "[Errno 13] Permission Denial"(errno.EACCES), both exceptions raised.

When SOCK_DGRAM is used on Linux, reset expected ICMP ID from the sock. This should resolve the ICMP ID rewrite problem.

@kyan001
Copy link
Owner

kyan001 commented Jul 10, 2021

This feature is done by version>=2.9.1

@kyan001 kyan001 closed this as completed Jul 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants