wuntee
wuntee

Reputation: 12470

Python raw IPv6 socket errors

I am having some problems using raw IPv6 sockets in python. I connect via:

    if self._socket != None:
        # Close out old socket first
        self._socket.close()
    self._socket = socket.socket(socket.AF_INET6, socket.SOCK_RAW)
    self._socket.bind((self._interface,0))
    self._socket.sendall(data)

where self._interface is my local address; specifically "fe80::fa1e:dfff:fed6:221d". When trying this, I get the following error:

  File "raw.py", line 164, in connect
    self._socket.bind((self._interface,0))
  File "<string>", line 1, in bind
socket.error: [Errno 49] Can't assign requested address

If I use my ipv6 localhost address for self._interface ("::1") I can actually bind the address, but can not send anything:

    self._socket.sendall(data)
  File "<string>", line 1, in sendall
socket.error: [Errno 39] Destination address required

Why would a raw socket need a destination address? Has anyone worked with raw IPv6 sockets in python, and can help me understand why this is happening?

Upvotes: 1

Views: 3966

Answers (3)

fkl
fkl

Reputation: 5535

Although this is an old question, i thought of adding an answer that works and helps any one who stumbles upon it latter.

The key problems are:

Raw sockets are not bound and connected to other sockets. Also sendto is the correct api to use.

Moreover, 4 tuple structure for destination address is required for ipv6 packets as opposed to two tuple ones for ipv4.

Lastly, the stack (at least on Linux mint 15) is more strict on ipv6 packets. If you try sending an empty icmpv4 echo request, python allows it and sends a meaning less packet on wire. Where as in case of ipv6, it simply gives error of 'invalid argument' when you try sending an empty packet. Hence a valid request is also required in case of ipv6. Following example does that all for ipv6 and sends a valid ping echo request to loop back address.

import socket

def main(dest_name):
    addrs = socket.getaddrinfo(dest_name, 0, socket.AF_INET6, 0, socket.SOL_IP)


    print addrs
    dest = addrs[0]

    # A minimal ICMP6-echo message (thanks to abarnert)
    data = '\x80\0\0\0\0\0\0\0'

    icmp = socket.getprotobyname('ipv6-icmp')
    #print icmp

    send_socket = socket.socket(socket.AF_INET6, socket.SOCK_RAW, icmp)
    print "sent to " + str(dest[4])
    send_socket.sendto(data, dest[4])
    send_socket.close()

if __name__ == '__main__':
    main('::1')

Upvotes: 2

wuntee
wuntee

Reputation: 12470

This code provides a raw socket with L2 access. Unfortunately OSX does not support the socket.PF_PACKET...

soc = socket.socket(socket.PF_PACKET, socket.SOCK_RAW) #create the raw-socket
soc.bind(("lo0", 0))

soc.send(packet)

Upvotes: 0

Bernd Petersohn
Bernd Petersohn

Reputation: 2264

I don't understand your combination of bind and sendall. In my understanding, bind is for server sockets and sendall requires a connection. Did you mean connect instead of bind?

Anyway, the IPv6 equivalent of INADDR_ANY is, according to the man page, IN6ADDR_ANY_INIT. Python does not define a constant for it, but this is the same as '::' (all zero).

This worked for me (as root):

>>> import socket
>>> s = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_RAW)
>>> s.bind(('::', 0))

EDIT: Oops, I first did not saw that you actually managed to bind the socket to an address. However your second problem is obvious: You must first connect to some address before you can send data. Or use sendto with an address. This is not different from IPv4.

Upvotes: 0

Related Questions