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

Provide changing source IP address bind to network interface. #223

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

jzrk
Copy link

@jzrk jzrk commented Jun 23, 2018

This change provides changing source IP address bind to the network interface.
It is popular option in applications like ssh(1) (-b parameter) and telnet(1) (-s parameter).

Example situation:
I have two IP addresses bind to one network interface:

# ifconfig net0
net0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
        ether 08:00:27:14:65:25
        inet 10.0.8.82 netmask 0xffff0000 broadcast 10.0.255.255 
        inet 10.0.100.182 netmask 0xffff0000 broadcast 10.0.255.255 
        nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
        media: Ethernet autoselect (1000baseT <full-duplex>)
        status: active

By default, connections will be performed using first IP address: 10.0.8.82.
But for some reason I would like to use second IP address: 10.0.100.182.

In this example, I connect to the machine with Windows having WinRM protocol configured (IP: 10.0.234.157).
Without this patch, sniffed packets looks like below:

# tcpdump -n -i net0 src 10.0.8.82 or src 10.0.100.182 and port 5986
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on net0, link-type EN10MB (Ethernet), capture size 65535 bytes
19:06:00.888194 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [S], seq 3358640735, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 1591692855 ecr 0], length 0
19:06:00.888679 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [.], ack 838433671, win 1040, options [nop,nop,TS val 1591692856 ecr 37444086], length 0
19:06:00.889176 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [P.], seq 0:517, ack 1, win 1040, options [nop,nop,TS val 1591692856 ecr 37444086], length 517
19:06:00.889999 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [P.], seq 517:843, ack 847, win 1040, options [nop,nop,TS val 1591692856 ecr 37444086], length 326
19:06:00.894194 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [.], seq 843:2291, ack 906, win 1040, options [nop,nop,TS val 1591692861 ecr 37444086], length 1448
19:06:00.894241 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [P.], seq 2291:2869, ack 906, win 1040, options [nop,nop,TS val 1591692861 ecr 37444086], length 578
19:06:00.932339 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [.], seq 2869:4317, ack 2159, win 1040, options [nop,nop,TS val 1591692899 ecr 37444090], length 1448
19:06:00.932816 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [P.], seq 4317:5007, ack 2159, win 1040, options [nop,nop,TS val 1591692900 ecr 37444090], length 690
19:06:00.938569 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [.], seq 5007:6455, ack 3188, win 1040, options [nop,nop,TS val 1591692905 ecr 37444090], length 1448
19:06:00.938766 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [P.], seq 6455:7049, ack 3188, win 1040, options [nop,nop,TS val 1591692906 ecr 37444090], length 594
19:06:00.955386 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [.], ack 4729, win 1016, options [nop,nop,TS val 1591692923 ecr 37444092], length 0
19:06:00.959428 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [.], seq 7049:8497, ack 4729, win 1040, options [nop,nop,TS val 1591692926 ecr 37444092], length 1448
19:06:00.959735 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [P.], seq 8497:9123, ack 4729, win 1040, options [nop,nop,TS val 1591692927 ecr 37444092], length 626
19:06:00.964671 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [.], seq 9123:10571, ack 5678, win 1040, options [nop,nop,TS val 1591692931 ecr 37444093], length 1448
19:06:00.964714 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [P.], seq 10571:11021, ack 5678, win 1040, options [nop,nop,TS val 1591692931 ecr 37444093], length 450
19:06:00.967122 IP 10.0.8.82.35849 > 10.0.234.157.5986: Flags [F.], seq 11021, ack 6467, win 1040, options [nop,nop,TS val 1591692934 ecr 37444093], length 0

Defining bind_to parameter enables to see packets bind to my network interface:

