brona / iproute2mac

CLI wrapper for basic network utilites on Mac OS X inspired with iproute2 on Linux systems - ip command.
MIT License
898 stars 73 forks source link

Linux compatibility: ip route get 8.8.8.8 doesn't list "src" #8

Closed medington closed 9 years ago

medington commented 9 years ago

I'm attempting to use iproute2mac to determine the in use IP address for a simple route to obtain a rouce address.

On a linux machine this command: ip route get 8.8.8.8 outputs this:

8.8.8.8 via 192.168.22.1 dev eth0  src 192.168.22.156

Whereas on my OS X 10.11 machine I get just this:

8.8.8.8 via 192.168.22.1 dev en11

Is there some way to get that same "src" value that will be compatible with linux?

For reference I'm trying follow this suggestion on my Mac and it doesn't work.

medington commented 9 years ago

I came up with a workaround, but it's definitely a hack. I use the adapter id returned after "dev" and look up the IP address from the full output of a 2nd ip route call, here is my bash function to do that:

# Return the ip address in use
function myip() {
    if [ "$OS_NAME" == "Linux" ] ; then
        ip route get 8.8.8.8 | awk 'NR==1 {print $NF}'
    else
        Adapter="$(ip route get 8.8.8.8 | awk 'NR==1 {print $NF}')"
        ip route | grep $Adapter | tail -1 | awk -F/ '{ print $1 }'
    fi
}

The grep of the adapter in the full output of the 2nd call matches several entries, not sure if the last one is always going to be the full IP address or not. Anyway, I tested this with all the combinations of connecting my wireless and ethernet adapters and it seems to work. I did notice however the output of the ip routes call is a traceback when there is no network connected at all instead of a sensible error message like ip provides: "Network is unreachable".

If you would like, I could submit a PR with changes to support this (seems like the data is there). Just let me know.

brona commented 9 years ago

Hi, great idea! I will incorporate this feature in future releases (You can submit PR), however are you sure that

ip route | grep $Adapter | tail -1 | awk -F/ '{ print $1 }'

realy works? Especially are you sure that last ip address listed for interface is always used? For example on my system I get:

[user:~]$ ip route | grep $Adapter | grep '/32'                         
192.168.1.1/32 dev en1  scope link
192.168.1.252/32 dev en1  scope link
medington commented 9 years ago

Especially are you sure that last ip address listed for interface is always used?

Not at all. That's why I was asking you. :grin:

I'll see if I can dig into that and figure out a reliable way to tell which one is the IP address of the localhost.

One other quick question, why isn't your Homebrew formula part of the normal master specs repo?

brona commented 9 years ago

So I've been thinking about how to get that ip address, Option 1: Open UDP Socket to 8.8.8.8 (Should not send packets):

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 7))
src = s.getsockname()[0]
s.close()
print src

Option 2: Use your method, but get interface ip address from ip addr instead of ip route (so first IP address on interface will be selected):

Adapter="$(ip route get 8.8.8.8 | awk 'NR==1 {print $NF}')"
ip a | egrep "inet.+$Adapter" | sed -E "s|^.*inet ([0-9\.]+)/.+|\1|" | tail -1

I prefer Option 1, what do you think?

Regarding Homebrew.. I tried to push it year ago https://github.com/Homebrew/homebrew/pull/32944 maybe I should try again.

brona commented 9 years ago

Well I just released v1.1.0 which includes Option 1 solution, hope this works for you. Closing this issue but feel free to comment.

medington commented 9 years ago

Thank you for implementing this! Seems to be working for me. I'll double check the behavior switching between wired and wireless networks and both.

With no active network connections I get this:

route: writing to routing socket: not in table
Usage: ip route list
       ip route get ADDRESS
       ip route { add | del } ROUTE
ROUTE := PREFIX [ nexthop NH ]
route: writing to routing socket: not in table
Usage: ip route list
       ip route get ADDRESS
       ip route { add | del } ROUTE
ROUTE := PREFIX [ nexthop NH ]

On Linux I see this:

RTNETLINK answers: Network is unreachable

Anyway, FYI in case that's something you are interested on making more consistent between the 2 platforms.

Update: I tested again with wired and wireless networks together and separately and it all seems to be working just like I expect. Again, thank you for implementing this!

medington commented 8 years ago

Regarding Homebrew.. I tried to push it year ago Homebrew/homebrew#32944 maybe I should try again.

@brona I inquired with the Homebrew folks regarding your formula submission and they said to submit a new pull request.

brona commented 8 years ago

@medington Ok great, I will preare new formula. Thank you for upvote :)

Regarding RTNETLINK answers: Network is unreachable I am aware of that, but all other iproute2mac commands return error messages of underlying tools. NETLINK does not even make sense on BSD flavored system.

As I see it I would use underlying error message as long as it is not causing any trouble to anyone. In the other case I would generate somethink linke iproute2mac answers: Network is unreachable. What do you think?

medington commented 8 years ago

Yeah, I think it's fine the way it is. Good luck with your Homebrew re-submission!

brona commented 8 years ago

@medington iproute2mac was accepted to Homebrew master branch :), thanks for support. Please brew untap iproute2mac.

medington commented 8 years ago

@brona Glad to hear you got accepted!

Hey, I have another quick question / possible issue. When I have no network active (WiFi disabled, Ethernet unpluged), I can't seem to redirect or otherwise hide the error message from ip route get 8.8.8.8.

In this scenario, I want to simply have my helper function return a blank string but can't seem to get that to work by redirecting STDERR + STDOUT. Can you suggest a way to do that?