How to set the socket type and the protocol of a socket using create_connection?

G

Guillaume Comte

Hello everyone,

I want to use socket.create_connection(...) to set a source address in a ping implementation in python.

But how can I then set the type and the protocol? Because, before, I did:

icmp = socket.getprotobyname("icmp")
my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)

But now, I do:

src_addr = socket.gethostbyname(src_addr)
dest_addr = socket.gethostbyname(dest_addr)
my_socket = socket.create_connection(dest_addr, socket.getdefaulttimeout(), src_addr)

Is there something like my_socket.setproto()? I haven't found such a function in the documentation.

Thank you,
Guillaume
 
G

Guillaume Comte

In fact, socket.create_connection is for TCP only so I cannot use it for a ping implementation. Does anyone have an idea about how to be able to set a source address for ICMP messages?
 
H

Hans Mulder

In fact, socket.create_connection is for TCP only so I cannot use it for a ping implementation.

Why are you trying to reimplement ping?

All OS'es I am aware of come with a working ping implementation.

Does anyone have an idea about how to be able to set a source address for ICMP messages?

Did you try not setting it?

The default is probably your own IP address, which is the only
sensible value anyway. Or are you trying to cause confusion
by sending ICMP packets with a forged source address?

-- HansM
 
G

Guillaume Comte

Le lundi 20 août 2012 15:38:14 UTC+2, Hans Mulder a écrit :
Why are you trying to reimplement ping?

Because I work on a network emulator and I want to check biterros patterns so I need to access the data of the packets. An dsince my test program is written in Python, it's easier to do it in Python.
All OS'es I am aware of come with a working ping implementation.








Did you try not setting it?



The default is probably your own IP address, which is the only

sensible value anyway. Or are you trying to cause confusion

by sending ICMP packets with a forged source address?

No, I want to do it on a machine with aliases as in:

ifconfig em0 10.0.1.1 netmask 255.255.255.0 alias
ifconfig em0 10.0.2.1 netmask 255.255.255.0 alias
ping -c4 -S 10.0.1.1 10.0.2.1


But I think I've found the solution: my_socket.bind((src_addr, 1))
 
C

Chris Angelico

Why are you trying to reimplement ping?

All OS'es I am aware of come with a working ping implementation.

For some definition of "working", at least. I've never managed to get
MS Windows to ping broadcast, for instance.

A Google search for 'python ping' comes up with a few good answers,
though. You may want to have a look at some of them; if nothing else,
you'll get confirmation that what you're doing corresponds to what
someone else has done (which isn't proof you're doing the right thing,
but it does suggest it).

ChrisA
 
G

Guillaume Comte

Unfortunatly, my_socket.bind((src_addr, 1)) doesn't work. I get the error message: "socket.error: [Errno 49] Can't assign requested address"...

I've tried to change the protocol to IPPROTO_RAW. Here is a simple example of the code:



import socket
import os
import struct
import time
import select

ICMP_ECHO_REQUEST = 8
PACKET_SIZE = 64 # Bytes
TIMEOUT = 0.5 # Seconds

def do_one(src_addr, dest_addr):
my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)

if src_addr != None:
src_addr = socket.gethostbyname(src_addr)
my_socket.bind((src_addr, 1))

my_id = os.getpid() & 0xFFFF

print "id: " + str(my_id)

print "sending..."
send_one(dest_addr, my_socket, my_id)

print "receiving..."
id = receive_one(my_socket)
if id == None:
print "nothing received !"
else:
print "received id: " + str(id)

my_socket.close()

def checksum(source_string):
...

def send_one(addr, my_socket, id):
# Header: type (8), code (8), checksum (16), id (16), sequence number (16)
cs = 0
header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, cs, socket.htons(id), 0)
data = PACKET_SIZE * "G"

cs = checksum(header + data)

header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(cs), socket.htons(id), 0)
packet = header + data

my_socket.sendto(packet, (socket.gethostbyname(addr), 1))

def receive_one(my_socket):
while True:
what_ready = select.select([my_socket], [], [], TIMEOUT)
if what_ready[0] == []:
return None

received_packet = my_socket.recvfrom(1024)[0]
header = received_packet[20:28]
id = struct.unpack("bbHHh", header)[3]
return socket.ntohs(id)

if __name__ == "__main__":

import sys
dst = sys.argv[1]
print "dst: " + dst
try:
src = sys.argv[2]
print "src: " + src
except IndexError:
src = None
do_one(src, dst)


But when I try to set a source address, I still get the same error message...

Does anyone know how I could build the IP header (I don't even know if it can be the solution but it's worth a try...)?
 
G

Guillaume Comte

Unfortunatly, my_socket.bind((src_addr, 1)) doesn't work. I get the error message: "socket.error: [Errno 49] Can't assign requested address"...

I've tried to change the protocol to IPPROTO_RAW. Here is a simple example of the code:



