I have two ethernet cards, and I would like to send out UDP broadcast message on the local network(192.168.0.255) but it seams that the UDP message send out only the first ethernet card.
ip ="255.255.255.255"
UDPSocket = socket.socket( socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP )
UDPSocket.setsockopt( socket.SOL_SOCKET, socket.SO_BROADCAST, True )
UDPSocket.settimeout( timeout )
UDPSocket.sendto( msg.xmlmsg, ( ip, UDPport ) )
UDPSocket.close()
How can I specified that witch subnet would I send the message?
You have to bind the socket to a local address.
For a non-broadcast UDP socket you usually bind to the any address (0.0.0.0). You can also choose a particular port or let the system choose one for you (port 0). If you don't bind the socket explicitly it will be bound automatically to 0.0.0.0:0 on the first send.
To do broadcast is always recommended to do the bind explicitly and specify your own local address of the selected interface. If you do not need a particular port you can use port 0.
The accepted answer doesn't work for me, trying to send ICMP ECHO requests on a raw socket. I had to do this:
target = '8.8.8.8'
packet = bytearray(...)
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
sock.setsockopt(socket.SOL_SOCKET, 25, 'eth0') # or whatever the name of the interface is
# 25 = SO_BINDTODEVICE
# Some Python installations will have the constant IN.SO_BINDTODEVICE which you can use here, YMMV
sock.sendto(packet, (target, 1))
Related
I tried setting up a TCP socket with Python, and it works totally fine as long as I'm in the same network. But my problem is that I can't get it to work if one of the devices isn't in the same network.
I already tried setting the bind IP to '0.0.0.0', and if I try connecting I get socket error 10060. I think the problem probably has something to do with port-forwarding, but I don't know how to do it in Python.
Here is the code I used to test it, and I don't know how I get the public IP from my device in Python (not the gethostbyname() function, that only returns the private IP).
I hope you can help me.
Server.py
HOST = '0.0.0.0'
PORT = 5000
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen()
conn, addr = s.accept()
with conn:
while True:
text = input("Enter text: ").encode("utf-8")
send_msg(conn,text)
echo = recv_msg(conn).decode("utf-8")
print(echo)
client.py
#HOST = '192.168.0.220'
HOST = 'x.x.x.x' #ip address of server
PORT = 5000`
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
while True:
data = recv_msg(s)
send_msg(s,data)
def send_msg(sock, msg):
msg = struct.pack('>Q', len(msg)) + msg
sock.sendall(msg)
def recv_msg(sock):
raw_msglen = recvall(sock, 8)
if not raw_msglen:
return None
msglen = struct.unpack('>Q', raw_msglen)[0]
return recvall(sock, msglen)
def recvall(sock: socket.socket, n):
data = bytearray()
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data.extend(packet)
return data
The code shown is fine. bind()'ing the server to 0.0.0.0 is typically the correct thing to do, that allows it to listen on all local IPs installed on its machine. Or, you can bind() it to the specific IP of the adapter that is connected to your network. Either way is fine.
On the client side, if the client is on the same network, it can connect() to the server's LAN IP, and all is well.
The problem comes when the client is not on the same network. That means the client simply cannot connect to your server directly. There is no physical route to facilitate that connection. So, to remedy that, you must setup a Port Forwarding rule in your LAN router. You must open a public IP/Port on the router's WAN, and map it to the server's LAN IP/Port. Then the client will be able to connect to the router's WAN IP/Port and let the router forward the packets to the server's LAN IP/Port, and vice versa.
If your router supports uPNP, your server code can setup this Port Forwarding programmably. There are uPNP libraries available, or your OS may even have APIs for that. Otherwise, your router admin must setup the Port Forwarding rule by hand.
To discover your router's WAN IP for the client to connect to, your server code can query an external site like https://whatismyip.com, or even query the router directly (if the router has an API for this purpose). Otherwise, you should subscribe to a 3rd party Dynamic DNS service that assigns you a static hostname that the client can always connect to, and then your server code, or even the router itself, can update the IP for that hostname whenever the router's WAN IP changes.
Basing on MC receiver from stack:
How do you UDP multicast in Python?
I would like to completely understand what is going on. Here is what I understand and what not:
As far as I understand: socket_name = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) => creating socket with IP proto ver 4, that will receive MC datagrams using UDP.
socket_name.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) this is responsible for setting parameter that socket could use the same address
(and in this case, port, because SO_REUSEADDR = SO_REUSEPORT for Multicasts).
(Paragraph multicast ~ How do SO_REUSEADDR and SO_REUSEPORT differ?)
if IS_ALL_GROUPS: socket_name.bind(('', MCAST_PORT)) means that if IS_ALL_GROUPS is true, bind socket to any addres, and else: socket_name.bind((MCAST_GRP, MCAST_PORT)) means bind socket to specific given IP address.
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) means convert IP address to binary form and socket_name.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) means that we add membership of group and after this line packets starting to arrive.
However I don't understand some things.
Why when I used IS_ALL_GROUPS = true where MCAST_GRP = '239.0.1.104' program could start and when the parameter was false, it didn't bound to specific multicast address? My logic is that when the parameter is true it binds to any MCast addres he gets from IGMP join messages and when parameter is false, it binds to specific given address. Am I correct?
I have multithread program that analyses bitrate to which i provide more than one address in form of list. When I set IS_ALL_GROUPS to false, program works correctly printing out e.g. 10.5, 4.5, 5.0 where each result is bitrate of one stream from unique address, however all of addresses share the same port 12345. When I set IS_ALL_GROUPS to true, program sums up results giving 20.0, 20.0, 20.0, do you know what may be the cause?
import socket
import struct
MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
IS_ALL_GROUPS = True
socket_name = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if IS_ALL_GROUPS:
# on this port, receives ALL multicast groups
socket_name.bind(('', MCAST_PORT))
else:
# on this port, listen ONLY to MCAST_GRP
socket_name.bind((MCAST_GRP, MCAST_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
print socket_name.recv(10240)
I think there are a few things going on:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
should probably be:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
the only "datagram" based protocol for IPv4 is UDP, the protocol field is just for "raw sockets" which are used by programs like tcpdump. this shouldn't matter though.
next:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
just allows multiple sockets to be bound to this same (addr, port) combination at once, i.e. you can run multiple copies of your program at once, or in quick succession. which I think is what you're saying
next:
sock.bind((host, port))
says you want to receive packets destined for the a given address and port. the special address '' (i.e. INADDR_ANY) means that the kernel shouldn't do any filtering on address. you probably don't want to bind to any specific address at this stage, see What does it mean to bind a multicast (UDP) socket?
next:
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
I'd suggest using "=4sl" for the format string as this gives me 8 bytes, rather than 16, and is consistent with a C struct ip_mreq on my (Linux) system. this difference in padding might just be a 32/64bit issue, and doesn't seem to hurt
to summarise: 1. the setsockopt(IP_ADD_MEMBERSHIP) call has asked your kernel's network stack to arrange (via IGMP) for multicast packets sent to MCAST_GRP to be delivered to a network interface attached to your host. 2. the bind() call has arranged for packets received via the network interface to make it to the socket in your process. it might be important that the bind() specifies INADDR_ANY so that all multicast packets that arrive are delivered to your process rather than being filtered
a couple of command lines that might be useful are:
tcpdump -i eth0 -vv 'ip proto 2'
which dumps IGMP packets, and:
tcpdump -i eth0 -vv 'net 224.0.0.0/4'
which dumps multicast packets
I'm not sure what your "multithread analysis program" is doing, as you've not given any relevant info. your posted code is also wrong, presumably socket_name and sock are the same? I'd also suggest using Python 3 as Python 2 is dying
I want to do a packet sniffer in Python 3.5 which captures UDP, TCP and ICMP. This is a short example of it:
import socket
import struct
# the public network interface
HOST = socket.gethostbyname(socket.gethostname())
# create a raw socket and bind it to the public interface
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.bind((HOST,0))
# Include IP headers
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# receive all packages
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# receive a package
n=1
while(n<=400):
print('Number ', n)
data=s.recvfrom(65565)
packet=data[0]
address= data[1]
header=struct.unpack('!BBHHHBBHBBBBBBBB', packet[:20])
if(header[6]==6): #header[6] is the field of the Protocol
print("Protocol = TCP")
elif(header[6]==17):
print("Protocol = UDP")
elif(header[5]==1):
print("Protocol = ICMP")
n=n+1
The problem is that it only captures UDP packets :(
Output:
Number 1 Protocol = UDP Number 2 Protocol = UDP Number 3 Protocol = UDP Number 4 Protocol = UDP Number 5 Protocol = UDP Number 6 Protocol = UDP Number 7
There are 2 options:
The sniffer can only capture UDP packets.
I'm just receiving UDP packets.
I think that the most logical answer is my sniffer doesn't work correctly and it's just capturing UDP. Any idea?
I myself am in the stage of creating a python packet parser/sniffer and in my research I found that, to be able parse all the incoming packets like TCP, ICMP, UDP, ARP ..etc., you must not use the below socket type because socket.IPPROTO_IP gives out only IP packets and is a dummy protocol
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
rather you must use this and works best with Linux systems
s = socket.socket( socket.AF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003))
You are using "gethostbyname" and this method obtains one between all ip adresses in your computer.
In place of this method you must use "gethostbyname_ex" that obtains a list with several addreses. You must select the address used for the browser, and you will capure TCP, UDP and ICMP packets.
I'm building a simple client-server multiplayer game and I want to have connected UDP sockets. However, when I call the listen() method it produces Operation not supported exception.
try:
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind((host, port))
server.listen(5)
except socket.error, (value, message):
print "E: " + message # E: Operation not supported
Is there a way to have connected datagram sockets?
UDP protocol is connectionless and thus you really cannot create connections between 2 sockets in the same manner as with the TCP client and server; thus you also cannot do the listen system call on an UDP socket, as it concerns only the TCP server sockets. Instead you use socket.recvfrom to receive datagrams from any address:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((host, port))
data, addr = sock.recvfrom(65536) # this will fit the maximum datagram
You can respond to the client (if they still have a socket open), by destructuring the addr which is a host, port tuple for socket.AF_INET, socket.SOCK_DGRAM.
client_host, client_port = addr
You can send data back there with sendto
sock.sendto(data, (client_host, client_port))
If you want to use a well-known port, you will bind the socket; if not, the system will assign a port for you.
It is possible to do a connect system call with datagram sockets on many socket implementations; this serves as a sort of filter, that drops the packets coming from unwanted sources, and for setting the default outgoing address for sock.send (it is still OK to use sock.sendto on such socket to another address, though the response might be dropped because of the "connection"). This is very useable on a client or between 2 nodes if they agree to use 2 well known ports with each other.
However if you do connect on server, it cannot serve any other requests on this socket. Also, listen with its queues only relates to SOCK_STREAM sockets.
Thus in case of many clients to 1 server, you could have the server listen to socket 12345, and when a client contacts the server, the server could respond from server:12345 with a message that contains another port number that the client should use with the server.
I want ro receive some data that is sent as a UDP packet over VPN. So wrote (mostly copied) this program in python:
import socket
import sys
HOST = ???????
PORT = 80
# SOCK_DGRAM is the socket type to use for UDP sockets
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((HOST,PORT))
data,addr = sock.recv(1024)
print "Received: %s" % data
print "Addr: %s" % addr
What should I use as host? I know the IP of the sender but it seems anything thats not local gives me socket.error: [Errno 10049]. The IP that the VPN gives me (the same IP that the sender sends to, that is)? Or just localhost?
The host argument is the host IP you want to bind to. Specify the IP of one of your interfaces (Eg, your public IP, or 127.0.0.1 for localhost), or use 0.0.0.0 to bind to all interfaces. If you bind to a specific interface, your service will only be available on that interface - for example, if you want to run something that can only be accessed via localhost, or if you have multiple IPs and need to run different servers on each.
"0.0.0.0" will listen for all incoming hosts. For example,
sock.bind(("0.0.0.0", 999))
data,addr = sock.recv(1024)
Use:
sock.bind(("", 999))