# tcpdump -n -i net0 src 10.0.8.82 or src 10.0.100.182 and port 5986
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on net0, link-type EN10MB (Ethernet), capture size 65535 bytes
19:06:24.700534 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [S], seq 1631079665, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 1591716667 ecr 0], length 0
19:06:24.700900 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [.], ack 41779453, win 1040, options [nop,nop,TS val 1591716668 ecr 37446467], length 0
19:06:24.701190 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [P.], seq 0:517, ack 1, win 1040, options [nop,nop,TS val 1591716668 ecr 37446467], length 517
19:06:24.702175 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [P.], seq 517:843, ack 847, win 1040, options [nop,nop,TS val 1591716668 ecr 37446467], length 326
19:06:24.706688 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [.], seq 843:2291, ack 906, win 1040, options [nop,nop,TS val 1591716674 ecr 37446468], length 1448
19:06:24.706727 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [P.], seq 2291:2869, ack 906, win 1040, options [nop,nop,TS val 1591716674 ecr 37446468], length 578
19:06:24.843192 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [.], seq 2869:4317, ack 2159, win 1040, options [nop,nop,TS val 1591716810 ecr 37446480], length 1448
19:06:24.843348 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [P.], seq 4317:5007, ack 2159, win 1040, options [nop,nop,TS val 1591716810 ecr 37446480], length 690
19:06:24.852503 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [.], seq 5007:6455, ack 3188, win 1040, options [nop,nop,TS val 1591716819 ecr 37446481], length 1448
19:06:24.852545 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [P.], seq 6455:7049, ack 3188, win 1040, options [nop,nop,TS val 1591716819 ecr 37446481], length 594
19:06:24.937357 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [.], ack 4729, win 1016, options [nop,nop,TS val 1591716905 ecr 37446491], length 0
19:06:24.942480 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [.], seq 7049:8497, ack 4729, win 1040, options [nop,nop,TS val 1591716909 ecr 37446491], length 1448
19:06:24.942529 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [P.], seq 8497:9123, ack 4729, win 1040, options [nop,nop,TS val 1591716909 ecr 37446491], length 626
19:06:24.946377 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [.], seq 9123:10571, ack 5678, win 1040, options [nop,nop,TS val 1591716913 ecr 37446491], length 1448
19:06:24.946414 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [P.], seq 10571:11021, ack 5678, win 1040, options [nop,nop,TS val 1591716913 ecr 37446491], length 450
19:06:24.948557 IP 10.0.100.182.55845 > 10.0.234.157.5986: Flags [F.], seq 11021, ack 6467, win 1040, options [nop,nop,TS val 1591716915 ecr 37446492], length 0

It is easy to use with Protocol class.
Example program looks like:

#!/usr/bin/env python

from winrm.protocol import Protocol


def main():
    protocol = Protocol(
        endpoint='https://10.0.234.157:5986/wsman',
        transport='ssl',
        username='Administrator',
        password='Root1234',
        bind_to='10.0.100.182',
        server_cert_validation='ignore'
    )
    shell = protocol.open_shell()

    command = 'dir' 

    command_result = protocol.run_command(shell, command)
    result = protocol.get_command_output(shell, command_result)[0]

    print "Result: " + result

    protocol.cleanup_command(shell, command_result)
    protocol.close_shell(shell)

I also modified Session class and now it is able to use bind_to option too:

# python
Python 2.7.8 (default, Apr 24 2018, 11:47:08) 
[GCC 4.2.1 20070831 patched [FreeBSD]] on freebsd10
Type "help", "copyright", "credits" or "license" for more information.
>>> import winrm 
>>> session = winrm.Session('10.0.234.157', auth=('Administrator', 'Root1234'), bind_to='10.0.100.182')
>>> result = session.run_cmd('dir')
>>> print result.std_out
 Volume in drive C has no label.
 Volume Serial Number is 36B7-C648

 Directory of C:\Users\Administrator

05/21/2018  03:29 AM    <DIR>          .
05/21/2018  03:29 AM    <DIR>          ..
03/12/2018  03:32 AM                 0 AppCmd
04/04/2017  10:04 AM    <DIR>          Contacts
11/23/2017  03:04 AM    <DIR>          Desktop
03/12/2018  02:58 AM    <DIR>          Documents
04/18/2018  05:02 AM    <DIR>          Downloads
04/04/2017  10:04 AM    <DIR>          Favorites
03/14/2018  05:41 AM    <DIR>          Links
04/04/2017  10:04 AM    <DIR>          Music
04/04/2017  10:04 AM    <DIR>          Pictures
04/04/2017  10:04 AM    <DIR>          Saved Games
04/04/2017  10:04 AM    <DIR>          Searches
04/04/2017  10:04 AM    <DIR>          Videos
09/07/2017  02:50 AM    <DIR>          WINDOWS
               1 File(s)              0 bytes
              14 Dir(s)  84,364,783,616 bytes free

During that operation, sniffed trafiic looks like:

# tcpdump -n -i net0 dst 10.0.234.157
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on net0, link-type EN10MB (Ethernet), capture size 65535 bytes
20:37:06.036513 ARP, Request who-has 10.0.234.157 tell 10.0.235.161, length 46
20:39:42.323855 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [S], seq 3085588200, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 1597314290 ecr 0], length 0
20:39:42.325149 ARP, Reply 10.0.100.182 is-at 08:00:27:14:65:25, length 28
20:39:42.325413 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [.], ack 271742052, win 1040, options [nop,nop,TS val 1597314292 ecr 38006227], length 0
20:39:42.325597 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [.], seq 0:1448, ack 1, win 1040, options [nop,nop,TS val 1597314292 ecr 38006227], length 1448
20:39:42.325635 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [P.], seq 1448:1961, ack 1, win 1040, options [nop,nop,TS val 1597314292 ecr 38006227], length 513
20:39:42.522301 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [.], seq 1961:3409, ack 1221, win 1040, options [nop,nop,TS val 1597314489 ecr 38006245], length 1448
20:39:42.522676 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [P.], seq 3409:4002, ack 1221, win 1040, options [nop,nop,TS val 1597314489 ecr 38006245], length 593
20:39:42.532641 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [.], seq 4002:5450, ack 2224, win 1040, options [nop,nop,TS val 1597314499 ecr 38006247], length 1448
20:39:42.532785 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [P.], seq 5450:5969, ack 2224, win 1040, options [nop,nop,TS val 1597314499 ecr 38006247], length 519
20:39:42.574111 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [.], ack 4916, win 998, options [nop,nop,TS val 1597314541 ecr 38006252], length 0
20:39:42.577903 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [.], seq 5969:7417, ack 4916, win 1040, options [nop,nop,TS val 1597314544 ecr 38006252], length 1448
20:39:42.577945 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [P.], seq 7417:7974, ack 4916, win 1040, options [nop,nop,TS val 1597314545 ecr 38006252], length 557
20:39:42.581812 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [.], seq 7974:9422, ack 5829, win 1040, options [nop,nop,TS val 1597314548 ecr 38006252], length 1448
20:39:42.581941 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [P.], seq 9422:9803, ack 5829, win 1040, options [nop,nop,TS val 1597314549 ecr 38006252], length 381
20:39:42.689067 IP 10.0.100.182.39017 > 10.0.234.157.5985: Flags [.], ack 6587, win 1040, options [nop,nop,TS val 1597314656 ecr 38006253], length 0