import socket
import os
import struct
import time
import select

ICMP_ECHO_REQUEST = 8
PACKET_SIZE = 64 # Bytes
TIMEOUT = 0.5 # Seconds

def do_one(src_addr, dest_addr):
my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)

if src_addr != None:
src_addr = socket.gethostbyname(src_addr)
my_socket.bind((src_addr, 1))

my_id = os.getpid() & 0xFFFF

print "id: " + str(my_id)

print "sending..."
send_one(dest_addr, my_socket, my_id)

print "receiving..."
id = receive_one(my_socket)
if id == None:
print "nothing received !"
else:
print "received id: " + str(id)

my_socket.close()

def checksum(source_string):
...

def send_one(addr, my_socket, id):
# Header: type (8), code (8), checksum (16), id (16), sequence number (16)
cs = 0
header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, cs, socket.htons(id), 0)
data = PACKET_SIZE * "G"

cs = checksum(header + data)

header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(cs), socket.htons(id), 0)
packet = header + data

my_socket.sendto(packet, (socket.gethostbyname(addr), 1))

def receive_one(my_socket):
while True:
what_ready = select.select([my_socket], [], [], TIMEOUT)
if what_ready[0] == []:
return None

received_packet = my_socket.recvfrom(1024)[0]
header = received_packet[20:28]
id = struct.unpack("bbHHh", header)[3]
return socket.ntohs(id)

if __name__ == "__main__":

import sys
dst = sys.argv[1]
print "dst: " + dst
try:
src = sys.argv[2]
print "src: " + src
except IndexError:
src = None
do_one(src, dst)


But when I try to set a source address, I still get the same error message...

Does anyone know how I could build the IP header (I don't even know if it can be the solution but it's worth a try...)?
 
D

Dennis Lee Bieber

Unfortunatly, my_socket.bind((src_addr, 1)) doesn't work. I get the error message: "socket.error: [Errno 49] Can't assign requested address"...

Port #1 is in the range of OS privileged ports; you may need root
privileges to bind to it.

http://www.w3.org/Daemon/User/Installation/PrivilegedPorts.html

{Note: one link I hit indicates that M$ Windows does not have this
restriction; and

seems to confirm that... OTOH: specifying an invalid IP address
regardless of port produces
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>

whereas using the DHCP assigned address gave
}
 
D

Dennis Lee Bieber

On Tue, 21 Aug 2012 10:00:28 -0700 (PDT), Guillaume Comte
<[email protected]> declaimed the following in
gmane.comp.python.general:

A later follow-up
Unfortunatly, my_socket.bind((src_addr, 1)) doesn't work. I get the error message: "socket.error: [Errno 49] Can't assign requested address"...

Since .bind() is used to set up a /listener/, the network stack
(LINK layer) probably has to be tied to the IP address; that is, a valid
"source" address needs to be supplied to .bind.

But when I try to set a source address, I still get the same error message...

Does anyone know how I could build the IP header (I don't even know if it can be the solution but it's worth a try...)?

Based upon http://linux.die.net/man/7/raw "Raw sockets allow new
IPv4 protocols to be implemented in user space. A raw socket receives or
sends the raw datagram not including link level headers. " implies that
you need to build the entire IP packet for your ICMP message... That
means you do NOT use socket methods to set fields -- you'll probably
have to use something like the struct module to lay out the entire IP
packet /including/ header contents, and then pass that as-is to the
socket.
 
G

Guillaume Comte

Le mercredi 22 août 2012 04:10:43 UTC+2, Dennis Lee Bieber a écrit :
On Tue, 21 Aug 2012 10:00:28 -0700 (PDT), Guillaume Comte

<[email protected]> declaimed the following in

gmane.comp.python.general:



A later follow-up
Unfortunatly, my_socket.bind((src_addr, 1)) doesn't work. I get the error message: "socket.error: [Errno 49] Can't assign requested address"...



Since .bind() is used to set up a /listener/, the network stack

(LINK layer) probably has to be tied to the IP address; that is, a valid

"source" address needs to be supplied to .bind.
Do you mean that an alias is not a valid source address? Because ping.c cando it...

I've also tried changing the port to 3333 or 8080 but the same error happens.
 
G

Guillaume Comte

Le mercredi 22 août 2012 04:10:43 UTC+2, Dennis Lee Bieber a écrit :
On Tue, 21 Aug 2012 10:00:28 -0700 (PDT), Guillaume Comte

<[email protected]> declaimed the following in

gmane.comp.python.general:



A later follow-up
Unfortunatly, my_socket.bind((src_addr, 1)) doesn't work. I get the error message: "socket.error: [Errno 49] Can't assign requested address"...



Since .bind() is used to set up a /listener/, the network stack

(LINK layer) probably has to be tied to the IP address; that is, a valid

"source" address needs to be supplied to .bind.
Do you mean that an alias is not a valid source address? Because ping.c cando it...

I've also tried changing the port to 3333 or 8080 but the same error happens.
 
G

Guillaume Comte

I've managed to build the IP header. I've put the source and destination addresses in this header but it doesn't change the real source address...

I'm trying to read the ping source code but I'm lost...
 
H

Hans Mulder

Le mercredi 22 août 2012 04:10:43 UTC+2, Dennis Lee Bieber a écrit :
On Tue, 21 Aug 2012 10:00:28 -0700 (PDT), Guillaume Comte
<[email protected]> declaimed the following in
gmane.comp.python.general:
A later follow-up
Unfortunately, my_socket.bind((src_addr, 1)) doesn't work. I get the
error message: "socket.error: [Errno 49] Can't assign requested address"...
Since .bind() is used to set up a /listener/, the network stack

Not necessarily. That is, a listener is normally bound to a
specific port, since otherwise the client doesn't know what port
to connect to (unless you provide that factoid on another port).

But nothing prevents a client from binding its socket to a
specific port number. These days, that doesn't buy you much,
but in the old days, some services would only talk to clients
using privileged port numbers (<1024).
Do you mean that an alias is not a valid source address? Because ping.c can do it...
I've also tried changing the port to 3333 or 8080 but the same error happens.

On my laptop, 0 appears to be the only port number that bind accepts
for a raw socket. Other numbers I tried all raise "socket.error:
[Errno 49] Can't assign requested address".

But this might depend on your OS. What OS are you using?

Hope this helps,

-- HansM
 
G

Guillaume Comte

Le mercredi 22 août 2012 11:03:11 UTC+2, Hans Mulder a écrit :
On my laptop, 0 appears to be the only port number that bind accepts

for a raw socket. Other numbers I tried all raise "socket.error:

[Errno 49] Can't assign requested address".



But this might depend on your OS. What OS are you using?

I'm using FreeBSD 7.3
 
D

Dennis Lee Bieber

I've managed to build the IP header. I've put the source and destination addresses in this header but it doesn't change the real source address...

For all I know (I've done very little network programming, and that
was years ago using plain TCP and UDP -- worse, on a VMS system so it
wasn't the "UNIX style" socket interface), your network stack may still
be overriding the packet at some lower level and inserting the IP
associated with the interface the packet went out on...
 
G

Grant Edwards

For all I know (I've done very little network programming, and that
was years ago using plain TCP and UDP -- worse, on a VMS system so it
wasn't the "UNIX style" socket interface), your network stack may still
be overriding the packet at some lower level and inserting the IP
associated with the interface the packet went out on...

I've only been intermittently following this thread, but back when I
added Python's raw packet support for Unix, the socket module was a
_very_ thin wrapper for the underlying OS network socket API. The
behavior of various types of sockets was defined entirely by the
underlying OS.

So, if you're trying to do something obscure (which it seems you are),
asking people who know how to do it in C on the relevent OS is
probably the best approach.

Below are examples of sending and receiving a completely raw packet on
Linux (where you provide _all_ the bytes: the MAC addreses, the
Ethernet type, everything).

------------------------------send------------------------------
#!/usr/bin/python
import sys,os,socket,struct
from optparse import OptionParser

p = OptionParser()
p.add_option("-i","--interface",dest="interface",metavar="<name>",type='str',default="eth0")
options,args = p.parse_args()

if len(args) != 1:
sys.stderr.write("you must provide a destination MAC address\n")
sys.exit(1)

def toHex(s):
return " ".join([("%02x" % ord(c)) for c in s])

ethProto = 0x5678
dstMacStr = args[0]
dstMacAddr = "".join(map(chr,[int(x,16) for x in dstMacStr.split(":")]))

s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, ethProto)
s.bind((options.interface,ethProto))

ifName,ifProto,pktType,hwType,hwAddr = s.getsockname()
srcMacAddr = hwAddr

ethHeader = struct.pack("!6s6sh",dstMacAddr,srcMacAddr,ethProto)
packet = ethHeader + "some ASCII data here"

sys.stdout.write("tx: %s\n" % toHex(packet))
s.send(packet)
s.close()
-----------------------------------------------------------------

------------------------------recv------------------------------
#!/usr/bin/python
import sys,os,socket,struct
from optparse import OptionParser

p = OptionParser()
p.add_option("-i","--interface",dest="interface",metavar="<name>",type='str',default="eth0")
options,args = p.parse_args()

if len(args) != 0:
sys.stderr.write("no arguments accepted\n")
sys.exit(1)

def toHex(s):
return " ".join([("%02x" % ord(c)) for c in s])

ethProto = 0x5678

s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, ethProto)
s.bind((options.interface,ethProto))

packet = s.recv(4096)
sys.stdout.write("rx: %s\n" % toHex(packet))
s.close()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top