I build sources and tested connections on my laptop with FreeBSD and Python 2.7.8.

@coveralls
Copy link

coveralls commented Jun 23, 2018

Coverage Status

Coverage decreased (-0.03%) to 69.395% when pulling 488b1aa on jzrk:master into ffec954 on diyan:master.

@badcure
Copy link
Collaborator

badcure commented Jun 11, 2019

@jzrk This looks pretty good, I have not seen this before.

Looks like you will need to rebase. Also, can you add some unit tests?

Also this may be nit-picking, but do you mind renaming bind_to to source_ip? I think that would make it a little clearer to others, let me know what you think.

@badcure badcure added the needs a rebase/merge Awaiting a merge/rebase from master label Jun 11, 2019
@jborean93
Copy link
Collaborator

It would be ideal if we could have this as an extras dep and not add it to the base requirements. We do this for various other features like Kerberos and CredSSP support where we provide the base package with enough to get you started and then provide optional extras. The main concern is that this would now be an extra library for Ansible users to deal with and keeping that to a minimum is a good thing for us.

@diyan
Copy link
Owner

diyan commented Jun 11, 2019

Also this may be nit-picking, but do you mind renaming bind_to to source_ip? I think that would make it a little clearer to others, let me know what you think.

@badcure Looks like we have a different opinions here. source_ip sounds like "here you have a local IP address to use for outbound traffic"; bind_to sounds like "please bind all outbound traffic for particular network interface". IMO, bind_to is more ubiquitous to people who have moderate to advanced skills in networking; I would prefer to stick with this name.

@nitzmahone , @jborean93 what do you think? source_ip or bind_to?

By the way, SourceAddressAdapter from requests_toolbelt uses source_address name:

https://github.com/requests/toolbelt/blob/master/requests_toolbelt/adapters/source.py#L45
def init(self, source_address, **kwargs):

@jborean93
Copy link
Collaborator

I don't have a preference on the name, personally I would try and align with the underlying library if it makes sense but that's just me.

@badcure
Copy link
Collaborator

badcure commented Jun 12, 2019

@jborean93 Ahh, good point. I don't foresee this feature being used too often.

@diyan So I agree 100% with your explanation, but I was thinking(i.e. assuming) that a non-insignificant portion of the users may not have that background. That is why I was leaning the other way. Also from my experience, I naturally associate "bind" to be both the interface and IP, but this allows for the IP only.

That said, source_address may be better, or something along the lines of bind_ip or bind_address works as well just to denote that it is limited to addresses only.

@jzrk
Copy link
Author

jzrk commented Jun 12, 2019

Hello, Gents!
Thank you for your replies! I am surprised because I thought that support for this library died. :) It is great to see the support again. I added this change because I needed something like this and I decided to share with you. It also may be useful for somebody.

Regarding to my change: purposely I decided to use bind prefix, because it is commonly used name. In ssh(1) manual we can read:

     -b bind_address
             Use bind_address on the local machine as the source address of
             the connection.  Only useful on systems with more than one
             address.

As @diyan said, it is also more ubiquitous to people who have moderate to advanced skills in networking. Of course, I am flexible and if we will decide on something I will change the name. Personally, I would suggest something with bind prefix, for example bind_address.

@badcure @jborean93 Of course, I will rebase the branch and I will add all suggestions. I do not have big experience with Python, because I am rather low-level programmer but I will try to add tests :) It will be good practice. So, I am asking for the indulgence. :)

Summarizing, current changelist is:

  • change name from bind_to to e.g. bind_address;
  • add tests;
  • make requests-toolbelt as an optional package.

Is there anything else?

@badcure
Copy link
Collaborator

badcure commented Jun 13, 2019

Nope, that looks good! bind_address works for me at least. If no one else speaks up, that should be fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs a rebase/merge Awaiting a merge/rebase from master
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants